From c39738595bba027b84c0e94a7b4e6679bb86fc96 Mon Sep 17 00:00:00 2001 From: Yonghua Huang Date: Fri, 31 Aug 2018 10:58:54 +0800 Subject: [PATCH 0001/1103] PCI: add pci_devices_ignore cmdline option some PCI devices may be occupied by hypervisor and do not want to enable in linux guest. add cmdline option "pci_devices_ignore=(B1:D1:F1),(B2:D2:F2, ...)" to ignore PCI devices when system doing pci scan. Change-Id: I506efef0a9d3a20b207770c744c70a013b10de13 Tracked-On:218445 Signed-off-by: Yonghua Huang Signed-off-by: Jason Chen CJ Reviewed-on: Reviewed-by: Chi, Mingqiang Reviewed-by: Dong, Eddie Tested-by: Dong, Eddie --- drivers/pci/probe.c | 69 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 69 insertions(+) diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index 201f9e5ff55c..b9dda363aea6 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -42,6 +42,70 @@ struct pci_domain_busn_res { int domain_nr; }; +#define PCI_IGNORE_MAX 8 + +static u16 devices_ignore_table[PCI_IGNORE_MAX]; +static int devices_ignore_cnt; + +static void parse_ignore_device(char *bdf_str) +{ + int fields; + unsigned int bus; + unsigned int dev; + unsigned int func; + + if (devices_ignore_cnt >= PCI_IGNORE_MAX - 1) + return; + + fields = sscanf(bdf_str, "%x:%x:%x", &bus, &dev, &func); + if (fields != 3) + return; + + devices_ignore_table[devices_ignore_cnt++] = + PCI_DEVID(bus, PCI_DEVFN(dev, func)); +} + +static int __init pci_deivces_ignore(char *str) +{ + int len; + char *start, *end; + char bdf[16]; + + devices_ignore_cnt = 0; + + while ((start = strchr(str, '('))) { + + end = strchr(start, ')'); + if (end == NULL) + break; + + len = end - start - 1; + if (len >= 16) /*invalid string*/ + break; + + memcpy((void *)bdf, (void *)(start+1), len); + bdf[len] = '\0'; + parse_ignore_device(bdf); + str = end + 1; + } + + return 1; +} +__setup("pci_devices_ignore=", pci_deivces_ignore); + +static bool device_on_ignore_list(int bus, int dev, int func) +{ + int i; + + for (i = 0; i < devices_ignore_cnt; i++) + if ((PCI_BUS_NUM(devices_ignore_table[i]) == bus) && + (PCI_SLOT(devices_ignore_table[i]) == dev) && + (PCI_FUNC(devices_ignore_table[i]) == func)) + return true; + + return false; +} + static struct resource *get_pci_domain_busn_res(int domain_nr) { struct pci_domain_busn_res *r; @@ -2442,6 +2506,11 @@ struct pci_dev *pci_scan_single_device(struct pci_bus *bus, int devfn) return dev; } + if (device_on_ignore_list(bus->number, + PCI_SLOT(devfn), + PCI_FUNC(devfn))) + return NULL; + dev = pci_scan_device(bus, devfn); if (!dev) return NULL; From 84b7f8e6ef91f05954d6a40418ced6318e1f767f Mon Sep 17 00:00:00 2001 From: Jason Chen CJ Date: Fri, 31 Aug 2018 10:58:54 +0800 Subject: [PATCH 0002/1103] x86: add ACRN hypervisor guest add x86_hyper_acrn into supported hypervisors array, which enabling ACRN services guest run on ACRN hypervisor. And it is restricted to X86_64. Change-Id: Ib9e7d9a8e971d5f32290953b4916dea064de8638 Tracked-On: 218445 Signed-off-by: Jason Chen CJ Signed-off-by: Jack Ren Reviewed-on: Reviewed-by: Chi, Mingqiang Reviewed-by: Dong, Eddie Tested-by: Dong, Eddie --- arch/x86/Kbuild | 2 + arch/x86/Kconfig | 2 + arch/x86/acrn/Kconfig | 15 +++++++ arch/x86/acrn/Makefile | 2 + arch/x86/acrn/acrn.c | 71 +++++++++++++++++++++++++++++++ arch/x86/include/asm/hypervisor.h | 1 + arch/x86/kernel/cpu/hypervisor.c | 4 ++ 7 files changed, 97 insertions(+) create mode 100644 arch/x86/acrn/Kconfig create mode 100644 arch/x86/acrn/Makefile create mode 100644 arch/x86/acrn/acrn.c diff --git a/arch/x86/Kbuild b/arch/x86/Kbuild index 0038a2d10a7a..466219296cd6 100644 --- a/arch/x86/Kbuild +++ b/arch/x86/Kbuild @@ -7,6 +7,8 @@ obj-$(CONFIG_KVM) += kvm/ # Xen paravirtualization support obj-$(CONFIG_XEN) += xen/ +obj-$(CONFIG_ACRN) += acrn/ + # Hyper-V paravirtualization support obj-$(subst m,y,$(CONFIG_HYPERV)) += hyperv/ diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig index 1a0be022f91d..04855e782c63 100644 --- a/arch/x86/Kconfig +++ b/arch/x86/Kconfig @@ -782,6 +782,8 @@ config QUEUED_LOCK_STAT behavior of paravirtualized queued spinlocks and report them on debugfs. +source "arch/x86/acrn/Kconfig" + source "arch/x86/xen/Kconfig" config KVM_GUEST diff --git a/arch/x86/acrn/Kconfig b/arch/x86/acrn/Kconfig new file mode 100644 index 000000000000..0ba9e36c41f3 --- /dev/null +++ b/arch/x86/acrn/Kconfig @@ -0,0 +1,15 @@ +# SPDX-License-Identifier: GPL-2.0 +# +# This Kconfig describes ACRN options +# + +config ACRN + bool "Enable services run on ACRN hypervisor" + depends on X86_64 + depends on PARAVIRT + depends on DMA_CMA + depends on !INTEL_IOMMU + depends on !VMAP_STACK + help + This option is needed if were to run ACRN services linux on top of + ACRN hypervisor. diff --git a/arch/x86/acrn/Makefile b/arch/x86/acrn/Makefile new file mode 100644 index 000000000000..d961d8c5ee93 --- /dev/null +++ b/arch/x86/acrn/Makefile @@ -0,0 +1,2 @@ +# SPDX-License-Identifier: GPL-2.0 +obj-$(CONFIG_ACRN) += acrn.o diff --git a/arch/x86/acrn/acrn.c b/arch/x86/acrn/acrn.c new file mode 100644 index 000000000000..a042b544af33 --- /dev/null +++ b/arch/x86/acrn/acrn.c @@ -0,0 +1,71 @@ +/* + * ACRN hypervisor support + * + * Copyright (C) 2017 Intel Corporation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 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 + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Jason Chen CJ + * + */ +#include + +static uint32_t __init acrn_detect(void) +{ + return hypervisor_cpuid_base("ACRNACRNACRN\0\0", 0); +} + +static void __init acrn_init_platform(void) +{ +} + +static void acrn_pin_vcpu(int cpu) +{ + /* do nothing here now */ +} + +static bool acrn_x2apic_available(void) +{ + /* do not support x2apic */ + return false; +} + +static void __init acrn_init_mem_mapping(void) +{ + /* do nothing here now */ +} + +const struct hypervisor_x86 x86_hyper_acrn = { + .name = "ACRN", + .detect = acrn_detect, + .type = X86_HYPER_ACRN, + .init.init_platform = acrn_init_platform, + .runtime.pin_vcpu = acrn_pin_vcpu, + .init.x2apic_available = acrn_x2apic_available, + .init.init_mem_mapping = acrn_init_mem_mapping, +}; +EXPORT_SYMBOL(x86_hyper_acrn); diff --git a/arch/x86/include/asm/hypervisor.h b/arch/x86/include/asm/hypervisor.h index 8c5aaba6633f..50a30f6c668b 100644 --- a/arch/x86/include/asm/hypervisor.h +++ b/arch/x86/include/asm/hypervisor.h @@ -29,6 +29,7 @@ enum x86_hypervisor_type { X86_HYPER_XEN_HVM, X86_HYPER_KVM, X86_HYPER_JAILHOUSE, + X86_HYPER_ACRN, }; #ifdef CONFIG_HYPERVISOR_GUEST diff --git a/arch/x86/kernel/cpu/hypervisor.c b/arch/x86/kernel/cpu/hypervisor.c index 479ca4728de0..5a6f072e6748 100644 --- a/arch/x86/kernel/cpu/hypervisor.c +++ b/arch/x86/kernel/cpu/hypervisor.c @@ -32,6 +32,7 @@ extern const struct hypervisor_x86 x86_hyper_xen_pv; extern const struct hypervisor_x86 x86_hyper_xen_hvm; extern const struct hypervisor_x86 x86_hyper_kvm; extern const struct hypervisor_x86 x86_hyper_jailhouse; +extern const struct hypervisor_x86 x86_hyper_acrn; static const __initconst struct hypervisor_x86 * const hypervisors[] = { @@ -49,6 +50,9 @@ static const __initconst struct hypervisor_x86 * const hypervisors[] = #ifdef CONFIG_JAILHOUSE_GUEST &x86_hyper_jailhouse, #endif +#ifdef CONFIG_ACRN + &x86_hyper_acrn, +#endif }; enum x86_hypervisor_type x86_hyper_type; From c9a0b20893cce13bc1cb11efddbee52bcb99d9e3 Mon Sep 17 00:00:00 2001 From: liang ding Date: Fri, 31 Aug 2018 10:58:55 +0800 Subject: [PATCH 0003/1103] VHM: add vhm char device driver VHM(virtio and hypervisor service module) is the important middle layer to run virtio and hypervisor services in linux kernel for ACRN hypervisor. The vhm char device is the main interface. It provides ioctls to applications and interacts with ACRN hypervisor through different hypercalls. This patch enable ACRN vhm service based on CONFIG_ACRN; added a basic vhm char device which contains services for VM management in drivers/char/vhm; and located vhm service lib in drivers/vhm. Change-Id: Ib6c95d810581abd226692cbec9649a24b466a93b Tracked-On: 218445 Signed-off-by: liang ding Signed-off-by: Jason Zeng Signed-off-by: Xiao Zheng Signed-off-by: Jason Chen CJ Signed-off-by: Jack Ren Signed-off-by: Mingqiang Chi Reviewed-on: Reviewed-by: Dong, Eddie Tested-by: Dong, Eddie --- drivers/Makefile | 1 + drivers/char/Makefile | 1 + drivers/char/vhm/Makefile | 1 + drivers/char/vhm/vhm_dev.c | 247 +++++++++++++++++++++++++++++ drivers/vhm/Makefile | 1 + drivers/vhm/vhm_hypercall.c | 134 ++++++++++++++++ drivers/vhm/vhm_vm_mngt.c | 107 +++++++++++++ include/linux/vhm/acrn_common.h | 65 ++++++++ include/linux/vhm/acrn_hv_defs.h | 81 ++++++++++ include/linux/vhm/vhm_hypercall.h | 148 +++++++++++++++++ include/linux/vhm/vhm_ioctl_defs.h | 67 ++++++++ include/linux/vhm/vhm_vm_mngt.h | 78 +++++++++ 12 files changed, 931 insertions(+) create mode 100644 drivers/char/vhm/Makefile create mode 100644 drivers/char/vhm/vhm_dev.c create mode 100644 drivers/vhm/Makefile create mode 100644 drivers/vhm/vhm_hypercall.c create mode 100644 drivers/vhm/vhm_vm_mngt.c create mode 100644 include/linux/vhm/acrn_common.h create mode 100644 include/linux/vhm/acrn_hv_defs.h create mode 100644 include/linux/vhm/vhm_hypercall.h create mode 100644 include/linux/vhm/vhm_ioctl_defs.h create mode 100644 include/linux/vhm/vhm_vm_mngt.h diff --git a/drivers/Makefile b/drivers/Makefile index 578f469f72fb..07528374561a 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -183,6 +183,7 @@ obj-$(CONFIG_FPGA) += fpga/ obj-$(CONFIG_FSI) += fsi/ obj-$(CONFIG_TEE) += tee/ obj-$(CONFIG_MULTIPLEXER) += mux/ +obj-$(CONFIG_ACRN) += vhm/ obj-$(CONFIG_UNISYS_VISORBUS) += visorbus/ obj-$(CONFIG_SIOX) += siox/ obj-$(CONFIG_GNSS) += gnss/ diff --git a/drivers/char/Makefile b/drivers/char/Makefile index b8d42b4e979b..39eccecf0d3a 100644 --- a/drivers/char/Makefile +++ b/drivers/char/Makefile @@ -58,3 +58,4 @@ js-rtc-y = rtc.o obj-$(CONFIG_XILLYBUS) += xillybus/ obj-$(CONFIG_POWERNV_OP_PANEL) += powernv-op-panel.o obj-$(CONFIG_ADI) += adi.o +obj-$(CONFIG_ACRN) += vhm/ diff --git a/drivers/char/vhm/Makefile b/drivers/char/vhm/Makefile new file mode 100644 index 000000000000..cb801c70a37e --- /dev/null +++ b/drivers/char/vhm/Makefile @@ -0,0 +1 @@ +obj-y += vhm_dev.o diff --git a/drivers/char/vhm/vhm_dev.c b/drivers/char/vhm/vhm_dev.c new file mode 100644 index 000000000000..527e90b187cf --- /dev/null +++ b/drivers/char/vhm/vhm_dev.c @@ -0,0 +1,247 @@ +/* + * virtio and hyperviosr service module (VHM): main framework + * + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright (c) 2017 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * 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. + * + * BSD LICENSE + * + * Copyright (C) 2017 Intel Corporation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 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 + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Liang Ding + * Jason Zeng + * Xiao Zheng + * Jason Chen CJ + * Jack Ren + * Mingqiang Chi + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#define DEVICE_NAME "acrn_vhm" +#define CLASS_NAME "vhm" + +static int major; +static struct class *vhm_class; +static struct device *vhm_device; + +static int vhm_dev_open(struct inode *inodep, struct file *filep) +{ + struct vhm_vm *vm; + + vm = kzalloc(sizeof(struct vhm_vm), GFP_KERNEL); + pr_info("vhm_dev_open: opening device node\n"); + + if (!vm) + return -ENOMEM; + vm->vmid = ACRN_INVALID_VMID; + vm->dev = vhm_device; + + vm_mutex_lock(&vhm_vm_list_lock); + vm->refcnt = 1; + vm_list_add(&vm->list); + vm_mutex_unlock(&vhm_vm_list_lock); + filep->private_data = vm; + return 0; +} + +static ssize_t vhm_dev_read(struct file *filep, char *buffer, size_t len, + loff_t *offset) +{ + /* Does Nothing */ + pr_info("vhm_dev_read: reading device node\n"); + return 0; +} + +static ssize_t vhm_dev_write(struct file *filep, const char *buffer, + size_t len, loff_t *offset) +{ + /* Does Nothing */ + pr_info("vhm_dev_read: writing device node\n"); + return 0; +} + +static long vhm_dev_ioctl(struct file *filep, + unsigned int ioctl_num, unsigned long ioctl_param) +{ + long ret = 0; + struct vhm_vm *vm; + + trace_printk("[%s] ioctl_num=0x%x\n", __func__, ioctl_num); + + vm = (struct vhm_vm *)filep->private_data; + if (vm == NULL) { + pr_err("vhm: invalid VM !\n"); + return -EFAULT; + } + if ((vm->vmid == ACRN_INVALID_VMID) && (ioctl_num != IC_CREATE_VM)) { + pr_err("vhm: invalid VM ID !\n"); + return -EFAULT; + } + + switch (ioctl_num) { + case IC_CREATE_VM: + ret = vhm_create_vm(vm, ioctl_param); + break; + + case IC_RESUME_VM: + ret = vhm_resume_vm(vm); + break; + + case IC_PAUSE_VM: + ret = vhm_pause_vm(vm); + break; + + case IC_DESTROY_VM: + ret = vhm_destroy_vm(vm); + break; + + case IC_QUERY_VMSTATE: + ret = vhm_query_vm_state(vm); + break; + + default: + pr_warn("Unknown IOCTL 0x%x\n", ioctl_num); + ret = 0; + break; + } + + return ret; +} + +static int vhm_dev_release(struct inode *inodep, struct file *filep) +{ + struct vhm_vm *vm = filep->private_data; + + if (vm == NULL) { + pr_err("vhm: invalid VM !\n"); + return -EFAULT; + } + put_vm(vm); + filep->private_data = NULL; + return 0; +} + +static const struct file_operations fops = { + .open = vhm_dev_open, + .read = vhm_dev_read, + .write = vhm_dev_write, + .release = vhm_dev_release, + .unlocked_ioctl = vhm_dev_ioctl, +}; + +static int __init vhm_init(void) +{ + pr_info("vhm: initializing\n"); + + /* Try to dynamically allocate a major number for the device */ + major = register_chrdev(0, DEVICE_NAME, &fops); + if (major < 0) { + pr_warn("vhm: failed to register a major number\n"); + return major; + } + pr_info("vhm: registered correctly with major number %d\n", major); + + /* Register the device class */ + vhm_class = class_create(THIS_MODULE, CLASS_NAME); + if (IS_ERR(vhm_class)) { + unregister_chrdev(major, DEVICE_NAME); + pr_warn("vhm: failed to register device class\n"); + return PTR_ERR(vhm_class); + } + pr_info("vhm: device class registered correctly\n"); + + /* Register the device driver */ + vhm_device = device_create(vhm_class, NULL, MKDEV(major, 0), + NULL, DEVICE_NAME); + if (IS_ERR(vhm_device)) { + class_destroy(vhm_class); + unregister_chrdev(major, DEVICE_NAME); + pr_warn("vhm: failed to create the device\n"); + return PTR_ERR(vhm_device); + } + + pr_info("vhm: Virtio & Hypervisor service module initialized\n"); + return 0; +} +static void __exit vhm_exit(void) +{ + device_destroy(vhm_class, MKDEV(major, 0)); + class_unregister(vhm_class); + class_destroy(vhm_class); + unregister_chrdev(major, DEVICE_NAME); + pr_info("vhm: exit\n"); +} + +module_init(vhm_init); +module_exit(vhm_exit); + +MODULE_AUTHOR("Intel"); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("This is a char device driver, acts as a route " + "responsible for transferring IO requsts from other modules " + "either in user-space or in kernel to and from hypervisor"); +MODULE_VERSION("0.1"); diff --git a/drivers/vhm/Makefile b/drivers/vhm/Makefile new file mode 100644 index 000000000000..220697aaccb7 --- /dev/null +++ b/drivers/vhm/Makefile @@ -0,0 +1 @@ +obj-y += vhm_vm_mngt.o vhm_hypercall.o diff --git a/drivers/vhm/vhm_hypercall.c b/drivers/vhm/vhm_hypercall.c new file mode 100644 index 000000000000..ddc085d0fa11 --- /dev/null +++ b/drivers/vhm/vhm_hypercall.c @@ -0,0 +1,134 @@ +/* + * virtio and hyperviosr service module (VHM): hypercall wrap + * + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright (c) 2017 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * 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. + * + * BSD LICENSE + * + * Copyright (C) 2017 Intel Corporation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 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 + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +#include +#include +#include +#include + +inline long vhm_create_vm(struct vhm_vm *vm, unsigned long ioctl_param) +{ + long ret = 0; + struct acrn_create_vm created_vm; + + if (copy_from_user(&created_vm, (void *)ioctl_param, + sizeof(struct acrn_create_vm))) + return -EFAULT; + + ret = acrn_hypercall2(HC_CREATE_VM, 0, + virt_to_phys(&created_vm)); + if ((ret < 0) || + (created_vm.vmid == ACRN_INVALID_VMID)) { + pr_err("vhm: failed to create VM from Hypervisor !\n"); + return -EFAULT; + } + + if (copy_to_user((void *)ioctl_param, &created_vm, + sizeof(struct acrn_create_vm))) + return -EFAULT; + + vm->vmid = created_vm.vmid; + pr_info("vhm: VM %ld created\n", created_vm.vmid); + + return ret; +} + +inline long vhm_resume_vm(struct vhm_vm *vm) +{ + long ret = 0; + + ret = acrn_hypercall1(HC_RESUME_VM, vm->vmid); + if (ret < 0) { + pr_err("vhm: failed to start VM %ld!\n", vm->vmid); + return -EFAULT; + } + + return ret; +} + +inline long vhm_pause_vm(struct vhm_vm *vm) +{ + long ret = 0; + + ret = acrn_hypercall1(HC_PAUSE_VM, vm->vmid); + if (ret < 0) { + pr_err("vhm: failed to pause VM %ld!\n", vm->vmid); + return -EFAULT; + } + + return ret; +} + +inline long vhm_destroy_vm(struct vhm_vm *vm) +{ + long ret = 0; + + ret = acrn_hypercall1(HC_DESTROY_VM, vm->vmid); + if (ret < 0) { + pr_err("failed to destroy VM %ld\n", vm->vmid); + return -EFAULT; + } + vm->vmid = ACRN_INVALID_VMID; + + return ret; +} + +inline long vhm_query_vm_state(struct vhm_vm *vm) +{ + long ret = 0; + + ret = acrn_hypercall1(HC_QUERY_VMSTATE, vm->vmid); + if (ret < 0) { + pr_err("vhm: failed to query VM State%ld!\n", vm->vmid); + return -EFAULT; + } + + return ret; +} diff --git a/drivers/vhm/vhm_vm_mngt.c b/drivers/vhm/vhm_vm_mngt.c new file mode 100644 index 000000000000..61db04b57362 --- /dev/null +++ b/drivers/vhm/vhm_vm_mngt.c @@ -0,0 +1,107 @@ +/* + * virtio and hyperviosr service module (VHM): vm management + * + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright (c) 2017 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * 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. + * + * BSD LICENSE + * + * Copyright (C) 2017 Intel Corporation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 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 + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Liang Ding + * Jason Zeng + * + */ + +#include +#include +#include +#include +#include +#include +#include + +LIST_HEAD(vhm_vm_list); +DEFINE_MUTEX(vhm_vm_list_lock); + +struct vhm_vm *find_get_vm(unsigned long vmid) +{ + struct vhm_vm *vm; + + mutex_lock(&vhm_vm_list_lock); + list_for_each_entry(vm, &vhm_vm_list, list) { + if (vm->vmid == vmid) { + vm->refcnt++; + mutex_unlock(&vhm_vm_list_lock); + return vm; + } + } + mutex_unlock(&vhm_vm_list_lock); + return NULL; +} + +void put_vm(struct vhm_vm *vm) +{ + mutex_lock(&vhm_vm_list_lock); + vm->refcnt--; + if (vm->refcnt == 0) { + list_del(&vm->list); + kfree(vm); + pr_info("vhm: freed vm\n"); + } + mutex_unlock(&vhm_vm_list_lock); +} + +void vm_list_add(struct list_head *list) +{ + list_add(list, &vhm_vm_list); +} + +void vm_mutex_lock(struct mutex *mlock) +{ + mutex_lock(mlock); +} + +void vm_mutex_unlock(struct mutex *mlock) +{ + mutex_unlock(mlock); +} diff --git a/include/linux/vhm/acrn_common.h b/include/linux/vhm/acrn_common.h new file mode 100644 index 000000000000..08e47732f4d0 --- /dev/null +++ b/include/linux/vhm/acrn_common.h @@ -0,0 +1,65 @@ +/* + * virtio and hyperviosr service module (VHM): commom.h + * + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright (c) 2017 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * 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. + * + * BSD LICENSE + * + * Copyright (C) 2017 Intel Corporation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 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 + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef ACRN_COMMON_H +#define ACRN_COMMON_H + +/* + * Commmon structures for ACRN/VHM/DM + */ + +/* Common API params */ +struct acrn_create_vm { + unsigned long vmid; /* OUT: HV return vmid to VHM */ + unsigned long vcpu_num; /* IN: VM vcpu number */ +} __attribute__((aligned(8))); + +#endif /* ACRN_COMMON_H */ diff --git a/include/linux/vhm/acrn_hv_defs.h b/include/linux/vhm/acrn_hv_defs.h new file mode 100644 index 000000000000..f338a8fbad3d --- /dev/null +++ b/include/linux/vhm/acrn_hv_defs.h @@ -0,0 +1,81 @@ +/* + * virtio and hyperviosr service module (VHM): hypercall header + * + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright (c) 2017 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * 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. + * + * BSD LICENSE + * + * Copyright (C) 2017 Intel Corporation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 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 + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef ACRN_HV_DEFS_H +#define ACRN_HV_DEFS_H + +/* + * Commmon structures for ACRN/VHM/DM + */ +#include "acrn_common.h" + +/* + * Commmon structures for HV/VHM + */ + +#define _HC_ID(x, y) (((x)<<24)|(y)) + +#define HC_ID 0x7FUL + +/* VM management */ +#define HC_ID_VM_BASE 0x0UL +#define HC_GET_API_VERSION _HC_ID(HC_ID, HC_ID_VM_BASE + 0x00) +#define HC_CREATE_VM _HC_ID(HC_ID, HC_ID_VM_BASE + 0x01) +#define HC_DESTROY_VM _HC_ID(HC_ID, HC_ID_VM_BASE + 0x02) +#define HC_RESUME_VM _HC_ID(HC_ID, HC_ID_VM_BASE + 0x03) +#define HC_PAUSE_VM _HC_ID(HC_ID, HC_ID_VM_BASE + 0x04) +#define HC_QUERY_VMSTATE _HC_ID(HC_ID, HC_ID_VM_BASE + 0x05) + +#define ACRN_DOM0_VMID (0UL) +#define ACRN_INVALID_VMID (-1UL) +#define ACRN_INVALID_HPA (-1UL) + +#endif /* ACRN_HV_DEFS_H */ diff --git a/include/linux/vhm/vhm_hypercall.h b/include/linux/vhm/vhm_hypercall.h new file mode 100644 index 000000000000..c07163dbc3bd --- /dev/null +++ b/include/linux/vhm/vhm_hypercall.h @@ -0,0 +1,148 @@ +/* + * virtio and hyperviosr service module (VHM): hypercall.h + * + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright (c) 2017 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * 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. + * + * BSD LICENSE + * + * Copyright (C) 2017 Intel Corporation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 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 + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef VHM_HYPERCALL_H +#define VHM_HYPERCALL_H + +#include + +static inline long acrn_hypercall0(unsigned long hyp_id) +{ + + /* x86-64 System V ABI register usage */ + register signed long result asm("rax"); + register unsigned long r8 asm("r8") = hyp_id; + + /* Execute vmcall */ + asm volatile(".byte 0x0F,0x01,0xC1\n" + : "=r"(result) + : "r"(r8)); + + /* Return result to caller */ + return result; +} + +static inline long acrn_hypercall1(unsigned long hyp_id, unsigned long param1) +{ + + /* x86-64 System V ABI register usage */ + register signed long result asm("rax"); + register unsigned long r8 asm("r8") = hyp_id; + + /* Execute vmcall */ + asm volatile(".byte 0x0F,0x01,0xC1\n" + : "=r"(result) + : "D"(param1), "r"(r8)); + + /* Return result to caller */ + return result; +} + +static inline long acrn_hypercall2(unsigned long hyp_id, unsigned long param1, + unsigned long param2) +{ + + /* x86-64 System V ABI register usage */ + register signed long result asm("rax"); + register unsigned long r8 asm("r8") = hyp_id; + + /* Execute vmcall */ + asm volatile(".byte 0x0F,0x01,0xC1\n" + : "=r"(result) + : "D"(param1), "S"(param2), "r"(r8)); + + /* Return result to caller */ + return result; +} + +static inline long acrn_hypercall3(unsigned long hyp_id, unsigned long param1, + unsigned long param2, unsigned long param3) +{ + + /* x86-64 System V ABI register usage */ + register signed long result asm("rax"); + register unsigned long r8 asm("r8") = hyp_id; + + /* Execute vmcall */ + asm volatile(".byte 0x0F,0x01,0xC1\n" + : "=r"(result) + : "D"(param1), "S"(param2), "d"(param3), "r"(r8)); + + /* Return result to caller */ + return result; +} + +static inline long acrn_hypercall4(unsigned long hyp_id, unsigned long param1, + unsigned long param2, unsigned long param3, + unsigned long param4) +{ + + /* x86-64 System V ABI register usage */ + register signed long result asm("rax"); + register unsigned long r8 asm("r8") = hyp_id; + + /* Execute vmcall */ + asm volatile(".byte 0x0F,0x01,0xC1\n" + : "=r"(result) + : "D"(param1), "S"(param2), "d"(param3), + "c"(param4), "r"(r8)); + + /* Return result to caller */ + return result; +} + +inline long vhm_create_vm(struct vhm_vm *vm, unsigned long ioctl_param); +inline long vhm_resume_vm(struct vhm_vm *vm); +inline long vhm_pause_vm(struct vhm_vm *vm); +inline long vhm_destroy_vm(struct vhm_vm *vm); +inline long vhm_query_vm_state(struct vhm_vm *vm); + +#endif /* VHM_HYPERCALL_H */ diff --git a/include/linux/vhm/vhm_ioctl_defs.h b/include/linux/vhm/vhm_ioctl_defs.h new file mode 100644 index 000000000000..d8c81b6e9306 --- /dev/null +++ b/include/linux/vhm/vhm_ioctl_defs.h @@ -0,0 +1,67 @@ +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright (c) 2017 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * 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. + * + * BSD LICENSE + * + * Copyright (C) 2017 Intel Corporation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY NETAPP, INC ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL NETAPP, INC OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * 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 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#ifndef _VHM_IOCTL_DEFS_H_ +#define _VHM_IOCTL_DEFS_H_ + +/* Commmon structures for ACRN/VHM/DM */ +#include "acrn_common.h" + +/* + * Commmon IOCTL ID defination for VHM/DM + */ +#define _IC_ID(x, y) (((x)<<24)|(y)) +#define IC_ID 0x5FUL + +/* VM management */ +#define IC_ID_VM_BASE 0x0UL +#define IC_GET_API_VERSION _IC_ID(IC_ID, IC_ID_VM_BASE + 0x00) +#define IC_CREATE_VM _IC_ID(IC_ID, IC_ID_VM_BASE + 0x01) +#define IC_DESTROY_VM _IC_ID(IC_ID, IC_ID_VM_BASE + 0x02) +#define IC_RESUME_VM _IC_ID(IC_ID, IC_ID_VM_BASE + 0x03) +#define IC_PAUSE_VM _IC_ID(IC_ID, IC_ID_VM_BASE + 0x04) +#define IC_QUERY_VMSTATE _IC_ID(IC_ID, IC_ID_VM_BASE + 0x05) + +#endif /* VHM_IOCTL_DEFS_H */ diff --git a/include/linux/vhm/vhm_vm_mngt.h b/include/linux/vhm/vhm_vm_mngt.h new file mode 100644 index 000000000000..dcb246af561a --- /dev/null +++ b/include/linux/vhm/vhm_vm_mngt.h @@ -0,0 +1,78 @@ +/* + * virtio and hyperviosr service module (VHM): vm management + * + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright (c) 2017 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * 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. + * + * BSD LICENSE + * + * Copyright (C) 2017 Intel Corporation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 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 + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Liang Ding + * Jason Zeng + * Xiao Zheng + * Jason Chen CJ + * + */ +#ifndef VHM_VM_MNGT_H +#define VHM_VM_MNGT_H + +#include + +extern struct list_head vhm_vm_list; +extern struct mutex vhm_vm_list_lock; + +struct vhm_vm { + struct device *dev; + struct list_head list; + unsigned long vmid; + long refcnt; +}; + +struct vhm_vm *find_get_vm(unsigned long vmid); +void put_vm(struct vhm_vm *vm); + +void vm_list_add(struct list_head *list); +void vm_mutex_lock(struct mutex *mlock); +void vm_mutex_unlock(struct mutex *mlock); + +#endif From cec9c8e107078aef67f705f81e67856e4186de1a Mon Sep 17 00:00:00 2001 From: Jason Chen CJ Date: Fri, 31 Aug 2018 10:58:55 +0800 Subject: [PATCH 0004/1103] VHM: add guest memory management support VHM provides guest memory management services for application. It allocates/frees contiguous physical memory for guest based on dma-cma, and provides corresponding EPT mapping for the allocated memory segment. Change-Id: Ibbe26b0ccf8436700f44bca899b1ee38c2e4ef72 Tracked-On: 218445 Signed-off-by: liang ding Signed-off-by: Jason Chen CJ Signed-off-by: Jason Zeng Signed-off-by: Xiao Zheng Signed-off-by: Mingqiang Chi Reviewed-on: Reviewed-by: Dong, Eddie Tested-by: Dong, Eddie --- drivers/char/vhm/vhm_dev.c | 26 +++ drivers/vhm/Makefile | 2 +- drivers/vhm/vhm_hypercall.c | 5 + drivers/vhm/vhm_mm.c | 364 +++++++++++++++++++++++++++++ drivers/vhm/vhm_vm_mngt.c | 2 + include/linux/vhm/acrn_hv_defs.h | 26 +++ include/linux/vhm/acrn_vhm_mm.h | 88 +++++++ include/linux/vhm/vhm_hypercall.h | 1 + include/linux/vhm/vhm_ioctl_defs.h | 40 ++++ include/linux/vhm/vhm_vm_mngt.h | 2 + 10 files changed, 555 insertions(+), 1 deletion(-) create mode 100644 drivers/vhm/vhm_mm.c create mode 100644 include/linux/vhm/acrn_vhm_mm.h diff --git a/drivers/char/vhm/vhm_dev.c b/drivers/char/vhm/vhm_dev.c index 527e90b187cf..3ea8de27cb3e 100644 --- a/drivers/char/vhm/vhm_dev.c +++ b/drivers/char/vhm/vhm_dev.c @@ -78,6 +78,7 @@ #include #include +#include #include #include @@ -100,6 +101,9 @@ static int vhm_dev_open(struct inode *inodep, struct file *filep) vm->vmid = ACRN_INVALID_VMID; vm->dev = vhm_device; + INIT_LIST_HEAD(&vm->memseg_list); + mutex_init(&vm->seg_lock); + vm_mutex_lock(&vhm_vm_list_lock); vm->refcnt = 1; vm_list_add(&vm->list); @@ -163,6 +167,27 @@ static long vhm_dev_ioctl(struct file *filep, ret = vhm_query_vm_state(vm); break; + case IC_ALLOC_MEMSEG: { + struct vm_memseg memseg; + + if (copy_from_user(&memseg, (void *)ioctl_param, + sizeof(struct vm_memseg))) + return -EFAULT; + + return alloc_guest_memseg(vm, &memseg); + } + + case IC_SET_MEMSEG: { + struct vm_memmap memmap; + + if (copy_from_user(&memmap, (void *)ioctl_param, + sizeof(struct vm_memmap))) + return -EFAULT; + + ret = map_guest_memseg(vm, &memmap); + break; + } + default: pr_warn("Unknown IOCTL 0x%x\n", ioctl_num); ret = 0; @@ -189,6 +214,7 @@ static const struct file_operations fops = { .open = vhm_dev_open, .read = vhm_dev_read, .write = vhm_dev_write, + .mmap = vhm_dev_mmap, .release = vhm_dev_release, .unlocked_ioctl = vhm_dev_ioctl, }; diff --git a/drivers/vhm/Makefile b/drivers/vhm/Makefile index 220697aaccb7..7e5ec421fbc7 100644 --- a/drivers/vhm/Makefile +++ b/drivers/vhm/Makefile @@ -1 +1 @@ -obj-y += vhm_vm_mngt.o vhm_hypercall.o +obj-y += vhm_mm.o vhm_vm_mngt.o vhm_hypercall.o diff --git a/drivers/vhm/vhm_hypercall.c b/drivers/vhm/vhm_hypercall.c index ddc085d0fa11..d80087bcb5fb 100644 --- a/drivers/vhm/vhm_hypercall.c +++ b/drivers/vhm/vhm_hypercall.c @@ -53,6 +53,11 @@ #include #include +inline long hcall_set_memmap(unsigned long vmid, unsigned long memmap) +{ + return acrn_hypercall2(HC_VM_SET_MEMMAP, vmid, memmap); +} + inline long vhm_create_vm(struct vhm_vm *vm, unsigned long ioctl_param) { long ret = 0; diff --git a/drivers/vhm/vhm_mm.c b/drivers/vhm/vhm_mm.c new file mode 100644 index 000000000000..9dd0b9414d3a --- /dev/null +++ b/drivers/vhm/vhm_mm.c @@ -0,0 +1,364 @@ +/* + * virtio and hyperviosr service module (VHM): memory map + * + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright (c) 2017 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * 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. + * + * BSD LICENSE + * + * Copyright (C) 2017 Intel Corporation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 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 + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * + * Jason Zeng + * Jason Chen CJ + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +struct guest_memseg { + struct list_head list; + int segid; + u64 base; + size_t len; + char name[SPECNAMELEN + 1]; + u64 gpa; + int prot; /* RWX */ + long vma_count; +}; + +static u64 _alloc_memblk(struct device *dev, size_t len) +{ + unsigned int count; + struct page *page; + + if (!PAGE_ALIGNED(len)) { + pr_warn("alloc size of memblk must be page aligned\n"); + return 0ULL; + } + + count = PAGE_ALIGN(len) >> PAGE_SHIFT; + page = dma_alloc_from_contiguous(dev, count, 1, GFP_KERNEL); + if (page) + return page_to_phys(page); + else + return 0ULL; +} + +static bool _free_memblk(struct device *dev, u64 base, size_t len) +{ + unsigned int count = PAGE_ALIGN(len) >> PAGE_SHIFT; + struct page *page = pfn_to_page(base >> PAGE_SHIFT); + + return dma_release_from_contiguous(dev, page, count); +} + +int alloc_guest_memseg(struct vhm_vm *vm, struct vm_memseg *memseg) +{ + struct guest_memseg *seg; + u64 base; + + seg = kzalloc(sizeof(struct guest_memseg), GFP_KERNEL); + if (seg == NULL) + return -ENOMEM; + + base = _alloc_memblk(vm->dev, memseg->len); + if (base == 0ULL) { + kfree(seg); + return -ENOMEM; + } + + seg->segid = memseg->segid; + seg->base = base; + seg->len = memseg->len; + strncpy(seg->name, memseg->name, SPECNAMELEN + 1); + seg->gpa = memseg->gpa; + + pr_info("VHM: alloc memseg[%s] with len=0x%lx, base=0x%llx," + " and its guest gpa = 0x%llx\n", + seg->name, seg->len, seg->base, seg->gpa); + + seg->vma_count = 0; + mutex_lock(&vm->seg_lock); + list_add(&seg->list, &vm->memseg_list); + mutex_unlock(&vm->seg_lock); + + return 0; +} + +static int _mem_set_memmap(unsigned long vmid, unsigned long guest_gpa, + unsigned long host_gpa, unsigned long len, int prot, int type) +{ + struct vm_set_memmap set_memmap; + + set_memmap.type = type; + set_memmap.foreign_gpa = guest_gpa; + set_memmap.hvm_gpa = host_gpa; + set_memmap.length = len; + set_memmap.prot = prot; + + /* hypercall to notify hv the guest EPT setting*/ + if (hcall_set_memmap(vmid, + virt_to_phys(&set_memmap)) < 0) { + pr_err("vhm: failed to set memmap %ld!\n", vmid); + return -EFAULT; + } + + pr_debug("VHM: set ept for mem map[type=0x%x, host_gpa=0x%lx," + "guest_gpa=0x%lx,len=0x%lx, prot=0x%x]\n", + type, host_gpa, guest_gpa, len, prot); + + return 0; +} + +int set_mmio_map(unsigned long vmid, unsigned long guest_gpa, + unsigned long host_gpa, unsigned long len, int prot) +{ + return _mem_set_memmap(vmid, guest_gpa, host_gpa, len, + prot, MAP_MMIO); +} + +int unset_mmio_map(unsigned long vmid, unsigned long guest_gpa, + unsigned long host_gpa, unsigned long len, int prot) +{ + return _mem_set_memmap(vmid, guest_gpa, host_gpa, len, + prot, MAP_UNMAP); +} + +int update_mem_map(unsigned long vmid, unsigned long guest_gpa, + unsigned long host_gpa, unsigned long len, int prot) +{ + return _mem_set_memmap(vmid, guest_gpa, host_gpa, len, + prot, MAP_UPDATE); +} + +int map_guest_memseg(struct vhm_vm *vm, struct vm_memmap *memmap) +{ + struct guest_memseg *seg = NULL; + struct vm_set_memmap set_memmap; + + mutex_lock(&vm->seg_lock); + + if (memmap->segid != VM_MMIO) { + list_for_each_entry(seg, &vm->memseg_list, list) { + if (seg->segid == memmap->segid + && seg->gpa == memmap->mem.gpa + && seg->len == memmap->mem.len) + break; + } + if (&seg->list == &vm->memseg_list) { + mutex_unlock(&vm->seg_lock); + return -EINVAL; + } + seg->prot = memmap->mem.prot; + set_memmap.type = MAP_MEM; + set_memmap.foreign_gpa = seg->gpa; + set_memmap.hvm_gpa = seg->base; + set_memmap.length = seg->len; + set_memmap.prot = seg->prot; + set_memmap.prot |= MMU_MEM_ATTR_WB_CACHE; + } else { + set_memmap.type = MAP_MMIO; + set_memmap.foreign_gpa = memmap->mmio.gpa; + set_memmap.hvm_gpa = memmap->mmio.hpa; + set_memmap.length = memmap->mmio.len; + set_memmap.prot = memmap->mmio.prot; + set_memmap.prot |= MMU_MEM_ATTR_UNCACHED; + } + + /* hypercall to notify hv the guest EPT setting*/ + if (hcall_set_memmap(vm->vmid, virt_to_phys(&set_memmap)) < 0) { + pr_err("vhm: failed to set memmap %ld!\n", vm->vmid); + mutex_unlock(&vm->seg_lock); + return -EFAULT; + } + + mutex_unlock(&vm->seg_lock); + + if (memmap->segid != VM_MMIO) + pr_debug("VHM: set ept for memseg [hvm_gpa=0x%llx," + "guest_gpa=0x%llx,len=0x%lx, prot=0x%x]\n", + seg->base, seg->gpa, seg->len, seg->prot); + else + pr_debug("VHM: set ept for mmio [hpa=0x%llx," + "gpa=0x%llx,len=0x%lx, prot=0x%x]\n", + memmap->mmio.hpa, memmap->mmio.gpa, + memmap->mmio.len, memmap->mmio.prot); + + return 0; +} + +void free_guest_mem(struct vhm_vm *vm) +{ + struct guest_memseg *seg; + + mutex_lock(&vm->seg_lock); + while (!list_empty(&vm->memseg_list)) { + seg = list_first_entry(&vm->memseg_list, + struct guest_memseg, list); + if (!_free_memblk(vm->dev, seg->base, seg->len)) + pr_warn("failed to free memblk\n"); + list_del(&seg->list); + kfree(seg); + } + mutex_unlock(&vm->seg_lock); +} + +int check_guest_mem(struct vhm_vm *vm) +{ + struct guest_memseg *seg; + + mutex_lock(&vm->seg_lock); + list_for_each_entry(seg, &vm->memseg_list, list) { + if (seg->segid != VM_SYSMEM) + continue; + + if (seg->vma_count == 0) + continue; + + mutex_unlock(&vm->seg_lock); + return -EAGAIN; + } + mutex_unlock(&vm->seg_lock); + return 0; +} + +static void guest_vm_open(struct vm_area_struct *vma) +{ + struct vhm_vm *vm = vma->vm_file->private_data; + struct guest_memseg *seg = vma->vm_private_data; + + mutex_lock(&vm->seg_lock); + seg->vma_count++; + mutex_unlock(&vm->seg_lock); +} + +static void guest_vm_close(struct vm_area_struct *vma) +{ + struct vhm_vm *vm = vma->vm_file->private_data; + struct guest_memseg *seg = vma->vm_private_data; + + mutex_lock(&vm->seg_lock); + seg->vma_count--; + BUG_ON(seg->vma_count < 0); + mutex_unlock(&vm->seg_lock); +} + +static const struct vm_operations_struct guest_vm_ops = { + .open = guest_vm_open, + .close = guest_vm_close, +}; + +static int do_mmap_guest(struct file *file, + struct vm_area_struct *vma, struct guest_memseg *seg) +{ + struct page *page; + size_t size = seg->len; + unsigned long pfn; + unsigned long start_addr; + + vma->vm_flags |= VM_MIXEDMAP | VM_DONTEXPAND | VM_DONTCOPY; + pfn = seg->base >> PAGE_SHIFT; + start_addr = vma->vm_start; + while (size > 0) { + page = pfn_to_page(pfn); + if (vm_insert_page(vma, start_addr, page)) + return -EINVAL; + size -= PAGE_SIZE; + start_addr += PAGE_SIZE; + pfn++; + } + seg->vma_count++; + vma->vm_ops = &guest_vm_ops; + vma->vm_private_data = (void *)seg; + + pr_info("VHM: mmap for memseg [seg base=0x%llx, gpa=0x%llx] " + "to start addr 0x%lx\n", + seg->base, seg->gpa, start_addr); + + return 0; +} + +int vhm_dev_mmap(struct file *file, struct vm_area_struct *vma) +{ + struct vhm_vm *vm = file->private_data; + struct guest_memseg *seg; + u64 offset = vma->vm_pgoff << PAGE_SHIFT; + size_t len = vma->vm_end - vma->vm_start; + int ret; + + mutex_lock(&vm->seg_lock); + list_for_each_entry(seg, &vm->memseg_list, list) { + if (seg->segid != VM_SYSMEM) + continue; + + if (seg->gpa != offset || seg->len != len) + continue; + + ret = do_mmap_guest(file, vma, seg); + mutex_unlock(&vm->seg_lock); + return ret; + } + mutex_unlock(&vm->seg_lock); + return -EINVAL; +} diff --git a/drivers/vhm/vhm_vm_mngt.c b/drivers/vhm/vhm_vm_mngt.c index 61db04b57362..3c4e6d2b2f23 100644 --- a/drivers/vhm/vhm_vm_mngt.c +++ b/drivers/vhm/vhm_vm_mngt.c @@ -58,6 +58,7 @@ #include #include #include +#include #include LIST_HEAD(vhm_vm_list); @@ -85,6 +86,7 @@ void put_vm(struct vhm_vm *vm) vm->refcnt--; if (vm->refcnt == 0) { list_del(&vm->list); + free_guest_mem(vm); kfree(vm); pr_info("vhm: freed vm\n"); } diff --git a/include/linux/vhm/acrn_hv_defs.h b/include/linux/vhm/acrn_hv_defs.h index f338a8fbad3d..ab6554d017cb 100644 --- a/include/linux/vhm/acrn_hv_defs.h +++ b/include/linux/vhm/acrn_hv_defs.h @@ -74,8 +74,34 @@ #define HC_PAUSE_VM _HC_ID(HC_ID, HC_ID_VM_BASE + 0x04) #define HC_QUERY_VMSTATE _HC_ID(HC_ID, HC_ID_VM_BASE + 0x05) +/* Guest memory management */ +#define HC_ID_MEM_BASE 0x300UL +#define HC_VM_SET_MEMMAP _HC_ID(HC_ID, HC_ID_MEM_BASE + 0x00) + #define ACRN_DOM0_VMID (0UL) #define ACRN_INVALID_VMID (-1UL) #define ACRN_INVALID_HPA (-1UL) +enum vm_memmap_type { + MAP_MEM = 0, + MAP_MMIO, + MAP_UNMAP, + MAP_UPDATE, +}; + +struct vm_set_memmap { + enum vm_memmap_type type; + /* IN: beginning guest GPA to map */ + unsigned long foreign_gpa; + + /* IN: VM0's GPA which foreign gpa will be mapped to */ + unsigned long hvm_gpa; + + /* IN: length of the range */ + unsigned long length; + + /* IN: not used right now */ + int prot; +} __attribute__((aligned(8))); + #endif /* ACRN_HV_DEFS_H */ diff --git a/include/linux/vhm/acrn_vhm_mm.h b/include/linux/vhm/acrn_vhm_mm.h new file mode 100644 index 000000000000..325f2b2026e8 --- /dev/null +++ b/include/linux/vhm/acrn_vhm_mm.h @@ -0,0 +1,88 @@ +/* + * virtio and hyperviosr service module (VHM): memory map + * + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright (c) 2017 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * 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. + * + * BSD LICENSE + * + * Copyright (C) 2017 Intel Corporation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 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 + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Jason Chen CJ + * + */ + +#ifndef __ACRN_VHM_MM_H__ +#define __ACRN_VHM_MM_H__ + +#include +#include + +#define MMU_MEM_ATTR_READ 0x00000001 +#define MMU_MEM_ATTR_WRITE 0x00000002 +#define MMU_MEM_ATTR_EXECUTE 0x00000004 +#define MMU_MEM_ATTR_WB_CACHE 0x00000040 +#define MMU_MEM_ATTR_WT_CACHE 0x00000080 +#define MMU_MEM_ATTR_UNCACHED 0x00000100 +#define MMU_MEM_ATTR_WC 0x00000200 + +#define MMU_MEM_ATTR_ALL 0x00000007 +#define MMU_MEM_ATTR_WP 0x00000005 +#define MMU_MEM_ATTR_ALL_WB 0x00000047 +#define MMU_MEM_ATTR_ALL_WC 0x00000207 + +int set_mmio_map(unsigned long vmid, unsigned long guest_gpa, + unsigned long host_gpa, unsigned long len, int prot); +int unset_mmio_map(unsigned long vmid, unsigned long guest_gpa, + unsigned long host_gpa, unsigned long len, int prot); +int update_mem_map(unsigned long vmid, unsigned long guest_gpa, + unsigned long host_gpa, unsigned long len, int prot); + +int vhm_dev_mmap(struct file *file, struct vm_area_struct *vma); + +int check_guest_mem(struct vhm_vm *vm); +void free_guest_mem(struct vhm_vm *vm); + +int alloc_guest_memseg(struct vhm_vm *vm, struct vm_memseg *memseg); +int map_guest_memseg(struct vhm_vm *vm, struct vm_memmap *memmap); + +#endif diff --git a/include/linux/vhm/vhm_hypercall.h b/include/linux/vhm/vhm_hypercall.h index c07163dbc3bd..e098a1f959bf 100644 --- a/include/linux/vhm/vhm_hypercall.h +++ b/include/linux/vhm/vhm_hypercall.h @@ -139,6 +139,7 @@ static inline long acrn_hypercall4(unsigned long hyp_id, unsigned long param1, return result; } +inline long hcall_set_memmap(unsigned long vmid, unsigned long memmap); inline long vhm_create_vm(struct vhm_vm *vm, unsigned long ioctl_param); inline long vhm_resume_vm(struct vhm_vm *vm); inline long vhm_pause_vm(struct vhm_vm *vm); diff --git a/include/linux/vhm/vhm_ioctl_defs.h b/include/linux/vhm/vhm_ioctl_defs.h index d8c81b6e9306..872092490259 100644 --- a/include/linux/vhm/vhm_ioctl_defs.h +++ b/include/linux/vhm/vhm_ioctl_defs.h @@ -64,4 +64,44 @@ #define IC_PAUSE_VM _IC_ID(IC_ID, IC_ID_VM_BASE + 0x04) #define IC_QUERY_VMSTATE _IC_ID(IC_ID, IC_ID_VM_BASE + 0x05) +/* Guest memory management */ +#define IC_ID_MEM_BASE 0x300UL +#define IC_ALLOC_MEMSEG _IC_ID(IC_ID, IC_ID_MEM_BASE + 0x00) +#define IC_SET_MEMSEG _IC_ID(IC_ID, IC_ID_MEM_BASE + 0x01) + +#define SPECNAMELEN 63 + +enum { + VM_SYSMEM, + VM_BOOTROM, + VM_FRAMEBUFFER, + VM_MMIO, +}; + +struct vm_memseg { + int segid; + size_t len; + char name[SPECNAMELEN + 1]; + unsigned long gpa; +}; + +struct vm_memmap { + int segid; /* memory segment */ + union { + struct { + uint64_t gpa; + uint64_t segoff; /* offset into memory segment */ + size_t len; /* mmap length */ + int prot; /* RWX */ + int flags; + } mem; + struct { + uint64_t gpa; + uint64_t hpa; + size_t len; + int prot; + } mmio; + }; +}; + #endif /* VHM_IOCTL_DEFS_H */ diff --git a/include/linux/vhm/vhm_vm_mngt.h b/include/linux/vhm/vhm_vm_mngt.h index dcb246af561a..4f1a0db2c54d 100644 --- a/include/linux/vhm/vhm_vm_mngt.h +++ b/include/linux/vhm/vhm_vm_mngt.h @@ -66,6 +66,8 @@ struct vhm_vm { struct list_head list; unsigned long vmid; long refcnt; + struct mutex seg_lock; + struct list_head memseg_list; }; struct vhm_vm *find_get_vm(unsigned long vmid); From 0f8a17a8308d64c9c679666c3f1430417b19242d Mon Sep 17 00:00:00 2001 From: Jason Zeng Date: Fri, 31 Aug 2018 10:58:55 +0800 Subject: [PATCH 0005/1103] VHM: add guest memory remote mapping support There is use case which needs do data operation based on guest physical address. This patch added such support to do remote mapping for guest physical memory. Change-Id: I37755ddcf742129d272f535e99a070965e01c01e Tracked-On: 218445 Signed-off-by: Jason Zeng Signed-off-by: liang ding Signed-off-by: Jason Chen CJ Signed-off-by: Min He Reviewed-on: Reviewed-by: Chi, Mingqiang Reviewed-by: Dong, Eddie Tested-by: Dong, Eddie --- drivers/vhm/vhm_mm.c | 79 +++++++++++++++++++++++++++++++++ include/linux/vhm/acrn_vhm_mm.h | 2 + 2 files changed, 81 insertions(+) diff --git a/drivers/vhm/vhm_mm.c b/drivers/vhm/vhm_mm.c index 9dd0b9414d3a..ea7604b19aaf 100644 --- a/drivers/vhm/vhm_mm.c +++ b/drivers/vhm/vhm_mm.c @@ -362,3 +362,82 @@ int vhm_dev_mmap(struct file *file, struct vm_area_struct *vma) mutex_unlock(&vm->seg_lock); return -EINVAL; } + +static void *do_map_guest_phys(struct vhm_vm *vm, u64 guest_phys, size_t size) +{ + struct guest_memseg *seg; + + mutex_lock(&vm->seg_lock); + list_for_each_entry(seg, &vm->memseg_list, list) { + if (seg->segid != VM_SYSMEM) + continue; + + if (seg->gpa > guest_phys || + guest_phys >= seg->gpa + seg->len) + continue; + + if (guest_phys + size > seg->gpa + seg->len) { + mutex_unlock(&vm->seg_lock); + return NULL; + } + + mutex_unlock(&vm->seg_lock); + return phys_to_virt(seg->base + guest_phys - seg->gpa); + } + mutex_unlock(&vm->seg_lock); + return NULL; +} + +void *map_guest_phys(unsigned long vmid, u64 guest_phys, size_t size) +{ + struct vhm_vm *vm; + void *ret; + + vm = find_get_vm(vmid); + if (vm == NULL) + return NULL; + + ret = do_map_guest_phys(vm, guest_phys, size); + + put_vm(vm); + + return ret; +} +EXPORT_SYMBOL(map_guest_phys); + +static int do_unmap_guest_phys(struct vhm_vm *vm, u64 guest_phys) +{ + struct guest_memseg *seg; + + mutex_lock(&vm->seg_lock); + list_for_each_entry(seg, &vm->memseg_list, list) { + if (seg->segid != VM_SYSMEM) + continue; + + if (seg->gpa <= guest_phys && + guest_phys < seg->gpa + seg->len) { + mutex_unlock(&vm->seg_lock); + return 0; + } + } + mutex_unlock(&vm->seg_lock); + + return -ESRCH; +} + +int unmap_guest_phys(unsigned long vmid, u64 guest_phys) +{ + struct vhm_vm *vm; + int ret; + + vm = find_get_vm(vmid); + if (vm == NULL) { + pr_warn("vm_list corrupted\n"); + return -ESRCH; + } + + ret = do_unmap_guest_phys(vm, guest_phys); + put_vm(vm); + return ret; +} +EXPORT_SYMBOL(unmap_guest_phys); diff --git a/include/linux/vhm/acrn_vhm_mm.h b/include/linux/vhm/acrn_vhm_mm.h index 325f2b2026e8..e701254bc249 100644 --- a/include/linux/vhm/acrn_vhm_mm.h +++ b/include/linux/vhm/acrn_vhm_mm.h @@ -70,6 +70,8 @@ #define MMU_MEM_ATTR_ALL_WB 0x00000047 #define MMU_MEM_ATTR_ALL_WC 0x00000207 +void *map_guest_phys(unsigned long vmid, u64 uos_phys, size_t size); +int unmap_guest_phys(unsigned long vmid, u64 uos_phys); int set_mmio_map(unsigned long vmid, unsigned long guest_gpa, unsigned long host_gpa, unsigned long len, int prot); int unset_mmio_map(unsigned long vmid, unsigned long guest_gpa, From 09a60cd43fdf6eaa86611669c04865ad4afa1aeb Mon Sep 17 00:00:00 2001 From: Jason Chen CJ Date: Fri, 31 Aug 2018 10:58:55 +0800 Subject: [PATCH 0006/1103] VHM: add ioreq service support Once there is an IO request, a virtual irq will be injected into service OS by ACRN hypervisor. The VHM handles this virtual irq (which is based on an ipi vector), parses corresponding IO request from shared IOReq buffer then distributes it to different ioreq client. This patch added ioreq service, and defines IOReq APIs like below: int acrn_ioreq_create_client(unsigned long vmid, ioreq_handler_t handler, char *name); void acrn_ioreq_destroy_client(int client_id); int acrn_ioreq_add_iorange(int client_id, enum request_type type, long start, long end); int acrn_ioreq_del_iorange(int client_id, enum request_type type, long start, long end); struct vhm_request * acrn_ioreq_get_reqbuf(int client_id); int acrn_ioreq_attach_client(int client_id); int acrn_ioreq_distribute_request(struct vhm_vm *vm); int acrn_ioreq_complete_request(int client_id); Change-Id: I828744cb60e1c77543e1fafaa372597173039846 Tracked-On: 218445 Signed-off-by: Jason Chen CJ Signed-off-by: liang ding Signed-off-by: Xiao Zheng Signed-off-by: Mingqiang Chi Reviewed-on: Reviewed-by: Dong, Eddie Tested-by: Dong, Eddie --- drivers/char/vhm/vhm_dev.c | 88 +++ drivers/vhm/Makefile | 2 +- drivers/vhm/vhm_hypercall.c | 10 + drivers/vhm/vhm_ioreq.c | 922 +++++++++++++++++++++++++++++ drivers/vhm/vhm_vm_mngt.c | 2 + include/linux/vhm/acrn_common.h | 117 ++++ include/linux/vhm/acrn_hv_defs.h | 5 + include/linux/vhm/acrn_vhm_ioreq.h | 86 +++ include/linux/vhm/vhm_hypercall.h | 3 + include/linux/vhm/vhm_ioctl_defs.h | 8 + include/linux/vhm/vhm_vm_mngt.h | 5 + 11 files changed, 1247 insertions(+), 1 deletion(-) create mode 100644 drivers/vhm/vhm_ioreq.c create mode 100644 include/linux/vhm/acrn_vhm_ioreq.h diff --git a/drivers/char/vhm/vhm_dev.c b/drivers/char/vhm/vhm_dev.c index 3ea8de27cb3e..3129a8f1503b 100644 --- a/drivers/char/vhm/vhm_dev.c +++ b/drivers/char/vhm/vhm_dev.c @@ -78,6 +78,7 @@ #include #include +#include #include #include #include @@ -88,6 +89,8 @@ static int major; static struct class *vhm_class; static struct device *vhm_device; +static struct tasklet_struct vhm_io_req_tasklet; +static atomic_t ioreq_retry = ATOMIC_INIT(0); static int vhm_dev_open(struct inode *inodep, struct file *filep) { @@ -104,6 +107,9 @@ static int vhm_dev_open(struct inode *inodep, struct file *filep) INIT_LIST_HEAD(&vm->memseg_list); mutex_init(&vm->seg_lock); + INIT_LIST_HEAD(&vm->ioreq_client_list); + spin_lock_init(&vm->ioreq_client_lock); + vm_mutex_lock(&vhm_vm_list_lock); vm->refcnt = 1; vm_list_add(&vm->list); @@ -188,6 +194,50 @@ static long vhm_dev_ioctl(struct file *filep, break; } + case IC_SET_IOREQ_BUFFER: { + /* init ioreq buffer */ + ret = acrn_ioreq_init(vm, (unsigned long)ioctl_param); + if (ret < 0) + return ret; + break; + } + + case IC_CREATE_IOREQ_CLIENT: { + int client_id; + + client_id = acrn_ioreq_create_fallback_client(vm->vmid, "acrndm"); + if (client_id < 0) + return -EFAULT; + return client_id; + } + + case IC_DESTROY_IOREQ_CLIENT: { + int client = ioctl_param; + + acrn_ioreq_destroy_client(client); + break; + } + + case IC_ATTACH_IOREQ_CLIENT: { + int client = ioctl_param; + + return acrn_ioreq_attach_client(client, 0); + } + + case IC_NOTIFY_REQUEST_FINISH: { + struct acrn_ioreq_notify notify; + + if (copy_from_user(¬ify, (void *)ioctl_param, + sizeof(notify))) + return -EFAULT; + + ret = acrn_ioreq_complete_request(notify.client_id, + notify.vcpu_mask); + if (ret < 0) + return -EFAULT; + break; + } + default: pr_warn("Unknown IOCTL 0x%x\n", ioctl_num); ret = 0; @@ -197,6 +247,31 @@ static long vhm_dev_ioctl(struct file *filep, return ret; } +static void io_req_tasklet(unsigned long data) +{ + struct vhm_vm *vm; + + list_for_each_entry(vm, &vhm_vm_list, list) { + if (!vm || !vm->req_buf) + break; + + acrn_ioreq_distribute_request(vm); + } + + if (atomic_read(&ioreq_retry) > 0) { + atomic_dec(&ioreq_retry); + tasklet_schedule(&vhm_io_req_tasklet); + } +} + +static void vhm_intr_handler(void) +{ + if (test_bit(TASKLET_STATE_SCHED, &(vhm_io_req_tasklet.state))) + atomic_inc(&ioreq_retry); + else + tasklet_schedule(&vhm_io_req_tasklet); +} + static int vhm_dev_release(struct inode *inodep, struct file *filep) { struct vhm_vm *vm = filep->private_data; @@ -217,10 +292,13 @@ static const struct file_operations fops = { .mmap = vhm_dev_mmap, .release = vhm_dev_release, .unlocked_ioctl = vhm_dev_ioctl, + .poll = vhm_dev_poll, }; static int __init vhm_init(void) { + unsigned long flag; + pr_info("vhm: initializing\n"); /* Try to dynamically allocate a major number for the device */ @@ -249,12 +327,22 @@ static int __init vhm_init(void) pr_warn("vhm: failed to create the device\n"); return PTR_ERR(vhm_device); } + pr_info("register IPI handler\n"); + tasklet_init(&vhm_io_req_tasklet, io_req_tasklet, 0); + if (x86_platform_ipi_callback) { + pr_warn("vhm: ipi callback was occupied\n"); + return -EINVAL; + } + local_irq_save(flag); + x86_platform_ipi_callback = vhm_intr_handler; + local_irq_restore(flag); pr_info("vhm: Virtio & Hypervisor service module initialized\n"); return 0; } static void __exit vhm_exit(void) { + tasklet_kill(&vhm_io_req_tasklet); device_destroy(vhm_class, MKDEV(major, 0)); class_unregister(vhm_class); class_destroy(vhm_class); diff --git a/drivers/vhm/Makefile b/drivers/vhm/Makefile index 7e5ec421fbc7..4bd960d564b3 100644 --- a/drivers/vhm/Makefile +++ b/drivers/vhm/Makefile @@ -1 +1 @@ -obj-y += vhm_mm.o vhm_vm_mngt.o vhm_hypercall.o +obj-y += vhm_mm.o vhm_ioreq.o vhm_vm_mngt.o vhm_hypercall.o diff --git a/drivers/vhm/vhm_hypercall.c b/drivers/vhm/vhm_hypercall.c index d80087bcb5fb..1b25f4ec4d06 100644 --- a/drivers/vhm/vhm_hypercall.c +++ b/drivers/vhm/vhm_hypercall.c @@ -53,6 +53,16 @@ #include #include +inline long hcall_set_ioreq_buffer(unsigned long vmid, unsigned long buffer) +{ + return acrn_hypercall2(HC_SET_IOREQ_BUFFER, vmid, buffer); +} + +inline long hcall_notify_req_finish(unsigned long vmid, unsigned long vcpu_mask) +{ + return acrn_hypercall2(HC_NOTIFY_REQUEST_FINISH, vmid, vcpu_mask); +} + inline long hcall_set_memmap(unsigned long vmid, unsigned long memmap) { return acrn_hypercall2(HC_VM_SET_MEMMAP, vmid, memmap); diff --git a/drivers/vhm/vhm_ioreq.c b/drivers/vhm/vhm_ioreq.c new file mode 100644 index 000000000000..6054e3d00eb2 --- /dev/null +++ b/drivers/vhm/vhm_ioreq.c @@ -0,0 +1,922 @@ +/* + * virtio and hyperviosr service module (VHM): ioreq multi client feature + * + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright (c) 2017 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * 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. + * + * BSD LICENSE + * + * Copyright (C) 2017 Intel Corporation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 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 + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Jason Chen CJ + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct ioreq_range { + struct list_head list; + enum request_type type; + long start; + long end; +}; + +struct ioreq_client { + /* client name */ + char name[16]; + /* client id */ + int id; + /* vm this client belongs to */ + unsigned long vmid; + /* list node for this ioreq_client */ + struct list_head list; + /* + * is this client fallback? + * there is only one fallback client in a vm - dm + * a fallback client shares IOReq buffer pages + * a fallback client handles all left IOReq not handled by other clients + * a fallback client does not need add io ranges + * a fallback client handles ioreq in its own context + */ + bool fallback; + + bool destroying; + bool kthread_exit; + + /* client covered io ranges - N/A for fallback client */ + struct list_head range_list; + spinlock_t range_lock; + + /* + * this req records the req number this client need handle + */ + atomic_t req; + + /* + * client ioreq handler: + * if client provides a handler, it means vhm need create a kthread + * to call the handler while there is ioreq. + * if client doesn't provide a handler, client should handle ioreq + * in its own context when calls acrn_ioreq_attach_client. + * + * NOTE: for fallback client, there is no ioreq handler. + */ + ioreq_handler_t handler; + bool vhm_create_kthread; + struct task_struct *thread; + wait_queue_head_t wq; + + /* pci bdf trap */ + bool trap_bdf; + int pci_bus; + int pci_dev; + int pci_func; +}; + +#define MAX_CLIENT 64 +static struct ioreq_client *clients[MAX_CLIENT]; +static DECLARE_BITMAP(client_bitmap, MAX_CLIENT); + +static void acrn_ioreq_notify_client(struct ioreq_client *client); + +static inline bool is_range_type(enum request_type type) +{ + return (type == REQ_MMIO || type == REQ_PORTIO || type == REQ_WP); +} + +static int alloc_client(void) +{ + struct ioreq_client *client; + int i; + + i = find_first_zero_bit(client_bitmap, MAX_CLIENT); + if (i >= MAX_CLIENT) + return -ENOMEM; + set_bit(i, client_bitmap); + + client = kzalloc(sizeof(struct ioreq_client), GFP_KERNEL); + if (!client) + return -ENOMEM; + client->id = i; + clients[i] = client; + + return i; +} + +static void free_client(int i) +{ + if (i < MAX_CLIENT && i >= 0) { + if (test_and_clear_bit(i, client_bitmap)) { + kfree(clients[i]); + clients[i] = NULL; + } + } +} + +int acrn_ioreq_create_client(unsigned long vmid, ioreq_handler_t handler, + char *name) +{ + struct vhm_vm *vm; + struct ioreq_client *client; + unsigned long flags; + int client_id; + + might_sleep(); + + vm = find_get_vm(vmid); + if (unlikely(vm == NULL)) { + pr_err("vhm-ioreq: failed to find vm from vmid %ld\n", + vmid); + return -EINVAL; + } + if (unlikely(vm->req_buf == NULL)) { + pr_err("vhm-ioreq: vm[%ld]'s reqbuf is not ready\n", + vmid); + put_vm(vm); + return -EINVAL; + } + + client_id = alloc_client(); + if (unlikely(client_id < 0)) { + pr_err("vhm-ioreq: vm[%ld] failed to alloc ioreq " + "client id\n", vmid); + put_vm(vm); + return -EINVAL; + } + + client = clients[client_id]; + + if (handler) { + client->handler = handler; + client->vhm_create_kthread = true; + } + + client->vmid = vmid; + if (name) + strncpy(client->name, name, 16); + spin_lock_init(&client->range_lock); + INIT_LIST_HEAD(&client->range_list); + init_waitqueue_head(&client->wq); + + spin_lock_irqsave(&vm->ioreq_client_lock, flags); + list_add(&client->list, &vm->ioreq_client_list); + spin_unlock_irqrestore(&vm->ioreq_client_lock, flags); + + put_vm(vm); + + pr_info("vhm-ioreq: created ioreq client %d\n", client_id); + + return client_id; +} + +int acrn_ioreq_create_fallback_client(unsigned long vmid, char *name) +{ + struct vhm_vm *vm; + int client_id; + + vm = find_get_vm(vmid); + if (unlikely(vm == NULL)) { + pr_err("vhm-ioreq: failed to find vm from vmid %ld\n", + vmid); + return -EINVAL; + } + + if (unlikely(vm->ioreq_fallback_client > 0)) { + pr_err("vhm-ioreq: there is already fallback " + "client exist for vm %ld\n", + vmid); + put_vm(vm); + return -EINVAL; + } + + client_id = acrn_ioreq_create_client(vmid, NULL, name); + if (unlikely(client_id < 0)) { + put_vm(vm); + return -EINVAL; + } + + clients[client_id]->fallback = true; + vm->ioreq_fallback_client = client_id; + + put_vm(vm); + + return client_id; +} + +static void acrn_ioreq_destroy_client_pervm(struct ioreq_client *client, + struct vhm_vm *vm) +{ + struct list_head *pos, *tmp; + unsigned long flags; + + /* blocking operation: notify client for cleanup + * if waitqueue not active, it means client is handling request, + * at that time, we need wait client finish its handling. + */ + while (!waitqueue_active(&client->wq) && !client->kthread_exit) + msleep(10); + client->destroying = true; + acrn_ioreq_notify_client(client); + + spin_lock_irqsave(&client->range_lock, flags); + list_for_each_safe(pos, tmp, &client->range_list) { + struct ioreq_range *range = + container_of(pos, struct ioreq_range, list); + list_del(&range->list); + kfree(range); + } + spin_unlock_irqrestore(&client->range_lock, flags); + + spin_lock_irqsave(&vm->ioreq_client_lock, flags); + list_del(&client->list); + spin_unlock_irqrestore(&vm->ioreq_client_lock, flags); + free_client(client->id); + + if (client->id == vm->ioreq_fallback_client) + vm->ioreq_fallback_client = -1; +} + +void acrn_ioreq_destroy_client(int client_id) +{ + struct vhm_vm *vm; + struct ioreq_client *client; + + if (client_id < 0 || client_id >= MAX_CLIENT) { + pr_err("vhm-ioreq: no client for id %d\n", client_id); + return; + } + client = clients[client_id]; + if (!client) { + pr_err("vhm-ioreq: no client for id %d\n", client_id); + return; + } + + might_sleep(); + + vm = find_get_vm(client->vmid); + if (unlikely(vm == NULL)) { + pr_err("vhm-ioreq: failed to find vm from vmid %ld\n", + client->vmid); + return; + } + + acrn_ioreq_destroy_client_pervm(client, vm); + + put_vm(vm); +} + +static void __attribute__((unused)) dump_iorange(struct ioreq_client *client) +{ + struct list_head *pos; + unsigned long flags; + + spin_lock_irqsave(&client->range_lock, flags); + list_for_each(pos, &client->range_list) { + struct ioreq_range *range = + container_of(pos, struct ioreq_range, list); + pr_debug("\tio range: type %d, start 0x%lx, " + "end 0x%lx\n", range->type, range->start, range->end); + } + spin_unlock_irqrestore(&client->range_lock, flags); +} + +/* + * NOTE: here just add iorange entry directly, no check for the overlap.. + * please client take care of it + */ +int acrn_ioreq_add_iorange(int client_id, enum request_type type, + long start, long end) +{ + struct ioreq_client *client; + struct ioreq_range *range; + unsigned long flags; + + if (client_id < 0 || client_id >= MAX_CLIENT) { + pr_err("vhm-ioreq: no client for id %d\n", client_id); + return -EFAULT; + } + client = clients[client_id]; + if (!client) { + pr_err("vhm-ioreq: no client for id %d\n", client_id); + return -EFAULT; + } + + if (end < start) { + pr_err("vhm-ioreq: end < start\n"); + return -EFAULT; + } + + might_sleep(); + + range = kzalloc(sizeof(struct ioreq_range), GFP_KERNEL); + if (!range) { + pr_err("vhm-ioreq: failed to alloc ioreq range\n"); + return -ENOMEM; + } + range->type = type; + range->start = start; + range->end = end; + + spin_lock_irqsave(&client->range_lock, flags); + list_add(&range->list, &client->range_list); + spin_unlock_irqrestore(&client->range_lock, flags); + + return 0; +} + +int acrn_ioreq_del_iorange(int client_id, enum request_type type, + long start, long end) +{ + struct ioreq_client *client; + struct ioreq_range *range; + struct list_head *pos, *tmp; + unsigned long flags; + + if (client_id < 0 || client_id >= MAX_CLIENT) { + pr_err("vhm-ioreq: no client for id %d\n", client_id); + return -EFAULT; + } + client = clients[client_id]; + if (!client) { + pr_err("vhm-ioreq: no client for id %d\n", client_id); + return -EFAULT; + } + + if (end < start) { + pr_err("vhm-ioreq: end < start\n"); + return -EFAULT; + } + + might_sleep(); + + spin_lock_irqsave(&client->range_lock, flags); + list_for_each_safe(pos, tmp, &client->range_list) { + range = container_of(pos, struct ioreq_range, list); + if (range->type == type) { + if (is_range_type(type)) { + if (start == range->start && + end == range->end) { + list_del(&range->list); + kfree(range); + break; + } + } else { + list_del(&range->list); + kfree(range); + break; + } + } + } + spin_unlock_irqrestore(&client->range_lock, flags); + + return 0; +} + +static inline bool is_destroying(struct ioreq_client *client) +{ + if (client) + return client->destroying; + else + return true; +} + +static inline bool has_pending_request(struct ioreq_client *client) +{ + if (client) + return (atomic_read(&client->req) > 0); + else + return false; +} + +struct vhm_request *acrn_ioreq_get_reqbuf(int client_id) +{ + struct ioreq_client *client; + struct vhm_vm *vm; + + if (client_id < 0 || client_id >= MAX_CLIENT) { + pr_err("vhm-ioreq: no client for id %d\n", client_id); + return NULL; + } + client = clients[client_id]; + if (!client) { + pr_err("vhm-ioreq: no client for id %d\n", client_id); + return NULL; + } + vm = find_get_vm(client->vmid); + if (unlikely(vm == NULL)) { + pr_err("vhm-ioreq: failed to find vm from vmid %ld\n", + client->vmid); + return NULL; + } + + if (vm->req_buf == NULL) { + pr_warn("vhm-ioreq: the req buf page not ready yet " + "for vmid %ld\n", client->vmid); + } + put_vm(vm); + return (struct vhm_request *)vm->req_buf; +} + +static int ioreq_client_thread(void *data) +{ + struct ioreq_client *client; + int ret, client_id = (unsigned long)data; + + while (1) { + client = clients[client_id]; + if (is_destroying(client)) { + pr_info("vhm-ioreq: client destroying->stop thread\n"); + break; + } + if (has_pending_request(client)) { + if (client->handler) { + ret = client->handler(client->id, + client->req.counter); + if (ret < 0) + BUG(); + } else { + pr_err("vhm-ioreq: no ioreq handler\n"); + break; + } + } else + wait_event_freezable(client->wq, + (has_pending_request(client) || + is_destroying(client))); + } + + return 0; +} + +int acrn_ioreq_attach_client(int client_id, bool check_kthread_stop) +{ + struct ioreq_client *client; + + if (client_id < 0 || client_id >= MAX_CLIENT) { + pr_err("vhm-ioreq: no client for id %d\n", client_id); + return -EFAULT; + } + client = clients[client_id]; + if (!client) { + pr_err("vhm-ioreq: no client for id %d\n", client_id); + return -EFAULT; + } + + if (client->vhm_create_kthread) { + if (client->thread) { + pr_warn("vhm-ioreq: kthread already exist" + " for client %s\n", client->name); + return 0; + } + client->thread = kthread_run(ioreq_client_thread, + (void *)(unsigned long)client_id, + "ioreq_client[%ld]:%s", + client->vmid, client->name); + if (IS_ERR(client->thread)) { + pr_err("vhm-ioreq: failed to run kthread " + "for client %s\n", client->name); + return -ENOMEM; + } + } else { + might_sleep(); + + if (check_kthread_stop) { + wait_event_freezable(client->wq, + (kthread_should_stop() || + has_pending_request(client) || + is_destroying(client))); + if (kthread_should_stop()) + client->kthread_exit = true; + } else { + wait_event_freezable(client->wq, + (has_pending_request(client) || + is_destroying(client))); + } + + if (is_destroying(client)) + return 1; + } + + return 0; +} + +void acrn_ioreq_intercept_bdf(int client_id, int bus, int dev, int func) +{ + struct ioreq_client *client; + + if (client_id < 0 || client_id >= MAX_CLIENT) { + pr_err("vhm-ioreq: no client for id %d\n", client_id); + return; + } + client = clients[client_id]; + if (!client) { + pr_err("vhm-ioreq: no client for id %d\n", client_id); + return; + } + client->trap_bdf = true; + client->pci_bus = bus; + client->pci_dev = dev; + client->pci_func = func; +} + +void acrn_ioreq_unintercept_bdf(int client_id) +{ + struct ioreq_client *client; + + if (client_id < 0 || client_id >= MAX_CLIENT) { + pr_err("vhm-ioreq: no client for id %d\n", client_id); + return; + } + client = clients[client_id]; + if (!client) { + pr_err("vhm-ioreq: no client for id %d\n", client_id); + return; + } + client->trap_bdf = false; + client->pci_bus = -1; + client->pci_dev = -1; + client->pci_func = -1; +} + +static void acrn_ioreq_notify_client(struct ioreq_client *client) +{ + /* if client thread is in waitqueue, wake up it */ + if (waitqueue_active(&client->wq)) + wake_up_interruptible(&client->wq); +} + +static bool req_in_range(struct ioreq_range *range, struct vhm_request *req) +{ + bool ret = false; + + if (range->type == req->type) { + switch (req->type) { + case REQ_MMIO: + case REQ_WP: + { + if (req->reqs.mmio_request.address >= range->start && + (req->reqs.mmio_request.address + + req->reqs.mmio_request.size - 1) <= range->end) + ret = true; + break; + } + case REQ_PORTIO: { + if (req->reqs.pio_request.address >= range->start && + (req->reqs.pio_request.address + + req->reqs.pio_request.size - 1) <= range->end) + ret = true; + break; + } + case REQ_MSR: /*TODO: add bitmap for MSR range */ + case REQ_CPUID: + case REQ_EXIT: + { + ret = true; + break; + } + + default: + ret = false; + break; + } + } + + return ret; +} + +static bool is_cfg_addr(struct vhm_request *req) +{ + return (req->type == REQ_PORTIO && + (req->reqs.pio_request.address >= 0xcf8 && + req->reqs.pio_request.address < 0xcf8+4)); +} + +static bool is_cfg_data(struct vhm_request *req) +{ + return (req->type == REQ_PORTIO && + (req->reqs.pio_request.address >= 0xcfc && + req->reqs.pio_request.address < 0xcfc+4)); +} + +static int cached_bus; +static int cached_dev; +static int cached_func; +static int cached_reg; +static int cached_enable; +#define PCI_REGMAX 255 /* highest supported config register addr.*/ +#define PCI_FUNCMAX 7 /* highest supported function number */ +#define PCI_SLOTMAX 31 /* highest supported slot number */ +#define PCI_BUSMAX 255 /* highest supported bus number */ +#define CONF1_ENABLE 0x80000000ul +static int handle_cf8cfc(struct vhm_vm *vm, struct vhm_request *req, int vcpu) +{ + int req_handled = 0; + + /*XXX: like DM, assume cfg address write is size 4 */ + if (is_cfg_addr(req)) { + if (req->reqs.pio_request.direction == REQUEST_WRITE) { + if (req->reqs.pio_request.size == 4) { + int value = req->reqs.pio_request.value; + + cached_bus = (value >> 16) & PCI_BUSMAX; + cached_dev = (value >> 11) & PCI_SLOTMAX; + cached_func = (value >> 8) & PCI_FUNCMAX; + cached_reg = value & PCI_REGMAX; + cached_enable = + (value & CONF1_ENABLE) == CONF1_ENABLE; + req_handled = 1; + } + } else { + if (req->reqs.pio_request.size == 4) { + req->reqs.pio_request.value = + (cached_bus << 16) | + (cached_dev << 11) | (cached_func << 8) + | cached_reg; + if (cached_enable) + req->reqs.pio_request.value |= + CONF1_ENABLE; + req_handled = 1; + } + } + } else if (is_cfg_data(req)) { + if (!cached_enable) { + if (req->reqs.pio_request.direction == REQUEST_READ) + req->reqs.pio_request.value = 0xffffffff; + req_handled = 1; + } else { + /* pci request is same as io request at top */ + int offset = req->reqs.pio_request.address - 0xcfc; + + req->type = REQ_PCICFG; + req->reqs.pci_request.bus = cached_bus; + req->reqs.pci_request.dev = cached_dev; + req->reqs.pci_request.func = cached_func; + req->reqs.pci_request.reg = cached_reg + offset; + } + } + + if (req_handled) { + req->processed = REQ_STATE_SUCCESS; + if (hcall_notify_req_finish(vm->vmid, 1 << vcpu) < 0) { + pr_err("vhm-ioreq: failed to " + "notify request finished !\n"); + return -EFAULT; + } + } + + return req_handled; +} + +static bool bdf_match(struct ioreq_client *client) +{ + return (client->trap_bdf && + client->pci_bus == cached_bus && + client->pci_dev == cached_dev && + client->pci_func == cached_func); +} + +static struct ioreq_client *acrn_ioreq_find_client_by_request(struct vhm_vm *vm, + struct vhm_request *req) +{ + struct list_head *pos, *range_pos; + struct ioreq_client *client; + struct ioreq_client *target_client = NULL, *fallback_client = NULL; + struct ioreq_range *range; + bool found = false; + + spin_lock(&vm->ioreq_client_lock); + list_for_each(pos, &vm->ioreq_client_list) { + client = container_of(pos, struct ioreq_client, list); + + if (client->fallback) { + fallback_client = client; + continue; + } + + if (req->type == REQ_PCICFG) { + if (bdf_match(client)) { /* bdf match client */ + target_client = client; + break; + } else /* other or fallback client */ + continue; + } + + spin_lock(&client->range_lock); + list_for_each(range_pos, &client->range_list) { + range = + container_of(range_pos, struct ioreq_range, list); + if (req_in_range(range, req)) { + found = true; + target_client = client; + break; + } + } + spin_unlock(&client->range_lock); + + if (found) + break; + } + spin_unlock(&vm->ioreq_client_lock); + + if (target_client) + return target_client; + + if (fallback_client) + return fallback_client; + + return NULL; +} + +int acrn_ioreq_distribute_request(struct vhm_vm *vm) +{ + struct vhm_request *req; + struct list_head *pos; + struct ioreq_client *client; + int i; + + /* TODO: replace VHM_REQUEST_MAX with vcpu num get at runtime */ + for (i = 0; i < VHM_REQUEST_MAX; i++) { + req = vm->req_buf->req_queue + i; + if (req->valid && (req->processed == REQ_STATE_PENDING)) { + if (handle_cf8cfc(vm, req, i)) + continue; + client = acrn_ioreq_find_client_by_request(vm, req); + if (client == NULL) { + pr_err("vhm-ioreq: failed to " + "find ioreq client -> " + "BUG\n"); + BUG(); + } else { + req->processed = REQ_STATE_PROCESSING; + req->client = client->id; + atomic_inc(&client->req); + } + } + } + + spin_lock(&vm->ioreq_client_lock); + list_for_each(pos, &vm->ioreq_client_list) { + client = container_of(pos, struct ioreq_client, list); + if (has_pending_request(client)) + acrn_ioreq_notify_client(client); + } + spin_unlock(&vm->ioreq_client_lock); + + return 0; +} + +int acrn_ioreq_complete_request(int client_id, uint64_t vcpu_mask) +{ + struct ioreq_client *client; + int ret; + + if (client_id < 0 || client_id >= MAX_CLIENT) { + pr_err("vhm-ioreq: no client for id %d\n", client_id); + return -EINVAL; + } + client = clients[client_id]; + if (!client) { + pr_err("vhm-ioreq: no client for id %d\n", client_id); + return -EINVAL; + } + + atomic_sub(bitmap_weight((unsigned long *)&vcpu_mask, + VHM_REQUEST_MAX), &client->req); + ret = hcall_notify_req_finish(client->vmid, vcpu_mask); + if (ret < 0) { + pr_err("vhm-ioreq: failed to notify request finished !\n"); + return -EFAULT; + } + + return 0; +} + +unsigned int vhm_dev_poll(struct file *filep, poll_table *wait) +{ + struct vhm_vm *vm = filep->private_data; + struct ioreq_client *fallback_client; + unsigned int ret = 0; + + if (vm == NULL || vm->req_buf == NULL || + vm->ioreq_fallback_client <= 0) { + pr_err("vhm: invalid VM !\n"); + ret = POLLERR; + return ret; + } + + fallback_client = clients[vm->ioreq_fallback_client]; + if (!fallback_client) { + pr_err("vhm-ioreq: no client for id %d\n", + vm->ioreq_fallback_client); + return -EINVAL; + } + + poll_wait(filep, &fallback_client->wq, wait); + if (has_pending_request(fallback_client) || + is_destroying(fallback_client)) + ret = POLLIN | POLLRDNORM; + + return ret; +} + +int acrn_ioreq_init(struct vhm_vm *vm, unsigned long vma) +{ + struct acrn_set_ioreq_buffer set_buffer; + struct page *page; + int ret; + + if (vm->req_buf) + BUG(); + + ret = get_user_pages_fast(vma, 1, 1, &page); + if (unlikely(ret != 1) || (page == NULL)) { + pr_err("vhm-ioreq: failed to pin request buffer!\n"); + return -ENOMEM; + } + + vm->req_buf = page_address(page); + vm->pg = page; + + set_buffer.req_buf = (long) page_to_phys(page); + + ret = hcall_set_ioreq_buffer(vm->vmid, virt_to_phys(&set_buffer)); + if (ret < 0) { + pr_err("vhm-ioreq: failed to set request buffer !\n"); + return -EFAULT; + } + + /* reserve 0, let client_id start from 1 */ + set_bit(0, client_bitmap); + + pr_info("vhm-ioreq: init request buffer @ %p!\n", + vm->req_buf); + + return 0; +} + +void acrn_ioreq_free(struct vhm_vm *vm) +{ + struct list_head *pos, *tmp; + + list_for_each_safe(pos, tmp, &vm->ioreq_client_list) { + struct ioreq_client *client = + container_of(pos, struct ioreq_client, list); + acrn_ioreq_destroy_client_pervm(client, vm); + } + + if (vm->req_buf && vm->pg) { + put_page(vm->pg); + vm->pg = NULL; + vm->req_buf = NULL; + } +} diff --git a/drivers/vhm/vhm_vm_mngt.c b/drivers/vhm/vhm_vm_mngt.c index 3c4e6d2b2f23..564435f2bb40 100644 --- a/drivers/vhm/vhm_vm_mngt.c +++ b/drivers/vhm/vhm_vm_mngt.c @@ -58,6 +58,7 @@ #include #include #include +#include #include #include @@ -87,6 +88,7 @@ void put_vm(struct vhm_vm *vm) if (vm->refcnt == 0) { list_del(&vm->list); free_guest_mem(vm); + acrn_ioreq_free(vm); kfree(vm); pr_info("vhm: freed vm\n"); } diff --git a/include/linux/vhm/acrn_common.h b/include/linux/vhm/acrn_common.h index 08e47732f4d0..bc2237331231 100644 --- a/include/linux/vhm/acrn_common.h +++ b/include/linux/vhm/acrn_common.h @@ -56,10 +56,127 @@ * Commmon structures for ACRN/VHM/DM */ +/* + * IO request + */ +#define VHM_REQUEST_MAX 16 + +enum request_state { + REQ_STATE_SUCCESS = 1, + REQ_STATE_PENDING = 0, + REQ_STATE_PROCESSING = 2, + REQ_STATE_FAILED = -1, +} __attribute__((aligned(4))); + +enum request_type { + REQ_MSR, + REQ_CPUID, + REQ_PORTIO, + REQ_MMIO, + REQ_PCICFG, + REQ_WP, + REQ_EXIT, + REQ_MAX, +} __attribute__((aligned(4))); + +enum request_direction { + REQUEST_READ, + REQUEST_WRITE, + DIRECTION_MAX, +} __attribute__((aligned(4))); + +struct msr_request { + enum request_direction direction; + long index; + long value; +} __attribute__((aligned(8))); + +struct cpuid_request { + long eax_in; + long ecx_in; + long eax_out; + long ebx_out; + long ecx_out; + long edx_out; +} __attribute__((aligned(8))); + +struct mmio_request { + enum request_direction direction; + long address; + long size; + long value; +} __attribute__((aligned(8))); + +struct io_request { + enum request_direction direction; + long address; + long size; + int value; +} __attribute__((aligned(8))); + +struct pci_request { + enum request_direction direction; + long reserve; /*io_request address*/ + long size; + int value; + int bus; + int dev; + int func; + int reg; +} __attribute__((aligned(8))); + +/* vhm_request are 256Bytes aligned */ +struct vhm_request { + /* offset: 0bytes - 63bytes */ + enum request_type type; + int reserved0[15]; + + /* offset: 64bytes-127bytes */ + union { + struct msr_request msr_request; + struct cpuid_request cpuid_request; + struct io_request pio_request; + struct pci_request pci_request; + struct mmio_request mmio_request; + long reserved1[8]; + } reqs; + + /* True: valid req which need VHM to process. + * ACRN write, VHM read only + **/ + int valid; + + /* the client which is distributed to handle this request */ + int client; + + /* 1: VHM had processed and success + * 0: VHM had not yet processed + * -1: VHM failed to process. Invalid request + * VHM write, ACRN read only + **/ + enum request_state processed; +} __attribute__((aligned(256))); + +struct vhm_request_buffer { + union { + struct vhm_request req_queue[VHM_REQUEST_MAX]; + char reserved[4096]; + }; +} __attribute__((aligned(4096))); + /* Common API params */ struct acrn_create_vm { unsigned long vmid; /* OUT: HV return vmid to VHM */ unsigned long vcpu_num; /* IN: VM vcpu number */ } __attribute__((aligned(8))); +struct acrn_set_ioreq_buffer { + long req_buf; /* IN: gpa of per VM request_buffer*/ +} __attribute__((aligned(8))); + +struct acrn_ioreq_notify { + int client_id; + unsigned long vcpu_mask; +} __attribute__((aligned(8))); + #endif /* ACRN_COMMON_H */ diff --git a/include/linux/vhm/acrn_hv_defs.h b/include/linux/vhm/acrn_hv_defs.h index ab6554d017cb..f57f2b62e972 100644 --- a/include/linux/vhm/acrn_hv_defs.h +++ b/include/linux/vhm/acrn_hv_defs.h @@ -74,6 +74,11 @@ #define HC_PAUSE_VM _HC_ID(HC_ID, HC_ID_VM_BASE + 0x04) #define HC_QUERY_VMSTATE _HC_ID(HC_ID, HC_ID_VM_BASE + 0x05) +/* DM ioreq management */ +#define HC_ID_IOREQ_BASE 0x200UL +#define HC_SET_IOREQ_BUFFER _HC_ID(HC_ID, HC_ID_IOREQ_BASE + 0x00) +#define HC_NOTIFY_REQUEST_FINISH _HC_ID(HC_ID, HC_ID_IOREQ_BASE + 0x01) + /* Guest memory management */ #define HC_ID_MEM_BASE 0x300UL #define HC_VM_SET_MEMMAP _HC_ID(HC_ID, HC_ID_MEM_BASE + 0x00) diff --git a/include/linux/vhm/acrn_vhm_ioreq.h b/include/linux/vhm/acrn_vhm_ioreq.h new file mode 100644 index 000000000000..0daf46dcf9f7 --- /dev/null +++ b/include/linux/vhm/acrn_vhm_ioreq.h @@ -0,0 +1,86 @@ +/* + * virtio and hyperviosr service module (VHM): ioreq multi client feature + * + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright (c) 2017 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * 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. + * + * BSD LICENSE + * + * Copyright (C) 2017 Intel Corporation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 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 + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Jason Chen CJ + * + */ + +#ifndef __ACRN_VHM_IOREQ_H__ +#define __ACRN_VHM_IOREQ_H__ + +#include +#include + +typedef int (*ioreq_handler_t)(int client_id, int req); + +int acrn_ioreq_create_client(unsigned long vmid, ioreq_handler_t handler, + char *name); +void acrn_ioreq_destroy_client(int client_id); + +int acrn_ioreq_add_iorange(int client_id, enum request_type type, + long start, long end); +int acrn_ioreq_del_iorange(int client_id, enum request_type type, + long start, long end); + +struct vhm_request *acrn_ioreq_get_reqbuf(int client_id); +int acrn_ioreq_attach_client(int client_id, bool check_kthread_stop); + +int acrn_ioreq_distribute_request(struct vhm_vm *vm); +int acrn_ioreq_complete_request(int client_id, uint64_t vcpu_mask); + +void acrn_ioreq_intercept_bdf(int client_id, int bus, int dev, int func); +void acrn_ioreq_unintercept_bdf(int client_id); + +/* IOReq APIs */ +int acrn_ioreq_init(struct vhm_vm *vm, unsigned long vma); +void acrn_ioreq_free(struct vhm_vm *vm); +int acrn_ioreq_create_fallback_client(unsigned long vmid, char *name); +unsigned int vhm_dev_poll(struct file *filep, poll_table *wait); + +#endif diff --git a/include/linux/vhm/vhm_hypercall.h b/include/linux/vhm/vhm_hypercall.h index e098a1f959bf..86b5f579687a 100644 --- a/include/linux/vhm/vhm_hypercall.h +++ b/include/linux/vhm/vhm_hypercall.h @@ -139,6 +139,9 @@ static inline long acrn_hypercall4(unsigned long hyp_id, unsigned long param1, return result; } +inline long hcall_set_ioreq_buffer(unsigned long vmid, unsigned long buffer); +inline long hcall_notify_req_finish(unsigned long vmid, + unsigned long vcpu_mask); inline long hcall_set_memmap(unsigned long vmid, unsigned long memmap); inline long vhm_create_vm(struct vhm_vm *vm, unsigned long ioctl_param); inline long vhm_resume_vm(struct vhm_vm *vm); diff --git a/include/linux/vhm/vhm_ioctl_defs.h b/include/linux/vhm/vhm_ioctl_defs.h index 872092490259..01adcfade99c 100644 --- a/include/linux/vhm/vhm_ioctl_defs.h +++ b/include/linux/vhm/vhm_ioctl_defs.h @@ -64,6 +64,14 @@ #define IC_PAUSE_VM _IC_ID(IC_ID, IC_ID_VM_BASE + 0x04) #define IC_QUERY_VMSTATE _IC_ID(IC_ID, IC_ID_VM_BASE + 0x05) +/* DM ioreq management */ +#define IC_ID_IOREQ_BASE 0x200UL +#define IC_SET_IOREQ_BUFFER _IC_ID(IC_ID, IC_ID_IOREQ_BASE + 0x00) +#define IC_NOTIFY_REQUEST_FINISH _IC_ID(IC_ID, IC_ID_IOREQ_BASE + 0x01) +#define IC_CREATE_IOREQ_CLIENT _IC_ID(IC_ID, IC_ID_IOREQ_BASE + 0x02) +#define IC_ATTACH_IOREQ_CLIENT _IC_ID(IC_ID, IC_ID_IOREQ_BASE + 0x03) +#define IC_DESTROY_IOREQ_CLIENT _IC_ID(IC_ID, IC_ID_IOREQ_BASE + 0x04) + /* Guest memory management */ #define IC_ID_MEM_BASE 0x300UL #define IC_ALLOC_MEMSEG _IC_ID(IC_ID, IC_ID_MEM_BASE + 0x00) diff --git a/include/linux/vhm/vhm_vm_mngt.h b/include/linux/vhm/vhm_vm_mngt.h index 4f1a0db2c54d..eb410024157f 100644 --- a/include/linux/vhm/vhm_vm_mngt.h +++ b/include/linux/vhm/vhm_vm_mngt.h @@ -65,9 +65,14 @@ struct vhm_vm { struct device *dev; struct list_head list; unsigned long vmid; + int ioreq_fallback_client; long refcnt; struct mutex seg_lock; struct list_head memseg_list; + spinlock_t ioreq_client_lock; + struct list_head ioreq_client_list; + struct vhm_request_buffer *req_buf; + struct page *pg; }; struct vhm_vm *find_get_vm(unsigned long vmid); From 6ba060039bc9c361f5b41f8a3d5184bc102faa5d Mon Sep 17 00:00:00 2001 From: liang ding Date: Fri, 29 Dec 2017 16:38:20 +0800 Subject: [PATCH 0007/1103] VHM: add interrupt injection support VHM provides interrupt injection service for emulated devices. this patch added interrupt injection support APIs. Change-Id: I10385318877aa52026d6d2fc56f5fdbc8106bbd9 Tracked-On: 218445 Signed-off-by: liang ding Signed-off-by: Xiao Zheng Signed-off-by: Jason Chen CJ Signed-off-by: Mingqiang Chi Reviewed-on: Reviewed-by: Dong, Eddie Tested-by: Dong, Eddie --- drivers/char/vhm/vhm_dev.c | 29 +++++++++++++++ drivers/vhm/vhm_hypercall.c | 59 ++++++++++++++++++++++++++++++ drivers/vhm/vhm_vm_mngt.c | 18 +++++++++ include/linux/vhm/acrn_common.h | 32 ++++++++++++++++ include/linux/vhm/acrn_hv_defs.h | 7 ++++ include/linux/vhm/vhm_hypercall.h | 4 ++ include/linux/vhm/vhm_ioctl_defs.h | 7 ++++ include/linux/vhm/vhm_vm_mngt.h | 2 + 8 files changed, 158 insertions(+) diff --git a/drivers/char/vhm/vhm_dev.c b/drivers/char/vhm/vhm_dev.c index 3129a8f1503b..97f7c466e11d 100644 --- a/drivers/char/vhm/vhm_dev.c +++ b/drivers/char/vhm/vhm_dev.c @@ -238,6 +238,35 @@ static long vhm_dev_ioctl(struct file *filep, break; } + case IC_ASSERT_IRQLINE: { + ret = vhm_assert_irqline(vm, ioctl_param); + break; + } + + case IC_DEASSERT_IRQLINE: { + ret = vhm_deassert_irqline(vm, ioctl_param); + break; + } + + case IC_PULSE_IRQLINE: { + ret = vhm_pulse_irqline(vm, ioctl_param); + break; + } + + case IC_INJECT_MSI: { + struct acrn_msi_entry msi; + + if (copy_from_user(&msi, (void *)ioctl_param, sizeof(msi))) + return -EFAULT; + + ret = hcall_inject_msi(vm->vmid, virt_to_phys(&msi)); + if (ret < 0) { + pr_err("vhm: failed to inject!\n"); + return -EFAULT; + } + break; + } + default: pr_warn("Unknown IOCTL 0x%x\n", ioctl_num); ret = 0; diff --git a/drivers/vhm/vhm_hypercall.c b/drivers/vhm/vhm_hypercall.c index 1b25f4ec4d06..dc87d30151d5 100644 --- a/drivers/vhm/vhm_hypercall.c +++ b/drivers/vhm/vhm_hypercall.c @@ -53,6 +53,11 @@ #include #include +inline long hcall_inject_msi(unsigned long vmid, unsigned long msi) +{ + return acrn_hypercall2(HC_INJECT_MSI, vmid, msi); +} + inline long hcall_set_ioreq_buffer(unsigned long vmid, unsigned long buffer) { return acrn_hypercall2(HC_SET_IOREQ_BUFFER, vmid, buffer); @@ -147,3 +152,57 @@ inline long vhm_query_vm_state(struct vhm_vm *vm) return ret; } + +inline long vhm_assert_irqline(struct vhm_vm *vm, unsigned long ioctl_param) +{ + long ret = 0; + struct acrn_irqline irq; + + if (copy_from_user(&irq, (void *)ioctl_param, sizeof(irq))) + return -EFAULT; + + ret = acrn_hypercall2(HC_ASSERT_IRQLINE, vm->vmid, + virt_to_phys(&irq)); + if (ret < 0) { + pr_err("vhm: failed to assert irq!\n"); + return -EFAULT; + } + + return ret; +} + +inline long vhm_deassert_irqline(struct vhm_vm *vm, unsigned long ioctl_param) +{ + long ret = 0; + struct acrn_irqline irq; + + if (copy_from_user(&irq, (void *)ioctl_param, sizeof(irq))) + return -EFAULT; + + ret = acrn_hypercall2(HC_DEASSERT_IRQLINE, vm->vmid, + virt_to_phys(&irq)); + if (ret < 0) { + pr_err("vhm: failed to deassert irq!\n"); + return -EFAULT; + } + + return ret; +} + +inline long vhm_pulse_irqline(struct vhm_vm *vm, unsigned long ioctl_param) +{ + long ret = 0; + struct acrn_irqline irq; + + if (copy_from_user(&irq, (void *)ioctl_param, sizeof(irq))) + return -EFAULT; + + ret = acrn_hypercall2(HC_PULSE_IRQLINE, vm->vmid, + virt_to_phys(&irq)); + if (ret < 0) { + pr_err("vhm: failed to assert irq!\n"); + return -EFAULT; + } + + return ret; +} diff --git a/drivers/vhm/vhm_vm_mngt.c b/drivers/vhm/vhm_vm_mngt.c index 564435f2bb40..048ab41f4f9c 100644 --- a/drivers/vhm/vhm_vm_mngt.c +++ b/drivers/vhm/vhm_vm_mngt.c @@ -95,6 +95,24 @@ void put_vm(struct vhm_vm *vm) mutex_unlock(&vhm_vm_list_lock); } +int vhm_inject_msi(unsigned long vmid, unsigned long msi_addr, + unsigned long msi_data) +{ + struct acrn_msi_entry msi; + int ret; + + /* msi_addr: addr[19:12] with dest vcpu id */ + /* msi_data: data[7:0] with vector */ + msi.msi_addr = msi_addr; + msi.msi_data = msi_data; + ret = hcall_inject_msi(vmid, virt_to_phys(&msi)); + if (ret < 0) { + pr_err("vhm: failed to inject!\n"); + return -EFAULT; + } + return 0; +} + void vm_list_add(struct list_head *list) { list_add(list, &vhm_vm_list); diff --git a/include/linux/vhm/acrn_common.h b/include/linux/vhm/acrn_common.h index bc2237331231..cafb171490ca 100644 --- a/include/linux/vhm/acrn_common.h +++ b/include/linux/vhm/acrn_common.h @@ -56,6 +56,20 @@ * Commmon structures for ACRN/VHM/DM */ +enum irq_mode { + IRQ_PULSE, + IRQ_ASSERT, + IRQ_DEASSERT, +} __attribute__((aligned(4))); + +/* ISA type + * inject interrut to both PIC and IOAPIC + */ +enum interrupt_type { + ACRN_INTR_TYPE_ISA, + ACRN_INTR_TYPE_IOAPIC, +} __attribute__((aligned(4))); + /* * IO request */ @@ -179,4 +193,22 @@ struct acrn_ioreq_notify { unsigned long vcpu_mask; } __attribute__((aligned(8))); +/* For ISA, PIC, IOAPIC etc */ +struct acrn_irqline { + enum interrupt_type intr_type; + unsigned long pic_irq; /* IN: for ISA type */ + unsigned long ioapic_irq; /* IN: for IOAPIC type, -1 don't inject */ +} __attribute__((aligned(8))); + +/* For MSI type inject */ +struct acrn_msi_entry { + unsigned long msi_addr; /* IN: addr[19:12] with dest vcpu id */ + unsigned long msi_data; /* IN: data[7:0] with vector */ +} __attribute__((aligned(8))); + +/* For NMI inject */ +struct acrn_nmi_entry { + unsigned long vcpuid; /* IN: -1 means vcpu0 */ +} __attribute__((aligned(8))); + #endif /* ACRN_COMMON_H */ diff --git a/include/linux/vhm/acrn_hv_defs.h b/include/linux/vhm/acrn_hv_defs.h index f57f2b62e972..7b438cc01b48 100644 --- a/include/linux/vhm/acrn_hv_defs.h +++ b/include/linux/vhm/acrn_hv_defs.h @@ -74,6 +74,13 @@ #define HC_PAUSE_VM _HC_ID(HC_ID, HC_ID_VM_BASE + 0x04) #define HC_QUERY_VMSTATE _HC_ID(HC_ID, HC_ID_VM_BASE + 0x05) +/* IRQ and Interrupts */ +#define HC_ID_IRQ_BASE 0x100UL +#define HC_ASSERT_IRQLINE _HC_ID(HC_ID, HC_ID_IRQ_BASE + 0x00) +#define HC_DEASSERT_IRQLINE _HC_ID(HC_ID, HC_ID_IRQ_BASE + 0x01) +#define HC_PULSE_IRQLINE _HC_ID(HC_ID, HC_ID_IRQ_BASE + 0x02) +#define HC_INJECT_MSI _HC_ID(HC_ID, HC_ID_IRQ_BASE + 0x03) + /* DM ioreq management */ #define HC_ID_IOREQ_BASE 0x200UL #define HC_SET_IOREQ_BUFFER _HC_ID(HC_ID, HC_ID_IOREQ_BASE + 0x00) diff --git a/include/linux/vhm/vhm_hypercall.h b/include/linux/vhm/vhm_hypercall.h index 86b5f579687a..e372ea48fa81 100644 --- a/include/linux/vhm/vhm_hypercall.h +++ b/include/linux/vhm/vhm_hypercall.h @@ -139,6 +139,7 @@ static inline long acrn_hypercall4(unsigned long hyp_id, unsigned long param1, return result; } +inline long hcall_inject_msi(unsigned long vmid, unsigned long msi); inline long hcall_set_ioreq_buffer(unsigned long vmid, unsigned long buffer); inline long hcall_notify_req_finish(unsigned long vmid, unsigned long vcpu_mask); @@ -148,5 +149,8 @@ inline long vhm_resume_vm(struct vhm_vm *vm); inline long vhm_pause_vm(struct vhm_vm *vm); inline long vhm_destroy_vm(struct vhm_vm *vm); inline long vhm_query_vm_state(struct vhm_vm *vm); +inline long vhm_assert_irqline(struct vhm_vm *vm, unsigned long ioctl_param); +inline long vhm_deassert_irqline(struct vhm_vm *vm, unsigned long ioctl_param); +inline long vhm_pulse_irqline(struct vhm_vm *vm, unsigned long ioctl_param); #endif /* VHM_HYPERCALL_H */ diff --git a/include/linux/vhm/vhm_ioctl_defs.h b/include/linux/vhm/vhm_ioctl_defs.h index 01adcfade99c..3be6aca40844 100644 --- a/include/linux/vhm/vhm_ioctl_defs.h +++ b/include/linux/vhm/vhm_ioctl_defs.h @@ -64,6 +64,13 @@ #define IC_PAUSE_VM _IC_ID(IC_ID, IC_ID_VM_BASE + 0x04) #define IC_QUERY_VMSTATE _IC_ID(IC_ID, IC_ID_VM_BASE + 0x05) +/* IRQ and Interrupts */ +#define IC_ID_IRQ_BASE 0x100UL +#define IC_ASSERT_IRQLINE _IC_ID(IC_ID, IC_ID_IRQ_BASE + 0x00) +#define IC_DEASSERT_IRQLINE _IC_ID(IC_ID, IC_ID_IRQ_BASE + 0x01) +#define IC_PULSE_IRQLINE _IC_ID(IC_ID, IC_ID_IRQ_BASE + 0x02) +#define IC_INJECT_MSI _IC_ID(IC_ID, IC_ID_IRQ_BASE + 0x03) + /* DM ioreq management */ #define IC_ID_IOREQ_BASE 0x200UL #define IC_SET_IOREQ_BUFFER _IC_ID(IC_ID, IC_ID_IOREQ_BASE + 0x00) diff --git a/include/linux/vhm/vhm_vm_mngt.h b/include/linux/vhm/vhm_vm_mngt.h index eb410024157f..fb02c00ec5e2 100644 --- a/include/linux/vhm/vhm_vm_mngt.h +++ b/include/linux/vhm/vhm_vm_mngt.h @@ -77,6 +77,8 @@ struct vhm_vm { struct vhm_vm *find_get_vm(unsigned long vmid); void put_vm(struct vhm_vm *vm); +int vhm_inject_msi(unsigned long vmid, unsigned long msi_addr, + unsigned long msi_data); void vm_list_add(struct list_head *list); void vm_mutex_lock(struct mutex *mlock); From 266da75c534e80e80d3edb9789b02a878f59551e Mon Sep 17 00:00:00 2001 From: Jason Chen CJ Date: Fri, 31 Aug 2018 10:58:55 +0800 Subject: [PATCH 0008/1103] VHM: add API to get vm info Added API vhm_get_vm_info: get guest vm's max_vcpu & max_gfn Change-Id: Ibe668c75e893092a1e5ea824aa09d9b65825fabb Tracked-On: 218445 Signed-off-by: Jason Chen CJ Reviewed-on: Reviewed-by: Chi, Mingqiang Reviewed-by: Dong, Eddie Tested-by: Dong, Eddie --- drivers/vhm/vhm_mm.c | 9 +++++++-- drivers/vhm/vhm_vm_mngt.c | 17 +++++++++++++++++ include/linux/vhm/vhm_vm_mngt.h | 7 +++++++ 3 files changed, 31 insertions(+), 2 deletions(-) diff --git a/drivers/vhm/vhm_mm.c b/drivers/vhm/vhm_mm.c index ea7604b19aaf..61ebb8c508d2 100644 --- a/drivers/vhm/vhm_mm.c +++ b/drivers/vhm/vhm_mm.c @@ -117,6 +117,7 @@ int alloc_guest_memseg(struct vhm_vm *vm, struct vm_memseg *memseg) { struct guest_memseg *seg; u64 base; + int max_gfn; seg = kzalloc(sizeof(struct guest_memseg), GFP_KERNEL); if (seg == NULL) @@ -134,9 +135,13 @@ int alloc_guest_memseg(struct vhm_vm *vm, struct vm_memseg *memseg) strncpy(seg->name, memseg->name, SPECNAMELEN + 1); seg->gpa = memseg->gpa; + max_gfn = (seg->gpa + seg->len) >> PAGE_SHIFT; + if (vm->max_gfn < max_gfn) + vm->max_gfn = max_gfn; + pr_info("VHM: alloc memseg[%s] with len=0x%lx, base=0x%llx," - " and its guest gpa = 0x%llx\n", - seg->name, seg->len, seg->base, seg->gpa); + " and its guest gpa = 0x%llx, vm max_gfn 0x%x\n", + seg->name, seg->len, seg->base, seg->gpa, vm->max_gfn); seg->vma_count = 0; mutex_lock(&vm->seg_lock); diff --git a/drivers/vhm/vhm_vm_mngt.c b/drivers/vhm/vhm_vm_mngt.c index 048ab41f4f9c..d1aa4ba1a4f0 100644 --- a/drivers/vhm/vhm_vm_mngt.c +++ b/drivers/vhm/vhm_vm_mngt.c @@ -95,6 +95,23 @@ void put_vm(struct vhm_vm *vm) mutex_unlock(&vhm_vm_list_lock); } +int vhm_get_vm_info(unsigned long vmid, struct vm_info *info) +{ + struct vhm_vm *vm; + + vm = find_get_vm(vmid); + if (unlikely(vm == NULL)) { + pr_err("vhm: failed to find vm from vmid %ld\n", + vmid); + return -EINVAL; + } + /*TODO: hardcode max_vcpu here, should be fixed by getting at runtime */ + info->max_vcpu = 4; + info->max_gfn = vm->max_gfn; + put_vm(vm); + return 0; +} + int vhm_inject_msi(unsigned long vmid, unsigned long msi_addr, unsigned long msi_data) { diff --git a/include/linux/vhm/vhm_vm_mngt.h b/include/linux/vhm/vhm_vm_mngt.h index fb02c00ec5e2..77c21c4bba7a 100644 --- a/include/linux/vhm/vhm_vm_mngt.h +++ b/include/linux/vhm/vhm_vm_mngt.h @@ -69,14 +69,21 @@ struct vhm_vm { long refcnt; struct mutex seg_lock; struct list_head memseg_list; + int max_gfn; spinlock_t ioreq_client_lock; struct list_head ioreq_client_list; struct vhm_request_buffer *req_buf; struct page *pg; }; +struct vm_info { + int max_vcpu; + int max_gfn; +}; + struct vhm_vm *find_get_vm(unsigned long vmid); void put_vm(struct vhm_vm *vm); +int vhm_get_vm_info(unsigned long vmid, struct vm_info *info); int vhm_inject_msi(unsigned long vmid, unsigned long msi_addr, unsigned long msi_data); From 6231054e544c8e07849353cf5426f0cf3c43b885 Mon Sep 17 00:00:00 2001 From: Jason Chen CJ Date: Fri, 31 Aug 2018 10:58:55 +0800 Subject: [PATCH 0009/1103] VHM: add API to do guest gpa2hpa translation Added API vhm_vm_gpa2hpa: do translation between gpa and hpa for corresponding guest. Change-Id: I5ccdc3c6ac73d02d854878957093895c7f0cbee6 Tracked-On: 218445 Signed-off-by: Jason Chen CJ Reviewed-on: Reviewed-by: Chi, Mingqiang Reviewed-by: Dong, Eddie Tested-by: Dong, Eddie --- drivers/vhm/vhm_hypercall.c | 5 +++++ drivers/vhm/vhm_vm_mngt.c | 16 ++++++++++++++++ include/linux/vhm/acrn_common.h | 5 +++++ include/linux/vhm/acrn_hv_defs.h | 2 ++ include/linux/vhm/acrn_vhm_mm.h | 6 ++++++ include/linux/vhm/vhm_hypercall.h | 1 + include/linux/vhm/vhm_vm_mngt.h | 1 + 7 files changed, 36 insertions(+) diff --git a/drivers/vhm/vhm_hypercall.c b/drivers/vhm/vhm_hypercall.c index dc87d30151d5..384b86e60c9c 100644 --- a/drivers/vhm/vhm_hypercall.c +++ b/drivers/vhm/vhm_hypercall.c @@ -73,6 +73,11 @@ inline long hcall_set_memmap(unsigned long vmid, unsigned long memmap) return acrn_hypercall2(HC_VM_SET_MEMMAP, vmid, memmap); } +inline long hcall_vm_gpa2hpa(unsigned long vmid, unsigned long gpa2hpa) +{ + return acrn_hypercall2(HC_VM_GPA2HPA, vmid, gpa2hpa); +} + inline long vhm_create_vm(struct vhm_vm *vm, unsigned long ioctl_param) { long ret = 0; diff --git a/drivers/vhm/vhm_vm_mngt.c b/drivers/vhm/vhm_vm_mngt.c index d1aa4ba1a4f0..8f1a00777dd4 100644 --- a/drivers/vhm/vhm_vm_mngt.c +++ b/drivers/vhm/vhm_vm_mngt.c @@ -130,6 +130,22 @@ int vhm_inject_msi(unsigned long vmid, unsigned long msi_addr, return 0; } +unsigned long vhm_vm_gpa2hpa(unsigned long vmid, unsigned long gpa) +{ + struct vm_gpa2hpa gpa2hpa; + int ret; + + gpa2hpa.gpa = gpa; + gpa2hpa.hpa = -1UL; /* Init value as invalid gpa */ + ret = hcall_vm_gpa2hpa(vmid, virt_to_phys(&gpa2hpa)); + if (ret < 0) { + pr_err("vhm: failed to inject!\n"); + return -EFAULT; + } + mb(); + return gpa2hpa.hpa; +} + void vm_list_add(struct list_head *list) { list_add(list, &vhm_vm_list); diff --git a/include/linux/vhm/acrn_common.h b/include/linux/vhm/acrn_common.h index cafb171490ca..f0567aa0f1dd 100644 --- a/include/linux/vhm/acrn_common.h +++ b/include/linux/vhm/acrn_common.h @@ -211,4 +211,9 @@ struct acrn_nmi_entry { unsigned long vcpuid; /* IN: -1 means vcpu0 */ } __attribute__((aligned(8))); +struct vm_gpa2hpa { + unsigned long gpa; /* IN: gpa to translation */ + unsigned long hpa; /* OUT: -1 means invalid gpa */ +} __attribute__((aligned(8))); + #endif /* ACRN_COMMON_H */ diff --git a/include/linux/vhm/acrn_hv_defs.h b/include/linux/vhm/acrn_hv_defs.h index 7b438cc01b48..d527a8fa8435 100644 --- a/include/linux/vhm/acrn_hv_defs.h +++ b/include/linux/vhm/acrn_hv_defs.h @@ -86,9 +86,11 @@ #define HC_SET_IOREQ_BUFFER _HC_ID(HC_ID, HC_ID_IOREQ_BASE + 0x00) #define HC_NOTIFY_REQUEST_FINISH _HC_ID(HC_ID, HC_ID_IOREQ_BASE + 0x01) + /* Guest memory management */ #define HC_ID_MEM_BASE 0x300UL #define HC_VM_SET_MEMMAP _HC_ID(HC_ID, HC_ID_MEM_BASE + 0x00) +#define HC_VM_GPA2HPA _HC_ID(HC_ID, HC_ID_MEM_BASE + 0x01) #define ACRN_DOM0_VMID (0UL) #define ACRN_INVALID_VMID (-1UL) diff --git a/include/linux/vhm/acrn_vhm_mm.h b/include/linux/vhm/acrn_vhm_mm.h index e701254bc249..1af6fd3aa11b 100644 --- a/include/linux/vhm/acrn_vhm_mm.h +++ b/include/linux/vhm/acrn_vhm_mm.h @@ -70,6 +70,12 @@ #define MMU_MEM_ATTR_ALL_WB 0x00000047 #define MMU_MEM_ATTR_ALL_WC 0x00000207 +/* 1:1 mapping for service OS */ +static inline unsigned long acrn_hpa2gpa(unsigned long hpa) +{ + return hpa; +} + void *map_guest_phys(unsigned long vmid, u64 uos_phys, size_t size); int unmap_guest_phys(unsigned long vmid, u64 uos_phys); int set_mmio_map(unsigned long vmid, unsigned long guest_gpa, diff --git a/include/linux/vhm/vhm_hypercall.h b/include/linux/vhm/vhm_hypercall.h index e372ea48fa81..f1ed9a07e708 100644 --- a/include/linux/vhm/vhm_hypercall.h +++ b/include/linux/vhm/vhm_hypercall.h @@ -144,6 +144,7 @@ inline long hcall_set_ioreq_buffer(unsigned long vmid, unsigned long buffer); inline long hcall_notify_req_finish(unsigned long vmid, unsigned long vcpu_mask); inline long hcall_set_memmap(unsigned long vmid, unsigned long memmap); +inline long hcall_vm_gpa2hpa(unsigned long vmid, unsigned long gpa2hpa); inline long vhm_create_vm(struct vhm_vm *vm, unsigned long ioctl_param); inline long vhm_resume_vm(struct vhm_vm *vm); inline long vhm_pause_vm(struct vhm_vm *vm); diff --git a/include/linux/vhm/vhm_vm_mngt.h b/include/linux/vhm/vhm_vm_mngt.h index 77c21c4bba7a..5edacb31dc1b 100644 --- a/include/linux/vhm/vhm_vm_mngt.h +++ b/include/linux/vhm/vhm_vm_mngt.h @@ -86,6 +86,7 @@ void put_vm(struct vhm_vm *vm); int vhm_get_vm_info(unsigned long vmid, struct vm_info *info); int vhm_inject_msi(unsigned long vmid, unsigned long msi_addr, unsigned long msi_data); +unsigned long vhm_vm_gpa2hpa(unsigned long vmid, unsigned long gpa); void vm_list_add(struct list_head *list); void vm_mutex_lock(struct mutex *mlock); From 34ef6a59fc35861de2dfa6de4ee10110848d800c Mon Sep 17 00:00:00 2001 From: Binbin Wu Date: Fri, 31 Aug 2018 10:58:55 +0800 Subject: [PATCH 0010/1103] VHM: add passthrough device support add following ioctl in vhm_dev to support device passthrough - assign, deassign pass-through device ACRN_ASSIGN_PTDEV ACRN_DEASSIGN_PTDEV - set, reset pass-through device intr info ACRN_SET_PTDEV_INTR_INFO ACRN_RESET_PTDEV_INTR_INFO reuse exist ioctl to support device passthrough - BAR mapping ACRN_IOC_SET_MEMSEG - MSI support ACRN_VM_PCI_MSIX_REMAP Change-Id: I94bbee48e8de1faf70804061c65c2e2855e6bf0f Tracked-On: 218445 Signed-off-by: Gao, Shiqing Signed-off-by: Binbin Wu Signed-off-by: Edwin Zhai Signed-off-by: Jason Chen CJ Reviewed-on: Reviewed-by: Chi, Mingqiang Reviewed-by: Dong, Eddie Tested-by: Dong, Eddie --- drivers/char/vhm/vhm_dev.c | 25 +++++ drivers/vhm/vhm_hypercall.c | 175 +++++++++++++++++++++++++++++ include/linux/vhm/acrn_common.h | 43 +++++++ include/linux/vhm/acrn_hv_defs.h | 8 ++ include/linux/vhm/vhm_hypercall.h | 8 ++ include/linux/vhm/vhm_ioctl_defs.h | 9 ++ 6 files changed, 268 insertions(+) diff --git a/drivers/char/vhm/vhm_dev.c b/drivers/char/vhm/vhm_dev.c index 97f7c466e11d..97c20d82154f 100644 --- a/drivers/char/vhm/vhm_dev.c +++ b/drivers/char/vhm/vhm_dev.c @@ -267,6 +267,31 @@ static long vhm_dev_ioctl(struct file *filep, break; } + case IC_ASSIGN_PTDEV: { + ret = vhm_assign_ptdev(vm, ioctl_param); + break; + } + + case IC_DEASSIGN_PTDEV: { + ret = vhm_deassign_ptdev(vm, ioctl_param); + break; + } + + case IC_SET_PTDEV_INTR_INFO: { + ret = vhm_set_ptdev_intr_info(vm, ioctl_param); + break; + } + + case IC_RESET_PTDEV_INTR_INFO: { + ret = vhm_reset_ptdev_intr_info(vm, ioctl_param); + break; + } + + case IC_VM_PCI_MSIX_REMAP: { + ret = vhm_remap_pci_msix(vm, ioctl_param); + break; + } + default: pr_warn("Unknown IOCTL 0x%x\n", ioctl_num); ret = 0; diff --git a/drivers/vhm/vhm_hypercall.c b/drivers/vhm/vhm_hypercall.c index 384b86e60c9c..0f3f6c1c5f4c 100644 --- a/drivers/vhm/vhm_hypercall.c +++ b/drivers/vhm/vhm_hypercall.c @@ -50,14 +50,30 @@ */ #include #include +#include #include #include +/* max num of pass-through devices using msix */ +#define MAX_ENTRY 3 + +struct table_iomems { + /* device's virtual BDF */ + unsigned short virt_bdf; + /* virtual base address of MSI-X table in memory space after ioremap */ + unsigned long mmap_addr; +} tables[MAX_ENTRY]; + inline long hcall_inject_msi(unsigned long vmid, unsigned long msi) { return acrn_hypercall2(HC_INJECT_MSI, vmid, msi); } +inline long hcall_remap_pci_msix(unsigned long vmid, unsigned long msix) +{ + return acrn_hypercall2(HC_VM_PCI_MSIX_REMAP, vmid, msix); +} + inline long hcall_set_ioreq_buffer(unsigned long vmid, unsigned long buffer) { return acrn_hypercall2(HC_SET_IOREQ_BUFFER, vmid, buffer); @@ -211,3 +227,162 @@ inline long vhm_pulse_irqline(struct vhm_vm *vm, unsigned long ioctl_param) return ret; } + +inline long vhm_assign_ptdev(struct vhm_vm *vm, unsigned long ioctl_param) +{ + long ret = 0; + uint16_t bdf; + + if (copy_from_user(&bdf, + (void *)ioctl_param, sizeof(uint16_t))) + return -EFAULT; + + ret = acrn_hypercall2(HC_ASSIGN_PTDEV, vm->vmid, + virt_to_phys(&bdf)); + if (ret < 0) { + pr_err("vhm: failed to assign ptdev!\n"); + return -EFAULT; + } + + return ret; +} + +inline long vhm_deassign_ptdev(struct vhm_vm *vm, unsigned long ioctl_param) +{ + long ret = 0; + uint16_t bdf; + + if (copy_from_user(&bdf, + (void *)ioctl_param, sizeof(uint16_t))) + return -EFAULT; + + ret = acrn_hypercall2(HC_DEASSIGN_PTDEV, vm->vmid, + virt_to_phys(&bdf)); + if (ret < 0) { + pr_err("vhm: failed to deassign ptdev!\n"); + return -EFAULT; + } + + return ret; +} + +inline long vhm_set_ptdev_intr_info(struct vhm_vm *vm, + unsigned long ioctl_param) +{ + long ret = 0; + struct acrn_ptdev_irq pt_irq; + int i; + + if (copy_from_user(&pt_irq, + (void *)ioctl_param, sizeof(pt_irq))) + return -EFAULT; + + ret = acrn_hypercall2(HC_SET_PTDEV_INTR_INFO, vm->vmid, + virt_to_phys(&pt_irq)); + if (ret < 0) { + pr_err("vhm: failed to set intr info for ptdev!\n"); + return -EFAULT; + } + + if (pt_irq.msix.table_paddr) { + for (i = 0; i < MAX_ENTRY; i++) { + if (tables[i].virt_bdf) + continue; + + tables[i].virt_bdf = pt_irq.virt_bdf; + tables[i].mmap_addr = (unsigned long) + ioremap_nocache(pt_irq.msix.table_paddr, + pt_irq.msix.table_size); + break; + } + } + + return ret; +} + +inline long vhm_reset_ptdev_intr_info(struct vhm_vm *vm, + unsigned long ioctl_param) +{ + long ret = 0; + struct acrn_ptdev_irq pt_irq; + int i; + + if (copy_from_user(&pt_irq, + (void *)ioctl_param, sizeof(pt_irq))) + return -EFAULT; + + ret = acrn_hypercall2(HC_RESET_PTDEV_INTR_INFO, vm->vmid, + virt_to_phys(&pt_irq)); + if (ret < 0) { + pr_err("vhm: failed to reset intr info for ptdev!\n"); + return -EFAULT; + } + + if (pt_irq.msix.table_paddr) { + for (i = 0; i < MAX_ENTRY; i++) { + if (tables[i].virt_bdf) + continue; + + tables[i].virt_bdf = pt_irq.virt_bdf; + tables[i].mmap_addr = (unsigned long) + ioremap_nocache(pt_irq.msix.table_paddr, + pt_irq.msix.table_size); + break; + } + } + + return ret; +} + +inline long vhm_remap_pci_msix(struct vhm_vm *vm, unsigned long ioctl_param) +{ + long ret = 0; + struct acrn_vm_pci_msix_remap msix_remap; + + if (copy_from_user(&msix_remap, + (void *)ioctl_param, sizeof(msix_remap))) + return -EFAULT; + + ret = acrn_hypercall2(HC_VM_PCI_MSIX_REMAP, vm->vmid, + virt_to_phys(&msix_remap)); + + if (copy_to_user((void *)ioctl_param, + &msix_remap, sizeof(msix_remap))) + return -EFAULT; + + if (msix_remap.msix) { + void __iomem *msix_entry; + int i; + + for (i = 0; i < MAX_ENTRY; i++) { + if (tables[i].virt_bdf == msix_remap.virt_bdf) + break; + } + + if (!tables[i].mmap_addr) + return -EFAULT; + + msix_entry = (void *)(tables[i].mmap_addr + + msix_remap.msix_entry_index * + PCI_MSIX_ENTRY_SIZE); + + /* mask the entry when setup */ + writel(PCI_MSIX_ENTRY_CTRL_MASKBIT, + msix_entry + PCI_MSIX_ENTRY_VECTOR_CTRL); + + /* setup the msi entry */ + writel((uint32_t)msix_remap.msi_addr, + msix_entry + PCI_MSIX_ENTRY_LOWER_ADDR); + writel((uint32_t)(msix_remap.msi_addr >> 32), + msix_entry + PCI_MSIX_ENTRY_UPPER_ADDR); + writel(msix_remap.msi_data, + msix_entry + PCI_MSIX_ENTRY_DATA); + + /* unmask the entry */ + writel(msix_remap.vector_ctl & + PCI_MSIX_ENTRY_CTRL_MASKBIT, + msix_entry + PCI_MSIX_ENTRY_VECTOR_CTRL); + } + + return ret; +} diff --git a/include/linux/vhm/acrn_common.h b/include/linux/vhm/acrn_common.h index f0567aa0f1dd..5723e5e2d537 100644 --- a/include/linux/vhm/acrn_common.h +++ b/include/linux/vhm/acrn_common.h @@ -99,6 +99,15 @@ enum request_direction { DIRECTION_MAX, } __attribute__((aligned(4))); +/* + * IRQ type for ptdev + */ +enum irq_type { + IRQ_INTX, + IRQ_MSI, + IRQ_MSIX, +} __attribute__((aligned(4))); + struct msr_request { enum request_direction direction; long index; @@ -216,4 +225,38 @@ struct vm_gpa2hpa { unsigned long hpa; /* OUT: -1 means invalid gpa */ } __attribute__((aligned(8))); +struct acrn_ptdev_irq { + enum irq_type type; + unsigned short virt_bdf; /* IN: Device virtual BDF# */ + unsigned short phys_bdf; /* IN: Device physical BDF# */ + union { + struct { + int virt_pin; /* IN: virtual IOAPIC pin */ + int phys_pin; /* IN: physical IOAPIC pin */ + bool pic_pin; /* IN: pin from PIC? */ + } intx; + struct { + int vector_cnt; /* IN: vector count of MSI/MSIX */ + + /* IN: physcial address of MSI-X table */ + unsigned long table_paddr; + + /* IN: size of MSI-X table (round up to 4K) */ + int table_size; + } msix; + }; +} __attribute__((aligned(8))); + +struct acrn_vm_pci_msix_remap { + unsigned short virt_bdf; /* IN: Device virtual BDF# */ + unsigned short phys_bdf; /* IN: Device physical BDF# */ + unsigned short msi_ctl; /* IN: PCI MSI/x cap control data */ + unsigned long msi_addr; /* IN/OUT: msi address to fix */ + unsigned int msi_data; /* IN/OUT: msi data to fix */ + int msix; /* IN: 0 - MSI, 1 - MSI-X */ + int msix_entry_index; /* IN: MSI-X the entry table index */ + /* IN: Vector Control for MSI-X Entry, field defined in MSIX spec */ + unsigned int vector_ctl; +} __attribute__((aligned(8))); + #endif /* ACRN_COMMON_H */ diff --git a/include/linux/vhm/acrn_hv_defs.h b/include/linux/vhm/acrn_hv_defs.h index d527a8fa8435..3e43da56813d 100644 --- a/include/linux/vhm/acrn_hv_defs.h +++ b/include/linux/vhm/acrn_hv_defs.h @@ -92,6 +92,14 @@ #define HC_VM_SET_MEMMAP _HC_ID(HC_ID, HC_ID_MEM_BASE + 0x00) #define HC_VM_GPA2HPA _HC_ID(HC_ID, HC_ID_MEM_BASE + 0x01) +/* PCI assignment*/ +#define HC_ID_PCI_BASE 0x400UL +#define HC_ASSIGN_PTDEV _HC_ID(HC_ID, HC_ID_PCI_BASE + 0x00) +#define HC_DEASSIGN_PTDEV _HC_ID(HC_ID, HC_ID_PCI_BASE + 0x01) +#define HC_VM_PCI_MSIX_REMAP _HC_ID(HC_ID, HC_ID_PCI_BASE + 0x02) +#define HC_SET_PTDEV_INTR_INFO _HC_ID(HC_ID, HC_ID_PCI_BASE + 0x03) +#define HC_RESET_PTDEV_INTR_INFO _HC_ID(HC_ID, HC_ID_PCI_BASE + 0x04) + #define ACRN_DOM0_VMID (0UL) #define ACRN_INVALID_VMID (-1UL) #define ACRN_INVALID_HPA (-1UL) diff --git a/include/linux/vhm/vhm_hypercall.h b/include/linux/vhm/vhm_hypercall.h index f1ed9a07e708..ce579e3734ff 100644 --- a/include/linux/vhm/vhm_hypercall.h +++ b/include/linux/vhm/vhm_hypercall.h @@ -140,6 +140,7 @@ static inline long acrn_hypercall4(unsigned long hyp_id, unsigned long param1, } inline long hcall_inject_msi(unsigned long vmid, unsigned long msi); +inline long hcall_remap_pci_msix(unsigned long vmid, unsigned long msix); inline long hcall_set_ioreq_buffer(unsigned long vmid, unsigned long buffer); inline long hcall_notify_req_finish(unsigned long vmid, unsigned long vcpu_mask); @@ -153,5 +154,12 @@ inline long vhm_query_vm_state(struct vhm_vm *vm); inline long vhm_assert_irqline(struct vhm_vm *vm, unsigned long ioctl_param); inline long vhm_deassert_irqline(struct vhm_vm *vm, unsigned long ioctl_param); inline long vhm_pulse_irqline(struct vhm_vm *vm, unsigned long ioctl_param); +inline long vhm_assign_ptdev(struct vhm_vm *vm, unsigned long ioctl_param); +inline long vhm_deassign_ptdev(struct vhm_vm *vm, unsigned long ioctl_param); +inline long vhm_set_ptdev_intr_info(struct vhm_vm *vm, + unsigned long ioctl_param); +inline long vhm_reset_ptdev_intr_info(struct vhm_vm *vm, + unsigned long ioctl_param); +inline long vhm_remap_pci_msix(struct vhm_vm *vm, unsigned long ioctl_param); #endif /* VHM_HYPERCALL_H */ diff --git a/include/linux/vhm/vhm_ioctl_defs.h b/include/linux/vhm/vhm_ioctl_defs.h index 3be6aca40844..8d03d38b788d 100644 --- a/include/linux/vhm/vhm_ioctl_defs.h +++ b/include/linux/vhm/vhm_ioctl_defs.h @@ -79,11 +79,20 @@ #define IC_ATTACH_IOREQ_CLIENT _IC_ID(IC_ID, IC_ID_IOREQ_BASE + 0x03) #define IC_DESTROY_IOREQ_CLIENT _IC_ID(IC_ID, IC_ID_IOREQ_BASE + 0x04) + /* Guest memory management */ #define IC_ID_MEM_BASE 0x300UL #define IC_ALLOC_MEMSEG _IC_ID(IC_ID, IC_ID_MEM_BASE + 0x00) #define IC_SET_MEMSEG _IC_ID(IC_ID, IC_ID_MEM_BASE + 0x01) +/* PCI assignment*/ +#define IC_ID_PCI_BASE 0x400UL +#define IC_ASSIGN_PTDEV _IC_ID(IC_ID, IC_ID_PCI_BASE + 0x00) +#define IC_DEASSIGN_PTDEV _IC_ID(IC_ID, IC_ID_PCI_BASE + 0x01) +#define IC_VM_PCI_MSIX_REMAP _IC_ID(IC_ID, IC_ID_PCI_BASE + 0x02) +#define IC_SET_PTDEV_INTR_INFO _IC_ID(IC_ID, IC_ID_PCI_BASE + 0x03) +#define IC_RESET_PTDEV_INTR_INFO _IC_ID(IC_ID, IC_ID_PCI_BASE + 0x04) + #define SPECNAMELEN 63 enum { From 484c2e4969bf53c7707ba2576e658e5f0bcfcd88 Mon Sep 17 00:00:00 2001 From: Jason Chen CJ Date: Fri, 31 Aug 2018 10:58:55 +0800 Subject: [PATCH 0011/1103] x86:acrn: add write_msi pv ops to intercept pci msi write with pv method added pv ops write_msi into pv_irq_ops, the function write_msi_msg_paravirt is to write msi msg through paravirt way. for acrn, it calls acrn_write_msi_msg which includes acrn_notify_msix_remap for passthrough device msi/msix remapping. Change-Id: Ib5c0687c6227b527ff629c7884246417686b5896 Tracked-On: 218445 Signed-off-by: Jason Chen CJ Signed-off-by: Zheng Xiao Reviewed-on: Reviewed-by: Chi, Mingqiang Reviewed-by: Dong, Eddie Tested-by: Dong, Eddie --- arch/x86/acrn/Kconfig | 1 + arch/x86/acrn/acrn.c | 4 + arch/x86/include/asm/paravirt.h | 10 ++ arch/x86/include/asm/paravirt_types.h | 4 + arch/x86/kernel/paravirt.c | 4 + drivers/pci/msi.c | 4 +- drivers/pci/pci.h | 2 + drivers/vhm/Makefile | 2 +- drivers/vhm/vhm_msi.c | 135 ++++++++++++++++++++++++++ include/linux/msi.h | 10 +- include/linux/vhm/vhm_msi.h | 61 ++++++++++++ 11 files changed, 233 insertions(+), 4 deletions(-) create mode 100644 drivers/vhm/vhm_msi.c create mode 100644 include/linux/vhm/vhm_msi.h diff --git a/arch/x86/acrn/Kconfig b/arch/x86/acrn/Kconfig index 0ba9e36c41f3..7788cb8cfb4b 100644 --- a/arch/x86/acrn/Kconfig +++ b/arch/x86/acrn/Kconfig @@ -8,6 +8,7 @@ config ACRN depends on X86_64 depends on PARAVIRT depends on DMA_CMA + depends on PCI_MSI depends on !INTEL_IOMMU depends on !VMAP_STACK help diff --git a/arch/x86/acrn/acrn.c b/arch/x86/acrn/acrn.c index a042b544af33..3987e2287a9f 100644 --- a/arch/x86/acrn/acrn.c +++ b/arch/x86/acrn/acrn.c @@ -33,6 +33,7 @@ * */ #include +#include static uint32_t __init acrn_detect(void) { @@ -41,6 +42,9 @@ static uint32_t __init acrn_detect(void) static void __init acrn_init_platform(void) { +#ifdef CONFIG_PCI_MSI + pv_irq_ops.write_msi = acrn_write_msi_msg; +#endif } static void acrn_pin_vcpu(int cpu) diff --git a/arch/x86/include/asm/paravirt.h b/arch/x86/include/asm/paravirt.h index e375d4266b53..f5af75bc2d45 100644 --- a/arch/x86/include/asm/paravirt.h +++ b/arch/x86/include/asm/paravirt.h @@ -807,6 +807,16 @@ static inline notrace unsigned long arch_local_irq_save(void) return f; } +static inline void write_msi_msg_paravirt(struct msi_desc *entry, + struct msi_msg *msg) +{ + if ((pv_irq_ops.write_msi == NULL) || + (pv_irq_ops.write_msi == paravirt_nop)) + return; + + return PVOP_VCALL2(pv_irq_ops.write_msi, entry, msg); +} + /* Make sure as little as possible of this mess escapes. */ #undef PARAVIRT_CALL diff --git a/arch/x86/include/asm/paravirt_types.h b/arch/x86/include/asm/paravirt_types.h index 4b75acc23b30..06e01d87d76a 100644 --- a/arch/x86/include/asm/paravirt_types.h +++ b/arch/x86/include/asm/paravirt_types.h @@ -56,6 +56,9 @@ struct cpumask; struct flush_tlb_info; struct mmu_gather; +struct msi_desc; +struct msi_msg; + /* * Wrapper type for pointers to code which uses the non-standard * calling convention. See PV_CALL_SAVE_REGS_THUNK below. @@ -196,6 +199,7 @@ struct pv_irq_ops { void (*safe_halt)(void); void (*halt)(void); + void (*write_msi)(struct msi_desc *entry, struct msi_msg *msg); } __no_randomize_layout; struct pv_mmu_ops { diff --git a/arch/x86/kernel/paravirt.c b/arch/x86/kernel/paravirt.c index 8dc69d82567e..eaa8917dab73 100644 --- a/arch/x86/kernel/paravirt.c +++ b/arch/x86/kernel/paravirt.c @@ -25,6 +25,7 @@ #include #include #include +#include #include #include @@ -336,6 +337,9 @@ __visible struct pv_irq_ops pv_irq_ops = { .irq_enable = __PV_IS_CALLEE_SAVE(native_irq_enable), .safe_halt = native_safe_halt, .halt = native_halt, +#ifdef CONFIG_PCI_MSI + .write_msi = native_write_msi_msg, +#endif }; __visible struct pv_cpu_ops pv_cpu_ops = { diff --git a/drivers/pci/msi.c b/drivers/pci/msi.c index f2ef896464b3..66b5001abe96 100644 --- a/drivers/pci/msi.c +++ b/drivers/pci/msi.c @@ -190,7 +190,7 @@ static void msi_mask_irq(struct msi_desc *desc, u32 mask, u32 flag) desc->masked = __pci_msi_desc_mask_irq(desc, mask, flag); } -static void __iomem *pci_msix_desc_addr(struct msi_desc *desc) +void __iomem *pci_msix_desc_addr(struct msi_desc *desc) { return desc->mask_base + desc->msi_attrib.entry_nr * PCI_MSIX_ENTRY_SIZE; @@ -294,7 +294,7 @@ void __pci_read_msi_msg(struct msi_desc *entry, struct msi_msg *msg) } } -void __pci_write_msi_msg(struct msi_desc *entry, struct msi_msg *msg) +void native_write_msi_msg(struct msi_desc *entry, struct msi_msg *msg) { struct pci_dev *dev = msi_desc_to_pci_dev(entry); diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h index 6e0d1528d471..d3fe892641d0 100644 --- a/drivers/pci/pci.h +++ b/drivers/pci/pci.h @@ -168,6 +168,8 @@ static inline void pci_msix_clear_and_set_ctrl(struct pci_dev *dev, u16 clear, u pci_write_config_word(dev, dev->msix_cap + PCI_MSIX_FLAGS, ctrl); } +void __iomem *pci_msix_desc_addr(struct msi_desc *desc); + void pci_realloc_get_opt(char *); static inline int pci_no_d1d2(struct pci_dev *dev) diff --git a/drivers/vhm/Makefile b/drivers/vhm/Makefile index 4bd960d564b3..b4d58a92dcfd 100644 --- a/drivers/vhm/Makefile +++ b/drivers/vhm/Makefile @@ -1 +1 @@ -obj-y += vhm_mm.o vhm_ioreq.o vhm_vm_mngt.o vhm_hypercall.o +obj-y += vhm_mm.o vhm_ioreq.o vhm_vm_mngt.o vhm_msi.o vhm_hypercall.o diff --git a/drivers/vhm/vhm_msi.c b/drivers/vhm/vhm_msi.c new file mode 100644 index 000000000000..73affd60fc46 --- /dev/null +++ b/drivers/vhm/vhm_msi.c @@ -0,0 +1,135 @@ +/* + * virtio and hyperviosr service module (VHM): msi paravirt + * + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright (c) 2017 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * 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. + * + * BSD LICENSE + * + * Copyright (C) 2017 Intel Corporation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 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 + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Jason Chen CJ + * + */ + +#include +#include +#include +#include + +#include "../pci/pci.h" + +static struct msi_msg acrn_notify_msix_remap(struct msi_desc *entry, + struct msi_msg *msg) +{ + volatile struct acrn_vm_pci_msix_remap notify; + struct pci_dev *dev = msi_desc_to_pci_dev(entry); + struct msi_msg remapped_msg = *msg; + u16 msgctl; + int ret; + + pci_read_config_word(dev, dev->msi_cap + PCI_MSI_FLAGS, &msgctl); + + notify.msi_ctl = msgctl; + notify.virt_bdf = (dev->bus->number << 8) | dev->devfn; + notify.msi_addr = msg->address_hi; + notify.msi_addr <<= 32; + notify.msi_addr |= msg->address_lo; + notify.msi_data = msg->data; + notify.msix = !!entry->msi_attrib.is_msix; + + if (notify.msix) + notify.msix_entry_index = entry->msi_attrib.entry_nr; + else + notify.msix_entry_index = 0; + + ret = hcall_remap_pci_msix(0, virt_to_phys(¬ify)); + if (ret < 0) + dev_err(&dev->dev, "Failed to notify MSI/x change to HV\n"); + else { + remapped_msg.address_hi = (unsigned int)(notify.msi_addr >> 32); + remapped_msg.address_lo = (unsigned int)notify.msi_addr; + remapped_msg.data = notify.msi_data; + } + return remapped_msg; +} + +void acrn_write_msi_msg(struct msi_desc *entry, struct msi_msg *msg) +{ + struct pci_dev *dev = msi_desc_to_pci_dev(entry); + struct msi_msg fmsg; + + if (dev->current_state != PCI_D0 || pci_dev_is_disconnected(dev)) { + /* Don't touch the hardware now */ + } else if (entry->msi_attrib.is_msix) { + void __iomem *base = pci_msix_desc_addr(entry); + + fmsg = acrn_notify_msix_remap(entry, msg); + + writel(fmsg.address_lo, base + PCI_MSIX_ENTRY_LOWER_ADDR); + writel(fmsg.address_hi, base + PCI_MSIX_ENTRY_UPPER_ADDR); + writel(fmsg.data, base + PCI_MSIX_ENTRY_DATA); + } else { + int pos = dev->msi_cap; + u16 msgctl; + + fmsg = acrn_notify_msix_remap(entry, msg); + + pci_read_config_word(dev, pos + PCI_MSI_FLAGS, &msgctl); + msgctl &= ~PCI_MSI_FLAGS_QSIZE; + msgctl |= entry->msi_attrib.multiple << 4; + pci_write_config_word(dev, pos + PCI_MSI_FLAGS, msgctl); + + pci_write_config_dword(dev, pos + PCI_MSI_ADDRESS_LO, + fmsg.address_lo); + if (entry->msi_attrib.is_64) { + pci_write_config_dword(dev, pos + PCI_MSI_ADDRESS_HI, + fmsg.address_hi); + pci_write_config_word(dev, pos + PCI_MSI_DATA_64, + fmsg.data); + } else { + pci_write_config_word(dev, pos + PCI_MSI_DATA_32, + fmsg.data); + } + } + entry->msg = *msg; +} diff --git a/include/linux/msi.h b/include/linux/msi.h index 5839d8062dfc..2c1e1d0c5d92 100644 --- a/include/linux/msi.h +++ b/include/linux/msi.h @@ -139,7 +139,15 @@ struct msi_desc *alloc_msi_entry(struct device *dev, int nvec, const struct cpumask *affinity); void free_msi_entry(struct msi_desc *entry); void __pci_read_msi_msg(struct msi_desc *entry, struct msi_msg *msg); -void __pci_write_msi_msg(struct msi_desc *entry, struct msi_msg *msg); + +void native_write_msi_msg(struct msi_desc *entry, struct msi_msg *msg); + +#if defined(CONFIG_PARAVIRT) && defined(CONFIG_X86) +#include +#define __pci_write_msi_msg write_msi_msg_paravirt +#else +#define __pci_write_msi_msg native_write_msi_msg +#endif u32 __pci_msix_desc_mask_irq(struct msi_desc *desc, u32 flag); u32 __pci_msi_desc_mask_irq(struct msi_desc *desc, u32 mask, u32 flag); diff --git a/include/linux/vhm/vhm_msi.h b/include/linux/vhm/vhm_msi.h new file mode 100644 index 000000000000..059e97a0e543 --- /dev/null +++ b/include/linux/vhm/vhm_msi.h @@ -0,0 +1,61 @@ +/* + * virtio and hyperviosr service module (VHM): msi paravirt + * + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright (c) 2017 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * 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. + * + * BSD LICENSE + * + * Copyright (C) 2017 Intel Corporation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 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 + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Jason Chen CJ + * + */ + +#ifndef __ACRN_VHM_MSI_H__ +#define __ACRN_VHM_MSI_H__ + +struct msi_desc; +struct msi_msg; +void acrn_write_msi_msg(struct msi_desc *entry, struct msi_msg *msg); + +#endif From 700fe3f8340a16e8023fdbd50d51ed257faaf074 Mon Sep 17 00:00:00 2001 From: Mingqiang Chi Date: Fri, 31 Aug 2018 10:58:56 +0800 Subject: [PATCH 0012/1103] sos: cleanup hypercall API Put all hypercall APIs into vhm_hypercall.c other modules need to call hypercall API from this file. Change-Id: Id896a8300cf54279151a9d5674ed27a352df3f3f Tracked-On:218445 Signed-off-by: Mingqiang Chi Reviewed-on: Reviewed-by: Dong, Eddie Tested-by: Dong, Eddie --- drivers/char/vhm/vhm_dev.c | 237 +++++++++++++++++++--- drivers/vhm/vhm_hypercall.c | 320 ++++-------------------------- include/linux/vhm/vhm_hypercall.h | 63 +++--- 3 files changed, 283 insertions(+), 337 deletions(-) diff --git a/drivers/char/vhm/vhm_dev.c b/drivers/char/vhm/vhm_dev.c index 97c20d82154f..32de7aeb30a1 100644 --- a/drivers/char/vhm/vhm_dev.c +++ b/drivers/char/vhm/vhm_dev.c @@ -92,6 +92,16 @@ static struct device *vhm_device; static struct tasklet_struct vhm_io_req_tasklet; static atomic_t ioreq_retry = ATOMIC_INIT(0); +/* max num of pass-through devices using msix */ +#define MAX_ENTRY 3 + +struct table_iomems { + /* device's virtual BDF */ + unsigned short virt_bdf; + /* virtual base address of MSI-X table in memory space after ioremap */ + unsigned long mmap_addr; +} tables[MAX_ENTRY]; + static int vhm_dev_open(struct inode *inodep, struct file *filep) { struct vhm_vm *vm; @@ -153,25 +163,66 @@ static long vhm_dev_ioctl(struct file *filep, } switch (ioctl_num) { - case IC_CREATE_VM: - ret = vhm_create_vm(vm, ioctl_param); - break; + case IC_CREATE_VM: { + struct acrn_create_vm created_vm; + + if (copy_from_user(&created_vm, (void *)ioctl_param, + sizeof(struct acrn_create_vm))) + return -EFAULT; + + ret = hcall_create_vm(virt_to_phys(&created_vm)); + if ((ret < 0) || + (created_vm.vmid == ACRN_INVALID_VMID)) { + pr_err("vhm: failed to create VM from Hypervisor !\n"); + return -EFAULT; + } + + if (copy_to_user((void *)ioctl_param, &created_vm, + sizeof(struct acrn_create_vm))) + return -EFAULT; + + vm->vmid = created_vm.vmid; - case IC_RESUME_VM: - ret = vhm_resume_vm(vm); + pr_info("vhm: VM %ld created\n", created_vm.vmid); break; + } - case IC_PAUSE_VM: - ret = vhm_pause_vm(vm); + case IC_RESUME_VM: { + ret = hcall_resume_vm(vm->vmid); + if (ret < 0) { + pr_err("vhm: failed to start VM %ld!\n", vm->vmid); + return -EFAULT; + } break; + } - case IC_DESTROY_VM: - ret = vhm_destroy_vm(vm); + case IC_PAUSE_VM: { + ret = hcall_pause_vm(vm->vmid); + if (ret < 0) { + pr_err("vhm: failed to pause VM %ld!\n", vm->vmid); + return -EFAULT; + } break; + } - case IC_QUERY_VMSTATE: - ret = vhm_query_vm_state(vm); + case IC_DESTROY_VM: { + ret = hcall_destroy_vm(vm->vmid); + if (ret < 0) { + pr_err("failed to destroy VM %ld\n", vm->vmid); + return -EFAULT; + } + vm->vmid = ACRN_INVALID_VMID; break; + } + + case IC_QUERY_VMSTATE: { + ret = hcall_query_vm_state(vm->vmid); + if (ret < 0) { + pr_err("vhm: failed to query VM State%ld!\n", vm->vmid); + return -EFAULT; + } + return ret; + } case IC_ALLOC_MEMSEG: { struct vm_memseg memseg; @@ -239,17 +290,43 @@ static long vhm_dev_ioctl(struct file *filep, } case IC_ASSERT_IRQLINE: { - ret = vhm_assert_irqline(vm, ioctl_param); + struct acrn_irqline irq; + + if (copy_from_user(&irq, (void *)ioctl_param, sizeof(irq))) + return -EFAULT; + + ret = hcall_assert_irqline(vm->vmid, virt_to_phys(&irq)); + if (ret < 0) { + pr_err("vhm: failed to assert irq!\n"); + return -EFAULT; + } break; } - case IC_DEASSERT_IRQLINE: { - ret = vhm_deassert_irqline(vm, ioctl_param); + struct acrn_irqline irq; + + if (copy_from_user(&irq, (void *)ioctl_param, sizeof(irq))) + return -EFAULT; + + ret = hcall_deassert_irqline(vm->vmid, virt_to_phys(&irq)); + if (ret < 0) { + pr_err("vhm: failed to deassert irq!\n"); + return -EFAULT; + } break; } - case IC_PULSE_IRQLINE: { - ret = vhm_pulse_irqline(vm, ioctl_param); + struct acrn_irqline irq; + + if (copy_from_user(&irq, (void *)ioctl_param, sizeof(irq))) + return -EFAULT; + + ret = hcall_pulse_irqline(vm->vmid, + virt_to_phys(&irq)); + if (ret < 0) { + pr_err("vhm: failed to assert irq!\n"); + return -EFAULT; + } break; } @@ -268,27 +345,141 @@ static long vhm_dev_ioctl(struct file *filep, } case IC_ASSIGN_PTDEV: { - ret = vhm_assign_ptdev(vm, ioctl_param); + uint16_t bdf; + + if (copy_from_user(&bdf, + (void *)ioctl_param, sizeof(uint16_t))) + return -EFAULT; + + ret = hcall_assign_ptdev(vm->vmid, virt_to_phys(&bdf)); + if (ret < 0) { + pr_err("vhm: failed to assign ptdev!\n"); + return -EFAULT; + } break; } - case IC_DEASSIGN_PTDEV: { - ret = vhm_deassign_ptdev(vm, ioctl_param); + uint16_t bdf; + + if (copy_from_user(&bdf, + (void *)ioctl_param, sizeof(uint16_t))) + return -EFAULT; + + ret = hcall_deassign_ptdev(vm->vmid, virt_to_phys(&bdf)); + if (ret < 0) { + pr_err("vhm: failed to deassign ptdev!\n"); + return -EFAULT; + } break; } case IC_SET_PTDEV_INTR_INFO: { - ret = vhm_set_ptdev_intr_info(vm, ioctl_param); + struct acrn_ptdev_irq pt_irq; + int i; + + if (copy_from_user(&pt_irq, + (void *)ioctl_param, sizeof(pt_irq))) + return -EFAULT; + + ret = hcall_set_ptdev_intr_info(vm->vmid, + virt_to_phys(&pt_irq)); + if (ret < 0) { + pr_err("vhm: failed to set intr info for ptdev!\n"); + return -EFAULT; + } + + if (pt_irq.msix.table_paddr) { + for (i = 0; i < MAX_ENTRY; i++) { + if (tables[i].virt_bdf) + continue; + + tables[i].virt_bdf = pt_irq.virt_bdf; + tables[i].mmap_addr = + ioremap_nocache(pt_irq.msix.table_paddr, + pt_irq.msix.table_size); + break; + } + } + break; } - case IC_RESET_PTDEV_INTR_INFO: { - ret = vhm_reset_ptdev_intr_info(vm, ioctl_param); + struct acrn_ptdev_irq pt_irq; + int i; + + if (copy_from_user(&pt_irq, + (void *)ioctl_param, sizeof(pt_irq))) + return -EFAULT; + + ret = hcall_reset_ptdev_intr_info(vm->vmid, + virt_to_phys(&pt_irq)); + if (ret < 0) { + pr_err("vhm: failed to reset intr info for ptdev!\n"); + return -EFAULT; + } + + if (pt_irq.msix.table_paddr) { + for (i = 0; i < MAX_ENTRY; i++) { + if (tables[i].virt_bdf) + continue; + + tables[i].virt_bdf = pt_irq.virt_bdf; + tables[i].mmap_addr = + ioremap_nocache(pt_irq.msix.table_paddr, + pt_irq.msix.table_size); + break; + } + } + break; } case IC_VM_PCI_MSIX_REMAP: { - ret = vhm_remap_pci_msix(vm, ioctl_param); + struct acrn_vm_pci_msix_remap msix_remap; + + if (copy_from_user(&msix_remap, + (void *)ioctl_param, sizeof(msix_remap))) + return -EFAULT; + + ret = hcall_remap_pci_msix(vm->vmid, virt_to_phys(&msix_remap)); + + if (copy_to_user((void *)ioctl_param, + &msix_remap, sizeof(msix_remap))) + return -EFAULT; + + if (msix_remap.msix) { + void __iomem *msix_entry; + int i; + + for (i = 0; i < MAX_ENTRY; i++) { + if (tables[i].virt_bdf == msix_remap.virt_bdf) + break; + } + + if (!tables[i].mmap_addr) + return -EFAULT; + + msix_entry = tables[i].mmap_addr + + msix_remap.msix_entry_index * + PCI_MSIX_ENTRY_SIZE; + + /* mask the entry when setup */ + writel(PCI_MSIX_ENTRY_CTRL_MASKBIT, + msix_entry + PCI_MSIX_ENTRY_VECTOR_CTRL); + + /* setup the msi entry */ + writel((uint32_t)msix_remap.msi_addr, + msix_entry + PCI_MSIX_ENTRY_LOWER_ADDR); + writel((uint32_t)(msix_remap.msi_addr >> 32), + msix_entry + PCI_MSIX_ENTRY_UPPER_ADDR); + writel(msix_remap.msi_data, + msix_entry + PCI_MSIX_ENTRY_DATA); + + /* unmask the entry */ + writel(msix_remap.vector_ctl & + PCI_MSIX_ENTRY_CTRL_MASKBIT, + msix_entry + PCI_MSIX_ENTRY_VECTOR_CTRL); + } break; } diff --git a/drivers/vhm/vhm_hypercall.c b/drivers/vhm/vhm_hypercall.c index 0f3f6c1c5f4c..11ca6b86baed 100644 --- a/drivers/vhm/vhm_hypercall.c +++ b/drivers/vhm/vhm_hypercall.c @@ -48,341 +48,97 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * */ -#include -#include -#include +#include #include #include -/* max num of pass-through devices using msix */ -#define MAX_ENTRY 3 - -struct table_iomems { - /* device's virtual BDF */ - unsigned short virt_bdf; - /* virtual base address of MSI-X table in memory space after ioremap */ - unsigned long mmap_addr; -} tables[MAX_ENTRY]; - -inline long hcall_inject_msi(unsigned long vmid, unsigned long msi) +inline long hcall_create_vm(unsigned long vminfo) { - return acrn_hypercall2(HC_INJECT_MSI, vmid, msi); + return acrn_hypercall2(HC_CREATE_VM, 0, vminfo); } -inline long hcall_remap_pci_msix(unsigned long vmid, unsigned long msix) +inline long hcall_resume_vm(unsigned long vmid) { - return acrn_hypercall2(HC_VM_PCI_MSIX_REMAP, vmid, msix); + return acrn_hypercall1(HC_RESUME_VM, vmid); } -inline long hcall_set_ioreq_buffer(unsigned long vmid, unsigned long buffer) +inline long hcall_pause_vm(unsigned long vmid) { - return acrn_hypercall2(HC_SET_IOREQ_BUFFER, vmid, buffer); + return acrn_hypercall1(HC_PAUSE_VM, vmid); } -inline long hcall_notify_req_finish(unsigned long vmid, unsigned long vcpu_mask) +inline long hcall_destroy_vm(unsigned long vmid) { - return acrn_hypercall2(HC_NOTIFY_REQUEST_FINISH, vmid, vcpu_mask); + return acrn_hypercall1(HC_DESTROY_VM, vmid); } -inline long hcall_set_memmap(unsigned long vmid, unsigned long memmap) +inline long hcall_query_vm_state(unsigned long vmid) { - return acrn_hypercall2(HC_VM_SET_MEMMAP, vmid, memmap); + return acrn_hypercall1(HC_QUERY_VMSTATE, vmid); } -inline long hcall_vm_gpa2hpa(unsigned long vmid, unsigned long gpa2hpa) -{ - return acrn_hypercall2(HC_VM_GPA2HPA, vmid, gpa2hpa); -} - -inline long vhm_create_vm(struct vhm_vm *vm, unsigned long ioctl_param) +inline long hcall_set_memmap(unsigned long vmid, unsigned long memmap) { - long ret = 0; - struct acrn_create_vm created_vm; - - if (copy_from_user(&created_vm, (void *)ioctl_param, - sizeof(struct acrn_create_vm))) - return -EFAULT; - - ret = acrn_hypercall2(HC_CREATE_VM, 0, - virt_to_phys(&created_vm)); - if ((ret < 0) || - (created_vm.vmid == ACRN_INVALID_VMID)) { - pr_err("vhm: failed to create VM from Hypervisor !\n"); - return -EFAULT; - } - - if (copy_to_user((void *)ioctl_param, &created_vm, - sizeof(struct acrn_create_vm))) - return -EFAULT; - - vm->vmid = created_vm.vmid; - pr_info("vhm: VM %ld created\n", created_vm.vmid); - - return ret; + return acrn_hypercall2(HC_VM_SET_MEMMAP, vmid, memmap); } -inline long vhm_resume_vm(struct vhm_vm *vm) +inline long hcall_set_ioreq_buffer(unsigned long vmid, unsigned long buffer) { - long ret = 0; - - ret = acrn_hypercall1(HC_RESUME_VM, vm->vmid); - if (ret < 0) { - pr_err("vhm: failed to start VM %ld!\n", vm->vmid); - return -EFAULT; - } - - return ret; + return acrn_hypercall2(HC_SET_IOREQ_BUFFER, vmid, buffer); } -inline long vhm_pause_vm(struct vhm_vm *vm) +inline long hcall_notify_req_finish(unsigned long vmid, unsigned long vcpu_mask) { - long ret = 0; - - ret = acrn_hypercall1(HC_PAUSE_VM, vm->vmid); - if (ret < 0) { - pr_err("vhm: failed to pause VM %ld!\n", vm->vmid); - return -EFAULT; - } - - return ret; + return acrn_hypercall2(HC_NOTIFY_REQUEST_FINISH, vmid, vcpu_mask); } -inline long vhm_destroy_vm(struct vhm_vm *vm) +inline long hcall_assert_irqline(unsigned long vmid, unsigned long irq) { - long ret = 0; - - ret = acrn_hypercall1(HC_DESTROY_VM, vm->vmid); - if (ret < 0) { - pr_err("failed to destroy VM %ld\n", vm->vmid); - return -EFAULT; - } - vm->vmid = ACRN_INVALID_VMID; - - return ret; + return acrn_hypercall2(HC_ASSERT_IRQLINE, vmid, irq); } -inline long vhm_query_vm_state(struct vhm_vm *vm) +inline long hcall_deassert_irqline(unsigned long vmid, unsigned long irq) { - long ret = 0; - - ret = acrn_hypercall1(HC_QUERY_VMSTATE, vm->vmid); - if (ret < 0) { - pr_err("vhm: failed to query VM State%ld!\n", vm->vmid); - return -EFAULT; - } - - return ret; + return acrn_hypercall2(HC_DEASSERT_IRQLINE, vmid, irq); } -inline long vhm_assert_irqline(struct vhm_vm *vm, unsigned long ioctl_param) +inline long hcall_pulse_irqline(unsigned long vmid, unsigned long irq) { - long ret = 0; - struct acrn_irqline irq; - - if (copy_from_user(&irq, (void *)ioctl_param, sizeof(irq))) - return -EFAULT; - - ret = acrn_hypercall2(HC_ASSERT_IRQLINE, vm->vmid, - virt_to_phys(&irq)); - if (ret < 0) { - pr_err("vhm: failed to assert irq!\n"); - return -EFAULT; - } - - return ret; + return acrn_hypercall2(HC_PULSE_IRQLINE, vmid, irq); } -inline long vhm_deassert_irqline(struct vhm_vm *vm, unsigned long ioctl_param) +inline long hcall_inject_msi(unsigned long vmid, unsigned long msi) { - long ret = 0; - struct acrn_irqline irq; - - if (copy_from_user(&irq, (void *)ioctl_param, sizeof(irq))) - return -EFAULT; - - ret = acrn_hypercall2(HC_DEASSERT_IRQLINE, vm->vmid, - virt_to_phys(&irq)); - if (ret < 0) { - pr_err("vhm: failed to deassert irq!\n"); - return -EFAULT; - } - - return ret; + return acrn_hypercall2(HC_INJECT_MSI, vmid, msi); } -inline long vhm_pulse_irqline(struct vhm_vm *vm, unsigned long ioctl_param) +inline long hcall_assign_ptdev(unsigned long vmid, unsigned long bdf) { - long ret = 0; - struct acrn_irqline irq; - - if (copy_from_user(&irq, (void *)ioctl_param, sizeof(irq))) - return -EFAULT; - - ret = acrn_hypercall2(HC_PULSE_IRQLINE, vm->vmid, - virt_to_phys(&irq)); - if (ret < 0) { - pr_err("vhm: failed to assert irq!\n"); - return -EFAULT; - } - - return ret; + return acrn_hypercall2(HC_ASSIGN_PTDEV, vmid, bdf); } -inline long vhm_assign_ptdev(struct vhm_vm *vm, unsigned long ioctl_param) +inline long hcall_deassign_ptdev(unsigned long vmid, unsigned long bdf) { - long ret = 0; - uint16_t bdf; - - if (copy_from_user(&bdf, - (void *)ioctl_param, sizeof(uint16_t))) - return -EFAULT; - - ret = acrn_hypercall2(HC_ASSIGN_PTDEV, vm->vmid, - virt_to_phys(&bdf)); - if (ret < 0) { - pr_err("vhm: failed to assign ptdev!\n"); - return -EFAULT; - } - - return ret; + return acrn_hypercall2(HC_DEASSIGN_PTDEV, vmid, bdf); } -inline long vhm_deassign_ptdev(struct vhm_vm *vm, unsigned long ioctl_param) +inline long hcall_set_ptdev_intr_info(unsigned long vmid, unsigned long pt_irq) { - long ret = 0; - uint16_t bdf; - - if (copy_from_user(&bdf, - (void *)ioctl_param, sizeof(uint16_t))) - return -EFAULT; - - ret = acrn_hypercall2(HC_DEASSIGN_PTDEV, vm->vmid, - virt_to_phys(&bdf)); - if (ret < 0) { - pr_err("vhm: failed to deassign ptdev!\n"); - return -EFAULT; - } - - return ret; + return acrn_hypercall2(HC_SET_PTDEV_INTR_INFO, vmid, pt_irq); } -inline long vhm_set_ptdev_intr_info(struct vhm_vm *vm, - unsigned long ioctl_param) +inline long hcall_reset_ptdev_intr_info(unsigned long vmid, + unsigned long pt_irq) { - long ret = 0; - struct acrn_ptdev_irq pt_irq; - int i; - - if (copy_from_user(&pt_irq, - (void *)ioctl_param, sizeof(pt_irq))) - return -EFAULT; - - ret = acrn_hypercall2(HC_SET_PTDEV_INTR_INFO, vm->vmid, - virt_to_phys(&pt_irq)); - if (ret < 0) { - pr_err("vhm: failed to set intr info for ptdev!\n"); - return -EFAULT; - } - - if (pt_irq.msix.table_paddr) { - for (i = 0; i < MAX_ENTRY; i++) { - if (tables[i].virt_bdf) - continue; - - tables[i].virt_bdf = pt_irq.virt_bdf; - tables[i].mmap_addr = (unsigned long) - ioremap_nocache(pt_irq.msix.table_paddr, - pt_irq.msix.table_size); - break; - } - } - - return ret; + return acrn_hypercall2(HC_RESET_PTDEV_INTR_INFO, vmid, pt_irq); } -inline long vhm_reset_ptdev_intr_info(struct vhm_vm *vm, - unsigned long ioctl_param) +inline long hcall_remap_pci_msix(unsigned long vmid, unsigned long msi) { - long ret = 0; - struct acrn_ptdev_irq pt_irq; - int i; - - if (copy_from_user(&pt_irq, - (void *)ioctl_param, sizeof(pt_irq))) - return -EFAULT; - - ret = acrn_hypercall2(HC_RESET_PTDEV_INTR_INFO, vm->vmid, - virt_to_phys(&pt_irq)); - if (ret < 0) { - pr_err("vhm: failed to reset intr info for ptdev!\n"); - return -EFAULT; - } - - if (pt_irq.msix.table_paddr) { - for (i = 0; i < MAX_ENTRY; i++) { - if (tables[i].virt_bdf) - continue; - - tables[i].virt_bdf = pt_irq.virt_bdf; - tables[i].mmap_addr = (unsigned long) - ioremap_nocache(pt_irq.msix.table_paddr, - pt_irq.msix.table_size); - break; - } - } - - return ret; + return acrn_hypercall2(HC_VM_PCI_MSIX_REMAP, vmid, msi); } -inline long vhm_remap_pci_msix(struct vhm_vm *vm, unsigned long ioctl_param) +inline long hcall_vm_gpa2hpa(unsigned long vmid, unsigned long addr) { - long ret = 0; - struct acrn_vm_pci_msix_remap msix_remap; - - if (copy_from_user(&msix_remap, - (void *)ioctl_param, sizeof(msix_remap))) - return -EFAULT; - - ret = acrn_hypercall2(HC_VM_PCI_MSIX_REMAP, vm->vmid, - virt_to_phys(&msix_remap)); - - if (copy_to_user((void *)ioctl_param, - &msix_remap, sizeof(msix_remap))) - return -EFAULT; - - if (msix_remap.msix) { - void __iomem *msix_entry; - int i; - - for (i = 0; i < MAX_ENTRY; i++) { - if (tables[i].virt_bdf == msix_remap.virt_bdf) - break; - } - - if (!tables[i].mmap_addr) - return -EFAULT; - - msix_entry = (void *)(tables[i].mmap_addr + - msix_remap.msix_entry_index * - PCI_MSIX_ENTRY_SIZE); - - /* mask the entry when setup */ - writel(PCI_MSIX_ENTRY_CTRL_MASKBIT, - msix_entry + PCI_MSIX_ENTRY_VECTOR_CTRL); - - /* setup the msi entry */ - writel((uint32_t)msix_remap.msi_addr, - msix_entry + PCI_MSIX_ENTRY_LOWER_ADDR); - writel((uint32_t)(msix_remap.msi_addr >> 32), - msix_entry + PCI_MSIX_ENTRY_UPPER_ADDR); - writel(msix_remap.msi_data, - msix_entry + PCI_MSIX_ENTRY_DATA); - - /* unmask the entry */ - writel(msix_remap.vector_ctl & - PCI_MSIX_ENTRY_CTRL_MASKBIT, - msix_entry + PCI_MSIX_ENTRY_VECTOR_CTRL); - } - - return ret; + return acrn_hypercall2(HC_VM_GPA2HPA, vmid, addr); } diff --git a/include/linux/vhm/vhm_hypercall.h b/include/linux/vhm/vhm_hypercall.h index ce579e3734ff..35bb48ae6cd3 100644 --- a/include/linux/vhm/vhm_hypercall.h +++ b/include/linux/vhm/vhm_hypercall.h @@ -52,14 +52,12 @@ #ifndef VHM_HYPERCALL_H #define VHM_HYPERCALL_H -#include - -static inline long acrn_hypercall0(unsigned long hyp_id) +static inline long acrn_hypercall0(unsigned long hcall_id) { /* x86-64 System V ABI register usage */ register signed long result asm("rax"); - register unsigned long r8 asm("r8") = hyp_id; + register unsigned long r8 asm("r8") = hcall_id; /* Execute vmcall */ asm volatile(".byte 0x0F,0x01,0xC1\n" @@ -70,12 +68,12 @@ static inline long acrn_hypercall0(unsigned long hyp_id) return result; } -static inline long acrn_hypercall1(unsigned long hyp_id, unsigned long param1) +static inline long acrn_hypercall1(unsigned long hcall_id, unsigned long param1) { /* x86-64 System V ABI register usage */ register signed long result asm("rax"); - register unsigned long r8 asm("r8") = hyp_id; + register unsigned long r8 asm("r8") = hcall_id; /* Execute vmcall */ asm volatile(".byte 0x0F,0x01,0xC1\n" @@ -86,13 +84,13 @@ static inline long acrn_hypercall1(unsigned long hyp_id, unsigned long param1) return result; } -static inline long acrn_hypercall2(unsigned long hyp_id, unsigned long param1, +static inline long acrn_hypercall2(unsigned long hcall_id, unsigned long param1, unsigned long param2) { /* x86-64 System V ABI register usage */ register signed long result asm("rax"); - register unsigned long r8 asm("r8") = hyp_id; + register unsigned long r8 asm("r8") = hcall_id; /* Execute vmcall */ asm volatile(".byte 0x0F,0x01,0xC1\n" @@ -103,13 +101,13 @@ static inline long acrn_hypercall2(unsigned long hyp_id, unsigned long param1, return result; } -static inline long acrn_hypercall3(unsigned long hyp_id, unsigned long param1, +static inline long acrn_hypercall3(unsigned long hcall_id, unsigned long param1, unsigned long param2, unsigned long param3) { /* x86-64 System V ABI register usage */ register signed long result asm("rax"); - register unsigned long r8 asm("r8") = hyp_id; + register unsigned long r8 asm("r8") = hcall_id; /* Execute vmcall */ asm volatile(".byte 0x0F,0x01,0xC1\n" @@ -120,14 +118,14 @@ static inline long acrn_hypercall3(unsigned long hyp_id, unsigned long param1, return result; } -static inline long acrn_hypercall4(unsigned long hyp_id, unsigned long param1, +static inline long acrn_hypercall4(unsigned long hcall_id, unsigned long param1, unsigned long param2, unsigned long param3, unsigned long param4) { /* x86-64 System V ABI register usage */ register signed long result asm("rax"); - register unsigned long r8 asm("r8") = hyp_id; + register unsigned long r8 asm("r8") = hcall_id; /* Execute vmcall */ asm volatile(".byte 0x0F,0x01,0xC1\n" @@ -139,27 +137,28 @@ static inline long acrn_hypercall4(unsigned long hyp_id, unsigned long param1, return result; } -inline long hcall_inject_msi(unsigned long vmid, unsigned long msi); -inline long hcall_remap_pci_msix(unsigned long vmid, unsigned long msix); -inline long hcall_set_ioreq_buffer(unsigned long vmid, unsigned long buffer); +inline long hcall_create_vm(unsigned long vminfo); +inline long hcall_resume_vm(unsigned long vmid); +inline long hcall_pause_vm(unsigned long vmid); +inline long hcall_destroy_vm(unsigned long vmid); +inline long hcall_query_vm_state(unsigned long vmid); +inline long hcall_set_memmap(unsigned long vmid, + unsigned long memmap); +inline long hcall_set_ioreq_buffer(unsigned long vmid, + unsigned long buffer); inline long hcall_notify_req_finish(unsigned long vmid, unsigned long vcpu_mask); -inline long hcall_set_memmap(unsigned long vmid, unsigned long memmap); -inline long hcall_vm_gpa2hpa(unsigned long vmid, unsigned long gpa2hpa); -inline long vhm_create_vm(struct vhm_vm *vm, unsigned long ioctl_param); -inline long vhm_resume_vm(struct vhm_vm *vm); -inline long vhm_pause_vm(struct vhm_vm *vm); -inline long vhm_destroy_vm(struct vhm_vm *vm); -inline long vhm_query_vm_state(struct vhm_vm *vm); -inline long vhm_assert_irqline(struct vhm_vm *vm, unsigned long ioctl_param); -inline long vhm_deassert_irqline(struct vhm_vm *vm, unsigned long ioctl_param); -inline long vhm_pulse_irqline(struct vhm_vm *vm, unsigned long ioctl_param); -inline long vhm_assign_ptdev(struct vhm_vm *vm, unsigned long ioctl_param); -inline long vhm_deassign_ptdev(struct vhm_vm *vm, unsigned long ioctl_param); -inline long vhm_set_ptdev_intr_info(struct vhm_vm *vm, - unsigned long ioctl_param); -inline long vhm_reset_ptdev_intr_info(struct vhm_vm *vm, - unsigned long ioctl_param); -inline long vhm_remap_pci_msix(struct vhm_vm *vm, unsigned long ioctl_param); +inline long hcall_assert_irqline(unsigned long vmid, unsigned long irq); +inline long hcall_deassert_irqline(unsigned long vmid, unsigned long irq); +inline long hcall_pulse_irqline(unsigned long vmid, unsigned long irq); +inline long hcall_inject_msi(unsigned long vmid, unsigned long msi); +inline long hcall_assign_ptdev(unsigned long vmid, unsigned long bdf); +inline long hcall_deassign_ptdev(unsigned long vmid, unsigned long bdf); +inline long hcall_set_ptdev_intr_info(unsigned long vmid, + unsigned long pt_irq); +inline long hcall_reset_ptdev_intr_info(unsigned long vmid, + unsigned long pt_irq); +inline long hcall_remap_pci_msix(unsigned long vmid, unsigned long msi); +inline long hcall_vm_gpa2hpa(unsigned long vmid, unsigned long addr); #endif /* VHM_HYPERCALL_H */ From f180cd87438f2c01ffbd50a7b91a917c9f6cb12c Mon Sep 17 00:00:00 2001 From: Yin Fengwei Date: Fri, 31 Aug 2018 10:58:56 +0800 Subject: [PATCH 0013/1103] vcpu: export vcpu create interface to DM Change-Id: I64a179c2452f67285f347bbaa60c702dec5f55de Tracked-On: 218445 Signed-off-by: Yin Fengwei Reviewed-on: Reviewed-by: Chi, Mingqiang Reviewed-by: Dong, Eddie Tested-by: Dong, Eddie --- drivers/char/vhm/vhm_dev.c | 17 +++++++++++++++++ include/linux/vhm/acrn_common.h | 5 +++++ include/linux/vhm/acrn_hv_defs.h | 1 + include/linux/vhm/vhm_ioctl_defs.h | 1 + 4 files changed, 24 insertions(+) diff --git a/drivers/char/vhm/vhm_dev.c b/drivers/char/vhm/vhm_dev.c index 32de7aeb30a1..d7ad18f6a55e 100644 --- a/drivers/char/vhm/vhm_dev.c +++ b/drivers/char/vhm/vhm_dev.c @@ -224,6 +224,23 @@ static long vhm_dev_ioctl(struct file *filep, return ret; } + case IC_CREATE_VCPU: { + struct acrn_create_vcpu cv; + + if (copy_from_user(&cv, (void *)ioctl_param, + sizeof(struct acrn_create_vcpu))) + return -EFAULT; + + ret = acrn_hypercall2(HC_CREATE_VCPU, vm->vmid, + virt_to_phys(&cv)); + if (ret < 0) { + pr_err("vhm: failed to create vcpu %ld!\n", cv.vcpuid); + return -EFAULT; + } + + return ret; + } + case IC_ALLOC_MEMSEG: { struct vm_memseg memseg; diff --git a/include/linux/vhm/acrn_common.h b/include/linux/vhm/acrn_common.h index 5723e5e2d537..2fa43a288ee5 100644 --- a/include/linux/vhm/acrn_common.h +++ b/include/linux/vhm/acrn_common.h @@ -193,6 +193,11 @@ struct acrn_create_vm { unsigned long vcpu_num; /* IN: VM vcpu number */ } __attribute__((aligned(8))); +struct acrn_create_vcpu { + int vcpuid; /* IN: vcpu id */ + int pcpuid; /* IN: pcpu id */ +} __attribute__((aligned(8))); + struct acrn_set_ioreq_buffer { long req_buf; /* IN: gpa of per VM request_buffer*/ } __attribute__((aligned(8))); diff --git a/include/linux/vhm/acrn_hv_defs.h b/include/linux/vhm/acrn_hv_defs.h index 3e43da56813d..329c38b961e5 100644 --- a/include/linux/vhm/acrn_hv_defs.h +++ b/include/linux/vhm/acrn_hv_defs.h @@ -73,6 +73,7 @@ #define HC_RESUME_VM _HC_ID(HC_ID, HC_ID_VM_BASE + 0x03) #define HC_PAUSE_VM _HC_ID(HC_ID, HC_ID_VM_BASE + 0x04) #define HC_QUERY_VMSTATE _HC_ID(HC_ID, HC_ID_VM_BASE + 0x05) +#define HC_CREATE_VCPU _HC_ID(HC_ID, HC_ID_VM_BASE + 0x06) /* IRQ and Interrupts */ #define HC_ID_IRQ_BASE 0x100UL diff --git a/include/linux/vhm/vhm_ioctl_defs.h b/include/linux/vhm/vhm_ioctl_defs.h index 8d03d38b788d..5ec2d10fc350 100644 --- a/include/linux/vhm/vhm_ioctl_defs.h +++ b/include/linux/vhm/vhm_ioctl_defs.h @@ -63,6 +63,7 @@ #define IC_RESUME_VM _IC_ID(IC_ID, IC_ID_VM_BASE + 0x03) #define IC_PAUSE_VM _IC_ID(IC_ID, IC_ID_VM_BASE + 0x04) #define IC_QUERY_VMSTATE _IC_ID(IC_ID, IC_ID_VM_BASE + 0x05) +#define IC_CREATE_VCPU _IC_ID(IC_ID, IC_ID_VM_BASE + 0x06) /* IRQ and Interrupts */ #define IC_ID_IRQ_BASE 0x100UL From c47b461e0a705f3d205a78e479e73e93c3c4808e Mon Sep 17 00:00:00 2001 From: Shiqing Gao Date: Fri, 31 Aug 2018 10:58:56 +0800 Subject: [PATCH 0014/1103] sos: clean up ptdev msi-x table ioremap operations - use list rather than array to store the virtual base address of MSI-X table in memory space after ioremap - use physical BDF rather than virtual BDF for index purpose this could avoid the issue when different UOSs might use same virtual BDF for different pass-through PCI devices. Change-Id: Ie8330bc5054ba549a11866b84770df5d1a257a6c Tracked-On:218445 Signed-off-by: Shiqing Gao Reviewed-on: Reviewed-by: Chi, Mingqiang Reviewed-by: Dong, Eddie Tested-by: Dong, Eddie --- drivers/char/vhm/vhm_dev.c | 70 +++++++++++++++++++++----------------- 1 file changed, 38 insertions(+), 32 deletions(-) diff --git a/drivers/char/vhm/vhm_dev.c b/drivers/char/vhm/vhm_dev.c index d7ad18f6a55e..5130fb508bc9 100644 --- a/drivers/char/vhm/vhm_dev.c +++ b/drivers/char/vhm/vhm_dev.c @@ -75,6 +75,7 @@ #include #include #include +#include #include #include @@ -92,15 +93,16 @@ static struct device *vhm_device; static struct tasklet_struct vhm_io_req_tasklet; static atomic_t ioreq_retry = ATOMIC_INIT(0); -/* max num of pass-through devices using msix */ -#define MAX_ENTRY 3 - struct table_iomems { - /* device's virtual BDF */ - unsigned short virt_bdf; + /* list node for this table_iomems */ + struct list_head list; + /* device's physical BDF */ + unsigned short phys_bdf; /* virtual base address of MSI-X table in memory space after ioremap */ unsigned long mmap_addr; -} tables[MAX_ENTRY]; +}; +static LIST_HEAD(table_iomems_list); +static DEFINE_MUTEX(table_iomems_lock); static int vhm_dev_open(struct inode *inodep, struct file *filep) { @@ -392,7 +394,7 @@ static long vhm_dev_ioctl(struct file *filep, case IC_SET_PTDEV_INTR_INFO: { struct acrn_ptdev_irq pt_irq; - int i; + struct table_iomems *new; if (copy_from_user(&pt_irq, (void *)ioctl_param, sizeof(pt_irq))) @@ -406,23 +408,24 @@ static long vhm_dev_ioctl(struct file *filep, } if (pt_irq.msix.table_paddr) { - for (i = 0; i < MAX_ENTRY; i++) { - if (tables[i].virt_bdf) - continue; - - tables[i].virt_bdf = pt_irq.virt_bdf; - tables[i].mmap_addr = - ioremap_nocache(pt_irq.msix.table_paddr, + new = kmalloc(sizeof(struct table_iomems), GFP_KERNEL); + if (new == NULL) + return -EFAULT; + new->phys_bdf = pt_irq.phys_bdf; + new->mmap_addr = (unsigned long) + ioremap_nocache(pt_irq.msix.table_paddr, pt_irq.msix.table_size); - break; - } + + mutex_lock(&table_iomems_lock); + list_add(&new->list, &table_iomems_list); + mutex_unlock(&table_iomems_lock); } break; } case IC_RESET_PTDEV_INTR_INFO: { struct acrn_ptdev_irq pt_irq; - int i; + struct table_iomems *new; if (copy_from_user(&pt_irq, (void *)ioctl_param, sizeof(pt_irq))) @@ -436,16 +439,17 @@ static long vhm_dev_ioctl(struct file *filep, } if (pt_irq.msix.table_paddr) { - for (i = 0; i < MAX_ENTRY; i++) { - if (tables[i].virt_bdf) - continue; - - tables[i].virt_bdf = pt_irq.virt_bdf; - tables[i].mmap_addr = - ioremap_nocache(pt_irq.msix.table_paddr, + new = kmalloc(sizeof(struct table_iomems), GFP_KERNEL); + if (new == NULL) + return -EFAULT; + new->phys_bdf = pt_irq.phys_bdf; + new->mmap_addr = (unsigned long) + ioremap_nocache(pt_irq.msix.table_paddr, pt_irq.msix.table_size); - break; - } + + mutex_lock(&table_iomems_lock); + list_add(&new->list, &table_iomems_list); + mutex_unlock(&table_iomems_lock); } break; @@ -466,19 +470,21 @@ static long vhm_dev_ioctl(struct file *filep, if (msix_remap.msix) { void __iomem *msix_entry; - int i; + struct table_iomems *ptr; - for (i = 0; i < MAX_ENTRY; i++) { - if (tables[i].virt_bdf == msix_remap.virt_bdf) + mutex_lock(&table_iomems_lock); + list_for_each_entry(ptr, &table_iomems_list, list) { + if (ptr->phys_bdf == msix_remap.phys_bdf) break; } + mutex_unlock(&table_iomems_lock); - if (!tables[i].mmap_addr) + if (!ptr->mmap_addr) return -EFAULT; - msix_entry = tables[i].mmap_addr + + msix_entry = (void __iomem *) (ptr->mmap_addr + msix_remap.msix_entry_index * - PCI_MSIX_ENTRY_SIZE; + PCI_MSIX_ENTRY_SIZE); /* mask the entry when setup */ writel(PCI_MSIX_ENTRY_CTRL_MASKBIT, From c0506bb303b2548ed93183a966c04fd855f458c0 Mon Sep 17 00:00:00 2001 From: Mingqiang Chi Date: Fri, 31 Aug 2018 10:58:56 +0800 Subject: [PATCH 0015/1103] sos: Update the common head file --remove unused data structures --move data structure(vm_gpa2hpa) to acrn_hv_defs.h --combine 2 data structures vm_exit(dm) and vhm_requeset(hv sos) to vhm_request and put it in acrn_common.h Change-Id: Ice1f93bf7083b08001b2dfdea257aa7d58e9e751 Tracked-On:218445 Signed-off-by: Mingqiang Chi Reviewed-on: Reviewed-by: Dong, Eddie Tested-by: Dong, Eddie --- include/linux/vhm/acrn_common.h | 29 ++++++----------------------- include/linux/vhm/acrn_hv_defs.h | 5 +++++ 2 files changed, 11 insertions(+), 23 deletions(-) diff --git a/include/linux/vhm/acrn_common.h b/include/linux/vhm/acrn_common.h index 2fa43a288ee5..0e9293d08e01 100644 --- a/include/linux/vhm/acrn_common.h +++ b/include/linux/vhm/acrn_common.h @@ -56,12 +56,6 @@ * Commmon structures for ACRN/VHM/DM */ -enum irq_mode { - IRQ_PULSE, - IRQ_ASSERT, - IRQ_DEASSERT, -} __attribute__((aligned(4))); - /* ISA type * inject interrut to both PIC and IOAPIC */ @@ -114,14 +108,6 @@ struct msr_request { long value; } __attribute__((aligned(8))); -struct cpuid_request { - long eax_in; - long ecx_in; - long eax_out; - long ebx_out; - long ecx_out; - long edx_out; -} __attribute__((aligned(8))); struct mmio_request { enum request_direction direction; @@ -151,13 +137,15 @@ struct pci_request { /* vhm_request are 256Bytes aligned */ struct vhm_request { /* offset: 0bytes - 63bytes */ - enum request_type type; - int reserved0[15]; - + union { + int exitcode; + enum request_type type; + unsigned long rip; + int reserved0[16]; + }; /* offset: 64bytes-127bytes */ union { struct msr_request msr_request; - struct cpuid_request cpuid_request; struct io_request pio_request; struct pci_request pci_request; struct mmio_request mmio_request; @@ -225,11 +213,6 @@ struct acrn_nmi_entry { unsigned long vcpuid; /* IN: -1 means vcpu0 */ } __attribute__((aligned(8))); -struct vm_gpa2hpa { - unsigned long gpa; /* IN: gpa to translation */ - unsigned long hpa; /* OUT: -1 means invalid gpa */ -} __attribute__((aligned(8))); - struct acrn_ptdev_irq { enum irq_type type; unsigned short virt_bdf; /* IN: Device virtual BDF# */ diff --git a/include/linux/vhm/acrn_hv_defs.h b/include/linux/vhm/acrn_hv_defs.h index 329c38b961e5..1d21bf21c91c 100644 --- a/include/linux/vhm/acrn_hv_defs.h +++ b/include/linux/vhm/acrn_hv_defs.h @@ -127,4 +127,9 @@ struct vm_set_memmap { int prot; } __attribute__((aligned(8))); +struct vm_gpa2hpa { + unsigned long gpa; /* IN: gpa to translation */ + unsigned long hpa; /* OUT: -1 means invalid gpa */ +} __attribute__((aligned(8))); + #endif /* ACRN_HV_DEFS_H */ From 701f412a95c62e70da5963cf127badb75f8d8f87 Mon Sep 17 00:00:00 2001 From: Binbin Wu Date: Fri, 31 Aug 2018 10:58:56 +0800 Subject: [PATCH 0016/1103] sos: cleanup ptdev irq structure - Use individual data struct of ptdev irq for ioctl and hypercall Change-Id: Id7b02038d0c149a0d1206206f18d54c91c7350d3 Tracked-On: 218445 Signed-off-by: Binbin Wu Reviewed-on: Reviewed-by: Chi, Mingqiang Reviewed-by: Dong, Eddie Tested-by: Dong, Eddie --- drivers/char/vhm/vhm_dev.c | 38 +++++++++++++++++------------- include/linux/vhm/acrn_hv_defs.h | 20 ++++++++++++++++ include/linux/vhm/vhm_ioctl_defs.h | 27 +++++++++++++++++++++ 3 files changed, 69 insertions(+), 16 deletions(-) diff --git a/drivers/char/vhm/vhm_dev.c b/drivers/char/vhm/vhm_dev.c index 5130fb508bc9..ebada9a11552 100644 --- a/drivers/char/vhm/vhm_dev.c +++ b/drivers/char/vhm/vhm_dev.c @@ -151,9 +151,13 @@ static long vhm_dev_ioctl(struct file *filep, { long ret = 0; struct vhm_vm *vm; + struct ic_ptdev_irq ic_pt_irq; + struct hc_ptdev_irq hc_pt_irq; trace_printk("[%s] ioctl_num=0x%x\n", __func__, ioctl_num); + memset(&hc_pt_irq, 0, sizeof(hc_pt_irq)); + memset(&ic_pt_irq, 0, sizeof(ic_pt_irq)); vm = (struct vhm_vm *)filep->private_data; if (vm == NULL) { pr_err("vhm: invalid VM !\n"); @@ -393,28 +397,29 @@ static long vhm_dev_ioctl(struct file *filep, } case IC_SET_PTDEV_INTR_INFO: { - struct acrn_ptdev_irq pt_irq; struct table_iomems *new; - if (copy_from_user(&pt_irq, - (void *)ioctl_param, sizeof(pt_irq))) + if (copy_from_user(&ic_pt_irq, + (void *)ioctl_param, sizeof(ic_pt_irq))) return -EFAULT; + memcpy(&hc_pt_irq, &ic_pt_irq, sizeof(hc_pt_irq)); + ret = hcall_set_ptdev_intr_info(vm->vmid, - virt_to_phys(&pt_irq)); + virt_to_phys(&hc_pt_irq)); if (ret < 0) { pr_err("vhm: failed to set intr info for ptdev!\n"); return -EFAULT; } - if (pt_irq.msix.table_paddr) { + if (ic_pt_irq.msix.table_paddr) { new = kmalloc(sizeof(struct table_iomems), GFP_KERNEL); if (new == NULL) return -EFAULT; - new->phys_bdf = pt_irq.phys_bdf; + new->phys_bdf = ic_pt_irq.phys_bdf; new->mmap_addr = (unsigned long) - ioremap_nocache(pt_irq.msix.table_paddr, - pt_irq.msix.table_size); + ioremap_nocache(ic_pt_irq.msix.table_paddr, + ic_pt_irq.msix.table_size); mutex_lock(&table_iomems_lock); list_add(&new->list, &table_iomems_list); @@ -424,28 +429,29 @@ static long vhm_dev_ioctl(struct file *filep, break; } case IC_RESET_PTDEV_INTR_INFO: { - struct acrn_ptdev_irq pt_irq; struct table_iomems *new; - if (copy_from_user(&pt_irq, - (void *)ioctl_param, sizeof(pt_irq))) + if (copy_from_user(&ic_pt_irq, + (void *)ioctl_param, sizeof(ic_pt_irq))) return -EFAULT; + memcpy(&hc_pt_irq, &ic_pt_irq, sizeof(hc_pt_irq)); + ret = hcall_reset_ptdev_intr_info(vm->vmid, - virt_to_phys(&pt_irq)); + virt_to_phys(&hc_pt_irq)); if (ret < 0) { pr_err("vhm: failed to reset intr info for ptdev!\n"); return -EFAULT; } - if (pt_irq.msix.table_paddr) { + if (ic_pt_irq.msix.table_paddr) { new = kmalloc(sizeof(struct table_iomems), GFP_KERNEL); if (new == NULL) return -EFAULT; - new->phys_bdf = pt_irq.phys_bdf; + new->phys_bdf = ic_pt_irq.phys_bdf; new->mmap_addr = (unsigned long) - ioremap_nocache(pt_irq.msix.table_paddr, - pt_irq.msix.table_size); + ioremap_nocache(ic_pt_irq.msix.table_paddr, + ic_pt_irq.msix.table_size); mutex_lock(&table_iomems_lock); list_add(&new->list, &table_iomems_list); diff --git a/include/linux/vhm/acrn_hv_defs.h b/include/linux/vhm/acrn_hv_defs.h index 1d21bf21c91c..eeac0e9b4e76 100644 --- a/include/linux/vhm/acrn_hv_defs.h +++ b/include/linux/vhm/acrn_hv_defs.h @@ -132,4 +132,24 @@ struct vm_gpa2hpa { unsigned long hpa; /* OUT: -1 means invalid gpa */ } __attribute__((aligned(8))); +struct hc_ptdev_irq { +#define IRQ_INTX 0 +#define IRQ_MSI 1 +#define IRQ_MSIX 2 + uint32_t type; + uint16_t virt_bdf; /* IN: Device virtual BDF# */ + uint16_t phys_bdf; /* IN: Device physical BDF# */ + union { + struct { + uint32_t virt_pin; /* IN: virtual IOAPIC pin */ + uint32_t phys_pin; /* IN: physical IOAPIC pin */ + uint32_t pic_pin; /* IN: pin from PIC? */ + } intx; + struct { + /* IN: vector count of MSI/MSIX */ + uint32_t vector_cnt; + } msix; + }; +} __attribute__((aligned(8))); + #endif /* ACRN_HV_DEFS_H */ diff --git a/include/linux/vhm/vhm_ioctl_defs.h b/include/linux/vhm/vhm_ioctl_defs.h index 5ec2d10fc350..df07e3c93467 100644 --- a/include/linux/vhm/vhm_ioctl_defs.h +++ b/include/linux/vhm/vhm_ioctl_defs.h @@ -129,4 +129,31 @@ struct vm_memmap { }; }; +struct ic_ptdev_irq { +#define IRQ_INTX 0 +#define IRQ_MSI 1 +#define IRQ_MSIX 2 + uint32_t type; + uint16_t virt_bdf; /* IN: Device virtual BDF# */ + uint16_t phys_bdf; /* IN: Device physical BDF# */ + union { + struct { + uint32_t virt_pin; /* IN: virtual IOAPIC pin */ + uint32_t phys_pin; /* IN: physical IOAPIC pin */ + uint32_t pic_pin; /* IN: pin from PIC? */ + } intx; + struct { + /* IN: vector count of MSI/MSIX, + * Keep this filed on top of msix */ + uint32_t vector_cnt; + + /* IN: size of MSI-X table (round up to 4K) */ + uint32_t table_size; + + /* IN: physical address of MSI-X table */ + uint64_t table_paddr; + } msix; + }; +}; + #endif /* VHM_IOCTL_DEFS_H */ From da48a8f469e4b4521d222431a86b9ca5bc43e3c2 Mon Sep 17 00:00:00 2001 From: Hao Li Date: Fri, 31 Aug 2018 10:58:56 +0800 Subject: [PATCH 0017/1103] VBS-K (Virtio Backend Service in Kernel): a kernel-level virtio framework for ACRN hypervisor. This patch added the basic VBS-K framework including the following: - Definitions of the data structures shared between VBS-K and its counterpart in userspace, which is VBS-U; - VBS-K device common data structures; - Core runtime control logic of the VBS-K framework; Change-Id: I8d9e86de701c1aef965a2490f398a2360cb5bd92 Tracked-On:218445 Signed-off-by: Hao Li Reviewed-on: Reviewed-by: Chi, Mingqiang Reviewed-by: Dong, Eddie Tested-by: Dong, Eddie --- drivers/Kconfig | 2 + drivers/Makefile | 1 + drivers/vbs/Kconfig | 20 +++++ drivers/vbs/Makefile | 3 + drivers/vbs/vbs.c | 125 ++++++++++++++++++++++++++++++ include/linux/vbs/vbs.h | 98 +++++++++++++++++++++++ include/linux/vbs/vbs_common_if.h | 78 +++++++++++++++++++ 7 files changed, 327 insertions(+) create mode 100644 drivers/vbs/Kconfig create mode 100644 drivers/vbs/Makefile create mode 100644 drivers/vbs/vbs.c create mode 100644 include/linux/vbs/vbs.h create mode 100644 include/linux/vbs/vbs_common_if.h diff --git a/drivers/Kconfig b/drivers/Kconfig index ab4d43923c4d..b9ac6b8cbdd4 100644 --- a/drivers/Kconfig +++ b/drivers/Kconfig @@ -219,4 +219,6 @@ source "drivers/siox/Kconfig" source "drivers/slimbus/Kconfig" +source "drivers/vbs/Kconfig" + endmenu diff --git a/drivers/Makefile b/drivers/Makefile index 07528374561a..6d0c3cd78ffd 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -146,6 +146,7 @@ obj-$(CONFIG_OF) += of/ obj-$(CONFIG_SSB) += ssb/ obj-$(CONFIG_BCMA) += bcma/ obj-$(CONFIG_VHOST_RING) += vhost/ +obj-$(CONFIG_VBS) += vbs/ obj-$(CONFIG_VHOST) += vhost/ obj-$(CONFIG_VLYNQ) += vlynq/ obj-$(CONFIG_STAGING) += staging/ diff --git a/drivers/vbs/Kconfig b/drivers/vbs/Kconfig new file mode 100644 index 000000000000..156c3162fd63 --- /dev/null +++ b/drivers/vbs/Kconfig @@ -0,0 +1,20 @@ +# +# This Kconfig describes VBS for ACRN hypervisor +# +config VBS + tristate "Enable VBS framework for ACRN hypervisor" + depends on ACRN + depends on ACRN_VHM + default n + ---help--- + This option is selected by any driver which needs to use + the Virtio Backend Service (VBS) framework on ACRN + hypervisor. + +config VBS_DEBUG + bool "ACRN VBS debugging" + depends on VBS != n + default n + ---help--- + This is an option for use by developers; most people should + say N here. This enables ACRN VBS debugging. diff --git a/drivers/vbs/Makefile b/drivers/vbs/Makefile new file mode 100644 index 000000000000..b52b65b6bd13 --- /dev/null +++ b/drivers/vbs/Makefile @@ -0,0 +1,3 @@ +ccflags-$(CONFIG_VBS_DEBUG) := -DDEBUG + +obj-$(CONFIG_VBS) += vbs.o diff --git a/drivers/vbs/vbs.c b/drivers/vbs/vbs.c new file mode 100644 index 000000000000..591d43dbe536 --- /dev/null +++ b/drivers/vbs/vbs.c @@ -0,0 +1,125 @@ +/* + * ACRN Project + * Virtio Backend Service (VBS) for ACRN hypervisor + * + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright (c) 2017 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * 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. + * + * Contact Information: Hao Li + * + * BSD LICENSE + * + * Copyright (c) 2017 Intel Corporation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 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 + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * + * Hao Li + * Created Virtio Backend Service (VBS) framework: + * - VBS-K is a kernel-level virtio framework that can be used for + * virtio backend driver development for ACRN hypervisor. + * - VBS-K should be working with VBS-U (Virtio Backend Service in + * User) together, in order to connect with virtio frontend driver. + * - VBS-K mainly handles data plane part of a virtio backend driver, + * such as virtqueue parsing and processing, while VBS-U mainly + * hanldes control plane part. + */ + +#include +#include +#include + +static long virtio_dev_info_set(struct virtio_dev_info *dev, + struct vbs_dev_info __user *i) +{ + struct vbs_dev_info info; + + if (copy_from_user(&info, i, sizeof(struct vbs_dev_info))) + return -EFAULT; + + /* setup struct virtio_dev_info based on info in vbs_dev_info */ + strncpy(dev->name, info.name, VBS_NAME_LEN); + dev->_ctx.vmid = info.vmid; + dev->negotiated_features = info.negotiated_features; + dev->io_range_start = info.pio_range_start; + dev->io_range_len = info.pio_range_len; + dev->io_range_type = PIO_RANGE; + + return 0; +} + +long virtio_dev_ioctl(struct virtio_dev_info *dev, unsigned int ioctl, + void __user *argp) +{ + long ret; + + /* + * Currently we don't conduct ownership checking, + * but assuming caller would have device mutex. + */ + + switch (ioctl) { + case VBS_SET_DEV: + ret = virtio_dev_info_set(dev, argp); + break; + default: + ret = -ENOIOCTLCMD; + break; + } + return ret; +} +EXPORT_SYMBOL_GPL(virtio_dev_ioctl); + +static int __init vbs_init(void) +{ + return 0; +} + +static void __exit vbs_exit(void) +{ +} + +module_init(vbs_init); +module_exit(vbs_exit); + +MODULE_VERSION("0.1"); +MODULE_AUTHOR("Intel Corporation"); +MODULE_LICENSE("GPL and additional rights"); +MODULE_DESCRIPTION("Virtio Backend Service framework for ACRN hypervisor"); diff --git a/include/linux/vbs/vbs.h b/include/linux/vbs/vbs.h new file mode 100644 index 000000000000..7b876782fe41 --- /dev/null +++ b/include/linux/vbs/vbs.h @@ -0,0 +1,98 @@ +/* + * ACRN Project + * Virtio Backend Service (VBS) for ACRN hypervisor + * + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright (c) 2017 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * 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. + * + * Contact Information: Hao Li + * + * BSD LICENSE + * + * Copyright (c) 2017 Intel Corporation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 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 + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * + * Hao Li + * Define data structures and runtime control APIs for VBS framework. + * - VBS-K is a kernel-level virtio framework that can be used for + * virtio backend driver development for ACRN hypervisor. + * - VBS-K should be working with VBS-U (Virtio Backend Service in + * User) together, in order to connect with virtio frontend driver. + */ + +#ifndef _VBS_H_ +#define _VBS_H_ + +#include + +/* + * VBS-K device needs to handle frontend driver's kick in kernel. + * For virtio 0.9.5, the kick register is a PIO register, + * for virtio 1.0+, the kick register could be a MMIO register. + */ +enum IORangeType { + PIO_RANGE = 0x0, /* default */ + MMIO_RANGE = 0x1, +}; + +/* device context */ +struct ctx { + /* VHM required info */ + int vmid; +}; + +/* struct used to maintain virtio device info from userspace VBS */ +struct virtio_dev_info { + /* dev info from VBS */ + char name[VBS_NAME_LEN]; /* VBS device name */ + struct ctx _ctx; /* device context */ + uint32_t negotiated_features; /* features after guest loads driver */ + uint64_t io_range_start; /* IO range start of VBS device */ + uint64_t io_range_len; /* IO range len of VBS device */ + enum IORangeType io_range_type; /* IO range type, PIO or MMIO */ +}; + +/* VBS Runtime Control APIs */ +long virtio_dev_ioctl(struct virtio_dev_info *dev, unsigned int ioctl, + void __user *argp); + +#endif diff --git a/include/linux/vbs/vbs_common_if.h b/include/linux/vbs/vbs_common_if.h new file mode 100644 index 000000000000..78b36a6c58c8 --- /dev/null +++ b/include/linux/vbs/vbs_common_if.h @@ -0,0 +1,78 @@ +/* + * ACRN Project + * Virtio Backend Service (VBS) for ACRN hypervisor + * + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright (c) 2017 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * 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. + * + * Contact Information: Hao Li + * + * BSD LICENSE + * + * Copyright (c) 2017 Intel Corporation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 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 + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * + * Hao Li + * - Define data structures shared between VBS userspace and VBS kernel + * space. + */ + +#ifndef _VBS_COMMON_IF_H_ +#define _VBS_COMMON_IF_H_ + +#define VBS_NAME_LEN 32 + +struct vbs_dev_info { + char name[VBS_NAME_LEN];/* VBS name */ + int vmid; /* id of VM this device belongs to */ + uint32_t negotiated_features; + /* features after VIRTIO_CONFIG_S_DRIVER_OK */ + uint64_t pio_range_start; + /* start of PIO range initialized by guest OS */ + uint64_t pio_range_len; /* len of PIO range initialized by guest OS */ +}; + +#define VBS_IOCTL 0xAF + +#define VBS_SET_DEV _IOW(VBS_IOCTL, 0x00, struct vbs_dev_info) + +#endif From eb44d2797b25dc8cce37f00e8b94af2e48cd5411 Mon Sep 17 00:00:00 2001 From: Hao Li Date: Fri, 31 Aug 2018 10:58:56 +0800 Subject: [PATCH 0018/1103] VBS-K: virtqueue initialization API. This patch added the following to the VBS-K framework: - virtqueue data structures shared between VBS-K and its counterpart in userspace, which is VBS-U; - virtqueue initialization API; Change-Id: Ib928ea94cb4f33cf30abd17921089afc14518365 Tracked-On:218445 Signed-off-by: Hao Li Reviewed-on: Reviewed-by: Chi, Mingqiang Reviewed-by: Dong, Eddie Tested-by: Dong, Eddie --- drivers/vbs/Makefile | 1 + drivers/vbs/vbs.c | 75 ++++++++++++++++++ drivers/vbs/vq.c | 125 ++++++++++++++++++++++++++++++ include/linux/vbs/vbs.h | 57 ++++++++++++++ include/linux/vbs/vbs_common_if.h | 18 +++++ include/linux/vbs/vq.h | 99 +++++++++++++++++++++++ 6 files changed, 375 insertions(+) create mode 100644 drivers/vbs/vq.c create mode 100644 include/linux/vbs/vq.h diff --git a/drivers/vbs/Makefile b/drivers/vbs/Makefile index b52b65b6bd13..cbd5076e2313 100644 --- a/drivers/vbs/Makefile +++ b/drivers/vbs/Makefile @@ -1,3 +1,4 @@ ccflags-$(CONFIG_VBS_DEBUG) := -DDEBUG obj-$(CONFIG_VBS) += vbs.o +obj-$(CONFIG_VBS) += vq.o diff --git a/drivers/vbs/vbs.c b/drivers/vbs/vbs.c index 591d43dbe536..1e7a9645a353 100644 --- a/drivers/vbs/vbs.c +++ b/drivers/vbs/vbs.c @@ -65,6 +65,66 @@ #include #include #include +#include + +static long virtio_vqs_info_set(struct virtio_dev_info *dev, + struct vbs_vqs_info __user *i) +{ + struct vbs_vqs_info info; + struct virtio_vq_info *vq; + int j; + + vq = dev->vqs; + + if (copy_from_user(&info, i, sizeof(struct vbs_vqs_info))) + return -EFAULT; + + /* setup struct virtio_vq_info based on info in struct vbs_vq_info */ + if (dev->nvq && dev->nvq != info.nvq) { + pr_err("Oops! dev's nvq != vqs's nvq. Not the same device?\n"); + return -EFAULT; + } + + for (j = 0; j < info.nvq; j++) { + vq->qsize = info.vqs[j].qsize; + vq->pfn = info.vqs[j].pfn; + vq->msix_idx = info.vqs[j].msix_idx; + vq->msix_addr = info.vqs[j].msix_addr; + vq->msix_data = info.vqs[j].msix_data; + + pr_debug("msix id %x, addr %llx, data %x\n", vq->msix_idx, + vq->msix_addr, vq->msix_data); + + virtio_vq_init(vq, vq->pfn); + + vq++; + } + + return 0; +} + +/* invoked by VBS-K device's ioctl routine */ +long virtio_vqs_ioctl(struct virtio_dev_info *dev, unsigned int ioctl, + void __user *argp) +{ + long ret; + + /* + * Currently we don't conduct ownership checking, + * but assuming caller would have device mutex. + */ + + switch (ioctl) { + case VBS_SET_VQ: + ret = virtio_vqs_info_set(dev, argp); + break; + default: + ret = -ENOIOCTLCMD; + break; + } + return ret; +} +EXPORT_SYMBOL_GPL(virtio_vqs_ioctl); static long virtio_dev_info_set(struct virtio_dev_info *dev, struct vbs_dev_info __user *i) @@ -77,6 +137,7 @@ static long virtio_dev_info_set(struct virtio_dev_info *dev, /* setup struct virtio_dev_info based on info in vbs_dev_info */ strncpy(dev->name, info.name, VBS_NAME_LEN); dev->_ctx.vmid = info.vmid; + dev->nvq = info.nvq; dev->negotiated_features = info.negotiated_features; dev->io_range_start = info.pio_range_start; dev->io_range_len = info.pio_range_len; @@ -85,6 +146,7 @@ static long virtio_dev_info_set(struct virtio_dev_info *dev, return 0; } +/* invoked by VBS-K device's ioctl routine */ long virtio_dev_ioctl(struct virtio_dev_info *dev, unsigned int ioctl, void __user *argp) { @@ -107,6 +169,19 @@ long virtio_dev_ioctl(struct virtio_dev_info *dev, unsigned int ioctl, } EXPORT_SYMBOL_GPL(virtio_dev_ioctl); +/* called in VBS-K device's .open() */ +long virtio_dev_init(struct virtio_dev_info *dev, + struct virtio_vq_info *vqs, int nvq) +{ + int i; + + for (i = 0; i < nvq; i++) + virtio_vq_reset(&vqs[i]); + + return 0; +} +EXPORT_SYMBOL_GPL(virtio_dev_init); + static int __init vbs_init(void) { return 0; diff --git a/drivers/vbs/vq.c b/drivers/vbs/vq.c new file mode 100644 index 000000000000..95a6757a1c85 --- /dev/null +++ b/drivers/vbs/vq.c @@ -0,0 +1,125 @@ +/* + * ACRN Project + * Virtio Backend Service (VBS) for ACRN hypervisor + * + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright (c) 2017 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * 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. + * + * Contact Information: Hao Li + * + * BSD LICENSE + * + * Copyright (c) 2017 Intel Corporation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 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 + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Chris Torek + * Hao Li + * Created Virtqueue APIs for ACRN VBS framework: + * - VBS-K is a kernel-level virtio framework that can be used for + * virtio backend driver development for ACRN hypervisor. + * - Virtqueue APIs abstract away the details of the internal data + * structures of virtqueue, so that callers could easily access + * the data from guest through virtqueues. + */ + +#include +#include +#include +#include + +/* helper function for remote memory map */ +void * paddr_guest2host(struct ctx *ctx, uintptr_t gaddr, size_t len) +{ + return map_guest_phys(ctx->vmid, gaddr, len); +} + +/* + * Initialize the currently-selected virtqueue. + * The guest just gave us a page frame number, from which we can + * calculate the addresses of the queue. + */ +void virtio_vq_init(struct virtio_vq_info *vq, uint32_t pfn) +{ + uint64_t phys; + size_t size; + char *base; + struct ctx *ctx; + + ctx = &vq->dev->_ctx; + + phys = (uint64_t)pfn << VRING_PAGE_BITS; + size = virtio_vq_ring_size(vq->qsize); + base = paddr_guest2host(ctx, phys, size); + + /* First page(s) are descriptors... */ + vq->desc = (struct virtio_desc *)base; + base += vq->qsize * sizeof(struct virtio_desc); + + /* ... immediately followed by "avail" ring (entirely uint16_t's) */ + vq->avail = (struct vring_avail *)base; + base += (2 + vq->qsize + 1) * sizeof(uint16_t); + + /* Then it's rounded up to the next page... */ + base = (char *)roundup2((uintptr_t)base, VRING_ALIGN); + + /* ... and the last page(s) are the used ring. */ + vq->used = (struct vring_used *)base; + + /* Mark queue as allocated, and start at 0 when we use it. */ + vq->flags = VQ_ALLOC; + vq->last_avail = 0; + vq->save_used = 0; +} + +/* reset one virtqueue, make it invalid */ +void virtio_vq_reset(struct virtio_vq_info *vq) +{ + if (!vq) { + pr_info("%s: vq is NULL!\n", __func__); + return; + } + + vq->pfn = 0; + vq->msix_idx = VIRTIO_MSI_NO_VECTOR; + vq->flags = 0; + vq->last_avail = 0; + vq->save_used = 0; +} diff --git a/include/linux/vbs/vbs.h b/include/linux/vbs/vbs.h index 7b876782fe41..715c49156a1a 100644 --- a/include/linux/vbs/vbs.h +++ b/include/linux/vbs/vbs.h @@ -80,19 +80,76 @@ struct ctx { int vmid; }; +struct virtio_desc { /* AKA vring_desc */ + uint64_t addr; /* guest physical address */ + uint32_t len; /* length of scatter/gather seg */ + uint16_t flags; /* desc flags */ + uint16_t next; /* next desc if F_NEXT */ +} __attribute__((packed)); + +struct virtio_used { /* AKA vring_used_elem */ + uint32_t idx; /* head of used descriptor chain */ + uint32_t len; /* length written-to */ +} __attribute__((packed)); + +struct vring_avail { + uint16_t flags; /* vring_avail flags */ + uint16_t idx; /* counts to 65535, then cycles */ + uint16_t ring[]; /* size N, reported in QNUM value */ +} __attribute__((packed)); + +struct vring_used { + uint16_t flags; /* vring_used flags */ + uint16_t idx; /* counts to 65535, then cycles */ + struct virtio_used ring[]; /* size N */ +} __attribute__((packed)); + +/* struct used to maintain virtqueue info from userspace VBS */ +struct virtio_vq_info { + /* virtqueue info from VBS-U */ + uint16_t qsize; /* size of this queue (a power of 2) */ + uint32_t pfn; /* PFN of virt queue (not shifted!) */ + uint16_t msix_idx; /* MSI-X index/VIRTIO_MSI_NO_VECTOR */ + uint64_t msix_addr; /* MSI-X address specified by index */ + uint32_t msix_data; /* MSI-X data specified by index */ + + /* members created in kernel space VBS */ + int (*vq_notify)(int); /* vq-wide notification */ + struct virtio_dev_info *dev; /* backpointer to virtio_dev_info */ + uint16_t num; /* we're the num'th virtqueue */ + uint16_t flags; /* virtqueue flags */ + uint16_t last_avail; /* a recent value of vq_avail->va_idx */ + uint16_t save_used; /* saved vq_used->vu_idx */ + + volatile struct virtio_desc *desc; /* descriptor array */ + volatile struct vring_avail *avail; /* the "avail" ring */ + volatile struct vring_used *used; /* the "used" ring */ +}; + /* struct used to maintain virtio device info from userspace VBS */ struct virtio_dev_info { /* dev info from VBS */ char name[VBS_NAME_LEN]; /* VBS device name */ struct ctx _ctx; /* device context */ + int nvq; /* number of virtqueues */ uint32_t negotiated_features; /* features after guest loads driver */ uint64_t io_range_start; /* IO range start of VBS device */ uint64_t io_range_len; /* IO range len of VBS device */ enum IORangeType io_range_type; /* IO range type, PIO or MMIO */ + + /* members created in kernel space VBS */ + void (*dev_notify)(void *, struct virtio_vq_info *); + /* device-wide notification */ + struct virtio_vq_info *vqs; /* virtqueue(s) */ + int curq; /* current virtqueue index */ }; /* VBS Runtime Control APIs */ +long virtio_dev_init(struct virtio_dev_info *dev, struct virtio_vq_info *vqs, + int nvq); long virtio_dev_ioctl(struct virtio_dev_info *dev, unsigned int ioctl, void __user *argp); +long virtio_vqs_ioctl(struct virtio_dev_info *dev, unsigned int ioctl, + void __user *argp); #endif diff --git a/include/linux/vbs/vbs_common_if.h b/include/linux/vbs/vbs_common_if.h index 78b36a6c58c8..91eb4e01d95c 100644 --- a/include/linux/vbs/vbs_common_if.h +++ b/include/linux/vbs/vbs_common_if.h @@ -59,11 +59,28 @@ #ifndef _VBS_COMMON_IF_H_ #define _VBS_COMMON_IF_H_ +#define VBS_MAX_VQ_CNT 10 #define VBS_NAME_LEN 32 +#define VIRTIO_MSI_NO_VECTOR 0xFFFF + +struct vbs_vq_info { + uint16_t qsize; /* size of this virtqueue (a power of 2) */ + uint32_t pfn; /* PFN of virtqueue (not shifted!) */ + uint16_t msix_idx; /* MSI-X index, or VIRTIO_MSI_NO_VECTOR */ + uint64_t msix_addr; /* MSI-X address specified by index */ + uint32_t msix_data; /* MSI-X data specified by index */ +}; + +struct vbs_vqs_info { + uint32_t nvq; /* number of virtqueues */ + struct vbs_vq_info vqs[VBS_MAX_VQ_CNT]; + /* array of struct vbs_vq_info */ +}; struct vbs_dev_info { char name[VBS_NAME_LEN];/* VBS name */ int vmid; /* id of VM this device belongs to */ + int nvq; /* number of virtqueues */ uint32_t negotiated_features; /* features after VIRTIO_CONFIG_S_DRIVER_OK */ uint64_t pio_range_start; @@ -74,5 +91,6 @@ struct vbs_dev_info { #define VBS_IOCTL 0xAF #define VBS_SET_DEV _IOW(VBS_IOCTL, 0x00, struct vbs_dev_info) +#define VBS_SET_VQ _IOW(VBS_IOCTL, 0x01, struct vbs_vqs_info) #endif diff --git a/include/linux/vbs/vq.h b/include/linux/vbs/vq.h new file mode 100644 index 000000000000..55ff810fa094 --- /dev/null +++ b/include/linux/vbs/vq.h @@ -0,0 +1,99 @@ +/* + * ACRN Project + * Virtio Backend Service (VBS) for ACRN hypervisor + * + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright (c) 2017 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * 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. + * + * Contact Information: Hao Li + * + * BSD LICENSE + * + * Copyright (c) 2017 Intel Corporation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 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 + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Chris Torek + * Hao Li + * Define virtqueue data structures and APIs for VBS framework. + * - VBS-K is a kernel-level virtio framework that can be used for + * virtio backend driver development for ACRN hypervisor. + * - VBS-K should be working with VBS-U (Virtio Backend Service in + * User) together, in order to connect with virtio frontend driver. + */ + +#ifndef _VQ_H_ +#define _VQ_H_ + +#include +#include + +/* virtqueue alignment */ +#define VRING_ALIGN 4096 +#define roundup2(x, y) (((x)+((y)-1))&(~((y)-1))) + +/* PFN register shift amount */ +#define VRING_PAGE_BITS 12 + +/* virtqueue flags */ +#define VQ_ALLOC 0x01 +#define VQ_BROKED 0x02 + +/* get virtqueue size according to virtio specification */ +static inline size_t virtio_vq_ring_size(unsigned int qsz) +{ + size_t size; + + /* constant 3 below = va_flags, va_idx, va_used_event */ + size = sizeof(struct virtio_desc) * qsz + sizeof(uint16_t) * (3 + qsz); + size = roundup2(size, VRING_ALIGN); + + /* constant 3 below = vu_flags, vu_idx, vu_avail_event */ + size += sizeof(uint16_t) * 3 + sizeof(struct virtio_used) * qsz; + size = roundup2(size, VRING_ALIGN); + + return size; +} + +/* virtqueue initialization APIs */ +void virtio_vq_init(struct virtio_vq_info *vq, uint32_t pfn); +void virtio_vq_reset(struct virtio_vq_info *vq); + +#endif From 343b3604c811fe4d1e441daa736bf9b43929b2dd Mon Sep 17 00:00:00 2001 From: Hao Li Date: Fri, 31 Aug 2018 10:58:56 +0800 Subject: [PATCH 0019/1103] VBS-K: virtqueue runtime API. This patch added the virtqueue runtime API to the VBS-K framework: - int virtio_vq_getchain(struct virtio_vq_info *vq, uint16_t *pidx, struct iovec *iov, int n_iov, uint16_t *flags); - void virtio_vq_retchain(struct virtio_vq_info *vq); - void virtio_vq_relchain(struct virtio_vq_info *vq, uint16_t idx, uint32_t iolen); - void virtio_vq_endchains(struct virtio_vq_info *vq, int used_all_avail); Change-Id: Ie7f81d96c895a16e210133c19aca99b185b8682d Tracked-On:218445 Signed-off-by: Hao Li Reviewed-on: Reviewed-by: Chi, Mingqiang Reviewed-by: Dong, Eddie Tested-by: Dong, Eddie --- drivers/vbs/vq.c | 270 +++++++++++++++++++++++++++++++++++++++++ include/linux/vbs/vq.h | 73 +++++++++++ 2 files changed, 343 insertions(+) diff --git a/drivers/vbs/vq.c b/drivers/vbs/vq.c index 95a6757a1c85..886f48225de9 100644 --- a/drivers/vbs/vq.c +++ b/drivers/vbs/vq.c @@ -71,6 +71,276 @@ void * paddr_guest2host(struct ctx *ctx, uintptr_t gaddr, size_t len) return map_guest_phys(ctx->vmid, gaddr, len); } +/* + * helper function for vq_getchain(): + * record the i'th "real" descriptor. + */ +static inline void _vq_record(int i, volatile struct virtio_desc *vd, + struct ctx *ctx, struct iovec *iov, + int n_iov, uint16_t *flags) +{ + if (i >= n_iov) + return; + + iov[i].iov_base = paddr_guest2host(ctx, vd->addr, vd->len); + iov[i].iov_len = vd->len; + + if (flags != NULL) + flags[i] = vd->flags; +} + +/* + * Walk descriptor table and put requests into iovec. + * + * Examine the chain of descriptors starting at the "next one" to + * make sure that they describe a sensible request. If so, return + * the number of "real" descriptors that would be needed/used in + * acting on this request. This may be smaller than the number of + * available descriptors, e.g., if there are two available but + * they are two separate requests, this just returns 1. Or, it + * may be larger: if there are indirect descriptors involved, + * there may only be one descriptor available but it may be an + * indirect pointing to eight more. We return 8 in this case, + * i.e., we do not count the indirect descriptors, only the "real" + * ones. + * + * Basically, this vets the vd_flags and vd_next field of each + * descriptor and tells you how many are involved. Since some may + * be indirect, this also needs the vmctx (in the pci_vdev + * at vc->vc_pi) so that it can find indirect descriptors. + * + * As we process each descriptor, we copy and adjust it (guest to + * host address wise, also using the vmtctx) into the given iov[] + * array (of the given size). If the array overflows, we stop + * placing values into the array but keep processing descriptors, + * up to VQ_MAX_DESCRIPTORS, before giving up and returning -1. + * So you, the caller, must not assume that iov[] is as big as the + * return value (you can process the same thing twice to allocate + * a larger iov array if needed, or supply a zero length to find + * out how much space is needed). + * + * If you want to verify the WRITE flag on each descriptor, pass a + * non-NULL "flags" pointer to an array of "uint16_t" of the same size + * as n_iov and we'll copy each vd_flags field after unwinding any + * indirects. + * + * If some descriptor(s) are invalid, this prints a diagnostic message + * and returns -1. If no descriptors are ready now it simply returns 0. + * + * You are assumed to have done a vq_ring_ready() if needed (note + * that vq_has_descs() does one). + */ +int virtio_vq_getchain(struct virtio_vq_info *vq, uint16_t *pidx, + struct iovec *iov, int n_iov, uint16_t *flags) +{ + int i; + unsigned int ndesc, n_indir; + unsigned int idx, next; + struct ctx *ctx; + struct virtio_dev_info *dev; + const char *name; + + volatile struct virtio_desc *vdir, *vindir, *vp; + + dev = vq->dev; + name = dev->name; + + /* + * Note: it's the responsibility of the guest not to + * update vq->vq_avail->va_idx until all of the descriptors + * the guest has written are valid (including all their + * vd_next fields and vd_flags). + * + * Compute (last_avail - va_idx) in integers mod 2**16. This is + * the number of descriptors the device has made available + * since the last time we updated vq->vq_last_avail. + * + * We just need to do the subtraction as an unsigned int, + * then trim off excess bits. + */ + idx = vq->last_avail; + ndesc = (uint16_t)((unsigned int)vq->avail->idx - idx); + + if (ndesc == 0) + return 0; + + if (ndesc > vq->qsize) { + /* XXX need better way to diagnose issues */ + pr_err("%s: ndesc (%u) out of range, driver confused?\r\n", + name, (unsigned int)ndesc); + return -1; + } + + /* + * Now count/parse "involved" descriptors starting from + * the head of the chain. + * + * To prevent loops, we could be more complicated and + * check whether we're re-visiting a previously visited + * index, but we just abort if the count gets excessive. + */ + ctx = &dev->_ctx; + *pidx = next = vq->avail->ring[idx & (vq->qsize - 1)]; + vq->last_avail++; + for (i = 0; i < VQ_MAX_DESCRIPTORS; next = vdir->next) { + if (next >= vq->qsize) { + pr_err("%s: descriptor index %u out of range, " + "driver confused?\r\n", name, next); + return -1; + } + vdir = &vq->desc[next]; + if ((vdir->flags & VRING_DESC_F_INDIRECT) == 0) { + _vq_record(i, vdir, ctx, iov, n_iov, flags); + i++; + } else if ((dev->negotiated_features & + VIRTIO_RING_F_INDIRECT_DESC) == 0) { + pr_err("%s: descriptor has forbidden INDIRECT flag, " + "driver confused?\r\n", name); + return -1; + } else { + n_indir = vdir->len / 16; + if ((vdir->len & 0xf) || n_indir == 0) { + pr_err("%s: invalid indir len 0x%x, " + "driver confused?\r\n", name, + (unsigned int)vdir->len); + return -1; + } + vindir = paddr_guest2host(ctx, vdir->addr, vdir->len); + /* + * Indirects start at the 0th, then follow + * their own embedded "next"s until those run + * out. Each one's indirect flag must be off + * (we don't really have to check, could just + * ignore errors...). + */ + next = 0; + for (;;) { + vp = &vindir[next]; + if (vp->flags & VRING_DESC_F_INDIRECT) { + pr_err("%s: indirect desc has INDIR flag," + " driver confused?\r\n", name); + return -1; + } + _vq_record(i, vp, ctx, iov, n_iov, flags); + if (++i > VQ_MAX_DESCRIPTORS) + goto loopy; + if ((vp->flags & VRING_DESC_F_NEXT) == 0) + break; + next = vp->next; + if (next >= n_indir) { + pr_err("%s: invalid next %u > %u, " + "driver confused?\r\n", + name, (unsigned int)next, n_indir); + return -1; + } + } + } + if ((vdir->flags & VRING_DESC_F_NEXT) == 0) + return i; + } +loopy: + pr_err("%s: descriptor loop? count > %d - driver confused?\r\n", + name, i); + return -1; +} + +/* + * Return the currently-first request chain back to the available queue. + * + * (This chain is the one you handled when you called vq_getchain() + * and used its positive return value.) + */ +void virtio_vq_retchain(struct virtio_vq_info *vq) +{ + vq->last_avail--; +} + +/* + * Return specified request chain to the guest, setting its I/O length + * to the provided value. + * + * (This chain is the one you handled when you called vq_getchain() + * and used its positive return value.) + */ +void virtio_vq_relchain(struct virtio_vq_info *vq, uint16_t idx, + uint32_t iolen) +{ + uint16_t uidx, mask; + volatile struct vring_used *vuh; + volatile struct virtio_used *vue; + + /* + * Notes: + * - mask is N-1 where N is a power of 2 so computes x % N + * - vuh points to the "used" data shared with guest + * - vue points to the "used" ring entry we want to update + * - head is the same value we compute in vq_iovecs(). + * + * (I apologize for the two fields named vu_idx; the + * virtio spec calls the one that vue points to, "id"...) + */ + mask = vq->qsize - 1; + vuh = vq->used; + + uidx = vuh->idx; + vue = &vuh->ring[uidx++ & mask]; + vue->idx = idx; + vue->len = iolen; + vuh->idx = uidx; +} + +/* + * Driver has finished processing "available" chains and calling + * vq_relchain on each one. If driver used all the available + * chains, used_all should be set. + * + * If the "used" index moved we may need to inform the guest, i.e., + * deliver an interrupt. Even if the used index did NOT move we + * may need to deliver an interrupt, if the avail ring is empty and + * we are supposed to interrupt on empty. + * + * Note that used_all_avail is provided by the caller because it's + * a snapshot of the ring state when he decided to finish interrupt + * processing -- it's possible that descriptors became available after + * that point. (It's also typically a constant 1/True as well.) + */ +void virtio_vq_endchains(struct virtio_vq_info *vq, int used_all_avail) +{ + struct virtio_dev_info *dev; + uint16_t event_idx, new_idx, old_idx; + int intr; + + /* + * Interrupt generation: if we're using EVENT_IDX, + * interrupt if we've crossed the event threshold. + * Otherwise interrupt is generated if we added "used" entries, + * but suppressed by VRING_AVAIL_F_NO_INTERRUPT. + * + * In any case, though, if NOTIFY_ON_EMPTY is set and the + * entire avail was processed, we need to interrupt always. + */ + dev = vq->dev; + old_idx = vq->save_used; + vq->save_used = new_idx = vq->used->idx; + if (used_all_avail && + (dev->negotiated_features & VIRTIO_F_NOTIFY_ON_EMPTY)) + intr = 1; + else if (dev->negotiated_features & VIRTIO_RING_F_EVENT_IDX) { + event_idx = VQ_USED_EVENT_IDX(vq); + /* + * This calculation is per docs and the kernel + * (see src/sys/dev/virtio/virtio_ring.h). + */ + intr = (uint16_t)(new_idx - event_idx - 1) < + (uint16_t)(new_idx - old_idx); + } else { + intr = new_idx != old_idx && + !(vq->avail->flags & VRING_AVAIL_F_NO_INTERRUPT); + } + if (intr) + virtio_vq_interrupt(dev, vq); +} + /* * Initialize the currently-selected virtqueue. * The guest just gave us a page frame number, from which we can diff --git a/include/linux/vbs/vq.h b/include/linux/vbs/vq.h index 55ff810fa094..9ebde05e4663 100644 --- a/include/linux/vbs/vq.h +++ b/include/linux/vbs/vq.h @@ -64,6 +64,7 @@ #include #include +#include /* virtqueue alignment */ #define VRING_ALIGN 4096 @@ -76,6 +77,30 @@ #define VQ_ALLOC 0x01 #define VQ_BROKED 0x02 +/* + * Feature flags. + * Note: bits 0 through 23 are reserved to each device type. + */ +#define VIRTIO_F_NOTIFY_ON_EMPTY (1 << 24) +#define VIRTIO_RING_F_INDIRECT_DESC (1 << 28) +#define VIRTIO_RING_F_EVENT_IDX (1 << 29) + +#define VQ_MAX_DESCRIPTORS 512 + +/* virtio_desc flags */ +#define VRING_DESC_F_NEXT (1 << 0) +#define VRING_DESC_F_WRITE (1 << 1) +#define VRING_DESC_F_INDIRECT (1 << 2) + +/* vring_avail flags */ +#define VRING_AVAIL_F_NO_INTERRUPT 1 + +/* vring_used flags */ +#define VRING_USED_F_NO_NOTIFY 1 + +/* Functions for dealing with generalized "virtual devices" */ +#define VQ_USED_EVENT_IDX(vq) ((vq)->avail->ring[(vq)->qsize]) + /* get virtqueue size according to virtio specification */ static inline size_t virtio_vq_ring_size(unsigned int qsz) { @@ -92,8 +117,56 @@ static inline size_t virtio_vq_ring_size(unsigned int qsz) return size; } +/* Is this ring ready for I/O? */ +static inline int virtio_vq_ring_ready(struct virtio_vq_info *vq) +{ + return (vq->flags & VQ_ALLOC); +} + +/* + * Are there "available" descriptors? (This does not count + * how many, just returns True if there are some). + */ +static inline int virtio_vq_has_descs(struct virtio_vq_info *vq) +{ + return (virtio_vq_ring_ready(vq) && + vq->last_avail != vq->avail->idx); +} + +/* Deliver an interrupt to guest on the given virtual queue */ +static inline void virtio_vq_interrupt(struct virtio_dev_info *dev, + struct virtio_vq_info *vq) +{ + uint16_t msix_idx; + uint64_t msix_addr; + uint32_t msix_data; + + /* Currently we only support MSIx */ + msix_idx = vq->msix_idx; + + if (msix_idx == VIRTIO_MSI_NO_VECTOR) { + pr_err("msix idx is VIRTIO_MSI_NO_VECTOR!\n"); + return; + } + + msix_addr = vq->msix_addr; + msix_data = vq->msix_data; + + pr_debug("virtio_vq_interrupt: vmid is %d\n", dev->_ctx.vmid); + vhm_inject_msi(dev->_ctx.vmid, msix_addr, msix_data); +} + + /* virtqueue initialization APIs */ void virtio_vq_init(struct virtio_vq_info *vq, uint32_t pfn); void virtio_vq_reset(struct virtio_vq_info *vq); +/* virtqueue runtime APIs */ +int virtio_vq_getchain(struct virtio_vq_info *vq, uint16_t *pidx, + struct iovec *iov, int n_iov, uint16_t *flags); +void virtio_vq_retchain(struct virtio_vq_info *vq); +void virtio_vq_relchain(struct virtio_vq_info *vq, uint16_t idx, + uint32_t iolen); +void virtio_vq_endchains(struct virtio_vq_info *vq, int used_all_avail); + #endif From 21d47ac2b29c0949cc6bd66d83943397c97c7fcb Mon Sep 17 00:00:00 2001 From: Hao Li Date: Fri, 31 Aug 2018 10:58:56 +0800 Subject: [PATCH 0020/1103] VBS-K: added a VBS-K reference driver. This patch implemented a VBS-K reference driver: virtio RNG. This reference driver shows how to use VBS-K APIs to implement virtio backend drivers for ACRN hypervisor. Key points from the reference driver: - Each VBS-K driver exports a char device to /dev/, e.g. /dev/vbs_rng; - Each VBS-K driver should use Virtqueue APIs to interact with the virtio frontend driver in guest; - Each VBS-K driver could register itelf as VHM (Virtio and Hypervisor service Module) client, and uses VHM API to handle frontend's register access to backend; - Each VBS-K driver could maintain the connections, from VBS-U, in a list/table, so that it could serve multiple guests. Sometimes even single guest could have multiple connections from VBS-U, depending on the device type. The reference driver shows how to maintain connections in a hashtable. Change-Id: Id590930d2f64d391ceb18c6ef491ec48412a89d8 Tracked-On:218445 Signed-off-by: Hao Li Reviewed-on: Reviewed-by: Chi, Mingqiang Reviewed-by: Dong, Eddie Tested-by: Dong, Eddie --- drivers/vbs/Kconfig | 11 + drivers/vbs/Makefile | 2 + drivers/vbs/vbs_rng.c | 589 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 602 insertions(+) create mode 100644 drivers/vbs/vbs_rng.c diff --git a/drivers/vbs/Kconfig b/drivers/vbs/Kconfig index 156c3162fd63..e3bb44d6a5e9 100644 --- a/drivers/vbs/Kconfig +++ b/drivers/vbs/Kconfig @@ -18,3 +18,14 @@ config VBS_DEBUG ---help--- This is an option for use by developers; most people should say N here. This enables ACRN VBS debugging. + +config VBS_RNG + tristate "ACRN VBS reference driver: virtio RNG" + depends on VBS != n + default n + ---help--- + Say M or * here to enable a VBS-K reference driver for ACRN + hypervisor, virtio RNG driver, to work with virtio-rng + frontend driver in guest. + The reference driver shows an example on how to use VBS-K + APIs. diff --git a/drivers/vbs/Makefile b/drivers/vbs/Makefile index cbd5076e2313..85e1cc252197 100644 --- a/drivers/vbs/Makefile +++ b/drivers/vbs/Makefile @@ -2,3 +2,5 @@ ccflags-$(CONFIG_VBS_DEBUG) := -DDEBUG obj-$(CONFIG_VBS) += vbs.o obj-$(CONFIG_VBS) += vq.o + +obj-$(CONFIG_VBS_RNG) += vbs_rng.o diff --git a/drivers/vbs/vbs_rng.c b/drivers/vbs/vbs_rng.c new file mode 100644 index 000000000000..ef6f8776e71c --- /dev/null +++ b/drivers/vbs/vbs_rng.c @@ -0,0 +1,589 @@ +/* + * ACRN Project + * Virtio Backend Service (VBS) for ACRN hypervisor + * + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright (c) 2017 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * 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. + * + * Contact Information: Hao Li + * + * BSD LICENSE + * + * Copyright (c) 2017 Intel Corporation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 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 + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * + * Hao Li + * VBS-K Reference Driver: virtio-rng + * - Each VBS-K driver exports a char device to /dev/, e.g. /dev/vbs_rng; + * - Each VBS-K driver uses Virtqueue APIs to interact with the virtio + * frontend driver in guest; + * - Each VBS-K driver registers itelf as VHM (Virtio and Hypervisor + * service Module) client, which enables in-kernel handling of register + * access of virtio device; + * - Each VBS-K driver could maintain the connections, from VBS-U, in a + * list/table, so that it could serve multiple guests. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +enum { + VBS_K_RNG_VQ = 0, + VBS_K_RNG_VQ_MAX = 1, +}; + +#define VTRND_RINGSZ 64 + +/* VBS-K features if any */ +/* + *enum { + * VBS_K_RNG_FEATURES = VBS_K_FEATURES | + * (1ULL << VIRTIO_F_VERSION_1), + *}; + */ + +struct vbs_rng { + struct virtio_dev_info dev; + struct virtio_vq_info vqs[VBS_K_RNG_VQ_MAX]; + int vhm_client_id; + /* Below could be device specific members */ + struct hwrng hwrng; +}; + +/* + * Each VBS-K module might serve multiple connections from multiple + * guests/device models/VBS-Us, so better to maintain the connections + * in a list, and here we use hashtalble as an example. + */ +struct vbs_rng_client { + struct vbs_rng *rng; + int vhm_client_id; + int max_vcpu; + struct vhm_request *req_buf; +}; + +/* instances malloced/freed by hashtable routines */ +struct vbs_rng_hash_entry { + struct vbs_rng_client *info; + struct hlist_node node; +}; + +#define RNG_MAX_HASH_BITS 4 /* MAX is 2^4 */ +#define HASH_NAME vbs_rng_hash + +DECLARE_HASHTABLE(HASH_NAME, RNG_MAX_HASH_BITS); +static int vbs_rng_hash_initialized = 0; +static int vbs_rng_connection_cnt = 0; + +/* function declarations */ +static int handle_kick(int client_id, int req_cnt); +static void vbs_rng_reset(struct vbs_rng *rng); +static void vbs_rng_disable_vq(struct vbs_rng *rng, + struct virtio_vq_info *vq); +static int vbs_rng_enable_vq(struct vbs_rng *rng, + struct virtio_vq_info *vq); +static void vbs_rng_stop_vq(struct vbs_rng *rng, + struct virtio_vq_info *vq); +static void vbs_rng_stop(struct vbs_rng *rng); +static void vbs_rng_flush_vq(struct vbs_rng *rng, int index); +static void vbs_rng_flush(struct vbs_rng *rng); + +/* hash table related functions */ +static void vbs_rng_hash_init(void) +{ + if (vbs_rng_hash_initialized) + return; + + hash_init(HASH_NAME); + vbs_rng_hash_initialized = 1; +} + +static int vbs_rng_hash_add(struct vbs_rng_client *client) +{ + struct vbs_rng_hash_entry *entry; + + if (!vbs_rng_hash_initialized) { + pr_err("RNG hash table not initialized!\n"); + return -1; + } + + entry = kmalloc(sizeof(*entry), GFP_KERNEL); + if (!entry) { + pr_err("Failed to alloc memory for rng hash entry!\n"); + return -1; + } + + entry->info = client; + + hash_add(HASH_NAME, &entry->node, entry->info->vhm_client_id); + return 0; +} + +static struct vbs_rng_client *vbs_rng_hash_find(int client_id) +{ + struct vbs_rng_hash_entry *entry; + int bkt; + + if (!vbs_rng_hash_initialized) { + pr_err("RNG hash table not initialized!\n"); + return NULL; + } + + hash_for_each(HASH_NAME, bkt, entry, node) + if (entry->info->vhm_client_id == client_id) + return entry->info; + + pr_err("Not found item matching client_id!\n"); + return NULL; +} + +static int vbs_rng_hash_del(int client_id) +{ + struct vbs_rng_hash_entry *entry; + int bkt; + + if (!vbs_rng_hash_initialized) { + pr_err("RNG hash table not initialized!\n"); + return -1; + } + + hash_for_each(HASH_NAME, bkt, entry, node) + if (entry->info->vhm_client_id == client_id) { + hash_del(&entry->node); + kfree(entry); + return 0; + } + + pr_err("%s failed, not found matching client_id!\n", + __func__); + return -1; +} + +static int vbs_rng_hash_del_all(void) +{ + struct vbs_rng_hash_entry *entry; + int bkt; + + if (!vbs_rng_hash_initialized) { + pr_err("RNG hash table not initialized!\n"); + return -1; + } + + hash_for_each(HASH_NAME, bkt, entry, node) + if (1) { + hash_del(&entry->node); + kfree(entry); + } + + return 0; +} + +static int register_vhm_client(struct virtio_dev_info *dev) +{ + unsigned int vmid; + struct vm_info info; + struct vbs_rng_client *client; + int ret; + + client = kcalloc(1, sizeof(*client), GFP_KERNEL); + if (!client) { + pr_err("failed to malloc vbs_rng_client!\n"); + return -EINVAL; + } + + client->rng = container_of(dev, struct vbs_rng, dev); + vmid = dev->_ctx.vmid; + pr_debug("vmid is %d\n", vmid); + + client->vhm_client_id = acrn_ioreq_create_client(vmid, handle_kick, + "vbs_rng kick init\n"); + if (client->vhm_client_id < 0) { + pr_err("failed to create client of acrn ioreq!\n"); + goto err; + } + + ret = acrn_ioreq_add_iorange(client->vhm_client_id, + dev->io_range_type ? REQ_MMIO : REQ_PORTIO, + dev->io_range_start, + dev->io_range_start + dev->io_range_len); + if (ret < 0) { + pr_err("failed to add iorange to acrn ioreq!\n"); + goto err; + } + + /* feed up max_cpu and req_buf */ + ret = vhm_get_vm_info(vmid, &info); + if (ret < 0) { + pr_err("failed in vhm_get_vm_info!\n"); + goto err; + } + client->max_vcpu = info.max_vcpu; + + client->req_buf = acrn_ioreq_get_reqbuf(client->vhm_client_id); + if (client->req_buf == NULL) { + pr_err("failed in acrn_ioreq_get_reqbuf!\n"); + goto err; + } + + /* just attach once as vhm will kick kthread */ + acrn_ioreq_attach_client(client->vhm_client_id, 0); + + client->rng->vhm_client_id = client->vhm_client_id; + vbs_rng_hash_add(client); + + return 0; +err: + acrn_ioreq_destroy_client(client->vhm_client_id); + kfree(client); + + return -EINVAL; +} + +static void handle_vq_kick(struct vbs_rng *rng, int vq_idx) +{ + struct iovec iov; + struct vbs_rng *sc; + struct virtio_vq_info *vq; + int len; + uint16_t idx; + + pr_debug("%s: vq_idx %d\n", __func__, vq_idx); + + sc = rng; + + if (!sc) { + pr_err("rng is NULL! Cannot proceed!\n"); + return; + } + + vq = &(sc->vqs[vq_idx]); + + pr_debug("before vq_has_desc!\n"); + + while (virtio_vq_has_descs(vq)) { + virtio_vq_getchain(vq, &idx, &iov, 1, NULL); + + /* device specific operations, for example: */ + /* len = read(sc->vrsc_fd, iov.iov_base, iov.iov_len); */ + pr_debug("iov base %p len %lx\n", iov.iov_base, iov.iov_len); + + /* let's generate some cool data... :-) */ + len = iov.iov_len; + + pr_debug("vtrnd: vtrnd_notify(): %d\r\n", len); + + /* + * Release this chain and handle more + */ + virtio_vq_relchain(vq, idx, len); + } + virtio_vq_endchains(vq, 1); /* Generate interrupt if appropriate. */ +} + +static int handle_kick(int client_id, int req_cnt) +{ + int val = -1; + struct vhm_request *req; + struct vbs_rng_client *client; + int i; + + if (unlikely(req_cnt <= 0)) + return -EINVAL; + + pr_debug("%s!\n", __func__); + + client = vbs_rng_hash_find(client_id); + if (!client) { + pr_err("Ooops! client %d not found!\n", client_id); + return -EINVAL; + } + + for (i = 0; i < client->max_vcpu; i++) { + req = &client->req_buf[i]; + if (req->valid && req->processed == REQ_STATE_PROCESSING && + req->client == client->vhm_client_id) { + if (req->reqs.pio_request.direction == REQUEST_READ) + /* currently we handle kick only, + * so read will return 0 + */ + req->reqs.pio_request.value = 0; + else + val = req->reqs.pio_request.value; + pr_debug("%s: ioreq type %d, direction %d, " + "addr 0x%lx, size 0x%lx, value 0x%x\n", + __func__, + req->type, + req->reqs.pio_request.direction, + req->reqs.pio_request.address, + req->reqs.pio_request.size, + req->reqs.pio_request.value); + req->processed = REQ_STATE_SUCCESS; + acrn_ioreq_complete_request(client->vhm_client_id, + 1 << i); + } + } + + if (val >= 0) + handle_vq_kick(client->rng, val); + return 0; +} + +static int vbs_rng_open(struct inode *inode, struct file *f) +{ + struct vbs_rng *rng; + struct virtio_dev_info *dev; + struct virtio_vq_info *vqs; + int i; + + pr_debug("%s!\n", __func__); + + rng = kmalloc(sizeof(*rng), GFP_KERNEL); + if (!rng) { + pr_err("Failed to allocate memory for vbs_rng!\n"); + return -ENOMEM; + } + + dev = &rng->dev; + vqs = (struct virtio_vq_info *)&rng->vqs; + + for (i = 0; i < VBS_K_RNG_VQ_MAX; i++) { + vqs[i].dev = dev; + /* + * Currently relies on VHM to kick us, + * thus vq_notify not used + */ + vqs[i].vq_notify = NULL; + } + + /* link dev and vqs */ + dev->vqs = vqs; + + virtio_dev_init(dev, vqs, VBS_K_RNG_VQ_MAX); + + f->private_data = rng; + vbs_rng_hash_init(); + + return 0; +} + +static int vbs_rng_release(struct inode *inode, struct file *f) +{ + struct vbs_rng *rng = f->private_data; + struct vbs_rng_client *client; + int i; + + pr_debug("%s!\n", __func__); + + client = vbs_rng_hash_find(rng->vhm_client_id); + if (!client) + pr_err("%s: UNLIKELY not found client!\n", + __func__); + + vbs_rng_stop(rng); + vbs_rng_flush(rng); + for (i = 0; i < VBS_K_RNG_VQ_MAX; i++) + virtio_vq_reset(&(rng->vqs[i])); + + /* device specific release */ + vbs_rng_reset(rng); + + pr_debug("vbs_rng_connection cnt is %d\n", vbs_rng_connection_cnt); + + if (client && vbs_rng_connection_cnt--) + vbs_rng_hash_del(client->vhm_client_id); + if (!vbs_rng_connection_cnt) { + pr_debug("vbs_rng remove all hash entries\n"); + vbs_rng_hash_del_all(); + } + + kfree(client); + kfree(rng); + + pr_debug("%s done\n", __func__); + return 0; +} + +static struct hwrng get_hwrng(struct vbs_rng *rng) +{ + return rng->hwrng; +} + +/* Set feature bits in kernel side device */ +static int vbs_rng_set_features(struct vbs_rng *rng, u64 features) +{ + return 0; +} + +static long vbs_rng_ioctl(struct file *f, unsigned int ioctl, + unsigned long arg) +{ + struct vbs_rng *rng = f->private_data; + void __user *argp = (void __user *)arg; + /*u64 __user *featurep = argp;*/ + /*u64 features;*/ + int r; + + switch (ioctl) { +/* + * case VHOST_GET_FEATURES: + * features = VHOST_NET_FEATURES; + * if (copy_to_user(featurep, &features, sizeof features)) + * return -EFAULT; + * return 0; + * case VHOST_SET_FEATURES: + * if (copy_from_user(&features, featurep, sizeof features)) + * return -EFAULT; + * if (features & ~VHOST_NET_FEATURES) + * return -EOPNOTSUPP; + * return vhost_net_set_features(n, features); + */ + case VBS_SET_VQ: + /* we handle this here because we want to register VHM client + * after handling VBS_K_SET_VQ request + */ + pr_debug("VBS_K_SET_VQ ioctl:\n"); + r = virtio_vqs_ioctl(&rng->dev, ioctl, argp); + if (r == -ENOIOCTLCMD) { + pr_err("VBS_K_SET_VQ: virtio_vqs_ioctl failed!\n"); + return -EFAULT; + } + /* Register VHM client */ + if (register_vhm_client(&rng->dev) < 0) { + pr_err("failed to register VHM client!\n"); + return -EFAULT; + } + vbs_rng_connection_cnt++; + return r; + default: + /*mutex_lock(&n->dev.mutex);*/ + pr_debug("VBS_K generic ioctls!\n"); + r = virtio_dev_ioctl(&rng->dev, ioctl, argp); + if (r == -ENOIOCTLCMD) + r = virtio_vqs_ioctl(&rng->dev, ioctl, argp); + else + vbs_rng_flush(rng); + /*mutex_unlock(&n->dev.mutex);*/ + return r; + } +} + +/* device specific function to cleanup itself */ +static void vbs_rng_reset(struct vbs_rng *rng) +{ +} + +/* device specific function */ +static void vbs_rng_disable_vq(struct vbs_rng *rng, + struct virtio_vq_info *vq) +{ +} + +/* device specific function */ +static int vbs_rng_enable_vq(struct vbs_rng *rng, + struct virtio_vq_info *vq) +{ + return 0; +} + +/* device specific function */ +static void vbs_rng_stop_vq(struct vbs_rng *rng, + struct virtio_vq_info *vq) +{ +} + +/* device specific function */ +static void vbs_rng_stop(struct vbs_rng *rng) +{ +} + +/* device specific function */ +static void vbs_rng_flush_vq(struct vbs_rng *rng, int index) +{ +} + +/* device specific function */ +static void vbs_rng_flush(struct vbs_rng *rng) +{ +} + +static const struct file_operations vbs_rng_fops = { + .owner = THIS_MODULE, + .release = vbs_rng_release, + .unlocked_ioctl = vbs_rng_ioctl, + .open = vbs_rng_open, + .llseek = noop_llseek, +}; + +static struct miscdevice vbs_rng_misc = { + .minor = MISC_DYNAMIC_MINOR, + .name = "vbs_rng", + .fops = &vbs_rng_fops, +}; + +static int vbs_rng_init(void) +{ + return misc_register(&vbs_rng_misc); +} +module_init(vbs_rng_init); + +static void vbs_rng_exit(void) +{ + misc_deregister(&vbs_rng_misc); +} +module_exit(vbs_rng_exit); + +MODULE_VERSION("0.1"); +MODULE_AUTHOR("Intel Corporation"); +MODULE_LICENSE("GPL and additional rights"); +MODULE_DESCRIPTION("Virtio Backend Service reference driver on ACRN hypervisor"); From 5c1cda54d26c34dca521424b4b007f51507bac70 Mon Sep 17 00:00:00 2001 From: Jason Chen CJ Date: Fri, 31 Aug 2018 10:58:57 +0800 Subject: [PATCH 0021/1103] hypercall: refine hypercall interfaces - HC_VM_SET_MEMMAP: remove MAP_UPDATE, refine API structure - HC_NOTIFY_REQUEST_FINISH: use vcpu_id instead of vcpu_mask - HC_VM_GPA2HPA: refine API structure - HC_SET_IOREQ_BUFFER: refine API structure - IC_XXXX_IRQLINE: refine API structure Change-Id: Ie9f6af005160a807335b2f266d7139abc06d8db9 Tracked-On: 218445 Signed-off-by: Jason Chen CJ Signed-off-by: Edwin Zhai Reviewed-on: Reviewed-by: Chi, Mingqiang Reviewed-by: Dong, Eddie Tested-by: Dong, Eddie --- drivers/char/vhm/vhm_dev.c | 5 +- drivers/vbs/vbs_rng.c | 3 +- drivers/vhm/vhm_hypercall.c | 4 +- drivers/vhm/vhm_ioreq.c | 26 ++--- drivers/vhm/vhm_mm.c | 29 ++--- include/linux/vhm/acrn_common.h | 181 ++++++++++------------------- include/linux/vhm/acrn_hv_defs.h | 63 +++++++--- include/linux/vhm/acrn_vhm_ioreq.h | 6 +- include/linux/vhm/acrn_vhm_mm.h | 21 +--- include/linux/vhm/vhm_hypercall.h | 3 +- include/linux/vhm/vhm_ioctl_defs.h | 5 + 11 files changed, 152 insertions(+), 194 deletions(-) diff --git a/drivers/char/vhm/vhm_dev.c b/drivers/char/vhm/vhm_dev.c index ebada9a11552..e36a5e225e12 100644 --- a/drivers/char/vhm/vhm_dev.c +++ b/drivers/char/vhm/vhm_dev.c @@ -299,14 +299,13 @@ static long vhm_dev_ioctl(struct file *filep, } case IC_NOTIFY_REQUEST_FINISH: { - struct acrn_ioreq_notify notify; + struct ioreq_notify notify; if (copy_from_user(¬ify, (void *)ioctl_param, sizeof(notify))) return -EFAULT; - ret = acrn_ioreq_complete_request(notify.client_id, - notify.vcpu_mask); + ret = acrn_ioreq_complete_request(notify.client_id, notify.vcpu); if (ret < 0) return -EFAULT; break; diff --git a/drivers/vbs/vbs_rng.c b/drivers/vbs/vbs_rng.c index ef6f8776e71c..f2234e73034d 100644 --- a/drivers/vbs/vbs_rng.c +++ b/drivers/vbs/vbs_rng.c @@ -369,8 +369,7 @@ static int handle_kick(int client_id, int req_cnt) req->reqs.pio_request.size, req->reqs.pio_request.value); req->processed = REQ_STATE_SUCCESS; - acrn_ioreq_complete_request(client->vhm_client_id, - 1 << i); + acrn_ioreq_complete_request(client->vhm_client_id, i); } } diff --git a/drivers/vhm/vhm_hypercall.c b/drivers/vhm/vhm_hypercall.c index 11ca6b86baed..94a95933d51e 100644 --- a/drivers/vhm/vhm_hypercall.c +++ b/drivers/vhm/vhm_hypercall.c @@ -87,9 +87,9 @@ inline long hcall_set_ioreq_buffer(unsigned long vmid, unsigned long buffer) return acrn_hypercall2(HC_SET_IOREQ_BUFFER, vmid, buffer); } -inline long hcall_notify_req_finish(unsigned long vmid, unsigned long vcpu_mask) +inline long hcall_notify_req_finish(unsigned long vmid, unsigned long vcpu) { - return acrn_hypercall2(HC_NOTIFY_REQUEST_FINISH, vmid, vcpu_mask); + return acrn_hypercall2(HC_NOTIFY_REQUEST_FINISH, vmid, vcpu); } inline long hcall_assert_irqline(unsigned long vmid, unsigned long irq) diff --git a/drivers/vhm/vhm_ioreq.c b/drivers/vhm/vhm_ioreq.c index 6054e3d00eb2..08826c575780 100644 --- a/drivers/vhm/vhm_ioreq.c +++ b/drivers/vhm/vhm_ioreq.c @@ -67,7 +67,7 @@ struct ioreq_range { struct list_head list; - enum request_type type; + uint32_t type; long start; long end; }; @@ -130,7 +130,7 @@ static DECLARE_BITMAP(client_bitmap, MAX_CLIENT); static void acrn_ioreq_notify_client(struct ioreq_client *client); -static inline bool is_range_type(enum request_type type) +static inline bool is_range_type(uint32_t type) { return (type == REQ_MMIO || type == REQ_PORTIO || type == REQ_WP); } @@ -335,7 +335,7 @@ static void __attribute__((unused)) dump_iorange(struct ioreq_client *client) * NOTE: here just add iorange entry directly, no check for the overlap.. * please client take care of it */ -int acrn_ioreq_add_iorange(int client_id, enum request_type type, +int acrn_ioreq_add_iorange(int client_id, uint32_t type, long start, long end) { struct ioreq_client *client; @@ -375,7 +375,7 @@ int acrn_ioreq_add_iorange(int client_id, enum request_type type, return 0; } -int acrn_ioreq_del_iorange(int client_id, enum request_type type, +int acrn_ioreq_del_iorange(int client_id, uint32_t type, long start, long end) { struct ioreq_client *client; @@ -617,13 +617,6 @@ static bool req_in_range(struct ioreq_range *range, struct vhm_request *req) ret = true; break; } - case REQ_MSR: /*TODO: add bitmap for MSR range */ - case REQ_CPUID: - case REQ_EXIT: - { - ret = true; - break; - } default: ret = false; @@ -707,7 +700,7 @@ static int handle_cf8cfc(struct vhm_vm *vm, struct vhm_request *req, int vcpu) if (req_handled) { req->processed = REQ_STATE_SUCCESS; - if (hcall_notify_req_finish(vm->vmid, 1 << vcpu) < 0) { + if (hcall_notify_req_finish(vm->vmid, vcpu) < 0) { pr_err("vhm-ioreq: failed to " "notify request finished !\n"); return -EFAULT; @@ -815,7 +808,7 @@ int acrn_ioreq_distribute_request(struct vhm_vm *vm) return 0; } -int acrn_ioreq_complete_request(int client_id, uint64_t vcpu_mask) +int acrn_ioreq_complete_request(int client_id, uint64_t vcpu) { struct ioreq_client *client; int ret; @@ -830,9 +823,8 @@ int acrn_ioreq_complete_request(int client_id, uint64_t vcpu_mask) return -EINVAL; } - atomic_sub(bitmap_weight((unsigned long *)&vcpu_mask, - VHM_REQUEST_MAX), &client->req); - ret = hcall_notify_req_finish(client->vmid, vcpu_mask); + atomic_dec(&client->req); + ret = hcall_notify_req_finish(client->vmid, vcpu); if (ret < 0) { pr_err("vhm-ioreq: failed to notify request finished !\n"); return -EFAULT; @@ -887,7 +879,7 @@ int acrn_ioreq_init(struct vhm_vm *vm, unsigned long vma) vm->req_buf = page_address(page); vm->pg = page; - set_buffer.req_buf = (long) page_to_phys(page); + set_buffer.req_buf = page_to_phys(page); ret = hcall_set_ioreq_buffer(vm->vmid, virt_to_phys(&set_buffer)); if (ret < 0) { diff --git a/drivers/vhm/vhm_mm.c b/drivers/vhm/vhm_mm.c index 61ebb8c508d2..b475aa91a348 100644 --- a/drivers/vhm/vhm_mm.c +++ b/drivers/vhm/vhm_mm.c @@ -152,13 +152,14 @@ int alloc_guest_memseg(struct vhm_vm *vm, struct vm_memseg *memseg) } static int _mem_set_memmap(unsigned long vmid, unsigned long guest_gpa, - unsigned long host_gpa, unsigned long len, int prot, int type) + unsigned long host_gpa, unsigned long len, + unsigned int prot, unsigned int type) { struct vm_set_memmap set_memmap; set_memmap.type = type; - set_memmap.foreign_gpa = guest_gpa; - set_memmap.hvm_gpa = host_gpa; + set_memmap.remote_gpa = guest_gpa; + set_memmap.vm0_gpa = host_gpa; set_memmap.length = len; set_memmap.prot = prot; @@ -177,24 +178,24 @@ static int _mem_set_memmap(unsigned long vmid, unsigned long guest_gpa, } int set_mmio_map(unsigned long vmid, unsigned long guest_gpa, - unsigned long host_gpa, unsigned long len, int prot) + unsigned long host_gpa, unsigned long len, unsigned int prot) { return _mem_set_memmap(vmid, guest_gpa, host_gpa, len, prot, MAP_MMIO); } int unset_mmio_map(unsigned long vmid, unsigned long guest_gpa, - unsigned long host_gpa, unsigned long len, int prot) + unsigned long host_gpa, unsigned long len, unsigned int prot) { return _mem_set_memmap(vmid, guest_gpa, host_gpa, len, prot, MAP_UNMAP); } -int update_mem_map(unsigned long vmid, unsigned long guest_gpa, - unsigned long host_gpa, unsigned long len, int prot) +int update_mmio_map(unsigned long vmid, unsigned long guest_gpa, + unsigned long host_gpa, unsigned long len, unsigned int prot) { return _mem_set_memmap(vmid, guest_gpa, host_gpa, len, - prot, MAP_UPDATE); + prot, MAP_MMIO); } int map_guest_memseg(struct vhm_vm *vm, struct vm_memmap *memmap) @@ -217,18 +218,18 @@ int map_guest_memseg(struct vhm_vm *vm, struct vm_memmap *memmap) } seg->prot = memmap->mem.prot; set_memmap.type = MAP_MEM; - set_memmap.foreign_gpa = seg->gpa; - set_memmap.hvm_gpa = seg->base; + set_memmap.remote_gpa = seg->gpa; + set_memmap.vm0_gpa = seg->base; set_memmap.length = seg->len; set_memmap.prot = seg->prot; - set_memmap.prot |= MMU_MEM_ATTR_WB_CACHE; + set_memmap.prot |= MEM_ATTR_WB_CACHE; } else { set_memmap.type = MAP_MMIO; - set_memmap.foreign_gpa = memmap->mmio.gpa; - set_memmap.hvm_gpa = memmap->mmio.hpa; + set_memmap.remote_gpa = memmap->mmio.gpa; + set_memmap.vm0_gpa = memmap->mmio.hpa; set_memmap.length = memmap->mmio.len; set_memmap.prot = memmap->mmio.prot; - set_memmap.prot |= MMU_MEM_ATTR_UNCACHED; + set_memmap.prot |= MEM_ATTR_UNCACHED; } /* hypercall to notify hv the guest EPT setting*/ diff --git a/include/linux/vhm/acrn_common.h b/include/linux/vhm/acrn_common.h index 0e9293d08e01..aa61fbed2596 100644 --- a/include/linux/vhm/acrn_common.h +++ b/include/linux/vhm/acrn_common.h @@ -1,5 +1,5 @@ /* - * virtio and hyperviosr service module (VHM): commom.h + * common definition * * This file is provided under a dual BSD/GPLv2 license. When using or * redistributing this file, you may do so under either license. @@ -53,125 +53,89 @@ #define ACRN_COMMON_H /* - * Commmon structures for ACRN/VHM/DM + * Common structures for ACRN/VHM/DM */ -/* ISA type - * inject interrut to both PIC and IOAPIC - */ -enum interrupt_type { - ACRN_INTR_TYPE_ISA, - ACRN_INTR_TYPE_IOAPIC, -} __attribute__((aligned(4))); - /* * IO request */ #define VHM_REQUEST_MAX 16 -enum request_state { - REQ_STATE_SUCCESS = 1, - REQ_STATE_PENDING = 0, - REQ_STATE_PROCESSING = 2, - REQ_STATE_FAILED = -1, -} __attribute__((aligned(4))); - -enum request_type { - REQ_MSR, - REQ_CPUID, - REQ_PORTIO, - REQ_MMIO, - REQ_PCICFG, - REQ_WP, - REQ_EXIT, - REQ_MAX, -} __attribute__((aligned(4))); - -enum request_direction { - REQUEST_READ, - REQUEST_WRITE, - DIRECTION_MAX, -} __attribute__((aligned(4))); +#define REQ_STATE_PENDING 0 +#define REQ_STATE_SUCCESS 1 +#define REQ_STATE_PROCESSING 2 +#define REQ_STATE_FAILED -1 -/* - * IRQ type for ptdev - */ -enum irq_type { - IRQ_INTX, - IRQ_MSI, - IRQ_MSIX, -} __attribute__((aligned(4))); - -struct msr_request { - enum request_direction direction; - long index; - long value; -} __attribute__((aligned(8))); +#define REQ_PORTIO 0 +#define REQ_MMIO 1 +#define REQ_PCICFG 2 +#define REQ_WP 3 +#define REQUEST_READ 0 +#define REQUEST_WRITE 1 struct mmio_request { - enum request_direction direction; - long address; - long size; - long value; + uint32_t direction; + uint32_t reserved; + int64_t address; + int64_t size; + int64_t value; } __attribute__((aligned(8))); -struct io_request { - enum request_direction direction; - long address; - long size; - int value; +struct pio_request { + uint32_t direction; + uint32_t reserved; + int64_t address; + int64_t size; + int32_t value; } __attribute__((aligned(8))); struct pci_request { - enum request_direction direction; - long reserve; /*io_request address*/ - long size; - int value; - int bus; - int dev; - int func; - int reg; + uint32_t direction; + uint32_t reserved[3];/* need keep same header fields with pio_request */ + int64_t size; + int32_t value; + int32_t bus; + int32_t dev; + int32_t func; + int32_t reg; } __attribute__((aligned(8))); /* vhm_request are 256Bytes aligned */ struct vhm_request { /* offset: 0bytes - 63bytes */ union { - int exitcode; - enum request_type type; - unsigned long rip; - int reserved0[16]; + uint32_t type; + int32_t reserved0[16]; }; /* offset: 64bytes-127bytes */ union { - struct msr_request msr_request; - struct io_request pio_request; + struct pio_request pio_request; struct pci_request pci_request; struct mmio_request mmio_request; - long reserved1[8]; + int64_t reserved1[8]; } reqs; /* True: valid req which need VHM to process. * ACRN write, VHM read only **/ - int valid; + int32_t valid; /* the client which is distributed to handle this request */ - int client; + int32_t client; /* 1: VHM had processed and success * 0: VHM had not yet processed * -1: VHM failed to process. Invalid request * VHM write, ACRN read only **/ - enum request_state processed; + int32_t processed; } __attribute__((aligned(256))); struct vhm_request_buffer { union { struct vhm_request req_queue[VHM_REQUEST_MAX]; - char reserved[4096]; + int8_t reserved[4096]; }; } __attribute__((aligned(4096))); @@ -182,69 +146,52 @@ struct acrn_create_vm { } __attribute__((aligned(8))); struct acrn_create_vcpu { - int vcpuid; /* IN: vcpu id */ + int vcpuid; /* IN: vcpu id */ int pcpuid; /* IN: pcpu id */ } __attribute__((aligned(8))); struct acrn_set_ioreq_buffer { - long req_buf; /* IN: gpa of per VM request_buffer*/ + uint64_t req_buf; /* IN: gpa of per VM request_buffer*/ } __attribute__((aligned(8))); -struct acrn_ioreq_notify { - int client_id; - unsigned long vcpu_mask; -} __attribute__((aligned(8))); +/* + * intr type + * IOAPIC: inject interrupt to IOAPIC + * ISA: inject interrupt to both PIC and IOAPIC + */ +#define ACRN_INTR_TYPE_ISA 0 +#define ACRN_INTR_TYPE_IOAPIC 1 /* For ISA, PIC, IOAPIC etc */ struct acrn_irqline { - enum interrupt_type intr_type; - unsigned long pic_irq; /* IN: for ISA type */ - unsigned long ioapic_irq; /* IN: for IOAPIC type, -1 don't inject */ + uint32_t intr_type; + uint32_t reserved; + uint64_t pic_irq; /* IN: for ISA type */ + uint64_t ioapic_irq; /* IN: for IOAPIC type, -1 don't inject */ } __attribute__((aligned(8))); /* For MSI type inject */ struct acrn_msi_entry { - unsigned long msi_addr; /* IN: addr[19:12] with dest vcpu id */ - unsigned long msi_data; /* IN: data[7:0] with vector */ + uint64_t msi_addr; /* IN: addr[19:12] with dest vcpu id */ + uint64_t msi_data; /* IN: data[7:0] with vector */ } __attribute__((aligned(8))); /* For NMI inject */ struct acrn_nmi_entry { - unsigned long vcpuid; /* IN: -1 means vcpu0 */ -} __attribute__((aligned(8))); - -struct acrn_ptdev_irq { - enum irq_type type; - unsigned short virt_bdf; /* IN: Device virtual BDF# */ - unsigned short phys_bdf; /* IN: Device physical BDF# */ - union { - struct { - int virt_pin; /* IN: virtual IOAPIC pin */ - int phys_pin; /* IN: physical IOAPIC pin */ - bool pic_pin; /* IN: pin from PIC? */ - } intx; - struct { - int vector_cnt; /* IN: vector count of MSI/MSIX */ - - /* IN: physcial address of MSI-X table */ - unsigned long table_paddr; - - /* IN: size of MSI-X table (round up to 4K) */ - int table_size; - } msix; - }; + int64_t vcpuid; /* IN: -1 means vcpu0 */ } __attribute__((aligned(8))); struct acrn_vm_pci_msix_remap { - unsigned short virt_bdf; /* IN: Device virtual BDF# */ - unsigned short phys_bdf; /* IN: Device physical BDF# */ - unsigned short msi_ctl; /* IN: PCI MSI/x cap control data */ - unsigned long msi_addr; /* IN/OUT: msi address to fix */ - unsigned int msi_data; /* IN/OUT: msi data to fix */ - int msix; /* IN: 0 - MSI, 1 - MSI-X */ - int msix_entry_index; /* IN: MSI-X the entry table index */ + uint16_t virt_bdf; /* IN: Device virtual BDF# */ + uint16_t phys_bdf; /* IN: Device physical BDF# */ + uint16_t msi_ctl; /* IN: PCI MSI/x cap control data */ + uint16_t reserved; + uint64_t msi_addr; /* IN/OUT: msi address to fix */ + uint32_t msi_data; /* IN/OUT: msi data to fix */ + int32_t msix; /* IN: 0 - MSI, 1 - MSI-X */ + int32_t msix_entry_index; /* IN: MSI-X the entry table index */ /* IN: Vector Control for MSI-X Entry, field defined in MSIX spec */ - unsigned int vector_ctl; + uint32_t vector_ctl; } __attribute__((aligned(8))); #endif /* ACRN_COMMON_H */ diff --git a/include/linux/vhm/acrn_hv_defs.h b/include/linux/vhm/acrn_hv_defs.h index eeac0e9b4e76..fa32243a6407 100644 --- a/include/linux/vhm/acrn_hv_defs.h +++ b/include/linux/vhm/acrn_hv_defs.h @@ -1,5 +1,5 @@ /* - * virtio and hyperviosr service module (VHM): hypercall header + * hypercall definition * * This file is provided under a dual BSD/GPLv2 license. When using or * redistributing this file, you may do so under either license. @@ -53,12 +53,12 @@ #define ACRN_HV_DEFS_H /* - * Commmon structures for ACRN/VHM/DM + * Common structures for ACRN/VHM/DM */ #include "acrn_common.h" /* - * Commmon structures for HV/VHM + * Common structures for HV/VHM */ #define _HC_ID(x, y) (((x)<<24)|(y)) @@ -101,35 +101,59 @@ #define HC_SET_PTDEV_INTR_INFO _HC_ID(HC_ID, HC_ID_PCI_BASE + 0x03) #define HC_RESET_PTDEV_INTR_INFO _HC_ID(HC_ID, HC_ID_PCI_BASE + 0x04) +/* TRACE */ +#define HC_ID_TRACE_BASE 0x600UL +#define HC_ACRN_SBUF_SETUP _HC_ID(HC_ID, HC_ID_TRACE_BASE + 0x00) + #define ACRN_DOM0_VMID (0UL) #define ACRN_INVALID_VMID (-1UL) #define ACRN_INVALID_HPA (-1UL) -enum vm_memmap_type { - MAP_MEM = 0, - MAP_MMIO, - MAP_UNMAP, - MAP_UPDATE, -}; +/* Generic memory attributes */ +#define MEM_ATTR_READ 0x00000001 +#define MEM_ATTR_WRITE 0x00000002 +#define MEM_ATTR_EXECUTE 0x00000004 +#define MEM_ATTR_USER 0x00000008 +#define MEM_ATTR_WB_CACHE 0x00000040 +#define MEM_ATTR_WT_CACHE 0x00000080 +#define MEM_ATTR_UNCACHED 0x00000100 +#define MEM_ATTR_WC 0x00000200 +#define MEM_ATTR_WP 0x00000400 + +#define MEM_ATTR_ALL 0x00000007 +#define MEM_ATTR_WRITE_PROT 0x00000005 +#define MEM_ATTR_ALL_WB 0x00000047 +#define MEM_ATTR_ALL_WC 0x00000207 struct vm_set_memmap { - enum vm_memmap_type type; +#define MAP_MEM 0 +#define MAP_MMIO 1 +#define MAP_UNMAP 2 + uint32_t type; + uint32_t reserved; + /* IN: beginning guest GPA to map */ - unsigned long foreign_gpa; + uint64_t remote_gpa; /* IN: VM0's GPA which foreign gpa will be mapped to */ - unsigned long hvm_gpa; + uint64_t vm0_gpa; /* IN: length of the range */ - unsigned long length; + uint64_t length; - /* IN: not used right now */ - int prot; + /* IN: mem attr */ + uint32_t prot; +} __attribute__((aligned(8))); + +struct sbuf_setup_param { + uint32_t pcpu_id; + uint32_t sbuf_id; + uint64_t gpa; } __attribute__((aligned(8))); struct vm_gpa2hpa { - unsigned long gpa; /* IN: gpa to translation */ - unsigned long hpa; /* OUT: -1 means invalid gpa */ + uint64_t gpa; /* IN: gpa to translation */ + uint64_t hpa; /* OUT: -1 means invalid gpa */ } __attribute__((aligned(8))); struct hc_ptdev_irq { @@ -152,4 +176,9 @@ struct hc_ptdev_irq { }; } __attribute__((aligned(8))); +struct hc_api_version { + uint32_t major_version; + uint32_t minor_version; +} __attribute__((aligned(8))); + #endif /* ACRN_HV_DEFS_H */ diff --git a/include/linux/vhm/acrn_vhm_ioreq.h b/include/linux/vhm/acrn_vhm_ioreq.h index 0daf46dcf9f7..fcec2c1e2eac 100644 --- a/include/linux/vhm/acrn_vhm_ioreq.h +++ b/include/linux/vhm/acrn_vhm_ioreq.h @@ -63,16 +63,16 @@ int acrn_ioreq_create_client(unsigned long vmid, ioreq_handler_t handler, char *name); void acrn_ioreq_destroy_client(int client_id); -int acrn_ioreq_add_iorange(int client_id, enum request_type type, +int acrn_ioreq_add_iorange(int client_id, uint32_t type, long start, long end); -int acrn_ioreq_del_iorange(int client_id, enum request_type type, +int acrn_ioreq_del_iorange(int client_id, uint32_t type, long start, long end); struct vhm_request *acrn_ioreq_get_reqbuf(int client_id); int acrn_ioreq_attach_client(int client_id, bool check_kthread_stop); int acrn_ioreq_distribute_request(struct vhm_vm *vm); -int acrn_ioreq_complete_request(int client_id, uint64_t vcpu_mask); +int acrn_ioreq_complete_request(int client_id, uint64_t vcpu); void acrn_ioreq_intercept_bdf(int client_id, int bus, int dev, int func); void acrn_ioreq_unintercept_bdf(int client_id); diff --git a/include/linux/vhm/acrn_vhm_mm.h b/include/linux/vhm/acrn_vhm_mm.h index 1af6fd3aa11b..f0401ac6a942 100644 --- a/include/linux/vhm/acrn_vhm_mm.h +++ b/include/linux/vhm/acrn_vhm_mm.h @@ -57,19 +57,6 @@ #include #include -#define MMU_MEM_ATTR_READ 0x00000001 -#define MMU_MEM_ATTR_WRITE 0x00000002 -#define MMU_MEM_ATTR_EXECUTE 0x00000004 -#define MMU_MEM_ATTR_WB_CACHE 0x00000040 -#define MMU_MEM_ATTR_WT_CACHE 0x00000080 -#define MMU_MEM_ATTR_UNCACHED 0x00000100 -#define MMU_MEM_ATTR_WC 0x00000200 - -#define MMU_MEM_ATTR_ALL 0x00000007 -#define MMU_MEM_ATTR_WP 0x00000005 -#define MMU_MEM_ATTR_ALL_WB 0x00000047 -#define MMU_MEM_ATTR_ALL_WC 0x00000207 - /* 1:1 mapping for service OS */ static inline unsigned long acrn_hpa2gpa(unsigned long hpa) { @@ -79,11 +66,11 @@ static inline unsigned long acrn_hpa2gpa(unsigned long hpa) void *map_guest_phys(unsigned long vmid, u64 uos_phys, size_t size); int unmap_guest_phys(unsigned long vmid, u64 uos_phys); int set_mmio_map(unsigned long vmid, unsigned long guest_gpa, - unsigned long host_gpa, unsigned long len, int prot); + unsigned long host_gpa, unsigned long len, unsigned int prot); int unset_mmio_map(unsigned long vmid, unsigned long guest_gpa, - unsigned long host_gpa, unsigned long len, int prot); -int update_mem_map(unsigned long vmid, unsigned long guest_gpa, - unsigned long host_gpa, unsigned long len, int prot); + unsigned long host_gpa, unsigned long len, unsigned int prot); +int update_mmio_map(unsigned long vmid, unsigned long guest_gpa, + unsigned long host_gpa, unsigned long len, unsigned int prot); int vhm_dev_mmap(struct file *file, struct vm_area_struct *vma); diff --git a/include/linux/vhm/vhm_hypercall.h b/include/linux/vhm/vhm_hypercall.h index 35bb48ae6cd3..5447e951bf4b 100644 --- a/include/linux/vhm/vhm_hypercall.h +++ b/include/linux/vhm/vhm_hypercall.h @@ -146,8 +146,7 @@ inline long hcall_set_memmap(unsigned long vmid, unsigned long memmap); inline long hcall_set_ioreq_buffer(unsigned long vmid, unsigned long buffer); -inline long hcall_notify_req_finish(unsigned long vmid, - unsigned long vcpu_mask); +inline long hcall_notify_req_finish(unsigned long vmid, unsigned long vcpu); inline long hcall_assert_irqline(unsigned long vmid, unsigned long irq); inline long hcall_deassert_irqline(unsigned long vmid, unsigned long irq); inline long hcall_pulse_irqline(unsigned long vmid, unsigned long irq); diff --git a/include/linux/vhm/vhm_ioctl_defs.h b/include/linux/vhm/vhm_ioctl_defs.h index df07e3c93467..79d91a858226 100644 --- a/include/linux/vhm/vhm_ioctl_defs.h +++ b/include/linux/vhm/vhm_ioctl_defs.h @@ -156,4 +156,9 @@ struct ic_ptdev_irq { }; }; +struct ioreq_notify { + int32_t client_id; + uint32_t vcpu; +}; + #endif /* VHM_IOCTL_DEFS_H */ From c6dc4aa94da75a03eb4395bab56fe7607ed88622 Mon Sep 17 00:00:00 2001 From: Yin Fengwei Date: Fri, 31 Aug 2018 10:58:57 +0800 Subject: [PATCH 0022/1103] vhm: refine vm related hypercall/ioctrl Change-Id: I028f59998733f3d066e2ead7768297570d97bf22 Tracked-On:218445 Signed-off-by: Yin Fengwei Reviewed-on: Reviewed-by: Dong, Eddie Tested-by: Dong, Eddie --- drivers/char/vhm/vhm_dev.c | 17 ++++------------- drivers/vhm/vhm_hypercall.c | 9 ++------- include/linux/vhm/acrn_common.h | 11 +++++++---- include/linux/vhm/acrn_hv_defs.h | 5 ++--- include/linux/vhm/vhm_hypercall.h | 2 +- include/linux/vhm/vhm_ioctl_defs.h | 5 ++--- 6 files changed, 18 insertions(+), 31 deletions(-) diff --git a/drivers/char/vhm/vhm_dev.c b/drivers/char/vhm/vhm_dev.c index e36a5e225e12..d3fd572c1642 100644 --- a/drivers/char/vhm/vhm_dev.c +++ b/drivers/char/vhm/vhm_dev.c @@ -189,12 +189,12 @@ static long vhm_dev_ioctl(struct file *filep, vm->vmid = created_vm.vmid; - pr_info("vhm: VM %ld created\n", created_vm.vmid); + pr_info("vhm: VM %d created\n", created_vm.vmid); break; } - case IC_RESUME_VM: { - ret = hcall_resume_vm(vm->vmid); + case IC_START_VM: { + ret = hcall_start_vm(vm->vmid); if (ret < 0) { pr_err("vhm: failed to start VM %ld!\n", vm->vmid); return -EFAULT; @@ -221,15 +221,6 @@ static long vhm_dev_ioctl(struct file *filep, break; } - case IC_QUERY_VMSTATE: { - ret = hcall_query_vm_state(vm->vmid); - if (ret < 0) { - pr_err("vhm: failed to query VM State%ld!\n", vm->vmid); - return -EFAULT; - } - return ret; - } - case IC_CREATE_VCPU: { struct acrn_create_vcpu cv; @@ -240,7 +231,7 @@ static long vhm_dev_ioctl(struct file *filep, ret = acrn_hypercall2(HC_CREATE_VCPU, vm->vmid, virt_to_phys(&cv)); if (ret < 0) { - pr_err("vhm: failed to create vcpu %ld!\n", cv.vcpuid); + pr_err("vhm: failed to create vcpu %d!\n", cv.vcpu_id); return -EFAULT; } diff --git a/drivers/vhm/vhm_hypercall.c b/drivers/vhm/vhm_hypercall.c index 94a95933d51e..b2738474afaf 100644 --- a/drivers/vhm/vhm_hypercall.c +++ b/drivers/vhm/vhm_hypercall.c @@ -57,9 +57,9 @@ inline long hcall_create_vm(unsigned long vminfo) return acrn_hypercall2(HC_CREATE_VM, 0, vminfo); } -inline long hcall_resume_vm(unsigned long vmid) +inline long hcall_start_vm(unsigned long vmid) { - return acrn_hypercall1(HC_RESUME_VM, vmid); + return acrn_hypercall1(HC_START_VM, vmid); } inline long hcall_pause_vm(unsigned long vmid) @@ -72,11 +72,6 @@ inline long hcall_destroy_vm(unsigned long vmid) return acrn_hypercall1(HC_DESTROY_VM, vmid); } -inline long hcall_query_vm_state(unsigned long vmid) -{ - return acrn_hypercall1(HC_QUERY_VMSTATE, vmid); -} - inline long hcall_set_memmap(unsigned long vmid, unsigned long memmap) { return acrn_hypercall2(HC_VM_SET_MEMMAP, vmid, memmap); diff --git a/include/linux/vhm/acrn_common.h b/include/linux/vhm/acrn_common.h index aa61fbed2596..23f80acd92f6 100644 --- a/include/linux/vhm/acrn_common.h +++ b/include/linux/vhm/acrn_common.h @@ -141,13 +141,16 @@ struct vhm_request_buffer { /* Common API params */ struct acrn_create_vm { - unsigned long vmid; /* OUT: HV return vmid to VHM */ - unsigned long vcpu_num; /* IN: VM vcpu number */ + int32_t vmid; /* OUT: return vmid to VHM. Keep it first field */ + uint32_t vcpu_num; /* IN: VM vcpu number */ + uint8_t GUID[16]; /* IN: GUID of this vm */ + uint8_t trusty_enabled;/* IN: whether trusty is enabled */ + uint8_t reserved[31]; /* Reserved for future use */ } __attribute__((aligned(8))); struct acrn_create_vcpu { - int vcpuid; /* IN: vcpu id */ - int pcpuid; /* IN: pcpu id */ + uint32_t vcpu_id; /* IN: vcpu id */ + uint32_t pcpu_id; /* IN: pcpu id */ } __attribute__((aligned(8))); struct acrn_set_ioreq_buffer { diff --git a/include/linux/vhm/acrn_hv_defs.h b/include/linux/vhm/acrn_hv_defs.h index fa32243a6407..eb1d4c974a3e 100644 --- a/include/linux/vhm/acrn_hv_defs.h +++ b/include/linux/vhm/acrn_hv_defs.h @@ -70,10 +70,9 @@ #define HC_GET_API_VERSION _HC_ID(HC_ID, HC_ID_VM_BASE + 0x00) #define HC_CREATE_VM _HC_ID(HC_ID, HC_ID_VM_BASE + 0x01) #define HC_DESTROY_VM _HC_ID(HC_ID, HC_ID_VM_BASE + 0x02) -#define HC_RESUME_VM _HC_ID(HC_ID, HC_ID_VM_BASE + 0x03) +#define HC_START_VM _HC_ID(HC_ID, HC_ID_VM_BASE + 0x03) #define HC_PAUSE_VM _HC_ID(HC_ID, HC_ID_VM_BASE + 0x04) -#define HC_QUERY_VMSTATE _HC_ID(HC_ID, HC_ID_VM_BASE + 0x05) -#define HC_CREATE_VCPU _HC_ID(HC_ID, HC_ID_VM_BASE + 0x06) +#define HC_CREATE_VCPU _HC_ID(HC_ID, HC_ID_VM_BASE + 0x05) /* IRQ and Interrupts */ #define HC_ID_IRQ_BASE 0x100UL diff --git a/include/linux/vhm/vhm_hypercall.h b/include/linux/vhm/vhm_hypercall.h index 5447e951bf4b..b40f8f898046 100644 --- a/include/linux/vhm/vhm_hypercall.h +++ b/include/linux/vhm/vhm_hypercall.h @@ -138,7 +138,7 @@ static inline long acrn_hypercall4(unsigned long hcall_id, unsigned long param1, } inline long hcall_create_vm(unsigned long vminfo); -inline long hcall_resume_vm(unsigned long vmid); +inline long hcall_start_vm(unsigned long vmid); inline long hcall_pause_vm(unsigned long vmid); inline long hcall_destroy_vm(unsigned long vmid); inline long hcall_query_vm_state(unsigned long vmid); diff --git a/include/linux/vhm/vhm_ioctl_defs.h b/include/linux/vhm/vhm_ioctl_defs.h index 79d91a858226..e157d6a86a66 100644 --- a/include/linux/vhm/vhm_ioctl_defs.h +++ b/include/linux/vhm/vhm_ioctl_defs.h @@ -60,10 +60,9 @@ #define IC_GET_API_VERSION _IC_ID(IC_ID, IC_ID_VM_BASE + 0x00) #define IC_CREATE_VM _IC_ID(IC_ID, IC_ID_VM_BASE + 0x01) #define IC_DESTROY_VM _IC_ID(IC_ID, IC_ID_VM_BASE + 0x02) -#define IC_RESUME_VM _IC_ID(IC_ID, IC_ID_VM_BASE + 0x03) +#define IC_START_VM _IC_ID(IC_ID, IC_ID_VM_BASE + 0x03) #define IC_PAUSE_VM _IC_ID(IC_ID, IC_ID_VM_BASE + 0x04) -#define IC_QUERY_VMSTATE _IC_ID(IC_ID, IC_ID_VM_BASE + 0x05) -#define IC_CREATE_VCPU _IC_ID(IC_ID, IC_ID_VM_BASE + 0x06) +#define IC_CREATE_VCPU _IC_ID(IC_ID, IC_ID_VM_BASE + 0x05) /* IRQ and Interrupts */ #define IC_ID_IRQ_BASE 0x100UL From f3bc0389b2d2a5dfb0e194189512cf86c969ef0a Mon Sep 17 00:00:00 2001 From: Jason Chen CJ Date: Fri, 31 Aug 2018 10:58:57 +0800 Subject: [PATCH 0023/1103] hypercall: refine HC ID and parameter number Change-Id: Ie5b73055add4c69b4dbf5cae1bb8bf941997ee6b Tracked-On: 218445 Signed-off-by: Jason Chen CJ Reviewed-on: Reviewed-by: Chi, Mingqiang Reviewed-by: Dong, Eddie Tested-by: Dong, Eddie --- drivers/vhm/vhm_hypercall.c | 7 +++++- include/linux/vhm/acrn_common.h | 10 ++++----- include/linux/vhm/acrn_hv_defs.h | 36 ++++++++++++++++--------------- include/linux/vhm/vhm_hypercall.h | 1 + 4 files changed, 31 insertions(+), 23 deletions(-) diff --git a/drivers/vhm/vhm_hypercall.c b/drivers/vhm/vhm_hypercall.c index b2738474afaf..741b8bd837cc 100644 --- a/drivers/vhm/vhm_hypercall.c +++ b/drivers/vhm/vhm_hypercall.c @@ -52,9 +52,14 @@ #include #include +inline long hcall_get_api_version(unsigned long api_version) +{ + return acrn_hypercall1(HC_GET_API_VERSION, api_version); +} + inline long hcall_create_vm(unsigned long vminfo) { - return acrn_hypercall2(HC_CREATE_VM, 0, vminfo); + return acrn_hypercall1(HC_CREATE_VM, vminfo); } inline long hcall_start_vm(unsigned long vmid) diff --git a/include/linux/vhm/acrn_common.h b/include/linux/vhm/acrn_common.h index 23f80acd92f6..a6f46648c853 100644 --- a/include/linux/vhm/acrn_common.h +++ b/include/linux/vhm/acrn_common.h @@ -141,11 +141,11 @@ struct vhm_request_buffer { /* Common API params */ struct acrn_create_vm { - int32_t vmid; /* OUT: return vmid to VHM. Keep it first field */ - uint32_t vcpu_num; /* IN: VM vcpu number */ - uint8_t GUID[16]; /* IN: GUID of this vm */ - uint8_t trusty_enabled;/* IN: whether trusty is enabled */ - uint8_t reserved[31]; /* Reserved for future use */ + int32_t vmid; /* OUT: return vmid to VHM. Keep it first field */ + uint32_t vcpu_num; /* IN: VM vcpu number */ + uint8_t GUID[16]; /* IN: GUID of this vm */ + uint8_t trusty_enabled;/* IN: whether trusty is enabled */ + uint8_t reserved[31]; /* Reserved for future use */ } __attribute__((aligned(8))); struct acrn_create_vcpu { diff --git a/include/linux/vhm/acrn_hv_defs.h b/include/linux/vhm/acrn_hv_defs.h index eb1d4c974a3e..bb57fb4f5cdd 100644 --- a/include/linux/vhm/acrn_hv_defs.h +++ b/include/linux/vhm/acrn_hv_defs.h @@ -63,49 +63,51 @@ #define _HC_ID(x, y) (((x)<<24)|(y)) -#define HC_ID 0x7FUL +#define HC_ID 0x80UL + +/* general */ +#define HC_ID_GEN_BASE 0x0UL +#define HC_GET_API_VERSION _HC_ID(HC_ID, HC_ID_GEN_BASE + 0x00) /* VM management */ -#define HC_ID_VM_BASE 0x0UL -#define HC_GET_API_VERSION _HC_ID(HC_ID, HC_ID_VM_BASE + 0x00) -#define HC_CREATE_VM _HC_ID(HC_ID, HC_ID_VM_BASE + 0x01) -#define HC_DESTROY_VM _HC_ID(HC_ID, HC_ID_VM_BASE + 0x02) -#define HC_START_VM _HC_ID(HC_ID, HC_ID_VM_BASE + 0x03) -#define HC_PAUSE_VM _HC_ID(HC_ID, HC_ID_VM_BASE + 0x04) -#define HC_CREATE_VCPU _HC_ID(HC_ID, HC_ID_VM_BASE + 0x05) +#define HC_ID_VM_BASE 0x10UL +#define HC_CREATE_VM _HC_ID(HC_ID, HC_ID_VM_BASE + 0x00) +#define HC_DESTROY_VM _HC_ID(HC_ID, HC_ID_VM_BASE + 0x01) +#define HC_START_VM _HC_ID(HC_ID, HC_ID_VM_BASE + 0x02) +#define HC_PAUSE_VM _HC_ID(HC_ID, HC_ID_VM_BASE + 0x03) +#define HC_CREATE_VCPU _HC_ID(HC_ID, HC_ID_VM_BASE + 0x04) /* IRQ and Interrupts */ -#define HC_ID_IRQ_BASE 0x100UL +#define HC_ID_IRQ_BASE 0x20UL #define HC_ASSERT_IRQLINE _HC_ID(HC_ID, HC_ID_IRQ_BASE + 0x00) #define HC_DEASSERT_IRQLINE _HC_ID(HC_ID, HC_ID_IRQ_BASE + 0x01) #define HC_PULSE_IRQLINE _HC_ID(HC_ID, HC_ID_IRQ_BASE + 0x02) #define HC_INJECT_MSI _HC_ID(HC_ID, HC_ID_IRQ_BASE + 0x03) /* DM ioreq management */ -#define HC_ID_IOREQ_BASE 0x200UL +#define HC_ID_IOREQ_BASE 0x30UL #define HC_SET_IOREQ_BUFFER _HC_ID(HC_ID, HC_ID_IOREQ_BASE + 0x00) #define HC_NOTIFY_REQUEST_FINISH _HC_ID(HC_ID, HC_ID_IOREQ_BASE + 0x01) - /* Guest memory management */ -#define HC_ID_MEM_BASE 0x300UL +#define HC_ID_MEM_BASE 0x40UL #define HC_VM_SET_MEMMAP _HC_ID(HC_ID, HC_ID_MEM_BASE + 0x00) #define HC_VM_GPA2HPA _HC_ID(HC_ID, HC_ID_MEM_BASE + 0x01) /* PCI assignment*/ -#define HC_ID_PCI_BASE 0x400UL +#define HC_ID_PCI_BASE 0x50UL #define HC_ASSIGN_PTDEV _HC_ID(HC_ID, HC_ID_PCI_BASE + 0x00) #define HC_DEASSIGN_PTDEV _HC_ID(HC_ID, HC_ID_PCI_BASE + 0x01) #define HC_VM_PCI_MSIX_REMAP _HC_ID(HC_ID, HC_ID_PCI_BASE + 0x02) #define HC_SET_PTDEV_INTR_INFO _HC_ID(HC_ID, HC_ID_PCI_BASE + 0x03) #define HC_RESET_PTDEV_INTR_INFO _HC_ID(HC_ID, HC_ID_PCI_BASE + 0x04) -/* TRACE */ -#define HC_ID_TRACE_BASE 0x600UL -#define HC_ACRN_SBUF_SETUP _HC_ID(HC_ID, HC_ID_TRACE_BASE + 0x00) +/* DEBUG */ +#define HC_ID_DBG_BASE 0x60UL +#define HC_SBUF_SETUP _HC_ID(HC_ID, HC_ID_DBG_BASE + 0x00) #define ACRN_DOM0_VMID (0UL) -#define ACRN_INVALID_VMID (-1UL) +#define ACRN_INVALID_VMID (-1) #define ACRN_INVALID_HPA (-1UL) /* Generic memory attributes */ diff --git a/include/linux/vhm/vhm_hypercall.h b/include/linux/vhm/vhm_hypercall.h index b40f8f898046..f4a5793f3ef7 100644 --- a/include/linux/vhm/vhm_hypercall.h +++ b/include/linux/vhm/vhm_hypercall.h @@ -137,6 +137,7 @@ static inline long acrn_hypercall4(unsigned long hcall_id, unsigned long param1, return result; } +inline long hcall_get_api_version(unsigned long api_version); inline long hcall_create_vm(unsigned long vminfo); inline long hcall_start_vm(unsigned long vmid); inline long hcall_pause_vm(unsigned long vmid); From 77aac8912a407be72b1486bab7e6131b70f5c34b Mon Sep 17 00:00:00 2001 From: Edwin Zhai Date: Fri, 31 Aug 2018 10:58:57 +0800 Subject: [PATCH 0024/1103] ioctl: cleanup ioctl structure vm_memseg/vm_memmap: remove unused fileds and define field size Change-Id: I9cb01cc6ea8eb97989e0b4b4ff6c55fa9b9822c8 Tracked-On: 218445 Signed-off-by: Edwin Zhai Reviewed-on: Reviewed-by: Chi, Mingqiang Reviewed-by: Dong, Eddie Tested-by: Dong, Eddie --- include/linux/vhm/vhm_ioctl_defs.h | 30 +++++++++++++----------------- 1 file changed, 13 insertions(+), 17 deletions(-) diff --git a/include/linux/vhm/vhm_ioctl_defs.h b/include/linux/vhm/vhm_ioctl_defs.h index e157d6a86a66..60bfb299e040 100644 --- a/include/linux/vhm/vhm_ioctl_defs.h +++ b/include/linux/vhm/vhm_ioctl_defs.h @@ -60,7 +60,7 @@ #define IC_GET_API_VERSION _IC_ID(IC_ID, IC_ID_VM_BASE + 0x00) #define IC_CREATE_VM _IC_ID(IC_ID, IC_ID_VM_BASE + 0x01) #define IC_DESTROY_VM _IC_ID(IC_ID, IC_ID_VM_BASE + 0x02) -#define IC_START_VM _IC_ID(IC_ID, IC_ID_VM_BASE + 0x03) +#define IC_START_VM _IC_ID(IC_ID, IC_ID_VM_BASE + 0x03) #define IC_PAUSE_VM _IC_ID(IC_ID, IC_ID_VM_BASE + 0x04) #define IC_CREATE_VCPU _IC_ID(IC_ID, IC_ID_VM_BASE + 0x05) @@ -95,35 +95,31 @@ #define SPECNAMELEN 63 -enum { - VM_SYSMEM, - VM_BOOTROM, - VM_FRAMEBUFFER, - VM_MMIO, -}; +#define VM_SYSMEM 0 +#define VM_MMIO 1 struct vm_memseg { - int segid; - size_t len; + uint32_t segid; + uint32_t reserved; + uint64_t len; + uint64_t gpa; char name[SPECNAMELEN + 1]; - unsigned long gpa; }; struct vm_memmap { - int segid; /* memory segment */ + uint32_t segid; /* memory segment */ + uint32_t reserved; union { struct { uint64_t gpa; - uint64_t segoff; /* offset into memory segment */ - size_t len; /* mmap length */ - int prot; /* RWX */ - int flags; + uint64_t len; /* mmap length */ + uint32_t prot; /* RWX */ } mem; struct { uint64_t gpa; uint64_t hpa; - size_t len; - int prot; + uint64_t len; + uint32_t prot; } mmio; }; }; From 3f67cd4b839911f79e225b943015b83bac4e077b Mon Sep 17 00:00:00 2001 From: "Li, Fei1" Date: Fri, 31 Aug 2018 10:58:57 +0800 Subject: [PATCH 0025/1103] Shared_buf: add shared buffer Added a ring buffer shared between ACRN hypervisor and service OS. Change-Id: Ib82f50d842592099629e0f764e0576306252c51b Tracked-On: Tracked-On: https://rtc.intel.com/ccm0001001/resource/itemName/com.ibm.team.workitem.WorkItem/216912 Signed-off-by: Li, Fei1 --- drivers/Kconfig | 1 + drivers/Makefile | 1 + drivers/acrn/Kconfig | 5 ++ drivers/acrn/Makefile | 1 + drivers/acrn/sbuf.c | 188 ++++++++++++++++++++++++++++++++++++++++++ drivers/acrn/sbuf.h | 119 ++++++++++++++++++++++++++ 6 files changed, 315 insertions(+) create mode 100644 drivers/acrn/Kconfig create mode 100644 drivers/acrn/Makefile create mode 100644 drivers/acrn/sbuf.c create mode 100644 drivers/acrn/sbuf.h diff --git a/drivers/Kconfig b/drivers/Kconfig index b9ac6b8cbdd4..ae0772a3581a 100644 --- a/drivers/Kconfig +++ b/drivers/Kconfig @@ -221,4 +221,5 @@ source "drivers/slimbus/Kconfig" source "drivers/vbs/Kconfig" +source "drivers/acrn/Kconfig" endmenu diff --git a/drivers/Makefile b/drivers/Makefile index 6d0c3cd78ffd..22376163e35d 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -185,6 +185,7 @@ obj-$(CONFIG_FSI) += fsi/ obj-$(CONFIG_TEE) += tee/ obj-$(CONFIG_MULTIPLEXER) += mux/ obj-$(CONFIG_ACRN) += vhm/ +obj-$(CONFIG_ACRN) += acrn/ obj-$(CONFIG_UNISYS_VISORBUS) += visorbus/ obj-$(CONFIG_SIOX) += siox/ obj-$(CONFIG_GNSS) += gnss/ diff --git a/drivers/acrn/Kconfig b/drivers/acrn/Kconfig new file mode 100644 index 000000000000..f25f0ae77727 --- /dev/null +++ b/drivers/acrn/Kconfig @@ -0,0 +1,5 @@ +config ACRN_SHARED_BUFFER + bool "Intel ACRN SHARED BUFFER" + ---help--- + Ring buffer shared between ACRN Hypervisor and its SOS. + Help ACRN performance profiling. diff --git a/drivers/acrn/Makefile b/drivers/acrn/Makefile new file mode 100644 index 000000000000..bc475f8116e3 --- /dev/null +++ b/drivers/acrn/Makefile @@ -0,0 +1 @@ +obj-$(CONFIG_ACRN_SHARED_BUFFER) += sbuf.o diff --git a/drivers/acrn/sbuf.c b/drivers/acrn/sbuf.c new file mode 100644 index 000000000000..dcf203222c5b --- /dev/null +++ b/drivers/acrn/sbuf.c @@ -0,0 +1,188 @@ +/* + * shared buffer + * + * This file is provided under a dual BSD/GPLv2 license.  When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright (c) 2017 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * 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. + * + * Contact Information: Li Fei + * + * BSD LICENSE + * + * Copyright (C) 2017 Intel Corporation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 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 + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Li Fei + * + */ + +#define pr_fmt(fmt) "SBuf: " fmt + +#include +#include +#include "sbuf.h" + +static inline bool sbuf_is_empty(shared_buf_t *sbuf) +{ + return (sbuf->head == sbuf->tail); +} + +static inline uint32_t sbuf_next_ptr(uint32_t pos, + uint32_t span, uint32_t scope) +{ + pos += span; + pos = (pos >= scope) ? (pos - scope) : pos; + return pos; +} + +static inline uint32_t sbuf_calculate_allocate_size(uint32_t ele_num, + uint32_t ele_size) +{ + uint64_t sbuf_allocate_size; + + sbuf_allocate_size = ele_num * ele_size; + sbuf_allocate_size += SBUF_HEAD_SIZE; + if (sbuf_allocate_size > SBUF_MAX_SIZE) { + pr_err("num=0x%x, size=0x%x exceed 0x%llx!\n", + ele_num, ele_size, SBUF_MAX_SIZE); + return 0; + } + + /* align to PAGE_SIZE */ + return (sbuf_allocate_size + PAGE_SIZE - 1) & PAGE_MASK; +} + +shared_buf_t *sbuf_allocate(uint32_t ele_num, uint32_t ele_size) +{ + shared_buf_t *sbuf; + struct page *page; + uint32_t sbuf_allocate_size; + + if (!ele_num || !ele_size) { + pr_err("invalid parameter %s!\n", __func__); + return NULL; + } + + sbuf_allocate_size = sbuf_calculate_allocate_size(ele_num, ele_size); + if (!sbuf_allocate_size) + return NULL; + + page = alloc_pages(GFP_KERNEL | __GFP_ZERO, + get_order(sbuf_allocate_size)); + if (page == NULL) { + pr_err("failed to alloc pages!\n"); + return NULL; + } + + sbuf = phys_to_virt(page_to_phys(page)); + sbuf->ele_num = ele_num; + sbuf->ele_size = ele_size; + sbuf->size = ele_num * ele_size; + sbuf->magic = SBUF_MAGIC; + pr_info("ele_num=0x%x, ele_size=0x%x allocated!\n", + ele_num, ele_size); + return sbuf; +} +EXPORT_SYMBOL(sbuf_allocate); + +void sbuf_free(shared_buf_t *sbuf) +{ + uint32_t sbuf_allocate_size; + + if ((sbuf == NULL) || sbuf->magic != SBUF_MAGIC) { + pr_err("invalid parameter %s\n", __func__); + return; + } + + sbuf_allocate_size = sbuf_calculate_allocate_size(sbuf->ele_num, + sbuf->ele_size); + if (!sbuf_allocate_size) + return; + + sbuf->magic = 0; + __free_pages((struct page *)virt_to_page(sbuf), + get_order(sbuf_allocate_size)); +} +EXPORT_SYMBOL(sbuf_free); + +int sbuf_get(shared_buf_t *sbuf, uint8_t *data) +{ + const void *from; + + if ((sbuf == NULL) || (data == NULL)) + return -EINVAL; + + if (sbuf_is_empty(sbuf)) { + /* no data available */ + return 0; + } + + from = (void *)sbuf + SBUF_HEAD_SIZE + sbuf->head; + + memcpy(data, from, sbuf->ele_size); + + sbuf->head = sbuf_next_ptr(sbuf->head, sbuf->ele_size, sbuf->size); + + return sbuf->ele_size; +} +EXPORT_SYMBOL(sbuf_get); + +shared_buf_t *sbuf_construct(uint32_t ele_num, uint32_t ele_size, + uint64_t paddr) +{ + shared_buf_t *sbuf; + + if (!ele_num || !ele_size || !paddr) + return NULL; + + sbuf = (shared_buf_t *)phys_to_virt(paddr); + BUG_ON(!virt_addr_valid(sbuf)); + + if ((sbuf->magic == SBUF_MAGIC) && + (sbuf->ele_num == ele_num) && + (sbuf->ele_size == ele_size)) { + pr_info("construct sbuf at 0x%llx.\n", paddr); + /* return sbuf for dump */ + return sbuf; + } + + return NULL; +} +EXPORT_SYMBOL(sbuf_construct); diff --git a/drivers/acrn/sbuf.h b/drivers/acrn/sbuf.h new file mode 100644 index 000000000000..7f3694920232 --- /dev/null +++ b/drivers/acrn/sbuf.h @@ -0,0 +1,119 @@ +/* + * shared buffer + * + * This file is provided under a dual BSD/GPLv2 license.  When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright (c) 2017 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * 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. + * + * Contact Information: Li Fei + * + * BSD LICENSE + * + * Copyright (C) 2017 Intel Corporation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 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 + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Li Fei + * + */ + +#ifndef SHARED_BUF_H +#define SHARED_BUF_H + +#include + + +#define SBUF_MAGIC 0x5aa57aa71aa13aa3 +#define SBUF_MAX_SIZE (1ULL << 22) +#define SBUF_HEAD_SIZE 64 + +/* sbuf flags */ +#define OVERRUN_CNT_EN (1ULL << 0) /* whether overrun counting is enabled */ +#define OVERWRITE_EN (1ULL << 1) /* whether overwrite is enabled */ + +/** + * (sbuf) head + buf (store (ele_num - 1) elements at most) + * buffer empty: tail == head + * buffer full: (tail + ele_size) % size == head + * + * Base of memory for elements + * | + * | + * --------------------------------------------------------------------------------------- + * | shared_buf_t | raw data (ele_size)| raw date (ele_size) | ... | raw data (ele_size) | + * --------------------------------------------------------------------------------------- + * | + * | + * shared_buf_t *buf + */ + +/* Make sure sizeof(shared_buf_t) == SBUF_HEAD_SIZE */ +typedef struct shared_buf { + uint64_t magic; + uint32_t ele_num; /* number of elements */ + uint32_t ele_size; /* sizeof of elements */ + uint32_t head; /* offset from base, to read */ + uint32_t tail; /* offset from base, to write */ + uint64_t flags; + uint32_t overrun_cnt; /* count of overrun */ + uint32_t size; /* ele_num * ele_size */ + uint32_t padding[6]; +} ____cacheline_aligned shared_buf_t; + +static inline void sbuf_clear_flags(shared_buf_t *sbuf, uint64_t flags) +{ + sbuf->flags &= ~flags; +} + +static inline void sbuf_set_flags(shared_buf_t *sbuf, uint64_t flags) +{ + sbuf->flags = flags; +} + +static inline void sbuf_add_flags(shared_buf_t *sbuf, uint64_t flags) +{ + sbuf->flags |= flags; +} + +shared_buf_t *sbuf_allocate(uint32_t ele_num, uint32_t ele_size); +void sbuf_free(shared_buf_t *sbuf); +int sbuf_get(shared_buf_t *sbuf, uint8_t *data); +shared_buf_t *sbuf_construct(uint32_t ele_num, uint32_t ele_size, uint64_t gpa); + +#endif /* SHARED_BUF_H */ From cc6e0a222da71a5dbc21a91adfccc2c0cfb90dbf Mon Sep 17 00:00:00 2001 From: "Li, Fei1" Date: Fri, 31 Aug 2018 10:58:57 +0800 Subject: [PATCH 0026/1103] Shared_buf: added hypercall for shared_buf setup Change-Id: I24ad2f767c7d633ad41d787c7d1a052b0fb75fb4 Tracked-On: https://rtc.intel.com/ccm0001001/resource/itemName/com.ibm.team.workitem.WorkItem/216912 Signed-off-by: Li, Fei1 --- drivers/acrn/sbuf.c | 21 +++++++++++++++++++++ drivers/acrn/sbuf.h | 1 + drivers/vhm/vhm_hypercall.c | 5 +++++ include/linux/vhm/acrn_hv_defs.h | 2 +- include/linux/vhm/vhm_hypercall.h | 1 + 5 files changed, 29 insertions(+), 1 deletion(-) diff --git a/drivers/acrn/sbuf.c b/drivers/acrn/sbuf.c index dcf203222c5b..8849ce28a06c 100644 --- a/drivers/acrn/sbuf.c +++ b/drivers/acrn/sbuf.c @@ -57,6 +57,8 @@ #include #include +#include +#include #include "sbuf.h" static inline bool sbuf_is_empty(shared_buf_t *sbuf) @@ -164,6 +166,25 @@ int sbuf_get(shared_buf_t *sbuf, uint8_t *data) } EXPORT_SYMBOL(sbuf_get); +int sbuf_share_setup(uint32_t pcpu_id, uint32_t sbuf_id, shared_buf_t *sbuf) +{ + struct sbuf_setup_param ssp; + + ssp.pcpu_id = pcpu_id; + ssp.sbuf_id = sbuf_id; + + if (!sbuf) { + ssp.gpa = 0; + } else { + BUG_ON(!virt_addr_valid(sbuf)); + ssp.gpa = virt_to_phys(sbuf); + } + pr_info("setup phys add = 0x%llx\n", ssp.gpa); + + return hcall_setup_sbuf(virt_to_phys(&ssp)); +} +EXPORT_SYMBOL(sbuf_share_setup); + shared_buf_t *sbuf_construct(uint32_t ele_num, uint32_t ele_size, uint64_t paddr) { diff --git a/drivers/acrn/sbuf.h b/drivers/acrn/sbuf.h index 7f3694920232..73608c35046c 100644 --- a/drivers/acrn/sbuf.h +++ b/drivers/acrn/sbuf.h @@ -114,6 +114,7 @@ static inline void sbuf_add_flags(shared_buf_t *sbuf, uint64_t flags) shared_buf_t *sbuf_allocate(uint32_t ele_num, uint32_t ele_size); void sbuf_free(shared_buf_t *sbuf); int sbuf_get(shared_buf_t *sbuf, uint8_t *data); +int sbuf_share_setup(uint32_t pcpu_id, uint32_t sbuf_id, shared_buf_t *sbuf); shared_buf_t *sbuf_construct(uint32_t ele_num, uint32_t ele_size, uint64_t gpa); #endif /* SHARED_BUF_H */ diff --git a/drivers/vhm/vhm_hypercall.c b/drivers/vhm/vhm_hypercall.c index 741b8bd837cc..d0da22f2a88b 100644 --- a/drivers/vhm/vhm_hypercall.c +++ b/drivers/vhm/vhm_hypercall.c @@ -77,6 +77,11 @@ inline long hcall_destroy_vm(unsigned long vmid) return acrn_hypercall1(HC_DESTROY_VM, vmid); } +inline long hcall_setup_sbuf(unsigned long sbuf_head) +{ + return acrn_hypercall1(HC_SETUP_SBUF, sbuf_head); +} + inline long hcall_set_memmap(unsigned long vmid, unsigned long memmap) { return acrn_hypercall2(HC_VM_SET_MEMMAP, vmid, memmap); diff --git a/include/linux/vhm/acrn_hv_defs.h b/include/linux/vhm/acrn_hv_defs.h index bb57fb4f5cdd..688d69b6f5b0 100644 --- a/include/linux/vhm/acrn_hv_defs.h +++ b/include/linux/vhm/acrn_hv_defs.h @@ -104,7 +104,7 @@ /* DEBUG */ #define HC_ID_DBG_BASE 0x60UL -#define HC_SBUF_SETUP _HC_ID(HC_ID, HC_ID_DBG_BASE + 0x00) +#define HC_SETUP_SBUF _HC_ID(HC_ID, HC_ID_DBG_BASE + 0x00) #define ACRN_DOM0_VMID (0UL) #define ACRN_INVALID_VMID (-1) diff --git a/include/linux/vhm/vhm_hypercall.h b/include/linux/vhm/vhm_hypercall.h index f4a5793f3ef7..e56a16c5518f 100644 --- a/include/linux/vhm/vhm_hypercall.h +++ b/include/linux/vhm/vhm_hypercall.h @@ -143,6 +143,7 @@ inline long hcall_start_vm(unsigned long vmid); inline long hcall_pause_vm(unsigned long vmid); inline long hcall_destroy_vm(unsigned long vmid); inline long hcall_query_vm_state(unsigned long vmid); +inline long hcall_setup_sbuf(unsigned long sbuf_head); inline long hcall_set_memmap(unsigned long vmid, unsigned long memmap); inline long hcall_set_ioreq_buffer(unsigned long vmid, From edd9e0395f006b4341b8cab60562a07bb318faac Mon Sep 17 00:00:00 2001 From: "Li, Fei1" Date: Fri, 31 Aug 2018 10:58:57 +0800 Subject: [PATCH 0027/1103] ACRNTrace: add acrn trace module Change-Id: I9bf3a0a13e411e15063eb50905875e86e5731d1b Tracked-On: https://rtc.intel.com/ccm0001001/resource/itemName/com.ibm.team.workitem.WorkItem/216912 Signed-off-by: Li, Fei1 --- drivers/acrn/Kconfig | 8 + drivers/acrn/Makefile | 1 + drivers/acrn/acrn_trace.c | 297 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 306 insertions(+) create mode 100644 drivers/acrn/acrn_trace.c diff --git a/drivers/acrn/Kconfig b/drivers/acrn/Kconfig index f25f0ae77727..08b24a168167 100644 --- a/drivers/acrn/Kconfig +++ b/drivers/acrn/Kconfig @@ -3,3 +3,11 @@ config ACRN_SHARED_BUFFER ---help--- Ring buffer shared between ACRN Hypervisor and its SOS. Help ACRN performance profiling. + +config ACRN_TRACE + tristate "Intel ACRN Hypervisor Trace support" + select ACRN_SHARED_BUFFER + ---help--- + This is the Trace driver for the Intel ACRN hypervisor. + You can say y to build it into the kernel, or m to build + it as a module. diff --git a/drivers/acrn/Makefile b/drivers/acrn/Makefile index bc475f8116e3..5430f4fa06fd 100644 --- a/drivers/acrn/Makefile +++ b/drivers/acrn/Makefile @@ -1 +1,2 @@ obj-$(CONFIG_ACRN_SHARED_BUFFER) += sbuf.o +obj-$(CONFIG_ACRN_TRACE) += acrn_trace.o \ No newline at end of file diff --git a/drivers/acrn/acrn_trace.c b/drivers/acrn/acrn_trace.c new file mode 100644 index 000000000000..31470a3de6ac --- /dev/null +++ b/drivers/acrn/acrn_trace.c @@ -0,0 +1,297 @@ +/* +* +* ACRN Trace module +* +* This file is provided under a dual BSD/GPLv2 license.  When using or +* redistributing this file, you may do so under either license. +* +* GPL LICENSE SUMMARY +* +* Copyright (c) 2017 Intel Corporation. All rights reserved. +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of version 2 of the GNU General Public License as +* published by the Free Software Foundation. +* +* 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. +* +* Contact Information: Yan, Like +* +* BSD LICENSE +* +* Copyright (c) 2017 Intel Corporation. All rights reserved. +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions +* are met: +* +*   * Redistributions of source code must retain the above copyright +*     notice, this list of conditions and the following disclaimer. +*   * Redistributions in binary form must reproduce the above copyright +*     notice, this list of conditions and the following disclaimer in +*     the documentation and/or other materials provided with the +*     distribution. +*   * Neither the name of Intel Corporation nor the names of its +*     contributors may be used to endorse or promote products derived +*     from this software without specific prior written permission. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 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 +* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +* +* Like Yan +* +*/ + +#define pr_fmt(fmt) "ACRNTrace: " fmt + +#include +#include +#include +#include +#include +#include + +#include "sbuf.h" + + +#define TRACE_SBUF_SIZE (4 * 1024 * 1024) +#define TRACE_ELEMENT_SIZE 32 /* byte */ +#define TRACE_ELEMENT_NUM ((TRACE_SBUF_SIZE - SBUF_HEAD_SIZE) / \ + TRACE_ELEMENT_SIZE) + +#define foreach_cpu(cpu, cpu_num) \ + for ((cpu) = 0; (cpu) < (cpu_num); (cpu)++) + +#define MAX_NR_CPUS 4 +/* actual physical cpu number, initialized by module init */ +static int pcpu_num; + +static int nr_cpus = MAX_NR_CPUS; +module_param(nr_cpus, int, S_IRUSR | S_IWUSR); + +static atomic_t open_cnt[MAX_NR_CPUS]; +static shared_buf_t *sbuf_per_cpu[MAX_NR_CPUS]; + +static inline int get_id_from_devname(struct file *filep) +{ + uint32_t cpuid; + int err; + char id_str[16]; + struct miscdevice *dev = filep->private_data; + + strncpy(id_str, (void *)dev->name + sizeof("acrn_trace_") - 1, 16); + id_str[15] = '\0'; + err = kstrtoul(&id_str[0], 10, (unsigned long *)&cpuid); + + if (err) + return err; + + if (cpuid >= pcpu_num) { + pr_err("%s, failed to get cpuid, cpuid %d\n", + __func__, cpuid); + return -1; + } + + return cpuid; +} + +/************************************************************************ + * + * file_operations functions + * + ***********************************************************************/ +static int acrn_trace_open(struct inode *inode, struct file *filep) +{ + int cpuid = get_id_from_devname(filep); + + pr_debug("%s, cpu %d\n", __func__, cpuid); + if (cpuid < 0) + return -ENXIO; + + /* More than one reader at the same time could get data messed up */ + if (atomic_read(&open_cnt[cpuid])) + return -EBUSY; + + atomic_inc(&open_cnt[cpuid]); + + return 0; +} + +static int acrn_trace_release(struct inode *inode, struct file *filep) +{ + int cpuid = get_id_from_devname(filep); + + pr_debug("%s, cpu %d\n", __func__, cpuid); + if (cpuid < 0) + return -ENXIO; + + atomic_dec(&open_cnt[cpuid]); + + return 0; +} + +static int acrn_trace_mmap(struct file *filep, struct vm_area_struct *vma) +{ + int cpuid = get_id_from_devname(filep); + phys_addr_t paddr; + + pr_debug("%s, cpu %d\n", __func__, cpuid); + if (cpuid < 0) + return -ENXIO; + + BUG_ON(!virt_addr_valid(sbuf_per_cpu[cpuid])); + paddr = virt_to_phys(sbuf_per_cpu[cpuid]); + + if (remap_pfn_range(vma, vma->vm_start, + paddr >> PAGE_SHIFT, + vma->vm_end - vma->vm_start, + vma->vm_page_prot)) { + pr_err("Failed to mmap sbuf for cpu%d\n", cpuid); + return -EAGAIN; + } + + return 0; +} + +static const struct file_operations acrn_trace_fops = { + .owner = THIS_MODULE, + .open = acrn_trace_open, + .release = acrn_trace_release, + .mmap = acrn_trace_mmap, +}; + +static struct miscdevice acrn_trace_dev0 = { + .name = "acrn_trace_0", + .minor = MISC_DYNAMIC_MINOR, + .fops = &acrn_trace_fops, +}; + +static struct miscdevice acrn_trace_dev1 = { + .name = "acrn_trace_1", + .minor = MISC_DYNAMIC_MINOR, + .fops = &acrn_trace_fops, +}; + +static struct miscdevice acrn_trace_dev2 = { + .name = "acrn_trace_2", + .minor = MISC_DYNAMIC_MINOR, + .fops = &acrn_trace_fops, +}; + +static struct miscdevice acrn_trace_dev3 = { + .name = "acrn_trace_3", + .minor = MISC_DYNAMIC_MINOR, + .fops = &acrn_trace_fops, +}; + +static struct miscdevice *acrn_trace_devs[4] = { + &acrn_trace_dev0, + &acrn_trace_dev1, + &acrn_trace_dev2, + &acrn_trace_dev3, +}; + +/* + * acrn_trace_init() + */ +static int __init acrn_trace_init(void) +{ + int ret = 0; + int i, cpu; + + /* TBD: we could get the native cpu number by hypercall later */ + pr_info("%s, cpu_num %d\n", __func__, nr_cpus); + if (nr_cpus > MAX_NR_CPUS) { + pr_err("nr_cpus %d exceed MAX_NR_CPUS %d !\n", + nr_cpus, MAX_NR_CPUS); + return -EINVAL; + } + pcpu_num = nr_cpus; + + foreach_cpu(cpu, pcpu_num) { + /* allocate shared_buf */ + sbuf_per_cpu[cpu] = sbuf_allocate(TRACE_ELEMENT_NUM, + TRACE_ELEMENT_SIZE); + if (!sbuf_per_cpu[cpu]) { + pr_err("Failed alloc SBuf, cpuid %d\n", cpu); + ret = -ENOMEM; + goto out_free; + } + } + + foreach_cpu(cpu, pcpu_num) { + ret = sbuf_share_setup(cpu, 0, sbuf_per_cpu[cpu]); + if (ret < 0) { + pr_err("Failed to setup SBuf, cpuid %d\n", cpu); + goto out_sbuf; + } + } + + foreach_cpu(cpu, pcpu_num) { + ret = misc_register(acrn_trace_devs[cpu]); + if (ret < 0) { + pr_err("Failed to register acrn_trace_%d, errno %d\n", + cpu, ret); + goto out_dereg; + } + } + + return ret; + +out_dereg: + for (i = --cpu; i >= 0; i--) + misc_deregister(acrn_trace_devs[i]); + cpu = pcpu_num; + +out_sbuf: + for (i = --cpu; i >= 0; i--) + sbuf_share_setup(i, 0, NULL); + cpu = pcpu_num; + +out_free: + for (i = --cpu; i >= 0; i--) + sbuf_free(sbuf_per_cpu[i]); + + return ret; +} + +/* + * acrn_trace_exit() + */ +static void __exit acrn_trace_exit(void) +{ + int cpu; + + pr_info("%s, cpu_num %d\n", __func__, pcpu_num); + + foreach_cpu(cpu, pcpu_num) { + /* deregister devices */ + misc_deregister(acrn_trace_devs[cpu]); + + /* set sbuf pointer to NULL in HV */ + sbuf_share_setup(cpu, 0, NULL); + + /* free sbuf, sbuf_per_cpu[cpu] should be set NULL */ + sbuf_free(sbuf_per_cpu[cpu]); + } +} + +module_init(acrn_trace_init); +module_exit(acrn_trace_exit); + +MODULE_LICENSE("Dual BSD/GPL"); +MODULE_AUTHOR("Intel Corp., http://www.intel.com"); +MODULE_DESCRIPTION("Driver for the Intel ACRN Hypervisor Trace"); +MODULE_VERSION("0.1"); From 349d5b6e49432d9a231c5cc518f2a6860ad7b559 Mon Sep 17 00:00:00 2001 From: Shiqing Gao Date: Fri, 31 Aug 2018 10:58:57 +0800 Subject: [PATCH 0028/1103] sos: fix potential bugs in ptdev msi-x access - delete the node in the 'table_iomems' list for msi-x table when the pass-through device is deinited. otherwise, memory leak might occur. - add a check of irq type before msi-x table access Change-Id: I9d2ec1e430356ef0ca61855cfbc76ae9cfdb2529 Signed-off-by: Shiqing Gao --- drivers/char/vhm/vhm_dev.c | 34 ++++++++++++++++++++-------------- 1 file changed, 20 insertions(+), 14 deletions(-) diff --git a/drivers/char/vhm/vhm_dev.c b/drivers/char/vhm/vhm_dev.c index d3fd572c1642..7894297ca872 100644 --- a/drivers/char/vhm/vhm_dev.c +++ b/drivers/char/vhm/vhm_dev.c @@ -402,7 +402,8 @@ static long vhm_dev_ioctl(struct file *filep, return -EFAULT; } - if (ic_pt_irq.msix.table_paddr) { + if ((ic_pt_irq.type == IRQ_MSIX) && + ic_pt_irq.msix.table_paddr) { new = kmalloc(sizeof(struct table_iomems), GFP_KERNEL); if (new == NULL) return -EFAULT; @@ -419,7 +420,8 @@ static long vhm_dev_ioctl(struct file *filep, break; } case IC_RESET_PTDEV_INTR_INFO: { - struct table_iomems *new; + struct table_iomems *ptr; + int dev_found = 0; if (copy_from_user(&ic_pt_irq, (void *)ioctl_param, sizeof(ic_pt_irq))) @@ -434,17 +436,18 @@ static long vhm_dev_ioctl(struct file *filep, return -EFAULT; } - if (ic_pt_irq.msix.table_paddr) { - new = kmalloc(sizeof(struct table_iomems), GFP_KERNEL); - if (new == NULL) - return -EFAULT; - new->phys_bdf = ic_pt_irq.phys_bdf; - new->mmap_addr = (unsigned long) - ioremap_nocache(ic_pt_irq.msix.table_paddr, - ic_pt_irq.msix.table_size); - + if (ic_pt_irq.type == IRQ_MSIX) { mutex_lock(&table_iomems_lock); - list_add(&new->list, &table_iomems_list); + list_for_each_entry(ptr, &table_iomems_list, list) { + if (ptr->phys_bdf == ic_pt_irq.phys_bdf) { + dev_found = 1; + break; + } + } + if (dev_found) { + iounmap((void __iomem *)ptr->mmap_addr); + list_del(&ptr->list); + } mutex_unlock(&table_iomems_lock); } @@ -467,15 +470,18 @@ static long vhm_dev_ioctl(struct file *filep, if (msix_remap.msix) { void __iomem *msix_entry; struct table_iomems *ptr; + int dev_found = 0; mutex_lock(&table_iomems_lock); list_for_each_entry(ptr, &table_iomems_list, list) { - if (ptr->phys_bdf == msix_remap.phys_bdf) + if (ptr->phys_bdf == msix_remap.phys_bdf) { + dev_found = 1; break; + } } mutex_unlock(&table_iomems_lock); - if (!ptr->mmap_addr) + if (!dev_found || !ptr->mmap_addr) return -EFAULT; msix_entry = (void __iomem *) (ptr->mmap_addr + From 533fc9d898c5dfe5597c6d5084110c5c6c8dfa13 Mon Sep 17 00:00:00 2001 From: Edwin Zhai Date: Fri, 31 Aug 2018 10:58:57 +0800 Subject: [PATCH 0029/1103] vhm: cleanup ioctls Redefine ioctl command number Change-Id: I555cdbdd03c50f9fa5b66eb95d61c8d83c60a276 Tracked-On: 212688 Signed-off-by: Edwin Zhai --- include/linux/vhm/vhm_ioctl_defs.h | 27 +++++++++++++++------------ 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/include/linux/vhm/vhm_ioctl_defs.h b/include/linux/vhm/vhm_ioctl_defs.h index 60bfb299e040..d00b6588f296 100644 --- a/include/linux/vhm/vhm_ioctl_defs.h +++ b/include/linux/vhm/vhm_ioctl_defs.h @@ -53,26 +53,29 @@ * Commmon IOCTL ID defination for VHM/DM */ #define _IC_ID(x, y) (((x)<<24)|(y)) -#define IC_ID 0x5FUL +#define IC_ID 0x43UL + +/* General */ +#define IC_ID_GEN_BASE 0x0UL +#define IC_GET_API_VERSION _IC_ID(IC_ID, IC_ID_GEN_BASE + 0x00) /* VM management */ -#define IC_ID_VM_BASE 0x0UL -#define IC_GET_API_VERSION _IC_ID(IC_ID, IC_ID_VM_BASE + 0x00) -#define IC_CREATE_VM _IC_ID(IC_ID, IC_ID_VM_BASE + 0x01) -#define IC_DESTROY_VM _IC_ID(IC_ID, IC_ID_VM_BASE + 0x02) -#define IC_START_VM _IC_ID(IC_ID, IC_ID_VM_BASE + 0x03) -#define IC_PAUSE_VM _IC_ID(IC_ID, IC_ID_VM_BASE + 0x04) -#define IC_CREATE_VCPU _IC_ID(IC_ID, IC_ID_VM_BASE + 0x05) +#define IC_ID_VM_BASE 0x10UL +#define IC_CREATE_VM _IC_ID(IC_ID, IC_ID_VM_BASE + 0x00) +#define IC_DESTROY_VM _IC_ID(IC_ID, IC_ID_VM_BASE + 0x01) +#define IC_START_VM _IC_ID(IC_ID, IC_ID_VM_BASE + 0x02) +#define IC_PAUSE_VM _IC_ID(IC_ID, IC_ID_VM_BASE + 0x03) +#define IC_CREATE_VCPU _IC_ID(IC_ID, IC_ID_VM_BASE + 0x04) /* IRQ and Interrupts */ -#define IC_ID_IRQ_BASE 0x100UL +#define IC_ID_IRQ_BASE 0x20UL #define IC_ASSERT_IRQLINE _IC_ID(IC_ID, IC_ID_IRQ_BASE + 0x00) #define IC_DEASSERT_IRQLINE _IC_ID(IC_ID, IC_ID_IRQ_BASE + 0x01) #define IC_PULSE_IRQLINE _IC_ID(IC_ID, IC_ID_IRQ_BASE + 0x02) #define IC_INJECT_MSI _IC_ID(IC_ID, IC_ID_IRQ_BASE + 0x03) /* DM ioreq management */ -#define IC_ID_IOREQ_BASE 0x200UL +#define IC_ID_IOREQ_BASE 0x30UL #define IC_SET_IOREQ_BUFFER _IC_ID(IC_ID, IC_ID_IOREQ_BASE + 0x00) #define IC_NOTIFY_REQUEST_FINISH _IC_ID(IC_ID, IC_ID_IOREQ_BASE + 0x01) #define IC_CREATE_IOREQ_CLIENT _IC_ID(IC_ID, IC_ID_IOREQ_BASE + 0x02) @@ -81,12 +84,12 @@ /* Guest memory management */ -#define IC_ID_MEM_BASE 0x300UL +#define IC_ID_MEM_BASE 0x40UL #define IC_ALLOC_MEMSEG _IC_ID(IC_ID, IC_ID_MEM_BASE + 0x00) #define IC_SET_MEMSEG _IC_ID(IC_ID, IC_ID_MEM_BASE + 0x01) /* PCI assignment*/ -#define IC_ID_PCI_BASE 0x400UL +#define IC_ID_PCI_BASE 0x50UL #define IC_ASSIGN_PTDEV _IC_ID(IC_ID, IC_ID_PCI_BASE + 0x00) #define IC_DEASSIGN_PTDEV _IC_ID(IC_ID, IC_ID_PCI_BASE + 0x01) #define IC_VM_PCI_MSIX_REMAP _IC_ID(IC_ID, IC_ID_PCI_BASE + 0x02) From 613f17b6b4edbaa7c507ea1925d1bcb2ee0050e5 Mon Sep 17 00:00:00 2001 From: Jason Chen CJ Date: Fri, 31 Aug 2018 10:58:57 +0800 Subject: [PATCH 0030/1103] VHM: check HV api version for VHM module init Change-Id: I8d49db28e235fe643380b4e8b82fb629e89accaf Tracked-On: 218802 Signed-off-by: Jason Chen CJ Signed-off-by: Yonghua Huang --- drivers/char/vhm/vhm_dev.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/drivers/char/vhm/vhm_dev.c b/drivers/char/vhm/vhm_dev.c index 7894297ca872..88fb0faf19fc 100644 --- a/drivers/char/vhm/vhm_dev.c +++ b/drivers/char/vhm/vhm_dev.c @@ -565,12 +565,30 @@ static const struct file_operations fops = { .poll = vhm_dev_poll, }; +#define SUPPORT_HV_API_VERSION_MAJOR 1 +#define SUPPORT_HV_API_VERSION_MINOR 0 static int __init vhm_init(void) { unsigned long flag; + struct hc_api_version api_version = {0, 0}; pr_info("vhm: initializing\n"); + if (hcall_get_api_version(virt_to_phys(&api_version)) < 0) { + pr_err("vhm: failed to get api version from Hypervisor !\n"); + return -EINVAL; + } + + if (api_version.major_version == SUPPORT_HV_API_VERSION_MAJOR && + api_version.minor_version == SUPPORT_HV_API_VERSION_MINOR) { + pr_info("vhm: hv api version %d.%d\n", + api_version.major_version, api_version.minor_version); + } else { + pr_err("vhm: not support hv api version %d.%d!\n", + api_version.major_version, api_version.minor_version); + return -EINVAL; + } + /* Try to dynamically allocate a major number for the device */ major = register_chrdev(0, DEVICE_NAME, &fops); if (major < 0) { From 66bbc0d10b4501acc1b140487280431801eab966 Mon Sep 17 00:00:00 2001 From: Jason Chen CJ Date: Fri, 31 Aug 2018 10:58:58 +0800 Subject: [PATCH 0031/1103] VHM: add VHM api version support Change-Id: I36dd051d0cc04720ab8d69817392ff97f1e5ad34 Tracked-On: 218802 Signed-off-by: Jason Chen CJ --- drivers/char/vhm/vhm_dev.c | 16 ++++++++++++++++ include/linux/vhm/vhm_ioctl_defs.h | 5 +++++ 2 files changed, 21 insertions(+) diff --git a/drivers/char/vhm/vhm_dev.c b/drivers/char/vhm/vhm_dev.c index 88fb0faf19fc..27844c87a41f 100644 --- a/drivers/char/vhm/vhm_dev.c +++ b/drivers/char/vhm/vhm_dev.c @@ -87,6 +87,9 @@ #define DEVICE_NAME "acrn_vhm" #define CLASS_NAME "vhm" +#define VHM_API_VERSION_MAJOR 1 +#define VHM_API_VERSION_MINOR 0 + static int major; static struct class *vhm_class; static struct device *vhm_device; @@ -156,6 +159,19 @@ static long vhm_dev_ioctl(struct file *filep, trace_printk("[%s] ioctl_num=0x%x\n", __func__, ioctl_num); + if (ioctl_num == IC_GET_API_VERSION) { + struct api_version api_version; + + api_version.major_version = VHM_API_VERSION_MAJOR; + api_version.minor_version = VHM_API_VERSION_MINOR; + + if (copy_to_user((void *)ioctl_param, &api_version, + sizeof(struct api_version))) + return -EFAULT; + + return 0; + } + memset(&hc_pt_irq, 0, sizeof(hc_pt_irq)); memset(&ic_pt_irq, 0, sizeof(ic_pt_irq)); vm = (struct vhm_vm *)filep->private_data; diff --git a/include/linux/vhm/vhm_ioctl_defs.h b/include/linux/vhm/vhm_ioctl_defs.h index d00b6588f296..258ec3982da9 100644 --- a/include/linux/vhm/vhm_ioctl_defs.h +++ b/include/linux/vhm/vhm_ioctl_defs.h @@ -159,4 +159,9 @@ struct ioreq_notify { uint32_t vcpu; }; +struct api_version { + uint32_t major_version; + uint32_t minor_version; +}; + #endif /* VHM_IOCTL_DEFS_H */ From 96c8e41262332f0f224a4d351225d96214fa463f Mon Sep 17 00:00:00 2001 From: Hao Li Date: Fri, 31 Aug 2018 10:58:58 +0800 Subject: [PATCH 0032/1103] virtio framework: support ACRN virtio devices To support ACRN virtio devices which use Intel VID:DID, relax virtio device probing conditions in frontend virtio framework. Change-Id: I9a49ad3fbdbd0a615398218382624031d6908526 Tracked-On: 219551 Signed-off-by: Hao Li Reviewed-on: Reviewed-by: Liu, Fuzhong Reviewed-by: Dong, Eddie Tested-by: Dong, Eddie --- drivers/virtio/Kconfig | 17 +++++++++++++++++ drivers/virtio/virtio_pci_common.c | 29 +++++++++++++++++++++++++++++ drivers/virtio/virtio_pci_legacy.c | 10 ++++++++++ drivers/virtio/virtio_pci_modern.c | 15 +++++++++++++++ include/uapi/linux/virtio_ids.h | 13 +++++++++++++ 5 files changed, 84 insertions(+) diff --git a/drivers/virtio/Kconfig b/drivers/virtio/Kconfig index 35897649c24f..51d43fd4bd2b 100644 --- a/drivers/virtio/Kconfig +++ b/drivers/virtio/Kconfig @@ -83,4 +83,21 @@ config VIRTIO_MMIO_CMDLINE_DEVICES If unsure, say 'N'. +config ACRN_VIRTIO_DEVICES + bool "Support for ACRN virtio devices drivers in frontend/guest" + default n + depends on VIRTIO_PCI + ---help--- + ACRN virtio devices support in frontend/guest. + + This option enables support for ACRN virtio devices which use Intel + vendor ID and device IDs, by extending virtio frontend framework + a little bit so that virtio PCI driver could be loaded for these + devices. + + Eventually if all devices obtain virtio VID and DIDs, we don't + need this option anymore. + + If unsure, say 'N'. + endif # VIRTIO_MENU diff --git a/drivers/virtio/virtio_pci_common.c b/drivers/virtio/virtio_pci_common.c index 465a6f5142cc..9bb79f578fe5 100644 --- a/drivers/virtio/virtio_pci_common.c +++ b/drivers/virtio/virtio_pci_common.c @@ -494,6 +494,35 @@ static const struct dev_pm_ops virtio_pci_pm_ops = { /* Qumranet donated their vendor ID for devices 0x1000 thru 0x10FF. */ static const struct pci_device_id virtio_pci_id_table[] = { { PCI_DEVICE(PCI_VENDOR_ID_REDHAT_QUMRANET, PCI_ANY_ID) }, +#ifdef CONFIG_ACRN_VIRTIO_DEVICES + /* + * To support ACRN virtio devices which haven't obtained valid + * virtio VID:DID in time, we relax the probing conditions a little. + */ +#define ACRN_VIRTIO_DEVICE_ID_RPMB 0x8601 + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, ACRN_VIRTIO_DEVICE_ID_RPMB) }, + +#define ACRN_VIRTIO_DEVICE_ID_HECI 0x8602 + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, ACRN_VIRTIO_DEVICE_ID_HECI) }, + +#define ACRN_VIRTIO_DEVICE_ID_AUDIO 0x8603 + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, ACRN_VIRTIO_DEVICE_ID_AUDIO) }, + +#define ACRN_VIRTIO_DEVICE_ID_IPU 0x8604 + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, ACRN_VIRTIO_DEVICE_ID_IPU) }, + +#define ACRN_VIRTIO_DEVICE_ID_TSN 0x8605 + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, ACRN_VIRTIO_DEVICE_ID_TSN) }, + +#define ACRN_VIRTIO_DEVICE_ID_HYPERDMABUF 0x8606 + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, ACRN_VIRTIO_DEVICE_ID_HYPERDMABUF) }, + +#define ACRN_VIRTIO_DEVICE_ID_HDCP 0x8607 + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, ACRN_VIRTIO_DEVICE_ID_HDCP) }, + +#define ACRN_VIRTIO_DEVICE_ID_COREU 0x8608 + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, ACRN_VIRTIO_DEVICE_ID_COREU) }, +#endif /* CONFIG_ACRN_VIRTIO_DEVICES */ { 0 } }; diff --git a/drivers/virtio/virtio_pci_legacy.c b/drivers/virtio/virtio_pci_legacy.c index de062fb201bc..b21f1034054c 100644 --- a/drivers/virtio/virtio_pci_legacy.c +++ b/drivers/virtio/virtio_pci_legacy.c @@ -215,9 +215,19 @@ int virtio_pci_legacy_probe(struct virtio_pci_device *vp_dev) struct pci_dev *pci_dev = vp_dev->pci_dev; int rc; +#ifdef CONFIG_ACRN_VIRTIO_DEVICES + /* + * To support ACRN virtio devices which haven't obtained valid + * virtio VID:DID in time, we relax the probing conditions a little. + */ + if (pci_dev->vendor == PCI_VENDOR_ID_REDHAT_QUMRANET && + (pci_dev->device < 0x1000 || pci_dev->device > 0x103f)) + return -ENODEV; +#else /* We only own devices >= 0x1000 and <= 0x103f: leave the rest. */ if (pci_dev->device < 0x1000 || pci_dev->device > 0x103f) return -ENODEV; +#endif /* CONFIG_ACRN_VIRTIO_DEVICES */ if (pci_dev->revision != VIRTIO_PCI_ABI_VERSION) { printk(KERN_ERR "virtio_pci: expected ABI version %d, got %d\n", diff --git a/drivers/virtio/virtio_pci_modern.c b/drivers/virtio/virtio_pci_modern.c index 07571daccfec..b7474e0cce21 100644 --- a/drivers/virtio/virtio_pci_modern.c +++ b/drivers/virtio/virtio_pci_modern.c @@ -590,11 +590,26 @@ int virtio_pci_modern_probe(struct virtio_pci_device *vp_dev) check_offsets(); +#ifdef CONFIG_ACRN_VIRTIO_DEVICES + /* + * To support ACRN virtio devices which haven't obtained valid + * virtio VID:DID in time, we relax the probing conditions a little. + */ + if (pci_dev->vendor == PCI_VENDOR_ID_REDHAT_QUMRANET && + (pci_dev->device < 0x1000 || pci_dev->device > 0x107f)) + return -ENODEV; + + if ((pci_dev->vendor == PCI_VENDOR_ID_REDHAT_QUMRANET && + pci_dev->device < 0x1040) || + (pci_dev->vendor == PCI_VENDOR_ID_INTEL && + pci_dev->device < 0x8640)) { +#else /* We only own devices >= 0x1000 and <= 0x107f: leave the rest. */ if (pci_dev->device < 0x1000 || pci_dev->device > 0x107f) return -ENODEV; if (pci_dev->device < 0x1040) { +#endif /* CONFIG_ACRN_VIRTIO_DEVICES */ /* Transitional devices: use the PCI subsystem device id as * virtio device id, same as legacy driver always did. */ diff --git a/include/uapi/linux/virtio_ids.h b/include/uapi/linux/virtio_ids.h index 6d5c3b2d4f4d..a95019652bb5 100644 --- a/include/uapi/linux/virtio_ids.h +++ b/include/uapi/linux/virtio_ids.h @@ -44,4 +44,17 @@ #define VIRTIO_ID_VSOCK 19 /* virtio vsock transport */ #define VIRTIO_ID_CRYPTO 20 /* virtio crypto */ +#ifdef CONFIG_ACRN_VIRTIO_DEVICES +/* ACRN virtio device types */ +#define VIRTIO_ID_RPMB 0xFFFF +#define VIRTIO_ID_HECI 0xFFFE +#define VIRTIO_ID_AUDIO 0xFFFD +#define VIRTIO_ID_IPU 0xFFFC +#define VIRTIO_ID_TSN 0xFFFB +#define VIRTIO_ID_HYPERDMABUF 0xFFFA +#define VIRTIO_ID_HDCP 0xFFF9 +#define VIRTIO_ID_COREU 0xFFF8 + +#endif /* CONFIG_ACRN_VIRTIO_DEVICES */ + #endif /* _LINUX_VIRTIO_IDS_H */ From a13c6254c90a3d367f6dfd3b2b1c7c66532add5e Mon Sep 17 00:00:00 2001 From: Jason Chen CJ Date: Fri, 31 Aug 2018 10:58:58 +0800 Subject: [PATCH 0033/1103] VHM: sync public header file acrn_common.h Change-Id: I1e0ac4d26b22cda4d1db81a83dca8d8806405a8c Tracked-On: 212688 Signed-off-by: Jason Chen CJ --- include/linux/vhm/acrn_common.h | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/include/linux/vhm/acrn_common.h b/include/linux/vhm/acrn_common.h index a6f46648c853..f27feb7a3e57 100644 --- a/include/linux/vhm/acrn_common.h +++ b/include/linux/vhm/acrn_common.h @@ -144,7 +144,7 @@ struct acrn_create_vm { int32_t vmid; /* OUT: return vmid to VHM. Keep it first field */ uint32_t vcpu_num; /* IN: VM vcpu number */ uint8_t GUID[16]; /* IN: GUID of this vm */ - uint8_t trusty_enabled;/* IN: whether trusty is enabled */ + uint8_t secure_world_enabled;/* IN: whether Secure World is enabled */ uint8_t reserved[31]; /* Reserved for future use */ } __attribute__((aligned(8))); @@ -197,4 +197,13 @@ struct acrn_vm_pci_msix_remap { uint32_t vector_ctl; } __attribute__((aligned(8))); +/* It's designed to support passing DM config data pointer, based on it, + * hypervisor would parse then pass DM defined configration to GUEST vcpu + * when booting guest VM. + * the address 0xd0000 here is designed by DM, as it arranged all memory + * layout below 1M, DM should make sure there is no overlap for the address + * 0xd0000 usage. + */ +#define GUEST_CFG_OFFSET 0xd0000 + #endif /* ACRN_COMMON_H */ From ce09037d7a0248e1949d5856bf1c7ceac9c8388c Mon Sep 17 00:00:00 2001 From: Jason Chen CJ Date: Fri, 31 Aug 2018 10:58:58 +0800 Subject: [PATCH 0034/1103] Check x86_hyper type before doing hypercall this is to fix native boot failure issue with CONFIG_ACRN Change-Id: I735283cbf462c8b79d9742d64950685d6ae552c1 Tracked-On: Signed-off-by: Jason Chen CJ --- drivers/acrn/acrn_trace.c | 7 +++++++ drivers/char/vhm/vhm_dev.c | 7 +++++++ 2 files changed, 14 insertions(+) diff --git a/drivers/acrn/acrn_trace.c b/drivers/acrn/acrn_trace.c index 31470a3de6ac..856ab650acfd 100644 --- a/drivers/acrn/acrn_trace.c +++ b/drivers/acrn/acrn_trace.c @@ -63,6 +63,8 @@ #include #include +#include + #include "sbuf.h" @@ -211,6 +213,11 @@ static int __init acrn_trace_init(void) int ret = 0; int i, cpu; + if (x86_hyper_type != X86_HYPER_ACRN) { + pr_err("acrn_trace: not support acrn hypervisor!\n"); + return -EINVAL; + } + /* TBD: we could get the native cpu number by hypercall later */ pr_info("%s, cpu_num %d\n", __func__, nr_cpus); if (nr_cpus > MAX_NR_CPUS) { diff --git a/drivers/char/vhm/vhm_dev.c b/drivers/char/vhm/vhm_dev.c index 27844c87a41f..b724c9e7bce2 100644 --- a/drivers/char/vhm/vhm_dev.c +++ b/drivers/char/vhm/vhm_dev.c @@ -84,6 +84,8 @@ #include #include +#include + #define DEVICE_NAME "acrn_vhm" #define CLASS_NAME "vhm" @@ -590,6 +592,11 @@ static int __init vhm_init(void) pr_info("vhm: initializing\n"); + if (x86_hyper_type != X86_HYPER_ACRN) { + pr_err("vhm: not support acrn hypervisor!\n"); + return -EINVAL; + } + if (hcall_get_api_version(virt_to_phys(&api_version)) < 0) { pr_err("vhm: failed to get api version from Hypervisor !\n"); return -EINVAL; From 3206e9480e92497a7aa7994d658e1a5972f0e88c Mon Sep 17 00:00:00 2001 From: Jason Chen CJ Date: Fri, 31 Aug 2018 10:58:58 +0800 Subject: [PATCH 0035/1103] VHM: replace function name update_mmio_map with update_memmap_attr Change-Id: Ia4e4c621d4a8bc6738042cede93b9b145af291f9 Tracked-On: 212688 Signed-off-by: Jason Chen CJ --- drivers/vhm/vhm_mm.c | 4 ++-- include/linux/vhm/acrn_vhm_mm.h | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/vhm/vhm_mm.c b/drivers/vhm/vhm_mm.c index b475aa91a348..712a905040a4 100644 --- a/drivers/vhm/vhm_mm.c +++ b/drivers/vhm/vhm_mm.c @@ -191,11 +191,11 @@ int unset_mmio_map(unsigned long vmid, unsigned long guest_gpa, prot, MAP_UNMAP); } -int update_mmio_map(unsigned long vmid, unsigned long guest_gpa, +int update_memmap_attr(unsigned long vmid, unsigned long guest_gpa, unsigned long host_gpa, unsigned long len, unsigned int prot) { return _mem_set_memmap(vmid, guest_gpa, host_gpa, len, - prot, MAP_MMIO); + prot, MAP_MEM); } int map_guest_memseg(struct vhm_vm *vm, struct vm_memmap *memmap) diff --git a/include/linux/vhm/acrn_vhm_mm.h b/include/linux/vhm/acrn_vhm_mm.h index f0401ac6a942..2ff1e25b22ce 100644 --- a/include/linux/vhm/acrn_vhm_mm.h +++ b/include/linux/vhm/acrn_vhm_mm.h @@ -69,7 +69,7 @@ int set_mmio_map(unsigned long vmid, unsigned long guest_gpa, unsigned long host_gpa, unsigned long len, unsigned int prot); int unset_mmio_map(unsigned long vmid, unsigned long guest_gpa, unsigned long host_gpa, unsigned long len, unsigned int prot); -int update_mmio_map(unsigned long vmid, unsigned long guest_gpa, +int update_memmap_attr(unsigned long vmid, unsigned long guest_gpa, unsigned long host_gpa, unsigned long len, unsigned int prot); int vhm_dev_mmap(struct file *file, struct vm_area_struct *vma); From 03c32d2dcc50986ea218b71d3d37e8a2097f3453 Mon Sep 17 00:00:00 2001 From: Jason Chen CJ Date: Fri, 31 Aug 2018 10:58:58 +0800 Subject: [PATCH 0036/1103] VHM: refine memory segment interface - restruct guest_memseg & remove redundant paramters in it - restruct vm_memmap & remove redundant paramters in it - remove redundant paramters in vm_memseg Change-Id: I7661cfd464bc2748f9d5f1d0751f52782332c97a Tracked-On: 212688 Signed-off-by: Jason Chen CJ --- drivers/vhm/vhm_mm.c | 90 ++++++++++-------------------- include/linux/vhm/vhm_ioctl_defs.h | 31 +++------- 2 files changed, 37 insertions(+), 84 deletions(-) diff --git a/drivers/vhm/vhm_mm.c b/drivers/vhm/vhm_mm.c index 712a905040a4..a9ba810a7fd7 100644 --- a/drivers/vhm/vhm_mm.c +++ b/drivers/vhm/vhm_mm.c @@ -78,12 +78,9 @@ struct guest_memseg { struct list_head list; - int segid; - u64 base; + u64 vm0_gpa; size_t len; - char name[SPECNAMELEN + 1]; u64 gpa; - int prot; /* RWX */ long vma_count; }; @@ -105,10 +102,10 @@ static u64 _alloc_memblk(struct device *dev, size_t len) return 0ULL; } -static bool _free_memblk(struct device *dev, u64 base, size_t len) +static bool _free_memblk(struct device *dev, u64 vm0_gpa, size_t len) { unsigned int count = PAGE_ALIGN(len) >> PAGE_SHIFT; - struct page *page = pfn_to_page(base >> PAGE_SHIFT); + struct page *page = pfn_to_page(vm0_gpa >> PAGE_SHIFT); return dma_release_from_contiguous(dev, page, count); } @@ -116,32 +113,30 @@ static bool _free_memblk(struct device *dev, u64 base, size_t len) int alloc_guest_memseg(struct vhm_vm *vm, struct vm_memseg *memseg) { struct guest_memseg *seg; - u64 base; + u64 vm0_gpa; int max_gfn; seg = kzalloc(sizeof(struct guest_memseg), GFP_KERNEL); if (seg == NULL) return -ENOMEM; - base = _alloc_memblk(vm->dev, memseg->len); - if (base == 0ULL) { + vm0_gpa = _alloc_memblk(vm->dev, memseg->len); + if (vm0_gpa == 0ULL) { kfree(seg); return -ENOMEM; } - seg->segid = memseg->segid; - seg->base = base; + seg->vm0_gpa = vm0_gpa; seg->len = memseg->len; - strncpy(seg->name, memseg->name, SPECNAMELEN + 1); seg->gpa = memseg->gpa; max_gfn = (seg->gpa + seg->len) >> PAGE_SHIFT; if (vm->max_gfn < max_gfn) vm->max_gfn = max_gfn; - pr_info("VHM: alloc memseg[%s] with len=0x%lx, base=0x%llx," + pr_info("VHM: alloc memseg with len=0x%lx, vm0_gpa=0x%llx," " and its guest gpa = 0x%llx, vm max_gfn 0x%x\n", - seg->name, seg->len, seg->base, seg->gpa, vm->max_gfn); + seg->len, seg->vm0_gpa, seg->gpa, vm->max_gfn); seg->vma_count = 0; mutex_lock(&vm->seg_lock); @@ -201,39 +196,34 @@ int update_memmap_attr(unsigned long vmid, unsigned long guest_gpa, int map_guest_memseg(struct vhm_vm *vm, struct vm_memmap *memmap) { struct guest_memseg *seg = NULL; - struct vm_set_memmap set_memmap; + unsigned int type, prot; + unsigned long guest_gpa, host_gpa; mutex_lock(&vm->seg_lock); - if (memmap->segid != VM_MMIO) { + if (memmap->type == VM_SYSMEM) { list_for_each_entry(seg, &vm->memseg_list, list) { - if (seg->segid == memmap->segid - && seg->gpa == memmap->mem.gpa - && seg->len == memmap->mem.len) + if (seg->gpa == memmap->gpa + && seg->len == memmap->len) break; } if (&seg->list == &vm->memseg_list) { mutex_unlock(&vm->seg_lock); return -EINVAL; } - seg->prot = memmap->mem.prot; - set_memmap.type = MAP_MEM; - set_memmap.remote_gpa = seg->gpa; - set_memmap.vm0_gpa = seg->base; - set_memmap.length = seg->len; - set_memmap.prot = seg->prot; - set_memmap.prot |= MEM_ATTR_WB_CACHE; + guest_gpa = seg->gpa; + host_gpa = seg->vm0_gpa; + prot = memmap->prot | MEM_ATTR_WB_CACHE; + type = MAP_MEM; } else { - set_memmap.type = MAP_MMIO; - set_memmap.remote_gpa = memmap->mmio.gpa; - set_memmap.vm0_gpa = memmap->mmio.hpa; - set_memmap.length = memmap->mmio.len; - set_memmap.prot = memmap->mmio.prot; - set_memmap.prot |= MEM_ATTR_UNCACHED; + guest_gpa = memmap->gpa; + host_gpa = acrn_hpa2gpa(memmap->hpa); + prot = memmap->prot | MEM_ATTR_UNCACHED; + type = MAP_MMIO; } - /* hypercall to notify hv the guest EPT setting*/ - if (hcall_set_memmap(vm->vmid, virt_to_phys(&set_memmap)) < 0) { + if (_mem_set_memmap(vm->vmid, guest_gpa, host_gpa, memmap->len, + prot, type) < 0) { pr_err("vhm: failed to set memmap %ld!\n", vm->vmid); mutex_unlock(&vm->seg_lock); return -EFAULT; @@ -241,16 +231,6 @@ int map_guest_memseg(struct vhm_vm *vm, struct vm_memmap *memmap) mutex_unlock(&vm->seg_lock); - if (memmap->segid != VM_MMIO) - pr_debug("VHM: set ept for memseg [hvm_gpa=0x%llx," - "guest_gpa=0x%llx,len=0x%lx, prot=0x%x]\n", - seg->base, seg->gpa, seg->len, seg->prot); - else - pr_debug("VHM: set ept for mmio [hpa=0x%llx," - "gpa=0x%llx,len=0x%lx, prot=0x%x]\n", - memmap->mmio.hpa, memmap->mmio.gpa, - memmap->mmio.len, memmap->mmio.prot); - return 0; } @@ -262,7 +242,7 @@ void free_guest_mem(struct vhm_vm *vm) while (!list_empty(&vm->memseg_list)) { seg = list_first_entry(&vm->memseg_list, struct guest_memseg, list); - if (!_free_memblk(vm->dev, seg->base, seg->len)) + if (!_free_memblk(vm->dev, seg->vm0_gpa, seg->len)) pr_warn("failed to free memblk\n"); list_del(&seg->list); kfree(seg); @@ -276,9 +256,6 @@ int check_guest_mem(struct vhm_vm *vm) mutex_lock(&vm->seg_lock); list_for_each_entry(seg, &vm->memseg_list, list) { - if (seg->segid != VM_SYSMEM) - continue; - if (seg->vma_count == 0) continue; @@ -324,7 +301,7 @@ static int do_mmap_guest(struct file *file, unsigned long start_addr; vma->vm_flags |= VM_MIXEDMAP | VM_DONTEXPAND | VM_DONTCOPY; - pfn = seg->base >> PAGE_SHIFT; + pfn = seg->vm0_gpa >> PAGE_SHIFT; start_addr = vma->vm_start; while (size > 0) { page = pfn_to_page(pfn); @@ -338,9 +315,9 @@ static int do_mmap_guest(struct file *file, vma->vm_ops = &guest_vm_ops; vma->vm_private_data = (void *)seg; - pr_info("VHM: mmap for memseg [seg base=0x%llx, gpa=0x%llx] " + pr_info("VHM: mmap for memseg [seg vm0_gpa=0x%llx, gpa=0x%llx] " "to start addr 0x%lx\n", - seg->base, seg->gpa, start_addr); + seg->vm0_gpa, seg->gpa, start_addr); return 0; } @@ -355,9 +332,6 @@ int vhm_dev_mmap(struct file *file, struct vm_area_struct *vma) mutex_lock(&vm->seg_lock); list_for_each_entry(seg, &vm->memseg_list, list) { - if (seg->segid != VM_SYSMEM) - continue; - if (seg->gpa != offset || seg->len != len) continue; @@ -375,9 +349,6 @@ static void *do_map_guest_phys(struct vhm_vm *vm, u64 guest_phys, size_t size) mutex_lock(&vm->seg_lock); list_for_each_entry(seg, &vm->memseg_list, list) { - if (seg->segid != VM_SYSMEM) - continue; - if (seg->gpa > guest_phys || guest_phys >= seg->gpa + seg->len) continue; @@ -388,7 +359,7 @@ static void *do_map_guest_phys(struct vhm_vm *vm, u64 guest_phys, size_t size) } mutex_unlock(&vm->seg_lock); - return phys_to_virt(seg->base + guest_phys - seg->gpa); + return phys_to_virt(seg->vm0_gpa + guest_phys - seg->gpa); } mutex_unlock(&vm->seg_lock); return NULL; @@ -417,9 +388,6 @@ static int do_unmap_guest_phys(struct vhm_vm *vm, u64 guest_phys) mutex_lock(&vm->seg_lock); list_for_each_entry(seg, &vm->memseg_list, list) { - if (seg->segid != VM_SYSMEM) - continue; - if (seg->gpa <= guest_phys && guest_phys < seg->gpa + seg->len) { mutex_unlock(&vm->seg_lock); diff --git a/include/linux/vhm/vhm_ioctl_defs.h b/include/linux/vhm/vhm_ioctl_defs.h index 258ec3982da9..494213a9f9f0 100644 --- a/include/linux/vhm/vhm_ioctl_defs.h +++ b/include/linux/vhm/vhm_ioctl_defs.h @@ -82,7 +82,6 @@ #define IC_ATTACH_IOREQ_CLIENT _IC_ID(IC_ID, IC_ID_IOREQ_BASE + 0x03) #define IC_DESTROY_IOREQ_CLIENT _IC_ID(IC_ID, IC_ID_IOREQ_BASE + 0x04) - /* Guest memory management */ #define IC_ID_MEM_BASE 0x40UL #define IC_ALLOC_MEMSEG _IC_ID(IC_ID, IC_ID_MEM_BASE + 0x00) @@ -96,35 +95,21 @@ #define IC_SET_PTDEV_INTR_INFO _IC_ID(IC_ID, IC_ID_PCI_BASE + 0x03) #define IC_RESET_PTDEV_INTR_INFO _IC_ID(IC_ID, IC_ID_PCI_BASE + 0x04) -#define SPECNAMELEN 63 - -#define VM_SYSMEM 0 -#define VM_MMIO 1 - struct vm_memseg { - uint32_t segid; - uint32_t reserved; uint64_t len; uint64_t gpa; - char name[SPECNAMELEN + 1]; }; +#define VM_SYSMEM 0 +#define VM_MMIO 1 + struct vm_memmap { - uint32_t segid; /* memory segment */ + uint32_t type; uint32_t reserved; - union { - struct { - uint64_t gpa; - uint64_t len; /* mmap length */ - uint32_t prot; /* RWX */ - } mem; - struct { - uint64_t gpa; - uint64_t hpa; - uint64_t len; - uint32_t prot; - } mmio; - }; + uint64_t gpa; + uint64_t hpa; /* only for type == VM_MMIO */ + uint64_t len; /* mmap length */ + uint32_t prot; /* RWX */ }; struct ic_ptdev_irq { From 135f39842edeb86ee6baec1d27b8b8748f3044ea Mon Sep 17 00:00:00 2001 From: Hao Li Date: Thu, 25 Jan 2018 10:12:26 -0500 Subject: [PATCH 0037/1103] VBS-K: added VHM wrapper APIs This patch added 3 VHM wrapper APIs to the VBS-K framework: - long virtio_dev_register(struct virtio_dev_info *dev); - long virtio_dev_deregister(struct virtio_dev_info *dev); - int virtio_vq_index_get(struct virtio_dev_info *dev, int req_cnt); VBS-K modules could use the APIs above to register kick callback handlers to VHM. This patch also updated the reference driver with the new APIs usage. Change-Id: I6a92a36eb785d55c1a4aa09bba46c67ed5dd2194 Signed-off-by: Hao Li --- drivers/vbs/vbs.c | 121 ++++++++++++++++++++++++ drivers/vbs/vbs_rng.c | 199 ++++++++++------------------------------ include/linux/vbs/vbs.h | 16 +++- 3 files changed, 181 insertions(+), 155 deletions(-) diff --git a/drivers/vbs/vbs.c b/drivers/vbs/vbs.c index 1e7a9645a353..9d96f45b9644 100644 --- a/drivers/vbs/vbs.c +++ b/drivers/vbs/vbs.c @@ -67,6 +67,127 @@ #include #include +long virtio_dev_register(struct virtio_dev_info *dev) +{ + struct vm_info info; + int ret; + + pr_debug("vmid is %d\n", dev->_ctx.vmid); + + if (dev->dev_notify == NULL) { + pr_err("%s dev_notify empty!\n", dev->name); + goto err; + } + + /* + * dev->name is 32 chars while vhm only accepts 16 chars + * at most, so we make sure there will be a NULL + * terminator for the chars. + */ + dev->name[15] = '\0'; + dev->_ctx.vhm_client_id = + acrn_ioreq_create_client(dev->_ctx.vmid, + dev->dev_notify, + dev->name); + if (dev->_ctx.vhm_client_id < 0) { + pr_err("failed to create client of acrn ioreq!\n"); + goto err; + } + + ret = acrn_ioreq_add_iorange(dev->_ctx.vhm_client_id, + dev->io_range_type ? REQ_MMIO : REQ_PORTIO, + dev->io_range_start, + dev->io_range_start + dev->io_range_len - 1); + if (ret < 0) { + pr_err("failed to add iorange to acrn ioreq!\n"); + goto err; + } + + /* feed up max_cpu and req_buf */ + ret = vhm_get_vm_info(dev->_ctx.vmid, &info); + if (ret < 0) { + pr_err("failed in vhm_get_vm_info!\n"); + goto range_err; + } + dev->_ctx.max_vcpu = info.max_vcpu; + + dev->_ctx.req_buf = acrn_ioreq_get_reqbuf(dev->_ctx.vhm_client_id); + if (dev->_ctx.req_buf == NULL) { + pr_err("failed in acrn_ioreq_get_reqbuf!\n"); + goto range_err; + } + + acrn_ioreq_attach_client(dev->_ctx.vhm_client_id, 0); + + return 0; + +range_err: + acrn_ioreq_del_iorange(dev->_ctx.vhm_client_id, + dev->io_range_type ? REQ_MMIO : REQ_PORTIO, + dev->io_range_start, + dev->io_range_start + dev->io_range_len); + +err: + acrn_ioreq_destroy_client(dev->_ctx.vhm_client_id); + + return -EINVAL; +} + +long virtio_dev_deregister(struct virtio_dev_info *dev) +{ + acrn_ioreq_del_iorange(dev->_ctx.vhm_client_id, + dev->io_range_type ? REQ_MMIO : REQ_PORTIO, + dev->io_range_start, + dev->io_range_start + dev->io_range_len); + + acrn_ioreq_destroy_client(dev->_ctx.vhm_client_id); + + return 0; +} + +int virtio_vq_index_get(struct virtio_dev_info *dev, int req_cnt) +{ + int val = -1; + struct vhm_request *req; + int i; + + if (unlikely(req_cnt <= 0)) + return -EINVAL; + + if (dev == NULL) { + pr_err("%s: dev is NULL!\n", __func__); + return -EINVAL; + } + + for (i = 0; i < dev->_ctx.max_vcpu; i++) { + req = &dev->_ctx.req_buf[i]; + if (req->valid && req->processed == REQ_STATE_PROCESSING && + req->client == dev->_ctx.vhm_client_id) { + if (req->reqs.pio_request.direction == REQUEST_READ) { + /* currently we handle kick only, + * so read will return 0 + */ + pr_debug("%s: read request!\n", __func__); + if (dev->io_range_type == PIO_RANGE) + req->reqs.pio_request.value = 0; + else + req->reqs.mmio_request.value = 0; + } else { + pr_debug("%s: write request! type %d\n", + __func__, req->type); + if (dev->io_range_type == PIO_RANGE) + val = req->reqs.pio_request.value; + else + val = req->reqs.mmio_request.value; + } + req->processed = REQ_STATE_SUCCESS; + acrn_ioreq_complete_request(dev->_ctx.vhm_client_id, i); + } + } + + return val; +} + static long virtio_vqs_info_set(struct virtio_dev_info *dev, struct vbs_vqs_info __user *i) { diff --git a/drivers/vbs/vbs_rng.c b/drivers/vbs/vbs_rng.c index f2234e73034d..87965bafbbb3 100644 --- a/drivers/vbs/vbs_rng.c +++ b/drivers/vbs/vbs_rng.c @@ -74,8 +74,6 @@ #include #include -#include -#include #include enum { @@ -96,26 +94,14 @@ enum { struct vbs_rng { struct virtio_dev_info dev; struct virtio_vq_info vqs[VBS_K_RNG_VQ_MAX]; - int vhm_client_id; /* Below could be device specific members */ struct hwrng hwrng; -}; - -/* - * Each VBS-K module might serve multiple connections from multiple - * guests/device models/VBS-Us, so better to maintain the connections - * in a list, and here we use hashtalble as an example. - */ -struct vbs_rng_client { - struct vbs_rng *rng; - int vhm_client_id; - int max_vcpu; - struct vhm_request *req_buf; -}; - -/* instances malloced/freed by hashtable routines */ -struct vbs_rng_hash_entry { - struct vbs_rng_client *info; + /* + * Each VBS-K module might serve multiple connections + * from multiple guests/device models/VBS-Us, so better + * to maintain the connections in a list, and here we + * use hashtable as an example. + */ struct hlist_node node; }; @@ -149,30 +135,20 @@ static void vbs_rng_hash_init(void) vbs_rng_hash_initialized = 1; } -static int vbs_rng_hash_add(struct vbs_rng_client *client) +static int vbs_rng_hash_add(struct vbs_rng *entry) { - struct vbs_rng_hash_entry *entry; - if (!vbs_rng_hash_initialized) { pr_err("RNG hash table not initialized!\n"); return -1; } - entry = kmalloc(sizeof(*entry), GFP_KERNEL); - if (!entry) { - pr_err("Failed to alloc memory for rng hash entry!\n"); - return -1; - } - - entry->info = client; - - hash_add(HASH_NAME, &entry->node, entry->info->vhm_client_id); + hash_add(HASH_NAME, &entry->node, virtio_dev_client_id(&entry->dev)); return 0; } -static struct vbs_rng_client *vbs_rng_hash_find(int client_id) +static struct vbs_rng *vbs_rng_hash_find(int client_id) { - struct vbs_rng_hash_entry *entry; + struct vbs_rng *entry; int bkt; if (!vbs_rng_hash_initialized) { @@ -181,8 +157,8 @@ static struct vbs_rng_client *vbs_rng_hash_find(int client_id) } hash_for_each(HASH_NAME, bkt, entry, node) - if (entry->info->vhm_client_id == client_id) - return entry->info; + if (virtio_dev_client_id(&entry->dev) == client_id) + return entry; pr_err("Not found item matching client_id!\n"); return NULL; @@ -190,7 +166,7 @@ static struct vbs_rng_client *vbs_rng_hash_find(int client_id) static int vbs_rng_hash_del(int client_id) { - struct vbs_rng_hash_entry *entry; + struct vbs_rng *entry; int bkt; if (!vbs_rng_hash_initialized) { @@ -199,9 +175,8 @@ static int vbs_rng_hash_del(int client_id) } hash_for_each(HASH_NAME, bkt, entry, node) - if (entry->info->vhm_client_id == client_id) { + if (virtio_dev_client_id(&entry->dev) == client_id) { hash_del(&entry->node); - kfree(entry); return 0; } @@ -212,7 +187,7 @@ static int vbs_rng_hash_del(int client_id) static int vbs_rng_hash_del_all(void) { - struct vbs_rng_hash_entry *entry; + struct vbs_rng *entry; int bkt; if (!vbs_rng_hash_initialized) { @@ -221,75 +196,11 @@ static int vbs_rng_hash_del_all(void) } hash_for_each(HASH_NAME, bkt, entry, node) - if (1) { - hash_del(&entry->node); - kfree(entry); - } + hash_del(&entry->node); return 0; } -static int register_vhm_client(struct virtio_dev_info *dev) -{ - unsigned int vmid; - struct vm_info info; - struct vbs_rng_client *client; - int ret; - - client = kcalloc(1, sizeof(*client), GFP_KERNEL); - if (!client) { - pr_err("failed to malloc vbs_rng_client!\n"); - return -EINVAL; - } - - client->rng = container_of(dev, struct vbs_rng, dev); - vmid = dev->_ctx.vmid; - pr_debug("vmid is %d\n", vmid); - - client->vhm_client_id = acrn_ioreq_create_client(vmid, handle_kick, - "vbs_rng kick init\n"); - if (client->vhm_client_id < 0) { - pr_err("failed to create client of acrn ioreq!\n"); - goto err; - } - - ret = acrn_ioreq_add_iorange(client->vhm_client_id, - dev->io_range_type ? REQ_MMIO : REQ_PORTIO, - dev->io_range_start, - dev->io_range_start + dev->io_range_len); - if (ret < 0) { - pr_err("failed to add iorange to acrn ioreq!\n"); - goto err; - } - - /* feed up max_cpu and req_buf */ - ret = vhm_get_vm_info(vmid, &info); - if (ret < 0) { - pr_err("failed in vhm_get_vm_info!\n"); - goto err; - } - client->max_vcpu = info.max_vcpu; - - client->req_buf = acrn_ioreq_get_reqbuf(client->vhm_client_id); - if (client->req_buf == NULL) { - pr_err("failed in acrn_ioreq_get_reqbuf!\n"); - goto err; - } - - /* just attach once as vhm will kick kthread */ - acrn_ioreq_attach_client(client->vhm_client_id, 0); - - client->rng->vhm_client_id = client->vhm_client_id; - vbs_rng_hash_add(client); - - return 0; -err: - acrn_ioreq_destroy_client(client->vhm_client_id); - kfree(client); - - return -EINVAL; -} - static void handle_vq_kick(struct vbs_rng *rng, int vq_idx) { struct iovec iov; @@ -309,8 +220,6 @@ static void handle_vq_kick(struct vbs_rng *rng, int vq_idx) vq = &(sc->vqs[vq_idx]); - pr_debug("before vq_has_desc!\n"); - while (virtio_vq_has_descs(vq)) { virtio_vq_getchain(vq, &idx, &iov, 1, NULL); @@ -334,47 +243,25 @@ static void handle_vq_kick(struct vbs_rng *rng, int vq_idx) static int handle_kick(int client_id, int req_cnt) { int val = -1; - struct vhm_request *req; - struct vbs_rng_client *client; - int i; + struct vbs_rng *rng; if (unlikely(req_cnt <= 0)) return -EINVAL; - pr_debug("%s!\n", __func__); + pr_debug("%s: handle kick!\n", __func__); - client = vbs_rng_hash_find(client_id); - if (!client) { - pr_err("Ooops! client %d not found!\n", client_id); + rng = vbs_rng_hash_find(client_id); + if (rng == NULL) { + pr_err("%s: client %d not found!\n", + __func__, client_id); return -EINVAL; } - for (i = 0; i < client->max_vcpu; i++) { - req = &client->req_buf[i]; - if (req->valid && req->processed == REQ_STATE_PROCESSING && - req->client == client->vhm_client_id) { - if (req->reqs.pio_request.direction == REQUEST_READ) - /* currently we handle kick only, - * so read will return 0 - */ - req->reqs.pio_request.value = 0; - else - val = req->reqs.pio_request.value; - pr_debug("%s: ioreq type %d, direction %d, " - "addr 0x%lx, size 0x%lx, value 0x%x\n", - __func__, - req->type, - req->reqs.pio_request.direction, - req->reqs.pio_request.address, - req->reqs.pio_request.size, - req->reqs.pio_request.value); - req->processed = REQ_STATE_SUCCESS; - acrn_ioreq_complete_request(client->vhm_client_id, i); - } - } + val = virtio_vq_index_get(&rng->dev, req_cnt); if (val >= 0) - handle_vq_kick(client->rng, val); + handle_vq_kick(rng, val); + return 0; } @@ -385,15 +272,15 @@ static int vbs_rng_open(struct inode *inode, struct file *f) struct virtio_vq_info *vqs; int i; - pr_debug("%s!\n", __func__); - rng = kmalloc(sizeof(*rng), GFP_KERNEL); - if (!rng) { + if (rng == NULL) { pr_err("Failed to allocate memory for vbs_rng!\n"); return -ENOMEM; } dev = &rng->dev; + strncpy(dev->name, "vbs_rng", VBS_NAME_LEN); + dev->dev_notify = handle_kick; vqs = (struct virtio_vq_info *)&rng->vqs; for (i = 0; i < VBS_K_RNG_VQ_MAX; i++) { @@ -411,6 +298,8 @@ static int vbs_rng_open(struct inode *inode, struct file *f) virtio_dev_init(dev, vqs, VBS_K_RNG_VQ_MAX); f->private_data = rng; + + /* init a hash table to maintain multi-connections */ vbs_rng_hash_init(); return 0; @@ -419,14 +308,10 @@ static int vbs_rng_open(struct inode *inode, struct file *f) static int vbs_rng_release(struct inode *inode, struct file *f) { struct vbs_rng *rng = f->private_data; - struct vbs_rng_client *client; int i; - pr_debug("%s!\n", __func__); - - client = vbs_rng_hash_find(rng->vhm_client_id); - if (!client) - pr_err("%s: UNLIKELY not found client!\n", + if (!rng) + pr_err("%s: UNLIKELY rng NULL!\n", __func__); vbs_rng_stop(rng); @@ -437,16 +322,16 @@ static int vbs_rng_release(struct inode *inode, struct file *f) /* device specific release */ vbs_rng_reset(rng); - pr_debug("vbs_rng_connection cnt is %d\n", vbs_rng_connection_cnt); + pr_debug("vbs_rng_connection cnt is %d\n", + vbs_rng_connection_cnt); - if (client && vbs_rng_connection_cnt--) - vbs_rng_hash_del(client->vhm_client_id); + if (rng && vbs_rng_connection_cnt--) + vbs_rng_hash_del(virtio_dev_client_id(&rng->dev)); if (!vbs_rng_connection_cnt) { pr_debug("vbs_rng remove all hash entries\n"); vbs_rng_hash_del_all(); } - kfree(client); kfree(rng); pr_debug("%s done\n", __func__); @@ -488,7 +373,8 @@ static long vbs_rng_ioctl(struct file *f, unsigned int ioctl, * return vhost_net_set_features(n, features); */ case VBS_SET_VQ: - /* we handle this here because we want to register VHM client + /* + * we handle this here because we want to register VHM client * after handling VBS_K_SET_VQ request */ pr_debug("VBS_K_SET_VQ ioctl:\n"); @@ -498,10 +384,16 @@ static long vbs_rng_ioctl(struct file *f, unsigned int ioctl, return -EFAULT; } /* Register VHM client */ - if (register_vhm_client(&rng->dev) < 0) { + if (virtio_dev_register(&rng->dev) < 0) { pr_err("failed to register VHM client!\n"); return -EFAULT; } + /* Added to local hash table */ + if (vbs_rng_hash_add(rng) < 0) { + pr_err("failed to add to hashtable!\n"); + return -EFAULT; + } + /* Increment counter */ vbs_rng_connection_cnt++; return r; default: @@ -544,6 +436,7 @@ static void vbs_rng_stop_vq(struct vbs_rng *rng, /* device specific function */ static void vbs_rng_stop(struct vbs_rng *rng) { + virtio_dev_deregister(&rng->dev); } /* device specific function */ diff --git a/include/linux/vbs/vbs.h b/include/linux/vbs/vbs.h index 715c49156a1a..b2e185e115c8 100644 --- a/include/linux/vbs/vbs.h +++ b/include/linux/vbs/vbs.h @@ -63,6 +63,8 @@ #define _VBS_H_ #include +#include +#include /* * VBS-K device needs to handle frontend driver's kick in kernel. @@ -78,6 +80,9 @@ enum IORangeType { struct ctx { /* VHM required info */ int vmid; + int vhm_client_id; + int max_vcpu; + struct vhm_request *req_buf; }; struct virtio_desc { /* AKA vring_desc */ @@ -138,12 +143,16 @@ struct virtio_dev_info { enum IORangeType io_range_type; /* IO range type, PIO or MMIO */ /* members created in kernel space VBS */ - void (*dev_notify)(void *, struct virtio_vq_info *); - /* device-wide notification */ + int (*dev_notify)(int, int); /* device-wide notification */ struct virtio_vq_info *vqs; /* virtqueue(s) */ int curq; /* current virtqueue index */ }; +static inline int virtio_dev_client_id(struct virtio_dev_info *dev) +{ + return dev->_ctx.vhm_client_id; +} + /* VBS Runtime Control APIs */ long virtio_dev_init(struct virtio_dev_info *dev, struct virtio_vq_info *vqs, int nvq); @@ -151,5 +160,8 @@ long virtio_dev_ioctl(struct virtio_dev_info *dev, unsigned int ioctl, void __user *argp); long virtio_vqs_ioctl(struct virtio_dev_info *dev, unsigned int ioctl, void __user *argp); +long virtio_dev_register(struct virtio_dev_info *dev); +long virtio_dev_deregister(struct virtio_dev_info *dev); +int virtio_vq_index_get(struct virtio_dev_info *dev, int req_cnt); #endif From 8950900f53f0779895c6ea61ef6a3a4cecc9e355 Mon Sep 17 00:00:00 2001 From: Hao Li Date: Fri, 31 Aug 2018 10:58:58 +0800 Subject: [PATCH 0038/1103] api doc: add ACRN VBS API docs Change-Id: I634c0117392ca529d7bd4b89a02fec43a0f70d63 Tracked-On: 220254 Signed-off-by: Hao Li --- Documentation/virtual/00-INDEX | 3 + Documentation/virtual/acrn/00-INDEX | 8 ++ Documentation/virtual/acrn/conf.py | 5 + Documentation/virtual/acrn/index.rst | 17 +++ Documentation/virtual/acrn/vbs.rst | 20 +++ Documentation/virtual/acrn/vhm.rst | 5 + drivers/vbs/vbs_rng.c | 9 ++ include/linux/vbs/vbs.h | 177 ++++++++++++++++++++++----- include/linux/vbs/vq.h | 106 +++++++++++++++- 9 files changed, 311 insertions(+), 39 deletions(-) create mode 100644 Documentation/virtual/acrn/00-INDEX create mode 100644 Documentation/virtual/acrn/conf.py create mode 100644 Documentation/virtual/acrn/index.rst create mode 100644 Documentation/virtual/acrn/vbs.rst create mode 100644 Documentation/virtual/acrn/vhm.rst diff --git a/Documentation/virtual/00-INDEX b/Documentation/virtual/00-INDEX index af0d23968ee7..257aec22dbff 100644 --- a/Documentation/virtual/00-INDEX +++ b/Documentation/virtual/00-INDEX @@ -9,3 +9,6 @@ kvm/ - Kernel Virtual Machine. See also http://linux-kvm.org uml/ - User Mode Linux, builds/runs Linux kernel as a userspace program. + +acrn/ + - ACRN Project. See also http://github.com/projectacrn/ diff --git a/Documentation/virtual/acrn/00-INDEX b/Documentation/virtual/acrn/00-INDEX new file mode 100644 index 000000000000..5beb50eef9e1 --- /dev/null +++ b/Documentation/virtual/acrn/00-INDEX @@ -0,0 +1,8 @@ +00-INDEX + - this file. +index.rst + - Index. +vhm.rst + - virtio and hypervisor service module (VHM) APIs. +vbs.rst + - virtio and backend service (VBS) APIs. diff --git a/Documentation/virtual/acrn/conf.py b/Documentation/virtual/acrn/conf.py new file mode 100644 index 000000000000..ed247df22700 --- /dev/null +++ b/Documentation/virtual/acrn/conf.py @@ -0,0 +1,5 @@ +# -*- coding: utf-8; mode: python -*- + +project = "ACRN Project" + +tags.add("subproject") diff --git a/Documentation/virtual/acrn/index.rst b/Documentation/virtual/acrn/index.rst new file mode 100644 index 000000000000..3630d4fe3207 --- /dev/null +++ b/Documentation/virtual/acrn/index.rst @@ -0,0 +1,17 @@ +.. -*- coding: utf-8; mode: rst -*- + +============================= +ACRN Project +============================= + +.. toctree:: + + vbs.rst + vhm.rst + +.. only:: subproject + + Indices + ======= + + * :ref:`genindex` diff --git a/Documentation/virtual/acrn/vbs.rst b/Documentation/virtual/acrn/vbs.rst new file mode 100644 index 000000000000..40a0683a1c0b --- /dev/null +++ b/Documentation/virtual/acrn/vbs.rst @@ -0,0 +1,20 @@ +================================ +Virtio and Backend Service (VBS) +================================ + +The Virtio and Backend Service (VBS) in part of ACRN Project. + +The VBS can be further divided into two parts: VBS in user space (VBS-U) +and VBS in kernel space (VBS-K). + +Example: +-------- +A reference driver for VBS-K can be found at :c:type:`struct vbs_rng`. + +.. kernel-doc:: drivers/vbs/vbs_rng.c + +APIs: +----- + +.. kernel-doc:: include/linux/vbs/vbs.h +.. kernel-doc:: include/linux/vbs/vq.h diff --git a/Documentation/virtual/acrn/vhm.rst b/Documentation/virtual/acrn/vhm.rst new file mode 100644 index 000000000000..56d498a016b0 --- /dev/null +++ b/Documentation/virtual/acrn/vhm.rst @@ -0,0 +1,5 @@ +================================== +Virtio and Hypervisor Module (VHM) +================================== + +The Virtio and Hypervisor service Module (VHM) in part of ACRN Project. diff --git a/drivers/vbs/vbs_rng.c b/drivers/vbs/vbs_rng.c index 87965bafbbb3..2c71186801e7 100644 --- a/drivers/vbs/vbs_rng.c +++ b/drivers/vbs/vbs_rng.c @@ -91,6 +91,15 @@ enum { *}; */ +/** + * struct vbs_rng - Backend of virtio-rng based on VBS-K + * + * @dev : instance of struct virtio_dev_info + * @vqs : instances of struct virtio_vq_info + * @hwrng : device specific member + * @node : hashtable maintaining multiple connections + * from multiple guests/devices + */ struct vbs_rng { struct virtio_dev_info dev; struct virtio_vq_info vqs[VBS_K_RNG_VQ_MAX]; diff --git a/include/linux/vbs/vbs.h b/include/linux/vbs/vbs.h index b2e185e115c8..725f1626dbc6 100644 --- a/include/linux/vbs/vbs.h +++ b/include/linux/vbs/vbs.h @@ -66,19 +66,26 @@ #include #include -/* - * VBS-K device needs to handle frontend driver's kick in kernel. - * For virtio 0.9.5, the kick register is a PIO register, - * for virtio 1.0+, the kick register could be a MMIO register. +/** + * enum IORangeType - type of registers to be handled in VBS-K + * + * @PIO_RANGE : Port I/O registers, for virtio 0.9.5 + * @MMIO_RANGE : Memory-Mapped I/O registers, for virtio 1.0+ */ enum IORangeType { PIO_RANGE = 0x0, /* default */ MMIO_RANGE = 0x1, }; -/* device context */ +/** + * struct ctx - VM context this device belongs to + * + * @vmid : ID of VM this device belongs to + * @vhm_client_id : ID of VHM client this device registers + * @max_vcpu : number of VCPU in this VM + * @req_buf : request buffers + */ struct ctx { - /* VHM required info */ int vmid; int vhm_client_id; int max_vcpu; @@ -109,59 +116,163 @@ struct vring_used { struct virtio_used ring[]; /* size N */ } __attribute__((packed)); -/* struct used to maintain virtqueue info from userspace VBS */ +/** + * struct virtio_vq_info - virtqueue data structure + */ struct virtio_vq_info { /* virtqueue info from VBS-U */ - uint16_t qsize; /* size of this queue (a power of 2) */ - uint32_t pfn; /* PFN of virt queue (not shifted!) */ - uint16_t msix_idx; /* MSI-X index/VIRTIO_MSI_NO_VECTOR */ - uint64_t msix_addr; /* MSI-X address specified by index */ - uint32_t msix_data; /* MSI-X data specified by index */ + /** @qsize: size of this queue (a power of 2) */ + uint16_t qsize; + /** @pfn: PFN of virt queue (not shifted!) */ + uint32_t pfn; + /** @msix_idx: MSI-X index/VIRTIO_MSI_NO_VECTOR */ + uint16_t msix_idx; + /** @msix_addr: MSI-X address specified by index */ + uint64_t msix_addr; + /** @msix_data: MSI-X data specified by index */ + uint32_t msix_data; /* members created in kernel space VBS */ - int (*vq_notify)(int); /* vq-wide notification */ - struct virtio_dev_info *dev; /* backpointer to virtio_dev_info */ - uint16_t num; /* we're the num'th virtqueue */ - uint16_t flags; /* virtqueue flags */ - uint16_t last_avail; /* a recent value of vq_avail->va_idx */ - uint16_t save_used; /* saved vq_used->vu_idx */ - - volatile struct virtio_desc *desc; /* descriptor array */ - volatile struct vring_avail *avail; /* the "avail" ring */ - volatile struct vring_used *used; /* the "used" ring */ + /** @vq_notify: vq-wide notification */ + int (*vq_notify)(int); + /** @dev: backpointer to virtio_dev_info */ + struct virtio_dev_info *dev; + /** @num: we're the num'th virtqueue */ + uint16_t num; + /** @flags: virtqueue flags */ + uint16_t flags; + /* private: a recent value of vq_avail->va_idx */ + uint16_t last_avail; + /* private: saved vq_used->vu_idx */ + uint16_t save_used; + + /* private: descriptor array */ + volatile struct virtio_desc *desc; + /* private: the "avail" ring */ + volatile struct vring_avail *avail; + /* private: the "used" ring */ + volatile struct vring_used *used; }; -/* struct used to maintain virtio device info from userspace VBS */ +/** + * struct virtio_dev_info - VBS-K device data structure + */ struct virtio_dev_info { /* dev info from VBS */ - char name[VBS_NAME_LEN]; /* VBS device name */ - struct ctx _ctx; /* device context */ - int nvq; /* number of virtqueues */ - uint32_t negotiated_features; /* features after guest loads driver */ - uint64_t io_range_start; /* IO range start of VBS device */ - uint64_t io_range_len; /* IO range len of VBS device */ - enum IORangeType io_range_type; /* IO range type, PIO or MMIO */ + /** @name[]: VBS device name */ + char name[VBS_NAME_LEN]; + /** @_ctx: VM context this device belongs to */ + struct ctx _ctx; + /** @nvq: number of virtqueues */ + int nvq; + /** @negotiated_features: features after guest loads driver */ + uint32_t negotiated_features; + /** @io_range_start: start of an IO range VBS needs to handle */ + uint64_t io_range_start; + /** @io_range_len: len of an IO range VBS needs to handle */ + uint64_t io_range_len; + /** @io_range_type: IO range type, PIO or MMIO */ + enum IORangeType io_range_type; /* members created in kernel space VBS */ - int (*dev_notify)(int, int); /* device-wide notification */ - struct virtio_vq_info *vqs; /* virtqueue(s) */ - int curq; /* current virtqueue index */ + /** + * @dev_notify: device-wide notification + * + * This is the callback function to be registered to VHM, + * so that VBS gets notified when frontend accessed the register. + */ + int (*dev_notify)(int, int); + /** @vqs: virtqueue(s) of this device */ + struct virtio_vq_info *vqs; + /** @curq: current virtqueue index */ + int curq; }; +/** + * virtio_dev_client_id - get device's VHM client ID + * + * @dev: VBS-K device data struct + * + * Return: device's VHM client ID + */ static inline int virtio_dev_client_id(struct virtio_dev_info *dev) { return dev->_ctx.vhm_client_id; } /* VBS Runtime Control APIs */ + +/** + * virtio_dev_init - Initialize VBS-K device data structures + * + * @dev: Pointer to VBS-K device data struct + * @vqs: Pointer to VBS-K virtqueue data struct, normally in an array + * @nvq: Number of virtqueues this device has + * + * Return: 0 on success, <0 on error + */ long virtio_dev_init(struct virtio_dev_info *dev, struct virtio_vq_info *vqs, int nvq); + +/** + * virtio_dev_ioctl - VBS-K device's common ioctl routine + * + * @dev: Pointer to VBS-K device data struct + * @ioctl: Command of ioctl to device + * @argp: Data from user space + * + * Return: 0 on success, <0 on error + */ long virtio_dev_ioctl(struct virtio_dev_info *dev, unsigned int ioctl, void __user *argp); + +/** + * virtio_vqs_ioctl - VBS-K vq's common ioctl routine + * + * @dev: Pointer to VBS-K device data struct + * @ioctl: Command of ioctl to virtqueue + * @argp: Data from user space + * + * Return: 0 on success, <0 on error + */ long virtio_vqs_ioctl(struct virtio_dev_info *dev, unsigned int ioctl, void __user *argp); + +/** + * virtio_dev_register - register a VBS-K device to VHM + * + * Each VBS-K device will be registered as a VHM client, with the + * information including "kick" register location, callback, etc. + * + * @dev: Pointer to VBS-K device data struct + * + * Return: 0 on success, <0 on error + */ long virtio_dev_register(struct virtio_dev_info *dev); + +/** + * virtio_dev_register - unregister a VBS-K device from VHM + * + * Destroy the client corresponding to the VBS-K device specified. + * + * @dev: Pointer to VBS-K device data struct + * + * Return: 0 on success, <0 on error + */ long virtio_dev_deregister(struct virtio_dev_info *dev); + +/** + * virtio_vq_index_get - get virtqueue index that frontend kicks + * + * This API is normally called in the VBS-K device's callback + * function, to get value write to the "kick" register from + * frontend. + * + * @dev: Pointer to VBS-K device data struct + * @req_cnt: Number of requests need to handle, provided by VHM + * + * Return: >=0 on virtqueue index, <0 on error + */ int virtio_vq_index_get(struct virtio_dev_info *dev, int req_cnt); #endif diff --git a/include/linux/vbs/vq.h b/include/linux/vbs/vq.h index 9ebde05e4663..9e865b8dff05 100644 --- a/include/linux/vbs/vq.h +++ b/include/linux/vbs/vq.h @@ -101,7 +101,13 @@ /* Functions for dealing with generalized "virtual devices" */ #define VQ_USED_EVENT_IDX(vq) ((vq)->avail->ring[(vq)->qsize]) -/* get virtqueue size according to virtio specification */ +/** + * virtio_vq_ring_size - Calculate size of a virtqueue + * + * @qsz: size of raw data in a certain virtqueue + * + * Return: size of a certain virtqueue + */ static inline size_t virtio_vq_ring_size(unsigned int qsz) { size_t size; @@ -117,15 +123,26 @@ static inline size_t virtio_vq_ring_size(unsigned int qsz) return size; } -/* Is this ring ready for I/O? */ +/** + * virtio_vq_ring_ready - Is this ring ready for I/O? + * + * @vq: Pointer to struct virtio_vq_info + * + * Return: 0 on not ready, and 1 on ready + */ static inline int virtio_vq_ring_ready(struct virtio_vq_info *vq) { return (vq->flags & VQ_ALLOC); } -/* - * Are there "available" descriptors? (This does not count - * how many, just returns True if there are some). +/** + * virtio_vq_has_descs - Are there "available" descriptors? + * + * This does not count how many, just returns True if there is any. + * + * @vq: Pointer to struct virtio_vq_info + * + * Return: 0 on no available, and non-zero on available */ static inline int virtio_vq_has_descs(struct virtio_vq_info *vq) { @@ -133,7 +150,16 @@ static inline int virtio_vq_has_descs(struct virtio_vq_info *vq) vq->last_avail != vq->avail->idx); } -/* Deliver an interrupt to guest on the given virtual queue */ +/** + * virtio_vq_interrupt - Deliver an interrupt to guest on the given + * virtqueue. + * MSI-x or a generic MSI interrupt. + * + * @dev: Pointer to struct virtio_dev_info + * @vq: Pointer to struct virtio_vq_info + * + * Return: NULL + */ static inline void virtio_vq_interrupt(struct virtio_dev_info *dev, struct virtio_vq_info *vq) { @@ -158,15 +184,83 @@ static inline void virtio_vq_interrupt(struct virtio_dev_info *dev, /* virtqueue initialization APIs */ + +/** + * virtio_vq_init - Initialize the currently-selected virtqueue + * + * The guest just gave us a page frame number, from which we can + * calculate the addresses of the queue. After calculation, the + * addresses are updated in vq's members. + * + * @vq: Pointer to struct virtio_vq_info + * @pfn: page frame number in guest physical address space + * + * Return: NULL + */ void virtio_vq_init(struct virtio_vq_info *vq, uint32_t pfn); + +/** + * virtio_vq_reset - reset one virtqueue, make it invalid + * + * @vq: Pointer to struct virtio_vq_info + * + * Return: NULL + */ void virtio_vq_reset(struct virtio_vq_info *vq); /* virtqueue runtime APIs */ + +/** + * virtio_vq_getchain - Walk through the chain of descriptors + * involved in a request and put them into + * a given iov[] array + * + * @vq: Pointer to struct virtio_vq_info + * @pidx: Pointer to available ring position + * @iov: Pointer to iov[] array prepared by caller + * @n_iov: Size of iov[] array + * @flags: Pointer to a uint16_t array which will contain flag of + * each descriptor + * + * Return: number of descriptors + */ int virtio_vq_getchain(struct virtio_vq_info *vq, uint16_t *pidx, struct iovec *iov, int n_iov, uint16_t *flags); + +/** + * virtio_vq_retchain - Return the currently-first request chain + * back to the available ring + * + * @vq: Pointer to struct virtio_vq_info + * + * Return: NULL + */ void virtio_vq_retchain(struct virtio_vq_info *vq); + +/** + * virtio_vq_relchain - Return specified request chain to the guest, + * setting its I/O length to the provided value + * + * @vq: Pointer to struct virtio_vq_info + * @idx: Pointer to available ring position, returned by vq_getchain() + * @iolen: Number of data bytes to be returned to frontend + * + * Return: NULL + */ void virtio_vq_relchain(struct virtio_vq_info *vq, uint16_t idx, uint32_t iolen); + +/** + * virtio_vq_endchains - Driver has finished processing "available" + * chains and calling vq_relchain on each one + * + * If driver used all the available chains, used_all should be set. + * + * @vq: Pointer to struct virtio_vq_info + * @used_all_avail: Flag indicating if driver used all available chains + * + * Return: NULL + */ void virtio_vq_endchains(struct virtio_vq_info *vq, int used_all_avail); #endif From a243c06c7db35bcfd275c6e6f2d563b98aa48bd7 Mon Sep 17 00:00:00 2001 From: "Li, Fei1" Date: Fri, 31 Aug 2018 10:58:58 +0800 Subject: [PATCH 0039/1103] HVLog: reserve memory for ACRN HVLog Change-Id: Ic87c83510d1405c791ce9c47872b960f801d45c2 Tracked-On: 220304 Signed-off-by: Li, Fei1 --- drivers/acrn/Kconfig | 7 ++++ drivers/acrn/Makefile | 3 +- drivers/acrn/acrn_hvlog.c | 83 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 92 insertions(+), 1 deletion(-) create mode 100644 drivers/acrn/acrn_hvlog.c diff --git a/drivers/acrn/Kconfig b/drivers/acrn/Kconfig index 08b24a168167..9056a4f1f20a 100644 --- a/drivers/acrn/Kconfig +++ b/drivers/acrn/Kconfig @@ -11,3 +11,10 @@ config ACRN_TRACE This is the Trace driver for the Intel ACRN hypervisor. You can say y to build it into the kernel, or m to build it as a module. + +config ACRN_HVLOG + bool "Intel ACRN Hypervisor Logmsg support" + select ACRN_SHARED_BUFFER + ---help--- + This is the Trace driver for the Intel ACRN hypervisor log. + You can say y to build it into the kernel. diff --git a/drivers/acrn/Makefile b/drivers/acrn/Makefile index 5430f4fa06fd..05dd698e8171 100644 --- a/drivers/acrn/Makefile +++ b/drivers/acrn/Makefile @@ -1,2 +1,3 @@ obj-$(CONFIG_ACRN_SHARED_BUFFER) += sbuf.o -obj-$(CONFIG_ACRN_TRACE) += acrn_trace.o \ No newline at end of file +obj-$(CONFIG_ACRN_TRACE) += acrn_trace.o +obj-$(CONFIG_ACRN_HVLOG) += acrn_hvlog.o diff --git a/drivers/acrn/acrn_hvlog.c b/drivers/acrn/acrn_hvlog.c new file mode 100644 index 000000000000..9c30fba58faf --- /dev/null +++ b/drivers/acrn/acrn_hvlog.c @@ -0,0 +1,83 @@ +/* + * ACRN Hypervisor logmsg + * + * This file is provided under a dual BSD/GPLv2 license.  When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright (c) 2017 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * 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. + * + * Contact Information: Li Fei + * + * BSD LICENSE + * + * Copyright (C) 2017 Intel Corporation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 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 + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Li Fei + * + */ +#include +#include + +static unsigned long long hvlog_buf_size; +static unsigned long long hvlog_buf_base; + +static int __init early_hvlog(char *p) +{ + int ret; + + pr_debug("%s(%s)\n", __func__, p); + hvlog_buf_size = memparse(p, &p); + if (*p != '@') + return 0; + hvlog_buf_base = memparse(p + 1, &p); + + if (!!hvlog_buf_base && !!hvlog_buf_size) { + ret = memblock_reserve(hvlog_buf_base, hvlog_buf_size); + if (ret) { + pr_err("%s: Error reserving hvlog memblock\n", + __func__); + hvlog_buf_base = 0; + hvlog_buf_size = 0; + return ret; + } + } + return 0; +} +early_param("hvlog", early_hvlog); From f3cbb432bfb2f1ca73c33caf15a35c5a7de28d1a Mon Sep 17 00:00:00 2001 From: "Li, Fei1" Date: Fri, 31 Aug 2018 10:58:59 +0800 Subject: [PATCH 0040/1103] HVLog: add HVLog module Change-Id: I328bee769ea93dacf1642e4ffc142adb66356d2a Tracked-On:220304 Signed-off-by: Li, Fei1 --- drivers/acrn/acrn_hvlog.c | 349 ++++++++++++++++++++++++++++++++++++++ drivers/acrn/acrn_trace.c | 6 +- drivers/acrn/sbuf.c | 34 +++- drivers/acrn/sbuf.h | 11 +- 4 files changed, 393 insertions(+), 7 deletions(-) diff --git a/drivers/acrn/acrn_hvlog.c b/drivers/acrn/acrn_hvlog.c index 9c30fba58faf..6d27b79cb1a1 100644 --- a/drivers/acrn/acrn_hvlog.c +++ b/drivers/acrn/acrn_hvlog.c @@ -52,8 +52,39 @@ * Li Fei * */ +#define pr_fmt(fmt) "ACRN HVLog: " fmt + #include #include +#include +#include +#include +#include +#include + +#include "sbuf.h" + +#define LOG_ENTRY_SIZE 80 +#define PCPU_NRS 4 + +#define foreach_cpu(cpu, cpu_num) \ + for ((cpu) = 0; (cpu) < (cpu_num); (cpu)++) + +#define foreach_hvlog_type(idx, hvlog_type) \ + for ((idx) = 0; (idx) < (hvlog_type); (idx)++) + +enum sbuf_hvlog_index { + ACRN_CURRNET_HVLOG = 0, + ACRN_LAST_HVLOG, + ACRN_HVLOG_TYPE +}; + +struct acrn_hvlog { + struct miscdevice miscdev; + shared_buf_t *sbuf; + atomic_t open_cnt; + int pcpu_num; +}; static unsigned long long hvlog_buf_size; static unsigned long long hvlog_buf_base; @@ -78,6 +109,324 @@ static int __init early_hvlog(char *p) return ret; } } + return 0; } early_param("hvlog", early_hvlog); + + +static inline shared_buf_t *hvlog_mark_unread(shared_buf_t *sbuf) +{ + /* sbuf must point to valid data. + * clear the lowest bit in the magic to indicate that + * the sbuf point to the last boot valid data, we should + * read it later. + */ + if (sbuf != NULL) + sbuf->magic &= ~1; + + return sbuf; +} + +static int acrn_hvlog_open(struct inode *inode, struct file *filp) +{ + struct acrn_hvlog *acrn_hvlog; + + acrn_hvlog = container_of(filp->private_data, + struct acrn_hvlog, miscdev); + pr_debug("%s, %s\n", __func__, acrn_hvlog->miscdev.name); + + if (acrn_hvlog->pcpu_num >= PCPU_NRS) { + pr_err("%s, invalid pcpu_num: %d\n", + __func__, acrn_hvlog->pcpu_num); + return -EIO; + } + + /* More than one reader at the same time could get data messed up */ + if (atomic_cmpxchg(&acrn_hvlog->open_cnt, 0, 1) != 0) + return -EBUSY; + + filp->private_data = acrn_hvlog; + + return 0; +} + +static int acrn_hvlog_release(struct inode *inode, struct file *filp) +{ + struct acrn_hvlog *acrn_hvlog; + + acrn_hvlog = filp->private_data; + + pr_debug("%s, %s\n", __func__, acrn_hvlog->miscdev.name); + + if (acrn_hvlog->pcpu_num >= PCPU_NRS) { + pr_err("%s, invalid pcpu_num: %d\n", + __func__, acrn_hvlog->pcpu_num); + return -EIO; + } + + atomic_dec(&acrn_hvlog->open_cnt); + filp->private_data = NULL; + + return 0; +} + +static ssize_t acrn_hvlog_read(struct file *filp, char __user *buf, + size_t count, loff_t *offset) +{ + char data[LOG_ENTRY_SIZE]; + struct acrn_hvlog *acrn_hvlog; + int ret; + + acrn_hvlog = (struct acrn_hvlog *)filp->private_data; + + pr_debug("%s, %s\n", __func__, acrn_hvlog->miscdev.name); + + if (acrn_hvlog->pcpu_num >= PCPU_NRS) { + pr_err("%s, invalid pcpu_num: %d\n", + __func__, acrn_hvlog->pcpu_num); + return -EIO; + } + + if (acrn_hvlog->sbuf != NULL) { + ret = sbuf_get(acrn_hvlog->sbuf, (uint8_t *)&data); + if (ret > 0) { + if (copy_to_user(buf, &data, ret)) + return -EFAULT; + } + + return ret; + } + + return 0; +} + +static const struct file_operations acrn_hvlog_fops = { + .owner = THIS_MODULE, + .open = acrn_hvlog_open, + .release = acrn_hvlog_release, + .read = acrn_hvlog_read, +}; + +static struct acrn_hvlog acrn_hvlog_devs[ACRN_HVLOG_TYPE][PCPU_NRS] = { + [ACRN_CURRNET_HVLOG] = { + { + .miscdev = { + .name = "acrn_hvlog_cur_0", + .minor = MISC_DYNAMIC_MINOR, + .fops = &acrn_hvlog_fops, + }, + .pcpu_num = 0, + }, + { + .miscdev = { + .name = "acrn_hvlog_cur_1", + .minor = MISC_DYNAMIC_MINOR, + .fops = &acrn_hvlog_fops, + }, + .pcpu_num = 1, + }, + { + .miscdev = { + .name = "acrn_hvlog_cur_2", + .minor = MISC_DYNAMIC_MINOR, + .fops = &acrn_hvlog_fops, + }, + .pcpu_num = 2, + }, + { + .miscdev = { + .name = "acrn_hvlog_cur_3", + .minor = MISC_DYNAMIC_MINOR, + .fops = &acrn_hvlog_fops, + }, + .pcpu_num = 3, + }, + }, + [ACRN_LAST_HVLOG] = { + { + .miscdev = { + .name = "acrn_hvlog_last_0", + .minor = MISC_DYNAMIC_MINOR, + .fops = &acrn_hvlog_fops, + }, + .pcpu_num = 0, + }, + { + .miscdev = { + .name = "acrn_hvlog_last_1", + .minor = MISC_DYNAMIC_MINOR, + .fops = &acrn_hvlog_fops, + }, + .pcpu_num = 1, + }, + { + .miscdev = { + .name = "acrn_hvlog_last_2", + .minor = MISC_DYNAMIC_MINOR, + .fops = &acrn_hvlog_fops, + }, + .pcpu_num = 2, + }, + { + .miscdev = { + .name = "acrn_hvlog_last_3", + .minor = MISC_DYNAMIC_MINOR, + .fops = &acrn_hvlog_fops, + }, + .pcpu_num = 3, + }, + } +}; + +static int __init acrn_hvlog_init(void) +{ + int ret = 0; + int i, j, idx; + uint32_t pcpu_id; + uint64_t logbuf_base0; + uint64_t logbuf_base1; + uint64_t logbuf_size; + uint32_t ele_size; + uint32_t ele_num; + uint32_t size; + bool sbuf_constructed = false; + + shared_buf_t *sbuf0[PCPU_NRS]; + shared_buf_t *sbuf1[PCPU_NRS]; + + pr_info("%s\n", __func__); + if (!hvlog_buf_base || !hvlog_buf_size) { + pr_warn("no fixed memory reserve for hvlog.\n"); + return 0; + } + + logbuf_base0 = hvlog_buf_base; + logbuf_size = (hvlog_buf_size >> 1); + logbuf_base1 = hvlog_buf_base + logbuf_size; + + size = (logbuf_size / PCPU_NRS); + ele_size = LOG_ENTRY_SIZE; + ele_num = (size - SBUF_HEAD_SIZE) / ele_size; + + foreach_cpu(pcpu_id, PCPU_NRS) { + sbuf0[pcpu_id] = sbuf_check_valid(ele_num, ele_size, + logbuf_base0 + size * pcpu_id); + sbuf1[pcpu_id] = sbuf_check_valid(ele_num, ele_size, + logbuf_base1 + size * pcpu_id); + } + + foreach_cpu(pcpu_id, PCPU_NRS) { + if (sbuf0[pcpu_id] == NULL) + continue; + + foreach_cpu(pcpu_id, PCPU_NRS) { + acrn_hvlog_devs[ACRN_LAST_HVLOG][pcpu_id].sbuf = + hvlog_mark_unread(sbuf0[pcpu_id]); + acrn_hvlog_devs[ACRN_CURRNET_HVLOG][pcpu_id].sbuf = + sbuf_construct(ele_num, ele_size, + logbuf_base1 + size * pcpu_id); + } + sbuf_constructed = true; + } + + if (sbuf_constructed == false) { + foreach_cpu(pcpu_id, PCPU_NRS) { + if (sbuf1[pcpu_id] == NULL) + continue; + + foreach_cpu(pcpu_id, PCPU_NRS) { + acrn_hvlog_devs[ACRN_LAST_HVLOG][pcpu_id].sbuf = + hvlog_mark_unread(sbuf1[pcpu_id]); + } + } + foreach_cpu(pcpu_id, PCPU_NRS) { + acrn_hvlog_devs[ACRN_CURRNET_HVLOG][pcpu_id].sbuf = + sbuf_construct(ele_num, ele_size, + logbuf_base0 + size * pcpu_id); + } + sbuf_constructed = true; + } + + idx = ACRN_CURRNET_HVLOG; + { + foreach_cpu(pcpu_id, PCPU_NRS) { + ret = sbuf_share_setup(pcpu_id, ACRN_HVLOG, + acrn_hvlog_devs[idx][pcpu_id].sbuf); + if (ret < 0) { + pr_err("Failed to setup %s, errno %d\n", + acrn_hvlog_devs[idx][pcpu_id].miscdev.name, ret); + goto setup_err; + } + } + } + + foreach_hvlog_type(idx, ACRN_HVLOG_TYPE) { + foreach_cpu(pcpu_id, PCPU_NRS) { + atomic_set(&acrn_hvlog_devs[idx][pcpu_id].open_cnt, 0); + + ret = misc_register( + &acrn_hvlog_devs[idx][pcpu_id].miscdev); + if (ret < 0) { + pr_err("Failed to register %s, errno %d\n", + acrn_hvlog_devs[idx][pcpu_id].miscdev.name, ret); + goto reg_err; + } + } + } + + return 0; + +reg_err: + foreach_hvlog_type(i, idx) { + foreach_cpu(j, PCPU_NRS) { + misc_deregister(&acrn_hvlog_devs[i][j].miscdev); + } + } + + foreach_cpu(j, pcpu_id) { + misc_deregister(&acrn_hvlog_devs[idx][j].miscdev); + } + + pcpu_id = PCPU_NRS; +setup_err: + idx = ACRN_CURRNET_HVLOG; + { + foreach_cpu(j, pcpu_id) { + sbuf_share_setup(j, ACRN_HVLOG, 0); + sbuf_deconstruct(acrn_hvlog_devs[idx][j].sbuf); + } + } + + return ret; +} + +static void __exit acrn_hvlog_exit(void) +{ + int idx; + uint32_t pcpu_id; + + pr_info("%s\n", __func__); + + foreach_hvlog_type(idx, ACRN_HVLOG_TYPE) { + foreach_cpu(pcpu_id, PCPU_NRS) { + misc_deregister(&acrn_hvlog_devs[idx][pcpu_id].miscdev); + } + } + + idx = ACRN_CURRNET_HVLOG; + { + foreach_cpu(pcpu_id, PCPU_NRS) { + sbuf_share_setup(pcpu_id, ACRN_HVLOG, 0); + sbuf_deconstruct(acrn_hvlog_devs[idx][pcpu_id].sbuf); + } + } +} + +module_init(acrn_hvlog_init); +module_exit(acrn_hvlog_exit); + +MODULE_LICENSE("Dual BSD/GPL"); +MODULE_AUTHOR("Intel Corp., http://www.intel.com"); +MODULE_DESCRIPTION("Driver for the Intel ACRN Hypervisor Logmsg"); +MODULE_VERSION("0.1"); diff --git a/drivers/acrn/acrn_trace.c b/drivers/acrn/acrn_trace.c index 856ab650acfd..d48b03625223 100644 --- a/drivers/acrn/acrn_trace.c +++ b/drivers/acrn/acrn_trace.c @@ -239,7 +239,7 @@ static int __init acrn_trace_init(void) } foreach_cpu(cpu, pcpu_num) { - ret = sbuf_share_setup(cpu, 0, sbuf_per_cpu[cpu]); + ret = sbuf_share_setup(cpu, ACRN_TRACE, sbuf_per_cpu[cpu]); if (ret < 0) { pr_err("Failed to setup SBuf, cpuid %d\n", cpu); goto out_sbuf; @@ -264,7 +264,7 @@ static int __init acrn_trace_init(void) out_sbuf: for (i = --cpu; i >= 0; i--) - sbuf_share_setup(i, 0, NULL); + sbuf_share_setup(i, ACRN_TRACE, NULL); cpu = pcpu_num; out_free: @@ -288,7 +288,7 @@ static void __exit acrn_trace_exit(void) misc_deregister(acrn_trace_devs[cpu]); /* set sbuf pointer to NULL in HV */ - sbuf_share_setup(cpu, 0, NULL); + sbuf_share_setup(cpu, ACRN_TRACE, NULL); /* free sbuf, sbuf_per_cpu[cpu] should be set NULL */ sbuf_free(sbuf_per_cpu[cpu]); diff --git a/drivers/acrn/sbuf.c b/drivers/acrn/sbuf.c index 8849ce28a06c..a3582325d9b9 100644 --- a/drivers/acrn/sbuf.c +++ b/drivers/acrn/sbuf.c @@ -185,7 +185,7 @@ int sbuf_share_setup(uint32_t pcpu_id, uint32_t sbuf_id, shared_buf_t *sbuf) } EXPORT_SYMBOL(sbuf_share_setup); -shared_buf_t *sbuf_construct(uint32_t ele_num, uint32_t ele_size, +shared_buf_t *sbuf_check_valid(uint32_t ele_num, uint32_t ele_size, uint64_t paddr) { shared_buf_t *sbuf; @@ -199,11 +199,39 @@ shared_buf_t *sbuf_construct(uint32_t ele_num, uint32_t ele_size, if ((sbuf->magic == SBUF_MAGIC) && (sbuf->ele_num == ele_num) && (sbuf->ele_size == ele_size)) { - pr_info("construct sbuf at 0x%llx.\n", paddr); - /* return sbuf for dump */ return sbuf; } return NULL; } +EXPORT_SYMBOL(sbuf_check_valid); + +shared_buf_t *sbuf_construct(uint32_t ele_num, uint32_t ele_size, + uint64_t paddr) +{ + shared_buf_t *sbuf; + + if (!ele_num || !ele_size || !paddr) + return NULL; + + sbuf = (shared_buf_t *)phys_to_virt(paddr); + BUG_ON(!virt_addr_valid(sbuf)); + + memset(sbuf, 0, SBUF_HEAD_SIZE); + sbuf->magic = SBUF_MAGIC; + sbuf->ele_num = ele_num; + sbuf->ele_size = ele_size; + sbuf->size = ele_num * ele_size; + pr_info("construct sbuf at 0x%llx.\n", paddr); + return sbuf; +} EXPORT_SYMBOL(sbuf_construct); + +void sbuf_deconstruct(shared_buf_t *sbuf) +{ + if (sbuf == NULL) + return; + + sbuf->magic = 0; +} +EXPORT_SYMBOL(sbuf_deconstruct); diff --git a/drivers/acrn/sbuf.h b/drivers/acrn/sbuf.h index 73608c35046c..4fae7a258bce 100644 --- a/drivers/acrn/sbuf.h +++ b/drivers/acrn/sbuf.h @@ -67,6 +67,11 @@ #define OVERRUN_CNT_EN (1ULL << 0) /* whether overrun counting is enabled */ #define OVERWRITE_EN (1ULL << 1) /* whether overwrite is enabled */ +enum sbuf_type { + ACRN_TRACE, + ACRN_HVLOG, + ACRN_SBUF_TYPE_MAX, +}; /** * (sbuf) head + buf (store (ele_num - 1) elements at most) * buffer empty: tail == head @@ -115,6 +120,10 @@ shared_buf_t *sbuf_allocate(uint32_t ele_num, uint32_t ele_size); void sbuf_free(shared_buf_t *sbuf); int sbuf_get(shared_buf_t *sbuf, uint8_t *data); int sbuf_share_setup(uint32_t pcpu_id, uint32_t sbuf_id, shared_buf_t *sbuf); -shared_buf_t *sbuf_construct(uint32_t ele_num, uint32_t ele_size, uint64_t gpa); +shared_buf_t *sbuf_check_valid(uint32_t ele_num, uint32_t ele_size, + uint64_t gpa); +shared_buf_t *sbuf_construct(uint32_t ele_num, uint32_t ele_size, + uint64_t gpa); +void sbuf_deconstruct(shared_buf_t *sbuf); #endif /* SHARED_BUF_H */ From dd3b4f426727668af90cbe9ccbbda5bfb03ff25b Mon Sep 17 00:00:00 2001 From: Jason Chen CJ Date: Fri, 31 Aug 2018 10:58:59 +0800 Subject: [PATCH 0041/1103] update MEM_ATTR_WRITE_PROT with WB policy Change-Id: Icfc16c58148f8329528e27346dc2db047b7f37e2 Tracked-On: Signed-off-by: Jason Chen CJ --- include/linux/vhm/acrn_hv_defs.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/linux/vhm/acrn_hv_defs.h b/include/linux/vhm/acrn_hv_defs.h index 688d69b6f5b0..12dc3c954526 100644 --- a/include/linux/vhm/acrn_hv_defs.h +++ b/include/linux/vhm/acrn_hv_defs.h @@ -122,7 +122,7 @@ #define MEM_ATTR_WP 0x00000400 #define MEM_ATTR_ALL 0x00000007 -#define MEM_ATTR_WRITE_PROT 0x00000005 +#define MEM_ATTR_WRITE_PROT 0x00000045 #define MEM_ATTR_ALL_WB 0x00000047 #define MEM_ATTR_ALL_WC 0x00000207 From a3c61e3633371faa05fca85ea1da6af2557362a0 Mon Sep 17 00:00:00 2001 From: Mingqiang Chi Date: Fri, 31 Aug 2018 10:58:59 +0800 Subject: [PATCH 0042/1103] vhm: modify mmio/memory map/unmap api Split the parameter(prot) to two parameters(mem_type and mem_access_right) Remove the parameter(prot) in unset_mmio_map Change-Id: I9d8bf3401898d53ec2b765135601d1e4bed1e09d Tracked-On: 222796 Signed-off-by: Mingqiang Chi Reviewed-on: Reviewed-by: Dong, Eddie Tested-by: Dong, Eddie --- drivers/vhm/vhm_mm.c | 33 +++++++++++++++++++------------- include/linux/vhm/acrn_hv_defs.h | 26 ++++++++++++------------- include/linux/vhm/acrn_vhm_mm.h | 8 +++++--- 3 files changed, 37 insertions(+), 30 deletions(-) diff --git a/drivers/vhm/vhm_mm.c b/drivers/vhm/vhm_mm.c index a9ba810a7fd7..be6a47afad9a 100644 --- a/drivers/vhm/vhm_mm.c +++ b/drivers/vhm/vhm_mm.c @@ -148,7 +148,8 @@ int alloc_guest_memseg(struct vhm_vm *vm, struct vm_memseg *memseg) static int _mem_set_memmap(unsigned long vmid, unsigned long guest_gpa, unsigned long host_gpa, unsigned long len, - unsigned int prot, unsigned int type) + unsigned int mem_type, unsigned int mem_access_right, + unsigned int type) { struct vm_set_memmap set_memmap; @@ -156,7 +157,8 @@ static int _mem_set_memmap(unsigned long vmid, unsigned long guest_gpa, set_memmap.remote_gpa = guest_gpa; set_memmap.vm0_gpa = host_gpa; set_memmap.length = len; - set_memmap.prot = prot; + set_memmap.prot = ((mem_type & MEM_TYPE_MASK) | + (mem_access_right & MEM_ACCESS_RIGHT_MASK)); /* hypercall to notify hv the guest EPT setting*/ if (hcall_set_memmap(vmid, @@ -167,36 +169,39 @@ static int _mem_set_memmap(unsigned long vmid, unsigned long guest_gpa, pr_debug("VHM: set ept for mem map[type=0x%x, host_gpa=0x%lx," "guest_gpa=0x%lx,len=0x%lx, prot=0x%x]\n", - type, host_gpa, guest_gpa, len, prot); + type, host_gpa, guest_gpa, len, set_memmap.prot); return 0; } int set_mmio_map(unsigned long vmid, unsigned long guest_gpa, - unsigned long host_gpa, unsigned long len, unsigned int prot) + unsigned long host_gpa, unsigned long len, + unsigned int mem_type, unsigned mem_access_right) { return _mem_set_memmap(vmid, guest_gpa, host_gpa, len, - prot, MAP_MMIO); + mem_type, mem_access_right, MAP_MMIO); } int unset_mmio_map(unsigned long vmid, unsigned long guest_gpa, - unsigned long host_gpa, unsigned long len, unsigned int prot) + unsigned long host_gpa, unsigned long len) { return _mem_set_memmap(vmid, guest_gpa, host_gpa, len, - prot, MAP_UNMAP); + 0, 0, MAP_UNMAP); } int update_memmap_attr(unsigned long vmid, unsigned long guest_gpa, - unsigned long host_gpa, unsigned long len, unsigned int prot) + unsigned long host_gpa, unsigned long len, + unsigned int mem_type, unsigned int mem_access_right) { return _mem_set_memmap(vmid, guest_gpa, host_gpa, len, - prot, MAP_MEM); + mem_type, mem_access_right, MAP_MEM); } int map_guest_memseg(struct vhm_vm *vm, struct vm_memmap *memmap) { struct guest_memseg *seg = NULL; - unsigned int type, prot; + unsigned int type; + unsigned int mem_type, mem_access_right; unsigned long guest_gpa, host_gpa; mutex_lock(&vm->seg_lock); @@ -213,17 +218,19 @@ int map_guest_memseg(struct vhm_vm *vm, struct vm_memmap *memmap) } guest_gpa = seg->gpa; host_gpa = seg->vm0_gpa; - prot = memmap->prot | MEM_ATTR_WB_CACHE; + mem_type = MEM_TYPE_WB; + mem_access_right = (memmap->prot & MEM_ACCESS_RIGHT_MASK); type = MAP_MEM; } else { guest_gpa = memmap->gpa; host_gpa = acrn_hpa2gpa(memmap->hpa); - prot = memmap->prot | MEM_ATTR_UNCACHED; + mem_type = MEM_TYPE_UC; + mem_access_right = (memmap->prot & MEM_ACCESS_RIGHT_MASK); type = MAP_MMIO; } if (_mem_set_memmap(vm->vmid, guest_gpa, host_gpa, memmap->len, - prot, type) < 0) { + mem_type, mem_access_right, type) < 0) { pr_err("vhm: failed to set memmap %ld!\n", vm->vmid); mutex_unlock(&vm->seg_lock); return -EFAULT; diff --git a/include/linux/vhm/acrn_hv_defs.h b/include/linux/vhm/acrn_hv_defs.h index 12dc3c954526..411f197f7f3a 100644 --- a/include/linux/vhm/acrn_hv_defs.h +++ b/include/linux/vhm/acrn_hv_defs.h @@ -111,20 +111,18 @@ #define ACRN_INVALID_HPA (-1UL) /* Generic memory attributes */ -#define MEM_ATTR_READ 0x00000001 -#define MEM_ATTR_WRITE 0x00000002 -#define MEM_ATTR_EXECUTE 0x00000004 -#define MEM_ATTR_USER 0x00000008 -#define MEM_ATTR_WB_CACHE 0x00000040 -#define MEM_ATTR_WT_CACHE 0x00000080 -#define MEM_ATTR_UNCACHED 0x00000100 -#define MEM_ATTR_WC 0x00000200 -#define MEM_ATTR_WP 0x00000400 - -#define MEM_ATTR_ALL 0x00000007 -#define MEM_ATTR_WRITE_PROT 0x00000045 -#define MEM_ATTR_ALL_WB 0x00000047 -#define MEM_ATTR_ALL_WC 0x00000207 +#define MEM_ACCESS_READ 0x00000001 +#define MEM_ACCESS_WRITE 0x00000002 +#define MEM_ACCESS_EXEC 0x00000004 +#define MEM_ACCESS_RWX (MEM_ACCESS_READ | MEM_ACCESS_WRITE | \ + MEM_ACCESS_EXEC) +#define MEM_ACCESS_RIGHT_MASK 0x00000007 +#define MEM_TYPE_WB 0x00000040 +#define MEM_TYPE_WT 0x00000080 +#define MEM_TYPE_UC 0x00000100 +#define MEM_TYPE_WC 0x00000200 +#define MEM_TYPE_WP 0x00000400 +#define MEM_TYPE_MASK 0x000007C0 struct vm_set_memmap { #define MAP_MEM 0 diff --git a/include/linux/vhm/acrn_vhm_mm.h b/include/linux/vhm/acrn_vhm_mm.h index 2ff1e25b22ce..ba8558949e48 100644 --- a/include/linux/vhm/acrn_vhm_mm.h +++ b/include/linux/vhm/acrn_vhm_mm.h @@ -66,11 +66,13 @@ static inline unsigned long acrn_hpa2gpa(unsigned long hpa) void *map_guest_phys(unsigned long vmid, u64 uos_phys, size_t size); int unmap_guest_phys(unsigned long vmid, u64 uos_phys); int set_mmio_map(unsigned long vmid, unsigned long guest_gpa, - unsigned long host_gpa, unsigned long len, unsigned int prot); + unsigned long host_gpa, unsigned long len, + unsigned int mem_type, unsigned int mem_access_right); int unset_mmio_map(unsigned long vmid, unsigned long guest_gpa, - unsigned long host_gpa, unsigned long len, unsigned int prot); + unsigned long host_gpa, unsigned long len); int update_memmap_attr(unsigned long vmid, unsigned long guest_gpa, - unsigned long host_gpa, unsigned long len, unsigned int prot); + unsigned long host_gpa, unsigned long len, + unsigned int mem_type, unsigned int mem_access_right); int vhm_dev_mmap(struct file *file, struct vm_area_struct *vma); From 25c8976e8bfa05c9c512e522b7490ceec770603f Mon Sep 17 00:00:00 2001 From: Yin Fengwei Date: Fri, 31 Aug 2018 10:58:59 +0800 Subject: [PATCH 0043/1103] vhm cleanup: update one field name in vhm Change-Id: Ib125147ff72b566b183d20496251fa74244d7970 Tracked-On: 212688 Signed-off-by: Yin Fengwei Reviewed-on: Reviewed-by: Dong, Eddie Tested-by: Dong, Eddie --- include/linux/vhm/vhm_ioctl_defs.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/linux/vhm/vhm_ioctl_defs.h b/include/linux/vhm/vhm_ioctl_defs.h index 494213a9f9f0..9f2f21acbbe3 100644 --- a/include/linux/vhm/vhm_ioctl_defs.h +++ b/include/linux/vhm/vhm_ioctl_defs.h @@ -123,7 +123,7 @@ struct ic_ptdev_irq { struct { uint32_t virt_pin; /* IN: virtual IOAPIC pin */ uint32_t phys_pin; /* IN: physical IOAPIC pin */ - uint32_t pic_pin; /* IN: pin from PIC? */ + uint32_t is_pic_pin; /* IN: pin from PIC? */ } intx; struct { /* IN: vector count of MSI/MSIX, From 1b405bab16a27dff0bf035716e19391ee0bdf3cb Mon Sep 17 00:00:00 2001 From: Shiqing Gao Date: Fri, 31 Aug 2018 10:58:59 +0800 Subject: [PATCH 0044/1103] sos: add a config for VHM seperate the config for ACRN and VHM SOS has to enable both CONFIG_ACRN and CONFIG_ACRN_VHM. UOS only needs to enable CONFIG_ACRN. VHM is not used in UOS. Change-Id: I8529771e1943c18d790230533f7a4bcc84966350 Tracked-On: 224645 Signed-off-by: Shiqing Gao Reviewed-on: --- arch/x86/acrn/Kconfig | 4 ---- arch/x86/acrn/acrn.c | 2 +- drivers/Kconfig | 2 ++ drivers/Makefile | 2 +- drivers/char/Makefile | 2 +- drivers/vhm/Kconfig | 18 ++++++++++++++++++ 6 files changed, 23 insertions(+), 7 deletions(-) create mode 100644 drivers/vhm/Kconfig diff --git a/arch/x86/acrn/Kconfig b/arch/x86/acrn/Kconfig index 7788cb8cfb4b..ce0abc8cdcad 100644 --- a/arch/x86/acrn/Kconfig +++ b/arch/x86/acrn/Kconfig @@ -7,10 +7,6 @@ config ACRN bool "Enable services run on ACRN hypervisor" depends on X86_64 depends on PARAVIRT - depends on DMA_CMA - depends on PCI_MSI - depends on !INTEL_IOMMU - depends on !VMAP_STACK help This option is needed if were to run ACRN services linux on top of ACRN hypervisor. diff --git a/arch/x86/acrn/acrn.c b/arch/x86/acrn/acrn.c index 3987e2287a9f..eea9db84ca89 100644 --- a/arch/x86/acrn/acrn.c +++ b/arch/x86/acrn/acrn.c @@ -42,7 +42,7 @@ static uint32_t __init acrn_detect(void) static void __init acrn_init_platform(void) { -#ifdef CONFIG_PCI_MSI +#if defined(CONFIG_PCI_MSI) && defined(CONFIG_ACRN_VHM) pv_irq_ops.write_msi = acrn_write_msi_msg; #endif } diff --git a/drivers/Kconfig b/drivers/Kconfig index ae0772a3581a..9ecbc1173cf9 100644 --- a/drivers/Kconfig +++ b/drivers/Kconfig @@ -222,4 +222,6 @@ source "drivers/slimbus/Kconfig" source "drivers/vbs/Kconfig" source "drivers/acrn/Kconfig" + +source "drivers/vhm/Kconfig" endmenu diff --git a/drivers/Makefile b/drivers/Makefile index 22376163e35d..a4712ea26716 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -184,8 +184,8 @@ obj-$(CONFIG_FPGA) += fpga/ obj-$(CONFIG_FSI) += fsi/ obj-$(CONFIG_TEE) += tee/ obj-$(CONFIG_MULTIPLEXER) += mux/ -obj-$(CONFIG_ACRN) += vhm/ obj-$(CONFIG_ACRN) += acrn/ +obj-$(CONFIG_ACRN_VHM) += vhm/ obj-$(CONFIG_UNISYS_VISORBUS) += visorbus/ obj-$(CONFIG_SIOX) += siox/ obj-$(CONFIG_GNSS) += gnss/ diff --git a/drivers/char/Makefile b/drivers/char/Makefile index 39eccecf0d3a..6c32da6e54ad 100644 --- a/drivers/char/Makefile +++ b/drivers/char/Makefile @@ -58,4 +58,4 @@ js-rtc-y = rtc.o obj-$(CONFIG_XILLYBUS) += xillybus/ obj-$(CONFIG_POWERNV_OP_PANEL) += powernv-op-panel.o obj-$(CONFIG_ADI) += adi.o -obj-$(CONFIG_ACRN) += vhm/ +obj-$(CONFIG_ACRN_VHM) += vhm/ diff --git a/drivers/vhm/Kconfig b/drivers/vhm/Kconfig new file mode 100644 index 000000000000..0b0faeeff74a --- /dev/null +++ b/drivers/vhm/Kconfig @@ -0,0 +1,18 @@ +config ACRN_VHM + bool "Intel ACRN Hypervisor Virtio and Hypervisor service Module (VHM)" + depends on ACRN + depends on DMA_CMA + depends on PCI_MSI + depends on !INTEL_IOMMU + depends on !VMAP_STACK + default n + ---help--- + This is the Virtio and Hypervisor service Module (VHM) for + Intel ACRN hypervisor. + + It is required for Service OS. + User OS doesn't need to have this config. + + Say Y for SOS and say N for UOS. + + If unsure, say N. From d4c8cbdef734626981e364eee38fee5f4ea2c4bb Mon Sep 17 00:00:00 2001 From: Yin Fengwei Date: Fri, 31 Aug 2018 10:58:59 +0800 Subject: [PATCH 0045/1103] api doc: add vhm API docs Change-Id: If6df309ea215c1592ce41f7da724388ff1084087 Tracked-On: 220254 Signed-off-by: Yin Fengwei Reviewed-on: Signed-off-by: Yin Fengwei Reviewed-on: --- Documentation/virtual/acrn/vhm.rst | 8 ++ include/linux/vhm/acrn_vhm_ioreq.h | 106 +++++++++++++++++++++++++ include/linux/vhm/acrn_vhm_mm.h | 119 ++++++++++++++++++++++++++++- include/linux/vhm/vhm_ioctl_defs.h | 60 +++++++++++++-- include/linux/vhm/vhm_vm_mngt.h | 73 ++++++++++++++++++ 5 files changed, 358 insertions(+), 8 deletions(-) diff --git a/Documentation/virtual/acrn/vhm.rst b/Documentation/virtual/acrn/vhm.rst index 56d498a016b0..901cff492e2b 100644 --- a/Documentation/virtual/acrn/vhm.rst +++ b/Documentation/virtual/acrn/vhm.rst @@ -3,3 +3,11 @@ Virtio and Hypervisor Module (VHM) ================================== The Virtio and Hypervisor service Module (VHM) in part of ACRN Project. + +APIs: +----- + +.. kernel-doc:: include/linux/vhm/acrn_vhm_ioreq.h +.. kernel-doc:: include/linux/vhm/acrn_vhm_mm.h +.. kernel-doc:: include/linux/vhm/vhm_ioctl_defs.h +.. kernel-doc:: include/linux/vhm/vhm_vm_mngt.h diff --git a/include/linux/vhm/acrn_vhm_ioreq.h b/include/linux/vhm/acrn_vhm_ioreq.h index fcec2c1e2eac..de3a8aa4eaf6 100644 --- a/include/linux/vhm/acrn_vhm_ioreq.h +++ b/include/linux/vhm/acrn_vhm_ioreq.h @@ -51,6 +51,12 @@ * */ +/** + * @file acrn_vhm_ioreq.h + * + * @brief Virtio and Hypervisor Module(VHM) ioreq APIs + */ + #ifndef __ACRN_VHM_IOREQ_H__ #define __ACRN_VHM_IOREQ_H__ @@ -59,22 +65,122 @@ typedef int (*ioreq_handler_t)(int client_id, int req); +/** + * acrn_ioreq_create_client - create ioreq client + * + * @vmid: ID to identify guest + * @handler: ioreq_handler of ioreq client + * If client want request handled in client thread context, set + * this parameter to NULL. If client want request handled out of + * client thread context, set handler function pointer of its own. + * VHM will create kernel thread and call handler to handle request + * + * @name: the name of ioreq client + * + * Return: client id on success, <0 on error + */ int acrn_ioreq_create_client(unsigned long vmid, ioreq_handler_t handler, char *name); + +/** + * acrn_ioreq_destroy_client - destroy ioreq client + * + * @client_id: client id to identify ioreq client + * + * Return: + */ void acrn_ioreq_destroy_client(int client_id); +/** + * acrn_ioreq_add_iorange - add iorange monitored by ioreq client + * + * @client_id: client id to identify ioreq client + * @type: iorange type + * @start: iorange start address + * @end: iorange end address + * + * Return: 0 on success, <0 on error + */ int acrn_ioreq_add_iorange(int client_id, uint32_t type, long start, long end); + +/** + * acrn_ioreq_del_iorange - del iorange monitored by ioreq client + * + * @client_id: client id to identify ioreq client + * @type: iorange type + * @start: iorange start address + * @end: iorange end address + * + * Return: 0 on success, <0 on error + */ int acrn_ioreq_del_iorange(int client_id, uint32_t type, long start, long end); +/** + * acrn_ioreq_get_reqbuf - get request buffer + * request buffer is shared by all clients in one guest + * + * @client_id: client id to identify ioreq client + * + * Return: pointer to request buffer, NULL on error + */ struct vhm_request *acrn_ioreq_get_reqbuf(int client_id); + +/** + * acrn_ioreq_attach_client - start handle request for ioreq client + * If request is handled out of client thread context, this function is + * only called once to be ready to handle new request. + * + * If request is handled in client thread context, this function must + * be called every time after the previous request handling is completed + * to be ready to handle new request. + * + * @client_id: client id to identify ioreq client + * @check_kthread_stop: whether check current kthread should be stopped + * + * Return: 0 on success, <0 on error, 1 if ioreq client is destroying + */ int acrn_ioreq_attach_client(int client_id, bool check_kthread_stop); +/** + * acrn_ioreq_distribute_request - deliver request to corresponding client + * + * @vm: pointer to guest + * + * Return: 0 always + */ int acrn_ioreq_distribute_request(struct vhm_vm *vm); + +/** + * acrn_ioreq_complete_request - notify guest request handling is completed + * + * @client_id: client id to identify ioreq client + * @vcpu: identify request submitter + * + * Return: 0 on success, <0 on error + */ int acrn_ioreq_complete_request(int client_id, uint64_t vcpu); +/** + * acrn_ioreq_intercept_bdf - set intercept bdf info of ioreq client + * + * @client_id: client id to identify ioreq client + * @bus: bus number + * @dev: device number + * @func: function number + * + * Return: + */ void acrn_ioreq_intercept_bdf(int client_id, int bus, int dev, int func); + +/** + * acrn_ioreq_unintercept_bdf - clear intercept bdf info of ioreq client + * + * @client_id: client id to identify ioreq client + * + * Return: + */ void acrn_ioreq_unintercept_bdf(int client_id); /* IOReq APIs */ diff --git a/include/linux/vhm/acrn_vhm_mm.h b/include/linux/vhm/acrn_vhm_mm.h index ba8558949e48..ba383b354986 100644 --- a/include/linux/vhm/acrn_vhm_mm.h +++ b/include/linux/vhm/acrn_vhm_mm.h @@ -51,25 +51,115 @@ * */ +/** + * @file acrn_vhm_mm.h + * + * @brief Virtio and Hypervisor Module memory manager APIs + */ + #ifndef __ACRN_VHM_MM_H__ #define __ACRN_VHM_MM_H__ #include #include -/* 1:1 mapping for service OS */ +/** + * acrn_hpa2gpa - physical address conversion + * + * convert host physical address (hpa) to guest physical address (gpa) + * gpa and hpa is 1:1 mapping for service OS + * + * @hpa: host physical address + * + * Return: guest physical address + */ static inline unsigned long acrn_hpa2gpa(unsigned long hpa) { return hpa; } +/** + * map_guest_phys - map guest physical address + * + * to SOS kernel virtual address + * + * @vmid: guest vmid + * @uos_phy: phsical address in guest + * @size: the memory size mapped + * + * Return: SOS kernel virtual address, NULL on error + */ void *map_guest_phys(unsigned long vmid, u64 uos_phys, size_t size); + +/** + * unmap_guest_phys - unmap guest physical address + * + * @vmid: guest vmid + * @uos_phy: phsical address in guest + * + * Return: 0 on success, <0 for error. + */ int unmap_guest_phys(unsigned long vmid, u64 uos_phys); + +/** + * set_mmio_map - map mmio EPT mapping between UOS gpa and SOS gpa + * + * @vmid: guest vmid + * @guest_gpa: gpa of UOS + * @host_gpa: gpa of SOS + * @len: memory mapped length + * @mem_type: memory mapping type. Possilble value could be: + * MEM_TYPE_WB + * MEM_TYPE_WT + * MEM_TYPE_UC + * MEM_TYPE_WC + * MEM_TYPE_WP + * @mem_access_right: memory mapping access. Possible value could be: + * MEM_ACCESS_READ + * MEM_ACCESS_WRITE + * MEM_ACCESS_EXEC + * MEM_ACCESS_RWX + * + * Return: 0 on success, <0 for error. + */ int set_mmio_map(unsigned long vmid, unsigned long guest_gpa, unsigned long host_gpa, unsigned long len, unsigned int mem_type, unsigned int mem_access_right); + +/** + * unset_mmio_map - unmap mmio mapping between UOS gpa and SOS gpa + * + * @vmid: guest vmid + * @guest_gpa: gpa of UOS + * @host_gpa: gpa of SOS + * @len: memory mapped length + * + * Return: 0 on success, <0 for error. + */ int unset_mmio_map(unsigned long vmid, unsigned long guest_gpa, unsigned long host_gpa, unsigned long len); + +/** + * update_memmap_attr - update mmio EPT mapping between UOS gpa and SOS gpa + * + * @vmid: guest vmid + * @guest_gpa: gpa of UOS + * @host_gpa: gpa of SOS + * @len: memory mapped length + * @mem_type: memory mapping type. Possilble value could be: + * MEM_TYPE_WB + * MEM_TYPE_WT + * MEM_TYPE_UC + * MEM_TYPE_WC + * MEM_TYPE_WP + * @mem_access_right: memory mapping access. Possible value could be: + * MEM_ACCESS_READ + * MEM_ACCESS_WRITE + * MEM_ACCESS_EXEC + * MEM_ACCESS_RWX + * + * Return: 0 on success, <0 for error. + */ int update_memmap_attr(unsigned long vmid, unsigned long guest_gpa, unsigned long host_gpa, unsigned long len, unsigned int mem_type, unsigned int mem_access_right); @@ -77,9 +167,36 @@ int update_memmap_attr(unsigned long vmid, unsigned long guest_gpa, int vhm_dev_mmap(struct file *file, struct vm_area_struct *vma); int check_guest_mem(struct vhm_vm *vm); + +/** + * free_guest_mem - free memory of guest + * + * @vm: pointer to guest vm + * + * Return: + */ void free_guest_mem(struct vhm_vm *vm); +/** + * alloc_guest_memseg - alloc memory of guest according to pre-defined + * memory segment info + * + * @vm: pointer to guest vm + * @memseg: pointer to guest memory segment info + * + * Return: + */ int alloc_guest_memseg(struct vhm_vm *vm, struct vm_memseg *memseg); + +/** + * map_guest_memseg - map EPT mmapping of memory of guest according to + * pre-defined memory mapping info + * + * @vm: pointer to guest vm + * @memmap: pointer to guest memory mapping info + * + * Return: + */ int map_guest_memseg(struct vhm_vm *vm, struct vm_memmap *memmap); #endif diff --git a/include/linux/vhm/vhm_ioctl_defs.h b/include/linux/vhm/vhm_ioctl_defs.h index 9f2f21acbbe3..5bc7c666f2ea 100644 --- a/include/linux/vhm/vhm_ioctl_defs.h +++ b/include/linux/vhm/vhm_ioctl_defs.h @@ -43,6 +43,12 @@ * $FreeBSD$ */ +/** + * @file vhm_ioctl_defs.h + * + * @brief Virtio and Hypervisor Module definition for ioctl to user space + */ + #ifndef _VHM_IOCTL_DEFS_H_ #define _VHM_IOCTL_DEFS_H_ @@ -95,6 +101,12 @@ #define IC_SET_PTDEV_INTR_INFO _IC_ID(IC_ID, IC_ID_PCI_BASE + 0x03) #define IC_RESET_PTDEV_INTR_INFO _IC_ID(IC_ID, IC_ID_PCI_BASE + 0x04) +/** + * struct vm_memseg - memory segment info for guest + * + * @len: length of memory segment + * @gpa: guest physical start address of memory segment + */ struct vm_memseg { uint64_t len; uint64_t gpa; @@ -103,6 +115,15 @@ struct vm_memseg { #define VM_SYSMEM 0 #define VM_MMIO 1 +/** + * struct vm_memmap - EPT memory mapping info for guest + * + * @type: memory mapping type + * @gpa: guest physical start address of memory mapping + * @hpa: host physical start address of memory + * @len: the length of memory range mapped + * @prot: memory mapping attribute + */ struct vm_memmap { uint32_t type; uint32_t reserved; @@ -112,38 +133,63 @@ struct vm_memmap { uint32_t prot; /* RWX */ }; +/** + * struct ic_ptdev_irq - pass thru device irq data structure + */ struct ic_ptdev_irq { #define IRQ_INTX 0 #define IRQ_MSI 1 #define IRQ_MSIX 2 + /** @type: irq type */ uint32_t type; + /** @virt_bdf: virtual bdf description of pass thru device */ uint16_t virt_bdf; /* IN: Device virtual BDF# */ + /** @phy_bdf: physical bdf description of pass thru device */ uint16_t phys_bdf; /* IN: Device physical BDF# */ + /** union */ union { + /** struct intx - info of IOAPIC/PIC interrupt */ struct { - uint32_t virt_pin; /* IN: virtual IOAPIC pin */ - uint32_t phys_pin; /* IN: physical IOAPIC pin */ - uint32_t is_pic_pin; /* IN: pin from PIC? */ + /** @virt_pin: virtual IOAPIC pin */ + uint32_t virt_pin; + /** @phys_pin: physical IOAPIC pin */ + uint32_t phys_pin; + /** @pic_pin: PIC pin */ + uint32_t is_pic_pin; } intx; + + /** struct msix - info of MSI/MSIX interrupt */ struct { - /* IN: vector count of MSI/MSIX, - * Keep this filed on top of msix */ + /* Keep this filed on top of msix */ + /** @vector_cnt: vector count of MSI/MSIX */ uint32_t vector_cnt; - /* IN: size of MSI-X table (round up to 4K) */ + /** @table_size: size of MSIX table(round up to 4K) */ uint32_t table_size; - /* IN: physical address of MSI-X table */ + /** @table_paddr: physical address of MSIX table */ uint64_t table_paddr; } msix; }; }; +/** + * struct ioreq_notify - data strcture to notify hypervisor ioreq is handled + * + * @client_id: client id to identify ioreq client + * @vcpu: identify the ioreq submitter + */ struct ioreq_notify { int32_t client_id; uint32_t vcpu; }; +/** + * struct api_version - data structure to track VHM API version + * + * @major_version: major version of VHM API + * @minor_version: minor version of VHM API + */ struct api_version { uint32_t major_version; uint32_t minor_version; diff --git a/include/linux/vhm/vhm_vm_mngt.h b/include/linux/vhm/vhm_vm_mngt.h index 5edacb31dc1b..e7bc8b2372f7 100644 --- a/include/linux/vhm/vhm_vm_mngt.h +++ b/include/linux/vhm/vhm_vm_mngt.h @@ -53,6 +53,12 @@ * Jason Chen CJ * */ + +/** + * @file vhm_vm_mngt.h + * + * @brief Virtio and Hypervisor Module(VHM) management APIs + */ #ifndef VHM_VM_MNGT_H #define VHM_VM_MNGT_H @@ -61,6 +67,22 @@ extern struct list_head vhm_vm_list; extern struct mutex vhm_vm_list_lock; +/** + * struct vhm_vm - data structure to track guest + * + * @dev: pointer to dev of linux device mode + * @list: list of vhm_vm + * @vmid: guest vmid + * @ioreq_fallback_client: default ioreq client + * @refcnt: reference count of guest + * @seg_lock: mutex to protect memseg_list + * @memseg_list: list of memseg + * @max_gfn: maximum guest page frame number + * @ioreq_client_lock: spinlock to protect ioreq_client_list + * @ioreq_client_list: list of ioreq clients + * @req_buf: request buffer shared between HV, SOS and UOS + * @pg: pointer to linux page which holds req_buf + */ struct vhm_vm { struct device *dev; struct list_head list; @@ -76,16 +98,67 @@ struct vhm_vm { struct page *pg; }; +/** + * struct vm_info - data structure to track guest info + * + * @max_vcpu: maximum vcpu number of guest + * @max_gfn: maximum guest page frame number + */ struct vm_info { int max_vcpu; int max_gfn; }; +/** + * struct find_get_vm - find and hold vhm_vm of guest according to guest vmid + * + * @vmid: guest vmid + * + * Return: pointer to vhm_vm, NULL if can't find vm matching vmid + */ struct vhm_vm *find_get_vm(unsigned long vmid); + +/** + * struct put_vm - release vhm_vm of guest according to guest vmid + * If the latest reference count drops to zero, free vhm_vm as well + * + * @vm: pointer to vhm_vm which identrify specific guest + * + * Return: + */ void put_vm(struct vhm_vm *vm); + +/** + * struct vhm_get_vm_info - get vm_info of specific guest + * + * @vmid: guest vmid + * @info: pointer to vm_info for returned vm_info + * + * Return: 0 on success, <0 on error + */ int vhm_get_vm_info(unsigned long vmid, struct vm_info *info); + +/** + * struct vhm_inject_msi - inject MSI interrupt to guest + * + * @vmid: guest vmid + * @msi_addr: MSI addr matches MSI spec + * @msi_data: MSI data matches MSI spec + * + * Return: 0 on success, <0 on error + */ int vhm_inject_msi(unsigned long vmid, unsigned long msi_addr, unsigned long msi_data); + +/** + * struct vhm_vm_gpa2hpa - convert guest physical address to + * host physical address + * + * @vmid: guest vmid + * @gap: guest physical address + * + * Return: host physical address, <0 on error + */ unsigned long vhm_vm_gpa2hpa(unsigned long vmid, unsigned long gpa); void vm_list_add(struct list_head *list); From 32974eb3d76a629b7c47f98bf8380c9fc4a74ccb Mon Sep 17 00:00:00 2001 From: Hao Li Date: Fri, 31 Aug 2018 10:58:59 +0800 Subject: [PATCH 0046/1103] api doc: update ACRN VBS API docs Change-Id: I9e56502a114019297ac04d7e8a2a07230e7adcfe Tracked-On: 220254 Signed-off-by: Hao Li Reviewed-on: --- include/linux/vbs/vq.h | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/include/linux/vbs/vq.h b/include/linux/vbs/vq.h index 9e865b8dff05..647466567db4 100644 --- a/include/linux/vbs/vq.h +++ b/include/linux/vbs/vq.h @@ -106,7 +106,7 @@ * * @qsz: size of raw data in a certain virtqueue * - * Return: size of a certain virtqueue + * Return: size of a certain virtqueue, in bytes */ static inline size_t virtio_vq_ring_size(unsigned int qsz) { @@ -158,7 +158,7 @@ static inline int virtio_vq_has_descs(struct virtio_vq_info *vq) * @dev: Pointer to struct virtio_dev_info * @vq: Pointer to struct virtio_vq_info * - * Return: NULL + * Return: N/A */ static inline void virtio_vq_interrupt(struct virtio_dev_info *dev, struct virtio_vq_info *vq) @@ -195,7 +195,7 @@ static inline void virtio_vq_interrupt(struct virtio_dev_info *dev, * @vq: Pointer to struct virtio_vq_info * @pfn: page frame number in guest physical address space * - * Return: NULL + * Return: N/A */ void virtio_vq_init(struct virtio_vq_info *vq, uint32_t pfn); @@ -204,7 +204,7 @@ void virtio_vq_init(struct virtio_vq_info *vq, uint32_t pfn); * * @vq: Pointer to struct virtio_vq_info * - * Return: NULL + * Return: N/A */ void virtio_vq_reset(struct virtio_vq_info *vq); @@ -233,7 +233,7 @@ int virtio_vq_getchain(struct virtio_vq_info *vq, uint16_t *pidx, * * @vq: Pointer to struct virtio_vq_info * - * Return: NULL + * Return: N/A */ void virtio_vq_retchain(struct virtio_vq_info *vq); @@ -245,7 +245,7 @@ void virtio_vq_retchain(struct virtio_vq_info *vq); * @idx: Pointer to available ring position, returned by vq_getchain() * @iolen: Number of data bytes to be returned to frontend * - * Return: NULL + * Return: N/A */ void virtio_vq_relchain(struct virtio_vq_info *vq, uint16_t idx, uint32_t iolen); @@ -259,7 +259,7 @@ void virtio_vq_relchain(struct virtio_vq_info *vq, uint16_t idx, * @vq: Pointer to struct virtio_vq_info * @used_all_avail: Flag indicating if driver used all available chains * - * Return: NULL + * Return: N/A */ void virtio_vq_endchains(struct virtio_vq_info *vq, int used_all_avail); From 5c975a4295034536040be2557af8a81981814610 Mon Sep 17 00:00:00 2001 From: Hao Li Date: Fri, 31 Aug 2018 10:58:59 +0800 Subject: [PATCH 0047/1103] license: update intel license for ACRN VBS Change-Id: I9afb8d0b45075051e59307b41f6754ebabfe6808 Tracked-On: 219330 Signed-off-by: Hao Li Reviewed-on: --- drivers/vbs/vbs.c | 4 ++-- drivers/vbs/vbs_rng.c | 4 ++-- drivers/vbs/vq.c | 4 ++-- include/linux/vbs/vbs.h | 4 ++-- include/linux/vbs/vbs_common_if.h | 4 ++-- include/linux/vbs/vq.h | 4 ++-- 6 files changed, 12 insertions(+), 12 deletions(-) diff --git a/drivers/vbs/vbs.c b/drivers/vbs/vbs.c index 9d96f45b9644..6c364364db3c 100644 --- a/drivers/vbs/vbs.c +++ b/drivers/vbs/vbs.c @@ -7,7 +7,7 @@ * * GPL LICENSE SUMMARY * - * Copyright (c) 2017 Intel Corporation. All rights reserved. + * Copyright (c) 2018 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of version 2 of the GNU General Public License as @@ -22,7 +22,7 @@ * * BSD LICENSE * - * Copyright (c) 2017 Intel Corporation. All rights reserved. + * Copyright (c) 2018 Intel Corporation. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions diff --git a/drivers/vbs/vbs_rng.c b/drivers/vbs/vbs_rng.c index 2c71186801e7..cdfed63bb1ad 100644 --- a/drivers/vbs/vbs_rng.c +++ b/drivers/vbs/vbs_rng.c @@ -7,7 +7,7 @@ * * GPL LICENSE SUMMARY * - * Copyright (c) 2017 Intel Corporation. All rights reserved. + * Copyright (c) 2018 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of version 2 of the GNU General Public License as @@ -22,7 +22,7 @@ * * BSD LICENSE * - * Copyright (c) 2017 Intel Corporation. All rights reserved. + * Copyright (c) 2018 Intel Corporation. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions diff --git a/drivers/vbs/vq.c b/drivers/vbs/vq.c index 886f48225de9..9f7a829c7a67 100644 --- a/drivers/vbs/vq.c +++ b/drivers/vbs/vq.c @@ -7,7 +7,7 @@ * * GPL LICENSE SUMMARY * - * Copyright (c) 2017 Intel Corporation. All rights reserved. + * Copyright (c) 2018 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of version 2 of the GNU General Public License as @@ -22,7 +22,7 @@ * * BSD LICENSE * - * Copyright (c) 2017 Intel Corporation. All rights reserved. + * Copyright (c) 2018 Intel Corporation. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions diff --git a/include/linux/vbs/vbs.h b/include/linux/vbs/vbs.h index 725f1626dbc6..e4ecba020b08 100644 --- a/include/linux/vbs/vbs.h +++ b/include/linux/vbs/vbs.h @@ -7,7 +7,7 @@ * * GPL LICENSE SUMMARY * - * Copyright (c) 2017 Intel Corporation. All rights reserved. + * Copyright (c) 2018 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of version 2 of the GNU General Public License as @@ -22,7 +22,7 @@ * * BSD LICENSE * - * Copyright (c) 2017 Intel Corporation. All rights reserved. + * Copyright (c) 2018 Intel Corporation. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions diff --git a/include/linux/vbs/vbs_common_if.h b/include/linux/vbs/vbs_common_if.h index 91eb4e01d95c..25d6002c92bb 100644 --- a/include/linux/vbs/vbs_common_if.h +++ b/include/linux/vbs/vbs_common_if.h @@ -7,7 +7,7 @@ * * GPL LICENSE SUMMARY * - * Copyright (c) 2017 Intel Corporation. All rights reserved. + * Copyright (c) 2018 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of version 2 of the GNU General Public License as @@ -22,7 +22,7 @@ * * BSD LICENSE * - * Copyright (c) 2017 Intel Corporation. All rights reserved. + * Copyright (c) 2018 Intel Corporation. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions diff --git a/include/linux/vbs/vq.h b/include/linux/vbs/vq.h index 647466567db4..ca14f8f34888 100644 --- a/include/linux/vbs/vq.h +++ b/include/linux/vbs/vq.h @@ -7,7 +7,7 @@ * * GPL LICENSE SUMMARY * - * Copyright (c) 2017 Intel Corporation. All rights reserved. + * Copyright (c) 2018 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of version 2 of the GNU General Public License as @@ -22,7 +22,7 @@ * * BSD LICENSE * - * Copyright (c) 2017 Intel Corporation. All rights reserved. + * Copyright (c) 2018 Intel Corporation. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions From 1b3aa7f99a9951d58db034637214a280e0d11c0f Mon Sep 17 00:00:00 2001 From: Hao Li Date: Fri, 31 Aug 2018 10:58:59 +0800 Subject: [PATCH 0048/1103] VBS-K: fix compilation warnings in VBS-K reference driver Change-Id: I0e67e625145082d7e2a9a44f744de015855aa7ce Tracked-On: 218445 Signed-off-by: Hao Li Reviewed-on: --- drivers/vbs/vbs_rng.c | 50 +++++++++++++++++++++++-------------------- 1 file changed, 27 insertions(+), 23 deletions(-) diff --git a/drivers/vbs/vbs_rng.c b/drivers/vbs/vbs_rng.c index cdfed63bb1ad..88f6108ca2e8 100644 --- a/drivers/vbs/vbs_rng.c +++ b/drivers/vbs/vbs_rng.c @@ -124,15 +124,17 @@ static int vbs_rng_connection_cnt = 0; /* function declarations */ static int handle_kick(int client_id, int req_cnt); static void vbs_rng_reset(struct vbs_rng *rng); -static void vbs_rng_disable_vq(struct vbs_rng *rng, - struct virtio_vq_info *vq); +static void vbs_rng_stop(struct vbs_rng *rng); +static void vbs_rng_flush(struct vbs_rng *rng); +#ifdef RUNTIME_CTRL static int vbs_rng_enable_vq(struct vbs_rng *rng, struct virtio_vq_info *vq); +static void vbs_rng_disable_vq(struct vbs_rng *rng, + struct virtio_vq_info *vq); static void vbs_rng_stop_vq(struct vbs_rng *rng, struct virtio_vq_info *vq); -static void vbs_rng_stop(struct vbs_rng *rng); static void vbs_rng_flush_vq(struct vbs_rng *rng, int index); -static void vbs_rng_flush(struct vbs_rng *rng); +#endif /* hash table related functions */ static void vbs_rng_hash_init(void) @@ -347,17 +349,6 @@ static int vbs_rng_release(struct inode *inode, struct file *f) return 0; } -static struct hwrng get_hwrng(struct vbs_rng *rng) -{ - return rng->hwrng; -} - -/* Set feature bits in kernel side device */ -static int vbs_rng_set_features(struct vbs_rng *rng, u64 features) -{ - return 0; -} - static long vbs_rng_ioctl(struct file *f, unsigned int ioctl, unsigned long arg) { @@ -424,11 +415,17 @@ static void vbs_rng_reset(struct vbs_rng *rng) } /* device specific function */ -static void vbs_rng_disable_vq(struct vbs_rng *rng, - struct virtio_vq_info *vq) +static void vbs_rng_stop(struct vbs_rng *rng) +{ + virtio_dev_deregister(&rng->dev); +} + +/* device specific function */ +static void vbs_rng_flush(struct vbs_rng *rng) { } +#ifdef RUNTIME_CTRL /* device specific function */ static int vbs_rng_enable_vq(struct vbs_rng *rng, struct virtio_vq_info *vq) @@ -437,15 +434,15 @@ static int vbs_rng_enable_vq(struct vbs_rng *rng, } /* device specific function */ -static void vbs_rng_stop_vq(struct vbs_rng *rng, - struct virtio_vq_info *vq) +static void vbs_rng_disable_vq(struct vbs_rng *rng, + struct virtio_vq_info *vq) { } /* device specific function */ -static void vbs_rng_stop(struct vbs_rng *rng) +static void vbs_rng_stop_vq(struct vbs_rng *rng, + struct virtio_vq_info *vq) { - virtio_dev_deregister(&rng->dev); } /* device specific function */ @@ -453,10 +450,17 @@ static void vbs_rng_flush_vq(struct vbs_rng *rng, int index) { } -/* device specific function */ -static void vbs_rng_flush(struct vbs_rng *rng) +static struct hwrng get_hwrng(struct vbs_rng *rng) { + return rng->hwrng; +} + +/* Set feature bits in kernel side device */ +static int vbs_rng_set_features(struct vbs_rng *rng, u64 features) +{ + return 0; } +#endif static const struct file_operations vbs_rng_fops = { .owner = THIS_MODULE, From 8f20ee7ed3df71407f29bb14bb74cc9ec6c7d508 Mon Sep 17 00:00:00 2001 From: "Yan, Like" Date: Fri, 31 Aug 2018 10:58:59 +0800 Subject: [PATCH 0049/1103] Cleanup Kconfig Change-Id: I219bc9343fe47a1cdab70c247370beb9e425fcd4 Signed-off-by: Yan, Like --- drivers/acrn/Kconfig | 5 +++-- drivers/vbs/Kconfig | 4 ++-- drivers/vhm/Kconfig | 1 - 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/acrn/Kconfig b/drivers/acrn/Kconfig index 9056a4f1f20a..9fc4cae04a56 100644 --- a/drivers/acrn/Kconfig +++ b/drivers/acrn/Kconfig @@ -1,12 +1,13 @@ config ACRN_SHARED_BUFFER bool "Intel ACRN SHARED BUFFER" + depends on ACRN_VHM ---help--- Ring buffer shared between ACRN Hypervisor and its SOS. Help ACRN performance profiling. config ACRN_TRACE tristate "Intel ACRN Hypervisor Trace support" - select ACRN_SHARED_BUFFER + depends on ACRN_SHARED_BUFFER ---help--- This is the Trace driver for the Intel ACRN hypervisor. You can say y to build it into the kernel, or m to build @@ -14,7 +15,7 @@ config ACRN_TRACE config ACRN_HVLOG bool "Intel ACRN Hypervisor Logmsg support" - select ACRN_SHARED_BUFFER + depends on ACRN_SHARED_BUFFER ---help--- This is the Trace driver for the Intel ACRN hypervisor log. You can say y to build it into the kernel. diff --git a/drivers/vbs/Kconfig b/drivers/vbs/Kconfig index e3bb44d6a5e9..663d09c2c42f 100644 --- a/drivers/vbs/Kconfig +++ b/drivers/vbs/Kconfig @@ -2,7 +2,7 @@ # This Kconfig describes VBS for ACRN hypervisor # config VBS - tristate "Enable VBS framework for ACRN hypervisor" + bool "Enable VBS framework for ACRN hypervisor" depends on ACRN depends on ACRN_VHM default n @@ -20,7 +20,7 @@ config VBS_DEBUG say N here. This enables ACRN VBS debugging. config VBS_RNG - tristate "ACRN VBS reference driver: virtio RNG" + bool "ACRN VBS reference driver: virtio RNG" depends on VBS != n default n ---help--- diff --git a/drivers/vhm/Kconfig b/drivers/vhm/Kconfig index 0b0faeeff74a..e59b9487cd65 100644 --- a/drivers/vhm/Kconfig +++ b/drivers/vhm/Kconfig @@ -3,7 +3,6 @@ config ACRN_VHM depends on ACRN depends on DMA_CMA depends on PCI_MSI - depends on !INTEL_IOMMU depends on !VMAP_STACK default n ---help--- From 90ed76f7142cee10e6ad31c82055dda8f535a76e Mon Sep 17 00:00:00 2001 From: Jack Ren Date: Fri, 31 Aug 2018 10:59:00 +0800 Subject: [PATCH 0050/1103] skip sbuf and vhm initialization when booting natively Change-Id: Ib5cd72c208f6e7cc905418671cd655054132806f Tracked-On: 229665 Signed-off-by: Jack Ren Reviewed-on: --- drivers/acrn/sbuf.c | 4 ++++ drivers/char/vhm/vhm_dev.c | 8 +++----- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/drivers/acrn/sbuf.c b/drivers/acrn/sbuf.c index a3582325d9b9..b51ee04e12fa 100644 --- a/drivers/acrn/sbuf.c +++ b/drivers/acrn/sbuf.c @@ -57,6 +57,7 @@ #include #include +#include #include #include #include "sbuf.h" @@ -170,6 +171,9 @@ int sbuf_share_setup(uint32_t pcpu_id, uint32_t sbuf_id, shared_buf_t *sbuf) { struct sbuf_setup_param ssp; + if (x86_hyper_type != X86_HYPER_ACRN) + return -ENODEV; + ssp.pcpu_id = pcpu_id; ssp.sbuf_id = sbuf_id; diff --git a/drivers/char/vhm/vhm_dev.c b/drivers/char/vhm/vhm_dev.c index b724c9e7bce2..8590d69fa4e6 100644 --- a/drivers/char/vhm/vhm_dev.c +++ b/drivers/char/vhm/vhm_dev.c @@ -590,12 +590,10 @@ static int __init vhm_init(void) unsigned long flag; struct hc_api_version api_version = {0, 0}; - pr_info("vhm: initializing\n"); + if (x86_hyper_type != X86_HYPER_ACRN) + return -ENODEV; - if (x86_hyper_type != X86_HYPER_ACRN) { - pr_err("vhm: not support acrn hypervisor!\n"); - return -EINVAL; - } + pr_info("vhm: initializing\n"); if (hcall_get_api_version(virt_to_phys(&api_version)) < 0) { pr_err("vhm: failed to get api version from Hypervisor !\n"); From 58260e202d198d5c2e60b8ab0c299174c2dc4586 Mon Sep 17 00:00:00 2001 From: Jason Chen CJ Date: Fri, 31 Aug 2018 10:59:00 +0800 Subject: [PATCH 0051/1103] VHM: add hugetlb page ept mapping support unlike cma, hugetlb allocates hugepage under user space, so VHM only need take care of ept mapping for these allocated huge pages. this patch add hugepage_map_guest function, it gets huge page struct pointer according to user virtual address input from ioctl IC_SET_MEMSEG, then build all required parameters for recording guest memseg and mapping ept entry through this page struct. Change-Id: I0b333613dc20fce41b9b091c72892bbac6b07735 Signed-off-by: Jason Chen CJ Reviewed-on: --- drivers/char/vhm/vhm_dev.c | 1 + drivers/vhm/Kconfig | 1 + drivers/vhm/vhm_mm.c | 101 +++++++++++++++++++++++++---- include/linux/vhm/vhm_ioctl_defs.h | 27 +++++--- include/linux/vhm/vhm_vm_mngt.h | 1 + 5 files changed, 110 insertions(+), 21 deletions(-) diff --git a/drivers/char/vhm/vhm_dev.c b/drivers/char/vhm/vhm_dev.c index 8590d69fa4e6..8d083a09bf06 100644 --- a/drivers/char/vhm/vhm_dev.c +++ b/drivers/char/vhm/vhm_dev.c @@ -129,6 +129,7 @@ static int vhm_dev_open(struct inode *inodep, struct file *filep) vm_mutex_lock(&vhm_vm_list_lock); vm->refcnt = 1; + vm->hugetlb_enabled = 0; vm_list_add(&vm->list); vm_mutex_unlock(&vhm_vm_list_lock); filep->private_data = vm; diff --git a/drivers/vhm/Kconfig b/drivers/vhm/Kconfig index e59b9487cd65..4ddb1314709a 100644 --- a/drivers/vhm/Kconfig +++ b/drivers/vhm/Kconfig @@ -3,6 +3,7 @@ config ACRN_VHM depends on ACRN depends on DMA_CMA depends on PCI_MSI + depends on HUGETLBFS depends on !VMAP_STACK default n ---help--- diff --git a/drivers/vhm/vhm_mm.c b/drivers/vhm/vhm_mm.c index be6a47afad9a..cc08fd9d0965 100644 --- a/drivers/vhm/vhm_mm.c +++ b/drivers/vhm/vhm_mm.c @@ -110,31 +110,25 @@ static bool _free_memblk(struct device *dev, u64 vm0_gpa, size_t len) return dma_release_from_contiguous(dev, page, count); } -int alloc_guest_memseg(struct vhm_vm *vm, struct vm_memseg *memseg) +static int add_guest_memseg(struct vhm_vm *vm, unsigned long vm0_gpa, + unsigned long guest_gpa, unsigned long len) { struct guest_memseg *seg; - u64 vm0_gpa; int max_gfn; seg = kzalloc(sizeof(struct guest_memseg), GFP_KERNEL); if (seg == NULL) return -ENOMEM; - vm0_gpa = _alloc_memblk(vm->dev, memseg->len); - if (vm0_gpa == 0ULL) { - kfree(seg); - return -ENOMEM; - } - seg->vm0_gpa = vm0_gpa; - seg->len = memseg->len; - seg->gpa = memseg->gpa; + seg->gpa = guest_gpa; + seg->len = len; max_gfn = (seg->gpa + seg->len) >> PAGE_SHIFT; if (vm->max_gfn < max_gfn) vm->max_gfn = max_gfn; - pr_info("VHM: alloc memseg with len=0x%lx, vm0_gpa=0x%llx," + pr_info("VHM: add memseg with len=0x%lx, vm0_gpa=0x%llx," " and its guest gpa = 0x%llx, vm max_gfn 0x%x\n", seg->len, seg->vm0_gpa, seg->gpa, vm->max_gfn); @@ -146,6 +140,22 @@ int alloc_guest_memseg(struct vhm_vm *vm, struct vm_memseg *memseg) return 0; } +int alloc_guest_memseg(struct vhm_vm *vm, struct vm_memseg *memseg) +{ + unsigned long vm0_gpa; + int ret; + + vm0_gpa = _alloc_memblk(vm->dev, memseg->len); + if (vm0_gpa == 0ULL) + return -ENOMEM; + + ret = add_guest_memseg(vm, vm0_gpa, memseg->gpa, memseg->len); + if (ret < 0) + _free_memblk(vm->dev, vm0_gpa, memseg->len); + + return ret; +} + static int _mem_set_memmap(unsigned long vmid, unsigned long guest_gpa, unsigned long host_gpa, unsigned long len, unsigned int mem_type, unsigned int mem_access_right, @@ -197,6 +207,61 @@ int update_memmap_attr(unsigned long vmid, unsigned long guest_gpa, mem_type, mem_access_right, MAP_MEM); } +static int hugepage_map_guest(struct vhm_vm *vm, struct vm_memmap *memmap) +{ + struct page *page; + unsigned long len, guest_gpa, vma; + unsigned int type; + unsigned int mem_type, mem_access_right; + int ret; + + if (vm == NULL || memmap == NULL) + return -EINVAL; + + len = memmap->len; + vma = memmap->vma_base; + guest_gpa = memmap->gpa; + + while (len > 0) { + unsigned long vm0_gpa, pagesize; + + ret = get_user_pages_fast(vma, 1, 1, &page); + if (unlikely(ret != 1) || (page == NULL)) { + pr_err("failed to pin huge page!\n"); + return -ENOMEM; + } + + vm0_gpa = page_to_phys(page); + pagesize = PAGE_SIZE << compound_order(page); + + ret = add_guest_memseg(vm, vm0_gpa, guest_gpa, pagesize); + if (ret < 0) { + pr_err("failed to add memseg for huge page!\n"); + put_page(page); + return ret; + } + + /* TODO: do batch hypercall for multi ept mapping */ + mem_type = MEM_TYPE_WB; + mem_access_right = (memmap->prot & MEM_ACCESS_RIGHT_MASK); + type = MAP_MEM; + if (_mem_set_memmap(vm->vmid, guest_gpa, vm0_gpa, pagesize, + mem_type, mem_access_right, type) < 0) { + pr_err("vhm: failed to set memmap %ld!\n", vm->vmid); + put_page(page); + return -EFAULT; + } + + len -= pagesize; + vma += pagesize; + guest_gpa += pagesize; + } + + vm->hugetlb_enabled = 1; + + return 0; +} + int map_guest_memseg(struct vhm_vm *vm, struct vm_memmap *memmap) { struct guest_memseg *seg = NULL; @@ -204,8 +269,13 @@ int map_guest_memseg(struct vhm_vm *vm, struct vm_memmap *memmap) unsigned int mem_type, mem_access_right; unsigned long guest_gpa, host_gpa; + /* hugetlb use vma to do the mapping */ + if (memmap->type == VM_SYSMEM && memmap->using_vma) + return hugepage_map_guest(vm, memmap); + mutex_lock(&vm->seg_lock); + /* cma or mmio */ if (memmap->type == VM_SYSMEM) { list_for_each_entry(seg, &vm->memseg_list, list) { if (seg->gpa == memmap->gpa @@ -249,8 +319,13 @@ void free_guest_mem(struct vhm_vm *vm) while (!list_empty(&vm->memseg_list)) { seg = list_first_entry(&vm->memseg_list, struct guest_memseg, list); - if (!_free_memblk(vm->dev, seg->vm0_gpa, seg->len)) - pr_warn("failed to free memblk\n"); + if (vm->hugetlb_enabled) { + /* just put_page to unpin huge page */ + put_page(pfn_to_page(seg->vm0_gpa >> PAGE_SHIFT)); + } else { + if (!_free_memblk(vm->dev, seg->vm0_gpa, seg->len)) + pr_warn("failed to free memblk\n"); + } list_del(&seg->list); kfree(seg); } diff --git a/include/linux/vhm/vhm_ioctl_defs.h b/include/linux/vhm/vhm_ioctl_defs.h index 5bc7c666f2ea..a0a830dec3fa 100644 --- a/include/linux/vhm/vhm_ioctl_defs.h +++ b/include/linux/vhm/vhm_ioctl_defs.h @@ -117,19 +117,30 @@ struct vm_memseg { /** * struct vm_memmap - EPT memory mapping info for guest - * - * @type: memory mapping type - * @gpa: guest physical start address of memory mapping - * @hpa: host physical start address of memory - * @len: the length of memory range mapped - * @prot: memory mapping attribute */ struct vm_memmap { + /** @type: memory mapping type */ uint32_t type; - uint32_t reserved; + /** @using_vma: using vma_base to get vm0_gpa, + * only for type == VM_SYSTEM + */ + uint32_t using_vma; + /** @gpa: user OS guest physical start address of memory mapping */ uint64_t gpa; - uint64_t hpa; /* only for type == VM_MMIO */ + /** union */ + union { + /** @hpa: host physical start address of memory, + * only for type == VM_MMIO + */ + uint64_t hpa; + /** @vma_base: service OS user virtual start address of + * memory, only for type == VM_SYSMEM && using_vma == true + */ + uint64_t vma_base; + }; + /** @len: the length of memory range mapped */ uint64_t len; /* mmap length */ + /** @prot: memory mapping attribute */ uint32_t prot; /* RWX */ }; diff --git a/include/linux/vhm/vhm_vm_mngt.h b/include/linux/vhm/vhm_vm_mngt.h index e7bc8b2372f7..306bd54c4103 100644 --- a/include/linux/vhm/vhm_vm_mngt.h +++ b/include/linux/vhm/vhm_vm_mngt.h @@ -96,6 +96,7 @@ struct vhm_vm { struct list_head ioreq_client_list; struct vhm_request_buffer *req_buf; struct page *pg; + int hugetlb_enabled; }; /** From 25a26d673dba5d0ea39ee7662eb3631a00391e0a Mon Sep 17 00:00:00 2001 From: Jason Chen CJ Date: Fri, 31 Aug 2018 10:59:00 +0800 Subject: [PATCH 0052/1103] VHM: change VM_SYSMEM/VM_MMIO to VM_MEMMAP_SYSMEM/VM_MEMMAP_MMIO Change-Id: I7dc07502530ae47c6f9a3bc6a29fc271a053e8da Signed-off-by: Jason Chen CJ Reviewed-on: --- drivers/vhm/vhm_mm.c | 4 ++-- include/linux/vhm/vhm_ioctl_defs.h | 9 +++++---- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/drivers/vhm/vhm_mm.c b/drivers/vhm/vhm_mm.c index cc08fd9d0965..728998d0341d 100644 --- a/drivers/vhm/vhm_mm.c +++ b/drivers/vhm/vhm_mm.c @@ -270,13 +270,13 @@ int map_guest_memseg(struct vhm_vm *vm, struct vm_memmap *memmap) unsigned long guest_gpa, host_gpa; /* hugetlb use vma to do the mapping */ - if (memmap->type == VM_SYSMEM && memmap->using_vma) + if (memmap->type == VM_MEMMAP_SYSMEM && memmap->using_vma) return hugepage_map_guest(vm, memmap); mutex_lock(&vm->seg_lock); /* cma or mmio */ - if (memmap->type == VM_SYSMEM) { + if (memmap->type == VM_MEMMAP_SYSMEM) { list_for_each_entry(seg, &vm->memseg_list, list) { if (seg->gpa == memmap->gpa && seg->len == memmap->len) diff --git a/include/linux/vhm/vhm_ioctl_defs.h b/include/linux/vhm/vhm_ioctl_defs.h index a0a830dec3fa..eb8d0d08a89d 100644 --- a/include/linux/vhm/vhm_ioctl_defs.h +++ b/include/linux/vhm/vhm_ioctl_defs.h @@ -112,8 +112,8 @@ struct vm_memseg { uint64_t gpa; }; -#define VM_SYSMEM 0 -#define VM_MMIO 1 +#define VM_MEMMAP_SYSMEM 0 +#define VM_MEMMAP_MMIO 1 /** * struct vm_memmap - EPT memory mapping info for guest @@ -130,11 +130,12 @@ struct vm_memmap { /** union */ union { /** @hpa: host physical start address of memory, - * only for type == VM_MMIO + * only for type == VM_MEMMAP_MMIO */ uint64_t hpa; /** @vma_base: service OS user virtual start address of - * memory, only for type == VM_SYSMEM && using_vma == true + * memory, only for type == VM_MEMMAP_SYSMEM && + * using_vma == true */ uint64_t vma_base; }; From b159c8a5c988bce3adbddd944b819685d50c4d9d Mon Sep 17 00:00:00 2001 From: Jason Chen CJ Date: Fri, 31 Aug 2018 10:59:00 +0800 Subject: [PATCH 0053/1103] VHM: add hash table support for huge pages use HUGEPAGE_2M_HLIST_ARRAY_SIZE(16) for 2M hash table size, HUGEPAGE_1G_HLIST_ARRAY_SIZE(1) for 1G hash table size. The assumption is that we only support 2M & 1G huge pages. Change-Id: I08d331d7b7ff7e6a96f36e8c496db3644628aa9e Signed-off-by: Jason Chen CJ Reviewed-on: --- drivers/char/vhm/vhm_dev.c | 5 + drivers/vhm/Makefile | 2 +- drivers/vhm/vhm_hugetlb.c | 273 ++++++++++++++++++++++++++++++++ drivers/vhm/vhm_mm.c | 83 +++------- include/linux/vhm/acrn_vhm_mm.h | 8 + include/linux/vhm/vhm_vm_mngt.h | 8 + 6 files changed, 313 insertions(+), 66 deletions(-) create mode 100644 drivers/vhm/vhm_hugetlb.c diff --git a/drivers/char/vhm/vhm_dev.c b/drivers/char/vhm/vhm_dev.c index 8d083a09bf06..f4d2ec2b7e23 100644 --- a/drivers/char/vhm/vhm_dev.c +++ b/drivers/char/vhm/vhm_dev.c @@ -112,6 +112,7 @@ static DEFINE_MUTEX(table_iomems_lock); static int vhm_dev_open(struct inode *inodep, struct file *filep) { struct vhm_vm *vm; + int i; vm = kzalloc(sizeof(struct vhm_vm), GFP_KERNEL); pr_info("vhm_dev_open: opening device node\n"); @@ -124,6 +125,10 @@ static int vhm_dev_open(struct inode *inodep, struct file *filep) INIT_LIST_HEAD(&vm->memseg_list); mutex_init(&vm->seg_lock); + for (i = 0; i < HUGEPAGE_HLIST_ARRAY_SIZE; i++) + INIT_HLIST_HEAD(&vm->hugepage_hlist[i]); + mutex_init(&vm->hugepage_lock); + INIT_LIST_HEAD(&vm->ioreq_client_list); spin_lock_init(&vm->ioreq_client_lock); diff --git a/drivers/vhm/Makefile b/drivers/vhm/Makefile index b4d58a92dcfd..23f17ae24f78 100644 --- a/drivers/vhm/Makefile +++ b/drivers/vhm/Makefile @@ -1 +1 @@ -obj-y += vhm_mm.o vhm_ioreq.o vhm_vm_mngt.o vhm_msi.o vhm_hypercall.o +obj-y += vhm_mm.o vhm_hugetlb.o vhm_ioreq.o vhm_vm_mngt.o vhm_msi.o vhm_hypercall.o diff --git a/drivers/vhm/vhm_hugetlb.c b/drivers/vhm/vhm_hugetlb.c new file mode 100644 index 000000000000..afab8ab52567 --- /dev/null +++ b/drivers/vhm/vhm_hugetlb.c @@ -0,0 +1,273 @@ +/* + * virtio and hyperviosr service module (VHM): hugetlb + * + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright (c) 2018 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * 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. + * + * BSD LICENSE + * + * Copyright (C) 2018 Intel Corporation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 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 + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * + * Jason Chen CJ + * + */ + +#include +#include +#include +#include +#include + +#include +#include +#include + +#define HUGEPAGE_2M_SHIFT 21 +#define HUGEPAGE_1G_SHIFT 30 + +#define HUGEPAGE_1G_HLIST_IDX (HUGEPAGE_HLIST_ARRAY_SIZE - 1) + +struct hugepage_map { + struct hlist_node hlist; + u64 vm0_gpa; + size_t size; + u64 guest_gpa; +}; + +static inline struct hlist_head *hlist_2m_hash(struct vhm_vm *vm, + unsigned long guest_gpa) +{ + return &vm->hugepage_hlist[guest_gpa >> HUGEPAGE_2M_SHIFT & + (HUGEPAGE_2M_HLIST_ARRAY_SIZE - 1)]; +} + +static int add_guest_map(struct vhm_vm *vm, unsigned long vm0_gpa, + unsigned long guest_gpa, unsigned long size) +{ + struct hugepage_map *map; + int max_gfn; + + map = kzalloc(sizeof(struct hugepage_map), GFP_KERNEL); + if (map == NULL) + return -ENOMEM; + + map->vm0_gpa = vm0_gpa; + map->guest_gpa = guest_gpa; + map->size = size; + + INIT_HLIST_NODE(&map->hlist); + + max_gfn = (map->guest_gpa + map->size) >> PAGE_SHIFT; + if (vm->max_gfn < max_gfn) + vm->max_gfn = max_gfn; + + pr_info("VHM: add hugepage with size=0x%lx, vm0_gpa=0x%llx," + " and its guest gpa = 0x%llx, vm max_gfn 0x%x\n", + map->size, map->vm0_gpa, map->guest_gpa, vm->max_gfn); + + mutex_lock(&vm->hugepage_lock); + /* 1G hugepage? */ + if (map->size == (1UL << HUGEPAGE_1G_SHIFT)) + hlist_add_head(&map->hlist, + &vm->hugepage_hlist[HUGEPAGE_1G_HLIST_IDX]); + else + hlist_add_head(&map->hlist, + hlist_2m_hash(vm, map->guest_gpa)); + mutex_unlock(&vm->hugepage_lock); + + return 0; +} + +int hugepage_map_guest(struct vhm_vm *vm, struct vm_memmap *memmap) +{ + struct page *page; + unsigned long len, guest_gpa, vma; + unsigned int type; + unsigned int mem_type, mem_access_right; + int ret; + + if (vm == NULL || memmap == NULL) + return -EINVAL; + + len = memmap->len; + vma = memmap->vma_base; + guest_gpa = memmap->gpa; + + while (len > 0) { + unsigned long vm0_gpa, pagesize; + + ret = get_user_pages_fast(vma, 1, 1, &page); + if (unlikely(ret != 1) || (page == NULL)) { + pr_err("failed to pin huge page!\n"); + return -ENOMEM; + } + + vm0_gpa = page_to_phys(page); + pagesize = PAGE_SIZE << compound_order(page); + + ret = add_guest_map(vm, vm0_gpa, guest_gpa, pagesize); + if (ret < 0) { + pr_err("failed to add memseg for huge page!\n"); + put_page(page); + return ret; + } + + /* TODO: do batch hypercall for multi ept mapping */ + mem_type = MEM_TYPE_WB; + mem_access_right = (memmap->prot & MEM_ACCESS_RIGHT_MASK); + type = MAP_MEM; + if (_mem_set_memmap(vm->vmid, guest_gpa, vm0_gpa, pagesize, + mem_type, mem_access_right, type) < 0) { + pr_err("vhm: failed to set memmap %ld!\n", vm->vmid); + put_page(page); + return -EFAULT; + } + + len -= pagesize; + vma += pagesize; + guest_gpa += pagesize; + } + + vm->hugetlb_enabled = 1; + + return 0; +} + +void hugepage_free_guest(struct vhm_vm *vm) +{ + struct hlist_node *htmp; + struct hugepage_map *map; + int i; + + mutex_lock(&vm->hugepage_lock); + for (i = 0; i < HUGEPAGE_HLIST_ARRAY_SIZE; i++) { + if (!hlist_empty(&vm->hugepage_hlist[i])) { + hlist_for_each_entry_safe(map, htmp, + &vm->hugepage_hlist[i], hlist) { + hlist_del(&map->hlist); + /* put_page to unpin huge page */ + put_page(pfn_to_page( + map->vm0_gpa >> PAGE_SHIFT)); + kfree(map); + } + } + } + mutex_unlock(&vm->hugepage_lock); +} + +void *hugepage_map_guest_phys(struct vhm_vm *vm, u64 guest_phys, size_t size) +{ + struct hlist_node *htmp; + struct hugepage_map *map; + + mutex_lock(&vm->hugepage_lock); + /* check 1G hlist first */ + if (!hlist_empty(&vm->hugepage_hlist[HUGEPAGE_1G_HLIST_IDX])) { + hlist_for_each_entry_safe(map, htmp, + &vm->hugepage_hlist[HUGEPAGE_1G_HLIST_IDX], hlist) { + if (map->guest_gpa >= guest_phys + size || + guest_phys >= map->guest_gpa + map->size) + continue; + + if (guest_phys + size > map->guest_gpa + map->size || + guest_phys < map->guest_gpa) + goto err; + + mutex_unlock(&vm->hugepage_lock); + return phys_to_virt(map->vm0_gpa + + guest_phys - map->guest_gpa); + } + } + + /* check 2m hlist */ + hlist_for_each_entry_safe(map, htmp, + hlist_2m_hash(vm, guest_phys), hlist) { + if (map->guest_gpa >= guest_phys + size || + guest_phys >= map->guest_gpa + map->size) + continue; + + if (guest_phys + size > map->guest_gpa + map->size || + guest_phys < map->guest_gpa) + goto err; + + mutex_unlock(&vm->hugepage_lock); + return phys_to_virt(map->vm0_gpa + + guest_phys - map->guest_gpa); + } + +err: + mutex_unlock(&vm->hugepage_lock); + printk(KERN_WARNING "cannot find correct mem map, please check the " + "input's range or alignment"); + return NULL; +} + +int hugepage_unmap_guest_phys(struct vhm_vm *vm, u64 guest_phys) +{ + struct hlist_node *htmp; + struct hugepage_map *map; + + mutex_lock(&vm->hugepage_lock); + /* check 1G hlist first */ + if (!hlist_empty(&vm->hugepage_hlist[HUGEPAGE_1G_HLIST_IDX])) { + hlist_for_each_entry_safe(map, htmp, + &vm->hugepage_hlist[HUGEPAGE_1G_HLIST_IDX], hlist) { + if (map->guest_gpa <= guest_phys && + guest_phys < map->guest_gpa + map->size) { + mutex_unlock(&vm->hugepage_lock); + return 0; + } + } + } + /* check 2m hlist */ + hlist_for_each_entry_safe(map, htmp, + hlist_2m_hash(vm, guest_phys), hlist) { + if (map->guest_gpa <= guest_phys && + guest_phys < map->guest_gpa + map->size) { + mutex_unlock(&vm->hugepage_lock); + return 0; + } + } + mutex_unlock(&vm->hugepage_lock); + return -ESRCH; +} diff --git a/drivers/vhm/vhm_mm.c b/drivers/vhm/vhm_mm.c index 728998d0341d..070327e616d6 100644 --- a/drivers/vhm/vhm_mm.c +++ b/drivers/vhm/vhm_mm.c @@ -156,7 +156,7 @@ int alloc_guest_memseg(struct vhm_vm *vm, struct vm_memseg *memseg) return ret; } -static int _mem_set_memmap(unsigned long vmid, unsigned long guest_gpa, +int _mem_set_memmap(unsigned long vmid, unsigned long guest_gpa, unsigned long host_gpa, unsigned long len, unsigned int mem_type, unsigned int mem_access_right, unsigned int type) @@ -207,61 +207,6 @@ int update_memmap_attr(unsigned long vmid, unsigned long guest_gpa, mem_type, mem_access_right, MAP_MEM); } -static int hugepage_map_guest(struct vhm_vm *vm, struct vm_memmap *memmap) -{ - struct page *page; - unsigned long len, guest_gpa, vma; - unsigned int type; - unsigned int mem_type, mem_access_right; - int ret; - - if (vm == NULL || memmap == NULL) - return -EINVAL; - - len = memmap->len; - vma = memmap->vma_base; - guest_gpa = memmap->gpa; - - while (len > 0) { - unsigned long vm0_gpa, pagesize; - - ret = get_user_pages_fast(vma, 1, 1, &page); - if (unlikely(ret != 1) || (page == NULL)) { - pr_err("failed to pin huge page!\n"); - return -ENOMEM; - } - - vm0_gpa = page_to_phys(page); - pagesize = PAGE_SIZE << compound_order(page); - - ret = add_guest_memseg(vm, vm0_gpa, guest_gpa, pagesize); - if (ret < 0) { - pr_err("failed to add memseg for huge page!\n"); - put_page(page); - return ret; - } - - /* TODO: do batch hypercall for multi ept mapping */ - mem_type = MEM_TYPE_WB; - mem_access_right = (memmap->prot & MEM_ACCESS_RIGHT_MASK); - type = MAP_MEM; - if (_mem_set_memmap(vm->vmid, guest_gpa, vm0_gpa, pagesize, - mem_type, mem_access_right, type) < 0) { - pr_err("vhm: failed to set memmap %ld!\n", vm->vmid); - put_page(page); - return -EFAULT; - } - - len -= pagesize; - vma += pagesize; - guest_gpa += pagesize; - } - - vm->hugetlb_enabled = 1; - - return 0; -} - int map_guest_memseg(struct vhm_vm *vm, struct vm_memmap *memmap) { struct guest_memseg *seg = NULL; @@ -315,17 +260,15 @@ void free_guest_mem(struct vhm_vm *vm) { struct guest_memseg *seg; + if (vm->hugetlb_enabled) + return hugepage_free_guest(vm); + mutex_lock(&vm->seg_lock); while (!list_empty(&vm->memseg_list)) { seg = list_first_entry(&vm->memseg_list, struct guest_memseg, list); - if (vm->hugetlb_enabled) { - /* just put_page to unpin huge page */ - put_page(pfn_to_page(seg->vm0_gpa >> PAGE_SHIFT)); - } else { - if (!_free_memblk(vm->dev, seg->vm0_gpa, seg->len)) - pr_warn("failed to free memblk\n"); - } + if (!_free_memblk(vm->dev, seg->vm0_gpa, seg->len)) + pr_warn("failed to free memblk\n"); list_del(&seg->list); kfree(seg); } @@ -412,6 +355,9 @@ int vhm_dev_mmap(struct file *file, struct vm_area_struct *vma) size_t len = vma->vm_end - vma->vm_start; int ret; + if (vm->hugetlb_enabled) + return -EINVAL; + mutex_lock(&vm->seg_lock); list_for_each_entry(seg, &vm->memseg_list, list) { if (seg->gpa != offset || seg->len != len) @@ -456,7 +402,10 @@ void *map_guest_phys(unsigned long vmid, u64 guest_phys, size_t size) if (vm == NULL) return NULL; - ret = do_map_guest_phys(vm, guest_phys, size); + if (vm->hugetlb_enabled) + ret = hugepage_map_guest_phys(vm, guest_phys, size); + else + ret = do_map_guest_phys(vm, guest_phys, size); put_vm(vm); @@ -492,7 +441,11 @@ int unmap_guest_phys(unsigned long vmid, u64 guest_phys) return -ESRCH; } - ret = do_unmap_guest_phys(vm, guest_phys); + if (vm->hugetlb_enabled) + ret = hugepage_unmap_guest_phys(vm, guest_phys); + else + ret = do_unmap_guest_phys(vm, guest_phys); + put_vm(vm); return ret; } diff --git a/include/linux/vhm/acrn_vhm_mm.h b/include/linux/vhm/acrn_vhm_mm.h index ba383b354986..9be6749d12e2 100644 --- a/include/linux/vhm/acrn_vhm_mm.h +++ b/include/linux/vhm/acrn_vhm_mm.h @@ -199,4 +199,12 @@ int alloc_guest_memseg(struct vhm_vm *vm, struct vm_memseg *memseg); */ int map_guest_memseg(struct vhm_vm *vm, struct vm_memmap *memmap); +int _mem_set_memmap(unsigned long vmid, unsigned long guest_gpa, + unsigned long host_gpa, unsigned long len, + unsigned int mem_type, unsigned int mem_access_right, + unsigned int type); +int hugepage_map_guest(struct vhm_vm *vm, struct vm_memmap *memmap); +void hugepage_free_guest(struct vhm_vm *vm); +void *hugepage_map_guest_phys(struct vhm_vm *vm, u64 guest_phys, size_t size); +int hugepage_unmap_guest_phys(struct vhm_vm *vm, u64 guest_phys); #endif diff --git a/include/linux/vhm/vhm_vm_mngt.h b/include/linux/vhm/vhm_vm_mngt.h index 306bd54c4103..f0a7e1cf7b05 100644 --- a/include/linux/vhm/vhm_vm_mngt.h +++ b/include/linux/vhm/vhm_vm_mngt.h @@ -67,6 +67,10 @@ extern struct list_head vhm_vm_list; extern struct mutex vhm_vm_list_lock; +#define HUGEPAGE_2M_HLIST_ARRAY_SIZE 16 +#define HUGEPAGE_1G_HLIST_ARRAY_SIZE 1 +#define HUGEPAGE_HLIST_ARRAY_SIZE (HUGEPAGE_2M_HLIST_ARRAY_SIZE + \ + HUGEPAGE_1G_HLIST_ARRAY_SIZE) /** * struct vhm_vm - data structure to track guest * @@ -77,6 +81,8 @@ extern struct mutex vhm_vm_list_lock; * @refcnt: reference count of guest * @seg_lock: mutex to protect memseg_list * @memseg_list: list of memseg + * @hugepage_lock: mutex to protect hugepage_hlist + * @hugepage_hlist: hash list of hugepage * @max_gfn: maximum guest page frame number * @ioreq_client_lock: spinlock to protect ioreq_client_list * @ioreq_client_list: list of ioreq clients @@ -91,6 +97,8 @@ struct vhm_vm { long refcnt; struct mutex seg_lock; struct list_head memseg_list; + struct mutex hugepage_lock; + struct hlist_head hugepage_hlist[HUGEPAGE_HLIST_ARRAY_SIZE]; int max_gfn; spinlock_t ioreq_client_lock; struct list_head ioreq_client_list; From fdae15e6d7ad3dc4db4a4aecf8b9316ea43cc23c Mon Sep 17 00:00:00 2001 From: Victor Sun Date: Fri, 31 Aug 2018 10:59:00 +0800 Subject: [PATCH 0054/1103] VHM: add service to support px data transition The px data is hard coded within HV, DM will get these data to build DSDT for UOS. With this DSDT, UOS would have capability on Px control if acpi-cpufreq driver is enabled in kernel. So this patch is to add the service to interact with both HV and DM. The detailed working rationale is illustrated in HV patch set. Change-Id: Icfd01880dcfe0fd938a05c6f31614dfdcd48631a Tracked-On: 212378 Signed-off-by: Victor Sun Reviewed-on: --- drivers/char/vhm/vhm_dev.c | 40 ++++++++++++++++++++++++++++++ drivers/vhm/vhm_hypercall.c | 5 ++++ include/linux/vhm/acrn_common.h | 23 +++++++++++++++++ include/linux/vhm/acrn_hv_defs.h | 4 +++ include/linux/vhm/vhm_hypercall.h | 1 + include/linux/vhm/vhm_ioctl_defs.h | 4 +++ 6 files changed, 77 insertions(+) diff --git a/drivers/char/vhm/vhm_dev.c b/drivers/char/vhm/vhm_dev.c index f4d2ec2b7e23..5000ed80e177 100644 --- a/drivers/char/vhm/vhm_dev.c +++ b/drivers/char/vhm/vhm_dev.c @@ -532,6 +532,46 @@ static long vhm_dev_ioctl(struct file *filep, break; } + case IC_PM_GET_CPU_STATE: { + uint64_t cmd; + + if (copy_from_user(&cmd, + (void *)ioctl_param, sizeof(cmd))) + return -EFAULT; + + switch (cmd & PMCMD_TYPE_MASK) { + case PMCMD_GET_PX_CNT: { + uint8_t px_cnt; + + ret = hcall_get_cpu_state(cmd, virt_to_phys(&px_cnt)); + if (ret < 0) + return -EFAULT; + + if (copy_to_user((void *)ioctl_param, + &px_cnt, sizeof(px_cnt))) + ret = -EFAULT; + + break; + } + case PMCMD_GET_PX_DATA: { + struct cpu_px_data px_data; + + ret = hcall_get_cpu_state(cmd, virt_to_phys(&px_data)); + if (ret < 0) + return -EFAULT; + + if (copy_to_user((void *)ioctl_param, + &px_data, sizeof(px_data))) + ret = -EFAULT; + break; + } + default: + ret = -EFAULT; + break; + } + break; + } + default: pr_warn("Unknown IOCTL 0x%x\n", ioctl_num); ret = 0; diff --git a/drivers/vhm/vhm_hypercall.c b/drivers/vhm/vhm_hypercall.c index d0da22f2a88b..df87febaf60d 100644 --- a/drivers/vhm/vhm_hypercall.c +++ b/drivers/vhm/vhm_hypercall.c @@ -82,6 +82,11 @@ inline long hcall_setup_sbuf(unsigned long sbuf_head) return acrn_hypercall1(HC_SETUP_SBUF, sbuf_head); } +inline long hcall_get_cpu_state(unsigned long cmd, unsigned long state_pa) +{ + return acrn_hypercall2(HC_PM_GET_CPU_STATE, cmd, state_pa); +} + inline long hcall_set_memmap(unsigned long vmid, unsigned long memmap) { return acrn_hypercall2(HC_VM_SET_MEMMAP, vmid, memmap); diff --git a/include/linux/vhm/acrn_common.h b/include/linux/vhm/acrn_common.h index f27feb7a3e57..d48fe80f6fab 100644 --- a/include/linux/vhm/acrn_common.h +++ b/include/linux/vhm/acrn_common.h @@ -206,4 +206,27 @@ struct acrn_vm_pci_msix_remap { */ #define GUEST_CFG_OFFSET 0xd0000 +struct cpu_px_data { + uint64_t core_frequency; /* megahertz */ + uint64_t power; /* milliWatts */ + uint64_t transition_latency; /* microseconds */ + uint64_t bus_master_latency; /* microseconds */ + uint64_t control; /* control value */ + uint64_t status; /* success indicator */ +} __attribute__((aligned(8))); + +#define PMCMD_VMID_MASK 0xff000000 +#define PMCMD_VCPUID_MASK 0x00ff0000 +#define PMCMD_STATE_NUM_MASK 0x0000ff00 +#define PMCMD_TYPE_MASK 0x000000ff + +#define PMCMD_VMID_SHIFT 24 +#define PMCMD_VCPUID_SHIFT 16 +#define PMCMD_STATE_NUM_SHIFT 8 + +enum pm_cmd_type { + PMCMD_GET_PX_CNT, + PMCMD_GET_PX_DATA, +}; + #endif /* ACRN_COMMON_H */ diff --git a/include/linux/vhm/acrn_hv_defs.h b/include/linux/vhm/acrn_hv_defs.h index 411f197f7f3a..d2da1a760783 100644 --- a/include/linux/vhm/acrn_hv_defs.h +++ b/include/linux/vhm/acrn_hv_defs.h @@ -106,6 +106,10 @@ #define HC_ID_DBG_BASE 0x60UL #define HC_SETUP_SBUF _HC_ID(HC_ID, HC_ID_DBG_BASE + 0x00) +/* Power management */ +#define HC_ID_PM_BASE 0x80UL +#define HC_PM_GET_CPU_STATE _HC_ID(HC_ID, HC_ID_PM_BASE + 0x00) + #define ACRN_DOM0_VMID (0UL) #define ACRN_INVALID_VMID (-1) #define ACRN_INVALID_HPA (-1UL) diff --git a/include/linux/vhm/vhm_hypercall.h b/include/linux/vhm/vhm_hypercall.h index e56a16c5518f..2372906946d6 100644 --- a/include/linux/vhm/vhm_hypercall.h +++ b/include/linux/vhm/vhm_hypercall.h @@ -144,6 +144,7 @@ inline long hcall_pause_vm(unsigned long vmid); inline long hcall_destroy_vm(unsigned long vmid); inline long hcall_query_vm_state(unsigned long vmid); inline long hcall_setup_sbuf(unsigned long sbuf_head); +inline long hcall_get_cpu_state(unsigned long cmd, unsigned long state_pa); inline long hcall_set_memmap(unsigned long vmid, unsigned long memmap); inline long hcall_set_ioreq_buffer(unsigned long vmid, diff --git a/include/linux/vhm/vhm_ioctl_defs.h b/include/linux/vhm/vhm_ioctl_defs.h index eb8d0d08a89d..3b05d8228e53 100644 --- a/include/linux/vhm/vhm_ioctl_defs.h +++ b/include/linux/vhm/vhm_ioctl_defs.h @@ -101,6 +101,10 @@ #define IC_SET_PTDEV_INTR_INFO _IC_ID(IC_ID, IC_ID_PCI_BASE + 0x03) #define IC_RESET_PTDEV_INTR_INFO _IC_ID(IC_ID, IC_ID_PCI_BASE + 0x04) +/* Power management */ +#define IC_ID_PM_BASE 0x60UL +#define IC_PM_GET_CPU_STATE _IC_ID(IC_ID, IC_ID_PM_BASE + 0x00) + /** * struct vm_memseg - memory segment info for guest * From 72bfb27244447742e73aadb283e198937bdd9fa8 Mon Sep 17 00:00:00 2001 From: Mingqiang Chi Date: Fri, 31 Aug 2018 10:59:00 +0800 Subject: [PATCH 0055/1103] sos: sync common header file sync common header file (acrn_common.h) Change-Id: I5d236b89f0799c788dca652ac0ebeb729e20e40c Signed-off-by: Mingqiang Chi Reviewed-on: --- include/linux/vhm/acrn_common.h | 169 ++++++++++++++++++++++++++------ 1 file changed, 138 insertions(+), 31 deletions(-) diff --git a/include/linux/vhm/acrn_common.h b/include/linux/vhm/acrn_common.h index d48fe80f6fab..40f3444b7e73 100644 --- a/include/linux/vhm/acrn_common.h +++ b/include/linux/vhm/acrn_common.h @@ -74,6 +74,16 @@ #define REQUEST_READ 0 #define REQUEST_WRITE 1 +/* Generic VM flags from guest OS */ +#define SECURE_WORLD_ENABLED (1UL<<0) /* Whether secure world is enabled */ + +/** + * @brief Hypercall + * + * @addtogroup acrn_hypercall ACRN Hypercall + * @{ + */ + struct mmio_request { uint32_t direction; uint32_t reserved; @@ -139,66 +149,149 @@ struct vhm_request_buffer { }; } __attribute__((aligned(4096))); -/* Common API params */ +/** + * @brief Info to create a VM, the parameter for HC_CREATE_VM hypercall + */ struct acrn_create_vm { - int32_t vmid; /* OUT: return vmid to VHM. Keep it first field */ - uint32_t vcpu_num; /* IN: VM vcpu number */ - uint8_t GUID[16]; /* IN: GUID of this vm */ - uint8_t secure_world_enabled;/* IN: whether Secure World is enabled */ - uint8_t reserved[31]; /* Reserved for future use */ + /** created vmid return to VHM. Keep it first field */ + int32_t vmid; + + /** VCPU numbers this VM want to create */ + uint32_t vcpu_num; + + /** the GUID of this VM */ + uint8_t GUID[16]; + + /* VM flag bits from Guest OS, now used + * SECURE_WORLD_ENABLED (1UL<<0) + */ + uint64_t vm_flag; + + /** Reserved for future use*/ + uint8_t reserved[24]; } __attribute__((aligned(8))); +/** + * @brief Info to create a VCPU + * + * the parameter for HC_CREATE_VCPU hypercall + */ struct acrn_create_vcpu { - uint32_t vcpu_id; /* IN: vcpu id */ - uint32_t pcpu_id; /* IN: pcpu id */ + /** the virtual CPU ID for the VCPU created */ + uint32_t vcpu_id; + + /** the physical CPU ID for the VCPU created */ + uint32_t pcpu_id; } __attribute__((aligned(8))); +/** + * @brief Info to set ioreq buffer for a created VM + * + * the parameter for HC_SET_IOREQ_BUFFER hypercall + */ struct acrn_set_ioreq_buffer { - uint64_t req_buf; /* IN: gpa of per VM request_buffer*/ + /** guest physical address of VM request_buffer */ + uint64_t req_buf; } __attribute__((aligned(8))); -/* - * intr type - * IOAPIC: inject interrupt to IOAPIC - * ISA: inject interrupt to both PIC and IOAPIC - */ +/** Interrupt type for acrn_irqline: inject interrupt to IOAPIC */ #define ACRN_INTR_TYPE_ISA 0 + +/** Interrupt type for acrn_irqline: inject interrupt to both PIC and IOAPIC */ #define ACRN_INTR_TYPE_IOAPIC 1 -/* For ISA, PIC, IOAPIC etc */ +/** + * @brief Info to assert/deassert/pulse a virtual IRQ line for a VM + * + * the parameter for HC_ASSERT_IRQLINE/HC_DEASSERT_IRQLINE/HC_PULSE_IRQLINE + * hypercall + */ struct acrn_irqline { + /** interrupt type which could be IOAPIC or ISA */ uint32_t intr_type; + + /** reserved for alignment padding */ uint32_t reserved; - uint64_t pic_irq; /* IN: for ISA type */ - uint64_t ioapic_irq; /* IN: for IOAPIC type, -1 don't inject */ + + /** pic IRQ for ISA type */ + uint64_t pic_irq; + + /** ioapic IRQ for IOAPIC & ISA TYPE, + * if -1 then this IRQ will not be injected + */ + uint64_t ioapic_irq; } __attribute__((aligned(8))); -/* For MSI type inject */ +/** + * @brief Info to inject a MSI interrupt to VM + * + * the parameter for HC_INJECT_MSI hypercall + */ struct acrn_msi_entry { - uint64_t msi_addr; /* IN: addr[19:12] with dest vcpu id */ - uint64_t msi_data; /* IN: data[7:0] with vector */ + /** MSI addr[19:12] with dest VCPU ID */ + uint64_t msi_addr; + + /** MSI data[7:0] with vector */ + uint64_t msi_data; } __attribute__((aligned(8))); -/* For NMI inject */ +/** + * @brief Info to inject a NMI interrupt for a VM + */ struct acrn_nmi_entry { - int64_t vcpuid; /* IN: -1 means vcpu0 */ + /** virtual CPU ID to inject */ + int64_t vcpu_id; } __attribute__((aligned(8))); +/** + * @brief Info to remap pass-through PCI MSI for a VM + * + * the parameter for HC_VM_PCI_MSIX_REMAP hypercall + */ struct acrn_vm_pci_msix_remap { - uint16_t virt_bdf; /* IN: Device virtual BDF# */ - uint16_t phys_bdf; /* IN: Device physical BDF# */ - uint16_t msi_ctl; /* IN: PCI MSI/x cap control data */ + /** pass-through PCI device virtual BDF# */ + uint16_t virt_bdf; + + /** pass-through PCI device physical BDF# */ + uint16_t phys_bdf; + + /** pass-through PCI device MSI/MSI-X cap control data */ + uint16_t msi_ctl; + + /** reserved for alignment padding */ uint16_t reserved; + + /** pass-through PCI device MSI address to remap, which will + * return the caller after remapping + */ uint64_t msi_addr; /* IN/OUT: msi address to fix */ - uint32_t msi_data; /* IN/OUT: msi data to fix */ - int32_t msix; /* IN: 0 - MSI, 1 - MSI-X */ - int32_t msix_entry_index; /* IN: MSI-X the entry table index */ - /* IN: Vector Control for MSI-X Entry, field defined in MSIX spec */ + + /** pass-through PCI device MSI data to remap, which will + * return the caller after remapping + */ + uint32_t msi_data; + + /** pass-through PCI device is MSI or MSI-X + * 0 - MSI, 1 - MSI-X + */ + int32_t msix; + + /** if the pass-through PCI device is MSI-X, this field contains + * the MSI-X entry table index + */ + int32_t msix_entry_index; + + /** if the pass-through PCI device is MSI-X, this field contains + * Vector Control for MSI-X Entry, field defined in MSI-X spec + */ uint32_t vector_ctl; } __attribute__((aligned(8))); -/* It's designed to support passing DM config data pointer, based on it, - * hypervisor would parse then pass DM defined configration to GUEST vcpu +/** + * @brief The guest config pointer offset. + * + * It's designed to support passing DM config data pointer, based on it, + * hypervisor would parse then pass DM defined configuration to GUEST VCPU * when booting guest VM. * the address 0xd0000 here is designed by DM, as it arranged all memory * layout below 1M, DM should make sure there is no overlap for the address @@ -206,6 +299,10 @@ struct acrn_vm_pci_msix_remap { */ #define GUEST_CFG_OFFSET 0xd0000 +/** + * @brief Info The power state data of a VCPU. + * + */ struct cpu_px_data { uint64_t core_frequency; /* megahertz */ uint64_t power; /* milliWatts */ @@ -215,6 +312,12 @@ struct cpu_px_data { uint64_t status; /* success indicator */ } __attribute__((aligned(8))); +/** + * @brief Info PM command from DM/VHM. + * + * The command would specify request type(i.e. get px count or data) for + * specific VM and specific VCPU with specific state number.like P(n). + */ #define PMCMD_VMID_MASK 0xff000000 #define PMCMD_VCPUID_MASK 0x00ff0000 #define PMCMD_STATE_NUM_MASK 0x0000ff00 @@ -229,4 +332,8 @@ enum pm_cmd_type { PMCMD_GET_PX_DATA, }; +/** + * @} + */ + #endif /* ACRN_COMMON_H */ From fc427f4dd0353a6dd2a2e2f809c822a6e7dfcc42 Mon Sep 17 00:00:00 2001 From: Yin Fengwei Date: Fri, 31 Aug 2018 10:59:00 +0800 Subject: [PATCH 0056/1103] sos_kernel: export restart vm function to DM. Two major changes: - Add ioctl interface to invoke vm restart between DM and vhm - Add hypercall interface to invoke vm restart between vhm and hv Change-Id: If5d1555b2fe7b6e3ef9dad2c471b67ff1ac888c9 Signed-off-by: Yin Fengwei --- drivers/char/vhm/vhm_dev.c | 9 +++++++++ drivers/vhm/vhm_hypercall.c | 5 +++++ include/linux/vhm/acrn_hv_defs.h | 1 + include/linux/vhm/vhm_hypercall.h | 1 + include/linux/vhm/vhm_ioctl_defs.h | 1 + 5 files changed, 17 insertions(+) diff --git a/drivers/char/vhm/vhm_dev.c b/drivers/char/vhm/vhm_dev.c index 5000ed80e177..29b962405f9d 100644 --- a/drivers/char/vhm/vhm_dev.c +++ b/drivers/char/vhm/vhm_dev.c @@ -235,6 +235,15 @@ static long vhm_dev_ioctl(struct file *filep, break; } + case IC_RESTART_VM: { + ret = hcall_restart_vm(vm->vmid); + if (ret < 0) { + pr_err("vhm: failed to restart VM %ld!\n", vm->vmid); + return -EFAULT; + } + break; + } + case IC_DESTROY_VM: { ret = hcall_destroy_vm(vm->vmid); if (ret < 0) { diff --git a/drivers/vhm/vhm_hypercall.c b/drivers/vhm/vhm_hypercall.c index df87febaf60d..463cd71d4b93 100644 --- a/drivers/vhm/vhm_hypercall.c +++ b/drivers/vhm/vhm_hypercall.c @@ -72,6 +72,11 @@ inline long hcall_pause_vm(unsigned long vmid) return acrn_hypercall1(HC_PAUSE_VM, vmid); } +inline long hcall_restart_vm(unsigned long vmid) +{ + return acrn_hypercall1(HC_RESTART_VM, vmid); +} + inline long hcall_destroy_vm(unsigned long vmid) { return acrn_hypercall1(HC_DESTROY_VM, vmid); diff --git a/include/linux/vhm/acrn_hv_defs.h b/include/linux/vhm/acrn_hv_defs.h index d2da1a760783..8dc5b37510bb 100644 --- a/include/linux/vhm/acrn_hv_defs.h +++ b/include/linux/vhm/acrn_hv_defs.h @@ -76,6 +76,7 @@ #define HC_START_VM _HC_ID(HC_ID, HC_ID_VM_BASE + 0x02) #define HC_PAUSE_VM _HC_ID(HC_ID, HC_ID_VM_BASE + 0x03) #define HC_CREATE_VCPU _HC_ID(HC_ID, HC_ID_VM_BASE + 0x04) +#define HC_RESTART_VM _HC_ID(HC_ID, HC_ID_VM_BASE + 0x05) /* IRQ and Interrupts */ #define HC_ID_IRQ_BASE 0x20UL diff --git a/include/linux/vhm/vhm_hypercall.h b/include/linux/vhm/vhm_hypercall.h index 2372906946d6..38a9cb9e8487 100644 --- a/include/linux/vhm/vhm_hypercall.h +++ b/include/linux/vhm/vhm_hypercall.h @@ -142,6 +142,7 @@ inline long hcall_create_vm(unsigned long vminfo); inline long hcall_start_vm(unsigned long vmid); inline long hcall_pause_vm(unsigned long vmid); inline long hcall_destroy_vm(unsigned long vmid); +inline long hcall_restart_vm(unsigned long vmid); inline long hcall_query_vm_state(unsigned long vmid); inline long hcall_setup_sbuf(unsigned long sbuf_head); inline long hcall_get_cpu_state(unsigned long cmd, unsigned long state_pa); diff --git a/include/linux/vhm/vhm_ioctl_defs.h b/include/linux/vhm/vhm_ioctl_defs.h index 3b05d8228e53..028096bde2ec 100644 --- a/include/linux/vhm/vhm_ioctl_defs.h +++ b/include/linux/vhm/vhm_ioctl_defs.h @@ -72,6 +72,7 @@ #define IC_START_VM _IC_ID(IC_ID, IC_ID_VM_BASE + 0x02) #define IC_PAUSE_VM _IC_ID(IC_ID, IC_ID_VM_BASE + 0x03) #define IC_CREATE_VCPU _IC_ID(IC_ID, IC_ID_VM_BASE + 0x04) +#define IC_RESTART_VM _IC_ID(IC_ID, IC_ID_VM_BASE + 0x05) /* IRQ and Interrupts */ #define IC_ID_IRQ_BASE 0x20UL From 5a643e2784ee72e12a6903992acc248760c6956f Mon Sep 17 00:00:00 2001 From: Victor Sun Date: Thu, 19 Apr 2018 00:15:43 +0800 Subject: [PATCH 0057/1103] VHM: add service to support cx data transition Like Acrn px enabling, the cx data is also hard coded within HV, DM will get hard coded cx data to build DSDT for UOS. With this DSDT, UOS would have capability on Cx control if acpi-idle driver is enabled in kernel. Change-Id: I34cf5d99a7458ced51a52789027b0451e40a20bb Signed-off-by: Victor Sun --- drivers/char/vhm/vhm_dev.c | 21 +++++++++++++++++---- include/linux/vhm/acrn_common.h | 32 ++++++++++++++++++++++++++++++-- 2 files changed, 47 insertions(+), 6 deletions(-) diff --git a/drivers/char/vhm/vhm_dev.c b/drivers/char/vhm/vhm_dev.c index 29b962405f9d..fe8d16df129f 100644 --- a/drivers/char/vhm/vhm_dev.c +++ b/drivers/char/vhm/vhm_dev.c @@ -549,15 +549,16 @@ static long vhm_dev_ioctl(struct file *filep, return -EFAULT; switch (cmd & PMCMD_TYPE_MASK) { - case PMCMD_GET_PX_CNT: { - uint8_t px_cnt; + case PMCMD_GET_PX_CNT: + case PMCMD_GET_CX_CNT: { + uint64_t pm_info; - ret = hcall_get_cpu_state(cmd, virt_to_phys(&px_cnt)); + ret = hcall_get_cpu_state(cmd, virt_to_phys(&pm_info)); if (ret < 0) return -EFAULT; if (copy_to_user((void *)ioctl_param, - &px_cnt, sizeof(px_cnt))) + &pm_info, sizeof(pm_info))) ret = -EFAULT; break; @@ -574,6 +575,18 @@ static long vhm_dev_ioctl(struct file *filep, ret = -EFAULT; break; } + case PMCMD_GET_CX_DATA: { + struct cpu_cx_data cx_data; + + ret = hcall_get_cpu_state(cmd, virt_to_phys(&cx_data)); + if (ret < 0) + return -EFAULT; + + if (copy_to_user((void *)ioctl_param, + &cx_data, sizeof(cx_data))) + ret = -EFAULT; + break; + } default: ret = -EFAULT; break; diff --git a/include/linux/vhm/acrn_common.h b/include/linux/vhm/acrn_common.h index 40f3444b7e73..00088dcc8d7d 100644 --- a/include/linux/vhm/acrn_common.h +++ b/include/linux/vhm/acrn_common.h @@ -303,6 +303,30 @@ struct acrn_vm_pci_msix_remap { * @brief Info The power state data of a VCPU. * */ + +#define SPACE_SYSTEM_MEMORY 0 +#define SPACE_SYSTEM_IO 1 +#define SPACE_PCI_CONFIG 2 +#define SPACE_Embedded_Control 3 +#define SPACE_SMBUS 4 +#define SPACE_PLATFORM_COMM 10 +#define SPACE_FFixedHW 0x7F + +struct acpi_generic_address { + uint8_t space_id; + uint8_t bit_width; + uint8_t bit_offset; + uint8_t access_size; + uint64_t address; +} __attribute__((aligned(8))); + +struct cpu_cx_data { + struct acpi_generic_address cx_reg; + uint8_t type; + uint32_t latency; + uint64_t power; +} __attribute__((aligned(8))); + struct cpu_px_data { uint64_t core_frequency; /* megahertz */ uint64_t power; /* milliWatts */ @@ -315,8 +339,10 @@ struct cpu_px_data { /** * @brief Info PM command from DM/VHM. * - * The command would specify request type(i.e. get px count or data) for - * specific VM and specific VCPU with specific state number.like P(n). + * The command would specify request type(e.g. get px count or data) for + * specific VM and specific VCPU with specific state number. + * For Px, PMCMD_STATE_NUM means Px number from 0 to (MAX_PSTATE - 1), + * For Cx, PMCMD_STATE_NUM means Cx entry index from 1 to MAX_CX_ENTRY. */ #define PMCMD_VMID_MASK 0xff000000 #define PMCMD_VCPUID_MASK 0x00ff0000 @@ -330,6 +356,8 @@ struct cpu_px_data { enum pm_cmd_type { PMCMD_GET_PX_CNT, PMCMD_GET_PX_DATA, + PMCMD_GET_CX_CNT, + PMCMD_GET_CX_DATA, }; /** From 3bfa7d3880f309b9cfd96e05a267e57f639f1d3f Mon Sep 17 00:00:00 2001 From: Jason Chen CJ Date: Fri, 31 Aug 2018 10:59:00 +0800 Subject: [PATCH 0058/1103] vhm: add set_memmaps hypercall support This new added hypercall is to support multi regions memmap in one time, which improve the performance. 1 API is added to support set_memmaps hypercall: - int set_memmaps(struct set_memmaps *memmaps) struct set_memmaps is added to present multi region memmap info, which include a page buffer to fill the memmaps array. Signed-off-by: Jason Chen CJ --- drivers/vhm/vhm_hypercall.c | 5 +++++ drivers/vhm/vhm_mm.c | 14 ++++++++++++++ include/linux/vhm/acrn_hv_defs.h | 32 +++++++++++++++++++++++++++++++ include/linux/vhm/acrn_vhm_mm.h | 12 +++++++++++- include/linux/vhm/vhm_hypercall.h | 1 + 5 files changed, 63 insertions(+), 1 deletion(-) diff --git a/drivers/vhm/vhm_hypercall.c b/drivers/vhm/vhm_hypercall.c index 463cd71d4b93..bbdbea8d623c 100644 --- a/drivers/vhm/vhm_hypercall.c +++ b/drivers/vhm/vhm_hypercall.c @@ -97,6 +97,11 @@ inline long hcall_set_memmap(unsigned long vmid, unsigned long memmap) return acrn_hypercall2(HC_VM_SET_MEMMAP, vmid, memmap); } +inline long hcall_set_memmaps(unsigned long pa_memmaps) +{ + return acrn_hypercall1(HC_VM_SET_MEMMAPS, pa_memmaps); +} + inline long hcall_set_ioreq_buffer(unsigned long vmid, unsigned long buffer) { return acrn_hypercall2(HC_SET_IOREQ_BUFFER, vmid, buffer); diff --git a/drivers/vhm/vhm_mm.c b/drivers/vhm/vhm_mm.c index 070327e616d6..75ccd3f09a4e 100644 --- a/drivers/vhm/vhm_mm.c +++ b/drivers/vhm/vhm_mm.c @@ -199,6 +199,20 @@ int unset_mmio_map(unsigned long vmid, unsigned long guest_gpa, 0, 0, MAP_UNMAP); } +int set_memmaps(struct set_memmaps *memmaps) +{ + if (memmaps == NULL) + return -EINVAL; + if (memmaps->memmaps_num > 0) { + if (hcall_set_memmaps(virt_to_phys(memmaps)) < 0) { + pr_err("vhm: failed to set memmaps!\n"); + return -EFAULT; + } + } + + return 0; +} + int update_memmap_attr(unsigned long vmid, unsigned long guest_gpa, unsigned long host_gpa, unsigned long len, unsigned int mem_type, unsigned int mem_access_right) diff --git a/include/linux/vhm/acrn_hv_defs.h b/include/linux/vhm/acrn_hv_defs.h index 8dc5b37510bb..cd0147f732bc 100644 --- a/include/linux/vhm/acrn_hv_defs.h +++ b/include/linux/vhm/acrn_hv_defs.h @@ -94,6 +94,7 @@ #define HC_ID_MEM_BASE 0x40UL #define HC_VM_SET_MEMMAP _HC_ID(HC_ID, HC_ID_MEM_BASE + 0x00) #define HC_VM_GPA2HPA _HC_ID(HC_ID, HC_ID_MEM_BASE + 0x01) +#define HC_VM_SET_MEMMAPS _HC_ID(HC_ID, HC_ID_MEM_BASE + 0x02) /* PCI assignment*/ #define HC_ID_PCI_BASE 0x50UL @@ -149,6 +150,37 @@ struct vm_set_memmap { uint32_t prot; } __attribute__((aligned(8))); +struct memory_map { + uint32_t type; + + /* IN: mem attr */ + uint32_t prot; + + /* IN: beginning guest GPA to map */ + uint64_t remote_gpa; + + /* IN: VM0's GPA which foreign gpa will be mapped to */ + uint64_t vm0_gpa; + + /* IN: length of the range */ + uint64_t length; +} __attribute__((aligned(8))); + +struct set_memmaps { + /*IN: vmid for this hypercall */ + uint64_t vmid; + + /* IN: multi memmaps numbers */ + uint32_t memmaps_num; + + /* IN: + * the gpa of memmaps buffer, point to the memmaps array: + * struct memory_map memmap_array[memmaps_num] + * the max buffer size is one page. + */ + uint64_t memmaps_gpa; +} __attribute__((aligned(8))); + struct sbuf_setup_param { uint32_t pcpu_id; uint32_t sbuf_id; diff --git a/include/linux/vhm/acrn_vhm_mm.h b/include/linux/vhm/acrn_vhm_mm.h index 9be6749d12e2..712860b5f5af 100644 --- a/include/linux/vhm/acrn_vhm_mm.h +++ b/include/linux/vhm/acrn_vhm_mm.h @@ -62,6 +62,7 @@ #include #include +#include /** * acrn_hpa2gpa - physical address conversion @@ -189,7 +190,7 @@ void free_guest_mem(struct vhm_vm *vm); int alloc_guest_memseg(struct vhm_vm *vm, struct vm_memseg *memseg); /** - * map_guest_memseg - map EPT mmapping of memory of guest according to + * map_guest_memseg - set guest mmapping of memory according to * pre-defined memory mapping info * * @vm: pointer to guest vm @@ -207,4 +208,13 @@ int hugepage_map_guest(struct vhm_vm *vm, struct vm_memmap *memmap); void hugepage_free_guest(struct vhm_vm *vm); void *hugepage_map_guest_phys(struct vhm_vm *vm, u64 guest_phys, size_t size); int hugepage_unmap_guest_phys(struct vhm_vm *vm, u64 guest_phys); + +/** + * set_memmaps - set guest mapping for multi regions + * + * @memmaps: pointer to set_memmaps + * + * Return: 0 on success, <0 for error. + */ +int set_memmaps(struct set_memmaps *memmaps); #endif diff --git a/include/linux/vhm/vhm_hypercall.h b/include/linux/vhm/vhm_hypercall.h index 38a9cb9e8487..ea4c3c2e416d 100644 --- a/include/linux/vhm/vhm_hypercall.h +++ b/include/linux/vhm/vhm_hypercall.h @@ -148,6 +148,7 @@ inline long hcall_setup_sbuf(unsigned long sbuf_head); inline long hcall_get_cpu_state(unsigned long cmd, unsigned long state_pa); inline long hcall_set_memmap(unsigned long vmid, unsigned long memmap); +inline long hcall_set_memmaps(unsigned long pa_memmaps); inline long hcall_set_ioreq_buffer(unsigned long vmid, unsigned long buffer); inline long hcall_notify_req_finish(unsigned long vmid, unsigned long vcpu); From f4be14d1efdbc4aead0c4f099f5a23a792032b5b Mon Sep 17 00:00:00 2001 From: Jason Chen CJ Date: Fri, 31 Aug 2018 10:59:00 +0800 Subject: [PATCH 0059/1103] vhm: use set memmaps hypercall for hugetlb If hugetlb is using 2M pages, there may be too many memmap hypercall for ept mapping. To avoid such kind of performance drop, this patch enabled set memmaps hypercall for hugetlb to handle multi memmap hypercall in one time. Signed-off-by: Jason Chen CJ --- drivers/vhm/vhm_hugetlb.c | 62 +++++++++++++++++++++++++++++---------- 1 file changed, 47 insertions(+), 15 deletions(-) diff --git a/drivers/vhm/vhm_hugetlb.c b/drivers/vhm/vhm_hugetlb.c index afab8ab52567..9c39f9167f77 100644 --- a/drivers/vhm/vhm_hugetlb.c +++ b/drivers/vhm/vhm_hugetlb.c @@ -120,10 +120,11 @@ static int add_guest_map(struct vhm_vm *vm, unsigned long vm0_gpa, int hugepage_map_guest(struct vhm_vm *vm, struct vm_memmap *memmap) { - struct page *page; + struct page *page = NULL, *memmaps_buf_pg = NULL; unsigned long len, guest_gpa, vma; - unsigned int type; - unsigned int mem_type, mem_access_right; + struct memory_map *memmap_array; + struct set_memmaps memmaps; + int max_size = PAGE_SIZE/sizeof(struct memory_map); int ret; if (vm == NULL || memmap == NULL) @@ -133,13 +134,23 @@ int hugepage_map_guest(struct vhm_vm *vm, struct vm_memmap *memmap) vma = memmap->vma_base; guest_gpa = memmap->gpa; + /* prepare set_memmaps info */ + memmaps_buf_pg = alloc_page(GFP_KERNEL); + if (memmaps_buf_pg == NULL) + return -ENOMEM; + memmaps.memmaps_num = 0; + memmaps.vmid = vm->vmid; + memmaps.memmaps_gpa = page_to_phys(memmaps_buf_pg); + memmap_array = page_to_virt(memmaps_buf_pg); + while (len > 0) { unsigned long vm0_gpa, pagesize; ret = get_user_pages_fast(vma, 1, 1, &page); if (unlikely(ret != 1) || (page == NULL)) { pr_err("failed to pin huge page!\n"); - return -ENOMEM; + ret = -ENOMEM; + goto err; } vm0_gpa = page_to_phys(page); @@ -148,19 +159,27 @@ int hugepage_map_guest(struct vhm_vm *vm, struct vm_memmap *memmap) ret = add_guest_map(vm, vm0_gpa, guest_gpa, pagesize); if (ret < 0) { pr_err("failed to add memseg for huge page!\n"); - put_page(page); - return ret; + goto err; } - /* TODO: do batch hypercall for multi ept mapping */ - mem_type = MEM_TYPE_WB; - mem_access_right = (memmap->prot & MEM_ACCESS_RIGHT_MASK); - type = MAP_MEM; - if (_mem_set_memmap(vm->vmid, guest_gpa, vm0_gpa, pagesize, - mem_type, mem_access_right, type) < 0) { - pr_err("vhm: failed to set memmap %ld!\n", vm->vmid); - put_page(page); - return -EFAULT; + /* fill each memmap region into memmap_array */ + memmap_array[memmaps.memmaps_num].type = MAP_MEM; + memmap_array[memmaps.memmaps_num].remote_gpa = guest_gpa; + memmap_array[memmaps.memmaps_num].vm0_gpa = vm0_gpa; + memmap_array[memmaps.memmaps_num].length = pagesize; + memmap_array[memmaps.memmaps_num].prot = + MEM_TYPE_WB & MEM_TYPE_MASK; + memmap_array[memmaps.memmaps_num].prot |= + memmap->prot & MEM_ACCESS_RIGHT_MASK; + memmaps.memmaps_num++; + if (memmaps.memmaps_num == max_size) { + pr_info("region buffer full, set & renew memmaps!\n"); + ret = set_memmaps(&memmaps); + if (ret < 0) { + pr_err("failed to set memmaps,ret=%d!\n", ret); + goto err; + } + memmaps.memmaps_num = 0; } len -= pagesize; @@ -168,9 +187,22 @@ int hugepage_map_guest(struct vhm_vm *vm, struct vm_memmap *memmap) guest_gpa += pagesize; } + ret = set_memmaps(&memmaps); + if (ret < 0) { + pr_err("failed to set memmaps, ret=%d!\n", ret); + goto err; + } + + __free_page(memmaps_buf_pg); vm->hugetlb_enabled = 1; return 0; +err: + if (memmaps_buf_pg) + __free_page(memmaps_buf_pg); + if (page) + put_page(page); + return ret; } void hugepage_free_guest(struct vhm_vm *vm) From 09ab5c34ac37622c00affcfb2e0324a0ba076418 Mon Sep 17 00:00:00 2001 From: Jason Chen CJ Date: Fri, 31 Aug 2018 10:59:01 +0800 Subject: [PATCH 0060/1103] vhm: prepare future update for struct vm_set_memmap for back compatible, there is a uint32_t reserved field in struct vm_set_memmap, which will be removed in the future - finally change to struct set_memmap. this patch is preparing such change by change reserved field to prot , prot field to prot_2, and updating both prot & prot_2 during vm_set_memmap setting. Signed-off-by: Jason Chen CJ --- drivers/vhm/vhm_mm.c | 2 +- include/linux/vhm/acrn_hv_defs.h | 7 ++++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/drivers/vhm/vhm_mm.c b/drivers/vhm/vhm_mm.c index 75ccd3f09a4e..fb09ed2f994f 100644 --- a/drivers/vhm/vhm_mm.c +++ b/drivers/vhm/vhm_mm.c @@ -167,7 +167,7 @@ int _mem_set_memmap(unsigned long vmid, unsigned long guest_gpa, set_memmap.remote_gpa = guest_gpa; set_memmap.vm0_gpa = host_gpa; set_memmap.length = len; - set_memmap.prot = ((mem_type & MEM_TYPE_MASK) | + set_memmap.prot = set_memmap.prot_2 = ((mem_type & MEM_TYPE_MASK) | (mem_access_right & MEM_ACCESS_RIGHT_MASK)); /* hypercall to notify hv the guest EPT setting*/ diff --git a/include/linux/vhm/acrn_hv_defs.h b/include/linux/vhm/acrn_hv_defs.h index cd0147f732bc..d3af0970f7d2 100644 --- a/include/linux/vhm/acrn_hv_defs.h +++ b/include/linux/vhm/acrn_hv_defs.h @@ -135,7 +135,9 @@ struct vm_set_memmap { #define MAP_MMIO 1 #define MAP_UNMAP 2 uint32_t type; - uint32_t reserved; + + /* IN: mem attr */ + uint32_t prot; /* IN: beginning guest GPA to map */ uint64_t remote_gpa; @@ -146,8 +148,7 @@ struct vm_set_memmap { /* IN: length of the range */ uint64_t length; - /* IN: mem attr */ - uint32_t prot; + uint32_t prot_2; } __attribute__((aligned(8))); struct memory_map { From 2d749be28becb21ff0394e47cf8e816f8e55d340 Mon Sep 17 00:00:00 2001 From: "Zheng, Gen" Date: Fri, 31 Aug 2018 10:59:01 +0800 Subject: [PATCH 0061/1103] VHM: bug fix on operating multi-thread synchronization With current code, the ioreq client based on VHM kthread may access client->wq after the client got freed. The acrn_ioreq_destroy_client_pervm should wait for the client thread exit then free its client. So do the following fixes: Make the client threads for vcpu and hyper-dma mark kthread_exit flag as true before exit. Make the task that triggered to destroy the client thread, explicitly waits for the kthread_exit flag turnning to true. Signed-off-by: Zheng, Gen Reviewed-by: Chen, Jason CJ Reviewed-by: Zhao, Yakui --- drivers/vhm/vhm_ioreq.c | 26 +++++++++++++++++--------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/drivers/vhm/vhm_ioreq.c b/drivers/vhm/vhm_ioreq.c index 08826c575780..b570b826be95 100644 --- a/drivers/vhm/vhm_ioreq.c +++ b/drivers/vhm/vhm_ioreq.c @@ -91,8 +91,8 @@ struct ioreq_client { */ bool fallback; - bool destroying; - bool kthread_exit; + volatile bool destroying; + volatile bool kthread_exit; /* client covered io ranges - N/A for fallback client */ struct list_head range_list; @@ -260,15 +260,15 @@ static void acrn_ioreq_destroy_client_pervm(struct ioreq_client *client, struct list_head *pos, *tmp; unsigned long flags; - /* blocking operation: notify client for cleanup - * if waitqueue not active, it means client is handling request, - * at that time, we need wait client finish its handling. - */ - while (!waitqueue_active(&client->wq) && !client->kthread_exit) - msleep(10); client->destroying = true; acrn_ioreq_notify_client(client); + /* the client thread will mark kthread_exit flag as true before exit, + * so wait for it exited. + */ + while (!client->kthread_exit) + msleep(10); + spin_lock_irqsave(&client->range_lock, flags); list_for_each_safe(pos, tmp, &client->range_list) { struct ioreq_range *range = @@ -495,6 +495,10 @@ static int ioreq_client_thread(void *data) is_destroying(client))); } + /* the client thread such as for hyper-dma will exit from here, + * so mark kthread_exit as true before exit */ + client->kthread_exit = true; + return 0; } @@ -543,8 +547,12 @@ int acrn_ioreq_attach_client(int client_id, bool check_kthread_stop) is_destroying(client))); } - if (is_destroying(client)) + if (is_destroying(client)) { + /* the client thread for vcpu will exit from here, + * so mark kthread_exit as true before exit */ + client->kthread_exit = true; return 1; + } } return 0; From 1888357f3132671df2e8f7721e8259072381c7af Mon Sep 17 00:00:00 2001 From: Victor Sun Date: Fri, 31 Aug 2018 10:59:01 +0800 Subject: [PATCH 0062/1103] vhm: add hypercall to set sstate data The host ACPI State data which needed by S3/S5 implementation will be parsed in userspace, add one hypercall api to pass these information to Hypervisor. Signed-off-by: Victor Sun Reviewed-by: Tian, Kevin Reviewed-by: Zhao, Yakui --- drivers/char/vhm/vhm_dev.c | 13 +++++++++++++ drivers/vhm/vhm_hypercall.c | 5 +++++ include/linux/vhm/acrn_common.h | 17 +++++++++++++++++ include/linux/vhm/acrn_hv_defs.h | 1 + include/linux/vhm/vhm_hypercall.h | 1 + include/linux/vhm/vhm_ioctl_defs.h | 1 + 6 files changed, 38 insertions(+) diff --git a/drivers/char/vhm/vhm_dev.c b/drivers/char/vhm/vhm_dev.c index fe8d16df129f..92075def6218 100644 --- a/drivers/char/vhm/vhm_dev.c +++ b/drivers/char/vhm/vhm_dev.c @@ -178,6 +178,19 @@ static long vhm_dev_ioctl(struct file *filep, return -EFAULT; return 0; + } else if (ioctl_num == IC_PM_SET_SSTATE_DATA) { + struct acpi_sstate_data host_sstate_data; + + if (copy_from_user(&host_sstate_data, + (void *)ioctl_param, sizeof(host_sstate_data))) + return -EFAULT; + + ret = hcall_set_sstate_data(virt_to_phys(&host_sstate_data)); + if (ret < 0) { + pr_err("vhm: failed to set host Sstate data!"); + return -EFAULT; + } + return 0; } memset(&hc_pt_irq, 0, sizeof(hc_pt_irq)); diff --git a/drivers/vhm/vhm_hypercall.c b/drivers/vhm/vhm_hypercall.c index bbdbea8d623c..93dfb661d133 100644 --- a/drivers/vhm/vhm_hypercall.c +++ b/drivers/vhm/vhm_hypercall.c @@ -87,6 +87,11 @@ inline long hcall_setup_sbuf(unsigned long sbuf_head) return acrn_hypercall1(HC_SETUP_SBUF, sbuf_head); } +inline long hcall_set_sstate_data(unsigned long sx_data_addr) +{ + return acrn_hypercall1(HC_PM_SET_SSTATE_DATA, sx_data_addr); +} + inline long hcall_get_cpu_state(unsigned long cmd, unsigned long state_pa) { return acrn_hypercall2(HC_PM_GET_CPU_STATE, cmd, state_pa); diff --git a/include/linux/vhm/acrn_common.h b/include/linux/vhm/acrn_common.h index 00088dcc8d7d..d45b27f55451 100644 --- a/include/linux/vhm/acrn_common.h +++ b/include/linux/vhm/acrn_common.h @@ -336,6 +336,23 @@ struct cpu_px_data { uint64_t status; /* success indicator */ } __attribute__((aligned(8))); +struct acpi_sstate_pkg { + uint8_t val_pm1a; + uint8_t val_pm1b; + uint16_t reserved; +} __attribute__((aligned(8))); + +struct acpi_sstate_data { + struct acpi_generic_address pm1a_evt; + struct acpi_generic_address pm1b_evt; + struct acpi_generic_address pm1a_cnt; + struct acpi_generic_address pm1b_cnt; + struct acpi_sstate_pkg s3_pkg; + struct acpi_sstate_pkg s5_pkg; + uint32_t *wake_vector_32; + uint64_t *wake_vector_64; +}__attribute__((aligned(8))); + /** * @brief Info PM command from DM/VHM. * diff --git a/include/linux/vhm/acrn_hv_defs.h b/include/linux/vhm/acrn_hv_defs.h index d3af0970f7d2..7cfcf7a30813 100644 --- a/include/linux/vhm/acrn_hv_defs.h +++ b/include/linux/vhm/acrn_hv_defs.h @@ -111,6 +111,7 @@ /* Power management */ #define HC_ID_PM_BASE 0x80UL #define HC_PM_GET_CPU_STATE _HC_ID(HC_ID, HC_ID_PM_BASE + 0x00) +#define HC_PM_SET_SSTATE_DATA _HC_ID(HC_ID, HC_ID_PM_BASE + 0x01) #define ACRN_DOM0_VMID (0UL) #define ACRN_INVALID_VMID (-1) diff --git a/include/linux/vhm/vhm_hypercall.h b/include/linux/vhm/vhm_hypercall.h index ea4c3c2e416d..49e94ecc37b9 100644 --- a/include/linux/vhm/vhm_hypercall.h +++ b/include/linux/vhm/vhm_hypercall.h @@ -145,6 +145,7 @@ inline long hcall_destroy_vm(unsigned long vmid); inline long hcall_restart_vm(unsigned long vmid); inline long hcall_query_vm_state(unsigned long vmid); inline long hcall_setup_sbuf(unsigned long sbuf_head); +inline long hcall_set_sstate_data(unsigned long sx_data_addr); inline long hcall_get_cpu_state(unsigned long cmd, unsigned long state_pa); inline long hcall_set_memmap(unsigned long vmid, unsigned long memmap); diff --git a/include/linux/vhm/vhm_ioctl_defs.h b/include/linux/vhm/vhm_ioctl_defs.h index 028096bde2ec..a9e71616cc80 100644 --- a/include/linux/vhm/vhm_ioctl_defs.h +++ b/include/linux/vhm/vhm_ioctl_defs.h @@ -105,6 +105,7 @@ /* Power management */ #define IC_ID_PM_BASE 0x60UL #define IC_PM_GET_CPU_STATE _IC_ID(IC_ID, IC_ID_PM_BASE + 0x00) +#define IC_PM_SET_SSTATE_DATA _IC_ID(IC_ID, IC_ID_PM_BASE + 0x01) /** * struct vm_memseg - memory segment info for guest From 500eec86addca71b22f135f7b6841026c1e634bd Mon Sep 17 00:00:00 2001 From: Xiangyang Wu Date: Fri, 31 Aug 2018 10:59:01 +0800 Subject: [PATCH 0063/1103] VHM:Update cpu id type as uint16_t for struct acrn_create_vcpu Update the cpu id type as uint16_t for struct acrn_create_vcpu in the VHM driver, this structure is for data transfering between the hypervisor and device modle in SOS. Change-Id: I6bfb67cc25d12f24dbc423ea1a0b91d876c9812e Tracked-On: Signed-off-by: Xiangyang Wu --- include/linux/vhm/acrn_common.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/linux/vhm/acrn_common.h b/include/linux/vhm/acrn_common.h index d45b27f55451..7482320a36a8 100644 --- a/include/linux/vhm/acrn_common.h +++ b/include/linux/vhm/acrn_common.h @@ -178,10 +178,10 @@ struct acrn_create_vm { */ struct acrn_create_vcpu { /** the virtual CPU ID for the VCPU created */ - uint32_t vcpu_id; + uint16_t vcpu_id; /** the physical CPU ID for the VCPU created */ - uint32_t pcpu_id; + uint16_t pcpu_id; } __attribute__((aligned(8))); /** From e7f4291eade427963272b2c24d4cfde2306863bc Mon Sep 17 00:00:00 2001 From: Jason Chen CJ Date: Fri, 31 Aug 2018 10:59:01 +0800 Subject: [PATCH 0064/1103] vhm: add sos offline cpu support add sysfs with attr "offline_cpu", use echo cpu_id > /sys/class/vhm/acrn_vhm/offline_cpu to do the hypercall offline/destroy according vcpu. before doing it, please make sure you already did cpu offline with standard flow like below: echo 0 > /sys/devices/system/cpu/cpuX/online Signed-off-by: Jason Chen CJ Reviewed-by: Zhao Yakui Acked-by: Eddie Dong --- drivers/char/vhm/vhm_dev.c | 41 +++++++++++++++++++++++++++++++ drivers/vhm/vhm_hypercall.c | 5 ++++ include/linux/vhm/acrn_hv_defs.h | 1 + include/linux/vhm/vhm_hypercall.h | 1 + 4 files changed, 48 insertions(+) diff --git a/drivers/char/vhm/vhm_dev.c b/drivers/char/vhm/vhm_dev.c index 92075def6218..75cdbb730fa9 100644 --- a/drivers/char/vhm/vhm_dev.c +++ b/drivers/char/vhm/vhm_dev.c @@ -664,6 +664,41 @@ static const struct file_operations fops = { .poll = vhm_dev_poll, }; +static ssize_t +store_offline_cpu(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ +#ifdef CONFIG_X86 + u64 cpu, lapicid; + + if (kstrtoull(buf, 0, &cpu) < 0) + return -EINVAL; + + if (cpu_possible(cpu)) { + lapicid = cpu_data(cpu).apicid; + pr_info("vhm: try to offline cpu %lld with lapicid %lld\n", + cpu, lapicid); + if (hcall_sos_offline_cpu(lapicid) < 0) { + pr_err("vhm: failed to offline cpu from Hypervisor!\n"); + return -EINVAL; + } + } +#endif + return count; +} + +static DEVICE_ATTR(offline_cpu, S_IWUSR, NULL, store_offline_cpu); + +static struct attribute *vhm_attrs[] = { + &dev_attr_offline_cpu.attr, + NULL +}; + +static struct attribute_group vhm_attr_group = { + .attrs = vhm_attrs, +}; + #define SUPPORT_HV_API_VERSION_MAJOR 1 #define SUPPORT_HV_API_VERSION_MINOR 0 static int __init vhm_init(void) @@ -727,6 +762,11 @@ static int __init vhm_init(void) x86_platform_ipi_callback = vhm_intr_handler; local_irq_restore(flag); + if (sysfs_create_group(&vhm_device->kobj, &vhm_attr_group)) { + pr_warn("vhm: sysfs create failed\n"); + return -EINVAL; + } + pr_info("vhm: Virtio & Hypervisor service module initialized\n"); return 0; } @@ -737,6 +777,7 @@ static void __exit vhm_exit(void) class_unregister(vhm_class); class_destroy(vhm_class); unregister_chrdev(major, DEVICE_NAME); + sysfs_remove_group(&vhm_device->kobj, &vhm_attr_group); pr_info("vhm: exit\n"); } diff --git a/drivers/vhm/vhm_hypercall.c b/drivers/vhm/vhm_hypercall.c index 93dfb661d133..4c94d8f962ad 100644 --- a/drivers/vhm/vhm_hypercall.c +++ b/drivers/vhm/vhm_hypercall.c @@ -52,6 +52,11 @@ #include #include +inline long hcall_sos_offline_cpu(unsigned long cpu) +{ + return acrn_hypercall1(HC_SOS_OFFLINE_CPU, cpu); +} + inline long hcall_get_api_version(unsigned long api_version) { return acrn_hypercall1(HC_GET_API_VERSION, api_version); diff --git a/include/linux/vhm/acrn_hv_defs.h b/include/linux/vhm/acrn_hv_defs.h index 7cfcf7a30813..b9465a87fe46 100644 --- a/include/linux/vhm/acrn_hv_defs.h +++ b/include/linux/vhm/acrn_hv_defs.h @@ -68,6 +68,7 @@ /* general */ #define HC_ID_GEN_BASE 0x0UL #define HC_GET_API_VERSION _HC_ID(HC_ID, HC_ID_GEN_BASE + 0x00) +#define HC_SOS_OFFLINE_CPU _HC_ID(HC_ID, HC_ID_GEN_BASE + 0x01) /* VM management */ #define HC_ID_VM_BASE 0x10UL diff --git a/include/linux/vhm/vhm_hypercall.h b/include/linux/vhm/vhm_hypercall.h index 49e94ecc37b9..7d4b15af39a0 100644 --- a/include/linux/vhm/vhm_hypercall.h +++ b/include/linux/vhm/vhm_hypercall.h @@ -137,6 +137,7 @@ static inline long acrn_hypercall4(unsigned long hcall_id, unsigned long param1, return result; } +inline long hcall_sos_offline_cpu(unsigned long cpu); inline long hcall_get_api_version(unsigned long api_version); inline long hcall_create_vm(unsigned long vminfo); inline long hcall_start_vm(unsigned long vmid); From 1ec8237f46bf83ea82d69b3901ae0466da35a204 Mon Sep 17 00:00:00 2001 From: Xinyun Liu Date: Fri, 31 Aug 2018 10:59:01 +0800 Subject: [PATCH 0065/1103] vhm: Fix kernel-doc issues Some comments are not in kernel-doc format so got error like: include/linux/vhm/vhm_vm_mngt.h:128: error: Cannot parse struct or union! Some are typo or not updated,eg: include/linux/vhm/acrn_vhm_mm.h:93: warning: Excess function parameter 'uos_phy' description in 'map_guest_phys' V2: More typo fix and re-wording on Geoffroy's suggestion V1: Fixed kernel-doc format issue Signed-off-by: Xinyun Liu Reviewed-by: Geoffroy Van Cutsem Reviewed-by: Eddie Dong --- include/linux/vhm/acrn_vhm_mm.h | 12 +++++------- include/linux/vhm/vhm_ioctl_defs.h | 10 +++++----- include/linux/vhm/vhm_vm_mngt.h | 16 ++++++++-------- 3 files changed, 18 insertions(+), 20 deletions(-) diff --git a/include/linux/vhm/acrn_vhm_mm.h b/include/linux/vhm/acrn_vhm_mm.h index 712860b5f5af..87d668f735dc 100644 --- a/include/linux/vhm/acrn_vhm_mm.h +++ b/include/linux/vhm/acrn_vhm_mm.h @@ -80,12 +80,10 @@ static inline unsigned long acrn_hpa2gpa(unsigned long hpa) } /** - * map_guest_phys - map guest physical address - * - * to SOS kernel virtual address + * map_guest_phys - map guest physical address to SOS kernel virtual address * * @vmid: guest vmid - * @uos_phy: phsical address in guest + * @uos_phys: physical address in guest * @size: the memory size mapped * * Return: SOS kernel virtual address, NULL on error @@ -96,7 +94,7 @@ void *map_guest_phys(unsigned long vmid, u64 uos_phys, size_t size); * unmap_guest_phys - unmap guest physical address * * @vmid: guest vmid - * @uos_phy: phsical address in guest + * @uos_phys: physical address in guest * * Return: 0 on success, <0 for error. */ @@ -109,7 +107,7 @@ int unmap_guest_phys(unsigned long vmid, u64 uos_phys); * @guest_gpa: gpa of UOS * @host_gpa: gpa of SOS * @len: memory mapped length - * @mem_type: memory mapping type. Possilble value could be: + * @mem_type: memory mapping type. Possible value could be: * MEM_TYPE_WB * MEM_TYPE_WT * MEM_TYPE_UC @@ -147,7 +145,7 @@ int unset_mmio_map(unsigned long vmid, unsigned long guest_gpa, * @guest_gpa: gpa of UOS * @host_gpa: gpa of SOS * @len: memory mapped length - * @mem_type: memory mapping type. Possilble value could be: + * @mem_type: memory mapping type. Possible value could be: * MEM_TYPE_WB * MEM_TYPE_WT * MEM_TYPE_UC diff --git a/include/linux/vhm/vhm_ioctl_defs.h b/include/linux/vhm/vhm_ioctl_defs.h index a9e71616cc80..9a7189ffffc4 100644 --- a/include/linux/vhm/vhm_ioctl_defs.h +++ b/include/linux/vhm/vhm_ioctl_defs.h @@ -52,11 +52,11 @@ #ifndef _VHM_IOCTL_DEFS_H_ #define _VHM_IOCTL_DEFS_H_ -/* Commmon structures for ACRN/VHM/DM */ +/* Common structures for ACRN/VHM/DM */ #include "acrn_common.h" /* - * Commmon IOCTL ID defination for VHM/DM + * Common IOCTL ID definition for VHM/DM */ #define _IC_ID(x, y) (((x)<<24)|(y)) #define IC_ID 0x43UL @@ -162,7 +162,7 @@ struct ic_ptdev_irq { uint32_t type; /** @virt_bdf: virtual bdf description of pass thru device */ uint16_t virt_bdf; /* IN: Device virtual BDF# */ - /** @phy_bdf: physical bdf description of pass thru device */ + /** @phys_bdf: physical bdf description of pass thru device */ uint16_t phys_bdf; /* IN: Device physical BDF# */ /** union */ union { @@ -172,7 +172,7 @@ struct ic_ptdev_irq { uint32_t virt_pin; /** @phys_pin: physical IOAPIC pin */ uint32_t phys_pin; - /** @pic_pin: PIC pin */ + /** @is_pic_pin: PIC pin */ uint32_t is_pic_pin; } intx; @@ -192,7 +192,7 @@ struct ic_ptdev_irq { }; /** - * struct ioreq_notify - data strcture to notify hypervisor ioreq is handled + * struct ioreq_notify - data structure to notify hypervisor ioreq is handled * * @client_id: client id to identify ioreq client * @vcpu: identify the ioreq submitter diff --git a/include/linux/vhm/vhm_vm_mngt.h b/include/linux/vhm/vhm_vm_mngt.h index f0a7e1cf7b05..14e9fe7a4d5f 100644 --- a/include/linux/vhm/vhm_vm_mngt.h +++ b/include/linux/vhm/vhm_vm_mngt.h @@ -88,6 +88,7 @@ extern struct mutex vhm_vm_list_lock; * @ioreq_client_list: list of ioreq clients * @req_buf: request buffer shared between HV, SOS and UOS * @pg: pointer to linux page which holds req_buf + * @hugetlb_enabled: flag to enable/disable hugetlb page ept mapping */ struct vhm_vm { struct device *dev; @@ -119,7 +120,7 @@ struct vm_info { }; /** - * struct find_get_vm - find and hold vhm_vm of guest according to guest vmid + * find_get_vm() - find and keep guest vhm_vm based on the vmid * * @vmid: guest vmid * @@ -128,17 +129,16 @@ struct vm_info { struct vhm_vm *find_get_vm(unsigned long vmid); /** - * struct put_vm - release vhm_vm of guest according to guest vmid + * put_vm() - release vhm_vm of guest according to guest vmid * If the latest reference count drops to zero, free vhm_vm as well - * - * @vm: pointer to vhm_vm which identrify specific guest + * @vm: pointer to vhm_vm which identify specific guest * * Return: */ void put_vm(struct vhm_vm *vm); /** - * struct vhm_get_vm_info - get vm_info of specific guest + * vhm_get_vm_info() - get vm_info of specific guest * * @vmid: guest vmid * @info: pointer to vm_info for returned vm_info @@ -148,7 +148,7 @@ void put_vm(struct vhm_vm *vm); int vhm_get_vm_info(unsigned long vmid, struct vm_info *info); /** - * struct vhm_inject_msi - inject MSI interrupt to guest + * vhm_inject_msi() - inject MSI interrupt to guest * * @vmid: guest vmid * @msi_addr: MSI addr matches MSI spec @@ -160,11 +160,11 @@ int vhm_inject_msi(unsigned long vmid, unsigned long msi_addr, unsigned long msi_data); /** - * struct vhm_vm_gpa2hpa - convert guest physical address to + * vhm_vm_gpa2hpa() - convert guest physical address to * host physical address * * @vmid: guest vmid - * @gap: guest physical address + * @gpa: guest physical address * * Return: host physical address, <0 on error */ From 80284496c38d16811befe89024a32fafbc4591ea Mon Sep 17 00:00:00 2001 From: Mingqiang Chi Date: Fri, 31 Aug 2018 10:59:01 +0800 Subject: [PATCH 0066/1103] vhm: add trusty init/de-init support vhm will allocate trusty memory from cma then do ept map for a VM with trusty. vhm will de-init trusty for a VM during its destroying. Signed-off-by: Mingqiang Chi Signed-off-by: Jason Chen CJ Reviewed-by: Zhao Yakui Acked-by: Eddie Dong --- drivers/char/vhm/vhm_dev.c | 10 ++++++++++ drivers/vhm/vhm_mm.c | 25 +++++++++++++++++++++++++ include/linux/vhm/acrn_vhm_mm.h | 3 +++ include/linux/vhm/vhm_vm_mngt.h | 1 + 4 files changed, 39 insertions(+) diff --git a/drivers/char/vhm/vhm_dev.c b/drivers/char/vhm/vhm_dev.c index 75cdbb730fa9..964aee295cda 100644 --- a/drivers/char/vhm/vhm_dev.c +++ b/drivers/char/vhm/vhm_dev.c @@ -226,6 +226,14 @@ static long vhm_dev_ioctl(struct file *filep, vm->vmid = created_vm.vmid; + if (created_vm.vm_flag & SECURE_WORLD_ENABLED) { + ret = init_trusty(vm); + if (ret < 0) { + pr_err("vhm: failed to init trusty for VM!\n"); + return ret; + } + } + pr_info("vhm: VM %d created\n", created_vm.vmid); break; } @@ -258,6 +266,8 @@ static long vhm_dev_ioctl(struct file *filep, } case IC_DESTROY_VM: { + if (vm->trusty_host_gpa) + deinit_trusty(vm); ret = hcall_destroy_vm(vm->vmid); if (ret < 0) { pr_err("failed to destroy VM %ld\n", vm->vmid); diff --git a/drivers/vhm/vhm_mm.c b/drivers/vhm/vhm_mm.c index fb09ed2f994f..bff448208836 100644 --- a/drivers/vhm/vhm_mm.c +++ b/drivers/vhm/vhm_mm.c @@ -305,6 +305,31 @@ int check_guest_mem(struct vhm_vm *vm) return 0; } +#define TRUSTY_MEM_GPA_BASE (511UL * 1024UL * 1024UL * 1024UL) +#define TRUSTY_MEM_SIZE (0x01000000) +int init_trusty(struct vhm_vm *vm) +{ + unsigned long host_gpa, guest_gpa = TRUSTY_MEM_GPA_BASE; + unsigned long len = TRUSTY_MEM_SIZE; + + host_gpa = _alloc_memblk(vm->dev, TRUSTY_MEM_SIZE); + if (host_gpa == 0ULL) + return -ENOMEM; + + vm->trusty_host_gpa = host_gpa; + + pr_info("VHM: set ept for trusty memory [host_gpa=0x%lx, " + "guest_gpa=0x%lx, len=0x%lx]", host_gpa, guest_gpa, len); + return _mem_set_memmap(vm->vmid, guest_gpa, host_gpa, len, + MEM_TYPE_WB, MEM_ACCESS_RWX, MAP_MEM); +} + +void deinit_trusty(struct vhm_vm *vm) +{ + _free_memblk(vm->dev, vm->trusty_host_gpa, TRUSTY_MEM_SIZE); + vm->trusty_host_gpa = 0; +} + static void guest_vm_open(struct vm_area_struct *vma) { struct vhm_vm *vm = vma->vm_file->private_data; diff --git a/include/linux/vhm/acrn_vhm_mm.h b/include/linux/vhm/acrn_vhm_mm.h index 87d668f735dc..5ff9af92f81f 100644 --- a/include/linux/vhm/acrn_vhm_mm.h +++ b/include/linux/vhm/acrn_vhm_mm.h @@ -198,6 +198,9 @@ int alloc_guest_memseg(struct vhm_vm *vm, struct vm_memseg *memseg); */ int map_guest_memseg(struct vhm_vm *vm, struct vm_memmap *memmap); +int init_trusty(struct vhm_vm *vm); +void deinit_trusty(struct vhm_vm *vm); + int _mem_set_memmap(unsigned long vmid, unsigned long guest_gpa, unsigned long host_gpa, unsigned long len, unsigned int mem_type, unsigned int mem_access_right, diff --git a/include/linux/vhm/vhm_vm_mngt.h b/include/linux/vhm/vhm_vm_mngt.h index 14e9fe7a4d5f..b5f6ca1f2dea 100644 --- a/include/linux/vhm/vhm_vm_mngt.h +++ b/include/linux/vhm/vhm_vm_mngt.h @@ -94,6 +94,7 @@ struct vhm_vm { struct device *dev; struct list_head list; unsigned long vmid; + unsigned long trusty_host_gpa; int ioreq_fallback_client; long refcnt; struct mutex seg_lock; From 589dd13db71098d1c634626a9eaa510f4c93aaa4 Mon Sep 17 00:00:00 2001 From: Yin Fengwei Date: Fri, 31 Aug 2018 10:59:01 +0800 Subject: [PATCH 0067/1103] vhm: Rename the restart_vm to reset_vm Signed-off-by: Yin Fengwei Reviewed-by: Zhao Yakui Reviewed-by: Eddie Dong --- drivers/char/vhm/vhm_dev.c | 4 ++-- drivers/vhm/vhm_hypercall.c | 4 ++-- include/linux/vhm/acrn_hv_defs.h | 2 +- include/linux/vhm/vhm_hypercall.h | 2 +- include/linux/vhm/vhm_ioctl_defs.h | 2 +- 5 files changed, 7 insertions(+), 7 deletions(-) diff --git a/drivers/char/vhm/vhm_dev.c b/drivers/char/vhm/vhm_dev.c index 964aee295cda..a67ba6c589f9 100644 --- a/drivers/char/vhm/vhm_dev.c +++ b/drivers/char/vhm/vhm_dev.c @@ -256,8 +256,8 @@ static long vhm_dev_ioctl(struct file *filep, break; } - case IC_RESTART_VM: { - ret = hcall_restart_vm(vm->vmid); + case IC_RESET_VM: { + ret = hcall_reset_vm(vm->vmid); if (ret < 0) { pr_err("vhm: failed to restart VM %ld!\n", vm->vmid); return -EFAULT; diff --git a/drivers/vhm/vhm_hypercall.c b/drivers/vhm/vhm_hypercall.c index 4c94d8f962ad..2c51c366c60f 100644 --- a/drivers/vhm/vhm_hypercall.c +++ b/drivers/vhm/vhm_hypercall.c @@ -77,9 +77,9 @@ inline long hcall_pause_vm(unsigned long vmid) return acrn_hypercall1(HC_PAUSE_VM, vmid); } -inline long hcall_restart_vm(unsigned long vmid) +inline long hcall_reset_vm(unsigned long vmid) { - return acrn_hypercall1(HC_RESTART_VM, vmid); + return acrn_hypercall1(HC_RESET_VM, vmid); } inline long hcall_destroy_vm(unsigned long vmid) diff --git a/include/linux/vhm/acrn_hv_defs.h b/include/linux/vhm/acrn_hv_defs.h index b9465a87fe46..f51f56b58147 100644 --- a/include/linux/vhm/acrn_hv_defs.h +++ b/include/linux/vhm/acrn_hv_defs.h @@ -77,7 +77,7 @@ #define HC_START_VM _HC_ID(HC_ID, HC_ID_VM_BASE + 0x02) #define HC_PAUSE_VM _HC_ID(HC_ID, HC_ID_VM_BASE + 0x03) #define HC_CREATE_VCPU _HC_ID(HC_ID, HC_ID_VM_BASE + 0x04) -#define HC_RESTART_VM _HC_ID(HC_ID, HC_ID_VM_BASE + 0x05) +#define HC_RESET_VM _HC_ID(HC_ID, HC_ID_VM_BASE + 0x05) /* IRQ and Interrupts */ #define HC_ID_IRQ_BASE 0x20UL diff --git a/include/linux/vhm/vhm_hypercall.h b/include/linux/vhm/vhm_hypercall.h index 7d4b15af39a0..4de5e46b9d0f 100644 --- a/include/linux/vhm/vhm_hypercall.h +++ b/include/linux/vhm/vhm_hypercall.h @@ -143,7 +143,7 @@ inline long hcall_create_vm(unsigned long vminfo); inline long hcall_start_vm(unsigned long vmid); inline long hcall_pause_vm(unsigned long vmid); inline long hcall_destroy_vm(unsigned long vmid); -inline long hcall_restart_vm(unsigned long vmid); +inline long hcall_reset_vm(unsigned long vmid); inline long hcall_query_vm_state(unsigned long vmid); inline long hcall_setup_sbuf(unsigned long sbuf_head); inline long hcall_set_sstate_data(unsigned long sx_data_addr); diff --git a/include/linux/vhm/vhm_ioctl_defs.h b/include/linux/vhm/vhm_ioctl_defs.h index 9a7189ffffc4..ec560621ca73 100644 --- a/include/linux/vhm/vhm_ioctl_defs.h +++ b/include/linux/vhm/vhm_ioctl_defs.h @@ -72,7 +72,7 @@ #define IC_START_VM _IC_ID(IC_ID, IC_ID_VM_BASE + 0x02) #define IC_PAUSE_VM _IC_ID(IC_ID, IC_ID_VM_BASE + 0x03) #define IC_CREATE_VCPU _IC_ID(IC_ID, IC_ID_VM_BASE + 0x04) -#define IC_RESTART_VM _IC_ID(IC_ID, IC_ID_VM_BASE + 0x05) +#define IC_RESET_VM _IC_ID(IC_ID, IC_ID_VM_BASE + 0x05) /* IRQ and Interrupts */ #define IC_ID_IRQ_BASE 0x20UL From 85bef16aa5dc2f27f4629222be45ab59dcd80f5d Mon Sep 17 00:00:00 2001 From: Xinyun Liu Date: Fri, 31 Aug 2018 10:59:01 +0800 Subject: [PATCH 0068/1103] vhm: fix kerneldoc format remove doxygen commands and add missing description Signed-off-by: Xinyun Liu Reviewed-by: Mingqiang Chi Acked-by: Yakui Zhao --- include/linux/vhm/acrn_vhm_ioreq.h | 4 +--- include/linux/vhm/acrn_vhm_mm.h | 4 +--- include/linux/vhm/vhm_vm_mngt.h | 5 ++--- 3 files changed, 4 insertions(+), 9 deletions(-) diff --git a/include/linux/vhm/acrn_vhm_ioreq.h b/include/linux/vhm/acrn_vhm_ioreq.h index de3a8aa4eaf6..fbf69b37d356 100644 --- a/include/linux/vhm/acrn_vhm_ioreq.h +++ b/include/linux/vhm/acrn_vhm_ioreq.h @@ -52,9 +52,7 @@ */ /** - * @file acrn_vhm_ioreq.h - * - * @brief Virtio and Hypervisor Module(VHM) ioreq APIs + * DOC: Virtio and Hypervisor Module(VHM) ioreq APIs */ #ifndef __ACRN_VHM_IOREQ_H__ diff --git a/include/linux/vhm/acrn_vhm_mm.h b/include/linux/vhm/acrn_vhm_mm.h index 5ff9af92f81f..21269e47b26a 100644 --- a/include/linux/vhm/acrn_vhm_mm.h +++ b/include/linux/vhm/acrn_vhm_mm.h @@ -52,9 +52,7 @@ */ /** - * @file acrn_vhm_mm.h - * - * @brief Virtio and Hypervisor Module memory manager APIs + * DOC: Virtio and Hypervisor Module memory manager APIs */ #ifndef __ACRN_VHM_MM_H__ diff --git a/include/linux/vhm/vhm_vm_mngt.h b/include/linux/vhm/vhm_vm_mngt.h index b5f6ca1f2dea..e89b78c7fff5 100644 --- a/include/linux/vhm/vhm_vm_mngt.h +++ b/include/linux/vhm/vhm_vm_mngt.h @@ -55,9 +55,7 @@ */ /** - * @file vhm_vm_mngt.h - * - * @brief Virtio and Hypervisor Module(VHM) management APIs + * DOC: brief Virtio and Hypervisor Module(VHM) management APIs */ #ifndef VHM_VM_MNGT_H #define VHM_VM_MNGT_H @@ -77,6 +75,7 @@ extern struct mutex vhm_vm_list_lock; * @dev: pointer to dev of linux device mode * @list: list of vhm_vm * @vmid: guest vmid + * @trusty_host_gpa: host physical address of continuous memory for Trusty * @ioreq_fallback_client: default ioreq client * @refcnt: reference count of guest * @seg_lock: mutex to protect memseg_list From d91ef40202358cb204710e488fec6225c4fe40c0 Mon Sep 17 00:00:00 2001 From: "Li, Fei1" Date: Fri, 31 Aug 2018 10:59:02 +0800 Subject: [PATCH 0069/1103] sos: vhm: remove set guest memory map by CMA We removed CMA Device Manager memory allocation mechanisms and use hugetlb as the only Device Manager memory allocation mechanism. So there is no needs to support set guest vm memory by CMA any more. Signed-off-by: Li, Fei1 Acked-by: Anthony Xu --- drivers/char/vhm/vhm_dev.c | 14 -- drivers/vhm/vhm_mm.c | 252 ++--------------------------- include/linux/vhm/acrn_vhm_mm.h | 13 -- include/linux/vhm/vhm_ioctl_defs.h | 1 + include/linux/vhm/vhm_vm_mngt.h | 4 - 5 files changed, 15 insertions(+), 269 deletions(-) diff --git a/drivers/char/vhm/vhm_dev.c b/drivers/char/vhm/vhm_dev.c index a67ba6c589f9..3dca3b3679a2 100644 --- a/drivers/char/vhm/vhm_dev.c +++ b/drivers/char/vhm/vhm_dev.c @@ -122,9 +122,6 @@ static int vhm_dev_open(struct inode *inodep, struct file *filep) vm->vmid = ACRN_INVALID_VMID; vm->dev = vhm_device; - INIT_LIST_HEAD(&vm->memseg_list); - mutex_init(&vm->seg_lock); - for (i = 0; i < HUGEPAGE_HLIST_ARRAY_SIZE; i++) INIT_HLIST_HEAD(&vm->hugepage_hlist[i]); mutex_init(&vm->hugepage_lock); @@ -294,16 +291,6 @@ static long vhm_dev_ioctl(struct file *filep, return ret; } - case IC_ALLOC_MEMSEG: { - struct vm_memseg memseg; - - if (copy_from_user(&memseg, (void *)ioctl_param, - sizeof(struct vm_memseg))) - return -EFAULT; - - return alloc_guest_memseg(vm, &memseg); - } - case IC_SET_MEMSEG: { struct vm_memmap memmap; @@ -668,7 +655,6 @@ static const struct file_operations fops = { .open = vhm_dev_open, .read = vhm_dev_read, .write = vhm_dev_write, - .mmap = vhm_dev_mmap, .release = vhm_dev_release, .unlocked_ioctl = vhm_dev_ioctl, .poll = vhm_dev_poll, diff --git a/drivers/vhm/vhm_mm.c b/drivers/vhm/vhm_mm.c index bff448208836..3c0c2acbe522 100644 --- a/drivers/vhm/vhm_mm.c +++ b/drivers/vhm/vhm_mm.c @@ -76,14 +76,6 @@ #include #include -struct guest_memseg { - struct list_head list; - u64 vm0_gpa; - size_t len; - u64 gpa; - long vma_count; -}; - static u64 _alloc_memblk(struct device *dev, size_t len) { unsigned int count; @@ -110,52 +102,6 @@ static bool _free_memblk(struct device *dev, u64 vm0_gpa, size_t len) return dma_release_from_contiguous(dev, page, count); } -static int add_guest_memseg(struct vhm_vm *vm, unsigned long vm0_gpa, - unsigned long guest_gpa, unsigned long len) -{ - struct guest_memseg *seg; - int max_gfn; - - seg = kzalloc(sizeof(struct guest_memseg), GFP_KERNEL); - if (seg == NULL) - return -ENOMEM; - - seg->vm0_gpa = vm0_gpa; - seg->gpa = guest_gpa; - seg->len = len; - - max_gfn = (seg->gpa + seg->len) >> PAGE_SHIFT; - if (vm->max_gfn < max_gfn) - vm->max_gfn = max_gfn; - - pr_info("VHM: add memseg with len=0x%lx, vm0_gpa=0x%llx," - " and its guest gpa = 0x%llx, vm max_gfn 0x%x\n", - seg->len, seg->vm0_gpa, seg->gpa, vm->max_gfn); - - seg->vma_count = 0; - mutex_lock(&vm->seg_lock); - list_add(&seg->list, &vm->memseg_list); - mutex_unlock(&vm->seg_lock); - - return 0; -} - -int alloc_guest_memseg(struct vhm_vm *vm, struct vm_memseg *memseg) -{ - unsigned long vm0_gpa; - int ret; - - vm0_gpa = _alloc_memblk(vm->dev, memseg->len); - if (vm0_gpa == 0ULL) - return -ENOMEM; - - ret = add_guest_memseg(vm, vm0_gpa, memseg->gpa, memseg->len); - if (ret < 0) - _free_memblk(vm->dev, vm0_gpa, memseg->len); - - return ret; -} - int _mem_set_memmap(unsigned long vmid, unsigned long guest_gpa, unsigned long host_gpa, unsigned long len, unsigned int mem_type, unsigned int mem_access_right, @@ -223,7 +169,6 @@ int update_memmap_attr(unsigned long vmid, unsigned long guest_gpa, int map_guest_memseg(struct vhm_vm *vm, struct vm_memmap *memmap) { - struct guest_memseg *seg = NULL; unsigned int type; unsigned int mem_type, mem_access_right; unsigned long guest_gpa, host_gpa; @@ -232,77 +177,31 @@ int map_guest_memseg(struct vhm_vm *vm, struct vm_memmap *memmap) if (memmap->type == VM_MEMMAP_SYSMEM && memmap->using_vma) return hugepage_map_guest(vm, memmap); - mutex_lock(&vm->seg_lock); - - /* cma or mmio */ - if (memmap->type == VM_MEMMAP_SYSMEM) { - list_for_each_entry(seg, &vm->memseg_list, list) { - if (seg->gpa == memmap->gpa - && seg->len == memmap->len) - break; - } - if (&seg->list == &vm->memseg_list) { - mutex_unlock(&vm->seg_lock); - return -EINVAL; - } - guest_gpa = seg->gpa; - host_gpa = seg->vm0_gpa; - mem_type = MEM_TYPE_WB; - mem_access_right = (memmap->prot & MEM_ACCESS_RIGHT_MASK); - type = MAP_MEM; - } else { - guest_gpa = memmap->gpa; - host_gpa = acrn_hpa2gpa(memmap->hpa); - mem_type = MEM_TYPE_UC; - mem_access_right = (memmap->prot & MEM_ACCESS_RIGHT_MASK); - type = MAP_MMIO; + /* mmio */ + if (memmap->type != VM_MEMMAP_MMIO) { + pr_err("vhm: %s invalid memmap type: %d\n", + __func__, memmap->type); + return -EINVAL; } + guest_gpa = memmap->gpa; + host_gpa = acrn_hpa2gpa(memmap->hpa); + mem_type = MEM_TYPE_UC; + mem_access_right = (memmap->prot & MEM_ACCESS_RIGHT_MASK); + type = MAP_MMIO; if (_mem_set_memmap(vm->vmid, guest_gpa, host_gpa, memmap->len, mem_type, mem_access_right, type) < 0) { pr_err("vhm: failed to set memmap %ld!\n", vm->vmid); - mutex_unlock(&vm->seg_lock); return -EFAULT; } - mutex_unlock(&vm->seg_lock); - return 0; } void free_guest_mem(struct vhm_vm *vm) { - struct guest_memseg *seg; - if (vm->hugetlb_enabled) return hugepage_free_guest(vm); - - mutex_lock(&vm->seg_lock); - while (!list_empty(&vm->memseg_list)) { - seg = list_first_entry(&vm->memseg_list, - struct guest_memseg, list); - if (!_free_memblk(vm->dev, seg->vm0_gpa, seg->len)) - pr_warn("failed to free memblk\n"); - list_del(&seg->list); - kfree(seg); - } - mutex_unlock(&vm->seg_lock); -} - -int check_guest_mem(struct vhm_vm *vm) -{ - struct guest_memseg *seg; - - mutex_lock(&vm->seg_lock); - list_for_each_entry(seg, &vm->memseg_list, list) { - if (seg->vma_count == 0) - continue; - - mutex_unlock(&vm->seg_lock); - return -EAGAIN; - } - mutex_unlock(&vm->seg_lock); - return 0; } #define TRUSTY_MEM_GPA_BASE (511UL * 1024UL * 1024UL * 1024UL) @@ -330,121 +229,17 @@ void deinit_trusty(struct vhm_vm *vm) vm->trusty_host_gpa = 0; } -static void guest_vm_open(struct vm_area_struct *vma) -{ - struct vhm_vm *vm = vma->vm_file->private_data; - struct guest_memseg *seg = vma->vm_private_data; - - mutex_lock(&vm->seg_lock); - seg->vma_count++; - mutex_unlock(&vm->seg_lock); -} - -static void guest_vm_close(struct vm_area_struct *vma) -{ - struct vhm_vm *vm = vma->vm_file->private_data; - struct guest_memseg *seg = vma->vm_private_data; - - mutex_lock(&vm->seg_lock); - seg->vma_count--; - BUG_ON(seg->vma_count < 0); - mutex_unlock(&vm->seg_lock); -} - -static const struct vm_operations_struct guest_vm_ops = { - .open = guest_vm_open, - .close = guest_vm_close, -}; - -static int do_mmap_guest(struct file *file, - struct vm_area_struct *vma, struct guest_memseg *seg) -{ - struct page *page; - size_t size = seg->len; - unsigned long pfn; - unsigned long start_addr; - - vma->vm_flags |= VM_MIXEDMAP | VM_DONTEXPAND | VM_DONTCOPY; - pfn = seg->vm0_gpa >> PAGE_SHIFT; - start_addr = vma->vm_start; - while (size > 0) { - page = pfn_to_page(pfn); - if (vm_insert_page(vma, start_addr, page)) - return -EINVAL; - size -= PAGE_SIZE; - start_addr += PAGE_SIZE; - pfn++; - } - seg->vma_count++; - vma->vm_ops = &guest_vm_ops; - vma->vm_private_data = (void *)seg; - - pr_info("VHM: mmap for memseg [seg vm0_gpa=0x%llx, gpa=0x%llx] " - "to start addr 0x%lx\n", - seg->vm0_gpa, seg->gpa, start_addr); - - return 0; -} - -int vhm_dev_mmap(struct file *file, struct vm_area_struct *vma) -{ - struct vhm_vm *vm = file->private_data; - struct guest_memseg *seg; - u64 offset = vma->vm_pgoff << PAGE_SHIFT; - size_t len = vma->vm_end - vma->vm_start; - int ret; - - if (vm->hugetlb_enabled) - return -EINVAL; - - mutex_lock(&vm->seg_lock); - list_for_each_entry(seg, &vm->memseg_list, list) { - if (seg->gpa != offset || seg->len != len) - continue; - - ret = do_mmap_guest(file, vma, seg); - mutex_unlock(&vm->seg_lock); - return ret; - } - mutex_unlock(&vm->seg_lock); - return -EINVAL; -} - -static void *do_map_guest_phys(struct vhm_vm *vm, u64 guest_phys, size_t size) -{ - struct guest_memseg *seg; - - mutex_lock(&vm->seg_lock); - list_for_each_entry(seg, &vm->memseg_list, list) { - if (seg->gpa > guest_phys || - guest_phys >= seg->gpa + seg->len) - continue; - - if (guest_phys + size > seg->gpa + seg->len) { - mutex_unlock(&vm->seg_lock); - return NULL; - } - - mutex_unlock(&vm->seg_lock); - return phys_to_virt(seg->vm0_gpa + guest_phys - seg->gpa); - } - mutex_unlock(&vm->seg_lock); - return NULL; -} - void *map_guest_phys(unsigned long vmid, u64 guest_phys, size_t size) { struct vhm_vm *vm; - void *ret; + void *ret = NULL; vm = find_get_vm(vmid); if (vm == NULL) - return NULL; + return ret; if (vm->hugetlb_enabled) ret = hugepage_map_guest_phys(vm, guest_phys, size); - else - ret = do_map_guest_phys(vm, guest_phys, size); put_vm(vm); @@ -452,38 +247,19 @@ void *map_guest_phys(unsigned long vmid, u64 guest_phys, size_t size) } EXPORT_SYMBOL(map_guest_phys); -static int do_unmap_guest_phys(struct vhm_vm *vm, u64 guest_phys) -{ - struct guest_memseg *seg; - - mutex_lock(&vm->seg_lock); - list_for_each_entry(seg, &vm->memseg_list, list) { - if (seg->gpa <= guest_phys && - guest_phys < seg->gpa + seg->len) { - mutex_unlock(&vm->seg_lock); - return 0; - } - } - mutex_unlock(&vm->seg_lock); - - return -ESRCH; -} - int unmap_guest_phys(unsigned long vmid, u64 guest_phys) { struct vhm_vm *vm; - int ret; + int ret = -ESRCH; vm = find_get_vm(vmid); if (vm == NULL) { pr_warn("vm_list corrupted\n"); - return -ESRCH; + return ret; } if (vm->hugetlb_enabled) ret = hugepage_unmap_guest_phys(vm, guest_phys); - else - ret = do_unmap_guest_phys(vm, guest_phys); put_vm(vm); return ret; diff --git a/include/linux/vhm/acrn_vhm_mm.h b/include/linux/vhm/acrn_vhm_mm.h index 21269e47b26a..645a8a56531e 100644 --- a/include/linux/vhm/acrn_vhm_mm.h +++ b/include/linux/vhm/acrn_vhm_mm.h @@ -163,8 +163,6 @@ int update_memmap_attr(unsigned long vmid, unsigned long guest_gpa, int vhm_dev_mmap(struct file *file, struct vm_area_struct *vma); -int check_guest_mem(struct vhm_vm *vm); - /** * free_guest_mem - free memory of guest * @@ -174,17 +172,6 @@ int check_guest_mem(struct vhm_vm *vm); */ void free_guest_mem(struct vhm_vm *vm); -/** - * alloc_guest_memseg - alloc memory of guest according to pre-defined - * memory segment info - * - * @vm: pointer to guest vm - * @memseg: pointer to guest memory segment info - * - * Return: - */ -int alloc_guest_memseg(struct vhm_vm *vm, struct vm_memseg *memseg); - /** * map_guest_memseg - set guest mmapping of memory according to * pre-defined memory mapping info diff --git a/include/linux/vhm/vhm_ioctl_defs.h b/include/linux/vhm/vhm_ioctl_defs.h index ec560621ca73..cf4f63211aa2 100644 --- a/include/linux/vhm/vhm_ioctl_defs.h +++ b/include/linux/vhm/vhm_ioctl_defs.h @@ -91,6 +91,7 @@ /* Guest memory management */ #define IC_ID_MEM_BASE 0x40UL +/* IC_ALLOC_MEMSEG not used */ #define IC_ALLOC_MEMSEG _IC_ID(IC_ID, IC_ID_MEM_BASE + 0x00) #define IC_SET_MEMSEG _IC_ID(IC_ID, IC_ID_MEM_BASE + 0x01) diff --git a/include/linux/vhm/vhm_vm_mngt.h b/include/linux/vhm/vhm_vm_mngt.h index e89b78c7fff5..91cd13dad69a 100644 --- a/include/linux/vhm/vhm_vm_mngt.h +++ b/include/linux/vhm/vhm_vm_mngt.h @@ -78,8 +78,6 @@ extern struct mutex vhm_vm_list_lock; * @trusty_host_gpa: host physical address of continuous memory for Trusty * @ioreq_fallback_client: default ioreq client * @refcnt: reference count of guest - * @seg_lock: mutex to protect memseg_list - * @memseg_list: list of memseg * @hugepage_lock: mutex to protect hugepage_hlist * @hugepage_hlist: hash list of hugepage * @max_gfn: maximum guest page frame number @@ -96,8 +94,6 @@ struct vhm_vm { unsigned long trusty_host_gpa; int ioreq_fallback_client; long refcnt; - struct mutex seg_lock; - struct list_head memseg_list; struct mutex hugepage_lock; struct hlist_head hugepage_hlist[HUGEPAGE_HLIST_ARRAY_SIZE]; int max_gfn; From 287dc6167f25ef70546cca06fb33d67948e97f55 Mon Sep 17 00:00:00 2001 From: "Li, Fei1" Date: Fri, 31 Aug 2018 10:59:02 +0800 Subject: [PATCH 0070/1103] sos: vhm: remove hugetlb_enabled flag Since we only have hugetlb memory allocation mechanism, there no needs hugetlb_enabled to indicate we're using hugetlb. Signed-off-by: Li, Fei1 --- drivers/char/vhm/vhm_dev.c | 1 - drivers/vhm/vhm_hugetlb.c | 1 - drivers/vhm/vhm_mm.c | 17 +++++++---------- include/linux/vhm/vhm_vm_mngt.h | 2 -- 4 files changed, 7 insertions(+), 14 deletions(-) diff --git a/drivers/char/vhm/vhm_dev.c b/drivers/char/vhm/vhm_dev.c index 3dca3b3679a2..e454b9efcda2 100644 --- a/drivers/char/vhm/vhm_dev.c +++ b/drivers/char/vhm/vhm_dev.c @@ -131,7 +131,6 @@ static int vhm_dev_open(struct inode *inodep, struct file *filep) vm_mutex_lock(&vhm_vm_list_lock); vm->refcnt = 1; - vm->hugetlb_enabled = 0; vm_list_add(&vm->list); vm_mutex_unlock(&vhm_vm_list_lock); filep->private_data = vm; diff --git a/drivers/vhm/vhm_hugetlb.c b/drivers/vhm/vhm_hugetlb.c index 9c39f9167f77..a83f00ad2e9d 100644 --- a/drivers/vhm/vhm_hugetlb.c +++ b/drivers/vhm/vhm_hugetlb.c @@ -194,7 +194,6 @@ int hugepage_map_guest(struct vhm_vm *vm, struct vm_memmap *memmap) } __free_page(memmaps_buf_pg); - vm->hugetlb_enabled = 1; return 0; err: diff --git a/drivers/vhm/vhm_mm.c b/drivers/vhm/vhm_mm.c index 3c0c2acbe522..c7ca10255064 100644 --- a/drivers/vhm/vhm_mm.c +++ b/drivers/vhm/vhm_mm.c @@ -200,8 +200,7 @@ int map_guest_memseg(struct vhm_vm *vm, struct vm_memmap *memmap) void free_guest_mem(struct vhm_vm *vm) { - if (vm->hugetlb_enabled) - return hugepage_free_guest(vm); + return hugepage_free_guest(vm); } #define TRUSTY_MEM_GPA_BASE (511UL * 1024UL * 1024UL * 1024UL) @@ -232,14 +231,13 @@ void deinit_trusty(struct vhm_vm *vm) void *map_guest_phys(unsigned long vmid, u64 guest_phys, size_t size) { struct vhm_vm *vm; - void *ret = NULL; + void *ret; vm = find_get_vm(vmid); if (vm == NULL) - return ret; + return NULL; - if (vm->hugetlb_enabled) - ret = hugepage_map_guest_phys(vm, guest_phys, size); + ret = hugepage_map_guest_phys(vm, guest_phys, size); put_vm(vm); @@ -250,16 +248,15 @@ EXPORT_SYMBOL(map_guest_phys); int unmap_guest_phys(unsigned long vmid, u64 guest_phys) { struct vhm_vm *vm; - int ret = -ESRCH; + int ret; vm = find_get_vm(vmid); if (vm == NULL) { pr_warn("vm_list corrupted\n"); - return ret; + return -ESRCH; } - if (vm->hugetlb_enabled) - ret = hugepage_unmap_guest_phys(vm, guest_phys); + ret = hugepage_unmap_guest_phys(vm, guest_phys); put_vm(vm); return ret; diff --git a/include/linux/vhm/vhm_vm_mngt.h b/include/linux/vhm/vhm_vm_mngt.h index 91cd13dad69a..29fee8fe0a7b 100644 --- a/include/linux/vhm/vhm_vm_mngt.h +++ b/include/linux/vhm/vhm_vm_mngt.h @@ -85,7 +85,6 @@ extern struct mutex vhm_vm_list_lock; * @ioreq_client_list: list of ioreq clients * @req_buf: request buffer shared between HV, SOS and UOS * @pg: pointer to linux page which holds req_buf - * @hugetlb_enabled: flag to enable/disable hugetlb page ept mapping */ struct vhm_vm { struct device *dev; @@ -101,7 +100,6 @@ struct vhm_vm { struct list_head ioreq_client_list; struct vhm_request_buffer *req_buf; struct page *pg; - int hugetlb_enabled; }; /** From 16a1761a0fb0a5849d995730468af80c88b794f6 Mon Sep 17 00:00:00 2001 From: "Li, Fei1" Date: Fri, 31 Aug 2018 10:59:02 +0800 Subject: [PATCH 0071/1103] sos: vhm: remove MAP_MMIO Now the MAP_MMIO has no difference with MAP_MEM. So there's no needs to keep it. Signed-off-by: Li, Fei1 Acked-by: Eddie Dong --- drivers/vhm/vhm_mm.c | 4 ++-- include/linux/vhm/acrn_hv_defs.h | 1 - 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/vhm/vhm_mm.c b/drivers/vhm/vhm_mm.c index c7ca10255064..f663558ae943 100644 --- a/drivers/vhm/vhm_mm.c +++ b/drivers/vhm/vhm_mm.c @@ -135,7 +135,7 @@ int set_mmio_map(unsigned long vmid, unsigned long guest_gpa, unsigned int mem_type, unsigned mem_access_right) { return _mem_set_memmap(vmid, guest_gpa, host_gpa, len, - mem_type, mem_access_right, MAP_MMIO); + mem_type, mem_access_right, MAP_MEM); } int unset_mmio_map(unsigned long vmid, unsigned long guest_gpa, @@ -187,7 +187,7 @@ int map_guest_memseg(struct vhm_vm *vm, struct vm_memmap *memmap) host_gpa = acrn_hpa2gpa(memmap->hpa); mem_type = MEM_TYPE_UC; mem_access_right = (memmap->prot & MEM_ACCESS_RIGHT_MASK); - type = MAP_MMIO; + type = MAP_MEM; if (_mem_set_memmap(vm->vmid, guest_gpa, host_gpa, memmap->len, mem_type, mem_access_right, type) < 0) { diff --git a/include/linux/vhm/acrn_hv_defs.h b/include/linux/vhm/acrn_hv_defs.h index f51f56b58147..31cdebefedf2 100644 --- a/include/linux/vhm/acrn_hv_defs.h +++ b/include/linux/vhm/acrn_hv_defs.h @@ -134,7 +134,6 @@ struct vm_set_memmap { #define MAP_MEM 0 -#define MAP_MMIO 1 #define MAP_UNMAP 2 uint32_t type; From 8a5c05c21e572c925d20ee3cda9d2aed55e1f6e9 Mon Sep 17 00:00:00 2001 From: Junjie Mao Date: Fri, 31 Aug 2018 10:59:02 +0800 Subject: [PATCH 0072/1103] vhm: revisit types in structure parameters of hypercalls While fixing the MISRA C violations related to integral types, we have unified the type of the following data: uint8_t: phys_pin, virt_pin, vpic_pin, ioapic_pin, vioapic_pin uint16_t: vm_id, pcpu_id, vcpu_id, vpid uint32_t: vector, irq This patch revisits the types of the fields in vhm_request as well as the structures used as parameters in the hypercalls, and make them aligned with the types the hypervisor uses for such data. Reserved fields are added to keep the size and layout of the structures. Implicit paddings are also made explicit as reserved fields. This is the update on the VHM side in correspondance to the same changes in the hypervisor and device model. v1 -> v2: * Make reserved fields unsigned. * Combine continuous reserved fields using proper arrays. * Make msix_entry_index unsigned as it is used in this way in both the hypervisor and kernel. Signed-off-by: Junjie Mao Reviewed-by: Zhao Yakui --- include/linux/vhm/acrn_common.h | 54 ++++++++++++++++++++------------ include/linux/vhm/acrn_hv_defs.h | 17 +++++++--- 2 files changed, 46 insertions(+), 25 deletions(-) diff --git a/include/linux/vhm/acrn_common.h b/include/linux/vhm/acrn_common.h index 7482320a36a8..91951b7e1fc3 100644 --- a/include/linux/vhm/acrn_common.h +++ b/include/linux/vhm/acrn_common.h @@ -87,17 +87,17 @@ struct mmio_request { uint32_t direction; uint32_t reserved; - int64_t address; - int64_t size; - int64_t value; + uint64_t address; + uint64_t size; + uint64_t value; } __attribute__((aligned(8))); struct pio_request { uint32_t direction; uint32_t reserved; - int64_t address; - int64_t size; - int32_t value; + uint64_t address; + uint64_t size; + uint32_t value; } __attribute__((aligned(8))); struct pci_request { @@ -114,16 +114,15 @@ struct pci_request { /* vhm_request are 256Bytes aligned */ struct vhm_request { /* offset: 0bytes - 63bytes */ - union { - uint32_t type; - int32_t reserved0[16]; - }; + uint32_t type; + uint32_t reserved0[15]; + /* offset: 64bytes-127bytes */ union { struct pio_request pio_request; struct pci_request pci_request; struct mmio_request mmio_request; - int64_t reserved1[8]; + uint64_t reserved1[8]; } reqs; /* True: valid req which need VHM to process. @@ -145,7 +144,7 @@ struct vhm_request { struct vhm_request_buffer { union { struct vhm_request req_queue[VHM_REQUEST_MAX]; - int8_t reserved[4096]; + uint8_t reserved[4096]; }; } __attribute__((aligned(4096))); @@ -154,10 +153,16 @@ struct vhm_request_buffer { */ struct acrn_create_vm { /** created vmid return to VHM. Keep it first field */ - int32_t vmid; + uint16_t vmid; + + /** Reserved */ + uint16_t reserved0; /** VCPU numbers this VM want to create */ - uint32_t vcpu_num; + uint16_t vcpu_num; + + /** Reserved */ + uint16_t reserved1; /** the GUID of this VM */ uint8_t GUID[16]; @@ -168,7 +173,7 @@ struct acrn_create_vm { uint64_t vm_flag; /** Reserved for future use*/ - uint8_t reserved[24]; + uint8_t reserved2[24]; } __attribute__((aligned(8))); /** @@ -214,12 +219,18 @@ struct acrn_irqline { uint32_t reserved; /** pic IRQ for ISA type */ - uint64_t pic_irq; + uint32_t pic_irq; + + /** Reserved */ + uint32_t reserved0; /** ioapic IRQ for IOAPIC & ISA TYPE, - * if -1 then this IRQ will not be injected + * if ~0U then this IRQ will not be injected */ - uint64_t ioapic_irq; + uint32_t ioapic_irq; + + /** Reserved */ + uint32_t reserved1; } __attribute__((aligned(8))); /** @@ -240,7 +251,10 @@ struct acrn_msi_entry { */ struct acrn_nmi_entry { /** virtual CPU ID to inject */ - int64_t vcpu_id; + uint16_t vcpu_id; + + /** Reserved */ + uint16_t reserved[3]; } __attribute__((aligned(8))); /** @@ -279,7 +293,7 @@ struct acrn_vm_pci_msix_remap { /** if the pass-through PCI device is MSI-X, this field contains * the MSI-X entry table index */ - int32_t msix_entry_index; + uint32_t msix_entry_index; /** if the pass-through PCI device is MSI-X, this field contains * Vector Control for MSI-X Entry, field defined in MSI-X spec diff --git a/include/linux/vhm/acrn_hv_defs.h b/include/linux/vhm/acrn_hv_defs.h index 31cdebefedf2..67316fb49999 100644 --- a/include/linux/vhm/acrn_hv_defs.h +++ b/include/linux/vhm/acrn_hv_defs.h @@ -170,7 +170,10 @@ struct memory_map { struct set_memmaps { /*IN: vmid for this hypercall */ - uint64_t vmid; + uint16_t vmid; + + /** Reserved */ + uint16_t reserved[3]; /* IN: multi memmaps numbers */ uint32_t memmaps_num; @@ -184,7 +187,8 @@ struct set_memmaps { } __attribute__((aligned(8))); struct sbuf_setup_param { - uint32_t pcpu_id; + uint16_t pcpu_id; + uint16_t reserved; uint32_t sbuf_id; uint64_t gpa; } __attribute__((aligned(8))); @@ -203,9 +207,12 @@ struct hc_ptdev_irq { uint16_t phys_bdf; /* IN: Device physical BDF# */ union { struct { - uint32_t virt_pin; /* IN: virtual IOAPIC pin */ - uint32_t phys_pin; /* IN: physical IOAPIC pin */ - uint32_t pic_pin; /* IN: pin from PIC? */ + uint8_t virt_pin; /* IN: virtual IOAPIC pin */ + uint8_t reserved0[3]; /* Reserved */ + uint8_t phys_pin; /* IN: physical IOAPIC pin */ + uint8_t reserved1[3]; /* Reserved */ + bool pic_pin; /* IN: pin from PIC? */ + uint8_t reserved2[3]; /* Reserved */ } intx; struct { /* IN: vector count of MSI/MSIX */ From 75bb0baf996fb35e501ad39d3de090ee51d72cec Mon Sep 17 00:00:00 2001 From: "Li, Fei1" Date: Fri, 31 Aug 2018 10:59:02 +0800 Subject: [PATCH 0073/1103] sos: vhm: add hcall_write_protect_page hypercall 1. add write_protect_page to set or unset one page write protect 2. replace update_memmap_attr with write_protect_page to set or unset one page write protect 3. replace update_memmap_attr with set_mmio_map to add guest memory region Signed-off-by: Li, Fei1 --- drivers/vhm/vhm_hypercall.c | 5 +++++ drivers/vhm/vhm_mm.c | 25 ++++++++++++++++++++----- include/linux/vhm/acrn_hv_defs.h | 14 ++++++++++++++ include/linux/vhm/acrn_vhm_mm.h | 23 +++++------------------ include/linux/vhm/vhm_hypercall.h | 2 ++ 5 files changed, 46 insertions(+), 23 deletions(-) diff --git a/drivers/vhm/vhm_hypercall.c b/drivers/vhm/vhm_hypercall.c index 2c51c366c60f..8611bb1819d4 100644 --- a/drivers/vhm/vhm_hypercall.c +++ b/drivers/vhm/vhm_hypercall.c @@ -112,6 +112,11 @@ inline long hcall_set_memmaps(unsigned long pa_memmaps) return acrn_hypercall1(HC_VM_SET_MEMMAPS, pa_memmaps); } +inline long hcall_write_protect_page(unsigned long vmid, unsigned long wp) +{ + return acrn_hypercall2(HC_VM_WRITE_PROTECT_PAGE, vmid, wp); +} + inline long hcall_set_ioreq_buffer(unsigned long vmid, unsigned long buffer) { return acrn_hypercall2(HC_SET_IOREQ_BUFFER, vmid, buffer); diff --git a/drivers/vhm/vhm_mm.c b/drivers/vhm/vhm_mm.c index f663558ae943..c7ca8e99612d 100644 --- a/drivers/vhm/vhm_mm.c +++ b/drivers/vhm/vhm_mm.c @@ -159,12 +159,27 @@ int set_memmaps(struct set_memmaps *memmaps) return 0; } -int update_memmap_attr(unsigned long vmid, unsigned long guest_gpa, - unsigned long host_gpa, unsigned long len, - unsigned int mem_type, unsigned int mem_access_right) +/* + * when set is true, set page write protection, + * else clear page write protection. + */ +int write_protect_page(unsigned long vmid, + unsigned long gpa, unsigned char set) { - return _mem_set_memmap(vmid, guest_gpa, host_gpa, len, - mem_type, mem_access_right, MAP_MEM); + struct wp_data wp; + + wp.set = set; + wp.gpa = gpa; + + if (hcall_write_protect_page(vmid, + virt_to_phys(&wp)) < 0) { + pr_err("vhm: vm[%ld] %s failed !\n", vmid, __func__); + return -EFAULT; + } + + pr_debug("VHM: %s, gpa: 0x%lx, set: %d\n", __func__, gpa, set); + + return 0; } int map_guest_memseg(struct vhm_vm *vm, struct vm_memmap *memmap) diff --git a/include/linux/vhm/acrn_hv_defs.h b/include/linux/vhm/acrn_hv_defs.h index 67316fb49999..74f8a137206d 100644 --- a/include/linux/vhm/acrn_hv_defs.h +++ b/include/linux/vhm/acrn_hv_defs.h @@ -96,6 +96,7 @@ #define HC_VM_SET_MEMMAP _HC_ID(HC_ID, HC_ID_MEM_BASE + 0x00) #define HC_VM_GPA2HPA _HC_ID(HC_ID, HC_ID_MEM_BASE + 0x01) #define HC_VM_SET_MEMMAPS _HC_ID(HC_ID, HC_ID_MEM_BASE + 0x02) +#define HC_VM_WRITE_PROTECT_PAGE _HC_ID(HC_ID, HC_ID_MEM_BASE + 0x03) /* PCI assignment*/ #define HC_ID_PCI_BASE 0x50UL @@ -186,6 +187,19 @@ struct set_memmaps { uint64_t memmaps_gpa; } __attribute__((aligned(8))); +struct wp_data { + /** set page write protect permission. + * ture: set the wp; flase: clear the wp + */ + uint8_t set; + + /** Reserved */ + uint64_t pad:56; + + /** the guest physical address of the page to change */ + uint64_t gpa; +} __aligned(8); + struct sbuf_setup_param { uint16_t pcpu_id; uint16_t reserved; diff --git a/include/linux/vhm/acrn_vhm_mm.h b/include/linux/vhm/acrn_vhm_mm.h index 645a8a56531e..0769200ea3bf 100644 --- a/include/linux/vhm/acrn_vhm_mm.h +++ b/include/linux/vhm/acrn_vhm_mm.h @@ -137,29 +137,16 @@ int unset_mmio_map(unsigned long vmid, unsigned long guest_gpa, unsigned long host_gpa, unsigned long len); /** - * update_memmap_attr - update mmio EPT mapping between UOS gpa and SOS gpa + * write_protect_page - change one page write protection * * @vmid: guest vmid - * @guest_gpa: gpa of UOS - * @host_gpa: gpa of SOS - * @len: memory mapped length - * @mem_type: memory mapping type. Possible value could be: - * MEM_TYPE_WB - * MEM_TYPE_WT - * MEM_TYPE_UC - * MEM_TYPE_WC - * MEM_TYPE_WP - * @mem_access_right: memory mapping access. Possible value could be: - * MEM_ACCESS_READ - * MEM_ACCESS_WRITE - * MEM_ACCESS_EXEC - * MEM_ACCESS_RWX + * @gpa: gpa in guest vmid + * @set: set or clear page write protection * * Return: 0 on success, <0 for error. */ -int update_memmap_attr(unsigned long vmid, unsigned long guest_gpa, - unsigned long host_gpa, unsigned long len, - unsigned int mem_type, unsigned int mem_access_right); +int write_protect_page(unsigned long vmid, + unsigned long gpa, unsigned char set); int vhm_dev_mmap(struct file *file, struct vm_area_struct *vma); diff --git a/include/linux/vhm/vhm_hypercall.h b/include/linux/vhm/vhm_hypercall.h index 4de5e46b9d0f..87a34fec8d90 100644 --- a/include/linux/vhm/vhm_hypercall.h +++ b/include/linux/vhm/vhm_hypercall.h @@ -151,6 +151,8 @@ inline long hcall_get_cpu_state(unsigned long cmd, unsigned long state_pa); inline long hcall_set_memmap(unsigned long vmid, unsigned long memmap); inline long hcall_set_memmaps(unsigned long pa_memmaps); +inline long hcall_write_protect_page(unsigned long vmid, + unsigned long wp); inline long hcall_set_ioreq_buffer(unsigned long vmid, unsigned long buffer); inline long hcall_notify_req_finish(unsigned long vmid, unsigned long vcpu); From 202a74de113bf7f75a2f0dccda2daea53ca14553 Mon Sep 17 00:00:00 2001 From: "Li, Fei1" Date: Fri, 31 Aug 2018 10:59:02 +0800 Subject: [PATCH 0074/1103] sos: vhm: refine set memory region API 1. rename set_mmio_map to add_memory_region; unset_mmio_map to del_memory_region. 2. rename set_memmap to set_memory_region; set_memmaps to set_memory_region; 3. remove HV_VM_SET_MEMORY_REGION hypercall Signed-off-by: Li, Fei1 Acked-by: Eddie Dong --- drivers/vhm/vhm_hugetlb.c | 61 +++++++++++---------- drivers/vhm/vhm_hypercall.c | 9 +--- drivers/vhm/vhm_mm.c | 89 +++++++++++++++---------------- include/linux/vhm/acrn_hv_defs.h | 39 ++++---------- include/linux/vhm/acrn_vhm_mm.h | 31 +++++------ include/linux/vhm/vhm_hypercall.h | 4 +- 6 files changed, 98 insertions(+), 135 deletions(-) diff --git a/drivers/vhm/vhm_hugetlb.c b/drivers/vhm/vhm_hugetlb.c index a83f00ad2e9d..34ebbd90acea 100644 --- a/drivers/vhm/vhm_hugetlb.c +++ b/drivers/vhm/vhm_hugetlb.c @@ -120,11 +120,11 @@ static int add_guest_map(struct vhm_vm *vm, unsigned long vm0_gpa, int hugepage_map_guest(struct vhm_vm *vm, struct vm_memmap *memmap) { - struct page *page = NULL, *memmaps_buf_pg = NULL; + struct page *page = NULL, *regions_buf_pg = NULL; unsigned long len, guest_gpa, vma; - struct memory_map *memmap_array; - struct set_memmaps memmaps; - int max_size = PAGE_SIZE/sizeof(struct memory_map); + struct vm_memory_region *region_array; + struct set_regions regions; + int max_size = PAGE_SIZE/sizeof(struct vm_memory_region); int ret; if (vm == NULL || memmap == NULL) @@ -134,14 +134,14 @@ int hugepage_map_guest(struct vhm_vm *vm, struct vm_memmap *memmap) vma = memmap->vma_base; guest_gpa = memmap->gpa; - /* prepare set_memmaps info */ - memmaps_buf_pg = alloc_page(GFP_KERNEL); - if (memmaps_buf_pg == NULL) + /* prepare set_memory_regions info */ + regions_buf_pg = alloc_page(GFP_KERNEL); + if (regions_buf_pg == NULL) return -ENOMEM; - memmaps.memmaps_num = 0; - memmaps.vmid = vm->vmid; - memmaps.memmaps_gpa = page_to_phys(memmaps_buf_pg); - memmap_array = page_to_virt(memmaps_buf_pg); + regions.mr_num = 0; + regions.vmid = vm->vmid; + regions.regions_gpa = page_to_phys(regions_buf_pg); + region_array = page_to_virt(regions_buf_pg); while (len > 0) { unsigned long vm0_gpa, pagesize; @@ -162,24 +162,23 @@ int hugepage_map_guest(struct vhm_vm *vm, struct vm_memmap *memmap) goto err; } - /* fill each memmap region into memmap_array */ - memmap_array[memmaps.memmaps_num].type = MAP_MEM; - memmap_array[memmaps.memmaps_num].remote_gpa = guest_gpa; - memmap_array[memmaps.memmaps_num].vm0_gpa = vm0_gpa; - memmap_array[memmaps.memmaps_num].length = pagesize; - memmap_array[memmaps.memmaps_num].prot = - MEM_TYPE_WB & MEM_TYPE_MASK; - memmap_array[memmaps.memmaps_num].prot |= - memmap->prot & MEM_ACCESS_RIGHT_MASK; - memmaps.memmaps_num++; - if (memmaps.memmaps_num == max_size) { - pr_info("region buffer full, set & renew memmaps!\n"); - ret = set_memmaps(&memmaps); + /* fill each memory region into region_array */ + region_array[regions.mr_num].type = MR_ADD; + region_array[regions.mr_num].gpa = guest_gpa; + region_array[regions.mr_num].vm0_gpa = vm0_gpa; + region_array[regions.mr_num].size = pagesize; + region_array[regions.mr_num].prot = + (MEM_TYPE_WB & MEM_TYPE_MASK) | + (memmap->prot & MEM_ACCESS_RIGHT_MASK); + regions.mr_num++; + if (regions.mr_num == max_size) { + pr_info("region buffer full, set & renew regions!\n"); + ret = set_memory_regions(®ions); if (ret < 0) { - pr_err("failed to set memmaps,ret=%d!\n", ret); + pr_err("failed to set regions,ret=%d!\n", ret); goto err; } - memmaps.memmaps_num = 0; + regions.mr_num = 0; } len -= pagesize; @@ -187,18 +186,18 @@ int hugepage_map_guest(struct vhm_vm *vm, struct vm_memmap *memmap) guest_gpa += pagesize; } - ret = set_memmaps(&memmaps); + ret = set_memory_regions(®ions); if (ret < 0) { - pr_err("failed to set memmaps, ret=%d!\n", ret); + pr_err("failed to set regions, ret=%d!\n", ret); goto err; } - __free_page(memmaps_buf_pg); + __free_page(regions_buf_pg); return 0; err: - if (memmaps_buf_pg) - __free_page(memmaps_buf_pg); + if (regions_buf_pg) + __free_page(regions_buf_pg); if (page) put_page(page); return ret; diff --git a/drivers/vhm/vhm_hypercall.c b/drivers/vhm/vhm_hypercall.c index 8611bb1819d4..9a92d6888b90 100644 --- a/drivers/vhm/vhm_hypercall.c +++ b/drivers/vhm/vhm_hypercall.c @@ -102,14 +102,9 @@ inline long hcall_get_cpu_state(unsigned long cmd, unsigned long state_pa) return acrn_hypercall2(HC_PM_GET_CPU_STATE, cmd, state_pa); } -inline long hcall_set_memmap(unsigned long vmid, unsigned long memmap) +inline long hcall_set_memory_regions(unsigned long pa_regions) { - return acrn_hypercall2(HC_VM_SET_MEMMAP, vmid, memmap); -} - -inline long hcall_set_memmaps(unsigned long pa_memmaps) -{ - return acrn_hypercall1(HC_VM_SET_MEMMAPS, pa_memmaps); + return acrn_hypercall1(HC_VM_SET_MEMORY_REGIONS, pa_regions); } inline long hcall_write_protect_page(unsigned long vmid, unsigned long wp) diff --git a/drivers/vhm/vhm_mm.c b/drivers/vhm/vhm_mm.c index c7ca8e99612d..4d5854d0c139 100644 --- a/drivers/vhm/vhm_mm.c +++ b/drivers/vhm/vhm_mm.c @@ -102,56 +102,59 @@ static bool _free_memblk(struct device *dev, u64 vm0_gpa, size_t len) return dma_release_from_contiguous(dev, page, count); } -int _mem_set_memmap(unsigned long vmid, unsigned long guest_gpa, - unsigned long host_gpa, unsigned long len, - unsigned int mem_type, unsigned int mem_access_right, - unsigned int type) +static int set_memory_region(unsigned long vmid, + struct vm_memory_region *region) { - struct vm_set_memmap set_memmap; + struct set_regions regions; - set_memmap.type = type; - set_memmap.remote_gpa = guest_gpa; - set_memmap.vm0_gpa = host_gpa; - set_memmap.length = len; - set_memmap.prot = set_memmap.prot_2 = ((mem_type & MEM_TYPE_MASK) | - (mem_access_right & MEM_ACCESS_RIGHT_MASK)); + regions.vmid = vmid; + regions.mr_num = 1; + regions.regions_gpa = virt_to_phys(region); - /* hypercall to notify hv the guest EPT setting*/ - if (hcall_set_memmap(vmid, - virt_to_phys(&set_memmap)) < 0) { - pr_err("vhm: failed to set memmap %ld!\n", vmid); + if (set_memory_regions(®ions) < 0) { + pr_err("vhm: failed to set memory region for vm[%ld]!\n", vmid); return -EFAULT; } - pr_debug("VHM: set ept for mem map[type=0x%x, host_gpa=0x%lx," - "guest_gpa=0x%lx,len=0x%lx, prot=0x%x]\n", - type, host_gpa, guest_gpa, len, set_memmap.prot); - return 0; } -int set_mmio_map(unsigned long vmid, unsigned long guest_gpa, - unsigned long host_gpa, unsigned long len, +int add_memory_region(unsigned long vmid, unsigned long gpa, + unsigned long host_gpa, unsigned long size, unsigned int mem_type, unsigned mem_access_right) { - return _mem_set_memmap(vmid, guest_gpa, host_gpa, len, - mem_type, mem_access_right, MAP_MEM); + struct vm_memory_region region; + + region.type = MR_ADD; + region.gpa = gpa; + region.vm0_gpa = host_gpa; + region.size = size; + region.prot = ((mem_type & MEM_TYPE_MASK) | + (mem_access_right & MEM_ACCESS_RIGHT_MASK)); + return set_memory_region(vmid, ®ion); } -int unset_mmio_map(unsigned long vmid, unsigned long guest_gpa, - unsigned long host_gpa, unsigned long len) +int del_memory_region(unsigned long vmid, unsigned long gpa, + unsigned long size) { - return _mem_set_memmap(vmid, guest_gpa, host_gpa, len, - 0, 0, MAP_UNMAP); + struct vm_memory_region region; + + region.type = MR_DEL; + region.gpa = gpa; + region.vm0_gpa = 0; + region.size = size; + region.prot = 0; + + return set_memory_region(vmid, ®ion); } -int set_memmaps(struct set_memmaps *memmaps) +int set_memory_regions(struct set_regions *regions) { - if (memmaps == NULL) + if (regions == NULL) return -EINVAL; - if (memmaps->memmaps_num > 0) { - if (hcall_set_memmaps(virt_to_phys(memmaps)) < 0) { - pr_err("vhm: failed to set memmaps!\n"); + if (regions->mr_num > 0) { + if (hcall_set_memory_regions(virt_to_phys(regions)) < 0) { + pr_err("vhm: failed to set memory regions!\n"); return -EFAULT; } } @@ -184,10 +187,6 @@ int write_protect_page(unsigned long vmid, int map_guest_memseg(struct vhm_vm *vm, struct vm_memmap *memmap) { - unsigned int type; - unsigned int mem_type, mem_access_right; - unsigned long guest_gpa, host_gpa; - /* hugetlb use vma to do the mapping */ if (memmap->type == VM_MEMMAP_SYSMEM && memmap->using_vma) return hugepage_map_guest(vm, memmap); @@ -198,15 +197,11 @@ int map_guest_memseg(struct vhm_vm *vm, struct vm_memmap *memmap) __func__, memmap->type); return -EINVAL; } - guest_gpa = memmap->gpa; - host_gpa = acrn_hpa2gpa(memmap->hpa); - mem_type = MEM_TYPE_UC; - mem_access_right = (memmap->prot & MEM_ACCESS_RIGHT_MASK); - type = MAP_MEM; - - if (_mem_set_memmap(vm->vmid, guest_gpa, host_gpa, memmap->len, - mem_type, mem_access_right, type) < 0) { - pr_err("vhm: failed to set memmap %ld!\n", vm->vmid); + + if (add_memory_region(vm->vmid, memmap->gpa, + acrn_hpa2gpa(memmap->hpa), memmap->len, + MEM_TYPE_UC, memmap->prot) < 0){ + pr_err("vhm: failed to set memory region %ld!\n", vm->vmid); return -EFAULT; } @@ -233,8 +228,8 @@ int init_trusty(struct vhm_vm *vm) pr_info("VHM: set ept for trusty memory [host_gpa=0x%lx, " "guest_gpa=0x%lx, len=0x%lx]", host_gpa, guest_gpa, len); - return _mem_set_memmap(vm->vmid, guest_gpa, host_gpa, len, - MEM_TYPE_WB, MEM_ACCESS_RWX, MAP_MEM); + return add_memory_region(vm->vmid, guest_gpa, host_gpa, len, + MEM_TYPE_WB, MEM_ACCESS_RWX); } void deinit_trusty(struct vhm_vm *vm) diff --git a/include/linux/vhm/acrn_hv_defs.h b/include/linux/vhm/acrn_hv_defs.h index 74f8a137206d..223702c0330c 100644 --- a/include/linux/vhm/acrn_hv_defs.h +++ b/include/linux/vhm/acrn_hv_defs.h @@ -93,9 +93,8 @@ /* Guest memory management */ #define HC_ID_MEM_BASE 0x40UL -#define HC_VM_SET_MEMMAP _HC_ID(HC_ID, HC_ID_MEM_BASE + 0x00) #define HC_VM_GPA2HPA _HC_ID(HC_ID, HC_ID_MEM_BASE + 0x01) -#define HC_VM_SET_MEMMAPS _HC_ID(HC_ID, HC_ID_MEM_BASE + 0x02) +#define HC_VM_SET_MEMORY_REGIONS _HC_ID(HC_ID, HC_ID_MEM_BASE + 0x02) #define HC_VM_WRITE_PROTECT_PAGE _HC_ID(HC_ID, HC_ID_MEM_BASE + 0x03) /* PCI assignment*/ @@ -133,43 +132,25 @@ #define MEM_TYPE_WP 0x00000400 #define MEM_TYPE_MASK 0x000007C0 -struct vm_set_memmap { -#define MAP_MEM 0 -#define MAP_UNMAP 2 +struct vm_memory_region { +#define MR_ADD 0 +#define MR_DEL 2 uint32_t type; /* IN: mem attr */ uint32_t prot; /* IN: beginning guest GPA to map */ - uint64_t remote_gpa; - - /* IN: VM0's GPA which foreign gpa will be mapped to */ - uint64_t vm0_gpa; - - /* IN: length of the range */ - uint64_t length; - - uint32_t prot_2; -} __attribute__((aligned(8))); - -struct memory_map { - uint32_t type; - - /* IN: mem attr */ - uint32_t prot; - - /* IN: beginning guest GPA to map */ - uint64_t remote_gpa; + uint64_t gpa; /* IN: VM0's GPA which foreign gpa will be mapped to */ uint64_t vm0_gpa; - /* IN: length of the range */ - uint64_t length; + /* IN: size of the region */ + uint64_t size; } __attribute__((aligned(8))); -struct set_memmaps { +struct set_regions { /*IN: vmid for this hypercall */ uint16_t vmid; @@ -177,14 +158,14 @@ struct set_memmaps { uint16_t reserved[3]; /* IN: multi memmaps numbers */ - uint32_t memmaps_num; + uint32_t mr_num; /* IN: * the gpa of memmaps buffer, point to the memmaps array: * struct memory_map memmap_array[memmaps_num] * the max buffer size is one page. */ - uint64_t memmaps_gpa; + uint64_t regions_gpa; } __attribute__((aligned(8))); struct wp_data { diff --git a/include/linux/vhm/acrn_vhm_mm.h b/include/linux/vhm/acrn_vhm_mm.h index 0769200ea3bf..62aed3466e9f 100644 --- a/include/linux/vhm/acrn_vhm_mm.h +++ b/include/linux/vhm/acrn_vhm_mm.h @@ -99,12 +99,12 @@ void *map_guest_phys(unsigned long vmid, u64 uos_phys, size_t size); int unmap_guest_phys(unsigned long vmid, u64 uos_phys); /** - * set_mmio_map - map mmio EPT mapping between UOS gpa and SOS gpa + * add_memory_region - add a guest memory region * * @vmid: guest vmid - * @guest_gpa: gpa of UOS + * @gpa: gpa of UOS * @host_gpa: gpa of SOS - * @len: memory mapped length + * @size: memory region size * @mem_type: memory mapping type. Possible value could be: * MEM_TYPE_WB * MEM_TYPE_WT @@ -119,22 +119,21 @@ int unmap_guest_phys(unsigned long vmid, u64 uos_phys); * * Return: 0 on success, <0 for error. */ -int set_mmio_map(unsigned long vmid, unsigned long guest_gpa, - unsigned long host_gpa, unsigned long len, +int add_memory_region(unsigned long vmid, unsigned long gpa, + unsigned long host_gpa, unsigned long size, unsigned int mem_type, unsigned int mem_access_right); /** - * unset_mmio_map - unmap mmio mapping between UOS gpa and SOS gpa + * del_memory_region - delete a guest memory region * * @vmid: guest vmid - * @guest_gpa: gpa of UOS - * @host_gpa: gpa of SOS - * @len: memory mapped length + * @gpa: gpa of UOS + * @size: memory region size * * Return: 0 on success, <0 for error. */ -int unset_mmio_map(unsigned long vmid, unsigned long guest_gpa, - unsigned long host_gpa, unsigned long len); +int del_memory_region(unsigned long vmid, unsigned long gpa, + unsigned long size); /** * write_protect_page - change one page write protection @@ -173,21 +172,17 @@ int map_guest_memseg(struct vhm_vm *vm, struct vm_memmap *memmap); int init_trusty(struct vhm_vm *vm); void deinit_trusty(struct vhm_vm *vm); -int _mem_set_memmap(unsigned long vmid, unsigned long guest_gpa, - unsigned long host_gpa, unsigned long len, - unsigned int mem_type, unsigned int mem_access_right, - unsigned int type); int hugepage_map_guest(struct vhm_vm *vm, struct vm_memmap *memmap); void hugepage_free_guest(struct vhm_vm *vm); void *hugepage_map_guest_phys(struct vhm_vm *vm, u64 guest_phys, size_t size); int hugepage_unmap_guest_phys(struct vhm_vm *vm, u64 guest_phys); /** - * set_memmaps - set guest mapping for multi regions + * set_memory_regions - set guest mapping for multi regions * - * @memmaps: pointer to set_memmaps + * @regions: pointer to set_regions * * Return: 0 on success, <0 for error. */ -int set_memmaps(struct set_memmaps *memmaps); +int set_memory_regions(struct set_regions *regions); #endif diff --git a/include/linux/vhm/vhm_hypercall.h b/include/linux/vhm/vhm_hypercall.h index 87a34fec8d90..0a3869b356b8 100644 --- a/include/linux/vhm/vhm_hypercall.h +++ b/include/linux/vhm/vhm_hypercall.h @@ -148,9 +148,7 @@ inline long hcall_query_vm_state(unsigned long vmid); inline long hcall_setup_sbuf(unsigned long sbuf_head); inline long hcall_set_sstate_data(unsigned long sx_data_addr); inline long hcall_get_cpu_state(unsigned long cmd, unsigned long state_pa); -inline long hcall_set_memmap(unsigned long vmid, - unsigned long memmap); -inline long hcall_set_memmaps(unsigned long pa_memmaps); +inline long hcall_set_memory_regions(unsigned long pa_regions); inline long hcall_write_protect_page(unsigned long vmid, unsigned long wp); inline long hcall_set_ioreq_buffer(unsigned long vmid, From 6922e97bce17ff1aef90fff4699ae7e50dcb3e0c Mon Sep 17 00:00:00 2001 From: Shuo Liu Date: Fri, 31 Aug 2018 10:59:02 +0800 Subject: [PATCH 0075/1103] vhm: remove re-schedule for ioreq tasklet io_req_tasklet can process all existing or even incoming ioreqs in the ioreq shared page once. So when the ioreq IPI rising, we needn't rearm the tasklet if the previous one havn't got running. tasklet_schedule can gurantee to execute once after schedule, and all pending ioreqs can be processed once. tasklet_schedule also can rearm the running tasklet so no ioreq interrupt lost. Signed-off-by: Shuo Liu Reviewed-by: Jason Chen CJ --- drivers/char/vhm/vhm_dev.c | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/drivers/char/vhm/vhm_dev.c b/drivers/char/vhm/vhm_dev.c index e454b9efcda2..39204b5d7898 100644 --- a/drivers/char/vhm/vhm_dev.c +++ b/drivers/char/vhm/vhm_dev.c @@ -96,7 +96,6 @@ static int major; static struct class *vhm_class; static struct device *vhm_device; static struct tasklet_struct vhm_io_req_tasklet; -static atomic_t ioreq_retry = ATOMIC_INIT(0); struct table_iomems { /* list node for this table_iomems */ @@ -622,19 +621,11 @@ static void io_req_tasklet(unsigned long data) acrn_ioreq_distribute_request(vm); } - - if (atomic_read(&ioreq_retry) > 0) { - atomic_dec(&ioreq_retry); - tasklet_schedule(&vhm_io_req_tasklet); - } } static void vhm_intr_handler(void) { - if (test_bit(TASKLET_STATE_SCHED, &(vhm_io_req_tasklet.state))) - atomic_inc(&ioreq_retry); - else - tasklet_schedule(&vhm_io_req_tasklet); + tasklet_schedule(&vhm_io_req_tasklet); } static int vhm_dev_release(struct inode *inodep, struct file *filep) From 49191f92894dfb88696ec4c4d9949890529e6dd0 Mon Sep 17 00:00:00 2001 From: Shuo Liu Date: Fri, 31 Aug 2018 10:59:02 +0800 Subject: [PATCH 0076/1103] vhm: Add vcpu_num to record vcpu number of each VM Add a new field 'vcpu_num' in vhm_vm struct to count the number of vcpu of this VM. And uses atomic operation to avoid lock. There is no vcpu_num decrease as we don't have vcpu destroying. Signed-off-by: Shuo Liu Reviewed-by: Anthony Xu Reviewed-by: Jason Chen CJ --- drivers/char/vhm/vhm_dev.c | 1 + drivers/vhm/vhm_ioreq.c | 6 +++--- include/linux/vhm/vhm_vm_mngt.h | 2 ++ 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/drivers/char/vhm/vhm_dev.c b/drivers/char/vhm/vhm_dev.c index 39204b5d7898..5884fd2b259e 100644 --- a/drivers/char/vhm/vhm_dev.c +++ b/drivers/char/vhm/vhm_dev.c @@ -285,6 +285,7 @@ static long vhm_dev_ioctl(struct file *filep, pr_err("vhm: failed to create vcpu %d!\n", cv.vcpu_id); return -EFAULT; } + atomic_inc(&vm->vcpu_num); return ret; } diff --git a/drivers/vhm/vhm_ioreq.c b/drivers/vhm/vhm_ioreq.c index b570b826be95..bf55a0138943 100644 --- a/drivers/vhm/vhm_ioreq.c +++ b/drivers/vhm/vhm_ioreq.c @@ -783,10 +783,10 @@ int acrn_ioreq_distribute_request(struct vhm_vm *vm) struct vhm_request *req; struct list_head *pos; struct ioreq_client *client; - int i; + int i, vcpu_num; - /* TODO: replace VHM_REQUEST_MAX with vcpu num get at runtime */ - for (i = 0; i < VHM_REQUEST_MAX; i++) { + vcpu_num = atomic_read(&vm->vcpu_num); + for (i = 0; i < vcpu_num; i++) { req = vm->req_buf->req_queue + i; if (req->valid && (req->processed == REQ_STATE_PENDING)) { if (handle_cf8cfc(vm, req, i)) diff --git a/include/linux/vhm/vhm_vm_mngt.h b/include/linux/vhm/vhm_vm_mngt.h index 29fee8fe0a7b..d3b26bdbc675 100644 --- a/include/linux/vhm/vhm_vm_mngt.h +++ b/include/linux/vhm/vhm_vm_mngt.h @@ -80,6 +80,7 @@ extern struct mutex vhm_vm_list_lock; * @refcnt: reference count of guest * @hugepage_lock: mutex to protect hugepage_hlist * @hugepage_hlist: hash list of hugepage + * @vcpu_num: vcpu number * @max_gfn: maximum guest page frame number * @ioreq_client_lock: spinlock to protect ioreq_client_list * @ioreq_client_list: list of ioreq clients @@ -95,6 +96,7 @@ struct vhm_vm { long refcnt; struct mutex hugepage_lock; struct hlist_head hugepage_hlist[HUGEPAGE_HLIST_ARRAY_SIZE]; + atomic_t vcpu_num; int max_gfn; spinlock_t ioreq_client_lock; struct list_head ioreq_client_list; From 062d08b616e82fb5e37f0481970cddbb44bbe62f Mon Sep 17 00:00:00 2001 From: Shuo Liu Date: Fri, 31 Aug 2018 10:59:02 +0800 Subject: [PATCH 0077/1103] vhm: mark pending ioreqs in bitmap then dispatch it to vhm client Currently, we are passing the req_count to vhm client which is useless. Clients need get the ioreq shared buffer and the vcpu number of this VM, then loop all the vcpu ioreq slots to find the ones to handle. The patch will record pending ioreqs of the vhm client into a bitmap, then vhm client can process the ioreq directly according to the bitmap. Signed-off-by: Shuo Liu Reviewed-by: Zhao Yakui Reviewed-by: Jason Chen CJ --- drivers/vbs/vbs.c | 16 ++++++++-------- drivers/vbs/vbs_rng.c | 8 ++++---- drivers/vhm/vhm_ioreq.c | 10 +++++----- include/linux/vbs/vbs.h | 6 +++--- include/linux/vhm/acrn_vhm_ioreq.h | 2 +- 5 files changed, 21 insertions(+), 21 deletions(-) diff --git a/drivers/vbs/vbs.c b/drivers/vbs/vbs.c index 6c364364db3c..1d2e1b40728e 100644 --- a/drivers/vbs/vbs.c +++ b/drivers/vbs/vbs.c @@ -145,22 +145,22 @@ long virtio_dev_deregister(struct virtio_dev_info *dev) return 0; } -int virtio_vq_index_get(struct virtio_dev_info *dev, int req_cnt) +int virtio_vq_index_get(struct virtio_dev_info *dev, unsigned long *ioreqs_map) { int val = -1; struct vhm_request *req; - int i; - - if (unlikely(req_cnt <= 0)) - return -EINVAL; + int vcpu; if (dev == NULL) { pr_err("%s: dev is NULL!\n", __func__); return -EINVAL; } - for (i = 0; i < dev->_ctx.max_vcpu; i++) { - req = &dev->_ctx.req_buf[i]; + while (1) { + vcpu = find_first_bit(ioreqs_map, dev->_ctx.max_vcpu); + if (vcpu == dev->_ctx.max_vcpu) + break; + req = &dev->_ctx.req_buf[vcpu]; if (req->valid && req->processed == REQ_STATE_PROCESSING && req->client == dev->_ctx.vhm_client_id) { if (req->reqs.pio_request.direction == REQUEST_READ) { @@ -181,7 +181,7 @@ int virtio_vq_index_get(struct virtio_dev_info *dev, int req_cnt) val = req->reqs.mmio_request.value; } req->processed = REQ_STATE_SUCCESS; - acrn_ioreq_complete_request(dev->_ctx.vhm_client_id, i); + acrn_ioreq_complete_request(req->client, vcpu); } } diff --git a/drivers/vbs/vbs_rng.c b/drivers/vbs/vbs_rng.c index 88f6108ca2e8..569d1d5d689c 100644 --- a/drivers/vbs/vbs_rng.c +++ b/drivers/vbs/vbs_rng.c @@ -122,7 +122,7 @@ static int vbs_rng_hash_initialized = 0; static int vbs_rng_connection_cnt = 0; /* function declarations */ -static int handle_kick(int client_id, int req_cnt); +static int handle_kick(int client_id, unsigned long *ioreqs_map); static void vbs_rng_reset(struct vbs_rng *rng); static void vbs_rng_stop(struct vbs_rng *rng); static void vbs_rng_flush(struct vbs_rng *rng); @@ -251,12 +251,12 @@ static void handle_vq_kick(struct vbs_rng *rng, int vq_idx) virtio_vq_endchains(vq, 1); /* Generate interrupt if appropriate. */ } -static int handle_kick(int client_id, int req_cnt) +static int handle_kick(int client_id, unsigned long *ioreqs_map) { int val = -1; struct vbs_rng *rng; - if (unlikely(req_cnt <= 0)) + if (unlikely(bitmap_empty(ioreqs_map, VHM_REQUEST_MAX) <= 0)) return -EINVAL; pr_debug("%s: handle kick!\n", __func__); @@ -268,7 +268,7 @@ static int handle_kick(int client_id, int req_cnt) return -EINVAL; } - val = virtio_vq_index_get(&rng->dev, req_cnt); + val = virtio_vq_index_get(&rng->dev, ioreqs_map); if (val >= 0) handle_vq_kick(rng, val); diff --git a/drivers/vhm/vhm_ioreq.c b/drivers/vhm/vhm_ioreq.c index bf55a0138943..c91a60598114 100644 --- a/drivers/vhm/vhm_ioreq.c +++ b/drivers/vhm/vhm_ioreq.c @@ -101,7 +101,7 @@ struct ioreq_client { /* * this req records the req number this client need handle */ - atomic_t req; + DECLARE_BITMAP(ioreqs_map, VHM_REQUEST_MAX); /* * client ioreq handler: @@ -434,7 +434,7 @@ static inline bool is_destroying(struct ioreq_client *client) static inline bool has_pending_request(struct ioreq_client *client) { if (client) - return (atomic_read(&client->req) > 0); + return !bitmap_empty(client->ioreqs_map, VHM_REQUEST_MAX); else return false; } @@ -482,7 +482,7 @@ static int ioreq_client_thread(void *data) if (has_pending_request(client)) { if (client->handler) { ret = client->handler(client->id, - client->req.counter); + client->ioreqs_map); if (ret < 0) BUG(); } else { @@ -800,7 +800,7 @@ int acrn_ioreq_distribute_request(struct vhm_vm *vm) } else { req->processed = REQ_STATE_PROCESSING; req->client = client->id; - atomic_inc(&client->req); + set_bit(i, client->ioreqs_map); } } } @@ -831,7 +831,7 @@ int acrn_ioreq_complete_request(int client_id, uint64_t vcpu) return -EINVAL; } - atomic_dec(&client->req); + clear_bit(vcpu, client->ioreqs_map); ret = hcall_notify_req_finish(client->vmid, vcpu); if (ret < 0) { pr_err("vhm-ioreq: failed to notify request finished !\n"); diff --git a/include/linux/vbs/vbs.h b/include/linux/vbs/vbs.h index e4ecba020b08..33ca072c5a83 100644 --- a/include/linux/vbs/vbs.h +++ b/include/linux/vbs/vbs.h @@ -181,7 +181,7 @@ struct virtio_dev_info { * This is the callback function to be registered to VHM, * so that VBS gets notified when frontend accessed the register. */ - int (*dev_notify)(int, int); + int (*dev_notify)(int, unsigned long *); /** @vqs: virtqueue(s) of this device */ struct virtio_vq_info *vqs; /** @curq: current virtqueue index */ @@ -269,10 +269,10 @@ long virtio_dev_deregister(struct virtio_dev_info *dev); * frontend. * * @dev: Pointer to VBS-K device data struct - * @req_cnt: Number of requests need to handle, provided by VHM + * @ioreqs_map: requests bitmap need to handle, provided by VHM * * Return: >=0 on virtqueue index, <0 on error */ -int virtio_vq_index_get(struct virtio_dev_info *dev, int req_cnt); +int virtio_vq_index_get(struct virtio_dev_info *dev, unsigned long *ioreqs_map); #endif diff --git a/include/linux/vhm/acrn_vhm_ioreq.h b/include/linux/vhm/acrn_vhm_ioreq.h index fbf69b37d356..52b3ac83203c 100644 --- a/include/linux/vhm/acrn_vhm_ioreq.h +++ b/include/linux/vhm/acrn_vhm_ioreq.h @@ -61,7 +61,7 @@ #include #include -typedef int (*ioreq_handler_t)(int client_id, int req); +typedef int (*ioreq_handler_t)(int client_id, unsigned long *ioreqs_map); /** * acrn_ioreq_create_client - create ioreq client From 512a087f6a93ed1369ae0eb03864cfa6b6773d1c Mon Sep 17 00:00:00 2001 From: Shuo Liu Date: Fri, 31 Aug 2018 10:59:02 +0800 Subject: [PATCH 0078/1103] vhm: use correct string length MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fix below compile warning: drivers/vhm/vhm_ioreq.c:207:3: warning: ‘strncpy’ specified bound 16 equals destination size [-Wstringop-truncation] strncpy(client->name, name, sizeof(client->name)); Signed-off-by: Shuo Liu Reviewed-by: Jason Chen CJ --- drivers/vhm/vhm_ioreq.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/vhm/vhm_ioreq.c b/drivers/vhm/vhm_ioreq.c index c91a60598114..5cb15b5badd8 100644 --- a/drivers/vhm/vhm_ioreq.c +++ b/drivers/vhm/vhm_ioreq.c @@ -204,7 +204,7 @@ int acrn_ioreq_create_client(unsigned long vmid, ioreq_handler_t handler, client->vmid = vmid; if (name) - strncpy(client->name, name, 16); + strncpy(client->name, name, sizeof(client->name) - 1); spin_lock_init(&client->range_lock); INIT_LIST_HEAD(&client->range_list); init_waitqueue_head(&client->wq); From 0812b351d8a75a2157e738e7b841153c5c4a2aab Mon Sep 17 00:00:00 2001 From: Junjie Mao Date: Fri, 31 Aug 2018 10:59:03 +0800 Subject: [PATCH 0079/1103] vhm: adapt to the new state transition of VHM requests This is the counter-part of VHM request state transition update. Instead of using two members (namely ''valid'' and ''processed''), the new state transition uses a single member (i.e. ''processed) following the transition pattern below. FREE -> PENDING -> PROCESSING -> COMPLETE -> FREE -> ... The ownership of a VHM request shifts according to its state. Namely: 1) When a request is in COMPLETE or FREE state, the request is owned by the hypervisor. SOS (VHM or DM) shall not read or write the internals of the request except the state. 2) When a request is in PENDING or PROCESSING state, the request is owned by SOS. The hypervisor shall not read or write the request other than the state. Atomic operations should be used to access the state. For this the ''processed'' member in vhm_request is typed 'atomic_t' which is essentially the same 32-bit signed integer. This allows us to use atomic operations on the state directly, instead of casting it everywhere. It is also worth noting that atomic_set() in Linux does not imply a memory fence; it only includes a barrier to prevent compiler reordering. Thus smp_mb() are added before changing the state to COMPLETE so that the hypervisor always has the right results after it sees the state change. v1 -> v2: * Keep the original flow (i.e. update state after a client is found) instead of atomic_cmpxchg, given that acrn_ioreq_distribute_request() is called in tasklet and only on cpu 0. * Add documentation on struct vhm_request, following the kernel-doc style. Signed-off-by: Junjie Mao Reviewed-by: Zhao Yakui --- drivers/vbs/vbs.c | 5 +- drivers/vhm/vhm_ioreq.c | 11 +++- include/linux/vhm/acrn_common.h | 113 +++++++++++++++++++++++++++----- 3 files changed, 108 insertions(+), 21 deletions(-) diff --git a/drivers/vbs/vbs.c b/drivers/vbs/vbs.c index 1d2e1b40728e..9b7a279bca90 100644 --- a/drivers/vbs/vbs.c +++ b/drivers/vbs/vbs.c @@ -161,7 +161,7 @@ int virtio_vq_index_get(struct virtio_dev_info *dev, unsigned long *ioreqs_map) if (vcpu == dev->_ctx.max_vcpu) break; req = &dev->_ctx.req_buf[vcpu]; - if (req->valid && req->processed == REQ_STATE_PROCESSING && + if (atomic_read(&req->processed) == REQ_STATE_PROCESSING && req->client == dev->_ctx.vhm_client_id) { if (req->reqs.pio_request.direction == REQUEST_READ) { /* currently we handle kick only, @@ -180,7 +180,8 @@ int virtio_vq_index_get(struct virtio_dev_info *dev, unsigned long *ioreqs_map) else val = req->reqs.mmio_request.value; } - req->processed = REQ_STATE_SUCCESS; + smp_mb(); + atomic_set(&req->processed, REQ_STATE_COMPLETE); acrn_ioreq_complete_request(req->client, vcpu); } } diff --git a/drivers/vhm/vhm_ioreq.c b/drivers/vhm/vhm_ioreq.c index 5cb15b5badd8..5716bc5968d5 100644 --- a/drivers/vhm/vhm_ioreq.c +++ b/drivers/vhm/vhm_ioreq.c @@ -707,7 +707,8 @@ static int handle_cf8cfc(struct vhm_vm *vm, struct vhm_request *req, int vcpu) } if (req_handled) { - req->processed = REQ_STATE_SUCCESS; + smp_mb(); + atomic_set(&req->processed, REQ_STATE_COMPLETE); if (hcall_notify_req_finish(vm->vmid, vcpu) < 0) { pr_err("vhm-ioreq: failed to " "notify request finished !\n"); @@ -788,7 +789,11 @@ int acrn_ioreq_distribute_request(struct vhm_vm *vm) vcpu_num = atomic_read(&vm->vcpu_num); for (i = 0; i < vcpu_num; i++) { req = vm->req_buf->req_queue + i; - if (req->valid && (req->processed == REQ_STATE_PENDING)) { + + /* This function is called in tasklet only on SOS CPU0. Thus it + * is safe to read the state first and update it later as long + * as the update is atomic. */ + if (atomic_read(&req->processed) == REQ_STATE_PENDING) { if (handle_cf8cfc(vm, req, i)) continue; client = acrn_ioreq_find_client_by_request(vm, req); @@ -798,8 +803,8 @@ int acrn_ioreq_distribute_request(struct vhm_vm *vm) "BUG\n"); BUG(); } else { - req->processed = REQ_STATE_PROCESSING; req->client = client->id; + atomic_set(&req->processed, REQ_STATE_PROCESSING); set_bit(i, client->ioreqs_map); } } diff --git a/include/linux/vhm/acrn_common.h b/include/linux/vhm/acrn_common.h index 91951b7e1fc3..6269b07dcea4 100644 --- a/include/linux/vhm/acrn_common.h +++ b/include/linux/vhm/acrn_common.h @@ -52,6 +52,8 @@ #ifndef ACRN_COMMON_H #define ACRN_COMMON_H +#include + /* * Common structures for ACRN/VHM/DM */ @@ -62,9 +64,9 @@ #define VHM_REQUEST_MAX 16 #define REQ_STATE_PENDING 0 -#define REQ_STATE_SUCCESS 1 +#define REQ_STATE_COMPLETE 1 #define REQ_STATE_PROCESSING 2 -#define REQ_STATE_FAILED -1 +#define REQ_STATE_FREE 3 #define REQ_PORTIO 0 #define REQ_MMIO 1 @@ -111,13 +113,88 @@ struct pci_request { int32_t reg; } __attribute__((aligned(8))); -/* vhm_request are 256Bytes aligned */ +/** + * struct vhm_request - 256-byte VHM request + * + * The state transitions of a VHM request are: + * + * FREE -> PENDING -> PROCESSING -> COMPLETE -> FREE -> ... + * \ / + * +--> FAILED -+ + * + * When a request is in COMPLETE or FREE state, the request is owned by the + * hypervisor. SOS (VHM or DM) shall not read or write the internals of the + * request except the state. + * + * When a request is in PENDING or PROCESSING state, the request is owned by + * SOS. The hypervisor shall not read or write the request other than the state. + * + * Based on the rules above, a typical VHM request lifecycle should looks like + * the following. + * + * (assume the initial state is FREE) + * + * SOS vCPU 0 SOS vCPU x UOS vCPU y + * + * hypervisor: + * fill in type, addr, etc. + * pause UOS vcpu y + * set state to PENDING (a) + * fire upcall to SOS vCPU 0 + * + * VHM: + * scan for pending requests + * set state to PROCESSING (b) + * assign requests to clients (c) + * + * client: + * scan for assigned requests + * handle the requests (d) + * set state to COMPLETE + * notify the hypervisor + * + * hypervisor: + * resume UOS vcpu y (e) + * + * hypervisor: + * post-work (f) + * set state to FREE + * + * Note that the following shall hold. + * + * 1. (a) happens before (b) + * 2. (c) happens before (d) + * 3. (e) happens before (f) + * 4. One vCPU cannot trigger another I/O request before the previous one has + * completed (i.e. the state switched to FREE) + * + * Accesses to the state of a vhm_request shall be atomic and proper barriers + * are needed to ensure that: + * + * 1. Setting state to PENDING is the last operation when issuing a request in + * the hypervisor, as the hypervisor shall not access the request any more. + * + * 2. Due to similar reasons, setting state to COMPLETE is the last operation + * of request handling in VHM or clients in SOS. + */ struct vhm_request { - /* offset: 0bytes - 63bytes */ + /** + * @type: Type of this request. Byte offset: 0. + */ uint32_t type; + + /** + * @reserved0: Reserved fields. Byte offset: 4. + */ uint32_t reserved0[15]; - /* offset: 64bytes-127bytes */ + /** + * @reqs: Details about this request. + * + * For REQ_PORTIO, this has type pio_request. For REQ_MMIO and REQ_WP, + * this has type mmio_request. For REQ_PCICFG, this has type + * pci_request. Byte offset: 64. + */ union { struct pio_request pio_request; struct pci_request pci_request; @@ -125,20 +202,24 @@ struct vhm_request { uint64_t reserved1[8]; } reqs; - /* True: valid req which need VHM to process. - * ACRN write, VHM read only - **/ - int32_t valid; + /** + * @reserved1: Reserved fields. Byte offset: 128. + */ + uint32_t reserved1; - /* the client which is distributed to handle this request */ + /** + * @client: The client which is distributed to handle this request. + * + * Accessed by VHM only. Byte offset: 132. + */ int32_t client; - /* 1: VHM had processed and success - * 0: VHM had not yet processed - * -1: VHM failed to process. Invalid request - * VHM write, ACRN read only - **/ - int32_t processed; + /** + * @processed: The status of this request. + * + * Take REQ_STATE_xxx as values. Byte offset: 136. + */ + atomic_t processed; } __attribute__((aligned(256))); struct vhm_request_buffer { From 495fccc6a908ebf62235a0ac3241648f47b0e976 Mon Sep 17 00:00:00 2001 From: Shuo Liu Date: Fri, 31 Aug 2018 10:59:03 +0800 Subject: [PATCH 0080/1103] vhm: Add error handling for IC_CREATE_VM ioctl If returned with error after hypervisor creating the VM, then we have no chance to destroy it. Add the error handling to destroy the VM if fail. Signed-off-by: Shuo Liu Reviewed-by: Jason Chen CJ Reviewed-by: Zhao Yakui Acked-by: Eddie Dong --- drivers/char/vhm/vhm_dev.c | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/drivers/char/vhm/vhm_dev.c b/drivers/char/vhm/vhm_dev.c index 5884fd2b259e..1286f7750906 100644 --- a/drivers/char/vhm/vhm_dev.c +++ b/drivers/char/vhm/vhm_dev.c @@ -216,21 +216,27 @@ static long vhm_dev_ioctl(struct file *filep, } if (copy_to_user((void *)ioctl_param, &created_vm, - sizeof(struct acrn_create_vm))) - return -EFAULT; - + sizeof(struct acrn_create_vm))) { + ret = -EFAULT; + goto create_vm_fail; + } vm->vmid = created_vm.vmid; if (created_vm.vm_flag & SECURE_WORLD_ENABLED) { ret = init_trusty(vm); if (ret < 0) { pr_err("vhm: failed to init trusty for VM!\n"); - return ret; + goto create_vm_fail; } } pr_info("vhm: VM %d created\n", created_vm.vmid); break; +create_vm_fail: + hcall_destroy_vm(created_vm.vmid); + vm->vmid = ACRN_INVALID_VMID; + break; + } case IC_START_VM: { From 40a8dec1f984ea2b983ca67eb4818bd3d7bdf527 Mon Sep 17 00:00:00 2001 From: Shuo Liu Date: Fri, 31 Aug 2018 10:59:03 +0800 Subject: [PATCH 0081/1103] vhm: setup ioreq shared buf in IC_CREATE_VM ioctl ioreq shared buffer page is a necessary for each VM. So separate VM creating and shared buffer page setup into two ioctls is meaningless. This patch intends to move the ioreq shared buffer page setup into IC_CREATE_VM ioctl. With this change, we can create vhm ioreq client just after CREATE_VM step. We need be careful with the compatibility. To achieve it safely, we will do, 1) Do shared page setup in IC_CREATE_VM, what this patch do exactly 2) Move the shared page setup action into vm creating in DM. 3) Remove the ioctl IC_SET_IOREQ_BUFFER in kernel. Signed-off-by: Shuo Liu Reviewed-by: Jason Chen CJ Reviewed-by: Zhao Yakui Acked-by: Eddie Dong --- drivers/char/vhm/vhm_dev.c | 12 +++++++++++- drivers/vhm/vhm_ioreq.c | 2 +- include/linux/vhm/acrn_common.h | 5 ++++- 3 files changed, 16 insertions(+), 3 deletions(-) diff --git a/drivers/char/vhm/vhm_dev.c b/drivers/char/vhm/vhm_dev.c index 1286f7750906..152aba23d95f 100644 --- a/drivers/char/vhm/vhm_dev.c +++ b/drivers/char/vhm/vhm_dev.c @@ -230,8 +230,17 @@ static long vhm_dev_ioctl(struct file *filep, } } + if (created_vm.req_buf) { + ret = acrn_ioreq_init(vm, created_vm.req_buf); + if (ret < 0) + goto ioreq_buf_fail; + } + pr_info("vhm: VM %d created\n", created_vm.vmid); break; +ioreq_buf_fail: + if (created_vm.vm_flag & SECURE_WORLD_ENABLED) + deinit_trusty(vm); create_vm_fail: hcall_destroy_vm(created_vm.vmid); vm->vmid = ACRN_INVALID_VMID; @@ -310,8 +319,9 @@ static long vhm_dev_ioctl(struct file *filep, case IC_SET_IOREQ_BUFFER: { /* init ioreq buffer */ ret = acrn_ioreq_init(vm, (unsigned long)ioctl_param); - if (ret < 0) + if (ret < 0 && ret != -EEXIST) return ret; + ret = 0; break; } diff --git a/drivers/vhm/vhm_ioreq.c b/drivers/vhm/vhm_ioreq.c index 5716bc5968d5..960723b1778d 100644 --- a/drivers/vhm/vhm_ioreq.c +++ b/drivers/vhm/vhm_ioreq.c @@ -881,7 +881,7 @@ int acrn_ioreq_init(struct vhm_vm *vm, unsigned long vma) int ret; if (vm->req_buf) - BUG(); + return -EEXIST; ret = get_user_pages_fast(vma, 1, 1, &page); if (unlikely(ret != 1) || (page == NULL)) { diff --git a/include/linux/vhm/acrn_common.h b/include/linux/vhm/acrn_common.h index 6269b07dcea4..dfe89309fb17 100644 --- a/include/linux/vhm/acrn_common.h +++ b/include/linux/vhm/acrn_common.h @@ -253,8 +253,11 @@ struct acrn_create_vm { */ uint64_t vm_flag; + /** guest physical address of VM request_buffer */ + uint64_t req_buf; + /** Reserved for future use*/ - uint8_t reserved2[24]; + uint8_t reserved2[16]; } __attribute__((aligned(8))); /** From fc076632a7f8c0dd566cdc9f2092535dbb23ea3c Mon Sep 17 00:00:00 2001 From: Jian Jun Chen Date: Fri, 31 Aug 2018 10:59:03 +0800 Subject: [PATCH 0082/1103] VBS-K: add virtio_dev_reset A new ioctl VBS_RESET_DEV is introduced to support D3. VBS-U issues this ioctl to VBS-K driver when receives reset notification from FE driver. VBS-K driver should perform stop/flush/reset to react to this ioctl. virtio_dev_reset can be called in VBS-K driver's reset function. Signed-off-by: Jian Jun Chen Reviewed-by: Shuo Liu Reviewed-by: Yu Wang Reviewed-by: Zhao Yakui --- drivers/vbs/vbs.c | 19 +++++++++++++++++++ include/linux/vbs/vbs.h | 8 ++++++++ include/linux/vbs/vbs_common_if.h | 1 + 3 files changed, 28 insertions(+) diff --git a/drivers/vbs/vbs.c b/drivers/vbs/vbs.c index 9b7a279bca90..7427942c12f7 100644 --- a/drivers/vbs/vbs.c +++ b/drivers/vbs/vbs.c @@ -304,6 +304,25 @@ long virtio_dev_init(struct virtio_dev_info *dev, } EXPORT_SYMBOL_GPL(virtio_dev_init); +long virtio_dev_reset(struct virtio_dev_info *dev) +{ + int i; + + for (i = 0; i < dev->nvq; i++) + virtio_vq_reset(&dev->vqs[i]); + + memset(dev->name, 0, sizeof(dev->name)); + dev->_ctx.vmid = 0; + dev->nvq = 0; + dev->negotiated_features = 0; + dev->io_range_start = 0; + dev->io_range_len = 0; + dev->io_range_type = PIO_RANGE; + + return 0; +} +EXPORT_SYMBOL_GPL(virtio_dev_reset); + static int __init vbs_init(void) { return 0; diff --git a/include/linux/vbs/vbs.h b/include/linux/vbs/vbs.h index 33ca072c5a83..30df8ebf68a0 100644 --- a/include/linux/vbs/vbs.h +++ b/include/linux/vbs/vbs.h @@ -275,4 +275,12 @@ long virtio_dev_deregister(struct virtio_dev_info *dev); */ int virtio_vq_index_get(struct virtio_dev_info *dev, unsigned long *ioreqs_map); +/** + * virtio_dev_reset - reset a VBS-K device + * + * @dev: Pointer to VBS-K device data struct + * + * Return: 0 on success, <0 on error + */ +long virtio_dev_reset(struct virtio_dev_info *dev); #endif diff --git a/include/linux/vbs/vbs_common_if.h b/include/linux/vbs/vbs_common_if.h index 25d6002c92bb..59c65786c088 100644 --- a/include/linux/vbs/vbs_common_if.h +++ b/include/linux/vbs/vbs_common_if.h @@ -92,5 +92,6 @@ struct vbs_dev_info { #define VBS_SET_DEV _IOW(VBS_IOCTL, 0x00, struct vbs_dev_info) #define VBS_SET_VQ _IOW(VBS_IOCTL, 0x01, struct vbs_vqs_info) +#define VBS_RESET_DEV _IO(VBS_IOCTL, 0x02) #endif From 6ca0cc6857350004365227467c44c1cd711eefac Mon Sep 17 00:00:00 2001 From: Jian Jun Chen Date: Fri, 31 Aug 2018 10:59:03 +0800 Subject: [PATCH 0083/1103] VBS-K: Check whether vhm_client_id is valid before deregister Need to check whether vhm_client_id is valid before trying to deregister from VHM. It is possbile that virtio_dev_deregister is called more than once by vbs-k driver. Signed-off-by: Jian Jun Chen Reviewed-by: Zhao Yakui --- drivers/vbs/vbs.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/vbs/vbs.c b/drivers/vbs/vbs.c index 7427942c12f7..c2d8a5262a8d 100644 --- a/drivers/vbs/vbs.c +++ b/drivers/vbs/vbs.c @@ -135,12 +135,15 @@ long virtio_dev_register(struct virtio_dev_info *dev) long virtio_dev_deregister(struct virtio_dev_info *dev) { + if (dev->_ctx.vhm_client_id < 0) + return 0; + acrn_ioreq_del_iorange(dev->_ctx.vhm_client_id, dev->io_range_type ? REQ_MMIO : REQ_PORTIO, dev->io_range_start, dev->io_range_start + dev->io_range_len); - acrn_ioreq_destroy_client(dev->_ctx.vhm_client_id); + dev->_ctx.vhm_client_id = -1; return 0; } @@ -300,6 +303,8 @@ long virtio_dev_init(struct virtio_dev_info *dev, for (i = 0; i < nvq; i++) virtio_vq_reset(&vqs[i]); + dev->_ctx.vhm_client_id = -1; + return 0; } EXPORT_SYMBOL_GPL(virtio_dev_init); From 593582fe610a8100fc7e7a4b4ed92ebb63095cac Mon Sep 17 00:00:00 2001 From: Jian Jun Chen Date: Fri, 31 Aug 2018 10:59:03 +0800 Subject: [PATCH 0084/1103] VBS-K: add reset support for vbs_rng Add reset support for vbs_rng, it acts as an example about how to support D3. Signed-off-by: Jian Jun Chen Reviewed-by: Shuo Liu Reviewed-by: Yu Wang Reviewed-by: Zhao Yakui --- drivers/vbs/vbs_rng.c | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/drivers/vbs/vbs_rng.c b/drivers/vbs/vbs_rng.c index 569d1d5d689c..45e17b086c29 100644 --- a/drivers/vbs/vbs_rng.c +++ b/drivers/vbs/vbs_rng.c @@ -123,7 +123,7 @@ static int vbs_rng_connection_cnt = 0; /* function declarations */ static int handle_kick(int client_id, unsigned long *ioreqs_map); -static void vbs_rng_reset(struct vbs_rng *rng); +static long vbs_rng_reset(struct vbs_rng *rng); static void vbs_rng_stop(struct vbs_rng *rng); static void vbs_rng_flush(struct vbs_rng *rng); #ifdef RUNTIME_CTRL @@ -319,7 +319,6 @@ static int vbs_rng_open(struct inode *inode, struct file *f) static int vbs_rng_release(struct inode *inode, struct file *f) { struct vbs_rng *rng = f->private_data; - int i; if (!rng) pr_err("%s: UNLIKELY rng NULL!\n", @@ -327,8 +326,6 @@ static int vbs_rng_release(struct inode *inode, struct file *f) vbs_rng_stop(rng); vbs_rng_flush(rng); - for (i = 0; i < VBS_K_RNG_VQ_MAX; i++) - virtio_vq_reset(&(rng->vqs[i])); /* device specific release */ vbs_rng_reset(rng); @@ -396,6 +393,12 @@ static long vbs_rng_ioctl(struct file *f, unsigned int ioctl, /* Increment counter */ vbs_rng_connection_cnt++; return r; + case VBS_RESET_DEV: + pr_debug("VBS_RESET_DEV ioctl:\n"); + vbs_rng_stop(rng); + vbs_rng_flush(rng); + r = vbs_rng_reset(rng); + return r; default: /*mutex_lock(&n->dev.mutex);*/ pr_debug("VBS_K generic ioctls!\n"); @@ -410,8 +413,9 @@ static long vbs_rng_ioctl(struct file *f, unsigned int ioctl, } /* device specific function to cleanup itself */ -static void vbs_rng_reset(struct vbs_rng *rng) +static long vbs_rng_reset(struct vbs_rng *rng) { + return virtio_dev_reset(&rng->dev); } /* device specific function */ From d0b05b310a5031c43422a0423464ce6b1ed1023f Mon Sep 17 00:00:00 2001 From: Jian Jun Chen Date: Fri, 31 Aug 2018 10:59:03 +0800 Subject: [PATCH 0085/1103] VBS-K: fix a bug due to incorrect check of return value of bitmap_empty When no bits are set in ioreqs_map, bitmap_empty returns 1. In this case we can just return 0 since no virtqueues are kicked. Signed-off-by: Jian Jun Chen Reviewed-by: Shuo Liu Reviewed-by: Yu Wang Reviewed-by: Zhao Yakui --- drivers/vbs/vbs_rng.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/vbs/vbs_rng.c b/drivers/vbs/vbs_rng.c index 45e17b086c29..fd2bb27af66e 100644 --- a/drivers/vbs/vbs_rng.c +++ b/drivers/vbs/vbs_rng.c @@ -256,8 +256,8 @@ static int handle_kick(int client_id, unsigned long *ioreqs_map) int val = -1; struct vbs_rng *rng; - if (unlikely(bitmap_empty(ioreqs_map, VHM_REQUEST_MAX) <= 0)) - return -EINVAL; + if (unlikely(bitmap_empty(ioreqs_map, VHM_REQUEST_MAX))) + return 0; pr_debug("%s: handle kick!\n", __func__); From ea53eb6cefc7cb10554dd10dcb2d6c7af2a1e297 Mon Sep 17 00:00:00 2001 From: Minggui Cao Date: Fri, 31 Aug 2018 10:59:03 +0800 Subject: [PATCH 0086/1103] VHM: remove panic action when ioreq fails. handle the ioreq failed cases instead of calling BUG(), which will cause system panic directly. Signed-off-by: Minggui Cao Reviewed-by: Zhao Yakui --- drivers/vhm/vhm_ioreq.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/drivers/vhm/vhm_ioreq.c b/drivers/vhm/vhm_ioreq.c index 960723b1778d..da61069b8d01 100644 --- a/drivers/vhm/vhm_ioreq.c +++ b/drivers/vhm/vhm_ioreq.c @@ -483,8 +483,10 @@ static int ioreq_client_thread(void *data) if (client->handler) { ret = client->handler(client->id, client->ioreqs_map); - if (ret < 0) - BUG(); + if (ret < 0) { + pr_err("vhm-ioreq: err:%d\n", ret); + break; + } } else { pr_err("vhm-ioreq: no ioreq handler\n"); break; @@ -799,9 +801,8 @@ int acrn_ioreq_distribute_request(struct vhm_vm *vm) client = acrn_ioreq_find_client_by_request(vm, req); if (client == NULL) { pr_err("vhm-ioreq: failed to " - "find ioreq client -> " - "BUG\n"); - BUG(); + "find ioreq client\n"); + return -EINVAL; } else { req->client = client->id; atomic_set(&req->processed, REQ_STATE_PROCESSING); From 3d60aa5e09d3798ec2cd8e810fa4c069c056df2b Mon Sep 17 00:00:00 2001 From: Ong Hock Yu Date: Fri, 31 Aug 2018 10:59:03 +0800 Subject: [PATCH 0087/1103] vbs: fix virtio_vq_index_get func handling of multi VQ concurrent request. Under multiple VQ use case, it is possible to have concurrent requests. Added support to return multiple vq index from all vcpu. Signed-off-by: Ong Hock Yu --- drivers/vbs/vbs.c | 27 ++++++++++++++++++++++----- drivers/vbs/vbs_rng.c | 2 +- include/linux/vbs/vbs.h | 11 ++++++++--- 3 files changed, 31 insertions(+), 9 deletions(-) diff --git a/drivers/vbs/vbs.c b/drivers/vbs/vbs.c index c2d8a5262a8d..0e0516ad8794 100644 --- a/drivers/vbs/vbs.c +++ b/drivers/vbs/vbs.c @@ -148,9 +148,12 @@ long virtio_dev_deregister(struct virtio_dev_info *dev) return 0; } -int virtio_vq_index_get(struct virtio_dev_info *dev, unsigned long *ioreqs_map) +int virtio_vqs_index_get(struct virtio_dev_info *dev, + unsigned long *ioreqs_map, + int *vqs_index, + int max_vqs_index) { - int val = -1; + int idx = 0; struct vhm_request *req; int vcpu; @@ -178,10 +181,24 @@ int virtio_vq_index_get(struct virtio_dev_info *dev, unsigned long *ioreqs_map) } else { pr_debug("%s: write request! type %d\n", __func__, req->type); + + if (idx == max_vqs_index) { + pr_warn("%s: The allocated vqs\n" + "size (%d) is smaller than the\n" + "number of vcpu (%d)! This\n" + "might caused the process of\n" + "some requests be delayed.", + __func__, max_vqs_index, + dev->_ctx.max_vcpu); + break; + } + if (dev->io_range_type == PIO_RANGE) - val = req->reqs.pio_request.value; + vqs_index[idx++] = + req->reqs.pio_request.value; else - val = req->reqs.mmio_request.value; + vqs_index[idx++] = + req->reqs.mmio_request.value; } smp_mb(); atomic_set(&req->processed, REQ_STATE_COMPLETE); @@ -189,7 +206,7 @@ int virtio_vq_index_get(struct virtio_dev_info *dev, unsigned long *ioreqs_map) } } - return val; + return idx; } static long virtio_vqs_info_set(struct virtio_dev_info *dev, diff --git a/drivers/vbs/vbs_rng.c b/drivers/vbs/vbs_rng.c index fd2bb27af66e..c5e28cc12c55 100644 --- a/drivers/vbs/vbs_rng.c +++ b/drivers/vbs/vbs_rng.c @@ -268,7 +268,7 @@ static int handle_kick(int client_id, unsigned long *ioreqs_map) return -EINVAL; } - val = virtio_vq_index_get(&rng->dev, ioreqs_map); + virtio_vqs_index_get(&rng->dev, ioreqs_map, &val, 1); if (val >= 0) handle_vq_kick(rng, val); diff --git a/include/linux/vbs/vbs.h b/include/linux/vbs/vbs.h index 30df8ebf68a0..d9d932c49e88 100644 --- a/include/linux/vbs/vbs.h +++ b/include/linux/vbs/vbs.h @@ -262,7 +262,7 @@ long virtio_dev_register(struct virtio_dev_info *dev); long virtio_dev_deregister(struct virtio_dev_info *dev); /** - * virtio_vq_index_get - get virtqueue index that frontend kicks + * virtio_vqs_index_get - get virtqueue indexes that frontend kicks * * This API is normally called in the VBS-K device's callback * function, to get value write to the "kick" register from @@ -270,10 +270,15 @@ long virtio_dev_deregister(struct virtio_dev_info *dev); * * @dev: Pointer to VBS-K device data struct * @ioreqs_map: requests bitmap need to handle, provided by VHM + * @vqs_index: array to store the vq indexes + * @max_vqs_index: size of vqs_index array * - * Return: >=0 on virtqueue index, <0 on error + * Return: Number of vq request */ -int virtio_vq_index_get(struct virtio_dev_info *dev, unsigned long *ioreqs_map); +int virtio_vqs_index_get(struct virtio_dev_info *dev, + unsigned long *ioreqs_map, + int *vqs_index, + int max_vqs_index); /** * virtio_dev_reset - reset a VBS-K device From ad54ec7ca79da2f2e5f457f5ac473b7328fcd0c8 Mon Sep 17 00:00:00 2001 From: Li Zhijian Date: Wed, 22 Aug 2018 16:12:20 +0000 Subject: [PATCH 0088/1103] vhm: init client->kthread_exit true Previously, there is a deadlock at below case - acrn-dm create gvt instance successfully - acrn-dm open uos image failed(wrong image path), the acrn-dm does some cleanup, like destroy gvt instance then acrn-dm stucks. when destroying gvt instance, it waits client->kthread_exit to be true while client->kthread_exit is set to be 0 at initializing and the thread is not created/started actually. V3: add vhm_create_kthread as a condition (Yakui) Signed-off-by: Li Zhijian Reviewed-by: Jason Chen CJ Reviewed-by: Zhao Yakui --- drivers/vhm/vhm_ioreq.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/vhm/vhm_ioreq.c b/drivers/vhm/vhm_ioreq.c index da61069b8d01..0bcb0e053947 100644 --- a/drivers/vhm/vhm_ioreq.c +++ b/drivers/vhm/vhm_ioreq.c @@ -149,6 +149,7 @@ static int alloc_client(void) if (!client) return -ENOMEM; client->id = i; + client->kthread_exit = true; clients[i] = client; return i; @@ -266,7 +267,7 @@ static void acrn_ioreq_destroy_client_pervm(struct ioreq_client *client, /* the client thread will mark kthread_exit flag as true before exit, * so wait for it exited. */ - while (!client->kthread_exit) + while (client->vhm_create_kthread && !client->kthread_exit) msleep(10); spin_lock_irqsave(&client->range_lock, flags); @@ -533,7 +534,9 @@ int acrn_ioreq_attach_client(int client_id, bool check_kthread_stop) "for client %s\n", client->name); return -ENOMEM; } + client->kthread_exit = false; } else { + client->kthread_exit = false; might_sleep(); if (check_kthread_stop) { From f5a0451af678556d48dc94c1fcb8eff1c5eadecd Mon Sep 17 00:00:00 2001 From: Li Zhijian Date: Fri, 31 Aug 2018 10:59:04 +0800 Subject: [PATCH 0089/1103] vhm: fix client use-after-free free_client() will free resource of client V2: adjust order to avoid use-after-free (yu1.wang@intel.com) Signed-off-by: Li Zhijian Reviewed-by: Jason Chen CJ Reviewed-by: Zhao Yakui --- drivers/vhm/vhm_ioreq.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/vhm/vhm_ioreq.c b/drivers/vhm/vhm_ioreq.c index 0bcb0e053947..6bf07e812d03 100644 --- a/drivers/vhm/vhm_ioreq.c +++ b/drivers/vhm/vhm_ioreq.c @@ -282,10 +282,11 @@ static void acrn_ioreq_destroy_client_pervm(struct ioreq_client *client, spin_lock_irqsave(&vm->ioreq_client_lock, flags); list_del(&client->list); spin_unlock_irqrestore(&vm->ioreq_client_lock, flags); - free_client(client->id); if (client->id == vm->ioreq_fallback_client) vm->ioreq_fallback_client = -1; + + free_client(client->id); } void acrn_ioreq_destroy_client(int client_id) From af9da91eaf76f9e4a3a55996cc053165505912e2 Mon Sep 17 00:00:00 2001 From: Edwin Zhai Date: Wed, 5 Sep 2018 09:35:05 +0800 Subject: [PATCH 0090/1103] Adds new API for unmap memseg It is required when DM unmap ptdev BAR in deinit Tracked-on: projectacrn/acrn-hypervisor#1146 Signed-off-by: Edwin Zhai Reviewed-by: Yin Fengwei Reviewed-by: Zhao Yakui --- drivers/char/vhm/vhm_dev.c | 11 +++++++++++ drivers/vhm/vhm_mm.c | 17 +++++++++++++++++ include/linux/vhm/acrn_vhm_mm.h | 11 +++++++++++ include/linux/vhm/vhm_ioctl_defs.h | 1 + 4 files changed, 40 insertions(+) diff --git a/drivers/char/vhm/vhm_dev.c b/drivers/char/vhm/vhm_dev.c index 152aba23d95f..6e351c387928 100644 --- a/drivers/char/vhm/vhm_dev.c +++ b/drivers/char/vhm/vhm_dev.c @@ -316,6 +316,17 @@ static long vhm_dev_ioctl(struct file *filep, break; } + case IC_UNSET_MEMSEG: { + struct vm_memmap memmap; + + if (copy_from_user(&memmap, (void *)ioctl_param, + sizeof(struct vm_memmap))) + return -EFAULT; + + ret = unmap_guest_memseg(vm, &memmap); + break; + } + case IC_SET_IOREQ_BUFFER: { /* init ioreq buffer */ ret = acrn_ioreq_init(vm, (unsigned long)ioctl_param); diff --git a/drivers/vhm/vhm_mm.c b/drivers/vhm/vhm_mm.c index 4d5854d0c139..6bea6688ddc0 100644 --- a/drivers/vhm/vhm_mm.c +++ b/drivers/vhm/vhm_mm.c @@ -208,6 +208,23 @@ int map_guest_memseg(struct vhm_vm *vm, struct vm_memmap *memmap) return 0; } +int unmap_guest_memseg(struct vhm_vm *vm, struct vm_memmap *memmap) +{ + /* only handle mmio */ + if (memmap->type != VM_MEMMAP_MMIO) { + pr_err("vhm: %s invalid memmap type: %d for unmap\n", + __func__, memmap->type); + return -EINVAL; + } + + if (del_memory_region(vm->vmid, memmap->gpa, memmap->len) < 0) { + pr_err("vhm: failed to del memory region %ld!\n", vm->vmid); + return -EFAULT; + } + + return 0; +} + void free_guest_mem(struct vhm_vm *vm) { return hugepage_free_guest(vm); diff --git a/include/linux/vhm/acrn_vhm_mm.h b/include/linux/vhm/acrn_vhm_mm.h index 62aed3466e9f..7b6b1b40615b 100644 --- a/include/linux/vhm/acrn_vhm_mm.h +++ b/include/linux/vhm/acrn_vhm_mm.h @@ -169,6 +169,17 @@ void free_guest_mem(struct vhm_vm *vm); */ int map_guest_memseg(struct vhm_vm *vm, struct vm_memmap *memmap); +/** + * unmap_guest_memseg - unset guest mmapping of memory set by + * map_guest_memseg + * + * @vm: pointer to guest vm + * @memmap: pointer to guest memory mapping info + * + * Return: + */ +int unmap_guest_memseg(struct vhm_vm *vm, struct vm_memmap *memmap); + int init_trusty(struct vhm_vm *vm); void deinit_trusty(struct vhm_vm *vm); diff --git a/include/linux/vhm/vhm_ioctl_defs.h b/include/linux/vhm/vhm_ioctl_defs.h index cf4f63211aa2..6c09157a9bef 100644 --- a/include/linux/vhm/vhm_ioctl_defs.h +++ b/include/linux/vhm/vhm_ioctl_defs.h @@ -94,6 +94,7 @@ /* IC_ALLOC_MEMSEG not used */ #define IC_ALLOC_MEMSEG _IC_ID(IC_ID, IC_ID_MEM_BASE + 0x00) #define IC_SET_MEMSEG _IC_ID(IC_ID, IC_ID_MEM_BASE + 0x01) +#define IC_UNSET_MEMSEG _IC_ID(IC_ID, IC_ID_MEM_BASE + 0x02) /* PCI assignment*/ #define IC_ID_PCI_BASE 0x50UL From 7f3e2f23b1486a16fc8c2078efd6cc8f4e40ad4d Mon Sep 17 00:00:00 2001 From: Zhi Jin Date: Wed, 1 Aug 2018 14:58:50 +0800 Subject: [PATCH 0091/1103] sos: vhm: add HC_SETUP_HV_NPK_LOG hypercall This hypercall is used to enable/disable/configure the hypervisor NPK log. Tracked-On: https://github.com/projectacrn/acrn-hypervisor/issues/1138 Signed-off-by: Zhi Jin Reviewed-by: Zhao Yakui --- drivers/vhm/vhm_hypercall.c | 5 +++++ include/linux/vhm/acrn_hv_defs.h | 18 ++++++++++++++++++ include/linux/vhm/vhm_hypercall.h | 1 + 3 files changed, 24 insertions(+) diff --git a/drivers/vhm/vhm_hypercall.c b/drivers/vhm/vhm_hypercall.c index 9a92d6888b90..d994835dd924 100644 --- a/drivers/vhm/vhm_hypercall.c +++ b/drivers/vhm/vhm_hypercall.c @@ -92,6 +92,11 @@ inline long hcall_setup_sbuf(unsigned long sbuf_head) return acrn_hypercall1(HC_SETUP_SBUF, sbuf_head); } +inline long hcall_setup_hv_npk_log(unsigned long hv_npk_log) +{ + return acrn_hypercall1(HC_SETUP_HV_NPK_LOG, hv_npk_log); +} + inline long hcall_set_sstate_data(unsigned long sx_data_addr) { return acrn_hypercall1(HC_PM_SET_SSTATE_DATA, sx_data_addr); diff --git a/include/linux/vhm/acrn_hv_defs.h b/include/linux/vhm/acrn_hv_defs.h index 223702c0330c..24ca7be2c2b9 100644 --- a/include/linux/vhm/acrn_hv_defs.h +++ b/include/linux/vhm/acrn_hv_defs.h @@ -108,6 +108,7 @@ /* DEBUG */ #define HC_ID_DBG_BASE 0x60UL #define HC_SETUP_SBUF _HC_ID(HC_ID, HC_ID_DBG_BASE + 0x00) +#define HC_SETUP_HV_NPK_LOG _HC_ID(HC_ID, HC_ID_DBG_BASE + 0x01) /* Power management */ #define HC_ID_PM_BASE 0x80UL @@ -188,6 +189,23 @@ struct sbuf_setup_param { uint64_t gpa; } __attribute__((aligned(8))); +struct hv_npk_log_param { + /* the setup command for the hypervisor NPK log */ + uint16_t cmd; + + /* the setup result for the hypervisor NPK log */ + uint16_t res; + + /* the loglevel for the hypervisor NPK log */ + uint16_t loglevel; + + /* Reserved */ + uint16_t reserved; + + /* the MMIO address for the hypervisor NPK log */ + uint64_t mmio_addr; +} __attribute__((aligned(8))); + struct vm_gpa2hpa { uint64_t gpa; /* IN: gpa to translation */ uint64_t hpa; /* OUT: -1 means invalid gpa */ diff --git a/include/linux/vhm/vhm_hypercall.h b/include/linux/vhm/vhm_hypercall.h index 0a3869b356b8..703b35bec053 100644 --- a/include/linux/vhm/vhm_hypercall.h +++ b/include/linux/vhm/vhm_hypercall.h @@ -146,6 +146,7 @@ inline long hcall_destroy_vm(unsigned long vmid); inline long hcall_reset_vm(unsigned long vmid); inline long hcall_query_vm_state(unsigned long vmid); inline long hcall_setup_sbuf(unsigned long sbuf_head); +inline long hcall_setup_hv_npk_log(unsigned long hv_npk_log); inline long hcall_set_sstate_data(unsigned long sx_data_addr); inline long hcall_get_cpu_state(unsigned long cmd, unsigned long state_pa); inline long hcall_set_memory_regions(unsigned long pa_regions); From 3167fcbdf777e4119fed12a57787ff4db05aae18 Mon Sep 17 00:00:00 2001 From: Zhi Jin Date: Wed, 1 Aug 2018 14:58:50 +0800 Subject: [PATCH 0092/1103] acrn: add hv_npk_log module The hv_npk_log driver is used to enable/disable/configure the hypervisor NPK log via the hypercall HC_SETUP_HV_NPK_LOG. Tracked-On: https://github.com/projectacrn/acrn-hypervisor/issues/1138 Signed-off-by: Zhi Jin Reviewed-by: Zhao Yakui --- drivers/acrn/Kconfig | 8 + drivers/acrn/Makefile | 1 + drivers/acrn/hv_npk_log.c | 384 ++++++++++++++++++++++++++++++++++++++ drivers/acrn/hv_npk_log.h | 109 +++++++++++ 4 files changed, 502 insertions(+) create mode 100644 drivers/acrn/hv_npk_log.c create mode 100644 drivers/acrn/hv_npk_log.h diff --git a/drivers/acrn/Kconfig b/drivers/acrn/Kconfig index 9fc4cae04a56..706aba14ad00 100644 --- a/drivers/acrn/Kconfig +++ b/drivers/acrn/Kconfig @@ -19,3 +19,11 @@ config ACRN_HVLOG ---help--- This is the Trace driver for the Intel ACRN hypervisor log. You can say y to build it into the kernel. + +config ACRN_HV_NPK_LOG + bool "Intel ACRN Hypervisor NPK Log" + depends on INTEL_TH + depends on ACRN_VHM + ---help--- + The driver is to configure/enable/disable the Intel ACRN hypervisor + NPK log. diff --git a/drivers/acrn/Makefile b/drivers/acrn/Makefile index 05dd698e8171..d0e14f43bcba 100644 --- a/drivers/acrn/Makefile +++ b/drivers/acrn/Makefile @@ -1,3 +1,4 @@ obj-$(CONFIG_ACRN_SHARED_BUFFER) += sbuf.o obj-$(CONFIG_ACRN_TRACE) += acrn_trace.o obj-$(CONFIG_ACRN_HVLOG) += acrn_hvlog.o +obj-$(CONFIG_ACRN_HV_NPK_LOG) += hv_npk_log.o diff --git a/drivers/acrn/hv_npk_log.c b/drivers/acrn/hv_npk_log.c new file mode 100644 index 000000000000..2303b9a72a3a --- /dev/null +++ b/drivers/acrn/hv_npk_log.c @@ -0,0 +1,384 @@ +/* + * ACRN Hypervisor NPK Log + * + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright (c) 2018 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * 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. + * + * BSD LICENSE + * + * Copyright (C) 2018 Intel Corporation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 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 + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +/* + * The hv_npk_log driver is to enable/disable/configure the ACRN hypervisor + * NPK log. It communicates with the hypervisor via the HC_SETUP_HV_NPK_LOG + * hypercall, and exposes the interface to usperspace via sysfs. + * With this driver, the user could: + * 1. Configure the Master/Channel used for the hypervisor NPK log. + * 2. Configure the log level of the hypervisor NPK log. + * 3. Enable/Disable the hypervisor NPK log. + * + * +-------------------------------------+ + * | Interfaces exposed by the driver | + * SOS | U: and used by the applications | + * | +----------------^----------------+ | + * | | | + * | K: +---------v---------+ | + * | | hv_npk_log driver | | + * | +---------^---------+ | + * +------------------|------------------+ + * | HC_SETUP_HV_NPK_LOG + * +------------------|------------------+ + * HV | +-------v-------+ | + * | | npk_log | | + * | +-------+-------+ | + * +------------------|------------------+ + * | + * +-------------+----v----+-------------+ + * NPK MMIO | | /////// | | + * | | /////// | | + * +-------------+----+----+-------------+ + * | + * +---+ The Master/Channel reserved + * for the hypervisor NPK logs + */ + +#define pr_fmt(fmt) "ACRN HV_NPK_LOG: " fmt + +#include +#include +#include +#include +#include +#include +#include "hv_npk_log.h" + +#define HV_NPK_LOG_USAGE \ + "echo \"E[nable] [#M #C] [#L]\" to enable the ACRN HV NPK Log\n" \ + "echo \"D[isable]\" to disable the ACRN HV NPK Log\n" \ + "echo \"C[onfig] [#M #C] [#L]\" to configure the ACRN HV NPK Log\n" + +static struct hv_npk_log_conf *hnl_conf; + +/* Try to get the master/channel based on the given address */ +static int addr2mc(phys_addr_t addr, int *master, int *channel) +{ + phys_addr_t offset, base; + unsigned int start, end; + int c, m; + + if (!hnl_conf || !master || !channel || !hnl_conf->nchan) + return -EINVAL; + + /* check if the addr belongs to SW_BAR or FW_BAR */ + if (addr >= hnl_conf->stmr_base && addr < hnl_conf->stmr_end) { + base = hnl_conf->stmr_base; + start = hnl_conf->sw_start; + end = hnl_conf->sw_end; + } else if (addr >= hnl_conf->ftmr_base && addr < hnl_conf->ftmr_end) { + base = hnl_conf->ftmr_base; + start = hnl_conf->fw_start; + end = hnl_conf->fw_end; + } else { + return -EINVAL; + } + + offset = addr - base; + if (offset % NPK_CHAN_SIZE) + return -EINVAL; + + c = offset / NPK_CHAN_SIZE; + m = c / hnl_conf->nchan; + c = c % hnl_conf->nchan; + if (start + m > end) + return -EINVAL; + + *channel = c; + *master = m + start; + return 0; +} + +/* Try to get the MMIO address based on the given master/channel */ +static int mc2addr(phys_addr_t *addr, unsigned int master, unsigned int channel) +{ + phys_addr_t base; + unsigned int start; + + if (!hnl_conf || !addr || !hnl_conf->nchan + || channel >= hnl_conf->nchan) + return -EINVAL; + + /* check if the master belongs to SW_BAR or FW_BAR */ + if (master >= hnl_conf->sw_start && master <= hnl_conf->sw_end) { + base = hnl_conf->stmr_base; + start = hnl_conf->sw_start; + } else if (master >= hnl_conf->fw_start && master <= hnl_conf->fw_end) { + base = hnl_conf->ftmr_base; + start = hnl_conf->fw_start; + } else { + return -EINVAL; + } + + *addr = base + ((master - start) * hnl_conf->nchan + channel) + * NPK_CHAN_SIZE; + return 0; +} + +static int npk_dev_match(struct device *dev, void *data) +{ + return 1; +} + +/* Check if the NPK device/driver exists, and get info from them */ +static int load_npk_conf(void) +{ + u32 reg; + int err; + void __iomem *base; + struct device *dev; + struct pci_dev *pdev; + struct device_driver *drv; + + /* check if the NPK device and driver exists */ + drv = driver_find(NPK_DRV_NAME, &pci_bus_type); + if (!drv) { + pr_err("Cannot find the %s driver\n", NPK_DRV_NAME); + return -ENODEV; + } + + dev = driver_find_device(drv, NULL, NULL, npk_dev_match); + if (!dev) { + pr_err("Cannot find the NPK device\n"); + return -ENODEV; + } + + hnl_conf = kzalloc(sizeof(struct hv_npk_log_conf), GFP_KERNEL); + if (!hnl_conf) + return -ENOMEM; + + /* get the base address of FW_BAR */ + pdev = to_pci_dev(dev); + err = pci_read_config_dword(pdev, PCI_REG_FW_LBAR, ®); + if (err) + return err; + hnl_conf->ftmr_base = reg & 0xfffc0000U; + err = pci_read_config_dword(pdev, PCI_REG_FW_UBAR, ®); + if (err) + return err; + hnl_conf->ftmr_base |= ((phys_addr_t)reg << 32); + + /* read out some configurations of NPK */ + base = devm_ioremap(dev, pdev->resource[TH_MMIO_CONFIG].start, + resource_size(&(pdev->resource[TH_MMIO_CONFIG]))); + if (!base) { + pr_err("Cannot map the NPK configuration address\n"); + goto error; + } + + reg = ioread32(base + REG_STH_STHCAP0); + hnl_conf->sw_start = reg & 0xffffU; + hnl_conf->sw_end = reg >> 16; + reg = ioread32(base + REG_STH_STHCAP1); + hnl_conf->nchan = reg & 0xffU; + hnl_conf->fw_end = reg >> 24; + reg = ioread32(base + REG_STH_STHCAP2); + hnl_conf->fw_start = reg & 0xffffU; + devm_iounmap(dev, base); + + hnl_conf->status = HV_NPK_LOG_UNKNOWN; + hnl_conf->master = HV_NPK_LOG_UNKNOWN; + hnl_conf->channel = HV_NPK_LOG_UNKNOWN; + hnl_conf->loglevel = HV_NPK_LOG_UNKNOWN; + hnl_conf->stmr_base = pdev->resource[TH_MMIO_SW].start; + + if (hnl_conf->sw_end < hnl_conf->sw_start + || hnl_conf->fw_end < hnl_conf->fw_start + || hnl_conf->nchan == 0) + goto error; + + hnl_conf->stmr_end = hnl_conf->stmr_base + (hnl_conf->sw_end - + hnl_conf->sw_start) * hnl_conf->nchan * NPK_CHAN_SIZE; + hnl_conf->ftmr_end = hnl_conf->ftmr_base + (hnl_conf->fw_end - + hnl_conf->fw_start) * hnl_conf->nchan * NPK_CHAN_SIZE; + + return 0; + +error: + kfree(hnl_conf); + hnl_conf = NULL; + return -EINVAL; +} + +/* User interface to set the configuration */ +static int hv_npk_log_conf_set(const char *val, const struct kernel_param *kp) +{ + char **argv; + int i, argc, ret = -EINVAL; + struct hv_npk_log_param cmd; + unsigned int args[HV_NPK_LOG_MAX_PARAM]; + + if (!hnl_conf && load_npk_conf() < 0) + return -EINVAL; + + argv = argv_split(GFP_KERNEL, val, &argc); + if (!argv) + return -ENOMEM; + if (!argc || argc > HV_NPK_LOG_MAX_PARAM) + goto out; + + for (i = 1; i < argc; i++) + if (kstrtouint(argv[i], 10, &args[i]) < 0) + goto out; + + memset(&cmd, 0, sizeof(struct hv_npk_log_param)); + cmd.loglevel = 0xffffU; + cmd.cmd = HV_NPK_LOG_CMD_INVALID; + switch (tolower(argv[0][0])) { + case 'e': /* enable */ + case 'c': /* configure */ + if (!strncasecmp(argv[0], "enable", strlen(argv[0]))) { + cmd.cmd = HV_NPK_LOG_CMD_ENABLE; + } else if (!strncasecmp(argv[0], "configure", strlen(argv[0])) + && argc != 1) { + cmd.cmd = HV_NPK_LOG_CMD_CONF; + } else + break; + + if (argc <= 2) { + cmd.loglevel = argc == 2 ? args[1] : 0xffffU; + if (hnl_conf->master == HV_NPK_LOG_UNKNOWN) + mc2addr(&cmd.mmio_addr, HV_NPK_LOG_DFT_MASTER, + HV_NPK_LOG_DFT_CHANNEL); + } else if (argc > 2 && !mc2addr(&cmd.mmio_addr, + args[1], args[2])) { + cmd.loglevel = argc == 4 ? args[3] : 0xffffU; + } + break; + case 'd': /* disable */ + if (!strncasecmp(argv[0], "disable", strlen(argv[0])) + && argc == 1) + cmd.cmd = HV_NPK_LOG_CMD_DISABLE; + break; + default: + pr_err("Unsupported command : %s\n", argv[0]); + break; + } + + if (cmd.cmd != HV_NPK_LOG_CMD_INVALID) { + ret = hcall_setup_hv_npk_log(virt_to_phys(&cmd)); + ret = (ret < 0 || cmd.res == HV_NPK_LOG_RES_KO) ? -EINVAL : 0; + } + +out: + argv_free(argv); + if (ret < 0) + pr_err("Unsupported configuration : %s\n", val); + return ret; +} + +/* User interface to query the configuration */ +static int hv_npk_log_conf_get(char *buffer, const struct kernel_param *kp) +{ + long ret; + struct hv_npk_log_param query; + + if (!hnl_conf && load_npk_conf() < 0) + return sprintf(buffer, "%s\n", + "Failed to init the configuration."); + + memset(&query, 0, sizeof(struct hv_npk_log_param)); + query.cmd = HV_NPK_LOG_CMD_QUERY; + ret = hcall_setup_hv_npk_log(virt_to_phys(&query)); + if (ret < 0 || query.res == HV_NPK_LOG_RES_KO) + return sprintf(buffer, "%s\n", "Failed to invoke the hcall."); + + if (!addr2mc(query.mmio_addr, &hnl_conf->master, &hnl_conf->channel)) { + hnl_conf->status = query.res == HV_NPK_LOG_RES_ENABLED ? + HV_NPK_LOG_ENABLED : HV_NPK_LOG_DISABLED; + } else { + hnl_conf->status = HV_NPK_LOG_UNKNOWN; + hnl_conf->master = HV_NPK_LOG_UNKNOWN; + hnl_conf->channel = HV_NPK_LOG_UNKNOWN; + } + hnl_conf->loglevel = query.loglevel; + + return scnprintf(buffer, PAGE_SIZE, "Master(SW:%d~%d FW:%d~%d):%d " + "Channel(0~%d):%d Status:%d Log Level: %d\n%s\n", + hnl_conf->sw_start, hnl_conf->sw_end, + hnl_conf->fw_start, hnl_conf->fw_end, + hnl_conf->master, hnl_conf->nchan - 1, + hnl_conf->channel, hnl_conf->status, + hnl_conf->loglevel, HV_NPK_LOG_USAGE); +} + +/* /sys/module/hv_npk_log/parameters/hv_npk_log_conf */ +static struct kernel_param_ops hv_npk_log_conf_param_ops = { + .set = hv_npk_log_conf_set, + .get = hv_npk_log_conf_get, +}; +module_param_cb(hv_npk_log_conf, &hv_npk_log_conf_param_ops, NULL, 0644); + +static struct miscdevice hv_npk_log_misc = { + .minor = MISC_DYNAMIC_MINOR, + .name = "hv_npk_log", +}; + +static int __init hv_npk_log_init(void) +{ + return misc_register(&hv_npk_log_misc); +} + +static void __exit hv_npk_log_exit(void) +{ + kfree(hnl_conf); + + misc_deregister(&hv_npk_log_misc); +} + +module_init(hv_npk_log_init); +module_exit(hv_npk_log_exit); + +MODULE_LICENSE("Dual BSD/GPL"); +MODULE_AUTHOR("Intel Corp., http://www.intel.com"); +MODULE_DESCRIPTION("Driver for the Intel ACRN Hypervisor NPK Log"); +MODULE_VERSION("0.1"); diff --git a/drivers/acrn/hv_npk_log.h b/drivers/acrn/hv_npk_log.h new file mode 100644 index 000000000000..68bd68286522 --- /dev/null +++ b/drivers/acrn/hv_npk_log.h @@ -0,0 +1,109 @@ +/* + * ACRN Hypervisor NPK Log + * + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright (c) 2018 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * 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. + * + * BSD LICENSE + * + * Copyright (C) 2018 Intel Corporation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 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 + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef _HV_NPK_LOG_H_ +#define _HV_NPK_LOG_H_ + +#define NPK_DRV_NAME "intel_th_pci" + +#define NPK_CHAN_SIZE 64 +#define TH_MMIO_CONFIG 0 +#define TH_MMIO_SW 2 + +#define PCI_REG_FW_LBAR 0x70 +#define PCI_REG_FW_UBAR 0x74 + +#define REG_STH_STHCAP0 0x4000 +#define REG_STH_STHCAP1 0x4004 +#define REG_STH_STHCAP2 0x407C + +#define HV_NPK_LOG_ENABLED 1 +#define HV_NPK_LOG_DISABLED 0 +#define HV_NPK_LOG_UNKNOWN (-1) +#define HV_NPK_LOG_MAX_PARAM 4 + +#define HV_NPK_LOG_DFT_MASTER 74 +#define HV_NPK_LOG_DFT_CHANNEL 0 + +enum { + HV_NPK_LOG_CMD_INVALID, + HV_NPK_LOG_CMD_CONF, + HV_NPK_LOG_CMD_ENABLE, + HV_NPK_LOG_CMD_DISABLE, + HV_NPK_LOG_CMD_QUERY, +}; + +enum { + HV_NPK_LOG_RES_INVALID, + HV_NPK_LOG_RES_OK, + HV_NPK_LOG_RES_KO, + HV_NPK_LOG_RES_ENABLED, + HV_NPK_LOG_RES_DISABLED, +}; + +struct hv_npk_log_conf { + int status; + int master; + int channel; + int loglevel; + unsigned int fw_start; + unsigned int fw_end; + unsigned int sw_start; + unsigned int sw_end; + unsigned int nchan; + phys_addr_t stmr_base; + phys_addr_t stmr_end; + phys_addr_t ftmr_base; + phys_addr_t ftmr_end; + phys_addr_t ch_addr; +}; + +#endif /* _HV_NPK_LOG_H_ */ From 2bf9628dcd13d38d5d304b60d612fbb76ea2ca22 Mon Sep 17 00:00:00 2001 From: Sainath Grandhi Date: Thu, 13 Sep 2018 16:14:53 +0800 Subject: [PATCH 0093/1103] Adding kernel parameter for forcing xapic in physical mode This patch does the following Adds a kernel boot parameter xapic_phys to force xAPIC to work in physical mode if the boot kernel parameter says so. This is needed for VMs running in ACRN partition mode Tracked-on: projectacrn/acrn-hypervisor#1163 Signed-off-by: Sainath Grandhi --- arch/x86/kernel/apic/apic_flat_64.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/arch/x86/kernel/apic/apic_flat_64.c b/arch/x86/kernel/apic/apic_flat_64.c index e84c9eb4e5b4..9ba0ac0c8c1f 100644 --- a/arch/x86/kernel/apic/apic_flat_64.c +++ b/arch/x86/kernel/apic/apic_flat_64.c @@ -29,6 +29,15 @@ static struct apic apic_flat; struct apic *apic __ro_after_init = &apic_flat; EXPORT_SYMBOL_GPL(apic); +int xapic_phys = 0; + +static int set_xapic_phys_mode(char *arg) +{ + xapic_phys = 1; + return 0; +} +early_param("xapic_phys", set_xapic_phys_mode); + static int flat_acpi_madt_oem_check(char *oem_id, char *oem_table_id) { return 1; @@ -236,6 +245,9 @@ static void physflat_send_IPI_all(int vector) static int physflat_probe(void) { + if (xapic_phys == 1) + return 1; + if (apic == &apic_physflat || num_possible_cpus() > 8 || jailhouse_paravirt()) return 1; From 4db5bab4b78535fc1fc1ea475121716b719d53ee Mon Sep 17 00:00:00 2001 From: Fei Yang Date: Fri, 21 Sep 2018 09:52:17 +0800 Subject: [PATCH 0094/1103] VHM: Add EXPORT_SYMBOL for VHM API function so that it can be used by other module The following symbols were not exported causing link error when building i915/gvt driver as module. ERROR: "acrn_ioreq_destroy_client" [drivers/gpu/drm/i915/gvt/acrngt.ko] undefined! ERROR: "acrn_ioreq_attach_client" [drivers/gpu/drm/i915/gvt/acrngt.ko] undefined! ERROR: "vhm_inject_msi" [drivers/gpu/drm/i915/gvt/acrngt.ko] undefined! ERROR: "add_memory_region" [drivers/gpu/drm/i915/gvt/acrngt.ko] undefined! ERROR: "del_memory_region" [drivers/gpu/drm/i915/gvt/acrngt.ko] undefined! ERROR: "acrn_ioreq_complete_request" [drivers/gpu/drm/i915/gvt/acrngt.ko] undefined! ERROR: "acrn_ioreq_intercept_bdf" [drivers/gpu/drm/i915/gvt/acrngt.ko] undefined! ERROR: "find_get_vm" [drivers/gpu/drm/i915/gvt/acrngt.ko] undefined! ERROR: "vhm_get_vm_info" [drivers/gpu/drm/i915/gvt/acrngt.ko] undefined! ERROR: "acrn_ioreq_del_iorange" [drivers/gpu/drm/i915/gvt/acrngt.ko] undefined! ERROR: "acrn_ioreq_get_reqbuf" [drivers/gpu/drm/i915/gvt/acrngt.ko] undefined! ERROR: "write_protect_page" [drivers/gpu/drm/i915/gvt/acrngt.ko] undefined! ERROR: "acrn_ioreq_add_iorange" [drivers/gpu/drm/i915/gvt/acrngt.ko] undefined! ERROR: "put_vm" [drivers/gpu/drm/i915/gvt/acrngt.ko] undefined! ERROR: "acrn_ioreq_create_client" [drivers/gpu/drm/i915/gvt/acrngt.ko] undefined! ERROR: "vhm_vm_gpa2hpa" [drivers/gpu/drm/i915/gvt/acrngt.ko] undefined! make[1]: *** [__modpost] Error 1 make: *** [modules] Error 2 Singed-off-by: Fei Yang --- drivers/vhm/vhm_ioreq.c | 8 ++++++++ drivers/vhm/vhm_mm.c | 3 +++ drivers/vhm/vhm_vm_mngt.c | 5 +++++ 3 files changed, 16 insertions(+) diff --git a/drivers/vhm/vhm_ioreq.c b/drivers/vhm/vhm_ioreq.c index 6bf07e812d03..ff19cc3b76ba 100644 --- a/drivers/vhm/vhm_ioreq.c +++ b/drivers/vhm/vhm_ioreq.c @@ -220,6 +220,7 @@ int acrn_ioreq_create_client(unsigned long vmid, ioreq_handler_t handler, return client_id; } +EXPORT_SYMBOL_GPL(acrn_ioreq_create_client); int acrn_ioreq_create_fallback_client(unsigned long vmid, char *name) { @@ -317,6 +318,7 @@ void acrn_ioreq_destroy_client(int client_id) put_vm(vm); } +EXPORT_SYMBOL_GPL(acrn_ioreq_destroy_client); static void __attribute__((unused)) dump_iorange(struct ioreq_client *client) { @@ -376,6 +378,7 @@ int acrn_ioreq_add_iorange(int client_id, uint32_t type, return 0; } +EXPORT_SYMBOL_GPL(acrn_ioreq_add_iorange); int acrn_ioreq_del_iorange(int client_id, uint32_t type, long start, long end) @@ -424,6 +427,7 @@ int acrn_ioreq_del_iorange(int client_id, uint32_t type, return 0; } +EXPORT_SYMBOL_GPL(acrn_ioreq_del_iorange); static inline bool is_destroying(struct ioreq_client *client) { @@ -469,6 +473,7 @@ struct vhm_request *acrn_ioreq_get_reqbuf(int client_id) put_vm(vm); return (struct vhm_request *)vm->req_buf; } +EXPORT_SYMBOL_GPL(acrn_ioreq_get_reqbuf); static int ioreq_client_thread(void *data) { @@ -563,6 +568,7 @@ int acrn_ioreq_attach_client(int client_id, bool check_kthread_stop) return 0; } +EXPORT_SYMBOL_GPL(acrn_ioreq_attach_client); void acrn_ioreq_intercept_bdf(int client_id, int bus, int dev, int func) { @@ -582,6 +588,7 @@ void acrn_ioreq_intercept_bdf(int client_id, int bus, int dev, int func) client->pci_dev = dev; client->pci_func = func; } +EXPORT_SYMBOL_GPL(acrn_ioreq_intercept_bdf); void acrn_ioreq_unintercept_bdf(int client_id) { @@ -850,6 +857,7 @@ int acrn_ioreq_complete_request(int client_id, uint64_t vcpu) return 0; } +EXPORT_SYMBOL_GPL(acrn_ioreq_complete_request); unsigned int vhm_dev_poll(struct file *filep, poll_table *wait) { diff --git a/drivers/vhm/vhm_mm.c b/drivers/vhm/vhm_mm.c index 6bea6688ddc0..0b9168f5672b 100644 --- a/drivers/vhm/vhm_mm.c +++ b/drivers/vhm/vhm_mm.c @@ -133,6 +133,7 @@ int add_memory_region(unsigned long vmid, unsigned long gpa, (mem_access_right & MEM_ACCESS_RIGHT_MASK)); return set_memory_region(vmid, ®ion); } +EXPORT_SYMBOL_GPL(add_memory_region); int del_memory_region(unsigned long vmid, unsigned long gpa, unsigned long size) @@ -147,6 +148,7 @@ int del_memory_region(unsigned long vmid, unsigned long gpa, return set_memory_region(vmid, ®ion); } +EXPORT_SYMBOL_GPL(del_memory_region); int set_memory_regions(struct set_regions *regions) { @@ -184,6 +186,7 @@ int write_protect_page(unsigned long vmid, return 0; } +EXPORT_SYMBOL_GPL(write_protect_page); int map_guest_memseg(struct vhm_vm *vm, struct vm_memmap *memmap) { diff --git a/drivers/vhm/vhm_vm_mngt.c b/drivers/vhm/vhm_vm_mngt.c index 8f1a00777dd4..c186b97a3c09 100644 --- a/drivers/vhm/vhm_vm_mngt.c +++ b/drivers/vhm/vhm_vm_mngt.c @@ -80,6 +80,7 @@ struct vhm_vm *find_get_vm(unsigned long vmid) mutex_unlock(&vhm_vm_list_lock); return NULL; } +EXPORT_SYMBOL_GPL(find_get_vm); void put_vm(struct vhm_vm *vm) { @@ -94,6 +95,7 @@ void put_vm(struct vhm_vm *vm) } mutex_unlock(&vhm_vm_list_lock); } +EXPORT_SYMBOL_GPL(put_vm); int vhm_get_vm_info(unsigned long vmid, struct vm_info *info) { @@ -111,6 +113,7 @@ int vhm_get_vm_info(unsigned long vmid, struct vm_info *info) put_vm(vm); return 0; } +EXPORT_SYMBOL_GPL(vhm_get_vm_info); int vhm_inject_msi(unsigned long vmid, unsigned long msi_addr, unsigned long msi_data) @@ -129,6 +132,7 @@ int vhm_inject_msi(unsigned long vmid, unsigned long msi_addr, } return 0; } +EXPORT_SYMBOL_GPL(vhm_inject_msi); unsigned long vhm_vm_gpa2hpa(unsigned long vmid, unsigned long gpa) { @@ -145,6 +149,7 @@ unsigned long vhm_vm_gpa2hpa(unsigned long vmid, unsigned long gpa) mb(); return gpa2hpa.hpa; } +EXPORT_SYMBOL_GPL(vhm_vm_gpa2hpa); void vm_list_add(struct list_head *list) { From 71e32a9b776c905c0ddfbc2ef7d3d46953b069d0 Mon Sep 17 00:00:00 2001 From: Jason Chen CJ Date: Sat, 8 Sep 2018 15:37:38 +0800 Subject: [PATCH 0095/1103] vhm: deinit trusty after hcall_destroy_vm after deinit_trusty, the released cma memory could be used by other drivers. while EPT of this memory area need be remap back to SOS by hcall_destroy_vm. with current sequence, an access to EPT unmapped area will cause MMIO access error. for trusty creation, the sequence is like: hcall_create_vm -> init_trusty (allocate CMA for trusty memory) ....> UOS hypercall to create trusty world (EPT remapping etc) for trusty destroy, the sequence changed like below: hcall_destroy_vm (include trusty destroy - EPT remapping back etc) -> deinit_trusty (free CMA for trusty memory) this sequence looks a little confuse, but as hcall_destroy_vm combined trusty world destroy operation, we need this operation be done before deinit_trusty. Tracked-On: https://github.com/projectacrn/acrn-hypervisor/issues/1200 Signed-off-by: Jason Chen CJ Acked-by: Anthony Xu --- drivers/char/vhm/vhm_dev.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/char/vhm/vhm_dev.c b/drivers/char/vhm/vhm_dev.c index 6e351c387928..d57dc3972bc5 100644 --- a/drivers/char/vhm/vhm_dev.c +++ b/drivers/char/vhm/vhm_dev.c @@ -276,13 +276,13 @@ static long vhm_dev_ioctl(struct file *filep, } case IC_DESTROY_VM: { - if (vm->trusty_host_gpa) - deinit_trusty(vm); ret = hcall_destroy_vm(vm->vmid); if (ret < 0) { pr_err("failed to destroy VM %ld\n", vm->vmid); return -EFAULT; } + if (vm->trusty_host_gpa) + deinit_trusty(vm); vm->vmid = ACRN_INVALID_VMID; break; } From 5ae5ac17f81c332dbd9a77cd6525e062699a988d Mon Sep 17 00:00:00 2001 From: Minggui Cao Date: Wed, 8 Aug 2018 15:42:00 +0800 Subject: [PATCH 0096/1103] VHM: add ioctl/hypercall for UOS intr data monitor DM can use this ioctl/hypercall to get the UOS pass-through devices' interrupt count data, to monitor its status. It is used to enhance the feature "interrupt storm mitigation"; DM can monitor UOS pass-thru devices' interrupt data and give a response if one "interrupt storm" happens. V2: Fix the building warning. Tracked-on: https://github.com/projectacrn/acrn-hypervisor/issues/866 Signed-off-by: Minggui Cao Signed-off-by: Wei Liu Reviewed-by: Zhao Yakui --- drivers/char/vhm/vhm_dev.c | 17 +++++++++++++++++ drivers/vhm/vhm_hypercall.c | 5 +++++ include/linux/vhm/acrn_hv_defs.h | 1 + include/linux/vhm/vhm_hypercall.h | 1 + include/linux/vhm/vhm_ioctl_defs.h | 1 + 5 files changed, 25 insertions(+) diff --git a/drivers/char/vhm/vhm_dev.c b/drivers/char/vhm/vhm_dev.c index d57dc3972bc5..0d25f732da5b 100644 --- a/drivers/char/vhm/vhm_dev.c +++ b/drivers/char/vhm/vhm_dev.c @@ -630,6 +630,23 @@ static long vhm_dev_ioctl(struct file *filep, break; } + case IC_VM_INTR_MONITOR: { + struct page *page; + + ret = get_user_pages_fast(ioctl_param, 1, 1, &page); + if (unlikely(ret != 1) || (page == NULL)) { + pr_err("vhm-dev: failed to pin intr hdr buffer!\n"); + return -ENOMEM; + } + + ret = hcall_vm_intr_monitor(vm->vmid, page_to_phys(page)); + if (ret < 0) { + pr_err("vhm-dev: monitor intr data err=%ld\n", ret); + return -EFAULT; + } + break; + } + default: pr_warn("Unknown IOCTL 0x%x\n", ioctl_num); ret = 0; diff --git a/drivers/vhm/vhm_hypercall.c b/drivers/vhm/vhm_hypercall.c index d994835dd924..666f9aeb87ad 100644 --- a/drivers/vhm/vhm_hypercall.c +++ b/drivers/vhm/vhm_hypercall.c @@ -177,3 +177,8 @@ inline long hcall_vm_gpa2hpa(unsigned long vmid, unsigned long addr) { return acrn_hypercall2(HC_VM_GPA2HPA, vmid, addr); } + +inline long hcall_vm_intr_monitor(unsigned long vmid, unsigned long addr) +{ + return acrn_hypercall2(HC_VM_INTR_MONITOR, vmid, addr); +} diff --git a/include/linux/vhm/acrn_hv_defs.h b/include/linux/vhm/acrn_hv_defs.h index 24ca7be2c2b9..2cd6172e8e1d 100644 --- a/include/linux/vhm/acrn_hv_defs.h +++ b/include/linux/vhm/acrn_hv_defs.h @@ -85,6 +85,7 @@ #define HC_DEASSERT_IRQLINE _HC_ID(HC_ID, HC_ID_IRQ_BASE + 0x01) #define HC_PULSE_IRQLINE _HC_ID(HC_ID, HC_ID_IRQ_BASE + 0x02) #define HC_INJECT_MSI _HC_ID(HC_ID, HC_ID_IRQ_BASE + 0x03) +#define HC_VM_INTR_MONITOR _HC_ID(HC_ID, HC_ID_IRQ_BASE + 0x04) /* DM ioreq management */ #define HC_ID_IOREQ_BASE 0x30UL diff --git a/include/linux/vhm/vhm_hypercall.h b/include/linux/vhm/vhm_hypercall.h index 703b35bec053..062196ab1194 100644 --- a/include/linux/vhm/vhm_hypercall.h +++ b/include/linux/vhm/vhm_hypercall.h @@ -167,5 +167,6 @@ inline long hcall_reset_ptdev_intr_info(unsigned long vmid, unsigned long pt_irq); inline long hcall_remap_pci_msix(unsigned long vmid, unsigned long msi); inline long hcall_vm_gpa2hpa(unsigned long vmid, unsigned long addr); +inline long hcall_vm_intr_monitor(unsigned long vmid, unsigned long addr); #endif /* VHM_HYPERCALL_H */ diff --git a/include/linux/vhm/vhm_ioctl_defs.h b/include/linux/vhm/vhm_ioctl_defs.h index 6c09157a9bef..063ebd5c7a93 100644 --- a/include/linux/vhm/vhm_ioctl_defs.h +++ b/include/linux/vhm/vhm_ioctl_defs.h @@ -80,6 +80,7 @@ #define IC_DEASSERT_IRQLINE _IC_ID(IC_ID, IC_ID_IRQ_BASE + 0x01) #define IC_PULSE_IRQLINE _IC_ID(IC_ID, IC_ID_IRQ_BASE + 0x02) #define IC_INJECT_MSI _IC_ID(IC_ID, IC_ID_IRQ_BASE + 0x03) +#define IC_VM_INTR_MONITOR _IC_ID(IC_ID, IC_ID_IRQ_BASE + 0x04) /* DM ioreq management */ #define IC_ID_IOREQ_BASE 0x30UL From b8268d04cfe009c40d4f1726fa028946421e6c39 Mon Sep 17 00:00:00 2001 From: Wei Liu Date: Thu, 20 Sep 2018 10:16:56 +0800 Subject: [PATCH 0097/1103] vhm: enable -Werror while compiling vhm/vbs/hyper-dmabuf Sometimes warnings are caused by the potential errors, in order to add the strict check, the warnings will be treated as error and should be fixed. Tracked-On: https://github.com/projectacrn/acrn-hypervisor/issues/1300 Signed-off-by: Wei Liu Reviewed-by: Zhao Yakui --- drivers/acrn/Makefile | 1 + drivers/char/vhm/Makefile | 1 + drivers/vbs/Makefile | 1 + drivers/vhm/Makefile | 1 + 4 files changed, 4 insertions(+) diff --git a/drivers/acrn/Makefile b/drivers/acrn/Makefile index d0e14f43bcba..dad6a9e8c42c 100644 --- a/drivers/acrn/Makefile +++ b/drivers/acrn/Makefile @@ -1,3 +1,4 @@ +subdir-ccflags-$(CONFIG_ACRN) := -Werror obj-$(CONFIG_ACRN_SHARED_BUFFER) += sbuf.o obj-$(CONFIG_ACRN_TRACE) += acrn_trace.o obj-$(CONFIG_ACRN_HVLOG) += acrn_hvlog.o diff --git a/drivers/char/vhm/Makefile b/drivers/char/vhm/Makefile index cb801c70a37e..5ee68c5f7278 100644 --- a/drivers/char/vhm/Makefile +++ b/drivers/char/vhm/Makefile @@ -1 +1,2 @@ +subdir-ccflags-$(CONFIG_ACRN_VHM) := -Werror obj-y += vhm_dev.o diff --git a/drivers/vbs/Makefile b/drivers/vbs/Makefile index 85e1cc252197..a6734db96eee 100644 --- a/drivers/vbs/Makefile +++ b/drivers/vbs/Makefile @@ -1,3 +1,4 @@ +subdir-ccflags-$(CONFIG_VBS) := -Werror ccflags-$(CONFIG_VBS_DEBUG) := -DDEBUG obj-$(CONFIG_VBS) += vbs.o diff --git a/drivers/vhm/Makefile b/drivers/vhm/Makefile index 23f17ae24f78..c3bce347f786 100644 --- a/drivers/vhm/Makefile +++ b/drivers/vhm/Makefile @@ -1 +1,2 @@ +subdir-ccflags-$(CONFIG_ACRN_VHM) := -Werror obj-y += vhm_mm.o vhm_hugetlb.o vhm_ioreq.o vhm_vm_mngt.o vhm_msi.o vhm_hypercall.o From 6ebc61bbbebffc8edcbbd6510bb29e5acad0f39e Mon Sep 17 00:00:00 2001 From: Shuo Liu Date: Sat, 29 Sep 2018 11:00:46 +0800 Subject: [PATCH 0098/1103] vhm: change trace_printk of vhm_dev_ioctl to pr_debug trace_printk will write into the ring buffer by default. Actually, we don't need this trace entry as there are many vhm ioctls happen at runtime which will flood the ring buffer. So change it to dynamic printk. Tracked-On: projectacrn/acrn-hypervisor#1328 Signed-off-by: Shuo Liu Acked-by: Anthony Xu --- drivers/char/vhm/vhm_dev.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/char/vhm/vhm_dev.c b/drivers/char/vhm/vhm_dev.c index 0d25f732da5b..987c7ceea261 100644 --- a/drivers/char/vhm/vhm_dev.c +++ b/drivers/char/vhm/vhm_dev.c @@ -160,7 +160,7 @@ static long vhm_dev_ioctl(struct file *filep, struct ic_ptdev_irq ic_pt_irq; struct hc_ptdev_irq hc_pt_irq; - trace_printk("[%s] ioctl_num=0x%x\n", __func__, ioctl_num); + pr_debug("[%s] ioctl_num=0x%x\n", __func__, ioctl_num); if (ioctl_num == IC_GET_API_VERSION) { struct api_version api_version; From 156cabfbe1c86eec13effedc053296da7893e48f Mon Sep 17 00:00:00 2001 From: Shuo Liu Date: Wed, 11 Jul 2018 14:37:53 +0800 Subject: [PATCH 0099/1103] vhm: add ioeventfd support for ACRN hypervisor service module ioeventfd which is based on eventfd, intends to glue vhm module and other modules who are interested in guest IOs. Each ioeventfd registered by userspace can map a PIO/MMIO range of the guest to eventfd, and response to signal the eventfd when get the in-range IO write from guest. Then the other side of eventfd can be notified to process the IO request. Now we only use the ioeventfd to listen virtqueue's kick register, there are some limitations: 1) Length support can only be 1, 2, 4 or 8 2) Only support write operation, read will get 0 3) Same address, shorter length writing can be handled with the integral data matching Tracked-On: projectacrn/acrn-hypervisor#1329 Signed-off-by: Shuo Liu Reviewed-by: Yu Wang Reviewed-by: Zhao Yakui --- drivers/char/vhm/vhm_dev.c | 13 + drivers/vhm/Makefile | 2 +- drivers/vhm/vhm_ioeventfd.c | 473 +++++++++++++++++++++++++++++ include/linux/vhm/vhm_eventfd.h | 10 + include/linux/vhm/vhm_ioctl_defs.h | 16 + 5 files changed, 513 insertions(+), 1 deletion(-) create mode 100644 drivers/vhm/vhm_ioeventfd.c create mode 100644 include/linux/vhm/vhm_eventfd.h diff --git a/drivers/char/vhm/vhm_dev.c b/drivers/char/vhm/vhm_dev.c index 987c7ceea261..672b58a29a1f 100644 --- a/drivers/char/vhm/vhm_dev.c +++ b/drivers/char/vhm/vhm_dev.c @@ -83,6 +83,7 @@ #include #include #include +#include #include @@ -236,6 +237,8 @@ static long vhm_dev_ioctl(struct file *filep, goto ioreq_buf_fail; } + acrn_ioeventfd_init(vm->vmid); + pr_info("vhm: VM %d created\n", created_vm.vmid); break; ioreq_buf_fail: @@ -276,6 +279,7 @@ static long vhm_dev_ioctl(struct file *filep, } case IC_DESTROY_VM: { + acrn_ioeventfd_deinit(vm->vmid); ret = hcall_destroy_vm(vm->vmid); if (ret < 0) { pr_err("failed to destroy VM %ld\n", vm->vmid); @@ -647,6 +651,15 @@ static long vhm_dev_ioctl(struct file *filep, break; } + case IC_EVENT_IOEVENTFD: { + struct acrn_ioeventfd args; + + if (copy_from_user(&args, (void *)ioctl_param, sizeof(args))) + return -EFAULT; + ret = acrn_ioeventfd(vm->vmid, &args); + break; + } + default: pr_warn("Unknown IOCTL 0x%x\n", ioctl_num); ret = 0; diff --git a/drivers/vhm/Makefile b/drivers/vhm/Makefile index c3bce347f786..193f7692c9e3 100644 --- a/drivers/vhm/Makefile +++ b/drivers/vhm/Makefile @@ -1,2 +1,2 @@ subdir-ccflags-$(CONFIG_ACRN_VHM) := -Werror -obj-y += vhm_mm.o vhm_hugetlb.o vhm_ioreq.o vhm_vm_mngt.o vhm_msi.o vhm_hypercall.o +obj-y += vhm_mm.o vhm_hugetlb.o vhm_ioreq.o vhm_vm_mngt.o vhm_msi.o vhm_hypercall.o vhm_ioeventfd.o diff --git a/drivers/vhm/vhm_ioeventfd.c b/drivers/vhm/vhm_ioeventfd.c new file mode 100644 index 000000000000..d5efb1a88dfa --- /dev/null +++ b/drivers/vhm/vhm_ioeventfd.c @@ -0,0 +1,473 @@ +/* + * ioeventfd for ACRN hypervisor + * + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright (c) 2018 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * 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. + * + * BSD LICENSE + * + * Copyright (C) 2018 Intel Corporation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 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 + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * + */ +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +static LIST_HEAD(vhm_ioeventfd_clients); +static DEFINE_MUTEX(vhm_ioeventfds_mutex); + +/* use internally to record properties of each ioeventfd */ +struct acrn_vhm_ioeventfd { + /* list to link all ioventfd together */ + struct list_head list; + /* eventfd of this ioeventfd */ + struct eventfd_ctx *eventfd; + /* start address for IO range*/ + u64 addr; + /* match data */ + u64 data; + /* length for IO range */ + int length; + /* IO range type, can be REQ_PORTIO and REQ_MMIO */ + int type; + /* ignore match data if true */ + bool wildcard; +}; + +/* instance to bind ioeventfds of each VM */ +struct vhm_ioeventfd_info { + struct list_head list; + int refcnt; + /* vmid of VM */ + uint16_t vmid; + /* vhm ioreq client for this instance */ + int vhm_client_id; + /* vcpu number of this VM */ + int vcpu_num; + /* ioreq shared buffer of this VM */ + struct vhm_request *req_buf; + + /* ioeventfds in this instance */ + struct list_head ioeventfds; + struct mutex ioeventfds_lock; +}; + +static struct vhm_ioeventfd_info *get_ioeventfd_info_by_client( + int client_id) +{ + struct vhm_ioeventfd_info *info = NULL; + + mutex_lock(&vhm_ioeventfds_mutex); + list_for_each_entry(info, &vhm_ioeventfd_clients, list) { + if (info->vhm_client_id == client_id) { + info->refcnt++; + mutex_unlock(&vhm_ioeventfds_mutex); + return info; + } + } + mutex_unlock(&vhm_ioeventfds_mutex); + return NULL; +} + +static struct vhm_ioeventfd_info *get_ioeventfd_info_by_vm(uint16_t vmid) +{ + struct vhm_ioeventfd_info *info = NULL; + + mutex_lock(&vhm_ioeventfds_mutex); + list_for_each_entry(info, &vhm_ioeventfd_clients, list) { + if (info->vmid == vmid) { + info->refcnt++; + mutex_unlock(&vhm_ioeventfds_mutex); + return info; + } + } + mutex_unlock(&vhm_ioeventfds_mutex); + return NULL; +} + +static void put_ioeventfd_info(struct vhm_ioeventfd_info *info) +{ + mutex_lock(&vhm_ioeventfds_mutex); + info->refcnt--; + if (info->refcnt == 0) { + list_del(&info->list); + mutex_unlock(&vhm_ioeventfds_mutex); + acrn_ioreq_destroy_client(info->vhm_client_id); + kfree(info); + return; + } + mutex_unlock(&vhm_ioeventfds_mutex); +} + +/* assumes info->ioeventfds_lock held */ +static void vhm_ioeventfd_shutdown(struct acrn_vhm_ioeventfd *p) +{ + eventfd_ctx_put(p->eventfd); + list_del(&p->list); + kfree(p); +} + +static inline int ioreq_type_from_flags(int flags) +{ + return flags & ACRN_IOEVENTFD_FLAG_PIO ? + REQ_PORTIO : REQ_MMIO; +} + +/* assumes info->ioeventfds_lock held */ +static bool vhm_ioeventfd_is_duplicated(struct vhm_ioeventfd_info *info, + struct acrn_vhm_ioeventfd *ioeventfd) +{ + struct acrn_vhm_ioeventfd *p; + + /* + * Treat same addr/type/data with different length combination + * as the same one. + * Register PIO[0x100~0x107] with data 0x10 as ioeventfd A, later + * PIO[0x100~0x103] with data 0x10 will be failed to register. + */ + list_for_each_entry(p, &info->ioeventfds, list) + if (p->addr == ioeventfd->addr && + p->type == ioeventfd->type && + (p->wildcard || ioeventfd->wildcard || + p->data == ioeventfd->data)) + return true; + + return false; +} + +static int acrn_assign_ioeventfd(struct vhm_ioeventfd_info *info, + struct acrn_ioeventfd *args) +{ + struct eventfd_ctx *eventfd; + struct acrn_vhm_ioeventfd *p; + int ret = -ENOENT; + + /* check for range overflow */ + if (args->addr + args->len < args->addr) + return -EINVAL; + + /* Only support 1,2,4,8 width registers */ + if (!(args->len == 1 || args->len == 2 || + args->len == 4 || args->len == 8)) + return -EINVAL; + + eventfd = eventfd_ctx_fdget(args->fd); + if (IS_ERR(eventfd)) + return PTR_ERR(eventfd); + + p = kzalloc(sizeof(*p), GFP_KERNEL); + if (!p) { + ret = -ENOMEM; + goto fail; + } + + INIT_LIST_HEAD(&p->list); + p->addr = args->addr; + p->length = args->len; + p->eventfd = eventfd; + p->type = ioreq_type_from_flags(args->flags); + + /* If datamatch enabled, we compare the data + * otherwise this is a wildcard + */ + if (args->flags & ACRN_IOEVENTFD_FLAG_DATAMATCH) + p->data = args->data; + else + p->wildcard = true; + + mutex_lock(&info->ioeventfds_lock); + + /* Verify that there isn't a match already */ + if (vhm_ioeventfd_is_duplicated(info, p)) { + ret = -EEXIST; + goto unlock_fail; + } + + /* register the IO range into vhm */ + ret = acrn_ioreq_add_iorange(info->vhm_client_id, p->type, + p->addr, p->addr + p->length - 1); + if (ret < 0) + goto unlock_fail; + + list_add_tail(&p->list, &info->ioeventfds); + mutex_unlock(&info->ioeventfds_lock); + + return 0; + +unlock_fail: + mutex_unlock(&info->ioeventfds_lock); +fail: + kfree(p); + eventfd_ctx_put(eventfd); + return ret; +} + +static int acrn_deassign_ioeventfd(struct vhm_ioeventfd_info *info, + struct acrn_ioeventfd *args) +{ + struct acrn_vhm_ioeventfd *p, *tmp; + struct eventfd_ctx *eventfd; + int ret = 0; + + eventfd = eventfd_ctx_fdget(args->fd); + if (IS_ERR(eventfd)) + return PTR_ERR(eventfd); + + mutex_lock(&info->ioeventfds_lock); + + list_for_each_entry_safe(p, tmp, &info->ioeventfds, list) { + if (p->eventfd != eventfd) + continue; + + ret = acrn_ioreq_del_iorange(info->vhm_client_id, p->type, + p->addr, p->addr + p->length - 1); + if (ret) + break; + vhm_ioeventfd_shutdown(p); + break; + } + + mutex_unlock(&info->ioeventfds_lock); + + eventfd_ctx_put(eventfd); + + return ret; +} + +int acrn_ioeventfd(uint16_t vmid, struct acrn_ioeventfd *args) +{ + struct vhm_ioeventfd_info *info = NULL; + int ret; + + info = get_ioeventfd_info_by_vm(vmid); + if (!info) + return -ENOENT; + + if (args->flags & ACRN_IOEVENTFD_FLAG_DEASSIGN) + ret = acrn_deassign_ioeventfd(info, args); + else + ret = acrn_assign_ioeventfd(info, args); + + put_ioeventfd_info(info); + return ret; +} + +static struct acrn_vhm_ioeventfd *vhm_ioeventfd_match( + struct vhm_ioeventfd_info *info, + u64 addr, u64 data, int length, int type) +{ + struct acrn_vhm_ioeventfd *p = NULL; + + /* + * Same addr/type/data will be treated as hit, otherwise ignore. + * Register PIO[0x100~0x107] with data 0x10 as ioeventfd A, later + * request PIO[0x100~0x103] with data 0x10 will hit A. + */ + list_for_each_entry(p, &info->ioeventfds, list) { + if (p->type == type && + p->addr == addr && + (p->wildcard || p->data == data)) + return p; + } + + return NULL; +} + +static int acrn_ioeventfd_dispatch_ioreq(int client_id, + unsigned long *ioreqs_map) +{ + struct vhm_request *req; + struct acrn_vhm_ioeventfd *p; + struct vhm_ioeventfd_info *info; + u64 addr; + u64 val; + int size; + int vcpu; + + info = get_ioeventfd_info_by_client(client_id); + if (!info) + return -EINVAL; + + /* get req buf */ + if (!info->req_buf) { + info->req_buf = acrn_ioreq_get_reqbuf(info->vhm_client_id); + if (!info->req_buf) { + pr_err("Failed to get req_buf for client %d\n", + info->vhm_client_id); + put_ioeventfd_info(info); + return -EINVAL; + } + } + + while (1) { + vcpu = find_first_bit(ioreqs_map, info->vcpu_num); + if (vcpu == info->vcpu_num) + break; + req = &info->req_buf[vcpu]; + if (atomic_read(&req->processed) == REQ_STATE_PROCESSING && + req->client == client_id) { + if (req->type == REQ_MMIO) { + if (req->reqs.mmio_request.direction == + REQUEST_READ) { + /* reading does nothing and return 0 */ + req->reqs.mmio_request.value = 0; + goto next_ioreq; + } + addr = req->reqs.mmio_request.address; + size = req->reqs.mmio_request.size; + val = req->reqs.mmio_request.value; + } else { + if (req->reqs.pio_request.direction == + REQUEST_READ) { + /* reading does nothing and return 0 */ + req->reqs.pio_request.value = 0; + goto next_ioreq; + } + addr = req->reqs.pio_request.address; + size = req->reqs.pio_request.size; + val = req->reqs.pio_request.value; + } + + mutex_lock(&info->ioeventfds_lock); + p = vhm_ioeventfd_match(info, addr, val, size, + req->type); + if (p) + eventfd_signal(p->eventfd, 1); + mutex_unlock(&info->ioeventfds_lock); + +next_ioreq: + atomic_set(&req->processed, REQ_STATE_COMPLETE); + acrn_ioreq_complete_request(client_id, vcpu); + } + } + + put_ioeventfd_info(info); + return 0; +} + +int acrn_ioeventfd_init(uint16_t vmid) +{ + int ret = 0; + char name[16]; + struct vm_info vm_info; + struct vhm_ioeventfd_info *info; + + info = get_ioeventfd_info_by_vm(vmid); + if (info) { + put_ioeventfd_info(info); + return -EEXIST; + } + + info = kzalloc(sizeof(struct vhm_ioeventfd_info), GFP_KERNEL); + if (!info) + return -ENOMEM; + snprintf(name, sizeof(name), "ioeventfd-%hu", vmid); + info->vhm_client_id = acrn_ioreq_create_client(vmid, + acrn_ioeventfd_dispatch_ioreq, name); + if (info->vhm_client_id < 0) { + pr_err("Failed to create ioeventfd client for ioreq!\n"); + ret = -EINVAL; + goto fail; + } + + ret = vhm_get_vm_info(vmid, &vm_info); + if (ret < 0) { + pr_err("Failed to get vm info for vmid %d\n", vmid); + goto client_fail; + } + + info->vcpu_num = vm_info.max_vcpu; + + ret = acrn_ioreq_attach_client(info->vhm_client_id, 0); + if (ret < 0) { + pr_err("Failed to attach vhm client %d!\n", + info->vhm_client_id); + goto client_fail; + } + mutex_init(&info->ioeventfds_lock); + info->vmid = vmid; + info->refcnt = 1; + INIT_LIST_HEAD(&info->ioeventfds); + + mutex_lock(&vhm_ioeventfds_mutex); + list_add(&info->list, &vhm_ioeventfd_clients); + mutex_unlock(&vhm_ioeventfds_mutex); + + pr_info("ACRN vhm ioeventfd init done!\n"); + return 0; +client_fail: + acrn_ioreq_destroy_client(info->vhm_client_id); +fail: + kfree(info); + return ret; +} + +void acrn_ioeventfd_deinit(uint16_t vmid) +{ + struct acrn_vhm_ioeventfd *p, *tmp; + struct vhm_ioeventfd_info *info = NULL; + + info = get_ioeventfd_info_by_vm(vmid); + if (!info) + return; + + put_ioeventfd_info(info); + + mutex_lock(&info->ioeventfds_lock); + list_for_each_entry_safe(p, tmp, &info->ioeventfds, list) + vhm_ioeventfd_shutdown(p); + mutex_unlock(&info->ioeventfds_lock); + + /* put one more as we count it in finding */ + put_ioeventfd_info(info); +} diff --git a/include/linux/vhm/vhm_eventfd.h b/include/linux/vhm/vhm_eventfd.h new file mode 100644 index 000000000000..3c73194cd1a1 --- /dev/null +++ b/include/linux/vhm/vhm_eventfd.h @@ -0,0 +1,10 @@ +#ifndef __ACRN_VHM_EVENTFD_H__ +#define __ACRN_VHM_EVENTFD_H__ + +/* ioeventfd APIs */ +struct acrn_ioeventfd; +int acrn_ioeventfd_init(int vmid); +int acrn_ioeventfd(int vmid, struct acrn_ioeventfd *args); +void acrn_ioeventfd_deinit(int vmid); + +#endif diff --git a/include/linux/vhm/vhm_ioctl_defs.h b/include/linux/vhm/vhm_ioctl_defs.h index 063ebd5c7a93..4ab6b02aced8 100644 --- a/include/linux/vhm/vhm_ioctl_defs.h +++ b/include/linux/vhm/vhm_ioctl_defs.h @@ -110,6 +110,10 @@ #define IC_PM_GET_CPU_STATE _IC_ID(IC_ID, IC_ID_PM_BASE + 0x00) #define IC_PM_SET_SSTATE_DATA _IC_ID(IC_ID, IC_ID_PM_BASE + 0x01) +/* VHM eventfd */ +#define IC_ID_EVENT_BASE 0x70UL +#define IC_EVENT_IOEVENTFD _IC_ID(IC_ID, IC_ID_EVENT_BASE + 0x00) + /** * struct vm_memseg - memory segment info for guest * @@ -216,4 +220,16 @@ struct api_version { uint32_t minor_version; }; +#define ACRN_IOEVENTFD_FLAG_PIO 0x01 +#define ACRN_IOEVENTFD_FLAG_DATAMATCH 0x02 +#define ACRN_IOEVENTFD_FLAG_DEASSIGN 0x04 +struct acrn_ioeventfd { + int32_t fd; + uint32_t flags; + uint64_t addr; + uint32_t len; + uint32_t reserved; + uint64_t data; +}; + #endif /* VHM_IOCTL_DEFS_H */ From a684cf6fc5a9c6feb2c9c04e3b29431a045f594c Mon Sep 17 00:00:00 2001 From: Shuo Liu Date: Thu, 12 Jul 2018 15:40:52 +0800 Subject: [PATCH 0100/1103] vhm: add irqfd support for ACRN hypervisor service module irqfd which is based on eventfd, provides a pipe for injecting guest interrupt through a file description writing operation. Each irqfd registered by userspace can map a interrupt of the guest to eventfd, and a writing operation on one side of the eventfd will trigger the interrupt injection on vhm side. Tracked-On: projectacrn/acrn-hypervisor#1329 Signed-off-by: Shuo Liu Reviewed-by: Zhao Yakui Reviewed-by: Yu Wang --- drivers/char/vhm/vhm_dev.c | 11 + drivers/vhm/Makefile | 2 +- drivers/vhm/vhm_irqfd.c | 381 +++++++++++++++++++++++++++++ include/linux/vhm/vhm_eventfd.h | 6 + include/linux/vhm/vhm_ioctl_defs.h | 8 + 5 files changed, 407 insertions(+), 1 deletion(-) create mode 100644 drivers/vhm/vhm_irqfd.c diff --git a/drivers/char/vhm/vhm_dev.c b/drivers/char/vhm/vhm_dev.c index 672b58a29a1f..4d76905e616d 100644 --- a/drivers/char/vhm/vhm_dev.c +++ b/drivers/char/vhm/vhm_dev.c @@ -238,6 +238,7 @@ static long vhm_dev_ioctl(struct file *filep, } acrn_ioeventfd_init(vm->vmid); + acrn_irqfd_init(vm->vmid); pr_info("vhm: VM %d created\n", created_vm.vmid); break; @@ -280,6 +281,7 @@ static long vhm_dev_ioctl(struct file *filep, case IC_DESTROY_VM: { acrn_ioeventfd_deinit(vm->vmid); + acrn_irqfd_deinit(vm->vmid); ret = hcall_destroy_vm(vm->vmid); if (ret < 0) { pr_err("failed to destroy VM %ld\n", vm->vmid); @@ -660,6 +662,15 @@ static long vhm_dev_ioctl(struct file *filep, break; } + case IC_EVENT_IRQFD: { + struct acrn_irqfd args; + + if (copy_from_user(&args, (void *)ioctl_param, sizeof(args))) + return -EFAULT; + ret = acrn_irqfd(vm->vmid, &args); + break; + } + default: pr_warn("Unknown IOCTL 0x%x\n", ioctl_num); ret = 0; diff --git a/drivers/vhm/Makefile b/drivers/vhm/Makefile index 193f7692c9e3..4abfbfcba4aa 100644 --- a/drivers/vhm/Makefile +++ b/drivers/vhm/Makefile @@ -1,2 +1,2 @@ subdir-ccflags-$(CONFIG_ACRN_VHM) := -Werror -obj-y += vhm_mm.o vhm_hugetlb.o vhm_ioreq.o vhm_vm_mngt.o vhm_msi.o vhm_hypercall.o vhm_ioeventfd.o +obj-y += vhm_mm.o vhm_hugetlb.o vhm_ioreq.o vhm_vm_mngt.o vhm_msi.o vhm_hypercall.o vhm_ioeventfd.o vhm_irqfd.o diff --git a/drivers/vhm/vhm_irqfd.c b/drivers/vhm/vhm_irqfd.c new file mode 100644 index 000000000000..b8c122d5ea1f --- /dev/null +++ b/drivers/vhm/vhm_irqfd.c @@ -0,0 +1,381 @@ +/* + * irqfd for ACRN hypervisor + * + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright (c) 2018 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * 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. + * + * BSD LICENSE + * + * Copyright (C) 2018 Intel Corporation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 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 + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +static LIST_HEAD(vhm_irqfd_clients); +static DEFINE_MUTEX(vhm_irqfds_mutex); +static ASYNC_DOMAIN_EXCLUSIVE(irqfd_domain); + +/* use internally to record properties of each irqfd */ +struct acrn_vhm_irqfd { + /* vhm_irqfd_info which this irqfd belong to */ + struct vhm_irqfd_info *info; + /* wait queue node */ + wait_queue_entry_t wait; + /* async shutdown work */ + struct work_struct shutdown; + /* eventfd of this irqfd */ + struct eventfd_ctx *eventfd; + /* list to link all ioventfd together */ + struct list_head list; + /* poll_table of this irqfd */ + poll_table pt; + /* msi to send when this irqfd triggerd */ + struct acrn_msi_entry msi; +}; + +/* instance to bind irqfds of each VM */ +struct vhm_irqfd_info { + struct list_head list; + int refcnt; + /* vmid of VM */ + uint16_t vmid; + /* workqueue for async shutdown work */ + struct workqueue_struct *wq; + + /* irqfds in this instance */ + struct list_head irqfds; + spinlock_t irqfds_lock; +}; + +static struct vhm_irqfd_info *get_irqfd_info_by_vm(uint16_t vmid) +{ + struct vhm_irqfd_info *info = NULL; + + mutex_lock(&vhm_irqfds_mutex); + list_for_each_entry(info, &vhm_irqfd_clients, list) { + if (info->vmid == vmid) { + info->refcnt++; + mutex_unlock(&vhm_irqfds_mutex); + return info; + } + } + mutex_unlock(&vhm_irqfds_mutex); + return NULL; +} + +static void put_irqfd_info(struct vhm_irqfd_info *info) +{ + mutex_lock(&vhm_irqfds_mutex); + info->refcnt--; + if (info->refcnt == 0) { + list_del(&info->list); + kfree(info); + } + mutex_unlock(&vhm_irqfds_mutex); +} + +static void vhm_irqfd_inject(struct acrn_vhm_irqfd *irqfd) +{ + struct vhm_irqfd_info *info = irqfd->info; + + vhm_inject_msi(info->vmid, irqfd->msi.msi_addr, + irqfd->msi.msi_data); +} + +/* + * Try to find if the irqfd still in list info->irqfds + * + * assumes info->irqfds_lock is held + */ +static bool vhm_irqfd_is_active(struct vhm_irqfd_info *info, + struct acrn_vhm_irqfd *irqfd) +{ + struct acrn_vhm_irqfd *_irqfd; + + list_for_each_entry(_irqfd, &info->irqfds, list) + if (_irqfd == irqfd) + return true; + + return false; +} + +/* + * Remove irqfd and free it. + * + * assumes info->irqfds_lock is held + */ +static void vhm_irqfd_shutdown(struct acrn_vhm_irqfd *irqfd) +{ + u64 cnt; + + /* remove from wait queue */ + list_del_init(&irqfd->list); + eventfd_ctx_remove_wait_queue(irqfd->eventfd, &irqfd->wait, &cnt); + eventfd_ctx_put(irqfd->eventfd); + kfree(irqfd); +} + +static void vhm_irqfd_shutdown_work(struct work_struct *work) +{ + unsigned long flags; + struct acrn_vhm_irqfd *irqfd = + container_of(work, struct acrn_vhm_irqfd, shutdown); + struct vhm_irqfd_info *info = irqfd->info; + + spin_lock_irqsave(&info->irqfds_lock, flags); + if (vhm_irqfd_is_active(info, irqfd)) + vhm_irqfd_shutdown(irqfd); + spin_unlock_irqrestore(&info->irqfds_lock, flags); +} + +/* + * Called with wqh->lock held and interrupts disabled + */ +static int vhm_irqfd_wakeup(wait_queue_entry_t *wait, unsigned int mode, + int sync, void *key) +{ + struct acrn_vhm_irqfd *irqfd = + container_of(wait, struct acrn_vhm_irqfd, wait); + unsigned long poll_bits = (unsigned long)key; + struct vhm_irqfd_info *info = irqfd->info; + + if (poll_bits & POLLIN) + /* An event has been signaled, inject an interrupt */ + vhm_irqfd_inject(irqfd); + + if (poll_bits & POLLHUP) + /* async close eventfd as shutdown need hold wqh->lock */ + queue_work(info->wq, &irqfd->shutdown); + + return 0; +} + +static void vhm_irqfd_poll_func(struct file *file, + wait_queue_head_t *wqh, poll_table *pt) +{ + struct acrn_vhm_irqfd *irqfd = + container_of(pt, struct acrn_vhm_irqfd, pt); + add_wait_queue(wqh, &irqfd->wait); +} + +static int acrn_irqfd_assign(struct vhm_irqfd_info *info, + struct acrn_irqfd *args) +{ + struct acrn_vhm_irqfd *irqfd, *tmp; + struct fd f; + struct eventfd_ctx *eventfd = NULL; + int ret = 0; + unsigned int events; + + irqfd = kzalloc(sizeof(*irqfd), GFP_KERNEL); + if (!irqfd) + return -ENOMEM; + + irqfd->info = info; + memcpy(&irqfd->msi, &args->msi, sizeof(args->msi)); + INIT_LIST_HEAD(&irqfd->list); + INIT_WORK(&irqfd->shutdown, vhm_irqfd_shutdown_work); + + f = fdget(args->fd); + if (!f.file) { + ret = -EBADF; + goto out; + } + + eventfd = eventfd_ctx_fileget(f.file); + if (IS_ERR(eventfd)) { + ret = PTR_ERR(eventfd); + goto fail; + } + + irqfd->eventfd = eventfd; + + /* + * Install our own custom wake-up handling so we are notified via + * a callback whenever someone signals the underlying eventfd + */ + init_waitqueue_func_entry(&irqfd->wait, vhm_irqfd_wakeup); + init_poll_funcptr(&irqfd->pt, vhm_irqfd_poll_func); + + spin_lock_irq(&info->irqfds_lock); + + list_for_each_entry(tmp, &info->irqfds, list) { + if (irqfd->eventfd != tmp->eventfd) + continue; + /* This fd is used for another irq already. */ + ret = -EBUSY; + spin_unlock_irq(&info->irqfds_lock); + goto fail; + } + list_add_tail(&irqfd->list, &info->irqfds); + + spin_unlock_irq(&info->irqfds_lock); + + /* Check the pending event in this stage */ + events = f.file->f_op->poll(f.file, &irqfd->pt); + + if (events & POLLIN) + vhm_irqfd_inject(irqfd); + + fdput(f); + + return 0; +fail: + if (eventfd && !IS_ERR(eventfd)) + eventfd_ctx_put(eventfd); + + fdput(f); +out: + kfree(irqfd); + return ret; +} + +static int acrn_irqfd_deassign(struct vhm_irqfd_info *info, + struct acrn_irqfd *args) +{ + struct acrn_vhm_irqfd *irqfd, *tmp; + struct eventfd_ctx *eventfd; + + eventfd = eventfd_ctx_fdget(args->fd); + if (IS_ERR(eventfd)) + return PTR_ERR(eventfd); + + spin_lock_irq(&info->irqfds_lock); + + list_for_each_entry_safe(irqfd, tmp, &info->irqfds, list) { + if (irqfd->eventfd == eventfd) { + vhm_irqfd_shutdown(irqfd); + break; + } + } + + spin_unlock_irq(&info->irqfds_lock); + eventfd_ctx_put(eventfd); + + return 0; +} + +int acrn_irqfd(uint16_t vmid, struct acrn_irqfd *args) +{ + struct vhm_irqfd_info *info; + int ret; + + info = get_irqfd_info_by_vm(vmid); + if (!info) + return -ENOENT; + + if (args->flags & ACRN_IRQFD_FLAG_DEASSIGN) + ret = acrn_irqfd_deassign(info, args); + else + ret = acrn_irqfd_assign(info, args); + + put_irqfd_info(info); + return ret; +} + +int acrn_irqfd_init(uint16_t vmid) +{ + struct vhm_irqfd_info *info; + + info = get_irqfd_info_by_vm(vmid); + if (info) { + put_irqfd_info(info); + return -EEXIST; + } + + info = kzalloc(sizeof(struct vhm_irqfd_info), GFP_KERNEL); + if (!info) + return -ENOMEM; + info->vmid = vmid; + info->refcnt = 1; + INIT_LIST_HEAD(&info->irqfds); + spin_lock_init(&info->irqfds_lock); + + info->wq = alloc_workqueue("acrn_irqfd-%d", 0, 0, vmid); + if (!info->wq) { + kfree(info); + return -ENOMEM; + } + + mutex_lock(&vhm_irqfds_mutex); + list_add(&info->list, &vhm_irqfd_clients); + mutex_unlock(&vhm_irqfds_mutex); + + pr_info("ACRN vhm irqfd init done!\n"); + return 0; +} + +void acrn_irqfd_deinit(uint16_t vmid) +{ + struct acrn_vhm_irqfd *irqfd, *tmp; + struct vhm_irqfd_info *info; + + info = get_irqfd_info_by_vm(vmid); + if (!info) + return; + put_irqfd_info(info); + + destroy_workqueue(info->wq); + + spin_lock_irq(&info->irqfds_lock); + list_for_each_entry_safe(irqfd, tmp, &info->irqfds, list) + vhm_irqfd_shutdown(irqfd); + spin_unlock_irq(&info->irqfds_lock); + + /* put one more to release it */ + put_irqfd_info(info); +} diff --git a/include/linux/vhm/vhm_eventfd.h b/include/linux/vhm/vhm_eventfd.h index 3c73194cd1a1..7ee5843eca0e 100644 --- a/include/linux/vhm/vhm_eventfd.h +++ b/include/linux/vhm/vhm_eventfd.h @@ -7,4 +7,10 @@ int acrn_ioeventfd_init(int vmid); int acrn_ioeventfd(int vmid, struct acrn_ioeventfd *args); void acrn_ioeventfd_deinit(int vmid); +/* irqfd APIs */ +struct acrn_irqfd; +int acrn_irqfd_init(uint16_t vmid); +int acrn_irqfd(uint16_t vmid, struct acrn_irqfd *args); +void acrn_irqfd_deinit(uint16_t vmid); + #endif diff --git a/include/linux/vhm/vhm_ioctl_defs.h b/include/linux/vhm/vhm_ioctl_defs.h index 4ab6b02aced8..2007d58faa3f 100644 --- a/include/linux/vhm/vhm_ioctl_defs.h +++ b/include/linux/vhm/vhm_ioctl_defs.h @@ -113,6 +113,7 @@ /* VHM eventfd */ #define IC_ID_EVENT_BASE 0x70UL #define IC_EVENT_IOEVENTFD _IC_ID(IC_ID, IC_ID_EVENT_BASE + 0x00) +#define IC_EVENT_IRQFD _IC_ID(IC_ID, IC_ID_EVENT_BASE + 0x01) /** * struct vm_memseg - memory segment info for guest @@ -232,4 +233,11 @@ struct acrn_ioeventfd { uint64_t data; }; +#define ACRN_IRQFD_FLAG_DEASSIGN 0x01 +struct acrn_irqfd { + int32_t fd; + uint32_t flags; + struct acrn_msi_entry msi; +}; + #endif /* VHM_IOCTL_DEFS_H */ From 5962af2d52743d50059c8ebcd675ecf3fccd111f Mon Sep 17 00:00:00 2001 From: Yonghua Huang Date: Thu, 27 Sep 2018 00:24:18 +0800 Subject: [PATCH 0101/1103] vhm: add ioctl for set/clear IRQ line ASSERT/DEASSERT/PULSE IRQ line ioctl commands will be deprecated. Tracked-On: projectacrn/acrn-hypervisor#861 Signed-off-by: Yonghua Huang Reviewed-by: Fei Li Acked-by: Eddie Dong --- drivers/char/vhm/vhm_dev.c | 9 +++++++++ drivers/vhm/vhm_hypercall.c | 5 +++++ include/linux/vhm/acrn_hv_defs.h | 1 + include/linux/vhm/vhm_hypercall.h | 1 + include/linux/vhm/vhm_ioctl_defs.h | 1 + 5 files changed, 17 insertions(+) diff --git a/drivers/char/vhm/vhm_dev.c b/drivers/char/vhm/vhm_dev.c index 4d76905e616d..0eb31d8d8535 100644 --- a/drivers/char/vhm/vhm_dev.c +++ b/drivers/char/vhm/vhm_dev.c @@ -418,6 +418,15 @@ static long vhm_dev_ioctl(struct file *filep, break; } + case IC_SET_IRQLINE: { + ret = hcall_set_irqline(vm->vmid, ioctl_param); + if (ret < 0) { + pr_err("vhm: failed to set irqline!\n"); + return -EFAULT; + } + break; + } + case IC_INJECT_MSI: { struct acrn_msi_entry msi; diff --git a/drivers/vhm/vhm_hypercall.c b/drivers/vhm/vhm_hypercall.c index 666f9aeb87ad..9a761022be08 100644 --- a/drivers/vhm/vhm_hypercall.c +++ b/drivers/vhm/vhm_hypercall.c @@ -142,6 +142,11 @@ inline long hcall_pulse_irqline(unsigned long vmid, unsigned long irq) return acrn_hypercall2(HC_PULSE_IRQLINE, vmid, irq); } +inline long hcall_set_irqline(unsigned long vmid, unsigned long op) +{ + return acrn_hypercall2(HC_SET_IRQLINE, vmid, op); +} + inline long hcall_inject_msi(unsigned long vmid, unsigned long msi) { return acrn_hypercall2(HC_INJECT_MSI, vmid, msi); diff --git a/include/linux/vhm/acrn_hv_defs.h b/include/linux/vhm/acrn_hv_defs.h index 2cd6172e8e1d..6b7bfb421301 100644 --- a/include/linux/vhm/acrn_hv_defs.h +++ b/include/linux/vhm/acrn_hv_defs.h @@ -86,6 +86,7 @@ #define HC_PULSE_IRQLINE _HC_ID(HC_ID, HC_ID_IRQ_BASE + 0x02) #define HC_INJECT_MSI _HC_ID(HC_ID, HC_ID_IRQ_BASE + 0x03) #define HC_VM_INTR_MONITOR _HC_ID(HC_ID, HC_ID_IRQ_BASE + 0x04) +#define HC_SET_IRQLINE _HC_ID(HC_ID, HC_ID_IRQ_BASE + 0x05) /* DM ioreq management */ #define HC_ID_IOREQ_BASE 0x30UL diff --git a/include/linux/vhm/vhm_hypercall.h b/include/linux/vhm/vhm_hypercall.h index 062196ab1194..7f8b6bcecd09 100644 --- a/include/linux/vhm/vhm_hypercall.h +++ b/include/linux/vhm/vhm_hypercall.h @@ -158,6 +158,7 @@ inline long hcall_notify_req_finish(unsigned long vmid, unsigned long vcpu); inline long hcall_assert_irqline(unsigned long vmid, unsigned long irq); inline long hcall_deassert_irqline(unsigned long vmid, unsigned long irq); inline long hcall_pulse_irqline(unsigned long vmid, unsigned long irq); +inline long hcall_set_irqline(unsigned long vmid, unsigned long op); inline long hcall_inject_msi(unsigned long vmid, unsigned long msi); inline long hcall_assign_ptdev(unsigned long vmid, unsigned long bdf); inline long hcall_deassign_ptdev(unsigned long vmid, unsigned long bdf); diff --git a/include/linux/vhm/vhm_ioctl_defs.h b/include/linux/vhm/vhm_ioctl_defs.h index 2007d58faa3f..fb7b154dbd6b 100644 --- a/include/linux/vhm/vhm_ioctl_defs.h +++ b/include/linux/vhm/vhm_ioctl_defs.h @@ -81,6 +81,7 @@ #define IC_PULSE_IRQLINE _IC_ID(IC_ID, IC_ID_IRQ_BASE + 0x02) #define IC_INJECT_MSI _IC_ID(IC_ID, IC_ID_IRQ_BASE + 0x03) #define IC_VM_INTR_MONITOR _IC_ID(IC_ID, IC_ID_IRQ_BASE + 0x04) +#define IC_SET_IRQLINE _IC_ID(IC_ID, IC_ID_IRQ_BASE + 0x05) /* DM ioreq management */ #define IC_ID_IOREQ_BASE 0x30UL From b8e50ea3c4b1319d8f891c038aeda15eb7e1394a Mon Sep 17 00:00:00 2001 From: Yin Fengwei Date: Thu, 23 Aug 2018 07:29:24 +0000 Subject: [PATCH 0102/1103] sos: vhm: add hypercall to set guest vcpu registers DM will use this hypercall to set the BSP registers of UOS. To avoid the hypervisor involving for UOS boot. Tracked-On: #1231 Signed-off-by: Yin Fengwei Acked-by: Eddie Dong --- drivers/char/vhm/vhm_dev.c | 17 +++++++ include/linux/vhm/acrn_common.h | 71 ++++++++++++++++++++++++++++++ include/linux/vhm/acrn_hv_defs.h | 1 + include/linux/vhm/vhm_ioctl_defs.h | 1 + 4 files changed, 90 insertions(+) diff --git a/drivers/char/vhm/vhm_dev.c b/drivers/char/vhm/vhm_dev.c index 0eb31d8d8535..c6a97e8305fd 100644 --- a/drivers/char/vhm/vhm_dev.c +++ b/drivers/char/vhm/vhm_dev.c @@ -311,6 +311,23 @@ static long vhm_dev_ioctl(struct file *filep, return ret; } + case IC_SET_VCPU_REGS: { + struct acrn_set_vcpu_regs asvr; + + if (copy_from_user(&asvr, (void *)ioctl_param, sizeof(asvr))) + return -EFAULT; + + ret = acrn_hypercall2(HC_SET_VCPU_REGS, vm->vmid, + virt_to_phys(&asvr)); + if (ret < 0) { + pr_err("vhm: failed to set bsp state of vm %ld!\n", + vm->vmid); + return -EFAULT; + } + + return ret; + } + case IC_SET_MEMSEG: { struct vm_memmap memmap; diff --git a/include/linux/vhm/acrn_common.h b/include/linux/vhm/acrn_common.h index dfe89309fb17..69499245a994 100644 --- a/include/linux/vhm/acrn_common.h +++ b/include/linux/vhm/acrn_common.h @@ -273,6 +273,77 @@ struct acrn_create_vcpu { uint16_t pcpu_id; } __attribute__((aligned(8))); +struct acrn_gp_regs { + uint64_t rax; + uint64_t rcx; + uint64_t rdx; + uint64_t rbx; + uint64_t rsp; + uint64_t rbp; + uint64_t rsi; + uint64_t rdi; + uint64_t r8; + uint64_t r9; + uint64_t r10; + uint64_t r11; + uint64_t r12; + uint64_t r13; + uint64_t r14; + uint64_t r15; +}; + +struct acrn_descriptor_ptr { + uint16_t limit; + uint64_t base; + uint16_t reserved[3]; +} __attribute__((packed)); + +struct acrn_vcpu_regs { + struct acrn_gp_regs gprs; + struct acrn_descriptor_ptr gdt; + struct acrn_descriptor_ptr idt; + + uint64_t rip; + uint64_t cs_base; + uint64_t cr0; + uint64_t cr4; + uint64_t cr3; + uint64_t ia32_efer; + uint64_t rflags; + uint64_t reserved_64[4]; + + uint32_t cs_ar; + uint32_t reserved_32[4]; + + /* don't change the order of following sel */ + uint16_t cs_sel; + uint16_t ss_sel; + uint16_t ds_sel; + uint16_t es_sel; + uint16_t fs_sel; + uint16_t gs_sel; + uint16_t ldt_sel; + uint16_t tr_sel; + + uint16_t reserved_16[4]; +}; + +/** + * @brief Info to set vcpu state + * + * the pamameter for HC_SET_VCPU_REGS + */ +struct acrn_set_vcpu_regs { + /** the virtual CPU ID for the VCPU */ + uint16_t vcpu_id; + + /** reserved space to make cpu_state aligned to 8 bytes */ + uint16_t reserved0[3]; + + /** the structure to hold vcpu state */ + struct acrn_vcpu_regs vcpu_regs; +} __attribute__((aligned(8))); + /** * @brief Info to set ioreq buffer for a created VM * diff --git a/include/linux/vhm/acrn_hv_defs.h b/include/linux/vhm/acrn_hv_defs.h index 6b7bfb421301..c74f05fc9d47 100644 --- a/include/linux/vhm/acrn_hv_defs.h +++ b/include/linux/vhm/acrn_hv_defs.h @@ -78,6 +78,7 @@ #define HC_PAUSE_VM _HC_ID(HC_ID, HC_ID_VM_BASE + 0x03) #define HC_CREATE_VCPU _HC_ID(HC_ID, HC_ID_VM_BASE + 0x04) #define HC_RESET_VM _HC_ID(HC_ID, HC_ID_VM_BASE + 0x05) +#define HC_SET_VCPU_REGS _HC_ID(HC_ID, HC_ID_VM_BASE + 0x06) /* IRQ and Interrupts */ #define HC_ID_IRQ_BASE 0x20UL diff --git a/include/linux/vhm/vhm_ioctl_defs.h b/include/linux/vhm/vhm_ioctl_defs.h index fb7b154dbd6b..73be2dde243d 100644 --- a/include/linux/vhm/vhm_ioctl_defs.h +++ b/include/linux/vhm/vhm_ioctl_defs.h @@ -73,6 +73,7 @@ #define IC_PAUSE_VM _IC_ID(IC_ID, IC_ID_VM_BASE + 0x03) #define IC_CREATE_VCPU _IC_ID(IC_ID, IC_ID_VM_BASE + 0x04) #define IC_RESET_VM _IC_ID(IC_ID, IC_ID_VM_BASE + 0x05) +#define IC_SET_VCPU_REGS _IC_ID(IC_ID, IC_ID_VM_BASE + 0x06) /* IRQ and Interrupts */ #define IC_ID_IRQ_BASE 0x20UL From 307d7c58982c3b2eebf205fdac4bdfdee9b6adbb Mon Sep 17 00:00:00 2001 From: Steve Muckle Date: Wed, 27 Sep 2017 17:18:48 -0700 Subject: [PATCH 0103/1103] ANDROID: add script to fetch android kernel config fragments The Android kernel config fragments now live in a separate repository. To prevent others from having to search for this location, add a script to fetch and unpack the fragments. Update .gitignore to include these fragments. Change-Id: If2d4a59b86e4573b0a9b3190025dfe4191870b46 Signed-off-by: Steve Muckle --- .gitignore | 3 +++ kernel/configs/android-fetch-configs.sh | 4 ++++ 2 files changed, 7 insertions(+) create mode 100755 kernel/configs/android-fetch-configs.sh diff --git a/.gitignore b/.gitignore index 97ba6b79834c..c62842976bdb 100644 --- a/.gitignore +++ b/.gitignore @@ -132,3 +132,6 @@ all.config # Kdevelop4 *.kdev4 + +# fetched Android config fragments +kernel/configs/android-*.cfg diff --git a/kernel/configs/android-fetch-configs.sh b/kernel/configs/android-fetch-configs.sh new file mode 100755 index 000000000000..a5b56d4908dc --- /dev/null +++ b/kernel/configs/android-fetch-configs.sh @@ -0,0 +1,4 @@ +#!/bin/sh + +curl https://android.googlesource.com/kernel/configs/+archive/master/android-4.9.tar.gz | tar xzv + From 216d5371f82f5875060cb10b049f25710f2b63a4 Mon Sep 17 00:00:00 2001 From: Benoit Goby Date: Fri, 8 Nov 2013 15:24:19 -0800 Subject: [PATCH 0104/1103] ANDROID: arm: Fix dtb list when DTB_IMAGE_NAMES is empty In the 3.10 kernel, dtb-y is not defined in Makefile.boot anymore but in dts/Makefile, so it needs to be included too. Change-Id: I6d6fccf933709bcb6220ce8f12b4b9e2a7c40d63 Signed-off-by: Benoit Goby --- arch/arm/boot/Makefile | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/arm/boot/Makefile b/arch/arm/boot/Makefile index a3af4dc08c3e..19c14972eb0d 100644 --- a/arch/arm/boot/Makefile +++ b/arch/arm/boot/Makefile @@ -16,6 +16,7 @@ OBJCOPYFLAGS :=-O binary -R .comment -S ifneq ($(MACHINE),) include $(MACHINE)/Makefile.boot endif +include $(srctree)/arch/arm/boot/dts/Makefile # Note: the following conditions must always be true: # ZRELADDR == virt_to_phys(PAGE_OFFSET + TEXT_OFFSET) From d997b23b8a5134bc29a9ba47830e676d49cef0d0 Mon Sep 17 00:00:00 2001 From: Erik Gilling Date: Mon, 25 Mar 2013 15:04:41 -0700 Subject: [PATCH 0105/1103] ANDROID: arm: add config option to build zImage/dtb combo Allows a defconfig to set a default dtb to concatenate with a zImage to create a zImage-dtb. Signed-off-by: Erik Gilling Change-Id: I34b643b1c49228fbae88a56e46c93c478089620d --- arch/arm/Kconfig | 15 +++++++++++++++ arch/arm/Makefile | 2 ++ 2 files changed, 17 insertions(+) diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index e8cd55a5b04c..4c6ef59610c0 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig @@ -1839,6 +1839,21 @@ config DEPRECATED_PARAM_STRUCT This was deprecated in 2001 and announced to live on for 5 years. Some old boot loaders still use this way. +config BUILD_ARM_APPENDED_DTB_IMAGE + bool "Build a concatenated zImage/dtb by default" + depends on OF + help + Enabling this option will cause a concatenated zImage and DTB to + be built by default (instead of a standalone zImage.) The image + will built in arch/arm/boot/zImage-dtb. + +config BUILD_ARM_APPENDED_DTB_IMAGE_NAME + string "Default dtb name" + depends on BUILD_ARM_APPENDED_DTB_IMAGE + help + name of the dtb to append when building a concatenated + zImage/dtb. + # Compressed boot loader in ROM. Yes, we really want to ask about # TEXT and BSS so we preserve their values in the config files. config ZBOOT_ROM_TEXT diff --git a/arch/arm/Makefile b/arch/arm/Makefile index d1516f85f25d..5235f58d9bba 100644 --- a/arch/arm/Makefile +++ b/arch/arm/Makefile @@ -303,6 +303,8 @@ libs-y := arch/arm/lib/ $(libs-y) boot := arch/arm/boot ifeq ($(CONFIG_XIP_KERNEL),y) KBUILD_IMAGE := $(boot)/xipImage +else ifeq ($(CONFIG_BUILD_ARM_APPENDED_DTB_IMAGE),y) +KBUILD_IMAGE := $(boot)/zImage-dtb.$(CONFIG_BUILD_ARM_APPENDED_DTB_IMAGE_NAME) else KBUILD_IMAGE := $(boot)/zImage endif From d83a78e179a4fb3494cda524d3ea6d233b0593e8 Mon Sep 17 00:00:00 2001 From: Colin Cross Date: Wed, 17 Apr 2013 16:58:36 -0700 Subject: [PATCH 0106/1103] ANDROID: arm: convert build of appended dtb zImage to list of dtbs Allow CONFIG_BUILD_ARM_APPENDED_DTB_IMAGE_NAMES to specify a space separated list of dtbs to append to the zImage, and name the resulting file zImage-dtb Change-Id: Ied5d0bafbd1d01fc1f109c15c4283de7029903c9 Signed-off-by: Colin Cross --- arch/arm/Kconfig | 14 +++++++------- arch/arm/Makefile | 5 ++++- arch/arm/boot/.gitignore | 1 + arch/arm/boot/Makefile | 12 ++++++++++++ scripts/Makefile.lib | 19 ++++++++++++++++++- 5 files changed, 42 insertions(+), 9 deletions(-) diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index 4c6ef59610c0..1d67c443f9be 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig @@ -1843,16 +1843,16 @@ config BUILD_ARM_APPENDED_DTB_IMAGE bool "Build a concatenated zImage/dtb by default" depends on OF help - Enabling this option will cause a concatenated zImage and DTB to - be built by default (instead of a standalone zImage.) The image - will built in arch/arm/boot/zImage-dtb. + Enabling this option will cause a concatenated zImage and list of + DTBs to be built by default (instead of a standalone zImage.) + The image will built in arch/arm/boot/zImage-dtb -config BUILD_ARM_APPENDED_DTB_IMAGE_NAME - string "Default dtb name" +config BUILD_ARM_APPENDED_DTB_IMAGE_NAMES + string "Default dtb names" depends on BUILD_ARM_APPENDED_DTB_IMAGE help - name of the dtb to append when building a concatenated - zImage/dtb. + Space separated list of names of dtbs to append when + building a concatenated zImage-dtb. # Compressed boot loader in ROM. Yes, we really want to ask about # TEXT and BSS so we preserve their values in the config files. diff --git a/arch/arm/Makefile b/arch/arm/Makefile index 5235f58d9bba..7574964b83b9 100644 --- a/arch/arm/Makefile +++ b/arch/arm/Makefile @@ -304,7 +304,7 @@ boot := arch/arm/boot ifeq ($(CONFIG_XIP_KERNEL),y) KBUILD_IMAGE := $(boot)/xipImage else ifeq ($(CONFIG_BUILD_ARM_APPENDED_DTB_IMAGE),y) -KBUILD_IMAGE := $(boot)/zImage-dtb.$(CONFIG_BUILD_ARM_APPENDED_DTB_IMAGE_NAME) +KBUILD_IMAGE := $(boot)/zImage-dtb else KBUILD_IMAGE := $(boot)/zImage endif @@ -358,6 +358,9 @@ ifeq ($(CONFIG_VDSO),y) $(Q)$(MAKE) $(build)=arch/arm/vdso $@ endif +zImage-dtb: vmlinux scripts dtbs + $(Q)$(MAKE) $(build)=$(boot) MACHINE=$(MACHINE) $(boot)/$@ + # We use MRPROPER_FILES and CLEAN_FILES now archclean: $(Q)$(MAKE) $(clean)=$(boot) diff --git a/arch/arm/boot/.gitignore b/arch/arm/boot/.gitignore index ce1c5ff746e7..025d8aaf013d 100644 --- a/arch/arm/boot/.gitignore +++ b/arch/arm/boot/.gitignore @@ -3,3 +3,4 @@ zImage xipImage bootpImage uImage +zImage-dtb diff --git a/arch/arm/boot/Makefile b/arch/arm/boot/Makefile index 19c14972eb0d..3e3199ac1820 100644 --- a/arch/arm/boot/Makefile +++ b/arch/arm/boot/Makefile @@ -30,6 +30,14 @@ export ZRELADDR INITRD_PHYS PARAMS_PHYS targets := Image zImage xipImage bootpImage uImage +DTB_NAMES := $(subst $\",,$(CONFIG_BUILD_ARM_APPENDED_DTB_IMAGE_NAMES)) +ifneq ($(DTB_NAMES),) +DTB_LIST := $(addsuffix .dtb,$(DTB_NAMES)) +else +DTB_LIST := $(dtb-y) +endif +DTB_OBJS := $(addprefix $(obj)/dts/,$(DTB_LIST)) + ifeq ($(CONFIG_XIP_KERNEL),y) cmd_deflate_xip_data = $(CONFIG_SHELL) -c \ @@ -67,6 +75,10 @@ $(obj)/compressed/vmlinux: $(obj)/Image FORCE $(obj)/zImage: $(obj)/compressed/vmlinux FORCE $(call if_changed,objcopy) +$(obj)/zImage-dtb: $(obj)/zImage $(DTB_OBJS) FORCE + $(call if_changed,cat) + @echo ' Kernel: $@ is ready' + endif ifneq ($(LOADADDR),) diff --git a/scripts/Makefile.lib b/scripts/Makefile.lib index 61e596650ed3..22667fe49a22 100644 --- a/scripts/Makefile.lib +++ b/scripts/Makefile.lib @@ -57,8 +57,19 @@ real-obj-y := $(foreach m, $(obj-y), $(if $(strip $($(m:.o=-objs)) $($(m:.o=-y)) real-obj-m := $(foreach m, $(obj-m), $(if $(strip $($(m:.o=-objs)) $($(m:.o=-y)) $($(m:.o=-m))),$($(m:.o=-objs)) $($(m:.o=-y)) $($(m:.o=-m)),$(m))) # DTB +DTB_NAMES := $(subst $\",,$(CONFIG_BUILD_ARM_APPENDED_DTB_IMAGE_NAMES)) +ifneq ($(DTB_NAMES),) +DTB_LIST := $(addsuffix .dtb,$(DTB_NAMES)) +else +DTB_LIST := $(dtb-y) +endif + +targets += dtbs dtbs_install +targets += $(DTB_LIST) + +extra-y += $(DTB_LIST) + # If CONFIG_OF_ALL_DTBS is enabled, all DT blobs are built -extra-y += $(dtb-y) extra-$(CONFIG_OF_ALL_DTBS) += $(dtb-) # Add subdir path @@ -294,6 +305,12 @@ $(obj)/%.dtb: $(src)/%.dts $(DTC) FORCE dtc-tmp = $(subst $(comma),_,$(dot-target).dts.tmp) +# cat +# --------------------------------------------------------------------------- +# Concatentate multiple files together +quiet_cmd_cat = CAT $@ +cmd_cat = (cat $(filter-out FORCE,$^) > $@) || (rm -f $@; false) + # Bzip2 # --------------------------------------------------------------------------- From 8d5015e1e768ce7acb8efd8e9d76d13925821fb8 Mon Sep 17 00:00:00 2001 From: Alex Ray Date: Mon, 17 Mar 2014 13:44:01 -0700 Subject: [PATCH 0107/1103] ANDROID: arm64: add option to build Image.gz/dtb combo Allows a defconfig to set a list of dtbs to concatenate with an Image.gz to create a Image.gz-dtb. Includes 8adb162 arm64: Fix correct dtb clean-files location Change-Id: I0b462322d5c970f1fdf37baffece7ad058099f4a Signed-off-by: Alex Ray --- arch/arm64/Kconfig | 15 +++++++++++++++ arch/arm64/Makefile | 8 ++++++++ arch/arm64/boot/.gitignore | 1 + arch/arm64/boot/Makefile | 13 +++++++++++++ arch/arm64/boot/dts/Makefile | 14 ++++++++++++++ 5 files changed, 51 insertions(+) diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig index 1b1a0e95c751..db39f2721e57 100644 --- a/arch/arm64/Kconfig +++ b/arch/arm64/Kconfig @@ -1280,6 +1280,21 @@ config DMI However, even with this option, the resultant kernel should continue to boot on existing non-UEFI platforms. +config BUILD_ARM64_APPENDED_DTB_IMAGE + bool "Build a concatenated Image.gz/dtb by default" + depends on OF + help + Enabling this option will cause a concatenated Image.gz and list of + DTBs to be built by default (instead of a standalone Image.gz.) + The image will built in arch/arm64/boot/Image.gz-dtb + +config BUILD_ARM64_APPENDED_DTB_IMAGE_NAMES + string "Default dtb names" + depends on BUILD_ARM64_APPENDED_DTB_IMAGE + help + Space separated list of names of dtbs to append when + building a concatenated Image.gz-dtb. + endmenu config COMPAT diff --git a/arch/arm64/Makefile b/arch/arm64/Makefile index 106039d25e2f..33d3d02976f2 100644 --- a/arch/arm64/Makefile +++ b/arch/arm64/Makefile @@ -112,7 +112,12 @@ core-$(CONFIG_EFI_STUB) += $(objtree)/drivers/firmware/efi/libstub/lib.a # Default target when executing plain make boot := arch/arm64/boot +ifeq ($(CONFIG_BUILD_ARM64_APPENDED_DTB_IMAGE),y) +KBUILD_IMAGE := $(boot)/Image.gz-dtb +else KBUILD_IMAGE := $(boot)/Image.gz +endif + KBUILD_DTBS := dtbs all: Image.gz $(KBUILD_DTBS) @@ -138,6 +143,9 @@ dtbs: prepare scripts dtbs_install: $(Q)$(MAKE) $(dtbinst)=$(boot)/dts +Image.gz-dtb: vmlinux scripts dtbs + $(Q)$(MAKE) $(build)=$(boot) $(boot)/$@ + PHONY += vdso_install vdso_install: $(Q)$(MAKE) $(build)=arch/arm64/kernel/vdso $@ diff --git a/arch/arm64/boot/.gitignore b/arch/arm64/boot/.gitignore index 8dab0bb6ae66..eb3551131b1e 100644 --- a/arch/arm64/boot/.gitignore +++ b/arch/arm64/boot/.gitignore @@ -1,2 +1,3 @@ Image Image.gz +Image.gz-dtb diff --git a/arch/arm64/boot/Makefile b/arch/arm64/boot/Makefile index 1f012c506434..5bb65a9e5d13 100644 --- a/arch/arm64/boot/Makefile +++ b/arch/arm64/boot/Makefile @@ -14,10 +14,20 @@ # Based on the ia64 boot/Makefile. # +include $(srctree)/arch/arm64/boot/dts/Makefile + OBJCOPYFLAGS_Image :=-O binary -R .note -R .note.gnu.build-id -R .comment -S targets := Image Image.gz +DTB_NAMES := $(subst $\",,$(CONFIG_BUILD_ARM64_APPENDED_DTB_IMAGE_NAMES)) +ifneq ($(DTB_NAMES),) +DTB_LIST := $(addsuffix .dtb,$(DTB_NAMES)) +else +DTB_LIST := $(dtb-y) +endif +DTB_OBJS := $(addprefix $(obj)/dts/,$(DTB_LIST)) + $(obj)/Image: vmlinux FORCE $(call if_changed,objcopy) @@ -36,6 +46,9 @@ $(obj)/Image.lzma: $(obj)/Image FORCE $(obj)/Image.lzo: $(obj)/Image FORCE $(call if_changed,lzo) +$(obj)/Image.gz-dtb: $(obj)/Image.gz $(DTB_OBJS) FORCE + $(call if_changed,cat) + install: $(CONFIG_SHELL) $(srctree)/$(src)/install.sh $(KERNELRELEASE) \ $(obj)/Image System.map "$(INSTALL_PATH)" diff --git a/arch/arm64/boot/dts/Makefile b/arch/arm64/boot/dts/Makefile index 4690364d584b..ce19f2766412 100644 --- a/arch/arm64/boot/dts/Makefile +++ b/arch/arm64/boot/dts/Makefile @@ -26,3 +26,17 @@ subdir-y += synaptics subdir-y += ti subdir-y += xilinx subdir-y += zte + +targets += dtbs + +DTB_NAMES := $(subst $\",,$(CONFIG_BUILD_ARM64_APPENDED_DTB_IMAGE_NAMES)) +ifneq ($(DTB_NAMES),) +DTB_LIST := $(addsuffix .dtb,$(DTB_NAMES)) +else +DTB_LIST := $(dtb-y) +endif +targets += $(DTB_LIST) + +dtbs: $(addprefix $(obj)/, $(DTB_LIST)) + +clean-files := dts/*.dtb *.dtb From be67f4eb0460a536414fa00b77fa74bb57b01206 Mon Sep 17 00:00:00 2001 From: Haojian Zhuang Date: Fri, 22 Apr 2016 17:23:29 +0800 Subject: [PATCH 0108/1103] ANDROID: arm64: add option to build Image-dtb Some bootloaders couldn't decompress Image.gz-dtb. Change-Id: I698cd0c4ee6894e8d0655d88f3ecf4826c28a645 Signed-off-by: Haojian Zhuang Signed-off-by: John Stultz Signed-off-by: Dmitry Shmidt [AmitP: Folded following android-4.9 commit changes into this patch 56b70ac2447f ("ANDROID: ARM64: Ignore Image-dtb from git point of view")] Signed-off-by: Amit Pundir --- arch/arm64/Makefile | 2 +- arch/arm64/boot/.gitignore | 1 + arch/arm64/boot/Makefile | 3 +++ 3 files changed, 5 insertions(+), 1 deletion(-) diff --git a/arch/arm64/Makefile b/arch/arm64/Makefile index 33d3d02976f2..89d3ca230b26 100644 --- a/arch/arm64/Makefile +++ b/arch/arm64/Makefile @@ -143,7 +143,7 @@ dtbs: prepare scripts dtbs_install: $(Q)$(MAKE) $(dtbinst)=$(boot)/dts -Image.gz-dtb: vmlinux scripts dtbs +Image-dtb Image.gz-dtb: vmlinux scripts dtbs $(Q)$(MAKE) $(build)=$(boot) $(boot)/$@ PHONY += vdso_install diff --git a/arch/arm64/boot/.gitignore b/arch/arm64/boot/.gitignore index eb3551131b1e..34e35209fc2e 100644 --- a/arch/arm64/boot/.gitignore +++ b/arch/arm64/boot/.gitignore @@ -1,3 +1,4 @@ Image +Image-dtb Image.gz Image.gz-dtb diff --git a/arch/arm64/boot/Makefile b/arch/arm64/boot/Makefile index 5bb65a9e5d13..2c8cb864315e 100644 --- a/arch/arm64/boot/Makefile +++ b/arch/arm64/boot/Makefile @@ -34,6 +34,9 @@ $(obj)/Image: vmlinux FORCE $(obj)/Image.bz2: $(obj)/Image FORCE $(call if_changed,bzip2) +$(obj)/Image-dtb: $(obj)/Image $(DTB_OBJS) FORCE + $(call if_changed,cat) + $(obj)/Image.gz: $(obj)/Image FORCE $(call if_changed,gzip) From 7b4a07149c8ce331cb27312cebd4190c0b8ccdec Mon Sep 17 00:00:00 2001 From: Dmitry Shmidt Date: Tue, 28 Mar 2017 13:30:18 -0700 Subject: [PATCH 0109/1103] ANDROID: arm64: Allow to choose appended kernel image By default appended kernel image is Image.gz-dtb. New config option BUILD_ARM64_APPENDED_KERNEL_IMAGE_NAME allows to choose between Image.gz-dtb and Image-dtb. Change-Id: I1c71b85136f1beeb61782e4646820718c1ccd7e4 Signed-off-by: Dmitry Shmidt [AmitP: Add BUILD_ARM64_APPENDED_KERNEL_IMAGE_NAME as one of the default build targets to align with upstream commit 06995804b57 ("arm64: Use full path in KBUILD_IMAGE definition")] Signed-off-by: Amit Pundir --- arch/arm64/Kconfig | 20 ++++++++++++++++++++ arch/arm64/Makefile | 4 ++-- 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig index db39f2721e57..15d659d62db1 100644 --- a/arch/arm64/Kconfig +++ b/arch/arm64/Kconfig @@ -1288,6 +1288,26 @@ config BUILD_ARM64_APPENDED_DTB_IMAGE DTBs to be built by default (instead of a standalone Image.gz.) The image will built in arch/arm64/boot/Image.gz-dtb +choice + prompt "Appended DTB Kernel Image name" + depends on BUILD_ARM64_APPENDED_DTB_IMAGE + help + Enabling this option will cause a specific kernel image Image or + Image.gz to be used for final image creation. + The image will built in arch/arm64/boot/IMAGE-NAME-dtb + + config IMG_GZ_DTB + bool "Image.gz-dtb" + config IMG_DTB + bool "Image-dtb" +endchoice + +config BUILD_ARM64_APPENDED_KERNEL_IMAGE_NAME + string + depends on BUILD_ARM64_APPENDED_DTB_IMAGE + default "Image.gz-dtb" if IMG_GZ_DTB + default "Image-dtb" if IMG_DTB + config BUILD_ARM64_APPENDED_DTB_IMAGE_NAMES string "Default dtb names" depends on BUILD_ARM64_APPENDED_DTB_IMAGE diff --git a/arch/arm64/Makefile b/arch/arm64/Makefile index 89d3ca230b26..2dbca8c11498 100644 --- a/arch/arm64/Makefile +++ b/arch/arm64/Makefile @@ -113,14 +113,14 @@ core-$(CONFIG_EFI_STUB) += $(objtree)/drivers/firmware/efi/libstub/lib.a # Default target when executing plain make boot := arch/arm64/boot ifeq ($(CONFIG_BUILD_ARM64_APPENDED_DTB_IMAGE),y) -KBUILD_IMAGE := $(boot)/Image.gz-dtb +KBUILD_IMAGE := $(boot)/$(subst $\",,$(CONFIG_BUILD_ARM64_APPENDED_KERNEL_IMAGE_NAME)) else KBUILD_IMAGE := $(boot)/Image.gz endif KBUILD_DTBS := dtbs -all: Image.gz $(KBUILD_DTBS) +all: Image.gz $(KBUILD_DTBS) $(subst $\",,$(CONFIG_BUILD_ARM64_APPENDED_KERNEL_IMAGE_NAME)) Image: vmlinux From 2c223f3b7224c1a89c30f008a3369c11da98cecf Mon Sep 17 00:00:00 2001 From: Colin Cross Date: Wed, 14 Mar 2012 19:26:53 -0700 Subject: [PATCH 0110/1103] ANDROID: kdb: support new lines without carriage returns kdb expects carriage returns through the serial port to terminate commands. Modify it to accept the first seen carriage return or new line as a terminator, but not treat \r\n as two terminators. Change-Id: I06166017e7703d24310eefcb71c3a7d427088db7 Signed-off-by: Colin Cross --- kernel/debug/kdb/kdb_io.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/kernel/debug/kdb/kdb_io.c b/kernel/debug/kdb/kdb_io.c index ed5d34925ad0..8d28e3062eab 100644 --- a/kernel/debug/kdb/kdb_io.c +++ b/kernel/debug/kdb/kdb_io.c @@ -217,7 +217,7 @@ static char *kdb_read(char *buffer, size_t bufsize) int i; int diag, dtab_count; int key; - + static int last_crlf; diag = kdbgetintenv("DTABCOUNT", &dtab_count); if (diag) @@ -238,6 +238,9 @@ static char *kdb_read(char *buffer, size_t bufsize) return buffer; if (key != 9) tab = 0; + if (key != 10 && key != 13) + last_crlf = 0; + switch (key) { case 8: /* backspace */ if (cp > buffer) { @@ -255,7 +258,12 @@ static char *kdb_read(char *buffer, size_t bufsize) *cp = tmp; } break; - case 13: /* enter */ + case 10: /* new line */ + case 13: /* carriage return */ + /* handle \n after \r */ + if (last_crlf && last_crlf != key) + break; + last_crlf = key; *lastchar++ = '\n'; *lastchar++ = '\0'; if (!KDB_STATE(KGDB_TRANS)) { From bb3f3a2ebc245ccf82f7a707f5473c0e736958e7 Mon Sep 17 00:00:00 2001 From: Martijn Coenen Date: Tue, 6 Jun 2017 17:04:42 -0700 Subject: [PATCH 0111/1103] ANDROID: binder: add support for RT prio inheritance. Adds support for SCHED_BATCH/SCHED_FIFO/SCHED_RR priority inheritance. Change-Id: I71f356e476be2933713a0ecfa2cc31aa141e2dc6 Signed-off-by: Martijn Coenen [AmitP: Include for struct sched_param] Signed-off-by: Amit Pundir --- drivers/android/binder.c | 164 ++++++++++++++++++++++++++++++++------- 1 file changed, 135 insertions(+), 29 deletions(-) diff --git a/drivers/android/binder.c b/drivers/android/binder.c index d58763b6b009..fe2d92e28105 100644 --- a/drivers/android/binder.c +++ b/drivers/android/binder.c @@ -73,6 +73,7 @@ #include #include +#include #include @@ -462,6 +463,22 @@ enum binder_deferred_state { BINDER_DEFERRED_RELEASE = 0x04, }; +/** + * struct binder_priority - scheduler policy and priority + * @sched_policy scheduler policy + * @prio [100..139] for SCHED_NORMAL, [0..99] for FIFO/RT + * + * The binder driver supports inheriting the following scheduler policies: + * SCHED_NORMAL + * SCHED_BATCH + * SCHED_FIFO + * SCHED_RR + */ +struct binder_priority { + unsigned int sched_policy; + int prio; +}; + /** * struct binder_proc - binder process bookkeeping * @proc_node: element for binder_procs list @@ -540,7 +557,7 @@ struct binder_proc { int requested_threads; int requested_threads_started; int tmp_ref; - long default_priority; + struct binder_priority default_priority; struct dentry *debugfs_entry; struct binder_alloc alloc; struct binder_context *context; @@ -625,8 +642,8 @@ struct binder_transaction { struct binder_buffer *buffer; unsigned int code; unsigned int flags; - long priority; - long saved_priority; + struct binder_priority priority; + struct binder_priority saved_priority; kuid_t sender_euid; /** * @lock: protects @from, @to_proc, and @to_thread @@ -1107,22 +1124,93 @@ static void binder_wakeup_proc_ilocked(struct binder_proc *proc) binder_wakeup_thread_ilocked(proc, thread, /* sync = */false); } -static void binder_set_nice(long nice) +static bool is_rt_policy(int policy) +{ + return policy == SCHED_FIFO || policy == SCHED_RR; +} + +static bool is_fair_policy(int policy) +{ + return policy == SCHED_NORMAL || policy == SCHED_BATCH; +} + +static bool binder_supported_policy(int policy) +{ + return is_fair_policy(policy) || is_rt_policy(policy); +} + +static int to_userspace_prio(int policy, int kernel_priority) +{ + if (is_fair_policy(policy)) + return PRIO_TO_NICE(kernel_priority); + else + return MAX_USER_RT_PRIO - 1 - kernel_priority; +} + +static int to_kernel_prio(int policy, int user_priority) +{ + if (is_fair_policy(policy)) + return NICE_TO_PRIO(user_priority); + else + return MAX_USER_RT_PRIO - 1 - user_priority; +} + +static void binder_set_priority(struct task_struct *task, + struct binder_priority desired) { - long min_nice; + int priority; /* user-space prio value */ + bool has_cap_nice; + unsigned int policy = desired.sched_policy; - if (can_nice(current, nice)) { - set_user_nice(current, nice); + if (task->policy == policy && task->normal_prio == desired.prio) return; + + has_cap_nice = has_capability_noaudit(task, CAP_SYS_NICE); + + priority = to_userspace_prio(policy, desired.prio); + + if (is_rt_policy(policy) && !has_cap_nice) { + long max_rtprio = task_rlimit(task, RLIMIT_RTPRIO); + + if (max_rtprio == 0) { + policy = SCHED_NORMAL; + priority = MIN_NICE; + } else if (priority > max_rtprio) { + priority = max_rtprio; + } } - min_nice = rlimit_to_nice(rlimit(RLIMIT_NICE)); - binder_debug(BINDER_DEBUG_PRIORITY_CAP, - "%d: nice value %ld not allowed use %ld instead\n", - current->pid, nice, min_nice); - set_user_nice(current, min_nice); - if (min_nice <= MAX_NICE) - return; - binder_user_error("%d RLIMIT_NICE not set\n", current->pid); + + if (is_fair_policy(policy) && !has_cap_nice) { + long min_nice = rlimit_to_nice(task_rlimit(task, RLIMIT_NICE)); + + if (min_nice > MAX_NICE) { + binder_user_error("%d RLIMIT_NICE not set\n", + task->pid); + return; + } else if (priority < min_nice) { + priority = min_nice; + } + } + + if (policy != desired.sched_policy || + to_kernel_prio(policy, priority) != desired.prio) + binder_debug(BINDER_DEBUG_PRIORITY_CAP, + "%d: priority %d not allowed, using %d instead\n", + task->pid, desired.prio, + to_kernel_prio(policy, priority)); + + /* Set the actual priority */ + if (task->policy != policy || is_rt_policy(policy)) { + struct sched_param params; + + params.sched_priority = is_rt_policy(policy) ? priority : 0; + + sched_setscheduler_nocheck(task, + policy | SCHED_RESET_ON_FORK, + ¶ms); + } + if (is_fair_policy(policy)) + set_user_nice(task, priority); } static struct binder_node *binder_get_node_ilocked(struct binder_proc *proc, @@ -1207,7 +1295,8 @@ static struct binder_node *binder_init_node_ilocked( node->ptr = ptr; node->cookie = cookie; node->work.type = BINDER_WORK_NODE; - node->min_priority = flags & FLAT_BINDER_FLAG_PRIORITY_MASK; + node->min_priority = NICE_TO_PRIO( + flags & FLAT_BINDER_FLAG_PRIORITY_MASK); node->accept_fds = !!(flags & FLAT_BINDER_FLAG_ACCEPTS_FDS); spin_lock_init(&node->lock); INIT_LIST_HEAD(&node->work.entry); @@ -2780,7 +2869,7 @@ static void binder_transaction(struct binder_proc *proc, } thread->transaction_stack = in_reply_to->to_parent; binder_inner_proc_unlock(proc); - binder_set_nice(in_reply_to->saved_priority); + binder_set_priority(current, in_reply_to->saved_priority); target_thread = binder_get_txn_from_and_acq_inner(in_reply_to); if (target_thread == NULL) { return_error = BR_DEAD_REPLY; @@ -2953,7 +3042,15 @@ static void binder_transaction(struct binder_proc *proc, t->to_thread = target_thread; t->code = tr->code; t->flags = tr->flags; - t->priority = task_nice(current); + if (!(t->flags & TF_ONE_WAY) && + binder_supported_policy(current->policy)) { + /* Inherit supported policies for synchronous transactions */ + t->priority.sched_policy = current->policy; + t->priority.prio = current->normal_prio; + } else { + /* Otherwise, fall back to the default priority */ + t->priority = target_proc->default_priority; + } trace_binder_transaction(reply, t, target_node); @@ -3865,7 +3962,7 @@ static int binder_thread_read(struct binder_proc *proc, wait_event_interruptible(binder_user_error_wait, binder_stop_on_user_error < 2); } - binder_set_nice(proc->default_priority); + binder_set_priority(current, proc->default_priority); } if (non_block) { @@ -4080,16 +4177,17 @@ static int binder_thread_read(struct binder_proc *proc, BUG_ON(t->buffer == NULL); if (t->buffer->target_node) { struct binder_node *target_node = t->buffer->target_node; + struct binder_priority prio = t->priority; tr.target.ptr = target_node->ptr; tr.cookie = target_node->cookie; - t->saved_priority = task_nice(current); - if (t->priority < target_node->min_priority && - !(t->flags & TF_ONE_WAY)) - binder_set_nice(t->priority); - else if (!(t->flags & TF_ONE_WAY) || - t->saved_priority > target_node->min_priority) - binder_set_nice(target_node->min_priority); + t->saved_priority.sched_policy = current->policy; + t->saved_priority.prio = current->normal_prio; + if (target_node->min_priority < t->priority.prio) { + prio.sched_policy = SCHED_NORMAL; + prio.prio = target_node->min_priority; + } + binder_set_priority(current, prio); cmd = BR_TRANSACTION; } else { tr.target.ptr = 0; @@ -4767,7 +4865,14 @@ static int binder_open(struct inode *nodp, struct file *filp) proc->tsk = current->group_leader; mutex_init(&proc->files_lock); INIT_LIST_HEAD(&proc->todo); - proc->default_priority = task_nice(current); + if (binder_supported_policy(current->policy)) { + proc->default_priority.sched_policy = current->policy; + proc->default_priority.prio = current->normal_prio; + } else { + proc->default_priority.sched_policy = SCHED_NORMAL; + proc->default_priority.prio = NICE_TO_PRIO(0); + } + binder_dev = container_of(filp->private_data, struct binder_device, miscdev); proc->context = &binder_dev->context; @@ -5061,13 +5166,14 @@ static void print_binder_transaction_ilocked(struct seq_file *m, spin_lock(&t->lock); to_proc = t->to_proc; seq_printf(m, - "%s %d: %pK from %d:%d to %d:%d code %x flags %x pri %ld r%d", + "%s %d: %pK from %d:%d to %d:%d code %x flags %x pri %d:%d r%d", prefix, t->debug_id, t, t->from ? t->from->proc->pid : 0, t->from ? t->from->pid : 0, to_proc ? to_proc->pid : 0, t->to_thread ? t->to_thread->pid : 0, - t->code, t->flags, t->priority, t->need_reply); + t->code, t->flags, t->priority.sched_policy, + t->priority.prio, t->need_reply); spin_unlock(&t->lock); if (proc != to_proc) { From 1da6a5f29d9a6386d09f3a3146814104bacf9064 Mon Sep 17 00:00:00 2001 From: Martijn Coenen Date: Wed, 7 Jun 2017 09:29:14 -0700 Subject: [PATCH 0112/1103] ANDROID: binder: add min sched_policy to node. This change adds flags to flat_binder_object.flags to allow indicating a minimum scheduling policy for the node. It also clarifies the valid value range for the priority bits in the flags. Internally, we use the priority map that the kernel uses, e.g. [0..99] for real-time policies and [100..139] for the SCHED_NORMAL/SCHED_BATCH policies. Bug: 34461621 Bug: 37293077 Change-Id: I12438deecb53df432da18c6fc77460768ae726d2 Signed-off-by: Martijn Coenen --- drivers/android/binder.c | 26 ++++++++++++++---- include/uapi/linux/android/binder.h | 41 ++++++++++++++++++++++++++++- 2 files changed, 61 insertions(+), 6 deletions(-) diff --git a/drivers/android/binder.c b/drivers/android/binder.c index fe2d92e28105..446d5b30bb9a 100644 --- a/drivers/android/binder.c +++ b/drivers/android/binder.c @@ -351,6 +351,8 @@ struct binder_error { * and by @lock) * @has_async_transaction: async transaction to node in progress * (protected by @lock) + * @sched_policy: minimum scheduling policy for node + * (invariant after initialized) * @accept_fds: file descriptor operations supported for node * (invariant after initialized) * @min_priority: minimum scheduling priority @@ -390,6 +392,7 @@ struct binder_node { /* * invariant after initialization */ + u8 sched_policy:2; u8 accept_fds:1; u8 min_priority; }; @@ -1263,6 +1266,7 @@ static struct binder_node *binder_init_node_ilocked( binder_uintptr_t ptr = fp ? fp->binder : 0; binder_uintptr_t cookie = fp ? fp->cookie : 0; __u32 flags = fp ? fp->flags : 0; + s8 priority; assert_spin_locked(&proc->inner_lock); @@ -1295,8 +1299,10 @@ static struct binder_node *binder_init_node_ilocked( node->ptr = ptr; node->cookie = cookie; node->work.type = BINDER_WORK_NODE; - node->min_priority = NICE_TO_PRIO( - flags & FLAT_BINDER_FLAG_PRIORITY_MASK); + priority = flags & FLAT_BINDER_FLAG_PRIORITY_MASK; + node->sched_policy = (flags & FLAT_BINDER_FLAG_PRIORITY_MASK) >> + FLAT_BINDER_FLAG_SCHED_POLICY_SHIFT; + node->min_priority = to_kernel_prio(node->sched_policy, priority); node->accept_fds = !!(flags & FLAT_BINDER_FLAG_ACCEPTS_FDS); spin_lock_init(&node->lock); INIT_LIST_HEAD(&node->work.entry); @@ -4183,8 +4189,17 @@ static int binder_thread_read(struct binder_proc *proc, tr.cookie = target_node->cookie; t->saved_priority.sched_policy = current->policy; t->saved_priority.prio = current->normal_prio; - if (target_node->min_priority < t->priority.prio) { - prio.sched_policy = SCHED_NORMAL; + if (target_node->min_priority < t->priority.prio || + (target_node->min_priority == t->priority.prio && + target_node->sched_policy == SCHED_FIFO)) { + /* + * In case the minimum priority on the node is + * higher (lower value), use that priority. If + * the priority is the same, but the node uses + * SCHED_FIFO, prefer SCHED_FIFO, since it can + * run unbounded, unlike SCHED_RR. + */ + prio.sched_policy = target_node->sched_policy; prio.prio = target_node->min_priority; } binder_set_priority(current, prio); @@ -5291,8 +5306,9 @@ static void print_binder_node_nilocked(struct seq_file *m, hlist_for_each_entry(ref, &node->refs, node_entry) count++; - seq_printf(m, " node %d: u%016llx c%016llx hs %d hw %d ls %d lw %d is %d iw %d tr %d", + seq_printf(m, " node %d: u%016llx c%016llx pri %d:%d hs %d hw %d ls %d lw %d is %d iw %d tr %d", node->debug_id, (u64)node->ptr, (u64)node->cookie, + node->sched_policy, node->min_priority, node->has_strong_ref, node->has_weak_ref, node->local_strong_refs, node->local_weak_refs, node->internal_strong_refs, count, node->tmp_refs); diff --git a/include/uapi/linux/android/binder.h b/include/uapi/linux/android/binder.h index bfaec6903b8b..3fe109467ee3 100644 --- a/include/uapi/linux/android/binder.h +++ b/include/uapi/linux/android/binder.h @@ -38,9 +38,48 @@ enum { BINDER_TYPE_PTR = B_PACK_CHARS('p', 't', '*', B_TYPE_LARGE), }; -enum { +/** + * enum flat_binder_object_shifts: shift values for flat_binder_object_flags + * @FLAT_BINDER_FLAG_SCHED_POLICY_SHIFT: shift for getting scheduler policy. + * + */ +enum flat_binder_object_shifts { + FLAT_BINDER_FLAG_SCHED_POLICY_SHIFT = 9, +}; + +/** + * enum flat_binder_object_flags - flags for use in flat_binder_object.flags + */ +enum flat_binder_object_flags { + /** + * @FLAT_BINDER_FLAG_PRIORITY_MASK: bit-mask for min scheduler priority + * + * These bits can be used to set the minimum scheduler priority + * at which transactions into this node should run. Valid values + * in these bits depend on the scheduler policy encoded in + * @FLAT_BINDER_FLAG_SCHED_POLICY_MASK. + * + * For SCHED_NORMAL/SCHED_BATCH, the valid range is between [-20..19] + * For SCHED_FIFO/SCHED_RR, the value can run between [1..99] + */ FLAT_BINDER_FLAG_PRIORITY_MASK = 0xff, + /** + * @FLAT_BINDER_FLAG_ACCEPTS_FDS: whether the node accepts fds. + */ FLAT_BINDER_FLAG_ACCEPTS_FDS = 0x100, + /** + * @FLAT_BINDER_FLAG_SCHED_POLICY_MASK: bit-mask for scheduling policy + * + * These two bits can be used to set the min scheduling policy at which + * transactions on this node should run. These match the UAPI + * scheduler policy values, eg: + * 00b: SCHED_NORMAL + * 01b: SCHED_FIFO + * 10b: SCHED_RR + * 11b: SCHED_BATCH + */ + FLAT_BINDER_FLAG_SCHED_POLICY_MASK = + 3U << FLAT_BINDER_FLAG_SCHED_POLICY_SHIFT, }; #ifdef BINDER_IPC_32BIT From 7c42d5d645e38c330d9a91b7bee8d9babe0d9fed Mon Sep 17 00:00:00 2001 From: Martijn Coenen Date: Wed, 7 Jun 2017 10:02:12 -0700 Subject: [PATCH 0113/1103] ANDROID: binder: improve priority inheritance. By raising the priority of a thread selected for a transaction *before* we wake it up. Delay restoring the priority when doing a reply until after we wake-up the process receiving the reply. Change-Id: Ic332e4e0ed7d2d3ca6ab1034da4629c9eadd3405 Signed-off-by: Martijn Coenen --- drivers/android/binder.c | 74 ++++++++++++++++++++++++++++------------ 1 file changed, 53 insertions(+), 21 deletions(-) diff --git a/drivers/android/binder.c b/drivers/android/binder.c index 446d5b30bb9a..fe7ec579e489 100644 --- a/drivers/android/binder.c +++ b/drivers/android/binder.c @@ -610,6 +610,7 @@ enum { * @is_dead: thread is dead and awaiting free * when outstanding transactions are cleaned up * (protected by @proc->inner_lock) + * @task: struct task_struct for this thread * * Bookkeeping structure for binder threads. */ @@ -629,6 +630,7 @@ struct binder_thread { struct binder_stats stats; atomic_t tmp_ref; bool is_dead; + struct task_struct *task; }; struct binder_transaction { @@ -647,6 +649,7 @@ struct binder_transaction { unsigned int flags; struct binder_priority priority; struct binder_priority saved_priority; + bool set_priority_called; kuid_t sender_euid; /** * @lock: protects @from, @to_proc, and @to_thread @@ -1216,6 +1219,38 @@ static void binder_set_priority(struct task_struct *task, set_user_nice(task, priority); } +static void binder_transaction_priority(struct task_struct *task, + struct binder_transaction *t, + struct binder_priority node_prio) +{ + struct binder_priority desired_prio; + + if (t->set_priority_called) + return; + + t->set_priority_called = true; + t->saved_priority.sched_policy = task->policy; + t->saved_priority.prio = task->normal_prio; + + desired_prio.prio = t->priority.prio; + desired_prio.sched_policy = t->priority.sched_policy; + + if (node_prio.prio < t->priority.prio || + (node_prio.prio == t->priority.prio && + node_prio.sched_policy == SCHED_FIFO)) { + /* + * In case the minimum priority on the node is + * higher (lower value), use that priority. If + * the priority is the same, but the node uses + * SCHED_FIFO, prefer SCHED_FIFO, since it can + * run unbounded, unlike SCHED_RR. + */ + desired_prio = node_prio; + } + + binder_set_priority(task, desired_prio); +} + static struct binder_node *binder_get_node_ilocked(struct binder_proc *proc, binder_uintptr_t ptr) { @@ -2728,11 +2763,15 @@ static bool binder_proc_transaction(struct binder_transaction *t, struct binder_thread *thread) { struct binder_node *node = t->buffer->target_node; + struct binder_priority node_prio; bool oneway = !!(t->flags & TF_ONE_WAY); bool pending_async = false; BUG_ON(!node); binder_node_lock(node); + node_prio.prio = node->min_priority; + node_prio.sched_policy = node->sched_policy; + if (oneway) { BUG_ON(thread); if (node->has_async_transaction) { @@ -2753,12 +2792,14 @@ static bool binder_proc_transaction(struct binder_transaction *t, if (!thread && !pending_async) thread = binder_select_thread_ilocked(proc); - if (thread) + if (thread) { + binder_transaction_priority(thread->task, t, node_prio); binder_enqueue_thread_work_ilocked(thread, &t->work); - else if (!pending_async) + } else if (!pending_async) { binder_enqueue_work_ilocked(&t->work, &proc->todo); - else + } else { binder_enqueue_work_ilocked(&t->work, &node->async_todo); + } if (!pending_async) binder_wakeup_thread_ilocked(proc, thread, !oneway /* sync */); @@ -2875,7 +2916,6 @@ static void binder_transaction(struct binder_proc *proc, } thread->transaction_stack = in_reply_to->to_parent; binder_inner_proc_unlock(proc); - binder_set_priority(current, in_reply_to->saved_priority); target_thread = binder_get_txn_from_and_acq_inner(in_reply_to); if (target_thread == NULL) { return_error = BR_DEAD_REPLY; @@ -3285,6 +3325,7 @@ static void binder_transaction(struct binder_proc *proc, binder_enqueue_thread_work_ilocked(target_thread, &t->work); binder_inner_proc_unlock(target_proc); wake_up_interruptible_sync(&target_thread->wait); + binder_set_priority(current, in_reply_to->saved_priority); binder_free_transaction(in_reply_to); } else if (!(t->flags & TF_ONE_WAY)) { BUG_ON(t->buffer->async_transaction != 0); @@ -3388,6 +3429,7 @@ static void binder_transaction(struct binder_proc *proc, BUG_ON(thread->return_error.cmd != BR_OK); if (in_reply_to) { + binder_set_priority(current, in_reply_to->saved_priority); thread->return_error.cmd = BR_TRANSACTION_COMPLETE; binder_enqueue_thread_work(thread, &thread->return_error.work); binder_send_failed_reply(in_reply_to, return_error); @@ -4183,26 +4225,13 @@ static int binder_thread_read(struct binder_proc *proc, BUG_ON(t->buffer == NULL); if (t->buffer->target_node) { struct binder_node *target_node = t->buffer->target_node; - struct binder_priority prio = t->priority; + struct binder_priority node_prio; tr.target.ptr = target_node->ptr; tr.cookie = target_node->cookie; - t->saved_priority.sched_policy = current->policy; - t->saved_priority.prio = current->normal_prio; - if (target_node->min_priority < t->priority.prio || - (target_node->min_priority == t->priority.prio && - target_node->sched_policy == SCHED_FIFO)) { - /* - * In case the minimum priority on the node is - * higher (lower value), use that priority. If - * the priority is the same, but the node uses - * SCHED_FIFO, prefer SCHED_FIFO, since it can - * run unbounded, unlike SCHED_RR. - */ - prio.sched_policy = target_node->sched_policy; - prio.prio = target_node->min_priority; - } - binder_set_priority(current, prio); + node_prio.sched_policy = target_node->sched_policy; + node_prio.prio = target_node->min_priority; + binder_transaction_priority(current, t, node_prio); cmd = BR_TRANSACTION; } else { tr.target.ptr = 0; @@ -4380,6 +4409,8 @@ static struct binder_thread *binder_get_thread_ilocked( binder_stats_created(BINDER_STAT_THREAD); thread->proc = proc; thread->pid = current->pid; + get_task_struct(current); + thread->task = current; atomic_set(&thread->tmp_ref, 0); init_waitqueue_head(&thread->wait); INIT_LIST_HEAD(&thread->todo); @@ -4430,6 +4461,7 @@ static void binder_free_thread(struct binder_thread *thread) BUG_ON(!list_empty(&thread->todo)); binder_stats_deleted(BINDER_STAT_THREAD); binder_proc_dec_tmpref(thread->proc); + put_task_struct(thread->task); kfree(thread); } From 330ce3e508ac96c68f5bc63162175aeacb9c66d7 Mon Sep 17 00:00:00 2001 From: Martijn Coenen Date: Fri, 23 Jun 2017 10:13:43 -0700 Subject: [PATCH 0114/1103] ANDROID: binder: add RT inheritance flag to node. Allows a binder node to specify whether it wants to inherit real-time scheduling policy from a caller. Change-Id: I375b6094bf441c19f19cba06d5a6be02cd07d714 Signed-off-by: Martijn Coenen --- drivers/android/binder.c | 22 +++++++++++++++++----- include/uapi/linux/android/binder.h | 8 ++++++++ 2 files changed, 25 insertions(+), 5 deletions(-) diff --git a/drivers/android/binder.c b/drivers/android/binder.c index fe7ec579e489..205863a07b89 100644 --- a/drivers/android/binder.c +++ b/drivers/android/binder.c @@ -357,6 +357,8 @@ struct binder_error { * (invariant after initialized) * @min_priority: minimum scheduling priority * (invariant after initialized) + * @inherit_rt: inherit RT scheduling policy from caller + * (invariant after initialized) * @async_todo: list of async work items * (protected by @proc->inner_lock) * @@ -393,6 +395,7 @@ struct binder_node { * invariant after initialization */ u8 sched_policy:2; + u8 inherit_rt:1; u8 accept_fds:1; u8 min_priority; }; @@ -1221,7 +1224,8 @@ static void binder_set_priority(struct task_struct *task, static void binder_transaction_priority(struct task_struct *task, struct binder_transaction *t, - struct binder_priority node_prio) + struct binder_priority node_prio, + bool inherit_rt) { struct binder_priority desired_prio; @@ -1232,8 +1236,13 @@ static void binder_transaction_priority(struct task_struct *task, t->saved_priority.sched_policy = task->policy; t->saved_priority.prio = task->normal_prio; - desired_prio.prio = t->priority.prio; - desired_prio.sched_policy = t->priority.sched_policy; + if (!inherit_rt && is_rt_policy(desired_prio.sched_policy)) { + desired_prio.prio = NICE_TO_PRIO(0); + desired_prio.sched_policy = SCHED_NORMAL; + } else { + desired_prio.prio = t->priority.prio; + desired_prio.sched_policy = t->priority.sched_policy; + } if (node_prio.prio < t->priority.prio || (node_prio.prio == t->priority.prio && @@ -1339,6 +1348,7 @@ static struct binder_node *binder_init_node_ilocked( FLAT_BINDER_FLAG_SCHED_POLICY_SHIFT; node->min_priority = to_kernel_prio(node->sched_policy, priority); node->accept_fds = !!(flags & FLAT_BINDER_FLAG_ACCEPTS_FDS); + node->inherit_rt = !!(flags & FLAT_BINDER_FLAG_INHERIT_RT); spin_lock_init(&node->lock); INIT_LIST_HEAD(&node->work.entry); INIT_LIST_HEAD(&node->async_todo); @@ -2793,7 +2803,8 @@ static bool binder_proc_transaction(struct binder_transaction *t, thread = binder_select_thread_ilocked(proc); if (thread) { - binder_transaction_priority(thread->task, t, node_prio); + binder_transaction_priority(thread->task, t, node_prio, + node->inherit_rt); binder_enqueue_thread_work_ilocked(thread, &t->work); } else if (!pending_async) { binder_enqueue_work_ilocked(&t->work, &proc->todo); @@ -4231,7 +4242,8 @@ static int binder_thread_read(struct binder_proc *proc, tr.cookie = target_node->cookie; node_prio.sched_policy = target_node->sched_policy; node_prio.prio = target_node->min_priority; - binder_transaction_priority(current, t, node_prio); + binder_transaction_priority(current, t, node_prio, + target_node->inherit_rt); cmd = BR_TRANSACTION; } else { tr.target.ptr = 0; diff --git a/include/uapi/linux/android/binder.h b/include/uapi/linux/android/binder.h index 3fe109467ee3..b4723e36b6cf 100644 --- a/include/uapi/linux/android/binder.h +++ b/include/uapi/linux/android/binder.h @@ -80,6 +80,14 @@ enum flat_binder_object_flags { */ FLAT_BINDER_FLAG_SCHED_POLICY_MASK = 3U << FLAT_BINDER_FLAG_SCHED_POLICY_SHIFT, + + /** + * @FLAT_BINDER_FLAG_INHERIT_RT: whether the node inherits RT policy + * + * Only when set, calls into this node will inherit a real-time + * scheduling policy from the caller (for synchronous transactions). + */ + FLAT_BINDER_FLAG_INHERIT_RT = 0x800, }; #ifdef BINDER_IPC_32BIT From b3cc172a9f7aa7ab7b19e3d68cf4f77d66363ef2 Mon Sep 17 00:00:00 2001 From: Martijn Coenen Date: Fri, 26 May 2017 10:48:56 -0700 Subject: [PATCH 0115/1103] ANDROID: binder: don't check prio permissions on restore. Because we have disabled RT priority inheritance for the regular binder domain, the following can happen: 1) thread A (prio 98) calls into thread B 2) because RT prio inheritance is disabled, thread B runs at the lowest nice (prio 100) instead 3) thread B calls back into A; A will run at prio 100 for the duration of the transaction 4) When thread A is done with the call from B, we will try to restore the prio back to 98. But, we fail because the process doesn't hold CAP_SYS_NICE, neither is RLIMIT_RT_PRIO set. While the proper fix going forward will be to correctly apply CAP_SYS_NICE or RLIMIT_RT_PRIO, for now it seems reasonable to not check permissions on the restore path. Change-Id: Ibede5960c9b7bb786271c001e405de50be64d944 Signed-off-by: Martijn Coenen --- drivers/android/binder.c | 27 ++++++++++++++++++++------- 1 file changed, 20 insertions(+), 7 deletions(-) diff --git a/drivers/android/binder.c b/drivers/android/binder.c index 205863a07b89..ba75a05b37fb 100644 --- a/drivers/android/binder.c +++ b/drivers/android/binder.c @@ -1164,8 +1164,9 @@ static int to_kernel_prio(int policy, int user_priority) return MAX_USER_RT_PRIO - 1 - user_priority; } -static void binder_set_priority(struct task_struct *task, - struct binder_priority desired) +static void binder_do_set_priority(struct task_struct *task, + struct binder_priority desired, + bool verify) { int priority; /* user-space prio value */ bool has_cap_nice; @@ -1178,7 +1179,7 @@ static void binder_set_priority(struct task_struct *task, priority = to_userspace_prio(policy, desired.prio); - if (is_rt_policy(policy) && !has_cap_nice) { + if (verify && is_rt_policy(policy) && !has_cap_nice) { long max_rtprio = task_rlimit(task, RLIMIT_RTPRIO); if (max_rtprio == 0) { @@ -1189,7 +1190,7 @@ static void binder_set_priority(struct task_struct *task, } } - if (is_fair_policy(policy) && !has_cap_nice) { + if (verify && is_fair_policy(policy) && !has_cap_nice) { long min_nice = rlimit_to_nice(task_rlimit(task, RLIMIT_NICE)); if (min_nice > MAX_NICE) { @@ -1222,6 +1223,18 @@ static void binder_set_priority(struct task_struct *task, set_user_nice(task, priority); } +static void binder_set_priority(struct task_struct *task, + struct binder_priority desired) +{ + binder_do_set_priority(task, desired, /* verify = */ true); +} + +static void binder_restore_priority(struct task_struct *task, + struct binder_priority desired) +{ + binder_do_set_priority(task, desired, /* verify = */ false); +} + static void binder_transaction_priority(struct task_struct *task, struct binder_transaction *t, struct binder_priority node_prio, @@ -3336,7 +3349,7 @@ static void binder_transaction(struct binder_proc *proc, binder_enqueue_thread_work_ilocked(target_thread, &t->work); binder_inner_proc_unlock(target_proc); wake_up_interruptible_sync(&target_thread->wait); - binder_set_priority(current, in_reply_to->saved_priority); + binder_restore_priority(current, in_reply_to->saved_priority); binder_free_transaction(in_reply_to); } else if (!(t->flags & TF_ONE_WAY)) { BUG_ON(t->buffer->async_transaction != 0); @@ -3440,7 +3453,7 @@ static void binder_transaction(struct binder_proc *proc, BUG_ON(thread->return_error.cmd != BR_OK); if (in_reply_to) { - binder_set_priority(current, in_reply_to->saved_priority); + binder_restore_priority(current, in_reply_to->saved_priority); thread->return_error.cmd = BR_TRANSACTION_COMPLETE; binder_enqueue_thread_work(thread, &thread->return_error.work); binder_send_failed_reply(in_reply_to, return_error); @@ -4021,7 +4034,7 @@ static int binder_thread_read(struct binder_proc *proc, wait_event_interruptible(binder_user_error_wait, binder_stop_on_user_error < 2); } - binder_set_priority(current, proc->default_priority); + binder_restore_priority(current, proc->default_priority); } if (non_block) { From 87f4dde96e04d69966972503381597c7571d8f2e Mon Sep 17 00:00:00 2001 From: Martijn Coenen Date: Mon, 8 May 2017 09:33:22 -0700 Subject: [PATCH 0116/1103] ANDROID: binder: Add tracing for binder priority inheritance. Bug: 34461621 Change-Id: I5ebb1c0c49fd42a89ee250a1d70221f767c82c7c Signed-off-by: Martijn Coenen --- drivers/android/binder.c | 4 ++++ drivers/android/binder_trace.h | 24 ++++++++++++++++++++++++ 2 files changed, 28 insertions(+) diff --git a/drivers/android/binder.c b/drivers/android/binder.c index ba75a05b37fb..67746f6ce6fb 100644 --- a/drivers/android/binder.c +++ b/drivers/android/binder.c @@ -1209,6 +1209,10 @@ static void binder_do_set_priority(struct task_struct *task, task->pid, desired.prio, to_kernel_prio(policy, priority)); + trace_binder_set_priority(task->tgid, task->pid, task->normal_prio, + to_kernel_prio(policy, priority), + desired.prio); + /* Set the actual priority */ if (task->policy != policy || is_rt_policy(policy)) { struct sched_param params; diff --git a/drivers/android/binder_trace.h b/drivers/android/binder_trace.h index 588eb3ec3507..7d8beb77f532 100644 --- a/drivers/android/binder_trace.h +++ b/drivers/android/binder_trace.h @@ -85,6 +85,30 @@ DEFINE_BINDER_FUNCTION_RETURN_EVENT(binder_ioctl_done); DEFINE_BINDER_FUNCTION_RETURN_EVENT(binder_write_done); DEFINE_BINDER_FUNCTION_RETURN_EVENT(binder_read_done); +TRACE_EVENT(binder_set_priority, + TP_PROTO(int proc, int thread, unsigned int old_prio, + unsigned int desired_prio, unsigned int new_prio), + TP_ARGS(proc, thread, old_prio, new_prio, desired_prio), + + TP_STRUCT__entry( + __field(int, proc) + __field(int, thread) + __field(unsigned int, old_prio) + __field(unsigned int, new_prio) + __field(unsigned int, desired_prio) + ), + TP_fast_assign( + __entry->proc = proc; + __entry->thread = thread; + __entry->old_prio = old_prio; + __entry->new_prio = new_prio; + __entry->desired_prio = desired_prio; + ), + TP_printk("proc=%d thread=%d old=%d => new=%d desired=%d", + __entry->proc, __entry->thread, __entry->old_prio, + __entry->new_prio, __entry->desired_prio) +); + TRACE_EVENT(binder_wait_for_work, TP_PROTO(bool proc_work, bool transaction_stack, bool thread_todo), TP_ARGS(proc_work, transaction_stack, thread_todo), From dab9bb26b8ffd23888dcfaf64b79494673a4b62c Mon Sep 17 00:00:00 2001 From: Ganesh Mahendran Date: Wed, 27 Sep 2017 15:12:25 +0800 Subject: [PATCH 0117/1103] ANDROID: binder: init desired_prio.sched_policy before use it In function binder_transaction_priority(), we access desired_prio before initialzing it. This patch fix this. Change-Id: I9d14d50f9a128010476a65b52631630899a44633 Signed-off-by: Ganesh Mahendran --- drivers/android/binder.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/drivers/android/binder.c b/drivers/android/binder.c index 67746f6ce6fb..95ad121fefc9 100644 --- a/drivers/android/binder.c +++ b/drivers/android/binder.c @@ -1244,7 +1244,7 @@ static void binder_transaction_priority(struct task_struct *task, struct binder_priority node_prio, bool inherit_rt) { - struct binder_priority desired_prio; + struct binder_priority desired_prio = t->priority; if (t->set_priority_called) return; @@ -1256,9 +1256,6 @@ static void binder_transaction_priority(struct task_struct *task, if (!inherit_rt && is_rt_policy(desired_prio.sched_policy)) { desired_prio.prio = NICE_TO_PRIO(0); desired_prio.sched_policy = SCHED_NORMAL; - } else { - desired_prio.prio = t->priority.prio; - desired_prio.sched_policy = t->priority.sched_policy; } if (node_prio.prio < t->priority.prio || From 37d6bd82a264c4fd1588c68c3fae26dab6a2a570 Mon Sep 17 00:00:00 2001 From: Ganesh Mahendran Date: Tue, 26 Sep 2017 17:56:25 +0800 Subject: [PATCH 0118/1103] ANDROID: binder: fix node sched policy calculation We should use FLAT_BINDER_FLAG_SCHED_POLICY_MASK as the mask to calculate sched policy. Change-Id: Ic252fd7c68495830690130d792802c02f99fc8fc Signed-off-by: Ganesh Mahendran --- drivers/android/binder.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/android/binder.c b/drivers/android/binder.c index 95ad121fefc9..0d475a17089b 100644 --- a/drivers/android/binder.c +++ b/drivers/android/binder.c @@ -1358,7 +1358,7 @@ static struct binder_node *binder_init_node_ilocked( node->cookie = cookie; node->work.type = BINDER_WORK_NODE; priority = flags & FLAT_BINDER_FLAG_PRIORITY_MASK; - node->sched_policy = (flags & FLAT_BINDER_FLAG_PRIORITY_MASK) >> + node->sched_policy = (flags & FLAT_BINDER_FLAG_SCHED_POLICY_MASK) >> FLAT_BINDER_FLAG_SCHED_POLICY_SHIFT; node->min_priority = to_kernel_prio(node->sched_policy, priority); node->accept_fds = !!(flags & FLAT_BINDER_FLAG_ACCEPTS_FDS); From 4bd672a95e53bc304a8780a55afe7bb63e17ad85 Mon Sep 17 00:00:00 2001 From: Colin Cross Date: Mon, 19 Sep 2011 16:42:44 -0700 Subject: [PATCH 0119/1103] ANDROID: cpuidle: governor: menu: don't use loadavg get_loadavg doesn't work as intended. According to the comments, it should be returning an average over a few seconds, but it is actually reading the instantaneous load. It is almost always returning 0, but can sometimes, depending on workload, spike very high into the hundreds even when the average cpu load is under 10%. Disable it for now. Change-Id: I63ed100af1cf9463549939b8113ed83676db5f86 Signed-off-by: Colin Cross --- drivers/cpuidle/governors/menu.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/cpuidle/governors/menu.c b/drivers/cpuidle/governors/menu.c index e26a40971b26..f9374dd071be 100644 --- a/drivers/cpuidle/governors/menu.c +++ b/drivers/cpuidle/governors/menu.c @@ -179,7 +179,12 @@ static inline int performance_multiplier(unsigned long nr_iowaiters, unsigned lo /* for higher loadavg, we are more reluctant */ - mult += 2 * get_loadavg(load); + /* + * this doesn't work as intended - it is almost always 0, but can + * sometimes, depending on workload, spike very high into the hundreds + * even when the average cpu load is under 10%. + */ + /* mult += 2 * get_loadavg(); */ /* for IO wait tasks (per cpu!) we add 5x each */ mult += 10 * nr_iowaiters; From a6fa087cf7203158a00241c7ae514a5c2494a844 Mon Sep 17 00:00:00 2001 From: Thierry Strudel Date: Tue, 14 Jun 2016 17:46:44 -0700 Subject: [PATCH 0120/1103] ANDROID: cpu: send KOBJ_ONLINE event when enabling cpus In case some sysfs nodes needs to be labeled with a different label than sysfs then user needs to be notified when a core is brought back online. Signed-off-by: Thierry Strudel Bug: 29359497 Change-Id: I0395c86e01cd49c348fda8f93087d26f88557c91 --- kernel/cpu.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/kernel/cpu.c b/kernel/cpu.c index 0097acec1c71..02a45dd96835 100644 --- a/kernel/cpu.c +++ b/kernel/cpu.c @@ -1211,6 +1211,7 @@ void __weak arch_enable_nonboot_cpus_end(void) void enable_nonboot_cpus(void) { int cpu, error; + struct device *cpu_device; /* Allow everyone to use the CPU hotplug again */ cpu_maps_update_begin(); @@ -1228,6 +1229,12 @@ void enable_nonboot_cpus(void) trace_suspend_resume(TPS("CPU_ON"), cpu, false); if (!error) { pr_info("CPU%d is up\n", cpu); + cpu_device = get_cpu_device(cpu); + if (!cpu_device) + pr_err("%s: failed to get cpu%d device\n", + __func__, cpu); + else + kobject_uevent(&cpu_device->kobj, KOBJ_ONLINE); continue; } pr_warn("Error taking CPU%d up: %d\n", cpu, error); From adcdcfe9794852221e2813041903a26e36a60819 Mon Sep 17 00:00:00 2001 From: Riley Andrews Date: Tue, 6 Sep 2016 15:16:25 -0700 Subject: [PATCH 0121/1103] ANDROID: cpuset: Make cpusets restore on hotplug This deliberately changes the behavior of the per-cpuset cpus file to not be effected by hotplug. When a cpu is offlined, it will be removed from the cpuset/cpus file. When a cpu is onlined, if the cpuset originally requested that that cpu was part of the cpuset, that cpu will be restored to the cpuset. The cpus files still have to be hierachical, but the ranges no longer have to be out of the currently online cpus, just the physically present cpus. Change-Id: I22cdf33e7d312117bcefba1aeb0125e1ada289a9 Signed-off-by: Dmitry Shmidt [AmitP: Refactored original changes to align with upstream commit 201af4c0fab0 ("cgroup: move cgroup files under kernel/cgroup/")] Signed-off-by: Amit Pundir --- kernel/cgroup/cpuset.c | 32 ++++++++++++++++++++++---------- 1 file changed, 22 insertions(+), 10 deletions(-) diff --git a/kernel/cgroup/cpuset.c b/kernel/cgroup/cpuset.c index 266f10cb7222..beccc2633cf4 100644 --- a/kernel/cgroup/cpuset.c +++ b/kernel/cgroup/cpuset.c @@ -103,6 +103,7 @@ struct cpuset { /* user-configured CPUs and Memory Nodes allow to tasks */ cpumask_var_t cpus_allowed; + cpumask_var_t cpus_requested; nodemask_t mems_allowed; /* effective CPUs and Memory Nodes allow to tasks */ @@ -412,7 +413,7 @@ static void cpuset_update_task_spread_flag(struct cpuset *cs, static int is_cpuset_subset(const struct cpuset *p, const struct cpuset *q) { - return cpumask_subset(p->cpus_allowed, q->cpus_allowed) && + return cpumask_subset(p->cpus_requested, q->cpus_requested) && nodes_subset(p->mems_allowed, q->mems_allowed) && is_cpu_exclusive(p) <= is_cpu_exclusive(q) && is_mem_exclusive(p) <= is_mem_exclusive(q); @@ -511,7 +512,7 @@ static int validate_change(struct cpuset *cur, struct cpuset *trial) cpuset_for_each_child(c, css, par) { if ((is_cpu_exclusive(trial) || is_cpu_exclusive(c)) && c != cur && - cpumask_intersects(trial->cpus_allowed, c->cpus_allowed)) + cpumask_intersects(trial->cpus_requested, c->cpus_requested)) goto out; if ((is_mem_exclusive(trial) || is_mem_exclusive(c)) && c != cur && @@ -972,17 +973,18 @@ static int update_cpumask(struct cpuset *cs, struct cpuset *trialcs, if (!*buf) { cpumask_clear(trialcs->cpus_allowed); } else { - retval = cpulist_parse(buf, trialcs->cpus_allowed); + retval = cpulist_parse(buf, trialcs->cpus_requested); if (retval < 0) return retval; - if (!cpumask_subset(trialcs->cpus_allowed, - top_cpuset.cpus_allowed)) + if (!cpumask_subset(trialcs->cpus_requested, cpu_present_mask)) return -EINVAL; + + cpumask_and(trialcs->cpus_allowed, trialcs->cpus_requested, cpu_active_mask); } /* Nothing to do if the cpus didn't change */ - if (cpumask_equal(cs->cpus_allowed, trialcs->cpus_allowed)) + if (cpumask_equal(cs->cpus_requested, trialcs->cpus_requested)) return 0; retval = validate_change(cs, trialcs); @@ -991,6 +993,7 @@ static int update_cpumask(struct cpuset *cs, struct cpuset *trialcs, spin_lock_irq(&callback_lock); cpumask_copy(cs->cpus_allowed, trialcs->cpus_allowed); + cpumask_copy(cs->cpus_requested, trialcs->cpus_requested); spin_unlock_irq(&callback_lock); /* use trialcs->cpus_allowed as a temp variable */ @@ -1759,7 +1762,7 @@ static int cpuset_common_seq_show(struct seq_file *sf, void *v) switch (type) { case FILE_CPULIST: - seq_printf(sf, "%*pbl\n", cpumask_pr_args(cs->cpus_allowed)); + seq_printf(sf, "%*pbl\n", cpumask_pr_args(cs->cpus_requested)); break; case FILE_MEMLIST: seq_printf(sf, "%*pbl\n", nodemask_pr_args(&cs->mems_allowed)); @@ -1949,11 +1952,14 @@ cpuset_css_alloc(struct cgroup_subsys_state *parent_css) return ERR_PTR(-ENOMEM); if (!alloc_cpumask_var(&cs->cpus_allowed, GFP_KERNEL)) goto free_cs; + if (!alloc_cpumask_var(&cs->cpus_requested, GFP_KERNEL)) + goto free_allowed; if (!alloc_cpumask_var(&cs->effective_cpus, GFP_KERNEL)) - goto free_cpus; + goto free_requested; set_bit(CS_SCHED_LOAD_BALANCE, &cs->flags); cpumask_clear(cs->cpus_allowed); + cpumask_clear(cs->cpus_requested); nodes_clear(cs->mems_allowed); cpumask_clear(cs->effective_cpus); nodes_clear(cs->effective_mems); @@ -1962,7 +1968,9 @@ cpuset_css_alloc(struct cgroup_subsys_state *parent_css) return &cs->css; -free_cpus: +free_requested: + free_cpumask_var(cs->cpus_requested); +free_allowed: free_cpumask_var(cs->cpus_allowed); free_cs: kfree(cs); @@ -2025,6 +2033,7 @@ static int cpuset_css_online(struct cgroup_subsys_state *css) cs->mems_allowed = parent->mems_allowed; cs->effective_mems = parent->mems_allowed; cpumask_copy(cs->cpus_allowed, parent->cpus_allowed); + cpumask_copy(cs->cpus_requested, parent->cpus_requested); cpumask_copy(cs->effective_cpus, parent->cpus_allowed); spin_unlock_irq(&callback_lock); out_unlock: @@ -2059,6 +2068,7 @@ static void cpuset_css_free(struct cgroup_subsys_state *css) free_cpumask_var(cs->effective_cpus); free_cpumask_var(cs->cpus_allowed); + free_cpumask_var(cs->cpus_requested); kfree(cs); } @@ -2121,8 +2131,10 @@ int __init cpuset_init(void) BUG_ON(!alloc_cpumask_var(&top_cpuset.cpus_allowed, GFP_KERNEL)); BUG_ON(!alloc_cpumask_var(&top_cpuset.effective_cpus, GFP_KERNEL)); + BUG_ON(!alloc_cpumask_var(&top_cpuset.cpus_requested, GFP_KERNEL)); cpumask_setall(top_cpuset.cpus_allowed); + cpumask_setall(top_cpuset.cpus_requested); nodes_setall(top_cpuset.mems_allowed); cpumask_setall(top_cpuset.effective_cpus); nodes_setall(top_cpuset.effective_mems); @@ -2255,7 +2267,7 @@ static void cpuset_hotplug_update_tasks(struct cpuset *cs) goto retry; } - cpumask_and(&new_cpus, cs->cpus_allowed, parent_cs(cs)->effective_cpus); + cpumask_and(&new_cpus, cs->cpus_requested, parent_cs(cs)->effective_cpus); nodes_and(new_mems, cs->mems_allowed, parent_cs(cs)->effective_mems); cpus_updated = !cpumask_equal(&new_cpus, cs->effective_cpus); From 76aaa2e8e95bfc9400a44159717badcc8fa5baca Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Thu, 6 Oct 2016 16:14:16 -0700 Subject: [PATCH 0122/1103] CHROMIUM: cgroups: relax permissions on moving tasks between cgroups Android expects system_server to be able to move tasks between different cgroups/cpusets, but does not want to be running as root. Let's relax permission check so that processes can move other tasks if they have CAP_SYS_NICE in the affected task's user namespace. BUG=b:31790445,chromium:647994 TEST=Boot android container, examine logcat Change-Id: Ia919c66ab6ed6a6daf7c4cf67feb38b13b1ad09b Signed-off-by: Dmitry Torokhov Reviewed-on: https://chromium-review.googlesource.com/394927 Reviewed-by: Ricky Zhou [AmitP: Refactored original changes to align with upstream commit 201af4c0fab0 ("cgroup: move cgroup files under kernel/cgroup/")] Signed-off-by: Amit Pundir --- kernel/cgroup/cgroup-v1.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/kernel/cgroup/cgroup-v1.c b/kernel/cgroup/cgroup-v1.c index 51063e7a93c2..0a39b26d6e4d 100644 --- a/kernel/cgroup/cgroup-v1.c +++ b/kernel/cgroup/cgroup-v1.c @@ -541,7 +541,8 @@ static ssize_t __cgroup1_procs_write(struct kernfs_open_file *of, tcred = get_task_cred(task); if (!uid_eq(cred->euid, GLOBAL_ROOT_UID) && !uid_eq(cred->euid, tcred->uid) && - !uid_eq(cred->euid, tcred->suid)) + !uid_eq(cred->euid, tcred->suid) && + !ns_capable(tcred->user_ns, CAP_SYS_NICE)) ret = -EACCES; put_cred(tcred); if (ret) From c88730c40a86bed19bcc3c0c17c8f6d053a2b71a Mon Sep 17 00:00:00 2001 From: Sami Tolvanen Date: Wed, 30 Mar 2016 14:10:13 -0700 Subject: [PATCH 0123/1103] ANDROID: dm: verity-fec: add sysfs attribute fec/corrected Add a sysfs entry that allows user space to determine whether dm-verity has come across correctable errors on the underlying block device. Bug: 22655252 Bug: 27928374 Change-Id: I80547a2aa944af2fb9ffde002650482877ade31b Signed-off-by: Sami Tolvanen (cherry picked from commit 7911fad5f0a2cf5afc2215657219a21e6630e001) [AmitP: Folded following android-4.9 commit changes into this patch 3278f53e4658 ("ANDROID: dm verity fec: add missing release from fec_ktype")] Signed-off-by: Amit Pundir --- drivers/md/dm-verity-fec.c | 46 +++++++++++++++++++++++++++++++++++++- drivers/md/dm-verity-fec.h | 4 ++++ 2 files changed, 49 insertions(+), 1 deletion(-) diff --git a/drivers/md/dm-verity-fec.c b/drivers/md/dm-verity-fec.c index 684af08d0747..8306ee0b2d0c 100644 --- a/drivers/md/dm-verity-fec.c +++ b/drivers/md/dm-verity-fec.c @@ -11,6 +11,7 @@ #include "dm-verity-fec.h" #include +#include #define DM_MSG_PREFIX "verity-fec" @@ -175,9 +176,11 @@ static int fec_decode_bufs(struct dm_verity *v, struct dm_verity_fec_io *fio, if (r < 0 && neras) DMERR_LIMIT("%s: FEC %llu: failed to correct: %d", v->data_dev->name, (unsigned long long)rsb, r); - else if (r > 0) + else if (r > 0) { DMWARN_LIMIT("%s: FEC %llu: corrected %d errors", v->data_dev->name, (unsigned long long)rsb, r); + atomic_add_unless(&v->fec->corrected, 1, INT_MAX); + } return r; } @@ -545,6 +548,7 @@ unsigned verity_fec_status_table(struct dm_verity *v, unsigned sz, void verity_fec_dtr(struct dm_verity *v) { struct dm_verity_fec *f = v->fec; + struct kobject *kobj = &f->kobj_holder.kobj; if (!verity_fec_is_enabled(v)) goto out; @@ -561,6 +565,12 @@ void verity_fec_dtr(struct dm_verity *v) if (f->dev) dm_put_device(v->ti, f->dev); + + if (kobj->state_initialized) { + kobject_put(kobj); + wait_for_completion(dm_get_completion_from_kobject(kobj)); + } + out: kfree(f); v->fec = NULL; @@ -649,6 +659,28 @@ int verity_fec_parse_opt_args(struct dm_arg_set *as, struct dm_verity *v, return 0; } +static ssize_t corrected_show(struct kobject *kobj, struct kobj_attribute *attr, + char *buf) +{ + struct dm_verity_fec *f = container_of(kobj, struct dm_verity_fec, + kobj_holder.kobj); + + return sprintf(buf, "%d\n", atomic_read(&f->corrected)); +} + +static struct kobj_attribute attr_corrected = __ATTR_RO(corrected); + +static struct attribute *fec_attrs[] = { + &attr_corrected.attr, + NULL +}; + +static struct kobj_type fec_ktype = { + .sysfs_ops = &kobj_sysfs_ops, + .default_attrs = fec_attrs, + .release = dm_kobject_release +}; + /* * Allocate dm_verity_fec for v->fec. Must be called before verity_fec_ctr. */ @@ -672,8 +704,10 @@ int verity_fec_ctr_alloc(struct dm_verity *v) */ int verity_fec_ctr(struct dm_verity *v) { + int r; struct dm_verity_fec *f = v->fec; struct dm_target *ti = v->ti; + struct mapped_device *md = dm_table_get_md(ti->table); u64 hash_blocks; int ret; @@ -682,6 +716,16 @@ int verity_fec_ctr(struct dm_verity *v) return 0; } + /* Create a kobject and sysfs attributes */ + init_completion(&f->kobj_holder.completion); + + r = kobject_init_and_add(&f->kobj_holder.kobj, &fec_ktype, + &disk_to_dev(dm_disk(md))->kobj, "%s", "fec"); + if (r) { + ti->error = "Cannot create kobject"; + return r; + } + /* * FEC is computed over data blocks, possible metadata, and * hash blocks. In other words, FEC covers total of fec_blocks diff --git a/drivers/md/dm-verity-fec.h b/drivers/md/dm-verity-fec.h index 6ad803b2b36c..93af41777b4f 100644 --- a/drivers/md/dm-verity-fec.h +++ b/drivers/md/dm-verity-fec.h @@ -12,6 +12,8 @@ #ifndef DM_VERITY_FEC_H #define DM_VERITY_FEC_H +#include "dm.h" +#include "dm-core.h" #include "dm-verity.h" #include @@ -51,6 +53,8 @@ struct dm_verity_fec { mempool_t extra_pool; /* mempool for extra buffers */ mempool_t output_pool; /* mempool for output */ struct kmem_cache *cache; /* cache for buffers */ + atomic_t corrected; /* corrected errors */ + struct dm_kobject_holder kobj_holder; /* for sysfs attributes */ }; /* per-bio data */ From ac33b169eaee4b2b6a12a20c68e3290d94d9c0a9 Mon Sep 17 00:00:00 2001 From: Will Drewry Date: Wed, 9 Jun 2010 17:47:38 -0500 Subject: [PATCH 0124/1103] CHROMIUM: dm: boot time specification of dm= This is a wrap-up of three patches pending upstream approval. I'm bundling them because they are interdependent, and it'll be easier to drop it on rebase later. 1. dm: allow a dm-fs-style device to be shared via dm-ioctl Integrates feedback from Alisdair, Mike, and Kiyoshi. Two main changes occur here: - One function is added which allows for a programmatically created mapped device to be inserted into the dm-ioctl hash table. This binds the device to a name and, optional, uuid which is needed by udev and allows for userspace management of the mapped device. - dm_table_complete() was extended to handle all of the final functional changes required for the table to be operational once called. 2. init: boot to device-mapper targets without an initr* Add a dm= kernel parameter modeled after the md= parameter from do_mounts_md. It allows for device-mapper targets to be configured at boot time for use early in the boot process (as the root device or otherwise). It also replaces /dev/XXX calls with major:minor opportunistically. The format is dm="name uuid ro,table line 1,table line 2,...". The parser expects the comma to be safe to use as a newline substitute but, otherwise, uses the normal separator of space. Some attempt has been made to make it forgiving of additional spaces (using skip_spaces()). A mapped device created during boot will be assigned a minor of 0 and may be access via /dev/dm-0. An example dm-linear root with no uuid may look like: root=/dev/dm-0 dm="lroot none ro, 0 4096 linear /dev/ubdb 0, 4096 4096 linear /dv/ubdc 0" Once udev is started, /dev/dm-0 will become /dev/mapper/lroot. Older upstream threads: http://marc.info/?l=dm-devel&m=127429492521964&w=2 http://marc.info/?l=dm-devel&m=127429499422096&w=2 http://marc.info/?l=dm-devel&m=127429493922000&w=2 Latest upstream threads: https://patchwork.kernel.org/patch/104859/ https://patchwork.kernel.org/patch/104860/ https://patchwork.kernel.org/patch/104861/ Bug: 27175947 Signed-off-by: Will Drewry Review URL: http://codereview.chromium.org/2020011 Change-Id: I92bd53432a11241228d2e5ac89a3b20d19b05a31 [AmitP: Refactored the original changes based on upstream changes, commit e52347bd66f6 ("Documentation/admin-guide: split the kernel parameter list to a separate file")] Signed-off-by: Amit Pundir --- .../admin-guide/kernel-parameters.txt | 3 + Documentation/device-mapper/boot.txt | 42 ++ drivers/md/dm-ioctl.c | 39 ++ drivers/md/dm-table.c | 1 + include/linux/device-mapper.h | 6 + init/Makefile | 1 + init/do_mounts.c | 1 + init/do_mounts.h | 10 + init/do_mounts_dm.c | 410 ++++++++++++++++++ 9 files changed, 513 insertions(+) create mode 100644 Documentation/device-mapper/boot.txt create mode 100644 init/do_mounts_dm.c diff --git a/Documentation/admin-guide/kernel-parameters.txt b/Documentation/admin-guide/kernel-parameters.txt index 92eb1f42240d..331195d19b32 100644 --- a/Documentation/admin-guide/kernel-parameters.txt +++ b/Documentation/admin-guide/kernel-parameters.txt @@ -880,6 +880,9 @@ dis_ucode_ldr [X86] Disable the microcode loader. + dm= [DM] Allows early creation of a device-mapper device. + See Documentation/device-mapper/boot.txt. + dma_debug=off If the kernel is compiled with DMA_API_DEBUG support, this option disables the debugging code at boot. diff --git a/Documentation/device-mapper/boot.txt b/Documentation/device-mapper/boot.txt new file mode 100644 index 000000000000..adcaad5e5e32 --- /dev/null +++ b/Documentation/device-mapper/boot.txt @@ -0,0 +1,42 @@ +Boot time creation of mapped devices +=================================== + +It is possible to configure a device mapper device to act as the root +device for your system in two ways. + +The first is to build an initial ramdisk which boots to a minimal +userspace which configures the device, then pivot_root(8) in to it. + +For simple device mapper configurations, it is possible to boot directly +using the following kernel command line: + +dm=" ,table line 1,...,table line n" + +name = the name to associate with the device + after boot, udev, if used, will use that name to label + the device node. +uuid = may be 'none' or the UUID desired for the device. +ro = may be "ro" or "rw". If "ro", the device and device table will be + marked read-only. + +Each table line may be as normal when using the dmsetup tool except for +two variations: +1. Any use of commas will be interpreted as a newline +2. Quotation marks cannot be escaped and cannot be used without + terminating the dm= argument. + +Unless renamed by udev, the device node created will be dm-0 as the +first minor number for the device-mapper is used during early creation. + +Example +======= + +- Booting to a linear array made up of user-mode linux block devices: + + dm="lroot none 0, 0 4096 linear 98:16 0, 4096 4096 linear 98:32 0" \ + root=/dev/dm-0 + +Will boot to a rw dm-linear target of 8192 sectors split across two +block devices identified by their major:minor numbers. After boot, udev +will rename this target to /dev/mapper/lroot (depending on the rules). +No uuid was assigned. diff --git a/drivers/md/dm-ioctl.c b/drivers/md/dm-ioctl.c index b810ea77e6b1..350aa7a7a591 100644 --- a/drivers/md/dm-ioctl.c +++ b/drivers/md/dm-ioctl.c @@ -1993,6 +1993,45 @@ void dm_interface_exit(void) dm_hash_exit(); } + +/** + * dm_ioctl_export - Permanently export a mapped device via the ioctl interface + * @md: Pointer to mapped_device + * @name: Buffer (size DM_NAME_LEN) for name + * @uuid: Buffer (size DM_UUID_LEN) for uuid or NULL if not desired + */ +int dm_ioctl_export(struct mapped_device *md, const char *name, + const char *uuid) +{ + int r = 0; + struct hash_cell *hc; + + if (!md) { + r = -ENXIO; + goto out; + } + + /* The name and uuid can only be set once. */ + mutex_lock(&dm_hash_cells_mutex); + hc = dm_get_mdptr(md); + mutex_unlock(&dm_hash_cells_mutex); + if (hc) { + DMERR("%s: already exported", dm_device_name(md)); + r = -ENXIO; + goto out; + } + + r = dm_hash_insert(name, uuid, md); + if (r) { + DMERR("%s: could not bind to '%s'", dm_device_name(md), name); + goto out; + } + + /* Let udev know we've changed. */ + dm_kobject_uevent(md, KOBJ_CHANGE, dm_get_event_nr(md)); +out: + return r; +} /** * dm_copy_name_and_uuid - Copy mapped device name & uuid into supplied buffers * @md: Pointer to mapped_device diff --git a/drivers/md/dm-table.c b/drivers/md/dm-table.c index 3d0e2c198f06..485626d5b9d7 100644 --- a/drivers/md/dm-table.c +++ b/drivers/md/dm-table.c @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include diff --git a/include/linux/device-mapper.h b/include/linux/device-mapper.h index 6fb0808e87c8..8d1ec55b43d1 100644 --- a/include/linux/device-mapper.h +++ b/include/linux/device-mapper.h @@ -397,6 +397,12 @@ void dm_put(struct mapped_device *md); void dm_set_mdptr(struct mapped_device *md, void *ptr); void *dm_get_mdptr(struct mapped_device *md); +/* + * Export the device via the ioctl interface (uses mdptr). + */ +int dm_ioctl_export(struct mapped_device *md, const char *name, + const char *uuid); + /* * A device can still be used while suspended, but I/O is deferred. */ diff --git a/init/Makefile b/init/Makefile index a3e5ce2bcf08..f814f0ff5974 100644 --- a/init/Makefile +++ b/init/Makefile @@ -19,6 +19,7 @@ mounts-y := do_mounts.o mounts-$(CONFIG_BLK_DEV_RAM) += do_mounts_rd.o mounts-$(CONFIG_BLK_DEV_INITRD) += do_mounts_initrd.o mounts-$(CONFIG_BLK_DEV_MD) += do_mounts_md.o +mounts-$(CONFIG_BLK_DEV_DM) += do_mounts_dm.o # dependencies on generated files need to be listed explicitly $(obj)/version.o: include/generated/compile.h diff --git a/init/do_mounts.c b/init/do_mounts.c index e1c9afa9d8c9..d707f12be6e7 100644 --- a/init/do_mounts.c +++ b/init/do_mounts.c @@ -555,6 +555,7 @@ void __init prepare_namespace(void) wait_for_device_probe(); md_run_setup(); + dm_run_setup(); if (saved_root_name[0]) { root_device_name = saved_root_name; diff --git a/init/do_mounts.h b/init/do_mounts.h index 0bb0806de4ce..0f57528ea324 100644 --- a/init/do_mounts.h +++ b/init/do_mounts.h @@ -61,3 +61,13 @@ void md_run_setup(void); static inline void md_run_setup(void) {} #endif + +#ifdef CONFIG_BLK_DEV_DM + +void dm_run_setup(void); + +#else + +static inline void dm_run_setup(void) {} + +#endif diff --git a/init/do_mounts_dm.c b/init/do_mounts_dm.c new file mode 100644 index 000000000000..0fd3411533f3 --- /dev/null +++ b/init/do_mounts_dm.c @@ -0,0 +1,410 @@ +/* do_mounts_dm.c + * Copyright (C) 2010 The Chromium OS Authors + * All Rights Reserved. + * Based on do_mounts_md.c + * + * This file is released under the GPL. + */ +#include +#include +#include + +#include "do_mounts.h" + +#define DM_MAX_NAME 32 +#define DM_MAX_UUID 129 +#define DM_NO_UUID "none" + +#define DM_MSG_PREFIX "init" + +/* Separators used for parsing the dm= argument. */ +#define DM_FIELD_SEP ' ' +#define DM_LINE_SEP ',' + +/* + * When the device-mapper and any targets are compiled into the kernel + * (not a module), one target may be created and used as the root device at + * boot time with the parameters given with the boot line dm=... + * The code for that is here. + */ + +struct dm_setup_target { + sector_t begin; + sector_t length; + char *type; + char *params; + /* simple singly linked list */ + struct dm_setup_target *next; +}; + +static struct { + int minor; + int ro; + char name[DM_MAX_NAME]; + char uuid[DM_MAX_UUID]; + char *targets; + struct dm_setup_target *target; + int target_count; +} dm_setup_args __initdata; + +static __initdata int dm_early_setup; + +static size_t __init get_dm_option(char *str, char **next, char sep) +{ + size_t len = 0; + char *endp = NULL; + + if (!str) + return 0; + + endp = strchr(str, sep); + if (!endp) { /* act like strchrnul */ + len = strlen(str); + endp = str + len; + } else { + len = endp - str; + } + + if (endp == str) + return 0; + + if (!next) + return len; + + if (*endp == 0) { + /* Don't advance past the nul. */ + *next = endp; + } else { + *next = endp + 1; + } + return len; +} + +static int __init dm_setup_args_init(void) +{ + dm_setup_args.minor = 0; + dm_setup_args.ro = 0; + dm_setup_args.target = NULL; + dm_setup_args.target_count = 0; + return 0; +} + +static int __init dm_setup_cleanup(void) +{ + struct dm_setup_target *target = dm_setup_args.target; + struct dm_setup_target *old_target = NULL; + while (target) { + kfree(target->type); + kfree(target->params); + old_target = target; + target = target->next; + kfree(old_target); + dm_setup_args.target_count--; + } + BUG_ON(dm_setup_args.target_count); + return 0; +} + +static char * __init dm_setup_parse_device_args(char *str) +{ + char *next = NULL; + size_t len = 0; + + /* Grab the logical name of the device to be exported to udev */ + len = get_dm_option(str, &next, DM_FIELD_SEP); + if (!len) { + DMERR("failed to parse device name"); + goto parse_fail; + } + len = min(len + 1, sizeof(dm_setup_args.name)); + strlcpy(dm_setup_args.name, str, len); /* includes nul */ + str = skip_spaces(next); + + /* Grab the UUID value or "none" */ + len = get_dm_option(str, &next, DM_FIELD_SEP); + if (!len) { + DMERR("failed to parse device uuid"); + goto parse_fail; + } + len = min(len + 1, sizeof(dm_setup_args.uuid)); + strlcpy(dm_setup_args.uuid, str, len); + str = skip_spaces(next); + + /* Determine if the table/device will be read only or read-write */ + if (!strncmp("ro,", str, 3)) { + dm_setup_args.ro = 1; + } else if (!strncmp("rw,", str, 3)) { + dm_setup_args.ro = 0; + } else { + DMERR("failed to parse table mode"); + goto parse_fail; + } + str = skip_spaces(str + 3); + + return str; + +parse_fail: + return NULL; +} + +static void __init dm_substitute_devices(char *str, size_t str_len) +{ + char *candidate = str; + char *candidate_end = str; + char old_char; + size_t len = 0; + dev_t dev; + + if (str_len < 3) + return; + + while (str && *str) { + candidate = strchr(str, '/'); + if (!candidate) + break; + + /* Avoid embedded slashes */ + if (candidate != str && *(candidate - 1) != DM_FIELD_SEP) { + str = strchr(candidate, DM_FIELD_SEP); + continue; + } + + len = get_dm_option(candidate, &candidate_end, DM_FIELD_SEP); + str = skip_spaces(candidate_end); + if (len < 3 || len > 37) /* name_to_dev_t max; maj:mix min */ + continue; + + /* Temporarily terminate with a nul */ + candidate_end--; + old_char = *candidate_end; + *candidate_end = '\0'; + + DMDEBUG("converting candidate device '%s' to dev_t", candidate); + /* Use the boot-time specific device naming */ + dev = name_to_dev_t(candidate); + *candidate_end = old_char; + + DMDEBUG(" -> %u", dev); + /* No suitable replacement found */ + if (!dev) + continue; + + /* Rewrite the /dev/path as a major:minor */ + len = snprintf(candidate, len, "%u:%u", MAJOR(dev), MINOR(dev)); + if (!len) { + DMERR("error substituting device major/minor."); + break; + } + candidate += len; + /* Pad out with spaces (fixing our nul) */ + while (candidate < candidate_end) + *(candidate++) = DM_FIELD_SEP; + } +} + +static int __init dm_setup_parse_targets(char *str) +{ + char *next = NULL; + size_t len = 0; + struct dm_setup_target **target = NULL; + + /* Targets are defined as per the table format but with a + * comma as a newline separator. */ + target = &dm_setup_args.target; + while (str && *str) { + *target = kzalloc(sizeof(struct dm_setup_target), GFP_KERNEL); + if (!*target) { + DMERR("failed to allocate memory for target %d", + dm_setup_args.target_count); + goto parse_fail; + } + dm_setup_args.target_count++; + + (*target)->begin = simple_strtoull(str, &next, 10); + if (!next || *next != DM_FIELD_SEP) { + DMERR("failed to parse starting sector for target %d", + dm_setup_args.target_count - 1); + goto parse_fail; + } + str = skip_spaces(next + 1); + + (*target)->length = simple_strtoull(str, &next, 10); + if (!next || *next != DM_FIELD_SEP) { + DMERR("failed to parse length for target %d", + dm_setup_args.target_count - 1); + goto parse_fail; + } + str = skip_spaces(next + 1); + + len = get_dm_option(str, &next, DM_FIELD_SEP); + if (!len || + !((*target)->type = kstrndup(str, len, GFP_KERNEL))) { + DMERR("failed to parse type for target %d", + dm_setup_args.target_count - 1); + goto parse_fail; + } + str = skip_spaces(next); + + len = get_dm_option(str, &next, DM_LINE_SEP); + if (!len || + !((*target)->params = kstrndup(str, len, GFP_KERNEL))) { + DMERR("failed to parse params for target %d", + dm_setup_args.target_count - 1); + goto parse_fail; + } + str = skip_spaces(next); + + /* Before moving on, walk through the copied target and + * attempt to replace all /dev/xxx with the major:minor number. + * It may not be possible to resolve them traditionally at + * boot-time. */ + dm_substitute_devices((*target)->params, len); + + target = &((*target)->next); + } + DMDEBUG("parsed %d targets", dm_setup_args.target_count); + + return 0; + +parse_fail: + return 1; +} + +/* + * Parse the command-line parameters given our kernel, but do not + * actually try to invoke the DM device now; that is handled by + * dm_setup_drive after the low-level disk drivers have initialised. + * dm format is as follows: + * dm="name uuid fmode,[table line 1],[table line 2],..." + * May be used with root=/dev/dm-0 as it always uses the first dm minor. + */ + +static int __init dm_setup(char *str) +{ + dm_setup_args_init(); + + str = dm_setup_parse_device_args(str); + if (!str) { + DMDEBUG("str is NULL"); + goto parse_fail; + } + + /* Target parsing is delayed until we have dynamic memory */ + dm_setup_args.targets = str; + + printk(KERN_INFO "dm: will configure '%s' on dm-%d\n", + dm_setup_args.name, dm_setup_args.minor); + + dm_early_setup = 1; + return 1; + +parse_fail: + printk(KERN_WARNING "dm: Invalid arguments supplied to dm=.\n"); + return 0; +} + + +static void __init dm_setup_drive(void) +{ + struct mapped_device *md = NULL; + struct dm_table *table = NULL; + struct dm_setup_target *target; + char *uuid = dm_setup_args.uuid; + fmode_t fmode = FMODE_READ; + + /* Finish parsing the targets. */ + if (dm_setup_parse_targets(dm_setup_args.targets)) + goto parse_fail; + + if (dm_create(dm_setup_args.minor, &md)) { + DMDEBUG("failed to create the device"); + goto dm_create_fail; + } + DMDEBUG("created device '%s'", dm_device_name(md)); + + /* In addition to flagging the table below, the disk must be + * set explicitly ro/rw. */ + set_disk_ro(dm_disk(md), dm_setup_args.ro); + + if (!dm_setup_args.ro) + fmode |= FMODE_WRITE; + if (dm_table_create(&table, fmode, dm_setup_args.target_count, md)) { + DMDEBUG("failed to create the table"); + goto dm_table_create_fail; + } + + target = dm_setup_args.target; + while (target) { + DMINFO("adding target '%llu %llu %s %s'", + (unsigned long long) target->begin, + (unsigned long long) target->length, target->type, + target->params); + if (dm_table_add_target(table, target->type, target->begin, + target->length, target->params)) { + DMDEBUG("failed to add the target to the table"); + goto add_target_fail; + } + target = target->next; + } + + if (dm_table_complete(table)) { + DMDEBUG("failed to complete the table"); + goto table_complete_fail; + } + + /* Suspend the device so that we can bind it to the table. */ + if (dm_suspend(md, 0)) { + DMDEBUG("failed to suspend the device pre-bind"); + goto suspend_fail; + } + + /* Bind the table to the device. This is the only way to associate + * md->map with the table and set the disk capacity directly. */ + if (dm_swap_table(md, table)) { /* should return NULL. */ + DMDEBUG("failed to bind the device to the table"); + goto table_bind_fail; + } + + /* Finally, resume and the device should be ready. */ + if (dm_resume(md)) { + DMDEBUG("failed to resume the device"); + goto resume_fail; + } + + /* Export the dm device via the ioctl interface */ + if (!strcmp(DM_NO_UUID, dm_setup_args.uuid)) + uuid = NULL; + if (dm_ioctl_export(md, dm_setup_args.name, uuid)) { + DMDEBUG("failed to export device with given name and uuid"); + goto export_fail; + } + printk(KERN_INFO "dm: dm-%d is ready\n", dm_setup_args.minor); + + dm_setup_cleanup(); + return; + +export_fail: +resume_fail: +table_bind_fail: +suspend_fail: +table_complete_fail: +add_target_fail: + dm_table_put(table); +dm_table_create_fail: + dm_put(md); +dm_create_fail: + dm_setup_cleanup(); +parse_fail: + printk(KERN_WARNING "dm: starting dm-%d (%s) failed\n", + dm_setup_args.minor, dm_setup_args.name); +} + +__setup("dm=", dm_setup); + +void __init dm_run_setup(void) +{ + if (!dm_early_setup) + return; + printk(KERN_INFO "dm: attempting early device configuration.\n"); + dm_setup_drive(); +} From 6656744be2d4d93db3076d08426dbe169f3636e0 Mon Sep 17 00:00:00 2001 From: Badhri Jagan Sridharan Date: Mon, 8 Feb 2016 16:47:41 -0800 Subject: [PATCH 0125/1103] ANDROID: dm: do_mounts_dm: Rebase on top of 4.9 1. "dm: optimize use SRCU and RCU" removes the use of dm_table_put. 2. "dm: remove request-based logic from make_request_fn wrapper" necessitates calling dm_setup_md_queue or else the request_queue's make_request_fn pointer ends being unset. [ 7.711600] Internal error: Oops - bad mode: 0 [#1] PREEMPT SMP [ 7.717519] CPU: 1 PID: 1 Comm: swapper/0 Tainted: G W 4.1.15-02273-gb057d16-dirty #33 [ 7.726559] Hardware name: HiKey Development Board (DT) [ 7.731779] task: ffffffc005f8acc0 ti: ffffffc005f8c000 task.ti: ffffffc005f8c000 [ 7.739257] PC is at 0x0 [ 7.741787] LR is at generic_make_request+0x8c/0x108 .... [ 9.082931] Call trace: [ 9.085372] [< (null)>] (null) [ 9.090074] [] submit_bio+0x98/0x1e0 [ 9.095212] [] _submit_bh+0x120/0x1f0 [ 9.096165] cfg80211: Calling CRDA to update world regulatory domain [ 9.106781] [] __bread_gfp+0x94/0x114 [ 9.112004] [] ext4_fill_super+0x18c/0x2d64 [ 9.117750] [] mount_bdev+0x194/0x1c0 [ 9.122973] [] ext4_mount+0x14/0x1c [ 9.128021] [] mount_fs+0x3c/0x194 [ 9.132985] [] vfs_kern_mount+0x4c/0x134 [ 9.138467] [] do_mount+0x204/0xbbc [ 9.143514] [] SyS_mount+0x94/0xe8 [ 9.148479] [] mount_block_root+0x120/0x24c [ 9.154222] [] mount_root+0x110/0x12c [ 9.159443] [] prepare_namespace+0x170/0x1b8 [ 9.165273] [] kernel_init_freeable+0x23c/0x260 [ 9.171365] [] kernel_init+0x10/0x118 [ 9.176589] Code: bad PC value [ 9.179807] ---[ end trace 75e1bc52ba364d13 ]--- Bug: 27175947 Signed-off-by: Badhri Jagan Sridharan Change-Id: I952d86fd1475f0825f9be1386e3497b36127abd0 --- init/do_mounts_dm.c | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/init/do_mounts_dm.c b/init/do_mounts_dm.c index 0fd3411533f3..0fe9c5f7d5e9 100644 --- a/init/do_mounts_dm.c +++ b/init/do_mounts_dm.c @@ -10,6 +10,7 @@ #include #include "do_mounts.h" +#include "../drivers/md/dm.h" #define DM_MAX_NAME 32 #define DM_MAX_UUID 129 @@ -333,6 +334,7 @@ static void __init dm_setup_drive(void) goto dm_table_create_fail; } + dm_lock_md_type(md); target = dm_setup_args.target; while (target) { DMINFO("adding target '%llu %llu %s %s'", @@ -352,6 +354,17 @@ static void __init dm_setup_drive(void) goto table_complete_fail; } + if (dm_get_md_type(md) == DM_TYPE_NONE) { + dm_set_md_type(md, dm_table_get_type(table)); + if (dm_setup_md_queue(md, table)) { + DMWARN("unable to set up device queue for new table."); + goto setup_md_queue_fail; + } + } else if (dm_get_md_type(md) != dm_table_get_type(table)) { + DMWARN("can't change device type after initial table load."); + goto setup_md_queue_fail; + } + /* Suspend the device so that we can bind it to the table. */ if (dm_suspend(md, 0)) { DMDEBUG("failed to suspend the device pre-bind"); @@ -380,6 +393,7 @@ static void __init dm_setup_drive(void) } printk(KERN_INFO "dm: dm-%d is ready\n", dm_setup_args.minor); + dm_unlock_md_type(md); dm_setup_cleanup(); return; @@ -387,9 +401,10 @@ static void __init dm_setup_drive(void) resume_fail: table_bind_fail: suspend_fail: +setup_md_queue_fail: table_complete_fail: add_target_fail: - dm_table_put(table); + dm_unlock_md_type(md); dm_table_create_fail: dm_put(md); dm_create_fail: From 2c17b1404c311a588ad41fd8a2b7bf71a75fb4d7 Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Mon, 2 May 2016 17:29:28 +0200 Subject: [PATCH 0126/1103] ANDROID: dm: do_mounts_dm: fix dm_substitute_devices() When candidate is the last parameter, candidate_end points to the '\0' character and not the DM_FIELD_SEP character. In such a situation, we should not move the candidate_end pointer one character backward. Signed-off-by: Jeremy Compostella --- init/do_mounts_dm.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/init/do_mounts_dm.c b/init/do_mounts_dm.c index 0fe9c5f7d5e9..a557c5ee00a7 100644 --- a/init/do_mounts_dm.c +++ b/init/do_mounts_dm.c @@ -176,7 +176,8 @@ static void __init dm_substitute_devices(char *str, size_t str_len) continue; /* Temporarily terminate with a nul */ - candidate_end--; + if (*candidate_end) + candidate_end--; old_char = *candidate_end; *candidate_end = '\0'; From 4714cc906f1161790339572318289290e9e2e388 Mon Sep 17 00:00:00 2001 From: David Zeuthen Date: Fri, 19 May 2017 17:20:18 -0400 Subject: [PATCH 0127/1103] ANDROID: dm: do_mounts_dm: Update init/do_mounts_dm.c to the latest ChromiumOS version. This is needed for AVB. Bug: None Test: Compiles. Change-Id: I45b5d435652ab66ec07420ab17f2c7889f7e4d95 Signed-off-by: David Zeuthen --- drivers/md/dm.h | 2 - include/linux/device-mapper.h | 7 + init/do_mounts_dm.c | 556 ++++++++++++++++++---------------- 3 files changed, 307 insertions(+), 258 deletions(-) diff --git a/drivers/md/dm.h b/drivers/md/dm.h index 114a81b27c37..d8db76afa622 100644 --- a/drivers/md/dm.h +++ b/drivers/md/dm.h @@ -80,8 +80,6 @@ void dm_set_md_type(struct mapped_device *md, enum dm_queue_mode type); enum dm_queue_mode dm_get_md_type(struct mapped_device *md); struct target_type *dm_get_immutable_target_type(struct mapped_device *md); -int dm_setup_md_queue(struct mapped_device *md, struct dm_table *t); - /* * To check the return value from dm_table_find_target(). */ diff --git a/include/linux/device-mapper.h b/include/linux/device-mapper.h index 8d1ec55b43d1..b7b047709918 100644 --- a/include/linux/device-mapper.h +++ b/include/linux/device-mapper.h @@ -432,6 +432,13 @@ union map_info *dm_get_rq_mapinfo(struct request *rq); struct queue_limits *dm_get_queue_limits(struct mapped_device *md); +void dm_lock_md_type(struct mapped_device *md); +void dm_unlock_md_type(struct mapped_device *md); +void dm_set_md_type(struct mapped_device *md, unsigned type); +unsigned dm_get_md_type(struct mapped_device *md); +int dm_setup_md_queue(struct mapped_device *md, struct dm_table *t); +unsigned dm_table_get_type(struct dm_table *t); + /* * Geometry functions. */ diff --git a/init/do_mounts_dm.c b/init/do_mounts_dm.c index a557c5ee00a7..af84b01ccfbc 100644 --- a/init/do_mounts_dm.c +++ b/init/do_mounts_dm.c @@ -5,13 +5,17 @@ * * This file is released under the GPL. */ +#include +#include #include #include #include +#include #include "do_mounts.h" -#include "../drivers/md/dm.h" +#define DM_MAX_DEVICES 256 +#define DM_MAX_TARGETS 256 #define DM_MAX_NAME 32 #define DM_MAX_UUID 129 #define DM_NO_UUID "none" @@ -19,14 +23,47 @@ #define DM_MSG_PREFIX "init" /* Separators used for parsing the dm= argument. */ -#define DM_FIELD_SEP ' ' -#define DM_LINE_SEP ',' +#define DM_FIELD_SEP " " +#define DM_LINE_SEP "," +#define DM_ANY_SEP DM_FIELD_SEP DM_LINE_SEP /* * When the device-mapper and any targets are compiled into the kernel - * (not a module), one target may be created and used as the root device at - * boot time with the parameters given with the boot line dm=... - * The code for that is here. + * (not a module), one or more device-mappers may be created and used + * as the root device at boot time with the parameters given with the + * boot line dm=... + * + * Multiple device-mappers can be stacked specifing the number of + * devices. A device can have multiple targets if the the number of + * targets is specified. + * + * TODO(taysom:defect 32847) + * In the future, the field will be mandatory. + * + * ::= [] + + * ::= "," + + * ::= [] + * ::= "," + * ::= "ro" | "rw" + * ::= xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx | "none" + * ::= "verity" | "bootcache" | ... + * + * Example: + * 2 vboot none ro 1, + * 0 1768000 bootcache + * device=aa55b119-2a47-8c45-946a-5ac57765011f+1 + * signature=76e9be054b15884a9fa85973e9cb274c93afadb6 + * cache_start=1768000 max_blocks=100000 size_limit=23 max_trace=20000, + * vroot none ro 1, + * 0 1740800 verity payload=254:0 hashtree=254:0 hashstart=1740800 alg=sha1 + * root_hexdigest=76e9be054b15884a9fa85973e9cb274c93afadb6 + * salt=5b3549d54d6c7a3837b9b81ed72e49463a64c03680c47835bef94d768e5646fe + * + * Notes: + * 1. uuid is a label for the device and we set it to "none". + * 2. The field will be optional initially and assumed to be 1. + * Once all the scripts that set these fields have been set, it will + * be made mandatory. */ struct dm_setup_target { @@ -38,381 +75,388 @@ struct dm_setup_target { struct dm_setup_target *next; }; -static struct { +struct dm_device { int minor; int ro; char name[DM_MAX_NAME]; char uuid[DM_MAX_UUID]; - char *targets; + unsigned long num_targets; struct dm_setup_target *target; int target_count; + struct dm_device *next; +}; + +struct dm_option { + char *start; + char *next; + size_t len; + char delim; +}; + +static struct { + unsigned long num_devices; + char *str; } dm_setup_args __initdata; static __initdata int dm_early_setup; -static size_t __init get_dm_option(char *str, char **next, char sep) +static int __init get_dm_option(struct dm_option *opt, const char *accept) { - size_t len = 0; - char *endp = NULL; + char *str = opt->next; + char *endp; if (!str) return 0; - endp = strchr(str, sep); + str = skip_spaces(str); + opt->start = str; + endp = strpbrk(str, accept); if (!endp) { /* act like strchrnul */ - len = strlen(str); - endp = str + len; + opt->len = strlen(str); + endp = str + opt->len; } else { - len = endp - str; + opt->len = endp - str; } - - if (endp == str) - return 0; - - if (!next) - return len; - + opt->delim = *endp; if (*endp == 0) { /* Don't advance past the nul. */ - *next = endp; + opt->next = endp; } else { - *next = endp + 1; + opt->next = endp + 1; } - return len; -} - -static int __init dm_setup_args_init(void) -{ - dm_setup_args.minor = 0; - dm_setup_args.ro = 0; - dm_setup_args.target = NULL; - dm_setup_args.target_count = 0; - return 0; + return opt->len != 0; } -static int __init dm_setup_cleanup(void) +static int __init dm_setup_cleanup(struct dm_device *devices) { - struct dm_setup_target *target = dm_setup_args.target; - struct dm_setup_target *old_target = NULL; - while (target) { - kfree(target->type); - kfree(target->params); - old_target = target; - target = target->next; - kfree(old_target); - dm_setup_args.target_count--; + struct dm_device *dev = devices; + + while (dev) { + struct dm_device *old_dev = dev; + struct dm_setup_target *target = dev->target; + while (target) { + struct dm_setup_target *old_target = target; + kfree(target->type); + kfree(target->params); + target = target->next; + kfree(old_target); + dev->target_count--; + } + BUG_ON(dev->target_count); + dev = dev->next; + kfree(old_dev); } - BUG_ON(dm_setup_args.target_count); return 0; } -static char * __init dm_setup_parse_device_args(char *str) +static char * __init dm_parse_device(struct dm_device *dev, char *str) { - char *next = NULL; - size_t len = 0; + struct dm_option opt; + size_t len; /* Grab the logical name of the device to be exported to udev */ - len = get_dm_option(str, &next, DM_FIELD_SEP); - if (!len) { + opt.next = str; + if (!get_dm_option(&opt, DM_FIELD_SEP)) { DMERR("failed to parse device name"); goto parse_fail; } - len = min(len + 1, sizeof(dm_setup_args.name)); - strlcpy(dm_setup_args.name, str, len); /* includes nul */ - str = skip_spaces(next); + len = min(opt.len + 1, sizeof(dev->name)); + strlcpy(dev->name, opt.start, len); /* includes nul */ /* Grab the UUID value or "none" */ - len = get_dm_option(str, &next, DM_FIELD_SEP); - if (!len) { + if (!get_dm_option(&opt, DM_FIELD_SEP)) { DMERR("failed to parse device uuid"); goto parse_fail; } - len = min(len + 1, sizeof(dm_setup_args.uuid)); - strlcpy(dm_setup_args.uuid, str, len); - str = skip_spaces(next); + len = min(opt.len + 1, sizeof(dev->uuid)); + strlcpy(dev->uuid, opt.start, len); /* Determine if the table/device will be read only or read-write */ - if (!strncmp("ro,", str, 3)) { - dm_setup_args.ro = 1; - } else if (!strncmp("rw,", str, 3)) { - dm_setup_args.ro = 0; + get_dm_option(&opt, DM_ANY_SEP); + if (!strncmp("ro", opt.start, opt.len)) { + dev->ro = 1; + } else if (!strncmp("rw", opt.start, opt.len)) { + dev->ro = 0; } else { DMERR("failed to parse table mode"); goto parse_fail; } - str = skip_spaces(str + 3); - return str; + /* Optional number field */ + /* XXX: The field will be mandatory in the next round */ + if (opt.delim == DM_FIELD_SEP[0]) { + if (!get_dm_option(&opt, DM_LINE_SEP)) + return NULL; + dev->num_targets = simple_strtoul(opt.start, NULL, 10); + } else { + dev->num_targets = 1; + } + if (dev->num_targets > DM_MAX_TARGETS) { + DMERR("too many targets %lu > %d", + dev->num_targets, DM_MAX_TARGETS); + } + return opt.next; parse_fail: return NULL; } -static void __init dm_substitute_devices(char *str, size_t str_len) +static char * __init dm_parse_targets(struct dm_device *dev, char *str) { - char *candidate = str; - char *candidate_end = str; - char old_char; - size_t len = 0; - dev_t dev; - - if (str_len < 3) - return; - - while (str && *str) { - candidate = strchr(str, '/'); - if (!candidate) - break; - - /* Avoid embedded slashes */ - if (candidate != str && *(candidate - 1) != DM_FIELD_SEP) { - str = strchr(candidate, DM_FIELD_SEP); - continue; - } - - len = get_dm_option(candidate, &candidate_end, DM_FIELD_SEP); - str = skip_spaces(candidate_end); - if (len < 3 || len > 37) /* name_to_dev_t max; maj:mix min */ - continue; - - /* Temporarily terminate with a nul */ - if (*candidate_end) - candidate_end--; - old_char = *candidate_end; - *candidate_end = '\0'; - - DMDEBUG("converting candidate device '%s' to dev_t", candidate); - /* Use the boot-time specific device naming */ - dev = name_to_dev_t(candidate); - *candidate_end = old_char; - - DMDEBUG(" -> %u", dev); - /* No suitable replacement found */ - if (!dev) - continue; - - /* Rewrite the /dev/path as a major:minor */ - len = snprintf(candidate, len, "%u:%u", MAJOR(dev), MINOR(dev)); - if (!len) { - DMERR("error substituting device major/minor."); - break; - } - candidate += len; - /* Pad out with spaces (fixing our nul) */ - while (candidate < candidate_end) - *(candidate++) = DM_FIELD_SEP; - } -} - -static int __init dm_setup_parse_targets(char *str) -{ - char *next = NULL; - size_t len = 0; - struct dm_setup_target **target = NULL; + struct dm_option opt; + struct dm_setup_target **target = &dev->target; + unsigned long num_targets = dev->num_targets; + unsigned long i; /* Targets are defined as per the table format but with a * comma as a newline separator. */ - target = &dm_setup_args.target; - while (str && *str) { + opt.next = str; + for (i = 0; i < num_targets; i++) { *target = kzalloc(sizeof(struct dm_setup_target), GFP_KERNEL); if (!*target) { - DMERR("failed to allocate memory for target %d", - dm_setup_args.target_count); + DMERR("failed to allocate memory for target %s<%ld>", + dev->name, i); goto parse_fail; } - dm_setup_args.target_count++; + dev->target_count++; - (*target)->begin = simple_strtoull(str, &next, 10); - if (!next || *next != DM_FIELD_SEP) { - DMERR("failed to parse starting sector for target %d", - dm_setup_args.target_count - 1); + if (!get_dm_option(&opt, DM_FIELD_SEP)) { + DMERR("failed to parse starting sector" + " for target %s<%ld>", dev->name, i); goto parse_fail; } - str = skip_spaces(next + 1); + (*target)->begin = simple_strtoull(opt.start, NULL, 10); - (*target)->length = simple_strtoull(str, &next, 10); - if (!next || *next != DM_FIELD_SEP) { - DMERR("failed to parse length for target %d", - dm_setup_args.target_count - 1); + if (!get_dm_option(&opt, DM_FIELD_SEP)) { + DMERR("failed to parse length for target %s<%ld>", + dev->name, i); goto parse_fail; } - str = skip_spaces(next + 1); - - len = get_dm_option(str, &next, DM_FIELD_SEP); - if (!len || - !((*target)->type = kstrndup(str, len, GFP_KERNEL))) { - DMERR("failed to parse type for target %d", - dm_setup_args.target_count - 1); + (*target)->length = simple_strtoull(opt.start, NULL, 10); + + if (get_dm_option(&opt, DM_FIELD_SEP)) + (*target)->type = kstrndup(opt.start, opt.len, + GFP_KERNEL); + if (!((*target)->type)) { + DMERR("failed to parse type for target %s<%ld>", + dev->name, i); goto parse_fail; } - str = skip_spaces(next); - - len = get_dm_option(str, &next, DM_LINE_SEP); - if (!len || - !((*target)->params = kstrndup(str, len, GFP_KERNEL))) { - DMERR("failed to parse params for target %d", - dm_setup_args.target_count - 1); + if (get_dm_option(&opt, DM_LINE_SEP)) + (*target)->params = kstrndup(opt.start, opt.len, + GFP_KERNEL); + if (!((*target)->params)) { + DMERR("failed to parse params for target %s<%ld>", + dev->name, i); goto parse_fail; } - str = skip_spaces(next); - - /* Before moving on, walk through the copied target and - * attempt to replace all /dev/xxx with the major:minor number. - * It may not be possible to resolve them traditionally at - * boot-time. */ - dm_substitute_devices((*target)->params, len); - target = &((*target)->next); } - DMDEBUG("parsed %d targets", dm_setup_args.target_count); + DMDEBUG("parsed %d targets", dev->target_count); - return 0; + return opt.next; parse_fail: - return 1; + return NULL; +} + +static struct dm_device * __init dm_parse_args(void) +{ + struct dm_device *devices = NULL; + struct dm_device **tail = &devices; + struct dm_device *dev; + char *str = dm_setup_args.str; + unsigned long num_devices = dm_setup_args.num_devices; + unsigned long i; + + if (!str) + return NULL; + for (i = 0; i < num_devices; i++) { + dev = kzalloc(sizeof(*dev), GFP_KERNEL); + if (!dev) { + DMERR("failed to allocated memory for dev"); + goto error; + } + *tail = dev; + tail = &dev->next; + /* + * devices are given minor numbers 0 - n-1 + * in the order they are found in the arg + * string. + */ + dev->minor = i; + str = dm_parse_device(dev, str); + if (!str) /* NULL indicates error in parsing, bail */ + goto error; + + str = dm_parse_targets(dev, str); + if (!str) + goto error; + } + return devices; +error: + dm_setup_cleanup(devices); + return NULL; } /* * Parse the command-line parameters given our kernel, but do not * actually try to invoke the DM device now; that is handled by - * dm_setup_drive after the low-level disk drivers have initialised. - * dm format is as follows: - * dm="name uuid fmode,[table line 1],[table line 2],..." - * May be used with root=/dev/dm-0 as it always uses the first dm minor. + * dm_setup_drives after the low-level disk drivers have initialised. + * dm format is described at the top of the file. + * + * Because dm minor numbers are assigned in assending order starting with 0, + * You can assume the first device is /dev/dm-0, the next device is /dev/dm-1, + * and so forth. */ - static int __init dm_setup(char *str) { - dm_setup_args_init(); + struct dm_option opt; + unsigned long num_devices; - str = dm_setup_parse_device_args(str); if (!str) { DMDEBUG("str is NULL"); goto parse_fail; } - - /* Target parsing is delayed until we have dynamic memory */ - dm_setup_args.targets = str; - - printk(KERN_INFO "dm: will configure '%s' on dm-%d\n", - dm_setup_args.name, dm_setup_args.minor); - + opt.next = str; + if (!get_dm_option(&opt, DM_FIELD_SEP)) + goto parse_fail; + if (isdigit(opt.start[0])) { /* XXX: Optional number field */ + num_devices = simple_strtoul(opt.start, NULL, 10); + str = opt.next; + } else { + num_devices = 1; + /* Don't advance str */ + } + if (num_devices > DM_MAX_DEVICES) { + DMDEBUG("too many devices %lu > %d", + num_devices, DM_MAX_DEVICES); + } + dm_setup_args.str = str; + dm_setup_args.num_devices = num_devices; + DMINFO("will configure %lu devices", num_devices); dm_early_setup = 1; return 1; parse_fail: - printk(KERN_WARNING "dm: Invalid arguments supplied to dm=.\n"); + DMWARN("Invalid arguments supplied to dm=."); return 0; } - -static void __init dm_setup_drive(void) +static void __init dm_setup_drives(void) { struct mapped_device *md = NULL; struct dm_table *table = NULL; struct dm_setup_target *target; - char *uuid = dm_setup_args.uuid; + struct dm_device *dev; + char *uuid; fmode_t fmode = FMODE_READ; + struct dm_device *devices; - /* Finish parsing the targets. */ - if (dm_setup_parse_targets(dm_setup_args.targets)) - goto parse_fail; - - if (dm_create(dm_setup_args.minor, &md)) { - DMDEBUG("failed to create the device"); - goto dm_create_fail; - } - DMDEBUG("created device '%s'", dm_device_name(md)); - - /* In addition to flagging the table below, the disk must be - * set explicitly ro/rw. */ - set_disk_ro(dm_disk(md), dm_setup_args.ro); + devices = dm_parse_args(); - if (!dm_setup_args.ro) - fmode |= FMODE_WRITE; - if (dm_table_create(&table, fmode, dm_setup_args.target_count, md)) { - DMDEBUG("failed to create the table"); - goto dm_table_create_fail; - } + for (dev = devices; dev; dev = dev->next) { + if (dm_create(dev->minor, &md)) { + DMDEBUG("failed to create the device"); + goto dm_create_fail; + } + DMDEBUG("created device '%s'", dm_device_name(md)); + + /* + * In addition to flagging the table below, the disk must be + * set explicitly ro/rw. + */ + set_disk_ro(dm_disk(md), dev->ro); + + if (!dev->ro) + fmode |= FMODE_WRITE; + if (dm_table_create(&table, fmode, dev->target_count, md)) { + DMDEBUG("failed to create the table"); + goto dm_table_create_fail; + } - dm_lock_md_type(md); - target = dm_setup_args.target; - while (target) { - DMINFO("adding target '%llu %llu %s %s'", - (unsigned long long) target->begin, - (unsigned long long) target->length, target->type, - target->params); - if (dm_table_add_target(table, target->type, target->begin, - target->length, target->params)) { - DMDEBUG("failed to add the target to the table"); - goto add_target_fail; + dm_lock_md_type(md); + + for (target = dev->target; target; target = target->next) { + DMINFO("adding target '%llu %llu %s %s'", + (unsigned long long) target->begin, + (unsigned long long) target->length, + target->type, target->params); + if (dm_table_add_target(table, target->type, + target->begin, + target->length, + target->params)) { + DMDEBUG("failed to add the target" + " to the table"); + goto add_target_fail; + } + } + if (dm_table_complete(table)) { + DMDEBUG("failed to complete the table"); + goto table_complete_fail; } - target = target->next; - } - if (dm_table_complete(table)) { - DMDEBUG("failed to complete the table"); - goto table_complete_fail; - } + /* Suspend the device so that we can bind it to the table. */ + if (dm_suspend(md, 0)) { + DMDEBUG("failed to suspend the device pre-bind"); + goto suspend_fail; + } - if (dm_get_md_type(md) == DM_TYPE_NONE) { + /* Initial table load: acquire type of table. */ dm_set_md_type(md, dm_table_get_type(table)); + + /* Setup md->queue to reflect md's type. */ if (dm_setup_md_queue(md, table)) { DMWARN("unable to set up device queue for new table."); goto setup_md_queue_fail; } - } else if (dm_get_md_type(md) != dm_table_get_type(table)) { - DMWARN("can't change device type after initial table load."); - goto setup_md_queue_fail; - } - - /* Suspend the device so that we can bind it to the table. */ - if (dm_suspend(md, 0)) { - DMDEBUG("failed to suspend the device pre-bind"); - goto suspend_fail; - } - /* Bind the table to the device. This is the only way to associate - * md->map with the table and set the disk capacity directly. */ - if (dm_swap_table(md, table)) { /* should return NULL. */ - DMDEBUG("failed to bind the device to the table"); - goto table_bind_fail; - } + /* + * Bind the table to the device. This is the only way + * to associate md->map with the table and set the disk + * capacity directly. + */ + if (dm_swap_table(md, table)) { /* should return NULL. */ + DMDEBUG("failed to bind the device to the table"); + goto table_bind_fail; + } - /* Finally, resume and the device should be ready. */ - if (dm_resume(md)) { - DMDEBUG("failed to resume the device"); - goto resume_fail; - } + /* Finally, resume and the device should be ready. */ + if (dm_resume(md)) { + DMDEBUG("failed to resume the device"); + goto resume_fail; + } - /* Export the dm device via the ioctl interface */ - if (!strcmp(DM_NO_UUID, dm_setup_args.uuid)) - uuid = NULL; - if (dm_ioctl_export(md, dm_setup_args.name, uuid)) { - DMDEBUG("failed to export device with given name and uuid"); - goto export_fail; - } - printk(KERN_INFO "dm: dm-%d is ready\n", dm_setup_args.minor); + /* Export the dm device via the ioctl interface */ + if (!strcmp(DM_NO_UUID, dev->uuid)) + uuid = NULL; + if (dm_ioctl_export(md, dev->name, uuid)) { + DMDEBUG("failed to export device with given" + " name and uuid"); + goto export_fail; + } - dm_unlock_md_type(md); - dm_setup_cleanup(); + dm_unlock_md_type(md); + + DMINFO("dm-%d is ready", dev->minor); + } + dm_setup_cleanup(devices); return; export_fail: resume_fail: table_bind_fail: -suspend_fail: setup_md_queue_fail: +suspend_fail: table_complete_fail: add_target_fail: dm_unlock_md_type(md); dm_table_create_fail: dm_put(md); dm_create_fail: - dm_setup_cleanup(); -parse_fail: - printk(KERN_WARNING "dm: starting dm-%d (%s) failed\n", - dm_setup_args.minor, dm_setup_args.name); + DMWARN("starting dm-%d (%s) failed", + dev->minor, dev->name); + dm_setup_cleanup(devices); } __setup("dm=", dm_setup); @@ -421,6 +465,6 @@ void __init dm_run_setup(void) { if (!dm_early_setup) return; - printk(KERN_INFO "dm: attempting early device configuration.\n"); - dm_setup_drive(); + DMINFO("attempting early device configuration."); + dm_setup_drives(); } From eb33b97e01ffa16e93b1da14c7137d4f3c68fa24 Mon Sep 17 00:00:00 2001 From: Badhri Jagan Sridharan Date: Mon, 14 Dec 2015 20:09:39 -0800 Subject: [PATCH 0128/1103] ANDROID: dm: android-verity: Add android verity target This device-mapper target is virtually a VERITY target. This target is setup by reading the metadata contents piggybacked to the actual data blocks in the block device. The signature of the metadata contents are verified against the key included in the system keyring. Upon success, the underlying verity target is setup. BUG: 27175947 Change-Id: I7e99644a0960ac8279f02c0158ed20999510ea97 Signed-off-by: Badhri Jagan Sridharan [AmitP: Folded following android-4.9 commit changes into this patch 56f6a6b2b1cd ("ANDROID: dm-android-verity: Rebase on top of 4.1")] Signed-off-by: Amit Pundir --- drivers/md/Kconfig | 16 + drivers/md/Makefile | 4 + drivers/md/dm-android-verity.c | 771 +++++++++++++++++++++++++++++++++ drivers/md/dm-android-verity.h | 92 ++++ drivers/md/dm-verity-target.c | 12 +- drivers/md/dm-verity.h | 12 + 6 files changed, 901 insertions(+), 6 deletions(-) create mode 100644 drivers/md/dm-android-verity.c create mode 100644 drivers/md/dm-android-verity.h diff --git a/drivers/md/Kconfig b/drivers/md/Kconfig index 8b8c123cae66..7601e36a9ee0 100644 --- a/drivers/md/Kconfig +++ b/drivers/md/Kconfig @@ -559,4 +559,20 @@ config DM_ZONED If unsure, say N. +config DM_ANDROID_VERITY + bool "Android verity target support" + depends on DM_VERITY + depends on X509_CERTIFICATE_PARSER + depends on SYSTEM_TRUSTED_KEYRING + depends on PUBLIC_KEY_ALGO_RSA + depends on KEYS + depends on ASYMMETRIC_KEY_TYPE + depends on ASYMMETRIC_PUBLIC_KEY_SUBTYPE + ---help--- + This device-mapper target is virtually a VERITY target. This + target is setup by reading the metadata contents piggybacked + to the actual data blocks in the block device. The signature + of the metadata contents are verified against the key included + in the system keyring. Upon success, the underlying verity + target is setup. endif # MD diff --git a/drivers/md/Makefile b/drivers/md/Makefile index 822f4e8753bc..dab38ff6f542 100644 --- a/drivers/md/Makefile +++ b/drivers/md/Makefile @@ -76,3 +76,7 @@ endif ifeq ($(CONFIG_DM_VERITY_FEC),y) dm-verity-objs += dm-verity-fec.o endif + +ifeq ($(CONFIG_DM_ANDROID_VERITY),y) +dm-verity-objs += dm-android-verity.o +endif diff --git a/drivers/md/dm-android-verity.c b/drivers/md/dm-android-verity.c new file mode 100644 index 000000000000..aeb5045830d9 --- /dev/null +++ b/drivers/md/dm-android-verity.c @@ -0,0 +1,771 @@ +/* + * Copyright (C) 2015 Google, Inc. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "dm-verity.h" +#include "dm-android-verity.h" + +static char verifiedbootstate[VERITY_COMMANDLINE_PARAM_LENGTH]; +static char veritymode[VERITY_COMMANDLINE_PARAM_LENGTH]; + +static int __init verified_boot_state_param(char *line) +{ + strlcpy(verifiedbootstate, line, sizeof(verifiedbootstate)); + return 1; +} + +__setup("androidboot.verifiedbootstate=", verified_boot_state_param); + +static int __init verity_mode_param(char *line) +{ + strlcpy(veritymode, line, sizeof(veritymode)); + return 1; +} + +__setup("androidboot.veritymode=", verity_mode_param); + +static int table_extract_mpi_array(struct public_key_signature *pks, + const void *data, size_t len) +{ + MPI mpi = mpi_read_raw_data(data, len); + + if (!mpi) { + DMERR("Error while allocating mpi array"); + return -ENOMEM; + } + + pks->mpi[0] = mpi; + pks->nr_mpi = 1; + return 0; +} + +static struct public_key_signature *table_make_digest( + enum hash_algo hash, + const void *table, + unsigned long table_len) +{ + struct public_key_signature *pks = NULL; + struct crypto_shash *tfm; + struct shash_desc *desc; + size_t digest_size, desc_size; + int ret; + + /* Allocate the hashing algorithm we're going to need and find out how + * big the hash operational data will be. + */ + tfm = crypto_alloc_shash(hash_algo_name[hash], 0, 0); + if (IS_ERR(tfm)) + return ERR_CAST(tfm); + + desc_size = crypto_shash_descsize(tfm) + sizeof(*desc); + digest_size = crypto_shash_digestsize(tfm); + + /* We allocate the hash operational data storage on the end of out + * context data and the digest output buffer on the end of that. + */ + ret = -ENOMEM; + pks = kzalloc(digest_size + sizeof(*pks) + desc_size, GFP_KERNEL); + if (!pks) + goto error; + + pks->pkey_hash_algo = hash; + pks->digest = (u8 *)pks + sizeof(*pks) + desc_size; + pks->digest_size = digest_size; + + desc = (struct shash_desc *)(pks + 1); + desc->tfm = tfm; + desc->flags = CRYPTO_TFM_REQ_MAY_SLEEP; + + ret = crypto_shash_init(desc); + if (ret < 0) + goto error; + + ret = crypto_shash_finup(desc, table, table_len, pks->digest); + if (ret < 0) + goto error; + + crypto_free_shash(tfm); + return pks; + +error: + kfree(pks); + crypto_free_shash(tfm); + return ERR_PTR(ret); +} + +static int read_block_dev(struct bio_read *payload, struct block_device *bdev, + sector_t offset, int length) +{ + struct bio *bio; + int err = 0, i; + + payload->number_of_pages = DIV_ROUND_UP(length, PAGE_SIZE); + + bio = bio_alloc(GFP_KERNEL, payload->number_of_pages); + if (!bio) { + DMERR("Error while allocating bio"); + return -ENOMEM; + } + + bio->bi_bdev = bdev; + bio->bi_iter.bi_sector = offset; + + payload->page_io = kzalloc(sizeof(struct page *) * + payload->number_of_pages, GFP_KERNEL); + if (!payload->page_io) { + DMERR("page_io array alloc failed"); + err = -ENOMEM; + goto free_bio; + } + + for (i = 0; i < payload->number_of_pages; i++) { + payload->page_io[i] = alloc_page(GFP_KERNEL); + if (!payload->page_io[i]) { + DMERR("alloc_page failed"); + err = -ENOMEM; + goto free_pages; + } + if (!bio_add_page(bio, payload->page_io[i], PAGE_SIZE, 0)) { + DMERR("bio_add_page error"); + err = -EIO; + goto free_pages; + } + } + + if (!submit_bio_wait(READ, bio)) + /* success */ + goto free_bio; + DMERR("bio read failed"); + err = -EIO; + +free_pages: + for (i = 0; i < payload->number_of_pages; i++) + if (payload->page_io[i]) + __free_page(payload->page_io[i]); + kfree(payload->page_io); +free_bio: + bio_put(bio); + return err; +} + +static inline u64 fec_div_round_up(u64 x, u64 y) +{ + u64 remainder; + + return div64_u64_rem(x, y, &remainder) + + (remainder > 0 ? 1 : 0); +} + +static inline void populate_fec_metadata(struct fec_header *header, + struct fec_ecc_metadata *ecc) +{ + ecc->blocks = fec_div_round_up(le64_to_cpu(header->inp_size), + FEC_BLOCK_SIZE); + ecc->roots = le32_to_cpu(header->roots); + ecc->start = le64_to_cpu(header->inp_size); +} + +static inline int validate_fec_header(struct fec_header *header, u64 offset) +{ + /* move offset to make the sanity check work for backup header + * as well. */ + offset -= offset % FEC_BLOCK_SIZE; + if (le32_to_cpu(header->magic) != FEC_MAGIC || + le32_to_cpu(header->version) != FEC_VERSION || + le32_to_cpu(header->size) != sizeof(struct fec_header) || + le32_to_cpu(header->roots) == 0 || + le32_to_cpu(header->roots) >= FEC_RSM || + offset < le32_to_cpu(header->fec_size) || + offset - le32_to_cpu(header->fec_size) != + le64_to_cpu(header->inp_size)) + return -EINVAL; + + return 0; +} + +static int extract_fec_header(dev_t dev, struct fec_header *fec, + struct fec_ecc_metadata *ecc) +{ + u64 device_size; + struct bio_read payload; + int i, err = 0; + struct block_device *bdev; + + bdev = blkdev_get_by_dev(dev, FMODE_READ, NULL); + + if (IS_ERR(bdev)) { + DMERR("bdev get error"); + return PTR_ERR(bdev); + } + + device_size = i_size_read(bdev->bd_inode); + + /* fec metadata size is a power of 2 and PAGE_SIZE + * is a power of 2 as well. + */ + BUG_ON(FEC_BLOCK_SIZE > PAGE_SIZE); + /* 512 byte sector alignment */ + BUG_ON(((device_size - FEC_BLOCK_SIZE) % (1 << SECTOR_SHIFT)) != 0); + + err = read_block_dev(&payload, bdev, (device_size - + FEC_BLOCK_SIZE) / (1 << SECTOR_SHIFT), FEC_BLOCK_SIZE); + if (err) { + DMERR("Error while reading verity metadata"); + goto error; + } + + BUG_ON(sizeof(struct fec_header) > PAGE_SIZE); + memcpy(fec, page_address(payload.page_io[0]), + sizeof(*fec)); + + ecc->valid = true; + if (validate_fec_header(fec, device_size - FEC_BLOCK_SIZE)) { + /* Try the backup header */ + memcpy(fec, page_address(payload.page_io[0]) + FEC_BLOCK_SIZE + - sizeof(*fec) , + sizeof(*fec)); + if (validate_fec_header(fec, device_size - + sizeof(struct fec_header))) + ecc->valid = false; + } + + if (ecc->valid) + populate_fec_metadata(fec, ecc); + + for (i = 0; i < payload.number_of_pages; i++) + __free_page(payload.page_io[i]); + kfree(payload.page_io); + +error: + blkdev_put(bdev, FMODE_READ); + return err; +} +static void find_metadata_offset(struct fec_header *fec, + struct block_device *bdev, u64 *metadata_offset) +{ + u64 device_size; + + device_size = i_size_read(bdev->bd_inode); + + if (le32_to_cpu(fec->magic) == FEC_MAGIC) + *metadata_offset = le64_to_cpu(fec->inp_size) - + VERITY_METADATA_SIZE; + else + *metadata_offset = device_size - VERITY_METADATA_SIZE; +} + +static struct android_metadata *extract_metadata(dev_t dev, + struct fec_header *fec) +{ + struct block_device *bdev; + struct android_metadata_header *header; + struct android_metadata *uninitialized_var(metadata); + int i; + u32 table_length, copy_length, offset; + u64 metadata_offset; + struct bio_read payload; + int err = 0; + + bdev = blkdev_get_by_dev(dev, FMODE_READ, NULL); + + if (IS_ERR(bdev)) { + DMERR("blkdev_get_by_dev failed"); + return ERR_CAST(bdev); + } + + find_metadata_offset(fec, bdev, &metadata_offset); + + /* Verity metadata size is a power of 2 and PAGE_SIZE + * is a power of 2 as well. + * PAGE_SIZE is also a multiple of 512 bytes. + */ + if (VERITY_METADATA_SIZE > PAGE_SIZE) + BUG_ON(VERITY_METADATA_SIZE % PAGE_SIZE != 0); + /* 512 byte sector alignment */ + BUG_ON(metadata_offset % (1 << SECTOR_SHIFT) != 0); + + err = read_block_dev(&payload, bdev, metadata_offset / + (1 << SECTOR_SHIFT), VERITY_METADATA_SIZE); + if (err) { + DMERR("Error while reading verity metadata"); + metadata = ERR_PTR(err); + goto blkdev_release; + } + + header = kzalloc(sizeof(*header), GFP_KERNEL); + if (!header) { + DMERR("kzalloc failed for header"); + err = -ENOMEM; + goto free_payload; + } + + memcpy(header, page_address(payload.page_io[0]), + sizeof(*header)); + + DMINFO("bio magic_number:%u protocol_version:%d table_length:%u", + le32_to_cpu(header->magic_number), + le32_to_cpu(header->protocol_version), + le32_to_cpu(header->table_length)); + + metadata = kzalloc(sizeof(*metadata), GFP_KERNEL); + if (!metadata) { + DMERR("kzalloc for metadata failed"); + err = -ENOMEM; + goto free_header; + } + + metadata->header = header; + table_length = le32_to_cpu(header->table_length); + + if (table_length == 0 || + table_length > (VERITY_METADATA_SIZE - + sizeof(struct android_metadata_header))) + goto free_metadata; + + metadata->verity_table = kzalloc(table_length + 1, GFP_KERNEL); + + if (!metadata->verity_table) { + DMERR("kzalloc verity_table failed"); + err = -ENOMEM; + goto free_metadata; + } + + if (sizeof(struct android_metadata_header) + + table_length <= PAGE_SIZE) { + memcpy(metadata->verity_table, page_address(payload.page_io[0]) + + sizeof(struct android_metadata_header), + table_length); + } else { + copy_length = PAGE_SIZE - + sizeof(struct android_metadata_header); + memcpy(metadata->verity_table, page_address(payload.page_io[0]) + + sizeof(struct android_metadata_header), + copy_length); + table_length -= copy_length; + offset = copy_length; + i = 1; + while (table_length != 0) { + if (table_length > PAGE_SIZE) { + memcpy(metadata->verity_table + offset, + page_address(payload.page_io[i]), + PAGE_SIZE); + offset += PAGE_SIZE; + table_length -= PAGE_SIZE; + } else { + memcpy(metadata->verity_table + offset, + page_address(payload.page_io[i]), + table_length); + table_length = 0; + } + i++; + } + } + metadata->verity_table[table_length] = '\0'; + + goto free_payload; + +free_metadata: + kfree(metadata); +free_header: + kfree(header); + metadata = ERR_PTR(err); +free_payload: + for (i = 0; i < payload.number_of_pages; i++) + if (payload.page_io[i]) + __free_page(payload.page_io[i]); + kfree(payload.page_io); + + DMINFO("verity_table: %s", metadata->verity_table); +blkdev_release: + blkdev_put(bdev, FMODE_READ); + return metadata; +} + +/* helper functions to extract properties from dts */ +const char *find_dt_value(const char *name) +{ + struct device_node *firmware; + const char *value; + + firmware = of_find_node_by_path("/firmware/android"); + if (!firmware) + return NULL; + value = of_get_property(firmware, name, NULL); + of_node_put(firmware); + + return value; +} + +static bool is_unlocked(void) +{ + static const char unlocked[] = "orange"; + static const char verified_boot_prop[] = "verifiedbootstate"; + const char *value; + + value = find_dt_value(verified_boot_prop); + if (!value) + value = verifiedbootstate; + + return !strncmp(value, unlocked, sizeof(unlocked) - 1); +} + +static int verity_mode(void) +{ + static const char enforcing[] = "enforcing"; + static const char verified_mode_prop[] = "veritymode"; + const char *value; + + value = find_dt_value(verified_mode_prop); + if (!value) + value = veritymode; + if (!strncmp(value, enforcing, sizeof(enforcing) - 1)) + return DM_VERITY_MODE_RESTART; + + return DM_VERITY_MODE_EIO; +} + +static int verify_header(struct android_metadata_header *header) +{ + int retval = -EINVAL; + + if (is_unlocked() && le32_to_cpu(header->magic_number) == + VERITY_METADATA_MAGIC_DISABLE) { + retval = VERITY_STATE_DISABLE; + return retval; + } + + if (!(le32_to_cpu(header->magic_number) == + VERITY_METADATA_MAGIC_NUMBER) || + (le32_to_cpu(header->magic_number) == + VERITY_METADATA_MAGIC_DISABLE)) { + DMERR("Incorrect magic number"); + return retval; + } + + if (le32_to_cpu(header->protocol_version) != + VERITY_METADATA_VERSION) { + DMERR("Unsupported version %u", + le32_to_cpu(header->protocol_version)); + return retval; + } + + return 0; +} + +static int verify_verity_signature(char *key_id, + struct android_metadata *metadata) +{ + key_ref_t key_ref; + struct key *key; + struct public_key_signature *pks = NULL; + int retval = -EINVAL; + + key_ref = keyring_search(make_key_ref(system_trusted_keyring, 1), + &key_type_asymmetric, key_id); + + if (IS_ERR(key_ref)) { + DMERR("keyring: key not found"); + return -ENOKEY; + } + + key = key_ref_to_ptr(key_ref); + + pks = table_make_digest(HASH_ALGO_SHA256, + (const void *)metadata->verity_table, + le32_to_cpu(metadata->header->table_length)); + + if (IS_ERR(pks)) { + DMERR("hashing failed"); + goto error; + } + + retval = table_extract_mpi_array(pks, &metadata->header->signature[0], + RSANUMBYTES); + if (retval < 0) { + DMERR("Error extracting mpi %d", retval); + goto error; + } + + retval = verify_signature(key, pks); + mpi_free(pks->rsa.s); +error: + kfree(pks); + key_put(key); + + return retval; +} + +static void handle_error(void) +{ + int mode = verity_mode(); + if (mode == DM_VERITY_MODE_RESTART) { + DMERR("triggering restart"); + kernel_restart("dm-verity device corrupted"); + } else { + DMERR("Mounting verity root failed"); + } +} + +static inline bool test_mult_overflow(sector_t a, u32 b) +{ + sector_t r = (sector_t)~0ULL; + + sector_div(r, b); + return a > r; +} + +/* + * Target parameters: + * Key id of the public key in the system keyring. + * Verity metadata's signature would be verified against + * this. If the key id contains spaces, replace them + * with '#'. + * The block device for which dm-verity is being setup. + */ +static int android_verity_ctr(struct dm_target *ti, unsigned argc, char **argv) +{ + dev_t uninitialized_var(dev); + struct android_metadata *uninitialized_var(metadata); + int err = 0, i, mode; + char *key_id, *table_ptr, dummy, + *verity_table_args[VERITY_TABLE_ARGS + 2 + VERITY_TABLE_OPT_FEC_ARGS]; + /* One for specifying number of opt args and one for mode */ + sector_t data_sectors; + u32 data_block_size; + unsigned int major, minor, + no_of_args = VERITY_TABLE_ARGS + 2 + VERITY_TABLE_OPT_FEC_ARGS; + struct fec_header uninitialized_var(fec); + struct fec_ecc_metadata uninitialized_var(ecc); + char buf[FEC_ARG_LENGTH], *buf_ptr; + unsigned long long tmpll; + + if (argc != 2) { + DMERR("Incorrect number of arguments"); + handle_error(); + return -EINVAL; + } + + /* should come as one of the arguments for the verity target */ + key_id = argv[0]; + strreplace(argv[0], '#', ' '); + + if (sscanf(argv[1], "%u:%u%c", &major, &minor, &dummy) == 2) { + dev = MKDEV(major, minor); + if (MAJOR(dev) != major || MINOR(dev) != minor) { + DMERR("Incorrect bdev major minor number"); + handle_error(); + return -EOVERFLOW; + } + } + + DMINFO("key:%s dev:%s", argv[0], argv[1]); + + if (extract_fec_header(dev, &fec, &ecc)) { + DMERR("Error while extracting fec header"); + handle_error(); + return -EINVAL; + } + + metadata = extract_metadata(dev, &fec); + + if (IS_ERR(metadata)) { + DMERR("Error while extracting metadata"); + handle_error(); + return -EINVAL; + } + + err = verify_header(metadata->header); + + if (err == VERITY_STATE_DISABLE) { + DMERR("Mounting root with verity disabled"); + return -EINVAL; + } else if (err) { + DMERR("Verity header handle error"); + handle_error(); + goto free_metadata; + } + + err = verify_verity_signature(key_id, metadata); + + if (err) { + DMERR("Signature verification failed"); + handle_error(); + goto free_metadata; + } else + DMINFO("Signature verification success"); + + table_ptr = metadata->verity_table; + + for (i = 0; i < VERITY_TABLE_ARGS; i++) { + verity_table_args[i] = strsep(&table_ptr, " "); + if (verity_table_args[i] == NULL) + break; + } + + if (i != VERITY_TABLE_ARGS) { + DMERR("Verity table not in the expected format"); + err = -EINVAL; + handle_error(); + goto free_metadata; + } + + if (sscanf(verity_table_args[5], "%llu%c", &tmpll, &dummy) + != 1) { + DMERR("Verity table not in the expected format"); + handle_error(); + err = -EINVAL; + goto free_metadata; + } + + if (tmpll > ULONG_MAX) { + DMERR(" too large. Forgot to turn on CONFIG_LBDAF?"); + handle_error(); + err = -EINVAL; + goto free_metadata; + } + + data_sectors = tmpll; + + if (sscanf(verity_table_args[3], "%u%c", &data_block_size, &dummy) + != 1) { + DMERR("Verity table not in the expected format"); + handle_error(); + err = -EINVAL; + goto free_metadata; + } + + if (test_mult_overflow(data_sectors, data_block_size >> + SECTOR_SHIFT)) { + DMERR("data_sectors too large"); + handle_error(); + err = -EOVERFLOW; + goto free_metadata; + } + + data_sectors *= data_block_size >> SECTOR_SHIFT; + DMINFO("Data sectors %llu", (unsigned long long)data_sectors); + + /* update target length */ + ti->len = data_sectors; + + /*substitute data_dev and hash_dev*/ + verity_table_args[1] = argv[1]; + verity_table_args[2] = argv[1]; + + mode = verity_mode(); + + if (ecc.valid && IS_BUILTIN(CONFIG_DM_VERITY_FEC)) { + if (mode) { + err = snprintf(buf, FEC_ARG_LENGTH, + "%u %s " VERITY_TABLE_OPT_FEC_FORMAT, + 1 + VERITY_TABLE_OPT_FEC_ARGS, + mode == DM_VERITY_MODE_RESTART ? + VERITY_TABLE_OPT_RESTART : VERITY_TABLE_OPT_LOGGING, + argv[1], ecc.start / FEC_BLOCK_SIZE, ecc.blocks, + ecc.roots); + } else { + err = snprintf(buf, FEC_ARG_LENGTH, + "%u " VERITY_TABLE_OPT_FEC_FORMAT, + VERITY_TABLE_OPT_FEC_ARGS, argv[1], + ecc.start / FEC_BLOCK_SIZE, ecc.blocks, ecc.roots); + } + } else if (mode) { + err = snprintf(buf, FEC_ARG_LENGTH, + "2 " VERITY_TABLE_OPT_IGNZERO " %s", + mode == DM_VERITY_MODE_RESTART ? + VERITY_TABLE_OPT_RESTART : VERITY_TABLE_OPT_LOGGING); + } else { + err = snprintf(buf, FEC_ARG_LENGTH, "1 %s", + "ignore_zero_blocks"); + } + + if (err < 0 || err >= FEC_ARG_LENGTH) + goto free_metadata; + + buf_ptr = buf; + + for (i = VERITY_TABLE_ARGS; i < (VERITY_TABLE_ARGS + + VERITY_TABLE_OPT_FEC_ARGS + 2); i++) { + verity_table_args[i] = strsep(&buf_ptr, " "); + if (verity_table_args[i] == NULL) { + no_of_args = i; + break; + } + } + + err = verity_ctr(ti, no_of_args, verity_table_args); + +free_metadata: + kfree(metadata->header); + kfree(metadata->verity_table); + kfree(metadata); + return err; +} + +static struct target_type android_verity_target = { + .name = "android-verity", + .version = {1, 0, 0}, + .module = THIS_MODULE, + .ctr = android_verity_ctr, + .dtr = verity_dtr, + .map = verity_map, + .status = verity_status, + .ioctl = verity_ioctl, + .merge = verity_merge, + .iterate_devices = verity_iterate_devices, + .io_hints = verity_io_hints, +}; + +static int __init dm_android_verity_init(void) +{ + int r; + + r = dm_register_target(&android_verity_target); + if (r < 0) + DMERR("register failed %d", r); + + return r; +} + +static void __exit dm_android_verity_exit(void) +{ + dm_unregister_target(&android_verity_target); +} + +module_init(dm_android_verity_init); +module_exit(dm_android_verity_exit); diff --git a/drivers/md/dm-android-verity.h b/drivers/md/dm-android-verity.h new file mode 100644 index 000000000000..11477ffd2243 --- /dev/null +++ b/drivers/md/dm-android-verity.h @@ -0,0 +1,92 @@ +/* + * Copyright (C) 2015 Google, Inc. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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. + * + */ + +#ifndef DM_ANDROID_VERITY_H +#define DM_ANDROID_VERITY_H + +#include + +#define RSANUMBYTES 256 +#define VERITY_METADATA_MAGIC_NUMBER 0xb001b001 +#define VERITY_METADATA_MAGIC_DISABLE 0x46464f56 +#define VERITY_METADATA_VERSION 0 +#define VERITY_STATE_DISABLE 1 +#define DATA_BLOCK_SIZE (4 * 1024) +#define VERITY_METADATA_SIZE (8 * DATA_BLOCK_SIZE) +#define VERITY_TABLE_ARGS 10 +#define VERITY_COMMANDLINE_PARAM_LENGTH 20 + +#define FEC_MAGIC 0xFECFECFE +#define FEC_BLOCK_SIZE (4 * 1024) +#define FEC_VERSION 0 +#define FEC_RSM 255 +#define FEC_ARG_LENGTH 300 + +#define VERITY_TABLE_OPT_RESTART "restart_on_corruption" +#define VERITY_TABLE_OPT_LOGGING "ignore_corruption" +#define VERITY_TABLE_OPT_IGNZERO "ignore_zero_blocks" + +#define VERITY_TABLE_OPT_FEC_FORMAT \ + "use_fec_from_device %s fec_start %llu fec_blocks %llu fec_roots %u ignore_zero_blocks" +#define VERITY_TABLE_OPT_FEC_ARGS 9 + +#define VERITY_DEBUG 0 + +#define DM_MSG_PREFIX "android-verity" +/* + * There can be two formats. + * if fec is present + * + * if fec is not present + * + */ +/* TODO: rearrange structure to reduce memory holes + * depends on userspace change. + */ +struct fec_header { + __le32 magic; + __le32 version; + __le32 size; + __le32 roots; + __le32 fec_size; + __le64 inp_size; + u8 hash[SHA256_DIGEST_SIZE]; +}; + +struct android_metadata_header { + __le32 magic_number; + __le32 protocol_version; + char signature[RSANUMBYTES]; + __le32 table_length; +}; + +struct android_metadata { + struct android_metadata_header *header; + char *verity_table; +}; + +struct fec_ecc_metadata { + bool valid; + u32 roots; + u64 blocks; + u64 rounds; + u64 start; +}; + +struct bio_read { + struct page **page_io; + int number_of_pages; +}; + +#endif /* DM_ANDROID_VERITY_H */ diff --git a/drivers/md/dm-verity-target.c b/drivers/md/dm-verity-target.c index fc65f0dedf7f..fc7895c6dd5d 100644 --- a/drivers/md/dm-verity-target.c +++ b/drivers/md/dm-verity-target.c @@ -630,7 +630,7 @@ static void verity_submit_prefetch(struct dm_verity *v, struct dm_verity_io *io) * Bio map function. It allocates dm_verity_io structure and bio vector and * fills them. Then it issues prefetches and the I/O. */ -static int verity_map(struct dm_target *ti, struct bio *bio) +int verity_map(struct dm_target *ti, struct bio *bio) { struct dm_verity *v = ti->private; struct dm_verity_io *io; @@ -675,7 +675,7 @@ static int verity_map(struct dm_target *ti, struct bio *bio) /* * Status: V (valid) or C (corruption found) */ -static void verity_status(struct dm_target *ti, status_type_t type, +void verity_status(struct dm_target *ti, status_type_t type, unsigned status_flags, char *result, unsigned maxlen) { struct dm_verity *v = ti->private; @@ -751,7 +751,7 @@ static int verity_prepare_ioctl(struct dm_target *ti, struct block_device **bdev return 0; } -static int verity_iterate_devices(struct dm_target *ti, +int verity_iterate_devices(struct dm_target *ti, iterate_devices_callout_fn fn, void *data) { struct dm_verity *v = ti->private; @@ -759,7 +759,7 @@ static int verity_iterate_devices(struct dm_target *ti, return fn(ti, v->data_dev, v->data_start, ti->len, data); } -static void verity_io_hints(struct dm_target *ti, struct queue_limits *limits) +void verity_io_hints(struct dm_target *ti, struct queue_limits *limits) { struct dm_verity *v = ti->private; @@ -772,7 +772,7 @@ static void verity_io_hints(struct dm_target *ti, struct queue_limits *limits) blk_limits_io_min(limits, limits->logical_block_size); } -static void verity_dtr(struct dm_target *ti) +void verity_dtr(struct dm_target *ti) { struct dm_verity *v = ti->private; @@ -927,7 +927,7 @@ static int verity_parse_opt_args(struct dm_arg_set *as, struct dm_verity *v) * * Hex string or "-" if no salt. */ -static int verity_ctr(struct dm_target *ti, unsigned argc, char **argv) +int verity_ctr(struct dm_target *ti, unsigned argc, char **argv) { struct dm_verity *v; struct dm_arg_set as; diff --git a/drivers/md/dm-verity.h b/drivers/md/dm-verity.h index 3441c10b840c..b07e2e5bd952 100644 --- a/drivers/md/dm-verity.h +++ b/drivers/md/dm-verity.h @@ -127,4 +127,16 @@ extern int verity_hash(struct dm_verity *v, struct ahash_request *req, extern int verity_hash_for_block(struct dm_verity *v, struct dm_verity_io *io, sector_t block, u8 *digest, bool *is_zero); +extern void verity_status(struct dm_target *ti, status_type_t type, + unsigned status_flags, char *result, unsigned maxlen); +extern int verity_ioctl(struct dm_target *ti, unsigned cmd, + unsigned long arg); +extern int verity_merge(struct dm_target *ti, struct bvec_merge_data *bvm, + struct bio_vec *biovec, int max_size); +extern int verity_iterate_devices(struct dm_target *ti, + iterate_devices_callout_fn fn, void *data); +extern void verity_io_hints(struct dm_target *ti, struct queue_limits *limits); +extern void verity_dtr(struct dm_target *ti); +extern int verity_ctr(struct dm_target *ti, unsigned argc, char **argv); +extern int verity_map(struct dm_target *ti, struct bio *bio); #endif /* DM_VERITY_H */ From 4a1fda5561580213dbec5d458412f04a4e51367f Mon Sep 17 00:00:00 2001 From: Badhri Jagan Sridharan Date: Mon, 21 Mar 2016 10:55:23 -0700 Subject: [PATCH 0129/1103] ANDROID: dm: android-verity: Mounting root as linear device when verity disabled This CL makes android-verity target to be added as linear dm device if when bootloader is unlocked and verity is disabled. Bug: 27175947 Change-Id: Ic41ca4b8908fb2777263799cf3a3e25934d70f18 Signed-off-by: Badhri Jagan Sridharan [AmitP: Folded following android-4.9 commit changes into this patch 7e70218c2699 ("ANDROID: dm: Minor cleanup") 67584ff8412b ("ANDROID: dm: rename dm-linear methods for dm-android-verity")] Signed-off-by: Amit Pundir --- drivers/md/dm-android-verity.c | 128 +++++++++++++++++++++++++++------ drivers/md/dm-android-verity.h | 17 +++++ drivers/md/dm-linear.c | 24 +++---- 3 files changed, 135 insertions(+), 34 deletions(-) diff --git a/drivers/md/dm-android-verity.c b/drivers/md/dm-android-verity.c index aeb5045830d9..b7e059595f75 100644 --- a/drivers/md/dm-android-verity.c +++ b/drivers/md/dm-android-verity.c @@ -13,6 +13,7 @@ */ #include +#include #include #include #include @@ -43,6 +44,25 @@ static char verifiedbootstate[VERITY_COMMANDLINE_PARAM_LENGTH]; static char veritymode[VERITY_COMMANDLINE_PARAM_LENGTH]; +static bool target_added; +static bool verity_enabled = true; +struct dentry *debug_dir; +static int android_verity_ctr(struct dm_target *ti, unsigned argc, char **argv); + +static struct target_type android_verity_target = { + .name = "android-verity", + .version = {1, 0, 0}, + .module = THIS_MODULE, + .ctr = android_verity_ctr, + .dtr = verity_dtr, + .map = verity_map, + .status = verity_status, + .ioctl = verity_ioctl, + .merge = verity_merge, + .iterate_devices = verity_iterate_devices, + .io_hints = verity_io_hints, +}; + static int __init verified_boot_state_param(char *line) { strlcpy(verifiedbootstate, line, sizeof(verifiedbootstate)); @@ -549,6 +569,32 @@ static inline bool test_mult_overflow(sector_t a, u32 b) return a > r; } +static int add_as_linear_device(struct dm_target *ti, char *dev) +{ + /*Move to linear mapping defines*/ + char *linear_table_args[DM_LINEAR_ARGS] = {dev, + DM_LINEAR_TARGET_OFFSET}; + int err = 0; + + android_verity_target.dtr = dm_linear_dtr, + android_verity_target.map = dm_linear_map, + android_verity_target.status = dm_linear_status, + android_verity_target.ioctl = dm_linear_ioctl, + android_verity_target.merge = dm_linear_merge, + android_verity_target.iterate_devices = dm_linear_iterate_devices, + android_verity_target.io_hints = NULL; + + err = dm_linear_ctr(ti, DM_LINEAR_ARGS, linear_table_args); + + if (!err) { + DMINFO("Added android-verity as a linear target"); + target_added = true; + } else + DMERR("Failed to add android-verity as linear target"); + + return err; +} + /* * Target parameters: * Key id of the public key in the system keyring. @@ -613,21 +659,27 @@ static int android_verity_ctr(struct dm_target *ti, unsigned argc, char **argv) if (err == VERITY_STATE_DISABLE) { DMERR("Mounting root with verity disabled"); - return -EINVAL; + verity_enabled = false; + /* we would still have to parse the args to figure out + * the data blocks size. Or may be could map the entire + * partition similar to mounting the device. + */ } else if (err) { DMERR("Verity header handle error"); handle_error(); goto free_metadata; } - err = verify_verity_signature(key_id, metadata); + if (!verity_enabled) { + err = verify_verity_signature(key_id, metadata); - if (err) { - DMERR("Signature verification failed"); - handle_error(); - goto free_metadata; - } else - DMINFO("Signature verification success"); + if (err) { + DMERR("Signature verification failed"); + handle_error(); + goto free_metadata; + } else + DMINFO("Signature verification success"); + } table_ptr = metadata->verity_table; @@ -683,6 +735,12 @@ static int android_verity_ctr(struct dm_target *ti, unsigned argc, char **argv) /* update target length */ ti->len = data_sectors; + /* Setup linear target and free */ + if (!verity_enabled) { + err = add_as_linear_device(ti, argv[1]); + goto free_metadata; + } + /*substitute data_dev and hash_dev*/ verity_table_args[1] = argv[1]; verity_table_args[2] = argv[1]; @@ -730,6 +788,13 @@ static int android_verity_ctr(struct dm_target *ti, unsigned argc, char **argv) err = verity_ctr(ti, no_of_args, verity_table_args); + if (err) + DMERR("android-verity failed to mount as verity target"); + else { + target_added = true; + DMINFO("android-verity mounted as verity target"); + } + free_metadata: kfree(metadata->header); kfree(metadata->verity_table); @@ -737,33 +802,52 @@ static int android_verity_ctr(struct dm_target *ti, unsigned argc, char **argv) return err; } -static struct target_type android_verity_target = { - .name = "android-verity", - .version = {1, 0, 0}, - .module = THIS_MODULE, - .ctr = android_verity_ctr, - .dtr = verity_dtr, - .map = verity_map, - .status = verity_status, - .ioctl = verity_ioctl, - .merge = verity_merge, - .iterate_devices = verity_iterate_devices, - .io_hints = verity_io_hints, -}; - static int __init dm_android_verity_init(void) { int r; + struct dentry *file; r = dm_register_target(&android_verity_target); if (r < 0) DMERR("register failed %d", r); + /* Tracks the status of the last added target */ + debug_dir = debugfs_create_dir("android_verity", NULL); + + if (IS_ERR_OR_NULL(debug_dir)) { + DMERR("Cannot create android_verity debugfs directory: %ld", + PTR_ERR(debug_dir)); + goto end; + } + + file = debugfs_create_bool("target_added", S_IRUGO, debug_dir, + (u32 *)&target_added); + + if (IS_ERR_OR_NULL(file)) { + DMERR("Cannot create android_verity debugfs directory: %ld", + PTR_ERR(debug_dir)); + debugfs_remove_recursive(debug_dir); + goto end; + } + + file = debugfs_create_bool("verity_enabled", S_IRUGO, debug_dir, + (u32 *)&verity_enabled); + + if (IS_ERR_OR_NULL(file)) { + DMERR("Cannot create android_verity debugfs directory: %ld", + PTR_ERR(debug_dir)); + debugfs_remove_recursive(debug_dir); + } + +end: return r; } static void __exit dm_android_verity_exit(void) { + if (!IS_ERR_OR_NULL(debug_dir)) + debugfs_remove_recursive(debug_dir); + dm_unregister_target(&android_verity_target); } diff --git a/drivers/md/dm-android-verity.h b/drivers/md/dm-android-verity.h index 11477ffd2243..efb796524896 100644 --- a/drivers/md/dm-android-verity.h +++ b/drivers/md/dm-android-verity.h @@ -44,6 +44,10 @@ #define VERITY_DEBUG 0 #define DM_MSG_PREFIX "android-verity" + +#define DM_LINEAR_ARGS 2 +#define DM_LINEAR_TARGET_OFFSET "0" + /* * There can be two formats. * if fec is present @@ -89,4 +93,17 @@ struct bio_read { int number_of_pages; }; +extern struct target_type linear_target; + +extern void dm_linear_dtr(struct dm_target *ti); +extern int dm_linear_map(struct dm_target *ti, struct bio *bio); +extern void dm_linear_status(struct dm_target *ti, status_type_t type, + unsigned status_flags, char *result, unsigned maxlen); +extern int dm_linear_ioctl(struct dm_target *ti, unsigned int cmd, + unsigned long arg); +extern int dm_linear_merge(struct dm_target *ti, struct bvec_merge_data *bvm, + struct bio_vec *biovec, int max_size); +extern int dm_linear_iterate_devices(struct dm_target *ti, + iterate_devices_callout_fn fn, void *data); +extern int dm_linear_ctr(struct dm_target *ti, unsigned int argc, char **argv); #endif /* DM_ANDROID_VERITY_H */ diff --git a/drivers/md/dm-linear.c b/drivers/md/dm-linear.c index d10964d41fd7..187574311a13 100644 --- a/drivers/md/dm-linear.c +++ b/drivers/md/dm-linear.c @@ -26,7 +26,7 @@ struct linear_c { /* * Construct a linear mapping: */ -static int linear_ctr(struct dm_target *ti, unsigned int argc, char **argv) +int dm_linear_ctr(struct dm_target *ti, unsigned int argc, char **argv) { struct linear_c *lc; unsigned long long tmp; @@ -70,7 +70,7 @@ static int linear_ctr(struct dm_target *ti, unsigned int argc, char **argv) return ret; } -static void linear_dtr(struct dm_target *ti) +void dm_linear_dtr(struct dm_target *ti) { struct linear_c *lc = (struct linear_c *) ti->private; @@ -95,7 +95,7 @@ static void linear_map_bio(struct dm_target *ti, struct bio *bio) linear_map_sector(ti, bio->bi_iter.bi_sector); } -static int linear_map(struct dm_target *ti, struct bio *bio) +int dm_linear_map(struct dm_target *ti, struct bio *bio) { linear_map_bio(ti, bio); @@ -113,7 +113,7 @@ static int linear_end_io(struct dm_target *ti, struct bio *bio, return DM_ENDIO_DONE; } -static void linear_status(struct dm_target *ti, status_type_t type, +void dm_linear_status(struct dm_target *ti, status_type_t type, unsigned status_flags, char *result, unsigned maxlen) { struct linear_c *lc = (struct linear_c *) ti->private; @@ -130,7 +130,7 @@ static void linear_status(struct dm_target *ti, status_type_t type, } } -static int linear_prepare_ioctl(struct dm_target *ti, struct block_device **bdev) +static int dm_linear_prepare_ioctl(struct dm_target *ti, struct block_device **bdev) { struct linear_c *lc = (struct linear_c *) ti->private; struct dm_dev *dev = lc->dev; @@ -146,7 +146,7 @@ static int linear_prepare_ioctl(struct dm_target *ti, struct block_device **bdev return 0; } -static int linear_iterate_devices(struct dm_target *ti, +int dm_linear_iterate_devices(struct dm_target *ti, iterate_devices_callout_fn fn, void *data) { struct linear_c *lc = ti->private; @@ -210,13 +210,13 @@ static struct target_type linear_target = { .version = {1, 4, 0}, .features = DM_TARGET_PASSES_INTEGRITY | DM_TARGET_ZONED_HM, .module = THIS_MODULE, - .ctr = linear_ctr, - .dtr = linear_dtr, - .map = linear_map, + .ctr = dm_linear_ctr, + .dtr = dm_linear_dtr, + .map = dm_linear_map, + .status = dm_linear_status, .end_io = linear_end_io, - .status = linear_status, - .prepare_ioctl = linear_prepare_ioctl, - .iterate_devices = linear_iterate_devices, + .prepare_ioctl = dm_linear_prepare_ioctl, + .iterate_devices = dm_linear_iterate_devices, .direct_access = linear_dax_direct_access, .dax_copy_from_iter = linear_dax_copy_from_iter, .dax_copy_to_iter = linear_dax_copy_to_iter, From bf8be2817c881f43e201ab5e8ec6bea6c52029dd Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Fri, 15 Apr 2016 13:32:54 +0200 Subject: [PATCH 0130/1103] ANDROID: dm: android-verity: use name_to_dev_t This patch makes android_verity_ctr() parse its block device string parameter with name_to_dev_t(). It allows the use of less hardware related block device reference like PARTUUID for instance. Change-Id: Idb84453e70cc11abd5ef3a0adfbb16f8b5feaf07 Signed-off-by: Jeremy Compostella --- drivers/md/dm-android-verity.c | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/drivers/md/dm-android-verity.c b/drivers/md/dm-android-verity.c index b7e059595f75..9c26cbb5f179 100644 --- a/drivers/md/dm-android-verity.c +++ b/drivers/md/dm-android-verity.c @@ -613,8 +613,7 @@ static int android_verity_ctr(struct dm_target *ti, unsigned argc, char **argv) /* One for specifying number of opt args and one for mode */ sector_t data_sectors; u32 data_block_size; - unsigned int major, minor, - no_of_args = VERITY_TABLE_ARGS + 2 + VERITY_TABLE_OPT_FEC_ARGS; + unsigned int no_of_args = VERITY_TABLE_ARGS + 2 + VERITY_TABLE_OPT_FEC_ARGS; struct fec_header uninitialized_var(fec); struct fec_ecc_metadata uninitialized_var(ecc); char buf[FEC_ARG_LENGTH], *buf_ptr; @@ -630,13 +629,11 @@ static int android_verity_ctr(struct dm_target *ti, unsigned argc, char **argv) key_id = argv[0]; strreplace(argv[0], '#', ' '); - if (sscanf(argv[1], "%u:%u%c", &major, &minor, &dummy) == 2) { - dev = MKDEV(major, minor); - if (MAJOR(dev) != major || MINOR(dev) != minor) { - DMERR("Incorrect bdev major minor number"); - handle_error(); - return -EOVERFLOW; - } + dev = name_to_dev_t(argv[1]); + if (!dev) { + DMERR("no dev found for %s", argv[1]); + handle_error(); + return -EINVAL; } DMINFO("key:%s dev:%s", argv[0], argv[1]); From 243b731dda946e3b83575c7f50fb8fbb6e5a9215 Mon Sep 17 00:00:00 2001 From: Badhri Jagan Sridharan Date: Fri, 20 May 2016 16:44:19 -0700 Subject: [PATCH 0131/1103] ANDROID: dm: android-verity: fix signature verification flag The bug was that the signature verification was only happening when verity was disabled. It should always happen when verity is enabled. Signed-off-by: Badhri Jagan Sridharan Change-Id: I2d9354e240d36ea06fc68c2d18d8e87b823a4c2f (cherry picked from commit 5364b5ca0b1a12a58283b51408e43fc36d4e4fe7) --- drivers/md/dm-android-verity.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/md/dm-android-verity.c b/drivers/md/dm-android-verity.c index 9c26cbb5f179..00275a986d03 100644 --- a/drivers/md/dm-android-verity.c +++ b/drivers/md/dm-android-verity.c @@ -667,7 +667,7 @@ static int android_verity_ctr(struct dm_target *ti, unsigned argc, char **argv) goto free_metadata; } - if (!verity_enabled) { + if (verity_enabled) { err = verify_verity_signature(key_id, metadata); if (err) { From 1e8b181391c1fe1565f485a680f0e78a791b24df Mon Sep 17 00:00:00 2001 From: Badhri Jagan Sridharan Date: Fri, 20 May 2016 16:45:45 -0700 Subject: [PATCH 0132/1103] ANDROID: dm: android-verity: use default verity public key If the dm-android-verity target does not provide a default key try using the default public key from the system keyring. The defualt verity keyid is passed as a kernel command line argument veritykeyid=. The order of the dm-android-verity params have been reversed to facilitate the change. Old format example: dm="system none ro,0 1 android-verity Android:#7e4333f9bba00adfe0ede979e28ed1920492b40f /dev/mmcblk0p43" New formats supported: dm="system none ro,0 1 android-verity /dev/mmcblk0p43 Android:#7e4333f9bba00adfe0ede979e28ed1920492b40f" (or) dm="system none ro,0 1 android-verity /dev/mmcblk0p43" when veritykeyid= is set in the kernel command line. BUG: 28384658 Signed-off-by: Badhri Jagan Sridharan Change-Id: I506c89b053d835ab579e703eef2bc1f8487250de (cherry picked from commit c5c74d0327729f35b576564976885596c6d0e7fb) --- drivers/md/dm-android-verity.c | 67 ++++++++++++++++++++++++---------- drivers/md/dm-android-verity.h | 16 ++++++++ 2 files changed, 63 insertions(+), 20 deletions(-) diff --git a/drivers/md/dm-android-verity.c b/drivers/md/dm-android-verity.c index 00275a986d03..097fb2b1de89 100644 --- a/drivers/md/dm-android-verity.c +++ b/drivers/md/dm-android-verity.c @@ -43,6 +43,7 @@ static char verifiedbootstate[VERITY_COMMANDLINE_PARAM_LENGTH]; static char veritymode[VERITY_COMMANDLINE_PARAM_LENGTH]; +static char veritykeyid[VERITY_DEFAULT_KEY_ID_LENGTH]; static bool target_added; static bool verity_enabled = true; @@ -79,6 +80,19 @@ static int __init verity_mode_param(char *line) __setup("androidboot.veritymode=", verity_mode_param); +static int __init verity_keyid_param(char *line) +{ + strlcpy(veritykeyid, line, sizeof(veritykeyid)); + return 1; +} + +__setup("veritykeyid=", verity_keyid_param); + +static inline bool default_verity_key_id(void) +{ + return veritykeyid[0] != '\0'; +} + static int table_extract_mpi_array(struct public_key_signature *pks, const void *data, size_t len) { @@ -608,7 +622,7 @@ static int android_verity_ctr(struct dm_target *ti, unsigned argc, char **argv) dev_t uninitialized_var(dev); struct android_metadata *uninitialized_var(metadata); int err = 0, i, mode; - char *key_id, *table_ptr, dummy, + char *key_id, *table_ptr, dummy, *target_device, *verity_table_args[VERITY_TABLE_ARGS + 2 + VERITY_TABLE_OPT_FEC_ARGS]; /* One for specifying number of opt args and one for mode */ sector_t data_sectors; @@ -619,24 +633,34 @@ static int android_verity_ctr(struct dm_target *ti, unsigned argc, char **argv) char buf[FEC_ARG_LENGTH], *buf_ptr; unsigned long long tmpll; - if (argc != 2) { + if (argc == 1) { + /* Use the default keyid */ + if (default_verity_key_id()) + key_id = veritykeyid; + else { + DMERR("veritykeyid= is not set"); + handle_error(); + return -EINVAL; + } + } else if (argc == 2) + key_id = argv[1]; + else { DMERR("Incorrect number of arguments"); handle_error(); return -EINVAL; } - /* should come as one of the arguments for the verity target */ - key_id = argv[0]; - strreplace(argv[0], '#', ' '); + strreplace(key_id, '#', ' '); + target_device = argv[0]; - dev = name_to_dev_t(argv[1]); + dev = name_to_dev_t(target_device); if (!dev) { - DMERR("no dev found for %s", argv[1]); + DMERR("no dev found for %s", target_device); handle_error(); return -EINVAL; } - DMINFO("key:%s dev:%s", argv[0], argv[1]); + DMINFO("key:%s dev:%s", key_id, target_device); if (extract_fec_header(dev, &fec, &ecc)) { DMERR("Error while extracting fec header"); @@ -734,30 +758,33 @@ static int android_verity_ctr(struct dm_target *ti, unsigned argc, char **argv) /* Setup linear target and free */ if (!verity_enabled) { - err = add_as_linear_device(ti, argv[1]); + err = add_as_linear_device(ti, target_device); goto free_metadata; } /*substitute data_dev and hash_dev*/ - verity_table_args[1] = argv[1]; - verity_table_args[2] = argv[1]; + verity_table_args[1] = target_device; + verity_table_args[2] = target_device; mode = verity_mode(); if (ecc.valid && IS_BUILTIN(CONFIG_DM_VERITY_FEC)) { if (mode) { err = snprintf(buf, FEC_ARG_LENGTH, - "%u %s " VERITY_TABLE_OPT_FEC_FORMAT, - 1 + VERITY_TABLE_OPT_FEC_ARGS, - mode == DM_VERITY_MODE_RESTART ? - VERITY_TABLE_OPT_RESTART : VERITY_TABLE_OPT_LOGGING, - argv[1], ecc.start / FEC_BLOCK_SIZE, ecc.blocks, - ecc.roots); + "%u %s " VERITY_TABLE_OPT_FEC_FORMAT, + 1 + VERITY_TABLE_OPT_FEC_ARGS, + mode == DM_VERITY_MODE_RESTART ? + VERITY_TABLE_OPT_RESTART : + VERITY_TABLE_OPT_LOGGING, + target_device, + ecc.start / FEC_BLOCK_SIZE, ecc.blocks, + ecc.roots); } else { err = snprintf(buf, FEC_ARG_LENGTH, - "%u " VERITY_TABLE_OPT_FEC_FORMAT, - VERITY_TABLE_OPT_FEC_ARGS, argv[1], - ecc.start / FEC_BLOCK_SIZE, ecc.blocks, ecc.roots); + "%u " VERITY_TABLE_OPT_FEC_FORMAT, + VERITY_TABLE_OPT_FEC_ARGS, target_device, + ecc.start / FEC_BLOCK_SIZE, ecc.blocks, + ecc.roots); } } else if (mode) { err = snprintf(buf, FEC_ARG_LENGTH, diff --git a/drivers/md/dm-android-verity.h b/drivers/md/dm-android-verity.h index efb796524896..43655ee0f813 100644 --- a/drivers/md/dm-android-verity.h +++ b/drivers/md/dm-android-verity.h @@ -27,6 +27,22 @@ #define VERITY_TABLE_ARGS 10 #define VERITY_COMMANDLINE_PARAM_LENGTH 20 +/* + * : is the format for the identifier. + * subject can either be the Common Name(CN) + Organization Name(O) or + * just the CN if the it is prefixed with O + * From https://tools.ietf.org/html/rfc5280#appendix-A + * ub-organization-name-length INTEGER ::= 64 + * ub-common-name-length INTEGER ::= 64 + * + * http://lxr.free-electrons.com/source/crypto/asymmetric_keys/x509_cert_parser.c?v=3.9#L278 + * ctx->o_size + 2 + ctx->cn_size + 1 + * + 41 characters for ":" and sha1 id + * 64 + 2 + 64 + 1 + 1 + 40 (172) + * setting VERITY_DEFAULT_KEY_ID_LENGTH to 200 characters. + */ +#define VERITY_DEFAULT_KEY_ID_LENGTH 200 + #define FEC_MAGIC 0xFECFECFE #define FEC_BLOCK_SIZE (4 * 1024) #define FEC_VERSION 0 From 71b8f7cc04b10908b12405e3c5e4bc04e46737d3 Mon Sep 17 00:00:00 2001 From: Badhri Jagan Sridharan Date: Fri, 17 Jun 2016 18:54:35 -0700 Subject: [PATCH 0133/1103] ANDROID: dm: android-verity: mount as linear target if eng build eng builds dont have verity enabled i.e it does even have verity metadata appended to the parition. Therefore add rootdev as linear device and map the entire partition if build variant is "eng". (Cherry-picked based on https://partner-android-review.git.corp.google.com/#/c/618690/) BUG: 29276559 Signed-off-by: Badhri Jagan Sridharan Change-Id: I8f5c2289b842b820ca04f5773525e5449bb3f355 --- drivers/md/dm-android-verity.c | 62 +++++++++++++++++++++++++++++++--- drivers/md/dm-android-verity.h | 1 + 2 files changed, 59 insertions(+), 4 deletions(-) diff --git a/drivers/md/dm-android-verity.c b/drivers/md/dm-android-verity.c index 097fb2b1de89..e1a8e284e7e4 100644 --- a/drivers/md/dm-android-verity.c +++ b/drivers/md/dm-android-verity.c @@ -44,6 +44,7 @@ static char verifiedbootstate[VERITY_COMMANDLINE_PARAM_LENGTH]; static char veritymode[VERITY_COMMANDLINE_PARAM_LENGTH]; static char veritykeyid[VERITY_DEFAULT_KEY_ID_LENGTH]; +static char buildvariant[BUILD_VARIANT]; static bool target_added; static bool verity_enabled = true; @@ -88,11 +89,26 @@ static int __init verity_keyid_param(char *line) __setup("veritykeyid=", verity_keyid_param); +static int __init verity_buildvariant(char *line) +{ + strlcpy(buildvariant, line, sizeof(buildvariant)); + return 1; +} + +__setup("buildvariant=", verity_buildvariant); + static inline bool default_verity_key_id(void) { return veritykeyid[0] != '\0'; } +static inline bool is_eng(void) +{ + static const char typeeng[] = "eng"; + + return !strncmp(buildvariant, typeeng, sizeof(typeeng)); +} + static int table_extract_mpi_array(struct public_key_signature *pks, const void *data, size_t len) { @@ -262,7 +278,7 @@ static int extract_fec_header(dev_t dev, struct fec_header *fec, bdev = blkdev_get_by_dev(dev, FMODE_READ, NULL); - if (IS_ERR(bdev)) { + if (IS_ERR_OR_NULL(bdev)) { DMERR("bdev get error"); return PTR_ERR(bdev); } @@ -323,6 +339,24 @@ static void find_metadata_offset(struct fec_header *fec, *metadata_offset = device_size - VERITY_METADATA_SIZE; } +static int find_size(dev_t dev, u64 *device_size) +{ + struct block_device *bdev; + + bdev = blkdev_get_by_dev(dev, FMODE_READ, NULL); + if (IS_ERR_OR_NULL(bdev)) { + DMERR("blkdev_get_by_dev failed"); + return PTR_ERR(bdev); + } + + *device_size = i_size_read(bdev->bd_inode); + *device_size >>= SECTOR_SHIFT; + + DMINFO("blkdev size in sectors: %llu", *device_size); + blkdev_put(bdev, FMODE_READ); + return 0; +} + static struct android_metadata *extract_metadata(dev_t dev, struct fec_header *fec) { @@ -337,7 +371,7 @@ static struct android_metadata *extract_metadata(dev_t dev, bdev = blkdev_get_by_dev(dev, FMODE_READ, NULL); - if (IS_ERR(bdev)) { + if (IS_ERR_OR_NULL(bdev)) { DMERR("blkdev_get_by_dev failed"); return ERR_CAST(bdev); } @@ -632,12 +666,13 @@ static int android_verity_ctr(struct dm_target *ti, unsigned argc, char **argv) struct fec_ecc_metadata uninitialized_var(ecc); char buf[FEC_ARG_LENGTH], *buf_ptr; unsigned long long tmpll; + u64 device_size; if (argc == 1) { /* Use the default keyid */ if (default_verity_key_id()) key_id = veritykeyid; - else { + else if (!is_eng()) { DMERR("veritykeyid= is not set"); handle_error(); return -EINVAL; @@ -650,7 +685,6 @@ static int android_verity_ctr(struct dm_target *ti, unsigned argc, char **argv) return -EINVAL; } - strreplace(key_id, '#', ' '); target_device = argv[0]; dev = name_to_dev_t(target_device); @@ -660,6 +694,26 @@ static int android_verity_ctr(struct dm_target *ti, unsigned argc, char **argv) return -EINVAL; } + if (is_eng()) { + err = find_size(dev, &device_size); + if (err) { + DMERR("error finding bdev size"); + handle_error(); + return err; + } + + ti->len = device_size; + err = add_as_linear_device(ti, target_device); + if (err) { + handle_error(); + return err; + } + verity_enabled = false; + return 0; + } + + strreplace(key_id, '#', ' '); + DMINFO("key:%s dev:%s", key_id, target_device); if (extract_fec_header(dev, &fec, &ecc)) { diff --git a/drivers/md/dm-android-verity.h b/drivers/md/dm-android-verity.h index 43655ee0f813..782e1c815c67 100644 --- a/drivers/md/dm-android-verity.h +++ b/drivers/md/dm-android-verity.h @@ -26,6 +26,7 @@ #define VERITY_METADATA_SIZE (8 * DATA_BLOCK_SIZE) #define VERITY_TABLE_ARGS 10 #define VERITY_COMMANDLINE_PARAM_LENGTH 20 +#define BUILD_VARIANT 20 /* * : is the format for the identifier. From 86842dc5498872915016d5c12c069b51f95d8007 Mon Sep 17 00:00:00 2001 From: Badhri Jagan Sridharan Date: Mon, 27 Jun 2016 16:25:55 -0700 Subject: [PATCH 0134/1103] ANDROID: dm: android-verity: allow adb disable-verity only in userdebug adb disable-verity was allowed when the phone is in the unlocked state. Since the driver is now aware of the build variant, honor "adb disable-verity" only in userdebug builds. (Cherry-picked from https://partner-android-review.git.corp.google.com/#/c/622117) BUG: 29276559 Signed-off-by: Badhri Jagan Sridharan Change-Id: I7ce9f38d8c7a62361392c5a8ccebb288f8a3a2ea --- drivers/md/dm-android-verity.c | 23 +++++++++-------------- 1 file changed, 9 insertions(+), 14 deletions(-) diff --git a/drivers/md/dm-android-verity.c b/drivers/md/dm-android-verity.c index e1a8e284e7e4..999e75bf2ba0 100644 --- a/drivers/md/dm-android-verity.c +++ b/drivers/md/dm-android-verity.c @@ -109,6 +109,14 @@ static inline bool is_eng(void) return !strncmp(buildvariant, typeeng, sizeof(typeeng)); } +static inline bool is_userdebug(void) +{ + static const char typeuserdebug[] = "userdebug"; + + return !strncmp(buildvariant, typeuserdebug, sizeof(typeuserdebug)); +} + + static int table_extract_mpi_array(struct public_key_signature *pks, const void *data, size_t len) { @@ -499,19 +507,6 @@ const char *find_dt_value(const char *name) return value; } -static bool is_unlocked(void) -{ - static const char unlocked[] = "orange"; - static const char verified_boot_prop[] = "verifiedbootstate"; - const char *value; - - value = find_dt_value(verified_boot_prop); - if (!value) - value = verifiedbootstate; - - return !strncmp(value, unlocked, sizeof(unlocked) - 1); -} - static int verity_mode(void) { static const char enforcing[] = "enforcing"; @@ -531,7 +526,7 @@ static int verify_header(struct android_metadata_header *header) { int retval = -EINVAL; - if (is_unlocked() && le32_to_cpu(header->magic_number) == + if (is_userdebug() && le32_to_cpu(header->magic_number) == VERITY_METADATA_MAGIC_DISABLE) { retval = VERITY_STATE_DISABLE; return retval; From 3f7a3b526cedf652d53c15344f316a2c10732a87 Mon Sep 17 00:00:00 2001 From: Badhri Jagan Sridharan Date: Wed, 6 Jul 2016 17:16:19 -0700 Subject: [PATCH 0135/1103] ANDROID: dm: android-verity: Verify header before fetching table Move header validation logic before reading the verity_table as an invalid header implies the table is invalid as well. (Cherry-picked from: https://partner-android-review.git.corp.google.com/#/c/625203) BUG: 29940612 Signed-off-by: Badhri Jagan Sridharan Change-Id: Ib34d25c0854202f3e70df0a6d0ef1d96f0250c8e --- drivers/md/dm-android-verity.c | 140 +++++++++++++++++---------------- 1 file changed, 71 insertions(+), 69 deletions(-) diff --git a/drivers/md/dm-android-verity.c b/drivers/md/dm-android-verity.c index 999e75bf2ba0..1f4eb099209d 100644 --- a/drivers/md/dm-android-verity.c +++ b/drivers/md/dm-android-verity.c @@ -365,12 +365,38 @@ static int find_size(dev_t dev, u64 *device_size) return 0; } -static struct android_metadata *extract_metadata(dev_t dev, - struct fec_header *fec) +static int verify_header(struct android_metadata_header *header) +{ + int retval = -EINVAL; + + if (is_userdebug() && le32_to_cpu(header->magic_number) == + VERITY_METADATA_MAGIC_DISABLE) + return VERITY_STATE_DISABLE; + + if (!(le32_to_cpu(header->magic_number) == + VERITY_METADATA_MAGIC_NUMBER) || + (le32_to_cpu(header->magic_number) == + VERITY_METADATA_MAGIC_DISABLE)) { + DMERR("Incorrect magic number"); + return retval; + } + + if (le32_to_cpu(header->protocol_version) != + VERITY_METADATA_VERSION) { + DMERR("Unsupported version %u", + le32_to_cpu(header->protocol_version)); + return retval; + } + + return 0; +} + +static int extract_metadata(dev_t dev, struct fec_header *fec, + struct android_metadata **metadata, + bool *verity_enabled) { struct block_device *bdev; struct android_metadata_header *header; - struct android_metadata *uninitialized_var(metadata); int i; u32 table_length, copy_length, offset; u64 metadata_offset; @@ -381,7 +407,7 @@ static struct android_metadata *extract_metadata(dev_t dev, if (IS_ERR_OR_NULL(bdev)) { DMERR("blkdev_get_by_dev failed"); - return ERR_CAST(bdev); + return -ENODEV; } find_metadata_offset(fec, bdev, &metadata_offset); @@ -399,7 +425,6 @@ static struct android_metadata *extract_metadata(dev_t dev, (1 << SECTOR_SHIFT), VERITY_METADATA_SIZE); if (err) { DMERR("Error while reading verity metadata"); - metadata = ERR_PTR(err); goto blkdev_release; } @@ -418,24 +443,42 @@ static struct android_metadata *extract_metadata(dev_t dev, le32_to_cpu(header->protocol_version), le32_to_cpu(header->table_length)); - metadata = kzalloc(sizeof(*metadata), GFP_KERNEL); - if (!metadata) { + err = verify_header(header); + + if (err == VERITY_STATE_DISABLE) { + DMERR("Mounting root with verity disabled"); + *verity_enabled = false; + /* we would still have to read the metadata to figure out + * the data blocks size. Or may be could map the entire + * partition similar to mounting the device. + * + * Reset error as well as the verity_enabled flag is changed. + */ + err = 0; + } else if (err) + goto free_header; + + *metadata = kzalloc(sizeof(**metadata), GFP_KERNEL); + if (!*metadata) { DMERR("kzalloc for metadata failed"); err = -ENOMEM; goto free_header; } - metadata->header = header; + (*metadata)->header = header; table_length = le32_to_cpu(header->table_length); if (table_length == 0 || table_length > (VERITY_METADATA_SIZE - - sizeof(struct android_metadata_header))) + sizeof(struct android_metadata_header))) { + DMERR("table_length too long"); + err = -EINVAL; goto free_metadata; + } - metadata->verity_table = kzalloc(table_length + 1, GFP_KERNEL); + (*metadata)->verity_table = kzalloc(table_length + 1, GFP_KERNEL); - if (!metadata->verity_table) { + if (!(*metadata)->verity_table) { DMERR("kzalloc verity_table failed"); err = -ENOMEM; goto free_metadata; @@ -443,13 +486,15 @@ static struct android_metadata *extract_metadata(dev_t dev, if (sizeof(struct android_metadata_header) + table_length <= PAGE_SIZE) { - memcpy(metadata->verity_table, page_address(payload.page_io[0]) + memcpy((*metadata)->verity_table, + page_address(payload.page_io[0]) + sizeof(struct android_metadata_header), table_length); } else { copy_length = PAGE_SIZE - sizeof(struct android_metadata_header); - memcpy(metadata->verity_table, page_address(payload.page_io[0]) + memcpy((*metadata)->verity_table, + page_address(payload.page_io[0]) + sizeof(struct android_metadata_header), copy_length); table_length -= copy_length; @@ -457,13 +502,13 @@ static struct android_metadata *extract_metadata(dev_t dev, i = 1; while (table_length != 0) { if (table_length > PAGE_SIZE) { - memcpy(metadata->verity_table + offset, + memcpy((*metadata)->verity_table + offset, page_address(payload.page_io[i]), PAGE_SIZE); offset += PAGE_SIZE; table_length -= PAGE_SIZE; } else { - memcpy(metadata->verity_table + offset, + memcpy((*metadata)->verity_table + offset, page_address(payload.page_io[i]), table_length); table_length = 0; @@ -471,25 +516,23 @@ static struct android_metadata *extract_metadata(dev_t dev, i++; } } - metadata->verity_table[table_length] = '\0'; + (*metadata)->verity_table[table_length] = '\0'; + DMINFO("verity_table: %s", (*metadata)->verity_table); goto free_payload; free_metadata: - kfree(metadata); + kfree(*metadata); free_header: kfree(header); - metadata = ERR_PTR(err); free_payload: for (i = 0; i < payload.number_of_pages; i++) if (payload.page_io[i]) __free_page(payload.page_io[i]); kfree(payload.page_io); - - DMINFO("verity_table: %s", metadata->verity_table); blkdev_release: blkdev_put(bdev, FMODE_READ); - return metadata; + return err; } /* helper functions to extract properties from dts */ @@ -522,34 +565,6 @@ static int verity_mode(void) return DM_VERITY_MODE_EIO; } -static int verify_header(struct android_metadata_header *header) -{ - int retval = -EINVAL; - - if (is_userdebug() && le32_to_cpu(header->magic_number) == - VERITY_METADATA_MAGIC_DISABLE) { - retval = VERITY_STATE_DISABLE; - return retval; - } - - if (!(le32_to_cpu(header->magic_number) == - VERITY_METADATA_MAGIC_NUMBER) || - (le32_to_cpu(header->magic_number) == - VERITY_METADATA_MAGIC_DISABLE)) { - DMERR("Incorrect magic number"); - return retval; - } - - if (le32_to_cpu(header->protocol_version) != - VERITY_METADATA_VERSION) { - DMERR("Unsupported version %u", - le32_to_cpu(header->protocol_version)); - return retval; - } - - return 0; -} - static int verify_verity_signature(char *key_id, struct android_metadata *metadata) { @@ -649,7 +664,7 @@ static int add_as_linear_device(struct dm_target *ti, char *dev) static int android_verity_ctr(struct dm_target *ti, unsigned argc, char **argv) { dev_t uninitialized_var(dev); - struct android_metadata *uninitialized_var(metadata); + struct android_metadata *metadata = NULL; int err = 0, i, mode; char *key_id, *table_ptr, dummy, *target_device, *verity_table_args[VERITY_TABLE_ARGS + 2 + VERITY_TABLE_OPT_FEC_ARGS]; @@ -717,26 +732,11 @@ static int android_verity_ctr(struct dm_target *ti, unsigned argc, char **argv) return -EINVAL; } - metadata = extract_metadata(dev, &fec); + err = extract_metadata(dev, &fec, &metadata, &verity_enabled); - if (IS_ERR(metadata)) { + if (err) { DMERR("Error while extracting metadata"); handle_error(); - return -EINVAL; - } - - err = verify_header(metadata->header); - - if (err == VERITY_STATE_DISABLE) { - DMERR("Mounting root with verity disabled"); - verity_enabled = false; - /* we would still have to parse the args to figure out - * the data blocks size. Or may be could map the entire - * partition similar to mounting the device. - */ - } else if (err) { - DMERR("Verity header handle error"); - handle_error(); goto free_metadata; } @@ -869,8 +869,10 @@ static int android_verity_ctr(struct dm_target *ti, unsigned argc, char **argv) } free_metadata: - kfree(metadata->header); - kfree(metadata->verity_table); + if (metadata) { + kfree(metadata->header); + kfree(metadata->verity_table); + } kfree(metadata); return err; } From 5ba582cc82f41cedf5e9d2feaeb483c05161d530 Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Tue, 10 May 2016 13:10:20 +0200 Subject: [PATCH 0136/1103] ANDROID: dm: android-verity: pack the fec_header structure The fec_header structure is generated build time and stored on disk. The fec_header might be build on a 64 bits machine while it is read per a 32 bits device or the other way around. In such situations, the fec_header fields are not aligned as expected by the device and it fails to read the fec_header structure. This patch makes the fec_header packed. Change-Id: Idb84453e70cc11abd5ef3a0adfbb16f8b5feaf06 Signed-off-by: Jeremy Compostella --- drivers/md/dm-android-verity.h | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/drivers/md/dm-android-verity.h b/drivers/md/dm-android-verity.h index 782e1c815c67..f43b02fbb475 100644 --- a/drivers/md/dm-android-verity.h +++ b/drivers/md/dm-android-verity.h @@ -72,9 +72,6 @@ * if fec is not present * */ -/* TODO: rearrange structure to reduce memory holes - * depends on userspace change. - */ struct fec_header { __le32 magic; __le32 version; @@ -83,7 +80,7 @@ struct fec_header { __le32 fec_size; __le64 inp_size; u8 hash[SHA256_DIGEST_SIZE]; -}; +} __attribute__((packed)); struct android_metadata_header { __le32 magic_number; From bdceabacb248c2d4940403b5f5eb8618e883a9c9 Mon Sep 17 00:00:00 2001 From: Badhri Jagan Sridharan Date: Tue, 9 Aug 2016 12:47:37 -0700 Subject: [PATCH 0137/1103] ANDROID: dm: android-verity: adopt changes made to dm callbacks v4.4 introduced changes to the callbacks used for dm-linear and dm-verity-target targets. Move to those headers in dm-android-verity. Verified on hikey while having BOARD_USES_RECOVERY_AS_BOOT := true BOARD_BUILD_SYSTEM_ROOT_IMAGE := true BUG: 27339727 Signed-off-by: Badhri Jagan Sridharan Change-Id: Ic64950c3b55f0a6eaa570bcedc2ace83bbf3005e --- drivers/md/dm-android-verity.c | 12 +++++------- drivers/md/dm-android-verity.h | 5 +---- drivers/md/dm-linear.c | 2 +- drivers/md/dm-verity-target.c | 2 +- drivers/md/dm-verity.h | 5 +---- 5 files changed, 9 insertions(+), 17 deletions(-) diff --git a/drivers/md/dm-android-verity.c b/drivers/md/dm-android-verity.c index 1f4eb099209d..15ce2a81c1f4 100644 --- a/drivers/md/dm-android-verity.c +++ b/drivers/md/dm-android-verity.c @@ -59,8 +59,7 @@ static struct target_type android_verity_target = { .dtr = verity_dtr, .map = verity_map, .status = verity_status, - .ioctl = verity_ioctl, - .merge = verity_merge, + .prepare_ioctl = verity_prepare_ioctl, .iterate_devices = verity_iterate_devices, .io_hints = verity_io_hints, }; @@ -637,8 +636,7 @@ static int add_as_linear_device(struct dm_target *ti, char *dev) android_verity_target.dtr = dm_linear_dtr, android_verity_target.map = dm_linear_map, android_verity_target.status = dm_linear_status, - android_verity_target.ioctl = dm_linear_ioctl, - android_verity_target.merge = dm_linear_merge, + android_verity_target.prepare_ioctl = dm_linear_prepare_ioctl, android_verity_target.iterate_devices = dm_linear_iterate_devices, android_verity_target.io_hints = NULL; @@ -676,7 +674,7 @@ static int android_verity_ctr(struct dm_target *ti, unsigned argc, char **argv) struct fec_ecc_metadata uninitialized_var(ecc); char buf[FEC_ARG_LENGTH], *buf_ptr; unsigned long long tmpll; - u64 device_size; + u64 uninitialized_var(device_size); if (argc == 1) { /* Use the default keyid */ @@ -896,7 +894,7 @@ static int __init dm_android_verity_init(void) } file = debugfs_create_bool("target_added", S_IRUGO, debug_dir, - (u32 *)&target_added); + &target_added); if (IS_ERR_OR_NULL(file)) { DMERR("Cannot create android_verity debugfs directory: %ld", @@ -906,7 +904,7 @@ static int __init dm_android_verity_init(void) } file = debugfs_create_bool("verity_enabled", S_IRUGO, debug_dir, - (u32 *)&verity_enabled); + &verity_enabled); if (IS_ERR_OR_NULL(file)) { DMERR("Cannot create android_verity debugfs directory: %ld", diff --git a/drivers/md/dm-android-verity.h b/drivers/md/dm-android-verity.h index f43b02fbb475..07e22bc8955c 100644 --- a/drivers/md/dm-android-verity.h +++ b/drivers/md/dm-android-verity.h @@ -113,10 +113,7 @@ extern void dm_linear_dtr(struct dm_target *ti); extern int dm_linear_map(struct dm_target *ti, struct bio *bio); extern void dm_linear_status(struct dm_target *ti, status_type_t type, unsigned status_flags, char *result, unsigned maxlen); -extern int dm_linear_ioctl(struct dm_target *ti, unsigned int cmd, - unsigned long arg); -extern int dm_linear_merge(struct dm_target *ti, struct bvec_merge_data *bvm, - struct bio_vec *biovec, int max_size); +extern int dm_linear_prepare_ioctl(struct dm_target *ti, struct block_device **bdev); extern int dm_linear_iterate_devices(struct dm_target *ti, iterate_devices_callout_fn fn, void *data); extern int dm_linear_ctr(struct dm_target *ti, unsigned int argc, char **argv); diff --git a/drivers/md/dm-linear.c b/drivers/md/dm-linear.c index 187574311a13..5be148922e33 100644 --- a/drivers/md/dm-linear.c +++ b/drivers/md/dm-linear.c @@ -130,7 +130,7 @@ void dm_linear_status(struct dm_target *ti, status_type_t type, } } -static int dm_linear_prepare_ioctl(struct dm_target *ti, struct block_device **bdev) +int dm_linear_prepare_ioctl(struct dm_target *ti, struct block_device **bdev) { struct linear_c *lc = (struct linear_c *) ti->private; struct dm_dev *dev = lc->dev; diff --git a/drivers/md/dm-verity-target.c b/drivers/md/dm-verity-target.c index fc7895c6dd5d..58fbf52fe38e 100644 --- a/drivers/md/dm-verity-target.c +++ b/drivers/md/dm-verity-target.c @@ -739,7 +739,7 @@ void verity_status(struct dm_target *ti, status_type_t type, } } -static int verity_prepare_ioctl(struct dm_target *ti, struct block_device **bdev) +int verity_prepare_ioctl(struct dm_target *ti, struct block_device **bdev) { struct dm_verity *v = ti->private; diff --git a/drivers/md/dm-verity.h b/drivers/md/dm-verity.h index b07e2e5bd952..0f634e49f63b 100644 --- a/drivers/md/dm-verity.h +++ b/drivers/md/dm-verity.h @@ -129,10 +129,7 @@ extern int verity_hash_for_block(struct dm_verity *v, struct dm_verity_io *io, extern void verity_status(struct dm_target *ti, status_type_t type, unsigned status_flags, char *result, unsigned maxlen); -extern int verity_ioctl(struct dm_target *ti, unsigned cmd, - unsigned long arg); -extern int verity_merge(struct dm_target *ti, struct bvec_merge_data *bvm, - struct bio_vec *biovec, int max_size); +extern int verity_prepare_ioctl(struct dm_target *ti, struct block_device **bdev); extern int verity_iterate_devices(struct dm_target *ti, iterate_devices_callout_fn fn, void *data); extern void verity_io_hints(struct dm_target *ti, struct queue_limits *limits); From be0937045fa3ca1e9c910dbe0cfcc3bfde5a46df Mon Sep 17 00:00:00 2001 From: Badhri Jagan Sridharan Date: Tue, 27 Sep 2016 13:48:29 -0700 Subject: [PATCH 0138/1103] ANDROID: dm: android-verity: Remove fec_header location constraint This CL removes the mandate of the fec_header being located right after the ECC data. (Cherry-picked from https://android-review.googlesource.com/#/c/280401) Bug: 28865197 Signed-off-by: Badhri Jagan Sridharan Change-Id: Ie04c8cf2dd755f54d02dbdc4e734a13d6f6507b5 --- drivers/md/dm-android-verity.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/drivers/md/dm-android-verity.c b/drivers/md/dm-android-verity.c index 15ce2a81c1f4..bb6c1285e499 100644 --- a/drivers/md/dm-android-verity.c +++ b/drivers/md/dm-android-verity.c @@ -266,10 +266,7 @@ static inline int validate_fec_header(struct fec_header *header, u64 offset) le32_to_cpu(header->version) != FEC_VERSION || le32_to_cpu(header->size) != sizeof(struct fec_header) || le32_to_cpu(header->roots) == 0 || - le32_to_cpu(header->roots) >= FEC_RSM || - offset < le32_to_cpu(header->fec_size) || - offset - le32_to_cpu(header->fec_size) != - le64_to_cpu(header->inp_size)) + le32_to_cpu(header->roots) >= FEC_RSM) return -EINVAL; return 0; From 4e4e3c7b23227f90cd7d707909130486f215ed69 Mon Sep 17 00:00:00 2001 From: Badhri Jagan Sridharan Date: Fri, 13 Jan 2017 11:05:00 -0800 Subject: [PATCH 0139/1103] ANDROID: dm: android-verity: rebase for 4.9 Export the direct_access method of dm_linear target for dm-android-verity target. Signed-off-by: Badhri Jagan Sridharan Change-Id: I46556d882305e5194352946264cbc9c06e5038e4 [AmitP: Rebased the changes for v4.14] Signed-off-by: Amit Pundir --- drivers/md/dm-android-verity.c | 3 +++ drivers/md/dm-android-verity.h | 11 +++++++++++ drivers/md/dm-linear.c | 25 ++++++++++++++----------- 3 files changed, 28 insertions(+), 11 deletions(-) diff --git a/drivers/md/dm-android-verity.c b/drivers/md/dm-android-verity.c index bb6c1285e499..6f616fdf3b73 100644 --- a/drivers/md/dm-android-verity.c +++ b/drivers/md/dm-android-verity.c @@ -633,8 +633,11 @@ static int add_as_linear_device(struct dm_target *ti, char *dev) android_verity_target.dtr = dm_linear_dtr, android_verity_target.map = dm_linear_map, android_verity_target.status = dm_linear_status, + android_verity_target.end_io = dm_linear_end_io, android_verity_target.prepare_ioctl = dm_linear_prepare_ioctl, android_verity_target.iterate_devices = dm_linear_iterate_devices, + android_verity_target.direct_access = dm_linear_dax_direct_access, + android_verity_target.dax_copy_from_iter = dm_linear_dax_copy_from_iter, android_verity_target.io_hints = NULL; err = dm_linear_ctr(ti, DM_LINEAR_ARGS, linear_table_args); diff --git a/drivers/md/dm-android-verity.h b/drivers/md/dm-android-verity.h index 07e22bc8955c..a637accefb5b 100644 --- a/drivers/md/dm-android-verity.h +++ b/drivers/md/dm-android-verity.h @@ -111,10 +111,21 @@ extern struct target_type linear_target; extern void dm_linear_dtr(struct dm_target *ti); extern int dm_linear_map(struct dm_target *ti, struct bio *bio); +extern int dm_linear_end_io(struct dm_target *ti, struct bio *bio, + blk_status_t *error); extern void dm_linear_status(struct dm_target *ti, status_type_t type, unsigned status_flags, char *result, unsigned maxlen); extern int dm_linear_prepare_ioctl(struct dm_target *ti, struct block_device **bdev); extern int dm_linear_iterate_devices(struct dm_target *ti, iterate_devices_callout_fn fn, void *data); extern int dm_linear_ctr(struct dm_target *ti, unsigned int argc, char **argv); +#if IS_ENABLED(CONFIG_DAX_DRIVER) +extern long dm_linear_dax_direct_access(struct dm_target *ti, sector_t sector, + void **kaddr, pfn_t *pfn, long size); +extern size_t dm_linear_dax_copy_from_iter(struct dm_target *ti, pgoff_t pgoff, + void *addr, size_t bytes, struct iov_iter *i); +#else +#define dm_linear_dax_direct_access NULL +#define dm_linear_dax_copy_from_iter NULL +#endif #endif /* DM_ANDROID_VERITY_H */ diff --git a/drivers/md/dm-linear.c b/drivers/md/dm-linear.c index 5be148922e33..ae9d111f519f 100644 --- a/drivers/md/dm-linear.c +++ b/drivers/md/dm-linear.c @@ -102,7 +102,7 @@ int dm_linear_map(struct dm_target *ti, struct bio *bio) return DM_MAPIO_REMAPPED; } -static int linear_end_io(struct dm_target *ti, struct bio *bio, +int dm_linear_end_io(struct dm_target *ti, struct bio *bio, blk_status_t *error) { struct linear_c *lc = ti->private; @@ -112,6 +112,7 @@ static int linear_end_io(struct dm_target *ti, struct bio *bio, return DM_ENDIO_DONE; } +EXPORT_SYMBOL_GPL(dm_linear_end_io); void dm_linear_status(struct dm_target *ti, status_type_t type, unsigned status_flags, char *result, unsigned maxlen) @@ -155,7 +156,7 @@ int dm_linear_iterate_devices(struct dm_target *ti, } #if IS_ENABLED(CONFIG_DAX_DRIVER) -static long linear_dax_direct_access(struct dm_target *ti, pgoff_t pgoff, +long dm_linear_dax_direct_access(struct dm_target *ti, pgoff_t pgoff, long nr_pages, void **kaddr, pfn_t *pfn) { long ret; @@ -170,8 +171,9 @@ static long linear_dax_direct_access(struct dm_target *ti, pgoff_t pgoff, return ret; return dax_direct_access(dax_dev, pgoff, nr_pages, kaddr, pfn); } +EXPORT_SYMBOL_GPL(dm_linear_dax_direct_access); -static size_t linear_dax_copy_from_iter(struct dm_target *ti, pgoff_t pgoff, +size_t dm_linear_dax_copy_from_iter(struct dm_target *ti, pgoff_t pgoff, void *addr, size_t bytes, struct iov_iter *i) { struct linear_c *lc = ti->private; @@ -184,8 +186,9 @@ static size_t linear_dax_copy_from_iter(struct dm_target *ti, pgoff_t pgoff, return 0; return dax_copy_from_iter(dax_dev, pgoff, addr, bytes, i); } +EXPORT_SYMBOL_GPL(dm_linear_dax_copy_from_iter); -static size_t linear_dax_copy_to_iter(struct dm_target *ti, pgoff_t pgoff, +static size_t dm_linear_dax_copy_to_iter(struct dm_target *ti, pgoff_t pgoff, void *addr, size_t bytes, struct iov_iter *i) { struct linear_c *lc = ti->private; @@ -200,9 +203,9 @@ static size_t linear_dax_copy_to_iter(struct dm_target *ti, pgoff_t pgoff, } #else -#define linear_dax_direct_access NULL -#define linear_dax_copy_from_iter NULL -#define linear_dax_copy_to_iter NULL +#define dm_linear_dax_direct_access NULL +#define dm_linear_dax_copy_from_iter NULL +#define dm_linear_dax_copy_to_iter NULL #endif static struct target_type linear_target = { @@ -214,12 +217,12 @@ static struct target_type linear_target = { .dtr = dm_linear_dtr, .map = dm_linear_map, .status = dm_linear_status, - .end_io = linear_end_io, + .end_io = dm_linear_end_io, .prepare_ioctl = dm_linear_prepare_ioctl, .iterate_devices = dm_linear_iterate_devices, - .direct_access = linear_dax_direct_access, - .dax_copy_from_iter = linear_dax_copy_from_iter, - .dax_copy_to_iter = linear_dax_copy_to_iter, + .direct_access = dm_linear_dax_direct_access, + .dax_copy_from_iter = dm_linear_dax_copy_from_iter, + .dax_copy_to_iter = dm_linear_dax_copy_to_iter, }; int __init dm_linear_init(void) From 7294b87d9ba9558d716b23b2b821b4347bb6d9f4 Mon Sep 17 00:00:00 2001 From: Greg Hackmann Date: Mon, 14 Nov 2016 09:48:02 -0800 Subject: [PATCH 0140/1103] ANDROID: dm: android-verity: fix table_make_digest() error handling If table_make_digest() fails, verify_verity_signature() would try to pass the returned ERR_PTR() to kfree(). This fixes the smatch error: drivers/md/dm-android-verity.c:601 verify_verity_signature() error: 'pks' dereferencing possible ERR_PTR() Change-Id: I9b9b7764b538cb4a5f94337660e9b0f149b139be Signed-off-by: Greg Hackmann --- drivers/md/dm-android-verity.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/md/dm-android-verity.c b/drivers/md/dm-android-verity.c index 6f616fdf3b73..ac73db367d17 100644 --- a/drivers/md/dm-android-verity.c +++ b/drivers/md/dm-android-verity.c @@ -585,6 +585,8 @@ static int verify_verity_signature(char *key_id, if (IS_ERR(pks)) { DMERR("hashing failed"); + retval = PTR_ERR(pks); + pks = NULL; goto error; } From e17693aed27ac4e5b2e66c3f390a928c11d3f682 Mon Sep 17 00:00:00 2001 From: Bowgo Tsai Date: Thu, 2 Mar 2017 18:54:15 +0800 Subject: [PATCH 0141/1103] ANDROID: dm: android-verity: allow disable dm-verity for Treble VTS To start Treble VTS test, a single AOSP system.img will be flashed onto the device. The size of AOSP system.img might be different than the system partition size on device, making locating verity metadata fail (at the last fixed size of the partition). This change allows disabling dm-verity on system partition when the device is unlocked (orange device state) with invalid metadata. BUG: 35603549 Test: boot device with a different-sized system.img, checks verity is not enabled via: "adb shell getprop | grep partition.system.verified" Change-Id: Ide78dca4eefde4ab019e4b202d3f590dcb1bb506 Signed-off-by: Bowgo Tsai --- drivers/md/dm-android-verity.c | 53 ++++++++++++++++++++++------------ 1 file changed, 35 insertions(+), 18 deletions(-) diff --git a/drivers/md/dm-android-verity.c b/drivers/md/dm-android-verity.c index ac73db367d17..b2665f569396 100644 --- a/drivers/md/dm-android-verity.c +++ b/drivers/md/dm-android-verity.c @@ -115,6 +115,12 @@ static inline bool is_userdebug(void) return !strncmp(buildvariant, typeuserdebug, sizeof(typeuserdebug)); } +static inline bool is_unlocked(void) +{ + static const char unlocked[] = "orange"; + + return !strncmp(verifiedbootstate, unlocked, sizeof(unlocked)); +} static int table_extract_mpi_array(struct public_key_signature *pks, const void *data, size_t len) @@ -653,6 +659,28 @@ static int add_as_linear_device(struct dm_target *ti, char *dev) return err; } +static int create_linear_device(struct dm_target *ti, dev_t dev, + char *target_device) +{ + u64 device_size = 0; + int err = find_size(dev, &device_size); + + if (err) { + DMERR("error finding bdev size"); + handle_error(); + return err; + } + + ti->len = device_size; + err = add_as_linear_device(ti, target_device); + if (err) { + handle_error(); + return err; + } + verity_enabled = false; + return 0; +} + /* * Target parameters: * Key id of the public key in the system keyring. @@ -676,7 +704,6 @@ static int android_verity_ctr(struct dm_target *ti, unsigned argc, char **argv) struct fec_ecc_metadata uninitialized_var(ecc); char buf[FEC_ARG_LENGTH], *buf_ptr; unsigned long long tmpll; - u64 uninitialized_var(device_size); if (argc == 1) { /* Use the default keyid */ @@ -704,23 +731,8 @@ static int android_verity_ctr(struct dm_target *ti, unsigned argc, char **argv) return -EINVAL; } - if (is_eng()) { - err = find_size(dev, &device_size); - if (err) { - DMERR("error finding bdev size"); - handle_error(); - return err; - } - - ti->len = device_size; - err = add_as_linear_device(ti, target_device); - if (err) { - handle_error(); - return err; - } - verity_enabled = false; - return 0; - } + if (is_eng()) + return create_linear_device(ti, dev, target_device); strreplace(key_id, '#', ' '); @@ -735,6 +747,11 @@ static int android_verity_ctr(struct dm_target *ti, unsigned argc, char **argv) err = extract_metadata(dev, &fec, &metadata, &verity_enabled); if (err) { + /* Allow invalid metadata when the device is unlocked */ + if (is_unlocked()) { + DMWARN("Allow invalid metadata when unlocked"); + return create_linear_device(ti, dev, target_device); + } DMERR("Error while extracting metadata"); handle_error(); goto free_metadata; From 27893632985e9cae76bcffae83bfe71cd2bcb3fc Mon Sep 17 00:00:00 2001 From: Badhri Jagan Sridharan Date: Fri, 3 Jun 2016 13:16:59 -0700 Subject: [PATCH 0142/1103] ANDROID: dm: android-verity: mark dev as rw for linear target Mark as rw when adding as linear target to allow changes to the underlying filesystem through adb disable verity and adb remount. (Cherry-picked from https://partner-android-review.googlesource.com/#/c/613573/ 79a3032bb62da65a5d724eb70c8bdc662945d475) BUG: 28845874 Signed-off-by: Badhri Jagan Sridharan Change-Id: If41e9cad8e0f054f4778c09a6e2f0cb8af6fddaf --- drivers/md/dm-android-verity.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/md/dm-android-verity.c b/drivers/md/dm-android-verity.c index b2665f569396..0dd69244f77c 100644 --- a/drivers/md/dm-android-verity.c +++ b/drivers/md/dm-android-verity.c @@ -648,6 +648,8 @@ static int add_as_linear_device(struct dm_target *ti, char *dev) android_verity_target.dax_copy_from_iter = dm_linear_dax_copy_from_iter, android_verity_target.io_hints = NULL; + set_disk_ro(dm_disk(dm_table_get_md(ti->table)), 0); + err = dm_linear_ctr(ti, DM_LINEAR_ARGS, linear_table_args); if (!err) { From c8b567866d7baff2b0a1589c4a192ed3144b7b7f Mon Sep 17 00:00:00 2001 From: Keun-young Park Date: Mon, 14 Nov 2016 18:25:15 -0800 Subject: [PATCH 0143/1103] ANDROID: dm: verity: add minimum prefetch size - For device like eMMC, it gives better performance to read more hash blocks at a time. - For android, set it to default 128. For other devices, set it to 1 which is the same as now. - saved boot-up time by 300ms in tested device bug: 32246564 Change-Id: Ibc0401a0cddba64b862a80445844b4e595213621 Cc: Sami Tolvanen Signed-off-by: Keun-young Park --- drivers/md/Kconfig | 16 ++++++++++++++++ drivers/md/dm-verity-target.c | 9 ++++++++- 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/drivers/md/Kconfig b/drivers/md/Kconfig index 7601e36a9ee0..44d56f41660f 100644 --- a/drivers/md/Kconfig +++ b/drivers/md/Kconfig @@ -479,6 +479,21 @@ config DM_VERITY If unsure, say N. +config DM_VERITY_HASH_PREFETCH_MIN_SIZE_128 + bool "Prefetch size 128" + +config DM_VERITY_HASH_PREFETCH_MIN_SIZE + int "Verity hash prefetch minimum size" + depends on DM_VERITY + range 1 4096 + default 128 if DM_VERITY_HASH_PREFETCH_MIN_SIZE_128 + default 1 + ---help--- + This sets minimum number of hash blocks to prefetch for dm-verity. + For devices like eMMC, having larger prefetch size like 128 can improve + performance with increased memory consumption for keeping more hashes + in RAM. + config DM_VERITY_FEC bool "Verity forward error correction support" depends on DM_VERITY @@ -568,6 +583,7 @@ config DM_ANDROID_VERITY depends on KEYS depends on ASYMMETRIC_KEY_TYPE depends on ASYMMETRIC_PUBLIC_KEY_SUBTYPE + select DM_VERITY_HASH_PREFETCH_MIN_SIZE_128 ---help--- This device-mapper target is virtually a VERITY target. This target is setup by reading the metadata contents piggybacked diff --git a/drivers/md/dm-verity-target.c b/drivers/md/dm-verity-target.c index 58fbf52fe38e..427cabe675a8 100644 --- a/drivers/md/dm-verity-target.c +++ b/drivers/md/dm-verity-target.c @@ -580,6 +580,7 @@ static void verity_prefetch_io(struct work_struct *work) container_of(work, struct dm_verity_prefetch_work, work); struct dm_verity *v = pw->v; int i; + sector_t prefetch_size; for (i = v->levels - 2; i >= 0; i--) { sector_t hash_block_start; @@ -602,8 +603,14 @@ static void verity_prefetch_io(struct work_struct *work) hash_block_end = v->hash_blocks - 1; } no_prefetch_cluster: + // for emmc, it is more efficient to send bigger read + prefetch_size = max((sector_t)CONFIG_DM_VERITY_HASH_PREFETCH_MIN_SIZE, + hash_block_end - hash_block_start + 1); + if ((hash_block_start + prefetch_size) >= (v->hash_start + v->hash_blocks)) { + prefetch_size = hash_block_end - hash_block_start + 1; + } dm_bufio_prefetch(v->bufio, hash_block_start, - hash_block_end - hash_block_start + 1); + prefetch_size); } kfree(pw); From 5777213387ee7f5cf75e5fb0c6bc8feec36b05ec Mon Sep 17 00:00:00 2001 From: Colin Cross Date: Mon, 6 May 2013 23:50:16 +0000 Subject: [PATCH 0144/1103] ANDROID: fs: epoll: use freezable blocking call Avoid waking up every thread sleeping in an epoll_wait call during suspend and resume by calling a freezable blocking call. Previous patches modified the freezer to avoid sending wakeups to threads that are blocked in freezable blocking calls. This call was selected to be converted to a freezable call because it doesn't hold any locks or release any resources when interrupted that might be needed by another freezing task or a kernel driver during suspend, and is a common site where idle userspace tasks are blocked. Change-Id: I848d08d28c89302fd42bbbdfa76489a474ab27bf Acked-by: Tejun Heo Signed-off-by: Colin Cross Signed-off-by: Rafael J. Wysocki --- fs/eventpoll.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/fs/eventpoll.c b/fs/eventpoll.c index 42bbe6824b4b..779b74160280 100644 --- a/fs/eventpoll.c +++ b/fs/eventpoll.c @@ -34,6 +34,7 @@ #include #include #include +#include #include #include #include @@ -1816,7 +1817,8 @@ static int ep_poll(struct eventpoll *ep, struct epoll_event __user *events, } spin_unlock_irq(&ep->wq.lock); - if (!schedule_hrtimeout_range(to, slack, HRTIMER_MODE_ABS)) + if (!freezable_schedule_hrtimeout_range(to, slack, + HRTIMER_MODE_ABS)) timed_out = 1; spin_lock_irq(&ep->wq.lock); From 30d9ae86330968e70dbe868293de7f5bf6076130 Mon Sep 17 00:00:00 2001 From: Jin Qian Date: Thu, 2 Mar 2017 13:32:59 -0800 Subject: [PATCH 0145/1103] ANDROID: fs: sched: add a counter to track fsync Change-Id: I6c138de5b2332eea70f57e098134d1d141247b3f Signed-off-by: Jin Qian [AmitP: Refactored changes to align with changes from upstream commit 9a07000400c8 ("sched/headers: Move CONFIG_TASK_XACCT bits from to ")] Signed-off-by: Amit Pundir --- fs/sync.c | 3 ++- include/linux/sched/xacct.h | 9 +++++++++ include/linux/task_io_accounting.h | 2 ++ include/linux/task_io_accounting_ops.h | 1 + 4 files changed, 14 insertions(+), 1 deletion(-) diff --git a/fs/sync.c b/fs/sync.c index b54e0541ad89..055daab8652a 100644 --- a/fs/sync.c +++ b/fs/sync.c @@ -9,7 +9,7 @@ #include #include #include -#include +#include #include #include #include @@ -220,6 +220,7 @@ static int do_fsync(unsigned int fd, int datasync) if (f.file) { ret = vfs_fsync(f.file, datasync); fdput(f); + inc_syscfs(current); } return ret; } diff --git a/include/linux/sched/xacct.h b/include/linux/sched/xacct.h index c078f0a94cec..9544c9d9d534 100644 --- a/include/linux/sched/xacct.h +++ b/include/linux/sched/xacct.h @@ -28,6 +28,11 @@ static inline void inc_syscw(struct task_struct *tsk) { tsk->ioac.syscw++; } + +static inline void inc_syscfs(struct task_struct *tsk) +{ + tsk->ioac.syscfs++; +} #else static inline void add_rchar(struct task_struct *tsk, ssize_t amt) { @@ -44,6 +49,10 @@ static inline void inc_syscr(struct task_struct *tsk) static inline void inc_syscw(struct task_struct *tsk) { } + +static inline void inc_syscfs(struct task_struct *tsk) +{ +} #endif #endif /* _LINUX_SCHED_XACCT_H */ diff --git a/include/linux/task_io_accounting.h b/include/linux/task_io_accounting.h index 6f6acce064de..bb26108ca23c 100644 --- a/include/linux/task_io_accounting.h +++ b/include/linux/task_io_accounting.h @@ -19,6 +19,8 @@ struct task_io_accounting { u64 syscr; /* # of write syscalls */ u64 syscw; + /* # of fsync syscalls */ + u64 syscfs; #endif /* CONFIG_TASK_XACCT */ #ifdef CONFIG_TASK_IO_ACCOUNTING diff --git a/include/linux/task_io_accounting_ops.h b/include/linux/task_io_accounting_ops.h index bb5498bcdd96..733ab62ae141 100644 --- a/include/linux/task_io_accounting_ops.h +++ b/include/linux/task_io_accounting_ops.h @@ -97,6 +97,7 @@ static inline void task_chr_io_accounting_add(struct task_io_accounting *dst, dst->wchar += src->wchar; dst->syscr += src->syscr; dst->syscw += src->syscw; + dst->syscfs += src->syscfs; } #else static inline void task_chr_io_accounting_add(struct task_io_accounting *dst, From acb933bb6e4f1ba5aead9ff6ba8133d51f14442e Mon Sep 17 00:00:00 2001 From: Mohan Srinivasan Date: Wed, 14 Dec 2016 16:39:51 -0800 Subject: [PATCH 0146/1103] ANDROID: fs: FS tracepoints to track IO. Adds tracepoints in ext4/f2fs/mpage to track readpages/buffered write()s. This allows us to track files that are being read/written to PIDs. (Merged from android4.4-common). Signed-off-by: Mohan Srinivasan --- fs/ext4/inline.c | 6 ++ fs/ext4/inode.c | 28 ++++++++ fs/ext4/readpage.c | 41 +++++++++-- fs/f2fs/data.c | 21 ++++++ fs/f2fs/inline.c | 11 +++ fs/mpage.c | 30 ++++++++ include/trace/events/android_fs.h | 31 +++++++++ include/trace/events/android_fs_template.h | 79 ++++++++++++++++++++++ 8 files changed, 243 insertions(+), 4 deletions(-) create mode 100644 include/trace/events/android_fs.h create mode 100644 include/trace/events/android_fs_template.h diff --git a/fs/ext4/inline.c b/fs/ext4/inline.c index 7b4736022761..09c8c3fd8491 100644 --- a/fs/ext4/inline.c +++ b/fs/ext4/inline.c @@ -12,6 +12,7 @@ #include "ext4.h" #include "xattr.h" #include "truncate.h" +#include #define EXT4_XATTR_SYSTEM_DATA "data" #define EXT4_MIN_INLINE_DATA_SIZE ((sizeof(__le32) * EXT4_N_BLOCKS)) @@ -505,6 +506,9 @@ int ext4_readpage_inline(struct inode *inode, struct page *page) return -EAGAIN; } + trace_android_fs_dataread_start(inode, page_offset(page), PAGE_SIZE, + current->pid, current->comm); + /* * Current inline data can only exist in the 1st page, * So for all the other pages, just set them uptodate. @@ -516,6 +520,8 @@ int ext4_readpage_inline(struct inode *inode, struct page *page) SetPageUptodate(page); } + trace_android_fs_dataread_end(inode, page_offset(page), PAGE_SIZE); + up_read(&EXT4_I(inode)->xattr_sem); unlock_page(page); diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c index d767e993591d..2f98c77e6d2a 100644 --- a/fs/ext4/inode.c +++ b/fs/ext4/inode.c @@ -47,6 +47,7 @@ #include "truncate.h" #include +#include #define MPAGE_DA_EXTENT_TAIL 0x01 @@ -1253,6 +1254,8 @@ static int ext4_write_begin(struct file *file, struct address_space *mapping, if (unlikely(ext4_forced_shutdown(EXT4_SB(inode->i_sb)))) return -EIO; + trace_android_fs_datawrite_start(inode, pos, len, + current->pid, current->comm); trace_ext4_write_begin(inode, pos, len, flags); /* * Reserve one block more for addition to orphan list in case @@ -1391,6 +1394,7 @@ static int ext4_write_end(struct file *file, int i_size_changed = 0; int inline_data = ext4_has_inline_data(inode); + trace_android_fs_datawrite_end(inode, pos, len); trace_ext4_write_end(inode, pos, len, copied); if (inline_data) { ret = ext4_write_inline_data_end(inode, pos, len, @@ -1496,6 +1500,7 @@ static int ext4_journalled_write_end(struct file *file, int size_changed = 0; int inline_data = ext4_has_inline_data(inode); + trace_android_fs_datawrite_end(inode, pos, len); trace_ext4_journalled_write_end(inode, pos, len, copied); from = pos & (PAGE_SIZE - 1); to = from + len; @@ -3024,6 +3029,8 @@ static int ext4_da_write_begin(struct file *file, struct address_space *mapping, len, flags, pagep, fsdata); } *fsdata = (void *)0; + trace_android_fs_datawrite_start(inode, pos, len, + current->pid, current->comm); trace_ext4_da_write_begin(inode, pos, len, flags); if (ext4_test_inode_state(inode, EXT4_STATE_MAY_INLINE_DATA)) { @@ -3142,6 +3149,7 @@ static int ext4_da_write_end(struct file *file, return ext4_write_end(file, mapping, pos, len, copied, page, fsdata); + trace_android_fs_datawrite_end(inode, pos, len); trace_ext4_da_write_end(inode, pos, len, copied); start = pos & (PAGE_SIZE - 1); end = start + copied - 1; @@ -3847,6 +3855,7 @@ static ssize_t ext4_direct_IO(struct kiocb *iocb, struct iov_iter *iter) size_t count = iov_iter_count(iter); loff_t offset = iocb->ki_pos; ssize_t ret; + int rw = iov_iter_rw(iter); #ifdef CONFIG_EXT4_FS_ENCRYPTION if (ext4_encrypted_inode(inode) && S_ISREG(inode->i_mode)) @@ -3863,12 +3872,31 @@ static ssize_t ext4_direct_IO(struct kiocb *iocb, struct iov_iter *iter) if (ext4_has_inline_data(inode)) return 0; + if (trace_android_fs_dataread_start_enabled() && + (rw == READ)) + trace_android_fs_dataread_start(inode, offset, count, + current->pid, + current->comm); + if (trace_android_fs_datawrite_start_enabled() && + (rw == WRITE)) + trace_android_fs_datawrite_start(inode, offset, count, + current->pid, + current->comm); + trace_ext4_direct_IO_enter(inode, offset, count, iov_iter_rw(iter)); if (iov_iter_rw(iter) == READ) ret = ext4_direct_IO_read(iocb, iter); else ret = ext4_direct_IO_write(iocb, iter); trace_ext4_direct_IO_exit(inode, offset, count, iov_iter_rw(iter), ret); + + if (trace_android_fs_dataread_start_enabled() && + (rw == READ)) + trace_android_fs_dataread_end(inode, offset, count); + if (trace_android_fs_datawrite_start_enabled() && + (rw == WRITE)) + trace_android_fs_datawrite_end(inode, offset, count); + return ret; } diff --git a/fs/ext4/readpage.c b/fs/ext4/readpage.c index f461d75ac049..44538c6c1131 100644 --- a/fs/ext4/readpage.c +++ b/fs/ext4/readpage.c @@ -46,6 +46,7 @@ #include #include "ext4.h" +#include static inline bool ext4_bio_encrypted(struct bio *bio) { @@ -56,6 +57,17 @@ static inline bool ext4_bio_encrypted(struct bio *bio) #endif } +static void +ext4_trace_read_completion(struct bio *bio) +{ + struct page *first_page = bio->bi_io_vec[0].bv_page; + + if (first_page != NULL) + trace_android_fs_dataread_end(first_page->mapping->host, + page_offset(first_page), + bio->bi_iter.bi_size); +} + /* * I/O completion handler for multipage BIOs. * @@ -73,6 +85,9 @@ static void mpage_end_io(struct bio *bio) struct bio_vec *bv; int i; + if (trace_android_fs_dataread_start_enabled()) + ext4_trace_read_completion(bio); + if (ext4_bio_encrypted(bio)) { if (bio->bi_status) { fscrypt_release_ctx(bio->bi_private); @@ -96,6 +111,24 @@ static void mpage_end_io(struct bio *bio) bio_put(bio); } +static void +ext4_submit_bio_read(struct bio *bio) +{ + if (trace_android_fs_dataread_start_enabled()) { + struct page *first_page = bio->bi_io_vec[0].bv_page; + + if (first_page != NULL) { + trace_android_fs_dataread_start( + first_page->mapping->host, + page_offset(first_page), + bio->bi_iter.bi_size, + current->pid, + current->comm); + } + } + submit_bio(bio); +} + int ext4_mpage_readpages(struct address_space *mapping, struct list_head *pages, struct page *page, unsigned nr_pages, bool is_readahead) @@ -236,7 +269,7 @@ int ext4_mpage_readpages(struct address_space *mapping, */ if (bio && (last_block_in_bio != blocks[0] - 1)) { submit_and_realloc: - submit_bio(bio); + ext4_submit_bio_read(bio); bio = NULL; } if (bio == NULL) { @@ -270,14 +303,14 @@ int ext4_mpage_readpages(struct address_space *mapping, if (((map.m_flags & EXT4_MAP_BOUNDARY) && (relative_block == map.m_len)) || (first_hole != blocks_per_page)) { - submit_bio(bio); + ext4_submit_bio_read(bio); bio = NULL; } else last_block_in_bio = blocks[blocks_per_page - 1]; goto next_page; confused: if (bio) { - submit_bio(bio); + ext4_submit_bio_read(bio); bio = NULL; } if (!PageUptodate(page)) @@ -290,6 +323,6 @@ int ext4_mpage_readpages(struct address_space *mapping, } BUG_ON(pages && !list_empty(pages)); if (bio) - submit_bio(bio); + ext4_submit_bio_read(bio); return 0; } diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c index 382c1ef9a9e4..1bd0f006288d 100644 --- a/fs/f2fs/data.c +++ b/fs/f2fs/data.c @@ -27,6 +27,7 @@ #include "segment.h" #include "trace.h" #include +#include #define NUM_PREALLOC_POST_READ_CTXS 128 @@ -2330,6 +2331,8 @@ static int f2fs_write_begin(struct file *file, struct address_space *mapping, block_t blkaddr = NULL_ADDR; int err = 0; + trace_android_fs_datawrite_start(inode, pos, len, + current->pid, current->comm); trace_f2fs_write_begin(inode, pos, len, flags); if ((f2fs_is_atomic_file(inode) && @@ -2429,6 +2432,7 @@ static int f2fs_write_end(struct file *file, { struct inode *inode = page->mapping->host; + trace_android_fs_datawrite_end(inode, pos, len); trace_f2fs_write_end(inode, pos, len, copied); /* @@ -2496,6 +2500,16 @@ static ssize_t f2fs_direct_IO(struct kiocb *iocb, struct iov_iter *iter) trace_f2fs_direct_IO_enter(inode, offset, count, rw); + if (trace_android_fs_dataread_start_enabled() && + (rw == READ)) + trace_android_fs_dataread_start(inode, offset, + count, current->pid, + current->comm); + if (trace_android_fs_datawrite_start_enabled() && + (rw == WRITE)) + trace_android_fs_datawrite_start(inode, offset, count, + current->pid, current->comm); + if (rw == WRITE && whint_mode == WHINT_MODE_OFF) iocb->ki_hint = WRITE_LIFE_NOT_SET; @@ -2524,6 +2538,13 @@ static ssize_t f2fs_direct_IO(struct kiocb *iocb, struct iov_iter *iter) } out: + if (trace_android_fs_dataread_start_enabled() && + (rw == READ)) + trace_android_fs_dataread_end(inode, offset, count); + if (trace_android_fs_datawrite_start_enabled() && + (rw == WRITE)) + trace_android_fs_datawrite_end(inode, offset, count); + trace_f2fs_direct_IO_exit(inode, offset, count, rw, err); return err; diff --git a/fs/f2fs/inline.c b/fs/f2fs/inline.c index 115dc219344b..fede695f722e 100644 --- a/fs/f2fs/inline.c +++ b/fs/f2fs/inline.c @@ -13,6 +13,7 @@ #include "f2fs.h" #include "node.h" +#include bool f2fs_may_inline_data(struct inode *inode) { @@ -86,14 +87,22 @@ int f2fs_read_inline_data(struct inode *inode, struct page *page) { struct page *ipage; + trace_android_fs_dataread_start(inode, page_offset(page), + PAGE_SIZE, current->pid, + current->comm); + ipage = f2fs_get_node_page(F2FS_I_SB(inode), inode->i_ino); if (IS_ERR(ipage)) { + trace_android_fs_dataread_end(inode, page_offset(page), + PAGE_SIZE); unlock_page(page); return PTR_ERR(ipage); } if (!f2fs_has_inline_data(inode)) { f2fs_put_page(ipage, 1); + trace_android_fs_dataread_end(inode, page_offset(page), + PAGE_SIZE); return -EAGAIN; } @@ -105,6 +114,8 @@ int f2fs_read_inline_data(struct inode *inode, struct page *page) if (!PageUptodate(page)) SetPageUptodate(page); f2fs_put_page(ipage, 1); + trace_android_fs_dataread_end(inode, page_offset(page), + PAGE_SIZE); unlock_page(page); return 0; } diff --git a/fs/mpage.c b/fs/mpage.c index c820dc9bebab..57468ecab5a6 100644 --- a/fs/mpage.c +++ b/fs/mpage.c @@ -32,6 +32,14 @@ #include #include "internal.h" +#define CREATE_TRACE_POINTS +#include + +EXPORT_TRACEPOINT_SYMBOL(android_fs_datawrite_start); +EXPORT_TRACEPOINT_SYMBOL(android_fs_datawrite_end); +EXPORT_TRACEPOINT_SYMBOL(android_fs_dataread_start); +EXPORT_TRACEPOINT_SYMBOL(android_fs_dataread_end); + /* * I/O completion handler for multipage BIOs. * @@ -49,6 +57,16 @@ static void mpage_end_io(struct bio *bio) struct bio_vec *bv; int i; + if (trace_android_fs_dataread_end_enabled() && + (bio_data_dir(bio) == READ)) { + struct page *first_page = bio->bi_io_vec[0].bv_page; + + if (first_page != NULL) + trace_android_fs_dataread_end(first_page->mapping->host, + page_offset(first_page), + bio->bi_iter.bi_size); + } + bio_for_each_segment_all(bv, bio, i) { struct page *page = bv->bv_page; page_endio(page, bio_op(bio), @@ -60,6 +78,18 @@ static void mpage_end_io(struct bio *bio) static struct bio *mpage_bio_submit(int op, int op_flags, struct bio *bio) { + if (trace_android_fs_dataread_start_enabled() && (op == REQ_OP_READ)) { + struct page *first_page = bio->bi_io_vec[0].bv_page; + + if (first_page != NULL) { + trace_android_fs_dataread_start( + first_page->mapping->host, + page_offset(first_page), + bio->bi_iter.bi_size, + current->pid, + current->comm); + } + } bio->bi_end_io = mpage_end_io; bio_set_op_attrs(bio, op, op_flags); guard_bio_eod(op, bio); diff --git a/include/trace/events/android_fs.h b/include/trace/events/android_fs.h new file mode 100644 index 000000000000..531da433a7bc --- /dev/null +++ b/include/trace/events/android_fs.h @@ -0,0 +1,31 @@ +#undef TRACE_SYSTEM +#define TRACE_SYSTEM android_fs + +#if !defined(_TRACE_ANDROID_FS_H) || defined(TRACE_HEADER_MULTI_READ) +#define _TRACE_ANDROID_FS_H + +#include +#include + +DEFINE_EVENT(android_fs_data_start_template, android_fs_dataread_start, + TP_PROTO(struct inode *inode, loff_t offset, int bytes, + pid_t pid, char *command), + TP_ARGS(inode, offset, bytes, pid, command)); + +DEFINE_EVENT(android_fs_data_end_template, android_fs_dataread_end, + TP_PROTO(struct inode *inode, loff_t offset, int bytes), + TP_ARGS(inode, offset, bytes)); + +DEFINE_EVENT(android_fs_data_start_template, android_fs_datawrite_start, + TP_PROTO(struct inode *inode, loff_t offset, int bytes, + pid_t pid, char *command), + TP_ARGS(inode, offset, bytes, pid, command)); + +DEFINE_EVENT(android_fs_data_end_template, android_fs_datawrite_end, + TP_PROTO(struct inode *inode, loff_t offset, int bytes), + TP_ARGS(inode, offset, bytes)); + +#endif /* _TRACE_ANDROID_FS_H */ + +/* This part must be outside protection */ +#include diff --git a/include/trace/events/android_fs_template.h b/include/trace/events/android_fs_template.h new file mode 100644 index 000000000000..618988b047c1 --- /dev/null +++ b/include/trace/events/android_fs_template.h @@ -0,0 +1,79 @@ +#if !defined(_TRACE_ANDROID_FS_TEMPLATE_H) || defined(TRACE_HEADER_MULTI_READ) +#define _TRACE_ANDROID_FS_TEMPLATE_H + +#include + +DECLARE_EVENT_CLASS(android_fs_data_start_template, + TP_PROTO(struct inode *inode, loff_t offset, int bytes, + pid_t pid, char *command), + TP_ARGS(inode, offset, bytes, pid, command), + TP_STRUCT__entry( + __array(char, path, MAX_FILTER_STR_VAL); + __field(char *, pathname); + __field(loff_t, offset); + __field(int, bytes); + __field(loff_t, i_size); + __string(cmdline, command); + __field(pid_t, pid); + __field(ino_t, ino); + ), + TP_fast_assign( + { + struct dentry *d; + + /* + * Grab a reference to the inode here because + * d_obtain_alias() will either drop the inode + * reference if it locates an existing dentry + * or transfer the reference to the new dentry + * created. In our case, the file is still open, + * so the dentry is guaranteed to exist (connected), + * so d_obtain_alias() drops the reference we + * grabbed here. + */ + ihold(inode); + d = d_obtain_alias(inode); + if (!IS_ERR(d)) { + __entry->pathname = dentry_path(d, + __entry->path, + MAX_FILTER_STR_VAL); + dput(d); + } else + __entry->pathname = ERR_PTR(-EINVAL); + __entry->offset = offset; + __entry->bytes = bytes; + __entry->i_size = i_size_read(inode); + __assign_str(cmdline, command); + __entry->pid = pid; + __entry->ino = inode->i_ino; + } + ), + TP_printk("entry_name %s, offset %llu, bytes %d, cmdline %s," + " pid %d, i_size %llu, ino %lu", + (IS_ERR(__entry->pathname) ? "ERROR" : __entry->pathname), + __entry->offset, __entry->bytes, __get_str(cmdline), + __entry->pid, __entry->i_size, + (unsigned long) __entry->ino) +); + +DECLARE_EVENT_CLASS(android_fs_data_end_template, + TP_PROTO(struct inode *inode, loff_t offset, int bytes), + TP_ARGS(inode, offset, bytes), + TP_STRUCT__entry( + __field(ino_t, ino); + __field(loff_t, offset); + __field(int, bytes); + ), + TP_fast_assign( + { + __entry->ino = inode->i_ino; + __entry->offset = offset; + __entry->bytes = bytes; + } + ), + TP_printk("ino %lu, offset %llu, bytes %d", + (unsigned long) __entry->ino, + __entry->offset, __entry->bytes) +); + +#endif /* _TRACE_ANDROID_FS_TEMPLATE_H */ From 63fd9b36ffbda6428e202a33137dcaf63cc95d9e Mon Sep 17 00:00:00 2001 From: Mohan Srinivasan Date: Fri, 10 Feb 2017 14:26:23 -0800 Subject: [PATCH 0147/1103] ANDROID: fs: Refactor FS readpage/write tracepoints. Refactor the fs readpage/write tracepoints to move the inode->path lookup outside the tracepoint code, and pass a pointer to the path into the tracepoint code instead. This is necessary because the tracepoint code runs non-preemptible. Thanks to Trilok Soni for catching this in 4.4. Signed-off-by: Mohan Srinivasan [AmitP: Folded following android-4.9 commit changes into this patch a5c4dbb05ab7 ("ANDROID: Replace spaces by '_' for some android filesystem tracepoints.")] Signed-off-by: Amit Pundir --- fs/ext4/inline.c | 12 +++++- fs/ext4/inode.c | 45 +++++++++++++++++----- fs/ext4/readpage.c | 6 +++ fs/f2fs/data.c | 33 +++++++++++++--- fs/f2fs/inline.c | 13 +++++-- fs/mpage.c | 6 +++ include/trace/events/android_fs.h | 44 ++++++++++++++++++--- include/trace/events/android_fs_template.h | 37 ++++++------------ 8 files changed, 145 insertions(+), 51 deletions(-) diff --git a/fs/ext4/inline.c b/fs/ext4/inline.c index 09c8c3fd8491..b75a7cf79784 100644 --- a/fs/ext4/inline.c +++ b/fs/ext4/inline.c @@ -506,8 +506,16 @@ int ext4_readpage_inline(struct inode *inode, struct page *page) return -EAGAIN; } - trace_android_fs_dataread_start(inode, page_offset(page), PAGE_SIZE, - current->pid, current->comm); + if (trace_android_fs_dataread_start_enabled()) { + char *path, pathbuf[MAX_TRACE_PATHBUF_LEN]; + + path = android_fstrace_get_pathname(pathbuf, + MAX_TRACE_PATHBUF_LEN, + inode); + trace_android_fs_dataread_start(inode, page_offset(page), + PAGE_SIZE, current->pid, + path, current->comm); + } /* * Current inline data can only exist in the 1st page, diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c index 2f98c77e6d2a..aaf56a88e173 100644 --- a/fs/ext4/inode.c +++ b/fs/ext4/inode.c @@ -1254,8 +1254,16 @@ static int ext4_write_begin(struct file *file, struct address_space *mapping, if (unlikely(ext4_forced_shutdown(EXT4_SB(inode->i_sb)))) return -EIO; - trace_android_fs_datawrite_start(inode, pos, len, - current->pid, current->comm); + if (trace_android_fs_datawrite_start_enabled()) { + char *path, pathbuf[MAX_TRACE_PATHBUF_LEN]; + + path = android_fstrace_get_pathname(pathbuf, + MAX_TRACE_PATHBUF_LEN, + inode); + trace_android_fs_datawrite_start(inode, pos, len, + current->pid, path, + current->comm); + } trace_ext4_write_begin(inode, pos, len, flags); /* * Reserve one block more for addition to orphan list in case @@ -3029,8 +3037,16 @@ static int ext4_da_write_begin(struct file *file, struct address_space *mapping, len, flags, pagep, fsdata); } *fsdata = (void *)0; - trace_android_fs_datawrite_start(inode, pos, len, - current->pid, current->comm); + if (trace_android_fs_datawrite_start_enabled()) { + char *path, pathbuf[MAX_TRACE_PATHBUF_LEN]; + + path = android_fstrace_get_pathname(pathbuf, + MAX_TRACE_PATHBUF_LEN, + inode); + trace_android_fs_datawrite_start(inode, pos, len, + current->pid, + path, current->comm); + } trace_ext4_da_write_begin(inode, pos, len, flags); if (ext4_test_inode_state(inode, EXT4_STATE_MAY_INLINE_DATA)) { @@ -3873,16 +3889,27 @@ static ssize_t ext4_direct_IO(struct kiocb *iocb, struct iov_iter *iter) return 0; if (trace_android_fs_dataread_start_enabled() && - (rw == READ)) + (rw == READ)) { + char *path, pathbuf[MAX_TRACE_PATHBUF_LEN]; + + path = android_fstrace_get_pathname(pathbuf, + MAX_TRACE_PATHBUF_LEN, + inode); trace_android_fs_dataread_start(inode, offset, count, - current->pid, + current->pid, path, current->comm); + } if (trace_android_fs_datawrite_start_enabled() && - (rw == WRITE)) + (rw == WRITE)) { + char *path, pathbuf[MAX_TRACE_PATHBUF_LEN]; + + path = android_fstrace_get_pathname(pathbuf, + MAX_TRACE_PATHBUF_LEN, + inode); trace_android_fs_datawrite_start(inode, offset, count, - current->pid, + current->pid, path, current->comm); - + } trace_ext4_direct_IO_enter(inode, offset, count, iov_iter_rw(iter)); if (iov_iter_rw(iter) == READ) ret = ext4_direct_IO_read(iocb, iter); diff --git a/fs/ext4/readpage.c b/fs/ext4/readpage.c index 44538c6c1131..aa1b9e181f31 100644 --- a/fs/ext4/readpage.c +++ b/fs/ext4/readpage.c @@ -118,11 +118,17 @@ ext4_submit_bio_read(struct bio *bio) struct page *first_page = bio->bi_io_vec[0].bv_page; if (first_page != NULL) { + char *path, pathbuf[MAX_TRACE_PATHBUF_LEN]; + + path = android_fstrace_get_pathname(pathbuf, + MAX_TRACE_PATHBUF_LEN, + first_page->mapping->host); trace_android_fs_dataread_start( first_page->mapping->host, page_offset(first_page), bio->bi_iter.bi_size, current->pid, + path, current->comm); } } diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c index 1bd0f006288d..dde566164ef0 100644 --- a/fs/f2fs/data.c +++ b/fs/f2fs/data.c @@ -2331,8 +2331,16 @@ static int f2fs_write_begin(struct file *file, struct address_space *mapping, block_t blkaddr = NULL_ADDR; int err = 0; - trace_android_fs_datawrite_start(inode, pos, len, - current->pid, current->comm); + if (trace_android_fs_datawrite_start_enabled()) { + char *path, pathbuf[MAX_TRACE_PATHBUF_LEN]; + + path = android_fstrace_get_pathname(pathbuf, + MAX_TRACE_PATHBUF_LEN, + inode); + trace_android_fs_datawrite_start(inode, pos, len, + current->pid, path, + current->comm); + } trace_f2fs_write_begin(inode, pos, len, flags); if ((f2fs_is_atomic_file(inode) && @@ -2501,14 +2509,27 @@ static ssize_t f2fs_direct_IO(struct kiocb *iocb, struct iov_iter *iter) trace_f2fs_direct_IO_enter(inode, offset, count, rw); if (trace_android_fs_dataread_start_enabled() && - (rw == READ)) + (rw == READ)) { + char *path, pathbuf[MAX_TRACE_PATHBUF_LEN]; + + path = android_fstrace_get_pathname(pathbuf, + MAX_TRACE_PATHBUF_LEN, + inode); trace_android_fs_dataread_start(inode, offset, - count, current->pid, + count, current->pid, path, current->comm); + } if (trace_android_fs_datawrite_start_enabled() && - (rw == WRITE)) + (rw == WRITE)) { + char *path, pathbuf[MAX_TRACE_PATHBUF_LEN]; + + path = android_fstrace_get_pathname(pathbuf, + MAX_TRACE_PATHBUF_LEN, + inode); trace_android_fs_datawrite_start(inode, offset, count, - current->pid, current->comm); + current->pid, path, + current->comm); + } if (rw == WRITE && whint_mode == WHINT_MODE_OFF) iocb->ki_hint = WRITE_LIFE_NOT_SET; diff --git a/fs/f2fs/inline.c b/fs/f2fs/inline.c index fede695f722e..df71d26192b8 100644 --- a/fs/f2fs/inline.c +++ b/fs/f2fs/inline.c @@ -87,9 +87,16 @@ int f2fs_read_inline_data(struct inode *inode, struct page *page) { struct page *ipage; - trace_android_fs_dataread_start(inode, page_offset(page), - PAGE_SIZE, current->pid, - current->comm); + if (trace_android_fs_dataread_start_enabled()) { + char *path, pathbuf[MAX_TRACE_PATHBUF_LEN]; + + path = android_fstrace_get_pathname(pathbuf, + MAX_TRACE_PATHBUF_LEN, + inode); + trace_android_fs_dataread_start(inode, page_offset(page), + PAGE_SIZE, current->pid, + path, current->comm); + } ipage = f2fs_get_node_page(F2FS_I_SB(inode), inode->i_ino); if (IS_ERR(ipage)) { diff --git a/fs/mpage.c b/fs/mpage.c index 57468ecab5a6..a5265828a2f2 100644 --- a/fs/mpage.c +++ b/fs/mpage.c @@ -82,11 +82,17 @@ static struct bio *mpage_bio_submit(int op, int op_flags, struct bio *bio) struct page *first_page = bio->bi_io_vec[0].bv_page; if (first_page != NULL) { + char *path, pathbuf[MAX_TRACE_PATHBUF_LEN]; + + path = android_fstrace_get_pathname(pathbuf, + MAX_TRACE_PATHBUF_LEN, + first_page->mapping->host); trace_android_fs_dataread_start( first_page->mapping->host, page_offset(first_page), bio->bi_iter.bi_size, current->pid, + path, current->comm); } } diff --git a/include/trace/events/android_fs.h b/include/trace/events/android_fs.h index 531da433a7bc..49509533d3fa 100644 --- a/include/trace/events/android_fs.h +++ b/include/trace/events/android_fs.h @@ -9,8 +9,8 @@ DEFINE_EVENT(android_fs_data_start_template, android_fs_dataread_start, TP_PROTO(struct inode *inode, loff_t offset, int bytes, - pid_t pid, char *command), - TP_ARGS(inode, offset, bytes, pid, command)); + pid_t pid, char *pathname, char *command), + TP_ARGS(inode, offset, bytes, pid, pathname, command)); DEFINE_EVENT(android_fs_data_end_template, android_fs_dataread_end, TP_PROTO(struct inode *inode, loff_t offset, int bytes), @@ -18,14 +18,48 @@ DEFINE_EVENT(android_fs_data_end_template, android_fs_dataread_end, DEFINE_EVENT(android_fs_data_start_template, android_fs_datawrite_start, TP_PROTO(struct inode *inode, loff_t offset, int bytes, - pid_t pid, char *command), - TP_ARGS(inode, offset, bytes, pid, command)); + pid_t pid, char *pathname, char *command), + TP_ARGS(inode, offset, bytes, pid, pathname, command)); DEFINE_EVENT(android_fs_data_end_template, android_fs_datawrite_end, TP_PROTO(struct inode *inode, loff_t offset, int bytes), - TP_ARGS(inode, offset, bytes)); + TP_ARGS(inode, offset, bytes)); #endif /* _TRACE_ANDROID_FS_H */ /* This part must be outside protection */ #include + +#ifndef ANDROID_FSTRACE_GET_PATHNAME +#define ANDROID_FSTRACE_GET_PATHNAME + +/* Sizes an on-stack array, so careful if sizing this up ! */ +#define MAX_TRACE_PATHBUF_LEN 256 + +static inline char * +android_fstrace_get_pathname(char *buf, int buflen, struct inode *inode) +{ + char *path; + struct dentry *d; + + /* + * d_obtain_alias() will either iput() if it locates an existing + * dentry or transfer the reference to the new dentry created. + * So get an extra reference here. + */ + ihold(inode); + d = d_obtain_alias(inode); + if (likely(!IS_ERR(d))) { + path = dentry_path_raw(d, buf, buflen); + if (unlikely(IS_ERR(path))) { + strcpy(buf, "ERROR"); + path = buf; + } + dput(d); + } else { + strcpy(buf, "ERROR"); + path = buf; + } + return path; +} +#endif diff --git a/include/trace/events/android_fs_template.h b/include/trace/events/android_fs_template.h index 618988b047c1..b23d17b56c63 100644 --- a/include/trace/events/android_fs_template.h +++ b/include/trace/events/android_fs_template.h @@ -5,11 +5,10 @@ DECLARE_EVENT_CLASS(android_fs_data_start_template, TP_PROTO(struct inode *inode, loff_t offset, int bytes, - pid_t pid, char *command), - TP_ARGS(inode, offset, bytes, pid, command), + pid_t pid, char *pathname, char *command), + TP_ARGS(inode, offset, bytes, pid, pathname, command), TP_STRUCT__entry( - __array(char, path, MAX_FILTER_STR_VAL); - __field(char *, pathname); + __string(pathbuf, pathname); __field(loff_t, offset); __field(int, bytes); __field(loff_t, i_size); @@ -19,40 +18,26 @@ DECLARE_EVENT_CLASS(android_fs_data_start_template, ), TP_fast_assign( { - struct dentry *d; - /* - * Grab a reference to the inode here because - * d_obtain_alias() will either drop the inode - * reference if it locates an existing dentry - * or transfer the reference to the new dentry - * created. In our case, the file is still open, - * so the dentry is guaranteed to exist (connected), - * so d_obtain_alias() drops the reference we - * grabbed here. + * Replace the spaces in filenames and cmdlines + * because this screws up the tooling that parses + * the traces. */ - ihold(inode); - d = d_obtain_alias(inode); - if (!IS_ERR(d)) { - __entry->pathname = dentry_path(d, - __entry->path, - MAX_FILTER_STR_VAL); - dput(d); - } else - __entry->pathname = ERR_PTR(-EINVAL); + __assign_str(pathbuf, pathname); + (void)strreplace(__get_str(pathbuf), ' ', '_'); __entry->offset = offset; __entry->bytes = bytes; __entry->i_size = i_size_read(inode); __assign_str(cmdline, command); + (void)strreplace(__get_str(cmdline), ' ', '_'); __entry->pid = pid; __entry->ino = inode->i_ino; } ), TP_printk("entry_name %s, offset %llu, bytes %d, cmdline %s," " pid %d, i_size %llu, ino %lu", - (IS_ERR(__entry->pathname) ? "ERROR" : __entry->pathname), - __entry->offset, __entry->bytes, __get_str(cmdline), - __entry->pid, __entry->i_size, + __get_str(pathbuf), __entry->offset, __entry->bytes, + __get_str(cmdline), __entry->pid, __entry->i_size, (unsigned long) __entry->ino) ); From c8e986b9700099c429e991a3d0ee7632151859f3 Mon Sep 17 00:00:00 2001 From: Christian Poetzsch Date: Fri, 24 Jul 2015 16:42:58 +0100 Subject: [PATCH 0148/1103] ANDROID: fs: Fix for in kernel emergency remount when loop mounts are used adb reboot calls /proc/sysrq-trigger to force an emergency remount (ro) of all mounted disks. This is executed in the order of the time the mount was originally done. Because we have a test system which loop mount images from an extra partition, we see errors cause the loop mounted partitions gets remounted after this physical partition was set to read only already. Fix this by reversing the order of the emergency remount. This will remount the disk first which have been mounted last. So instead of remounting in this order: /dev/sda1 /dev/loop1 /dev/loop2 we now remount in this order: /dev/loop2 /dev/loop1 /dev/sda1 Change-Id: I68fe7e16cc9400ab5278877af70c9ea1d9b57936 Signed-off-by: Christian Poetzsch --- fs/super.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/super.c b/fs/super.c index f3a8c008e164..04178a266607 100644 --- a/fs/super.c +++ b/fs/super.c @@ -622,7 +622,7 @@ void iterate_supers(void (*f)(struct super_block *, void *), void *arg) struct super_block *sb, *p = NULL; spin_lock(&sb_lock); - list_for_each_entry(sb, &super_blocks, s_list) { + list_for_each_entry_reverse(sb, &super_blocks, s_list) { if (hlist_unhashed(&sb->s_instances)) continue; sb->s_count++; From e62ff7d30a8560f6e6c5bf31a52f7476642bada6 Mon Sep 17 00:00:00 2001 From: Jin Qian Date: Fri, 7 Oct 2016 16:20:47 -0700 Subject: [PATCH 0149/1103] ANDROID: goldfish: add ranchu defconfigs Change-Id: I73ef1b132b6203ae921a1e1d4850eaadf58f8926 [AmitP: Folded following android-4.9 commit changes into this patch b821439faf75 ("ANDROID: arm64: rename ranchu defconfig to ranchu64") 1a03fc05a569 ("ANDROID: goldfish: enable CONFIG_INET_DIAG_DESTROY") 2bed6160b367 ("ANDROID: goldfish: disable GOLDFISH_SYNC") ec6a764367a4 ("ANDROID: goldfish_sync: update defconfig for 4.9-compatible version")] Signed-off-by: Amit Pundir --- arch/arm/configs/ranchu_defconfig | 314 +++++++++++++++++ arch/arm64/configs/ranchu64_defconfig | 310 +++++++++++++++++ arch/x86/configs/i386_ranchu_defconfig | 422 +++++++++++++++++++++++ arch/x86/configs/x86_64_ranchu_defconfig | 417 ++++++++++++++++++++++ 4 files changed, 1463 insertions(+) create mode 100644 arch/arm/configs/ranchu_defconfig create mode 100644 arch/arm64/configs/ranchu64_defconfig create mode 100644 arch/x86/configs/i386_ranchu_defconfig create mode 100644 arch/x86/configs/x86_64_ranchu_defconfig diff --git a/arch/arm/configs/ranchu_defconfig b/arch/arm/configs/ranchu_defconfig new file mode 100644 index 000000000000..461a85a02764 --- /dev/null +++ b/arch/arm/configs/ranchu_defconfig @@ -0,0 +1,314 @@ +# CONFIG_LOCALVERSION_AUTO is not set +CONFIG_AUDIT=y +CONFIG_NO_HZ=y +CONFIG_HIGH_RES_TIMERS=y +CONFIG_TASKSTATS=y +CONFIG_TASK_DELAY_ACCT=y +CONFIG_TASK_XACCT=y +CONFIG_TASK_IO_ACCOUNTING=y +CONFIG_IKCONFIG=y +CONFIG_IKCONFIG_PROC=y +CONFIG_LOG_BUF_SHIFT=14 +CONFIG_CGROUPS=y +CONFIG_CGROUP_DEBUG=y +CONFIG_CGROUP_FREEZER=y +CONFIG_CPUSETS=y +CONFIG_CGROUP_CPUACCT=y +CONFIG_CGROUP_SCHED=y +CONFIG_RT_GROUP_SCHED=y +CONFIG_BLK_DEV_INITRD=y +CONFIG_KALLSYMS_ALL=y +CONFIG_EMBEDDED=y +CONFIG_PROFILING=y +CONFIG_OPROFILE=y +CONFIG_ARCH_MMAP_RND_BITS=16 +# CONFIG_BLK_DEV_BSG is not set +# CONFIG_IOSCHED_DEADLINE is not set +# CONFIG_IOSCHED_CFQ is not set +CONFIG_ARCH_VIRT=y +CONFIG_ARM_KERNMEM_PERMS=y +CONFIG_SMP=y +CONFIG_PREEMPT=y +CONFIG_AEABI=y +CONFIG_HIGHMEM=y +CONFIG_KSM=y +CONFIG_SECCOMP=y +CONFIG_CMDLINE="console=ttyAMA0" +CONFIG_VFP=y +CONFIG_NEON=y +# CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set +CONFIG_PM_AUTOSLEEP=y +CONFIG_PM_WAKELOCKS=y +CONFIG_PM_WAKELOCKS_LIMIT=0 +# CONFIG_PM_WAKELOCKS_GC is not set +CONFIG_PM_DEBUG=y +CONFIG_NET=y +CONFIG_PACKET=y +CONFIG_UNIX=y +CONFIG_XFRM_USER=y +CONFIG_NET_KEY=y +CONFIG_INET=y +CONFIG_INET_DIAG_DESTROY=y +CONFIG_IP_MULTICAST=y +CONFIG_IP_ADVANCED_ROUTER=y +CONFIG_IP_MULTIPLE_TABLES=y +CONFIG_IP_PNP=y +CONFIG_IP_PNP_DHCP=y +CONFIG_IP_PNP_BOOTP=y +CONFIG_INET_ESP=y +# CONFIG_INET_LRO is not set +CONFIG_IPV6_ROUTER_PREF=y +CONFIG_IPV6_ROUTE_INFO=y +CONFIG_IPV6_OPTIMISTIC_DAD=y +CONFIG_INET6_AH=y +CONFIG_INET6_ESP=y +CONFIG_INET6_IPCOMP=y +CONFIG_IPV6_MIP6=y +CONFIG_IPV6_MULTIPLE_TABLES=y +CONFIG_NETFILTER=y +CONFIG_NF_CONNTRACK=y +CONFIG_NF_CONNTRACK_SECMARK=y +CONFIG_NF_CONNTRACK_EVENTS=y +CONFIG_NF_CT_PROTO_DCCP=y +CONFIG_NF_CT_PROTO_SCTP=y +CONFIG_NF_CT_PROTO_UDPLITE=y +CONFIG_NF_CONNTRACK_AMANDA=y +CONFIG_NF_CONNTRACK_FTP=y +CONFIG_NF_CONNTRACK_H323=y +CONFIG_NF_CONNTRACK_IRC=y +CONFIG_NF_CONNTRACK_NETBIOS_NS=y +CONFIG_NF_CONNTRACK_PPTP=y +CONFIG_NF_CONNTRACK_SANE=y +CONFIG_NF_CONNTRACK_TFTP=y +CONFIG_NF_CT_NETLINK=y +CONFIG_NETFILTER_XT_TARGET_CLASSIFY=y +CONFIG_NETFILTER_XT_TARGET_CONNMARK=y +CONFIG_NETFILTER_XT_TARGET_CONNSECMARK=y +CONFIG_NETFILTER_XT_TARGET_IDLETIMER=y +CONFIG_NETFILTER_XT_TARGET_MARK=y +CONFIG_NETFILTER_XT_TARGET_NFLOG=y +CONFIG_NETFILTER_XT_TARGET_NFQUEUE=y +CONFIG_NETFILTER_XT_TARGET_TPROXY=y +CONFIG_NETFILTER_XT_TARGET_TRACE=y +CONFIG_NETFILTER_XT_TARGET_SECMARK=y +CONFIG_NETFILTER_XT_TARGET_TCPMSS=y +CONFIG_NETFILTER_XT_MATCH_COMMENT=y +CONFIG_NETFILTER_XT_MATCH_CONNLIMIT=y +CONFIG_NETFILTER_XT_MATCH_CONNMARK=y +CONFIG_NETFILTER_XT_MATCH_CONNTRACK=y +CONFIG_NETFILTER_XT_MATCH_HASHLIMIT=y +CONFIG_NETFILTER_XT_MATCH_HELPER=y +CONFIG_NETFILTER_XT_MATCH_IPRANGE=y +CONFIG_NETFILTER_XT_MATCH_LENGTH=y +CONFIG_NETFILTER_XT_MATCH_LIMIT=y +CONFIG_NETFILTER_XT_MATCH_MAC=y +CONFIG_NETFILTER_XT_MATCH_MARK=y +CONFIG_NETFILTER_XT_MATCH_POLICY=y +CONFIG_NETFILTER_XT_MATCH_PKTTYPE=y +CONFIG_NETFILTER_XT_MATCH_QTAGUID=y +CONFIG_NETFILTER_XT_MATCH_QUOTA=y +CONFIG_NETFILTER_XT_MATCH_QUOTA2=y +CONFIG_NETFILTER_XT_MATCH_SOCKET=y +CONFIG_NETFILTER_XT_MATCH_STATE=y +CONFIG_NETFILTER_XT_MATCH_STATISTIC=y +CONFIG_NETFILTER_XT_MATCH_STRING=y +CONFIG_NETFILTER_XT_MATCH_TIME=y +CONFIG_NETFILTER_XT_MATCH_U32=y +CONFIG_NF_CONNTRACK_IPV4=y +CONFIG_IP_NF_IPTABLES=y +CONFIG_IP_NF_MATCH_AH=y +CONFIG_IP_NF_MATCH_ECN=y +CONFIG_IP_NF_MATCH_TTL=y +CONFIG_IP_NF_FILTER=y +CONFIG_IP_NF_TARGET_REJECT=y +CONFIG_IP_NF_MANGLE=y +CONFIG_IP_NF_RAW=y +CONFIG_IP_NF_SECURITY=y +CONFIG_IP_NF_ARPTABLES=y +CONFIG_IP_NF_ARPFILTER=y +CONFIG_IP_NF_ARP_MANGLE=y +CONFIG_NF_CONNTRACK_IPV6=y +CONFIG_IP6_NF_IPTABLES=y +CONFIG_IP6_NF_FILTER=y +CONFIG_IP6_NF_TARGET_REJECT=y +CONFIG_IP6_NF_MANGLE=y +CONFIG_IP6_NF_RAW=y +CONFIG_BRIDGE=y +CONFIG_NET_SCHED=y +CONFIG_NET_SCH_HTB=y +CONFIG_NET_CLS_U32=y +CONFIG_NET_EMATCH=y +CONFIG_NET_EMATCH_U32=y +CONFIG_NET_CLS_ACT=y +# CONFIG_WIRELESS is not set +CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug" +CONFIG_MTD=y +CONFIG_MTD_CMDLINE_PARTS=y +CONFIG_MTD_BLOCK=y +CONFIG_MTD_CFI=y +CONFIG_MTD_CFI_INTELEXT=y +CONFIG_MTD_CFI_AMDSTD=y +CONFIG_BLK_DEV_LOOP=y +CONFIG_BLK_DEV_RAM=y +CONFIG_BLK_DEV_RAM_SIZE=8192 +CONFIG_VIRTIO_BLK=y +CONFIG_MD=y +CONFIG_BLK_DEV_DM=y +CONFIG_DM_CRYPT=y +CONFIG_DM_UEVENT=y +CONFIG_DM_VERITY=y +CONFIG_DM_VERITY_FEC=y +CONFIG_NETDEVICES=y +CONFIG_TUN=y +CONFIG_VIRTIO_NET=y +CONFIG_SMSC911X=y +CONFIG_PPP=y +CONFIG_PPP_BSDCOMP=y +CONFIG_PPP_DEFLATE=y +CONFIG_PPP_MPPE=y +CONFIG_USB_USBNET=y +# CONFIG_WLAN is not set +CONFIG_INPUT_EVDEV=y +CONFIG_INPUT_KEYRESET=y +CONFIG_KEYBOARD_GOLDFISH_EVENTS=y +# CONFIG_INPUT_MOUSE is not set +CONFIG_INPUT_JOYSTICK=y +CONFIG_JOYSTICK_XPAD=y +CONFIG_JOYSTICK_XPAD_FF=y +CONFIG_JOYSTICK_XPAD_LEDS=y +CONFIG_INPUT_TABLET=y +CONFIG_TABLET_USB_ACECAD=y +CONFIG_TABLET_USB_AIPTEK=y +CONFIG_TABLET_USB_GTCO=y +CONFIG_TABLET_USB_HANWANG=y +CONFIG_TABLET_USB_KBTAB=y +CONFIG_INPUT_MISC=y +CONFIG_INPUT_KEYCHORD=y +CONFIG_INPUT_UINPUT=y +CONFIG_INPUT_GPIO=y +# CONFIG_SERIO_SERPORT is not set +CONFIG_SERIO_AMBAKMI=y +# CONFIG_VT is not set +# CONFIG_LEGACY_PTYS is not set +# CONFIG_DEVMEM is not set +# CONFIG_DEVKMEM is not set +CONFIG_SERIAL_AMBA_PL011=y +CONFIG_SERIAL_AMBA_PL011_CONSOLE=y +CONFIG_VIRTIO_CONSOLE=y +# CONFIG_HW_RANDOM is not set +# CONFIG_HWMON is not set +CONFIG_MEDIA_SUPPORT=y +CONFIG_FB=y +CONFIG_FB_GOLDFISH=y +CONFIG_FB_SIMPLE=y +CONFIG_BACKLIGHT_LCD_SUPPORT=y +CONFIG_LOGO=y +# CONFIG_LOGO_LINUX_MONO is not set +# CONFIG_LOGO_LINUX_VGA16 is not set +CONFIG_SOUND=y +CONFIG_SND=y +CONFIG_HIDRAW=y +CONFIG_UHID=y +CONFIG_HID_A4TECH=y +CONFIG_HID_ACRUX=y +CONFIG_HID_ACRUX_FF=y +CONFIG_HID_APPLE=y +CONFIG_HID_BELKIN=y +CONFIG_HID_CHERRY=y +CONFIG_HID_CHICONY=y +CONFIG_HID_PRODIKEYS=y +CONFIG_HID_CYPRESS=y +CONFIG_HID_DRAGONRISE=y +CONFIG_DRAGONRISE_FF=y +CONFIG_HID_EMS_FF=y +CONFIG_HID_ELECOM=y +CONFIG_HID_EZKEY=y +CONFIG_HID_HOLTEK=y +CONFIG_HID_KEYTOUCH=y +CONFIG_HID_KYE=y +CONFIG_HID_UCLOGIC=y +CONFIG_HID_WALTOP=y +CONFIG_HID_GYRATION=y +CONFIG_HID_TWINHAN=y +CONFIG_HID_KENSINGTON=y +CONFIG_HID_LCPOWER=y +CONFIG_HID_LOGITECH=y +CONFIG_HID_LOGITECH_DJ=y +CONFIG_LOGITECH_FF=y +CONFIG_LOGIRUMBLEPAD2_FF=y +CONFIG_LOGIG940_FF=y +CONFIG_HID_MAGICMOUSE=y +CONFIG_HID_MICROSOFT=y +CONFIG_HID_MONTEREY=y +CONFIG_HID_MULTITOUCH=y +CONFIG_HID_NTRIG=y +CONFIG_HID_ORTEK=y +CONFIG_HID_PANTHERLORD=y +CONFIG_PANTHERLORD_FF=y +CONFIG_HID_PETALYNX=y +CONFIG_HID_PICOLCD=y +CONFIG_HID_PRIMAX=y +CONFIG_HID_ROCCAT=y +CONFIG_HID_SAITEK=y +CONFIG_HID_SAMSUNG=y +CONFIG_HID_SONY=y +CONFIG_HID_SPEEDLINK=y +CONFIG_HID_SUNPLUS=y +CONFIG_HID_GREENASIA=y +CONFIG_GREENASIA_FF=y +CONFIG_HID_SMARTJOYPLUS=y +CONFIG_SMARTJOYPLUS_FF=y +CONFIG_HID_TIVO=y +CONFIG_HID_TOPSEED=y +CONFIG_HID_THRUSTMASTER=y +CONFIG_HID_WACOM=y +CONFIG_HID_WIIMOTE=y +CONFIG_HID_ZEROPLUS=y +CONFIG_HID_ZYDACRON=y +CONFIG_USB_HIDDEV=y +CONFIG_USB_ANNOUNCE_NEW_DEVICES=y +CONFIG_USB_EHCI_HCD=y +CONFIG_USB_OTG_WAKELOCK=y +CONFIG_RTC_CLASS=y +CONFIG_RTC_DRV_PL031=y +CONFIG_VIRTIO_MMIO=y +CONFIG_STAGING=y +CONFIG_ASHMEM=y +CONFIG_ANDROID_LOW_MEMORY_KILLER=y +CONFIG_SYNC=y +CONFIG_SW_SYNC=y +CONFIG_SW_SYNC_USER=y +CONFIG_ION=y +CONFIG_GOLDFISH_AUDIO=y +CONFIG_GOLDFISH=y +CONFIG_GOLDFISH_PIPE=y +CONFIG_ANDROID=y +CONFIG_ANDROID_BINDER_IPC=y +CONFIG_EXT4_FS=y +CONFIG_EXT4_FS_SECURITY=y +CONFIG_QUOTA=y +CONFIG_FUSE_FS=y +CONFIG_CUSE=y +CONFIG_MSDOS_FS=y +CONFIG_VFAT_FS=y +CONFIG_TMPFS=y +CONFIG_TMPFS_POSIX_ACL=y +CONFIG_PSTORE=y +CONFIG_PSTORE_CONSOLE=y +CONFIG_PSTORE_RAM=y +CONFIG_NFS_FS=y +CONFIG_ROOT_NFS=y +CONFIG_NLS_CODEPAGE_437=y +CONFIG_NLS_ISO8859_1=y +CONFIG_DEBUG_INFO=y +CONFIG_MAGIC_SYSRQ=y +CONFIG_DETECT_HUNG_TASK=y +CONFIG_PANIC_TIMEOUT=5 +# CONFIG_SCHED_DEBUG is not set +CONFIG_SCHEDSTATS=y +CONFIG_TIMER_STATS=y +CONFIG_ENABLE_DEFAULT_TRACERS=y +CONFIG_SECURITY=y +CONFIG_SECURITY_NETWORK=y +CONFIG_SECURITY_SELINUX=y +CONFIG_VIRTUALIZATION=y diff --git a/arch/arm64/configs/ranchu64_defconfig b/arch/arm64/configs/ranchu64_defconfig new file mode 100644 index 000000000000..51c3bfc8658c --- /dev/null +++ b/arch/arm64/configs/ranchu64_defconfig @@ -0,0 +1,310 @@ +# CONFIG_LOCALVERSION_AUTO is not set +# CONFIG_SWAP is not set +CONFIG_POSIX_MQUEUE=y +CONFIG_AUDIT=y +CONFIG_NO_HZ=y +CONFIG_HIGH_RES_TIMERS=y +CONFIG_BSD_PROCESS_ACCT=y +CONFIG_BSD_PROCESS_ACCT_V3=y +CONFIG_TASKSTATS=y +CONFIG_TASK_DELAY_ACCT=y +CONFIG_TASK_XACCT=y +CONFIG_TASK_IO_ACCOUNTING=y +CONFIG_IKCONFIG=y +CONFIG_IKCONFIG_PROC=y +CONFIG_LOG_BUF_SHIFT=14 +CONFIG_CGROUP_DEBUG=y +CONFIG_CGROUP_FREEZER=y +CONFIG_CGROUP_CPUACCT=y +CONFIG_RT_GROUP_SCHED=y +CONFIG_SCHED_AUTOGROUP=y +CONFIG_BLK_DEV_INITRD=y +CONFIG_KALLSYMS_ALL=y +CONFIG_EMBEDDED=y +# CONFIG_COMPAT_BRK is not set +CONFIG_PROFILING=y +CONFIG_ARCH_MMAP_RND_BITS=24 +CONFIG_ARCH_MMAP_RND_COMPAT_BITS=16 +# CONFIG_BLK_DEV_BSG is not set +# CONFIG_IOSCHED_DEADLINE is not set +CONFIG_ARCH_VEXPRESS=y +CONFIG_NR_CPUS=4 +CONFIG_PREEMPT=y +CONFIG_KSM=y +CONFIG_SECCOMP=y +CONFIG_ARMV8_DEPRECATED=y +CONFIG_SWP_EMULATION=y +CONFIG_CP15_BARRIER_EMULATION=y +CONFIG_SETEND_EMULATION=y +CONFIG_CMDLINE="console=ttyAMA0" +# CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set +CONFIG_COMPAT=y +CONFIG_PM_AUTOSLEEP=y +CONFIG_PM_WAKELOCKS=y +CONFIG_PM_WAKELOCKS_LIMIT=0 +# CONFIG_PM_WAKELOCKS_GC is not set +CONFIG_PM_DEBUG=y +CONFIG_NET=y +CONFIG_PACKET=y +CONFIG_UNIX=y +CONFIG_XFRM_USER=y +CONFIG_NET_KEY=y +CONFIG_INET=y +CONFIG_INET_DIAG_DESTROY=y +CONFIG_IP_MULTICAST=y +CONFIG_IP_ADVANCED_ROUTER=y +CONFIG_IP_MULTIPLE_TABLES=y +CONFIG_IP_PNP=y +CONFIG_IP_PNP_DHCP=y +CONFIG_IP_PNP_BOOTP=y +CONFIG_INET_ESP=y +# CONFIG_INET_LRO is not set +CONFIG_IPV6_ROUTER_PREF=y +CONFIG_IPV6_ROUTE_INFO=y +CONFIG_IPV6_OPTIMISTIC_DAD=y +CONFIG_INET6_AH=y +CONFIG_INET6_ESP=y +CONFIG_INET6_IPCOMP=y +CONFIG_IPV6_MIP6=y +CONFIG_IPV6_MULTIPLE_TABLES=y +CONFIG_NETFILTER=y +CONFIG_NF_CONNTRACK=y +CONFIG_NF_CONNTRACK_SECMARK=y +CONFIG_NF_CONNTRACK_EVENTS=y +CONFIG_NF_CT_PROTO_DCCP=y +CONFIG_NF_CT_PROTO_SCTP=y +CONFIG_NF_CT_PROTO_UDPLITE=y +CONFIG_NF_CONNTRACK_AMANDA=y +CONFIG_NF_CONNTRACK_FTP=y +CONFIG_NF_CONNTRACK_H323=y +CONFIG_NF_CONNTRACK_IRC=y +CONFIG_NF_CONNTRACK_NETBIOS_NS=y +CONFIG_NF_CONNTRACK_PPTP=y +CONFIG_NF_CONNTRACK_SANE=y +CONFIG_NF_CONNTRACK_TFTP=y +CONFIG_NF_CT_NETLINK=y +CONFIG_NETFILTER_XT_TARGET_CLASSIFY=y +CONFIG_NETFILTER_XT_TARGET_CONNMARK=y +CONFIG_NETFILTER_XT_TARGET_CONNSECMARK=y +CONFIG_NETFILTER_XT_TARGET_IDLETIMER=y +CONFIG_NETFILTER_XT_TARGET_MARK=y +CONFIG_NETFILTER_XT_TARGET_NFLOG=y +CONFIG_NETFILTER_XT_TARGET_NFQUEUE=y +CONFIG_NETFILTER_XT_TARGET_TPROXY=y +CONFIG_NETFILTER_XT_TARGET_TRACE=y +CONFIG_NETFILTER_XT_TARGET_SECMARK=y +CONFIG_NETFILTER_XT_TARGET_TCPMSS=y +CONFIG_NETFILTER_XT_MATCH_COMMENT=y +CONFIG_NETFILTER_XT_MATCH_CONNLIMIT=y +CONFIG_NETFILTER_XT_MATCH_CONNMARK=y +CONFIG_NETFILTER_XT_MATCH_CONNTRACK=y +CONFIG_NETFILTER_XT_MATCH_HASHLIMIT=y +CONFIG_NETFILTER_XT_MATCH_HELPER=y +CONFIG_NETFILTER_XT_MATCH_IPRANGE=y +CONFIG_NETFILTER_XT_MATCH_LENGTH=y +CONFIG_NETFILTER_XT_MATCH_LIMIT=y +CONFIG_NETFILTER_XT_MATCH_MAC=y +CONFIG_NETFILTER_XT_MATCH_MARK=y +CONFIG_NETFILTER_XT_MATCH_POLICY=y +CONFIG_NETFILTER_XT_MATCH_PKTTYPE=y +CONFIG_NETFILTER_XT_MATCH_QTAGUID=y +CONFIG_NETFILTER_XT_MATCH_QUOTA=y +CONFIG_NETFILTER_XT_MATCH_QUOTA2=y +CONFIG_NETFILTER_XT_MATCH_SOCKET=y +CONFIG_NETFILTER_XT_MATCH_STATE=y +CONFIG_NETFILTER_XT_MATCH_STATISTIC=y +CONFIG_NETFILTER_XT_MATCH_STRING=y +CONFIG_NETFILTER_XT_MATCH_TIME=y +CONFIG_NETFILTER_XT_MATCH_U32=y +CONFIG_NF_CONNTRACK_IPV4=y +CONFIG_IP_NF_IPTABLES=y +CONFIG_IP_NF_MATCH_AH=y +CONFIG_IP_NF_MATCH_ECN=y +CONFIG_IP_NF_MATCH_RPFILTER=y +CONFIG_IP_NF_MATCH_TTL=y +CONFIG_IP_NF_FILTER=y +CONFIG_IP_NF_TARGET_REJECT=y +CONFIG_IP_NF_MANGLE=y +CONFIG_IP_NF_TARGET_ECN=y +CONFIG_IP_NF_TARGET_TTL=y +CONFIG_IP_NF_RAW=y +CONFIG_IP_NF_SECURITY=y +CONFIG_IP_NF_ARPTABLES=y +CONFIG_IP_NF_ARPFILTER=y +CONFIG_IP_NF_ARP_MANGLE=y +CONFIG_NF_CONNTRACK_IPV6=y +CONFIG_IP6_NF_IPTABLES=y +CONFIG_IP6_NF_MATCH_AH=y +CONFIG_IP6_NF_MATCH_EUI64=y +CONFIG_IP6_NF_MATCH_FRAG=y +CONFIG_IP6_NF_MATCH_OPTS=y +CONFIG_IP6_NF_MATCH_HL=y +CONFIG_IP6_NF_MATCH_IPV6HEADER=y +CONFIG_IP6_NF_MATCH_MH=y +CONFIG_IP6_NF_MATCH_RT=y +CONFIG_IP6_NF_TARGET_HL=y +CONFIG_IP6_NF_FILTER=y +CONFIG_IP6_NF_TARGET_REJECT=y +CONFIG_IP6_NF_MANGLE=y +CONFIG_IP6_NF_RAW=y +CONFIG_BRIDGE=y +CONFIG_NET_SCHED=y +CONFIG_NET_SCH_HTB=y +CONFIG_NET_CLS_U32=y +CONFIG_NET_EMATCH=y +CONFIG_NET_EMATCH_U32=y +CONFIG_NET_CLS_ACT=y +# CONFIG_WIRELESS is not set +CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug" +CONFIG_BLK_DEV_LOOP=y +CONFIG_BLK_DEV_RAM=y +CONFIG_BLK_DEV_RAM_SIZE=8192 +CONFIG_VIRTIO_BLK=y +CONFIG_SCSI=y +# CONFIG_SCSI_PROC_FS is not set +CONFIG_BLK_DEV_SD=y +# CONFIG_SCSI_LOWLEVEL is not set +CONFIG_MD=y +CONFIG_BLK_DEV_DM=y +CONFIG_DM_CRYPT=y +CONFIG_DM_UEVENT=y +CONFIG_DM_VERITY=y +CONFIG_DM_VERITY_FEC=y +CONFIG_NETDEVICES=y +CONFIG_TUN=y +CONFIG_VIRTIO_NET=y +CONFIG_SMC91X=y +CONFIG_PPP=y +CONFIG_PPP_BSDCOMP=y +CONFIG_PPP_DEFLATE=y +CONFIG_PPP_MPPE=y +# CONFIG_WLAN is not set +CONFIG_INPUT_EVDEV=y +CONFIG_INPUT_KEYRESET=y +CONFIG_KEYBOARD_GOLDFISH_EVENTS=y +# CONFIG_INPUT_MOUSE is not set +CONFIG_INPUT_JOYSTICK=y +CONFIG_INPUT_TABLET=y +CONFIG_INPUT_MISC=y +CONFIG_INPUT_KEYCHORD=y +CONFIG_INPUT_UINPUT=y +CONFIG_INPUT_GPIO=y +# CONFIG_SERIO_SERPORT is not set +# CONFIG_VT is not set +# CONFIG_LEGACY_PTYS is not set +# CONFIG_DEVMEM is not set +# CONFIG_DEVKMEM is not set +CONFIG_SERIAL_AMBA_PL011=y +CONFIG_SERIAL_AMBA_PL011_CONSOLE=y +CONFIG_VIRTIO_CONSOLE=y +# CONFIG_HW_RANDOM is not set +CONFIG_BATTERY_GOLDFISH=y +# CONFIG_HWMON is not set +CONFIG_MEDIA_SUPPORT=y +CONFIG_FB=y +CONFIG_FB_GOLDFISH=y +CONFIG_FB_SIMPLE=y +CONFIG_BACKLIGHT_LCD_SUPPORT=y +CONFIG_LOGO=y +# CONFIG_LOGO_LINUX_MONO is not set +# CONFIG_LOGO_LINUX_VGA16 is not set +CONFIG_SOUND=y +CONFIG_SND=y +CONFIG_HIDRAW=y +CONFIG_UHID=y +CONFIG_HID_A4TECH=y +CONFIG_HID_ACRUX=y +CONFIG_HID_ACRUX_FF=y +CONFIG_HID_APPLE=y +CONFIG_HID_BELKIN=y +CONFIG_HID_CHERRY=y +CONFIG_HID_CHICONY=y +CONFIG_HID_PRODIKEYS=y +CONFIG_HID_CYPRESS=y +CONFIG_HID_DRAGONRISE=y +CONFIG_DRAGONRISE_FF=y +CONFIG_HID_EMS_FF=y +CONFIG_HID_ELECOM=y +CONFIG_HID_EZKEY=y +CONFIG_HID_KEYTOUCH=y +CONFIG_HID_KYE=y +CONFIG_HID_WALTOP=y +CONFIG_HID_GYRATION=y +CONFIG_HID_TWINHAN=y +CONFIG_HID_KENSINGTON=y +CONFIG_HID_LCPOWER=y +CONFIG_HID_LOGITECH=y +CONFIG_HID_LOGITECH_DJ=y +CONFIG_LOGITECH_FF=y +CONFIG_LOGIRUMBLEPAD2_FF=y +CONFIG_LOGIG940_FF=y +CONFIG_HID_MAGICMOUSE=y +CONFIG_HID_MICROSOFT=y +CONFIG_HID_MONTEREY=y +CONFIG_HID_MULTITOUCH=y +CONFIG_HID_ORTEK=y +CONFIG_HID_PANTHERLORD=y +CONFIG_PANTHERLORD_FF=y +CONFIG_HID_PETALYNX=y +CONFIG_HID_PICOLCD=y +CONFIG_HID_PRIMAX=y +CONFIG_HID_SAITEK=y +CONFIG_HID_SAMSUNG=y +CONFIG_HID_SPEEDLINK=y +CONFIG_HID_SUNPLUS=y +CONFIG_HID_GREENASIA=y +CONFIG_GREENASIA_FF=y +CONFIG_HID_SMARTJOYPLUS=y +CONFIG_SMARTJOYPLUS_FF=y +CONFIG_HID_TIVO=y +CONFIG_HID_TOPSEED=y +CONFIG_HID_THRUSTMASTER=y +CONFIG_HID_WACOM=y +CONFIG_HID_WIIMOTE=y +CONFIG_HID_ZEROPLUS=y +CONFIG_HID_ZYDACRON=y +# CONFIG_USB_SUPPORT is not set +CONFIG_RTC_CLASS=y +CONFIG_VIRTIO_MMIO=y +CONFIG_STAGING=y +CONFIG_ASHMEM=y +CONFIG_ANDROID_TIMED_GPIO=y +CONFIG_ANDROID_LOW_MEMORY_KILLER=y +CONFIG_SYNC=y +CONFIG_SW_SYNC=y +CONFIG_SW_SYNC_USER=y +CONFIG_ION=y +CONFIG_GOLDFISH_AUDIO=y +CONFIG_GOLDFISH=y +CONFIG_GOLDFISH_PIPE=y +# CONFIG_IOMMU_SUPPORT is not set +CONFIG_ANDROID=y +CONFIG_ANDROID_BINDER_IPC=y +CONFIG_EXT2_FS=y +CONFIG_EXT4_FS=y +CONFIG_EXT4_FS_SECURITY=y +CONFIG_QUOTA=y +CONFIG_FUSE_FS=y +CONFIG_CUSE=y +CONFIG_MSDOS_FS=y +CONFIG_VFAT_FS=y +CONFIG_TMPFS=y +CONFIG_TMPFS_POSIX_ACL=y +# CONFIG_MISC_FILESYSTEMS is not set +CONFIG_NFS_FS=y +CONFIG_ROOT_NFS=y +CONFIG_NLS_CODEPAGE_437=y +CONFIG_NLS_ISO8859_1=y +CONFIG_DEBUG_INFO=y +CONFIG_DEBUG_FS=y +CONFIG_MAGIC_SYSRQ=y +CONFIG_PANIC_TIMEOUT=5 +# CONFIG_SCHED_DEBUG is not set +CONFIG_SCHEDSTATS=y +CONFIG_TIMER_STATS=y +# CONFIG_FTRACE is not set +CONFIG_ATOMIC64_SELFTEST=y +CONFIG_DEBUG_RODATA=y +CONFIG_SECURITY=y +CONFIG_SECURITY_NETWORK=y +CONFIG_SECURITY_SELINUX=y diff --git a/arch/x86/configs/i386_ranchu_defconfig b/arch/x86/configs/i386_ranchu_defconfig new file mode 100644 index 000000000000..18d3675d28f0 --- /dev/null +++ b/arch/x86/configs/i386_ranchu_defconfig @@ -0,0 +1,422 @@ +# CONFIG_64BIT is not set +# CONFIG_LOCALVERSION_AUTO is not set +CONFIG_POSIX_MQUEUE=y +CONFIG_AUDIT=y +CONFIG_NO_HZ=y +CONFIG_HIGH_RES_TIMERS=y +CONFIG_BSD_PROCESS_ACCT=y +CONFIG_TASKSTATS=y +CONFIG_TASK_DELAY_ACCT=y +CONFIG_TASK_XACCT=y +CONFIG_TASK_IO_ACCOUNTING=y +CONFIG_CGROUPS=y +CONFIG_CGROUP_DEBUG=y +CONFIG_CGROUP_FREEZER=y +CONFIG_CGROUP_CPUACCT=y +CONFIG_CGROUP_SCHED=y +CONFIG_RT_GROUP_SCHED=y +CONFIG_BLK_DEV_INITRD=y +CONFIG_CC_OPTIMIZE_FOR_SIZE=y +CONFIG_SYSCTL_SYSCALL=y +CONFIG_KALLSYMS_ALL=y +CONFIG_EMBEDDED=y +# CONFIG_COMPAT_BRK is not set +CONFIG_ARCH_MMAP_RND_BITS=16 +CONFIG_PARTITION_ADVANCED=y +CONFIG_OSF_PARTITION=y +CONFIG_AMIGA_PARTITION=y +CONFIG_MAC_PARTITION=y +CONFIG_BSD_DISKLABEL=y +CONFIG_MINIX_SUBPARTITION=y +CONFIG_SOLARIS_X86_PARTITION=y +CONFIG_UNIXWARE_DISKLABEL=y +CONFIG_SGI_PARTITION=y +CONFIG_SUN_PARTITION=y +CONFIG_KARMA_PARTITION=y +CONFIG_SMP=y +CONFIG_X86_BIGSMP=y +CONFIG_MCORE2=y +CONFIG_X86_GENERIC=y +CONFIG_HPET_TIMER=y +CONFIG_NR_CPUS=512 +CONFIG_PREEMPT=y +# CONFIG_X86_MCE is not set +CONFIG_X86_REBOOTFIXUPS=y +CONFIG_X86_MSR=y +CONFIG_X86_CPUID=y +CONFIG_KSM=y +CONFIG_CMA=y +# CONFIG_MTRR_SANITIZER is not set +CONFIG_EFI=y +CONFIG_EFI_STUB=y +CONFIG_HZ_100=y +CONFIG_PHYSICAL_START=0x100000 +CONFIG_PM_AUTOSLEEP=y +CONFIG_PM_WAKELOCKS=y +CONFIG_PM_WAKELOCKS_LIMIT=0 +# CONFIG_PM_WAKELOCKS_GC is not set +CONFIG_PM_DEBUG=y +CONFIG_CPU_FREQ=y +# CONFIG_CPU_FREQ_STAT is not set +CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMAND=y +CONFIG_CPU_FREQ_GOV_USERSPACE=y +CONFIG_PCIEPORTBUS=y +# CONFIG_PCIEASPM is not set +CONFIG_PCCARD=y +CONFIG_YENTA=y +CONFIG_HOTPLUG_PCI=y +# CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set +CONFIG_BINFMT_MISC=y +CONFIG_NET=y +CONFIG_PACKET=y +CONFIG_UNIX=y +CONFIG_XFRM_USER=y +CONFIG_NET_KEY=y +CONFIG_INET=y +CONFIG_IP_MULTICAST=y +CONFIG_IP_ADVANCED_ROUTER=y +CONFIG_IP_MULTIPLE_TABLES=y +CONFIG_IP_ROUTE_MULTIPATH=y +CONFIG_IP_ROUTE_VERBOSE=y +CONFIG_IP_PNP=y +CONFIG_IP_PNP_DHCP=y +CONFIG_IP_PNP_BOOTP=y +CONFIG_IP_PNP_RARP=y +CONFIG_IP_MROUTE=y +CONFIG_IP_PIMSM_V1=y +CONFIG_IP_PIMSM_V2=y +CONFIG_SYN_COOKIES=y +CONFIG_INET_ESP=y +# CONFIG_INET_XFRM_MODE_BEET is not set +# CONFIG_INET_LRO is not set +CONFIG_INET_DIAG_DESTROY=y +CONFIG_IPV6_ROUTER_PREF=y +CONFIG_IPV6_ROUTE_INFO=y +CONFIG_IPV6_OPTIMISTIC_DAD=y +CONFIG_INET6_AH=y +CONFIG_INET6_ESP=y +CONFIG_INET6_IPCOMP=y +CONFIG_IPV6_MIP6=y +CONFIG_IPV6_MULTIPLE_TABLES=y +CONFIG_NETLABEL=y +CONFIG_NETFILTER=y +CONFIG_NF_CONNTRACK=y +CONFIG_NF_CONNTRACK_SECMARK=y +CONFIG_NF_CONNTRACK_EVENTS=y +CONFIG_NF_CT_PROTO_DCCP=y +CONFIG_NF_CT_PROTO_SCTP=y +CONFIG_NF_CT_PROTO_UDPLITE=y +CONFIG_NF_CONNTRACK_AMANDA=y +CONFIG_NF_CONNTRACK_FTP=y +CONFIG_NF_CONNTRACK_H323=y +CONFIG_NF_CONNTRACK_IRC=y +CONFIG_NF_CONNTRACK_NETBIOS_NS=y +CONFIG_NF_CONNTRACK_PPTP=y +CONFIG_NF_CONNTRACK_SANE=y +CONFIG_NF_CONNTRACK_TFTP=y +CONFIG_NF_CT_NETLINK=y +CONFIG_NETFILTER_XT_TARGET_CLASSIFY=y +CONFIG_NETFILTER_XT_TARGET_CONNMARK=y +CONFIG_NETFILTER_XT_TARGET_CONNSECMARK=y +CONFIG_NETFILTER_XT_TARGET_IDLETIMER=y +CONFIG_NETFILTER_XT_TARGET_MARK=y +CONFIG_NETFILTER_XT_TARGET_NFLOG=y +CONFIG_NETFILTER_XT_TARGET_NFQUEUE=y +CONFIG_NETFILTER_XT_TARGET_TPROXY=y +CONFIG_NETFILTER_XT_TARGET_TRACE=y +CONFIG_NETFILTER_XT_TARGET_SECMARK=y +CONFIG_NETFILTER_XT_TARGET_TCPMSS=y +CONFIG_NETFILTER_XT_MATCH_COMMENT=y +CONFIG_NETFILTER_XT_MATCH_CONNLIMIT=y +CONFIG_NETFILTER_XT_MATCH_CONNMARK=y +CONFIG_NETFILTER_XT_MATCH_CONNTRACK=y +CONFIG_NETFILTER_XT_MATCH_HASHLIMIT=y +CONFIG_NETFILTER_XT_MATCH_HELPER=y +CONFIG_NETFILTER_XT_MATCH_IPRANGE=y +CONFIG_NETFILTER_XT_MATCH_LENGTH=y +CONFIG_NETFILTER_XT_MATCH_LIMIT=y +CONFIG_NETFILTER_XT_MATCH_MAC=y +CONFIG_NETFILTER_XT_MATCH_MARK=y +CONFIG_NETFILTER_XT_MATCH_POLICY=y +CONFIG_NETFILTER_XT_MATCH_PKTTYPE=y +CONFIG_NETFILTER_XT_MATCH_QTAGUID=y +CONFIG_NETFILTER_XT_MATCH_QUOTA=y +CONFIG_NETFILTER_XT_MATCH_QUOTA2=y +CONFIG_NETFILTER_XT_MATCH_SOCKET=y +CONFIG_NETFILTER_XT_MATCH_STATE=y +CONFIG_NETFILTER_XT_MATCH_STATISTIC=y +CONFIG_NETFILTER_XT_MATCH_STRING=y +CONFIG_NETFILTER_XT_MATCH_TIME=y +CONFIG_NETFILTER_XT_MATCH_U32=y +CONFIG_NF_CONNTRACK_IPV4=y +CONFIG_IP_NF_IPTABLES=y +CONFIG_IP_NF_MATCH_AH=y +CONFIG_IP_NF_MATCH_ECN=y +CONFIG_IP_NF_MATCH_TTL=y +CONFIG_IP_NF_FILTER=y +CONFIG_IP_NF_TARGET_REJECT=y +CONFIG_IP_NF_MANGLE=y +CONFIG_IP_NF_RAW=y +CONFIG_IP_NF_SECURITY=y +CONFIG_IP_NF_ARPTABLES=y +CONFIG_IP_NF_ARPFILTER=y +CONFIG_IP_NF_ARP_MANGLE=y +CONFIG_NF_CONNTRACK_IPV6=y +CONFIG_IP6_NF_IPTABLES=y +CONFIG_IP6_NF_FILTER=y +CONFIG_IP6_NF_TARGET_REJECT=y +CONFIG_IP6_NF_MANGLE=y +CONFIG_IP6_NF_RAW=y +CONFIG_NET_SCHED=y +CONFIG_NET_SCH_HTB=y +CONFIG_NET_CLS_U32=y +CONFIG_NET_EMATCH=y +CONFIG_NET_EMATCH_U32=y +CONFIG_NET_CLS_ACT=y +CONFIG_CFG80211=y +CONFIG_MAC80211=y +CONFIG_MAC80211_LEDS=y +CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug" +CONFIG_DMA_CMA=y +CONFIG_CMA_SIZE_MBYTES=16 +CONFIG_CONNECTOR=y +CONFIG_BLK_DEV_LOOP=y +CONFIG_BLK_DEV_RAM=y +CONFIG_BLK_DEV_RAM_SIZE=8192 +CONFIG_VIRTIO_BLK=y +CONFIG_BLK_DEV_SD=y +CONFIG_BLK_DEV_SR=y +CONFIG_BLK_DEV_SR_VENDOR=y +CONFIG_CHR_DEV_SG=y +CONFIG_SCSI_CONSTANTS=y +CONFIG_SCSI_SPI_ATTRS=y +CONFIG_SCSI_ISCSI_ATTRS=y +# CONFIG_SCSI_LOWLEVEL is not set +CONFIG_ATA=y +CONFIG_SATA_AHCI=y +CONFIG_ATA_PIIX=y +CONFIG_PATA_AMD=y +CONFIG_PATA_OLDPIIX=y +CONFIG_PATA_SCH=y +CONFIG_PATA_MPIIX=y +CONFIG_ATA_GENERIC=y +CONFIG_MD=y +CONFIG_BLK_DEV_MD=y +CONFIG_BLK_DEV_DM=y +CONFIG_DM_DEBUG=y +CONFIG_DM_CRYPT=y +CONFIG_DM_MIRROR=y +CONFIG_DM_ZERO=y +CONFIG_DM_UEVENT=y +CONFIG_DM_VERITY=y +CONFIG_DM_VERITY_FEC=y +CONFIG_NETDEVICES=y +CONFIG_NETCONSOLE=y +CONFIG_TUN=y +CONFIG_VIRTIO_NET=y +CONFIG_BNX2=y +CONFIG_TIGON3=y +CONFIG_NET_TULIP=y +CONFIG_E100=y +CONFIG_E1000=y +CONFIG_E1000E=y +CONFIG_SKY2=y +CONFIG_NE2K_PCI=y +CONFIG_FORCEDETH=y +CONFIG_8139TOO=y +# CONFIG_8139TOO_PIO is not set +CONFIG_R8169=y +CONFIG_FDDI=y +CONFIG_PPP=y +CONFIG_PPP_BSDCOMP=y +CONFIG_PPP_DEFLATE=y +CONFIG_PPP_MPPE=y +CONFIG_USB_USBNET=y +CONFIG_INPUT_POLLDEV=y +# CONFIG_INPUT_MOUSEDEV_PSAUX is not set +CONFIG_INPUT_EVDEV=y +CONFIG_INPUT_KEYRESET=y +# CONFIG_KEYBOARD_ATKBD is not set +CONFIG_KEYBOARD_GOLDFISH_EVENTS=y +# CONFIG_INPUT_MOUSE is not set +CONFIG_INPUT_JOYSTICK=y +CONFIG_JOYSTICK_XPAD=y +CONFIG_JOYSTICK_XPAD_FF=y +CONFIG_JOYSTICK_XPAD_LEDS=y +CONFIG_INPUT_TABLET=y +CONFIG_TABLET_USB_ACECAD=y +CONFIG_TABLET_USB_AIPTEK=y +CONFIG_TABLET_USB_GTCO=y +CONFIG_TABLET_USB_HANWANG=y +CONFIG_TABLET_USB_KBTAB=y +CONFIG_INPUT_TOUCHSCREEN=y +CONFIG_INPUT_MISC=y +CONFIG_INPUT_KEYCHORD=y +CONFIG_INPUT_UINPUT=y +CONFIG_INPUT_GPIO=y +# CONFIG_SERIO is not set +# CONFIG_VT is not set +# CONFIG_LEGACY_PTYS is not set +CONFIG_SERIAL_NONSTANDARD=y +# CONFIG_DEVMEM is not set +# CONFIG_DEVKMEM is not set +CONFIG_SERIAL_8250=y +CONFIG_SERIAL_8250_CONSOLE=y +CONFIG_VIRTIO_CONSOLE=y +CONFIG_NVRAM=y +CONFIG_I2C_I801=y +CONFIG_BATTERY_GOLDFISH=y +CONFIG_WATCHDOG=y +CONFIG_MEDIA_SUPPORT=y +CONFIG_AGP=y +CONFIG_AGP_AMD64=y +CONFIG_AGP_INTEL=y +CONFIG_DRM=y +CONFIG_FB_MODE_HELPERS=y +CONFIG_FB_TILEBLITTING=y +CONFIG_FB_EFI=y +CONFIG_FB_GOLDFISH=y +CONFIG_BACKLIGHT_LCD_SUPPORT=y +# CONFIG_LCD_CLASS_DEVICE is not set +CONFIG_SOUND=y +CONFIG_SND=y +CONFIG_HIDRAW=y +CONFIG_UHID=y +CONFIG_HID_A4TECH=y +CONFIG_HID_ACRUX=y +CONFIG_HID_ACRUX_FF=y +CONFIG_HID_APPLE=y +CONFIG_HID_BELKIN=y +CONFIG_HID_CHERRY=y +CONFIG_HID_CHICONY=y +CONFIG_HID_PRODIKEYS=y +CONFIG_HID_CYPRESS=y +CONFIG_HID_DRAGONRISE=y +CONFIG_DRAGONRISE_FF=y +CONFIG_HID_EMS_FF=y +CONFIG_HID_ELECOM=y +CONFIG_HID_EZKEY=y +CONFIG_HID_HOLTEK=y +CONFIG_HID_KEYTOUCH=y +CONFIG_HID_KYE=y +CONFIG_HID_UCLOGIC=y +CONFIG_HID_WALTOP=y +CONFIG_HID_GYRATION=y +CONFIG_HID_TWINHAN=y +CONFIG_HID_KENSINGTON=y +CONFIG_HID_LCPOWER=y +CONFIG_HID_LOGITECH=y +CONFIG_HID_LOGITECH_DJ=y +CONFIG_LOGITECH_FF=y +CONFIG_LOGIRUMBLEPAD2_FF=y +CONFIG_LOGIG940_FF=y +CONFIG_HID_MAGICMOUSE=y +CONFIG_HID_MICROSOFT=y +CONFIG_HID_MONTEREY=y +CONFIG_HID_MULTITOUCH=y +CONFIG_HID_NTRIG=y +CONFIG_HID_ORTEK=y +CONFIG_HID_PANTHERLORD=y +CONFIG_PANTHERLORD_FF=y +CONFIG_HID_PETALYNX=y +CONFIG_HID_PICOLCD=y +CONFIG_HID_PRIMAX=y +CONFIG_HID_ROCCAT=y +CONFIG_HID_SAITEK=y +CONFIG_HID_SAMSUNG=y +CONFIG_HID_SONY=y +CONFIG_HID_SPEEDLINK=y +CONFIG_HID_SUNPLUS=y +CONFIG_HID_GREENASIA=y +CONFIG_GREENASIA_FF=y +CONFIG_HID_SMARTJOYPLUS=y +CONFIG_SMARTJOYPLUS_FF=y +CONFIG_HID_TIVO=y +CONFIG_HID_TOPSEED=y +CONFIG_HID_THRUSTMASTER=y +CONFIG_HID_WACOM=y +CONFIG_HID_WIIMOTE=y +CONFIG_HID_ZEROPLUS=y +CONFIG_HID_ZYDACRON=y +CONFIG_HID_PID=y +CONFIG_USB_HIDDEV=y +CONFIG_USB_ANNOUNCE_NEW_DEVICES=y +CONFIG_USB_MON=y +CONFIG_USB_EHCI_HCD=y +# CONFIG_USB_EHCI_TT_NEWSCHED is not set +CONFIG_USB_OHCI_HCD=y +CONFIG_USB_UHCI_HCD=y +CONFIG_USB_PRINTER=y +CONFIG_USB_STORAGE=y +CONFIG_USB_OTG_WAKELOCK=y +CONFIG_EDAC=y +CONFIG_RTC_CLASS=y +# CONFIG_RTC_HCTOSYS is not set +CONFIG_DMADEVICES=y +CONFIG_VIRTIO_PCI=y +CONFIG_STAGING=y +CONFIG_ASHMEM=y +CONFIG_ANDROID_LOW_MEMORY_KILLER=y +CONFIG_SYNC=y +CONFIG_SW_SYNC=y +CONFIG_SYNC_FILE=y +CONFIG_ION=y +CONFIG_GOLDFISH_AUDIO=y +CONFIG_SND_HDA_INTEL=y +CONFIG_GOLDFISH=y +CONFIG_GOLDFISH_PIPE=y +CONFIG_GOLDFISH_SYNC=y +CONFIG_ANDROID=y +CONFIG_ANDROID_BINDER_IPC=y +CONFIG_ISCSI_IBFT_FIND=y +CONFIG_EXT4_FS=y +CONFIG_EXT4_FS_SECURITY=y +CONFIG_QUOTA=y +CONFIG_QUOTA_NETLINK_INTERFACE=y +# CONFIG_PRINT_QUOTA_WARNING is not set +CONFIG_FUSE_FS=y +CONFIG_ISO9660_FS=y +CONFIG_JOLIET=y +CONFIG_ZISOFS=y +CONFIG_MSDOS_FS=y +CONFIG_VFAT_FS=y +CONFIG_PROC_KCORE=y +CONFIG_TMPFS=y +CONFIG_TMPFS_POSIX_ACL=y +CONFIG_HUGETLBFS=y +CONFIG_PSTORE=y +CONFIG_PSTORE_CONSOLE=y +CONFIG_PSTORE_RAM=y +# CONFIG_NETWORK_FILESYSTEMS is not set +CONFIG_NLS_DEFAULT="utf8" +CONFIG_NLS_CODEPAGE_437=y +CONFIG_NLS_ASCII=y +CONFIG_NLS_ISO8859_1=y +CONFIG_NLS_UTF8=y +CONFIG_PRINTK_TIME=y +CONFIG_DEBUG_INFO=y +# CONFIG_ENABLE_WARN_DEPRECATED is not set +# CONFIG_ENABLE_MUST_CHECK is not set +CONFIG_FRAME_WARN=2048 +# CONFIG_UNUSED_SYMBOLS is not set +CONFIG_MAGIC_SYSRQ=y +CONFIG_DEBUG_MEMORY_INIT=y +CONFIG_PANIC_TIMEOUT=5 +CONFIG_SCHEDSTATS=y +CONFIG_TIMER_STATS=y +CONFIG_SCHED_TRACER=y +CONFIG_BLK_DEV_IO_TRACE=y +CONFIG_PROVIDE_OHCI1394_DMA_INIT=y +CONFIG_KEYS=y +CONFIG_SECURITY=y +CONFIG_SECURITY_NETWORK=y +CONFIG_SECURITY_SELINUX=y +CONFIG_CRYPTO_AES_586=y +CONFIG_CRYPTO_TWOFISH=y +CONFIG_ASYMMETRIC_KEY_TYPE=y +CONFIG_ASYMMETRIC_PUBLIC_KEY_SUBTYPE=y +CONFIG_X509_CERTIFICATE_PARSER=y +CONFIG_PKCS7_MESSAGE_PARSER=y +CONFIG_PKCS7_TEST_KEY=y +# CONFIG_VIRTUALIZATION is not set +CONFIG_CRC_T10DIF=y diff --git a/arch/x86/configs/x86_64_ranchu_defconfig b/arch/x86/configs/x86_64_ranchu_defconfig new file mode 100644 index 000000000000..7eff3002db18 --- /dev/null +++ b/arch/x86/configs/x86_64_ranchu_defconfig @@ -0,0 +1,417 @@ +# CONFIG_LOCALVERSION_AUTO is not set +CONFIG_POSIX_MQUEUE=y +CONFIG_AUDIT=y +CONFIG_NO_HZ=y +CONFIG_HIGH_RES_TIMERS=y +CONFIG_BSD_PROCESS_ACCT=y +CONFIG_TASKSTATS=y +CONFIG_TASK_DELAY_ACCT=y +CONFIG_TASK_XACCT=y +CONFIG_TASK_IO_ACCOUNTING=y +CONFIG_CGROUPS=y +CONFIG_CGROUP_DEBUG=y +CONFIG_CGROUP_FREEZER=y +CONFIG_CGROUP_CPUACCT=y +CONFIG_CGROUP_SCHED=y +CONFIG_RT_GROUP_SCHED=y +CONFIG_BLK_DEV_INITRD=y +CONFIG_CC_OPTIMIZE_FOR_SIZE=y +CONFIG_SYSCTL_SYSCALL=y +CONFIG_KALLSYMS_ALL=y +CONFIG_EMBEDDED=y +# CONFIG_COMPAT_BRK is not set +CONFIG_ARCH_MMAP_RND_BITS=32 +CONFIG_ARCH_MMAP_RND_COMPAT_BITS=16 +CONFIG_PARTITION_ADVANCED=y +CONFIG_OSF_PARTITION=y +CONFIG_AMIGA_PARTITION=y +CONFIG_MAC_PARTITION=y +CONFIG_BSD_DISKLABEL=y +CONFIG_MINIX_SUBPARTITION=y +CONFIG_SOLARIS_X86_PARTITION=y +CONFIG_UNIXWARE_DISKLABEL=y +CONFIG_SGI_PARTITION=y +CONFIG_SUN_PARTITION=y +CONFIG_KARMA_PARTITION=y +CONFIG_SMP=y +CONFIG_MCORE2=y +CONFIG_MAXSMP=y +CONFIG_PREEMPT=y +# CONFIG_X86_MCE is not set +CONFIG_X86_MSR=y +CONFIG_X86_CPUID=y +CONFIG_KSM=y +CONFIG_CMA=y +# CONFIG_MTRR_SANITIZER is not set +CONFIG_EFI=y +CONFIG_EFI_STUB=y +CONFIG_HZ_100=y +CONFIG_PHYSICAL_START=0x100000 +CONFIG_PM_AUTOSLEEP=y +CONFIG_PM_WAKELOCKS=y +CONFIG_PM_WAKELOCKS_LIMIT=0 +# CONFIG_PM_WAKELOCKS_GC is not set +CONFIG_PM_DEBUG=y +CONFIG_CPU_FREQ=y +# CONFIG_CPU_FREQ_STAT is not set +CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMAND=y +CONFIG_CPU_FREQ_GOV_USERSPACE=y +CONFIG_PCI_MMCONFIG=y +CONFIG_PCIEPORTBUS=y +# CONFIG_PCIEASPM is not set +CONFIG_PCCARD=y +CONFIG_YENTA=y +CONFIG_HOTPLUG_PCI=y +# CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set +CONFIG_BINFMT_MISC=y +CONFIG_IA32_EMULATION=y +CONFIG_NET=y +CONFIG_PACKET=y +CONFIG_UNIX=y +CONFIG_XFRM_USER=y +CONFIG_NET_KEY=y +CONFIG_INET=y +CONFIG_IP_MULTICAST=y +CONFIG_IP_ADVANCED_ROUTER=y +CONFIG_IP_MULTIPLE_TABLES=y +CONFIG_IP_ROUTE_MULTIPATH=y +CONFIG_IP_ROUTE_VERBOSE=y +CONFIG_IP_PNP=y +CONFIG_IP_PNP_DHCP=y +CONFIG_IP_PNP_BOOTP=y +CONFIG_IP_PNP_RARP=y +CONFIG_IP_MROUTE=y +CONFIG_IP_PIMSM_V1=y +CONFIG_IP_PIMSM_V2=y +CONFIG_SYN_COOKIES=y +CONFIG_INET_ESP=y +# CONFIG_INET_XFRM_MODE_BEET is not set +# CONFIG_INET_LRO is not set +CONFIG_INET_DIAG_DESTROY=y +CONFIG_IPV6_ROUTER_PREF=y +CONFIG_IPV6_ROUTE_INFO=y +CONFIG_IPV6_OPTIMISTIC_DAD=y +CONFIG_INET6_AH=y +CONFIG_INET6_ESP=y +CONFIG_INET6_IPCOMP=y +CONFIG_IPV6_MIP6=y +CONFIG_IPV6_MULTIPLE_TABLES=y +CONFIG_NETLABEL=y +CONFIG_NETFILTER=y +CONFIG_NF_CONNTRACK=y +CONFIG_NF_CONNTRACK_SECMARK=y +CONFIG_NF_CONNTRACK_EVENTS=y +CONFIG_NF_CT_PROTO_DCCP=y +CONFIG_NF_CT_PROTO_SCTP=y +CONFIG_NF_CT_PROTO_UDPLITE=y +CONFIG_NF_CONNTRACK_AMANDA=y +CONFIG_NF_CONNTRACK_FTP=y +CONFIG_NF_CONNTRACK_H323=y +CONFIG_NF_CONNTRACK_IRC=y +CONFIG_NF_CONNTRACK_NETBIOS_NS=y +CONFIG_NF_CONNTRACK_PPTP=y +CONFIG_NF_CONNTRACK_SANE=y +CONFIG_NF_CONNTRACK_TFTP=y +CONFIG_NF_CT_NETLINK=y +CONFIG_NETFILTER_XT_TARGET_CLASSIFY=y +CONFIG_NETFILTER_XT_TARGET_CONNMARK=y +CONFIG_NETFILTER_XT_TARGET_CONNSECMARK=y +CONFIG_NETFILTER_XT_TARGET_IDLETIMER=y +CONFIG_NETFILTER_XT_TARGET_MARK=y +CONFIG_NETFILTER_XT_TARGET_NFLOG=y +CONFIG_NETFILTER_XT_TARGET_NFQUEUE=y +CONFIG_NETFILTER_XT_TARGET_TPROXY=y +CONFIG_NETFILTER_XT_TARGET_TRACE=y +CONFIG_NETFILTER_XT_TARGET_SECMARK=y +CONFIG_NETFILTER_XT_TARGET_TCPMSS=y +CONFIG_NETFILTER_XT_MATCH_COMMENT=y +CONFIG_NETFILTER_XT_MATCH_CONNLIMIT=y +CONFIG_NETFILTER_XT_MATCH_CONNMARK=y +CONFIG_NETFILTER_XT_MATCH_CONNTRACK=y +CONFIG_NETFILTER_XT_MATCH_HASHLIMIT=y +CONFIG_NETFILTER_XT_MATCH_HELPER=y +CONFIG_NETFILTER_XT_MATCH_IPRANGE=y +CONFIG_NETFILTER_XT_MATCH_LENGTH=y +CONFIG_NETFILTER_XT_MATCH_LIMIT=y +CONFIG_NETFILTER_XT_MATCH_MAC=y +CONFIG_NETFILTER_XT_MATCH_MARK=y +CONFIG_NETFILTER_XT_MATCH_POLICY=y +CONFIG_NETFILTER_XT_MATCH_PKTTYPE=y +CONFIG_NETFILTER_XT_MATCH_QTAGUID=y +CONFIG_NETFILTER_XT_MATCH_QUOTA=y +CONFIG_NETFILTER_XT_MATCH_QUOTA2=y +CONFIG_NETFILTER_XT_MATCH_SOCKET=y +CONFIG_NETFILTER_XT_MATCH_STATE=y +CONFIG_NETFILTER_XT_MATCH_STATISTIC=y +CONFIG_NETFILTER_XT_MATCH_STRING=y +CONFIG_NETFILTER_XT_MATCH_TIME=y +CONFIG_NETFILTER_XT_MATCH_U32=y +CONFIG_NF_CONNTRACK_IPV4=y +CONFIG_IP_NF_IPTABLES=y +CONFIG_IP_NF_MATCH_AH=y +CONFIG_IP_NF_MATCH_ECN=y +CONFIG_IP_NF_MATCH_TTL=y +CONFIG_IP_NF_FILTER=y +CONFIG_IP_NF_TARGET_REJECT=y +CONFIG_IP_NF_MANGLE=y +CONFIG_IP_NF_RAW=y +CONFIG_IP_NF_SECURITY=y +CONFIG_IP_NF_ARPTABLES=y +CONFIG_IP_NF_ARPFILTER=y +CONFIG_IP_NF_ARP_MANGLE=y +CONFIG_NF_CONNTRACK_IPV6=y +CONFIG_IP6_NF_IPTABLES=y +CONFIG_IP6_NF_FILTER=y +CONFIG_IP6_NF_TARGET_REJECT=y +CONFIG_IP6_NF_MANGLE=y +CONFIG_IP6_NF_RAW=y +CONFIG_NET_SCHED=y +CONFIG_NET_SCH_HTB=y +CONFIG_NET_CLS_U32=y +CONFIG_NET_EMATCH=y +CONFIG_NET_EMATCH_U32=y +CONFIG_NET_CLS_ACT=y +CONFIG_CFG80211=y +CONFIG_MAC80211=y +CONFIG_MAC80211_LEDS=y +CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug" +CONFIG_DMA_CMA=y +CONFIG_CONNECTOR=y +CONFIG_BLK_DEV_LOOP=y +CONFIG_BLK_DEV_RAM=y +CONFIG_BLK_DEV_RAM_SIZE=8192 +CONFIG_VIRTIO_BLK=y +CONFIG_BLK_DEV_SD=y +CONFIG_BLK_DEV_SR=y +CONFIG_BLK_DEV_SR_VENDOR=y +CONFIG_CHR_DEV_SG=y +CONFIG_SCSI_CONSTANTS=y +CONFIG_SCSI_SPI_ATTRS=y +CONFIG_SCSI_ISCSI_ATTRS=y +# CONFIG_SCSI_LOWLEVEL is not set +CONFIG_ATA=y +CONFIG_SATA_AHCI=y +CONFIG_ATA_PIIX=y +CONFIG_PATA_AMD=y +CONFIG_PATA_OLDPIIX=y +CONFIG_PATA_SCH=y +CONFIG_PATA_MPIIX=y +CONFIG_ATA_GENERIC=y +CONFIG_MD=y +CONFIG_BLK_DEV_MD=y +CONFIG_BLK_DEV_DM=y +CONFIG_DM_DEBUG=y +CONFIG_DM_CRYPT=y +CONFIG_DM_MIRROR=y +CONFIG_DM_ZERO=y +CONFIG_DM_UEVENT=y +CONFIG_DM_VERITY=y +CONFIG_DM_VERITY_FEC=y +CONFIG_NETDEVICES=y +CONFIG_NETCONSOLE=y +CONFIG_TUN=y +CONFIG_VIRTIO_NET=y +CONFIG_BNX2=y +CONFIG_TIGON3=y +CONFIG_NET_TULIP=y +CONFIG_E100=y +CONFIG_E1000=y +CONFIG_E1000E=y +CONFIG_SKY2=y +CONFIG_NE2K_PCI=y +CONFIG_FORCEDETH=y +CONFIG_8139TOO=y +# CONFIG_8139TOO_PIO is not set +CONFIG_R8169=y +CONFIG_FDDI=y +CONFIG_PPP=y +CONFIG_PPP_BSDCOMP=y +CONFIG_PPP_DEFLATE=y +CONFIG_PPP_MPPE=y +CONFIG_USB_USBNET=y +CONFIG_INPUT_POLLDEV=y +# CONFIG_INPUT_MOUSEDEV_PSAUX is not set +CONFIG_INPUT_EVDEV=y +CONFIG_INPUT_KEYRESET=y +# CONFIG_KEYBOARD_ATKBD is not set +CONFIG_KEYBOARD_GOLDFISH_EVENTS=y +# CONFIG_INPUT_MOUSE is not set +CONFIG_INPUT_JOYSTICK=y +CONFIG_JOYSTICK_XPAD=y +CONFIG_JOYSTICK_XPAD_FF=y +CONFIG_JOYSTICK_XPAD_LEDS=y +CONFIG_INPUT_TABLET=y +CONFIG_TABLET_USB_ACECAD=y +CONFIG_TABLET_USB_AIPTEK=y +CONFIG_TABLET_USB_GTCO=y +CONFIG_TABLET_USB_HANWANG=y +CONFIG_TABLET_USB_KBTAB=y +CONFIG_INPUT_TOUCHSCREEN=y +CONFIG_INPUT_MISC=y +CONFIG_INPUT_KEYCHORD=y +CONFIG_INPUT_UINPUT=y +CONFIG_INPUT_GPIO=y +# CONFIG_SERIO is not set +# CONFIG_VT is not set +# CONFIG_LEGACY_PTYS is not set +CONFIG_SERIAL_NONSTANDARD=y +# CONFIG_DEVMEM is not set +# CONFIG_DEVKMEM is not set +CONFIG_SERIAL_8250=y +CONFIG_SERIAL_8250_CONSOLE=y +CONFIG_VIRTIO_CONSOLE=y +CONFIG_NVRAM=y +CONFIG_I2C_I801=y +CONFIG_BATTERY_GOLDFISH=y +CONFIG_WATCHDOG=y +CONFIG_MEDIA_SUPPORT=y +CONFIG_AGP=y +CONFIG_AGP_AMD64=y +CONFIG_AGP_INTEL=y +CONFIG_DRM=y +CONFIG_FB_MODE_HELPERS=y +CONFIG_FB_TILEBLITTING=y +CONFIG_FB_EFI=y +CONFIG_FB_GOLDFISH=y +CONFIG_BACKLIGHT_LCD_SUPPORT=y +# CONFIG_LCD_CLASS_DEVICE is not set +CONFIG_SOUND=y +CONFIG_SND=y +CONFIG_HIDRAW=y +CONFIG_UHID=y +CONFIG_HID_A4TECH=y +CONFIG_HID_ACRUX=y +CONFIG_HID_ACRUX_FF=y +CONFIG_HID_APPLE=y +CONFIG_HID_BELKIN=y +CONFIG_HID_CHERRY=y +CONFIG_HID_CHICONY=y +CONFIG_HID_PRODIKEYS=y +CONFIG_HID_CYPRESS=y +CONFIG_HID_DRAGONRISE=y +CONFIG_DRAGONRISE_FF=y +CONFIG_HID_EMS_FF=y +CONFIG_HID_ELECOM=y +CONFIG_HID_EZKEY=y +CONFIG_HID_HOLTEK=y +CONFIG_HID_KEYTOUCH=y +CONFIG_HID_KYE=y +CONFIG_HID_UCLOGIC=y +CONFIG_HID_WALTOP=y +CONFIG_HID_GYRATION=y +CONFIG_HID_TWINHAN=y +CONFIG_HID_KENSINGTON=y +CONFIG_HID_LCPOWER=y +CONFIG_HID_LOGITECH=y +CONFIG_HID_LOGITECH_DJ=y +CONFIG_LOGITECH_FF=y +CONFIG_LOGIRUMBLEPAD2_FF=y +CONFIG_LOGIG940_FF=y +CONFIG_HID_MAGICMOUSE=y +CONFIG_HID_MICROSOFT=y +CONFIG_HID_MONTEREY=y +CONFIG_HID_MULTITOUCH=y +CONFIG_HID_NTRIG=y +CONFIG_HID_ORTEK=y +CONFIG_HID_PANTHERLORD=y +CONFIG_PANTHERLORD_FF=y +CONFIG_HID_PETALYNX=y +CONFIG_HID_PICOLCD=y +CONFIG_HID_PRIMAX=y +CONFIG_HID_ROCCAT=y +CONFIG_HID_SAITEK=y +CONFIG_HID_SAMSUNG=y +CONFIG_HID_SONY=y +CONFIG_HID_SPEEDLINK=y +CONFIG_HID_SUNPLUS=y +CONFIG_HID_GREENASIA=y +CONFIG_GREENASIA_FF=y +CONFIG_HID_SMARTJOYPLUS=y +CONFIG_SMARTJOYPLUS_FF=y +CONFIG_HID_TIVO=y +CONFIG_HID_TOPSEED=y +CONFIG_HID_THRUSTMASTER=y +CONFIG_HID_WACOM=y +CONFIG_HID_WIIMOTE=y +CONFIG_HID_ZEROPLUS=y +CONFIG_HID_ZYDACRON=y +CONFIG_HID_PID=y +CONFIG_USB_HIDDEV=y +CONFIG_USB_ANNOUNCE_NEW_DEVICES=y +CONFIG_USB_MON=y +CONFIG_USB_EHCI_HCD=y +# CONFIG_USB_EHCI_TT_NEWSCHED is not set +CONFIG_USB_OHCI_HCD=y +CONFIG_USB_UHCI_HCD=y +CONFIG_USB_PRINTER=y +CONFIG_USB_STORAGE=y +CONFIG_USB_OTG_WAKELOCK=y +CONFIG_EDAC=y +CONFIG_RTC_CLASS=y +# CONFIG_RTC_HCTOSYS is not set +CONFIG_DMADEVICES=y +CONFIG_VIRTIO_PCI=y +CONFIG_STAGING=y +CONFIG_ASHMEM=y +CONFIG_ANDROID_LOW_MEMORY_KILLER=y +CONFIG_SYNC=y +CONFIG_SW_SYNC=y +CONFIG_SYNC_FILE=y +CONFIG_ION=y +CONFIG_GOLDFISH_AUDIO=y +CONFIG_SND_HDA_INTEL=y +CONFIG_GOLDFISH=y +CONFIG_GOLDFISH_PIPE=y +CONFIG_GOLDFISH_SYNC=y +CONFIG_ANDROID=y +CONFIG_ANDROID_BINDER_IPC=y +CONFIG_ISCSI_IBFT_FIND=y +CONFIG_EXT4_FS=y +CONFIG_EXT4_FS_SECURITY=y +CONFIG_QUOTA=y +CONFIG_QUOTA_NETLINK_INTERFACE=y +# CONFIG_PRINT_QUOTA_WARNING is not set +CONFIG_FUSE_FS=y +CONFIG_ISO9660_FS=y +CONFIG_JOLIET=y +CONFIG_ZISOFS=y +CONFIG_MSDOS_FS=y +CONFIG_VFAT_FS=y +CONFIG_PROC_KCORE=y +CONFIG_TMPFS=y +CONFIG_TMPFS_POSIX_ACL=y +CONFIG_HUGETLBFS=y +CONFIG_PSTORE=y +CONFIG_PSTORE_CONSOLE=y +CONFIG_PSTORE_RAM=y +# CONFIG_NETWORK_FILESYSTEMS is not set +CONFIG_NLS_DEFAULT="utf8" +CONFIG_NLS_CODEPAGE_437=y +CONFIG_NLS_ASCII=y +CONFIG_NLS_ISO8859_1=y +CONFIG_NLS_UTF8=y +CONFIG_PRINTK_TIME=y +CONFIG_DEBUG_INFO=y +# CONFIG_ENABLE_WARN_DEPRECATED is not set +# CONFIG_ENABLE_MUST_CHECK is not set +# CONFIG_UNUSED_SYMBOLS is not set +CONFIG_MAGIC_SYSRQ=y +CONFIG_DEBUG_MEMORY_INIT=y +CONFIG_PANIC_TIMEOUT=5 +CONFIG_SCHEDSTATS=y +CONFIG_TIMER_STATS=y +CONFIG_SCHED_TRACER=y +CONFIG_BLK_DEV_IO_TRACE=y +CONFIG_PROVIDE_OHCI1394_DMA_INIT=y +CONFIG_KEYS=y +CONFIG_SECURITY=y +CONFIG_SECURITY_NETWORK=y +CONFIG_SECURITY_SELINUX=y +CONFIG_CRYPTO_TWOFISH=y +CONFIG_ASYMMETRIC_KEY_TYPE=y +CONFIG_ASYMMETRIC_PUBLIC_KEY_SUBTYPE=y +CONFIG_X509_CERTIFICATE_PARSER=y +CONFIG_PKCS7_MESSAGE_PARSER=y +CONFIG_PKCS7_TEST_KEY=y +# CONFIG_VIRTUALIZATION is not set +CONFIG_CRC_T10DIF=y From 68c92dff3f4530b20f85d86931711dc2a7a809b3 Mon Sep 17 00:00:00 2001 From: Jin Qian Date: Mon, 12 Sep 2016 15:51:35 -0700 Subject: [PATCH 0150/1103] ANDROID: build: add build server configs for goldfish Change-Id: Icd7a8d44df2b09394be5c6230c64ecb374cae236 [AmitP: Folded following android-4.9 commit changes into this patch d7d2efab84d5 ("ANDROID: build: fix build config kernel_dir")] Signed-off-by: Amit Pundir --- build.config.goldfish.arm | 12 ++++++++++++ build.config.goldfish.arm64 | 12 ++++++++++++ build.config.goldfish.mips | 11 +++++++++++ build.config.goldfish.mips64 | 11 +++++++++++ build.config.goldfish.x86 | 12 ++++++++++++ build.config.goldfish.x86_64 | 12 ++++++++++++ 6 files changed, 70 insertions(+) create mode 100644 build.config.goldfish.arm create mode 100644 build.config.goldfish.arm64 create mode 100644 build.config.goldfish.mips create mode 100644 build.config.goldfish.mips64 create mode 100644 build.config.goldfish.x86 create mode 100644 build.config.goldfish.x86_64 diff --git a/build.config.goldfish.arm b/build.config.goldfish.arm new file mode 100644 index 000000000000..866da9361b71 --- /dev/null +++ b/build.config.goldfish.arm @@ -0,0 +1,12 @@ +ARCH=arm +BRANCH=android-4.4 +CROSS_COMPILE=arm-linux-androidkernel- +DEFCONFIG=ranchu_defconfig +EXTRA_CMDS='' +KERNEL_DIR=common +LINUX_GCC_CROSS_COMPILE_PREBUILTS_BIN=prebuilts/gcc/linux-x86/arm/arm-linux-androideabi-4.9/bin +FILES=" +arch/arm/boot/zImage +vmlinux +System.map +" diff --git a/build.config.goldfish.arm64 b/build.config.goldfish.arm64 new file mode 100644 index 000000000000..9c963cf4a3d8 --- /dev/null +++ b/build.config.goldfish.arm64 @@ -0,0 +1,12 @@ +ARCH=arm64 +BRANCH=android-4.4 +CROSS_COMPILE=aarch64-linux-android- +DEFCONFIG=ranchu64_defconfig +EXTRA_CMDS='' +KERNEL_DIR=common +LINUX_GCC_CROSS_COMPILE_PREBUILTS_BIN=prebuilts/gcc/linux-x86/aarch64/aarch64-linux-android-4.9/bin +FILES=" +arch/arm64/boot/Image +vmlinux +System.map +" diff --git a/build.config.goldfish.mips b/build.config.goldfish.mips new file mode 100644 index 000000000000..8af53d2c2940 --- /dev/null +++ b/build.config.goldfish.mips @@ -0,0 +1,11 @@ +ARCH=mips +BRANCH=android-4.4 +CROSS_COMPILE=mips64el-linux-android- +DEFCONFIG=ranchu_defconfig +EXTRA_CMDS='' +KERNEL_DIR=common +LINUX_GCC_CROSS_COMPILE_PREBUILTS_BIN=prebuilts/gcc/linux-x86/mips/mips64el-linux-android-4.9/bin +FILES=" +vmlinux +System.map +" diff --git a/build.config.goldfish.mips64 b/build.config.goldfish.mips64 new file mode 100644 index 000000000000..2a33d36dc4c8 --- /dev/null +++ b/build.config.goldfish.mips64 @@ -0,0 +1,11 @@ +ARCH=mips +BRANCH=android-4.4 +CROSS_COMPILE=mips64el-linux-android- +DEFCONFIG=ranchu64_defconfig +EXTRA_CMDS='' +KERNEL_DIR=common +LINUX_GCC_CROSS_COMPILE_PREBUILTS_BIN=prebuilts/gcc/linux-x86/mips/mips64el-linux-android-4.9/bin +FILES=" +vmlinux +System.map +" diff --git a/build.config.goldfish.x86 b/build.config.goldfish.x86 new file mode 100644 index 000000000000..f86253f58d4d --- /dev/null +++ b/build.config.goldfish.x86 @@ -0,0 +1,12 @@ +ARCH=x86 +BRANCH=android-4.4 +CROSS_COMPILE=x86_64-linux-android- +DEFCONFIG=i386_ranchu_defconfig +EXTRA_CMDS='' +KERNEL_DIR=common +LINUX_GCC_CROSS_COMPILE_PREBUILTS_BIN=prebuilts/gcc/linux-x86/x86/x86_64-linux-android-4.9/bin +FILES=" +arch/x86/boot/bzImage +vmlinux +System.map +" diff --git a/build.config.goldfish.x86_64 b/build.config.goldfish.x86_64 new file mode 100644 index 000000000000..e1738861ec5c --- /dev/null +++ b/build.config.goldfish.x86_64 @@ -0,0 +1,12 @@ +ARCH=x86_64 +BRANCH=android-4.4 +CROSS_COMPILE=x86_64-linux-android- +DEFCONFIG=x86_64_ranchu_defconfig +EXTRA_CMDS='' +KERNEL_DIR=common +LINUX_GCC_CROSS_COMPILE_PREBUILTS_BIN=prebuilts/gcc/linux-x86/x86/x86_64-linux-android-4.9/bin +FILES=" +arch/x86/boot/bzImage +vmlinux +System.map +" From ced54e911b0313b2498ca56df27db918e5f6e71d Mon Sep 17 00:00:00 2001 From: Rom Lemarchand Date: Mon, 6 Jul 2015 16:50:33 -0700 Subject: [PATCH 0151/1103] ANDROID: initramfs: Add skip_initramfs command line option Add a skip_initramfs option to allow choosing whether to boot using the initramfs or not at runtime. Change-Id: If30428fa748c1d4d3d7b9d97c1f781de5e4558c3 Signed-off-by: Rom Lemarchand --- include/linux/initramfs.h | 32 ++++++++++++++++++++++++++++++++ init/Makefile | 3 --- init/initramfs.c | 19 ++++++++++++++++++- init/noinitramfs.c | 9 ++++++++- 4 files changed, 58 insertions(+), 5 deletions(-) create mode 100644 include/linux/initramfs.h diff --git a/include/linux/initramfs.h b/include/linux/initramfs.h new file mode 100644 index 000000000000..fc7da63b125b --- /dev/null +++ b/include/linux/initramfs.h @@ -0,0 +1,32 @@ +/* + * include/linux/initramfs.h + * + * Copyright (C) 2015, Google + * Rom Lemarchand + * + * 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; version 2 of the License. + * + * 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. + * + * 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 + */ + +#ifndef _LINUX_INITRAMFS_H +#define _LINUX_INITRAMFS_H + +#include + +#if IS_BUILTIN(CONFIG_BLK_DEV_INITRD) + +int __init default_rootfs(void); + +#endif + +#endif /* _LINUX_INITRAMFS_H */ diff --git a/init/Makefile b/init/Makefile index f814f0ff5974..1bac4381de9a 100644 --- a/init/Makefile +++ b/init/Makefile @@ -6,11 +6,8 @@ ccflags-y := -fno-function-sections -fno-data-sections obj-y := main.o version.o mounts.o -ifneq ($(CONFIG_BLK_DEV_INITRD),y) obj-y += noinitramfs.o -else obj-$(CONFIG_BLK_DEV_INITRD) += initramfs.o -endif obj-$(CONFIG_GENERIC_CALIBRATE_DELAY) += calibrate.o obj-y += init_task.o diff --git a/init/initramfs.c b/init/initramfs.c index 640557788026..cc21c6cfd20f 100644 --- a/init/initramfs.c +++ b/init/initramfs.c @@ -10,6 +10,7 @@ #include #include #include +#include static ssize_t __init xwrite(int fd, const char *p, size_t count) { @@ -597,10 +598,26 @@ static void __init clean_rootfs(void) } #endif +static int __initdata do_skip_initramfs; + +static int __init skip_initramfs_param(char *str) +{ + if (*str) + return 0; + do_skip_initramfs = 1; + return 1; +} +__setup("skip_initramfs", skip_initramfs_param); + static int __init populate_rootfs(void) { + char *err; + + if (do_skip_initramfs) + return default_rootfs(); + /* Load the built in initramfs */ - char *err = unpack_to_rootfs(__initramfs_start, __initramfs_size); + err = unpack_to_rootfs(__initramfs_start, __initramfs_size); if (err) panic("%s", err); /* Failed to decompress INTERNAL initramfs */ /* If available load the bootloader supplied initrd */ diff --git a/init/noinitramfs.c b/init/noinitramfs.c index f4bad8436c93..39e0b4f7b48b 100644 --- a/init/noinitramfs.c +++ b/init/noinitramfs.c @@ -21,11 +21,16 @@ #include #include #include +#include +#include /* * Create a simple rootfs that is similar to the default initramfs */ -static int __init default_rootfs(void) +#if !IS_BUILTIN(CONFIG_BLK_DEV_INITRD) +static +#endif +int __init default_rootfs(void) { int err; @@ -49,4 +54,6 @@ static int __init default_rootfs(void) printk(KERN_WARNING "Failed to create a rootfs\n"); return err; } +#if !IS_BUILTIN(CONFIG_BLK_DEV_INITRD) rootfs_initcall(default_rootfs); +#endif From 847700ed0f87d75e064355a33cd6dac409c8e37a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arve=20Hj=C3=B8nnev=C3=A5g?= Date: Wed, 15 Oct 2008 18:23:47 -0700 Subject: [PATCH 0152/1103] ANDROID: input: Generic GPIO Input devices MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Supports keyboard matrixces, direct inputs, direct outputs and axes connected to gpios. Change-Id: I5e921e6e3a1cc169316ee3b665f4cc21b5735114 Signed-off-by: Arve Hjønnevåg Signed-off-by: Nick Pelly [AmitP: Use ktime directly to align with upstream commit 2456e8553544 ("ktime: Get rid of the union")] Signed-off-by: Amit Pundir --- drivers/input/misc/Kconfig | 5 + drivers/input/misc/Makefile | 1 + drivers/input/misc/gpio_axis.c | 192 ++++++++++++++ drivers/input/misc/gpio_event.c | 249 +++++++++++++++++ drivers/input/misc/gpio_input.c | 376 ++++++++++++++++++++++++++ drivers/input/misc/gpio_matrix.c | 441 +++++++++++++++++++++++++++++++ drivers/input/misc/gpio_output.c | 97 +++++++ include/linux/gpio_event.h | 170 ++++++++++++ 8 files changed, 1531 insertions(+) create mode 100644 drivers/input/misc/gpio_axis.c create mode 100644 drivers/input/misc/gpio_event.c create mode 100644 drivers/input/misc/gpio_input.c create mode 100644 drivers/input/misc/gpio_matrix.c create mode 100644 drivers/input/misc/gpio_output.c create mode 100644 include/linux/gpio_event.h diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig index ca59a2be9bc5..9e0232c517d0 100644 --- a/drivers/input/misc/Kconfig +++ b/drivers/input/misc/Kconfig @@ -521,6 +521,11 @@ config INPUT_SGI_BTNS To compile this driver as a module, choose M here: the module will be called sgi_btns. +config INPUT_GPIO + tristate "GPIO driver support" + help + Say Y here if you want to support gpio based keys, wheels etc... + config HP_SDC_RTC tristate "HP SDC Real Time Clock" depends on (GSC || HP300) && SERIO diff --git a/drivers/input/misc/Makefile b/drivers/input/misc/Makefile index 9d0f9d1ff68f..02e9edcde799 100644 --- a/drivers/input/misc/Makefile +++ b/drivers/input/misc/Makefile @@ -36,6 +36,7 @@ obj-$(CONFIG_INPUT_DRV2667_HAPTICS) += drv2667.o obj-$(CONFIG_INPUT_GP2A) += gp2ap002a00f.o obj-$(CONFIG_INPUT_GPIO_BEEPER) += gpio-beeper.o obj-$(CONFIG_INPUT_GPIO_DECODER) += gpio_decoder.o +obj-$(CONFIG_INPUT_GPIO) += gpio_event.o gpio_matrix.o gpio_input.o gpio_output.o gpio_axis.o obj-$(CONFIG_INPUT_HISI_POWERKEY) += hisi_powerkey.o obj-$(CONFIG_HP_SDC_RTC) += hp_sdc_rtc.o obj-$(CONFIG_INPUT_IMS_PCU) += ims-pcu.o diff --git a/drivers/input/misc/gpio_axis.c b/drivers/input/misc/gpio_axis.c new file mode 100644 index 000000000000..0acf4a576f53 --- /dev/null +++ b/drivers/input/misc/gpio_axis.c @@ -0,0 +1,192 @@ +/* drivers/input/misc/gpio_axis.c + * + * Copyright (C) 2007 Google, Inc. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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. + * + */ + +#include +#include +#include +#include +#include + +struct gpio_axis_state { + struct gpio_event_input_devs *input_devs; + struct gpio_event_axis_info *info; + uint32_t pos; +}; + +uint16_t gpio_axis_4bit_gray_map_table[] = { + [0x0] = 0x0, [0x1] = 0x1, /* 0000 0001 */ + [0x3] = 0x2, [0x2] = 0x3, /* 0011 0010 */ + [0x6] = 0x4, [0x7] = 0x5, /* 0110 0111 */ + [0x5] = 0x6, [0x4] = 0x7, /* 0101 0100 */ + [0xc] = 0x8, [0xd] = 0x9, /* 1100 1101 */ + [0xf] = 0xa, [0xe] = 0xb, /* 1111 1110 */ + [0xa] = 0xc, [0xb] = 0xd, /* 1010 1011 */ + [0x9] = 0xe, [0x8] = 0xf, /* 1001 1000 */ +}; +uint16_t gpio_axis_4bit_gray_map(struct gpio_event_axis_info *info, uint16_t in) +{ + return gpio_axis_4bit_gray_map_table[in]; +} + +uint16_t gpio_axis_5bit_singletrack_map_table[] = { + [0x10] = 0x00, [0x14] = 0x01, [0x1c] = 0x02, /* 10000 10100 11100 */ + [0x1e] = 0x03, [0x1a] = 0x04, [0x18] = 0x05, /* 11110 11010 11000 */ + [0x08] = 0x06, [0x0a] = 0x07, [0x0e] = 0x08, /* 01000 01010 01110 */ + [0x0f] = 0x09, [0x0d] = 0x0a, [0x0c] = 0x0b, /* 01111 01101 01100 */ + [0x04] = 0x0c, [0x05] = 0x0d, [0x07] = 0x0e, /* 00100 00101 00111 */ + [0x17] = 0x0f, [0x16] = 0x10, [0x06] = 0x11, /* 10111 10110 00110 */ + [0x02] = 0x12, [0x12] = 0x13, [0x13] = 0x14, /* 00010 10010 10011 */ + [0x1b] = 0x15, [0x0b] = 0x16, [0x03] = 0x17, /* 11011 01011 00011 */ + [0x01] = 0x18, [0x09] = 0x19, [0x19] = 0x1a, /* 00001 01001 11001 */ + [0x1d] = 0x1b, [0x15] = 0x1c, [0x11] = 0x1d, /* 11101 10101 10001 */ +}; +uint16_t gpio_axis_5bit_singletrack_map( + struct gpio_event_axis_info *info, uint16_t in) +{ + return gpio_axis_5bit_singletrack_map_table[in]; +} + +static void gpio_event_update_axis(struct gpio_axis_state *as, int report) +{ + struct gpio_event_axis_info *ai = as->info; + int i; + int change; + uint16_t state = 0; + uint16_t pos; + uint16_t old_pos = as->pos; + for (i = ai->count - 1; i >= 0; i--) + state = (state << 1) | gpio_get_value(ai->gpio[i]); + pos = ai->map(ai, state); + if (ai->flags & GPIOEAF_PRINT_RAW) + pr_info("axis %d-%d raw %x, pos %d -> %d\n", + ai->type, ai->code, state, old_pos, pos); + if (report && pos != old_pos) { + if (ai->type == EV_REL) { + change = (ai->decoded_size + pos - old_pos) % + ai->decoded_size; + if (change > ai->decoded_size / 2) + change -= ai->decoded_size; + if (change == ai->decoded_size / 2) { + if (ai->flags & GPIOEAF_PRINT_EVENT) + pr_info("axis %d-%d unknown direction, " + "pos %d -> %d\n", ai->type, + ai->code, old_pos, pos); + change = 0; /* no closest direction */ + } + if (ai->flags & GPIOEAF_PRINT_EVENT) + pr_info("axis %d-%d change %d\n", + ai->type, ai->code, change); + input_report_rel(as->input_devs->dev[ai->dev], + ai->code, change); + } else { + if (ai->flags & GPIOEAF_PRINT_EVENT) + pr_info("axis %d-%d now %d\n", + ai->type, ai->code, pos); + input_event(as->input_devs->dev[ai->dev], + ai->type, ai->code, pos); + } + input_sync(as->input_devs->dev[ai->dev]); + } + as->pos = pos; +} + +static irqreturn_t gpio_axis_irq_handler(int irq, void *dev_id) +{ + struct gpio_axis_state *as = dev_id; + gpio_event_update_axis(as, 1); + return IRQ_HANDLED; +} + +int gpio_event_axis_func(struct gpio_event_input_devs *input_devs, + struct gpio_event_info *info, void **data, int func) +{ + int ret; + int i; + int irq; + struct gpio_event_axis_info *ai; + struct gpio_axis_state *as; + + ai = container_of(info, struct gpio_event_axis_info, info); + if (func == GPIO_EVENT_FUNC_SUSPEND) { + for (i = 0; i < ai->count; i++) + disable_irq(gpio_to_irq(ai->gpio[i])); + return 0; + } + if (func == GPIO_EVENT_FUNC_RESUME) { + for (i = 0; i < ai->count; i++) + enable_irq(gpio_to_irq(ai->gpio[i])); + return 0; + } + + if (func == GPIO_EVENT_FUNC_INIT) { + *data = as = kmalloc(sizeof(*as), GFP_KERNEL); + if (as == NULL) { + ret = -ENOMEM; + goto err_alloc_axis_state_failed; + } + as->input_devs = input_devs; + as->info = ai; + if (ai->dev >= input_devs->count) { + pr_err("gpio_event_axis: bad device index %d >= %d " + "for %d:%d\n", ai->dev, input_devs->count, + ai->type, ai->code); + ret = -EINVAL; + goto err_bad_device_index; + } + + input_set_capability(input_devs->dev[ai->dev], + ai->type, ai->code); + if (ai->type == EV_ABS) { + input_set_abs_params(input_devs->dev[ai->dev], ai->code, + 0, ai->decoded_size - 1, 0, 0); + } + for (i = 0; i < ai->count; i++) { + ret = gpio_request(ai->gpio[i], "gpio_event_axis"); + if (ret < 0) + goto err_request_gpio_failed; + ret = gpio_direction_input(ai->gpio[i]); + if (ret < 0) + goto err_gpio_direction_input_failed; + ret = irq = gpio_to_irq(ai->gpio[i]); + if (ret < 0) + goto err_get_irq_num_failed; + ret = request_irq(irq, gpio_axis_irq_handler, + IRQF_TRIGGER_RISING | + IRQF_TRIGGER_FALLING, + "gpio_event_axis", as); + if (ret < 0) + goto err_request_irq_failed; + } + gpio_event_update_axis(as, 0); + return 0; + } + + ret = 0; + as = *data; + for (i = ai->count - 1; i >= 0; i--) { + free_irq(gpio_to_irq(ai->gpio[i]), as); +err_request_irq_failed: +err_get_irq_num_failed: +err_gpio_direction_input_failed: + gpio_free(ai->gpio[i]); +err_request_gpio_failed: + ; + } +err_bad_device_index: + kfree(as); + *data = NULL; +err_alloc_axis_state_failed: + return ret; +} diff --git a/drivers/input/misc/gpio_event.c b/drivers/input/misc/gpio_event.c new file mode 100644 index 000000000000..d4e5b4dfe19f --- /dev/null +++ b/drivers/input/misc/gpio_event.c @@ -0,0 +1,249 @@ +/* drivers/input/misc/gpio_event.c + * + * Copyright (C) 2007 Google, Inc. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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. + * + */ + +#include +#include +#include +#include +#include +#include +#include + +struct gpio_event { + struct gpio_event_input_devs *input_devs; + const struct gpio_event_platform_data *info; + struct early_suspend early_suspend; + void *state[0]; +}; + +static int gpio_input_event( + struct input_dev *dev, unsigned int type, unsigned int code, int value) +{ + int i; + int devnr; + int ret = 0; + int tmp_ret; + struct gpio_event_info **ii; + struct gpio_event *ip = input_get_drvdata(dev); + + for (devnr = 0; devnr < ip->input_devs->count; devnr++) + if (ip->input_devs->dev[devnr] == dev) + break; + if (devnr == ip->input_devs->count) { + pr_err("gpio_input_event: unknown device %p\n", dev); + return -EIO; + } + + for (i = 0, ii = ip->info->info; i < ip->info->info_count; i++, ii++) { + if ((*ii)->event) { + tmp_ret = (*ii)->event(ip->input_devs, *ii, + &ip->state[i], + devnr, type, code, value); + if (tmp_ret) + ret = tmp_ret; + } + } + return ret; +} + +static int gpio_event_call_all_func(struct gpio_event *ip, int func) +{ + int i; + int ret; + struct gpio_event_info **ii; + + if (func == GPIO_EVENT_FUNC_INIT || func == GPIO_EVENT_FUNC_RESUME) { + ii = ip->info->info; + for (i = 0; i < ip->info->info_count; i++, ii++) { + if ((*ii)->func == NULL) { + ret = -ENODEV; + pr_err("gpio_event_probe: Incomplete pdata, " + "no function\n"); + goto err_no_func; + } + if (func == GPIO_EVENT_FUNC_RESUME && (*ii)->no_suspend) + continue; + ret = (*ii)->func(ip->input_devs, *ii, &ip->state[i], + func); + if (ret) { + pr_err("gpio_event_probe: function failed\n"); + goto err_func_failed; + } + } + return 0; + } + + ret = 0; + i = ip->info->info_count; + ii = ip->info->info + i; + while (i > 0) { + i--; + ii--; + if ((func & ~1) == GPIO_EVENT_FUNC_SUSPEND && (*ii)->no_suspend) + continue; + (*ii)->func(ip->input_devs, *ii, &ip->state[i], func & ~1); +err_func_failed: +err_no_func: + ; + } + return ret; +} + +#ifdef CONFIG_HAS_EARLYSUSPEND +void gpio_event_suspend(struct early_suspend *h) +{ + struct gpio_event *ip; + ip = container_of(h, struct gpio_event, early_suspend); + gpio_event_call_all_func(ip, GPIO_EVENT_FUNC_SUSPEND); + ip->info->power(ip->info, 0); +} + +void gpio_event_resume(struct early_suspend *h) +{ + struct gpio_event *ip; + ip = container_of(h, struct gpio_event, early_suspend); + ip->info->power(ip->info, 1); + gpio_event_call_all_func(ip, GPIO_EVENT_FUNC_RESUME); +} +#endif + +static int gpio_event_probe(struct platform_device *pdev) +{ + int err; + struct gpio_event *ip; + struct gpio_event_platform_data *event_info; + int dev_count = 1; + int i; + int registered = 0; + + event_info = pdev->dev.platform_data; + if (event_info == NULL) { + pr_err("gpio_event_probe: No pdata\n"); + return -ENODEV; + } + if ((!event_info->name && !event_info->names[0]) || + !event_info->info || !event_info->info_count) { + pr_err("gpio_event_probe: Incomplete pdata\n"); + return -ENODEV; + } + if (!event_info->name) + while (event_info->names[dev_count]) + dev_count++; + ip = kzalloc(sizeof(*ip) + + sizeof(ip->state[0]) * event_info->info_count + + sizeof(*ip->input_devs) + + sizeof(ip->input_devs->dev[0]) * dev_count, GFP_KERNEL); + if (ip == NULL) { + err = -ENOMEM; + pr_err("gpio_event_probe: Failed to allocate private data\n"); + goto err_kp_alloc_failed; + } + ip->input_devs = (void*)&ip->state[event_info->info_count]; + platform_set_drvdata(pdev, ip); + + for (i = 0; i < dev_count; i++) { + struct input_dev *input_dev = input_allocate_device(); + if (input_dev == NULL) { + err = -ENOMEM; + pr_err("gpio_event_probe: " + "Failed to allocate input device\n"); + goto err_input_dev_alloc_failed; + } + input_set_drvdata(input_dev, ip); + input_dev->name = event_info->name ? + event_info->name : event_info->names[i]; + input_dev->event = gpio_input_event; + ip->input_devs->dev[i] = input_dev; + } + ip->input_devs->count = dev_count; + ip->info = event_info; + if (event_info->power) { +#ifdef CONFIG_HAS_EARLYSUSPEND + ip->early_suspend.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN + 1; + ip->early_suspend.suspend = gpio_event_suspend; + ip->early_suspend.resume = gpio_event_resume; + register_early_suspend(&ip->early_suspend); +#endif + ip->info->power(ip->info, 1); + } + + err = gpio_event_call_all_func(ip, GPIO_EVENT_FUNC_INIT); + if (err) + goto err_call_all_func_failed; + + for (i = 0; i < dev_count; i++) { + err = input_register_device(ip->input_devs->dev[i]); + if (err) { + pr_err("gpio_event_probe: Unable to register %s " + "input device\n", ip->input_devs->dev[i]->name); + goto err_input_register_device_failed; + } + registered++; + } + + return 0; + +err_input_register_device_failed: + gpio_event_call_all_func(ip, GPIO_EVENT_FUNC_UNINIT); +err_call_all_func_failed: + if (event_info->power) { +#ifdef CONFIG_HAS_EARLYSUSPEND + unregister_early_suspend(&ip->early_suspend); +#endif + ip->info->power(ip->info, 0); + } + for (i = 0; i < registered; i++) + input_unregister_device(ip->input_devs->dev[i]); + for (i = dev_count - 1; i >= registered; i--) { + input_free_device(ip->input_devs->dev[i]); +err_input_dev_alloc_failed: + ; + } + kfree(ip); +err_kp_alloc_failed: + return err; +} + +static int gpio_event_remove(struct platform_device *pdev) +{ + struct gpio_event *ip = platform_get_drvdata(pdev); + int i; + + gpio_event_call_all_func(ip, GPIO_EVENT_FUNC_UNINIT); + if (ip->info->power) { +#ifdef CONFIG_HAS_EARLYSUSPEND + unregister_early_suspend(&ip->early_suspend); +#endif + ip->info->power(ip->info, 0); + } + for (i = 0; i < ip->input_devs->count; i++) + input_unregister_device(ip->input_devs->dev[i]); + kfree(ip); + return 0; +} + +static struct platform_driver gpio_event_driver = { + .probe = gpio_event_probe, + .remove = gpio_event_remove, + .driver = { + .name = GPIO_EVENT_DEV_NAME, + }, +}; + +module_platform_driver(gpio_event_driver); + +MODULE_DESCRIPTION("GPIO Event Driver"); +MODULE_LICENSE("GPL"); + diff --git a/drivers/input/misc/gpio_input.c b/drivers/input/misc/gpio_input.c new file mode 100644 index 000000000000..3122c19466a4 --- /dev/null +++ b/drivers/input/misc/gpio_input.c @@ -0,0 +1,376 @@ +/* drivers/input/misc/gpio_input.c + * + * Copyright (C) 2007 Google, Inc. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +enum { + DEBOUNCE_UNSTABLE = BIT(0), /* Got irq, while debouncing */ + DEBOUNCE_PRESSED = BIT(1), + DEBOUNCE_NOTPRESSED = BIT(2), + DEBOUNCE_WAIT_IRQ = BIT(3), /* Stable irq state */ + DEBOUNCE_POLL = BIT(4), /* Stable polling state */ + + DEBOUNCE_UNKNOWN = + DEBOUNCE_PRESSED | DEBOUNCE_NOTPRESSED, +}; + +struct gpio_key_state { + struct gpio_input_state *ds; + uint8_t debounce; +}; + +struct gpio_input_state { + struct gpio_event_input_devs *input_devs; + const struct gpio_event_input_info *info; + struct hrtimer timer; + int use_irq; + int debounce_count; + spinlock_t irq_lock; + struct wake_lock wake_lock; + struct gpio_key_state key_state[0]; +}; + +static enum hrtimer_restart gpio_event_input_timer_func(struct hrtimer *timer) +{ + int i; + int pressed; + struct gpio_input_state *ds = + container_of(timer, struct gpio_input_state, timer); + unsigned gpio_flags = ds->info->flags; + unsigned npolarity; + int nkeys = ds->info->keymap_size; + const struct gpio_event_direct_entry *key_entry; + struct gpio_key_state *key_state; + unsigned long irqflags; + uint8_t debounce; + bool sync_needed; + +#if 0 + key_entry = kp->keys_info->keymap; + key_state = kp->key_state; + for (i = 0; i < nkeys; i++, key_entry++, key_state++) + pr_info("gpio_read_detect_status %d %d\n", key_entry->gpio, + gpio_read_detect_status(key_entry->gpio)); +#endif + key_entry = ds->info->keymap; + key_state = ds->key_state; + sync_needed = false; + spin_lock_irqsave(&ds->irq_lock, irqflags); + for (i = 0; i < nkeys; i++, key_entry++, key_state++) { + debounce = key_state->debounce; + if (debounce & DEBOUNCE_WAIT_IRQ) + continue; + if (key_state->debounce & DEBOUNCE_UNSTABLE) { + debounce = key_state->debounce = DEBOUNCE_UNKNOWN; + enable_irq(gpio_to_irq(key_entry->gpio)); + if (gpio_flags & GPIOEDF_PRINT_KEY_UNSTABLE) + pr_info("gpio_keys_scan_keys: key %x-%x, %d " + "(%d) continue debounce\n", + ds->info->type, key_entry->code, + i, key_entry->gpio); + } + npolarity = !(gpio_flags & GPIOEDF_ACTIVE_HIGH); + pressed = gpio_get_value(key_entry->gpio) ^ npolarity; + if (debounce & DEBOUNCE_POLL) { + if (pressed == !(debounce & DEBOUNCE_PRESSED)) { + ds->debounce_count++; + key_state->debounce = DEBOUNCE_UNKNOWN; + if (gpio_flags & GPIOEDF_PRINT_KEY_DEBOUNCE) + pr_info("gpio_keys_scan_keys: key %x-" + "%x, %d (%d) start debounce\n", + ds->info->type, key_entry->code, + i, key_entry->gpio); + } + continue; + } + if (pressed && (debounce & DEBOUNCE_NOTPRESSED)) { + if (gpio_flags & GPIOEDF_PRINT_KEY_DEBOUNCE) + pr_info("gpio_keys_scan_keys: key %x-%x, %d " + "(%d) debounce pressed 1\n", + ds->info->type, key_entry->code, + i, key_entry->gpio); + key_state->debounce = DEBOUNCE_PRESSED; + continue; + } + if (!pressed && (debounce & DEBOUNCE_PRESSED)) { + if (gpio_flags & GPIOEDF_PRINT_KEY_DEBOUNCE) + pr_info("gpio_keys_scan_keys: key %x-%x, %d " + "(%d) debounce pressed 0\n", + ds->info->type, key_entry->code, + i, key_entry->gpio); + key_state->debounce = DEBOUNCE_NOTPRESSED; + continue; + } + /* key is stable */ + ds->debounce_count--; + if (ds->use_irq) + key_state->debounce |= DEBOUNCE_WAIT_IRQ; + else + key_state->debounce |= DEBOUNCE_POLL; + if (gpio_flags & GPIOEDF_PRINT_KEYS) + pr_info("gpio_keys_scan_keys: key %x-%x, %d (%d) " + "changed to %d\n", ds->info->type, + key_entry->code, i, key_entry->gpio, pressed); + input_event(ds->input_devs->dev[key_entry->dev], ds->info->type, + key_entry->code, pressed); + sync_needed = true; + } + if (sync_needed) { + for (i = 0; i < ds->input_devs->count; i++) + input_sync(ds->input_devs->dev[i]); + } + +#if 0 + key_entry = kp->keys_info->keymap; + key_state = kp->key_state; + for (i = 0; i < nkeys; i++, key_entry++, key_state++) { + pr_info("gpio_read_detect_status %d %d\n", key_entry->gpio, + gpio_read_detect_status(key_entry->gpio)); + } +#endif + + if (ds->debounce_count) + hrtimer_start(timer, ds->info->debounce_time, HRTIMER_MODE_REL); + else if (!ds->use_irq) + hrtimer_start(timer, ds->info->poll_time, HRTIMER_MODE_REL); + else + wake_unlock(&ds->wake_lock); + + spin_unlock_irqrestore(&ds->irq_lock, irqflags); + + return HRTIMER_NORESTART; +} + +static irqreturn_t gpio_event_input_irq_handler(int irq, void *dev_id) +{ + struct gpio_key_state *ks = dev_id; + struct gpio_input_state *ds = ks->ds; + int keymap_index = ks - ds->key_state; + const struct gpio_event_direct_entry *key_entry; + unsigned long irqflags; + int pressed; + + if (!ds->use_irq) + return IRQ_HANDLED; + + key_entry = &ds->info->keymap[keymap_index]; + + if (ds->info->debounce_time) { + spin_lock_irqsave(&ds->irq_lock, irqflags); + if (ks->debounce & DEBOUNCE_WAIT_IRQ) { + ks->debounce = DEBOUNCE_UNKNOWN; + if (ds->debounce_count++ == 0) { + wake_lock(&ds->wake_lock); + hrtimer_start( + &ds->timer, ds->info->debounce_time, + HRTIMER_MODE_REL); + } + if (ds->info->flags & GPIOEDF_PRINT_KEY_DEBOUNCE) + pr_info("gpio_event_input_irq_handler: " + "key %x-%x, %d (%d) start debounce\n", + ds->info->type, key_entry->code, + keymap_index, key_entry->gpio); + } else { + disable_irq_nosync(irq); + ks->debounce = DEBOUNCE_UNSTABLE; + } + spin_unlock_irqrestore(&ds->irq_lock, irqflags); + } else { + pressed = gpio_get_value(key_entry->gpio) ^ + !(ds->info->flags & GPIOEDF_ACTIVE_HIGH); + if (ds->info->flags & GPIOEDF_PRINT_KEYS) + pr_info("gpio_event_input_irq_handler: key %x-%x, %d " + "(%d) changed to %d\n", + ds->info->type, key_entry->code, keymap_index, + key_entry->gpio, pressed); + input_event(ds->input_devs->dev[key_entry->dev], ds->info->type, + key_entry->code, pressed); + input_sync(ds->input_devs->dev[key_entry->dev]); + } + return IRQ_HANDLED; +} + +static int gpio_event_input_request_irqs(struct gpio_input_state *ds) +{ + int i; + int err; + unsigned int irq; + unsigned long req_flags = IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING; + + for (i = 0; i < ds->info->keymap_size; i++) { + err = irq = gpio_to_irq(ds->info->keymap[i].gpio); + if (err < 0) + goto err_gpio_get_irq_num_failed; + err = request_irq(irq, gpio_event_input_irq_handler, + req_flags, "gpio_keys", &ds->key_state[i]); + if (err) { + pr_err("gpio_event_input_request_irqs: request_irq " + "failed for input %d, irq %d\n", + ds->info->keymap[i].gpio, irq); + goto err_request_irq_failed; + } + if (ds->info->info.no_suspend) { + err = enable_irq_wake(irq); + if (err) { + pr_err("gpio_event_input_request_irqs: " + "enable_irq_wake failed for input %d, " + "irq %d\n", + ds->info->keymap[i].gpio, irq); + goto err_enable_irq_wake_failed; + } + } + } + return 0; + + for (i = ds->info->keymap_size - 1; i >= 0; i--) { + irq = gpio_to_irq(ds->info->keymap[i].gpio); + if (ds->info->info.no_suspend) + disable_irq_wake(irq); +err_enable_irq_wake_failed: + free_irq(irq, &ds->key_state[i]); +err_request_irq_failed: +err_gpio_get_irq_num_failed: + ; + } + return err; +} + +int gpio_event_input_func(struct gpio_event_input_devs *input_devs, + struct gpio_event_info *info, void **data, int func) +{ + int ret; + int i; + unsigned long irqflags; + struct gpio_event_input_info *di; + struct gpio_input_state *ds = *data; + + di = container_of(info, struct gpio_event_input_info, info); + + if (func == GPIO_EVENT_FUNC_SUSPEND) { + if (ds->use_irq) + for (i = 0; i < di->keymap_size; i++) + disable_irq(gpio_to_irq(di->keymap[i].gpio)); + hrtimer_cancel(&ds->timer); + return 0; + } + if (func == GPIO_EVENT_FUNC_RESUME) { + spin_lock_irqsave(&ds->irq_lock, irqflags); + if (ds->use_irq) + for (i = 0; i < di->keymap_size; i++) + enable_irq(gpio_to_irq(di->keymap[i].gpio)); + hrtimer_start(&ds->timer, ktime_set(0, 0), HRTIMER_MODE_REL); + spin_unlock_irqrestore(&ds->irq_lock, irqflags); + return 0; + } + + if (func == GPIO_EVENT_FUNC_INIT) { + if (ktime_to_ns(di->poll_time) <= 0) + di->poll_time = ktime_set(0, 20 * NSEC_PER_MSEC); + + *data = ds = kzalloc(sizeof(*ds) + sizeof(ds->key_state[0]) * + di->keymap_size, GFP_KERNEL); + if (ds == NULL) { + ret = -ENOMEM; + pr_err("gpio_event_input_func: " + "Failed to allocate private data\n"); + goto err_ds_alloc_failed; + } + ds->debounce_count = di->keymap_size; + ds->input_devs = input_devs; + ds->info = di; + wake_lock_init(&ds->wake_lock, WAKE_LOCK_SUSPEND, "gpio_input"); + spin_lock_init(&ds->irq_lock); + + for (i = 0; i < di->keymap_size; i++) { + int dev = di->keymap[i].dev; + if (dev >= input_devs->count) { + pr_err("gpio_event_input_func: bad device " + "index %d >= %d for key code %d\n", + dev, input_devs->count, + di->keymap[i].code); + ret = -EINVAL; + goto err_bad_keymap; + } + input_set_capability(input_devs->dev[dev], di->type, + di->keymap[i].code); + ds->key_state[i].ds = ds; + ds->key_state[i].debounce = DEBOUNCE_UNKNOWN; + } + + for (i = 0; i < di->keymap_size; i++) { + ret = gpio_request(di->keymap[i].gpio, "gpio_kp_in"); + if (ret) { + pr_err("gpio_event_input_func: gpio_request " + "failed for %d\n", di->keymap[i].gpio); + goto err_gpio_request_failed; + } + ret = gpio_direction_input(di->keymap[i].gpio); + if (ret) { + pr_err("gpio_event_input_func: " + "gpio_direction_input failed for %d\n", + di->keymap[i].gpio); + goto err_gpio_configure_failed; + } + } + + ret = gpio_event_input_request_irqs(ds); + + spin_lock_irqsave(&ds->irq_lock, irqflags); + ds->use_irq = ret == 0; + + pr_info("GPIO Input Driver: Start gpio inputs for %s%s in %s " + "mode\n", input_devs->dev[0]->name, + (input_devs->count > 1) ? "..." : "", + ret == 0 ? "interrupt" : "polling"); + + hrtimer_init(&ds->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); + ds->timer.function = gpio_event_input_timer_func; + hrtimer_start(&ds->timer, ktime_set(0, 0), HRTIMER_MODE_REL); + spin_unlock_irqrestore(&ds->irq_lock, irqflags); + return 0; + } + + ret = 0; + spin_lock_irqsave(&ds->irq_lock, irqflags); + hrtimer_cancel(&ds->timer); + if (ds->use_irq) { + for (i = di->keymap_size - 1; i >= 0; i--) { + int irq = gpio_to_irq(di->keymap[i].gpio); + if (ds->info->info.no_suspend) + disable_irq_wake(irq); + free_irq(irq, &ds->key_state[i]); + } + } + spin_unlock_irqrestore(&ds->irq_lock, irqflags); + + for (i = di->keymap_size - 1; i >= 0; i--) { +err_gpio_configure_failed: + gpio_free(di->keymap[i].gpio); +err_gpio_request_failed: + ; + } +err_bad_keymap: + wake_lock_destroy(&ds->wake_lock); + kfree(ds); +err_ds_alloc_failed: + return ret; +} diff --git a/drivers/input/misc/gpio_matrix.c b/drivers/input/misc/gpio_matrix.c new file mode 100644 index 000000000000..eaa9e89d473a --- /dev/null +++ b/drivers/input/misc/gpio_matrix.c @@ -0,0 +1,441 @@ +/* drivers/input/misc/gpio_matrix.c + * + * Copyright (C) 2007 Google, Inc. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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. + * + */ + +#include +#include +#include +#include +#include +#include +#include + +struct gpio_kp { + struct gpio_event_input_devs *input_devs; + struct gpio_event_matrix_info *keypad_info; + struct hrtimer timer; + struct wake_lock wake_lock; + int current_output; + unsigned int use_irq:1; + unsigned int key_state_changed:1; + unsigned int last_key_state_changed:1; + unsigned int some_keys_pressed:2; + unsigned int disabled_irq:1; + unsigned long keys_pressed[0]; +}; + +static void clear_phantom_key(struct gpio_kp *kp, int out, int in) +{ + struct gpio_event_matrix_info *mi = kp->keypad_info; + int key_index = out * mi->ninputs + in; + unsigned short keyentry = mi->keymap[key_index]; + unsigned short keycode = keyentry & MATRIX_KEY_MASK; + unsigned short dev = keyentry >> MATRIX_CODE_BITS; + + if (!test_bit(keycode, kp->input_devs->dev[dev]->key)) { + if (mi->flags & GPIOKPF_PRINT_PHANTOM_KEYS) + pr_info("gpiomatrix: phantom key %x, %d-%d (%d-%d) " + "cleared\n", keycode, out, in, + mi->output_gpios[out], mi->input_gpios[in]); + __clear_bit(key_index, kp->keys_pressed); + } else { + if (mi->flags & GPIOKPF_PRINT_PHANTOM_KEYS) + pr_info("gpiomatrix: phantom key %x, %d-%d (%d-%d) " + "not cleared\n", keycode, out, in, + mi->output_gpios[out], mi->input_gpios[in]); + } +} + +static int restore_keys_for_input(struct gpio_kp *kp, int out, int in) +{ + int rv = 0; + int key_index; + + key_index = out * kp->keypad_info->ninputs + in; + while (out < kp->keypad_info->noutputs) { + if (test_bit(key_index, kp->keys_pressed)) { + rv = 1; + clear_phantom_key(kp, out, in); + } + key_index += kp->keypad_info->ninputs; + out++; + } + return rv; +} + +static void remove_phantom_keys(struct gpio_kp *kp) +{ + int out, in, inp; + int key_index; + + if (kp->some_keys_pressed < 3) + return; + + for (out = 0; out < kp->keypad_info->noutputs; out++) { + inp = -1; + key_index = out * kp->keypad_info->ninputs; + for (in = 0; in < kp->keypad_info->ninputs; in++, key_index++) { + if (test_bit(key_index, kp->keys_pressed)) { + if (inp == -1) { + inp = in; + continue; + } + if (inp >= 0) { + if (!restore_keys_for_input(kp, out + 1, + inp)) + break; + clear_phantom_key(kp, out, inp); + inp = -2; + } + restore_keys_for_input(kp, out, in); + } + } + } +} + +static void report_key(struct gpio_kp *kp, int key_index, int out, int in) +{ + struct gpio_event_matrix_info *mi = kp->keypad_info; + int pressed = test_bit(key_index, kp->keys_pressed); + unsigned short keyentry = mi->keymap[key_index]; + unsigned short keycode = keyentry & MATRIX_KEY_MASK; + unsigned short dev = keyentry >> MATRIX_CODE_BITS; + + if (pressed != test_bit(keycode, kp->input_devs->dev[dev]->key)) { + if (keycode == KEY_RESERVED) { + if (mi->flags & GPIOKPF_PRINT_UNMAPPED_KEYS) + pr_info("gpiomatrix: unmapped key, %d-%d " + "(%d-%d) changed to %d\n", + out, in, mi->output_gpios[out], + mi->input_gpios[in], pressed); + } else { + if (mi->flags & GPIOKPF_PRINT_MAPPED_KEYS) + pr_info("gpiomatrix: key %x, %d-%d (%d-%d) " + "changed to %d\n", keycode, + out, in, mi->output_gpios[out], + mi->input_gpios[in], pressed); + input_report_key(kp->input_devs->dev[dev], keycode, pressed); + } + } +} + +static void report_sync(struct gpio_kp *kp) +{ + int i; + + for (i = 0; i < kp->input_devs->count; i++) + input_sync(kp->input_devs->dev[i]); +} + +static enum hrtimer_restart gpio_keypad_timer_func(struct hrtimer *timer) +{ + int out, in; + int key_index; + int gpio; + struct gpio_kp *kp = container_of(timer, struct gpio_kp, timer); + struct gpio_event_matrix_info *mi = kp->keypad_info; + unsigned gpio_keypad_flags = mi->flags; + unsigned polarity = !!(gpio_keypad_flags & GPIOKPF_ACTIVE_HIGH); + + out = kp->current_output; + if (out == mi->noutputs) { + out = 0; + kp->last_key_state_changed = kp->key_state_changed; + kp->key_state_changed = 0; + kp->some_keys_pressed = 0; + } else { + key_index = out * mi->ninputs; + for (in = 0; in < mi->ninputs; in++, key_index++) { + gpio = mi->input_gpios[in]; + if (gpio_get_value(gpio) ^ !polarity) { + if (kp->some_keys_pressed < 3) + kp->some_keys_pressed++; + kp->key_state_changed |= !__test_and_set_bit( + key_index, kp->keys_pressed); + } else + kp->key_state_changed |= __test_and_clear_bit( + key_index, kp->keys_pressed); + } + gpio = mi->output_gpios[out]; + if (gpio_keypad_flags & GPIOKPF_DRIVE_INACTIVE) + gpio_set_value(gpio, !polarity); + else + gpio_direction_input(gpio); + out++; + } + kp->current_output = out; + if (out < mi->noutputs) { + gpio = mi->output_gpios[out]; + if (gpio_keypad_flags & GPIOKPF_DRIVE_INACTIVE) + gpio_set_value(gpio, polarity); + else + gpio_direction_output(gpio, polarity); + hrtimer_start(timer, mi->settle_time, HRTIMER_MODE_REL); + return HRTIMER_NORESTART; + } + if (gpio_keypad_flags & GPIOKPF_DEBOUNCE) { + if (kp->key_state_changed) { + hrtimer_start(&kp->timer, mi->debounce_delay, + HRTIMER_MODE_REL); + return HRTIMER_NORESTART; + } + kp->key_state_changed = kp->last_key_state_changed; + } + if (kp->key_state_changed) { + if (gpio_keypad_flags & GPIOKPF_REMOVE_SOME_PHANTOM_KEYS) + remove_phantom_keys(kp); + key_index = 0; + for (out = 0; out < mi->noutputs; out++) + for (in = 0; in < mi->ninputs; in++, key_index++) + report_key(kp, key_index, out, in); + report_sync(kp); + } + if (!kp->use_irq || kp->some_keys_pressed) { + hrtimer_start(timer, mi->poll_time, HRTIMER_MODE_REL); + return HRTIMER_NORESTART; + } + + /* No keys are pressed, reenable interrupt */ + for (out = 0; out < mi->noutputs; out++) { + if (gpio_keypad_flags & GPIOKPF_DRIVE_INACTIVE) + gpio_set_value(mi->output_gpios[out], polarity); + else + gpio_direction_output(mi->output_gpios[out], polarity); + } + for (in = 0; in < mi->ninputs; in++) + enable_irq(gpio_to_irq(mi->input_gpios[in])); + wake_unlock(&kp->wake_lock); + return HRTIMER_NORESTART; +} + +static irqreturn_t gpio_keypad_irq_handler(int irq_in, void *dev_id) +{ + int i; + struct gpio_kp *kp = dev_id; + struct gpio_event_matrix_info *mi = kp->keypad_info; + unsigned gpio_keypad_flags = mi->flags; + + if (!kp->use_irq) { + /* ignore interrupt while registering the handler */ + kp->disabled_irq = 1; + disable_irq_nosync(irq_in); + return IRQ_HANDLED; + } + + for (i = 0; i < mi->ninputs; i++) + disable_irq_nosync(gpio_to_irq(mi->input_gpios[i])); + for (i = 0; i < mi->noutputs; i++) { + if (gpio_keypad_flags & GPIOKPF_DRIVE_INACTIVE) + gpio_set_value(mi->output_gpios[i], + !(gpio_keypad_flags & GPIOKPF_ACTIVE_HIGH)); + else + gpio_direction_input(mi->output_gpios[i]); + } + wake_lock(&kp->wake_lock); + hrtimer_start(&kp->timer, ktime_set(0, 0), HRTIMER_MODE_REL); + return IRQ_HANDLED; +} + +static int gpio_keypad_request_irqs(struct gpio_kp *kp) +{ + int i; + int err; + unsigned int irq; + unsigned long request_flags; + struct gpio_event_matrix_info *mi = kp->keypad_info; + + switch (mi->flags & (GPIOKPF_ACTIVE_HIGH|GPIOKPF_LEVEL_TRIGGERED_IRQ)) { + default: + request_flags = IRQF_TRIGGER_FALLING; + break; + case GPIOKPF_ACTIVE_HIGH: + request_flags = IRQF_TRIGGER_RISING; + break; + case GPIOKPF_LEVEL_TRIGGERED_IRQ: + request_flags = IRQF_TRIGGER_LOW; + break; + case GPIOKPF_LEVEL_TRIGGERED_IRQ | GPIOKPF_ACTIVE_HIGH: + request_flags = IRQF_TRIGGER_HIGH; + break; + } + + for (i = 0; i < mi->ninputs; i++) { + err = irq = gpio_to_irq(mi->input_gpios[i]); + if (err < 0) + goto err_gpio_get_irq_num_failed; + err = request_irq(irq, gpio_keypad_irq_handler, request_flags, + "gpio_kp", kp); + if (err) { + pr_err("gpiomatrix: request_irq failed for input %d, " + "irq %d\n", mi->input_gpios[i], irq); + goto err_request_irq_failed; + } + err = enable_irq_wake(irq); + if (err) { + pr_err("gpiomatrix: set_irq_wake failed for input %d, " + "irq %d\n", mi->input_gpios[i], irq); + } + disable_irq(irq); + if (kp->disabled_irq) { + kp->disabled_irq = 0; + enable_irq(irq); + } + } + return 0; + + for (i = mi->noutputs - 1; i >= 0; i--) { + free_irq(gpio_to_irq(mi->input_gpios[i]), kp); +err_request_irq_failed: +err_gpio_get_irq_num_failed: + ; + } + return err; +} + +int gpio_event_matrix_func(struct gpio_event_input_devs *input_devs, + struct gpio_event_info *info, void **data, int func) +{ + int i; + int err; + int key_count; + struct gpio_kp *kp; + struct gpio_event_matrix_info *mi; + + mi = container_of(info, struct gpio_event_matrix_info, info); + if (func == GPIO_EVENT_FUNC_SUSPEND || func == GPIO_EVENT_FUNC_RESUME) { + /* TODO: disable scanning */ + return 0; + } + + if (func == GPIO_EVENT_FUNC_INIT) { + if (mi->keymap == NULL || + mi->input_gpios == NULL || + mi->output_gpios == NULL) { + err = -ENODEV; + pr_err("gpiomatrix: Incomplete pdata\n"); + goto err_invalid_platform_data; + } + key_count = mi->ninputs * mi->noutputs; + + *data = kp = kzalloc(sizeof(*kp) + sizeof(kp->keys_pressed[0]) * + BITS_TO_LONGS(key_count), GFP_KERNEL); + if (kp == NULL) { + err = -ENOMEM; + pr_err("gpiomatrix: Failed to allocate private data\n"); + goto err_kp_alloc_failed; + } + kp->input_devs = input_devs; + kp->keypad_info = mi; + for (i = 0; i < key_count; i++) { + unsigned short keyentry = mi->keymap[i]; + unsigned short keycode = keyentry & MATRIX_KEY_MASK; + unsigned short dev = keyentry >> MATRIX_CODE_BITS; + if (dev >= input_devs->count) { + pr_err("gpiomatrix: bad device index %d >= " + "%d for key code %d\n", + dev, input_devs->count, keycode); + err = -EINVAL; + goto err_bad_keymap; + } + if (keycode && keycode <= KEY_MAX) + input_set_capability(input_devs->dev[dev], + EV_KEY, keycode); + } + + for (i = 0; i < mi->noutputs; i++) { + err = gpio_request(mi->output_gpios[i], "gpio_kp_out"); + if (err) { + pr_err("gpiomatrix: gpio_request failed for " + "output %d\n", mi->output_gpios[i]); + goto err_request_output_gpio_failed; + } + if (gpio_cansleep(mi->output_gpios[i])) { + pr_err("gpiomatrix: unsupported output gpio %d," + " can sleep\n", mi->output_gpios[i]); + err = -EINVAL; + goto err_output_gpio_configure_failed; + } + if (mi->flags & GPIOKPF_DRIVE_INACTIVE) + err = gpio_direction_output(mi->output_gpios[i], + !(mi->flags & GPIOKPF_ACTIVE_HIGH)); + else + err = gpio_direction_input(mi->output_gpios[i]); + if (err) { + pr_err("gpiomatrix: gpio_configure failed for " + "output %d\n", mi->output_gpios[i]); + goto err_output_gpio_configure_failed; + } + } + for (i = 0; i < mi->ninputs; i++) { + err = gpio_request(mi->input_gpios[i], "gpio_kp_in"); + if (err) { + pr_err("gpiomatrix: gpio_request failed for " + "input %d\n", mi->input_gpios[i]); + goto err_request_input_gpio_failed; + } + err = gpio_direction_input(mi->input_gpios[i]); + if (err) { + pr_err("gpiomatrix: gpio_direction_input failed" + " for input %d\n", mi->input_gpios[i]); + goto err_gpio_direction_input_failed; + } + } + kp->current_output = mi->noutputs; + kp->key_state_changed = 1; + + hrtimer_init(&kp->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); + kp->timer.function = gpio_keypad_timer_func; + wake_lock_init(&kp->wake_lock, WAKE_LOCK_SUSPEND, "gpio_kp"); + err = gpio_keypad_request_irqs(kp); + kp->use_irq = err == 0; + + pr_info("GPIO Matrix Keypad Driver: Start keypad matrix for " + "%s%s in %s mode\n", input_devs->dev[0]->name, + (input_devs->count > 1) ? "..." : "", + kp->use_irq ? "interrupt" : "polling"); + + if (kp->use_irq) + wake_lock(&kp->wake_lock); + hrtimer_start(&kp->timer, ktime_set(0, 0), HRTIMER_MODE_REL); + + return 0; + } + + err = 0; + kp = *data; + + if (kp->use_irq) + for (i = mi->noutputs - 1; i >= 0; i--) + free_irq(gpio_to_irq(mi->input_gpios[i]), kp); + + hrtimer_cancel(&kp->timer); + wake_lock_destroy(&kp->wake_lock); + for (i = mi->noutputs - 1; i >= 0; i--) { +err_gpio_direction_input_failed: + gpio_free(mi->input_gpios[i]); +err_request_input_gpio_failed: + ; + } + for (i = mi->noutputs - 1; i >= 0; i--) { +err_output_gpio_configure_failed: + gpio_free(mi->output_gpios[i]); +err_request_output_gpio_failed: + ; + } +err_bad_keymap: + kfree(kp); +err_kp_alloc_failed: +err_invalid_platform_data: + return err; +} diff --git a/drivers/input/misc/gpio_output.c b/drivers/input/misc/gpio_output.c new file mode 100644 index 000000000000..2aac2fad0a17 --- /dev/null +++ b/drivers/input/misc/gpio_output.c @@ -0,0 +1,97 @@ +/* drivers/input/misc/gpio_output.c + * + * Copyright (C) 2007 Google, Inc. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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. + * + */ + +#include +#include +#include + +int gpio_event_output_event( + struct gpio_event_input_devs *input_devs, struct gpio_event_info *info, + void **data, unsigned int dev, unsigned int type, + unsigned int code, int value) +{ + int i; + struct gpio_event_output_info *oi; + oi = container_of(info, struct gpio_event_output_info, info); + if (type != oi->type) + return 0; + if (!(oi->flags & GPIOEDF_ACTIVE_HIGH)) + value = !value; + for (i = 0; i < oi->keymap_size; i++) + if (dev == oi->keymap[i].dev && code == oi->keymap[i].code) + gpio_set_value(oi->keymap[i].gpio, value); + return 0; +} + +int gpio_event_output_func( + struct gpio_event_input_devs *input_devs, struct gpio_event_info *info, + void **data, int func) +{ + int ret; + int i; + struct gpio_event_output_info *oi; + oi = container_of(info, struct gpio_event_output_info, info); + + if (func == GPIO_EVENT_FUNC_SUSPEND || func == GPIO_EVENT_FUNC_RESUME) + return 0; + + if (func == GPIO_EVENT_FUNC_INIT) { + int output_level = !(oi->flags & GPIOEDF_ACTIVE_HIGH); + + for (i = 0; i < oi->keymap_size; i++) { + int dev = oi->keymap[i].dev; + if (dev >= input_devs->count) { + pr_err("gpio_event_output_func: bad device " + "index %d >= %d for key code %d\n", + dev, input_devs->count, + oi->keymap[i].code); + ret = -EINVAL; + goto err_bad_keymap; + } + input_set_capability(input_devs->dev[dev], oi->type, + oi->keymap[i].code); + } + + for (i = 0; i < oi->keymap_size; i++) { + ret = gpio_request(oi->keymap[i].gpio, + "gpio_event_output"); + if (ret) { + pr_err("gpio_event_output_func: gpio_request " + "failed for %d\n", oi->keymap[i].gpio); + goto err_gpio_request_failed; + } + ret = gpio_direction_output(oi->keymap[i].gpio, + output_level); + if (ret) { + pr_err("gpio_event_output_func: " + "gpio_direction_output failed for %d\n", + oi->keymap[i].gpio); + goto err_gpio_direction_output_failed; + } + } + return 0; + } + + ret = 0; + for (i = oi->keymap_size - 1; i >= 0; i--) { +err_gpio_direction_output_failed: + gpio_free(oi->keymap[i].gpio); +err_gpio_request_failed: + ; + } +err_bad_keymap: + return ret; +} + diff --git a/include/linux/gpio_event.h b/include/linux/gpio_event.h new file mode 100644 index 000000000000..2613fc5e4a93 --- /dev/null +++ b/include/linux/gpio_event.h @@ -0,0 +1,170 @@ +/* include/linux/gpio_event.h + * + * Copyright (C) 2007 Google, Inc. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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. + * + */ + +#ifndef _LINUX_GPIO_EVENT_H +#define _LINUX_GPIO_EVENT_H + +#include + +struct gpio_event_input_devs { + int count; + struct input_dev *dev[]; +}; +enum { + GPIO_EVENT_FUNC_UNINIT = 0x0, + GPIO_EVENT_FUNC_INIT = 0x1, + GPIO_EVENT_FUNC_SUSPEND = 0x2, + GPIO_EVENT_FUNC_RESUME = 0x3, +}; +struct gpio_event_info { + int (*func)(struct gpio_event_input_devs *input_devs, + struct gpio_event_info *info, + void **data, int func); + int (*event)(struct gpio_event_input_devs *input_devs, + struct gpio_event_info *info, + void **data, unsigned int dev, unsigned int type, + unsigned int code, int value); /* out events */ + bool no_suspend; +}; + +struct gpio_event_platform_data { + const char *name; + struct gpio_event_info **info; + size_t info_count; + int (*power)(const struct gpio_event_platform_data *pdata, bool on); + const char *names[]; /* If name is NULL, names contain a NULL */ + /* terminated list of input devices to create */ +}; + +#define GPIO_EVENT_DEV_NAME "gpio-event" + +/* Key matrix */ + +enum gpio_event_matrix_flags { + /* unset: drive active output low, set: drive active output high */ + GPIOKPF_ACTIVE_HIGH = 1U << 0, + GPIOKPF_DEBOUNCE = 1U << 1, + GPIOKPF_REMOVE_SOME_PHANTOM_KEYS = 1U << 2, + GPIOKPF_REMOVE_PHANTOM_KEYS = GPIOKPF_REMOVE_SOME_PHANTOM_KEYS | + GPIOKPF_DEBOUNCE, + GPIOKPF_DRIVE_INACTIVE = 1U << 3, + GPIOKPF_LEVEL_TRIGGERED_IRQ = 1U << 4, + GPIOKPF_PRINT_UNMAPPED_KEYS = 1U << 16, + GPIOKPF_PRINT_MAPPED_KEYS = 1U << 17, + GPIOKPF_PRINT_PHANTOM_KEYS = 1U << 18, +}; + +#define MATRIX_CODE_BITS (10) +#define MATRIX_KEY_MASK ((1U << MATRIX_CODE_BITS) - 1) +#define MATRIX_KEY(dev, code) \ + (((dev) << MATRIX_CODE_BITS) | (code & MATRIX_KEY_MASK)) + +extern int gpio_event_matrix_func(struct gpio_event_input_devs *input_devs, + struct gpio_event_info *info, void **data, int func); +struct gpio_event_matrix_info { + /* initialize to gpio_event_matrix_func */ + struct gpio_event_info info; + /* size must be ninputs * noutputs */ + const unsigned short *keymap; + unsigned int *input_gpios; + unsigned int *output_gpios; + unsigned int ninputs; + unsigned int noutputs; + /* time to wait before reading inputs after driving each output */ + ktime_t settle_time; + /* time to wait before scanning the keypad a second time */ + ktime_t debounce_delay; + ktime_t poll_time; + unsigned flags; +}; + +/* Directly connected inputs and outputs */ + +enum gpio_event_direct_flags { + GPIOEDF_ACTIVE_HIGH = 1U << 0, +/* GPIOEDF_USE_DOWN_IRQ = 1U << 1, */ +/* GPIOEDF_USE_IRQ = (1U << 2) | GPIOIDF_USE_DOWN_IRQ, */ + GPIOEDF_PRINT_KEYS = 1U << 8, + GPIOEDF_PRINT_KEY_DEBOUNCE = 1U << 9, + GPIOEDF_PRINT_KEY_UNSTABLE = 1U << 10, +}; + +struct gpio_event_direct_entry { + uint32_t gpio:16; + uint32_t code:10; + uint32_t dev:6; +}; + +/* inputs */ +extern int gpio_event_input_func(struct gpio_event_input_devs *input_devs, + struct gpio_event_info *info, void **data, int func); +struct gpio_event_input_info { + /* initialize to gpio_event_input_func */ + struct gpio_event_info info; + ktime_t debounce_time; + ktime_t poll_time; + uint16_t flags; + uint16_t type; + const struct gpio_event_direct_entry *keymap; + size_t keymap_size; +}; + +/* outputs */ +extern int gpio_event_output_func(struct gpio_event_input_devs *input_devs, + struct gpio_event_info *info, void **data, int func); +extern int gpio_event_output_event(struct gpio_event_input_devs *input_devs, + struct gpio_event_info *info, void **data, + unsigned int dev, unsigned int type, + unsigned int code, int value); +struct gpio_event_output_info { + /* initialize to gpio_event_output_func and gpio_event_output_event */ + struct gpio_event_info info; + uint16_t flags; + uint16_t type; + const struct gpio_event_direct_entry *keymap; + size_t keymap_size; +}; + + +/* axes */ + +enum gpio_event_axis_flags { + GPIOEAF_PRINT_UNKNOWN_DIRECTION = 1U << 16, + GPIOEAF_PRINT_RAW = 1U << 17, + GPIOEAF_PRINT_EVENT = 1U << 18, +}; + +extern int gpio_event_axis_func(struct gpio_event_input_devs *input_devs, + struct gpio_event_info *info, void **data, int func); +struct gpio_event_axis_info { + /* initialize to gpio_event_axis_func */ + struct gpio_event_info info; + uint8_t count; /* number of gpios for this axis */ + uint8_t dev; /* device index when using multiple input devices */ + uint8_t type; /* EV_REL or EV_ABS */ + uint16_t code; + uint16_t decoded_size; + uint16_t (*map)(struct gpio_event_axis_info *info, uint16_t in); + uint32_t *gpio; + uint32_t flags; +}; +#define gpio_axis_2bit_gray_map gpio_axis_4bit_gray_map +#define gpio_axis_3bit_gray_map gpio_axis_4bit_gray_map +uint16_t gpio_axis_4bit_gray_map( + struct gpio_event_axis_info *info, uint16_t in); +uint16_t gpio_axis_5bit_singletrack_map( + struct gpio_event_axis_info *info, uint16_t in); + +#endif From 2e4cdd5a12ea427628873174a0e8999f0aa1cda3 Mon Sep 17 00:00:00 2001 From: Colin Cross Date: Wed, 1 Feb 2012 20:26:28 -0800 Subject: [PATCH 0153/1103] ANDROID: input: gpio_event: remove early suspend Remove the early suspend handler. Leave the suspend functions for now, they should eventually get called through a userspace interface.x Change-Id: I67f9dafe32fe32577bab93c42b95824db96c215c Signed-off-by: Colin Cross --- drivers/input/misc/gpio_event.c | 39 ++++++++------------------------- 1 file changed, 9 insertions(+), 30 deletions(-) diff --git a/drivers/input/misc/gpio_event.c b/drivers/input/misc/gpio_event.c index d4e5b4dfe19f..90f07eba3ce9 100644 --- a/drivers/input/misc/gpio_event.c +++ b/drivers/input/misc/gpio_event.c @@ -13,7 +13,6 @@ * */ -#include #include #include #include @@ -24,7 +23,6 @@ struct gpio_event { struct gpio_event_input_devs *input_devs; const struct gpio_event_platform_data *info; - struct early_suspend early_suspend; void *state[0]; }; @@ -101,23 +99,19 @@ static int gpio_event_call_all_func(struct gpio_event *ip, int func) return ret; } -#ifdef CONFIG_HAS_EARLYSUSPEND -void gpio_event_suspend(struct early_suspend *h) +static void __maybe_unused gpio_event_suspend(struct gpio_event *ip) { - struct gpio_event *ip; - ip = container_of(h, struct gpio_event, early_suspend); gpio_event_call_all_func(ip, GPIO_EVENT_FUNC_SUSPEND); - ip->info->power(ip->info, 0); + if (ip->info->power) + ip->info->power(ip->info, 0); } -void gpio_event_resume(struct early_suspend *h) +static void __maybe_unused gpio_event_resume(struct gpio_event *ip) { - struct gpio_event *ip; - ip = container_of(h, struct gpio_event, early_suspend); - ip->info->power(ip->info, 1); + if (ip->info->power) + ip->info->power(ip->info, 1); gpio_event_call_all_func(ip, GPIO_EVENT_FUNC_RESUME); } -#endif static int gpio_event_probe(struct platform_device *pdev) { @@ -169,15 +163,8 @@ static int gpio_event_probe(struct platform_device *pdev) } ip->input_devs->count = dev_count; ip->info = event_info; - if (event_info->power) { -#ifdef CONFIG_HAS_EARLYSUSPEND - ip->early_suspend.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN + 1; - ip->early_suspend.suspend = gpio_event_suspend; - ip->early_suspend.resume = gpio_event_resume; - register_early_suspend(&ip->early_suspend); -#endif + if (event_info->power) ip->info->power(ip->info, 1); - } err = gpio_event_call_all_func(ip, GPIO_EVENT_FUNC_INIT); if (err) @@ -198,12 +185,8 @@ static int gpio_event_probe(struct platform_device *pdev) err_input_register_device_failed: gpio_event_call_all_func(ip, GPIO_EVENT_FUNC_UNINIT); err_call_all_func_failed: - if (event_info->power) { -#ifdef CONFIG_HAS_EARLYSUSPEND - unregister_early_suspend(&ip->early_suspend); -#endif + if (event_info->power) ip->info->power(ip->info, 0); - } for (i = 0; i < registered; i++) input_unregister_device(ip->input_devs->dev[i]); for (i = dev_count - 1; i >= registered; i--) { @@ -222,12 +205,8 @@ static int gpio_event_remove(struct platform_device *pdev) int i; gpio_event_call_all_func(ip, GPIO_EVENT_FUNC_UNINIT); - if (ip->info->power) { -#ifdef CONFIG_HAS_EARLYSUSPEND - unregister_early_suspend(&ip->early_suspend); -#endif + if (ip->info->power) ip->info->power(ip->info, 0); - } for (i = 0; i < ip->input_devs->count; i++) input_unregister_device(ip->input_devs->dev[i]); kfree(ip); From c98303c54ec63d45360b26610fa6d7ff3bdb7128 Mon Sep 17 00:00:00 2001 From: Todd Poynor Date: Tue, 19 Jun 2012 21:06:47 -0700 Subject: [PATCH 0154/1103] ANDROID: input: gpio_input: convert from wakelocks to wakeup sources And add device names to wakeup source names Change-Id: Ia5f2723319a2e749f00d6ec7d846edff6af6d5c2 Signed-off-by: Todd Poynor --- drivers/input/misc/gpio_input.c | 26 ++++++++++++++++++++------ 1 file changed, 20 insertions(+), 6 deletions(-) diff --git a/drivers/input/misc/gpio_input.c b/drivers/input/misc/gpio_input.c index 3122c19466a4..5875d739c550 100644 --- a/drivers/input/misc/gpio_input.c +++ b/drivers/input/misc/gpio_input.c @@ -20,7 +20,7 @@ #include #include #include -#include +#include enum { DEBOUNCE_UNSTABLE = BIT(0), /* Got irq, while debouncing */ @@ -45,7 +45,7 @@ struct gpio_input_state { int use_irq; int debounce_count; spinlock_t irq_lock; - struct wake_lock wake_lock; + struct wakeup_source *ws; struct gpio_key_state key_state[0]; }; @@ -153,7 +153,7 @@ static enum hrtimer_restart gpio_event_input_timer_func(struct hrtimer *timer) else if (!ds->use_irq) hrtimer_start(timer, ds->info->poll_time, HRTIMER_MODE_REL); else - wake_unlock(&ds->wake_lock); + __pm_relax(ds->ws); spin_unlock_irqrestore(&ds->irq_lock, irqflags); @@ -179,7 +179,7 @@ static irqreturn_t gpio_event_input_irq_handler(int irq, void *dev_id) if (ks->debounce & DEBOUNCE_WAIT_IRQ) { ks->debounce = DEBOUNCE_UNKNOWN; if (ds->debounce_count++ == 0) { - wake_lock(&ds->wake_lock); + __pm_stay_awake(ds->ws); hrtimer_start( &ds->timer, ds->info->debounce_time, HRTIMER_MODE_REL); @@ -262,6 +262,7 @@ int gpio_event_input_func(struct gpio_event_input_devs *input_devs, unsigned long irqflags; struct gpio_event_input_info *di; struct gpio_input_state *ds = *data; + char *wlname; di = container_of(info, struct gpio_event_input_info, info); @@ -297,7 +298,19 @@ int gpio_event_input_func(struct gpio_event_input_devs *input_devs, ds->debounce_count = di->keymap_size; ds->input_devs = input_devs; ds->info = di; - wake_lock_init(&ds->wake_lock, WAKE_LOCK_SUSPEND, "gpio_input"); + wlname = kasprintf(GFP_KERNEL, "gpio_input:%s%s", + input_devs->dev[0]->name, + (input_devs->count > 1) ? "..." : ""); + + ds->ws = wakeup_source_register(wlname); + kfree(wlname); + if (!ds->ws) { + ret = -ENOMEM; + pr_err("gpio_event_input_func: " + "Failed to allocate wakeup source\n"); + goto err_ws_failed; + } + spin_lock_init(&ds->irq_lock); for (i = 0; i < di->keymap_size; i++) { @@ -369,7 +382,8 @@ int gpio_event_input_func(struct gpio_event_input_devs *input_devs, ; } err_bad_keymap: - wake_lock_destroy(&ds->wake_lock); + wakeup_source_unregister(ds->ws); +err_ws_failed: kfree(ds); err_ds_alloc_failed: return ret; From 336dbee1b00b25ba23b6441538d684a3e2742922 Mon Sep 17 00:00:00 2001 From: Dmitry Shmidt Date: Thu, 12 Jan 2017 12:34:22 -0800 Subject: [PATCH 0155/1103] ANDROID: input: gpio_matrix: Remove wakelock.h dependencies Change-Id: I228bcdebf28f5c67765002043d3f919718827316 Signed-off-by: Dmitry Shmidt --- drivers/input/misc/gpio_matrix.c | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/drivers/input/misc/gpio_matrix.c b/drivers/input/misc/gpio_matrix.c index eaa9e89d473a..08769dd88f56 100644 --- a/drivers/input/misc/gpio_matrix.c +++ b/drivers/input/misc/gpio_matrix.c @@ -19,13 +19,12 @@ #include #include #include -#include struct gpio_kp { struct gpio_event_input_devs *input_devs; struct gpio_event_matrix_info *keypad_info; struct hrtimer timer; - struct wake_lock wake_lock; + struct wakeup_source wake_src; int current_output; unsigned int use_irq:1; unsigned int key_state_changed:1; @@ -215,7 +214,7 @@ static enum hrtimer_restart gpio_keypad_timer_func(struct hrtimer *timer) } for (in = 0; in < mi->ninputs; in++) enable_irq(gpio_to_irq(mi->input_gpios[in])); - wake_unlock(&kp->wake_lock); + __pm_relax(&kp->wake_src); return HRTIMER_NORESTART; } @@ -242,7 +241,7 @@ static irqreturn_t gpio_keypad_irq_handler(int irq_in, void *dev_id) else gpio_direction_input(mi->output_gpios[i]); } - wake_lock(&kp->wake_lock); + __pm_stay_awake(&kp->wake_src); hrtimer_start(&kp->timer, ktime_set(0, 0), HRTIMER_MODE_REL); return IRQ_HANDLED; } @@ -396,7 +395,7 @@ int gpio_event_matrix_func(struct gpio_event_input_devs *input_devs, hrtimer_init(&kp->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); kp->timer.function = gpio_keypad_timer_func; - wake_lock_init(&kp->wake_lock, WAKE_LOCK_SUSPEND, "gpio_kp"); + wakeup_source_init(&kp->wake_src, "gpio_kp"); err = gpio_keypad_request_irqs(kp); kp->use_irq = err == 0; @@ -406,7 +405,7 @@ int gpio_event_matrix_func(struct gpio_event_input_devs *input_devs, kp->use_irq ? "interrupt" : "polling"); if (kp->use_irq) - wake_lock(&kp->wake_lock); + __pm_stay_awake(&kp->wake_src); hrtimer_start(&kp->timer, ktime_set(0, 0), HRTIMER_MODE_REL); return 0; @@ -420,7 +419,7 @@ int gpio_event_matrix_func(struct gpio_event_input_devs *input_devs, free_irq(gpio_to_irq(mi->input_gpios[i]), kp); hrtimer_cancel(&kp->timer); - wake_lock_destroy(&kp->wake_lock); + wakeup_source_trash(&kp->wake_src); for (i = mi->noutputs - 1; i >= 0; i--) { err_gpio_direction_input_failed: gpio_free(mi->input_gpios[i]); From 3dd4171775a354ebaddbe75da4ab563e09876187 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arve=20Hj=C3=B8nnev=C3=A5g?= Date: Fri, 21 Nov 2008 21:47:23 -0800 Subject: [PATCH 0156/1103] ANDROID: input: keyreset: Add keyreset driver. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a platform device in the board file to specify a reset key-combo. The first time the key-combo is detected a work function that syncs the filesystems is scheduled. If all the keys are released and then pressed again, it calls panic. Reboot on panic should be set for this to work. Change-Id: I9d54283ca1fba45e4b1ae1a407524cdda8171143 Signed-off-by: Arve Hjønnevåg --- drivers/input/Kconfig | 6 + drivers/input/Makefile | 1 + drivers/input/keyreset.c | 239 +++++++++++++++++++++++++++++++++++++++ include/linux/keyreset.h | 28 +++++ 4 files changed, 274 insertions(+) create mode 100644 drivers/input/keyreset.c create mode 100644 include/linux/keyreset.h diff --git a/drivers/input/Kconfig b/drivers/input/Kconfig index c5992cd195a1..8c81757e2e8d 100644 --- a/drivers/input/Kconfig +++ b/drivers/input/Kconfig @@ -184,6 +184,12 @@ config INPUT_APMPOWER To compile this driver as a module, choose M here: the module will be called apm-power. +config INPUT_KEYRESET + bool "Reset key" + depends on INPUT + ---help--- + Say Y here if you want to reboot when some keys are pressed; + comment "Input Device Drivers" source "drivers/input/keyboard/Kconfig" diff --git a/drivers/input/Makefile b/drivers/input/Makefile index 40de6a7be641..b5f3008f92f4 100644 --- a/drivers/input/Makefile +++ b/drivers/input/Makefile @@ -27,5 +27,6 @@ obj-$(CONFIG_INPUT_TOUCHSCREEN) += touchscreen/ obj-$(CONFIG_INPUT_MISC) += misc/ obj-$(CONFIG_INPUT_APMPOWER) += apm-power.o +obj-$(CONFIG_INPUT_KEYRESET) += keyreset.o obj-$(CONFIG_RMI4_CORE) += rmi4/ diff --git a/drivers/input/keyreset.c b/drivers/input/keyreset.c new file mode 100644 index 000000000000..36208fe0baae --- /dev/null +++ b/drivers/input/keyreset.c @@ -0,0 +1,239 @@ +/* drivers/input/keyreset.c + * + * Copyright (C) 2008 Google, Inc. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include + + +struct keyreset_state { + struct input_handler input_handler; + unsigned long keybit[BITS_TO_LONGS(KEY_CNT)]; + unsigned long upbit[BITS_TO_LONGS(KEY_CNT)]; + unsigned long key[BITS_TO_LONGS(KEY_CNT)]; + spinlock_t lock; + int key_down_target; + int key_down; + int key_up; + int restart_disabled; + int (*reset_fn)(void); +}; + +int restart_requested; +static void deferred_restart(struct work_struct *dummy) +{ + restart_requested = 2; + sys_sync(); + restart_requested = 3; + kernel_restart(NULL); +} +static DECLARE_WORK(restart_work, deferred_restart); + +static void keyreset_event(struct input_handle *handle, unsigned int type, + unsigned int code, int value) +{ + unsigned long flags; + struct keyreset_state *state = handle->private; + + if (type != EV_KEY) + return; + + if (code >= KEY_MAX) + return; + + if (!test_bit(code, state->keybit)) + return; + + spin_lock_irqsave(&state->lock, flags); + if (!test_bit(code, state->key) == !value) + goto done; + __change_bit(code, state->key); + if (test_bit(code, state->upbit)) { + if (value) { + state->restart_disabled = 1; + state->key_up++; + } else + state->key_up--; + } else { + if (value) + state->key_down++; + else + state->key_down--; + } + if (state->key_down == 0 && state->key_up == 0) + state->restart_disabled = 0; + + pr_debug("reset key changed %d %d new state %d-%d-%d\n", code, value, + state->key_down, state->key_up, state->restart_disabled); + + if (value && !state->restart_disabled && + state->key_down == state->key_down_target) { + state->restart_disabled = 1; + if (restart_requested) + panic("keyboard reset failed, %d", restart_requested); + if (state->reset_fn) { + restart_requested = state->reset_fn(); + } else { + pr_info("keyboard reset\n"); + schedule_work(&restart_work); + restart_requested = 1; + } + } +done: + spin_unlock_irqrestore(&state->lock, flags); +} + +static int keyreset_connect(struct input_handler *handler, + struct input_dev *dev, + const struct input_device_id *id) +{ + int i; + int ret; + struct input_handle *handle; + struct keyreset_state *state = + container_of(handler, struct keyreset_state, input_handler); + + for (i = 0; i < KEY_MAX; i++) { + if (test_bit(i, state->keybit) && test_bit(i, dev->keybit)) + break; + } + if (i == KEY_MAX) + return -ENODEV; + + handle = kzalloc(sizeof(*handle), GFP_KERNEL); + if (!handle) + return -ENOMEM; + + handle->dev = dev; + handle->handler = handler; + handle->name = "keyreset"; + handle->private = state; + + ret = input_register_handle(handle); + if (ret) + goto err_input_register_handle; + + ret = input_open_device(handle); + if (ret) + goto err_input_open_device; + + pr_info("using input dev %s for key reset\n", dev->name); + + return 0; + +err_input_open_device: + input_unregister_handle(handle); +err_input_register_handle: + kfree(handle); + return ret; +} + +static void keyreset_disconnect(struct input_handle *handle) +{ + input_close_device(handle); + input_unregister_handle(handle); + kfree(handle); +} + +static const struct input_device_id keyreset_ids[] = { + { + .flags = INPUT_DEVICE_ID_MATCH_EVBIT, + .evbit = { BIT_MASK(EV_KEY) }, + }, + { }, +}; +MODULE_DEVICE_TABLE(input, keyreset_ids); + +static int keyreset_probe(struct platform_device *pdev) +{ + int ret; + int key, *keyp; + struct keyreset_state *state; + struct keyreset_platform_data *pdata = pdev->dev.platform_data; + + if (!pdata) + return -EINVAL; + + state = kzalloc(sizeof(*state), GFP_KERNEL); + if (!state) + return -ENOMEM; + + spin_lock_init(&state->lock); + keyp = pdata->keys_down; + while ((key = *keyp++)) { + if (key >= KEY_MAX) + continue; + state->key_down_target++; + __set_bit(key, state->keybit); + } + if (pdata->keys_up) { + keyp = pdata->keys_up; + while ((key = *keyp++)) { + if (key >= KEY_MAX) + continue; + __set_bit(key, state->keybit); + __set_bit(key, state->upbit); + } + } + + if (pdata->reset_fn) + state->reset_fn = pdata->reset_fn; + + state->input_handler.event = keyreset_event; + state->input_handler.connect = keyreset_connect; + state->input_handler.disconnect = keyreset_disconnect; + state->input_handler.name = KEYRESET_NAME; + state->input_handler.id_table = keyreset_ids; + ret = input_register_handler(&state->input_handler); + if (ret) { + kfree(state); + return ret; + } + platform_set_drvdata(pdev, state); + return 0; +} + +int keyreset_remove(struct platform_device *pdev) +{ + struct keyreset_state *state = platform_get_drvdata(pdev); + input_unregister_handler(&state->input_handler); + kfree(state); + return 0; +} + + +struct platform_driver keyreset_driver = { + .driver.name = KEYRESET_NAME, + .probe = keyreset_probe, + .remove = keyreset_remove, +}; + +static int __init keyreset_init(void) +{ + return platform_driver_register(&keyreset_driver); +} + +static void __exit keyreset_exit(void) +{ + return platform_driver_unregister(&keyreset_driver); +} + +module_init(keyreset_init); +module_exit(keyreset_exit); diff --git a/include/linux/keyreset.h b/include/linux/keyreset.h new file mode 100644 index 000000000000..a2ac49e5b684 --- /dev/null +++ b/include/linux/keyreset.h @@ -0,0 +1,28 @@ +/* + * include/linux/keyreset.h - platform data structure for resetkeys driver + * + * Copyright (C) 2008 Google, Inc. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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. + * + */ + +#ifndef _LINUX_KEYRESET_H +#define _LINUX_KEYRESET_H + +#define KEYRESET_NAME "keyreset" + +struct keyreset_platform_data { + int (*reset_fn)(void); + int *keys_up; + int keys_down[]; /* 0 terminated */ +}; + +#endif /* _LINUX_KEYRESET_H */ From 4e1c5a0f48a5e50e99d7eae21f69195fbe068815 Mon Sep 17 00:00:00 2001 From: Daniel Rosenberg Date: Wed, 7 May 2014 16:52:10 -0700 Subject: [PATCH 0157/1103] ANDROID: input: keycombo: add keycombo, a general key combo driver. Keycombo lets you provide a key up and key down function, and an optional time delay for key down. The driver will call the key down function after the specified key combo has been held for the speicified time delay. After you release the combo, if the key down has happened, it calls key up. Change-Id: I6a9a94e96a8f58fadd908fd1dc7944b9102a089f Signed-off-by: Daniel Rosenberg --- drivers/input/Kconfig | 6 + drivers/input/Makefile | 1 + drivers/input/keycombo.c | 261 +++++++++++++++++++++++++++++++++++++++ include/linux/keycombo.h | 36 ++++++ 4 files changed, 304 insertions(+) create mode 100644 drivers/input/keycombo.c create mode 100644 include/linux/keycombo.h diff --git a/drivers/input/Kconfig b/drivers/input/Kconfig index 8c81757e2e8d..a1979e6e189e 100644 --- a/drivers/input/Kconfig +++ b/drivers/input/Kconfig @@ -190,6 +190,12 @@ config INPUT_KEYRESET ---help--- Say Y here if you want to reboot when some keys are pressed; +config INPUT_KEYCOMBO + bool "Key combo" + depends on INPUT + ---help--- + Say Y here if you want to take action when some keys are pressed; + comment "Input Device Drivers" source "drivers/input/keyboard/Kconfig" diff --git a/drivers/input/Makefile b/drivers/input/Makefile index b5f3008f92f4..f0351af763bd 100644 --- a/drivers/input/Makefile +++ b/drivers/input/Makefile @@ -28,5 +28,6 @@ obj-$(CONFIG_INPUT_MISC) += misc/ obj-$(CONFIG_INPUT_APMPOWER) += apm-power.o obj-$(CONFIG_INPUT_KEYRESET) += keyreset.o +obj-$(CONFIG_INPUT_KEYCOMBO) += keycombo.o obj-$(CONFIG_RMI4_CORE) += rmi4/ diff --git a/drivers/input/keycombo.c b/drivers/input/keycombo.c new file mode 100644 index 000000000000..2fba451b91d5 --- /dev/null +++ b/drivers/input/keycombo.c @@ -0,0 +1,261 @@ +/* drivers/input/keycombo.c + * + * Copyright (C) 2014 Google, Inc. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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. + * + */ + +#include +#include +#include +#include +#include +#include +#include + +struct keycombo_state { + struct input_handler input_handler; + unsigned long keybit[BITS_TO_LONGS(KEY_CNT)]; + unsigned long upbit[BITS_TO_LONGS(KEY_CNT)]; + unsigned long key[BITS_TO_LONGS(KEY_CNT)]; + spinlock_t lock; + struct workqueue_struct *wq; + int key_down_target; + int key_down; + int key_up; + struct delayed_work key_down_work; + int delay; + struct work_struct key_up_work; + void (*key_up_fn)(void *); + void (*key_down_fn)(void *); + void *priv; + int key_is_down; + struct wakeup_source combo_held_wake_source; + struct wakeup_source combo_up_wake_source; +}; + +static void do_key_down(struct work_struct *work) +{ + struct delayed_work *dwork = container_of(work, struct delayed_work, + work); + struct keycombo_state *state = container_of(dwork, + struct keycombo_state, key_down_work); + if (state->key_down_fn) + state->key_down_fn(state->priv); +} + +static void do_key_up(struct work_struct *work) +{ + struct keycombo_state *state = container_of(work, struct keycombo_state, + key_up_work); + if (state->key_up_fn) + state->key_up_fn(state->priv); + __pm_relax(&state->combo_up_wake_source); +} + +static void keycombo_event(struct input_handle *handle, unsigned int type, + unsigned int code, int value) +{ + unsigned long flags; + struct keycombo_state *state = handle->private; + + if (type != EV_KEY) + return; + + if (code >= KEY_MAX) + return; + + if (!test_bit(code, state->keybit)) + return; + + spin_lock_irqsave(&state->lock, flags); + if (!test_bit(code, state->key) == !value) + goto done; + __change_bit(code, state->key); + if (test_bit(code, state->upbit)) { + if (value) + state->key_up++; + else + state->key_up--; + } else { + if (value) + state->key_down++; + else + state->key_down--; + } + if (state->key_down == state->key_down_target && state->key_up == 0) { + __pm_stay_awake(&state->combo_held_wake_source); + state->key_is_down = 1; + if (queue_delayed_work(state->wq, &state->key_down_work, + state->delay)) + pr_debug("Key down work already queued!"); + } else if (state->key_is_down) { + if (!cancel_delayed_work(&state->key_down_work)) { + __pm_stay_awake(&state->combo_up_wake_source); + queue_work(state->wq, &state->key_up_work); + } + __pm_relax(&state->combo_held_wake_source); + state->key_is_down = 0; + } +done: + spin_unlock_irqrestore(&state->lock, flags); +} + +static int keycombo_connect(struct input_handler *handler, + struct input_dev *dev, + const struct input_device_id *id) +{ + int i; + int ret; + struct input_handle *handle; + struct keycombo_state *state = + container_of(handler, struct keycombo_state, input_handler); + for (i = 0; i < KEY_MAX; i++) { + if (test_bit(i, state->keybit) && test_bit(i, dev->keybit)) + break; + } + if (i == KEY_MAX) + return -ENODEV; + + handle = kzalloc(sizeof(*handle), GFP_KERNEL); + if (!handle) + return -ENOMEM; + + handle->dev = dev; + handle->handler = handler; + handle->name = KEYCOMBO_NAME; + handle->private = state; + + ret = input_register_handle(handle); + if (ret) + goto err_input_register_handle; + + ret = input_open_device(handle); + if (ret) + goto err_input_open_device; + + return 0; + +err_input_open_device: + input_unregister_handle(handle); +err_input_register_handle: + kfree(handle); + return ret; +} + +static void keycombo_disconnect(struct input_handle *handle) +{ + input_close_device(handle); + input_unregister_handle(handle); + kfree(handle); +} + +static const struct input_device_id keycombo_ids[] = { + { + .flags = INPUT_DEVICE_ID_MATCH_EVBIT, + .evbit = { BIT_MASK(EV_KEY) }, + }, + { }, +}; +MODULE_DEVICE_TABLE(input, keycombo_ids); + +static int keycombo_probe(struct platform_device *pdev) +{ + int ret; + int key, *keyp; + struct keycombo_state *state; + struct keycombo_platform_data *pdata = pdev->dev.platform_data; + + if (!pdata) + return -EINVAL; + + state = kzalloc(sizeof(*state), GFP_KERNEL); + if (!state) + return -ENOMEM; + + spin_lock_init(&state->lock); + keyp = pdata->keys_down; + while ((key = *keyp++)) { + if (key >= KEY_MAX) + continue; + state->key_down_target++; + __set_bit(key, state->keybit); + } + if (pdata->keys_up) { + keyp = pdata->keys_up; + while ((key = *keyp++)) { + if (key >= KEY_MAX) + continue; + __set_bit(key, state->keybit); + __set_bit(key, state->upbit); + } + } + + state->wq = alloc_ordered_workqueue("keycombo", 0); + if (!state->wq) + return -ENOMEM; + + state->priv = pdata->priv; + + if (pdata->key_down_fn) + state->key_down_fn = pdata->key_down_fn; + INIT_DELAYED_WORK(&state->key_down_work, do_key_down); + + if (pdata->key_up_fn) + state->key_up_fn = pdata->key_up_fn; + INIT_WORK(&state->key_up_work, do_key_up); + + wakeup_source_init(&state->combo_held_wake_source, "key combo"); + wakeup_source_init(&state->combo_up_wake_source, "key combo up"); + state->delay = msecs_to_jiffies(pdata->key_down_delay); + + state->input_handler.event = keycombo_event; + state->input_handler.connect = keycombo_connect; + state->input_handler.disconnect = keycombo_disconnect; + state->input_handler.name = KEYCOMBO_NAME; + state->input_handler.id_table = keycombo_ids; + ret = input_register_handler(&state->input_handler); + if (ret) { + kfree(state); + return ret; + } + platform_set_drvdata(pdev, state); + return 0; +} + +int keycombo_remove(struct platform_device *pdev) +{ + struct keycombo_state *state = platform_get_drvdata(pdev); + input_unregister_handler(&state->input_handler); + destroy_workqueue(state->wq); + kfree(state); + return 0; +} + + +struct platform_driver keycombo_driver = { + .driver.name = KEYCOMBO_NAME, + .probe = keycombo_probe, + .remove = keycombo_remove, +}; + +static int __init keycombo_init(void) +{ + return platform_driver_register(&keycombo_driver); +} + +static void __exit keycombo_exit(void) +{ + return platform_driver_unregister(&keycombo_driver); +} + +module_init(keycombo_init); +module_exit(keycombo_exit); diff --git a/include/linux/keycombo.h b/include/linux/keycombo.h new file mode 100644 index 000000000000..c6db2626b0d3 --- /dev/null +++ b/include/linux/keycombo.h @@ -0,0 +1,36 @@ +/* + * include/linux/keycombo.h - platform data structure for keycombo driver + * + * Copyright (C) 2014 Google, Inc. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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. + * + */ + +#ifndef _LINUX_KEYCOMBO_H +#define _LINUX_KEYCOMBO_H + +#define KEYCOMBO_NAME "keycombo" + +/* + * if key_down_fn and key_up_fn are both present, you are guaranteed that + * key_down_fn will return before key_up_fn is called, and that key_up_fn + * is called iff key_down_fn is called. + */ +struct keycombo_platform_data { + void (*key_down_fn)(void *); + void (*key_up_fn)(void *); + void *priv; + int key_down_delay; /* Time in ms */ + int *keys_up; + int keys_down[]; /* 0 terminated */ +}; + +#endif /* _LINUX_KEYCOMBO_H */ From 218a7725d1e50a75dbb26f2115f5ea0aff3f11ba Mon Sep 17 00:00:00 2001 From: Daniel Rosenberg Date: Wed, 7 May 2014 14:17:47 -0700 Subject: [PATCH 0158/1103] ANDROID: input: keyreset: Changed keyreset to act as a wrapper for keycombo. keyreset now registers a keycombo driver that acts as the old keyreset driver acted. Change-Id: I08f5279e3a33b267571b699697f9f54508868983 Signed-off-by: Daniel Rosenberg --- drivers/input/Kconfig | 1 + drivers/input/keyreset.c | 206 +++++++++++---------------------------- include/linux/keyreset.h | 3 +- 3 files changed, 58 insertions(+), 152 deletions(-) diff --git a/drivers/input/Kconfig b/drivers/input/Kconfig index a1979e6e189e..a78c8309bb3f 100644 --- a/drivers/input/Kconfig +++ b/drivers/input/Kconfig @@ -187,6 +187,7 @@ config INPUT_APMPOWER config INPUT_KEYRESET bool "Reset key" depends on INPUT + select INPUT_KEYCOMBO ---help--- Say Y here if you want to reboot when some keys are pressed; diff --git a/drivers/input/keyreset.c b/drivers/input/keyreset.c index 36208fe0baae..eaaccde82210 100644 --- a/drivers/input/keyreset.c +++ b/drivers/input/keyreset.c @@ -1,6 +1,6 @@ /* drivers/input/keyreset.c * - * Copyright (C) 2008 Google, Inc. + * Copyright (C) 2014 Google, Inc. * * This software is licensed under the terms of the GNU General Public * License version 2, as published by the Free Software Foundation, and @@ -21,200 +21,104 @@ #include #include #include - +#include struct keyreset_state { - struct input_handler input_handler; - unsigned long keybit[BITS_TO_LONGS(KEY_CNT)]; - unsigned long upbit[BITS_TO_LONGS(KEY_CNT)]; - unsigned long key[BITS_TO_LONGS(KEY_CNT)]; - spinlock_t lock; - int key_down_target; - int key_down; - int key_up; - int restart_disabled; + int restart_requested; int (*reset_fn)(void); + struct platform_device *pdev_child; }; -int restart_requested; -static void deferred_restart(struct work_struct *dummy) +static void do_restart(void) { - restart_requested = 2; sys_sync(); - restart_requested = 3; kernel_restart(NULL); } -static DECLARE_WORK(restart_work, deferred_restart); -static void keyreset_event(struct input_handle *handle, unsigned int type, - unsigned int code, int value) +static void do_reset_fn(void *priv) { - unsigned long flags; - struct keyreset_state *state = handle->private; - - if (type != EV_KEY) - return; - - if (code >= KEY_MAX) - return; - - if (!test_bit(code, state->keybit)) - return; - - spin_lock_irqsave(&state->lock, flags); - if (!test_bit(code, state->key) == !value) - goto done; - __change_bit(code, state->key); - if (test_bit(code, state->upbit)) { - if (value) { - state->restart_disabled = 1; - state->key_up++; - } else - state->key_up--; + struct keyreset_state *state = priv; + if (state->restart_requested) + panic("keyboard reset failed, %d", state->restart_requested); + if (state->reset_fn) { + state->restart_requested = state->reset_fn(); } else { - if (value) - state->key_down++; - else - state->key_down--; + pr_info("keyboard reset\n"); + do_restart(); + state->restart_requested = 1; } - if (state->key_down == 0 && state->key_up == 0) - state->restart_disabled = 0; - - pr_debug("reset key changed %d %d new state %d-%d-%d\n", code, value, - state->key_down, state->key_up, state->restart_disabled); - - if (value && !state->restart_disabled && - state->key_down == state->key_down_target) { - state->restart_disabled = 1; - if (restart_requested) - panic("keyboard reset failed, %d", restart_requested); - if (state->reset_fn) { - restart_requested = state->reset_fn(); - } else { - pr_info("keyboard reset\n"); - schedule_work(&restart_work); - restart_requested = 1; - } - } -done: - spin_unlock_irqrestore(&state->lock, flags); } -static int keyreset_connect(struct input_handler *handler, - struct input_dev *dev, - const struct input_device_id *id) -{ - int i; - int ret; - struct input_handle *handle; - struct keyreset_state *state = - container_of(handler, struct keyreset_state, input_handler); - - for (i = 0; i < KEY_MAX; i++) { - if (test_bit(i, state->keybit) && test_bit(i, dev->keybit)) - break; - } - if (i == KEY_MAX) - return -ENODEV; - - handle = kzalloc(sizeof(*handle), GFP_KERNEL); - if (!handle) - return -ENOMEM; - - handle->dev = dev; - handle->handler = handler; - handle->name = "keyreset"; - handle->private = state; - - ret = input_register_handle(handle); - if (ret) - goto err_input_register_handle; - - ret = input_open_device(handle); - if (ret) - goto err_input_open_device; - - pr_info("using input dev %s for key reset\n", dev->name); - - return 0; - -err_input_open_device: - input_unregister_handle(handle); -err_input_register_handle: - kfree(handle); - return ret; -} - -static void keyreset_disconnect(struct input_handle *handle) -{ - input_close_device(handle); - input_unregister_handle(handle); - kfree(handle); -} - -static const struct input_device_id keyreset_ids[] = { - { - .flags = INPUT_DEVICE_ID_MATCH_EVBIT, - .evbit = { BIT_MASK(EV_KEY) }, - }, - { }, -}; -MODULE_DEVICE_TABLE(input, keyreset_ids); - static int keyreset_probe(struct platform_device *pdev) { - int ret; + int ret = -ENOMEM; + struct keycombo_platform_data *pdata_child; + struct keyreset_platform_data *pdata = pdev->dev.platform_data; + int up_size = 0, down_size = 0, size; int key, *keyp; struct keyreset_state *state; - struct keyreset_platform_data *pdata = pdev->dev.platform_data; if (!pdata) return -EINVAL; - - state = kzalloc(sizeof(*state), GFP_KERNEL); + state = devm_kzalloc(&pdev->dev, sizeof(*state), GFP_KERNEL); if (!state) return -ENOMEM; - spin_lock_init(&state->lock); + state->pdev_child = platform_device_alloc(KEYCOMBO_NAME, + PLATFORM_DEVID_AUTO); + if (!state->pdev_child) + return -ENOMEM; + state->pdev_child->dev.parent = &pdev->dev; + keyp = pdata->keys_down; while ((key = *keyp++)) { if (key >= KEY_MAX) continue; - state->key_down_target++; - __set_bit(key, state->keybit); + down_size++; } if (pdata->keys_up) { keyp = pdata->keys_up; while ((key = *keyp++)) { if (key >= KEY_MAX) continue; - __set_bit(key, state->keybit); - __set_bit(key, state->upbit); + up_size++; } } - - if (pdata->reset_fn) - state->reset_fn = pdata->reset_fn; - - state->input_handler.event = keyreset_event; - state->input_handler.connect = keyreset_connect; - state->input_handler.disconnect = keyreset_disconnect; - state->input_handler.name = KEYRESET_NAME; - state->input_handler.id_table = keyreset_ids; - ret = input_register_handler(&state->input_handler); - if (ret) { - kfree(state); - return ret; + size = sizeof(struct keycombo_platform_data) + + sizeof(int) * (down_size + 1); + pdata_child = devm_kzalloc(&pdev->dev, size, GFP_KERNEL); + if (!pdata_child) + goto error; + memcpy(pdata_child->keys_down, pdata->keys_down, + sizeof(int) * down_size); + if (up_size > 0) { + pdata_child->keys_up = devm_kzalloc(&pdev->dev, up_size + 1, + GFP_KERNEL); + if (!pdata_child->keys_up) + goto error; + memcpy(pdata_child->keys_up, pdata->keys_up, + sizeof(int) * up_size); + if (!pdata_child->keys_up) + goto error; } + state->reset_fn = pdata->reset_fn; + pdata_child->key_down_fn = do_reset_fn; + pdata_child->priv = state; + pdata_child->key_down_delay = pdata->key_down_delay; + ret = platform_device_add_data(state->pdev_child, pdata_child, size); + if (ret) + goto error; platform_set_drvdata(pdev, state); - return 0; + return platform_device_add(state->pdev_child); +error: + platform_device_put(state->pdev_child); + return ret; } int keyreset_remove(struct platform_device *pdev) { struct keyreset_state *state = platform_get_drvdata(pdev); - input_unregister_handler(&state->input_handler); - kfree(state); + platform_device_put(state->pdev_child); return 0; } diff --git a/include/linux/keyreset.h b/include/linux/keyreset.h index a2ac49e5b684..2e34afab65e4 100644 --- a/include/linux/keyreset.h +++ b/include/linux/keyreset.h @@ -1,7 +1,7 @@ /* * include/linux/keyreset.h - platform data structure for resetkeys driver * - * Copyright (C) 2008 Google, Inc. + * Copyright (C) 2014 Google, Inc. * * This software is licensed under the terms of the GNU General Public * License version 2, as published by the Free Software Foundation, and @@ -21,6 +21,7 @@ struct keyreset_platform_data { int (*reset_fn)(void); + int key_down_delay; int *keys_up; int keys_down[]; /* 0 terminated */ }; From 7be8c1c4f1a9a836d6a1516ff768869242c92588 Mon Sep 17 00:00:00 2001 From: Daniel Rosenberg Date: Fri, 27 Jun 2014 16:39:35 -0700 Subject: [PATCH 0159/1103] ANDROID: input: keyreset: Made keyreset more robust Switched do_restart to run in a seperate workqueue to handle cases where kernel_restart hangs. Change-Id: I1ecd61f8d0859f1a86d37c692351d644b5db9c69 Signed-off-by: Daniel Rosenberg --- drivers/input/keyreset.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/input/keyreset.c b/drivers/input/keyreset.c index eaaccde82210..7fbf7247e65f 100644 --- a/drivers/input/keyreset.c +++ b/drivers/input/keyreset.c @@ -27,9 +27,10 @@ struct keyreset_state { int restart_requested; int (*reset_fn)(void); struct platform_device *pdev_child; + struct work_struct restart_work; }; -static void do_restart(void) +static void do_restart(struct work_struct *unused) { sys_sync(); kernel_restart(NULL); @@ -44,7 +45,7 @@ static void do_reset_fn(void *priv) state->restart_requested = state->reset_fn(); } else { pr_info("keyboard reset\n"); - do_restart(); + schedule_work(&state->restart_work); state->restart_requested = 1; } } @@ -69,6 +70,7 @@ static int keyreset_probe(struct platform_device *pdev) if (!state->pdev_child) return -ENOMEM; state->pdev_child->dev.parent = &pdev->dev; + INIT_WORK(&state->restart_work, do_restart); keyp = pdata->keys_down; while ((key = *keyp++)) { From 5810ab34e6730743475b62f038a3be8b45ce710a Mon Sep 17 00:00:00 2001 From: Eric Ernst Date: Fri, 2 Sep 2016 16:12:06 -0700 Subject: [PATCH 0160/1103] ANDROID: input: keyreset: switch to orderly_reboot Prior restart function would make a call to sys_sync and then execute a kernel reset. Rather than call the sync directly, thus necessitating this driver to be builtin, call orderly_reboot, which will take care of the file system sync. Note: since CONFIG_INPUT Kconfig is tristate, this driver can be built as module, despite being marked bool. Signed-off-by: Eric Ernst --- drivers/input/keyreset.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/input/keyreset.c b/drivers/input/keyreset.c index 7fbf7247e65f..7e5222aec7c1 100644 --- a/drivers/input/keyreset.c +++ b/drivers/input/keyreset.c @@ -32,8 +32,7 @@ struct keyreset_state { static void do_restart(struct work_struct *unused) { - sys_sync(); - kernel_restart(NULL); + orderly_reboot(); } static void do_reset_fn(void *priv) From 45d25d03efa2f680e8f4c11d89b537734158689f Mon Sep 17 00:00:00 2001 From: Rom Lemarchand Date: Thu, 5 Feb 2015 16:07:59 -0800 Subject: [PATCH 0161/1103] ANDROID: kbuild: make it possible to specify the module output dir Make modinst_dir user-defined on the command line. This allows to do things like: make MODLIB=output/ modinst_dir=. modules_install to ensure all the .ko are in the output/ directory. Change-Id: I2bc007eea27ee744d35289e26e4a8ac43ba04151 Signed-off-by: Rom Lemarchand --- scripts/Makefile.modinst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/Makefile.modinst b/scripts/Makefile.modinst index ff5ca9817a85..8ff0669b3d0d 100644 --- a/scripts/Makefile.modinst +++ b/scripts/Makefile.modinst @@ -30,7 +30,7 @@ quiet_cmd_modules_install = INSTALL $@ INSTALL_MOD_DIR ?= extra ext-mod-dir = $(INSTALL_MOD_DIR)$(subst $(patsubst %/,%,$(KBUILD_EXTMOD)),,$(@D)) -modinst_dir = $(if $(KBUILD_EXTMOD),$(ext-mod-dir),kernel/$(@D)) +modinst_dir ?= $(if $(KBUILD_EXTMOD),$(ext-mod-dir),kernel/$(@D)) $(modules): $(call cmd,modules_install,$(MODLIB)/$(modinst_dir)) From bca65c0452f96e537769069d2261f0e56f4d90af Mon Sep 17 00:00:00 2001 From: Amit Pundir Date: Tue, 5 Jan 2016 17:36:31 +0530 Subject: [PATCH 0162/1103] ANDROID: kbuild: Makefile.clean: make Kbuild and Makefile optional AOSP commit b13ce9f4aa6f "ARM64: add option to build Image.gz/dtb combo" broke archclean / mrproper build targets and we run into: ---------- ./scripts/Makefile.clean:14: arch/arm64/boot/amd/Makefile: No such file or directory make[2]: *** No rule to make target `arch/arm64/boot/amd/Makefile'. Stop. make[1]: *** [arch/arm64/boot/amd] Error 2 make: *** [archclean] Error 2 ---------- This patch skip the missing Kbuild/Makefile reporting error. It does the job (i.e cleanup dts/*/*.dtb and do not spit out missing file error messages as well). Signed-off-by: Amit Pundir --- scripts/Makefile.clean | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/Makefile.clean b/scripts/Makefile.clean index 0b80e3207b20..b20bce9235b9 100644 --- a/scripts/Makefile.clean +++ b/scripts/Makefile.clean @@ -12,7 +12,7 @@ include scripts/Kbuild.include # The filename Kbuild has precedence over Makefile kbuild-dir := $(if $(filter /%,$(src)),$(src),$(srctree)/$(src)) -include $(if $(wildcard $(kbuild-dir)/Kbuild), $(kbuild-dir)/Kbuild, $(kbuild-dir)/Makefile) +-include $(if $(wildcard $(kbuild-dir)/Kbuild), $(kbuild-dir)/Kbuild, $(kbuild-dir)/Makefile) # Figure out what we need to build from the various variables # ========================================================================== From ed3859a6afca814161471637886898223456993b Mon Sep 17 00:00:00 2001 From: Matthias Kaehlcke Date: Fri, 21 Apr 2017 14:04:28 -0700 Subject: [PATCH 0163/1103] CHROMIUM: kbuild: clang: Disable the 'duplicate-decl-specifier' warning clang generates plenty of these warnings in different parts of the code. They are mostly caused by container_of() and other macros which declare a "const *" variable for their internal use which triggers a "duplicate 'const' specifier" warning if the is already const qualified. Change-Id: I3ad9d33e31b7b40f926554eed2afeea1ebb7e961 Wording-mostly-from: Michael Davidson Signed-off-by: Matthias Kaehlcke --- Makefile | 1 + 1 file changed, 1 insertion(+) diff --git a/Makefile b/Makefile index 9b2df076885a..45225b03eed3 100644 --- a/Makefile +++ b/Makefile @@ -700,6 +700,7 @@ KBUILD_CPPFLAGS += $(call cc-option,-Qunused-arguments,) KBUILD_CFLAGS += $(call cc-disable-warning, format-invalid-specifier) KBUILD_CFLAGS += $(call cc-disable-warning, gnu) KBUILD_CFLAGS += $(call cc-disable-warning, address-of-packed-member) +KBUILD_CFLAGS += $(call cc-disable-warning, duplicate-decl-specifier) # Quiet clang warning: comparison of unsigned expression < 0 is always false KBUILD_CFLAGS += $(call cc-disable-warning, tautological-compare) # CLANG uses a _MergedGlobals as optimization, but this breaks modpost, as the From 48a96c47e9a6b9db1960f198b69e9018527490e4 Mon Sep 17 00:00:00 2001 From: Greg Hackmann Date: Tue, 25 Oct 2016 13:59:59 -0700 Subject: [PATCH 0164/1103] ANDROID: Kbuild, LLVMLinux: allow overriding clang target triple Android has an unusual setup where the kernel needs to target [arch]-linux-gnu to avoid Android userspace-specific flags and optimizations, but AOSP doesn't ship a matching binutils. Add a new variable CLANG_TRIPLE which can override the "-target" triple used to compile the kernel, while using a different CROSS_COMPILE to pick the binutils/gcc installation. For Android you'd do something like: export CLANG_TRIPLE=aarch64-linux-gnu- export CROSS_COMPILE=aarch64-linux-android- If you don't need something like this, leave CLANG_TRIPLE unset and it will default to CROSS_COMPILE. Change-Id: I85d63599c6ab8ed458071cdf9197d85b1f7f150b Signed-off-by: Greg Hackmann --- Makefile | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 45225b03eed3..e06e2a325422 100644 --- a/Makefile +++ b/Makefile @@ -482,7 +482,8 @@ endif ifeq ($(cc-name),clang) ifneq ($(CROSS_COMPILE),) -CLANG_TARGET := --target=$(notdir $(CROSS_COMPILE:%-=%)) +CLANG_TRIPLE ?= $(CROSS_COMPILE) +CLANG_TARGET := --target=$(notdir $(CLANG_TRIPLE:%-=%)) GCC_TOOLCHAIN := $(realpath $(dir $(shell which $(LD)))/..) endif ifneq ($(GCC_TOOLCHAIN),) From 1d97477dba7bfdcc1715e901b5ae6c3d9538a051 Mon Sep 17 00:00:00 2001 From: Colin Cross Date: Tue, 27 Oct 2015 16:42:08 -0700 Subject: [PATCH 0165/1103] ANDROID: mm: add a field to store names for private anonymous memory Userspace processes often have multiple allocators that each do anonymous mmaps to get memory. When examining memory usage of individual processes or systems as a whole, it is useful to be able to break down the various heaps that were allocated by each layer and examine their size, RSS, and physical memory usage. This patch adds a user pointer to the shared union in vm_area_struct that points to a null terminated string inside the user process containing a name for the vma. vmas that point to the same address will be merged, but vmas that point to equivalent strings at different addresses will not be merged. Userspace can set the name for a region of memory by calling prctl(PR_SET_VMA, PR_SET_VMA_ANON_NAME, start, len, (unsigned long)name); Setting the name to NULL clears it. The names of named anonymous vmas are shown in /proc/pid/maps as [anon:] and in /proc/pid/smaps in a new "Name" field that is only present for named vmas. If the userspace pointer is no longer valid all or part of the name will be replaced with "". The idea to store a userspace pointer to reduce the complexity within mm (at the expense of the complexity of reading /proc/pid/mem) came from Dave Hansen. This results in no runtime overhead in the mm subsystem other than comparing the anon_name pointers when considering vma merging. The pointer is stored in a union with fieds that are only used on file-backed mappings, so it does not increase memory usage. Includes fix from Jed Davis for typo in prctl_set_vma_anon_name, which could attempt to set the name across two vmas at the same time due to a typo, which might corrupt the vma list. Fix it to use tmp instead of end to limit the name setting to a single vma at a time. Change-Id: I9aa7b6b5ef536cd780599ba4e2fba8ceebe8b59f Signed-off-by: Dmitry Shmidt [AmitP: Fix get_user_pages_remote() call to align with upstream commit 5b56d49fc31d ("mm: add locked parameter to get_user_pages_remote()")] Signed-off-by: Amit Pundir --- Documentation/filesystems/proc.txt | 6 ++ fs/proc/task_mmu.c | 64 +++++++++++- fs/userfaultfd.c | 9 +- include/linux/mm.h | 2 +- include/linux/mm_types.h | 24 ++++- include/uapi/linux/prctl.h | 3 + kernel/sys.c | 152 +++++++++++++++++++++++++++++ mm/madvise.c | 2 +- mm/mempolicy.c | 3 +- mm/mlock.c | 2 +- mm/mmap.c | 39 +++++--- mm/mprotect.c | 2 +- 12 files changed, 280 insertions(+), 28 deletions(-) diff --git a/Documentation/filesystems/proc.txt b/Documentation/filesystems/proc.txt index 22b4b00dee31..02ba2136a358 100644 --- a/Documentation/filesystems/proc.txt +++ b/Documentation/filesystems/proc.txt @@ -398,6 +398,8 @@ is not associated with a file: [stack] = the stack of the main process [vdso] = the "virtual dynamic shared object", the kernel system call handler + [anon:] = an anonymous mapping that has been + named by userspace or if empty, the mapping is anonymous. @@ -426,6 +428,7 @@ KernelPageSize: 4 kB MMUPageSize: 4 kB Locked: 0 kB VmFlags: rd ex mr mw me dw +Name: name from userspace the first of these lines shows the same information as is displayed for the mapping in /proc/PID/maps. The remaining lines show the size of the mapping @@ -498,6 +501,9 @@ Note that there is no guarantee that every flag and associated mnemonic will be present in all further kernel releases. Things get changed, the flags may be vanished or the reverse -- new added. +The "Name" field will only be present on a mapping that has been named by +userspace, and will show the name passed in by userspace. + This file is only present if the CONFIG_MMU kernel configuration option is enabled. diff --git a/fs/proc/task_mmu.c b/fs/proc/task_mmu.c index 5ea1d64cb0b4..2c57d817876c 100644 --- a/fs/proc/task_mmu.c +++ b/fs/proc/task_mmu.c @@ -123,6 +123,56 @@ static void release_task_mempolicy(struct proc_maps_private *priv) } #endif +static void seq_print_vma_name(struct seq_file *m, struct vm_area_struct *vma) +{ + const char __user *name = vma_get_anon_name(vma); + struct mm_struct *mm = vma->vm_mm; + + unsigned long page_start_vaddr; + unsigned long page_offset; + unsigned long num_pages; + unsigned long max_len = NAME_MAX; + int i; + + page_start_vaddr = (unsigned long)name & PAGE_MASK; + page_offset = (unsigned long)name - page_start_vaddr; + num_pages = DIV_ROUND_UP(page_offset + max_len, PAGE_SIZE); + + seq_puts(m, "[anon:"); + + for (i = 0; i < num_pages; i++) { + int len; + int write_len; + const char *kaddr; + long pages_pinned; + struct page *page; + + pages_pinned = get_user_pages_remote(current, mm, + page_start_vaddr, 1, 0, &page, NULL, NULL); + if (pages_pinned < 1) { + seq_puts(m, "]"); + return; + } + + kaddr = (const char *)kmap(page); + len = min(max_len, PAGE_SIZE - page_offset); + write_len = strnlen(kaddr + page_offset, len); + seq_write(m, kaddr + page_offset, write_len); + kunmap(page); + put_page(page); + + /* if strnlen hit a null terminator then we're done */ + if (write_len != len) + break; + + max_len -= len; + page_offset = 0; + page_start_vaddr += PAGE_SIZE; + } + + seq_putc(m, ']'); +} + static void vma_stop(struct proc_maps_private *priv) { struct mm_struct *mm = priv->mm; @@ -344,8 +394,15 @@ show_map_vma(struct seq_file *m, struct vm_area_struct *vma) goto done; } - if (is_stack(vma)) + if (is_stack(vma)) { name = "[stack]"; + goto done; + } + + if (vma_get_anon_name(vma)) { + seq_pad(m, ' '); + seq_print_vma_name(m, vma); + } } done: @@ -780,6 +837,11 @@ static int show_smap(struct seq_file *m, void *v) smap_gather_stats(vma, &mss); show_map_vma(m, vma); + if (vma_get_anon_name(vma)) { + seq_puts(m, "Name: "); + seq_print_vma_name(m, vma); + seq_putc(m, '\n'); + } SEQ_PUT_DEC("Size: ", vma->vm_end - vma->vm_start); SEQ_PUT_DEC(" kB\nKernelPageSize: ", vma_kernel_pagesize(vma)); diff --git a/fs/userfaultfd.c b/fs/userfaultfd.c index bfa0ec69f924..f4a021d5ec83 100644 --- a/fs/userfaultfd.c +++ b/fs/userfaultfd.c @@ -890,7 +890,8 @@ static int userfaultfd_release(struct inode *inode, struct file *file) new_flags, vma->anon_vma, vma->vm_file, vma->vm_pgoff, vma_policy(vma), - NULL_VM_UFFD_CTX); + NULL_VM_UFFD_CTX, + vma_get_anon_name(vma)); if (prev) vma = prev; else @@ -1423,7 +1424,8 @@ static int userfaultfd_register(struct userfaultfd_ctx *ctx, prev = vma_merge(mm, prev, start, vma_end, new_flags, vma->anon_vma, vma->vm_file, vma->vm_pgoff, vma_policy(vma), - ((struct vm_userfaultfd_ctx){ ctx })); + ((struct vm_userfaultfd_ctx){ ctx }), + vma_get_anon_name(vma)); if (prev) { vma = prev; goto next; @@ -1581,7 +1583,8 @@ static int userfaultfd_unregister(struct userfaultfd_ctx *ctx, prev = vma_merge(mm, prev, start, vma_end, new_flags, vma->anon_vma, vma->vm_file, vma->vm_pgoff, vma_policy(vma), - NULL_VM_UFFD_CTX); + NULL_VM_UFFD_CTX, + vma_get_anon_name(vma)); if (prev) { vma = prev; goto next; diff --git a/include/linux/mm.h b/include/linux/mm.h index 0416a7204be3..5c5f2184228f 100644 --- a/include/linux/mm.h +++ b/include/linux/mm.h @@ -2227,7 +2227,7 @@ static inline int vma_adjust(struct vm_area_struct *vma, unsigned long start, extern struct vm_area_struct *vma_merge(struct mm_struct *, struct vm_area_struct *prev, unsigned long addr, unsigned long end, unsigned long vm_flags, struct anon_vma *, struct file *, pgoff_t, - struct mempolicy *, struct vm_userfaultfd_ctx); + struct mempolicy *, struct vm_userfaultfd_ctx, const char __user *); extern struct anon_vma *find_mergeable_anon_vma(struct vm_area_struct *); extern int __split_vma(struct mm_struct *, struct vm_area_struct *, unsigned long addr, int new_below); diff --git a/include/linux/mm_types.h b/include/linux/mm_types.h index 5ed8f6292a53..7dfd40dd8ddc 100644 --- a/include/linux/mm_types.h +++ b/include/linux/mm_types.h @@ -290,11 +290,18 @@ struct vm_area_struct { /* * For areas with an address space and backing store, * linkage into the address_space->i_mmap interval tree. + * + * For private anonymous mappings, a pointer to a null terminated string + * in the user process containing the name given to the vma, or NULL + * if unnamed. */ - struct { - struct rb_node rb; - unsigned long rb_subtree_last; - } shared; + union { + struct { + struct rb_node rb; + unsigned long rb_subtree_last; + } shared; + const char __user *anon_name; + }; /* * A file's MAP_PRIVATE vma can be in both i_mmap tree and anon_vma @@ -649,4 +656,13 @@ typedef struct { unsigned long val; } swp_entry_t; +/* Return the name for an anonymous mapping or NULL for a file-backed mapping */ +static inline const char __user *vma_get_anon_name(struct vm_area_struct *vma) +{ + if (vma->vm_file) + return NULL; + + return vma->anon_name; +} + #endif /* _LINUX_MM_TYPES_H */ diff --git a/include/uapi/linux/prctl.h b/include/uapi/linux/prctl.h index c0d7ea0bf5b6..851c0032e14b 100644 --- a/include/uapi/linux/prctl.h +++ b/include/uapi/linux/prctl.h @@ -219,4 +219,7 @@ struct prctl_mm_map { # define PR_SPEC_DISABLE (1UL << 2) # define PR_SPEC_FORCE_DISABLE (1UL << 3) +#define PR_SET_VMA 0x53564d41 +# define PR_SET_VMA_ANON_NAME 0 + #endif /* _LINUX_PRCTL_H */ diff --git a/kernel/sys.c b/kernel/sys.c index 123bd73046ec..e7e277ff559c 100644 --- a/kernel/sys.c +++ b/kernel/sys.c @@ -42,6 +42,8 @@ #include #include #include +#include +#include #include #include @@ -2258,6 +2260,153 @@ int __weak arch_prctl_spec_ctrl_set(struct task_struct *t, unsigned long which, return -EINVAL; } +#ifdef CONFIG_MMU +static int prctl_update_vma_anon_name(struct vm_area_struct *vma, + struct vm_area_struct **prev, + unsigned long start, unsigned long end, + const char __user *name_addr) +{ + struct mm_struct *mm = vma->vm_mm; + int error = 0; + pgoff_t pgoff; + + if (name_addr == vma_get_anon_name(vma)) { + *prev = vma; + goto out; + } + + pgoff = vma->vm_pgoff + ((start - vma->vm_start) >> PAGE_SHIFT); + *prev = vma_merge(mm, *prev, start, end, vma->vm_flags, vma->anon_vma, + vma->vm_file, pgoff, vma_policy(vma), + vma->vm_userfaultfd_ctx, name_addr); + if (*prev) { + vma = *prev; + goto success; + } + + *prev = vma; + + if (start != vma->vm_start) { + error = split_vma(mm, vma, start, 1); + if (error) + goto out; + } + + if (end != vma->vm_end) { + error = split_vma(mm, vma, end, 0); + if (error) + goto out; + } + +success: + if (!vma->vm_file) + vma->anon_name = name_addr; + +out: + if (error == -ENOMEM) + error = -EAGAIN; + return error; +} + +static int prctl_set_vma_anon_name(unsigned long start, unsigned long end, + unsigned long arg) +{ + unsigned long tmp; + struct vm_area_struct *vma, *prev; + int unmapped_error = 0; + int error = -EINVAL; + + /* + * If the interval [start,end) covers some unmapped address + * ranges, just ignore them, but return -ENOMEM at the end. + * - this matches the handling in madvise. + */ + vma = find_vma_prev(current->mm, start, &prev); + if (vma && start > vma->vm_start) + prev = vma; + + for (;;) { + /* Still start < end. */ + error = -ENOMEM; + if (!vma) + return error; + + /* Here start < (end|vma->vm_end). */ + if (start < vma->vm_start) { + unmapped_error = -ENOMEM; + start = vma->vm_start; + if (start >= end) + return error; + } + + /* Here vma->vm_start <= start < (end|vma->vm_end) */ + tmp = vma->vm_end; + if (end < tmp) + tmp = end; + + /* Here vma->vm_start <= start < tmp <= (end|vma->vm_end). */ + error = prctl_update_vma_anon_name(vma, &prev, start, tmp, + (const char __user *)arg); + if (error) + return error; + start = tmp; + if (prev && start < prev->vm_end) + start = prev->vm_end; + error = unmapped_error; + if (start >= end) + return error; + if (prev) + vma = prev->vm_next; + else /* madvise_remove dropped mmap_sem */ + vma = find_vma(current->mm, start); + } +} + +static int prctl_set_vma(unsigned long opt, unsigned long start, + unsigned long len_in, unsigned long arg) +{ + struct mm_struct *mm = current->mm; + int error; + unsigned long len; + unsigned long end; + + if (start & ~PAGE_MASK) + return -EINVAL; + len = (len_in + ~PAGE_MASK) & PAGE_MASK; + + /* Check to see whether len was rounded up from small -ve to zero */ + if (len_in && !len) + return -EINVAL; + + end = start + len; + if (end < start) + return -EINVAL; + + if (end == start) + return 0; + + down_write(&mm->mmap_sem); + + switch (opt) { + case PR_SET_VMA_ANON_NAME: + error = prctl_set_vma_anon_name(start, end, arg); + break; + default: + error = -EINVAL; + } + + up_write(&mm->mmap_sem); + + return error; +} +#else /* CONFIG_MMU */ +static int prctl_set_vma(unsigned long opt, unsigned long start, + unsigned long len_in, unsigned long arg) +{ + return -EINVAL; +} +#endif + SYSCALL_DEFINE5(prctl, int, option, unsigned long, arg2, unsigned long, arg3, unsigned long, arg4, unsigned long, arg5) { @@ -2476,6 +2625,9 @@ SYSCALL_DEFINE5(prctl, int, option, unsigned long, arg2, unsigned long, arg3, return -EINVAL; error = arch_prctl_spec_ctrl_set(me, arg2, arg3); break; + case PR_SET_VMA: + error = prctl_set_vma(arg2, arg3, arg4, arg5); + break; default: error = -EINVAL; break; diff --git a/mm/madvise.c b/mm/madvise.c index 71d21df2a3f3..899b19e38aee 100644 --- a/mm/madvise.c +++ b/mm/madvise.c @@ -138,7 +138,7 @@ static long madvise_behavior(struct vm_area_struct *vma, pgoff = vma->vm_pgoff + ((start - vma->vm_start) >> PAGE_SHIFT); *prev = vma_merge(mm, *prev, start, end, new_flags, vma->anon_vma, vma->vm_file, pgoff, vma_policy(vma), - vma->vm_userfaultfd_ctx); + vma->vm_userfaultfd_ctx, vma_get_anon_name(vma)); if (*prev) { vma = *prev; goto success; diff --git a/mm/mempolicy.c b/mm/mempolicy.c index da858f794eb6..9625e14b37b4 100644 --- a/mm/mempolicy.c +++ b/mm/mempolicy.c @@ -705,7 +705,8 @@ static int mbind_range(struct mm_struct *mm, unsigned long start, ((vmstart - vma->vm_start) >> PAGE_SHIFT); prev = vma_merge(mm, prev, vmstart, vmend, vma->vm_flags, vma->anon_vma, vma->vm_file, pgoff, - new_pol, vma->vm_userfaultfd_ctx); + new_pol, vma->vm_userfaultfd_ctx, + vma_get_anon_name(vma)); if (prev) { vma = prev; next = vma->vm_next; diff --git a/mm/mlock.c b/mm/mlock.c index 41cc47e28ad6..9fbe2af06cff 100644 --- a/mm/mlock.c +++ b/mm/mlock.c @@ -535,7 +535,7 @@ static int mlock_fixup(struct vm_area_struct *vma, struct vm_area_struct **prev, pgoff = vma->vm_pgoff + ((start - vma->vm_start) >> PAGE_SHIFT); *prev = vma_merge(mm, *prev, start, end, newflags, vma->anon_vma, vma->vm_file, pgoff, vma_policy(vma), - vma->vm_userfaultfd_ctx); + vma->vm_userfaultfd_ctx, vma_get_anon_name(vma)); if (*prev) { vma = *prev; goto success; diff --git a/mm/mmap.c b/mm/mmap.c index 5f2b2b184c60..6d42dc2e792a 100644 --- a/mm/mmap.c +++ b/mm/mmap.c @@ -982,7 +982,8 @@ int __vma_adjust(struct vm_area_struct *vma, unsigned long start, */ static inline int is_mergeable_vma(struct vm_area_struct *vma, struct file *file, unsigned long vm_flags, - struct vm_userfaultfd_ctx vm_userfaultfd_ctx) + struct vm_userfaultfd_ctx vm_userfaultfd_ctx, + const char __user *anon_name) { /* * VM_SOFTDIRTY should not prevent from VMA merging, if we @@ -1000,6 +1001,8 @@ static inline int is_mergeable_vma(struct vm_area_struct *vma, return 0; if (!is_mergeable_vm_userfaultfd_ctx(vma, vm_userfaultfd_ctx)) return 0; + if (vma_get_anon_name(vma) != anon_name) + return 0; return 1; } @@ -1032,9 +1035,10 @@ static int can_vma_merge_before(struct vm_area_struct *vma, unsigned long vm_flags, struct anon_vma *anon_vma, struct file *file, pgoff_t vm_pgoff, - struct vm_userfaultfd_ctx vm_userfaultfd_ctx) + struct vm_userfaultfd_ctx vm_userfaultfd_ctx, + const char __user *anon_name) { - if (is_mergeable_vma(vma, file, vm_flags, vm_userfaultfd_ctx) && + if (is_mergeable_vma(vma, file, vm_flags, vm_userfaultfd_ctx, anon_name) && is_mergeable_anon_vma(anon_vma, vma->anon_vma, vma)) { if (vma->vm_pgoff == vm_pgoff) return 1; @@ -1053,9 +1057,10 @@ static int can_vma_merge_after(struct vm_area_struct *vma, unsigned long vm_flags, struct anon_vma *anon_vma, struct file *file, pgoff_t vm_pgoff, - struct vm_userfaultfd_ctx vm_userfaultfd_ctx) + struct vm_userfaultfd_ctx vm_userfaultfd_ctx, + const char __user *anon_name) { - if (is_mergeable_vma(vma, file, vm_flags, vm_userfaultfd_ctx) && + if (is_mergeable_vma(vma, file, vm_flags, vm_userfaultfd_ctx, anon_name) && is_mergeable_anon_vma(anon_vma, vma->anon_vma, vma)) { pgoff_t vm_pglen; vm_pglen = vma_pages(vma); @@ -1066,9 +1071,9 @@ can_vma_merge_after(struct vm_area_struct *vma, unsigned long vm_flags, } /* - * Given a mapping request (addr,end,vm_flags,file,pgoff), figure out - * whether that can be merged with its predecessor or its successor. - * Or both (it neatly fills a hole). + * Given a mapping request (addr,end,vm_flags,file,pgoff,anon_name), + * figure out whether that can be merged with its predecessor or its + * successor. Or both (it neatly fills a hole). * * In most cases - when called for mmap, brk or mremap - [addr,end) is * certain not to be mapped by the time vma_merge is called; but when @@ -1110,7 +1115,8 @@ struct vm_area_struct *vma_merge(struct mm_struct *mm, unsigned long end, unsigned long vm_flags, struct anon_vma *anon_vma, struct file *file, pgoff_t pgoff, struct mempolicy *policy, - struct vm_userfaultfd_ctx vm_userfaultfd_ctx) + struct vm_userfaultfd_ctx vm_userfaultfd_ctx, + const char __user *anon_name) { pgoff_t pglen = (end - addr) >> PAGE_SHIFT; struct vm_area_struct *area, *next; @@ -1143,7 +1149,8 @@ struct vm_area_struct *vma_merge(struct mm_struct *mm, mpol_equal(vma_policy(prev), policy) && can_vma_merge_after(prev, vm_flags, anon_vma, file, pgoff, - vm_userfaultfd_ctx)) { + vm_userfaultfd_ctx, + anon_name)) { /* * OK, it can. Can we now merge in the successor as well? */ @@ -1152,7 +1159,8 @@ struct vm_area_struct *vma_merge(struct mm_struct *mm, can_vma_merge_before(next, vm_flags, anon_vma, file, pgoff+pglen, - vm_userfaultfd_ctx) && + vm_userfaultfd_ctx, + anon_name) && is_mergeable_anon_vma(prev->anon_vma, next->anon_vma, NULL)) { /* cases 1, 6 */ @@ -1175,7 +1183,8 @@ struct vm_area_struct *vma_merge(struct mm_struct *mm, mpol_equal(policy, vma_policy(next)) && can_vma_merge_before(next, vm_flags, anon_vma, file, pgoff+pglen, - vm_userfaultfd_ctx)) { + vm_userfaultfd_ctx, + anon_name)) { if (prev && addr < prev->vm_end) /* case 4 */ err = __vma_adjust(prev, prev->vm_start, addr, prev->vm_pgoff, NULL, next); @@ -1720,7 +1729,7 @@ unsigned long mmap_region(struct file *file, unsigned long addr, * Can we just expand an old mapping? */ vma = vma_merge(mm, prev, addr, addr + len, vm_flags, - NULL, file, pgoff, NULL, NULL_VM_UFFD_CTX); + NULL, file, pgoff, NULL, NULL_VM_UFFD_CTX, NULL); if (vma) goto out; @@ -2973,7 +2982,7 @@ static int do_brk_flags(unsigned long addr, unsigned long len, unsigned long fla /* Can we just expand an old private anonymous mapping? */ vma = vma_merge(mm, prev, addr, addr + len, flags, - NULL, NULL, pgoff, NULL, NULL_VM_UFFD_CTX); + NULL, NULL, pgoff, NULL, NULL_VM_UFFD_CTX, NULL); if (vma) goto out; @@ -3171,7 +3180,7 @@ struct vm_area_struct *copy_vma(struct vm_area_struct **vmap, return NULL; /* should never get here */ new_vma = vma_merge(mm, prev, addr, addr + len, vma->vm_flags, vma->anon_vma, vma->vm_file, pgoff, vma_policy(vma), - vma->vm_userfaultfd_ctx); + vma->vm_userfaultfd_ctx, vma_get_anon_name(vma)); if (new_vma) { /* * Source vma may have been merged into new_vma diff --git a/mm/mprotect.c b/mm/mprotect.c index 6d331620b9e5..58f591daf578 100644 --- a/mm/mprotect.c +++ b/mm/mprotect.c @@ -398,7 +398,7 @@ mprotect_fixup(struct vm_area_struct *vma, struct vm_area_struct **pprev, pgoff = vma->vm_pgoff + ((start - vma->vm_start) >> PAGE_SHIFT); *pprev = vma_merge(mm, *pprev, start, end, newflags, vma->anon_vma, vma->vm_file, pgoff, vma_policy(vma), - vma->vm_userfaultfd_ctx); + vma->vm_userfaultfd_ctx, vma_get_anon_name(vma)); if (*pprev) { vma = *pprev; VM_WARN_ON((vma->vm_flags ^ newflags) & ~VM_SOFTDIRTY); From 819df70197442e904d72bfc522ede7493efa689f Mon Sep 17 00:00:00 2001 From: Dmitry Shmidt Date: Thu, 7 Oct 2010 14:39:16 -0700 Subject: [PATCH 0166/1103] ANDROID: mmc: core: Add "ignore mmc pm notify" functionality Change-Id: I20821a82831b07ca037973d5d92e832372c6b583 Signed-off-by: Dmitry Shmidt --- drivers/mmc/core/host.c | 6 ++++-- include/linux/mmc/pm.h | 1 + 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/drivers/mmc/core/host.c b/drivers/mmc/core/host.c index f57f5de54206..f3dc49fa078d 100644 --- a/drivers/mmc/core/host.c +++ b/drivers/mmc/core/host.c @@ -441,7 +441,8 @@ int mmc_add_host(struct mmc_host *host) #endif mmc_start_host(host); - mmc_register_pm_notifier(host); + if (!(host->pm_flags & MMC_PM_IGNORE_PM_NOTIFY)) + mmc_register_pm_notifier(host); return 0; } @@ -458,7 +459,8 @@ EXPORT_SYMBOL(mmc_add_host); */ void mmc_remove_host(struct mmc_host *host) { - mmc_unregister_pm_notifier(host); + if (!(host->pm_flags & MMC_PM_IGNORE_PM_NOTIFY)) + mmc_unregister_pm_notifier(host); mmc_stop_host(host); #ifdef CONFIG_DEBUG_FS diff --git a/include/linux/mmc/pm.h b/include/linux/mmc/pm.h index 4a139204c20c..6e2d6a135c7e 100644 --- a/include/linux/mmc/pm.h +++ b/include/linux/mmc/pm.h @@ -26,5 +26,6 @@ typedef unsigned int mmc_pm_flag_t; #define MMC_PM_KEEP_POWER (1 << 0) /* preserve card power during suspend */ #define MMC_PM_WAKE_SDIO_IRQ (1 << 1) /* wake up host system on SDIO IRQ assertion */ +#define MMC_PM_IGNORE_PM_NOTIFY (1 << 2) /* ignore mmc pm notify */ #endif /* LINUX_MMC_PM_H */ From 3e9cbad0a645d737e93ac37a245e8e0eb0cd2a4f Mon Sep 17 00:00:00 2001 From: Robert Love Date: Wed, 15 Oct 2008 15:35:44 -0400 Subject: [PATCH 0167/1103] ANDROID: net: Paranoid network. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit With CONFIG_ANDROID_PARANOID_NETWORK, require specific uids/gids to instantiate network sockets. Signed-off-by: Robert Love paranoid networking: Use in_egroup_p() to check group membership The previous group_search() caused trouble for partners with module builds. in_egroup_p() is also cleaner. Signed-off-by: Nick Pelly Fix 2.6.29 build. Signed-off-by: Arve Hjønnevåg net: Fix compilation of the IPv6 module Fix compilation of the IPv6 module -- current->euid does not exist anymore, current_euid() is what needs to be used. Signed-off-by: Steinar H. Gunderson net: bluetooth: Remove the AID_NET_BT* gid numbers Removed bluetooth checks for AID_NET_BT and AID_NET_BT_ADMIN which are not useful anymore. This is in preparation for getting rid of all the AID_* gids. Change-Id: I879d7181f07532784499ef152288d12a03ab6354 Signed-off-by: JP Abgrall [AmitP: Folded following android-4.9 commit changes into this patch a2624d7b9d73 ("ANDROID: Add android_aid.h")] Signed-off-by: Amit Pundir --- include/linux/android_aid.h | 25 +++++++++++++++++++++++++ net/Kconfig | 6 ++++++ net/bluetooth/af_bluetooth.c | 29 +++++++++++++++++++++++++++++ net/ipv4/af_inet.c | 32 +++++++++++++++++++++++++++++++- net/ipv6/af_inet6.c | 32 +++++++++++++++++++++++++++++++- 5 files changed, 122 insertions(+), 2 deletions(-) create mode 100644 include/linux/android_aid.h diff --git a/include/linux/android_aid.h b/include/linux/android_aid.h new file mode 100644 index 000000000000..dc66530e5fc7 --- /dev/null +++ b/include/linux/android_aid.h @@ -0,0 +1,25 @@ +/* include/linux/android_aid.h + * + * Copyright (C) 2008 Google, Inc. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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. + * + */ + +#ifndef _LINUX_ANDROID_AID_H +#define _LINUX_ANDROID_AID_H + +/* AIDs that the kernel treats differently */ +#define AID_OBSOLETE_000 KGIDT_INIT(3001) /* was NET_BT_ADMIN */ +#define AID_OBSOLETE_001 KGIDT_INIT(3002) /* was NET_BT */ +#define AID_INET KGIDT_INIT(3003) +#define AID_NET_RAW KGIDT_INIT(3004) + +#endif diff --git a/net/Kconfig b/net/Kconfig index 228dfa382eec..4d84cee12ab7 100644 --- a/net/Kconfig +++ b/net/Kconfig @@ -92,6 +92,12 @@ source "net/netlabel/Kconfig" endif # if INET +config ANDROID_PARANOID_NETWORK + bool "Only allow certain groups to create sockets" + default y + help + none + config NETWORK_SECMARK bool "Security Marking" help diff --git a/net/bluetooth/af_bluetooth.c b/net/bluetooth/af_bluetooth.c index deacc52d7ff1..6fa61b875b69 100644 --- a/net/bluetooth/af_bluetooth.c +++ b/net/bluetooth/af_bluetooth.c @@ -108,11 +108,40 @@ void bt_sock_unregister(int proto) } EXPORT_SYMBOL(bt_sock_unregister); +#ifdef CONFIG_PARANOID_NETWORK +static inline int current_has_bt_admin(void) +{ + return !current_euid(); +} + +static inline int current_has_bt(void) +{ + return current_has_bt_admin(); +} +# else +static inline int current_has_bt_admin(void) +{ + return 1; +} + +static inline int current_has_bt(void) +{ + return 1; +} +#endif + static int bt_sock_create(struct net *net, struct socket *sock, int proto, int kern) { int err; + if (proto == BTPROTO_RFCOMM || proto == BTPROTO_SCO || + proto == BTPROTO_L2CAP) { + if (!current_has_bt()) + return -EPERM; + } else if (!current_has_bt_admin()) + return -EPERM; + if (net != &init_net) return -EAFNOSUPPORT; diff --git a/net/ipv4/af_inet.c b/net/ipv4/af_inet.c index 1fbe2f815474..ef1275fcc0b8 100644 --- a/net/ipv4/af_inet.c +++ b/net/ipv4/af_inet.c @@ -123,6 +123,10 @@ #include +#ifdef CONFIG_ANDROID_PARANOID_NETWORK +#include +#endif + /* The inetsw table contains everything that inet_create needs to * build a new socket. */ @@ -240,6 +244,29 @@ int inet_listen(struct socket *sock, int backlog) } EXPORT_SYMBOL(inet_listen); +#ifdef CONFIG_ANDROID_PARANOID_NETWORK +static inline int current_has_network(void) +{ + return (!current_euid() || in_egroup_p(AID_INET) || + in_egroup_p(AID_NET_RAW)); +} +static inline int current_has_cap(struct net *net, int cap) +{ + if (cap == CAP_NET_RAW && in_egroup_p(AID_NET_RAW)) + return 1; + return ns_capable(net->user_ns, cap); +} +# else +static inline int current_has_network(void) +{ + return 1; +} +static inline int current_has_cap(struct net *net, int cap) +{ + return ns_capable(net->user_ns, cap); +} +#endif + /* * Create an inet socket. */ @@ -258,6 +285,9 @@ static int inet_create(struct net *net, struct socket *sock, int protocol, if (protocol < 0 || protocol >= IPPROTO_MAX) return -EINVAL; + if (!current_has_network()) + return -EACCES; + sock->state = SS_UNCONNECTED; /* Look for the requested type/protocol pair. */ @@ -307,7 +337,7 @@ static int inet_create(struct net *net, struct socket *sock, int protocol, err = -EPERM; if (sock->type == SOCK_RAW && !kern && - !ns_capable(net->user_ns, CAP_NET_RAW)) + !current_has_cap(net, CAP_NET_RAW)) goto out_rcu_unlock; sock->ops = answer->ops; diff --git a/net/ipv6/af_inet6.c b/net/ipv6/af_inet6.c index 9a4261e50272..e55548e56843 100644 --- a/net/ipv6/af_inet6.c +++ b/net/ipv6/af_inet6.c @@ -66,6 +66,10 @@ #include #include +#ifdef CONFIG_ANDROID_PARANOID_NETWORK +#include +#endif + #include "ip6_offload.h" MODULE_AUTHOR("Cast of dozens"); @@ -107,6 +111,29 @@ static __inline__ struct ipv6_pinfo *inet6_sk_generic(struct sock *sk) return (struct ipv6_pinfo *)(((u8 *)sk) + offset); } +#ifdef CONFIG_ANDROID_PARANOID_NETWORK +static inline int current_has_network(void) +{ + return (!current_euid() || in_egroup_p(AID_INET) || + in_egroup_p(AID_NET_RAW)); +} +static inline int current_has_cap(struct net *net, int cap) +{ + if (cap == CAP_NET_RAW && in_egroup_p(AID_NET_RAW)) + return 1; + return ns_capable(net->user_ns, cap); +} +# else +static inline int current_has_network(void) +{ + return 1; +} +static inline int current_has_cap(struct net *net, int cap) +{ + return ns_capable(net->user_ns, cap); +} +#endif + static int inet6_create(struct net *net, struct socket *sock, int protocol, int kern) { @@ -122,6 +149,9 @@ static int inet6_create(struct net *net, struct socket *sock, int protocol, if (protocol < 0 || protocol >= IPPROTO_MAX) return -EINVAL; + if (!current_has_network()) + return -EACCES; + /* Look for the requested type/protocol pair. */ lookup_protocol: err = -ESOCKTNOSUPPORT; @@ -169,7 +199,7 @@ static int inet6_create(struct net *net, struct socket *sock, int protocol, err = -EPERM; if (sock->type == SOCK_RAW && !kern && - !ns_capable(net->user_ns, CAP_NET_RAW)) + !current_has_cap(net, CAP_NET_RAW)) goto out_rcu_unlock; sock->ops = answer->ops; From 0793cdd7ef2c15a7c629e40c078a296dca59f82b Mon Sep 17 00:00:00 2001 From: Chia-chi Yeh Date: Fri, 19 Jun 2009 07:15:05 +0800 Subject: [PATCH 0168/1103] ANDROID: net: paranoid: security: Add AID_NET_RAW and AID_NET_ADMIN capability check in cap_capable(). Signed-off-by: Chia-chi Yeh --- include/linux/android_aid.h | 1 + security/commoncap.c | 9 +++++++++ 2 files changed, 10 insertions(+) diff --git a/include/linux/android_aid.h b/include/linux/android_aid.h index dc66530e5fc7..3d7a5ead1200 100644 --- a/include/linux/android_aid.h +++ b/include/linux/android_aid.h @@ -21,5 +21,6 @@ #define AID_OBSOLETE_001 KGIDT_INIT(3002) /* was NET_BT */ #define AID_INET KGIDT_INIT(3003) #define AID_NET_RAW KGIDT_INIT(3004) +#define AID_NET_ADMIN KGIDT_INIT(3005) #endif diff --git a/security/commoncap.c b/security/commoncap.c index 2e489d6a3ac8..ce07c5bc359b 100644 --- a/security/commoncap.c +++ b/security/commoncap.c @@ -31,6 +31,10 @@ #include #include +#ifdef CONFIG_ANDROID_PARANOID_NETWORK +#include +#endif + /* * If a non-root user executes a setuid-root binary in * !secure(SECURE_NOROOT) mode, then we raise capabilities. @@ -73,6 +77,11 @@ int cap_capable(const struct cred *cred, struct user_namespace *targ_ns, { struct user_namespace *ns = targ_ns; + if (cap == CAP_NET_RAW && in_egroup_p(AID_NET_RAW)) + return 0; + if (cap == CAP_NET_ADMIN && in_egroup_p(AID_NET_ADMIN)) + return 0; + /* See if cred has the capability in the target user namespace * by examining the target user namespace and all of the target * user namespace's parents. From 0b5d3cb2b9ca6da7ae2fa7208237776ff7fd0e6e Mon Sep 17 00:00:00 2001 From: Chia-chi Yeh Date: Tue, 30 Jun 2009 11:23:04 +0800 Subject: [PATCH 0169/1103] ANDROID: net: paranoid: Replace AID_NET_RAW checks with capable(CAP_NET_RAW). Signed-off-by: Chia-chi Yeh --- net/ipv4/af_inet.c | 36 +++++++++++------------------------- net/ipv6/af_inet6.c | 36 +++++++++++------------------------- 2 files changed, 22 insertions(+), 50 deletions(-) diff --git a/net/ipv4/af_inet.c b/net/ipv4/af_inet.c index ef1275fcc0b8..32615ca1dd6f 100644 --- a/net/ipv4/af_inet.c +++ b/net/ipv4/af_inet.c @@ -125,6 +125,16 @@ #ifdef CONFIG_ANDROID_PARANOID_NETWORK #include + +static inline int current_has_network(void) +{ + return in_egroup_p(AID_INET) || capable(CAP_NET_RAW); +} +#else +static inline int current_has_network(void) +{ + return 1; +} #endif /* The inetsw table contains everything that inet_create needs to @@ -244,29 +254,6 @@ int inet_listen(struct socket *sock, int backlog) } EXPORT_SYMBOL(inet_listen); -#ifdef CONFIG_ANDROID_PARANOID_NETWORK -static inline int current_has_network(void) -{ - return (!current_euid() || in_egroup_p(AID_INET) || - in_egroup_p(AID_NET_RAW)); -} -static inline int current_has_cap(struct net *net, int cap) -{ - if (cap == CAP_NET_RAW && in_egroup_p(AID_NET_RAW)) - return 1; - return ns_capable(net->user_ns, cap); -} -# else -static inline int current_has_network(void) -{ - return 1; -} -static inline int current_has_cap(struct net *net, int cap) -{ - return ns_capable(net->user_ns, cap); -} -#endif - /* * Create an inet socket. */ @@ -336,8 +323,7 @@ static int inet_create(struct net *net, struct socket *sock, int protocol, } err = -EPERM; - if (sock->type == SOCK_RAW && !kern && - !current_has_cap(net, CAP_NET_RAW)) + if (sock->type == SOCK_RAW && !kern && !capable(CAP_NET_RAW)) goto out_rcu_unlock; sock->ops = answer->ops; diff --git a/net/ipv6/af_inet6.c b/net/ipv6/af_inet6.c index e55548e56843..6c330ed494b5 100644 --- a/net/ipv6/af_inet6.c +++ b/net/ipv6/af_inet6.c @@ -68,6 +68,16 @@ #ifdef CONFIG_ANDROID_PARANOID_NETWORK #include + +static inline int current_has_network(void) +{ + return in_egroup_p(AID_INET) || capable(CAP_NET_RAW); +} +#else +static inline int current_has_network(void) +{ + return 1; +} #endif #include "ip6_offload.h" @@ -111,29 +121,6 @@ static __inline__ struct ipv6_pinfo *inet6_sk_generic(struct sock *sk) return (struct ipv6_pinfo *)(((u8 *)sk) + offset); } -#ifdef CONFIG_ANDROID_PARANOID_NETWORK -static inline int current_has_network(void) -{ - return (!current_euid() || in_egroup_p(AID_INET) || - in_egroup_p(AID_NET_RAW)); -} -static inline int current_has_cap(struct net *net, int cap) -{ - if (cap == CAP_NET_RAW && in_egroup_p(AID_NET_RAW)) - return 1; - return ns_capable(net->user_ns, cap); -} -# else -static inline int current_has_network(void) -{ - return 1; -} -static inline int current_has_cap(struct net *net, int cap) -{ - return ns_capable(net->user_ns, cap); -} -#endif - static int inet6_create(struct net *net, struct socket *sock, int protocol, int kern) { @@ -198,8 +185,7 @@ static int inet6_create(struct net *net, struct socket *sock, int protocol, } err = -EPERM; - if (sock->type == SOCK_RAW && !kern && - !current_has_cap(net, CAP_NET_RAW)) + if (sock->type == SOCK_RAW && !kern && !capable(CAP_NET_RAW)) goto out_rcu_unlock; sock->ops = answer->ops; From e1f4ca467957289752ecc0400d908c66b2a92937 Mon Sep 17 00:00:00 2001 From: Chia-chi Yeh Date: Fri, 15 Jul 2011 15:32:57 -0700 Subject: [PATCH 0170/1103] ANDROID: net: paranoid: Only NET_ADMIN is allowed to fully control TUN interfaces. Signed-off-by: Chia-chi Yeh --- drivers/net/tun.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/net/tun.c b/drivers/net/tun.c index 50e9cc19023a..467b51b1570b 100644 --- a/drivers/net/tun.c +++ b/drivers/net/tun.c @@ -2840,6 +2840,12 @@ static long __tun_chr_ioctl(struct file *file, unsigned int cmd, int ret; bool do_notify = false; +#ifdef CONFIG_ANDROID_PARANOID_NETWORK + if (cmd != TUNGETIFF && !capable(CAP_NET_ADMIN)) { + return -EPERM; + } +#endif + if (cmd == TUNSETIFF || cmd == TUNSETQUEUE || (_IOC_TYPE(cmd) == SOCK_IOC_TYPE && cmd != SIOCGSKNS)) { if (copy_from_user(&ifr, argp, ifreq_len)) From 395b7ed5081f92bbd1e689b25fb29d2005e2c7f1 Mon Sep 17 00:00:00 2001 From: Tushar Behera Date: Mon, 26 Mar 2012 16:54:15 +0530 Subject: [PATCH 0171/1103] ANDROID: net: paranoid: security: Add proper checks for Android specific capability checks Commit b641072 ("security: Add AID_NET_RAW and AID_NET_ADMIN capability check in cap_capable().") introduces additional checks for AID_NET_xxx macros. Since the header file including those macros are conditionally included, the checks should also be conditionally executed. Change-Id: Iaec5208d5b95a46b1ac3f2db8449c661e803fa5b Signed-off-by: Tushar Behera Signed-off-by: Andrey Konovalov --- security/commoncap.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/security/commoncap.c b/security/commoncap.c index ce07c5bc359b..5973c420c1f5 100644 --- a/security/commoncap.c +++ b/security/commoncap.c @@ -77,10 +77,12 @@ int cap_capable(const struct cred *cred, struct user_namespace *targ_ns, { struct user_namespace *ns = targ_ns; +#ifdef CONFIG_ANDROID_PARANOID_NETWORK if (cap == CAP_NET_RAW && in_egroup_p(AID_NET_RAW)) return 0; if (cap == CAP_NET_ADMIN && in_egroup_p(AID_NET_ADMIN)) return 0; +#endif /* See if cred has the capability in the target user namespace * by examining the target user namespace and all of the target From aba9870545a715b94f184e3a6bb339b7c0a1188b Mon Sep 17 00:00:00 2001 From: John Stultz Date: Fri, 25 Aug 2017 16:41:26 -0700 Subject: [PATCH 0172/1103] ANDROID: net: paranoid: commoncap: Begin to warn users of implicit PARANOID_NETWORK capability grants CAP_NET_ADMIN and CAP_NET_RAW are implicity granted to the "special" Android groups net_admin and net_raw. This is a byproduct of the init system not being able to specify capabilities back in the day, but has now been resolved and .rc files can explictly specify the capabilities to be granted to a service. Thus, we should start to remove this implict capability grant, and the first step is to warn when a process doesn't have explicit capablity but is a member of the implicitly granted group, when that capability is checked. This will allow for the PARANOID_NETWORK checks in commoncap.c to be totally removed in a future kernel. Change-Id: I6dac90e23608b6dba14a8f2049ba29ae56cb7ae4 Signed-off-by: John Stultz --- security/commoncap.c | 32 +++++++++++++++++++++++--------- 1 file changed, 23 insertions(+), 9 deletions(-) diff --git a/security/commoncap.c b/security/commoncap.c index 5973c420c1f5..ccc992794004 100644 --- a/security/commoncap.c +++ b/security/commoncap.c @@ -58,7 +58,7 @@ static void warn_setuid_and_fcaps_mixed(const char *fname) } /** - * cap_capable - Determine whether a task has a particular effective capability + * __cap_capable - Determine whether a task has a particular effective capability * @cred: The credentials to use * @ns: The user namespace in which we need the capability * @cap: The capability to check for @@ -72,18 +72,11 @@ static void warn_setuid_and_fcaps_mixed(const char *fname) * cap_has_capability() returns 0 when a task has a capability, but the * kernel's capable() and has_capability() returns 1 for this case. */ -int cap_capable(const struct cred *cred, struct user_namespace *targ_ns, +int __cap_capable(const struct cred *cred, struct user_namespace *targ_ns, int cap, int audit) { struct user_namespace *ns = targ_ns; -#ifdef CONFIG_ANDROID_PARANOID_NETWORK - if (cap == CAP_NET_RAW && in_egroup_p(AID_NET_RAW)) - return 0; - if (cap == CAP_NET_ADMIN && in_egroup_p(AID_NET_ADMIN)) - return 0; -#endif - /* See if cred has the capability in the target user namespace * by examining the target user namespace and all of the target * user namespace's parents. @@ -117,6 +110,27 @@ int cap_capable(const struct cred *cred, struct user_namespace *targ_ns, /* We never get here */ } +int cap_capable(const struct cred *cred, struct user_namespace *targ_ns, + int cap, int audit) +{ + int ret = __cap_capable(cred, targ_ns, cap, audit); + +#ifdef CONFIG_ANDROID_PARANOID_NETWORK + if (ret != 0 && cap == CAP_NET_RAW && in_egroup_p(AID_NET_RAW)) { + printk("Process %s granted CAP_NET_RAW from Android group net_raw.\n", current->comm); + printk(" Please update the .rc file to explictly set 'capabilities NET_RAW'\n"); + printk(" Implicit grants are deprecated and will be removed in the future.\n"); + return 0; + } + if (ret != 0 && cap == CAP_NET_ADMIN && in_egroup_p(AID_NET_ADMIN)) { + printk("Process %s granted CAP_NET_ADMIN from Android group net_admin.\n", current->comm); + printk(" Please update the .rc file to explictly set 'capabilities NET_ADMIN'\n"); + printk(" Implicit grants are deprecated and will be removed in the future.\n"); + return 0; + } +#endif + return ret; +} /** * cap_settime - Determine whether the current process may set the system clock * @ts: The time to set From 677ebf16d90f6a95e6f9e4414bca29d3faaa2973 Mon Sep 17 00:00:00 2001 From: Lorenzo Colitti Date: Wed, 26 Mar 2014 13:03:12 +0900 Subject: [PATCH 0173/1103] ANDROID: net: ip-sysctl: Document tcp_fwmark_accept This documentation patch is the part of original tcp_fwmark_accept implementation, Change-Id: I26bc1eceefd2c588d73b921865ab70e4645ade57 ("net: support marking accepting TCP sockets"). The implementation since then got upstreamed, 84f39b08d786 ("net: support marking accepting TCP sockets"), without this documentation part. Change-Id: I26bc1eceefd2c588d73b921865ab70e4645ade57 Signed-off-by: Lorenzo Colitti [AmitP: Refactored the commit log. Fixes: 84f39b08d786 ("net: support marking accepting TCP sockets") Signed-off-by: Amit Pundir --- Documentation/networking/ip-sysctl.txt | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/Documentation/networking/ip-sysctl.txt b/Documentation/networking/ip-sysctl.txt index 960de8fe3f40..f234dbe3c3f1 100644 --- a/Documentation/networking/ip-sysctl.txt +++ b/Documentation/networking/ip-sysctl.txt @@ -630,6 +630,16 @@ tcp_fastopen_blackhole_timeout_sec - INTEGER 0 to disable the blackhole detection. By default, it is set to 1hr. +tcp_fwmark_accept - BOOLEAN + If set, incoming connections to listening sockets that do not have a + socket mark will set the mark of the accepting socket to the fwmark of + the incoming SYN packet. This will cause all packets on that connection + (starting from the first SYNACK) to be sent with that fwmark. The + listening socket's mark is unchanged. Listening sockets that already + have a fwmark set via setsockopt(SOL_SOCKET, SO_MARK, ...) are + unaffected. + Default: 0 + tcp_syn_retries - INTEGER Number of times initial SYNs for an active TCP connection attempt will be retransmitted. Should not be higher than 127. Default value From 2becf6c782b3eac0196fbfe9732b36501840d7c5 Mon Sep 17 00:00:00 2001 From: Robert Love Date: Thu, 31 Jul 2008 11:12:44 -0400 Subject: [PATCH 0174/1103] ANDROID: net: ipv4: sysfs_net_ipv4: Add sysfs-based knobs for controlling TCP window size Add a family of knobs to /sys/kernel/ipv4 for controlling the TCP window size: tcp_wmem_min tcp_wmem_def tcp_wmem_max tcp_rmem_min tcp_rmem_def tcp_rmem_max This six values mirror the sysctl knobs in /proc/sys/net/ipv4/tcp_wmem and /proc/sys/net/ipv4/tcp_rmem. Sysfs, unlike sysctl, allows us to set and manage the files' permissions and owners. Signed-off-by: Robert Love --- net/ipv4/Makefile | 1 + net/ipv4/sysfs_net_ipv4.c | 88 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 89 insertions(+) create mode 100644 net/ipv4/sysfs_net_ipv4.c diff --git a/net/ipv4/Makefile b/net/ipv4/Makefile index 7446b98661d8..433e35abbcc5 100644 --- a/net/ipv4/Makefile +++ b/net/ipv4/Makefile @@ -20,6 +20,7 @@ obj-$(CONFIG_BPFILTER) += bpfilter/ obj-$(CONFIG_NET_IP_TUNNEL) += ip_tunnel.o obj-$(CONFIG_SYSCTL) += sysctl_net_ipv4.o +obj-$(CONFIG_SYSFS) += sysfs_net_ipv4.o obj-$(CONFIG_PROC_FS) += proc.o obj-$(CONFIG_IP_MULTIPLE_TABLES) += fib_rules.o obj-$(CONFIG_IP_MROUTE) += ipmr.o diff --git a/net/ipv4/sysfs_net_ipv4.c b/net/ipv4/sysfs_net_ipv4.c new file mode 100644 index 000000000000..0cbbf10026a6 --- /dev/null +++ b/net/ipv4/sysfs_net_ipv4.c @@ -0,0 +1,88 @@ +/* + * net/ipv4/sysfs_net_ipv4.c + * + * sysfs-based networking knobs (so we can, unlike with sysctl, control perms) + * + * Copyright (C) 2008 Google, Inc. + * + * Robert Love + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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. + */ + +#include +#include +#include +#include +#include + +#define CREATE_IPV4_FILE(_name, _var) \ +static ssize_t _name##_show(struct kobject *kobj, \ + struct kobj_attribute *attr, char *buf) \ +{ \ + return sprintf(buf, "%d\n", _var); \ +} \ +static ssize_t _name##_store(struct kobject *kobj, \ + struct kobj_attribute *attr, \ + const char *buf, size_t count) \ +{ \ + int val, ret; \ + ret = sscanf(buf, "%d", &val); \ + if (ret != 1) \ + return -EINVAL; \ + if (val < 0) \ + return -EINVAL; \ + _var = val; \ + return count; \ +} \ +static struct kobj_attribute _name##_attr = \ + __ATTR(_name, 0644, _name##_show, _name##_store) + +CREATE_IPV4_FILE(tcp_wmem_min, sysctl_tcp_wmem[0]); +CREATE_IPV4_FILE(tcp_wmem_def, sysctl_tcp_wmem[1]); +CREATE_IPV4_FILE(tcp_wmem_max, sysctl_tcp_wmem[2]); + +CREATE_IPV4_FILE(tcp_rmem_min, sysctl_tcp_rmem[0]); +CREATE_IPV4_FILE(tcp_rmem_def, sysctl_tcp_rmem[1]); +CREATE_IPV4_FILE(tcp_rmem_max, sysctl_tcp_rmem[2]); + +static struct attribute *ipv4_attrs[] = { + &tcp_wmem_min_attr.attr, + &tcp_wmem_def_attr.attr, + &tcp_wmem_max_attr.attr, + &tcp_rmem_min_attr.attr, + &tcp_rmem_def_attr.attr, + &tcp_rmem_max_attr.attr, + NULL +}; + +static struct attribute_group ipv4_attr_group = { + .attrs = ipv4_attrs, +}; + +static __init int sysfs_ipv4_init(void) +{ + struct kobject *ipv4_kobject; + int ret; + + ipv4_kobject = kobject_create_and_add("ipv4", kernel_kobj); + if (!ipv4_kobject) + return -ENOMEM; + + ret = sysfs_create_group(ipv4_kobject, &ipv4_attr_group); + if (ret) { + kobject_put(ipv4_kobject); + return ret; + } + + return 0; +} + +subsys_initcall(sysfs_ipv4_init); From 0ec71cdba3467a219c24bfcac6a5b1808a718524 Mon Sep 17 00:00:00 2001 From: JP Abgrall Date: Fri, 7 Feb 2014 18:40:10 -0800 Subject: [PATCH 0175/1103] ANDROID: net: ipv4: tcp: add a sysctl to config the tcp_default_init_rwnd The default initial rwnd is hardcoded to 10. Now we allow it to be controlled via /proc/sys/net/ipv4/tcp_default_init_rwnd which limits the values from 3 to 100 This is somewhat needed because ipv6 routes are autoconfigured by the kernel. See "An Argument for Increasing TCP's Initial Congestion Window" in https://developers.google.com/speed/articles/tcp_initcwnd_paper.pdf Change-Id: I386b2a9d62de0ebe05c1ebe1b4bd91b314af5c54 Signed-off-by: JP Abgrall Conflicts: net/ipv4/sysctl_net_ipv4.c net/ipv4/tcp_input.c [AmitP: Folded following android-4.9 commit changes into this patch 3823c8b26e6e ("ANDROID: tcp: fix tcp_default_init_rwnd() for 4.1")] Signed-off-by: Amit Pundir --- include/net/tcp.h | 2 ++ net/ipv4/sysctl_net_ipv4.c | 22 ++++++++++++++++++++++ net/ipv4/tcp_input.c | 1 + net/ipv4/tcp_output.c | 2 +- 4 files changed, 26 insertions(+), 1 deletion(-) diff --git a/include/net/tcp.h b/include/net/tcp.h index 770917d0caa7..87ed93f14c52 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -248,6 +248,8 @@ extern long sysctl_tcp_mem[3]; #define TCP_RACK_STATIC_REO_WND 0x2 /* Use static RACK reo wnd */ #define TCP_RACK_NO_DUPTHRESH 0x4 /* Do not use DUPACK threshold in RACK */ +extern int sysctl_tcp_default_init_rwnd; + extern atomic_long_t tcp_memory_allocated; extern struct percpu_counter tcp_sockets_allocated; extern unsigned long tcp_memory_pressure; diff --git a/net/ipv4/sysctl_net_ipv4.c b/net/ipv4/sysctl_net_ipv4.c index 891ed2f91467..bad5a38de987 100644 --- a/net/ipv4/sysctl_net_ipv4.c +++ b/net/ipv4/sysctl_net_ipv4.c @@ -219,6 +219,21 @@ static int ipv4_fwd_update_priority(struct ctl_table *table, int write, return ret; } +/* Validate changes from /proc interface. */ +static int proc_tcp_default_init_rwnd(struct ctl_table *ctl, int write, + void __user *buffer, + size_t *lenp, loff_t *ppos) +{ + int old_value = *(int *)ctl->data; + int ret = proc_dointvec(ctl, write, buffer, lenp, ppos); + int new_value = *(int *)ctl->data; + + if (write && ret == 0 && (new_value < 3 || new_value > 100)) + *(int *)ctl->data = old_value; + + return ret; +} + static int proc_tcp_congestion_control(struct ctl_table *ctl, int write, void __user *buffer, size_t *lenp, loff_t *ppos) { @@ -526,6 +541,13 @@ static struct ctl_table ipv4_table[] = { .mode = 0444, .proc_handler = proc_tcp_available_ulp, }, + { + .procname = "tcp_default_init_rwnd", + .data = &sysctl_tcp_default_init_rwnd, + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = proc_tcp_default_init_rwnd + }, { .procname = "icmp_msgs_per_sec", .data = &sysctl_icmp_msgs_per_sec, diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c index 47e08c1b5bc3..88db2d6f70aa 100644 --- a/net/ipv4/tcp_input.c +++ b/net/ipv4/tcp_input.c @@ -81,6 +81,7 @@ #include int sysctl_tcp_max_orphans __read_mostly = NR_FILE; +int sysctl_tcp_default_init_rwnd __read_mostly = TCP_INIT_CWND * 2; #define FLAG_DATA 0x01 /* Incoming frame contained data. */ #define FLAG_WIN_UPDATE 0x02 /* Incoming ACK was a window update. */ diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c index 597dbd749f05..20abda1b6ff1 100644 --- a/net/ipv4/tcp_output.c +++ b/net/ipv4/tcp_output.c @@ -187,7 +187,7 @@ u32 tcp_default_init_rwnd(u32 mss) * (RFC 3517, Section 4, NextSeg() rule (2)). Further place a * limit when mss is larger than 1460. */ - u32 init_rwnd = TCP_INIT_CWND * 2; + u32 init_rwnd = sysctl_tcp_default_init_rwnd; if (mss > 1460) init_rwnd = max((1460 * init_rwnd) / mss, 2U); From 87b633228cff686b9cb4347807029e0b48bdddd9 Mon Sep 17 00:00:00 2001 From: JP Abgrall Date: Thu, 29 Sep 2011 15:36:49 -0700 Subject: [PATCH 0176/1103] ANDROID: net: ipv6: fix crash caused by ipv6_find_hdr() When calling: ipv6_find_hdr(skb, &thoff, -1, NULL) on a fragmented packet, thoff would be left with a random value causing callers to read random memory offsets with: skb_header_pointer(skb, thoff, ...) Now we force ipv6_find_hdr() to return a failure in this case. Calling: ipv6_find_hdr(skb, &thoff, -1, &fragoff) will set fragoff as expected, and not return a failure. Change-Id: Ib474e8a4267dd2b300feca325811330329684a88 Signed-off-by: JP Abgrall --- net/ipv6/exthdrs_core.c | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/net/ipv6/exthdrs_core.c b/net/ipv6/exthdrs_core.c index ae365df8abf7..1af240dcc50d 100644 --- a/net/ipv6/exthdrs_core.c +++ b/net/ipv6/exthdrs_core.c @@ -166,15 +166,15 @@ EXPORT_SYMBOL_GPL(ipv6_find_tlv); * to explore inner IPv6 header, eg. ICMPv6 error messages. * * If target header is found, its offset is set in *offset and return protocol - * number. Otherwise, return -1. + * number. Otherwise, return -ENOENT or -EBADMSG. * * If the first fragment doesn't contain the final protocol header or * NEXTHDR_NONE it is considered invalid. * * Note that non-1st fragment is special case that "the protocol number * of last header" is "next header" field in Fragment header. In this case, - * *offset is meaningless and fragment offset is stored in *fragoff if fragoff - * isn't NULL. + * *offset is meaningless. If fragoff is not NULL, the fragment offset is + * stored in *fragoff; if it is NULL, return -EINVAL. * * if flags is not NULL and it's a fragment, then the frag flag * IP6_FH_F_FRAG will be set. If it's an AH header, the @@ -251,9 +251,12 @@ int ipv6_find_hdr(const struct sk_buff *skb, unsigned int *offset, if (target < 0 && ((!ipv6_ext_hdr(hp->nexthdr)) || hp->nexthdr == NEXTHDR_NONE)) { - if (fragoff) + if (fragoff) { *fragoff = _frag_off; - return hp->nexthdr; + return hp->nexthdr; + } else { + return -EINVAL; + } } if (!found) return -ENOENT; From 34928efde744bf4ad832c25b8b568797abe85166 Mon Sep 17 00:00:00 2001 From: Lorenzo Colitti Date: Wed, 26 Mar 2014 19:35:41 +0900 Subject: [PATCH 0177/1103] ANDROID: net: ipv6: autoconf routes into per-device tables Currently, IPv6 router discovery always puts routes into RT6_TABLE_MAIN. This causes problems for connection managers that want to support multiple simultaneous network connections and want control over which one is used by default (e.g., wifi and wired). To work around this connection managers typically take the routes they prefer and copy them to static routes with low metrics in the main table. This puts the burden on the connection manager to watch netlink to see if the routes have changed, delete the routes when their lifetime expires, etc. Instead, this patch adds a per-interface sysctl to have the kernel put autoconf routes into different tables. This allows each interface to have its own autoconf table, and choosing the default interface (or using different interfaces at the same time for different types of traffic) can be done using appropriate ip rules. The sysctl behaves as follows: - = 0: default. Put routes into RT6_TABLE_MAIN as before. - > 0: manual. Put routes into the specified table. - < 0: automatic. Add the absolute value of the sysctl to the device's ifindex, and use that table. The automatic mode is most useful in conjunction with net.ipv6.conf.default.accept_ra_rt_table. A connection manager or distribution could set it to, say, -100 on boot, and thereafter just use IP rules. Change-Id: I82d16e3737d9cdfa6489e649e247894d0d60cbb1 Signed-off-by: Lorenzo Colitti [AmitP: Refactored original changes to align with the changes introduced by upstream commit 830218c1add1 ("net: ipv6: Fix processing of RAs in presence of VRF") Also folded following android-4.9 commit changes into this patch be65fb01da4d ("ANDROID: net: ipv6: remove unused variable ifindex in")] Signed-off-by: Amit Pundir --- include/linux/ipv6.h | 1 + include/net/addrconf.h | 2 ++ include/uapi/linux/ipv6.h | 1 + net/ipv6/addrconf.c | 39 ++++++++++++++++++++++++++-- net/ipv6/route.c | 54 ++++++++------------------------------- 5 files changed, 52 insertions(+), 45 deletions(-) diff --git a/include/linux/ipv6.h b/include/linux/ipv6.h index 8415bf1a9776..4a0fc3bce67b 100644 --- a/include/linux/ipv6.h +++ b/include/linux/ipv6.h @@ -42,6 +42,7 @@ struct ipv6_devconf { __s32 accept_ra_rt_info_max_plen; #endif #endif + __s32 accept_ra_rt_table; __s32 proxy_ndp; __s32 accept_source_route; __s32 accept_ra_from_local; diff --git a/include/net/addrconf.h b/include/net/addrconf.h index 6def0351bcc3..cf1076601732 100644 --- a/include/net/addrconf.h +++ b/include/net/addrconf.h @@ -296,6 +296,8 @@ static inline bool ipv6_is_mld(struct sk_buff *skb, int nexthdr, int offset) void addrconf_prefix_rcv(struct net_device *dev, u8 *opt, int len, bool sllao); +u32 addrconf_rt_table(const struct net_device *dev, u32 default_table); + /* * anycast prototypes (anycast.c) */ diff --git a/include/uapi/linux/ipv6.h b/include/uapi/linux/ipv6.h index 9c0f4a92bcff..f2cef9c68424 100644 --- a/include/uapi/linux/ipv6.h +++ b/include/uapi/linux/ipv6.h @@ -166,6 +166,7 @@ enum { DEVCONF_ACCEPT_DAD, DEVCONF_FORCE_TLLAO, DEVCONF_NDISC_NOTIFY, + DEVCONF_ACCEPT_RA_RT_TABLE, DEVCONF_MLDV1_UNSOLICITED_REPORT_INTERVAL, DEVCONF_MLDV2_UNSOLICITED_REPORT_INTERVAL, DEVCONF_SUPPRESS_FRAG_NDISC, diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c index c63ccce6425f..bdc4280a5d95 100644 --- a/net/ipv6/addrconf.c +++ b/net/ipv6/addrconf.c @@ -220,6 +220,7 @@ static struct ipv6_devconf ipv6_devconf __read_mostly = { .accept_ra_rt_info_max_plen = 0, #endif #endif + .accept_ra_rt_table = 0, .proxy_ndp = 0, .accept_source_route = 0, /* we do not accept RH0 by default. */ .disable_ipv6 = 0, @@ -274,6 +275,7 @@ static struct ipv6_devconf ipv6_devconf_dflt __read_mostly = { .accept_ra_rt_info_max_plen = 0, #endif #endif + .accept_ra_rt_table = 0, .proxy_ndp = 0, .accept_source_route = 0, /* we do not accept RH0 by default. */ .disable_ipv6 = 0, @@ -2310,6 +2312,31 @@ static void ipv6_try_regen_rndid(struct inet6_dev *idev, struct in6_addr *tmpad ipv6_regen_rndid(idev); } +u32 addrconf_rt_table(const struct net_device *dev, u32 default_table) { + /* Determines into what table to put autoconf PIO/RIO/default routes + * learned on this device. + * + * - If 0, use the same table for every device. This puts routes into + * one of RT_TABLE_{PREFIX,INFO,DFLT} depending on the type of route + * (but note that these three are currently all equal to + * RT6_TABLE_MAIN). + * - If > 0, use the specified table. + * - If < 0, put routes into table dev->ifindex + (-rt_table). + */ + struct inet6_dev *idev = in6_dev_get(dev); + u32 table; + int sysctl = idev->cnf.accept_ra_rt_table; + if (sysctl == 0) { + table = default_table; + } else if (sysctl > 0) { + table = (u32) sysctl; + } else { + table = (unsigned) dev->ifindex + (-sysctl); + } + in6_dev_put(idev); + return table; +} + /* * Add prefix route. */ @@ -2320,7 +2347,7 @@ addrconf_prefix_route(struct in6_addr *pfx, int plen, u32 metric, u32 flags, gfp_t gfp_flags) { struct fib6_config cfg = { - .fc_table = l3mdev_fib_table(dev) ? : RT6_TABLE_PREFIX, + .fc_table = l3mdev_fib_table(dev) ? : addrconf_rt_table(dev, RT6_TABLE_PREFIX), .fc_metric = metric ? : IP6_RT_PRIO_ADDRCONF, .fc_ifindex = dev->ifindex, .fc_expires = expires, @@ -2354,7 +2381,7 @@ static struct fib6_info *addrconf_get_prefix_route(const struct in6_addr *pfx, struct fib6_node *fn; struct fib6_info *rt = NULL; struct fib6_table *table; - u32 tb_id = l3mdev_fib_table(dev) ? : RT6_TABLE_PREFIX; + u32 tb_id = l3mdev_fib_table(dev) ? : addrconf_rt_table(dev, RT6_TABLE_PREFIX); table = fib6_get_table(dev_net(dev), tb_id); if (!table) @@ -5168,6 +5195,7 @@ static inline void ipv6_store_devconf(struct ipv6_devconf *cnf, array[DEVCONF_ACCEPT_RA_RT_INFO_MAX_PLEN] = cnf->accept_ra_rt_info_max_plen; #endif #endif + array[DEVCONF_ACCEPT_RA_RT_TABLE] = cnf->accept_ra_rt_table; array[DEVCONF_PROXY_NDP] = cnf->proxy_ndp; array[DEVCONF_ACCEPT_SOURCE_ROUTE] = cnf->accept_source_route; #ifdef CONFIG_IPV6_OPTIMISTIC_DAD @@ -6344,6 +6372,13 @@ static const struct ctl_table addrconf_sysctl[] = { }, #endif #endif + { + .procname = "accept_ra_rt_table", + .data = &ipv6_devconf.accept_ra_rt_table, + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = proc_dointvec, + }, { .procname = "proxy_ndp", .data = &ipv6_devconf.proxy_ndp, diff --git a/net/ipv6/route.c b/net/ipv6/route.c index a366c05a239d..76623b84467d 100644 --- a/net/ipv6/route.c +++ b/net/ipv6/route.c @@ -3468,8 +3468,7 @@ static struct fib6_info *rt6_get_route_info(struct net *net, const struct in6_addr *gwaddr, struct net_device *dev) { - u32 tb_id = l3mdev_fib_table(dev) ? : RT6_TABLE_INFO; - int ifindex = dev->ifindex; + u32 tb_id = l3mdev_fib_table(dev) ? : addrconf_rt_table(dev, RT6_TABLE_INFO); struct fib6_node *fn; struct fib6_info *rt = NULL; struct fib6_table *table; @@ -3484,7 +3483,7 @@ static struct fib6_info *rt6_get_route_info(struct net *net, goto out; for_each_fib6_node_rt_rcu(fn) { - if (rt->fib6_nh.nh_dev->ifindex != ifindex) + if (rt->fib6_nh.nh_dev->ifindex != dev->ifindex) continue; if ((rt->fib6_flags & (RTF_ROUTEINFO|RTF_GATEWAY)) != (RTF_ROUTEINFO|RTF_GATEWAY)) continue; @@ -3518,7 +3517,7 @@ static struct fib6_info *rt6_add_route_info(struct net *net, .fc_nlinfo.nl_net = net, }; - cfg.fc_table = l3mdev_fib_table(dev) ? : RT6_TABLE_INFO, + cfg.fc_table = l3mdev_fib_table(dev) ? : addrconf_rt_table(dev, RT6_TABLE_INFO), cfg.fc_dst = *prefix; cfg.fc_gateway = *gwaddr; @@ -3536,7 +3535,7 @@ struct fib6_info *rt6_get_dflt_router(struct net *net, const struct in6_addr *addr, struct net_device *dev) { - u32 tb_id = l3mdev_fib_table(dev) ? : RT6_TABLE_DFLT; + u32 tb_id = l3mdev_fib_table(dev) ? : addrconf_rt_table(dev, RT6_TABLE_MAIN); struct fib6_info *rt; struct fib6_table *table; @@ -3563,7 +3562,7 @@ struct fib6_info *rt6_add_dflt_router(struct net *net, unsigned int pref) { struct fib6_config cfg = { - .fc_table = l3mdev_fib_table(dev) ? : RT6_TABLE_DFLT, + .fc_table = l3mdev_fib_table(dev) ? : addrconf_rt_table(dev, RT6_TABLE_DFLT), .fc_metric = IP6_RT_PRIO_USER, .fc_ifindex = dev->ifindex, .fc_flags = RTF_GATEWAY | RTF_ADDRCONF | RTF_DEFAULT | @@ -3588,47 +3587,16 @@ struct fib6_info *rt6_add_dflt_router(struct net *net, return rt6_get_dflt_router(net, gwaddr, dev); } -static void __rt6_purge_dflt_routers(struct net *net, - struct fib6_table *table) -{ - struct fib6_info *rt; - -restart: - rcu_read_lock(); - for_each_fib6_node_rt_rcu(&table->tb6_root) { - struct net_device *dev = fib6_info_nh_dev(rt); - struct inet6_dev *idev = dev ? __in6_dev_get(dev) : NULL; - - if (rt->fib6_flags & (RTF_DEFAULT | RTF_ADDRCONF) && - (!idev || idev->cnf.accept_ra != 2) && - fib6_info_hold_safe(rt)) { - rcu_read_unlock(); - ip6_del_rt(net, rt); - goto restart; - } - } - rcu_read_unlock(); - - table->flags &= ~RT6_TABLE_HAS_DFLT_ROUTER; +int rt6_addrconf_purge(struct rt6_info *rt, void *arg) { + if (rt->rt6i_flags & (RTF_DEFAULT | RTF_ADDRCONF) && + (!rt->rt6i_idev || rt->rt6i_idev->cnf.accept_ra != 2)) + return -1; + return 0; } void rt6_purge_dflt_routers(struct net *net) { - struct fib6_table *table; - struct hlist_head *head; - unsigned int h; - - rcu_read_lock(); - - for (h = 0; h < FIB6_TABLE_HASHSZ; h++) { - head = &net->ipv6.fib_table_hash[h]; - hlist_for_each_entry_rcu(table, head, tb6_hlist) { - if (table->flags & RT6_TABLE_HAS_DFLT_ROUTER) - __rt6_purge_dflt_routers(net, table); - } - } - - rcu_read_unlock(); + fib6_clean_all(net, rt6_addrconf_purge, NULL); } static void rtmsg_to_fib6_config(struct net *net, From c8351519582e7773d83b14b6fc976fa2c06b6cd0 Mon Sep 17 00:00:00 2001 From: Nick Pelly Date: Thu, 4 Dec 2008 17:37:05 -0800 Subject: [PATCH 0178/1103] ANDROID: net: rfkill: Introduce CONFIG_RFKILL_PM and use instead of CONFIG_PM to power down Some platforms do not want to power down rfkill devices on suspend. Change-Id: I62a11630521c636d54a4a02ab9037a43435925f5 Signed-off-by: Nick Pelly [AmitP: Folded following android-4.9 commit changes into this patch faad2b874fea ("rfkill: fix unused function warning")] Signed-off-by: Amit Pundir --- net/rfkill/Kconfig | 5 +++++ net/rfkill/core.c | 11 +++-------- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/net/rfkill/Kconfig b/net/rfkill/Kconfig index 060600b03fad..7c33c8bb2cd9 100644 --- a/net/rfkill/Kconfig +++ b/net/rfkill/Kconfig @@ -10,6 +10,11 @@ menuconfig RFKILL To compile this driver as a module, choose M here: the module will be called rfkill. +config RFKILL_PM + bool "Power off on suspend" + depends on RFKILL && PM + default y + # LED trigger support config RFKILL_LEDS bool diff --git a/net/rfkill/core.c b/net/rfkill/core.c index 1355f5ca8d22..f0a061c11a40 100644 --- a/net/rfkill/core.c +++ b/net/rfkill/core.c @@ -870,8 +870,7 @@ void rfkill_resume_polling(struct rfkill *rfkill) } EXPORT_SYMBOL(rfkill_resume_polling); -#ifdef CONFIG_PM_SLEEP -static int rfkill_suspend(struct device *dev) +static __maybe_unused int rfkill_suspend(struct device *dev) { struct rfkill *rfkill = to_rfkill(dev); @@ -881,7 +880,7 @@ static int rfkill_suspend(struct device *dev) return 0; } -static int rfkill_resume(struct device *dev) +static __maybe_unused int rfkill_resume(struct device *dev) { struct rfkill *rfkill = to_rfkill(dev); bool cur; @@ -901,17 +900,13 @@ static int rfkill_resume(struct device *dev) } static SIMPLE_DEV_PM_OPS(rfkill_pm_ops, rfkill_suspend, rfkill_resume); -#define RFKILL_PM_OPS (&rfkill_pm_ops) -#else -#define RFKILL_PM_OPS NULL -#endif static struct class rfkill_class = { .name = "rfkill", .dev_release = rfkill_release, .dev_groups = rfkill_dev_groups, .dev_uevent = rfkill_dev_uevent, - .pm = RFKILL_PM_OPS, + .pm = IS_ENABLED(CONFIG_RFKILL_PM) ? &rfkill_pm_ops : NULL, }; bool rfkill_blocked(struct rfkill *rfkill) From 3bc56fee148102b542b944e83630992bcb3070ef Mon Sep 17 00:00:00 2001 From: Dmitry Shmidt Date: Tue, 24 Jun 2014 09:36:50 -0700 Subject: [PATCH 0179/1103] ANDROID: net: wireless: Decrease scan entry expiration to avoid stall results Change-Id: I0e23ce45d78d7c17633670973f49943a5ed6032d Signed-off-by: Dmitry Shmidt --- net/wireless/scan.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/wireless/scan.c b/net/wireless/scan.c index d0e7472dd9fd..3391c14ee3a1 100644 --- a/net/wireless/scan.c +++ b/net/wireless/scan.c @@ -71,7 +71,7 @@ module_param(bss_entries_limit, int, 0644); MODULE_PARM_DESC(bss_entries_limit, "limit to number of scan BSS entries (per wiphy, default 1000)"); -#define IEEE80211_SCAN_RESULT_EXPIRE (30 * HZ) +#define IEEE80211_SCAN_RESULT_EXPIRE (7 * HZ) static void bss_free(struct cfg80211_internal_bss *bss) { From ff0cc7f3b5228fd0e4d0d9dc8eaf830e8dd59710 Mon Sep 17 00:00:00 2001 From: Jimmy Perchet Date: Mon, 9 May 2016 10:32:04 -0700 Subject: [PATCH 0180/1103] ANDROID: net: wireless: wlcore: Disable filtering in AP role When you configure (set it up) a STA interface, the driver install a multicast filter. This is normal behavior, when one application subscribe to multicast address the filter is updated. When Access Point interface is configured, there is no filter installation and the "filter update" path is disabled in the driver. The problem happens when you switch an interface from STA type to AP type. The filter is installed but there are no means to update it. Change-Id: Ied22323af831575303abd548574918baa9852dd0 Signed-off-by: Dmitry Shmidt Signed-off-by: Amit Pundir --- drivers/net/wireless/ti/wlcore/init.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/net/wireless/ti/wlcore/init.c b/drivers/net/wireless/ti/wlcore/init.c index 58898b99d3f7..145e10a8be55 100644 --- a/drivers/net/wireless/ti/wlcore/init.c +++ b/drivers/net/wireless/ti/wlcore/init.c @@ -549,6 +549,11 @@ static int wl12xx_init_ap_role(struct wl1271 *wl, struct wl12xx_vif *wlvif) { int ret; + /* Disable filtering */ + ret = wl1271_acx_group_address_tbl(wl, wlvif, false, NULL, 0); + if (ret < 0) + return ret; + ret = wl1271_acx_ap_max_tx_retry(wl, wlvif); if (ret < 0) return ret; From ad90cc2d00f61b2c66f4c5ee140ae2f99858705b Mon Sep 17 00:00:00 2001 From: Lorenzo Colitti Date: Wed, 10 May 2017 23:54:04 +0900 Subject: [PATCH 0181/1103] ANDROID: net: xfrm: make PF_KEY SHA256 use RFC-compliant truncation. When using the PF_KEY interface, SHA-256 hashes are hardcoded to use 96-bit truncation. This is a violation of RFC4868, which specifies 128-bit truncation, but will not be fixed upstream due to backwards compatibility concerns and because the PF_KEY interface is deprecated in favour of netlink XFRM (which allows the app to specify an arbitrary truncation length). Change the hardcoded truncation length from 96 to 128 so that PF_KEY apps such as racoon will work with standards-compliant VPN servers. Bug: 34114242 Change-Id: Ie46bff4b6358f18117d0be241171d677d31d33f7 Signed-off-by: Lorenzo Colitti --- net/xfrm/xfrm_algo.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/xfrm/xfrm_algo.c b/net/xfrm/xfrm_algo.c index 44ac85fe2bc9..d0ca0dbf494e 100644 --- a/net/xfrm/xfrm_algo.c +++ b/net/xfrm/xfrm_algo.c @@ -241,7 +241,7 @@ static struct xfrm_algo_desc aalg_list[] = { .uinfo = { .auth = { - .icv_truncbits = 96, + .icv_truncbits = 128, .icv_fullbits = 256, } }, From 0edecf5be1a30b94605eaf0e3a09fe10475c53a6 Mon Sep 17 00:00:00 2001 From: JP Abgrall Date: Tue, 21 Jun 2011 11:14:49 -0700 Subject: [PATCH 0182/1103] ANDROID: netfilter: xt_quota2: adding the original quota2 from xtables-addons The original xt_quota in the kernel is plain broken: - counts quota at a per CPU level (was written back when ubiquitous SMP was just a dream) - provides no way to count across IPV4/IPV6. This patch is the original unaltered code from: http://sourceforge.net/projects/xtables-addons at commit e84391ce665cef046967f796dd91026851d6bbf3 Change-Id: I19d49858840effee9ecf6cff03c23b45a97efdeb Signed-off-by: JP Abgrall --- include/linux/netfilter/xt_quota2.h | 25 +++ net/netfilter/xt_quota2.c | 274 ++++++++++++++++++++++++++++ 2 files changed, 299 insertions(+) create mode 100644 include/linux/netfilter/xt_quota2.h create mode 100644 net/netfilter/xt_quota2.c diff --git a/include/linux/netfilter/xt_quota2.h b/include/linux/netfilter/xt_quota2.h new file mode 100644 index 000000000000..eadc6903314e --- /dev/null +++ b/include/linux/netfilter/xt_quota2.h @@ -0,0 +1,25 @@ +#ifndef _XT_QUOTA_H +#define _XT_QUOTA_H + +enum xt_quota_flags { + XT_QUOTA_INVERT = 1 << 0, + XT_QUOTA_GROW = 1 << 1, + XT_QUOTA_PACKET = 1 << 2, + XT_QUOTA_NO_CHANGE = 1 << 3, + XT_QUOTA_MASK = 0x0F, +}; + +struct xt_quota_counter; + +struct xt_quota_mtinfo2 { + char name[15]; + u_int8_t flags; + + /* Comparison-invariant */ + aligned_u64 quota; + + /* Used internally by the kernel */ + struct xt_quota_counter *master __attribute__((aligned(8))); +}; + +#endif /* _XT_QUOTA_H */ diff --git a/net/netfilter/xt_quota2.c b/net/netfilter/xt_quota2.c new file mode 100644 index 000000000000..4857008f1eb0 --- /dev/null +++ b/net/netfilter/xt_quota2.c @@ -0,0 +1,274 @@ +/* + * xt_quota2 - enhanced xt_quota that can count upwards and in packets + * as a minimal accounting match. + * by Jan Engelhardt , 2008 + * + * Originally based on xt_quota.c: + * netfilter module to enforce network quotas + * Sam Johnston + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License; either + * version 2 of the License, as published by the Free Software Foundation. + */ +#include +#include +#include +#include +#include + +#include +#include "xt_quota2.h" +#include "compat_xtables.h" + +/** + * @lock: lock to protect quota writers from each other + */ +struct xt_quota_counter { + u_int64_t quota; + spinlock_t lock; + struct list_head list; + atomic_t ref; + char name[sizeof(((struct xt_quota_mtinfo2 *)NULL)->name)]; + struct proc_dir_entry *procfs_entry; +}; + +static LIST_HEAD(counter_list); +static DEFINE_SPINLOCK(counter_list_lock); + +static struct proc_dir_entry *proc_xt_quota; +static unsigned int quota_list_perms = S_IRUGO | S_IWUSR; +static unsigned int quota_list_uid = 0; +static unsigned int quota_list_gid = 0; +module_param_named(perms, quota_list_perms, uint, S_IRUGO | S_IWUSR); +module_param_named(uid, quota_list_uid, uint, S_IRUGO | S_IWUSR); +module_param_named(gid, quota_list_gid, uint, S_IRUGO | S_IWUSR); + +static int quota_proc_read(char *page, char **start, off_t offset, + int count, int *eof, void *data) +{ + struct xt_quota_counter *e = data; + int ret; + + spin_lock_bh(&e->lock); + ret = snprintf(page, PAGE_SIZE, "%llu\n", e->quota); + spin_unlock_bh(&e->lock); + return ret; +} + +static int quota_proc_write(struct file *file, const char __user *input, + unsigned long size, void *data) +{ + struct xt_quota_counter *e = data; + char buf[sizeof("18446744073709551616")]; + + if (size > sizeof(buf)) + size = sizeof(buf); + if (copy_from_user(buf, input, size) != 0) + return -EFAULT; + buf[sizeof(buf)-1] = '\0'; + + spin_lock_bh(&e->lock); + e->quota = simple_strtoull(buf, NULL, 0); + spin_unlock_bh(&e->lock); + return size; +} + +static struct xt_quota_counter * +q2_new_counter(const struct xt_quota_mtinfo2 *q, bool anon) +{ + struct xt_quota_counter *e; + unsigned int size; + + /* Do not need all the procfs things for anonymous counters. */ + size = anon ? offsetof(typeof(*e), list) : sizeof(*e); + e = kmalloc(size, GFP_KERNEL); + if (e == NULL) + return NULL; + + e->quota = q->quota; + spin_lock_init(&e->lock); + if (!anon) { + INIT_LIST_HEAD(&e->list); + atomic_set(&e->ref, 1); + strncpy(e->name, q->name, sizeof(e->name)); + } + return e; +} + +/** + * q2_get_counter - get ref to counter or create new + * @name: name of counter + */ +static struct xt_quota_counter * +q2_get_counter(const struct xt_quota_mtinfo2 *q) +{ + struct proc_dir_entry *p; + struct xt_quota_counter *e; + + if (*q->name == '\0') + return q2_new_counter(q, true); + + spin_lock_bh(&counter_list_lock); + list_for_each_entry(e, &counter_list, list) + if (strcmp(e->name, q->name) == 0) { + atomic_inc(&e->ref); + spin_unlock_bh(&counter_list_lock); + return e; + } + + e = q2_new_counter(q, false); + if (e == NULL) + goto out; + + p = e->procfs_entry = create_proc_entry(e->name, quota_list_perms, + proc_xt_quota); + if (p == NULL || IS_ERR(p)) + goto out; + +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 29) + p->owner = THIS_MODULE; +#endif + p->data = e; + p->read_proc = quota_proc_read; + p->write_proc = quota_proc_write; + p->uid = quota_list_uid; + p->gid = quota_list_gid; + list_add_tail(&e->list, &counter_list); + spin_unlock_bh(&counter_list_lock); + return e; + + out: + spin_unlock_bh(&counter_list_lock); + kfree(e); + return NULL; +} + +static int quota_mt2_check(const struct xt_mtchk_param *par) +{ + struct xt_quota_mtinfo2 *q = par->matchinfo; + + if (q->flags & ~XT_QUOTA_MASK) + return -EINVAL; + + q->name[sizeof(q->name)-1] = '\0'; + if (*q->name == '.' || strchr(q->name, '/') != NULL) { + printk(KERN_ERR "xt_quota.3: illegal name\n"); + return -EINVAL; + } + + q->master = q2_get_counter(q); + if (q->master == NULL) { + printk(KERN_ERR "xt_quota.3: memory alloc failure\n"); + return -ENOMEM; + } + + return 0; +} + +static void quota_mt2_destroy(const struct xt_mtdtor_param *par) +{ + struct xt_quota_mtinfo2 *q = par->matchinfo; + struct xt_quota_counter *e = q->master; + + if (*q->name == '\0') { + kfree(e); + return; + } + + spin_lock_bh(&counter_list_lock); + if (!atomic_dec_and_test(&e->ref)) { + spin_unlock_bh(&counter_list_lock); + return; + } + + list_del(&e->list); + remove_proc_entry(e->name, proc_xt_quota); + spin_unlock_bh(&counter_list_lock); + kfree(e); +} + +static bool +quota_mt2(const struct sk_buff *skb, struct xt_action_param *par) +{ + struct xt_quota_mtinfo2 *q = (void *)par->matchinfo; + struct xt_quota_counter *e = q->master; + bool ret = q->flags & XT_QUOTA_INVERT; + + spin_lock_bh(&e->lock); + if (q->flags & XT_QUOTA_GROW) { + /* + * While no_change is pointless in "grow" mode, we will + * implement it here simply to have a consistent behavior. + */ + if (!(q->flags & XT_QUOTA_NO_CHANGE)) { + e->quota += (q->flags & XT_QUOTA_PACKET) ? 1 : skb->len; + q->quota = e->quota; + } + ret = true; + } else { + if (e->quota >= skb->len) { + if (!(q->flags & XT_QUOTA_NO_CHANGE)) + e->quota -= (q->flags & XT_QUOTA_PACKET) ? 1 : skb->len; + ret = !ret; + } else { + /* we do not allow even small packets from now on */ + e->quota = 0; + } + q->quota = e->quota; + } + spin_unlock_bh(&e->lock); + return ret; +} + +static struct xt_match quota_mt2_reg[] __read_mostly = { + { + .name = "quota2", + .revision = 3, + .family = NFPROTO_IPV4, + .checkentry = quota_mt2_check, + .match = quota_mt2, + .destroy = quota_mt2_destroy, + .matchsize = sizeof(struct xt_quota_mtinfo2), + .me = THIS_MODULE, + }, + { + .name = "quota2", + .revision = 3, + .family = NFPROTO_IPV6, + .checkentry = quota_mt2_check, + .match = quota_mt2, + .destroy = quota_mt2_destroy, + .matchsize = sizeof(struct xt_quota_mtinfo2), + .me = THIS_MODULE, + }, +}; + +static int __init quota_mt2_init(void) +{ + int ret; + + proc_xt_quota = proc_mkdir("xt_quota", init_net__proc_net); + if (proc_xt_quota == NULL) + return -EACCES; + + ret = xt_register_matches(quota_mt2_reg, ARRAY_SIZE(quota_mt2_reg)); + if (ret < 0) + remove_proc_entry("xt_quota", init_net__proc_net); + return ret; +} + +static void __exit quota_mt2_exit(void) +{ + xt_unregister_matches(quota_mt2_reg, ARRAY_SIZE(quota_mt2_reg)); + remove_proc_entry("xt_quota", init_net__proc_net); +} + +module_init(quota_mt2_init); +module_exit(quota_mt2_exit); +MODULE_DESCRIPTION("Xtables: countdown quota match; up counter"); +MODULE_AUTHOR("Sam Johnston "); +MODULE_AUTHOR("Jan Engelhardt "); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("ipt_quota2"); +MODULE_ALIAS("ip6t_quota2"); From acd76ddc8c24df7f3fa6db75f0316f57ac0ebfad Mon Sep 17 00:00:00 2001 From: JP Abgrall Date: Tue, 12 Jul 2011 12:02:59 -0700 Subject: [PATCH 0183/1103] ANDROID: netfilter: xt_quota2: fixup the quota2, and enable. The xt_quota2 came from http://sourceforge.net/projects/xtables-addons/develop It needed tweaking for it to compile within the kernel tree. Fixed kmalloc() and create_proc_entry() invocations within a non-interruptible context. Removed useless copying of current quota back to the iptable's struct matchinfo: - those are per CPU: they will change randomly based on which cpu gets to update the value. - they prevent matching a rule: e.g. -A chain -m quota2 --name q1 --quota 123 can't be followed by -D chain -m quota2 --name q1 --quota 123 as the 123 will be compared to the struct matchinfo's quota member. Use the NETLINK NETLINK_NFLOG family to log a single message when the quota limit is reached. It uses the same packet type as ipt_ULOG, but - never copies skb data, - uses 112 as the event number (ULOG's +1) It doesn't log if the module param "event_num" is 0. Change-Id: I021d3b743db3b22158cc49acb5c94d905b501492 Signed-off-by: JP Abgrall [AmitP: Fix quota2_log() call and use ktime directly to align with upstream commits 2456e8553544 ("ktime: Get rid of the union") and 613dbd95723a ("netfilter: x_tables: move hook state into xt_action_param structure"). Also folded following android-4.9 commit changes into this patch eb6aba2a14b9 ("ANDROID: netfilter: xt_quota2: 3.10 fixes.") Parts of 85a2eb5b48fc ("ANDROID: netfilter: xt_qtaguid: 64-bit warning fixes") 89f9044e826c ("ANDROID: net: kuid/kguid build fixes") 60d4c172c5cd ("ANDROID: netfilter: xt_quota2: make quota2_log work well")] Signed-off-by: Amit Pundir --- net/netfilter/Kconfig | 23 +++++ net/netfilter/Makefile | 1 + net/netfilter/xt_quota2.c | 211 ++++++++++++++++++++++++++++++-------- 3 files changed, 193 insertions(+), 42 deletions(-) diff --git a/net/netfilter/Kconfig b/net/netfilter/Kconfig index f61c306de1d0..4bde51a5902e 100644 --- a/net/netfilter/Kconfig +++ b/net/netfilter/Kconfig @@ -1460,6 +1460,29 @@ config NETFILTER_XT_MATCH_QUOTA If you want to compile it as a module, say M here and read . If unsure, say `N'. +config NETFILTER_XT_MATCH_QUOTA2 + tristate '"quota2" match support' + depends on NETFILTER_ADVANCED + help + This option adds a `quota2' match, which allows to match on a + byte counter correctly and not per CPU. + It allows naming the quotas. + This is based on http://xtables-addons.git.sourceforge.net + + If you want to compile it as a module, say M here and read + . If unsure, say `N'. + +config NETFILTER_XT_MATCH_QUOTA2_LOG + bool '"quota2" Netfilter LOG support' + depends on NETFILTER_XT_MATCH_QUOTA2 + default n + help + This option allows `quota2' to log ONCE when a quota limit + is passed. It logs via NETLINK using the NETLINK_NFLOG family. + It logs similarly to how ipt_ULOG would without data. + + If unsure, say `N'. + config NETFILTER_XT_MATCH_RATEEST tristate '"rateest" match support' depends on NETFILTER_ADVANCED diff --git a/net/netfilter/Makefile b/net/netfilter/Makefile index 16895e045b66..9c87ed55eba7 100644 --- a/net/netfilter/Makefile +++ b/net/netfilter/Makefile @@ -191,6 +191,7 @@ obj-$(CONFIG_NETFILTER_XT_MATCH_PHYSDEV) += xt_physdev.o obj-$(CONFIG_NETFILTER_XT_MATCH_PKTTYPE) += xt_pkttype.o obj-$(CONFIG_NETFILTER_XT_MATCH_POLICY) += xt_policy.o obj-$(CONFIG_NETFILTER_XT_MATCH_QUOTA) += xt_quota.o +obj-$(CONFIG_NETFILTER_XT_MATCH_QUOTA2) += xt_quota2.o obj-$(CONFIG_NETFILTER_XT_MATCH_RATEEST) += xt_rateest.o obj-$(CONFIG_NETFILTER_XT_MATCH_REALM) += xt_realm.o obj-$(CONFIG_NETFILTER_XT_MATCH_RECENT) += xt_recent.o diff --git a/net/netfilter/xt_quota2.c b/net/netfilter/xt_quota2.c index 4857008f1eb0..24b774263aa6 100644 --- a/net/netfilter/xt_quota2.c +++ b/net/netfilter/xt_quota2.c @@ -12,14 +12,37 @@ * version 2 of the License, as published by the Free Software Foundation. */ #include +#include #include #include #include #include +#include #include -#include "xt_quota2.h" -#include "compat_xtables.h" +#include + +#ifdef CONFIG_NETFILTER_XT_MATCH_QUOTA2_LOG +/* For compatibility, these definitions are copied from the + * deprecated header file */ +#define ULOG_MAC_LEN 80 +#define ULOG_PREFIX_LEN 32 + +/* Format of the ULOG packets passed through netlink */ +typedef struct ulog_packet_msg { + unsigned long mark; + long timestamp_sec; + long timestamp_usec; + unsigned int hook; + char indev_name[IFNAMSIZ]; + char outdev_name[IFNAMSIZ]; + size_t data_len; + char prefix[ULOG_PREFIX_LEN]; + unsigned char mac_len; + unsigned char mac[ULOG_MAC_LEN]; + unsigned char payload[0]; +} ulog_packet_msg_t; +#endif /** * @lock: lock to protect quota writers from each other @@ -33,33 +56,105 @@ struct xt_quota_counter { struct proc_dir_entry *procfs_entry; }; +#ifdef CONFIG_NETFILTER_XT_MATCH_QUOTA2_LOG +/* Harald's favorite number +1 :D From ipt_ULOG.C */ +static int qlog_nl_event = 112; +module_param_named(event_num, qlog_nl_event, uint, S_IRUGO | S_IWUSR); +MODULE_PARM_DESC(event_num, + "Event number for NETLINK_NFLOG message. 0 disables log." + "111 is what ipt_ULOG uses."); +static struct sock *nflognl; +#endif + static LIST_HEAD(counter_list); static DEFINE_SPINLOCK(counter_list_lock); static struct proc_dir_entry *proc_xt_quota; static unsigned int quota_list_perms = S_IRUGO | S_IWUSR; -static unsigned int quota_list_uid = 0; -static unsigned int quota_list_gid = 0; +static kuid_t quota_list_uid = KUIDT_INIT(0); +static kgid_t quota_list_gid = KGIDT_INIT(0); module_param_named(perms, quota_list_perms, uint, S_IRUGO | S_IWUSR); -module_param_named(uid, quota_list_uid, uint, S_IRUGO | S_IWUSR); -module_param_named(gid, quota_list_gid, uint, S_IRUGO | S_IWUSR); -static int quota_proc_read(char *page, char **start, off_t offset, - int count, int *eof, void *data) +#ifdef CONFIG_NETFILTER_XT_MATCH_QUOTA2_LOG +static void quota2_log(unsigned int hooknum, + const struct sk_buff *skb, + const struct net_device *in, + const struct net_device *out, + const char *prefix) { - struct xt_quota_counter *e = data; - int ret; + ulog_packet_msg_t *pm; + struct sk_buff *log_skb; + size_t size; + struct nlmsghdr *nlh; + + if (!qlog_nl_event) + return; + + size = NLMSG_SPACE(sizeof(*pm)); + size = max(size, (size_t)NLMSG_GOODSIZE); + log_skb = alloc_skb(size, GFP_ATOMIC); + if (!log_skb) { + pr_err("xt_quota2: cannot alloc skb for logging\n"); + return; + } + + nlh = nlmsg_put(log_skb, /*pid*/0, /*seq*/0, qlog_nl_event, + sizeof(*pm), 0); + if (!nlh) { + pr_err("xt_quota2: nlmsg_put failed\n"); + kfree_skb(log_skb); + return; + } + pm = nlmsg_data(nlh); + if (skb->tstamp == 0) + __net_timestamp((struct sk_buff *)skb); + pm->data_len = 0; + pm->hook = hooknum; + if (prefix != NULL) + strlcpy(pm->prefix, prefix, sizeof(pm->prefix)); + else + *(pm->prefix) = '\0'; + if (in) + strlcpy(pm->indev_name, in->name, sizeof(pm->indev_name)); + else + pm->indev_name[0] = '\0'; + + if (out) + strlcpy(pm->outdev_name, out->name, sizeof(pm->outdev_name)); + else + pm->outdev_name[0] = '\0'; + + NETLINK_CB(log_skb).dst_group = 1; + pr_debug("throwing 1 packets to netlink group 1\n"); + netlink_broadcast(nflognl, log_skb, 0, 1, GFP_ATOMIC); +} +#else +static void quota2_log(unsigned int hooknum, + const struct sk_buff *skb, + const struct net_device *in, + const struct net_device *out, + const char *prefix) +{ +} +#endif /* if+else CONFIG_NETFILTER_XT_MATCH_QUOTA2_LOG */ + +static ssize_t quota_proc_read(struct file *file, char __user *buf, + size_t size, loff_t *ppos) +{ + struct xt_quota_counter *e = PDE_DATA(file_inode(file)); + char tmp[24]; + size_t tmp_size; spin_lock_bh(&e->lock); - ret = snprintf(page, PAGE_SIZE, "%llu\n", e->quota); + tmp_size = scnprintf(tmp, sizeof(tmp), "%llu\n", e->quota); spin_unlock_bh(&e->lock); - return ret; + return simple_read_from_buffer(buf, size, ppos, tmp, tmp_size); } -static int quota_proc_write(struct file *file, const char __user *input, - unsigned long size, void *data) +static ssize_t quota_proc_write(struct file *file, const char __user *input, + size_t size, loff_t *ppos) { - struct xt_quota_counter *e = data; + struct xt_quota_counter *e = PDE_DATA(file_inode(file)); char buf[sizeof("18446744073709551616")]; if (size > sizeof(buf)) @@ -74,6 +169,12 @@ static int quota_proc_write(struct file *file, const char __user *input, return size; } +static const struct file_operations q2_counter_fops = { + .read = quota_proc_read, + .write = quota_proc_write, + .llseek = default_llseek, +}; + static struct xt_quota_counter * q2_new_counter(const struct xt_quota_mtinfo2 *q, bool anon) { @@ -91,7 +192,7 @@ q2_new_counter(const struct xt_quota_mtinfo2 *q, bool anon) if (!anon) { INIT_LIST_HEAD(&e->list); atomic_set(&e->ref, 1); - strncpy(e->name, q->name, sizeof(e->name)); + strlcpy(e->name, q->name, sizeof(e->name)); } return e; } @@ -104,42 +205,52 @@ static struct xt_quota_counter * q2_get_counter(const struct xt_quota_mtinfo2 *q) { struct proc_dir_entry *p; - struct xt_quota_counter *e; + struct xt_quota_counter *e = NULL; + struct xt_quota_counter *new_e; if (*q->name == '\0') return q2_new_counter(q, true); + /* No need to hold a lock while getting a new counter */ + new_e = q2_new_counter(q, false); + if (new_e == NULL) + goto out; + spin_lock_bh(&counter_list_lock); list_for_each_entry(e, &counter_list, list) if (strcmp(e->name, q->name) == 0) { atomic_inc(&e->ref); spin_unlock_bh(&counter_list_lock); + kfree(new_e); + pr_debug("xt_quota2: old counter name=%s", e->name); return e; } + e = new_e; + pr_debug("xt_quota2: new_counter name=%s", e->name); + list_add_tail(&e->list, &counter_list); + /* The entry having a refcount of 1 is not directly destructible. + * This func has not yet returned the new entry, thus iptables + * has not references for destroying this entry. + * For another rule to try to destroy it, it would 1st need for this + * func* to be re-invoked, acquire a new ref for the same named quota. + * Nobody will access the e->procfs_entry either. + * So release the lock. */ + spin_unlock_bh(&counter_list_lock); - e = q2_new_counter(q, false); - if (e == NULL) - goto out; + /* create_proc_entry() is not spin_lock happy */ + p = e->procfs_entry = proc_create_data(e->name, quota_list_perms, + proc_xt_quota, &q2_counter_fops, e); - p = e->procfs_entry = create_proc_entry(e->name, quota_list_perms, - proc_xt_quota); - if (p == NULL || IS_ERR(p)) + if (IS_ERR_OR_NULL(p)) { + spin_lock_bh(&counter_list_lock); + list_del(&e->list); + spin_unlock_bh(&counter_list_lock); goto out; - -#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 29) - p->owner = THIS_MODULE; -#endif - p->data = e; - p->read_proc = quota_proc_read; - p->write_proc = quota_proc_write; - p->uid = quota_list_uid; - p->gid = quota_list_gid; - list_add_tail(&e->list, &counter_list); - spin_unlock_bh(&counter_list_lock); + } + proc_set_user(p, quota_list_uid, quota_list_gid); return e; out: - spin_unlock_bh(&counter_list_lock); kfree(e); return NULL; } @@ -148,6 +259,8 @@ static int quota_mt2_check(const struct xt_mtchk_param *par) { struct xt_quota_mtinfo2 *q = par->matchinfo; + pr_debug("xt_quota2: check() flags=0x%04x", q->flags); + if (q->flags & ~XT_QUOTA_MASK) return -EINVAL; @@ -203,7 +316,6 @@ quota_mt2(const struct sk_buff *skb, struct xt_action_param *par) */ if (!(q->flags & XT_QUOTA_NO_CHANGE)) { e->quota += (q->flags & XT_QUOTA_PACKET) ? 1 : skb->len; - q->quota = e->quota; } ret = true; } else { @@ -212,10 +324,17 @@ quota_mt2(const struct sk_buff *skb, struct xt_action_param *par) e->quota -= (q->flags & XT_QUOTA_PACKET) ? 1 : skb->len; ret = !ret; } else { + /* We are transitioning, log that fact. */ + if (e->quota) { + quota2_log(xt_hooknum(par), + skb, + xt_in(par), + xt_out(par), + q->name); + } /* we do not allow even small packets from now on */ e->quota = 0; } - q->quota = e->quota; } spin_unlock_bh(&e->lock); return ret; @@ -228,7 +347,7 @@ static struct xt_match quota_mt2_reg[] __read_mostly = { .family = NFPROTO_IPV4, .checkentry = quota_mt2_check, .match = quota_mt2, - .destroy = quota_mt2_destroy, + .destroy = quota_mt2_destroy, .matchsize = sizeof(struct xt_quota_mtinfo2), .me = THIS_MODULE, }, @@ -238,7 +357,7 @@ static struct xt_match quota_mt2_reg[] __read_mostly = { .family = NFPROTO_IPV6, .checkentry = quota_mt2_check, .match = quota_mt2, - .destroy = quota_mt2_destroy, + .destroy = quota_mt2_destroy, .matchsize = sizeof(struct xt_quota_mtinfo2), .me = THIS_MODULE, }, @@ -247,21 +366,29 @@ static struct xt_match quota_mt2_reg[] __read_mostly = { static int __init quota_mt2_init(void) { int ret; + pr_debug("xt_quota2: init()"); + +#ifdef CONFIG_NETFILTER_XT_MATCH_QUOTA2_LOG + nflognl = netlink_kernel_create(&init_net, NETLINK_NFLOG, NULL); + if (!nflognl) + return -ENOMEM; +#endif - proc_xt_quota = proc_mkdir("xt_quota", init_net__proc_net); + proc_xt_quota = proc_mkdir("xt_quota", init_net.proc_net); if (proc_xt_quota == NULL) return -EACCES; ret = xt_register_matches(quota_mt2_reg, ARRAY_SIZE(quota_mt2_reg)); if (ret < 0) - remove_proc_entry("xt_quota", init_net__proc_net); + remove_proc_entry("xt_quota", init_net.proc_net); + pr_debug("xt_quota2: init() %d", ret); return ret; } static void __exit quota_mt2_exit(void) { xt_unregister_matches(quota_mt2_reg, ARRAY_SIZE(quota_mt2_reg)); - remove_proc_entry("xt_quota", init_net__proc_net); + remove_proc_entry("xt_quota", init_net.proc_net); } module_init(quota_mt2_init); From 4aae2b58a0217eca78b104b17d55e8792b71216c Mon Sep 17 00:00:00 2001 From: JP Abgrall Date: Mon, 20 Jun 2011 12:41:46 -0700 Subject: [PATCH 0184/1103] ANDROID: netfilter: xt_qtaguid: add qtaguid matching module This module allows tracking stats at the socket level for given UIDs. It replaces xt_owner. If the --uid-owner is not specified, it will just count stats based on who the skb belongs to. This will even happen on incoming skbs as it looks into the skb via xt_socket magic to see who owns it. If an skb is lost, it will be assigned to uid=0. To control what sockets of what UIDs are tagged by what, one uses: echo t $sock_fd $accounting_tag $the_billed_uid \ > /proc/net/xt_qtaguid/ctrl So whenever an skb belongs to a sock_fd, it will be accounted against $the_billed_uid and matching stats will show up under the uid with the given $accounting_tag. Because the number of allocations for the stats structs is not that big: ~500 apps * 32 per app we'll just do it atomic. This avoids walking lists many times, and the fancy worker thread handling. Slabs will grow when needed later. It use netdevice and inetaddr notifications instead of hooks in the core dev code to track when a device comes and goes. This removes the need for exposed iface_stat.h. Put procfs dirs in /proc/net/xt_qtaguid/ ctrl stats iface_stat//... The uid stats are obtainable in ./stats. Change-Id: I01af4fd91c8de651668d3decb76d9bdc1e343919 Signed-off-by: JP Abgrall [AmitP: Folded following android-4.9 commit changes into this patch e5d798684a71 ("ANDROID: netfilter: qtaguid: initialize a local var to keep compiler happy")] Signed-off-by: Amit Pundir --- include/linux/android_aid.h | 2 + include/linux/netfilter/xt_qtaguid.h | 13 + net/netfilter/Kconfig | 18 + net/netfilter/Makefile | 1 + net/netfilter/xt_qtaguid.c | 2796 ++++++++++++++++++++++++++ net/netfilter/xt_qtaguid_internal.h | 330 +++ net/netfilter/xt_qtaguid_print.c | 556 +++++ net/netfilter/xt_qtaguid_print.h | 120 ++ 8 files changed, 3836 insertions(+) create mode 100644 include/linux/netfilter/xt_qtaguid.h create mode 100644 net/netfilter/xt_qtaguid.c create mode 100644 net/netfilter/xt_qtaguid_internal.h create mode 100644 net/netfilter/xt_qtaguid_print.c create mode 100644 net/netfilter/xt_qtaguid_print.h diff --git a/include/linux/android_aid.h b/include/linux/android_aid.h index 3d7a5ead1200..6f1fa1792dfc 100644 --- a/include/linux/android_aid.h +++ b/include/linux/android_aid.h @@ -22,5 +22,7 @@ #define AID_INET KGIDT_INIT(3003) #define AID_NET_RAW KGIDT_INIT(3004) #define AID_NET_ADMIN KGIDT_INIT(3005) +#define AID_NET_BW_STATS KGIDT_INIT(3006) /* read bandwidth statistics */ +#define AID_NET_BW_ACCT KGIDT_INIT(3007) /* change bandwidth statistics accounting */ #endif diff --git a/include/linux/netfilter/xt_qtaguid.h b/include/linux/netfilter/xt_qtaguid.h new file mode 100644 index 000000000000..ca60fbdec2f3 --- /dev/null +++ b/include/linux/netfilter/xt_qtaguid.h @@ -0,0 +1,13 @@ +#ifndef _XT_QTAGUID_MATCH_H +#define _XT_QTAGUID_MATCH_H + +/* For now we just replace the xt_owner. + * FIXME: make iptables aware of qtaguid. */ +#include + +#define XT_QTAGUID_UID XT_OWNER_UID +#define XT_QTAGUID_GID XT_OWNER_GID +#define XT_QTAGUID_SOCKET XT_OWNER_SOCKET +#define xt_qtaguid_match_info xt_owner_match_info + +#endif /* _XT_QTAGUID_MATCH_H */ diff --git a/net/netfilter/Kconfig b/net/netfilter/Kconfig index 4bde51a5902e..1b371f6cdedb 100644 --- a/net/netfilter/Kconfig +++ b/net/netfilter/Kconfig @@ -1417,6 +1417,8 @@ config NETFILTER_XT_MATCH_OWNER based on who created the socket: the user or group. It is also possible to check whether a socket actually exists. + Conflicts with '"quota, tag, uid" match' + config NETFILTER_XT_MATCH_POLICY tristate 'IPsec "policy" match support' depends on XFRM @@ -1450,6 +1452,22 @@ config NETFILTER_XT_MATCH_PKTTYPE To compile it as a module, choose M here. If unsure, say N. +config NETFILTER_XT_MATCH_QTAGUID + bool '"quota, tag, owner" match and stats support' + depends on NETFILTER_XT_MATCH_SOCKET + depends on NETFILTER_XT_MATCH_OWNER=n + help + This option replaces the `owner' match. In addition to matching + on uid, it keeps stats based on a tag assigned to a socket. + The full tag is comprised of a UID and an accounting tag. + The tags are assignable to sockets from user space (e.g. a download + manager can assign the socket to another UID for accounting). + Stats and control are done via /proc/net/xt_qtaguid/. + It replaces owner as it takes the same arguments, but should + really be recognized by the iptables tool. + + If unsure, say `N'. + config NETFILTER_XT_MATCH_QUOTA tristate '"quota" match support' depends on NETFILTER_ADVANCED diff --git a/net/netfilter/Makefile b/net/netfilter/Makefile index 9c87ed55eba7..f2c701e55b1c 100644 --- a/net/netfilter/Makefile +++ b/net/netfilter/Makefile @@ -190,6 +190,7 @@ obj-$(CONFIG_NETFILTER_XT_MATCH_CGROUP) += xt_cgroup.o obj-$(CONFIG_NETFILTER_XT_MATCH_PHYSDEV) += xt_physdev.o obj-$(CONFIG_NETFILTER_XT_MATCH_PKTTYPE) += xt_pkttype.o obj-$(CONFIG_NETFILTER_XT_MATCH_POLICY) += xt_policy.o +obj-$(CONFIG_NETFILTER_XT_MATCH_QTAGUID) += xt_qtaguid_print.o xt_qtaguid.o obj-$(CONFIG_NETFILTER_XT_MATCH_QUOTA) += xt_quota.o obj-$(CONFIG_NETFILTER_XT_MATCH_QUOTA2) += xt_quota2.o obj-$(CONFIG_NETFILTER_XT_MATCH_RATEEST) += xt_rateest.o diff --git a/net/netfilter/xt_qtaguid.c b/net/netfilter/xt_qtaguid.c new file mode 100644 index 000000000000..3d3928291efb --- /dev/null +++ b/net/netfilter/xt_qtaguid.c @@ -0,0 +1,2796 @@ +/* + * Kernel iptables module to track stats for packets based on user tags. + * + * (C) 2011 Google, Inc + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +/* + * There are run-time debug flags enabled via the debug_mask module param, or + * via the DEFAULT_DEBUG_MASK. See xt_qtaguid_internal.h. + */ +#define DEBUG + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include "xt_qtaguid_internal.h" +#include "xt_qtaguid_print.h" + +/* + * We only use the xt_socket funcs within a similar context to avoid unexpected + * return values. + */ +#define XT_SOCKET_SUPPORTED_HOOKS \ + ((1 << NF_INET_PRE_ROUTING) | (1 << NF_INET_LOCAL_IN)) + + +static const char *module_procdirname = "xt_qtaguid"; +static struct proc_dir_entry *xt_qtaguid_procdir; + +static unsigned int proc_iface_perms = S_IRUGO; +module_param_named(iface_perms, proc_iface_perms, uint, S_IRUGO | S_IWUSR); + +static struct proc_dir_entry *xt_qtaguid_stats_file; +static unsigned int proc_stats_perms = S_IRUGO; +module_param_named(stats_perms, proc_stats_perms, uint, S_IRUGO | S_IWUSR); + +static struct proc_dir_entry *xt_qtaguid_ctrl_file; +#ifdef CONFIG_ANDROID_PARANOID_NETWORK +static unsigned int proc_ctrl_perms = S_IRUGO | S_IWUGO; +#else +static unsigned int proc_ctrl_perms = S_IRUGO | S_IWUSR; +#endif +module_param_named(ctrl_perms, proc_ctrl_perms, uint, S_IRUGO | S_IWUSR); + +#ifdef CONFIG_ANDROID_PARANOID_NETWORK +#include +static gid_t proc_stats_readall_gid = AID_NET_BW_STATS; +static gid_t proc_ctrl_write_gid = AID_NET_BW_ACCT; +#else +/* 0 means, don't limit anybody */ +static gid_t proc_stats_readall_gid; +static gid_t proc_ctrl_write_gid; +#endif +module_param_named(stats_readall_gid, proc_stats_readall_gid, uint, + S_IRUGO | S_IWUSR); +module_param_named(ctrl_write_gid, proc_ctrl_write_gid, uint, + S_IRUGO | S_IWUSR); + +/* + * Limit the number of active tags (via socket tags) for a given UID. + * Multiple processes could share the UID. + */ +static int max_sock_tags = DEFAULT_MAX_SOCK_TAGS; +module_param(max_sock_tags, int, S_IRUGO | S_IWUSR); + +/* + * After the kernel has initiallized this module, it is still possible + * to make it passive. + * Setting passive to Y: + * - the iface stats handling will not act on notifications. + * - iptables matches will never match. + * - ctrl commands silently succeed. + * - stats are always empty. + * This is mostly usefull when a bug is suspected. + */ +static bool module_passive; +module_param_named(passive, module_passive, bool, S_IRUGO | S_IWUSR); + +/* + * Control how qtaguid data is tracked per proc/uid. + * Setting tag_tracking_passive to Y: + * - don't create proc specific structs to track tags + * - don't check that active tag stats exceed some limits. + * - don't clean up socket tags on process exits. + * This is mostly usefull when a bug is suspected. + */ +static bool qtu_proc_handling_passive; +module_param_named(tag_tracking_passive, qtu_proc_handling_passive, bool, + S_IRUGO | S_IWUSR); + +#define QTU_DEV_NAME "xt_qtaguid" + +uint qtaguid_debug_mask = DEFAULT_DEBUG_MASK; +module_param_named(debug_mask, qtaguid_debug_mask, uint, S_IRUGO | S_IWUSR); + +/*---------------------------------------------------------------------------*/ +static const char *iface_stat_procdirname = "iface_stat"; +static struct proc_dir_entry *iface_stat_procdir; +static const char *iface_stat_all_procfilename = "iface_stat_all"; +static struct proc_dir_entry *iface_stat_all_procfile; + +/* + * Ordering of locks: + * outer locks: + * iface_stat_list_lock + * sock_tag_list_lock + * inner locks: + * uid_tag_data_tree_lock + * tag_counter_set_list_lock + * Notice how sock_tag_list_lock is held sometimes when uid_tag_data_tree_lock + * is acquired. + * + * Call tree with all lock holders as of 2011-09-25: + * + * iface_stat_all_proc_read() + * iface_stat_list_lock + * (struct iface_stat) + * + * qtaguid_ctrl_proc_read() + * sock_tag_list_lock + * (sock_tag_tree) + * (struct proc_qtu_data->sock_tag_list) + * prdebug_full_state() + * sock_tag_list_lock + * (sock_tag_tree) + * uid_tag_data_tree_lock + * (uid_tag_data_tree) + * (proc_qtu_data_tree) + * iface_stat_list_lock + * + * qtaguid_stats_proc_read() + * iface_stat_list_lock + * struct iface_stat->tag_stat_list_lock + * + * qtudev_open() + * uid_tag_data_tree_lock + * + * qtudev_release() + * sock_tag_data_list_lock + * uid_tag_data_tree_lock + * prdebug_full_state() + * sock_tag_list_lock + * uid_tag_data_tree_lock + * iface_stat_list_lock + * + * iface_netdev_event_handler() + * iface_stat_create() + * iface_stat_list_lock + * iface_stat_update() + * iface_stat_list_lock + * + * iface_inetaddr_event_handler() + * iface_stat_create() + * iface_stat_list_lock + * iface_stat_update() + * iface_stat_list_lock + * + * iface_inet6addr_event_handler() + * iface_stat_create_ipv6() + * iface_stat_list_lock + * iface_stat_update() + * iface_stat_list_lock + * + * qtaguid_mt() + * account_for_uid() + * if_tag_stat_update() + * get_sock_stat() + * sock_tag_list_lock + * struct iface_stat->tag_stat_list_lock + * tag_stat_update() + * get_active_counter_set() + * tag_counter_set_list_lock + * tag_stat_update() + * get_active_counter_set() + * tag_counter_set_list_lock + * + * + * qtaguid_ctrl_parse() + * ctrl_cmd_delete() + * sock_tag_list_lock + * tag_counter_set_list_lock + * iface_stat_list_lock + * struct iface_stat->tag_stat_list_lock + * uid_tag_data_tree_lock + * ctrl_cmd_counter_set() + * tag_counter_set_list_lock + * ctrl_cmd_tag() + * sock_tag_list_lock + * (sock_tag_tree) + * get_tag_ref() + * uid_tag_data_tree_lock + * (uid_tag_data_tree) + * uid_tag_data_tree_lock + * (proc_qtu_data_tree) + * ctrl_cmd_untag() + * sock_tag_list_lock + * uid_tag_data_tree_lock + * + */ +static LIST_HEAD(iface_stat_list); +static DEFINE_SPINLOCK(iface_stat_list_lock); + +static struct rb_root sock_tag_tree = RB_ROOT; +static DEFINE_SPINLOCK(sock_tag_list_lock); + +static struct rb_root tag_counter_set_tree = RB_ROOT; +static DEFINE_SPINLOCK(tag_counter_set_list_lock); + +static struct rb_root uid_tag_data_tree = RB_ROOT; +static DEFINE_SPINLOCK(uid_tag_data_tree_lock); + +static struct rb_root proc_qtu_data_tree = RB_ROOT; +/* No proc_qtu_data_tree_lock; use uid_tag_data_tree_lock */ + +static struct qtaguid_event_counts qtu_events; +/*----------------------------------------------*/ +static bool can_manipulate_uids(void) +{ + /* root pwnd */ + return unlikely(!current_fsuid()) || unlikely(!proc_ctrl_write_gid) + || in_egroup_p(proc_ctrl_write_gid); +} + +static bool can_impersonate_uid(uid_t uid) +{ + return uid == current_fsuid() || can_manipulate_uids(); +} + +static bool can_read_other_uid_stats(uid_t uid) +{ + /* root pwnd */ + return unlikely(!current_fsuid()) || uid == current_fsuid() + || unlikely(!proc_stats_readall_gid) + || in_egroup_p(proc_stats_readall_gid); +} + +static inline void dc_add_byte_packets(struct data_counters *counters, int set, + enum ifs_tx_rx direction, + enum ifs_proto ifs_proto, + int bytes, + int packets) +{ + counters->bpc[set][direction][ifs_proto].bytes += bytes; + counters->bpc[set][direction][ifs_proto].packets += packets; +} + +static inline uint64_t dc_sum_bytes(struct data_counters *counters, + int set, + enum ifs_tx_rx direction) +{ + return counters->bpc[set][direction][IFS_TCP].bytes + + counters->bpc[set][direction][IFS_UDP].bytes + + counters->bpc[set][direction][IFS_PROTO_OTHER].bytes; +} + +static inline uint64_t dc_sum_packets(struct data_counters *counters, + int set, + enum ifs_tx_rx direction) +{ + return counters->bpc[set][direction][IFS_TCP].packets + + counters->bpc[set][direction][IFS_UDP].packets + + counters->bpc[set][direction][IFS_PROTO_OTHER].packets; +} + +static struct tag_node *tag_node_tree_search(struct rb_root *root, tag_t tag) +{ + struct rb_node *node = root->rb_node; + + while (node) { + struct tag_node *data = rb_entry(node, struct tag_node, node); + int result; + RB_DEBUG("qtaguid: tag_node_tree_search(0x%llx): " + " node=%p data=%p\n", tag, node, data); + result = tag_compare(tag, data->tag); + RB_DEBUG("qtaguid: tag_node_tree_search(0x%llx): " + " data.tag=0x%llx (uid=%u) res=%d\n", + tag, data->tag, get_uid_from_tag(data->tag), result); + if (result < 0) + node = node->rb_left; + else if (result > 0) + node = node->rb_right; + else + return data; + } + return NULL; +} + +static void tag_node_tree_insert(struct tag_node *data, struct rb_root *root) +{ + struct rb_node **new = &(root->rb_node), *parent = NULL; + + /* Figure out where to put new node */ + while (*new) { + struct tag_node *this = rb_entry(*new, struct tag_node, + node); + int result = tag_compare(data->tag, this->tag); + RB_DEBUG("qtaguid: %s(): tag=0x%llx" + " (uid=%u)\n", __func__, + this->tag, + get_uid_from_tag(this->tag)); + parent = *new; + if (result < 0) + new = &((*new)->rb_left); + else if (result > 0) + new = &((*new)->rb_right); + else + BUG(); + } + + /* Add new node and rebalance tree. */ + rb_link_node(&data->node, parent, new); + rb_insert_color(&data->node, root); +} + +static void tag_stat_tree_insert(struct tag_stat *data, struct rb_root *root) +{ + tag_node_tree_insert(&data->tn, root); +} + +static struct tag_stat *tag_stat_tree_search(struct rb_root *root, tag_t tag) +{ + struct tag_node *node = tag_node_tree_search(root, tag); + if (!node) + return NULL; + return rb_entry(&node->node, struct tag_stat, tn.node); +} + +static void tag_counter_set_tree_insert(struct tag_counter_set *data, + struct rb_root *root) +{ + tag_node_tree_insert(&data->tn, root); +} + +static struct tag_counter_set *tag_counter_set_tree_search(struct rb_root *root, + tag_t tag) +{ + struct tag_node *node = tag_node_tree_search(root, tag); + if (!node) + return NULL; + return rb_entry(&node->node, struct tag_counter_set, tn.node); + +} + +static void tag_ref_tree_insert(struct tag_ref *data, struct rb_root *root) +{ + tag_node_tree_insert(&data->tn, root); +} + +static struct tag_ref *tag_ref_tree_search(struct rb_root *root, tag_t tag) +{ + struct tag_node *node = tag_node_tree_search(root, tag); + if (!node) + return NULL; + return rb_entry(&node->node, struct tag_ref, tn.node); +} + +static struct sock_tag *sock_tag_tree_search(struct rb_root *root, + const struct sock *sk) +{ + struct rb_node *node = root->rb_node; + + while (node) { + struct sock_tag *data = rb_entry(node, struct sock_tag, + sock_node); + if (sk < data->sk) + node = node->rb_left; + else if (sk > data->sk) + node = node->rb_right; + else + return data; + } + return NULL; +} + +static void sock_tag_tree_insert(struct sock_tag *data, struct rb_root *root) +{ + struct rb_node **new = &(root->rb_node), *parent = NULL; + + /* Figure out where to put new node */ + while (*new) { + struct sock_tag *this = rb_entry(*new, struct sock_tag, + sock_node); + parent = *new; + if (data->sk < this->sk) + new = &((*new)->rb_left); + else if (data->sk > this->sk) + new = &((*new)->rb_right); + else + BUG(); + } + + /* Add new node and rebalance tree. */ + rb_link_node(&data->sock_node, parent, new); + rb_insert_color(&data->sock_node, root); +} + +static void sock_tag_tree_erase(struct rb_root *st_to_free_tree) +{ + struct rb_node *node; + struct sock_tag *st_entry; + + node = rb_first(st_to_free_tree); + while (node) { + st_entry = rb_entry(node, struct sock_tag, sock_node); + node = rb_next(node); + CT_DEBUG("qtaguid: %s(): " + "erase st: sk=%p tag=0x%llx (uid=%u)\n", __func__, + st_entry->sk, + st_entry->tag, + get_uid_from_tag(st_entry->tag)); + rb_erase(&st_entry->sock_node, st_to_free_tree); + sockfd_put(st_entry->socket); + kfree(st_entry); + } +} + +static struct proc_qtu_data *proc_qtu_data_tree_search(struct rb_root *root, + const pid_t pid) +{ + struct rb_node *node = root->rb_node; + + while (node) { + struct proc_qtu_data *data = rb_entry(node, + struct proc_qtu_data, + node); + if (pid < data->pid) + node = node->rb_left; + else if (pid > data->pid) + node = node->rb_right; + else + return data; + } + return NULL; +} + +static void proc_qtu_data_tree_insert(struct proc_qtu_data *data, + struct rb_root *root) +{ + struct rb_node **new = &(root->rb_node), *parent = NULL; + + /* Figure out where to put new node */ + while (*new) { + struct proc_qtu_data *this = rb_entry(*new, + struct proc_qtu_data, + node); + parent = *new; + if (data->pid < this->pid) + new = &((*new)->rb_left); + else if (data->pid > this->pid) + new = &((*new)->rb_right); + else + BUG(); + } + + /* Add new node and rebalance tree. */ + rb_link_node(&data->node, parent, new); + rb_insert_color(&data->node, root); +} + +static void uid_tag_data_tree_insert(struct uid_tag_data *data, + struct rb_root *root) +{ + struct rb_node **new = &(root->rb_node), *parent = NULL; + + /* Figure out where to put new node */ + while (*new) { + struct uid_tag_data *this = rb_entry(*new, + struct uid_tag_data, + node); + parent = *new; + if (data->uid < this->uid) + new = &((*new)->rb_left); + else if (data->uid > this->uid) + new = &((*new)->rb_right); + else + BUG(); + } + + /* Add new node and rebalance tree. */ + rb_link_node(&data->node, parent, new); + rb_insert_color(&data->node, root); +} + +static struct uid_tag_data *uid_tag_data_tree_search(struct rb_root *root, + uid_t uid) +{ + struct rb_node *node = root->rb_node; + + while (node) { + struct uid_tag_data *data = rb_entry(node, + struct uid_tag_data, + node); + if (uid < data->uid) + node = node->rb_left; + else if (uid > data->uid) + node = node->rb_right; + else + return data; + } + return NULL; +} + +/* + * Allocates a new uid_tag_data struct if needed. + * Returns a pointer to the found or allocated uid_tag_data. + * Returns a PTR_ERR on failures, and lock is not held. + * If found is not NULL: + * sets *found to true if not allocated. + * sets *found to false if allocated. + */ +struct uid_tag_data *get_uid_data(uid_t uid, bool *found_res) +{ + struct uid_tag_data *utd_entry; + + /* Look for top level uid_tag_data for the UID */ + utd_entry = uid_tag_data_tree_search(&uid_tag_data_tree, uid); + DR_DEBUG("qtaguid: get_uid_data(%u) utd=%p\n", uid, utd_entry); + + if (found_res) + *found_res = utd_entry; + if (utd_entry) + return utd_entry; + + utd_entry = kzalloc(sizeof(*utd_entry), GFP_ATOMIC); + if (!utd_entry) { + pr_err("qtaguid: get_uid_data(%u): " + "tag data alloc failed\n", uid); + return ERR_PTR(-ENOMEM); + } + + utd_entry->uid = uid; + utd_entry->tag_ref_tree = RB_ROOT; + uid_tag_data_tree_insert(utd_entry, &uid_tag_data_tree); + DR_DEBUG("qtaguid: get_uid_data(%u) new utd=%p\n", uid, utd_entry); + return utd_entry; +} + +/* Never returns NULL. Either PTR_ERR or a valid ptr. */ +static struct tag_ref *new_tag_ref(tag_t new_tag, + struct uid_tag_data *utd_entry) +{ + struct tag_ref *tr_entry; + int res; + + if (utd_entry->num_active_tags + 1 > max_sock_tags) { + pr_info("qtaguid: new_tag_ref(0x%llx): " + "tag ref alloc quota exceeded. max=%d\n", + new_tag, max_sock_tags); + res = -EMFILE; + goto err_res; + + } + + tr_entry = kzalloc(sizeof(*tr_entry), GFP_ATOMIC); + if (!tr_entry) { + pr_err("qtaguid: new_tag_ref(0x%llx): " + "tag ref alloc failed\n", + new_tag); + res = -ENOMEM; + goto err_res; + } + tr_entry->tn.tag = new_tag; + /* tr_entry->num_sock_tags handled by caller */ + utd_entry->num_active_tags++; + tag_ref_tree_insert(tr_entry, &utd_entry->tag_ref_tree); + DR_DEBUG("qtaguid: new_tag_ref(0x%llx): " + " inserted new tag ref %p\n", + new_tag, tr_entry); + return tr_entry; + +err_res: + return ERR_PTR(res); +} + +static struct tag_ref *lookup_tag_ref(tag_t full_tag, + struct uid_tag_data **utd_res) +{ + struct uid_tag_data *utd_entry; + struct tag_ref *tr_entry; + bool found_utd; + uid_t uid = get_uid_from_tag(full_tag); + + DR_DEBUG("qtaguid: lookup_tag_ref(tag=0x%llx (uid=%u))\n", + full_tag, uid); + + utd_entry = get_uid_data(uid, &found_utd); + if (IS_ERR_OR_NULL(utd_entry)) { + if (utd_res) + *utd_res = utd_entry; + return NULL; + } + + tr_entry = tag_ref_tree_search(&utd_entry->tag_ref_tree, full_tag); + if (utd_res) + *utd_res = utd_entry; + DR_DEBUG("qtaguid: lookup_tag_ref(0x%llx) utd_entry=%p tr_entry=%p\n", + full_tag, utd_entry, tr_entry); + return tr_entry; +} + +/* Never returns NULL. Either PTR_ERR or a valid ptr. */ +static struct tag_ref *get_tag_ref(tag_t full_tag, + struct uid_tag_data **utd_res) +{ + struct uid_tag_data *utd_entry; + struct tag_ref *tr_entry; + + DR_DEBUG("qtaguid: get_tag_ref(0x%llx)\n", + full_tag); + spin_lock_bh(&uid_tag_data_tree_lock); + tr_entry = lookup_tag_ref(full_tag, &utd_entry); + BUG_ON(IS_ERR_OR_NULL(utd_entry)); + if (!tr_entry) + tr_entry = new_tag_ref(full_tag, utd_entry); + + spin_unlock_bh(&uid_tag_data_tree_lock); + if (utd_res) + *utd_res = utd_entry; + DR_DEBUG("qtaguid: get_tag_ref(0x%llx) utd=%p tr=%p\n", + full_tag, utd_entry, tr_entry); + return tr_entry; +} + +/* Checks and maybe frees the UID Tag Data entry */ +static void put_utd_entry(struct uid_tag_data *utd_entry) +{ + /* Are we done with the UID tag data entry? */ + if (RB_EMPTY_ROOT(&utd_entry->tag_ref_tree) && + !utd_entry->num_pqd) { + DR_DEBUG("qtaguid: %s(): " + "erase utd_entry=%p uid=%u " + "by pid=%u tgid=%u uid=%u\n", __func__, + utd_entry, utd_entry->uid, + current->pid, current->tgid, current_fsuid()); + BUG_ON(utd_entry->num_active_tags); + rb_erase(&utd_entry->node, &uid_tag_data_tree); + kfree(utd_entry); + } else { + DR_DEBUG("qtaguid: %s(): " + "utd_entry=%p still has %d tags %d proc_qtu_data\n", + __func__, utd_entry, utd_entry->num_active_tags, + utd_entry->num_pqd); + BUG_ON(!(utd_entry->num_active_tags || + utd_entry->num_pqd)); + } +} + +/* + * If no sock_tags are using this tag_ref, + * decrements refcount of utd_entry, removes tr_entry + * from utd_entry->tag_ref_tree and frees. + */ +static void free_tag_ref_from_utd_entry(struct tag_ref *tr_entry, + struct uid_tag_data *utd_entry) +{ + DR_DEBUG("qtaguid: %s(): %p tag=0x%llx (uid=%u)\n", __func__, + tr_entry, tr_entry->tn.tag, + get_uid_from_tag(tr_entry->tn.tag)); + if (!tr_entry->num_sock_tags) { + BUG_ON(!utd_entry->num_active_tags); + utd_entry->num_active_tags--; + rb_erase(&tr_entry->tn.node, &utd_entry->tag_ref_tree); + DR_DEBUG("qtaguid: %s(): erased %p\n", __func__, tr_entry); + kfree(tr_entry); + } +} + +static void put_tag_ref_tree(tag_t full_tag, struct uid_tag_data *utd_entry) +{ + struct rb_node *node; + struct tag_ref *tr_entry; + tag_t acct_tag; + + DR_DEBUG("qtaguid: %s(tag=0x%llx (uid=%u))\n", __func__, + full_tag, get_uid_from_tag(full_tag)); + acct_tag = get_atag_from_tag(full_tag); + node = rb_first(&utd_entry->tag_ref_tree); + while (node) { + tr_entry = rb_entry(node, struct tag_ref, tn.node); + node = rb_next(node); + if (!acct_tag || tr_entry->tn.tag == full_tag) + free_tag_ref_from_utd_entry(tr_entry, utd_entry); + } +} + +static int read_proc_u64(char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + int len; + uint64_t value; + char *p = page; + uint64_t *iface_entry = data; + + if (!data) + return 0; + + value = *iface_entry; + p += sprintf(p, "%llu\n", value); + len = (p - page) - off; + *eof = (len <= count) ? 1 : 0; + *start = page + off; + return len; +} + +static int read_proc_bool(char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + int len; + bool value; + char *p = page; + bool *bool_entry = data; + + if (!data) + return 0; + + value = *bool_entry; + p += sprintf(p, "%u\n", value); + len = (p - page) - off; + *eof = (len <= count) ? 1 : 0; + *start = page + off; + return len; +} + +static int get_active_counter_set(tag_t tag) +{ + int active_set = 0; + struct tag_counter_set *tcs; + + MT_DEBUG("qtaguid: get_active_counter_set(tag=0x%llx)" + " (uid=%u)\n", + tag, get_uid_from_tag(tag)); + /* For now we only handle UID tags for active sets */ + tag = get_utag_from_tag(tag); + spin_lock_bh(&tag_counter_set_list_lock); + tcs = tag_counter_set_tree_search(&tag_counter_set_tree, tag); + if (tcs) + active_set = tcs->active_set; + spin_unlock_bh(&tag_counter_set_list_lock); + return active_set; +} + +/* + * Find the entry for tracking the specified interface. + * Caller must hold iface_stat_list_lock + */ +static struct iface_stat *get_iface_entry(const char *ifname) +{ + struct iface_stat *iface_entry; + + /* Find the entry for tracking the specified tag within the interface */ + if (ifname == NULL) { + pr_info("qtaguid: iface_stat: get() NULL device name\n"); + return NULL; + } + + /* Iterate over interfaces */ + list_for_each_entry(iface_entry, &iface_stat_list, list) { + if (!strcmp(ifname, iface_entry->ifname)) + goto done; + } + iface_entry = NULL; +done: + return iface_entry; +} + +static int iface_stat_all_proc_read(char *page, char **num_items_returned, + off_t items_to_skip, int char_count, + int *eof, void *data) +{ + char *outp = page; + int item_index = 0; + int len; + struct iface_stat *iface_entry; + struct rtnl_link_stats64 dev_stats, *stats; + struct rtnl_link_stats64 no_dev_stats = {0}; + + if (unlikely(module_passive)) { + *eof = 1; + return 0; + } + + CT_DEBUG("qtaguid:proc iface_stat_all " + "page=%p *num_items_returned=%p off=%ld " + "char_count=%d *eof=%d\n", page, *num_items_returned, + items_to_skip, char_count, *eof); + + if (*eof) + return 0; + + /* + * This lock will prevent iface_stat_update() from changing active, + * and in turn prevent an interface from unregistering itself. + */ + spin_lock_bh(&iface_stat_list_lock); + list_for_each_entry(iface_entry, &iface_stat_list, list) { + if (item_index++ < items_to_skip) + continue; + + if (iface_entry->active) { + stats = dev_get_stats(iface_entry->net_dev, + &dev_stats); + } else { + stats = &no_dev_stats; + } + len = snprintf(outp, char_count, + "%s %d " + "%llu %llu %llu %llu " + "%llu %llu %llu %llu\n", + iface_entry->ifname, + iface_entry->active, + iface_entry->totals[IFS_RX].bytes, + iface_entry->totals[IFS_RX].packets, + iface_entry->totals[IFS_TX].bytes, + iface_entry->totals[IFS_TX].packets, + stats->rx_bytes, stats->rx_packets, + stats->tx_bytes, stats->tx_packets); + if (len >= char_count) { + spin_unlock_bh(&iface_stat_list_lock); + *outp = '\0'; + return outp - page; + } + outp += len; + char_count -= len; + (*num_items_returned)++; + } + spin_unlock_bh(&iface_stat_list_lock); + + *eof = 1; + return outp - page; +} + +static void iface_create_proc_worker(struct work_struct *work) +{ + struct proc_dir_entry *proc_entry; + struct iface_stat_work *isw = container_of(work, struct iface_stat_work, + iface_work); + struct iface_stat *new_iface = isw->iface_entry; + + /* iface_entries are not deleted, so safe to manipulate. */ + proc_entry = proc_mkdir(new_iface->ifname, iface_stat_procdir); + if (IS_ERR_OR_NULL(proc_entry)) { + pr_err("qtaguid: iface_stat: create_proc(): alloc failed.\n"); + kfree(isw); + return; + } + + new_iface->proc_ptr = proc_entry; + + create_proc_read_entry("tx_bytes", proc_iface_perms, proc_entry, + read_proc_u64, &new_iface->totals[IFS_TX].bytes); + create_proc_read_entry("rx_bytes", proc_iface_perms, proc_entry, + read_proc_u64, &new_iface->totals[IFS_RX].bytes); + create_proc_read_entry("tx_packets", proc_iface_perms, proc_entry, + read_proc_u64, &new_iface->totals[IFS_TX].packets); + create_proc_read_entry("rx_packets", proc_iface_perms, proc_entry, + read_proc_u64, &new_iface->totals[IFS_RX].packets); + create_proc_read_entry("active", proc_iface_perms, proc_entry, + read_proc_bool, &new_iface->active); + + IF_DEBUG("qtaguid: iface_stat: create_proc(): done " + "entry=%p dev=%s\n", new_iface, new_iface->ifname); + kfree(isw); +} + +/* + * Will set the entry's active state, and + * update the net_dev accordingly also. + */ +static void _iface_stat_set_active(struct iface_stat *entry, + struct net_device *net_dev, + bool activate) +{ + if (activate) { + entry->net_dev = net_dev; + entry->active = true; + IF_DEBUG("qtaguid: %s(%s): " + "enable tracking. rfcnt=%d\n", __func__, + entry->ifname, + __this_cpu_read(*net_dev->pcpu_refcnt)); + } else { + entry->active = false; + entry->net_dev = NULL; + IF_DEBUG("qtaguid: %s(%s): " + "disable tracking. rfcnt=%d\n", __func__, + entry->ifname, + __this_cpu_read(*net_dev->pcpu_refcnt)); + + } +} + +/* Caller must hold iface_stat_list_lock */ +static struct iface_stat *iface_alloc(struct net_device *net_dev) +{ + struct iface_stat *new_iface; + struct iface_stat_work *isw; + + new_iface = kzalloc(sizeof(*new_iface), GFP_ATOMIC); + if (new_iface == NULL) { + pr_err("qtaguid: iface_stat: create(%s): " + "iface_stat alloc failed\n", net_dev->name); + return NULL; + } + new_iface->ifname = kstrdup(net_dev->name, GFP_ATOMIC); + if (new_iface->ifname == NULL) { + pr_err("qtaguid: iface_stat: create(%s): " + "ifname alloc failed\n", net_dev->name); + kfree(new_iface); + return NULL; + } + spin_lock_init(&new_iface->tag_stat_list_lock); + new_iface->tag_stat_tree = RB_ROOT; + _iface_stat_set_active(new_iface, net_dev, true); + + /* + * ipv6 notifier chains are atomic :( + * No create_proc_read_entry() for you! + */ + isw = kmalloc(sizeof(*isw), GFP_ATOMIC); + if (!isw) { + pr_err("qtaguid: iface_stat: create(%s): " + "work alloc failed\n", new_iface->ifname); + _iface_stat_set_active(new_iface, net_dev, false); + kfree(new_iface->ifname); + kfree(new_iface); + return NULL; + } + isw->iface_entry = new_iface; + INIT_WORK(&isw->iface_work, iface_create_proc_worker); + schedule_work(&isw->iface_work); + list_add(&new_iface->list, &iface_stat_list); + return new_iface; +} + +static void iface_check_stats_reset_and_adjust(struct net_device *net_dev, + struct iface_stat *iface) +{ + struct rtnl_link_stats64 dev_stats, *stats; + bool stats_rewound; + + stats = dev_get_stats(net_dev, &dev_stats); + /* No empty packets */ + stats_rewound = + (stats->rx_bytes < iface->last_known[IFS_RX].bytes) + || (stats->tx_bytes < iface->last_known[IFS_TX].bytes); + + IF_DEBUG("qtaguid: %s(%s): iface=%p netdev=%p " + "bytes rx/tx=%llu/%llu " + "active=%d last_known=%d " + "stats_rewound=%d\n", __func__, + net_dev ? net_dev->name : "?", + iface, net_dev, + stats->rx_bytes, stats->tx_bytes, + iface->active, iface->last_known_valid, stats_rewound); + + if (iface->active && iface->last_known_valid && stats_rewound) { + pr_warn_once("qtaguid: iface_stat: %s(%s): " + "iface reset its stats unexpectedly\n", __func__, + net_dev->name); + + iface->totals[IFS_TX].bytes += iface->last_known[IFS_TX].bytes; + iface->totals[IFS_TX].packets += + iface->last_known[IFS_TX].packets; + iface->totals[IFS_RX].bytes += iface->last_known[IFS_RX].bytes; + iface->totals[IFS_RX].packets += + iface->last_known[IFS_RX].packets; + iface->last_known_valid = false; + IF_DEBUG("qtaguid: %s(%s): iface=%p " + "used last known bytes rx/tx=%llu/%llu\n", __func__, + iface->ifname, iface, iface->last_known[IFS_RX].bytes, + iface->last_known[IFS_TX].bytes); + } +} + +/* + * Create a new entry for tracking the specified interface. + * Do nothing if the entry already exists. + * Called when an interface is configured with a valid IP address. + */ +static void iface_stat_create(struct net_device *net_dev, + struct in_ifaddr *ifa) +{ + struct in_device *in_dev = NULL; + const char *ifname; + struct iface_stat *entry; + __be32 ipaddr = 0; + struct iface_stat *new_iface; + + IF_DEBUG("qtaguid: iface_stat: create(%s): ifa=%p netdev=%p\n", + net_dev ? net_dev->name : "?", + ifa, net_dev); + if (!net_dev) { + pr_err("qtaguid: iface_stat: create(): no net dev\n"); + return; + } + + ifname = net_dev->name; + if (!ifa) { + in_dev = in_dev_get(net_dev); + if (!in_dev) { + pr_err("qtaguid: iface_stat: create(%s): no inet dev\n", + ifname); + return; + } + IF_DEBUG("qtaguid: iface_stat: create(%s): in_dev=%p\n", + ifname, in_dev); + for (ifa = in_dev->ifa_list; ifa; ifa = ifa->ifa_next) { + IF_DEBUG("qtaguid: iface_stat: create(%s): " + "ifa=%p ifa_label=%s\n", + ifname, ifa, + ifa->ifa_label ? ifa->ifa_label : "(null)"); + if (ifa->ifa_label && !strcmp(ifname, ifa->ifa_label)) + break; + } + } + + if (!ifa) { + IF_DEBUG("qtaguid: iface_stat: create(%s): no matching IP\n", + ifname); + goto done_put; + } + ipaddr = ifa->ifa_local; + + spin_lock_bh(&iface_stat_list_lock); + entry = get_iface_entry(ifname); + if (entry != NULL) { + bool activate = !ipv4_is_loopback(ipaddr); + IF_DEBUG("qtaguid: iface_stat: create(%s): entry=%p\n", + ifname, entry); + iface_check_stats_reset_and_adjust(net_dev, entry); + _iface_stat_set_active(entry, net_dev, activate); + IF_DEBUG("qtaguid: %s(%s): " + "tracking now %d on ip=%pI4\n", __func__, + entry->ifname, activate, &ipaddr); + goto done_unlock_put; + } else if (ipv4_is_loopback(ipaddr)) { + IF_DEBUG("qtaguid: iface_stat: create(%s): " + "ignore loopback dev. ip=%pI4\n", ifname, &ipaddr); + goto done_unlock_put; + } + + new_iface = iface_alloc(net_dev); + IF_DEBUG("qtaguid: iface_stat: create(%s): done " + "entry=%p ip=%pI4\n", ifname, new_iface, &ipaddr); +done_unlock_put: + spin_unlock_bh(&iface_stat_list_lock); +done_put: + if (in_dev) + in_dev_put(in_dev); +} + +static void iface_stat_create_ipv6(struct net_device *net_dev, + struct inet6_ifaddr *ifa) +{ + struct in_device *in_dev; + const char *ifname; + struct iface_stat *entry; + struct iface_stat *new_iface; + int addr_type; + + IF_DEBUG("qtaguid: iface_stat: create6(): ifa=%p netdev=%p->name=%s\n", + ifa, net_dev, net_dev ? net_dev->name : ""); + if (!net_dev) { + pr_err("qtaguid: iface_stat: create6(): no net dev!\n"); + return; + } + ifname = net_dev->name; + + in_dev = in_dev_get(net_dev); + if (!in_dev) { + pr_err("qtaguid: iface_stat: create6(%s): no inet dev\n", + ifname); + return; + } + + IF_DEBUG("qtaguid: iface_stat: create6(%s): in_dev=%p\n", + ifname, in_dev); + + if (!ifa) { + IF_DEBUG("qtaguid: iface_stat: create6(%s): no matching IP\n", + ifname); + goto done_put; + } + addr_type = ipv6_addr_type(&ifa->addr); + + spin_lock_bh(&iface_stat_list_lock); + entry = get_iface_entry(ifname); + if (entry != NULL) { + bool activate = !(addr_type & IPV6_ADDR_LOOPBACK); + IF_DEBUG("qtaguid: %s(%s): entry=%p\n", __func__, + ifname, entry); + iface_check_stats_reset_and_adjust(net_dev, entry); + _iface_stat_set_active(entry, net_dev, activate); + IF_DEBUG("qtaguid: %s(%s): " + "tracking now %d on ip=%pI6c\n", __func__, + entry->ifname, activate, &ifa->addr); + goto done_unlock_put; + } else if (addr_type & IPV6_ADDR_LOOPBACK) { + IF_DEBUG("qtaguid: %s(%s): " + "ignore loopback dev. ip=%pI6c\n", __func__, + ifname, &ifa->addr); + goto done_unlock_put; + } + + new_iface = iface_alloc(net_dev); + IF_DEBUG("qtaguid: iface_stat: create6(%s): done " + "entry=%p ip=%pI6c\n", ifname, new_iface, &ifa->addr); + +done_unlock_put: + spin_unlock_bh(&iface_stat_list_lock); +done_put: + in_dev_put(in_dev); +} + +static struct sock_tag *get_sock_stat_nl(const struct sock *sk) +{ + MT_DEBUG("qtaguid: get_sock_stat_nl(sk=%p)\n", sk); + return sock_tag_tree_search(&sock_tag_tree, sk); +} + +static struct sock_tag *get_sock_stat(const struct sock *sk) +{ + struct sock_tag *sock_tag_entry; + MT_DEBUG("qtaguid: get_sock_stat(sk=%p)\n", sk); + if (!sk) + return NULL; + spin_lock_bh(&sock_tag_list_lock); + sock_tag_entry = get_sock_stat_nl(sk); + spin_unlock_bh(&sock_tag_list_lock); + return sock_tag_entry; +} + +static void +data_counters_update(struct data_counters *dc, int set, + enum ifs_tx_rx direction, int proto, int bytes) +{ + switch (proto) { + case IPPROTO_TCP: + dc_add_byte_packets(dc, set, direction, IFS_TCP, bytes, 1); + break; + case IPPROTO_UDP: + dc_add_byte_packets(dc, set, direction, IFS_UDP, bytes, 1); + break; + case IPPROTO_IP: + default: + dc_add_byte_packets(dc, set, direction, IFS_PROTO_OTHER, bytes, + 1); + break; + } +} + +/* + * Update stats for the specified interface. Do nothing if the entry + * does not exist (when a device was never configured with an IP address). + * Called when an device is being unregistered. + */ +static void iface_stat_update(struct net_device *net_dev, bool stash_only) +{ + struct rtnl_link_stats64 dev_stats, *stats; + struct iface_stat *entry; + + stats = dev_get_stats(net_dev, &dev_stats); + spin_lock_bh(&iface_stat_list_lock); + entry = get_iface_entry(net_dev->name); + if (entry == NULL) { + IF_DEBUG("qtaguid: iface_stat: update(%s): not tracked\n", + net_dev->name); + spin_unlock_bh(&iface_stat_list_lock); + return; + } + + IF_DEBUG("qtaguid: %s(%s): entry=%p\n", __func__, + net_dev->name, entry); + if (!entry->active) { + IF_DEBUG("qtaguid: %s(%s): already disabled\n", __func__, + net_dev->name); + spin_unlock_bh(&iface_stat_list_lock); + return; + } + + if (stash_only) { + entry->last_known[IFS_TX].bytes = stats->tx_bytes; + entry->last_known[IFS_TX].packets = stats->tx_packets; + entry->last_known[IFS_RX].bytes = stats->rx_bytes; + entry->last_known[IFS_RX].packets = stats->rx_packets; + entry->last_known_valid = true; + IF_DEBUG("qtaguid: %s(%s): " + "dev stats stashed rx/tx=%llu/%llu\n", __func__, + net_dev->name, stats->rx_bytes, stats->tx_bytes); + spin_unlock_bh(&iface_stat_list_lock); + return; + } + entry->totals[IFS_TX].bytes += stats->tx_bytes; + entry->totals[IFS_TX].packets += stats->tx_packets; + entry->totals[IFS_RX].bytes += stats->rx_bytes; + entry->totals[IFS_RX].packets += stats->rx_packets; + /* We don't need the last_known[] anymore */ + entry->last_known_valid = false; + _iface_stat_set_active(entry, net_dev, false); + IF_DEBUG("qtaguid: %s(%s): " + "disable tracking. rx/tx=%llu/%llu\n", __func__, + net_dev->name, stats->rx_bytes, stats->tx_bytes); + spin_unlock_bh(&iface_stat_list_lock); +} + +static void tag_stat_update(struct tag_stat *tag_entry, + enum ifs_tx_rx direction, int proto, int bytes) +{ + int active_set; + active_set = get_active_counter_set(tag_entry->tn.tag); + MT_DEBUG("qtaguid: tag_stat_update(tag=0x%llx (uid=%u) set=%d " + "dir=%d proto=%d bytes=%d)\n", + tag_entry->tn.tag, get_uid_from_tag(tag_entry->tn.tag), + active_set, direction, proto, bytes); + data_counters_update(&tag_entry->counters, active_set, direction, + proto, bytes); + if (tag_entry->parent_counters) + data_counters_update(tag_entry->parent_counters, active_set, + direction, proto, bytes); +} + +/* + * Create a new entry for tracking the specified {acct_tag,uid_tag} within + * the interface. + * iface_entry->tag_stat_list_lock should be held. + */ +static struct tag_stat *create_if_tag_stat(struct iface_stat *iface_entry, + tag_t tag) +{ + struct tag_stat *new_tag_stat_entry = NULL; + IF_DEBUG("qtaguid: iface_stat: %s(): ife=%p tag=0x%llx" + " (uid=%u)\n", __func__, + iface_entry, tag, get_uid_from_tag(tag)); + new_tag_stat_entry = kzalloc(sizeof(*new_tag_stat_entry), GFP_ATOMIC); + if (!new_tag_stat_entry) { + pr_err("qtaguid: iface_stat: tag stat alloc failed\n"); + goto done; + } + new_tag_stat_entry->tn.tag = tag; + tag_stat_tree_insert(new_tag_stat_entry, &iface_entry->tag_stat_tree); +done: + return new_tag_stat_entry; +} + +static void if_tag_stat_update(const char *ifname, uid_t uid, + const struct sock *sk, enum ifs_tx_rx direction, + int proto, int bytes) +{ + struct tag_stat *tag_stat_entry; + tag_t tag, acct_tag; + tag_t uid_tag; + struct data_counters *uid_tag_counters; + struct sock_tag *sock_tag_entry; + struct iface_stat *iface_entry; + struct tag_stat *new_tag_stat = NULL; + MT_DEBUG("qtaguid: if_tag_stat_update(ifname=%s " + "uid=%u sk=%p dir=%d proto=%d bytes=%d)\n", + ifname, uid, sk, direction, proto, bytes); + + + iface_entry = get_iface_entry(ifname); + if (!iface_entry) { + pr_err("qtaguid: iface_stat: stat_update() %s not found\n", + ifname); + return; + } + /* It is ok to process data when an iface_entry is inactive */ + + MT_DEBUG("qtaguid: iface_stat: stat_update() dev=%s entry=%p\n", + ifname, iface_entry); + + /* + * Look for a tagged sock. + * It will have an acct_uid. + */ + sock_tag_entry = get_sock_stat(sk); + if (sock_tag_entry) { + tag = sock_tag_entry->tag; + acct_tag = get_atag_from_tag(tag); + uid_tag = get_utag_from_tag(tag); + } else { + acct_tag = make_atag_from_value(0); + tag = combine_atag_with_uid(acct_tag, uid); + uid_tag = make_tag_from_uid(uid); + } + MT_DEBUG("qtaguid: iface_stat: stat_update(): " + " looking for tag=0x%llx (uid=%u) in ife=%p\n", + tag, get_uid_from_tag(tag), iface_entry); + /* Loop over tag list under this interface for {acct_tag,uid_tag} */ + spin_lock_bh(&iface_entry->tag_stat_list_lock); + + tag_stat_entry = tag_stat_tree_search(&iface_entry->tag_stat_tree, + tag); + if (tag_stat_entry) { + /* + * Updating the {acct_tag, uid_tag} entry handles both stats: + * {0, uid_tag} will also get updated. + */ + tag_stat_update(tag_stat_entry, direction, proto, bytes); + spin_unlock_bh(&iface_entry->tag_stat_list_lock); + return; + } + + /* Loop over tag list under this interface for {0,uid_tag} */ + tag_stat_entry = tag_stat_tree_search(&iface_entry->tag_stat_tree, + uid_tag); + if (!tag_stat_entry) { + /* Here: the base uid_tag did not exist */ + /* + * No parent counters. So + * - No {0, uid_tag} stats and no {acc_tag, uid_tag} stats. + */ + new_tag_stat = create_if_tag_stat(iface_entry, uid_tag); + uid_tag_counters = &new_tag_stat->counters; + } else { + uid_tag_counters = &tag_stat_entry->counters; + } + + if (acct_tag) { + /* Create the child {acct_tag, uid_tag} and hook up parent. */ + new_tag_stat = create_if_tag_stat(iface_entry, tag); + new_tag_stat->parent_counters = uid_tag_counters; + } else { + /* + * For new_tag_stat to be still NULL here would require: + * {0, uid_tag} exists + * and {acct_tag, uid_tag} doesn't exist + * AND acct_tag == 0. + * Impossible. This reassures us that new_tag_stat + * below will always be assigned. + */ + BUG_ON(!new_tag_stat); + } + tag_stat_update(new_tag_stat, direction, proto, bytes); + spin_unlock_bh(&iface_entry->tag_stat_list_lock); +} + +static int iface_netdev_event_handler(struct notifier_block *nb, + unsigned long event, void *ptr) { + struct net_device *dev = ptr; + + if (unlikely(module_passive)) + return NOTIFY_DONE; + + IF_DEBUG("qtaguid: iface_stat: netdev_event(): " + "ev=0x%lx/%s netdev=%p->name=%s\n", + event, netdev_evt_str(event), dev, dev ? dev->name : ""); + + switch (event) { + case NETDEV_UP: + iface_stat_create(dev, NULL); + atomic64_inc(&qtu_events.iface_events); + break; + case NETDEV_DOWN: + case NETDEV_UNREGISTER: + iface_stat_update(dev, event == NETDEV_DOWN); + atomic64_inc(&qtu_events.iface_events); + break; + } + return NOTIFY_DONE; +} + +static int iface_inet6addr_event_handler(struct notifier_block *nb, + unsigned long event, void *ptr) +{ + struct inet6_ifaddr *ifa = ptr; + struct net_device *dev; + + if (unlikely(module_passive)) + return NOTIFY_DONE; + + IF_DEBUG("qtaguid: iface_stat: inet6addr_event(): " + "ev=0x%lx/%s ifa=%p\n", + event, netdev_evt_str(event), ifa); + + switch (event) { + case NETDEV_UP: + BUG_ON(!ifa || !ifa->idev); + dev = (struct net_device *)ifa->idev->dev; + iface_stat_create_ipv6(dev, ifa); + atomic64_inc(&qtu_events.iface_events); + break; + case NETDEV_DOWN: + case NETDEV_UNREGISTER: + BUG_ON(!ifa || !ifa->idev); + dev = (struct net_device *)ifa->idev->dev; + iface_stat_update(dev, event == NETDEV_DOWN); + atomic64_inc(&qtu_events.iface_events); + break; + } + return NOTIFY_DONE; +} + +static int iface_inetaddr_event_handler(struct notifier_block *nb, + unsigned long event, void *ptr) +{ + struct in_ifaddr *ifa = ptr; + struct net_device *dev; + + if (unlikely(module_passive)) + return NOTIFY_DONE; + + IF_DEBUG("qtaguid: iface_stat: inetaddr_event(): " + "ev=0x%lx/%s ifa=%p\n", + event, netdev_evt_str(event), ifa); + + switch (event) { + case NETDEV_UP: + BUG_ON(!ifa || !ifa->ifa_dev); + dev = ifa->ifa_dev->dev; + iface_stat_create(dev, ifa); + atomic64_inc(&qtu_events.iface_events); + break; + case NETDEV_DOWN: + case NETDEV_UNREGISTER: + BUG_ON(!ifa || !ifa->ifa_dev); + dev = ifa->ifa_dev->dev; + iface_stat_update(dev, event == NETDEV_DOWN); + atomic64_inc(&qtu_events.iface_events); + break; + } + return NOTIFY_DONE; +} + +static struct notifier_block iface_netdev_notifier_blk = { + .notifier_call = iface_netdev_event_handler, +}; + +static struct notifier_block iface_inetaddr_notifier_blk = { + .notifier_call = iface_inetaddr_event_handler, +}; + +static struct notifier_block iface_inet6addr_notifier_blk = { + .notifier_call = iface_inet6addr_event_handler, +}; + +static int __init iface_stat_init(struct proc_dir_entry *parent_procdir) +{ + int err; + + iface_stat_procdir = proc_mkdir(iface_stat_procdirname, parent_procdir); + if (!iface_stat_procdir) { + pr_err("qtaguid: iface_stat: init failed to create proc entry\n"); + err = -1; + goto err; + } + + iface_stat_all_procfile = create_proc_entry(iface_stat_all_procfilename, + proc_iface_perms, + parent_procdir); + if (!iface_stat_all_procfile) { + pr_err("qtaguid: iface_stat: init " + " failed to create stat_all proc entry\n"); + err = -1; + goto err_zap_entry; + } + iface_stat_all_procfile->read_proc = iface_stat_all_proc_read; + + + err = register_netdevice_notifier(&iface_netdev_notifier_blk); + if (err) { + pr_err("qtaguid: iface_stat: init " + "failed to register dev event handler\n"); + goto err_zap_all_stats_entry; + } + err = register_inetaddr_notifier(&iface_inetaddr_notifier_blk); + if (err) { + pr_err("qtaguid: iface_stat: init " + "failed to register ipv4 dev event handler\n"); + goto err_unreg_nd; + } + + err = register_inet6addr_notifier(&iface_inet6addr_notifier_blk); + if (err) { + pr_err("qtaguid: iface_stat: init " + "failed to register ipv6 dev event handler\n"); + goto err_unreg_ip4_addr; + } + return 0; + +err_unreg_ip4_addr: + unregister_inetaddr_notifier(&iface_inetaddr_notifier_blk); +err_unreg_nd: + unregister_netdevice_notifier(&iface_netdev_notifier_blk); +err_zap_all_stats_entry: + remove_proc_entry(iface_stat_all_procfilename, parent_procdir); +err_zap_entry: + remove_proc_entry(iface_stat_procdirname, parent_procdir); +err: + return err; +} + +static struct sock *qtaguid_find_sk(const struct sk_buff *skb, + struct xt_action_param *par) +{ + struct sock *sk; + unsigned int hook_mask = (1 << par->hooknum); + + MT_DEBUG("qtaguid: find_sk(skb=%p) hooknum=%d family=%d\n", skb, + par->hooknum, par->family); + + /* + * Let's not abuse the the xt_socket_get*_sk(), or else it will + * return garbage SKs. + */ + if (!(hook_mask & XT_SOCKET_SUPPORTED_HOOKS)) + return NULL; + + switch (par->family) { + case NFPROTO_IPV6: + sk = xt_socket_get6_sk(skb, par); + break; + case NFPROTO_IPV4: + sk = xt_socket_get4_sk(skb, par); + break; + default: + return NULL; + } + + /* + * Seems to be issues on the file ptr for TCP_TIME_WAIT SKs. + * http://kerneltrap.org/mailarchive/linux-netdev/2010/10/21/6287959 + * Not fixed in 3.0-r3 :( + */ + if (sk) { + MT_DEBUG("qtaguid: %p->sk_proto=%u " + "->sk_state=%d\n", sk, sk->sk_protocol, sk->sk_state); + if (sk->sk_state == TCP_TIME_WAIT) { + xt_socket_put_sk(sk); + sk = NULL; + } + } + return sk; +} + +static void account_for_uid(const struct sk_buff *skb, + const struct sock *alternate_sk, uid_t uid, + struct xt_action_param *par) +{ + const struct net_device *el_dev; + + if (!skb->dev) { + MT_DEBUG("qtaguid[%d]: no skb->dev\n", par->hooknum); + el_dev = par->in ? : par->out; + } else { + const struct net_device *other_dev; + el_dev = skb->dev; + other_dev = par->in ? : par->out; + if (el_dev != other_dev) { + MT_DEBUG("qtaguid[%d]: skb->dev=%p %s vs " + "par->(in/out)=%p %s\n", + par->hooknum, el_dev, el_dev->name, other_dev, + other_dev->name); + } + } + + if (unlikely(!el_dev)) { + pr_info("qtaguid[%d]: no par->in/out?!!\n", par->hooknum); + } else if (unlikely(!el_dev->name)) { + pr_info("qtaguid[%d]: no dev->name?!!\n", par->hooknum); + } else { + MT_DEBUG("qtaguid[%d]: dev name=%s type=%d\n", + par->hooknum, + el_dev->name, + el_dev->type); + + if_tag_stat_update(el_dev->name, uid, + skb->sk ? skb->sk : alternate_sk, + par->in ? IFS_RX : IFS_TX, + ip_hdr(skb)->protocol, skb->len); + } +} + +static bool qtaguid_mt(const struct sk_buff *skb, struct xt_action_param *par) +{ + const struct xt_qtaguid_match_info *info = par->matchinfo; + const struct file *filp; + bool got_sock = false; + struct sock *sk; + uid_t sock_uid; + bool res; + + if (unlikely(module_passive)) + return (info->match ^ info->invert) == 0; + + MT_DEBUG("qtaguid[%d]: entered skb=%p par->in=%p/out=%p fam=%d\n", + par->hooknum, skb, par->in, par->out, par->family); + + atomic64_inc(&qtu_events.match_calls); + if (skb == NULL) { + res = (info->match ^ info->invert) == 0; + goto ret_res; + } + + sk = skb->sk; + + if (sk == NULL) { + /* + * A missing sk->sk_socket happens when packets are in-flight + * and the matching socket is already closed and gone. + */ + sk = qtaguid_find_sk(skb, par); + /* + * If we got the socket from the find_sk(), we will need to put + * it back, as nf_tproxy_get_sock_v4() got it. + */ + got_sock = sk; + if (sk) + atomic64_inc(&qtu_events.match_found_sk_in_ct); + else + atomic64_inc(&qtu_events.match_found_no_sk_in_ct); + } else { + atomic64_inc(&qtu_events.match_found_sk); + } + MT_DEBUG("qtaguid[%d]: sk=%p got_sock=%d proto=%d\n", + par->hooknum, sk, got_sock, ip_hdr(skb)->protocol); + if (sk != NULL) { + MT_DEBUG("qtaguid[%d]: sk=%p->sk_socket=%p->file=%p\n", + par->hooknum, sk, sk->sk_socket, + sk->sk_socket ? sk->sk_socket->file : (void *)-1LL); + filp = sk->sk_socket ? sk->sk_socket->file : NULL; + MT_DEBUG("qtaguid[%d]: filp...uid=%u\n", + par->hooknum, filp ? filp->f_cred->fsuid : -1); + } + + if (sk == NULL || sk->sk_socket == NULL) { + /* + * Here, the qtaguid_find_sk() using connection tracking + * couldn't find the owner, so for now we just count them + * against the system. + */ + /* + * TODO: unhack how to force just accounting. + * For now we only do iface stats when the uid-owner is not + * requested. + */ + if (!(info->match & XT_QTAGUID_UID)) + account_for_uid(skb, sk, 0, par); + MT_DEBUG("qtaguid[%d]: leaving (sk?sk->sk_socket)=%p\n", + par->hooknum, + sk ? sk->sk_socket : NULL); + res = (info->match ^ info->invert) == 0; + atomic64_inc(&qtu_events.match_no_sk); + goto put_sock_ret_res; + } else if (info->match & info->invert & XT_QTAGUID_SOCKET) { + res = false; + goto put_sock_ret_res; + } + filp = sk->sk_socket->file; + if (filp == NULL) { + MT_DEBUG("qtaguid[%d]: leaving filp=NULL\n", par->hooknum); + account_for_uid(skb, sk, 0, par); + res = ((info->match ^ info->invert) & + (XT_QTAGUID_UID | XT_QTAGUID_GID)) == 0; + atomic64_inc(&qtu_events.match_no_sk_file); + goto put_sock_ret_res; + } + sock_uid = filp->f_cred->fsuid; + /* + * TODO: unhack how to force just accounting. + * For now we only do iface stats when the uid-owner is not requested + */ + if (!(info->match & XT_QTAGUID_UID)) + account_for_uid(skb, sk, sock_uid, par); + + /* + * The following two tests fail the match when: + * id not in range AND no inverted condition requested + * or id in range AND inverted condition requested + * Thus (!a && b) || (a && !b) == a ^ b + */ + if (info->match & XT_QTAGUID_UID) + if ((filp->f_cred->fsuid >= info->uid_min && + filp->f_cred->fsuid <= info->uid_max) ^ + !(info->invert & XT_QTAGUID_UID)) { + MT_DEBUG("qtaguid[%d]: leaving uid not matching\n", + par->hooknum); + res = false; + goto put_sock_ret_res; + } + if (info->match & XT_QTAGUID_GID) + if ((filp->f_cred->fsgid >= info->gid_min && + filp->f_cred->fsgid <= info->gid_max) ^ + !(info->invert & XT_QTAGUID_GID)) { + MT_DEBUG("qtaguid[%d]: leaving gid not matching\n", + par->hooknum); + res = false; + goto put_sock_ret_res; + } + + MT_DEBUG("qtaguid[%d]: leaving matched\n", par->hooknum); + res = true; + +put_sock_ret_res: + if (got_sock) + xt_socket_put_sk(sk); +ret_res: + MT_DEBUG("qtaguid[%d]: left %d\n", par->hooknum, res); + return res; +} + +#ifdef DDEBUG +/* This function is not in xt_qtaguid_print.c because of locks visibility */ +static void prdebug_full_state(int indent_level, const char *fmt, ...) +{ + va_list args; + char *fmt_buff; + char *buff; + + if (!unlikely(qtaguid_debug_mask & DDEBUG_MASK)) + return; + + fmt_buff = kasprintf(GFP_ATOMIC, + "qtaguid: %s(): %s {\n", __func__, fmt); + BUG_ON(!fmt_buff); + va_start(args, fmt); + buff = kvasprintf(GFP_ATOMIC, + fmt_buff, args); + BUG_ON(!buff); + pr_debug("%s", buff); + kfree(fmt_buff); + kfree(buff); + va_end(args); + + spin_lock_bh(&sock_tag_list_lock); + prdebug_sock_tag_tree(indent_level, &sock_tag_tree); + spin_unlock_bh(&sock_tag_list_lock); + + spin_lock_bh(&sock_tag_list_lock); + spin_lock_bh(&uid_tag_data_tree_lock); + prdebug_uid_tag_data_tree(indent_level, &uid_tag_data_tree); + prdebug_proc_qtu_data_tree(indent_level, &proc_qtu_data_tree); + spin_unlock_bh(&uid_tag_data_tree_lock); + spin_unlock_bh(&sock_tag_list_lock); + + spin_lock_bh(&iface_stat_list_lock); + prdebug_iface_stat_list(indent_level, &iface_stat_list); + spin_unlock_bh(&iface_stat_list_lock); + + pr_debug("qtaguid: %s(): }\n", __func__); +} +#else +static void prdebug_full_state(int indent_level, const char *fmt, ...) {} +#endif + +/* + * Procfs reader to get all active socket tags using style "1)" as described in + * fs/proc/generic.c + */ +static int qtaguid_ctrl_proc_read(char *page, char **num_items_returned, + off_t items_to_skip, int char_count, int *eof, + void *data) +{ + char *outp = page; + int len; + uid_t uid; + struct rb_node *node; + struct sock_tag *sock_tag_entry; + int item_index = 0; + int indent_level = 0; + long f_count; + + if (unlikely(module_passive)) { + *eof = 1; + return 0; + } + + if (*eof) + return 0; + + CT_DEBUG("qtaguid: proc ctrl page=%p off=%ld char_count=%d *eof=%d\n", + page, items_to_skip, char_count, *eof); + + spin_lock_bh(&sock_tag_list_lock); + for (node = rb_first(&sock_tag_tree); + node; + node = rb_next(node)) { + if (item_index++ < items_to_skip) + continue; + sock_tag_entry = rb_entry(node, struct sock_tag, sock_node); + uid = get_uid_from_tag(sock_tag_entry->tag); + CT_DEBUG("qtaguid: proc_read(): sk=%p tag=0x%llx (uid=%u) " + "pid=%u\n", + sock_tag_entry->sk, + sock_tag_entry->tag, + uid, + sock_tag_entry->pid + ); + f_count = atomic_long_read( + &sock_tag_entry->socket->file->f_count); + len = snprintf(outp, char_count, + "sock=%p tag=0x%llx (uid=%u) pid=%u " + "f_count=%lu\n", + sock_tag_entry->sk, + sock_tag_entry->tag, uid, + sock_tag_entry->pid, f_count); + if (len >= char_count) { + spin_unlock_bh(&sock_tag_list_lock); + *outp = '\0'; + return outp - page; + } + outp += len; + char_count -= len; + (*num_items_returned)++; + } + spin_unlock_bh(&sock_tag_list_lock); + + if (item_index++ >= items_to_skip) { + len = snprintf(outp, char_count, + "events: sockets_tagged=%llu " + "sockets_untagged=%llu " + "counter_set_changes=%llu " + "delete_cmds=%llu " + "iface_events=%llu " + "match_calls=%llu " + "match_found_sk=%llu " + "match_found_sk_in_ct=%llu " + "match_found_no_sk_in_ct=%llu " + "match_no_sk=%llu " + "match_no_sk_file=%llu\n", + atomic64_read(&qtu_events.sockets_tagged), + atomic64_read(&qtu_events.sockets_untagged), + atomic64_read(&qtu_events.counter_set_changes), + atomic64_read(&qtu_events.delete_cmds), + atomic64_read(&qtu_events.iface_events), + atomic64_read(&qtu_events.match_calls), + atomic64_read(&qtu_events.match_found_sk), + atomic64_read(&qtu_events.match_found_sk_in_ct), + atomic64_read( + &qtu_events.match_found_no_sk_in_ct), + atomic64_read(&qtu_events.match_no_sk), + atomic64_read(&qtu_events.match_no_sk_file)); + if (len >= char_count) { + *outp = '\0'; + return outp - page; + } + outp += len; + char_count -= len; + (*num_items_returned)++; + } + + /* Count the following as part of the last item_index */ + if (item_index > items_to_skip) { + prdebug_full_state(indent_level, "proc ctrl"); + } + + *eof = 1; + return outp - page; +} + +/* + * Delete socket tags, and stat tags associated with a given + * accouting tag and uid. + */ +static int ctrl_cmd_delete(const char *input) +{ + char cmd; + uid_t uid; + uid_t entry_uid; + tag_t acct_tag; + tag_t tag; + int res, argc; + struct iface_stat *iface_entry; + struct rb_node *node; + struct sock_tag *st_entry; + struct rb_root st_to_free_tree = RB_ROOT; + struct tag_stat *ts_entry; + struct tag_counter_set *tcs_entry; + struct tag_ref *tr_entry; + struct uid_tag_data *utd_entry; + + argc = sscanf(input, "%c %llu %u", &cmd, &acct_tag, &uid); + CT_DEBUG("qtaguid: ctrl_delete(%s): argc=%d cmd=%c " + "user_tag=0x%llx uid=%u\n", input, argc, cmd, + acct_tag, uid); + if (argc < 2) { + res = -EINVAL; + goto err; + } + if (!valid_atag(acct_tag)) { + pr_info("qtaguid: ctrl_delete(%s): invalid tag\n", input); + res = -EINVAL; + goto err; + } + if (argc < 3) { + uid = current_fsuid(); + } else if (!can_impersonate_uid(uid)) { + pr_info("qtaguid: ctrl_delete(%s): " + "insufficient priv from pid=%u tgid=%u uid=%u\n", + input, current->pid, current->tgid, current_fsuid()); + res = -EPERM; + goto err; + } + + tag = combine_atag_with_uid(acct_tag, uid); + CT_DEBUG("qtaguid: ctrl_delete(%s): " + "looking for tag=0x%llx (uid=%u)\n", + input, tag, uid); + + /* Delete socket tags */ + spin_lock_bh(&sock_tag_list_lock); + node = rb_first(&sock_tag_tree); + while (node) { + st_entry = rb_entry(node, struct sock_tag, sock_node); + entry_uid = get_uid_from_tag(st_entry->tag); + node = rb_next(node); + if (entry_uid != uid) + continue; + + CT_DEBUG("qtaguid: ctrl_delete(%s): st tag=0x%llx (uid=%u)\n", + input, st_entry->tag, entry_uid); + + if (!acct_tag || st_entry->tag == tag) { + rb_erase(&st_entry->sock_node, &sock_tag_tree); + /* Can't sockfd_put() within spinlock, do it later. */ + sock_tag_tree_insert(st_entry, &st_to_free_tree); + tr_entry = lookup_tag_ref(st_entry->tag, NULL); + BUG_ON(tr_entry->num_sock_tags <= 0); + tr_entry->num_sock_tags--; + /* + * TODO: remove if, and start failing. + * This is a hack to work around the fact that in some + * places we have "if (IS_ERR_OR_NULL(pqd_entry))" + * and are trying to work around apps + * that didn't open the /dev/xt_qtaguid. + */ + if (st_entry->list.next && st_entry->list.prev) + list_del(&st_entry->list); + } + } + spin_unlock_bh(&sock_tag_list_lock); + + sock_tag_tree_erase(&st_to_free_tree); + + /* Delete tag counter-sets */ + spin_lock_bh(&tag_counter_set_list_lock); + /* Counter sets are only on the uid tag, not full tag */ + tcs_entry = tag_counter_set_tree_search(&tag_counter_set_tree, tag); + if (tcs_entry) { + CT_DEBUG("qtaguid: ctrl_delete(%s): " + "erase tcs: tag=0x%llx (uid=%u) set=%d\n", + input, + tcs_entry->tn.tag, + get_uid_from_tag(tcs_entry->tn.tag), + tcs_entry->active_set); + rb_erase(&tcs_entry->tn.node, &tag_counter_set_tree); + kfree(tcs_entry); + } + spin_unlock_bh(&tag_counter_set_list_lock); + + /* + * If acct_tag is 0, then all entries belonging to uid are + * erased. + */ + spin_lock_bh(&iface_stat_list_lock); + list_for_each_entry(iface_entry, &iface_stat_list, list) { + spin_lock_bh(&iface_entry->tag_stat_list_lock); + node = rb_first(&iface_entry->tag_stat_tree); + while (node) { + ts_entry = rb_entry(node, struct tag_stat, tn.node); + entry_uid = get_uid_from_tag(ts_entry->tn.tag); + node = rb_next(node); + + CT_DEBUG("qtaguid: ctrl_delete(%s): " + "ts tag=0x%llx (uid=%u)\n", + input, ts_entry->tn.tag, entry_uid); + + if (entry_uid != uid) + continue; + if (!acct_tag || ts_entry->tn.tag == tag) { + CT_DEBUG("qtaguid: ctrl_delete(%s): " + "erase ts: %s 0x%llx %u\n", + input, iface_entry->ifname, + get_atag_from_tag(ts_entry->tn.tag), + entry_uid); + rb_erase(&ts_entry->tn.node, + &iface_entry->tag_stat_tree); + kfree(ts_entry); + } + } + spin_unlock_bh(&iface_entry->tag_stat_list_lock); + } + spin_unlock_bh(&iface_stat_list_lock); + + /* Cleanup the uid_tag_data */ + spin_lock_bh(&uid_tag_data_tree_lock); + node = rb_first(&uid_tag_data_tree); + while (node) { + utd_entry = rb_entry(node, struct uid_tag_data, node); + entry_uid = utd_entry->uid; + node = rb_next(node); + + CT_DEBUG("qtaguid: ctrl_delete(%s): " + "utd uid=%u\n", + input, entry_uid); + + if (entry_uid != uid) + continue; + /* + * Go over the tag_refs, and those that don't have + * sock_tags using them are freed. + */ + put_tag_ref_tree(tag, utd_entry); + put_utd_entry(utd_entry); + } + spin_unlock_bh(&uid_tag_data_tree_lock); + + atomic64_inc(&qtu_events.delete_cmds); + res = 0; + +err: + return res; +} + +static int ctrl_cmd_counter_set(const char *input) +{ + char cmd; + uid_t uid = 0; + tag_t tag; + int res, argc; + struct tag_counter_set *tcs; + int counter_set; + + argc = sscanf(input, "%c %d %u", &cmd, &counter_set, &uid); + CT_DEBUG("qtaguid: ctrl_counterset(%s): argc=%d cmd=%c " + "set=%d uid=%u\n", input, argc, cmd, + counter_set, uid); + if (argc != 3) { + res = -EINVAL; + goto err; + } + if (counter_set < 0 || counter_set >= IFS_MAX_COUNTER_SETS) { + pr_info("qtaguid: ctrl_counterset(%s): invalid counter_set range\n", + input); + res = -EINVAL; + goto err; + } + if (!can_manipulate_uids()) { + pr_info("qtaguid: ctrl_counterset(%s): " + "insufficient priv from pid=%u tgid=%u uid=%u\n", + input, current->pid, current->tgid, current_fsuid()); + res = -EPERM; + goto err; + } + + tag = make_tag_from_uid(uid); + spin_lock_bh(&tag_counter_set_list_lock); + tcs = tag_counter_set_tree_search(&tag_counter_set_tree, tag); + if (!tcs) { + tcs = kzalloc(sizeof(*tcs), GFP_ATOMIC); + if (!tcs) { + spin_unlock_bh(&tag_counter_set_list_lock); + pr_err("qtaguid: ctrl_counterset(%s): " + "failed to alloc counter set\n", + input); + res = -ENOMEM; + goto err; + } + tcs->tn.tag = tag; + tag_counter_set_tree_insert(tcs, &tag_counter_set_tree); + CT_DEBUG("qtaguid: ctrl_counterset(%s): added tcs tag=0x%llx " + "(uid=%u) set=%d\n", + input, tag, get_uid_from_tag(tag), counter_set); + } + tcs->active_set = counter_set; + spin_unlock_bh(&tag_counter_set_list_lock); + atomic64_inc(&qtu_events.counter_set_changes); + res = 0; + +err: + return res; +} + +static int ctrl_cmd_tag(const char *input) +{ + char cmd; + int sock_fd = 0; + uid_t uid = 0; + tag_t acct_tag = make_atag_from_value(0); + tag_t full_tag; + struct socket *el_socket; + int res, argc; + struct sock_tag *sock_tag_entry; + struct tag_ref *tag_ref_entry; + struct uid_tag_data *uid_tag_data_entry; + struct proc_qtu_data *pqd_entry; + + /* Unassigned args will get defaulted later. */ + argc = sscanf(input, "%c %d %llu %u", &cmd, &sock_fd, &acct_tag, &uid); + CT_DEBUG("qtaguid: ctrl_tag(%s): argc=%d cmd=%c sock_fd=%d " + "acct_tag=0x%llx uid=%u\n", input, argc, cmd, sock_fd, + acct_tag, uid); + if (argc < 2) { + res = -EINVAL; + goto err; + } + el_socket = sockfd_lookup(sock_fd, &res); /* This locks the file */ + if (!el_socket) { + pr_info("qtaguid: ctrl_tag(%s): failed to lookup" + " sock_fd=%d err=%d\n", input, sock_fd, res); + goto err; + } + CT_DEBUG("qtaguid: ctrl_tag(%s): socket->...->f_count=%ld ->sk=%p\n", + input, atomic_long_read(&el_socket->file->f_count), + el_socket->sk); + if (argc < 3) { + acct_tag = make_atag_from_value(0); + } else if (!valid_atag(acct_tag)) { + pr_info("qtaguid: ctrl_tag(%s): invalid tag\n", input); + res = -EINVAL; + goto err_put; + } + CT_DEBUG("qtaguid: ctrl_tag(%s): " + "pid=%u tgid=%u uid=%u euid=%u fsuid=%u " + "in_group=%d in_egroup=%d\n", + input, current->pid, current->tgid, current_uid(), + current_euid(), current_fsuid(), + in_group_p(proc_ctrl_write_gid), + in_egroup_p(proc_ctrl_write_gid)); + if (argc < 4) { + uid = current_fsuid(); + } else if (!can_impersonate_uid(uid)) { + pr_info("qtaguid: ctrl_tag(%s): " + "insufficient priv from pid=%u tgid=%u uid=%u\n", + input, current->pid, current->tgid, current_fsuid()); + res = -EPERM; + goto err_put; + } + full_tag = combine_atag_with_uid(acct_tag, uid); + + spin_lock_bh(&sock_tag_list_lock); + sock_tag_entry = get_sock_stat_nl(el_socket->sk); + tag_ref_entry = get_tag_ref(full_tag, &uid_tag_data_entry); + if (IS_ERR(tag_ref_entry)) { + res = PTR_ERR(tag_ref_entry); + spin_unlock_bh(&sock_tag_list_lock); + goto err_put; + } + tag_ref_entry->num_sock_tags++; + if (sock_tag_entry) { + struct tag_ref *prev_tag_ref_entry; + + CT_DEBUG("qtaguid: ctrl_tag(%s): retag for sk=%p " + "st@%p ...->f_count=%ld\n", + input, el_socket->sk, sock_tag_entry, + atomic_long_read(&el_socket->file->f_count)); + /* + * This is a re-tagging, so release the sock_fd that was + * locked at the time of the 1st tagging. + * There is still the ref from this call's sockfd_lookup() so + * it can be done within the spinlock. + */ + sockfd_put(sock_tag_entry->socket); + prev_tag_ref_entry = lookup_tag_ref(sock_tag_entry->tag, + &uid_tag_data_entry); + BUG_ON(IS_ERR_OR_NULL(prev_tag_ref_entry)); + BUG_ON(prev_tag_ref_entry->num_sock_tags <= 0); + prev_tag_ref_entry->num_sock_tags--; + sock_tag_entry->tag = full_tag; + } else { + CT_DEBUG("qtaguid: ctrl_tag(%s): newtag for sk=%p\n", + input, el_socket->sk); + sock_tag_entry = kzalloc(sizeof(*sock_tag_entry), + GFP_ATOMIC); + if (!sock_tag_entry) { + pr_err("qtaguid: ctrl_tag(%s): " + "socket tag alloc failed\n", + input); + spin_unlock_bh(&sock_tag_list_lock); + res = -ENOMEM; + goto err_tag_unref_put; + } + sock_tag_entry->sk = el_socket->sk; + sock_tag_entry->socket = el_socket; + sock_tag_entry->pid = current->tgid; + sock_tag_entry->tag = combine_atag_with_uid(acct_tag, + uid); + spin_lock_bh(&uid_tag_data_tree_lock); + pqd_entry = proc_qtu_data_tree_search( + &proc_qtu_data_tree, current->tgid); + /* + * TODO: remove if, and start failing. + * At first, we want to catch user-space code that is not + * opening the /dev/xt_qtaguid. + */ + if (IS_ERR_OR_NULL(pqd_entry)) + pr_warn_once( + "qtaguid: %s(): " + "User space forgot to open /dev/xt_qtaguid? " + "pid=%u tgid=%u uid=%u\n", __func__, + current->pid, current->tgid, + current_fsuid()); + else + list_add(&sock_tag_entry->list, + &pqd_entry->sock_tag_list); + spin_unlock_bh(&uid_tag_data_tree_lock); + + sock_tag_tree_insert(sock_tag_entry, &sock_tag_tree); + atomic64_inc(&qtu_events.sockets_tagged); + } + spin_unlock_bh(&sock_tag_list_lock); + /* We keep the ref to the socket (file) until it is untagged */ + CT_DEBUG("qtaguid: ctrl_tag(%s): done st@%p ...->f_count=%ld\n", + input, sock_tag_entry, + atomic_long_read(&el_socket->file->f_count)); + return 0; + +err_tag_unref_put: + BUG_ON(tag_ref_entry->num_sock_tags <= 0); + tag_ref_entry->num_sock_tags--; + free_tag_ref_from_utd_entry(tag_ref_entry, uid_tag_data_entry); +err_put: + CT_DEBUG("qtaguid: ctrl_tag(%s): done. ...->f_count=%ld\n", + input, atomic_long_read(&el_socket->file->f_count) - 1); + /* Release the sock_fd that was grabbed by sockfd_lookup(). */ + sockfd_put(el_socket); + return res; + +err: + CT_DEBUG("qtaguid: ctrl_tag(%s): done.\n", input); + return res; +} + +static int ctrl_cmd_untag(const char *input) +{ + char cmd; + int sock_fd = 0; + struct socket *el_socket; + int res, argc; + struct sock_tag *sock_tag_entry; + struct tag_ref *tag_ref_entry; + struct uid_tag_data *utd_entry; + struct proc_qtu_data *pqd_entry; + + argc = sscanf(input, "%c %d", &cmd, &sock_fd); + CT_DEBUG("qtaguid: ctrl_untag(%s): argc=%d cmd=%c sock_fd=%d\n", + input, argc, cmd, sock_fd); + if (argc < 2) { + res = -EINVAL; + goto err; + } + el_socket = sockfd_lookup(sock_fd, &res); /* This locks the file */ + if (!el_socket) { + pr_info("qtaguid: ctrl_untag(%s): failed to lookup" + " sock_fd=%d err=%d\n", input, sock_fd, res); + goto err; + } + CT_DEBUG("qtaguid: ctrl_untag(%s): socket->...->f_count=%ld ->sk=%p\n", + input, atomic_long_read(&el_socket->file->f_count), + el_socket->sk); + spin_lock_bh(&sock_tag_list_lock); + sock_tag_entry = get_sock_stat_nl(el_socket->sk); + if (!sock_tag_entry) { + spin_unlock_bh(&sock_tag_list_lock); + res = -EINVAL; + goto err_put; + } + /* + * The socket already belongs to the current process + * so it can do whatever it wants to it. + */ + rb_erase(&sock_tag_entry->sock_node, &sock_tag_tree); + + tag_ref_entry = lookup_tag_ref(sock_tag_entry->tag, &utd_entry); + BUG_ON(!tag_ref_entry); + BUG_ON(tag_ref_entry->num_sock_tags <= 0); + spin_lock_bh(&uid_tag_data_tree_lock); + pqd_entry = proc_qtu_data_tree_search( + &proc_qtu_data_tree, current->tgid); + /* + * TODO: remove if, and start failing. + * At first, we want to catch user-space code that is not + * opening the /dev/xt_qtaguid. + */ + if (IS_ERR_OR_NULL(pqd_entry)) + pr_warn_once("qtaguid: %s(): " + "User space forgot to open /dev/xt_qtaguid? " + "pid=%u tgid=%u uid=%u\n", __func__, + current->pid, current->tgid, current_fsuid()); + else + list_del(&sock_tag_entry->list); + spin_unlock_bh(&uid_tag_data_tree_lock); + /* + * We don't free tag_ref from the utd_entry here, + * only during a cmd_delete(). + */ + tag_ref_entry->num_sock_tags--; + spin_unlock_bh(&sock_tag_list_lock); + /* + * Release the sock_fd that was grabbed at tag time, + * and once more for the sockfd_lookup() here. + */ + sockfd_put(sock_tag_entry->socket); + CT_DEBUG("qtaguid: ctrl_untag(%s): done. st@%p ...->f_count=%ld\n", + input, sock_tag_entry, + atomic_long_read(&el_socket->file->f_count) - 1); + sockfd_put(el_socket); + + kfree(sock_tag_entry); + atomic64_inc(&qtu_events.sockets_untagged); + + return 0; + +err_put: + CT_DEBUG("qtaguid: ctrl_untag(%s): done. socket->...->f_count=%ld\n", + input, atomic_long_read(&el_socket->file->f_count) - 1); + /* Release the sock_fd that was grabbed by sockfd_lookup(). */ + sockfd_put(el_socket); + return res; + +err: + CT_DEBUG("qtaguid: ctrl_untag(%s): done.\n", input); + return res; +} + +static int qtaguid_ctrl_parse(const char *input, int count) +{ + char cmd; + int res; + + cmd = input[0]; + /* Collect params for commands */ + switch (cmd) { + case 'd': + res = ctrl_cmd_delete(input); + break; + + case 's': + res = ctrl_cmd_counter_set(input); + break; + + case 't': + res = ctrl_cmd_tag(input); + break; + + case 'u': + res = ctrl_cmd_untag(input); + break; + + default: + res = -EINVAL; + goto err; + } + if (!res) + res = count; +err: + CT_DEBUG("qtaguid: ctrl(%s): res=%d\n", input, res); + return res; +} + +#define MAX_QTAGUID_CTRL_INPUT_LEN 255 +static int qtaguid_ctrl_proc_write(struct file *file, const char __user *buffer, + unsigned long count, void *data) +{ + char input_buf[MAX_QTAGUID_CTRL_INPUT_LEN]; + + if (unlikely(module_passive)) + return count; + + if (count >= MAX_QTAGUID_CTRL_INPUT_LEN) + return -EINVAL; + + if (copy_from_user(input_buf, buffer, count)) + return -EFAULT; + + input_buf[count] = '\0'; + return qtaguid_ctrl_parse(input_buf, count); +} + +struct proc_print_info { + char *outp; + char **num_items_returned; + struct iface_stat *iface_entry; + struct tag_stat *ts_entry; + int item_index; + int items_to_skip; + int char_count; +}; + +static int pp_stats_line(struct proc_print_info *ppi, int cnt_set) +{ + int len; + struct data_counters *cnts; + + if (!ppi->item_index) { + if (ppi->item_index++ < ppi->items_to_skip) + return 0; + len = snprintf(ppi->outp, ppi->char_count, + "idx iface acct_tag_hex uid_tag_int cnt_set " + "rx_bytes rx_packets " + "tx_bytes tx_packets " + "rx_tcp_bytes rx_tcp_packets " + "rx_udp_bytes rx_udp_packets " + "rx_other_bytes rx_other_packets " + "tx_tcp_bytes tx_tcp_packets " + "tx_udp_bytes tx_udp_packets " + "tx_other_bytes tx_other_packets\n"); + } else { + tag_t tag = ppi->ts_entry->tn.tag; + uid_t stat_uid = get_uid_from_tag(tag); + + if (!can_read_other_uid_stats(stat_uid)) { + CT_DEBUG("qtaguid: stats line: " + "%s 0x%llx %u: insufficient priv " + "from pid=%u tgid=%u uid=%u\n", + ppi->iface_entry->ifname, + get_atag_from_tag(tag), stat_uid, + current->pid, current->tgid, current_fsuid()); + return 0; + } + if (ppi->item_index++ < ppi->items_to_skip) + return 0; + cnts = &ppi->ts_entry->counters; + len = snprintf( + ppi->outp, ppi->char_count, + "%d %s 0x%llx %u %u " + "%llu %llu " + "%llu %llu " + "%llu %llu " + "%llu %llu " + "%llu %llu " + "%llu %llu " + "%llu %llu " + "%llu %llu\n", + ppi->item_index, + ppi->iface_entry->ifname, + get_atag_from_tag(tag), + stat_uid, + cnt_set, + dc_sum_bytes(cnts, cnt_set, IFS_RX), + dc_sum_packets(cnts, cnt_set, IFS_RX), + dc_sum_bytes(cnts, cnt_set, IFS_TX), + dc_sum_packets(cnts, cnt_set, IFS_TX), + cnts->bpc[cnt_set][IFS_RX][IFS_TCP].bytes, + cnts->bpc[cnt_set][IFS_RX][IFS_TCP].packets, + cnts->bpc[cnt_set][IFS_RX][IFS_UDP].bytes, + cnts->bpc[cnt_set][IFS_RX][IFS_UDP].packets, + cnts->bpc[cnt_set][IFS_RX][IFS_PROTO_OTHER].bytes, + cnts->bpc[cnt_set][IFS_RX][IFS_PROTO_OTHER].packets, + cnts->bpc[cnt_set][IFS_TX][IFS_TCP].bytes, + cnts->bpc[cnt_set][IFS_TX][IFS_TCP].packets, + cnts->bpc[cnt_set][IFS_TX][IFS_UDP].bytes, + cnts->bpc[cnt_set][IFS_TX][IFS_UDP].packets, + cnts->bpc[cnt_set][IFS_TX][IFS_PROTO_OTHER].bytes, + cnts->bpc[cnt_set][IFS_TX][IFS_PROTO_OTHER].packets); + } + return len; +} + +static bool pp_sets(struct proc_print_info *ppi) +{ + int len; + int counter_set; + for (counter_set = 0; counter_set < IFS_MAX_COUNTER_SETS; + counter_set++) { + len = pp_stats_line(ppi, counter_set); + if (len >= ppi->char_count) { + *ppi->outp = '\0'; + return false; + } + if (len) { + ppi->outp += len; + ppi->char_count -= len; + (*ppi->num_items_returned)++; + } + } + return true; +} + +/* + * Procfs reader to get all tag stats using style "1)" as described in + * fs/proc/generic.c + * Groups all protocols tx/rx bytes. + */ +static int qtaguid_stats_proc_read(char *page, char **num_items_returned, + off_t items_to_skip, int char_count, int *eof, + void *data) +{ + struct proc_print_info ppi; + int len; + + ppi.outp = page; + ppi.item_index = 0; + ppi.char_count = char_count; + ppi.num_items_returned = num_items_returned; + ppi.items_to_skip = items_to_skip; + + if (unlikely(module_passive)) { + len = pp_stats_line(&ppi, 0); + /* The header should always be shorter than the buffer. */ + BUG_ON(len >= ppi.char_count); + (*num_items_returned)++; + *eof = 1; + return len; + } + + CT_DEBUG("qtaguid:proc stats page=%p *num_items_returned=%p off=%ld " + "char_count=%d *eof=%d\n", page, *num_items_returned, + items_to_skip, char_count, *eof); + + if (*eof) + return 0; + + /* The idx is there to help debug when things go belly up. */ + len = pp_stats_line(&ppi, 0); + /* Don't advance the outp unless the whole line was printed */ + if (len >= ppi.char_count) { + *ppi.outp = '\0'; + return ppi.outp - page; + } + if (len) { + ppi.outp += len; + ppi.char_count -= len; + (*num_items_returned)++; + } + + spin_lock_bh(&iface_stat_list_lock); + list_for_each_entry(ppi.iface_entry, &iface_stat_list, list) { + struct rb_node *node; + spin_lock_bh(&ppi.iface_entry->tag_stat_list_lock); + for (node = rb_first(&ppi.iface_entry->tag_stat_tree); + node; + node = rb_next(node)) { + ppi.ts_entry = rb_entry(node, struct tag_stat, tn.node); + if (!pp_sets(&ppi)) { + spin_unlock_bh( + &ppi.iface_entry->tag_stat_list_lock); + spin_unlock_bh(&iface_stat_list_lock); + return ppi.outp - page; + } + } + spin_unlock_bh(&ppi.iface_entry->tag_stat_list_lock); + } + spin_unlock_bh(&iface_stat_list_lock); + + *eof = 1; + return ppi.outp - page; +} + +/*------------------------------------------*/ +static int qtudev_open(struct inode *inode, struct file *file) +{ + struct uid_tag_data *utd_entry; + struct proc_qtu_data *pqd_entry; + struct proc_qtu_data *new_pqd_entry; + int res; + bool utd_entry_found; + + if (unlikely(qtu_proc_handling_passive)) + return 0; + + DR_DEBUG("qtaguid: qtudev_open(): pid=%u tgid=%u uid=%u\n", + current->pid, current->tgid, current_fsuid()); + + spin_lock_bh(&uid_tag_data_tree_lock); + + /* Look for existing uid data, or alloc one. */ + utd_entry = get_uid_data(current_fsuid(), &utd_entry_found); + if (IS_ERR_OR_NULL(utd_entry)) { + res = PTR_ERR(utd_entry); + goto err; + } + + /* Look for existing PID based proc_data */ + pqd_entry = proc_qtu_data_tree_search(&proc_qtu_data_tree, + current->tgid); + if (pqd_entry) { + pr_err("qtaguid: qtudev_open(): %u/%u %u " + "%s already opened\n", + current->pid, current->tgid, current_fsuid(), + QTU_DEV_NAME); + res = -EBUSY; + goto err_unlock_free_utd; + } + + new_pqd_entry = kzalloc(sizeof(*new_pqd_entry), GFP_ATOMIC); + if (!new_pqd_entry) { + pr_err("qtaguid: qtudev_open(): %u/%u %u: " + "proc data alloc failed\n", + current->pid, current->tgid, current_fsuid()); + res = -ENOMEM; + goto err_unlock_free_utd; + } + new_pqd_entry->pid = current->tgid; + INIT_LIST_HEAD(&new_pqd_entry->sock_tag_list); + new_pqd_entry->parent_tag_data = utd_entry; + utd_entry->num_pqd++; + + proc_qtu_data_tree_insert(new_pqd_entry, + &proc_qtu_data_tree); + + spin_unlock_bh(&uid_tag_data_tree_lock); + DR_DEBUG("qtaguid: tracking data for uid=%u in pqd=%p\n", + current_fsuid(), new_pqd_entry); + file->private_data = new_pqd_entry; + return 0; + +err_unlock_free_utd: + if (!utd_entry_found) { + rb_erase(&utd_entry->node, &uid_tag_data_tree); + kfree(utd_entry); + } + spin_unlock_bh(&uid_tag_data_tree_lock); +err: + return res; +} + +static int qtudev_release(struct inode *inode, struct file *file) +{ + struct proc_qtu_data *pqd_entry = file->private_data; + struct uid_tag_data *utd_entry = pqd_entry->parent_tag_data; + struct sock_tag *st_entry; + struct rb_root st_to_free_tree = RB_ROOT; + struct list_head *entry, *next; + struct tag_ref *tr; + + if (unlikely(qtu_proc_handling_passive)) + return 0; + + /* + * Do not trust the current->pid, it might just be a kworker cleaning + * up after a dead proc. + */ + DR_DEBUG("qtaguid: qtudev_release(): " + "pid=%u tgid=%u uid=%u " + "pqd_entry=%p->pid=%u utd_entry=%p->active_tags=%d\n", + current->pid, current->tgid, pqd_entry->parent_tag_data->uid, + pqd_entry, pqd_entry->pid, utd_entry, + utd_entry->num_active_tags); + + spin_lock_bh(&sock_tag_list_lock); + spin_lock_bh(&uid_tag_data_tree_lock); + + list_for_each_safe(entry, next, &pqd_entry->sock_tag_list) { + st_entry = list_entry(entry, struct sock_tag, list); + DR_DEBUG("qtaguid: %s(): " + "erase sock_tag=%p->sk=%p pid=%u tgid=%u uid=%u\n", + __func__, + st_entry, st_entry->sk, + current->pid, current->tgid, + pqd_entry->parent_tag_data->uid); + + utd_entry = uid_tag_data_tree_search( + &uid_tag_data_tree, + get_uid_from_tag(st_entry->tag)); + BUG_ON(IS_ERR_OR_NULL(utd_entry)); + DR_DEBUG("qtaguid: %s(): " + "looking for tag=0x%llx in utd_entry=%p\n", __func__, + st_entry->tag, utd_entry); + tr = tag_ref_tree_search(&utd_entry->tag_ref_tree, + st_entry->tag); + BUG_ON(!tr); + BUG_ON(tr->num_sock_tags <= 0); + tr->num_sock_tags--; + free_tag_ref_from_utd_entry(tr, utd_entry); + + rb_erase(&st_entry->sock_node, &sock_tag_tree); + list_del(&st_entry->list); + /* Can't sockfd_put() within spinlock, do it later. */ + sock_tag_tree_insert(st_entry, &st_to_free_tree); + + /* + * Try to free the utd_entry if no other proc_qtu_data is + * using it (num_pqd is 0) and it doesn't have active tags + * (num_active_tags is 0). + */ + put_utd_entry(utd_entry); + } + + rb_erase(&pqd_entry->node, &proc_qtu_data_tree); + BUG_ON(pqd_entry->parent_tag_data->num_pqd < 1); + pqd_entry->parent_tag_data->num_pqd--; + put_utd_entry(pqd_entry->parent_tag_data); + kfree(pqd_entry); + file->private_data = NULL; + + spin_unlock_bh(&uid_tag_data_tree_lock); + spin_unlock_bh(&sock_tag_list_lock); + + + sock_tag_tree_erase(&st_to_free_tree); + + prdebug_full_state(0, "%s(): pid=%u tgid=%u", __func__, + current->pid, current->tgid); + return 0; +} + +/*------------------------------------------*/ +static const struct file_operations qtudev_fops = { + .owner = THIS_MODULE, + .open = qtudev_open, + .release = qtudev_release, +}; + +static struct miscdevice qtu_device = { + .minor = MISC_DYNAMIC_MINOR, + .name = QTU_DEV_NAME, + .fops = &qtudev_fops, + /* How sad it doesn't allow for defaults: .mode = S_IRUGO | S_IWUSR */ +}; + +/*------------------------------------------*/ +static int __init qtaguid_proc_register(struct proc_dir_entry **res_procdir) +{ + int ret; + *res_procdir = proc_mkdir(module_procdirname, init_net.proc_net); + if (!*res_procdir) { + pr_err("qtaguid: failed to create proc/.../xt_qtaguid\n"); + ret = -ENOMEM; + goto no_dir; + } + + xt_qtaguid_ctrl_file = create_proc_entry("ctrl", proc_ctrl_perms, + *res_procdir); + if (!xt_qtaguid_ctrl_file) { + pr_err("qtaguid: failed to create xt_qtaguid/ctrl " + " file\n"); + ret = -ENOMEM; + goto no_ctrl_entry; + } + xt_qtaguid_ctrl_file->read_proc = qtaguid_ctrl_proc_read; + xt_qtaguid_ctrl_file->write_proc = qtaguid_ctrl_proc_write; + + xt_qtaguid_stats_file = create_proc_entry("stats", proc_stats_perms, + *res_procdir); + if (!xt_qtaguid_stats_file) { + pr_err("qtaguid: failed to create xt_qtaguid/stats " + "file\n"); + ret = -ENOMEM; + goto no_stats_entry; + } + xt_qtaguid_stats_file->read_proc = qtaguid_stats_proc_read; + /* + * TODO: add support counter hacking + * xt_qtaguid_stats_file->write_proc = qtaguid_stats_proc_write; + */ + return 0; + +no_stats_entry: + remove_proc_entry("ctrl", *res_procdir); +no_ctrl_entry: + remove_proc_entry("xt_qtaguid", NULL); +no_dir: + return ret; +} + +static struct xt_match qtaguid_mt_reg __read_mostly = { + /* + * This module masquerades as the "owner" module so that iptables + * tools can deal with it. + */ + .name = "owner", + .revision = 1, + .family = NFPROTO_UNSPEC, + .match = qtaguid_mt, + .matchsize = sizeof(struct xt_qtaguid_match_info), + .me = THIS_MODULE, +}; + +static int __init qtaguid_mt_init(void) +{ + if (qtaguid_proc_register(&xt_qtaguid_procdir) + || iface_stat_init(xt_qtaguid_procdir) + || xt_register_match(&qtaguid_mt_reg) + || misc_register(&qtu_device)) + return -1; + return 0; +} + +/* + * TODO: allow unloading of the module. + * For now stats are permanent. + * Kconfig forces'y/n' and never an 'm'. + */ + +module_init(qtaguid_mt_init); +MODULE_AUTHOR("jpa "); +MODULE_DESCRIPTION("Xtables: socket owner+tag matching and associated stats"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("ipt_owner"); +MODULE_ALIAS("ip6t_owner"); +MODULE_ALIAS("ipt_qtaguid"); +MODULE_ALIAS("ip6t_qtaguid"); diff --git a/net/netfilter/xt_qtaguid_internal.h b/net/netfilter/xt_qtaguid_internal.h new file mode 100644 index 000000000000..02479d6d317d --- /dev/null +++ b/net/netfilter/xt_qtaguid_internal.h @@ -0,0 +1,330 @@ +/* + * Kernel iptables module to track stats for packets based on user tags. + * + * (C) 2011 Google, Inc + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#ifndef __XT_QTAGUID_INTERNAL_H__ +#define __XT_QTAGUID_INTERNAL_H__ + +#include +#include +#include +#include + +/* Iface handling */ +#define IDEBUG_MASK (1<<0) +/* Iptable Matching. Per packet. */ +#define MDEBUG_MASK (1<<1) +/* Red-black tree handling. Per packet. */ +#define RDEBUG_MASK (1<<2) +/* procfs ctrl/stats handling */ +#define CDEBUG_MASK (1<<3) +/* dev and resource tracking */ +#define DDEBUG_MASK (1<<4) + +/* E.g (IDEBUG_MASK | CDEBUG_MASK | DDEBUG_MASK) */ +#define DEFAULT_DEBUG_MASK 0 + +/* + * (Un)Define these *DEBUG to compile out/in the pr_debug calls. + * All undef: text size ~ 0x3030; all def: ~ 0x4404. + */ +#define IDEBUG +#define MDEBUG +#define RDEBUG +#define CDEBUG +#define DDEBUG + +#define MSK_DEBUG(mask, ...) do { \ + if (unlikely(qtaguid_debug_mask & (mask))) \ + pr_debug(__VA_ARGS__); \ + } while (0) +#ifdef IDEBUG +#define IF_DEBUG(...) MSK_DEBUG(IDEBUG_MASK, __VA_ARGS__) +#else +#define IF_DEBUG(...) no_printk(__VA_ARGS__) +#endif +#ifdef MDEBUG +#define MT_DEBUG(...) MSK_DEBUG(MDEBUG_MASK, __VA_ARGS__) +#else +#define MT_DEBUG(...) no_printk(__VA_ARGS__) +#endif +#ifdef RDEBUG +#define RB_DEBUG(...) MSK_DEBUG(RDEBUG_MASK, __VA_ARGS__) +#else +#define RB_DEBUG(...) no_printk(__VA_ARGS__) +#endif +#ifdef CDEBUG +#define CT_DEBUG(...) MSK_DEBUG(CDEBUG_MASK, __VA_ARGS__) +#else +#define CT_DEBUG(...) no_printk(__VA_ARGS__) +#endif +#ifdef DDEBUG +#define DR_DEBUG(...) MSK_DEBUG(DDEBUG_MASK, __VA_ARGS__) +#else +#define DR_DEBUG(...) no_printk(__VA_ARGS__) +#endif + +extern uint qtaguid_debug_mask; + +/*---------------------------------------------------------------------------*/ +/* + * Tags: + * + * They represent what the data usage counters will be tracked against. + * By default a tag is just based on the UID. + * The UID is used as the base for policing, and can not be ignored. + * So a tag will always at least represent a UID (uid_tag). + * + * A tag can be augmented with an "accounting tag" which is associated + * with a UID. + * User space can set the acct_tag portion of the tag which is then used + * with sockets: all data belonging to that socket will be counted against the + * tag. The policing is then based on the tag's uid_tag portion, + * and stats are collected for the acct_tag portion separately. + * + * There could be + * a: {acct_tag=1, uid_tag=10003} + * b: {acct_tag=2, uid_tag=10003} + * c: {acct_tag=3, uid_tag=10003} + * d: {acct_tag=0, uid_tag=10003} + * a, b, and c represent tags associated with specific sockets. + * d is for the totals for that uid, including all untagged traffic. + * Typically d is used with policing/quota rules. + * + * We want tag_t big enough to distinguish uid_t and acct_tag. + * It might become a struct if needed. + * Nothing should be using it as an int. + */ +typedef uint64_t tag_t; /* Only used via accessors */ + +#define TAG_UID_MASK 0xFFFFFFFFULL +#define TAG_ACCT_MASK (~0xFFFFFFFFULL) + +static inline int tag_compare(tag_t t1, tag_t t2) +{ + return t1 < t2 ? -1 : t1 == t2 ? 0 : 1; +} + +static inline tag_t combine_atag_with_uid(tag_t acct_tag, uid_t uid) +{ + return acct_tag | uid; +} +static inline tag_t make_tag_from_uid(uid_t uid) +{ + return uid; +} +static inline uid_t get_uid_from_tag(tag_t tag) +{ + return tag & TAG_UID_MASK; +} +static inline tag_t get_utag_from_tag(tag_t tag) +{ + return tag & TAG_UID_MASK; +} +static inline tag_t get_atag_from_tag(tag_t tag) +{ + return tag & TAG_ACCT_MASK; +} + +static inline bool valid_atag(tag_t tag) +{ + return !(tag & TAG_UID_MASK); +} +static inline tag_t make_atag_from_value(uint32_t value) +{ + return (uint64_t)value << 32; +} +/*---------------------------------------------------------------------------*/ + +/* + * Maximum number of socket tags that a UID is allowed to have active. + * Multiple processes belonging to the same UID contribute towards this limit. + * Special UIDs that can impersonate a UID also contribute (e.g. download + * manager, ...) + */ +#define DEFAULT_MAX_SOCK_TAGS 1024 + +/* + * For now we only track 2 sets of counters. + * The default set is 0. + * Userspace can activate another set for a given uid being tracked. + */ +#define IFS_MAX_COUNTER_SETS 2 + +enum ifs_tx_rx { + IFS_TX, + IFS_RX, + IFS_MAX_DIRECTIONS +}; + +/* For now, TCP, UDP, the rest */ +enum ifs_proto { + IFS_TCP, + IFS_UDP, + IFS_PROTO_OTHER, + IFS_MAX_PROTOS +}; + +struct byte_packet_counters { + uint64_t bytes; + uint64_t packets; +}; + +struct data_counters { + struct byte_packet_counters bpc[IFS_MAX_COUNTER_SETS][IFS_MAX_DIRECTIONS][IFS_MAX_PROTOS]; +}; + +/* Generic X based nodes used as a base for rb_tree ops */ +struct tag_node { + struct rb_node node; + tag_t tag; +}; + +struct tag_stat { + struct tag_node tn; + struct data_counters counters; + /* + * If this tag is acct_tag based, we need to count against the + * matching parent uid_tag. + */ + struct data_counters *parent_counters; +}; + +struct iface_stat { + struct list_head list; /* in iface_stat_list */ + char *ifname; + bool active; + /* net_dev is only valid for active iface_stat */ + struct net_device *net_dev; + + struct byte_packet_counters totals[IFS_MAX_DIRECTIONS]; + /* + * We keep the last_known, because some devices reset their counters + * just before NETDEV_UP, while some will reset just before + * NETDEV_REGISTER (which is more normal). + * So now, if the device didn't do a NETDEV_UNREGISTER and we see + * its current dev stats smaller that what was previously known, we + * assume an UNREGISTER and just use the last_known. + */ + struct byte_packet_counters last_known[IFS_MAX_DIRECTIONS]; + /* last_known is usable when last_known_valid is true */ + bool last_known_valid; + + struct proc_dir_entry *proc_ptr; + + struct rb_root tag_stat_tree; + spinlock_t tag_stat_list_lock; +}; + +/* This is needed to create proc_dir_entries from atomic context. */ +struct iface_stat_work { + struct work_struct iface_work; + struct iface_stat *iface_entry; +}; + +/* + * Track tag that this socket is transferring data for, and not necessarily + * the uid that owns the socket. + * This is the tag against which tag_stat.counters will be billed. + * These structs need to be looked up by sock and pid. + */ +struct sock_tag { + struct rb_node sock_node; + struct sock *sk; /* Only used as a number, never dereferenced */ + /* The socket is needed for sockfd_put() */ + struct socket *socket; + /* Used to associate with a given pid */ + struct list_head list; /* in proc_qtu_data.sock_tag_list */ + pid_t pid; + + tag_t tag; +}; + +struct qtaguid_event_counts { + /* Various successful events */ + atomic64_t sockets_tagged; + atomic64_t sockets_untagged; + atomic64_t counter_set_changes; + atomic64_t delete_cmds; + atomic64_t iface_events; /* Number of NETDEV_* events handled */ + + atomic64_t match_calls; /* Number of times iptables called mt */ + /* + * match_found_sk_*: numbers related to the netfilter matching + * function finding a sock for the sk_buff. + * Total skbs processed is sum(match_found*). + */ + atomic64_t match_found_sk; /* An sk was already in the sk_buff. */ + /* The connection tracker had or didn't have the sk. */ + atomic64_t match_found_sk_in_ct; + atomic64_t match_found_no_sk_in_ct; + /* + * No sk could be found. No apparent owner. Could happen with + * unsolicited traffic. + */ + atomic64_t match_no_sk; + /* + * The file ptr in the sk_socket wasn't there. + * This might happen for traffic while the socket is being closed. + */ + atomic64_t match_no_sk_file; +}; + +/* Track the set active_set for the given tag. */ +struct tag_counter_set { + struct tag_node tn; + int active_set; +}; + +/*----------------------------------------------*/ +/* + * The qtu uid data is used to track resources that are created directly or + * indirectly by processes (uid tracked). + * It is shared by the processes with the same uid. + * Some of the resource will be counted to prevent further rogue allocations, + * some will need freeing once the owner process (uid) exits. + */ +struct uid_tag_data { + struct rb_node node; + uid_t uid; + + /* + * For the uid, how many accounting tags have been set. + */ + int num_active_tags; + /* Track the number of proc_qtu_data that reference it */ + int num_pqd; + struct rb_root tag_ref_tree; + /* No tag_node_tree_lock; use uid_tag_data_tree_lock */ +}; + +struct tag_ref { + struct tag_node tn; + + /* + * This tracks the number of active sockets that have a tag on them + * which matches this tag_ref.tn.tag. + * A tag ref can live on after the sockets are untagged. + * A tag ref can only be removed during a tag delete command. + */ + int num_sock_tags; +}; + +struct proc_qtu_data { + struct rb_node node; + pid_t pid; + + struct uid_tag_data *parent_tag_data; + + /* Tracks the sock_tags that need freeing upon this proc's death */ + struct list_head sock_tag_list; + /* No spinlock_t sock_tag_list_lock; use the global one. */ +}; + +/*----------------------------------------------*/ +#endif /* ifndef __XT_QTAGUID_INTERNAL_H__ */ diff --git a/net/netfilter/xt_qtaguid_print.c b/net/netfilter/xt_qtaguid_print.c new file mode 100644 index 000000000000..39176785c91f --- /dev/null +++ b/net/netfilter/xt_qtaguid_print.c @@ -0,0 +1,556 @@ +/* + * Pretty printing Support for iptables xt_qtaguid module. + * + * (C) 2011 Google, Inc + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +/* + * Most of the functions in this file just waste time if DEBUG is not defined. + * The matching xt_qtaguid_print.h will static inline empty funcs if the needed + * debug flags ore not defined. + * Those funcs that fail to allocate memory will panic as there is no need to + * hobble allong just pretending to do the requested work. + */ + +#define DEBUG + +#include +#include +#include +#include +#include +#include + + +#include "xt_qtaguid_internal.h" +#include "xt_qtaguid_print.h" + +#ifdef DDEBUG + +static void _bug_on_err_or_null(void *ptr) +{ + if (IS_ERR_OR_NULL(ptr)) { + pr_err("qtaguid: kmalloc failed\n"); + BUG(); + } +} + +char *pp_tag_t(tag_t *tag) +{ + char *res; + + if (!tag) + res = kasprintf(GFP_ATOMIC, "tag_t@null{}"); + else + res = kasprintf(GFP_ATOMIC, + "tag_t@%p{tag=0x%llx, uid=%u}", + tag, *tag, get_uid_from_tag(*tag)); + _bug_on_err_or_null(res); + return res; +} + +char *pp_data_counters(struct data_counters *dc, bool showValues) +{ + char *res; + + if (!dc) + res = kasprintf(GFP_ATOMIC, "data_counters@null{}"); + else if (showValues) + res = kasprintf( + GFP_ATOMIC, "data_counters@%p{" + "set0{" + "rx{" + "tcp{b=%llu, p=%llu}, " + "udp{b=%llu, p=%llu}," + "other{b=%llu, p=%llu}}, " + "tx{" + "tcp{b=%llu, p=%llu}, " + "udp{b=%llu, p=%llu}," + "other{b=%llu, p=%llu}}}, " + "set1{" + "rx{" + "tcp{b=%llu, p=%llu}, " + "udp{b=%llu, p=%llu}," + "other{b=%llu, p=%llu}}, " + "tx{" + "tcp{b=%llu, p=%llu}, " + "udp{b=%llu, p=%llu}," + "other{b=%llu, p=%llu}}}}", + dc, + dc->bpc[0][IFS_RX][IFS_TCP].bytes, + dc->bpc[0][IFS_RX][IFS_TCP].packets, + dc->bpc[0][IFS_RX][IFS_UDP].bytes, + dc->bpc[0][IFS_RX][IFS_UDP].packets, + dc->bpc[0][IFS_RX][IFS_PROTO_OTHER].bytes, + dc->bpc[0][IFS_RX][IFS_PROTO_OTHER].packets, + dc->bpc[0][IFS_TX][IFS_TCP].bytes, + dc->bpc[0][IFS_TX][IFS_TCP].packets, + dc->bpc[0][IFS_TX][IFS_UDP].bytes, + dc->bpc[0][IFS_TX][IFS_UDP].packets, + dc->bpc[0][IFS_TX][IFS_PROTO_OTHER].bytes, + dc->bpc[0][IFS_TX][IFS_PROTO_OTHER].packets, + dc->bpc[1][IFS_RX][IFS_TCP].bytes, + dc->bpc[1][IFS_RX][IFS_TCP].packets, + dc->bpc[1][IFS_RX][IFS_UDP].bytes, + dc->bpc[1][IFS_RX][IFS_UDP].packets, + dc->bpc[1][IFS_RX][IFS_PROTO_OTHER].bytes, + dc->bpc[1][IFS_RX][IFS_PROTO_OTHER].packets, + dc->bpc[1][IFS_TX][IFS_TCP].bytes, + dc->bpc[1][IFS_TX][IFS_TCP].packets, + dc->bpc[1][IFS_TX][IFS_UDP].bytes, + dc->bpc[1][IFS_TX][IFS_UDP].packets, + dc->bpc[1][IFS_TX][IFS_PROTO_OTHER].bytes, + dc->bpc[1][IFS_TX][IFS_PROTO_OTHER].packets); + else + res = kasprintf(GFP_ATOMIC, "data_counters@%p{...}", dc); + _bug_on_err_or_null(res); + return res; +} + +char *pp_tag_node(struct tag_node *tn) +{ + char *tag_str; + char *res; + + if (!tn) { + res = kasprintf(GFP_ATOMIC, "tag_node@null{}"); + _bug_on_err_or_null(res); + return res; + } + tag_str = pp_tag_t(&tn->tag); + res = kasprintf(GFP_ATOMIC, + "tag_node@%p{tag=%s}", + tn, tag_str); + _bug_on_err_or_null(res); + kfree(tag_str); + return res; +} + +char *pp_tag_ref(struct tag_ref *tr) +{ + char *tn_str; + char *res; + + if (!tr) { + res = kasprintf(GFP_ATOMIC, "tag_ref@null{}"); + _bug_on_err_or_null(res); + return res; + } + tn_str = pp_tag_node(&tr->tn); + res = kasprintf(GFP_ATOMIC, + "tag_ref@%p{%s, num_sock_tags=%d}", + tr, tn_str, tr->num_sock_tags); + _bug_on_err_or_null(res); + kfree(tn_str); + return res; +} + +char *pp_tag_stat(struct tag_stat *ts) +{ + char *tn_str; + char *counters_str; + char *parent_counters_str; + char *res; + + if (!ts) { + res = kasprintf(GFP_ATOMIC, "tag_stat@null{}"); + _bug_on_err_or_null(res); + return res; + } + tn_str = pp_tag_node(&ts->tn); + counters_str = pp_data_counters(&ts->counters, true); + parent_counters_str = pp_data_counters(ts->parent_counters, false); + res = kasprintf(GFP_ATOMIC, + "tag_stat@%p{%s, counters=%s, parent_counters=%s}", + ts, tn_str, counters_str, parent_counters_str); + _bug_on_err_or_null(res); + kfree(tn_str); + kfree(counters_str); + kfree(parent_counters_str); + return res; +} + +char *pp_iface_stat(struct iface_stat *is) +{ + char *res; + if (!is) + res = kasprintf(GFP_ATOMIC, "iface_stat@null{}"); + else + res = kasprintf(GFP_ATOMIC, "iface_stat@%p{" + "list=list_head{...}, " + "ifname=%s, " + "total={rx={bytes=%llu, " + "packets=%llu}, " + "tx={bytes=%llu, " + "packets=%llu}}, " + "last_known_valid=%d, " + "last_known={rx={bytes=%llu, " + "packets=%llu}, " + "tx={bytes=%llu, " + "packets=%llu}}, " + "active=%d, " + "net_dev=%p, " + "proc_ptr=%p, " + "tag_stat_tree=rb_root{...}}", + is, + is->ifname, + is->totals[IFS_RX].bytes, + is->totals[IFS_RX].packets, + is->totals[IFS_TX].bytes, + is->totals[IFS_TX].packets, + is->last_known_valid, + is->last_known[IFS_RX].bytes, + is->last_known[IFS_RX].packets, + is->last_known[IFS_TX].bytes, + is->last_known[IFS_TX].packets, + is->active, + is->net_dev, + is->proc_ptr); + _bug_on_err_or_null(res); + return res; +} + +char *pp_sock_tag(struct sock_tag *st) +{ + char *tag_str; + char *res; + + if (!st) { + res = kasprintf(GFP_ATOMIC, "sock_tag@null{}"); + _bug_on_err_or_null(res); + return res; + } + tag_str = pp_tag_t(&st->tag); + res = kasprintf(GFP_ATOMIC, "sock_tag@%p{" + "sock_node=rb_node{...}, " + "sk=%p socket=%p (f_count=%lu), list=list_head{...}, " + "pid=%u, tag=%s}", + st, st->sk, st->socket, atomic_long_read( + &st->socket->file->f_count), + st->pid, tag_str); + _bug_on_err_or_null(res); + kfree(tag_str); + return res; +} + +char *pp_uid_tag_data(struct uid_tag_data *utd) +{ + char *res; + + if (!utd) + res = kasprintf(GFP_ATOMIC, "uid_tag_data@null{}"); + else + res = kasprintf(GFP_ATOMIC, "uid_tag_data@%p{" + "uid=%u, num_active_acct_tags=%d, " + "num_pqd=%d, " + "tag_node_tree=rb_root{...}, " + "proc_qtu_data_tree=rb_root{...}}", + utd, utd->uid, + utd->num_active_tags, utd->num_pqd); + _bug_on_err_or_null(res); + return res; +} + +char *pp_proc_qtu_data(struct proc_qtu_data *pqd) +{ + char *parent_tag_data_str; + char *res; + + if (!pqd) { + res = kasprintf(GFP_ATOMIC, "proc_qtu_data@null{}"); + _bug_on_err_or_null(res); + return res; + } + parent_tag_data_str = pp_uid_tag_data(pqd->parent_tag_data); + res = kasprintf(GFP_ATOMIC, "proc_qtu_data@%p{" + "node=rb_node{...}, pid=%u, " + "parent_tag_data=%s, " + "sock_tag_list=list_head{...}}", + pqd, pqd->pid, parent_tag_data_str + ); + _bug_on_err_or_null(res); + kfree(parent_tag_data_str); + return res; +} + +/*------------------------------------------*/ +void prdebug_sock_tag_tree(int indent_level, + struct rb_root *sock_tag_tree) +{ + struct rb_node *node; + struct sock_tag *sock_tag_entry; + char *str; + + if (!unlikely(qtaguid_debug_mask & DDEBUG_MASK)) + return; + + if (RB_EMPTY_ROOT(sock_tag_tree)) { + str = "sock_tag_tree=rb_root{}"; + pr_debug("%*d: %s\n", indent_level*2, indent_level, str); + return; + } + + str = "sock_tag_tree=rb_root{"; + pr_debug("%*d: %s\n", indent_level*2, indent_level, str); + indent_level++; + for (node = rb_first(sock_tag_tree); + node; + node = rb_next(node)) { + sock_tag_entry = rb_entry(node, struct sock_tag, sock_node); + str = pp_sock_tag(sock_tag_entry); + pr_debug("%*d: %s,\n", indent_level*2, indent_level, str); + kfree(str); + } + indent_level--; + str = "}"; + pr_debug("%*d: %s\n", indent_level*2, indent_level, str); +} + +void prdebug_sock_tag_list(int indent_level, + struct list_head *sock_tag_list) +{ + struct sock_tag *sock_tag_entry; + char *str; + + if (!unlikely(qtaguid_debug_mask & DDEBUG_MASK)) + return; + + if (list_empty(sock_tag_list)) { + str = "sock_tag_list=list_head{}"; + pr_debug("%*d: %s\n", indent_level*2, indent_level, str); + return; + } + + str = "sock_tag_list=list_head{"; + pr_debug("%*d: %s\n", indent_level*2, indent_level, str); + indent_level++; + list_for_each_entry(sock_tag_entry, sock_tag_list, list) { + str = pp_sock_tag(sock_tag_entry); + pr_debug("%*d: %s,\n", indent_level*2, indent_level, str); + kfree(str); + } + indent_level--; + str = "}"; + pr_debug("%*d: %s\n", indent_level*2, indent_level, str); +} + +void prdebug_proc_qtu_data_tree(int indent_level, + struct rb_root *proc_qtu_data_tree) +{ + char *str; + struct rb_node *node; + struct proc_qtu_data *proc_qtu_data_entry; + + if (!unlikely(qtaguid_debug_mask & DDEBUG_MASK)) + return; + + if (RB_EMPTY_ROOT(proc_qtu_data_tree)) { + str = "proc_qtu_data_tree=rb_root{}"; + pr_debug("%*d: %s\n", indent_level*2, indent_level, str); + return; + } + + str = "proc_qtu_data_tree=rb_root{"; + pr_debug("%*d: %s\n", indent_level*2, indent_level, str); + indent_level++; + for (node = rb_first(proc_qtu_data_tree); + node; + node = rb_next(node)) { + proc_qtu_data_entry = rb_entry(node, + struct proc_qtu_data, + node); + str = pp_proc_qtu_data(proc_qtu_data_entry); + pr_debug("%*d: %s,\n", indent_level*2, indent_level, + str); + kfree(str); + indent_level++; + prdebug_sock_tag_list(indent_level, + &proc_qtu_data_entry->sock_tag_list); + indent_level--; + + } + indent_level--; + str = "}"; + pr_debug("%*d: %s\n", indent_level*2, indent_level, str); +} + +void prdebug_tag_ref_tree(int indent_level, struct rb_root *tag_ref_tree) +{ + char *str; + struct rb_node *node; + struct tag_ref *tag_ref_entry; + + if (!unlikely(qtaguid_debug_mask & DDEBUG_MASK)) + return; + + if (RB_EMPTY_ROOT(tag_ref_tree)) { + str = "tag_ref_tree{}"; + pr_debug("%*d: %s\n", indent_level*2, indent_level, str); + return; + } + + str = "tag_ref_tree{"; + pr_debug("%*d: %s\n", indent_level*2, indent_level, str); + indent_level++; + for (node = rb_first(tag_ref_tree); + node; + node = rb_next(node)) { + tag_ref_entry = rb_entry(node, + struct tag_ref, + tn.node); + str = pp_tag_ref(tag_ref_entry); + pr_debug("%*d: %s,\n", indent_level*2, indent_level, + str); + kfree(str); + } + indent_level--; + str = "}"; + pr_debug("%*d: %s\n", indent_level*2, indent_level, str); +} + +void prdebug_uid_tag_data_tree(int indent_level, + struct rb_root *uid_tag_data_tree) +{ + char *str; + struct rb_node *node; + struct uid_tag_data *uid_tag_data_entry; + + if (!unlikely(qtaguid_debug_mask & DDEBUG_MASK)) + return; + + if (RB_EMPTY_ROOT(uid_tag_data_tree)) { + str = "uid_tag_data_tree=rb_root{}"; + pr_debug("%*d: %s\n", indent_level*2, indent_level, str); + return; + } + + str = "uid_tag_data_tree=rb_root{"; + pr_debug("%*d: %s\n", indent_level*2, indent_level, str); + indent_level++; + for (node = rb_first(uid_tag_data_tree); + node; + node = rb_next(node)) { + uid_tag_data_entry = rb_entry(node, struct uid_tag_data, + node); + str = pp_uid_tag_data(uid_tag_data_entry); + pr_debug("%*d: %s,\n", indent_level*2, indent_level, str); + kfree(str); + if (!RB_EMPTY_ROOT(&uid_tag_data_entry->tag_ref_tree)) { + indent_level++; + prdebug_tag_ref_tree(indent_level, + &uid_tag_data_entry->tag_ref_tree); + indent_level--; + } + } + indent_level--; + str = "}"; + pr_debug("%*d: %s\n", indent_level*2, indent_level, str); +} + +void prdebug_tag_stat_tree(int indent_level, + struct rb_root *tag_stat_tree) +{ + char *str; + struct rb_node *node; + struct tag_stat *ts_entry; + + if (!unlikely(qtaguid_debug_mask & DDEBUG_MASK)) + return; + + if (RB_EMPTY_ROOT(tag_stat_tree)) { + str = "tag_stat_tree{}"; + pr_debug("%*d: %s\n", indent_level*2, indent_level, str); + return; + } + + str = "tag_stat_tree{"; + pr_debug("%*d: %s\n", indent_level*2, indent_level, str); + indent_level++; + for (node = rb_first(tag_stat_tree); + node; + node = rb_next(node)) { + ts_entry = rb_entry(node, struct tag_stat, tn.node); + str = pp_tag_stat(ts_entry); + pr_debug("%*d: %s\n", indent_level*2, indent_level, + str); + kfree(str); + } + indent_level--; + str = "}"; + pr_debug("%*d: %s\n", indent_level*2, indent_level, str); +} + +void prdebug_iface_stat_list(int indent_level, + struct list_head *iface_stat_list) +{ + char *str; + struct iface_stat *iface_entry; + + if (!unlikely(qtaguid_debug_mask & DDEBUG_MASK)) + return; + + if (list_empty(iface_stat_list)) { + str = "iface_stat_list=list_head{}"; + pr_debug("%*d: %s\n", indent_level*2, indent_level, str); + return; + } + + str = "iface_stat_list=list_head{"; + pr_debug("%*d: %s\n", indent_level*2, indent_level, str); + indent_level++; + list_for_each_entry(iface_entry, iface_stat_list, list) { + str = pp_iface_stat(iface_entry); + pr_debug("%*d: %s\n", indent_level*2, indent_level, str); + kfree(str); + + spin_lock_bh(&iface_entry->tag_stat_list_lock); + if (!RB_EMPTY_ROOT(&iface_entry->tag_stat_tree)) { + indent_level++; + prdebug_tag_stat_tree(indent_level, + &iface_entry->tag_stat_tree); + indent_level--; + } + spin_unlock_bh(&iface_entry->tag_stat_list_lock); + } + indent_level--; + str = "}"; + pr_debug("%*d: %s\n", indent_level*2, indent_level, str); +} + +#endif /* ifdef DDEBUG */ +/*------------------------------------------*/ +static const char * const netdev_event_strings[] = { + "netdev_unknown", + "NETDEV_UP", + "NETDEV_DOWN", + "NETDEV_REBOOT", + "NETDEV_CHANGE", + "NETDEV_REGISTER", + "NETDEV_UNREGISTER", + "NETDEV_CHANGEMTU", + "NETDEV_CHANGEADDR", + "NETDEV_GOING_DOWN", + "NETDEV_CHANGENAME", + "NETDEV_FEAT_CHANGE", + "NETDEV_BONDING_FAILOVER", + "NETDEV_PRE_UP", + "NETDEV_PRE_TYPE_CHANGE", + "NETDEV_POST_TYPE_CHANGE", + "NETDEV_POST_INIT", + "NETDEV_UNREGISTER_BATCH", + "NETDEV_RELEASE", + "NETDEV_NOTIFY_PEERS", + "NETDEV_JOIN", +}; + +const char *netdev_evt_str(int netdev_event) +{ + if (netdev_event < 0 + || netdev_event >= ARRAY_SIZE(netdev_event_strings)) + return "bad event num"; + return netdev_event_strings[netdev_event]; +} diff --git a/net/netfilter/xt_qtaguid_print.h b/net/netfilter/xt_qtaguid_print.h new file mode 100644 index 000000000000..b63871a0be5a --- /dev/null +++ b/net/netfilter/xt_qtaguid_print.h @@ -0,0 +1,120 @@ +/* + * Pretty printing Support for iptables xt_qtaguid module. + * + * (C) 2011 Google, Inc + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#ifndef __XT_QTAGUID_PRINT_H__ +#define __XT_QTAGUID_PRINT_H__ + +#include "xt_qtaguid_internal.h" + +#ifdef DDEBUG + +char *pp_tag_t(tag_t *tag); +char *pp_data_counters(struct data_counters *dc, bool showValues); +char *pp_tag_node(struct tag_node *tn); +char *pp_tag_ref(struct tag_ref *tr); +char *pp_tag_stat(struct tag_stat *ts); +char *pp_iface_stat(struct iface_stat *is); +char *pp_sock_tag(struct sock_tag *st); +char *pp_uid_tag_data(struct uid_tag_data *qtd); +char *pp_proc_qtu_data(struct proc_qtu_data *pqd); + +/*------------------------------------------*/ +void prdebug_sock_tag_list(int indent_level, + struct list_head *sock_tag_list); +void prdebug_sock_tag_tree(int indent_level, + struct rb_root *sock_tag_tree); +void prdebug_proc_qtu_data_tree(int indent_level, + struct rb_root *proc_qtu_data_tree); +void prdebug_tag_ref_tree(int indent_level, struct rb_root *tag_ref_tree); +void prdebug_uid_tag_data_tree(int indent_level, + struct rb_root *uid_tag_data_tree); +void prdebug_tag_stat_tree(int indent_level, + struct rb_root *tag_stat_tree); +void prdebug_iface_stat_list(int indent_level, + struct list_head *iface_stat_list); + +#else + +/*------------------------------------------*/ +static inline char *pp_tag_t(tag_t *tag) +{ + return NULL; +} +static inline char *pp_data_counters(struct data_counters *dc, bool showValues) +{ + return NULL; +} +static inline char *pp_tag_node(struct tag_node *tn) +{ + return NULL; +} +static inline char *pp_tag_ref(struct tag_ref *tr) +{ + return NULL; +} +static inline char *pp_tag_stat(struct tag_stat *ts) +{ + return NULL; +} +static inline char *pp_iface_stat(struct iface_stat *is) +{ + return NULL; +} +static inline char *pp_sock_tag(struct sock_tag *st) +{ + return NULL; +} +static inline char *pp_uid_tag_data(struct uid_tag_data *qtd) +{ + return NULL; +} +static inline char *pp_proc_qtu_data(struct proc_qtu_data *pqd) +{ + return NULL; +} + +/*------------------------------------------*/ +static inline +void prdebug_sock_tag_list(int indent_level, + struct list_head *sock_tag_list) +{ +} +static inline +void prdebug_sock_tag_tree(int indent_level, + struct rb_root *sock_tag_tree) +{ +} +static inline +void prdebug_proc_qtu_data_tree(int indent_level, + struct rb_root *proc_qtu_data_tree) +{ +} +static inline +void prdebug_tag_ref_tree(int indent_level, struct rb_root *tag_ref_tree) +{ +} +static inline +void prdebug_uid_tag_data_tree(int indent_level, + struct rb_root *uid_tag_data_tree) +{ +} +static inline +void prdebug_tag_stat_tree(int indent_level, + struct rb_root *tag_stat_tree) +{ +} +static inline +void prdebug_iface_stat_list(int indent_level, + struct list_head *iface_stat_list) +{ +} +#endif +/*------------------------------------------*/ +const char *netdev_evt_str(int netdev_event); +#endif /* ifndef __XT_QTAGUID_PRINT_H__ */ From 8893655ea57a066615aa1c69c8e57f116d6990d1 Mon Sep 17 00:00:00 2001 From: JP Abgrall Date: Tue, 17 Apr 2012 16:00:07 -0700 Subject: [PATCH 0185/1103] ANDROID: netfilter: xt_qtaguid: fix ipv6 protocol lookup When updating the stats for a given uid it would incorrectly assume IPV4 and pick up the wrong protocol when IPV6. Change-Id: Iea4a635012b4123bf7aa93809011b7b2040bb3d5 Signed-off-by: JP Abgrall --- net/netfilter/xt_qtaguid.c | 39 +++++++++++++++++++++++++++++++------- 1 file changed, 32 insertions(+), 7 deletions(-) diff --git a/net/netfilter/xt_qtaguid.c b/net/netfilter/xt_qtaguid.c index 3d3928291efb..2c1170f89d0f 100644 --- a/net/netfilter/xt_qtaguid.c +++ b/net/netfilter/xt_qtaguid.c @@ -26,6 +26,10 @@ #include #include +#if defined(CONFIG_IP6_NF_IPTABLES) || defined(CONFIG_IP6_NF_IPTABLES_MODULE) +#include +#endif + #include #include "xt_qtaguid_internal.h" #include "xt_qtaguid_print.h" @@ -1546,6 +1550,27 @@ static struct sock *qtaguid_find_sk(const struct sk_buff *skb, return sk; } +static int ipx_proto(const struct sk_buff *skb, + struct xt_action_param *par) +{ + int thoff = 0, tproto; + + switch (par->family) { + case NFPROTO_IPV6: + tproto = ipv6_find_hdr(skb, &thoff, -1, NULL, NULL); + if (tproto < 0) + MT_DEBUG("%s(): transport header not found in ipv6" + " skb=%p\n", __func__, skb); + break; + case NFPROTO_IPV4: + tproto = ip_hdr(skb)->protocol; + break; + default: + tproto = IPPROTO_RAW; + } + return tproto; +} + static void account_for_uid(const struct sk_buff *skb, const struct sock *alternate_sk, uid_t uid, struct xt_action_param *par) @@ -1572,15 +1597,15 @@ static void account_for_uid(const struct sk_buff *skb, } else if (unlikely(!el_dev->name)) { pr_info("qtaguid[%d]: no dev->name?!!\n", par->hooknum); } else { - MT_DEBUG("qtaguid[%d]: dev name=%s type=%d\n", - par->hooknum, - el_dev->name, - el_dev->type); + int proto = ipx_proto(skb, par); + MT_DEBUG("qtaguid[%d]: dev name=%s type=%d fam=%d proto=%d\n", + par->hooknum, el_dev->name, el_dev->type, + par->family, proto); if_tag_stat_update(el_dev->name, uid, skb->sk ? skb->sk : alternate_sk, par->in ? IFS_RX : IFS_TX, - ip_hdr(skb)->protocol, skb->len); + proto, skb->len); } } @@ -1625,8 +1650,8 @@ static bool qtaguid_mt(const struct sk_buff *skb, struct xt_action_param *par) } else { atomic64_inc(&qtu_events.match_found_sk); } - MT_DEBUG("qtaguid[%d]: sk=%p got_sock=%d proto=%d\n", - par->hooknum, sk, got_sock, ip_hdr(skb)->protocol); + MT_DEBUG("qtaguid[%d]: sk=%p got_sock=%d fam=%d proto=%d\n", + par->hooknum, sk, got_sock, par->family, ipx_proto(skb, par)); if (sk != NULL) { MT_DEBUG("qtaguid[%d]: sk=%p->sk_socket=%p->file=%p\n", par->hooknum, sk, sk->sk_socket, From e6c9234fab749ac77314d1632fd28bee1cd13eac Mon Sep 17 00:00:00 2001 From: JP Abgrall Date: Fri, 27 Apr 2012 12:57:39 -0700 Subject: [PATCH 0186/1103] ANDROID: netfilter: xt_qtaguid: start tracking iface rx/tx at low level qtaguid tracks the device stats by monitoring when it goes up and down, then it gets the dev_stats(). But devs don't correctly report stats (either they don't count headers symmetrically between rx/tx, or they count internal control messages). Now qtaguid counts the rx/tx bytes/packets during raw:prerouting and mangle:postrouting (nat is not available in ipv6). The results are in /proc/net/xt_qtaguid/iface_stat_fmt which outputs a format line (bash expansion): ifname total_skb_{rx,tx}_{bytes,packets} Added event counters for pre/post handling. Added extra ctrl_*() pid/uid debugging. Change-Id: Id84345d544ad1dd5f63e3842cab229e71d339297 Signed-off-by: JP Abgrall --- net/netfilter/xt_qtaguid.c | 277 ++++++++++++++++++++++------ net/netfilter/xt_qtaguid_internal.h | 5 +- net/netfilter/xt_qtaguid_print.c | 18 +- 3 files changed, 233 insertions(+), 67 deletions(-) diff --git a/net/netfilter/xt_qtaguid.c b/net/netfilter/xt_qtaguid.c index 2c1170f89d0f..9fd0ffa6c365 100644 --- a/net/netfilter/xt_qtaguid.c +++ b/net/netfilter/xt_qtaguid.c @@ -114,8 +114,15 @@ module_param_named(debug_mask, qtaguid_debug_mask, uint, S_IRUGO | S_IWUSR); /*---------------------------------------------------------------------------*/ static const char *iface_stat_procdirname = "iface_stat"; static struct proc_dir_entry *iface_stat_procdir; +/* + * The iface_stat_all* will go away once userspace gets use to the new fields + * that have a format line. + */ static const char *iface_stat_all_procfilename = "iface_stat_all"; static struct proc_dir_entry *iface_stat_all_procfile; +static const char *iface_stat_fmt_procfilename = "iface_stat_fmt"; +static struct proc_dir_entry *iface_stat_fmt_procfile; + /* * Ordering of locks: @@ -128,9 +135,9 @@ static struct proc_dir_entry *iface_stat_all_procfile; * Notice how sock_tag_list_lock is held sometimes when uid_tag_data_tree_lock * is acquired. * - * Call tree with all lock holders as of 2011-09-25: + * Call tree with all lock holders as of 2012-04-27: * - * iface_stat_all_proc_read() + * iface_stat_fmt_proc_read() * iface_stat_list_lock * (struct iface_stat) * @@ -781,13 +788,14 @@ static struct iface_stat *get_iface_entry(const char *ifname) return iface_entry; } -static int iface_stat_all_proc_read(char *page, char **num_items_returned, +static int iface_stat_fmt_proc_read(char *page, char **num_items_returned, off_t items_to_skip, int char_count, int *eof, void *data) { char *outp = page; int item_index = 0; int len; + int fmt = (int)data; /* The data is just 1 (old) or 2 (uses fmt) */ struct iface_stat *iface_entry; struct rtnl_link_stats64 dev_stats, *stats; struct rtnl_link_stats64 no_dev_stats = {0}; @@ -797,14 +805,32 @@ static int iface_stat_all_proc_read(char *page, char **num_items_returned, return 0; } - CT_DEBUG("qtaguid:proc iface_stat_all " + CT_DEBUG("qtaguid:proc iface_stat_fmt " + "pid=%u tgid=%u uid=%u " "page=%p *num_items_returned=%p off=%ld " - "char_count=%d *eof=%d\n", page, *num_items_returned, + "char_count=%d *eof=%d\n", + current->pid, current->tgid, current_fsuid(), + page, *num_items_returned, items_to_skip, char_count, *eof); if (*eof) return 0; + if (fmt == 2 && item_index++ >= items_to_skip) { + len = snprintf(outp, char_count, + "ifname " + "total_skb_rx_bytes total_skb_rx_packets " + "total_skb_tx_bytes total_skb_tx_packets\n" + ); + if (len >= char_count) { + *outp = '\0'; + return outp - page; + } + outp += len; + char_count -= len; + (*num_items_returned)++; + } + /* * This lock will prevent iface_stat_update() from changing active, * and in turn prevent an interface from unregistering itself. @@ -820,18 +846,37 @@ static int iface_stat_all_proc_read(char *page, char **num_items_returned, } else { stats = &no_dev_stats; } - len = snprintf(outp, char_count, - "%s %d " - "%llu %llu %llu %llu " - "%llu %llu %llu %llu\n", - iface_entry->ifname, - iface_entry->active, - iface_entry->totals[IFS_RX].bytes, - iface_entry->totals[IFS_RX].packets, - iface_entry->totals[IFS_TX].bytes, - iface_entry->totals[IFS_TX].packets, - stats->rx_bytes, stats->rx_packets, - stats->tx_bytes, stats->tx_packets); + /* + * If the meaning of the data changes, then update the fmtX + * string. + */ + if (fmt == 1) { + len = snprintf( + outp, char_count, + "%s %d " + "%llu %llu %llu %llu " + "%llu %llu %llu %llu\n", + iface_entry->ifname, + iface_entry->active, + iface_entry->totals_via_dev[IFS_RX].bytes, + iface_entry->totals_via_dev[IFS_RX].packets, + iface_entry->totals_via_dev[IFS_TX].bytes, + iface_entry->totals_via_dev[IFS_TX].packets, + stats->rx_bytes, stats->rx_packets, + stats->tx_bytes, stats->tx_packets + ); + } else { + len = snprintf( + outp, char_count, + "%s " + "%llu %llu %llu %llu\n", + iface_entry->ifname, + iface_entry->totals_via_skb[IFS_RX].bytes, + iface_entry->totals_via_skb[IFS_RX].packets, + iface_entry->totals_via_skb[IFS_TX].bytes, + iface_entry->totals_via_skb[IFS_TX].packets + ); + } if (len >= char_count) { spin_unlock_bh(&iface_stat_list_lock); *outp = '\0'; @@ -865,13 +910,17 @@ static void iface_create_proc_worker(struct work_struct *work) new_iface->proc_ptr = proc_entry; create_proc_read_entry("tx_bytes", proc_iface_perms, proc_entry, - read_proc_u64, &new_iface->totals[IFS_TX].bytes); + read_proc_u64, + &new_iface->totals_via_dev[IFS_TX].bytes); create_proc_read_entry("rx_bytes", proc_iface_perms, proc_entry, - read_proc_u64, &new_iface->totals[IFS_RX].bytes); + read_proc_u64, + &new_iface->totals_via_dev[IFS_RX].bytes); create_proc_read_entry("tx_packets", proc_iface_perms, proc_entry, - read_proc_u64, &new_iface->totals[IFS_TX].packets); + read_proc_u64, + &new_iface->totals_via_dev[IFS_TX].packets); create_proc_read_entry("rx_packets", proc_iface_perms, proc_entry, - read_proc_u64, &new_iface->totals[IFS_RX].packets); + read_proc_u64, + &new_iface->totals_via_dev[IFS_RX].packets); create_proc_read_entry("active", proc_iface_perms, proc_entry, read_proc_bool, &new_iface->active); @@ -975,11 +1024,13 @@ static void iface_check_stats_reset_and_adjust(struct net_device *net_dev, "iface reset its stats unexpectedly\n", __func__, net_dev->name); - iface->totals[IFS_TX].bytes += iface->last_known[IFS_TX].bytes; - iface->totals[IFS_TX].packets += + iface->totals_via_dev[IFS_TX].bytes += + iface->last_known[IFS_TX].bytes; + iface->totals_via_dev[IFS_TX].packets += iface->last_known[IFS_TX].packets; - iface->totals[IFS_RX].bytes += iface->last_known[IFS_RX].bytes; - iface->totals[IFS_RX].packets += + iface->totals_via_dev[IFS_RX].bytes += + iface->last_known[IFS_RX].bytes; + iface->totals_via_dev[IFS_RX].packets += iface->last_known[IFS_RX].packets; iface->last_known_valid = false; IF_DEBUG("qtaguid: %s(%s): iface=%p " @@ -1147,6 +1198,27 @@ static struct sock_tag *get_sock_stat(const struct sock *sk) return sock_tag_entry; } +static int ipx_proto(const struct sk_buff *skb, + struct xt_action_param *par) +{ + int thoff = 0, tproto; + + switch (par->family) { + case NFPROTO_IPV6: + tproto = ipv6_find_hdr(skb, &thoff, -1, NULL, NULL); + if (tproto < 0) + MT_DEBUG("%s(): transport header not found in ipv6" + " skb=%p\n", __func__, skb); + break; + case NFPROTO_IPV4: + tproto = ip_hdr(skb)->protocol; + break; + default: + tproto = IPPROTO_RAW; + } + return tproto; +} + static void data_counters_update(struct data_counters *dc, int set, enum ifs_tx_rx direction, int proto, int bytes) @@ -1207,10 +1279,10 @@ static void iface_stat_update(struct net_device *net_dev, bool stash_only) spin_unlock_bh(&iface_stat_list_lock); return; } - entry->totals[IFS_TX].bytes += stats->tx_bytes; - entry->totals[IFS_TX].packets += stats->tx_packets; - entry->totals[IFS_RX].bytes += stats->rx_bytes; - entry->totals[IFS_RX].packets += stats->rx_packets; + entry->totals_via_dev[IFS_TX].bytes += stats->tx_bytes; + entry->totals_via_dev[IFS_TX].packets += stats->tx_packets; + entry->totals_via_dev[IFS_RX].bytes += stats->rx_bytes; + entry->totals_via_dev[IFS_RX].packets += stats->rx_packets; /* We don't need the last_known[] anymore */ entry->last_known_valid = false; _iface_stat_set_active(entry, net_dev, false); @@ -1220,6 +1292,67 @@ static void iface_stat_update(struct net_device *net_dev, bool stash_only) spin_unlock_bh(&iface_stat_list_lock); } +/* + * Update stats for the specified interface from the skb. + * Do nothing if the entry + * does not exist (when a device was never configured with an IP address). + * Called on each sk. + */ +static void iface_stat_update_from_skb(const struct sk_buff *skb, + struct xt_action_param *par) +{ + struct iface_stat *entry; + const struct net_device *el_dev; + enum ifs_tx_rx direction = par->in ? IFS_RX : IFS_TX; + int bytes = skb->len; + + if (!skb->dev) { + MT_DEBUG("qtaguid[%d]: no skb->dev\n", par->hooknum); + el_dev = par->in ? : par->out; + } else { + const struct net_device *other_dev; + el_dev = skb->dev; + other_dev = par->in ? : par->out; + if (el_dev != other_dev) { + MT_DEBUG("qtaguid[%d]: skb->dev=%p %s vs " + "par->(in/out)=%p %s\n", + par->hooknum, el_dev, el_dev->name, other_dev, + other_dev->name); + } + } + + if (unlikely(!el_dev)) { + pr_err("qtaguid[%d]: %s(): no par->in/out?!!\n", + par->hooknum, __func__); + BUG(); + } else if (unlikely(!el_dev->name)) { + pr_err("qtaguid[%d]: %s(): no dev->name?!!\n", + par->hooknum, __func__); + BUG(); + } else { + int proto = ipx_proto(skb, par); + MT_DEBUG("qtaguid[%d]: dev name=%s type=%d fam=%d proto=%d\n", + par->hooknum, el_dev->name, el_dev->type, + par->family, proto); + } + + spin_lock_bh(&iface_stat_list_lock); + entry = get_iface_entry(el_dev->name); + if (entry == NULL) { + IF_DEBUG("qtaguid: iface_stat: %s(%s): not tracked\n", + __func__, el_dev->name); + spin_unlock_bh(&iface_stat_list_lock); + return; + } + + IF_DEBUG("qtaguid: %s(%s): entry=%p\n", __func__, + el_dev->name, entry); + + entry->totals_via_skb[direction].bytes += bytes; + entry->totals_via_skb[direction].packets++; + spin_unlock_bh(&iface_stat_list_lock); +} + static void tag_stat_update(struct tag_stat *tag_entry, enum ifs_tx_rx direction, int proto, int bytes) { @@ -1467,18 +1600,31 @@ static int __init iface_stat_init(struct proc_dir_entry *parent_procdir) parent_procdir); if (!iface_stat_all_procfile) { pr_err("qtaguid: iface_stat: init " - " failed to create stat_all proc entry\n"); + " failed to create stat_old proc entry\n"); err = -1; goto err_zap_entry; } - iface_stat_all_procfile->read_proc = iface_stat_all_proc_read; + iface_stat_all_procfile->read_proc = iface_stat_fmt_proc_read; + iface_stat_all_procfile->data = (void *)1; /* fmt1 */ + + iface_stat_fmt_procfile = create_proc_entry(iface_stat_fmt_procfilename, + proc_iface_perms, + parent_procdir); + if (!iface_stat_fmt_procfile) { + pr_err("qtaguid: iface_stat: init " + " failed to create stat_all proc entry\n"); + err = -1; + goto err_zap_all_stats_entry; + } + iface_stat_fmt_procfile->read_proc = iface_stat_fmt_proc_read; + iface_stat_fmt_procfile->data = (void *)2; /* fmt2 */ err = register_netdevice_notifier(&iface_netdev_notifier_blk); if (err) { pr_err("qtaguid: iface_stat: init " "failed to register dev event handler\n"); - goto err_zap_all_stats_entry; + goto err_zap_all_stats_entries; } err = register_inetaddr_notifier(&iface_inetaddr_notifier_blk); if (err) { @@ -1499,6 +1645,8 @@ static int __init iface_stat_init(struct proc_dir_entry *parent_procdir) unregister_inetaddr_notifier(&iface_inetaddr_notifier_blk); err_unreg_nd: unregister_netdevice_notifier(&iface_netdev_notifier_blk); +err_zap_all_stats_entries: + remove_proc_entry(iface_stat_fmt_procfilename, parent_procdir); err_zap_all_stats_entry: remove_proc_entry(iface_stat_all_procfilename, parent_procdir); err_zap_entry: @@ -1550,27 +1698,6 @@ static struct sock *qtaguid_find_sk(const struct sk_buff *skb, return sk; } -static int ipx_proto(const struct sk_buff *skb, - struct xt_action_param *par) -{ - int thoff = 0, tproto; - - switch (par->family) { - case NFPROTO_IPV6: - tproto = ipv6_find_hdr(skb, &thoff, -1, NULL, NULL); - if (tproto < 0) - MT_DEBUG("%s(): transport header not found in ipv6" - " skb=%p\n", __func__, skb); - break; - case NFPROTO_IPV4: - tproto = ip_hdr(skb)->protocol; - break; - default: - tproto = IPPROTO_RAW; - } - return tproto; -} - static void account_for_uid(const struct sk_buff *skb, const struct sock *alternate_sk, uid_t uid, struct xt_action_param *par) @@ -1630,8 +1757,22 @@ static bool qtaguid_mt(const struct sk_buff *skb, struct xt_action_param *par) goto ret_res; } - sk = skb->sk; + switch (par->hooknum) { + case NF_INET_PRE_ROUTING: + case NF_INET_POST_ROUTING: + atomic64_inc(&qtu_events.match_calls_prepost); + iface_stat_update_from_skb(skb, par); + /* + * We are done in pre/post. The skb will get processed + * further alter. + */ + res = (info->match ^ info->invert); + goto ret_res; + break; + /* default: Fall through and do UID releated work */ + } + sk = skb->sk; if (sk == NULL) { /* * A missing sk->sk_socket happens when packets are in-flight @@ -1806,8 +1947,10 @@ static int qtaguid_ctrl_proc_read(char *page, char **num_items_returned, if (*eof) return 0; - CT_DEBUG("qtaguid: proc ctrl page=%p off=%ld char_count=%d *eof=%d\n", - page, items_to_skip, char_count, *eof); + CT_DEBUG("qtaguid: proc ctrl pid=%u tgid=%u uid=%u " + "page=%p off=%ld char_count=%d *eof=%d\n", + current->pid, current->tgid, current_fsuid(), + page, items_to_skip, char_count, *eof); spin_lock_bh(&sock_tag_list_lock); for (node = rb_first(&sock_tag_tree); @@ -1851,6 +1994,7 @@ static int qtaguid_ctrl_proc_read(char *page, char **num_items_returned, "delete_cmds=%llu " "iface_events=%llu " "match_calls=%llu " + "match_calls_prepost=%llu " "match_found_sk=%llu " "match_found_sk_in_ct=%llu " "match_found_no_sk_in_ct=%llu " @@ -1862,6 +2006,7 @@ static int qtaguid_ctrl_proc_read(char *page, char **num_items_returned, atomic64_read(&qtu_events.delete_cmds), atomic64_read(&qtu_events.iface_events), atomic64_read(&qtu_events.match_calls), + atomic64_read(&qtu_events.match_calls_prepost), atomic64_read(&qtu_events.match_found_sk), atomic64_read(&qtu_events.match_found_sk_in_ct), atomic64_read( @@ -2135,7 +2280,9 @@ static int ctrl_cmd_tag(const char *input) el_socket = sockfd_lookup(sock_fd, &res); /* This locks the file */ if (!el_socket) { pr_info("qtaguid: ctrl_tag(%s): failed to lookup" - " sock_fd=%d err=%d\n", input, sock_fd, res); + " sock_fd=%d err=%d pid=%u tgid=%u uid=%u\n", + input, sock_fd, res, current->pid, current->tgid, + current_fsuid()); goto err; } CT_DEBUG("qtaguid: ctrl_tag(%s): socket->...->f_count=%ld ->sk=%p\n", @@ -2280,7 +2427,9 @@ static int ctrl_cmd_untag(const char *input) el_socket = sockfd_lookup(sock_fd, &res); /* This locks the file */ if (!el_socket) { pr_info("qtaguid: ctrl_untag(%s): failed to lookup" - " sock_fd=%d err=%d\n", input, sock_fd, res); + " sock_fd=%d err=%d pid=%u tgid=%u uid=%u\n", + input, sock_fd, res, current->pid, current->tgid, + current_fsuid()); goto err; } CT_DEBUG("qtaguid: ctrl_untag(%s): socket->...->f_count=%ld ->sk=%p\n", @@ -2356,6 +2505,9 @@ static int qtaguid_ctrl_parse(const char *input, int count) char cmd; int res; + CT_DEBUG("qtaguid: ctrl(%s): pid=%u tgid=%u uid=%u\n", + input, current->pid, current->tgid, current_fsuid()); + cmd = input[0]; /* Collect params for commands */ switch (cmd) { @@ -2532,9 +2684,12 @@ static int qtaguid_stats_proc_read(char *page, char **num_items_returned, return len; } - CT_DEBUG("qtaguid:proc stats page=%p *num_items_returned=%p off=%ld " - "char_count=%d *eof=%d\n", page, *num_items_returned, - items_to_skip, char_count, *eof); + CT_DEBUG("qtaguid:proc stats pid=%u tgid=%u uid=%u " + "page=%p *num_items_returned=%p off=%ld " + "char_count=%d *eof=%d\n", + current->pid, current->tgid, current_fsuid(), + page, *num_items_returned, + items_to_skip, char_count, *eof); if (*eof) return 0; diff --git a/net/netfilter/xt_qtaguid_internal.h b/net/netfilter/xt_qtaguid_internal.h index 02479d6d317d..d79f8383abf4 100644 --- a/net/netfilter/xt_qtaguid_internal.h +++ b/net/netfilter/xt_qtaguid_internal.h @@ -202,7 +202,8 @@ struct iface_stat { /* net_dev is only valid for active iface_stat */ struct net_device *net_dev; - struct byte_packet_counters totals[IFS_MAX_DIRECTIONS]; + struct byte_packet_counters totals_via_dev[IFS_MAX_DIRECTIONS]; + struct byte_packet_counters totals_via_skb[IFS_MAX_DIRECTIONS]; /* * We keep the last_known, because some devices reset their counters * just before NETDEV_UP, while some will reset just before @@ -254,6 +255,8 @@ struct qtaguid_event_counts { atomic64_t iface_events; /* Number of NETDEV_* events handled */ atomic64_t match_calls; /* Number of times iptables called mt */ + /* Number of times iptables called mt from pre or post routing hooks */ + atomic64_t match_calls_prepost; /* * match_found_sk_*: numbers related to the netfilter matching * function finding a sock for the sk_buff. diff --git a/net/netfilter/xt_qtaguid_print.c b/net/netfilter/xt_qtaguid_print.c index 39176785c91f..8cbd8e42bcc4 100644 --- a/net/netfilter/xt_qtaguid_print.c +++ b/net/netfilter/xt_qtaguid_print.c @@ -183,7 +183,11 @@ char *pp_iface_stat(struct iface_stat *is) res = kasprintf(GFP_ATOMIC, "iface_stat@%p{" "list=list_head{...}, " "ifname=%s, " - "total={rx={bytes=%llu, " + "total_dev={rx={bytes=%llu, " + "packets=%llu}, " + "tx={bytes=%llu, " + "packets=%llu}}, " + "total_skb={rx={bytes=%llu, " "packets=%llu}, " "tx={bytes=%llu, " "packets=%llu}}, " @@ -198,10 +202,14 @@ char *pp_iface_stat(struct iface_stat *is) "tag_stat_tree=rb_root{...}}", is, is->ifname, - is->totals[IFS_RX].bytes, - is->totals[IFS_RX].packets, - is->totals[IFS_TX].bytes, - is->totals[IFS_TX].packets, + is->totals_via_dev[IFS_RX].bytes, + is->totals_via_dev[IFS_RX].packets, + is->totals_via_dev[IFS_TX].bytes, + is->totals_via_dev[IFS_TX].packets, + is->totals_via_skb[IFS_RX].bytes, + is->totals_via_skb[IFS_RX].packets, + is->totals_via_skb[IFS_TX].bytes, + is->totals_via_skb[IFS_TX].packets, is->last_known_valid, is->last_known[IFS_RX].bytes, is->last_known[IFS_RX].packets, From e7dc81d0293c4f2c84e0bb41dc5022a88504503a Mon Sep 17 00:00:00 2001 From: JP Abgrall Date: Tue, 28 Aug 2012 16:53:32 -0700 Subject: [PATCH 0187/1103] ANDROID: netfilter: xt_qtaguid: report only uid tags to non-privileged processes In the past, a process could only see its own stats (uid-based summary, and details). Now we allow any process to see other UIDs uid-based stats, but still hide the detailed stats. Change-Id: I7666961ed244ac1d9359c339b048799e5db9facc Signed-off-by: JP Abgrall --- net/netfilter/xt_qtaguid.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/net/netfilter/xt_qtaguid.c b/net/netfilter/xt_qtaguid.c index 9fd0ffa6c365..14b003da1423 100644 --- a/net/netfilter/xt_qtaguid.c +++ b/net/netfilter/xt_qtaguid.c @@ -2588,8 +2588,9 @@ static int pp_stats_line(struct proc_print_info *ppi, int cnt_set) } else { tag_t tag = ppi->ts_entry->tn.tag; uid_t stat_uid = get_uid_from_tag(tag); - - if (!can_read_other_uid_stats(stat_uid)) { + /* Detailed tags are not available to everybody */ + if (get_atag_from_tag(tag) + && !can_read_other_uid_stats(stat_uid)) { CT_DEBUG("qtaguid: stats line: " "%s 0x%llx %u: insufficient priv " "from pid=%u tgid=%u uid=%u\n", From 1ed79a8002e4ea526b550f97816dab82c96f1ed9 Mon Sep 17 00:00:00 2001 From: JP Abgrall Date: Tue, 9 Oct 2012 20:38:21 -0700 Subject: [PATCH 0188/1103] ANDROID: netfilter: xt_qtaguid: fix error exit that would keep a spinlock. qtudev_open() could return with a uid_tag_data_tree_lock held when an kzalloc(..., GFP_ATOMIC) would fail. Very unlikely to get triggered AND survive the mayhem of running out of mem. Signed-off-by: JP Abgrall --- net/netfilter/xt_qtaguid.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/net/netfilter/xt_qtaguid.c b/net/netfilter/xt_qtaguid.c index 14b003da1423..6b22563a924f 100644 --- a/net/netfilter/xt_qtaguid.c +++ b/net/netfilter/xt_qtaguid.c @@ -2752,7 +2752,7 @@ static int qtudev_open(struct inode *inode, struct file *file) utd_entry = get_uid_data(current_fsuid(), &utd_entry_found); if (IS_ERR_OR_NULL(utd_entry)) { res = PTR_ERR(utd_entry); - goto err; + goto err_unlock; } /* Look for existing PID based proc_data */ @@ -2794,8 +2794,8 @@ static int qtudev_open(struct inode *inode, struct file *file) rb_erase(&utd_entry->node, &uid_tag_data_tree); kfree(utd_entry); } +err_unlock: spin_unlock_bh(&uid_tag_data_tree_lock); -err: return res; } From 1b4f990d30e87e411ec06b24a2dc907e7bd085dd Mon Sep 17 00:00:00 2001 From: Pontus Fuchs Date: Mon, 19 Nov 2012 11:44:51 -0800 Subject: [PATCH 0189/1103] ANDROID: netfilter: xt_qtaguid: Don't BUG_ON if create_if_tag_stat fails If create_if_tag_stat fails to allocate memory (GFP_ATOMIC) the following will happen: qtaguid: iface_stat: tag stat alloc failed ... kernel BUG at xt_qtaguid.c:1482! Signed-off-by: Pontus Fuchs --- net/netfilter/xt_qtaguid.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/net/netfilter/xt_qtaguid.c b/net/netfilter/xt_qtaguid.c index 6b22563a924f..603bdd206990 100644 --- a/net/netfilter/xt_qtaguid.c +++ b/net/netfilter/xt_qtaguid.c @@ -1461,6 +1461,8 @@ static void if_tag_stat_update(const char *ifname, uid_t uid, * - No {0, uid_tag} stats and no {acc_tag, uid_tag} stats. */ new_tag_stat = create_if_tag_stat(iface_entry, uid_tag); + if (!new_tag_stat) + goto unlock; uid_tag_counters = &new_tag_stat->counters; } else { uid_tag_counters = &tag_stat_entry->counters; @@ -1469,6 +1471,8 @@ static void if_tag_stat_update(const char *ifname, uid_t uid, if (acct_tag) { /* Create the child {acct_tag, uid_tag} and hook up parent. */ new_tag_stat = create_if_tag_stat(iface_entry, tag); + if (!new_tag_stat) + goto unlock; new_tag_stat->parent_counters = uid_tag_counters; } else { /* @@ -1482,6 +1486,7 @@ static void if_tag_stat_update(const char *ifname, uid_t uid, BUG_ON(!new_tag_stat); } tag_stat_update(new_tag_stat, direction, proto, bytes); +unlock: spin_unlock_bh(&iface_entry->tag_stat_list_lock); } From 8d8cd87efb52cd2e81914e5df5974ca657599751 Mon Sep 17 00:00:00 2001 From: JP Abgrall Date: Fri, 4 Jan 2013 18:18:36 -0800 Subject: [PATCH 0190/1103] ANDROID: netfilter: xt_qtaguid: remove AID_* dependency for access control qtaguid limits what can be done with /ctrl and /stats based on group membership. This changes removes AID_NET_BW_STATS and AID_NET_BW_ACCT, and picks up the groups from the gid of the matching proc entry files. Signed-off-by: JP Abgrall Change-Id: I42e477adde78a12ed5eb58fbc0b277cdaadb6f94 --- net/netfilter/xt_qtaguid.c | 51 +++++++++++++++++++------------------- 1 file changed, 26 insertions(+), 25 deletions(-) diff --git a/net/netfilter/xt_qtaguid.c b/net/netfilter/xt_qtaguid.c index 603bdd206990..923f1bdd02e8 100644 --- a/net/netfilter/xt_qtaguid.c +++ b/net/netfilter/xt_qtaguid.c @@ -53,25 +53,22 @@ static unsigned int proc_stats_perms = S_IRUGO; module_param_named(stats_perms, proc_stats_perms, uint, S_IRUGO | S_IWUSR); static struct proc_dir_entry *xt_qtaguid_ctrl_file; -#ifdef CONFIG_ANDROID_PARANOID_NETWORK + +/* Everybody can write. But proc_ctrl_write_limited is true by default which + * limits what can be controlled. See the can_*() functions. + */ static unsigned int proc_ctrl_perms = S_IRUGO | S_IWUGO; -#else -static unsigned int proc_ctrl_perms = S_IRUGO | S_IWUSR; -#endif module_param_named(ctrl_perms, proc_ctrl_perms, uint, S_IRUGO | S_IWUSR); -#ifdef CONFIG_ANDROID_PARANOID_NETWORK -#include -static gid_t proc_stats_readall_gid = AID_NET_BW_STATS; -static gid_t proc_ctrl_write_gid = AID_NET_BW_ACCT; -#else -/* 0 means, don't limit anybody */ -static gid_t proc_stats_readall_gid; -static gid_t proc_ctrl_write_gid; -#endif -module_param_named(stats_readall_gid, proc_stats_readall_gid, uint, +/* Limited by default, so the gid of the ctrl and stats proc entries + * will limit what can be done. See the can_*() functions. + */ +static bool proc_stats_readall_limited = true; +static bool proc_ctrl_write_limited = true; + +module_param_named(stats_readall_limited, proc_stats_readall_limited, bool, S_IRUGO | S_IWUSR); -module_param_named(ctrl_write_gid, proc_ctrl_write_gid, uint, +module_param_named(ctrl_write_limited, proc_ctrl_write_limited, bool, S_IRUGO | S_IWUSR); /* @@ -242,8 +239,9 @@ static struct qtaguid_event_counts qtu_events; static bool can_manipulate_uids(void) { /* root pwnd */ - return unlikely(!current_fsuid()) || unlikely(!proc_ctrl_write_gid) - || in_egroup_p(proc_ctrl_write_gid); + return in_egroup_p(xt_qtaguid_ctrl_file->gid) + || unlikely(!current_fsuid()) || unlikely(!proc_ctrl_write_limited) + || unlikely(current_fsuid() == xt_qtaguid_ctrl_file->uid); } static bool can_impersonate_uid(uid_t uid) @@ -254,9 +252,10 @@ static bool can_impersonate_uid(uid_t uid) static bool can_read_other_uid_stats(uid_t uid) { /* root pwnd */ - return unlikely(!current_fsuid()) || uid == current_fsuid() - || unlikely(!proc_stats_readall_gid) - || in_egroup_p(proc_stats_readall_gid); + return in_egroup_p(xt_qtaguid_stats_file->gid) + || unlikely(!current_fsuid()) || uid == current_fsuid() + || unlikely(!proc_stats_readall_limited) + || unlikely(current_fsuid() == xt_qtaguid_ctrl_file->uid); } static inline void dc_add_byte_packets(struct data_counters *counters, int set, @@ -2302,11 +2301,12 @@ static int ctrl_cmd_tag(const char *input) } CT_DEBUG("qtaguid: ctrl_tag(%s): " "pid=%u tgid=%u uid=%u euid=%u fsuid=%u " - "in_group=%d in_egroup=%d\n", + "ctrl.gid=%u in_group()=%d in_egroup()=%d\n", input, current->pid, current->tgid, current_uid(), current_euid(), current_fsuid(), - in_group_p(proc_ctrl_write_gid), - in_egroup_p(proc_ctrl_write_gid)); + xt_qtaguid_ctrl_file->gid, + in_group_p(xt_qtaguid_ctrl_file->gid), + in_egroup_p(xt_qtaguid_ctrl_file->gid)); if (argc < 4) { uid = current_fsuid(); } else if (!can_impersonate_uid(uid)) { @@ -2598,10 +2598,11 @@ static int pp_stats_line(struct proc_print_info *ppi, int cnt_set) && !can_read_other_uid_stats(stat_uid)) { CT_DEBUG("qtaguid: stats line: " "%s 0x%llx %u: insufficient priv " - "from pid=%u tgid=%u uid=%u\n", + "from pid=%u tgid=%u uid=%u stats.gid=%u\n", ppi->iface_entry->ifname, get_atag_from_tag(tag), stat_uid, - current->pid, current->tgid, current_fsuid()); + current->pid, current->tgid, current_fsuid(), + xt_qtaguid_stats_file->gid); return 0; } if (ppi->item_index++ < ppi->items_to_skip) From 9302fc46528e6581608e4714fc239353e3a53f87 Mon Sep 17 00:00:00 2001 From: JP Abgrall Date: Mon, 28 Jan 2013 16:50:44 -0800 Subject: [PATCH 0191/1103] ANDROID: netfilter: xt_qtaguid: extend iface stat to report protocols In the past the iface_stat_fmt would only show global bytes/packets for the skb-based numbers. For stall detection in userspace, distinguishing tcp vs other protocols makes it easier. Now we report ifname total_skb_rx_bytes total_skb_rx_packets total_skb_tx_bytes total_skb_tx_packets {rx,tx}_{tcp,udp,ohter}_{bytes,packets} Bug: 6818637 Signed-off-by: JP Abgrall --- net/netfilter/xt_qtaguid.c | 90 +++++++++++++++++------------ net/netfilter/xt_qtaguid_internal.h | 21 ++++++- net/netfilter/xt_qtaguid_print.c | 14 +++-- 3 files changed, 82 insertions(+), 43 deletions(-) diff --git a/net/netfilter/xt_qtaguid.c b/net/netfilter/xt_qtaguid.c index 923f1bdd02e8..92e5f80bd8fa 100644 --- a/net/netfilter/xt_qtaguid.c +++ b/net/netfilter/xt_qtaguid.c @@ -268,24 +268,6 @@ static inline void dc_add_byte_packets(struct data_counters *counters, int set, counters->bpc[set][direction][ifs_proto].packets += packets; } -static inline uint64_t dc_sum_bytes(struct data_counters *counters, - int set, - enum ifs_tx_rx direction) -{ - return counters->bpc[set][direction][IFS_TCP].bytes - + counters->bpc[set][direction][IFS_UDP].bytes - + counters->bpc[set][direction][IFS_PROTO_OTHER].bytes; -} - -static inline uint64_t dc_sum_packets(struct data_counters *counters, - int set, - enum ifs_tx_rx direction) -{ - return counters->bpc[set][direction][IFS_TCP].packets - + counters->bpc[set][direction][IFS_UDP].packets - + counters->bpc[set][direction][IFS_PROTO_OTHER].packets; -} - static struct tag_node *tag_node_tree_search(struct rb_root *root, tag_t tag) { struct rb_node *node = root->rb_node; @@ -787,6 +769,53 @@ static struct iface_stat *get_iface_entry(const char *ifname) return iface_entry; } +/* This is for fmt2 only */ +static int pp_iface_stat_line(bool header, char *outp, + int char_count, struct iface_stat *iface_entry) +{ + int len; + if (header) { + len = snprintf(outp, char_count, + "ifname " + "total_skb_rx_bytes total_skb_rx_packets " + "total_skb_tx_bytes total_skb_tx_packets " + "rx_tcp_bytes rx_tcp_packets " + "rx_udp_bytes rx_udp_packets " + "rx_other_bytes rx_other_packets " + "tx_tcp_bytes tx_tcp_packets " + "tx_udp_bytes tx_udp_packets " + "tx_other_bytes tx_other_packets\n" + ); + } else { + struct data_counters *cnts; + int cnt_set = 0; /* We only use one set for the device */ + cnts = &iface_entry->totals_via_skb; + len = snprintf( + outp, char_count, + "%s " + "%llu %llu %llu %llu %llu %llu %llu %llu " + "%llu %llu %llu %llu %llu %llu %llu %llu\n", + iface_entry->ifname, + dc_sum_bytes(cnts, cnt_set, IFS_RX), + dc_sum_packets(cnts, cnt_set, IFS_RX), + dc_sum_bytes(cnts, cnt_set, IFS_TX), + dc_sum_packets(cnts, cnt_set, IFS_TX), + cnts->bpc[cnt_set][IFS_RX][IFS_TCP].bytes, + cnts->bpc[cnt_set][IFS_RX][IFS_TCP].packets, + cnts->bpc[cnt_set][IFS_RX][IFS_UDP].bytes, + cnts->bpc[cnt_set][IFS_RX][IFS_UDP].packets, + cnts->bpc[cnt_set][IFS_RX][IFS_PROTO_OTHER].bytes, + cnts->bpc[cnt_set][IFS_RX][IFS_PROTO_OTHER].packets, + cnts->bpc[cnt_set][IFS_TX][IFS_TCP].bytes, + cnts->bpc[cnt_set][IFS_TX][IFS_TCP].packets, + cnts->bpc[cnt_set][IFS_TX][IFS_UDP].bytes, + cnts->bpc[cnt_set][IFS_TX][IFS_UDP].packets, + cnts->bpc[cnt_set][IFS_TX][IFS_PROTO_OTHER].bytes, + cnts->bpc[cnt_set][IFS_TX][IFS_PROTO_OTHER].packets); + } + return len; +} + static int iface_stat_fmt_proc_read(char *page, char **num_items_returned, off_t items_to_skip, int char_count, int *eof, void *data) @@ -816,11 +845,7 @@ static int iface_stat_fmt_proc_read(char *page, char **num_items_returned, return 0; if (fmt == 2 && item_index++ >= items_to_skip) { - len = snprintf(outp, char_count, - "ifname " - "total_skb_rx_bytes total_skb_rx_packets " - "total_skb_tx_bytes total_skb_tx_packets\n" - ); + len = pp_iface_stat_line(true, outp, char_count, NULL); if (len >= char_count) { *outp = '\0'; return outp - page; @@ -865,16 +890,8 @@ static int iface_stat_fmt_proc_read(char *page, char **num_items_returned, stats->tx_bytes, stats->tx_packets ); } else { - len = snprintf( - outp, char_count, - "%s " - "%llu %llu %llu %llu\n", - iface_entry->ifname, - iface_entry->totals_via_skb[IFS_RX].bytes, - iface_entry->totals_via_skb[IFS_RX].packets, - iface_entry->totals_via_skb[IFS_TX].bytes, - iface_entry->totals_via_skb[IFS_TX].packets - ); + len = pp_iface_stat_line(false, outp, char_count, + iface_entry); } if (len >= char_count) { spin_unlock_bh(&iface_stat_list_lock); @@ -1304,6 +1321,7 @@ static void iface_stat_update_from_skb(const struct sk_buff *skb, const struct net_device *el_dev; enum ifs_tx_rx direction = par->in ? IFS_RX : IFS_TX; int bytes = skb->len; + int proto; if (!skb->dev) { MT_DEBUG("qtaguid[%d]: no skb->dev\n", par->hooknum); @@ -1329,7 +1347,7 @@ static void iface_stat_update_from_skb(const struct sk_buff *skb, par->hooknum, __func__); BUG(); } else { - int proto = ipx_proto(skb, par); + proto = ipx_proto(skb, par); MT_DEBUG("qtaguid[%d]: dev name=%s type=%d fam=%d proto=%d\n", par->hooknum, el_dev->name, el_dev->type, par->family, proto); @@ -1347,8 +1365,8 @@ static void iface_stat_update_from_skb(const struct sk_buff *skb, IF_DEBUG("qtaguid: %s(%s): entry=%p\n", __func__, el_dev->name, entry); - entry->totals_via_skb[direction].bytes += bytes; - entry->totals_via_skb[direction].packets++; + data_counters_update(&entry->totals_via_skb, 0, direction, proto, + bytes); spin_unlock_bh(&iface_stat_list_lock); } diff --git a/net/netfilter/xt_qtaguid_internal.h b/net/netfilter/xt_qtaguid_internal.h index d79f8383abf4..6dc14a9c6889 100644 --- a/net/netfilter/xt_qtaguid_internal.h +++ b/net/netfilter/xt_qtaguid_internal.h @@ -179,6 +179,25 @@ struct data_counters { struct byte_packet_counters bpc[IFS_MAX_COUNTER_SETS][IFS_MAX_DIRECTIONS][IFS_MAX_PROTOS]; }; +static inline uint64_t dc_sum_bytes(struct data_counters *counters, + int set, + enum ifs_tx_rx direction) +{ + return counters->bpc[set][direction][IFS_TCP].bytes + + counters->bpc[set][direction][IFS_UDP].bytes + + counters->bpc[set][direction][IFS_PROTO_OTHER].bytes; +} + +static inline uint64_t dc_sum_packets(struct data_counters *counters, + int set, + enum ifs_tx_rx direction) +{ + return counters->bpc[set][direction][IFS_TCP].packets + + counters->bpc[set][direction][IFS_UDP].packets + + counters->bpc[set][direction][IFS_PROTO_OTHER].packets; +} + + /* Generic X based nodes used as a base for rb_tree ops */ struct tag_node { struct rb_node node; @@ -203,7 +222,7 @@ struct iface_stat { struct net_device *net_dev; struct byte_packet_counters totals_via_dev[IFS_MAX_DIRECTIONS]; - struct byte_packet_counters totals_via_skb[IFS_MAX_DIRECTIONS]; + struct data_counters totals_via_skb; /* * We keep the last_known, because some devices reset their counters * just before NETDEV_UP, while some will reset just before diff --git a/net/netfilter/xt_qtaguid_print.c b/net/netfilter/xt_qtaguid_print.c index 8cbd8e42bcc4..f6a00a3520ed 100644 --- a/net/netfilter/xt_qtaguid_print.c +++ b/net/netfilter/xt_qtaguid_print.c @@ -177,9 +177,10 @@ char *pp_tag_stat(struct tag_stat *ts) char *pp_iface_stat(struct iface_stat *is) { char *res; - if (!is) + if (!is) { res = kasprintf(GFP_ATOMIC, "iface_stat@null{}"); - else + } else { + struct data_counters *cnts = &is->totals_via_skb; res = kasprintf(GFP_ATOMIC, "iface_stat@%p{" "list=list_head{...}, " "ifname=%s, " @@ -206,10 +207,10 @@ char *pp_iface_stat(struct iface_stat *is) is->totals_via_dev[IFS_RX].packets, is->totals_via_dev[IFS_TX].bytes, is->totals_via_dev[IFS_TX].packets, - is->totals_via_skb[IFS_RX].bytes, - is->totals_via_skb[IFS_RX].packets, - is->totals_via_skb[IFS_TX].bytes, - is->totals_via_skb[IFS_TX].packets, + dc_sum_bytes(cnts, 0, IFS_RX), + dc_sum_packets(cnts, 0, IFS_RX), + dc_sum_bytes(cnts, 0, IFS_TX), + dc_sum_packets(cnts, 0, IFS_TX), is->last_known_valid, is->last_known[IFS_RX].bytes, is->last_known[IFS_RX].packets, @@ -218,6 +219,7 @@ char *pp_iface_stat(struct iface_stat *is) is->active, is->net_dev, is->proc_ptr); + } _bug_on_err_or_null(res); return res; } From 616f1965b07be433a4b45996f75b829dd4353532 Mon Sep 17 00:00:00 2001 From: JP Abgrall Date: Wed, 6 Feb 2013 17:40:07 -0800 Subject: [PATCH 0192/1103] ANDROID: netfilter: xt_qtaguid: Allow tracking loopback In the past it would always ignore interfaces with loopback addresses. Now we just treat them like any other. This also helps with writing tests that check for the presence of the qtaguid module. Signed-off-by: JP Abgrall --- net/netfilter/xt_qtaguid.c | 19 ++++--------------- 1 file changed, 4 insertions(+), 15 deletions(-) diff --git a/net/netfilter/xt_qtaguid.c b/net/netfilter/xt_qtaguid.c index 92e5f80bd8fa..992a6e044902 100644 --- a/net/netfilter/xt_qtaguid.c +++ b/net/netfilter/xt_qtaguid.c @@ -1108,18 +1108,13 @@ static void iface_stat_create(struct net_device *net_dev, spin_lock_bh(&iface_stat_list_lock); entry = get_iface_entry(ifname); if (entry != NULL) { - bool activate = !ipv4_is_loopback(ipaddr); IF_DEBUG("qtaguid: iface_stat: create(%s): entry=%p\n", ifname, entry); iface_check_stats_reset_and_adjust(net_dev, entry); - _iface_stat_set_active(entry, net_dev, activate); + _iface_stat_set_active(entry, net_dev, true); IF_DEBUG("qtaguid: %s(%s): " "tracking now %d on ip=%pI4\n", __func__, - entry->ifname, activate, &ipaddr); - goto done_unlock_put; - } else if (ipv4_is_loopback(ipaddr)) { - IF_DEBUG("qtaguid: iface_stat: create(%s): " - "ignore loopback dev. ip=%pI4\n", ifname, &ipaddr); + entry->ifname, true, &ipaddr); goto done_unlock_put; } @@ -1170,19 +1165,13 @@ static void iface_stat_create_ipv6(struct net_device *net_dev, spin_lock_bh(&iface_stat_list_lock); entry = get_iface_entry(ifname); if (entry != NULL) { - bool activate = !(addr_type & IPV6_ADDR_LOOPBACK); IF_DEBUG("qtaguid: %s(%s): entry=%p\n", __func__, ifname, entry); iface_check_stats_reset_and_adjust(net_dev, entry); - _iface_stat_set_active(entry, net_dev, activate); + _iface_stat_set_active(entry, net_dev, true); IF_DEBUG("qtaguid: %s(%s): " "tracking now %d on ip=%pI6c\n", __func__, - entry->ifname, activate, &ifa->addr); - goto done_unlock_put; - } else if (addr_type & IPV6_ADDR_LOOPBACK) { - IF_DEBUG("qtaguid: %s(%s): " - "ignore loopback dev. ip=%pI6c\n", __func__, - ifname, &ifa->addr); + entry->ifname, true, &ifa->addr); goto done_unlock_put; } From 18471d6b4b7018cac8a9f917fa17d9cb9dbcea97 Mon Sep 17 00:00:00 2001 From: JP Abgrall Date: Mon, 8 Apr 2013 15:09:26 -0700 Subject: [PATCH 0193/1103] ANDROID: netfilter: xt_qtaguid: rate limit some of the printks Some of the printks are in the packet handling path. We now ratelimit the very unlikely errors to avoid kmsg spamming. Signed-off-by: JP Abgrall --- net/netfilter/xt_qtaguid.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/net/netfilter/xt_qtaguid.c b/net/netfilter/xt_qtaguid.c index 992a6e044902..4ec6d23876c5 100644 --- a/net/netfilter/xt_qtaguid.c +++ b/net/netfilter/xt_qtaguid.c @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include @@ -1328,12 +1329,12 @@ static void iface_stat_update_from_skb(const struct sk_buff *skb, } if (unlikely(!el_dev)) { - pr_err("qtaguid[%d]: %s(): no par->in/out?!!\n", - par->hooknum, __func__); + pr_err_ratelimited("qtaguid[%d]: %s(): no par->in/out?!!\n", + par->hooknum, __func__); BUG(); } else if (unlikely(!el_dev->name)) { - pr_err("qtaguid[%d]: %s(): no dev->name?!!\n", - par->hooknum, __func__); + pr_err_ratelimited("qtaguid[%d]: %s(): no dev->name?!!\n", + par->hooknum, __func__); BUG(); } else { proto = ipx_proto(skb, par); @@ -1416,8 +1417,8 @@ static void if_tag_stat_update(const char *ifname, uid_t uid, iface_entry = get_iface_entry(ifname); if (!iface_entry) { - pr_err("qtaguid: iface_stat: stat_update() %s not found\n", - ifname); + pr_err_ratelimited("qtaguid: iface_stat: stat_update() " + "%s not found\n", ifname); return; } /* It is ok to process data when an iface_entry is inactive */ From 6adbffe6f6fac2620dc07e8438bd015564f798e1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arve=20Hj=C3=B8nnev=C3=A5g?= Date: Mon, 13 May 2013 20:45:02 -0700 Subject: [PATCH 0194/1103] ANDROID: netfilter: xt_qtaguid: 3.10 fixes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Stop using obsolete procfs api. Signed-off-by: Arve Hjønnevåg [AmitP: Folded following android-4.9 commit changes into this patch 564729173b12 ("netfilter: xt_qtaguid: fix memory leak in seq_file handlers") 85a2eb5b48fc ("ANDROID: netfilter: xt_qtaguid: 64-bit warning fixes")] Signed-off-by: Amit Pundir --- net/netfilter/xt_qtaguid.c | 1063 ++++++++++++++++++------------------ 1 file changed, 539 insertions(+), 524 deletions(-) diff --git a/net/netfilter/xt_qtaguid.c b/net/netfilter/xt_qtaguid.c index 4ec6d23876c5..505dbab40210 100644 --- a/net/netfilter/xt_qtaguid.c +++ b/net/netfilter/xt_qtaguid.c @@ -20,6 +20,7 @@ #include #include #include +#include #include #include #include @@ -34,6 +35,7 @@ #include #include "xt_qtaguid_internal.h" #include "xt_qtaguid_print.h" +#include "../../fs/proc/internal.h" /* * We only use the xt_socket funcs within a similar context to avoid unexpected @@ -122,104 +124,6 @@ static const char *iface_stat_fmt_procfilename = "iface_stat_fmt"; static struct proc_dir_entry *iface_stat_fmt_procfile; -/* - * Ordering of locks: - * outer locks: - * iface_stat_list_lock - * sock_tag_list_lock - * inner locks: - * uid_tag_data_tree_lock - * tag_counter_set_list_lock - * Notice how sock_tag_list_lock is held sometimes when uid_tag_data_tree_lock - * is acquired. - * - * Call tree with all lock holders as of 2012-04-27: - * - * iface_stat_fmt_proc_read() - * iface_stat_list_lock - * (struct iface_stat) - * - * qtaguid_ctrl_proc_read() - * sock_tag_list_lock - * (sock_tag_tree) - * (struct proc_qtu_data->sock_tag_list) - * prdebug_full_state() - * sock_tag_list_lock - * (sock_tag_tree) - * uid_tag_data_tree_lock - * (uid_tag_data_tree) - * (proc_qtu_data_tree) - * iface_stat_list_lock - * - * qtaguid_stats_proc_read() - * iface_stat_list_lock - * struct iface_stat->tag_stat_list_lock - * - * qtudev_open() - * uid_tag_data_tree_lock - * - * qtudev_release() - * sock_tag_data_list_lock - * uid_tag_data_tree_lock - * prdebug_full_state() - * sock_tag_list_lock - * uid_tag_data_tree_lock - * iface_stat_list_lock - * - * iface_netdev_event_handler() - * iface_stat_create() - * iface_stat_list_lock - * iface_stat_update() - * iface_stat_list_lock - * - * iface_inetaddr_event_handler() - * iface_stat_create() - * iface_stat_list_lock - * iface_stat_update() - * iface_stat_list_lock - * - * iface_inet6addr_event_handler() - * iface_stat_create_ipv6() - * iface_stat_list_lock - * iface_stat_update() - * iface_stat_list_lock - * - * qtaguid_mt() - * account_for_uid() - * if_tag_stat_update() - * get_sock_stat() - * sock_tag_list_lock - * struct iface_stat->tag_stat_list_lock - * tag_stat_update() - * get_active_counter_set() - * tag_counter_set_list_lock - * tag_stat_update() - * get_active_counter_set() - * tag_counter_set_list_lock - * - * - * qtaguid_ctrl_parse() - * ctrl_cmd_delete() - * sock_tag_list_lock - * tag_counter_set_list_lock - * iface_stat_list_lock - * struct iface_stat->tag_stat_list_lock - * uid_tag_data_tree_lock - * ctrl_cmd_counter_set() - * tag_counter_set_list_lock - * ctrl_cmd_tag() - * sock_tag_list_lock - * (sock_tag_tree) - * get_tag_ref() - * uid_tag_data_tree_lock - * (uid_tag_data_tree) - * uid_tag_data_tree_lock - * (proc_qtu_data_tree) - * ctrl_cmd_untag() - * sock_tag_list_lock - * uid_tag_data_tree_lock - * - */ static LIST_HEAD(iface_stat_list); static DEFINE_SPINLOCK(iface_stat_list_lock); @@ -690,42 +594,26 @@ static void put_tag_ref_tree(tag_t full_tag, struct uid_tag_data *utd_entry) } } -static int read_proc_u64(char *page, char **start, off_t off, - int count, int *eof, void *data) +static ssize_t read_proc_u64(struct file *file, char __user *buf, + size_t size, loff_t *ppos) { - int len; - uint64_t value; - char *p = page; - uint64_t *iface_entry = data; + uint64_t *valuep = PDE_DATA(file_inode(file)); + char tmp[24]; + size_t tmp_size; - if (!data) - return 0; - - value = *iface_entry; - p += sprintf(p, "%llu\n", value); - len = (p - page) - off; - *eof = (len <= count) ? 1 : 0; - *start = page + off; - return len; + tmp_size = scnprintf(tmp, sizeof(tmp), "%llu\n", *valuep); + return simple_read_from_buffer(buf, size, ppos, tmp, tmp_size); } -static int read_proc_bool(char *page, char **start, off_t off, - int count, int *eof, void *data) +static ssize_t read_proc_bool(struct file *file, char __user *buf, + size_t size, loff_t *ppos) { - int len; - bool value; - char *p = page; - bool *bool_entry = data; - - if (!data) - return 0; + bool *valuep = PDE_DATA(file_inode(file)); + char tmp[24]; + size_t tmp_size; - value = *bool_entry; - p += sprintf(p, "%u\n", value); - len = (p - page) - off; - *eof = (len <= count) ? 1 : 0; - *start = page + off; - return len; + tmp_size = scnprintf(tmp, sizeof(tmp), "%u\n", *valuep); + return simple_read_from_buffer(buf, size, ppos, tmp, tmp_size); } static int get_active_counter_set(tag_t tag) @@ -771,144 +659,132 @@ static struct iface_stat *get_iface_entry(const char *ifname) } /* This is for fmt2 only */ -static int pp_iface_stat_line(bool header, char *outp, - int char_count, struct iface_stat *iface_entry) -{ - int len; - if (header) { - len = snprintf(outp, char_count, - "ifname " - "total_skb_rx_bytes total_skb_rx_packets " - "total_skb_tx_bytes total_skb_tx_packets " - "rx_tcp_bytes rx_tcp_packets " - "rx_udp_bytes rx_udp_packets " - "rx_other_bytes rx_other_packets " - "tx_tcp_bytes tx_tcp_packets " - "tx_udp_bytes tx_udp_packets " - "tx_other_bytes tx_other_packets\n" - ); - } else { - struct data_counters *cnts; - int cnt_set = 0; /* We only use one set for the device */ - cnts = &iface_entry->totals_via_skb; - len = snprintf( - outp, char_count, - "%s " - "%llu %llu %llu %llu %llu %llu %llu %llu " - "%llu %llu %llu %llu %llu %llu %llu %llu\n", - iface_entry->ifname, - dc_sum_bytes(cnts, cnt_set, IFS_RX), - dc_sum_packets(cnts, cnt_set, IFS_RX), - dc_sum_bytes(cnts, cnt_set, IFS_TX), - dc_sum_packets(cnts, cnt_set, IFS_TX), - cnts->bpc[cnt_set][IFS_RX][IFS_TCP].bytes, - cnts->bpc[cnt_set][IFS_RX][IFS_TCP].packets, - cnts->bpc[cnt_set][IFS_RX][IFS_UDP].bytes, - cnts->bpc[cnt_set][IFS_RX][IFS_UDP].packets, - cnts->bpc[cnt_set][IFS_RX][IFS_PROTO_OTHER].bytes, - cnts->bpc[cnt_set][IFS_RX][IFS_PROTO_OTHER].packets, - cnts->bpc[cnt_set][IFS_TX][IFS_TCP].bytes, - cnts->bpc[cnt_set][IFS_TX][IFS_TCP].packets, - cnts->bpc[cnt_set][IFS_TX][IFS_UDP].bytes, - cnts->bpc[cnt_set][IFS_TX][IFS_UDP].packets, - cnts->bpc[cnt_set][IFS_TX][IFS_PROTO_OTHER].bytes, - cnts->bpc[cnt_set][IFS_TX][IFS_PROTO_OTHER].packets); - } - return len; -} - -static int iface_stat_fmt_proc_read(char *page, char **num_items_returned, - off_t items_to_skip, int char_count, - int *eof, void *data) -{ - char *outp = page; - int item_index = 0; - int len; - int fmt = (int)data; /* The data is just 1 (old) or 2 (uses fmt) */ - struct iface_stat *iface_entry; - struct rtnl_link_stats64 dev_stats, *stats; - struct rtnl_link_stats64 no_dev_stats = {0}; - - if (unlikely(module_passive)) { - *eof = 1; - return 0; - } - - CT_DEBUG("qtaguid:proc iface_stat_fmt " - "pid=%u tgid=%u uid=%u " - "page=%p *num_items_returned=%p off=%ld " - "char_count=%d *eof=%d\n", - current->pid, current->tgid, current_fsuid(), - page, *num_items_returned, - items_to_skip, char_count, *eof); +static void pp_iface_stat_header(struct seq_file *m) +{ + seq_puts(m, + "ifname " + "total_skb_rx_bytes total_skb_rx_packets " + "total_skb_tx_bytes total_skb_tx_packets " + "rx_tcp_bytes rx_tcp_packets " + "rx_udp_bytes rx_udp_packets " + "rx_other_bytes rx_other_packets " + "tx_tcp_bytes tx_tcp_packets " + "tx_udp_bytes tx_udp_packets " + "tx_other_bytes tx_other_packets\n" + ); +} - if (*eof) - return 0; +static void pp_iface_stat_line(struct seq_file *m, + struct iface_stat *iface_entry) +{ + struct data_counters *cnts; + int cnt_set = 0; /* We only use one set for the device */ + cnts = &iface_entry->totals_via_skb; + seq_printf(m, "%s %llu %llu %llu %llu %llu %llu %llu %llu " + "%llu %llu %llu %llu %llu %llu %llu %llu\n", + iface_entry->ifname, + dc_sum_bytes(cnts, cnt_set, IFS_RX), + dc_sum_packets(cnts, cnt_set, IFS_RX), + dc_sum_bytes(cnts, cnt_set, IFS_TX), + dc_sum_packets(cnts, cnt_set, IFS_TX), + cnts->bpc[cnt_set][IFS_RX][IFS_TCP].bytes, + cnts->bpc[cnt_set][IFS_RX][IFS_TCP].packets, + cnts->bpc[cnt_set][IFS_RX][IFS_UDP].bytes, + cnts->bpc[cnt_set][IFS_RX][IFS_UDP].packets, + cnts->bpc[cnt_set][IFS_RX][IFS_PROTO_OTHER].bytes, + cnts->bpc[cnt_set][IFS_RX][IFS_PROTO_OTHER].packets, + cnts->bpc[cnt_set][IFS_TX][IFS_TCP].bytes, + cnts->bpc[cnt_set][IFS_TX][IFS_TCP].packets, + cnts->bpc[cnt_set][IFS_TX][IFS_UDP].bytes, + cnts->bpc[cnt_set][IFS_TX][IFS_UDP].packets, + cnts->bpc[cnt_set][IFS_TX][IFS_PROTO_OTHER].bytes, + cnts->bpc[cnt_set][IFS_TX][IFS_PROTO_OTHER].packets); +} + +struct proc_iface_stat_fmt_info { + int fmt; +}; - if (fmt == 2 && item_index++ >= items_to_skip) { - len = pp_iface_stat_line(true, outp, char_count, NULL); - if (len >= char_count) { - *outp = '\0'; - return outp - page; - } - outp += len; - char_count -= len; - (*num_items_returned)++; - } +static void *iface_stat_fmt_proc_start(struct seq_file *m, loff_t *pos) +{ + struct proc_iface_stat_fmt_info *p = m->private; + loff_t n = *pos; /* * This lock will prevent iface_stat_update() from changing active, * and in turn prevent an interface from unregistering itself. */ spin_lock_bh(&iface_stat_list_lock); - list_for_each_entry(iface_entry, &iface_stat_list, list) { - if (item_index++ < items_to_skip) - continue; - if (iface_entry->active) { - stats = dev_get_stats(iface_entry->net_dev, - &dev_stats); - } else { - stats = &no_dev_stats; - } - /* - * If the meaning of the data changes, then update the fmtX - * string. - */ - if (fmt == 1) { - len = snprintf( - outp, char_count, - "%s %d " - "%llu %llu %llu %llu " - "%llu %llu %llu %llu\n", - iface_entry->ifname, - iface_entry->active, - iface_entry->totals_via_dev[IFS_RX].bytes, - iface_entry->totals_via_dev[IFS_RX].packets, - iface_entry->totals_via_dev[IFS_TX].bytes, - iface_entry->totals_via_dev[IFS_TX].packets, - stats->rx_bytes, stats->rx_packets, - stats->tx_bytes, stats->tx_packets - ); - } else { - len = pp_iface_stat_line(false, outp, char_count, - iface_entry); - } - if (len >= char_count) { - spin_unlock_bh(&iface_stat_list_lock); - *outp = '\0'; - return outp - page; - } - outp += len; - char_count -= len; - (*num_items_returned)++; - } + if (unlikely(module_passive)) + return NULL; + + if (!n && p->fmt == 2) + pp_iface_stat_header(m); + + return seq_list_start(&iface_stat_list, n); +} + +static void *iface_stat_fmt_proc_next(struct seq_file *m, void *p, loff_t *pos) +{ + return seq_list_next(p, &iface_stat_list, pos); +} + +static void iface_stat_fmt_proc_stop(struct seq_file *m, void *p) +{ spin_unlock_bh(&iface_stat_list_lock); +} - *eof = 1; - return outp - page; +static int iface_stat_fmt_proc_show(struct seq_file *m, void *v) +{ + struct proc_iface_stat_fmt_info *p = m->private; + struct iface_stat *iface_entry; + struct rtnl_link_stats64 dev_stats, *stats; + struct rtnl_link_stats64 no_dev_stats = {0}; + + + CT_DEBUG("qtaguid:proc iface_stat_fmt pid=%u tgid=%u uid=%u\n", + current->pid, current->tgid, current_fsuid()); + + iface_entry = list_entry(v, struct iface_stat, list); + + if (iface_entry->active) { + stats = dev_get_stats(iface_entry->net_dev, + &dev_stats); + } else { + stats = &no_dev_stats; + } + /* + * If the meaning of the data changes, then update the fmtX + * string. + */ + if (p->fmt == 1) { + seq_printf(m, "%s %d %llu %llu %llu %llu %llu %llu %llu %llu\n", + iface_entry->ifname, + iface_entry->active, + iface_entry->totals_via_dev[IFS_RX].bytes, + iface_entry->totals_via_dev[IFS_RX].packets, + iface_entry->totals_via_dev[IFS_TX].bytes, + iface_entry->totals_via_dev[IFS_TX].packets, + stats->rx_bytes, stats->rx_packets, + stats->tx_bytes, stats->tx_packets + ); + } else { + pp_iface_stat_line(m, iface_entry); + } + return 0; } +static const struct file_operations read_u64_fops = { + .read = read_proc_u64, + .llseek = default_llseek, +}; + +static const struct file_operations read_bool_fops = { + .read = read_proc_bool, + .llseek = default_llseek, +}; + static void iface_create_proc_worker(struct work_struct *work) { struct proc_dir_entry *proc_entry; @@ -926,20 +802,20 @@ static void iface_create_proc_worker(struct work_struct *work) new_iface->proc_ptr = proc_entry; - create_proc_read_entry("tx_bytes", proc_iface_perms, proc_entry, - read_proc_u64, - &new_iface->totals_via_dev[IFS_TX].bytes); - create_proc_read_entry("rx_bytes", proc_iface_perms, proc_entry, - read_proc_u64, - &new_iface->totals_via_dev[IFS_RX].bytes); - create_proc_read_entry("tx_packets", proc_iface_perms, proc_entry, - read_proc_u64, - &new_iface->totals_via_dev[IFS_TX].packets); - create_proc_read_entry("rx_packets", proc_iface_perms, proc_entry, - read_proc_u64, - &new_iface->totals_via_dev[IFS_RX].packets); - create_proc_read_entry("active", proc_iface_perms, proc_entry, - read_proc_bool, &new_iface->active); + proc_create_data("tx_bytes", proc_iface_perms, proc_entry, + &read_u64_fops, + &new_iface->totals_via_dev[IFS_TX].bytes); + proc_create_data("rx_bytes", proc_iface_perms, proc_entry, + &read_u64_fops, + &new_iface->totals_via_dev[IFS_RX].bytes); + proc_create_data("tx_packets", proc_iface_perms, proc_entry, + &read_u64_fops, + &new_iface->totals_via_dev[IFS_TX].packets); + proc_create_data("rx_packets", proc_iface_perms, proc_entry, + &read_u64_fops, + &new_iface->totals_via_dev[IFS_RX].packets); + proc_create_data("active", proc_iface_perms, proc_entry, + &read_bool_fops, &new_iface->active); IF_DEBUG("qtaguid: iface_stat: create_proc(): done " "entry=%p dev=%s\n", new_iface, new_iface->ifname); @@ -1596,6 +1472,33 @@ static struct notifier_block iface_inet6addr_notifier_blk = { .notifier_call = iface_inet6addr_event_handler, }; +static const struct seq_operations iface_stat_fmt_proc_seq_ops = { + .start = iface_stat_fmt_proc_start, + .next = iface_stat_fmt_proc_next, + .stop = iface_stat_fmt_proc_stop, + .show = iface_stat_fmt_proc_show, +}; + +static int proc_iface_stat_fmt_open(struct inode *inode, struct file *file) +{ + struct proc_iface_stat_fmt_info *s; + + s = __seq_open_private(file, &iface_stat_fmt_proc_seq_ops, + sizeof(struct proc_iface_stat_fmt_info)); + if (!s) + return -ENOMEM; + + s->fmt = (uintptr_t)PDE_DATA(inode); + return 0; +} + +static const struct file_operations proc_iface_stat_fmt_fops = { + .open = proc_iface_stat_fmt_open, + .read = seq_read, + .llseek = seq_lseek, + .release = seq_release_private, +}; + static int __init iface_stat_init(struct proc_dir_entry *parent_procdir) { int err; @@ -1607,29 +1510,29 @@ static int __init iface_stat_init(struct proc_dir_entry *parent_procdir) goto err; } - iface_stat_all_procfile = create_proc_entry(iface_stat_all_procfilename, - proc_iface_perms, - parent_procdir); + iface_stat_all_procfile = proc_create_data(iface_stat_all_procfilename, + proc_iface_perms, + parent_procdir, + &proc_iface_stat_fmt_fops, + (void *)1 /* fmt1 */); if (!iface_stat_all_procfile) { pr_err("qtaguid: iface_stat: init " " failed to create stat_old proc entry\n"); err = -1; goto err_zap_entry; } - iface_stat_all_procfile->read_proc = iface_stat_fmt_proc_read; - iface_stat_all_procfile->data = (void *)1; /* fmt1 */ - iface_stat_fmt_procfile = create_proc_entry(iface_stat_fmt_procfilename, - proc_iface_perms, - parent_procdir); + iface_stat_fmt_procfile = proc_create_data(iface_stat_fmt_procfilename, + proc_iface_perms, + parent_procdir, + &proc_iface_stat_fmt_fops, + (void *)2 /* fmt2 */); if (!iface_stat_fmt_procfile) { pr_err("qtaguid: iface_stat: init " " failed to create stat_all proc entry\n"); err = -1; goto err_zap_all_stats_entry; } - iface_stat_fmt_procfile->read_proc = iface_stat_fmt_proc_read; - iface_stat_fmt_procfile->data = (void *)2; /* fmt2 */ err = register_netdevice_notifier(&iface_netdev_notifier_blk); @@ -1934,43 +1837,85 @@ static void prdebug_full_state(int indent_level, const char *fmt, ...) static void prdebug_full_state(int indent_level, const char *fmt, ...) {} #endif +struct proc_ctrl_print_info { + struct sock *sk; /* socket found by reading to sk_pos */ + loff_t sk_pos; +}; + +static void *qtaguid_ctrl_proc_next(struct seq_file *m, void *v, loff_t *pos) +{ + struct proc_ctrl_print_info *pcpi = m->private; + struct sock_tag *sock_tag_entry = v; + struct rb_node *node; + + (*pos)++; + + if (!v || v == SEQ_START_TOKEN) + return NULL; + + node = rb_next(&sock_tag_entry->sock_node); + if (!node) { + pcpi->sk = NULL; + sock_tag_entry = SEQ_START_TOKEN; + } else { + sock_tag_entry = rb_entry(node, struct sock_tag, sock_node); + pcpi->sk = sock_tag_entry->sk; + } + pcpi->sk_pos = *pos; + return sock_tag_entry; +} + +static void *qtaguid_ctrl_proc_start(struct seq_file *m, loff_t *pos) +{ + struct proc_ctrl_print_info *pcpi = m->private; + struct sock_tag *sock_tag_entry; + struct rb_node *node; + + spin_lock_bh(&sock_tag_list_lock); + + if (unlikely(module_passive)) + return NULL; + + if (*pos == 0) { + pcpi->sk_pos = 0; + node = rb_first(&sock_tag_tree); + if (!node) { + pcpi->sk = NULL; + return SEQ_START_TOKEN; + } + sock_tag_entry = rb_entry(node, struct sock_tag, sock_node); + pcpi->sk = sock_tag_entry->sk; + } else { + sock_tag_entry = (pcpi->sk ? get_sock_stat_nl(pcpi->sk) : + NULL) ?: SEQ_START_TOKEN; + if (*pos != pcpi->sk_pos) { + /* seq_read skipped a next call */ + *pos = pcpi->sk_pos; + return qtaguid_ctrl_proc_next(m, sock_tag_entry, pos); + } + } + return sock_tag_entry; +} + +static void qtaguid_ctrl_proc_stop(struct seq_file *m, void *v) +{ + spin_unlock_bh(&sock_tag_list_lock); +} + /* * Procfs reader to get all active socket tags using style "1)" as described in * fs/proc/generic.c */ -static int qtaguid_ctrl_proc_read(char *page, char **num_items_returned, - off_t items_to_skip, int char_count, int *eof, - void *data) +static int qtaguid_ctrl_proc_show(struct seq_file *m, void *v) { - char *outp = page; - int len; + struct sock_tag *sock_tag_entry = v; uid_t uid; - struct rb_node *node; - struct sock_tag *sock_tag_entry; - int item_index = 0; - int indent_level = 0; long f_count; - if (unlikely(module_passive)) { - *eof = 1; - return 0; - } - - if (*eof) - return 0; - - CT_DEBUG("qtaguid: proc ctrl pid=%u tgid=%u uid=%u " - "page=%p off=%ld char_count=%d *eof=%d\n", - current->pid, current->tgid, current_fsuid(), - page, items_to_skip, char_count, *eof); + CT_DEBUG("qtaguid: proc ctrl pid=%u tgid=%u uid=%u\n", + current->pid, current->tgid, current_fsuid()); - spin_lock_bh(&sock_tag_list_lock); - for (node = rb_first(&sock_tag_tree); - node; - node = rb_next(node)) { - if (item_index++ < items_to_skip) - continue; - sock_tag_entry = rb_entry(node, struct sock_tag, sock_node); + if (sock_tag_entry != SEQ_START_TOKEN) { uid = get_uid_from_tag(sock_tag_entry->tag); CT_DEBUG("qtaguid: proc_read(): sk=%p tag=0x%llx (uid=%u) " "pid=%u\n", @@ -1981,66 +1926,42 @@ static int qtaguid_ctrl_proc_read(char *page, char **num_items_returned, ); f_count = atomic_long_read( &sock_tag_entry->socket->file->f_count); - len = snprintf(outp, char_count, - "sock=%p tag=0x%llx (uid=%u) pid=%u " - "f_count=%lu\n", - sock_tag_entry->sk, - sock_tag_entry->tag, uid, - sock_tag_entry->pid, f_count); - if (len >= char_count) { - spin_unlock_bh(&sock_tag_list_lock); - *outp = '\0'; - return outp - page; - } - outp += len; - char_count -= len; - (*num_items_returned)++; - } - spin_unlock_bh(&sock_tag_list_lock); - - if (item_index++ >= items_to_skip) { - len = snprintf(outp, char_count, - "events: sockets_tagged=%llu " - "sockets_untagged=%llu " - "counter_set_changes=%llu " - "delete_cmds=%llu " - "iface_events=%llu " - "match_calls=%llu " - "match_calls_prepost=%llu " - "match_found_sk=%llu " - "match_found_sk_in_ct=%llu " - "match_found_no_sk_in_ct=%llu " - "match_no_sk=%llu " - "match_no_sk_file=%llu\n", - atomic64_read(&qtu_events.sockets_tagged), - atomic64_read(&qtu_events.sockets_untagged), - atomic64_read(&qtu_events.counter_set_changes), - atomic64_read(&qtu_events.delete_cmds), - atomic64_read(&qtu_events.iface_events), - atomic64_read(&qtu_events.match_calls), - atomic64_read(&qtu_events.match_calls_prepost), - atomic64_read(&qtu_events.match_found_sk), - atomic64_read(&qtu_events.match_found_sk_in_ct), - atomic64_read( - &qtu_events.match_found_no_sk_in_ct), - atomic64_read(&qtu_events.match_no_sk), - atomic64_read(&qtu_events.match_no_sk_file)); - if (len >= char_count) { - *outp = '\0'; - return outp - page; - } - outp += len; - char_count -= len; - (*num_items_returned)++; - } - - /* Count the following as part of the last item_index */ - if (item_index > items_to_skip) { - prdebug_full_state(indent_level, "proc ctrl"); + seq_printf(m, "sock=%p tag=0x%llx (uid=%u) pid=%u " + "f_count=%lu\n", + sock_tag_entry->sk, + sock_tag_entry->tag, uid, + sock_tag_entry->pid, f_count); + } else { + seq_printf(m, "events: sockets_tagged=%llu " + "sockets_untagged=%llu " + "counter_set_changes=%llu " + "delete_cmds=%llu " + "iface_events=%llu " + "match_calls=%llu " + "match_calls_prepost=%llu " + "match_found_sk=%llu " + "match_found_sk_in_ct=%llu " + "match_found_no_sk_in_ct=%llu " + "match_no_sk=%llu " + "match_no_sk_file=%llu\n", + atomic64_read(&qtu_events.sockets_tagged), + atomic64_read(&qtu_events.sockets_untagged), + atomic64_read(&qtu_events.counter_set_changes), + atomic64_read(&qtu_events.delete_cmds), + atomic64_read(&qtu_events.iface_events), + atomic64_read(&qtu_events.match_calls), + atomic64_read(&qtu_events.match_calls_prepost), + atomic64_read(&qtu_events.match_found_sk), + atomic64_read(&qtu_events.match_found_sk_in_ct), + atomic64_read(&qtu_events.match_found_no_sk_in_ct), + atomic64_read(&qtu_events.match_no_sk), + atomic64_read(&qtu_events.match_no_sk_file)); + + /* Count the following as part of the last item_index */ + prdebug_full_state(0, "proc ctrl"); } - *eof = 1; - return outp - page; + return 0; } /* @@ -2513,10 +2434,10 @@ static int ctrl_cmd_untag(const char *input) return res; } -static int qtaguid_ctrl_parse(const char *input, int count) +static ssize_t qtaguid_ctrl_parse(const char *input, size_t count) { char cmd; - int res; + ssize_t res; CT_DEBUG("qtaguid: ctrl(%s): pid=%u tgid=%u uid=%u\n", input, current->pid, current->tgid, current_fsuid()); @@ -2547,13 +2468,13 @@ static int qtaguid_ctrl_parse(const char *input, int count) if (!res) res = count; err: - CT_DEBUG("qtaguid: ctrl(%s): res=%d\n", input, res); + CT_DEBUG("qtaguid: ctrl(%s): res=%zd\n", input, res); return res; } #define MAX_QTAGUID_CTRL_INPUT_LEN 255 -static int qtaguid_ctrl_proc_write(struct file *file, const char __user *buffer, - unsigned long count, void *data) +static ssize_t qtaguid_ctrl_proc_write(struct file *file, const char __user *buffer, + size_t count, loff_t *offp) { char input_buf[MAX_QTAGUID_CTRL_INPUT_LEN]; @@ -2571,178 +2492,230 @@ static int qtaguid_ctrl_proc_write(struct file *file, const char __user *buffer, } struct proc_print_info { - char *outp; - char **num_items_returned; struct iface_stat *iface_entry; - struct tag_stat *ts_entry; int item_index; - int items_to_skip; - int char_count; + tag_t tag; /* tag found by reading to tag_pos */ + off_t tag_pos; + int tag_item_index; }; -static int pp_stats_line(struct proc_print_info *ppi, int cnt_set) +static void pp_stats_header(struct seq_file *m) { - int len; - struct data_counters *cnts; + seq_puts(m, + "idx iface acct_tag_hex uid_tag_int cnt_set " + "rx_bytes rx_packets " + "tx_bytes tx_packets " + "rx_tcp_bytes rx_tcp_packets " + "rx_udp_bytes rx_udp_packets " + "rx_other_bytes rx_other_packets " + "tx_tcp_bytes tx_tcp_packets " + "tx_udp_bytes tx_udp_packets " + "tx_other_bytes tx_other_packets\n"); +} - if (!ppi->item_index) { - if (ppi->item_index++ < ppi->items_to_skip) - return 0; - len = snprintf(ppi->outp, ppi->char_count, - "idx iface acct_tag_hex uid_tag_int cnt_set " - "rx_bytes rx_packets " - "tx_bytes tx_packets " - "rx_tcp_bytes rx_tcp_packets " - "rx_udp_bytes rx_udp_packets " - "rx_other_bytes rx_other_packets " - "tx_tcp_bytes tx_tcp_packets " - "tx_udp_bytes tx_udp_packets " - "tx_other_bytes tx_other_packets\n"); - } else { - tag_t tag = ppi->ts_entry->tn.tag; - uid_t stat_uid = get_uid_from_tag(tag); - /* Detailed tags are not available to everybody */ - if (get_atag_from_tag(tag) - && !can_read_other_uid_stats(stat_uid)) { - CT_DEBUG("qtaguid: stats line: " - "%s 0x%llx %u: insufficient priv " - "from pid=%u tgid=%u uid=%u stats.gid=%u\n", - ppi->iface_entry->ifname, - get_atag_from_tag(tag), stat_uid, - current->pid, current->tgid, current_fsuid(), - xt_qtaguid_stats_file->gid); - return 0; - } - if (ppi->item_index++ < ppi->items_to_skip) - return 0; - cnts = &ppi->ts_entry->counters; - len = snprintf( - ppi->outp, ppi->char_count, - "%d %s 0x%llx %u %u " - "%llu %llu " - "%llu %llu " - "%llu %llu " - "%llu %llu " - "%llu %llu " - "%llu %llu " - "%llu %llu " - "%llu %llu\n", - ppi->item_index, - ppi->iface_entry->ifname, - get_atag_from_tag(tag), - stat_uid, - cnt_set, - dc_sum_bytes(cnts, cnt_set, IFS_RX), - dc_sum_packets(cnts, cnt_set, IFS_RX), - dc_sum_bytes(cnts, cnt_set, IFS_TX), - dc_sum_packets(cnts, cnt_set, IFS_TX), - cnts->bpc[cnt_set][IFS_RX][IFS_TCP].bytes, - cnts->bpc[cnt_set][IFS_RX][IFS_TCP].packets, - cnts->bpc[cnt_set][IFS_RX][IFS_UDP].bytes, - cnts->bpc[cnt_set][IFS_RX][IFS_UDP].packets, - cnts->bpc[cnt_set][IFS_RX][IFS_PROTO_OTHER].bytes, - cnts->bpc[cnt_set][IFS_RX][IFS_PROTO_OTHER].packets, - cnts->bpc[cnt_set][IFS_TX][IFS_TCP].bytes, - cnts->bpc[cnt_set][IFS_TX][IFS_TCP].packets, - cnts->bpc[cnt_set][IFS_TX][IFS_UDP].bytes, - cnts->bpc[cnt_set][IFS_TX][IFS_UDP].packets, - cnts->bpc[cnt_set][IFS_TX][IFS_PROTO_OTHER].bytes, - cnts->bpc[cnt_set][IFS_TX][IFS_PROTO_OTHER].packets); - } - return len; -} - -static bool pp_sets(struct proc_print_info *ppi) -{ - int len; +static int pp_stats_line(struct seq_file *m, struct tag_stat *ts_entry, + int cnt_set) +{ + int ret; + struct data_counters *cnts; + tag_t tag = ts_entry->tn.tag; + uid_t stat_uid = get_uid_from_tag(tag); + struct proc_print_info *ppi = m->private; + /* Detailed tags are not available to everybody */ + if (get_atag_from_tag(tag) && !can_read_other_uid_stats(stat_uid)) { + CT_DEBUG("qtaguid: stats line: " + "%s 0x%llx %u: insufficient priv " + "from pid=%u tgid=%u uid=%u stats.gid=%u\n", + ppi->iface_entry->ifname, + get_atag_from_tag(tag), stat_uid, + current->pid, current->tgid, current_fsuid(), + xt_qtaguid_stats_file->gid); + return 0; + } + ppi->item_index++; + cnts = &ts_entry->counters; + ret = seq_printf(m, "%d %s 0x%llx %u %u " + "%llu %llu " + "%llu %llu " + "%llu %llu " + "%llu %llu " + "%llu %llu " + "%llu %llu " + "%llu %llu " + "%llu %llu\n", + ppi->item_index, + ppi->iface_entry->ifname, + get_atag_from_tag(tag), + stat_uid, + cnt_set, + dc_sum_bytes(cnts, cnt_set, IFS_RX), + dc_sum_packets(cnts, cnt_set, IFS_RX), + dc_sum_bytes(cnts, cnt_set, IFS_TX), + dc_sum_packets(cnts, cnt_set, IFS_TX), + cnts->bpc[cnt_set][IFS_RX][IFS_TCP].bytes, + cnts->bpc[cnt_set][IFS_RX][IFS_TCP].packets, + cnts->bpc[cnt_set][IFS_RX][IFS_UDP].bytes, + cnts->bpc[cnt_set][IFS_RX][IFS_UDP].packets, + cnts->bpc[cnt_set][IFS_RX][IFS_PROTO_OTHER].bytes, + cnts->bpc[cnt_set][IFS_RX][IFS_PROTO_OTHER].packets, + cnts->bpc[cnt_set][IFS_TX][IFS_TCP].bytes, + cnts->bpc[cnt_set][IFS_TX][IFS_TCP].packets, + cnts->bpc[cnt_set][IFS_TX][IFS_UDP].bytes, + cnts->bpc[cnt_set][IFS_TX][IFS_UDP].packets, + cnts->bpc[cnt_set][IFS_TX][IFS_PROTO_OTHER].bytes, + cnts->bpc[cnt_set][IFS_TX][IFS_PROTO_OTHER].packets); + return ret ?: 1; +} + +static bool pp_sets(struct seq_file *m, struct tag_stat *ts_entry) +{ + int ret; int counter_set; for (counter_set = 0; counter_set < IFS_MAX_COUNTER_SETS; counter_set++) { - len = pp_stats_line(ppi, counter_set); - if (len >= ppi->char_count) { - *ppi->outp = '\0'; + ret = pp_stats_line(m, ts_entry, counter_set); + if (ret < 0) return false; - } - if (len) { - ppi->outp += len; - ppi->char_count -= len; - (*ppi->num_items_returned)++; - } } return true; } -/* - * Procfs reader to get all tag stats using style "1)" as described in - * fs/proc/generic.c - * Groups all protocols tx/rx bytes. - */ -static int qtaguid_stats_proc_read(char *page, char **num_items_returned, - off_t items_to_skip, int char_count, int *eof, - void *data) -{ - struct proc_print_info ppi; - int len; - - ppi.outp = page; - ppi.item_index = 0; - ppi.char_count = char_count; - ppi.num_items_returned = num_items_returned; - ppi.items_to_skip = items_to_skip; - - if (unlikely(module_passive)) { - len = pp_stats_line(&ppi, 0); - /* The header should always be shorter than the buffer. */ - BUG_ON(len >= ppi.char_count); - (*num_items_returned)++; - *eof = 1; - return len; - } - - CT_DEBUG("qtaguid:proc stats pid=%u tgid=%u uid=%u " - "page=%p *num_items_returned=%p off=%ld " - "char_count=%d *eof=%d\n", - current->pid, current->tgid, current_fsuid(), - page, *num_items_returned, - items_to_skip, char_count, *eof); - - if (*eof) - return 0; +static int qtaguid_stats_proc_iface_stat_ptr_valid(struct iface_stat *ptr) +{ + struct iface_stat *iface_entry; + + if (!ptr) + return false; - /* The idx is there to help debug when things go belly up. */ - len = pp_stats_line(&ppi, 0); - /* Don't advance the outp unless the whole line was printed */ - if (len >= ppi.char_count) { - *ppi.outp = '\0'; - return ppi.outp - page; + list_for_each_entry(iface_entry, &iface_stat_list, list) + if (iface_entry == ptr) + return true; + return false; +} + +static void qtaguid_stats_proc_next_iface_entry(struct proc_print_info *ppi) +{ + spin_unlock_bh(&ppi->iface_entry->tag_stat_list_lock); + list_for_each_entry_continue(ppi->iface_entry, &iface_stat_list, list) { + spin_lock_bh(&ppi->iface_entry->tag_stat_list_lock); + return; } - if (len) { - ppi.outp += len; - ppi.char_count -= len; - (*num_items_returned)++; + ppi->iface_entry = NULL; +} + +static void *qtaguid_stats_proc_next(struct seq_file *m, void *v, loff_t *pos) +{ + struct proc_print_info *ppi = m->private; + struct tag_stat *ts_entry; + struct rb_node *node; + + if (!v) { + pr_err("qtaguid: %s(): unexpected v: NULL\n", __func__); + return NULL; } + (*pos)++; + + if (!ppi->iface_entry || unlikely(module_passive)) + return NULL; + + if (v == SEQ_START_TOKEN) + node = rb_first(&ppi->iface_entry->tag_stat_tree); + else + node = rb_next(&((struct tag_stat *)v)->tn.node); + + while (!node) { + qtaguid_stats_proc_next_iface_entry(ppi); + if (!ppi->iface_entry) + return NULL; + node = rb_first(&ppi->iface_entry->tag_stat_tree); + } + + ts_entry = rb_entry(node, struct tag_stat, tn.node); + ppi->tag = ts_entry->tn.tag; + ppi->tag_pos = *pos; + ppi->tag_item_index = ppi->item_index; + return ts_entry; +} + +static void *qtaguid_stats_proc_start(struct seq_file *m, loff_t *pos) +{ + struct proc_print_info *ppi = m->private; + struct tag_stat *ts_entry = NULL; + spin_lock_bh(&iface_stat_list_lock); - list_for_each_entry(ppi.iface_entry, &iface_stat_list, list) { - struct rb_node *node; - spin_lock_bh(&ppi.iface_entry->tag_stat_list_lock); - for (node = rb_first(&ppi.iface_entry->tag_stat_tree); - node; - node = rb_next(node)) { - ppi.ts_entry = rb_entry(node, struct tag_stat, tn.node); - if (!pp_sets(&ppi)) { - spin_unlock_bh( - &ppi.iface_entry->tag_stat_list_lock); - spin_unlock_bh(&iface_stat_list_lock); - return ppi.outp - page; - } + + if (*pos == 0) { + ppi->item_index = 1; + ppi->tag_pos = 0; + if (list_empty(&iface_stat_list)) { + ppi->iface_entry = NULL; + } else { + ppi->iface_entry = list_first_entry(&iface_stat_list, + struct iface_stat, + list); + spin_lock_bh(&ppi->iface_entry->tag_stat_list_lock); + } + return SEQ_START_TOKEN; + } + if (!qtaguid_stats_proc_iface_stat_ptr_valid(ppi->iface_entry)) { + if (ppi->iface_entry) { + pr_err("qtaguid: %s(): iface_entry %p not found\n", + __func__, ppi->iface_entry); + ppi->iface_entry = NULL; + } + return NULL; + } + + spin_lock_bh(&ppi->iface_entry->tag_stat_list_lock); + + if (!ppi->tag_pos) { + /* seq_read skipped first next call */ + ts_entry = SEQ_START_TOKEN; + } else { + ts_entry = tag_stat_tree_search( + &ppi->iface_entry->tag_stat_tree, ppi->tag); + if (!ts_entry) { + pr_info("qtaguid: %s(): tag_stat.tag 0x%llx not found. Abort.\n", + __func__, ppi->tag); + return NULL; } - spin_unlock_bh(&ppi.iface_entry->tag_stat_list_lock); } + + if (*pos == ppi->tag_pos) { /* normal resume */ + ppi->item_index = ppi->tag_item_index; + } else { + /* seq_read skipped a next call */ + *pos = ppi->tag_pos; + ts_entry = qtaguid_stats_proc_next(m, ts_entry, pos); + } + + return ts_entry; +} + +static void qtaguid_stats_proc_stop(struct seq_file *m, void *v) +{ + struct proc_print_info *ppi = m->private; + if (ppi->iface_entry) + spin_unlock_bh(&ppi->iface_entry->tag_stat_list_lock); spin_unlock_bh(&iface_stat_list_lock); +} - *eof = 1; - return ppi.outp - page; +/* + * Procfs reader to get all tag stats using style "1)" as described in + * fs/proc/generic.c + * Groups all protocols tx/rx bytes. + */ +static int qtaguid_stats_proc_show(struct seq_file *m, void *v) +{ + struct tag_stat *ts_entry = v; + + if (v == SEQ_START_TOKEN) + pp_stats_header(m); + else + pp_sets(m, ts_entry); + + return 0; } /*------------------------------------------*/ @@ -2907,6 +2880,47 @@ static struct miscdevice qtu_device = { /* How sad it doesn't allow for defaults: .mode = S_IRUGO | S_IWUSR */ }; +static const struct seq_operations proc_qtaguid_ctrl_seqops = { + .start = qtaguid_ctrl_proc_start, + .next = qtaguid_ctrl_proc_next, + .stop = qtaguid_ctrl_proc_stop, + .show = qtaguid_ctrl_proc_show, +}; + +static int proc_qtaguid_ctrl_open(struct inode *inode, struct file *file) +{ + return seq_open_private(file, &proc_qtaguid_ctrl_seqops, + sizeof(struct proc_ctrl_print_info)); +} + +static const struct file_operations proc_qtaguid_ctrl_fops = { + .open = proc_qtaguid_ctrl_open, + .read = seq_read, + .write = qtaguid_ctrl_proc_write, + .llseek = seq_lseek, + .release = seq_release_private, +}; + +static const struct seq_operations proc_qtaguid_stats_seqops = { + .start = qtaguid_stats_proc_start, + .next = qtaguid_stats_proc_next, + .stop = qtaguid_stats_proc_stop, + .show = qtaguid_stats_proc_show, +}; + +static int proc_qtaguid_stats_open(struct inode *inode, struct file *file) +{ + return seq_open_private(file, &proc_qtaguid_stats_seqops, + sizeof(struct proc_print_info)); +} + +static const struct file_operations proc_qtaguid_stats_fops = { + .open = proc_qtaguid_stats_open, + .read = seq_read, + .llseek = seq_lseek, + .release = seq_release_private, +}; + /*------------------------------------------*/ static int __init qtaguid_proc_register(struct proc_dir_entry **res_procdir) { @@ -2918,26 +2932,27 @@ static int __init qtaguid_proc_register(struct proc_dir_entry **res_procdir) goto no_dir; } - xt_qtaguid_ctrl_file = create_proc_entry("ctrl", proc_ctrl_perms, - *res_procdir); + xt_qtaguid_ctrl_file = proc_create_data("ctrl", proc_ctrl_perms, + *res_procdir, + &proc_qtaguid_ctrl_fops, + NULL); if (!xt_qtaguid_ctrl_file) { pr_err("qtaguid: failed to create xt_qtaguid/ctrl " " file\n"); ret = -ENOMEM; goto no_ctrl_entry; } - xt_qtaguid_ctrl_file->read_proc = qtaguid_ctrl_proc_read; - xt_qtaguid_ctrl_file->write_proc = qtaguid_ctrl_proc_write; - xt_qtaguid_stats_file = create_proc_entry("stats", proc_stats_perms, - *res_procdir); + xt_qtaguid_stats_file = proc_create_data("stats", proc_stats_perms, + *res_procdir, + &proc_qtaguid_stats_fops, + NULL); if (!xt_qtaguid_stats_file) { pr_err("qtaguid: failed to create xt_qtaguid/stats " "file\n"); ret = -ENOMEM; goto no_stats_entry; } - xt_qtaguid_stats_file->read_proc = qtaguid_stats_proc_read; /* * TODO: add support counter hacking * xt_qtaguid_stats_file->write_proc = qtaguid_stats_proc_write; From 8dc810a4b10c61a398a76e866977eb518b316ecb Mon Sep 17 00:00:00 2001 From: JP Abgrall Date: Wed, 20 Feb 2013 16:38:34 -0800 Subject: [PATCH 0195/1103] ANDROID: netfilter: xt_qtaguid: fix bad tcp_time_wait sock handling Since (41063e9 ipv4: Early TCP socket demux), skb's can have an sk which is not a struct sock but the smaller struct inet_timewait_sock without an sk->sk_socket. Now we bypass sk_state == TCP_TIME_WAIT Signed-off-by: JP Abgrall --- net/netfilter/xt_qtaguid.c | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/net/netfilter/xt_qtaguid.c b/net/netfilter/xt_qtaguid.c index 505dbab40210..eee667987f7d 100644 --- a/net/netfilter/xt_qtaguid.c +++ b/net/netfilter/xt_qtaguid.c @@ -1597,14 +1597,13 @@ static struct sock *qtaguid_find_sk(const struct sk_buff *skb, return NULL; } - /* - * Seems to be issues on the file ptr for TCP_TIME_WAIT SKs. - * http://kerneltrap.org/mailarchive/linux-netdev/2010/10/21/6287959 - * Not fixed in 3.0-r3 :( - */ if (sk) { MT_DEBUG("qtaguid: %p->sk_proto=%u " "->sk_state=%d\n", sk, sk->sk_protocol, sk->sk_state); + /* + * When in TCP_TIME_WAIT the sk is not a "struct sock" but + * "struct inet_timewait_sock" which is missing fields. + */ if (sk->sk_state == TCP_TIME_WAIT) { xt_socket_put_sk(sk); sk = NULL; @@ -1688,6 +1687,13 @@ static bool qtaguid_mt(const struct sk_buff *skb, struct xt_action_param *par) } sk = skb->sk; + /* + * When in TCP_TIME_WAIT the sk is not a "struct sock" but + * "struct inet_timewait_sock" which is missing fields. + * So we ignore it. + */ + if (sk && sk->sk_state == TCP_TIME_WAIT) + sk = NULL; if (sk == NULL) { /* * A missing sk->sk_socket happens when packets are in-flight From ad60cb48bc6ea88c5a0629973c60652d21167a8b Mon Sep 17 00:00:00 2001 From: "Jon Medhurst (Tixy)" Date: Mon, 14 Apr 2014 21:20:49 -0700 Subject: [PATCH 0196/1103] ANDROID: netfilter: xt_qtaguid: Fix boot panic We need the change below because of mainline commit 351638e7de (net: pass info struct via netdevice notifier). Otherwise we panic. Change-Id: I7daf7513a733933fdcbaeebea7f8191f8b6a0432 Signed-off-by: John Stultz --- net/netfilter/xt_qtaguid.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/netfilter/xt_qtaguid.c b/net/netfilter/xt_qtaguid.c index eee667987f7d..4f574a6fc1fb 100644 --- a/net/netfilter/xt_qtaguid.c +++ b/net/netfilter/xt_qtaguid.c @@ -1375,7 +1375,7 @@ static void if_tag_stat_update(const char *ifname, uid_t uid, static int iface_netdev_event_handler(struct notifier_block *nb, unsigned long event, void *ptr) { - struct net_device *dev = ptr; + struct net_device *dev = netdev_notifier_info_to_dev(ptr); if (unlikely(module_passive)) return NOTIFY_DONE; From 0c3eb06fc093a05b8170a1677605ca8c1395c479 Mon Sep 17 00:00:00 2001 From: John Stultz Date: Fri, 28 Mar 2014 16:23:48 -0700 Subject: [PATCH 0197/1103] ANDROID: netfilter: xt_qtaguid/xt_socket: Build fixups Fix up build kuid/kguid build issues in netfilter code. Also re-add the xt_socket_get/put_sk interfaces needed by xt_qtaguid. Change-Id: I7027fb840e109785bddffe8ea717b8d018b26d82 Signed-off-by: John Stultz [AmitP: Folded following android-4.9 commit changes into this patch da5ea99a74f2 ("ANDROID: netfilter: xt_qtaguid: fix seq_printf type mismatch warning") 070eff8f023c ("ANDROID: netfilter: xt_qtaguid: fix broken uid/gid range check")] 2879b6ec24ee ("ANDROID: xt_qtaguid: use sock_gen_put() instead of xt_socket_put_sk()")] Signed-off-by: Amit Pundir --- include/uapi/linux/netfilter/xt_socket.h | 5 + net/netfilter/xt_qtaguid.c | 149 +++++++++++++---------- 2 files changed, 87 insertions(+), 67 deletions(-) diff --git a/include/uapi/linux/netfilter/xt_socket.h b/include/uapi/linux/netfilter/xt_socket.h index a7bdc9d882b0..5075d2e3ae30 100644 --- a/include/uapi/linux/netfilter/xt_socket.h +++ b/include/uapi/linux/netfilter/xt_socket.h @@ -27,4 +27,9 @@ struct xt_socket_mtinfo3 { | XT_SOCKET_NOWILDCARD \ | XT_SOCKET_RESTORESKMARK) +struct sock *xt_socket_get4_sk(const struct sk_buff *skb, + struct xt_action_param *par); +struct sock *xt_socket_get6_sk(const struct sk_buff *skb, + struct xt_action_param *par); + #endif /* _XT_SOCKET_H */ diff --git a/net/netfilter/xt_qtaguid.c b/net/netfilter/xt_qtaguid.c index 4f574a6fc1fb..cd2da5460db2 100644 --- a/net/netfilter/xt_qtaguid.c +++ b/net/netfilter/xt_qtaguid.c @@ -145,22 +145,22 @@ static bool can_manipulate_uids(void) { /* root pwnd */ return in_egroup_p(xt_qtaguid_ctrl_file->gid) - || unlikely(!current_fsuid()) || unlikely(!proc_ctrl_write_limited) - || unlikely(current_fsuid() == xt_qtaguid_ctrl_file->uid); + || unlikely(!from_kuid(&init_user_ns, current_fsuid())) || unlikely(!proc_ctrl_write_limited) + || unlikely(uid_eq(current_fsuid(), xt_qtaguid_ctrl_file->uid)); } -static bool can_impersonate_uid(uid_t uid) +static bool can_impersonate_uid(kuid_t uid) { - return uid == current_fsuid() || can_manipulate_uids(); + return uid_eq(uid, current_fsuid()) || can_manipulate_uids(); } -static bool can_read_other_uid_stats(uid_t uid) +static bool can_read_other_uid_stats(kuid_t uid) { /* root pwnd */ return in_egroup_p(xt_qtaguid_stats_file->gid) - || unlikely(!current_fsuid()) || uid == current_fsuid() + || unlikely(!from_kuid(&init_user_ns, current_fsuid())) || uid_eq(uid, current_fsuid()) || unlikely(!proc_stats_readall_limited) - || unlikely(current_fsuid() == xt_qtaguid_ctrl_file->uid); + || unlikely(uid_eq(current_fsuid(), xt_qtaguid_ctrl_file->uid)); } static inline void dc_add_byte_packets(struct data_counters *counters, int set, @@ -542,7 +542,7 @@ static void put_utd_entry(struct uid_tag_data *utd_entry) "erase utd_entry=%p uid=%u " "by pid=%u tgid=%u uid=%u\n", __func__, utd_entry, utd_entry->uid, - current->pid, current->tgid, current_fsuid()); + current->pid, current->tgid, from_kuid(&init_user_ns, current_fsuid())); BUG_ON(utd_entry->num_active_tags); rb_erase(&utd_entry->node, &uid_tag_data_tree); kfree(utd_entry); @@ -744,7 +744,7 @@ static int iface_stat_fmt_proc_show(struct seq_file *m, void *v) CT_DEBUG("qtaguid:proc iface_stat_fmt pid=%u tgid=%u uid=%u\n", - current->pid, current->tgid, current_fsuid()); + current->pid, current->tgid, from_kuid(&init_user_ns, current_fsuid())); iface_entry = list_entry(v, struct iface_stat, list); @@ -1605,7 +1605,7 @@ static struct sock *qtaguid_find_sk(const struct sk_buff *skb, * "struct inet_timewait_sock" which is missing fields. */ if (sk->sk_state == TCP_TIME_WAIT) { - xt_socket_put_sk(sk); + sock_gen_put(sk); sk = NULL; } } @@ -1656,7 +1656,7 @@ static bool qtaguid_mt(const struct sk_buff *skb, struct xt_action_param *par) const struct file *filp; bool got_sock = false; struct sock *sk; - uid_t sock_uid; + kuid_t sock_uid; bool res; if (unlikely(module_passive)) @@ -1720,7 +1720,7 @@ static bool qtaguid_mt(const struct sk_buff *skb, struct xt_action_param *par) sk->sk_socket ? sk->sk_socket->file : (void *)-1LL); filp = sk->sk_socket ? sk->sk_socket->file : NULL; MT_DEBUG("qtaguid[%d]: filp...uid=%u\n", - par->hooknum, filp ? filp->f_cred->fsuid : -1); + par->hooknum, filp ? from_kuid(&init_user_ns, filp->f_cred->fsuid) : -1); } if (sk == NULL || sk->sk_socket == NULL) { @@ -1761,7 +1761,7 @@ static bool qtaguid_mt(const struct sk_buff *skb, struct xt_action_param *par) * For now we only do iface stats when the uid-owner is not requested */ if (!(info->match & XT_QTAGUID_UID)) - account_for_uid(skb, sk, sock_uid, par); + account_for_uid(skb, sk, from_kuid(&init_user_ns, sock_uid), par); /* * The following two tests fail the match when: @@ -1769,31 +1769,38 @@ static bool qtaguid_mt(const struct sk_buff *skb, struct xt_action_param *par) * or id in range AND inverted condition requested * Thus (!a && b) || (a && !b) == a ^ b */ - if (info->match & XT_QTAGUID_UID) - if ((filp->f_cred->fsuid >= info->uid_min && - filp->f_cred->fsuid <= info->uid_max) ^ + if (info->match & XT_QTAGUID_UID) { + kuid_t uid_min = make_kuid(&init_user_ns, info->uid_min); + kuid_t uid_max = make_kuid(&init_user_ns, info->uid_max); + + if ((uid_gte(filp->f_cred->fsuid, uid_min) && + uid_lte(filp->f_cred->fsuid, uid_max)) ^ !(info->invert & XT_QTAGUID_UID)) { MT_DEBUG("qtaguid[%d]: leaving uid not matching\n", par->hooknum); res = false; goto put_sock_ret_res; } - if (info->match & XT_QTAGUID_GID) - if ((filp->f_cred->fsgid >= info->gid_min && - filp->f_cred->fsgid <= info->gid_max) ^ + } + if (info->match & XT_QTAGUID_GID) { + kgid_t gid_min = make_kgid(&init_user_ns, info->gid_min); + kgid_t gid_max = make_kgid(&init_user_ns, info->gid_max); + + if ((gid_gte(filp->f_cred->fsgid, gid_min) && + gid_lte(filp->f_cred->fsgid, gid_max)) ^ !(info->invert & XT_QTAGUID_GID)) { MT_DEBUG("qtaguid[%d]: leaving gid not matching\n", par->hooknum); res = false; goto put_sock_ret_res; } - + } MT_DEBUG("qtaguid[%d]: leaving matched\n", par->hooknum); res = true; put_sock_ret_res: if (got_sock) - xt_socket_put_sk(sk); + sock_gen_put(sk); ret_res: MT_DEBUG("qtaguid[%d]: left %d\n", par->hooknum, res); return res; @@ -1919,7 +1926,7 @@ static int qtaguid_ctrl_proc_show(struct seq_file *m, void *v) long f_count; CT_DEBUG("qtaguid: proc ctrl pid=%u tgid=%u uid=%u\n", - current->pid, current->tgid, current_fsuid()); + current->pid, current->tgid, from_kuid(&init_user_ns, current_fsuid())); if (sock_tag_entry != SEQ_START_TOKEN) { uid = get_uid_from_tag(sock_tag_entry->tag); @@ -1950,18 +1957,18 @@ static int qtaguid_ctrl_proc_show(struct seq_file *m, void *v) "match_found_no_sk_in_ct=%llu " "match_no_sk=%llu " "match_no_sk_file=%llu\n", - atomic64_read(&qtu_events.sockets_tagged), - atomic64_read(&qtu_events.sockets_untagged), - atomic64_read(&qtu_events.counter_set_changes), - atomic64_read(&qtu_events.delete_cmds), - atomic64_read(&qtu_events.iface_events), - atomic64_read(&qtu_events.match_calls), - atomic64_read(&qtu_events.match_calls_prepost), - atomic64_read(&qtu_events.match_found_sk), - atomic64_read(&qtu_events.match_found_sk_in_ct), - atomic64_read(&qtu_events.match_found_no_sk_in_ct), - atomic64_read(&qtu_events.match_no_sk), - atomic64_read(&qtu_events.match_no_sk_file)); + (u64)atomic64_read(&qtu_events.sockets_tagged), + (u64)atomic64_read(&qtu_events.sockets_untagged), + (u64)atomic64_read(&qtu_events.counter_set_changes), + (u64)atomic64_read(&qtu_events.delete_cmds), + (u64)atomic64_read(&qtu_events.iface_events), + (u64)atomic64_read(&qtu_events.match_calls), + (u64)atomic64_read(&qtu_events.match_calls_prepost), + (u64)atomic64_read(&qtu_events.match_found_sk), + (u64)atomic64_read(&qtu_events.match_found_sk_in_ct), + (u64)atomic64_read(&qtu_events.match_found_no_sk_in_ct), + (u64)atomic64_read(&qtu_events.match_no_sk), + (u64)atomic64_read(&qtu_events.match_no_sk_file)); /* Count the following as part of the last item_index */ prdebug_full_state(0, "proc ctrl"); @@ -1977,7 +1984,8 @@ static int qtaguid_ctrl_proc_show(struct seq_file *m, void *v) static int ctrl_cmd_delete(const char *input) { char cmd; - uid_t uid; + int uid_int; + kuid_t uid; uid_t entry_uid; tag_t acct_tag; tag_t tag; @@ -1991,10 +1999,11 @@ static int ctrl_cmd_delete(const char *input) struct tag_ref *tr_entry; struct uid_tag_data *utd_entry; - argc = sscanf(input, "%c %llu %u", &cmd, &acct_tag, &uid); + argc = sscanf(input, "%c %llu %u", &cmd, &acct_tag, &uid_int); + uid = make_kuid(&init_user_ns, uid_int); CT_DEBUG("qtaguid: ctrl_delete(%s): argc=%d cmd=%c " "user_tag=0x%llx uid=%u\n", input, argc, cmd, - acct_tag, uid); + acct_tag, uid_int); if (argc < 2) { res = -EINVAL; goto err; @@ -2006,18 +2015,19 @@ static int ctrl_cmd_delete(const char *input) } if (argc < 3) { uid = current_fsuid(); + uid_int = from_kuid(&init_user_ns, uid); } else if (!can_impersonate_uid(uid)) { pr_info("qtaguid: ctrl_delete(%s): " "insufficient priv from pid=%u tgid=%u uid=%u\n", - input, current->pid, current->tgid, current_fsuid()); + input, current->pid, current->tgid, from_kuid(&init_user_ns, current_fsuid())); res = -EPERM; goto err; } - tag = combine_atag_with_uid(acct_tag, uid); + tag = combine_atag_with_uid(acct_tag, uid_int); CT_DEBUG("qtaguid: ctrl_delete(%s): " "looking for tag=0x%llx (uid=%u)\n", - input, tag, uid); + input, tag, uid_int); /* Delete socket tags */ spin_lock_bh(&sock_tag_list_lock); @@ -2026,7 +2036,7 @@ static int ctrl_cmd_delete(const char *input) st_entry = rb_entry(node, struct sock_tag, sock_node); entry_uid = get_uid_from_tag(st_entry->tag); node = rb_next(node); - if (entry_uid != uid) + if (entry_uid != uid_int) continue; CT_DEBUG("qtaguid: ctrl_delete(%s): st tag=0x%llx (uid=%u)\n", @@ -2087,7 +2097,7 @@ static int ctrl_cmd_delete(const char *input) "ts tag=0x%llx (uid=%u)\n", input, ts_entry->tn.tag, entry_uid); - if (entry_uid != uid) + if (entry_uid != uid_int) continue; if (!acct_tag || ts_entry->tn.tag == tag) { CT_DEBUG("qtaguid: ctrl_delete(%s): " @@ -2116,7 +2126,7 @@ static int ctrl_cmd_delete(const char *input) "utd uid=%u\n", input, entry_uid); - if (entry_uid != uid) + if (entry_uid != uid_int) continue; /* * Go over the tag_refs, and those that don't have @@ -2160,7 +2170,7 @@ static int ctrl_cmd_counter_set(const char *input) if (!can_manipulate_uids()) { pr_info("qtaguid: ctrl_counterset(%s): " "insufficient priv from pid=%u tgid=%u uid=%u\n", - input, current->pid, current->tgid, current_fsuid()); + input, current->pid, current->tgid, from_kuid(&init_user_ns, current_fsuid())); res = -EPERM; goto err; } @@ -2197,7 +2207,8 @@ static int ctrl_cmd_tag(const char *input) { char cmd; int sock_fd = 0; - uid_t uid = 0; + kuid_t uid; + unsigned int uid_int = 0; tag_t acct_tag = make_atag_from_value(0); tag_t full_tag; struct socket *el_socket; @@ -2208,10 +2219,11 @@ static int ctrl_cmd_tag(const char *input) struct proc_qtu_data *pqd_entry; /* Unassigned args will get defaulted later. */ - argc = sscanf(input, "%c %d %llu %u", &cmd, &sock_fd, &acct_tag, &uid); + argc = sscanf(input, "%c %d %llu %u", &cmd, &sock_fd, &acct_tag, &uid_int); + uid = make_kuid(&init_user_ns, uid_int); CT_DEBUG("qtaguid: ctrl_tag(%s): argc=%d cmd=%c sock_fd=%d " "acct_tag=0x%llx uid=%u\n", input, argc, cmd, sock_fd, - acct_tag, uid); + acct_tag, uid_int); if (argc < 2) { res = -EINVAL; goto err; @@ -2221,7 +2233,7 @@ static int ctrl_cmd_tag(const char *input) pr_info("qtaguid: ctrl_tag(%s): failed to lookup" " sock_fd=%d err=%d pid=%u tgid=%u uid=%u\n", input, sock_fd, res, current->pid, current->tgid, - current_fsuid()); + from_kuid(&init_user_ns, current_fsuid())); goto err; } CT_DEBUG("qtaguid: ctrl_tag(%s): socket->...->f_count=%ld ->sk=%p\n", @@ -2237,21 +2249,24 @@ static int ctrl_cmd_tag(const char *input) CT_DEBUG("qtaguid: ctrl_tag(%s): " "pid=%u tgid=%u uid=%u euid=%u fsuid=%u " "ctrl.gid=%u in_group()=%d in_egroup()=%d\n", - input, current->pid, current->tgid, current_uid(), - current_euid(), current_fsuid(), - xt_qtaguid_ctrl_file->gid, + input, current->pid, current->tgid, + from_kuid(&init_user_ns, current_uid()), + from_kuid(&init_user_ns, current_euid()), + from_kuid(&init_user_ns, current_fsuid()), + from_kgid(&init_user_ns, xt_qtaguid_ctrl_file->gid), in_group_p(xt_qtaguid_ctrl_file->gid), in_egroup_p(xt_qtaguid_ctrl_file->gid)); if (argc < 4) { uid = current_fsuid(); + uid_int = from_kuid(&init_user_ns, uid); } else if (!can_impersonate_uid(uid)) { pr_info("qtaguid: ctrl_tag(%s): " "insufficient priv from pid=%u tgid=%u uid=%u\n", - input, current->pid, current->tgid, current_fsuid()); + input, current->pid, current->tgid, from_kuid(&init_user_ns, current_fsuid())); res = -EPERM; goto err_put; } - full_tag = combine_atag_with_uid(acct_tag, uid); + full_tag = combine_atag_with_uid(acct_tag, uid_int); spin_lock_bh(&sock_tag_list_lock); sock_tag_entry = get_sock_stat_nl(el_socket->sk); @@ -2298,8 +2313,7 @@ static int ctrl_cmd_tag(const char *input) sock_tag_entry->sk = el_socket->sk; sock_tag_entry->socket = el_socket; sock_tag_entry->pid = current->tgid; - sock_tag_entry->tag = combine_atag_with_uid(acct_tag, - uid); + sock_tag_entry->tag = combine_atag_with_uid(acct_tag, uid_int); spin_lock_bh(&uid_tag_data_tree_lock); pqd_entry = proc_qtu_data_tree_search( &proc_qtu_data_tree, current->tgid); @@ -2314,7 +2328,7 @@ static int ctrl_cmd_tag(const char *input) "User space forgot to open /dev/xt_qtaguid? " "pid=%u tgid=%u uid=%u\n", __func__, current->pid, current->tgid, - current_fsuid()); + from_kuid(&init_user_ns, current_fsuid())); else list_add(&sock_tag_entry->list, &pqd_entry->sock_tag_list); @@ -2369,7 +2383,7 @@ static int ctrl_cmd_untag(const char *input) pr_info("qtaguid: ctrl_untag(%s): failed to lookup" " sock_fd=%d err=%d pid=%u tgid=%u uid=%u\n", input, sock_fd, res, current->pid, current->tgid, - current_fsuid()); + from_kuid(&init_user_ns, current_fsuid())); goto err; } CT_DEBUG("qtaguid: ctrl_untag(%s): socket->...->f_count=%ld ->sk=%p\n", @@ -2403,7 +2417,7 @@ static int ctrl_cmd_untag(const char *input) pr_warn_once("qtaguid: %s(): " "User space forgot to open /dev/xt_qtaguid? " "pid=%u tgid=%u uid=%u\n", __func__, - current->pid, current->tgid, current_fsuid()); + current->pid, current->tgid, from_kuid(&init_user_ns, current_fsuid())); else list_del(&sock_tag_entry->list); spin_unlock_bh(&uid_tag_data_tree_lock); @@ -2446,7 +2460,7 @@ static ssize_t qtaguid_ctrl_parse(const char *input, size_t count) ssize_t res; CT_DEBUG("qtaguid: ctrl(%s): pid=%u tgid=%u uid=%u\n", - input, current->pid, current->tgid, current_fsuid()); + input, current->pid, current->tgid, from_kuid(&init_user_ns, current_fsuid())); cmd = input[0]; /* Collect params for commands */ @@ -2528,14 +2542,15 @@ static int pp_stats_line(struct seq_file *m, struct tag_stat *ts_entry, uid_t stat_uid = get_uid_from_tag(tag); struct proc_print_info *ppi = m->private; /* Detailed tags are not available to everybody */ - if (get_atag_from_tag(tag) && !can_read_other_uid_stats(stat_uid)) { + if (get_atag_from_tag(tag) && !can_read_other_uid_stats( + make_kuid(&init_user_ns,stat_uid))) { CT_DEBUG("qtaguid: stats line: " "%s 0x%llx %u: insufficient priv " "from pid=%u tgid=%u uid=%u stats.gid=%u\n", ppi->iface_entry->ifname, get_atag_from_tag(tag), stat_uid, - current->pid, current->tgid, current_fsuid(), - xt_qtaguid_stats_file->gid); + current->pid, current->tgid, from_kuid(&init_user_ns, current_fsuid()), + from_kgid(&init_user_ns,xt_qtaguid_stats_file->gid)); return 0; } ppi->item_index++; @@ -2737,12 +2752,12 @@ static int qtudev_open(struct inode *inode, struct file *file) return 0; DR_DEBUG("qtaguid: qtudev_open(): pid=%u tgid=%u uid=%u\n", - current->pid, current->tgid, current_fsuid()); + current->pid, current->tgid, from_kuid(&init_user_ns, current_fsuid())); spin_lock_bh(&uid_tag_data_tree_lock); /* Look for existing uid data, or alloc one. */ - utd_entry = get_uid_data(current_fsuid(), &utd_entry_found); + utd_entry = get_uid_data(from_kuid(&init_user_ns, current_fsuid()), &utd_entry_found); if (IS_ERR_OR_NULL(utd_entry)) { res = PTR_ERR(utd_entry); goto err_unlock; @@ -2754,7 +2769,7 @@ static int qtudev_open(struct inode *inode, struct file *file) if (pqd_entry) { pr_err("qtaguid: qtudev_open(): %u/%u %u " "%s already opened\n", - current->pid, current->tgid, current_fsuid(), + current->pid, current->tgid, from_kuid(&init_user_ns, current_fsuid()), QTU_DEV_NAME); res = -EBUSY; goto err_unlock_free_utd; @@ -2764,7 +2779,7 @@ static int qtudev_open(struct inode *inode, struct file *file) if (!new_pqd_entry) { pr_err("qtaguid: qtudev_open(): %u/%u %u: " "proc data alloc failed\n", - current->pid, current->tgid, current_fsuid()); + current->pid, current->tgid, from_kuid(&init_user_ns, current_fsuid())); res = -ENOMEM; goto err_unlock_free_utd; } @@ -2778,7 +2793,7 @@ static int qtudev_open(struct inode *inode, struct file *file) spin_unlock_bh(&uid_tag_data_tree_lock); DR_DEBUG("qtaguid: tracking data for uid=%u in pqd=%p\n", - current_fsuid(), new_pqd_entry); + from_kuid(&init_user_ns, current_fsuid()), new_pqd_entry); file->private_data = new_pqd_entry; return 0; From b90560623538b36a58721ac763cfa11cb66fdb6c Mon Sep 17 00:00:00 2001 From: Mohamad Ayyash Date: Tue, 13 Jan 2015 19:20:44 -0800 Subject: [PATCH 0198/1103] ANDROID: netfilter: xt_qtaguid: Use sk_callback_lock read locks before reading sk->sk_socket It prevents a kernel panic when accessing sk->sk_socket fields due to NULLing sk->sk_socket when sock_orphan is called through sk_common_release. Change-Id: I4aa46b4e2d8600e4d4ef8dcdd363aa4e6e5f8433 Signed-off-by: Mohamad Ayyash (cherry picked from commit cdea0ebcb8bcfe57688f6cb692b49e550ebd9796) Signed-off-by: John Stultz --- net/netfilter/xt_qtaguid.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/net/netfilter/xt_qtaguid.c b/net/netfilter/xt_qtaguid.c index cd2da5460db2..2f9784c1e692 100644 --- a/net/netfilter/xt_qtaguid.c +++ b/net/netfilter/xt_qtaguid.c @@ -1658,6 +1658,7 @@ static bool qtaguid_mt(const struct sk_buff *skb, struct xt_action_param *par) struct sock *sk; kuid_t sock_uid; bool res; + bool set_sk_callback_lock = false; if (unlikely(module_passive)) return (info->match ^ info->invert) == 0; @@ -1715,6 +1716,8 @@ static bool qtaguid_mt(const struct sk_buff *skb, struct xt_action_param *par) MT_DEBUG("qtaguid[%d]: sk=%p got_sock=%d fam=%d proto=%d\n", par->hooknum, sk, got_sock, par->family, ipx_proto(skb, par)); if (sk != NULL) { + set_sk_callback_lock = true; + read_lock_bh(&sk->sk_callback_lock); MT_DEBUG("qtaguid[%d]: sk=%p->sk_socket=%p->file=%p\n", par->hooknum, sk, sk->sk_socket, sk->sk_socket ? sk->sk_socket->file : (void *)-1LL); @@ -1801,6 +1804,8 @@ static bool qtaguid_mt(const struct sk_buff *skb, struct xt_action_param *par) put_sock_ret_res: if (got_sock) sock_gen_put(sk); + if (set_sk_callback_lock) + read_unlock_bh(&sk->sk_callback_lock); ret_res: MT_DEBUG("qtaguid[%d]: left %d\n", par->hooknum, res); return res; From 4d8a8ef94485e779dcb9ba051b9ea9ed1546d2f4 Mon Sep 17 00:00:00 2001 From: Amit Pundir Date: Tue, 7 Jul 2015 00:28:49 +0530 Subject: [PATCH 0199/1103] ANDROID: netfilter: xt_qtaguid: xt_socket: build fixes Add missing header and use xt_socket_lookup_slow_v* instead of xt_socket_get*_sk in xt_qtaguid.c. Fix xt_socket_lookup_slow_v* functions in xt_socket.c and declare them in xt_socket.h Change-Id: I55819b2d4ffa82a2be20995c87d28fb5cc77b5ba Signed-off-by: John Stultz [AmitP: Upstream commit 8db4c5be88f6 ("netfilter: move socket lookup infrastructure to nf_socket_ipv{4,6}.c")] moved socket lookup to nf_socket_ipv{4,6}.c, hence use nf_sk_lookup_slow_v[4|6]() instead of obsolete xt_socket_lookup_slow_v[4|6](). Also folded following android-4.9 commit changes into this patch 7de1bb86dc5a ("ANDROID: netfilter: xt_qtaguid/socket: build fixes for 4.4") 5b5ab94817f9 ("ANDROID: netfilter: xt_qtaguid: seq_printf fixes")] Signed-off-by: Amit Pundir --- include/uapi/linux/netfilter/xt_socket.h | 5 ----- net/netfilter/xt_qtaguid.c | 10 +++++----- 2 files changed, 5 insertions(+), 10 deletions(-) diff --git a/include/uapi/linux/netfilter/xt_socket.h b/include/uapi/linux/netfilter/xt_socket.h index 5075d2e3ae30..a7bdc9d882b0 100644 --- a/include/uapi/linux/netfilter/xt_socket.h +++ b/include/uapi/linux/netfilter/xt_socket.h @@ -27,9 +27,4 @@ struct xt_socket_mtinfo3 { | XT_SOCKET_NOWILDCARD \ | XT_SOCKET_RESTORESKMARK) -struct sock *xt_socket_get4_sk(const struct sk_buff *skb, - struct xt_action_param *par); -struct sock *xt_socket_get6_sk(const struct sk_buff *skb, - struct xt_action_param *par); - #endif /* _XT_SOCKET_H */ diff --git a/net/netfilter/xt_qtaguid.c b/net/netfilter/xt_qtaguid.c index 2f9784c1e692..b43e7dc301e7 100644 --- a/net/netfilter/xt_qtaguid.c +++ b/net/netfilter/xt_qtaguid.c @@ -17,6 +17,7 @@ #include #include #include +#include #include #include #include @@ -1588,10 +1589,10 @@ static struct sock *qtaguid_find_sk(const struct sk_buff *skb, switch (par->family) { case NFPROTO_IPV6: - sk = xt_socket_get6_sk(skb, par); + sk = nf_sk_lookup_slow_v6(dev_net(skb->dev), skb, par->in); break; case NFPROTO_IPV4: - sk = xt_socket_get4_sk(skb, par); + sk = nf_sk_lookup_slow_v4(dev_net(skb->dev), skb, par->in); break; default: return NULL; @@ -2541,7 +2542,6 @@ static void pp_stats_header(struct seq_file *m) static int pp_stats_line(struct seq_file *m, struct tag_stat *ts_entry, int cnt_set) { - int ret; struct data_counters *cnts; tag_t tag = ts_entry->tn.tag; uid_t stat_uid = get_uid_from_tag(tag); @@ -2560,7 +2560,7 @@ static int pp_stats_line(struct seq_file *m, struct tag_stat *ts_entry, } ppi->item_index++; cnts = &ts_entry->counters; - ret = seq_printf(m, "%d %s 0x%llx %u %u " + seq_printf(m, "%d %s 0x%llx %u %u " "%llu %llu " "%llu %llu " "%llu %llu " @@ -2590,7 +2590,7 @@ static int pp_stats_line(struct seq_file *m, struct tag_stat *ts_entry, cnts->bpc[cnt_set][IFS_TX][IFS_UDP].packets, cnts->bpc[cnt_set][IFS_TX][IFS_PROTO_OTHER].bytes, cnts->bpc[cnt_set][IFS_TX][IFS_PROTO_OTHER].packets); - return ret ?: 1; + return seq_has_overflowed(m) ? -ENOSPC : 1; } static bool pp_sets(struct seq_file *m, struct tag_stat *ts_entry) From 86dd299d24fdd7f4d9d867ade42bd259d929b144 Mon Sep 17 00:00:00 2001 From: "liping.zhang" Date: Mon, 11 Jan 2016 13:31:01 +0800 Subject: [PATCH 0200/1103] ANDROID: netfilter: xt_qtaguid: fix a race condition in if_tag_stat_update Miss a lock protection in if_tag_stat_update while doing get_iface_entry. So if one CPU is doing iface_stat_create while another CPU is doing if_tag_stat_update, race will happened. Change-Id: Ib8d98e542f4e385685499f5b7bb7354f08654a75 Signed-off-by: Liping Zhang --- net/netfilter/xt_qtaguid.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/net/netfilter/xt_qtaguid.c b/net/netfilter/xt_qtaguid.c index b43e7dc301e7..243d9b8e84e9 100644 --- a/net/netfilter/xt_qtaguid.c +++ b/net/netfilter/xt_qtaguid.c @@ -1291,11 +1291,12 @@ static void if_tag_stat_update(const char *ifname, uid_t uid, "uid=%u sk=%p dir=%d proto=%d bytes=%d)\n", ifname, uid, sk, direction, proto, bytes); - + spin_lock_bh(&iface_stat_list_lock); iface_entry = get_iface_entry(ifname); if (!iface_entry) { pr_err_ratelimited("qtaguid: iface_stat: stat_update() " "%s not found\n", ifname); + spin_unlock_bh(&iface_stat_list_lock); return; } /* It is ok to process data when an iface_entry is inactive */ @@ -1331,8 +1332,7 @@ static void if_tag_stat_update(const char *ifname, uid_t uid, * {0, uid_tag} will also get updated. */ tag_stat_update(tag_stat_entry, direction, proto, bytes); - spin_unlock_bh(&iface_entry->tag_stat_list_lock); - return; + goto unlock; } /* Loop over tag list under this interface for {0,uid_tag} */ @@ -1372,6 +1372,7 @@ static void if_tag_stat_update(const char *ifname, uid_t uid, tag_stat_update(new_tag_stat, direction, proto, bytes); unlock: spin_unlock_bh(&iface_entry->tag_stat_list_lock); + spin_unlock_bh(&iface_stat_list_lock); } static int iface_netdev_event_handler(struct notifier_block *nb, From 798220b13b2aa66e2ff63ac20481fbfd78487ab2 Mon Sep 17 00:00:00 2001 From: John Stultz Date: Fri, 22 Apr 2016 17:12:57 -0700 Subject: [PATCH 0201/1103] ANDROID: netfilter: xt_qtaguid: Fix panic caused by synack processing In upstream commit ca6fb06518836ef9b65dc0aac02ff97704d52a05 (tcp: attach SYNACK messages to request sockets instead of listener) http://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=ca6fb0651883 The building of synack messages was changed, which made it so the skb->sk points to a casted request_sock. This is problematic, as there is no sk_socket in a request_sock. So when the qtaguid_mt function tries to access the sk->sk_socket, it accesses uninitialized memory. After looking at how other netfilter implementations handle this, I realized there was a skb_to_full_sk() helper added, which the xt_qtaguid code isn't yet using. This patch adds its use, and resovles panics seen when accessing uninitialzed memory when processing synack packets. Reported-by: YongQin Liu Signed-off-by: John Stultz --- net/netfilter/xt_qtaguid.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/netfilter/xt_qtaguid.c b/net/netfilter/xt_qtaguid.c index 243d9b8e84e9..3f5a107342af 100644 --- a/net/netfilter/xt_qtaguid.c +++ b/net/netfilter/xt_qtaguid.c @@ -1689,7 +1689,7 @@ static bool qtaguid_mt(const struct sk_buff *skb, struct xt_action_param *par) /* default: Fall through and do UID releated work */ } - sk = skb->sk; + sk = skb_to_full_sk(skb); /* * When in TCP_TIME_WAIT the sk is not a "struct sock" but * "struct inet_timewait_sock" which is missing fields. From 47788af1649266511161a41add8c6d03ddfb5eb1 Mon Sep 17 00:00:00 2001 From: John Stultz Date: Thu, 12 May 2016 11:17:52 -0700 Subject: [PATCH 0202/1103] ANDROID: netfilter: xt_qtaguid: Fix panic caused by processing non-full socket. In an issue very similar to 4e461c777e3 (xt_qtaguid: Fix panic caused by synack processing), we were seeing panics on occasion in testing. In this case, it was the same issue, but caused by a different call path, as the sk being returned from qtaguid_find_sk() was not a full socket. Resulting in the sk->sk_socket deref to fail. This patch adds an extra check to ensure the sk being retuned is a full socket, and if not it returns NULL. Reported-by: Milosz Wasilewski Signed-off-by: John Stultz --- net/netfilter/xt_qtaguid.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/netfilter/xt_qtaguid.c b/net/netfilter/xt_qtaguid.c index 3f5a107342af..1c419df66958 100644 --- a/net/netfilter/xt_qtaguid.c +++ b/net/netfilter/xt_qtaguid.c @@ -1606,7 +1606,7 @@ static struct sock *qtaguid_find_sk(const struct sk_buff *skb, * When in TCP_TIME_WAIT the sk is not a "struct sock" but * "struct inet_timewait_sock" which is missing fields. */ - if (sk->sk_state == TCP_TIME_WAIT) { + if (!sk_fullsock(sk) || sk->sk_state == TCP_TIME_WAIT) { sock_gen_put(sk); sk = NULL; } From e1d29b5f4a3af43f454535db5f0e32b4c64f65ab Mon Sep 17 00:00:00 2001 From: Mohamad Ayyash Date: Wed, 11 May 2016 13:18:35 -0700 Subject: [PATCH 0203/1103] ANDROID: netfilter: xt_qtaguid: Don't show empty tag stats for unprivileged uids BUG: 27577101 BUG: 27532522 Change-Id: Ibee3c5d224f139b9312a40acb203e87aa7060797 Signed-off-by: Mohamad Ayyash --- net/netfilter/xt_qtaguid.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/net/netfilter/xt_qtaguid.c b/net/netfilter/xt_qtaguid.c index 1c419df66958..8e971c9065cc 100644 --- a/net/netfilter/xt_qtaguid.c +++ b/net/netfilter/xt_qtaguid.c @@ -1946,7 +1946,7 @@ static int qtaguid_ctrl_proc_show(struct seq_file *m, void *v) ); f_count = atomic_long_read( &sock_tag_entry->socket->file->f_count); - seq_printf(m, "sock=%p tag=0x%llx (uid=%u) pid=%u " + seq_printf(m, "sock=%pK tag=0x%llx (uid=%u) pid=%u " "f_count=%lu\n", sock_tag_entry->sk, sock_tag_entry->tag, uid, @@ -2548,8 +2548,7 @@ static int pp_stats_line(struct seq_file *m, struct tag_stat *ts_entry, uid_t stat_uid = get_uid_from_tag(tag); struct proc_print_info *ppi = m->private; /* Detailed tags are not available to everybody */ - if (get_atag_from_tag(tag) && !can_read_other_uid_stats( - make_kuid(&init_user_ns,stat_uid))) { + if (!can_read_other_uid_stats(make_kuid(&init_user_ns,stat_uid))) { CT_DEBUG("qtaguid: stats line: " "%s 0x%llx %u: insufficient priv " "from pid=%u tgid=%u uid=%u stats.gid=%u\n", From 6784fc32fbc50fba678096fc6eae0374451c819b Mon Sep 17 00:00:00 2001 From: Chenbo Feng Date: Thu, 23 Mar 2017 13:51:24 -0700 Subject: [PATCH 0204/1103] ANDROID: netfilter: xt_qtaguid: fix the deadlock when enable DDEBUG When DDEBUG is enabled, the prdebug_full_state() function will try to recursively aquire the spinlock of sock_tag_list and causing deadlock. A check statement is added before it aquire the spinlock to differentiate the behavior depend on the caller of the function. Bug: 36559739 Test: Compile and run test under system/extra/test/iptables/ Change-Id: Ie3397fbaa207e14fe214d47aaf5e8ca1f4a712ee Signed-off-by: Chenbo Feng (cherry picked from commit f0faedd6b468777f3bb5834f97100794d562c8b7) --- net/netfilter/xt_qtaguid.c | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/net/netfilter/xt_qtaguid.c b/net/netfilter/xt_qtaguid.c index 8e971c9065cc..44db68bf2575 100644 --- a/net/netfilter/xt_qtaguid.c +++ b/net/netfilter/xt_qtaguid.c @@ -1814,8 +1814,11 @@ static bool qtaguid_mt(const struct sk_buff *skb, struct xt_action_param *par) } #ifdef DDEBUG -/* This function is not in xt_qtaguid_print.c because of locks visibility */ -static void prdebug_full_state(int indent_level, const char *fmt, ...) +/* + * This function is not in xt_qtaguid_print.c because of locks visibility. + * The lock of sock_tag_list must be aquired before calling this function + */ +static void prdebug_full_state_locked(int indent_level, const char *fmt, ...) { va_list args; char *fmt_buff; @@ -1836,16 +1839,12 @@ static void prdebug_full_state(int indent_level, const char *fmt, ...) kfree(buff); va_end(args); - spin_lock_bh(&sock_tag_list_lock); prdebug_sock_tag_tree(indent_level, &sock_tag_tree); - spin_unlock_bh(&sock_tag_list_lock); - spin_lock_bh(&sock_tag_list_lock); spin_lock_bh(&uid_tag_data_tree_lock); prdebug_uid_tag_data_tree(indent_level, &uid_tag_data_tree); prdebug_proc_qtu_data_tree(indent_level, &proc_qtu_data_tree); spin_unlock_bh(&uid_tag_data_tree_lock); - spin_unlock_bh(&sock_tag_list_lock); spin_lock_bh(&iface_stat_list_lock); prdebug_iface_stat_list(indent_level, &iface_stat_list); @@ -1854,7 +1853,7 @@ static void prdebug_full_state(int indent_level, const char *fmt, ...) pr_debug("qtaguid: %s(): }\n", __func__); } #else -static void prdebug_full_state(int indent_level, const char *fmt, ...) {} +static void prdebug_full_state_locked(int indent_level, const char *fmt, ...) {} #endif struct proc_ctrl_print_info { @@ -1977,8 +1976,11 @@ static int qtaguid_ctrl_proc_show(struct seq_file *m, void *v) (u64)atomic64_read(&qtu_events.match_no_sk), (u64)atomic64_read(&qtu_events.match_no_sk_file)); - /* Count the following as part of the last item_index */ - prdebug_full_state(0, "proc ctrl"); + /* Count the following as part of the last item_index. No need + * to lock the sock_tag_list here since it is already locked when + * starting the seq_file operation + */ + prdebug_full_state_locked(0, "proc ctrl"); } return 0; @@ -2887,8 +2889,10 @@ static int qtudev_release(struct inode *inode, struct file *file) sock_tag_tree_erase(&st_to_free_tree); - prdebug_full_state(0, "%s(): pid=%u tgid=%u", __func__, + spin_lock_bh(&sock_tag_list_lock); + prdebug_full_state_locked(0, "%s(): pid=%u tgid=%u", __func__, current->pid, current->tgid); + spin_unlock_bh(&sock_tag_list_lock); return 0; } From d20aa67ea4a16a96189c0ed8358e098516ab725c Mon Sep 17 00:00:00 2001 From: Greg Hackmann Date: Mon, 24 Apr 2017 16:16:15 -0700 Subject: [PATCH 0205/1103] ANDROID: netfilter: xt_qtaguid: don't check if embedded arrays are NULL clang warns about four NULL pointer checks: net/netfilter/xt_qtaguid.c:973:11: warning: address of array 'ifa->ifa_label' will always evaluate to 'true' [-Wpointer-bool-conversion] net/netfilter/xt_qtaguid.c:974:13: warning: address of array 'ifa->ifa_label' will always evaluate to 'true' [-Wpointer-bool-conversion] net/netfilter/xt_qtaguid.c:1212:31: warning: address of array 'el_dev->name' will always evaluate to 'true' [-Wpointer-bool-conversion] net/netfilter/xt_qtaguid.c:1640:31: warning: address of array 'el_dev->name' will always evaluate to 'true' [-Wpointer-bool-conversion] Both of these fields are embedded char[16] arrays rather than pointers, so they can never be NULL. Change-Id: I748ff6dd11569e5596a9d5cecdf9c334847e7307 Signed-off-by: Greg Hackmann --- net/netfilter/xt_qtaguid.c | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/net/netfilter/xt_qtaguid.c b/net/netfilter/xt_qtaguid.c index 44db68bf2575..caba267c6289 100644 --- a/net/netfilter/xt_qtaguid.c +++ b/net/netfilter/xt_qtaguid.c @@ -969,9 +969,8 @@ static void iface_stat_create(struct net_device *net_dev, for (ifa = in_dev->ifa_list; ifa; ifa = ifa->ifa_next) { IF_DEBUG("qtaguid: iface_stat: create(%s): " "ifa=%p ifa_label=%s\n", - ifname, ifa, - ifa->ifa_label ? ifa->ifa_label : "(null)"); - if (ifa->ifa_label && !strcmp(ifname, ifa->ifa_label)) + ifname, ifa, ifa->ifa_label); + if (!strcmp(ifname, ifa->ifa_label)) break; } } @@ -1209,10 +1208,6 @@ static void iface_stat_update_from_skb(const struct sk_buff *skb, pr_err_ratelimited("qtaguid[%d]: %s(): no par->in/out?!!\n", par->hooknum, __func__); BUG(); - } else if (unlikely(!el_dev->name)) { - pr_err_ratelimited("qtaguid[%d]: %s(): no dev->name?!!\n", - par->hooknum, __func__); - BUG(); } else { proto = ipx_proto(skb, par); MT_DEBUG("qtaguid[%d]: dev name=%s type=%d fam=%d proto=%d\n", @@ -1637,8 +1632,6 @@ static void account_for_uid(const struct sk_buff *skb, if (unlikely(!el_dev)) { pr_info("qtaguid[%d]: no par->in/out?!!\n", par->hooknum); - } else if (unlikely(!el_dev->name)) { - pr_info("qtaguid[%d]: no dev->name?!!\n", par->hooknum); } else { int proto = ipx_proto(skb, par); MT_DEBUG("qtaguid[%d]: dev name=%s type=%d fam=%d proto=%d\n", From 925622cd966c8d4a58cef295ffee9ec0eb705489 Mon Sep 17 00:00:00 2001 From: Chenbo Feng Date: Wed, 19 Apr 2017 14:22:47 -0700 Subject: [PATCH 0206/1103] ANDROID: netfilter: xt_qtaguid: Add untag hacks to inet_release function To prevent protential risk of memory leak caused by closing socket with out untag it from qtaguid module, the qtaguid module now do not hold any socket file reference count. Instead, it will increase the sk_refcnt of the sk struct to prevent a reuse of the socket pointer. And when a socket is released. It will delete the tag if the socket is previously tagged so no more resources is held by xt_qtaguid moudle. A flag is added to the untag process to prevent possible kernel crash caused by fail to delete corresponding socket_tag_entry list. Bug: 36374484 Test: compile and run test under system/extra/test/iptables, run cts -m CtsNetTestCases -t android.net.cts.SocketRefCntTest Signed-off-by: Chenbo Feng Change-Id: Iea7c3bf0c59b9774a5114af905b2405f6bc9ee52 --- include/linux/netfilter/xt_qtaguid.h | 1 + net/ipv4/af_inet.c | 4 + net/netfilter/xt_qtaguid.c | 107 ++++++++++++++------------- net/netfilter/xt_qtaguid_internal.h | 2 - net/netfilter/xt_qtaguid_print.c | 8 +- 5 files changed, 63 insertions(+), 59 deletions(-) diff --git a/include/linux/netfilter/xt_qtaguid.h b/include/linux/netfilter/xt_qtaguid.h index ca60fbdec2f3..1c671552ec37 100644 --- a/include/linux/netfilter/xt_qtaguid.h +++ b/include/linux/netfilter/xt_qtaguid.h @@ -10,4 +10,5 @@ #define XT_QTAGUID_SOCKET XT_OWNER_SOCKET #define xt_qtaguid_match_info xt_owner_match_info +int qtaguid_untag(struct socket *sock, bool kernel); #endif /* _XT_QTAGUID_MATCH_H */ diff --git a/net/ipv4/af_inet.c b/net/ipv4/af_inet.c index 32615ca1dd6f..66c70a1becfe 100644 --- a/net/ipv4/af_inet.c +++ b/net/ipv4/af_inet.c @@ -89,6 +89,7 @@ #include #include #include +#include #include @@ -426,6 +427,9 @@ int inet_release(struct socket *sock) if (sk) { long timeout; +#ifdef CONFIG_NETFILTER_XT_MATCH_QTAGUID + qtaguid_untag(sock, true); +#endif /* Applications forget to leave groups before exiting */ ip_mc_drop_socket(sk); diff --git a/net/netfilter/xt_qtaguid.c b/net/netfilter/xt_qtaguid.c index caba267c6289..a7f4d8b3e9e8 100644 --- a/net/netfilter/xt_qtaguid.c +++ b/net/netfilter/xt_qtaguid.c @@ -321,7 +321,7 @@ static void sock_tag_tree_erase(struct rb_root *st_to_free_tree) st_entry->tag, get_uid_from_tag(st_entry->tag)); rb_erase(&st_entry->sock_node, st_to_free_tree); - sockfd_put(st_entry->socket); + sock_put(st_entry->sk); kfree(st_entry); } } @@ -1922,12 +1922,12 @@ static int qtaguid_ctrl_proc_show(struct seq_file *m, void *v) { struct sock_tag *sock_tag_entry = v; uid_t uid; - long f_count; CT_DEBUG("qtaguid: proc ctrl pid=%u tgid=%u uid=%u\n", current->pid, current->tgid, from_kuid(&init_user_ns, current_fsuid())); if (sock_tag_entry != SEQ_START_TOKEN) { + int sk_ref_count; uid = get_uid_from_tag(sock_tag_entry->tag); CT_DEBUG("qtaguid: proc_read(): sk=%p tag=0x%llx (uid=%u) " "pid=%u\n", @@ -1936,13 +1936,13 @@ static int qtaguid_ctrl_proc_show(struct seq_file *m, void *v) uid, sock_tag_entry->pid ); - f_count = atomic_long_read( - &sock_tag_entry->socket->file->f_count); + sk_ref_count = atomic_read( + &sock_tag_entry->sk->sk_refcnt); seq_printf(m, "sock=%pK tag=0x%llx (uid=%u) pid=%u " - "f_count=%lu\n", + "f_count=%d\n", sock_tag_entry->sk, sock_tag_entry->tag, uid, - sock_tag_entry->pid, f_count); + sock_tag_entry->pid, sk_ref_count); } else { seq_printf(m, "events: sockets_tagged=%llu " "sockets_untagged=%llu " @@ -2238,8 +2238,8 @@ static int ctrl_cmd_tag(const char *input) from_kuid(&init_user_ns, current_fsuid())); goto err; } - CT_DEBUG("qtaguid: ctrl_tag(%s): socket->...->f_count=%ld ->sk=%p\n", - input, atomic_long_read(&el_socket->file->f_count), + CT_DEBUG("qtaguid: ctrl_tag(%s): socket->...->sk_refcnt=%d ->sk=%p\n", + input, atomic_read(&el_socket->sk->sk_refcnt), el_socket->sk); if (argc < 3) { acct_tag = make_atag_from_value(0); @@ -2283,16 +2283,9 @@ static int ctrl_cmd_tag(const char *input) struct tag_ref *prev_tag_ref_entry; CT_DEBUG("qtaguid: ctrl_tag(%s): retag for sk=%p " - "st@%p ...->f_count=%ld\n", + "st@%p ...->sk_refcnt=%d\n", input, el_socket->sk, sock_tag_entry, - atomic_long_read(&el_socket->file->f_count)); - /* - * This is a re-tagging, so release the sock_fd that was - * locked at the time of the 1st tagging. - * There is still the ref from this call's sockfd_lookup() so - * it can be done within the spinlock. - */ - sockfd_put(sock_tag_entry->socket); + atomic_read(&el_socket->sk->sk_refcnt)); prev_tag_ref_entry = lookup_tag_ref(sock_tag_entry->tag, &uid_tag_data_entry); BUG_ON(IS_ERR_OR_NULL(prev_tag_ref_entry)); @@ -2312,8 +2305,12 @@ static int ctrl_cmd_tag(const char *input) res = -ENOMEM; goto err_tag_unref_put; } + /* + * Hold the sk refcount here to make sure the sk pointer cannot + * be freed and reused + */ + sock_hold(el_socket->sk); sock_tag_entry->sk = el_socket->sk; - sock_tag_entry->socket = el_socket; sock_tag_entry->pid = current->tgid; sock_tag_entry->tag = combine_atag_with_uid(acct_tag, uid_int); spin_lock_bh(&uid_tag_data_tree_lock); @@ -2340,10 +2337,11 @@ static int ctrl_cmd_tag(const char *input) atomic64_inc(&qtu_events.sockets_tagged); } spin_unlock_bh(&sock_tag_list_lock); - /* We keep the ref to the socket (file) until it is untagged */ - CT_DEBUG("qtaguid: ctrl_tag(%s): done st@%p ...->f_count=%ld\n", + /* We keep the ref to the sk until it is untagged */ + CT_DEBUG("qtaguid: ctrl_tag(%s): done st@%p ...->sk_refcnt=%d\n", input, sock_tag_entry, - atomic_long_read(&el_socket->file->f_count)); + atomic_read(&el_socket->sk->sk_refcnt)); + sockfd_put(el_socket); return 0; err_tag_unref_put: @@ -2351,8 +2349,8 @@ static int ctrl_cmd_tag(const char *input) tag_ref_entry->num_sock_tags--; free_tag_ref_from_utd_entry(tag_ref_entry, uid_tag_data_entry); err_put: - CT_DEBUG("qtaguid: ctrl_tag(%s): done. ...->f_count=%ld\n", - input, atomic_long_read(&el_socket->file->f_count) - 1); + CT_DEBUG("qtaguid: ctrl_tag(%s): done. ...->sk_refcnt=%d\n", + input, atomic_read(&el_socket->sk->sk_refcnt) - 1); /* Release the sock_fd that was grabbed by sockfd_lookup(). */ sockfd_put(el_socket); return res; @@ -2368,17 +2366,13 @@ static int ctrl_cmd_untag(const char *input) int sock_fd = 0; struct socket *el_socket; int res, argc; - struct sock_tag *sock_tag_entry; - struct tag_ref *tag_ref_entry; - struct uid_tag_data *utd_entry; - struct proc_qtu_data *pqd_entry; argc = sscanf(input, "%c %d", &cmd, &sock_fd); CT_DEBUG("qtaguid: ctrl_untag(%s): argc=%d cmd=%c sock_fd=%d\n", input, argc, cmd, sock_fd); if (argc < 2) { res = -EINVAL; - goto err; + return res; } el_socket = sockfd_lookup(sock_fd, &res); /* This locks the file */ if (!el_socket) { @@ -2386,17 +2380,31 @@ static int ctrl_cmd_untag(const char *input) " sock_fd=%d err=%d pid=%u tgid=%u uid=%u\n", input, sock_fd, res, current->pid, current->tgid, from_kuid(&init_user_ns, current_fsuid())); - goto err; + return res; } CT_DEBUG("qtaguid: ctrl_untag(%s): socket->...->f_count=%ld ->sk=%p\n", input, atomic_long_read(&el_socket->file->f_count), el_socket->sk); + res = qtaguid_untag(el_socket, false); + sockfd_put(el_socket); + return res; +} + +int qtaguid_untag(struct socket *el_socket, bool kernel) +{ + int res; + pid_t pid; + struct sock_tag *sock_tag_entry; + struct tag_ref *tag_ref_entry; + struct uid_tag_data *utd_entry; + struct proc_qtu_data *pqd_entry; + spin_lock_bh(&sock_tag_list_lock); sock_tag_entry = get_sock_stat_nl(el_socket->sk); if (!sock_tag_entry) { spin_unlock_bh(&sock_tag_list_lock); res = -EINVAL; - goto err_put; + return res; } /* * The socket already belongs to the current process @@ -2408,20 +2416,26 @@ static int ctrl_cmd_untag(const char *input) BUG_ON(!tag_ref_entry); BUG_ON(tag_ref_entry->num_sock_tags <= 0); spin_lock_bh(&uid_tag_data_tree_lock); + if (kernel) + pid = sock_tag_entry->pid; + else + pid = current->tgid; pqd_entry = proc_qtu_data_tree_search( - &proc_qtu_data_tree, current->tgid); + &proc_qtu_data_tree, pid); /* * TODO: remove if, and start failing. * At first, we want to catch user-space code that is not * opening the /dev/xt_qtaguid. */ - if (IS_ERR_OR_NULL(pqd_entry)) + if (IS_ERR_OR_NULL(pqd_entry) || !sock_tag_entry->list.next) { pr_warn_once("qtaguid: %s(): " "User space forgot to open /dev/xt_qtaguid? " - "pid=%u tgid=%u uid=%u\n", __func__, - current->pid, current->tgid, from_kuid(&init_user_ns, current_fsuid())); - else + "pid=%u tgid=%u sk_pid=%u, uid=%u\n", __func__, + current->pid, current->tgid, sock_tag_entry->pid, + from_kuid(&init_user_ns, current_fsuid())); + } else { list_del(&sock_tag_entry->list); + } spin_unlock_bh(&uid_tag_data_tree_lock); /* * We don't free tag_ref from the utd_entry here, @@ -2430,30 +2444,17 @@ static int ctrl_cmd_untag(const char *input) tag_ref_entry->num_sock_tags--; spin_unlock_bh(&sock_tag_list_lock); /* - * Release the sock_fd that was grabbed at tag time, - * and once more for the sockfd_lookup() here. + * Release the sock_fd that was grabbed at tag time. */ - sockfd_put(sock_tag_entry->socket); - CT_DEBUG("qtaguid: ctrl_untag(%s): done. st@%p ...->f_count=%ld\n", - input, sock_tag_entry, - atomic_long_read(&el_socket->file->f_count) - 1); - sockfd_put(el_socket); + sock_put(sock_tag_entry->sk); + CT_DEBUG("qtaguid: done. st@%p ...->sk_refcnt=%d\n", + sock_tag_entry, + atomic_read(&el_socket->sk->sk_refcnt)); kfree(sock_tag_entry); atomic64_inc(&qtu_events.sockets_untagged); return 0; - -err_put: - CT_DEBUG("qtaguid: ctrl_untag(%s): done. socket->...->f_count=%ld\n", - input, atomic_long_read(&el_socket->file->f_count) - 1); - /* Release the sock_fd that was grabbed by sockfd_lookup(). */ - sockfd_put(el_socket); - return res; - -err: - CT_DEBUG("qtaguid: ctrl_untag(%s): done.\n", input); - return res; } static ssize_t qtaguid_ctrl_parse(const char *input, size_t count) diff --git a/net/netfilter/xt_qtaguid_internal.h b/net/netfilter/xt_qtaguid_internal.h index 6dc14a9c6889..8178fbdfb036 100644 --- a/net/netfilter/xt_qtaguid_internal.h +++ b/net/netfilter/xt_qtaguid_internal.h @@ -256,8 +256,6 @@ struct iface_stat_work { struct sock_tag { struct rb_node sock_node; struct sock *sk; /* Only used as a number, never dereferenced */ - /* The socket is needed for sockfd_put() */ - struct socket *socket; /* Used to associate with a given pid */ struct list_head list; /* in proc_qtu_data.sock_tag_list */ pid_t pid; diff --git a/net/netfilter/xt_qtaguid_print.c b/net/netfilter/xt_qtaguid_print.c index f6a00a3520ed..2a7190d285e6 100644 --- a/net/netfilter/xt_qtaguid_print.c +++ b/net/netfilter/xt_qtaguid_print.c @@ -24,7 +24,7 @@ #include #include #include - +#include #include "xt_qtaguid_internal.h" #include "xt_qtaguid_print.h" @@ -237,10 +237,10 @@ char *pp_sock_tag(struct sock_tag *st) tag_str = pp_tag_t(&st->tag); res = kasprintf(GFP_ATOMIC, "sock_tag@%p{" "sock_node=rb_node{...}, " - "sk=%p socket=%p (f_count=%lu), list=list_head{...}, " + "sk=%p (f_count=%d), list=list_head{...}, " "pid=%u, tag=%s}", - st, st->sk, st->socket, atomic_long_read( - &st->socket->file->f_count), + st, st->sk, atomic_read( + &st->sk->sk_refcnt), st->pid, tag_str); _bug_on_err_or_null(res); kfree(tag_str); From a98df7f8d7dfca61ff6dd505eaf0c9aed002fd0d Mon Sep 17 00:00:00 2001 From: Simon Dubray Date: Tue, 1 Aug 2017 18:22:47 +0200 Subject: [PATCH 0207/1103] ANDROID: netfilter: xt_qtaguid: handle properly request sockets To match rules related to uid/gid for syn recv packets we need to get the full socket from request_sock struct. Bug: 63917742 Change-Id: I03acb2251319fd800d0e36a6dde30fc1fbb7d1b0 Signed-off-by: Simon Dubray --- net/netfilter/xt_qtaguid.c | 29 ++++++++++++++++++----------- 1 file changed, 18 insertions(+), 11 deletions(-) diff --git a/net/netfilter/xt_qtaguid.c b/net/netfilter/xt_qtaguid.c index a7f4d8b3e9e8..00a0ebbbb609 100644 --- a/net/netfilter/xt_qtaguid.c +++ b/net/netfilter/xt_qtaguid.c @@ -1597,14 +1597,6 @@ static struct sock *qtaguid_find_sk(const struct sk_buff *skb, if (sk) { MT_DEBUG("qtaguid: %p->sk_proto=%u " "->sk_state=%d\n", sk, sk->sk_protocol, sk->sk_state); - /* - * When in TCP_TIME_WAIT the sk is not a "struct sock" but - * "struct inet_timewait_sock" which is missing fields. - */ - if (!sk_fullsock(sk) || sk->sk_state == TCP_TIME_WAIT) { - sock_gen_put(sk); - sk = NULL; - } } return sk; } @@ -1697,10 +1689,25 @@ static bool qtaguid_mt(const struct sk_buff *skb, struct xt_action_param *par) */ sk = qtaguid_find_sk(skb, par); /* - * If we got the socket from the find_sk(), we will need to put - * it back, as nf_tproxy_get_sock_v4() got it. + * TCP_NEW_SYN_RECV are not "struct sock" but "struct request_sock" + * where we can get a pointer to a full socket to retrieve uid/gid. + * When in TCP_TIME_WAIT, sk is a struct inet_timewait_sock + * which is missing fields and does not contain any reference + * to a full socket, so just ignore the socket. */ - got_sock = sk; + if (sk && sk->sk_state == TCP_NEW_SYN_RECV) { + sock_gen_put(sk); + sk = sk_to_full_sk(sk); + } else if (sk && (!sk_fullsock(sk) || sk->sk_state == TCP_TIME_WAIT)) { + sock_gen_put(sk); + sk = NULL; + } else { + /* + * If we got the socket from the find_sk(), we will need to put + * it back, as nf_tproxy_get_sock_v4() got it. + */ + got_sock = sk; + } if (sk) atomic64_inc(&qtu_events.match_found_sk_in_ct); else From 5e4f8bc6feaa69141df4aa27b7a42ccdd0226e21 Mon Sep 17 00:00:00 2001 From: JP Abgrall Date: Fri, 20 Dec 2013 16:51:11 -0800 Subject: [PATCH 0208/1103] ANDROID: netfilter: xt_qtaguid: fix handling for cases where tunnels are used. * fix skb->dev vs par->in/out When there is some forwarding going on, it introduces extra state around devs associated with xt_action_param->in/out and sk_buff->dev. E.g. par->in and par->out are both set, or skb->dev and par->out are both set (and different) This would lead qtaguid to make the wrong assumption about the direction and update the wrong device stats. Now we rely more on par->in/out. * Fix handling when qtaguid is used as "owner" When qtaguid is used as an owner module, and sk_socket->file is not there (happens when tunnels are involved), it would incorrectly do a tag stats update. * Correct debug messages. Bug: 11687690 Change-Id: I2b1ff8bd7131969ce9e25f8291d83a6280b3ba7f CRs-Fixed: 747810 Signed-off-by: JP Abgrall Git-commit: 2b71479d6f5fe8f33b335f713380f72037244395 Git-repo: https://www.codeaurora.org/cgit/quic/la/kernel/mediatek [imaund@codeaurora.org: Resolved trivial context conflicts.] Signed-off-by: Ian Maund [bflowers@codeaurora.org: Resolved merge conflicts] Signed-off-by: Bryse Flowers Signed-off-by: Chenbo Feng --- net/netfilter/xt_qtaguid.c | 144 ++++++++++++++++++------------------- 1 file changed, 69 insertions(+), 75 deletions(-) diff --git a/net/netfilter/xt_qtaguid.c b/net/netfilter/xt_qtaguid.c index 00a0ebbbb609..5f23d35ebb96 100644 --- a/net/netfilter/xt_qtaguid.c +++ b/net/netfilter/xt_qtaguid.c @@ -1174,6 +1174,38 @@ static void iface_stat_update(struct net_device *net_dev, bool stash_only) spin_unlock_bh(&iface_stat_list_lock); } +/* Guarantied to return a net_device that has a name */ +static void get_dev_and_dir(const struct sk_buff *skb, + struct xt_action_param *par, + enum ifs_tx_rx *direction, + const struct net_device **el_dev) +{ + BUG_ON(!direction || !el_dev); + + if (par->in) { + *el_dev = par->in; + *direction = IFS_RX; + } else if (par->out) { + *el_dev = par->out; + *direction = IFS_TX; + } else { + pr_err("qtaguid[%d]: %s(): no par->in/out?!!\n", + par->hooknum, __func__); + BUG(); + } + if (unlikely(!(*el_dev)->name)) { + pr_err("qtaguid[%d]: %s(): no dev->name?!!\n", + par->hooknum, __func__); + BUG(); + } + if (skb->dev && *el_dev != skb->dev) { + MT_DEBUG("qtaguid[%d]: skb->dev=%p %s vs par->%s=%p %s\n", + par->hooknum, skb->dev, skb->dev->name, + *direction == IFS_RX ? "in" : "out", *el_dev, + (*el_dev)->name); + } +} + /* * Update stats for the specified interface from the skb. * Do nothing if the entry @@ -1185,46 +1217,27 @@ static void iface_stat_update_from_skb(const struct sk_buff *skb, { struct iface_stat *entry; const struct net_device *el_dev; - enum ifs_tx_rx direction = par->in ? IFS_RX : IFS_TX; + enum ifs_tx_rx direction; int bytes = skb->len; int proto; - if (!skb->dev) { - MT_DEBUG("qtaguid[%d]: no skb->dev\n", par->hooknum); - el_dev = par->in ? : par->out; - } else { - const struct net_device *other_dev; - el_dev = skb->dev; - other_dev = par->in ? : par->out; - if (el_dev != other_dev) { - MT_DEBUG("qtaguid[%d]: skb->dev=%p %s vs " - "par->(in/out)=%p %s\n", - par->hooknum, el_dev, el_dev->name, other_dev, - other_dev->name); - } - } - - if (unlikely(!el_dev)) { - pr_err_ratelimited("qtaguid[%d]: %s(): no par->in/out?!!\n", - par->hooknum, __func__); - BUG(); - } else { - proto = ipx_proto(skb, par); - MT_DEBUG("qtaguid[%d]: dev name=%s type=%d fam=%d proto=%d\n", - par->hooknum, el_dev->name, el_dev->type, - par->family, proto); - } + get_dev_and_dir(skb, par, &direction, &el_dev); + proto = ipx_proto(skb, par); + MT_DEBUG("qtaguid[%d]: iface_stat: %s(%s): " + "type=%d fam=%d proto=%d dir=%d\n", + par->hooknum, __func__, el_dev->name, el_dev->type, + par->family, proto, direction); spin_lock_bh(&iface_stat_list_lock); entry = get_iface_entry(el_dev->name); if (entry == NULL) { - IF_DEBUG("qtaguid: iface_stat: %s(%s): not tracked\n", - __func__, el_dev->name); + IF_DEBUG("qtaguid[%d]: iface_stat: %s(%s): not tracked\n", + par->hooknum, __func__, el_dev->name); spin_unlock_bh(&iface_stat_list_lock); return; } - IF_DEBUG("qtaguid: %s(%s): entry=%p\n", __func__, + IF_DEBUG("qtaguid[%d]: %s(%s): entry=%p\n", par->hooknum, __func__, el_dev->name, entry); data_counters_update(&entry->totals_via_skb, 0, direction, proto, @@ -1289,14 +1302,14 @@ static void if_tag_stat_update(const char *ifname, uid_t uid, spin_lock_bh(&iface_stat_list_lock); iface_entry = get_iface_entry(ifname); if (!iface_entry) { - pr_err_ratelimited("qtaguid: iface_stat: stat_update() " + pr_err_ratelimited("qtaguid: tag_stat: stat_update() " "%s not found\n", ifname); spin_unlock_bh(&iface_stat_list_lock); return; } /* It is ok to process data when an iface_entry is inactive */ - MT_DEBUG("qtaguid: iface_stat: stat_update() dev=%s entry=%p\n", + MT_DEBUG("qtaguid: tag_stat: stat_update() dev=%s entry=%p\n", ifname, iface_entry); /* @@ -1313,7 +1326,7 @@ static void if_tag_stat_update(const char *ifname, uid_t uid, tag = combine_atag_with_uid(acct_tag, uid); uid_tag = make_tag_from_uid(uid); } - MT_DEBUG("qtaguid: iface_stat: stat_update(): " + MT_DEBUG("qtaguid: tag_stat: stat_update(): " " looking for tag=0x%llx (uid=%u) in ife=%p\n", tag, get_uid_from_tag(tag), iface_entry); /* Loop over tag list under this interface for {acct_tag,uid_tag} */ @@ -1573,8 +1586,8 @@ static struct sock *qtaguid_find_sk(const struct sk_buff *skb, struct sock *sk; unsigned int hook_mask = (1 << par->hooknum); - MT_DEBUG("qtaguid: find_sk(skb=%p) hooknum=%d family=%d\n", skb, - par->hooknum, par->family); + MT_DEBUG("qtaguid[%d]: find_sk(skb=%p) family=%d\n", + par->hooknum, skb, par->family); /* * Let's not abuse the the xt_socket_get*_sk(), or else it will @@ -1595,8 +1608,8 @@ static struct sock *qtaguid_find_sk(const struct sk_buff *skb, } if (sk) { - MT_DEBUG("qtaguid: %p->sk_proto=%u " - "->sk_state=%d\n", sk, sk->sk_protocol, sk->sk_state); + MT_DEBUG("qtaguid[%d]: %p->sk_proto=%u->sk_state=%d\n", + par->hooknum, sk, sk->sk_protocol, sk->sk_state); } return sk; } @@ -1606,35 +1619,19 @@ static void account_for_uid(const struct sk_buff *skb, struct xt_action_param *par) { const struct net_device *el_dev; + enum ifs_tx_rx direction; + int proto; - if (!skb->dev) { - MT_DEBUG("qtaguid[%d]: no skb->dev\n", par->hooknum); - el_dev = par->in ? : par->out; - } else { - const struct net_device *other_dev; - el_dev = skb->dev; - other_dev = par->in ? : par->out; - if (el_dev != other_dev) { - MT_DEBUG("qtaguid[%d]: skb->dev=%p %s vs " - "par->(in/out)=%p %s\n", - par->hooknum, el_dev, el_dev->name, other_dev, - other_dev->name); - } - } - - if (unlikely(!el_dev)) { - pr_info("qtaguid[%d]: no par->in/out?!!\n", par->hooknum); - } else { - int proto = ipx_proto(skb, par); - MT_DEBUG("qtaguid[%d]: dev name=%s type=%d fam=%d proto=%d\n", - par->hooknum, el_dev->name, el_dev->type, - par->family, proto); + get_dev_and_dir(skb, par, &direction, &el_dev); + proto = ipx_proto(skb, par); + MT_DEBUG("qtaguid[%d]: dev name=%s type=%d fam=%d proto=%d dir=%d\n", + par->hooknum, el_dev->name, el_dev->type, + par->family, proto, direction); - if_tag_stat_update(el_dev->name, uid, - skb->sk ? skb->sk : alternate_sk, - par->in ? IFS_RX : IFS_TX, - proto, skb->len); - } + if_tag_stat_update(el_dev->name, uid, + skb->sk ? skb->sk : alternate_sk, + direction, + proto, skb->len); } static bool qtaguid_mt(const struct sk_buff *skb, struct xt_action_param *par) @@ -1646,6 +1643,11 @@ static bool qtaguid_mt(const struct sk_buff *skb, struct xt_action_param *par) kuid_t sock_uid; bool res; bool set_sk_callback_lock = false; + /* + * TODO: unhack how to force just accounting. + * For now we only do tag stats when the uid-owner is not requested + */ + bool do_tag_stat = !(info->match & XT_QTAGUID_UID); if (unlikely(module_passive)) return (info->match ^ info->invert) == 0; @@ -1734,12 +1736,7 @@ static bool qtaguid_mt(const struct sk_buff *skb, struct xt_action_param *par) * couldn't find the owner, so for now we just count them * against the system. */ - /* - * TODO: unhack how to force just accounting. - * For now we only do iface stats when the uid-owner is not - * requested. - */ - if (!(info->match & XT_QTAGUID_UID)) + if (do_tag_stat) account_for_uid(skb, sk, 0, par); MT_DEBUG("qtaguid[%d]: leaving (sk?sk->sk_socket)=%p\n", par->hooknum, @@ -1754,18 +1751,15 @@ static bool qtaguid_mt(const struct sk_buff *skb, struct xt_action_param *par) filp = sk->sk_socket->file; if (filp == NULL) { MT_DEBUG("qtaguid[%d]: leaving filp=NULL\n", par->hooknum); - account_for_uid(skb, sk, 0, par); + if (do_tag_stat) + account_for_uid(skb, sk, 0, par); res = ((info->match ^ info->invert) & (XT_QTAGUID_UID | XT_QTAGUID_GID)) == 0; atomic64_inc(&qtu_events.match_no_sk_file); goto put_sock_ret_res; } sock_uid = filp->f_cred->fsuid; - /* - * TODO: unhack how to force just accounting. - * For now we only do iface stats when the uid-owner is not requested - */ - if (!(info->match & XT_QTAGUID_UID)) + if (do_tag_stat) account_for_uid(skb, sk, from_kuid(&init_user_ns, sock_uid), par); /* From 15c0cc51e86ef40db450e952431d69ae8ffa2883 Mon Sep 17 00:00:00 2001 From: Chenbo Feng Date: Thu, 20 Apr 2017 18:54:13 -0700 Subject: [PATCH 0209/1103] ANDROID: netfilter: xt_qtaguid: Use sk_uid to replace uid get from socket file Retrieve socket uid from the sk_uid field added to struct sk instead of read it from sk->socket->file. It prevent the packet been dropped when the socket file doesn't exist. Bug: 37524657 Signed-off-by: Chenbo Feng Change-Id: Ic58239c1f9aa7e0eb1d4d1c09d40b845fd4e8e57 --- net/netfilter/xt_qtaguid.c | 55 +++++++++++++---------------- net/netfilter/xt_qtaguid_internal.h | 4 +-- 2 files changed, 26 insertions(+), 33 deletions(-) diff --git a/net/netfilter/xt_qtaguid.c b/net/netfilter/xt_qtaguid.c index 5f23d35ebb96..661a496f2c04 100644 --- a/net/netfilter/xt_qtaguid.c +++ b/net/netfilter/xt_qtaguid.c @@ -1719,18 +1719,8 @@ static bool qtaguid_mt(const struct sk_buff *skb, struct xt_action_param *par) } MT_DEBUG("qtaguid[%d]: sk=%p got_sock=%d fam=%d proto=%d\n", par->hooknum, sk, got_sock, par->family, ipx_proto(skb, par)); - if (sk != NULL) { - set_sk_callback_lock = true; - read_lock_bh(&sk->sk_callback_lock); - MT_DEBUG("qtaguid[%d]: sk=%p->sk_socket=%p->file=%p\n", - par->hooknum, sk, sk->sk_socket, - sk->sk_socket ? sk->sk_socket->file : (void *)-1LL); - filp = sk->sk_socket ? sk->sk_socket->file : NULL; - MT_DEBUG("qtaguid[%d]: filp...uid=%u\n", - par->hooknum, filp ? from_kuid(&init_user_ns, filp->f_cred->fsuid) : -1); - } - if (sk == NULL || sk->sk_socket == NULL) { + if (!sk) { /* * Here, the qtaguid_find_sk() using connection tracking * couldn't find the owner, so for now we just count them @@ -1738,9 +1728,7 @@ static bool qtaguid_mt(const struct sk_buff *skb, struct xt_action_param *par) */ if (do_tag_stat) account_for_uid(skb, sk, 0, par); - MT_DEBUG("qtaguid[%d]: leaving (sk?sk->sk_socket)=%p\n", - par->hooknum, - sk ? sk->sk_socket : NULL); + MT_DEBUG("qtaguid[%d]: leaving (sk=NULL)\n", par->hooknum); res = (info->match ^ info->invert) == 0; atomic64_inc(&qtu_events.match_no_sk); goto put_sock_ret_res; @@ -1748,19 +1736,10 @@ static bool qtaguid_mt(const struct sk_buff *skb, struct xt_action_param *par) res = false; goto put_sock_ret_res; } - filp = sk->sk_socket->file; - if (filp == NULL) { - MT_DEBUG("qtaguid[%d]: leaving filp=NULL\n", par->hooknum); - if (do_tag_stat) - account_for_uid(skb, sk, 0, par); - res = ((info->match ^ info->invert) & - (XT_QTAGUID_UID | XT_QTAGUID_GID)) == 0; - atomic64_inc(&qtu_events.match_no_sk_file); - goto put_sock_ret_res; - } - sock_uid = filp->f_cred->fsuid; + sock_uid = sk->sk_uid; if (do_tag_stat) - account_for_uid(skb, sk, from_kuid(&init_user_ns, sock_uid), par); + account_for_uid(skb, sk, from_kuid(&init_user_ns, sock_uid), + par); /* * The following two tests fail the match when: @@ -1772,8 +1751,8 @@ static bool qtaguid_mt(const struct sk_buff *skb, struct xt_action_param *par) kuid_t uid_min = make_kuid(&init_user_ns, info->uid_min); kuid_t uid_max = make_kuid(&init_user_ns, info->uid_max); - if ((uid_gte(filp->f_cred->fsuid, uid_min) && - uid_lte(filp->f_cred->fsuid, uid_max)) ^ + if ((uid_gte(sock_uid, uid_min) && + uid_lte(sock_uid, uid_max)) ^ !(info->invert & XT_QTAGUID_UID)) { MT_DEBUG("qtaguid[%d]: leaving uid not matching\n", par->hooknum); @@ -1784,7 +1763,21 @@ static bool qtaguid_mt(const struct sk_buff *skb, struct xt_action_param *par) if (info->match & XT_QTAGUID_GID) { kgid_t gid_min = make_kgid(&init_user_ns, info->gid_min); kgid_t gid_max = make_kgid(&init_user_ns, info->gid_max); - + set_sk_callback_lock = true; + read_lock_bh(&sk->sk_callback_lock); + MT_DEBUG("qtaguid[%d]: sk=%p->sk_socket=%p->file=%p\n", + par->hooknum, sk, sk->sk_socket, + sk->sk_socket ? sk->sk_socket->file : (void *)-1LL); + filp = sk->sk_socket ? sk->sk_socket->file : NULL; + if (!filp) { + res = ((info->match ^ info->invert) & + XT_QTAGUID_GID) == 0; + atomic64_inc(&qtu_events.match_no_sk_gid); + goto put_sock_ret_res; + } + MT_DEBUG("qtaguid[%d]: filp...uid=%u\n", + par->hooknum, filp ? + from_kuid(&init_user_ns, filp->f_cred->fsuid) : -1); if ((gid_gte(filp->f_cred->fsgid, gid_min) && gid_lte(filp->f_cred->fsgid, gid_max)) ^ !(info->invert & XT_QTAGUID_GID)) { @@ -1956,7 +1949,7 @@ static int qtaguid_ctrl_proc_show(struct seq_file *m, void *v) "match_found_sk_in_ct=%llu " "match_found_no_sk_in_ct=%llu " "match_no_sk=%llu " - "match_no_sk_file=%llu\n", + "match_no_sk_gid=%llu\n", (u64)atomic64_read(&qtu_events.sockets_tagged), (u64)atomic64_read(&qtu_events.sockets_untagged), (u64)atomic64_read(&qtu_events.counter_set_changes), @@ -1968,7 +1961,7 @@ static int qtaguid_ctrl_proc_show(struct seq_file *m, void *v) (u64)atomic64_read(&qtu_events.match_found_sk_in_ct), (u64)atomic64_read(&qtu_events.match_found_no_sk_in_ct), (u64)atomic64_read(&qtu_events.match_no_sk), - (u64)atomic64_read(&qtu_events.match_no_sk_file)); + (u64)atomic64_read(&qtu_events.match_no_sk_gid)); /* Count the following as part of the last item_index. No need * to lock the sock_tag_list here since it is already locked when diff --git a/net/netfilter/xt_qtaguid_internal.h b/net/netfilter/xt_qtaguid_internal.h index 8178fbdfb036..c7052707a6a4 100644 --- a/net/netfilter/xt_qtaguid_internal.h +++ b/net/netfilter/xt_qtaguid_internal.h @@ -289,10 +289,10 @@ struct qtaguid_event_counts { */ atomic64_t match_no_sk; /* - * The file ptr in the sk_socket wasn't there. + * The file ptr in the sk_socket wasn't there and we couldn't get GID. * This might happen for traffic while the socket is being closed. */ - atomic64_t match_no_sk_file; + atomic64_t match_no_sk_gid; }; /* Track the set active_set for the given tag. */ From cf5f377101bb12772681ff427b998785aa3834ea Mon Sep 17 00:00:00 2001 From: JP Abgrall Date: Thu, 26 Apr 2012 23:28:35 -0700 Subject: [PATCH 0210/1103] ANDROID: netfilter: xt_IDLETIMER: Add new netlink msg type Send notifications when the label becomes active after an idle period. Send netlink message notifications in addition to sysfs notifications. Using a uevent with subsystem=xt_idletimer INTERFACE=... STATE={active,inactive} This is backport from common android-3.0 commit: beb914e987cbbd368988d2b94a6661cb907c4d5a with uevent support instead of a new netlink message type. Change-Id: I31677ef00c94b5f82c8457e5bf9e5e584c23c523 Signed-off-by: Ashish Sharma Signed-off-by: JP Abgrall --- include/uapi/linux/netfilter/xt_IDLETIMER.h | 8 +++ net/netfilter/xt_IDLETIMER.c | 78 +++++++++++++++++++-- 2 files changed, 79 insertions(+), 7 deletions(-) diff --git a/include/uapi/linux/netfilter/xt_IDLETIMER.h b/include/uapi/linux/netfilter/xt_IDLETIMER.h index 3c586a19baea..c82a1c1d53ec 100644 --- a/include/uapi/linux/netfilter/xt_IDLETIMER.h +++ b/include/uapi/linux/netfilter/xt_IDLETIMER.h @@ -5,6 +5,7 @@ * Header file for Xtables timer target module. * * Copyright (C) 2004, 2010 Nokia Corporation + * * Written by Timo Teras * * Converted to x_tables and forward-ported to 2.6.34 @@ -33,12 +34,19 @@ #include #define MAX_IDLETIMER_LABEL_SIZE 28 +#define NLMSG_MAX_SIZE 64 + +#define NL_EVENT_TYPE_INACTIVE 0 +#define NL_EVENT_TYPE_ACTIVE 1 struct idletimer_tg_info { __u32 timeout; char label[MAX_IDLETIMER_LABEL_SIZE]; + /* Use netlink messages for notification in addition to sysfs */ + __u8 send_nl_msg; + /* for kernel module internal use only */ struct idletimer_tg *timer __attribute__((aligned(8))); }; diff --git a/net/netfilter/xt_IDLETIMER.c b/net/netfilter/xt_IDLETIMER.c index 5ee859193783..c76e5d4b088a 100644 --- a/net/netfilter/xt_IDLETIMER.c +++ b/net/netfilter/xt_IDLETIMER.c @@ -5,6 +5,7 @@ * After timer expires a kevent will be sent. * * Copyright (C) 2004, 2010 Nokia Corporation + * * Written by Timo Teras * * Converted to x_tables and reworked for upstream inclusion @@ -38,8 +39,10 @@ #include #include #include +#include #include #include +#include struct idletimer_tg_attr { struct attribute attr; @@ -56,6 +59,8 @@ struct idletimer_tg { struct idletimer_tg_attr attr; unsigned int refcnt; + bool send_nl_msg; + bool active; }; static LIST_HEAD(idletimer_tg_list); @@ -63,6 +68,32 @@ static DEFINE_MUTEX(list_mutex); static struct kobject *idletimer_tg_kobj; +static void notify_netlink_uevent(const char *iface, struct idletimer_tg *timer) +{ + char iface_msg[NLMSG_MAX_SIZE]; + char state_msg[NLMSG_MAX_SIZE]; + char *envp[] = { iface_msg, state_msg, NULL }; + int res; + + res = snprintf(iface_msg, NLMSG_MAX_SIZE, "INTERFACE=%s", + iface); + if (NLMSG_MAX_SIZE <= res) { + pr_err("message too long (%d)", res); + return; + } + res = snprintf(state_msg, NLMSG_MAX_SIZE, "STATE=%s", + timer->active ? "active" : "inactive"); + if (NLMSG_MAX_SIZE <= res) { + pr_err("message too long (%d)", res); + return; + } + pr_debug("putting nlmsg: <%s> <%s>\n", iface_msg, state_msg); + kobject_uevent_env(idletimer_tg_kobj, KOBJ_CHANGE, envp); + return; + + +} + static struct idletimer_tg *__idletimer_tg_find_by_label(const char *label) { @@ -83,6 +114,7 @@ static ssize_t idletimer_tg_show(struct kobject *kobj, struct attribute *attr, { struct idletimer_tg *timer; unsigned long expires = 0; + unsigned long now = jiffies; mutex_lock(&list_mutex); @@ -92,11 +124,15 @@ static ssize_t idletimer_tg_show(struct kobject *kobj, struct attribute *attr, mutex_unlock(&list_mutex); - if (time_after(expires, jiffies)) + if (time_after(expires, now)) return sprintf(buf, "%u\n", - jiffies_to_msecs(expires - jiffies) / 1000); + jiffies_to_msecs(expires - now) / 1000); - return sprintf(buf, "0\n"); + if (timer->send_nl_msg) + return sprintf(buf, "0 %d\n", + jiffies_to_msecs(now - expires) / 1000); + else + return sprintf(buf, "0\n"); } static void idletimer_tg_work(struct work_struct *work) @@ -105,6 +141,9 @@ static void idletimer_tg_work(struct work_struct *work) work); sysfs_notify(idletimer_tg_kobj, NULL, timer->attr.attr.name); + + if (timer->send_nl_msg) + notify_netlink_uevent(timer->attr.attr.name, timer); } static void idletimer_tg_expired(struct timer_list *t) @@ -113,6 +152,7 @@ static void idletimer_tg_expired(struct timer_list *t) pr_debug("timer %s expired\n", timer->attr.attr.name); + timer->active = false; schedule_work(&timer->work); } @@ -145,6 +185,8 @@ static int idletimer_tg_create(struct idletimer_tg_info *info) timer_setup(&info->timer->timer, idletimer_tg_expired, 0); info->timer->refcnt = 1; + info->timer->send_nl_msg = (info->send_nl_msg == 0) ? false : true; + info->timer->active = true; INIT_WORK(&info->timer->work, idletimer_tg_work); @@ -168,14 +210,24 @@ static unsigned int idletimer_tg_target(struct sk_buff *skb, const struct xt_action_param *par) { const struct idletimer_tg_info *info = par->targinfo; + unsigned long now = jiffies; pr_debug("resetting timer %s, timeout period %u\n", info->label, info->timeout); BUG_ON(!info->timer); + info->timer->active = true; + + if (time_before(info->timer->timer.expires, now)) { + schedule_work(&info->timer->work); + pr_debug("Starting timer %s (Expired, Jiffies): %lu, %lu\n", + info->label, info->timer->timer.expires, now); + } + + /* TODO: Avoid modifying timers on each packet */ mod_timer(&info->timer->timer, - msecs_to_jiffies(info->timeout * 1000) + jiffies); + msecs_to_jiffies(info->timeout * 1000) + now); return XT_CONTINUE; } @@ -184,8 +236,9 @@ static int idletimer_tg_checkentry(const struct xt_tgchk_param *par) { struct idletimer_tg_info *info = par->targinfo; int ret; + unsigned long now = jiffies; - pr_debug("checkentry targinfo%s\n", info->label); + pr_debug("checkentry targinfo %s\n", info->label); if (info->timeout == 0) { pr_debug("timeout value is zero\n"); @@ -207,8 +260,16 @@ static int idletimer_tg_checkentry(const struct xt_tgchk_param *par) info->timer = __idletimer_tg_find_by_label(info->label); if (info->timer) { info->timer->refcnt++; + info->timer->active = true; + + if (time_before(info->timer->timer.expires, now)) { + schedule_work(&info->timer->work); + pr_debug("Starting Checkentry timer (Expired, Jiffies): %lu, %lu\n", + info->timer->timer.expires, now); + } + mod_timer(&info->timer->timer, - msecs_to_jiffies(info->timeout * 1000) + jiffies); + msecs_to_jiffies(info->timeout * 1000) + now); pr_debug("increased refcnt of timer %s to %u\n", info->label, info->timer->refcnt); @@ -222,6 +283,7 @@ static int idletimer_tg_checkentry(const struct xt_tgchk_param *par) } mutex_unlock(&list_mutex); + return 0; } @@ -244,7 +306,7 @@ static void idletimer_tg_destroy(const struct xt_tgdtor_param *par) kfree(info->timer); } else { pr_debug("decreased refcnt of timer %s to %u\n", - info->label, info->timer->refcnt); + info->label, info->timer->refcnt); } mutex_unlock(&list_mutex); @@ -252,6 +314,7 @@ static void idletimer_tg_destroy(const struct xt_tgdtor_param *par) static struct xt_target idletimer_tg __read_mostly = { .name = "IDLETIMER", + .revision = 1, .family = NFPROTO_UNSPEC, .target = idletimer_tg_target, .targetsize = sizeof(struct idletimer_tg_info), @@ -318,3 +381,4 @@ MODULE_DESCRIPTION("Xtables: idle time monitor"); MODULE_LICENSE("GPL v2"); MODULE_ALIAS("ipt_IDLETIMER"); MODULE_ALIAS("ip6t_IDLETIMER"); +MODULE_ALIAS("arpt_IDLETIMER"); From 7d2591be0c17d46be18143cf97f9532ef485346d Mon Sep 17 00:00:00 2001 From: Ruchi Kandoi Date: Tue, 25 Mar 2014 16:43:28 -0700 Subject: [PATCH 0211/1103] ANDROID: netfilter: xt_IDLETIMER: time-stamp and suspend/resume handling. Message notifications contains an additional timestamp field in nano seconds. The expiry time for the timers are modified during suspend/resume. If timer was supposed to expire while the system is suspended then a notification is sent when it resumes with the timestamp of the scheduled expiry. Removes the race condition for multiple work scheduled. Bug: 13247811 Change-Id: I752c5b00225fe7085482819f975cc0eb5af89bff Signed-off-by: Ruchi Kandoi [AmitP: Folded following android-4.9 commit changes into this patch 13e257eaa624 ("nf: Remove compilation error caused by e8430cbed3ef15fdb1ac26cfd020e010aa5f1c35")] Signed-off-by: Amit Pundir --- net/netfilter/xt_IDLETIMER.c | 170 +++++++++++++++++++++++++++++++---- 1 file changed, 152 insertions(+), 18 deletions(-) diff --git a/net/netfilter/xt_IDLETIMER.c b/net/netfilter/xt_IDLETIMER.c index c76e5d4b088a..25a021a9d8ef 100644 --- a/net/netfilter/xt_IDLETIMER.c +++ b/net/netfilter/xt_IDLETIMER.c @@ -42,6 +42,11 @@ #include #include #include +#include +#include +#include +#include +#include #include struct idletimer_tg_attr { @@ -58,22 +63,65 @@ struct idletimer_tg { struct kobject *kobj; struct idletimer_tg_attr attr; + struct timespec delayed_timer_trigger; + struct timespec last_modified_timer; + struct timespec last_suspend_time; + struct notifier_block pm_nb; + + int timeout; unsigned int refcnt; + bool work_pending; bool send_nl_msg; bool active; }; static LIST_HEAD(idletimer_tg_list); static DEFINE_MUTEX(list_mutex); +static DEFINE_SPINLOCK(timestamp_lock); static struct kobject *idletimer_tg_kobj; +static bool check_for_delayed_trigger(struct idletimer_tg *timer, + struct timespec *ts) +{ + bool state; + struct timespec temp; + spin_lock_bh(×tamp_lock); + timer->work_pending = false; + if ((ts->tv_sec - timer->last_modified_timer.tv_sec) > timer->timeout || + timer->delayed_timer_trigger.tv_sec != 0) { + state = false; + temp.tv_sec = timer->timeout; + temp.tv_nsec = 0; + if (timer->delayed_timer_trigger.tv_sec != 0) { + temp = timespec_add(timer->delayed_timer_trigger, temp); + ts->tv_sec = temp.tv_sec; + ts->tv_nsec = temp.tv_nsec; + timer->delayed_timer_trigger.tv_sec = 0; + timer->work_pending = true; + schedule_work(&timer->work); + } else { + temp = timespec_add(timer->last_modified_timer, temp); + ts->tv_sec = temp.tv_sec; + ts->tv_nsec = temp.tv_nsec; + } + } else { + state = timer->active; + } + spin_unlock_bh(×tamp_lock); + return state; +} + static void notify_netlink_uevent(const char *iface, struct idletimer_tg *timer) { char iface_msg[NLMSG_MAX_SIZE]; char state_msg[NLMSG_MAX_SIZE]; - char *envp[] = { iface_msg, state_msg, NULL }; + char timestamp_msg[NLMSG_MAX_SIZE]; + char *envp[] = { iface_msg, state_msg, timestamp_msg, NULL }; int res; + struct timespec ts; + uint64_t time_ns; + bool state; res = snprintf(iface_msg, NLMSG_MAX_SIZE, "INTERFACE=%s", iface); @@ -81,12 +129,24 @@ static void notify_netlink_uevent(const char *iface, struct idletimer_tg *timer) pr_err("message too long (%d)", res); return; } + + get_monotonic_boottime(&ts); + state = check_for_delayed_trigger(timer, &ts); res = snprintf(state_msg, NLMSG_MAX_SIZE, "STATE=%s", - timer->active ? "active" : "inactive"); + state ? "active" : "inactive"); + if (NLMSG_MAX_SIZE <= res) { pr_err("message too long (%d)", res); return; } + + time_ns = timespec_to_ns(&ts); + res = snprintf(timestamp_msg, NLMSG_MAX_SIZE, "TIME_NS=%llu", time_ns); + if (NLMSG_MAX_SIZE <= res) { + timestamp_msg[0] = '\0'; + pr_err("message too long (%d)", res); + } + pr_debug("putting nlmsg: <%s> <%s>\n", iface_msg, state_msg); kobject_uevent_env(idletimer_tg_kobj, KOBJ_CHANGE, envp); return; @@ -151,9 +211,55 @@ static void idletimer_tg_expired(struct timer_list *t) struct idletimer_tg *timer = from_timer(timer, t, timer); pr_debug("timer %s expired\n", timer->attr.attr.name); - + spin_lock_bh(×tamp_lock); timer->active = false; + timer->work_pending = true; schedule_work(&timer->work); + spin_unlock_bh(×tamp_lock); +} + +static int idletimer_resume(struct notifier_block *notifier, + unsigned long pm_event, void *unused) +{ + struct timespec ts; + unsigned long time_diff, now = jiffies; + struct idletimer_tg *timer = container_of(notifier, + struct idletimer_tg, pm_nb); + if (!timer) + return NOTIFY_DONE; + switch (pm_event) { + case PM_SUSPEND_PREPARE: + get_monotonic_boottime(&timer->last_suspend_time); + break; + case PM_POST_SUSPEND: + spin_lock_bh(×tamp_lock); + if (!timer->active) { + spin_unlock_bh(×tamp_lock); + break; + } + /* since jiffies are not updated when suspended now represents + * the time it would have suspended */ + if (time_after(timer->timer.expires, now)) { + get_monotonic_boottime(&ts); + ts = timespec_sub(ts, timer->last_suspend_time); + time_diff = timespec_to_jiffies(&ts); + if (timer->timer.expires > (time_diff + now)) { + mod_timer_pending(&timer->timer, + (timer->timer.expires - time_diff)); + } else { + del_timer(&timer->timer); + timer->timer.expires = 0; + timer->active = false; + timer->work_pending = true; + schedule_work(&timer->work); + } + } + spin_unlock_bh(×tamp_lock); + break; + default: + break; + } + return NOTIFY_DONE; } static int idletimer_tg_create(struct idletimer_tg_info *info) @@ -187,6 +293,18 @@ static int idletimer_tg_create(struct idletimer_tg_info *info) info->timer->refcnt = 1; info->timer->send_nl_msg = (info->send_nl_msg == 0) ? false : true; info->timer->active = true; + info->timer->timeout = info->timeout; + + info->timer->delayed_timer_trigger.tv_sec = 0; + info->timer->delayed_timer_trigger.tv_nsec = 0; + info->timer->work_pending = false; + get_monotonic_boottime(&info->timer->last_modified_timer); + + info->timer->pm_nb.notifier_call = idletimer_resume; + ret = register_pm_notifier(&info->timer->pm_nb); + if (ret) + printk(KERN_WARNING "[%s] Failed to register pm notifier %d\n", + __func__, ret); INIT_WORK(&info->timer->work, idletimer_tg_work); @@ -203,6 +321,34 @@ static int idletimer_tg_create(struct idletimer_tg_info *info) return ret; } +static void reset_timer(const struct idletimer_tg_info *info) +{ + unsigned long now = jiffies; + struct idletimer_tg *timer = info->timer; + bool timer_prev; + + spin_lock_bh(×tamp_lock); + timer_prev = timer->active; + timer->active = true; + /* timer_prev is used to guard overflow problem in time_before*/ + if (!timer_prev || time_before(timer->timer.expires, now)) { + pr_debug("Starting Checkentry timer (Expired, Jiffies): %lu, %lu\n", + timer->timer.expires, now); + /* checks if there is a pending inactive notification*/ + if (timer->work_pending) + timer->delayed_timer_trigger = timer->last_modified_timer; + else { + timer->work_pending = true; + schedule_work(&timer->work); + } + } + + get_monotonic_boottime(&timer->last_modified_timer); + mod_timer(&timer->timer, + msecs_to_jiffies(info->timeout * 1000) + now); + spin_unlock_bh(×tamp_lock); +} + /* * The actual xt_tables plugin. */ @@ -226,9 +372,7 @@ static unsigned int idletimer_tg_target(struct sk_buff *skb, } /* TODO: Avoid modifying timers on each packet */ - mod_timer(&info->timer->timer, - msecs_to_jiffies(info->timeout * 1000) + now); - + reset_timer(info); return XT_CONTINUE; } @@ -236,7 +380,6 @@ static int idletimer_tg_checkentry(const struct xt_tgchk_param *par) { struct idletimer_tg_info *info = par->targinfo; int ret; - unsigned long now = jiffies; pr_debug("checkentry targinfo %s\n", info->label); @@ -260,17 +403,7 @@ static int idletimer_tg_checkentry(const struct xt_tgchk_param *par) info->timer = __idletimer_tg_find_by_label(info->label); if (info->timer) { info->timer->refcnt++; - info->timer->active = true; - - if (time_before(info->timer->timer.expires, now)) { - schedule_work(&info->timer->work); - pr_debug("Starting Checkentry timer (Expired, Jiffies): %lu, %lu\n", - info->timer->timer.expires, now); - } - - mod_timer(&info->timer->timer, - msecs_to_jiffies(info->timeout * 1000) + now); - + reset_timer(info); pr_debug("increased refcnt of timer %s to %u\n", info->label, info->timer->refcnt); } else { @@ -302,6 +435,7 @@ static void idletimer_tg_destroy(const struct xt_tgdtor_param *par) del_timer_sync(&info->timer->timer); cancel_work_sync(&info->timer->work); sysfs_remove_file(idletimer_tg_kobj, &info->timer->attr.attr); + unregister_pm_notifier(&info->timer->pm_nb); kfree(info->timer->attr.attr.name); kfree(info->timer); } else { From c219ead5b0ca6019a6f46144a29c397d9c360257 Mon Sep 17 00:00:00 2001 From: Ruchi Kandoi Date: Thu, 23 Apr 2015 12:09:09 -0700 Subject: [PATCH 0212/1103] ANDROID: netfilter: xt_IDLETIMER: Adds the uid field in the msg Message notifications contains an additional uid field. This field represents the uid that was responsible for waking the radio. And hence it is present only in notifications stating that the radio is now active. Change-Id: I18fc73eada512e370d7ab24fc9f890845037b729 Signed-off-by: Ruchi Kandoi Bug: 20264396 [AmitP: Folded following android-4.9 commit changes into this patch 22ea73dee036 ("nf: IDLETIMER: Fix broken uid field in the msg")] Signed-off-by: Amit Pundir --- net/netfilter/xt_IDLETIMER.c | 33 ++++++++++++++++++++++++++++----- 1 file changed, 28 insertions(+), 5 deletions(-) diff --git a/net/netfilter/xt_IDLETIMER.c b/net/netfilter/xt_IDLETIMER.c index 25a021a9d8ef..b8ea93603b83 100644 --- a/net/netfilter/xt_IDLETIMER.c +++ b/net/netfilter/xt_IDLETIMER.c @@ -48,6 +48,7 @@ #include #include #include +#include struct idletimer_tg_attr { struct attribute attr; @@ -73,6 +74,7 @@ struct idletimer_tg { bool work_pending; bool send_nl_msg; bool active; + uid_t uid; }; static LIST_HEAD(idletimer_tg_list); @@ -117,7 +119,8 @@ static void notify_netlink_uevent(const char *iface, struct idletimer_tg *timer) char iface_msg[NLMSG_MAX_SIZE]; char state_msg[NLMSG_MAX_SIZE]; char timestamp_msg[NLMSG_MAX_SIZE]; - char *envp[] = { iface_msg, state_msg, timestamp_msg, NULL }; + char uid_msg[NLMSG_MAX_SIZE]; + char *envp[] = { iface_msg, state_msg, timestamp_msg, uid_msg, NULL }; int res; struct timespec ts; uint64_t time_ns; @@ -140,6 +143,16 @@ static void notify_netlink_uevent(const char *iface, struct idletimer_tg *timer) return; } + if (state) { + res = snprintf(uid_msg, NLMSG_MAX_SIZE, "UID=%u", timer->uid); + if (NLMSG_MAX_SIZE <= res) + pr_err("message too long (%d)", res); + } else { + res = snprintf(uid_msg, NLMSG_MAX_SIZE, "UID="); + if (NLMSG_MAX_SIZE <= res) + pr_err("message too long (%d)", res); + } + time_ns = timespec_to_ns(&ts); res = snprintf(timestamp_msg, NLMSG_MAX_SIZE, "TIME_NS=%llu", time_ns); if (NLMSG_MAX_SIZE <= res) { @@ -147,7 +160,8 @@ static void notify_netlink_uevent(const char *iface, struct idletimer_tg *timer) pr_err("message too long (%d)", res); } - pr_debug("putting nlmsg: <%s> <%s>\n", iface_msg, state_msg); + pr_debug("putting nlmsg: <%s> <%s> <%s> <%s>\n", iface_msg, state_msg, + timestamp_msg, uid_msg); kobject_uevent_env(idletimer_tg_kobj, KOBJ_CHANGE, envp); return; @@ -298,6 +312,7 @@ static int idletimer_tg_create(struct idletimer_tg_info *info) info->timer->delayed_timer_trigger.tv_sec = 0; info->timer->delayed_timer_trigger.tv_nsec = 0; info->timer->work_pending = false; + info->timer->uid = 0; get_monotonic_boottime(&info->timer->last_modified_timer); info->timer->pm_nb.notifier_call = idletimer_resume; @@ -321,7 +336,8 @@ static int idletimer_tg_create(struct idletimer_tg_info *info) return ret; } -static void reset_timer(const struct idletimer_tg_info *info) +static void reset_timer(const struct idletimer_tg_info *info, + struct sk_buff *skb) { unsigned long now = jiffies; struct idletimer_tg *timer = info->timer; @@ -334,6 +350,13 @@ static void reset_timer(const struct idletimer_tg_info *info) if (!timer_prev || time_before(timer->timer.expires, now)) { pr_debug("Starting Checkentry timer (Expired, Jiffies): %lu, %lu\n", timer->timer.expires, now); + + /* Stores the uid resposible for waking up the radio */ + if (skb && (skb->sk)) { + timer->uid = from_kuid_munged(current_user_ns(), + sock_i_uid(skb->sk)); + } + /* checks if there is a pending inactive notification*/ if (timer->work_pending) timer->delayed_timer_trigger = timer->last_modified_timer; @@ -372,7 +395,7 @@ static unsigned int idletimer_tg_target(struct sk_buff *skb, } /* TODO: Avoid modifying timers on each packet */ - reset_timer(info); + reset_timer(info, skb); return XT_CONTINUE; } @@ -403,7 +426,7 @@ static int idletimer_tg_checkentry(const struct xt_tgchk_param *par) info->timer = __idletimer_tg_find_by_label(info->label); if (info->timer) { info->timer->refcnt++; - reset_timer(info); + reset_timer(info, NULL); pr_debug("increased refcnt of timer %s to %u\n", info->label, info->timer->refcnt); } else { From bec0d4429666bb9f47386544d706aa2679ece04b Mon Sep 17 00:00:00 2001 From: Subash Abhinov Kasiviswanathan Date: Thu, 10 Nov 2016 19:36:15 -0700 Subject: [PATCH 0213/1103] ANDROID: netfilter: xt_IDLETIMER: Fix use after free condition during work schedule_work(&timer->work) appears to be called after cancel_work_sync(&info->timer->work) is completed. Work can be scheduled from the PM_POST_SUSPEND notification event even after cancel_work_sync is called. Call stack -004|notify_netlink_uevent( | [X19] timer = 0xFFFFFFC0A5DFC780 -> ( | ... | [NSD:0xFFFFFFC0A5DFC800] kobj = 0x6B6B6B6B6B6B6B6B, | [NSD:0xFFFFFFC0A5DFC868] timeout = 0x6B6B6B6B, | [NSD:0xFFFFFFC0A5DFC86C] refcnt = 0x6B6B6B6B, | [NSD:0xFFFFFFC0A5DFC870] work_pending = 0x6B, | [NSD:0xFFFFFFC0A5DFC871] send_nl_msg = 0x6B, | [NSD:0xFFFFFFC0A5DFC872] active = 0x6B, | [NSD:0xFFFFFFC0A5DFC874] uid = 0x6B6B6B6B, | [NSD:0xFFFFFFC0A5DFC878] suspend_time_valid = 0x6B)) -005|idletimer_tg_work( -006|__read_once_size(inline) -006|static_key_count(inline) -006|static_key_false(inline) -006|trace_workqueue_execute_end(inline) -006|process_one_work( -007|worker_thread( -008|kthread( -009|ret_from_fork(asm) ---|end of frame Force any pending idletimer_tg_work() to complete before freeing the associated work struct and after unregistering to the pm_notifier callback. Change-Id: I4c5f0a1c142f7d698c092cf7bcafdb0f9fbaa9c1 Signed-off-by: Subash Abhinov Kasiviswanathan --- net/netfilter/xt_IDLETIMER.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/netfilter/xt_IDLETIMER.c b/net/netfilter/xt_IDLETIMER.c index b8ea93603b83..a385487811ad 100644 --- a/net/netfilter/xt_IDLETIMER.c +++ b/net/netfilter/xt_IDLETIMER.c @@ -456,9 +456,9 @@ static void idletimer_tg_destroy(const struct xt_tgdtor_param *par) list_del(&info->timer->entry); del_timer_sync(&info->timer->timer); - cancel_work_sync(&info->timer->work); sysfs_remove_file(idletimer_tg_kobj, &info->timer->attr.attr); unregister_pm_notifier(&info->timer->pm_nb); + cancel_work_sync(&info->timer->work); kfree(info->timer->attr.attr.name); kfree(info->timer); } else { From 312ef0d8e759bbfb9b5aa7155f688221dee71389 Mon Sep 17 00:00:00 2001 From: Subash Abhinov Kasiviswanathan Date: Wed, 2 Nov 2016 11:56:40 -0600 Subject: [PATCH 0214/1103] ANDROID: netfilter: xt_IDLETIMER: Use fullsock when querying uid sock_i_uid() acquires the sk_callback_lock which does not exist for sockets in TCP_NEW_SYN_RECV state. This results in errors showing up as spinlock bad magic. Fix this by looking for the full sock as suggested by Eric. Callstack for reference - -003|rwlock_bug -004|arch_read_lock -004|do_raw_read_lock -005|raw_read_lock_bh -006|sock_i_uid -007|from_kuid_munged(inline) -007|reset_timer -008|idletimer_tg_target -009|ipt_do_table -010|iptable_mangle_hook -011|nf_iterate -012|nf_hook_slow -013|NF_HOOK_COND(inline) -013|ip_output -014|ip_local_out -015|ip_build_and_send_pkt -016|tcp_v4_send_synack -017|atomic_sub_return(inline) -017|reqsk_put(inline) -017|tcp_conn_request -018|tcp_v4_conn_request -019|tcp_rcv_state_process -020|tcp_v4_do_rcv -021|tcp_v4_rcv -022|ip_local_deliver_finish -023|NF_HOOK_THRESH(inline) -023|NF_HOOK(inline) -023|ip_local_deliver -024|ip_rcv_finish -025|NF_HOOK_THRESH(inline) -025|NF_HOOK(inline) -025|ip_rcv -026|deliver_skb(inline) -026|deliver_ptype_list_skb(inline) -026|__netif_receive_skb_core -027|__netif_receive_skb -028|netif_receive_skb_internal -029|netif_receive_skb Change-Id: Ic8f3a3d2d7af31434d1163b03971994e2125d552 Signed-off-by: Subash Abhinov Kasiviswanathan Cc: Eric Dumazet --- net/netfilter/xt_IDLETIMER.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/net/netfilter/xt_IDLETIMER.c b/net/netfilter/xt_IDLETIMER.c index a385487811ad..673860af4934 100644 --- a/net/netfilter/xt_IDLETIMER.c +++ b/net/netfilter/xt_IDLETIMER.c @@ -49,6 +49,7 @@ #include #include #include +#include struct idletimer_tg_attr { struct attribute attr; @@ -354,7 +355,7 @@ static void reset_timer(const struct idletimer_tg_info *info, /* Stores the uid resposible for waking up the radio */ if (skb && (skb->sk)) { timer->uid = from_kuid_munged(current_user_ns(), - sock_i_uid(skb->sk)); + sock_i_uid(skb_to_full_sk(skb))); } /* checks if there is a pending inactive notification*/ From 597dd8df8ba7db8069e1d01a952bbc57e47e4de7 Mon Sep 17 00:00:00 2001 From: Suren Baghdasaryan Date: Thu, 17 Aug 2017 10:43:14 -0700 Subject: [PATCH 0215/1103] ANDROID: NFC: st21nfca: Fix out of bounds kernel access when handling ATR_REQ Out of bounds kernel accesses in st21nfca's NFC HCI layer might happen when handling ATR_REQ events if user-specified atr_req->length is bigger than the buffer size. In that case memcpy() inside st21nfca_tm_send_atr_res() will read extra bytes resulting in OOB read from the kernel heap. Bug: 62679012 Signed-off-by: Suren Baghdasaryan --- drivers/nfc/st21nfca/dep.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/nfc/st21nfca/dep.c b/drivers/nfc/st21nfca/dep.c index fd08be2917e6..3420c5104c94 100644 --- a/drivers/nfc/st21nfca/dep.c +++ b/drivers/nfc/st21nfca/dep.c @@ -217,7 +217,8 @@ static int st21nfca_tm_recv_atr_req(struct nfc_hci_dev *hdev, atr_req = (struct st21nfca_atr_req *)skb->data; - if (atr_req->length < sizeof(struct st21nfca_atr_req)) { + if (atr_req->length < sizeof(struct st21nfca_atr_req) || + atr_req->length > skb->len) { r = -EPROTO; goto exit; } From a25dc31e0610d093503ce5fa588d2673c86aca05 Mon Sep 17 00:00:00 2001 From: Suren Baghdasaryan Date: Thu, 17 Aug 2017 16:30:47 -0700 Subject: [PATCH 0216/1103] ANDROID: nfc: fdp: Fix possible buffer overflow in WCS4000 NFC driver Possible buffer overflow when reading next_read_size bytes into tmp buffer after next_read_size was extracted from a previous packet. Bug: 62678828 Signed-off-by: Suren Baghdasaryan --- drivers/nfc/fdp/i2c.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/drivers/nfc/fdp/i2c.c b/drivers/nfc/fdp/i2c.c index d8d70dd830b0..a3bc8f0e56ab 100644 --- a/drivers/nfc/fdp/i2c.c +++ b/drivers/nfc/fdp/i2c.c @@ -176,6 +176,16 @@ static int fdp_nci_i2c_read(struct fdp_i2c_phy *phy, struct sk_buff **skb) /* Packet that contains a length */ if (tmp[0] == 0 && tmp[1] == 0) { phy->next_read_size = (tmp[2] << 8) + tmp[3] + 3; + /* + * Ensure next_read_size does not exceed sizeof(tmp) + * for reading that many bytes during next iteration + */ + if (phy->next_read_size > FDP_NCI_I2C_MAX_PAYLOAD) { + dev_dbg(&client->dev, "%s: corrupted packet\n", + __func__); + phy->next_read_size = 5; + goto flush; + } } else { phy->next_read_size = FDP_NCI_I2C_MIN_PAYLOAD; From 5b45050ad8670359e1a74ff882537687c7372de7 Mon Sep 17 00:00:00 2001 From: Suren Baghdasaryan Date: Thu, 17 Aug 2017 13:58:40 -0700 Subject: [PATCH 0217/1103] ANDROID: NFC: st21nfca: Fix memory OOB and leak issues in connectivity events handler Overflow on memcpy is possible in kernel driver for st21nfca's NFC HCI layer when handling connectivity events if aid_len or params_len are bigger than the buffer size. Memory leak is possible when parameter tag is invalid. Bug: 62679581 Signed-off-by: Suren Baghdasaryan --- drivers/nfc/st21nfca/se.c | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/drivers/nfc/st21nfca/se.c b/drivers/nfc/st21nfca/se.c index 4bed9e842db3..acdce231e227 100644 --- a/drivers/nfc/st21nfca/se.c +++ b/drivers/nfc/st21nfca/se.c @@ -322,23 +322,33 @@ int st21nfca_connectivity_event_received(struct nfc_hci_dev *hdev, u8 host, * AID 81 5 to 16 * PARAMETERS 82 0 to 255 */ - if (skb->len < NFC_MIN_AID_LENGTH + 2 && + if (skb->len < NFC_MIN_AID_LENGTH + 2 || skb->data[0] != NFC_EVT_TRANSACTION_AID_TAG) return -EPROTO; + /* + * Buffer should have enough space for at least + * two tag fields + two length fields + aid_len (skb->data[1]) + */ + if (skb->len < skb->data[1] + 4) + return -EPROTO; + transaction = (struct nfc_evt_transaction *)devm_kzalloc(dev, skb->len - 2, GFP_KERNEL); transaction->aid_len = skb->data[1]; memcpy(transaction->aid, &skb->data[2], transaction->aid_len); + transaction->params_len = skb->data[transaction->aid_len + 3]; - /* Check next byte is PARAMETERS tag (82) */ + /* Check next byte is PARAMETERS tag (82) and the length field */ if (skb->data[transaction->aid_len + 2] != - NFC_EVT_TRANSACTION_PARAMS_TAG) + NFC_EVT_TRANSACTION_PARAMS_TAG || + skb->len < transaction->aid_len + transaction->params_len + 4) { + devm_kfree(dev, transaction); return -EPROTO; + } - transaction->params_len = skb->data[transaction->aid_len + 3]; memcpy(transaction->params, skb->data + transaction->aid_len + 4, transaction->params_len); From 30c7527063087e558c47b5b2a656725f0256d5ed Mon Sep 17 00:00:00 2001 From: Doug Anderson Date: Thu, 2 Feb 2012 22:58:28 -0800 Subject: [PATCH 0218/1103] ANDROID: of: Support CONFIG_CMDLINE_EXTEND config option The old logic assumes CMDLINE_FROM_BOOTLOADER vs. CMDLINE_FORCE and ignores CMDLINE_EXTEND. Here's the old logic: - CONFIG_CMDLINE_FORCE=true CONFIG_CMDLINE - dt bootargs=non-empty: dt bootargs - dt bootargs=empty, @data is non-empty string @data is left unchanged - dt bootargs=empty, @data is empty string CONFIG_CMDLINE (or "" if that's not defined) The new logic is now documented in of_fdt.h and is copied here for reference: - CONFIG_CMDLINE_FORCE=true CONFIG_CMDLINE - CONFIG_CMDLINE_EXTEND=true, @data is non-empty string @data + dt bootargs (even if dt bootargs are empty) - CONFIG_CMDLINE_EXTEND=true, @data is empty string CONFIG_CMDLINE + dt bootargs (even if dt bootargs are empty) - CMDLINE_FROM_BOOTLOADER=true, dt bootargs=non-empty: dt bootargs - CMDLINE_FROM_BOOTLOADER=true, dt bootargs=empty, @data is non-empty string @data is left unchanged - CMDLINE_FROM_BOOTLOADER=true, dt bootargs=empty, @data is empty string CONFIG_CMDLINE (or "" if that's not defined) Signed-off-by: Doug Anderson CC: devicetree-discuss@lists.ozlabs.org CC: Grant Likely CC: Benjamin Herrenschmidt CC: Rob Herring Change-Id: I40ace250847f813358125dfcaa8998fd32cf7ea3 Signed-off-by: Colin Cross [AmitP: Folded following android-4.9 commit changes into this patch e820270abb5d ("ANDROID: of: fix CONFIG_CMDLINE_EXTEND") 9a4a74055444 ("ANDROID: of: Fix build warnings")] Signed-off-by: Amit Pundir --- drivers/of/fdt.c | 74 ++++++++++++++++++++++++++++-------------- include/linux/of_fdt.h | 21 ++++++++++++ 2 files changed, 70 insertions(+), 25 deletions(-) diff --git a/drivers/of/fdt.c b/drivers/of/fdt.c index 800ad252cf9c..2aa4261d3e8f 100644 --- a/drivers/of/fdt.c +++ b/drivers/of/fdt.c @@ -1072,42 +1072,66 @@ int __init early_init_dt_scan_memory(unsigned long node, const char *uname, return 0; } +/* + * Convert configs to something easy to use in C code + */ +#if defined(CONFIG_CMDLINE_FORCE) +static const int overwrite_incoming_cmdline = 1; +static const int read_dt_cmdline; +static const int concat_cmdline; +#elif defined(CONFIG_CMDLINE_EXTEND) +static const int overwrite_incoming_cmdline; +static const int read_dt_cmdline = 1; +static const int concat_cmdline = 1; +#else /* CMDLINE_FROM_BOOTLOADER */ +static const int overwrite_incoming_cmdline; +static const int read_dt_cmdline = 1; +static const int concat_cmdline; +#endif + +#ifdef CONFIG_CMDLINE +static const char *config_cmdline = CONFIG_CMDLINE; +#else +static const char *config_cmdline = ""; +#endif + int __init early_init_dt_scan_chosen(unsigned long node, const char *uname, int depth, void *data) { - int l; - const char *p; + int l = 0; + const char *p = NULL; + char *cmdline = data; pr_debug("search \"chosen\", depth: %d, uname: %s\n", depth, uname); - if (depth != 1 || !data || + if (depth != 1 || !cmdline || (strcmp(uname, "chosen") != 0 && strcmp(uname, "chosen@0") != 0)) return 0; early_init_dt_check_for_initrd(node); - /* Retrieve command line */ - p = of_get_flat_dt_prop(node, "bootargs", &l); - if (p != NULL && l > 0) - strlcpy(data, p, min((int)l, COMMAND_LINE_SIZE)); - - /* - * CONFIG_CMDLINE is meant to be a default in case nothing else - * managed to set the command line, unless CONFIG_CMDLINE_FORCE - * is set in which case we override whatever was found earlier. - */ -#ifdef CONFIG_CMDLINE -#if defined(CONFIG_CMDLINE_EXTEND) - strlcat(data, " ", COMMAND_LINE_SIZE); - strlcat(data, CONFIG_CMDLINE, COMMAND_LINE_SIZE); -#elif defined(CONFIG_CMDLINE_FORCE) - strlcpy(data, CONFIG_CMDLINE, COMMAND_LINE_SIZE); -#else - /* No arguments from boot loader, use kernel's cmdl*/ - if (!((char *)data)[0]) - strlcpy(data, CONFIG_CMDLINE, COMMAND_LINE_SIZE); -#endif -#endif /* CONFIG_CMDLINE */ + /* Put CONFIG_CMDLINE in if forced or if data had nothing in it to start */ + if (overwrite_incoming_cmdline || !cmdline[0]) + strlcpy(cmdline, config_cmdline, COMMAND_LINE_SIZE); + + /* Retrieve command line unless forcing */ + if (read_dt_cmdline) + p = of_get_flat_dt_prop(node, "bootargs", &l); + + if (p != NULL && l > 0) { + if (concat_cmdline) { + int cmdline_len; + int copy_len; + strlcat(cmdline, " ", COMMAND_LINE_SIZE); + cmdline_len = strlen(cmdline); + copy_len = COMMAND_LINE_SIZE - cmdline_len - 1; + copy_len = min((int)l, copy_len); + strncpy(cmdline + cmdline_len, p, copy_len); + cmdline[cmdline_len + copy_len] = '\0'; + } else { + strlcpy(cmdline, p, min((int)l, COMMAND_LINE_SIZE)); + } + } pr_debug("Command line is: %s\n", (char*)data); diff --git a/include/linux/of_fdt.h b/include/linux/of_fdt.h index b9cd9ebdf9b9..587c147a2436 100644 --- a/include/linux/of_fdt.h +++ b/include/linux/of_fdt.h @@ -69,6 +69,27 @@ extern unsigned long of_get_flat_dt_root(void); extern int of_get_flat_dt_size(void); extern uint32_t of_get_flat_dt_phandle(unsigned long node); +/* + * early_init_dt_scan_chosen - scan the device tree for ramdisk and bootargs + * + * The boot arguments will be placed into the memory pointed to by @data. + * That memory should be COMMAND_LINE_SIZE big and initialized to be a valid + * (possibly empty) string. Logic for what will be in @data after this + * function finishes: + * + * - CONFIG_CMDLINE_FORCE=true + * CONFIG_CMDLINE + * - CONFIG_CMDLINE_EXTEND=true, @data is non-empty string + * @data + dt bootargs (even if dt bootargs are empty) + * - CONFIG_CMDLINE_EXTEND=true, @data is empty string + * CONFIG_CMDLINE + dt bootargs (even if dt bootargs are empty) + * - CMDLINE_FROM_BOOTLOADER=true, dt bootargs=non-empty: + * dt bootargs + * - CMDLINE_FROM_BOOTLOADER=true, dt bootargs=empty, @data is non-empty string + * @data is left unchanged + * - CMDLINE_FROM_BOOTLOADER=true, dt bootargs=empty, @data is empty string + * CONFIG_CMDLINE (or "" if that's not defined) + */ extern int early_init_dt_scan_chosen(unsigned long node, const char *uname, int depth, void *data); extern int early_init_dt_scan_memory(unsigned long node, const char *uname, From b2f2b3053a01fc218c0c7bc941acd4e565a3aeee Mon Sep 17 00:00:00 2001 From: Colin Cross Date: Wed, 2 Apr 2014 18:02:15 -0700 Subject: [PATCH 0219/1103] ANDROID: arm64: copy CONFIG_CMDLINE_EXTEND from ARM Copy the config choice for CONFIG_CMDLINE_EXTEND from arch/arm/Kconfig, including CONFIG_CMDLINE_FROM_BOOTLOADER as the default. These will be used by drivers/of/fdt.c. Change-Id: I8416038498ddf8fc1e99ab06109825eb1492aa7f Signed-off-by: Colin Cross --- arch/arm64/Kconfig | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig index 15d659d62db1..da5bc2c9392e 100644 --- a/arch/arm64/Kconfig +++ b/arch/arm64/Kconfig @@ -1239,6 +1239,23 @@ config CMDLINE entering them here. As a minimum, you should specify the the root device (e.g. root=/dev/nfs). +choice + prompt "Kernel command line type" if CMDLINE != "" + default CMDLINE_FROM_BOOTLOADER + +config CMDLINE_FROM_BOOTLOADER + bool "Use bootloader kernel arguments if available" + help + Uses the command-line options passed by the boot loader. If + the boot loader doesn't provide any, the default kernel command + string provided in CMDLINE will be used. + +config CMDLINE_EXTEND + bool "Extend bootloader kernel arguments" + help + The command-line arguments provided by the boot loader will be + appended to the default kernel command string. + config CMDLINE_FORCE bool "Always use the default kernel command string" help @@ -1246,6 +1263,7 @@ config CMDLINE_FORCE loader passes other arguments to the kernel. This is useful if you cannot or don't want to change the command-line options your boot loader passes to the kernel. +endchoice config EFI_STUB bool From 17520b3ae2c13d4a40f1dfd47e48d4de388216fa Mon Sep 17 00:00:00 2001 From: Todd Poynor Date: Tue, 3 Jul 2012 15:41:20 -0700 Subject: [PATCH 0220/1103] ANDROID: power: power_supply: Add custom property for USB High Current mode For smb347. Change-Id: I3323469072e1ee5085d61af8a89612b06b91f94a Signed-off-by: Todd Poynor [AmitP: Folded following android-4.9 commit changes into this patch af4f6ce7f57b ("ANDROID: power: power_supply: move POWER_SUPPLY_PROP_USB_HC to type 'int' order")] Signed-off-by: Amit Pundir --- drivers/power/supply/power_supply_sysfs.c | 2 ++ include/linux/power_supply.h | 2 ++ 2 files changed, 4 insertions(+) diff --git a/drivers/power/supply/power_supply_sysfs.c b/drivers/power/supply/power_supply_sysfs.c index 6170ed8b6854..ba9083d9822a 100644 --- a/drivers/power/supply/power_supply_sysfs.c +++ b/drivers/power/supply/power_supply_sysfs.c @@ -302,6 +302,8 @@ static struct device_attribute power_supply_attrs[] = { POWER_SUPPLY_ATTR(precharge_current), POWER_SUPPLY_ATTR(charge_term_current), POWER_SUPPLY_ATTR(calibrate), + /* Local extensions */ + POWER_SUPPLY_ATTR(usb_hc), /* Properties of type `const char *' */ POWER_SUPPLY_ATTR(model_name), POWER_SUPPLY_ATTR(manufacturer), diff --git a/include/linux/power_supply.h b/include/linux/power_supply.h index f80769175c56..285ee75b810e 100644 --- a/include/linux/power_supply.h +++ b/include/linux/power_supply.h @@ -150,6 +150,8 @@ enum power_supply_property { POWER_SUPPLY_PROP_PRECHARGE_CURRENT, POWER_SUPPLY_PROP_CHARGE_TERM_CURRENT, POWER_SUPPLY_PROP_CALIBRATE, + /* Local extensions */ + POWER_SUPPLY_PROP_USB_HC, /* Properties of type `const char *' */ POWER_SUPPLY_PROP_MODEL_NAME, POWER_SUPPLY_PROP_MANUFACTURER, From e82d83b92bac16cd6501f898c37d4801c22ff8a5 Mon Sep 17 00:00:00 2001 From: Todd Poynor Date: Fri, 13 Jul 2012 13:30:04 -0700 Subject: [PATCH 0221/1103] ANDROID: power: power_supply: add POWER_SUPPLY_PROP_USB_OTG Change-Id: Idfc6ef2e37d62aad6f26cc8eafa53db642cd352b Signed-off-by: Todd Poynor --- drivers/power/supply/power_supply_sysfs.c | 1 + include/linux/power_supply.h | 1 + 2 files changed, 2 insertions(+) diff --git a/drivers/power/supply/power_supply_sysfs.c b/drivers/power/supply/power_supply_sysfs.c index ba9083d9822a..58ad46c04c2c 100644 --- a/drivers/power/supply/power_supply_sysfs.c +++ b/drivers/power/supply/power_supply_sysfs.c @@ -304,6 +304,7 @@ static struct device_attribute power_supply_attrs[] = { POWER_SUPPLY_ATTR(calibrate), /* Local extensions */ POWER_SUPPLY_ATTR(usb_hc), + POWER_SUPPLY_ATTR(usb_otg), /* Properties of type `const char *' */ POWER_SUPPLY_ATTR(model_name), POWER_SUPPLY_ATTR(manufacturer), diff --git a/include/linux/power_supply.h b/include/linux/power_supply.h index 285ee75b810e..714c245c0346 100644 --- a/include/linux/power_supply.h +++ b/include/linux/power_supply.h @@ -152,6 +152,7 @@ enum power_supply_property { POWER_SUPPLY_PROP_CALIBRATE, /* Local extensions */ POWER_SUPPLY_PROP_USB_HC, + POWER_SUPPLY_PROP_USB_OTG, /* Properties of type `const char *' */ POWER_SUPPLY_PROP_MODEL_NAME, POWER_SUPPLY_PROP_MANUFACTURER, From ba4d48bd8c8bbbcc3d3baaae05ff885903a44e05 Mon Sep 17 00:00:00 2001 From: Todd Poynor Date: Wed, 18 Jul 2012 16:28:50 -0700 Subject: [PATCH 0222/1103] ANDROID: power: power_supply: add POWER_SUPPLY_PROP_CHARGE_ENABLED Change-Id: I3e93b502452811cbfc4d904202b4f1d94edc143d Signed-off-by: Todd Poynor --- drivers/power/supply/power_supply_sysfs.c | 1 + include/linux/power_supply.h | 1 + 2 files changed, 2 insertions(+) diff --git a/drivers/power/supply/power_supply_sysfs.c b/drivers/power/supply/power_supply_sysfs.c index 58ad46c04c2c..3bd7c71c247e 100644 --- a/drivers/power/supply/power_supply_sysfs.c +++ b/drivers/power/supply/power_supply_sysfs.c @@ -305,6 +305,7 @@ static struct device_attribute power_supply_attrs[] = { /* Local extensions */ POWER_SUPPLY_ATTR(usb_hc), POWER_SUPPLY_ATTR(usb_otg), + POWER_SUPPLY_ATTR(charge_enabled), /* Properties of type `const char *' */ POWER_SUPPLY_ATTR(model_name), POWER_SUPPLY_ATTR(manufacturer), diff --git a/include/linux/power_supply.h b/include/linux/power_supply.h index 714c245c0346..3ac0232ce39c 100644 --- a/include/linux/power_supply.h +++ b/include/linux/power_supply.h @@ -153,6 +153,7 @@ enum power_supply_property { /* Local extensions */ POWER_SUPPLY_PROP_USB_HC, POWER_SUPPLY_PROP_USB_OTG, + POWER_SUPPLY_PROP_CHARGE_ENABLED, /* Properties of type `const char *' */ POWER_SUPPLY_PROP_MODEL_NAME, POWER_SUPPLY_PROP_MANUFACTURER, From e662907d30105559beea3d5bcfbbb16adcf7a7a1 Mon Sep 17 00:00:00 2001 From: Todd Poynor Date: Thu, 12 Dec 2013 15:59:09 -0800 Subject: [PATCH 0223/1103] ANDROID: power: power_supply: Add property CHARGE_COUNTER_EXT and 64-bit precision properties Add POWER_SUPPLY_PROP_CHARGE_COUNTER_EXT that stores accumulated charge in nAh units as a signed 64-bit value. Add generic support for signed 64-bit property values. Change-Id: I2bd34b1e95ffba24e7bfef81f398f22bd2aaf05e Signed-off-by: Todd Poynor --- drivers/power/supply/power_supply_sysfs.c | 5 +++++ include/linux/power_supply.h | 4 ++++ 2 files changed, 9 insertions(+) diff --git a/drivers/power/supply/power_supply_sysfs.c b/drivers/power/supply/power_supply_sysfs.c index 3bd7c71c247e..4e18ba98987e 100644 --- a/drivers/power/supply/power_supply_sysfs.c +++ b/drivers/power/supply/power_supply_sysfs.c @@ -171,6 +171,9 @@ static ssize_t power_supply_show_property(struct device *dev, ret = sprintf(buf, "%s\n", power_supply_scope_text[value.intval]); break; + case POWER_SUPPLY_PROP_CHARGE_COUNTER_EXT: + ret = sprintf(buf, "%lld\n", value.int64val); + break; case POWER_SUPPLY_PROP_MODEL_NAME ... POWER_SUPPLY_PROP_SERIAL_NUMBER: ret = sprintf(buf, "%s\n", value.strval); break; @@ -306,6 +309,8 @@ static struct device_attribute power_supply_attrs[] = { POWER_SUPPLY_ATTR(usb_hc), POWER_SUPPLY_ATTR(usb_otg), POWER_SUPPLY_ATTR(charge_enabled), + /* Local extensions of type int64_t */ + POWER_SUPPLY_ATTR(charge_counter_ext), /* Properties of type `const char *' */ POWER_SUPPLY_ATTR(model_name), POWER_SUPPLY_ATTR(manufacturer), diff --git a/include/linux/power_supply.h b/include/linux/power_supply.h index 3ac0232ce39c..5025cba766c7 100644 --- a/include/linux/power_supply.h +++ b/include/linux/power_supply.h @@ -18,6 +18,7 @@ #include #include #include +#include /* * All voltages, currents, charges, energies, time and temperatures in uV, @@ -154,6 +155,8 @@ enum power_supply_property { POWER_SUPPLY_PROP_USB_HC, POWER_SUPPLY_PROP_USB_OTG, POWER_SUPPLY_PROP_CHARGE_ENABLED, + /* Local extensions of type int64_t */ + POWER_SUPPLY_PROP_CHARGE_COUNTER_EXT, /* Properties of type `const char *' */ POWER_SUPPLY_PROP_MODEL_NAME, POWER_SUPPLY_PROP_MANUFACTURER, @@ -195,6 +198,7 @@ enum power_supply_notifier_events { union power_supply_propval { int intval; const char *strval; + int64_t int64val; }; struct device_node; From ee7c68681fa0cccbf8d2ede24a3cc72f8cd0c9b0 Mon Sep 17 00:00:00 2001 From: Ruchi Kandoi Date: Wed, 19 Feb 2014 15:30:47 -0800 Subject: [PATCH 0224/1103] ANDROID: power: wakeup_reason: add an API to log wakeup reasons Add API log_wakeup_reason() and expose it to userspace via sysfs path /sys/kernel/wakeup_reasons/last_resume_reason Change-Id: I81addaf420f1338255c5d0638b0d244a99d777d1 Signed-off-by: Ruchi Kandoi [AmitP: Folded following android-4.9 commit changes into this patch 1135122a192a ("ANDROID: POWER: fix compile warnings in log_wakeup_reason") b4e6247778b0 ("ANDROID: Power: Changes the permission to read only for sysfs file /sys/kernel/wakeup_reasons/last_resume_reason") e13dbc7c69cd ("ANDROID: power: wakeup_reason: rename irq_count to irqcount")] Signed-off-by: Amit Pundir --- include/linux/wakeup_reason.h | 23 ++++++ kernel/power/Makefile | 2 + kernel/power/wakeup_reason.c | 132 ++++++++++++++++++++++++++++++++++ 3 files changed, 157 insertions(+) create mode 100644 include/linux/wakeup_reason.h create mode 100644 kernel/power/wakeup_reason.c diff --git a/include/linux/wakeup_reason.h b/include/linux/wakeup_reason.h new file mode 100644 index 000000000000..7ce50f0debc4 --- /dev/null +++ b/include/linux/wakeup_reason.h @@ -0,0 +1,23 @@ +/* + * include/linux/wakeup_reason.h + * + * Logs the reason which caused the kernel to resume + * from the suspend mode. + * + * Copyright (C) 2014 Google, Inc. + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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. + */ + +#ifndef _LINUX_WAKEUP_REASON_H +#define _LINUX_WAKEUP_REASON_H + +void log_wakeup_reason(int irq); + +#endif /* _LINUX_WAKEUP_REASON_H */ diff --git a/kernel/power/Makefile b/kernel/power/Makefile index a3f79f0eef36..5c1743d4d8ef 100644 --- a/kernel/power/Makefile +++ b/kernel/power/Makefile @@ -15,3 +15,5 @@ obj-$(CONFIG_PM_AUTOSLEEP) += autosleep.o obj-$(CONFIG_PM_WAKELOCKS) += wakelock.o obj-$(CONFIG_MAGIC_SYSRQ) += poweroff.o + +obj-$(CONFIG_SUSPEND) += wakeup_reason.o diff --git a/kernel/power/wakeup_reason.c b/kernel/power/wakeup_reason.c new file mode 100644 index 000000000000..1491bc402b05 --- /dev/null +++ b/kernel/power/wakeup_reason.c @@ -0,0 +1,132 @@ +/* + * kernel/power/wakeup_reason.c + * + * Logs the reasons which caused the kernel to resume from + * the suspend mode. + * + * Copyright (C) 2014 Google, Inc. + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +#define MAX_WAKEUP_REASON_IRQS 32 +static int irq_list[MAX_WAKEUP_REASON_IRQS]; +static int irqcount; +static struct kobject *wakeup_reason; +static spinlock_t resume_reason_lock; + +static ssize_t last_resume_reason_show(struct kobject *kobj, struct kobj_attribute *attr, + char *buf) +{ + int irq_no, buf_offset = 0; + struct irq_desc *desc; + spin_lock(&resume_reason_lock); + for (irq_no = 0; irq_no < irqcount; irq_no++) { + desc = irq_to_desc(irq_list[irq_no]); + if (desc && desc->action && desc->action->name) + buf_offset += sprintf(buf + buf_offset, "%d %s\n", + irq_list[irq_no], desc->action->name); + else + buf_offset += sprintf(buf + buf_offset, "%d\n", + irq_list[irq_no]); + } + spin_unlock(&resume_reason_lock); + return buf_offset; +} + +static struct kobj_attribute resume_reason = __ATTR_RO(last_resume_reason); + +static struct attribute *attrs[] = { + &resume_reason.attr, + NULL, +}; +static struct attribute_group attr_group = { + .attrs = attrs, +}; + +/* + * logs all the wake up reasons to the kernel + * stores the irqs to expose them to the userspace via sysfs + */ +void log_wakeup_reason(int irq) +{ + struct irq_desc *desc; + desc = irq_to_desc(irq); + if (desc && desc->action && desc->action->name) + printk(KERN_INFO "Resume caused by IRQ %d, %s\n", irq, + desc->action->name); + else + printk(KERN_INFO "Resume caused by IRQ %d\n", irq); + + spin_lock(&resume_reason_lock); + irq_list[irqcount++] = irq; + spin_unlock(&resume_reason_lock); +} + +/* Detects a suspend and clears all the previous wake up reasons*/ +static int wakeup_reason_pm_event(struct notifier_block *notifier, + unsigned long pm_event, void *unused) +{ + switch (pm_event) { + case PM_SUSPEND_PREPARE: + spin_lock(&resume_reason_lock); + irqcount = 0; + spin_unlock(&resume_reason_lock); + break; + default: + break; + } + return NOTIFY_DONE; +} + +static struct notifier_block wakeup_reason_pm_notifier_block = { + .notifier_call = wakeup_reason_pm_event, +}; + +/* Initializes the sysfs parameter + * registers the pm_event notifier + */ +int __init wakeup_reason_init(void) +{ + int retval; + spin_lock_init(&resume_reason_lock); + retval = register_pm_notifier(&wakeup_reason_pm_notifier_block); + if (retval) + printk(KERN_WARNING "[%s] failed to register PM notifier %d\n", + __func__, retval); + + wakeup_reason = kobject_create_and_add("wakeup_reasons", kernel_kobj); + if (!wakeup_reason) { + printk(KERN_WARNING "[%s] failed to create a sysfs kobject\n", + __func__); + return 1; + } + retval = sysfs_create_group(wakeup_reason, &attr_group); + if (retval) { + kobject_put(wakeup_reason); + printk(KERN_WARNING "[%s] failed to create a sysfs group %d\n", + __func__, retval); + } + return 0; +} + +late_initcall(wakeup_reason_init); From 631a780d59b0e884ea6ecf2a69df73135a447f26 Mon Sep 17 00:00:00 2001 From: Ruchi Kandoi Date: Fri, 7 Mar 2014 12:54:30 -0800 Subject: [PATCH 0225/1103] ANDROID: power: wakeup_reason: Add guard condition for maximum wakeup reasons Ensure the array for the wakeup reason IRQs does not overflow. Change-Id: Iddc57a3aeb1888f39d4e7b004164611803a4d37c Signed-off-by: Ruchi Kandoi (cherry picked from commit b5ea40cdfcf38296535f931a7e5e7bf47b6fad7f) --- kernel/power/wakeup_reason.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/kernel/power/wakeup_reason.c b/kernel/power/wakeup_reason.c index 1491bc402b05..187e4e9105fb 100644 --- a/kernel/power/wakeup_reason.c +++ b/kernel/power/wakeup_reason.c @@ -78,6 +78,13 @@ void log_wakeup_reason(int irq) printk(KERN_INFO "Resume caused by IRQ %d\n", irq); spin_lock(&resume_reason_lock); + if (irqcount == MAX_WAKEUP_REASON_IRQS) { + spin_unlock(&resume_reason_lock); + printk(KERN_WARNING "Resume caused by more than %d IRQs\n", + MAX_WAKEUP_REASON_IRQS); + return; + } + irq_list[irqcount++] = irq; spin_unlock(&resume_reason_lock); } From e8481813e794589828a8c375c621cb78812fc9e9 Mon Sep 17 00:00:00 2001 From: Ruchi Kandoi Date: Tue, 14 Oct 2014 17:43:21 -0700 Subject: [PATCH 0226/1103] ANDROID: power: wakeup_reason: Avoids bogus error messages for the suspend aborts. Avoids printing bogus error message "tasks refusing to freeze", in cases where pending wakeup source caused the suspend abort. Signed-off-by: Ruchi Kandoi Change-Id: I913ad290f501b31cd536d039834c8d24c6f16928 --- kernel/power/process.c | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/kernel/power/process.c b/kernel/power/process.c index 7381d49a44db..d76e61606f51 100644 --- a/kernel/power/process.c +++ b/kernel/power/process.c @@ -85,26 +85,27 @@ static int try_to_freeze_tasks(bool user_only) elapsed = ktime_sub(end, start); elapsed_msecs = ktime_to_ms(elapsed); - if (todo) { + if (wakeup) { pr_cont("\n"); - pr_err("Freezing of tasks %s after %d.%03d seconds " - "(%d tasks refusing to freeze, wq_busy=%d):\n", - wakeup ? "aborted" : "failed", + pr_err("Freezing of tasks aborted after %d.%03d seconds", + elapsed_msecs / 1000, elapsed_msecs % 1000); + } else if (todo) { + pr_cont("\n"); + pr_err("Freezing of tasks failed after %d.%03d seconds" + " (%d tasks refusing to freeze, wq_busy=%d):\n", elapsed_msecs / 1000, elapsed_msecs % 1000, todo - wq_busy, wq_busy); if (wq_busy) show_workqueue_state(); - if (!wakeup) { - read_lock(&tasklist_lock); - for_each_process_thread(g, p) { - if (p != current && !freezer_should_skip(p) - && freezing(p) && !frozen(p)) - sched_show_task(p); - } - read_unlock(&tasklist_lock); + read_lock(&tasklist_lock); + for_each_process_thread(g, p) { + if (p != current && !freezer_should_skip(p) + && freezing(p) && !frozen(p)) + sched_show_task(p); } + read_unlock(&tasklist_lock); } else { pr_cont("(elapsed %d.%03d seconds) ", elapsed_msecs / 1000, elapsed_msecs % 1000); From ce2651ca75027b61b8a2fb1fa76965f845c94850 Mon Sep 17 00:00:00 2001 From: Ruchi Kandoi Date: Wed, 29 Oct 2014 10:36:27 -0700 Subject: [PATCH 0227/1103] ANDROID: power: wakeup_reason: Adds functionality to log the last suspend abort reason. Extends the last_resume_reason to log suspend abort reason. The abort reasons will have "Abort:" appended at the start to distinguish itself from the resume reason. Signed-off-by: Ruchi Kandoi Change-Id: I3207f1844e3d87c706dfc298fb10e1c648814c5f [AmitP: Folded following android-4.9 changes into this patch 00a83e61b4fc ("ANDROID: Make suspend abort reason logging depend on CONFIG_PM_SLEEP") 9d17e24b036e ("ANDROID: wakeup_reason: use vsnprintf instead of snsprintf for vargs.") 7961972600ba ("ANDROID: power: Provide dummy log_suspend_abort_reason() if SUSPEND is disabled")] Signed-off-by: Amit Pundir --- drivers/base/power/main.c | 5 +++++ drivers/base/power/wakeup.c | 16 +++++++++++++ drivers/base/syscore.c | 3 +++ include/linux/suspend.h | 1 + include/linux/wakeup_reason.h | 7 ++++++ kernel/power/process.c | 9 ++++++++ kernel/power/suspend.c | 20 +++++++++++++++-- kernel/power/wakeup_reason.c | 42 ++++++++++++++++++++++++++++------- 8 files changed, 93 insertions(+), 10 deletions(-) diff --git a/drivers/base/power/main.c b/drivers/base/power/main.c index a690fd400260..ee0d68f61cfb 100644 --- a/drivers/base/power/main.c +++ b/drivers/base/power/main.c @@ -33,6 +33,7 @@ #include #include #include +#include #include "../base.h" #include "power.h" @@ -1706,6 +1707,7 @@ static int __device_suspend(struct device *dev, pm_message_t state, bool async) pm_callback_t callback = NULL; const char *info = NULL; int error = 0; + char suspend_abort[MAX_SUSPEND_ABORT_LEN]; DECLARE_DPM_WATCHDOG_ON_STACK(wd); TRACE_DEVICE(dev); @@ -1729,6 +1731,9 @@ static int __device_suspend(struct device *dev, pm_message_t state, bool async) if (pm_wakeup_pending()) { dev->power.direct_complete = false; + pm_get_active_wakeup_sources(suspend_abort, + MAX_SUSPEND_ABORT_LEN); + log_suspend_abort_reason(suspend_abort); async_error = -EBUSY; goto Complete; } diff --git a/drivers/base/power/wakeup.c b/drivers/base/power/wakeup.c index 5fa1898755a3..a4ebcf37c612 100644 --- a/drivers/base/power/wakeup.c +++ b/drivers/base/power/wakeup.c @@ -803,6 +803,22 @@ void pm_wakeup_dev_event(struct device *dev, unsigned int msec, bool hard) } EXPORT_SYMBOL_GPL(pm_wakeup_dev_event); +void pm_get_active_wakeup_sources(char *pending_wakeup_source, size_t max) +{ + struct wakeup_source *ws; + int len = 0; + rcu_read_lock(); + len += snprintf(pending_wakeup_source, max, "Pending Wakeup Sources: "); + list_for_each_entry_rcu(ws, &wakeup_sources, entry) { + if (ws->active) { + len += snprintf(pending_wakeup_source + len, max, + "%s ", ws->name); + } + } + rcu_read_unlock(); +} +EXPORT_SYMBOL_GPL(pm_get_active_wakeup_sources); + void pm_print_active_wakeup_sources(void) { struct wakeup_source *ws; diff --git a/drivers/base/syscore.c b/drivers/base/syscore.c index 6e076f359dcc..996573ffa58e 100644 --- a/drivers/base/syscore.c +++ b/drivers/base/syscore.c @@ -10,6 +10,7 @@ #include #include #include +#include static LIST_HEAD(syscore_ops_list); static DEFINE_MUTEX(syscore_ops_lock); @@ -74,6 +75,8 @@ int syscore_suspend(void) return 0; err_out: + log_suspend_abort_reason("System core suspend callback %pF failed", + ops->suspend); pr_err("PM: System core suspend callback %pF failed.\n", ops->suspend); list_for_each_entry_continue(ops, &syscore_ops_list, node) diff --git a/include/linux/suspend.h b/include/linux/suspend.h index 5a28ac9284f0..f60edadf0989 100644 --- a/include/linux/suspend.h +++ b/include/linux/suspend.h @@ -444,6 +444,7 @@ extern bool pm_get_wakeup_count(unsigned int *count, bool block); extern bool pm_save_wakeup_count(unsigned int count); extern void pm_wakep_autosleep_enabled(bool set); extern void pm_print_active_wakeup_sources(void); +extern void pm_get_active_wakeup_sources(char *pending_sources, size_t max); extern void lock_system_sleep(void); extern void unlock_system_sleep(void); diff --git a/include/linux/wakeup_reason.h b/include/linux/wakeup_reason.h index 7ce50f0debc4..9fbe209c7177 100644 --- a/include/linux/wakeup_reason.h +++ b/include/linux/wakeup_reason.h @@ -18,6 +18,13 @@ #ifndef _LINUX_WAKEUP_REASON_H #define _LINUX_WAKEUP_REASON_H +#define MAX_SUSPEND_ABORT_LEN 256 + void log_wakeup_reason(int irq); +#ifdef CONFIG_SUSPEND +void log_suspend_abort_reason(const char *fmt, ...); +#else +static inline void log_suspend_abort_reason(const char *fmt, ...) { } +#endif #endif /* _LINUX_WAKEUP_REASON_H */ diff --git a/kernel/power/process.c b/kernel/power/process.c index d76e61606f51..c366e3d34a07 100644 --- a/kernel/power/process.c +++ b/kernel/power/process.c @@ -22,6 +22,7 @@ #include #include #include +#include /* * Timeout for stopping processes @@ -38,6 +39,9 @@ static int try_to_freeze_tasks(bool user_only) unsigned int elapsed_msecs; bool wakeup = false; int sleep_usecs = USEC_PER_MSEC; +#ifdef CONFIG_PM_SLEEP + char suspend_abort[MAX_SUSPEND_ABORT_LEN]; +#endif start = ktime_get_boottime(); @@ -67,6 +71,11 @@ static int try_to_freeze_tasks(bool user_only) break; if (pm_wakeup_pending()) { +#ifdef CONFIG_PM_SLEEP + pm_get_active_wakeup_sources(suspend_abort, + MAX_SUSPEND_ABORT_LEN); + log_suspend_abort_reason(suspend_abort); +#endif wakeup = true; break; } diff --git a/kernel/power/suspend.c b/kernel/power/suspend.c index 5342f6fc022e..a8885b3d2898 100644 --- a/kernel/power/suspend.c +++ b/kernel/power/suspend.c @@ -32,6 +32,7 @@ #include #include #include +#include #include "power.h" @@ -390,7 +391,8 @@ void __weak arch_suspend_enable_irqs(void) */ static int suspend_enter(suspend_state_t state, bool *wakeup) { - int error; + char suspend_abort[MAX_SUSPEND_ABORT_LEN]; + int error, last_dev; error = platform_suspend_prepare(state); if (error) @@ -398,7 +400,11 @@ static int suspend_enter(suspend_state_t state, bool *wakeup) error = dpm_suspend_late(PMSG_SUSPEND); if (error) { + last_dev = suspend_stats.last_failed_dev + REC_FAILED_NUM - 1; + last_dev %= REC_FAILED_NUM; pr_err("late suspend of devices failed\n"); + log_suspend_abort_reason("%s device failed to power down", + suspend_stats.failed_devs[last_dev]); goto Platform_finish; } error = platform_suspend_prepare_late(state); @@ -412,7 +418,11 @@ static int suspend_enter(suspend_state_t state, bool *wakeup) error = dpm_suspend_noirq(PMSG_SUSPEND); if (error) { + last_dev = suspend_stats.last_failed_dev + REC_FAILED_NUM - 1; + last_dev %= REC_FAILED_NUM; pr_err("noirq suspend of devices failed\n"); + log_suspend_abort_reason("noirq suspend of %s device failed", + suspend_stats.failed_devs[last_dev]); goto Platform_early_resume; } error = platform_suspend_prepare_noirq(state); @@ -423,8 +433,10 @@ static int suspend_enter(suspend_state_t state, bool *wakeup) goto Platform_wake; error = disable_nonboot_cpus(); - if (error || suspend_test(TEST_CPUS)) + if (error || suspend_test(TEST_CPUS)) { + log_suspend_abort_reason("Disabling non-boot cpus failed"); goto Enable_cpus; + } arch_suspend_disable_irqs(); BUG_ON(!irqs_disabled()); @@ -441,6 +453,9 @@ static int suspend_enter(suspend_state_t state, bool *wakeup) trace_suspend_resume(TPS("machine_suspend"), state, false); } else if (*wakeup) { + pm_get_active_wakeup_sources(suspend_abort, + MAX_SUSPEND_ABORT_LEN); + log_suspend_abort_reason(suspend_abort); error = -EBUSY; } syscore_resume(); @@ -492,6 +507,7 @@ int suspend_devices_and_enter(suspend_state_t state) error = dpm_suspend_start(PMSG_SUSPEND); if (error) { pr_err("Some devices failed to suspend, or early wake event detected\n"); + log_suspend_abort_reason("Some devices failed to suspend, or early wake event detected"); goto Recover_platform; } suspend_test_finish("suspend devices"); diff --git a/kernel/power/wakeup_reason.c b/kernel/power/wakeup_reason.c index 187e4e9105fb..5944dfe78191 100644 --- a/kernel/power/wakeup_reason.c +++ b/kernel/power/wakeup_reason.c @@ -31,6 +31,8 @@ #define MAX_WAKEUP_REASON_IRQS 32 static int irq_list[MAX_WAKEUP_REASON_IRQS]; static int irqcount; +static bool suspend_abort; +static char abort_reason[MAX_SUSPEND_ABORT_LEN]; static struct kobject *wakeup_reason; static spinlock_t resume_reason_lock; @@ -40,14 +42,18 @@ static ssize_t last_resume_reason_show(struct kobject *kobj, struct kobj_attribu int irq_no, buf_offset = 0; struct irq_desc *desc; spin_lock(&resume_reason_lock); - for (irq_no = 0; irq_no < irqcount; irq_no++) { - desc = irq_to_desc(irq_list[irq_no]); - if (desc && desc->action && desc->action->name) - buf_offset += sprintf(buf + buf_offset, "%d %s\n", - irq_list[irq_no], desc->action->name); - else - buf_offset += sprintf(buf + buf_offset, "%d\n", - irq_list[irq_no]); + if (suspend_abort) { + buf_offset = sprintf(buf, "Abort: %s", abort_reason); + } else { + for (irq_no = 0; irq_no < irqcount; irq_no++) { + desc = irq_to_desc(irq_list[irq_no]); + if (desc && desc->action && desc->action->name) + buf_offset += sprintf(buf + buf_offset, "%d %s\n", + irq_list[irq_no], desc->action->name); + else + buf_offset += sprintf(buf + buf_offset, "%d\n", + irq_list[irq_no]); + } } spin_unlock(&resume_reason_lock); return buf_offset; @@ -89,6 +95,25 @@ void log_wakeup_reason(int irq) spin_unlock(&resume_reason_lock); } +void log_suspend_abort_reason(const char *fmt, ...) +{ + va_list args; + + spin_lock(&resume_reason_lock); + + //Suspend abort reason has already been logged. + if (suspend_abort) { + spin_unlock(&resume_reason_lock); + return; + } + + suspend_abort = true; + va_start(args, fmt); + vsnprintf(abort_reason, MAX_SUSPEND_ABORT_LEN, fmt, args); + va_end(args); + spin_unlock(&resume_reason_lock); +} + /* Detects a suspend and clears all the previous wake up reasons*/ static int wakeup_reason_pm_event(struct notifier_block *notifier, unsigned long pm_event, void *unused) @@ -97,6 +122,7 @@ static int wakeup_reason_pm_event(struct notifier_block *notifier, case PM_SUSPEND_PREPARE: spin_lock(&resume_reason_lock); irqcount = 0; + suspend_abort = false; spin_unlock(&resume_reason_lock); break; default: From 24dbd5d6fc2b78159f7778edf4d4cc9216481da8 Mon Sep 17 00:00:00 2001 From: jinqian Date: Wed, 25 Mar 2015 16:18:44 -0700 Subject: [PATCH 0228/1103] ANDROID: power: wakeup_reason: Report suspend times from last_suspend_time This node epxorts two values separated by space. From left to right: 1. time spent in suspend/resume process 2. time spent sleep in suspend state Change-Id: I2cb9a9408a5fd12166aaec11b935a0fd6a408c63 --- .../ABI/testing/sysfs-kernel-wakeup_reasons | 16 +++++++++ kernel/power/wakeup_reason.c | 36 +++++++++++++++++++ 2 files changed, 52 insertions(+) create mode 100644 Documentation/ABI/testing/sysfs-kernel-wakeup_reasons diff --git a/Documentation/ABI/testing/sysfs-kernel-wakeup_reasons b/Documentation/ABI/testing/sysfs-kernel-wakeup_reasons new file mode 100644 index 000000000000..acb19b91c192 --- /dev/null +++ b/Documentation/ABI/testing/sysfs-kernel-wakeup_reasons @@ -0,0 +1,16 @@ +What: /sys/kernel/wakeup_reasons/last_resume_reason +Date: February 2014 +Contact: Ruchi Kandoi +Description: + The /sys/kernel/wakeup_reasons/last_resume_reason is + used to report wakeup reasons after system exited suspend. + +What: /sys/kernel/wakeup_reasons/last_suspend_time +Date: March 2015 +Contact: jinqian +Description: + The /sys/kernel/wakeup_reasons/last_suspend_time is + used to report time spent in last suspend cycle. It contains + two numbers (in seconds) separated by space. First number is + the time spent in suspend and resume processes. Second number + is the time spent in sleep state. \ No newline at end of file diff --git a/kernel/power/wakeup_reason.c b/kernel/power/wakeup_reason.c index 5944dfe78191..8480e32b41cb 100644 --- a/kernel/power/wakeup_reason.c +++ b/kernel/power/wakeup_reason.c @@ -36,6 +36,11 @@ static char abort_reason[MAX_SUSPEND_ABORT_LEN]; static struct kobject *wakeup_reason; static spinlock_t resume_reason_lock; +static struct timespec last_xtime; /* wall time before last suspend */ +static struct timespec curr_xtime; /* wall time after last suspend */ +static struct timespec last_stime; /* total_sleep_time before last suspend */ +static struct timespec curr_stime; /* total_sleep_time after last suspend */ + static ssize_t last_resume_reason_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf) { @@ -59,10 +64,32 @@ static ssize_t last_resume_reason_show(struct kobject *kobj, struct kobj_attribu return buf_offset; } +static ssize_t last_suspend_time_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + struct timespec sleep_time; + struct timespec total_time; + struct timespec suspend_resume_time; + + sleep_time = timespec_sub(curr_stime, last_stime); + total_time = timespec_sub(curr_xtime, last_xtime); + suspend_resume_time = timespec_sub(total_time, sleep_time); + + /* + * suspend_resume_time is calculated from sleep_time. Userspace would + * always need both. Export them in pair here. + */ + return sprintf(buf, "%lu.%09lu %lu.%09lu\n", + suspend_resume_time.tv_sec, suspend_resume_time.tv_nsec, + sleep_time.tv_sec, sleep_time.tv_nsec); +} + static struct kobj_attribute resume_reason = __ATTR_RO(last_resume_reason); +static struct kobj_attribute suspend_time = __ATTR_RO(last_suspend_time); static struct attribute *attrs[] = { &resume_reason.attr, + &suspend_time.attr, NULL, }; static struct attribute_group attr_group = { @@ -118,12 +145,21 @@ void log_suspend_abort_reason(const char *fmt, ...) static int wakeup_reason_pm_event(struct notifier_block *notifier, unsigned long pm_event, void *unused) { + struct timespec xtom; /* wall_to_monotonic, ignored */ + switch (pm_event) { case PM_SUSPEND_PREPARE: spin_lock(&resume_reason_lock); irqcount = 0; suspend_abort = false; spin_unlock(&resume_reason_lock); + + get_xtime_and_monotonic_and_sleep_offset(&last_xtime, &xtom, + &last_stime); + break; + case PM_POST_SUSPEND: + get_xtime_and_monotonic_and_sleep_offset(&curr_xtime, &xtom, + &curr_stime); break; default: break; From b18c871c1f726e2679fa3eaff6252c8796c533ff Mon Sep 17 00:00:00 2001 From: Amit Pundir Date: Tue, 14 Apr 2015 02:38:20 +0530 Subject: [PATCH 0229/1103] ANDROID: power: wakeup_reason: fix suspend time reporting Suspend time reporting Change-Id: I2cb9a9408a5fd12166aaec11b935a0fd6a408c63 (Power: Report suspend times from last_suspend_time), is broken on 3.16+ kernels because get_xtime_and_monotonic_and_sleep_offset() hrtimer helper routine is removed from kernel timekeeping. The replacement helper routines ktime_get_update_offsets_{tick,now}() are private to core kernel timekeeping so we can't use them, hence using ktime_get() and ktime_get_boottime() instead and sampling the time twice. Idea is to use Monotonic boottime offset to calculate total time spent in last suspend state and CLOCK_MONOTONIC to calculate time spent in last suspend-resume process. Signed-off-by: Amit Pundir --- kernel/power/wakeup_reason.c | 41 ++++++++++++++++++++++-------------- 1 file changed, 25 insertions(+), 16 deletions(-) diff --git a/kernel/power/wakeup_reason.c b/kernel/power/wakeup_reason.c index 8480e32b41cb..ddbcf15ec667 100644 --- a/kernel/power/wakeup_reason.c +++ b/kernel/power/wakeup_reason.c @@ -36,10 +36,10 @@ static char abort_reason[MAX_SUSPEND_ABORT_LEN]; static struct kobject *wakeup_reason; static spinlock_t resume_reason_lock; -static struct timespec last_xtime; /* wall time before last suspend */ -static struct timespec curr_xtime; /* wall time after last suspend */ -static struct timespec last_stime; /* total_sleep_time before last suspend */ -static struct timespec curr_stime; /* total_sleep_time after last suspend */ +static ktime_t last_monotime; /* monotonic time before last suspend */ +static ktime_t curr_monotime; /* monotonic time after last suspend */ +static ktime_t last_stime; /* monotonic boottime offset before last suspend */ +static ktime_t curr_stime; /* monotonic boottime offset after last suspend */ static ssize_t last_resume_reason_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf) @@ -71,14 +71,22 @@ static ssize_t last_suspend_time_show(struct kobject *kobj, struct timespec total_time; struct timespec suspend_resume_time; - sleep_time = timespec_sub(curr_stime, last_stime); - total_time = timespec_sub(curr_xtime, last_xtime); - suspend_resume_time = timespec_sub(total_time, sleep_time); + /* + * total_time is calculated from monotonic bootoffsets because + * unlike CLOCK_MONOTONIC it include the time spent in suspend state. + */ + total_time = ktime_to_timespec(ktime_sub(curr_stime, last_stime)); /* - * suspend_resume_time is calculated from sleep_time. Userspace would - * always need both. Export them in pair here. + * suspend_resume_time is calculated as monotonic (CLOCK_MONOTONIC) + * time interval before entering suspend and post suspend. */ + suspend_resume_time = ktime_to_timespec(ktime_sub(curr_monotime, last_monotime)); + + /* sleep_time = total_time - suspend_resume_time */ + sleep_time = timespec_sub(total_time, suspend_resume_time); + + /* Export suspend_resume_time and sleep_time in pair here. */ return sprintf(buf, "%lu.%09lu %lu.%09lu\n", suspend_resume_time.tv_sec, suspend_resume_time.tv_nsec, sleep_time.tv_sec, sleep_time.tv_nsec); @@ -145,21 +153,22 @@ void log_suspend_abort_reason(const char *fmt, ...) static int wakeup_reason_pm_event(struct notifier_block *notifier, unsigned long pm_event, void *unused) { - struct timespec xtom; /* wall_to_monotonic, ignored */ - switch (pm_event) { case PM_SUSPEND_PREPARE: spin_lock(&resume_reason_lock); irqcount = 0; suspend_abort = false; spin_unlock(&resume_reason_lock); - - get_xtime_and_monotonic_and_sleep_offset(&last_xtime, &xtom, - &last_stime); + /* monotonic time since boot */ + last_monotime = ktime_get(); + /* monotonic time since boot including the time spent in suspend */ + last_stime = ktime_get_boottime(); break; case PM_POST_SUSPEND: - get_xtime_and_monotonic_and_sleep_offset(&curr_xtime, &xtom, - &curr_stime); + /* monotonic time since boot */ + curr_monotime = ktime_get(); + /* monotonic time since boot including the time spent in suspend */ + curr_stime = ktime_get_boottime(); break; default: break; From 152ad9e399d36d64884679550107613e21632790 Mon Sep 17 00:00:00 2001 From: Ruchi Kandoi Date: Wed, 8 Apr 2015 15:42:29 -0700 Subject: [PATCH 0230/1103] ANDROID: power: wakeup: Add last wake up source logging for suspend abort reason. There is a possibility that a wakeup source event is received after the device prepares to suspend which might cause the suspend to abort. This patch adds the functionality of reporting the last active wakeup source which is currently not active but caused the suspend to abort reason via the /sys/kernel/power/last_wakeup_reason file. Change-Id: I1760d462f497b33e425f5565cb6cff5973932ec3 Signed-off-by: Ruchi Kandoi --- drivers/base/power/wakeup.c | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/drivers/base/power/wakeup.c b/drivers/base/power/wakeup.c index a4ebcf37c612..4110de9fe190 100644 --- a/drivers/base/power/wakeup.c +++ b/drivers/base/power/wakeup.c @@ -15,6 +15,7 @@ #include #include #include +#include #include #include "power.h" @@ -805,16 +806,31 @@ EXPORT_SYMBOL_GPL(pm_wakeup_dev_event); void pm_get_active_wakeup_sources(char *pending_wakeup_source, size_t max) { - struct wakeup_source *ws; + struct wakeup_source *ws, *last_active_ws = NULL; int len = 0; + bool active = false; + rcu_read_lock(); - len += snprintf(pending_wakeup_source, max, "Pending Wakeup Sources: "); list_for_each_entry_rcu(ws, &wakeup_sources, entry) { if (ws->active) { - len += snprintf(pending_wakeup_source + len, max, + if (!active) + len += scnprintf(pending_wakeup_source, max, + "Pending Wakeup Sources: "); + len += scnprintf(pending_wakeup_source + len, max - len, "%s ", ws->name); + active = true; + } else if (!active && + (!last_active_ws || + ktime_to_ns(ws->last_time) > + ktime_to_ns(last_active_ws->last_time))) { + last_active_ws = ws; } } + if (!active && last_active_ws) { + scnprintf(pending_wakeup_source, max, + "Last active Wakeup Source: %s", + last_active_ws->name); + } rcu_read_unlock(); } EXPORT_SYMBOL_GPL(pm_get_active_wakeup_sources); From 4dbe4f44a548701f9184743ef53a6822d65aa46a Mon Sep 17 00:00:00 2001 From: Ruchi Kandoi Date: Tue, 10 Nov 2015 10:53:55 -0800 Subject: [PATCH 0231/1103] ANDROID: power: wakeup: Add the guard condition for len in pm_get_active_wakeup_sources Check if the len is not greater than maximum to prevent buffer overflow. Signed-off-by: Ruchi Kandoi Change-Id: I575b0a72bb5448b68353408d71fa8b83420c9088 --- drivers/base/power/wakeup.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/base/power/wakeup.c b/drivers/base/power/wakeup.c index 4110de9fe190..e75512dd9733 100644 --- a/drivers/base/power/wakeup.c +++ b/drivers/base/power/wakeup.c @@ -812,7 +812,7 @@ void pm_get_active_wakeup_sources(char *pending_wakeup_source, size_t max) rcu_read_lock(); list_for_each_entry_rcu(ws, &wakeup_sources, entry) { - if (ws->active) { + if (ws->active && len < max) { if (!active) len += scnprintf(pending_wakeup_source, max, "Pending Wakeup Sources: "); From 4d8f5833f86b8fc5935458bdc5c3fb6c69f86065 Mon Sep 17 00:00:00 2001 From: Jeff Vander Stoep Date: Sun, 29 May 2016 14:22:32 -0700 Subject: [PATCH 0232/1103] ANDROID: security,perf: Allow further restriction of perf_event_open When kernel.perf_event_open is set to 3 (or greater), disallow all access to performance events by users without CAP_SYS_ADMIN. Add a Kconfig symbol CONFIG_SECURITY_PERF_EVENTS_RESTRICT that makes this value the default. This is based on a similar feature in grsecurity (CONFIG_GRKERNSEC_PERF_HARDEN). This version doesn't include making the variable read-only. It also allows enabling further restriction at run-time regardless of whether the default is changed. https://lkml.org/lkml/2016/1/11/587 Signed-off-by: Ben Hutchings Bug: 29054680 Change-Id: Iff5bff4fc1042e85866df9faa01bce8d04335ab8 --- Documentation/sysctl/kernel.txt | 4 +++- include/linux/perf_event.h | 5 +++++ kernel/events/core.c | 8 ++++++++ security/Kconfig | 9 +++++++++ 4 files changed, 25 insertions(+), 1 deletion(-) diff --git a/Documentation/sysctl/kernel.txt b/Documentation/sysctl/kernel.txt index 37a679501ddc..0e4a8129e86f 100644 --- a/Documentation/sysctl/kernel.txt +++ b/Documentation/sysctl/kernel.txt @@ -693,7 +693,8 @@ allowed to execute. perf_event_paranoid: Controls use of the performance events system by unprivileged -users (without CAP_SYS_ADMIN). The default value is 2. +users (without CAP_SYS_ADMIN). The default value is 3 if +CONFIG_SECURITY_PERF_EVENTS_RESTRICT is set, or 2 otherwise. -1: Allow use of (almost) all events by all users Ignore mlock limit after perf_event_mlock_kb without CAP_IPC_LOCK @@ -701,6 +702,7 @@ users (without CAP_SYS_ADMIN). The default value is 2. Disallow raw tracepoint access by users without CAP_SYS_ADMIN >=1: Disallow CPU event access by users without CAP_SYS_ADMIN >=2: Disallow kernel profiling by users without CAP_SYS_ADMIN +>=3: Disallow all event access by users without CAP_SYS_ADMIN ============================================================== diff --git a/include/linux/perf_event.h b/include/linux/perf_event.h index 53c500f0ca79..15c236b8aba3 100644 --- a/include/linux/perf_event.h +++ b/include/linux/perf_event.h @@ -1179,6 +1179,11 @@ extern int perf_cpu_time_max_percent_handler(struct ctl_table *table, int write, int perf_event_max_stack_handler(struct ctl_table *table, int write, void __user *buffer, size_t *lenp, loff_t *ppos); +static inline bool perf_paranoid_any(void) +{ + return sysctl_perf_event_paranoid > 2; +} + static inline bool perf_paranoid_tracepoint_raw(void) { return sysctl_perf_event_paranoid > -1; diff --git a/kernel/events/core.c b/kernel/events/core.c index 5a97f34bc14c..a4a4fc1e1586 100644 --- a/kernel/events/core.c +++ b/kernel/events/core.c @@ -397,8 +397,13 @@ static cpumask_var_t perf_online_mask; * 0 - disallow raw tracepoint access for unpriv * 1 - disallow cpu events for unpriv * 2 - disallow kernel profiling for unpriv + * 3 - disallow all unpriv perf event use */ +#ifdef CONFIG_SECURITY_PERF_EVENTS_RESTRICT +int sysctl_perf_event_paranoid __read_mostly = 3; +#else int sysctl_perf_event_paranoid __read_mostly = 2; +#endif /* Minimum for 512 kiB + 1 user control page */ int sysctl_perf_event_mlock __read_mostly = 512 + (PAGE_SIZE / 1024); /* 'free' kiB per user */ @@ -10410,6 +10415,9 @@ SYSCALL_DEFINE5(perf_event_open, if (flags & ~PERF_FLAG_ALL) return -EINVAL; + if (perf_paranoid_any() && !capable(CAP_SYS_ADMIN)) + return -EACCES; + err = perf_copy_attr(attr_uptr, &attr); if (err) return err; diff --git a/security/Kconfig b/security/Kconfig index d9aa521b5206..6c379cd0df1e 100644 --- a/security/Kconfig +++ b/security/Kconfig @@ -18,6 +18,15 @@ config SECURITY_DMESG_RESTRICT If you are unsure how to answer this question, answer N. +config SECURITY_PERF_EVENTS_RESTRICT + bool "Restrict unprivileged use of performance events" + depends on PERF_EVENTS + help + If you say Y here, the kernel.perf_event_paranoid sysctl + will be set to 3 by default, and no unprivileged use of the + perf_event_open syscall will be permitted unless it is + changed. + config SECURITY bool "Enable different security models" depends on SYSFS From bedc729ad27cf36ec490307ee88e3cbad196583d Mon Sep 17 00:00:00 2001 From: Riley Andrews Date: Fri, 2 Oct 2015 00:39:53 -0700 Subject: [PATCH 0233/1103] ANDROID: trace: sched: add sched blocked tracepoint which dumps out context of sleep. Decare war on uninterruptible sleep. Add a tracepoint which walks the kernel stack and dumps the first non-scheduler function called before the scheduler is invoked. Change-Id: I19e965d5206329360a92cbfe2afcc8c30f65c229 Signed-off-by: Riley Andrews --- include/trace/events/sched.h | 26 +++++++++++++++++++++++++- kernel/sched/fair.c | 1 + 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/include/trace/events/sched.h b/include/trace/events/sched.h index 0be866c91f62..4a68273b29d5 100644 --- a/include/trace/events/sched.h +++ b/include/trace/events/sched.h @@ -226,7 +226,7 @@ DECLARE_EVENT_CLASS(sched_process_template, DEFINE_EVENT(sched_process_template, sched_process_free, TP_PROTO(struct task_struct *p), TP_ARGS(p)); - + /* * Tracepoint for a task exiting: @@ -380,6 +380,30 @@ DEFINE_EVENT(sched_stat_template, sched_stat_blocked, TP_PROTO(struct task_struct *tsk, u64 delay), TP_ARGS(tsk, delay)); +/* + * Tracepoint for recording the cause of uninterruptible sleep. + */ +TRACE_EVENT(sched_blocked_reason, + + TP_PROTO(struct task_struct *tsk), + + TP_ARGS(tsk), + + TP_STRUCT__entry( + __field( pid_t, pid ) + __field( void*, caller ) + __field( bool, io_wait ) + ), + + TP_fast_assign( + __entry->pid = tsk->pid; + __entry->caller = (void*)get_wchan(tsk); + __entry->io_wait = tsk->in_iowait; + ), + + TP_printk("pid=%d iowait=%d caller=%pS", __entry->pid, __entry->io_wait, __entry->caller) +); + /* * Tracepoint for accounting runtime (time the task is executing * on a CPU). diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c index 7fc4a371bdd2..c24f3cfbf012 100644 --- a/kernel/sched/fair.c +++ b/kernel/sched/fair.c @@ -941,6 +941,7 @@ update_stats_enqueue_sleeper(struct cfs_rq *cfs_rq, struct sched_entity *se) } trace_sched_stat_blocked(tsk, delta); + trace_sched_blocked_reason(tsk); /* * Blocking time is in units of nanosecs, so shift by From 083490a11fef949114c6bee7e5770009609a2366 Mon Sep 17 00:00:00 2001 From: Benoit Goby Date: Mon, 19 Dec 2011 14:39:37 -0800 Subject: [PATCH 0234/1103] ANDROID: usb: gadget: f_accessory: Add Android Accessory function USB accessory mode allows users to connect USB host hardware specifically designed for Android-powered devices. The accessories must adhere to the Android accessory protocol outlined in the http://accessories.android.com documentation. This allows Android devices that cannot act as a USB host to still interact with USB hardware. When an Android device is in USB accessory mode, the attached Android USB accessory acts as the host, provides power to the USB bus, and enumerates connected devices. Change-Id: I67964b50d278f3c0471d47efbb7b0973a3502681 Signed-off-by: Mike Lockwood [AmitP: Folded following android-4.9 commit changes into this patch ceb2f0aac624 ("ANDROID: usb: gadget: accessory: Fix section mismatch") Parts of e27543931009 ("ANDROID: usb: gadget: Fixes and hacks to make android usb gadget compile on 3.8") 1b07ec751563 ("ANDROID: drivers: usb: gadget: 64-bit related type fixes")] Signed-off-by: Amit Pundir --- drivers/usb/gadget/f_accessory.c | 798 +++++++++++++++++++++++++++++++ include/linux/usb/f_accessory.h | 83 ++++ 2 files changed, 881 insertions(+) create mode 100644 drivers/usb/gadget/f_accessory.c create mode 100644 include/linux/usb/f_accessory.h diff --git a/drivers/usb/gadget/f_accessory.c b/drivers/usb/gadget/f_accessory.c new file mode 100644 index 000000000000..72e9f0b1b57c --- /dev/null +++ b/drivers/usb/gadget/f_accessory.c @@ -0,0 +1,798 @@ +/* + * Gadget Function Driver for Android USB accessories + * + * Copyright (C) 2011 Google, Inc. + * Author: Mike Lockwood + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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. + * + */ + +/* #define DEBUG */ +/* #define VERBOSE_DEBUG */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include + +#define BULK_BUFFER_SIZE 16384 +#define ACC_STRING_SIZE 256 + +#define PROTOCOL_VERSION 1 + +/* String IDs */ +#define INTERFACE_STRING_INDEX 0 + +/* number of tx and rx requests to allocate */ +#define TX_REQ_MAX 4 +#define RX_REQ_MAX 2 + +struct acc_dev { + struct usb_function function; + struct usb_composite_dev *cdev; + spinlock_t lock; + + struct usb_ep *ep_in; + struct usb_ep *ep_out; + + /* set to 1 when we connect */ + int online:1; + /* Set to 1 when we disconnect. + * Not cleared until our file is closed. + */ + int disconnected:1; + + /* strings sent by the host */ + char manufacturer[ACC_STRING_SIZE]; + char model[ACC_STRING_SIZE]; + char description[ACC_STRING_SIZE]; + char version[ACC_STRING_SIZE]; + char uri[ACC_STRING_SIZE]; + char serial[ACC_STRING_SIZE]; + + /* for acc_complete_set_string */ + int string_index; + + /* set to 1 if we have a pending start request */ + int start_requested; + + /* synchronize access to our device file */ + atomic_t open_excl; + + struct list_head tx_idle; + + wait_queue_head_t read_wq; + wait_queue_head_t write_wq; + struct usb_request *rx_req[RX_REQ_MAX]; + int rx_done; + struct delayed_work work; +}; + +static struct usb_interface_descriptor acc_interface_desc = { + .bLength = USB_DT_INTERFACE_SIZE, + .bDescriptorType = USB_DT_INTERFACE, + .bInterfaceNumber = 0, + .bNumEndpoints = 2, + .bInterfaceClass = USB_CLASS_VENDOR_SPEC, + .bInterfaceSubClass = USB_SUBCLASS_VENDOR_SPEC, + .bInterfaceProtocol = 0, +}; + +static struct usb_endpoint_descriptor acc_highspeed_in_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = USB_DIR_IN, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = __constant_cpu_to_le16(512), +}; + +static struct usb_endpoint_descriptor acc_highspeed_out_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = USB_DIR_OUT, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = __constant_cpu_to_le16(512), +}; + +static struct usb_endpoint_descriptor acc_fullspeed_in_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = USB_DIR_IN, + .bmAttributes = USB_ENDPOINT_XFER_BULK, +}; + +static struct usb_endpoint_descriptor acc_fullspeed_out_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = USB_DIR_OUT, + .bmAttributes = USB_ENDPOINT_XFER_BULK, +}; + +static struct usb_descriptor_header *fs_acc_descs[] = { + (struct usb_descriptor_header *) &acc_interface_desc, + (struct usb_descriptor_header *) &acc_fullspeed_in_desc, + (struct usb_descriptor_header *) &acc_fullspeed_out_desc, + NULL, +}; + +static struct usb_descriptor_header *hs_acc_descs[] = { + (struct usb_descriptor_header *) &acc_interface_desc, + (struct usb_descriptor_header *) &acc_highspeed_in_desc, + (struct usb_descriptor_header *) &acc_highspeed_out_desc, + NULL, +}; + +static struct usb_string acc_string_defs[] = { + [INTERFACE_STRING_INDEX].s = "Android Accessory Interface", + { }, /* end of list */ +}; + +static struct usb_gadget_strings acc_string_table = { + .language = 0x0409, /* en-US */ + .strings = acc_string_defs, +}; + +static struct usb_gadget_strings *acc_strings[] = { + &acc_string_table, + NULL, +}; + +/* temporary variable used between acc_open() and acc_gadget_bind() */ +static struct acc_dev *_acc_dev; + +static inline struct acc_dev *func_to_dev(struct usb_function *f) +{ + return container_of(f, struct acc_dev, function); +} + +static struct usb_request *acc_request_new(struct usb_ep *ep, int buffer_size) +{ + struct usb_request *req = usb_ep_alloc_request(ep, GFP_KERNEL); + if (!req) + return NULL; + + /* now allocate buffers for the requests */ + req->buf = kmalloc(buffer_size, GFP_KERNEL); + if (!req->buf) { + usb_ep_free_request(ep, req); + return NULL; + } + + return req; +} + +static void acc_request_free(struct usb_request *req, struct usb_ep *ep) +{ + if (req) { + kfree(req->buf); + usb_ep_free_request(ep, req); + } +} + +/* add a request to the tail of a list */ +static void req_put(struct acc_dev *dev, struct list_head *head, + struct usb_request *req) +{ + unsigned long flags; + + spin_lock_irqsave(&dev->lock, flags); + list_add_tail(&req->list, head); + spin_unlock_irqrestore(&dev->lock, flags); +} + +/* remove a request from the head of a list */ +static struct usb_request *req_get(struct acc_dev *dev, struct list_head *head) +{ + unsigned long flags; + struct usb_request *req; + + spin_lock_irqsave(&dev->lock, flags); + if (list_empty(head)) { + req = 0; + } else { + req = list_first_entry(head, struct usb_request, list); + list_del(&req->list); + } + spin_unlock_irqrestore(&dev->lock, flags); + return req; +} + +static void acc_set_disconnected(struct acc_dev *dev) +{ + dev->online = 0; + dev->disconnected = 1; +} + +static void acc_complete_in(struct usb_ep *ep, struct usb_request *req) +{ + struct acc_dev *dev = _acc_dev; + + if (req->status != 0) + acc_set_disconnected(dev); + + req_put(dev, &dev->tx_idle, req); + + wake_up(&dev->write_wq); +} + +static void acc_complete_out(struct usb_ep *ep, struct usb_request *req) +{ + struct acc_dev *dev = _acc_dev; + + dev->rx_done = 1; + if (req->status != 0) + acc_set_disconnected(dev); + + wake_up(&dev->read_wq); +} + +static void acc_complete_set_string(struct usb_ep *ep, struct usb_request *req) +{ + struct acc_dev *dev = ep->driver_data; + char *string_dest = NULL; + int length = req->actual; + + if (req->status != 0) { + pr_err("acc_complete_set_string, err %d\n", req->status); + return; + } + + switch (dev->string_index) { + case ACCESSORY_STRING_MANUFACTURER: + string_dest = dev->manufacturer; + break; + case ACCESSORY_STRING_MODEL: + string_dest = dev->model; + break; + case ACCESSORY_STRING_DESCRIPTION: + string_dest = dev->description; + break; + case ACCESSORY_STRING_VERSION: + string_dest = dev->version; + break; + case ACCESSORY_STRING_URI: + string_dest = dev->uri; + break; + case ACCESSORY_STRING_SERIAL: + string_dest = dev->serial; + break; + } + if (string_dest) { + unsigned long flags; + + if (length >= ACC_STRING_SIZE) + length = ACC_STRING_SIZE - 1; + + spin_lock_irqsave(&dev->lock, flags); + memcpy(string_dest, req->buf, length); + /* ensure zero termination */ + string_dest[length] = 0; + spin_unlock_irqrestore(&dev->lock, flags); + } else { + pr_err("unknown accessory string index %d\n", + dev->string_index); + } +} + +static int create_bulk_endpoints(struct acc_dev *dev, + struct usb_endpoint_descriptor *in_desc, + struct usb_endpoint_descriptor *out_desc) +{ + struct usb_composite_dev *cdev = dev->cdev; + struct usb_request *req; + struct usb_ep *ep; + int i; + + DBG(cdev, "create_bulk_endpoints dev: %p\n", dev); + + ep = usb_ep_autoconfig(cdev->gadget, in_desc); + if (!ep) { + DBG(cdev, "usb_ep_autoconfig for ep_in failed\n"); + return -ENODEV; + } + DBG(cdev, "usb_ep_autoconfig for ep_in got %s\n", ep->name); + ep->driver_data = dev; /* claim the endpoint */ + dev->ep_in = ep; + + ep = usb_ep_autoconfig(cdev->gadget, out_desc); + if (!ep) { + DBG(cdev, "usb_ep_autoconfig for ep_out failed\n"); + return -ENODEV; + } + DBG(cdev, "usb_ep_autoconfig for ep_out got %s\n", ep->name); + ep->driver_data = dev; /* claim the endpoint */ + dev->ep_out = ep; + + ep = usb_ep_autoconfig(cdev->gadget, out_desc); + if (!ep) { + DBG(cdev, "usb_ep_autoconfig for ep_out failed\n"); + return -ENODEV; + } + DBG(cdev, "usb_ep_autoconfig for ep_out got %s\n", ep->name); + ep->driver_data = dev; /* claim the endpoint */ + dev->ep_out = ep; + + /* now allocate requests for our endpoints */ + for (i = 0; i < TX_REQ_MAX; i++) { + req = acc_request_new(dev->ep_in, BULK_BUFFER_SIZE); + if (!req) + goto fail; + req->complete = acc_complete_in; + req_put(dev, &dev->tx_idle, req); + } + for (i = 0; i < RX_REQ_MAX; i++) { + req = acc_request_new(dev->ep_out, BULK_BUFFER_SIZE); + if (!req) + goto fail; + req->complete = acc_complete_out; + dev->rx_req[i] = req; + } + + return 0; + +fail: + printk(KERN_ERR "acc_bind() could not allocate requests\n"); + while ((req = req_get(dev, &dev->tx_idle))) + acc_request_free(req, dev->ep_in); + for (i = 0; i < RX_REQ_MAX; i++) + acc_request_free(dev->rx_req[i], dev->ep_out); + return -1; +} + +static ssize_t acc_read(struct file *fp, char __user *buf, + size_t count, loff_t *pos) +{ + struct acc_dev *dev = fp->private_data; + struct usb_request *req; + ssize_t r = count; + unsigned xfer; + int ret = 0; + + pr_debug("acc_read(%zu)\n", count); + + if (dev->disconnected) + return -ENODEV; + + if (count > BULK_BUFFER_SIZE) + count = BULK_BUFFER_SIZE; + + /* we will block until we're online */ + pr_debug("acc_read: waiting for online\n"); + ret = wait_event_interruptible(dev->read_wq, dev->online); + if (ret < 0) { + r = ret; + goto done; + } + +requeue_req: + /* queue a request */ + req = dev->rx_req[0]; + req->length = count; + dev->rx_done = 0; + ret = usb_ep_queue(dev->ep_out, req, GFP_KERNEL); + if (ret < 0) { + r = -EIO; + goto done; + } else { + pr_debug("rx %p queue\n", req); + } + + /* wait for a request to complete */ + ret = wait_event_interruptible(dev->read_wq, dev->rx_done); + if (ret < 0) { + r = ret; + usb_ep_dequeue(dev->ep_out, req); + goto done; + } + if (dev->online) { + /* If we got a 0-len packet, throw it back and try again. */ + if (req->actual == 0) + goto requeue_req; + + pr_debug("rx %p %u\n", req, req->actual); + xfer = (req->actual < count) ? req->actual : count; + r = xfer; + if (copy_to_user(buf, req->buf, xfer)) + r = -EFAULT; + } else + r = -EIO; + +done: + pr_debug("acc_read returning %zd\n", r); + return r; +} + +static ssize_t acc_write(struct file *fp, const char __user *buf, + size_t count, loff_t *pos) +{ + struct acc_dev *dev = fp->private_data; + struct usb_request *req = 0; + ssize_t r = count; + unsigned xfer; + int ret; + + pr_debug("acc_write(%zu)\n", count); + + if (!dev->online || dev->disconnected) + return -ENODEV; + + while (count > 0) { + if (!dev->online) { + pr_debug("acc_write dev->error\n"); + r = -EIO; + break; + } + + /* get an idle tx request to use */ + req = 0; + ret = wait_event_interruptible(dev->write_wq, + ((req = req_get(dev, &dev->tx_idle)) || !dev->online)); + if (!req) { + r = ret; + break; + } + + if (count > BULK_BUFFER_SIZE) + xfer = BULK_BUFFER_SIZE; + else + xfer = count; + if (copy_from_user(req->buf, buf, xfer)) { + r = -EFAULT; + break; + } + + req->length = xfer; + ret = usb_ep_queue(dev->ep_in, req, GFP_KERNEL); + if (ret < 0) { + pr_debug("acc_write: xfer error %d\n", ret); + r = -EIO; + break; + } + + buf += xfer; + count -= xfer; + + /* zero this so we don't try to free it on error exit */ + req = 0; + } + + if (req) + req_put(dev, &dev->tx_idle, req); + + pr_debug("acc_write returning %zd\n", r); + return r; +} + +static long acc_ioctl(struct file *fp, unsigned code, unsigned long value) +{ + struct acc_dev *dev = fp->private_data; + char *src = NULL; + int ret; + + switch (code) { + case ACCESSORY_GET_STRING_MANUFACTURER: + src = dev->manufacturer; + break; + case ACCESSORY_GET_STRING_MODEL: + src = dev->model; + break; + case ACCESSORY_GET_STRING_DESCRIPTION: + src = dev->description; + break; + case ACCESSORY_GET_STRING_VERSION: + src = dev->version; + break; + case ACCESSORY_GET_STRING_URI: + src = dev->uri; + break; + case ACCESSORY_GET_STRING_SERIAL: + src = dev->serial; + break; + case ACCESSORY_IS_START_REQUESTED: + return dev->start_requested; + } + if (!src) + return -EINVAL; + + ret = strlen(src) + 1; + if (copy_to_user((void __user *)value, src, ret)) + ret = -EFAULT; + return ret; +} + +static int acc_open(struct inode *ip, struct file *fp) +{ + printk(KERN_INFO "acc_open\n"); + if (atomic_xchg(&_acc_dev->open_excl, 1)) + return -EBUSY; + + _acc_dev->disconnected = 0; + fp->private_data = _acc_dev; + return 0; +} + +static int acc_release(struct inode *ip, struct file *fp) +{ + printk(KERN_INFO "acc_release\n"); + + WARN_ON(!atomic_xchg(&_acc_dev->open_excl, 0)); + _acc_dev->disconnected = 0; + return 0; +} + +/* file operations for /dev/acc_usb */ +static const struct file_operations acc_fops = { + .owner = THIS_MODULE, + .read = acc_read, + .write = acc_write, + .unlocked_ioctl = acc_ioctl, + .open = acc_open, + .release = acc_release, +}; + +static struct miscdevice acc_device = { + .minor = MISC_DYNAMIC_MINOR, + .name = "usb_accessory", + .fops = &acc_fops, +}; + + +static int acc_ctrlrequest(struct usb_composite_dev *cdev, + const struct usb_ctrlrequest *ctrl) +{ + struct acc_dev *dev = _acc_dev; + int value = -EOPNOTSUPP; + u8 b_requestType = ctrl->bRequestType; + u8 b_request = ctrl->bRequest; + u16 w_index = le16_to_cpu(ctrl->wIndex); + u16 w_value = le16_to_cpu(ctrl->wValue); + u16 w_length = le16_to_cpu(ctrl->wLength); + +/* + printk(KERN_INFO "acc_ctrlrequest " + "%02x.%02x v%04x i%04x l%u\n", + b_requestType, b_request, + w_value, w_index, w_length); +*/ + + if (b_requestType == (USB_DIR_OUT | USB_TYPE_VENDOR)) { + if (b_request == ACCESSORY_START) { + dev->start_requested = 1; + schedule_delayed_work( + &dev->work, msecs_to_jiffies(10)); + value = 0; + } else if (b_request == ACCESSORY_SEND_STRING) { + dev->string_index = w_index; + cdev->gadget->ep0->driver_data = dev; + cdev->req->complete = acc_complete_set_string; + value = w_length; + } + } else if (b_requestType == (USB_DIR_IN | USB_TYPE_VENDOR)) { + if (b_request == ACCESSORY_GET_PROTOCOL) { + *((u16 *)cdev->req->buf) = PROTOCOL_VERSION; + value = sizeof(u16); + + /* clear any string left over from a previous session */ + memset(dev->manufacturer, 0, sizeof(dev->manufacturer)); + memset(dev->model, 0, sizeof(dev->model)); + memset(dev->description, 0, sizeof(dev->description)); + memset(dev->version, 0, sizeof(dev->version)); + memset(dev->uri, 0, sizeof(dev->uri)); + memset(dev->serial, 0, sizeof(dev->serial)); + dev->start_requested = 0; + } + } + + if (value >= 0) { + cdev->req->zero = 0; + cdev->req->length = value; + value = usb_ep_queue(cdev->gadget->ep0, cdev->req, GFP_ATOMIC); + if (value < 0) + ERROR(cdev, "%s setup response queue error\n", + __func__); + } + + if (value == -EOPNOTSUPP) + VDBG(cdev, + "unknown class-specific control req " + "%02x.%02x v%04x i%04x l%u\n", + ctrl->bRequestType, ctrl->bRequest, + w_value, w_index, w_length); + return value; +} + +static int +acc_function_bind(struct usb_configuration *c, struct usb_function *f) +{ + struct usb_composite_dev *cdev = c->cdev; + struct acc_dev *dev = func_to_dev(f); + int id; + int ret; + + DBG(cdev, "acc_function_bind dev: %p\n", dev); + + dev->start_requested = 0; + + /* allocate interface ID(s) */ + id = usb_interface_id(c, f); + if (id < 0) + return id; + acc_interface_desc.bInterfaceNumber = id; + + /* allocate endpoints */ + ret = create_bulk_endpoints(dev, &acc_fullspeed_in_desc, + &acc_fullspeed_out_desc); + if (ret) + return ret; + + /* support high speed hardware */ + if (gadget_is_dualspeed(c->cdev->gadget)) { + acc_highspeed_in_desc.bEndpointAddress = + acc_fullspeed_in_desc.bEndpointAddress; + acc_highspeed_out_desc.bEndpointAddress = + acc_fullspeed_out_desc.bEndpointAddress; + } + + DBG(cdev, "%s speed %s: IN/%s, OUT/%s\n", + gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full", + f->name, dev->ep_in->name, dev->ep_out->name); + return 0; +} + +static void +acc_function_unbind(struct usb_configuration *c, struct usb_function *f) +{ + struct acc_dev *dev = func_to_dev(f); + struct usb_request *req; + int i; + + while ((req = req_get(dev, &dev->tx_idle))) + acc_request_free(req, dev->ep_in); + for (i = 0; i < RX_REQ_MAX; i++) + acc_request_free(dev->rx_req[i], dev->ep_out); +} + +static void acc_work(struct work_struct *data) +{ + char *envp[2] = { "ACCESSORY=START", NULL }; + kobject_uevent_env(&acc_device.this_device->kobj, KOBJ_CHANGE, envp); +} + +static int acc_function_set_alt(struct usb_function *f, + unsigned intf, unsigned alt) +{ + struct acc_dev *dev = func_to_dev(f); + struct usb_composite_dev *cdev = f->config->cdev; + int ret; + + DBG(cdev, "acc_function_set_alt intf: %d alt: %d\n", intf, alt); + + ret = config_ep_by_speed(cdev->gadget, f, dev->ep_in); + if (ret) + return ret; + + ret = usb_ep_enable(dev->ep_in); + if (ret) + return ret; + + ret = config_ep_by_speed(cdev->gadget, f, dev->ep_out); + if (ret) + return ret; + + ret = usb_ep_enable(dev->ep_out); + if (ret) { + usb_ep_disable(dev->ep_in); + return ret; + } + + dev->online = 1; + + /* readers may be blocked waiting for us to go online */ + wake_up(&dev->read_wq); + return 0; +} + +static void acc_function_disable(struct usb_function *f) +{ + struct acc_dev *dev = func_to_dev(f); + struct usb_composite_dev *cdev = dev->cdev; + + DBG(cdev, "acc_function_disable\n"); + acc_set_disconnected(dev); + usb_ep_disable(dev->ep_in); + usb_ep_disable(dev->ep_out); + + /* readers may be blocked waiting for us to go online */ + wake_up(&dev->read_wq); + + VDBG(cdev, "%s disabled\n", dev->function.name); +} + +static int acc_bind_config(struct usb_configuration *c) +{ + struct acc_dev *dev = _acc_dev; + int ret; + + printk(KERN_INFO "acc_bind_config\n"); + + /* allocate a string ID for our interface */ + if (acc_string_defs[INTERFACE_STRING_INDEX].id == 0) { + ret = usb_string_id(c->cdev); + if (ret < 0) + return ret; + acc_string_defs[INTERFACE_STRING_INDEX].id = ret; + acc_interface_desc.iInterface = ret; + } + + dev->cdev = c->cdev; + dev->function.name = "accessory"; + dev->function.strings = acc_strings, + dev->function.fs_descriptors = fs_acc_descs; + dev->function.hs_descriptors = hs_acc_descs; + dev->function.bind = acc_function_bind; + dev->function.unbind = acc_function_unbind; + dev->function.set_alt = acc_function_set_alt; + dev->function.disable = acc_function_disable; + + return usb_add_function(c, &dev->function); +} + +static int acc_setup(void) +{ + struct acc_dev *dev; + int ret; + + dev = kzalloc(sizeof(*dev), GFP_KERNEL); + if (!dev) + return -ENOMEM; + + spin_lock_init(&dev->lock); + init_waitqueue_head(&dev->read_wq); + init_waitqueue_head(&dev->write_wq); + atomic_set(&dev->open_excl, 0); + INIT_LIST_HEAD(&dev->tx_idle); + INIT_DELAYED_WORK(&dev->work, acc_work); + + /* _acc_dev must be set before calling usb_gadget_register_driver */ + _acc_dev = dev; + + ret = misc_register(&acc_device); + if (ret) + goto err; + + return 0; + +err: + kfree(dev); + printk(KERN_ERR "USB accessory gadget driver failed to initialize\n"); + return ret; +} + +static void acc_cleanup(void) +{ + misc_deregister(&acc_device); + kfree(_acc_dev); + _acc_dev = NULL; +} diff --git a/include/linux/usb/f_accessory.h b/include/linux/usb/f_accessory.h new file mode 100644 index 000000000000..5b2dcf9728e1 --- /dev/null +++ b/include/linux/usb/f_accessory.h @@ -0,0 +1,83 @@ +/* + * Gadget Function Driver for Android USB accessories + * + * Copyright (C) 2011 Google, Inc. + * Author: Mike Lockwood + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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. + * + */ + +#ifndef __LINUX_USB_F_ACCESSORY_H +#define __LINUX_USB_F_ACCESSORY_H + +/* Use Google Vendor ID when in accessory mode */ +#define USB_ACCESSORY_VENDOR_ID 0x18D1 + + +/* Product ID to use when in accessory mode */ +#define USB_ACCESSORY_PRODUCT_ID 0x2D00 + +/* Product ID to use when in accessory mode and adb is enabled */ +#define USB_ACCESSORY_ADB_PRODUCT_ID 0x2D01 + +/* Indexes for strings sent by the host via ACCESSORY_SEND_STRING */ +#define ACCESSORY_STRING_MANUFACTURER 0 +#define ACCESSORY_STRING_MODEL 1 +#define ACCESSORY_STRING_DESCRIPTION 2 +#define ACCESSORY_STRING_VERSION 3 +#define ACCESSORY_STRING_URI 4 +#define ACCESSORY_STRING_SERIAL 5 + +/* Control request for retrieving device's protocol version (currently 1) + * + * requestType: USB_DIR_IN | USB_TYPE_VENDOR + * request: ACCESSORY_GET_PROTOCOL + * value: 0 + * index: 0 + * data version number (16 bits little endian) + */ +#define ACCESSORY_GET_PROTOCOL 51 + +/* Control request for host to send a string to the device + * + * requestType: USB_DIR_OUT | USB_TYPE_VENDOR + * request: ACCESSORY_SEND_STRING + * value: 0 + * index: string ID + * data zero terminated UTF8 string + * + * The device can later retrieve these strings via the + * ACCESSORY_GET_STRING_* ioctls + */ +#define ACCESSORY_SEND_STRING 52 + +/* Control request for starting device in accessory mode. + * The host sends this after setting all its strings to the device. + * + * requestType: USB_DIR_OUT | USB_TYPE_VENDOR + * request: ACCESSORY_START + * value: 0 + * index: 0 + * data none + */ +#define ACCESSORY_START 53 + +/* ioctls for retrieving strings set by the host */ +#define ACCESSORY_GET_STRING_MANUFACTURER _IOW('M', 1, char[256]) +#define ACCESSORY_GET_STRING_MODEL _IOW('M', 2, char[256]) +#define ACCESSORY_GET_STRING_DESCRIPTION _IOW('M', 3, char[256]) +#define ACCESSORY_GET_STRING_VERSION _IOW('M', 4, char[256]) +#define ACCESSORY_GET_STRING_URI _IOW('M', 5, char[256]) +#define ACCESSORY_GET_STRING_SERIAL _IOW('M', 6, char[256]) +/* returns 1 if there is a start request pending */ +#define ACCESSORY_IS_START_REQUESTED _IO('M', 7) + +#endif /* __LINUX_USB_F_ACCESSORY_H */ From 3109dd3edab876a077c1a827d7aeadfd9afae487 Mon Sep 17 00:00:00 2001 From: Mike Lockwood Date: Fri, 11 May 2012 09:00:40 -0700 Subject: [PATCH 0235/1103] ANDROID: usb: gadget: f_accessory: Add ACCESSORY_SET_AUDIO_MODE control request and ioctl The control request will be used by the host to enable/disable USB audio and the ioctl will be used by userspace to read the audio mode Change-Id: I81c38611b588451e80eacdccc417ca6e11c60cab Signed-off-by: Mike Lockwood --- drivers/usb/gadget/f_accessory.c | 11 ++++++++++- include/linux/usb/f_accessory.h | 17 ++++++++++++++++- 2 files changed, 26 insertions(+), 2 deletions(-) diff --git a/drivers/usb/gadget/f_accessory.c b/drivers/usb/gadget/f_accessory.c index 72e9f0b1b57c..216d669e6344 100644 --- a/drivers/usb/gadget/f_accessory.c +++ b/drivers/usb/gadget/f_accessory.c @@ -40,7 +40,7 @@ #define BULK_BUFFER_SIZE 16384 #define ACC_STRING_SIZE 256 -#define PROTOCOL_VERSION 1 +#define PROTOCOL_VERSION 2 /* String IDs */ #define INTERFACE_STRING_INDEX 0 @@ -78,6 +78,8 @@ struct acc_dev { /* set to 1 if we have a pending start request */ int start_requested; + int audio_mode; + /* synchronize access to our device file */ atomic_t open_excl; @@ -512,6 +514,8 @@ static long acc_ioctl(struct file *fp, unsigned code, unsigned long value) break; case ACCESSORY_IS_START_REQUESTED: return dev->start_requested; + case ACCESSORY_GET_AUDIO_MODE: + return dev->audio_mode; } if (!src) return -EINVAL; @@ -588,6 +592,10 @@ static int acc_ctrlrequest(struct usb_composite_dev *cdev, cdev->gadget->ep0->driver_data = dev; cdev->req->complete = acc_complete_set_string; value = w_length; + } else if (b_request == ACCESSORY_SET_AUDIO_MODE && + w_index == 0 && w_length == 0) { + dev->audio_mode = w_value; + value = 0; } } else if (b_requestType == (USB_DIR_IN | USB_TYPE_VENDOR)) { if (b_request == ACCESSORY_GET_PROTOCOL) { @@ -602,6 +610,7 @@ static int acc_ctrlrequest(struct usb_composite_dev *cdev, memset(dev->uri, 0, sizeof(dev->uri)); memset(dev->serial, 0, sizeof(dev->serial)); dev->start_requested = 0; + dev->audio_mode = 0; } } diff --git a/include/linux/usb/f_accessory.h b/include/linux/usb/f_accessory.h index 5b2dcf9728e1..ddb2fd0e88fb 100644 --- a/include/linux/usb/f_accessory.h +++ b/include/linux/usb/f_accessory.h @@ -36,13 +36,15 @@ #define ACCESSORY_STRING_URI 4 #define ACCESSORY_STRING_SERIAL 5 -/* Control request for retrieving device's protocol version (currently 1) +/* Control request for retrieving device's protocol version * * requestType: USB_DIR_IN | USB_TYPE_VENDOR * request: ACCESSORY_GET_PROTOCOL * value: 0 * index: 0 * data version number (16 bits little endian) + * 1 for original accessory support + * 2 adds device to host audio support */ #define ACCESSORY_GET_PROTOCOL 51 @@ -70,6 +72,17 @@ */ #define ACCESSORY_START 53 +/* Control request for setting the audio mode. + * + * requestType: USB_DIR_OUT | USB_TYPE_VENDOR + * request: ACCESSORY_SET_AUDIO_MODE + * value: 0 - no audio + * 1 - device to host, 44100 16-bit stereo PCM + * index: 0 + * data none + */ +#define ACCESSORY_SET_AUDIO_MODE 58 + /* ioctls for retrieving strings set by the host */ #define ACCESSORY_GET_STRING_MANUFACTURER _IOW('M', 1, char[256]) #define ACCESSORY_GET_STRING_MODEL _IOW('M', 2, char[256]) @@ -79,5 +92,7 @@ #define ACCESSORY_GET_STRING_SERIAL _IOW('M', 6, char[256]) /* returns 1 if there is a start request pending */ #define ACCESSORY_IS_START_REQUESTED _IO('M', 7) +/* returns audio mode (set via the ACCESSORY_SET_AUDIO_MODE control request) */ +#define ACCESSORY_GET_AUDIO_MODE _IO('M', 8) #endif /* __LINUX_USB_F_ACCESSORY_H */ From 58f732407b26bb061925a9bc2a1e292fd4aff64d Mon Sep 17 00:00:00 2001 From: Mike Lockwood Date: Mon, 26 Mar 2012 11:03:55 -0700 Subject: [PATCH 0236/1103] ANDROID: usb: gadget: f_accessory: Add support for HID input devices Change-Id: I4f1452db32508382df52acdc47c0eb395ae328c7 Signed-off-by: Mike Lockwood [AmitP: Folded following android-4.9 commit changes into this patch 4ce5e656ae74 ("ANDROID: usb: gadget: accessory: Fix section mismatch (again)")] Signed-off-by: Amit Pundir --- drivers/usb/gadget/f_accessory.c | 389 ++++++++++++++++++++++++++++++- include/linux/usb/f_accessory.h | 50 +++- 2 files changed, 431 insertions(+), 8 deletions(-) diff --git a/drivers/usb/gadget/f_accessory.c b/drivers/usb/gadget/f_accessory.c index 216d669e6344..73c6b2072d73 100644 --- a/drivers/usb/gadget/f_accessory.c +++ b/drivers/usb/gadget/f_accessory.c @@ -33,6 +33,8 @@ #include #include +#include +#include #include #include #include @@ -49,6 +51,20 @@ #define TX_REQ_MAX 4 #define RX_REQ_MAX 2 +struct acc_hid_dev { + struct list_head list; + struct hid_device *hid; + struct acc_dev *dev; + /* accessory defined ID */ + int id; + /* HID report descriptor */ + u8 *report_desc; + /* length of HID report descriptor */ + int report_desc_len; + /* number of bytes of report_desc we have received so far */ + int report_desc_offset; +}; + struct acc_dev { struct usb_function function; struct usb_composite_dev *cdev; @@ -89,7 +105,21 @@ struct acc_dev { wait_queue_head_t write_wq; struct usb_request *rx_req[RX_REQ_MAX]; int rx_done; - struct delayed_work work; + + /* delayed work for handling ACCESSORY_START */ + struct delayed_work start_work; + + /* worker for registering and unregistering hid devices */ + struct work_struct hid_work; + + /* list of active HID devices */ + struct list_head hid_list; + + /* list of new HID devices to register */ + struct list_head new_hid_list; + + /* list of dead HID devices to unregister */ + struct list_head dead_hid_list; }; static struct usb_interface_descriptor acc_interface_desc = { @@ -298,6 +328,160 @@ static void acc_complete_set_string(struct usb_ep *ep, struct usb_request *req) } } +static void acc_complete_set_hid_report_desc(struct usb_ep *ep, + struct usb_request *req) +{ + struct acc_hid_dev *hid = req->context; + struct acc_dev *dev = hid->dev; + int length = req->actual; + + if (req->status != 0) { + pr_err("acc_complete_set_hid_report_desc, err %d\n", + req->status); + return; + } + + memcpy(hid->report_desc + hid->report_desc_offset, req->buf, length); + hid->report_desc_offset += length; + if (hid->report_desc_offset == hid->report_desc_len) { + /* After we have received the entire report descriptor + * we schedule work to initialize the HID device + */ + schedule_work(&dev->hid_work); + } +} + +static void acc_complete_send_hid_event(struct usb_ep *ep, + struct usb_request *req) +{ + struct acc_hid_dev *hid = req->context; + int length = req->actual; + + if (req->status != 0) { + pr_err("acc_complete_send_hid_event, err %d\n", req->status); + return; + } + + hid_report_raw_event(hid->hid, HID_INPUT_REPORT, req->buf, length, 1); +} + +static int acc_hid_parse(struct hid_device *hid) +{ + struct acc_hid_dev *hdev = hid->driver_data; + + hid_parse_report(hid, hdev->report_desc, hdev->report_desc_len); + return 0; +} + +static int acc_hid_start(struct hid_device *hid) +{ + return 0; +} + +static void acc_hid_stop(struct hid_device *hid) +{ +} + +static int acc_hid_open(struct hid_device *hid) +{ + return 0; +} + +static void acc_hid_close(struct hid_device *hid) +{ +} + +static struct hid_ll_driver acc_hid_ll_driver = { + .parse = acc_hid_parse, + .start = acc_hid_start, + .stop = acc_hid_stop, + .open = acc_hid_open, + .close = acc_hid_close, +}; + +static struct acc_hid_dev *acc_hid_new(struct acc_dev *dev, + int id, int desc_len) +{ + struct acc_hid_dev *hdev; + + hdev = kzalloc(sizeof(*hdev), GFP_ATOMIC); + if (!hdev) + return NULL; + hdev->report_desc = kzalloc(desc_len, GFP_ATOMIC); + if (!hdev->report_desc) { + kfree(hdev); + return NULL; + } + hdev->dev = dev; + hdev->id = id; + hdev->report_desc_len = desc_len; + + return hdev; +} + +static struct acc_hid_dev *acc_hid_get(struct list_head *list, int id) +{ + struct acc_hid_dev *hid; + + list_for_each_entry(hid, list, list) { + if (hid->id == id) + return hid; + } + return NULL; +} + +static int acc_register_hid(struct acc_dev *dev, int id, int desc_length) +{ + struct acc_hid_dev *hid; + unsigned long flags; + + /* report descriptor length must be > 0 */ + if (desc_length <= 0) + return -EINVAL; + + spin_lock_irqsave(&dev->lock, flags); + /* replace HID if one already exists with this ID */ + hid = acc_hid_get(&dev->hid_list, id); + if (!hid) + hid = acc_hid_get(&dev->new_hid_list, id); + if (hid) + list_move(&hid->list, &dev->dead_hid_list); + + hid = acc_hid_new(dev, id, desc_length); + if (!hid) { + spin_unlock_irqrestore(&dev->lock, flags); + return -ENOMEM; + } + + list_add(&hid->list, &dev->new_hid_list); + spin_unlock_irqrestore(&dev->lock, flags); + + /* schedule work to register the HID device */ + schedule_work(&dev->hid_work); + return 0; +} + +static int acc_unregister_hid(struct acc_dev *dev, int id) +{ + struct acc_hid_dev *hid; + unsigned long flags; + + spin_lock_irqsave(&dev->lock, flags); + hid = acc_hid_get(&dev->hid_list, id); + if (!hid) + hid = acc_hid_get(&dev->new_hid_list, id); + if (!hid) { + spin_unlock_irqrestore(&dev->lock, flags); + return -EINVAL; + } + + list_move(&hid->list, &dev->dead_hid_list); + spin_unlock_irqrestore(&dev->lock, flags); + + schedule_work(&dev->hid_work); + return 0; +} + static int create_bulk_endpoints(struct acc_dev *dev, struct usb_endpoint_descriptor *in_desc, struct usb_endpoint_descriptor *out_desc) @@ -355,7 +539,7 @@ static int create_bulk_endpoints(struct acc_dev *dev, return 0; fail: - printk(KERN_ERR "acc_bind() could not allocate requests\n"); + pr_err("acc_bind() could not allocate requests\n"); while ((req = req_get(dev, &dev->tx_idle))) acc_request_free(req, dev->ep_in); for (i = 0; i < RX_REQ_MAX; i++) @@ -546,7 +730,7 @@ static int acc_release(struct inode *ip, struct file *fp) return 0; } -/* file operations for /dev/acc_usb */ +/* file operations for /dev/usb_accessory */ static const struct file_operations acc_fops = { .owner = THIS_MODULE, .read = acc_read, @@ -556,23 +740,47 @@ static const struct file_operations acc_fops = { .release = acc_release, }; +static int acc_hid_probe(struct hid_device *hdev, + const struct hid_device_id *id) +{ + int ret; + + ret = hid_parse(hdev); + if (ret) + return ret; + return hid_hw_start(hdev, HID_CONNECT_DEFAULT); +} + static struct miscdevice acc_device = { .minor = MISC_DYNAMIC_MINOR, .name = "usb_accessory", .fops = &acc_fops, }; +static const struct hid_device_id acc_hid_table[] = { + { HID_USB_DEVICE(HID_ANY_ID, HID_ANY_ID) }, + { } +}; + +static struct hid_driver acc_hid_driver = { + .name = "USB accessory", + .id_table = acc_hid_table, + .probe = acc_hid_probe, +}; static int acc_ctrlrequest(struct usb_composite_dev *cdev, const struct usb_ctrlrequest *ctrl) { struct acc_dev *dev = _acc_dev; int value = -EOPNOTSUPP; + struct acc_hid_dev *hid; + int offset; u8 b_requestType = ctrl->bRequestType; u8 b_request = ctrl->bRequest; u16 w_index = le16_to_cpu(ctrl->wIndex); u16 w_value = le16_to_cpu(ctrl->wValue); u16 w_length = le16_to_cpu(ctrl->wLength); + unsigned long flags; /* printk(KERN_INFO "acc_ctrlrequest " @@ -585,7 +793,7 @@ static int acc_ctrlrequest(struct usb_composite_dev *cdev, if (b_request == ACCESSORY_START) { dev->start_requested = 1; schedule_delayed_work( - &dev->work, msecs_to_jiffies(10)); + &dev->start_work, msecs_to_jiffies(10)); value = 0; } else if (b_request == ACCESSORY_SEND_STRING) { dev->string_index = w_index; @@ -596,6 +804,38 @@ static int acc_ctrlrequest(struct usb_composite_dev *cdev, w_index == 0 && w_length == 0) { dev->audio_mode = w_value; value = 0; + } else if (b_request == ACCESSORY_REGISTER_HID) { + value = acc_register_hid(dev, w_value, w_index); + } else if (b_request == ACCESSORY_UNREGISTER_HID) { + value = acc_unregister_hid(dev, w_value); + } else if (b_request == ACCESSORY_SET_HID_REPORT_DESC) { + spin_lock_irqsave(&dev->lock, flags); + hid = acc_hid_get(&dev->new_hid_list, w_value); + spin_unlock_irqrestore(&dev->lock, flags); + if (!hid) { + value = -EINVAL; + goto err; + } + offset = w_index; + if (offset != hid->report_desc_offset + || offset + w_length > hid->report_desc_len) { + value = -EINVAL; + goto err; + } + cdev->req->context = hid; + cdev->req->complete = acc_complete_set_hid_report_desc; + value = w_length; + } else if (b_request == ACCESSORY_SEND_HID_EVENT) { + spin_lock_irqsave(&dev->lock, flags); + hid = acc_hid_get(&dev->hid_list, w_value); + spin_unlock_irqrestore(&dev->lock, flags); + if (!hid) { + value = -EINVAL; + goto err; + } + cdev->req->context = hid; + cdev->req->complete = acc_complete_send_hid_event; + value = w_length; } } else if (b_requestType == (USB_DIR_IN | USB_TYPE_VENDOR)) { if (b_request == ACCESSORY_GET_PROTOCOL) { @@ -623,6 +863,7 @@ static int acc_ctrlrequest(struct usb_composite_dev *cdev, __func__); } +err: if (value == -EOPNOTSUPP) VDBG(cdev, "unknown class-specific control req " @@ -642,6 +883,10 @@ acc_function_bind(struct usb_configuration *c, struct usb_function *f) DBG(cdev, "acc_function_bind dev: %p\n", dev); + ret = hid_register_driver(&acc_hid_driver); + if (ret) + return ret; + dev->start_requested = 0; /* allocate interface ID(s) */ @@ -670,6 +915,36 @@ acc_function_bind(struct usb_configuration *c, struct usb_function *f) return 0; } +static void +kill_all_hid_devices(struct acc_dev *dev) +{ + struct acc_hid_dev *hid; + struct list_head *entry, *temp; + unsigned long flags; + + spin_lock_irqsave(&dev->lock, flags); + list_for_each_safe(entry, temp, &dev->hid_list) { + hid = list_entry(entry, struct acc_hid_dev, list); + list_del(&hid->list); + list_add(&hid->list, &dev->dead_hid_list); + } + list_for_each_safe(entry, temp, &dev->new_hid_list) { + hid = list_entry(entry, struct acc_hid_dev, list); + list_del(&hid->list); + list_add(&hid->list, &dev->dead_hid_list); + } + spin_unlock_irqrestore(&dev->lock, flags); + + schedule_work(&dev->hid_work); +} + +static void +acc_hid_unbind(struct acc_dev *dev) +{ + hid_unregister_driver(&acc_hid_driver); + kill_all_hid_devices(dev); +} + static void acc_function_unbind(struct usb_configuration *c, struct usb_function *f) { @@ -681,14 +956,104 @@ acc_function_unbind(struct usb_configuration *c, struct usb_function *f) acc_request_free(req, dev->ep_in); for (i = 0; i < RX_REQ_MAX; i++) acc_request_free(dev->rx_req[i], dev->ep_out); + + acc_hid_unbind(dev); } -static void acc_work(struct work_struct *data) +static void acc_start_work(struct work_struct *data) { char *envp[2] = { "ACCESSORY=START", NULL }; kobject_uevent_env(&acc_device.this_device->kobj, KOBJ_CHANGE, envp); } +static int acc_hid_init(struct acc_hid_dev *hdev) +{ + struct hid_device *hid; + int ret; + + hid = hid_allocate_device(); + if (IS_ERR(hid)) + return PTR_ERR(hid); + + hid->ll_driver = &acc_hid_ll_driver; + hid->dev.parent = acc_device.this_device; + + hid->bus = BUS_USB; + hid->vendor = HID_ANY_ID; + hid->product = HID_ANY_ID; + hid->driver_data = hdev; + ret = hid_add_device(hid); + if (ret) { + pr_err("can't add hid device: %d\n", ret); + hid_destroy_device(hid); + return ret; + } + + hdev->hid = hid; + return 0; +} + +static void acc_hid_delete(struct acc_hid_dev *hid) +{ + kfree(hid->report_desc); + kfree(hid); +} + +static void acc_hid_work(struct work_struct *data) +{ + struct acc_dev *dev = _acc_dev; + struct list_head *entry, *temp; + struct acc_hid_dev *hid; + struct list_head new_list, dead_list; + unsigned long flags; + + INIT_LIST_HEAD(&new_list); + + spin_lock_irqsave(&dev->lock, flags); + + /* copy hids that are ready for initialization to new_list */ + list_for_each_safe(entry, temp, &dev->new_hid_list) { + hid = list_entry(entry, struct acc_hid_dev, list); + if (hid->report_desc_offset == hid->report_desc_len) + list_move(&hid->list, &new_list); + } + + if (list_empty(&dev->dead_hid_list)) { + INIT_LIST_HEAD(&dead_list); + } else { + /* move all of dev->dead_hid_list to dead_list */ + dead_list.prev = dev->dead_hid_list.prev; + dead_list.next = dev->dead_hid_list.next; + dead_list.next->prev = &dead_list; + dead_list.prev->next = &dead_list; + INIT_LIST_HEAD(&dev->dead_hid_list); + } + + spin_unlock_irqrestore(&dev->lock, flags); + + /* register new HID devices */ + list_for_each_safe(entry, temp, &new_list) { + hid = list_entry(entry, struct acc_hid_dev, list); + if (acc_hid_init(hid)) { + pr_err("can't add HID device %p\n", hid); + acc_hid_delete(hid); + } else { + spin_lock_irqsave(&dev->lock, flags); + list_move(&hid->list, &dev->hid_list); + spin_unlock_irqrestore(&dev->lock, flags); + } + } + + /* remove dead HID devices */ + list_for_each_safe(entry, temp, &dead_list) { + hid = list_entry(entry, struct acc_hid_dev, list); + list_del(&hid->list); + if (hid->hid) + hid_destroy_device(hid->hid); + acc_hid_delete(hid); + } +} + static int acc_function_set_alt(struct usb_function *f, unsigned intf, unsigned alt) { @@ -782,7 +1147,11 @@ static int acc_setup(void) init_waitqueue_head(&dev->write_wq); atomic_set(&dev->open_excl, 0); INIT_LIST_HEAD(&dev->tx_idle); - INIT_DELAYED_WORK(&dev->work, acc_work); + INIT_LIST_HEAD(&dev->hid_list); + INIT_LIST_HEAD(&dev->new_hid_list); + INIT_LIST_HEAD(&dev->dead_hid_list); + INIT_DELAYED_WORK(&dev->start_work, acc_start_work); + INIT_WORK(&dev->hid_work, acc_hid_work); /* _acc_dev must be set before calling usb_gadget_register_driver */ _acc_dev = dev; @@ -795,10 +1164,16 @@ static int acc_setup(void) err: kfree(dev); - printk(KERN_ERR "USB accessory gadget driver failed to initialize\n"); + pr_err("USB accessory gadget driver failed to initialize\n"); return ret; } +static void acc_disconnect(void) +{ + /* unregister all HID devices if USB is disconnected */ + kill_all_hid_devices(_acc_dev); +} + static void acc_cleanup(void) { misc_deregister(&acc_device); diff --git a/include/linux/usb/f_accessory.h b/include/linux/usb/f_accessory.h index ddb2fd0e88fb..61ebe0aabc5b 100644 --- a/include/linux/usb/f_accessory.h +++ b/include/linux/usb/f_accessory.h @@ -44,7 +44,7 @@ * index: 0 * data version number (16 bits little endian) * 1 for original accessory support - * 2 adds device to host audio support + * 2 adds HID and device to host audio support */ #define ACCESSORY_GET_PROTOCOL 51 @@ -72,6 +72,54 @@ */ #define ACCESSORY_START 53 +/* Control request for registering a HID device. + * Upon registering, a unique ID is sent by the accessory in the + * value parameter. This ID will be used for future commands for + * the device + * + * requestType: USB_DIR_OUT | USB_TYPE_VENDOR + * request: ACCESSORY_REGISTER_HID_DEVICE + * value: Accessory assigned ID for the HID device + * index: total length of the HID report descriptor + * data none + */ +#define ACCESSORY_REGISTER_HID 54 + +/* Control request for unregistering a HID device. + * + * requestType: USB_DIR_OUT | USB_TYPE_VENDOR + * request: ACCESSORY_REGISTER_HID + * value: Accessory assigned ID for the HID device + * index: 0 + * data none + */ +#define ACCESSORY_UNREGISTER_HID 55 + +/* Control request for sending the HID report descriptor. + * If the HID descriptor is longer than the endpoint zero max packet size, + * the descriptor will be sent in multiple ACCESSORY_SET_HID_REPORT_DESC + * commands. The data for the descriptor must be sent sequentially + * if multiple packets are needed. + * + * requestType: USB_DIR_OUT | USB_TYPE_VENDOR + * request: ACCESSORY_SET_HID_REPORT_DESC + * value: Accessory assigned ID for the HID device + * index: offset of data in descriptor + * (needed when HID descriptor is too big for one packet) + * data the HID report descriptor + */ +#define ACCESSORY_SET_HID_REPORT_DESC 56 + +/* Control request for sending HID events. + * + * requestType: USB_DIR_OUT | USB_TYPE_VENDOR + * request: ACCESSORY_SEND_HID_EVENT + * value: Accessory assigned ID for the HID device + * index: 0 + * data the HID report for the event + */ +#define ACCESSORY_SEND_HID_EVENT 57 + /* Control request for setting the audio mode. * * requestType: USB_DIR_OUT | USB_TYPE_VENDOR From 97bf61732ffecd5b31977312a3b6f74df7b04991 Mon Sep 17 00:00:00 2001 From: Colin Cross Date: Thu, 7 Nov 2013 13:08:39 -0800 Subject: [PATCH 0237/1103] ANDROID: usb: gadget: f_accessory: move userspace interface to uapi Move the entire contents of linux/usb/f_accessory.h header to uapi, it only contains a userspace interface. Change-Id: Ieb5547da449588ae554988a201c0e6b4e3afc531 Signed-off-by: Colin Cross --- include/linux/usb/f_accessory.h | 125 +---------------------- include/uapi/linux/usb/f_accessory.h | 146 +++++++++++++++++++++++++++ 2 files changed, 147 insertions(+), 124 deletions(-) create mode 100644 include/uapi/linux/usb/f_accessory.h diff --git a/include/linux/usb/f_accessory.h b/include/linux/usb/f_accessory.h index 61ebe0aabc5b..ebe3c4d59309 100644 --- a/include/linux/usb/f_accessory.h +++ b/include/linux/usb/f_accessory.h @@ -18,129 +18,6 @@ #ifndef __LINUX_USB_F_ACCESSORY_H #define __LINUX_USB_F_ACCESSORY_H -/* Use Google Vendor ID when in accessory mode */ -#define USB_ACCESSORY_VENDOR_ID 0x18D1 - - -/* Product ID to use when in accessory mode */ -#define USB_ACCESSORY_PRODUCT_ID 0x2D00 - -/* Product ID to use when in accessory mode and adb is enabled */ -#define USB_ACCESSORY_ADB_PRODUCT_ID 0x2D01 - -/* Indexes for strings sent by the host via ACCESSORY_SEND_STRING */ -#define ACCESSORY_STRING_MANUFACTURER 0 -#define ACCESSORY_STRING_MODEL 1 -#define ACCESSORY_STRING_DESCRIPTION 2 -#define ACCESSORY_STRING_VERSION 3 -#define ACCESSORY_STRING_URI 4 -#define ACCESSORY_STRING_SERIAL 5 - -/* Control request for retrieving device's protocol version - * - * requestType: USB_DIR_IN | USB_TYPE_VENDOR - * request: ACCESSORY_GET_PROTOCOL - * value: 0 - * index: 0 - * data version number (16 bits little endian) - * 1 for original accessory support - * 2 adds HID and device to host audio support - */ -#define ACCESSORY_GET_PROTOCOL 51 - -/* Control request for host to send a string to the device - * - * requestType: USB_DIR_OUT | USB_TYPE_VENDOR - * request: ACCESSORY_SEND_STRING - * value: 0 - * index: string ID - * data zero terminated UTF8 string - * - * The device can later retrieve these strings via the - * ACCESSORY_GET_STRING_* ioctls - */ -#define ACCESSORY_SEND_STRING 52 - -/* Control request for starting device in accessory mode. - * The host sends this after setting all its strings to the device. - * - * requestType: USB_DIR_OUT | USB_TYPE_VENDOR - * request: ACCESSORY_START - * value: 0 - * index: 0 - * data none - */ -#define ACCESSORY_START 53 - -/* Control request for registering a HID device. - * Upon registering, a unique ID is sent by the accessory in the - * value parameter. This ID will be used for future commands for - * the device - * - * requestType: USB_DIR_OUT | USB_TYPE_VENDOR - * request: ACCESSORY_REGISTER_HID_DEVICE - * value: Accessory assigned ID for the HID device - * index: total length of the HID report descriptor - * data none - */ -#define ACCESSORY_REGISTER_HID 54 - -/* Control request for unregistering a HID device. - * - * requestType: USB_DIR_OUT | USB_TYPE_VENDOR - * request: ACCESSORY_REGISTER_HID - * value: Accessory assigned ID for the HID device - * index: 0 - * data none - */ -#define ACCESSORY_UNREGISTER_HID 55 - -/* Control request for sending the HID report descriptor. - * If the HID descriptor is longer than the endpoint zero max packet size, - * the descriptor will be sent in multiple ACCESSORY_SET_HID_REPORT_DESC - * commands. The data for the descriptor must be sent sequentially - * if multiple packets are needed. - * - * requestType: USB_DIR_OUT | USB_TYPE_VENDOR - * request: ACCESSORY_SET_HID_REPORT_DESC - * value: Accessory assigned ID for the HID device - * index: offset of data in descriptor - * (needed when HID descriptor is too big for one packet) - * data the HID report descriptor - */ -#define ACCESSORY_SET_HID_REPORT_DESC 56 - -/* Control request for sending HID events. - * - * requestType: USB_DIR_OUT | USB_TYPE_VENDOR - * request: ACCESSORY_SEND_HID_EVENT - * value: Accessory assigned ID for the HID device - * index: 0 - * data the HID report for the event - */ -#define ACCESSORY_SEND_HID_EVENT 57 - -/* Control request for setting the audio mode. - * - * requestType: USB_DIR_OUT | USB_TYPE_VENDOR - * request: ACCESSORY_SET_AUDIO_MODE - * value: 0 - no audio - * 1 - device to host, 44100 16-bit stereo PCM - * index: 0 - * data none - */ -#define ACCESSORY_SET_AUDIO_MODE 58 - -/* ioctls for retrieving strings set by the host */ -#define ACCESSORY_GET_STRING_MANUFACTURER _IOW('M', 1, char[256]) -#define ACCESSORY_GET_STRING_MODEL _IOW('M', 2, char[256]) -#define ACCESSORY_GET_STRING_DESCRIPTION _IOW('M', 3, char[256]) -#define ACCESSORY_GET_STRING_VERSION _IOW('M', 4, char[256]) -#define ACCESSORY_GET_STRING_URI _IOW('M', 5, char[256]) -#define ACCESSORY_GET_STRING_SERIAL _IOW('M', 6, char[256]) -/* returns 1 if there is a start request pending */ -#define ACCESSORY_IS_START_REQUESTED _IO('M', 7) -/* returns audio mode (set via the ACCESSORY_SET_AUDIO_MODE control request) */ -#define ACCESSORY_GET_AUDIO_MODE _IO('M', 8) +#include #endif /* __LINUX_USB_F_ACCESSORY_H */ diff --git a/include/uapi/linux/usb/f_accessory.h b/include/uapi/linux/usb/f_accessory.h new file mode 100644 index 000000000000..0baeb7d0d74c --- /dev/null +++ b/include/uapi/linux/usb/f_accessory.h @@ -0,0 +1,146 @@ +/* + * Gadget Function Driver for Android USB accessories + * + * Copyright (C) 2011 Google, Inc. + * Author: Mike Lockwood + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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. + * + */ + +#ifndef _UAPI_LINUX_USB_F_ACCESSORY_H +#define _UAPI_LINUX_USB_F_ACCESSORY_H + +/* Use Google Vendor ID when in accessory mode */ +#define USB_ACCESSORY_VENDOR_ID 0x18D1 + + +/* Product ID to use when in accessory mode */ +#define USB_ACCESSORY_PRODUCT_ID 0x2D00 + +/* Product ID to use when in accessory mode and adb is enabled */ +#define USB_ACCESSORY_ADB_PRODUCT_ID 0x2D01 + +/* Indexes for strings sent by the host via ACCESSORY_SEND_STRING */ +#define ACCESSORY_STRING_MANUFACTURER 0 +#define ACCESSORY_STRING_MODEL 1 +#define ACCESSORY_STRING_DESCRIPTION 2 +#define ACCESSORY_STRING_VERSION 3 +#define ACCESSORY_STRING_URI 4 +#define ACCESSORY_STRING_SERIAL 5 + +/* Control request for retrieving device's protocol version + * + * requestType: USB_DIR_IN | USB_TYPE_VENDOR + * request: ACCESSORY_GET_PROTOCOL + * value: 0 + * index: 0 + * data version number (16 bits little endian) + * 1 for original accessory support + * 2 adds HID and device to host audio support + */ +#define ACCESSORY_GET_PROTOCOL 51 + +/* Control request for host to send a string to the device + * + * requestType: USB_DIR_OUT | USB_TYPE_VENDOR + * request: ACCESSORY_SEND_STRING + * value: 0 + * index: string ID + * data zero terminated UTF8 string + * + * The device can later retrieve these strings via the + * ACCESSORY_GET_STRING_* ioctls + */ +#define ACCESSORY_SEND_STRING 52 + +/* Control request for starting device in accessory mode. + * The host sends this after setting all its strings to the device. + * + * requestType: USB_DIR_OUT | USB_TYPE_VENDOR + * request: ACCESSORY_START + * value: 0 + * index: 0 + * data none + */ +#define ACCESSORY_START 53 + +/* Control request for registering a HID device. + * Upon registering, a unique ID is sent by the accessory in the + * value parameter. This ID will be used for future commands for + * the device + * + * requestType: USB_DIR_OUT | USB_TYPE_VENDOR + * request: ACCESSORY_REGISTER_HID_DEVICE + * value: Accessory assigned ID for the HID device + * index: total length of the HID report descriptor + * data none + */ +#define ACCESSORY_REGISTER_HID 54 + +/* Control request for unregistering a HID device. + * + * requestType: USB_DIR_OUT | USB_TYPE_VENDOR + * request: ACCESSORY_REGISTER_HID + * value: Accessory assigned ID for the HID device + * index: 0 + * data none + */ +#define ACCESSORY_UNREGISTER_HID 55 + +/* Control request for sending the HID report descriptor. + * If the HID descriptor is longer than the endpoint zero max packet size, + * the descriptor will be sent in multiple ACCESSORY_SET_HID_REPORT_DESC + * commands. The data for the descriptor must be sent sequentially + * if multiple packets are needed. + * + * requestType: USB_DIR_OUT | USB_TYPE_VENDOR + * request: ACCESSORY_SET_HID_REPORT_DESC + * value: Accessory assigned ID for the HID device + * index: offset of data in descriptor + * (needed when HID descriptor is too big for one packet) + * data the HID report descriptor + */ +#define ACCESSORY_SET_HID_REPORT_DESC 56 + +/* Control request for sending HID events. + * + * requestType: USB_DIR_OUT | USB_TYPE_VENDOR + * request: ACCESSORY_SEND_HID_EVENT + * value: Accessory assigned ID for the HID device + * index: 0 + * data the HID report for the event + */ +#define ACCESSORY_SEND_HID_EVENT 57 + +/* Control request for setting the audio mode. + * + * requestType: USB_DIR_OUT | USB_TYPE_VENDOR + * request: ACCESSORY_SET_AUDIO_MODE + * value: 0 - no audio + * 1 - device to host, 44100 16-bit stereo PCM + * index: 0 + * data none + */ +#define ACCESSORY_SET_AUDIO_MODE 58 + +/* ioctls for retrieving strings set by the host */ +#define ACCESSORY_GET_STRING_MANUFACTURER _IOW('M', 1, char[256]) +#define ACCESSORY_GET_STRING_MODEL _IOW('M', 2, char[256]) +#define ACCESSORY_GET_STRING_DESCRIPTION _IOW('M', 3, char[256]) +#define ACCESSORY_GET_STRING_VERSION _IOW('M', 4, char[256]) +#define ACCESSORY_GET_STRING_URI _IOW('M', 5, char[256]) +#define ACCESSORY_GET_STRING_SERIAL _IOW('M', 6, char[256]) +/* returns 1 if there is a start request pending */ +#define ACCESSORY_IS_START_REQUESTED _IO('M', 7) +/* returns audio mode (set via the ACCESSORY_SET_AUDIO_MODE control request) */ +#define ACCESSORY_GET_AUDIO_MODE _IO('M', 8) + +#endif /* _UAPI_LINUX_USB_F_ACCESSORY_H */ From 37da4f5219b94a13f0daca758269a6d605930b09 Mon Sep 17 00:00:00 2001 From: Anson Jacob Date: Mon, 23 Jun 2014 19:07:44 +0800 Subject: [PATCH 0238/1103] ANDROID: usb: gadget: f_accessory: Enabled Zero Length Packet (ZLP) for acc_write Accessory connected to Android Device requires Zero Length Packet (ZLP) to be written when data transferred out from the Android device are multiples of wMaxPacketSize (64bytes (Full-Speed) / 512bytes (High-Speed)) to end the transfer. Change-Id: Ib2c2c0ab98ef9afa10e74a720142deca5c0ed476 Signed-off-by: Anson Jacob --- drivers/usb/gadget/f_accessory.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/drivers/usb/gadget/f_accessory.c b/drivers/usb/gadget/f_accessory.c index 73c6b2072d73..f8490c034107 100644 --- a/drivers/usb/gadget/f_accessory.c +++ b/drivers/usb/gadget/f_accessory.c @@ -640,10 +640,17 @@ static ssize_t acc_write(struct file *fp, const char __user *buf, break; } - if (count > BULK_BUFFER_SIZE) + if (count > BULK_BUFFER_SIZE) { xfer = BULK_BUFFER_SIZE; - else + /* ZLP, They will be more TX requests so not yet. */ + req->zero = 0; + } else { xfer = count; + /* If the data length is a multple of the + * maxpacket size then send a zero length packet(ZLP). + */ + req->zero = ((xfer % dev->ep_in->maxpacket) == 0); + } if (copy_from_user(req->buf, buf, xfer)) { r = -EFAULT; break; From 1a8695f9477fc25f6662397ebc817c782c7ac24f Mon Sep 17 00:00:00 2001 From: Amit Pundir Date: Fri, 16 Jan 2015 05:41:10 +0530 Subject: [PATCH 0239/1103] ANDROID: usb: gadget: f_accessory: check for accessory device before disconnecting HIDs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit While disabling ConfigFS Android gadget, android_disconnect() calls kill_all_hid_devices(), if CONFIG_USB_CONFIGFS_F_ACC is enabled, to free the registered HIDs without checking whether the USB accessory device really exist or not. If USB accessory device doesn't exist then we run into following kernel panic: ----8<---- [  136.724761] Unable to handle kernel NULL pointer dereference at virtual address 00000064 [  136.724809] pgd = c0204000 [  136.731924] [00000064] *pgd=00000000 [  136.737830] Internal error: Oops: 5 [#1] SMP ARM [  136.738108] CPU: 0 PID: 0 Comm: swapper/0 Not tainted 3.18.0-rc4-00400-gf75300e-dirty #76 [  136.742788] task: c0fb19d8 ti: c0fa4000 task.ti: c0fa4000 [  136.750890] PC is at _raw_spin_lock_irqsave+0x24/0x60 [  136.756246] LR is at kill_all_hid_devices+0x24/0x114 ---->8---- This patch adds a test to check if USB Accessory device exists before freeing HIDs. Change-Id: Ie229feaf0de3f4f7a151fcaa9a994e34e15ff73b Signed-off-by: Amit Pundir --- drivers/usb/gadget/f_accessory.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/usb/gadget/f_accessory.c b/drivers/usb/gadget/f_accessory.c index f8490c034107..9ffe017bf1cf 100644 --- a/drivers/usb/gadget/f_accessory.c +++ b/drivers/usb/gadget/f_accessory.c @@ -929,6 +929,10 @@ kill_all_hid_devices(struct acc_dev *dev) struct list_head *entry, *temp; unsigned long flags; + /* do nothing if usb accessory device doesn't exist */ + if (!dev) + return; + spin_lock_irqsave(&dev->lock, flags); list_for_each_safe(entry, temp, &dev->hid_list) { hid = list_entry(entry, struct acc_hid_dev, list); From 8e28a0df9bb965a43c6fa2b77df655a0f2d345dd Mon Sep 17 00:00:00 2001 From: Mike Lockwood Date: Fri, 11 May 2012 09:01:08 -0700 Subject: [PATCH 0240/1103] ANDROID: usb: gadget: f_audio_source: New gadget driver for audio output This driver presents a standard USB audio class interface to the host and an ALSA PCM device to userspace Change-Id: If16b14a5ff27045f9cb2daaf1ae9195c5eeab7d0 Signed-off-by: Mike Lockwood [AmitP: Folded following android-4.9 commit changes into this patch Parts of e27543931009 ("ANDROID: usb: gadget: Fixes and hacks to make android usb gadget compile on 3.8") i6d9285e2574a ("ANDROID: usb: gadget: f_audio_source:replace deprecated API")] Signed-off-by: Amit Pundir --- drivers/usb/gadget/f_audio_source.c | 828 ++++++++++++++++++++++++++++ 1 file changed, 828 insertions(+) create mode 100644 drivers/usb/gadget/f_audio_source.c diff --git a/drivers/usb/gadget/f_audio_source.c b/drivers/usb/gadget/f_audio_source.c new file mode 100644 index 000000000000..54f1699613bf --- /dev/null +++ b/drivers/usb/gadget/f_audio_source.c @@ -0,0 +1,828 @@ +/* + * Gadget Function Driver for USB audio source device + * + * Copyright (C) 2012 Google, Inc. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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. + * + */ + +#include +#include +#include +#include +#include +#include + +#define SAMPLE_RATE 44100 +#define FRAMES_PER_MSEC (SAMPLE_RATE / 1000) + +#define IN_EP_MAX_PACKET_SIZE 384 + +/* Number of requests to allocate */ +#define IN_EP_REQ_COUNT 4 + +#define AUDIO_AC_INTERFACE 0 +#define AUDIO_AS_INTERFACE 1 +#define AUDIO_NUM_INTERFACES 2 + +/* B.3.1 Standard AC Interface Descriptor */ +static struct usb_interface_descriptor ac_interface_desc = { + .bLength = USB_DT_INTERFACE_SIZE, + .bDescriptorType = USB_DT_INTERFACE, + .bNumEndpoints = 0, + .bInterfaceClass = USB_CLASS_AUDIO, + .bInterfaceSubClass = USB_SUBCLASS_AUDIOCONTROL, +}; + +DECLARE_UAC_AC_HEADER_DESCRIPTOR(2); + +#define UAC_DT_AC_HEADER_LENGTH UAC_DT_AC_HEADER_SIZE(AUDIO_NUM_INTERFACES) +/* 1 input terminal, 1 output terminal and 1 feature unit */ +#define UAC_DT_TOTAL_LENGTH (UAC_DT_AC_HEADER_LENGTH \ + + UAC_DT_INPUT_TERMINAL_SIZE + UAC_DT_OUTPUT_TERMINAL_SIZE \ + + UAC_DT_FEATURE_UNIT_SIZE(0)) +/* B.3.2 Class-Specific AC Interface Descriptor */ +static struct uac1_ac_header_descriptor_2 ac_header_desc = { + .bLength = UAC_DT_AC_HEADER_LENGTH, + .bDescriptorType = USB_DT_CS_INTERFACE, + .bDescriptorSubtype = UAC_HEADER, + .bcdADC = __constant_cpu_to_le16(0x0100), + .wTotalLength = __constant_cpu_to_le16(UAC_DT_TOTAL_LENGTH), + .bInCollection = AUDIO_NUM_INTERFACES, + .baInterfaceNr = { + [0] = AUDIO_AC_INTERFACE, + [1] = AUDIO_AS_INTERFACE, + } +}; + +#define INPUT_TERMINAL_ID 1 +static struct uac_input_terminal_descriptor input_terminal_desc = { + .bLength = UAC_DT_INPUT_TERMINAL_SIZE, + .bDescriptorType = USB_DT_CS_INTERFACE, + .bDescriptorSubtype = UAC_INPUT_TERMINAL, + .bTerminalID = INPUT_TERMINAL_ID, + .wTerminalType = UAC_INPUT_TERMINAL_MICROPHONE, + .bAssocTerminal = 0, + .wChannelConfig = 0x3, +}; + +DECLARE_UAC_FEATURE_UNIT_DESCRIPTOR(0); + +#define FEATURE_UNIT_ID 2 +static struct uac_feature_unit_descriptor_0 feature_unit_desc = { + .bLength = UAC_DT_FEATURE_UNIT_SIZE(0), + .bDescriptorType = USB_DT_CS_INTERFACE, + .bDescriptorSubtype = UAC_FEATURE_UNIT, + .bUnitID = FEATURE_UNIT_ID, + .bSourceID = INPUT_TERMINAL_ID, + .bControlSize = 2, +}; + +#define OUTPUT_TERMINAL_ID 3 +static struct uac1_output_terminal_descriptor output_terminal_desc = { + .bLength = UAC_DT_OUTPUT_TERMINAL_SIZE, + .bDescriptorType = USB_DT_CS_INTERFACE, + .bDescriptorSubtype = UAC_OUTPUT_TERMINAL, + .bTerminalID = OUTPUT_TERMINAL_ID, + .wTerminalType = UAC_TERMINAL_STREAMING, + .bAssocTerminal = FEATURE_UNIT_ID, + .bSourceID = FEATURE_UNIT_ID, +}; + +/* B.4.1 Standard AS Interface Descriptor */ +static struct usb_interface_descriptor as_interface_alt_0_desc = { + .bLength = USB_DT_INTERFACE_SIZE, + .bDescriptorType = USB_DT_INTERFACE, + .bAlternateSetting = 0, + .bNumEndpoints = 0, + .bInterfaceClass = USB_CLASS_AUDIO, + .bInterfaceSubClass = USB_SUBCLASS_AUDIOSTREAMING, +}; + +static struct usb_interface_descriptor as_interface_alt_1_desc = { + .bLength = USB_DT_INTERFACE_SIZE, + .bDescriptorType = USB_DT_INTERFACE, + .bAlternateSetting = 1, + .bNumEndpoints = 1, + .bInterfaceClass = USB_CLASS_AUDIO, + .bInterfaceSubClass = USB_SUBCLASS_AUDIOSTREAMING, +}; + +/* B.4.2 Class-Specific AS Interface Descriptor */ +static struct uac1_as_header_descriptor as_header_desc = { + .bLength = UAC_DT_AS_HEADER_SIZE, + .bDescriptorType = USB_DT_CS_INTERFACE, + .bDescriptorSubtype = UAC_AS_GENERAL, + .bTerminalLink = INPUT_TERMINAL_ID, + .bDelay = 1, + .wFormatTag = UAC_FORMAT_TYPE_I_PCM, +}; + +DECLARE_UAC_FORMAT_TYPE_I_DISCRETE_DESC(1); + +static struct uac_format_type_i_discrete_descriptor_1 as_type_i_desc = { + .bLength = UAC_FORMAT_TYPE_I_DISCRETE_DESC_SIZE(1), + .bDescriptorType = USB_DT_CS_INTERFACE, + .bDescriptorSubtype = UAC_FORMAT_TYPE, + .bFormatType = UAC_FORMAT_TYPE_I, + .bSubframeSize = 2, + .bBitResolution = 16, + .bSamFreqType = 1, +}; + +/* Standard ISO IN Endpoint Descriptor for highspeed */ +static struct usb_endpoint_descriptor hs_as_in_ep_desc = { + .bLength = USB_DT_ENDPOINT_AUDIO_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = USB_DIR_IN, + .bmAttributes = USB_ENDPOINT_SYNC_SYNC + | USB_ENDPOINT_XFER_ISOC, + .wMaxPacketSize = __constant_cpu_to_le16(IN_EP_MAX_PACKET_SIZE), + .bInterval = 4, /* poll 1 per millisecond */ +}; + +/* Standard ISO IN Endpoint Descriptor for highspeed */ +static struct usb_endpoint_descriptor fs_as_in_ep_desc = { + .bLength = USB_DT_ENDPOINT_AUDIO_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = USB_DIR_IN, + .bmAttributes = USB_ENDPOINT_SYNC_SYNC + | USB_ENDPOINT_XFER_ISOC, + .wMaxPacketSize = __constant_cpu_to_le16(IN_EP_MAX_PACKET_SIZE), + .bInterval = 1, /* poll 1 per millisecond */ +}; + +/* Class-specific AS ISO OUT Endpoint Descriptor */ +static struct uac_iso_endpoint_descriptor as_iso_in_desc = { + .bLength = UAC_ISO_ENDPOINT_DESC_SIZE, + .bDescriptorType = USB_DT_CS_ENDPOINT, + .bDescriptorSubtype = UAC_EP_GENERAL, + .bmAttributes = 1, + .bLockDelayUnits = 1, + .wLockDelay = __constant_cpu_to_le16(1), +}; + +static struct usb_descriptor_header *hs_audio_desc[] = { + (struct usb_descriptor_header *)&ac_interface_desc, + (struct usb_descriptor_header *)&ac_header_desc, + + (struct usb_descriptor_header *)&input_terminal_desc, + (struct usb_descriptor_header *)&output_terminal_desc, + (struct usb_descriptor_header *)&feature_unit_desc, + + (struct usb_descriptor_header *)&as_interface_alt_0_desc, + (struct usb_descriptor_header *)&as_interface_alt_1_desc, + (struct usb_descriptor_header *)&as_header_desc, + + (struct usb_descriptor_header *)&as_type_i_desc, + + (struct usb_descriptor_header *)&hs_as_in_ep_desc, + (struct usb_descriptor_header *)&as_iso_in_desc, + NULL, +}; + +static struct usb_descriptor_header *fs_audio_desc[] = { + (struct usb_descriptor_header *)&ac_interface_desc, + (struct usb_descriptor_header *)&ac_header_desc, + + (struct usb_descriptor_header *)&input_terminal_desc, + (struct usb_descriptor_header *)&output_terminal_desc, + (struct usb_descriptor_header *)&feature_unit_desc, + + (struct usb_descriptor_header *)&as_interface_alt_0_desc, + (struct usb_descriptor_header *)&as_interface_alt_1_desc, + (struct usb_descriptor_header *)&as_header_desc, + + (struct usb_descriptor_header *)&as_type_i_desc, + + (struct usb_descriptor_header *)&fs_as_in_ep_desc, + (struct usb_descriptor_header *)&as_iso_in_desc, + NULL, +}; + +static struct snd_pcm_hardware audio_hw_info = { + .info = SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_BATCH | + SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_BLOCK_TRANSFER, + + .formats = SNDRV_PCM_FMTBIT_S16_LE, + .channels_min = 2, + .channels_max = 2, + .rate_min = SAMPLE_RATE, + .rate_max = SAMPLE_RATE, + + .buffer_bytes_max = 1024 * 1024, + .period_bytes_min = 64, + .period_bytes_max = 512 * 1024, + .periods_min = 2, + .periods_max = 1024, +}; + +/*-------------------------------------------------------------------------*/ + +struct audio_source_config { + int card; + int device; +}; + +struct audio_dev { + struct usb_function func; + struct snd_card *card; + struct snd_pcm *pcm; + struct snd_pcm_substream *substream; + + struct list_head idle_reqs; + struct usb_ep *in_ep; + + spinlock_t lock; + + /* beginning, end and current position in our buffer */ + void *buffer_start; + void *buffer_end; + void *buffer_pos; + + /* byte size of a "period" */ + unsigned int period; + /* bytes sent since last call to snd_pcm_period_elapsed */ + unsigned int period_offset; + /* time we started playing */ + ktime_t start_time; + /* number of frames sent since start_time */ + s64 frames_sent; +}; + +static inline struct audio_dev *func_to_audio(struct usb_function *f) +{ + return container_of(f, struct audio_dev, func); +} + +/*-------------------------------------------------------------------------*/ + +static struct usb_request *audio_request_new(struct usb_ep *ep, int buffer_size) +{ + struct usb_request *req = usb_ep_alloc_request(ep, GFP_KERNEL); + if (!req) + return NULL; + + req->buf = kmalloc(buffer_size, GFP_KERNEL); + if (!req->buf) { + usb_ep_free_request(ep, req); + return NULL; + } + req->length = buffer_size; + return req; +} + +static void audio_request_free(struct usb_request *req, struct usb_ep *ep) +{ + if (req) { + kfree(req->buf); + usb_ep_free_request(ep, req); + } +} + +static void audio_req_put(struct audio_dev *audio, struct usb_request *req) +{ + unsigned long flags; + + spin_lock_irqsave(&audio->lock, flags); + list_add_tail(&req->list, &audio->idle_reqs); + spin_unlock_irqrestore(&audio->lock, flags); +} + +static struct usb_request *audio_req_get(struct audio_dev *audio) +{ + unsigned long flags; + struct usb_request *req; + + spin_lock_irqsave(&audio->lock, flags); + if (list_empty(&audio->idle_reqs)) { + req = 0; + } else { + req = list_first_entry(&audio->idle_reqs, struct usb_request, + list); + list_del(&req->list); + } + spin_unlock_irqrestore(&audio->lock, flags); + return req; +} + +/* send the appropriate number of packets to match our bitrate */ +static void audio_send(struct audio_dev *audio) +{ + struct snd_pcm_runtime *runtime; + struct usb_request *req; + int length, length1, length2, ret; + s64 msecs; + s64 frames; + ktime_t now; + + /* audio->substream will be null if we have been closed */ + if (!audio->substream) + return; + /* audio->buffer_pos will be null if we have been stopped */ + if (!audio->buffer_pos) + return; + + runtime = audio->substream->runtime; + + /* compute number of frames to send */ + now = ktime_get(); + msecs = ktime_to_ns(now) - ktime_to_ns(audio->start_time); + do_div(msecs, 1000000); + frames = msecs * SAMPLE_RATE; + do_div(frames, 1000); + + /* Readjust our frames_sent if we fall too far behind. + * If we get too far behind it is better to drop some frames than + * to keep sending data too fast in an attempt to catch up. + */ + if (frames - audio->frames_sent > 10 * FRAMES_PER_MSEC) + audio->frames_sent = frames - FRAMES_PER_MSEC; + + frames -= audio->frames_sent; + + /* We need to send something to keep the pipeline going */ + if (frames <= 0) + frames = FRAMES_PER_MSEC; + + while (frames > 0) { + req = audio_req_get(audio); + if (!req) + break; + + length = frames_to_bytes(runtime, frames); + if (length > IN_EP_MAX_PACKET_SIZE) + length = IN_EP_MAX_PACKET_SIZE; + + if (audio->buffer_pos + length > audio->buffer_end) + length1 = audio->buffer_end - audio->buffer_pos; + else + length1 = length; + memcpy(req->buf, audio->buffer_pos, length1); + if (length1 < length) { + /* Wrap around and copy remaining length + * at beginning of buffer. + */ + length2 = length - length1; + memcpy(req->buf + length1, audio->buffer_start, + length2); + audio->buffer_pos = audio->buffer_start + length2; + } else { + audio->buffer_pos += length1; + if (audio->buffer_pos >= audio->buffer_end) + audio->buffer_pos = audio->buffer_start; + } + + req->length = length; + ret = usb_ep_queue(audio->in_ep, req, GFP_ATOMIC); + if (ret < 0) { + pr_err("usb_ep_queue failed ret: %d\n", ret); + audio_req_put(audio, req); + break; + } + + frames -= bytes_to_frames(runtime, length); + audio->frames_sent += bytes_to_frames(runtime, length); + } +} + +static void audio_control_complete(struct usb_ep *ep, struct usb_request *req) +{ + /* nothing to do here */ +} + +static void audio_data_complete(struct usb_ep *ep, struct usb_request *req) +{ + struct audio_dev *audio = req->context; + + pr_debug("audio_data_complete req->status %d req->actual %d\n", + req->status, req->actual); + + audio_req_put(audio, req); + + if (!audio->buffer_start || req->status) + return; + + audio->period_offset += req->actual; + if (audio->period_offset >= audio->period) { + snd_pcm_period_elapsed(audio->substream); + audio->period_offset = 0; + } + audio_send(audio); +} + +static int audio_set_endpoint_req(struct usb_function *f, + const struct usb_ctrlrequest *ctrl) +{ + int value = -EOPNOTSUPP; + u16 ep = le16_to_cpu(ctrl->wIndex); + u16 len = le16_to_cpu(ctrl->wLength); + u16 w_value = le16_to_cpu(ctrl->wValue); + + pr_debug("bRequest 0x%x, w_value 0x%04x, len %d, endpoint %d\n", + ctrl->bRequest, w_value, len, ep); + + switch (ctrl->bRequest) { + case UAC_SET_CUR: + case UAC_SET_MIN: + case UAC_SET_MAX: + case UAC_SET_RES: + value = len; + break; + default: + break; + } + + return value; +} + +static int audio_get_endpoint_req(struct usb_function *f, + const struct usb_ctrlrequest *ctrl) +{ + struct usb_composite_dev *cdev = f->config->cdev; + int value = -EOPNOTSUPP; + u8 ep = ((le16_to_cpu(ctrl->wIndex) >> 8) & 0xFF); + u16 len = le16_to_cpu(ctrl->wLength); + u16 w_value = le16_to_cpu(ctrl->wValue); + u8 *buf = cdev->req->buf; + + pr_debug("bRequest 0x%x, w_value 0x%04x, len %d, endpoint %d\n", + ctrl->bRequest, w_value, len, ep); + + if (w_value == UAC_EP_CS_ATTR_SAMPLE_RATE << 8) { + switch (ctrl->bRequest) { + case UAC_GET_CUR: + case UAC_GET_MIN: + case UAC_GET_MAX: + case UAC_GET_RES: + /* return our sample rate */ + buf[0] = (u8)SAMPLE_RATE; + buf[1] = (u8)(SAMPLE_RATE >> 8); + buf[2] = (u8)(SAMPLE_RATE >> 16); + value = 3; + break; + default: + break; + } + } + + return value; +} + +static int +audio_setup(struct usb_function *f, const struct usb_ctrlrequest *ctrl) +{ + struct usb_composite_dev *cdev = f->config->cdev; + struct usb_request *req = cdev->req; + int value = -EOPNOTSUPP; + u16 w_index = le16_to_cpu(ctrl->wIndex); + u16 w_value = le16_to_cpu(ctrl->wValue); + u16 w_length = le16_to_cpu(ctrl->wLength); + + /* composite driver infrastructure handles everything; interface + * activation uses set_alt(). + */ + switch (ctrl->bRequestType) { + case USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_ENDPOINT: + value = audio_set_endpoint_req(f, ctrl); + break; + + case USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_ENDPOINT: + value = audio_get_endpoint_req(f, ctrl); + break; + } + + /* respond with data transfer or status phase? */ + if (value >= 0) { + pr_debug("audio req%02x.%02x v%04x i%04x l%d\n", + ctrl->bRequestType, ctrl->bRequest, + w_value, w_index, w_length); + req->zero = 0; + req->length = value; + req->complete = audio_control_complete; + value = usb_ep_queue(cdev->gadget->ep0, req, GFP_ATOMIC); + if (value < 0) + pr_err("audio response on err %d\n", value); + } + + /* device either stalls (value < 0) or reports success */ + return value; +} + +static int audio_set_alt(struct usb_function *f, unsigned intf, unsigned alt) +{ + struct audio_dev *audio = func_to_audio(f); + struct usb_composite_dev *cdev = f->config->cdev; + int ret; + + pr_debug("audio_set_alt intf %d, alt %d\n", intf, alt); + + ret = config_ep_by_speed(cdev->gadget, f, audio->in_ep); + if (ret) + return ret; + + usb_ep_enable(audio->in_ep); + return 0; +} + +static void audio_disable(struct usb_function *f) +{ + struct audio_dev *audio = func_to_audio(f); + + pr_debug("audio_disable\n"); + usb_ep_disable(audio->in_ep); +} + +/*-------------------------------------------------------------------------*/ + +static void audio_build_desc(struct audio_dev *audio) +{ + u8 *sam_freq; + int rate; + + /* Set channel numbers */ + input_terminal_desc.bNrChannels = 2; + as_type_i_desc.bNrChannels = 2; + + /* Set sample rates */ + rate = SAMPLE_RATE; + sam_freq = as_type_i_desc.tSamFreq[0]; + memcpy(sam_freq, &rate, 3); +} + +/* audio function driver setup/binding */ +static int +audio_bind(struct usb_configuration *c, struct usb_function *f) +{ + struct usb_composite_dev *cdev = c->cdev; + struct audio_dev *audio = func_to_audio(f); + int status; + struct usb_ep *ep; + struct usb_request *req; + int i; + + audio_build_desc(audio); + + /* allocate instance-specific interface IDs, and patch descriptors */ + status = usb_interface_id(c, f); + if (status < 0) + goto fail; + ac_interface_desc.bInterfaceNumber = status; + + status = usb_interface_id(c, f); + if (status < 0) + goto fail; + as_interface_alt_0_desc.bInterfaceNumber = status; + as_interface_alt_1_desc.bInterfaceNumber = status; + + status = -ENODEV; + + /* allocate our endpoint */ + ep = usb_ep_autoconfig(cdev->gadget, &fs_as_in_ep_desc); + if (!ep) + goto fail; + audio->in_ep = ep; + ep->driver_data = audio; /* claim */ + + if (gadget_is_dualspeed(c->cdev->gadget)) + hs_as_in_ep_desc.bEndpointAddress = + fs_as_in_ep_desc.bEndpointAddress; + + f->fs_descriptors = fs_audio_desc; + f->hs_descriptors = hs_audio_desc; + + for (i = 0, status = 0; i < IN_EP_REQ_COUNT && status == 0; i++) { + req = audio_request_new(ep, IN_EP_MAX_PACKET_SIZE); + if (req) { + req->context = audio; + req->complete = audio_data_complete; + audio_req_put(audio, req); + } else + status = -ENOMEM; + } + +fail: + return status; +} + +static void +audio_unbind(struct usb_configuration *c, struct usb_function *f) +{ + struct audio_dev *audio = func_to_audio(f); + struct usb_request *req; + + while ((req = audio_req_get(audio))) + audio_request_free(req, audio->in_ep); + + snd_card_free_when_closed(audio->card); + audio->card = NULL; + audio->pcm = NULL; + audio->substream = NULL; + audio->in_ep = NULL; +} + +static void audio_pcm_playback_start(struct audio_dev *audio) +{ + audio->start_time = ktime_get(); + audio->frames_sent = 0; + audio_send(audio); +} + +static void audio_pcm_playback_stop(struct audio_dev *audio) +{ + unsigned long flags; + + spin_lock_irqsave(&audio->lock, flags); + audio->buffer_start = 0; + audio->buffer_end = 0; + audio->buffer_pos = 0; + spin_unlock_irqrestore(&audio->lock, flags); +} + +static int audio_pcm_open(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct audio_dev *audio = substream->private_data; + + runtime->private_data = audio; + runtime->hw = audio_hw_info; + snd_pcm_limit_hw_rates(runtime); + runtime->hw.channels_max = 2; + + audio->substream = substream; + return 0; +} + +static int audio_pcm_close(struct snd_pcm_substream *substream) +{ + struct audio_dev *audio = substream->private_data; + unsigned long flags; + + spin_lock_irqsave(&audio->lock, flags); + audio->substream = NULL; + spin_unlock_irqrestore(&audio->lock, flags); + + return 0; +} + +static int audio_pcm_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + unsigned int channels = params_channels(params); + unsigned int rate = params_rate(params); + + if (rate != SAMPLE_RATE) + return -EINVAL; + if (channels != 2) + return -EINVAL; + + return snd_pcm_lib_alloc_vmalloc_buffer(substream, + params_buffer_bytes(params)); +} + +static int audio_pcm_hw_free(struct snd_pcm_substream *substream) +{ + return snd_pcm_lib_free_vmalloc_buffer(substream); +} + +static int audio_pcm_prepare(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct audio_dev *audio = runtime->private_data; + + audio->period = snd_pcm_lib_period_bytes(substream); + audio->period_offset = 0; + audio->buffer_start = runtime->dma_area; + audio->buffer_end = audio->buffer_start + + snd_pcm_lib_buffer_bytes(substream); + audio->buffer_pos = audio->buffer_start; + + return 0; +} + +static snd_pcm_uframes_t audio_pcm_pointer(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct audio_dev *audio = runtime->private_data; + ssize_t bytes = audio->buffer_pos - audio->buffer_start; + + /* return offset of next frame to fill in our buffer */ + return bytes_to_frames(runtime, bytes); +} + +static int audio_pcm_playback_trigger(struct snd_pcm_substream *substream, + int cmd) +{ + struct audio_dev *audio = substream->runtime->private_data; + int ret = 0; + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_RESUME: + audio_pcm_playback_start(audio); + break; + + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_SUSPEND: + audio_pcm_playback_stop(audio); + break; + + default: + ret = -EINVAL; + } + + return ret; +} + +static struct audio_dev _audio_dev = { + .func = { + .name = "audio_source", + .bind = audio_bind, + .unbind = audio_unbind, + .set_alt = audio_set_alt, + .setup = audio_setup, + .disable = audio_disable, + }, + .lock = __SPIN_LOCK_UNLOCKED(_audio_dev.lock), + .idle_reqs = LIST_HEAD_INIT(_audio_dev.idle_reqs), +}; + +static struct snd_pcm_ops audio_playback_ops = { + .open = audio_pcm_open, + .close = audio_pcm_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = audio_pcm_hw_params, + .hw_free = audio_pcm_hw_free, + .prepare = audio_pcm_prepare, + .trigger = audio_pcm_playback_trigger, + .pointer = audio_pcm_pointer, +}; + +int audio_source_bind_config(struct usb_configuration *c, + struct audio_source_config *config) +{ + struct audio_dev *audio; + struct snd_card *card; + struct snd_pcm *pcm; + int err; + + config->card = -1; + config->device = -1; + + audio = &_audio_dev; + + err = snd_card_new(&c->cdev->gadget->dev, + SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1, + THIS_MODULE, 0, &card); + if (err) + return err; + + err = snd_pcm_new(card, "USB audio source", 0, 1, 0, &pcm); + if (err) + goto pcm_fail; + + pcm->private_data = audio; + pcm->info_flags = 0; + audio->pcm = pcm; + + strlcpy(pcm->name, "USB gadget audio", sizeof(pcm->name)); + + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &audio_playback_ops); + snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, + NULL, 0, 64 * 1024); + + strlcpy(card->driver, "audio_source", sizeof(card->driver)); + strlcpy(card->shortname, card->driver, sizeof(card->shortname)); + strlcpy(card->longname, "USB accessory audio source", + sizeof(card->longname)); + + err = snd_card_register(card); + if (err) + goto register_fail; + + err = usb_add_function(c, &audio->func); + if (err) + goto add_fail; + + config->card = pcm->card->number; + config->device = pcm->device; + audio->card = card; + return 0; + +add_fail: +register_fail: +pcm_fail: + snd_card_free(audio->card); + return err; +} From 05770fbe85f3adaeebe123d2f1d04e8ee0b2b23d Mon Sep 17 00:00:00 2001 From: Anson Jacob Date: Tue, 1 Jul 2014 18:17:20 +0800 Subject: [PATCH 0241/1103] ANDROID: usb: gadget: f_audio_source: change max ISO packet size Re-applying from https://gitorious.org/shr/linux/commit/eb4c9d2db894c3492c0a848581bd4f6790f93d5f Most USB-AUDIO devices are limited to 256 byte for max iso buffer size. If a IN_EP_MAX_PACKET_SIZE is bigger than a USB-AUDIO device's max iso buffer size, it will cause noise. This patch will prevent this case as possibe by reducing packet size. When using 44.1khz, 2ch, 16bit audio data, if max packet size is bigger than 176 bytes, it's no problem. Credits to: Iliyan Malchev Change-Id: Ic2a1c19ea65d5fb42bf12926b51b255b465d7215 Signed-off-by: Anson Jacob --- drivers/usb/gadget/f_audio_source.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/gadget/f_audio_source.c b/drivers/usb/gadget/f_audio_source.c index 54f1699613bf..904b1ef6658f 100644 --- a/drivers/usb/gadget/f_audio_source.c +++ b/drivers/usb/gadget/f_audio_source.c @@ -24,7 +24,7 @@ #define SAMPLE_RATE 44100 #define FRAMES_PER_MSEC (SAMPLE_RATE / 1000) -#define IN_EP_MAX_PACKET_SIZE 384 +#define IN_EP_MAX_PACKET_SIZE 256 /* Number of requests to allocate */ #define IN_EP_REQ_COUNT 4 From efae9ef866548636d6f252c87d681f4a08e5ba47 Mon Sep 17 00:00:00 2001 From: Anson Jacob Date: Mon, 23 Jun 2014 19:14:01 +0800 Subject: [PATCH 0242/1103] ANDROID: usb: gadget: f_audio_source: Fixed USB Audio Class Interface Descriptor Fixed Android Issue #56549. When both Vendor Class and Audio Class are activated for AOA 2.0, the baInterfaceNr of the AudioControl Interface Descriptor points to wrong interface numbers. They should be pointing to Audio Control Device and Audio Streaming interfaces. Replaced baInterfaceNr with the correct value. Change-Id: Iaa083f3d97c1f0fc9481bf87852b2b51278a6351 Signed-off-by: Anson Jacob --- drivers/usb/gadget/f_audio_source.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/usb/gadget/f_audio_source.c b/drivers/usb/gadget/f_audio_source.c index 904b1ef6658f..4b9786b48950 100644 --- a/drivers/usb/gadget/f_audio_source.c +++ b/drivers/usb/gadget/f_audio_source.c @@ -580,12 +580,18 @@ audio_bind(struct usb_configuration *c, struct usb_function *f) goto fail; ac_interface_desc.bInterfaceNumber = status; + /* AUDIO_AC_INTERFACE */ + ac_header_desc.baInterfaceNr[0] = status; + status = usb_interface_id(c, f); if (status < 0) goto fail; as_interface_alt_0_desc.bInterfaceNumber = status; as_interface_alt_1_desc.bInterfaceNumber = status; + /* AUDIO_AS_INTERFACE */ + ac_header_desc.baInterfaceNr[1] = status; + status = -ENODEV; /* allocate our endpoint */ From 9f7bbba7397aa504735c6ae0d8a34fb54bcc98a7 Mon Sep 17 00:00:00 2001 From: Badhri Jagan Sridharan Date: Mon, 15 Dec 2014 16:42:27 -0800 Subject: [PATCH 0243/1103] ANDROID: usb: gadget: configfs: Add usb_function ptr to fi struct Add a pointer to the usb_function inside the usb_function_instance structure to service functions specific setup requests even before the function gets added to the usb_gadget Signed-off-by: Badhri Jagan Sridharan Change-Id: I6f457006f6c5516cc6986ec2acdf5b1ecf259d0c --- include/linux/usb/composite.h | 1 + 1 file changed, 1 insertion(+) diff --git a/include/linux/usb/composite.h b/include/linux/usb/composite.h index 8675e145ea8b..af4396cc4ea8 100644 --- a/include/linux/usb/composite.h +++ b/include/linux/usb/composite.h @@ -587,6 +587,7 @@ struct usb_function_instance { struct config_group group; struct list_head cfs_list; struct usb_function_driver *fd; + struct usb_function *f; int (*set_inst_name)(struct usb_function_instance *inst, const char *name); void (*free_func_inst)(struct usb_function_instance *inst); From 6382b0ca8b25ec5a336a2fdd8aae4c1a4dae21c3 Mon Sep 17 00:00:00 2001 From: Badhri Jagan Sridharan Date: Mon, 15 Dec 2014 10:44:47 -0800 Subject: [PATCH 0244/1103] ANDROID: usb: gadget: configfs: Add Uevent to notify userspace Android userspace UsbDeviceManager relies on the uevents generated by the composition driver to generate user notifications. This CL adds uevents to be generated whenever USB changes its state i.e. connected, disconnected, configured. This CL also intercepts the setup requests from the usb_core anb routes it to the specific usb function if required. Signed-off-by: Badhri Jagan Sridharan Change-Id: Ib3d3a78255a532f7449dac286f776c2966caf8c1 [AmitP: Folded following android-4.9 commit changes into this patch 9214c899f730 ("ANDROID: usb: gadget: configfs: handle gadget reset request for android")] Signed-off-by: Amit Pundir --- drivers/usb/gadget/Kconfig | 8 ++ drivers/usb/gadget/configfs.c | 155 +++++++++++++++++++++++++++++++++- 2 files changed, 160 insertions(+), 3 deletions(-) diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig index 31cce7805eb2..d92762d134b4 100644 --- a/drivers/usb/gadget/Kconfig +++ b/drivers/usb/gadget/Kconfig @@ -368,6 +368,14 @@ config USB_CONFIGFS_F_FS implemented in kernel space (for instance Ethernet, serial or mass storage) and other are implemented in user space. +config USB_CONFIGFS_UEVENT + bool "Uevent notification of Gadget state" + depends on USB_CONFIGFS + help + Enable uevent notifications to userspace when the gadget + state changes. The gadget can be in any of the following + three states: "CONNECTED/DISCONNECTED/CONFIGURED" + config USB_CONFIGFS_F_UAC1 bool "Audio Class 1.0" depends on USB_CONFIGFS diff --git a/drivers/usb/gadget/configfs.c b/drivers/usb/gadget/configfs.c index 025129942894..0c67c7302405 100644 --- a/drivers/usb/gadget/configfs.c +++ b/drivers/usb/gadget/configfs.c @@ -10,6 +10,20 @@ #include "u_f.h" #include "u_os_desc.h" +#ifdef CONFIG_USB_CONFIGFS_UEVENT +#include +#include +#include +#include "u_fs.h" + +#ifdef CONFIG_USB_CONFIGFS_F_ACC +extern int acc_ctrlrequest(struct usb_composite_dev *cdev, + const struct usb_ctrlrequest *ctrl); +void acc_disconnect(void); +#endif +static struct class *android_class; +#endif + int check_user_usb_string(const char *name, struct usb_gadget_strings *stringtab_dev) { @@ -61,6 +75,12 @@ struct gadget_info { bool use_os_desc; char b_vendor_code; char qw_sign[OS_STRING_QW_SIGN_LEN]; +#ifdef CONFIG_USB_CONFIGFS_UEVENT + bool connected; + bool sw_connected; + struct work_struct work; + struct device *dev; +#endif }; static inline struct gadget_info *to_gadget_info(struct config_item *item) @@ -266,7 +286,7 @@ static ssize_t gadget_dev_desc_UDC_store(struct config_item *item, mutex_lock(&gi->lock); - if (!strlen(name)) { + if (!strlen(name) || strcmp(name, "none") == 0) { ret = unregister_gadget(gi); if (ret) goto err; @@ -1372,6 +1392,57 @@ static int configfs_composite_bind(struct usb_gadget *gadget, return ret; } +#ifdef CONFIG_USB_CONFIGFS_UEVENT +static void android_work(struct work_struct *data) +{ + struct gadget_info *gi = container_of(data, struct gadget_info, work); + struct usb_composite_dev *cdev = &gi->cdev; + char *disconnected[2] = { "USB_STATE=DISCONNECTED", NULL }; + char *connected[2] = { "USB_STATE=CONNECTED", NULL }; + char *configured[2] = { "USB_STATE=CONFIGURED", NULL }; + /* 0-connected 1-configured 2-disconnected*/ + bool status[3] = { false, false, false }; + unsigned long flags; + bool uevent_sent = false; + + spin_lock_irqsave(&cdev->lock, flags); + if (cdev->config) + status[1] = true; + + if (gi->connected != gi->sw_connected) { + if (gi->connected) + status[0] = true; + else + status[2] = true; + gi->sw_connected = gi->connected; + } + spin_unlock_irqrestore(&cdev->lock, flags); + + if (status[0]) { + kobject_uevent_env(&gi->dev->kobj, KOBJ_CHANGE, connected); + pr_info("%s: sent uevent %s\n", __func__, connected[0]); + uevent_sent = true; + } + + if (status[1]) { + kobject_uevent_env(&gi->dev->kobj, KOBJ_CHANGE, configured); + pr_info("%s: sent uevent %s\n", __func__, configured[0]); + uevent_sent = true; + } + + if (status[2]) { + kobject_uevent_env(&gi->dev->kobj, KOBJ_CHANGE, disconnected); + pr_info("%s: sent uevent %s\n", __func__, disconnected[0]); + uevent_sent = true; + } + + if (!uevent_sent) { + pr_info("%s: did not send uevent (%d %d %p)\n", __func__, + gi->connected, gi->sw_connected, cdev->config); + } +} +#endif + static void configfs_composite_unbind(struct usb_gadget *gadget) { struct usb_composite_dev *cdev; @@ -1391,14 +1462,79 @@ static void configfs_composite_unbind(struct usb_gadget *gadget) set_gadget_data(gadget, NULL); } +#ifdef CONFIG_USB_CONFIGFS_UEVENT +static int android_setup(struct usb_gadget *gadget, + const struct usb_ctrlrequest *c) +{ + struct usb_composite_dev *cdev = get_gadget_data(gadget); + unsigned long flags; + struct gadget_info *gi = container_of(cdev, struct gadget_info, cdev); + int value = -EOPNOTSUPP; + struct usb_function_instance *fi; + + spin_lock_irqsave(&cdev->lock, flags); + if (!gi->connected) { + gi->connected = 1; + schedule_work(&gi->work); + } + spin_unlock_irqrestore(&cdev->lock, flags); + list_for_each_entry(fi, &gi->available_func, cfs_list) { + if (fi != NULL && fi->f != NULL && fi->f->setup != NULL) { + value = fi->f->setup(fi->f, c); + if (value >= 0) + break; + } + } + +#ifdef CONFIG_USB_CONFIGFS_F_ACC + if (value < 0) + value = acc_ctrlrequest(cdev, c); +#endif + + if (value < 0) + value = composite_setup(gadget, c); + + spin_lock_irqsave(&cdev->lock, flags); + if (c->bRequest == USB_REQ_SET_CONFIGURATION && + cdev->config) { + schedule_work(&gi->work); + } + spin_unlock_irqrestore(&cdev->lock, flags); + + return value; +} + +static void android_disconnect(struct usb_gadget *gadget) +{ + struct usb_composite_dev *cdev = get_gadget_data(gadget); + struct gadget_info *gi = container_of(cdev, struct gadget_info, cdev); + + /* accessory HID support can be active while the + accessory function is not actually enabled, + so we need to inform it when we are disconnected. + */ + +#ifdef CONFIG_USB_CONFIGFS_F_ACC + acc_disconnect(); +#endif + gi->connected = 0; + schedule_work(&gi->work); + composite_disconnect(gadget); +} +#endif + static const struct usb_gadget_driver configfs_driver_template = { .bind = configfs_composite_bind, .unbind = configfs_composite_unbind, - +#ifdef CONFIG_USB_CONFIGFS_UEVENT + .setup = android_setup, + .reset = android_disconnect, + .disconnect = android_disconnect, +#else .setup = composite_setup, .reset = composite_disconnect, .disconnect = composite_disconnect, - +#endif .suspend = composite_suspend, .resume = composite_resume, @@ -1458,6 +1594,12 @@ static struct config_group *gadgets_make( gi->composite.gadget_driver.function = kstrdup(name, GFP_KERNEL); gi->composite.name = gi->composite.gadget_driver.function; +#ifdef CONFIG_USB_CONFIGFS_UEVENT + INIT_WORK(&gi->work, android_work); + gi->dev = device_create(android_class, NULL, + MKDEV(0, 0), NULL, "android0"); +#endif + if (!gi->composite.gadget_driver.function) goto err; @@ -1509,6 +1651,13 @@ static int __init gadget_cfs_init(void) config_group_init(&gadget_subsys.su_group); ret = configfs_register_subsystem(&gadget_subsys); + +#ifdef CONFIG_USB_CONFIGFS_UEVENT + android_class = class_create(THIS_MODULE, "android_usb"); + if (IS_ERR(android_class)) + return PTR_ERR(android_class); +#endif + return ret; } module_init(gadget_cfs_init); From d32975042d1ac5a64f0f909d29e617f68a7bc444 Mon Sep 17 00:00:00 2001 From: Badhri Jagan Sridharan Date: Fri, 27 Mar 2015 14:15:19 -0700 Subject: [PATCH 0245/1103] ANDROID: usb: gadget: configfs: Add function devices to the parent Added create_function_device to create child function devices for USB gadget functions. Android UsbDeviceManager relies on communicating to the devices created by the gadget functions to implement functions such as audio_source. Signed-off-by: Badhri Jagan Sridharan Change-Id: I0df9ad86ac32d8cdacdea164e9fed49891b45fc2 --- drivers/usb/gadget/configfs.c | 34 +++++++++++++++++++++++++++++----- 1 file changed, 29 insertions(+), 5 deletions(-) diff --git a/drivers/usb/gadget/configfs.c b/drivers/usb/gadget/configfs.c index 0c67c7302405..37eb312d7498 100644 --- a/drivers/usb/gadget/configfs.c +++ b/drivers/usb/gadget/configfs.c @@ -14,7 +14,6 @@ #include #include #include -#include "u_fs.h" #ifdef CONFIG_USB_CONFIGFS_F_ACC extern int acc_ctrlrequest(struct usb_composite_dev *cdev, @@ -22,6 +21,18 @@ extern int acc_ctrlrequest(struct usb_composite_dev *cdev, void acc_disconnect(void); #endif static struct class *android_class; +static struct device *android_device; +static int index; + +struct device *create_function_device(char *name) +{ + if (android_device && !IS_ERR(android_device)) + return device_create(android_class, android_device, + MKDEV(0, index++), NULL, name); + else + return ERR_PTR(-EINVAL); +} +EXPORT_SYMBOL_GPL(create_function_device); #endif int check_user_usb_string(const char *name, @@ -1419,19 +1430,22 @@ static void android_work(struct work_struct *data) spin_unlock_irqrestore(&cdev->lock, flags); if (status[0]) { - kobject_uevent_env(&gi->dev->kobj, KOBJ_CHANGE, connected); + kobject_uevent_env(&android_device->kobj, + KOBJ_CHANGE, connected); pr_info("%s: sent uevent %s\n", __func__, connected[0]); uevent_sent = true; } if (status[1]) { - kobject_uevent_env(&gi->dev->kobj, KOBJ_CHANGE, configured); + kobject_uevent_env(&android_device->kobj, + KOBJ_CHANGE, configured); pr_info("%s: sent uevent %s\n", __func__, configured[0]); uevent_sent = true; } if (status[2]) { - kobject_uevent_env(&gi->dev->kobj, KOBJ_CHANGE, disconnected); + kobject_uevent_env(&android_device->kobj, + KOBJ_CHANGE, disconnected); pr_info("%s: sent uevent %s\n", __func__, disconnected[0]); uevent_sent = true; } @@ -1596,8 +1610,10 @@ static struct config_group *gadgets_make( #ifdef CONFIG_USB_CONFIGFS_UEVENT INIT_WORK(&gi->work, android_work); - gi->dev = device_create(android_class, NULL, + android_device = device_create(android_class, NULL, MKDEV(0, 0), NULL, "android0"); + if (IS_ERR(android_device)) + goto err; #endif if (!gi->composite.gadget_driver.function) @@ -1612,6 +1628,9 @@ static struct config_group *gadgets_make( static void gadgets_drop(struct config_group *group, struct config_item *item) { config_item_put(item); +#ifdef CONFIG_USB_CONFIGFS_UEVENT + device_destroy(android_device->class, android_device->devt); +#endif } static struct configfs_group_operations gadgets_ops = { @@ -1665,5 +1684,10 @@ module_init(gadget_cfs_init); static void __exit gadget_cfs_exit(void) { configfs_unregister_subsystem(&gadget_subsys); +#ifdef CONFIG_USB_CONFIGFS_UEVENT + if (!IS_ERR(android_class)) + class_destroy(android_class); +#endif + } module_exit(gadget_cfs_exit); From a3bf73ee26a1853945133d845bef256cee349e3e Mon Sep 17 00:00:00 2001 From: Badhri Jagan Sridharan Date: Tue, 14 Jul 2015 15:46:11 -0700 Subject: [PATCH 0246/1103] ANDROID: usb: gadget: configfs: Add "state" attribute to android_device Added a device attribute to android_device to determine USB_GADGET's state Signed-off-by: Badhri Jagan Sridharan Change-Id: I17f8903120df96bf2f4bf441940b53a87b818230 --- drivers/usb/gadget/configfs.c | 66 ++++++++++++++++++++++++++++++++++- 1 file changed, 65 insertions(+), 1 deletion(-) diff --git a/drivers/usb/gadget/configfs.c b/drivers/usb/gadget/configfs.c index 37eb312d7498..68f67ce607da 100644 --- a/drivers/usb/gadget/configfs.c +++ b/drivers/usb/gadget/configfs.c @@ -1560,11 +1560,49 @@ static const struct usb_gadget_driver configfs_driver_template = { .match_existing_only = 1, }; +#ifdef CONFIG_USB_CONFIGFS_UEVENT +static ssize_t state_show(struct device *pdev, struct device_attribute *attr, + char *buf) +{ + struct gadget_info *dev = dev_get_drvdata(pdev); + struct usb_composite_dev *cdev; + char *state = "DISCONNECTED"; + unsigned long flags; + + if (!dev) + goto out; + + cdev = &dev->cdev; + + if (!cdev) + goto out; + + spin_lock_irqsave(&cdev->lock, flags); + if (cdev->config) + state = "CONFIGURED"; + else if (dev->connected) + state = "CONNECTED"; + spin_unlock_irqrestore(&cdev->lock, flags); +out: + return sprintf(buf, "%s\n", state); +} + +static DEVICE_ATTR(state, S_IRUGO, state_show, NULL); + +static struct device_attribute *android_usb_attributes[] = { + &dev_attr_state, + NULL +}; +#endif + static struct config_group *gadgets_make( struct config_group *group, const char *name) { struct gadget_info *gi; + struct device_attribute **attrs; + struct device_attribute *attr; + int err; gi = kzalloc(sizeof(*gi), GFP_KERNEL); if (!gi) @@ -1614,12 +1652,31 @@ static struct config_group *gadgets_make( MKDEV(0, 0), NULL, "android0"); if (IS_ERR(android_device)) goto err; + + dev_set_drvdata(android_device, gi); + + attrs = android_usb_attributes; + while ((attr = *attrs++)) { + err = device_create_file(android_device, attr); + if (err) + goto err1; + } #endif if (!gi->composite.gadget_driver.function) - goto err; + goto err1; return &gi->group; + +err1: +#ifdef CONFIG_USB_CONFIGFS_UEVENT + attrs = android_usb_attributes; + while ((attr = *attrs++)) + device_remove_file(android_device, attr); + + device_destroy(android_device->class, + android_device->devt); +#endif err: kfree(gi); return ERR_PTR(-ENOMEM); @@ -1627,8 +1684,15 @@ static struct config_group *gadgets_make( static void gadgets_drop(struct config_group *group, struct config_item *item) { + struct device_attribute **attrs; + struct device_attribute *attr; + config_item_put(item); + #ifdef CONFIG_USB_CONFIGFS_UEVENT + attrs = android_usb_attributes; + while ((attr = *attrs++)) + device_remove_file(android_device, attr); device_destroy(android_device->class, android_device->devt); #endif } From bd5174fcdf251e96bc00c841e54aa9f2036450f7 Mon Sep 17 00:00:00 2001 From: Badhri Jagan Sridharan Date: Sun, 9 Aug 2015 15:12:50 -0700 Subject: [PATCH 0247/1103] ANDROID: usb: gadget: configfs: Add device attribute to determine gadget state Android frameworks (UsbDeviceManager) relies on gadget state exported through device attributes. This CL adds the device attribute to export USB gadget state. Change-Id: Id0391810d75b58c579610fbec6e37ab22f28886d Signed-off-by: Badhri Jagan Sridharan [AmitP: Folded following android-4.9 commit changes into this patch Parts of e45c769fa7af ("ANDROID: usb: gadget: cleanup: fix unused variable and function warnings") Signed-off-by: Amit Pundir --- drivers/usb/gadget/configfs.c | 91 ++++++++++++++++++++--------------- 1 file changed, 52 insertions(+), 39 deletions(-) diff --git a/drivers/usb/gadget/configfs.c b/drivers/usb/gadget/configfs.c index 68f67ce607da..59a97eba1aa8 100644 --- a/drivers/usb/gadget/configfs.c +++ b/drivers/usb/gadget/configfs.c @@ -1593,6 +1593,54 @@ static struct device_attribute *android_usb_attributes[] = { &dev_attr_state, NULL }; + +static int android_device_create(struct gadget_info *gi) +{ + struct device_attribute **attrs; + struct device_attribute *attr; + + INIT_WORK(&gi->work, android_work); + android_device = device_create(android_class, NULL, + MKDEV(0, 0), NULL, "android0"); + if (IS_ERR(android_device)) + return PTR_ERR(android_device); + + dev_set_drvdata(android_device, gi); + + attrs = android_usb_attributes; + while ((attr = *attrs++)) { + int err; + + err = device_create_file(android_device, attr); + if (err) { + device_destroy(android_device->class, + android_device->devt); + return err; + } + } + + return 0; +} + +static void android_device_destroy(void) +{ + struct device_attribute **attrs; + struct device_attribute *attr; + + attrs = android_usb_attributes; + while ((attr = *attrs++)) + device_remove_file(android_device, attr); + device_destroy(android_device->class, android_device->devt); +} +#else +static inline int android_device_create(struct gadget_info *gi) +{ + return 0; +} + +static inline void android_device_destroy(void) +{ +} #endif static struct config_group *gadgets_make( @@ -1600,9 +1648,6 @@ static struct config_group *gadgets_make( const char *name) { struct gadget_info *gi; - struct device_attribute **attrs; - struct device_attribute *attr; - int err; gi = kzalloc(sizeof(*gi), GFP_KERNEL); if (!gi) @@ -1646,37 +1691,14 @@ static struct config_group *gadgets_make( gi->composite.gadget_driver.function = kstrdup(name, GFP_KERNEL); gi->composite.name = gi->composite.gadget_driver.function; -#ifdef CONFIG_USB_CONFIGFS_UEVENT - INIT_WORK(&gi->work, android_work); - android_device = device_create(android_class, NULL, - MKDEV(0, 0), NULL, "android0"); - if (IS_ERR(android_device)) + if (!gi->composite.gadget_driver.function) goto err; - dev_set_drvdata(android_device, gi); - - attrs = android_usb_attributes; - while ((attr = *attrs++)) { - err = device_create_file(android_device, attr); - if (err) - goto err1; - } -#endif - - if (!gi->composite.gadget_driver.function) - goto err1; + if (android_device_create(gi) < 0) + goto err; return &gi->group; -err1: -#ifdef CONFIG_USB_CONFIGFS_UEVENT - attrs = android_usb_attributes; - while ((attr = *attrs++)) - device_remove_file(android_device, attr); - - device_destroy(android_device->class, - android_device->devt); -#endif err: kfree(gi); return ERR_PTR(-ENOMEM); @@ -1684,17 +1706,8 @@ static struct config_group *gadgets_make( static void gadgets_drop(struct config_group *group, struct config_item *item) { - struct device_attribute **attrs; - struct device_attribute *attr; - config_item_put(item); - -#ifdef CONFIG_USB_CONFIGFS_UEVENT - attrs = android_usb_attributes; - while ((attr = *attrs++)) - device_remove_file(android_device, attr); - device_destroy(android_device->class, android_device->devt); -#endif + android_device_destroy(); } static struct configfs_group_operations gadgets_ops = { From 6a51f1d2b220659c0a97f4a098efe1da39238f28 Mon Sep 17 00:00:00 2001 From: Danilo Krummrich Date: Mon, 14 Aug 2017 12:45:38 +0200 Subject: [PATCH 0248/1103] ANDROID: usb: gadget: configfs: fix null ptr in android_disconnect There's a race between usb_gadget_udc_stop() which is likely to set the gadget driver to NULL in the udc driver and this drivers gadget disconnect fn which likely checks for the gadget driver to a null ptr. It happens that unbind (doing set_gadget_data(NULL)) is called before the gadget driver is set to NULL and the udc driver calls disconnect fn which results in cdev being a null ptr. As a workaround we check cdev in android_disconnect() to prevent the following panic: Unable to handle kernel NULL pointer dereference at virtual address 000000a8 pgd = ffffff800940a000 [000000a8] *pgd=00000000be1fe003, *pud=00000000be1fe003, *pmd=0000000000000000 Internal error: Oops: 96000046 [#1] PREEMPT SMP CPU: 7 PID: 1134 Comm: kworker/u16:3 Tainted: G S 4.9.41-g75cd2a0231ea-dirty #4 Hardware name: HiKey960 (DT) Workqueue: events_power_efficient event_work task: ffffffc0b5f4f000 task.stack: ffffffc0b5b94000 PC is at android_disconnect+0x54/0xa4 LR is at android_disconnect+0x54/0xa4 pc : [] lr : [] pstate: 80000185 sp : ffffffc0b5b97bf0 x29: ffffffc0b5b97bf0 x28: 0000000000000003 x27: ffffffc0b5181c54 x26: ffffffc0b5181c68 x25: ffffff8008dc1000 x24: ffffffc0b5181d70 x23: ffffff8008dc18a0 x22: ffffffc0b5f5a018 x21: ffffffc0b5894ad8 x20: 0000000000000000 x19: ffffff8008ddaec8 x18: 0000000000000000 x17: 0000000000000000 x16: 0000000000000000 x15: 0000000000000000 x14: 00000000007c9ccd x13: 0000000000000000 x12: 0000000000000000 x11: 0000000000000001 x10: 0000000000000001 x9 : ffffff800930f1a8 x8 : ffffff800932a133 x7 : 0000000000000000 x6 : 0000000000000000 x5 : ffffffc0b5b97a50 x4 : ffffffc0be19f090 x3 : 0000000000000000 x2 : ffffff80091ca000 x1 : 000000000000002f x0 : 000000000000002f This happened on a hikey960 with the following backtrace: [] android_disconnect+0x54/0xa4 [] dwc3_disconnect_gadget.part.19+0x114.888119] [] dwc3_gadget_suspend+0x6c/0x70 [] dwc3_suspend_device+0x58/0xa0 [] dwc3_otg_work+0x214/0x474 [] event_work+0x3bc/0x5ac [] process_one_work+0x14c/0x43c [] worker_thread+0x5c/0x438 [] kthread+0xec/0x100 [] ret_from_fork+0x10/0x50 dwc3_otg_work tries to handle a switch from host to device mode and therefore calls disconnect on the gadget driver. To reproduce the issue it is enaugh to enable tethering (rndis gadget), unplug and plug in again the usb connector which causes the change from device to host and back to device mode. Signed-off-by: Danilo Krummrich --- drivers/usb/gadget/configfs.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/drivers/usb/gadget/configfs.c b/drivers/usb/gadget/configfs.c index 59a97eba1aa8..b0148a449306 100644 --- a/drivers/usb/gadget/configfs.c +++ b/drivers/usb/gadget/configfs.c @@ -1523,6 +1523,18 @@ static void android_disconnect(struct usb_gadget *gadget) struct usb_composite_dev *cdev = get_gadget_data(gadget); struct gadget_info *gi = container_of(cdev, struct gadget_info, cdev); + /* FIXME: There's a race between usb_gadget_udc_stop() which is likely + * to set the gadget driver to NULL in the udc driver and this drivers + * gadget disconnect fn which likely checks for the gadget driver to + * be a null ptr. It happens that unbind (doing set_gadget_data(NULL)) + * is called before the gadget driver is set to NULL and the udc driver + * calls disconnect fn which results in cdev being a null ptr. + */ + if (cdev == NULL) { + WARN(1, "%s: gadget driver already disconnected\n", __func__); + return; + } + /* accessory HID support can be active while the accessory function is not actually enabled, so we need to inform it when we are disconnected. From 5996896ecb00a80d7ff8bec358d7bd1d3e773ba5 Mon Sep 17 00:00:00 2001 From: Badhri Jagan Sridharan Date: Sun, 23 Nov 2014 13:51:28 -0800 Subject: [PATCH 0249/1103] ANDROID: usb: gadget: f_audio_source: Move to USB_FUNCTION API This patch adds support to use audio_source gadget function through DECLARE_USB_FUNCTION_INIT interface. Signed-off-by: Badhri Jagan Sridharan Change-Id: I1fc6c9ea07105ae4eb785eebd3bb925bfdd8bc6b [AmitP: Folded following android-4.9 commit changes into this patch 95106e3d2071 ("ANDROID: usb: gadget: build audio_source function only if SND is enabled") 4de9e33e5046 ("ANDROID: usb: gadget: audio_source: fix comparison of distinct pointer types") Parts of 051584e76d12 ("ANDROID: usb: gadget: function: cleanup: Add blank line after declaration")] Signed-off-by: Amit Pundir --- drivers/usb/gadget/Kconfig | 12 ++ drivers/usb/gadget/Makefile | 2 + drivers/usb/gadget/f_audio_source.c | 242 ++++++++++++++++++++++++++-- 3 files changed, 245 insertions(+), 11 deletions(-) diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig index d92762d134b4..a306bb3bef85 100644 --- a/drivers/usb/gadget/Kconfig +++ b/drivers/usb/gadget/Kconfig @@ -215,6 +215,9 @@ config USB_F_PRINTER config USB_F_TCM tristate +config USB_F_AUDIO_SRC + tristate + # this first set of drivers all depend on bulk-capable hardware. config USB_CONFIGFS @@ -368,6 +371,15 @@ config USB_CONFIGFS_F_FS implemented in kernel space (for instance Ethernet, serial or mass storage) and other are implemented in user space. +config USB_CONFIGFS_F_AUDIO_SRC + bool "Audio Source gadget" + depends on USB_CONFIGFS + depends on SND + select SND_PCM + select USB_F_AUDIO_SRC + help + USB gadget Audio Source support + config USB_CONFIGFS_UEVENT bool "Uevent notification of Gadget state" depends on USB_CONFIGFS diff --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile index 130dad7130b6..f019157a0908 100644 --- a/drivers/usb/gadget/Makefile +++ b/drivers/usb/gadget/Makefile @@ -11,3 +11,5 @@ libcomposite-y := usbstring.o config.o epautoconf.o libcomposite-y += composite.o functions.o configfs.o u_f.o obj-$(CONFIG_USB_GADGET) += udc/ function/ legacy/ +usb_f_audio_source-y := f_audio_source.o +obj-$(CONFIG_USB_F_AUDIO_SRC) += usb_f_audio_source.o diff --git a/drivers/usb/gadget/f_audio_source.c b/drivers/usb/gadget/f_audio_source.c index 4b9786b48950..61b47b820a90 100644 --- a/drivers/usb/gadget/f_audio_source.c +++ b/drivers/usb/gadget/f_audio_source.c @@ -21,6 +21,13 @@ #include #include +#include +#include +#include +#include +#include +#include +#include #define SAMPLE_RATE 44100 #define FRAMES_PER_MSEC (SAMPLE_RATE / 1000) @@ -32,6 +39,7 @@ #define AUDIO_AC_INTERFACE 0 #define AUDIO_AS_INTERFACE 1 #define AUDIO_NUM_INTERFACES 2 +#define MAX_INST_NAME_LEN 40 /* B.3.1 Standard AC Interface Descriptor */ static struct usb_interface_descriptor ac_interface_desc = { @@ -259,6 +267,7 @@ struct audio_dev { ktime_t start_time; /* number of frames sent since start_time */ s64 frames_sent; + struct audio_source_config *config; }; static inline struct audio_dev *func_to_audio(struct usb_function *f) @@ -268,9 +277,40 @@ static inline struct audio_dev *func_to_audio(struct usb_function *f) /*-------------------------------------------------------------------------*/ +struct audio_source_instance { + struct usb_function_instance func_inst; + const char *name; + struct audio_source_config *config; + struct device *audio_device; +}; + +static void audio_source_attr_release(struct config_item *item); + +static struct configfs_item_operations audio_source_item_ops = { + .release = audio_source_attr_release, +}; + +static struct config_item_type audio_source_func_type = { + .ct_item_ops = &audio_source_item_ops, + .ct_owner = THIS_MODULE, +}; + +static ssize_t audio_source_pcm_show(struct device *dev, + struct device_attribute *attr, char *buf); + +static DEVICE_ATTR(pcm, S_IRUGO, audio_source_pcm_show, NULL); + +static struct device_attribute *audio_source_function_attributes[] = { + &dev_attr_pcm, + NULL +}; + +/*--------------------------------------------------------------------------*/ + static struct usb_request *audio_request_new(struct usb_ep *ep, int buffer_size) { struct usb_request *req = usb_ep_alloc_request(ep, GFP_KERNEL); + if (!req) return NULL; @@ -338,10 +378,9 @@ static void audio_send(struct audio_dev *audio) /* compute number of frames to send */ now = ktime_get(); - msecs = ktime_to_ns(now) - ktime_to_ns(audio->start_time); - do_div(msecs, 1000000); - frames = msecs * SAMPLE_RATE; - do_div(frames, 1000); + msecs = div_s64((ktime_to_ns(now) - ktime_to_ns(audio->start_time)), + 1000000); + frames = div_s64((msecs * SAMPLE_RATE), 1000); /* Readjust our frames_sent if we fall too far behind. * If we get too far behind it is better to drop some frames than @@ -561,6 +600,13 @@ static void audio_build_desc(struct audio_dev *audio) memcpy(sam_freq, &rate, 3); } + +static int snd_card_setup(struct usb_configuration *c, + struct audio_source_config *config); +static struct audio_source_instance *to_fi_audio_source( + const struct usb_function_instance *fi); + + /* audio function driver setup/binding */ static int audio_bind(struct usb_configuration *c, struct usb_function *f) @@ -571,6 +617,18 @@ audio_bind(struct usb_configuration *c, struct usb_function *f) struct usb_ep *ep; struct usb_request *req; int i; + int err; + + if (IS_ENABLED(CONFIG_USB_CONFIGFS)) { + struct audio_source_instance *fi_audio = + to_fi_audio_source(f->fi); + struct audio_source_config *config = + fi_audio->config; + + err = snd_card_setup(c, config); + if (err) + return err; + } audio_build_desc(audio); @@ -636,6 +694,16 @@ audio_unbind(struct usb_configuration *c, struct usb_function *f) audio->pcm = NULL; audio->substream = NULL; audio->in_ep = NULL; + + if (IS_ENABLED(CONFIG_USB_CONFIGFS)) { + struct audio_source_instance *fi_audio = + to_fi_audio_source(f->fi); + struct audio_source_config *config = + fi_audio->config; + + config->card = -1; + config->device = -1; + } } static void audio_pcm_playback_start(struct audio_dev *audio) @@ -779,8 +847,6 @@ int audio_source_bind_config(struct usb_configuration *c, struct audio_source_config *config) { struct audio_dev *audio; - struct snd_card *card; - struct snd_pcm *pcm; int err; config->card = -1; @@ -788,6 +854,31 @@ int audio_source_bind_config(struct usb_configuration *c, audio = &_audio_dev; + err = snd_card_setup(c, config); + if (err) + return err; + + err = usb_add_function(c, &audio->func); + if (err) + goto add_fail; + + return 0; + +add_fail: + snd_card_free(audio->card); + return err; +} + +static int snd_card_setup(struct usb_configuration *c, + struct audio_source_config *config) +{ + struct audio_dev *audio; + struct snd_card *card; + struct snd_pcm *pcm; + int err; + + audio = &_audio_dev; + err = snd_card_new(&c->cdev->gadget->dev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1, THIS_MODULE, 0, &card); @@ -817,18 +908,147 @@ int audio_source_bind_config(struct usb_configuration *c, if (err) goto register_fail; - err = usb_add_function(c, &audio->func); - if (err) - goto add_fail; - config->card = pcm->card->number; config->device = pcm->device; audio->card = card; return 0; -add_fail: register_fail: pcm_fail: snd_card_free(audio->card); return err; } + +static struct audio_source_instance *to_audio_source_instance( + struct config_item *item) +{ + return container_of(to_config_group(item), struct audio_source_instance, + func_inst.group); +} + +static struct audio_source_instance *to_fi_audio_source( + const struct usb_function_instance *fi) +{ + return container_of(fi, struct audio_source_instance, func_inst); +} + +static void audio_source_attr_release(struct config_item *item) +{ + struct audio_source_instance *fi_audio = to_audio_source_instance(item); + + usb_put_function_instance(&fi_audio->func_inst); +} + +static int audio_source_set_inst_name(struct usb_function_instance *fi, + const char *name) +{ + struct audio_source_instance *fi_audio; + char *ptr; + int name_len; + + name_len = strlen(name) + 1; + if (name_len > MAX_INST_NAME_LEN) + return -ENAMETOOLONG; + + ptr = kstrndup(name, name_len, GFP_KERNEL); + if (!ptr) + return -ENOMEM; + + fi_audio = to_fi_audio_source(fi); + fi_audio->name = ptr; + + return 0; +} + +static void audio_source_free_inst(struct usb_function_instance *fi) +{ + struct audio_source_instance *fi_audio; + + fi_audio = to_fi_audio_source(fi); + device_destroy(fi_audio->audio_device->class, + fi_audio->audio_device->devt); + kfree(fi_audio->name); + kfree(fi_audio->config); +} + +static ssize_t audio_source_pcm_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct audio_source_instance *fi_audio = dev_get_drvdata(dev); + struct audio_source_config *config = fi_audio->config; + + /* print PCM card and device numbers */ + return sprintf(buf, "%d %d\n", config->card, config->device); +} + +struct device *create_function_device(char *name); + +static struct usb_function_instance *audio_source_alloc_inst(void) +{ + struct audio_source_instance *fi_audio; + struct device_attribute **attrs; + struct device_attribute *attr; + struct device *dev; + void *err_ptr; + int err = 0; + + fi_audio = kzalloc(sizeof(*fi_audio), GFP_KERNEL); + if (!fi_audio) + return ERR_PTR(-ENOMEM); + + fi_audio->func_inst.set_inst_name = audio_source_set_inst_name; + fi_audio->func_inst.free_func_inst = audio_source_free_inst; + + fi_audio->config = kzalloc(sizeof(struct audio_source_config), + GFP_KERNEL); + if (!fi_audio->config) { + err_ptr = ERR_PTR(-ENOMEM); + goto fail_audio; + } + + config_group_init_type_name(&fi_audio->func_inst.group, "", + &audio_source_func_type); + dev = create_function_device("f_audio_source"); + + if (IS_ERR(dev)) { + err_ptr = dev; + goto fail_audio_config; + } + + fi_audio->config->card = -1; + fi_audio->config->device = -1; + fi_audio->audio_device = dev; + + attrs = audio_source_function_attributes; + if (attrs) { + while ((attr = *attrs++) && !err) + err = device_create_file(dev, attr); + if (err) { + err_ptr = ERR_PTR(-EINVAL); + goto fail_device; + } + } + + dev_set_drvdata(dev, fi_audio); + _audio_dev.config = fi_audio->config; + + return &fi_audio->func_inst; + +fail_device: + device_destroy(dev->class, dev->devt); +fail_audio_config: + kfree(fi_audio->config); +fail_audio: + kfree(fi_audio); + return err_ptr; + +} + +static struct usb_function *audio_source_alloc(struct usb_function_instance *fi) +{ + return &_audio_dev.func; +} + +DECLARE_USB_FUNCTION_INIT(audio_source, audio_source_alloc_inst, + audio_source_alloc); +MODULE_LICENSE("GPL"); From f196355cdae1071f67ef36e4ed10d83dcf4d9fc2 Mon Sep 17 00:00:00 2001 From: Badhri Jagan Sridharan Date: Fri, 27 Mar 2015 14:49:55 -0700 Subject: [PATCH 0250/1103] ANDROID: usb: gadget: f_audio_source: Move gadget functions code 3.18 kernel has reorganized drivers/usb/gadget directory. Moving gadget functions drivers from drivers/usb/gadget to drivers/usb/gadget/function Signed-off-by: Badhri Jagan Sridharan Change-Id: I1eab0190f8d42e3be1b4e91ad3bc3a2dc853b0ef [AmitP: Refactored the original changes after squashing of related patches] Signed-off-by: Amit Pundir --- drivers/usb/gadget/Makefile | 2 -- drivers/usb/gadget/function/Makefile | 2 ++ drivers/usb/gadget/{ => function}/f_audio_source.c | 0 3 files changed, 2 insertions(+), 2 deletions(-) rename drivers/usb/gadget/{ => function}/f_audio_source.c (100%) diff --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile index f019157a0908..130dad7130b6 100644 --- a/drivers/usb/gadget/Makefile +++ b/drivers/usb/gadget/Makefile @@ -11,5 +11,3 @@ libcomposite-y := usbstring.o config.o epautoconf.o libcomposite-y += composite.o functions.o configfs.o u_f.o obj-$(CONFIG_USB_GADGET) += udc/ function/ legacy/ -usb_f_audio_source-y := f_audio_source.o -obj-$(CONFIG_USB_F_AUDIO_SRC) += usb_f_audio_source.o diff --git a/drivers/usb/gadget/function/Makefile b/drivers/usb/gadget/function/Makefile index 5d3a6cf02218..42a0b6ef39da 100644 --- a/drivers/usb/gadget/function/Makefile +++ b/drivers/usb/gadget/function/Makefile @@ -50,3 +50,5 @@ usb_f_printer-y := f_printer.o obj-$(CONFIG_USB_F_PRINTER) += usb_f_printer.o usb_f_tcm-y := f_tcm.o obj-$(CONFIG_USB_F_TCM) += usb_f_tcm.o +usb_f_audio_source-y := f_audio_source.o +obj-$(CONFIG_USB_F_AUDIO_SRC) += usb_f_audio_source.o diff --git a/drivers/usb/gadget/f_audio_source.c b/drivers/usb/gadget/function/f_audio_source.c similarity index 100% rename from drivers/usb/gadget/f_audio_source.c rename to drivers/usb/gadget/function/f_audio_source.c From 51cdd8854107d7a5498ac5ce59dd5e27fda24c16 Mon Sep 17 00:00:00 2001 From: Konrad Leszczynski Date: Wed, 24 Feb 2016 10:47:12 +0000 Subject: [PATCH 0251/1103] ANDROID: usb: gadget: f_audio_source: disable the CPU C-states upon playback Due to the issue with the isoc transfers being interrupted by the CPU going into the idle state, the C-states will be disabled for the playback time. Change-Id: If4e52673606923d7e33a1d1dbe0192b8ad24f78c Signed-off-by: Konrad Leszczynski Signed-off-by: Russ Weight --- drivers/usb/gadget/function/f_audio_source.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/drivers/usb/gadget/function/f_audio_source.c b/drivers/usb/gadget/function/f_audio_source.c index 61b47b820a90..3cf60609a83e 100644 --- a/drivers/usb/gadget/function/f_audio_source.c +++ b/drivers/usb/gadget/function/f_audio_source.c @@ -17,6 +17,7 @@ #include #include #include +#include #include #include #include @@ -268,6 +269,8 @@ struct audio_dev { /* number of frames sent since start_time */ s64 frames_sent; struct audio_source_config *config; + /* for creating and issuing QoS requests */ + struct pm_qos_request pm_qos; }; static inline struct audio_dev *func_to_audio(struct usb_function *f) @@ -735,6 +738,10 @@ static int audio_pcm_open(struct snd_pcm_substream *substream) runtime->hw.channels_max = 2; audio->substream = substream; + + /* Add the QoS request and set the latency to 0 */ + pm_qos_add_request(&audio->pm_qos, PM_QOS_CPU_DMA_LATENCY, 0); + return 0; } @@ -744,6 +751,10 @@ static int audio_pcm_close(struct snd_pcm_substream *substream) unsigned long flags; spin_lock_irqsave(&audio->lock, flags); + + /* Remove the QoS request */ + pm_qos_remove_request(&audio->pm_qos); + audio->substream = NULL; spin_unlock_irqrestore(&audio->lock, flags); From 14cb62fcb5700a4d68c7fdb720fe50e8594f37a8 Mon Sep 17 00:00:00 2001 From: Badhri Jagan Sridharan Date: Sun, 23 Nov 2014 17:21:22 -0800 Subject: [PATCH 0252/1103] ANDROID: usb: gadget: f_accessory: Migrate to USB_FUNCTION API This patch adds support to use Android accessory gadget function through the DECLARE_USB_FUNCTION_INIT interface. Signed-off-by: Badhri Jagan Sridharan Change-Id: Ib352752d5bc905fa1df9049b53eabf1294930db7 [AmitP: Folded following android-4.9 commit changes into this patch Parts of e45c769fa7af ("ANDROID: usb: gadget: cleanup: fix unused variable and function warnings") a0a752add9b5 ("ANDROID: usb: gadget: f_accessory: remove duplicate endpoint alloc") Parts of 051584e76d12 ("ANDROID: usb: gadget: function: cleanup: Add blank line after declaration")] Signed-off-by: Amit Pundir --- drivers/usb/gadget/Kconfig | 12 +- drivers/usb/gadget/Makefile | 3 + drivers/usb/gadget/f_accessory.c | 188 ++++++++++++++++++++++++------- 3 files changed, 161 insertions(+), 42 deletions(-) diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig index a306bb3bef85..812800c255ec 100644 --- a/drivers/usb/gadget/Kconfig +++ b/drivers/usb/gadget/Kconfig @@ -218,6 +218,9 @@ config USB_F_TCM config USB_F_AUDIO_SRC tristate +config USB_F_ACC + tristate + # this first set of drivers all depend on bulk-capable hardware. config USB_CONFIGFS @@ -371,9 +374,16 @@ config USB_CONFIGFS_F_FS implemented in kernel space (for instance Ethernet, serial or mass storage) and other are implemented in user space. +config USB_CONFIGFS_F_ACC + bool "Accessory gadget" + depends on USB_CONFIGFS + select USB_F_ACC + help + USB gadget Accessory support + config USB_CONFIGFS_F_AUDIO_SRC bool "Audio Source gadget" - depends on USB_CONFIGFS + depends on USB_CONFIGFS && USB_CONFIGFS_F_ACC depends on SND select SND_PCM select USB_F_AUDIO_SRC diff --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile index 130dad7130b6..92dd916df04d 100644 --- a/drivers/usb/gadget/Makefile +++ b/drivers/usb/gadget/Makefile @@ -10,4 +10,7 @@ obj-$(CONFIG_USB_LIBCOMPOSITE) += libcomposite.o libcomposite-y := usbstring.o config.o epautoconf.o libcomposite-y += composite.o functions.o configfs.o u_f.o +usb_f_accessory-y := f_accessory.o +obj-$(CONFIG_USB_F_ACC) += usb_f_accessory.o + obj-$(CONFIG_USB_GADGET) += udc/ function/ legacy/ diff --git a/drivers/usb/gadget/f_accessory.c b/drivers/usb/gadget/f_accessory.c index 9ffe017bf1cf..ef1ffcf88cdc 100644 --- a/drivers/usb/gadget/f_accessory.c +++ b/drivers/usb/gadget/f_accessory.c @@ -39,6 +39,10 @@ #include #include +#include +#include + +#define MAX_INST_NAME_LEN 40 #define BULK_BUFFER_SIZE 16384 #define ACC_STRING_SIZE 256 @@ -194,6 +198,11 @@ static struct usb_gadget_strings *acc_strings[] = { /* temporary variable used between acc_open() and acc_gadget_bind() */ static struct acc_dev *_acc_dev; +struct acc_instance { + struct usb_function_instance func_inst; + const char *name; +}; + static inline struct acc_dev *func_to_dev(struct usb_function *f) { return container_of(f, struct acc_dev, function); @@ -202,6 +211,7 @@ static inline struct acc_dev *func_to_dev(struct usb_function *f) static struct usb_request *acc_request_new(struct usb_ep *ep, int buffer_size) { struct usb_request *req = usb_ep_alloc_request(ep, GFP_KERNEL); + if (!req) return NULL; @@ -511,15 +521,6 @@ static int create_bulk_endpoints(struct acc_dev *dev, ep->driver_data = dev; /* claim the endpoint */ dev->ep_out = ep; - ep = usb_ep_autoconfig(cdev->gadget, out_desc); - if (!ep) { - DBG(cdev, "usb_ep_autoconfig for ep_out failed\n"); - return -ENODEV; - } - DBG(cdev, "usb_ep_autoconfig for ep_out got %s\n", ep->name); - ep->driver_data = dev; /* claim the endpoint */ - dev->ep_out = ep; - /* now allocate requests for our endpoints */ for (i = 0; i < TX_REQ_MAX; i++) { req = acc_request_new(dev->ep_in, BULK_BUFFER_SIZE); @@ -775,7 +776,7 @@ static struct hid_driver acc_hid_driver = { .probe = acc_hid_probe, }; -static int acc_ctrlrequest(struct usb_composite_dev *cdev, +int acc_ctrlrequest(struct usb_composite_dev *cdev, const struct usb_ctrlrequest *ctrl) { struct acc_dev *dev = _acc_dev; @@ -879,9 +880,11 @@ static int acc_ctrlrequest(struct usb_composite_dev *cdev, w_value, w_index, w_length); return value; } +EXPORT_SYMBOL_GPL(acc_ctrlrequest); static int -acc_function_bind(struct usb_configuration *c, struct usb_function *f) +__acc_function_bind(struct usb_configuration *c, + struct usb_function *f, bool configfs) { struct usb_composite_dev *cdev = c->cdev; struct acc_dev *dev = func_to_dev(f); @@ -890,6 +893,16 @@ acc_function_bind(struct usb_configuration *c, struct usb_function *f) DBG(cdev, "acc_function_bind dev: %p\n", dev); + if (configfs) { + if (acc_string_defs[INTERFACE_STRING_INDEX].id == 0) { + ret = usb_string_id(c->cdev); + if (ret < 0) + return ret; + acc_string_defs[INTERFACE_STRING_INDEX].id = ret; + acc_interface_desc.iInterface = ret; + } + dev->cdev = c->cdev; + } ret = hid_register_driver(&acc_hid_driver); if (ret) return ret; @@ -922,6 +935,12 @@ acc_function_bind(struct usb_configuration *c, struct usb_function *f) return 0; } +static int +acc_function_bind_configfs(struct usb_configuration *c, + struct usb_function *f) { + return __acc_function_bind(c, f, true); +} + static void kill_all_hid_devices(struct acc_dev *dev) { @@ -974,6 +993,7 @@ acc_function_unbind(struct usb_configuration *c, struct usb_function *f) static void acc_start_work(struct work_struct *data) { char *envp[2] = { "ACCESSORY=START", NULL }; + kobject_uevent_env(&acc_device.this_device->kobj, KOBJ_CHANGE, envp); } @@ -1115,35 +1135,6 @@ static void acc_function_disable(struct usb_function *f) VDBG(cdev, "%s disabled\n", dev->function.name); } -static int acc_bind_config(struct usb_configuration *c) -{ - struct acc_dev *dev = _acc_dev; - int ret; - - printk(KERN_INFO "acc_bind_config\n"); - - /* allocate a string ID for our interface */ - if (acc_string_defs[INTERFACE_STRING_INDEX].id == 0) { - ret = usb_string_id(c->cdev); - if (ret < 0) - return ret; - acc_string_defs[INTERFACE_STRING_INDEX].id = ret; - acc_interface_desc.iInterface = ret; - } - - dev->cdev = c->cdev; - dev->function.name = "accessory"; - dev->function.strings = acc_strings, - dev->function.fs_descriptors = fs_acc_descs; - dev->function.hs_descriptors = hs_acc_descs; - dev->function.bind = acc_function_bind; - dev->function.unbind = acc_function_unbind; - dev->function.set_alt = acc_function_set_alt; - dev->function.disable = acc_function_disable; - - return usb_add_function(c, &dev->function); -} - static int acc_setup(void) { struct acc_dev *dev; @@ -1179,11 +1170,12 @@ static int acc_setup(void) return ret; } -static void acc_disconnect(void) +void acc_disconnect(void) { /* unregister all HID devices if USB is disconnected */ kill_all_hid_devices(_acc_dev); } +EXPORT_SYMBOL_GPL(acc_disconnect); static void acc_cleanup(void) { @@ -1191,3 +1183,117 @@ static void acc_cleanup(void) kfree(_acc_dev); _acc_dev = NULL; } +static struct acc_instance *to_acc_instance(struct config_item *item) +{ + return container_of(to_config_group(item), struct acc_instance, + func_inst.group); +} + +static void acc_attr_release(struct config_item *item) +{ + struct acc_instance *fi_acc = to_acc_instance(item); + + usb_put_function_instance(&fi_acc->func_inst); +} + +static struct configfs_item_operations acc_item_ops = { + .release = acc_attr_release, +}; + +static struct config_item_type acc_func_type = { + .ct_item_ops = &acc_item_ops, + .ct_owner = THIS_MODULE, +}; + +static struct acc_instance *to_fi_acc(struct usb_function_instance *fi) +{ + return container_of(fi, struct acc_instance, func_inst); +} + +static int acc_set_inst_name(struct usb_function_instance *fi, const char *name) +{ + struct acc_instance *fi_acc; + char *ptr; + int name_len; + + name_len = strlen(name) + 1; + if (name_len > MAX_INST_NAME_LEN) + return -ENAMETOOLONG; + + ptr = kstrndup(name, name_len, GFP_KERNEL); + if (!ptr) + return -ENOMEM; + + fi_acc = to_fi_acc(fi); + fi_acc->name = ptr; + return 0; +} + +static void acc_free_inst(struct usb_function_instance *fi) +{ + struct acc_instance *fi_acc; + + fi_acc = to_fi_acc(fi); + kfree(fi_acc->name); + acc_cleanup(); +} + +static struct usb_function_instance *acc_alloc_inst(void) +{ + struct acc_instance *fi_acc; + struct acc_dev *dev; + int err; + + fi_acc = kzalloc(sizeof(*fi_acc), GFP_KERNEL); + if (!fi_acc) + return ERR_PTR(-ENOMEM); + fi_acc->func_inst.set_inst_name = acc_set_inst_name; + fi_acc->func_inst.free_func_inst = acc_free_inst; + + err = acc_setup(); + if (err) { + kfree(fi_acc); + pr_err("Error setting ACCESSORY\n"); + return ERR_PTR(err); + } + + config_group_init_type_name(&fi_acc->func_inst.group, + "", &acc_func_type); + dev = _acc_dev; + return &fi_acc->func_inst; +} + +static void acc_free(struct usb_function *f) +{ +/*NO-OP: no function specific resource allocation in mtp_alloc*/ +} + +int acc_ctrlrequest_configfs(struct usb_function *f, + const struct usb_ctrlrequest *ctrl) { + if (f->config != NULL && f->config->cdev != NULL) + return acc_ctrlrequest(f->config->cdev, ctrl); + else + return -1; +} + +static struct usb_function *acc_alloc(struct usb_function_instance *fi) +{ + struct acc_dev *dev = _acc_dev; + + pr_info("acc_alloc\n"); + + dev->function.name = "accessory"; + dev->function.strings = acc_strings, + dev->function.fs_descriptors = fs_acc_descs; + dev->function.hs_descriptors = hs_acc_descs; + dev->function.bind = acc_function_bind_configfs; + dev->function.unbind = acc_function_unbind; + dev->function.set_alt = acc_function_set_alt; + dev->function.disable = acc_function_disable; + dev->function.free_func = acc_free; + dev->function.setup = acc_ctrlrequest_configfs; + + return &dev->function; +} +DECLARE_USB_FUNCTION_INIT(accessory, acc_alloc_inst, acc_alloc); +MODULE_LICENSE("GPL"); From 2fbd6aa3831b767ae4d6162b5f24f9628ca7fec3 Mon Sep 17 00:00:00 2001 From: Badhri Jagan Sridharan Date: Mon, 30 Mar 2015 15:32:22 -0700 Subject: [PATCH 0253/1103] ANDROID: usb: gadget: f_accessory: Move gadget functions code 3.18 kernel has reorganized drivers/usb/gadget directory. Moving accessory gadget driver from drivers/usb/gadget to drivers/usb/gadget/function Signed-off-by: Badhri Jagan Sridharan Change-Id: If73c6df0537c4b1f51338ed3b0db817e51f06b4a [AmitP: Refactored the original changes after squashing of related patches] Signed-off-by: Amit Pundir --- drivers/usb/gadget/Makefile | 3 --- drivers/usb/gadget/function/Makefile | 2 ++ drivers/usb/gadget/{ => function}/f_accessory.c | 0 3 files changed, 2 insertions(+), 3 deletions(-) rename drivers/usb/gadget/{ => function}/f_accessory.c (100%) diff --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile index 92dd916df04d..130dad7130b6 100644 --- a/drivers/usb/gadget/Makefile +++ b/drivers/usb/gadget/Makefile @@ -10,7 +10,4 @@ obj-$(CONFIG_USB_LIBCOMPOSITE) += libcomposite.o libcomposite-y := usbstring.o config.o epautoconf.o libcomposite-y += composite.o functions.o configfs.o u_f.o -usb_f_accessory-y := f_accessory.o -obj-$(CONFIG_USB_F_ACC) += usb_f_accessory.o - obj-$(CONFIG_USB_GADGET) += udc/ function/ legacy/ diff --git a/drivers/usb/gadget/function/Makefile b/drivers/usb/gadget/function/Makefile index 42a0b6ef39da..d7d5673d8343 100644 --- a/drivers/usb/gadget/function/Makefile +++ b/drivers/usb/gadget/function/Makefile @@ -52,3 +52,5 @@ usb_f_tcm-y := f_tcm.o obj-$(CONFIG_USB_F_TCM) += usb_f_tcm.o usb_f_audio_source-y := f_audio_source.o obj-$(CONFIG_USB_F_AUDIO_SRC) += usb_f_audio_source.o +usb_f_accessory-y := f_accessory.o +obj-$(CONFIG_USB_F_ACC) += usb_f_accessory.o diff --git a/drivers/usb/gadget/f_accessory.c b/drivers/usb/gadget/function/f_accessory.c similarity index 100% rename from drivers/usb/gadget/f_accessory.c rename to drivers/usb/gadget/function/f_accessory.c From 6a5fdf752bce52371dea453dae26d3e209a586f3 Mon Sep 17 00:00:00 2001 From: keunyoung Date: Wed, 29 Jan 2014 12:41:50 -0800 Subject: [PATCH 0254/1103] ANDROID: usb: gadget: f_accessory: fix false disconnect due to a signal sent to the reading process - In the current implementation, when a signal is sent to the reading process, read is cancelled by calling usb_ep_dequeue, which lead into calling acc_complete_out with ECONNRESET, but the current logic treats it as disconnection, which makes the device inaccessible until cable is actually disconnected. - The fix calls disconnect only when ESHUTDOWN error is passed. - If data has already arrived while trying cancelling, the data is marked as available, and it will be read out on the next read. This is necessary as USB bulk is assumed to guarantee no data loss. Signed-off-by: keunyoung --- drivers/usb/gadget/function/f_accessory.c | 32 +++++++++++++++++++---- 1 file changed, 27 insertions(+), 5 deletions(-) diff --git a/drivers/usb/gadget/function/f_accessory.c b/drivers/usb/gadget/function/f_accessory.c index ef1ffcf88cdc..efb22a3ffa3f 100644 --- a/drivers/usb/gadget/function/f_accessory.c +++ b/drivers/usb/gadget/function/f_accessory.c @@ -271,8 +271,10 @@ static void acc_complete_in(struct usb_ep *ep, struct usb_request *req) { struct acc_dev *dev = _acc_dev; - if (req->status != 0) + if (req->status == -ESHUTDOWN) { + pr_debug("acc_complete_in set disconnected"); acc_set_disconnected(dev); + } req_put(dev, &dev->tx_idle, req); @@ -284,8 +286,10 @@ static void acc_complete_out(struct usb_ep *ep, struct usb_request *req) struct acc_dev *dev = _acc_dev; dev->rx_done = 1; - if (req->status != 0) + if (req->status == -ESHUTDOWN) { + pr_debug("acc_complete_out set disconnected"); acc_set_disconnected(dev); + } wake_up(&dev->read_wq); } @@ -559,8 +563,10 @@ static ssize_t acc_read(struct file *fp, char __user *buf, pr_debug("acc_read(%zu)\n", count); - if (dev->disconnected) + if (dev->disconnected) { + pr_debug("acc_read disconnected"); return -ENODEV; + } if (count > BULK_BUFFER_SIZE) count = BULK_BUFFER_SIZE; @@ -573,6 +579,12 @@ static ssize_t acc_read(struct file *fp, char __user *buf, goto done; } + if (dev->rx_done) { + // last req cancelled. try to get it. + req = dev->rx_req[0]; + goto copy_data; + } + requeue_req: /* queue a request */ req = dev->rx_req[0]; @@ -590,9 +602,17 @@ static ssize_t acc_read(struct file *fp, char __user *buf, ret = wait_event_interruptible(dev->read_wq, dev->rx_done); if (ret < 0) { r = ret; - usb_ep_dequeue(dev->ep_out, req); + ret = usb_ep_dequeue(dev->ep_out, req); + if (ret != 0) { + // cancel failed. There can be a data already received. + // it will be retrieved in the next read. + pr_debug("acc_read: cancelling failed %d", ret); + } goto done; } + +copy_data: + dev->rx_done = 0; if (dev->online) { /* If we got a 0-len packet, throw it back and try again. */ if (req->actual == 0) @@ -622,8 +642,10 @@ static ssize_t acc_write(struct file *fp, const char __user *buf, pr_debug("acc_write(%zu)\n", count); - if (!dev->online || dev->disconnected) + if (!dev->online || dev->disconnected) { + pr_debug("acc_write disconnected or not online"); return -ENODEV; + } while (count > 0) { if (!dev->online) { From 5bce6a39975b8207a98b1e65271a7294d699111d Mon Sep 17 00:00:00 2001 From: Anson Jacob Date: Fri, 12 Aug 2016 20:38:10 -0400 Subject: [PATCH 0255/1103] ANDROID: usb: gadget: f_accessory: Fix for UsbAccessory clean unbind. Reapplying fix by Darren Whobrey (Change 69674) Fixes issues: 20545, 59667 and 61390. With prior version of f_accessory.c, UsbAccessories would not unbind cleanly when application is closed or i/o stopped while the usb cable is still connected. The accessory gadget driver would be left in an invalid state which was not reset on subsequent binding or opening. A reboot was necessary to clear. In some phones this issues causes the phone to reboot upon unplugging the USB cable. Main problem was that acc_disconnect was being called on I/O error which reset disconnected and online. Minor fix required to properly track setting and unsetting of disconnected and online flags. Also added urb Q wakeup's on unbind to help unblock waiting threads. Tested on Nexus 7 grouper. Expected behaviour now observed: closing accessory causes blocked i/o to interrupt with IOException. Accessory can be restarted following closing of file handle and re-opening. This is a generic fix that applies to all devices. Change-Id: I4e08b326730dd3a2820c863124cee10f7cb5501e Signed-off-by: Darren Whobrey Signed-off-by: Anson Jacob --- drivers/usb/gadget/function/f_accessory.c | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/drivers/usb/gadget/function/f_accessory.c b/drivers/usb/gadget/function/f_accessory.c index efb22a3ffa3f..e0a677a44763 100644 --- a/drivers/usb/gadget/function/f_accessory.c +++ b/drivers/usb/gadget/function/f_accessory.c @@ -77,9 +77,13 @@ struct acc_dev { struct usb_ep *ep_in; struct usb_ep *ep_out; - /* set to 1 when we connect */ + /* online indicates state of function_set_alt & function_unbind + * set to 1 when we connect + */ int online:1; - /* Set to 1 when we disconnect. + + /* disconnected indicates state of open & release + * Set to 1 when we disconnect. * Not cleared until our file is closed. */ int disconnected:1; @@ -263,7 +267,6 @@ static struct usb_request *req_get(struct acc_dev *dev, struct list_head *head) static void acc_set_disconnected(struct acc_dev *dev) { - dev->online = 0; dev->disconnected = 1; } @@ -756,7 +759,10 @@ static int acc_release(struct inode *ip, struct file *fp) printk(KERN_INFO "acc_release\n"); WARN_ON(!atomic_xchg(&_acc_dev->open_excl, 0)); - _acc_dev->disconnected = 0; + /* indicate that we are disconnected + * still could be online so don't touch online flag + */ + _acc_dev->disconnected = 1; return 0; } @@ -1004,6 +1010,10 @@ acc_function_unbind(struct usb_configuration *c, struct usb_function *f) struct usb_request *req; int i; + dev->online = 0; /* clear online flag */ + wake_up(&dev->read_wq); /* unblock reads on closure */ + wake_up(&dev->write_wq); /* likewise for writes */ + while ((req = req_get(dev, &dev->tx_idle))) acc_request_free(req, dev->ep_in); for (i = 0; i < RX_REQ_MAX; i++) @@ -1135,6 +1145,7 @@ static int acc_function_set_alt(struct usb_function *f, } dev->online = 1; + dev->disconnected = 0; /* if online then not disconnected */ /* readers may be blocked waiting for us to go online */ wake_up(&dev->read_wq); @@ -1147,7 +1158,8 @@ static void acc_function_disable(struct usb_function *f) struct usb_composite_dev *cdev = dev->cdev; DBG(cdev, "acc_function_disable\n"); - acc_set_disconnected(dev); + acc_set_disconnected(dev); /* this now only sets disconnected */ + dev->online = 0; /* so now need to clear online flag here too */ usb_ep_disable(dev->ep_in); usb_ep_disable(dev->ep_out); From 5cab9c5bd227d1e5dcbe24dc466e37029750eaf1 Mon Sep 17 00:00:00 2001 From: Badhri Jagan Sridharan Date: Fri, 21 Jul 2017 14:58:16 -0700 Subject: [PATCH 0256/1103] ANDROID: usb: gadget: f_accessory: assign no-op request complete callbacks The req->complete seems to presist the callback pointer for the control requests. This causes the serial of the accessory to be overridden when an accessory function specific out control request is issued right after the ACCESSORY_SEND_STRING control request. Therefore, assign a no-op req complete function when nothing needs to be done once the request is completed. Signed-off-by: Badhri Jagan Sridharan Bug: 63867169 Change-Id: I78c1602e9a044b8718b270b8a068cf5afc83f984 --- drivers/usb/gadget/function/f_accessory.c | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/drivers/usb/gadget/function/f_accessory.c b/drivers/usb/gadget/function/f_accessory.c index e0a677a44763..b22b77c8aef4 100644 --- a/drivers/usb/gadget/function/f_accessory.c +++ b/drivers/usb/gadget/function/f_accessory.c @@ -804,6 +804,14 @@ static struct hid_driver acc_hid_driver = { .probe = acc_hid_probe, }; +static void acc_complete_setup_noop(struct usb_ep *ep, struct usb_request *req) +{ + /* + * Default no-op function when nothing needs to be done for the + * setup request + */ +} + int acc_ctrlrequest(struct usb_composite_dev *cdev, const struct usb_ctrlrequest *ctrl) { @@ -831,6 +839,7 @@ int acc_ctrlrequest(struct usb_composite_dev *cdev, schedule_delayed_work( &dev->start_work, msecs_to_jiffies(10)); value = 0; + cdev->req->complete = acc_complete_setup_noop; } else if (b_request == ACCESSORY_SEND_STRING) { dev->string_index = w_index; cdev->gadget->ep0->driver_data = dev; @@ -839,10 +848,13 @@ int acc_ctrlrequest(struct usb_composite_dev *cdev, } else if (b_request == ACCESSORY_SET_AUDIO_MODE && w_index == 0 && w_length == 0) { dev->audio_mode = w_value; + cdev->req->complete = acc_complete_setup_noop; value = 0; } else if (b_request == ACCESSORY_REGISTER_HID) { + cdev->req->complete = acc_complete_setup_noop; value = acc_register_hid(dev, w_value, w_index); } else if (b_request == ACCESSORY_UNREGISTER_HID) { + cdev->req->complete = acc_complete_setup_noop; value = acc_unregister_hid(dev, w_value); } else if (b_request == ACCESSORY_SET_HID_REPORT_DESC) { spin_lock_irqsave(&dev->lock, flags); @@ -877,7 +889,7 @@ int acc_ctrlrequest(struct usb_composite_dev *cdev, if (b_request == ACCESSORY_GET_PROTOCOL) { *((u16 *)cdev->req->buf) = PROTOCOL_VERSION; value = sizeof(u16); - + cdev->req->complete = acc_complete_setup_noop; /* clear any string left over from a previous session */ memset(dev->manufacturer, 0, sizeof(dev->manufacturer)); memset(dev->model, 0, sizeof(dev->model)); From 9afe30b74fae79b0712be3e51eeea5bddc920095 Mon Sep 17 00:00:00 2001 From: Badhri Jagan Sridharan Date: Wed, 2 Sep 2015 22:49:10 -0700 Subject: [PATCH 0257/1103] ANDROID: usb: gadget: f_midi: create F_midi device Android frameworks relies on the alsa config reported by the f_midi device. Signed-off-by: Badhri Jagan Sridharan Change-Id: I0695e00b166fd953f50acea93802245b0d5a5240 --- drivers/usb/gadget/function/f_midi.c | 65 ++++++++++++++++++++++++++++ 1 file changed, 65 insertions(+) diff --git a/drivers/usb/gadget/function/f_midi.c b/drivers/usb/gadget/function/f_midi.c index 46af0aa07e2e..2a86b3cf313f 100644 --- a/drivers/usb/gadget/function/f_midi.c +++ b/drivers/usb/gadget/function/f_midi.c @@ -1216,6 +1216,65 @@ static void f_midi_free_inst(struct usb_function_instance *f) } } +#ifdef CONFIG_USB_CONFIGFS_UEVENT +extern struct device *create_function_device(char *name); +static ssize_t alsa_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct usb_function_instance *fi_midi = dev_get_drvdata(dev); + struct f_midi *midi; + + if (!fi_midi->f) + dev_warn(dev, "f_midi: function not set\n"); + + if (fi_midi && fi_midi->f) { + midi = func_to_midi(fi_midi->f); + if (midi->rmidi && midi->rmidi->card) + return sprintf(buf, "%d %d\n", + midi->rmidi->card->number, midi->rmidi->device); + } + + /* print PCM card and device numbers */ + return sprintf(buf, "%d %d\n", -1, -1); +} + +static DEVICE_ATTR(alsa, S_IRUGO, alsa_show, NULL); + +static struct device_attribute *alsa_function_attributes[] = { + &dev_attr_alsa, + NULL +}; + +static int create_alsa_device(struct usb_function_instance *fi) +{ + struct device *dev; + struct device_attribute **attrs; + struct device_attribute *attr; + int err = 0; + + dev = create_function_device("f_midi"); + if (IS_ERR(dev)) + return PTR_ERR(dev); + + attrs = alsa_function_attributes; + if (attrs) { + while ((attr = *attrs++) && !err) + err = device_create_file(dev, attr); + if (err) { + device_destroy(dev->class, dev->devt); + return -EINVAL; + } + } + dev_set_drvdata(dev, fi); + return 0; +} +#else +static int create_alsa_device(struct usb_function_instance *fi) +{ + return 0; +} +#endif + static struct usb_function_instance *f_midi_alloc_inst(void) { struct f_midi_opts *opts; @@ -1234,6 +1293,11 @@ static struct usb_function_instance *f_midi_alloc_inst(void) opts->out_ports = 1; opts->refcnt = 1; + if (create_alsa_device(&opts->func_inst)) { + kfree(opts); + return ERR_PTR(-ENODEV); + } + config_group_init_type_name(&opts->func_inst.group, "", &midi_func_type); @@ -1341,6 +1405,7 @@ static struct usb_function *f_midi_alloc(struct usb_function_instance *fi) midi->func.disable = f_midi_disable; midi->func.free_func = f_midi_free; + fi->f = &midi->func; return &midi->func; setup_fail: From 9281c5559f9ea8f458171cba52d39fd51974624b Mon Sep 17 00:00:00 2001 From: Winter Wang Date: Fri, 20 May 2016 11:05:00 +0800 Subject: [PATCH 0258/1103] ANDROID: usb: gadget: f_midi: set fi->f to NULL when free f_midi function fi->f is set in f_midi's alloc_func, need to clean this to NULL in free_func, otherwise on ConfigFS's function switch, midi->usb_function it self is freed, fi->f will be a wild pointer and run into below kernel panic: --------------- [ 58.950628] Unable to handle kernel paging request at virtual address 63697664 [ 58.957869] pgd = c0004000 [ 58.960583] [63697664] *pgd=00000000 [ 58.964185] Internal error: Oops: 80000005 [#1] PREEMPT SMP ARM [ 58.970111] Modules linked in: [ 58.973191] CPU: 0 PID: 0 Comm: swapper/0 Not tainted 4.1.15-03504-g34c857c-dirty #89 [ 58.981024] Hardware name: Freescale i.MX6 Quad/DualLite (Device Tree) [ 58.987557] task: c110bd70 ti: c1100000 task.ti: c1100000 [ 58.992962] PC is at 0x63697664 [ 58.996120] LR is at android_setup+0x78/0x138 <..snip..> [ 60.044980] 1fc0: ffffffff ffffffff c1000684 00000000 00000000 c108ecd0 c11f7294 c11039c0 [ 60.053181] 1fe0: c108eccc c110d148 1000406a 412fc09a 00000000 1000807c 00000000 00000000 [ 60.061420] [] (android_setup) from [] (udc_irq+0x758/0x1034) [ 60.068951] [] (udc_irq) from [] (handle_irq_event_percpu+0x50/0x254) [ 60.077165] [] (handle_irq_event_percpu) from [] (handle_irq_event+0x3c/0x5c) [ 60.086072] [] (handle_irq_event) from [] (handle_fasteoi_irq+0xe0/0x198) [ 60.094630] [] (handle_fasteoi_irq) from [] (generic_handle_irq+0x2c/0x3c) [ 60.103271] [] (generic_handle_irq) from [] (__handle_domain_irq+0x7c/0xec) [ 60.112000] [] (__handle_domain_irq) from [] (gic_handle_irq+0x24/0x5c) -------------- Signed-off-by: Winter Wang --- drivers/usb/gadget/function/f_midi.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/usb/gadget/function/f_midi.c b/drivers/usb/gadget/function/f_midi.c index 2a86b3cf313f..4713a1c7f622 100644 --- a/drivers/usb/gadget/function/f_midi.c +++ b/drivers/usb/gadget/function/f_midi.c @@ -1318,6 +1318,7 @@ static void f_midi_free(struct usb_function *f) kfifo_free(&midi->in_req_fifo); kfree(midi); free = true; + opts->func_inst.f = NULL; } mutex_unlock(&opts->lock); From ac0b4079b76239e0708461c7cb98d06ee06c5732 Mon Sep 17 00:00:00 2001 From: Mark Kuo Date: Mon, 11 Jan 2016 17:49:16 +0800 Subject: [PATCH 0259/1103] CHROMIUM: usb: gadget: f_audio_source: add .free_func callback When userspace unbinds gadget functions through configfs, the .free_func() callback is always invoked. (in config_usb_cfg_unlink()) Implement it as a no-op to avoid the following crash: [ 68.125679] configfs-gadget gadget: unbind function 'accessory'/ffffffc0720bf000 [ 68.133202] configfs-gadget gadget: unbind function 'audio_source'/ffffffc0012ca3c0 [ 68.142668] tegra-xudc 700d0000.usb-device: ep 0 disabled [ 68.148186] Bad mode in Synchronous Abort handler detected, code 0x86000006 [ 68.155144] CPU: 2 PID: 1 Comm: init Tainted: G U W 3.18.0-09419-g87296c3-dirty #561 [ 68.163743] Hardware name: Google Tegra210 Smaug Rev 1,3+ (DT) [ 68.169566] task: ffffffc0bc8d0000 ti: ffffffc0bc8bc000 task.ti: ffffffc0bc8bc000 [ 68.177039] PC is at 0x0 [ 68.179577] LR is at usb_put_function+0x14/0x1c .... BUG=chrome-os-partner:49140 TEST="setprop sys.usb.config accessory,audio_source" on A44 and then switch back to default: "setprop sys.usb.config mtp,adb", no crash will be seen. Change-Id: I5b6141964aab861e86e3afb139ded02d4d122dab Signed-off-by: Mark Kuo Reviewed-on: https://chromium-review.googlesource.com/321013 Commit-Ready: Andrew Bresticker Tested-by: Andrew Bresticker Reviewed-by: Andrew Bresticker --- drivers/usb/gadget/function/f_audio_source.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/usb/gadget/function/f_audio_source.c b/drivers/usb/gadget/function/f_audio_source.c index 3cf60609a83e..8124af33b738 100644 --- a/drivers/usb/gadget/function/f_audio_source.c +++ b/drivers/usb/gadget/function/f_audio_source.c @@ -586,6 +586,11 @@ static void audio_disable(struct usb_function *f) usb_ep_disable(audio->in_ep); } +static void audio_free_func(struct usb_function *f) +{ + /* no-op */ +} + /*-------------------------------------------------------------------------*/ static void audio_build_desc(struct audio_dev *audio) @@ -838,6 +843,7 @@ static struct audio_dev _audio_dev = { .set_alt = audio_set_alt, .setup = audio_setup, .disable = audio_disable, + .free_func = audio_free_func, }, .lock = __SPIN_LOCK_UNLOCKED(_audio_dev.lock), .idle_reqs = LIST_HEAD_INIT(_audio_dev.idle_reqs), From 03462dad65fae7bdf797ecc87201b1bfbc223aa1 Mon Sep 17 00:00:00 2001 From: Mark Kuo Date: Mon, 11 Jan 2016 19:07:12 +0800 Subject: [PATCH 0260/1103] CHROMIUM: usb: gadget: f_accessory: add .raw_request callback After this upstream commit: 3c86726cfe38952f0366f86acfbbb025813ec1c2, .raw_request is mandatory in hid_ll_driver structure, hence add an empty raw_request() function. BUG=chrome-os-partner:49140 TEST=none Change-Id: Idd0bbe6960aad2c557376e4a24827d7e1df8e023 Signed-off-by: Mark Kuo Reviewed-on: https://chromium-review.googlesource.com/321038 Commit-Ready: Andrew Bresticker Tested-by: Andrew Bresticker Reviewed-by: Andrew Bresticker --- drivers/usb/gadget/function/f_accessory.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/usb/gadget/function/f_accessory.c b/drivers/usb/gadget/function/f_accessory.c index b22b77c8aef4..7aa2656a2328 100644 --- a/drivers/usb/gadget/function/f_accessory.c +++ b/drivers/usb/gadget/function/f_accessory.c @@ -408,12 +408,19 @@ static void acc_hid_close(struct hid_device *hid) { } +static int acc_hid_raw_request(struct hid_device *hid, unsigned char reportnum, + __u8 *buf, size_t len, unsigned char rtype, int reqtype) +{ + return 0; +} + static struct hid_ll_driver acc_hid_ll_driver = { .parse = acc_hid_parse, .start = acc_hid_start, .stop = acc_hid_stop, .open = acc_hid_open, .close = acc_hid_close, + .raw_request = acc_hid_raw_request, }; static struct acc_hid_dev *acc_hid_new(struct acc_dev *dev, From 7618bad1ff921dfb6285ba2760daea4ea57ba738 Mon Sep 17 00:00:00 2001 From: jinqian Date: Wed, 11 Mar 2015 10:44:50 -0700 Subject: [PATCH 0261/1103] ANDROID: uid_cputime: Adds accounting for the cputimes per uid. Adds proc files /proc/uid_cputime/show_uid_stat and /proc/uid_cputime/remove_uid_range. show_uid_stat lists the total utime and stime for the active as well as terminated processes for each of the uids. Writing a range of uids to remove_uid_range will delete the accounting for all the uids within that range. Change-Id: I21d9210379da730b33ddc1a0ea663c8c9d2ac15b [AmitP: Refactored the original patch because upstream commit 605dc2b31a2a ("tsacct: Convert obsolete cputime type to nsecs") made cputime_t type obsolete, so use u64 nanoseconds directly instead. Also folded following android-4.9 changes into this patch 48a9906c0fd8 ("ANDROID: proc: uid_cputime: create uids from kuids") 453ac31cab34 ("ANDROID: proc: uid_cputime: fix show_uid_stat permission")] Signed-off-by: Amit Pundir --- drivers/misc/Kconfig | 6 + drivers/misc/Makefile | 1 + drivers/misc/uid_cputime.c | 237 +++++++++++++++++++++++++++++++++++++ 3 files changed, 244 insertions(+) create mode 100644 drivers/misc/uid_cputime.c diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig index 3726eacdf65d..9cb0c65577ec 100644 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig @@ -513,6 +513,12 @@ config MISC_RTSX tristate default MISC_RTSX_PCI || MISC_RTSX_USB +config UID_CPUTIME + bool "Per-UID cpu time statistics" + depends on PROFILING + help + Per UID based cpu time statistics exported to /proc/uid_cputime + source "drivers/misc/c2port/Kconfig" source "drivers/misc/eeprom/Kconfig" source "drivers/misc/cb710/Kconfig" diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile index af22bbc3d00c..d3dd7eb910d2 100644 --- a/drivers/misc/Makefile +++ b/drivers/misc/Makefile @@ -58,3 +58,4 @@ obj-$(CONFIG_ASPEED_LPC_SNOOP) += aspeed-lpc-snoop.o obj-$(CONFIG_PCI_ENDPOINT_TEST) += pci_endpoint_test.o obj-$(CONFIG_OCXL) += ocxl/ obj-$(CONFIG_MISC_RTSX) += cardreader/ +obj-$(CONFIG_UID_CPUTIME) += uid_cputime.o diff --git a/drivers/misc/uid_cputime.c b/drivers/misc/uid_cputime.c new file mode 100644 index 000000000000..e0f0b5cb0baa --- /dev/null +++ b/drivers/misc/uid_cputime.c @@ -0,0 +1,237 @@ +/* drivers/misc/uid_cputime.c + * + * Copyright (C) 2014 - 2015 Google, Inc. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define UID_HASH_BITS 10 +DECLARE_HASHTABLE(hash_table, UID_HASH_BITS); + +static DEFINE_MUTEX(uid_lock); +static struct proc_dir_entry *parent; + +struct uid_entry { + uid_t uid; + u64 utime; + u64 stime; + u64 active_utime; + u64 active_stime; + struct hlist_node hash; +}; + +static struct uid_entry *find_uid_entry(uid_t uid) +{ + struct uid_entry *uid_entry; + hash_for_each_possible(hash_table, uid_entry, hash, uid) { + if (uid_entry->uid == uid) + return uid_entry; + } + return NULL; +} + +static struct uid_entry *find_or_register_uid(uid_t uid) +{ + struct uid_entry *uid_entry; + + uid_entry = find_uid_entry(uid); + if (uid_entry) + return uid_entry; + + uid_entry = kzalloc(sizeof(struct uid_entry), GFP_ATOMIC); + if (!uid_entry) + return NULL; + + uid_entry->uid = uid; + + hash_add(hash_table, &uid_entry->hash, uid); + + return uid_entry; +} + +static int uid_stat_show(struct seq_file *m, void *v) +{ + struct uid_entry *uid_entry; + struct task_struct *task; + u64 utime; + u64 stime; + unsigned long bkt; + + mutex_lock(&uid_lock); + + hash_for_each(hash_table, bkt, uid_entry, hash) { + uid_entry->active_stime = 0; + uid_entry->active_utime = 0; + } + + read_lock(&tasklist_lock); + for_each_process(task) { + uid_entry = find_or_register_uid(from_kuid_munged( + current_user_ns(), task_uid(task))); + if (!uid_entry) { + read_unlock(&tasklist_lock); + mutex_unlock(&uid_lock); + pr_err("%s: failed to find the uid_entry for uid %d\n", + __func__, from_kuid_munged(current_user_ns(), + task_uid(task))); + return -ENOMEM; + } + task_cputime_adjusted(task, &utime, &stime); + uid_entry->active_utime += utime; + uid_entry->active_stime += stime; + } + read_unlock(&tasklist_lock); + + hash_for_each(hash_table, bkt, uid_entry, hash) { + u64 total_utime = uid_entry->utime + + uid_entry->active_utime; + u64 total_stime = uid_entry->stime + + uid_entry->active_stime; + seq_printf(m, "%d: %u %u\n", uid_entry->uid, + cputime_to_usecs(total_utime), + cputime_to_usecs(total_stime)); + } + + mutex_unlock(&uid_lock); + return 0; +} + +static int uid_stat_open(struct inode *inode, struct file *file) +{ + return single_open(file, uid_stat_show, PDE_DATA(inode)); +} + +static const struct file_operations uid_stat_fops = { + .open = uid_stat_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static int uid_remove_open(struct inode *inode, struct file *file) +{ + return single_open(file, NULL, NULL); +} + +static ssize_t uid_remove_write(struct file *file, + const char __user *buffer, size_t count, loff_t *ppos) +{ + struct uid_entry *uid_entry; + struct hlist_node *tmp; + char uids[128]; + char *start_uid, *end_uid = NULL; + long int uid_start = 0, uid_end = 0; + + if (count >= sizeof(uids)) + count = sizeof(uids) - 1; + + if (copy_from_user(uids, buffer, count)) + return -EFAULT; + + uids[count] = '\0'; + end_uid = uids; + start_uid = strsep(&end_uid, "-"); + + if (!start_uid || !end_uid) + return -EINVAL; + + if (kstrtol(start_uid, 10, &uid_start) != 0 || + kstrtol(end_uid, 10, &uid_end) != 0) { + return -EINVAL; + } + + mutex_lock(&uid_lock); + + for (; uid_start <= uid_end; uid_start++) { + hash_for_each_possible_safe(hash_table, uid_entry, tmp, + hash, uid_start) { + hash_del(&uid_entry->hash); + kfree(uid_entry); + } + } + + mutex_unlock(&uid_lock); + return count; +} + +static const struct file_operations uid_remove_fops = { + .open = uid_remove_open, + .release = single_release, + .write = uid_remove_write, +}; + +static int process_notifier(struct notifier_block *self, + unsigned long cmd, void *v) +{ + struct task_struct *task = v; + struct uid_entry *uid_entry; + u64 utime, stime; + uid_t uid; + + if (!task) + return NOTIFY_OK; + + mutex_lock(&uid_lock); + uid = from_kuid_munged(current_user_ns(), task_uid(task)); + uid_entry = find_or_register_uid(uid); + if (!uid_entry) { + pr_err("%s: failed to find uid %d\n", __func__, uid); + goto exit; + } + + task_cputime_adjusted(task, &utime, &stime); + uid_entry->utime += utime; + uid_entry->stime += stime; + +exit: + mutex_unlock(&uid_lock); + return NOTIFY_OK; +} + +static struct notifier_block process_notifier_block = { + .notifier_call = process_notifier, +}; + +static int __init proc_uid_cputime_init(void) +{ + hash_init(hash_table); + + parent = proc_mkdir("uid_cputime", NULL); + if (!parent) { + pr_err("%s: failed to create proc entry\n", __func__); + return -ENOMEM; + } + + proc_create_data("remove_uid_range", S_IWUGO, parent, &uid_remove_fops, + NULL); + + proc_create_data("show_uid_stat", S_IRUGO, parent, &uid_stat_fops, + NULL); + + profile_event_register(PROFILE_TASK_EXIT, &process_notifier_block); + + return 0; +} + +early_initcall(proc_uid_cputime_init); From 874312db5aa93a9498a1164f34121d23a15c0f2f Mon Sep 17 00:00:00 2001 From: Jin Qian Date: Mon, 13 Jul 2015 18:16:55 -0700 Subject: [PATCH 0262/1103] ANDROID: uid_cputime: fix cputime overflow Converting cputime_t to usec caused overflow when the value is greater than 1 hour. Use msec and convert to unsigned long long to support bigger range. Bug: 22461683 Change-Id: I853fe3e8e7dbf0d3e2cc5c6f9688a5a6e1f1fb3e Signed-off-by: Jin Qian [AmitP: Refactored the original changes because cputime_to_jiffies() is obsolete and removed in upstream commit f22d6df0b23e ("sched/cputime: Remove jiffies based cputime")] Signed-off-by: Amit Pundir --- drivers/misc/uid_cputime.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/misc/uid_cputime.c b/drivers/misc/uid_cputime.c index e0f0b5cb0baa..0df8d70b4e12 100644 --- a/drivers/misc/uid_cputime.c +++ b/drivers/misc/uid_cputime.c @@ -108,9 +108,8 @@ static int uid_stat_show(struct seq_file *m, void *v) uid_entry->active_utime; u64 total_stime = uid_entry->stime + uid_entry->active_stime; - seq_printf(m, "%d: %u %u\n", uid_entry->uid, - cputime_to_usecs(total_utime), - cputime_to_usecs(total_stime)); + seq_printf(m, "%d: %llu %llu\n", uid_entry->uid, + ktime_to_ms(total_utime), ktime_to_ms(total_stime)); } mutex_unlock(&uid_lock); From 74186d6de83b9f4901ce15cfbf84eb6ab925cd07 Mon Sep 17 00:00:00 2001 From: Ruchi Kandoi Date: Fri, 31 Jul 2015 10:17:54 -0700 Subject: [PATCH 0263/1103] ANDROID: uid_cputime: Iterates over all the threads instead of processes. Bug: 22833116 Change-Id: I775a18f61bd2f4df2bec23d01bd49421d0969f87 Signed-off-by: Ruchi Kandoi --- drivers/misc/uid_cputime.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/misc/uid_cputime.c b/drivers/misc/uid_cputime.c index 0df8d70b4e12..07c8cca0b172 100644 --- a/drivers/misc/uid_cputime.c +++ b/drivers/misc/uid_cputime.c @@ -73,7 +73,7 @@ static struct uid_entry *find_or_register_uid(uid_t uid) static int uid_stat_show(struct seq_file *m, void *v) { struct uid_entry *uid_entry; - struct task_struct *task; + struct task_struct *task, *temp; u64 utime; u64 stime; unsigned long bkt; @@ -86,7 +86,7 @@ static int uid_stat_show(struct seq_file *m, void *v) } read_lock(&tasklist_lock); - for_each_process(task) { + do_each_thread(temp, task) { uid_entry = find_or_register_uid(from_kuid_munged( current_user_ns(), task_uid(task))); if (!uid_entry) { @@ -100,7 +100,7 @@ static int uid_stat_show(struct seq_file *m, void *v) task_cputime_adjusted(task, &utime, &stime); uid_entry->active_utime += utime; uid_entry->active_stime += stime; - } + } while_each_thread(temp, task); read_unlock(&tasklist_lock); hash_for_each(hash_table, bkt, uid_entry, hash) { From f8d38a6a8fa74acf1eb2a23feb49f5c8284a2cfe Mon Sep 17 00:00:00 2001 From: Ruchi Kandoi Date: Fri, 23 Oct 2015 17:49:11 -0700 Subject: [PATCH 0264/1103] ANDROID: uid_cputime: Check for the range while removing range of UIDs. Checking if the uid_entry->uid matches the uid intended to be removed will prevent deleting unwanted uid_entry. Type cast the key for the hashtable to the same size, as when they were inserted. This will make sure that we can find the uid_entry we want. Bug: 25195548 Change-Id: I567942123cfb20e4b61ad624da19ec4cc84642c1 Signed-off: Ruchi kandoi --- drivers/misc/uid_cputime.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/drivers/misc/uid_cputime.c b/drivers/misc/uid_cputime.c index 07c8cca0b172..869d7c67d81b 100644 --- a/drivers/misc/uid_cputime.c +++ b/drivers/misc/uid_cputime.c @@ -159,14 +159,15 @@ static ssize_t uid_remove_write(struct file *file, kstrtol(end_uid, 10, &uid_end) != 0) { return -EINVAL; } - mutex_lock(&uid_lock); for (; uid_start <= uid_end; uid_start++) { hash_for_each_possible_safe(hash_table, uid_entry, tmp, - hash, uid_start) { - hash_del(&uid_entry->hash); - kfree(uid_entry); + hash, (uid_t)uid_start) { + if (uid_start == uid_entry->uid) { + hash_del(&uid_entry->hash); + kfree(uid_entry); + } } } From 943c0166027180f3a45ce69acbd1a09e76cec935 Mon Sep 17 00:00:00 2001 From: Jin Qian Date: Tue, 10 Jan 2017 16:10:35 -0800 Subject: [PATCH 0265/1103] ANDROID: uid_cputime: add per-uid IO usage accounting IO usages are accounted in foreground and background buckets. For each uid, io usage is calculated in two steps. delta = current total of all uid tasks - previus total current bucket += delta Bucket is determined by current uid stat. Userspace writes to /proc/uid_procstat/set when uid stat is updated. /proc/uid_io/stats shows IO usage in this format. Signed-off-by: Jin Qian Bug: 34198239 Change-Id: Ib8bebda53e7a56f45ea3eb0ec9a3153d44188102 --- drivers/misc/uid_cputime.c | 236 ++++++++++++++++++++++++++++++++++--- 1 file changed, 220 insertions(+), 16 deletions(-) diff --git a/drivers/misc/uid_cputime.c b/drivers/misc/uid_cputime.c index 869d7c67d81b..6a61c8fb0642 100644 --- a/drivers/misc/uid_cputime.c +++ b/drivers/misc/uid_cputime.c @@ -30,7 +30,24 @@ DECLARE_HASHTABLE(hash_table, UID_HASH_BITS); static DEFINE_MUTEX(uid_lock); -static struct proc_dir_entry *parent; +static struct proc_dir_entry *cpu_parent; +static struct proc_dir_entry *io_parent; +static struct proc_dir_entry *proc_parent; + +struct io_stats { + u64 read_bytes; + u64 write_bytes; + u64 rchar; + u64 wchar; +}; + +#define UID_STATE_FOREGROUND 0 +#define UID_STATE_BACKGROUND 1 +#define UID_STATE_BUCKET_SIZE 2 + +#define UID_STATE_TOTAL_CURR 2 +#define UID_STATE_TOTAL_LAST 3 +#define UID_STATE_SIZE 4 struct uid_entry { uid_t uid; @@ -38,6 +55,8 @@ struct uid_entry { u64 stime; u64 active_utime; u64 active_stime; + int state; + struct io_stats io[UID_STATE_SIZE]; struct hlist_node hash; }; @@ -70,7 +89,7 @@ static struct uid_entry *find_or_register_uid(uid_t uid) return uid_entry; } -static int uid_stat_show(struct seq_file *m, void *v) +static int uid_cputime_show(struct seq_file *m, void *v) { struct uid_entry *uid_entry; struct task_struct *task, *temp; @@ -116,13 +135,13 @@ static int uid_stat_show(struct seq_file *m, void *v) return 0; } -static int uid_stat_open(struct inode *inode, struct file *file) +static int uid_cputime_open(struct inode *inode, struct file *file) { - return single_open(file, uid_stat_show, PDE_DATA(inode)); + return single_open(file, uid_cputime_show, PDE_DATA(inode)); } -static const struct file_operations uid_stat_fops = { - .open = uid_stat_open, +static const struct file_operations uid_cputime_fops = { + .open = uid_cputime_open, .read = seq_read, .llseek = seq_lseek, .release = single_release, @@ -181,6 +200,162 @@ static const struct file_operations uid_remove_fops = { .write = uid_remove_write, }; +static void add_uid_io_curr_stats(struct uid_entry *uid_entry, + struct task_struct *task) +{ + struct io_stats *io_curr = &uid_entry->io[UID_STATE_TOTAL_CURR]; + + io_curr->read_bytes += task->ioac.read_bytes; + io_curr->write_bytes += + task->ioac.write_bytes - task->ioac.cancelled_write_bytes; + io_curr->rchar += task->ioac.rchar; + io_curr->wchar += task->ioac.wchar; +} + +static void clean_uid_io_last_stats(struct uid_entry *uid_entry, + struct task_struct *task) +{ + struct io_stats *io_last = &uid_entry->io[UID_STATE_TOTAL_LAST]; + + io_last->read_bytes -= task->ioac.read_bytes; + io_last->write_bytes -= + task->ioac.write_bytes - task->ioac.cancelled_write_bytes; + io_last->rchar -= task->ioac.rchar; + io_last->wchar -= task->ioac.wchar; +} + +static void update_io_stats_locked(void) +{ + struct uid_entry *uid_entry; + struct task_struct *task, *temp; + struct io_stats *io_bucket, *io_curr, *io_last; + unsigned long bkt; + + BUG_ON(!mutex_is_locked(&uid_lock)); + + hash_for_each(hash_table, bkt, uid_entry, hash) + memset(&uid_entry->io[UID_STATE_TOTAL_CURR], 0, + sizeof(struct io_stats)); + + read_lock(&tasklist_lock); + do_each_thread(temp, task) { + uid_entry = find_or_register_uid(from_kuid_munged( + current_user_ns(), task_uid(task))); + if (!uid_entry) + continue; + add_uid_io_curr_stats(uid_entry, task); + } while_each_thread(temp, task); + read_unlock(&tasklist_lock); + + hash_for_each(hash_table, bkt, uid_entry, hash) { + io_bucket = &uid_entry->io[uid_entry->state]; + io_curr = &uid_entry->io[UID_STATE_TOTAL_CURR]; + io_last = &uid_entry->io[UID_STATE_TOTAL_LAST]; + + io_bucket->read_bytes += + io_curr->read_bytes - io_last->read_bytes; + io_bucket->write_bytes += + io_curr->write_bytes - io_last->write_bytes; + io_bucket->rchar += io_curr->rchar - io_last->rchar; + io_bucket->wchar += io_curr->wchar - io_last->wchar; + + io_last->read_bytes = io_curr->read_bytes; + io_last->write_bytes = io_curr->write_bytes; + io_last->rchar = io_curr->rchar; + io_last->wchar = io_curr->wchar; + } +} + +static int uid_io_show(struct seq_file *m, void *v) +{ + struct uid_entry *uid_entry; + unsigned long bkt; + + mutex_lock(&uid_lock); + + update_io_stats_locked(); + + hash_for_each(hash_table, bkt, uid_entry, hash) { + seq_printf(m, "%d %llu %llu %llu %llu %llu %llu %llu %llu\n", + uid_entry->uid, + uid_entry->io[UID_STATE_FOREGROUND].rchar, + uid_entry->io[UID_STATE_FOREGROUND].wchar, + uid_entry->io[UID_STATE_FOREGROUND].read_bytes, + uid_entry->io[UID_STATE_FOREGROUND].write_bytes, + uid_entry->io[UID_STATE_BACKGROUND].rchar, + uid_entry->io[UID_STATE_BACKGROUND].wchar, + uid_entry->io[UID_STATE_BACKGROUND].read_bytes, + uid_entry->io[UID_STATE_BACKGROUND].write_bytes); + } + + mutex_unlock(&uid_lock); + + return 0; +} + +static int uid_io_open(struct inode *inode, struct file *file) +{ + return single_open(file, uid_io_show, PDE_DATA(inode)); +} + +static const struct file_operations uid_io_fops = { + .open = uid_io_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static int uid_procstat_open(struct inode *inode, struct file *file) +{ + return single_open(file, NULL, NULL); +} + +static ssize_t uid_procstat_write(struct file *file, + const char __user *buffer, size_t count, loff_t *ppos) +{ + struct uid_entry *uid_entry; + uid_t uid; + int argc, state; + char input[128]; + + if (count >= sizeof(input)) + return -EINVAL; + + if (copy_from_user(input, buffer, count)) + return -EFAULT; + + input[count] = '\0'; + + argc = sscanf(input, "%u %d", &uid, &state); + if (argc != 2) + return -EINVAL; + + if (state != UID_STATE_BACKGROUND && state != UID_STATE_FOREGROUND) + return -EINVAL; + + mutex_lock(&uid_lock); + + uid_entry = find_or_register_uid(uid); + if (!uid_entry || uid_entry->state == state) { + mutex_unlock(&uid_lock); + return -EINVAL; + } + + update_io_stats_locked(); + + uid_entry->state = state; + + mutex_unlock(&uid_lock); + + return count; +} + +static const struct file_operations uid_procstat_fops = { + .open = uid_procstat_open, + .release = single_release, + .write = uid_procstat_write, +}; + static int process_notifier(struct notifier_block *self, unsigned long cmd, void *v) { @@ -204,6 +379,9 @@ static int process_notifier(struct notifier_block *self, uid_entry->utime += utime; uid_entry->stime += stime; + update_io_stats_locked(); + clean_uid_io_last_stats(uid_entry, task); + exit: mutex_unlock(&uid_lock); return NOTIFY_OK; @@ -213,25 +391,51 @@ static struct notifier_block process_notifier_block = { .notifier_call = process_notifier, }; -static int __init proc_uid_cputime_init(void) +static int __init proc_uid_sys_stats_init(void) { hash_init(hash_table); - parent = proc_mkdir("uid_cputime", NULL); - if (!parent) { - pr_err("%s: failed to create proc entry\n", __func__); - return -ENOMEM; + cpu_parent = proc_mkdir("uid_cputime", NULL); + if (!cpu_parent) { + pr_err("%s: failed to create uid_cputime proc entry\n", + __func__); + goto err; } - proc_create_data("remove_uid_range", S_IWUGO, parent, &uid_remove_fops, - NULL); + proc_create_data("remove_uid_range", 0222, cpu_parent, + &uid_remove_fops, NULL); + proc_create_data("show_uid_stat", 0444, cpu_parent, + &uid_cputime_fops, NULL); - proc_create_data("show_uid_stat", S_IRUGO, parent, &uid_stat_fops, - NULL); + io_parent = proc_mkdir("uid_io", NULL); + if (!io_parent) { + pr_err("%s: failed to create uid_io proc entry\n", + __func__); + goto err; + } + + proc_create_data("stats", 0444, io_parent, + &uid_io_fops, NULL); + + proc_parent = proc_mkdir("uid_procstat", NULL); + if (!proc_parent) { + pr_err("%s: failed to create uid_procstat proc entry\n", + __func__); + goto err; + } + + proc_create_data("set", 0222, proc_parent, + &uid_procstat_fops, NULL); profile_event_register(PROFILE_TASK_EXIT, &process_notifier_block); return 0; + +err: + remove_proc_subtree("uid_cputime", NULL); + remove_proc_subtree("uid_io", NULL); + remove_proc_subtree("uid_procstat", NULL); + return -ENOMEM; } -early_initcall(proc_uid_cputime_init); +early_initcall(proc_uid_sys_stats_init); From 76ebf15470435b9453af5278ef6280ff1bff67b7 Mon Sep 17 00:00:00 2001 From: Jin Qian Date: Tue, 10 Jan 2017 16:11:07 -0800 Subject: [PATCH 0266/1103] ANDROID: uid_sys_stats: rename uid_cputime.c to uid_sys_stats.c This module tracks cputime and io stats. Signed-off-by: Jin Qian Bug: 34198239 Change-Id: I9ee7d9e915431e0bb714b36b5a2282e1fdcc7342 --- drivers/misc/Kconfig | 6 ++++-- drivers/misc/Makefile | 2 +- drivers/misc/{uid_cputime.c => uid_sys_stats.c} | 0 3 files changed, 5 insertions(+), 3 deletions(-) rename drivers/misc/{uid_cputime.c => uid_sys_stats.c} (100%) diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig index 9cb0c65577ec..ca1cbe5ec2e7 100644 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig @@ -513,11 +513,13 @@ config MISC_RTSX tristate default MISC_RTSX_PCI || MISC_RTSX_USB -config UID_CPUTIME - bool "Per-UID cpu time statistics" +config UID_SYS_STATS + bool "Per-UID statistics" depends on PROFILING help Per UID based cpu time statistics exported to /proc/uid_cputime + Per UID based io statistics exported to /proc/uid_io + Per UID based procstat control in /proc/uid_procstat source "drivers/misc/c2port/Kconfig" source "drivers/misc/eeprom/Kconfig" diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile index d3dd7eb910d2..fca6aae030f0 100644 --- a/drivers/misc/Makefile +++ b/drivers/misc/Makefile @@ -58,4 +58,4 @@ obj-$(CONFIG_ASPEED_LPC_SNOOP) += aspeed-lpc-snoop.o obj-$(CONFIG_PCI_ENDPOINT_TEST) += pci_endpoint_test.o obj-$(CONFIG_OCXL) += ocxl/ obj-$(CONFIG_MISC_RTSX) += cardreader/ -obj-$(CONFIG_UID_CPUTIME) += uid_cputime.o +obj-$(CONFIG_UID_SYS_STATS) += uid_sys_stats.o diff --git a/drivers/misc/uid_cputime.c b/drivers/misc/uid_sys_stats.c similarity index 100% rename from drivers/misc/uid_cputime.c rename to drivers/misc/uid_sys_stats.c From 9106561ab1f55d7072fe8ef4bdd7fa812b3ee6c7 Mon Sep 17 00:00:00 2001 From: Jin Qian Date: Tue, 17 Jan 2017 17:26:07 -0800 Subject: [PATCH 0267/1103] ANDROID: uid_sys_stats: allow writing same state Signed-off-by: Jin Qian Bug: 34360629 Change-Id: Ia748351e07910b1febe54f0484ca1be58c4eb9c7 --- drivers/misc/uid_sys_stats.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/misc/uid_sys_stats.c b/drivers/misc/uid_sys_stats.c index 6a61c8fb0642..c93045cab241 100644 --- a/drivers/misc/uid_sys_stats.c +++ b/drivers/misc/uid_sys_stats.c @@ -336,11 +336,16 @@ static ssize_t uid_procstat_write(struct file *file, mutex_lock(&uid_lock); uid_entry = find_or_register_uid(uid); - if (!uid_entry || uid_entry->state == state) { + if (!uid_entry) { mutex_unlock(&uid_lock); return -EINVAL; } + if (uid_entry->state == state) { + mutex_unlock(&uid_lock); + return count; + } + update_io_stats_locked(); uid_entry->state = state; From 09d17efaa89a2b713ad861e607970738e98a54ea Mon Sep 17 00:00:00 2001 From: Jin Qian Date: Tue, 28 Feb 2017 15:09:42 -0800 Subject: [PATCH 0268/1103] ANDROID: uid_sys_stats: fix negative write bytes. A task can cancel writes made by other tasks. In rare cases, cancelled_write_bytes is larger than write_bytes if the task itself didn't make any write. This doesn't affect total size but may cause confusion when looking at IO usage on individual tasks. Bug: 35851986 Change-Id: If6cb549aeef9e248e18d804293401bb2b91918ca Signed-off-by: Jin Qian --- drivers/misc/uid_sys_stats.c | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/drivers/misc/uid_sys_stats.c b/drivers/misc/uid_sys_stats.c index c93045cab241..dcf82a1e83bc 100644 --- a/drivers/misc/uid_sys_stats.c +++ b/drivers/misc/uid_sys_stats.c @@ -200,14 +200,21 @@ static const struct file_operations uid_remove_fops = { .write = uid_remove_write, }; +static u64 compute_write_bytes(struct task_struct *task) +{ + if (task->ioac.write_bytes <= task->ioac.cancelled_write_bytes) + return 0; + + return task->ioac.write_bytes - task->ioac.cancelled_write_bytes; +} + static void add_uid_io_curr_stats(struct uid_entry *uid_entry, struct task_struct *task) { struct io_stats *io_curr = &uid_entry->io[UID_STATE_TOTAL_CURR]; io_curr->read_bytes += task->ioac.read_bytes; - io_curr->write_bytes += - task->ioac.write_bytes - task->ioac.cancelled_write_bytes; + io_curr->write_bytes += compute_write_bytes(task); io_curr->rchar += task->ioac.rchar; io_curr->wchar += task->ioac.wchar; } @@ -218,8 +225,7 @@ static void clean_uid_io_last_stats(struct uid_entry *uid_entry, struct io_stats *io_last = &uid_entry->io[UID_STATE_TOTAL_LAST]; io_last->read_bytes -= task->ioac.read_bytes; - io_last->write_bytes -= - task->ioac.write_bytes - task->ioac.cancelled_write_bytes; + io_last->write_bytes -= compute_write_bytes(task); io_last->rchar -= task->ioac.rchar; io_last->wchar -= task->ioac.wchar; } From 093149d8f2688dce5fc11a26178b89614c5c2eec Mon Sep 17 00:00:00 2001 From: Jin Qian Date: Thu, 2 Mar 2017 13:39:43 -0800 Subject: [PATCH 0269/1103] ANDROID: uid_sys_stats: account for fsync syscalls Change-Id: Ie888d8a0f4ec7a27dea86dc4afba8e6fd4203488 Signed-off-by: Jin Qian --- drivers/misc/uid_sys_stats.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/drivers/misc/uid_sys_stats.c b/drivers/misc/uid_sys_stats.c index dcf82a1e83bc..8764077a6e58 100644 --- a/drivers/misc/uid_sys_stats.c +++ b/drivers/misc/uid_sys_stats.c @@ -39,6 +39,7 @@ struct io_stats { u64 write_bytes; u64 rchar; u64 wchar; + u64 fsync; }; #define UID_STATE_FOREGROUND 0 @@ -217,6 +218,7 @@ static void add_uid_io_curr_stats(struct uid_entry *uid_entry, io_curr->write_bytes += compute_write_bytes(task); io_curr->rchar += task->ioac.rchar; io_curr->wchar += task->ioac.wchar; + io_curr->fsync += task->ioac.syscfs; } static void clean_uid_io_last_stats(struct uid_entry *uid_entry, @@ -228,6 +230,7 @@ static void clean_uid_io_last_stats(struct uid_entry *uid_entry, io_last->write_bytes -= compute_write_bytes(task); io_last->rchar -= task->ioac.rchar; io_last->wchar -= task->ioac.wchar; + io_last->fsync -= task->ioac.syscfs; } static void update_io_stats_locked(void) @@ -264,11 +267,13 @@ static void update_io_stats_locked(void) io_curr->write_bytes - io_last->write_bytes; io_bucket->rchar += io_curr->rchar - io_last->rchar; io_bucket->wchar += io_curr->wchar - io_last->wchar; + io_bucket->fsync += io_curr->fsync - io_last->fsync; io_last->read_bytes = io_curr->read_bytes; io_last->write_bytes = io_curr->write_bytes; io_last->rchar = io_curr->rchar; io_last->wchar = io_curr->wchar; + io_last->fsync = io_curr->fsync; } } @@ -282,7 +287,7 @@ static int uid_io_show(struct seq_file *m, void *v) update_io_stats_locked(); hash_for_each(hash_table, bkt, uid_entry, hash) { - seq_printf(m, "%d %llu %llu %llu %llu %llu %llu %llu %llu\n", + seq_printf(m, "%d %llu %llu %llu %llu %llu %llu %llu %llu %llu %llu\n", uid_entry->uid, uid_entry->io[UID_STATE_FOREGROUND].rchar, uid_entry->io[UID_STATE_FOREGROUND].wchar, @@ -291,7 +296,9 @@ static int uid_io_show(struct seq_file *m, void *v) uid_entry->io[UID_STATE_BACKGROUND].rchar, uid_entry->io[UID_STATE_BACKGROUND].wchar, uid_entry->io[UID_STATE_BACKGROUND].read_bytes, - uid_entry->io[UID_STATE_BACKGROUND].write_bytes); + uid_entry->io[UID_STATE_BACKGROUND].write_bytes, + uid_entry->io[UID_STATE_FOREGROUND].fsync, + uid_entry->io[UID_STATE_BACKGROUND].fsync); } mutex_unlock(&uid_lock); From f93d9f11b46f13275aa416bf8e94a69780e6eebc Mon Sep 17 00:00:00 2001 From: Wei Wang Date: Mon, 13 Mar 2017 12:22:21 -0700 Subject: [PATCH 0270/1103] ANDROID: uid_sys_stats: change to use rt_mutex We see this happens multiple times in heavy workload in systrace and AMS stuck in uid_lock. Running process: Process 953 Running thread: android.ui State: Uninterruptible Sleep Start: 1,025.628 ms Duration: 27,955.949 ms On CPU: Running instead: system_server Args: {kernel callsite when blocked:: "uid_procstat_write+0xb8/0x144"} Changing to rt_mutex can mitigate the priority inversion Bug: 34991231 Bug: 34193533 Test: on marlin Change-Id: I28eb3971331cea60b1075740c792ab87d103262c Signed-off-by: Wei Wang --- drivers/misc/uid_sys_stats.c | 32 +++++++++++++++++--------------- 1 file changed, 17 insertions(+), 15 deletions(-) diff --git a/drivers/misc/uid_sys_stats.c b/drivers/misc/uid_sys_stats.c index 8764077a6e58..2f4572fe8fd1 100644 --- a/drivers/misc/uid_sys_stats.c +++ b/drivers/misc/uid_sys_stats.c @@ -21,15 +21,17 @@ #include #include #include +#include #include #include #include #include + #define UID_HASH_BITS 10 DECLARE_HASHTABLE(hash_table, UID_HASH_BITS); -static DEFINE_MUTEX(uid_lock); +static DEFINE_RT_MUTEX(uid_lock); static struct proc_dir_entry *cpu_parent; static struct proc_dir_entry *io_parent; static struct proc_dir_entry *proc_parent; @@ -98,7 +100,7 @@ static int uid_cputime_show(struct seq_file *m, void *v) u64 stime; unsigned long bkt; - mutex_lock(&uid_lock); + rt_mutex_lock(&uid_lock); hash_for_each(hash_table, bkt, uid_entry, hash) { uid_entry->active_stime = 0; @@ -111,7 +113,7 @@ static int uid_cputime_show(struct seq_file *m, void *v) current_user_ns(), task_uid(task))); if (!uid_entry) { read_unlock(&tasklist_lock); - mutex_unlock(&uid_lock); + rt_mutex_unlock(&uid_lock); pr_err("%s: failed to find the uid_entry for uid %d\n", __func__, from_kuid_munged(current_user_ns(), task_uid(task))); @@ -132,7 +134,7 @@ static int uid_cputime_show(struct seq_file *m, void *v) ktime_to_ms(total_utime), ktime_to_ms(total_stime)); } - mutex_unlock(&uid_lock); + rt_mutex_unlock(&uid_lock); return 0; } @@ -179,7 +181,7 @@ static ssize_t uid_remove_write(struct file *file, kstrtol(end_uid, 10, &uid_end) != 0) { return -EINVAL; } - mutex_lock(&uid_lock); + rt_mutex_lock(&uid_lock); for (; uid_start <= uid_end; uid_start++) { hash_for_each_possible_safe(hash_table, uid_entry, tmp, @@ -191,7 +193,7 @@ static ssize_t uid_remove_write(struct file *file, } } - mutex_unlock(&uid_lock); + rt_mutex_unlock(&uid_lock); return count; } @@ -240,7 +242,7 @@ static void update_io_stats_locked(void) struct io_stats *io_bucket, *io_curr, *io_last; unsigned long bkt; - BUG_ON(!mutex_is_locked(&uid_lock)); + BUG_ON(!rt_mutex_is_locked(&uid_lock)); hash_for_each(hash_table, bkt, uid_entry, hash) memset(&uid_entry->io[UID_STATE_TOTAL_CURR], 0, @@ -282,7 +284,7 @@ static int uid_io_show(struct seq_file *m, void *v) struct uid_entry *uid_entry; unsigned long bkt; - mutex_lock(&uid_lock); + rt_mutex_lock(&uid_lock); update_io_stats_locked(); @@ -301,7 +303,7 @@ static int uid_io_show(struct seq_file *m, void *v) uid_entry->io[UID_STATE_BACKGROUND].fsync); } - mutex_unlock(&uid_lock); + rt_mutex_unlock(&uid_lock); return 0; } @@ -346,16 +348,16 @@ static ssize_t uid_procstat_write(struct file *file, if (state != UID_STATE_BACKGROUND && state != UID_STATE_FOREGROUND) return -EINVAL; - mutex_lock(&uid_lock); + rt_mutex_lock(&uid_lock); uid_entry = find_or_register_uid(uid); if (!uid_entry) { - mutex_unlock(&uid_lock); + rt_mutex_unlock(&uid_lock); return -EINVAL; } if (uid_entry->state == state) { - mutex_unlock(&uid_lock); + rt_mutex_unlock(&uid_lock); return count; } @@ -363,7 +365,7 @@ static ssize_t uid_procstat_write(struct file *file, uid_entry->state = state; - mutex_unlock(&uid_lock); + rt_mutex_unlock(&uid_lock); return count; } @@ -385,7 +387,7 @@ static int process_notifier(struct notifier_block *self, if (!task) return NOTIFY_OK; - mutex_lock(&uid_lock); + rt_mutex_lock(&uid_lock); uid = from_kuid_munged(current_user_ns(), task_uid(task)); uid_entry = find_or_register_uid(uid); if (!uid_entry) { @@ -401,7 +403,7 @@ static int process_notifier(struct notifier_block *self, clean_uid_io_last_stats(uid_entry, task); exit: - mutex_unlock(&uid_lock); + rt_mutex_unlock(&uid_lock); return NOTIFY_OK; } From 4e5182ff302d74cd63414906c4ef4882399c8907 Mon Sep 17 00:00:00 2001 From: Jin Qian Date: Thu, 13 Apr 2017 17:07:58 -0700 Subject: [PATCH 0271/1103] ANDROID: uid_sys_stats: reduce update_io_stats overhead Replaced read_lock with rcu_read_lock to reduce time that preemption is disabled. Added a function to update io stats for specific uid and moved hash table lookup, user_namespace out of loops. Bug: 37319300 Change-Id: I2b81b5cd3b6399b40d08c3c14b42cad044556970 Signed-off-by: Jin Qian --- drivers/misc/uid_sys_stats.c | 61 ++++++++++++++++++++++++++++++------ 1 file changed, 51 insertions(+), 10 deletions(-) diff --git a/drivers/misc/uid_sys_stats.c b/drivers/misc/uid_sys_stats.c index 2f4572fe8fd1..c9ee4f0cd4e4 100644 --- a/drivers/misc/uid_sys_stats.c +++ b/drivers/misc/uid_sys_stats.c @@ -235,28 +235,28 @@ static void clean_uid_io_last_stats(struct uid_entry *uid_entry, io_last->fsync -= task->ioac.syscfs; } -static void update_io_stats_locked(void) +static void update_io_stats_all_locked(void) { struct uid_entry *uid_entry; struct task_struct *task, *temp; struct io_stats *io_bucket, *io_curr, *io_last; + struct user_namespace *user_ns = current_user_ns(); unsigned long bkt; - - BUG_ON(!rt_mutex_is_locked(&uid_lock)); + uid_t uid; hash_for_each(hash_table, bkt, uid_entry, hash) memset(&uid_entry->io[UID_STATE_TOTAL_CURR], 0, sizeof(struct io_stats)); - read_lock(&tasklist_lock); + rcu_read_lock(); do_each_thread(temp, task) { - uid_entry = find_or_register_uid(from_kuid_munged( - current_user_ns(), task_uid(task))); + uid = from_kuid_munged(user_ns, task_uid(task)); + uid_entry = find_or_register_uid(uid); if (!uid_entry) continue; add_uid_io_curr_stats(uid_entry, task); } while_each_thread(temp, task); - read_unlock(&tasklist_lock); + rcu_read_unlock(); hash_for_each(hash_table, bkt, uid_entry, hash) { io_bucket = &uid_entry->io[uid_entry->state]; @@ -279,6 +279,47 @@ static void update_io_stats_locked(void) } } +static void update_io_stats_uid_locked(uid_t target_uid) +{ + struct uid_entry *uid_entry; + struct task_struct *task, *temp; + struct io_stats *io_bucket, *io_curr, *io_last; + struct user_namespace *user_ns = current_user_ns(); + + uid_entry = find_or_register_uid(target_uid); + if (!uid_entry) + return; + + memset(&uid_entry->io[UID_STATE_TOTAL_CURR], 0, + sizeof(struct io_stats)); + + rcu_read_lock(); + do_each_thread(temp, task) { + if (from_kuid_munged(user_ns, task_uid(task)) != target_uid) + continue; + add_uid_io_curr_stats(uid_entry, task); + } while_each_thread(temp, task); + rcu_read_unlock(); + + io_bucket = &uid_entry->io[uid_entry->state]; + io_curr = &uid_entry->io[UID_STATE_TOTAL_CURR]; + io_last = &uid_entry->io[UID_STATE_TOTAL_LAST]; + + io_bucket->read_bytes += + io_curr->read_bytes - io_last->read_bytes; + io_bucket->write_bytes += + io_curr->write_bytes - io_last->write_bytes; + io_bucket->rchar += io_curr->rchar - io_last->rchar; + io_bucket->wchar += io_curr->wchar - io_last->wchar; + io_bucket->fsync += io_curr->fsync - io_last->fsync; + + io_last->read_bytes = io_curr->read_bytes; + io_last->write_bytes = io_curr->write_bytes; + io_last->rchar = io_curr->rchar; + io_last->wchar = io_curr->wchar; + io_last->fsync = io_curr->fsync; +} + static int uid_io_show(struct seq_file *m, void *v) { struct uid_entry *uid_entry; @@ -286,7 +327,7 @@ static int uid_io_show(struct seq_file *m, void *v) rt_mutex_lock(&uid_lock); - update_io_stats_locked(); + update_io_stats_all_locked(); hash_for_each(hash_table, bkt, uid_entry, hash) { seq_printf(m, "%d %llu %llu %llu %llu %llu %llu %llu %llu %llu %llu\n", @@ -361,7 +402,7 @@ static ssize_t uid_procstat_write(struct file *file, return count; } - update_io_stats_locked(); + update_io_stats_uid_locked(uid); uid_entry->state = state; @@ -399,7 +440,7 @@ static int process_notifier(struct notifier_block *self, uid_entry->utime += utime; uid_entry->stime += stime; - update_io_stats_locked(); + update_io_stats_uid_locked(uid); clean_uid_io_last_stats(uid_entry, task); exit: From 53c33a213e3784f6cd25537e57fe99373e3c60cc Mon Sep 17 00:00:00 2001 From: Ganesh Mahendran Date: Tue, 25 Apr 2017 18:07:43 +0800 Subject: [PATCH 0272/1103] ANDROID: uid_sys_stats: fix access of task_uid(task) struct task_struct *task should be proteced by tasklist_lock. Change-Id: Iefcd13442a9b9d855a2bbcde9fd838a4132fee58 Signed-off-by: Ganesh Mahendran (cherry picked from commit 90d78776c4a0e13fb7ee5bd0787f04a1730631a6) --- drivers/misc/uid_sys_stats.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/drivers/misc/uid_sys_stats.c b/drivers/misc/uid_sys_stats.c index c9ee4f0cd4e4..6934380014c0 100644 --- a/drivers/misc/uid_sys_stats.c +++ b/drivers/misc/uid_sys_stats.c @@ -96,9 +96,11 @@ static int uid_cputime_show(struct seq_file *m, void *v) { struct uid_entry *uid_entry; struct task_struct *task, *temp; + struct user_namespace *user_ns = current_user_ns(); u64 utime; u64 stime; unsigned long bkt; + uid_t uid; rt_mutex_lock(&uid_lock); @@ -109,14 +111,13 @@ static int uid_cputime_show(struct seq_file *m, void *v) read_lock(&tasklist_lock); do_each_thread(temp, task) { - uid_entry = find_or_register_uid(from_kuid_munged( - current_user_ns(), task_uid(task))); + uid = from_kuid_munged(user_ns, task_uid(task)); + uid_entry = find_or_register_uid(uid); if (!uid_entry) { read_unlock(&tasklist_lock); rt_mutex_unlock(&uid_lock); pr_err("%s: failed to find the uid_entry for uid %d\n", - __func__, from_kuid_munged(current_user_ns(), - task_uid(task))); + __func__, uid); return -ENOMEM; } task_cputime_adjusted(task, &utime, &stime); From 5bf8f0c4cb732f9fa94396c4413c0699e1970fa6 Mon Sep 17 00:00:00 2001 From: Jin Qian Date: Mon, 22 May 2017 12:08:06 -0700 Subject: [PATCH 0273/1103] ANDROID: uid_sys_stats: defer io stats calulation for dead tasks Store sum of dead task io stats in uid_entry and defer uid io calulation until next uid proc stat change or dumpsys. Bug: 37754877 Change-Id: I970f010a4c841c5ca26d0efc7e027414c3c952e0 Signed-off-by: Jin Qian --- drivers/misc/uid_sys_stats.c | 107 ++++++++++++++--------------------- 1 file changed, 42 insertions(+), 65 deletions(-) diff --git a/drivers/misc/uid_sys_stats.c b/drivers/misc/uid_sys_stats.c index 6934380014c0..79ad83529f33 100644 --- a/drivers/misc/uid_sys_stats.c +++ b/drivers/misc/uid_sys_stats.c @@ -50,7 +50,8 @@ struct io_stats { #define UID_STATE_TOTAL_CURR 2 #define UID_STATE_TOTAL_LAST 3 -#define UID_STATE_SIZE 4 +#define UID_STATE_DEAD_TASKS 4 +#define UID_STATE_SIZE 5 struct uid_entry { uid_t uid; @@ -212,35 +213,44 @@ static u64 compute_write_bytes(struct task_struct *task) return task->ioac.write_bytes - task->ioac.cancelled_write_bytes; } -static void add_uid_io_curr_stats(struct uid_entry *uid_entry, - struct task_struct *task) +static void add_uid_io_stats(struct uid_entry *uid_entry, + struct task_struct *task, int slot) { - struct io_stats *io_curr = &uid_entry->io[UID_STATE_TOTAL_CURR]; + struct io_stats *io_slot = &uid_entry->io[slot]; - io_curr->read_bytes += task->ioac.read_bytes; - io_curr->write_bytes += compute_write_bytes(task); - io_curr->rchar += task->ioac.rchar; - io_curr->wchar += task->ioac.wchar; - io_curr->fsync += task->ioac.syscfs; + io_slot->read_bytes += task->ioac.read_bytes; + io_slot->write_bytes += compute_write_bytes(task); + io_slot->rchar += task->ioac.rchar; + io_slot->wchar += task->ioac.wchar; + io_slot->fsync += task->ioac.syscfs; } -static void clean_uid_io_last_stats(struct uid_entry *uid_entry, - struct task_struct *task) +static void compute_uid_io_bucket_stats(struct io_stats *io_bucket, + struct io_stats *io_curr, + struct io_stats *io_last, + struct io_stats *io_dead) { - struct io_stats *io_last = &uid_entry->io[UID_STATE_TOTAL_LAST]; + io_bucket->read_bytes += io_curr->read_bytes + io_dead->read_bytes - + io_last->read_bytes; + io_bucket->write_bytes += io_curr->write_bytes + io_dead->write_bytes - + io_last->write_bytes; + io_bucket->rchar += io_curr->rchar + io_dead->rchar - io_last->rchar; + io_bucket->wchar += io_curr->wchar + io_dead->wchar - io_last->wchar; + io_bucket->fsync += io_curr->fsync + io_dead->fsync - io_last->fsync; - io_last->read_bytes -= task->ioac.read_bytes; - io_last->write_bytes -= compute_write_bytes(task); - io_last->rchar -= task->ioac.rchar; - io_last->wchar -= task->ioac.wchar; - io_last->fsync -= task->ioac.syscfs; + io_last->read_bytes = io_curr->read_bytes; + io_last->write_bytes = io_curr->write_bytes; + io_last->rchar = io_curr->rchar; + io_last->wchar = io_curr->wchar; + io_last->fsync = io_curr->fsync; + + memset(io_dead, 0, sizeof(struct io_stats)); } static void update_io_stats_all_locked(void) { struct uid_entry *uid_entry; struct task_struct *task, *temp; - struct io_stats *io_bucket, *io_curr, *io_last; struct user_namespace *user_ns = current_user_ns(); unsigned long bkt; uid_t uid; @@ -255,70 +265,38 @@ static void update_io_stats_all_locked(void) uid_entry = find_or_register_uid(uid); if (!uid_entry) continue; - add_uid_io_curr_stats(uid_entry, task); + add_uid_io_stats(uid_entry, task, UID_STATE_TOTAL_CURR); } while_each_thread(temp, task); rcu_read_unlock(); hash_for_each(hash_table, bkt, uid_entry, hash) { - io_bucket = &uid_entry->io[uid_entry->state]; - io_curr = &uid_entry->io[UID_STATE_TOTAL_CURR]; - io_last = &uid_entry->io[UID_STATE_TOTAL_LAST]; - - io_bucket->read_bytes += - io_curr->read_bytes - io_last->read_bytes; - io_bucket->write_bytes += - io_curr->write_bytes - io_last->write_bytes; - io_bucket->rchar += io_curr->rchar - io_last->rchar; - io_bucket->wchar += io_curr->wchar - io_last->wchar; - io_bucket->fsync += io_curr->fsync - io_last->fsync; - - io_last->read_bytes = io_curr->read_bytes; - io_last->write_bytes = io_curr->write_bytes; - io_last->rchar = io_curr->rchar; - io_last->wchar = io_curr->wchar; - io_last->fsync = io_curr->fsync; + compute_uid_io_bucket_stats(&uid_entry->io[uid_entry->state], + &uid_entry->io[UID_STATE_TOTAL_CURR], + &uid_entry->io[UID_STATE_TOTAL_LAST], + &uid_entry->io[UID_STATE_DEAD_TASKS]); } } -static void update_io_stats_uid_locked(uid_t target_uid) +static void update_io_stats_uid_locked(struct uid_entry *uid_entry) { - struct uid_entry *uid_entry; struct task_struct *task, *temp; - struct io_stats *io_bucket, *io_curr, *io_last; struct user_namespace *user_ns = current_user_ns(); - uid_entry = find_or_register_uid(target_uid); - if (!uid_entry) - return; - memset(&uid_entry->io[UID_STATE_TOTAL_CURR], 0, sizeof(struct io_stats)); rcu_read_lock(); do_each_thread(temp, task) { - if (from_kuid_munged(user_ns, task_uid(task)) != target_uid) + if (from_kuid_munged(user_ns, task_uid(task)) != uid_entry->uid) continue; - add_uid_io_curr_stats(uid_entry, task); + add_uid_io_stats(uid_entry, task, UID_STATE_TOTAL_CURR); } while_each_thread(temp, task); rcu_read_unlock(); - io_bucket = &uid_entry->io[uid_entry->state]; - io_curr = &uid_entry->io[UID_STATE_TOTAL_CURR]; - io_last = &uid_entry->io[UID_STATE_TOTAL_LAST]; - - io_bucket->read_bytes += - io_curr->read_bytes - io_last->read_bytes; - io_bucket->write_bytes += - io_curr->write_bytes - io_last->write_bytes; - io_bucket->rchar += io_curr->rchar - io_last->rchar; - io_bucket->wchar += io_curr->wchar - io_last->wchar; - io_bucket->fsync += io_curr->fsync - io_last->fsync; - - io_last->read_bytes = io_curr->read_bytes; - io_last->write_bytes = io_curr->write_bytes; - io_last->rchar = io_curr->rchar; - io_last->wchar = io_curr->wchar; - io_last->fsync = io_curr->fsync; + compute_uid_io_bucket_stats(&uid_entry->io[uid_entry->state], + &uid_entry->io[UID_STATE_TOTAL_CURR], + &uid_entry->io[UID_STATE_TOTAL_LAST], + &uid_entry->io[UID_STATE_DEAD_TASKS]); } static int uid_io_show(struct seq_file *m, void *v) @@ -403,7 +381,7 @@ static ssize_t uid_procstat_write(struct file *file, return count; } - update_io_stats_uid_locked(uid); + update_io_stats_uid_locked(uid_entry); uid_entry->state = state; @@ -441,8 +419,7 @@ static int process_notifier(struct notifier_block *self, uid_entry->utime += utime; uid_entry->stime += stime; - update_io_stats_uid_locked(uid); - clean_uid_io_last_stats(uid_entry, task); + add_uid_io_stats(uid_entry, task, UID_STATE_DEAD_TASKS); exit: rt_mutex_unlock(&uid_lock); From c2d40ebb7f39f3833edac763af31fba0a0140091 Mon Sep 17 00:00:00 2001 From: Ganesh Mahendran Date: Wed, 24 May 2017 10:28:27 +0800 Subject: [PATCH 0274/1103] ANDROID: uid_sys_stats: Kconfig: add depends for UID_SYS_STATS uid_io depends on TASK_XACCT and TASK_IO_ACCOUNTING. So add depends in Kconfig before compiling code. Change-Id: Ie6bf57ec7c2eceffadf4da0fc2aca001ce10c36e Signed-off-by: Ganesh Mahendran --- drivers/misc/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig index ca1cbe5ec2e7..d36249491167 100644 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig @@ -515,7 +515,7 @@ config MISC_RTSX config UID_SYS_STATS bool "Per-UID statistics" - depends on PROFILING + depends on PROFILING && TASK_XACCT && TASK_IO_ACCOUNTING help Per UID based cpu time statistics exported to /proc/uid_cputime Per UID based io statistics exported to /proc/uid_io From e9d4681208171f07b02a8cbdae66f21beacc5fbe Mon Sep 17 00:00:00 2001 From: Ganesh Mahendran Date: Thu, 25 May 2017 15:20:29 +0800 Subject: [PATCH 0275/1103] ANDROID: uid_sys_stats: check previous uid_entry before call find_or_register_uid Theads in a process are stored in list struct task_struct->thread_group, so it will be visited continiously in below loop: do_each_thread(temp, task) { ... } while_each_thread(temp, task); I add some log in the loop, we can see below information: [ 65.033561] uid 1000, uid_entry ffffffc0f2761600 [ 65.033567] uid 1000, uid_entry ffffffc0f2761600 [ 65.033574] uid 1000, uid_entry ffffffc0f2761600 [ 65.033581] uid 1000, uid_entry ffffffc0f2761600 [ 65.033588] uid 1000, uid_entry ffffffc0f2761600 [ 65.033595] uid 1000, uid_entry ffffffc0f2761600 [ 65.033602] uid 1000, uid_entry ffffffc0f2761600 [ 65.033609] uid 1000, uid_entry ffffffc0f2761600 [ 65.033615] uid 1000, uid_entry ffffffc0f2761600 [ 65.033622] uid 1000, uid_entry ffffffc0f2761600 [ 65.033629] uid 1000, uid_entry ffffffc0f2761600 [ 65.033637] uid 1000, uid_entry ffffffc0f2761600 [ 65.033644] uid 1000, uid_entry ffffffc0f2761600 [ 65.033651] uid 1000, uid_entry ffffffc0f2761600 [ 65.033658] uid 1000, uid_entry ffffffc0f2761600 [ 65.033665] uid 1000, uid_entry ffffffc0f2761600 [ 65.033672] uid 1000, uid_entry ffffffc0f2761600 [ 65.033680] uid 1000, uid_entry ffffffc0f2761600 [ 65.033687] uid 1000, uid_entry ffffffc0f2761600 [ 65.033694] uid 1000, uid_entry ffffffc0f2761600 [ 65.033701] uid 1000, uid_entry ffffffc0f2761600 [ 65.033708] uid 1000, uid_entry ffffffc0f2761600 [ 65.033715] uid 1000, uid_entry ffffffc0f2761600 [ 65.033722] uid 1000, uid_entry ffffffc0f2761600 [ 65.033729] uid 1000, uid_entry ffffffc0f2761600 [ 65.033736] uid 1000, uid_entry ffffffc0f2761600 [ 65.033743] uid 1000, uid_entry ffffffc0f2761600 [ 65.033750] uid 1000, uid_entry ffffffc0f2761600 [ 65.033757] uid 1000, uid_entry ffffffc0f2761600 [ 65.033763] uid 1000, uid_entry ffffffc0f2761600 [ 65.033770] uid 1000, uid_entry ffffffc0f2761600 [ 65.033777] uid 1000, uid_entry ffffffc0f2761600 [ 65.033784] uid 1000, uid_entry ffffffc0f2761600 [ 65.033791] uid 1000, uid_entry ffffffc0f2761600 [ 65.033798] uid 1000, uid_entry ffffffc0f2761600 So we can check the previous uid_entry before calling find_or_register_uid to save time. Change-Id: I05ec1a1405a80c0a620cb4b4b2f6483dbfde7829 Signed-off-by: Ganesh Mahendran --- drivers/misc/uid_sys_stats.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/drivers/misc/uid_sys_stats.c b/drivers/misc/uid_sys_stats.c index 79ad83529f33..6d58eb0120f6 100644 --- a/drivers/misc/uid_sys_stats.c +++ b/drivers/misc/uid_sys_stats.c @@ -95,7 +95,7 @@ static struct uid_entry *find_or_register_uid(uid_t uid) static int uid_cputime_show(struct seq_file *m, void *v) { - struct uid_entry *uid_entry; + struct uid_entry *uid_entry = NULL; struct task_struct *task, *temp; struct user_namespace *user_ns = current_user_ns(); u64 utime; @@ -113,7 +113,8 @@ static int uid_cputime_show(struct seq_file *m, void *v) read_lock(&tasklist_lock); do_each_thread(temp, task) { uid = from_kuid_munged(user_ns, task_uid(task)); - uid_entry = find_or_register_uid(uid); + if (!uid_entry || uid_entry->uid != uid) + uid_entry = find_or_register_uid(uid); if (!uid_entry) { read_unlock(&tasklist_lock); rt_mutex_unlock(&uid_lock); @@ -249,7 +250,7 @@ static void compute_uid_io_bucket_stats(struct io_stats *io_bucket, static void update_io_stats_all_locked(void) { - struct uid_entry *uid_entry; + struct uid_entry *uid_entry = NULL; struct task_struct *task, *temp; struct user_namespace *user_ns = current_user_ns(); unsigned long bkt; @@ -262,7 +263,8 @@ static void update_io_stats_all_locked(void) rcu_read_lock(); do_each_thread(temp, task) { uid = from_kuid_munged(user_ns, task_uid(task)); - uid_entry = find_or_register_uid(uid); + if (!uid_entry || uid_entry->uid != uid) + uid_entry = find_or_register_uid(uid); if (!uid_entry) continue; add_uid_io_stats(uid_entry, task, UID_STATE_TOTAL_CURR); From 10b76c0ac03ffa98fcb110d44769fe18e94c29af Mon Sep 17 00:00:00 2001 From: Yang Jin Date: Wed, 26 Jul 2017 12:52:22 -0700 Subject: [PATCH 0276/1103] ANDROID: uid_sys_stats: log task io with a debug flag Add a hashmap inside each uid_entry to keep track of task name and io. Task full name is a combination of thread and process name. Bug: 63739275 Change-Id: I30083b757eaef8c61e55a213a883ce8d0c9cf2b1 Signed-off-by: Yang Jin [AmitP: Folded following android-4.9 commit changes into this patch 0ea205a6a224 ("ANDROID: uid_sys_stats: Fix implicit declaration of get_cmdline()")] Signed-off-by: Amit Pundir --- drivers/misc/Kconfig | 7 + drivers/misc/uid_sys_stats.c | 304 ++++++++++++++++++++++++++++++----- 2 files changed, 267 insertions(+), 44 deletions(-) diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig index d36249491167..241e3d9ee7aa 100644 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig @@ -521,6 +521,13 @@ config UID_SYS_STATS Per UID based io statistics exported to /proc/uid_io Per UID based procstat control in /proc/uid_procstat +config UID_SYS_STATS_DEBUG + bool "Per-TASK statistics" + depends on UID_SYS_STATS + default n + help + Per TASK based io statistics exported to /proc/uid_io + source "drivers/misc/c2port/Kconfig" source "drivers/misc/eeprom/Kconfig" source "drivers/misc/cb710/Kconfig" diff --git a/drivers/misc/uid_sys_stats.c b/drivers/misc/uid_sys_stats.c index 6d58eb0120f6..7069b0b064a5 100644 --- a/drivers/misc/uid_sys_stats.c +++ b/drivers/misc/uid_sys_stats.c @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include @@ -53,6 +54,15 @@ struct io_stats { #define UID_STATE_DEAD_TASKS 4 #define UID_STATE_SIZE 5 +#define MAX_TASK_COMM_LEN 256 + +struct task_entry { + char comm[MAX_TASK_COMM_LEN]; + pid_t pid; + struct io_stats io[UID_STATE_SIZE]; + struct hlist_node hash; +}; + struct uid_entry { uid_t uid; u64 utime; @@ -62,8 +72,231 @@ struct uid_entry { int state; struct io_stats io[UID_STATE_SIZE]; struct hlist_node hash; +#ifdef CONFIG_UID_SYS_STATS_DEBUG + DECLARE_HASHTABLE(task_entries, UID_HASH_BITS); +#endif }; +static u64 compute_write_bytes(struct task_struct *task) +{ + if (task->ioac.write_bytes <= task->ioac.cancelled_write_bytes) + return 0; + + return task->ioac.write_bytes - task->ioac.cancelled_write_bytes; +} + +static void compute_io_bucket_stats(struct io_stats *io_bucket, + struct io_stats *io_curr, + struct io_stats *io_last, + struct io_stats *io_dead) +{ + /* tasks could switch to another uid group, but its io_last in the + * previous uid group could still be positive. + * therefore before each update, do an overflow check first + */ + int64_t delta; + + delta = io_curr->read_bytes + io_dead->read_bytes - + io_last->read_bytes; + io_bucket->read_bytes += delta > 0 ? delta : 0; + delta = io_curr->write_bytes + io_dead->write_bytes - + io_last->write_bytes; + io_bucket->write_bytes += delta > 0 ? delta : 0; + delta = io_curr->rchar + io_dead->rchar - io_last->rchar; + io_bucket->rchar += delta > 0 ? delta : 0; + delta = io_curr->wchar + io_dead->wchar - io_last->wchar; + io_bucket->wchar += delta > 0 ? delta : 0; + delta = io_curr->fsync + io_dead->fsync - io_last->fsync; + io_bucket->fsync += delta > 0 ? delta : 0; + + io_last->read_bytes = io_curr->read_bytes; + io_last->write_bytes = io_curr->write_bytes; + io_last->rchar = io_curr->rchar; + io_last->wchar = io_curr->wchar; + io_last->fsync = io_curr->fsync; + + memset(io_dead, 0, sizeof(struct io_stats)); +} + +#ifdef CONFIG_UID_SYS_STATS_DEBUG +static void get_full_task_comm(struct task_entry *task_entry, + struct task_struct *task) +{ + int i = 0, offset = 0, len = 0; + /* save one byte for terminating null character */ + int unused_len = MAX_TASK_COMM_LEN - TASK_COMM_LEN - 1; + char buf[unused_len]; + struct mm_struct *mm = task->mm; + + /* fill the first TASK_COMM_LEN bytes with thread name */ + get_task_comm(task_entry->comm, task); + i = strlen(task_entry->comm); + while (i < TASK_COMM_LEN) + task_entry->comm[i++] = ' '; + + /* next the executable file name */ + if (mm) { + down_read(&mm->mmap_sem); + if (mm->exe_file) { + char *pathname = d_path(&mm->exe_file->f_path, buf, + unused_len); + + if (!IS_ERR(pathname)) { + len = strlcpy(task_entry->comm + i, pathname, + unused_len); + i += len; + task_entry->comm[i++] = ' '; + unused_len--; + } + } + up_read(&mm->mmap_sem); + } + unused_len -= len; + + /* fill the rest with command line argument + * replace each null or new line character + * between args in argv with whitespace */ + len = get_cmdline(task, buf, unused_len); + while (offset < len) { + if (buf[offset] != '\0' && buf[offset] != '\n') + task_entry->comm[i++] = buf[offset]; + else + task_entry->comm[i++] = ' '; + offset++; + } + + /* get rid of trailing whitespaces in case when arg is memset to + * zero before being reset in userspace + */ + while (task_entry->comm[i-1] == ' ') + i--; + task_entry->comm[i] = '\0'; +} + +static struct task_entry *find_task_entry(struct uid_entry *uid_entry, + struct task_struct *task) +{ + struct task_entry *task_entry; + + hash_for_each_possible(uid_entry->task_entries, task_entry, hash, + task->pid) { + if (task->pid == task_entry->pid) { + /* if thread name changed, update the entire command */ + int len = strnchr(task_entry->comm, ' ', TASK_COMM_LEN) + - task_entry->comm; + + if (strncmp(task_entry->comm, task->comm, len)) + get_full_task_comm(task_entry, task); + return task_entry; + } + } + return NULL; +} + +static struct task_entry *find_or_register_task(struct uid_entry *uid_entry, + struct task_struct *task) +{ + struct task_entry *task_entry; + pid_t pid = task->pid; + + task_entry = find_task_entry(uid_entry, task); + if (task_entry) + return task_entry; + + task_entry = kzalloc(sizeof(struct task_entry), GFP_ATOMIC); + if (!task_entry) + return NULL; + + get_full_task_comm(task_entry, task); + + task_entry->pid = pid; + hash_add(uid_entry->task_entries, &task_entry->hash, (unsigned int)pid); + + return task_entry; +} + +static void remove_uid_tasks(struct uid_entry *uid_entry) +{ + struct task_entry *task_entry; + unsigned long bkt_task; + struct hlist_node *tmp_task; + + hash_for_each_safe(uid_entry->task_entries, bkt_task, + tmp_task, task_entry, hash) { + hash_del(&task_entry->hash); + kfree(task_entry); + } +} + +static void set_io_uid_tasks_zero(struct uid_entry *uid_entry) +{ + struct task_entry *task_entry; + unsigned long bkt_task; + + hash_for_each(uid_entry->task_entries, bkt_task, task_entry, hash) { + memset(&task_entry->io[UID_STATE_TOTAL_CURR], 0, + sizeof(struct io_stats)); + } +} + +static void add_uid_tasks_io_stats(struct uid_entry *uid_entry, + struct task_struct *task, int slot) +{ + struct task_entry *task_entry = find_or_register_task(uid_entry, task); + struct io_stats *task_io_slot = &task_entry->io[slot]; + + task_io_slot->read_bytes += task->ioac.read_bytes; + task_io_slot->write_bytes += compute_write_bytes(task); + task_io_slot->rchar += task->ioac.rchar; + task_io_slot->wchar += task->ioac.wchar; + task_io_slot->fsync += task->ioac.syscfs; +} + +static void compute_io_uid_tasks(struct uid_entry *uid_entry) +{ + struct task_entry *task_entry; + unsigned long bkt_task; + + hash_for_each(uid_entry->task_entries, bkt_task, task_entry, hash) { + compute_io_bucket_stats(&task_entry->io[uid_entry->state], + &task_entry->io[UID_STATE_TOTAL_CURR], + &task_entry->io[UID_STATE_TOTAL_LAST], + &task_entry->io[UID_STATE_DEAD_TASKS]); + } +} + +static void show_io_uid_tasks(struct seq_file *m, struct uid_entry *uid_entry) +{ + struct task_entry *task_entry; + unsigned long bkt_task; + + hash_for_each(uid_entry->task_entries, bkt_task, task_entry, hash) { + /* Separated by comma because space exists in task comm */ + seq_printf(m, "task,%s,%lu,%llu,%llu,%llu,%llu,%llu,%llu,%llu,%llu,%llu,%llu\n", + task_entry->comm, + (unsigned long)task_entry->pid, + task_entry->io[UID_STATE_FOREGROUND].rchar, + task_entry->io[UID_STATE_FOREGROUND].wchar, + task_entry->io[UID_STATE_FOREGROUND].read_bytes, + task_entry->io[UID_STATE_FOREGROUND].write_bytes, + task_entry->io[UID_STATE_BACKGROUND].rchar, + task_entry->io[UID_STATE_BACKGROUND].wchar, + task_entry->io[UID_STATE_BACKGROUND].read_bytes, + task_entry->io[UID_STATE_BACKGROUND].write_bytes, + task_entry->io[UID_STATE_FOREGROUND].fsync, + task_entry->io[UID_STATE_BACKGROUND].fsync); + } +} +#else +static void remove_uid_tasks(struct uid_entry *uid_entry) {}; +static void set_io_uid_tasks_zero(struct uid_entry *uid_entry) {}; +static void add_uid_tasks_io_stats(struct uid_entry *uid_entry, + struct task_struct *task, int slot) {}; +static void compute_io_uid_tasks(struct uid_entry *uid_entry) {}; +static void show_io_uid_tasks(struct seq_file *m, + struct uid_entry *uid_entry) {} +#endif + static struct uid_entry *find_uid_entry(uid_t uid) { struct uid_entry *uid_entry; @@ -87,7 +320,9 @@ static struct uid_entry *find_or_register_uid(uid_t uid) return NULL; uid_entry->uid = uid; - +#ifdef CONFIG_UID_SYS_STATS_DEBUG + hash_init(uid_entry->task_entries); +#endif hash_add(hash_table, &uid_entry->hash, uid); return uid_entry; @@ -190,6 +425,7 @@ static ssize_t uid_remove_write(struct file *file, hash_for_each_possible_safe(hash_table, uid_entry, tmp, hash, (uid_t)uid_start) { if (uid_start == uid_entry->uid) { + remove_uid_tasks(uid_entry); hash_del(&uid_entry->hash); kfree(uid_entry); } @@ -206,13 +442,6 @@ static const struct file_operations uid_remove_fops = { .write = uid_remove_write, }; -static u64 compute_write_bytes(struct task_struct *task) -{ - if (task->ioac.write_bytes <= task->ioac.cancelled_write_bytes) - return 0; - - return task->ioac.write_bytes - task->ioac.cancelled_write_bytes; -} static void add_uid_io_stats(struct uid_entry *uid_entry, struct task_struct *task, int slot) @@ -224,28 +453,8 @@ static void add_uid_io_stats(struct uid_entry *uid_entry, io_slot->rchar += task->ioac.rchar; io_slot->wchar += task->ioac.wchar; io_slot->fsync += task->ioac.syscfs; -} -static void compute_uid_io_bucket_stats(struct io_stats *io_bucket, - struct io_stats *io_curr, - struct io_stats *io_last, - struct io_stats *io_dead) -{ - io_bucket->read_bytes += io_curr->read_bytes + io_dead->read_bytes - - io_last->read_bytes; - io_bucket->write_bytes += io_curr->write_bytes + io_dead->write_bytes - - io_last->write_bytes; - io_bucket->rchar += io_curr->rchar + io_dead->rchar - io_last->rchar; - io_bucket->wchar += io_curr->wchar + io_dead->wchar - io_last->wchar; - io_bucket->fsync += io_curr->fsync + io_dead->fsync - io_last->fsync; - - io_last->read_bytes = io_curr->read_bytes; - io_last->write_bytes = io_curr->write_bytes; - io_last->rchar = io_curr->rchar; - io_last->wchar = io_curr->wchar; - io_last->fsync = io_curr->fsync; - - memset(io_dead, 0, sizeof(struct io_stats)); + add_uid_tasks_io_stats(uid_entry, task, slot); } static void update_io_stats_all_locked(void) @@ -256,9 +465,11 @@ static void update_io_stats_all_locked(void) unsigned long bkt; uid_t uid; - hash_for_each(hash_table, bkt, uid_entry, hash) + hash_for_each(hash_table, bkt, uid_entry, hash) { memset(&uid_entry->io[UID_STATE_TOTAL_CURR], 0, sizeof(struct io_stats)); + set_io_uid_tasks_zero(uid_entry); + } rcu_read_lock(); do_each_thread(temp, task) { @@ -272,10 +483,11 @@ static void update_io_stats_all_locked(void) rcu_read_unlock(); hash_for_each(hash_table, bkt, uid_entry, hash) { - compute_uid_io_bucket_stats(&uid_entry->io[uid_entry->state], + compute_io_bucket_stats(&uid_entry->io[uid_entry->state], &uid_entry->io[UID_STATE_TOTAL_CURR], &uid_entry->io[UID_STATE_TOTAL_LAST], &uid_entry->io[UID_STATE_DEAD_TASKS]); + compute_io_uid_tasks(uid_entry); } } @@ -286,6 +498,7 @@ static void update_io_stats_uid_locked(struct uid_entry *uid_entry) memset(&uid_entry->io[UID_STATE_TOTAL_CURR], 0, sizeof(struct io_stats)); + set_io_uid_tasks_zero(uid_entry); rcu_read_lock(); do_each_thread(temp, task) { @@ -295,12 +508,14 @@ static void update_io_stats_uid_locked(struct uid_entry *uid_entry) } while_each_thread(temp, task); rcu_read_unlock(); - compute_uid_io_bucket_stats(&uid_entry->io[uid_entry->state], + compute_io_bucket_stats(&uid_entry->io[uid_entry->state], &uid_entry->io[UID_STATE_TOTAL_CURR], &uid_entry->io[UID_STATE_TOTAL_LAST], &uid_entry->io[UID_STATE_DEAD_TASKS]); + compute_io_uid_tasks(uid_entry); } + static int uid_io_show(struct seq_file *m, void *v) { struct uid_entry *uid_entry; @@ -312,21 +527,22 @@ static int uid_io_show(struct seq_file *m, void *v) hash_for_each(hash_table, bkt, uid_entry, hash) { seq_printf(m, "%d %llu %llu %llu %llu %llu %llu %llu %llu %llu %llu\n", - uid_entry->uid, - uid_entry->io[UID_STATE_FOREGROUND].rchar, - uid_entry->io[UID_STATE_FOREGROUND].wchar, - uid_entry->io[UID_STATE_FOREGROUND].read_bytes, - uid_entry->io[UID_STATE_FOREGROUND].write_bytes, - uid_entry->io[UID_STATE_BACKGROUND].rchar, - uid_entry->io[UID_STATE_BACKGROUND].wchar, - uid_entry->io[UID_STATE_BACKGROUND].read_bytes, - uid_entry->io[UID_STATE_BACKGROUND].write_bytes, - uid_entry->io[UID_STATE_FOREGROUND].fsync, - uid_entry->io[UID_STATE_BACKGROUND].fsync); + uid_entry->uid, + uid_entry->io[UID_STATE_FOREGROUND].rchar, + uid_entry->io[UID_STATE_FOREGROUND].wchar, + uid_entry->io[UID_STATE_FOREGROUND].read_bytes, + uid_entry->io[UID_STATE_FOREGROUND].write_bytes, + uid_entry->io[UID_STATE_BACKGROUND].rchar, + uid_entry->io[UID_STATE_BACKGROUND].wchar, + uid_entry->io[UID_STATE_BACKGROUND].read_bytes, + uid_entry->io[UID_STATE_BACKGROUND].write_bytes, + uid_entry->io[UID_STATE_FOREGROUND].fsync, + uid_entry->io[UID_STATE_BACKGROUND].fsync); + + show_io_uid_tasks(m, uid_entry); } rt_mutex_unlock(&uid_lock); - return 0; } From ebb284f0b21f6ec5f6d24591e19a102f74f67614 Mon Sep 17 00:00:00 2001 From: James Carr Date: Fri, 29 Jul 2016 19:02:16 -0700 Subject: [PATCH 0277/1103] ANDROID: memory_state_time: Implement memory_state_time, used by qcom,cpubw New driver memory_state_time tracks time spent in different DDR frequency and bandwidth states. Memory drivers such as qcom,cpubw can post updated state to the driver after registering a callback. Processed by a workqueue Bandwidth buckets are read in from device tree in the relevant qualcomm section, can be defined in any quantity and spacing. The data is exposed at /sys/kernel/memory_state_time, able to be read by the Android framework. Functionality is behind a config option CONFIG_MEMORY_STATE_TIME Change-Id: I4fee165571cb975fb9eacbc9aada5e6d7dd748f0 Signed-off-by: James Carr --- .../bindings/misc/memory-state-time.txt | 8 + drivers/misc/Kconfig | 6 + drivers/misc/Makefile | 1 + drivers/misc/memory_state_time.c | 454 ++++++++++++++++++ include/linux/memory-state-time.h | 42 ++ 5 files changed, 511 insertions(+) create mode 100644 Documentation/devicetree/bindings/misc/memory-state-time.txt create mode 100644 drivers/misc/memory_state_time.c create mode 100644 include/linux/memory-state-time.h diff --git a/Documentation/devicetree/bindings/misc/memory-state-time.txt b/Documentation/devicetree/bindings/misc/memory-state-time.txt new file mode 100644 index 000000000000..c99a506c030d --- /dev/null +++ b/Documentation/devicetree/bindings/misc/memory-state-time.txt @@ -0,0 +1,8 @@ +Memory bandwidth and frequency state tracking + +Required properties: +- compatible : should be: + "memory-state-time" +- freq-tbl: Should contain entries with each frequency in Hz. +- bw-buckets: Should contain upper-bound limits for each bandwidth bucket in Mbps. + Must match the framework power_profile.xml for the device. diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig index 241e3d9ee7aa..3242af01f8fa 100644 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig @@ -528,6 +528,12 @@ config UID_SYS_STATS_DEBUG help Per TASK based io statistics exported to /proc/uid_io +config MEMORY_STATE_TIME + tristate "Memory freq/bandwidth time statistics" + depends on PROFILING + help + Memory time statistics exported to /sys/kernel/memory_state_time + source "drivers/misc/c2port/Kconfig" source "drivers/misc/eeprom/Kconfig" source "drivers/misc/cb710/Kconfig" diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile index fca6aae030f0..f4d0fd9afcb8 100644 --- a/drivers/misc/Makefile +++ b/drivers/misc/Makefile @@ -59,3 +59,4 @@ obj-$(CONFIG_PCI_ENDPOINT_TEST) += pci_endpoint_test.o obj-$(CONFIG_OCXL) += ocxl/ obj-$(CONFIG_MISC_RTSX) += cardreader/ obj-$(CONFIG_UID_SYS_STATS) += uid_sys_stats.o +obj-$(CONFIG_MEMORY_STATE_TIME) += memory_state_time.o diff --git a/drivers/misc/memory_state_time.c b/drivers/misc/memory_state_time.c new file mode 100644 index 000000000000..34c797a06a31 --- /dev/null +++ b/drivers/misc/memory_state_time.c @@ -0,0 +1,454 @@ +/* drivers/misc/memory_state_time.c + * + * Copyright (C) 2016 Google, Inc. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define KERNEL_ATTR_RO(_name) \ +static struct kobj_attribute _name##_attr = __ATTR_RO(_name) + +#define KERNEL_ATTR_RW(_name) \ +static struct kobj_attribute _name##_attr = \ + __ATTR(_name, 0644, _name##_show, _name##_store) + +#define FREQ_HASH_BITS 4 +DECLARE_HASHTABLE(freq_hash_table, FREQ_HASH_BITS); + +static DEFINE_MUTEX(mem_lock); + +#define TAG "memory_state_time" +#define BW_NODE "/soc/memory-state-time" +#define FREQ_TBL "freq-tbl" +#define BW_TBL "bw-buckets" +#define NUM_SOURCES "num-sources" + +#define LOWEST_FREQ 2 + +static int curr_bw; +static int curr_freq; +static u32 *bw_buckets; +static u32 *freq_buckets; +static int num_freqs; +static int num_buckets; +static int registered_bw_sources; +static u64 last_update; +static bool init_success; +static struct workqueue_struct *memory_wq; +static u32 num_sources = 10; +static int *bandwidths; + +struct freq_entry { + int freq; + u64 *buckets; /* Bandwidth buckets. */ + struct hlist_node hash; +}; + +struct queue_container { + struct work_struct update_state; + int value; + u64 time_now; + int id; + struct mutex *lock; +}; + +static int find_bucket(int bw) +{ + int i; + + if (bw_buckets != NULL) { + for (i = 0; i < num_buckets; i++) { + if (bw_buckets[i] > bw) { + pr_debug("Found bucket %d for bandwidth %d\n", + i, bw); + return i; + } + } + return num_buckets - 1; + } + return 0; +} + +static u64 get_time_diff(u64 time_now) +{ + u64 ms; + + ms = time_now - last_update; + last_update = time_now; + return ms; +} + +static ssize_t show_stat_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + int i, j; + int len = 0; + struct freq_entry *freq_entry; + + for (i = 0; i < num_freqs; i++) { + hash_for_each_possible(freq_hash_table, freq_entry, hash, + freq_buckets[i]) { + if (freq_entry->freq == freq_buckets[i]) { + len += scnprintf(buf + len, PAGE_SIZE - len, + "%d ", freq_buckets[i]); + if (len >= PAGE_SIZE) + break; + for (j = 0; j < num_buckets; j++) { + len += scnprintf(buf + len, + PAGE_SIZE - len, + "%llu ", + freq_entry->buckets[j]); + } + len += scnprintf(buf + len, PAGE_SIZE - len, + "\n"); + } + } + } + pr_debug("Current Time: %llu\n", ktime_get_boot_ns()); + return len; +} +KERNEL_ATTR_RO(show_stat); + +static void update_table(u64 time_now) +{ + struct freq_entry *freq_entry; + + pr_debug("Last known bw %d freq %d\n", curr_bw, curr_freq); + hash_for_each_possible(freq_hash_table, freq_entry, hash, curr_freq) { + if (curr_freq == freq_entry->freq) { + freq_entry->buckets[find_bucket(curr_bw)] + += get_time_diff(time_now); + break; + } + } +} + +static bool freq_exists(int freq) +{ + int i; + + for (i = 0; i < num_freqs; i++) { + if (freq == freq_buckets[i]) + return true; + } + return false; +} + +static int calculate_total_bw(int bw, int index) +{ + int i; + int total_bw = 0; + + pr_debug("memory_state_time New bw %d for id %d\n", bw, index); + bandwidths[index] = bw; + for (i = 0; i < registered_bw_sources; i++) + total_bw += bandwidths[i]; + return total_bw; +} + +static void freq_update_do_work(struct work_struct *work) +{ + struct queue_container *freq_state_update + = container_of(work, struct queue_container, + update_state); + if (freq_state_update) { + mutex_lock(&mem_lock); + update_table(freq_state_update->time_now); + curr_freq = freq_state_update->value; + mutex_unlock(&mem_lock); + kfree(freq_state_update); + } +} + +static void bw_update_do_work(struct work_struct *work) +{ + struct queue_container *bw_state_update + = container_of(work, struct queue_container, + update_state); + if (bw_state_update) { + mutex_lock(&mem_lock); + update_table(bw_state_update->time_now); + curr_bw = calculate_total_bw(bw_state_update->value, + bw_state_update->id); + mutex_unlock(&mem_lock); + kfree(bw_state_update); + } +} + +static void memory_state_freq_update(struct memory_state_update_block *ub, + int value) +{ + if (IS_ENABLED(CONFIG_MEMORY_STATE_TIME)) { + if (freq_exists(value) && init_success) { + struct queue_container *freq_container + = kmalloc(sizeof(struct queue_container), + GFP_KERNEL); + if (!freq_container) + return; + INIT_WORK(&freq_container->update_state, + freq_update_do_work); + freq_container->time_now = ktime_get_boot_ns(); + freq_container->value = value; + pr_debug("Scheduling freq update in work queue\n"); + queue_work(memory_wq, &freq_container->update_state); + } else { + pr_debug("Freq does not exist.\n"); + } + } +} + +static void memory_state_bw_update(struct memory_state_update_block *ub, + int value) +{ + if (IS_ENABLED(CONFIG_MEMORY_STATE_TIME)) { + if (init_success) { + struct queue_container *bw_container + = kmalloc(sizeof(struct queue_container), + GFP_KERNEL); + if (!bw_container) + return; + INIT_WORK(&bw_container->update_state, + bw_update_do_work); + bw_container->time_now = ktime_get_boot_ns(); + bw_container->value = value; + bw_container->id = ub->id; + pr_debug("Scheduling bandwidth update in work queue\n"); + queue_work(memory_wq, &bw_container->update_state); + } + } +} + +struct memory_state_update_block *memory_state_register_frequency_source(void) +{ + struct memory_state_update_block *block; + + if (IS_ENABLED(CONFIG_MEMORY_STATE_TIME)) { + pr_debug("Allocating frequency source\n"); + block = kmalloc(sizeof(struct memory_state_update_block), + GFP_KERNEL); + if (!block) + return NULL; + block->update_call = memory_state_freq_update; + return block; + } + pr_err("Config option disabled.\n"); + return NULL; +} +EXPORT_SYMBOL_GPL(memory_state_register_frequency_source); + +struct memory_state_update_block *memory_state_register_bandwidth_source(void) +{ + struct memory_state_update_block *block; + + if (IS_ENABLED(CONFIG_MEMORY_STATE_TIME)) { + pr_debug("Allocating bandwidth source %d\n", + registered_bw_sources); + block = kmalloc(sizeof(struct memory_state_update_block), + GFP_KERNEL); + if (!block) + return NULL; + block->update_call = memory_state_bw_update; + if (registered_bw_sources < num_sources) { + block->id = registered_bw_sources++; + } else { + pr_err("Unable to allocate source; max number reached\n"); + kfree(block); + return NULL; + } + return block; + } + pr_err("Config option disabled.\n"); + return NULL; +} +EXPORT_SYMBOL_GPL(memory_state_register_bandwidth_source); + +/* Buckets are designated by their maximum. + * Returns the buckets decided by the capability of the device. + */ +static int get_bw_buckets(struct device *dev) +{ + int ret, lenb; + struct device_node *node = dev->of_node; + + of_property_read_u32(node, NUM_SOURCES, &num_sources); + if (of_find_property(node, BW_TBL, &lenb)) { + bandwidths = devm_kzalloc(dev, + sizeof(*bandwidths) * num_sources, GFP_KERNEL); + if (!bandwidths) + return -ENOMEM; + lenb /= sizeof(*bw_buckets); + bw_buckets = devm_kzalloc(dev, lenb * sizeof(*bw_buckets), + GFP_KERNEL); + if (!bw_buckets) { + devm_kfree(dev, bandwidths); + return -ENOMEM; + } + ret = of_property_read_u32_array(node, BW_TBL, bw_buckets, + lenb); + if (ret < 0) { + devm_kfree(dev, bandwidths); + devm_kfree(dev, bw_buckets); + pr_err("Unable to read bandwidth table from device tree.\n"); + return ret; + } + } + curr_bw = 0; + num_buckets = lenb; + return 0; +} + +/* Adds struct freq_entry nodes to the hashtable for each compatible frequency. + * Returns the supported number of frequencies. + */ +static int freq_buckets_init(struct device *dev) +{ + struct freq_entry *freq_entry; + int i; + int ret, lenf; + struct device_node *node = dev->of_node; + + if (of_find_property(node, FREQ_TBL, &lenf)) { + lenf /= sizeof(*freq_buckets); + freq_buckets = devm_kzalloc(dev, lenf * sizeof(*freq_buckets), + GFP_KERNEL); + if (!freq_buckets) + return -ENOMEM; + pr_debug("freqs found len %d\n", lenf); + ret = of_property_read_u32_array(node, FREQ_TBL, freq_buckets, + lenf); + if (ret < 0) { + devm_kfree(dev, freq_buckets); + pr_err("Unable to read frequency table from device tree.\n"); + return ret; + } + pr_debug("ret freq %d\n", ret); + } + num_freqs = lenf; + curr_freq = freq_buckets[LOWEST_FREQ]; + + for (i = 0; i < num_freqs; i++) { + freq_entry = devm_kzalloc(dev, sizeof(struct freq_entry), + GFP_KERNEL); + if (!freq_entry) + return -ENOMEM; + freq_entry->buckets = devm_kzalloc(dev, sizeof(u64)*num_buckets, + GFP_KERNEL); + if (!freq_entry->buckets) { + devm_kfree(dev, freq_entry); + return -ENOMEM; + } + pr_debug("memory_state_time Adding freq to ht %d\n", + freq_buckets[i]); + freq_entry->freq = freq_buckets[i]; + hash_add(freq_hash_table, &freq_entry->hash, freq_buckets[i]); + } + return 0; +} + +struct kobject *memory_kobj; +EXPORT_SYMBOL_GPL(memory_kobj); + +static struct attribute *memory_attrs[] = { + &show_stat_attr.attr, + NULL +}; + +static struct attribute_group memory_attr_group = { + .attrs = memory_attrs, +}; + +static int memory_state_time_probe(struct platform_device *pdev) +{ + int error; + + error = get_bw_buckets(&pdev->dev); + if (error) + return error; + error = freq_buckets_init(&pdev->dev); + if (error) + return error; + last_update = ktime_get_boot_ns(); + init_success = true; + + pr_debug("memory_state_time initialized with num_freqs %d\n", + num_freqs); + return 0; +} + +static const struct of_device_id match_table[] = { + { .compatible = "memory-state-time" }, + {} +}; + +static struct platform_driver memory_state_time_driver = { + .probe = memory_state_time_probe, + .driver = { + .name = "memory-state-time", + .of_match_table = match_table, + .owner = THIS_MODULE, + }, +}; + +static int __init memory_state_time_init(void) +{ + int error; + + hash_init(freq_hash_table); + memory_wq = create_singlethread_workqueue("memory_wq"); + if (!memory_wq) { + pr_err("Unable to create workqueue.\n"); + return -EINVAL; + } + /* + * Create sys/kernel directory for memory_state_time. + */ + memory_kobj = kobject_create_and_add(TAG, kernel_kobj); + if (!memory_kobj) { + pr_err("Unable to allocate memory_kobj for sysfs directory.\n"); + error = -ENOMEM; + goto wq; + } + error = sysfs_create_group(memory_kobj, &memory_attr_group); + if (error) { + pr_err("Unable to create sysfs folder.\n"); + goto kobj; + } + + error = platform_driver_register(&memory_state_time_driver); + if (error) { + pr_err("Unable to register memory_state_time platform driver.\n"); + goto group; + } + return 0; + +group: sysfs_remove_group(memory_kobj, &memory_attr_group); +kobj: kobject_put(memory_kobj); +wq: destroy_workqueue(memory_wq); + return error; +} +module_init(memory_state_time_init); diff --git a/include/linux/memory-state-time.h b/include/linux/memory-state-time.h new file mode 100644 index 000000000000..d2212b027866 --- /dev/null +++ b/include/linux/memory-state-time.h @@ -0,0 +1,42 @@ +/* include/linux/memory-state-time.h + * + * Copyright (C) 2016 Google, Inc. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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. + * + */ + +#include + +#define UPDATE_MEMORY_STATE(BLOCK, VALUE) BLOCK->update_call(BLOCK, VALUE) + +struct memory_state_update_block; + +typedef void (*memory_state_update_fn_t)(struct memory_state_update_block *ub, + int value); + +/* This struct is populated when you pass it to a memory_state_register* + * function. The update_call function is used for an update and defined in the + * typedef memory_state_update_fn_t + */ +struct memory_state_update_block { + memory_state_update_fn_t update_call; + int id; +}; + +/* Register a frequency struct memory_state_update_block to provide updates to + * memory_state_time about frequency changes using its update_call function. + */ +struct memory_state_update_block *memory_state_register_frequency_source(void); + +/* Register a bandwidth struct memory_state_update_block to provide updates to + * memory_state_time about bandwidth changes using its update_call function. + */ +struct memory_state_update_block *memory_state_register_bandwidth_source(void); From f12db87630c6a1cd5a5683c60ebff5b0a7a92c35 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Wed, 10 May 2017 11:21:27 +0200 Subject: [PATCH 0278/1103] ANDROID: memory_state_time: fix undefined behavior with missing DT properties kernelci reports warnings about unintialized variable usage: drivers/misc/memory_state_time.c:351:12: warning: 'lenf' is used uninitialized in this function [-Wuninitialized] drivers/misc/memory_state_time.c:321:14: warning: 'lenb' is used uninitialized in this function [-Wuninitialized] In both cases we try to continue without a DT property but use the length that has not been assigned at this point. This rearranges the code in the two functions to bail out earlier in case of an error. The patch is needed for both android-common-4.9, 4.4 and 3.18. Link: https://kernelci.org/build/id/591177f459b5147648b12d54/logs/ Fixes: ad3c02f8b3a5 ("ANDROID: Implement memory_state_time, used by qcom,cpubw") Cc: James Carr Signed-off-by: Arnd Bergmann Signed-off-by: Greg Kroah-Hartman --- drivers/misc/memory_state_time.c | 78 ++++++++++++++++++-------------- 1 file changed, 43 insertions(+), 35 deletions(-) diff --git a/drivers/misc/memory_state_time.c b/drivers/misc/memory_state_time.c index 34c797a06a31..ba94dcf09169 100644 --- a/drivers/misc/memory_state_time.c +++ b/drivers/misc/memory_state_time.c @@ -296,27 +296,31 @@ static int get_bw_buckets(struct device *dev) struct device_node *node = dev->of_node; of_property_read_u32(node, NUM_SOURCES, &num_sources); - if (of_find_property(node, BW_TBL, &lenb)) { - bandwidths = devm_kzalloc(dev, - sizeof(*bandwidths) * num_sources, GFP_KERNEL); - if (!bandwidths) - return -ENOMEM; - lenb /= sizeof(*bw_buckets); - bw_buckets = devm_kzalloc(dev, lenb * sizeof(*bw_buckets), - GFP_KERNEL); - if (!bw_buckets) { - devm_kfree(dev, bandwidths); - return -ENOMEM; - } - ret = of_property_read_u32_array(node, BW_TBL, bw_buckets, - lenb); - if (ret < 0) { - devm_kfree(dev, bandwidths); - devm_kfree(dev, bw_buckets); - pr_err("Unable to read bandwidth table from device tree.\n"); - return ret; - } + if (!of_find_property(node, BW_TBL, &lenb)) { + pr_err("Missing %s property\n", BW_TBL); + return -ENODATA; + } + + bandwidths = devm_kzalloc(dev, + sizeof(*bandwidths) * num_sources, GFP_KERNEL); + if (!bandwidths) + return -ENOMEM; + lenb /= sizeof(*bw_buckets); + bw_buckets = devm_kzalloc(dev, lenb * sizeof(*bw_buckets), + GFP_KERNEL); + if (!bw_buckets) { + devm_kfree(dev, bandwidths); + return -ENOMEM; + } + ret = of_property_read_u32_array(node, BW_TBL, bw_buckets, + lenb); + if (ret < 0) { + devm_kfree(dev, bandwidths); + devm_kfree(dev, bw_buckets); + pr_err("Unable to read bandwidth table from device tree.\n"); + return ret; } + curr_bw = 0; num_buckets = lenb; return 0; @@ -332,22 +336,26 @@ static int freq_buckets_init(struct device *dev) int ret, lenf; struct device_node *node = dev->of_node; - if (of_find_property(node, FREQ_TBL, &lenf)) { - lenf /= sizeof(*freq_buckets); - freq_buckets = devm_kzalloc(dev, lenf * sizeof(*freq_buckets), - GFP_KERNEL); - if (!freq_buckets) - return -ENOMEM; - pr_debug("freqs found len %d\n", lenf); - ret = of_property_read_u32_array(node, FREQ_TBL, freq_buckets, - lenf); - if (ret < 0) { - devm_kfree(dev, freq_buckets); - pr_err("Unable to read frequency table from device tree.\n"); - return ret; - } - pr_debug("ret freq %d\n", ret); + if (!of_find_property(node, FREQ_TBL, &lenf)) { + pr_err("Missing %s property\n", FREQ_TBL); + return -ENODATA; } + + lenf /= sizeof(*freq_buckets); + freq_buckets = devm_kzalloc(dev, lenf * sizeof(*freq_buckets), + GFP_KERNEL); + if (!freq_buckets) + return -ENOMEM; + pr_debug("freqs found len %d\n", lenf); + ret = of_property_read_u32_array(node, FREQ_TBL, freq_buckets, + lenf); + if (ret < 0) { + devm_kfree(dev, freq_buckets); + pr_err("Unable to read frequency table from device tree.\n"); + return ret; + } + pr_debug("ret freq %d\n", ret); + num_freqs = lenf; curr_freq = freq_buckets[LOWEST_FREQ]; From fa4022481c12d33cc7538ebf354b9102f997261a Mon Sep 17 00:00:00 2001 From: Nick Bray Date: Thu, 30 Nov 2017 15:49:54 -0800 Subject: [PATCH 0279/1103] ANDROID: initramfs: call free_initrd() when skipping init Memory allocated for initrd would not be reclaimed if initializing ramfs was skipped. Bug: 69901741 Test: "grep MemTotal /proc/meminfo" increases by a few MB on an Android device with a/b boot. Change-Id: Ifbe094d303ed12cfd6de6aa004a8a19137a2f58a Signed-off-by: Nick Bray --- init/initramfs.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/init/initramfs.c b/init/initramfs.c index cc21c6cfd20f..c18b91dec432 100644 --- a/init/initramfs.c +++ b/init/initramfs.c @@ -613,8 +613,11 @@ static int __init populate_rootfs(void) { char *err; - if (do_skip_initramfs) + if (do_skip_initramfs) { + if (initrd_start) + free_initrd(); return default_rootfs(); + } /* Load the built in initramfs */ err = unpack_to_rootfs(__initramfs_start, __initramfs_size); From f4a1908191857cfa8ada678628961efcaf488e5f Mon Sep 17 00:00:00 2001 From: Dmitry Shmidt Date: Mon, 8 Jan 2018 15:21:57 -0800 Subject: [PATCH 0280/1103] ANDROID: netfilter: xt_qtaguid: Fix 4.14 compilation struct xt_action_param was changed: in, out, family and hooknum were moved to struct nf_hook_state *state in, out, pf and hook Replace atomic_read() with refcount_read() Change-Id: If463bf84db08fe382baa825ca7818cab2150b60d Signed-off-by: Dmitry Shmidt --- net/netfilter/xt_qtaguid.c | 80 +++++++++++++++++--------------- net/netfilter/xt_qtaguid_print.c | 3 +- 2 files changed, 44 insertions(+), 39 deletions(-) diff --git a/net/netfilter/xt_qtaguid.c b/net/netfilter/xt_qtaguid.c index 661a496f2c04..c6a51d91f26f 100644 --- a/net/netfilter/xt_qtaguid.c +++ b/net/netfilter/xt_qtaguid.c @@ -28,6 +28,7 @@ #include #include #include +#include #if defined(CONFIG_IP6_NF_IPTABLES) || defined(CONFIG_IP6_NF_IPTABLES_MODULE) #include @@ -1085,7 +1086,7 @@ static int ipx_proto(const struct sk_buff *skb, { int thoff = 0, tproto; - switch (par->family) { + switch (par->state->pf) { case NFPROTO_IPV6: tproto = ipv6_find_hdr(skb, &thoff, -1, NULL, NULL); if (tproto < 0) @@ -1180,27 +1181,29 @@ static void get_dev_and_dir(const struct sk_buff *skb, enum ifs_tx_rx *direction, const struct net_device **el_dev) { + const struct nf_hook_state *parst = par->state; + BUG_ON(!direction || !el_dev); - if (par->in) { - *el_dev = par->in; + if (parst->in) { + *el_dev = parst->in; *direction = IFS_RX; - } else if (par->out) { - *el_dev = par->out; + } else if (parst->out) { + *el_dev = parst->out; *direction = IFS_TX; } else { - pr_err("qtaguid[%d]: %s(): no par->in/out?!!\n", - par->hooknum, __func__); + pr_err("qtaguid[%d]: %s(): no par->state->in/out?!!\n", + parst->hook, __func__); BUG(); } if (unlikely(!(*el_dev)->name)) { pr_err("qtaguid[%d]: %s(): no dev->name?!!\n", - par->hooknum, __func__); + parst->hook, __func__); BUG(); } if (skb->dev && *el_dev != skb->dev) { MT_DEBUG("qtaguid[%d]: skb->dev=%p %s vs par->%s=%p %s\n", - par->hooknum, skb->dev, skb->dev->name, + parst->hook, skb->dev, skb->dev->name, *direction == IFS_RX ? "in" : "out", *el_dev, (*el_dev)->name); } @@ -1215,6 +1218,7 @@ static void get_dev_and_dir(const struct sk_buff *skb, static void iface_stat_update_from_skb(const struct sk_buff *skb, struct xt_action_param *par) { + const struct nf_hook_state *parst = par->state; struct iface_stat *entry; const struct net_device *el_dev; enum ifs_tx_rx direction; @@ -1225,19 +1229,19 @@ static void iface_stat_update_from_skb(const struct sk_buff *skb, proto = ipx_proto(skb, par); MT_DEBUG("qtaguid[%d]: iface_stat: %s(%s): " "type=%d fam=%d proto=%d dir=%d\n", - par->hooknum, __func__, el_dev->name, el_dev->type, - par->family, proto, direction); + parst->hook, __func__, el_dev->name, el_dev->type, + parst->pf, proto, direction); spin_lock_bh(&iface_stat_list_lock); entry = get_iface_entry(el_dev->name); if (entry == NULL) { IF_DEBUG("qtaguid[%d]: iface_stat: %s(%s): not tracked\n", - par->hooknum, __func__, el_dev->name); + parst->hook, __func__, el_dev->name); spin_unlock_bh(&iface_stat_list_lock); return; } - IF_DEBUG("qtaguid[%d]: %s(%s): entry=%p\n", par->hooknum, __func__, + IF_DEBUG("qtaguid[%d]: %s(%s): entry=%p\n", parst->hook, __func__, el_dev->name, entry); data_counters_update(&entry->totals_via_skb, 0, direction, proto, @@ -1583,11 +1587,12 @@ static int __init iface_stat_init(struct proc_dir_entry *parent_procdir) static struct sock *qtaguid_find_sk(const struct sk_buff *skb, struct xt_action_param *par) { + const struct nf_hook_state *parst = par->state; struct sock *sk; - unsigned int hook_mask = (1 << par->hooknum); + unsigned int hook_mask = (1 << parst->hook); MT_DEBUG("qtaguid[%d]: find_sk(skb=%p) family=%d\n", - par->hooknum, skb, par->family); + parst->hook, skb, parst->pf); /* * Let's not abuse the the xt_socket_get*_sk(), or else it will @@ -1596,12 +1601,12 @@ static struct sock *qtaguid_find_sk(const struct sk_buff *skb, if (!(hook_mask & XT_SOCKET_SUPPORTED_HOOKS)) return NULL; - switch (par->family) { + switch (parst->pf) { case NFPROTO_IPV6: - sk = nf_sk_lookup_slow_v6(dev_net(skb->dev), skb, par->in); + sk = nf_sk_lookup_slow_v6(dev_net(skb->dev), skb, parst->in); break; case NFPROTO_IPV4: - sk = nf_sk_lookup_slow_v4(dev_net(skb->dev), skb, par->in); + sk = nf_sk_lookup_slow_v4(dev_net(skb->dev), skb, parst->in); break; default: return NULL; @@ -1609,7 +1614,7 @@ static struct sock *qtaguid_find_sk(const struct sk_buff *skb, if (sk) { MT_DEBUG("qtaguid[%d]: %p->sk_proto=%u->sk_state=%d\n", - par->hooknum, sk, sk->sk_protocol, sk->sk_state); + parst->hook, sk, sk->sk_protocol, sk->sk_state); } return sk; } @@ -1625,8 +1630,8 @@ static void account_for_uid(const struct sk_buff *skb, get_dev_and_dir(skb, par, &direction, &el_dev); proto = ipx_proto(skb, par); MT_DEBUG("qtaguid[%d]: dev name=%s type=%d fam=%d proto=%d dir=%d\n", - par->hooknum, el_dev->name, el_dev->type, - par->family, proto, direction); + par->state->hook, el_dev->name, el_dev->type, + par->state->pf, proto, direction); if_tag_stat_update(el_dev->name, uid, skb->sk ? skb->sk : alternate_sk, @@ -1637,6 +1642,7 @@ static void account_for_uid(const struct sk_buff *skb, static bool qtaguid_mt(const struct sk_buff *skb, struct xt_action_param *par) { const struct xt_qtaguid_match_info *info = par->matchinfo; + const struct nf_hook_state *parst = par->state; const struct file *filp; bool got_sock = false; struct sock *sk; @@ -1653,7 +1659,7 @@ static bool qtaguid_mt(const struct sk_buff *skb, struct xt_action_param *par) return (info->match ^ info->invert) == 0; MT_DEBUG("qtaguid[%d]: entered skb=%p par->in=%p/out=%p fam=%d\n", - par->hooknum, skb, par->in, par->out, par->family); + parst->hook, skb, parst->in, parst->out, parst->pf); atomic64_inc(&qtu_events.match_calls); if (skb == NULL) { @@ -1661,7 +1667,7 @@ static bool qtaguid_mt(const struct sk_buff *skb, struct xt_action_param *par) goto ret_res; } - switch (par->hooknum) { + switch (parst->hook) { case NF_INET_PRE_ROUTING: case NF_INET_POST_ROUTING: atomic64_inc(&qtu_events.match_calls_prepost); @@ -1718,7 +1724,7 @@ static bool qtaguid_mt(const struct sk_buff *skb, struct xt_action_param *par) atomic64_inc(&qtu_events.match_found_sk); } MT_DEBUG("qtaguid[%d]: sk=%p got_sock=%d fam=%d proto=%d\n", - par->hooknum, sk, got_sock, par->family, ipx_proto(skb, par)); + parst->hook, sk, got_sock, parst->pf, ipx_proto(skb, par)); if (!sk) { /* @@ -1728,7 +1734,7 @@ static bool qtaguid_mt(const struct sk_buff *skb, struct xt_action_param *par) */ if (do_tag_stat) account_for_uid(skb, sk, 0, par); - MT_DEBUG("qtaguid[%d]: leaving (sk=NULL)\n", par->hooknum); + MT_DEBUG("qtaguid[%d]: leaving (sk=NULL)\n", parst->hook); res = (info->match ^ info->invert) == 0; atomic64_inc(&qtu_events.match_no_sk); goto put_sock_ret_res; @@ -1755,7 +1761,7 @@ static bool qtaguid_mt(const struct sk_buff *skb, struct xt_action_param *par) uid_lte(sock_uid, uid_max)) ^ !(info->invert & XT_QTAGUID_UID)) { MT_DEBUG("qtaguid[%d]: leaving uid not matching\n", - par->hooknum); + parst->hook); res = false; goto put_sock_ret_res; } @@ -1766,7 +1772,7 @@ static bool qtaguid_mt(const struct sk_buff *skb, struct xt_action_param *par) set_sk_callback_lock = true; read_lock_bh(&sk->sk_callback_lock); MT_DEBUG("qtaguid[%d]: sk=%p->sk_socket=%p->file=%p\n", - par->hooknum, sk, sk->sk_socket, + parst->hook, sk, sk->sk_socket, sk->sk_socket ? sk->sk_socket->file : (void *)-1LL); filp = sk->sk_socket ? sk->sk_socket->file : NULL; if (!filp) { @@ -1776,18 +1782,18 @@ static bool qtaguid_mt(const struct sk_buff *skb, struct xt_action_param *par) goto put_sock_ret_res; } MT_DEBUG("qtaguid[%d]: filp...uid=%u\n", - par->hooknum, filp ? + parst->hook, filp ? from_kuid(&init_user_ns, filp->f_cred->fsuid) : -1); if ((gid_gte(filp->f_cred->fsgid, gid_min) && gid_lte(filp->f_cred->fsgid, gid_max)) ^ !(info->invert & XT_QTAGUID_GID)) { MT_DEBUG("qtaguid[%d]: leaving gid not matching\n", - par->hooknum); + parst->hook); res = false; goto put_sock_ret_res; } } - MT_DEBUG("qtaguid[%d]: leaving matched\n", par->hooknum); + MT_DEBUG("qtaguid[%d]: leaving matched\n", parst->hook); res = true; put_sock_ret_res: @@ -1796,7 +1802,7 @@ static bool qtaguid_mt(const struct sk_buff *skb, struct xt_action_param *par) if (set_sk_callback_lock) read_unlock_bh(&sk->sk_callback_lock); ret_res: - MT_DEBUG("qtaguid[%d]: left %d\n", par->hooknum, res); + MT_DEBUG("qtaguid[%d]: left %d\n", parst->hook, res); return res; } @@ -1930,7 +1936,7 @@ static int qtaguid_ctrl_proc_show(struct seq_file *m, void *v) uid, sock_tag_entry->pid ); - sk_ref_count = atomic_read( + sk_ref_count = refcount_read( &sock_tag_entry->sk->sk_refcnt); seq_printf(m, "sock=%pK tag=0x%llx (uid=%u) pid=%u " "f_count=%d\n", @@ -2233,7 +2239,7 @@ static int ctrl_cmd_tag(const char *input) goto err; } CT_DEBUG("qtaguid: ctrl_tag(%s): socket->...->sk_refcnt=%d ->sk=%p\n", - input, atomic_read(&el_socket->sk->sk_refcnt), + input, refcount_read(&el_socket->sk->sk_refcnt), el_socket->sk); if (argc < 3) { acct_tag = make_atag_from_value(0); @@ -2279,7 +2285,7 @@ static int ctrl_cmd_tag(const char *input) CT_DEBUG("qtaguid: ctrl_tag(%s): retag for sk=%p " "st@%p ...->sk_refcnt=%d\n", input, el_socket->sk, sock_tag_entry, - atomic_read(&el_socket->sk->sk_refcnt)); + refcount_read(&el_socket->sk->sk_refcnt)); prev_tag_ref_entry = lookup_tag_ref(sock_tag_entry->tag, &uid_tag_data_entry); BUG_ON(IS_ERR_OR_NULL(prev_tag_ref_entry)); @@ -2334,7 +2340,7 @@ static int ctrl_cmd_tag(const char *input) /* We keep the ref to the sk until it is untagged */ CT_DEBUG("qtaguid: ctrl_tag(%s): done st@%p ...->sk_refcnt=%d\n", input, sock_tag_entry, - atomic_read(&el_socket->sk->sk_refcnt)); + refcount_read(&el_socket->sk->sk_refcnt)); sockfd_put(el_socket); return 0; @@ -2344,7 +2350,7 @@ static int ctrl_cmd_tag(const char *input) free_tag_ref_from_utd_entry(tag_ref_entry, uid_tag_data_entry); err_put: CT_DEBUG("qtaguid: ctrl_tag(%s): done. ...->sk_refcnt=%d\n", - input, atomic_read(&el_socket->sk->sk_refcnt) - 1); + input, refcount_read(&el_socket->sk->sk_refcnt) - 1); /* Release the sock_fd that was grabbed by sockfd_lookup(). */ sockfd_put(el_socket); return res; @@ -2443,7 +2449,7 @@ int qtaguid_untag(struct socket *el_socket, bool kernel) sock_put(sock_tag_entry->sk); CT_DEBUG("qtaguid: done. st@%p ...->sk_refcnt=%d\n", sock_tag_entry, - atomic_read(&el_socket->sk->sk_refcnt)); + refcount_read(&el_socket->sk->sk_refcnt)); kfree(sock_tag_entry); atomic64_inc(&qtu_events.sockets_untagged); diff --git a/net/netfilter/xt_qtaguid_print.c b/net/netfilter/xt_qtaguid_print.c index 2a7190d285e6..cab478eba9c8 100644 --- a/net/netfilter/xt_qtaguid_print.c +++ b/net/netfilter/xt_qtaguid_print.c @@ -239,8 +239,7 @@ char *pp_sock_tag(struct sock_tag *st) "sock_node=rb_node{...}, " "sk=%p (f_count=%d), list=list_head{...}, " "pid=%u, tag=%s}", - st, st->sk, atomic_read( - &st->sk->sk_refcnt), + st, st->sk, refcount_read(&st->sk->sk_refcnt), st->pid, tag_str); _bug_on_err_or_null(res); kfree(tag_str); From a0c2ce8dd59b80aaca7471596e3060f42e4eb4af Mon Sep 17 00:00:00 2001 From: Artem Borisov Date: Sat, 13 Jan 2018 18:03:40 +0300 Subject: [PATCH 0281/1103] ANDROID: uid_sys_stats: fix the comment It is not uid_cputime.c anymore. Change-Id: I7effc2a449c1f9cba9d86a7b122a9c05fc266405 Signed-off-by: Artem Borisov --- drivers/misc/uid_sys_stats.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/misc/uid_sys_stats.c b/drivers/misc/uid_sys_stats.c index 7069b0b064a5..9f5b6d1dde0a 100644 --- a/drivers/misc/uid_sys_stats.c +++ b/drivers/misc/uid_sys_stats.c @@ -1,4 +1,4 @@ -/* drivers/misc/uid_cputime.c +/* drivers/misc/uid_sys_stats.c * * Copyright (C) 2014 - 2015 Google, Inc. * From e58835383f28270c36fa73a26f8695e899e8586d Mon Sep 17 00:00:00 2001 From: Dmitry Shmidt Date: Wed, 24 Jan 2018 13:59:50 -0800 Subject: [PATCH 0282/1103] ANDROID: Fix script to fetch android kernel config fragments for 4.14 Change-Id: I6036d0cc9d4f28f0df0e8d3267034b34ec276a08 Signed-off-by: Dmitry Shmidt --- kernel/configs/android-fetch-configs.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kernel/configs/android-fetch-configs.sh b/kernel/configs/android-fetch-configs.sh index a5b56d4908dc..2dcd2981e1be 100755 --- a/kernel/configs/android-fetch-configs.sh +++ b/kernel/configs/android-fetch-configs.sh @@ -1,4 +1,4 @@ #!/bin/sh -curl https://android.googlesource.com/kernel/configs/+archive/master/android-4.9.tar.gz | tar xzv +curl https://android.googlesource.com/kernel/configs/+archive/master/android-4.14.tar.gz | tar xzv From bbba2b7ba33f157fa5aa2d9c8c069b39f91624e5 Mon Sep 17 00:00:00 2001 From: Daniel Campello Date: Mon, 20 Jul 2015 16:23:50 -0700 Subject: [PATCH 0283/1103] ANDROID: Included sdcardfs source code for kernel 3.0 Only included the source code as is for kernel 3.0. Following patches take care of porting this file system to version 3.10. Change-Id: I09e76db77cd98a059053ba5b6fd88572a4b75b5b Signed-off-by: Daniel Campello --- fs/Kconfig | 1 + fs/Makefile | 5 +- fs/sdcardfs/Kconfig | 18 + fs/sdcardfs/Makefile | 7 + fs/sdcardfs/dentry.c | 182 ++++++++ fs/sdcardfs/derived_perm.c | 290 ++++++++++++ fs/sdcardfs/file.c | 357 +++++++++++++++ fs/sdcardfs/hashtable.h | 190 ++++++++ fs/sdcardfs/inode.c | 886 +++++++++++++++++++++++++++++++++++++ fs/sdcardfs/lookup.c | 386 ++++++++++++++++ fs/sdcardfs/main.c | 425 ++++++++++++++++++ fs/sdcardfs/mmap.c | 82 ++++ fs/sdcardfs/multiuser.h | 37 ++ fs/sdcardfs/packagelist.c | 458 +++++++++++++++++++ fs/sdcardfs/sdcardfs.h | 493 +++++++++++++++++++++ fs/sdcardfs/strtok.h | 75 ++++ fs/sdcardfs/super.c | 229 ++++++++++ include/uapi/linux/magic.h | 2 + 18 files changed, 4121 insertions(+), 2 deletions(-) create mode 100644 fs/sdcardfs/Kconfig create mode 100644 fs/sdcardfs/Makefile create mode 100644 fs/sdcardfs/dentry.c create mode 100644 fs/sdcardfs/derived_perm.c create mode 100644 fs/sdcardfs/file.c create mode 100644 fs/sdcardfs/hashtable.h create mode 100644 fs/sdcardfs/inode.c create mode 100644 fs/sdcardfs/lookup.c create mode 100644 fs/sdcardfs/main.c create mode 100644 fs/sdcardfs/mmap.c create mode 100644 fs/sdcardfs/multiuser.h create mode 100644 fs/sdcardfs/packagelist.c create mode 100644 fs/sdcardfs/sdcardfs.h create mode 100644 fs/sdcardfs/strtok.h create mode 100644 fs/sdcardfs/super.c diff --git a/fs/Kconfig b/fs/Kconfig index ac474a61be37..ffbd9a4356ed 100644 --- a/fs/Kconfig +++ b/fs/Kconfig @@ -234,6 +234,7 @@ source "fs/orangefs/Kconfig" source "fs/adfs/Kconfig" source "fs/affs/Kconfig" source "fs/ecryptfs/Kconfig" +source "fs/sdcardfs/Kconfig" source "fs/hfs/Kconfig" source "fs/hfsplus/Kconfig" source "fs/befs/Kconfig" diff --git a/fs/Makefile b/fs/Makefile index 293733f61594..57744e192335 100644 --- a/fs/Makefile +++ b/fs/Makefile @@ -4,7 +4,7 @@ # # 14 Sep 2000, Christoph Hellwig # Rewritten to use lists instead of if-statements. -# +# obj-y := open.o read_write.o file_table.o super.o \ char_dev.o stat.o exec.o pipe.o namei.o fcntl.o \ @@ -62,7 +62,7 @@ obj-y += devpts/ obj-$(CONFIG_PROFILING) += dcookies.o obj-$(CONFIG_DLM) += dlm/ - + # Do not add any filesystems before this line obj-$(CONFIG_FSCACHE) += fscache/ obj-$(CONFIG_REISERFS_FS) += reiserfs/ @@ -84,6 +84,7 @@ obj-$(CONFIG_ISO9660_FS) += isofs/ obj-$(CONFIG_HFSPLUS_FS) += hfsplus/ # Before hfs to find wrapped HFS+ obj-$(CONFIG_HFS_FS) += hfs/ obj-$(CONFIG_ECRYPT_FS) += ecryptfs/ +obj-$(CONFIG_SDCARD_FS) += sdcardfs/ obj-$(CONFIG_VXFS_FS) += freevxfs/ obj-$(CONFIG_NFS_FS) += nfs/ obj-$(CONFIG_EXPORTFS) += exportfs/ diff --git a/fs/sdcardfs/Kconfig b/fs/sdcardfs/Kconfig new file mode 100644 index 000000000000..657f4958e8d6 --- /dev/null +++ b/fs/sdcardfs/Kconfig @@ -0,0 +1,18 @@ +config SDCARD_FS + tristate "sdcard file system" + depends on EXPERIMENTAL + default n + help + Sdcardfs is based on Wrapfs file system. + +config SDCARD_FS_FADV_NOACTIVE + bool "sdcardfs fadvise noactive support" + depends on FADV_NOACTIVE + default y + help + Sdcardfs supports fadvise noactive mode. + +config SDCARD_FS_CI_SEARCH + tristate "sdcardfs case-insensitive search support" + depends on SDCARD_FS + default y diff --git a/fs/sdcardfs/Makefile b/fs/sdcardfs/Makefile new file mode 100644 index 000000000000..b84fbb2b45a4 --- /dev/null +++ b/fs/sdcardfs/Makefile @@ -0,0 +1,7 @@ +SDCARDFS_VERSION="0.1" + +EXTRA_CFLAGS += -DSDCARDFS_VERSION=\"$(SDCARDFS_VERSION)\" + +obj-$(CONFIG_SDCARD_FS) += sdcardfs.o + +sdcardfs-y := dentry.o file.o inode.o main.o super.o lookup.o mmap.o packagelist.o derived_perm.o diff --git a/fs/sdcardfs/dentry.c b/fs/sdcardfs/dentry.c new file mode 100644 index 000000000000..4572a5403bb2 --- /dev/null +++ b/fs/sdcardfs/dentry.c @@ -0,0 +1,182 @@ +/* + * fs/sdcardfs/dentry.c + * + * Copyright (c) 2013 Samsung Electronics Co. Ltd + * Authors: Daeho Jeong, Woojoong Lee, Seunghwan Hyun, + * Sunghwan Yun, Sungjong Seo + * + * This program has been developed as a stackable file system based on + * the WrapFS which written by + * + * Copyright (c) 1998-2011 Erez Zadok + * Copyright (c) 2009 Shrikar Archak + * Copyright (c) 2003-2011 Stony Brook University + * Copyright (c) 2003-2011 The Research Foundation of SUNY + * + * This file is dual licensed. It may be redistributed and/or modified + * under the terms of the Apache 2.0 License OR version 2 of the GNU + * General Public License. + */ + +#include "sdcardfs.h" +#include "linux/ctype.h" + +/* + * returns: -ERRNO if error (returned to user) + * 0: tell VFS to invalidate dentry + * 1: dentry is valid + */ +static int sdcardfs_d_revalidate(struct dentry *dentry, struct nameidata *nd) +{ + int err = 1; + struct path parent_lower_path, lower_path; + struct dentry *parent_dentry = NULL; + struct dentry *parent_lower_dentry = NULL; + struct dentry *lower_cur_parent_dentry = NULL; + struct dentry *lower_dentry = NULL; + + if (nd && nd->flags & LOOKUP_RCU) + return -ECHILD; + + spin_lock(&dentry->d_lock); + if (IS_ROOT(dentry)) { + spin_unlock(&dentry->d_lock); + return 1; + } + spin_unlock(&dentry->d_lock); + + /* check uninitialized obb_dentry and + * whether the base obbpath has been changed or not */ + if (is_obbpath_invalid(dentry)) { + d_drop(dentry); + return 0; + } + + parent_dentry = dget_parent(dentry); + sdcardfs_get_lower_path(parent_dentry, &parent_lower_path); + sdcardfs_get_real_lower(dentry, &lower_path); + parent_lower_dentry = parent_lower_path.dentry; + lower_dentry = lower_path.dentry; + lower_cur_parent_dentry = dget_parent(lower_dentry); + + spin_lock(&lower_dentry->d_lock); + if (d_unhashed(lower_dentry)) { + spin_unlock(&lower_dentry->d_lock); + d_drop(dentry); + err = 0; + goto out; + } + spin_unlock(&lower_dentry->d_lock); + + if (parent_lower_dentry != lower_cur_parent_dentry) { + d_drop(dentry); + err = 0; + goto out; + } + + if (dentry < lower_dentry) { + spin_lock(&dentry->d_lock); + spin_lock(&lower_dentry->d_lock); + } else { + spin_lock(&lower_dentry->d_lock); + spin_lock(&dentry->d_lock); + } + + if (dentry->d_name.len != lower_dentry->d_name.len) { + __d_drop(dentry); + err = 0; + } else if (strncasecmp(dentry->d_name.name, lower_dentry->d_name.name, + dentry->d_name.len) != 0) { + __d_drop(dentry); + err = 0; + } + + if (dentry < lower_dentry) { + spin_unlock(&lower_dentry->d_lock); + spin_unlock(&dentry->d_lock); + } else { + spin_unlock(&dentry->d_lock); + spin_unlock(&lower_dentry->d_lock); + } + +out: + dput(parent_dentry); + dput(lower_cur_parent_dentry); + sdcardfs_put_lower_path(parent_dentry, &parent_lower_path); + sdcardfs_put_real_lower(dentry, &lower_path); + return err; +} + +static void sdcardfs_d_release(struct dentry *dentry) +{ + /* release and reset the lower paths */ + if(has_graft_path(dentry)) { + sdcardfs_put_reset_orig_path(dentry); + } + sdcardfs_put_reset_lower_path(dentry); + free_dentry_private_data(dentry); + return; +} + +static int sdcardfs_hash_ci(const struct dentry *dentry, + const struct inode *inode, struct qstr *qstr) +{ + /* + * This function is copy of vfat_hashi. + * FIXME Should we support national language? + * Refer to vfat_hashi() + * struct nls_table *t = MSDOS_SB(dentry->d_sb)->nls_io; + */ + const unsigned char *name; + unsigned int len; + unsigned long hash; + + name = qstr->name; + //len = vfat_striptail_len(qstr); + len = qstr->len; + + hash = init_name_hash(); + while (len--) + //hash = partial_name_hash(nls_tolower(t, *name++), hash); + hash = partial_name_hash(tolower(*name++), hash); + qstr->hash = end_name_hash(hash); + + return 0; +} + +/* + * Case insensitive compare of two vfat names. + */ +static int sdcardfs_cmp_ci(const struct dentry *parent, + const struct inode *pinode, + const struct dentry *dentry, const struct inode *inode, + unsigned int len, const char *str, const struct qstr *name) +{ + /* This function is copy of vfat_cmpi */ + // FIXME Should we support national language? + //struct nls_table *t = MSDOS_SB(parent->d_sb)->nls_io; + //unsigned int alen, blen; + + /* A filename cannot end in '.' or we treat it like it has none */ + /* + alen = vfat_striptail_len(name); + blen = __vfat_striptail_len(len, str); + if (alen == blen) { + if (nls_strnicmp(t, name->name, str, alen) == 0) + return 0; + } + */ + if (name->len == len) { + if (strncasecmp(name->name, str, len) == 0) + return 0; + } + return 1; +} + +const struct dentry_operations sdcardfs_ci_dops = { + .d_revalidate = sdcardfs_d_revalidate, + .d_release = sdcardfs_d_release, + .d_hash = sdcardfs_hash_ci, + .d_compare = sdcardfs_cmp_ci, +}; + diff --git a/fs/sdcardfs/derived_perm.c b/fs/sdcardfs/derived_perm.c new file mode 100644 index 000000000000..00c33a471dcc --- /dev/null +++ b/fs/sdcardfs/derived_perm.c @@ -0,0 +1,290 @@ +/* + * fs/sdcardfs/derived_perm.c + * + * Copyright (c) 2013 Samsung Electronics Co. Ltd + * Authors: Daeho Jeong, Woojoong Lee, Seunghwan Hyun, + * Sunghwan Yun, Sungjong Seo + * + * This program has been developed as a stackable file system based on + * the WrapFS which written by + * + * Copyright (c) 1998-2011 Erez Zadok + * Copyright (c) 2009 Shrikar Archak + * Copyright (c) 2003-2011 Stony Brook University + * Copyright (c) 2003-2011 The Research Foundation of SUNY + * + * This file is dual licensed. It may be redistributed and/or modified + * under the terms of the Apache 2.0 License OR version 2 of the GNU + * General Public License. + */ + +#include "sdcardfs.h" + +/* copy derived state from parent inode */ +static void inherit_derived_state(struct inode *parent, struct inode *child) +{ + struct sdcardfs_inode_info *pi = SDCARDFS_I(parent); + struct sdcardfs_inode_info *ci = SDCARDFS_I(child); + + ci->perm = PERM_INHERIT; + ci->userid = pi->userid; + ci->d_uid = pi->d_uid; + ci->d_gid = pi->d_gid; + ci->d_mode = pi->d_mode; +} + +/* helper function for derived state */ +void setup_derived_state(struct inode *inode, perm_t perm, + userid_t userid, uid_t uid, gid_t gid, mode_t mode) +{ + struct sdcardfs_inode_info *info = SDCARDFS_I(inode); + + info->perm = perm; + info->userid = userid; + info->d_uid = uid; + info->d_gid = gid; + info->d_mode = mode; +} + +void get_derived_permission(struct dentry *parent, struct dentry *dentry) +{ + struct sdcardfs_sb_info *sbi = SDCARDFS_SB(dentry->d_sb); + struct sdcardfs_inode_info *info = SDCARDFS_I(dentry->d_inode); + struct sdcardfs_inode_info *parent_info= SDCARDFS_I(parent->d_inode); + appid_t appid; + + /* By default, each inode inherits from its parent. + * the properties are maintained on its private fields + * because the inode attributes will be modified with that of + * its lower inode. + * The derived state will be updated on the last + * stage of each system call by fix_derived_permission(inode). + */ + + inherit_derived_state(parent->d_inode, dentry->d_inode); + + //printk(KERN_INFO "sdcardfs: derived: %s, %s, %d\n", parent->d_name.name, + // dentry->d_name.name, parent_info->perm); + + if (sbi->options.derive == DERIVE_NONE) { + return; + } + + /* Derive custom permissions based on parent and current node */ + switch (parent_info->perm) { + case PERM_INHERIT: + /* Already inherited above */ + break; + case PERM_LEGACY_PRE_ROOT: + /* Legacy internal layout places users at top level */ + info->perm = PERM_ROOT; + info->userid = simple_strtoul(dentry->d_name.name, NULL, 10); + break; + case PERM_ROOT: + /* Assume masked off by default. */ + info->d_mode = 00770; + if (!strcasecmp(dentry->d_name.name, "Android")) { + /* App-specific directories inside; let anyone traverse */ + info->perm = PERM_ANDROID; + info->d_mode = 00771; + } else if (sbi->options.split_perms) { + if (!strcasecmp(dentry->d_name.name, "DCIM") + || !strcasecmp(dentry->d_name.name, "Pictures")) { + info->d_gid = AID_SDCARD_PICS; + } else if (!strcasecmp(dentry->d_name.name, "Alarms") + || !strcasecmp(dentry->d_name.name, "Movies") + || !strcasecmp(dentry->d_name.name, "Music") + || !strcasecmp(dentry->d_name.name, "Notifications") + || !strcasecmp(dentry->d_name.name, "Podcasts") + || !strcasecmp(dentry->d_name.name, "Ringtones")) { + info->d_gid = AID_SDCARD_AV; + } + } + break; + case PERM_ANDROID: + if (!strcasecmp(dentry->d_name.name, "data")) { + /* App-specific directories inside; let anyone traverse */ + info->perm = PERM_ANDROID_DATA; + info->d_mode = 00771; + } else if (!strcasecmp(dentry->d_name.name, "obb")) { + /* App-specific directories inside; let anyone traverse */ + info->perm = PERM_ANDROID_OBB; + info->d_mode = 00771; + // FIXME : this feature will be implemented later. + /* Single OBB directory is always shared */ + } else if (!strcasecmp(dentry->d_name.name, "user")) { + /* User directories must only be accessible to system, protected + * by sdcard_all. Zygote will bind mount the appropriate user- + * specific path. */ + info->perm = PERM_ANDROID_USER; + info->d_gid = AID_SDCARD_ALL; + info->d_mode = 00770; + } + break; + /* same policy will be applied on PERM_ANDROID_DATA + * and PERM_ANDROID_OBB */ + case PERM_ANDROID_DATA: + case PERM_ANDROID_OBB: + appid = get_appid(sbi->pkgl_id, dentry->d_name.name); + if (appid != 0) { + info->d_uid = multiuser_get_uid(parent_info->userid, appid); + } + info->d_mode = 00770; + break; + case PERM_ANDROID_USER: + /* Root of a secondary user */ + info->perm = PERM_ROOT; + info->userid = simple_strtoul(dentry->d_name.name, NULL, 10); + info->d_gid = AID_SDCARD_R; + info->d_mode = 00771; + break; + } +} + +/* main function for updating derived permission */ +inline void update_derived_permission(struct dentry *dentry) +{ + struct dentry *parent; + + if(!dentry || !dentry->d_inode) { + printk(KERN_ERR "sdcardfs: %s: invalid dentry\n", __func__); + return; + } + /* FIXME: + * 1. need to check whether the dentry is updated or not + * 2. remove the root dentry update + */ + if(IS_ROOT(dentry)) { + //setup_default_pre_root_state(dentry->d_inode); + } else { + parent = dget_parent(dentry); + if(parent) { + get_derived_permission(parent, dentry); + dput(parent); + } + } + fix_derived_permission(dentry->d_inode); +} + +int need_graft_path(struct dentry *dentry) +{ + int ret = 0; + struct dentry *parent = dget_parent(dentry); + struct sdcardfs_inode_info *parent_info= SDCARDFS_I(parent->d_inode); + struct sdcardfs_sb_info *sbi = SDCARDFS_SB(dentry->d_sb); + + if(parent_info->perm == PERM_ANDROID && + !strcasecmp(dentry->d_name.name, "obb")) { + + /* /Android/obb is the base obbpath of DERIVED_UNIFIED */ + if(!(sbi->options.derive == DERIVE_UNIFIED + && parent_info->userid == 0)) { + ret = 1; + } + } + dput(parent); + return ret; +} + +int is_obbpath_invalid(struct dentry *dent) +{ + int ret = 0; + struct sdcardfs_dentry_info *di = SDCARDFS_D(dent); + struct sdcardfs_sb_info *sbi = SDCARDFS_SB(dent->d_sb); + char *path_buf, *obbpath_s; + + /* check the base obbpath has been changed. + * this routine can check an uninitialized obb dentry as well. + * regarding the uninitialized obb, refer to the sdcardfs_mkdir() */ + spin_lock(&di->lock); + if(di->orig_path.dentry) { + if(!di->lower_path.dentry) { + ret = 1; + } else { + path_get(&di->lower_path); + //lower_parent = lock_parent(lower_path->dentry); + + path_buf = kmalloc(PATH_MAX, GFP_ATOMIC); + if(!path_buf) { + ret = 1; + printk(KERN_ERR "sdcardfs: " + "fail to allocate path_buf in %s.\n", __func__); + } else { + obbpath_s = d_path(&di->lower_path, path_buf, PATH_MAX); + if (d_unhashed(di->lower_path.dentry) || + strcasecmp(sbi->obbpath_s, obbpath_s)) { + ret = 1; + } + kfree(path_buf); + } + + //unlock_dir(lower_parent); + path_put(&di->lower_path); + } + } + spin_unlock(&di->lock); + return ret; +} + +int is_base_obbpath(struct dentry *dentry) +{ + int ret = 0; + struct dentry *parent = dget_parent(dentry); + struct sdcardfs_inode_info *parent_info= SDCARDFS_I(parent->d_inode); + struct sdcardfs_sb_info *sbi = SDCARDFS_SB(dentry->d_sb); + + spin_lock(&SDCARDFS_D(dentry)->lock); + /* DERIVED_LEGACY */ + if(parent_info->perm == PERM_LEGACY_PRE_ROOT && + !strcasecmp(dentry->d_name.name, "obb")) { + ret = 1; + } + /* DERIVED_UNIFIED :/Android/obb is the base obbpath */ + else if (parent_info->perm == PERM_ANDROID && + !strcasecmp(dentry->d_name.name, "obb")) { + if((sbi->options.derive == DERIVE_UNIFIED + && parent_info->userid == 0)) { + ret = 1; + } + } + spin_unlock(&SDCARDFS_D(dentry)->lock); + dput(parent); + return ret; +} + +/* The lower_path will be stored to the dentry's orig_path + * and the base obbpath will be copyed to the lower_path variable. + * if an error returned, there's no change in the lower_path + * returns: -ERRNO if error (0: no error) */ +int setup_obb_dentry(struct dentry *dentry, struct path *lower_path) +{ + int err = 0; + struct sdcardfs_sb_info *sbi = SDCARDFS_SB(dentry->d_sb); + struct path obbpath; + + /* A local obb dentry must have its own orig_path to support rmdir + * and mkdir of itself. Usually, we expect that the sbi->obbpath + * is avaiable on this stage. */ + sdcardfs_set_orig_path(dentry, lower_path); + + err = kern_path(sbi->obbpath_s, + LOOKUP_FOLLOW | LOOKUP_DIRECTORY, &obbpath); + + if(!err) { + /* the obbpath base has been found */ + printk(KERN_INFO "sdcardfs: " + "the sbi->obbpath is found\n"); + pathcpy(lower_path, &obbpath); + } else { + /* if the sbi->obbpath is not available, we can optionally + * setup the lower_path with its orig_path. + * but, the current implementation just returns an error + * because the sdcard daemon also regards this case as + * a lookup fail. */ + printk(KERN_INFO "sdcardfs: " + "the sbi->obbpath is not available\n"); + } + return err; +} + + diff --git a/fs/sdcardfs/file.c b/fs/sdcardfs/file.c new file mode 100644 index 000000000000..bcacb947c874 --- /dev/null +++ b/fs/sdcardfs/file.c @@ -0,0 +1,357 @@ +/* + * fs/sdcardfs/file.c + * + * Copyright (c) 2013 Samsung Electronics Co. Ltd + * Authors: Daeho Jeong, Woojoong Lee, Seunghwan Hyun, + * Sunghwan Yun, Sungjong Seo + * + * This program has been developed as a stackable file system based on + * the WrapFS which written by + * + * Copyright (c) 1998-2011 Erez Zadok + * Copyright (c) 2009 Shrikar Archak + * Copyright (c) 2003-2011 Stony Brook University + * Copyright (c) 2003-2011 The Research Foundation of SUNY + * + * This file is dual licensed. It may be redistributed and/or modified + * under the terms of the Apache 2.0 License OR version 2 of the GNU + * General Public License. + */ + +#include "sdcardfs.h" +#ifdef CONFIG_SDCARD_FS_FADV_NOACTIVE +#include +#endif + +static ssize_t sdcardfs_read(struct file *file, char __user *buf, + size_t count, loff_t *ppos) +{ + int err; + struct file *lower_file; + struct dentry *dentry = file->f_path.dentry; +#ifdef CONFIG_SDCARD_FS_FADV_NOACTIVE + struct backing_dev_info *bdi; +#endif + + lower_file = sdcardfs_lower_file(file); + +#ifdef CONFIG_SDCARD_FS_FADV_NOACTIVE + if (file->f_mode & FMODE_NOACTIVE) { + if (!(lower_file->f_mode & FMODE_NOACTIVE)) { + bdi = lower_file->f_mapping->backing_dev_info; + lower_file->f_ra.ra_pages = bdi->ra_pages * 2; + spin_lock(&lower_file->f_lock); + lower_file->f_mode |= FMODE_NOACTIVE; + spin_unlock(&lower_file->f_lock); + } + } +#endif + + err = vfs_read(lower_file, buf, count, ppos); + /* update our inode atime upon a successful lower read */ + if (err >= 0) + fsstack_copy_attr_atime(dentry->d_inode, + lower_file->f_path.dentry->d_inode); + + return err; +} + +static ssize_t sdcardfs_write(struct file *file, const char __user *buf, + size_t count, loff_t *ppos) +{ + int err = 0; + struct file *lower_file; + struct dentry *dentry = file->f_path.dentry; + + /* check disk space */ + if (!check_min_free_space(dentry, count, 0)) { + printk(KERN_INFO "No minimum free space.\n"); + return -ENOSPC; + } + + lower_file = sdcardfs_lower_file(file); + err = vfs_write(lower_file, buf, count, ppos); + /* update our inode times+sizes upon a successful lower write */ + if (err >= 0) { + fsstack_copy_inode_size(dentry->d_inode, + lower_file->f_path.dentry->d_inode); + fsstack_copy_attr_times(dentry->d_inode, + lower_file->f_path.dentry->d_inode); + } + + return err; +} + +static int sdcardfs_readdir(struct file *file, void *dirent, filldir_t filldir) +{ + int err = 0; + struct file *lower_file = NULL; + struct dentry *dentry = file->f_path.dentry; + + lower_file = sdcardfs_lower_file(file); + + lower_file->f_pos = file->f_pos; + err = vfs_readdir(lower_file, filldir, dirent); + file->f_pos = lower_file->f_pos; + if (err >= 0) /* copy the atime */ + fsstack_copy_attr_atime(dentry->d_inode, + lower_file->f_path.dentry->d_inode); + return err; +} + +static long sdcardfs_unlocked_ioctl(struct file *file, unsigned int cmd, + unsigned long arg) +{ + long err = -ENOTTY; + struct file *lower_file; + + lower_file = sdcardfs_lower_file(file); + + /* XXX: use vfs_ioctl if/when VFS exports it */ + if (!lower_file || !lower_file->f_op) + goto out; + if (lower_file->f_op->unlocked_ioctl) + err = lower_file->f_op->unlocked_ioctl(lower_file, cmd, arg); + +out: + return err; +} + +#ifdef CONFIG_COMPAT +static long sdcardfs_compat_ioctl(struct file *file, unsigned int cmd, + unsigned long arg) +{ + long err = -ENOTTY; + struct file *lower_file; + + lower_file = sdcardfs_lower_file(file); + + /* XXX: use vfs_ioctl if/when VFS exports it */ + if (!lower_file || !lower_file->f_op) + goto out; + if (lower_file->f_op->compat_ioctl) + err = lower_file->f_op->compat_ioctl(lower_file, cmd, arg); + +out: + return err; +} +#endif + +static int sdcardfs_mmap(struct file *file, struct vm_area_struct *vma) +{ + int err = 0; + bool willwrite; + struct file *lower_file; + const struct vm_operations_struct *saved_vm_ops = NULL; + + /* this might be deferred to mmap's writepage */ + willwrite = ((vma->vm_flags | VM_SHARED | VM_WRITE) == vma->vm_flags); + + /* + * File systems which do not implement ->writepage may use + * generic_file_readonly_mmap as their ->mmap op. If you call + * generic_file_readonly_mmap with VM_WRITE, you'd get an -EINVAL. + * But we cannot call the lower ->mmap op, so we can't tell that + * writeable mappings won't work. Therefore, our only choice is to + * check if the lower file system supports the ->writepage, and if + * not, return EINVAL (the same error that + * generic_file_readonly_mmap returns in that case). + */ + lower_file = sdcardfs_lower_file(file); + if (willwrite && !lower_file->f_mapping->a_ops->writepage) { + err = -EINVAL; + printk(KERN_ERR "sdcardfs: lower file system does not " + "support writeable mmap\n"); + goto out; + } + + /* + * find and save lower vm_ops. + * + * XXX: the VFS should have a cleaner way of finding the lower vm_ops + */ + if (!SDCARDFS_F(file)->lower_vm_ops) { + err = lower_file->f_op->mmap(lower_file, vma); + if (err) { + printk(KERN_ERR "sdcardfs: lower mmap failed %d\n", err); + goto out; + } + saved_vm_ops = vma->vm_ops; /* save: came from lower ->mmap */ + err = do_munmap(current->mm, vma->vm_start, + vma->vm_end - vma->vm_start); + if (err) { + printk(KERN_ERR "sdcardfs: do_munmap failed %d\n", err); + goto out; + } + } + + /* + * Next 3 lines are all I need from generic_file_mmap. I definitely + * don't want its test for ->readpage which returns -ENOEXEC. + */ + file_accessed(file); + vma->vm_ops = &sdcardfs_vm_ops; + vma->vm_flags |= VM_CAN_NONLINEAR; + + file->f_mapping->a_ops = &sdcardfs_aops; /* set our aops */ + if (!SDCARDFS_F(file)->lower_vm_ops) /* save for our ->fault */ + SDCARDFS_F(file)->lower_vm_ops = saved_vm_ops; + +out: + return err; +} + +static int sdcardfs_open(struct inode *inode, struct file *file) +{ + int err = 0; + struct file *lower_file = NULL; + struct path lower_path; + struct dentry *dentry = file->f_path.dentry; + struct dentry *parent = dget_parent(dentry); + struct sdcardfs_sb_info *sbi = SDCARDFS_SB(dentry->d_sb); + const struct cred *saved_cred = NULL; + int has_rw; + + /* don't open unhashed/deleted files */ + if (d_unhashed(dentry)) { + err = -ENOENT; + goto out_err; + } + + has_rw = get_caller_has_rw_locked(sbi->pkgl_id, sbi->options.derive); + + if(!check_caller_access_to_name(parent->d_inode, dentry->d_name.name, + sbi->options.derive, + open_flags_to_access_mode(file->f_flags), has_rw)) { + printk(KERN_INFO "%s: need to check the caller's gid in packages.list\n" + " dentry: %s, task:%s\n", + __func__, dentry->d_name.name, current->comm); + err = -EACCES; + goto out_err; + } + + /* save current_cred and override it */ + OVERRIDE_CRED(sbi, saved_cred); + + file->private_data = + kzalloc(sizeof(struct sdcardfs_file_info), GFP_KERNEL); + if (!SDCARDFS_F(file)) { + err = -ENOMEM; + goto out_revert_cred; + } + + /* open lower object and link sdcardfs's file struct to lower's */ + sdcardfs_get_lower_path(file->f_path.dentry, &lower_path); + lower_file = dentry_open(lower_path.dentry, lower_path.mnt, + file->f_flags, current_cred()); + if (IS_ERR(lower_file)) { + err = PTR_ERR(lower_file); + lower_file = sdcardfs_lower_file(file); + if (lower_file) { + sdcardfs_set_lower_file(file, NULL); + fput(lower_file); /* fput calls dput for lower_dentry */ + } + } else { + sdcardfs_set_lower_file(file, lower_file); + } + + if (err) + kfree(SDCARDFS_F(file)); + else { + fsstack_copy_attr_all(inode, sdcardfs_lower_inode(inode)); + fix_derived_permission(inode); + } + +out_revert_cred: + REVERT_CRED(saved_cred); +out_err: + dput(parent); + return err; +} + +static int sdcardfs_flush(struct file *file, fl_owner_t id) +{ + int err = 0; + struct file *lower_file = NULL; + + lower_file = sdcardfs_lower_file(file); + if (lower_file && lower_file->f_op && lower_file->f_op->flush) + err = lower_file->f_op->flush(lower_file, id); + + return err; +} + +/* release all lower object references & free the file info structure */ +static int sdcardfs_file_release(struct inode *inode, struct file *file) +{ + struct file *lower_file; + + lower_file = sdcardfs_lower_file(file); + if (lower_file) { + sdcardfs_set_lower_file(file, NULL); + fput(lower_file); + } + + kfree(SDCARDFS_F(file)); + return 0; +} + +static int +sdcardfs_fsync(struct file *file, int datasync) +{ + int err; + struct file *lower_file; + struct path lower_path; + struct dentry *dentry = file->f_path.dentry; + + lower_file = sdcardfs_lower_file(file); + sdcardfs_get_lower_path(dentry, &lower_path); + err = vfs_fsync(lower_file, datasync); + sdcardfs_put_lower_path(dentry, &lower_path); + + return err; +} + +static int sdcardfs_fasync(int fd, struct file *file, int flag) +{ + int err = 0; + struct file *lower_file = NULL; + + lower_file = sdcardfs_lower_file(file); + if (lower_file->f_op && lower_file->f_op->fasync) + err = lower_file->f_op->fasync(fd, lower_file, flag); + + return err; +} + +const struct file_operations sdcardfs_main_fops = { + .llseek = generic_file_llseek, + .read = sdcardfs_read, + .write = sdcardfs_write, + .unlocked_ioctl = sdcardfs_unlocked_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl = sdcardfs_compat_ioctl, +#endif + .mmap = sdcardfs_mmap, + .open = sdcardfs_open, + .flush = sdcardfs_flush, + .release = sdcardfs_file_release, + .fsync = sdcardfs_fsync, + .fasync = sdcardfs_fasync, +}; + +/* trimmed directory options */ +const struct file_operations sdcardfs_dir_fops = { + .llseek = generic_file_llseek, + .read = generic_read_dir, + .readdir = sdcardfs_readdir, + .unlocked_ioctl = sdcardfs_unlocked_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl = sdcardfs_compat_ioctl, +#endif + .open = sdcardfs_open, + .release = sdcardfs_file_release, + .flush = sdcardfs_flush, + .fsync = sdcardfs_fsync, + .fasync = sdcardfs_fasync, +}; diff --git a/fs/sdcardfs/hashtable.h b/fs/sdcardfs/hashtable.h new file mode 100644 index 000000000000..1e770f3df148 --- /dev/null +++ b/fs/sdcardfs/hashtable.h @@ -0,0 +1,190 @@ +/* + * Statically sized hash table implementation + * (C) 2012 Sasha Levin + */ + +#ifndef _LINUX_HASHTABLE_H +#define _LINUX_HASHTABLE_H + +#include +#include +#include +#include +#include + +#define DEFINE_HASHTABLE(name, bits) \ + struct hlist_head name[1 << (bits)] = \ + { [0 ... ((1 << (bits)) - 1)] = HLIST_HEAD_INIT } + +#define DECLARE_HASHTABLE(name, bits) \ + struct hlist_head name[1 << (bits)] + +#define HASH_SIZE(name) (ARRAY_SIZE(name)) +#define HASH_BITS(name) ilog2(HASH_SIZE(name)) + +/* Use hash_32 when possible to allow for fast 32bit hashing in 64bit kernels. */ +#define hash_min(val, bits) \ + (sizeof(val) <= 4 ? hash_32(val, bits) : hash_long(val, bits)) + +static inline void __hash_init(struct hlist_head *ht, unsigned int sz) +{ + unsigned int i; + + for (i = 0; i < sz; i++) + INIT_HLIST_HEAD(&ht[i]); +} + +/** + * hash_init - initialize a hash table + * @hashtable: hashtable to be initialized + * + * Calculates the size of the hashtable from the given parameter, otherwise + * same as hash_init_size. + * + * This has to be a macro since HASH_BITS() will not work on pointers since + * it calculates the size during preprocessing. + */ +#define hash_init(hashtable) __hash_init(hashtable, HASH_SIZE(hashtable)) + +/** + * hash_add - add an object to a hashtable + * @hashtable: hashtable to add to + * @node: the &struct hlist_node of the object to be added + * @key: the key of the object to be added + */ +#define hash_add(hashtable, node, key) \ + hlist_add_head(node, &hashtable[hash_min(key, HASH_BITS(hashtable))]) + +/** + * hash_add_rcu - add an object to a rcu enabled hashtable + * @hashtable: hashtable to add to + * @node: the &struct hlist_node of the object to be added + * @key: the key of the object to be added + */ +#define hash_add_rcu(hashtable, node, key) \ + hlist_add_head_rcu(node, &hashtable[hash_min(key, HASH_BITS(hashtable))]) + +/** + * hash_hashed - check whether an object is in any hashtable + * @node: the &struct hlist_node of the object to be checked + */ +static inline bool hash_hashed(struct hlist_node *node) +{ + return !hlist_unhashed(node); +} + +static inline bool __hash_empty(struct hlist_head *ht, unsigned int sz) +{ + unsigned int i; + + for (i = 0; i < sz; i++) + if (!hlist_empty(&ht[i])) + return false; + + return true; +} + +/** + * hash_empty - check whether a hashtable is empty + * @hashtable: hashtable to check + * + * This has to be a macro since HASH_BITS() will not work on pointers since + * it calculates the size during preprocessing. + */ +#define hash_empty(hashtable) __hash_empty(hashtable, HASH_SIZE(hashtable)) + +/** + * hash_del - remove an object from a hashtable + * @node: &struct hlist_node of the object to remove + */ +static inline void hash_del(struct hlist_node *node) +{ + hlist_del_init(node); +} + +/** + * hash_del_rcu - remove an object from a rcu enabled hashtable + * @node: &struct hlist_node of the object to remove + */ +static inline void hash_del_rcu(struct hlist_node *node) +{ + hlist_del_init_rcu(node); +} + +/** + * hash_for_each - iterate over a hashtable + * @name: hashtable to iterate + * @bkt: integer to use as bucket loop cursor + * @obj: the type * to use as a loop cursor for each entry + * @member: the name of the hlist_node within the struct + */ +#define hash_for_each(name, bkt, obj, member, pos) \ + for ((bkt) = 0, obj = NULL; obj == NULL && (bkt) < HASH_SIZE(name);\ + (bkt)++)\ + hlist_for_each_entry(obj, pos, &name[bkt], member) + +/** + * hash_for_each_rcu - iterate over a rcu enabled hashtable + * @name: hashtable to iterate + * @bkt: integer to use as bucket loop cursor + * @obj: the type * to use as a loop cursor for each entry + * @member: the name of the hlist_node within the struct + */ +#define hash_for_each_rcu(name, bkt, obj, member) \ + for ((bkt) = 0, obj = NULL; obj == NULL && (bkt) < HASH_SIZE(name);\ + (bkt)++)\ + hlist_for_each_entry_rcu(obj, &name[bkt], member) + +/** + * hash_for_each_safe - iterate over a hashtable safe against removal of + * hash entry + * @name: hashtable to iterate + * @bkt: integer to use as bucket loop cursor + * @tmp: a &struct used for temporary storage + * @obj: the type * to use as a loop cursor for each entry + * @member: the name of the hlist_node within the struct + */ +#define hash_for_each_safe(name, bkt, tmp, obj, member, pos) \ + for ((bkt) = 0, obj = NULL; (bkt) < HASH_SIZE(name);\ + (bkt)++)\ + hlist_for_each_entry_safe(obj, pos, tmp, &name[bkt], member) + +/** + * hash_for_each_possible - iterate over all possible objects hashing to the + * same bucket + * @name: hashtable to iterate + * @obj: the type * to use as a loop cursor for each entry + * @member: the name of the hlist_node within the struct + * @key: the key of the objects to iterate over + */ +#define hash_for_each_possible(name, obj, member, key, pos) \ + hlist_for_each_entry(obj, pos, &name[hash_min(key, HASH_BITS(name))], member) + +/** + * hash_for_each_possible_rcu - iterate over all possible objects hashing to the + * same bucket in an rcu enabled hashtable + * in a rcu enabled hashtable + * @name: hashtable to iterate + * @obj: the type * to use as a loop cursor for each entry + * @member: the name of the hlist_node within the struct + * @key: the key of the objects to iterate over + */ +#define hash_for_each_possible_rcu(name, obj, member, key) \ + hlist_for_each_entry_rcu(obj, &name[hash_min(key, HASH_BITS(name))],\ + member) + +/** + * hash_for_each_possible_safe - iterate over all possible objects hashing to the + * same bucket safe against removals + * @name: hashtable to iterate + * @obj: the type * to use as a loop cursor for each entry + * @tmp: a &struct used for temporary storage + * @member: the name of the hlist_node within the struct + * @key: the key of the objects to iterate over + */ +#define hash_for_each_possible_safe(name, obj, tmp, member, key) \ + hlist_for_each_entry_safe(obj, tmp,\ + &name[hash_min(key, HASH_BITS(name))], member) + + +#endif diff --git a/fs/sdcardfs/inode.c b/fs/sdcardfs/inode.c new file mode 100644 index 000000000000..e8ed04250ed1 --- /dev/null +++ b/fs/sdcardfs/inode.c @@ -0,0 +1,886 @@ +/* + * fs/sdcardfs/inode.c + * + * Copyright (c) 2013 Samsung Electronics Co. Ltd + * Authors: Daeho Jeong, Woojoong Lee, Seunghwan Hyun, + * Sunghwan Yun, Sungjong Seo + * + * This program has been developed as a stackable file system based on + * the WrapFS which written by + * + * Copyright (c) 1998-2011 Erez Zadok + * Copyright (c) 2009 Shrikar Archak + * Copyright (c) 2003-2011 Stony Brook University + * Copyright (c) 2003-2011 The Research Foundation of SUNY + * + * This file is dual licensed. It may be redistributed and/or modified + * under the terms of the Apache 2.0 License OR version 2 of the GNU + * General Public License. + */ + +#include "sdcardfs.h" + +/* Do not directly use this function. Use OVERRIDE_CRED() instead. */ +const struct cred * override_fsids(struct sdcardfs_sb_info* sbi) +{ + struct cred * cred; + const struct cred * old_cred; + + cred = prepare_creds(); + if (!cred) + return NULL; + + cred->fsuid = sbi->options.fs_low_uid; + cred->fsgid = sbi->options.fs_low_gid; + + old_cred = override_creds(cred); + + return old_cred; +} + +/* Do not directly use this function, use REVERT_CRED() instead. */ +void revert_fsids(const struct cred * old_cred) +{ + const struct cred * cur_cred; + + cur_cred = current->cred; + revert_creds(old_cred); + put_cred(cur_cred); +} + +static int sdcardfs_create(struct inode *dir, struct dentry *dentry, + int mode, struct nameidata *nd) +{ + int err = 0; + struct dentry *lower_dentry; + struct dentry *lower_parent_dentry = NULL; + struct path lower_path, saved_path; + struct sdcardfs_sb_info *sbi = SDCARDFS_SB(dentry->d_sb); + const struct cred *saved_cred = NULL; + + int has_rw = get_caller_has_rw_locked(sbi->pkgl_id, sbi->options.derive); + if(!check_caller_access_to_name(dir, dentry->d_name.name, sbi->options.derive, 1, has_rw)) { + printk(KERN_INFO "%s: need to check the caller's gid in packages.list\n" + " dentry: %s, task:%s\n", + __func__, dentry->d_name.name, current->comm); + err = -EACCES; + goto out_eacces; + } + + /* save current_cred and override it */ + OVERRIDE_CRED(SDCARDFS_SB(dir->i_sb), saved_cred); + + sdcardfs_get_lower_path(dentry, &lower_path); + lower_dentry = lower_path.dentry; + lower_parent_dentry = lock_parent(lower_dentry); + + err = mnt_want_write(lower_path.mnt); + if (err) + goto out_unlock; + + pathcpy(&saved_path, &nd->path); + pathcpy(&nd->path, &lower_path); + + /* set last 16bytes of mode field to 0664 */ + mode = (mode & S_IFMT) | 00664; + err = vfs_create(lower_parent_dentry->d_inode, lower_dentry, mode, nd); + + pathcpy(&nd->path, &saved_path); + if (err) + goto out; + + err = sdcardfs_interpose(dentry, dir->i_sb, &lower_path); + if (err) + goto out; + fsstack_copy_attr_times(dir, sdcardfs_lower_inode(dir)); + fsstack_copy_inode_size(dir, lower_parent_dentry->d_inode); + +out: + mnt_drop_write(lower_path.mnt); +out_unlock: + unlock_dir(lower_parent_dentry); + sdcardfs_put_lower_path(dentry, &lower_path); + REVERT_CRED(saved_cred); +out_eacces: + return err; +} + +#if 0 +static int sdcardfs_link(struct dentry *old_dentry, struct inode *dir, + struct dentry *new_dentry) +{ + struct dentry *lower_old_dentry; + struct dentry *lower_new_dentry; + struct dentry *lower_dir_dentry; + u64 file_size_save; + int err; + struct path lower_old_path, lower_new_path; + + OVERRIDE_CRED(SDCARDFS_SB(dir->i_sb)); + + file_size_save = i_size_read(old_dentry->d_inode); + sdcardfs_get_lower_path(old_dentry, &lower_old_path); + sdcardfs_get_lower_path(new_dentry, &lower_new_path); + lower_old_dentry = lower_old_path.dentry; + lower_new_dentry = lower_new_path.dentry; + lower_dir_dentry = lock_parent(lower_new_dentry); + + err = mnt_want_write(lower_new_path.mnt); + if (err) + goto out_unlock; + + err = vfs_link(lower_old_dentry, lower_dir_dentry->d_inode, + lower_new_dentry); + if (err || !lower_new_dentry->d_inode) + goto out; + + err = sdcardfs_interpose(new_dentry, dir->i_sb, &lower_new_path); + if (err) + goto out; + fsstack_copy_attr_times(dir, lower_new_dentry->d_inode); + fsstack_copy_inode_size(dir, lower_new_dentry->d_inode); + old_dentry->d_inode->i_nlink = + sdcardfs_lower_inode(old_dentry->d_inode)->i_nlink; + i_size_write(new_dentry->d_inode, file_size_save); +out: + mnt_drop_write(lower_new_path.mnt); +out_unlock: + unlock_dir(lower_dir_dentry); + sdcardfs_put_lower_path(old_dentry, &lower_old_path); + sdcardfs_put_lower_path(new_dentry, &lower_new_path); + REVERT_CRED(); + return err; +} +#endif + +static int sdcardfs_unlink(struct inode *dir, struct dentry *dentry) +{ + int err; + struct dentry *lower_dentry; + struct inode *lower_dir_inode = sdcardfs_lower_inode(dir); + struct dentry *lower_dir_dentry; + struct path lower_path; + struct sdcardfs_sb_info *sbi = SDCARDFS_SB(dentry->d_sb); + const struct cred *saved_cred = NULL; + + int has_rw = get_caller_has_rw_locked(sbi->pkgl_id, sbi->options.derive); + if(!check_caller_access_to_name(dir, dentry->d_name.name, sbi->options.derive, 1, has_rw)) { + printk(KERN_INFO "%s: need to check the caller's gid in packages.list\n" + " dentry: %s, task:%s\n", + __func__, dentry->d_name.name, current->comm); + err = -EACCES; + goto out_eacces; + } + + /* save current_cred and override it */ + OVERRIDE_CRED(SDCARDFS_SB(dir->i_sb), saved_cred); + + sdcardfs_get_lower_path(dentry, &lower_path); + lower_dentry = lower_path.dentry; + dget(lower_dentry); + lower_dir_dentry = lock_parent(lower_dentry); + + err = mnt_want_write(lower_path.mnt); + if (err) + goto out_unlock; + err = vfs_unlink(lower_dir_inode, lower_dentry); + + /* + * Note: unlinking on top of NFS can cause silly-renamed files. + * Trying to delete such files results in EBUSY from NFS + * below. Silly-renamed files will get deleted by NFS later on, so + * we just need to detect them here and treat such EBUSY errors as + * if the upper file was successfully deleted. + */ + if (err == -EBUSY && lower_dentry->d_flags & DCACHE_NFSFS_RENAMED) + err = 0; + if (err) + goto out; + fsstack_copy_attr_times(dir, lower_dir_inode); + fsstack_copy_inode_size(dir, lower_dir_inode); + dentry->d_inode->i_nlink = + sdcardfs_lower_inode(dentry->d_inode)->i_nlink; + dentry->d_inode->i_ctime = dir->i_ctime; + d_drop(dentry); /* this is needed, else LTP fails (VFS won't do it) */ +out: + mnt_drop_write(lower_path.mnt); +out_unlock: + unlock_dir(lower_dir_dentry); + dput(lower_dentry); + sdcardfs_put_lower_path(dentry, &lower_path); + REVERT_CRED(saved_cred); +out_eacces: + return err; +} + +#if 0 +static int sdcardfs_symlink(struct inode *dir, struct dentry *dentry, + const char *symname) +{ + int err = 0; + struct dentry *lower_dentry; + struct dentry *lower_parent_dentry = NULL; + struct path lower_path; + + OVERRIDE_CRED(SDCARDFS_SB(dir->i_sb)); + + sdcardfs_get_lower_path(dentry, &lower_path); + lower_dentry = lower_path.dentry; + lower_parent_dentry = lock_parent(lower_dentry); + + err = mnt_want_write(lower_path.mnt); + if (err) + goto out_unlock; + err = vfs_symlink(lower_parent_dentry->d_inode, lower_dentry, symname); + if (err) + goto out; + err = sdcardfs_interpose(dentry, dir->i_sb, &lower_path); + if (err) + goto out; + fsstack_copy_attr_times(dir, sdcardfs_lower_inode(dir)); + fsstack_copy_inode_size(dir, lower_parent_dentry->d_inode); + +out: + mnt_drop_write(lower_path.mnt); +out_unlock: + unlock_dir(lower_parent_dentry); + sdcardfs_put_lower_path(dentry, &lower_path); + REVERT_CRED(); + return err; +} +#endif + +static int touch(char *abs_path, mode_t mode) { + struct file *filp = filp_open(abs_path, O_RDWR|O_CREAT|O_EXCL|O_NOFOLLOW, mode); + if (IS_ERR(filp)) { + if (PTR_ERR(filp) == -EEXIST) { + return 0; + } + else { + printk(KERN_ERR "sdcardfs: failed to open(%s): %ld\n", + abs_path, PTR_ERR(filp)); + return PTR_ERR(filp); + } + } + filp_close(filp, current->files); + return 0; +} + +static int sdcardfs_mkdir(struct inode *dir, struct dentry *dentry, int mode) +{ + int err = 0; + int make_nomedia_in_obb = 0; + struct dentry *lower_dentry; + struct dentry *lower_parent_dentry = NULL; + struct path lower_path; + struct sdcardfs_sb_info *sbi = SDCARDFS_SB(dentry->d_sb); + const struct cred *saved_cred = NULL; + struct sdcardfs_inode_info *pi = SDCARDFS_I(dir); + char *page_buf; + char *nomedia_dir_name; + char *nomedia_fullpath; + int fullpath_namelen; + int touch_err = 0; + + int has_rw = get_caller_has_rw_locked(sbi->pkgl_id, sbi->options.derive); + if(!check_caller_access_to_name(dir, dentry->d_name.name, sbi->options.derive, 1, has_rw)) { + printk(KERN_INFO "%s: need to check the caller's gid in packages.list\n" + " dentry: %s, task:%s\n", + __func__, dentry->d_name.name, current->comm); + err = -EACCES; + goto out_eacces; + } + + /* save current_cred and override it */ + OVERRIDE_CRED(SDCARDFS_SB(dir->i_sb), saved_cred); + + /* check disk space */ + if (!check_min_free_space(dentry, 0, 1)) { + printk(KERN_INFO "sdcardfs: No minimum free space.\n"); + err = -ENOSPC; + goto out_revert; + } + + /* the lower_dentry is negative here */ + sdcardfs_get_lower_path(dentry, &lower_path); + lower_dentry = lower_path.dentry; + lower_parent_dentry = lock_parent(lower_dentry); + + err = mnt_want_write(lower_path.mnt); + if (err) + goto out_unlock; + + /* set last 16bytes of mode field to 0775 */ + mode = (mode & S_IFMT) | 00775; + err = vfs_mkdir(lower_parent_dentry->d_inode, lower_dentry, mode); + + if (err) + goto out; + + /* if it is a local obb dentry, setup it with the base obbpath */ + if(need_graft_path(dentry)) { + + err = setup_obb_dentry(dentry, &lower_path); + if(err) { + /* if the sbi->obbpath is not available, the lower_path won't be + * changed by setup_obb_dentry() but the lower path is saved to + * its orig_path. this dentry will be revalidated later. + * but now, the lower_path should be NULL */ + sdcardfs_put_reset_lower_path(dentry); + + /* the newly created lower path which saved to its orig_path or + * the lower_path is the base obbpath. + * therefore, an additional path_get is required */ + path_get(&lower_path); + } else + make_nomedia_in_obb = 1; + } + + err = sdcardfs_interpose(dentry, dir->i_sb, &lower_path); + if (err) + goto out; + + fsstack_copy_attr_times(dir, sdcardfs_lower_inode(dir)); + fsstack_copy_inode_size(dir, lower_parent_dentry->d_inode); + /* update number of links on parent directory */ + dir->i_nlink = sdcardfs_lower_inode(dir)->i_nlink; + + if ((sbi->options.derive == DERIVE_UNIFIED) && (!strcasecmp(dentry->d_name.name, "obb")) + && (pi->perm == PERM_ANDROID) && (pi->userid == 0)) + make_nomedia_in_obb = 1; + + /* When creating /Android/data and /Android/obb, mark them as .nomedia */ + if (make_nomedia_in_obb || + ((pi->perm == PERM_ANDROID) && (!strcasecmp(dentry->d_name.name, "data")))) { + + page_buf = (char *)__get_free_page(GFP_KERNEL); + if (!page_buf) { + printk(KERN_ERR "sdcardfs: failed to allocate page buf\n"); + goto out; + } + + nomedia_dir_name = d_absolute_path(&lower_path, page_buf, PAGE_SIZE); + if (IS_ERR(nomedia_dir_name)) { + free_page((unsigned long)page_buf); + printk(KERN_ERR "sdcardfs: failed to get .nomedia dir name\n"); + goto out; + } + + fullpath_namelen = page_buf + PAGE_SIZE - nomedia_dir_name - 1; + fullpath_namelen += strlen("/.nomedia"); + nomedia_fullpath = kzalloc(fullpath_namelen + 1, GFP_KERNEL); + if (!nomedia_fullpath) { + free_page((unsigned long)page_buf); + printk(KERN_ERR "sdcardfs: failed to allocate .nomedia fullpath buf\n"); + goto out; + } + + strcpy(nomedia_fullpath, nomedia_dir_name); + free_page((unsigned long)page_buf); + strcat(nomedia_fullpath, "/.nomedia"); + touch_err = touch(nomedia_fullpath, 0664); + if (touch_err) { + printk(KERN_ERR "sdcardfs: failed to touch(%s): %d\n", + nomedia_fullpath, touch_err); + kfree(nomedia_fullpath); + goto out; + } + kfree(nomedia_fullpath); + } +out: + mnt_drop_write(lower_path.mnt); +out_unlock: + unlock_dir(lower_parent_dentry); + sdcardfs_put_lower_path(dentry, &lower_path); +out_revert: + REVERT_CRED(saved_cred); +out_eacces: + return err; +} + +static int sdcardfs_rmdir(struct inode *dir, struct dentry *dentry) +{ + struct dentry *lower_dentry; + struct dentry *lower_dir_dentry; + int err; + struct path lower_path; + struct sdcardfs_sb_info *sbi = SDCARDFS_SB(dentry->d_sb); + const struct cred *saved_cred = NULL; + //char *path_s = NULL; + + int has_rw = get_caller_has_rw_locked(sbi->pkgl_id, sbi->options.derive); + if(!check_caller_access_to_name(dir, dentry->d_name.name, sbi->options.derive, 1, has_rw)) { + printk(KERN_INFO "%s: need to check the caller's gid in packages.list\n" + " dentry: %s, task:%s\n", + __func__, dentry->d_name.name, current->comm); + err = -EACCES; + goto out_eacces; + } + + /* save current_cred and override it */ + OVERRIDE_CRED(SDCARDFS_SB(dir->i_sb), saved_cred); + + /* sdcardfs_get_real_lower(): in case of remove an user's obb dentry + * the dentry on the original path should be deleted. */ + sdcardfs_get_real_lower(dentry, &lower_path); + + lower_dentry = lower_path.dentry; + lower_dir_dentry = lock_parent(lower_dentry); + + err = mnt_want_write(lower_path.mnt); + if (err) + goto out_unlock; + err = vfs_rmdir(lower_dir_dentry->d_inode, lower_dentry); + if (err) + goto out; + + d_drop(dentry); /* drop our dentry on success (why not VFS's job?) */ + if (dentry->d_inode) + clear_nlink(dentry->d_inode); + fsstack_copy_attr_times(dir, lower_dir_dentry->d_inode); + fsstack_copy_inode_size(dir, lower_dir_dentry->d_inode); + dir->i_nlink = lower_dir_dentry->d_inode->i_nlink; + +out: + mnt_drop_write(lower_path.mnt); +out_unlock: + unlock_dir(lower_dir_dentry); + sdcardfs_put_real_lower(dentry, &lower_path); + REVERT_CRED(saved_cred); +out_eacces: + return err; +} + +#if 0 +static int sdcardfs_mknod(struct inode *dir, struct dentry *dentry, int mode, + dev_t dev) +{ + int err = 0; + struct dentry *lower_dentry; + struct dentry *lower_parent_dentry = NULL; + struct path lower_path; + + OVERRIDE_CRED(SDCARDFS_SB(dir->i_sb)); + + sdcardfs_get_lower_path(dentry, &lower_path); + lower_dentry = lower_path.dentry; + lower_parent_dentry = lock_parent(lower_dentry); + + err = mnt_want_write(lower_path.mnt); + if (err) + goto out_unlock; + err = vfs_mknod(lower_parent_dentry->d_inode, lower_dentry, mode, dev); + if (err) + goto out; + + err = sdcardfs_interpose(dentry, dir->i_sb, &lower_path); + if (err) + goto out; + fsstack_copy_attr_times(dir, sdcardfs_lower_inode(dir)); + fsstack_copy_inode_size(dir, lower_parent_dentry->d_inode); + +out: + mnt_drop_write(lower_path.mnt); +out_unlock: + unlock_dir(lower_parent_dentry); + sdcardfs_put_lower_path(dentry, &lower_path); + REVERT_CRED(); + return err; +} +#endif + +/* + * The locking rules in sdcardfs_rename are complex. We could use a simpler + * superblock-level name-space lock for renames and copy-ups. + */ +static int sdcardfs_rename(struct inode *old_dir, struct dentry *old_dentry, + struct inode *new_dir, struct dentry *new_dentry) +{ + int err = 0; + struct dentry *lower_old_dentry = NULL; + struct dentry *lower_new_dentry = NULL; + struct dentry *lower_old_dir_dentry = NULL; + struct dentry *lower_new_dir_dentry = NULL; + struct dentry *trap = NULL; + struct dentry *new_parent = NULL; + struct path lower_old_path, lower_new_path; + struct sdcardfs_sb_info *sbi = SDCARDFS_SB(old_dentry->d_sb); + const struct cred *saved_cred = NULL; + + int has_rw = get_caller_has_rw_locked(sbi->pkgl_id, sbi->options.derive); + if(!check_caller_access_to_name(old_dir, old_dentry->d_name.name, + sbi->options.derive, 1, has_rw) || + !check_caller_access_to_name(new_dir, new_dentry->d_name.name, + sbi->options.derive, 1, has_rw)) { + printk(KERN_INFO "%s: need to check the caller's gid in packages.list\n" + " new_dentry: %s, task:%s\n", + __func__, new_dentry->d_name.name, current->comm); + err = -EACCES; + goto out_eacces; + } + + /* save current_cred and override it */ + OVERRIDE_CRED(SDCARDFS_SB(old_dir->i_sb), saved_cred); + + sdcardfs_get_real_lower(old_dentry, &lower_old_path); + sdcardfs_get_lower_path(new_dentry, &lower_new_path); + lower_old_dentry = lower_old_path.dentry; + lower_new_dentry = lower_new_path.dentry; + lower_old_dir_dentry = dget_parent(lower_old_dentry); + lower_new_dir_dentry = dget_parent(lower_new_dentry); + + trap = lock_rename(lower_old_dir_dentry, lower_new_dir_dentry); + /* source should not be ancestor of target */ + if (trap == lower_old_dentry) { + err = -EINVAL; + goto out; + } + /* target should not be ancestor of source */ + if (trap == lower_new_dentry) { + err = -ENOTEMPTY; + goto out; + } + + err = mnt_want_write(lower_old_path.mnt); + if (err) + goto out; + err = mnt_want_write(lower_new_path.mnt); + if (err) + goto out_drop_old_write; + + err = vfs_rename(lower_old_dir_dentry->d_inode, lower_old_dentry, + lower_new_dir_dentry->d_inode, lower_new_dentry); + if (err) + goto out_err; + + /* Copy attrs from lower dir, but i_uid/i_gid */ + fsstack_copy_attr_all(new_dir, lower_new_dir_dentry->d_inode); + fsstack_copy_inode_size(new_dir, lower_new_dir_dentry->d_inode); + fix_derived_permission(new_dir); + if (new_dir != old_dir) { + fsstack_copy_attr_all(old_dir, lower_old_dir_dentry->d_inode); + fsstack_copy_inode_size(old_dir, lower_old_dir_dentry->d_inode); + fix_derived_permission(old_dir); + /* update the derived permission of the old_dentry + * with its new parent + */ + new_parent = dget_parent(new_dentry); + if(new_parent) { + if(old_dentry->d_inode) { + get_derived_permission(new_parent, old_dentry); + fix_derived_permission(old_dentry->d_inode); + } + dput(new_parent); + } + } + +out_err: + mnt_drop_write(lower_new_path.mnt); +out_drop_old_write: + mnt_drop_write(lower_old_path.mnt); +out: + unlock_rename(lower_old_dir_dentry, lower_new_dir_dentry); + dput(lower_old_dir_dentry); + dput(lower_new_dir_dentry); + sdcardfs_put_real_lower(old_dentry, &lower_old_path); + sdcardfs_put_lower_path(new_dentry, &lower_new_path); + REVERT_CRED(saved_cred); +out_eacces: + return err; +} + +#if 0 +static int sdcardfs_readlink(struct dentry *dentry, char __user *buf, int bufsiz) +{ + int err; + struct dentry *lower_dentry; + struct path lower_path; + /* XXX readlink does not requires overriding credential */ + + sdcardfs_get_lower_path(dentry, &lower_path); + lower_dentry = lower_path.dentry; + if (!lower_dentry->d_inode->i_op || + !lower_dentry->d_inode->i_op->readlink) { + err = -EINVAL; + goto out; + } + + err = lower_dentry->d_inode->i_op->readlink(lower_dentry, + buf, bufsiz); + if (err < 0) + goto out; + fsstack_copy_attr_atime(dentry->d_inode, lower_dentry->d_inode); + +out: + sdcardfs_put_lower_path(dentry, &lower_path); + return err; +} +#endif + +#if 0 +static void *sdcardfs_follow_link(struct dentry *dentry, struct nameidata *nd) +{ + char *buf; + int len = PAGE_SIZE, err; + mm_segment_t old_fs; + + /* This is freed by the put_link method assuming a successful call. */ + buf = kmalloc(len, GFP_KERNEL); + if (!buf) { + buf = ERR_PTR(-ENOMEM); + goto out; + } + + /* read the symlink, and then we will follow it */ + old_fs = get_fs(); + set_fs(KERNEL_DS); + err = sdcardfs_readlink(dentry, buf, len); + set_fs(old_fs); + if (err < 0) { + kfree(buf); + buf = ERR_PTR(err); + } else { + buf[err] = '\0'; + } +out: + nd_set_link(nd, buf); + return NULL; +} +#endif + +#if 0 +/* this @nd *IS* still used */ +static void sdcardfs_put_link(struct dentry *dentry, struct nameidata *nd, + void *cookie) +{ + char *buf = nd_get_link(nd); + if (!IS_ERR(buf)) /* free the char* */ + kfree(buf); +} +#endif + +static int sdcardfs_permission(struct inode *inode, int mask, unsigned int flags) +{ + int err; + + if (flags & IPERM_FLAG_RCU) + return -ECHILD; + + /* + * Permission check on sdcardfs inode. + * Calling process should have AID_SDCARD_RW permission + */ + err = generic_permission(inode, mask, 0, inode->i_op->check_acl); + + /* XXX + * Original sdcardfs code calls inode_permission(lower_inode,.. ) + * for checking inode permission. But doing such things here seems + * duplicated work, because the functions called after this func, + * such as vfs_create, vfs_unlink, vfs_rename, and etc, + * does exactly same thing, i.e., they calls inode_permission(). + * So we just let they do the things. + * If there are any security hole, just uncomment following if block. + */ +#if 0 + if (!err) { + /* + * Permission check on lower_inode(=EXT4). + * we check it with AID_MEDIA_RW permission + */ + struct inode *lower_inode; + OVERRIDE_CRED(SDCARDFS_SB(inode->sb)); + + lower_inode = sdcardfs_lower_inode(inode); + err = inode_permission(lower_inode, mask); + + REVERT_CRED(); + } +#endif + return err; + +} + +static int sdcardfs_getattr(struct vfsmount *mnt, struct dentry *dentry, + struct kstat *stat) +{ + struct dentry *lower_dentry; + struct inode *inode; + struct inode *lower_inode; + struct path lower_path; + struct dentry *parent; + struct sdcardfs_sb_info *sbi = SDCARDFS_SB(dentry->d_sb); + + parent = dget_parent(dentry); + if(!check_caller_access_to_name(parent->d_inode, dentry->d_name.name, + sbi->options.derive, 0, 0)) { + printk(KERN_INFO "%s: need to check the caller's gid in packages.list\n" + " dentry: %s, task:%s\n", + __func__, dentry->d_name.name, current->comm); + dput(parent); + return -EACCES; + } + dput(parent); + + inode = dentry->d_inode; + + sdcardfs_get_lower_path(dentry, &lower_path); + lower_dentry = lower_path.dentry; + lower_inode = sdcardfs_lower_inode(inode); + + fsstack_copy_attr_all(inode, lower_inode); + fsstack_copy_inode_size(inode, lower_inode); + /* if the dentry has been moved from other location + * so, on this stage, its derived permission must be + * rechecked from its private field. + */ + fix_derived_permission(inode); + + generic_fillattr(inode, stat); + sdcardfs_put_lower_path(dentry, &lower_path); + return 0; +} + +static int sdcardfs_setattr(struct dentry *dentry, struct iattr *ia) +{ + int err = 0; + struct dentry *lower_dentry; + struct inode *inode; + struct inode *lower_inode; + struct path lower_path; + struct iattr lower_ia; + struct sdcardfs_sb_info *sbi = SDCARDFS_SB(dentry->d_sb); + struct dentry *parent; + int has_rw; + + inode = dentry->d_inode; + + /* + * Check if user has permission to change inode. We don't check if + * this user can change the lower inode: that should happen when + * calling notify_change on the lower inode. + */ + err = inode_change_ok(inode, ia); + + /* no vfs_XXX operations required, cred overriding will be skipped. wj*/ + if (!err) { + /* check the Android group ID */ + has_rw = get_caller_has_rw_locked(sbi->pkgl_id, sbi->options.derive); + parent = dget_parent(dentry); + if(!check_caller_access_to_name(parent->d_inode, dentry->d_name.name, + sbi->options.derive, 1, has_rw)) { + printk(KERN_INFO "%s: need to check the caller's gid in packages.list\n" + " dentry: %s, task:%s\n", + __func__, dentry->d_name.name, current->comm); + err = -EACCES; + } + dput(parent); + } + + if (err) + goto out_err; + + sdcardfs_get_lower_path(dentry, &lower_path); + lower_dentry = lower_path.dentry; + lower_inode = sdcardfs_lower_inode(inode); + + /* prepare our own lower struct iattr (with the lower file) */ + memcpy(&lower_ia, ia, sizeof(lower_ia)); + if (ia->ia_valid & ATTR_FILE) + lower_ia.ia_file = sdcardfs_lower_file(ia->ia_file); + + lower_ia.ia_valid &= ~(ATTR_UID | ATTR_GID | ATTR_MODE); + + /* + * If shrinking, first truncate upper level to cancel writing dirty + * pages beyond the new eof; and also if its' maxbytes is more + * limiting (fail with -EFBIG before making any change to the lower + * level). There is no need to vmtruncate the upper level + * afterwards in the other cases: we fsstack_copy_inode_size from + * the lower level. + */ + if (current->mm) + down_write(¤t->mm->mmap_sem); + if (ia->ia_valid & ATTR_SIZE) { + err = inode_newsize_ok(inode, ia->ia_size); + if (err) { + if (current->mm) + up_write(¤t->mm->mmap_sem); + goto out; + } + truncate_setsize(inode, ia->ia_size); + } + + /* + * mode change is for clearing setuid/setgid bits. Allow lower fs + * to interpret this in its own way. + */ + if (lower_ia.ia_valid & (ATTR_KILL_SUID | ATTR_KILL_SGID)) + lower_ia.ia_valid &= ~ATTR_MODE; + + /* notify the (possibly copied-up) lower inode */ + /* + * Note: we use lower_dentry->d_inode, because lower_inode may be + * unlinked (no inode->i_sb and i_ino==0. This happens if someone + * tries to open(), unlink(), then ftruncate() a file. + */ + mutex_lock(&lower_dentry->d_inode->i_mutex); + err = notify_change(lower_dentry, &lower_ia); /* note: lower_ia */ + mutex_unlock(&lower_dentry->d_inode->i_mutex); + if (current->mm) + up_write(¤t->mm->mmap_sem); + if (err) + goto out; + + /* get attributes from the lower inode */ + fsstack_copy_attr_all(inode, lower_inode); + /* update derived permission of the upper inode */ + fix_derived_permission(inode); + + /* + * Not running fsstack_copy_inode_size(inode, lower_inode), because + * VFS should update our inode size, and notify_change on + * lower_inode should update its size. + */ + +out: + sdcardfs_put_lower_path(dentry, &lower_path); +out_err: + return err; +} + +const struct inode_operations sdcardfs_symlink_iops = { + .permission = sdcardfs_permission, + .setattr = sdcardfs_setattr, + /* XXX Following operations are implemented, + * but FUSE(sdcard) or FAT does not support them + * These methods are *NOT* perfectly tested. + .readlink = sdcardfs_readlink, + .follow_link = sdcardfs_follow_link, + .put_link = sdcardfs_put_link, + */ +}; + +const struct inode_operations sdcardfs_dir_iops = { + .create = sdcardfs_create, + .lookup = sdcardfs_lookup, + .permission = sdcardfs_permission, + .unlink = sdcardfs_unlink, + .mkdir = sdcardfs_mkdir, + .rmdir = sdcardfs_rmdir, + .rename = sdcardfs_rename, + .setattr = sdcardfs_setattr, + .getattr = sdcardfs_getattr, + /* XXX Following operations are implemented, + * but FUSE(sdcard) or FAT does not support them + * These methods are *NOT* perfectly tested. + .symlink = sdcardfs_symlink, + .link = sdcardfs_link, + .mknod = sdcardfs_mknod, + */ +}; + +const struct inode_operations sdcardfs_main_iops = { + .permission = sdcardfs_permission, + .setattr = sdcardfs_setattr, + .getattr = sdcardfs_getattr, +}; diff --git a/fs/sdcardfs/lookup.c b/fs/sdcardfs/lookup.c new file mode 100644 index 000000000000..c0b12375b1bf --- /dev/null +++ b/fs/sdcardfs/lookup.c @@ -0,0 +1,386 @@ +/* + * fs/sdcardfs/lookup.c + * + * Copyright (c) 2013 Samsung Electronics Co. Ltd + * Authors: Daeho Jeong, Woojoong Lee, Seunghwan Hyun, + * Sunghwan Yun, Sungjong Seo + * + * This program has been developed as a stackable file system based on + * the WrapFS which written by + * + * Copyright (c) 1998-2011 Erez Zadok + * Copyright (c) 2009 Shrikar Archak + * Copyright (c) 2003-2011 Stony Brook University + * Copyright (c) 2003-2011 The Research Foundation of SUNY + * + * This file is dual licensed. It may be redistributed and/or modified + * under the terms of the Apache 2.0 License OR version 2 of the GNU + * General Public License. + */ + +#include "sdcardfs.h" +#include "linux/delay.h" + +/* The dentry cache is just so we have properly sized dentries */ +static struct kmem_cache *sdcardfs_dentry_cachep; + +int sdcardfs_init_dentry_cache(void) +{ + sdcardfs_dentry_cachep = + kmem_cache_create("sdcardfs_dentry", + sizeof(struct sdcardfs_dentry_info), + 0, SLAB_RECLAIM_ACCOUNT, NULL); + + return sdcardfs_dentry_cachep ? 0 : -ENOMEM; +} + +void sdcardfs_destroy_dentry_cache(void) +{ + if (sdcardfs_dentry_cachep) + kmem_cache_destroy(sdcardfs_dentry_cachep); +} + +void free_dentry_private_data(struct dentry *dentry) +{ + if (!dentry || !dentry->d_fsdata) + return; + kmem_cache_free(sdcardfs_dentry_cachep, dentry->d_fsdata); + dentry->d_fsdata = NULL; +} + +/* allocate new dentry private data */ +int new_dentry_private_data(struct dentry *dentry) +{ + struct sdcardfs_dentry_info *info = SDCARDFS_D(dentry); + + /* use zalloc to init dentry_info.lower_path */ + info = kmem_cache_zalloc(sdcardfs_dentry_cachep, GFP_ATOMIC); + if (!info) + return -ENOMEM; + + spin_lock_init(&info->lock); + dentry->d_fsdata = info; + + return 0; +} + +static int sdcardfs_inode_test(struct inode *inode, void *candidate_lower_inode) +{ + struct inode *current_lower_inode = sdcardfs_lower_inode(inode); + if (current_lower_inode == (struct inode *)candidate_lower_inode) + return 1; /* found a match */ + else + return 0; /* no match */ +} + +static int sdcardfs_inode_set(struct inode *inode, void *lower_inode) +{ + /* we do actual inode initialization in sdcardfs_iget */ + return 0; +} + +static struct inode *sdcardfs_iget(struct super_block *sb, + struct inode *lower_inode) +{ + struct sdcardfs_inode_info *info; + struct inode *inode; /* the new inode to return */ + int err; + + inode = iget5_locked(sb, /* our superblock */ + /* + * hashval: we use inode number, but we can + * also use "(unsigned long)lower_inode" + * instead. + */ + lower_inode->i_ino, /* hashval */ + sdcardfs_inode_test, /* inode comparison function */ + sdcardfs_inode_set, /* inode init function */ + lower_inode); /* data passed to test+set fxns */ + if (!inode) { + err = -EACCES; + iput(lower_inode); + return ERR_PTR(err); + } + /* if found a cached inode, then just return it */ + if (!(inode->i_state & I_NEW)) + return inode; + + /* initialize new inode */ + info = SDCARDFS_I(inode); + + inode->i_ino = lower_inode->i_ino; + if (!igrab(lower_inode)) { + err = -ESTALE; + return ERR_PTR(err); + } + sdcardfs_set_lower_inode(inode, lower_inode); + + inode->i_version++; + + /* use different set of inode ops for symlinks & directories */ + if (S_ISDIR(lower_inode->i_mode)) + inode->i_op = &sdcardfs_dir_iops; + else if (S_ISLNK(lower_inode->i_mode)) + inode->i_op = &sdcardfs_symlink_iops; + else + inode->i_op = &sdcardfs_main_iops; + + /* use different set of file ops for directories */ + if (S_ISDIR(lower_inode->i_mode)) + inode->i_fop = &sdcardfs_dir_fops; + else + inode->i_fop = &sdcardfs_main_fops; + + inode->i_mapping->a_ops = &sdcardfs_aops; + + inode->i_atime.tv_sec = 0; + inode->i_atime.tv_nsec = 0; + inode->i_mtime.tv_sec = 0; + inode->i_mtime.tv_nsec = 0; + inode->i_ctime.tv_sec = 0; + inode->i_ctime.tv_nsec = 0; + + /* properly initialize special inodes */ + if (S_ISBLK(lower_inode->i_mode) || S_ISCHR(lower_inode->i_mode) || + S_ISFIFO(lower_inode->i_mode) || S_ISSOCK(lower_inode->i_mode)) + init_special_inode(inode, lower_inode->i_mode, + lower_inode->i_rdev); + + /* all well, copy inode attributes */ + fsstack_copy_attr_all(inode, lower_inode); + fsstack_copy_inode_size(inode, lower_inode); + + fix_derived_permission(inode); + + unlock_new_inode(inode); + return inode; +} + +/* + * Connect a sdcardfs inode dentry/inode with several lower ones. This is + * the classic stackable file system "vnode interposition" action. + * + * @dentry: sdcardfs's dentry which interposes on lower one + * @sb: sdcardfs's super_block + * @lower_path: the lower path (caller does path_get/put) + */ +int sdcardfs_interpose(struct dentry *dentry, struct super_block *sb, + struct path *lower_path) +{ + int err = 0; + struct inode *inode; + struct inode *lower_inode; + struct super_block *lower_sb; + + lower_inode = lower_path->dentry->d_inode; + lower_sb = sdcardfs_lower_super(sb); + + /* check that the lower file system didn't cross a mount point */ + if (lower_inode->i_sb != lower_sb) { + err = -EXDEV; + goto out; + } + + /* + * We allocate our new inode below by calling sdcardfs_iget, + * which will initialize some of the new inode's fields + */ + + /* inherit lower inode number for sdcardfs's inode */ + inode = sdcardfs_iget(sb, lower_inode); + if (IS_ERR(inode)) { + err = PTR_ERR(inode); + goto out; + } + + d_add(dentry, inode); + update_derived_permission(dentry); +out: + return err; +} + +/* + * Main driver function for sdcardfs's lookup. + * + * Returns: NULL (ok), ERR_PTR if an error occurred. + * Fills in lower_parent_path with on success. + */ +static struct dentry *__sdcardfs_lookup(struct dentry *dentry, + struct nameidata *nd, struct path *lower_parent_path) +{ + int err = 0; + struct vfsmount *lower_dir_mnt; + struct dentry *lower_dir_dentry = NULL; + struct dentry *lower_dentry; + const char *name; + struct nameidata lower_nd; + struct path lower_path; + struct qstr this; + struct sdcardfs_sb_info *sbi; + + sbi = SDCARDFS_SB(dentry->d_sb); + /* must initialize dentry operations */ + d_set_d_op(dentry, &sdcardfs_ci_dops); + + if (IS_ROOT(dentry)) + goto out; + + name = dentry->d_name.name; + + /* now start the actual lookup procedure */ + lower_dir_dentry = lower_parent_path->dentry; + lower_dir_mnt = lower_parent_path->mnt; + + /* Use vfs_path_lookup to check if the dentry exists or not */ + if (sbi->options.lower_fs == LOWER_FS_EXT4) { + err = vfs_path_lookup(lower_dir_dentry, lower_dir_mnt, name, + LOOKUP_CASE_INSENSITIVE, &lower_nd); + } else if (sbi->options.lower_fs == LOWER_FS_FAT) { + err = vfs_path_lookup(lower_dir_dentry, lower_dir_mnt, name, 0, + &lower_nd); + } + + /* no error: handle positive dentries */ + if (!err) { + /* check if the dentry is an obb dentry + * if true, the lower_inode must be replaced with + * the inode of the graft path */ + + if(need_graft_path(dentry)) { + + /* setup_obb_dentry() + * The lower_path will be stored to the dentry's orig_path + * and the base obbpath will be copyed to the lower_path variable. + * if an error returned, there's no change in the lower_path + * returns: -ERRNO if error (0: no error) */ + err = setup_obb_dentry(dentry, &lower_nd.path); + + if(err) { + /* if the sbi->obbpath is not available, we can optionally + * setup the lower_path with its orig_path. + * but, the current implementation just returns an error + * because the sdcard daemon also regards this case as + * a lookup fail. */ + printk(KERN_INFO "sdcardfs: base obbpath is not available\n"); + sdcardfs_put_reset_orig_path(dentry); + goto out; + } + } + + sdcardfs_set_lower_path(dentry, &lower_nd.path); + err = sdcardfs_interpose(dentry, dentry->d_sb, &lower_nd.path); + if (err) /* path_put underlying path on error */ + sdcardfs_put_reset_lower_path(dentry); + goto out; + } + + /* + * We don't consider ENOENT an error, and we want to return a + * negative dentry. + */ + if (err && err != -ENOENT) + goto out; + + /* instatiate a new negative dentry */ + this.name = name; + this.len = strlen(name); + this.hash = full_name_hash(this.name, this.len); + lower_dentry = d_lookup(lower_dir_dentry, &this); + if (lower_dentry) + goto setup_lower; + + lower_dentry = d_alloc(lower_dir_dentry, &this); + if (!lower_dentry) { + err = -ENOMEM; + goto out; + } + d_add(lower_dentry, NULL); /* instantiate and hash */ + +setup_lower: + lower_path.dentry = lower_dentry; + lower_path.mnt = mntget(lower_dir_mnt); + sdcardfs_set_lower_path(dentry, &lower_path); + + /* + * If the intent is to create a file, then don't return an error, so + * the VFS will continue the process of making this negative dentry + * into a positive one. + */ + if (nd) { + if (nd->flags & (LOOKUP_CREATE|LOOKUP_RENAME_TARGET)) + err = 0; + } else + err = 0; + +out: + return ERR_PTR(err); +} + +/* + * On success: + * fills dentry object appropriate values and returns NULL. + * On fail (== error) + * returns error ptr + * + * @dir : Parent inode. It is locked (dir->i_mutex) + * @dentry : Target dentry to lookup. we should set each of fields. + * (dentry->d_name is initialized already) + * @nd : nameidata of parent inode + */ +struct dentry *sdcardfs_lookup(struct inode *dir, struct dentry *dentry, + struct nameidata *nd) +{ + struct dentry *ret = NULL, *parent; + struct path lower_parent_path; + int err = 0; + struct sdcardfs_sb_info *sbi = SDCARDFS_SB(dentry->d_sb); + const struct cred *saved_cred = NULL; + + parent = dget_parent(dentry); + + if(!check_caller_access_to_name(parent->d_inode, dentry->d_name.name, + sbi->options.derive, 0, 0)) { + ret = ERR_PTR(-EACCES); + printk(KERN_INFO "%s: need to check the caller's gid in packages.list\n" + " dentry: %s, task:%s\n", + __func__, dentry->d_name.name, current->comm); + goto out_err; + } + + /* save current_cred and override it */ + OVERRIDE_CRED_PTR(SDCARDFS_SB(dir->i_sb), saved_cred); + + sdcardfs_get_lower_path(parent, &lower_parent_path); + + /* allocate dentry private data. We free it in ->d_release */ + err = new_dentry_private_data(dentry); + if (err) { + ret = ERR_PTR(err); + goto out; + } + + ret = __sdcardfs_lookup(dentry, nd, &lower_parent_path); + if (IS_ERR(ret)) + { + goto out; + } + if (ret) + dentry = ret; + if (dentry->d_inode) { + fsstack_copy_attr_times(dentry->d_inode, + sdcardfs_lower_inode(dentry->d_inode)); + /* get drived permission */ + get_derived_permission(parent, dentry); + fix_derived_permission(dentry->d_inode); + } + /* update parent directory's atime */ + fsstack_copy_attr_atime(parent->d_inode, + sdcardfs_lower_inode(parent->d_inode)); + +out: + sdcardfs_put_lower_path(parent, &lower_parent_path); + REVERT_CRED(saved_cred); +out_err: + dput(parent); + return ret; +} diff --git a/fs/sdcardfs/main.c b/fs/sdcardfs/main.c new file mode 100644 index 000000000000..1fdceffec72c --- /dev/null +++ b/fs/sdcardfs/main.c @@ -0,0 +1,425 @@ +/* + * fs/sdcardfs/main.c + * + * Copyright (c) 2013 Samsung Electronics Co. Ltd + * Authors: Daeho Jeong, Woojoong Lee, Seunghwan Hyun, + * Sunghwan Yun, Sungjong Seo + * + * This program has been developed as a stackable file system based on + * the WrapFS which written by + * + * Copyright (c) 1998-2011 Erez Zadok + * Copyright (c) 2009 Shrikar Archak + * Copyright (c) 2003-2011 Stony Brook University + * Copyright (c) 2003-2011 The Research Foundation of SUNY + * + * This file is dual licensed. It may be redistributed and/or modified + * under the terms of the Apache 2.0 License OR version 2 of the GNU + * General Public License. + */ + +#include "sdcardfs.h" +#include +#include +#include + +enum { + Opt_uid, + Opt_gid, + Opt_wgid, + Opt_debug, + Opt_split, + Opt_derive, + Opt_lower_fs, + Opt_reserved_mb, + Opt_err, +}; + +static const match_table_t sdcardfs_tokens = { + {Opt_uid, "uid=%u"}, + {Opt_gid, "gid=%u"}, + {Opt_wgid, "wgid=%u"}, + {Opt_debug, "debug"}, + {Opt_split, "split"}, + {Opt_derive, "derive=%s"}, + {Opt_lower_fs, "lower_fs=%s"}, + {Opt_reserved_mb, "reserved_mb=%u"}, + {Opt_err, NULL} +}; + +static int parse_options(struct super_block *sb, char *options, int silent, + int *debug, struct sdcardfs_mount_options *opts) +{ + char *p; + substring_t args[MAX_OPT_ARGS]; + int option; + char *string_option; + + /* by default, we use AID_MEDIA_RW as uid, gid */ + opts->fs_low_uid = AID_MEDIA_RW; + opts->fs_low_gid = AID_MEDIA_RW; + /* by default, we use AID_SDCARD_RW as write_gid */ + opts->write_gid = AID_SDCARD_RW; + /* default permission policy + * (DERIVE_NONE | DERIVE_LEGACY | DERIVE_UNIFIED) */ + opts->derive = DERIVE_NONE; + opts->split_perms = 0; + /* by default, we use LOWER_FS_EXT4 as lower fs type */ + opts->lower_fs = LOWER_FS_EXT4; + /* by default, 0MB is reserved */ + opts->reserved_mb = 0; + + *debug = 0; + + if (!options) + return 0; + + while ((p = strsep(&options, ",")) != NULL) { + int token; + if (!*p) + continue; + + token = match_token(p, sdcardfs_tokens, args); + + switch (token) { + case Opt_debug: + *debug = 1; + break; + case Opt_uid: + if (match_int(&args[0], &option)) + return 0; + opts->fs_low_uid = option; + break; + case Opt_gid: + if (match_int(&args[0], &option)) + return 0; + opts->fs_low_gid = option; + break; + case Opt_wgid: + if (match_int(&args[0], &option)) + return 0; + opts->write_gid = option; + break; + case Opt_split: + opts->split_perms=1; + break; + case Opt_derive: + string_option = match_strdup(&args[0]); + if (!strcmp("none", string_option)) { + opts->derive = DERIVE_NONE; + } else if (!strcmp("legacy", string_option)) { + opts->derive = DERIVE_LEGACY; + } else if (!strcmp("unified", string_option)) { + opts->derive = DERIVE_UNIFIED; + } else { + kfree(string_option); + goto invalid_option; + } + kfree(string_option); + break; + case Opt_lower_fs: + string_option = match_strdup(&args[0]); + if (!strcmp("ext4", string_option)) { + opts->lower_fs = LOWER_FS_EXT4; + } else if (!strcmp("fat", string_option)) { + opts->lower_fs = LOWER_FS_FAT; + } else { + kfree(string_option); + goto invalid_option; + } + kfree(string_option); + break; + case Opt_reserved_mb: + if (match_int(&args[0], &option)) + return 0; + opts->reserved_mb = option; + break; + /* unknown option */ + default: +invalid_option: + if (!silent) { + printk( KERN_ERR "Unrecognized mount option \"%s\" " + "or missing value", p); + } + return -EINVAL; + } + } + + if (*debug) { + printk( KERN_INFO "sdcardfs : options - debug:%d\n", *debug); + printk( KERN_INFO "sdcardfs : options - uid:%d\n", + opts->fs_low_uid); + printk( KERN_INFO "sdcardfs : options - gid:%d\n", + opts->fs_low_gid); + } + + return 0; +} + +/* + * our custom d_alloc_root work-alike + * + * we can't use d_alloc_root if we want to use our own interpose function + * unchanged, so we simply call our own "fake" d_alloc_root + */ +static struct dentry *sdcardfs_d_alloc_root(struct super_block *sb) +{ + struct dentry *ret = NULL; + + if (sb) { + static const struct qstr name = { + .name = "/", + .len = 1 + }; + + ret = d_alloc(NULL, &name); + if (ret) { + d_set_d_op(ret, &sdcardfs_ci_dops); + ret->d_sb = sb; + ret->d_parent = ret; + } + } + return ret; +} + +/* + * There is no need to lock the sdcardfs_super_info's rwsem as there is no + * way anyone can have a reference to the superblock at this point in time. + */ +static int sdcardfs_read_super(struct super_block *sb, const char *dev_name, + void *raw_data, int silent) +{ + int err = 0; + int debug; + struct super_block *lower_sb; + struct path lower_path; + struct sdcardfs_sb_info *sb_info; + void *pkgl_id; + + printk(KERN_INFO "sdcardfs version 2.0\n"); + + if (!dev_name) { + printk(KERN_ERR + "sdcardfs: read_super: missing dev_name argument\n"); + err = -EINVAL; + goto out; + } + + printk(KERN_INFO "sdcardfs: dev_name -> %s\n", dev_name); + printk(KERN_INFO "sdcardfs: options -> %s\n", (char *)raw_data); + + /* parse lower path */ + err = kern_path(dev_name, LOOKUP_FOLLOW | LOOKUP_DIRECTORY, + &lower_path); + if (err) { + printk(KERN_ERR "sdcardfs: error accessing " + "lower directory '%s'\n", dev_name); + goto out; + } + + /* allocate superblock private data */ + sb->s_fs_info = kzalloc(sizeof(struct sdcardfs_sb_info), GFP_KERNEL); + if (!SDCARDFS_SB(sb)) { + printk(KERN_CRIT "sdcardfs: read_super: out of memory\n"); + err = -ENOMEM; + goto out_free; + } + + sb_info = sb->s_fs_info; + + /* parse options */ + err = parse_options(sb, raw_data, silent, &debug, &sb_info->options); + if (err) { + printk(KERN_ERR "sdcardfs: invalid options\n"); + goto out_freesbi; + } + + if (sb_info->options.derive != DERIVE_NONE) { + pkgl_id = packagelist_create(sb_info->options.write_gid); + if(IS_ERR(pkgl_id)) + goto out_freesbi; + else + sb_info->pkgl_id = pkgl_id; + } + + /* set the lower superblock field of upper superblock */ + lower_sb = lower_path.dentry->d_sb; + atomic_inc(&lower_sb->s_active); + sdcardfs_set_lower_super(sb, lower_sb); + + /* inherit maxbytes from lower file system */ + sb->s_maxbytes = lower_sb->s_maxbytes; + + /* + * Our c/m/atime granularity is 1 ns because we may stack on file + * systems whose granularity is as good. + */ + sb->s_time_gran = 1; + + sb->s_magic = SDCARDFS_SUPER_MAGIC; + sb->s_op = &sdcardfs_sops; + + /* see comment next to the definition of sdcardfs_d_alloc_root */ + sb->s_root = sdcardfs_d_alloc_root(sb); + if (!sb->s_root) { + err = -ENOMEM; + goto out_sput; + } + + /* link the upper and lower dentries */ + sb->s_root->d_fsdata = NULL; + err = new_dentry_private_data(sb->s_root); + if (err) + goto out_freeroot; + + /* set the lower dentries for s_root */ + sdcardfs_set_lower_path(sb->s_root, &lower_path); + + /* call interpose to create the upper level inode */ + err = sdcardfs_interpose(sb->s_root, sb, &lower_path); + if (!err) { + /* setup permission policy */ + switch(sb_info->options.derive) { + case DERIVE_NONE: + setup_derived_state(sb->s_root->d_inode, + PERM_ROOT, 0, AID_ROOT, AID_SDCARD_RW, 00775); + sb_info->obbpath_s = NULL; + break; + case DERIVE_LEGACY: + /* Legacy behavior used to support internal multiuser layout which + * places user_id at the top directory level, with the actual roots + * just below that. Shared OBB path is also at top level. */ + setup_derived_state(sb->s_root->d_inode, + PERM_LEGACY_PRE_ROOT, 0, AID_ROOT, AID_SDCARD_R, 00771); + /* initialize the obbpath string and lookup the path + * sb_info->obb_path will be deactivated by path_put + * on sdcardfs_put_super */ + sb_info->obbpath_s = kzalloc(PATH_MAX, GFP_KERNEL); + snprintf(sb_info->obbpath_s, PATH_MAX, "%s/obb", dev_name); + err = prepare_dir(sb_info->obbpath_s, + sb_info->options.fs_low_uid, + sb_info->options.fs_low_gid, 00755); + if(err) + printk(KERN_ERR "sdcardfs: %s: %d, error on creating %s\n", + __func__,__LINE__, sb_info->obbpath_s); + break; + case DERIVE_UNIFIED: + /* Unified multiuser layout which places secondary user_id under + * /Android/user and shared OBB path under /Android/obb. */ + setup_derived_state(sb->s_root->d_inode, + PERM_ROOT, 0, AID_ROOT, AID_SDCARD_R, 00771); + + sb_info->obbpath_s = kzalloc(PATH_MAX, GFP_KERNEL); + snprintf(sb_info->obbpath_s, PATH_MAX, "%s/Android/obb", dev_name); + break; + } + fix_derived_permission(sb->s_root->d_inode); + + if (!silent) + printk(KERN_INFO "sdcardfs: mounted on top of %s type %s\n", + dev_name, lower_sb->s_type->name); + goto out; + } + /* else error: fall through */ + + free_dentry_private_data(sb->s_root); +out_freeroot: + dput(sb->s_root); +out_sput: + /* drop refs we took earlier */ + atomic_dec(&lower_sb->s_active); + packagelist_destroy(sb_info->pkgl_id); +out_freesbi: + kfree(SDCARDFS_SB(sb)); + sb->s_fs_info = NULL; +out_free: + path_put(&lower_path); + +out: + return err; +} + +/* A feature which supports mount_nodev() with options */ +static struct dentry *mount_nodev_with_options(struct file_system_type *fs_type, + int flags, const char *dev_name, void *data, + int (*fill_super)(struct super_block *, const char *, void *, int)) + +{ + int error; + struct super_block *s = sget(fs_type, NULL, set_anon_super, NULL); + + if (IS_ERR(s)) + return ERR_CAST(s); + + s->s_flags = flags; + + error = fill_super(s, dev_name, data, flags & MS_SILENT ? 1 : 0); + if (error) { + deactivate_locked_super(s); + return ERR_PTR(error); + } + s->s_flags |= MS_ACTIVE; + return dget(s->s_root); +} + +struct dentry *sdcardfs_mount(struct file_system_type *fs_type, int flags, + const char *dev_name, void *raw_data) +{ + /* + * dev_name is a lower_path_name, + * raw_data is a option string. + */ + return mount_nodev_with_options(fs_type, flags, dev_name, + raw_data, sdcardfs_read_super); +} + +static struct file_system_type sdcardfs_fs_type = { + .owner = THIS_MODULE, + .name = SDCARDFS_NAME, + .mount = sdcardfs_mount, + .kill_sb = generic_shutdown_super, + .fs_flags = FS_REVAL_DOT, +}; + +static int __init init_sdcardfs_fs(void) +{ + int err; + + pr_info("Registering sdcardfs " SDCARDFS_VERSION "\n"); + + err = sdcardfs_init_inode_cache(); + if (err) + goto out; + err = sdcardfs_init_dentry_cache(); + if (err) + goto out; + err = packagelist_init(); + if (err) + goto out; + err = register_filesystem(&sdcardfs_fs_type); +out: + if (err) { + sdcardfs_destroy_inode_cache(); + sdcardfs_destroy_dentry_cache(); + packagelist_exit(); + } + return err; +} + +static void __exit exit_sdcardfs_fs(void) +{ + sdcardfs_destroy_inode_cache(); + sdcardfs_destroy_dentry_cache(); + packagelist_exit(); + unregister_filesystem(&sdcardfs_fs_type); + pr_info("Completed sdcardfs module unload\n"); +} + +MODULE_AUTHOR("Erez Zadok, Filesystems and Storage Lab, Stony Brook University" + " (http://www.fsl.cs.sunysb.edu/)"); +MODULE_DESCRIPTION("Wrapfs " SDCARDFS_VERSION + " (http://wrapfs.filesystems.org/)"); +MODULE_LICENSE("GPL"); + +module_init(init_sdcardfs_fs); +module_exit(exit_sdcardfs_fs); diff --git a/fs/sdcardfs/mmap.c b/fs/sdcardfs/mmap.c new file mode 100644 index 000000000000..c807d7f18f8b --- /dev/null +++ b/fs/sdcardfs/mmap.c @@ -0,0 +1,82 @@ +/* + * fs/sdcardfs/mmap.c + * + * Copyright (c) 2013 Samsung Electronics Co. Ltd + * Authors: Daeho Jeong, Woojoong Lee, Seunghwan Hyun, + * Sunghwan Yun, Sungjong Seo + * + * This program has been developed as a stackable file system based on + * the WrapFS which written by + * + * Copyright (c) 1998-2011 Erez Zadok + * Copyright (c) 2009 Shrikar Archak + * Copyright (c) 2003-2011 Stony Brook University + * Copyright (c) 2003-2011 The Research Foundation of SUNY + * + * This file is dual licensed. It may be redistributed and/or modified + * under the terms of the Apache 2.0 License OR version 2 of the GNU + * General Public License. + */ + +#include "sdcardfs.h" + +static int sdcardfs_fault(struct vm_area_struct *vma, struct vm_fault *vmf) +{ + int err; + struct file *file, *lower_file; + const struct vm_operations_struct *lower_vm_ops; + struct vm_area_struct lower_vma; + + memcpy(&lower_vma, vma, sizeof(struct vm_area_struct)); + file = lower_vma.vm_file; + lower_vm_ops = SDCARDFS_F(file)->lower_vm_ops; + BUG_ON(!lower_vm_ops); + + lower_file = sdcardfs_lower_file(file); + /* + * XXX: vm_ops->fault may be called in parallel. Because we have to + * resort to temporarily changing the vma->vm_file to point to the + * lower file, a concurrent invocation of sdcardfs_fault could see a + * different value. In this workaround, we keep a different copy of + * the vma structure in our stack, so we never expose a different + * value of the vma->vm_file called to us, even temporarily. A + * better fix would be to change the calling semantics of ->fault to + * take an explicit file pointer. + */ + lower_vma.vm_file = lower_file; + err = lower_vm_ops->fault(&lower_vma, vmf); + return err; +} + +static ssize_t sdcardfs_direct_IO(int rw, struct kiocb *iocb, + const struct iovec *iov, loff_t offset, + unsigned long nr_segs) +{ + /* + * This function returns zero on purpose in order to support direct IO. + * __dentry_open checks a_ops->direct_IO and returns EINVAL if it is null. + * + * However, this function won't be called by certain file operations + * including generic fs functions. * reads and writes are delivered to + * the lower file systems and the direct IOs will be handled by them. + * + * NOTE: exceptionally, on the recent kernels (since Linux 3.8.x), + * swap_writepage invokes this function directly. + */ + printk(KERN_INFO "%s, operation is not supported\n", __func__); + return 0; +} + +/* + * XXX: the default address_space_ops for sdcardfs is empty. We cannot set + * our inode->i_mapping->a_ops to NULL because too many code paths expect + * the a_ops vector to be non-NULL. + */ +const struct address_space_operations sdcardfs_aops = { + /* empty on purpose */ + .direct_IO = sdcardfs_direct_IO, +}; + +const struct vm_operations_struct sdcardfs_vm_ops = { + .fault = sdcardfs_fault, +}; diff --git a/fs/sdcardfs/multiuser.h b/fs/sdcardfs/multiuser.h new file mode 100644 index 000000000000..923ba101dfa9 --- /dev/null +++ b/fs/sdcardfs/multiuser.h @@ -0,0 +1,37 @@ +/* + * fs/sdcardfs/multiuser.h + * + * Copyright (c) 2013 Samsung Electronics Co. Ltd + * Authors: Daeho Jeong, Woojoong Lee, Seunghwan Hyun, + * Sunghwan Yun, Sungjong Seo + * + * This program has been developed as a stackable file system based on + * the WrapFS which written by + * + * Copyright (c) 1998-2011 Erez Zadok + * Copyright (c) 2009 Shrikar Archak + * Copyright (c) 2003-2011 Stony Brook University + * Copyright (c) 2003-2011 The Research Foundation of SUNY + * + * This file is dual licensed. It may be redistributed and/or modified + * under the terms of the Apache 2.0 License OR version 2 of the GNU + * General Public License. + */ + +#define MULTIUSER_APP_PER_USER_RANGE 100000 + +typedef uid_t userid_t; +typedef uid_t appid_t; + +static inline userid_t multiuser_get_user_id(uid_t uid) { + return uid / MULTIUSER_APP_PER_USER_RANGE; +} + +static inline appid_t multiuser_get_app_id(uid_t uid) { + return uid % MULTIUSER_APP_PER_USER_RANGE; +} + +static inline uid_t multiuser_get_uid(userid_t userId, appid_t appId) { + return userId * MULTIUSER_APP_PER_USER_RANGE + (appId % MULTIUSER_APP_PER_USER_RANGE); +} + diff --git a/fs/sdcardfs/packagelist.c b/fs/sdcardfs/packagelist.c new file mode 100644 index 000000000000..c786d8f92203 --- /dev/null +++ b/fs/sdcardfs/packagelist.c @@ -0,0 +1,458 @@ +/* + * fs/sdcardfs/packagelist.c + * + * Copyright (c) 2013 Samsung Electronics Co. Ltd + * Authors: Daeho Jeong, Woojoong Lee, Seunghwan Hyun, + * Sunghwan Yun, Sungjong Seo + * + * This program has been developed as a stackable file system based on + * the WrapFS which written by + * + * Copyright (c) 1998-2011 Erez Zadok + * Copyright (c) 2009 Shrikar Archak + * Copyright (c) 2003-2011 Stony Brook University + * Copyright (c) 2003-2011 The Research Foundation of SUNY + * + * This file is dual licensed. It may be redistributed and/or modified + * under the terms of the Apache 2.0 License OR version 2 of the GNU + * General Public License. + */ + +#include "sdcardfs.h" +#include "strtok.h" +#include "hashtable.h" +#include +#include +#include +#include + +#define STRING_BUF_SIZE (512) + +struct hashtable_entry { + struct hlist_node hlist; + void *key; + int value; +}; + +struct packagelist_data { + DECLARE_HASHTABLE(package_to_appid,8); + DECLARE_HASHTABLE(appid_with_rw,7); + struct mutex hashtable_lock; + struct task_struct *thread_id; + gid_t write_gid; + char *strtok_last; + char read_buf[STRING_BUF_SIZE]; + char event_buf[STRING_BUF_SIZE]; + char app_name_buf[STRING_BUF_SIZE]; + char gids_buf[STRING_BUF_SIZE]; +}; + +static struct kmem_cache *hashtable_entry_cachep; + +/* Path to system-provided mapping of package name to appIds */ +static const char* const kpackageslist_file = "/data/system/packages.list"; +/* Supplementary groups to execute with */ +static const gid_t kgroups[1] = { AID_PACKAGE_INFO }; + +static unsigned int str_hash(void *key) { + int i; + unsigned int h = strlen(key); + char *data = (char *)key; + + for (i = 0; i < strlen(key); i++) { + h = h * 31 + *data; + data++; + } + return h; +} + +static int contain_appid_key(struct packagelist_data *pkgl_dat, void *appid) { + struct hashtable_entry *hash_cur; + struct hlist_node *h_n; + + hash_for_each_possible(pkgl_dat->appid_with_rw, hash_cur, hlist, (unsigned int)appid, h_n) + if (appid == hash_cur->key) + return 1; + return 0; +} + +/* Return if the calling UID holds sdcard_rw. */ +int get_caller_has_rw_locked(void *pkgl_id, derive_t derive) { + struct packagelist_data *pkgl_dat = (struct packagelist_data *)pkgl_id; + appid_t appid; + int ret; + + /* No additional permissions enforcement */ + if (derive == DERIVE_NONE) { + return 1; + } + + appid = multiuser_get_app_id(current_fsuid()); + mutex_lock(&pkgl_dat->hashtable_lock); + ret = contain_appid_key(pkgl_dat, (void *)appid); + mutex_unlock(&pkgl_dat->hashtable_lock); + return ret; +} + +appid_t get_appid(void *pkgl_id, const char *app_name) +{ + struct packagelist_data *pkgl_dat = (struct packagelist_data *)pkgl_id; + struct hashtable_entry *hash_cur; + struct hlist_node *h_n; + unsigned int hash = str_hash((void *)app_name); + appid_t ret_id; + + //printk(KERN_INFO "sdcardfs: %s: %s, %u\n", __func__, (char *)app_name, hash); + mutex_lock(&pkgl_dat->hashtable_lock); + hash_for_each_possible(pkgl_dat->package_to_appid, hash_cur, hlist, hash, h_n) { + //printk(KERN_INFO "sdcardfs: %s: %s\n", __func__, (char *)hash_cur->key); + if (!strcasecmp(app_name, hash_cur->key)) { + ret_id = (appid_t)hash_cur->value; + mutex_unlock(&pkgl_dat->hashtable_lock); + //printk(KERN_INFO "=> app_id: %d\n", (int)ret_id); + return ret_id; + } + } + mutex_unlock(&pkgl_dat->hashtable_lock); + //printk(KERN_INFO "=> app_id: %d\n", 0); + return 0; +} + +/* Kernel has already enforced everything we returned through + * derive_permissions_locked(), so this is used to lock down access + * even further, such as enforcing that apps hold sdcard_rw. */ +int check_caller_access_to_name(struct inode *parent_node, const char* name, + derive_t derive, int w_ok, int has_rw) { + + /* Always block security-sensitive files at root */ + if (parent_node && SDCARDFS_I(parent_node)->perm == PERM_ROOT) { + if (!strcasecmp(name, "autorun.inf") + || !strcasecmp(name, ".android_secure") + || !strcasecmp(name, "android_secure")) { + return 0; + } + } + + /* No additional permissions enforcement */ + if (derive == DERIVE_NONE) { + return 1; + } + + /* Root always has access; access for any other UIDs should always + * be controlled through packages.list. */ + if (current_fsuid() == 0) { + return 1; + } + + /* If asking to write, verify that caller either owns the + * parent or holds sdcard_rw. */ + if (w_ok) { + if (parent_node && + (current_fsuid() == SDCARDFS_I(parent_node)->d_uid)) { + return 1; + } + return has_rw; + } + + /* No extra permissions to enforce */ + return 1; +} + +/* This function is used when file opening. The open flags must be + * checked before calling check_caller_access_to_name() */ +int open_flags_to_access_mode(int open_flags) { + if((open_flags & O_ACCMODE) == O_RDONLY) { + return 0; /* R_OK */ + } else if ((open_flags & O_ACCMODE) == O_WRONLY) { + return 1; /* W_OK */ + } else { + /* Probably O_RDRW, but treat as default to be safe */ + return 1; /* R_OK | W_OK */ + } +} + +static int insert_str_to_int(struct packagelist_data *pkgl_dat, void *key, int value) { + struct hashtable_entry *hash_cur; + struct hashtable_entry *new_entry; + struct hlist_node *h_n; + unsigned int hash = str_hash(key); + + //printk(KERN_INFO "sdcardfs: %s: %s: %d, %u\n", __func__, (char *)key, value, hash); + hash_for_each_possible(pkgl_dat->package_to_appid, hash_cur, hlist, hash, h_n) { + if (!strcasecmp(key, hash_cur->key)) { + hash_cur->value = value; + return 0; + } + } + new_entry = kmem_cache_alloc(hashtable_entry_cachep, GFP_KERNEL); + if (!new_entry) + return -ENOMEM; + new_entry->key = kstrdup(key, GFP_KERNEL); + new_entry->value = value; + hash_add(pkgl_dat->package_to_appid, &new_entry->hlist, hash); + return 0; +} + +static void remove_str_to_int(struct hashtable_entry *h_entry) { + //printk(KERN_INFO "sdcardfs: %s: %s: %d\n", __func__, (char *)h_entry->key, h_entry->value); + kfree(h_entry->key); + kmem_cache_free(hashtable_entry_cachep, h_entry); +} + +static int insert_int_to_null(struct packagelist_data *pkgl_dat, void *key, int value) { + struct hashtable_entry *hash_cur; + struct hashtable_entry *new_entry; + struct hlist_node *h_n; + + //printk(KERN_INFO "sdcardfs: %s: %d: %d\n", __func__, (int)key, value); + hash_for_each_possible(pkgl_dat->appid_with_rw, hash_cur, hlist, + (unsigned int)key, h_n) { + if (key == hash_cur->key) { + hash_cur->value = value; + return 0; + } + } + new_entry = kmem_cache_alloc(hashtable_entry_cachep, GFP_KERNEL); + if (!new_entry) + return -ENOMEM; + new_entry->key = key; + new_entry->value = value; + hash_add(pkgl_dat->appid_with_rw, &new_entry->hlist, + (unsigned int)new_entry->key); + return 0; +} + +static void remove_int_to_null(struct hashtable_entry *h_entry) { + //printk(KERN_INFO "sdcardfs: %s: %d: %d\n", __func__, (int)h_entry->key, h_entry->value); + kmem_cache_free(hashtable_entry_cachep, h_entry); +} + +static void remove_all_hashentrys(struct packagelist_data *pkgl_dat) +{ + struct hashtable_entry *hash_cur; + struct hlist_node *h_n; + struct hlist_node *h_t; + int i; + + hash_for_each_safe(pkgl_dat->package_to_appid, i, h_t, hash_cur, hlist, h_n) + remove_str_to_int(hash_cur); + hash_for_each_safe(pkgl_dat->appid_with_rw, i, h_t, hash_cur, hlist, h_n) + remove_int_to_null(hash_cur); + + hash_init(pkgl_dat->package_to_appid); + hash_init(pkgl_dat->appid_with_rw); +} + +static int read_package_list(struct packagelist_data *pkgl_dat) { + int ret; + int fd; + int read_amount; + + printk(KERN_INFO "sdcardfs: read_package_list\n"); + + mutex_lock(&pkgl_dat->hashtable_lock); + + remove_all_hashentrys(pkgl_dat); + + fd = sys_open(kpackageslist_file, O_RDONLY, 0); + if (fd < 0) { + printk(KERN_ERR "sdcardfs: failed to open package list\n"); + mutex_unlock(&pkgl_dat->hashtable_lock); + return fd; + } + + while ((read_amount = sys_read(fd, pkgl_dat->read_buf, + sizeof(pkgl_dat->read_buf))) > 0) { + int appid; + char *token; + int one_line_len = 0; + int additional_read; + unsigned long ret_gid; + + while (one_line_len < read_amount) { + if (pkgl_dat->read_buf[one_line_len] == '\n') { + one_line_len++; + break; + } + one_line_len++; + } + additional_read = read_amount - one_line_len; + if (additional_read > 0) + sys_lseek(fd, -additional_read, SEEK_CUR); + + if (sscanf(pkgl_dat->read_buf, "%s %d %*d %*s %*s %s", + pkgl_dat->app_name_buf, &appid, + pkgl_dat->gids_buf) == 3) { + ret = insert_str_to_int(pkgl_dat, pkgl_dat->app_name_buf, appid); + if (ret) { + sys_close(fd); + mutex_unlock(&pkgl_dat->hashtable_lock); + return ret; + } + + token = strtok_r(pkgl_dat->gids_buf, ",", &pkgl_dat->strtok_last); + while (token != NULL) { + if (!kstrtoul(token, 10, &ret_gid) && + (ret_gid == pkgl_dat->write_gid)) { + ret = insert_int_to_null(pkgl_dat, (void *)appid, 1); + if (ret) { + sys_close(fd); + mutex_unlock(&pkgl_dat->hashtable_lock); + return ret; + } + break; + } + token = strtok_r(NULL, ",", &pkgl_dat->strtok_last); + } + } + } + + sys_close(fd); + mutex_unlock(&pkgl_dat->hashtable_lock); + return 0; +} + +static int packagelist_reader(void *thread_data) +{ + struct packagelist_data *pkgl_dat = (struct packagelist_data *)thread_data; + struct inotify_event *event; + bool active = false; + int event_pos; + int event_size; + int res = 0; + int nfd; + + allow_signal(SIGINT); + + nfd = sys_inotify_init(); + if (nfd < 0) { + printk(KERN_ERR "sdcardfs: inotify_init failed: %d\n", nfd); + return nfd; + } + + while (!kthread_should_stop()) { + if (signal_pending(current)) { + ssleep(1); + continue; + } + + if (!active) { + res = sys_inotify_add_watch(nfd, kpackageslist_file, IN_DELETE_SELF); + if (res < 0) { + if (res == -ENOENT || res == -EACCES) { + /* Framework may not have created yet, sleep and retry */ + printk(KERN_ERR "sdcardfs: missing packages.list; retrying\n"); + ssleep(2); + printk(KERN_ERR "sdcardfs: missing packages.list_end; retrying\n"); + continue; + } else { + printk(KERN_ERR "sdcardfs: inotify_add_watch failed: %d\n", res); + goto interruptable_sleep; + } + } + /* Watch above will tell us about any future changes, so + * read the current state. */ + res = read_package_list(pkgl_dat); + if (res) { + printk(KERN_ERR "sdcardfs: read_package_list failed: %d\n", res); + goto interruptable_sleep; + } + active = true; + } + + event_pos = 0; + res = sys_read(nfd, pkgl_dat->event_buf, sizeof(pkgl_dat->event_buf)); + if (res < (int) sizeof(*event)) { + if (res == -EINTR) + continue; + printk(KERN_ERR "sdcardfs: failed to read inotify event: %d\n", res); + goto interruptable_sleep; + } + + while (res >= (int) sizeof(*event)) { + event = (struct inotify_event *) (pkgl_dat->event_buf + event_pos); + + printk(KERN_INFO "sdcardfs: inotify event: %08x\n", event->mask); + if ((event->mask & IN_IGNORED) == IN_IGNORED) { + /* Previously watched file was deleted, probably due to move + * that swapped in new data; re-arm the watch and read. */ + active = false; + } + + event_size = sizeof(*event) + event->len; + res -= event_size; + event_pos += event_size; + } + continue; + +interruptable_sleep: + set_current_state(TASK_INTERRUPTIBLE); + schedule(); + } + flush_signals(current); + sys_close(nfd); + return res; +} + +void * packagelist_create(gid_t write_gid) +{ + struct packagelist_data *pkgl_dat; + struct task_struct *packagelist_thread; + + pkgl_dat = kmalloc(sizeof(*pkgl_dat), GFP_KERNEL | __GFP_ZERO); + if (!pkgl_dat) { + printk(KERN_ERR "sdcardfs: creating kthread failed\n"); + return ERR_PTR(-ENOMEM); + } + + mutex_init(&pkgl_dat->hashtable_lock); + hash_init(pkgl_dat->package_to_appid); + hash_init(pkgl_dat->appid_with_rw); + pkgl_dat->write_gid = write_gid; + + packagelist_thread = kthread_run(packagelist_reader, (void *)pkgl_dat, "pkgld"); + if (IS_ERR(packagelist_thread)) { + printk(KERN_ERR "sdcardfs: creating kthread failed\n"); + kfree(pkgl_dat); + return packagelist_thread; + } + pkgl_dat->thread_id = packagelist_thread; + + printk(KERN_INFO "sdcardfs: created packagelist pkgld/%d\n", + (int)pkgl_dat->thread_id->pid); + + return (void *)pkgl_dat; +} + +void packagelist_destroy(void *pkgl_id) +{ + struct packagelist_data *pkgl_dat = (struct packagelist_data *)pkgl_id; + pid_t pkgl_pid = pkgl_dat->thread_id->pid; + + force_sig_info(SIGINT, SEND_SIG_PRIV, pkgl_dat->thread_id); + kthread_stop(pkgl_dat->thread_id); + remove_all_hashentrys(pkgl_dat); + printk(KERN_INFO "sdcardfs: destroyed packagelist pkgld/%d\n", (int)pkgl_pid); + kfree(pkgl_dat); +} + +int packagelist_init(void) +{ + hashtable_entry_cachep = + kmem_cache_create("packagelist_hashtable_entry", + sizeof(struct hashtable_entry), 0, 0, NULL); + if (!hashtable_entry_cachep) { + printk(KERN_ERR "sdcardfs: failed creating pkgl_hashtable entry slab cache\n"); + return -ENOMEM; + } + + return 0; +} + +void packagelist_exit(void) +{ + if (hashtable_entry_cachep) + kmem_cache_destroy(hashtable_entry_cachep); +} + + diff --git a/fs/sdcardfs/sdcardfs.h b/fs/sdcardfs/sdcardfs.h new file mode 100644 index 000000000000..90f8b24e4a52 --- /dev/null +++ b/fs/sdcardfs/sdcardfs.h @@ -0,0 +1,493 @@ +/* + * fs/sdcardfs/sdcardfs.h + * + * The sdcardfs v2.0 + * This file system replaces the sdcard daemon on Android + * On version 2.0, some of the daemon functions have been ported + * to support the multi-user concepts of Android 4.4 + * + * Copyright (c) 2013 Samsung Electronics Co. Ltd + * Authors: Daeho Jeong, Woojoong Lee, Seunghwan Hyun, + * Sunghwan Yun, Sungjong Seo + * + * This program has been developed as a stackable file system based on + * the WrapFS which written by + * + * Copyright (c) 1998-2011 Erez Zadok + * Copyright (c) 2009 Shrikar Archak + * Copyright (c) 2003-2011 Stony Brook University + * Copyright (c) 2003-2011 The Research Foundation of SUNY + * + * This file is dual licensed. It may be redistributed and/or modified + * under the terms of the Apache 2.0 License OR version 2 of the GNU + * General Public License. + */ + +#ifndef _SDCARDFS_H_ +#define _SDCARDFS_H_ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "multiuser.h" + +/* the file system name */ +#define SDCARDFS_NAME "sdcardfs" + +/* sdcardfs root inode number */ +#define SDCARDFS_ROOT_INO 1 + +/* useful for tracking code reachability */ +#define UDBG printk(KERN_DEFAULT "DBG:%s:%s:%d\n", __FILE__, __func__, __LINE__) + +#define SDCARDFS_DIRENT_SIZE 256 + +/* temporary static uid settings for development */ +#define AID_ROOT 0 /* uid for accessing /mnt/sdcard & extSdcard */ +#define AID_MEDIA_RW 1023 /* internal media storage write access */ + +#define AID_SDCARD_RW 1015 /* external storage write access */ +#define AID_SDCARD_R 1028 /* external storage read access */ +#define AID_SDCARD_PICS 1033 /* external storage photos access */ +#define AID_SDCARD_AV 1034 /* external storage audio/video access */ +#define AID_SDCARD_ALL 1035 /* access all users external storage */ + +#define AID_PACKAGE_INFO 1027 + +#define fix_derived_permission(x) \ + do { \ + (x)->i_uid = SDCARDFS_I(x)->d_uid; \ + (x)->i_gid = SDCARDFS_I(x)->d_gid; \ + (x)->i_mode = ((x)->i_mode & S_IFMT) | SDCARDFS_I(x)->d_mode;\ + } while (0) + +/* OVERRIDE_CRED() and REVERT_CRED() + * OVERRID_CRED() + * backup original task->cred + * and modifies task->cred->fsuid/fsgid to specified value. + * REVERT_CRED() + * restore original task->cred->fsuid/fsgid. + * These two macro should be used in pair, and OVERRIDE_CRED() should be + * placed at the beginning of a function, right after variable declaration. + */ +#define OVERRIDE_CRED(sdcardfs_sbi, saved_cred) \ + saved_cred = override_fsids(sdcardfs_sbi); \ + if (!saved_cred) { return -ENOMEM; } + +#define OVERRIDE_CRED_PTR(sdcardfs_sbi, saved_cred) \ + saved_cred = override_fsids(sdcardfs_sbi); \ + if (!saved_cred) { return ERR_PTR(-ENOMEM); } + +#define REVERT_CRED(saved_cred) revert_fsids(saved_cred) + +#define DEBUG_CRED() \ + printk("KAKJAGI: %s:%d fsuid %d fsgid %d\n", \ + __FUNCTION__, __LINE__, \ + (int)current->cred->fsuid, \ + (int)current->cred->fsgid); + +/* Android 4.4 support */ + +/* Permission mode for a specific node. Controls how file permissions + * are derived for children nodes. */ +typedef enum { + /* Nothing special; this node should just inherit from its parent. */ + PERM_INHERIT, + /* This node is one level above a normal root; used for legacy layouts + * which use the first level to represent user_id. */ + PERM_LEGACY_PRE_ROOT, + /* This node is "/" */ + PERM_ROOT, + /* This node is "/Android" */ + PERM_ANDROID, + /* This node is "/Android/data" */ + PERM_ANDROID_DATA, + /* This node is "/Android/obb" */ + PERM_ANDROID_OBB, + /* This node is "/Android/user" */ + PERM_ANDROID_USER, +} perm_t; + +/* Permissions structure to derive */ +typedef enum { + DERIVE_NONE, + DERIVE_LEGACY, + DERIVE_UNIFIED, +} derive_t; + +typedef enum { + LOWER_FS_EXT4, + LOWER_FS_FAT, +} lower_fs_t; + +struct sdcardfs_sb_info; +struct sdcardfs_mount_options; + +/* Do not directly use this function. Use OVERRIDE_CRED() instead. */ +const struct cred * override_fsids(struct sdcardfs_sb_info* sbi); +/* Do not directly use this function, use REVERT_CRED() instead. */ +void revert_fsids(const struct cred * old_cred); + +/* operations vectors defined in specific files */ +extern const struct file_operations sdcardfs_main_fops; +extern const struct file_operations sdcardfs_dir_fops; +extern const struct inode_operations sdcardfs_main_iops; +extern const struct inode_operations sdcardfs_dir_iops; +extern const struct inode_operations sdcardfs_symlink_iops; +extern const struct super_operations sdcardfs_sops; +extern const struct dentry_operations sdcardfs_ci_dops; +extern const struct address_space_operations sdcardfs_aops, sdcardfs_dummy_aops; +extern const struct vm_operations_struct sdcardfs_vm_ops; + +extern int sdcardfs_init_inode_cache(void); +extern void sdcardfs_destroy_inode_cache(void); +extern int sdcardfs_init_dentry_cache(void); +extern void sdcardfs_destroy_dentry_cache(void); +extern int new_dentry_private_data(struct dentry *dentry); +extern void free_dentry_private_data(struct dentry *dentry); +extern struct dentry *sdcardfs_lookup(struct inode *dir, struct dentry *dentry, + struct nameidata *nd); +extern int sdcardfs_interpose(struct dentry *dentry, struct super_block *sb, + struct path *lower_path); + +/* file private data */ +struct sdcardfs_file_info { + struct file *lower_file; + const struct vm_operations_struct *lower_vm_ops; +}; + +/* sdcardfs inode data in memory */ +struct sdcardfs_inode_info { + struct inode *lower_inode; + /* state derived based on current position in hierachy + * caution: d_mode does not include file types + */ + perm_t perm; + userid_t userid; + uid_t d_uid; + gid_t d_gid; + mode_t d_mode; + + struct inode vfs_inode; +}; + +/* sdcardfs dentry data in memory */ +struct sdcardfs_dentry_info { + spinlock_t lock; /* protects lower_path */ + struct path lower_path; + struct path orig_path; +}; + +struct sdcardfs_mount_options { + uid_t fs_low_uid; + gid_t fs_low_gid; + gid_t write_gid; + int split_perms; + derive_t derive; + lower_fs_t lower_fs; + unsigned int reserved_mb; +}; + +/* sdcardfs super-block data in memory */ +struct sdcardfs_sb_info { + struct super_block *lower_sb; + /* derived perm policy : some of options have been added + * to sdcardfs_mount_options (Android 4.4 support) */ + struct sdcardfs_mount_options options; + spinlock_t lock; /* protects obbpath */ + char *obbpath_s; + struct path obbpath; + void *pkgl_id; +}; + +/* + * inode to private data + * + * Since we use containers and the struct inode is _inside_ the + * sdcardfs_inode_info structure, SDCARDFS_I will always (given a non-NULL + * inode pointer), return a valid non-NULL pointer. + */ +static inline struct sdcardfs_inode_info *SDCARDFS_I(const struct inode *inode) +{ + return container_of(inode, struct sdcardfs_inode_info, vfs_inode); +} + +/* dentry to private data */ +#define SDCARDFS_D(dent) ((struct sdcardfs_dentry_info *)(dent)->d_fsdata) + +/* superblock to private data */ +#define SDCARDFS_SB(super) ((struct sdcardfs_sb_info *)(super)->s_fs_info) + +/* file to private Data */ +#define SDCARDFS_F(file) ((struct sdcardfs_file_info *)((file)->private_data)) + +/* file to lower file */ +static inline struct file *sdcardfs_lower_file(const struct file *f) +{ + return SDCARDFS_F(f)->lower_file; +} + +static inline void sdcardfs_set_lower_file(struct file *f, struct file *val) +{ + SDCARDFS_F(f)->lower_file = val; +} + +/* inode to lower inode. */ +static inline struct inode *sdcardfs_lower_inode(const struct inode *i) +{ + return SDCARDFS_I(i)->lower_inode; +} + +static inline void sdcardfs_set_lower_inode(struct inode *i, struct inode *val) +{ + SDCARDFS_I(i)->lower_inode = val; +} + +/* superblock to lower superblock */ +static inline struct super_block *sdcardfs_lower_super( + const struct super_block *sb) +{ + return SDCARDFS_SB(sb)->lower_sb; +} + +static inline void sdcardfs_set_lower_super(struct super_block *sb, + struct super_block *val) +{ + SDCARDFS_SB(sb)->lower_sb = val; +} + +/* path based (dentry/mnt) macros */ +static inline void pathcpy(struct path *dst, const struct path *src) +{ + dst->dentry = src->dentry; + dst->mnt = src->mnt; +} + +/* sdcardfs_get_pname functions calls path_get() + * therefore, the caller must call "proper" path_put functions + */ +#define SDCARDFS_DENT_FUNC(pname) \ +static inline void sdcardfs_get_##pname(const struct dentry *dent, \ + struct path *pname) \ +{ \ + spin_lock(&SDCARDFS_D(dent)->lock); \ + pathcpy(pname, &SDCARDFS_D(dent)->pname); \ + path_get(pname); \ + spin_unlock(&SDCARDFS_D(dent)->lock); \ + return; \ +} \ +static inline void sdcardfs_put_##pname(const struct dentry *dent, \ + struct path *pname) \ +{ \ + path_put(pname); \ + return; \ +} \ +static inline void sdcardfs_set_##pname(const struct dentry *dent, \ + struct path *pname) \ +{ \ + spin_lock(&SDCARDFS_D(dent)->lock); \ + pathcpy(&SDCARDFS_D(dent)->pname, pname); \ + spin_unlock(&SDCARDFS_D(dent)->lock); \ + return; \ +} \ +static inline void sdcardfs_reset_##pname(const struct dentry *dent) \ +{ \ + spin_lock(&SDCARDFS_D(dent)->lock); \ + SDCARDFS_D(dent)->pname.dentry = NULL; \ + SDCARDFS_D(dent)->pname.mnt = NULL; \ + spin_unlock(&SDCARDFS_D(dent)->lock); \ + return; \ +} \ +static inline void sdcardfs_put_reset_##pname(const struct dentry *dent) \ +{ \ + struct path pname; \ + spin_lock(&SDCARDFS_D(dent)->lock); \ + if(SDCARDFS_D(dent)->pname.dentry) { \ + pathcpy(&pname, &SDCARDFS_D(dent)->pname); \ + SDCARDFS_D(dent)->pname.dentry = NULL; \ + SDCARDFS_D(dent)->pname.mnt = NULL; \ + spin_unlock(&SDCARDFS_D(dent)->lock); \ + path_put(&pname); \ + } else \ + spin_unlock(&SDCARDFS_D(dent)->lock); \ + return; \ +} + +SDCARDFS_DENT_FUNC(lower_path) +SDCARDFS_DENT_FUNC(orig_path) + +static inline int has_graft_path(const struct dentry *dent) +{ + int ret = 0; + + spin_lock(&SDCARDFS_D(dent)->lock); + if (SDCARDFS_D(dent)->orig_path.dentry != NULL) + ret = 1; + spin_unlock(&SDCARDFS_D(dent)->lock); + + return ret; +} + +static inline void sdcardfs_get_real_lower(const struct dentry *dent, + struct path *real_lower) +{ + /* in case of a local obb dentry + * the orig_path should be returned + */ + if(has_graft_path(dent)) + sdcardfs_get_orig_path(dent, real_lower); + else + sdcardfs_get_lower_path(dent, real_lower); +} + +static inline void sdcardfs_put_real_lower(const struct dentry *dent, + struct path *real_lower) +{ + if(has_graft_path(dent)) + sdcardfs_put_orig_path(dent, real_lower); + else + sdcardfs_put_lower_path(dent, real_lower); +} + +/* for packagelist.c */ +extern int get_caller_has_rw_locked(void *pkgl_id, derive_t derive); +extern appid_t get_appid(void *pkgl_id, const char *app_name); +extern int check_caller_access_to_name(struct inode *parent_node, const char* name, + derive_t derive, int w_ok, int has_rw); +extern int open_flags_to_access_mode(int open_flags); +extern void * packagelist_create(gid_t write_gid); +extern void packagelist_destroy(void *pkgl_id); +extern int packagelist_init(void); +extern void packagelist_exit(void); + +/* for derived_perm.c */ +extern void setup_derived_state(struct inode *inode, perm_t perm, + userid_t userid, uid_t uid, gid_t gid, mode_t mode); +extern void get_derived_permission(struct dentry *parent, struct dentry *dentry); +extern void update_derived_permission(struct dentry *dentry); +extern int need_graft_path(struct dentry *dentry); +extern int is_base_obbpath(struct dentry *dentry); +extern int is_obbpath_invalid(struct dentry *dentry); +extern int setup_obb_dentry(struct dentry *dentry, struct path *lower_path); + +/* locking helpers */ +static inline struct dentry *lock_parent(struct dentry *dentry) +{ + struct dentry *dir = dget_parent(dentry); + mutex_lock_nested(&dir->d_inode->i_mutex, I_MUTEX_PARENT); + return dir; +} + +static inline void unlock_dir(struct dentry *dir) +{ + mutex_unlock(&dir->d_inode->i_mutex); + dput(dir); +} + +static inline int prepare_dir(const char *path_s, uid_t uid, gid_t gid, mode_t mode) +{ + int err; + struct dentry *dent; + struct iattr attrs; + struct nameidata nd; + + err = kern_path_parent(path_s, &nd); + if (err) { + if (err == -EEXIST) + err = 0; + goto out; + } + + dent = lookup_create(&nd, 1); + if (IS_ERR(dent)) { + err = PTR_ERR(dent); + if (err == -EEXIST) + err = 0; + goto out_unlock; + } + + err = vfs_mkdir(nd.path.dentry->d_inode, dent, mode); + if (err) { + if (err == -EEXIST) + err = 0; + goto out_dput; + } + + attrs.ia_uid = uid; + attrs.ia_gid = gid; + attrs.ia_valid = ATTR_UID | ATTR_GID; + mutex_lock(&dent->d_inode->i_mutex); + notify_change(dent, &attrs); + mutex_unlock(&dent->d_inode->i_mutex); + +out_dput: + dput(dent); + +out_unlock: + /* parent dentry locked by lookup_create */ + mutex_unlock(&nd.path.dentry->d_inode->i_mutex); + path_put(&nd.path); + +out: + return err; +} + +/* + * Return 1, if a disk has enough free space, otherwise 0. + * We assume that any files can not be overwritten. + */ +static inline int check_min_free_space(struct dentry *dentry, size_t size, int dir) +{ + int err; + struct path lower_path; + struct kstatfs statfs; + u64 avail; + struct sdcardfs_sb_info *sbi = SDCARDFS_SB(dentry->d_sb); + + if (sbi->options.reserved_mb) { + /* Get fs stat of lower filesystem. */ + sdcardfs_get_lower_path(dentry, &lower_path); + err = vfs_statfs(&lower_path, &statfs); + sdcardfs_put_lower_path(dentry, &lower_path); + + if (unlikely(err)) + return 0; + + /* Invalid statfs informations. */ + if (unlikely(statfs.f_bsize == 0)) + return 0; + + /* if you are checking directory, set size to f_bsize. */ + if (unlikely(dir)) + size = statfs.f_bsize; + + /* available size */ + avail = statfs.f_bavail * statfs.f_bsize; + + /* not enough space */ + if ((u64)size > avail) + return 0; + + /* enough space */ + if ((avail - size) > (sbi->options.reserved_mb * 1024 * 1024)) + return 1; + + return 0; + } else + return 1; +} + +#endif /* not _SDCARDFS_H_ */ diff --git a/fs/sdcardfs/strtok.h b/fs/sdcardfs/strtok.h new file mode 100644 index 000000000000..50ab25aa0bc4 --- /dev/null +++ b/fs/sdcardfs/strtok.h @@ -0,0 +1,75 @@ +/* + * fs/sdcardfs/strtok.h + * + * Copyright (c) 2013 Samsung Electronics Co. Ltd + * Authors: Daeho Jeong, Woojoong Lee, Seunghwan Hyun, + * Sunghwan Yun, Sungjong Seo + * + * This program has been developed as a stackable file system based on + * the WrapFS which written by + * + * Copyright (c) 1998-2011 Erez Zadok + * Copyright (c) 2009 Shrikar Archak + * Copyright (c) 2003-2011 Stony Brook University + * Copyright (c) 2003-2011 The Research Foundation of SUNY + * + * This file is dual licensed. It may be redistributed and/or modified + * under the terms of the Apache 2.0 License OR version 2 of the GNU + * General Public License. + */ + +static char * +strtok_r(char *s, const char *delim, char **last) +{ + char *spanp; + int c, sc; + char *tok; + + + /* if (s == NULL && (s = *last) == NULL) + return NULL; */ + if (s == NULL) { + s = *last; + if (s == NULL) + return NULL; + } + + /* + * Skip (span) leading delimiters (s += strspn(s, delim), sort of). + */ +cont: + c = *s++; + for (spanp = (char *)delim; (sc = *spanp++) != 0;) { + if (c == sc) + goto cont; + } + + if (c == 0) { /* no non-delimiter characters */ + *last = NULL; + return NULL; + } + tok = s - 1; + + /* + * Scan token (scan for delimiters: s += strcspn(s, delim), sort of). + * Note that delim must have one NUL; we stop if we see that, too. + */ + for (;;) { + c = *s++; + spanp = (char *)delim; + do { + sc = *spanp++; + if (sc == c) { + if (c == 0) + s = NULL; + else + s[-1] = 0; + *last = s; + return tok; + } + } while (sc != 0); + } + + /* NOTREACHED */ +} + diff --git a/fs/sdcardfs/super.c b/fs/sdcardfs/super.c new file mode 100644 index 000000000000..1d206c82dfdf --- /dev/null +++ b/fs/sdcardfs/super.c @@ -0,0 +1,229 @@ +/* + * fs/sdcardfs/super.c + * + * Copyright (c) 2013 Samsung Electronics Co. Ltd + * Authors: Daeho Jeong, Woojoong Lee, Seunghwan Hyun, + * Sunghwan Yun, Sungjong Seo + * + * This program has been developed as a stackable file system based on + * the WrapFS which written by + * + * Copyright (c) 1998-2011 Erez Zadok + * Copyright (c) 2009 Shrikar Archak + * Copyright (c) 2003-2011 Stony Brook University + * Copyright (c) 2003-2011 The Research Foundation of SUNY + * + * This file is dual licensed. It may be redistributed and/or modified + * under the terms of the Apache 2.0 License OR version 2 of the GNU + * General Public License. + */ + +#include "sdcardfs.h" + +/* + * The inode cache is used with alloc_inode for both our inode info and the + * vfs inode. + */ +static struct kmem_cache *sdcardfs_inode_cachep; + +/* final actions when unmounting a file system */ +static void sdcardfs_put_super(struct super_block *sb) +{ + struct sdcardfs_sb_info *spd; + struct super_block *s; + + spd = SDCARDFS_SB(sb); + if (!spd) + return; + + if(spd->obbpath_s) { + kfree(spd->obbpath_s); + path_put(&spd->obbpath); + } + + /* decrement lower super references */ + s = sdcardfs_lower_super(sb); + sdcardfs_set_lower_super(sb, NULL); + atomic_dec(&s->s_active); + + if(spd->pkgl_id) + packagelist_destroy(spd->pkgl_id); + + kfree(spd); + sb->s_fs_info = NULL; +} + +static int sdcardfs_statfs(struct dentry *dentry, struct kstatfs *buf) +{ + int err; + struct path lower_path; + u32 min_blocks; + struct sdcardfs_sb_info *sbi = SDCARDFS_SB(dentry->d_sb); + + sdcardfs_get_lower_path(dentry, &lower_path); + err = vfs_statfs(&lower_path, buf); + sdcardfs_put_lower_path(dentry, &lower_path); + + if (sbi->options.reserved_mb) { + /* Invalid statfs informations. */ + if (buf->f_bsize == 0) { + printk(KERN_ERR "Returned block size is zero.\n"); + return -EINVAL; + } + + min_blocks = ((sbi->options.reserved_mb * 1024 * 1024)/buf->f_bsize); + buf->f_blocks -= min_blocks; + + if (buf->f_bavail > min_blocks) + buf->f_bavail -= min_blocks; + else + buf->f_bavail = 0; + + /* Make reserved blocks invisiable to media storage */ + buf->f_bfree = buf->f_bavail; + } + + /* set return buf to our f/s to avoid confusing user-level utils */ + buf->f_type = SDCARDFS_SUPER_MAGIC; + + return err; +} + +/* + * @flags: numeric mount options + * @options: mount options string + */ +static int sdcardfs_remount_fs(struct super_block *sb, int *flags, char *options) +{ + int err = 0; + + /* + * The VFS will take care of "ro" and "rw" flags among others. We + * can safely accept a few flags (RDONLY, MANDLOCK), and honor + * SILENT, but anything else left over is an error. + */ + if ((*flags & ~(MS_RDONLY | MS_MANDLOCK | MS_SILENT)) != 0) { + printk(KERN_ERR + "sdcardfs: remount flags 0x%x unsupported\n", *flags); + err = -EINVAL; + } + + return err; +} + +/* + * Called by iput() when the inode reference count reached zero + * and the inode is not hashed anywhere. Used to clear anything + * that needs to be, before the inode is completely destroyed and put + * on the inode free list. + */ +static void sdcardfs_evict_inode(struct inode *inode) +{ + struct inode *lower_inode; + + truncate_inode_pages(&inode->i_data, 0); + end_writeback(inode); + /* + * Decrement a reference to a lower_inode, which was incremented + * by our read_inode when it was created initially. + */ + lower_inode = sdcardfs_lower_inode(inode); + sdcardfs_set_lower_inode(inode, NULL); + iput(lower_inode); +} + +static struct inode *sdcardfs_alloc_inode(struct super_block *sb) +{ + struct sdcardfs_inode_info *i; + + i = kmem_cache_alloc(sdcardfs_inode_cachep, GFP_KERNEL); + if (!i) + return NULL; + + /* memset everything up to the inode to 0 */ + memset(i, 0, offsetof(struct sdcardfs_inode_info, vfs_inode)); + + i->vfs_inode.i_version = 1; + return &i->vfs_inode; +} + +static void sdcardfs_destroy_inode(struct inode *inode) +{ + kmem_cache_free(sdcardfs_inode_cachep, SDCARDFS_I(inode)); +} + +/* sdcardfs inode cache constructor */ +static void init_once(void *obj) +{ + struct sdcardfs_inode_info *i = obj; + + inode_init_once(&i->vfs_inode); +} + +int sdcardfs_init_inode_cache(void) +{ + int err = 0; + + sdcardfs_inode_cachep = + kmem_cache_create("sdcardfs_inode_cache", + sizeof(struct sdcardfs_inode_info), 0, + SLAB_RECLAIM_ACCOUNT, init_once); + if (!sdcardfs_inode_cachep) + err = -ENOMEM; + return err; +} + +/* sdcardfs inode cache destructor */ +void sdcardfs_destroy_inode_cache(void) +{ + if (sdcardfs_inode_cachep) + kmem_cache_destroy(sdcardfs_inode_cachep); +} + +/* + * Used only in nfs, to kill any pending RPC tasks, so that subsequent + * code can actually succeed and won't leave tasks that need handling. + */ +static void sdcardfs_umount_begin(struct super_block *sb) +{ + struct super_block *lower_sb; + + lower_sb = sdcardfs_lower_super(sb); + if (lower_sb && lower_sb->s_op && lower_sb->s_op->umount_begin) + lower_sb->s_op->umount_begin(lower_sb); +} + +static int sdcardfs_show_options(struct seq_file *m, struct vfsmount *mnt) +{ + struct sdcardfs_sb_info *sbi = SDCARDFS_SB(mnt->mnt_sb); + struct sdcardfs_mount_options *opts = &sbi->options; + + if (opts->fs_low_uid != 0) + seq_printf(m, ",uid=%u", opts->fs_low_uid); + if (opts->fs_low_gid != 0) + seq_printf(m, ",gid=%u", opts->fs_low_gid); + + if (opts->derive == DERIVE_NONE) + seq_printf(m, ",derive=none"); + else if (opts->derive == DERIVE_LEGACY) + seq_printf(m, ",derive=legacy"); + else if (opts->derive == DERIVE_UNIFIED) + seq_printf(m, ",derive=unified"); + + if (opts->reserved_mb != 0) + seq_printf(m, ",reserved=%uMB", opts->reserved_mb); + + return 0; +}; + +const struct super_operations sdcardfs_sops = { + .put_super = sdcardfs_put_super, + .statfs = sdcardfs_statfs, + .remount_fs = sdcardfs_remount_fs, + .evict_inode = sdcardfs_evict_inode, + .umount_begin = sdcardfs_umount_begin, + .show_options = sdcardfs_show_options, + .alloc_inode = sdcardfs_alloc_inode, + .destroy_inode = sdcardfs_destroy_inode, + .drop_inode = generic_delete_inode, +}; diff --git a/include/uapi/linux/magic.h b/include/uapi/linux/magic.h index 1a6fee974116..6f7b217c44ac 100644 --- a/include/uapi/linux/magic.h +++ b/include/uapi/linux/magic.h @@ -56,6 +56,8 @@ #define REISER2FS_SUPER_MAGIC_STRING "ReIsEr2Fs" #define REISER2FS_JR_SUPER_MAGIC_STRING "ReIsEr3Fs" +#define SDCARDFS_SUPER_MAGIC 0xb550ca10 + #define SMB_SUPER_MAGIC 0x517B #define CGROUP_SUPER_MAGIC 0x27e0eb #define CGROUP2_SUPER_MAGIC 0x63677270 From dbe3bbf776c9e06d81bc0ac7e13c1ae546088d37 Mon Sep 17 00:00:00 2001 From: Daniel Rosenberg Date: Thu, 11 Feb 2016 16:44:15 -0800 Subject: [PATCH 0284/1103] ANDROID: vfs: add d_canonical_path for stacked filesystem support Inotify does not currently know when a filesystem is acting as a wrapper around another fs. This means that inotify watchers will miss any modifications to the base file, as well as any made in a separate stacked fs that points to the same file. d_canonical_path solves this problem by allowing the fs to map a dentry to a path in the lower fs. Inotify can use it to find the appropriate place to watch to be informed of all changes to a file. Change-Id: I09563baffad1711a045e45c1bd0bd8713c2cc0b6 Signed-off-by: Daniel Rosenberg --- fs/notify/inotify/inotify_user.c | 15 +++++++++++++-- include/linux/dcache.h | 1 + 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/fs/notify/inotify/inotify_user.c b/fs/notify/inotify/inotify_user.c index ac6978d3208c..15f5655df87d 100644 --- a/fs/notify/inotify/inotify_user.c +++ b/fs/notify/inotify/inotify_user.c @@ -699,6 +699,8 @@ SYSCALL_DEFINE3(inotify_add_watch, int, fd, const char __user *, pathname, struct fsnotify_group *group; struct inode *inode; struct path path; + struct path alteredpath; + struct path *canonical_path = &path; struct fd f; int ret; unsigned flags = 0; @@ -742,13 +744,22 @@ SYSCALL_DEFINE3(inotify_add_watch, int, fd, const char __user *, pathname, if (ret) goto fput_and_out; + /* support stacked filesystems */ + if(path.dentry && path.dentry->d_op) { + if (path.dentry->d_op->d_canonical_path) { + path.dentry->d_op->d_canonical_path(&path, &alteredpath); + canonical_path = &alteredpath; + path_put(&path); + } + } + /* inode held in place by reference to path; group by fget on fd */ - inode = path.dentry->d_inode; + inode = canonical_path->dentry->d_inode; group = f.file->private_data; /* create/update an inode mark */ ret = inotify_update_watch(group, inode, mask); - path_put(&path); + path_put(canonical_path); fput_and_out: fdput(f); return ret; diff --git a/include/linux/dcache.h b/include/linux/dcache.h index ef4b70f64f33..2dc6915d7abf 100644 --- a/include/linux/dcache.h +++ b/include/linux/dcache.h @@ -146,6 +146,7 @@ struct dentry_operations { struct vfsmount *(*d_automount)(struct path *); int (*d_manage)(const struct path *, bool); struct dentry *(*d_real)(struct dentry *, const struct inode *); + void (*d_canonical_path)(const struct path *, struct path *); } ____cacheline_aligned; /* From c13c7e491e62b6a87e26788017b8e46d4214822f Mon Sep 17 00:00:00 2001 From: Daniel Rosenberg Date: Wed, 26 Oct 2016 15:29:51 -0700 Subject: [PATCH 0285/1103] ANDROID: mnt: Add filesystem private data to mount points This starts to add private data associated directly to mount points. The intent is to give filesystems a sense of where they have come from, as a means of letting a filesystem take different actions based on this information. Change-Id: Ie769d7b3bb2f5972afe05c1bf16cf88c91647ab2 Signed-off-by: Daniel Rosenberg --- fs/namespace.c | 26 +++++++++++++++++++++++++- fs/pnode.c | 29 +++++++++++++++++++++++++++++ fs/pnode.h | 1 + include/linux/fs.h | 3 +++ include/linux/mount.h | 1 + 5 files changed, 59 insertions(+), 1 deletion(-) diff --git a/fs/namespace.c b/fs/namespace.c index 99186556f8d3..cd4fb0e7e241 100644 --- a/fs/namespace.c +++ b/fs/namespace.c @@ -200,6 +200,7 @@ static struct mount *alloc_vfsmnt(const char *name) mnt->mnt_count = 1; mnt->mnt_writers = 0; #endif + mnt->mnt.data = NULL; INIT_HLIST_NODE(&mnt->mnt_hash); INIT_LIST_HEAD(&mnt->mnt_child); @@ -552,6 +553,7 @@ int sb_prepare_remount_readonly(struct super_block *sb) static void free_vfsmnt(struct mount *mnt) { + kfree(mnt->mnt.data); kfree_const(mnt->mnt_devname); #ifdef CONFIG_SMP free_percpu(mnt->mnt_pcp); @@ -955,6 +957,14 @@ vfs_kern_mount(struct file_system_type *type, int flags, const char *name, void if (!mnt) return ERR_PTR(-ENOMEM); + if (type->alloc_mnt_data) { + mnt->mnt.data = type->alloc_mnt_data(); + if (!mnt->mnt.data) { + mnt_free_id(mnt); + free_vfsmnt(mnt); + return ERR_PTR(-ENOMEM); + } + } if (flags & SB_KERNMOUNT) mnt->mnt.mnt_flags = MNT_INTERNAL; @@ -1002,6 +1012,14 @@ static struct mount *clone_mnt(struct mount *old, struct dentry *root, if (!mnt) return ERR_PTR(-ENOMEM); + if (sb->s_op->clone_mnt_data) { + mnt->mnt.data = sb->s_op->clone_mnt_data(old->mnt.data); + if (!mnt->mnt.data) { + err = -ENOMEM; + goto out_free; + } + } + if (flag & (CL_SLAVE | CL_PRIVATE | CL_SHARED_TO_SLAVE)) mnt->mnt_group_id = 0; /* not a peer of original */ else @@ -2274,8 +2292,14 @@ static int do_remount(struct path *path, int ms_flags, int sb_flags, err = change_mount_flags(path->mnt, ms_flags); else if (!ns_capable(sb->s_user_ns, CAP_SYS_ADMIN)) err = -EPERM; - else + else { err = do_remount_sb(sb, sb_flags, data, 0); + namespace_lock(); + lock_mount_hash(); + propagate_remount(mnt); + unlock_mount_hash(); + namespace_unlock(); + } if (!err) { lock_mount_hash(); mnt_flags |= mnt->mnt.mnt_flags & ~MNT_USER_SETTABLE_MASK; diff --git a/fs/pnode.c b/fs/pnode.c index 53d411a371ce..386884dd6d97 100644 --- a/fs/pnode.c +++ b/fs/pnode.c @@ -607,3 +607,32 @@ int propagate_umount(struct list_head *list) return 0; } + +/* + * Iterates over all slaves, and slaves of slaves. + */ +static struct mount *next_descendent(struct mount *root, struct mount *cur) +{ + if (!IS_MNT_NEW(cur) && !list_empty(&cur->mnt_slave_list)) + return first_slave(cur); + do { + if (cur->mnt_slave.next != &cur->mnt_master->mnt_slave_list) + return next_slave(cur); + cur = cur->mnt_master; + } while (cur != root); + return NULL; +} + +void propagate_remount(struct mount *mnt) +{ + struct mount *m = mnt; + struct super_block *sb = mnt->mnt.mnt_sb; + + if (sb->s_op->copy_mnt_data) { + m = next_descendent(mnt, m); + while (m) { + sb->s_op->copy_mnt_data(m->mnt.data, mnt->mnt.data); + m = next_descendent(mnt, m); + } + } +} diff --git a/fs/pnode.h b/fs/pnode.h index dc87e65becd2..a9a6576540ad 100644 --- a/fs/pnode.h +++ b/fs/pnode.h @@ -44,6 +44,7 @@ int propagate_mnt(struct mount *, struct mountpoint *, struct mount *, int propagate_umount(struct list_head *); int propagate_mount_busy(struct mount *, int); void propagate_mount_unlock(struct mount *); +void propagate_remount(struct mount *); void mnt_release_group_id(struct mount *); int get_dominating_id(struct mount *mnt, const struct path *root); unsigned int mnt_get_count(struct mount *mnt); diff --git a/include/linux/fs.h b/include/linux/fs.h index 897eae8faee1..e30ab9dcaab4 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -1858,6 +1858,8 @@ struct super_operations { int (*unfreeze_fs) (struct super_block *); int (*statfs) (struct dentry *, struct kstatfs *); int (*remount_fs) (struct super_block *, int *, char *); + void *(*clone_mnt_data) (void *); + void (*copy_mnt_data) (void *, void *); void (*umount_begin) (struct super_block *); int (*show_options)(struct seq_file *, struct dentry *); @@ -2120,6 +2122,7 @@ struct file_system_type { #define FS_RENAME_DOES_D_MOVE 32768 /* FS will handle d_move() during rename() internally. */ struct dentry *(*mount) (struct file_system_type *, int, const char *, void *); + void *(*alloc_mnt_data) (void); void (*kill_sb) (struct super_block *); struct module *owner; struct file_system_type * next; diff --git a/include/linux/mount.h b/include/linux/mount.h index 45b1f56c6c2f..1ff21c19b0b9 100644 --- a/include/linux/mount.h +++ b/include/linux/mount.h @@ -68,6 +68,7 @@ struct vfsmount { struct dentry *mnt_root; /* root of the mounted tree */ struct super_block *mnt_sb; /* pointer to superblock */ int mnt_flags; + void *data; } __randomize_layout; struct file; /* forward dec */ From 6c46ea8eef192e76a59f9495fbc571e6127a1c05 Mon Sep 17 00:00:00 2001 From: Daniel Rosenberg Date: Fri, 22 Apr 2016 00:00:48 -0700 Subject: [PATCH 0286/1103] ANDROID: fuse: Add support for d_canonical_path Allows FUSE to report to inotify that it is acting as a layered filesystem. The userspace component returns a string representing the location of the underlying file. If the string cannot be resolved into a path, the top level path is returned instead. bug: 23904372 Change-Id: Iabdca0bbedfbff59e9c820c58636a68ef9683d9f Signed-off-by: Daniel Rosenberg --- fs/fuse/dev.c | 5 +++++ fs/fuse/dir.c | 46 +++++++++++++++++++++++++++++++++++++++ fs/fuse/fuse_i.h | 3 +++ include/uapi/linux/fuse.h | 1 + 4 files changed, 55 insertions(+) diff --git a/fs/fuse/dev.c b/fs/fuse/dev.c index 11ea2c4a38ab..9fc81f1f0e7e 100644 --- a/fs/fuse/dev.c +++ b/fs/fuse/dev.c @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -1900,6 +1901,10 @@ static ssize_t fuse_dev_do_write(struct fuse_dev *fud, cs->move_pages = 0; err = copy_out_args(cs, &req->out, nbytes); + if (req->in.h.opcode == FUSE_CANONICAL_PATH) { + req->out.h.error = kern_path((char *)req->out.args[0].value, 0, + req->canonical_path); + } fuse_copy_finish(cs); spin_lock(&fpq->lock); diff --git a/fs/fuse/dir.c b/fs/fuse/dir.c index 0979609d6eba..6c407b930e36 100644 --- a/fs/fuse/dir.c +++ b/fs/fuse/dir.c @@ -262,6 +262,50 @@ static int fuse_dentry_revalidate(struct dentry *entry, unsigned int flags) goto out; } +/* + * Get the canonical path. Since we must translate to a path, this must be done + * in the context of the userspace daemon, however, the userspace daemon cannot + * look up paths on its own. Instead, we handle the lookup as a special case + * inside of the write request. + */ +static void fuse_dentry_canonical_path(const struct path *path, struct path *canonical_path) { + struct inode *inode = path->dentry->d_inode; + struct fuse_conn *fc = get_fuse_conn(inode); + struct fuse_req *req; + int err; + char *path_name; + + req = fuse_get_req(fc, 1); + err = PTR_ERR(req); + if (IS_ERR(req)) + goto default_path; + + path_name = (char*)__get_free_page(GFP_KERNEL); + if (!path_name) { + fuse_put_request(fc, req); + goto default_path; + } + + req->in.h.opcode = FUSE_CANONICAL_PATH; + req->in.h.nodeid = get_node_id(inode); + req->in.numargs = 0; + req->out.numargs = 1; + req->out.args[0].size = PATH_MAX; + req->out.args[0].value = path_name; + req->canonical_path = canonical_path; + req->out.argvar = 1; + fuse_request_send(fc, req); + err = req->out.h.error; + fuse_put_request(fc, req); + free_page((unsigned long)path_name); + if (!err) + return; +default_path: + canonical_path->dentry = path->dentry; + canonical_path->mnt = path->mnt; + path_get(canonical_path); +} + static int invalid_nodeid(u64 nodeid) { return !nodeid || nodeid == FUSE_ROOT_ID; @@ -284,11 +328,13 @@ const struct dentry_operations fuse_dentry_operations = { .d_revalidate = fuse_dentry_revalidate, .d_init = fuse_dentry_init, .d_release = fuse_dentry_release, + .d_canonical_path = fuse_dentry_canonical_path, }; const struct dentry_operations fuse_root_dentry_operations = { .d_init = fuse_dentry_init, .d_release = fuse_dentry_release, + .d_canonical_path = fuse_dentry_canonical_path, }; int fuse_valid_type(int m) diff --git a/fs/fuse/fuse_i.h b/fs/fuse/fuse_i.h index f78e9614bb5f..a110175e0075 100644 --- a/fs/fuse/fuse_i.h +++ b/fs/fuse/fuse_i.h @@ -371,6 +371,9 @@ struct fuse_req { /** Inode used in the request or NULL */ struct inode *inode; + /** Path used for completing d_canonical_path */ + struct path *canonical_path; + /** AIO control block */ struct fuse_io_priv *io; diff --git a/include/uapi/linux/fuse.h b/include/uapi/linux/fuse.h index 92fa24c24c92..fbb318d7dc89 100644 --- a/include/uapi/linux/fuse.h +++ b/include/uapi/linux/fuse.h @@ -381,6 +381,7 @@ enum fuse_opcode { FUSE_READDIRPLUS = 44, FUSE_RENAME2 = 45, FUSE_LSEEK = 46, + FUSE_CANONICAL_PATH= 2016, /* CUSE specific operations */ CUSE_INIT = 4096, From d58954103a5fcdc5773d1e0472a1266d6e3dfa88 Mon Sep 17 00:00:00 2001 From: Daniel Rosenberg Date: Wed, 26 Oct 2016 15:58:22 -0700 Subject: [PATCH 0287/1103] ANDROID: vfs: Allow filesystems to access their private mount data Now we pass the vfsmount when mounting and remounting. This allows the filesystem to actually set up the mount specific data, although we can't quite do anything with it yet. show_options is expanded to include data that lives with the mount. To avoid changing existing filesystems, these have been added as new vfs functions. Change-Id: If80670bfad9f287abb8ac22457e1b034c9697097 Signed-off-by: Daniel Rosenberg --- fs/internal.h | 4 +++- fs/namespace.c | 4 ++-- fs/proc_namespace.c | 8 ++++++-- fs/super.c | 28 +++++++++++++++++++++++----- include/linux/fs.h | 4 ++++ 5 files changed, 38 insertions(+), 10 deletions(-) diff --git a/fs/internal.h b/fs/internal.h index d410186bc369..a1ac9427243f 100644 --- a/fs/internal.h +++ b/fs/internal.h @@ -100,9 +100,11 @@ extern struct file *alloc_empty_file_noaccount(int, const struct cred *); * super.c */ extern int do_remount_sb(struct super_block *, int, void *, int); +extern int do_remount_sb2(struct vfsmount *, struct super_block *, int, + void *, int); extern bool trylock_super(struct super_block *sb); extern struct dentry *mount_fs(struct file_system_type *, - int, const char *, void *); + int, const char *, struct vfsmount *, void *); extern struct super_block *user_get_super(dev_t); /* diff --git a/fs/namespace.c b/fs/namespace.c index cd4fb0e7e241..b4976060aed4 100644 --- a/fs/namespace.c +++ b/fs/namespace.c @@ -968,7 +968,7 @@ vfs_kern_mount(struct file_system_type *type, int flags, const char *name, void if (flags & SB_KERNMOUNT) mnt->mnt.mnt_flags = MNT_INTERNAL; - root = mount_fs(type, flags, name, data); + root = mount_fs(type, flags, name, &mnt->mnt, data); if (IS_ERR(root)) { mnt_free_id(mnt); free_vfsmnt(mnt); @@ -2293,7 +2293,7 @@ static int do_remount(struct path *path, int ms_flags, int sb_flags, else if (!ns_capable(sb->s_user_ns, CAP_SYS_ADMIN)) err = -EPERM; else { - err = do_remount_sb(sb, sb_flags, data, 0); + err = do_remount_sb2(path->mnt, sb, sb_flags, data, 0); namespace_lock(); lock_mount_hash(); propagate_remount(mnt); diff --git a/fs/proc_namespace.c b/fs/proc_namespace.c index e16fb8f2049e..bd07f0f4f06b 100644 --- a/fs/proc_namespace.c +++ b/fs/proc_namespace.c @@ -121,7 +121,9 @@ static int show_vfsmnt(struct seq_file *m, struct vfsmount *mnt) if (err) goto out; show_mnt_opts(m, mnt); - if (sb->s_op->show_options) + if (sb->s_op->show_options2) + err = sb->s_op->show_options2(mnt, m, mnt_path.dentry); + else if (sb->s_op->show_options) err = sb->s_op->show_options(m, mnt_path.dentry); seq_puts(m, " 0 0\n"); out: @@ -183,7 +185,9 @@ static int show_mountinfo(struct seq_file *m, struct vfsmount *mnt) err = show_sb_opts(m, sb); if (err) goto out; - if (sb->s_op->show_options) + if (sb->s_op->show_options2) { + err = sb->s_op->show_options2(mnt, m, mnt->mnt_root); + } else if (sb->s_op->show_options) err = sb->s_op->show_options(m, mnt->mnt_root); seq_putc(m, '\n'); out: diff --git a/fs/super.c b/fs/super.c index 04178a266607..b02e08652122 100644 --- a/fs/super.c +++ b/fs/super.c @@ -834,7 +834,8 @@ struct super_block *user_get_super(dev_t dev) } /** - * do_remount_sb - asks filesystem to change mount options. + * do_remount_sb2 - asks filesystem to change mount options. + * @mnt: mount we are looking at * @sb: superblock in question * @sb_flags: revised superblock flags * @data: the rest of options @@ -842,7 +843,7 @@ struct super_block *user_get_super(dev_t dev) * * Alters the mount options of a mounted file system. */ -int do_remount_sb(struct super_block *sb, int sb_flags, void *data, int force) +int do_remount_sb2(struct vfsmount *mnt, struct super_block *sb, int sb_flags, void *data, int force) { int retval; int remount_ro; @@ -884,7 +885,16 @@ int do_remount_sb(struct super_block *sb, int sb_flags, void *data, int force) } } - if (sb->s_op->remount_fs) { + if (mnt && sb->s_op->remount_fs2) { + retval = sb->s_op->remount_fs2(mnt, sb, &sb_flags, data); + if (retval) { + if (!force) + goto cancel_readonly; + /* If forced remount, go ahead despite any errors */ + WARN(1, "forced remount of a %s fs returned %i\n", + sb->s_type->name, retval); + } + } else if (sb->s_op->remount_fs) { retval = sb->s_op->remount_fs(sb, &sb_flags, data); if (retval) { if (!force) @@ -916,6 +926,11 @@ int do_remount_sb(struct super_block *sb, int sb_flags, void *data, int force) return retval; } +int do_remount_sb(struct super_block *sb, int flags, void *data, int force) +{ + return do_remount_sb2(NULL, sb, flags, data, force); +} + static void do_emergency_remount_callback(struct super_block *sb) { down_write(&sb->s_umount); @@ -1241,7 +1256,7 @@ struct dentry *mount_single(struct file_system_type *fs_type, EXPORT_SYMBOL(mount_single); struct dentry * -mount_fs(struct file_system_type *type, int flags, const char *name, void *data) +mount_fs(struct file_system_type *type, int flags, const char *name, struct vfsmount *mnt, void *data) { struct dentry *root; struct super_block *sb; @@ -1258,7 +1273,10 @@ mount_fs(struct file_system_type *type, int flags, const char *name, void *data) goto out_free_secdata; } - root = type->mount(type, flags, name, data); + if (type->mount2) + root = type->mount2(mnt, type, flags, name, data); + else + root = type->mount(type, flags, name, data); if (IS_ERR(root)) { error = PTR_ERR(root); goto out_free_secdata; diff --git a/include/linux/fs.h b/include/linux/fs.h index e30ab9dcaab4..e013cfb206d8 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -1858,11 +1858,13 @@ struct super_operations { int (*unfreeze_fs) (struct super_block *); int (*statfs) (struct dentry *, struct kstatfs *); int (*remount_fs) (struct super_block *, int *, char *); + int (*remount_fs2) (struct vfsmount *, struct super_block *, int *, char *); void *(*clone_mnt_data) (void *); void (*copy_mnt_data) (void *, void *); void (*umount_begin) (struct super_block *); int (*show_options)(struct seq_file *, struct dentry *); + int (*show_options2)(struct vfsmount *,struct seq_file *, struct dentry *); int (*show_devname)(struct seq_file *, struct dentry *); int (*show_path)(struct seq_file *, struct dentry *); int (*show_stats)(struct seq_file *, struct dentry *); @@ -2122,6 +2124,8 @@ struct file_system_type { #define FS_RENAME_DOES_D_MOVE 32768 /* FS will handle d_move() during rename() internally. */ struct dentry *(*mount) (struct file_system_type *, int, const char *, void *); + struct dentry *(*mount2) (struct vfsmount *, struct file_system_type *, int, + const char *, void *); void *(*alloc_mnt_data) (void); void (*kill_sb) (struct super_block *); struct module *owner; From dd22c7c76260f29f0ea1884bcc621d7bfbecd958 Mon Sep 17 00:00:00 2001 From: Daniel Rosenberg Date: Wed, 26 Oct 2016 16:33:11 -0700 Subject: [PATCH 0288/1103] ANDROID: vfs: Add setattr2 for filesystems with per mount permissions This allows filesystems to use their mount private data to influence the permssions they use in setattr2. It has been separated into a new call to avoid disrupting current setattr users. Change-Id: I19959038309284448f1b7f232d579674ef546385 Signed-off-by: Daniel Rosenberg --- fs/attr.c | 12 ++++++++++-- fs/coredump.c | 2 +- fs/inode.c | 6 +++--- fs/namei.c | 2 +- fs/open.c | 21 ++++++++++++++------- fs/utimes.c | 2 +- include/linux/fs.h | 6 +++++- 7 files changed, 35 insertions(+), 16 deletions(-) diff --git a/fs/attr.c b/fs/attr.c index d22e8187477f..e55ed6e817d3 100644 --- a/fs/attr.c +++ b/fs/attr.c @@ -223,7 +223,7 @@ EXPORT_SYMBOL(setattr_copy); * the file open for write, as there can be no conflicting delegation in * that case. */ -int notify_change(struct dentry * dentry, struct iattr * attr, struct inode **delegated_inode) +int notify_change2(struct vfsmount *mnt, struct dentry * dentry, struct iattr * attr, struct inode **delegated_inode) { struct inode *inode = dentry->d_inode; umode_t mode = inode->i_mode; @@ -330,7 +330,9 @@ int notify_change(struct dentry * dentry, struct iattr * attr, struct inode **de if (error) return error; - if (inode->i_op->setattr) + if (mnt && inode->i_op->setattr2) + error = inode->i_op->setattr2(mnt, dentry, attr); + else if (inode->i_op->setattr) error = inode->i_op->setattr(dentry, attr); else error = simple_setattr(dentry, attr); @@ -343,4 +345,10 @@ int notify_change(struct dentry * dentry, struct iattr * attr, struct inode **de return error; } +EXPORT_SYMBOL(notify_change2); + +int notify_change(struct dentry * dentry, struct iattr * attr, struct inode **delegated_inode) +{ + return notify_change2(NULL, dentry, attr, delegated_inode); +} EXPORT_SYMBOL(notify_change); diff --git a/fs/coredump.c b/fs/coredump.c index 1e2c87acac9b..d9b3ba086d92 100644 --- a/fs/coredump.c +++ b/fs/coredump.c @@ -742,7 +742,7 @@ void do_coredump(const siginfo_t *siginfo) goto close_fail; if (!(cprm.file->f_mode & FMODE_CAN_WRITE)) goto close_fail; - if (do_truncate(cprm.file->f_path.dentry, 0, 0, cprm.file)) + if (do_truncate2(cprm.file->f_path.mnt, cprm.file->f_path.dentry, 0, 0, cprm.file)) goto close_fail; } diff --git a/fs/inode.c b/fs/inode.c index 42f6d25f32a5..790552ed0c62 100644 --- a/fs/inode.c +++ b/fs/inode.c @@ -1794,7 +1794,7 @@ int dentry_needs_remove_privs(struct dentry *dentry) return mask; } -static int __remove_privs(struct dentry *dentry, int kill) +static int __remove_privs(struct vfsmount *mnt, struct dentry *dentry, int kill) { struct iattr newattrs; @@ -1803,7 +1803,7 @@ static int __remove_privs(struct dentry *dentry, int kill) * Note we call this on write, so notify_change will not * encounter any conflicting delegations: */ - return notify_change(dentry, &newattrs, NULL); + return notify_change2(mnt, dentry, &newattrs, NULL); } /* @@ -1825,7 +1825,7 @@ int file_remove_privs(struct file *file) if (kill < 0) return kill; if (kill) - error = __remove_privs(dentry, kill); + error = __remove_privs(file->f_path.mnt, dentry, kill); if (!error) inode_has_no_xattr(inode); diff --git a/fs/namei.c b/fs/namei.c index 0cab6494978c..be445b3c28a0 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -3005,7 +3005,7 @@ static int handle_truncate(struct file *filp) if (!error) error = security_path_truncate(path); if (!error) { - error = do_truncate(path->dentry, 0, + error = do_truncate2(path->mnt, path->dentry, 0, ATTR_MTIME|ATTR_CTIME|ATTR_OPEN, filp); } diff --git a/fs/open.c b/fs/open.c index 0285ce7dbd51..a642ec16944b 100644 --- a/fs/open.c +++ b/fs/open.c @@ -34,8 +34,8 @@ #include "internal.h" -int do_truncate(struct dentry *dentry, loff_t length, unsigned int time_attrs, - struct file *filp) +int do_truncate2(struct vfsmount *mnt, struct dentry *dentry, loff_t length, + unsigned int time_attrs, struct file *filp) { int ret; struct iattr newattrs; @@ -60,10 +60,15 @@ int do_truncate(struct dentry *dentry, loff_t length, unsigned int time_attrs, inode_lock(dentry->d_inode); /* Note any delegations or leases have already been broken: */ - ret = notify_change(dentry, &newattrs, NULL); + ret = notify_change2(mnt, dentry, &newattrs, NULL); inode_unlock(dentry->d_inode); return ret; } +int do_truncate(struct dentry *dentry, loff_t length, unsigned int time_attrs, + struct file *filp) +{ + return do_truncate2(NULL, dentry, length, time_attrs, filp); +} long vfs_truncate(const struct path *path, loff_t length) { @@ -106,7 +111,7 @@ long vfs_truncate(const struct path *path, loff_t length) if (!error) error = security_path_truncate(path); if (!error) - error = do_truncate(path->dentry, length, 0, NULL); + error = do_truncate2(mnt, path->dentry, length, 0, NULL); put_write_and_out: put_write_access(inode); @@ -155,6 +160,7 @@ long do_sys_ftruncate(unsigned int fd, loff_t length, int small) { struct inode *inode; struct dentry *dentry; + struct vfsmount *mnt; struct fd f; int error; @@ -171,6 +177,7 @@ long do_sys_ftruncate(unsigned int fd, loff_t length, int small) small = 0; dentry = f.file->f_path.dentry; + mnt = f.file->f_path.mnt; inode = dentry->d_inode; error = -EINVAL; if (!S_ISREG(inode->i_mode) || !(f.file->f_mode & FMODE_WRITE)) @@ -191,7 +198,7 @@ long do_sys_ftruncate(unsigned int fd, loff_t length, int small) if (!error) error = security_path_truncate(&f.file->f_path); if (!error) - error = do_truncate(dentry, length, ATTR_MTIME|ATTR_CTIME, f.file); + error = do_truncate2(mnt, dentry, length, ATTR_MTIME|ATTR_CTIME, f.file); sb_end_write(inode->i_sb); out_putf: fdput(f); @@ -538,7 +545,7 @@ static int chmod_common(const struct path *path, umode_t mode) goto out_unlock; newattrs.ia_mode = (mode & S_IALLUGO) | (inode->i_mode & ~S_IALLUGO); newattrs.ia_valid = ATTR_MODE | ATTR_CTIME; - error = notify_change(path->dentry, &newattrs, &delegated_inode); + error = notify_change2(path->mnt, path->dentry, &newattrs, &delegated_inode); out_unlock: inode_unlock(inode); if (delegated_inode) { @@ -629,7 +636,7 @@ static int chown_common(const struct path *path, uid_t user, gid_t group) inode_lock(inode); error = security_path_chown(path, uid, gid); if (!error) - error = notify_change(path->dentry, &newattrs, &delegated_inode); + error = notify_change2(path->mnt, path->dentry, &newattrs, &delegated_inode); inode_unlock(inode); if (delegated_inode) { error = break_deleg_wait(&delegated_inode); diff --git a/fs/utimes.c b/fs/utimes.c index 69d4b6ba1bfb..1039ef7378a5 100644 --- a/fs/utimes.c +++ b/fs/utimes.c @@ -88,7 +88,7 @@ static int utimes_common(const struct path *path, struct timespec64 *times) } retry_deleg: inode_lock(inode); - error = notify_change(path->dentry, &newattrs, &delegated_inode); + error = notify_change2(path->mnt, path->dentry, &newattrs, &delegated_inode); inode_unlock(inode); if (delegated_inode) { error = break_deleg_wait(&delegated_inode); diff --git a/include/linux/fs.h b/include/linux/fs.h index e013cfb206d8..3ccb51f23689 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -1784,7 +1784,8 @@ struct inode_operations { int (*rename) (struct inode *, struct dentry *, struct inode *, struct dentry *, unsigned int); int (*setattr) (struct dentry *, struct iattr *); - int (*getattr) (const struct path *, struct kstat *, u32, unsigned int); + int (*setattr2) (struct vfsmount *, struct dentry *, struct iattr *); + int (*getattr) (const struct path *, struct kstat *, u32, unsigned int); ssize_t (*listxattr) (struct dentry *, char *, size_t); int (*fiemap)(struct inode *, struct fiemap_extent_info *, u64 start, u64 len); @@ -2443,6 +2444,8 @@ struct filename { extern long vfs_truncate(const struct path *, loff_t); extern int do_truncate(struct dentry *, loff_t start, unsigned int time_attrs, struct file *filp); +extern int do_truncate2(struct vfsmount *, struct dentry *, loff_t start, + unsigned int time_attrs, struct file *filp); extern int vfs_fallocate(struct file *file, int mode, loff_t offset, loff_t len); extern long do_sys_open(int dfd, const char __user *filename, int flags, @@ -2752,6 +2755,7 @@ extern void emergency_remount(void); extern sector_t bmap(struct inode *, sector_t); #endif extern int notify_change(struct dentry *, struct iattr *, struct inode **); +extern int notify_change2(struct vfsmount *, struct dentry *, struct iattr *, struct inode **); extern int inode_permission(struct inode *, int); extern int generic_permission(struct inode *, int); extern int __check_sticky(struct inode *dir, struct inode *inode); From d9b293607cbdc739bb0dc47734e4573b19c69b30 Mon Sep 17 00:00:00 2001 From: Daniel Rosenberg Date: Wed, 26 Oct 2016 16:27:45 -0700 Subject: [PATCH 0289/1103] RFC: ANDROID: vfs: Add permission2 for filesystems with per mount permissions This allows filesystems to use their mount private data to influence the permssions they return in permission2. It has been separated into a new call to avoid disrupting current permission users. Change-Id: I9d416e3b8b6eca84ef3e336bd2af89ddd51df6ca Signed-off-by: Daniel Rosenberg [AmitP: Minor refactoring of original patch to align with changes from the following upstream commit 4bfd054ae11e ("fs: fold __inode_permission() into inode_permission()"). Also introduce vfs_mkobj2(), because do_create() moved from using vfs_create() to vfs_mkobj() eecec19d9e70 ("mqueue: switch to vfs_mkobj(), quit abusing ->d_fsdata") do_create() is dropped/cleaned up upstream so a minor refactoring there as well. 066cc813e94a ("do_mq_open(): move all work prior to dentry_open() into a helper")] Signed-off-by: Amit Pundir --- fs/attr.c | 2 +- fs/exec.c | 2 +- fs/namei.c | 185 ++++++++++++++++++++--------- fs/notify/fanotify/fanotify_user.c | 2 +- fs/notify/inotify/inotify_user.c | 2 +- fs/open.c | 15 ++- include/linux/fs.h | 13 ++ include/linux/namei.h | 1 + ipc/mqueue.c | 14 +-- security/inode.c | 2 +- 10 files changed, 167 insertions(+), 71 deletions(-) diff --git a/fs/attr.c b/fs/attr.c index e55ed6e817d3..bea2f7cfd52c 100644 --- a/fs/attr.c +++ b/fs/attr.c @@ -247,7 +247,7 @@ int notify_change2(struct vfsmount *mnt, struct dentry * dentry, struct iattr * return -EPERM; if (!inode_owner_or_capable(inode)) { - error = inode_permission(inode, MAY_WRITE); + error = inode_permission2(mnt, inode, MAY_WRITE); if (error) return error; } diff --git a/fs/exec.c b/fs/exec.c index 1ebf6e5a521d..c7e3417a10ae 100644 --- a/fs/exec.c +++ b/fs/exec.c @@ -1308,7 +1308,7 @@ EXPORT_SYMBOL(flush_old_exec); void would_dump(struct linux_binprm *bprm, struct file *file) { struct inode *inode = file_inode(file); - if (inode_permission(inode, MAY_READ) < 0) { + if (inode_permission2(file->f_path.mnt, inode, MAY_READ) < 0) { struct user_namespace *old, *user_ns; bprm->interp_flags |= BINPRM_FLAGS_ENFORCE_NONDUMP; diff --git a/fs/namei.c b/fs/namei.c index be445b3c28a0..b463413ec56e 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -379,9 +379,11 @@ EXPORT_SYMBOL(generic_permission); * flag in inode->i_opflags, that says "this has not special * permission function, use the fast case". */ -static inline int do_inode_permission(struct inode *inode, int mask) +static inline int do_inode_permission(struct vfsmount *mnt, struct inode *inode, int mask) { if (unlikely(!(inode->i_opflags & IOP_FASTPERM))) { + if (likely(mnt && inode->i_op->permission2)) + return inode->i_op->permission2(mnt, inode, mask); if (likely(inode->i_op->permission)) return inode->i_op->permission(inode, mask); @@ -414,7 +416,8 @@ static int sb_permission(struct super_block *sb, struct inode *inode, int mask) } /** - * inode_permission - Check for access rights to a given inode + * inode_permission2 - Check for access rights to a given inode + * @mnt: * @inode: Inode to check permission on * @mask: Right to check for (%MAY_READ, %MAY_WRITE, %MAY_EXEC) * @@ -424,7 +427,7 @@ static int sb_permission(struct super_block *sb, struct inode *inode, int mask) * * When checking for MAY_APPEND, MAY_WRITE must also be set in @mask. */ -int inode_permission(struct inode *inode, int mask) +int inode_permission2(struct vfsmount *mnt, struct inode *inode, int mask) { int retval; @@ -448,7 +451,7 @@ int inode_permission(struct inode *inode, int mask) return -EACCES; } - retval = do_inode_permission(inode, mask); + retval = do_inode_permission(mnt, inode, mask); if (retval) return retval; @@ -456,7 +459,14 @@ int inode_permission(struct inode *inode, int mask) if (retval) return retval; - return security_inode_permission(inode, mask); + retval = security_inode_permission(inode, mask); + return retval; +} +EXPORT_SYMBOL(inode_permission2); + +int inode_permission(struct inode *inode, int mask) +{ + return inode_permission2(NULL, inode, mask); } EXPORT_SYMBOL(inode_permission); @@ -1693,13 +1703,13 @@ static struct dentry *lookup_slow(const struct qstr *name, static inline int may_lookup(struct nameidata *nd) { if (nd->flags & LOOKUP_RCU) { - int err = inode_permission(nd->inode, MAY_EXEC|MAY_NOT_BLOCK); + int err = inode_permission2(nd->path.mnt, nd->inode, MAY_EXEC|MAY_NOT_BLOCK); if (err != -ECHILD) return err; if (unlazy_walk(nd)) return -ECHILD; } - return inode_permission(nd->inode, MAY_EXEC); + return inode_permission2(nd->path.mnt, nd->inode, MAY_EXEC); } static inline int handle_dots(struct nameidata *nd, int type) @@ -2455,8 +2465,8 @@ int vfs_path_lookup(struct dentry *dentry, struct vfsmount *mnt, } EXPORT_SYMBOL(vfs_path_lookup); -static int lookup_one_len_common(const char *name, struct dentry *base, - int len, struct qstr *this) +static int lookup_one_len_common(const char *name, struct vfsmount *mnt, + struct dentry *base, int len, struct qstr *this) { this->name = name; this->len = len; @@ -2484,7 +2494,7 @@ static int lookup_one_len_common(const char *name, struct dentry *base, return err; } - return inode_permission(base->d_inode, MAY_EXEC); + return inode_permission2(mnt, base->d_inode, MAY_EXEC); } /** @@ -2508,7 +2518,7 @@ struct dentry *try_lookup_one_len(const char *name, struct dentry *base, int len WARN_ON_ONCE(!inode_is_locked(base->d_inode)); - err = lookup_one_len_common(name, base, len, &this); + err = lookup_one_len_common(name, NULL, base, len, &this); if (err) return ERR_PTR(err); @@ -2527,7 +2537,7 @@ EXPORT_SYMBOL(try_lookup_one_len); * * The caller must hold base->i_mutex. */ -struct dentry *lookup_one_len(const char *name, struct dentry *base, int len) +struct dentry *lookup_one_len2(const char *name, struct vfsmount *mnt, struct dentry *base, int len) { struct dentry *dentry; struct qstr this; @@ -2535,13 +2545,19 @@ struct dentry *lookup_one_len(const char *name, struct dentry *base, int len) WARN_ON_ONCE(!inode_is_locked(base->d_inode)); - err = lookup_one_len_common(name, base, len, &this); + err = lookup_one_len_common(name, mnt, base, len, &this); if (err) return ERR_PTR(err); dentry = lookup_dcache(&this, base, 0); return dentry ? dentry : __lookup_slow(&this, base, 0); } +EXPORT_SYMBOL(lookup_one_len2); + +struct dentry *lookup_one_len(const char *name, struct dentry *base, int len) +{ + return lookup_one_len2(name, NULL, base, len); +} EXPORT_SYMBOL(lookup_one_len); /** @@ -2563,7 +2579,7 @@ struct dentry *lookup_one_len_unlocked(const char *name, int err; struct dentry *ret; - err = lookup_one_len_common(name, base, len, &this); + err = lookup_one_len_common(name, NULL, base, len, &this); if (err) return ERR_PTR(err); @@ -2787,7 +2803,7 @@ EXPORT_SYMBOL(__check_sticky); * 11. We don't allow removal of NFS sillyrenamed files; it's handled by * nfs_async_unlink(). */ -static int may_delete(struct inode *dir, struct dentry *victim, bool isdir) +static int may_delete(struct vfsmount *mnt, struct inode *dir, struct dentry *victim, bool isdir) { struct inode *inode = d_backing_inode(victim); int error; @@ -2804,7 +2820,7 @@ static int may_delete(struct inode *dir, struct dentry *victim, bool isdir) audit_inode_child(dir, victim, AUDIT_TYPE_CHILD_DELETE); - error = inode_permission(dir, MAY_WRITE | MAY_EXEC); + error = inode_permission2(mnt, dir, MAY_WRITE | MAY_EXEC); if (error) return error; if (IS_APPEND(dir)) @@ -2836,7 +2852,7 @@ static int may_delete(struct inode *dir, struct dentry *victim, bool isdir) * 4. We should have write and exec permissions on dir * 5. We can't do it if dir is immutable (done in permission()) */ -static inline int may_create(struct inode *dir, struct dentry *child) +static inline int may_create(struct vfsmount *mnt, struct inode *dir, struct dentry *child) { struct user_namespace *s_user_ns; audit_inode_child(dir, child, AUDIT_TYPE_CHILD_CREATE); @@ -2848,7 +2864,7 @@ static inline int may_create(struct inode *dir, struct dentry *child) if (!kuid_has_mapping(s_user_ns, current_fsuid()) || !kgid_has_mapping(s_user_ns, current_fsgid())) return -EOVERFLOW; - return inode_permission(dir, MAY_WRITE | MAY_EXEC); + return inode_permission2(mnt, dir, MAY_WRITE | MAY_EXEC); } /* @@ -2895,10 +2911,10 @@ void unlock_rename(struct dentry *p1, struct dentry *p2) } EXPORT_SYMBOL(unlock_rename); -int vfs_create(struct inode *dir, struct dentry *dentry, umode_t mode, - bool want_excl) +int vfs_create2(struct vfsmount *mnt, struct inode *dir, struct dentry *dentry, + umode_t mode, bool want_excl) { - int error = may_create(dir, dentry); + int error = may_create(mnt, dir, dentry); if (error) return error; @@ -2914,14 +2930,21 @@ int vfs_create(struct inode *dir, struct dentry *dentry, umode_t mode, fsnotify_create(dir, dentry); return error; } +EXPORT_SYMBOL(vfs_create2); + +int vfs_create(struct inode *dir, struct dentry *dentry, umode_t mode, + bool want_excl) +{ + return vfs_create2(NULL, dir, dentry, mode, want_excl); +} EXPORT_SYMBOL(vfs_create); -int vfs_mkobj(struct dentry *dentry, umode_t mode, +int vfs_mkobj2(struct vfsmount *mnt, struct dentry *dentry, umode_t mode, int (*f)(struct dentry *, umode_t, void *), void *arg) { struct inode *dir = dentry->d_parent->d_inode; - int error = may_create(dir, dentry); + int error = may_create(mnt, dir, dentry); if (error) return error; @@ -2935,6 +2958,15 @@ int vfs_mkobj(struct dentry *dentry, umode_t mode, fsnotify_create(dir, dentry); return error; } +EXPORT_SYMBOL(vfs_mkobj2); + + +int vfs_mkobj(struct dentry *dentry, umode_t mode, + int (*f)(struct dentry *, umode_t, void *), + void *arg) +{ + return vfs_mkobj2(NULL, dentry, mode, f, arg); +} EXPORT_SYMBOL(vfs_mkobj); bool may_open_dev(const struct path *path) @@ -2946,6 +2978,7 @@ bool may_open_dev(const struct path *path) static int may_open(const struct path *path, int acc_mode, int flag) { struct dentry *dentry = path->dentry; + struct vfsmount *mnt = path->mnt; struct inode *inode = dentry->d_inode; int error; @@ -2970,7 +3003,7 @@ static int may_open(const struct path *path, int acc_mode, int flag) break; } - error = inode_permission(inode, MAY_OPEN | acc_mode); + error = inode_permission2(mnt, inode, MAY_OPEN | acc_mode); if (error) return error; @@ -3032,7 +3065,7 @@ static int may_o_create(const struct path *dir, struct dentry *dentry, umode_t m !kgid_has_mapping(s_user_ns, current_fsgid())) return -EOVERFLOW; - error = inode_permission(dir->dentry->d_inode, MAY_WRITE | MAY_EXEC); + error = inode_permission2(dir->mnt, dir->dentry->d_inode, MAY_WRITE | MAY_EXEC); if (error) return error; @@ -3440,7 +3473,8 @@ struct dentry *vfs_tmpfile(struct dentry *dentry, umode_t mode, int open_flag) int error; /* we want directory to be writable */ - error = inode_permission(dir, MAY_WRITE | MAY_EXEC); + error = inode_permission2(ERR_PTR(-EOPNOTSUPP), dir, + MAY_WRITE | MAY_EXEC); if (error) goto out_err; error = -EOPNOTSUPP; @@ -3694,9 +3728,9 @@ inline struct dentry *user_path_create(int dfd, const char __user *pathname, } EXPORT_SYMBOL(user_path_create); -int vfs_mknod(struct inode *dir, struct dentry *dentry, umode_t mode, dev_t dev) +int vfs_mknod2(struct vfsmount *mnt, struct inode *dir, struct dentry *dentry, umode_t mode, dev_t dev) { - int error = may_create(dir, dentry); + int error = may_create(mnt, dir, dentry); if (error) return error; @@ -3721,6 +3755,12 @@ int vfs_mknod(struct inode *dir, struct dentry *dentry, umode_t mode, dev_t dev) fsnotify_create(dir, dentry); return error; } +EXPORT_SYMBOL(vfs_mknod2); + +int vfs_mknod(struct inode *dir, struct dentry *dentry, umode_t mode, dev_t dev) +{ + return vfs_mknod2(NULL, dir, dentry, mode, dev); +} EXPORT_SYMBOL(vfs_mknod); static int may_mknod(umode_t mode) @@ -3763,12 +3803,12 @@ long do_mknodat(int dfd, const char __user *filename, umode_t mode, goto out; switch (mode & S_IFMT) { case 0: case S_IFREG: - error = vfs_create(path.dentry->d_inode,dentry,mode,true); + error = vfs_create2(path.mnt, path.dentry->d_inode,dentry,mode,true); if (!error) ima_post_path_mknod(dentry); break; case S_IFCHR: case S_IFBLK: - error = vfs_mknod(path.dentry->d_inode,dentry,mode, + error = vfs_mknod2(path.mnt, path.dentry->d_inode,dentry,mode, new_decode_dev(dev)); break; case S_IFIFO: case S_IFSOCK: @@ -3795,9 +3835,9 @@ SYSCALL_DEFINE3(mknod, const char __user *, filename, umode_t, mode, unsigned, d return do_mknodat(AT_FDCWD, filename, mode, dev); } -int vfs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode) +int vfs_mkdir2(struct vfsmount *mnt, struct inode *dir, struct dentry *dentry, umode_t mode) { - int error = may_create(dir, dentry); + int error = may_create(mnt, dir, dentry); unsigned max_links = dir->i_sb->s_max_links; if (error) @@ -3819,6 +3859,12 @@ int vfs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode) fsnotify_mkdir(dir, dentry); return error; } +EXPORT_SYMBOL(vfs_mkdir2); + +int vfs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode) +{ + return vfs_mkdir2(NULL, dir, dentry, mode); +} EXPORT_SYMBOL(vfs_mkdir); long do_mkdirat(int dfd, const char __user *pathname, umode_t mode) @@ -3837,7 +3883,7 @@ long do_mkdirat(int dfd, const char __user *pathname, umode_t mode) mode &= ~current_umask(); error = security_path_mkdir(&path, dentry, mode); if (!error) - error = vfs_mkdir(path.dentry->d_inode, dentry, mode); + error = vfs_mkdir2(path.mnt, path.dentry->d_inode, dentry, mode); done_path_create(&path, dentry); if (retry_estale(error, lookup_flags)) { lookup_flags |= LOOKUP_REVAL; @@ -3856,9 +3902,9 @@ SYSCALL_DEFINE2(mkdir, const char __user *, pathname, umode_t, mode) return do_mkdirat(AT_FDCWD, pathname, mode); } -int vfs_rmdir(struct inode *dir, struct dentry *dentry) +int vfs_rmdir2(struct vfsmount *mnt, struct inode *dir, struct dentry *dentry) { - int error = may_delete(dir, dentry, 1); + int error = may_delete(mnt, dir, dentry, 1); if (error) return error; @@ -3893,6 +3939,10 @@ int vfs_rmdir(struct inode *dir, struct dentry *dentry) d_delete(dentry); return error; } +int vfs_rmdir(struct inode *dir, struct dentry *dentry) +{ + return vfs_rmdir2(NULL, dir, dentry); +} EXPORT_SYMBOL(vfs_rmdir); long do_rmdir(int dfd, const char __user *pathname) @@ -3938,7 +3988,7 @@ long do_rmdir(int dfd, const char __user *pathname) error = security_path_rmdir(&path, dentry); if (error) goto exit3; - error = vfs_rmdir(path.dentry->d_inode, dentry); + error = vfs_rmdir2(path.mnt, path.dentry->d_inode, dentry); exit3: dput(dentry); exit2: @@ -3977,10 +4027,10 @@ SYSCALL_DEFINE1(rmdir, const char __user *, pathname) * be appropriate for callers that expect the underlying filesystem not * to be NFS exported. */ -int vfs_unlink(struct inode *dir, struct dentry *dentry, struct inode **delegated_inode) +int vfs_unlink2(struct vfsmount *mnt, struct inode *dir, struct dentry *dentry, struct inode **delegated_inode) { struct inode *target = dentry->d_inode; - int error = may_delete(dir, dentry, 0); + int error = may_delete(mnt, dir, dentry, 0); if (error) return error; @@ -4015,6 +4065,12 @@ int vfs_unlink(struct inode *dir, struct dentry *dentry, struct inode **delegate return error; } +EXPORT_SYMBOL(vfs_unlink2); + +int vfs_unlink(struct inode *dir, struct dentry *dentry, struct inode **delegated_inode) +{ + return vfs_unlink2(NULL, dir, dentry, delegated_inode); +} EXPORT_SYMBOL(vfs_unlink); /* @@ -4060,7 +4116,7 @@ long do_unlinkat(int dfd, struct filename *name) error = security_path_unlink(&path, dentry); if (error) goto exit2; - error = vfs_unlink(path.dentry->d_inode, dentry, &delegated_inode); + error = vfs_unlink2(path.mnt, path.dentry->d_inode, dentry, &delegated_inode); exit2: dput(dentry); } @@ -4110,9 +4166,9 @@ SYSCALL_DEFINE1(unlink, const char __user *, pathname) return do_unlinkat(AT_FDCWD, getname(pathname)); } -int vfs_symlink(struct inode *dir, struct dentry *dentry, const char *oldname) +int vfs_symlink2(struct vfsmount *mnt, struct inode *dir, struct dentry *dentry, const char *oldname) { - int error = may_create(dir, dentry); + int error = may_create(mnt, dir, dentry); if (error) return error; @@ -4129,6 +4185,12 @@ int vfs_symlink(struct inode *dir, struct dentry *dentry, const char *oldname) fsnotify_create(dir, dentry); return error; } +EXPORT_SYMBOL(vfs_symlink2); + +int vfs_symlink(struct inode *dir, struct dentry *dentry, const char *oldname) +{ + return vfs_symlink2(NULL, dir, dentry, oldname); +} EXPORT_SYMBOL(vfs_symlink); long do_symlinkat(const char __user *oldname, int newdfd, @@ -4151,7 +4213,7 @@ long do_symlinkat(const char __user *oldname, int newdfd, error = security_path_symlink(&path, dentry, from->name); if (!error) - error = vfs_symlink(path.dentry->d_inode, dentry, from->name); + error = vfs_symlink2(path.mnt, path.dentry->d_inode, dentry, from->name); done_path_create(&path, dentry); if (retry_estale(error, lookup_flags)) { lookup_flags |= LOOKUP_REVAL; @@ -4192,7 +4254,7 @@ SYSCALL_DEFINE2(symlink, const char __user *, oldname, const char __user *, newn * be appropriate for callers that expect the underlying filesystem not * to be NFS exported. */ -int vfs_link(struct dentry *old_dentry, struct inode *dir, struct dentry *new_dentry, struct inode **delegated_inode) +int vfs_link2(struct vfsmount *mnt, struct dentry *old_dentry, struct inode *dir, struct dentry *new_dentry, struct inode **delegated_inode) { struct inode *inode = old_dentry->d_inode; unsigned max_links = dir->i_sb->s_max_links; @@ -4201,7 +4263,7 @@ int vfs_link(struct dentry *old_dentry, struct inode *dir, struct dentry *new_de if (!inode) return -ENOENT; - error = may_create(dir, new_dentry); + error = may_create(mnt, dir, new_dentry); if (error) return error; @@ -4251,6 +4313,12 @@ int vfs_link(struct dentry *old_dentry, struct inode *dir, struct dentry *new_de fsnotify_link(dir, inode, new_dentry); return error; } +EXPORT_SYMBOL(vfs_link2); + +int vfs_link(struct dentry *old_dentry, struct inode *dir, struct dentry *new_dentry, struct inode **delegated_inode) +{ + return vfs_link2(NULL, old_dentry, dir, new_dentry, delegated_inode); +} EXPORT_SYMBOL(vfs_link); /* @@ -4306,7 +4374,7 @@ int do_linkat(int olddfd, const char __user *oldname, int newdfd, error = security_path_link(old_path.dentry, &new_path, new_dentry); if (error) goto out_dput; - error = vfs_link(old_path.dentry, new_path.dentry->d_inode, new_dentry, &delegated_inode); + error = vfs_link2(old_path.mnt, old_path.dentry, new_path.dentry->d_inode, new_dentry, &delegated_inode); out_dput: done_path_create(&new_path, new_dentry); if (delegated_inode) { @@ -4388,7 +4456,8 @@ SYSCALL_DEFINE2(link, const char __user *, oldname, const char __user *, newname * ->i_mutex on parents, which works but leads to some truly excessive * locking]. */ -int vfs_rename(struct inode *old_dir, struct dentry *old_dentry, +int vfs_rename2(struct vfsmount *mnt, + struct inode *old_dir, struct dentry *old_dentry, struct inode *new_dir, struct dentry *new_dentry, struct inode **delegated_inode, unsigned int flags) { @@ -4403,19 +4472,19 @@ int vfs_rename(struct inode *old_dir, struct dentry *old_dentry, if (source == target) return 0; - error = may_delete(old_dir, old_dentry, is_dir); + error = may_delete(mnt, old_dir, old_dentry, is_dir); if (error) return error; if (!target) { - error = may_create(new_dir, new_dentry); + error = may_create(mnt, new_dir, new_dentry); } else { new_is_dir = d_is_dir(new_dentry); if (!(flags & RENAME_EXCHANGE)) - error = may_delete(new_dir, new_dentry, is_dir); + error = may_delete(mnt, new_dir, new_dentry, is_dir); else - error = may_delete(new_dir, new_dentry, new_is_dir); + error = may_delete(mnt, new_dir, new_dentry, new_is_dir); } if (error) return error; @@ -4429,12 +4498,12 @@ int vfs_rename(struct inode *old_dir, struct dentry *old_dentry, */ if (new_dir != old_dir) { if (is_dir) { - error = inode_permission(source, MAY_WRITE); + error = inode_permission2(mnt, source, MAY_WRITE); if (error) return error; } if ((flags & RENAME_EXCHANGE) && new_is_dir) { - error = inode_permission(target, MAY_WRITE); + error = inode_permission2(mnt, target, MAY_WRITE); if (error) return error; } @@ -4511,6 +4580,14 @@ int vfs_rename(struct inode *old_dir, struct dentry *old_dentry, return error; } +EXPORT_SYMBOL(vfs_rename2); + +int vfs_rename(struct inode *old_dir, struct dentry *old_dentry, + struct inode *new_dir, struct dentry *new_dentry, + struct inode **delegated_inode, unsigned int flags) +{ + return vfs_rename2(NULL, old_dir, old_dentry, new_dir, new_dentry, delegated_inode, flags); +} EXPORT_SYMBOL(vfs_rename); static int do_renameat2(int olddfd, const char __user *oldname, int newdfd, @@ -4624,7 +4701,7 @@ static int do_renameat2(int olddfd, const char __user *oldname, int newdfd, &new_path, new_dentry, flags); if (error) goto exit5; - error = vfs_rename(old_path.dentry->d_inode, old_dentry, + error = vfs_rename2(old_path.mnt, old_path.dentry->d_inode, old_dentry, new_path.dentry->d_inode, new_dentry, &delegated_inode, flags); exit5: @@ -4675,7 +4752,7 @@ SYSCALL_DEFINE2(rename, const char __user *, oldname, const char __user *, newna int vfs_whiteout(struct inode *dir, struct dentry *dentry) { - int error = may_create(dir, dentry); + int error = may_create(NULL, dir, dentry); if (error) return error; diff --git a/fs/notify/fanotify/fanotify_user.c b/fs/notify/fanotify/fanotify_user.c index 69054886915b..06c57294f231 100644 --- a/fs/notify/fanotify/fanotify_user.c +++ b/fs/notify/fanotify/fanotify_user.c @@ -490,7 +490,7 @@ static int fanotify_find_path(int dfd, const char __user *filename, } /* you can only watch an inode if you have read permissions on it */ - ret = inode_permission(path->dentry->d_inode, MAY_READ); + ret = inode_permission2(path->mnt, path->dentry->d_inode, MAY_READ); if (ret) path_put(path); out: diff --git a/fs/notify/inotify/inotify_user.c b/fs/notify/inotify/inotify_user.c index 15f5655df87d..f9a95fb1008f 100644 --- a/fs/notify/inotify/inotify_user.c +++ b/fs/notify/inotify/inotify_user.c @@ -350,7 +350,7 @@ static int inotify_find_inode(const char __user *dirname, struct path *path, uns if (error) return error; /* you can only watch an inode if you have read permissions on it */ - error = inode_permission(path->dentry->d_inode, MAY_READ); + error = inode_permission2(path->mnt, path->dentry->d_inode, MAY_READ); if (error) path_put(path); return error; diff --git a/fs/open.c b/fs/open.c index a642ec16944b..f862e1c9141c 100644 --- a/fs/open.c +++ b/fs/open.c @@ -73,9 +73,11 @@ int do_truncate(struct dentry *dentry, loff_t length, unsigned int time_attrs, long vfs_truncate(const struct path *path, loff_t length) { struct inode *inode; + struct vfsmount *mnt; long error; inode = path->dentry->d_inode; + mnt = path->mnt; /* For directories it's -EISDIR, for other non-regulars - -EINVAL */ if (S_ISDIR(inode->i_mode)) @@ -87,7 +89,7 @@ long vfs_truncate(const struct path *path, loff_t length) if (error) goto out; - error = inode_permission(inode, MAY_WRITE); + error = inode_permission2(mnt, inode, MAY_WRITE); if (error) goto mnt_drop_write_and_out; @@ -357,6 +359,7 @@ long do_faccessat(int dfd, const char __user *filename, int mode) struct cred *override_cred; struct path path; struct inode *inode; + struct vfsmount *mnt; int res; unsigned int lookup_flags = LOOKUP_FOLLOW; @@ -387,6 +390,7 @@ long do_faccessat(int dfd, const char __user *filename, int mode) goto out; inode = d_backing_inode(path.dentry); + mnt = path.mnt; if ((mode & MAY_EXEC) && S_ISREG(inode->i_mode)) { /* @@ -398,7 +402,7 @@ long do_faccessat(int dfd, const char __user *filename, int mode) goto out_path_release; } - res = inode_permission(inode, mode | MAY_ACCESS); + res = inode_permission2(mnt, inode, mode | MAY_ACCESS); /* SuS v2 requires we report a read only fs too */ if (res || !(mode & S_IWOTH) || special_file(inode->i_mode)) goto out_path_release; @@ -447,7 +451,7 @@ int ksys_chdir(const char __user *filename) if (error) goto out; - error = inode_permission(path.dentry->d_inode, MAY_EXEC | MAY_CHDIR); + error = inode_permission2(path.mnt, path.dentry->d_inode, MAY_EXEC | MAY_CHDIR); if (error) goto dput_and_out; @@ -481,7 +485,8 @@ SYSCALL_DEFINE1(fchdir, unsigned int, fd) if (!d_can_lookup(f.file->f_path.dentry)) goto out_putf; - error = inode_permission(file_inode(f.file), MAY_EXEC | MAY_CHDIR); + error = inode_permission2(f.file->f_path.mnt, file_inode(f.file), + MAY_EXEC | MAY_CHDIR); if (!error) set_fs_pwd(current->fs, &f.file->f_path); out_putf: @@ -500,7 +505,7 @@ int ksys_chroot(const char __user *filename) if (error) goto out; - error = inode_permission(path.dentry->d_inode, MAY_EXEC | MAY_CHDIR); + error = inode_permission2(path.mnt, path.dentry->d_inode, MAY_EXEC | MAY_CHDIR); if (error) goto dput_and_out; diff --git a/include/linux/fs.h b/include/linux/fs.h index 3ccb51f23689..b12dc3fcf0cd 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -1625,13 +1625,21 @@ extern bool inode_owner_or_capable(const struct inode *inode); * VFS helper functions.. */ extern int vfs_create(struct inode *, struct dentry *, umode_t, bool); +extern int vfs_create2(struct vfsmount *, struct inode *, struct dentry *, umode_t, bool); extern int vfs_mkdir(struct inode *, struct dentry *, umode_t); +extern int vfs_mkdir2(struct vfsmount *, struct inode *, struct dentry *, umode_t); extern int vfs_mknod(struct inode *, struct dentry *, umode_t, dev_t); +extern int vfs_mknod2(struct vfsmount *, struct inode *, struct dentry *, umode_t, dev_t); extern int vfs_symlink(struct inode *, struct dentry *, const char *); +extern int vfs_symlink2(struct vfsmount *, struct inode *, struct dentry *, const char *); extern int vfs_link(struct dentry *, struct inode *, struct dentry *, struct inode **); +extern int vfs_link2(struct vfsmount *, struct dentry *, struct inode *, struct dentry *, struct inode **); extern int vfs_rmdir(struct inode *, struct dentry *); +extern int vfs_rmdir2(struct vfsmount *, struct inode *, struct dentry *); extern int vfs_unlink(struct inode *, struct dentry *, struct inode **); +extern int vfs_unlink2(struct vfsmount *, struct inode *, struct dentry *, struct inode **); extern int vfs_rename(struct inode *, struct dentry *, struct inode *, struct dentry *, struct inode **, unsigned int); +extern int vfs_rename2(struct vfsmount *, struct inode *, struct dentry *, struct inode *, struct dentry *, struct inode **, unsigned int); extern int vfs_whiteout(struct inode *, struct dentry *); extern struct dentry *vfs_tmpfile(struct dentry *dentry, umode_t mode, @@ -1640,6 +1648,9 @@ extern struct dentry *vfs_tmpfile(struct dentry *dentry, umode_t mode, int vfs_mkobj(struct dentry *, umode_t, int (*f)(struct dentry *, umode_t, void *), void *); +int vfs_mkobj2(struct vfsmount *, struct dentry *, umode_t, + int (*f)(struct dentry *, umode_t, void *), + void *); extern long vfs_ioctl(struct file *file, unsigned int cmd, unsigned long arg); @@ -1770,6 +1781,7 @@ struct inode_operations { struct dentry * (*lookup) (struct inode *,struct dentry *, unsigned int); const char * (*get_link) (struct dentry *, struct inode *, struct delayed_call *); int (*permission) (struct inode *, int); + int (*permission2) (struct vfsmount *, struct inode *, int); struct posix_acl * (*get_acl)(struct inode *, int); int (*readlink) (struct dentry *, char __user *,int); @@ -2757,6 +2769,7 @@ extern sector_t bmap(struct inode *, sector_t); extern int notify_change(struct dentry *, struct iattr *, struct inode **); extern int notify_change2(struct vfsmount *, struct dentry *, struct iattr *, struct inode **); extern int inode_permission(struct inode *, int); +extern int inode_permission2(struct vfsmount *, struct inode *, int); extern int generic_permission(struct inode *, int); extern int __check_sticky(struct inode *dir, struct inode *inode); diff --git a/include/linux/namei.h b/include/linux/namei.h index a78606e8e3df..788f3cbc12b7 100644 --- a/include/linux/namei.h +++ b/include/linux/namei.h @@ -83,6 +83,7 @@ extern int kern_path_mountpoint(int, const char *, struct path *, unsigned int); extern struct dentry *try_lookup_one_len(const char *, struct dentry *, int); extern struct dentry *lookup_one_len(const char *, struct dentry *, int); +extern struct dentry *lookup_one_len2(const char *, struct vfsmount *mnt, struct dentry *, int); extern struct dentry *lookup_one_len_unlocked(const char *, struct dentry *, int); extern int follow_down_one(struct path *); diff --git a/ipc/mqueue.c b/ipc/mqueue.c index c0d58f390c3b..37f7c7c0a1eb 100644 --- a/ipc/mqueue.c +++ b/ipc/mqueue.c @@ -714,7 +714,7 @@ static void remove_notification(struct mqueue_inode_info *info) info->notify_user_ns = NULL; } -static int prepare_open(struct dentry *dentry, int oflag, int ro, +static int prepare_open(struct vfsmount *mnt, struct dentry *dentry, int oflag, int ro, umode_t mode, struct filename *name, struct mq_attr *attr) { @@ -728,7 +728,7 @@ static int prepare_open(struct dentry *dentry, int oflag, int ro, if (ro) return ro; audit_inode_parent_hidden(name, dentry->d_parent); - return vfs_mkobj(dentry, mode & ~current_umask(), + return vfs_mkobj2(mnt, dentry, mode & ~current_umask(), mqueue_create_attr, attr); } /* it already existed */ @@ -738,7 +738,7 @@ static int prepare_open(struct dentry *dentry, int oflag, int ro, if ((oflag & O_ACCMODE) == (O_RDWR | O_WRONLY)) return -EINVAL; acc = oflag2acc[oflag & O_ACCMODE]; - return inode_permission(d_inode(dentry), acc); + return inode_permission2(mnt, d_inode(dentry), acc); } static int do_mq_open(const char __user *u_name, int oflag, umode_t mode, @@ -762,13 +762,13 @@ static int do_mq_open(const char __user *u_name, int oflag, umode_t mode, ro = mnt_want_write(mnt); /* we'll drop it in any case */ inode_lock(d_inode(root)); - path.dentry = lookup_one_len(name->name, root, strlen(name->name)); + path.dentry = lookup_one_len2(name->name, mnt, root, strlen(name->name)); if (IS_ERR(path.dentry)) { error = PTR_ERR(path.dentry); goto out_putfd; } path.mnt = mntget(mnt); - error = prepare_open(path.dentry, oflag, ro, mode, name, attr); + error = prepare_open(path.mnt, path.dentry, oflag, ro, mode, name, attr); if (!error) { struct file *file = dentry_open(&path, oflag, current_cred()); if (!IS_ERR(file)) @@ -818,7 +818,7 @@ SYSCALL_DEFINE1(mq_unlink, const char __user *, u_name) if (err) goto out_name; inode_lock_nested(d_inode(mnt->mnt_root), I_MUTEX_PARENT); - dentry = lookup_one_len(name->name, mnt->mnt_root, + dentry = lookup_one_len2(name->name, mnt, mnt->mnt_root, strlen(name->name)); if (IS_ERR(dentry)) { err = PTR_ERR(dentry); @@ -830,7 +830,7 @@ SYSCALL_DEFINE1(mq_unlink, const char __user *, u_name) err = -ENOENT; } else { ihold(inode); - err = vfs_unlink(d_inode(dentry->d_parent), dentry, NULL); + err = vfs_unlink2(mnt, d_inode(dentry->d_parent), dentry, NULL); } dput(dentry); diff --git a/security/inode.c b/security/inode.c index 8dd9ca8848e4..bf2810936dfb 100644 --- a/security/inode.c +++ b/security/inode.c @@ -122,7 +122,7 @@ static struct dentry *securityfs_create_dentry(const char *name, umode_t mode, dir = d_inode(parent); inode_lock(dir); - dentry = lookup_one_len(name, parent, strlen(name)); + dentry = lookup_one_len2(name, mount, parent, strlen(name)); if (IS_ERR(dentry)) goto out; From 2433b15d88ad8df642e53dd3e857d242ef56d008 Mon Sep 17 00:00:00 2001 From: Daniel Rosenberg Date: Tue, 23 Jan 2018 14:34:38 -0800 Subject: [PATCH 0290/1103] ANDROID: xattr: Pass EOPNOTSUPP to permission2 The permission call for xattr operations happens regardless of whether or not the xattr functions are implemented. The xattr functions currently don't have support for permission2. Passing EOPNOTSUPP as the mount point in xattr_permission allows us to return EOPNOTSUPP early in permission2, if the filesystem supports it. Change-Id: I9d07e4cd633cf40af60450ffbff7ac5c1b4e8c2c Signed-off-by: Daniel Rosenberg Bug: 35848445 --- fs/xattr.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/xattr.c b/fs/xattr.c index 0d6a6a4af861..2b10376568f1 100644 --- a/fs/xattr.c +++ b/fs/xattr.c @@ -130,7 +130,7 @@ xattr_permission(struct inode *inode, const char *name, int mask) return -EPERM; } - return inode_permission(inode, mask); + return inode_permission2(ERR_PTR(-EOPNOTSUPP), inode, mask); } int From 62e21e4a177456acb587c714a73db1414aadb785 Mon Sep 17 00:00:00 2001 From: Daniel Campello Date: Mon, 20 Jul 2015 16:27:37 -0700 Subject: [PATCH 0291/1103] ANDROID: Port of sdcardfs to 4.4 Change-Id: I25b99ecf214e72ebf6a57ec3085972542a8d7951 Signed-off-by: Daniel Rosenberg --- fs/sdcardfs/Kconfig | 1 - fs/sdcardfs/dentry.c | 9 +- fs/sdcardfs/file.c | 47 ++++--- fs/sdcardfs/hashtable.h | 190 -------------------------- fs/sdcardfs/inode.c | 280 +++++++++++++++----------------------- fs/sdcardfs/lookup.c | 25 ++-- fs/sdcardfs/main.c | 113 ++++++++------- fs/sdcardfs/mmap.c | 5 +- fs/sdcardfs/packagelist.c | 39 +++--- fs/sdcardfs/sdcardfs.h | 41 +++--- fs/sdcardfs/super.c | 6 +- include/linux/namei.h | 2 + 12 files changed, 252 insertions(+), 506 deletions(-) delete mode 100644 fs/sdcardfs/hashtable.h diff --git a/fs/sdcardfs/Kconfig b/fs/sdcardfs/Kconfig index 657f4958e8d6..d995f3eaae6d 100644 --- a/fs/sdcardfs/Kconfig +++ b/fs/sdcardfs/Kconfig @@ -1,6 +1,5 @@ config SDCARD_FS tristate "sdcard file system" - depends on EXPERIMENTAL default n help Sdcardfs is based on Wrapfs file system. diff --git a/fs/sdcardfs/dentry.c b/fs/sdcardfs/dentry.c index 4572a5403bb2..dbbcfd091fc7 100644 --- a/fs/sdcardfs/dentry.c +++ b/fs/sdcardfs/dentry.c @@ -26,7 +26,7 @@ * 0: tell VFS to invalidate dentry * 1: dentry is valid */ -static int sdcardfs_d_revalidate(struct dentry *dentry, struct nameidata *nd) +static int sdcardfs_d_revalidate(struct dentry *dentry, unsigned int flags) { int err = 1; struct path parent_lower_path, lower_path; @@ -35,7 +35,7 @@ static int sdcardfs_d_revalidate(struct dentry *dentry, struct nameidata *nd) struct dentry *lower_cur_parent_dentry = NULL; struct dentry *lower_dentry = NULL; - if (nd && nd->flags & LOOKUP_RCU) + if (flags & LOOKUP_RCU) return -ECHILD; spin_lock(&dentry->d_lock); @@ -119,7 +119,7 @@ static void sdcardfs_d_release(struct dentry *dentry) } static int sdcardfs_hash_ci(const struct dentry *dentry, - const struct inode *inode, struct qstr *qstr) + struct qstr *qstr) { /* * This function is copy of vfat_hashi. @@ -148,8 +148,7 @@ static int sdcardfs_hash_ci(const struct dentry *dentry, * Case insensitive compare of two vfat names. */ static int sdcardfs_cmp_ci(const struct dentry *parent, - const struct inode *pinode, - const struct dentry *dentry, const struct inode *inode, + const struct dentry *dentry, unsigned int len, const char *str, const struct qstr *name) { /* This function is copy of vfat_cmpi */ diff --git a/fs/sdcardfs/file.c b/fs/sdcardfs/file.c index bcacb947c874..f9c5eaafc619 100644 --- a/fs/sdcardfs/file.c +++ b/fs/sdcardfs/file.c @@ -50,8 +50,8 @@ static ssize_t sdcardfs_read(struct file *file, char __user *buf, err = vfs_read(lower_file, buf, count, ppos); /* update our inode atime upon a successful lower read */ if (err >= 0) - fsstack_copy_attr_atime(dentry->d_inode, - lower_file->f_path.dentry->d_inode); + fsstack_copy_attr_atime(d_inode(dentry), + file_inode(lower_file)); return err; } @@ -59,7 +59,7 @@ static ssize_t sdcardfs_read(struct file *file, char __user *buf, static ssize_t sdcardfs_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos) { - int err = 0; + int err; struct file *lower_file; struct dentry *dentry = file->f_path.dentry; @@ -73,29 +73,29 @@ static ssize_t sdcardfs_write(struct file *file, const char __user *buf, err = vfs_write(lower_file, buf, count, ppos); /* update our inode times+sizes upon a successful lower write */ if (err >= 0) { - fsstack_copy_inode_size(dentry->d_inode, - lower_file->f_path.dentry->d_inode); - fsstack_copy_attr_times(dentry->d_inode, - lower_file->f_path.dentry->d_inode); + fsstack_copy_inode_size(d_inode(dentry), + file_inode(lower_file)); + fsstack_copy_attr_times(d_inode(dentry), + file_inode(lower_file)); } return err; } -static int sdcardfs_readdir(struct file *file, void *dirent, filldir_t filldir) +static int sdcardfs_readdir(struct file *file, struct dir_context *ctx) { - int err = 0; + int err; struct file *lower_file = NULL; struct dentry *dentry = file->f_path.dentry; lower_file = sdcardfs_lower_file(file); lower_file->f_pos = file->f_pos; - err = vfs_readdir(lower_file, filldir, dirent); + err = iterate_dir(lower_file, ctx); file->f_pos = lower_file->f_pos; if (err >= 0) /* copy the atime */ - fsstack_copy_attr_atime(dentry->d_inode, - lower_file->f_path.dentry->d_inode); + fsstack_copy_attr_atime(d_inode(dentry), + file_inode(lower_file)); return err; } @@ -191,7 +191,6 @@ static int sdcardfs_mmap(struct file *file, struct vm_area_struct *vma) */ file_accessed(file); vma->vm_ops = &sdcardfs_vm_ops; - vma->vm_flags |= VM_CAN_NONLINEAR; file->f_mapping->a_ops = &sdcardfs_aops; /* set our aops */ if (!SDCARDFS_F(file)->lower_vm_ops) /* save for our ->fault */ @@ -242,8 +241,8 @@ static int sdcardfs_open(struct inode *inode, struct file *file) /* open lower object and link sdcardfs's file struct to lower's */ sdcardfs_get_lower_path(file->f_path.dentry, &lower_path); - lower_file = dentry_open(lower_path.dentry, lower_path.mnt, - file->f_flags, current_cred()); + lower_file = dentry_open(&lower_path, file->f_flags, current_cred()); + path_put(&lower_path); if (IS_ERR(lower_file)) { err = PTR_ERR(lower_file); lower_file = sdcardfs_lower_file(file); @@ -275,8 +274,10 @@ static int sdcardfs_flush(struct file *file, fl_owner_t id) struct file *lower_file = NULL; lower_file = sdcardfs_lower_file(file); - if (lower_file && lower_file->f_op && lower_file->f_op->flush) + if (lower_file && lower_file->f_op && lower_file->f_op->flush) { + filemap_write_and_wait(file->f_mapping); err = lower_file->f_op->flush(lower_file, id); + } return err; } @@ -296,19 +297,23 @@ static int sdcardfs_file_release(struct inode *inode, struct file *file) return 0; } -static int -sdcardfs_fsync(struct file *file, int datasync) +static int sdcardfs_fsync(struct file *file, loff_t start, loff_t end, + int datasync) { int err; struct file *lower_file; struct path lower_path; struct dentry *dentry = file->f_path.dentry; + err = __generic_file_fsync(file, start, end, datasync); + if (err) + goto out; + lower_file = sdcardfs_lower_file(file); sdcardfs_get_lower_path(dentry, &lower_path); - err = vfs_fsync(lower_file, datasync); + err = vfs_fsync_range(lower_file, start, end, datasync); sdcardfs_put_lower_path(dentry, &lower_path); - +out: return err; } @@ -344,7 +349,7 @@ const struct file_operations sdcardfs_main_fops = { const struct file_operations sdcardfs_dir_fops = { .llseek = generic_file_llseek, .read = generic_read_dir, - .readdir = sdcardfs_readdir, + .iterate = sdcardfs_readdir, .unlocked_ioctl = sdcardfs_unlocked_ioctl, #ifdef CONFIG_COMPAT .compat_ioctl = sdcardfs_compat_ioctl, diff --git a/fs/sdcardfs/hashtable.h b/fs/sdcardfs/hashtable.h deleted file mode 100644 index 1e770f3df148..000000000000 --- a/fs/sdcardfs/hashtable.h +++ /dev/null @@ -1,190 +0,0 @@ -/* - * Statically sized hash table implementation - * (C) 2012 Sasha Levin - */ - -#ifndef _LINUX_HASHTABLE_H -#define _LINUX_HASHTABLE_H - -#include -#include -#include -#include -#include - -#define DEFINE_HASHTABLE(name, bits) \ - struct hlist_head name[1 << (bits)] = \ - { [0 ... ((1 << (bits)) - 1)] = HLIST_HEAD_INIT } - -#define DECLARE_HASHTABLE(name, bits) \ - struct hlist_head name[1 << (bits)] - -#define HASH_SIZE(name) (ARRAY_SIZE(name)) -#define HASH_BITS(name) ilog2(HASH_SIZE(name)) - -/* Use hash_32 when possible to allow for fast 32bit hashing in 64bit kernels. */ -#define hash_min(val, bits) \ - (sizeof(val) <= 4 ? hash_32(val, bits) : hash_long(val, bits)) - -static inline void __hash_init(struct hlist_head *ht, unsigned int sz) -{ - unsigned int i; - - for (i = 0; i < sz; i++) - INIT_HLIST_HEAD(&ht[i]); -} - -/** - * hash_init - initialize a hash table - * @hashtable: hashtable to be initialized - * - * Calculates the size of the hashtable from the given parameter, otherwise - * same as hash_init_size. - * - * This has to be a macro since HASH_BITS() will not work on pointers since - * it calculates the size during preprocessing. - */ -#define hash_init(hashtable) __hash_init(hashtable, HASH_SIZE(hashtable)) - -/** - * hash_add - add an object to a hashtable - * @hashtable: hashtable to add to - * @node: the &struct hlist_node of the object to be added - * @key: the key of the object to be added - */ -#define hash_add(hashtable, node, key) \ - hlist_add_head(node, &hashtable[hash_min(key, HASH_BITS(hashtable))]) - -/** - * hash_add_rcu - add an object to a rcu enabled hashtable - * @hashtable: hashtable to add to - * @node: the &struct hlist_node of the object to be added - * @key: the key of the object to be added - */ -#define hash_add_rcu(hashtable, node, key) \ - hlist_add_head_rcu(node, &hashtable[hash_min(key, HASH_BITS(hashtable))]) - -/** - * hash_hashed - check whether an object is in any hashtable - * @node: the &struct hlist_node of the object to be checked - */ -static inline bool hash_hashed(struct hlist_node *node) -{ - return !hlist_unhashed(node); -} - -static inline bool __hash_empty(struct hlist_head *ht, unsigned int sz) -{ - unsigned int i; - - for (i = 0; i < sz; i++) - if (!hlist_empty(&ht[i])) - return false; - - return true; -} - -/** - * hash_empty - check whether a hashtable is empty - * @hashtable: hashtable to check - * - * This has to be a macro since HASH_BITS() will not work on pointers since - * it calculates the size during preprocessing. - */ -#define hash_empty(hashtable) __hash_empty(hashtable, HASH_SIZE(hashtable)) - -/** - * hash_del - remove an object from a hashtable - * @node: &struct hlist_node of the object to remove - */ -static inline void hash_del(struct hlist_node *node) -{ - hlist_del_init(node); -} - -/** - * hash_del_rcu - remove an object from a rcu enabled hashtable - * @node: &struct hlist_node of the object to remove - */ -static inline void hash_del_rcu(struct hlist_node *node) -{ - hlist_del_init_rcu(node); -} - -/** - * hash_for_each - iterate over a hashtable - * @name: hashtable to iterate - * @bkt: integer to use as bucket loop cursor - * @obj: the type * to use as a loop cursor for each entry - * @member: the name of the hlist_node within the struct - */ -#define hash_for_each(name, bkt, obj, member, pos) \ - for ((bkt) = 0, obj = NULL; obj == NULL && (bkt) < HASH_SIZE(name);\ - (bkt)++)\ - hlist_for_each_entry(obj, pos, &name[bkt], member) - -/** - * hash_for_each_rcu - iterate over a rcu enabled hashtable - * @name: hashtable to iterate - * @bkt: integer to use as bucket loop cursor - * @obj: the type * to use as a loop cursor for each entry - * @member: the name of the hlist_node within the struct - */ -#define hash_for_each_rcu(name, bkt, obj, member) \ - for ((bkt) = 0, obj = NULL; obj == NULL && (bkt) < HASH_SIZE(name);\ - (bkt)++)\ - hlist_for_each_entry_rcu(obj, &name[bkt], member) - -/** - * hash_for_each_safe - iterate over a hashtable safe against removal of - * hash entry - * @name: hashtable to iterate - * @bkt: integer to use as bucket loop cursor - * @tmp: a &struct used for temporary storage - * @obj: the type * to use as a loop cursor for each entry - * @member: the name of the hlist_node within the struct - */ -#define hash_for_each_safe(name, bkt, tmp, obj, member, pos) \ - for ((bkt) = 0, obj = NULL; (bkt) < HASH_SIZE(name);\ - (bkt)++)\ - hlist_for_each_entry_safe(obj, pos, tmp, &name[bkt], member) - -/** - * hash_for_each_possible - iterate over all possible objects hashing to the - * same bucket - * @name: hashtable to iterate - * @obj: the type * to use as a loop cursor for each entry - * @member: the name of the hlist_node within the struct - * @key: the key of the objects to iterate over - */ -#define hash_for_each_possible(name, obj, member, key, pos) \ - hlist_for_each_entry(obj, pos, &name[hash_min(key, HASH_BITS(name))], member) - -/** - * hash_for_each_possible_rcu - iterate over all possible objects hashing to the - * same bucket in an rcu enabled hashtable - * in a rcu enabled hashtable - * @name: hashtable to iterate - * @obj: the type * to use as a loop cursor for each entry - * @member: the name of the hlist_node within the struct - * @key: the key of the objects to iterate over - */ -#define hash_for_each_possible_rcu(name, obj, member, key) \ - hlist_for_each_entry_rcu(obj, &name[hash_min(key, HASH_BITS(name))],\ - member) - -/** - * hash_for_each_possible_safe - iterate over all possible objects hashing to the - * same bucket safe against removals - * @name: hashtable to iterate - * @obj: the type * to use as a loop cursor for each entry - * @tmp: a &struct used for temporary storage - * @member: the name of the hlist_node within the struct - * @key: the key of the objects to iterate over - */ -#define hash_for_each_possible_safe(name, obj, tmp, member, key) \ - hlist_for_each_entry_safe(obj, tmp,\ - &name[hash_min(key, HASH_BITS(name))], member) - - -#endif diff --git a/fs/sdcardfs/inode.c b/fs/sdcardfs/inode.c index e8ed04250ed1..75c622bac2f5 100644 --- a/fs/sdcardfs/inode.c +++ b/fs/sdcardfs/inode.c @@ -30,8 +30,8 @@ const struct cred * override_fsids(struct sdcardfs_sb_info* sbi) if (!cred) return NULL; - cred->fsuid = sbi->options.fs_low_uid; - cred->fsgid = sbi->options.fs_low_gid; + cred->fsuid = make_kuid(&init_user_ns, sbi->options.fs_low_uid); + cred->fsgid = make_kgid(&init_user_ns, sbi->options.fs_low_gid); old_cred = override_creds(cred); @@ -49,12 +49,12 @@ void revert_fsids(const struct cred * old_cred) } static int sdcardfs_create(struct inode *dir, struct dentry *dentry, - int mode, struct nameidata *nd) + umode_t mode, bool want_excl) { - int err = 0; + int err; struct dentry *lower_dentry; struct dentry *lower_parent_dentry = NULL; - struct path lower_path, saved_path; + struct path lower_path; struct sdcardfs_sb_info *sbi = SDCARDFS_SB(dentry->d_sb); const struct cred *saved_cred = NULL; @@ -74,18 +74,9 @@ static int sdcardfs_create(struct inode *dir, struct dentry *dentry, lower_dentry = lower_path.dentry; lower_parent_dentry = lock_parent(lower_dentry); - err = mnt_want_write(lower_path.mnt); - if (err) - goto out_unlock; - - pathcpy(&saved_path, &nd->path); - pathcpy(&nd->path, &lower_path); - /* set last 16bytes of mode field to 0664 */ mode = (mode & S_IFMT) | 00664; - err = vfs_create(lower_parent_dentry->d_inode, lower_dentry, mode, nd); - - pathcpy(&nd->path, &saved_path); + err = vfs_create(d_inode(lower_parent_dentry), lower_dentry, mode, want_excl); if (err) goto out; @@ -93,11 +84,9 @@ static int sdcardfs_create(struct inode *dir, struct dentry *dentry, if (err) goto out; fsstack_copy_attr_times(dir, sdcardfs_lower_inode(dir)); - fsstack_copy_inode_size(dir, lower_parent_dentry->d_inode); + fsstack_copy_inode_size(dir, d_inode(lower_parent_dentry)); out: - mnt_drop_write(lower_path.mnt); -out_unlock: unlock_dir(lower_parent_dentry); sdcardfs_put_lower_path(dentry, &lower_path); REVERT_CRED(saved_cred); @@ -118,33 +107,27 @@ static int sdcardfs_link(struct dentry *old_dentry, struct inode *dir, OVERRIDE_CRED(SDCARDFS_SB(dir->i_sb)); - file_size_save = i_size_read(old_dentry->d_inode); + file_size_save = i_size_read(d_inode(old_dentry)); sdcardfs_get_lower_path(old_dentry, &lower_old_path); sdcardfs_get_lower_path(new_dentry, &lower_new_path); lower_old_dentry = lower_old_path.dentry; lower_new_dentry = lower_new_path.dentry; lower_dir_dentry = lock_parent(lower_new_dentry); - err = mnt_want_write(lower_new_path.mnt); - if (err) - goto out_unlock; - - err = vfs_link(lower_old_dentry, lower_dir_dentry->d_inode, - lower_new_dentry); - if (err || !lower_new_dentry->d_inode) + err = vfs_link(lower_old_dentry, d_inode(lower_dir_dentry), + lower_new_dentry, NULL); + if (err || !d_inode(lower_new_dentry)) goto out; err = sdcardfs_interpose(new_dentry, dir->i_sb, &lower_new_path); if (err) goto out; - fsstack_copy_attr_times(dir, lower_new_dentry->d_inode); - fsstack_copy_inode_size(dir, lower_new_dentry->d_inode); - old_dentry->d_inode->i_nlink = - sdcardfs_lower_inode(old_dentry->d_inode)->i_nlink; - i_size_write(new_dentry->d_inode, file_size_save); + fsstack_copy_attr_times(dir, d_inode(lower_new_dentry)); + fsstack_copy_inode_size(dir, d_inode(lower_new_dentry)); + set_nlink(d_inode(old_dentry), + sdcardfs_lower_inode(d_inode(old_dentry))->i_nlink); + i_size_write(d_inode(new_dentry), file_size_save); out: - mnt_drop_write(lower_new_path.mnt); -out_unlock: unlock_dir(lower_dir_dentry); sdcardfs_put_lower_path(old_dentry, &lower_old_path); sdcardfs_put_lower_path(new_dentry, &lower_new_path); @@ -180,10 +163,7 @@ static int sdcardfs_unlink(struct inode *dir, struct dentry *dentry) dget(lower_dentry); lower_dir_dentry = lock_parent(lower_dentry); - err = mnt_want_write(lower_path.mnt); - if (err) - goto out_unlock; - err = vfs_unlink(lower_dir_inode, lower_dentry); + err = vfs_unlink(lower_dir_inode, lower_dentry, NULL); /* * Note: unlinking on top of NFS can cause silly-renamed files. @@ -198,13 +178,11 @@ static int sdcardfs_unlink(struct inode *dir, struct dentry *dentry) goto out; fsstack_copy_attr_times(dir, lower_dir_inode); fsstack_copy_inode_size(dir, lower_dir_inode); - dentry->d_inode->i_nlink = - sdcardfs_lower_inode(dentry->d_inode)->i_nlink; - dentry->d_inode->i_ctime = dir->i_ctime; + set_nlink(d_inode(dentry), + sdcardfs_lower_inode(d_inode(dentry))->i_nlink); + d_inode(dentry)->i_ctime = dir->i_ctime; d_drop(dentry); /* this is needed, else LTP fails (VFS won't do it) */ out: - mnt_drop_write(lower_path.mnt); -out_unlock: unlock_dir(lower_dir_dentry); dput(lower_dentry); sdcardfs_put_lower_path(dentry, &lower_path); @@ -217,7 +195,7 @@ static int sdcardfs_unlink(struct inode *dir, struct dentry *dentry) static int sdcardfs_symlink(struct inode *dir, struct dentry *dentry, const char *symname) { - int err = 0; + int err; struct dentry *lower_dentry; struct dentry *lower_parent_dentry = NULL; struct path lower_path; @@ -228,21 +206,16 @@ static int sdcardfs_symlink(struct inode *dir, struct dentry *dentry, lower_dentry = lower_path.dentry; lower_parent_dentry = lock_parent(lower_dentry); - err = mnt_want_write(lower_path.mnt); - if (err) - goto out_unlock; - err = vfs_symlink(lower_parent_dentry->d_inode, lower_dentry, symname); + err = vfs_symlink(d_inode(lower_parent_dentry), lower_dentry, symname); if (err) goto out; err = sdcardfs_interpose(dentry, dir->i_sb, &lower_path); if (err) goto out; fsstack_copy_attr_times(dir, sdcardfs_lower_inode(dir)); - fsstack_copy_inode_size(dir, lower_parent_dentry->d_inode); + fsstack_copy_inode_size(dir, d_inode(lower_parent_dentry)); out: - mnt_drop_write(lower_path.mnt); -out_unlock: unlock_dir(lower_parent_dentry); sdcardfs_put_lower_path(dentry, &lower_path); REVERT_CRED(); @@ -266,9 +239,9 @@ static int touch(char *abs_path, mode_t mode) { return 0; } -static int sdcardfs_mkdir(struct inode *dir, struct dentry *dentry, int mode) +static int sdcardfs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode) { - int err = 0; + int err; int make_nomedia_in_obb = 0; struct dentry *lower_dentry; struct dentry *lower_parent_dentry = NULL; @@ -306,13 +279,9 @@ static int sdcardfs_mkdir(struct inode *dir, struct dentry *dentry, int mode) lower_dentry = lower_path.dentry; lower_parent_dentry = lock_parent(lower_dentry); - err = mnt_want_write(lower_path.mnt); - if (err) - goto out_unlock; - /* set last 16bytes of mode field to 0775 */ mode = (mode & S_IFMT) | 00775; - err = vfs_mkdir(lower_parent_dentry->d_inode, lower_dentry, mode); + err = vfs_mkdir(d_inode(lower_parent_dentry), lower_dentry, mode); if (err) goto out; @@ -341,9 +310,9 @@ static int sdcardfs_mkdir(struct inode *dir, struct dentry *dentry, int mode) goto out; fsstack_copy_attr_times(dir, sdcardfs_lower_inode(dir)); - fsstack_copy_inode_size(dir, lower_parent_dentry->d_inode); + fsstack_copy_inode_size(dir, d_inode(lower_parent_dentry)); /* update number of links on parent directory */ - dir->i_nlink = sdcardfs_lower_inode(dir)->i_nlink; + set_nlink(dir, sdcardfs_lower_inode(dir)->i_nlink); if ((sbi->options.derive == DERIVE_UNIFIED) && (!strcasecmp(dentry->d_name.name, "obb")) && (pi->perm == PERM_ANDROID) && (pi->userid == 0)) @@ -388,8 +357,6 @@ static int sdcardfs_mkdir(struct inode *dir, struct dentry *dentry, int mode) kfree(nomedia_fullpath); } out: - mnt_drop_write(lower_path.mnt); -out_unlock: unlock_dir(lower_parent_dentry); sdcardfs_put_lower_path(dentry, &lower_path); out_revert: @@ -427,23 +394,18 @@ static int sdcardfs_rmdir(struct inode *dir, struct dentry *dentry) lower_dentry = lower_path.dentry; lower_dir_dentry = lock_parent(lower_dentry); - err = mnt_want_write(lower_path.mnt); - if (err) - goto out_unlock; - err = vfs_rmdir(lower_dir_dentry->d_inode, lower_dentry); + err = vfs_rmdir(d_inode(lower_dir_dentry), lower_dentry); if (err) goto out; d_drop(dentry); /* drop our dentry on success (why not VFS's job?) */ - if (dentry->d_inode) - clear_nlink(dentry->d_inode); - fsstack_copy_attr_times(dir, lower_dir_dentry->d_inode); - fsstack_copy_inode_size(dir, lower_dir_dentry->d_inode); - dir->i_nlink = lower_dir_dentry->d_inode->i_nlink; + if (d_inode(dentry)) + clear_nlink(d_inode(dentry)); + fsstack_copy_attr_times(dir, d_inode(lower_dir_dentry)); + fsstack_copy_inode_size(dir, d_inode(lower_dir_dentry)); + set_nlink(dir, d_inode(lower_dir_dentry)->i_nlink); out: - mnt_drop_write(lower_path.mnt); -out_unlock: unlock_dir(lower_dir_dentry); sdcardfs_put_real_lower(dentry, &lower_path); REVERT_CRED(saved_cred); @@ -452,10 +414,10 @@ static int sdcardfs_rmdir(struct inode *dir, struct dentry *dentry) } #if 0 -static int sdcardfs_mknod(struct inode *dir, struct dentry *dentry, int mode, +static int sdcardfs_mknod(struct inode *dir, struct dentry *dentry, umode_t mode, dev_t dev) { - int err = 0; + int err; struct dentry *lower_dentry; struct dentry *lower_parent_dentry = NULL; struct path lower_path; @@ -466,10 +428,7 @@ static int sdcardfs_mknod(struct inode *dir, struct dentry *dentry, int mode, lower_dentry = lower_path.dentry; lower_parent_dentry = lock_parent(lower_dentry); - err = mnt_want_write(lower_path.mnt); - if (err) - goto out_unlock; - err = vfs_mknod(lower_parent_dentry->d_inode, lower_dentry, mode, dev); + err = vfs_mknod(d_inode(lower_parent_dentry), lower_dentry, mode, dev); if (err) goto out; @@ -477,11 +436,9 @@ static int sdcardfs_mknod(struct inode *dir, struct dentry *dentry, int mode, if (err) goto out; fsstack_copy_attr_times(dir, sdcardfs_lower_inode(dir)); - fsstack_copy_inode_size(dir, lower_parent_dentry->d_inode); + fsstack_copy_inode_size(dir, d_inode(lower_parent_dentry)); out: - mnt_drop_write(lower_path.mnt); -out_unlock: unlock_dir(lower_parent_dentry); sdcardfs_put_lower_path(dentry, &lower_path); REVERT_CRED(); @@ -541,43 +498,33 @@ static int sdcardfs_rename(struct inode *old_dir, struct dentry *old_dentry, goto out; } - err = mnt_want_write(lower_old_path.mnt); + err = vfs_rename(d_inode(lower_old_dir_dentry), lower_old_dentry, + d_inode(lower_new_dir_dentry), lower_new_dentry, + NULL, 0); if (err) goto out; - err = mnt_want_write(lower_new_path.mnt); - if (err) - goto out_drop_old_write; - - err = vfs_rename(lower_old_dir_dentry->d_inode, lower_old_dentry, - lower_new_dir_dentry->d_inode, lower_new_dentry); - if (err) - goto out_err; /* Copy attrs from lower dir, but i_uid/i_gid */ - fsstack_copy_attr_all(new_dir, lower_new_dir_dentry->d_inode); - fsstack_copy_inode_size(new_dir, lower_new_dir_dentry->d_inode); + fsstack_copy_attr_all(new_dir, d_inode(lower_new_dir_dentry)); + fsstack_copy_inode_size(new_dir, d_inode(lower_new_dir_dentry)); fix_derived_permission(new_dir); if (new_dir != old_dir) { - fsstack_copy_attr_all(old_dir, lower_old_dir_dentry->d_inode); - fsstack_copy_inode_size(old_dir, lower_old_dir_dentry->d_inode); + fsstack_copy_attr_all(old_dir, d_inode(lower_old_dir_dentry)); + fsstack_copy_inode_size(old_dir, d_inode(lower_old_dir_dentry)); fix_derived_permission(old_dir); /* update the derived permission of the old_dentry * with its new parent */ new_parent = dget_parent(new_dentry); if(new_parent) { - if(old_dentry->d_inode) { + if(d_inode(old_dentry)) { get_derived_permission(new_parent, old_dentry); - fix_derived_permission(old_dentry->d_inode); + fix_derived_permission(d_inode(old_dentry)); } dput(new_parent); } } -out_err: - mnt_drop_write(lower_new_path.mnt); -out_drop_old_write: - mnt_drop_write(lower_old_path.mnt); out: unlock_rename(lower_old_dir_dentry, lower_new_dir_dentry); dput(lower_old_dir_dentry); @@ -599,17 +546,17 @@ static int sdcardfs_readlink(struct dentry *dentry, char __user *buf, int bufsiz sdcardfs_get_lower_path(dentry, &lower_path); lower_dentry = lower_path.dentry; - if (!lower_dentry->d_inode->i_op || - !lower_dentry->d_inode->i_op->readlink) { + if (!d_inode(lower_dentry)->i_op || + !d_inode(lower_dentry)->i_op->readlink) { err = -EINVAL; goto out; } - err = lower_dentry->d_inode->i_op->readlink(lower_dentry, + err = d_inode(lower_dentry)->i_op->readlink(lower_dentry, buf, bufsiz); if (err < 0) goto out; - fsstack_copy_attr_atime(dentry->d_inode, lower_dentry->d_inode); + fsstack_copy_attr_atime(d_inode(dentry), d_inode(lower_dentry)); out: sdcardfs_put_lower_path(dentry, &lower_path); @@ -618,7 +565,7 @@ static int sdcardfs_readlink(struct dentry *dentry, char __user *buf, int bufsiz #endif #if 0 -static void *sdcardfs_follow_link(struct dentry *dentry, struct nameidata *nd) +static const char *sdcardfs_follow_link(struct dentry *dentry, void **cookie) { char *buf; int len = PAGE_SIZE, err; @@ -628,7 +575,7 @@ static void *sdcardfs_follow_link(struct dentry *dentry, struct nameidata *nd) buf = kmalloc(len, GFP_KERNEL); if (!buf) { buf = ERR_PTR(-ENOMEM); - goto out; + return buf; } /* read the symlink, and then we will follow it */ @@ -642,35 +589,19 @@ static void *sdcardfs_follow_link(struct dentry *dentry, struct nameidata *nd) } else { buf[err] = '\0'; } -out: - nd_set_link(nd, buf); - return NULL; -} -#endif - -#if 0 -/* this @nd *IS* still used */ -static void sdcardfs_put_link(struct dentry *dentry, struct nameidata *nd, - void *cookie) -{ - char *buf = nd_get_link(nd); - if (!IS_ERR(buf)) /* free the char* */ - kfree(buf); + return *cookie = buf; } #endif -static int sdcardfs_permission(struct inode *inode, int mask, unsigned int flags) +static int sdcardfs_permission(struct inode *inode, int mask) { int err; - if (flags & IPERM_FLAG_RCU) - return -ECHILD; - /* * Permission check on sdcardfs inode. * Calling process should have AID_SDCARD_RW permission */ - err = generic_permission(inode, mask, 0, inode->i_op->check_acl); + err = generic_permission(inode, mask); /* XXX * Original sdcardfs code calls inode_permission(lower_inode,.. ) @@ -700,49 +631,9 @@ static int sdcardfs_permission(struct inode *inode, int mask, unsigned int flags } -static int sdcardfs_getattr(struct vfsmount *mnt, struct dentry *dentry, - struct kstat *stat) -{ - struct dentry *lower_dentry; - struct inode *inode; - struct inode *lower_inode; - struct path lower_path; - struct dentry *parent; - struct sdcardfs_sb_info *sbi = SDCARDFS_SB(dentry->d_sb); - - parent = dget_parent(dentry); - if(!check_caller_access_to_name(parent->d_inode, dentry->d_name.name, - sbi->options.derive, 0, 0)) { - printk(KERN_INFO "%s: need to check the caller's gid in packages.list\n" - " dentry: %s, task:%s\n", - __func__, dentry->d_name.name, current->comm); - dput(parent); - return -EACCES; - } - dput(parent); - - inode = dentry->d_inode; - - sdcardfs_get_lower_path(dentry, &lower_path); - lower_dentry = lower_path.dentry; - lower_inode = sdcardfs_lower_inode(inode); - - fsstack_copy_attr_all(inode, lower_inode); - fsstack_copy_inode_size(inode, lower_inode); - /* if the dentry has been moved from other location - * so, on this stage, its derived permission must be - * rechecked from its private field. - */ - fix_derived_permission(inode); - - generic_fillattr(inode, stat); - sdcardfs_put_lower_path(dentry, &lower_path); - return 0; -} - static int sdcardfs_setattr(struct dentry *dentry, struct iattr *ia) { - int err = 0; + int err; struct dentry *lower_dentry; struct inode *inode; struct inode *lower_inode; @@ -752,7 +643,7 @@ static int sdcardfs_setattr(struct dentry *dentry, struct iattr *ia) struct dentry *parent; int has_rw; - inode = dentry->d_inode; + inode = d_inode(dentry); /* * Check if user has permission to change inode. We don't check if @@ -766,7 +657,7 @@ static int sdcardfs_setattr(struct dentry *dentry, struct iattr *ia) /* check the Android group ID */ has_rw = get_caller_has_rw_locked(sbi->pkgl_id, sbi->options.derive); parent = dget_parent(dentry); - if(!check_caller_access_to_name(parent->d_inode, dentry->d_name.name, + if(!check_caller_access_to_name(d_inode(parent), dentry->d_name.name, sbi->options.derive, 1, has_rw)) { printk(KERN_INFO "%s: need to check the caller's gid in packages.list\n" " dentry: %s, task:%s\n", @@ -819,13 +710,14 @@ static int sdcardfs_setattr(struct dentry *dentry, struct iattr *ia) /* notify the (possibly copied-up) lower inode */ /* - * Note: we use lower_dentry->d_inode, because lower_inode may be + * Note: we use d_inode(lower_dentry), because lower_inode may be * unlinked (no inode->i_sb and i_ino==0. This happens if someone * tries to open(), unlink(), then ftruncate() a file. */ - mutex_lock(&lower_dentry->d_inode->i_mutex); - err = notify_change(lower_dentry, &lower_ia); /* note: lower_ia */ - mutex_unlock(&lower_dentry->d_inode->i_mutex); + mutex_lock(&d_inode(lower_dentry)->i_mutex); + err = notify_change(lower_dentry, &lower_ia, /* note: lower_ia */ + NULL); + mutex_unlock(&d_inode(lower_dentry)->i_mutex); if (current->mm) up_write(¤t->mm->mmap_sem); if (err) @@ -848,6 +740,46 @@ static int sdcardfs_setattr(struct dentry *dentry, struct iattr *ia) return err; } +static int sdcardfs_getattr(struct vfsmount *mnt, struct dentry *dentry, + struct kstat *stat) +{ + struct dentry *lower_dentry; + struct inode *inode; + struct inode *lower_inode; + struct path lower_path; + struct dentry *parent; + struct sdcardfs_sb_info *sbi = SDCARDFS_SB(dentry->d_sb); + + parent = dget_parent(dentry); + if(!check_caller_access_to_name(d_inode(parent), dentry->d_name.name, + sbi->options.derive, 0, 0)) { + printk(KERN_INFO "%s: need to check the caller's gid in packages.list\n" + " dentry: %s, task:%s\n", + __func__, dentry->d_name.name, current->comm); + dput(parent); + return -EACCES; + } + dput(parent); + + inode = d_inode(dentry); + + sdcardfs_get_lower_path(dentry, &lower_path); + lower_dentry = lower_path.dentry; + lower_inode = sdcardfs_lower_inode(inode); + + fsstack_copy_attr_all(inode, lower_inode); + fsstack_copy_inode_size(inode, lower_inode); + /* if the dentry has been moved from other location + * so, on this stage, its derived permission must be + * rechecked from its private field. + */ + fix_derived_permission(inode); + + generic_fillattr(inode, stat); + sdcardfs_put_lower_path(dentry, &lower_path); + return 0; +} + const struct inode_operations sdcardfs_symlink_iops = { .permission = sdcardfs_permission, .setattr = sdcardfs_setattr, @@ -856,14 +788,16 @@ const struct inode_operations sdcardfs_symlink_iops = { * These methods are *NOT* perfectly tested. .readlink = sdcardfs_readlink, .follow_link = sdcardfs_follow_link, - .put_link = sdcardfs_put_link, + .put_link = kfree_put_link, */ }; const struct inode_operations sdcardfs_dir_iops = { .create = sdcardfs_create, .lookup = sdcardfs_lookup, +#if 0 .permission = sdcardfs_permission, +#endif .unlink = sdcardfs_unlink, .mkdir = sdcardfs_mkdir, .rmdir = sdcardfs_rmdir, diff --git a/fs/sdcardfs/lookup.c b/fs/sdcardfs/lookup.c index c0b12375b1bf..a4b94df99f32 100644 --- a/fs/sdcardfs/lookup.c +++ b/fs/sdcardfs/lookup.c @@ -79,8 +79,7 @@ static int sdcardfs_inode_set(struct inode *inode, void *lower_inode) return 0; } -static struct inode *sdcardfs_iget(struct super_block *sb, - struct inode *lower_inode) +struct inode *sdcardfs_iget(struct super_block *sb, struct inode *lower_inode) { struct sdcardfs_inode_info *info; struct inode *inode; /* the new inode to return */ @@ -206,14 +205,13 @@ int sdcardfs_interpose(struct dentry *dentry, struct super_block *sb, * Fills in lower_parent_path with on success. */ static struct dentry *__sdcardfs_lookup(struct dentry *dentry, - struct nameidata *nd, struct path *lower_parent_path) + unsigned int flags, struct path *lower_parent_path) { int err = 0; struct vfsmount *lower_dir_mnt; struct dentry *lower_dir_dentry = NULL; struct dentry *lower_dentry; const char *name; - struct nameidata lower_nd; struct path lower_path; struct qstr this; struct sdcardfs_sb_info *sbi; @@ -234,10 +232,10 @@ static struct dentry *__sdcardfs_lookup(struct dentry *dentry, /* Use vfs_path_lookup to check if the dentry exists or not */ if (sbi->options.lower_fs == LOWER_FS_EXT4) { err = vfs_path_lookup(lower_dir_dentry, lower_dir_mnt, name, - LOOKUP_CASE_INSENSITIVE, &lower_nd); + LOOKUP_CASE_INSENSITIVE, &lower_path); } else if (sbi->options.lower_fs == LOWER_FS_FAT) { err = vfs_path_lookup(lower_dir_dentry, lower_dir_mnt, name, 0, - &lower_nd); + &lower_path); } /* no error: handle positive dentries */ @@ -253,7 +251,7 @@ static struct dentry *__sdcardfs_lookup(struct dentry *dentry, * and the base obbpath will be copyed to the lower_path variable. * if an error returned, there's no change in the lower_path * returns: -ERRNO if error (0: no error) */ - err = setup_obb_dentry(dentry, &lower_nd.path); + err = setup_obb_dentry(dentry, &lower_path); if(err) { /* if the sbi->obbpath is not available, we can optionally @@ -267,8 +265,8 @@ static struct dentry *__sdcardfs_lookup(struct dentry *dentry, } } - sdcardfs_set_lower_path(dentry, &lower_nd.path); - err = sdcardfs_interpose(dentry, dentry->d_sb, &lower_nd.path); + sdcardfs_set_lower_path(dentry, &lower_path); + err = sdcardfs_interpose(dentry, dentry->d_sb, &lower_path); if (err) /* path_put underlying path on error */ sdcardfs_put_reset_lower_path(dentry); goto out; @@ -306,10 +304,7 @@ static struct dentry *__sdcardfs_lookup(struct dentry *dentry, * the VFS will continue the process of making this negative dentry * into a positive one. */ - if (nd) { - if (nd->flags & (LOOKUP_CREATE|LOOKUP_RENAME_TARGET)) - err = 0; - } else + if (flags & (LOOKUP_CREATE|LOOKUP_RENAME_TARGET)) err = 0; out: @@ -328,7 +323,7 @@ static struct dentry *__sdcardfs_lookup(struct dentry *dentry, * @nd : nameidata of parent inode */ struct dentry *sdcardfs_lookup(struct inode *dir, struct dentry *dentry, - struct nameidata *nd) + unsigned int flags) { struct dentry *ret = NULL, *parent; struct path lower_parent_path; @@ -359,7 +354,7 @@ struct dentry *sdcardfs_lookup(struct inode *dir, struct dentry *dentry, goto out; } - ret = __sdcardfs_lookup(dentry, nd, &lower_parent_path); + ret = __sdcardfs_lookup(dentry, flags, &lower_parent_path); if (IS_ERR(ret)) { goto out; diff --git a/fs/sdcardfs/main.c b/fs/sdcardfs/main.c index 1fdceffec72c..9d04ae8ceb46 100644 --- a/fs/sdcardfs/main.c +++ b/fs/sdcardfs/main.c @@ -156,6 +156,7 @@ static int parse_options(struct super_block *sb, char *options, int silent, return 0; } +#if 0 /* * our custom d_alloc_root work-alike * @@ -181,6 +182,7 @@ static struct dentry *sdcardfs_d_alloc_root(struct super_block *sb) } return ret; } +#endif /* * There is no need to lock the sdcardfs_super_info's rwsem as there is no @@ -195,6 +197,7 @@ static int sdcardfs_read_super(struct super_block *sb, const char *dev_name, struct path lower_path; struct sdcardfs_sb_info *sb_info; void *pkgl_id; + struct inode *inode; printk(KERN_INFO "sdcardfs version 2.0\n"); @@ -259,12 +262,18 @@ static int sdcardfs_read_super(struct super_block *sb, const char *dev_name, sb->s_magic = SDCARDFS_SUPER_MAGIC; sb->s_op = &sdcardfs_sops; - /* see comment next to the definition of sdcardfs_d_alloc_root */ - sb->s_root = sdcardfs_d_alloc_root(sb); + /* get a new inode and allocate our root dentry */ + inode = sdcardfs_iget(sb, lower_path.dentry->d_inode); + if (IS_ERR(inode)) { + err = PTR_ERR(inode); + goto out_sput; + } + sb->s_root = d_make_root(inode); if (!sb->s_root) { err = -ENOMEM; - goto out_sput; + goto out_iput; } + d_set_d_op(sb->s_root, &sdcardfs_ci_dops); /* link the upper and lower dentries */ sb->s_root->d_fsdata = NULL; @@ -275,56 +284,60 @@ static int sdcardfs_read_super(struct super_block *sb, const char *dev_name, /* set the lower dentries for s_root */ sdcardfs_set_lower_path(sb->s_root, &lower_path); - /* call interpose to create the upper level inode */ - err = sdcardfs_interpose(sb->s_root, sb, &lower_path); - if (!err) { - /* setup permission policy */ - switch(sb_info->options.derive) { - case DERIVE_NONE: - setup_derived_state(sb->s_root->d_inode, - PERM_ROOT, 0, AID_ROOT, AID_SDCARD_RW, 00775); - sb_info->obbpath_s = NULL; - break; - case DERIVE_LEGACY: - /* Legacy behavior used to support internal multiuser layout which - * places user_id at the top directory level, with the actual roots - * just below that. Shared OBB path is also at top level. */ - setup_derived_state(sb->s_root->d_inode, - PERM_LEGACY_PRE_ROOT, 0, AID_ROOT, AID_SDCARD_R, 00771); - /* initialize the obbpath string and lookup the path - * sb_info->obb_path will be deactivated by path_put - * on sdcardfs_put_super */ - sb_info->obbpath_s = kzalloc(PATH_MAX, GFP_KERNEL); - snprintf(sb_info->obbpath_s, PATH_MAX, "%s/obb", dev_name); - err = prepare_dir(sb_info->obbpath_s, - sb_info->options.fs_low_uid, - sb_info->options.fs_low_gid, 00755); - if(err) - printk(KERN_ERR "sdcardfs: %s: %d, error on creating %s\n", - __func__,__LINE__, sb_info->obbpath_s); - break; - case DERIVE_UNIFIED: - /* Unified multiuser layout which places secondary user_id under - * /Android/user and shared OBB path under /Android/obb. */ - setup_derived_state(sb->s_root->d_inode, - PERM_ROOT, 0, AID_ROOT, AID_SDCARD_R, 00771); - - sb_info->obbpath_s = kzalloc(PATH_MAX, GFP_KERNEL); - snprintf(sb_info->obbpath_s, PATH_MAX, "%s/Android/obb", dev_name); - break; - } - fix_derived_permission(sb->s_root->d_inode); + /* + * No need to call interpose because we already have a positive + * dentry, which was instantiated by d_make_root. Just need to + * d_rehash it. + */ + d_rehash(sb->s_root); - if (!silent) - printk(KERN_INFO "sdcardfs: mounted on top of %s type %s\n", - dev_name, lower_sb->s_type->name); - goto out; + /* setup permission policy */ + switch(sb_info->options.derive) { + case DERIVE_NONE: + setup_derived_state(sb->s_root->d_inode, + PERM_ROOT, 0, AID_ROOT, AID_SDCARD_RW, 00775); + sb_info->obbpath_s = NULL; + break; + case DERIVE_LEGACY: + /* Legacy behavior used to support internal multiuser layout which + * places user_id at the top directory level, with the actual roots + * just below that. Shared OBB path is also at top level. */ + setup_derived_state(sb->s_root->d_inode, + PERM_LEGACY_PRE_ROOT, 0, AID_ROOT, AID_SDCARD_R, 00771); + /* initialize the obbpath string and lookup the path + * sb_info->obb_path will be deactivated by path_put + * on sdcardfs_put_super */ + sb_info->obbpath_s = kzalloc(PATH_MAX, GFP_KERNEL); + snprintf(sb_info->obbpath_s, PATH_MAX, "%s/obb", dev_name); + err = prepare_dir(sb_info->obbpath_s, + sb_info->options.fs_low_uid, + sb_info->options.fs_low_gid, 00755); + if(err) + printk(KERN_ERR "sdcardfs: %s: %d, error on creating %s\n", + __func__,__LINE__, sb_info->obbpath_s); + break; + case DERIVE_UNIFIED: + /* Unified multiuser layout which places secondary user_id under + * /Android/user and shared OBB path under /Android/obb. */ + setup_derived_state(sb->s_root->d_inode, + PERM_ROOT, 0, AID_ROOT, AID_SDCARD_R, 00771); + + sb_info->obbpath_s = kzalloc(PATH_MAX, GFP_KERNEL); + snprintf(sb_info->obbpath_s, PATH_MAX, "%s/Android/obb", dev_name); + break; } - /* else error: fall through */ + fix_derived_permission(sb->s_root->d_inode); + + if (!silent) + printk(KERN_INFO "sdcardfs: mounted on top of %s type %s\n", + dev_name, lower_sb->s_type->name); + goto out; /* all is well */ - free_dentry_private_data(sb->s_root); + /* no longer needed: free_dentry_private_data(sb->s_root); */ out_freeroot: dput(sb->s_root); +out_iput: + iput(inode); out_sput: /* drop refs we took earlier */ atomic_dec(&lower_sb->s_active); @@ -346,7 +359,7 @@ static struct dentry *mount_nodev_with_options(struct file_system_type *fs_type, { int error; - struct super_block *s = sget(fs_type, NULL, set_anon_super, NULL); + struct super_block *s = sget(fs_type, NULL, set_anon_super, flags, NULL); if (IS_ERR(s)) return ERR_CAST(s); @@ -378,7 +391,7 @@ static struct file_system_type sdcardfs_fs_type = { .name = SDCARDFS_NAME, .mount = sdcardfs_mount, .kill_sb = generic_shutdown_super, - .fs_flags = FS_REVAL_DOT, + .fs_flags = 0, }; static int __init init_sdcardfs_fs(void) diff --git a/fs/sdcardfs/mmap.c b/fs/sdcardfs/mmap.c index c807d7f18f8b..e21f64675a80 100644 --- a/fs/sdcardfs/mmap.c +++ b/fs/sdcardfs/mmap.c @@ -48,9 +48,8 @@ static int sdcardfs_fault(struct vm_area_struct *vma, struct vm_fault *vmf) return err; } -static ssize_t sdcardfs_direct_IO(int rw, struct kiocb *iocb, - const struct iovec *iov, loff_t offset, - unsigned long nr_segs) +static ssize_t sdcardfs_direct_IO(struct kiocb *iocb, + struct iov_iter *iter, loff_t pos) { /* * This function returns zero on purpose in order to support direct IO. diff --git a/fs/sdcardfs/packagelist.c b/fs/sdcardfs/packagelist.c index c786d8f92203..d7ba8d4a423e 100644 --- a/fs/sdcardfs/packagelist.c +++ b/fs/sdcardfs/packagelist.c @@ -20,7 +20,7 @@ #include "sdcardfs.h" #include "strtok.h" -#include "hashtable.h" +#include #include #include #include @@ -29,8 +29,8 @@ #define STRING_BUF_SIZE (512) struct hashtable_entry { - struct hlist_node hlist; - void *key; + struct hlist_node hlist; + void *key; int value; }; @@ -67,12 +67,12 @@ static unsigned int str_hash(void *key) { } static int contain_appid_key(struct packagelist_data *pkgl_dat, void *appid) { - struct hashtable_entry *hash_cur; - struct hlist_node *h_n; + struct hashtable_entry *hash_cur; + + hash_for_each_possible(pkgl_dat->appid_with_rw, hash_cur, hlist, (unsigned int)appid) - hash_for_each_possible(pkgl_dat->appid_with_rw, hash_cur, hlist, (unsigned int)appid, h_n) - if (appid == hash_cur->key) - return 1; + if (appid == hash_cur->key) + return 1; return 0; } @@ -87,7 +87,7 @@ int get_caller_has_rw_locked(void *pkgl_id, derive_t derive) { return 1; } - appid = multiuser_get_app_id(current_fsuid()); + appid = multiuser_get_app_id(from_kuid(&init_user_ns, current_fsuid())); mutex_lock(&pkgl_dat->hashtable_lock); ret = contain_appid_key(pkgl_dat, (void *)appid); mutex_unlock(&pkgl_dat->hashtable_lock); @@ -98,13 +98,12 @@ appid_t get_appid(void *pkgl_id, const char *app_name) { struct packagelist_data *pkgl_dat = (struct packagelist_data *)pkgl_id; struct hashtable_entry *hash_cur; - struct hlist_node *h_n; unsigned int hash = str_hash((void *)app_name); appid_t ret_id; //printk(KERN_INFO "sdcardfs: %s: %s, %u\n", __func__, (char *)app_name, hash); mutex_lock(&pkgl_dat->hashtable_lock); - hash_for_each_possible(pkgl_dat->package_to_appid, hash_cur, hlist, hash, h_n) { + hash_for_each_possible(pkgl_dat->package_to_appid, hash_cur, hlist, hash) { //printk(KERN_INFO "sdcardfs: %s: %s\n", __func__, (char *)hash_cur->key); if (!strcasecmp(app_name, hash_cur->key)) { ret_id = (appid_t)hash_cur->value; @@ -140,7 +139,7 @@ int check_caller_access_to_name(struct inode *parent_node, const char* name, /* Root always has access; access for any other UIDs should always * be controlled through packages.list. */ - if (current_fsuid() == 0) { + if (from_kuid(&init_user_ns, current_fsuid()) == 0) { return 1; } @@ -148,7 +147,8 @@ int check_caller_access_to_name(struct inode *parent_node, const char* name, * parent or holds sdcard_rw. */ if (w_ok) { if (parent_node && - (current_fsuid() == SDCARDFS_I(parent_node)->d_uid)) { + (from_kuid(&init_user_ns, current_fsuid()) == + SDCARDFS_I(parent_node)->d_uid)) { return 1; } return has_rw; @@ -174,11 +174,10 @@ int open_flags_to_access_mode(int open_flags) { static int insert_str_to_int(struct packagelist_data *pkgl_dat, void *key, int value) { struct hashtable_entry *hash_cur; struct hashtable_entry *new_entry; - struct hlist_node *h_n; unsigned int hash = str_hash(key); //printk(KERN_INFO "sdcardfs: %s: %s: %d, %u\n", __func__, (char *)key, value, hash); - hash_for_each_possible(pkgl_dat->package_to_appid, hash_cur, hlist, hash, h_n) { + hash_for_each_possible(pkgl_dat->package_to_appid, hash_cur, hlist, hash) { if (!strcasecmp(key, hash_cur->key)) { hash_cur->value = value; return 0; @@ -202,11 +201,10 @@ static void remove_str_to_int(struct hashtable_entry *h_entry) { static int insert_int_to_null(struct packagelist_data *pkgl_dat, void *key, int value) { struct hashtable_entry *hash_cur; struct hashtable_entry *new_entry; - struct hlist_node *h_n; //printk(KERN_INFO "sdcardfs: %s: %d: %d\n", __func__, (int)key, value); hash_for_each_possible(pkgl_dat->appid_with_rw, hash_cur, hlist, - (unsigned int)key, h_n) { + (unsigned int)key) { if (key == hash_cur->key) { hash_cur->value = value; return 0; @@ -230,14 +228,13 @@ static void remove_int_to_null(struct hashtable_entry *h_entry) { static void remove_all_hashentrys(struct packagelist_data *pkgl_dat) { struct hashtable_entry *hash_cur; - struct hlist_node *h_n; struct hlist_node *h_t; int i; - hash_for_each_safe(pkgl_dat->package_to_appid, i, h_t, hash_cur, hlist, h_n) + hash_for_each_safe(pkgl_dat->package_to_appid, i, h_t, hash_cur, hlist) remove_str_to_int(hash_cur); - hash_for_each_safe(pkgl_dat->appid_with_rw, i, h_t, hash_cur, hlist, h_n) - remove_int_to_null(hash_cur); + hash_for_each_safe(pkgl_dat->appid_with_rw, i, h_t, hash_cur, hlist) + remove_int_to_null(hash_cur); hash_init(pkgl_dat->package_to_appid); hash_init(pkgl_dat->appid_with_rw); diff --git a/fs/sdcardfs/sdcardfs.h b/fs/sdcardfs/sdcardfs.h index 90f8b24e4a52..51f6c7912584 100644 --- a/fs/sdcardfs/sdcardfs.h +++ b/fs/sdcardfs/sdcardfs.h @@ -69,8 +69,8 @@ #define fix_derived_permission(x) \ do { \ - (x)->i_uid = SDCARDFS_I(x)->d_uid; \ - (x)->i_gid = SDCARDFS_I(x)->d_gid; \ + (x)->i_uid = make_kuid(&init_user_ns, SDCARDFS_I(x)->d_uid); \ + (x)->i_gid = make_kgid(&init_user_ns, SDCARDFS_I(x)->d_gid); \ (x)->i_mode = ((x)->i_mode & S_IFMT) | SDCARDFS_I(x)->d_mode;\ } while (0) @@ -159,7 +159,9 @@ extern void sdcardfs_destroy_dentry_cache(void); extern int new_dentry_private_data(struct dentry *dentry); extern void free_dentry_private_data(struct dentry *dentry); extern struct dentry *sdcardfs_lookup(struct inode *dir, struct dentry *dentry, - struct nameidata *nd); + unsigned int flags); +extern struct inode *sdcardfs_iget(struct super_block *sb, + struct inode *lower_inode); extern int sdcardfs_interpose(struct dentry *dentry, struct super_block *sb, struct path *lower_path); @@ -387,13 +389,13 @@ extern int setup_obb_dentry(struct dentry *dentry, struct path *lower_path); static inline struct dentry *lock_parent(struct dentry *dentry) { struct dentry *dir = dget_parent(dentry); - mutex_lock_nested(&dir->d_inode->i_mutex, I_MUTEX_PARENT); + mutex_lock_nested(&d_inode(dir)->i_mutex, I_MUTEX_PARENT); return dir; } static inline void unlock_dir(struct dentry *dir) { - mutex_unlock(&dir->d_inode->i_mutex); + mutex_unlock(&d_inode(dir)->i_mutex); dput(dir); } @@ -402,16 +404,9 @@ static inline int prepare_dir(const char *path_s, uid_t uid, gid_t gid, mode_t m int err; struct dentry *dent; struct iattr attrs; - struct nameidata nd; + struct path parent; - err = kern_path_parent(path_s, &nd); - if (err) { - if (err == -EEXIST) - err = 0; - goto out; - } - - dent = lookup_create(&nd, 1); + dent = kern_path_locked(path_s, &parent); if (IS_ERR(dent)) { err = PTR_ERR(dent); if (err == -EEXIST) @@ -419,29 +414,27 @@ static inline int prepare_dir(const char *path_s, uid_t uid, gid_t gid, mode_t m goto out_unlock; } - err = vfs_mkdir(nd.path.dentry->d_inode, dent, mode); + err = vfs_mkdir(d_inode(parent.dentry), dent, mode); if (err) { if (err == -EEXIST) err = 0; goto out_dput; } - attrs.ia_uid = uid; - attrs.ia_gid = gid; + attrs.ia_uid = make_kuid(&init_user_ns, uid); + attrs.ia_gid = make_kgid(&init_user_ns, gid); attrs.ia_valid = ATTR_UID | ATTR_GID; - mutex_lock(&dent->d_inode->i_mutex); - notify_change(dent, &attrs); - mutex_unlock(&dent->d_inode->i_mutex); + mutex_lock(&d_inode(dent)->i_mutex); + notify_change(dent, &attrs, NULL); + mutex_unlock(&d_inode(dent)->i_mutex); out_dput: dput(dent); out_unlock: /* parent dentry locked by lookup_create */ - mutex_unlock(&nd.path.dentry->d_inode->i_mutex); - path_put(&nd.path); - -out: + mutex_unlock(&d_inode(parent.dentry)->i_mutex); + path_put(&parent); return err; } diff --git a/fs/sdcardfs/super.c b/fs/sdcardfs/super.c index 1d206c82dfdf..f153ce1b8cf3 100644 --- a/fs/sdcardfs/super.c +++ b/fs/sdcardfs/super.c @@ -122,7 +122,7 @@ static void sdcardfs_evict_inode(struct inode *inode) struct inode *lower_inode; truncate_inode_pages(&inode->i_data, 0); - end_writeback(inode); + clear_inode(inode); /* * Decrement a reference to a lower_inode, which was incremented * by our read_inode when it was created initially. @@ -193,9 +193,9 @@ static void sdcardfs_umount_begin(struct super_block *sb) lower_sb->s_op->umount_begin(lower_sb); } -static int sdcardfs_show_options(struct seq_file *m, struct vfsmount *mnt) +static int sdcardfs_show_options(struct seq_file *m, struct dentry *root) { - struct sdcardfs_sb_info *sbi = SDCARDFS_SB(mnt->mnt_sb); + struct sdcardfs_sb_info *sbi = SDCARDFS_SB(root->d_sb); struct sdcardfs_mount_options *opts = &sbi->options; if (opts->fs_low_uid != 0) diff --git a/include/linux/namei.h b/include/linux/namei.h index 788f3cbc12b7..5f0abcf0aff4 100644 --- a/include/linux/namei.h +++ b/include/linux/namei.h @@ -80,6 +80,8 @@ extern struct dentry *user_path_create(int, const char __user *, struct path *, extern void done_path_create(struct path *, struct dentry *); extern struct dentry *kern_path_locked(const char *, struct path *); extern int kern_path_mountpoint(int, const char *, struct path *, unsigned int); +extern int vfs_path_lookup(struct dentry *, struct vfsmount *, + const char *, unsigned int, struct path *); extern struct dentry *try_lookup_one_len(const char *, struct dentry *, int); extern struct dentry *lookup_one_len(const char *, struct dentry *, int); From 9411c122ad040bc92f2d51140cbfdd570d8e6cab Mon Sep 17 00:00:00 2001 From: Daniel Campello Date: Mon, 20 Jul 2015 16:33:46 -0700 Subject: [PATCH 0292/1103] ANDROID: Changed type-casting in packagelist management Fixed existing type-casting in packagelist management code. All warnings at compile time were taken care of. Change-Id: I1ea97786d1d1325f31b9f09ae966af1f896a2af5 Signed-off-by: Daniel Campello --- fs/sdcardfs/packagelist.c | 40 ++++++++++++++++++++------------------- 1 file changed, 21 insertions(+), 19 deletions(-) diff --git a/fs/sdcardfs/packagelist.c b/fs/sdcardfs/packagelist.c index d7ba8d4a423e..f11591da141d 100644 --- a/fs/sdcardfs/packagelist.c +++ b/fs/sdcardfs/packagelist.c @@ -31,7 +31,7 @@ struct hashtable_entry { struct hlist_node hlist; void *key; - int value; + unsigned int value; }; struct packagelist_data { @@ -54,7 +54,7 @@ static const char* const kpackageslist_file = "/data/system/packages.list"; /* Supplementary groups to execute with */ static const gid_t kgroups[1] = { AID_PACKAGE_INFO }; -static unsigned int str_hash(void *key) { +static unsigned int str_hash(const char *key) { int i; unsigned int h = strlen(key); char *data = (char *)key; @@ -66,13 +66,13 @@ static unsigned int str_hash(void *key) { return h; } -static int contain_appid_key(struct packagelist_data *pkgl_dat, void *appid) { +static int contain_appid_key(struct packagelist_data *pkgl_dat, unsigned int appid) { struct hashtable_entry *hash_cur; - hash_for_each_possible(pkgl_dat->appid_with_rw, hash_cur, hlist, (unsigned int)appid) - - if (appid == hash_cur->key) + hash_for_each_possible(pkgl_dat->appid_with_rw, hash_cur, hlist, appid) + if ((void *)(uintptr_t)appid == hash_cur->key) return 1; + return 0; } @@ -89,7 +89,7 @@ int get_caller_has_rw_locked(void *pkgl_id, derive_t derive) { appid = multiuser_get_app_id(from_kuid(&init_user_ns, current_fsuid())); mutex_lock(&pkgl_dat->hashtable_lock); - ret = contain_appid_key(pkgl_dat, (void *)appid); + ret = contain_appid_key(pkgl_dat, appid); mutex_unlock(&pkgl_dat->hashtable_lock); return ret; } @@ -98,7 +98,7 @@ appid_t get_appid(void *pkgl_id, const char *app_name) { struct packagelist_data *pkgl_dat = (struct packagelist_data *)pkgl_id; struct hashtable_entry *hash_cur; - unsigned int hash = str_hash((void *)app_name); + unsigned int hash = str_hash(app_name); appid_t ret_id; //printk(KERN_INFO "sdcardfs: %s: %s, %u\n", __func__, (char *)app_name, hash); @@ -171,7 +171,9 @@ int open_flags_to_access_mode(int open_flags) { } } -static int insert_str_to_int(struct packagelist_data *pkgl_dat, void *key, int value) { +static int insert_str_to_int(struct packagelist_data *pkgl_dat, char *key, + unsigned int value) +{ struct hashtable_entry *hash_cur; struct hashtable_entry *new_entry; unsigned int hash = str_hash(key); @@ -198,14 +200,15 @@ static void remove_str_to_int(struct hashtable_entry *h_entry) { kmem_cache_free(hashtable_entry_cachep, h_entry); } -static int insert_int_to_null(struct packagelist_data *pkgl_dat, void *key, int value) { +static int insert_int_to_null(struct packagelist_data *pkgl_dat, unsigned int key, + unsigned int value) +{ struct hashtable_entry *hash_cur; struct hashtable_entry *new_entry; //printk(KERN_INFO "sdcardfs: %s: %d: %d\n", __func__, (int)key, value); - hash_for_each_possible(pkgl_dat->appid_with_rw, hash_cur, hlist, - (unsigned int)key) { - if (key == hash_cur->key) { + hash_for_each_possible(pkgl_dat->appid_with_rw, hash_cur, hlist, key) { + if ((void *)(uintptr_t)key == hash_cur->key) { hash_cur->value = value; return 0; } @@ -213,10 +216,9 @@ static int insert_int_to_null(struct packagelist_data *pkgl_dat, void *key, int new_entry = kmem_cache_alloc(hashtable_entry_cachep, GFP_KERNEL); if (!new_entry) return -ENOMEM; - new_entry->key = key; + new_entry->key = (void *)(uintptr_t)key; new_entry->value = value; - hash_add(pkgl_dat->appid_with_rw, &new_entry->hlist, - (unsigned int)new_entry->key); + hash_add(pkgl_dat->appid_with_rw, &new_entry->hlist, key); return 0; } @@ -260,7 +262,7 @@ static int read_package_list(struct packagelist_data *pkgl_dat) { while ((read_amount = sys_read(fd, pkgl_dat->read_buf, sizeof(pkgl_dat->read_buf))) > 0) { - int appid; + unsigned int appid; char *token; int one_line_len = 0; int additional_read; @@ -277,7 +279,7 @@ static int read_package_list(struct packagelist_data *pkgl_dat) { if (additional_read > 0) sys_lseek(fd, -additional_read, SEEK_CUR); - if (sscanf(pkgl_dat->read_buf, "%s %d %*d %*s %*s %s", + if (sscanf(pkgl_dat->read_buf, "%s %u %*d %*s %*s %s", pkgl_dat->app_name_buf, &appid, pkgl_dat->gids_buf) == 3) { ret = insert_str_to_int(pkgl_dat, pkgl_dat->app_name_buf, appid); @@ -291,7 +293,7 @@ static int read_package_list(struct packagelist_data *pkgl_dat) { while (token != NULL) { if (!kstrtoul(token, 10, &ret_gid) && (ret_gid == pkgl_dat->write_gid)) { - ret = insert_int_to_null(pkgl_dat, (void *)appid, 1); + ret = insert_int_to_null(pkgl_dat, appid, 1); if (ret) { sys_close(fd); mutex_unlock(&pkgl_dat->hashtable_lock); From 31bd83e1a27768a15a516cfa973cebe21365ee5f Mon Sep 17 00:00:00 2001 From: Daniel Rosenberg Date: Wed, 3 Feb 2016 21:08:21 -0800 Subject: [PATCH 0293/1103] ANDROID: sdcardfs: Bring up to date with Android M permissions: In M, the workings of sdcardfs were changed significantly. This brings sdcardfs into line with the changes. Change-Id: I10e91a84a884c838feef7aa26c0a2b21f02e052e --- fs/sdcardfs/Kconfig | 1 + fs/sdcardfs/derived_perm.c | 119 ++++----- fs/sdcardfs/file.c | 10 +- fs/sdcardfs/inode.c | 78 +++--- fs/sdcardfs/lookup.c | 40 +-- fs/sdcardfs/main.c | 141 +++++------ fs/sdcardfs/packagelist.c | 504 ++++++++++++++++++------------------- fs/sdcardfs/sdcardfs.h | 134 ++++++---- fs/sdcardfs/strtok.h | 75 ------ fs/sdcardfs/super.c | 11 +- 10 files changed, 501 insertions(+), 612 deletions(-) delete mode 100644 fs/sdcardfs/strtok.h diff --git a/fs/sdcardfs/Kconfig b/fs/sdcardfs/Kconfig index d995f3eaae6d..ab25f88ebb37 100644 --- a/fs/sdcardfs/Kconfig +++ b/fs/sdcardfs/Kconfig @@ -1,5 +1,6 @@ config SDCARD_FS tristate "sdcard file system" + depends on CONFIGFS_FS default n help Sdcardfs is based on Wrapfs file system. diff --git a/fs/sdcardfs/derived_perm.c b/fs/sdcardfs/derived_perm.c index 00c33a471dcc..128b3e56851f 100644 --- a/fs/sdcardfs/derived_perm.c +++ b/fs/sdcardfs/derived_perm.c @@ -29,24 +29,23 @@ static void inherit_derived_state(struct inode *parent, struct inode *child) ci->perm = PERM_INHERIT; ci->userid = pi->userid; ci->d_uid = pi->d_uid; - ci->d_gid = pi->d_gid; - ci->d_mode = pi->d_mode; + ci->under_android = pi->under_android; } /* helper function for derived state */ void setup_derived_state(struct inode *inode, perm_t perm, - userid_t userid, uid_t uid, gid_t gid, mode_t mode) + userid_t userid, uid_t uid, bool under_android) { struct sdcardfs_inode_info *info = SDCARDFS_I(inode); info->perm = perm; info->userid = userid; info->d_uid = uid; - info->d_gid = gid; - info->d_mode = mode; + info->under_android = under_android; } -void get_derived_permission(struct dentry *parent, struct dentry *dentry) +/* While renaming, there is a point where we want the path from dentry, but the name from newdentry */ +void get_derived_permission_new(struct dentry *parent, struct dentry *dentry, struct dentry *newdentry) { struct sdcardfs_sb_info *sbi = SDCARDFS_SB(dentry->d_sb); struct sdcardfs_inode_info *info = SDCARDFS_I(dentry->d_inode); @@ -63,86 +62,68 @@ void get_derived_permission(struct dentry *parent, struct dentry *dentry) inherit_derived_state(parent->d_inode, dentry->d_inode); - //printk(KERN_INFO "sdcardfs: derived: %s, %s, %d\n", parent->d_name.name, - // dentry->d_name.name, parent_info->perm); - - if (sbi->options.derive == DERIVE_NONE) { - return; - } - /* Derive custom permissions based on parent and current node */ switch (parent_info->perm) { case PERM_INHERIT: /* Already inherited above */ break; - case PERM_LEGACY_PRE_ROOT: + case PERM_PRE_ROOT: /* Legacy internal layout places users at top level */ info->perm = PERM_ROOT; - info->userid = simple_strtoul(dentry->d_name.name, NULL, 10); + info->userid = simple_strtoul(newdentry->d_name.name, NULL, 10); break; case PERM_ROOT: /* Assume masked off by default. */ - info->d_mode = 00770; - if (!strcasecmp(dentry->d_name.name, "Android")) { + if (!strcasecmp(newdentry->d_name.name, "Android")) { /* App-specific directories inside; let anyone traverse */ info->perm = PERM_ANDROID; - info->d_mode = 00771; - } else if (sbi->options.split_perms) { - if (!strcasecmp(dentry->d_name.name, "DCIM") - || !strcasecmp(dentry->d_name.name, "Pictures")) { - info->d_gid = AID_SDCARD_PICS; - } else if (!strcasecmp(dentry->d_name.name, "Alarms") - || !strcasecmp(dentry->d_name.name, "Movies") - || !strcasecmp(dentry->d_name.name, "Music") - || !strcasecmp(dentry->d_name.name, "Notifications") - || !strcasecmp(dentry->d_name.name, "Podcasts") - || !strcasecmp(dentry->d_name.name, "Ringtones")) { - info->d_gid = AID_SDCARD_AV; - } + info->under_android = true; } break; case PERM_ANDROID: - if (!strcasecmp(dentry->d_name.name, "data")) { + if (!strcasecmp(newdentry->d_name.name, "data")) { /* App-specific directories inside; let anyone traverse */ info->perm = PERM_ANDROID_DATA; - info->d_mode = 00771; - } else if (!strcasecmp(dentry->d_name.name, "obb")) { + } else if (!strcasecmp(newdentry->d_name.name, "obb")) { /* App-specific directories inside; let anyone traverse */ info->perm = PERM_ANDROID_OBB; - info->d_mode = 00771; - // FIXME : this feature will be implemented later. /* Single OBB directory is always shared */ - } else if (!strcasecmp(dentry->d_name.name, "user")) { - /* User directories must only be accessible to system, protected - * by sdcard_all. Zygote will bind mount the appropriate user- - * specific path. */ - info->perm = PERM_ANDROID_USER; - info->d_gid = AID_SDCARD_ALL; - info->d_mode = 00770; + } else if (!strcasecmp(newdentry->d_name.name, "media")) { + /* App-specific directories inside; let anyone traverse */ + info->perm = PERM_ANDROID_MEDIA; } break; - /* same policy will be applied on PERM_ANDROID_DATA - * and PERM_ANDROID_OBB */ case PERM_ANDROID_DATA: case PERM_ANDROID_OBB: - appid = get_appid(sbi->pkgl_id, dentry->d_name.name); + case PERM_ANDROID_MEDIA: + appid = get_appid(sbi->pkgl_id, newdentry->d_name.name); if (appid != 0) { info->d_uid = multiuser_get_uid(parent_info->userid, appid); } - info->d_mode = 00770; - break; - case PERM_ANDROID_USER: - /* Root of a secondary user */ - info->perm = PERM_ROOT; - info->userid = simple_strtoul(dentry->d_name.name, NULL, 10); - info->d_gid = AID_SDCARD_R; - info->d_mode = 00771; break; } } +void get_derived_permission(struct dentry *parent, struct dentry *dentry) +{ + get_derived_permission_new(parent, dentry, dentry); +} + +void get_derive_permissions_recursive(struct dentry *parent) { + struct dentry *dentry; + list_for_each_entry(dentry, &parent->d_subdirs, d_child) { + if (dentry && dentry->d_inode) { + mutex_lock(&dentry->d_inode->i_mutex); + get_derived_permission(parent, dentry); + fix_derived_permission(dentry->d_inode); + get_derive_permissions_recursive(dentry); + mutex_unlock(&dentry->d_inode->i_mutex); + } + } +} + /* main function for updating derived permission */ -inline void update_derived_permission(struct dentry *dentry) +inline void update_derived_permission_lock(struct dentry *dentry) { struct dentry *parent; @@ -154,6 +135,7 @@ inline void update_derived_permission(struct dentry *dentry) * 1. need to check whether the dentry is updated or not * 2. remove the root dentry update */ + mutex_lock(&dentry->d_inode->i_mutex); if(IS_ROOT(dentry)) { //setup_default_pre_root_state(dentry->d_inode); } else { @@ -164,6 +146,7 @@ inline void update_derived_permission(struct dentry *dentry) } } fix_derived_permission(dentry->d_inode); + mutex_unlock(&dentry->d_inode->i_mutex); } int need_graft_path(struct dentry *dentry) @@ -177,7 +160,7 @@ int need_graft_path(struct dentry *dentry) !strcasecmp(dentry->d_name.name, "obb")) { /* /Android/obb is the base obbpath of DERIVED_UNIFIED */ - if(!(sbi->options.derive == DERIVE_UNIFIED + if(!(sbi->options.multiuser == false && parent_info->userid == 0)) { ret = 1; } @@ -207,8 +190,7 @@ int is_obbpath_invalid(struct dentry *dent) path_buf = kmalloc(PATH_MAX, GFP_ATOMIC); if(!path_buf) { ret = 1; - printk(KERN_ERR "sdcardfs: " - "fail to allocate path_buf in %s.\n", __func__); + printk(KERN_ERR "sdcardfs: fail to allocate path_buf in %s.\n", __func__); } else { obbpath_s = d_path(&di->lower_path, path_buf, PATH_MAX); if (d_unhashed(di->lower_path.dentry) || @@ -234,21 +216,16 @@ int is_base_obbpath(struct dentry *dentry) struct sdcardfs_sb_info *sbi = SDCARDFS_SB(dentry->d_sb); spin_lock(&SDCARDFS_D(dentry)->lock); - /* DERIVED_LEGACY */ - if(parent_info->perm == PERM_LEGACY_PRE_ROOT && - !strcasecmp(dentry->d_name.name, "obb")) { - ret = 1; - } - /* DERIVED_UNIFIED :/Android/obb is the base obbpath */ - else if (parent_info->perm == PERM_ANDROID && - !strcasecmp(dentry->d_name.name, "obb")) { - if((sbi->options.derive == DERIVE_UNIFIED - && parent_info->userid == 0)) { + if (sbi->options.multiuser) { + if(parent_info->perm == PERM_PRE_ROOT && + !strcasecmp(dentry->d_name.name, "obb")) { ret = 1; } + } else if (parent_info->perm == PERM_ANDROID && + !strcasecmp(dentry->d_name.name, "obb")) { + ret = 1; } spin_unlock(&SDCARDFS_D(dentry)->lock); - dput(parent); return ret; } @@ -272,8 +249,7 @@ int setup_obb_dentry(struct dentry *dentry, struct path *lower_path) if(!err) { /* the obbpath base has been found */ - printk(KERN_INFO "sdcardfs: " - "the sbi->obbpath is found\n"); + printk(KERN_INFO "sdcardfs: the sbi->obbpath is found\n"); pathcpy(lower_path, &obbpath); } else { /* if the sbi->obbpath is not available, we can optionally @@ -281,8 +257,7 @@ int setup_obb_dentry(struct dentry *dentry, struct path *lower_path) * but, the current implementation just returns an error * because the sdcard daemon also regards this case as * a lookup fail. */ - printk(KERN_INFO "sdcardfs: " - "the sbi->obbpath is not available\n"); + printk(KERN_INFO "sdcardfs: the sbi->obbpath is not available\n"); } return err; } diff --git a/fs/sdcardfs/file.c b/fs/sdcardfs/file.c index f9c5eaafc619..c249fa982d3c 100644 --- a/fs/sdcardfs/file.c +++ b/fs/sdcardfs/file.c @@ -209,7 +209,6 @@ static int sdcardfs_open(struct inode *inode, struct file *file) struct dentry *parent = dget_parent(dentry); struct sdcardfs_sb_info *sbi = SDCARDFS_SB(dentry->d_sb); const struct cred *saved_cred = NULL; - int has_rw; /* don't open unhashed/deleted files */ if (d_unhashed(dentry)) { @@ -217,11 +216,7 @@ static int sdcardfs_open(struct inode *inode, struct file *file) goto out_err; } - has_rw = get_caller_has_rw_locked(sbi->pkgl_id, sbi->options.derive); - - if(!check_caller_access_to_name(parent->d_inode, dentry->d_name.name, - sbi->options.derive, - open_flags_to_access_mode(file->f_flags), has_rw)) { + if(!check_caller_access_to_name(parent->d_inode, dentry->d_name.name)) { printk(KERN_INFO "%s: need to check the caller's gid in packages.list\n" " dentry: %s, task:%s\n", __func__, dentry->d_name.name, current->comm); @@ -257,8 +252,7 @@ static int sdcardfs_open(struct inode *inode, struct file *file) if (err) kfree(SDCARDFS_F(file)); else { - fsstack_copy_attr_all(inode, sdcardfs_lower_inode(inode)); - fix_derived_permission(inode); + sdcardfs_copy_and_fix_attrs(inode, sdcardfs_lower_inode(inode)); } out_revert_cred: diff --git a/fs/sdcardfs/inode.c b/fs/sdcardfs/inode.c index 75c622bac2f5..2528da0d3ae1 100644 --- a/fs/sdcardfs/inode.c +++ b/fs/sdcardfs/inode.c @@ -55,11 +55,9 @@ static int sdcardfs_create(struct inode *dir, struct dentry *dentry, struct dentry *lower_dentry; struct dentry *lower_parent_dentry = NULL; struct path lower_path; - struct sdcardfs_sb_info *sbi = SDCARDFS_SB(dentry->d_sb); const struct cred *saved_cred = NULL; - int has_rw = get_caller_has_rw_locked(sbi->pkgl_id, sbi->options.derive); - if(!check_caller_access_to_name(dir, dentry->d_name.name, sbi->options.derive, 1, has_rw)) { + if(!check_caller_access_to_name(dir, dentry->d_name.name)) { printk(KERN_INFO "%s: need to check the caller's gid in packages.list\n" " dentry: %s, task:%s\n", __func__, dentry->d_name.name, current->comm); @@ -80,7 +78,7 @@ static int sdcardfs_create(struct inode *dir, struct dentry *dentry, if (err) goto out; - err = sdcardfs_interpose(dentry, dir->i_sb, &lower_path); + err = sdcardfs_interpose(dentry, dir->i_sb, &lower_path, SDCARDFS_I(dir)->userid); if (err) goto out; fsstack_copy_attr_times(dir, sdcardfs_lower_inode(dir)); @@ -143,11 +141,9 @@ static int sdcardfs_unlink(struct inode *dir, struct dentry *dentry) struct inode *lower_dir_inode = sdcardfs_lower_inode(dir); struct dentry *lower_dir_dentry; struct path lower_path; - struct sdcardfs_sb_info *sbi = SDCARDFS_SB(dentry->d_sb); const struct cred *saved_cred = NULL; - int has_rw = get_caller_has_rw_locked(sbi->pkgl_id, sbi->options.derive); - if(!check_caller_access_to_name(dir, dentry->d_name.name, sbi->options.derive, 1, has_rw)) { + if(!check_caller_access_to_name(dir, dentry->d_name.name)) { printk(KERN_INFO "%s: need to check the caller's gid in packages.list\n" " dentry: %s, task:%s\n", __func__, dentry->d_name.name, current->comm); @@ -255,8 +251,7 @@ static int sdcardfs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode int fullpath_namelen; int touch_err = 0; - int has_rw = get_caller_has_rw_locked(sbi->pkgl_id, sbi->options.derive); - if(!check_caller_access_to_name(dir, dentry->d_name.name, sbi->options.derive, 1, has_rw)) { + if(!check_caller_access_to_name(dir, dentry->d_name.name)) { printk(KERN_INFO "%s: need to check the caller's gid in packages.list\n" " dentry: %s, task:%s\n", __func__, dentry->d_name.name, current->comm); @@ -293,19 +288,19 @@ static int sdcardfs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode if(err) { /* if the sbi->obbpath is not available, the lower_path won't be * changed by setup_obb_dentry() but the lower path is saved to - * its orig_path. this dentry will be revalidated later. + * its orig_path. this dentry will be revalidated later. * but now, the lower_path should be NULL */ sdcardfs_put_reset_lower_path(dentry); /* the newly created lower path which saved to its orig_path or * the lower_path is the base obbpath. - * therefore, an additional path_get is required */ + * therefore, an additional path_get is required */ path_get(&lower_path); } else make_nomedia_in_obb = 1; } - err = sdcardfs_interpose(dentry, dir->i_sb, &lower_path); + err = sdcardfs_interpose(dentry, dir->i_sb, &lower_path, pi->userid); if (err) goto out; @@ -314,7 +309,7 @@ static int sdcardfs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode /* update number of links on parent directory */ set_nlink(dir, sdcardfs_lower_inode(dir)->i_nlink); - if ((sbi->options.derive == DERIVE_UNIFIED) && (!strcasecmp(dentry->d_name.name, "obb")) + if ((!sbi->options.multiuser) && (!strcasecmp(dentry->d_name.name, "obb")) && (pi->perm == PERM_ANDROID) && (pi->userid == 0)) make_nomedia_in_obb = 1; @@ -371,12 +366,9 @@ static int sdcardfs_rmdir(struct inode *dir, struct dentry *dentry) struct dentry *lower_dir_dentry; int err; struct path lower_path; - struct sdcardfs_sb_info *sbi = SDCARDFS_SB(dentry->d_sb); const struct cred *saved_cred = NULL; - //char *path_s = NULL; - int has_rw = get_caller_has_rw_locked(sbi->pkgl_id, sbi->options.derive); - if(!check_caller_access_to_name(dir, dentry->d_name.name, sbi->options.derive, 1, has_rw)) { + if(!check_caller_access_to_name(dir, dentry->d_name.name)) { printk(KERN_INFO "%s: need to check the caller's gid in packages.list\n" " dentry: %s, task:%s\n", __func__, dentry->d_name.name, current->comm); @@ -461,14 +453,10 @@ static int sdcardfs_rename(struct inode *old_dir, struct dentry *old_dentry, struct dentry *trap = NULL; struct dentry *new_parent = NULL; struct path lower_old_path, lower_new_path; - struct sdcardfs_sb_info *sbi = SDCARDFS_SB(old_dentry->d_sb); const struct cred *saved_cred = NULL; - int has_rw = get_caller_has_rw_locked(sbi->pkgl_id, sbi->options.derive); - if(!check_caller_access_to_name(old_dir, old_dentry->d_name.name, - sbi->options.derive, 1, has_rw) || - !check_caller_access_to_name(new_dir, new_dentry->d_name.name, - sbi->options.derive, 1, has_rw)) { + if(!check_caller_access_to_name(old_dir, old_dentry->d_name.name) || + !check_caller_access_to_name(new_dir, new_dentry->d_name.name)) { printk(KERN_INFO "%s: need to check the caller's gid in packages.list\n" " new_dentry: %s, task:%s\n", __func__, new_dentry->d_name.name, current->comm); @@ -505,26 +493,31 @@ static int sdcardfs_rename(struct inode *old_dir, struct dentry *old_dentry, goto out; /* Copy attrs from lower dir, but i_uid/i_gid */ - fsstack_copy_attr_all(new_dir, d_inode(lower_new_dir_dentry)); + sdcardfs_copy_and_fix_attrs(new_dir, d_inode(lower_new_dir_dentry)); fsstack_copy_inode_size(new_dir, d_inode(lower_new_dir_dentry)); - fix_derived_permission(new_dir); + if (new_dir != old_dir) { - fsstack_copy_attr_all(old_dir, d_inode(lower_old_dir_dentry)); + sdcardfs_copy_and_fix_attrs(old_dir, d_inode(lower_old_dir_dentry)); fsstack_copy_inode_size(old_dir, d_inode(lower_old_dir_dentry)); - fix_derived_permission(old_dir); + /* update the derived permission of the old_dentry * with its new parent */ new_parent = dget_parent(new_dentry); if(new_parent) { if(d_inode(old_dentry)) { - get_derived_permission(new_parent, old_dentry); - fix_derived_permission(d_inode(old_dentry)); + update_derived_permission_lock(old_dentry); } dput(new_parent); } } - + /* At this point, not all dentry information has been moved, so + * we pass along new_dentry for the name.*/ + mutex_lock(&d_inode(old_dentry)->i_mutex); + get_derived_permission_new(new_dentry->d_parent, old_dentry, new_dentry); + fix_derived_permission(d_inode(old_dentry)); + get_derive_permissions_recursive(old_dentry); + mutex_unlock(&d_inode(old_dentry)->i_mutex); out: unlock_rename(lower_old_dir_dentry, lower_new_dir_dentry); dput(lower_old_dir_dentry); @@ -639,9 +632,7 @@ static int sdcardfs_setattr(struct dentry *dentry, struct iattr *ia) struct inode *lower_inode; struct path lower_path; struct iattr lower_ia; - struct sdcardfs_sb_info *sbi = SDCARDFS_SB(dentry->d_sb); struct dentry *parent; - int has_rw; inode = d_inode(dentry); @@ -655,10 +646,8 @@ static int sdcardfs_setattr(struct dentry *dentry, struct iattr *ia) /* no vfs_XXX operations required, cred overriding will be skipped. wj*/ if (!err) { /* check the Android group ID */ - has_rw = get_caller_has_rw_locked(sbi->pkgl_id, sbi->options.derive); parent = dget_parent(dentry); - if(!check_caller_access_to_name(d_inode(parent), dentry->d_name.name, - sbi->options.derive, 1, has_rw)) { + if(!check_caller_access_to_name(d_inode(parent), dentry->d_name.name)) { printk(KERN_INFO "%s: need to check the caller's gid in packages.list\n" " dentry: %s, task:%s\n", __func__, dentry->d_name.name, current->comm); @@ -723,10 +712,8 @@ static int sdcardfs_setattr(struct dentry *dentry, struct iattr *ia) if (err) goto out; - /* get attributes from the lower inode */ - fsstack_copy_attr_all(inode, lower_inode); - /* update derived permission of the upper inode */ - fix_derived_permission(inode); + /* get attributes from the lower inode and update derived permissions */ + sdcardfs_copy_and_fix_attrs(inode, lower_inode); /* * Not running fsstack_copy_inode_size(inode, lower_inode), because @@ -748,11 +735,9 @@ static int sdcardfs_getattr(struct vfsmount *mnt, struct dentry *dentry, struct inode *lower_inode; struct path lower_path; struct dentry *parent; - struct sdcardfs_sb_info *sbi = SDCARDFS_SB(dentry->d_sb); parent = dget_parent(dentry); - if(!check_caller_access_to_name(d_inode(parent), dentry->d_name.name, - sbi->options.derive, 0, 0)) { + if(!check_caller_access_to_name(d_inode(parent), dentry->d_name.name)) { printk(KERN_INFO "%s: need to check the caller's gid in packages.list\n" " dentry: %s, task:%s\n", __func__, dentry->d_name.name, current->comm); @@ -767,13 +752,10 @@ static int sdcardfs_getattr(struct vfsmount *mnt, struct dentry *dentry, lower_dentry = lower_path.dentry; lower_inode = sdcardfs_lower_inode(inode); - fsstack_copy_attr_all(inode, lower_inode); + + sdcardfs_copy_and_fix_attrs(inode, lower_inode); fsstack_copy_inode_size(inode, lower_inode); - /* if the dentry has been moved from other location - * so, on this stage, its derived permission must be - * rechecked from its private field. - */ - fix_derived_permission(inode); + generic_fillattr(inode, stat); sdcardfs_put_lower_path(dentry, &lower_path); diff --git a/fs/sdcardfs/lookup.c b/fs/sdcardfs/lookup.c index a4b94df99f32..f80abcb6b467 100644 --- a/fs/sdcardfs/lookup.c +++ b/fs/sdcardfs/lookup.c @@ -64,10 +64,17 @@ int new_dentry_private_data(struct dentry *dentry) return 0; } -static int sdcardfs_inode_test(struct inode *inode, void *candidate_lower_inode) +struct inode_data { + struct inode *lower_inode; + userid_t id; +}; + +static int sdcardfs_inode_test(struct inode *inode, void *candidate_data/*void *candidate_lower_inode*/) { struct inode *current_lower_inode = sdcardfs_lower_inode(inode); - if (current_lower_inode == (struct inode *)candidate_lower_inode) + userid_t current_userid = SDCARDFS_I(inode)->userid; + if (current_lower_inode == ((struct inode_data *)candidate_data)->lower_inode && + current_userid == ((struct inode_data *)candidate_data)->id) return 1; /* found a match */ else return 0; /* no match */ @@ -79,12 +86,15 @@ static int sdcardfs_inode_set(struct inode *inode, void *lower_inode) return 0; } -struct inode *sdcardfs_iget(struct super_block *sb, struct inode *lower_inode) +struct inode *sdcardfs_iget(struct super_block *sb, struct inode *lower_inode, userid_t id) { struct sdcardfs_inode_info *info; + struct inode_data data; struct inode *inode; /* the new inode to return */ int err; + data.id = id; + data.lower_inode = lower_inode; inode = iget5_locked(sb, /* our superblock */ /* * hashval: we use inode number, but we can @@ -94,7 +104,7 @@ struct inode *sdcardfs_iget(struct super_block *sb, struct inode *lower_inode) lower_inode->i_ino, /* hashval */ sdcardfs_inode_test, /* inode comparison function */ sdcardfs_inode_set, /* inode init function */ - lower_inode); /* data passed to test+set fxns */ + &data); /* data passed to test+set fxns */ if (!inode) { err = -EACCES; iput(lower_inode); @@ -146,11 +156,9 @@ struct inode *sdcardfs_iget(struct super_block *sb, struct inode *lower_inode) lower_inode->i_rdev); /* all well, copy inode attributes */ - fsstack_copy_attr_all(inode, lower_inode); + sdcardfs_copy_and_fix_attrs(inode, lower_inode); fsstack_copy_inode_size(inode, lower_inode); - fix_derived_permission(inode); - unlock_new_inode(inode); return inode; } @@ -164,7 +172,7 @@ struct inode *sdcardfs_iget(struct super_block *sb, struct inode *lower_inode) * @lower_path: the lower path (caller does path_get/put) */ int sdcardfs_interpose(struct dentry *dentry, struct super_block *sb, - struct path *lower_path) + struct path *lower_path, userid_t id) { int err = 0; struct inode *inode; @@ -186,14 +194,14 @@ int sdcardfs_interpose(struct dentry *dentry, struct super_block *sb, */ /* inherit lower inode number for sdcardfs's inode */ - inode = sdcardfs_iget(sb, lower_inode); + inode = sdcardfs_iget(sb, lower_inode, id); if (IS_ERR(inode)) { err = PTR_ERR(inode); goto out; } d_add(dentry, inode); - update_derived_permission(dentry); + update_derived_permission_lock(dentry); out: return err; } @@ -205,7 +213,7 @@ int sdcardfs_interpose(struct dentry *dentry, struct super_block *sb, * Fills in lower_parent_path with on success. */ static struct dentry *__sdcardfs_lookup(struct dentry *dentry, - unsigned int flags, struct path *lower_parent_path) + unsigned int flags, struct path *lower_parent_path, userid_t id) { int err = 0; struct vfsmount *lower_dir_mnt; @@ -266,7 +274,7 @@ static struct dentry *__sdcardfs_lookup(struct dentry *dentry, } sdcardfs_set_lower_path(dentry, &lower_path); - err = sdcardfs_interpose(dentry, dentry->d_sb, &lower_path); + err = sdcardfs_interpose(dentry, dentry->d_sb, &lower_path, id); if (err) /* path_put underlying path on error */ sdcardfs_put_reset_lower_path(dentry); goto out; @@ -328,13 +336,11 @@ struct dentry *sdcardfs_lookup(struct inode *dir, struct dentry *dentry, struct dentry *ret = NULL, *parent; struct path lower_parent_path; int err = 0; - struct sdcardfs_sb_info *sbi = SDCARDFS_SB(dentry->d_sb); const struct cred *saved_cred = NULL; parent = dget_parent(dentry); - if(!check_caller_access_to_name(parent->d_inode, dentry->d_name.name, - sbi->options.derive, 0, 0)) { + if(!check_caller_access_to_name(parent->d_inode, dentry->d_name.name)) { ret = ERR_PTR(-EACCES); printk(KERN_INFO "%s: need to check the caller's gid in packages.list\n" " dentry: %s, task:%s\n", @@ -354,7 +360,7 @@ struct dentry *sdcardfs_lookup(struct inode *dir, struct dentry *dentry, goto out; } - ret = __sdcardfs_lookup(dentry, flags, &lower_parent_path); + ret = __sdcardfs_lookup(dentry, flags, &lower_parent_path, SDCARDFS_I(dir)->userid); if (IS_ERR(ret)) { goto out; @@ -365,8 +371,10 @@ struct dentry *sdcardfs_lookup(struct inode *dir, struct dentry *dentry, fsstack_copy_attr_times(dentry->d_inode, sdcardfs_lower_inode(dentry->d_inode)); /* get drived permission */ + mutex_lock(&dentry->d_inode->i_mutex); get_derived_permission(parent, dentry); fix_derived_permission(dentry->d_inode); + mutex_unlock(&dentry->d_inode->i_mutex); } /* update parent directory's atime */ fsstack_copy_attr_atime(parent->d_inode, diff --git a/fs/sdcardfs/main.c b/fs/sdcardfs/main.c index 9d04ae8ceb46..80aa355d801e 100644 --- a/fs/sdcardfs/main.c +++ b/fs/sdcardfs/main.c @@ -24,25 +24,27 @@ #include enum { - Opt_uid, + Opt_fsuid, + Opt_fsgid, Opt_gid, - Opt_wgid, Opt_debug, - Opt_split, - Opt_derive, Opt_lower_fs, + Opt_mask, + Opt_multiuser, // May need? + Opt_userid, Opt_reserved_mb, Opt_err, }; static const match_table_t sdcardfs_tokens = { - {Opt_uid, "uid=%u"}, + {Opt_fsuid, "fsuid=%u"}, + {Opt_fsgid, "fsgid=%u"}, {Opt_gid, "gid=%u"}, - {Opt_wgid, "wgid=%u"}, {Opt_debug, "debug"}, - {Opt_split, "split"}, - {Opt_derive, "derive=%s"}, {Opt_lower_fs, "lower_fs=%s"}, + {Opt_mask, "mask=%u"}, + {Opt_userid, "userid=%d"}, + {Opt_multiuser, "multiuser"}, {Opt_reserved_mb, "reserved_mb=%u"}, {Opt_err, NULL} }; @@ -58,12 +60,10 @@ static int parse_options(struct super_block *sb, char *options, int silent, /* by default, we use AID_MEDIA_RW as uid, gid */ opts->fs_low_uid = AID_MEDIA_RW; opts->fs_low_gid = AID_MEDIA_RW; - /* by default, we use AID_SDCARD_RW as write_gid */ - opts->write_gid = AID_SDCARD_RW; - /* default permission policy - * (DERIVE_NONE | DERIVE_LEGACY | DERIVE_UNIFIED) */ - opts->derive = DERIVE_NONE; - opts->split_perms = 0; + opts->mask = 0; + opts->multiuser = false; + opts->fs_user_id = 0; + opts->gid = 0; /* by default, we use LOWER_FS_EXT4 as lower fs type */ opts->lower_fs = LOWER_FS_EXT4; /* by default, 0MB is reserved */ @@ -85,37 +85,33 @@ static int parse_options(struct super_block *sb, char *options, int silent, case Opt_debug: *debug = 1; break; - case Opt_uid: + case Opt_fsuid: if (match_int(&args[0], &option)) return 0; opts->fs_low_uid = option; break; - case Opt_gid: + case Opt_fsgid: if (match_int(&args[0], &option)) return 0; opts->fs_low_gid = option; break; - case Opt_wgid: + case Opt_gid: if (match_int(&args[0], &option)) return 0; - opts->write_gid = option; + opts->gid = option; break; - case Opt_split: - opts->split_perms=1; + case Opt_userid: + if (match_int(&args[0], &option)) + return 0; + opts->fs_user_id = option; break; - case Opt_derive: - string_option = match_strdup(&args[0]); - if (!strcmp("none", string_option)) { - opts->derive = DERIVE_NONE; - } else if (!strcmp("legacy", string_option)) { - opts->derive = DERIVE_LEGACY; - } else if (!strcmp("unified", string_option)) { - opts->derive = DERIVE_UNIFIED; - } else { - kfree(string_option); - goto invalid_option; - } - kfree(string_option); + case Opt_mask: + if (match_int(&args[0], &option)) + return 0; + opts->mask = option; + break; + case Opt_multiuser: + opts->multiuser = true; break; case Opt_lower_fs: string_option = match_strdup(&args[0]); @@ -184,6 +180,11 @@ static struct dentry *sdcardfs_d_alloc_root(struct super_block *sb) } #endif +DEFINE_MUTEX(sdcardfs_super_list_lock); +LIST_HEAD(sdcardfs_super_list); +EXPORT_SYMBOL_GPL(sdcardfs_super_list_lock); +EXPORT_SYMBOL_GPL(sdcardfs_super_list); + /* * There is no need to lock the sdcardfs_super_info's rwsem as there is no * way anyone can have a reference to the superblock at this point in time. @@ -196,7 +197,6 @@ static int sdcardfs_read_super(struct super_block *sb, const char *dev_name, struct super_block *lower_sb; struct path lower_path; struct sdcardfs_sb_info *sb_info; - void *pkgl_id; struct inode *inode; printk(KERN_INFO "sdcardfs version 2.0\n"); @@ -215,8 +215,7 @@ static int sdcardfs_read_super(struct super_block *sb, const char *dev_name, err = kern_path(dev_name, LOOKUP_FOLLOW | LOOKUP_DIRECTORY, &lower_path); if (err) { - printk(KERN_ERR "sdcardfs: error accessing " - "lower directory '%s'\n", dev_name); + printk(KERN_ERR "sdcardfs: error accessing lower directory '%s'\n", dev_name); goto out; } @@ -229,7 +228,6 @@ static int sdcardfs_read_super(struct super_block *sb, const char *dev_name, } sb_info = sb->s_fs_info; - /* parse options */ err = parse_options(sb, raw_data, silent, &debug, &sb_info->options); if (err) { @@ -237,14 +235,6 @@ static int sdcardfs_read_super(struct super_block *sb, const char *dev_name, goto out_freesbi; } - if (sb_info->options.derive != DERIVE_NONE) { - pkgl_id = packagelist_create(sb_info->options.write_gid); - if(IS_ERR(pkgl_id)) - goto out_freesbi; - else - sb_info->pkgl_id = pkgl_id; - } - /* set the lower superblock field of upper superblock */ lower_sb = lower_path.dentry->d_sb; atomic_inc(&lower_sb->s_active); @@ -263,7 +253,7 @@ static int sdcardfs_read_super(struct super_block *sb, const char *dev_name, sb->s_op = &sdcardfs_sops; /* get a new inode and allocate our root dentry */ - inode = sdcardfs_iget(sb, lower_path.dentry->d_inode); + inode = sdcardfs_iget(sb, lower_path.dentry->d_inode, 0); if (IS_ERR(inode)) { err = PTR_ERR(inode); goto out_sput; @@ -292,41 +282,22 @@ static int sdcardfs_read_super(struct super_block *sb, const char *dev_name, d_rehash(sb->s_root); /* setup permission policy */ - switch(sb_info->options.derive) { - case DERIVE_NONE: - setup_derived_state(sb->s_root->d_inode, - PERM_ROOT, 0, AID_ROOT, AID_SDCARD_RW, 00775); - sb_info->obbpath_s = NULL; - break; - case DERIVE_LEGACY: - /* Legacy behavior used to support internal multiuser layout which - * places user_id at the top directory level, with the actual roots - * just below that. Shared OBB path is also at top level. */ - setup_derived_state(sb->s_root->d_inode, - PERM_LEGACY_PRE_ROOT, 0, AID_ROOT, AID_SDCARD_R, 00771); - /* initialize the obbpath string and lookup the path - * sb_info->obb_path will be deactivated by path_put - * on sdcardfs_put_super */ - sb_info->obbpath_s = kzalloc(PATH_MAX, GFP_KERNEL); - snprintf(sb_info->obbpath_s, PATH_MAX, "%s/obb", dev_name); - err = prepare_dir(sb_info->obbpath_s, + sb_info->obbpath_s = kzalloc(PATH_MAX, GFP_KERNEL); + mutex_lock(&sdcardfs_super_list_lock); + if(sb_info->options.multiuser) { + setup_derived_state(sb->s_root->d_inode, PERM_PRE_ROOT, sb_info->options.fs_user_id, AID_ROOT, false); + snprintf(sb_info->obbpath_s, PATH_MAX, "%s/obb", dev_name); + /*err = prepare_dir(sb_info->obbpath_s, sb_info->options.fs_low_uid, - sb_info->options.fs_low_gid, 00755); - if(err) - printk(KERN_ERR "sdcardfs: %s: %d, error on creating %s\n", - __func__,__LINE__, sb_info->obbpath_s); - break; - case DERIVE_UNIFIED: - /* Unified multiuser layout which places secondary user_id under - * /Android/user and shared OBB path under /Android/obb. */ - setup_derived_state(sb->s_root->d_inode, - PERM_ROOT, 0, AID_ROOT, AID_SDCARD_R, 00771); - - sb_info->obbpath_s = kzalloc(PATH_MAX, GFP_KERNEL); - snprintf(sb_info->obbpath_s, PATH_MAX, "%s/Android/obb", dev_name); - break; + sb_info->options.fs_low_gid, 00755);*/ + } else { + setup_derived_state(sb->s_root->d_inode, PERM_ROOT, sb_info->options.fs_low_uid, AID_ROOT, false); + snprintf(sb_info->obbpath_s, PATH_MAX, "%s/Android/obb", dev_name); } fix_derived_permission(sb->s_root->d_inode); + sb_info->sb = sb; + list_add(&sb_info->list, &sdcardfs_super_list); + mutex_unlock(&sdcardfs_super_list_lock); if (!silent) printk(KERN_INFO "sdcardfs: mounted on top of %s type %s\n", @@ -341,7 +312,6 @@ static int sdcardfs_read_super(struct super_block *sb, const char *dev_name, out_sput: /* drop refs we took earlier */ atomic_dec(&lower_sb->s_active); - packagelist_destroy(sb_info->pkgl_id); out_freesbi: kfree(SDCARDFS_SB(sb)); sb->s_fs_info = NULL; @@ -386,11 +356,22 @@ struct dentry *sdcardfs_mount(struct file_system_type *fs_type, int flags, raw_data, sdcardfs_read_super); } +void sdcardfs_kill_sb(struct super_block *sb) { + struct sdcardfs_sb_info *sbi; + if (sb->s_magic == SDCARDFS_SUPER_MAGIC) { + sbi = SDCARDFS_SB(sb); + mutex_lock(&sdcardfs_super_list_lock); + list_del(&sbi->list); + mutex_unlock(&sdcardfs_super_list_lock); + } + generic_shutdown_super(sb); +} + static struct file_system_type sdcardfs_fs_type = { .owner = THIS_MODULE, .name = SDCARDFS_NAME, .mount = sdcardfs_mount, - .kill_sb = generic_shutdown_super, + .kill_sb = sdcardfs_kill_sb, .fs_flags = 0, }; diff --git a/fs/sdcardfs/packagelist.c b/fs/sdcardfs/packagelist.c index f11591da141d..ba3478d94107 100644 --- a/fs/sdcardfs/packagelist.c +++ b/fs/sdcardfs/packagelist.c @@ -19,13 +19,16 @@ */ #include "sdcardfs.h" -#include "strtok.h" #include -#include -#include -#include #include + +#include +#include +#include + +#include + #define STRING_BUF_SIZE (512) struct hashtable_entry { @@ -34,25 +37,20 @@ struct hashtable_entry { unsigned int value; }; +struct sb_list { + struct super_block *sb; + struct list_head list; +}; + struct packagelist_data { DECLARE_HASHTABLE(package_to_appid,8); - DECLARE_HASHTABLE(appid_with_rw,7); struct mutex hashtable_lock; - struct task_struct *thread_id; - gid_t write_gid; - char *strtok_last; - char read_buf[STRING_BUF_SIZE]; - char event_buf[STRING_BUF_SIZE]; - char app_name_buf[STRING_BUF_SIZE]; - char gids_buf[STRING_BUF_SIZE]; + }; -static struct kmem_cache *hashtable_entry_cachep; +static struct packagelist_data *pkgl_data_all; -/* Path to system-provided mapping of package name to appIds */ -static const char* const kpackageslist_file = "/data/system/packages.list"; -/* Supplementary groups to execute with */ -static const gid_t kgroups[1] = { AID_PACKAGE_INFO }; +static struct kmem_cache *hashtable_entry_cachep; static unsigned int str_hash(const char *key) { int i; @@ -66,62 +64,29 @@ static unsigned int str_hash(const char *key) { return h; } -static int contain_appid_key(struct packagelist_data *pkgl_dat, unsigned int appid) { - struct hashtable_entry *hash_cur; - - hash_for_each_possible(pkgl_dat->appid_with_rw, hash_cur, hlist, appid) - if ((void *)(uintptr_t)appid == hash_cur->key) - return 1; - - return 0; -} - -/* Return if the calling UID holds sdcard_rw. */ -int get_caller_has_rw_locked(void *pkgl_id, derive_t derive) { - struct packagelist_data *pkgl_dat = (struct packagelist_data *)pkgl_id; - appid_t appid; - int ret; - - /* No additional permissions enforcement */ - if (derive == DERIVE_NONE) { - return 1; - } - - appid = multiuser_get_app_id(from_kuid(&init_user_ns, current_fsuid())); - mutex_lock(&pkgl_dat->hashtable_lock); - ret = contain_appid_key(pkgl_dat, appid); - mutex_unlock(&pkgl_dat->hashtable_lock); - return ret; -} - appid_t get_appid(void *pkgl_id, const char *app_name) { - struct packagelist_data *pkgl_dat = (struct packagelist_data *)pkgl_id; + struct packagelist_data *pkgl_dat = pkgl_data_all; struct hashtable_entry *hash_cur; unsigned int hash = str_hash(app_name); appid_t ret_id; - //printk(KERN_INFO "sdcardfs: %s: %s, %u\n", __func__, (char *)app_name, hash); mutex_lock(&pkgl_dat->hashtable_lock); hash_for_each_possible(pkgl_dat->package_to_appid, hash_cur, hlist, hash) { - //printk(KERN_INFO "sdcardfs: %s: %s\n", __func__, (char *)hash_cur->key); if (!strcasecmp(app_name, hash_cur->key)) { ret_id = (appid_t)hash_cur->value; mutex_unlock(&pkgl_dat->hashtable_lock); - //printk(KERN_INFO "=> app_id: %d\n", (int)ret_id); return ret_id; } } mutex_unlock(&pkgl_dat->hashtable_lock); - //printk(KERN_INFO "=> app_id: %d\n", 0); return 0; } /* Kernel has already enforced everything we returned through * derive_permissions_locked(), so this is used to lock down access * even further, such as enforcing that apps hold sdcard_rw. */ -int check_caller_access_to_name(struct inode *parent_node, const char* name, - derive_t derive, int w_ok, int has_rw) { +int check_caller_access_to_name(struct inode *parent_node, const char* name) { /* Always block security-sensitive files at root */ if (parent_node && SDCARDFS_I(parent_node)->perm == PERM_ROOT) { @@ -132,28 +97,12 @@ int check_caller_access_to_name(struct inode *parent_node, const char* name, } } - /* No additional permissions enforcement */ - if (derive == DERIVE_NONE) { - return 1; - } - /* Root always has access; access for any other UIDs should always * be controlled through packages.list. */ if (from_kuid(&init_user_ns, current_fsuid()) == 0) { return 1; } - /* If asking to write, verify that caller either owns the - * parent or holds sdcard_rw. */ - if (w_ok) { - if (parent_node && - (from_kuid(&init_user_ns, current_fsuid()) == - SDCARDFS_I(parent_node)->d_uid)) { - return 1; - } - return has_rw; - } - /* No extra permissions to enforce */ return 1; } @@ -171,14 +120,13 @@ int open_flags_to_access_mode(int open_flags) { } } -static int insert_str_to_int(struct packagelist_data *pkgl_dat, char *key, +static int insert_str_to_int_lock(struct packagelist_data *pkgl_dat, char *key, unsigned int value) { struct hashtable_entry *hash_cur; struct hashtable_entry *new_entry; unsigned int hash = str_hash(key); - //printk(KERN_INFO "sdcardfs: %s: %s: %d, %u\n", __func__, (char *)key, value, hash); hash_for_each_possible(pkgl_dat->package_to_appid, hash_cur, hlist, hash) { if (!strcasecmp(key, hash_cur->key)) { hash_cur->value = value; @@ -194,245 +142,275 @@ static int insert_str_to_int(struct packagelist_data *pkgl_dat, char *key, return 0; } -static void remove_str_to_int(struct hashtable_entry *h_entry) { - //printk(KERN_INFO "sdcardfs: %s: %s: %d\n", __func__, (char *)h_entry->key, h_entry->value); - kfree(h_entry->key); - kmem_cache_free(hashtable_entry_cachep, h_entry); +static void fixup_perms(struct super_block *sb) { + if (sb && sb->s_magic == SDCARDFS_SUPER_MAGIC) { + mutex_lock(&sb->s_root->d_inode->i_mutex); + get_derive_permissions_recursive(sb->s_root); + mutex_unlock(&sb->s_root->d_inode->i_mutex); + } } -static int insert_int_to_null(struct packagelist_data *pkgl_dat, unsigned int key, - unsigned int value) -{ - struct hashtable_entry *hash_cur; - struct hashtable_entry *new_entry; +static int insert_str_to_int(struct packagelist_data *pkgl_dat, char *key, + unsigned int value) { + int ret; + struct sdcardfs_sb_info *sbinfo; + mutex_lock(&sdcardfs_super_list_lock); + mutex_lock(&pkgl_dat->hashtable_lock); + ret = insert_str_to_int_lock(pkgl_dat, key, value); + mutex_unlock(&pkgl_dat->hashtable_lock); - //printk(KERN_INFO "sdcardfs: %s: %d: %d\n", __func__, (int)key, value); - hash_for_each_possible(pkgl_dat->appid_with_rw, hash_cur, hlist, key) { - if ((void *)(uintptr_t)key == hash_cur->key) { - hash_cur->value = value; - return 0; + list_for_each_entry(sbinfo, &sdcardfs_super_list, list) { + if (sbinfo) { + fixup_perms(sbinfo->sb); } } - new_entry = kmem_cache_alloc(hashtable_entry_cachep, GFP_KERNEL); - if (!new_entry) - return -ENOMEM; - new_entry->key = (void *)(uintptr_t)key; - new_entry->value = value; - hash_add(pkgl_dat->appid_with_rw, &new_entry->hlist, key); - return 0; + mutex_unlock(&sdcardfs_super_list_lock); + return ret; } -static void remove_int_to_null(struct hashtable_entry *h_entry) { - //printk(KERN_INFO "sdcardfs: %s: %d: %d\n", __func__, (int)h_entry->key, h_entry->value); +static void remove_str_to_int_lock(struct hashtable_entry *h_entry) { + kfree(h_entry->key); + hash_del(&h_entry->hlist); kmem_cache_free(hashtable_entry_cachep, h_entry); } +static void remove_str_to_int(struct packagelist_data *pkgl_dat, const char *key) +{ + struct sdcardfs_sb_info *sbinfo; + struct hashtable_entry *hash_cur; + unsigned int hash = str_hash(key); + mutex_lock(&sdcardfs_super_list_lock); + mutex_lock(&pkgl_dat->hashtable_lock); + hash_for_each_possible(pkgl_dat->package_to_appid, hash_cur, hlist, hash) { + if (!strcasecmp(key, hash_cur->key)) { + remove_str_to_int_lock(hash_cur); + break; + } + } + mutex_unlock(&pkgl_dat->hashtable_lock); + list_for_each_entry(sbinfo, &sdcardfs_super_list, list) { + if (sbinfo) { + fixup_perms(sbinfo->sb); + } + } + mutex_unlock(&sdcardfs_super_list_lock); + return; +} + static void remove_all_hashentrys(struct packagelist_data *pkgl_dat) { struct hashtable_entry *hash_cur; struct hlist_node *h_t; int i; - + mutex_lock(&pkgl_dat->hashtable_lock); hash_for_each_safe(pkgl_dat->package_to_appid, i, h_t, hash_cur, hlist) - remove_str_to_int(hash_cur); - hash_for_each_safe(pkgl_dat->appid_with_rw, i, h_t, hash_cur, hlist) - remove_int_to_null(hash_cur); - + remove_str_to_int_lock(hash_cur); + mutex_unlock(&pkgl_dat->hashtable_lock); hash_init(pkgl_dat->package_to_appid); - hash_init(pkgl_dat->appid_with_rw); } -static int read_package_list(struct packagelist_data *pkgl_dat) { - int ret; - int fd; - int read_amount; +static struct packagelist_data * packagelist_create(void) +{ + struct packagelist_data *pkgl_dat; - printk(KERN_INFO "sdcardfs: read_package_list\n"); + pkgl_dat = kmalloc(sizeof(*pkgl_dat), GFP_KERNEL | __GFP_ZERO); + if (!pkgl_dat) { + printk(KERN_ERR "sdcardfs: Failed to create hash\n"); + return ERR_PTR(-ENOMEM); + } - mutex_lock(&pkgl_dat->hashtable_lock); + mutex_init(&pkgl_dat->hashtable_lock); + hash_init(pkgl_dat->package_to_appid); + + return pkgl_dat; +} +static void packagelist_destroy(struct packagelist_data *pkgl_dat) +{ remove_all_hashentrys(pkgl_dat); + printk(KERN_INFO "sdcardfs: destroyed packagelist pkgld\n"); + kfree(pkgl_dat); +} - fd = sys_open(kpackageslist_file, O_RDONLY, 0); - if (fd < 0) { - printk(KERN_ERR "sdcardfs: failed to open package list\n"); - mutex_unlock(&pkgl_dat->hashtable_lock); - return fd; - } +struct package_appid { + struct config_item item; + int add_pid; +}; - while ((read_amount = sys_read(fd, pkgl_dat->read_buf, - sizeof(pkgl_dat->read_buf))) > 0) { - unsigned int appid; - char *token; - int one_line_len = 0; - int additional_read; - unsigned long ret_gid; - - while (one_line_len < read_amount) { - if (pkgl_dat->read_buf[one_line_len] == '\n') { - one_line_len++; - break; - } - one_line_len++; - } - additional_read = read_amount - one_line_len; - if (additional_read > 0) - sys_lseek(fd, -additional_read, SEEK_CUR); - - if (sscanf(pkgl_dat->read_buf, "%s %u %*d %*s %*s %s", - pkgl_dat->app_name_buf, &appid, - pkgl_dat->gids_buf) == 3) { - ret = insert_str_to_int(pkgl_dat, pkgl_dat->app_name_buf, appid); - if (ret) { - sys_close(fd); - mutex_unlock(&pkgl_dat->hashtable_lock); - return ret; - } - - token = strtok_r(pkgl_dat->gids_buf, ",", &pkgl_dat->strtok_last); - while (token != NULL) { - if (!kstrtoul(token, 10, &ret_gid) && - (ret_gid == pkgl_dat->write_gid)) { - ret = insert_int_to_null(pkgl_dat, appid, 1); - if (ret) { - sys_close(fd); - mutex_unlock(&pkgl_dat->hashtable_lock); - return ret; - } - break; - } - token = strtok_r(NULL, ",", &pkgl_dat->strtok_last); - } - } - } +static inline struct package_appid *to_package_appid(struct config_item *item) +{ + return item ? container_of(item, struct package_appid, item) : NULL; +} - sys_close(fd); - mutex_unlock(&pkgl_dat->hashtable_lock); - return 0; +static ssize_t package_appid_attr_show(struct config_item *item, + char *page) +{ + ssize_t count; + count = sprintf(page, "%d\n", get_appid(pkgl_data_all, item->ci_name)); + return count; } -static int packagelist_reader(void *thread_data) +static ssize_t package_appid_attr_store(struct config_item *item, + const char *page, size_t count) { - struct packagelist_data *pkgl_dat = (struct packagelist_data *)thread_data; - struct inotify_event *event; - bool active = false; - int event_pos; - int event_size; - int res = 0; - int nfd; - - allow_signal(SIGINT); - - nfd = sys_inotify_init(); - if (nfd < 0) { - printk(KERN_ERR "sdcardfs: inotify_init failed: %d\n", nfd); - return nfd; - } + struct package_appid *package_appid = to_package_appid(item); + unsigned long tmp; + char *p = (char *) page; + int ret; - while (!kthread_should_stop()) { - if (signal_pending(current)) { - ssleep(1); - continue; - } + tmp = simple_strtoul(p, &p, 10); + if (!p || (*p && (*p != '\n'))) + return -EINVAL; - if (!active) { - res = sys_inotify_add_watch(nfd, kpackageslist_file, IN_DELETE_SELF); - if (res < 0) { - if (res == -ENOENT || res == -EACCES) { - /* Framework may not have created yet, sleep and retry */ - printk(KERN_ERR "sdcardfs: missing packages.list; retrying\n"); - ssleep(2); - printk(KERN_ERR "sdcardfs: missing packages.list_end; retrying\n"); - continue; - } else { - printk(KERN_ERR "sdcardfs: inotify_add_watch failed: %d\n", res); - goto interruptable_sleep; - } - } - /* Watch above will tell us about any future changes, so - * read the current state. */ - res = read_package_list(pkgl_dat); - if (res) { - printk(KERN_ERR "sdcardfs: read_package_list failed: %d\n", res); - goto interruptable_sleep; - } - active = true; - } + if (tmp > INT_MAX) + return -ERANGE; + ret = insert_str_to_int(pkgl_data_all, item->ci_name, (unsigned int)tmp); + package_appid->add_pid = tmp; + if (ret) + return ret; - event_pos = 0; - res = sys_read(nfd, pkgl_dat->event_buf, sizeof(pkgl_dat->event_buf)); - if (res < (int) sizeof(*event)) { - if (res == -EINTR) - continue; - printk(KERN_ERR "sdcardfs: failed to read inotify event: %d\n", res); - goto interruptable_sleep; - } + return count; +} - while (res >= (int) sizeof(*event)) { - event = (struct inotify_event *) (pkgl_dat->event_buf + event_pos); +static struct configfs_attribute package_appid_attr_add_pid = { + .ca_owner = THIS_MODULE, + .ca_name = "appid", + .ca_mode = S_IRUGO | S_IWUGO, + .show = package_appid_attr_show, + .store = package_appid_attr_store, +}; - printk(KERN_INFO "sdcardfs: inotify event: %08x\n", event->mask); - if ((event->mask & IN_IGNORED) == IN_IGNORED) { - /* Previously watched file was deleted, probably due to move - * that swapped in new data; re-arm the watch and read. */ - active = false; - } +static struct configfs_attribute *package_appid_attrs[] = { + &package_appid_attr_add_pid, + NULL, +}; - event_size = sizeof(*event) + event->len; - res -= event_size; - event_pos += event_size; - } - continue; +static void package_appid_release(struct config_item *item) +{ + printk(KERN_INFO "sdcardfs: removing %s\n", item->ci_dentry->d_name.name); + /* item->ci_name is freed already, so we rely on the dentry */ + remove_str_to_int(pkgl_data_all, item->ci_dentry->d_name.name); + kfree(to_package_appid(item)); +} -interruptable_sleep: - set_current_state(TASK_INTERRUPTIBLE); - schedule(); - } - flush_signals(current); - sys_close(nfd); - return res; +static struct configfs_item_operations package_appid_item_ops = { + .release = package_appid_release, +}; + +static struct config_item_type package_appid_type = { + .ct_item_ops = &package_appid_item_ops, + .ct_attrs = package_appid_attrs, + .ct_owner = THIS_MODULE, +}; + + +struct sdcardfs_packages { + struct config_group group; +}; + +static inline struct sdcardfs_packages *to_sdcardfs_packages(struct config_item *item) +{ + return item ? container_of(to_config_group(item), struct sdcardfs_packages, group) : NULL; } -void * packagelist_create(gid_t write_gid) +static struct config_item *sdcardfs_packages_make_item(struct config_group *group, const char *name) { - struct packagelist_data *pkgl_dat; - struct task_struct *packagelist_thread; + struct package_appid *package_appid; - pkgl_dat = kmalloc(sizeof(*pkgl_dat), GFP_KERNEL | __GFP_ZERO); - if (!pkgl_dat) { - printk(KERN_ERR "sdcardfs: creating kthread failed\n"); + package_appid = kzalloc(sizeof(struct package_appid), GFP_KERNEL); + if (!package_appid) return ERR_PTR(-ENOMEM); - } - mutex_init(&pkgl_dat->hashtable_lock); - hash_init(pkgl_dat->package_to_appid); - hash_init(pkgl_dat->appid_with_rw); - pkgl_dat->write_gid = write_gid; + config_item_init_type_name(&package_appid->item, name, + &package_appid_type); + + package_appid->add_pid = 0; - packagelist_thread = kthread_run(packagelist_reader, (void *)pkgl_dat, "pkgld"); - if (IS_ERR(packagelist_thread)) { - printk(KERN_ERR "sdcardfs: creating kthread failed\n"); - kfree(pkgl_dat); - return packagelist_thread; - } - pkgl_dat->thread_id = packagelist_thread; + return &package_appid->item; +} - printk(KERN_INFO "sdcardfs: created packagelist pkgld/%d\n", - (int)pkgl_dat->thread_id->pid); +static ssize_t packages_attr_show(struct config_item *item, + char *page) +{ + struct hashtable_entry *hash_cur; + struct hlist_node *h_t; + int i; + int count = 0; + mutex_lock(&pkgl_data_all->hashtable_lock); + hash_for_each_safe(pkgl_data_all->package_to_appid, i, h_t, hash_cur, hlist) + count += snprintf(page + count, PAGE_SIZE - count, "%s %d\n", (char *)hash_cur->key, hash_cur->value); + mutex_unlock(&pkgl_data_all->hashtable_lock); - return (void *)pkgl_dat; + + return count; } -void packagelist_destroy(void *pkgl_id) +static struct configfs_attribute sdcardfs_packages_attr_description = { + .ca_owner = THIS_MODULE, + .ca_name = "packages_gid.list", + .ca_mode = S_IRUGO, + .show = packages_attr_show, +}; + +static struct configfs_attribute *sdcardfs_packages_attrs[] = { + &sdcardfs_packages_attr_description, + NULL, +}; + +static void sdcardfs_packages_release(struct config_item *item) { - struct packagelist_data *pkgl_dat = (struct packagelist_data *)pkgl_id; - pid_t pkgl_pid = pkgl_dat->thread_id->pid; - force_sig_info(SIGINT, SEND_SIG_PRIV, pkgl_dat->thread_id); - kthread_stop(pkgl_dat->thread_id); - remove_all_hashentrys(pkgl_dat); - printk(KERN_INFO "sdcardfs: destroyed packagelist pkgld/%d\n", (int)pkgl_pid); - kfree(pkgl_dat); + printk(KERN_INFO "sdcardfs: destroyed something?\n"); + kfree(to_sdcardfs_packages(item)); +} + +static struct configfs_item_operations sdcardfs_packages_item_ops = { + .release = sdcardfs_packages_release, +}; + +/* + * Note that, since no extra work is required on ->drop_item(), + * no ->drop_item() is provided. + */ +static struct configfs_group_operations sdcardfs_packages_group_ops = { + .make_item = sdcardfs_packages_make_item, +}; + +static struct config_item_type sdcardfs_packages_type = { + .ct_item_ops = &sdcardfs_packages_item_ops, + .ct_group_ops = &sdcardfs_packages_group_ops, + .ct_attrs = sdcardfs_packages_attrs, + .ct_owner = THIS_MODULE, +}; + +static struct configfs_subsystem sdcardfs_packages_subsys = { + .su_group = { + .cg_item = { + .ci_namebuf = "sdcardfs", + .ci_type = &sdcardfs_packages_type, + }, + }, +}; + +static int __init configfs_sdcardfs_init(void) +{ + int ret; + struct configfs_subsystem *subsys = &sdcardfs_packages_subsys; + + config_group_init(&subsys->su_group); + mutex_init(&subsys->su_mutex); + ret = configfs_register_subsystem(subsys); + if (ret) { + printk(KERN_ERR "Error %d while registering subsystem %s\n", + ret, + subsys->su_group.cg_item.ci_namebuf); + } + return ret; +} + +static void __exit configfs_sdcardfs_exit(void) +{ + configfs_unregister_subsystem(&sdcardfs_packages_subsys); } int packagelist_init(void) @@ -445,13 +423,15 @@ int packagelist_init(void) return -ENOMEM; } + pkgl_data_all = packagelist_create(); + configfs_sdcardfs_init(); return 0; } void packagelist_exit(void) { + configfs_sdcardfs_exit(); + packagelist_destroy(pkgl_data_all); if (hashtable_entry_cachep) kmem_cache_destroy(hashtable_entry_cachep); } - - diff --git a/fs/sdcardfs/sdcardfs.h b/fs/sdcardfs/sdcardfs.h index 51f6c7912584..1b85f4e70324 100644 --- a/fs/sdcardfs/sdcardfs.h +++ b/fs/sdcardfs/sdcardfs.h @@ -42,6 +42,7 @@ #include #include #include +#include #include "multiuser.h" /* the file system name */ @@ -70,10 +71,11 @@ #define fix_derived_permission(x) \ do { \ (x)->i_uid = make_kuid(&init_user_ns, SDCARDFS_I(x)->d_uid); \ - (x)->i_gid = make_kgid(&init_user_ns, SDCARDFS_I(x)->d_gid); \ - (x)->i_mode = ((x)->i_mode & S_IFMT) | SDCARDFS_I(x)->d_mode;\ + (x)->i_gid = make_kgid(&init_user_ns, get_gid(SDCARDFS_I(x))); \ + (x)->i_mode = ((x)->i_mode & S_IFMT) | get_mode(SDCARDFS_I(x));\ } while (0) + /* OVERRIDE_CRED() and REVERT_CRED() * OVERRID_CRED() * backup original task->cred @@ -99,35 +101,28 @@ (int)current->cred->fsuid, \ (int)current->cred->fsgid); -/* Android 4.4 support */ +/* Android 5.0 support */ /* Permission mode for a specific node. Controls how file permissions * are derived for children nodes. */ typedef enum { - /* Nothing special; this node should just inherit from its parent. */ - PERM_INHERIT, - /* This node is one level above a normal root; used for legacy layouts - * which use the first level to represent user_id. */ - PERM_LEGACY_PRE_ROOT, - /* This node is "/" */ - PERM_ROOT, - /* This node is "/Android" */ - PERM_ANDROID, - /* This node is "/Android/data" */ - PERM_ANDROID_DATA, - /* This node is "/Android/obb" */ - PERM_ANDROID_OBB, - /* This node is "/Android/user" */ - PERM_ANDROID_USER, + /* Nothing special; this node should just inherit from its parent. */ + PERM_INHERIT, + /* This node is one level above a normal root; used for legacy layouts + * which use the first level to represent user_id. */ + PERM_PRE_ROOT, + /* This node is "/" */ + PERM_ROOT, + /* This node is "/Android" */ + PERM_ANDROID, + /* This node is "/Android/data" */ + PERM_ANDROID_DATA, + /* This node is "/Android/obb" */ + PERM_ANDROID_OBB, + /* This node is "/Android/media" */ + PERM_ANDROID_MEDIA, } perm_t; -/* Permissions structure to derive */ -typedef enum { - DERIVE_NONE, - DERIVE_LEGACY, - DERIVE_UNIFIED, -} derive_t; - typedef enum { LOWER_FS_EXT4, LOWER_FS_FAT, @@ -161,9 +156,9 @@ extern void free_dentry_private_data(struct dentry *dentry); extern struct dentry *sdcardfs_lookup(struct inode *dir, struct dentry *dentry, unsigned int flags); extern struct inode *sdcardfs_iget(struct super_block *sb, - struct inode *lower_inode); + struct inode *lower_inode, userid_t id); extern int sdcardfs_interpose(struct dentry *dentry, struct super_block *sb, - struct path *lower_path); + struct path *lower_path, userid_t id); /* file private data */ struct sdcardfs_file_info { @@ -174,18 +169,16 @@ struct sdcardfs_file_info { /* sdcardfs inode data in memory */ struct sdcardfs_inode_info { struct inode *lower_inode; - /* state derived based on current position in hierachy - * caution: d_mode does not include file types - */ + /* state derived based on current position in hierachy */ perm_t perm; userid_t userid; uid_t d_uid; - gid_t d_gid; - mode_t d_mode; + bool under_android; struct inode vfs_inode; }; + /* sdcardfs dentry data in memory */ struct sdcardfs_dentry_info { spinlock_t lock; /* protects lower_path */ @@ -196,15 +189,17 @@ struct sdcardfs_dentry_info { struct sdcardfs_mount_options { uid_t fs_low_uid; gid_t fs_low_gid; - gid_t write_gid; - int split_perms; - derive_t derive; + userid_t fs_user_id; + gid_t gid; lower_fs_t lower_fs; + mode_t mask; + bool multiuser; unsigned int reserved_mb; }; /* sdcardfs super-block data in memory */ struct sdcardfs_sb_info { + struct super_block *sb; struct super_block *lower_sb; /* derived perm policy : some of options have been added * to sdcardfs_mount_options (Android 4.4 support) */ @@ -213,6 +208,7 @@ struct sdcardfs_sb_info { char *obbpath_s; struct path obbpath; void *pkgl_id; + struct list_head list; }; /* @@ -331,6 +327,44 @@ static inline void sdcardfs_put_reset_##pname(const struct dentry *dent) \ SDCARDFS_DENT_FUNC(lower_path) SDCARDFS_DENT_FUNC(orig_path) +static inline int get_gid(struct sdcardfs_inode_info *info) { + struct sdcardfs_sb_info *sb_info = SDCARDFS_SB(info->vfs_inode.i_sb); + if (sb_info->options.gid == AID_SDCARD_RW) { + /* As an optimization, certain trusted system components only run + * as owner but operate across all users. Since we're now handing + * out the sdcard_rw GID only to trusted apps, we're okay relaxing + * the user boundary enforcement for the default view. The UIDs + * assigned to app directories are still multiuser aware. */ + return AID_SDCARD_RW; + } else { + return multiuser_get_uid(info->userid, sb_info->options.gid); + } +} +static inline int get_mode(struct sdcardfs_inode_info *info) { + int owner_mode; + int filtered_mode; + struct sdcardfs_sb_info *sb_info = SDCARDFS_SB(info->vfs_inode.i_sb); + int visible_mode = 0775 & ~sb_info->options.mask; + + if (info->perm == PERM_PRE_ROOT) { + /* Top of multi-user view should always be visible to ensure + * secondary users can traverse inside. */ + visible_mode = 0711; + } else if (info->under_android) { + /* Block "other" access to Android directories, since only apps + * belonging to a specific user should be in there; we still + * leave +x open for the default view. */ + if (sb_info->options.gid == AID_SDCARD_RW) { + visible_mode = visible_mode & ~0006; + } else { + visible_mode = visible_mode & ~0007; + } + } + owner_mode = info->lower_inode->i_mode & 0700; + filtered_mode = visible_mode & (owner_mode | (owner_mode >> 3) | (owner_mode >> 6)); + return filtered_mode; +} + static inline int has_graft_path(const struct dentry *dent) { int ret = 0; @@ -364,22 +398,24 @@ static inline void sdcardfs_put_real_lower(const struct dentry *dent, sdcardfs_put_lower_path(dent, real_lower); } +extern struct mutex sdcardfs_super_list_lock; +extern struct list_head sdcardfs_super_list; + /* for packagelist.c */ -extern int get_caller_has_rw_locked(void *pkgl_id, derive_t derive); extern appid_t get_appid(void *pkgl_id, const char *app_name); -extern int check_caller_access_to_name(struct inode *parent_node, const char* name, - derive_t derive, int w_ok, int has_rw); +extern int check_caller_access_to_name(struct inode *parent_node, const char* name); extern int open_flags_to_access_mode(int open_flags); -extern void * packagelist_create(gid_t write_gid); -extern void packagelist_destroy(void *pkgl_id); extern int packagelist_init(void); extern void packagelist_exit(void); /* for derived_perm.c */ extern void setup_derived_state(struct inode *inode, perm_t perm, - userid_t userid, uid_t uid, gid_t gid, mode_t mode); + userid_t userid, uid_t uid, bool under_android); extern void get_derived_permission(struct dentry *parent, struct dentry *dentry); -extern void update_derived_permission(struct dentry *dentry); +extern void get_derived_permission_new(struct dentry *parent, struct dentry *dentry, struct dentry *newdentry); +extern void get_derive_permissions_recursive(struct dentry *parent); + +extern void update_derived_permission_lock(struct dentry *dentry); extern int need_graft_path(struct dentry *dentry); extern int is_base_obbpath(struct dentry *dentry); extern int is_obbpath_invalid(struct dentry *dentry); @@ -483,4 +519,18 @@ static inline int check_min_free_space(struct dentry *dentry, size_t size, int d return 1; } +/* Copies attrs and maintains sdcardfs managed attrs */ +static inline void sdcardfs_copy_and_fix_attrs(struct inode *dest, const struct inode *src) +{ + dest->i_mode = (src->i_mode & S_IFMT) | get_mode(SDCARDFS_I(dest)); + dest->i_uid = make_kuid(&init_user_ns, SDCARDFS_I(dest)->d_uid); + dest->i_gid = make_kgid(&init_user_ns, get_gid(SDCARDFS_I(dest))); + dest->i_rdev = src->i_rdev; + dest->i_atime = src->i_atime; + dest->i_mtime = src->i_mtime; + dest->i_ctime = src->i_ctime; + dest->i_blkbits = src->i_blkbits; + dest->i_flags = src->i_flags; + set_nlink(dest, src->i_nlink); +} #endif /* not _SDCARDFS_H_ */ diff --git a/fs/sdcardfs/strtok.h b/fs/sdcardfs/strtok.h deleted file mode 100644 index 50ab25aa0bc4..000000000000 --- a/fs/sdcardfs/strtok.h +++ /dev/null @@ -1,75 +0,0 @@ -/* - * fs/sdcardfs/strtok.h - * - * Copyright (c) 2013 Samsung Electronics Co. Ltd - * Authors: Daeho Jeong, Woojoong Lee, Seunghwan Hyun, - * Sunghwan Yun, Sungjong Seo - * - * This program has been developed as a stackable file system based on - * the WrapFS which written by - * - * Copyright (c) 1998-2011 Erez Zadok - * Copyright (c) 2009 Shrikar Archak - * Copyright (c) 2003-2011 Stony Brook University - * Copyright (c) 2003-2011 The Research Foundation of SUNY - * - * This file is dual licensed. It may be redistributed and/or modified - * under the terms of the Apache 2.0 License OR version 2 of the GNU - * General Public License. - */ - -static char * -strtok_r(char *s, const char *delim, char **last) -{ - char *spanp; - int c, sc; - char *tok; - - - /* if (s == NULL && (s = *last) == NULL) - return NULL; */ - if (s == NULL) { - s = *last; - if (s == NULL) - return NULL; - } - - /* - * Skip (span) leading delimiters (s += strspn(s, delim), sort of). - */ -cont: - c = *s++; - for (spanp = (char *)delim; (sc = *spanp++) != 0;) { - if (c == sc) - goto cont; - } - - if (c == 0) { /* no non-delimiter characters */ - *last = NULL; - return NULL; - } - tok = s - 1; - - /* - * Scan token (scan for delimiters: s += strcspn(s, delim), sort of). - * Note that delim must have one NUL; we stop if we see that, too. - */ - for (;;) { - c = *s++; - spanp = (char *)delim; - do { - sc = *spanp++; - if (sc == c) { - if (c == 0) - s = NULL; - else - s[-1] = 0; - *last = s; - return tok; - } - } while (sc != 0); - } - - /* NOTREACHED */ -} - diff --git a/fs/sdcardfs/super.c b/fs/sdcardfs/super.c index f153ce1b8cf3..1d6490128c99 100644 --- a/fs/sdcardfs/super.c +++ b/fs/sdcardfs/super.c @@ -46,9 +46,6 @@ static void sdcardfs_put_super(struct super_block *sb) sdcardfs_set_lower_super(sb, NULL); atomic_dec(&s->s_active); - if(spd->pkgl_id) - packagelist_destroy(spd->pkgl_id); - kfree(spd); sb->s_fs_info = NULL; } @@ -203,12 +200,8 @@ static int sdcardfs_show_options(struct seq_file *m, struct dentry *root) if (opts->fs_low_gid != 0) seq_printf(m, ",gid=%u", opts->fs_low_gid); - if (opts->derive == DERIVE_NONE) - seq_printf(m, ",derive=none"); - else if (opts->derive == DERIVE_LEGACY) - seq_printf(m, ",derive=legacy"); - else if (opts->derive == DERIVE_UNIFIED) - seq_printf(m, ",derive=unified"); + if (opts->multiuser) + seq_printf(m, ",multiuser"); if (opts->reserved_mb != 0) seq_printf(m, ",reserved=%uMB", opts->reserved_mb); From 70c63ec01c18db8472094fdfb8778fc3fce9bb5e Mon Sep 17 00:00:00 2001 From: Daniel Rosenberg Date: Thu, 11 Feb 2016 16:53:36 -0800 Subject: [PATCH 0294/1103] ANDROID: sdcardfs: Add support for d_canonical_path Change-Id: I5d6f0e71b8ca99aec4b0894412f1dfd1cfe12add Signed-off-by: Daniel Rosenberg --- fs/sdcardfs/dentry.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/fs/sdcardfs/dentry.c b/fs/sdcardfs/dentry.c index dbbcfd091fc7..971928ab6c21 100644 --- a/fs/sdcardfs/dentry.c +++ b/fs/sdcardfs/dentry.c @@ -172,10 +172,15 @@ static int sdcardfs_cmp_ci(const struct dentry *parent, return 1; } +static void sdcardfs_canonical_path(const struct path *path, struct path *actual_path) { + sdcardfs_get_real_lower(path->dentry, actual_path); +} + const struct dentry_operations sdcardfs_ci_dops = { .d_revalidate = sdcardfs_d_revalidate, .d_release = sdcardfs_d_release, .d_hash = sdcardfs_hash_ci, .d_compare = sdcardfs_cmp_ci, + .d_canonical_path = sdcardfs_canonical_path, }; From ff09869ee7d32fd3c8266a2865cf7475484af8d2 Mon Sep 17 00:00:00 2001 From: Daniel Rosenberg Date: Wed, 23 Mar 2016 16:39:30 -0700 Subject: [PATCH 0295/1103] ANDROID: sdcardfs: remove effectless config option CONFIG_SDCARD_FS_CI_SEARCH only guards a define for LOOKUP_CASE_INSENSITIVE, which is never used in the kernel. Remove both, along with the option matching that supports it. Change-Id: I363a8f31de8ee7a7a934d75300cc9ba8176e2edf Signed-off-by: Daniel Rosenberg --- fs/sdcardfs/Kconfig | 5 ----- fs/sdcardfs/lookup.c | 7 +------ fs/sdcardfs/main.c | 15 --------------- fs/sdcardfs/sdcardfs.h | 6 ------ 4 files changed, 1 insertion(+), 32 deletions(-) diff --git a/fs/sdcardfs/Kconfig b/fs/sdcardfs/Kconfig index ab25f88ebb37..a1c103316ac7 100644 --- a/fs/sdcardfs/Kconfig +++ b/fs/sdcardfs/Kconfig @@ -11,8 +11,3 @@ config SDCARD_FS_FADV_NOACTIVE default y help Sdcardfs supports fadvise noactive mode. - -config SDCARD_FS_CI_SEARCH - tristate "sdcardfs case-insensitive search support" - depends on SDCARD_FS - default y diff --git a/fs/sdcardfs/lookup.c b/fs/sdcardfs/lookup.c index f80abcb6b467..a01b06a514fd 100644 --- a/fs/sdcardfs/lookup.c +++ b/fs/sdcardfs/lookup.c @@ -238,13 +238,8 @@ static struct dentry *__sdcardfs_lookup(struct dentry *dentry, lower_dir_mnt = lower_parent_path->mnt; /* Use vfs_path_lookup to check if the dentry exists or not */ - if (sbi->options.lower_fs == LOWER_FS_EXT4) { - err = vfs_path_lookup(lower_dir_dentry, lower_dir_mnt, name, - LOOKUP_CASE_INSENSITIVE, &lower_path); - } else if (sbi->options.lower_fs == LOWER_FS_FAT) { - err = vfs_path_lookup(lower_dir_dentry, lower_dir_mnt, name, 0, + err = vfs_path_lookup(lower_dir_dentry, lower_dir_mnt, name, 0, &lower_path); - } /* no error: handle positive dentries */ if (!err) { diff --git a/fs/sdcardfs/main.c b/fs/sdcardfs/main.c index 80aa355d801e..fa11a0458b84 100644 --- a/fs/sdcardfs/main.c +++ b/fs/sdcardfs/main.c @@ -41,7 +41,6 @@ static const match_table_t sdcardfs_tokens = { {Opt_fsgid, "fsgid=%u"}, {Opt_gid, "gid=%u"}, {Opt_debug, "debug"}, - {Opt_lower_fs, "lower_fs=%s"}, {Opt_mask, "mask=%u"}, {Opt_userid, "userid=%d"}, {Opt_multiuser, "multiuser"}, @@ -64,8 +63,6 @@ static int parse_options(struct super_block *sb, char *options, int silent, opts->multiuser = false; opts->fs_user_id = 0; opts->gid = 0; - /* by default, we use LOWER_FS_EXT4 as lower fs type */ - opts->lower_fs = LOWER_FS_EXT4; /* by default, 0MB is reserved */ opts->reserved_mb = 0; @@ -113,18 +110,6 @@ static int parse_options(struct super_block *sb, char *options, int silent, case Opt_multiuser: opts->multiuser = true; break; - case Opt_lower_fs: - string_option = match_strdup(&args[0]); - if (!strcmp("ext4", string_option)) { - opts->lower_fs = LOWER_FS_EXT4; - } else if (!strcmp("fat", string_option)) { - opts->lower_fs = LOWER_FS_FAT; - } else { - kfree(string_option); - goto invalid_option; - } - kfree(string_option); - break; case Opt_reserved_mb: if (match_int(&args[0], &option)) return 0; diff --git a/fs/sdcardfs/sdcardfs.h b/fs/sdcardfs/sdcardfs.h index 1b85f4e70324..f111f898b630 100644 --- a/fs/sdcardfs/sdcardfs.h +++ b/fs/sdcardfs/sdcardfs.h @@ -123,11 +123,6 @@ typedef enum { PERM_ANDROID_MEDIA, } perm_t; -typedef enum { - LOWER_FS_EXT4, - LOWER_FS_FAT, -} lower_fs_t; - struct sdcardfs_sb_info; struct sdcardfs_mount_options; @@ -191,7 +186,6 @@ struct sdcardfs_mount_options { gid_t fs_low_gid; userid_t fs_user_id; gid_t gid; - lower_fs_t lower_fs; mode_t mask; bool multiuser; unsigned int reserved_mb; From 65525ee02df1e0e4bcd7b34be93fd069ab7a0635 Mon Sep 17 00:00:00 2001 From: Daniel Rosenberg Date: Mon, 28 Mar 2016 15:00:20 -0700 Subject: [PATCH 0296/1103] ANDROID: sdcardfs: Remove unused code Change-Id: Ie97cba27ce44818ac56cfe40954f164ad44eccf6 --- fs/sdcardfs/main.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/fs/sdcardfs/main.c b/fs/sdcardfs/main.c index fa11a0458b84..a6522286d731 100644 --- a/fs/sdcardfs/main.c +++ b/fs/sdcardfs/main.c @@ -54,7 +54,6 @@ static int parse_options(struct super_block *sb, char *options, int silent, char *p; substring_t args[MAX_OPT_ARGS]; int option; - char *string_option; /* by default, we use AID_MEDIA_RW as uid, gid */ opts->fs_low_uid = AID_MEDIA_RW; @@ -117,7 +116,6 @@ static int parse_options(struct super_block *sb, char *options, int silent, break; /* unknown option */ default: -invalid_option: if (!silent) { printk( KERN_ERR "Unrecognized mount option \"%s\" " "or missing value", p); From 287e9438ea461253373636ed9f496c019b629fb5 Mon Sep 17 00:00:00 2001 From: Daniel Rosenberg Date: Mon, 28 Mar 2016 16:00:34 -0700 Subject: [PATCH 0297/1103] ANDROID: sdcardfs: remove unneeded __init and __exit Change-Id: I2a2d45d52f891332174c3000e8681c5167c1564f --- fs/sdcardfs/packagelist.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/fs/sdcardfs/packagelist.c b/fs/sdcardfs/packagelist.c index ba3478d94107..10f0d6be718b 100644 --- a/fs/sdcardfs/packagelist.c +++ b/fs/sdcardfs/packagelist.c @@ -392,7 +392,7 @@ static struct configfs_subsystem sdcardfs_packages_subsys = { }, }; -static int __init configfs_sdcardfs_init(void) +static int configfs_sdcardfs_init(void) { int ret; struct configfs_subsystem *subsys = &sdcardfs_packages_subsys; @@ -408,7 +408,7 @@ static int __init configfs_sdcardfs_init(void) return ret; } -static void __exit configfs_sdcardfs_exit(void) +static void configfs_sdcardfs_exit(void) { configfs_unregister_subsystem(&sdcardfs_packages_subsys); } From ba6cdb65aed744dfae9dd058c798752667cafdef Mon Sep 17 00:00:00 2001 From: Daniel Rosenberg Date: Fri, 8 Jul 2016 14:15:14 -0700 Subject: [PATCH 0298/1103] ANDROID: sdcardfs: Truncate packages_gid.list on overflow packages_gid.list was improperly returning the wrong count. Use scnprintf instead, and inform the user that the list was truncated if it is. Bug: 30013843 Change-Id: Ida2b2ef7cd86dd87300bfb4c2cdb6bfe2ee1650d Signed-off-by: Daniel Rosenberg --- fs/sdcardfs/packagelist.c | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/fs/sdcardfs/packagelist.c b/fs/sdcardfs/packagelist.c index 10f0d6be718b..9c3340528eee 100644 --- a/fs/sdcardfs/packagelist.c +++ b/fs/sdcardfs/packagelist.c @@ -335,13 +335,20 @@ static ssize_t packages_attr_show(struct config_item *item, struct hashtable_entry *hash_cur; struct hlist_node *h_t; int i; - int count = 0; + int count = 0, written = 0; + char errormsg[] = "\n"; + mutex_lock(&pkgl_data_all->hashtable_lock); - hash_for_each_safe(pkgl_data_all->package_to_appid, i, h_t, hash_cur, hlist) - count += snprintf(page + count, PAGE_SIZE - count, "%s %d\n", (char *)hash_cur->key, hash_cur->value); + hash_for_each_safe(pkgl_data_all->package_to_appid, i, h_t, hash_cur, hlist) { + written = scnprintf(page + count, PAGE_SIZE - sizeof(errormsg) - count, "%s %d\n", (char *)hash_cur->key, hash_cur->value); + if (count + written == PAGE_SIZE - sizeof(errormsg)) { + count += scnprintf(page + count, PAGE_SIZE - count, errormsg); + break; + } + count += written; + } mutex_unlock(&pkgl_data_all->hashtable_lock); - return count; } From 4e9e06b73446f7e964c1c2b7f0bd8c15f43b2f3f Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Wed, 1 Jun 2016 10:28:49 -0700 Subject: [PATCH 0299/1103] ANDROID: sdcardfs: fix itnull.cocci warnings List_for_each_entry has the property that the first argument is always bound to a real list element, never NULL, so testing dentry is not needed. Generated by: scripts/coccinelle/iterators/itnull.cocci Cc: Daniel Rosenberg Signed-off-by: Julia Lawall Signed-off-by: Fengguang Wu Signed-off-by: Guenter Roeck --- fs/sdcardfs/derived_perm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/sdcardfs/derived_perm.c b/fs/sdcardfs/derived_perm.c index 128b3e56851f..41e0e11b3c35 100644 --- a/fs/sdcardfs/derived_perm.c +++ b/fs/sdcardfs/derived_perm.c @@ -112,7 +112,7 @@ void get_derived_permission(struct dentry *parent, struct dentry *dentry) void get_derive_permissions_recursive(struct dentry *parent) { struct dentry *dentry; list_for_each_entry(dentry, &parent->d_subdirs, d_child) { - if (dentry && dentry->d_inode) { + if (dentry->d_inode) { mutex_lock(&dentry->d_inode->i_mutex); get_derived_permission(parent, dentry); fix_derived_permission(dentry->d_inode); From eccc16afd8911c15d6021c3d2454f5ffcfcaf398 Mon Sep 17 00:00:00 2001 From: Daniel Rosenberg Date: Wed, 13 Apr 2016 16:38:34 -0700 Subject: [PATCH 0300/1103] ANDROID: sdcardfs: override umask on mkdir and create The mode on files created on the lower fs should not be affected by the umask of the calling task's fs_struct. Instead, we create a copy and modify it as needed. This also lets us avoid the string shenanigans around .nomedia files. Bug: 27992761 Change-Id: Ia3a6e56c24c6e19b3b01c1827e46403bb71c2f4c Signed-off-by: Daniel Rosenberg --- fs/fs_struct.c | 1 + fs/sdcardfs/inode.c | 70 ++++++++++++++++++++++----------------------- 2 files changed, 36 insertions(+), 35 deletions(-) diff --git a/fs/fs_struct.c b/fs/fs_struct.c index be0250788b73..47b0ec5d5006 100644 --- a/fs/fs_struct.c +++ b/fs/fs_struct.c @@ -128,6 +128,7 @@ struct fs_struct *copy_fs_struct(struct fs_struct *old) } return fs; } +EXPORT_SYMBOL_GPL(copy_fs_struct); int unshare_fs_struct(void) { diff --git a/fs/sdcardfs/inode.c b/fs/sdcardfs/inode.c index 2528da0d3ae1..4b140ba86955 100644 --- a/fs/sdcardfs/inode.c +++ b/fs/sdcardfs/inode.c @@ -19,6 +19,7 @@ */ #include "sdcardfs.h" +#include /* Do not directly use this function. Use OVERRIDE_CRED() instead. */ const struct cred * override_fsids(struct sdcardfs_sb_info* sbi) @@ -56,6 +57,8 @@ static int sdcardfs_create(struct inode *dir, struct dentry *dentry, struct dentry *lower_parent_dentry = NULL; struct path lower_path; const struct cred *saved_cred = NULL; + struct fs_struct *saved_fs; + struct fs_struct *copied_fs; if(!check_caller_access_to_name(dir, dentry->d_name.name)) { printk(KERN_INFO "%s: need to check the caller's gid in packages.list\n" @@ -74,6 +77,16 @@ static int sdcardfs_create(struct inode *dir, struct dentry *dentry, /* set last 16bytes of mode field to 0664 */ mode = (mode & S_IFMT) | 00664; + + /* temporarily change umask for lower fs write */ + saved_fs = current->fs; + copied_fs = copy_fs_struct(current->fs); + if (!copied_fs) { + err = -ENOMEM; + goto out_unlock; + } + current->fs = copied_fs; + current->fs->umask = 0; err = vfs_create(d_inode(lower_parent_dentry), lower_dentry, mode, want_excl); if (err) goto out; @@ -85,6 +98,9 @@ static int sdcardfs_create(struct inode *dir, struct dentry *dentry, fsstack_copy_inode_size(dir, d_inode(lower_parent_dentry)); out: + current->fs = saved_fs; + free_fs_struct(copied_fs); +out_unlock: unlock_dir(lower_parent_dentry); sdcardfs_put_lower_path(dentry, &lower_path); REVERT_CRED(saved_cred); @@ -245,11 +261,9 @@ static int sdcardfs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode struct sdcardfs_sb_info *sbi = SDCARDFS_SB(dentry->d_sb); const struct cred *saved_cred = NULL; struct sdcardfs_inode_info *pi = SDCARDFS_I(dir); - char *page_buf; - char *nomedia_dir_name; - char *nomedia_fullpath; - int fullpath_namelen; int touch_err = 0; + struct fs_struct *saved_fs; + struct fs_struct *copied_fs; if(!check_caller_access_to_name(dir, dentry->d_name.name)) { printk(KERN_INFO "%s: need to check the caller's gid in packages.list\n" @@ -276,6 +290,16 @@ static int sdcardfs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode /* set last 16bytes of mode field to 0775 */ mode = (mode & S_IFMT) | 00775; + + /* temporarily change umask for lower fs write */ + saved_fs = current->fs; + copied_fs = copy_fs_struct(current->fs); + if (!copied_fs) { + err = -ENOMEM; + goto out_unlock; + } + current->fs = copied_fs; + current->fs->umask = 0; err = vfs_mkdir(d_inode(lower_parent_dentry), lower_dentry, mode); if (err) @@ -316,42 +340,18 @@ static int sdcardfs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode /* When creating /Android/data and /Android/obb, mark them as .nomedia */ if (make_nomedia_in_obb || ((pi->perm == PERM_ANDROID) && (!strcasecmp(dentry->d_name.name, "data")))) { - - page_buf = (char *)__get_free_page(GFP_KERNEL); - if (!page_buf) { - printk(KERN_ERR "sdcardfs: failed to allocate page buf\n"); - goto out; - } - - nomedia_dir_name = d_absolute_path(&lower_path, page_buf, PAGE_SIZE); - if (IS_ERR(nomedia_dir_name)) { - free_page((unsigned long)page_buf); - printk(KERN_ERR "sdcardfs: failed to get .nomedia dir name\n"); - goto out; - } - - fullpath_namelen = page_buf + PAGE_SIZE - nomedia_dir_name - 1; - fullpath_namelen += strlen("/.nomedia"); - nomedia_fullpath = kzalloc(fullpath_namelen + 1, GFP_KERNEL); - if (!nomedia_fullpath) { - free_page((unsigned long)page_buf); - printk(KERN_ERR "sdcardfs: failed to allocate .nomedia fullpath buf\n"); - goto out; - } - - strcpy(nomedia_fullpath, nomedia_dir_name); - free_page((unsigned long)page_buf); - strcat(nomedia_fullpath, "/.nomedia"); - touch_err = touch(nomedia_fullpath, 0664); + set_fs_pwd(current->fs, &lower_path); + touch_err = touch(".nomedia", 0664); if (touch_err) { - printk(KERN_ERR "sdcardfs: failed to touch(%s): %d\n", - nomedia_fullpath, touch_err); - kfree(nomedia_fullpath); + printk(KERN_ERR "sdcardfs: failed to create .nomedia in %s: %d\n", + lower_path.dentry->d_name.name, touch_err); goto out; } - kfree(nomedia_fullpath); } out: + current->fs = saved_fs; + free_fs_struct(copied_fs); +out_unlock: unlock_dir(lower_parent_dentry); sdcardfs_put_lower_path(dentry, &lower_path); out_revert: From 18a06f051cd31395352a9f31f150325424be09a0 Mon Sep 17 00:00:00 2001 From: Daniel Rosenberg Date: Wed, 27 Apr 2016 15:31:29 -0700 Subject: [PATCH 0301/1103] ANDROID: sdcardfs: Check for other cases on path lookup This fixes a bug where the first lookup of a file or folder created under a different view would not be case insensitive. It will now search through for a case insensitive match if the initial lookup fails. Bug:28024488 Change-Id: I4ff9ce297b9f2f9864b47540e740fd491c545229 Signed-off-by: Daniel Rosenberg --- fs/sdcardfs/lookup.c | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/fs/sdcardfs/lookup.c b/fs/sdcardfs/lookup.c index a01b06a514fd..a127d05b5054 100644 --- a/fs/sdcardfs/lookup.c +++ b/fs/sdcardfs/lookup.c @@ -240,6 +240,28 @@ static struct dentry *__sdcardfs_lookup(struct dentry *dentry, /* Use vfs_path_lookup to check if the dentry exists or not */ err = vfs_path_lookup(lower_dir_dentry, lower_dir_mnt, name, 0, &lower_path); + /* check for other cases */ + if (err == -ENOENT) { + struct dentry *child; + struct dentry *match = NULL; + spin_lock(&lower_dir_dentry->d_lock); + list_for_each_entry(child, &lower_dir_dentry->d_subdirs, d_child) { + if (child && d_inode(child)) { + if (strcasecmp(child->d_name.name, name)==0) { + match = dget(child); + break; + } + } + } + spin_unlock(&lower_dir_dentry->d_lock); + if (match) { + err = vfs_path_lookup(lower_dir_dentry, + lower_dir_mnt, + match->d_name.name, 0, + &lower_path); + dput(match); + } + } /* no error: handle positive dentries */ if (!err) { From 8854974f0d82171111aa69b361c663b6fe4ed7f4 Mon Sep 17 00:00:00 2001 From: Daniel Rosenberg Date: Tue, 16 Aug 2016 15:19:26 -0700 Subject: [PATCH 0302/1103] ANDROID: sdcardfs: Fix locking for permission fix up Iterating over d_subdirs requires taking d_lock. Removed several unneeded locks. Change-Id: I5b1588e54c7e6ee19b756d6705171c7f829e2650 Signed-off-by: Daniel Rosenberg --- fs/sdcardfs/derived_perm.c | 6 ++---- fs/sdcardfs/inode.c | 2 -- fs/sdcardfs/lookup.c | 4 +--- 3 files changed, 3 insertions(+), 9 deletions(-) diff --git a/fs/sdcardfs/derived_perm.c b/fs/sdcardfs/derived_perm.c index 41e0e11b3c35..bfe402b8cf32 100644 --- a/fs/sdcardfs/derived_perm.c +++ b/fs/sdcardfs/derived_perm.c @@ -111,15 +111,15 @@ void get_derived_permission(struct dentry *parent, struct dentry *dentry) void get_derive_permissions_recursive(struct dentry *parent) { struct dentry *dentry; + spin_lock(&parent->d_lock); list_for_each_entry(dentry, &parent->d_subdirs, d_child) { if (dentry->d_inode) { - mutex_lock(&dentry->d_inode->i_mutex); get_derived_permission(parent, dentry); fix_derived_permission(dentry->d_inode); get_derive_permissions_recursive(dentry); - mutex_unlock(&dentry->d_inode->i_mutex); } } + spin_unlock(&parent->d_lock); } /* main function for updating derived permission */ @@ -135,7 +135,6 @@ inline void update_derived_permission_lock(struct dentry *dentry) * 1. need to check whether the dentry is updated or not * 2. remove the root dentry update */ - mutex_lock(&dentry->d_inode->i_mutex); if(IS_ROOT(dentry)) { //setup_default_pre_root_state(dentry->d_inode); } else { @@ -146,7 +145,6 @@ inline void update_derived_permission_lock(struct dentry *dentry) } } fix_derived_permission(dentry->d_inode); - mutex_unlock(&dentry->d_inode->i_mutex); } int need_graft_path(struct dentry *dentry) diff --git a/fs/sdcardfs/inode.c b/fs/sdcardfs/inode.c index 4b140ba86955..1a23c0cc8f58 100644 --- a/fs/sdcardfs/inode.c +++ b/fs/sdcardfs/inode.c @@ -513,11 +513,9 @@ static int sdcardfs_rename(struct inode *old_dir, struct dentry *old_dentry, } /* At this point, not all dentry information has been moved, so * we pass along new_dentry for the name.*/ - mutex_lock(&d_inode(old_dentry)->i_mutex); get_derived_permission_new(new_dentry->d_parent, old_dentry, new_dentry); fix_derived_permission(d_inode(old_dentry)); get_derive_permissions_recursive(old_dentry); - mutex_unlock(&d_inode(old_dentry)->i_mutex); out: unlock_rename(lower_old_dir_dentry, lower_new_dir_dentry); dput(lower_old_dir_dentry); diff --git a/fs/sdcardfs/lookup.c b/fs/sdcardfs/lookup.c index a127d05b5054..c74a7d1bc18e 100644 --- a/fs/sdcardfs/lookup.c +++ b/fs/sdcardfs/lookup.c @@ -387,11 +387,9 @@ struct dentry *sdcardfs_lookup(struct inode *dir, struct dentry *dentry, if (dentry->d_inode) { fsstack_copy_attr_times(dentry->d_inode, sdcardfs_lower_inode(dentry->d_inode)); - /* get drived permission */ - mutex_lock(&dentry->d_inode->i_mutex); + /* get derived permission */ get_derived_permission(parent, dentry); fix_derived_permission(dentry->d_inode); - mutex_unlock(&dentry->d_inode->i_mutex); } /* update parent directory's atime */ fsstack_copy_attr_atime(parent->d_inode, From af3a41a361b6e8e1ffdd9c0a158af73076b3d2d4 Mon Sep 17 00:00:00 2001 From: Daniel Rosenberg Date: Tue, 10 May 2016 13:42:43 -0700 Subject: [PATCH 0303/1103] ANDROID: sdcardfs: Switch package list to RCU Switched the package id hashmap to use RCU. Change-Id: I9fdcab279009005bf28536247d11e13babab0b93 Signed-off-by: Daniel Rosenberg --- fs/sdcardfs/derived_perm.c | 3 +- fs/sdcardfs/packagelist.c | 200 +++++++++++++++++-------------------- fs/sdcardfs/sdcardfs.h | 2 +- 3 files changed, 93 insertions(+), 112 deletions(-) diff --git a/fs/sdcardfs/derived_perm.c b/fs/sdcardfs/derived_perm.c index bfe402b8cf32..2a75ad873a7c 100644 --- a/fs/sdcardfs/derived_perm.c +++ b/fs/sdcardfs/derived_perm.c @@ -47,7 +47,6 @@ void setup_derived_state(struct inode *inode, perm_t perm, /* While renaming, there is a point where we want the path from dentry, but the name from newdentry */ void get_derived_permission_new(struct dentry *parent, struct dentry *dentry, struct dentry *newdentry) { - struct sdcardfs_sb_info *sbi = SDCARDFS_SB(dentry->d_sb); struct sdcardfs_inode_info *info = SDCARDFS_I(dentry->d_inode); struct sdcardfs_inode_info *parent_info= SDCARDFS_I(parent->d_inode); appid_t appid; @@ -96,7 +95,7 @@ void get_derived_permission_new(struct dentry *parent, struct dentry *dentry, st case PERM_ANDROID_DATA: case PERM_ANDROID_OBB: case PERM_ANDROID_MEDIA: - appid = get_appid(sbi->pkgl_id, newdentry->d_name.name); + appid = get_appid(newdentry->d_name.name); if (appid != 0) { info->d_uid = multiuser_get_uid(parent_info->userid, appid); } diff --git a/fs/sdcardfs/packagelist.c b/fs/sdcardfs/packagelist.c index 9c3340528eee..f5a49c513568 100644 --- a/fs/sdcardfs/packagelist.c +++ b/fs/sdcardfs/packagelist.c @@ -29,26 +29,13 @@ #include -#define STRING_BUF_SIZE (512) - struct hashtable_entry { struct hlist_node hlist; - void *key; - unsigned int value; -}; - -struct sb_list { - struct super_block *sb; - struct list_head list; + const char *key; + atomic_t value; }; -struct packagelist_data { - DECLARE_HASHTABLE(package_to_appid,8); - struct mutex hashtable_lock; - -}; - -static struct packagelist_data *pkgl_data_all; +static DEFINE_HASHTABLE(package_to_appid, 8); static struct kmem_cache *hashtable_entry_cachep; @@ -64,22 +51,21 @@ static unsigned int str_hash(const char *key) { return h; } -appid_t get_appid(void *pkgl_id, const char *app_name) +appid_t get_appid(const char *app_name) { - struct packagelist_data *pkgl_dat = pkgl_data_all; struct hashtable_entry *hash_cur; unsigned int hash = str_hash(app_name); appid_t ret_id; - mutex_lock(&pkgl_dat->hashtable_lock); - hash_for_each_possible(pkgl_dat->package_to_appid, hash_cur, hlist, hash) { + rcu_read_lock(); + hash_for_each_possible_rcu(package_to_appid, hash_cur, hlist, hash) { if (!strcasecmp(app_name, hash_cur->key)) { - ret_id = (appid_t)hash_cur->value; - mutex_unlock(&pkgl_dat->hashtable_lock); + ret_id = atomic_read(&hash_cur->value); + rcu_read_unlock(); return ret_id; } } - mutex_unlock(&pkgl_dat->hashtable_lock); + rcu_read_unlock(); return 0; } @@ -120,116 +106,118 @@ int open_flags_to_access_mode(int open_flags) { } } -static int insert_str_to_int_lock(struct packagelist_data *pkgl_dat, char *key, - unsigned int value) +static struct hashtable_entry *alloc_packagelist_entry(const char *key, + appid_t value) +{ + struct hashtable_entry *ret = kmem_cache_alloc(hashtable_entry_cachep, + GFP_KERNEL); + if (!ret) + return NULL; + + ret->key = kstrdup(key, GFP_KERNEL); + if (!ret->key) { + kmem_cache_free(hashtable_entry_cachep, ret); + return NULL; + } + + atomic_set(&ret->value, value); + return ret; +} + +static int insert_packagelist_entry_locked(const char *key, appid_t value) { struct hashtable_entry *hash_cur; struct hashtable_entry *new_entry; unsigned int hash = str_hash(key); - hash_for_each_possible(pkgl_dat->package_to_appid, hash_cur, hlist, hash) { + hash_for_each_possible_rcu(package_to_appid, hash_cur, hlist, hash) { if (!strcasecmp(key, hash_cur->key)) { - hash_cur->value = value; + atomic_set(&hash_cur->value, value); return 0; } } - new_entry = kmem_cache_alloc(hashtable_entry_cachep, GFP_KERNEL); + new_entry = alloc_packagelist_entry(key, value); if (!new_entry) return -ENOMEM; - new_entry->key = kstrdup(key, GFP_KERNEL); - new_entry->value = value; - hash_add(pkgl_dat->package_to_appid, &new_entry->hlist, hash); + hash_add_rcu(package_to_appid, &new_entry->hlist, hash); return 0; } static void fixup_perms(struct super_block *sb) { if (sb && sb->s_magic == SDCARDFS_SUPER_MAGIC) { - mutex_lock(&sb->s_root->d_inode->i_mutex); get_derive_permissions_recursive(sb->s_root); - mutex_unlock(&sb->s_root->d_inode->i_mutex); } } -static int insert_str_to_int(struct packagelist_data *pkgl_dat, char *key, - unsigned int value) { - int ret; +static void fixup_all_perms(void) +{ struct sdcardfs_sb_info *sbinfo; - mutex_lock(&sdcardfs_super_list_lock); - mutex_lock(&pkgl_dat->hashtable_lock); - ret = insert_str_to_int_lock(pkgl_dat, key, value); - mutex_unlock(&pkgl_dat->hashtable_lock); - - list_for_each_entry(sbinfo, &sdcardfs_super_list, list) { - if (sbinfo) { + list_for_each_entry(sbinfo, &sdcardfs_super_list, list) + if (sbinfo) fixup_perms(sbinfo->sb); - } - } +} + +static int insert_packagelist_entry(const char *key, appid_t value) +{ + int err; + + mutex_lock(&sdcardfs_super_list_lock); + err = insert_packagelist_entry_locked(key, value); + if (!err) + fixup_all_perms(); mutex_unlock(&sdcardfs_super_list_lock); - return ret; + + return err; } -static void remove_str_to_int_lock(struct hashtable_entry *h_entry) { - kfree(h_entry->key); - hash_del(&h_entry->hlist); - kmem_cache_free(hashtable_entry_cachep, h_entry); +static void free_packagelist_entry(struct hashtable_entry *entry) +{ + kfree(entry->key); + hash_del_rcu(&entry->hlist); + kmem_cache_free(hashtable_entry_cachep, entry); } -static void remove_str_to_int(struct packagelist_data *pkgl_dat, const char *key) +static void remove_packagelist_entry_locked(const char *key) { - struct sdcardfs_sb_info *sbinfo; struct hashtable_entry *hash_cur; unsigned int hash = str_hash(key); - mutex_lock(&sdcardfs_super_list_lock); - mutex_lock(&pkgl_dat->hashtable_lock); - hash_for_each_possible(pkgl_dat->package_to_appid, hash_cur, hlist, hash) { + + hash_for_each_possible_rcu(package_to_appid, hash_cur, hlist, hash) { if (!strcasecmp(key, hash_cur->key)) { - remove_str_to_int_lock(hash_cur); - break; - } - } - mutex_unlock(&pkgl_dat->hashtable_lock); - list_for_each_entry(sbinfo, &sdcardfs_super_list, list) { - if (sbinfo) { - fixup_perms(sbinfo->sb); + hash_del_rcu(&hash_cur->hlist); + synchronize_rcu(); + free_packagelist_entry(hash_cur); + return; } } +} + +static void remove_packagelist_entry(const char *key) +{ + mutex_lock(&sdcardfs_super_list_lock); + remove_packagelist_entry_locked(key); + fixup_all_perms(); mutex_unlock(&sdcardfs_super_list_lock); return; } -static void remove_all_hashentrys(struct packagelist_data *pkgl_dat) +static void packagelist_destroy(void) { struct hashtable_entry *hash_cur; struct hlist_node *h_t; + HLIST_HEAD(free_list); int i; - mutex_lock(&pkgl_dat->hashtable_lock); - hash_for_each_safe(pkgl_dat->package_to_appid, i, h_t, hash_cur, hlist) - remove_str_to_int_lock(hash_cur); - mutex_unlock(&pkgl_dat->hashtable_lock); - hash_init(pkgl_dat->package_to_appid); -} - -static struct packagelist_data * packagelist_create(void) -{ - struct packagelist_data *pkgl_dat; + mutex_lock(&sdcardfs_super_list_lock); + hash_for_each_rcu(package_to_appid, i, hash_cur, hlist) { + hash_del_rcu(&hash_cur->hlist); + hlist_add_head(&hash_cur->hlist, &free_list); - pkgl_dat = kmalloc(sizeof(*pkgl_dat), GFP_KERNEL | __GFP_ZERO); - if (!pkgl_dat) { - printk(KERN_ERR "sdcardfs: Failed to create hash\n"); - return ERR_PTR(-ENOMEM); } - - mutex_init(&pkgl_dat->hashtable_lock); - hash_init(pkgl_dat->package_to_appid); - - return pkgl_dat; -} - -static void packagelist_destroy(struct packagelist_data *pkgl_dat) -{ - remove_all_hashentrys(pkgl_dat); + synchronize_rcu(); + hlist_for_each_entry_safe(hash_cur, h_t, &free_list, hlist) + free_packagelist_entry(hash_cur); + mutex_unlock(&sdcardfs_super_list_lock); printk(KERN_INFO "sdcardfs: destroyed packagelist pkgld\n"); - kfree(pkgl_dat); } struct package_appid { @@ -245,26 +233,21 @@ static inline struct package_appid *to_package_appid(struct config_item *item) static ssize_t package_appid_attr_show(struct config_item *item, char *page) { - ssize_t count; - count = sprintf(page, "%d\n", get_appid(pkgl_data_all, item->ci_name)); - return count; + return scnprintf(page, PAGE_SIZE, "%u\n", get_appid(item->ci_name)); } static ssize_t package_appid_attr_store(struct config_item *item, const char *page, size_t count) { struct package_appid *package_appid = to_package_appid(item); - unsigned long tmp; - char *p = (char *) page; + unsigned int tmp; int ret; - tmp = simple_strtoul(p, &p, 10); - if (!p || (*p && (*p != '\n'))) - return -EINVAL; + ret = kstrtouint(page, 10, &tmp); + if (ret) + return ret; - if (tmp > INT_MAX) - return -ERANGE; - ret = insert_str_to_int(pkgl_data_all, item->ci_name, (unsigned int)tmp); + ret = insert_packagelist_entry(item->ci_name, tmp); package_appid->add_pid = tmp; if (ret) return ret; @@ -289,7 +272,7 @@ static void package_appid_release(struct config_item *item) { printk(KERN_INFO "sdcardfs: removing %s\n", item->ci_dentry->d_name.name); /* item->ci_name is freed already, so we rely on the dentry */ - remove_str_to_int(pkgl_data_all, item->ci_dentry->d_name.name); + remove_packagelist_entry(item->ci_dentry->d_name.name); kfree(to_package_appid(item)); } @@ -333,21 +316,21 @@ static ssize_t packages_attr_show(struct config_item *item, char *page) { struct hashtable_entry *hash_cur; - struct hlist_node *h_t; int i; int count = 0, written = 0; - char errormsg[] = "\n"; + const char errormsg[] = "\n"; - mutex_lock(&pkgl_data_all->hashtable_lock); - hash_for_each_safe(pkgl_data_all->package_to_appid, i, h_t, hash_cur, hlist) { - written = scnprintf(page + count, PAGE_SIZE - sizeof(errormsg) - count, "%s %d\n", (char *)hash_cur->key, hash_cur->value); + rcu_read_lock(); + hash_for_each_rcu(package_to_appid, i, hash_cur, hlist) { + written = scnprintf(page + count, PAGE_SIZE - sizeof(errormsg) - count, "%s %d\n", + (const char *)hash_cur->key, atomic_read(&hash_cur->value)); if (count + written == PAGE_SIZE - sizeof(errormsg)) { count += scnprintf(page + count, PAGE_SIZE - count, errormsg); break; } count += written; } - mutex_unlock(&pkgl_data_all->hashtable_lock); + rcu_read_unlock(); return count; } @@ -430,7 +413,6 @@ int packagelist_init(void) return -ENOMEM; } - pkgl_data_all = packagelist_create(); configfs_sdcardfs_init(); return 0; } @@ -438,7 +420,7 @@ int packagelist_init(void) void packagelist_exit(void) { configfs_sdcardfs_exit(); - packagelist_destroy(pkgl_data_all); + packagelist_destroy(); if (hashtable_entry_cachep) kmem_cache_destroy(hashtable_entry_cachep); } diff --git a/fs/sdcardfs/sdcardfs.h b/fs/sdcardfs/sdcardfs.h index f111f898b630..75284f339ae0 100644 --- a/fs/sdcardfs/sdcardfs.h +++ b/fs/sdcardfs/sdcardfs.h @@ -396,7 +396,7 @@ extern struct mutex sdcardfs_super_list_lock; extern struct list_head sdcardfs_super_list; /* for packagelist.c */ -extern appid_t get_appid(void *pkgl_id, const char *app_name); +extern appid_t get_appid(const char *app_name); extern int check_caller_access_to_name(struct inode *parent_node, const char* name); extern int open_flags_to_access_mode(int open_flags); extern int packagelist_init(void); From 0fc1aa5c0b5058c754059560a4af7fbfa478e831 Mon Sep 17 00:00:00 2001 From: Daniel Rosenberg Date: Wed, 18 May 2016 16:57:10 -0700 Subject: [PATCH 0304/1103] ANDROID: sdcardfs: Added top to sdcardfs_inode_info Adding packages to the package list and moving files takes a large amount of locks, and is currently a heavy operation. This adds a 'top' field to the inode_info, which points to the inode for the top most directory whose owner you would like to match. On permission checks and get_attr, we look up the owner based on the information at top. When we change a package mapping, we need only modify the information in the corresponding top inode_info's. When renaming, we must ensure top is set correctly in all children. This happens when an app specific folder gets moved outside of the folder for that app. Change-Id: Ib749c60b568e9a45a46f8ceed985c1338246ec6c Signed-off-by: Daniel Rosenberg --- fs/sdcardfs/derived_perm.c | 73 ++++++++++++++++++++++++++++++++++---- fs/sdcardfs/inode.c | 45 +++++++++++++++++++---- fs/sdcardfs/main.c | 4 +-- fs/sdcardfs/packagelist.c | 12 +++---- fs/sdcardfs/sdcardfs.h | 40 ++++++++++++++++++--- fs/sdcardfs/super.c | 1 + 6 files changed, 149 insertions(+), 26 deletions(-) diff --git a/fs/sdcardfs/derived_perm.c b/fs/sdcardfs/derived_perm.c index 2a75ad873a7c..89daf69efbaa 100644 --- a/fs/sdcardfs/derived_perm.c +++ b/fs/sdcardfs/derived_perm.c @@ -30,11 +30,12 @@ static void inherit_derived_state(struct inode *parent, struct inode *child) ci->userid = pi->userid; ci->d_uid = pi->d_uid; ci->under_android = pi->under_android; + set_top(ci, pi->top); } /* helper function for derived state */ -void setup_derived_state(struct inode *inode, perm_t perm, - userid_t userid, uid_t uid, bool under_android) +void setup_derived_state(struct inode *inode, perm_t perm, userid_t userid, + uid_t uid, bool under_android, struct inode *top) { struct sdcardfs_inode_info *info = SDCARDFS_I(inode); @@ -42,6 +43,7 @@ void setup_derived_state(struct inode *inode, perm_t perm, info->userid = userid; info->d_uid = uid; info->under_android = under_android; + set_top(info, top); } /* While renaming, there is a point where we want the path from dentry, but the name from newdentry */ @@ -70,6 +72,7 @@ void get_derived_permission_new(struct dentry *parent, struct dentry *dentry, st /* Legacy internal layout places users at top level */ info->perm = PERM_ROOT; info->userid = simple_strtoul(newdentry->d_name.name, NULL, 10); + set_top(info, &info->vfs_inode); break; case PERM_ROOT: /* Assume masked off by default. */ @@ -77,19 +80,23 @@ void get_derived_permission_new(struct dentry *parent, struct dentry *dentry, st /* App-specific directories inside; let anyone traverse */ info->perm = PERM_ANDROID; info->under_android = true; + set_top(info, &info->vfs_inode); } break; case PERM_ANDROID: if (!strcasecmp(newdentry->d_name.name, "data")) { /* App-specific directories inside; let anyone traverse */ info->perm = PERM_ANDROID_DATA; + set_top(info, &info->vfs_inode); } else if (!strcasecmp(newdentry->d_name.name, "obb")) { /* App-specific directories inside; let anyone traverse */ info->perm = PERM_ANDROID_OBB; + set_top(info, &info->vfs_inode); /* Single OBB directory is always shared */ } else if (!strcasecmp(newdentry->d_name.name, "media")) { /* App-specific directories inside; let anyone traverse */ info->perm = PERM_ANDROID_MEDIA; + set_top(info, &info->vfs_inode); } break; case PERM_ANDROID_DATA: @@ -99,6 +106,7 @@ void get_derived_permission_new(struct dentry *parent, struct dentry *dentry, st if (appid != 0) { info->d_uid = multiuser_get_uid(parent_info->userid, appid); } + set_top(info, &info->vfs_inode); break; } } @@ -108,14 +116,65 @@ void get_derived_permission(struct dentry *parent, struct dentry *dentry) get_derived_permission_new(parent, dentry, dentry); } -void get_derive_permissions_recursive(struct dentry *parent) { +static int descendant_may_need_fixup(perm_t perm) { + if (perm == PERM_PRE_ROOT || perm == PERM_ROOT || perm == PERM_ANDROID) + return 1; + return 0; +} + +static int needs_fixup(perm_t perm) { + if (perm == PERM_ANDROID_DATA || perm == PERM_ANDROID_OBB + || perm == PERM_ANDROID_MEDIA) + return 1; + return 0; +} + +void fixup_perms_recursive(struct dentry *dentry, const char* name, size_t len) { + struct dentry *child; + struct sdcardfs_inode_info *info; + if (!dget(dentry)) + return; + if (!dentry->d_inode) { + dput(dentry); + return; + } + info = SDCARDFS_I(d_inode(dentry)); + + if (needs_fixup(info->perm)) { + mutex_lock(&d_inode(dentry)->i_mutex); + child = lookup_one_len(name, dentry, len); + mutex_unlock(&d_inode(dentry)->i_mutex); + if (!IS_ERR(child)) { + if (child->d_inode) { + get_derived_permission(dentry, child); + fix_derived_permission(d_inode(child)); + } + dput(child); + } + } else if (descendant_may_need_fixup(info->perm)) { + mutex_lock(&d_inode(dentry)->i_mutex); + list_for_each_entry(child, &dentry->d_subdirs, d_child) { + fixup_perms_recursive(child, name, len); + } + mutex_unlock(&d_inode(dentry)->i_mutex); + } + dput(dentry); +} + +void fixup_top_recursive(struct dentry *parent) { struct dentry *dentry; + struct sdcardfs_inode_info *info; + if (!d_inode(parent)) + return; + info = SDCARDFS_I(d_inode(parent)); spin_lock(&parent->d_lock); list_for_each_entry(dentry, &parent->d_subdirs, d_child) { - if (dentry->d_inode) { - get_derived_permission(parent, dentry); - fix_derived_permission(dentry->d_inode); - get_derive_permissions_recursive(dentry); + if (d_inode(dentry)) { + if (SDCARDFS_I(d_inode(parent))->top != SDCARDFS_I(d_inode(dentry))->top) { + get_derived_permission(parent, dentry); + fix_derived_permission(d_inode(dentry)); + fixup_top_recursive(dentry); + } } } spin_unlock(&parent->d_lock); diff --git a/fs/sdcardfs/inode.c b/fs/sdcardfs/inode.c index 1a23c0cc8f58..67bcee2c379a 100644 --- a/fs/sdcardfs/inode.c +++ b/fs/sdcardfs/inode.c @@ -515,7 +515,7 @@ static int sdcardfs_rename(struct inode *old_dir, struct dentry *old_dentry, * we pass along new_dentry for the name.*/ get_derived_permission_new(new_dentry->d_parent, old_dentry, new_dentry); fix_derived_permission(d_inode(old_dentry)); - get_derive_permissions_recursive(old_dentry); + fixup_top_recursive(old_dentry); out: unlock_rename(lower_old_dir_dentry, lower_new_dir_dentry); dput(lower_old_dir_dentry); @@ -587,6 +587,16 @@ static const char *sdcardfs_follow_link(struct dentry *dentry, void **cookie) static int sdcardfs_permission(struct inode *inode, int mask) { int err; + struct inode *top = grab_top(SDCARDFS_I(inode)); + + if (!top) + return -EINVAL; + /* Ensure owner is up to date */ + if (!uid_eq(inode->i_uid, top->i_uid)) { + SDCARDFS_I(inode)->d_uid = SDCARDFS_I(top)->d_uid; + fix_derived_permission(inode); + } + release_top(SDCARDFS_I(inode)); /* * Permission check on sdcardfs inode. @@ -725,6 +735,30 @@ static int sdcardfs_setattr(struct dentry *dentry, struct iattr *ia) return err; } +static int sdcardfs_fillattr(struct inode *inode, struct kstat *stat) +{ + struct sdcardfs_inode_info *info = SDCARDFS_I(inode); + struct inode *top = grab_top(info); + if (!top) + return -EINVAL; + + stat->dev = inode->i_sb->s_dev; + stat->ino = inode->i_ino; + stat->mode = (inode->i_mode & S_IFMT) | get_mode(SDCARDFS_I(top)); + stat->nlink = inode->i_nlink; + stat->uid = make_kuid(&init_user_ns, SDCARDFS_I(top)->d_uid); + stat->gid = make_kgid(&init_user_ns, get_gid(SDCARDFS_I(top))); + stat->rdev = inode->i_rdev; + stat->size = i_size_read(inode); + stat->atime = inode->i_atime; + stat->mtime = inode->i_mtime; + stat->ctime = inode->i_ctime; + stat->blksize = (1 << inode->i_blkbits); + stat->blocks = inode->i_blocks; + release_top(info); + return 0; +} + static int sdcardfs_getattr(struct vfsmount *mnt, struct dentry *dentry, struct kstat *stat) { @@ -733,6 +767,7 @@ static int sdcardfs_getattr(struct vfsmount *mnt, struct dentry *dentry, struct inode *lower_inode; struct path lower_path; struct dentry *parent; + int err; parent = dget_parent(dentry); if(!check_caller_access_to_name(d_inode(parent), dentry->d_name.name)) { @@ -750,14 +785,12 @@ static int sdcardfs_getattr(struct vfsmount *mnt, struct dentry *dentry, lower_dentry = lower_path.dentry; lower_inode = sdcardfs_lower_inode(inode); - sdcardfs_copy_and_fix_attrs(inode, lower_inode); fsstack_copy_inode_size(inode, lower_inode); - - generic_fillattr(inode, stat); + err = sdcardfs_fillattr(inode, stat); sdcardfs_put_lower_path(dentry, &lower_path); - return 0; + return err; } const struct inode_operations sdcardfs_symlink_iops = { @@ -775,9 +808,7 @@ const struct inode_operations sdcardfs_symlink_iops = { const struct inode_operations sdcardfs_dir_iops = { .create = sdcardfs_create, .lookup = sdcardfs_lookup, -#if 0 .permission = sdcardfs_permission, -#endif .unlink = sdcardfs_unlink, .mkdir = sdcardfs_mkdir, .rmdir = sdcardfs_rmdir, diff --git a/fs/sdcardfs/main.c b/fs/sdcardfs/main.c index a6522286d731..6d526bf3d956 100644 --- a/fs/sdcardfs/main.c +++ b/fs/sdcardfs/main.c @@ -268,13 +268,13 @@ static int sdcardfs_read_super(struct super_block *sb, const char *dev_name, sb_info->obbpath_s = kzalloc(PATH_MAX, GFP_KERNEL); mutex_lock(&sdcardfs_super_list_lock); if(sb_info->options.multiuser) { - setup_derived_state(sb->s_root->d_inode, PERM_PRE_ROOT, sb_info->options.fs_user_id, AID_ROOT, false); + setup_derived_state(d_inode(sb->s_root), PERM_PRE_ROOT, sb_info->options.fs_user_id, AID_ROOT, false, d_inode(sb->s_root)); snprintf(sb_info->obbpath_s, PATH_MAX, "%s/obb", dev_name); /*err = prepare_dir(sb_info->obbpath_s, sb_info->options.fs_low_uid, sb_info->options.fs_low_gid, 00755);*/ } else { - setup_derived_state(sb->s_root->d_inode, PERM_ROOT, sb_info->options.fs_low_uid, AID_ROOT, false); + setup_derived_state(sb->s_root->d_inode, PERM_ROOT, sb_info->options.fs_low_uid, AID_ROOT, false, sb->s_root->d_inode); snprintf(sb_info->obbpath_s, PATH_MAX, "%s/Android/obb", dev_name); } fix_derived_permission(sb->s_root->d_inode); diff --git a/fs/sdcardfs/packagelist.c b/fs/sdcardfs/packagelist.c index f5a49c513568..03776fa5f26c 100644 --- a/fs/sdcardfs/packagelist.c +++ b/fs/sdcardfs/packagelist.c @@ -143,18 +143,18 @@ static int insert_packagelist_entry_locked(const char *key, appid_t value) return 0; } -static void fixup_perms(struct super_block *sb) { +static void fixup_perms(struct super_block *sb, const char *key) { if (sb && sb->s_magic == SDCARDFS_SUPER_MAGIC) { - get_derive_permissions_recursive(sb->s_root); + fixup_perms_recursive(sb->s_root, key, strlen(key)); } } -static void fixup_all_perms(void) +static void fixup_all_perms(const char *key) { struct sdcardfs_sb_info *sbinfo; list_for_each_entry(sbinfo, &sdcardfs_super_list, list) if (sbinfo) - fixup_perms(sbinfo->sb); + fixup_perms(sbinfo->sb, key); } static int insert_packagelist_entry(const char *key, appid_t value) @@ -164,7 +164,7 @@ static int insert_packagelist_entry(const char *key, appid_t value) mutex_lock(&sdcardfs_super_list_lock); err = insert_packagelist_entry_locked(key, value); if (!err) - fixup_all_perms(); + fixup_all_perms(key); mutex_unlock(&sdcardfs_super_list_lock); return err; @@ -196,7 +196,7 @@ static void remove_packagelist_entry(const char *key) { mutex_lock(&sdcardfs_super_list_lock); remove_packagelist_entry_locked(key); - fixup_all_perms(); + fixup_all_perms(key); mutex_unlock(&sdcardfs_super_list_lock); return; } diff --git a/fs/sdcardfs/sdcardfs.h b/fs/sdcardfs/sdcardfs.h index 75284f339ae0..cfda98d257b6 100644 --- a/fs/sdcardfs/sdcardfs.h +++ b/fs/sdcardfs/sdcardfs.h @@ -169,6 +169,8 @@ struct sdcardfs_inode_info { userid_t userid; uid_t d_uid; bool under_android; + /* top folder for ownership */ + struct inode *top; struct inode vfs_inode; }; @@ -321,6 +323,35 @@ static inline void sdcardfs_put_reset_##pname(const struct dentry *dent) \ SDCARDFS_DENT_FUNC(lower_path) SDCARDFS_DENT_FUNC(orig_path) +/* grab a refererence if we aren't linking to ourself */ +static inline void set_top(struct sdcardfs_inode_info *info, struct inode *top) +{ + struct inode *old_top = NULL; + BUG_ON(IS_ERR_OR_NULL(top)); + if (info->top && info->top != &info->vfs_inode) { + old_top = info->top; + } + if (top != &info->vfs_inode) + igrab(top); + info->top = top; + iput(old_top); +} + +static inline struct inode *grab_top(struct sdcardfs_inode_info *info) +{ + struct inode *top = info->top; + if (top) { + return igrab(top); + } else { + return NULL; + } +} + +static inline void release_top(struct sdcardfs_inode_info *info) +{ + iput(info->top); +} + static inline int get_gid(struct sdcardfs_inode_info *info) { struct sdcardfs_sb_info *sb_info = SDCARDFS_SB(info->vfs_inode.i_sb); if (sb_info->options.gid == AID_SDCARD_RW) { @@ -337,7 +368,7 @@ static inline int get_gid(struct sdcardfs_inode_info *info) { static inline int get_mode(struct sdcardfs_inode_info *info) { int owner_mode; int filtered_mode; - struct sdcardfs_sb_info *sb_info = SDCARDFS_SB(info->vfs_inode.i_sb); + struct sdcardfs_sb_info * sb_info = SDCARDFS_SB(info->vfs_inode.i_sb); int visible_mode = 0775 & ~sb_info->options.mask; if (info->perm == PERM_PRE_ROOT) { @@ -403,11 +434,12 @@ extern int packagelist_init(void); extern void packagelist_exit(void); /* for derived_perm.c */ -extern void setup_derived_state(struct inode *inode, perm_t perm, - userid_t userid, uid_t uid, bool under_android); +extern void setup_derived_state(struct inode *inode, perm_t perm, userid_t userid, + uid_t uid, bool under_android, struct inode *top); extern void get_derived_permission(struct dentry *parent, struct dentry *dentry); extern void get_derived_permission_new(struct dentry *parent, struct dentry *dentry, struct dentry *newdentry); -extern void get_derive_permissions_recursive(struct dentry *parent); +extern void fixup_top_recursive(struct dentry *parent); +extern void fixup_perms_recursive(struct dentry *dentry, const char *name, size_t len); extern void update_derived_permission_lock(struct dentry *dentry); extern int need_graft_path(struct dentry *dentry); diff --git a/fs/sdcardfs/super.c b/fs/sdcardfs/super.c index 1d6490128c99..0a465395aab7 100644 --- a/fs/sdcardfs/super.c +++ b/fs/sdcardfs/super.c @@ -126,6 +126,7 @@ static void sdcardfs_evict_inode(struct inode *inode) */ lower_inode = sdcardfs_lower_inode(inode); sdcardfs_set_lower_inode(inode, NULL); + set_top(SDCARDFS_I(inode), inode); iput(lower_inode); } From e0e93d5ea9f3377fa5918f223e8cca53809ffb66 Mon Sep 17 00:00:00 2001 From: alvin_liang Date: Mon, 19 Sep 2016 16:59:12 +0800 Subject: [PATCH 0305/1103] ANDROID: sdcardfs: fix external storage exporting incorrect uid Symptom: App cannot write into per-app folder Root Cause: sdcardfs exports incorrect uid Solution: fix uid Project: All Note: Test done by RD: passed Change-Id: Iff64f6f40ba4c679f07f4426d3db6e6d0db7e3ca --- fs/sdcardfs/main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/sdcardfs/main.c b/fs/sdcardfs/main.c index 6d526bf3d956..2decea3d1e3e 100644 --- a/fs/sdcardfs/main.c +++ b/fs/sdcardfs/main.c @@ -274,7 +274,7 @@ static int sdcardfs_read_super(struct super_block *sb, const char *dev_name, sb_info->options.fs_low_uid, sb_info->options.fs_low_gid, 00755);*/ } else { - setup_derived_state(sb->s_root->d_inode, PERM_ROOT, sb_info->options.fs_low_uid, AID_ROOT, false, sb->s_root->d_inode); + setup_derived_state(d_inode(sb->s_root), PERM_ROOT, sb_info->options.fs_user_id, AID_ROOT, false, d_inode(sb->s_root)); snprintf(sb_info->obbpath_s, PATH_MAX, "%s/Android/obb", dev_name); } fix_derived_permission(sb->s_root->d_inode); From ecf3f8b2449fac57dc6762ea96d5b9c0ceb1a3e9 Mon Sep 17 00:00:00 2001 From: Daniel Rosenberg Date: Mon, 26 Sep 2016 14:48:22 -0700 Subject: [PATCH 0306/1103] ANDROID: sdcardfs: Move directory unlock before touch This removes a deadlock under low memory conditions. filp_open can call lookup_slow, which will attempt to lock the parent. Change-Id: I940643d0793f5051d1e79a56f4da2fa8ca3d8ff7 Signed-off-by: Daniel Rosenberg --- fs/sdcardfs/inode.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/fs/sdcardfs/inode.c b/fs/sdcardfs/inode.c index 67bcee2c379a..3c353c95ef3e 100644 --- a/fs/sdcardfs/inode.c +++ b/fs/sdcardfs/inode.c @@ -296,14 +296,17 @@ static int sdcardfs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode copied_fs = copy_fs_struct(current->fs); if (!copied_fs) { err = -ENOMEM; + unlock_dir(lower_parent_dentry); goto out_unlock; } current->fs = copied_fs; current->fs->umask = 0; err = vfs_mkdir(d_inode(lower_parent_dentry), lower_dentry, mode); - if (err) + if (err) { + unlock_dir(lower_parent_dentry); goto out; + } /* if it is a local obb dentry, setup it with the base obbpath */ if(need_graft_path(dentry)) { @@ -325,14 +328,18 @@ static int sdcardfs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode } err = sdcardfs_interpose(dentry, dir->i_sb, &lower_path, pi->userid); - if (err) + if (err) { + unlock_dir(lower_parent_dentry); goto out; + } fsstack_copy_attr_times(dir, sdcardfs_lower_inode(dir)); fsstack_copy_inode_size(dir, d_inode(lower_parent_dentry)); /* update number of links on parent directory */ set_nlink(dir, sdcardfs_lower_inode(dir)->i_nlink); + unlock_dir(lower_parent_dentry); + if ((!sbi->options.multiuser) && (!strcasecmp(dentry->d_name.name, "obb")) && (pi->perm == PERM_ANDROID) && (pi->userid == 0)) make_nomedia_in_obb = 1; @@ -352,7 +359,6 @@ static int sdcardfs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode current->fs = saved_fs; free_fs_struct(copied_fs); out_unlock: - unlock_dir(lower_parent_dentry); sdcardfs_put_lower_path(dentry, &lower_path); out_revert: REVERT_CRED(saved_cred); From 1c12f327b4e24111359b6d651519efc6c32275a6 Mon Sep 17 00:00:00 2001 From: Daniel Rosenberg Date: Wed, 26 Oct 2016 16:48:45 -0700 Subject: [PATCH 0307/1103] ANDROID: sdcardfs: User new permission2 functions Change-Id: Ic7e0fb8fdcebb31e657b079fe02ac834c4a50db9 Signed-off-by: Daniel Rosenberg --- fs/sdcardfs/inode.c | 27 +++++++++++++++++++++------ fs/sdcardfs/sdcardfs.h | 4 ++-- 2 files changed, 23 insertions(+), 8 deletions(-) diff --git a/fs/sdcardfs/inode.c b/fs/sdcardfs/inode.c index 3c353c95ef3e..503501388ef9 100644 --- a/fs/sdcardfs/inode.c +++ b/fs/sdcardfs/inode.c @@ -54,6 +54,7 @@ static int sdcardfs_create(struct inode *dir, struct dentry *dentry, { int err; struct dentry *lower_dentry; + struct vfsmount *lower_dentry_mnt; struct dentry *lower_parent_dentry = NULL; struct path lower_path; const struct cred *saved_cred = NULL; @@ -73,6 +74,7 @@ static int sdcardfs_create(struct inode *dir, struct dentry *dentry, sdcardfs_get_lower_path(dentry, &lower_path); lower_dentry = lower_path.dentry; + lower_dentry_mnt = lower_path.mnt; lower_parent_dentry = lock_parent(lower_dentry); /* set last 16bytes of mode field to 0664 */ @@ -87,7 +89,7 @@ static int sdcardfs_create(struct inode *dir, struct dentry *dentry, } current->fs = copied_fs; current->fs->umask = 0; - err = vfs_create(d_inode(lower_parent_dentry), lower_dentry, mode, want_excl); + err = vfs_create2(lower_dentry_mnt, d_inode(lower_parent_dentry), lower_dentry, mode, want_excl); if (err) goto out; @@ -154,6 +156,7 @@ static int sdcardfs_unlink(struct inode *dir, struct dentry *dentry) { int err; struct dentry *lower_dentry; + struct vfsmount *lower_mnt; struct inode *lower_dir_inode = sdcardfs_lower_inode(dir); struct dentry *lower_dir_dentry; struct path lower_path; @@ -172,10 +175,11 @@ static int sdcardfs_unlink(struct inode *dir, struct dentry *dentry) sdcardfs_get_lower_path(dentry, &lower_path); lower_dentry = lower_path.dentry; + lower_mnt = lower_path.mnt; dget(lower_dentry); lower_dir_dentry = lock_parent(lower_dentry); - err = vfs_unlink(lower_dir_inode, lower_dentry, NULL); + err = vfs_unlink2(lower_mnt, lower_dir_inode, lower_dentry, NULL); /* * Note: unlinking on top of NFS can cause silly-renamed files. @@ -256,6 +260,7 @@ static int sdcardfs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode int err; int make_nomedia_in_obb = 0; struct dentry *lower_dentry; + struct vfsmount *lower_mnt; struct dentry *lower_parent_dentry = NULL; struct path lower_path; struct sdcardfs_sb_info *sbi = SDCARDFS_SB(dentry->d_sb); @@ -286,6 +291,7 @@ static int sdcardfs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode /* the lower_dentry is negative here */ sdcardfs_get_lower_path(dentry, &lower_path); lower_dentry = lower_path.dentry; + lower_mnt = lower_path.mnt; lower_parent_dentry = lock_parent(lower_dentry); /* set last 16bytes of mode field to 0775 */ @@ -301,7 +307,7 @@ static int sdcardfs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode } current->fs = copied_fs; current->fs->umask = 0; - err = vfs_mkdir(d_inode(lower_parent_dentry), lower_dentry, mode); + err = vfs_mkdir2(lower_mnt, d_inode(lower_parent_dentry), lower_dentry, mode); if (err) { unlock_dir(lower_parent_dentry); @@ -370,6 +376,7 @@ static int sdcardfs_rmdir(struct inode *dir, struct dentry *dentry) { struct dentry *lower_dentry; struct dentry *lower_dir_dentry; + struct vfsmount *lower_mnt; int err; struct path lower_path; const struct cred *saved_cred = NULL; @@ -390,9 +397,10 @@ static int sdcardfs_rmdir(struct inode *dir, struct dentry *dentry) sdcardfs_get_real_lower(dentry, &lower_path); lower_dentry = lower_path.dentry; + lower_mnt = lower_path.mnt; lower_dir_dentry = lock_parent(lower_dentry); - err = vfs_rmdir(d_inode(lower_dir_dentry), lower_dentry); + err = vfs_rmdir2(lower_mnt, d_inode(lower_dir_dentry), lower_dentry); if (err) goto out; @@ -456,6 +464,7 @@ static int sdcardfs_rename(struct inode *old_dir, struct dentry *old_dentry, struct dentry *lower_new_dentry = NULL; struct dentry *lower_old_dir_dentry = NULL; struct dentry *lower_new_dir_dentry = NULL; + struct vfsmount *lower_mnt = NULL; struct dentry *trap = NULL; struct dentry *new_parent = NULL; struct path lower_old_path, lower_new_path; @@ -477,6 +486,7 @@ static int sdcardfs_rename(struct inode *old_dir, struct dentry *old_dentry, sdcardfs_get_lower_path(new_dentry, &lower_new_path); lower_old_dentry = lower_old_path.dentry; lower_new_dentry = lower_new_path.dentry; + lower_mnt = lower_old_path.mnt; lower_old_dir_dentry = dget_parent(lower_old_dentry); lower_new_dir_dentry = dget_parent(lower_new_dentry); @@ -492,7 +502,8 @@ static int sdcardfs_rename(struct inode *old_dir, struct dentry *old_dentry, goto out; } - err = vfs_rename(d_inode(lower_old_dir_dentry), lower_old_dentry, + err = vfs_rename2(lower_mnt, + d_inode(lower_old_dir_dentry), lower_old_dentry, d_inode(lower_new_dir_dentry), lower_new_dentry, NULL, 0); if (err) @@ -595,6 +606,8 @@ static int sdcardfs_permission(struct inode *inode, int mask) int err; struct inode *top = grab_top(SDCARDFS_I(inode)); + if (IS_ERR(mnt)) + return PTR_ERR(mnt); if (!top) return -EINVAL; /* Ensure owner is up to date */ @@ -642,6 +655,7 @@ static int sdcardfs_setattr(struct dentry *dentry, struct iattr *ia) { int err; struct dentry *lower_dentry; + struct vfsmount *lower_mnt; struct inode *inode; struct inode *lower_inode; struct path lower_path; @@ -675,6 +689,7 @@ static int sdcardfs_setattr(struct dentry *dentry, struct iattr *ia) sdcardfs_get_lower_path(dentry, &lower_path); lower_dentry = lower_path.dentry; + lower_mnt = lower_path.mnt; lower_inode = sdcardfs_lower_inode(inode); /* prepare our own lower struct iattr (with the lower file) */ @@ -718,7 +733,7 @@ static int sdcardfs_setattr(struct dentry *dentry, struct iattr *ia) * tries to open(), unlink(), then ftruncate() a file. */ mutex_lock(&d_inode(lower_dentry)->i_mutex); - err = notify_change(lower_dentry, &lower_ia, /* note: lower_ia */ + err = notify_change2(lower_mnt, lower_dentry, &lower_ia, /* note: lower_ia */ NULL); mutex_unlock(&d_inode(lower_dentry)->i_mutex); if (current->mm) diff --git a/fs/sdcardfs/sdcardfs.h b/fs/sdcardfs/sdcardfs.h index cfda98d257b6..5132f1dc5a4d 100644 --- a/fs/sdcardfs/sdcardfs.h +++ b/fs/sdcardfs/sdcardfs.h @@ -476,7 +476,7 @@ static inline int prepare_dir(const char *path_s, uid_t uid, gid_t gid, mode_t m goto out_unlock; } - err = vfs_mkdir(d_inode(parent.dentry), dent, mode); + err = vfs_mkdir2(parent.mnt, d_inode(parent.dentry), dent, mode); if (err) { if (err == -EEXIST) err = 0; @@ -487,7 +487,7 @@ static inline int prepare_dir(const char *path_s, uid_t uid, gid_t gid, mode_t m attrs.ia_gid = make_kgid(&init_user_ns, gid); attrs.ia_valid = ATTR_UID | ATTR_GID; mutex_lock(&d_inode(dent)->i_mutex); - notify_change(dent, &attrs, NULL); + notify_change2(parent.mnt, dent, &attrs, NULL); mutex_unlock(&d_inode(dent)->i_mutex); out_dput: From 698e4c315b1836b245dbd550b01084c39f9693cb Mon Sep 17 00:00:00 2001 From: Daniel Rosenberg Date: Wed, 26 Oct 2016 17:36:05 -0700 Subject: [PATCH 0308/1103] ANDROID: sdcardfs: Add gid and mask to private mount data Adds support for mount2, remount2, and the functions to allocate/clone/copy the private data The next patch will switch over to actually using it. Change-Id: I8a43da26021d33401f655f0b2784ead161c575e3 Signed-off-by: Daniel Rosenberg --- fs/sdcardfs/main.c | 103 ++++++++++++++++++++++++++++++++++++----- fs/sdcardfs/sdcardfs.h | 8 ++++ fs/sdcardfs/super.c | 64 ++++++++++++++++++++++--- 3 files changed, 157 insertions(+), 18 deletions(-) diff --git a/fs/sdcardfs/main.c b/fs/sdcardfs/main.c index 2decea3d1e3e..5400e7e63d27 100644 --- a/fs/sdcardfs/main.c +++ b/fs/sdcardfs/main.c @@ -49,7 +49,8 @@ static const match_table_t sdcardfs_tokens = { }; static int parse_options(struct super_block *sb, char *options, int silent, - int *debug, struct sdcardfs_mount_options *opts) + int *debug, struct sdcardfs_vfsmount_options *vfsopts, + struct sdcardfs_mount_options *opts) { char *p; substring_t args[MAX_OPT_ARGS]; @@ -58,9 +59,11 @@ static int parse_options(struct super_block *sb, char *options, int silent, /* by default, we use AID_MEDIA_RW as uid, gid */ opts->fs_low_uid = AID_MEDIA_RW; opts->fs_low_gid = AID_MEDIA_RW; + vfsopts->mask = 0; opts->mask = 0; opts->multiuser = false; opts->fs_user_id = 0; + vfsopts->gid = 0; opts->gid = 0; /* by default, 0MB is reserved */ opts->reserved_mb = 0; @@ -95,6 +98,7 @@ static int parse_options(struct super_block *sb, char *options, int silent, if (match_int(&args[0], &option)) return 0; opts->gid = option; + vfsopts->gid = option; break; case Opt_userid: if (match_int(&args[0], &option)) @@ -105,6 +109,7 @@ static int parse_options(struct super_block *sb, char *options, int silent, if (match_int(&args[0], &option)) return 0; opts->mask = option; + vfsopts->mask = option; break; case Opt_multiuser: opts->multiuser = true; @@ -135,6 +140,65 @@ static int parse_options(struct super_block *sb, char *options, int silent, return 0; } +int parse_options_remount(struct super_block *sb, char *options, int silent, + struct sdcardfs_vfsmount_options *vfsopts) +{ + char *p; + substring_t args[MAX_OPT_ARGS]; + int option; + int debug; + + if (!options) + return 0; + + while ((p = strsep(&options, ",")) != NULL) { + int token; + if (!*p) + continue; + + token = match_token(p, sdcardfs_tokens, args); + + switch (token) { + case Opt_debug: + debug = 1; + break; + case Opt_gid: + if (match_int(&args[0], &option)) + return 0; + vfsopts->gid = option; + + break; + case Opt_mask: + if (match_int(&args[0], &option)) + return 0; + vfsopts->mask = option; + break; + case Opt_multiuser: + case Opt_userid: + case Opt_fsuid: + case Opt_fsgid: + case Opt_reserved_mb: + printk( KERN_WARNING "Option \"%s\" can't be changed during remount\n", p); + break; + /* unknown option */ + default: + if (!silent) { + printk( KERN_ERR "Unrecognized mount option \"%s\" " + "or missing value", p); + } + return -EINVAL; + } + } + + if (debug) { + printk( KERN_INFO "sdcardfs : options - debug:%d\n", debug); + printk( KERN_INFO "sdcardfs : options - gid:%d\n", vfsopts->gid); + printk( KERN_INFO "sdcardfs : options - mask:%d\n", vfsopts->mask); + } + + return 0; +} + #if 0 /* * our custom d_alloc_root work-alike @@ -172,14 +236,15 @@ EXPORT_SYMBOL_GPL(sdcardfs_super_list); * There is no need to lock the sdcardfs_super_info's rwsem as there is no * way anyone can have a reference to the superblock at this point in time. */ -static int sdcardfs_read_super(struct super_block *sb, const char *dev_name, - void *raw_data, int silent) +static int sdcardfs_read_super(struct vfsmount *mnt, struct super_block *sb, + const char *dev_name, void *raw_data, int silent) { int err = 0; int debug; struct super_block *lower_sb; struct path lower_path; struct sdcardfs_sb_info *sb_info; + struct sdcardfs_vfsmount_options *mnt_opt = mnt->data; struct inode *inode; printk(KERN_INFO "sdcardfs version 2.0\n"); @@ -212,7 +277,7 @@ static int sdcardfs_read_super(struct super_block *sb, const char *dev_name, sb_info = sb->s_fs_info; /* parse options */ - err = parse_options(sb, raw_data, silent, &debug, &sb_info->options); + err = parse_options(sb, raw_data, silent, &debug, mnt_opt, &sb_info->options); if (err) { printk(KERN_ERR "sdcardfs: invalid options\n"); goto out_freesbi; @@ -306,9 +371,9 @@ static int sdcardfs_read_super(struct super_block *sb, const char *dev_name, } /* A feature which supports mount_nodev() with options */ -static struct dentry *mount_nodev_with_options(struct file_system_type *fs_type, - int flags, const char *dev_name, void *data, - int (*fill_super)(struct super_block *, const char *, void *, int)) +static struct dentry *mount_nodev_with_options(struct vfsmount *mnt, + struct file_system_type *fs_type, int flags, const char *dev_name, void *data, + int (*fill_super)(struct vfsmount *, struct super_block *, const char *, void *, int)) { int error; @@ -319,7 +384,7 @@ static struct dentry *mount_nodev_with_options(struct file_system_type *fs_type, s->s_flags = flags; - error = fill_super(s, dev_name, data, flags & MS_SILENT ? 1 : 0); + error = fill_super(mnt, s, dev_name, data, flags & MS_SILENT ? 1 : 0); if (error) { deactivate_locked_super(s); return ERR_PTR(error); @@ -328,15 +393,27 @@ static struct dentry *mount_nodev_with_options(struct file_system_type *fs_type, return dget(s->s_root); } -struct dentry *sdcardfs_mount(struct file_system_type *fs_type, int flags, +static struct dentry *sdcardfs_mount(struct vfsmount *mnt, + struct file_system_type *fs_type, int flags, const char *dev_name, void *raw_data) { /* * dev_name is a lower_path_name, * raw_data is a option string. */ - return mount_nodev_with_options(fs_type, flags, dev_name, - raw_data, sdcardfs_read_super); + return mount_nodev_with_options(mnt, fs_type, flags, dev_name, + raw_data, sdcardfs_read_super); +} + +static struct dentry *sdcardfs_mount_wrn(struct file_system_type *fs_type, int flags, + const char *dev_name, void *raw_data) +{ + WARN(1, "sdcardfs does not support mount. Use mount2.\n"); + return ERR_PTR(-EINVAL); +} + +void *sdcardfs_alloc_mnt_data(void) { + return kmalloc(sizeof(struct sdcardfs_vfsmount_options), GFP_KERNEL); } void sdcardfs_kill_sb(struct super_block *sb) { @@ -353,7 +430,9 @@ void sdcardfs_kill_sb(struct super_block *sb) { static struct file_system_type sdcardfs_fs_type = { .owner = THIS_MODULE, .name = SDCARDFS_NAME, - .mount = sdcardfs_mount, + .mount = sdcardfs_mount_wrn, + .mount2 = sdcardfs_mount, + .alloc_mnt_data = sdcardfs_alloc_mnt_data, .kill_sb = sdcardfs_kill_sb, .fs_flags = 0, }; diff --git a/fs/sdcardfs/sdcardfs.h b/fs/sdcardfs/sdcardfs.h index 5132f1dc5a4d..22ef29857022 100644 --- a/fs/sdcardfs/sdcardfs.h +++ b/fs/sdcardfs/sdcardfs.h @@ -193,6 +193,14 @@ struct sdcardfs_mount_options { unsigned int reserved_mb; }; +struct sdcardfs_vfsmount_options { + gid_t gid; + mode_t mask; +}; + +extern int parse_options_remount(struct super_block *sb, char *options, int silent, + struct sdcardfs_vfsmount_options *vfsopts); + /* sdcardfs super-block data in memory */ struct sdcardfs_sb_info { struct super_block *sb; diff --git a/fs/sdcardfs/super.c b/fs/sdcardfs/super.c index 0a465395aab7..edda32b68dc0 100644 --- a/fs/sdcardfs/super.c +++ b/fs/sdcardfs/super.c @@ -108,6 +108,50 @@ static int sdcardfs_remount_fs(struct super_block *sb, int *flags, char *options return err; } +/* + * @mnt: mount point we are remounting + * @sb: superblock we are remounting + * @flags: numeric mount options + * @options: mount options string + */ +static int sdcardfs_remount_fs2(struct vfsmount *mnt, struct super_block *sb, + int *flags, char *options) +{ + int err = 0; + + /* + * The VFS will take care of "ro" and "rw" flags among others. We + * can safely accept a few flags (RDONLY, MANDLOCK), and honor + * SILENT, but anything else left over is an error. + */ + if ((*flags & ~(MS_RDONLY | MS_MANDLOCK | MS_SILENT | MS_REMOUNT)) != 0) { + printk(KERN_ERR + "sdcardfs: remount flags 0x%x unsupported\n", *flags); + err = -EINVAL; + } + printk(KERN_INFO "Remount options were %s for vfsmnt %p.\n", options, mnt); + err = parse_options_remount(sb, options, *flags & ~MS_SILENT, mnt->data); + + + return err; +} + +static void* sdcardfs_clone_mnt_data(void *data) { + struct sdcardfs_vfsmount_options* opt = kmalloc(sizeof(struct sdcardfs_vfsmount_options), GFP_KERNEL); + struct sdcardfs_vfsmount_options* old = data; + if(!opt) return NULL; + opt->gid = old->gid; + opt->mask = old->mask; + return opt; +} + +static void sdcardfs_copy_mnt_data(void *data, void *newdata) { + struct sdcardfs_vfsmount_options* old = data; + struct sdcardfs_vfsmount_options* new = newdata; + old->gid = new->gid; + old->mask = new->mask; +} + /* * Called by iput() when the inode reference count reached zero * and the inode is not hashed anywhere. Used to clear anything @@ -191,19 +235,24 @@ static void sdcardfs_umount_begin(struct super_block *sb) lower_sb->s_op->umount_begin(lower_sb); } -static int sdcardfs_show_options(struct seq_file *m, struct dentry *root) +static int sdcardfs_show_options(struct vfsmount *mnt, struct seq_file *m, struct dentry *root) { struct sdcardfs_sb_info *sbi = SDCARDFS_SB(root->d_sb); struct sdcardfs_mount_options *opts = &sbi->options; + struct sdcardfs_vfsmount_options *vfsopts = mnt->data; if (opts->fs_low_uid != 0) - seq_printf(m, ",uid=%u", opts->fs_low_uid); + seq_printf(m, ",fsuid=%u", opts->fs_low_uid); if (opts->fs_low_gid != 0) - seq_printf(m, ",gid=%u", opts->fs_low_gid); - + seq_printf(m, ",fsgid=%u", opts->fs_low_gid); + if (vfsopts->gid != 0) + seq_printf(m, ",gid=%u", vfsopts->gid); if (opts->multiuser) seq_printf(m, ",multiuser"); - + if (vfsopts->mask) + seq_printf(m, ",mask=%u", vfsopts->mask); + if (opts->fs_user_id) + seq_printf(m, ",userid=%u", opts->fs_user_id); if (opts->reserved_mb != 0) seq_printf(m, ",reserved=%uMB", opts->reserved_mb); @@ -214,9 +263,12 @@ const struct super_operations sdcardfs_sops = { .put_super = sdcardfs_put_super, .statfs = sdcardfs_statfs, .remount_fs = sdcardfs_remount_fs, + .remount_fs2 = sdcardfs_remount_fs2, + .clone_mnt_data = sdcardfs_clone_mnt_data, + .copy_mnt_data = sdcardfs_copy_mnt_data, .evict_inode = sdcardfs_evict_inode, .umount_begin = sdcardfs_umount_begin, - .show_options = sdcardfs_show_options, + .show_options2 = sdcardfs_show_options, .alloc_inode = sdcardfs_alloc_inode, .destroy_inode = sdcardfs_destroy_inode, .drop_inode = generic_delete_inode, From 44726124087dddd9c5bc447bd99377add2f08bbe Mon Sep 17 00:00:00 2001 From: Daniel Rosenberg Date: Wed, 26 Oct 2016 20:27:20 -0700 Subject: [PATCH 0309/1103] ANDROID: sdcardfs: Use per mount permissions This switches sdcardfs over to using permission2. Instead of mounting several sdcardfs instances onto the same underlaying directory, you bind mount a single mount several times, and remount with the options you want. These are stored in the private mount data, allowing you to maintain the same tree, but have different permissions for different mount points. Warning functions have been added for permission, as it should never be called, and the correct behavior is unclear. Change-Id: I841b1d70ec60cf2b866fa48edeb74a0b0f8334f5 Signed-off-by: Daniel Rosenberg --- fs/sdcardfs/derived_perm.c | 20 ++++-- fs/sdcardfs/inode.c | 128 ++++++++++++++++++++++++++++++------- fs/sdcardfs/lookup.c | 4 +- fs/sdcardfs/main.c | 8 +-- fs/sdcardfs/sdcardfs.h | 44 ++++++++----- 5 files changed, 151 insertions(+), 53 deletions(-) diff --git a/fs/sdcardfs/derived_perm.c b/fs/sdcardfs/derived_perm.c index 89daf69efbaa..066edbbb6ad6 100644 --- a/fs/sdcardfs/derived_perm.c +++ b/fs/sdcardfs/derived_perm.c @@ -141,13 +141,23 @@ void fixup_perms_recursive(struct dentry *dentry, const char* name, size_t len) info = SDCARDFS_I(d_inode(dentry)); if (needs_fixup(info->perm)) { + /* We need permission to fix up these values. + * Since permissions are based of of the mount, and + * we are accessing without the mount point, we create + * a fake mount with the permissions we will be using. + */ + struct vfsmount fakemnt; + struct sdcardfs_vfsmount_options opts; + fakemnt.data = &opts; + opts.gid = AID_SDCARD_RW; + opts.mask = 0; mutex_lock(&d_inode(dentry)->i_mutex); - child = lookup_one_len(name, dentry, len); + child = lookup_one_len2(name, &fakemnt, dentry, len); mutex_unlock(&d_inode(dentry)->i_mutex); if (!IS_ERR(child)) { - if (child->d_inode) { + if (d_inode(child)) { get_derived_permission(dentry, child); - fix_derived_permission(d_inode(child)); + fixup_tmp_permissions(d_inode(child)); } dput(child); } @@ -172,7 +182,7 @@ void fixup_top_recursive(struct dentry *parent) { if (d_inode(dentry)) { if (SDCARDFS_I(d_inode(parent))->top != SDCARDFS_I(d_inode(dentry))->top) { get_derived_permission(parent, dentry); - fix_derived_permission(d_inode(dentry)); + fixup_tmp_permissions(d_inode(dentry)); fixup_top_recursive(dentry); } } @@ -202,7 +212,7 @@ inline void update_derived_permission_lock(struct dentry *dentry) dput(parent); } } - fix_derived_permission(dentry->d_inode); + fixup_tmp_permissions(d_inode(dentry)); } int need_graft_path(struct dentry *dentry) diff --git a/fs/sdcardfs/inode.c b/fs/sdcardfs/inode.c index 503501388ef9..1b8f068987c7 100644 --- a/fs/sdcardfs/inode.c +++ b/fs/sdcardfs/inode.c @@ -531,7 +531,7 @@ static int sdcardfs_rename(struct inode *old_dir, struct dentry *old_dentry, /* At this point, not all dentry information has been moved, so * we pass along new_dentry for the name.*/ get_derived_permission_new(new_dentry->d_parent, old_dentry, new_dentry); - fix_derived_permission(d_inode(old_dentry)); + fixup_tmp_permissions(d_inode(old_dentry)); fixup_top_recursive(old_dentry); out: unlock_rename(lower_old_dir_dentry, lower_new_dir_dentry); @@ -601,28 +601,66 @@ static const char *sdcardfs_follow_link(struct dentry *dentry, void **cookie) } #endif -static int sdcardfs_permission(struct inode *inode, int mask) +static int sdcardfs_permission_wrn(struct inode *inode, int mask) +{ + WARN(1, "sdcardfs does not support permission. Use permission2.\n"); + return -EINVAL; +} + +void copy_attrs(struct inode *dest, const struct inode *src) +{ + dest->i_mode = src->i_mode; + dest->i_uid = src->i_uid; + dest->i_gid = src->i_gid; + dest->i_rdev = src->i_rdev; + dest->i_atime = src->i_atime; + dest->i_mtime = src->i_mtime; + dest->i_ctime = src->i_ctime; + dest->i_blkbits = src->i_blkbits; + dest->i_flags = src->i_flags; +#ifdef CONFIG_FS_POSIX_ACL + dest->i_acl = src->i_acl; +#endif +#ifdef CONFIG_SECURITY + dest->i_security = src->i_security; +#endif +} + +static int sdcardfs_permission(struct vfsmount *mnt, struct inode *inode, int mask) { int err; + struct inode tmp; struct inode *top = grab_top(SDCARDFS_I(inode)); if (IS_ERR(mnt)) return PTR_ERR(mnt); - if (!top) + + if (!top) { + release_top(SDCARDFS_I(inode)); + WARN(1, "Top value was null!\n"); return -EINVAL; - /* Ensure owner is up to date */ - if (!uid_eq(inode->i_uid, top->i_uid)) { - SDCARDFS_I(inode)->d_uid = SDCARDFS_I(top)->d_uid; - fix_derived_permission(inode); } - release_top(SDCARDFS_I(inode)); /* * Permission check on sdcardfs inode. * Calling process should have AID_SDCARD_RW permission + * Since generic_permission only needs i_mode, i_uid, + * i_gid, and i_sb, we can create a fake inode to pass + * this information down in. + * + * The underlying code may attempt to take locks in some + * cases for features we're not using, but if that changes, + * locks must be dealt with to avoid undefined behavior. */ - err = generic_permission(inode, mask); - + copy_attrs(&tmp, inode); + tmp.i_uid = make_kuid(&init_user_ns, SDCARDFS_I(top)->d_uid); + tmp.i_gid = make_kgid(&init_user_ns, get_gid(mnt, SDCARDFS_I(top))); + tmp.i_mode = (inode->i_mode & S_IFMT) | get_mode(mnt, SDCARDFS_I(top)); + release_top(SDCARDFS_I(inode)); + tmp.i_sb = inode->i_sb; + if (IS_POSIXACL(inode)) + printk(KERN_WARNING "%s: This may be undefined behavior... \n", __func__); + err = generic_permission(&tmp, mask); /* XXX * Original sdcardfs code calls inode_permission(lower_inode,.. ) * for checking inode permission. But doing such things here seems @@ -651,7 +689,13 @@ static int sdcardfs_permission(struct inode *inode, int mask) } -static int sdcardfs_setattr(struct dentry *dentry, struct iattr *ia) +static int sdcardfs_setattr_wrn(struct dentry *dentry, struct iattr *ia) +{ + WARN(1, "sdcardfs does not support setattr. User setattr2.\n"); + return -EINVAL; +} + +static int sdcardfs_setattr(struct vfsmount *mnt, struct dentry *dentry, struct iattr *ia) { int err; struct dentry *lower_dentry; @@ -661,17 +705,45 @@ static int sdcardfs_setattr(struct dentry *dentry, struct iattr *ia) struct path lower_path; struct iattr lower_ia; struct dentry *parent; + struct inode tmp; + struct inode *top; + const struct cred *saved_cred = NULL; inode = d_inode(dentry); + top = grab_top(SDCARDFS_I(inode)); + + if (!top) { + release_top(SDCARDFS_I(inode)); + return -EINVAL; + } + + /* + * Permission check on sdcardfs inode. + * Calling process should have AID_SDCARD_RW permission + * Since generic_permission only needs i_mode, i_uid, + * i_gid, and i_sb, we can create a fake inode to pass + * this information down in. + * + * The underlying code may attempt to take locks in some + * cases for features we're not using, but if that changes, + * locks must be dealt with to avoid undefined behavior. + * + */ + copy_attrs(&tmp, inode); + tmp.i_uid = make_kuid(&init_user_ns, SDCARDFS_I(top)->d_uid); + tmp.i_gid = make_kgid(&init_user_ns, get_gid(mnt, SDCARDFS_I(top))); + tmp.i_mode = (inode->i_mode & S_IFMT) | get_mode(mnt, SDCARDFS_I(top)); + tmp.i_size = i_size_read(inode); + release_top(SDCARDFS_I(inode)); + tmp.i_sb = inode->i_sb; /* * Check if user has permission to change inode. We don't check if * this user can change the lower inode: that should happen when * calling notify_change on the lower inode. */ - err = inode_change_ok(inode, ia); + err = inode_change_ok(&tmp, ia); - /* no vfs_XXX operations required, cred overriding will be skipped. wj*/ if (!err) { /* check the Android group ID */ parent = dget_parent(dentry); @@ -687,6 +759,9 @@ static int sdcardfs_setattr(struct dentry *dentry, struct iattr *ia) if (err) goto out_err; + /* save current_cred and override it */ + OVERRIDE_CRED(SDCARDFS_SB(dentry->d_sb), saved_cred); + sdcardfs_get_lower_path(dentry, &lower_path); lower_dentry = lower_path.dentry; lower_mnt = lower_path.mnt; @@ -710,7 +785,7 @@ static int sdcardfs_setattr(struct dentry *dentry, struct iattr *ia) if (current->mm) down_write(¤t->mm->mmap_sem); if (ia->ia_valid & ATTR_SIZE) { - err = inode_newsize_ok(inode, ia->ia_size); + err = inode_newsize_ok(&tmp, ia->ia_size); if (err) { if (current->mm) up_write(¤t->mm->mmap_sem); @@ -752,11 +827,12 @@ static int sdcardfs_setattr(struct dentry *dentry, struct iattr *ia) out: sdcardfs_put_lower_path(dentry, &lower_path); + REVERT_CRED(saved_cred); out_err: return err; } -static int sdcardfs_fillattr(struct inode *inode, struct kstat *stat) +static int sdcardfs_fillattr(struct vfsmount *mnt, struct inode *inode, struct kstat *stat) { struct sdcardfs_inode_info *info = SDCARDFS_I(inode); struct inode *top = grab_top(info); @@ -765,10 +841,10 @@ static int sdcardfs_fillattr(struct inode *inode, struct kstat *stat) stat->dev = inode->i_sb->s_dev; stat->ino = inode->i_ino; - stat->mode = (inode->i_mode & S_IFMT) | get_mode(SDCARDFS_I(top)); + stat->mode = (inode->i_mode & S_IFMT) | get_mode(mnt, SDCARDFS_I(top)); stat->nlink = inode->i_nlink; stat->uid = make_kuid(&init_user_ns, SDCARDFS_I(top)->d_uid); - stat->gid = make_kgid(&init_user_ns, get_gid(SDCARDFS_I(top))); + stat->gid = make_kgid(&init_user_ns, get_gid(mnt, SDCARDFS_I(top))); stat->rdev = inode->i_rdev; stat->size = i_size_read(inode); stat->atime = inode->i_atime; @@ -809,14 +885,14 @@ static int sdcardfs_getattr(struct vfsmount *mnt, struct dentry *dentry, sdcardfs_copy_and_fix_attrs(inode, lower_inode); fsstack_copy_inode_size(inode, lower_inode); - err = sdcardfs_fillattr(inode, stat); + err = sdcardfs_fillattr(mnt, inode, stat); sdcardfs_put_lower_path(dentry, &lower_path); return err; } const struct inode_operations sdcardfs_symlink_iops = { - .permission = sdcardfs_permission, - .setattr = sdcardfs_setattr, + .permission2 = sdcardfs_permission, + .setattr2 = sdcardfs_setattr, /* XXX Following operations are implemented, * but FUSE(sdcard) or FAT does not support them * These methods are *NOT* perfectly tested. @@ -829,12 +905,14 @@ const struct inode_operations sdcardfs_symlink_iops = { const struct inode_operations sdcardfs_dir_iops = { .create = sdcardfs_create, .lookup = sdcardfs_lookup, - .permission = sdcardfs_permission, + .permission = sdcardfs_permission_wrn, + .permission2 = sdcardfs_permission, .unlink = sdcardfs_unlink, .mkdir = sdcardfs_mkdir, .rmdir = sdcardfs_rmdir, .rename = sdcardfs_rename, - .setattr = sdcardfs_setattr, + .setattr = sdcardfs_setattr_wrn, + .setattr2 = sdcardfs_setattr, .getattr = sdcardfs_getattr, /* XXX Following operations are implemented, * but FUSE(sdcard) or FAT does not support them @@ -846,7 +924,9 @@ const struct inode_operations sdcardfs_dir_iops = { }; const struct inode_operations sdcardfs_main_iops = { - .permission = sdcardfs_permission, - .setattr = sdcardfs_setattr, + .permission = sdcardfs_permission_wrn, + .permission2 = sdcardfs_permission, + .setattr = sdcardfs_setattr_wrn, + .setattr2 = sdcardfs_setattr, .getattr = sdcardfs_getattr, }; diff --git a/fs/sdcardfs/lookup.c b/fs/sdcardfs/lookup.c index c74a7d1bc18e..00a711ec2733 100644 --- a/fs/sdcardfs/lookup.c +++ b/fs/sdcardfs/lookup.c @@ -244,6 +244,7 @@ static struct dentry *__sdcardfs_lookup(struct dentry *dentry, if (err == -ENOENT) { struct dentry *child; struct dentry *match = NULL; + mutex_lock(&d_inode(lower_dir_dentry)->i_mutex); spin_lock(&lower_dir_dentry->d_lock); list_for_each_entry(child, &lower_dir_dentry->d_subdirs, d_child) { if (child && d_inode(child)) { @@ -254,6 +255,7 @@ static struct dentry *__sdcardfs_lookup(struct dentry *dentry, } } spin_unlock(&lower_dir_dentry->d_lock); + mutex_unlock(&d_inode(lower_dir_dentry)->i_mutex); if (match) { err = vfs_path_lookup(lower_dir_dentry, lower_dir_mnt, @@ -389,7 +391,7 @@ struct dentry *sdcardfs_lookup(struct inode *dir, struct dentry *dentry, sdcardfs_lower_inode(dentry->d_inode)); /* get derived permission */ get_derived_permission(parent, dentry); - fix_derived_permission(dentry->d_inode); + fixup_tmp_permissions(d_inode(dentry)); } /* update parent directory's atime */ fsstack_copy_attr_atime(parent->d_inode, diff --git a/fs/sdcardfs/main.c b/fs/sdcardfs/main.c index 5400e7e63d27..eec10ccacd99 100644 --- a/fs/sdcardfs/main.c +++ b/fs/sdcardfs/main.c @@ -28,7 +28,6 @@ enum { Opt_fsgid, Opt_gid, Opt_debug, - Opt_lower_fs, Opt_mask, Opt_multiuser, // May need? Opt_userid, @@ -60,11 +59,9 @@ static int parse_options(struct super_block *sb, char *options, int silent, opts->fs_low_uid = AID_MEDIA_RW; opts->fs_low_gid = AID_MEDIA_RW; vfsopts->mask = 0; - opts->mask = 0; opts->multiuser = false; opts->fs_user_id = 0; vfsopts->gid = 0; - opts->gid = 0; /* by default, 0MB is reserved */ opts->reserved_mb = 0; @@ -97,7 +94,6 @@ static int parse_options(struct super_block *sb, char *options, int silent, case Opt_gid: if (match_int(&args[0], &option)) return 0; - opts->gid = option; vfsopts->gid = option; break; case Opt_userid: @@ -108,7 +104,6 @@ static int parse_options(struct super_block *sb, char *options, int silent, case Opt_mask: if (match_int(&args[0], &option)) return 0; - opts->mask = option; vfsopts->mask = option; break; case Opt_multiuser: @@ -258,6 +253,7 @@ static int sdcardfs_read_super(struct vfsmount *mnt, struct super_block *sb, printk(KERN_INFO "sdcardfs: dev_name -> %s\n", dev_name); printk(KERN_INFO "sdcardfs: options -> %s\n", (char *)raw_data); + printk(KERN_INFO "sdcardfs: mnt -> %p\n", mnt); /* parse lower path */ err = kern_path(dev_name, LOOKUP_FOLLOW | LOOKUP_DIRECTORY, @@ -342,7 +338,7 @@ static int sdcardfs_read_super(struct vfsmount *mnt, struct super_block *sb, setup_derived_state(d_inode(sb->s_root), PERM_ROOT, sb_info->options.fs_user_id, AID_ROOT, false, d_inode(sb->s_root)); snprintf(sb_info->obbpath_s, PATH_MAX, "%s/Android/obb", dev_name); } - fix_derived_permission(sb->s_root->d_inode); + fixup_tmp_permissions(d_inode(sb->s_root)); sb_info->sb = sb; list_add(&sb_info->list, &sdcardfs_super_list); mutex_unlock(&sdcardfs_super_list_lock); diff --git a/fs/sdcardfs/sdcardfs.h b/fs/sdcardfs/sdcardfs.h index 22ef29857022..b03130329014 100644 --- a/fs/sdcardfs/sdcardfs.h +++ b/fs/sdcardfs/sdcardfs.h @@ -68,14 +68,20 @@ #define AID_PACKAGE_INFO 1027 -#define fix_derived_permission(x) \ + +/* + * Permissions are handled by our permission function. + * We don't want anyone who happens to look at our inode value to prematurely + * block access, so store more permissive values. These are probably never + * used. + */ +#define fixup_tmp_permissions(x) \ do { \ (x)->i_uid = make_kuid(&init_user_ns, SDCARDFS_I(x)->d_uid); \ - (x)->i_gid = make_kgid(&init_user_ns, get_gid(SDCARDFS_I(x))); \ - (x)->i_mode = ((x)->i_mode & S_IFMT) | get_mode(SDCARDFS_I(x));\ + (x)->i_gid = make_kgid(&init_user_ns, AID_SDCARD_RW); \ + (x)->i_mode = ((x)->i_mode & S_IFMT) | 0775;\ } while (0) - /* OVERRIDE_CRED() and REVERT_CRED() * OVERRID_CRED() * backup original task->cred @@ -187,8 +193,6 @@ struct sdcardfs_mount_options { uid_t fs_low_uid; gid_t fs_low_gid; userid_t fs_user_id; - gid_t gid; - mode_t mask; bool multiuser; unsigned int reserved_mb; }; @@ -360,9 +364,10 @@ static inline void release_top(struct sdcardfs_inode_info *info) iput(info->top); } -static inline int get_gid(struct sdcardfs_inode_info *info) { - struct sdcardfs_sb_info *sb_info = SDCARDFS_SB(info->vfs_inode.i_sb); - if (sb_info->options.gid == AID_SDCARD_RW) { +static inline int get_gid(struct vfsmount *mnt, struct sdcardfs_inode_info *info) { + struct sdcardfs_vfsmount_options *opts = mnt->data; + + if (opts->gid == AID_SDCARD_RW) { /* As an optimization, certain trusted system components only run * as owner but operate across all users. Since we're now handing * out the sdcard_rw GID only to trusted apps, we're okay relaxing @@ -370,14 +375,15 @@ static inline int get_gid(struct sdcardfs_inode_info *info) { * assigned to app directories are still multiuser aware. */ return AID_SDCARD_RW; } else { - return multiuser_get_uid(info->userid, sb_info->options.gid); + return multiuser_get_uid(info->userid, opts->gid); } } -static inline int get_mode(struct sdcardfs_inode_info *info) { +static inline int get_mode(struct vfsmount *mnt, struct sdcardfs_inode_info *info) { int owner_mode; int filtered_mode; - struct sdcardfs_sb_info * sb_info = SDCARDFS_SB(info->vfs_inode.i_sb); - int visible_mode = 0775 & ~sb_info->options.mask; + struct sdcardfs_vfsmount_options *opts = mnt->data; + int visible_mode = 0775 & ~opts->mask; + if (info->perm == PERM_PRE_ROOT) { /* Top of multi-user view should always be visible to ensure @@ -387,7 +393,7 @@ static inline int get_mode(struct sdcardfs_inode_info *info) { /* Block "other" access to Android directories, since only apps * belonging to a specific user should be in there; we still * leave +x open for the default view. */ - if (sb_info->options.gid == AID_SDCARD_RW) { + if (opts->gid == AID_SDCARD_RW) { visible_mode = visible_mode & ~0006; } else { visible_mode = visible_mode & ~0007; @@ -553,12 +559,16 @@ static inline int check_min_free_space(struct dentry *dentry, size_t size, int d return 1; } -/* Copies attrs and maintains sdcardfs managed attrs */ +/* + * Copies attrs and maintains sdcardfs managed attrs + * Since our permission check handles all special permissions, set those to be open + */ static inline void sdcardfs_copy_and_fix_attrs(struct inode *dest, const struct inode *src) { - dest->i_mode = (src->i_mode & S_IFMT) | get_mode(SDCARDFS_I(dest)); + dest->i_mode = (src->i_mode & S_IFMT) | S_IRWXU | S_IRWXG | + S_IROTH | S_IXOTH; /* 0775 */ dest->i_uid = make_kuid(&init_user_ns, SDCARDFS_I(dest)->d_uid); - dest->i_gid = make_kgid(&init_user_ns, get_gid(SDCARDFS_I(dest))); + dest->i_gid = make_kgid(&init_user_ns, AID_SDCARD_RW); dest->i_rdev = src->i_rdev; dest->i_atime = src->i_atime; dest->i_mtime = src->i_mtime; From ec2dba5eebc653bbb58f0d7a706a9c0e4a390203 Mon Sep 17 00:00:00 2001 From: Daniel Rosenberg Date: Tue, 15 Nov 2016 13:35:18 -0800 Subject: [PATCH 0310/1103] ANDROID: sdcardfs: Change magic value Sdcardfs uses the same magic value as wrapfs. This should not be the case. As it is entirely in memory, the value can be changed without any loss of compatibility. Change-Id: I24200b805d5e6d32702638be99e47d50d7f2f746 Signed-off-by: Daniel Rosenberg --- include/uapi/linux/magic.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/uapi/linux/magic.h b/include/uapi/linux/magic.h index 6f7b217c44ac..d72347947304 100644 --- a/include/uapi/linux/magic.h +++ b/include/uapi/linux/magic.h @@ -56,7 +56,7 @@ #define REISER2FS_SUPER_MAGIC_STRING "ReIsEr2Fs" #define REISER2FS_JR_SUPER_MAGIC_STRING "ReIsEr3Fs" -#define SDCARDFS_SUPER_MAGIC 0xb550ca10 +#define SDCARDFS_SUPER_MAGIC 0x5dca2df5 #define SMB_SUPER_MAGIC 0x517B #define CGROUP_SUPER_MAGIC 0x27e0eb From e36949172bafa7da117aba029006f0b1ae38ec7a Mon Sep 17 00:00:00 2001 From: Daniel Rosenberg Date: Thu, 1 Dec 2016 14:36:29 -0800 Subject: [PATCH 0311/1103] ANDROID: sdcardfs: Switch ->d_inode to d_inode() Change-Id: I12375cc2d6e82fb8adf0319be971f335f8d7a312 Signed-off-by: Daniel Rosenberg --- fs/sdcardfs/derived_perm.c | 16 ++++++++-------- fs/sdcardfs/file.c | 2 +- fs/sdcardfs/lookup.c | 14 +++++++------- fs/sdcardfs/main.c | 2 +- 4 files changed, 17 insertions(+), 17 deletions(-) diff --git a/fs/sdcardfs/derived_perm.c b/fs/sdcardfs/derived_perm.c index 066edbbb6ad6..60ae94bb99f7 100644 --- a/fs/sdcardfs/derived_perm.c +++ b/fs/sdcardfs/derived_perm.c @@ -49,8 +49,8 @@ void setup_derived_state(struct inode *inode, perm_t perm, userid_t userid, /* While renaming, there is a point where we want the path from dentry, but the name from newdentry */ void get_derived_permission_new(struct dentry *parent, struct dentry *dentry, struct dentry *newdentry) { - struct sdcardfs_inode_info *info = SDCARDFS_I(dentry->d_inode); - struct sdcardfs_inode_info *parent_info= SDCARDFS_I(parent->d_inode); + struct sdcardfs_inode_info *info = SDCARDFS_I(d_inode(dentry)); + struct sdcardfs_inode_info *parent_info= SDCARDFS_I(d_inode(parent)); appid_t appid; /* By default, each inode inherits from its parent. @@ -61,7 +61,7 @@ void get_derived_permission_new(struct dentry *parent, struct dentry *dentry, st * stage of each system call by fix_derived_permission(inode). */ - inherit_derived_state(parent->d_inode, dentry->d_inode); + inherit_derived_state(d_inode(parent), d_inode(dentry)); /* Derive custom permissions based on parent and current node */ switch (parent_info->perm) { @@ -134,7 +134,7 @@ void fixup_perms_recursive(struct dentry *dentry, const char* name, size_t len) struct sdcardfs_inode_info *info; if (!dget(dentry)) return; - if (!dentry->d_inode) { + if (!d_inode(dentry)) { dput(dentry); return; } @@ -195,7 +195,7 @@ inline void update_derived_permission_lock(struct dentry *dentry) { struct dentry *parent; - if(!dentry || !dentry->d_inode) { + if(!dentry || !d_inode(dentry)) { printk(KERN_ERR "sdcardfs: %s: invalid dentry\n", __func__); return; } @@ -204,7 +204,7 @@ inline void update_derived_permission_lock(struct dentry *dentry) * 2. remove the root dentry update */ if(IS_ROOT(dentry)) { - //setup_default_pre_root_state(dentry->d_inode); + //setup_default_pre_root_state(d_inode(dentry)); } else { parent = dget_parent(dentry); if(parent) { @@ -219,7 +219,7 @@ int need_graft_path(struct dentry *dentry) { int ret = 0; struct dentry *parent = dget_parent(dentry); - struct sdcardfs_inode_info *parent_info= SDCARDFS_I(parent->d_inode); + struct sdcardfs_inode_info *parent_info= SDCARDFS_I(d_inode(parent)); struct sdcardfs_sb_info *sbi = SDCARDFS_SB(dentry->d_sb); if(parent_info->perm == PERM_ANDROID && @@ -278,7 +278,7 @@ int is_base_obbpath(struct dentry *dentry) { int ret = 0; struct dentry *parent = dget_parent(dentry); - struct sdcardfs_inode_info *parent_info= SDCARDFS_I(parent->d_inode); + struct sdcardfs_inode_info *parent_info= SDCARDFS_I(d_inode(parent)); struct sdcardfs_sb_info *sbi = SDCARDFS_SB(dentry->d_sb); spin_lock(&SDCARDFS_D(dentry)->lock); diff --git a/fs/sdcardfs/file.c b/fs/sdcardfs/file.c index c249fa982d3c..7750a0472389 100644 --- a/fs/sdcardfs/file.c +++ b/fs/sdcardfs/file.c @@ -216,7 +216,7 @@ static int sdcardfs_open(struct inode *inode, struct file *file) goto out_err; } - if(!check_caller_access_to_name(parent->d_inode, dentry->d_name.name)) { + if(!check_caller_access_to_name(d_inode(parent), dentry->d_name.name)) { printk(KERN_INFO "%s: need to check the caller's gid in packages.list\n" " dentry: %s, task:%s\n", __func__, dentry->d_name.name, current->comm); diff --git a/fs/sdcardfs/lookup.c b/fs/sdcardfs/lookup.c index 00a711ec2733..e94a65c8bbbd 100644 --- a/fs/sdcardfs/lookup.c +++ b/fs/sdcardfs/lookup.c @@ -179,7 +179,7 @@ int sdcardfs_interpose(struct dentry *dentry, struct super_block *sb, struct inode *lower_inode; struct super_block *lower_sb; - lower_inode = lower_path->dentry->d_inode; + lower_inode = d_inode(lower_path->dentry); lower_sb = sdcardfs_lower_super(sb); /* check that the lower file system didn't cross a mount point */ @@ -359,7 +359,7 @@ struct dentry *sdcardfs_lookup(struct inode *dir, struct dentry *dentry, parent = dget_parent(dentry); - if(!check_caller_access_to_name(parent->d_inode, dentry->d_name.name)) { + if(!check_caller_access_to_name(d_inode(parent), dentry->d_name.name)) { ret = ERR_PTR(-EACCES); printk(KERN_INFO "%s: need to check the caller's gid in packages.list\n" " dentry: %s, task:%s\n", @@ -386,16 +386,16 @@ struct dentry *sdcardfs_lookup(struct inode *dir, struct dentry *dentry, } if (ret) dentry = ret; - if (dentry->d_inode) { - fsstack_copy_attr_times(dentry->d_inode, - sdcardfs_lower_inode(dentry->d_inode)); + if (d_inode(dentry)) { + fsstack_copy_attr_times(d_inode(dentry), + sdcardfs_lower_inode(d_inode(dentry))); /* get derived permission */ get_derived_permission(parent, dentry); fixup_tmp_permissions(d_inode(dentry)); } /* update parent directory's atime */ - fsstack_copy_attr_atime(parent->d_inode, - sdcardfs_lower_inode(parent->d_inode)); + fsstack_copy_attr_atime(d_inode(parent), + sdcardfs_lower_inode(d_inode(parent))); out: sdcardfs_put_lower_path(parent, &lower_parent_path); diff --git a/fs/sdcardfs/main.c b/fs/sdcardfs/main.c index eec10ccacd99..7a8eae29e44d 100644 --- a/fs/sdcardfs/main.c +++ b/fs/sdcardfs/main.c @@ -297,7 +297,7 @@ static int sdcardfs_read_super(struct vfsmount *mnt, struct super_block *sb, sb->s_op = &sdcardfs_sops; /* get a new inode and allocate our root dentry */ - inode = sdcardfs_iget(sb, lower_path.dentry->d_inode, 0); + inode = sdcardfs_iget(sb, d_inode(lower_path.dentry), 0); if (IS_ERR(inode)) { err = PTR_ERR(inode); goto out_sput; From eca974b15513f5f799ca522143836c9f4748aca0 Mon Sep 17 00:00:00 2001 From: Daniel Rosenberg Date: Tue, 27 Dec 2016 12:36:29 -0800 Subject: [PATCH 0312/1103] ANDROID: sdcardfs: Fix locking issue with permision fix up Don't use lookup_one_len so we can grab the spinlock that protects d_subdirs. Bug: 30954918 Change-Id: I0c6a393252db7beb467e0d563739a3a14e1b5115 Signed-off-by: Daniel Rosenberg --- fs/sdcardfs/derived_perm.c | 36 +++++++++++++++--------------------- 1 file changed, 15 insertions(+), 21 deletions(-) diff --git a/fs/sdcardfs/derived_perm.c b/fs/sdcardfs/derived_perm.c index 60ae94bb99f7..9408a5477ada 100644 --- a/fs/sdcardfs/derived_perm.c +++ b/fs/sdcardfs/derived_perm.c @@ -141,32 +141,26 @@ void fixup_perms_recursive(struct dentry *dentry, const char* name, size_t len) info = SDCARDFS_I(d_inode(dentry)); if (needs_fixup(info->perm)) { - /* We need permission to fix up these values. - * Since permissions are based of of the mount, and - * we are accessing without the mount point, we create - * a fake mount with the permissions we will be using. - */ - struct vfsmount fakemnt; - struct sdcardfs_vfsmount_options opts; - fakemnt.data = &opts; - opts.gid = AID_SDCARD_RW; - opts.mask = 0; - mutex_lock(&d_inode(dentry)->i_mutex); - child = lookup_one_len2(name, &fakemnt, dentry, len); - mutex_unlock(&d_inode(dentry)->i_mutex); - if (!IS_ERR(child)) { - if (d_inode(child)) { - get_derived_permission(dentry, child); - fixup_tmp_permissions(d_inode(child)); - } - dput(child); + spin_lock(&dentry->d_lock); + list_for_each_entry(child, &dentry->d_subdirs, d_child) { + dget(child); + if (!strncasecmp(child->d_name.name, name, len)) { + if (child->d_inode) { + get_derived_permission(dentry, child); + fixup_tmp_permissions(child->d_inode); + dput(child); + break; + } + } + dput(child); } + spin_unlock(&dentry->d_lock); } else if (descendant_may_need_fixup(info->perm)) { - mutex_lock(&d_inode(dentry)->i_mutex); + spin_lock(&dentry->d_lock); list_for_each_entry(child, &dentry->d_subdirs, d_child) { fixup_perms_recursive(child, name, len); } - mutex_unlock(&d_inode(dentry)->i_mutex); + spin_unlock(&dentry->d_lock); } dput(dentry); } From 1eec517eff7e28931d0327c309a941f967b72f14 Mon Sep 17 00:00:00 2001 From: Amit Pundir Date: Wed, 1 Jun 2016 21:53:20 +0530 Subject: [PATCH 0313/1103] ANDROID: sdcardfs: use wrappers to access i_mutex MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use inode_{lock,unlock,lock_nested} wrappers as suggested by upstream commit 5955102c9984 (wrappers for ->i_mutex access) for access to ->i_mutex, otherwise we run into following build error: CC [M] fs/sdcardfs/dentry.o In file included from fs/sdcardfs/dentry.c:21:0: fs/sdcardfs/sdcardfs.h: In function ‘lock_parent’: fs/sdcardfs/sdcardfs.h:422:33: error: ‘struct inode’ has no member named ‘i_mutex’ mutex_lock_nested(&d_inode(dir)->i_mutex, I_MUTEX_PARENT); ^ fs/sdcardfs/sdcardfs.h: In function ‘unlock_dir’: fs/sdcardfs/sdcardfs.h:428:28: error: ‘struct inode’ has no member named ‘i_mutex’ mutex_unlock(&d_inode(dir)->i_mutex); ^ In file included from ./include/linux/fs.h:19:0, from fs/sdcardfs/sdcardfs.h:31, from fs/sdcardfs/dentry.c:21: fs/sdcardfs/sdcardfs.h: In function ‘prepare_dir’: fs/sdcardfs/sdcardfs.h:457:27: error: ‘struct inode’ has no member named ‘i_mutex’ mutex_lock(&d_inode(dent)->i_mutex); ^ ./include/linux/mutex.h:146:44: note: in definition of macro ‘mutex_lock’ #define mutex_lock(lock) mutex_lock_nested(lock, 0) ^ In file included from fs/sdcardfs/dentry.c:21:0: fs/sdcardfs/sdcardfs.h:459:29: error: ‘struct inode’ has no member named‘i_mutex’ mutex_unlock(&d_inode(dent)->i_mutex); ^ fs/sdcardfs/sdcardfs.h:466:38: error: ‘struct inode’ has no member named ‘i_mutex’ mutex_unlock(&d_inode(parent.dentry)->i_mutex); ^ Change-Id: I4c8298045ac511aba5542d9ca967331f550376a5 Signed-off-by: Amit Pundir --- fs/sdcardfs/inode.c | 4 ++-- fs/sdcardfs/lookup.c | 6 +++--- fs/sdcardfs/sdcardfs.h | 10 +++++----- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/fs/sdcardfs/inode.c b/fs/sdcardfs/inode.c index 1b8f068987c7..bd8a70185147 100644 --- a/fs/sdcardfs/inode.c +++ b/fs/sdcardfs/inode.c @@ -807,10 +807,10 @@ static int sdcardfs_setattr(struct vfsmount *mnt, struct dentry *dentry, struct * unlinked (no inode->i_sb and i_ino==0. This happens if someone * tries to open(), unlink(), then ftruncate() a file. */ - mutex_lock(&d_inode(lower_dentry)->i_mutex); + inode_lock(d_inode(lower_dentry)); err = notify_change2(lower_mnt, lower_dentry, &lower_ia, /* note: lower_ia */ NULL); - mutex_unlock(&d_inode(lower_dentry)->i_mutex); + inode_unlock(d_inode(lower_dentry)); if (current->mm) up_write(¤t->mm->mmap_sem); if (err) diff --git a/fs/sdcardfs/lookup.c b/fs/sdcardfs/lookup.c index e94a65c8bbbd..d9d46308ef94 100644 --- a/fs/sdcardfs/lookup.c +++ b/fs/sdcardfs/lookup.c @@ -244,7 +244,7 @@ static struct dentry *__sdcardfs_lookup(struct dentry *dentry, if (err == -ENOENT) { struct dentry *child; struct dentry *match = NULL; - mutex_lock(&d_inode(lower_dir_dentry)->i_mutex); + inode_lock(d_inode(lower_dir_dentry)); spin_lock(&lower_dir_dentry->d_lock); list_for_each_entry(child, &lower_dir_dentry->d_subdirs, d_child) { if (child && d_inode(child)) { @@ -255,7 +255,7 @@ static struct dentry *__sdcardfs_lookup(struct dentry *dentry, } } spin_unlock(&lower_dir_dentry->d_lock); - mutex_unlock(&d_inode(lower_dir_dentry)->i_mutex); + inode_unlock(d_inode(lower_dir_dentry)); if (match) { err = vfs_path_lookup(lower_dir_dentry, lower_dir_mnt, @@ -344,7 +344,7 @@ static struct dentry *__sdcardfs_lookup(struct dentry *dentry, * On fail (== error) * returns error ptr * - * @dir : Parent inode. It is locked (dir->i_mutex) + * @dir : Parent inode. * @dentry : Target dentry to lookup. we should set each of fields. * (dentry->d_name is initialized already) * @nd : nameidata of parent inode diff --git a/fs/sdcardfs/sdcardfs.h b/fs/sdcardfs/sdcardfs.h index b03130329014..66a97ef8d261 100644 --- a/fs/sdcardfs/sdcardfs.h +++ b/fs/sdcardfs/sdcardfs.h @@ -465,13 +465,13 @@ extern int setup_obb_dentry(struct dentry *dentry, struct path *lower_path); static inline struct dentry *lock_parent(struct dentry *dentry) { struct dentry *dir = dget_parent(dentry); - mutex_lock_nested(&d_inode(dir)->i_mutex, I_MUTEX_PARENT); + inode_lock_nested(d_inode(dir), I_MUTEX_PARENT); return dir; } static inline void unlock_dir(struct dentry *dir) { - mutex_unlock(&d_inode(dir)->i_mutex); + inode_unlock(d_inode(dir)); dput(dir); } @@ -500,16 +500,16 @@ static inline int prepare_dir(const char *path_s, uid_t uid, gid_t gid, mode_t m attrs.ia_uid = make_kuid(&init_user_ns, uid); attrs.ia_gid = make_kgid(&init_user_ns, gid); attrs.ia_valid = ATTR_UID | ATTR_GID; - mutex_lock(&d_inode(dent)->i_mutex); + inode_lock(d_inode(dent)); notify_change2(parent.mnt, dent, &attrs, NULL); - mutex_unlock(&d_inode(dent)->i_mutex); + inode_unlock(d_inode(dent)); out_dput: dput(dent); out_unlock: /* parent dentry locked by lookup_create */ - mutex_unlock(&d_inode(parent.dentry)->i_mutex); + inode_unlock(d_inode(parent.dentry)); path_put(&parent); return err; } From 5c18dcd9643d59198bf5f0573c799e1b243aab94 Mon Sep 17 00:00:00 2001 From: Amit Pundir Date: Thu, 4 Aug 2016 21:04:31 +0530 Subject: [PATCH 0314/1103] ANDROID: sdcardfs: add parent pointer into dentry name hash MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fix following sdcardfs compilation error introduced in code refactoring by upstream commit 8387ff2577eb ("vfs: make the string hashes salt the hash"). CC [M] fs/sdcardfs/dentry.o In file included from ./include/linux/dcache.h:13:0, from fs/sdcardfs/sdcardfs.h:29, from fs/sdcardfs/dentry.c:21: fs/sdcardfs/dentry.c: In function ‘sdcardfs_hash_ci’: ./include/linux/stringhash.h:38:51: error: expected expression before ‘)’ token #define init_name_hash(salt) (unsigned long)(salt) ^ fs/sdcardfs/dentry.c:138:9: note: in expansion of macro ‘init_name_hash’ hash = init_name_hash(); ^ Change-Id: I9feb6c075a7e953726954f5746fc009202d3121c Signed-off-by: Amit Pundir --- fs/sdcardfs/dentry.c | 2 +- fs/sdcardfs/lookup.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/fs/sdcardfs/dentry.c b/fs/sdcardfs/dentry.c index 971928ab6c21..e4156a6fa8c8 100644 --- a/fs/sdcardfs/dentry.c +++ b/fs/sdcardfs/dentry.c @@ -135,7 +135,7 @@ static int sdcardfs_hash_ci(const struct dentry *dentry, //len = vfat_striptail_len(qstr); len = qstr->len; - hash = init_name_hash(); + hash = init_name_hash(dentry); while (len--) //hash = partial_name_hash(nls_tolower(t, *name++), hash); hash = partial_name_hash(tolower(*name++), hash); diff --git a/fs/sdcardfs/lookup.c b/fs/sdcardfs/lookup.c index d9d46308ef94..d271617290ee 100644 --- a/fs/sdcardfs/lookup.c +++ b/fs/sdcardfs/lookup.c @@ -309,7 +309,7 @@ static struct dentry *__sdcardfs_lookup(struct dentry *dentry, /* instatiate a new negative dentry */ this.name = name; this.len = strlen(name); - this.hash = full_name_hash(this.name, this.len); + this.hash = full_name_hash(dentry, this.name, this.len); lower_dentry = d_lookup(lower_dir_dentry, &this); if (lower_dentry) goto setup_lower; From f7a747c5521a21d8e9b347567046af1054358196 Mon Sep 17 00:00:00 2001 From: Amit Pundir Date: Mon, 8 Aug 2016 12:27:33 +0530 Subject: [PATCH 0315/1103] ANDROID: sdcardfs: get rid of 'parent' argument of ->d_compare() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ->d_compare() doesn't get parent as a separate argument anymore according to upstream commit 6fa67e707559 ("get rid of 'parent' argument of ->d_compare()"). We run into following build error otherwise: CC [M] fs/sdcardfs/dentry.o fs/sdcardfs/dentry.c:183:15: error: initialization from incompatible pointer type [-Werror=incompatible-pointer-types] .d_compare = sdcardfs_cmp_ci, ^ fs/sdcardfs/dentry.c:183:15: note: (near initialization for ‘sdcardfs_ci_dops.d_compare’) Change-Id: I51801b57aeb8287f1e69ce6cb944e8722ff37bea Signed-off-by: Amit Pundir --- fs/sdcardfs/dentry.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/fs/sdcardfs/dentry.c b/fs/sdcardfs/dentry.c index e4156a6fa8c8..f22de8add10c 100644 --- a/fs/sdcardfs/dentry.c +++ b/fs/sdcardfs/dentry.c @@ -147,8 +147,7 @@ static int sdcardfs_hash_ci(const struct dentry *dentry, /* * Case insensitive compare of two vfat names. */ -static int sdcardfs_cmp_ci(const struct dentry *parent, - const struct dentry *dentry, +static int sdcardfs_cmp_ci(const struct dentry *dentry, unsigned int len, const char *str, const struct qstr *name) { /* This function is copy of vfat_cmpi */ From 33cbe5f115aef58dc02366b409dee07cc4753e47 Mon Sep 17 00:00:00 2001 From: Amit Pundir Date: Tue, 11 Oct 2016 13:26:17 +0530 Subject: [PATCH 0316/1103] ANDROID: sdcardfs: Propagate dentry down to inode_change_ok() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since commit 31051c85b5e2 ("fs: Give dentry to inode_change_ok() instead of inode"), to avoid clearing of capabilities or security related extended attributes too early, inode_change_ok() will need to take dentry instead of inode. Propagate it down to sdcardfs_setattr() and also rename it to setattr_prepare(), otherwise we run into following build error: CC [M] fs/sdcardfs/inode.o fs/sdcardfs/inode.c: In function ‘sdcardfs_setattr’: fs/sdcardfs/inode.c:644:8: error: implicit declaration of function ‘inode_change_ok’ [-Werror=implicit-function-declaration] err = inode_change_ok(inode, ia); ^ Change-Id: I714b4f4f68b7fea1ac82a71d2f323c76b11fa008 Signed-off-by: Amit Pundir --- fs/sdcardfs/inode.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/fs/sdcardfs/inode.c b/fs/sdcardfs/inode.c index bd8a70185147..76b57c8c32fd 100644 --- a/fs/sdcardfs/inode.c +++ b/fs/sdcardfs/inode.c @@ -706,6 +706,7 @@ static int sdcardfs_setattr(struct vfsmount *mnt, struct dentry *dentry, struct struct iattr lower_ia; struct dentry *parent; struct inode tmp; + struct dentry tmp_d; struct inode *top; const struct cred *saved_cred = NULL; @@ -736,13 +737,14 @@ static int sdcardfs_setattr(struct vfsmount *mnt, struct dentry *dentry, struct tmp.i_size = i_size_read(inode); release_top(SDCARDFS_I(inode)); tmp.i_sb = inode->i_sb; + tmp_d.d_inode = &tmp; /* - * Check if user has permission to change inode. We don't check if + * Check if user has permission to change dentry. We don't check if * this user can change the lower inode: that should happen when * calling notify_change on the lower inode. */ - err = inode_change_ok(&tmp, ia); + err = setattr_prepare(&tmp_d, ia); if (!err) { /* check the Android group ID */ From 3d7b2e7ec5f3dad66a8a16fb57f05402043fad32 Mon Sep 17 00:00:00 2001 From: Amit Pundir Date: Sun, 16 Oct 2016 15:24:15 +0530 Subject: [PATCH 0317/1103] ANDROID: sdcardfs: make it use new .rename i_op MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since commit 2773bf00aeb9 ("fs: rename "rename2" i_op to "rename""), syscall rename2 is merged with rename syscall and it broke sdcard_fs build and we get following build error: CC [M] fs/sdcardfs/inode.o fs/sdcardfs/inode.c:786:13: error: initialization from incompatible pointer type [-Werror=incompatible-pointer-types] .rename = sdcardfs_rename, ^ fs/sdcardfs/inode.c:786:13: note: (near initialization for ‘sdcardfs_dir_iops.rename’) renameat2 syscall is the same as renameat with an added flags argument and calling renameat2 with flags=0 is equivalent to calling renameat. Change-Id: I48f3c76c3af481241188253a76f310670de6bd18 Signed-off-by: Amit Pundir --- fs/sdcardfs/inode.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/fs/sdcardfs/inode.c b/fs/sdcardfs/inode.c index 76b57c8c32fd..b5a43c11a00c 100644 --- a/fs/sdcardfs/inode.c +++ b/fs/sdcardfs/inode.c @@ -457,7 +457,8 @@ static int sdcardfs_mknod(struct inode *dir, struct dentry *dentry, umode_t mode * superblock-level name-space lock for renames and copy-ups. */ static int sdcardfs_rename(struct inode *old_dir, struct dentry *old_dentry, - struct inode *new_dir, struct dentry *new_dentry) + struct inode *new_dir, struct dentry *new_dentry, + unsigned int flags) { int err = 0; struct dentry *lower_old_dentry = NULL; @@ -470,6 +471,9 @@ static int sdcardfs_rename(struct inode *old_dir, struct dentry *old_dentry, struct path lower_old_path, lower_new_path; const struct cred *saved_cred = NULL; + if (flags) + return -EINVAL; + if(!check_caller_access_to_name(old_dir, old_dentry->d_name.name) || !check_caller_access_to_name(new_dir, new_dentry->d_name.name)) { printk(KERN_INFO "%s: need to check the caller's gid in packages.list\n" From 5fc47809c8ac8fc5b98b4e1f8b3c76151ae78126 Mon Sep 17 00:00:00 2001 From: Amit Pundir Date: Tue, 7 Jun 2016 16:30:56 +0530 Subject: [PATCH 0318/1103] ANDROID: sdcardfs: eliminate the offset argument to ->direct_IO MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Eliminate the offset argument to sdcardfs_direct_IO() which is dropped by upstream commit c8b8e32d700f ("direct-io: eliminate the offset argument to ->direct_IO"), otherwise we run into following build error: CC [M] fs/sdcardfs/mmap.o fs/sdcardfs/mmap.c:76:15: error: initialization from incompatible pointer type [-Werror=incompatible-pointer-types] .direct_IO = sdcardfs_direct_IO, ^ fs/sdcardfs/mmap.c:76:15: note: (near initialization for ‘sdcardfs_aops.direct_IO’) Change-Id: I292d93bb16365a9fa46494accb2b5da51028b5c1 Signed-off-by: Amit Pundir --- fs/sdcardfs/mmap.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/fs/sdcardfs/mmap.c b/fs/sdcardfs/mmap.c index e21f64675a80..ac5f3deae088 100644 --- a/fs/sdcardfs/mmap.c +++ b/fs/sdcardfs/mmap.c @@ -48,8 +48,7 @@ static int sdcardfs_fault(struct vm_area_struct *vma, struct vm_fault *vmf) return err; } -static ssize_t sdcardfs_direct_IO(struct kiocb *iocb, - struct iov_iter *iter, loff_t pos) +static ssize_t sdcardfs_direct_IO(struct kiocb *iocb, struct iov_iter *iter) { /* * This function returns zero on purpose in order to support direct IO. From 4962455ded34a8964d69e1f908dccc3feb29f3f3 Mon Sep 17 00:00:00 2001 From: Daniel Rosenberg Date: Fri, 20 Jan 2017 15:19:13 -0800 Subject: [PATCH 0319/1103] ANDROID: sdcardfs: Allow non-owners to touch This modifies the permission checks in setattr to allow for non-owners to modify the timestamp of files to things other than the current time. This still requires write access, as enforced by the permission call, but relaxes the requirement that the caller must be the owner, allowing those with group permissions to change it as well. Signed-off-by: Daniel Rosenberg Bug: 11118565 Change-Id: Ied31f0cce2797675c7ef179eeb4e088185adcbad --- fs/sdcardfs/inode.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/fs/sdcardfs/inode.c b/fs/sdcardfs/inode.c index b5a43c11a00c..4bafdf783354 100644 --- a/fs/sdcardfs/inode.c +++ b/fs/sdcardfs/inode.c @@ -748,6 +748,11 @@ static int sdcardfs_setattr(struct vfsmount *mnt, struct dentry *dentry, struct * this user can change the lower inode: that should happen when * calling notify_change on the lower inode. */ + /* prepare our own lower struct iattr (with the lower file) */ + memcpy(&lower_ia, ia, sizeof(lower_ia)); + /* Allow touch updating timestamps. A previous permission check ensures + * we have write access. Changes to mode, owner, and group are ignored*/ + ia->ia_valid |= ATTR_FORCE; err = setattr_prepare(&tmp_d, ia); if (!err) { @@ -773,8 +778,6 @@ static int sdcardfs_setattr(struct vfsmount *mnt, struct dentry *dentry, struct lower_mnt = lower_path.mnt; lower_inode = sdcardfs_lower_inode(inode); - /* prepare our own lower struct iattr (with the lower file) */ - memcpy(&lower_ia, ia, sizeof(lower_ia)); if (ia->ia_valid & ATTR_FILE) lower_ia.ia_file = sdcardfs_lower_file(ia->ia_file); From 388fa10060b2767ebbba1adaef46aa5053fd1036 Mon Sep 17 00:00:00 2001 From: Daniel Rosenberg Date: Sat, 21 Jan 2017 00:35:26 -0800 Subject: [PATCH 0320/1103] ANDROID: sdcardfs: Refactor configfs interface This refactors the configfs code to be more easily extended. It will allow additional files to be added easily. Signed-off-by: Daniel Rosenberg Bug: 34542611 Bug: 34262585 Change-Id: I73c9b0ae5ca7eb27f4ebef3e6807f088b512d539 --- fs/sdcardfs/packagelist.c | 133 +++++++++++++++----------------------- 1 file changed, 53 insertions(+), 80 deletions(-) diff --git a/fs/sdcardfs/packagelist.c b/fs/sdcardfs/packagelist.c index 03776fa5f26c..0b3fb50b1fe4 100644 --- a/fs/sdcardfs/packagelist.c +++ b/fs/sdcardfs/packagelist.c @@ -220,26 +220,24 @@ static void packagelist_destroy(void) printk(KERN_INFO "sdcardfs: destroyed packagelist pkgld\n"); } -struct package_appid { +struct package_details { struct config_item item; - int add_pid; + const char *name; }; -static inline struct package_appid *to_package_appid(struct config_item *item) +static inline struct package_details *to_package_details(struct config_item *item) { - return item ? container_of(item, struct package_appid, item) : NULL; + return item ? container_of(item, struct package_details, item) : NULL; } -static ssize_t package_appid_attr_show(struct config_item *item, - char *page) +static ssize_t package_details_appid_show(struct config_item *item, char *page) { - return scnprintf(page, PAGE_SIZE, "%u\n", get_appid(item->ci_name)); + return scnprintf(page, PAGE_SIZE, "%u\n", get_appid(to_package_details(item)->name)); } -static ssize_t package_appid_attr_store(struct config_item *item, +static ssize_t package_details_appid_store(struct config_item *item, const char *page, size_t count) { - struct package_appid *package_appid = to_package_appid(item); unsigned int tmp; int ret; @@ -247,73 +245,60 @@ static ssize_t package_appid_attr_store(struct config_item *item, if (ret) return ret; - ret = insert_packagelist_entry(item->ci_name, tmp); - package_appid->add_pid = tmp; + ret = insert_packagelist_entry(to_package_details(item)->name, tmp); + if (ret) return ret; return count; } -static struct configfs_attribute package_appid_attr_add_pid = { - .ca_owner = THIS_MODULE, - .ca_name = "appid", - .ca_mode = S_IRUGO | S_IWUGO, - .show = package_appid_attr_show, - .store = package_appid_attr_store, -}; +static void package_details_release(struct config_item *item) +{ + struct package_details *package_details = to_package_details(item); + printk(KERN_INFO "sdcardfs: removing %s\n", package_details->name); + remove_packagelist_entry(package_details->name); + kfree(package_details->name); + kfree(package_details); +} + +CONFIGFS_ATTR(package_details_, appid); -static struct configfs_attribute *package_appid_attrs[] = { - &package_appid_attr_add_pid, +static struct configfs_attribute *package_details_attrs[] = { + &package_details_attr_appid, NULL, }; -static void package_appid_release(struct config_item *item) -{ - printk(KERN_INFO "sdcardfs: removing %s\n", item->ci_dentry->d_name.name); - /* item->ci_name is freed already, so we rely on the dentry */ - remove_packagelist_entry(item->ci_dentry->d_name.name); - kfree(to_package_appid(item)); -} - -static struct configfs_item_operations package_appid_item_ops = { - .release = package_appid_release, +static struct configfs_item_operations package_details_item_ops = { + .release = package_details_release, }; static struct config_item_type package_appid_type = { - .ct_item_ops = &package_appid_item_ops, - .ct_attrs = package_appid_attrs, + .ct_item_ops = &package_details_item_ops, + .ct_attrs = package_details_attrs, .ct_owner = THIS_MODULE, }; - -struct sdcardfs_packages { - struct config_group group; -}; - -static inline struct sdcardfs_packages *to_sdcardfs_packages(struct config_item *item) +static struct config_item *packages_make_item(struct config_group *group, const char *name) { - return item ? container_of(to_config_group(item), struct sdcardfs_packages, group) : NULL; -} + struct package_details *package_details; -static struct config_item *sdcardfs_packages_make_item(struct config_group *group, const char *name) -{ - struct package_appid *package_appid; - - package_appid = kzalloc(sizeof(struct package_appid), GFP_KERNEL); - if (!package_appid) + package_details = kzalloc(sizeof(struct package_details), GFP_KERNEL); + if (!package_details) + return ERR_PTR(-ENOMEM); + package_details->name = kstrdup(name, GFP_KERNEL); + if (!package_details->name) { + kfree(package_details); return ERR_PTR(-ENOMEM); + } - config_item_init_type_name(&package_appid->item, name, + config_item_init_type_name(&package_details->item, name, &package_appid_type); - package_appid->add_pid = 0; - - return &package_appid->item; + return &package_details->item; } -static ssize_t packages_attr_show(struct config_item *item, - char *page) +static ssize_t packages_list_show(struct config_item *item, char *page) { struct hashtable_entry *hash_cur; int i; @@ -335,49 +320,37 @@ static ssize_t packages_attr_show(struct config_item *item, return count; } -static struct configfs_attribute sdcardfs_packages_attr_description = { - .ca_owner = THIS_MODULE, - .ca_name = "packages_gid.list", - .ca_mode = S_IRUGO, - .show = packages_attr_show, +static struct configfs_attribute packages_attr_packages_gid_list = { + .ca_name = "packages_gid.list", + .ca_mode = S_IRUGO, + .ca_owner = THIS_MODULE, + .show = packages_list_show, }; -static struct configfs_attribute *sdcardfs_packages_attrs[] = { - &sdcardfs_packages_attr_description, +static struct configfs_attribute *packages_attrs[] = { + &packages_attr_packages_gid_list, NULL, }; -static void sdcardfs_packages_release(struct config_item *item) -{ - - printk(KERN_INFO "sdcardfs: destroyed something?\n"); - kfree(to_sdcardfs_packages(item)); -} - -static struct configfs_item_operations sdcardfs_packages_item_ops = { - .release = sdcardfs_packages_release, -}; - /* * Note that, since no extra work is required on ->drop_item(), * no ->drop_item() is provided. */ -static struct configfs_group_operations sdcardfs_packages_group_ops = { - .make_item = sdcardfs_packages_make_item, +static struct configfs_group_operations packages_group_ops = { + .make_item = packages_make_item, }; -static struct config_item_type sdcardfs_packages_type = { - .ct_item_ops = &sdcardfs_packages_item_ops, - .ct_group_ops = &sdcardfs_packages_group_ops, - .ct_attrs = sdcardfs_packages_attrs, +static struct config_item_type packages_type = { + .ct_group_ops = &packages_group_ops, + .ct_attrs = packages_attrs, .ct_owner = THIS_MODULE, }; -static struct configfs_subsystem sdcardfs_packages_subsys = { +static struct configfs_subsystem sdcardfs_packages = { .su_group = { .cg_item = { .ci_namebuf = "sdcardfs", - .ci_type = &sdcardfs_packages_type, + .ci_type = &packages_type, }, }, }; @@ -385,7 +358,7 @@ static struct configfs_subsystem sdcardfs_packages_subsys = { static int configfs_sdcardfs_init(void) { int ret; - struct configfs_subsystem *subsys = &sdcardfs_packages_subsys; + struct configfs_subsystem *subsys = &sdcardfs_packages; config_group_init(&subsys->su_group); mutex_init(&subsys->su_mutex); @@ -400,7 +373,7 @@ static int configfs_sdcardfs_init(void) static void configfs_sdcardfs_exit(void) { - configfs_unregister_subsystem(&sdcardfs_packages_subsys); + configfs_unregister_subsystem(&sdcardfs_packages); } int packagelist_init(void) From a8777da810e96024fa07fe1544e2c4bb60eb703d Mon Sep 17 00:00:00 2001 From: Daniel Rosenberg Date: Sun, 22 Jan 2017 15:32:49 -0800 Subject: [PATCH 0321/1103] ANDROID: sdcardfs: add support for user permission isolation This allows you to hide the existence of a package from a user by adding them to an exclude list. If a user creates that package's folder and is on the exclude list, they will not see that package's id. Signed-off-by: Daniel Rosenberg Bug: 34542611 Change-Id: I9eb82e0bf2457d7eb81ee56153b9c7d2f6646323 --- fs/sdcardfs/derived_perm.c | 34 +++-- fs/sdcardfs/packagelist.c | 297 ++++++++++++++++++++++++++++++++++--- fs/sdcardfs/sdcardfs.h | 17 ++- 3 files changed, 308 insertions(+), 40 deletions(-) diff --git a/fs/sdcardfs/derived_perm.c b/fs/sdcardfs/derived_perm.c index 9408a5477ada..a6d87d900ce5 100644 --- a/fs/sdcardfs/derived_perm.c +++ b/fs/sdcardfs/derived_perm.c @@ -103,7 +103,7 @@ void get_derived_permission_new(struct dentry *parent, struct dentry *dentry, st case PERM_ANDROID_OBB: case PERM_ANDROID_MEDIA: appid = get_appid(newdentry->d_name.name); - if (appid != 0) { + if (appid != 0 && !is_excluded(newdentry->d_name.name, parent_info->userid)) { info->d_uid = multiuser_get_uid(parent_info->userid, appid); } set_top(info, &info->vfs_inode); @@ -116,8 +116,11 @@ void get_derived_permission(struct dentry *parent, struct dentry *dentry) get_derived_permission_new(parent, dentry, dentry); } -static int descendant_may_need_fixup(perm_t perm) { - if (perm == PERM_PRE_ROOT || perm == PERM_ROOT || perm == PERM_ANDROID) +static int descendant_may_need_fixup(struct sdcardfs_inode_info *info, struct limit_search *limit) +{ + if (info->perm == PERM_ROOT) + return (limit->flags & BY_USERID)?info->userid == limit->userid:1; + if (info->perm == PERM_PRE_ROOT || info->perm == PERM_ANDROID) return 1; return 0; } @@ -129,7 +132,8 @@ static int needs_fixup(perm_t perm) { return 0; } -void fixup_perms_recursive(struct dentry *dentry, const char* name, size_t len) { +void fixup_perms_recursive(struct dentry *dentry, struct limit_search *limit) +{ struct dentry *child; struct sdcardfs_inode_info *info; if (!dget(dentry)) @@ -143,22 +147,22 @@ void fixup_perms_recursive(struct dentry *dentry, const char* name, size_t len) if (needs_fixup(info->perm)) { spin_lock(&dentry->d_lock); list_for_each_entry(child, &dentry->d_subdirs, d_child) { - dget(child); - if (!strncasecmp(child->d_name.name, name, len)) { - if (child->d_inode) { - get_derived_permission(dentry, child); - fixup_tmp_permissions(child->d_inode); - dput(child); - break; - } + dget(child); + if (!(limit->flags & BY_NAME) || !strncasecmp(child->d_name.name, limit->name, limit->length)) { + if (d_inode(child)) { + get_derived_permission(dentry, child); + fixup_tmp_permissions(d_inode(child)); + dput(child); + break; } - dput(child); + } + dput(child); } spin_unlock(&dentry->d_lock); - } else if (descendant_may_need_fixup(info->perm)) { + } else if (descendant_may_need_fixup(info, limit)) { spin_lock(&dentry->d_lock); list_for_each_entry(child, &dentry->d_subdirs, d_child) { - fixup_perms_recursive(child, name, len); + fixup_perms_recursive(child, limit); } spin_unlock(&dentry->d_lock); } diff --git a/fs/sdcardfs/packagelist.c b/fs/sdcardfs/packagelist.c index 0b3fb50b1fe4..6eb73ddc2ceb 100644 --- a/fs/sdcardfs/packagelist.c +++ b/fs/sdcardfs/packagelist.c @@ -31,11 +31,13 @@ struct hashtable_entry { struct hlist_node hlist; + struct hlist_node dlist; /* for deletion cleanup */ const char *key; atomic_t value; }; static DEFINE_HASHTABLE(package_to_appid, 8); +static DEFINE_HASHTABLE(package_to_userid, 8); static struct kmem_cache *hashtable_entry_cachep; @@ -69,6 +71,22 @@ appid_t get_appid(const char *app_name) return 0; } +appid_t is_excluded(const char *app_name, userid_t user) +{ + struct hashtable_entry *hash_cur; + unsigned int hash = str_hash(app_name); + + rcu_read_lock(); + hash_for_each_possible_rcu(package_to_userid, hash_cur, hlist, hash) { + if (atomic_read(&hash_cur->value) == user && !strcasecmp(app_name, hash_cur->key)) { + rcu_read_unlock(); + return 1; + } + } + rcu_read_unlock(); + return 0; +} + /* Kernel has already enforced everything we returned through * derive_permissions_locked(), so this is used to lock down access * even further, such as enforcing that apps hold sdcard_rw. */ @@ -124,7 +142,7 @@ static struct hashtable_entry *alloc_packagelist_entry(const char *key, return ret; } -static int insert_packagelist_entry_locked(const char *key, appid_t value) +static int insert_packagelist_appid_entry_locked(const char *key, appid_t value) { struct hashtable_entry *hash_cur; struct hashtable_entry *new_entry; @@ -143,18 +161,64 @@ static int insert_packagelist_entry_locked(const char *key, appid_t value) return 0; } -static void fixup_perms(struct super_block *sb, const char *key) { - if (sb && sb->s_magic == SDCARDFS_SUPER_MAGIC) { - fixup_perms_recursive(sb->s_root, key, strlen(key)); +static int insert_userid_exclude_entry_locked(const char *key, userid_t value) +{ + struct hashtable_entry *hash_cur; + struct hashtable_entry *new_entry; + unsigned int hash = str_hash(key); + + /* Only insert if not already present */ + hash_for_each_possible_rcu(package_to_userid, hash_cur, hlist, hash) { + if (atomic_read(&hash_cur->value) == value && !strcasecmp(key, hash_cur->key)) + return 0; + } + new_entry = alloc_packagelist_entry(key, value); + if (!new_entry) + return -ENOMEM; + hash_add_rcu(package_to_userid, &new_entry->hlist, hash); + return 0; +} + +static void fixup_all_perms_name(const char *key) +{ + struct sdcardfs_sb_info *sbinfo; + struct limit_search limit = { + .flags = BY_NAME, + .name = key, + .length = strlen(key), + }; + list_for_each_entry(sbinfo, &sdcardfs_super_list, list) { + if (sbinfo_has_sdcard_magic(sbinfo)) + fixup_perms_recursive(sbinfo->sb->s_root, &limit); } } -static void fixup_all_perms(const char *key) +static void fixup_all_perms_name_userid(const char *key, userid_t userid) { struct sdcardfs_sb_info *sbinfo; - list_for_each_entry(sbinfo, &sdcardfs_super_list, list) - if (sbinfo) - fixup_perms(sbinfo->sb, key); + struct limit_search limit = { + .flags = BY_NAME | BY_USERID, + .name = key, + .length = strlen(key), + .userid = userid, + }; + list_for_each_entry(sbinfo, &sdcardfs_super_list, list) { + if (sbinfo_has_sdcard_magic(sbinfo)) + fixup_perms_recursive(sbinfo->sb->s_root, &limit); + } +} + +static void fixup_all_perms_userid(userid_t userid) +{ + struct sdcardfs_sb_info *sbinfo; + struct limit_search limit = { + .flags = BY_USERID, + .userid = userid, + }; + list_for_each_entry(sbinfo, &sdcardfs_super_list, list) { + if (sbinfo_has_sdcard_magic(sbinfo)) + fixup_perms_recursive(sbinfo->sb->s_root, &limit); + } } static int insert_packagelist_entry(const char *key, appid_t value) @@ -162,9 +226,22 @@ static int insert_packagelist_entry(const char *key, appid_t value) int err; mutex_lock(&sdcardfs_super_list_lock); - err = insert_packagelist_entry_locked(key, value); + err = insert_packagelist_appid_entry_locked(key, value); if (!err) - fixup_all_perms(key); + fixup_all_perms_name(key); + mutex_unlock(&sdcardfs_super_list_lock); + + return err; +} + +static int insert_userid_exclude_entry(const char *key, userid_t value) +{ + int err; + + mutex_lock(&sdcardfs_super_list_lock); + err = insert_userid_exclude_entry_locked(key, value); + if (!err) + fixup_all_perms_name_userid(key, value); mutex_unlock(&sdcardfs_super_list_lock); return err; @@ -173,7 +250,7 @@ static int insert_packagelist_entry(const char *key, appid_t value) static void free_packagelist_entry(struct hashtable_entry *entry) { kfree(entry->key); - hash_del_rcu(&entry->hlist); + hash_del_rcu(&entry->dlist); kmem_cache_free(hashtable_entry_cachep, entry); } @@ -181,22 +258,84 @@ static void remove_packagelist_entry_locked(const char *key) { struct hashtable_entry *hash_cur; unsigned int hash = str_hash(key); + struct hlist_node *h_t; + HLIST_HEAD(free_list); + hash_for_each_possible_rcu(package_to_userid, hash_cur, hlist, hash) { + if (!strcasecmp(key, hash_cur->key)) { + hash_del_rcu(&hash_cur->hlist); + hlist_add_head(&hash_cur->dlist, &free_list); + } + } hash_for_each_possible_rcu(package_to_appid, hash_cur, hlist, hash) { if (!strcasecmp(key, hash_cur->key)) { hash_del_rcu(&hash_cur->hlist); - synchronize_rcu(); - free_packagelist_entry(hash_cur); - return; + hlist_add_head(&hash_cur->dlist, &free_list); + break; } } + synchronize_rcu(); + hlist_for_each_entry_safe(hash_cur, h_t, &free_list, dlist) + free_packagelist_entry(hash_cur); } static void remove_packagelist_entry(const char *key) { mutex_lock(&sdcardfs_super_list_lock); remove_packagelist_entry_locked(key); - fixup_all_perms(key); + fixup_all_perms_name(key); + mutex_unlock(&sdcardfs_super_list_lock); + return; +} + +static void remove_userid_all_entry_locked(userid_t userid) +{ + struct hashtable_entry *hash_cur; + struct hlist_node *h_t; + HLIST_HEAD(free_list); + int i; + + hash_for_each_rcu(package_to_userid, i, hash_cur, hlist) { + if (atomic_read(&hash_cur->value) == userid) { + hash_del_rcu(&hash_cur->hlist); + hlist_add_head(&hash_cur->dlist, &free_list); + } + } + synchronize_rcu(); + hlist_for_each_entry_safe(hash_cur, h_t, &free_list, dlist) { + free_packagelist_entry(hash_cur); + } +} + +static void remove_userid_all_entry(userid_t userid) +{ + mutex_lock(&sdcardfs_super_list_lock); + remove_userid_all_entry_locked(userid); + fixup_all_perms_userid(userid); + mutex_unlock(&sdcardfs_super_list_lock); + return; +} + +static void remove_userid_exclude_entry_locked(const char *key, userid_t userid) +{ + struct hashtable_entry *hash_cur; + unsigned int hash = str_hash(key); + + hash_for_each_possible_rcu(package_to_userid, hash_cur, hlist, hash) { + if (!strcasecmp(key, hash_cur->key) && atomic_read(&hash_cur->value) == userid) { + hash_del_rcu(&hash_cur->hlist); + synchronize_rcu(); + free_packagelist_entry(hash_cur); + break; + } + } +} + +static void remove_userid_exclude_entry(const char *key, userid_t userid) +{ + mutex_lock(&sdcardfs_super_list_lock); + remove_userid_exclude_entry_locked(key, userid); + fixup_all_perms_name_userid(key, userid); mutex_unlock(&sdcardfs_super_list_lock); return; } @@ -210,16 +349,44 @@ static void packagelist_destroy(void) mutex_lock(&sdcardfs_super_list_lock); hash_for_each_rcu(package_to_appid, i, hash_cur, hlist) { hash_del_rcu(&hash_cur->hlist); - hlist_add_head(&hash_cur->hlist, &free_list); - + hlist_add_head(&hash_cur->dlist, &free_list); + } + hash_for_each_rcu(package_to_userid, i, hash_cur, hlist) { + hash_del_rcu(&hash_cur->hlist); + hlist_add_head(&hash_cur->dlist, &free_list); } synchronize_rcu(); - hlist_for_each_entry_safe(hash_cur, h_t, &free_list, hlist) + hlist_for_each_entry_safe(hash_cur, h_t, &free_list, dlist) free_packagelist_entry(hash_cur); mutex_unlock(&sdcardfs_super_list_lock); printk(KERN_INFO "sdcardfs: destroyed packagelist pkgld\n"); } +#define SDCARDFS_CONFIGFS_ATTR(_pfx, _name) \ +static struct configfs_attribute _pfx##attr_##_name = { \ + .ca_name = __stringify(_name), \ + .ca_mode = S_IRUGO | S_IWUGO, \ + .ca_owner = THIS_MODULE, \ + .show = _pfx##_name##_show, \ + .store = _pfx##_name##_store, \ +} + +#define SDCARDFS_CONFIGFS_ATTR_RO(_pfx, _name) \ +static struct configfs_attribute _pfx##attr_##_name = { \ + .ca_name = __stringify(_name), \ + .ca_mode = S_IRUGO, \ + .ca_owner = THIS_MODULE, \ + .show = _pfx##_name##_show, \ +} + +#define SDCARDFS_CONFIGFS_ATTR_WO(_pfx, _name) \ +static struct configfs_attribute _pfx##attr_##_name = { \ + .ca_name = __stringify(_name), \ + .ca_mode = S_IWUGO, \ + .ca_owner = THIS_MODULE, \ + .store = _pfx##_name##_store, \ +} + struct package_details { struct config_item item; const char *name; @@ -253,6 +420,58 @@ static ssize_t package_details_appid_store(struct config_item *item, return count; } +static ssize_t package_details_excluded_userids_show(struct config_item *item, + char *page) +{ + struct package_details *package_details = to_package_details(item); + struct hashtable_entry *hash_cur; + unsigned int hash = str_hash(package_details->name); + int count = 0; + + rcu_read_lock(); + hash_for_each_possible_rcu(package_to_userid, hash_cur, hlist, hash) { + if (!strcasecmp(package_details->name, hash_cur->key)) + count += scnprintf(page + count, PAGE_SIZE - count, + "%d ", atomic_read(&hash_cur->value)); + } + rcu_read_unlock(); + if (count) + count--; + count += scnprintf(page + count, PAGE_SIZE - count, "\n"); + return count; +} + +static ssize_t package_details_excluded_userids_store(struct config_item *item, + const char *page, size_t count) +{ + unsigned int tmp; + int ret; + + ret = kstrtouint(page, 10, &tmp); + if (ret) + return ret; + + ret = insert_userid_exclude_entry(to_package_details(item)->name, tmp); + + if (ret) + return ret; + + return count; +} + +static ssize_t package_details_clear_userid_store(struct config_item *item, + const char *page, size_t count) +{ + unsigned int tmp; + int ret; + + ret = kstrtouint(page, 10, &tmp); + if (ret) + return ret; + remove_userid_exclude_entry(to_package_details(item)->name, tmp); + return count; +} + static void package_details_release(struct config_item *item) { struct package_details *package_details = to_package_details(item); @@ -262,10 +481,14 @@ static void package_details_release(struct config_item *item) kfree(package_details); } -CONFIGFS_ATTR(package_details_, appid); +SDCARDFS_CONFIGFS_ATTR(package_details_, appid); +SDCARDFS_CONFIGFS_ATTR(package_details_, excluded_userids); +SDCARDFS_CONFIGFS_ATTR_WO(package_details_, clear_userid); static struct configfs_attribute *package_details_attrs[] = { &package_details_attr_appid, + &package_details_attr_excluded_userids, + &package_details_attr_clear_userid, NULL, }; @@ -293,23 +516,33 @@ static struct config_item *packages_make_item(struct config_group *group, const } config_item_init_type_name(&package_details->item, name, - &package_appid_type); + &package_appid_type); return &package_details->item; } static ssize_t packages_list_show(struct config_item *item, char *page) { - struct hashtable_entry *hash_cur; + struct hashtable_entry *hash_cur_app; + struct hashtable_entry *hash_cur_user; int i; int count = 0, written = 0; const char errormsg[] = "\n"; + unsigned int hash; rcu_read_lock(); - hash_for_each_rcu(package_to_appid, i, hash_cur, hlist) { + hash_for_each_rcu(package_to_appid, i, hash_cur_app, hlist) { written = scnprintf(page + count, PAGE_SIZE - sizeof(errormsg) - count, "%s %d\n", - (const char *)hash_cur->key, atomic_read(&hash_cur->value)); - if (count + written == PAGE_SIZE - sizeof(errormsg)) { + hash_cur_app->key, atomic_read(&hash_cur_app->value)); + hash = str_hash(hash_cur_app->key); + hash_for_each_possible_rcu(package_to_userid, hash_cur_user, hlist, hash) { + if (!strcasecmp(hash_cur_app->key, hash_cur_user->key)) { + written += scnprintf(page + count + written - 1, + PAGE_SIZE - sizeof(errormsg) - count - written + 1, + " %d\n", atomic_read(&hash_cur_user->value)) - 1; + } + } + if (count + written == PAGE_SIZE - sizeof(errormsg) - 1) { count += scnprintf(page + count, PAGE_SIZE - count, errormsg); break; } @@ -320,6 +553,19 @@ static ssize_t packages_list_show(struct config_item *item, char *page) return count; } +static ssize_t packages_remove_userid_store(struct config_item *item, + const char *page, size_t count) +{ + unsigned int tmp; + int ret; + + ret = kstrtouint(page, 10, &tmp); + if (ret) + return ret; + remove_userid_all_entry(tmp); + return count; +} + static struct configfs_attribute packages_attr_packages_gid_list = { .ca_name = "packages_gid.list", .ca_mode = S_IRUGO, @@ -327,8 +573,11 @@ static struct configfs_attribute packages_attr_packages_gid_list = { .show = packages_list_show, }; +SDCARDFS_CONFIGFS_ATTR_WO(packages_, remove_userid); + static struct configfs_attribute *packages_attrs[] = { &packages_attr_packages_gid_list, + &packages_attr_remove_userid, NULL, }; diff --git a/fs/sdcardfs/sdcardfs.h b/fs/sdcardfs/sdcardfs.h index 66a97ef8d261..9f287a65338c 100644 --- a/fs/sdcardfs/sdcardfs.h +++ b/fs/sdcardfs/sdcardfs.h @@ -335,6 +335,11 @@ static inline void sdcardfs_put_reset_##pname(const struct dentry *dent) \ SDCARDFS_DENT_FUNC(lower_path) SDCARDFS_DENT_FUNC(orig_path) +static inline bool sbinfo_has_sdcard_magic(struct sdcardfs_sb_info *sbinfo) +{ + return sbinfo && sbinfo->sb && sbinfo->sb->s_magic == SDCARDFS_SUPER_MAGIC; +} + /* grab a refererence if we aren't linking to ourself */ static inline void set_top(struct sdcardfs_inode_info *info, struct inode *top) { @@ -442,18 +447,28 @@ extern struct list_head sdcardfs_super_list; /* for packagelist.c */ extern appid_t get_appid(const char *app_name); +extern appid_t is_excluded(const char *app_name, userid_t userid); extern int check_caller_access_to_name(struct inode *parent_node, const char* name); extern int open_flags_to_access_mode(int open_flags); extern int packagelist_init(void); extern void packagelist_exit(void); /* for derived_perm.c */ +#define BY_NAME (1 << 0) +#define BY_USERID (1 << 1) +struct limit_search { + unsigned int flags; + const char *name; + size_t length; + userid_t userid; +}; + extern void setup_derived_state(struct inode *inode, perm_t perm, userid_t userid, uid_t uid, bool under_android, struct inode *top); extern void get_derived_permission(struct dentry *parent, struct dentry *dentry); extern void get_derived_permission_new(struct dentry *parent, struct dentry *dentry, struct dentry *newdentry); extern void fixup_top_recursive(struct dentry *parent); -extern void fixup_perms_recursive(struct dentry *dentry, const char *name, size_t len); +extern void fixup_perms_recursive(struct dentry *dentry, struct limit_search *limit); extern void update_derived_permission_lock(struct dentry *dentry); extern int need_graft_path(struct dentry *dentry); From a7d863e97f9587eaf59ef46d640b0423b54a4a9f Mon Sep 17 00:00:00 2001 From: Daniel Rosenberg Date: Thu, 26 Jan 2017 20:10:34 -0800 Subject: [PATCH 0322/1103] ANDROID: sdcardfs: Remove redundant operation We call get_derived_permission_new unconditionally, so we don't need to call update_derived_permission_lock, which does the same thing. Signed-off-by: Daniel Rosenberg Change-Id: I0748100828c6af806da807241a33bf42be614935 --- fs/sdcardfs/inode.c | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/fs/sdcardfs/inode.c b/fs/sdcardfs/inode.c index 4bafdf783354..d2b404c7da14 100644 --- a/fs/sdcardfs/inode.c +++ b/fs/sdcardfs/inode.c @@ -467,7 +467,6 @@ static int sdcardfs_rename(struct inode *old_dir, struct dentry *old_dentry, struct dentry *lower_new_dir_dentry = NULL; struct vfsmount *lower_mnt = NULL; struct dentry *trap = NULL; - struct dentry *new_parent = NULL; struct path lower_old_path, lower_new_path; const struct cred *saved_cred = NULL; @@ -520,17 +519,6 @@ static int sdcardfs_rename(struct inode *old_dir, struct dentry *old_dentry, if (new_dir != old_dir) { sdcardfs_copy_and_fix_attrs(old_dir, d_inode(lower_old_dir_dentry)); fsstack_copy_inode_size(old_dir, d_inode(lower_old_dir_dentry)); - - /* update the derived permission of the old_dentry - * with its new parent - */ - new_parent = dget_parent(new_dentry); - if(new_parent) { - if(d_inode(old_dentry)) { - update_derived_permission_lock(old_dentry); - } - dput(new_parent); - } } /* At this point, not all dentry information has been moved, so * we pass along new_dentry for the name.*/ From 87f4afc4c931f0c4fb7baa631697983be0d1d499 Mon Sep 17 00:00:00 2001 From: Daniel Rosenberg Date: Wed, 25 Jan 2017 13:48:45 -0800 Subject: [PATCH 0323/1103] ANDROID: sdcardfs: Add GID Derivation to sdcardfs This changes sdcardfs to modify the user and group in the underlying filesystem depending on its usage. Ownership is set by Android user, and package, as well as if the file is under obb or cache. Other files can be labeled by extension. Those values are set via the configfs interace. To add an entry, mkdir -p [configfs root]/sdcardfs/extensions/[gid]/[ext] Signed-off-by: Daniel Rosenberg Bug: 34262585 Change-Id: I4e030ce84f094a678376349b1a96923e5076a0f4 --- fs/sdcardfs/derived_perm.c | 239 ++++++++++++++++++++++++++++++------- fs/sdcardfs/file.c | 2 +- fs/sdcardfs/inode.c | 42 ++++--- fs/sdcardfs/lookup.c | 3 +- fs/sdcardfs/multiuser.h | 31 +++-- fs/sdcardfs/packagelist.c | 231 ++++++++++++++++++++++++++++++++--- fs/sdcardfs/sdcardfs.h | 35 ++++-- 7 files changed, 487 insertions(+), 96 deletions(-) diff --git a/fs/sdcardfs/derived_perm.c b/fs/sdcardfs/derived_perm.c index a6d87d900ce5..16f05172f9bf 100644 --- a/fs/sdcardfs/derived_perm.c +++ b/fs/sdcardfs/derived_perm.c @@ -30,6 +30,8 @@ static void inherit_derived_state(struct inode *parent, struct inode *child) ci->userid = pi->userid; ci->d_uid = pi->d_uid; ci->under_android = pi->under_android; + ci->under_cache = pi->under_cache; + ci->under_obb = pi->under_obb; set_top(ci, pi->top); } @@ -43,11 +45,13 @@ void setup_derived_state(struct inode *inode, perm_t perm, userid_t userid, info->userid = userid; info->d_uid = uid; info->under_android = under_android; + info->under_cache = false; + info->under_obb = false; set_top(info, top); } /* While renaming, there is a point where we want the path from dentry, but the name from newdentry */ -void get_derived_permission_new(struct dentry *parent, struct dentry *dentry, struct dentry *newdentry) +void get_derived_permission_new(struct dentry *parent, struct dentry *dentry, const char *name) { struct sdcardfs_inode_info *info = SDCARDFS_I(d_inode(dentry)); struct sdcardfs_inode_info *parent_info= SDCARDFS_I(d_inode(parent)); @@ -57,63 +61,185 @@ void get_derived_permission_new(struct dentry *parent, struct dentry *dentry, st * the properties are maintained on its private fields * because the inode attributes will be modified with that of * its lower inode. - * The derived state will be updated on the last - * stage of each system call by fix_derived_permission(inode). + * These values are used by our custom permission call instead + * of using the inode permissions. */ inherit_derived_state(d_inode(parent), d_inode(dentry)); + /* Files don't get special labels */ + if (!S_ISDIR(d_inode(dentry)->i_mode)) + return; /* Derive custom permissions based on parent and current node */ switch (parent_info->perm) { - case PERM_INHERIT: - /* Already inherited above */ - break; - case PERM_PRE_ROOT: - /* Legacy internal layout places users at top level */ - info->perm = PERM_ROOT; - info->userid = simple_strtoul(newdentry->d_name.name, NULL, 10); + case PERM_INHERIT: + case PERM_ANDROID_PACKAGE_CACHE: + /* Already inherited above */ + break; + case PERM_PRE_ROOT: + /* Legacy internal layout places users at top level */ + info->perm = PERM_ROOT; + info->userid = simple_strtoul(name, NULL, 10); + set_top(info, &info->vfs_inode); + break; + case PERM_ROOT: + /* Assume masked off by default. */ + if (!strcasecmp(name, "Android")) { + /* App-specific directories inside; let anyone traverse */ + info->perm = PERM_ANDROID; + info->under_android = true; set_top(info, &info->vfs_inode); - break; - case PERM_ROOT: - /* Assume masked off by default. */ - if (!strcasecmp(newdentry->d_name.name, "Android")) { - /* App-specific directories inside; let anyone traverse */ - info->perm = PERM_ANDROID; - info->under_android = true; - set_top(info, &info->vfs_inode); - } - break; - case PERM_ANDROID: - if (!strcasecmp(newdentry->d_name.name, "data")) { - /* App-specific directories inside; let anyone traverse */ - info->perm = PERM_ANDROID_DATA; - set_top(info, &info->vfs_inode); - } else if (!strcasecmp(newdentry->d_name.name, "obb")) { - /* App-specific directories inside; let anyone traverse */ - info->perm = PERM_ANDROID_OBB; - set_top(info, &info->vfs_inode); - /* Single OBB directory is always shared */ - } else if (!strcasecmp(newdentry->d_name.name, "media")) { - /* App-specific directories inside; let anyone traverse */ - info->perm = PERM_ANDROID_MEDIA; - set_top(info, &info->vfs_inode); - } - break; - case PERM_ANDROID_DATA: - case PERM_ANDROID_OBB: - case PERM_ANDROID_MEDIA: - appid = get_appid(newdentry->d_name.name); - if (appid != 0 && !is_excluded(newdentry->d_name.name, parent_info->userid)) { - info->d_uid = multiuser_get_uid(parent_info->userid, appid); - } + } + break; + case PERM_ANDROID: + if (!strcasecmp(name, "data")) { + /* App-specific directories inside; let anyone traverse */ + info->perm = PERM_ANDROID_DATA; + set_top(info, &info->vfs_inode); + } else if (!strcasecmp(name, "obb")) { + /* App-specific directories inside; let anyone traverse */ + info->perm = PERM_ANDROID_OBB; + info->under_obb = true; set_top(info, &info->vfs_inode); - break; + /* Single OBB directory is always shared */ + } else if (!strcasecmp(name, "media")) { + /* App-specific directories inside; let anyone traverse */ + info->perm = PERM_ANDROID_MEDIA; + set_top(info, &info->vfs_inode); + } + break; + case PERM_ANDROID_OBB: + case PERM_ANDROID_DATA: + case PERM_ANDROID_MEDIA: + info->perm = PERM_ANDROID_PACKAGE; + appid = get_appid(name); + if (appid != 0 && !is_excluded(name, parent_info->userid)) { + info->d_uid = multiuser_get_uid(parent_info->userid, appid); + } + set_top(info, &info->vfs_inode); + break; + case PERM_ANDROID_PACKAGE: + if (!strcasecmp(name, "cache")) { + info->perm = PERM_ANDROID_PACKAGE_CACHE; + info->under_cache = true; + } + break; } } void get_derived_permission(struct dentry *parent, struct dentry *dentry) { - get_derived_permission_new(parent, dentry, dentry); + get_derived_permission_new(parent, dentry, dentry->d_name.name); +} + +static appid_t get_type(const char *name) +{ + const char *ext = strrchr(name, '.'); + appid_t id; + + if (ext && ext[0]) { + ext = &ext[1]; + id = get_ext_gid(ext); + return id?:AID_MEDIA_RW; + } + return AID_MEDIA_RW; +} + +void fixup_lower_ownership(struct dentry *dentry, const char *name) +{ + struct path path; + struct inode *inode; + struct inode *delegated_inode = NULL; + int error; + struct sdcardfs_inode_info *info; + struct sdcardfs_inode_info *info_top; + perm_t perm; + struct sdcardfs_sb_info *sbi = SDCARDFS_SB(dentry->d_sb); + uid_t uid = sbi->options.fs_low_uid; + gid_t gid = sbi->options.fs_low_gid; + struct iattr newattrs; + + info = SDCARDFS_I(d_inode(dentry)); + perm = info->perm; + if (info->under_obb) { + perm = PERM_ANDROID_OBB; + } else if (info->under_cache) { + perm = PERM_ANDROID_PACKAGE_CACHE; + } else if (perm == PERM_INHERIT) { + info_top = SDCARDFS_I(grab_top(info)); + perm = info_top->perm; + release_top(info); + } + + switch (perm) { + case PERM_ROOT: + case PERM_ANDROID: + case PERM_ANDROID_DATA: + case PERM_ANDROID_MEDIA: + case PERM_ANDROID_PACKAGE: + case PERM_ANDROID_PACKAGE_CACHE: + uid = multiuser_get_uid(info->userid, uid); + break; + case PERM_ANDROID_OBB: + uid = AID_MEDIA_OBB; + break; + case PERM_PRE_ROOT: + default: + break; + } + switch (perm) { + case PERM_ROOT: + case PERM_ANDROID: + case PERM_ANDROID_DATA: + case PERM_ANDROID_MEDIA: + if (S_ISDIR(d_inode(dentry)->i_mode)) + gid = multiuser_get_uid(info->userid, AID_MEDIA_RW); + else + gid = multiuser_get_uid(info->userid, get_type(name)); + break; + case PERM_ANDROID_OBB: + gid = AID_MEDIA_OBB; + break; + case PERM_ANDROID_PACKAGE: + if (info->d_uid != 0) + gid = multiuser_get_ext_gid(info->userid, info->d_uid); + else + gid = multiuser_get_uid(info->userid, uid); + break; + case PERM_ANDROID_PACKAGE_CACHE: + if (info->d_uid != 0) + gid = multiuser_get_cache_gid(info->userid, info->d_uid); + else + gid = multiuser_get_uid(info->userid, uid); + break; + case PERM_PRE_ROOT: + default: + break; + } + + sdcardfs_get_lower_path(dentry, &path); + inode = d_inode(path.dentry); + if (d_inode(path.dentry)->i_gid.val != gid || d_inode(path.dentry)->i_uid.val != uid) { +retry_deleg: + newattrs.ia_valid = ATTR_GID | ATTR_UID | ATTR_FORCE; + newattrs.ia_uid = make_kuid(current_user_ns(), uid); + newattrs.ia_gid = make_kgid(current_user_ns(), gid); + if (!S_ISDIR(inode->i_mode)) + newattrs.ia_valid |= + ATTR_KILL_SUID | ATTR_KILL_SGID | ATTR_KILL_PRIV; + inode_lock(inode); + error = security_path_chown(&path, newattrs.ia_uid, newattrs.ia_gid); + if (!error) + error = notify_change2(path.mnt, path.dentry, &newattrs, &delegated_inode); + inode_unlock(inode); + if (delegated_inode) { + error = break_deleg_wait(&delegated_inode); + if (!error) + goto retry_deleg; + } + if (error) + pr_err("sdcardfs: Failed to touch up lower fs gid/uid.\n"); + } } static int descendant_may_need_fixup(struct sdcardfs_inode_info *info, struct limit_search *limit) @@ -169,9 +295,30 @@ void fixup_perms_recursive(struct dentry *dentry, struct limit_search *limit) dput(dentry); } -void fixup_top_recursive(struct dentry *parent) { +void drop_recursive(struct dentry *parent) +{ struct dentry *dentry; struct sdcardfs_inode_info *info; + if (!d_inode(parent)) + return; + info = SDCARDFS_I(d_inode(parent)); + spin_lock(&parent->d_lock); + list_for_each_entry(dentry, &parent->d_subdirs, d_child) { + if (d_inode(dentry)) { + if (SDCARDFS_I(d_inode(parent))->top != SDCARDFS_I(d_inode(dentry))->top) { + drop_recursive(dentry); + d_drop(dentry); + } + } + } + spin_unlock(&parent->d_lock); +} + +void fixup_top_recursive(struct dentry *parent) +{ + struct dentry *dentry; + struct sdcardfs_inode_info *info; + if (!d_inode(parent)) return; info = SDCARDFS_I(d_inode(parent)); diff --git a/fs/sdcardfs/file.c b/fs/sdcardfs/file.c index 7750a0472389..006c6ff57ad7 100644 --- a/fs/sdcardfs/file.c +++ b/fs/sdcardfs/file.c @@ -225,7 +225,7 @@ static int sdcardfs_open(struct inode *inode, struct file *file) } /* save current_cred and override it */ - OVERRIDE_CRED(sbi, saved_cred); + OVERRIDE_CRED(sbi, saved_cred, SDCARDFS_I(inode)); file->private_data = kzalloc(sizeof(struct sdcardfs_file_info), GFP_KERNEL); diff --git a/fs/sdcardfs/inode.c b/fs/sdcardfs/inode.c index d2b404c7da14..103558987362 100644 --- a/fs/sdcardfs/inode.c +++ b/fs/sdcardfs/inode.c @@ -22,16 +22,21 @@ #include /* Do not directly use this function. Use OVERRIDE_CRED() instead. */ -const struct cred * override_fsids(struct sdcardfs_sb_info* sbi) +const struct cred *override_fsids(struct sdcardfs_sb_info *sbi, struct sdcardfs_inode_info *info) { - struct cred * cred; - const struct cred * old_cred; + struct cred *cred; + const struct cred *old_cred; + uid_t uid; cred = prepare_creds(); if (!cred) return NULL; - cred->fsuid = make_kuid(&init_user_ns, sbi->options.fs_low_uid); + if (info->under_obb) + uid = AID_MEDIA_OBB; + else + uid = multiuser_get_uid(info->userid, sbi->options.fs_low_uid); + cred->fsuid = make_kuid(&init_user_ns, uid); cred->fsgid = make_kgid(&init_user_ns, sbi->options.fs_low_gid); old_cred = override_creds(cred); @@ -40,9 +45,9 @@ const struct cred * override_fsids(struct sdcardfs_sb_info* sbi) } /* Do not directly use this function, use REVERT_CRED() instead. */ -void revert_fsids(const struct cred * old_cred) +void revert_fsids(const struct cred *old_cred) { - const struct cred * cur_cred; + const struct cred *cur_cred; cur_cred = current->cred; revert_creds(old_cred); @@ -70,7 +75,7 @@ static int sdcardfs_create(struct inode *dir, struct dentry *dentry, } /* save current_cred and override it */ - OVERRIDE_CRED(SDCARDFS_SB(dir->i_sb), saved_cred); + OVERRIDE_CRED(SDCARDFS_SB(dir->i_sb), saved_cred, SDCARDFS_I(dir)); sdcardfs_get_lower_path(dentry, &lower_path); lower_dentry = lower_path.dentry; @@ -98,6 +103,7 @@ static int sdcardfs_create(struct inode *dir, struct dentry *dentry, goto out; fsstack_copy_attr_times(dir, sdcardfs_lower_inode(dir)); fsstack_copy_inode_size(dir, d_inode(lower_parent_dentry)); + fixup_lower_ownership(dentry, dentry->d_name.name); out: current->fs = saved_fs; @@ -171,7 +177,7 @@ static int sdcardfs_unlink(struct inode *dir, struct dentry *dentry) } /* save current_cred and override it */ - OVERRIDE_CRED(SDCARDFS_SB(dir->i_sb), saved_cred); + OVERRIDE_CRED(SDCARDFS_SB(dir->i_sb), saved_cred, SDCARDFS_I(dir)); sdcardfs_get_lower_path(dentry, &lower_path); lower_dentry = lower_path.dentry; @@ -279,7 +285,7 @@ static int sdcardfs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode } /* save current_cred and override it */ - OVERRIDE_CRED(SDCARDFS_SB(dir->i_sb), saved_cred); + OVERRIDE_CRED(SDCARDFS_SB(dir->i_sb), saved_cred, SDCARDFS_I(dir)); /* check disk space */ if (!check_min_free_space(dentry, 0, 1)) { @@ -343,9 +349,8 @@ static int sdcardfs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode fsstack_copy_inode_size(dir, d_inode(lower_parent_dentry)); /* update number of links on parent directory */ set_nlink(dir, sdcardfs_lower_inode(dir)->i_nlink); - + fixup_lower_ownership(dentry, dentry->d_name.name); unlock_dir(lower_parent_dentry); - if ((!sbi->options.multiuser) && (!strcasecmp(dentry->d_name.name, "obb")) && (pi->perm == PERM_ANDROID) && (pi->userid == 0)) make_nomedia_in_obb = 1; @@ -353,6 +358,8 @@ static int sdcardfs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode /* When creating /Android/data and /Android/obb, mark them as .nomedia */ if (make_nomedia_in_obb || ((pi->perm == PERM_ANDROID) && (!strcasecmp(dentry->d_name.name, "data")))) { + REVERT_CRED(saved_cred); + OVERRIDE_CRED(SDCARDFS_SB(dir->i_sb), saved_cred, SDCARDFS_I(d_inode(dentry))); set_fs_pwd(current->fs, &lower_path); touch_err = touch(".nomedia", 0664); if (touch_err) { @@ -390,7 +397,7 @@ static int sdcardfs_rmdir(struct inode *dir, struct dentry *dentry) } /* save current_cred and override it */ - OVERRIDE_CRED(SDCARDFS_SB(dir->i_sb), saved_cred); + OVERRIDE_CRED(SDCARDFS_SB(dir->i_sb), saved_cred, SDCARDFS_I(dir)); /* sdcardfs_get_real_lower(): in case of remove an user's obb dentry * the dentry on the original path should be deleted. */ @@ -483,7 +490,7 @@ static int sdcardfs_rename(struct inode *old_dir, struct dentry *old_dentry, } /* save current_cred and override it */ - OVERRIDE_CRED(SDCARDFS_SB(old_dir->i_sb), saved_cred); + OVERRIDE_CRED(SDCARDFS_SB(old_dir->i_sb), saved_cred, SDCARDFS_I(new_dir)); sdcardfs_get_real_lower(old_dentry, &lower_old_path); sdcardfs_get_lower_path(new_dentry, &lower_new_path); @@ -520,11 +527,10 @@ static int sdcardfs_rename(struct inode *old_dir, struct dentry *old_dentry, sdcardfs_copy_and_fix_attrs(old_dir, d_inode(lower_old_dir_dentry)); fsstack_copy_inode_size(old_dir, d_inode(lower_old_dir_dentry)); } - /* At this point, not all dentry information has been moved, so - * we pass along new_dentry for the name.*/ - get_derived_permission_new(new_dentry->d_parent, old_dentry, new_dentry); + get_derived_permission_new(new_dentry->d_parent, old_dentry, new_dentry->d_name.name); fixup_tmp_permissions(d_inode(old_dentry)); - fixup_top_recursive(old_dentry); + fixup_lower_ownership(old_dentry, new_dentry->d_name.name); + drop_recursive(old_dentry); /* Can't fixup ownership recursively :( */ out: unlock_rename(lower_old_dir_dentry, lower_new_dir_dentry); dput(lower_old_dir_dentry); @@ -759,7 +765,7 @@ static int sdcardfs_setattr(struct vfsmount *mnt, struct dentry *dentry, struct goto out_err; /* save current_cred and override it */ - OVERRIDE_CRED(SDCARDFS_SB(dentry->d_sb), saved_cred); + OVERRIDE_CRED(SDCARDFS_SB(dentry->d_sb), saved_cred, SDCARDFS_I(inode)); sdcardfs_get_lower_path(dentry, &lower_path); lower_dentry = lower_path.dentry; diff --git a/fs/sdcardfs/lookup.c b/fs/sdcardfs/lookup.c index d271617290ee..1d664c9e2942 100644 --- a/fs/sdcardfs/lookup.c +++ b/fs/sdcardfs/lookup.c @@ -368,7 +368,7 @@ struct dentry *sdcardfs_lookup(struct inode *dir, struct dentry *dentry, } /* save current_cred and override it */ - OVERRIDE_CRED_PTR(SDCARDFS_SB(dir->i_sb), saved_cred); + OVERRIDE_CRED_PTR(SDCARDFS_SB(dir->i_sb), saved_cred, SDCARDFS_I(dir)); sdcardfs_get_lower_path(parent, &lower_parent_path); @@ -392,6 +392,7 @@ struct dentry *sdcardfs_lookup(struct inode *dir, struct dentry *dentry, /* get derived permission */ get_derived_permission(parent, dentry); fixup_tmp_permissions(d_inode(dentry)); + fixup_lower_ownership(dentry, dentry->d_name.name); } /* update parent directory's atime */ fsstack_copy_attr_atime(d_inode(parent), diff --git a/fs/sdcardfs/multiuser.h b/fs/sdcardfs/multiuser.h index 923ba101dfa9..950cffddc556 100644 --- a/fs/sdcardfs/multiuser.h +++ b/fs/sdcardfs/multiuser.h @@ -18,20 +18,35 @@ * General Public License. */ -#define MULTIUSER_APP_PER_USER_RANGE 100000 +#define AID_USER_OFFSET 100000 /* offset for uid ranges for each user */ +#define AID_APP_START 10000 /* first app user */ +#define AID_APP_END 19999 /* last app user */ +#define AID_CACHE_GID_START 20000 /* start of gids for apps to mark cached data */ +#define AID_EXT_GID_START 30000 /* start of gids for apps to mark external data */ +#define AID_SHARED_GID_START 50000 /* start of gids for apps in each user to share */ typedef uid_t userid_t; typedef uid_t appid_t; -static inline userid_t multiuser_get_user_id(uid_t uid) { - return uid / MULTIUSER_APP_PER_USER_RANGE; +static inline uid_t multiuser_get_uid(userid_t user_id, appid_t app_id) +{ + return (user_id * AID_USER_OFFSET) + (app_id % AID_USER_OFFSET); } -static inline appid_t multiuser_get_app_id(uid_t uid) { - return uid % MULTIUSER_APP_PER_USER_RANGE; +static inline gid_t multiuser_get_cache_gid(userid_t user_id, appid_t app_id) +{ + if (app_id >= AID_APP_START && app_id <= AID_APP_END) { + return multiuser_get_uid(user_id, (app_id - AID_APP_START) + AID_CACHE_GID_START); + } else { + return -1; + } } -static inline uid_t multiuser_get_uid(userid_t userId, appid_t appId) { - return userId * MULTIUSER_APP_PER_USER_RANGE + (appId % MULTIUSER_APP_PER_USER_RANGE); +static inline gid_t multiuser_get_ext_gid(userid_t user_id, appid_t app_id) +{ + if (app_id >= AID_APP_START && app_id <= AID_APP_END) { + return multiuser_get_uid(user_id, (app_id - AID_APP_START) + AID_EXT_GID_START); + } else { + return -1; + } } - diff --git a/fs/sdcardfs/packagelist.c b/fs/sdcardfs/packagelist.c index 6eb73ddc2ceb..8ef0b0797872 100644 --- a/fs/sdcardfs/packagelist.c +++ b/fs/sdcardfs/packagelist.c @@ -21,6 +21,7 @@ #include "sdcardfs.h" #include #include +#include #include @@ -38,6 +39,8 @@ struct hashtable_entry { static DEFINE_HASHTABLE(package_to_appid, 8); static DEFINE_HASHTABLE(package_to_userid, 8); +static DEFINE_HASHTABLE(ext_to_groupid, 8); + static struct kmem_cache *hashtable_entry_cachep; @@ -53,15 +56,33 @@ static unsigned int str_hash(const char *key) { return h; } -appid_t get_appid(const char *app_name) +appid_t get_appid(const char *key) { struct hashtable_entry *hash_cur; - unsigned int hash = str_hash(app_name); + unsigned int hash = str_hash(key); appid_t ret_id; rcu_read_lock(); hash_for_each_possible_rcu(package_to_appid, hash_cur, hlist, hash) { - if (!strcasecmp(app_name, hash_cur->key)) { + if (!strcasecmp(key, hash_cur->key)) { + ret_id = atomic_read(&hash_cur->value); + rcu_read_unlock(); + return ret_id; + } + } + rcu_read_unlock(); + return 0; +} + +appid_t get_ext_gid(const char *key) +{ + struct hashtable_entry *hash_cur; + unsigned int hash = str_hash(key); + appid_t ret_id; + + rcu_read_lock(); + hash_for_each_possible_rcu(ext_to_groupid, hash_cur, hlist, hash) { + if (!strcasecmp(key, hash_cur->key)) { ret_id = atomic_read(&hash_cur->value); rcu_read_unlock(); return ret_id; @@ -90,8 +111,8 @@ appid_t is_excluded(const char *app_name, userid_t user) /* Kernel has already enforced everything we returned through * derive_permissions_locked(), so this is used to lock down access * even further, such as enforcing that apps hold sdcard_rw. */ -int check_caller_access_to_name(struct inode *parent_node, const char* name) { - +int check_caller_access_to_name(struct inode *parent_node, const char *name) +{ /* Always block security-sensitive files at root */ if (parent_node && SDCARDFS_I(parent_node)->perm == PERM_ROOT) { if (!strcasecmp(name, "autorun.inf") @@ -124,7 +145,7 @@ int open_flags_to_access_mode(int open_flags) { } } -static struct hashtable_entry *alloc_packagelist_entry(const char *key, +static struct hashtable_entry *alloc_hashtable_entry(const char *key, appid_t value) { struct hashtable_entry *ret = kmem_cache_alloc(hashtable_entry_cachep, @@ -154,13 +175,31 @@ static int insert_packagelist_appid_entry_locked(const char *key, appid_t value) return 0; } } - new_entry = alloc_packagelist_entry(key, value); + new_entry = alloc_hashtable_entry(key, value); if (!new_entry) return -ENOMEM; hash_add_rcu(package_to_appid, &new_entry->hlist, hash); return 0; } +static int insert_ext_gid_entry_locked(const char *key, appid_t value) +{ + struct hashtable_entry *hash_cur; + struct hashtable_entry *new_entry; + unsigned int hash = str_hash(key); + + /* An extension can only belong to one gid */ + hash_for_each_possible_rcu(ext_to_groupid, hash_cur, hlist, hash) { + if (!strcasecmp(key, hash_cur->key)) + return -EINVAL; + } + new_entry = alloc_hashtable_entry(key, value); + if (!new_entry) + return -ENOMEM; + hash_add_rcu(ext_to_groupid, &new_entry->hlist, hash); + return 0; +} + static int insert_userid_exclude_entry_locked(const char *key, userid_t value) { struct hashtable_entry *hash_cur; @@ -172,7 +211,7 @@ static int insert_userid_exclude_entry_locked(const char *key, userid_t value) if (atomic_read(&hash_cur->value) == value && !strcasecmp(key, hash_cur->key)) return 0; } - new_entry = alloc_packagelist_entry(key, value); + new_entry = alloc_hashtable_entry(key, value); if (!new_entry) return -ENOMEM; hash_add_rcu(package_to_userid, &new_entry->hlist, hash); @@ -234,6 +273,17 @@ static int insert_packagelist_entry(const char *key, appid_t value) return err; } +static int insert_ext_gid_entry(const char *key, appid_t value) +{ + int err; + + mutex_lock(&sdcardfs_super_list_lock); + err = insert_ext_gid_entry_locked(key, value); + mutex_unlock(&sdcardfs_super_list_lock); + + return err; +} + static int insert_userid_exclude_entry(const char *key, userid_t value) { int err; @@ -247,7 +297,7 @@ static int insert_userid_exclude_entry(const char *key, userid_t value) return err; } -static void free_packagelist_entry(struct hashtable_entry *entry) +static void free_hashtable_entry(struct hashtable_entry *entry) { kfree(entry->key); hash_del_rcu(&entry->dlist); @@ -276,7 +326,7 @@ static void remove_packagelist_entry_locked(const char *key) } synchronize_rcu(); hlist_for_each_entry_safe(hash_cur, h_t, &free_list, dlist) - free_packagelist_entry(hash_cur); + free_hashtable_entry(hash_cur); } static void remove_packagelist_entry(const char *key) @@ -288,6 +338,29 @@ static void remove_packagelist_entry(const char *key) return; } +static void remove_ext_gid_entry_locked(const char *key, gid_t group) +{ + struct hashtable_entry *hash_cur; + unsigned int hash = str_hash(key); + + hash_for_each_possible_rcu(ext_to_groupid, hash_cur, hlist, hash) { + if (!strcasecmp(key, hash_cur->key) && atomic_read(&hash_cur->value) == group) { + hash_del_rcu(&hash_cur->hlist); + synchronize_rcu(); + free_hashtable_entry(hash_cur); + break; + } + } +} + +static void remove_ext_gid_entry(const char *key, gid_t group) +{ + mutex_lock(&sdcardfs_super_list_lock); + remove_ext_gid_entry_locked(key, group); + mutex_unlock(&sdcardfs_super_list_lock); + return; +} + static void remove_userid_all_entry_locked(userid_t userid) { struct hashtable_entry *hash_cur; @@ -303,7 +376,7 @@ static void remove_userid_all_entry_locked(userid_t userid) } synchronize_rcu(); hlist_for_each_entry_safe(hash_cur, h_t, &free_list, dlist) { - free_packagelist_entry(hash_cur); + free_hashtable_entry(hash_cur); } } @@ -325,7 +398,7 @@ static void remove_userid_exclude_entry_locked(const char *key, userid_t userid) if (!strcasecmp(key, hash_cur->key) && atomic_read(&hash_cur->value) == userid) { hash_del_rcu(&hash_cur->hlist); synchronize_rcu(); - free_packagelist_entry(hash_cur); + free_hashtable_entry(hash_cur); break; } } @@ -357,7 +430,7 @@ static void packagelist_destroy(void) } synchronize_rcu(); hlist_for_each_entry_safe(hash_cur, h_t, &free_list, dlist) - free_packagelist_entry(hash_cur); + free_hashtable_entry(hash_cur); mutex_unlock(&sdcardfs_super_list_lock); printk(KERN_INFO "sdcardfs: destroyed packagelist pkgld\n"); } @@ -502,6 +575,127 @@ static struct config_item_type package_appid_type = { .ct_owner = THIS_MODULE, }; +struct extensions_value { + struct config_group group; + unsigned int num; +}; + +struct extension_details { + struct config_item item; + const char *name; + unsigned int num; +}; + +static inline struct extensions_value *to_extensions_value(struct config_item *item) +{ + return item ? container_of(to_config_group(item), struct extensions_value, group) : NULL; +} + +static inline struct extension_details *to_extension_details(struct config_item *item) +{ + return item ? container_of(item, struct extension_details, item) : NULL; +} + +static void extension_details_release(struct config_item *item) +{ + struct extension_details *extension_details = to_extension_details(item); + + printk(KERN_INFO "sdcardfs: No longer mapping %s files to gid %d\n", + extension_details->name, extension_details->num); + remove_ext_gid_entry(extension_details->name, extension_details->num); + kfree(extension_details->name); + kfree(extension_details); +} + +static struct configfs_item_operations extension_details_item_ops = { + .release = extension_details_release, +}; + +static struct config_item_type extension_details_type = { + .ct_item_ops = &extension_details_item_ops, + .ct_owner = THIS_MODULE, +}; + +static struct config_item *extension_details_make_item(struct config_group *group, const char *name) +{ + struct extensions_value *extensions_value = to_extensions_value(&group->cg_item); + struct extension_details *extension_details = kzalloc(sizeof(struct extension_details), GFP_KERNEL); + int ret; + if (!extension_details) + return ERR_PTR(-ENOMEM); + + extension_details->name = kstrdup(name, GFP_KERNEL); + if (!extension_details->name) { + kfree(extension_details); + return ERR_PTR(-ENOMEM); + } + extension_details->num = extensions_value->num; + ret = insert_ext_gid_entry(name, extensions_value->num); + + if (ret) { + kfree(extension_details->name); + kfree(extension_details); + return ERR_PTR(ret); + } + config_item_init_type_name(&extension_details->item, name, &extension_details_type); + + return &extension_details->item; +} + +static struct configfs_group_operations extensions_value_group_ops = { + .make_item = extension_details_make_item, +}; + +static struct config_item_type extensions_name_type = { + .ct_group_ops = &extensions_value_group_ops, + .ct_owner = THIS_MODULE, +}; + +static struct config_group *extensions_make_group(struct config_group *group, const char *name) +{ + struct extensions_value *extensions_value; + unsigned int tmp; + int ret; + + extensions_value = kzalloc(sizeof(struct extensions_value), GFP_KERNEL); + if (!extensions_value) + return ERR_PTR(-ENOMEM); + ret = kstrtouint(name, 10, &tmp); + if (ret) { + kfree(extensions_value); + return ERR_PTR(ret); + } + + extensions_value->num = tmp; + config_group_init_type_name(&extensions_value->group, name, + &extensions_name_type); + return &extensions_value->group; +} + +static void extensions_drop_group(struct config_group *group, struct config_item *item) +{ + struct extensions_value *value = to_extensions_value(item); + printk(KERN_INFO "sdcardfs: No longer mapping any files to gid %d\n", value->num); + kfree(value); +} + +static struct configfs_group_operations extensions_group_ops = { + .make_group = extensions_make_group, + .drop_item = extensions_drop_group, +}; + +static struct config_item_type extensions_type = { + .ct_group_ops = &extensions_group_ops, + .ct_owner = THIS_MODULE, +}; + +struct config_group extension_group = { + .cg_item = { + .ci_namebuf = "extensions", + .ci_type = &extensions_type, + }, +}; + static struct config_item *packages_make_item(struct config_group *group, const char *name) { struct package_details *package_details; @@ -595,6 +789,11 @@ static struct config_item_type packages_type = { .ct_owner = THIS_MODULE, }; +struct config_group *sd_default_groups[] = { + &extension_group, + NULL, +}; + static struct configfs_subsystem sdcardfs_packages = { .su_group = { .cg_item = { @@ -606,10 +805,14 @@ static struct configfs_subsystem sdcardfs_packages = { static int configfs_sdcardfs_init(void) { - int ret; + int ret, i; struct configfs_subsystem *subsys = &sdcardfs_packages; config_group_init(&subsys->su_group); + for (i = 0; sd_default_groups[i]; i++) { + config_group_init(sd_default_groups[i]); + configfs_add_default_group(sd_default_groups[i], &subsys->su_group); + } mutex_init(&subsys->su_mutex); ret = configfs_register_subsystem(subsys); if (ret) { diff --git a/fs/sdcardfs/sdcardfs.h b/fs/sdcardfs/sdcardfs.h index 9f287a65338c..87a57ce25785 100644 --- a/fs/sdcardfs/sdcardfs.h +++ b/fs/sdcardfs/sdcardfs.h @@ -65,6 +65,9 @@ #define AID_SDCARD_PICS 1033 /* external storage photos access */ #define AID_SDCARD_AV 1034 /* external storage audio/video access */ #define AID_SDCARD_ALL 1035 /* access all users external storage */ +#define AID_MEDIA_OBB 1059 /* obb files */ + +#define AID_SDCARD_IMAGE 1057 #define AID_PACKAGE_INFO 1027 @@ -91,13 +94,19 @@ * These two macro should be used in pair, and OVERRIDE_CRED() should be * placed at the beginning of a function, right after variable declaration. */ -#define OVERRIDE_CRED(sdcardfs_sbi, saved_cred) \ - saved_cred = override_fsids(sdcardfs_sbi); \ - if (!saved_cred) { return -ENOMEM; } +#define OVERRIDE_CRED(sdcardfs_sbi, saved_cred, info) \ + do { \ + saved_cred = override_fsids(sdcardfs_sbi, info); \ + if (!saved_cred) \ + return -ENOMEM; \ + } while (0) -#define OVERRIDE_CRED_PTR(sdcardfs_sbi, saved_cred) \ - saved_cred = override_fsids(sdcardfs_sbi); \ - if (!saved_cred) { return ERR_PTR(-ENOMEM); } +#define OVERRIDE_CRED_PTR(sdcardfs_sbi, saved_cred, info) \ + do { \ + saved_cred = override_fsids(sdcardfs_sbi, info); \ + if (!saved_cred) \ + return ERR_PTR(-ENOMEM); \ + } while (0) #define REVERT_CRED(saved_cred) revert_fsids(saved_cred) @@ -127,13 +136,18 @@ typedef enum { PERM_ANDROID_OBB, /* This node is "/Android/media" */ PERM_ANDROID_MEDIA, + /* This node is "/Android/[data|media|obb]/[package]" */ + PERM_ANDROID_PACKAGE, + /* This node is "/Android/[data|media|obb]/[package]/cache" */ + PERM_ANDROID_PACKAGE_CACHE, } perm_t; struct sdcardfs_sb_info; struct sdcardfs_mount_options; +struct sdcardfs_inode_info; /* Do not directly use this function. Use OVERRIDE_CRED() instead. */ -const struct cred * override_fsids(struct sdcardfs_sb_info* sbi); +const struct cred *override_fsids(struct sdcardfs_sb_info *sbi, struct sdcardfs_inode_info *info); /* Do not directly use this function, use REVERT_CRED() instead. */ void revert_fsids(const struct cred * old_cred); @@ -175,6 +189,8 @@ struct sdcardfs_inode_info { userid_t userid; uid_t d_uid; bool under_android; + bool under_cache; + bool under_obb; /* top folder for ownership */ struct inode *top; @@ -447,6 +463,7 @@ extern struct list_head sdcardfs_super_list; /* for packagelist.c */ extern appid_t get_appid(const char *app_name); +extern appid_t get_ext_gid(const char *app_name); extern appid_t is_excluded(const char *app_name, userid_t userid); extern int check_caller_access_to_name(struct inode *parent_node, const char* name); extern int open_flags_to_access_mode(int open_flags); @@ -466,11 +483,13 @@ struct limit_search { extern void setup_derived_state(struct inode *inode, perm_t perm, userid_t userid, uid_t uid, bool under_android, struct inode *top); extern void get_derived_permission(struct dentry *parent, struct dentry *dentry); -extern void get_derived_permission_new(struct dentry *parent, struct dentry *dentry, struct dentry *newdentry); +extern void get_derived_permission_new(struct dentry *parent, struct dentry *dentry, const char *name); +extern void drop_recursive(struct dentry *parent); extern void fixup_top_recursive(struct dentry *parent); extern void fixup_perms_recursive(struct dentry *dentry, struct limit_search *limit); extern void update_derived_permission_lock(struct dentry *dentry); +void fixup_lower_ownership(struct dentry *dentry, const char *name); extern int need_graft_path(struct dentry *dentry); extern int is_base_obbpath(struct dentry *dentry); extern int is_obbpath_invalid(struct dentry *dentry); From 062e28b172699b7666283f1e6bb1262093cbf1ba Mon Sep 17 00:00:00 2001 From: Daniel Rosenberg Date: Fri, 27 Jan 2017 19:35:08 -0800 Subject: [PATCH 0324/1103] ANDROID: sdcardfs: switch to full_name_hash and qstr Use the kernel's string hash function instead of rolling our own. Additionally, save a bit of calculation by using the qstr struct in place of strings. Signed-off-by: Daniel Rosenberg Change-Id: I0bbeb5ec2a9233f40135ad632e6f22c30ffa95c1 --- fs/sdcardfs/packagelist.c | 191 ++++++++++++++++++++++---------------- 1 file changed, 110 insertions(+), 81 deletions(-) diff --git a/fs/sdcardfs/packagelist.c b/fs/sdcardfs/packagelist.c index 8ef0b0797872..2b0040f95396 100644 --- a/fs/sdcardfs/packagelist.c +++ b/fs/sdcardfs/packagelist.c @@ -22,7 +22,7 @@ #include #include #include - +#include #include #include @@ -33,7 +33,7 @@ struct hashtable_entry { struct hlist_node hlist; struct hlist_node dlist; /* for deletion cleanup */ - const char *key; + struct qstr key; atomic_t value; }; @@ -44,27 +44,30 @@ static DEFINE_HASHTABLE(ext_to_groupid, 8); static struct kmem_cache *hashtable_entry_cachep; -static unsigned int str_hash(const char *key) { - int i; - unsigned int h = strlen(key); - char *data = (char *)key; +static inline void qstr_init(struct qstr *q, const char *name) +{ + q->name = name; + q->len = strlen(q->name); + q->hash = full_name_hash(0, q->name, q->len); +} - for (i = 0; i < strlen(key); i++) { - h = h * 31 + *data; - data++; - } - return h; +static inline int qstr_copy(const struct qstr *src, struct qstr *dest) +{ + dest->name = kstrdup(src->name, GFP_KERNEL); + dest->hash_len = src->hash_len; + return !!dest->name; } -appid_t get_appid(const char *key) + +static appid_t __get_appid(const struct qstr *key) { struct hashtable_entry *hash_cur; - unsigned int hash = str_hash(key); + unsigned int hash = key->hash; appid_t ret_id; rcu_read_lock(); hash_for_each_possible_rcu(package_to_appid, hash_cur, hlist, hash) { - if (!strcasecmp(key, hash_cur->key)) { + if (!strcasecmp(key->name, hash_cur->key.name)) { ret_id = atomic_read(&hash_cur->value); rcu_read_unlock(); return ret_id; @@ -74,15 +77,22 @@ appid_t get_appid(const char *key) return 0; } -appid_t get_ext_gid(const char *key) +appid_t get_appid(const char *key) +{ + struct qstr q; + qstr_init(&q, key); + return __get_appid(&q); +} + +static appid_t __get_ext_gid(const struct qstr *key) { struct hashtable_entry *hash_cur; - unsigned int hash = str_hash(key); + unsigned int hash = key->hash; appid_t ret_id; rcu_read_lock(); hash_for_each_possible_rcu(ext_to_groupid, hash_cur, hlist, hash) { - if (!strcasecmp(key, hash_cur->key)) { + if (!strcasecmp(key->name, hash_cur->key.name)) { ret_id = atomic_read(&hash_cur->value); rcu_read_unlock(); return ret_id; @@ -92,14 +102,22 @@ appid_t get_ext_gid(const char *key) return 0; } -appid_t is_excluded(const char *app_name, userid_t user) +appid_t get_ext_gid(const char *key) +{ + struct qstr q; + qstr_init(&q, key); + return __get_ext_gid(&q); +} + +static appid_t __is_excluded(const struct qstr *app_name, userid_t user) { struct hashtable_entry *hash_cur; - unsigned int hash = str_hash(app_name); + unsigned int hash = app_name->hash; rcu_read_lock(); hash_for_each_possible_rcu(package_to_userid, hash_cur, hlist, hash) { - if (atomic_read(&hash_cur->value) == user && !strcasecmp(app_name, hash_cur->key)) { + if (atomic_read(&hash_cur->value) == user && + !strcasecmp(app_name->name, hash_cur->key.name)) { rcu_read_unlock(); return 1; } @@ -108,6 +126,14 @@ appid_t is_excluded(const char *app_name, userid_t user) return 0; } +appid_t is_excluded(const char *app_name, userid_t user) +{ + struct qstr q; + qstr_init(&q, app_name); + return __is_excluded(&q, user); +} + + /* Kernel has already enforced everything we returned through * derive_permissions_locked(), so this is used to lock down access * even further, such as enforcing that apps hold sdcard_rw. */ @@ -145,7 +171,7 @@ int open_flags_to_access_mode(int open_flags) { } } -static struct hashtable_entry *alloc_hashtable_entry(const char *key, +static struct hashtable_entry *alloc_hashtable_entry(const struct qstr *key, appid_t value) { struct hashtable_entry *ret = kmem_cache_alloc(hashtable_entry_cachep, @@ -153,8 +179,7 @@ static struct hashtable_entry *alloc_hashtable_entry(const char *key, if (!ret) return NULL; - ret->key = kstrdup(key, GFP_KERNEL); - if (!ret->key) { + if (!qstr_copy(key, &ret->key)) { kmem_cache_free(hashtable_entry_cachep, ret); return NULL; } @@ -163,14 +188,14 @@ static struct hashtable_entry *alloc_hashtable_entry(const char *key, return ret; } -static int insert_packagelist_appid_entry_locked(const char *key, appid_t value) +static int insert_packagelist_appid_entry_locked(const struct qstr *key, appid_t value) { struct hashtable_entry *hash_cur; struct hashtable_entry *new_entry; - unsigned int hash = str_hash(key); + unsigned int hash = key->hash; hash_for_each_possible_rcu(package_to_appid, hash_cur, hlist, hash) { - if (!strcasecmp(key, hash_cur->key)) { + if (!strcasecmp(key->name, hash_cur->key.name)) { atomic_set(&hash_cur->value, value); return 0; } @@ -182,15 +207,15 @@ static int insert_packagelist_appid_entry_locked(const char *key, appid_t value) return 0; } -static int insert_ext_gid_entry_locked(const char *key, appid_t value) +static int insert_ext_gid_entry_locked(const struct qstr *key, appid_t value) { struct hashtable_entry *hash_cur; struct hashtable_entry *new_entry; - unsigned int hash = str_hash(key); + unsigned int hash = key->hash; /* An extension can only belong to one gid */ hash_for_each_possible_rcu(ext_to_groupid, hash_cur, hlist, hash) { - if (!strcasecmp(key, hash_cur->key)) + if (!strcasecmp(key->name, hash_cur->key.name)) return -EINVAL; } new_entry = alloc_hashtable_entry(key, value); @@ -200,15 +225,16 @@ static int insert_ext_gid_entry_locked(const char *key, appid_t value) return 0; } -static int insert_userid_exclude_entry_locked(const char *key, userid_t value) +static int insert_userid_exclude_entry_locked(const struct qstr *key, userid_t value) { struct hashtable_entry *hash_cur; struct hashtable_entry *new_entry; - unsigned int hash = str_hash(key); + unsigned int hash = key->hash; /* Only insert if not already present */ hash_for_each_possible_rcu(package_to_userid, hash_cur, hlist, hash) { - if (atomic_read(&hash_cur->value) == value && !strcasecmp(key, hash_cur->key)) + if (atomic_read(&hash_cur->value) == value && + !strcasecmp(key->name, hash_cur->key.name)) return 0; } new_entry = alloc_hashtable_entry(key, value); @@ -218,13 +244,13 @@ static int insert_userid_exclude_entry_locked(const char *key, userid_t value) return 0; } -static void fixup_all_perms_name(const char *key) +static void fixup_all_perms_name(const struct qstr *key) { struct sdcardfs_sb_info *sbinfo; struct limit_search limit = { .flags = BY_NAME, - .name = key, - .length = strlen(key), + .name = key->name, + .length = key->len, }; list_for_each_entry(sbinfo, &sdcardfs_super_list, list) { if (sbinfo_has_sdcard_magic(sbinfo)) @@ -232,13 +258,13 @@ static void fixup_all_perms_name(const char *key) } } -static void fixup_all_perms_name_userid(const char *key, userid_t userid) +static void fixup_all_perms_name_userid(const struct qstr *key, userid_t userid) { struct sdcardfs_sb_info *sbinfo; struct limit_search limit = { .flags = BY_NAME | BY_USERID, - .name = key, - .length = strlen(key), + .name = key->name, + .length = key->len, .userid = userid, }; list_for_each_entry(sbinfo, &sdcardfs_super_list, list) { @@ -260,7 +286,7 @@ static void fixup_all_perms_userid(userid_t userid) } } -static int insert_packagelist_entry(const char *key, appid_t value) +static int insert_packagelist_entry(const struct qstr *key, appid_t value) { int err; @@ -273,7 +299,7 @@ static int insert_packagelist_entry(const char *key, appid_t value) return err; } -static int insert_ext_gid_entry(const char *key, appid_t value) +static int insert_ext_gid_entry(const struct qstr *key, appid_t value) { int err; @@ -284,7 +310,7 @@ static int insert_ext_gid_entry(const char *key, appid_t value) return err; } -static int insert_userid_exclude_entry(const char *key, userid_t value) +static int insert_userid_exclude_entry(const struct qstr *key, userid_t value) { int err; @@ -299,26 +325,26 @@ static int insert_userid_exclude_entry(const char *key, userid_t value) static void free_hashtable_entry(struct hashtable_entry *entry) { - kfree(entry->key); + kfree(entry->key.name); hash_del_rcu(&entry->dlist); kmem_cache_free(hashtable_entry_cachep, entry); } -static void remove_packagelist_entry_locked(const char *key) +static void remove_packagelist_entry_locked(const struct qstr *key) { struct hashtable_entry *hash_cur; - unsigned int hash = str_hash(key); + unsigned int hash = key->hash; struct hlist_node *h_t; HLIST_HEAD(free_list); hash_for_each_possible_rcu(package_to_userid, hash_cur, hlist, hash) { - if (!strcasecmp(key, hash_cur->key)) { + if (!strcasecmp(key->name, hash_cur->key.name)) { hash_del_rcu(&hash_cur->hlist); hlist_add_head(&hash_cur->dlist, &free_list); } } hash_for_each_possible_rcu(package_to_appid, hash_cur, hlist, hash) { - if (!strcasecmp(key, hash_cur->key)) { + if (!strcasecmp(key->name, hash_cur->key.name)) { hash_del_rcu(&hash_cur->hlist); hlist_add_head(&hash_cur->dlist, &free_list); break; @@ -329,7 +355,7 @@ static void remove_packagelist_entry_locked(const char *key) free_hashtable_entry(hash_cur); } -static void remove_packagelist_entry(const char *key) +static void remove_packagelist_entry(const struct qstr *key) { mutex_lock(&sdcardfs_super_list_lock); remove_packagelist_entry_locked(key); @@ -338,13 +364,13 @@ static void remove_packagelist_entry(const char *key) return; } -static void remove_ext_gid_entry_locked(const char *key, gid_t group) +static void remove_ext_gid_entry_locked(const struct qstr *key, gid_t group) { struct hashtable_entry *hash_cur; - unsigned int hash = str_hash(key); + unsigned int hash = key->hash; hash_for_each_possible_rcu(ext_to_groupid, hash_cur, hlist, hash) { - if (!strcasecmp(key, hash_cur->key) && atomic_read(&hash_cur->value) == group) { + if (!strcasecmp(key->name, hash_cur->key.name) && atomic_read(&hash_cur->value) == group) { hash_del_rcu(&hash_cur->hlist); synchronize_rcu(); free_hashtable_entry(hash_cur); @@ -353,7 +379,7 @@ static void remove_ext_gid_entry_locked(const char *key, gid_t group) } } -static void remove_ext_gid_entry(const char *key, gid_t group) +static void remove_ext_gid_entry(const struct qstr *key, gid_t group) { mutex_lock(&sdcardfs_super_list_lock); remove_ext_gid_entry_locked(key, group); @@ -389,13 +415,14 @@ static void remove_userid_all_entry(userid_t userid) return; } -static void remove_userid_exclude_entry_locked(const char *key, userid_t userid) +static void remove_userid_exclude_entry_locked(const struct qstr *key, userid_t userid) { struct hashtable_entry *hash_cur; - unsigned int hash = str_hash(key); + unsigned int hash = key->hash; hash_for_each_possible_rcu(package_to_userid, hash_cur, hlist, hash) { - if (!strcasecmp(key, hash_cur->key) && atomic_read(&hash_cur->value) == userid) { + if (!strcasecmp(key->name, hash_cur->key.name) && + atomic_read(&hash_cur->value) == userid) { hash_del_rcu(&hash_cur->hlist); synchronize_rcu(); free_hashtable_entry(hash_cur); @@ -404,7 +431,7 @@ static void remove_userid_exclude_entry_locked(const char *key, userid_t userid) } } -static void remove_userid_exclude_entry(const char *key, userid_t userid) +static void remove_userid_exclude_entry(const struct qstr *key, userid_t userid) { mutex_lock(&sdcardfs_super_list_lock); remove_userid_exclude_entry_locked(key, userid); @@ -462,7 +489,7 @@ static struct configfs_attribute _pfx##attr_##_name = { \ struct package_details { struct config_item item; - const char *name; + struct qstr name; }; static inline struct package_details *to_package_details(struct config_item *item) @@ -472,7 +499,7 @@ static inline struct package_details *to_package_details(struct config_item *ite static ssize_t package_details_appid_show(struct config_item *item, char *page) { - return scnprintf(page, PAGE_SIZE, "%u\n", get_appid(to_package_details(item)->name)); + return scnprintf(page, PAGE_SIZE, "%u\n", __get_appid(&to_package_details(item)->name)); } static ssize_t package_details_appid_store(struct config_item *item, @@ -485,7 +512,7 @@ static ssize_t package_details_appid_store(struct config_item *item, if (ret) return ret; - ret = insert_packagelist_entry(to_package_details(item)->name, tmp); + ret = insert_packagelist_entry(&to_package_details(item)->name, tmp); if (ret) return ret; @@ -498,12 +525,12 @@ static ssize_t package_details_excluded_userids_show(struct config_item *item, { struct package_details *package_details = to_package_details(item); struct hashtable_entry *hash_cur; - unsigned int hash = str_hash(package_details->name); + unsigned int hash = package_details->name.hash; int count = 0; rcu_read_lock(); hash_for_each_possible_rcu(package_to_userid, hash_cur, hlist, hash) { - if (!strcasecmp(package_details->name, hash_cur->key)) + if (!strcasecmp(package_details->name.name, hash_cur->key.name)) count += scnprintf(page + count, PAGE_SIZE - count, "%d ", atomic_read(&hash_cur->value)); } @@ -524,7 +551,7 @@ static ssize_t package_details_excluded_userids_store(struct config_item *item, if (ret) return ret; - ret = insert_userid_exclude_entry(to_package_details(item)->name, tmp); + ret = insert_userid_exclude_entry(&to_package_details(item)->name, tmp); if (ret) return ret; @@ -541,16 +568,16 @@ static ssize_t package_details_clear_userid_store(struct config_item *item, ret = kstrtouint(page, 10, &tmp); if (ret) return ret; - remove_userid_exclude_entry(to_package_details(item)->name, tmp); + remove_userid_exclude_entry(&to_package_details(item)->name, tmp); return count; } static void package_details_release(struct config_item *item) { struct package_details *package_details = to_package_details(item); - printk(KERN_INFO "sdcardfs: removing %s\n", package_details->name); - remove_packagelist_entry(package_details->name); - kfree(package_details->name); + printk(KERN_INFO "sdcardfs: removing %s\n", package_details->name.name); + remove_packagelist_entry(&package_details->name); + kfree(package_details->name.name); kfree(package_details); } @@ -582,7 +609,7 @@ struct extensions_value { struct extension_details { struct config_item item; - const char *name; + struct qstr name; unsigned int num; }; @@ -601,9 +628,9 @@ static void extension_details_release(struct config_item *item) struct extension_details *extension_details = to_extension_details(item); printk(KERN_INFO "sdcardfs: No longer mapping %s files to gid %d\n", - extension_details->name, extension_details->num); - remove_ext_gid_entry(extension_details->name, extension_details->num); - kfree(extension_details->name); + extension_details->name.name, extension_details->num); + remove_ext_gid_entry(&extension_details->name, extension_details->num); + kfree(extension_details->name.name); kfree(extension_details); } @@ -620,20 +647,21 @@ static struct config_item *extension_details_make_item(struct config_group *grou { struct extensions_value *extensions_value = to_extensions_value(&group->cg_item); struct extension_details *extension_details = kzalloc(sizeof(struct extension_details), GFP_KERNEL); + const char *tmp; int ret; if (!extension_details) return ERR_PTR(-ENOMEM); - extension_details->name = kstrdup(name, GFP_KERNEL); - if (!extension_details->name) { + tmp = kstrdup(name, GFP_KERNEL); + if (!tmp) { kfree(extension_details); return ERR_PTR(-ENOMEM); } - extension_details->num = extensions_value->num; - ret = insert_ext_gid_entry(name, extensions_value->num); + qstr_init(&extension_details->name, tmp); + ret = insert_ext_gid_entry(&extension_details->name, extensions_value->num); if (ret) { - kfree(extension_details->name); + kfree(extension_details->name.name); kfree(extension_details); return ERR_PTR(ret); } @@ -699,16 +727,17 @@ struct config_group extension_group = { static struct config_item *packages_make_item(struct config_group *group, const char *name) { struct package_details *package_details; + const char *tmp; package_details = kzalloc(sizeof(struct package_details), GFP_KERNEL); if (!package_details) return ERR_PTR(-ENOMEM); - package_details->name = kstrdup(name, GFP_KERNEL); - if (!package_details->name) { + tmp = kstrdup(name, GFP_KERNEL); + if (!tmp) { kfree(package_details); return ERR_PTR(-ENOMEM); } - + qstr_init(&package_details->name, tmp); config_item_init_type_name(&package_details->item, name, &package_appid_type); @@ -727,13 +756,13 @@ static ssize_t packages_list_show(struct config_item *item, char *page) rcu_read_lock(); hash_for_each_rcu(package_to_appid, i, hash_cur_app, hlist) { written = scnprintf(page + count, PAGE_SIZE - sizeof(errormsg) - count, "%s %d\n", - hash_cur_app->key, atomic_read(&hash_cur_app->value)); - hash = str_hash(hash_cur_app->key); + hash_cur_app->key.name, atomic_read(&hash_cur_app->value)); + hash = hash_cur_app->key.hash; hash_for_each_possible_rcu(package_to_userid, hash_cur_user, hlist, hash) { - if (!strcasecmp(hash_cur_app->key, hash_cur_user->key)) { + if (!strcasecmp(hash_cur_app->key.name, hash_cur_user->key.name)) { written += scnprintf(page + count + written - 1, PAGE_SIZE - sizeof(errormsg) - count - written + 1, - " %d\n", atomic_read(&hash_cur_user->value)) - 1; + " %d\n", atomic_read(&hash_cur_user->value)) - 1; } } if (count + written == PAGE_SIZE - sizeof(errormsg) - 1) { From fa01f0f7e9148069b0e7434f5dd0c8e45c4447d3 Mon Sep 17 00:00:00 2001 From: Daniel Rosenberg Date: Tue, 31 Jan 2017 20:07:51 -0800 Subject: [PATCH 0325/1103] ANDROID: sdcardfs: Switch strcasecmp for internal call This moves our uses of strcasecmp over to an internal call so we can easily change implementations later if we so desire. Additionally, we leverage qstr's where appropriate to save time on comparisons. Signed-off-by: Daniel Rosenberg Change-Id: I32fdc4fd0cd3b7b735dcfd82f60a2516fd8272a5 --- fs/sdcardfs/derived_perm.c | 35 +++++++++++++++++++------------- fs/sdcardfs/file.c | 2 +- fs/sdcardfs/inode.c | 24 ++++++++++++---------- fs/sdcardfs/lookup.c | 18 +++++++---------- fs/sdcardfs/packagelist.c | 41 ++++++++++++++++++++------------------ fs/sdcardfs/sdcardfs.h | 17 ++++++++++++++-- 6 files changed, 79 insertions(+), 58 deletions(-) diff --git a/fs/sdcardfs/derived_perm.c b/fs/sdcardfs/derived_perm.c index 16f05172f9bf..4b3801885959 100644 --- a/fs/sdcardfs/derived_perm.c +++ b/fs/sdcardfs/derived_perm.c @@ -51,11 +51,16 @@ void setup_derived_state(struct inode *inode, perm_t perm, userid_t userid, } /* While renaming, there is a point where we want the path from dentry, but the name from newdentry */ -void get_derived_permission_new(struct dentry *parent, struct dentry *dentry, const char *name) +void get_derived_permission_new(struct dentry *parent, struct dentry *dentry, const struct qstr *name) { struct sdcardfs_inode_info *info = SDCARDFS_I(d_inode(dentry)); struct sdcardfs_inode_info *parent_info= SDCARDFS_I(d_inode(parent)); appid_t appid; + struct qstr q_Android = QSTR_LITERAL("Android"); + struct qstr q_data = QSTR_LITERAL("data"); + struct qstr q_obb = QSTR_LITERAL("obb"); + struct qstr q_media = QSTR_LITERAL("media"); + struct qstr q_cache = QSTR_LITERAL("cache"); /* By default, each inode inherits from its parent. * the properties are maintained on its private fields @@ -79,12 +84,12 @@ void get_derived_permission_new(struct dentry *parent, struct dentry *dentry, co case PERM_PRE_ROOT: /* Legacy internal layout places users at top level */ info->perm = PERM_ROOT; - info->userid = simple_strtoul(name, NULL, 10); + info->userid = simple_strtoul(name->name, NULL, 10); set_top(info, &info->vfs_inode); break; case PERM_ROOT: /* Assume masked off by default. */ - if (!strcasecmp(name, "Android")) { + if (qstr_case_eq(name, &q_Android)) { /* App-specific directories inside; let anyone traverse */ info->perm = PERM_ANDROID; info->under_android = true; @@ -92,17 +97,17 @@ void get_derived_permission_new(struct dentry *parent, struct dentry *dentry, co } break; case PERM_ANDROID: - if (!strcasecmp(name, "data")) { + if (qstr_case_eq(name, &q_data)) { /* App-specific directories inside; let anyone traverse */ info->perm = PERM_ANDROID_DATA; set_top(info, &info->vfs_inode); - } else if (!strcasecmp(name, "obb")) { + } else if (qstr_case_eq(name, &q_obb)) { /* App-specific directories inside; let anyone traverse */ info->perm = PERM_ANDROID_OBB; info->under_obb = true; set_top(info, &info->vfs_inode); /* Single OBB directory is always shared */ - } else if (!strcasecmp(name, "media")) { + } else if (qstr_case_eq(name, &q_media)) { /* App-specific directories inside; let anyone traverse */ info->perm = PERM_ANDROID_MEDIA; set_top(info, &info->vfs_inode); @@ -112,14 +117,14 @@ void get_derived_permission_new(struct dentry *parent, struct dentry *dentry, co case PERM_ANDROID_DATA: case PERM_ANDROID_MEDIA: info->perm = PERM_ANDROID_PACKAGE; - appid = get_appid(name); - if (appid != 0 && !is_excluded(name, parent_info->userid)) { + appid = get_appid(name->name); + if (appid != 0 && !is_excluded(name->name, parent_info->userid)) { info->d_uid = multiuser_get_uid(parent_info->userid, appid); } set_top(info, &info->vfs_inode); break; case PERM_ANDROID_PACKAGE: - if (!strcasecmp(name, "cache")) { + if (qstr_case_eq(name, &q_cache)) { info->perm = PERM_ANDROID_PACKAGE_CACHE; info->under_cache = true; } @@ -129,7 +134,7 @@ void get_derived_permission_new(struct dentry *parent, struct dentry *dentry, co void get_derived_permission(struct dentry *parent, struct dentry *dentry) { - get_derived_permission_new(parent, dentry, dentry->d_name.name); + get_derived_permission_new(parent, dentry, &dentry->d_name); } static appid_t get_type(const char *name) @@ -366,9 +371,10 @@ int need_graft_path(struct dentry *dentry) struct dentry *parent = dget_parent(dentry); struct sdcardfs_inode_info *parent_info= SDCARDFS_I(d_inode(parent)); struct sdcardfs_sb_info *sbi = SDCARDFS_SB(dentry->d_sb); + struct qstr obb = QSTR_LITERAL("obb"); if(parent_info->perm == PERM_ANDROID && - !strcasecmp(dentry->d_name.name, "obb")) { + qstr_case_eq(&dentry->d_name, &obb)) { /* /Android/obb is the base obbpath of DERIVED_UNIFIED */ if(!(sbi->options.multiuser == false @@ -405,7 +411,7 @@ int is_obbpath_invalid(struct dentry *dent) } else { obbpath_s = d_path(&di->lower_path, path_buf, PATH_MAX); if (d_unhashed(di->lower_path.dentry) || - strcasecmp(sbi->obbpath_s, obbpath_s)) { + !str_case_eq(sbi->obbpath_s, obbpath_s)) { ret = 1; } kfree(path_buf); @@ -425,15 +431,16 @@ int is_base_obbpath(struct dentry *dentry) struct dentry *parent = dget_parent(dentry); struct sdcardfs_inode_info *parent_info= SDCARDFS_I(d_inode(parent)); struct sdcardfs_sb_info *sbi = SDCARDFS_SB(dentry->d_sb); + struct qstr q_obb = QSTR_LITERAL("obb"); spin_lock(&SDCARDFS_D(dentry)->lock); if (sbi->options.multiuser) { if(parent_info->perm == PERM_PRE_ROOT && - !strcasecmp(dentry->d_name.name, "obb")) { + qstr_case_eq(&dentry->d_name, &q_obb)) { ret = 1; } } else if (parent_info->perm == PERM_ANDROID && - !strcasecmp(dentry->d_name.name, "obb")) { + qstr_case_eq(&dentry->d_name, &q_obb)) { ret = 1; } spin_unlock(&SDCARDFS_D(dentry)->lock); diff --git a/fs/sdcardfs/file.c b/fs/sdcardfs/file.c index 006c6ff57ad7..56000a004b38 100644 --- a/fs/sdcardfs/file.c +++ b/fs/sdcardfs/file.c @@ -216,7 +216,7 @@ static int sdcardfs_open(struct inode *inode, struct file *file) goto out_err; } - if(!check_caller_access_to_name(d_inode(parent), dentry->d_name.name)) { + if (!check_caller_access_to_name(d_inode(parent), &dentry->d_name)) { printk(KERN_INFO "%s: need to check the caller's gid in packages.list\n" " dentry: %s, task:%s\n", __func__, dentry->d_name.name, current->comm); diff --git a/fs/sdcardfs/inode.c b/fs/sdcardfs/inode.c index 103558987362..4646dbb3ee03 100644 --- a/fs/sdcardfs/inode.c +++ b/fs/sdcardfs/inode.c @@ -66,7 +66,7 @@ static int sdcardfs_create(struct inode *dir, struct dentry *dentry, struct fs_struct *saved_fs; struct fs_struct *copied_fs; - if(!check_caller_access_to_name(dir, dentry->d_name.name)) { + if (!check_caller_access_to_name(dir, &dentry->d_name)) { printk(KERN_INFO "%s: need to check the caller's gid in packages.list\n" " dentry: %s, task:%s\n", __func__, dentry->d_name.name, current->comm); @@ -168,7 +168,7 @@ static int sdcardfs_unlink(struct inode *dir, struct dentry *dentry) struct path lower_path; const struct cred *saved_cred = NULL; - if(!check_caller_access_to_name(dir, dentry->d_name.name)) { + if (!check_caller_access_to_name(dir, &dentry->d_name)) { printk(KERN_INFO "%s: need to check the caller's gid in packages.list\n" " dentry: %s, task:%s\n", __func__, dentry->d_name.name, current->comm); @@ -275,8 +275,10 @@ static int sdcardfs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode int touch_err = 0; struct fs_struct *saved_fs; struct fs_struct *copied_fs; + struct qstr q_obb = QSTR_LITERAL("obb"); + struct qstr q_data = QSTR_LITERAL("data"); - if(!check_caller_access_to_name(dir, dentry->d_name.name)) { + if (!check_caller_access_to_name(dir, &dentry->d_name)) { printk(KERN_INFO "%s: need to check the caller's gid in packages.list\n" " dentry: %s, task:%s\n", __func__, dentry->d_name.name, current->comm); @@ -351,13 +353,13 @@ static int sdcardfs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode set_nlink(dir, sdcardfs_lower_inode(dir)->i_nlink); fixup_lower_ownership(dentry, dentry->d_name.name); unlock_dir(lower_parent_dentry); - if ((!sbi->options.multiuser) && (!strcasecmp(dentry->d_name.name, "obb")) + if ((!sbi->options.multiuser) && (qstr_case_eq(&dentry->d_name, &q_obb)) && (pi->perm == PERM_ANDROID) && (pi->userid == 0)) make_nomedia_in_obb = 1; /* When creating /Android/data and /Android/obb, mark them as .nomedia */ if (make_nomedia_in_obb || - ((pi->perm == PERM_ANDROID) && (!strcasecmp(dentry->d_name.name, "data")))) { + ((pi->perm == PERM_ANDROID) && (qstr_case_eq(&dentry->d_name, &q_data)))) { REVERT_CRED(saved_cred); OVERRIDE_CRED(SDCARDFS_SB(dir->i_sb), saved_cred, SDCARDFS_I(d_inode(dentry))); set_fs_pwd(current->fs, &lower_path); @@ -388,7 +390,7 @@ static int sdcardfs_rmdir(struct inode *dir, struct dentry *dentry) struct path lower_path; const struct cred *saved_cred = NULL; - if(!check_caller_access_to_name(dir, dentry->d_name.name)) { + if (!check_caller_access_to_name(dir, &dentry->d_name)) { printk(KERN_INFO "%s: need to check the caller's gid in packages.list\n" " dentry: %s, task:%s\n", __func__, dentry->d_name.name, current->comm); @@ -480,8 +482,8 @@ static int sdcardfs_rename(struct inode *old_dir, struct dentry *old_dentry, if (flags) return -EINVAL; - if(!check_caller_access_to_name(old_dir, old_dentry->d_name.name) || - !check_caller_access_to_name(new_dir, new_dentry->d_name.name)) { + if (!check_caller_access_to_name(old_dir, &old_dentry->d_name) || + !check_caller_access_to_name(new_dir, &new_dentry->d_name)) { printk(KERN_INFO "%s: need to check the caller's gid in packages.list\n" " new_dentry: %s, task:%s\n", __func__, new_dentry->d_name.name, current->comm); @@ -527,7 +529,7 @@ static int sdcardfs_rename(struct inode *old_dir, struct dentry *old_dentry, sdcardfs_copy_and_fix_attrs(old_dir, d_inode(lower_old_dir_dentry)); fsstack_copy_inode_size(old_dir, d_inode(lower_old_dir_dentry)); } - get_derived_permission_new(new_dentry->d_parent, old_dentry, new_dentry->d_name.name); + get_derived_permission_new(new_dentry->d_parent, old_dentry, &new_dentry->d_name); fixup_tmp_permissions(d_inode(old_dentry)); fixup_lower_ownership(old_dentry, new_dentry->d_name.name); drop_recursive(old_dentry); /* Can't fixup ownership recursively :( */ @@ -752,7 +754,7 @@ static int sdcardfs_setattr(struct vfsmount *mnt, struct dentry *dentry, struct if (!err) { /* check the Android group ID */ parent = dget_parent(dentry); - if(!check_caller_access_to_name(d_inode(parent), dentry->d_name.name)) { + if (!check_caller_access_to_name(d_inode(parent), &dentry->d_name)) { printk(KERN_INFO "%s: need to check the caller's gid in packages.list\n" " dentry: %s, task:%s\n", __func__, dentry->d_name.name, current->comm); @@ -870,7 +872,7 @@ static int sdcardfs_getattr(struct vfsmount *mnt, struct dentry *dentry, int err; parent = dget_parent(dentry); - if(!check_caller_access_to_name(d_inode(parent), dentry->d_name.name)) { + if (!check_caller_access_to_name(d_inode(parent), &dentry->d_name)) { printk(KERN_INFO "%s: need to check the caller's gid in packages.list\n" " dentry: %s, task:%s\n", __func__, dentry->d_name.name, current->comm); diff --git a/fs/sdcardfs/lookup.c b/fs/sdcardfs/lookup.c index 1d664c9e2942..0d2caddd8510 100644 --- a/fs/sdcardfs/lookup.c +++ b/fs/sdcardfs/lookup.c @@ -219,9 +219,8 @@ static struct dentry *__sdcardfs_lookup(struct dentry *dentry, struct vfsmount *lower_dir_mnt; struct dentry *lower_dir_dentry = NULL; struct dentry *lower_dentry; - const char *name; + const struct qstr *name; struct path lower_path; - struct qstr this; struct sdcardfs_sb_info *sbi; sbi = SDCARDFS_SB(dentry->d_sb); @@ -231,14 +230,14 @@ static struct dentry *__sdcardfs_lookup(struct dentry *dentry, if (IS_ROOT(dentry)) goto out; - name = dentry->d_name.name; + name = &dentry->d_name; /* now start the actual lookup procedure */ lower_dir_dentry = lower_parent_path->dentry; lower_dir_mnt = lower_parent_path->mnt; /* Use vfs_path_lookup to check if the dentry exists or not */ - err = vfs_path_lookup(lower_dir_dentry, lower_dir_mnt, name, 0, + err = vfs_path_lookup(lower_dir_dentry, lower_dir_mnt, name->name, 0, &lower_path); /* check for other cases */ if (err == -ENOENT) { @@ -248,7 +247,7 @@ static struct dentry *__sdcardfs_lookup(struct dentry *dentry, spin_lock(&lower_dir_dentry->d_lock); list_for_each_entry(child, &lower_dir_dentry->d_subdirs, d_child) { if (child && d_inode(child)) { - if (strcasecmp(child->d_name.name, name)==0) { + if (qstr_case_eq(&child->d_name, name)) { match = dget(child); break; } @@ -307,14 +306,11 @@ static struct dentry *__sdcardfs_lookup(struct dentry *dentry, goto out; /* instatiate a new negative dentry */ - this.name = name; - this.len = strlen(name); - this.hash = full_name_hash(dentry, this.name, this.len); - lower_dentry = d_lookup(lower_dir_dentry, &this); + lower_dentry = d_lookup(lower_dir_dentry, name); if (lower_dentry) goto setup_lower; - lower_dentry = d_alloc(lower_dir_dentry, &this); + lower_dentry = d_alloc(lower_dir_dentry, name); if (!lower_dentry) { err = -ENOMEM; goto out; @@ -359,7 +355,7 @@ struct dentry *sdcardfs_lookup(struct inode *dir, struct dentry *dentry, parent = dget_parent(dentry); - if(!check_caller_access_to_name(d_inode(parent), dentry->d_name.name)) { + if (!check_caller_access_to_name(d_inode(parent), &dentry->d_name)) { ret = ERR_PTR(-EACCES); printk(KERN_INFO "%s: need to check the caller's gid in packages.list\n" " dentry: %s, task:%s\n", diff --git a/fs/sdcardfs/packagelist.c b/fs/sdcardfs/packagelist.c index 2b0040f95396..537ff523c32a 100644 --- a/fs/sdcardfs/packagelist.c +++ b/fs/sdcardfs/packagelist.c @@ -67,7 +67,7 @@ static appid_t __get_appid(const struct qstr *key) rcu_read_lock(); hash_for_each_possible_rcu(package_to_appid, hash_cur, hlist, hash) { - if (!strcasecmp(key->name, hash_cur->key.name)) { + if (qstr_case_eq(key, &hash_cur->key)) { ret_id = atomic_read(&hash_cur->value); rcu_read_unlock(); return ret_id; @@ -92,7 +92,7 @@ static appid_t __get_ext_gid(const struct qstr *key) rcu_read_lock(); hash_for_each_possible_rcu(ext_to_groupid, hash_cur, hlist, hash) { - if (!strcasecmp(key->name, hash_cur->key.name)) { + if (qstr_case_eq(key, &hash_cur->key)) { ret_id = atomic_read(&hash_cur->value); rcu_read_unlock(); return ret_id; @@ -117,7 +117,7 @@ static appid_t __is_excluded(const struct qstr *app_name, userid_t user) rcu_read_lock(); hash_for_each_possible_rcu(package_to_userid, hash_cur, hlist, hash) { if (atomic_read(&hash_cur->value) == user && - !strcasecmp(app_name->name, hash_cur->key.name)) { + qstr_case_eq(app_name, &hash_cur->key)) { rcu_read_unlock(); return 1; } @@ -126,24 +126,27 @@ static appid_t __is_excluded(const struct qstr *app_name, userid_t user) return 0; } -appid_t is_excluded(const char *app_name, userid_t user) +appid_t is_excluded(const char *key, userid_t user) { struct qstr q; - qstr_init(&q, app_name); + qstr_init(&q, key); return __is_excluded(&q, user); } - /* Kernel has already enforced everything we returned through * derive_permissions_locked(), so this is used to lock down access * even further, such as enforcing that apps hold sdcard_rw. */ -int check_caller_access_to_name(struct inode *parent_node, const char *name) +int check_caller_access_to_name(struct inode *parent_node, const struct qstr *name) { + struct qstr q_autorun = QSTR_LITERAL("autorun.inf"); + struct qstr q__android_secure = QSTR_LITERAL(".android_secure"); + struct qstr q_android_secure = QSTR_LITERAL("android_secure"); + /* Always block security-sensitive files at root */ if (parent_node && SDCARDFS_I(parent_node)->perm == PERM_ROOT) { - if (!strcasecmp(name, "autorun.inf") - || !strcasecmp(name, ".android_secure") - || !strcasecmp(name, "android_secure")) { + if (qstr_case_eq(name, &q_autorun) + || qstr_case_eq(name, &q__android_secure) + || qstr_case_eq(name, &q_android_secure)) { return 0; } } @@ -195,7 +198,7 @@ static int insert_packagelist_appid_entry_locked(const struct qstr *key, appid_t unsigned int hash = key->hash; hash_for_each_possible_rcu(package_to_appid, hash_cur, hlist, hash) { - if (!strcasecmp(key->name, hash_cur->key.name)) { + if (qstr_case_eq(key, &hash_cur->key)) { atomic_set(&hash_cur->value, value); return 0; } @@ -215,7 +218,7 @@ static int insert_ext_gid_entry_locked(const struct qstr *key, appid_t value) /* An extension can only belong to one gid */ hash_for_each_possible_rcu(ext_to_groupid, hash_cur, hlist, hash) { - if (!strcasecmp(key->name, hash_cur->key.name)) + if (qstr_case_eq(key, &hash_cur->key)) return -EINVAL; } new_entry = alloc_hashtable_entry(key, value); @@ -234,7 +237,7 @@ static int insert_userid_exclude_entry_locked(const struct qstr *key, userid_t v /* Only insert if not already present */ hash_for_each_possible_rcu(package_to_userid, hash_cur, hlist, hash) { if (atomic_read(&hash_cur->value) == value && - !strcasecmp(key->name, hash_cur->key.name)) + qstr_case_eq(key, &hash_cur->key)) return 0; } new_entry = alloc_hashtable_entry(key, value); @@ -338,13 +341,13 @@ static void remove_packagelist_entry_locked(const struct qstr *key) HLIST_HEAD(free_list); hash_for_each_possible_rcu(package_to_userid, hash_cur, hlist, hash) { - if (!strcasecmp(key->name, hash_cur->key.name)) { + if (qstr_case_eq(key, &hash_cur->key)) { hash_del_rcu(&hash_cur->hlist); hlist_add_head(&hash_cur->dlist, &free_list); } } hash_for_each_possible_rcu(package_to_appid, hash_cur, hlist, hash) { - if (!strcasecmp(key->name, hash_cur->key.name)) { + if (qstr_case_eq(key, &hash_cur->key)) { hash_del_rcu(&hash_cur->hlist); hlist_add_head(&hash_cur->dlist, &free_list); break; @@ -370,7 +373,7 @@ static void remove_ext_gid_entry_locked(const struct qstr *key, gid_t group) unsigned int hash = key->hash; hash_for_each_possible_rcu(ext_to_groupid, hash_cur, hlist, hash) { - if (!strcasecmp(key->name, hash_cur->key.name) && atomic_read(&hash_cur->value) == group) { + if (qstr_case_eq(key, &hash_cur->key) && atomic_read(&hash_cur->value) == group) { hash_del_rcu(&hash_cur->hlist); synchronize_rcu(); free_hashtable_entry(hash_cur); @@ -421,7 +424,7 @@ static void remove_userid_exclude_entry_locked(const struct qstr *key, userid_t unsigned int hash = key->hash; hash_for_each_possible_rcu(package_to_userid, hash_cur, hlist, hash) { - if (!strcasecmp(key->name, hash_cur->key.name) && + if (qstr_case_eq(key, &hash_cur->key) && atomic_read(&hash_cur->value) == userid) { hash_del_rcu(&hash_cur->hlist); synchronize_rcu(); @@ -530,7 +533,7 @@ static ssize_t package_details_excluded_userids_show(struct config_item *item, rcu_read_lock(); hash_for_each_possible_rcu(package_to_userid, hash_cur, hlist, hash) { - if (!strcasecmp(package_details->name.name, hash_cur->key.name)) + if (qstr_case_eq(&package_details->name, &hash_cur->key)) count += scnprintf(page + count, PAGE_SIZE - count, "%d ", atomic_read(&hash_cur->value)); } @@ -759,7 +762,7 @@ static ssize_t packages_list_show(struct config_item *item, char *page) hash_cur_app->key.name, atomic_read(&hash_cur_app->value)); hash = hash_cur_app->key.hash; hash_for_each_possible_rcu(package_to_userid, hash_cur_user, hlist, hash) { - if (!strcasecmp(hash_cur_app->key.name, hash_cur_user->key.name)) { + if (qstr_case_eq(&hash_cur_app->key, &hash_cur_user->key)) { written += scnprintf(page + count + written - 1, PAGE_SIZE - sizeof(errormsg) - count - written + 1, " %d\n", atomic_read(&hash_cur_user->value)) - 1; diff --git a/fs/sdcardfs/sdcardfs.h b/fs/sdcardfs/sdcardfs.h index 87a57ce25785..62838aeb57ec 100644 --- a/fs/sdcardfs/sdcardfs.h +++ b/fs/sdcardfs/sdcardfs.h @@ -465,7 +465,7 @@ extern struct list_head sdcardfs_super_list; extern appid_t get_appid(const char *app_name); extern appid_t get_ext_gid(const char *app_name); extern appid_t is_excluded(const char *app_name, userid_t userid); -extern int check_caller_access_to_name(struct inode *parent_node, const char* name); +extern int check_caller_access_to_name(struct inode *parent_node, const struct qstr *name); extern int open_flags_to_access_mode(int open_flags); extern int packagelist_init(void); extern void packagelist_exit(void); @@ -483,7 +483,7 @@ struct limit_search { extern void setup_derived_state(struct inode *inode, perm_t perm, userid_t userid, uid_t uid, bool under_android, struct inode *top); extern void get_derived_permission(struct dentry *parent, struct dentry *dentry); -extern void get_derived_permission_new(struct dentry *parent, struct dentry *dentry, const char *name); +extern void get_derived_permission_new(struct dentry *parent, struct dentry *dentry, const struct qstr *name); extern void drop_recursive(struct dentry *parent); extern void fixup_top_recursive(struct dentry *parent); extern void fixup_perms_recursive(struct dentry *dentry, struct limit_search *limit); @@ -611,4 +611,17 @@ static inline void sdcardfs_copy_and_fix_attrs(struct inode *dest, const struct dest->i_flags = src->i_flags; set_nlink(dest, src->i_nlink); } + +static inline bool str_case_eq(const char *s1, const char *s2) +{ + return !strcasecmp(s1, s2); +} + +static inline bool qstr_case_eq(const struct qstr *q1, const struct qstr *q2) +{ + return q1->len == q2->len && str_case_eq(q1->name, q2->name); +} + +#define QSTR_LITERAL(string) QSTR_INIT(string, sizeof(string)-1) + #endif /* not _SDCARDFS_H_ */ From aff9ad79976277f24b5973793a7bb43b56aebad2 Mon Sep 17 00:00:00 2001 From: Daniel Rosenberg Date: Tue, 14 Feb 2017 20:47:17 -0800 Subject: [PATCH 0326/1103] ANDROID: sdcardfs: Fix incorrect hash This adds back the hash calculation removed as part of the previous patch, as it is in fact necessary. Signed-off-by: Daniel Rosenberg Bug: 35307857 Change-Id: Ie607332bcf2c5d2efdf924e4060ef3f576bf25dc --- fs/sdcardfs/lookup.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/fs/sdcardfs/lookup.c b/fs/sdcardfs/lookup.c index 0d2caddd8510..ae8e9b519e16 100644 --- a/fs/sdcardfs/lookup.c +++ b/fs/sdcardfs/lookup.c @@ -221,6 +221,7 @@ static struct dentry *__sdcardfs_lookup(struct dentry *dentry, struct dentry *lower_dentry; const struct qstr *name; struct path lower_path; + struct qstr dname; struct sdcardfs_sb_info *sbi; sbi = SDCARDFS_SB(dentry->d_sb); @@ -306,11 +307,14 @@ static struct dentry *__sdcardfs_lookup(struct dentry *dentry, goto out; /* instatiate a new negative dentry */ - lower_dentry = d_lookup(lower_dir_dentry, name); + dname.name = name->name; + dname.len = name->len; + dname.hash = full_name_hash(lower_dir_dentry, dname.name, dname.len); + lower_dentry = d_lookup(lower_dir_dentry, &dname); if (lower_dentry) goto setup_lower; - lower_dentry = d_alloc(lower_dir_dentry, name); + lower_dentry = d_alloc(lower_dir_dentry, &dname); if (!lower_dentry) { err = -ENOMEM; goto out; From b6026cdbf240841af8d0f557f783b85d216ce331 Mon Sep 17 00:00:00 2001 From: Daniel Rosenberg Date: Thu, 16 Feb 2017 17:55:22 -0800 Subject: [PATCH 0327/1103] ANDROID: sdcardfs: Add missing path_put "ANDROID: sdcardfs: Add GID Derivation to sdcardfs" introduced an unbalanced pat_get, leading to storage space not being freed after deleting a file until rebooting. This adds the missing path_put. Signed-off-by: Daniel Rosenberg Bug: 34691169 Change-Id: Ia7ef97ec2eca2c555cc06b235715635afc87940e --- fs/sdcardfs/derived_perm.c | 1 + 1 file changed, 1 insertion(+) diff --git a/fs/sdcardfs/derived_perm.c b/fs/sdcardfs/derived_perm.c index 4b3801885959..1b5029d81921 100644 --- a/fs/sdcardfs/derived_perm.c +++ b/fs/sdcardfs/derived_perm.c @@ -245,6 +245,7 @@ void fixup_lower_ownership(struct dentry *dentry, const char *name) if (error) pr_err("sdcardfs: Failed to touch up lower fs gid/uid.\n"); } + sdcardfs_put_lower_path(dentry, &path); } static int descendant_may_need_fixup(struct sdcardfs_inode_info *info, struct limit_search *limit) From 3fe80faa39b979f9e5b8e8eddd767fbea85b6e9b Mon Sep 17 00:00:00 2001 From: Daniel Rosenberg Date: Wed, 22 Feb 2017 14:41:58 -0800 Subject: [PATCH 0328/1103] ANDROID: sdcardfs: Don't bother deleting freelist There is no point deleting entries from dlist, as that is a temporary list on the stack from which contains only entries that are being deleted. Not all code paths set up dlist, so those that don't were performing invalid accesses in hash_del_rcu. As an additional means to prevent any other issue, we null out the list entries when we allocate from the cache. Signed-off-by: Daniel Rosenberg Bug: 35666680 Change-Id: Ibb1e28c08c3a600c29418d39ba1c0f3db3bf31e5 --- fs/sdcardfs/packagelist.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/fs/sdcardfs/packagelist.c b/fs/sdcardfs/packagelist.c index 537ff523c32a..5f81284a4f44 100644 --- a/fs/sdcardfs/packagelist.c +++ b/fs/sdcardfs/packagelist.c @@ -181,6 +181,8 @@ static struct hashtable_entry *alloc_hashtable_entry(const struct qstr *key, GFP_KERNEL); if (!ret) return NULL; + INIT_HLIST_NODE(&ret->dlist); + INIT_HLIST_NODE(&ret->hlist); if (!qstr_copy(key, &ret->key)) { kmem_cache_free(hashtable_entry_cachep, ret); @@ -329,7 +331,6 @@ static int insert_userid_exclude_entry(const struct qstr *key, userid_t value) static void free_hashtable_entry(struct hashtable_entry *entry) { kfree(entry->key.name); - hash_del_rcu(&entry->dlist); kmem_cache_free(hashtable_entry_cachep, entry); } From 2f73b478eaf942a2f39bc245f762c939efe1c6fc Mon Sep 17 00:00:00 2001 From: Daniel Rosenberg Date: Fri, 24 Feb 2017 15:41:48 -0800 Subject: [PATCH 0329/1103] ANDROID: sdcardfs: implement vm_ops->page_mkwrite This comes from wrapfs commit 3dfec0ffe5e2 ("Wrapfs: implement vm_ops->page_mkwrite") Some file systems (e.g., ext4) require it. Reported by Ted Ts'o. Signed-off-by: Erez Zadok Signed-off-by: Daniel Rosenberg Bug: 34133558 Change-Id: I1a389b2422c654a6d3046bb8ec3e20511aebfa8e --- fs/sdcardfs/mmap.c | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/fs/sdcardfs/mmap.c b/fs/sdcardfs/mmap.c index ac5f3deae088..28db1d11c99e 100644 --- a/fs/sdcardfs/mmap.c +++ b/fs/sdcardfs/mmap.c @@ -48,6 +48,39 @@ static int sdcardfs_fault(struct vm_area_struct *vma, struct vm_fault *vmf) return err; } +static int sdcardfs_page_mkwrite(struct vm_area_struct *vma, + struct vm_fault *vmf) +{ + int err = 0; + struct file *file, *lower_file; + const struct vm_operations_struct *lower_vm_ops; + struct vm_area_struct lower_vma; + + memcpy(&lower_vma, vma, sizeof(struct vm_area_struct)); + file = lower_vma.vm_file; + lower_vm_ops = SDCARDFS_F(file)->lower_vm_ops; + BUG_ON(!lower_vm_ops); + if (!lower_vm_ops->page_mkwrite) + goto out; + + lower_file = sdcardfs_lower_file(file); + /* + * XXX: vm_ops->page_mkwrite may be called in parallel. + * Because we have to resort to temporarily changing the + * vma->vm_file to point to the lower file, a concurrent + * invocation of sdcardfs_page_mkwrite could see a different + * value. In this workaround, we keep a different copy of the + * vma structure in our stack, so we never expose a different + * value of the vma->vm_file called to us, even temporarily. + * A better fix would be to change the calling semantics of + * ->page_mkwrite to take an explicit file pointer. + */ + lower_vma.vm_file = lower_file; + err = lower_vm_ops->page_mkwrite(&lower_vma, vmf); +out: + return err; +} + static ssize_t sdcardfs_direct_IO(struct kiocb *iocb, struct iov_iter *iter) { /* @@ -77,4 +110,5 @@ const struct address_space_operations sdcardfs_aops = { const struct vm_operations_struct sdcardfs_vm_ops = { .fault = sdcardfs_fault, + .page_mkwrite = sdcardfs_page_mkwrite, }; From d404b02b69d34b0c924139321ddca84a71606801 Mon Sep 17 00:00:00 2001 From: Daniel Rosenberg Date: Fri, 24 Feb 2017 15:49:45 -0800 Subject: [PATCH 0330/1103] ANDROID: sdcardfs: support direct-IO (DIO) operations This comes from the wrapfs commit 2e346c83b26e ("Wrapfs: support direct-IO (DIO) operations") Signed-off-by: Li Mengyang Signed-off-by: Erez Zadok Signed-off-by: Daniel Rosenberg Bug: 34133558 Change-Id: I3fd779c510ab70d56b1d918f99c20421b524cdc4 --- fs/sdcardfs/mmap.c | 21 ++++----------------- fs/sdcardfs/sdcardfs.h | 1 + 2 files changed, 5 insertions(+), 17 deletions(-) diff --git a/fs/sdcardfs/mmap.c b/fs/sdcardfs/mmap.c index 28db1d11c99e..51266f517fe2 100644 --- a/fs/sdcardfs/mmap.c +++ b/fs/sdcardfs/mmap.c @@ -84,27 +84,14 @@ static int sdcardfs_page_mkwrite(struct vm_area_struct *vma, static ssize_t sdcardfs_direct_IO(struct kiocb *iocb, struct iov_iter *iter) { /* - * This function returns zero on purpose in order to support direct IO. - * __dentry_open checks a_ops->direct_IO and returns EINVAL if it is null. - * - * However, this function won't be called by certain file operations - * including generic fs functions. * reads and writes are delivered to - * the lower file systems and the direct IOs will be handled by them. - * - * NOTE: exceptionally, on the recent kernels (since Linux 3.8.x), - * swap_writepage invokes this function directly. + * This function should never be called directly. We need it + * to exist, to get past a check in open_check_o_direct(), + * which is called from do_last(). */ - printk(KERN_INFO "%s, operation is not supported\n", __func__); - return 0; + return -EINVAL; } -/* - * XXX: the default address_space_ops for sdcardfs is empty. We cannot set - * our inode->i_mapping->a_ops to NULL because too many code paths expect - * the a_ops vector to be non-NULL. - */ const struct address_space_operations sdcardfs_aops = { - /* empty on purpose */ .direct_IO = sdcardfs_direct_IO, }; diff --git a/fs/sdcardfs/sdcardfs.h b/fs/sdcardfs/sdcardfs.h index 62838aeb57ec..7740124049cd 100644 --- a/fs/sdcardfs/sdcardfs.h +++ b/fs/sdcardfs/sdcardfs.h @@ -29,6 +29,7 @@ #include #include #include +#include #include #include #include From ff0118392956d4a208f924616c192393c6275bf3 Mon Sep 17 00:00:00 2001 From: Daniel Rosenberg Date: Wed, 1 Mar 2017 17:04:41 -0800 Subject: [PATCH 0331/1103] ANDROID: sdcardfs: Fix case insensitive lookup The previous case insensitive lookup relied on the entry being present in the dcache. This instead uses iterate_dir to find the correct case. Signed-off-by: Daniel Rosenberg bug: 35633782 Change-Id: I556f7090773468c1943c89a5e2aa07f746ba49c5 --- fs/sdcardfs/lookup.c | 68 +++++++++++++++++++++++++++++++++----------- 1 file changed, 51 insertions(+), 17 deletions(-) diff --git a/fs/sdcardfs/lookup.c b/fs/sdcardfs/lookup.c index ae8e9b519e16..58a1d8001037 100644 --- a/fs/sdcardfs/lookup.c +++ b/fs/sdcardfs/lookup.c @@ -206,6 +206,28 @@ int sdcardfs_interpose(struct dentry *dentry, struct super_block *sb, return err; } +struct sdcardfs_name_data { + struct dir_context ctx; + const struct qstr *to_find; + char *name; + bool found; +}; + +static int sdcardfs_name_match(struct dir_context *ctx, const char *name, int namelen, + loff_t offset, u64 ino, unsigned int d_type) +{ + struct sdcardfs_name_data *buf = container_of(ctx, struct sdcardfs_name_data, ctx); + struct qstr candidate = QSTR_INIT(name, namelen); + + if (qstr_case_eq(buf->to_find, &candidate)) { + memcpy(buf->name, name, namelen); + buf->name[namelen] = 0; + buf->found = true; + return 1; + } + return 0; +} + /* * Main driver function for sdcardfs's lookup. * @@ -242,27 +264,39 @@ static struct dentry *__sdcardfs_lookup(struct dentry *dentry, &lower_path); /* check for other cases */ if (err == -ENOENT) { - struct dentry *child; - struct dentry *match = NULL; - inode_lock(d_inode(lower_dir_dentry)); - spin_lock(&lower_dir_dentry->d_lock); - list_for_each_entry(child, &lower_dir_dentry->d_subdirs, d_child) { - if (child && d_inode(child)) { - if (qstr_case_eq(&child->d_name, name)) { - match = dget(child); - break; - } - } + struct file *file; + const struct cred *cred = current_cred(); + + struct sdcardfs_name_data buffer = { + .ctx.actor = sdcardfs_name_match, + .to_find = name, + .name = __getname(), + .found = false, + }; + + if (!buffer.name) { + err = -ENOMEM; + goto out; + } + file = dentry_open(lower_parent_path, O_RDONLY, cred); + if (IS_ERR(file)) { + err = PTR_ERR(file); + goto put_name; } - spin_unlock(&lower_dir_dentry->d_lock); - inode_unlock(d_inode(lower_dir_dentry)); - if (match) { + err = iterate_dir(file, &buffer.ctx); + fput(file); + if (err) + goto put_name; + + if (buffer.found) err = vfs_path_lookup(lower_dir_dentry, lower_dir_mnt, - match->d_name.name, 0, + buffer.name, 0, &lower_path); - dput(match); - } + else + err = -ENOENT; +put_name: + __putname(buffer.name); } /* no error: handle positive dentries */ From bfff2bd8b8d5befc5ae25ffc74d19048751ed5ea Mon Sep 17 00:00:00 2001 From: Daniel Rosenberg Date: Thu, 2 Mar 2017 18:07:21 -0800 Subject: [PATCH 0332/1103] ANDROID: sdcardfs: rate limit warning print Signed-off-by: Daniel Rosenberg Bug: 35848445 Change-Id: Ida72ea0ece191b2ae4a8babae096b2451eb563f6 --- fs/sdcardfs/inode.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/fs/sdcardfs/inode.c b/fs/sdcardfs/inode.c index 4646dbb3ee03..5d05449720d8 100644 --- a/fs/sdcardfs/inode.c +++ b/fs/sdcardfs/inode.c @@ -20,6 +20,7 @@ #include "sdcardfs.h" #include +#include /* Do not directly use this function. Use OVERRIDE_CRED() instead. */ const struct cred *override_fsids(struct sdcardfs_sb_info *sbi, struct sdcardfs_inode_info *info) @@ -603,7 +604,7 @@ static const char *sdcardfs_follow_link(struct dentry *dentry, void **cookie) static int sdcardfs_permission_wrn(struct inode *inode, int mask) { - WARN(1, "sdcardfs does not support permission. Use permission2.\n"); + WARN_RATELIMIT(1, "sdcardfs does not support permission. Use permission2.\n"); return -EINVAL; } @@ -691,7 +692,7 @@ static int sdcardfs_permission(struct vfsmount *mnt, struct inode *inode, int ma static int sdcardfs_setattr_wrn(struct dentry *dentry, struct iattr *ia) { - WARN(1, "sdcardfs does not support setattr. User setattr2.\n"); + WARN_RATELIMIT(1, "sdcardfs does not support setattr. User setattr2.\n"); return -EINVAL; } From fb86d2104e0cd8ea4a687ec3402acdb9e5b7005c Mon Sep 17 00:00:00 2001 From: Daniel Rosenberg Date: Thu, 2 Mar 2017 15:11:27 -0800 Subject: [PATCH 0333/1103] ANDROID: sdcardfs: Replace get/put with d_lock dput cannot be called with a spin_lock. Instead, we protect our accesses by holding the d_lock. Signed-off-by: Daniel Rosenberg Bug: 35643557 Change-Id: I22cf30856d75b5616cbb0c223724f5ab866b5114 --- fs/sdcardfs/derived_perm.c | 33 ++++++++++++++++++++------------- 1 file changed, 20 insertions(+), 13 deletions(-) diff --git a/fs/sdcardfs/derived_perm.c b/fs/sdcardfs/derived_perm.c index 1b5029d81921..0f227b95c9e3 100644 --- a/fs/sdcardfs/derived_perm.c +++ b/fs/sdcardfs/derived_perm.c @@ -264,41 +264,48 @@ static int needs_fixup(perm_t perm) { return 0; } -void fixup_perms_recursive(struct dentry *dentry, struct limit_search *limit) +static void __fixup_perms_recursive(struct dentry *dentry, struct limit_search *limit, int depth) { struct dentry *child; struct sdcardfs_inode_info *info; - if (!dget(dentry)) - return; + + /* + * All paths will terminate their recursion on hitting PERM_ANDROID_OBB, + * PERM_ANDROID_MEDIA, or PERM_ANDROID_DATA. This happens at a depth of + * at most 3. + */ + WARN(depth > 3, "%s: Max expected depth exceeded!\n", __func__); + spin_lock_nested(&dentry->d_lock, depth); if (!d_inode(dentry)) { - dput(dentry); + spin_unlock(&dentry->d_lock); return; } info = SDCARDFS_I(d_inode(dentry)); if (needs_fixup(info->perm)) { - spin_lock(&dentry->d_lock); list_for_each_entry(child, &dentry->d_subdirs, d_child) { - dget(child); + spin_lock_nested(&child->d_lock, depth + 1); if (!(limit->flags & BY_NAME) || !strncasecmp(child->d_name.name, limit->name, limit->length)) { if (d_inode(child)) { get_derived_permission(dentry, child); fixup_tmp_permissions(d_inode(child)); - dput(child); + spin_unlock(&child->d_lock); break; } } - dput(child); + spin_unlock(&child->d_lock); } - spin_unlock(&dentry->d_lock); } else if (descendant_may_need_fixup(info, limit)) { - spin_lock(&dentry->d_lock); list_for_each_entry(child, &dentry->d_subdirs, d_child) { - fixup_perms_recursive(child, limit); + __fixup_perms_recursive(child, limit, depth + 1); } - spin_unlock(&dentry->d_lock); } - dput(dentry); + spin_unlock(&dentry->d_lock); +} + +void fixup_perms_recursive(struct dentry *dentry, struct limit_search *limit) +{ + __fixup_perms_recursive(dentry, limit, 0); } void drop_recursive(struct dentry *parent) From 7b38abe8ad83b27fe36f78edb0b2f31cfa57196e Mon Sep 17 00:00:00 2001 From: Daniel Rosenberg Date: Wed, 8 Mar 2017 17:11:51 -0800 Subject: [PATCH 0334/1103] ANDROID: sdcardfs: Use spin_lock_nested Signed-off-by: Daniel Rosenberg Bug: 36007653 Change-Id: I805d5afec797669679853fb2bb993ee38e6276e4 --- fs/sdcardfs/dentry.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/fs/sdcardfs/dentry.c b/fs/sdcardfs/dentry.c index f22de8add10c..d1eff456cc8a 100644 --- a/fs/sdcardfs/dentry.c +++ b/fs/sdcardfs/dentry.c @@ -76,10 +76,10 @@ static int sdcardfs_d_revalidate(struct dentry *dentry, unsigned int flags) if (dentry < lower_dentry) { spin_lock(&dentry->d_lock); - spin_lock(&lower_dentry->d_lock); + spin_lock_nested(&lower_dentry->d_lock, DENTRY_D_LOCK_NESTED); } else { spin_lock(&lower_dentry->d_lock); - spin_lock(&dentry->d_lock); + spin_lock_nested(&dentry->d_lock, DENTRY_D_LOCK_NESTED); } if (dentry->d_name.len != lower_dentry->d_name.len) { From fa8879d0dd62f4afd07d82922e18dafa8dba6f6e Mon Sep 17 00:00:00 2001 From: Daniel Rosenberg Date: Wed, 8 Mar 2017 17:20:02 -0800 Subject: [PATCH 0335/1103] ANDROID: sdcardfs: Switch to internal case insensitive compare There were still a few places where we called into a case insensitive lookup that was not defined by sdcardfs. Moving them all to the same place will allow us to switch the implementation in the future. Additionally, the check in fixup_perms_recursive did not take into account the length of both strings, causing extraneous matches when the name we were looking for was a prefix of the child name. Signed-off-by: Daniel Rosenberg Change-Id: I45ce768cd782cb4ea1ae183772781387c590ecc2 --- fs/sdcardfs/dentry.c | 8 ++------ fs/sdcardfs/derived_perm.c | 2 +- fs/sdcardfs/packagelist.c | 6 ++---- fs/sdcardfs/sdcardfs.h | 8 ++++++-- 4 files changed, 11 insertions(+), 13 deletions(-) diff --git a/fs/sdcardfs/dentry.c b/fs/sdcardfs/dentry.c index d1eff456cc8a..2797d2fc4227 100644 --- a/fs/sdcardfs/dentry.c +++ b/fs/sdcardfs/dentry.c @@ -82,11 +82,7 @@ static int sdcardfs_d_revalidate(struct dentry *dentry, unsigned int flags) spin_lock_nested(&dentry->d_lock, DENTRY_D_LOCK_NESTED); } - if (dentry->d_name.len != lower_dentry->d_name.len) { - __d_drop(dentry); - err = 0; - } else if (strncasecmp(dentry->d_name.name, lower_dentry->d_name.name, - dentry->d_name.len) != 0) { + if (!qstr_case_eq(&dentry->d_name, &lower_dentry->d_name)) { __d_drop(dentry); err = 0; } @@ -165,7 +161,7 @@ static int sdcardfs_cmp_ci(const struct dentry *dentry, } */ if (name->len == len) { - if (strncasecmp(name->name, str, len) == 0) + if (str_n_case_eq(name->name, str, len)) return 0; } return 1; diff --git a/fs/sdcardfs/derived_perm.c b/fs/sdcardfs/derived_perm.c index 0f227b95c9e3..69ffbf253645 100644 --- a/fs/sdcardfs/derived_perm.c +++ b/fs/sdcardfs/derived_perm.c @@ -285,7 +285,7 @@ static void __fixup_perms_recursive(struct dentry *dentry, struct limit_search * if (needs_fixup(info->perm)) { list_for_each_entry(child, &dentry->d_subdirs, d_child) { spin_lock_nested(&child->d_lock, depth + 1); - if (!(limit->flags & BY_NAME) || !strncasecmp(child->d_name.name, limit->name, limit->length)) { + if (!(limit->flags & BY_NAME) || qstr_case_eq(&child->d_name, &limit->name)) { if (d_inode(child)) { get_derived_permission(dentry, child); fixup_tmp_permissions(d_inode(child)); diff --git a/fs/sdcardfs/packagelist.c b/fs/sdcardfs/packagelist.c index 5f81284a4f44..d933bffacc7a 100644 --- a/fs/sdcardfs/packagelist.c +++ b/fs/sdcardfs/packagelist.c @@ -254,8 +254,7 @@ static void fixup_all_perms_name(const struct qstr *key) struct sdcardfs_sb_info *sbinfo; struct limit_search limit = { .flags = BY_NAME, - .name = key->name, - .length = key->len, + .name = QSTR_INIT(key->name, key->len), }; list_for_each_entry(sbinfo, &sdcardfs_super_list, list) { if (sbinfo_has_sdcard_magic(sbinfo)) @@ -268,8 +267,7 @@ static void fixup_all_perms_name_userid(const struct qstr *key, userid_t userid) struct sdcardfs_sb_info *sbinfo; struct limit_search limit = { .flags = BY_NAME | BY_USERID, - .name = key->name, - .length = key->len, + .name = QSTR_INIT(key->name, key->len), .userid = userid, }; list_for_each_entry(sbinfo, &sdcardfs_super_list, list) { diff --git a/fs/sdcardfs/sdcardfs.h b/fs/sdcardfs/sdcardfs.h index 7740124049cd..a42125ef39f5 100644 --- a/fs/sdcardfs/sdcardfs.h +++ b/fs/sdcardfs/sdcardfs.h @@ -476,8 +476,7 @@ extern void packagelist_exit(void); #define BY_USERID (1 << 1) struct limit_search { unsigned int flags; - const char *name; - size_t length; + struct qstr name; userid_t userid; }; @@ -618,6 +617,11 @@ static inline bool str_case_eq(const char *s1, const char *s2) return !strcasecmp(s1, s2); } +static inline bool str_n_case_eq(const char *s1, const char *s2, size_t len) +{ + return !strncasecmp(s1, s2, len); +} + static inline bool qstr_case_eq(const struct qstr *q1, const struct qstr *q2) { return q1->len == q2->len && str_case_eq(q1->name, q2->name); From c46f76dadf1614a4a4486ab7d8d59ed9cb79b364 Mon Sep 17 00:00:00 2001 From: Daniel Rosenberg Date: Wed, 8 Mar 2017 17:45:46 -0800 Subject: [PATCH 0336/1103] ANDROID: sdcardfs: Use d_invalidate instead of drop_recurisve drop_recursive did not properly remove stale dentries. Instead, we use the vfs's d_invalidate, which does the proper cleanup. Additionally, remove the no longer used drop_recursive, and fixup_top_recursive that that are no longer used. Signed-off-by: Daniel Rosenberg Change-Id: Ibff61b0c34b725b024a050169047a415bc90f0d8 --- fs/sdcardfs/derived_perm.c | 40 -------------------------------------- fs/sdcardfs/inode.c | 2 +- fs/sdcardfs/sdcardfs.h | 2 -- 3 files changed, 1 insertion(+), 43 deletions(-) diff --git a/fs/sdcardfs/derived_perm.c b/fs/sdcardfs/derived_perm.c index 69ffbf253645..72ae4b9edc31 100644 --- a/fs/sdcardfs/derived_perm.c +++ b/fs/sdcardfs/derived_perm.c @@ -308,46 +308,6 @@ void fixup_perms_recursive(struct dentry *dentry, struct limit_search *limit) __fixup_perms_recursive(dentry, limit, 0); } -void drop_recursive(struct dentry *parent) -{ - struct dentry *dentry; - struct sdcardfs_inode_info *info; - if (!d_inode(parent)) - return; - info = SDCARDFS_I(d_inode(parent)); - spin_lock(&parent->d_lock); - list_for_each_entry(dentry, &parent->d_subdirs, d_child) { - if (d_inode(dentry)) { - if (SDCARDFS_I(d_inode(parent))->top != SDCARDFS_I(d_inode(dentry))->top) { - drop_recursive(dentry); - d_drop(dentry); - } - } - } - spin_unlock(&parent->d_lock); -} - -void fixup_top_recursive(struct dentry *parent) -{ - struct dentry *dentry; - struct sdcardfs_inode_info *info; - - if (!d_inode(parent)) - return; - info = SDCARDFS_I(d_inode(parent)); - spin_lock(&parent->d_lock); - list_for_each_entry(dentry, &parent->d_subdirs, d_child) { - if (d_inode(dentry)) { - if (SDCARDFS_I(d_inode(parent))->top != SDCARDFS_I(d_inode(dentry))->top) { - get_derived_permission(parent, dentry); - fixup_tmp_permissions(d_inode(dentry)); - fixup_top_recursive(dentry); - } - } - } - spin_unlock(&parent->d_lock); -} - /* main function for updating derived permission */ inline void update_derived_permission_lock(struct dentry *dentry) { diff --git a/fs/sdcardfs/inode.c b/fs/sdcardfs/inode.c index 5d05449720d8..9052ed472157 100644 --- a/fs/sdcardfs/inode.c +++ b/fs/sdcardfs/inode.c @@ -533,7 +533,7 @@ static int sdcardfs_rename(struct inode *old_dir, struct dentry *old_dentry, get_derived_permission_new(new_dentry->d_parent, old_dentry, &new_dentry->d_name); fixup_tmp_permissions(d_inode(old_dentry)); fixup_lower_ownership(old_dentry, new_dentry->d_name.name); - drop_recursive(old_dentry); /* Can't fixup ownership recursively :( */ + d_invalidate(old_dentry); /* Can't fixup ownership recursively :( */ out: unlock_rename(lower_old_dir_dentry, lower_new_dir_dentry); dput(lower_old_dir_dentry); diff --git a/fs/sdcardfs/sdcardfs.h b/fs/sdcardfs/sdcardfs.h index a42125ef39f5..09ec1e415e6d 100644 --- a/fs/sdcardfs/sdcardfs.h +++ b/fs/sdcardfs/sdcardfs.h @@ -484,8 +484,6 @@ extern void setup_derived_state(struct inode *inode, perm_t perm, userid_t useri uid_t uid, bool under_android, struct inode *top); extern void get_derived_permission(struct dentry *parent, struct dentry *dentry); extern void get_derived_permission_new(struct dentry *parent, struct dentry *dentry, const struct qstr *name); -extern void drop_recursive(struct dentry *parent); -extern void fixup_top_recursive(struct dentry *parent); extern void fixup_perms_recursive(struct dentry *dentry, struct limit_search *limit); extern void update_derived_permission_lock(struct dentry *dentry); From 40444d2a8535a05144a4786b69d77d2e07daa260 Mon Sep 17 00:00:00 2001 From: Daniel Rosenberg Date: Thu, 9 Mar 2017 18:12:16 -0800 Subject: [PATCH 0337/1103] ANDROID: sdcardfs: Get the blocksize from the lower fs This changes sdcardfs to be more in line with the getattr in wrapfs, which calls the lower fs's getattr to get the block size Signed-off-by: Daniel Rosenberg Bug: 34723223 Change-Id: I1c9e16604ba580a8cdefa17f02dcc489d7351aed --- fs/sdcardfs/inode.c | 21 +++++++++------------ 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/fs/sdcardfs/inode.c b/fs/sdcardfs/inode.c index 9052ed472157..3022cccc039b 100644 --- a/fs/sdcardfs/inode.c +++ b/fs/sdcardfs/inode.c @@ -865,9 +865,7 @@ static int sdcardfs_fillattr(struct vfsmount *mnt, struct inode *inode, struct k static int sdcardfs_getattr(struct vfsmount *mnt, struct dentry *dentry, struct kstat *stat) { - struct dentry *lower_dentry; - struct inode *inode; - struct inode *lower_inode; + struct kstat lower_stat; struct path lower_path; struct dentry *parent; int err; @@ -882,16 +880,15 @@ static int sdcardfs_getattr(struct vfsmount *mnt, struct dentry *dentry, } dput(parent); - inode = d_inode(dentry); - sdcardfs_get_lower_path(dentry, &lower_path); - lower_dentry = lower_path.dentry; - lower_inode = sdcardfs_lower_inode(inode); - - sdcardfs_copy_and_fix_attrs(inode, lower_inode); - fsstack_copy_inode_size(inode, lower_inode); - - err = sdcardfs_fillattr(mnt, inode, stat); + err = vfs_getattr(&lower_path, &lower_stat); + if (err) + goto out; + sdcardfs_copy_and_fix_attrs(d_inode(dentry), + d_inode(lower_path.dentry)); + err = sdcardfs_fillattr(mnt, d_inode(dentry), stat); + stat->blocks = lower_stat.blocks; +out: sdcardfs_put_lower_path(dentry, &lower_path); return err; } From 00461a830a5523ea97852fab4a7773948ad1aec2 Mon Sep 17 00:00:00 2001 From: Daniel Rosenberg Date: Thu, 9 Mar 2017 20:59:18 -0800 Subject: [PATCH 0338/1103] ANDROID: sdcardfs: declare MODULE_ALIAS_FS From commit ee616b78aa87 ("Wrapfs: declare MODULE_ALIAS_FS") Signed-off-by: Daniel Rosenberg bug: 35766959 Change-Id: Ia4728ab49d065b1d2eb27825046f14b97c328cba --- fs/sdcardfs/main.c | 1 + 1 file changed, 1 insertion(+) diff --git a/fs/sdcardfs/main.c b/fs/sdcardfs/main.c index 7a8eae29e44d..4e2aded8d1d9 100644 --- a/fs/sdcardfs/main.c +++ b/fs/sdcardfs/main.c @@ -432,6 +432,7 @@ static struct file_system_type sdcardfs_fs_type = { .kill_sb = sdcardfs_kill_sb, .fs_flags = 0, }; +MODULE_ALIAS_FS(SDCARDFS_NAME); static int __init init_sdcardfs_fs(void) { From b6e4f7091608f5c4a91250733a7385e54e8cfafa Mon Sep 17 00:00:00 2001 From: Daniel Rosenberg Date: Fri, 10 Mar 2017 12:39:42 -0800 Subject: [PATCH 0339/1103] ANDROID: sdcardfs: Use case insensitive hash function Case insensitive comparisons don't help us much if we hash to different buckets... Signed-off-by: Daniel Rosenberg bug: 36004503 Change-Id: I91e00dbcd860a709cbd4f7fd7fc6d855779f3285 --- fs/sdcardfs/packagelist.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/fs/sdcardfs/packagelist.c b/fs/sdcardfs/packagelist.c index d933bffacc7a..2cc076ca85de 100644 --- a/fs/sdcardfs/packagelist.c +++ b/fs/sdcardfs/packagelist.c @@ -20,6 +20,7 @@ #include "sdcardfs.h" #include +#include #include #include #include @@ -44,11 +45,19 @@ static DEFINE_HASHTABLE(ext_to_groupid, 8); static struct kmem_cache *hashtable_entry_cachep; +static unsigned int full_name_case_hash(const void *salt, const unsigned char *name, unsigned int len) +{ + unsigned long hash = init_name_hash(salt); + while (len--) + hash = partial_name_hash(tolower(*name++), hash); + return end_name_hash(hash); +} + static inline void qstr_init(struct qstr *q, const char *name) { q->name = name; q->len = strlen(q->name); - q->hash = full_name_hash(0, q->name, q->len); + q->hash = full_name_case_hash(0, q->name, q->len); } static inline int qstr_copy(const struct qstr *src, struct qstr *dest) From 2d5ab716915b6859a840657223d3c7c3760bfcb0 Mon Sep 17 00:00:00 2001 From: Daniel Rosenberg Date: Fri, 10 Mar 2017 13:54:30 -0800 Subject: [PATCH 0340/1103] ANDROID: sdcardfs: move path_put outside of spinlock Signed-off-by: Daniel Rosenberg Bug: 35643557 Change-Id: Ib279ebd7dd4e5884d184d67696a93e34993bc1ef --- fs/sdcardfs/derived_perm.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/fs/sdcardfs/derived_perm.c b/fs/sdcardfs/derived_perm.c index 72ae4b9edc31..f47884b7d397 100644 --- a/fs/sdcardfs/derived_perm.c +++ b/fs/sdcardfs/derived_perm.c @@ -360,6 +360,8 @@ int is_obbpath_invalid(struct dentry *dent) struct sdcardfs_dentry_info *di = SDCARDFS_D(dent); struct sdcardfs_sb_info *sbi = SDCARDFS_SB(dent->d_sb); char *path_buf, *obbpath_s; + int need_put = 0; + struct path lower_path; /* check the base obbpath has been changed. * this routine can check an uninitialized obb dentry as well. @@ -386,10 +388,13 @@ int is_obbpath_invalid(struct dentry *dent) } //unlock_dir(lower_parent); - path_put(&di->lower_path); + pathcpy(&lower_path, &di->lower_path); + need_put = 1; } } spin_unlock(&di->lock); + if (need_put) + path_put(&lower_path); return ret; } From 1a0bed2ef2ed38c4bcfdff78e36b226eacd6c455 Mon Sep 17 00:00:00 2001 From: Daniel Rosenberg Date: Fri, 10 Mar 2017 18:58:25 -0800 Subject: [PATCH 0341/1103] ANDROID: sdcardfs: Remove uninformative prints At best these prints do not provide useful information, and at worst, some allow userspace to abuse the kernel log. Signed-off-by: Daniel Rosenberg Bug: 36138424 Change-Id: I812c57cc6a22b37262935ab77f48f3af4c36827e --- fs/sdcardfs/derived_perm.c | 1 - fs/sdcardfs/file.c | 3 --- fs/sdcardfs/inode.c | 24 +----------------------- fs/sdcardfs/lookup.c | 3 --- 4 files changed, 1 insertion(+), 30 deletions(-) diff --git a/fs/sdcardfs/derived_perm.c b/fs/sdcardfs/derived_perm.c index f47884b7d397..bc7f09800dbb 100644 --- a/fs/sdcardfs/derived_perm.c +++ b/fs/sdcardfs/derived_perm.c @@ -440,7 +440,6 @@ int setup_obb_dentry(struct dentry *dentry, struct path *lower_path) if(!err) { /* the obbpath base has been found */ - printk(KERN_INFO "sdcardfs: the sbi->obbpath is found\n"); pathcpy(lower_path, &obbpath); } else { /* if the sbi->obbpath is not available, we can optionally diff --git a/fs/sdcardfs/file.c b/fs/sdcardfs/file.c index 56000a004b38..0f2db26895bf 100644 --- a/fs/sdcardfs/file.c +++ b/fs/sdcardfs/file.c @@ -217,9 +217,6 @@ static int sdcardfs_open(struct inode *inode, struct file *file) } if (!check_caller_access_to_name(d_inode(parent), &dentry->d_name)) { - printk(KERN_INFO "%s: need to check the caller's gid in packages.list\n" - " dentry: %s, task:%s\n", - __func__, dentry->d_name.name, current->comm); err = -EACCES; goto out_err; } diff --git a/fs/sdcardfs/inode.c b/fs/sdcardfs/inode.c index 3022cccc039b..7ca5b8ab4313 100644 --- a/fs/sdcardfs/inode.c +++ b/fs/sdcardfs/inode.c @@ -68,9 +68,6 @@ static int sdcardfs_create(struct inode *dir, struct dentry *dentry, struct fs_struct *copied_fs; if (!check_caller_access_to_name(dir, &dentry->d_name)) { - printk(KERN_INFO "%s: need to check the caller's gid in packages.list\n" - " dentry: %s, task:%s\n", - __func__, dentry->d_name.name, current->comm); err = -EACCES; goto out_eacces; } @@ -170,9 +167,6 @@ static int sdcardfs_unlink(struct inode *dir, struct dentry *dentry) const struct cred *saved_cred = NULL; if (!check_caller_access_to_name(dir, &dentry->d_name)) { - printk(KERN_INFO "%s: need to check the caller's gid in packages.list\n" - " dentry: %s, task:%s\n", - __func__, dentry->d_name.name, current->comm); err = -EACCES; goto out_eacces; } @@ -280,9 +274,6 @@ static int sdcardfs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode struct qstr q_data = QSTR_LITERAL("data"); if (!check_caller_access_to_name(dir, &dentry->d_name)) { - printk(KERN_INFO "%s: need to check the caller's gid in packages.list\n" - " dentry: %s, task:%s\n", - __func__, dentry->d_name.name, current->comm); err = -EACCES; goto out_eacces; } @@ -392,9 +383,6 @@ static int sdcardfs_rmdir(struct inode *dir, struct dentry *dentry) const struct cred *saved_cred = NULL; if (!check_caller_access_to_name(dir, &dentry->d_name)) { - printk(KERN_INFO "%s: need to check the caller's gid in packages.list\n" - " dentry: %s, task:%s\n", - __func__, dentry->d_name.name, current->comm); err = -EACCES; goto out_eacces; } @@ -485,9 +473,6 @@ static int sdcardfs_rename(struct inode *old_dir, struct dentry *old_dentry, if (!check_caller_access_to_name(old_dir, &old_dentry->d_name) || !check_caller_access_to_name(new_dir, &new_dentry->d_name)) { - printk(KERN_INFO "%s: need to check the caller's gid in packages.list\n" - " new_dentry: %s, task:%s\n", - __func__, new_dentry->d_name.name, current->comm); err = -EACCES; goto out_eacces; } @@ -755,12 +740,8 @@ static int sdcardfs_setattr(struct vfsmount *mnt, struct dentry *dentry, struct if (!err) { /* check the Android group ID */ parent = dget_parent(dentry); - if (!check_caller_access_to_name(d_inode(parent), &dentry->d_name)) { - printk(KERN_INFO "%s: need to check the caller's gid in packages.list\n" - " dentry: %s, task:%s\n", - __func__, dentry->d_name.name, current->comm); + if (!check_caller_access_to_name(d_inode(parent), &dentry->d_name)) err = -EACCES; - } dput(parent); } @@ -872,9 +853,6 @@ static int sdcardfs_getattr(struct vfsmount *mnt, struct dentry *dentry, parent = dget_parent(dentry); if (!check_caller_access_to_name(d_inode(parent), &dentry->d_name)) { - printk(KERN_INFO "%s: need to check the caller's gid in packages.list\n" - " dentry: %s, task:%s\n", - __func__, dentry->d_name.name, current->comm); dput(parent); return -EACCES; } diff --git a/fs/sdcardfs/lookup.c b/fs/sdcardfs/lookup.c index 58a1d8001037..7d26c269da35 100644 --- a/fs/sdcardfs/lookup.c +++ b/fs/sdcardfs/lookup.c @@ -395,9 +395,6 @@ struct dentry *sdcardfs_lookup(struct inode *dir, struct dentry *dentry, if (!check_caller_access_to_name(d_inode(parent), &dentry->d_name)) { ret = ERR_PTR(-EACCES); - printk(KERN_INFO "%s: need to check the caller's gid in packages.list\n" - " dentry: %s, task:%s\n", - __func__, dentry->d_name.name, current->comm); goto out_err; } From 750e9dc40532dfb7474133594f9e533798d99b08 Mon Sep 17 00:00:00 2001 From: Daniel Rosenberg Date: Mon, 13 Mar 2017 15:34:03 -0700 Subject: [PATCH 0342/1103] ANDROID: sdcardfs: Fix gid issue We were already calculating most of these values, and erroring out because the check was confused by this. Instead of recalculating, adjust it as needed. Signed-off-by: Daniel Rosenberg Bug: 36160015 Change-Id: I9caf3e2fd32ca2e37ff8ed71b1d392f1761bc9a9 --- fs/sdcardfs/derived_perm.c | 4 ++-- fs/sdcardfs/multiuser.h | 16 ++++------------ 2 files changed, 6 insertions(+), 14 deletions(-) diff --git a/fs/sdcardfs/derived_perm.c b/fs/sdcardfs/derived_perm.c index bc7f09800dbb..fc5a632e9b83 100644 --- a/fs/sdcardfs/derived_perm.c +++ b/fs/sdcardfs/derived_perm.c @@ -207,13 +207,13 @@ void fixup_lower_ownership(struct dentry *dentry, const char *name) break; case PERM_ANDROID_PACKAGE: if (info->d_uid != 0) - gid = multiuser_get_ext_gid(info->userid, info->d_uid); + gid = multiuser_get_ext_gid(info->d_uid); else gid = multiuser_get_uid(info->userid, uid); break; case PERM_ANDROID_PACKAGE_CACHE: if (info->d_uid != 0) - gid = multiuser_get_cache_gid(info->userid, info->d_uid); + gid = multiuser_get_cache_gid(info->d_uid); else gid = multiuser_get_uid(info->userid, uid); break; diff --git a/fs/sdcardfs/multiuser.h b/fs/sdcardfs/multiuser.h index 950cffddc556..2e89b5872314 100644 --- a/fs/sdcardfs/multiuser.h +++ b/fs/sdcardfs/multiuser.h @@ -33,20 +33,12 @@ static inline uid_t multiuser_get_uid(userid_t user_id, appid_t app_id) return (user_id * AID_USER_OFFSET) + (app_id % AID_USER_OFFSET); } -static inline gid_t multiuser_get_cache_gid(userid_t user_id, appid_t app_id) +static inline gid_t multiuser_get_cache_gid(uid_t uid) { - if (app_id >= AID_APP_START && app_id <= AID_APP_END) { - return multiuser_get_uid(user_id, (app_id - AID_APP_START) + AID_CACHE_GID_START); - } else { - return -1; - } + return uid - AID_APP_START + AID_CACHE_GID_START; } -static inline gid_t multiuser_get_ext_gid(userid_t user_id, appid_t app_id) +static inline gid_t multiuser_get_ext_gid(uid_t uid) { - if (app_id >= AID_APP_START && app_id <= AID_APP_END) { - return multiuser_get_uid(user_id, (app_id - AID_APP_START) + AID_EXT_GID_START); - } else { - return -1; - } + return uid - AID_APP_START + AID_EXT_GID_START; } From b48e0f261d8a9daf63a29ca17607bba8cd2bd8dd Mon Sep 17 00:00:00 2001 From: Daniel Rosenberg Date: Tue, 21 Mar 2017 16:28:27 -0700 Subject: [PATCH 0343/1103] ANDROID: sdcardfs: correct order of descriptors Signed-off-by: Daniel Rosenberg Bug: 35331000 Change-Id: Ia6d16b19c8c911f41231d2a12be0740057edfacf --- fs/sdcardfs/packagelist.c | 1 + 1 file changed, 1 insertion(+) diff --git a/fs/sdcardfs/packagelist.c b/fs/sdcardfs/packagelist.c index 2cc076ca85de..7abfeb124fc2 100644 --- a/fs/sdcardfs/packagelist.c +++ b/fs/sdcardfs/packagelist.c @@ -48,6 +48,7 @@ static struct kmem_cache *hashtable_entry_cachep; static unsigned int full_name_case_hash(const void *salt, const unsigned char *name, unsigned int len) { unsigned long hash = init_name_hash(salt); + while (len--) hash = partial_name_hash(tolower(*name++), hash); return end_name_hash(hash); From f83526cf90daec506a8ce1bf0ee802e2267fe975 Mon Sep 17 00:00:00 2001 From: Daniel Rosenberg Date: Thu, 16 Mar 2017 17:42:58 -0700 Subject: [PATCH 0344/1103] ANDROID: sdcardfs: Fix formatting This fixes various spacing and bracket related issues pointed out by checkpatch. Signed-off-by: Daniel Rosenberg Bug: 35331000 Change-Id: I6e248833a7a04e3899f3ae9462d765cfcaa70c96 --- fs/sdcardfs/dentry.c | 13 ++--- fs/sdcardfs/derived_perm.c | 55 +++++++++++--------- fs/sdcardfs/file.c | 3 +- fs/sdcardfs/inode.c | 28 +++++++---- fs/sdcardfs/lookup.c | 30 +++++------ fs/sdcardfs/main.c | 34 ++++++++----- fs/sdcardfs/packagelist.c | 36 ++++++------- fs/sdcardfs/sdcardfs.h | 100 ++++++++++++++++++++----------------- fs/sdcardfs/super.c | 29 ++++++----- 9 files changed, 184 insertions(+), 144 deletions(-) diff --git a/fs/sdcardfs/dentry.c b/fs/sdcardfs/dentry.c index 2797d2fc4227..26665da31732 100644 --- a/fs/sdcardfs/dentry.c +++ b/fs/sdcardfs/dentry.c @@ -46,7 +46,8 @@ static int sdcardfs_d_revalidate(struct dentry *dentry, unsigned int flags) spin_unlock(&dentry->d_lock); /* check uninitialized obb_dentry and - * whether the base obbpath has been changed or not */ + * whether the base obbpath has been changed or not + */ if (is_obbpath_invalid(dentry)) { d_drop(dentry); return 0; @@ -106,12 +107,10 @@ static int sdcardfs_d_revalidate(struct dentry *dentry, unsigned int flags) static void sdcardfs_d_release(struct dentry *dentry) { /* release and reset the lower paths */ - if(has_graft_path(dentry)) { + if (has_graft_path(dentry)) sdcardfs_put_reset_orig_path(dentry); - } sdcardfs_put_reset_lower_path(dentry); free_dentry_private_data(dentry); - return; } static int sdcardfs_hash_ci(const struct dentry *dentry, @@ -167,14 +166,16 @@ static int sdcardfs_cmp_ci(const struct dentry *dentry, return 1; } -static void sdcardfs_canonical_path(const struct path *path, struct path *actual_path) { +static void sdcardfs_canonical_path(const struct path *path, + struct path *actual_path) +{ sdcardfs_get_real_lower(path->dentry, actual_path); } const struct dentry_operations sdcardfs_ci_dops = { .d_revalidate = sdcardfs_d_revalidate, .d_release = sdcardfs_d_release, - .d_hash = sdcardfs_hash_ci, + .d_hash = sdcardfs_hash_ci, .d_compare = sdcardfs_cmp_ci, .d_canonical_path = sdcardfs_canonical_path, }; diff --git a/fs/sdcardfs/derived_perm.c b/fs/sdcardfs/derived_perm.c index fc5a632e9b83..9de548016a3b 100644 --- a/fs/sdcardfs/derived_perm.c +++ b/fs/sdcardfs/derived_perm.c @@ -37,7 +37,8 @@ static void inherit_derived_state(struct inode *parent, struct inode *child) /* helper function for derived state */ void setup_derived_state(struct inode *inode, perm_t perm, userid_t userid, - uid_t uid, bool under_android, struct inode *top) + uid_t uid, bool under_android, + struct inode *top) { struct sdcardfs_inode_info *info = SDCARDFS_I(inode); @@ -50,11 +51,14 @@ void setup_derived_state(struct inode *inode, perm_t perm, userid_t userid, set_top(info, top); } -/* While renaming, there is a point where we want the path from dentry, but the name from newdentry */ -void get_derived_permission_new(struct dentry *parent, struct dentry *dentry, const struct qstr *name) +/* While renaming, there is a point where we want the path from dentry, + * but the name from newdentry + */ +void get_derived_permission_new(struct dentry *parent, struct dentry *dentry, + const struct qstr *name) { struct sdcardfs_inode_info *info = SDCARDFS_I(d_inode(dentry)); - struct sdcardfs_inode_info *parent_info= SDCARDFS_I(d_inode(parent)); + struct sdcardfs_inode_info *parent_info = SDCARDFS_I(d_inode(parent)); appid_t appid; struct qstr q_Android = QSTR_LITERAL("Android"); struct qstr q_data = QSTR_LITERAL("data"); @@ -118,9 +122,8 @@ void get_derived_permission_new(struct dentry *parent, struct dentry *dentry, co case PERM_ANDROID_MEDIA: info->perm = PERM_ANDROID_PACKAGE; appid = get_appid(name->name); - if (appid != 0 && !is_excluded(name->name, parent_info->userid)) { + if (appid != 0 && !is_excluded(name->name, parent_info->userid)) info->d_uid = multiuser_get_uid(parent_info->userid, appid); - } set_top(info, &info->vfs_inode); break; case PERM_ANDROID_PACKAGE: @@ -257,7 +260,8 @@ static int descendant_may_need_fixup(struct sdcardfs_inode_info *info, struct li return 0; } -static int needs_fixup(perm_t perm) { +static int needs_fixup(perm_t perm) +{ if (perm == PERM_ANDROID_DATA || perm == PERM_ANDROID_OBB || perm == PERM_ANDROID_MEDIA) return 1; @@ -295,9 +299,9 @@ static void __fixup_perms_recursive(struct dentry *dentry, struct limit_search * } spin_unlock(&child->d_lock); } - } else if (descendant_may_need_fixup(info, limit)) { + } else if (descendant_may_need_fixup(info, limit)) { list_for_each_entry(child, &dentry->d_subdirs, d_child) { - __fixup_perms_recursive(child, limit, depth + 1); + __fixup_perms_recursive(child, limit, depth + 1); } } spin_unlock(&dentry->d_lock); @@ -313,7 +317,7 @@ inline void update_derived_permission_lock(struct dentry *dentry) { struct dentry *parent; - if(!dentry || !d_inode(dentry)) { + if (!dentry || !d_inode(dentry)) { printk(KERN_ERR "sdcardfs: %s: invalid dentry\n", __func__); return; } @@ -321,11 +325,11 @@ inline void update_derived_permission_lock(struct dentry *dentry) * 1. need to check whether the dentry is updated or not * 2. remove the root dentry update */ - if(IS_ROOT(dentry)) { + if (IS_ROOT(dentry)) { //setup_default_pre_root_state(d_inode(dentry)); } else { parent = dget_parent(dentry); - if(parent) { + if (parent) { get_derived_permission(parent, dentry); dput(parent); } @@ -337,15 +341,15 @@ int need_graft_path(struct dentry *dentry) { int ret = 0; struct dentry *parent = dget_parent(dentry); - struct sdcardfs_inode_info *parent_info= SDCARDFS_I(d_inode(parent)); + struct sdcardfs_inode_info *parent_info = SDCARDFS_I(d_inode(parent)); struct sdcardfs_sb_info *sbi = SDCARDFS_SB(dentry->d_sb); struct qstr obb = QSTR_LITERAL("obb"); - if(parent_info->perm == PERM_ANDROID && + if (parent_info->perm == PERM_ANDROID && qstr_case_eq(&dentry->d_name, &obb)) { /* /Android/obb is the base obbpath of DERIVED_UNIFIED */ - if(!(sbi->options.multiuser == false + if (!(sbi->options.multiuser == false && parent_info->userid == 0)) { ret = 1; } @@ -365,17 +369,18 @@ int is_obbpath_invalid(struct dentry *dent) /* check the base obbpath has been changed. * this routine can check an uninitialized obb dentry as well. - * regarding the uninitialized obb, refer to the sdcardfs_mkdir() */ + * regarding the uninitialized obb, refer to the sdcardfs_mkdir() + */ spin_lock(&di->lock); - if(di->orig_path.dentry) { - if(!di->lower_path.dentry) { + if (di->orig_path.dentry) { + if (!di->lower_path.dentry) { ret = 1; } else { path_get(&di->lower_path); //lower_parent = lock_parent(lower_path->dentry); path_buf = kmalloc(PATH_MAX, GFP_ATOMIC); - if(!path_buf) { + if (!path_buf) { ret = 1; printk(KERN_ERR "sdcardfs: fail to allocate path_buf in %s.\n", __func__); } else { @@ -402,13 +407,13 @@ int is_base_obbpath(struct dentry *dentry) { int ret = 0; struct dentry *parent = dget_parent(dentry); - struct sdcardfs_inode_info *parent_info= SDCARDFS_I(d_inode(parent)); + struct sdcardfs_inode_info *parent_info = SDCARDFS_I(d_inode(parent)); struct sdcardfs_sb_info *sbi = SDCARDFS_SB(dentry->d_sb); struct qstr q_obb = QSTR_LITERAL("obb"); spin_lock(&SDCARDFS_D(dentry)->lock); if (sbi->options.multiuser) { - if(parent_info->perm == PERM_PRE_ROOT && + if (parent_info->perm == PERM_PRE_ROOT && qstr_case_eq(&dentry->d_name, &q_obb)) { ret = 1; } @@ -423,7 +428,8 @@ int is_base_obbpath(struct dentry *dentry) /* The lower_path will be stored to the dentry's orig_path * and the base obbpath will be copyed to the lower_path variable. * if an error returned, there's no change in the lower_path - * returns: -ERRNO if error (0: no error) */ + * returns: -ERRNO if error (0: no error) + */ int setup_obb_dentry(struct dentry *dentry, struct path *lower_path) { int err = 0; @@ -438,7 +444,7 @@ int setup_obb_dentry(struct dentry *dentry, struct path *lower_path) err = kern_path(sbi->obbpath_s, LOOKUP_FOLLOW | LOOKUP_DIRECTORY, &obbpath); - if(!err) { + if (!err) { /* the obbpath base has been found */ pathcpy(lower_path, &obbpath); } else { @@ -446,7 +452,8 @@ int setup_obb_dentry(struct dentry *dentry, struct path *lower_path) * setup the lower_path with its orig_path. * but, the current implementation just returns an error * because the sdcard daemon also regards this case as - * a lookup fail. */ + * a lookup fail. + */ printk(KERN_INFO "sdcardfs: the sbi->obbpath is not available\n"); } return err; diff --git a/fs/sdcardfs/file.c b/fs/sdcardfs/file.c index 0f2db26895bf..4c9726606c0f 100644 --- a/fs/sdcardfs/file.c +++ b/fs/sdcardfs/file.c @@ -248,9 +248,8 @@ static int sdcardfs_open(struct inode *inode, struct file *file) if (err) kfree(SDCARDFS_F(file)); - else { + else sdcardfs_copy_and_fix_attrs(inode, sdcardfs_lower_inode(inode)); - } out_revert_cred: REVERT_CRED(saved_cred); diff --git a/fs/sdcardfs/inode.c b/fs/sdcardfs/inode.c index 7ca5b8ab4313..bd05ed88d692 100644 --- a/fs/sdcardfs/inode.c +++ b/fs/sdcardfs/inode.c @@ -240,13 +240,14 @@ static int sdcardfs_symlink(struct inode *dir, struct dentry *dentry, } #endif -static int touch(char *abs_path, mode_t mode) { +static int touch(char *abs_path, mode_t mode) +{ struct file *filp = filp_open(abs_path, O_RDWR|O_CREAT|O_EXCL|O_NOFOLLOW, mode); + if (IS_ERR(filp)) { if (PTR_ERR(filp) == -EEXIST) { return 0; - } - else { + } else { printk(KERN_ERR "sdcardfs: failed to open(%s): %ld\n", abs_path, PTR_ERR(filp)); return PTR_ERR(filp); @@ -315,19 +316,21 @@ static int sdcardfs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode } /* if it is a local obb dentry, setup it with the base obbpath */ - if(need_graft_path(dentry)) { + if (need_graft_path(dentry)) { err = setup_obb_dentry(dentry, &lower_path); - if(err) { + if (err) { /* if the sbi->obbpath is not available, the lower_path won't be * changed by setup_obb_dentry() but the lower path is saved to * its orig_path. this dentry will be revalidated later. - * but now, the lower_path should be NULL */ + * but now, the lower_path should be NULL + */ sdcardfs_put_reset_lower_path(dentry); /* the newly created lower path which saved to its orig_path or * the lower_path is the base obbpath. - * therefore, an additional path_get is required */ + * therefore, an additional path_get is required + */ path_get(&lower_path); } else make_nomedia_in_obb = 1; @@ -391,7 +394,8 @@ static int sdcardfs_rmdir(struct inode *dir, struct dentry *dentry) OVERRIDE_CRED(SDCARDFS_SB(dir->i_sb), saved_cred, SDCARDFS_I(dir)); /* sdcardfs_get_real_lower(): in case of remove an user's obb dentry - * the dentry on the original path should be deleted. */ + * the dentry on the original path should be deleted. + */ sdcardfs_get_real_lower(dentry, &lower_path); lower_dentry = lower_path.dentry; @@ -663,6 +667,7 @@ static int sdcardfs_permission(struct vfsmount *mnt, struct inode *inode, int ma * we check it with AID_MEDIA_RW permission */ struct inode *lower_inode; + OVERRIDE_CRED(SDCARDFS_SB(inode->sb)); lower_inode = sdcardfs_lower_inode(inode); @@ -733,7 +738,8 @@ static int sdcardfs_setattr(struct vfsmount *mnt, struct dentry *dentry, struct /* prepare our own lower struct iattr (with the lower file) */ memcpy(&lower_ia, ia, sizeof(lower_ia)); /* Allow touch updating timestamps. A previous permission check ensures - * we have write access. Changes to mode, owner, and group are ignored*/ + * we have write access. Changes to mode, owner, and group are ignored + */ ia->ia_valid |= ATTR_FORCE; err = setattr_prepare(&tmp_d, ia); @@ -819,10 +825,12 @@ static int sdcardfs_setattr(struct vfsmount *mnt, struct dentry *dentry, struct return err; } -static int sdcardfs_fillattr(struct vfsmount *mnt, struct inode *inode, struct kstat *stat) +static int sdcardfs_fillattr(struct vfsmount *mnt, + struct inode *inode, struct kstat *stat) { struct sdcardfs_inode_info *info = SDCARDFS_I(inode); struct inode *top = grab_top(info); + if (!top) return -EINVAL; diff --git a/fs/sdcardfs/lookup.c b/fs/sdcardfs/lookup.c index 7d26c269da35..6b450c48e5d3 100644 --- a/fs/sdcardfs/lookup.c +++ b/fs/sdcardfs/lookup.c @@ -73,6 +73,7 @@ static int sdcardfs_inode_test(struct inode *inode, void *candidate_data/*void * { struct inode *current_lower_inode = sdcardfs_lower_inode(inode); userid_t current_userid = SDCARDFS_I(inode)->userid; + if (current_lower_inode == ((struct inode_data *)candidate_data)->lower_inode && current_userid == ((struct inode_data *)candidate_data)->id) return 1; /* found a match */ @@ -102,7 +103,7 @@ struct inode *sdcardfs_iget(struct super_block *sb, struct inode *lower_inode, u * instead. */ lower_inode->i_ino, /* hashval */ - sdcardfs_inode_test, /* inode comparison function */ + sdcardfs_inode_test, /* inode comparison function */ sdcardfs_inode_set, /* inode init function */ &data); /* data passed to test+set fxns */ if (!inode) { @@ -213,8 +214,8 @@ struct sdcardfs_name_data { bool found; }; -static int sdcardfs_name_match(struct dir_context *ctx, const char *name, int namelen, - loff_t offset, u64 ino, unsigned int d_type) +static int sdcardfs_name_match(struct dir_context *ctx, const char *name, + int namelen, loff_t offset, u64 ino, unsigned int d_type) { struct sdcardfs_name_data *buf = container_of(ctx, struct sdcardfs_name_data, ctx); struct qstr candidate = QSTR_INIT(name, namelen); @@ -303,23 +304,26 @@ static struct dentry *__sdcardfs_lookup(struct dentry *dentry, if (!err) { /* check if the dentry is an obb dentry * if true, the lower_inode must be replaced with - * the inode of the graft path */ + * the inode of the graft path + */ - if(need_graft_path(dentry)) { + if (need_graft_path(dentry)) { /* setup_obb_dentry() - * The lower_path will be stored to the dentry's orig_path + * The lower_path will be stored to the dentry's orig_path * and the base obbpath will be copyed to the lower_path variable. * if an error returned, there's no change in the lower_path - * returns: -ERRNO if error (0: no error) */ + * returns: -ERRNO if error (0: no error) + */ err = setup_obb_dentry(dentry, &lower_path); - if(err) { + if (err) { /* if the sbi->obbpath is not available, we can optionally * setup the lower_path with its orig_path. * but, the current implementation just returns an error * because the sdcard daemon also regards this case as - * a lookup fail. */ + * a lookup fail. + */ printk(KERN_INFO "sdcardfs: base obbpath is not available\n"); sdcardfs_put_reset_orig_path(dentry); goto out; @@ -374,9 +378,9 @@ static struct dentry *__sdcardfs_lookup(struct dentry *dentry, /* * On success: - * fills dentry object appropriate values and returns NULL. + * fills dentry object appropriate values and returns NULL. * On fail (== error) - * returns error ptr + * returns error ptr * * @dir : Parent inode. * @dentry : Target dentry to lookup. we should set each of fields. @@ -396,7 +400,7 @@ struct dentry *sdcardfs_lookup(struct inode *dir, struct dentry *dentry, if (!check_caller_access_to_name(d_inode(parent), &dentry->d_name)) { ret = ERR_PTR(-EACCES); goto out_err; - } + } /* save current_cred and override it */ OVERRIDE_CRED_PTR(SDCARDFS_SB(dir->i_sb), saved_cred, SDCARDFS_I(dir)); @@ -412,9 +416,7 @@ struct dentry *sdcardfs_lookup(struct inode *dir, struct dentry *dentry, ret = __sdcardfs_lookup(dentry, flags, &lower_parent_path, SDCARDFS_I(dir)->userid); if (IS_ERR(ret)) - { goto out; - } if (ret) dentry = ret; if (d_inode(dentry)) { diff --git a/fs/sdcardfs/main.c b/fs/sdcardfs/main.c index 4e2aded8d1d9..c249bd73b121 100644 --- a/fs/sdcardfs/main.c +++ b/fs/sdcardfs/main.c @@ -72,6 +72,7 @@ static int parse_options(struct super_block *sb, char *options, int silent, while ((p = strsep(&options, ",")) != NULL) { int token; + if (!*p) continue; @@ -148,6 +149,7 @@ int parse_options_remount(struct super_block *sb, char *options, int silent, while ((p = strsep(&options, ",")) != NULL) { int token; + if (!*p) continue; @@ -223,8 +225,8 @@ static struct dentry *sdcardfs_d_alloc_root(struct super_block *sb) #endif DEFINE_MUTEX(sdcardfs_super_list_lock); -LIST_HEAD(sdcardfs_super_list); EXPORT_SYMBOL_GPL(sdcardfs_super_list_lock); +LIST_HEAD(sdcardfs_super_list); EXPORT_SYMBOL_GPL(sdcardfs_super_list); /* @@ -328,14 +330,15 @@ static int sdcardfs_read_super(struct vfsmount *mnt, struct super_block *sb, /* setup permission policy */ sb_info->obbpath_s = kzalloc(PATH_MAX, GFP_KERNEL); mutex_lock(&sdcardfs_super_list_lock); - if(sb_info->options.multiuser) { - setup_derived_state(d_inode(sb->s_root), PERM_PRE_ROOT, sb_info->options.fs_user_id, AID_ROOT, false, d_inode(sb->s_root)); + if (sb_info->options.multiuser) { + setup_derived_state(d_inode(sb->s_root), PERM_PRE_ROOT, + sb_info->options.fs_user_id, AID_ROOT, + false, d_inode(sb->s_root)); snprintf(sb_info->obbpath_s, PATH_MAX, "%s/obb", dev_name); - /*err = prepare_dir(sb_info->obbpath_s, - sb_info->options.fs_low_uid, - sb_info->options.fs_low_gid, 00755);*/ } else { - setup_derived_state(d_inode(sb->s_root), PERM_ROOT, sb_info->options.fs_user_id, AID_ROOT, false, d_inode(sb->s_root)); + setup_derived_state(d_inode(sb->s_root), PERM_ROOT, + sb_info->options.fs_user_id, AID_ROOT, + false, d_inode(sb->s_root)); snprintf(sb_info->obbpath_s, PATH_MAX, "%s/Android/obb", dev_name); } fixup_tmp_permissions(d_inode(sb->s_root)); @@ -368,8 +371,10 @@ static int sdcardfs_read_super(struct vfsmount *mnt, struct super_block *sb, /* A feature which supports mount_nodev() with options */ static struct dentry *mount_nodev_with_options(struct vfsmount *mnt, - struct file_system_type *fs_type, int flags, const char *dev_name, void *data, - int (*fill_super)(struct vfsmount *, struct super_block *, const char *, void *, int)) + struct file_system_type *fs_type, int flags, + const char *dev_name, void *data, + int (*fill_super)(struct vfsmount *, struct super_block *, + const char *, void *, int)) { int error; @@ -401,19 +406,22 @@ static struct dentry *sdcardfs_mount(struct vfsmount *mnt, raw_data, sdcardfs_read_super); } -static struct dentry *sdcardfs_mount_wrn(struct file_system_type *fs_type, int flags, - const char *dev_name, void *raw_data) +static struct dentry *sdcardfs_mount_wrn(struct file_system_type *fs_type, + int flags, const char *dev_name, void *raw_data) { WARN(1, "sdcardfs does not support mount. Use mount2.\n"); return ERR_PTR(-EINVAL); } -void *sdcardfs_alloc_mnt_data(void) { +void *sdcardfs_alloc_mnt_data(void) +{ return kmalloc(sizeof(struct sdcardfs_vfsmount_options), GFP_KERNEL); } -void sdcardfs_kill_sb(struct super_block *sb) { +void sdcardfs_kill_sb(struct super_block *sb) +{ struct sdcardfs_sb_info *sbi; + if (sb->s_magic == SDCARDFS_SUPER_MAGIC) { sbi = SDCARDFS_SB(sb); mutex_lock(&sdcardfs_super_list_lock); diff --git a/fs/sdcardfs/packagelist.c b/fs/sdcardfs/packagelist.c index 7abfeb124fc2..3df39a715a58 100644 --- a/fs/sdcardfs/packagelist.c +++ b/fs/sdcardfs/packagelist.c @@ -90,6 +90,7 @@ static appid_t __get_appid(const struct qstr *key) appid_t get_appid(const char *key) { struct qstr q; + qstr_init(&q, key); return __get_appid(&q); } @@ -115,6 +116,7 @@ static appid_t __get_ext_gid(const struct qstr *key) appid_t get_ext_gid(const char *key) { struct qstr q; + qstr_init(&q, key); return __get_ext_gid(&q); } @@ -145,7 +147,8 @@ appid_t is_excluded(const char *key, userid_t user) /* Kernel has already enforced everything we returned through * derive_permissions_locked(), so this is used to lock down access - * even further, such as enforcing that apps hold sdcard_rw. */ + * even further, such as enforcing that apps hold sdcard_rw. + */ int check_caller_access_to_name(struct inode *parent_node, const struct qstr *name) { struct qstr q_autorun = QSTR_LITERAL("autorun.inf"); @@ -162,26 +165,26 @@ int check_caller_access_to_name(struct inode *parent_node, const struct qstr *na } /* Root always has access; access for any other UIDs should always - * be controlled through packages.list. */ - if (from_kuid(&init_user_ns, current_fsuid()) == 0) { + * be controlled through packages.list. + */ + if (from_kuid(&init_user_ns, current_fsuid()) == 0) return 1; - } /* No extra permissions to enforce */ return 1; } /* This function is used when file opening. The open flags must be - * checked before calling check_caller_access_to_name() */ -int open_flags_to_access_mode(int open_flags) { - if((open_flags & O_ACCMODE) == O_RDONLY) { + * checked before calling check_caller_access_to_name() + */ +int open_flags_to_access_mode(int open_flags) +{ + if ((open_flags & O_ACCMODE) == O_RDONLY) return 0; /* R_OK */ - } else if ((open_flags & O_ACCMODE) == O_WRONLY) { + if ((open_flags & O_ACCMODE) == O_WRONLY) return 1; /* W_OK */ - } else { - /* Probably O_RDRW, but treat as default to be safe */ + /* Probably O_RDRW, but treat as default to be safe */ return 1; /* R_OK | W_OK */ - } } static struct hashtable_entry *alloc_hashtable_entry(const struct qstr *key, @@ -373,7 +376,6 @@ static void remove_packagelist_entry(const struct qstr *key) remove_packagelist_entry_locked(key); fixup_all_perms_name(key); mutex_unlock(&sdcardfs_super_list_lock); - return; } static void remove_ext_gid_entry_locked(const struct qstr *key, gid_t group) @@ -396,7 +398,6 @@ static void remove_ext_gid_entry(const struct qstr *key, gid_t group) mutex_lock(&sdcardfs_super_list_lock); remove_ext_gid_entry_locked(key, group); mutex_unlock(&sdcardfs_super_list_lock); - return; } static void remove_userid_all_entry_locked(userid_t userid) @@ -424,7 +425,6 @@ static void remove_userid_all_entry(userid_t userid) remove_userid_all_entry_locked(userid); fixup_all_perms_userid(userid); mutex_unlock(&sdcardfs_super_list_lock); - return; } static void remove_userid_exclude_entry_locked(const struct qstr *key, userid_t userid) @@ -449,7 +449,6 @@ static void remove_userid_exclude_entry(const struct qstr *key, userid_t userid) remove_userid_exclude_entry_locked(key, userid); fixup_all_perms_name_userid(key, userid); mutex_unlock(&sdcardfs_super_list_lock); - return; } static void packagelist_destroy(void) @@ -458,6 +457,7 @@ static void packagelist_destroy(void) struct hlist_node *h_t; HLIST_HEAD(free_list); int i; + mutex_lock(&sdcardfs_super_list_lock); hash_for_each_rcu(package_to_appid, i, hash_cur, hlist) { hash_del_rcu(&hash_cur->hlist); @@ -605,7 +605,7 @@ static struct configfs_attribute *package_details_attrs[] = { }; static struct configfs_item_operations package_details_item_ops = { - .release = package_details_release, + .release = package_details_release, }; static struct config_item_type package_appid_type = { @@ -661,6 +661,7 @@ static struct config_item *extension_details_make_item(struct config_group *grou struct extension_details *extension_details = kzalloc(sizeof(struct extension_details), GFP_KERNEL); const char *tmp; int ret; + if (!extension_details) return ERR_PTR(-ENOMEM); @@ -715,6 +716,7 @@ static struct config_group *extensions_make_group(struct config_group *group, co static void extensions_drop_group(struct config_group *group, struct config_item *item) { struct extensions_value *value = to_extensions_value(item); + printk(KERN_INFO "sdcardfs: No longer mapping any files to gid %d\n", value->num); kfree(value); } @@ -880,7 +882,7 @@ int packagelist_init(void) } configfs_sdcardfs_init(); - return 0; + return 0; } void packagelist_exit(void) diff --git a/fs/sdcardfs/sdcardfs.h b/fs/sdcardfs/sdcardfs.h index 09ec1e415e6d..34e6f318b60d 100644 --- a/fs/sdcardfs/sdcardfs.h +++ b/fs/sdcardfs/sdcardfs.h @@ -87,11 +87,11 @@ } while (0) /* OVERRIDE_CRED() and REVERT_CRED() - * OVERRID_CRED() - * backup original task->cred - * and modifies task->cred->fsuid/fsgid to specified value. + * OVERRIDE_CRED() + * backup original task->cred + * and modifies task->cred->fsuid/fsgid to specified value. * REVERT_CRED() - * restore original task->cred->fsuid/fsgid. + * restore original task->cred->fsuid/fsgid. * These two macro should be used in pair, and OVERRIDE_CRED() should be * placed at the beginning of a function, right after variable declaration. */ @@ -120,27 +120,29 @@ /* Android 5.0 support */ /* Permission mode for a specific node. Controls how file permissions - * are derived for children nodes. */ + * are derived for children nodes. + */ typedef enum { - /* Nothing special; this node should just inherit from its parent. */ - PERM_INHERIT, - /* This node is one level above a normal root; used for legacy layouts - * which use the first level to represent user_id. */ - PERM_PRE_ROOT, - /* This node is "/" */ - PERM_ROOT, - /* This node is "/Android" */ - PERM_ANDROID, - /* This node is "/Android/data" */ - PERM_ANDROID_DATA, - /* This node is "/Android/obb" */ - PERM_ANDROID_OBB, - /* This node is "/Android/media" */ - PERM_ANDROID_MEDIA, - /* This node is "/Android/[data|media|obb]/[package]" */ - PERM_ANDROID_PACKAGE, - /* This node is "/Android/[data|media|obb]/[package]/cache" */ - PERM_ANDROID_PACKAGE_CACHE, + /* Nothing special; this node should just inherit from its parent. */ + PERM_INHERIT, + /* This node is one level above a normal root; used for legacy layouts + * which use the first level to represent user_id. + */ + PERM_PRE_ROOT, + /* This node is "/" */ + PERM_ROOT, + /* This node is "/Android" */ + PERM_ANDROID, + /* This node is "/Android/data" */ + PERM_ANDROID_DATA, + /* This node is "/Android/obb" */ + PERM_ANDROID_OBB, + /* This node is "/Android/media" */ + PERM_ANDROID_MEDIA, + /* This node is "/Android/[data|media|obb]/[package]" */ + PERM_ANDROID_PACKAGE, + /* This node is "/Android/[data|media|obb]/[package]/cache" */ + PERM_ANDROID_PACKAGE_CACHE, } perm_t; struct sdcardfs_sb_info; @@ -150,7 +152,7 @@ struct sdcardfs_inode_info; /* Do not directly use this function. Use OVERRIDE_CRED() instead. */ const struct cred *override_fsids(struct sdcardfs_sb_info *sbi, struct sdcardfs_inode_info *info); /* Do not directly use this function, use REVERT_CRED() instead. */ -void revert_fsids(const struct cred * old_cred); +void revert_fsids(const struct cred *old_cred); /* operations vectors defined in specific files */ extern const struct file_operations sdcardfs_main_fops; @@ -227,7 +229,8 @@ struct sdcardfs_sb_info { struct super_block *sb; struct super_block *lower_sb; /* derived perm policy : some of options have been added - * to sdcardfs_mount_options (Android 4.4 support) */ + * to sdcardfs_mount_options (Android 4.4 support) + */ struct sdcardfs_mount_options options; spinlock_t lock; /* protects obbpath */ char *obbpath_s; @@ -338,7 +341,7 @@ static inline void sdcardfs_put_reset_##pname(const struct dentry *dent) \ { \ struct path pname; \ spin_lock(&SDCARDFS_D(dent)->lock); \ - if(SDCARDFS_D(dent)->pname.dentry) { \ + if (SDCARDFS_D(dent)->pname.dentry) { \ pathcpy(&pname, &SDCARDFS_D(dent)->pname); \ SDCARDFS_D(dent)->pname.dentry = NULL; \ SDCARDFS_D(dent)->pname.mnt = NULL; \ @@ -354,17 +357,17 @@ SDCARDFS_DENT_FUNC(orig_path) static inline bool sbinfo_has_sdcard_magic(struct sdcardfs_sb_info *sbinfo) { - return sbinfo && sbinfo->sb && sbinfo->sb->s_magic == SDCARDFS_SUPER_MAGIC; + return sbinfo && sbinfo->sb && sbinfo->sb->s_magic == SDCARDFS_SUPER_MAGIC; } /* grab a refererence if we aren't linking to ourself */ static inline void set_top(struct sdcardfs_inode_info *info, struct inode *top) { struct inode *old_top = NULL; + BUG_ON(IS_ERR_OR_NULL(top)); - if (info->top && info->top != &info->vfs_inode) { + if (info->top && info->top != &info->vfs_inode) old_top = info->top; - } if (top != &info->vfs_inode) igrab(top); info->top = top; @@ -374,11 +377,11 @@ static inline void set_top(struct sdcardfs_inode_info *info, struct inode *top) static inline struct inode *grab_top(struct sdcardfs_inode_info *info) { struct inode *top = info->top; - if (top) { + + if (top) return igrab(top); - } else { + else return NULL; - } } static inline void release_top(struct sdcardfs_inode_info *info) @@ -386,21 +389,24 @@ static inline void release_top(struct sdcardfs_inode_info *info) iput(info->top); } -static inline int get_gid(struct vfsmount *mnt, struct sdcardfs_inode_info *info) { +static inline int get_gid(struct vfsmount *mnt, struct sdcardfs_inode_info *info) +{ struct sdcardfs_vfsmount_options *opts = mnt->data; - if (opts->gid == AID_SDCARD_RW) { + if (opts->gid == AID_SDCARD_RW) /* As an optimization, certain trusted system components only run * as owner but operate across all users. Since we're now handing * out the sdcard_rw GID only to trusted apps, we're okay relaxing * the user boundary enforcement for the default view. The UIDs - * assigned to app directories are still multiuser aware. */ + * assigned to app directories are still multiuser aware. + */ return AID_SDCARD_RW; - } else { + else return multiuser_get_uid(info->userid, opts->gid); - } } -static inline int get_mode(struct vfsmount *mnt, struct sdcardfs_inode_info *info) { + +static inline int get_mode(struct vfsmount *mnt, struct sdcardfs_inode_info *info) +{ int owner_mode; int filtered_mode; struct sdcardfs_vfsmount_options *opts = mnt->data; @@ -409,17 +415,18 @@ static inline int get_mode(struct vfsmount *mnt, struct sdcardfs_inode_info *inf if (info->perm == PERM_PRE_ROOT) { /* Top of multi-user view should always be visible to ensure - * secondary users can traverse inside. */ + * secondary users can traverse inside. + */ visible_mode = 0711; } else if (info->under_android) { /* Block "other" access to Android directories, since only apps * belonging to a specific user should be in there; we still - * leave +x open for the default view. */ - if (opts->gid == AID_SDCARD_RW) { + * leave +x open for the default view. + */ + if (opts->gid == AID_SDCARD_RW) visible_mode = visible_mode & ~0006; - } else { + else visible_mode = visible_mode & ~0007; - } } owner_mode = info->lower_inode->i_mode & 0700; filtered_mode = visible_mode & (owner_mode | (owner_mode >> 3) | (owner_mode >> 6)); @@ -444,7 +451,7 @@ static inline void sdcardfs_get_real_lower(const struct dentry *dent, /* in case of a local obb dentry * the orig_path should be returned */ - if(has_graft_path(dent)) + if (has_graft_path(dent)) sdcardfs_get_orig_path(dent, real_lower); else sdcardfs_get_lower_path(dent, real_lower); @@ -453,7 +460,7 @@ static inline void sdcardfs_get_real_lower(const struct dentry *dent, static inline void sdcardfs_put_real_lower(const struct dentry *dent, struct path *real_lower) { - if(has_graft_path(dent)) + if (has_graft_path(dent)) sdcardfs_put_orig_path(dent, real_lower); else sdcardfs_put_lower_path(dent, real_lower); @@ -497,6 +504,7 @@ extern int setup_obb_dentry(struct dentry *dentry, struct path *lower_path); static inline struct dentry *lock_parent(struct dentry *dentry) { struct dentry *dir = dget_parent(dentry); + inode_lock_nested(d_inode(dir), I_MUTEX_PARENT); return dir; } diff --git a/fs/sdcardfs/super.c b/fs/sdcardfs/super.c index edda32b68dc0..a4629f20812e 100644 --- a/fs/sdcardfs/super.c +++ b/fs/sdcardfs/super.c @@ -36,7 +36,7 @@ static void sdcardfs_put_super(struct super_block *sb) if (!spd) return; - if(spd->obbpath_s) { + if (spd->obbpath_s) { kfree(spd->obbpath_s); path_put(&spd->obbpath); } @@ -125,29 +125,33 @@ static int sdcardfs_remount_fs2(struct vfsmount *mnt, struct super_block *sb, * SILENT, but anything else left over is an error. */ if ((*flags & ~(MS_RDONLY | MS_MANDLOCK | MS_SILENT | MS_REMOUNT)) != 0) { - printk(KERN_ERR - "sdcardfs: remount flags 0x%x unsupported\n", *flags); + pr_err("sdcardfs: remount flags 0x%x unsupported\n", *flags); err = -EINVAL; } - printk(KERN_INFO "Remount options were %s for vfsmnt %p.\n", options, mnt); + pr_info("Remount options were %s for vfsmnt %p.\n", options, mnt); err = parse_options_remount(sb, options, *flags & ~MS_SILENT, mnt->data); return err; } -static void* sdcardfs_clone_mnt_data(void *data) { - struct sdcardfs_vfsmount_options* opt = kmalloc(sizeof(struct sdcardfs_vfsmount_options), GFP_KERNEL); - struct sdcardfs_vfsmount_options* old = data; - if(!opt) return NULL; +static void *sdcardfs_clone_mnt_data(void *data) +{ + struct sdcardfs_vfsmount_options *opt = kmalloc(sizeof(struct sdcardfs_vfsmount_options), GFP_KERNEL); + struct sdcardfs_vfsmount_options *old = data; + + if (!opt) + return NULL; opt->gid = old->gid; opt->mask = old->mask; return opt; } -static void sdcardfs_copy_mnt_data(void *data, void *newdata) { - struct sdcardfs_vfsmount_options* old = data; - struct sdcardfs_vfsmount_options* new = newdata; +static void sdcardfs_copy_mnt_data(void *data, void *newdata) +{ + struct sdcardfs_vfsmount_options *old = data; + struct sdcardfs_vfsmount_options *new = newdata; + old->gid = new->gid; old->mask = new->mask; } @@ -235,7 +239,8 @@ static void sdcardfs_umount_begin(struct super_block *sb) lower_sb->s_op->umount_begin(lower_sb); } -static int sdcardfs_show_options(struct vfsmount *mnt, struct seq_file *m, struct dentry *root) +static int sdcardfs_show_options(struct vfsmount *mnt, struct seq_file *m, + struct dentry *root) { struct sdcardfs_sb_info *sbi = SDCARDFS_SB(root->d_sb); struct sdcardfs_mount_options *opts = &sbi->options; From 389bedd86115bb7c5b0eaf5bf98cda653a1de3eb Mon Sep 17 00:00:00 2001 From: Daniel Rosenberg Date: Thu, 16 Mar 2017 19:33:35 -0700 Subject: [PATCH 0345/1103] ANDROID: sdcardfs: Fix style issues with comments Signed-off-by: Daniel Rosenberg Bug: 35331000 Change-Id: I8791ef7eac527645ecb9407908e7e5ece35b8f80 --- fs/sdcardfs/dentry.c | 16 +--------------- fs/sdcardfs/derived_perm.c | 9 +++------ fs/sdcardfs/main.c | 2 +- 3 files changed, 5 insertions(+), 22 deletions(-) diff --git a/fs/sdcardfs/dentry.c b/fs/sdcardfs/dentry.c index 26665da31732..afd97710f7ad 100644 --- a/fs/sdcardfs/dentry.c +++ b/fs/sdcardfs/dentry.c @@ -127,12 +127,10 @@ static int sdcardfs_hash_ci(const struct dentry *dentry, unsigned long hash; name = qstr->name; - //len = vfat_striptail_len(qstr); len = qstr->len; hash = init_name_hash(dentry); while (len--) - //hash = partial_name_hash(nls_tolower(t, *name++), hash); hash = partial_name_hash(tolower(*name++), hash); qstr->hash = end_name_hash(hash); @@ -145,20 +143,8 @@ static int sdcardfs_hash_ci(const struct dentry *dentry, static int sdcardfs_cmp_ci(const struct dentry *dentry, unsigned int len, const char *str, const struct qstr *name) { - /* This function is copy of vfat_cmpi */ - // FIXME Should we support national language? - //struct nls_table *t = MSDOS_SB(parent->d_sb)->nls_io; - //unsigned int alen, blen; + /* FIXME Should we support national language? */ - /* A filename cannot end in '.' or we treat it like it has none */ - /* - alen = vfat_striptail_len(name); - blen = __vfat_striptail_len(len, str); - if (alen == blen) { - if (nls_strnicmp(t, name->name, str, alen) == 0) - return 0; - } - */ if (name->len == len) { if (str_n_case_eq(name->name, str, len)) return 0; diff --git a/fs/sdcardfs/derived_perm.c b/fs/sdcardfs/derived_perm.c index 9de548016a3b..329e91129808 100644 --- a/fs/sdcardfs/derived_perm.c +++ b/fs/sdcardfs/derived_perm.c @@ -325,9 +325,7 @@ inline void update_derived_permission_lock(struct dentry *dentry) * 1. need to check whether the dentry is updated or not * 2. remove the root dentry update */ - if (IS_ROOT(dentry)) { - //setup_default_pre_root_state(d_inode(dentry)); - } else { + if (!IS_ROOT(dentry)) { parent = dget_parent(dentry); if (parent) { get_derived_permission(parent, dentry); @@ -377,7 +375,6 @@ int is_obbpath_invalid(struct dentry *dent) ret = 1; } else { path_get(&di->lower_path); - //lower_parent = lock_parent(lower_path->dentry); path_buf = kmalloc(PATH_MAX, GFP_ATOMIC); if (!path_buf) { @@ -392,7 +389,6 @@ int is_obbpath_invalid(struct dentry *dent) kfree(path_buf); } - //unlock_dir(lower_parent); pathcpy(&lower_path, &di->lower_path); need_put = 1; } @@ -438,7 +434,8 @@ int setup_obb_dentry(struct dentry *dentry, struct path *lower_path) /* A local obb dentry must have its own orig_path to support rmdir * and mkdir of itself. Usually, we expect that the sbi->obbpath - * is avaiable on this stage. */ + * is avaiable on this stage. + */ sdcardfs_set_orig_path(dentry, lower_path); err = kern_path(sbi->obbpath_s, diff --git a/fs/sdcardfs/main.c b/fs/sdcardfs/main.c index c249bd73b121..95cf03379019 100644 --- a/fs/sdcardfs/main.c +++ b/fs/sdcardfs/main.c @@ -29,7 +29,7 @@ enum { Opt_gid, Opt_debug, Opt_mask, - Opt_multiuser, // May need? + Opt_multiuser, Opt_userid, Opt_reserved_mb, Opt_err, From 4e85fddd0216b0817ecf7cc43649ca987fbb04d4 Mon Sep 17 00:00:00 2001 From: Daniel Rosenberg Date: Tue, 21 Mar 2017 16:29:13 -0700 Subject: [PATCH 0346/1103] ANDROID: sdcardfs: remove unneeded null check As pointed out by checkpatch, these functions already handle null inputs, so the checks are not needed. Signed-off-by: Daniel Rosenberg Bug: 35331000 Change-Id: I189342f032dfcefee36b27648bb512488ad61d20 --- fs/sdcardfs/lookup.c | 3 +-- fs/sdcardfs/packagelist.c | 3 +-- fs/sdcardfs/super.c | 3 +-- 3 files changed, 3 insertions(+), 6 deletions(-) diff --git a/fs/sdcardfs/lookup.c b/fs/sdcardfs/lookup.c index 6b450c48e5d3..6de2747b636a 100644 --- a/fs/sdcardfs/lookup.c +++ b/fs/sdcardfs/lookup.c @@ -36,8 +36,7 @@ int sdcardfs_init_dentry_cache(void) void sdcardfs_destroy_dentry_cache(void) { - if (sdcardfs_dentry_cachep) - kmem_cache_destroy(sdcardfs_dentry_cachep); + kmem_cache_destroy(sdcardfs_dentry_cachep); } void free_dentry_private_data(struct dentry *dentry) diff --git a/fs/sdcardfs/packagelist.c b/fs/sdcardfs/packagelist.c index 3df39a715a58..cadb5fa231c1 100644 --- a/fs/sdcardfs/packagelist.c +++ b/fs/sdcardfs/packagelist.c @@ -889,6 +889,5 @@ void packagelist_exit(void) { configfs_sdcardfs_exit(); packagelist_destroy(); - if (hashtable_entry_cachep) - kmem_cache_destroy(hashtable_entry_cachep); + kmem_cache_destroy(hashtable_entry_cachep); } diff --git a/fs/sdcardfs/super.c b/fs/sdcardfs/super.c index a4629f20812e..7d3c331379f5 100644 --- a/fs/sdcardfs/super.c +++ b/fs/sdcardfs/super.c @@ -222,8 +222,7 @@ int sdcardfs_init_inode_cache(void) /* sdcardfs inode cache destructor */ void sdcardfs_destroy_inode_cache(void) { - if (sdcardfs_inode_cachep) - kmem_cache_destroy(sdcardfs_inode_cachep); + kmem_cache_destroy(sdcardfs_inode_cachep); } /* From 6abedb280053c0ef018074509842c955bad9312a Mon Sep 17 00:00:00 2001 From: Daniel Rosenberg Date: Thu, 16 Mar 2017 17:46:13 -0700 Subject: [PATCH 0347/1103] ANDROID: sdcardfs: Use pr_[...] instead of printk Signed-off-by: Daniel Rosenberg Bug: 35331000 Change-Id: Ibc635ec865750530d32b87067779f681fe58a003 --- fs/sdcardfs/derived_perm.c | 6 ++--- fs/sdcardfs/file.c | 9 ++++---- fs/sdcardfs/inode.c | 8 +++---- fs/sdcardfs/lookup.c | 2 +- fs/sdcardfs/main.c | 45 +++++++++++++++++--------------------- fs/sdcardfs/packagelist.c | 13 ++++++----- fs/sdcardfs/sdcardfs.h | 2 +- fs/sdcardfs/super.c | 5 ++--- 8 files changed, 42 insertions(+), 48 deletions(-) diff --git a/fs/sdcardfs/derived_perm.c b/fs/sdcardfs/derived_perm.c index 329e91129808..411a9a9258b6 100644 --- a/fs/sdcardfs/derived_perm.c +++ b/fs/sdcardfs/derived_perm.c @@ -318,7 +318,7 @@ inline void update_derived_permission_lock(struct dentry *dentry) struct dentry *parent; if (!dentry || !d_inode(dentry)) { - printk(KERN_ERR "sdcardfs: %s: invalid dentry\n", __func__); + pr_err("sdcardfs: %s: invalid dentry\n", __func__); return; } /* FIXME: @@ -379,7 +379,7 @@ int is_obbpath_invalid(struct dentry *dent) path_buf = kmalloc(PATH_MAX, GFP_ATOMIC); if (!path_buf) { ret = 1; - printk(KERN_ERR "sdcardfs: fail to allocate path_buf in %s.\n", __func__); + pr_err("sdcardfs: fail to allocate path_buf in %s.\n", __func__); } else { obbpath_s = d_path(&di->lower_path, path_buf, PATH_MAX); if (d_unhashed(di->lower_path.dentry) || @@ -451,7 +451,7 @@ int setup_obb_dentry(struct dentry *dentry, struct path *lower_path) * because the sdcard daemon also regards this case as * a lookup fail. */ - printk(KERN_INFO "sdcardfs: the sbi->obbpath is not available\n"); + pr_info("sdcardfs: the sbi->obbpath is not available\n"); } return err; } diff --git a/fs/sdcardfs/file.c b/fs/sdcardfs/file.c index 4c9726606c0f..eee4eb5896e5 100644 --- a/fs/sdcardfs/file.c +++ b/fs/sdcardfs/file.c @@ -65,7 +65,7 @@ static ssize_t sdcardfs_write(struct file *file, const char __user *buf, /* check disk space */ if (!check_min_free_space(dentry, count, 0)) { - printk(KERN_INFO "No minimum free space.\n"); + pr_err("No minimum free space.\n"); return -ENOSPC; } @@ -160,8 +160,7 @@ static int sdcardfs_mmap(struct file *file, struct vm_area_struct *vma) lower_file = sdcardfs_lower_file(file); if (willwrite && !lower_file->f_mapping->a_ops->writepage) { err = -EINVAL; - printk(KERN_ERR "sdcardfs: lower file system does not " - "support writeable mmap\n"); + pr_err("sdcardfs: lower file system does not support writeable mmap\n"); goto out; } @@ -173,14 +172,14 @@ static int sdcardfs_mmap(struct file *file, struct vm_area_struct *vma) if (!SDCARDFS_F(file)->lower_vm_ops) { err = lower_file->f_op->mmap(lower_file, vma); if (err) { - printk(KERN_ERR "sdcardfs: lower mmap failed %d\n", err); + pr_err("sdcardfs: lower mmap failed %d\n", err); goto out; } saved_vm_ops = vma->vm_ops; /* save: came from lower ->mmap */ err = do_munmap(current->mm, vma->vm_start, vma->vm_end - vma->vm_start); if (err) { - printk(KERN_ERR "sdcardfs: do_munmap failed %d\n", err); + pr_err("sdcardfs: do_munmap failed %d\n", err); goto out; } } diff --git a/fs/sdcardfs/inode.c b/fs/sdcardfs/inode.c index bd05ed88d692..97120e85e31c 100644 --- a/fs/sdcardfs/inode.c +++ b/fs/sdcardfs/inode.c @@ -248,7 +248,7 @@ static int touch(char *abs_path, mode_t mode) if (PTR_ERR(filp) == -EEXIST) { return 0; } else { - printk(KERN_ERR "sdcardfs: failed to open(%s): %ld\n", + pr_err("sdcardfs: failed to open(%s): %ld\n", abs_path, PTR_ERR(filp)); return PTR_ERR(filp); } @@ -284,7 +284,7 @@ static int sdcardfs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode /* check disk space */ if (!check_min_free_space(dentry, 0, 1)) { - printk(KERN_INFO "sdcardfs: No minimum free space.\n"); + pr_err("sdcardfs: No minimum free space.\n"); err = -ENOSPC; goto out_revert; } @@ -360,7 +360,7 @@ static int sdcardfs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode set_fs_pwd(current->fs, &lower_path); touch_err = touch(".nomedia", 0664); if (touch_err) { - printk(KERN_ERR "sdcardfs: failed to create .nomedia in %s: %d\n", + pr_err("sdcardfs: failed to create .nomedia in %s: %d\n", lower_path.dentry->d_name.name, touch_err); goto out; } @@ -649,7 +649,7 @@ static int sdcardfs_permission(struct vfsmount *mnt, struct inode *inode, int ma release_top(SDCARDFS_I(inode)); tmp.i_sb = inode->i_sb; if (IS_POSIXACL(inode)) - printk(KERN_WARNING "%s: This may be undefined behavior... \n", __func__); + pr_warn("%s: This may be undefined behavior...\n", __func__); err = generic_permission(&tmp, mask); /* XXX * Original sdcardfs code calls inode_permission(lower_inode,.. ) diff --git a/fs/sdcardfs/lookup.c b/fs/sdcardfs/lookup.c index 6de2747b636a..f028bfdebeaf 100644 --- a/fs/sdcardfs/lookup.c +++ b/fs/sdcardfs/lookup.c @@ -323,7 +323,7 @@ static struct dentry *__sdcardfs_lookup(struct dentry *dentry, * because the sdcard daemon also regards this case as * a lookup fail. */ - printk(KERN_INFO "sdcardfs: base obbpath is not available\n"); + pr_info("sdcardfs: base obbpath is not available\n"); sdcardfs_put_reset_orig_path(dentry); goto out; } diff --git a/fs/sdcardfs/main.c b/fs/sdcardfs/main.c index 95cf03379019..7344635522cd 100644 --- a/fs/sdcardfs/main.c +++ b/fs/sdcardfs/main.c @@ -117,19 +117,17 @@ static int parse_options(struct super_block *sb, char *options, int silent, break; /* unknown option */ default: - if (!silent) { - printk( KERN_ERR "Unrecognized mount option \"%s\" " - "or missing value", p); - } + if (!silent) + pr_err("Unrecognized mount option \"%s\" or missing value", p); return -EINVAL; } } if (*debug) { - printk( KERN_INFO "sdcardfs : options - debug:%d\n", *debug); - printk( KERN_INFO "sdcardfs : options - uid:%d\n", + pr_info("sdcardfs : options - debug:%d\n", *debug); + pr_info("sdcardfs : options - uid:%d\n", opts->fs_low_uid); - printk( KERN_INFO "sdcardfs : options - gid:%d\n", + pr_info("sdcardfs : options - gid:%d\n", opts->fs_low_gid); } @@ -175,22 +173,20 @@ int parse_options_remount(struct super_block *sb, char *options, int silent, case Opt_fsuid: case Opt_fsgid: case Opt_reserved_mb: - printk( KERN_WARNING "Option \"%s\" can't be changed during remount\n", p); + pr_warn("Option \"%s\" can't be changed during remount\n", p); break; /* unknown option */ default: - if (!silent) { - printk( KERN_ERR "Unrecognized mount option \"%s\" " - "or missing value", p); - } + if (!silent) + pr_err("Unrecognized mount option \"%s\" or missing value", p); return -EINVAL; } } if (debug) { - printk( KERN_INFO "sdcardfs : options - debug:%d\n", debug); - printk( KERN_INFO "sdcardfs : options - gid:%d\n", vfsopts->gid); - printk( KERN_INFO "sdcardfs : options - mask:%d\n", vfsopts->mask); + pr_info("sdcardfs : options - debug:%d\n", debug); + pr_info("sdcardfs : options - gid:%d\n", vfsopts->gid); + pr_info("sdcardfs : options - mask:%d\n", vfsopts->mask); } return 0; @@ -244,31 +240,30 @@ static int sdcardfs_read_super(struct vfsmount *mnt, struct super_block *sb, struct sdcardfs_vfsmount_options *mnt_opt = mnt->data; struct inode *inode; - printk(KERN_INFO "sdcardfs version 2.0\n"); + pr_info("sdcardfs version 2.0\n"); if (!dev_name) { - printk(KERN_ERR - "sdcardfs: read_super: missing dev_name argument\n"); + pr_err("sdcardfs: read_super: missing dev_name argument\n"); err = -EINVAL; goto out; } - printk(KERN_INFO "sdcardfs: dev_name -> %s\n", dev_name); - printk(KERN_INFO "sdcardfs: options -> %s\n", (char *)raw_data); - printk(KERN_INFO "sdcardfs: mnt -> %p\n", mnt); + pr_info("sdcardfs: dev_name -> %s\n", dev_name); + pr_info("sdcardfs: options -> %s\n", (char *)raw_data); + pr_info("sdcardfs: mnt -> %p\n", mnt); /* parse lower path */ err = kern_path(dev_name, LOOKUP_FOLLOW | LOOKUP_DIRECTORY, &lower_path); if (err) { - printk(KERN_ERR "sdcardfs: error accessing lower directory '%s'\n", dev_name); + pr_err("sdcardfs: error accessing lower directory '%s'\n", dev_name); goto out; } /* allocate superblock private data */ sb->s_fs_info = kzalloc(sizeof(struct sdcardfs_sb_info), GFP_KERNEL); if (!SDCARDFS_SB(sb)) { - printk(KERN_CRIT "sdcardfs: read_super: out of memory\n"); + pr_crit("sdcardfs: read_super: out of memory\n"); err = -ENOMEM; goto out_free; } @@ -277,7 +272,7 @@ static int sdcardfs_read_super(struct vfsmount *mnt, struct super_block *sb, /* parse options */ err = parse_options(sb, raw_data, silent, &debug, mnt_opt, &sb_info->options); if (err) { - printk(KERN_ERR "sdcardfs: invalid options\n"); + pr_err("sdcardfs: invalid options\n"); goto out_freesbi; } @@ -347,7 +342,7 @@ static int sdcardfs_read_super(struct vfsmount *mnt, struct super_block *sb, mutex_unlock(&sdcardfs_super_list_lock); if (!silent) - printk(KERN_INFO "sdcardfs: mounted on top of %s type %s\n", + pr_info("sdcardfs: mounted on top of %s type %s\n", dev_name, lower_sb->s_type->name); goto out; /* all is well */ diff --git a/fs/sdcardfs/packagelist.c b/fs/sdcardfs/packagelist.c index cadb5fa231c1..5ea6469638d8 100644 --- a/fs/sdcardfs/packagelist.c +++ b/fs/sdcardfs/packagelist.c @@ -471,7 +471,7 @@ static void packagelist_destroy(void) hlist_for_each_entry_safe(hash_cur, h_t, &free_list, dlist) free_hashtable_entry(hash_cur); mutex_unlock(&sdcardfs_super_list_lock); - printk(KERN_INFO "sdcardfs: destroyed packagelist pkgld\n"); + pr_info("sdcardfs: destroyed packagelist pkgld\n"); } #define SDCARDFS_CONFIGFS_ATTR(_pfx, _name) \ @@ -587,7 +587,8 @@ static ssize_t package_details_clear_userid_store(struct config_item *item, static void package_details_release(struct config_item *item) { struct package_details *package_details = to_package_details(item); - printk(KERN_INFO "sdcardfs: removing %s\n", package_details->name.name); + + pr_info("sdcardfs: removing %s\n", package_details->name.name); remove_packagelist_entry(&package_details->name); kfree(package_details->name.name); kfree(package_details); @@ -639,7 +640,7 @@ static void extension_details_release(struct config_item *item) { struct extension_details *extension_details = to_extension_details(item); - printk(KERN_INFO "sdcardfs: No longer mapping %s files to gid %d\n", + pr_info("sdcardfs: No longer mapping %s files to gid %d\n", extension_details->name.name, extension_details->num); remove_ext_gid_entry(&extension_details->name, extension_details->num); kfree(extension_details->name.name); @@ -717,7 +718,7 @@ static void extensions_drop_group(struct config_group *group, struct config_item { struct extensions_value *value = to_extensions_value(item); - printk(KERN_INFO "sdcardfs: No longer mapping any files to gid %d\n", value->num); + pr_info("sdcardfs: No longer mapping any files to gid %d\n", value->num); kfree(value); } @@ -859,7 +860,7 @@ static int configfs_sdcardfs_init(void) mutex_init(&subsys->su_mutex); ret = configfs_register_subsystem(subsys); if (ret) { - printk(KERN_ERR "Error %d while registering subsystem %s\n", + pr_err("Error %d while registering subsystem %s\n", ret, subsys->su_group.cg_item.ci_namebuf); } @@ -877,7 +878,7 @@ int packagelist_init(void) kmem_cache_create("packagelist_hashtable_entry", sizeof(struct hashtable_entry), 0, 0, NULL); if (!hashtable_entry_cachep) { - printk(KERN_ERR "sdcardfs: failed creating pkgl_hashtable entry slab cache\n"); + pr_err("sdcardfs: failed creating pkgl_hashtable entry slab cache\n"); return -ENOMEM; } diff --git a/fs/sdcardfs/sdcardfs.h b/fs/sdcardfs/sdcardfs.h index 34e6f318b60d..cbeac711c396 100644 --- a/fs/sdcardfs/sdcardfs.h +++ b/fs/sdcardfs/sdcardfs.h @@ -53,7 +53,7 @@ #define SDCARDFS_ROOT_INO 1 /* useful for tracking code reachability */ -#define UDBG printk(KERN_DEFAULT "DBG:%s:%s:%d\n", __FILE__, __func__, __LINE__) +#define UDBG pr_default("DBG:%s:%s:%d\n", __FILE__, __func__, __LINE__) #define SDCARDFS_DIRENT_SIZE 256 diff --git a/fs/sdcardfs/super.c b/fs/sdcardfs/super.c index 7d3c331379f5..67b4ae24337a 100644 --- a/fs/sdcardfs/super.c +++ b/fs/sdcardfs/super.c @@ -64,7 +64,7 @@ static int sdcardfs_statfs(struct dentry *dentry, struct kstatfs *buf) if (sbi->options.reserved_mb) { /* Invalid statfs informations. */ if (buf->f_bsize == 0) { - printk(KERN_ERR "Returned block size is zero.\n"); + pr_err("Returned block size is zero.\n"); return -EINVAL; } @@ -100,8 +100,7 @@ static int sdcardfs_remount_fs(struct super_block *sb, int *flags, char *options * SILENT, but anything else left over is an error. */ if ((*flags & ~(MS_RDONLY | MS_MANDLOCK | MS_SILENT)) != 0) { - printk(KERN_ERR - "sdcardfs: remount flags 0x%x unsupported\n", *flags); + pr_err("sdcardfs: remount flags 0x%x unsupported\n", *flags); err = -EINVAL; } From 4cb79141f57f2f004f6fd9f5dd2d271069aa4864 Mon Sep 17 00:00:00 2001 From: Daniel Rosenberg Date: Thu, 16 Mar 2017 19:32:59 -0700 Subject: [PATCH 0348/1103] ANDROID: sdcardfs: Use to kstrout Switch from deprecated simple_strtoul to kstrout Signed-off-by: Daniel Rosenberg Bug: 35331000 Change-Id: If18bd133b4d2877f71e58b58fc31371ff6613ed5 --- fs/sdcardfs/derived_perm.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/fs/sdcardfs/derived_perm.c b/fs/sdcardfs/derived_perm.c index 411a9a9258b6..14747a83ef7e 100644 --- a/fs/sdcardfs/derived_perm.c +++ b/fs/sdcardfs/derived_perm.c @@ -60,6 +60,8 @@ void get_derived_permission_new(struct dentry *parent, struct dentry *dentry, struct sdcardfs_inode_info *info = SDCARDFS_I(d_inode(dentry)); struct sdcardfs_inode_info *parent_info = SDCARDFS_I(d_inode(parent)); appid_t appid; + unsigned long user_num; + int err; struct qstr q_Android = QSTR_LITERAL("Android"); struct qstr q_data = QSTR_LITERAL("data"); struct qstr q_obb = QSTR_LITERAL("obb"); @@ -88,7 +90,11 @@ void get_derived_permission_new(struct dentry *parent, struct dentry *dentry, case PERM_PRE_ROOT: /* Legacy internal layout places users at top level */ info->perm = PERM_ROOT; - info->userid = simple_strtoul(name->name, NULL, 10); + err = kstrtoul(name->name, 10, &user_num); + if (err) + info->userid = 0; + else + info->userid = user_num; set_top(info, &info->vfs_inode); break; case PERM_ROOT: From 8f9df32ce6a547be95f2f1c0012c3d1dfd1a7fdb Mon Sep 17 00:00:00 2001 From: Daniel Rosenberg Date: Tue, 21 Mar 2017 17:27:40 -0700 Subject: [PATCH 0349/1103] ANDROID: sdcardfs: Use seq_puts over seq_printf Signed-off-by: Daniel Rosenberg Bug: 35331000 Change-Id: I3795ec61ce61e324738815b1ce3b0e09b25d723f --- fs/sdcardfs/super.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/sdcardfs/super.c b/fs/sdcardfs/super.c index 67b4ae24337a..a3393e959c63 100644 --- a/fs/sdcardfs/super.c +++ b/fs/sdcardfs/super.c @@ -251,7 +251,7 @@ static int sdcardfs_show_options(struct vfsmount *mnt, struct seq_file *m, if (vfsopts->gid != 0) seq_printf(m, ",gid=%u", vfsopts->gid); if (opts->multiuser) - seq_printf(m, ",multiuser"); + seq_puts(m, ",multiuser"); if (vfsopts->mask) seq_printf(m, ",mask=%u", vfsopts->mask); if (opts->fs_user_id) From f775676437909e5c807a37fd6278d2a0362d619c Mon Sep 17 00:00:00 2001 From: Daniel Rosenberg Date: Tue, 21 Mar 2017 19:11:38 -0700 Subject: [PATCH 0350/1103] ANDROID: sdcardfs: Fix style issues in macros Signed-off-by: Daniel Rosenberg Bug: 35331000 Change-Id: I89c4035029dc2236081a7685c55cac595d9e7ebf --- fs/sdcardfs/sdcardfs.h | 6 ------ 1 file changed, 6 deletions(-) diff --git a/fs/sdcardfs/sdcardfs.h b/fs/sdcardfs/sdcardfs.h index cbeac711c396..380982b4a567 100644 --- a/fs/sdcardfs/sdcardfs.h +++ b/fs/sdcardfs/sdcardfs.h @@ -111,12 +111,6 @@ #define REVERT_CRED(saved_cred) revert_fsids(saved_cred) -#define DEBUG_CRED() \ - printk("KAKJAGI: %s:%d fsuid %d fsgid %d\n", \ - __FUNCTION__, __LINE__, \ - (int)current->cred->fsuid, \ - (int)current->cred->fsgid); - /* Android 5.0 support */ /* Permission mode for a specific node. Controls how file permissions From 5232261ab996c4eae451964ecf465d22703dcfd1 Mon Sep 17 00:00:00 2001 From: Daniel Rosenberg Date: Thu, 9 Mar 2017 21:14:45 -0800 Subject: [PATCH 0351/1103] ANDROID: sdcardfs: remove unnecessary call to do_munmap Adapted from wrapfs commit 5be6de9ecf02 ("Wrapfs: use vm_munmap in ->mmap") commit 2c9f6014a8bb ("Wrapfs: remove unnecessary call to vm_unmap in ->mmap") Code is unnecessary and causes deadlocks in newer kernels. Signed-off-by: Erez Zadok Signed-off-by: Daniel Rosenberg Bug: 35766959 Change-Id: Ia252d60c60799d7e28fc5f1f0f5b5ec2430a2379 --- fs/sdcardfs/file.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/fs/sdcardfs/file.c b/fs/sdcardfs/file.c index eee4eb5896e5..7e2c50b30782 100644 --- a/fs/sdcardfs/file.c +++ b/fs/sdcardfs/file.c @@ -176,12 +176,6 @@ static int sdcardfs_mmap(struct file *file, struct vm_area_struct *vma) goto out; } saved_vm_ops = vma->vm_ops; /* save: came from lower ->mmap */ - err = do_munmap(current->mm, vma->vm_start, - vma->vm_end - vma->vm_start); - if (err) { - pr_err("sdcardfs: do_munmap failed %d\n", err); - goto out; - } } /* From 64f58365f45d1d3803df40fee0f9b2135c62c649 Mon Sep 17 00:00:00 2001 From: Daniel Rosenberg Date: Thu, 9 Mar 2017 21:24:58 -0800 Subject: [PATCH 0352/1103] ANDROID: sdcardfs: copy lower inode attributes in ->ioctl Adapted from wrapfs commit fbc9c6f83ea6 ("Wrapfs: copy lower inode attributes in ->ioctl") commit e97d8e26cc9e ("Wrapfs: use file_inode helper") Some ioctls (e.g., EXT2_IOC_SETFLAGS) can change inode attributes, so copy them from lower inode. Signed-off-by: Erez Zadok Signed-off-by: Daniel Rosenberg Bug: 35766959 Change-Id: I0f12684b9dbd4088b4a622c7ea9c03087f40e572 --- fs/sdcardfs/file.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/fs/sdcardfs/file.c b/fs/sdcardfs/file.c index 7e2c50b30782..41a550fec3f2 100644 --- a/fs/sdcardfs/file.c +++ b/fs/sdcardfs/file.c @@ -113,6 +113,10 @@ static long sdcardfs_unlocked_ioctl(struct file *file, unsigned int cmd, if (lower_file->f_op->unlocked_ioctl) err = lower_file->f_op->unlocked_ioctl(lower_file, cmd, arg); + /* some ioctls can change inode attributes (EXT2_IOC_SETFLAGS) */ + if (!err) + sdcardfs_copy_and_fix_attrs(file_inode(file), + file_inode(lower_file)); out: return err; } From 7ae3e5536fe11ef436e431a1a7faa5bbc45b82dd Mon Sep 17 00:00:00 2001 From: Daniel Rosenberg Date: Thu, 9 Mar 2017 21:42:01 -0800 Subject: [PATCH 0353/1103] ANDROID: sdcardfs: fix ->llseek to update upper and lower offset Adapted from wrapfs commit 1d1d23a47baa ("Wrapfs: fix ->llseek to update upper and lower offsets") Fixes bug: xfstests generic/257. f_pos consistently is required by and only by dir_ops->wrapfs_readdir, main_ops is not affected. Signed-off-by: Erez Zadok Signed-off-by: Mengyang Li Signed-off-by: Daniel Rosenberg Bug: 35766959 Change-Id: I360a1368ac37ea8966910a58972b81504031d437 --- fs/sdcardfs/file.c | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/fs/sdcardfs/file.c b/fs/sdcardfs/file.c index 41a550fec3f2..f49df7ac370e 100644 --- a/fs/sdcardfs/file.c +++ b/fs/sdcardfs/file.c @@ -316,6 +316,29 @@ static int sdcardfs_fasync(int fd, struct file *file, int flag) return err; } +/* + * Sdcardfs cannot use generic_file_llseek as ->llseek, because it would + * only set the offset of the upper file. So we have to implement our + * own method to set both the upper and lower file offsets + * consistently. + */ +static loff_t sdcardfs_file_llseek(struct file *file, loff_t offset, int whence) +{ + int err; + struct file *lower_file; + + err = generic_file_llseek(file, offset, whence); + if (err < 0) + goto out; + + lower_file = sdcardfs_lower_file(file); + err = generic_file_llseek(lower_file, offset, whence); + +out: + return err; +} + + const struct file_operations sdcardfs_main_fops = { .llseek = generic_file_llseek, .read = sdcardfs_read, @@ -334,7 +357,7 @@ const struct file_operations sdcardfs_main_fops = { /* trimmed directory options */ const struct file_operations sdcardfs_dir_fops = { - .llseek = generic_file_llseek, + .llseek = sdcardfs_file_llseek, .read = generic_read_dir, .iterate = sdcardfs_readdir, .unlocked_ioctl = sdcardfs_unlocked_ioctl, From 1a0d0d5721a740c541cdb870204ce9cffb3cae3e Mon Sep 17 00:00:00 2001 From: Daniel Rosenberg Date: Thu, 9 Mar 2017 21:48:09 -0800 Subject: [PATCH 0354/1103] ANDROID: sdcardfs: add read_iter/write_iter opeations Adapted from wrapfs commit f398bf6a7377 ("Wrapfs: add read_iter/write_iter opeations") Signed-off-by: Erez Zadok Signed-off-by: Mengyang Li Signed-off-by: Daniel Rosenberg Bug: 35766959 Change-Id: I2b3de59c9682fc705bf21df0de6df81e76fd2e40 --- fs/sdcardfs/file.c | 48 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/fs/sdcardfs/file.c b/fs/sdcardfs/file.c index f49df7ac370e..c0146e03fa2e 100644 --- a/fs/sdcardfs/file.c +++ b/fs/sdcardfs/file.c @@ -338,6 +338,52 @@ static loff_t sdcardfs_file_llseek(struct file *file, loff_t offset, int whence) return err; } +/* + * Sdcardfs read_iter, redirect modified iocb to lower read_iter + */ +ssize_t sdcardfs_read_iter(struct kiocb *iocb, struct iov_iter *iter) +{ + int err; + struct file *file = iocb->ki_filp, *lower_file; + + lower_file = sdcardfs_lower_file(file); + if (!lower_file->f_op->read_iter) { + err = -EINVAL; + goto out; + } + + get_file(lower_file); /* prevent lower_file from being released */ + iocb->ki_filp = lower_file; + err = lower_file->f_op->read_iter(iocb, iter); + /* ? wait IO finish to update atime as ecryptfs ? */ + iocb->ki_filp = file; + fput(lower_file); +out: + return err; +} + +/* + * Sdcardfs write_iter, redirect modified iocb to lower write_iter + */ +ssize_t sdcardfs_write_iter(struct kiocb *iocb, struct iov_iter *iter) +{ + int err; + struct file *file = iocb->ki_filp, *lower_file; + + lower_file = sdcardfs_lower_file(file); + if (!lower_file->f_op->write_iter) { + err = -EINVAL; + goto out; + } + + get_file(lower_file); /* prevent lower_file from being released */ + iocb->ki_filp = lower_file; + err = lower_file->f_op->write_iter(iocb, iter); + iocb->ki_filp = file; + fput(lower_file); +out: + return err; +} const struct file_operations sdcardfs_main_fops = { .llseek = generic_file_llseek, @@ -353,6 +399,8 @@ const struct file_operations sdcardfs_main_fops = { .release = sdcardfs_file_release, .fsync = sdcardfs_fsync, .fasync = sdcardfs_fasync, + .read_iter = sdcardfs_read_iter, + .write_iter = sdcardfs_write_iter, }; /* trimmed directory options */ From be50bffb73c2181439b0e053cab756da55cf1bcc Mon Sep 17 00:00:00 2001 From: Daniel Rosenberg Date: Thu, 9 Mar 2017 22:11:08 -0800 Subject: [PATCH 0355/1103] ANDROID: sdcardfs: use d_splice_alias adapted from wrapfs commit 9671770ff8b9 ("Wrapfs: use d_splice_alias") Refactor interpose code to allow lookup to use d_splice_alias. Signed-off-by: Erez Zadok Signed-off-by: Daniel Rosenberg Bug: 35766959 Change-Id: Icf51db8658202c48456724275b03dc77f73f585b --- fs/sdcardfs/lookup.c | 55 +++++++++++++++++++++++++++++++------------- 1 file changed, 39 insertions(+), 16 deletions(-) diff --git a/fs/sdcardfs/lookup.c b/fs/sdcardfs/lookup.c index f028bfdebeaf..f9c02828f6aa 100644 --- a/fs/sdcardfs/lookup.c +++ b/fs/sdcardfs/lookup.c @@ -164,27 +164,25 @@ struct inode *sdcardfs_iget(struct super_block *sb, struct inode *lower_inode, u } /* - * Connect a sdcardfs inode dentry/inode with several lower ones. This is - * the classic stackable file system "vnode interposition" action. - * - * @dentry: sdcardfs's dentry which interposes on lower one - * @sb: sdcardfs's super_block - * @lower_path: the lower path (caller does path_get/put) + * Helper interpose routine, called directly by ->lookup to handle + * spliced dentries. */ -int sdcardfs_interpose(struct dentry *dentry, struct super_block *sb, - struct path *lower_path, userid_t id) +static struct dentry *__sdcardfs_interpose(struct dentry *dentry, + struct super_block *sb, + struct path *lower_path, + userid_t id) { - int err = 0; struct inode *inode; struct inode *lower_inode; struct super_block *lower_sb; + struct dentry *ret_dentry; lower_inode = d_inode(lower_path->dentry); lower_sb = sdcardfs_lower_super(sb); /* check that the lower file system didn't cross a mount point */ if (lower_inode->i_sb != lower_sb) { - err = -EXDEV; + ret_dentry = ERR_PTR(-EXDEV); goto out; } @@ -196,14 +194,32 @@ int sdcardfs_interpose(struct dentry *dentry, struct super_block *sb, /* inherit lower inode number for sdcardfs's inode */ inode = sdcardfs_iget(sb, lower_inode, id); if (IS_ERR(inode)) { - err = PTR_ERR(inode); + ret_dentry = ERR_CAST(inode); goto out; } - d_add(dentry, inode); + ret_dentry = d_splice_alias(inode, dentry); + dentry = ret_dentry ?: dentry; update_derived_permission_lock(dentry); out: - return err; + return ret_dentry; +} + +/* + * Connect an sdcardfs inode dentry/inode with several lower ones. This is + * the classic stackable file system "vnode interposition" action. + * + * @dentry: sdcardfs's dentry which interposes on lower one + * @sb: sdcardfs's super_block + * @lower_path: the lower path (caller does path_get/put) + */ +int sdcardfs_interpose(struct dentry *dentry, struct super_block *sb, + struct path *lower_path, userid_t id) +{ + struct dentry *ret_dentry; + + ret_dentry = __sdcardfs_interpose(dentry, sb, lower_path, id); + return PTR_ERR(ret_dentry); } struct sdcardfs_name_data { @@ -244,6 +260,7 @@ static struct dentry *__sdcardfs_lookup(struct dentry *dentry, const struct qstr *name; struct path lower_path; struct qstr dname; + struct dentry *ret_dentry = NULL; struct sdcardfs_sb_info *sbi; sbi = SDCARDFS_SB(dentry->d_sb); @@ -330,9 +347,13 @@ static struct dentry *__sdcardfs_lookup(struct dentry *dentry, } sdcardfs_set_lower_path(dentry, &lower_path); - err = sdcardfs_interpose(dentry, dentry->d_sb, &lower_path, id); - if (err) /* path_put underlying path on error */ + ret_dentry = + __sdcardfs_interpose(dentry, dentry->d_sb, &lower_path, id); + if (IS_ERR(ret_dentry)) { + err = PTR_ERR(ret_dentry); + /* path_put underlying path on error */ sdcardfs_put_reset_lower_path(dentry); + } goto out; } @@ -372,7 +393,9 @@ static struct dentry *__sdcardfs_lookup(struct dentry *dentry, err = 0; out: - return ERR_PTR(err); + if (err) + return ERR_PTR(err); + return ret_dentry; } /* From a46ed28dae3b6db3104fbb6b3039c5932d419d0d Mon Sep 17 00:00:00 2001 From: Daniel Rosenberg Date: Thu, 9 Mar 2017 20:56:05 -0800 Subject: [PATCH 0356/1103] ANDROID: sdcardfs: update module info Signed-off-by: Daniel Rosenberg Change-Id: I958c7c226d4e9265fea8996803e5b004fb33d8ad --- fs/sdcardfs/main.c | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/fs/sdcardfs/main.c b/fs/sdcardfs/main.c index 7344635522cd..953d2156d2e9 100644 --- a/fs/sdcardfs/main.c +++ b/fs/sdcardfs/main.c @@ -471,10 +471,15 @@ static void __exit exit_sdcardfs_fs(void) pr_info("Completed sdcardfs module unload\n"); } -MODULE_AUTHOR("Erez Zadok, Filesystems and Storage Lab, Stony Brook University" - " (http://www.fsl.cs.sunysb.edu/)"); -MODULE_DESCRIPTION("Wrapfs " SDCARDFS_VERSION - " (http://wrapfs.filesystems.org/)"); +/* Original wrapfs authors */ +MODULE_AUTHOR("Erez Zadok, Filesystems and Storage Lab, Stony Brook University (http://www.fsl.cs.sunysb.edu/)"); + +/* Original sdcardfs authors */ +MODULE_AUTHOR("Woojoong Lee, Daeho Jeong, Kitae Lee, Yeongjin Gil System Memory Lab., Samsung Electronics"); + +/* Current maintainer */ +MODULE_AUTHOR("Daniel Rosenberg, Google"); +MODULE_DESCRIPTION("Sdcardfs " SDCARDFS_VERSION); MODULE_LICENSE("GPL"); module_init(init_sdcardfs_fs); From dd4cc961092d2b4f18ef48aecccccc749052dd43 Mon Sep 17 00:00:00 2001 From: Daniel Rosenberg Date: Mon, 10 Apr 2017 20:54:30 -0700 Subject: [PATCH 0357/1103] ANDROID: sdcardfs: Directly pass lower file for mmap Instead of relying on a copy hack, pass the lower file as private data. This lets the kernel find the vma mapping for pages used by the file, allowing pages used by mapping to be reclaimed. This is adapted from following esdfs patches commit 0647e638d: ("esdfs: store lower file in vm_file for mmap") commit 064850866: ("esdfs: keep a counter for mmaped file") Change-Id: I75b74d1e5061db1b8c13be38d184e118c0851a1a Signed-off-by: Daniel Rosenberg --- fs/sdcardfs/file.c | 3 +++ fs/sdcardfs/mmap.c | 57 ++++++++++++++++++---------------------------- 2 files changed, 25 insertions(+), 35 deletions(-) diff --git a/fs/sdcardfs/file.c b/fs/sdcardfs/file.c index c0146e03fa2e..1f6921e2ffbf 100644 --- a/fs/sdcardfs/file.c +++ b/fs/sdcardfs/file.c @@ -192,6 +192,9 @@ static int sdcardfs_mmap(struct file *file, struct vm_area_struct *vma) file->f_mapping->a_ops = &sdcardfs_aops; /* set our aops */ if (!SDCARDFS_F(file)->lower_vm_ops) /* save for our ->fault */ SDCARDFS_F(file)->lower_vm_ops = saved_vm_ops; + vma->vm_private_data = file; + get_file(lower_file); + vma->vm_file = lower_file; out: return err; diff --git a/fs/sdcardfs/mmap.c b/fs/sdcardfs/mmap.c index 51266f517fe2..391d2a7d10e9 100644 --- a/fs/sdcardfs/mmap.c +++ b/fs/sdcardfs/mmap.c @@ -23,60 +23,45 @@ static int sdcardfs_fault(struct vm_area_struct *vma, struct vm_fault *vmf) { int err; - struct file *file, *lower_file; + struct file *file; const struct vm_operations_struct *lower_vm_ops; - struct vm_area_struct lower_vma; - memcpy(&lower_vma, vma, sizeof(struct vm_area_struct)); - file = lower_vma.vm_file; + file = (struct file *)vma->vm_private_data; lower_vm_ops = SDCARDFS_F(file)->lower_vm_ops; BUG_ON(!lower_vm_ops); - lower_file = sdcardfs_lower_file(file); - /* - * XXX: vm_ops->fault may be called in parallel. Because we have to - * resort to temporarily changing the vma->vm_file to point to the - * lower file, a concurrent invocation of sdcardfs_fault could see a - * different value. In this workaround, we keep a different copy of - * the vma structure in our stack, so we never expose a different - * value of the vma->vm_file called to us, even temporarily. A - * better fix would be to change the calling semantics of ->fault to - * take an explicit file pointer. - */ - lower_vma.vm_file = lower_file; - err = lower_vm_ops->fault(&lower_vma, vmf); + err = lower_vm_ops->fault(vma, vmf); return err; } +static void sdcardfs_vm_open(struct vm_area_struct *vma) +{ + struct file *file = (struct file *)vma->vm_private_data; + + get_file(file); +} + +static void sdcardfs_vm_close(struct vm_area_struct *vma) +{ + struct file *file = (struct file *)vma->vm_private_data; + + fput(file); +} + static int sdcardfs_page_mkwrite(struct vm_area_struct *vma, struct vm_fault *vmf) { int err = 0; - struct file *file, *lower_file; + struct file *file; const struct vm_operations_struct *lower_vm_ops; - struct vm_area_struct lower_vma; - memcpy(&lower_vma, vma, sizeof(struct vm_area_struct)); - file = lower_vma.vm_file; + file = (struct file *)vma->vm_private_data; lower_vm_ops = SDCARDFS_F(file)->lower_vm_ops; BUG_ON(!lower_vm_ops); if (!lower_vm_ops->page_mkwrite) goto out; - lower_file = sdcardfs_lower_file(file); - /* - * XXX: vm_ops->page_mkwrite may be called in parallel. - * Because we have to resort to temporarily changing the - * vma->vm_file to point to the lower file, a concurrent - * invocation of sdcardfs_page_mkwrite could see a different - * value. In this workaround, we keep a different copy of the - * vma structure in our stack, so we never expose a different - * value of the vma->vm_file called to us, even temporarily. - * A better fix would be to change the calling semantics of - * ->page_mkwrite to take an explicit file pointer. - */ - lower_vma.vm_file = lower_file; - err = lower_vm_ops->page_mkwrite(&lower_vma, vmf); + err = lower_vm_ops->page_mkwrite(vma, vmf); out: return err; } @@ -98,4 +83,6 @@ const struct address_space_operations sdcardfs_aops = { const struct vm_operations_struct sdcardfs_vm_ops = { .fault = sdcardfs_fault, .page_mkwrite = sdcardfs_page_mkwrite, + .open = sdcardfs_vm_open, + .close = sdcardfs_vm_close, }; From 9a7b724e8761464eb8f1e130023b6f3146a78c18 Mon Sep 17 00:00:00 2001 From: Daniel Rosenberg Date: Mon, 17 Apr 2017 17:11:38 -0700 Subject: [PATCH 0358/1103] ANDROID: sdcardfs: Change cache GID value Change-Id: Ieb955dd26493da26a458bc20fbbe75bca32b094f Signed-off-by: Daniel Rosenberg Bug: 37193650 --- fs/sdcardfs/derived_perm.c | 2 +- fs/sdcardfs/multiuser.h | 6 ++++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/fs/sdcardfs/derived_perm.c b/fs/sdcardfs/derived_perm.c index 14747a83ef7e..10153339288a 100644 --- a/fs/sdcardfs/derived_perm.c +++ b/fs/sdcardfs/derived_perm.c @@ -222,7 +222,7 @@ void fixup_lower_ownership(struct dentry *dentry, const char *name) break; case PERM_ANDROID_PACKAGE_CACHE: if (info->d_uid != 0) - gid = multiuser_get_cache_gid(info->d_uid); + gid = multiuser_get_ext_cache_gid(info->d_uid); else gid = multiuser_get_uid(info->userid, uid); break; diff --git a/fs/sdcardfs/multiuser.h b/fs/sdcardfs/multiuser.h index 2e89b5872314..d0c925cda299 100644 --- a/fs/sdcardfs/multiuser.h +++ b/fs/sdcardfs/multiuser.h @@ -23,6 +23,8 @@ #define AID_APP_END 19999 /* last app user */ #define AID_CACHE_GID_START 20000 /* start of gids for apps to mark cached data */ #define AID_EXT_GID_START 30000 /* start of gids for apps to mark external data */ +#define AID_EXT_CACHE_GID_START 40000 /* start of gids for apps to mark external cached data */ +#define AID_EXT_CACHE_GID_END 49999 /* end of gids for apps to mark external cached data */ #define AID_SHARED_GID_START 50000 /* start of gids for apps in each user to share */ typedef uid_t userid_t; @@ -33,9 +35,9 @@ static inline uid_t multiuser_get_uid(userid_t user_id, appid_t app_id) return (user_id * AID_USER_OFFSET) + (app_id % AID_USER_OFFSET); } -static inline gid_t multiuser_get_cache_gid(uid_t uid) +static inline gid_t multiuser_get_ext_cache_gid(uid_t uid) { - return uid - AID_APP_START + AID_CACHE_GID_START; + return uid - AID_APP_START + AID_EXT_CACHE_GID_START; } static inline gid_t multiuser_get_ext_gid(uid_t uid) From ee6d002e907f556c216c04c7fdbaa53eed6e6a6a Mon Sep 17 00:00:00 2001 From: Daniel Rosenberg Date: Tue, 18 Apr 2017 12:45:48 -0700 Subject: [PATCH 0359/1103] ANDROID: sdcardfs: ->iget fixes Adapted from wrapfs commit 8c49eaa0sb9c ("Wrapfs: ->iget fixes") Change where we igrab/iput to ensure we always hold a valid lower_inode. Return ENOMEM (not EACCES) if iget5_locked returns NULL. Signed-off-by: Erez Zadok Signed-off-by: Daniel Rosenberg Bug: 35766959 Change-Id: Id8d4e0c0cbc685a0a77685ce73c923e9a3ddc094 --- fs/sdcardfs/lookup.c | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/fs/sdcardfs/lookup.c b/fs/sdcardfs/lookup.c index f9c02828f6aa..19154b77b0fc 100644 --- a/fs/sdcardfs/lookup.c +++ b/fs/sdcardfs/lookup.c @@ -91,7 +91,9 @@ struct inode *sdcardfs_iget(struct super_block *sb, struct inode *lower_inode, u struct sdcardfs_inode_info *info; struct inode_data data; struct inode *inode; /* the new inode to return */ - int err; + + if (!igrab(lower_inode)) + return ERR_PTR(-ESTALE); data.id = id; data.lower_inode = lower_inode; @@ -106,22 +108,19 @@ struct inode *sdcardfs_iget(struct super_block *sb, struct inode *lower_inode, u sdcardfs_inode_set, /* inode init function */ &data); /* data passed to test+set fxns */ if (!inode) { - err = -EACCES; iput(lower_inode); - return ERR_PTR(err); + return ERR_PTR(-ENOMEM); } - /* if found a cached inode, then just return it */ - if (!(inode->i_state & I_NEW)) + /* if found a cached inode, then just return it (after iput) */ + if (!(inode->i_state & I_NEW)) { + iput(lower_inode); return inode; + } /* initialize new inode */ info = SDCARDFS_I(inode); inode->i_ino = lower_inode->i_ino; - if (!igrab(lower_inode)) { - err = -ESTALE; - return ERR_PTR(err); - } sdcardfs_set_lower_inode(inode, lower_inode); inode->i_version++; From 2c4b398490a206424c58964e17351f3782a1d30e Mon Sep 17 00:00:00 2001 From: Daniel Rosenberg Date: Tue, 18 Apr 2017 22:25:15 -0700 Subject: [PATCH 0360/1103] ANDROID: sdcardfs: Don't do d_add for lower fs For file based encryption, ext4 explicitly does not create negative dentries for encrypted files. If you force one over it, the decrypted file will be hidden until the cache is cleared. Instead, just fail out. Signed-off-by: Daniel Rosenberg Bug: 37231161 Change-Id: Id2a9708dfa75e1c22f89915c529789caadd2ca4b --- fs/sdcardfs/lookup.c | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/fs/sdcardfs/lookup.c b/fs/sdcardfs/lookup.c index 19154b77b0fc..126e233654fe 100644 --- a/fs/sdcardfs/lookup.c +++ b/fs/sdcardfs/lookup.c @@ -368,17 +368,15 @@ static struct dentry *__sdcardfs_lookup(struct dentry *dentry, dname.len = name->len; dname.hash = full_name_hash(lower_dir_dentry, dname.name, dname.len); lower_dentry = d_lookup(lower_dir_dentry, &dname); - if (lower_dentry) - goto setup_lower; - - lower_dentry = d_alloc(lower_dir_dentry, &dname); if (!lower_dentry) { - err = -ENOMEM; + /* We called vfs_path_lookup earlier, and did not get a negative + * dentry then. Don't confuse the lower filesystem by forcing + * one on it now... + */ + err = -ENOENT; goto out; } - d_add(lower_dentry, NULL); /* instantiate and hash */ -setup_lower: lower_path.dentry = lower_dentry; lower_path.mnt = mntget(lower_dir_mnt); sdcardfs_set_lower_path(dentry, &lower_path); From 6f16bc29b086fc60d0957c9dee1f9c49485dd9b0 Mon Sep 17 00:00:00 2001 From: Daniel Rosenberg Date: Tue, 18 Apr 2017 22:49:38 -0700 Subject: [PATCH 0361/1103] ANDROID: sdcardfs: Don't complain in fixup_lower_ownership Not all filesystems support changing the owner of a file. We shouldn't complain if it doesn't happen. Signed-off-by: Daniel Rosenberg Bug: 37488099 Change-Id: I403e44ab7230f176e6df82f6adb4e5c82ce57f33 --- fs/sdcardfs/derived_perm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/sdcardfs/derived_perm.c b/fs/sdcardfs/derived_perm.c index 10153339288a..29645276c734 100644 --- a/fs/sdcardfs/derived_perm.c +++ b/fs/sdcardfs/derived_perm.c @@ -252,7 +252,7 @@ void fixup_lower_ownership(struct dentry *dentry, const char *name) goto retry_deleg; } if (error) - pr_err("sdcardfs: Failed to touch up lower fs gid/uid.\n"); + pr_debug("sdcardfs: Failed to touch up lower fs gid/uid for %s\n", name); } sdcardfs_put_lower_path(dentry, &path); } From 6c1e0a40ff4b5f385db727c03ff55b7c980da6a1 Mon Sep 17 00:00:00 2001 From: Daniel Rosenberg Date: Thu, 20 Apr 2017 18:05:02 -0700 Subject: [PATCH 0362/1103] ANDROID: sdcardfs: Use filesystem specific hash We weren't accounting for FS specific hash functions, causing us to miss negative dentries for any FS that had one. Similar to a patch from esdfs commit 75bd25a9476d ("esdfs: support lower's own hash") Signed-off-by: Daniel Rosenberg Change-Id: I32d1ba304d728e0ca2648cacfb4c2e441ae63608 --- fs/sdcardfs/lookup.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/fs/sdcardfs/lookup.c b/fs/sdcardfs/lookup.c index 126e233654fe..7d7c4515539b 100644 --- a/fs/sdcardfs/lookup.c +++ b/fs/sdcardfs/lookup.c @@ -366,8 +366,14 @@ static struct dentry *__sdcardfs_lookup(struct dentry *dentry, /* instatiate a new negative dentry */ dname.name = name->name; dname.len = name->len; - dname.hash = full_name_hash(lower_dir_dentry, dname.name, dname.len); - lower_dentry = d_lookup(lower_dir_dentry, &dname); + + /* See if the low-level filesystem might want + * to use its own hash + */ + lower_dentry = d_hash_and_lookup(lower_dir_dentry, &dname); + if (IS_ERR(lower_dentry)) + return lower_dentry; + if (!lower_dentry) { /* We called vfs_path_lookup earlier, and did not get a negative * dentry then. Don't confuse the lower filesystem by forcing From 55c9868a339ef13f773d91599388bbf4c2260bda Mon Sep 17 00:00:00 2001 From: Daniel Rosenberg Date: Mon, 24 Apr 2017 16:10:21 -0700 Subject: [PATCH 0363/1103] ANDROID: sdcardfs: Copy meta-data from lower inode From wrapfs commit 3ee9b365e38c ("Wrapfs: properly copy meta-data after AIO operations from lower inode") Signed-off-by: Daniel Rosenberg Bug: 35766959 Change-Id: I9a789222e27a17b8d85ce61c45397d1839f9a675 --- fs/sdcardfs/file.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/fs/sdcardfs/file.c b/fs/sdcardfs/file.c index 1f6921e2ffbf..6076c342dae6 100644 --- a/fs/sdcardfs/file.c +++ b/fs/sdcardfs/file.c @@ -358,9 +358,12 @@ ssize_t sdcardfs_read_iter(struct kiocb *iocb, struct iov_iter *iter) get_file(lower_file); /* prevent lower_file from being released */ iocb->ki_filp = lower_file; err = lower_file->f_op->read_iter(iocb, iter); - /* ? wait IO finish to update atime as ecryptfs ? */ iocb->ki_filp = file; fput(lower_file); + /* update upper inode atime as needed */ + if (err >= 0 || err == -EIOCBQUEUED) + fsstack_copy_attr_atime(file->f_path.dentry->d_inode, + file_inode(lower_file)); out: return err; } @@ -384,6 +387,13 @@ ssize_t sdcardfs_write_iter(struct kiocb *iocb, struct iov_iter *iter) err = lower_file->f_op->write_iter(iocb, iter); iocb->ki_filp = file; fput(lower_file); + /* update upper inode times/sizes as needed */ + if (err >= 0 || err == -EIOCBQUEUED) { + fsstack_copy_inode_size(file->f_path.dentry->d_inode, + file_inode(lower_file)); + fsstack_copy_attr_times(file->f_path.dentry->d_inode, + file_inode(lower_file)); + } out: return err; } From 1a70026c07ea8dd888d4685b18dcfc559c93ac0e Mon Sep 17 00:00:00 2001 From: Daniel Rosenberg Date: Mon, 24 Apr 2017 16:11:03 -0700 Subject: [PATCH 0364/1103] ANDROID: sdcardfs: Avoid setting GIDs outside of valid ranges When setting up the ownership of files on the lower filesystem, ensure that these values are in reasonable ranges for apps. If they aren't, default to AID_MEDIA_RW Signed-off-by: Daniel Rosenberg Bug: 37516160 Change-Id: I0bec76a61ac72aff0b993ab1ad04be8382178a00 --- fs/sdcardfs/derived_perm.c | 8 ++++---- fs/sdcardfs/multiuser.h | 7 +++++++ 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/fs/sdcardfs/derived_perm.c b/fs/sdcardfs/derived_perm.c index 29645276c734..5a0ef3889846 100644 --- a/fs/sdcardfs/derived_perm.c +++ b/fs/sdcardfs/derived_perm.c @@ -215,16 +215,16 @@ void fixup_lower_ownership(struct dentry *dentry, const char *name) gid = AID_MEDIA_OBB; break; case PERM_ANDROID_PACKAGE: - if (info->d_uid != 0) + if (uid_is_app(info->d_uid)) gid = multiuser_get_ext_gid(info->d_uid); else - gid = multiuser_get_uid(info->userid, uid); + gid = multiuser_get_uid(info->userid, AID_MEDIA_RW); break; case PERM_ANDROID_PACKAGE_CACHE: - if (info->d_uid != 0) + if (uid_is_app(info->d_uid)) gid = multiuser_get_ext_cache_gid(info->d_uid); else - gid = multiuser_get_uid(info->userid, uid); + gid = multiuser_get_uid(info->userid, AID_MEDIA_RW); break; case PERM_PRE_ROOT: default: diff --git a/fs/sdcardfs/multiuser.h b/fs/sdcardfs/multiuser.h index d0c925cda299..85341e753f8c 100644 --- a/fs/sdcardfs/multiuser.h +++ b/fs/sdcardfs/multiuser.h @@ -35,6 +35,13 @@ static inline uid_t multiuser_get_uid(userid_t user_id, appid_t app_id) return (user_id * AID_USER_OFFSET) + (app_id % AID_USER_OFFSET); } +static inline bool uid_is_app(uid_t uid) +{ + appid_t appid = uid % AID_USER_OFFSET; + + return appid >= AID_APP_START && appid <= AID_APP_END; +} + static inline gid_t multiuser_get_ext_cache_gid(uid_t uid) { return uid - AID_APP_START + AID_EXT_CACHE_GID_START; From 8c68bbe840cbdf6577863066d5eee3df4124ce71 Mon Sep 17 00:00:00 2001 From: Daniel Rosenberg Date: Mon, 24 Apr 2017 19:49:02 -0700 Subject: [PATCH 0365/1103] ANDROID: sdcardfs: Call lower fs's revalidate We should be calling the lower filesystem's revalidate inside of sdcardfs's revalidate, as wrapfs does. Signed-off-by: Daniel Rosenberg Bug: 35766959 Change-Id: I939d1c4192fafc1e21678aeab43fe3d588b8e2f4 --- fs/sdcardfs/dentry.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/fs/sdcardfs/dentry.c b/fs/sdcardfs/dentry.c index afd97710f7ad..ae2b4babe2e5 100644 --- a/fs/sdcardfs/dentry.c +++ b/fs/sdcardfs/dentry.c @@ -60,6 +60,14 @@ static int sdcardfs_d_revalidate(struct dentry *dentry, unsigned int flags) lower_dentry = lower_path.dentry; lower_cur_parent_dentry = dget_parent(lower_dentry); + if ((lower_dentry->d_flags & DCACHE_OP_REVALIDATE)) { + err = lower_dentry->d_op->d_revalidate(lower_dentry, flags); + if (err == 0) { + d_drop(dentry); + goto out; + } + } + spin_lock(&lower_dentry->d_lock); if (d_unhashed(lower_dentry)) { spin_unlock(&lower_dentry->d_lock); From 62281e6a62f08c5eca5be05fc8e554a5cf645f0a Mon Sep 17 00:00:00 2001 From: Daniel Roseberg Date: Tue, 9 May 2017 13:36:35 -0700 Subject: [PATCH 0366/1103] ANDROID: sdcardfs: Don't iput if we didn't igrab If we fail to get top, top is either NULL, or igrab found that we're in the process of freeing that inode, and did not grab it. Either way, we didn't grab it, and have no business putting it. Signed-off-by: Daniel Rosenberg Bug: 38117720 Change-Id: Ie2f587483b9abb5144263156a443e89bc69b767b --- fs/sdcardfs/inode.c | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/fs/sdcardfs/inode.c b/fs/sdcardfs/inode.c index 97120e85e31c..1beda0c7772b 100644 --- a/fs/sdcardfs/inode.c +++ b/fs/sdcardfs/inode.c @@ -625,11 +625,8 @@ static int sdcardfs_permission(struct vfsmount *mnt, struct inode *inode, int ma if (IS_ERR(mnt)) return PTR_ERR(mnt); - if (!top) { - release_top(SDCARDFS_I(inode)); - WARN(1, "Top value was null!\n"); + if (!top) return -EINVAL; - } /* * Permission check on sdcardfs inode. @@ -704,10 +701,8 @@ static int sdcardfs_setattr(struct vfsmount *mnt, struct dentry *dentry, struct inode = d_inode(dentry); top = grab_top(SDCARDFS_I(inode)); - if (!top) { - release_top(SDCARDFS_I(inode)); + if (!top) return -EINVAL; - } /* * Permission check on sdcardfs inode. From 85a0208389c1e639355e363673223699d12c2482 Mon Sep 17 00:00:00 2001 From: Gao Xiang Date: Wed, 10 May 2017 23:01:15 +0800 Subject: [PATCH 0367/1103] ANDROID: sdcardfs: fix sdcardfs_destroy_inode for the inode RCU approach According to the following commits, fs: icache RCU free inodes vfs: fix the stupidity with i_dentry in inode destructors sdcardfs_destroy_inode should be fixed for the fast path safety. Signed-off-by: Gao Xiang Change-Id: I84f43c599209d23737c7e28b499dd121cb43636d --- fs/sdcardfs/super.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/fs/sdcardfs/super.c b/fs/sdcardfs/super.c index a3393e959c63..8a9c9c7adca2 100644 --- a/fs/sdcardfs/super.c +++ b/fs/sdcardfs/super.c @@ -192,11 +192,18 @@ static struct inode *sdcardfs_alloc_inode(struct super_block *sb) return &i->vfs_inode; } -static void sdcardfs_destroy_inode(struct inode *inode) +static void i_callback(struct rcu_head *head) { + struct inode *inode = container_of(head, struct inode, i_rcu); + kmem_cache_free(sdcardfs_inode_cachep, SDCARDFS_I(inode)); } +static void sdcardfs_destroy_inode(struct inode *inode) +{ + call_rcu(&inode->i_rcu, i_callback); +} + /* sdcardfs inode cache constructor */ static void init_once(void *obj) { From a24d7261fd2106e3c44e775fec584c14de7858bd Mon Sep 17 00:00:00 2001 From: Daniel Rosenberg Date: Mon, 15 May 2017 14:03:15 -0700 Subject: [PATCH 0368/1103] ANDROID: sdcardfs: Move top to its own struct Move top, and the associated data, to its own struct. This way, we can properly track refcounts on top without interfering with the inode's accounting. Signed-off-by: Daniel Rosenberg Bug: 38045152 Change-Id: I1968e480d966c3f234800b72e43670ca11e1d3fd --- fs/sdcardfs/dentry.c | 15 +++++ fs/sdcardfs/derived_perm.c | 130 +++++++++++++++++++------------------ fs/sdcardfs/inode.c | 54 ++++++++------- fs/sdcardfs/lookup.c | 5 +- fs/sdcardfs/main.c | 8 +-- fs/sdcardfs/packagelist.c | 2 +- fs/sdcardfs/sdcardfs.h | 106 +++++++++++++++++++----------- fs/sdcardfs/super.c | 49 ++++++++++++-- 8 files changed, 234 insertions(+), 135 deletions(-) diff --git a/fs/sdcardfs/dentry.c b/fs/sdcardfs/dentry.c index ae2b4babe2e5..a23168179716 100644 --- a/fs/sdcardfs/dentry.c +++ b/fs/sdcardfs/dentry.c @@ -34,6 +34,8 @@ static int sdcardfs_d_revalidate(struct dentry *dentry, unsigned int flags) struct dentry *parent_lower_dentry = NULL; struct dentry *lower_cur_parent_dentry = NULL; struct dentry *lower_dentry = NULL; + struct inode *inode; + struct sdcardfs_inode_data *data; if (flags & LOOKUP_RCU) return -ECHILD; @@ -103,6 +105,19 @@ static int sdcardfs_d_revalidate(struct dentry *dentry, unsigned int flags) spin_unlock(&dentry->d_lock); spin_unlock(&lower_dentry->d_lock); } + if (!err) + goto out; + + /* If our top's inode is gone, we may be out of date */ + inode = d_inode(dentry); + if (inode) { + data = top_data_get(SDCARDFS_I(inode)); + if (data->abandoned) { + d_drop(dentry); + err = 0; + } + data_put(data); + } out: dput(parent_dentry); diff --git a/fs/sdcardfs/derived_perm.c b/fs/sdcardfs/derived_perm.c index 5a0ef3889846..1239d1cd208b 100644 --- a/fs/sdcardfs/derived_perm.c +++ b/fs/sdcardfs/derived_perm.c @@ -26,28 +26,28 @@ static void inherit_derived_state(struct inode *parent, struct inode *child) struct sdcardfs_inode_info *pi = SDCARDFS_I(parent); struct sdcardfs_inode_info *ci = SDCARDFS_I(child); - ci->perm = PERM_INHERIT; - ci->userid = pi->userid; - ci->d_uid = pi->d_uid; - ci->under_android = pi->under_android; - ci->under_cache = pi->under_cache; - ci->under_obb = pi->under_obb; - set_top(ci, pi->top); + ci->data->perm = PERM_INHERIT; + ci->data->userid = pi->data->userid; + ci->data->d_uid = pi->data->d_uid; + ci->data->under_android = pi->data->under_android; + ci->data->under_cache = pi->data->under_cache; + ci->data->under_obb = pi->data->under_obb; + set_top(ci, pi->top_data); } /* helper function for derived state */ void setup_derived_state(struct inode *inode, perm_t perm, userid_t userid, - uid_t uid, bool under_android, - struct inode *top) + uid_t uid, bool under_android, + struct sdcardfs_inode_data *top) { struct sdcardfs_inode_info *info = SDCARDFS_I(inode); - info->perm = perm; - info->userid = userid; - info->d_uid = uid; - info->under_android = under_android; - info->under_cache = false; - info->under_obb = false; + info->data->perm = perm; + info->data->userid = userid; + info->data->d_uid = uid; + info->data->under_android = under_android; + info->data->under_cache = false; + info->data->under_obb = false; set_top(info, top); } @@ -58,7 +58,8 @@ void get_derived_permission_new(struct dentry *parent, struct dentry *dentry, const struct qstr *name) { struct sdcardfs_inode_info *info = SDCARDFS_I(d_inode(dentry)); - struct sdcardfs_inode_info *parent_info = SDCARDFS_I(d_inode(parent)); + struct sdcardfs_inode_data *parent_data = + SDCARDFS_I(d_inode(parent))->data; appid_t appid; unsigned long user_num; int err; @@ -82,60 +83,61 @@ void get_derived_permission_new(struct dentry *parent, struct dentry *dentry, if (!S_ISDIR(d_inode(dentry)->i_mode)) return; /* Derive custom permissions based on parent and current node */ - switch (parent_info->perm) { + switch (parent_data->perm) { case PERM_INHERIT: case PERM_ANDROID_PACKAGE_CACHE: /* Already inherited above */ break; case PERM_PRE_ROOT: /* Legacy internal layout places users at top level */ - info->perm = PERM_ROOT; + info->data->perm = PERM_ROOT; err = kstrtoul(name->name, 10, &user_num); if (err) - info->userid = 0; + info->data->userid = 0; else - info->userid = user_num; - set_top(info, &info->vfs_inode); + info->data->userid = user_num; + set_top(info, info->data); break; case PERM_ROOT: /* Assume masked off by default. */ if (qstr_case_eq(name, &q_Android)) { /* App-specific directories inside; let anyone traverse */ - info->perm = PERM_ANDROID; - info->under_android = true; - set_top(info, &info->vfs_inode); + info->data->perm = PERM_ANDROID; + info->data->under_android = true; + set_top(info, info->data); } break; case PERM_ANDROID: if (qstr_case_eq(name, &q_data)) { /* App-specific directories inside; let anyone traverse */ - info->perm = PERM_ANDROID_DATA; - set_top(info, &info->vfs_inode); + info->data->perm = PERM_ANDROID_DATA; + set_top(info, info->data); } else if (qstr_case_eq(name, &q_obb)) { /* App-specific directories inside; let anyone traverse */ - info->perm = PERM_ANDROID_OBB; - info->under_obb = true; - set_top(info, &info->vfs_inode); + info->data->perm = PERM_ANDROID_OBB; + info->data->under_obb = true; + set_top(info, info->data); /* Single OBB directory is always shared */ } else if (qstr_case_eq(name, &q_media)) { /* App-specific directories inside; let anyone traverse */ - info->perm = PERM_ANDROID_MEDIA; - set_top(info, &info->vfs_inode); + info->data->perm = PERM_ANDROID_MEDIA; + set_top(info, info->data); } break; case PERM_ANDROID_OBB: case PERM_ANDROID_DATA: case PERM_ANDROID_MEDIA: - info->perm = PERM_ANDROID_PACKAGE; + info->data->perm = PERM_ANDROID_PACKAGE; appid = get_appid(name->name); - if (appid != 0 && !is_excluded(name->name, parent_info->userid)) - info->d_uid = multiuser_get_uid(parent_info->userid, appid); - set_top(info, &info->vfs_inode); + if (appid != 0 && !is_excluded(name->name, parent_data->userid)) + info->data->d_uid = + multiuser_get_uid(parent_data->userid, appid); + set_top(info, info->data); break; case PERM_ANDROID_PACKAGE: if (qstr_case_eq(name, &q_cache)) { - info->perm = PERM_ANDROID_PACKAGE_CACHE; - info->under_cache = true; + info->data->perm = PERM_ANDROID_PACKAGE_CACHE; + info->data->under_cache = true; } break; } @@ -166,7 +168,8 @@ void fixup_lower_ownership(struct dentry *dentry, const char *name) struct inode *delegated_inode = NULL; int error; struct sdcardfs_inode_info *info; - struct sdcardfs_inode_info *info_top; + struct sdcardfs_inode_data *info_d; + struct sdcardfs_inode_data *info_top; perm_t perm; struct sdcardfs_sb_info *sbi = SDCARDFS_SB(dentry->d_sb); uid_t uid = sbi->options.fs_low_uid; @@ -174,15 +177,16 @@ void fixup_lower_ownership(struct dentry *dentry, const char *name) struct iattr newattrs; info = SDCARDFS_I(d_inode(dentry)); - perm = info->perm; - if (info->under_obb) { + info_d = info->data; + perm = info_d->perm; + if (info_d->under_obb) { perm = PERM_ANDROID_OBB; - } else if (info->under_cache) { + } else if (info_d->under_cache) { perm = PERM_ANDROID_PACKAGE_CACHE; } else if (perm == PERM_INHERIT) { - info_top = SDCARDFS_I(grab_top(info)); + info_top = top_data_get(info); perm = info_top->perm; - release_top(info); + data_put(info_top); } switch (perm) { @@ -192,7 +196,7 @@ void fixup_lower_ownership(struct dentry *dentry, const char *name) case PERM_ANDROID_MEDIA: case PERM_ANDROID_PACKAGE: case PERM_ANDROID_PACKAGE_CACHE: - uid = multiuser_get_uid(info->userid, uid); + uid = multiuser_get_uid(info_d->userid, uid); break; case PERM_ANDROID_OBB: uid = AID_MEDIA_OBB; @@ -207,24 +211,24 @@ void fixup_lower_ownership(struct dentry *dentry, const char *name) case PERM_ANDROID_DATA: case PERM_ANDROID_MEDIA: if (S_ISDIR(d_inode(dentry)->i_mode)) - gid = multiuser_get_uid(info->userid, AID_MEDIA_RW); + gid = multiuser_get_uid(info_d->userid, AID_MEDIA_RW); else - gid = multiuser_get_uid(info->userid, get_type(name)); + gid = multiuser_get_uid(info_d->userid, get_type(name)); break; case PERM_ANDROID_OBB: gid = AID_MEDIA_OBB; break; case PERM_ANDROID_PACKAGE: - if (uid_is_app(info->d_uid)) - gid = multiuser_get_ext_gid(info->d_uid); + if (uid_is_app(info_d->d_uid)) + gid = multiuser_get_ext_gid(info_d->d_uid); else - gid = multiuser_get_uid(info->userid, AID_MEDIA_RW); + gid = multiuser_get_uid(info_d->userid, AID_MEDIA_RW); break; case PERM_ANDROID_PACKAGE_CACHE: - if (uid_is_app(info->d_uid)) - gid = multiuser_get_ext_cache_gid(info->d_uid); + if (uid_is_app(info_d->d_uid)) + gid = multiuser_get_ext_cache_gid(info_d->d_uid); else - gid = multiuser_get_uid(info->userid, AID_MEDIA_RW); + gid = multiuser_get_uid(info_d->userid, AID_MEDIA_RW); break; case PERM_PRE_ROOT: default: @@ -257,11 +261,13 @@ void fixup_lower_ownership(struct dentry *dentry, const char *name) sdcardfs_put_lower_path(dentry, &path); } -static int descendant_may_need_fixup(struct sdcardfs_inode_info *info, struct limit_search *limit) +static int descendant_may_need_fixup(struct sdcardfs_inode_data *data, + struct limit_search *limit) { - if (info->perm == PERM_ROOT) - return (limit->flags & BY_USERID)?info->userid == limit->userid:1; - if (info->perm == PERM_PRE_ROOT || info->perm == PERM_ANDROID) + if (data->perm == PERM_ROOT) + return (limit->flags & BY_USERID) ? + data->userid == limit->userid : 1; + if (data->perm == PERM_PRE_ROOT || data->perm == PERM_ANDROID) return 1; return 0; } @@ -292,7 +298,7 @@ static void __fixup_perms_recursive(struct dentry *dentry, struct limit_search * } info = SDCARDFS_I(d_inode(dentry)); - if (needs_fixup(info->perm)) { + if (needs_fixup(info->data->perm)) { list_for_each_entry(child, &dentry->d_subdirs, d_child) { spin_lock_nested(&child->d_lock, depth + 1); if (!(limit->flags & BY_NAME) || qstr_case_eq(&child->d_name, &limit->name)) { @@ -305,7 +311,7 @@ static void __fixup_perms_recursive(struct dentry *dentry, struct limit_search * } spin_unlock(&child->d_lock); } - } else if (descendant_may_need_fixup(info, limit)) { + } else if (descendant_may_need_fixup(info->data, limit)) { list_for_each_entry(child, &dentry->d_subdirs, d_child) { __fixup_perms_recursive(child, limit, depth + 1); } @@ -349,12 +355,12 @@ int need_graft_path(struct dentry *dentry) struct sdcardfs_sb_info *sbi = SDCARDFS_SB(dentry->d_sb); struct qstr obb = QSTR_LITERAL("obb"); - if (parent_info->perm == PERM_ANDROID && + if (parent_info->data->perm == PERM_ANDROID && qstr_case_eq(&dentry->d_name, &obb)) { /* /Android/obb is the base obbpath of DERIVED_UNIFIED */ if (!(sbi->options.multiuser == false - && parent_info->userid == 0)) { + && parent_info->data->userid == 0)) { ret = 1; } } @@ -415,11 +421,11 @@ int is_base_obbpath(struct dentry *dentry) spin_lock(&SDCARDFS_D(dentry)->lock); if (sbi->options.multiuser) { - if (parent_info->perm == PERM_PRE_ROOT && + if (parent_info->data->perm == PERM_PRE_ROOT && qstr_case_eq(&dentry->d_name, &q_obb)) { ret = 1; } - } else if (parent_info->perm == PERM_ANDROID && + } else if (parent_info->data->perm == PERM_ANDROID && qstr_case_eq(&dentry->d_name, &q_obb)) { ret = 1; } diff --git a/fs/sdcardfs/inode.c b/fs/sdcardfs/inode.c index 1beda0c7772b..a3f7c8edb9d5 100644 --- a/fs/sdcardfs/inode.c +++ b/fs/sdcardfs/inode.c @@ -23,7 +23,8 @@ #include /* Do not directly use this function. Use OVERRIDE_CRED() instead. */ -const struct cred *override_fsids(struct sdcardfs_sb_info *sbi, struct sdcardfs_inode_info *info) +const struct cred *override_fsids(struct sdcardfs_sb_info *sbi, + struct sdcardfs_inode_data *data) { struct cred *cred; const struct cred *old_cred; @@ -33,10 +34,10 @@ const struct cred *override_fsids(struct sdcardfs_sb_info *sbi, struct sdcardfs_ if (!cred) return NULL; - if (info->under_obb) + if (data->under_obb) uid = AID_MEDIA_OBB; else - uid = multiuser_get_uid(info->userid, sbi->options.fs_low_uid); + uid = multiuser_get_uid(data->userid, sbi->options.fs_low_uid); cred->fsuid = make_kuid(&init_user_ns, uid); cred->fsgid = make_kgid(&init_user_ns, sbi->options.fs_low_gid); @@ -96,7 +97,8 @@ static int sdcardfs_create(struct inode *dir, struct dentry *dentry, if (err) goto out; - err = sdcardfs_interpose(dentry, dir->i_sb, &lower_path, SDCARDFS_I(dir)->userid); + err = sdcardfs_interpose(dentry, dir->i_sb, &lower_path, + SDCARDFS_I(dir)->data->userid); if (err) goto out; fsstack_copy_attr_times(dir, sdcardfs_lower_inode(dir)); @@ -267,7 +269,7 @@ static int sdcardfs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode struct path lower_path; struct sdcardfs_sb_info *sbi = SDCARDFS_SB(dentry->d_sb); const struct cred *saved_cred = NULL; - struct sdcardfs_inode_info *pi = SDCARDFS_I(dir); + struct sdcardfs_inode_data *pd = SDCARDFS_I(dir)->data; int touch_err = 0; struct fs_struct *saved_fs; struct fs_struct *copied_fs; @@ -336,7 +338,7 @@ static int sdcardfs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode make_nomedia_in_obb = 1; } - err = sdcardfs_interpose(dentry, dir->i_sb, &lower_path, pi->userid); + err = sdcardfs_interpose(dentry, dir->i_sb, &lower_path, pd->userid); if (err) { unlock_dir(lower_parent_dentry); goto out; @@ -349,12 +351,13 @@ static int sdcardfs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode fixup_lower_ownership(dentry, dentry->d_name.name); unlock_dir(lower_parent_dentry); if ((!sbi->options.multiuser) && (qstr_case_eq(&dentry->d_name, &q_obb)) - && (pi->perm == PERM_ANDROID) && (pi->userid == 0)) + && (pd->perm == PERM_ANDROID) && (pd->userid == 0)) make_nomedia_in_obb = 1; /* When creating /Android/data and /Android/obb, mark them as .nomedia */ if (make_nomedia_in_obb || - ((pi->perm == PERM_ANDROID) && (qstr_case_eq(&dentry->d_name, &q_data)))) { + ((pd->perm == PERM_ANDROID) + && (qstr_case_eq(&dentry->d_name, &q_data)))) { REVERT_CRED(saved_cred); OVERRIDE_CRED(SDCARDFS_SB(dir->i_sb), saved_cred, SDCARDFS_I(d_inode(dentry))); set_fs_pwd(current->fs, &lower_path); @@ -620,7 +623,7 @@ static int sdcardfs_permission(struct vfsmount *mnt, struct inode *inode, int ma { int err; struct inode tmp; - struct inode *top = grab_top(SDCARDFS_I(inode)); + struct sdcardfs_inode_data *top = top_data_get(SDCARDFS_I(inode)); if (IS_ERR(mnt)) return PTR_ERR(mnt); @@ -640,10 +643,11 @@ static int sdcardfs_permission(struct vfsmount *mnt, struct inode *inode, int ma * locks must be dealt with to avoid undefined behavior. */ copy_attrs(&tmp, inode); - tmp.i_uid = make_kuid(&init_user_ns, SDCARDFS_I(top)->d_uid); - tmp.i_gid = make_kgid(&init_user_ns, get_gid(mnt, SDCARDFS_I(top))); - tmp.i_mode = (inode->i_mode & S_IFMT) | get_mode(mnt, SDCARDFS_I(top)); - release_top(SDCARDFS_I(inode)); + tmp.i_uid = make_kuid(&init_user_ns, top->d_uid); + tmp.i_gid = make_kgid(&init_user_ns, get_gid(mnt, top)); + tmp.i_mode = (inode->i_mode & S_IFMT) + | get_mode(mnt, SDCARDFS_I(inode), top); + data_put(top); tmp.i_sb = inode->i_sb; if (IS_POSIXACL(inode)) pr_warn("%s: This may be undefined behavior...\n", __func__); @@ -695,11 +699,12 @@ static int sdcardfs_setattr(struct vfsmount *mnt, struct dentry *dentry, struct struct dentry *parent; struct inode tmp; struct dentry tmp_d; - struct inode *top; + struct sdcardfs_inode_data *top; + const struct cred *saved_cred = NULL; inode = d_inode(dentry); - top = grab_top(SDCARDFS_I(inode)); + top = top_data_get(SDCARDFS_I(inode)); if (!top) return -EINVAL; @@ -717,11 +722,12 @@ static int sdcardfs_setattr(struct vfsmount *mnt, struct dentry *dentry, struct * */ copy_attrs(&tmp, inode); - tmp.i_uid = make_kuid(&init_user_ns, SDCARDFS_I(top)->d_uid); - tmp.i_gid = make_kgid(&init_user_ns, get_gid(mnt, SDCARDFS_I(top))); - tmp.i_mode = (inode->i_mode & S_IFMT) | get_mode(mnt, SDCARDFS_I(top)); + tmp.i_uid = make_kuid(&init_user_ns, top->d_uid); + tmp.i_gid = make_kgid(&init_user_ns, get_gid(mnt, top)); + tmp.i_mode = (inode->i_mode & S_IFMT) + | get_mode(mnt, SDCARDFS_I(inode), top); tmp.i_size = i_size_read(inode); - release_top(SDCARDFS_I(inode)); + data_put(top); tmp.i_sb = inode->i_sb; tmp_d.d_inode = &tmp; @@ -824,17 +830,17 @@ static int sdcardfs_fillattr(struct vfsmount *mnt, struct inode *inode, struct kstat *stat) { struct sdcardfs_inode_info *info = SDCARDFS_I(inode); - struct inode *top = grab_top(info); + struct sdcardfs_inode_data *top = top_data_get(info); if (!top) return -EINVAL; stat->dev = inode->i_sb->s_dev; stat->ino = inode->i_ino; - stat->mode = (inode->i_mode & S_IFMT) | get_mode(mnt, SDCARDFS_I(top)); + stat->mode = (inode->i_mode & S_IFMT) | get_mode(mnt, info, top); stat->nlink = inode->i_nlink; - stat->uid = make_kuid(&init_user_ns, SDCARDFS_I(top)->d_uid); - stat->gid = make_kgid(&init_user_ns, get_gid(mnt, SDCARDFS_I(top))); + stat->uid = make_kuid(&init_user_ns, top->d_uid); + stat->gid = make_kgid(&init_user_ns, get_gid(mnt, top)); stat->rdev = inode->i_rdev; stat->size = i_size_read(inode); stat->atime = inode->i_atime; @@ -842,7 +848,7 @@ static int sdcardfs_fillattr(struct vfsmount *mnt, stat->ctime = inode->i_ctime; stat->blksize = (1 << inode->i_blkbits); stat->blocks = inode->i_blocks; - release_top(info); + data_put(top); return 0; } diff --git a/fs/sdcardfs/lookup.c b/fs/sdcardfs/lookup.c index 7d7c4515539b..83f6083e5aad 100644 --- a/fs/sdcardfs/lookup.c +++ b/fs/sdcardfs/lookup.c @@ -71,7 +71,7 @@ struct inode_data { static int sdcardfs_inode_test(struct inode *inode, void *candidate_data/*void *candidate_lower_inode*/) { struct inode *current_lower_inode = sdcardfs_lower_inode(inode); - userid_t current_userid = SDCARDFS_I(inode)->userid; + userid_t current_userid = SDCARDFS_I(inode)->data->userid; if (current_lower_inode == ((struct inode_data *)candidate_data)->lower_inode && current_userid == ((struct inode_data *)candidate_data)->id) @@ -439,7 +439,8 @@ struct dentry *sdcardfs_lookup(struct inode *dir, struct dentry *dentry, goto out; } - ret = __sdcardfs_lookup(dentry, flags, &lower_parent_path, SDCARDFS_I(dir)->userid); + ret = __sdcardfs_lookup(dentry, flags, &lower_parent_path, + SDCARDFS_I(dir)->data->userid); if (IS_ERR(ret)) goto out; if (ret) diff --git a/fs/sdcardfs/main.c b/fs/sdcardfs/main.c index 953d2156d2e9..3c5b51d49d21 100644 --- a/fs/sdcardfs/main.c +++ b/fs/sdcardfs/main.c @@ -327,13 +327,13 @@ static int sdcardfs_read_super(struct vfsmount *mnt, struct super_block *sb, mutex_lock(&sdcardfs_super_list_lock); if (sb_info->options.multiuser) { setup_derived_state(d_inode(sb->s_root), PERM_PRE_ROOT, - sb_info->options.fs_user_id, AID_ROOT, - false, d_inode(sb->s_root)); + sb_info->options.fs_user_id, AID_ROOT, + false, SDCARDFS_I(d_inode(sb->s_root))->data); snprintf(sb_info->obbpath_s, PATH_MAX, "%s/obb", dev_name); } else { setup_derived_state(d_inode(sb->s_root), PERM_ROOT, - sb_info->options.fs_user_id, AID_ROOT, - false, d_inode(sb->s_root)); + sb_info->options.fs_user_id, AID_ROOT, + false, SDCARDFS_I(d_inode(sb->s_root))->data); snprintf(sb_info->obbpath_s, PATH_MAX, "%s/Android/obb", dev_name); } fixup_tmp_permissions(d_inode(sb->s_root)); diff --git a/fs/sdcardfs/packagelist.c b/fs/sdcardfs/packagelist.c index 5ea6469638d8..00a0f656acc7 100644 --- a/fs/sdcardfs/packagelist.c +++ b/fs/sdcardfs/packagelist.c @@ -156,7 +156,7 @@ int check_caller_access_to_name(struct inode *parent_node, const struct qstr *na struct qstr q_android_secure = QSTR_LITERAL("android_secure"); /* Always block security-sensitive files at root */ - if (parent_node && SDCARDFS_I(parent_node)->perm == PERM_ROOT) { + if (parent_node && SDCARDFS_I(parent_node)->data->perm == PERM_ROOT) { if (qstr_case_eq(name, &q_autorun) || qstr_case_eq(name, &q__android_secure) || qstr_case_eq(name, &q_android_secure)) { diff --git a/fs/sdcardfs/sdcardfs.h b/fs/sdcardfs/sdcardfs.h index 380982b4a567..3687b22a2e6b 100644 --- a/fs/sdcardfs/sdcardfs.h +++ b/fs/sdcardfs/sdcardfs.h @@ -30,6 +30,7 @@ #include #include #include +#include #include #include #include @@ -81,7 +82,8 @@ */ #define fixup_tmp_permissions(x) \ do { \ - (x)->i_uid = make_kuid(&init_user_ns, SDCARDFS_I(x)->d_uid); \ + (x)->i_uid = make_kuid(&init_user_ns, \ + SDCARDFS_I(x)->data->d_uid); \ (x)->i_gid = make_kgid(&init_user_ns, AID_SDCARD_RW); \ (x)->i_mode = ((x)->i_mode & S_IFMT) | 0775;\ } while (0) @@ -97,14 +99,14 @@ */ #define OVERRIDE_CRED(sdcardfs_sbi, saved_cred, info) \ do { \ - saved_cred = override_fsids(sdcardfs_sbi, info); \ + saved_cred = override_fsids(sdcardfs_sbi, info->data); \ if (!saved_cred) \ return -ENOMEM; \ } while (0) #define OVERRIDE_CRED_PTR(sdcardfs_sbi, saved_cred, info) \ do { \ - saved_cred = override_fsids(sdcardfs_sbi, info); \ + saved_cred = override_fsids(sdcardfs_sbi, info->data); \ if (!saved_cred) \ return ERR_PTR(-ENOMEM); \ } while (0) @@ -142,9 +144,11 @@ typedef enum { struct sdcardfs_sb_info; struct sdcardfs_mount_options; struct sdcardfs_inode_info; +struct sdcardfs_inode_data; /* Do not directly use this function. Use OVERRIDE_CRED() instead. */ -const struct cred *override_fsids(struct sdcardfs_sb_info *sbi, struct sdcardfs_inode_info *info); +const struct cred *override_fsids(struct sdcardfs_sb_info *sbi, + struct sdcardfs_inode_data *data); /* Do not directly use this function, use REVERT_CRED() instead. */ void revert_fsids(const struct cred *old_cred); @@ -178,18 +182,26 @@ struct sdcardfs_file_info { const struct vm_operations_struct *lower_vm_ops; }; -/* sdcardfs inode data in memory */ -struct sdcardfs_inode_info { - struct inode *lower_inode; - /* state derived based on current position in hierachy */ +struct sdcardfs_inode_data { + struct kref refcount; + bool abandoned; + perm_t perm; userid_t userid; uid_t d_uid; bool under_android; bool under_cache; bool under_obb; +}; + +/* sdcardfs inode data in memory */ +struct sdcardfs_inode_info { + struct inode *lower_inode; + /* state derived based on current position in hierarchy */ + struct sdcardfs_inode_data *data; + /* top folder for ownership */ - struct inode *top; + struct sdcardfs_inode_data *top_data; struct inode vfs_inode; }; @@ -351,39 +363,56 @@ SDCARDFS_DENT_FUNC(orig_path) static inline bool sbinfo_has_sdcard_magic(struct sdcardfs_sb_info *sbinfo) { - return sbinfo && sbinfo->sb && sbinfo->sb->s_magic == SDCARDFS_SUPER_MAGIC; + return sbinfo && sbinfo->sb + && sbinfo->sb->s_magic == SDCARDFS_SUPER_MAGIC; } -/* grab a refererence if we aren't linking to ourself */ -static inline void set_top(struct sdcardfs_inode_info *info, struct inode *top) +static inline struct sdcardfs_inode_data *data_get( + struct sdcardfs_inode_data *data) { - struct inode *old_top = NULL; - - BUG_ON(IS_ERR_OR_NULL(top)); - if (info->top && info->top != &info->vfs_inode) - old_top = info->top; - if (top != &info->vfs_inode) - igrab(top); - info->top = top; - iput(old_top); + if (data) + kref_get(&data->refcount); + return data; } -static inline struct inode *grab_top(struct sdcardfs_inode_info *info) +static inline struct sdcardfs_inode_data *top_data_get( + struct sdcardfs_inode_info *info) { - struct inode *top = info->top; + return data_get(info->top_data); +} - if (top) - return igrab(top); - else - return NULL; +extern void data_release(struct kref *ref); + +static inline void data_put(struct sdcardfs_inode_data *data) +{ + kref_put(&data->refcount, data_release); +} + +static inline void release_own_data(struct sdcardfs_inode_info *info) +{ + /* + * This happens exactly once per inode. At this point, the inode that + * originally held this data is about to be freed, and all references + * to it are held as a top value, and will likely be released soon. + */ + info->data->abandoned = true; + data_put(info->data); } -static inline void release_top(struct sdcardfs_inode_info *info) +static inline void set_top(struct sdcardfs_inode_info *info, + struct sdcardfs_inode_data *top) { - iput(info->top); + struct sdcardfs_inode_data *old_top = info->top_data; + + if (top) + data_get(top); + info->top_data = top; + if (old_top) + data_put(old_top); } -static inline int get_gid(struct vfsmount *mnt, struct sdcardfs_inode_info *info) +static inline int get_gid(struct vfsmount *mnt, + struct sdcardfs_inode_data *data) { struct sdcardfs_vfsmount_options *opts = mnt->data; @@ -396,10 +425,12 @@ static inline int get_gid(struct vfsmount *mnt, struct sdcardfs_inode_info *info */ return AID_SDCARD_RW; else - return multiuser_get_uid(info->userid, opts->gid); + return multiuser_get_uid(data->userid, opts->gid); } -static inline int get_mode(struct vfsmount *mnt, struct sdcardfs_inode_info *info) +static inline int get_mode(struct vfsmount *mnt, + struct sdcardfs_inode_info *info, + struct sdcardfs_inode_data *data) { int owner_mode; int filtered_mode; @@ -407,12 +438,12 @@ static inline int get_mode(struct vfsmount *mnt, struct sdcardfs_inode_info *inf int visible_mode = 0775 & ~opts->mask; - if (info->perm == PERM_PRE_ROOT) { + if (data->perm == PERM_PRE_ROOT) { /* Top of multi-user view should always be visible to ensure * secondary users can traverse inside. */ visible_mode = 0711; - } else if (info->under_android) { + } else if (data->under_android) { /* Block "other" access to Android directories, since only apps * belonging to a specific user should be in there; we still * leave +x open for the default view. @@ -481,8 +512,9 @@ struct limit_search { userid_t userid; }; -extern void setup_derived_state(struct inode *inode, perm_t perm, userid_t userid, - uid_t uid, bool under_android, struct inode *top); +extern void setup_derived_state(struct inode *inode, perm_t perm, + userid_t userid, uid_t uid, bool under_android, + struct sdcardfs_inode_data *top); extern void get_derived_permission(struct dentry *parent, struct dentry *dentry); extern void get_derived_permission_new(struct dentry *parent, struct dentry *dentry, const struct qstr *name); extern void fixup_perms_recursive(struct dentry *dentry, struct limit_search *limit); @@ -601,7 +633,7 @@ static inline void sdcardfs_copy_and_fix_attrs(struct inode *dest, const struct { dest->i_mode = (src->i_mode & S_IFMT) | S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH; /* 0775 */ - dest->i_uid = make_kuid(&init_user_ns, SDCARDFS_I(dest)->d_uid); + dest->i_uid = make_kuid(&init_user_ns, SDCARDFS_I(dest)->data->d_uid); dest->i_gid = make_kgid(&init_user_ns, AID_SDCARD_RW); dest->i_rdev = src->i_rdev; dest->i_atime = src->i_atime; diff --git a/fs/sdcardfs/super.c b/fs/sdcardfs/super.c index 8a9c9c7adca2..7f4539b4b249 100644 --- a/fs/sdcardfs/super.c +++ b/fs/sdcardfs/super.c @@ -26,6 +26,23 @@ */ static struct kmem_cache *sdcardfs_inode_cachep; +/* + * To support the top references, we must track some data separately. + * An sdcardfs_inode_info always has a reference to its data, and once set up, + * also has a reference to its top. The top may be itself, in which case it + * holds two references to its data. When top is changed, it takes a ref to the + * new data and then drops the ref to the old data. + */ +static struct kmem_cache *sdcardfs_inode_data_cachep; + +void data_release(struct kref *ref) +{ + struct sdcardfs_inode_data *data = + container_of(ref, struct sdcardfs_inode_data, refcount); + + kmem_cache_free(sdcardfs_inode_data_cachep, data); +} + /* final actions when unmounting a file system */ static void sdcardfs_put_super(struct super_block *sb) { @@ -166,6 +183,7 @@ static void sdcardfs_evict_inode(struct inode *inode) struct inode *lower_inode; truncate_inode_pages(&inode->i_data, 0); + set_top(SDCARDFS_I(inode), NULL); clear_inode(inode); /* * Decrement a reference to a lower_inode, which was incremented @@ -173,13 +191,13 @@ static void sdcardfs_evict_inode(struct inode *inode) */ lower_inode = sdcardfs_lower_inode(inode); sdcardfs_set_lower_inode(inode, NULL); - set_top(SDCARDFS_I(inode), inode); iput(lower_inode); } static struct inode *sdcardfs_alloc_inode(struct super_block *sb) { struct sdcardfs_inode_info *i; + struct sdcardfs_inode_data *d; i = kmem_cache_alloc(sdcardfs_inode_cachep, GFP_KERNEL); if (!i) @@ -188,6 +206,16 @@ static struct inode *sdcardfs_alloc_inode(struct super_block *sb) /* memset everything up to the inode to 0 */ memset(i, 0, offsetof(struct sdcardfs_inode_info, vfs_inode)); + d = kmem_cache_alloc(sdcardfs_inode_data_cachep, + GFP_KERNEL | __GFP_ZERO); + if (!d) { + kmem_cache_free(sdcardfs_inode_cachep, i); + return NULL; + } + + i->data = d; + kref_init(&d->refcount); + i->vfs_inode.i_version = 1; return &i->vfs_inode; } @@ -196,6 +224,7 @@ static void i_callback(struct rcu_head *head) { struct inode *inode = container_of(head, struct inode, i_rcu); + release_own_data(SDCARDFS_I(inode)); kmem_cache_free(sdcardfs_inode_cachep, SDCARDFS_I(inode)); } @@ -214,20 +243,30 @@ static void init_once(void *obj) int sdcardfs_init_inode_cache(void) { - int err = 0; - sdcardfs_inode_cachep = kmem_cache_create("sdcardfs_inode_cache", sizeof(struct sdcardfs_inode_info), 0, SLAB_RECLAIM_ACCOUNT, init_once); + if (!sdcardfs_inode_cachep) - err = -ENOMEM; - return err; + return -ENOMEM; + + sdcardfs_inode_data_cachep = + kmem_cache_create("sdcardfs_inode_data_cache", + sizeof(struct sdcardfs_inode_data), 0, + SLAB_RECLAIM_ACCOUNT, NULL); + if (!sdcardfs_inode_data_cachep) { + kmem_cache_destroy(sdcardfs_inode_cachep); + return -ENOMEM; + } + + return 0; } /* sdcardfs inode cache destructor */ void sdcardfs_destroy_inode_cache(void) { + kmem_cache_destroy(sdcardfs_inode_data_cachep); kmem_cache_destroy(sdcardfs_inode_cachep); } From c37377b64e61ce8de28e3d7e56f17dd540cea4e6 Mon Sep 17 00:00:00 2001 From: Daniel Rosenberg Date: Mon, 22 May 2017 13:23:56 -0700 Subject: [PATCH 0369/1103] ANDROID: sdcardfs: Check for NULL in revalidate If the inode is in the process of being evicted, the top value may be NULL. Signed-off-by: Daniel Rosenberg Bug: 38502532 Change-Id: I0b9d04aab621e0398d44d1c5dc53293106aa5f89 --- fs/sdcardfs/dentry.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/fs/sdcardfs/dentry.c b/fs/sdcardfs/dentry.c index a23168179716..e9426a61d04a 100644 --- a/fs/sdcardfs/dentry.c +++ b/fs/sdcardfs/dentry.c @@ -109,14 +109,16 @@ static int sdcardfs_d_revalidate(struct dentry *dentry, unsigned int flags) goto out; /* If our top's inode is gone, we may be out of date */ - inode = d_inode(dentry); + inode = igrab(d_inode(dentry)); if (inode) { data = top_data_get(SDCARDFS_I(inode)); - if (data->abandoned) { + if (!data || data->abandoned) { d_drop(dentry); err = 0; } - data_put(data); + if (data) + data_put(data); + iput(inode); } out: From 8d0f6794bfe7f8f399eb6ce7a722d5636294df15 Mon Sep 17 00:00:00 2001 From: Daniel Rosenberg Date: Wed, 7 Jun 2017 12:44:50 -0700 Subject: [PATCH 0370/1103] ANDROID: sdcardfs: d_splice_alias can return error values We must check that d_splice_alias was successful before using its output. Signed-off-by: Daniel Rosenberg Bug: 62390017 Change-Id: Ifda0a052fb3f67e35c635a4e5e907876c5400978 --- fs/sdcardfs/lookup.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/fs/sdcardfs/lookup.c b/fs/sdcardfs/lookup.c index 83f6083e5aad..7dab5f76896b 100644 --- a/fs/sdcardfs/lookup.c +++ b/fs/sdcardfs/lookup.c @@ -199,7 +199,8 @@ static struct dentry *__sdcardfs_interpose(struct dentry *dentry, ret_dentry = d_splice_alias(inode, dentry); dentry = ret_dentry ?: dentry; - update_derived_permission_lock(dentry); + if (!IS_ERR(dentry)) + update_derived_permission_lock(dentry); out: return ret_dentry; } From 315b4351de09188a648d4513bf35da4e354ccb97 Mon Sep 17 00:00:00 2001 From: Greg Hackmann Date: Tue, 16 May 2017 16:48:49 -0700 Subject: [PATCH 0371/1103] ANDROID: sdcardfs: remove dead function open_flags_to_access_mode() smatch warns about the suspicious formatting in the last line of open_flags_to_access_mode(). It turns out the only caller was deleted over a year ago by "ANDROID: sdcardfs: Bring up to date with Android M permissions:", so we can "fix" the function's formatting by deleting it. Change-Id: Id85946f3eb01722eef35b1815f405a6fda3aa4ff Signed-off-by: Greg Hackmann --- fs/sdcardfs/packagelist.c | 13 ------------- fs/sdcardfs/sdcardfs.h | 1 - 2 files changed, 14 deletions(-) diff --git a/fs/sdcardfs/packagelist.c b/fs/sdcardfs/packagelist.c index 00a0f656acc7..6da0c2186d39 100644 --- a/fs/sdcardfs/packagelist.c +++ b/fs/sdcardfs/packagelist.c @@ -174,19 +174,6 @@ int check_caller_access_to_name(struct inode *parent_node, const struct qstr *na return 1; } -/* This function is used when file opening. The open flags must be - * checked before calling check_caller_access_to_name() - */ -int open_flags_to_access_mode(int open_flags) -{ - if ((open_flags & O_ACCMODE) == O_RDONLY) - return 0; /* R_OK */ - if ((open_flags & O_ACCMODE) == O_WRONLY) - return 1; /* W_OK */ - /* Probably O_RDRW, but treat as default to be safe */ - return 1; /* R_OK | W_OK */ -} - static struct hashtable_entry *alloc_hashtable_entry(const struct qstr *key, appid_t value) { diff --git a/fs/sdcardfs/sdcardfs.h b/fs/sdcardfs/sdcardfs.h index 3687b22a2e6b..4e0ce49a906d 100644 --- a/fs/sdcardfs/sdcardfs.h +++ b/fs/sdcardfs/sdcardfs.h @@ -499,7 +499,6 @@ extern appid_t get_appid(const char *app_name); extern appid_t get_ext_gid(const char *app_name); extern appid_t is_excluded(const char *app_name, userid_t userid); extern int check_caller_access_to_name(struct inode *parent_node, const struct qstr *name); -extern int open_flags_to_access_mode(int open_flags); extern int packagelist_init(void); extern void packagelist_exit(void); From 3cafe6f79726e3e4c111a4ad91ceea1671e1e3b6 Mon Sep 17 00:00:00 2001 From: Gao Xiang Date: Tue, 9 May 2017 12:30:56 +0800 Subject: [PATCH 0372/1103] ANDROID: sdcardfs: use mount_nodev and fix a issue in sdcardfs_kill_sb Use the VFS mount_nodev instead of customized mount_nodev_with_options and fix generic_shutdown_super to kill_anon_super because of set_anon_super Signed-off-by: Gao Xiang Change-Id: Ibe46647aa2ce49d79291aa9d0295e9625cfccd80 --- fs/sdcardfs/main.c | 47 ++++++++++++++++++++-------------------------- 1 file changed, 20 insertions(+), 27 deletions(-) diff --git a/fs/sdcardfs/main.c b/fs/sdcardfs/main.c index 3c5b51d49d21..80825b287836 100644 --- a/fs/sdcardfs/main.c +++ b/fs/sdcardfs/main.c @@ -364,41 +364,34 @@ static int sdcardfs_read_super(struct vfsmount *mnt, struct super_block *sb, return err; } -/* A feature which supports mount_nodev() with options */ -static struct dentry *mount_nodev_with_options(struct vfsmount *mnt, - struct file_system_type *fs_type, int flags, - const char *dev_name, void *data, - int (*fill_super)(struct vfsmount *, struct super_block *, - const char *, void *, int)) +struct sdcardfs_mount_private { + struct vfsmount *mnt; + const char *dev_name; + void *raw_data; +}; +static int __sdcardfs_fill_super( + struct super_block *sb, + void *_priv, int silent) { - int error; - struct super_block *s = sget(fs_type, NULL, set_anon_super, flags, NULL); - - if (IS_ERR(s)) - return ERR_CAST(s); - - s->s_flags = flags; + struct sdcardfs_mount_private *priv = _priv; - error = fill_super(mnt, s, dev_name, data, flags & MS_SILENT ? 1 : 0); - if (error) { - deactivate_locked_super(s); - return ERR_PTR(error); - } - s->s_flags |= MS_ACTIVE; - return dget(s->s_root); + return sdcardfs_read_super(priv->mnt, + sb, priv->dev_name, priv->raw_data, silent); } static struct dentry *sdcardfs_mount(struct vfsmount *mnt, struct file_system_type *fs_type, int flags, const char *dev_name, void *raw_data) { - /* - * dev_name is a lower_path_name, - * raw_data is a option string. - */ - return mount_nodev_with_options(mnt, fs_type, flags, dev_name, - raw_data, sdcardfs_read_super); + struct sdcardfs_mount_private priv = { + .mnt = mnt, + .dev_name = dev_name, + .raw_data = raw_data + }; + + return mount_nodev(fs_type, flags, + &priv, __sdcardfs_fill_super); } static struct dentry *sdcardfs_mount_wrn(struct file_system_type *fs_type, @@ -423,7 +416,7 @@ void sdcardfs_kill_sb(struct super_block *sb) list_del(&sbi->list); mutex_unlock(&sdcardfs_super_list_lock); } - generic_shutdown_super(sb); + kill_anon_super(sb); } static struct file_system_type sdcardfs_fs_type = { From e09056e616bfed61906f12384fc5fcfaa370f786 Mon Sep 17 00:00:00 2001 From: Daniel Rosenberg Date: Wed, 19 Jul 2017 17:16:43 -0700 Subject: [PATCH 0373/1103] ANDROID: sdcardfs: Remove unnecessary lock The mmap_sem lock does not appear to be protecting anything, and has been removed in Samsung's more recent versions of sdcardfs. Signed-off-by: Daniel Rosenberg Change-Id: I76ff3e33002716b8384fc8be368028ed63dffe4e Bug: 63785372 --- fs/sdcardfs/inode.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/fs/sdcardfs/inode.c b/fs/sdcardfs/inode.c index a3f7c8edb9d5..70a4e3cb8781 100644 --- a/fs/sdcardfs/inode.c +++ b/fs/sdcardfs/inode.c @@ -776,13 +776,9 @@ static int sdcardfs_setattr(struct vfsmount *mnt, struct dentry *dentry, struct * afterwards in the other cases: we fsstack_copy_inode_size from * the lower level. */ - if (current->mm) - down_write(¤t->mm->mmap_sem); if (ia->ia_valid & ATTR_SIZE) { err = inode_newsize_ok(&tmp, ia->ia_size); if (err) { - if (current->mm) - up_write(¤t->mm->mmap_sem); goto out; } truncate_setsize(inode, ia->ia_size); @@ -805,8 +801,6 @@ static int sdcardfs_setattr(struct vfsmount *mnt, struct dentry *dentry, struct err = notify_change2(lower_mnt, lower_dentry, &lower_ia, /* note: lower_ia */ NULL); inode_unlock(d_inode(lower_dentry)); - if (current->mm) - up_write(¤t->mm->mmap_sem); if (err) goto out; From d62f641142585878b8cf4d3ec459295732f405e4 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Thu, 6 Jul 2017 19:12:22 -0700 Subject: [PATCH 0374/1103] ANDROID: sdcardfs: override credential for ioctl to lower fs Otherwise, lower_fs->ioctl() fails due to inode_owner_or_capable(). Signed-off-by: Jaegeuk Kim Bug: 63260873 Change-Id: I623a6c7c5f8a3cbd7ec73ef89e18ddb093c43805 --- fs/sdcardfs/file.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/fs/sdcardfs/file.c b/fs/sdcardfs/file.c index 6076c342dae6..5ac0b0bbb0ec 100644 --- a/fs/sdcardfs/file.c +++ b/fs/sdcardfs/file.c @@ -104,12 +104,19 @@ static long sdcardfs_unlocked_ioctl(struct file *file, unsigned int cmd, { long err = -ENOTTY; struct file *lower_file; + const struct cred *saved_cred = NULL; + struct dentry *dentry = file->f_path.dentry; + struct sdcardfs_sb_info *sbi = SDCARDFS_SB(dentry->d_sb); lower_file = sdcardfs_lower_file(file); /* XXX: use vfs_ioctl if/when VFS exports it */ if (!lower_file || !lower_file->f_op) goto out; + + /* save current_cred and override it */ + OVERRIDE_CRED(sbi, saved_cred, SDCARDFS_I(file_inode(file))); + if (lower_file->f_op->unlocked_ioctl) err = lower_file->f_op->unlocked_ioctl(lower_file, cmd, arg); @@ -117,6 +124,7 @@ static long sdcardfs_unlocked_ioctl(struct file *file, unsigned int cmd, if (!err) sdcardfs_copy_and_fix_attrs(file_inode(file), file_inode(lower_file)); + REVERT_CRED(saved_cred); out: return err; } @@ -127,15 +135,23 @@ static long sdcardfs_compat_ioctl(struct file *file, unsigned int cmd, { long err = -ENOTTY; struct file *lower_file; + const struct cred *saved_cred = NULL; + struct dentry *dentry = file->f_path.dentry; + struct sdcardfs_sb_info *sbi = SDCARDFS_SB(dentry->d_sb); lower_file = sdcardfs_lower_file(file); /* XXX: use vfs_ioctl if/when VFS exports it */ if (!lower_file || !lower_file->f_op) goto out; + + /* save current_cred and override it */ + OVERRIDE_CRED(sbi, saved_cred, SDCARDFS_I(file_inode(file))); + if (lower_file->f_op->compat_ioctl) err = lower_file->f_op->compat_ioctl(lower_file, cmd, arg); + REVERT_CRED(saved_cred); out: return err; } From da1720e9aa2a96510eb9ebc3d5b527fb70220856 Mon Sep 17 00:00:00 2001 From: Daniel Rosenberg Date: Wed, 19 Jul 2017 17:25:07 -0700 Subject: [PATCH 0375/1103] ANDROID: Sdcardfs: Move gid derivation under flag This moves the code to adjust the gid/uid of lower filesystem files under the mount flag derive_gid. Signed-off-by: Daniel Rosenberg Change-Id: I44eaad4ef67c7fcfda3b6ea3502afab94442610c Bug: 63245673 --- fs/sdcardfs/derived_perm.c | 3 +++ fs/sdcardfs/inode.c | 12 ++++++++---- fs/sdcardfs/main.c | 7 +++++++ fs/sdcardfs/sdcardfs.h | 1 + fs/sdcardfs/super.c | 2 ++ 5 files changed, 21 insertions(+), 4 deletions(-) diff --git a/fs/sdcardfs/derived_perm.c b/fs/sdcardfs/derived_perm.c index 1239d1cd208b..fffaad4701cf 100644 --- a/fs/sdcardfs/derived_perm.c +++ b/fs/sdcardfs/derived_perm.c @@ -176,6 +176,9 @@ void fixup_lower_ownership(struct dentry *dentry, const char *name) gid_t gid = sbi->options.fs_low_gid; struct iattr newattrs; + if (!sbi->options.gid_derivation) + return; + info = SDCARDFS_I(d_inode(dentry)); info_d = info->data; perm = info_d->perm; diff --git a/fs/sdcardfs/inode.c b/fs/sdcardfs/inode.c index 70a4e3cb8781..5e919e2956e0 100644 --- a/fs/sdcardfs/inode.c +++ b/fs/sdcardfs/inode.c @@ -34,10 +34,14 @@ const struct cred *override_fsids(struct sdcardfs_sb_info *sbi, if (!cred) return NULL; - if (data->under_obb) - uid = AID_MEDIA_OBB; - else - uid = multiuser_get_uid(data->userid, sbi->options.fs_low_uid); + if (sbi->options.gid_derivation) { + if (data->under_obb) + uid = AID_MEDIA_OBB; + else + uid = multiuser_get_uid(data->userid, sbi->options.fs_low_uid); + } else { + uid = sbi->options.fs_low_uid; + } cred->fsuid = make_kuid(&init_user_ns, uid); cred->fsgid = make_kgid(&init_user_ns, sbi->options.fs_low_gid); diff --git a/fs/sdcardfs/main.c b/fs/sdcardfs/main.c index 80825b287836..0a2b5167e9a2 100644 --- a/fs/sdcardfs/main.c +++ b/fs/sdcardfs/main.c @@ -32,6 +32,7 @@ enum { Opt_multiuser, Opt_userid, Opt_reserved_mb, + Opt_gid_derivation, Opt_err, }; @@ -43,6 +44,7 @@ static const match_table_t sdcardfs_tokens = { {Opt_mask, "mask=%u"}, {Opt_userid, "userid=%d"}, {Opt_multiuser, "multiuser"}, + {Opt_gid_derivation, "derive_gid"}, {Opt_reserved_mb, "reserved_mb=%u"}, {Opt_err, NULL} }; @@ -64,6 +66,8 @@ static int parse_options(struct super_block *sb, char *options, int silent, vfsopts->gid = 0; /* by default, 0MB is reserved */ opts->reserved_mb = 0; + /* by default, gid derivation is off */ + opts->gid_derivation = false; *debug = 0; @@ -115,6 +119,9 @@ static int parse_options(struct super_block *sb, char *options, int silent, return 0; opts->reserved_mb = option; break; + case Opt_gid_derivation: + opts->gid_derivation = true; + break; /* unknown option */ default: if (!silent) diff --git a/fs/sdcardfs/sdcardfs.h b/fs/sdcardfs/sdcardfs.h index 4e0ce49a906d..d1d8bab00fe5 100644 --- a/fs/sdcardfs/sdcardfs.h +++ b/fs/sdcardfs/sdcardfs.h @@ -219,6 +219,7 @@ struct sdcardfs_mount_options { gid_t fs_low_gid; userid_t fs_user_id; bool multiuser; + bool gid_derivation; unsigned int reserved_mb; }; diff --git a/fs/sdcardfs/super.c b/fs/sdcardfs/super.c index 7f4539b4b249..b89947d878e3 100644 --- a/fs/sdcardfs/super.c +++ b/fs/sdcardfs/super.c @@ -302,6 +302,8 @@ static int sdcardfs_show_options(struct vfsmount *mnt, struct seq_file *m, seq_printf(m, ",mask=%u", vfsopts->mask); if (opts->fs_user_id) seq_printf(m, ",userid=%u", opts->fs_user_id); + if (opts->gid_derivation) + seq_puts(m, ",derive_gid"); if (opts->reserved_mb != 0) seq_printf(m, ",reserved=%uMB", opts->reserved_mb); From dd212d89e010439cdd8f3eefc04d3f6aa136276f Mon Sep 17 00:00:00 2001 From: Daniel Rosenberg Date: Tue, 2 Jan 2018 14:44:49 -0800 Subject: [PATCH 0376/1103] ANDROID: sdcardfs: Add default_normal option The default_normal option causes mounts with the gid set to AID_SDCARD_RW to have user specific gids, as in the normal case. Signed-off-by: Daniel Rosenberg Change-Id: I9619b8ac55f41415df943484dc8db1ea986cef6f Bug: 64672411 --- fs/sdcardfs/inode.c | 7 ++++--- fs/sdcardfs/main.c | 7 +++++++ fs/sdcardfs/sdcardfs.h | 9 ++++++--- fs/sdcardfs/super.c | 2 ++ 4 files changed, 19 insertions(+), 6 deletions(-) diff --git a/fs/sdcardfs/inode.c b/fs/sdcardfs/inode.c index 5e919e2956e0..c3763c50b018 100644 --- a/fs/sdcardfs/inode.c +++ b/fs/sdcardfs/inode.c @@ -648,7 +648,7 @@ static int sdcardfs_permission(struct vfsmount *mnt, struct inode *inode, int ma */ copy_attrs(&tmp, inode); tmp.i_uid = make_kuid(&init_user_ns, top->d_uid); - tmp.i_gid = make_kgid(&init_user_ns, get_gid(mnt, top)); + tmp.i_gid = make_kgid(&init_user_ns, get_gid(mnt, inode->i_sb, top)); tmp.i_mode = (inode->i_mode & S_IFMT) | get_mode(mnt, SDCARDFS_I(inode), top); data_put(top); @@ -727,7 +727,7 @@ static int sdcardfs_setattr(struct vfsmount *mnt, struct dentry *dentry, struct */ copy_attrs(&tmp, inode); tmp.i_uid = make_kuid(&init_user_ns, top->d_uid); - tmp.i_gid = make_kgid(&init_user_ns, get_gid(mnt, top)); + tmp.i_gid = make_kgid(&init_user_ns, get_gid(mnt, dentry->d_sb, top)); tmp.i_mode = (inode->i_mode & S_IFMT) | get_mode(mnt, SDCARDFS_I(inode), top); tmp.i_size = i_size_read(inode); @@ -829,6 +829,7 @@ static int sdcardfs_fillattr(struct vfsmount *mnt, { struct sdcardfs_inode_info *info = SDCARDFS_I(inode); struct sdcardfs_inode_data *top = top_data_get(info); + struct super_block *sb = inode->i_sb; if (!top) return -EINVAL; @@ -838,7 +839,7 @@ static int sdcardfs_fillattr(struct vfsmount *mnt, stat->mode = (inode->i_mode & S_IFMT) | get_mode(mnt, info, top); stat->nlink = inode->i_nlink; stat->uid = make_kuid(&init_user_ns, top->d_uid); - stat->gid = make_kgid(&init_user_ns, get_gid(mnt, top)); + stat->gid = make_kgid(&init_user_ns, get_gid(mnt, sb, top)); stat->rdev = inode->i_rdev; stat->size = i_size_read(inode); stat->atime = inode->i_atime; diff --git a/fs/sdcardfs/main.c b/fs/sdcardfs/main.c index 0a2b5167e9a2..ac27bb301c5e 100644 --- a/fs/sdcardfs/main.c +++ b/fs/sdcardfs/main.c @@ -33,6 +33,7 @@ enum { Opt_userid, Opt_reserved_mb, Opt_gid_derivation, + Opt_default_normal, Opt_err, }; @@ -45,6 +46,7 @@ static const match_table_t sdcardfs_tokens = { {Opt_userid, "userid=%d"}, {Opt_multiuser, "multiuser"}, {Opt_gid_derivation, "derive_gid"}, + {Opt_default_normal, "default_normal"}, {Opt_reserved_mb, "reserved_mb=%u"}, {Opt_err, NULL} }; @@ -68,6 +70,7 @@ static int parse_options(struct super_block *sb, char *options, int silent, opts->reserved_mb = 0; /* by default, gid derivation is off */ opts->gid_derivation = false; + opts->default_normal = false; *debug = 0; @@ -122,6 +125,9 @@ static int parse_options(struct super_block *sb, char *options, int silent, case Opt_gid_derivation: opts->gid_derivation = true; break; + case Opt_default_normal: + opts->default_normal = true; + break; /* unknown option */ default: if (!silent) @@ -175,6 +181,7 @@ int parse_options_remount(struct super_block *sb, char *options, int silent, return 0; vfsopts->mask = option; break; + case Opt_default_normal: case Opt_multiuser: case Opt_userid: case Opt_fsuid: diff --git a/fs/sdcardfs/sdcardfs.h b/fs/sdcardfs/sdcardfs.h index d1d8bab00fe5..3da9fe94b772 100644 --- a/fs/sdcardfs/sdcardfs.h +++ b/fs/sdcardfs/sdcardfs.h @@ -220,6 +220,7 @@ struct sdcardfs_mount_options { userid_t fs_user_id; bool multiuser; bool gid_derivation; + bool default_normal; unsigned int reserved_mb; }; @@ -413,11 +414,13 @@ static inline void set_top(struct sdcardfs_inode_info *info, } static inline int get_gid(struct vfsmount *mnt, + struct super_block *sb, struct sdcardfs_inode_data *data) { - struct sdcardfs_vfsmount_options *opts = mnt->data; + struct sdcardfs_vfsmount_options *vfsopts = mnt->data; + struct sdcardfs_sb_info *sbi = SDCARDFS_SB(sb); - if (opts->gid == AID_SDCARD_RW) + if (vfsopts->gid == AID_SDCARD_RW && !sbi->options.default_normal) /* As an optimization, certain trusted system components only run * as owner but operate across all users. Since we're now handing * out the sdcard_rw GID only to trusted apps, we're okay relaxing @@ -426,7 +429,7 @@ static inline int get_gid(struct vfsmount *mnt, */ return AID_SDCARD_RW; else - return multiuser_get_uid(data->userid, opts->gid); + return multiuser_get_uid(data->userid, vfsopts->gid); } static inline int get_mode(struct vfsmount *mnt, diff --git a/fs/sdcardfs/super.c b/fs/sdcardfs/super.c index b89947d878e3..87d6f836592e 100644 --- a/fs/sdcardfs/super.c +++ b/fs/sdcardfs/super.c @@ -304,6 +304,8 @@ static int sdcardfs_show_options(struct vfsmount *mnt, struct seq_file *m, seq_printf(m, ",userid=%u", opts->fs_user_id); if (opts->gid_derivation) seq_puts(m, ",derive_gid"); + if (opts->default_normal) + seq_puts(m, ",default_normal"); if (opts->reserved_mb != 0) seq_printf(m, ",reserved=%uMB", opts->reserved_mb); From b6499fa4d5b80fccc1707b73afe9e19c4c55b5d1 Mon Sep 17 00:00:00 2001 From: Daniel Rosenberg Date: Thu, 9 Feb 2017 19:38:57 -0800 Subject: [PATCH 0377/1103] ANDROID: export security_path_chown Signed-off-by: Daniel Rosenberg BUG: 35142419 Change-Id: I05a9430a3c1bc624e019055175ad377290b4e774 --- security/security.c | 1 + 1 file changed, 1 insertion(+) diff --git a/security/security.c b/security/security.c index 736e78da1ab9..957be344cd25 100644 --- a/security/security.c +++ b/security/security.c @@ -607,6 +607,7 @@ int security_path_chown(const struct path *path, kuid_t uid, kgid_t gid) return 0; return call_int_hook(path_chown, 0, path, uid, gid); } +EXPORT_SYMBOL(security_path_chown); int security_path_chroot(const struct path *path) { From 353dd741be9e3b6e8054ee08ef8cbe49f5383308 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Mon, 30 Jan 2017 12:26:08 -0800 Subject: [PATCH 0378/1103] ANDROID: fs: Export free_fs_struct and set_fs_pwd allmodconfig builds fail with: ERROR: "free_fs_struct" undefined! ERROR: "set_fs_pwd" undefined! Export the missing symbols. Change-Id: I4877ead19d7e7f0c93d4c4cad5681364284323aa Fixes: 0ec03f845799 ("ANDROID: sdcardfs: override umask on mkdir and create") Signed-off-by: Guenter Roeck --- fs/fs_struct.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/fs/fs_struct.c b/fs/fs_struct.c index 47b0ec5d5006..987c95b950f6 100644 --- a/fs/fs_struct.c +++ b/fs/fs_struct.c @@ -45,6 +45,7 @@ void set_fs_pwd(struct fs_struct *fs, const struct path *path) if (old_pwd.dentry) path_put(&old_pwd); } +EXPORT_SYMBOL(set_fs_pwd); static inline int replace_path(struct path *p, const struct path *old, const struct path *new) { @@ -90,6 +91,7 @@ void free_fs_struct(struct fs_struct *fs) path_put(&fs->pwd); kmem_cache_free(fs_cachep, fs); } +EXPORT_SYMBOL(free_fs_struct); void exit_fs(struct task_struct *tsk) { From 9f2663fc57cad7cc1457029a2ae43ca72863d66d Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Thu, 24 Mar 2016 10:32:35 -0700 Subject: [PATCH 0379/1103] ANDROID: fs: Export d_absolute_path The 0-day build bot reports the following build error, seen if SDCARD_FS is built as module. ERROR: "d_absolute_path" undefined! Fixes: 84a1b7d3d312 ("Included sdcardfs source code for kernel 3.0") Reported-by: Fengguang Wu Signed-off-by: Guenter Roeck --- fs/d_path.c | 1 + 1 file changed, 1 insertion(+) diff --git a/fs/d_path.c b/fs/d_path.c index e8fce6b1174f..3af92cc4bdae 100644 --- a/fs/d_path.c +++ b/fs/d_path.c @@ -204,6 +204,7 @@ char *d_absolute_path(const struct path *path, return ERR_PTR(error); return res; } +EXPORT_SYMBOL(d_absolute_path); /* * same as __d_path but appends "(deleted)" for unlinked files. From 432152c44ec5564dd09d44d689cbee7cc20c3ca6 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Thu, 24 Mar 2016 10:39:14 -0700 Subject: [PATCH 0380/1103] ANDROID: mm: Export do_munmap The 0-day build bot reports the following build error, seen if SDCARD_FS is built as module. ERROR: "do_munmap" undefined! Fixes: 84a1b7d3d312 ("Included sdcardfs source code for kernel 3.0") Reported-by: Fengguang Wu Signed-off-by: Guenter Roeck --- mm/mmap.c | 1 + 1 file changed, 1 insertion(+) diff --git a/mm/mmap.c b/mm/mmap.c index 6d42dc2e792a..3e274fbd1144 100644 --- a/mm/mmap.c +++ b/mm/mmap.c @@ -2796,6 +2796,7 @@ int do_munmap(struct mm_struct *mm, unsigned long start, size_t len, return 0; } +EXPORT_SYMBOL(do_munmap); int vm_munmap(unsigned long start, size_t len) { From 289cb43844a703caf78102b1022656b498b0473d Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Mon, 30 Jan 2017 12:29:00 -0800 Subject: [PATCH 0381/1103] ANDROID: fs: Export vfs_rmdir2 allmodconfig builds fail with ERROR: "vfs_rmdir2" undefined! Export the missing function. Change-Id: I983d327e59fd34e0484f3c54d925e97d3905c19c Fixes: f9cb61dcb00c ("ANDROID: sdcardfs: User new permission2 functions") Signed-off-by: Guenter Roeck --- fs/namei.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/fs/namei.c b/fs/namei.c index b463413ec56e..bd04eef84c6f 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -3939,6 +3939,8 @@ int vfs_rmdir2(struct vfsmount *mnt, struct inode *dir, struct dentry *dentry) d_delete(dentry); return error; } +EXPORT_SYMBOL(vfs_rmdir2); + int vfs_rmdir(struct inode *dir, struct dentry *dentry) { return vfs_rmdir2(NULL, dir, dentry); From 8fa5e427b8736426fde910cb61a0f5d746ef0795 Mon Sep 17 00:00:00 2001 From: Daniel Rosenberg Date: Wed, 24 Jan 2018 17:25:05 -0800 Subject: [PATCH 0382/1103] ANDROID: sdcardfs: port to 4.14 Change-Id: I03271d8e8229ce6f22f337dc7d1938e0bf060f2a Signed-off-by: Daniel Rosenberg Bug: 70278506 --- fs/sdcardfs/inode.c | 9 +++++---- fs/sdcardfs/mmap.c | 13 ++++++------- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/fs/sdcardfs/inode.c b/fs/sdcardfs/inode.c index c3763c50b018..08ef11e94cca 100644 --- a/fs/sdcardfs/inode.c +++ b/fs/sdcardfs/inode.c @@ -850,10 +850,11 @@ static int sdcardfs_fillattr(struct vfsmount *mnt, data_put(top); return 0; } - -static int sdcardfs_getattr(struct vfsmount *mnt, struct dentry *dentry, - struct kstat *stat) +static int sdcardfs_getattr(const struct path *path, struct kstat *stat, + u32 request_mask, unsigned int flags) { + struct vfsmount *mnt = path->mnt; + struct dentry *dentry = path->dentry; struct kstat lower_stat; struct path lower_path; struct dentry *parent; @@ -867,7 +868,7 @@ static int sdcardfs_getattr(struct vfsmount *mnt, struct dentry *dentry, dput(parent); sdcardfs_get_lower_path(dentry, &lower_path); - err = vfs_getattr(&lower_path, &lower_stat); + err = vfs_getattr(&lower_path, &lower_stat, request_mask, flags); if (err) goto out; sdcardfs_copy_and_fix_attrs(d_inode(dentry), diff --git a/fs/sdcardfs/mmap.c b/fs/sdcardfs/mmap.c index 391d2a7d10e9..2847c0ec5e0a 100644 --- a/fs/sdcardfs/mmap.c +++ b/fs/sdcardfs/mmap.c @@ -20,17 +20,17 @@ #include "sdcardfs.h" -static int sdcardfs_fault(struct vm_area_struct *vma, struct vm_fault *vmf) +static int sdcardfs_fault(struct vm_fault *vmf) { int err; struct file *file; const struct vm_operations_struct *lower_vm_ops; - file = (struct file *)vma->vm_private_data; + file = (struct file *)vmf->vma->vm_private_data; lower_vm_ops = SDCARDFS_F(file)->lower_vm_ops; BUG_ON(!lower_vm_ops); - err = lower_vm_ops->fault(vma, vmf); + err = lower_vm_ops->fault(vmf); return err; } @@ -48,20 +48,19 @@ static void sdcardfs_vm_close(struct vm_area_struct *vma) fput(file); } -static int sdcardfs_page_mkwrite(struct vm_area_struct *vma, - struct vm_fault *vmf) +static int sdcardfs_page_mkwrite(struct vm_fault *vmf) { int err = 0; struct file *file; const struct vm_operations_struct *lower_vm_ops; - file = (struct file *)vma->vm_private_data; + file = (struct file *)vmf->vma->vm_private_data; lower_vm_ops = SDCARDFS_F(file)->lower_vm_ops; BUG_ON(!lower_vm_ops); if (!lower_vm_ops->page_mkwrite) goto out; - err = lower_vm_ops->page_mkwrite(vma, vmf); + err = lower_vm_ops->page_mkwrite(vmf); out: return err; } From 80ad32c395eaded1f50dc139b84c2b48ec165712 Mon Sep 17 00:00:00 2001 From: Daniel Rosenberg Date: Tue, 30 Jan 2018 14:24:02 -0800 Subject: [PATCH 0383/1103] ANDROID: Revert "fs: unexport vfs_read and vfs_write" This reverts commit bd8df82be66698042d11e7919e244c8d72b042ca. These functions are used in sdcardfs Change-Id: If740718cca903c211d1c2832c7fd95a4c4cfe20f Signed-off-by: Daniel Rosenberg --- fs/read_write.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/fs/read_write.c b/fs/read_write.c index 8a2737f0d61d..26f71acb0c48 100644 --- a/fs/read_write.c +++ b/fs/read_write.c @@ -460,6 +460,8 @@ ssize_t vfs_read(struct file *file, char __user *buf, size_t count, loff_t *pos) return ret; } +EXPORT_SYMBOL(vfs_read); + static ssize_t new_sync_write(struct file *filp, const char __user *buf, size_t len, loff_t *ppos) { struct iovec iov = { .iov_base = (void __user *)buf, .iov_len = len }; @@ -558,6 +560,8 @@ ssize_t vfs_write(struct file *file, const char __user *buf, size_t count, loff_ return ret; } +EXPORT_SYMBOL(vfs_write); + static inline loff_t file_pos_read(struct file *file) { return file->f_pos; From 920174a5f50e136d80d8324c0c96186651a84585 Mon Sep 17 00:00:00 2001 From: Daniel Rosenberg Date: Mon, 29 Jan 2018 21:31:21 -0800 Subject: [PATCH 0384/1103] ANDROID: sdcardfs: Use lower getattr times/size We now use the lower filesystem's getattr for time and size related information. Change-Id: I3dd05614a0c2837a13eeb033444fbdf070ddce2a Signed-off-by: Daniel Rosenberg Bug: 72007585 --- fs/sdcardfs/inode.c | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/fs/sdcardfs/inode.c b/fs/sdcardfs/inode.c index 08ef11e94cca..b43258684fb9 100644 --- a/fs/sdcardfs/inode.c +++ b/fs/sdcardfs/inode.c @@ -824,8 +824,8 @@ static int sdcardfs_setattr(struct vfsmount *mnt, struct dentry *dentry, struct return err; } -static int sdcardfs_fillattr(struct vfsmount *mnt, - struct inode *inode, struct kstat *stat) +static int sdcardfs_fillattr(struct vfsmount *mnt, struct inode *inode, + struct kstat *lower_stat, struct kstat *stat) { struct sdcardfs_inode_info *info = SDCARDFS_I(inode); struct sdcardfs_inode_data *top = top_data_get(info); @@ -841,12 +841,12 @@ static int sdcardfs_fillattr(struct vfsmount *mnt, stat->uid = make_kuid(&init_user_ns, top->d_uid); stat->gid = make_kgid(&init_user_ns, get_gid(mnt, sb, top)); stat->rdev = inode->i_rdev; - stat->size = i_size_read(inode); - stat->atime = inode->i_atime; - stat->mtime = inode->i_mtime; - stat->ctime = inode->i_ctime; - stat->blksize = (1 << inode->i_blkbits); - stat->blocks = inode->i_blocks; + stat->size = lower_stat->size; + stat->atime = lower_stat->atime; + stat->mtime = lower_stat->mtime; + stat->ctime = lower_stat->ctime; + stat->blksize = lower_stat->blksize; + stat->blocks = lower_stat->blocks; data_put(top); return 0; } @@ -873,8 +873,7 @@ static int sdcardfs_getattr(const struct path *path, struct kstat *stat, goto out; sdcardfs_copy_and_fix_attrs(d_inode(dentry), d_inode(lower_path.dentry)); - err = sdcardfs_fillattr(mnt, d_inode(dentry), stat); - stat->blocks = lower_stat.blocks; + err = sdcardfs_fillattr(mnt, d_inode(dentry), &lower_stat, stat); out: sdcardfs_put_lower_path(dentry, &lower_path); return err; From 873024053f92d681c6456300c6382d39c6559cd0 Mon Sep 17 00:00:00 2001 From: Daniel Rosenberg Date: Tue, 23 Jan 2018 15:02:50 -0800 Subject: [PATCH 0385/1103] ANDROID: fsnotify: Notify lower fs of open If the filesystem being watched supports d_canonical_path, notify the lower filesystem of the open as well. Change-Id: I2b1739e068afbaf5eb39950516072bff8345ebfe Signed-off-by: Daniel Rosenberg Bug: 70706497 --- include/linux/fsnotify.h | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/include/linux/fsnotify.h b/include/linux/fsnotify.h index fd1ce10553bf..61b72519f2f2 100644 --- a/include/linux/fsnotify.h +++ b/include/linux/fsnotify.h @@ -210,12 +210,19 @@ static inline void fsnotify_modify(struct file *file) static inline void fsnotify_open(struct file *file) { const struct path *path = &file->f_path; + struct path lower_path; struct inode *inode = file_inode(file); __u32 mask = FS_OPEN; if (S_ISDIR(inode->i_mode)) mask |= FS_ISDIR; + if (path->dentry->d_op && path->dentry->d_op->d_canonical_path) { + path->dentry->d_op->d_canonical_path(path, &lower_path); + fsnotify_parent(&lower_path, NULL, mask); + fsnotify(lower_path.dentry->d_inode, mask, &lower_path, FSNOTIFY_EVENT_PATH, NULL, 0); + path_put(&lower_path); + } fsnotify_parent(path, NULL, mask); fsnotify(inode, mask, path, FSNOTIFY_EVENT_PATH, NULL, 0); } From d0e2c252b546a8cd489e1560ccba7a8934de869f Mon Sep 17 00:00:00 2001 From: Daniel Rosenberg Date: Thu, 1 Feb 2018 16:52:22 -0800 Subject: [PATCH 0386/1103] ANDROID: sdcardfs: Protect set_top If the top is changed while we're attempting to use it, it's possible that the reference will be put while we are in the process of grabbing a reference. Now we grab a spinlock to protect grabbing our reference count. Additionally, we now set the inode_info's top value to point to it's own data when initializing, which makes tracking changes easier. Change-Id: If15748c786ce4c0480ab8c5051a92523aff284d2 Signed-off-by: Daniel Rosenberg --- fs/sdcardfs/derived_perm.c | 28 +++++++++++++--------------- fs/sdcardfs/main.c | 6 ++---- fs/sdcardfs/sdcardfs.h | 26 ++++++++++++++++++-------- fs/sdcardfs/super.c | 3 +++ 4 files changed, 36 insertions(+), 27 deletions(-) diff --git a/fs/sdcardfs/derived_perm.c b/fs/sdcardfs/derived_perm.c index fffaad4701cf..0b3b22334e54 100644 --- a/fs/sdcardfs/derived_perm.c +++ b/fs/sdcardfs/derived_perm.c @@ -32,23 +32,20 @@ static void inherit_derived_state(struct inode *parent, struct inode *child) ci->data->under_android = pi->data->under_android; ci->data->under_cache = pi->data->under_cache; ci->data->under_obb = pi->data->under_obb; - set_top(ci, pi->top_data); } /* helper function for derived state */ void setup_derived_state(struct inode *inode, perm_t perm, userid_t userid, - uid_t uid, bool under_android, - struct sdcardfs_inode_data *top) + uid_t uid) { struct sdcardfs_inode_info *info = SDCARDFS_I(inode); info->data->perm = perm; info->data->userid = userid; info->data->d_uid = uid; - info->data->under_android = under_android; + info->data->under_android = false; info->data->under_cache = false; info->data->under_obb = false; - set_top(info, top); } /* While renaming, there is a point where we want the path from dentry, @@ -58,8 +55,8 @@ void get_derived_permission_new(struct dentry *parent, struct dentry *dentry, const struct qstr *name) { struct sdcardfs_inode_info *info = SDCARDFS_I(d_inode(dentry)); - struct sdcardfs_inode_data *parent_data = - SDCARDFS_I(d_inode(parent))->data; + struct sdcardfs_inode_info *parent_info = SDCARDFS_I(d_inode(parent)); + struct sdcardfs_inode_data *parent_data = parent_info->data; appid_t appid; unsigned long user_num; int err; @@ -80,13 +77,15 @@ void get_derived_permission_new(struct dentry *parent, struct dentry *dentry, inherit_derived_state(d_inode(parent), d_inode(dentry)); /* Files don't get special labels */ - if (!S_ISDIR(d_inode(dentry)->i_mode)) + if (!S_ISDIR(d_inode(dentry)->i_mode)) { + set_top(info, parent_info); return; + } /* Derive custom permissions based on parent and current node */ switch (parent_data->perm) { case PERM_INHERIT: case PERM_ANDROID_PACKAGE_CACHE: - /* Already inherited above */ + set_top(info, parent_info); break; case PERM_PRE_ROOT: /* Legacy internal layout places users at top level */ @@ -96,7 +95,6 @@ void get_derived_permission_new(struct dentry *parent, struct dentry *dentry, info->data->userid = 0; else info->data->userid = user_num; - set_top(info, info->data); break; case PERM_ROOT: /* Assume masked off by default. */ @@ -104,24 +102,24 @@ void get_derived_permission_new(struct dentry *parent, struct dentry *dentry, /* App-specific directories inside; let anyone traverse */ info->data->perm = PERM_ANDROID; info->data->under_android = true; - set_top(info, info->data); + } else { + set_top(info, parent_info); } break; case PERM_ANDROID: if (qstr_case_eq(name, &q_data)) { /* App-specific directories inside; let anyone traverse */ info->data->perm = PERM_ANDROID_DATA; - set_top(info, info->data); } else if (qstr_case_eq(name, &q_obb)) { /* App-specific directories inside; let anyone traverse */ info->data->perm = PERM_ANDROID_OBB; info->data->under_obb = true; - set_top(info, info->data); /* Single OBB directory is always shared */ } else if (qstr_case_eq(name, &q_media)) { /* App-specific directories inside; let anyone traverse */ info->data->perm = PERM_ANDROID_MEDIA; - set_top(info, info->data); + } else { + set_top(info, parent_info); } break; case PERM_ANDROID_OBB: @@ -132,13 +130,13 @@ void get_derived_permission_new(struct dentry *parent, struct dentry *dentry, if (appid != 0 && !is_excluded(name->name, parent_data->userid)) info->data->d_uid = multiuser_get_uid(parent_data->userid, appid); - set_top(info, info->data); break; case PERM_ANDROID_PACKAGE: if (qstr_case_eq(name, &q_cache)) { info->data->perm = PERM_ANDROID_PACKAGE_CACHE; info->data->under_cache = true; } + set_top(info, parent_info); break; } } diff --git a/fs/sdcardfs/main.c b/fs/sdcardfs/main.c index ac27bb301c5e..e4fd3fbb05e6 100644 --- a/fs/sdcardfs/main.c +++ b/fs/sdcardfs/main.c @@ -341,13 +341,11 @@ static int sdcardfs_read_super(struct vfsmount *mnt, struct super_block *sb, mutex_lock(&sdcardfs_super_list_lock); if (sb_info->options.multiuser) { setup_derived_state(d_inode(sb->s_root), PERM_PRE_ROOT, - sb_info->options.fs_user_id, AID_ROOT, - false, SDCARDFS_I(d_inode(sb->s_root))->data); + sb_info->options.fs_user_id, AID_ROOT); snprintf(sb_info->obbpath_s, PATH_MAX, "%s/obb", dev_name); } else { setup_derived_state(d_inode(sb->s_root), PERM_ROOT, - sb_info->options.fs_user_id, AID_ROOT, - false, SDCARDFS_I(d_inode(sb->s_root))->data); + sb_info->options.fs_user_id, AID_ROOT); snprintf(sb_info->obbpath_s, PATH_MAX, "%s/Android/obb", dev_name); } fixup_tmp_permissions(d_inode(sb->s_root)); diff --git a/fs/sdcardfs/sdcardfs.h b/fs/sdcardfs/sdcardfs.h index 3da9fe94b772..610466ad153d 100644 --- a/fs/sdcardfs/sdcardfs.h +++ b/fs/sdcardfs/sdcardfs.h @@ -201,6 +201,7 @@ struct sdcardfs_inode_info { struct sdcardfs_inode_data *data; /* top folder for ownership */ + spinlock_t top_lock; struct sdcardfs_inode_data *top_data; struct inode vfs_inode; @@ -380,7 +381,12 @@ static inline struct sdcardfs_inode_data *data_get( static inline struct sdcardfs_inode_data *top_data_get( struct sdcardfs_inode_info *info) { - return data_get(info->top_data); + struct sdcardfs_inode_data *top_data; + + spin_lock(&info->top_lock); + top_data = data_get(info->top_data); + spin_unlock(&info->top_lock); + return top_data; } extern void data_release(struct kref *ref); @@ -402,15 +408,20 @@ static inline void release_own_data(struct sdcardfs_inode_info *info) } static inline void set_top(struct sdcardfs_inode_info *info, - struct sdcardfs_inode_data *top) + struct sdcardfs_inode_info *top_owner) { - struct sdcardfs_inode_data *old_top = info->top_data; + struct sdcardfs_inode_data *old_top; + struct sdcardfs_inode_data *new_top = NULL; + + if (top_owner) + new_top = top_data_get(top_owner); - if (top) - data_get(top); - info->top_data = top; + spin_lock(&info->top_lock); + old_top = info->top_data; + info->top_data = new_top; if (old_top) data_put(old_top); + spin_unlock(&info->top_lock); } static inline int get_gid(struct vfsmount *mnt, @@ -516,8 +527,7 @@ struct limit_search { }; extern void setup_derived_state(struct inode *inode, perm_t perm, - userid_t userid, uid_t uid, bool under_android, - struct sdcardfs_inode_data *top); + userid_t userid, uid_t uid); extern void get_derived_permission(struct dentry *parent, struct dentry *dentry); extern void get_derived_permission_new(struct dentry *parent, struct dentry *dentry, const struct qstr *name); extern void fixup_perms_recursive(struct dentry *dentry, struct limit_search *limit); diff --git a/fs/sdcardfs/super.c b/fs/sdcardfs/super.c index 87d6f836592e..cffcdb11cb8a 100644 --- a/fs/sdcardfs/super.c +++ b/fs/sdcardfs/super.c @@ -215,6 +215,9 @@ static struct inode *sdcardfs_alloc_inode(struct super_block *sb) i->data = d; kref_init(&d->refcount); + i->top_data = d; + spin_lock_init(&i->top_lock); + kref_get(&d->refcount); i->vfs_inode.i_version = 1; return &i->vfs_inode; From 665633d5c298307387f1cefc6b44f18079f3613f Mon Sep 17 00:00:00 2001 From: Chenbo Feng Date: Tue, 28 Nov 2017 18:22:11 -0800 Subject: [PATCH 0387/1103] ANDROID: qtaguid: Fix the UAF probelm with tag_ref_tree When multiple threads is trying to tag/delete the same socket at the same time, there is a chance the tag_ref_entry of the target socket to be null before the uid_tag_data entry is freed. It is caused by the ctrl_cmd_tag function where it doesn't correctly grab the spinlocks when tagging a socket. Signed-off-by: Chenbo Feng Bug: 65853158 Change-Id: I5d89885918054cf835370a52bff2d693362ac5f0 --- net/netfilter/xt_qtaguid.c | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/net/netfilter/xt_qtaguid.c b/net/netfilter/xt_qtaguid.c index c6a51d91f26f..cd7c34b45221 100644 --- a/net/netfilter/xt_qtaguid.c +++ b/net/netfilter/xt_qtaguid.c @@ -520,13 +520,11 @@ static struct tag_ref *get_tag_ref(tag_t full_tag, DR_DEBUG("qtaguid: get_tag_ref(0x%llx)\n", full_tag); - spin_lock_bh(&uid_tag_data_tree_lock); tr_entry = lookup_tag_ref(full_tag, &utd_entry); BUG_ON(IS_ERR_OR_NULL(utd_entry)); if (!tr_entry) tr_entry = new_tag_ref(full_tag, utd_entry); - spin_unlock_bh(&uid_tag_data_tree_lock); if (utd_res) *utd_res = utd_entry; DR_DEBUG("qtaguid: get_tag_ref(0x%llx) utd=%p tr=%p\n", @@ -2033,6 +2031,7 @@ static int ctrl_cmd_delete(const char *input) /* Delete socket tags */ spin_lock_bh(&sock_tag_list_lock); + spin_lock_bh(&uid_tag_data_tree_lock); node = rb_first(&sock_tag_tree); while (node) { st_entry = rb_entry(node, struct sock_tag, sock_node); @@ -2062,6 +2061,7 @@ static int ctrl_cmd_delete(const char *input) list_del(&st_entry->list); } } + spin_unlock_bh(&uid_tag_data_tree_lock); spin_unlock_bh(&sock_tag_list_lock); sock_tag_tree_erase(&st_to_free_tree); @@ -2271,10 +2271,12 @@ static int ctrl_cmd_tag(const char *input) full_tag = combine_atag_with_uid(acct_tag, uid_int); spin_lock_bh(&sock_tag_list_lock); + spin_lock_bh(&uid_tag_data_tree_lock); sock_tag_entry = get_sock_stat_nl(el_socket->sk); tag_ref_entry = get_tag_ref(full_tag, &uid_tag_data_entry); if (IS_ERR(tag_ref_entry)) { res = PTR_ERR(tag_ref_entry); + spin_unlock_bh(&uid_tag_data_tree_lock); spin_unlock_bh(&sock_tag_list_lock); goto err_put; } @@ -2301,9 +2303,14 @@ static int ctrl_cmd_tag(const char *input) pr_err("qtaguid: ctrl_tag(%s): " "socket tag alloc failed\n", input); + BUG_ON(tag_ref_entry->num_sock_tags <= 0); + tag_ref_entry->num_sock_tags--; + free_tag_ref_from_utd_entry(tag_ref_entry, + uid_tag_data_entry); + spin_unlock_bh(&uid_tag_data_tree_lock); spin_unlock_bh(&sock_tag_list_lock); res = -ENOMEM; - goto err_tag_unref_put; + goto err_put; } /* * Hold the sk refcount here to make sure the sk pointer cannot @@ -2313,7 +2320,6 @@ static int ctrl_cmd_tag(const char *input) sock_tag_entry->sk = el_socket->sk; sock_tag_entry->pid = current->tgid; sock_tag_entry->tag = combine_atag_with_uid(acct_tag, uid_int); - spin_lock_bh(&uid_tag_data_tree_lock); pqd_entry = proc_qtu_data_tree_search( &proc_qtu_data_tree, current->tgid); /* @@ -2331,11 +2337,11 @@ static int ctrl_cmd_tag(const char *input) else list_add(&sock_tag_entry->list, &pqd_entry->sock_tag_list); - spin_unlock_bh(&uid_tag_data_tree_lock); sock_tag_tree_insert(sock_tag_entry, &sock_tag_tree); atomic64_inc(&qtu_events.sockets_tagged); } + spin_unlock_bh(&uid_tag_data_tree_lock); spin_unlock_bh(&sock_tag_list_lock); /* We keep the ref to the sk until it is untagged */ CT_DEBUG("qtaguid: ctrl_tag(%s): done st@%p ...->sk_refcnt=%d\n", @@ -2344,10 +2350,6 @@ static int ctrl_cmd_tag(const char *input) sockfd_put(el_socket); return 0; -err_tag_unref_put: - BUG_ON(tag_ref_entry->num_sock_tags <= 0); - tag_ref_entry->num_sock_tags--; - free_tag_ref_from_utd_entry(tag_ref_entry, uid_tag_data_entry); err_put: CT_DEBUG("qtaguid: ctrl_tag(%s): done. ...->sk_refcnt=%d\n", input, refcount_read(&el_socket->sk->sk_refcnt) - 1); From bec74dea4a19e33b08cda45bc45e48e2f1b8eba3 Mon Sep 17 00:00:00 2001 From: Daniel Rosenberg Date: Tue, 20 Feb 2018 20:25:45 -0800 Subject: [PATCH 0388/1103] ANDROID: sdcardfs: Hold i_mutex for i_size_write When we call i_size_write, we must be holding i_mutex to avoid possible lockups on 32 bit/SMP architectures. This is not necessary on 64 bit architectures. Change-Id: Ic3b946507c54d81b5c9046f9b57d25d4b0f9feef Signed-off-by: Daniel Rosenberg Bug: 73287721 --- fs/sdcardfs/file.c | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/fs/sdcardfs/file.c b/fs/sdcardfs/file.c index 5ac0b0bbb0ec..2879d1291a11 100644 --- a/fs/sdcardfs/file.c +++ b/fs/sdcardfs/file.c @@ -62,6 +62,7 @@ static ssize_t sdcardfs_write(struct file *file, const char __user *buf, int err; struct file *lower_file; struct dentry *dentry = file->f_path.dentry; + struct inode *inode = d_inode(dentry); /* check disk space */ if (!check_min_free_space(dentry, count, 0)) { @@ -73,10 +74,12 @@ static ssize_t sdcardfs_write(struct file *file, const char __user *buf, err = vfs_write(lower_file, buf, count, ppos); /* update our inode times+sizes upon a successful lower write */ if (err >= 0) { - fsstack_copy_inode_size(d_inode(dentry), - file_inode(lower_file)); - fsstack_copy_attr_times(d_inode(dentry), - file_inode(lower_file)); + if (sizeof(loff_t) > sizeof(long)) + inode_lock(inode); + fsstack_copy_inode_size(inode, file_inode(lower_file)); + fsstack_copy_attr_times(inode, file_inode(lower_file)); + if (sizeof(loff_t) > sizeof(long)) + inode_unlock(inode); } return err; @@ -391,6 +394,7 @@ ssize_t sdcardfs_write_iter(struct kiocb *iocb, struct iov_iter *iter) { int err; struct file *file = iocb->ki_filp, *lower_file; + struct inode *inode = file->f_path.dentry->d_inode; lower_file = sdcardfs_lower_file(file); if (!lower_file->f_op->write_iter) { @@ -405,10 +409,12 @@ ssize_t sdcardfs_write_iter(struct kiocb *iocb, struct iov_iter *iter) fput(lower_file); /* update upper inode times/sizes as needed */ if (err >= 0 || err == -EIOCBQUEUED) { - fsstack_copy_inode_size(file->f_path.dentry->d_inode, - file_inode(lower_file)); - fsstack_copy_attr_times(file->f_path.dentry->d_inode, - file_inode(lower_file)); + if (sizeof(loff_t) > sizeof(long)) + inode_lock(inode); + fsstack_copy_inode_size(inode, file_inode(lower_file)); + fsstack_copy_attr_times(inode, file_inode(lower_file)); + if (sizeof(loff_t) > sizeof(long)) + inode_lock(inode); } out: return err; From a0d9b3935e9e52907323cc496e0f480ff0c6eaa6 Mon Sep 17 00:00:00 2001 From: Ritesh Harjani Date: Mon, 4 Dec 2017 09:51:07 +0530 Subject: [PATCH 0389/1103] ANDROID: sdcardfs: Set num in extension_details during make_item Without this patch when you delete an extension from configfs it still exists in the hash table data structures and we are unable to delete it or change it's group. This happens because during deletion the key & value is taken from extension_details, and was not properly set. Fix it by this patch. Change-Id: I7c20cb1ab4d99e6aceadcb5ef850f0bb47f18be8 Signed-off-by: Ritesh Harjani Signed-off-by: Daniel Rosenberg Bug: 73055997 --- fs/sdcardfs/packagelist.c | 1 + 1 file changed, 1 insertion(+) diff --git a/fs/sdcardfs/packagelist.c b/fs/sdcardfs/packagelist.c index 6da0c2186d39..4b9a5635f1e0 100644 --- a/fs/sdcardfs/packagelist.c +++ b/fs/sdcardfs/packagelist.c @@ -659,6 +659,7 @@ static struct config_item *extension_details_make_item(struct config_group *grou return ERR_PTR(-ENOMEM); } qstr_init(&extension_details->name, tmp); + extension_details->num = extensions_value->num; ret = insert_ext_gid_entry(&extension_details->name, extensions_value->num); if (ret) { From 35069b1afa81b81ef29dc14b9273710e8316430b Mon Sep 17 00:00:00 2001 From: Dmitry Shmidt Date: Wed, 7 Mar 2018 14:11:03 -0800 Subject: [PATCH 0390/1103] ANDROID: uid_sys_stats: Copy task_struct comm field to bigger buffer get_task_comm() currently checks if buf_size != TASK_COMM_LEN and fails even if sizeof(buf) > TASK_COMM_LEN. Change-Id: Icb3e9c172607534ef1db10baf5d626083db73498 Signed-off-by: Dmitry Shmidt --- drivers/misc/uid_sys_stats.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/misc/uid_sys_stats.c b/drivers/misc/uid_sys_stats.c index 9f5b6d1dde0a..ace629934821 100644 --- a/drivers/misc/uid_sys_stats.c +++ b/drivers/misc/uid_sys_stats.c @@ -129,7 +129,7 @@ static void get_full_task_comm(struct task_entry *task_entry, struct mm_struct *mm = task->mm; /* fill the first TASK_COMM_LEN bytes with thread name */ - get_task_comm(task_entry->comm, task); + __get_task_comm(task_entry->comm, TASK_COMM_LEN, task); i = strlen(task_entry->comm); while (i < TASK_COMM_LEN) task_entry->comm[i++] = ' '; From dfd26b310e27e6fa8a4d392d9794882daea13d41 Mon Sep 17 00:00:00 2001 From: Daniel Rosenberg Date: Thu, 15 Mar 2018 19:37:16 -0700 Subject: [PATCH 0391/1103] ANDROID: sdcardfs: fix lock issue on 32 bit/SMP architectures Fixes: cc668ff4b6a1 ("ANDROID: sdcardfs: Hold i_mutex for i_size_write") Change-Id: If7f2ed90f59c552b9ef9262b0f6aaed394f68784 Signed-off-by: Daniel Rosenberg Bug: 73287721 --- fs/sdcardfs/file.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/sdcardfs/file.c b/fs/sdcardfs/file.c index 2879d1291a11..1461254f301d 100644 --- a/fs/sdcardfs/file.c +++ b/fs/sdcardfs/file.c @@ -414,7 +414,7 @@ ssize_t sdcardfs_write_iter(struct kiocb *iocb, struct iov_iter *iter) fsstack_copy_inode_size(inode, file_inode(lower_file)); fsstack_copy_attr_times(inode, file_inode(lower_file)); if (sizeof(loff_t) > sizeof(long)) - inode_lock(inode); + inode_unlock(inode); } out: return err; From 2f0ddfa29011f292e7527f4544c2d2adf0ef59d7 Mon Sep 17 00:00:00 2001 From: Amit Pundir Date: Mon, 26 Mar 2018 20:43:33 +0530 Subject: [PATCH 0392/1103] ANDROID: arm64: Image.gz-dtb build target depends on Image.gz While doing parallel builds using "make -j" option, I ran into a build race condition a few times where-in Image.gz-dtb target starts building before Image.gz is even ready, resulting in a corrupt Image.gz-dtb kernel image. How to reproduce --> $ make ARCH=arm64 CROSS_COMPILE=aarch64-linux-androidkernel- defconfig menuconfig + CONFIG_BUILD_ARM64_APPENDED_DTB_IMAGE=y CONFIG_BUILD_ARM64_APPENDED_DTB_IMAGE_NAMES="qcom/apq8096-db820c" $ make ARCH=arm64 CROSS_COMPILE=aarch64-linux-androidkernel- -j9 .. SYSMAP System.map OBJCOPY arch/arm64/boot/Image GZIP arch/arm64/boot/Image.gz DTC arch/arm64/boot/dts/qcom/apq8096-db820c.dtb Building modules, stage 2. CAT arch/arm64/boot/Image.gz-dtb GZIP arch/arm64/boot/Image.gz .. $ du -sh arch/arm64/boot/Image.gz-dtb 28K arch/arm64/boot/Image.gz-dtb When built with this patch --> $ du -sh arch/arm64/boot/Image.gz-dtb 8.9M arch/arm64/boot/Image.gz-dtb Let's make Image.gz-dtb build target depend on Image.gz explicitly. Signed-off-by: Amit Pundir --- arch/arm64/Makefile | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/arch/arm64/Makefile b/arch/arm64/Makefile index 2dbca8c11498..0b0f5b7cead1 100644 --- a/arch/arm64/Makefile +++ b/arch/arm64/Makefile @@ -143,7 +143,10 @@ dtbs: prepare scripts dtbs_install: $(Q)$(MAKE) $(dtbinst)=$(boot)/dts -Image-dtb Image.gz-dtb: vmlinux scripts dtbs +Image-dtb: vmlinux scripts dtbs + $(Q)$(MAKE) $(build)=$(boot) $(boot)/$@ + +Image.gz-dtb: vmlinux scripts dtbs Image.gz $(Q)$(MAKE) $(build)=$(boot) $(boot)/$@ PHONY += vdso_install From ac45fe3de669254ddade4236f323a95775c7b92e Mon Sep 17 00:00:00 2001 From: Ritesh Harjani Date: Mon, 19 Mar 2018 15:49:54 +0530 Subject: [PATCH 0393/1103] ANDROID: sdcardfs: Fix sdcardfs to stop creating cases-sensitive duplicate entries. sdcardfs_name_match gets a 'name' argument from the underlying FS. This need not be null terminated string. So in sdcardfs_name_match -> qstr_case_eq -> we should use str_n_case_eq. This happens because few of the entries in lower level FS may not be NULL terminated and may have some garbage characters passed while doing sdcardfs_name_match. For e.g. # dmesg |grep Download [ 103.646386] sdcardfs_name_match: q1->name=.nomedia, q1->len=8, q2->name=Download\x17\x80\x03, q2->len=8 [ 104.021340] sdcardfs_name_match: q1->name=.nomedia, q1->len=8, q2->name=Download\x17\x80\x03, q2->len=8 [ 105.196864] sdcardfs_name_match: q1->name=.nomedia, q1->len=8, q2->name=Download\x17\x80\x03, q2->len=8 [ 109.113521] sdcardfs_name_match: q1->name=logs, q1->len=4, q2->name=Download\x17\x80\x03, q2->len=8 Now when we try to create a directory with different case for a such files. SDCARDFS creates a entry if it could not find the underlying entry in it's dcache. To reproduce:- 1. bootup the device wait for some time after sdcardfs mounting to complete. 2. cd /storage/emulated/0 3. echo 3 > /proc/sys/vm/drop_caches 4. mkdir download We now start seeing two entries with name. Download & download. Change-Id: I976d92a220a607dd8cdb96c01c2041c5c2bc3326 Signed-off-by: Ritesh Harjani bug: 75987238 --- fs/sdcardfs/sdcardfs.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/sdcardfs/sdcardfs.h b/fs/sdcardfs/sdcardfs.h index 610466ad153d..826afb5c7e88 100644 --- a/fs/sdcardfs/sdcardfs.h +++ b/fs/sdcardfs/sdcardfs.h @@ -669,7 +669,7 @@ static inline bool str_n_case_eq(const char *s1, const char *s2, size_t len) static inline bool qstr_case_eq(const struct qstr *q1, const struct qstr *q2) { - return q1->len == q2->len && str_case_eq(q1->name, q2->name); + return q1->len == q2->len && str_n_case_eq(q1->name, q2->name, q2->len); } #define QSTR_LITERAL(string) QSTR_INIT(string, sizeof(string)-1) From 74714623967425c96fff51e5642d99df96f958ce Mon Sep 17 00:00:00 2001 From: Ritesh Harjani Date: Mon, 19 Mar 2018 16:03:09 +0530 Subject: [PATCH 0394/1103] ANDROID: fuse: Add null terminator to path in canonical path to avoid issue page allocated in fuse_dentry_canonical_path to be handled in fuse_dev_do_write is allocated using __get_free_pages(GFP_KERNEL). This may not return a page with data filled with 0. Now this page may not have a null terminator at all. If this happens and userspace fuse daemon screws up by passing a string to kernel which is not NULL terminated (or did not fill anything), then inside fuse driver in kernel when we try to do strlen(fuse_dev_write->kern_path->getname_kernel) on that page data -> it may give us issue with kernel paging request. Unable to handle kernel paging request at virtual address ------------[ cut here ]------------ <..> PC is at strlen+0x10/0x90 LR is at getname_kernel+0x2c/0xf4 <..> strlen+0x10/0x90 kern_path+0x28/0x4c fuse_dev_do_write+0x5b8/0x694 fuse_dev_write+0x74/0x94 do_iter_readv_writev+0x80/0xb8 do_readv_writev+0xec/0x1cc vfs_writev+0x54/0x64 SyS_writev+0x64/0xe4 el0_svc_naked+0x24/0x28 To avoid this we should ensure in case of FUSE_CANONICAL_PATH, the page is null terminated. Change-Id: I33ca7cc76b4472eaa982c67bb20685df451121f5 Signed-off-by: Ritesh Harjani Bug: 75984715 [Daniel - small edit, using args size ] Signed-off-by: Daniel Rosenberg --- fs/fuse/dev.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/fs/fuse/dev.c b/fs/fuse/dev.c index 9fc81f1f0e7e..c5d395f4f181 100644 --- a/fs/fuse/dev.c +++ b/fs/fuse/dev.c @@ -1902,8 +1902,10 @@ static ssize_t fuse_dev_do_write(struct fuse_dev *fud, err = copy_out_args(cs, &req->out, nbytes); if (req->in.h.opcode == FUSE_CANONICAL_PATH) { - req->out.h.error = kern_path((char *)req->out.args[0].value, 0, - req->canonical_path); + char *path = (char *)req->out.args[0].value; + + path[req->out.args[0].size - 1] = 0; + req->out.h.error = kern_path(path, 0, req->canonical_path); } fuse_copy_finish(cs); From a49713d31d66a2863541c760be11942948f06727 Mon Sep 17 00:00:00 2001 From: Connor O'Brien Date: Wed, 31 Jan 2018 18:11:57 -0800 Subject: [PATCH 0395/1103] ANDROID: cpufreq: track per-task time in state Add time in state data to task structs, and create /proc//time_in_state files to show how long each individual task has run at each frequency. Create a CONFIG_CPU_FREQ_TIMES option to enable/disable this tracking. Signed-off-by: Connor O'Brien Bug: 72339335 Test: Read /proc//time_in_state Change-Id: Ia6456754f4cb1e83b2bc35efa8fbe9f8696febc8 --- drivers/cpufreq/Kconfig | 7 ++ drivers/cpufreq/Makefile | 5 +- drivers/cpufreq/cpufreq.c | 3 + drivers/cpufreq/cpufreq_times.c | 204 ++++++++++++++++++++++++++++++++ fs/proc/base.c | 7 ++ include/linux/cpufreq_times.h | 35 ++++++ include/linux/sched.h | 4 + kernel/exit.c | 4 + kernel/sched/core.c | 5 + kernel/sched/cputime.c | 10 ++ 10 files changed, 283 insertions(+), 1 deletion(-) create mode 100644 drivers/cpufreq/cpufreq_times.c create mode 100644 include/linux/cpufreq_times.h diff --git a/drivers/cpufreq/Kconfig b/drivers/cpufreq/Kconfig index 608af20a3494..e1312374725b 100644 --- a/drivers/cpufreq/Kconfig +++ b/drivers/cpufreq/Kconfig @@ -37,6 +37,13 @@ config CPU_FREQ_STAT If in doubt, say N. +config CPU_FREQ_TIMES + bool "CPU frequency time-in-state statistics" + help + Export CPU time-in-state information through procfs. + + If in doubt, say N. + choice prompt "Default CPUFreq governor" default CPU_FREQ_DEFAULT_GOV_USERSPACE if ARM_SA1100_CPUFREQ || ARM_SA1110_CPUFREQ diff --git a/drivers/cpufreq/Makefile b/drivers/cpufreq/Makefile index c1ffeabe4ecf..648beca8ad41 100644 --- a/drivers/cpufreq/Makefile +++ b/drivers/cpufreq/Makefile @@ -5,7 +5,10 @@ obj-$(CONFIG_CPU_FREQ) += cpufreq.o freq_table.o # CPUfreq stats obj-$(CONFIG_CPU_FREQ_STAT) += cpufreq_stats.o -# CPUfreq governors +# CPUfreq times +obj-$(CONFIG_CPU_FREQ_TIMES) += cpufreq_times.o + +# CPUfreq governors obj-$(CONFIG_CPU_FREQ_GOV_PERFORMANCE) += cpufreq_performance.o obj-$(CONFIG_CPU_FREQ_GOV_POWERSAVE) += cpufreq_powersave.o obj-$(CONFIG_CPU_FREQ_GOV_USERSPACE) += cpufreq_userspace.o diff --git a/drivers/cpufreq/cpufreq.c b/drivers/cpufreq/cpufreq.c index f53fb41efb7b..98b5bac02dff 100644 --- a/drivers/cpufreq/cpufreq.c +++ b/drivers/cpufreq/cpufreq.c @@ -19,6 +19,7 @@ #include #include +#include #include #include #include @@ -349,6 +350,7 @@ static void cpufreq_notify_transition(struct cpufreq_policy *policy, } cpufreq_stats_record_transition(policy, freqs->new); + cpufreq_times_record_transition(freqs); policy->cur = freqs->new; } } @@ -1295,6 +1297,7 @@ static int cpufreq_online(unsigned int cpu) goto out_destroy_policy; cpufreq_stats_create_table(policy); + cpufreq_times_create_policy(policy); write_lock_irqsave(&cpufreq_driver_lock, flags); list_add(&policy->policy_list, &cpufreq_policy_list); diff --git a/drivers/cpufreq/cpufreq_times.c b/drivers/cpufreq/cpufreq_times.c new file mode 100644 index 000000000000..fa46fcec5388 --- /dev/null +++ b/drivers/cpufreq/cpufreq_times.c @@ -0,0 +1,204 @@ +/* drivers/cpufreq/cpufreq_times.c + * + * Copyright (C) 2018 Google, Inc. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +static DEFINE_SPINLOCK(task_time_in_state_lock); /* task->time_in_state */ + +/** + * struct cpu_freqs - per-cpu frequency information + * @offset: start of these freqs' stats in task time_in_state array + * @max_state: number of entries in freq_table + * @last_index: index in freq_table of last frequency switched to + * @freq_table: list of available frequencies + */ +struct cpu_freqs { + unsigned int offset; + unsigned int max_state; + unsigned int last_index; + unsigned int freq_table[0]; +}; + +static struct cpu_freqs *all_freqs[NR_CPUS]; + +static unsigned int next_offset; + +void cpufreq_task_times_init(struct task_struct *p) +{ + void *temp; + unsigned long flags; + unsigned int max_state; + + spin_lock_irqsave(&task_time_in_state_lock, flags); + p->time_in_state = NULL; + spin_unlock_irqrestore(&task_time_in_state_lock, flags); + p->max_state = 0; + + max_state = READ_ONCE(next_offset); + + /* We use one array to avoid multiple allocs per task */ + temp = kcalloc(max_state, sizeof(p->time_in_state[0]), GFP_ATOMIC); + if (!temp) + return; + + spin_lock_irqsave(&task_time_in_state_lock, flags); + p->time_in_state = temp; + spin_unlock_irqrestore(&task_time_in_state_lock, flags); + p->max_state = max_state; +} + +/* Caller must hold task_time_in_state_lock */ +static int cpufreq_task_times_realloc_locked(struct task_struct *p) +{ + void *temp; + unsigned int max_state = READ_ONCE(next_offset); + + temp = krealloc(p->time_in_state, max_state * sizeof(u64), GFP_ATOMIC); + if (!temp) + return -ENOMEM; + p->time_in_state = temp; + memset(p->time_in_state + p->max_state, 0, + (max_state - p->max_state) * sizeof(u64)); + p->max_state = max_state; + return 0; +} + +void cpufreq_task_times_exit(struct task_struct *p) +{ + unsigned long flags; + void *temp; + + spin_lock_irqsave(&task_time_in_state_lock, flags); + temp = p->time_in_state; + p->time_in_state = NULL; + spin_unlock_irqrestore(&task_time_in_state_lock, flags); + kfree(temp); +} + +int proc_time_in_state_show(struct seq_file *m, struct pid_namespace *ns, + struct pid *pid, struct task_struct *p) +{ + unsigned int cpu, i; + u64 cputime; + unsigned long flags; + struct cpu_freqs *freqs; + struct cpu_freqs *last_freqs = NULL; + + spin_lock_irqsave(&task_time_in_state_lock, flags); + for_each_possible_cpu(cpu) { + freqs = all_freqs[cpu]; + if (!freqs || freqs == last_freqs) + continue; + last_freqs = freqs; + + seq_printf(m, "cpu%u\n", cpu); + for (i = 0; i < freqs->max_state; i++) { + if (freqs->freq_table[i] == CPUFREQ_ENTRY_INVALID) + continue; + cputime = 0; + if (freqs->offset + i < p->max_state && + p->time_in_state) + cputime = p->time_in_state[freqs->offset + i]; + seq_printf(m, "%u %lu\n", freqs->freq_table[i], + (unsigned long)nsec_to_clock_t(cputime)); + } + } + spin_unlock_irqrestore(&task_time_in_state_lock, flags); + return 0; +} + +void cpufreq_acct_update_power(struct task_struct *p, u64 cputime) +{ + unsigned long flags; + unsigned int state; + struct cpu_freqs *freqs = all_freqs[task_cpu(p)]; + + if (!freqs || p->flags & PF_EXITING) + return; + + state = freqs->offset + READ_ONCE(freqs->last_index); + + spin_lock_irqsave(&task_time_in_state_lock, flags); + if ((state < p->max_state || !cpufreq_task_times_realloc_locked(p)) && + p->time_in_state) + p->time_in_state[state] += cputime; + spin_unlock_irqrestore(&task_time_in_state_lock, flags); +} + +void cpufreq_times_create_policy(struct cpufreq_policy *policy) +{ + int cpu, index; + unsigned int count = 0; + struct cpufreq_frequency_table *pos, *table; + struct cpu_freqs *freqs; + void *tmp; + + if (all_freqs[policy->cpu]) + return; + + table = policy->freq_table; + if (!table) + return; + + cpufreq_for_each_entry(pos, table) + count++; + + tmp = kzalloc(sizeof(*freqs) + sizeof(freqs->freq_table[0]) * count, + GFP_KERNEL); + if (!tmp) + return; + + freqs = tmp; + freqs->max_state = count; + + index = cpufreq_frequency_table_get_index(policy, policy->cur); + if (index >= 0) + WRITE_ONCE(freqs->last_index, index); + + cpufreq_for_each_entry(pos, table) + freqs->freq_table[pos - table] = pos->frequency; + + freqs->offset = next_offset; + WRITE_ONCE(next_offset, freqs->offset + count); + for_each_cpu(cpu, policy->related_cpus) + all_freqs[cpu] = freqs; +} + +void cpufreq_times_record_transition(struct cpufreq_freqs *freq) +{ + int index; + struct cpu_freqs *freqs = all_freqs[freq->cpu]; + struct cpufreq_policy *policy; + + if (!freqs) + return; + + policy = cpufreq_cpu_get(freq->cpu); + if (!policy) + return; + + index = cpufreq_frequency_table_get_index(policy, freq->new); + if (index >= 0) + WRITE_ONCE(freqs->last_index, index); + + cpufreq_cpu_put(policy); +} diff --git a/fs/proc/base.c b/fs/proc/base.c index 7e9f07bf260d..b51e81e1158c 100644 --- a/fs/proc/base.c +++ b/fs/proc/base.c @@ -94,6 +94,7 @@ #include #include #include +#include #include #include "internal.h" #include "fd.h" @@ -3006,6 +3007,9 @@ static const struct pid_entry tgid_base_stuff[] = { #ifdef CONFIG_LIVEPATCH ONE("patch_state", S_IRUSR, proc_pid_patch_state), #endif +#ifdef CONFIG_CPU_FREQ_TIMES + ONE("time_in_state", 0444, proc_time_in_state_show), +#endif }; static int proc_tgid_base_readdir(struct file *file, struct dir_context *ctx) @@ -3384,6 +3388,9 @@ static const struct pid_entry tid_base_stuff[] = { #ifdef CONFIG_LIVEPATCH ONE("patch_state", S_IRUSR, proc_pid_patch_state), #endif +#ifdef CONFIG_CPU_FREQ_TIMES + ONE("time_in_state", 0444, proc_time_in_state_show), +#endif }; static int proc_tid_base_readdir(struct file *file, struct dir_context *ctx) diff --git a/include/linux/cpufreq_times.h b/include/linux/cpufreq_times.h new file mode 100644 index 000000000000..8cdbbc8fccec --- /dev/null +++ b/include/linux/cpufreq_times.h @@ -0,0 +1,35 @@ +/* drivers/cpufreq/cpufreq_times.c + * + * Copyright (C) 2018 Google, Inc. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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. + * + */ + +#ifndef _LINUX_CPUFREQ_TIMES_H +#define _LINUX_CPUFREQ_TIMES_H + +#include +#include + +#ifdef CONFIG_CPU_FREQ_TIMES +void cpufreq_task_times_init(struct task_struct *p); +void cpufreq_task_times_exit(struct task_struct *p); +int proc_time_in_state_show(struct seq_file *m, struct pid_namespace *ns, + struct pid *pid, struct task_struct *p); +void cpufreq_acct_update_power(struct task_struct *p, u64 cputime); +void cpufreq_times_create_policy(struct cpufreq_policy *policy); +void cpufreq_times_record_transition(struct cpufreq_freqs *freq); +#else +static inline void cpufreq_times_create_policy(struct cpufreq_policy *policy) {} +static inline void cpufreq_times_record_transition( + struct cpufreq_freqs *freq) {} +#endif /* CONFIG_CPU_FREQ_TIMES */ +#endif /* _LINUX_CPUFREQ_TIMES_H */ diff --git a/include/linux/sched.h b/include/linux/sched.h index 977cb57d7bc9..97f4d64d0b92 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -799,6 +799,10 @@ struct task_struct { u64 stimescaled; #endif u64 gtime; +#ifdef CONFIG_CPU_FREQ_TIMES + u64 *time_in_state; + unsigned int max_state; +#endif struct prev_cputime prev_cputime; #ifdef CONFIG_VIRT_CPU_ACCOUNTING_GEN struct vtime vtime; diff --git a/kernel/exit.c b/kernel/exit.c index 0e21e6d21f35..07eceecc6402 100644 --- a/kernel/exit.c +++ b/kernel/exit.c @@ -62,6 +62,7 @@ #include #include #include +#include #include #include @@ -186,6 +187,9 @@ void release_task(struct task_struct *p) { struct task_struct *leader; int zap_leader; +#ifdef CONFIG_CPU_FREQ_TIMES + cpufreq_task_times_exit(p); +#endif repeat: /* don't need to get the RCU readlock here - the process is dead and * can't be modifying its own credentials. But shut RCU-lockdep up */ diff --git a/kernel/sched/core.c b/kernel/sched/core.c index ad97f3ba5ec5..6b2a27bd29bc 100644 --- a/kernel/sched/core.c +++ b/kernel/sched/core.c @@ -6,6 +6,7 @@ * Copyright (C) 1991-2002 Linus Torvalds */ #include "sched.h" +#include #include @@ -2156,6 +2157,10 @@ static void __sched_fork(unsigned long clone_flags, struct task_struct *p) memset(&p->se.statistics, 0, sizeof(p->se.statistics)); #endif +#ifdef CONFIG_CPU_FREQ_TIMES + cpufreq_task_times_init(p); +#endif + RB_CLEAR_NODE(&p->dl.rb_node); init_dl_task_timer(&p->dl); init_dl_inactive_task_timer(&p->dl); diff --git a/kernel/sched/cputime.c b/kernel/sched/cputime.c index 0796f938c4f0..ab9b6ff7117a 100644 --- a/kernel/sched/cputime.c +++ b/kernel/sched/cputime.c @@ -1,6 +1,7 @@ /* * Simple CPU accounting cgroup controller */ +#include #include "sched.h" #ifdef CONFIG_IRQ_TIME_ACCOUNTING @@ -128,6 +129,11 @@ void account_user_time(struct task_struct *p, u64 cputime) /* Account for user time used */ acct_account_cputime(p); + +#ifdef CONFIG_CPU_FREQ_TIMES + /* Account power usage for user time */ + cpufreq_acct_update_power(p, cputime); +#endif } /* @@ -172,6 +178,10 @@ void account_system_index_time(struct task_struct *p, /* Account for system time used */ acct_account_cputime(p); +#ifdef CONFIG_CPU_FREQ_TIMES + /* Account power usage for system time */ + cpufreq_acct_update_power(p, cputime); +#endif } /* From 06ab83942fb4c297df00b2b124288ed596ca353d Mon Sep 17 00:00:00 2001 From: Connor O'Brien Date: Tue, 6 Feb 2018 13:30:27 -0800 Subject: [PATCH 0396/1103] ANDROID: cpufreq: times: track per-uid time in state Add /proc/uid_time_in_state showing per uid/frequency/cluster times. Allow uid removal through /proc/uid_cputime/remove_uid_range. Signed-off-by: Connor O'Brien Bug: 72339335 Test: Read /proc/uid_time_in_state Change-Id: I20ba3546a27c25b7e7991e2a86986e158aafa58c --- drivers/cpufreq/cpufreq_times.c | 208 ++++++++++++++++++++++++++++++++ drivers/misc/uid_sys_stats.c | 5 + include/linux/cpufreq_times.h | 3 + 3 files changed, 216 insertions(+) diff --git a/drivers/cpufreq/cpufreq_times.c b/drivers/cpufreq/cpufreq_times.c index fa46fcec5388..5e19ac5c8157 100644 --- a/drivers/cpufreq/cpufreq_times.c +++ b/drivers/cpufreq/cpufreq_times.c @@ -15,14 +15,30 @@ #include #include +#include +#include #include +#include #include #include #include #include #include +#define UID_HASH_BITS 10 + +static DECLARE_HASHTABLE(uid_hash_table, UID_HASH_BITS); + static DEFINE_SPINLOCK(task_time_in_state_lock); /* task->time_in_state */ +static DEFINE_SPINLOCK(uid_lock); /* uid_hash_table */ + +struct uid_entry { + uid_t uid; + unsigned int max_state; + struct hlist_node hash; + struct rcu_head rcu; + u64 time_in_state[0]; +}; /** * struct cpu_freqs - per-cpu frequency information @@ -42,6 +58,137 @@ static struct cpu_freqs *all_freqs[NR_CPUS]; static unsigned int next_offset; +/* Caller must hold uid lock */ +static struct uid_entry *find_uid_entry_locked(uid_t uid) +{ + struct uid_entry *uid_entry; + + hash_for_each_possible(uid_hash_table, uid_entry, hash, uid) { + if (uid_entry->uid == uid) + return uid_entry; + } + return NULL; +} + +/* Caller must hold uid lock */ +static struct uid_entry *find_or_register_uid_locked(uid_t uid) +{ + struct uid_entry *uid_entry, *temp; + unsigned int max_state = READ_ONCE(next_offset); + size_t alloc_size = sizeof(*uid_entry) + max_state * + sizeof(uid_entry->time_in_state[0]); + + uid_entry = find_uid_entry_locked(uid); + if (uid_entry) { + if (uid_entry->max_state == max_state) + return uid_entry; + /* uid_entry->time_in_state is too small to track all freqs, so + * expand it. + */ + temp = __krealloc(uid_entry, alloc_size, GFP_ATOMIC); + if (!temp) + return uid_entry; + temp->max_state = max_state; + memset(temp->time_in_state + uid_entry->max_state, 0, + (max_state - uid_entry->max_state) * + sizeof(uid_entry->time_in_state[0])); + if (temp != uid_entry) { + hlist_replace_rcu(&uid_entry->hash, &temp->hash); + kfree_rcu(uid_entry, rcu); + } + return temp; + } + + uid_entry = kzalloc(alloc_size, GFP_ATOMIC); + if (!uid_entry) + return NULL; + + uid_entry->uid = uid; + uid_entry->max_state = max_state; + + hash_add_rcu(uid_hash_table, &uid_entry->hash, uid); + + return uid_entry; +} + +static bool freq_index_invalid(unsigned int index) +{ + unsigned int cpu; + struct cpu_freqs *freqs; + + for_each_possible_cpu(cpu) { + freqs = all_freqs[cpu]; + if (!freqs || index < freqs->offset || + freqs->offset + freqs->max_state <= index) + continue; + return freqs->freq_table[index - freqs->offset] == + CPUFREQ_ENTRY_INVALID; + } + return true; +} + +static void *uid_seq_start(struct seq_file *seq, loff_t *pos) +{ + if (*pos >= HASH_SIZE(uid_hash_table)) + return NULL; + + return &uid_hash_table[*pos]; +} + +static void *uid_seq_next(struct seq_file *seq, void *v, loff_t *pos) +{ + (*pos)++; + + if (*pos >= HASH_SIZE(uid_hash_table)) + return NULL; + + return &uid_hash_table[*pos]; +} + +static void uid_seq_stop(struct seq_file *seq, void *v) { } + +static int uid_time_in_state_seq_show(struct seq_file *m, void *v) +{ + struct uid_entry *uid_entry; + struct cpu_freqs *freqs, *last_freqs = NULL; + int i, cpu; + + if (v == uid_hash_table) { + seq_puts(m, "uid:"); + for_each_possible_cpu(cpu) { + freqs = all_freqs[cpu]; + if (!freqs || freqs == last_freqs) + continue; + last_freqs = freqs; + for (i = 0; i < freqs->max_state; i++) { + if (freqs->freq_table[i] == + CPUFREQ_ENTRY_INVALID) + continue; + seq_printf(m, " %d", freqs->freq_table[i]); + } + } + seq_putc(m, '\n'); + } + + rcu_read_lock(); + + hlist_for_each_entry_rcu(uid_entry, (struct hlist_head *)v, hash) { + if (uid_entry->max_state) + seq_printf(m, "%d:", uid_entry->uid); + for (i = 0; i < uid_entry->max_state; ++i) { + if (freq_index_invalid(i)) + continue; + seq_printf(m, " %lu", (unsigned long)nsec_to_clock_t( + uid_entry->time_in_state[i])); + } + if (uid_entry->max_state) + seq_putc(m, '\n'); + } + + rcu_read_unlock(); + return 0; +} + void cpufreq_task_times_init(struct task_struct *p) { void *temp; @@ -87,6 +234,9 @@ void cpufreq_task_times_exit(struct task_struct *p) unsigned long flags; void *temp; + if (!p->time_in_state) + return; + spin_lock_irqsave(&task_time_in_state_lock, flags); temp = p->time_in_state; p->time_in_state = NULL; @@ -130,7 +280,9 @@ void cpufreq_acct_update_power(struct task_struct *p, u64 cputime) { unsigned long flags; unsigned int state; + struct uid_entry *uid_entry; struct cpu_freqs *freqs = all_freqs[task_cpu(p)]; + uid_t uid = from_kuid_munged(current_user_ns(), task_uid(p)); if (!freqs || p->flags & PF_EXITING) return; @@ -142,6 +294,12 @@ void cpufreq_acct_update_power(struct task_struct *p, u64 cputime) p->time_in_state) p->time_in_state[state] += cputime; spin_unlock_irqrestore(&task_time_in_state_lock, flags); + + spin_lock_irqsave(&uid_lock, flags); + uid_entry = find_or_register_uid_locked(uid); + if (uid_entry && state < uid_entry->max_state) + uid_entry->time_in_state[state] += cputime; + spin_unlock_irqrestore(&uid_lock, flags); } void cpufreq_times_create_policy(struct cpufreq_policy *policy) @@ -183,6 +341,27 @@ void cpufreq_times_create_policy(struct cpufreq_policy *policy) all_freqs[cpu] = freqs; } +void cpufreq_task_times_remove_uids(uid_t uid_start, uid_t uid_end) +{ + struct uid_entry *uid_entry; + struct hlist_node *tmp; + unsigned long flags; + + spin_lock_irqsave(&uid_lock, flags); + + for (; uid_start <= uid_end; uid_start++) { + hash_for_each_possible_safe(uid_hash_table, uid_entry, tmp, + hash, uid_start) { + if (uid_start == uid_entry->uid) { + hash_del_rcu(&uid_entry->hash); + kfree_rcu(uid_entry, rcu); + } + } + } + + spin_unlock_irqrestore(&uid_lock, flags); +} + void cpufreq_times_record_transition(struct cpufreq_freqs *freq) { int index; @@ -202,3 +381,32 @@ void cpufreq_times_record_transition(struct cpufreq_freqs *freq) cpufreq_cpu_put(policy); } + +static const struct seq_operations uid_time_in_state_seq_ops = { + .start = uid_seq_start, + .next = uid_seq_next, + .stop = uid_seq_stop, + .show = uid_time_in_state_seq_show, +}; + +static int uid_time_in_state_open(struct inode *inode, struct file *file) +{ + return seq_open(file, &uid_time_in_state_seq_ops); +} + +static const struct file_operations uid_time_in_state_fops = { + .open = uid_time_in_state_open, + .read = seq_read, + .llseek = seq_lseek, + .release = seq_release, +}; + +static int __init cpufreq_times_init(void) +{ + proc_create_data("uid_time_in_state", 0444, NULL, + &uid_time_in_state_fops, NULL); + + return 0; +} + +early_initcall(cpufreq_times_init); diff --git a/drivers/misc/uid_sys_stats.c b/drivers/misc/uid_sys_stats.c index ace629934821..fbf7db598d55 100644 --- a/drivers/misc/uid_sys_stats.c +++ b/drivers/misc/uid_sys_stats.c @@ -14,6 +14,7 @@ */ #include +#include #include #include #include @@ -419,6 +420,10 @@ static ssize_t uid_remove_write(struct file *file, kstrtol(end_uid, 10, &uid_end) != 0) { return -EINVAL; } + + /* Also remove uids from /proc/uid_time_in_state */ + cpufreq_task_times_remove_uids(uid_start, uid_end); + rt_mutex_lock(&uid_lock); for (; uid_start <= uid_end; uid_start++) { diff --git a/include/linux/cpufreq_times.h b/include/linux/cpufreq_times.h index 8cdbbc8fccec..b7ea7d343317 100644 --- a/include/linux/cpufreq_times.h +++ b/include/linux/cpufreq_times.h @@ -27,9 +27,12 @@ int proc_time_in_state_show(struct seq_file *m, struct pid_namespace *ns, void cpufreq_acct_update_power(struct task_struct *p, u64 cputime); void cpufreq_times_create_policy(struct cpufreq_policy *policy); void cpufreq_times_record_transition(struct cpufreq_freqs *freq); +void cpufreq_task_times_remove_uids(uid_t uid_start, uid_t uid_end); #else static inline void cpufreq_times_create_policy(struct cpufreq_policy *policy) {} static inline void cpufreq_times_record_transition( struct cpufreq_freqs *freq) {} +static inline void cpufreq_task_times_remove_uids(uid_t uid_start, + uid_t uid_end) {} #endif /* CONFIG_CPU_FREQ_TIMES */ #endif /* _LINUX_CPUFREQ_TIMES_H */ From 57921ca895b336297c969d542bda4021b8f84893 Mon Sep 17 00:00:00 2001 From: Connor O'Brien Date: Mon, 16 Oct 2017 10:30:24 -0700 Subject: [PATCH 0397/1103] ANDROID: proc: Add /proc/uid directory Add support for reporting per-uid information through procfs, roughly following the approach used for per-tid and per-tgid directories in fs/proc/base.c. This also entails some new tracking of which uids have been used, to avoid losing information when the last task with a given uid exits. Signed-off-by: Connor O'Brien Bug: 72339335 Test: ls /proc/uid/; compare with UIDs in /proc/uid_time_in_state Change-Id: I0908f0c04438b11ceb673d860e58441bf503d478 --- fs/proc/Kconfig | 7 + fs/proc/Makefile | 1 + fs/proc/internal.h | 9 ++ fs/proc/root.c | 1 + fs/proc/uid.c | 281 ++++++++++++++++++++++++++++++++++++++++ include/linux/proc_fs.h | 6 + kernel/user.c | 3 + 7 files changed, 308 insertions(+) create mode 100644 fs/proc/uid.c diff --git a/fs/proc/Kconfig b/fs/proc/Kconfig index 817c02b13b1d..4d96a7cc7ea8 100644 --- a/fs/proc/Kconfig +++ b/fs/proc/Kconfig @@ -97,3 +97,10 @@ config PROC_CHILDREN Say Y if you are running any user-space software which takes benefit from this interface. For example, rkt is such a piece of software. + +config PROC_UID + bool "Include /proc/uid/ files" + default y + depends on PROC_FS && RT_MUTEXES + help + Provides aggregated per-uid information under /proc/uid. diff --git a/fs/proc/Makefile b/fs/proc/Makefile index ead487e80510..3f849ca0edce 100644 --- a/fs/proc/Makefile +++ b/fs/proc/Makefile @@ -27,6 +27,7 @@ proc-y += softirqs.o proc-y += namespaces.o proc-y += self.o proc-y += thread_self.o +proc-$(CONFIG_PROC_UID) += uid.o proc-$(CONFIG_PROC_SYSCTL) += proc_sysctl.o proc-$(CONFIG_NET) += proc_net.o proc-$(CONFIG_PROC_KCORE) += kcore.o diff --git a/fs/proc/internal.h b/fs/proc/internal.h index 5185d7f6a51e..93b92902ee12 100644 --- a/fs/proc/internal.h +++ b/fs/proc/internal.h @@ -256,6 +256,15 @@ static inline void proc_sys_evict_inode(struct inode *inode, struct ctl_table_header *head) { } #endif +/* + * uid.c + */ +#ifdef CONFIG_PROC_UID +extern int proc_uid_init(void); +#else +static inline void proc_uid_init(void) { } +#endif + /* * proc_tty.c */ diff --git a/fs/proc/root.c b/fs/proc/root.c index f4b1a9d2eca6..efc63a6d5a87 100644 --- a/fs/proc/root.c +++ b/fs/proc/root.c @@ -130,6 +130,7 @@ void __init proc_root_init(void) proc_symlink("mounts", NULL, "self/mounts"); proc_net_init(); + proc_uid_init(); proc_mkdir("fs", NULL); proc_mkdir("driver", NULL); proc_create_mount_point("fs/nfsd"); /* somewhere for the nfsd filesystem to be mounted */ diff --git a/fs/proc/uid.c b/fs/proc/uid.c new file mode 100644 index 000000000000..ec4e2ae4f6f7 --- /dev/null +++ b/fs/proc/uid.c @@ -0,0 +1,281 @@ +/* + * /proc/uid support + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "internal.h" + +static struct proc_dir_entry *proc_uid; + +#define UID_HASH_BITS 10 + +static DECLARE_HASHTABLE(proc_uid_hash_table, UID_HASH_BITS); + +/* + * use rt_mutex here to avoid priority inversion between high-priority readers + * of these files and tasks calling proc_register_uid(). + */ +static DEFINE_RT_MUTEX(proc_uid_lock); /* proc_uid_hash_table */ + +struct uid_hash_entry { + uid_t uid; + struct hlist_node hash; +}; + +/* Caller must hold proc_uid_lock */ +static bool uid_hash_entry_exists_locked(uid_t uid) +{ + struct uid_hash_entry *entry; + + hash_for_each_possible(proc_uid_hash_table, entry, hash, uid) { + if (entry->uid == uid) + return true; + } + return false; +} + +void proc_register_uid(kuid_t kuid) +{ + struct uid_hash_entry *entry; + bool exists; + uid_t uid = from_kuid_munged(current_user_ns(), kuid); + + rt_mutex_lock(&proc_uid_lock); + exists = uid_hash_entry_exists_locked(uid); + rt_mutex_unlock(&proc_uid_lock); + if (exists) + return; + + entry = kzalloc(sizeof(struct uid_hash_entry), GFP_KERNEL); + if (!entry) + return; + entry->uid = uid; + + rt_mutex_lock(&proc_uid_lock); + if (uid_hash_entry_exists_locked(uid)) + kfree(entry); + else + hash_add(proc_uid_hash_table, &entry->hash, uid); + rt_mutex_unlock(&proc_uid_lock); +} + +struct uid_entry { + const char *name; + int len; + umode_t mode; + const struct inode_operations *iop; + const struct file_operations *fop; +}; + +#define NOD(NAME, MODE, IOP, FOP) { \ + .name = (NAME), \ + .len = sizeof(NAME) - 1, \ + .mode = MODE, \ + .iop = IOP, \ + .fop = FOP, \ +} + +static const struct uid_entry uid_base_stuff[] = {}; + +static const struct inode_operations proc_uid_def_inode_operations = { + .setattr = proc_setattr, +}; + +static struct inode *proc_uid_make_inode(struct super_block *sb, kuid_t kuid) +{ + struct inode *inode; + + inode = new_inode(sb); + if (!inode) + return NULL; + + inode->i_ino = get_next_ino(); + inode->i_mtime = inode->i_atime = inode->i_ctime = current_time(inode); + inode->i_op = &proc_uid_def_inode_operations; + inode->i_uid = kuid; + + return inode; +} + +static int proc_uident_instantiate(struct inode *dir, struct dentry *dentry, + struct task_struct *unused, const void *ptr) +{ + const struct uid_entry *u = ptr; + struct inode *inode; + + inode = proc_uid_make_inode(dir->i_sb, dir->i_uid); + if (!inode) + return -ENOENT; + + inode->i_mode = u->mode; + if (S_ISDIR(inode->i_mode)) + set_nlink(inode, 2); + if (u->iop) + inode->i_op = u->iop; + if (u->fop) + inode->i_fop = u->fop; + d_add(dentry, inode); + return 0; +} + +static struct dentry *proc_uid_base_lookup(struct inode *dir, + struct dentry *dentry, + unsigned int flags) +{ + const struct uid_entry *u, *last; + unsigned int nents = ARRAY_SIZE(uid_base_stuff); + + if (nents == 0) + return ERR_PTR(-ENOENT); + + last = &uid_base_stuff[nents - 1]; + for (u = uid_base_stuff; u <= last; u++) { + if (u->len != dentry->d_name.len) + continue; + if (!memcmp(dentry->d_name.name, u->name, u->len)) + break; + } + if (u > last) + return ERR_PTR(-ENOENT); + + return ERR_PTR(proc_uident_instantiate(dir, dentry, NULL, u)); +} + +static int proc_uid_base_readdir(struct file *file, struct dir_context *ctx) +{ + unsigned int nents = ARRAY_SIZE(uid_base_stuff); + const struct uid_entry *u; + + if (!dir_emit_dots(file, ctx)) + return 0; + + if (ctx->pos >= nents + 2) + return 0; + + for (u = uid_base_stuff + (ctx->pos - 2); + u <= uid_base_stuff + nents - 1; u++) { + if (!proc_fill_cache(file, ctx, u->name, u->len, + proc_uident_instantiate, NULL, u)) + break; + ctx->pos++; + } + + return 0; +} + +static const struct inode_operations proc_uid_base_inode_operations = { + .lookup = proc_uid_base_lookup, + .setattr = proc_setattr, +}; + +static const struct file_operations proc_uid_base_operations = { + .read = generic_read_dir, + .iterate = proc_uid_base_readdir, + .llseek = default_llseek, +}; + +static int proc_uid_instantiate(struct inode *dir, struct dentry *dentry, + struct task_struct *unused, const void *ptr) +{ + unsigned int i, len; + nlink_t nlinks; + kuid_t *kuid = (kuid_t *)ptr; + struct inode *inode = proc_uid_make_inode(dir->i_sb, *kuid); + + if (!inode) + return -ENOENT; + + inode->i_mode = S_IFDIR | 0555; + inode->i_op = &proc_uid_base_inode_operations; + inode->i_fop = &proc_uid_base_operations; + inode->i_flags |= S_IMMUTABLE; + + nlinks = 2; + len = ARRAY_SIZE(uid_base_stuff); + for (i = 0; i < len; ++i) { + if (S_ISDIR(uid_base_stuff[i].mode)) + ++nlinks; + } + set_nlink(inode, nlinks); + + d_add(dentry, inode); + + return 0; +} + +static int proc_uid_readdir(struct file *file, struct dir_context *ctx) +{ + int last_shown, i; + unsigned long bkt; + struct uid_hash_entry *entry; + + if (!dir_emit_dots(file, ctx)) + return 0; + + i = 0; + last_shown = ctx->pos - 2; + rt_mutex_lock(&proc_uid_lock); + hash_for_each(proc_uid_hash_table, bkt, entry, hash) { + int len; + char buf[PROC_NUMBUF]; + + if (i < last_shown) + continue; + len = snprintf(buf, sizeof(buf), "%u", entry->uid); + if (!proc_fill_cache(file, ctx, buf, len, + proc_uid_instantiate, NULL, &entry->uid)) + break; + i++; + ctx->pos++; + } + rt_mutex_unlock(&proc_uid_lock); + return 0; +} + +static struct dentry *proc_uid_lookup(struct inode *dir, struct dentry *dentry, + unsigned int flags) +{ + int result = -ENOENT; + + uid_t uid = name_to_int(&dentry->d_name); + bool uid_exists; + + rt_mutex_lock(&proc_uid_lock); + uid_exists = uid_hash_entry_exists_locked(uid); + rt_mutex_unlock(&proc_uid_lock); + if (uid_exists) { + kuid_t kuid = make_kuid(current_user_ns(), uid); + + result = proc_uid_instantiate(dir, dentry, NULL, &kuid); + } + return ERR_PTR(result); +} + +static const struct file_operations proc_uid_operations = { + .read = generic_read_dir, + .iterate = proc_uid_readdir, + .llseek = default_llseek, +}; + +static const struct inode_operations proc_uid_inode_operations = { + .lookup = proc_uid_lookup, + .setattr = proc_setattr, +}; + +int __init proc_uid_init(void) +{ + proc_uid = proc_mkdir("uid", NULL); + if (!proc_uid) + return -ENOMEM; + proc_uid->proc_iops = &proc_uid_inode_operations; + proc_uid->proc_fops = &proc_uid_operations; + + return 0; +} diff --git a/include/linux/proc_fs.h b/include/linux/proc_fs.h index d0e1f1522a78..a50b1c981749 100644 --- a/include/linux/proc_fs.h +++ b/include/linux/proc_fs.h @@ -116,6 +116,12 @@ static inline int remove_proc_subtree(const char *name, struct proc_dir_entry *p #endif /* CONFIG_PROC_FS */ +#ifdef CONFIG_PROC_UID +extern void proc_register_uid(kuid_t uid); +#else +static inline void proc_register_uid(kuid_t uid) {} +#endif + struct net; static inline struct proc_dir_entry *proc_net_mkdir( diff --git a/kernel/user.c b/kernel/user.c index 0df9b1640b2a..7f74a8a6fb30 100644 --- a/kernel/user.c +++ b/kernel/user.c @@ -17,6 +17,7 @@ #include #include #include +#include #include /* @@ -208,6 +209,7 @@ struct user_struct *alloc_uid(kuid_t uid) } spin_unlock_irq(&uidhash_lock); } + proc_register_uid(uid); return up; @@ -229,6 +231,7 @@ static int __init uid_cache_init(void) spin_lock_irq(&uidhash_lock); uid_hash_insert(&root_user, uidhashentry(GLOBAL_ROOT_UID)); spin_unlock_irq(&uidhash_lock); + proc_register_uid(GLOBAL_ROOT_UID); return 0; } From d1c027cd6e5a47737db107036f34176334199e09 Mon Sep 17 00:00:00 2001 From: Connor O'Brien Date: Mon, 22 Jan 2018 18:28:08 -0800 Subject: [PATCH 0398/1103] ANDROID: cpufreq: Add time_in_state to /proc/uid directories Add per-uid files that report the data in binary format rather than text, to allow faster reading & parsing by userspace. Signed-off-by: Connor O'Brien Bug: 72339335 Test: compare values to those reported in /proc/uid_time_in_state Change-Id: I463039ea7f17b842be4c70024fe772539fe2ce02 --- drivers/cpufreq/cpufreq_times.c | 49 +++++++++++++++++++++++++++++++++ fs/proc/uid.c | 16 ++++++++++- include/linux/cpufreq_times.h | 1 + 3 files changed, 65 insertions(+), 1 deletion(-) diff --git a/drivers/cpufreq/cpufreq_times.c b/drivers/cpufreq/cpufreq_times.c index 5e19ac5c8157..f560e10ba183 100644 --- a/drivers/cpufreq/cpufreq_times.c +++ b/drivers/cpufreq/cpufreq_times.c @@ -58,6 +58,19 @@ static struct cpu_freqs *all_freqs[NR_CPUS]; static unsigned int next_offset; + +/* Caller must hold rcu_read_lock() */ +static struct uid_entry *find_uid_entry_rcu(uid_t uid) +{ + struct uid_entry *uid_entry; + + hash_for_each_possible_rcu(uid_hash_table, uid_entry, hash, uid) { + if (uid_entry->uid == uid) + return uid_entry; + } + return NULL; +} + /* Caller must hold uid lock */ static struct uid_entry *find_uid_entry_locked(uid_t uid) { @@ -127,6 +140,36 @@ static bool freq_index_invalid(unsigned int index) return true; } +static int single_uid_time_in_state_show(struct seq_file *m, void *ptr) +{ + struct uid_entry *uid_entry; + unsigned int i; + u64 time; + uid_t uid = from_kuid_munged(current_user_ns(), *(kuid_t *)m->private); + + if (uid == overflowuid) + return -EINVAL; + + rcu_read_lock(); + + uid_entry = find_uid_entry_rcu(uid); + if (!uid_entry) { + rcu_read_unlock(); + return 0; + } + + for (i = 0; i < uid_entry->max_state; ++i) { + if (freq_index_invalid(i)) + continue; + time = nsec_to_clock_t(uid_entry->time_in_state[i]); + seq_write(m, &time, sizeof(time)); + } + + rcu_read_unlock(); + + return 0; +} + static void *uid_seq_start(struct seq_file *seq, loff_t *pos) { if (*pos >= HASH_SIZE(uid_hash_table)) @@ -394,6 +437,12 @@ static int uid_time_in_state_open(struct inode *inode, struct file *file) return seq_open(file, &uid_time_in_state_seq_ops); } +int single_uid_time_in_state_open(struct inode *inode, struct file *file) +{ + return single_open(file, single_uid_time_in_state_show, + &(inode->i_uid)); +} + static const struct file_operations uid_time_in_state_fops = { .open = uid_time_in_state_open, .read = seq_read, diff --git a/fs/proc/uid.c b/fs/proc/uid.c index ec4e2ae4f6f7..9e15be510d71 100644 --- a/fs/proc/uid.c +++ b/fs/proc/uid.c @@ -2,6 +2,7 @@ * /proc/uid support */ +#include #include #include #include @@ -82,7 +83,20 @@ struct uid_entry { .fop = FOP, \ } -static const struct uid_entry uid_base_stuff[] = {}; +#ifdef CONFIG_CPU_FREQ_TIMES +static const struct file_operations proc_uid_time_in_state_operations = { + .open = single_uid_time_in_state_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; +#endif + +static const struct uid_entry uid_base_stuff[] = { +#ifdef CONFIG_CPU_FREQ_TIMES + NOD("time_in_state", 0444, NULL, &proc_uid_time_in_state_operations), +#endif +}; static const struct inode_operations proc_uid_def_inode_operations = { .setattr = proc_setattr, diff --git a/include/linux/cpufreq_times.h b/include/linux/cpufreq_times.h index b7ea7d343317..e84b576a20d5 100644 --- a/include/linux/cpufreq_times.h +++ b/include/linux/cpufreq_times.h @@ -28,6 +28,7 @@ void cpufreq_acct_update_power(struct task_struct *p, u64 cputime); void cpufreq_times_create_policy(struct cpufreq_policy *policy); void cpufreq_times_record_transition(struct cpufreq_freqs *freq); void cpufreq_task_times_remove_uids(uid_t uid_start, uid_t uid_end); +int single_uid_time_in_state_open(struct inode *inode, struct file *file); #else static inline void cpufreq_times_create_policy(struct cpufreq_policy *policy) {} static inline void cpufreq_times_record_transition( From de0cfa5cb3d7e8cd3c651d0fdafdcd573dd9d4c7 Mon Sep 17 00:00:00 2001 From: Alistair Strachan Date: Thu, 5 Apr 2018 18:01:04 -0700 Subject: [PATCH 0399/1103] ANDROID: Add defconfig for cuttlefish. This file is based on x86_64_defconfig, merged with the base and recommended configs from configs.git, with the virtio drivers enabled and some spurious kernel features turned off. Change-Id: I61bde941e8cfef2dd83cb4ff040f7380922cc44e Signed-off-by: Alistair Strachan --- arch/x86/configs/x86_64_cuttlefish_defconfig | 444 +++++++++++++++++++ 1 file changed, 444 insertions(+) create mode 100644 arch/x86/configs/x86_64_cuttlefish_defconfig diff --git a/arch/x86/configs/x86_64_cuttlefish_defconfig b/arch/x86/configs/x86_64_cuttlefish_defconfig new file mode 100644 index 000000000000..1b9020912aae --- /dev/null +++ b/arch/x86/configs/x86_64_cuttlefish_defconfig @@ -0,0 +1,444 @@ +CONFIG_POSIX_MQUEUE=y +# CONFIG_FHANDLE is not set +# CONFIG_USELIB is not set +CONFIG_AUDIT=y +CONFIG_NO_HZ=y +CONFIG_HIGH_RES_TIMERS=y +CONFIG_BSD_PROCESS_ACCT=y +CONFIG_TASKSTATS=y +CONFIG_TASK_DELAY_ACCT=y +CONFIG_TASK_XACCT=y +CONFIG_TASK_IO_ACCOUNTING=y +CONFIG_IKCONFIG=y +CONFIG_IKCONFIG_PROC=y +CONFIG_CGROUPS=y +CONFIG_MEMCG=y +CONFIG_MEMCG_SWAP=y +CONFIG_CGROUP_SCHED=y +CONFIG_RT_GROUP_SCHED=y +CONFIG_CGROUP_FREEZER=y +CONFIG_CGROUP_CPUACCT=y +CONFIG_CGROUP_BPF=y +CONFIG_NAMESPACES=y +CONFIG_BLK_DEV_INITRD=y +# CONFIG_RD_LZ4 is not set +CONFIG_KALLSYMS_ALL=y +# CONFIG_PCSPKR_PLATFORM is not set +CONFIG_BPF_SYSCALL=y +CONFIG_EMBEDDED=y +# CONFIG_COMPAT_BRK is not set +CONFIG_PROFILING=y +CONFIG_OPROFILE=y +CONFIG_KPROBES=y +CONFIG_JUMP_LABEL=y +CONFIG_CC_STACKPROTECTOR_STRONG=y +CONFIG_MODULES=y +CONFIG_MODULE_UNLOAD=y +CONFIG_MODVERSIONS=y +CONFIG_PARTITION_ADVANCED=y +CONFIG_SMP=y +CONFIG_HYPERVISOR_GUEST=y +CONFIG_PARAVIRT=y +CONFIG_PARAVIRT_SPINLOCKS=y +CONFIG_MCORE2=y +CONFIG_PROCESSOR_SELECT=y +# CONFIG_CPU_SUP_CENTAUR is not set +CONFIG_NR_CPUS=8 +CONFIG_PREEMPT=y +# CONFIG_MICROCODE is not set +CONFIG_X86_MSR=y +CONFIG_X86_CPUID=y +CONFIG_KSM=y +CONFIG_DEFAULT_MMAP_MIN_ADDR=65536 +CONFIG_TRANSPARENT_HUGEPAGE=y +# CONFIG_MTRR is not set +CONFIG_HZ_100=y +CONFIG_KEXEC=y +CONFIG_CRASH_DUMP=y +CONFIG_PHYSICAL_START=0x200000 +CONFIG_PHYSICAL_ALIGN=0x1000000 +CONFIG_CMDLINE_BOOL=y +CONFIG_CMDLINE="console=ttyS0 reboot=p" +CONFIG_PM_WAKELOCKS=y +CONFIG_PM_WAKELOCKS_LIMIT=0 +# CONFIG_PM_WAKELOCKS_GC is not set +CONFIG_PM_DEBUG=y +CONFIG_ACPI_PROCFS_POWER=y +# CONFIG_ACPI_FAN is not set +# CONFIG_ACPI_THERMAL is not set +# CONFIG_X86_PM_TIMER is not set +CONFIG_CPU_FREQ_GOV_ONDEMAND=y +CONFIG_X86_ACPI_CPUFREQ=y +CONFIG_PCI_MMCONFIG=y +CONFIG_PCI_MSI=y +# CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set +CONFIG_BINFMT_MISC=y +CONFIG_IA32_EMULATION=y +CONFIG_NET=y +CONFIG_PACKET=y +CONFIG_UNIX=y +CONFIG_XFRM_USER=y +CONFIG_NET_KEY=y +CONFIG_INET=y +CONFIG_IP_MULTICAST=y +CONFIG_IP_ADVANCED_ROUTER=y +CONFIG_IP_MULTIPLE_TABLES=y +CONFIG_IP_ROUTE_MULTIPATH=y +CONFIG_IP_ROUTE_VERBOSE=y +CONFIG_IP_MROUTE=y +CONFIG_IP_PIMSM_V1=y +CONFIG_IP_PIMSM_V2=y +CONFIG_SYN_COOKIES=y +CONFIG_INET_ESP=y +# CONFIG_INET_XFRM_MODE_TRANSPORT is not set +# CONFIG_INET_XFRM_MODE_BEET is not set +CONFIG_INET_DIAG_DESTROY=y +CONFIG_TCP_CONG_ADVANCED=y +# CONFIG_TCP_CONG_BIC is not set +# CONFIG_TCP_CONG_WESTWOOD is not set +# CONFIG_TCP_CONG_HTCP is not set +CONFIG_TCP_MD5SIG=y +CONFIG_IPV6_ROUTER_PREF=y +CONFIG_IPV6_ROUTE_INFO=y +CONFIG_IPV6_OPTIMISTIC_DAD=y +CONFIG_INET6_AH=y +CONFIG_INET6_ESP=y +CONFIG_INET6_IPCOMP=y +CONFIG_IPV6_MIP6=y +CONFIG_IPV6_MULTIPLE_TABLES=y +CONFIG_NETLABEL=y +CONFIG_NETFILTER=y +CONFIG_NF_CONNTRACK=y +CONFIG_NF_CONNTRACK_SECMARK=y +CONFIG_NF_CONNTRACK_EVENTS=y +CONFIG_NF_CONNTRACK_AMANDA=y +CONFIG_NF_CONNTRACK_FTP=y +CONFIG_NF_CONNTRACK_H323=y +CONFIG_NF_CONNTRACK_IRC=y +CONFIG_NF_CONNTRACK_NETBIOS_NS=y +CONFIG_NF_CONNTRACK_PPTP=y +CONFIG_NF_CONNTRACK_SANE=y +CONFIG_NF_CONNTRACK_TFTP=y +CONFIG_NF_CT_NETLINK=y +CONFIG_NETFILTER_XT_TARGET_CLASSIFY=y +CONFIG_NETFILTER_XT_TARGET_CONNMARK=y +CONFIG_NETFILTER_XT_TARGET_CONNSECMARK=y +CONFIG_NETFILTER_XT_TARGET_IDLETIMER=y +CONFIG_NETFILTER_XT_TARGET_MARK=y +CONFIG_NETFILTER_XT_TARGET_NFLOG=y +CONFIG_NETFILTER_XT_TARGET_NFQUEUE=y +CONFIG_NETFILTER_XT_TARGET_TPROXY=y +CONFIG_NETFILTER_XT_TARGET_TRACE=y +CONFIG_NETFILTER_XT_TARGET_SECMARK=y +CONFIG_NETFILTER_XT_TARGET_TCPMSS=y +CONFIG_NETFILTER_XT_MATCH_COMMENT=y +CONFIG_NETFILTER_XT_MATCH_CONNLIMIT=y +CONFIG_NETFILTER_XT_MATCH_CONNMARK=y +CONFIG_NETFILTER_XT_MATCH_CONNTRACK=y +CONFIG_NETFILTER_XT_MATCH_HASHLIMIT=y +CONFIG_NETFILTER_XT_MATCH_HELPER=y +CONFIG_NETFILTER_XT_MATCH_IPRANGE=y +CONFIG_NETFILTER_XT_MATCH_LENGTH=y +CONFIG_NETFILTER_XT_MATCH_LIMIT=y +CONFIG_NETFILTER_XT_MATCH_MAC=y +CONFIG_NETFILTER_XT_MATCH_MARK=y +CONFIG_NETFILTER_XT_MATCH_POLICY=y +CONFIG_NETFILTER_XT_MATCH_PKTTYPE=y +CONFIG_NETFILTER_XT_MATCH_QUOTA=y +CONFIG_NETFILTER_XT_MATCH_QUOTA2=y +CONFIG_NETFILTER_XT_MATCH_STATE=y +CONFIG_NETFILTER_XT_MATCH_STATISTIC=y +CONFIG_NETFILTER_XT_MATCH_STRING=y +CONFIG_NETFILTER_XT_MATCH_TIME=y +CONFIG_NETFILTER_XT_MATCH_U32=y +CONFIG_NF_CONNTRACK_IPV4=y +CONFIG_IP_NF_IPTABLES=y +CONFIG_IP_NF_MATCH_AH=y +CONFIG_IP_NF_MATCH_ECN=y +CONFIG_IP_NF_MATCH_TTL=y +CONFIG_IP_NF_FILTER=y +CONFIG_IP_NF_TARGET_REJECT=y +CONFIG_IP_NF_NAT=y +CONFIG_IP_NF_TARGET_MASQUERADE=y +CONFIG_IP_NF_TARGET_NETMAP=y +CONFIG_IP_NF_TARGET_REDIRECT=y +CONFIG_IP_NF_MANGLE=y +CONFIG_IP_NF_RAW=y +CONFIG_IP_NF_SECURITY=y +CONFIG_IP_NF_ARPTABLES=y +CONFIG_IP_NF_ARPFILTER=y +CONFIG_IP_NF_ARP_MANGLE=y +CONFIG_NF_CONNTRACK_IPV6=y +CONFIG_IP6_NF_IPTABLES=y +CONFIG_IP6_NF_MATCH_IPV6HEADER=y +CONFIG_IP6_NF_MATCH_RPFILTER=y +CONFIG_IP6_NF_FILTER=y +CONFIG_IP6_NF_TARGET_REJECT=y +CONFIG_IP6_NF_MANGLE=y +CONFIG_IP6_NF_RAW=y +CONFIG_NET_SCHED=y +CONFIG_NET_SCH_HTB=y +CONFIG_NET_CLS_U32=y +CONFIG_NET_EMATCH=y +CONFIG_NET_EMATCH_U32=y +CONFIG_NET_CLS_ACT=y +CONFIG_CFG80211=y +CONFIG_MAC80211=y +CONFIG_RFKILL=y +CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug" +CONFIG_DEVTMPFS=y +CONFIG_DEBUG_DEVRES=y +CONFIG_OF=y +CONFIG_OF_UNITTEST=y +# CONFIG_PNP_DEBUG_MESSAGES is not set +CONFIG_BLK_DEV_LOOP=y +CONFIG_BLK_DEV_RAM=y +CONFIG_BLK_DEV_RAM_SIZE=8192 +CONFIG_VIRTIO_BLK=y +CONFIG_UID_SYS_STATS=y +CONFIG_MEMORY_STATE_TIME=y +CONFIG_SCSI=y +CONFIG_BLK_DEV_SD=y +CONFIG_BLK_DEV_SR=y +CONFIG_BLK_DEV_SR_VENDOR=y +CONFIG_CHR_DEV_SG=y +CONFIG_SCSI_CONSTANTS=y +CONFIG_SCSI_SPI_ATTRS=y +CONFIG_SCSI_VIRTIO=y +CONFIG_MD=y +CONFIG_BLK_DEV_DM=y +CONFIG_DM_CRYPT=y +CONFIG_DM_MIRROR=y +CONFIG_DM_ZERO=y +CONFIG_DM_UEVENT=y +CONFIG_DM_VERITY=y +CONFIG_DM_VERITY_FEC=y +CONFIG_NETDEVICES=y +CONFIG_NETCONSOLE=y +CONFIG_NETCONSOLE_DYNAMIC=y +CONFIG_TUN=y +CONFIG_VIRTIO_NET=y +# CONFIG_ETHERNET is not set +CONFIG_PPP=y +CONFIG_PPP_BSDCOMP=y +CONFIG_PPP_DEFLATE=y +CONFIG_PPP_MPPE=y +CONFIG_USB_USBNET=y +# CONFIG_USB_NET_AX8817X is not set +# CONFIG_USB_NET_AX88179_178A is not set +# CONFIG_USB_NET_CDCETHER is not set +# CONFIG_USB_NET_CDC_NCM is not set +# CONFIG_USB_NET_NET1080 is not set +# CONFIG_USB_NET_CDC_SUBSET is not set +# CONFIG_USB_NET_ZAURUS is not set +# CONFIG_WLAN_VENDOR_ADMTEK is not set +# CONFIG_WLAN_VENDOR_ATH is not set +# CONFIG_WLAN_VENDOR_ATMEL is not set +# CONFIG_WLAN_VENDOR_BROADCOM is not set +# CONFIG_WLAN_VENDOR_CISCO is not set +# CONFIG_WLAN_VENDOR_INTEL is not set +# CONFIG_WLAN_VENDOR_INTERSIL is not set +# CONFIG_WLAN_VENDOR_MARVELL is not set +# CONFIG_WLAN_VENDOR_MEDIATEK is not set +# CONFIG_WLAN_VENDOR_RALINK is not set +# CONFIG_WLAN_VENDOR_REALTEK is not set +# CONFIG_WLAN_VENDOR_RSI is not set +# CONFIG_WLAN_VENDOR_ST is not set +# CONFIG_WLAN_VENDOR_TI is not set +# CONFIG_WLAN_VENDOR_ZYDAS is not set +# CONFIG_WLAN_VENDOR_QUANTENNA is not set +CONFIG_MAC80211_HWSIM=y +CONFIG_INPUT_EVDEV=y +CONFIG_INPUT_KEYRESET=y +# CONFIG_INPUT_KEYBOARD is not set +# CONFIG_INPUT_MOUSE is not set +CONFIG_INPUT_JOYSTICK=y +CONFIG_JOYSTICK_XPAD=y +CONFIG_JOYSTICK_XPAD_FF=y +CONFIG_JOYSTICK_XPAD_LEDS=y +CONFIG_INPUT_TABLET=y +CONFIG_TABLET_USB_ACECAD=y +CONFIG_TABLET_USB_AIPTEK=y +CONFIG_TABLET_USB_GTCO=y +CONFIG_TABLET_USB_HANWANG=y +CONFIG_TABLET_USB_KBTAB=y +CONFIG_INPUT_MISC=y +CONFIG_INPUT_KEYCHORD=y +CONFIG_INPUT_UINPUT=y +CONFIG_INPUT_GPIO=y +# CONFIG_SERIO_I8042 is not set +# CONFIG_VT is not set +# CONFIG_LEGACY_PTYS is not set +# CONFIG_DEVMEM is not set +CONFIG_SERIAL_8250=y +# CONFIG_SERIAL_8250_DEPRECATED_OPTIONS is not set +CONFIG_SERIAL_8250_CONSOLE=y +# CONFIG_SERIAL_8250_EXAR is not set +CONFIG_SERIAL_8250_NR_UARTS=48 +CONFIG_SERIAL_8250_EXTENDED=y +CONFIG_SERIAL_8250_MANY_PORTS=y +CONFIG_SERIAL_8250_SHARE_IRQ=y +CONFIG_VIRTIO_CONSOLE=y +CONFIG_HW_RANDOM=y +# CONFIG_HW_RANDOM_INTEL is not set +# CONFIG_HW_RANDOM_AMD is not set +# CONFIG_HW_RANDOM_VIA is not set +CONFIG_HW_RANDOM_VIRTIO=y +CONFIG_HPET=y +# CONFIG_HPET_MMAP_DEFAULT is not set +# CONFIG_DEVPORT is not set +# CONFIG_ACPI_I2C_OPREGION is not set +# CONFIG_I2C_COMPAT is not set +# CONFIG_I2C_HELPER_AUTO is not set +CONFIG_PTP_1588_CLOCK=y +# CONFIG_HWMON is not set +# CONFIG_X86_PKG_TEMP_THERMAL is not set +CONFIG_WATCHDOG=y +CONFIG_SOFT_WATCHDOG=y +CONFIG_MEDIA_SUPPORT=y +# CONFIG_VGA_ARB is not set +CONFIG_DRM=y +# CONFIG_DRM_FBDEV_EMULATION is not set +CONFIG_DRM_VIRTIO_GPU=y +CONFIG_SOUND=y +CONFIG_SND=y +CONFIG_HIDRAW=y +CONFIG_UHID=y +# CONFIG_HID_GENERIC is not set +CONFIG_HID_A4TECH=y +CONFIG_HID_ACRUX=y +CONFIG_HID_ACRUX_FF=y +CONFIG_HID_APPLE=y +CONFIG_HID_BELKIN=y +CONFIG_HID_CHERRY=y +CONFIG_HID_CHICONY=y +CONFIG_HID_PRODIKEYS=y +CONFIG_HID_CYPRESS=y +CONFIG_HID_DRAGONRISE=y +CONFIG_DRAGONRISE_FF=y +CONFIG_HID_EMS_FF=y +CONFIG_HID_ELECOM=y +CONFIG_HID_EZKEY=y +CONFIG_HID_HOLTEK=y +CONFIG_HID_KEYTOUCH=y +CONFIG_HID_KYE=y +CONFIG_HID_UCLOGIC=y +CONFIG_HID_WALTOP=y +CONFIG_HID_GYRATION=y +CONFIG_HID_TWINHAN=y +CONFIG_HID_KENSINGTON=y +CONFIG_HID_LCPOWER=y +CONFIG_HID_LOGITECH=y +CONFIG_HID_LOGITECH_DJ=y +CONFIG_LOGITECH_FF=y +CONFIG_LOGIRUMBLEPAD2_FF=y +CONFIG_LOGIG940_FF=y +CONFIG_HID_MAGICMOUSE=y +CONFIG_HID_MICROSOFT=y +CONFIG_HID_MONTEREY=y +CONFIG_HID_MULTITOUCH=y +CONFIG_HID_NTRIG=y +CONFIG_HID_ORTEK=y +CONFIG_HID_PANTHERLORD=y +CONFIG_PANTHERLORD_FF=y +CONFIG_HID_PETALYNX=y +CONFIG_HID_PICOLCD=y +CONFIG_HID_PRIMAX=y +CONFIG_HID_ROCCAT=y +CONFIG_HID_SAITEK=y +CONFIG_HID_SAMSUNG=y +CONFIG_HID_SONY=y +CONFIG_HID_SPEEDLINK=y +CONFIG_HID_SUNPLUS=y +CONFIG_HID_GREENASIA=y +CONFIG_GREENASIA_FF=y +CONFIG_HID_SMARTJOYPLUS=y +CONFIG_SMARTJOYPLUS_FF=y +CONFIG_HID_TIVO=y +CONFIG_HID_TOPSEED=y +CONFIG_HID_THRUSTMASTER=y +CONFIG_HID_WACOM=y +CONFIG_HID_WIIMOTE=y +CONFIG_HID_ZEROPLUS=y +CONFIG_HID_ZYDACRON=y +CONFIG_USB_HIDDEV=y +CONFIG_USB_ANNOUNCE_NEW_DEVICES=y +CONFIG_USB_EHCI_HCD=y +CONFIG_USB_GADGET=y +CONFIG_USB_DUMMY_HCD=y +CONFIG_USB_CONFIGFS=y +CONFIG_USB_CONFIGFS_F_FS=y +CONFIG_USB_CONFIGFS_F_ACC=y +CONFIG_USB_CONFIGFS_F_AUDIO_SRC=y +CONFIG_USB_CONFIGFS_UEVENT=y +CONFIG_USB_CONFIGFS_F_MIDI=y +CONFIG_RTC_CLASS=y +# CONFIG_RTC_HCTOSYS is not set +CONFIG_SW_SYNC=y +CONFIG_VIRTIO_PCI=y +CONFIG_VIRTIO_BALLOON=y +CONFIG_VIRTIO_MMIO=y +CONFIG_VIRTIO_MMIO_CMDLINE_DEVICES=y +CONFIG_STAGING=y +CONFIG_ASHMEM=y +CONFIG_ANDROID_VSOC=y +CONFIG_ION=y +# CONFIG_X86_PLATFORM_DEVICES is not set +# CONFIG_IOMMU_SUPPORT is not set +CONFIG_ANDROID=y +CONFIG_ANDROID_BINDER_IPC=y +# CONFIG_FIRMWARE_MEMMAP is not set +CONFIG_EXT4_FS=y +CONFIG_EXT4_FS_POSIX_ACL=y +CONFIG_EXT4_FS_SECURITY=y +CONFIG_EXT4_ENCRYPTION=y +CONFIG_QUOTA=y +CONFIG_QUOTA_NETLINK_INTERFACE=y +# CONFIG_PRINT_QUOTA_WARNING is not set +CONFIG_QFMT_V2=y +CONFIG_AUTOFS4_FS=y +CONFIG_MSDOS_FS=y +CONFIG_VFAT_FS=y +CONFIG_PROC_KCORE=y +CONFIG_TMPFS=y +CONFIG_TMPFS_POSIX_ACL=y +CONFIG_HUGETLBFS=y +CONFIG_SDCARD_FS=y +CONFIG_PSTORE=y +CONFIG_PSTORE_CONSOLE=y +CONFIG_PSTORE_RAM=y +CONFIG_NLS_DEFAULT="utf8" +CONFIG_NLS_CODEPAGE_437=y +CONFIG_NLS_ASCII=y +CONFIG_NLS_ISO8859_1=y +CONFIG_NLS_UTF8=y +CONFIG_PRINTK_TIME=y +CONFIG_DEBUG_INFO=y +# CONFIG_ENABLE_WARN_DEPRECATED is not set +# CONFIG_ENABLE_MUST_CHECK is not set +CONFIG_FRAME_WARN=1024 +# CONFIG_UNUSED_SYMBOLS is not set +CONFIG_MAGIC_SYSRQ=y +CONFIG_DEBUG_STACK_USAGE=y +CONFIG_DEBUG_MEMORY_INIT=y +CONFIG_DEBUG_STACKOVERFLOW=y +CONFIG_HARDLOCKUP_DETECTOR=y +CONFIG_PANIC_TIMEOUT=5 +# CONFIG_SCHED_DEBUG is not set +CONFIG_SCHEDSTATS=y +CONFIG_RCU_CPU_STALL_TIMEOUT=60 +CONFIG_ENABLE_DEFAULT_TRACERS=y +# CONFIG_KPROBE_EVENTS is not set +# CONFIG_UPROBE_EVENTS is not set +CONFIG_IO_DELAY_NONE=y +CONFIG_DEBUG_BOOT_PARAMS=y +CONFIG_OPTIMIZE_INLINING=y +CONFIG_SECURITY_PERF_EVENTS_RESTRICT=y +CONFIG_SECURITY=y +CONFIG_SECURITY_NETWORK=y +CONFIG_SECURITY_PATH=y +CONFIG_HARDENED_USERCOPY=y +CONFIG_SECURITY_SELINUX=y +CONFIG_SECURITY_SELINUX_CHECKREQPROT_VALUE=1 +# CONFIG_CRYPTO_MANAGER_DISABLE_TESTS is not set +CONFIG_CRYPTO_DEV_VIRTIO=y From 3b976639c4e5d151f36da1b9ac483b23b521c359 Mon Sep 17 00:00:00 2001 From: Alistair Strachan Date: Fri, 13 Apr 2018 15:36:37 -0700 Subject: [PATCH 0400/1103] ANDROID: Add build server config for cuttlefish. The build server config can be used with gcc or clang. Specify CC=clang to build with clang. Change-Id: Id346ab1489ecaaef8e9e66b084cc416dd0581f69 Signed-off-by: Alistair Strachan --- build.config.cuttlefish.x86_64 | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 build.config.cuttlefish.x86_64 diff --git a/build.config.cuttlefish.x86_64 b/build.config.cuttlefish.x86_64 new file mode 100644 index 000000000000..36c8a933f843 --- /dev/null +++ b/build.config.cuttlefish.x86_64 @@ -0,0 +1,15 @@ +ARCH=x86_64 +BRANCH=android-4.14 +CLANG_TRIPLE=x86_64-linux-gnu- +CROSS_COMPILE=x86_64-linux-androidkernel- +DEFCONFIG=x86_64_cuttlefish_defconfig +EXTRA_CMDS='' +KERNEL_DIR=common +POST_DEFCONFIG_CMDS="check_defconfig" +CLANG_PREBUILT_BIN=prebuilts/clang/host/linux-x86/clang-4630689/bin +LINUX_GCC_CROSS_COMPILE_PREBUILTS_BIN=prebuilts/gcc/linux-x86/x86/x86_64-linux-android-4.9/bin +FILES=" +arch/x86/boot/bzImage +vmlinux +System.map +" From 765d61f77e89b2b0fb1ff17c28c86fb33db21fc8 Mon Sep 17 00:00:00 2001 From: Sami Tolvanen Date: Mon, 13 Nov 2017 08:19:17 -0800 Subject: [PATCH 0401/1103] FROMLIST: arm64: kvm: use -fno-jump-tables with clang Starting with LLVM r308050, clang generates a jump table with EL1 virtual addresses in __init_stage2_translation, which results in a kernel panic when booting at EL2: Kernel panic - not syncing: HYP panic: PS:800003c9 PC:ffff0000089e6fd8 ESR:86000004 FAR:ffff0000089e6fd8 HPFAR:0000000009825000 PAR:0000000000000000 VCPU:000804fc20001221 CPU: 0 PID: 0 Comm: swapper/0 Not tainted 4.14.0-rc7-dirty #3 Hardware name: ARM Juno development board (r1) (DT) Call trace: [] dump_backtrace+0x0/0x34c [] show_stack+0x18/0x20 [] dump_stack+0xc4/0xfc [] panic+0x138/0x2b4 [] panic+0x0/0x2b4 SMP: stopping secondary CPUs SMP: failed to stop secondary CPUs 0-3,5 Kernel Offset: disabled CPU features: 0x002086 Memory Limit: none ---[ end Kernel panic - not syncing: HYP panic: PS:800003c9 PC:ffff0000089e6fd8 ESR:86000004 FAR:ffff0000089e6fd8 HPFAR:0000000009825000 PAR:0000000000000000 VCPU:000804fc20001221 This change adds -fno-jump-tables to arm64/hyp to work around the bug. Bug: 62093296 Bug: 67506682 Change-Id: I1257be1febdcbfcc886fe6183c698b7a98d2a153 (am from https://patchwork.kernel.org/patch/10060301/) Suggested-by: AKASHI Takahiro Signed-off-by: Sami Tolvanen --- arch/arm64/kvm/hyp/Makefile | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/arch/arm64/kvm/hyp/Makefile b/arch/arm64/kvm/hyp/Makefile index 2fabc2dc1966..78e8a37fba9a 100644 --- a/arch/arm64/kvm/hyp/Makefile +++ b/arch/arm64/kvm/hyp/Makefile @@ -6,6 +6,10 @@ ccflags-y += -fno-stack-protector -DDISABLE_BRANCH_PROFILING \ $(DISABLE_STACKLEAK_PLUGIN) +ifeq ($(cc-name),clang) +ccflags-y += -fno-jump-tables +endif + KVM=../../../../virt/kvm obj-$(CONFIG_KVM_ARM_HOST) += $(KVM)/arm/hyp/vgic-v3-sr.o From 64a008e2954e40bcfa7399a6f06a11df6c551664 Mon Sep 17 00:00:00 2001 From: Daniel Rosenberg Date: Wed, 11 Apr 2018 16:19:10 -0700 Subject: [PATCH 0402/1103] ANDROID: sdcardfs: Check for private data earlier When an sdcardfs dentry is destroyed, it may not yet have its fsdata initialized. It must be checked before we try to access the paths in its private data. Additionally, when cleaning up the superblock after a failure, we don't have our sb private data, so check for that case. Bug: 77923821 Change-Id: I89caf6e121ed86480b42024664453fe0031bbcf3 Signed-off-by: Daniel Rosenberg --- fs/sdcardfs/dentry.c | 2 ++ fs/sdcardfs/lookup.c | 2 -- fs/sdcardfs/main.c | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/fs/sdcardfs/dentry.c b/fs/sdcardfs/dentry.c index e9426a61d04a..166f14b2400b 100644 --- a/fs/sdcardfs/dentry.c +++ b/fs/sdcardfs/dentry.c @@ -131,6 +131,8 @@ static int sdcardfs_d_revalidate(struct dentry *dentry, unsigned int flags) static void sdcardfs_d_release(struct dentry *dentry) { + if (!dentry || !dentry->d_fsdata) + return; /* release and reset the lower paths */ if (has_graft_path(dentry)) sdcardfs_put_reset_orig_path(dentry); diff --git a/fs/sdcardfs/lookup.c b/fs/sdcardfs/lookup.c index 7dab5f76896b..2ff305161882 100644 --- a/fs/sdcardfs/lookup.c +++ b/fs/sdcardfs/lookup.c @@ -41,8 +41,6 @@ void sdcardfs_destroy_dentry_cache(void) void free_dentry_private_data(struct dentry *dentry) { - if (!dentry || !dentry->d_fsdata) - return; kmem_cache_free(sdcardfs_dentry_cachep, dentry->d_fsdata); dentry->d_fsdata = NULL; } diff --git a/fs/sdcardfs/main.c b/fs/sdcardfs/main.c index e4fd3fbb05e6..c3120108f627 100644 --- a/fs/sdcardfs/main.c +++ b/fs/sdcardfs/main.c @@ -422,7 +422,7 @@ void sdcardfs_kill_sb(struct super_block *sb) { struct sdcardfs_sb_info *sbi; - if (sb->s_magic == SDCARDFS_SUPER_MAGIC) { + if (sb->s_magic == SDCARDFS_SUPER_MAGIC && sb->s_fs_info) { sbi = SDCARDFS_SB(sb); mutex_lock(&sdcardfs_super_list_lock); list_del(&sbi->list); From 504533cbce11712b8774ccc65a46c99f58459e67 Mon Sep 17 00:00:00 2001 From: Daniel Rosenberg Date: Wed, 11 Apr 2018 16:24:51 -0700 Subject: [PATCH 0403/1103] ANDROID: sdcardfs: d_make_root calls iput d_make_root will call iput on failure, so we shouldn't try to do that ourselves. Signed-off-by: Daniel Rosenberg Bug: 77923821 Change-Id: I1abb4afb0f894ab917b7c6be8c833676f436beb7 --- fs/sdcardfs/main.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/fs/sdcardfs/main.c b/fs/sdcardfs/main.c index c3120108f627..1a977493f88d 100644 --- a/fs/sdcardfs/main.c +++ b/fs/sdcardfs/main.c @@ -316,7 +316,7 @@ static int sdcardfs_read_super(struct vfsmount *mnt, struct super_block *sb, sb->s_root = d_make_root(inode); if (!sb->s_root) { err = -ENOMEM; - goto out_iput; + goto out_sput; } d_set_d_op(sb->s_root, &sdcardfs_ci_dops); @@ -361,8 +361,6 @@ static int sdcardfs_read_super(struct vfsmount *mnt, struct super_block *sb, /* no longer needed: free_dentry_private_data(sb->s_root); */ out_freeroot: dput(sb->s_root); -out_iput: - iput(inode); out_sput: /* drop refs we took earlier */ atomic_dec(&lower_sb->s_active); From 1ab3888f0c7522370e096f36a5f382e5d71387ff Mon Sep 17 00:00:00 2001 From: Daniel Rosenberg Date: Wed, 11 Apr 2018 18:39:43 -0700 Subject: [PATCH 0404/1103] ANDROID: sdcardfs: Set s_root to NULL after putting Signed-off-by: Daniel Rosenberg Bug: 77923821 Change-Id: I1705bfd146009561d2d1da5f0e6a342ec6932a1c --- fs/sdcardfs/main.c | 1 + 1 file changed, 1 insertion(+) diff --git a/fs/sdcardfs/main.c b/fs/sdcardfs/main.c index 1a977493f88d..30e0c431a1ea 100644 --- a/fs/sdcardfs/main.c +++ b/fs/sdcardfs/main.c @@ -361,6 +361,7 @@ static int sdcardfs_read_super(struct vfsmount *mnt, struct super_block *sb, /* no longer needed: free_dentry_private_data(sb->s_root); */ out_freeroot: dput(sb->s_root); + sb->s_root = NULL; out_sput: /* drop refs we took earlier */ atomic_dec(&lower_sb->s_active); From 32d3ebd02a53160c16151a924c4a1de426be3dbb Mon Sep 17 00:00:00 2001 From: Sami Tolvanen Date: Thu, 11 May 2017 15:03:36 -0700 Subject: [PATCH 0405/1103] RFC: ANDROID: add support for clang Control Flow Integrity (CFI) This change adds the CONFIG_CFI_CLANG option, CFI error handling, and a faster look-up table for cross module CFI checks. Bug: 67506682 Change-Id: Ic009f0a629b552a0eb16e6d89808c7029e91447d Signed-off-by: Sami Tolvanen [AmitP: Rebased to newer kernels without clang LTO support] Signed-off-by: Amit Pundir --- Makefile | 30 +++ arch/Kconfig | 28 +++ include/asm-generic/vmlinux.lds.h | 3 + include/linux/cfi.h | 38 ++++ include/linux/compiler-clang.h | 4 + include/linux/compiler_types.h | 4 + include/linux/init.h | 2 +- include/linux/module.h | 5 + init/Kconfig | 2 +- kernel/Makefile | 4 + kernel/cfi.c | 300 ++++++++++++++++++++++++++++++ kernel/module.c | 27 +++ net/Kconfig | 1 + 13 files changed, 446 insertions(+), 2 deletions(-) create mode 100644 include/linux/cfi.h create mode 100644 kernel/cfi.c diff --git a/Makefile b/Makefile index e06e2a325422..9afe2ca3b29c 100644 --- a/Makefile +++ b/Makefile @@ -790,6 +790,30 @@ KBUILD_CFLAGS_KERNEL += -ffunction-sections -fdata-sections LDFLAGS_vmlinux += --gc-sections endif +ifdef CONFIG_CFI_CLANG +cfi-clang-flags += -fsanitize=cfi +DISABLE_CFI_CLANG := -fno-sanitize=cfi +ifdef CONFIG_MODULES +cfi-clang-flags += -fsanitize-cfi-cross-dso +DISABLE_CFI_CLANG += -fno-sanitize-cfi-cross-dso +endif +ifdef CONFIG_CFI_PERMISSIVE +cfi-clang-flags += -fsanitize-recover=cfi -fno-sanitize-trap=cfi +endif + +# allow disabling only clang CFI where needed +export DISABLE_CFI_CLANG +endif + +ifdef CONFIG_CFI +# cfi-flags are re-tested in prepare-compiler-check +cfi-flags := $(cfi-clang-flags) +KBUILD_CFLAGS += $(cfi-flags) + +DISABLE_CFI := $(DISABLE_CFI_CLANG) +export DISABLE_CFI +endif + # arch Makefile may override CC so keep this after arch Makefile is included NOSTDINC_FLAGS += -nostdinc -isystem $(shell $(CC) -print-file-name=include) @@ -1116,6 +1140,12 @@ uapi-asm-generic: PHONY += prepare-objtool prepare-objtool: $(objtool_target) +ifdef cfi-flags + ifeq ($(call cc-option, $(cfi-flags)),) + @echo Cannot use CONFIG_CFI: $(cfi-flags) not supported by compiler >&2 && exit 1 + endif +endif + # Generate some files # --------------------------------------------------------------------------- diff --git a/arch/Kconfig b/arch/Kconfig index 6801123932a5..9379d03bd5a4 100644 --- a/arch/Kconfig +++ b/arch/Kconfig @@ -474,6 +474,34 @@ config STACKPROTECTOR_STRONG about 20% of all kernel functions, which increases the kernel code size by about 2%. +config CFI + bool + +config CFI_PERMISSIVE + bool "Use CFI in permissive mode" + depends on CFI + help + When selected, Control Flow Integrity (CFI) violations result in a + warning instead of a kernel panic. This option is useful for finding + CFI violations in drivers during development. + +config CFI_CLANG + bool "Use clang Control Flow Integrity (CFI) (EXPERIMENTAL)" + depends on CC_IS_CLANG + depends on KALLSYMS + select CFI + help + This option enables clang Control Flow Integrity (CFI), which adds + runtime checking for indirect function calls. + +config CFI_CLANG_SHADOW + bool "Use CFI shadow to speed up cross-module checks" + default y + depends on CFI_CLANG + help + If you select this option, the kernel builds a fast look-up table of + CFI check functions in loaded modules to reduce overhead. + config HAVE_ARCH_WITHIN_STACK_FRAMES bool help diff --git a/include/asm-generic/vmlinux.lds.h b/include/asm-generic/vmlinux.lds.h index 7b75ff6e2fce..1a25efcf109f 100644 --- a/include/asm-generic/vmlinux.lds.h +++ b/include/asm-generic/vmlinux.lds.h @@ -68,6 +68,7 @@ */ #ifdef CONFIG_LD_DEAD_CODE_DATA_ELIMINATION #define TEXT_MAIN .text .text.[0-9a-zA-Z_]* +#define TEXT_CFI_MAIN .text.cfi .text.[0-9a-zA-Z_]*.cfi #define DATA_MAIN .data .data.[0-9a-zA-Z_]* #define SDATA_MAIN .sdata .sdata.[0-9a-zA-Z_]* #define RODATA_MAIN .rodata .rodata.[0-9a-zA-Z_]* @@ -75,6 +76,7 @@ #define SBSS_MAIN .sbss .sbss.[0-9a-zA-Z_]* #else #define TEXT_MAIN .text +#define TEXT_CFI_MAIN .text.cfi #define DATA_MAIN .data #define SDATA_MAIN .sdata #define RODATA_MAIN .rodata @@ -492,6 +494,7 @@ ALIGN_FUNCTION(); \ *(.text.hot TEXT_MAIN .text.fixup .text.unlikely) \ *(.text..refcount) \ + *(TEXT_CFI_MAIN) \ *(.ref.text) \ MEM_KEEP(init.text*) \ MEM_KEEP(exit.text*) \ diff --git a/include/linux/cfi.h b/include/linux/cfi.h new file mode 100644 index 000000000000..e27033d5dd53 --- /dev/null +++ b/include/linux/cfi.h @@ -0,0 +1,38 @@ +#ifndef _LINUX_CFI_H +#define _LINUX_CFI_H + +#include + +#ifdef CONFIG_CFI_CLANG +#ifdef CONFIG_MODULES + +typedef void (*cfi_check_fn)(uint64_t, void *, void *); + +/* Compiler-generated function in each module, and the kernel */ +#define CFI_CHECK_FN __cfi_check +#define CFI_CHECK_FN_NAME __stringify(CFI_CHECK_FN) + +extern void CFI_CHECK_FN(uint64_t, void *, void *); + +#ifdef CONFIG_CFI_CLANG_SHADOW +extern void cfi_module_add(struct module *mod, unsigned long min_addr, + unsigned long max_addr); + +extern void cfi_module_remove(struct module *mod, unsigned long min_addr, + unsigned long max_addr); +#else +static inline void cfi_module_add(struct module *mod, unsigned long min_addr, + unsigned long max_addr) +{ +} + +static inline void cfi_module_remove(struct module *mod, unsigned long min_addr, + unsigned long max_addr) +{ +} +#endif /* CONFIG_CFI_CLANG_SHADOW */ + +#endif /* CONFIG_MODULES */ +#endif /* CONFIG_CFI_CLANG */ + +#endif /* _LINUX_CFI_H */ diff --git a/include/linux/compiler-clang.h b/include/linux/compiler-clang.h index b1ce500fe8b3..426c9e9093a7 100644 --- a/include/linux/compiler-clang.h +++ b/include/linux/compiler-clang.h @@ -44,3 +44,7 @@ #define __must_be_array(a) BUILD_BUG_ON_ZERO(__same_type((a), &(a)[0])) #define __assume_aligned(a, ...) \ __attribute__((__assume_aligned__(a, ## __VA_ARGS__))) + +#ifdef CONFIG_CFI_CLANG +#define __nocfi __attribute__((no_sanitize("cfi"))) +#endif diff --git a/include/linux/compiler_types.h b/include/linux/compiler_types.h index db192becfec4..ecb53be5844d 100644 --- a/include/linux/compiler_types.h +++ b/include/linux/compiler_types.h @@ -144,6 +144,10 @@ struct ftrace_likely_data { #define __visible #endif +#ifndef __nocfi +#define __nocfi +#endif + /* * Assume alignment of return value. */ diff --git a/include/linux/init.h b/include/linux/init.h index 2538d176dd1f..9ef90a312a6f 100644 --- a/include/linux/init.h +++ b/include/linux/init.h @@ -47,7 +47,7 @@ /* These are for everybody (although not all archs will actually discard it in modules) */ -#define __init __section(.init.text) __cold __latent_entropy __noinitretpoline +#define __init __section(.init.text) __cold __latent_entropy __noinitretpoline __nocfi #define __initdata __section(.init.data) #define __initconst __section(.init.rodata) #define __exitdata __section(.exit.data) diff --git a/include/linux/module.h b/include/linux/module.h index f807f15bebbe..4b995dc988e5 100644 --- a/include/linux/module.h +++ b/include/linux/module.h @@ -20,6 +20,7 @@ #include #include #include +#include #include #include @@ -347,6 +348,10 @@ struct module { const s32 *crcs; unsigned int num_syms; +#ifdef CONFIG_CFI_CLANG + cfi_check_fn cfi_check; +#endif + /* Kernel parameters. */ #ifdef CONFIG_SYSFS struct mutex param_lock; diff --git a/init/Kconfig b/init/Kconfig index 1e234e2f1cba..01cad95346df 100644 --- a/init/Kconfig +++ b/init/Kconfig @@ -1972,7 +1972,7 @@ endif # MODULES config MODULES_TREE_LOOKUP def_bool y - depends on PERF_EVENTS || TRACING + depends on PERF_EVENTS || TRACING || CFI_CLANG config INIT_ALL_POSSIBLE bool diff --git a/kernel/Makefile b/kernel/Makefile index 7a63d567fdb5..7d146967402a 100644 --- a/kernel/Makefile +++ b/kernel/Makefile @@ -34,6 +34,9 @@ KASAN_SANITIZE_kcov.o := n # cond_syscall is currently not LTO compatible CFLAGS_sys_ni.o = $(DISABLE_LTO) +# Don't instrument error handlers +CFLAGS_cfi.o = $(DISABLE_CFI_CLANG) + obj-y += sched/ obj-y += locking/ obj-y += power/ @@ -103,6 +106,7 @@ obj-$(CONFIG_TRACEPOINTS) += trace/ obj-$(CONFIG_IRQ_WORK) += irq_work.o obj-$(CONFIG_CPU_PM) += cpu_pm.o obj-$(CONFIG_BPF) += bpf/ +obj-$(CONFIG_CFI_CLANG) += cfi.o obj-$(CONFIG_PERF_EVENTS) += events/ diff --git a/kernel/cfi.c b/kernel/cfi.c new file mode 100644 index 000000000000..7c403dc5091c --- /dev/null +++ b/kernel/cfi.c @@ -0,0 +1,300 @@ +/* + * CFI (Control Flow Integrity) error and slowpath handling + * + * Copyright (C) 2017 Google, Inc. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Compiler-defined handler names */ +#ifdef CONFIG_CFI_PERMISSIVE +#define cfi_failure_handler __ubsan_handle_cfi_check_fail +#define cfi_slowpath_handler __cfi_slowpath_diag +#else /* enforcing */ +#define cfi_failure_handler __ubsan_handle_cfi_check_fail_abort +#define cfi_slowpath_handler __cfi_slowpath +#endif /* CONFIG_CFI_PERMISSIVE */ + +static inline void handle_cfi_failure() +{ +#ifdef CONFIG_CFI_PERMISSIVE + WARN_RATELIMIT(1, "CFI failure:\n"); +#else + pr_err("CFI failure:\n"); + BUG(); +#endif +} + +#ifdef CONFIG_MODULES +#ifdef CONFIG_CFI_CLANG_SHADOW +struct shadow_range { + /* Module address range */ + unsigned long mod_min_addr; + unsigned long mod_max_addr; + /* Module page range */ + unsigned long min_page; + unsigned long max_page; +}; + +#define SHADOW_ORDER 1 +#define SHADOW_PAGES (1 << SHADOW_ORDER) +#define SHADOW_SIZE \ + ((SHADOW_PAGES * PAGE_SIZE - sizeof(struct shadow_range)) / sizeof(u16)) +#define SHADOW_INVALID 0xFFFF + +struct cfi_shadow { + /* Page range covered by the shadow */ + struct shadow_range r; + /* Page offsets to __cfi_check functions in modules */ + u16 shadow[SHADOW_SIZE]; +}; + +static DEFINE_SPINLOCK(shadow_update_lock); +static struct cfi_shadow __rcu *cfi_shadow __read_mostly = NULL; + +static inline int ptr_to_shadow(const struct cfi_shadow *s, unsigned long ptr) +{ + unsigned long index; + unsigned long page = ptr >> PAGE_SHIFT; + + if (unlikely(page < s->r.min_page)) + return -1; /* Outside of module area */ + + index = page - s->r.min_page; + + if (index >= SHADOW_SIZE) + return -1; /* Cannot be addressed with shadow */ + + return (int)index; +} + +static inline unsigned long shadow_to_ptr(const struct cfi_shadow *s, + int index) +{ + BUG_ON(index < 0 || index >= SHADOW_SIZE); + + if (unlikely(s->shadow[index] == SHADOW_INVALID)) + return 0; + + return (s->r.min_page + s->shadow[index]) << PAGE_SHIFT; +} + +static void prepare_next_shadow(const struct cfi_shadow __rcu *prev, + struct cfi_shadow *next) +{ + int i, index, check; + + /* Mark everything invalid */ + memset(next->shadow, 0xFF, sizeof(next->shadow)); + + if (!prev) + return; /* No previous shadow */ + + /* If the base address didn't change, update is not needed */ + if (prev->r.min_page == next->r.min_page) { + memcpy(next->shadow, prev->shadow, sizeof(next->shadow)); + return; + } + + /* Convert the previous shadow to the new address range */ + for (i = 0; i < SHADOW_SIZE; ++i) { + if (prev->shadow[i] == SHADOW_INVALID) + continue; + + index = ptr_to_shadow(next, shadow_to_ptr(prev, i)); + if (index < 0) + continue; + + check = ptr_to_shadow(next, + shadow_to_ptr(prev, prev->shadow[i])); + if (check < 0) + continue; + + next->shadow[index] = (u16)check; + } +} + +static void add_module_to_shadow(struct cfi_shadow *s, struct module *mod) +{ + unsigned long ptr; + unsigned long min_page_addr; + unsigned long max_page_addr; + unsigned long check = (unsigned long)mod->cfi_check; + int check_index = ptr_to_shadow(s, check); + + BUG_ON((check & PAGE_MASK) != check); /* Must be page aligned */ + + if (check_index < 0) + return; /* Module not addressable with shadow */ + + min_page_addr = (unsigned long)mod->core_layout.base & PAGE_MASK; + max_page_addr = (unsigned long)mod->core_layout.base + + mod->core_layout.text_size; + max_page_addr &= PAGE_MASK; + + /* For each page, store the check function index in the shadow */ + for (ptr = min_page_addr; ptr <= max_page_addr; ptr += PAGE_SIZE) { + int index = ptr_to_shadow(s, ptr); + if (index >= 0) { + /* Assume a page only contains code for one module */ + BUG_ON(s->shadow[index] != SHADOW_INVALID); + s->shadow[index] = (u16)check_index; + } + } +} + +static void remove_module_from_shadow(struct cfi_shadow *s, struct module *mod) +{ + unsigned long ptr; + unsigned long min_page_addr; + unsigned long max_page_addr; + + min_page_addr = (unsigned long)mod->core_layout.base & PAGE_MASK; + max_page_addr = (unsigned long)mod->core_layout.base + + mod->core_layout.text_size; + max_page_addr &= PAGE_MASK; + + for (ptr = min_page_addr; ptr <= max_page_addr; ptr += PAGE_SIZE) { + int index = ptr_to_shadow(s, ptr); + if (index >= 0) + s->shadow[index] = SHADOW_INVALID; + } +} + +typedef void (*update_shadow_fn)(struct cfi_shadow *, struct module *); + +static void update_shadow(struct module *mod, unsigned long min_addr, + unsigned long max_addr, update_shadow_fn fn) +{ + struct cfi_shadow *prev; + struct cfi_shadow *next = (struct cfi_shadow *) + __get_free_pages(GFP_KERNEL, SHADOW_ORDER); + + BUG_ON(!next); + + next->r.mod_min_addr = min_addr; + next->r.mod_max_addr = max_addr; + next->r.min_page = min_addr >> PAGE_SHIFT; + next->r.max_page = max_addr >> PAGE_SHIFT; + + spin_lock(&shadow_update_lock); + prev = rcu_dereference_protected(cfi_shadow, 1); + prepare_next_shadow(prev, next); + + fn(next, mod); + set_memory_ro((unsigned long)next, SHADOW_PAGES); + rcu_assign_pointer(cfi_shadow, next); + + spin_unlock(&shadow_update_lock); + synchronize_rcu(); + + if (prev) { + set_memory_rw((unsigned long)prev, SHADOW_PAGES); + free_pages((unsigned long)prev, SHADOW_ORDER); + } +} + +void cfi_module_add(struct module *mod, unsigned long min_addr, + unsigned long max_addr) +{ + update_shadow(mod, min_addr, max_addr, add_module_to_shadow); +} +EXPORT_SYMBOL(cfi_module_add); + +void cfi_module_remove(struct module *mod, unsigned long min_addr, + unsigned long max_addr) +{ + update_shadow(mod, min_addr, max_addr, remove_module_from_shadow); +} +EXPORT_SYMBOL(cfi_module_remove); + +static inline cfi_check_fn ptr_to_check_fn(const struct cfi_shadow __rcu *s, + unsigned long ptr) +{ + int index; + unsigned long check; + + if (unlikely(!s)) + return NULL; /* No shadow available */ + + if (ptr < s->r.mod_min_addr || ptr > s->r.mod_max_addr) + return NULL; /* Not in a mapped module */ + + index = ptr_to_shadow(s, ptr); + if (index < 0) + return NULL; /* Cannot be addressed with shadow */ + + return (cfi_check_fn)shadow_to_ptr(s, index); +} +#endif /* CONFIG_CFI_CLANG_SHADOW */ + +static inline cfi_check_fn find_module_cfi_check(void *ptr) +{ + struct module *mod; + + preempt_disable(); + mod = __module_address((unsigned long)ptr); + preempt_enable(); + + if (mod) + return mod->cfi_check; + + return CFI_CHECK_FN; +} + +static inline cfi_check_fn find_cfi_check(void *ptr) +{ +#ifdef CONFIG_CFI_CLANG_SHADOW + cfi_check_fn f; + + if (!rcu_access_pointer(cfi_shadow)) + return CFI_CHECK_FN; /* No loaded modules */ + + /* Look up the __cfi_check function to use */ + rcu_read_lock(); + f = ptr_to_check_fn(rcu_dereference(cfi_shadow), (unsigned long)ptr); + rcu_read_unlock(); + + if (f) + return f; + + /* + * Fall back to find_module_cfi_check, which works also for a larger + * module address space, but is slower. + */ +#endif /* CONFIG_CFI_CLANG_SHADOW */ + + return find_module_cfi_check(ptr); +} + +void cfi_slowpath_handler(uint64_t id, void *ptr, void *diag) +{ + cfi_check_fn check = find_cfi_check(ptr); + + if (likely(check)) + check(id, ptr, diag); + else /* Don't allow unchecked modules */ + handle_cfi_failure(); +} +EXPORT_SYMBOL(cfi_slowpath_handler); +#endif /* CONFIG_MODULES */ + +void cfi_failure_handler(void *data, void *value, void *vtable) +{ + handle_cfi_failure(); +} +EXPORT_SYMBOL(cfi_failure_handler); + +void __cfi_check_fail(void *data, void *value) +{ + handle_cfi_failure(); +} diff --git a/kernel/module.c b/kernel/module.c index 6746c85511fe..f896873975a5 100644 --- a/kernel/module.c +++ b/kernel/module.c @@ -2123,6 +2123,8 @@ void __weak module_arch_freeing_init(struct module *mod) { } +static void cfi_cleanup(struct module *mod); + /* Free a module, remove from lists, etc. */ static void free_module(struct module *mod) { @@ -2164,6 +2166,10 @@ static void free_module(struct module *mod) /* This may be empty, but that's OK */ disable_ro_nx(&mod->init_layout); + + /* Clean up CFI for the module. */ + cfi_cleanup(mod); + module_arch_freeing_init(mod); module_memfree(mod->init_layout.base); kfree(mod->args); @@ -3351,6 +3357,8 @@ int __weak module_finalize(const Elf_Ehdr *hdr, return 0; } +static void cfi_init(struct module *mod); + static int post_relocation(struct module *mod, const struct load_info *info) { /* Sort exception table now relocations are done. */ @@ -3363,6 +3371,9 @@ static int post_relocation(struct module *mod, const struct load_info *info) /* Setup kallsyms-specific fields. */ add_kallsyms(mod, info); + /* Setup CFI for the module. */ + cfi_init(mod); + /* Arch-specific module finalizing. */ return module_finalize(info->hdr, info->sechdrs, mod); } @@ -4132,6 +4143,22 @@ int module_kallsyms_on_each_symbol(int (*fn)(void *, const char *, } #endif /* CONFIG_KALLSYMS */ +static void cfi_init(struct module *mod) +{ +#ifdef CONFIG_CFI_CLANG + mod->cfi_check = + (cfi_check_fn)mod_find_symname(mod, CFI_CHECK_FN_NAME); + cfi_module_add(mod, module_addr_min, module_addr_max); +#endif +} + +static void cfi_cleanup(struct module *mod) +{ +#ifdef CONFIG_CFI_CLANG + cfi_module_remove(mod, module_addr_min, module_addr_max); +#endif +} + /* Maximum number of characters written by module_flags() */ #define MODULE_FLAGS_BUF_SIZE (TAINT_FLAGS_COUNT + 4) diff --git a/net/Kconfig b/net/Kconfig index 4d84cee12ab7..56bf7db443a2 100644 --- a/net/Kconfig +++ b/net/Kconfig @@ -293,6 +293,7 @@ config BPF_JIT bool "enable BPF Just In Time compiler" depends on HAVE_CBPF_JIT || HAVE_EBPF_JIT depends on MODULES + depends on !CFI ---help--- Berkeley Packet Filter filtering capabilities are normally handled by an interpreter. This option allows kernel to generate a native From 3fb2d342e541bebf6e652c93d48b16eefb10674c Mon Sep 17 00:00:00 2001 From: Sami Tolvanen Date: Wed, 14 Feb 2018 10:26:35 -0800 Subject: [PATCH 0406/1103] ANDROID: kallsyms: strip the .cfi postfix from symbols with CONFIG_CFI_CLANG With CFI enabled, LLVM appends .cfi to most function names, which potentially breaks user space tools. While stripping the postfix is not optimal either, this should at least create less confusion. Bug: 67506682 Bug: 73328469 Change-Id: I253f34a562629032ddd792b8498e171109ea7cbc Signed-off-by: Sami Tolvanen --- kernel/kallsyms.c | 49 ++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 44 insertions(+), 5 deletions(-) diff --git a/kernel/kallsyms.c b/kernel/kallsyms.c index 02a0b01380d8..672ed40e60c2 100644 --- a/kernel/kallsyms.c +++ b/kernel/kallsyms.c @@ -268,6 +268,24 @@ int kallsyms_lookup_size_offset(unsigned long addr, unsigned long *symbolsize, !!__bpf_address_lookup(addr, symbolsize, offset, namebuf); } +#ifdef CONFIG_CFI_CLANG +/* + * LLVM appends .cfi to function names when CONFIG_CFI_CLANG is enabled, + * which causes confusion and potentially breaks user space tools, so we + * will strip the postfix from expanded symbol names. + */ +static inline void cleanup_symbol_name(char *s) +{ + char *res; + + res = strrchr(s, '.'); + if (res && !strcmp(res, ".cfi")) + *res = '\0'; +} +#else +static inline void cleanup_symbol_name(char *s) {} +#endif + /* * Lookup an address * - modname is set to NULL if it's in the kernel. @@ -294,7 +312,9 @@ const char *kallsyms_lookup(unsigned long addr, namebuf, KSYM_NAME_LEN); if (modname) *modname = NULL; - return namebuf; + + ret = namebuf; + goto found; } /* See if it's in a module or a BPF JITed image. */ @@ -307,11 +327,16 @@ const char *kallsyms_lookup(unsigned long addr, if (!ret) ret = ftrace_mod_address_lookup(addr, symbolsize, offset, modname, namebuf); + +found: + cleanup_symbol_name(namebuf); return ret; } int lookup_symbol_name(unsigned long addr, char *symname) { + int res; + symname[0] = '\0'; symname[KSYM_NAME_LEN - 1] = '\0'; @@ -322,15 +347,23 @@ int lookup_symbol_name(unsigned long addr, char *symname) /* Grab name */ kallsyms_expand_symbol(get_symbol_offset(pos), symname, KSYM_NAME_LEN); - return 0; + goto found; } /* See if it's in a module. */ - return lookup_module_symbol_name(addr, symname); + res = lookup_module_symbol_name(addr, symname); + if (res) + return res; + +found: + cleanup_symbol_name(symname); + return 0; } int lookup_symbol_attrs(unsigned long addr, unsigned long *size, unsigned long *offset, char *modname, char *name) { + int res; + name[0] = '\0'; name[KSYM_NAME_LEN - 1] = '\0'; @@ -342,10 +375,16 @@ int lookup_symbol_attrs(unsigned long addr, unsigned long *size, kallsyms_expand_symbol(get_symbol_offset(pos), name, KSYM_NAME_LEN); modname[0] = '\0'; - return 0; + goto found; } /* See if it's in a module. */ - return lookup_module_symbol_attrs(addr, size, offset, modname, name); + res = lookup_module_symbol_attrs(addr, size, offset, modname, name); + if (res) + return res; + +found: + cleanup_symbol_name(name); + return 0; } /* Look up a kernel symbol and return it in a text buffer. */ From 072ee5836a109c40867ea0f4f44d44ddc3e6eb17 Mon Sep 17 00:00:00 2001 From: Sami Tolvanen Date: Fri, 18 Aug 2017 14:31:23 -0700 Subject: [PATCH 0407/1103] ANDROID: arm64: disable CFI for cpu_replace_ttbr1 Disable CFI to allow an indirect call to a physical address. Bug: 67506682 Change-Id: I0ec38f34245a4ad52f508f6989093526d3bf442f Signed-off-by: Sami Tolvanen --- arch/arm64/include/asm/mmu_context.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm64/include/asm/mmu_context.h b/arch/arm64/include/asm/mmu_context.h index 39ec0b8a689e..1b7aa97b9c9a 100644 --- a/arch/arm64/include/asm/mmu_context.h +++ b/arch/arm64/include/asm/mmu_context.h @@ -141,7 +141,7 @@ static inline void cpu_install_idmap(void) * Atomically replaces the active TTBR1_EL1 PGD with a new VA-compatible PGD, * avoiding the possibility of conflicting TLB entries being allocated. */ -static inline void cpu_replace_ttbr1(pgd_t *pgdp) +static inline void __nocfi cpu_replace_ttbr1(pgd_t *pgdp) { typedef void (ttbr_replace_func)(phys_addr_t); extern ttbr_replace_func idmap_cpu_replace_ttbr1; From fcae30413463e3147f2d7e4ac419018459849026 Mon Sep 17 00:00:00 2001 From: Greg Hackmann Date: Mon, 9 Apr 2018 13:48:49 -0700 Subject: [PATCH 0408/1103] ANDROID: arm64: mark kpti_install_ng_mappings as __nocfi 4.9.93 panics on boot when CFI_CLANG and UNMAP_KERNEL_AT_EL0 are both enabled. From Sami Tolvanen: "kpti_install_ng_mappings makes an indirect call to a physical address, which trips CFI. Adding the __nocfi attribute to this function should fix the problem." Bug: 77811249 Change-Id: I87d1ceb29f1ba2caee8954547596f4236bdfc31f Reported-by: Jean-Baptiste Theou Signed-off-by: Greg Hackmann --- arch/arm64/kernel/cpufeature.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm64/kernel/cpufeature.c b/arch/arm64/kernel/cpufeature.c index e238b7932096..703ebe0de2f8 100644 --- a/arch/arm64/kernel/cpufeature.c +++ b/arch/arm64/kernel/cpufeature.c @@ -902,7 +902,7 @@ static bool unmap_kernel_at_el0(const struct arm64_cpu_capabilities *entry, return !has_cpuid_feature(entry, scope); } -static void +static void __nocfi kpti_install_ng_mappings(const struct arm64_cpu_capabilities *__unused) { typedef void (kpti_remap_fn)(int, int, phys_addr_t); From 612ad522ed7b043593d1c9ad7fa0b288bcdc0198 Mon Sep 17 00:00:00 2001 From: Sami Tolvanen Date: Mon, 23 Apr 2018 12:52:07 -0700 Subject: [PATCH 0409/1103] ANDROID: arm64: kvm: disable CFI Disable CFI for code that runs at EL2 because __cfi_check only understands EL1 addresses. Bug: 67506682 Change-Id: Ia582943be0b31669d88464fd99228a5368b1aa6a Signed-off-by: Sami Tolvanen --- arch/arm64/kvm/hyp/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm64/kvm/hyp/Makefile b/arch/arm64/kvm/hyp/Makefile index 78e8a37fba9a..d9028bba9d46 100644 --- a/arch/arm64/kvm/hyp/Makefile +++ b/arch/arm64/kvm/hyp/Makefile @@ -4,7 +4,7 @@ # ccflags-y += -fno-stack-protector -DDISABLE_BRANCH_PROFILING \ - $(DISABLE_STACKLEAK_PLUGIN) + $(DISABLE_STACKLEAK_PLUGIN) $(DISABLE_CFI) ifeq ($(cc-name),clang) ccflags-y += -fno-jump-tables From cf5d3b95032036a597d15d20b6f248d346a87e47 Mon Sep 17 00:00:00 2001 From: Sami Tolvanen Date: Thu, 10 Aug 2017 09:39:53 -0700 Subject: [PATCH 0410/1103] ANDROID: arch/arm64/crypto: fix CFI in SHA CE Add C wrappers to allow indirect calls to sha[12]_ce_transform without tripping CFI. Bug: 67506682 Change-Id: If872f30095994206bc768eee13670be552b2a247 Signed-off-by: Sami Tolvanen --- arch/arm64/crypto/sha1-ce-glue.c | 8 ++++++++ arch/arm64/crypto/sha2-ce-glue.c | 8 ++++++++ 2 files changed, 16 insertions(+) diff --git a/arch/arm64/crypto/sha1-ce-glue.c b/arch/arm64/crypto/sha1-ce-glue.c index 17fac2889f56..c3d572af201c 100644 --- a/arch/arm64/crypto/sha1-ce-glue.c +++ b/arch/arm64/crypto/sha1-ce-glue.c @@ -29,6 +29,14 @@ struct sha1_ce_state { asmlinkage void sha1_ce_transform(struct sha1_ce_state *sst, u8 const *src, int blocks); +#ifdef CONFIG_CFI_CLANG +static inline void __cfi_sha1_ce_transform(struct sha1_state *sst, + u8 const *src, int blocks) +{ + sha1_ce_transform((struct sha1_ce_state *)sst, src, blocks); +} +#define sha1_ce_transform __cfi_sha1_ce_transform +#endif const u32 sha1_ce_offsetof_count = offsetof(struct sha1_ce_state, sst.count); const u32 sha1_ce_offsetof_finalize = offsetof(struct sha1_ce_state, finalize); diff --git a/arch/arm64/crypto/sha2-ce-glue.c b/arch/arm64/crypto/sha2-ce-glue.c index 261f5195cab7..db37282ca060 100644 --- a/arch/arm64/crypto/sha2-ce-glue.c +++ b/arch/arm64/crypto/sha2-ce-glue.c @@ -29,6 +29,14 @@ struct sha256_ce_state { asmlinkage void sha2_ce_transform(struct sha256_ce_state *sst, u8 const *src, int blocks); +#ifdef CONFIG_CFI_CLANG +static inline void __cfi_sha2_ce_transform(struct sha256_state *sst, + u8 const *src, int blocks) +{ + sha2_ce_transform((struct sha256_ce_state *)sst, src, blocks); +} +#define sha2_ce_transform __cfi_sha2_ce_transform +#endif const u32 sha256_ce_offsetof_count = offsetof(struct sha256_ce_state, sst.count); From 6c16b641faaae5dba762a81b6df9b11c02ceb8d1 Mon Sep 17 00:00:00 2001 From: Sami Tolvanen Date: Wed, 25 Apr 2018 16:08:11 -0700 Subject: [PATCH 0411/1103] ANDROID: mm: fix filler function type mismatch Bug: 67506682 Change-Id: I6f615164ccd86b407540ada9bbcb39d910395db9 Signed-off-by: Sami Tolvanen --- include/linux/pagemap.h | 4 ++-- mm/filemap.c | 6 +++--- mm/readahead.c | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/include/linux/pagemap.h b/include/linux/pagemap.h index b1bd2186e6d2..f24b02098061 100644 --- a/include/linux/pagemap.h +++ b/include/linux/pagemap.h @@ -239,7 +239,7 @@ static inline gfp_t readahead_gfp_mask(struct address_space *x) return mapping_gfp_mask(x) | __GFP_NORETRY | __GFP_NOWARN; } -typedef int filler_t(void *, struct page *); +typedef int filler_t(struct file *, struct page *); pgoff_t page_cache_next_hole(struct address_space *mapping, pgoff_t index, unsigned long max_scan); @@ -398,7 +398,7 @@ extern int read_cache_pages(struct address_space *mapping, static inline struct page *read_mapping_page(struct address_space *mapping, pgoff_t index, void *data) { - filler_t *filler = (filler_t *)mapping->a_ops->readpage; + filler_t *filler = mapping->a_ops->readpage; return read_cache_page(mapping, index, filler, data); } diff --git a/mm/filemap.c b/mm/filemap.c index 52517f28e6f4..59ebf349a988 100644 --- a/mm/filemap.c +++ b/mm/filemap.c @@ -2780,7 +2780,7 @@ static struct page *wait_on_page_read(struct page *page) static struct page *do_read_cache_page(struct address_space *mapping, pgoff_t index, - int (*filler)(void *, struct page *), + int (*filler)(struct file *, struct page *), void *data, gfp_t gfp) { @@ -2887,7 +2887,7 @@ static struct page *do_read_cache_page(struct address_space *mapping, */ struct page *read_cache_page(struct address_space *mapping, pgoff_t index, - int (*filler)(void *, struct page *), + int (*filler)(struct file *, struct page *), void *data) { return do_read_cache_page(mapping, index, filler, data, mapping_gfp_mask(mapping)); @@ -2909,7 +2909,7 @@ struct page *read_cache_page_gfp(struct address_space *mapping, pgoff_t index, gfp_t gfp) { - filler_t *filler = (filler_t *)mapping->a_ops->readpage; + filler_t *filler = mapping->a_ops->readpage; return do_read_cache_page(mapping, index, filler, NULL, gfp); } diff --git a/mm/readahead.c b/mm/readahead.c index 4e630143a0ba..4b47bef36dad 100644 --- a/mm/readahead.c +++ b/mm/readahead.c @@ -83,7 +83,7 @@ static void read_cache_pages_invalidate_pages(struct address_space *mapping, * Hides the details of the LRU cache etc from the filesystems. */ int read_cache_pages(struct address_space *mapping, struct list_head *pages, - int (*filler)(void *, struct page *), void *data) + int (*filler)(struct file *, struct page *), void *data) { struct page *page; int ret = 0; From f2d8f6c27c81b86510c71aff500e09fc1b97f2de Mon Sep 17 00:00:00 2001 From: Sami Tolvanen Date: Wed, 25 Apr 2018 16:08:28 -0700 Subject: [PATCH 0412/1103] ANDROID: fs: fuse: fix filler function type mismatch Bug: 67506682 Change-Id: Iabe7cdcc90dd2ea62976860531b8cbfcd76bd64b Signed-off-by: Sami Tolvanen --- fs/fuse/file.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/fs/fuse/file.c b/fs/fuse/file.c index 32d0b883e74f..5e1177754944 100644 --- a/fs/fuse/file.c +++ b/fs/fuse/file.c @@ -838,9 +838,9 @@ struct fuse_fill_data { unsigned nr_pages; }; -static int fuse_readpages_fill(void *_data, struct page *page) +static int fuse_readpages_fill(struct file *_data, struct page *page) { - struct fuse_fill_data *data = _data; + struct fuse_fill_data *data = (struct fuse_fill_data *)_data; struct fuse_req *req = data->req; struct inode *inode = data->inode; struct fuse_conn *fc = get_fuse_conn(inode); From 229e4c04ec17d20a1e960a4c8fb11ac34e4b3b9d Mon Sep 17 00:00:00 2001 From: Sami Tolvanen Date: Tue, 13 Feb 2018 13:01:39 -0800 Subject: [PATCH 0413/1103] ANDROID: fs: nfs: fix filler function type Bug: 67506682 Change-Id: I04d4b1b9ab0720a4f342d6617dd132de8654b94c Signed-off-by: Sami Tolvanen --- fs/nfs/dir.c | 5 +++-- fs/nfs/read.c | 2 +- fs/nfs/symlink.c | 5 +++-- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/fs/nfs/dir.c b/fs/nfs/dir.c index 8bfaa658b2c1..e337a16236a3 100644 --- a/fs/nfs/dir.c +++ b/fs/nfs/dir.c @@ -664,8 +664,9 @@ int nfs_readdir_xdr_to_array(nfs_readdir_descriptor_t *desc, struct page *page, * We only need to convert from xdr once so future lookups are much simpler */ static -int nfs_readdir_filler(nfs_readdir_descriptor_t *desc, struct page* page) +int nfs_readdir_filler(struct file *file, struct page* page) { + nfs_readdir_descriptor_t *desc = (nfs_readdir_descriptor_t *)file; struct inode *inode = file_inode(desc->file); int ret; @@ -698,7 +699,7 @@ static struct page *get_cache_page(nfs_readdir_descriptor_t *desc) { return read_cache_page(desc->file->f_mapping, - desc->page_index, (filler_t *)nfs_readdir_filler, desc); + desc->page_index, nfs_readdir_filler, desc); } /* diff --git a/fs/nfs/read.c b/fs/nfs/read.c index 48d7277c60a9..42dbf4f4d5aa 100644 --- a/fs/nfs/read.c +++ b/fs/nfs/read.c @@ -354,7 +354,7 @@ struct nfs_readdesc { }; static int -readpage_async_filler(void *data, struct page *page) +readpage_async_filler(struct file *data, struct page *page) { struct nfs_readdesc *desc = (struct nfs_readdesc *)data; struct nfs_page *new; diff --git a/fs/nfs/symlink.c b/fs/nfs/symlink.c index 06eb44b47885..220d5ba2bd9b 100644 --- a/fs/nfs/symlink.c +++ b/fs/nfs/symlink.c @@ -26,8 +26,9 @@ * and straight-forward than readdir caching. */ -static int nfs_symlink_filler(struct inode *inode, struct page *page) +static int nfs_symlink_filler(struct file *file, struct page *page) { + struct inode *inode = (struct inode *)file; int error; error = NFS_PROTO(inode)->readlink(inode, page, 0, PAGE_SIZE); @@ -66,7 +67,7 @@ static const char *nfs_get_link(struct dentry *dentry, if (err) return err; page = read_cache_page(&inode->i_data, 0, - (filler_t *)nfs_symlink_filler, inode); + nfs_symlink_filler, inode); if (IS_ERR(page)) return ERR_CAST(page); } From bf356754a4f3e0179e4d946853d31ffae0558108 Mon Sep 17 00:00:00 2001 From: Sami Tolvanen Date: Fri, 2 Mar 2018 09:02:16 -0800 Subject: [PATCH 0414/1103] ANDROID: fs: afs: fix filler function type Bug: 67506682 Change-Id: I76d208c8606ee5af144891d14bd309912d4d788d Signed-off-by: Sami Tolvanen --- fs/afs/file.c | 14 ++++++++++---- fs/afs/internal.h | 2 +- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/fs/afs/file.c b/fs/afs/file.c index 7d4f26198573..b95ff424bd08 100644 --- a/fs/afs/file.c +++ b/fs/afs/file.c @@ -260,12 +260,11 @@ int afs_fetch_data(struct afs_vnode *vnode, struct key *key, struct afs_read *de /* * read page from file, directory or symlink, given a key to use */ -int afs_page_filler(void *data, struct page *page) +static int __afs_page_filler(struct key *key, struct page *page) { struct inode *inode = page->mapping->host; struct afs_vnode *vnode = AFS_FS_I(inode); struct afs_read *req; - struct key *key = data; int ret; _enter("{%x},{%lu},{%lu}", key_serial(key), inode->i_ino, page->index); @@ -372,6 +371,13 @@ int afs_page_filler(void *data, struct page *page) return ret; } +int afs_page_filler(struct file *data, struct page *page) +{ + struct key *key = (struct key *)data; + + return __afs_page_filler(key, page); +} + /* * read page from file, directory or symlink, given a file to nominate the key * to be used @@ -384,14 +390,14 @@ static int afs_readpage(struct file *file, struct page *page) if (file) { key = afs_file_key(file); ASSERT(key != NULL); - ret = afs_page_filler(key, page); + ret = __afs_page_filler(key, page); } else { struct inode *inode = page->mapping->host; key = afs_request_key(AFS_FS_S(inode->i_sb)->cell); if (IS_ERR(key)) { ret = PTR_ERR(key); } else { - ret = afs_page_filler(key, page); + ret = __afs_page_filler(key, page); key_put(key); } } diff --git a/fs/afs/internal.h b/fs/afs/internal.h index 871a228d7f37..9d62a6a2454c 100644 --- a/fs/afs/internal.h +++ b/fs/afs/internal.h @@ -752,7 +752,7 @@ extern void afs_put_wb_key(struct afs_wb_key *); extern int afs_open(struct inode *, struct file *); extern int afs_release(struct inode *, struct file *); extern int afs_fetch_data(struct afs_vnode *, struct key *, struct afs_read *); -extern int afs_page_filler(void *, struct page *); +extern int afs_page_filler(struct file *, struct page *); extern void afs_put_read(struct afs_read *); /* From 012ba7daae395adc68f71cbddf5c61f4d0a9f615 Mon Sep 17 00:00:00 2001 From: Sami Tolvanen Date: Fri, 2 Mar 2018 09:04:53 -0800 Subject: [PATCH 0415/1103] ANDROID: fs: exofs: fix filler function type Bug: 67506682 Change-Id: I42f297bfe07a1b7916790415f35ad4f2574ceec7 Signed-off-by: Sami Tolvanen --- fs/exofs/inode.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/fs/exofs/inode.c b/fs/exofs/inode.c index 5f81fcd383a4..aef3f4206540 100644 --- a/fs/exofs/inode.c +++ b/fs/exofs/inode.c @@ -377,9 +377,8 @@ static int read_exec(struct page_collect *pcol) * and will start a new collection. Eventually caller must submit the last * segment if present. */ -static int readpage_strip(void *data, struct page *page) +static int __readpage_strip(struct page_collect *pcol, struct page *page) { - struct page_collect *pcol = data; struct inode *inode = pcol->inode; struct exofs_i_info *oi = exofs_i(inode); loff_t i_size = i_size_read(inode); @@ -470,6 +469,13 @@ static int readpage_strip(void *data, struct page *page) return ret; } +static int readpage_strip(struct file *data, struct page *page) +{ + struct page_collect *pcol = (struct page_collect *)data; + + return __readpage_strip(pcol, page); +} + static int exofs_readpages(struct file *file, struct address_space *mapping, struct list_head *pages, unsigned nr_pages) { @@ -499,7 +505,7 @@ static int _readpage(struct page *page, bool read_4_write) _pcol_init(&pcol, 1, page->mapping->host); pcol.read_4_write = read_4_write; - ret = readpage_strip(&pcol, page); + ret = __readpage_strip(&pcol, page); if (ret) { EXOFS_ERR("_readpage => %d\n", ret); return ret; From fb87c5d4d2f02b5f93e4ef4b0ed9cd16a1fac097 Mon Sep 17 00:00:00 2001 From: Sami Tolvanen Date: Fri, 2 Mar 2018 09:06:24 -0800 Subject: [PATCH 0416/1103] ANDROID: fs: gfs2: fix filler function type Bug: 67506682 Change-Id: I50a3f85965de6e041d0f40e7bf9c2ced15ccfd49 Signed-off-by: Sami Tolvanen --- fs/gfs2/aops.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/gfs2/aops.c b/fs/gfs2/aops.c index 31e8270d0b26..f3bdc4bdd5b1 100644 --- a/fs/gfs2/aops.c +++ b/fs/gfs2/aops.c @@ -504,7 +504,7 @@ int stuffed_readpage(struct gfs2_inode *ip, struct page *page) * called by gfs2_readpage() once the required lock has been granted. */ -static int __gfs2_readpage(void *file, struct page *page) +static int __gfs2_readpage(struct file *file, struct page *page) { struct gfs2_inode *ip = GFS2_I(page->mapping->host); struct gfs2_sbd *sdp = GFS2_SB(page->mapping->host); From 5a332ec7dadb65a4025b5fba75ba4c822b532481 Mon Sep 17 00:00:00 2001 From: Sami Tolvanen Date: Mon, 30 Apr 2018 12:23:14 -0700 Subject: [PATCH 0417/1103] cfi: print target address on failure Bug: 78862212 Bug: 67506682 Change-Id: Ifaa3e3f8fc5f19649f4857d185d50383b4a89055 Signed-off-by: Sami Tolvanen --- kernel/cfi.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/kernel/cfi.c b/kernel/cfi.c index 7c403dc5091c..c32e6b358797 100644 --- a/kernel/cfi.c +++ b/kernel/cfi.c @@ -24,12 +24,12 @@ #define cfi_slowpath_handler __cfi_slowpath #endif /* CONFIG_CFI_PERMISSIVE */ -static inline void handle_cfi_failure() +static inline void handle_cfi_failure(void *ptr) { #ifdef CONFIG_CFI_PERMISSIVE - WARN_RATELIMIT(1, "CFI failure:\n"); + WARN_RATELIMIT(1, "CFI failure (target: [<%px>] %pF):\n", ptr, ptr); #else - pr_err("CFI failure:\n"); + pr_err("CFI failure (target: [<%px>] %pF):\n", ptr, ptr); BUG(); #endif } @@ -283,18 +283,18 @@ void cfi_slowpath_handler(uint64_t id, void *ptr, void *diag) if (likely(check)) check(id, ptr, diag); else /* Don't allow unchecked modules */ - handle_cfi_failure(); + handle_cfi_failure(ptr); } EXPORT_SYMBOL(cfi_slowpath_handler); #endif /* CONFIG_MODULES */ -void cfi_failure_handler(void *data, void *value, void *vtable) +void cfi_failure_handler(void *data, void *ptr, void *vtable) { - handle_cfi_failure(); + handle_cfi_failure(ptr); } EXPORT_SYMBOL(cfi_failure_handler); -void __cfi_check_fail(void *data, void *value) +void __cfi_check_fail(void *data, void *ptr) { - handle_cfi_failure(); + handle_cfi_failure(ptr); } From dd31e064bbc468c28e96e1d86a38f3705c3c5fde Mon Sep 17 00:00:00 2001 From: Wei Wang Date: Thu, 3 May 2018 22:16:10 -0700 Subject: [PATCH 0418/1103] ANDROID: build.config: enforce trace_printk check Bug: 79166848 Change-Id: I41d2fe57b377e305b4b68c30c98ee94643d142e4 Test: Build a kernel with trace_prink and see warning Signed-off-by: Wei Wang --- build.config.cuttlefish.x86_64 | 1 + build.config.goldfish.arm | 1 + build.config.goldfish.arm64 | 1 + build.config.goldfish.mips | 1 + build.config.goldfish.mips64 | 1 + build.config.goldfish.x86 | 1 + build.config.goldfish.x86_64 | 1 + 7 files changed, 7 insertions(+) diff --git a/build.config.cuttlefish.x86_64 b/build.config.cuttlefish.x86_64 index 36c8a933f843..83344f38d104 100644 --- a/build.config.cuttlefish.x86_64 +++ b/build.config.cuttlefish.x86_64 @@ -13,3 +13,4 @@ arch/x86/boot/bzImage vmlinux System.map " +STOP_SHIP_TRACEPRINTK=1 diff --git a/build.config.goldfish.arm b/build.config.goldfish.arm index 866da9361b71..ff5646ab4f40 100644 --- a/build.config.goldfish.arm +++ b/build.config.goldfish.arm @@ -10,3 +10,4 @@ arch/arm/boot/zImage vmlinux System.map " +STOP_SHIP_TRACEPRINTK=1 diff --git a/build.config.goldfish.arm64 b/build.config.goldfish.arm64 index 9c963cf4a3d8..4c896a679ab9 100644 --- a/build.config.goldfish.arm64 +++ b/build.config.goldfish.arm64 @@ -10,3 +10,4 @@ arch/arm64/boot/Image vmlinux System.map " +STOP_SHIP_TRACEPRINTK=1 diff --git a/build.config.goldfish.mips b/build.config.goldfish.mips index 8af53d2c2940..9a14a444ac14 100644 --- a/build.config.goldfish.mips +++ b/build.config.goldfish.mips @@ -9,3 +9,4 @@ FILES=" vmlinux System.map " +STOP_SHIP_TRACEPRINTK=1 diff --git a/build.config.goldfish.mips64 b/build.config.goldfish.mips64 index 2a33d36dc4c8..6ad9759f5f4a 100644 --- a/build.config.goldfish.mips64 +++ b/build.config.goldfish.mips64 @@ -9,3 +9,4 @@ FILES=" vmlinux System.map " +STOP_SHIP_TRACEPRINTK=1 diff --git a/build.config.goldfish.x86 b/build.config.goldfish.x86 index f86253f58d4d..2266c621835e 100644 --- a/build.config.goldfish.x86 +++ b/build.config.goldfish.x86 @@ -10,3 +10,4 @@ arch/x86/boot/bzImage vmlinux System.map " +STOP_SHIP_TRACEPRINTK=1 diff --git a/build.config.goldfish.x86_64 b/build.config.goldfish.x86_64 index e1738861ec5c..08c42c2eba03 100644 --- a/build.config.goldfish.x86_64 +++ b/build.config.goldfish.x86_64 @@ -10,3 +10,4 @@ arch/x86/boot/bzImage vmlinux System.map " +STOP_SHIP_TRACEPRINTK=1 From 8066212e5874e1d7737c0d0777fa003408ea2cb8 Mon Sep 17 00:00:00 2001 From: David Herrmann Date: Sat, 24 Oct 2015 16:20:18 -0700 Subject: [PATCH 0419/1103] ANDROID: goldfish: drop CONFIG_INPUT_KEYCHORD Remove keychord driver, replaced in user space by https://android-review.googlesource.com/c/677629. Signed-off-by: Mark Salyzyn Cc: Jin Qian Cc: Amit Pundir Bug: 64114943 Change-Id: I0b673a5c68dbe28afa033d2ca70e12daea144b2a --- arch/arm/configs/ranchu_defconfig | 1 - arch/arm64/configs/ranchu64_defconfig | 1 - arch/x86/configs/i386_ranchu_defconfig | 1 - arch/x86/configs/x86_64_cuttlefish_defconfig | 1 - arch/x86/configs/x86_64_ranchu_defconfig | 1 - 5 files changed, 5 deletions(-) diff --git a/arch/arm/configs/ranchu_defconfig b/arch/arm/configs/ranchu_defconfig index 461a85a02764..69157c4c21fd 100644 --- a/arch/arm/configs/ranchu_defconfig +++ b/arch/arm/configs/ranchu_defconfig @@ -183,7 +183,6 @@ CONFIG_TABLET_USB_GTCO=y CONFIG_TABLET_USB_HANWANG=y CONFIG_TABLET_USB_KBTAB=y CONFIG_INPUT_MISC=y -CONFIG_INPUT_KEYCHORD=y CONFIG_INPUT_UINPUT=y CONFIG_INPUT_GPIO=y # CONFIG_SERIO_SERPORT is not set diff --git a/arch/arm64/configs/ranchu64_defconfig b/arch/arm64/configs/ranchu64_defconfig index 51c3bfc8658c..3d2eb3275b1f 100644 --- a/arch/arm64/configs/ranchu64_defconfig +++ b/arch/arm64/configs/ranchu64_defconfig @@ -186,7 +186,6 @@ CONFIG_KEYBOARD_GOLDFISH_EVENTS=y CONFIG_INPUT_JOYSTICK=y CONFIG_INPUT_TABLET=y CONFIG_INPUT_MISC=y -CONFIG_INPUT_KEYCHORD=y CONFIG_INPUT_UINPUT=y CONFIG_INPUT_GPIO=y # CONFIG_SERIO_SERPORT is not set diff --git a/arch/x86/configs/i386_ranchu_defconfig b/arch/x86/configs/i386_ranchu_defconfig index 18d3675d28f0..4e9dc7d49cbe 100644 --- a/arch/x86/configs/i386_ranchu_defconfig +++ b/arch/x86/configs/i386_ranchu_defconfig @@ -251,7 +251,6 @@ CONFIG_TABLET_USB_HANWANG=y CONFIG_TABLET_USB_KBTAB=y CONFIG_INPUT_TOUCHSCREEN=y CONFIG_INPUT_MISC=y -CONFIG_INPUT_KEYCHORD=y CONFIG_INPUT_UINPUT=y CONFIG_INPUT_GPIO=y # CONFIG_SERIO is not set diff --git a/arch/x86/configs/x86_64_cuttlefish_defconfig b/arch/x86/configs/x86_64_cuttlefish_defconfig index 1b9020912aae..3de85deeb44b 100644 --- a/arch/x86/configs/x86_64_cuttlefish_defconfig +++ b/arch/x86/configs/x86_64_cuttlefish_defconfig @@ -263,7 +263,6 @@ CONFIG_TABLET_USB_GTCO=y CONFIG_TABLET_USB_HANWANG=y CONFIG_TABLET_USB_KBTAB=y CONFIG_INPUT_MISC=y -CONFIG_INPUT_KEYCHORD=y CONFIG_INPUT_UINPUT=y CONFIG_INPUT_GPIO=y # CONFIG_SERIO_I8042 is not set diff --git a/arch/x86/configs/x86_64_ranchu_defconfig b/arch/x86/configs/x86_64_ranchu_defconfig index 7eff3002db18..81202e3f6ae8 100644 --- a/arch/x86/configs/x86_64_ranchu_defconfig +++ b/arch/x86/configs/x86_64_ranchu_defconfig @@ -248,7 +248,6 @@ CONFIG_TABLET_USB_HANWANG=y CONFIG_TABLET_USB_KBTAB=y CONFIG_INPUT_TOUCHSCREEN=y CONFIG_INPUT_MISC=y -CONFIG_INPUT_KEYCHORD=y CONFIG_INPUT_UINPUT=y CONFIG_INPUT_GPIO=y # CONFIG_SERIO is not set From cbf464e2d25aad87f72649c3e0bc7e653ae70cdc Mon Sep 17 00:00:00 2001 From: Daniel Rosenberg Date: Tue, 24 Apr 2018 18:06:56 -0700 Subject: [PATCH 0420/1103] ANDROID: sdcardfs: Don't d_drop in d_revalidate After d_revalidate returns 0, the vfs will call d_invalidate, which will call d_drop itself, along with other cleanup. Bug: 78262592 Change-Id: Idbb30e008c05d62edf2217679cb6a5517d8d1a2c Signed-off-by: Daniel Rosenberg --- fs/sdcardfs/dentry.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/fs/sdcardfs/dentry.c b/fs/sdcardfs/dentry.c index 166f14b2400b..776d549b397b 100644 --- a/fs/sdcardfs/dentry.c +++ b/fs/sdcardfs/dentry.c @@ -51,7 +51,6 @@ static int sdcardfs_d_revalidate(struct dentry *dentry, unsigned int flags) * whether the base obbpath has been changed or not */ if (is_obbpath_invalid(dentry)) { - d_drop(dentry); return 0; } @@ -65,7 +64,6 @@ static int sdcardfs_d_revalidate(struct dentry *dentry, unsigned int flags) if ((lower_dentry->d_flags & DCACHE_OP_REVALIDATE)) { err = lower_dentry->d_op->d_revalidate(lower_dentry, flags); if (err == 0) { - d_drop(dentry); goto out; } } @@ -73,14 +71,12 @@ static int sdcardfs_d_revalidate(struct dentry *dentry, unsigned int flags) spin_lock(&lower_dentry->d_lock); if (d_unhashed(lower_dentry)) { spin_unlock(&lower_dentry->d_lock); - d_drop(dentry); err = 0; goto out; } spin_unlock(&lower_dentry->d_lock); if (parent_lower_dentry != lower_cur_parent_dentry) { - d_drop(dentry); err = 0; goto out; } @@ -94,7 +90,6 @@ static int sdcardfs_d_revalidate(struct dentry *dentry, unsigned int flags) } if (!qstr_case_eq(&dentry->d_name, &lower_dentry->d_name)) { - __d_drop(dentry); err = 0; } @@ -113,7 +108,6 @@ static int sdcardfs_d_revalidate(struct dentry *dentry, unsigned int flags) if (inode) { data = top_data_get(SDCARDFS_I(inode)); if (!data || data->abandoned) { - d_drop(dentry); err = 0; } if (data) From f800db91dbd9c53f2a393586ecdc69a7b4e44b96 Mon Sep 17 00:00:00 2001 From: Alistair Strachan Date: Tue, 22 May 2018 11:41:31 -0700 Subject: [PATCH 0421/1103] ANDROID: build: cuttlefish: Fix path to clang. Reconcile with changes made to the kernel manifest. Clang must come from master because it was not usable for kernel builds in older branches of the Android platform. Bug: 63889157 Change-Id: Id0a080fc2f1cba495f37f26afa48e43e736b756a Signed-off-by: Alistair Strachan --- build.config.cuttlefish.x86_64 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.config.cuttlefish.x86_64 b/build.config.cuttlefish.x86_64 index 83344f38d104..5741a01ba5a2 100644 --- a/build.config.cuttlefish.x86_64 +++ b/build.config.cuttlefish.x86_64 @@ -6,7 +6,7 @@ DEFCONFIG=x86_64_cuttlefish_defconfig EXTRA_CMDS='' KERNEL_DIR=common POST_DEFCONFIG_CMDS="check_defconfig" -CLANG_PREBUILT_BIN=prebuilts/clang/host/linux-x86/clang-4630689/bin +CLANG_PREBUILT_BIN=prebuilts-master/clang/host/linux-x86/clang-4630689/bin LINUX_GCC_CROSS_COMPILE_PREBUILTS_BIN=prebuilts/gcc/linux-x86/x86/x86_64-linux-android-4.9/bin FILES=" arch/x86/boot/bzImage From db3675f14f3725424f19e2f871420e04f6558a48 Mon Sep 17 00:00:00 2001 From: Alistair Strachan Date: Tue, 22 May 2018 16:09:23 -0700 Subject: [PATCH 0422/1103] ANDROID: build: cuttlefish: Upgrade clang to newer version. Use the same clang version as hikey-linaro. Bug: 63889157 Change-Id: I6932d6149642d429086207e63aa8a8d5c2afd6f7 Signed-off-by: Alistair Strachan --- build.config.cuttlefish.x86_64 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.config.cuttlefish.x86_64 b/build.config.cuttlefish.x86_64 index 5741a01ba5a2..770f1b382e72 100644 --- a/build.config.cuttlefish.x86_64 +++ b/build.config.cuttlefish.x86_64 @@ -6,7 +6,7 @@ DEFCONFIG=x86_64_cuttlefish_defconfig EXTRA_CMDS='' KERNEL_DIR=common POST_DEFCONFIG_CMDS="check_defconfig" -CLANG_PREBUILT_BIN=prebuilts-master/clang/host/linux-x86/clang-4630689/bin +CLANG_PREBUILT_BIN=prebuilts-master/clang/host/linux-x86/clang-4679922/bin LINUX_GCC_CROSS_COMPILE_PREBUILTS_BIN=prebuilts/gcc/linux-x86/x86/x86_64-linux-android-4.9/bin FILES=" arch/x86/boot/bzImage From 81896fc0e4f003c54f9445f54a2be13e13b4597b Mon Sep 17 00:00:00 2001 From: Alistair Strachan Date: Tue, 22 May 2018 18:01:41 -0700 Subject: [PATCH 0423/1103] ANDROID: build: cuttlefish: Upgrade clang to newer version. The last upgrade introduced a new build failure, because it had a bug which caused it to emit PLT relocations, certain types of which cannot be handled by the reloc tool in the kernel. See https://bugs.llvm.org/show_bug.cgi?id=36674 for more details. Bug: 63889157 Change-Id: I813febdbacb0579abcb12dc7f2164cce1e2f5a26 Signed-off-by: Alistair Strachan --- build.config.cuttlefish.x86_64 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.config.cuttlefish.x86_64 b/build.config.cuttlefish.x86_64 index 770f1b382e72..694ed56a5f47 100644 --- a/build.config.cuttlefish.x86_64 +++ b/build.config.cuttlefish.x86_64 @@ -6,7 +6,7 @@ DEFCONFIG=x86_64_cuttlefish_defconfig EXTRA_CMDS='' KERNEL_DIR=common POST_DEFCONFIG_CMDS="check_defconfig" -CLANG_PREBUILT_BIN=prebuilts-master/clang/host/linux-x86/clang-4679922/bin +CLANG_PREBUILT_BIN=prebuilts-master/clang/host/linux-x86/clang-r328903/bin LINUX_GCC_CROSS_COMPILE_PREBUILTS_BIN=prebuilts/gcc/linux-x86/x86/x86_64-linux-android-4.9/bin FILES=" arch/x86/boot/bzImage From a22ca6b3185735bab0713c786bc8cddc631182e1 Mon Sep 17 00:00:00 2001 From: Alistair Strachan Date: Tue, 22 May 2018 16:35:04 -0700 Subject: [PATCH 0424/1103] ANDROID: x86_64_cuttlefish_defconfig: Disable ORC unwinder. Disable the ORC unwinder. This feature requires libelf-dev, which is breaking some automated build systems that do not have it installed. As we already enabled CONFIG_FRAME_POINTER, we already incurred the performance penalty of the legacy stack unwinder, so this is pretty much a no-op change. Bug: 63889157 Change-Id: Ic0704ebb726c97449ed873556262cc0db3e9a6cf Signed-off-by: Alistair Strachan --- arch/x86/configs/x86_64_cuttlefish_defconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/x86/configs/x86_64_cuttlefish_defconfig b/arch/x86/configs/x86_64_cuttlefish_defconfig index 3de85deeb44b..90be23b26f21 100644 --- a/arch/x86/configs/x86_64_cuttlefish_defconfig +++ b/arch/x86/configs/x86_64_cuttlefish_defconfig @@ -432,6 +432,7 @@ CONFIG_ENABLE_DEFAULT_TRACERS=y CONFIG_IO_DELAY_NONE=y CONFIG_DEBUG_BOOT_PARAMS=y CONFIG_OPTIMIZE_INLINING=y +CONFIG_UNWINDER_FRAME_POINTER=y CONFIG_SECURITY_PERF_EVENTS_RESTRICT=y CONFIG_SECURITY=y CONFIG_SECURITY_NETWORK=y From 6a4bc1a5e17cfe3afdef3853e6969c6e7cb51c42 Mon Sep 17 00:00:00 2001 From: Connor O'Brien Date: Wed, 23 May 2018 13:00:23 -0700 Subject: [PATCH 0425/1103] ANDROID: proc: fix undefined behavior in proc_uid_base_readdir When uid_base_stuff has no entries, proc_uid_base_readdir tries to compute an address before the start of the array. Revise this check to use uid_base_stuff + nents instead, which makes the code valid regardless of array size. Bug: 80158484 Test: No more compiler warning with CONFIG_CPU_FREQ_TIMES=n Change-Id: I6e55b27c3ba8210cee194f6d27bbd62c0b263796 Signed-off-by: Connor O'Brien --- fs/proc/uid.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/proc/uid.c b/fs/proc/uid.c index 9e15be510d71..6a096d25109d 100644 --- a/fs/proc/uid.c +++ b/fs/proc/uid.c @@ -174,7 +174,7 @@ static int proc_uid_base_readdir(struct file *file, struct dir_context *ctx) return 0; for (u = uid_base_stuff + (ctx->pos - 2); - u <= uid_base_stuff + nents - 1; u++) { + u < uid_base_stuff + nents; u++) { if (!proc_fill_cache(file, ctx, u->name, u->len, proc_uident_instantiate, NULL, u)) break; From 957521298a30a4ff236c069e59735b6cb459d3f6 Mon Sep 17 00:00:00 2001 From: Alistair Strachan Date: Thu, 31 May 2018 13:36:29 -0700 Subject: [PATCH 0426/1103] ANDROID: Update x86_64_cuttlefish_defconfig Merge with the configs from kernel/configs.git added recently. This should fix ipsec VPN functionality. Bug: 80540078 Change-Id: I9cc99f5e34d2809670fe2fc0df121610657f6769 Signed-off-by: Alistair Strachan --- arch/x86/configs/x86_64_cuttlefish_defconfig | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/arch/x86/configs/x86_64_cuttlefish_defconfig b/arch/x86/configs/x86_64_cuttlefish_defconfig index 90be23b26f21..1c6247dd06d9 100644 --- a/arch/x86/configs/x86_64_cuttlefish_defconfig +++ b/arch/x86/configs/x86_64_cuttlefish_defconfig @@ -32,6 +32,7 @@ CONFIG_OPROFILE=y CONFIG_KPROBES=y CONFIG_JUMP_LABEL=y CONFIG_CC_STACKPROTECTOR_STRONG=y +CONFIG_REFCOUNT_FULL=y CONFIG_MODULES=y CONFIG_MODULE_UNLOAD=y CONFIG_MODVERSIONS=y @@ -89,8 +90,8 @@ CONFIG_IP_MROUTE=y CONFIG_IP_PIMSM_V1=y CONFIG_IP_PIMSM_V2=y CONFIG_SYN_COOKIES=y +CONFIG_NET_IPVTI=y CONFIG_INET_ESP=y -# CONFIG_INET_XFRM_MODE_TRANSPORT is not set # CONFIG_INET_XFRM_MODE_BEET is not set CONFIG_INET_DIAG_DESTROY=y CONFIG_TCP_CONG_ADVANCED=y @@ -105,6 +106,7 @@ CONFIG_INET6_AH=y CONFIG_INET6_ESP=y CONFIG_INET6_IPCOMP=y CONFIG_IPV6_MIP6=y +CONFIG_IPV6_VTI=y CONFIG_IPV6_MULTIPLE_TABLES=y CONFIG_NETLABEL=y CONFIG_NETFILTER=y @@ -131,6 +133,7 @@ CONFIG_NETFILTER_XT_TARGET_TPROXY=y CONFIG_NETFILTER_XT_TARGET_TRACE=y CONFIG_NETFILTER_XT_TARGET_SECMARK=y CONFIG_NETFILTER_XT_TARGET_TCPMSS=y +CONFIG_NETFILTER_XT_MATCH_BPF=y CONFIG_NETFILTER_XT_MATCH_COMMENT=y CONFIG_NETFILTER_XT_MATCH_CONNLIMIT=y CONFIG_NETFILTER_XT_MATCH_CONNMARK=y @@ -144,14 +147,17 @@ CONFIG_NETFILTER_XT_MATCH_MAC=y CONFIG_NETFILTER_XT_MATCH_MARK=y CONFIG_NETFILTER_XT_MATCH_POLICY=y CONFIG_NETFILTER_XT_MATCH_PKTTYPE=y +CONFIG_NETFILTER_XT_MATCH_QTAGUID=y CONFIG_NETFILTER_XT_MATCH_QUOTA=y CONFIG_NETFILTER_XT_MATCH_QUOTA2=y +CONFIG_NETFILTER_XT_MATCH_SOCKET=y CONFIG_NETFILTER_XT_MATCH_STATE=y CONFIG_NETFILTER_XT_MATCH_STATISTIC=y CONFIG_NETFILTER_XT_MATCH_STRING=y CONFIG_NETFILTER_XT_MATCH_TIME=y CONFIG_NETFILTER_XT_MATCH_U32=y CONFIG_NF_CONNTRACK_IPV4=y +CONFIG_NF_SOCKET_IPV4=y CONFIG_IP_NF_IPTABLES=y CONFIG_IP_NF_MATCH_AH=y CONFIG_IP_NF_MATCH_ECN=y @@ -169,6 +175,7 @@ CONFIG_IP_NF_ARPTABLES=y CONFIG_IP_NF_ARPFILTER=y CONFIG_IP_NF_ARP_MANGLE=y CONFIG_NF_CONNTRACK_IPV6=y +CONFIG_NF_SOCKET_IPV6=y CONFIG_IP6_NF_IPTABLES=y CONFIG_IP6_NF_MATCH_IPV6HEADER=y CONFIG_IP6_NF_MATCH_RPFILTER=y @@ -263,6 +270,7 @@ CONFIG_TABLET_USB_GTCO=y CONFIG_TABLET_USB_HANWANG=y CONFIG_TABLET_USB_KBTAB=y CONFIG_INPUT_MISC=y +CONFIG_INPUT_KEYCHORD=y CONFIG_INPUT_UINPUT=y CONFIG_INPUT_GPIO=y # CONFIG_SERIO_I8042 is not set @@ -303,7 +311,6 @@ CONFIG_SOUND=y CONFIG_SND=y CONFIG_HIDRAW=y CONFIG_UHID=y -# CONFIG_HID_GENERIC is not set CONFIG_HID_A4TECH=y CONFIG_HID_ACRUX=y CONFIG_HID_ACRUX_FF=y @@ -423,12 +430,9 @@ CONFIG_DEBUG_MEMORY_INIT=y CONFIG_DEBUG_STACKOVERFLOW=y CONFIG_HARDLOCKUP_DETECTOR=y CONFIG_PANIC_TIMEOUT=5 -# CONFIG_SCHED_DEBUG is not set CONFIG_SCHEDSTATS=y CONFIG_RCU_CPU_STALL_TIMEOUT=60 CONFIG_ENABLE_DEFAULT_TRACERS=y -# CONFIG_KPROBE_EVENTS is not set -# CONFIG_UPROBE_EVENTS is not set CONFIG_IO_DELAY_NONE=y CONFIG_DEBUG_BOOT_PARAMS=y CONFIG_OPTIMIZE_INLINING=y @@ -441,4 +445,5 @@ CONFIG_HARDENED_USERCOPY=y CONFIG_SECURITY_SELINUX=y CONFIG_SECURITY_SELINUX_CHECKREQPROT_VALUE=1 # CONFIG_CRYPTO_MANAGER_DISABLE_TESTS is not set +CONFIG_CRYPTO_SHA512=y CONFIG_CRYPTO_DEV_VIRTIO=y From 6294b35a3b4bbec48bda529d7d12fcab584ca9f0 Mon Sep 17 00:00:00 2001 From: Alistair Strachan Date: Thu, 31 May 2018 15:47:36 -0700 Subject: [PATCH 0427/1103] ANDROID: x86_64_cuttlefish_defconfig: Enable F2FS Bug: 80475502 Change-Id: I061467404f1d4b828ac1b7423db375a35934ce28 Signed-off-by: Alistair Strachan --- arch/x86/configs/x86_64_cuttlefish_defconfig | 3 +++ 1 file changed, 3 insertions(+) diff --git a/arch/x86/configs/x86_64_cuttlefish_defconfig b/arch/x86/configs/x86_64_cuttlefish_defconfig index 1c6247dd06d9..26a42b91140b 100644 --- a/arch/x86/configs/x86_64_cuttlefish_defconfig +++ b/arch/x86/configs/x86_64_cuttlefish_defconfig @@ -398,6 +398,9 @@ CONFIG_EXT4_FS=y CONFIG_EXT4_FS_POSIX_ACL=y CONFIG_EXT4_FS_SECURITY=y CONFIG_EXT4_ENCRYPTION=y +CONFIG_F2FS_FS=y +CONFIG_F2FS_FS_SECURITY=y +CONFIG_F2FS_FS_ENCRYPTION=y CONFIG_QUOTA=y CONFIG_QUOTA_NETLINK_INTERFACE=y # CONFIG_PRINT_QUOTA_WARNING is not set From 0896f5eb885141d6e5cc1dac578986cdefd0b5dc Mon Sep 17 00:00:00 2001 From: Rik van Riel Date: Thu, 1 Sep 2011 15:26:50 -0400 Subject: [PATCH 0428/1103] ANDROID: add extra free kbytes tunable Add a userspace visible knob to tell the VM to keep an extra amount of memory free, by increasing the gap between each zone's min and low watermarks. This is useful for realtime applications that call system calls and have a bound on the number of allocations that happen in any short time period. In this application, extra_free_kbytes would be left at an amount equal to or larger than than the maximum number of allocations that happen in any burst. It may also be useful to reduce the memory use of virtual machines (temporarily?), in a way that does not cause memory fragmentation like ballooning does. [ccross] Revived for use on old kernels where no other solution exists. The tunable will be removed on kernels that do better at avoiding direct reclaim. [surenb] Will be reverted as soon as Android framework is reworked to use upstream-supported watermark_scale_factor instead of extra_free_kbytes. Bug: 86445363 Change-Id: I765a42be8e964bfd3e2886d1ca85a29d60c3bb3e Signed-off-by: Rik van Riel Signed-off-by: Colin Cross Signed-off-by: Suren Baghdasaryan --- Documentation/sysctl/vm.txt | 16 ++++++++++++++++ kernel/sysctl.c | 9 +++++++++ mm/page_alloc.c | 34 ++++++++++++++++++++++++++-------- 3 files changed, 51 insertions(+), 8 deletions(-) diff --git a/Documentation/sysctl/vm.txt b/Documentation/sysctl/vm.txt index 7d73882e2c27..a48baf202265 100644 --- a/Documentation/sysctl/vm.txt +++ b/Documentation/sysctl/vm.txt @@ -31,6 +31,7 @@ Currently, these files are in /proc/sys/vm: - dirty_writeback_centisecs - drop_caches - extfrag_threshold +- extra_free_kbytes - hugetlb_shm_group - laptop_mode - legacy_va_layout @@ -274,6 +275,21 @@ any throttling. ============================================================== +extra_free_kbytes + +This parameter tells the VM to keep extra free memory between the threshold +where background reclaim (kswapd) kicks in, and the threshold where direct +reclaim (by allocating processes) kicks in. + +This is useful for workloads that require low latency memory allocations +and have a bounded burstiness in memory allocations, for example a +realtime application that receives and transmits network traffic +(causing in-kernel memory allocations) with a maximum total message burst +size of 200MB may need 200MB of extra free memory to avoid direct reclaim +related latencies. + +============================================================== + hugetlb_shm_group hugetlb_shm_group contains group id that is allowed to create SysV diff --git a/kernel/sysctl.c b/kernel/sysctl.c index cc02050fd0c4..8c7635ecb752 100644 --- a/kernel/sysctl.c +++ b/kernel/sysctl.c @@ -106,6 +106,7 @@ extern char core_pattern[]; extern unsigned int core_pipe_limit; #endif extern int pid_max; +extern int extra_free_kbytes; extern int pid_max_min, pid_max_max; extern int percpu_pagelist_fraction; extern int latencytop_enabled; @@ -1459,6 +1460,14 @@ static struct ctl_table vm_table[] = { .extra1 = &one, .extra2 = &one_thousand, }, + { + .procname = "extra_free_kbytes", + .data = &extra_free_kbytes, + .maxlen = sizeof(extra_free_kbytes), + .mode = 0644, + .proc_handler = min_free_kbytes_sysctl_handler, + .extra1 = &zero, + }, { .procname = "percpu_pagelist_fraction", .data = &percpu_pagelist_fraction, diff --git a/mm/page_alloc.c b/mm/page_alloc.c index 706a738c0aee..226f7c3fad8c 100644 --- a/mm/page_alloc.c +++ b/mm/page_alloc.c @@ -261,10 +261,22 @@ compound_page_dtor * const compound_page_dtors[] = { #endif }; +/* + * Try to keep at least this much lowmem free. Do not allow normal + * allocations below this point, only high priority ones. Automatically + * tuned according to the amount of memory in the system. + */ int min_free_kbytes = 1024; int user_min_free_kbytes = -1; int watermark_scale_factor = 10; +/* + * Extra memory for the system to try freeing. Used to temporarily + * free memory, to make space for new workloads. Anyone can allocate + * down to the min watermarks controlled by min_free_kbytes above. + */ +int extra_free_kbytes = 0; + static unsigned long nr_kernel_pages __meminitdata; static unsigned long nr_all_pages __meminitdata; static unsigned long dma_reserve __meminitdata; @@ -7224,6 +7236,7 @@ static void setup_per_zone_lowmem_reserve(void) static void __setup_per_zone_wmarks(void) { unsigned long pages_min = min_free_kbytes >> (PAGE_SHIFT - 10); + unsigned long pages_low = extra_free_kbytes >> (PAGE_SHIFT - 10); unsigned long lowmem_pages = 0; struct zone *zone; unsigned long flags; @@ -7235,11 +7248,14 @@ static void __setup_per_zone_wmarks(void) } for_each_zone(zone) { - u64 tmp; + u64 min, low; spin_lock_irqsave(&zone->lock, flags); - tmp = (u64)pages_min * zone->managed_pages; - do_div(tmp, lowmem_pages); + min = (u64)pages_min * zone->managed_pages; + do_div(min, lowmem_pages); + low = (u64)pages_low * zone->managed_pages; + do_div(low, vm_total_pages); + if (is_highmem(zone)) { /* * __GFP_HIGH and PF_MEMALLOC allocations usually don't @@ -7260,7 +7276,7 @@ static void __setup_per_zone_wmarks(void) * If it's a lowmem zone, reserve a number of pages * proportionate to the zone's size. */ - zone->watermark[WMARK_MIN] = tmp; + zone->watermark[WMARK_MIN] = min; } /* @@ -7268,12 +7284,14 @@ static void __setup_per_zone_wmarks(void) * scale factor in proportion to available memory, but * ensure a minimum size on small systems. */ - tmp = max_t(u64, tmp >> 2, + min = max_t(u64, min >> 2, mult_frac(zone->managed_pages, watermark_scale_factor, 10000)); - zone->watermark[WMARK_LOW] = min_wmark_pages(zone) + tmp; - zone->watermark[WMARK_HIGH] = min_wmark_pages(zone) + tmp * 2; + zone->watermark[WMARK_LOW] = min_wmark_pages(zone) + + low + min; + zone->watermark[WMARK_HIGH] = min_wmark_pages(zone) + + low + min * 2; spin_unlock_irqrestore(&zone->lock, flags); } @@ -7356,7 +7374,7 @@ core_initcall(init_per_zone_wmark_min) /* * min_free_kbytes_sysctl_handler - just a wrapper around proc_dointvec() so * that we can call two helper functions whenever min_free_kbytes - * changes. + * or extra_free_kbytes changes. */ int min_free_kbytes_sysctl_handler(struct ctl_table *table, int write, void __user *buffer, size_t *length, loff_t *ppos) From 1d5d9654d96be049cd319c3da2cd437ed4336133 Mon Sep 17 00:00:00 2001 From: Patrik Torstensson Date: Fri, 13 Apr 2018 15:34:48 -0700 Subject: [PATCH 0429/1103] ANDROID: Add kconfig to make dm-verity check_at_most_once default enabled This change adds a kernel config for default enable the check_at_most_once dm-verity option. This is to give us the ability to enforce the usage of at_most_once for entry-level phones. Change-Id: Id40416672c4c2209a9866997d8c164b5de5dc7dc Signed-off-by: Patrik Torstensson Bug: 72664474 --- drivers/md/Kconfig | 20 ++++++++++++++++++++ drivers/md/dm-verity-target.c | 8 ++++++++ 2 files changed, 28 insertions(+) diff --git a/drivers/md/Kconfig b/drivers/md/Kconfig index 44d56f41660f..8a258c688a07 100644 --- a/drivers/md/Kconfig +++ b/drivers/md/Kconfig @@ -591,4 +591,24 @@ config DM_ANDROID_VERITY of the metadata contents are verified against the key included in the system keyring. Upon success, the underlying verity target is setup. + +config DM_ANDROID_VERITY_AT_MOST_ONCE_DEFAULT_ENABLED + bool "Verity will validate blocks at most once" + depends on DM_VERITY + ---help--- + Default enables at_most_once option for dm-verity + + Verify data blocks only the first time they are read from the + data device, rather than every time. This reduces the overhead + of dm-verity so that it can be used on systems that are memory + and/or CPU constrained. However, it provides a reduced level + of security because only offline tampering of the data device's + content will be detected, not online tampering. + + Hash blocks are still verified each time they are read from the + hash device, since verification of hash blocks is less performance + critical than data blocks, and a hash block will not be verified + any more after all the data blocks it covers have been verified anyway. + + If unsure, say N. endif # MD diff --git a/drivers/md/dm-verity-target.c b/drivers/md/dm-verity-target.c index 427cabe675a8..1a9f464d15c7 100644 --- a/drivers/md/dm-verity-target.c +++ b/drivers/md/dm-verity-target.c @@ -1098,6 +1098,14 @@ int verity_ctr(struct dm_target *ti, unsigned argc, char **argv) goto bad; } +#ifdef CONFIG_DM_ANDROID_VERITY_AT_MOST_ONCE_DEFAULT_ENABLED + if (!v->validated_blocks) { + r = verity_alloc_most_once(v); + if (r) + goto bad; + } +#endif + v->hash_per_block_bits = __fls((1 << v->hash_dev_block_bits) / v->digest_size); From 876ba681fb4680a900d1e780420ab206d8bfa1bb Mon Sep 17 00:00:00 2001 From: Nathan Chancellor Date: Sat, 31 Mar 2018 20:56:23 -0700 Subject: [PATCH 0430/1103] ANDROID: xt_qtaguid: Remove unnecessary null checks to device's name 'name' will never be NULL since it isn't a plain pointer but an array of char values. ../net/netfilter/xt_qtaguid.c:1195:27: warning: address of array '(*el_dev)->name' will always evaluate to 'true' [-Wpointer-bool-conversion] if (unlikely(!(*el_dev)->name)) { ~~~~~~~~~~~~^~~~ Change-Id: If3b25f17829b43e8a639193fb9cd04ae45947200 Signed-off-by: Nathan Chancellor (cherry picked from android-4.4 commit 207b579e3db6fd0cb6fe40ba3e929635ad748d89) Signed-off-by: Chenbo Feng --- net/netfilter/xt_qtaguid.c | 5 ----- 1 file changed, 5 deletions(-) diff --git a/net/netfilter/xt_qtaguid.c b/net/netfilter/xt_qtaguid.c index cd7c34b45221..d261932ee595 100644 --- a/net/netfilter/xt_qtaguid.c +++ b/net/netfilter/xt_qtaguid.c @@ -1194,11 +1194,6 @@ static void get_dev_and_dir(const struct sk_buff *skb, parst->hook, __func__); BUG(); } - if (unlikely(!(*el_dev)->name)) { - pr_err("qtaguid[%d]: %s(): no dev->name?!!\n", - parst->hook, __func__); - BUG(); - } if (skb->dev && *el_dev != skb->dev) { MT_DEBUG("qtaguid[%d]: skb->dev=%p %s vs par->%s=%p %s\n", parst->hook, skb->dev, skb->dev->name, From 9485011bc13377a02fcfa911331a6948cca0cf11 Mon Sep 17 00:00:00 2001 From: Lianjun Huang Date: Sat, 16 Jun 2018 22:59:46 +0800 Subject: [PATCH 0431/1103] ANDROID: sdcardfs: fix potential crash when reserved_mb is not zero sdcardfs_mkdir() calls check_min_free_space(). When reserved_mb is not zero, a negative dentry will be passed to ext4_statfs() at last and ext4_statfs() will crash. The parent dentry is positive. So we use the parent dentry to check free space. Change-Id: I80ab9623fe59ba911f4cc9f0e029a1c6f7ee421b Signed-off-by: Lianjun Huang --- fs/sdcardfs/inode.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/fs/sdcardfs/inode.c b/fs/sdcardfs/inode.c index b43258684fb9..2de5a4dffa22 100644 --- a/fs/sdcardfs/inode.c +++ b/fs/sdcardfs/inode.c @@ -270,6 +270,7 @@ static int sdcardfs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode struct dentry *lower_dentry; struct vfsmount *lower_mnt; struct dentry *lower_parent_dentry = NULL; + struct dentry *parent_dentry = NULL; struct path lower_path; struct sdcardfs_sb_info *sbi = SDCARDFS_SB(dentry->d_sb); const struct cred *saved_cred = NULL; @@ -289,11 +290,14 @@ static int sdcardfs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode OVERRIDE_CRED(SDCARDFS_SB(dir->i_sb), saved_cred, SDCARDFS_I(dir)); /* check disk space */ - if (!check_min_free_space(dentry, 0, 1)) { + parent_dentry = dget_parent(dentry); + if (!check_min_free_space(parent_dentry, 0, 1)) { pr_err("sdcardfs: No minimum free space.\n"); err = -ENOSPC; + dput(parent_dentry); goto out_revert; } + dput(parent_dentry); /* the lower_dentry is negative here */ sdcardfs_get_lower_path(dentry, &lower_path); From f0ce2af55eff94b4fbfc9f7f6b23db29f8b7a2a0 Mon Sep 17 00:00:00 2001 From: Connor O'Brien Date: Fri, 13 Jul 2018 14:31:40 -0700 Subject: [PATCH 0432/1103] ANDROID: Reduce use of #ifdef CONFIG_CPU_FREQ_TIMES Add empty versions of functions to cpufreq_times.h to cut down on use of #ifdef in .c files. Test: kernel builds with and without CONFIG_CPU_FREQ_TIMES=y Change-Id: I49ac364fac3d42bba0ca1801e23b15081094fb12 Signed-off-by: Connor O'Brien --- include/linux/cpufreq_times.h | 4 ++++ kernel/exit.c | 3 +-- kernel/sched/core.c | 2 -- kernel/sched/cputime.c | 5 +---- 4 files changed, 6 insertions(+), 8 deletions(-) diff --git a/include/linux/cpufreq_times.h b/include/linux/cpufreq_times.h index e84b576a20d5..54419522a53c 100644 --- a/include/linux/cpufreq_times.h +++ b/include/linux/cpufreq_times.h @@ -30,6 +30,10 @@ void cpufreq_times_record_transition(struct cpufreq_freqs *freq); void cpufreq_task_times_remove_uids(uid_t uid_start, uid_t uid_end); int single_uid_time_in_state_open(struct inode *inode, struct file *file); #else +static inline void cpufreq_task_times_init(struct task_struct *p) {} +static inline void cpufreq_task_times_exit(struct task_struct *p) {} +static inline void cpufreq_acct_update_power(struct task_struct *p, + u64 cputime) {} static inline void cpufreq_times_create_policy(struct cpufreq_policy *policy) {} static inline void cpufreq_times_record_transition( struct cpufreq_freqs *freq) {} diff --git a/kernel/exit.c b/kernel/exit.c index 07eceecc6402..2d95762343af 100644 --- a/kernel/exit.c +++ b/kernel/exit.c @@ -187,9 +187,8 @@ void release_task(struct task_struct *p) { struct task_struct *leader; int zap_leader; -#ifdef CONFIG_CPU_FREQ_TIMES + cpufreq_task_times_exit(p); -#endif repeat: /* don't need to get the RCU readlock here - the process is dead and * can't be modifying its own credentials. But shut RCU-lockdep up */ diff --git a/kernel/sched/core.c b/kernel/sched/core.c index 6b2a27bd29bc..623c6c5c941a 100644 --- a/kernel/sched/core.c +++ b/kernel/sched/core.c @@ -2157,9 +2157,7 @@ static void __sched_fork(unsigned long clone_flags, struct task_struct *p) memset(&p->se.statistics, 0, sizeof(p->se.statistics)); #endif -#ifdef CONFIG_CPU_FREQ_TIMES cpufreq_task_times_init(p); -#endif RB_CLEAR_NODE(&p->dl.rb_node); init_dl_task_timer(&p->dl); diff --git a/kernel/sched/cputime.c b/kernel/sched/cputime.c index ab9b6ff7117a..ca4208d46872 100644 --- a/kernel/sched/cputime.c +++ b/kernel/sched/cputime.c @@ -130,10 +130,8 @@ void account_user_time(struct task_struct *p, u64 cputime) /* Account for user time used */ acct_account_cputime(p); -#ifdef CONFIG_CPU_FREQ_TIMES /* Account power usage for user time */ cpufreq_acct_update_power(p, cputime); -#endif } /* @@ -178,10 +176,9 @@ void account_system_index_time(struct task_struct *p, /* Account for system time used */ acct_account_cputime(p); -#ifdef CONFIG_CPU_FREQ_TIMES + /* Account power usage for system time */ cpufreq_acct_update_power(p, cputime); -#endif } /* From 05b988ec1b821fdf4e311e19dd62c7ca72485627 Mon Sep 17 00:00:00 2001 From: Sultan Alsawaf Date: Sun, 3 Jun 2018 10:47:51 -0700 Subject: [PATCH 0433/1103] ANDROID: Fix massive cpufreq_times memory leaks Every time _cpu_up() is called for a CPU, idle_thread_get() is called which then re-initializes a CPU's idle thread that was already previously created and cached in a global variable in smpboot.c. idle_thread_get() calls init_idle() which then calls __sched_fork(). __sched_fork() is where cpufreq_task_times_init() is, and cpufreq_task_times_init() allocates memory for the task struct's time_in_state array. Since idle_thread_get() reuses a task struct instance that was already previously created, this means that every time it calls init_idle(), cpufreq_task_times_init() allocates this array again and overwrites the existing allocation that the idle thread already had. This causes memory to be leaked every time a CPU is onlined. In order to fix this, move allocation of time_in_state into _do_fork to avoid allocating it at all for idle threads. The cpufreq times interface is intended to be used for tracking userspace tasks, so we can safely remove it from the kernel's idle threads without killing any functionality. But that's not all! Task structs can be freed outside of release_task(), which creates another memory leak because a task struct can be freed without having its cpufreq times allocation freed. To fix this, free the cpufreq times allocation at the same time that task struct allocations are freed, in free_task(). Since free_task() can also be called in error paths of copy_process() after dup_task_struct(), set time_in_state to NULL immediately after calling dup_task_struct() to avoid possible double free. Bug description and fix adapted from patch submitted by Sultan Alsawaf at https://android-review.googlesource.com/c/kernel/msm/+/700134 Bug: 110044919 Test: Hikey960 builds, boots & reports /proc//time_in_state correctly Change-Id: I12fe7611fc88eb7f6c39f8f7629ad27b6ec4722c Signed-off-by: Connor O'Brien --- drivers/cpufreq/cpufreq_times.c | 9 ++++++--- include/linux/cpufreq_times.h | 2 ++ kernel/exit.c | 3 --- kernel/fork.c | 7 +++++++ kernel/sched/core.c | 3 --- 5 files changed, 15 insertions(+), 9 deletions(-) diff --git a/drivers/cpufreq/cpufreq_times.c b/drivers/cpufreq/cpufreq_times.c index f560e10ba183..a43eeee30e8e 100644 --- a/drivers/cpufreq/cpufreq_times.c +++ b/drivers/cpufreq/cpufreq_times.c @@ -234,16 +234,19 @@ static int uid_time_in_state_seq_show(struct seq_file *m, void *v) void cpufreq_task_times_init(struct task_struct *p) { - void *temp; unsigned long flags; - unsigned int max_state; spin_lock_irqsave(&task_time_in_state_lock, flags); p->time_in_state = NULL; spin_unlock_irqrestore(&task_time_in_state_lock, flags); p->max_state = 0; +} - max_state = READ_ONCE(next_offset); +void cpufreq_task_times_alloc(struct task_struct *p) +{ + void *temp; + unsigned long flags; + unsigned int max_state = READ_ONCE(next_offset); /* We use one array to avoid multiple allocs per task */ temp = kcalloc(max_state, sizeof(p->time_in_state[0]), GFP_ATOMIC); diff --git a/include/linux/cpufreq_times.h b/include/linux/cpufreq_times.h index 54419522a53c..757bf0cb6070 100644 --- a/include/linux/cpufreq_times.h +++ b/include/linux/cpufreq_times.h @@ -21,6 +21,7 @@ #ifdef CONFIG_CPU_FREQ_TIMES void cpufreq_task_times_init(struct task_struct *p); +void cpufreq_task_times_alloc(struct task_struct *p); void cpufreq_task_times_exit(struct task_struct *p); int proc_time_in_state_show(struct seq_file *m, struct pid_namespace *ns, struct pid *pid, struct task_struct *p); @@ -31,6 +32,7 @@ void cpufreq_task_times_remove_uids(uid_t uid_start, uid_t uid_end); int single_uid_time_in_state_open(struct inode *inode, struct file *file); #else static inline void cpufreq_task_times_init(struct task_struct *p) {} +static inline void cpufreq_task_times_alloc(struct task_struct *p) {} static inline void cpufreq_task_times_exit(struct task_struct *p) {} static inline void cpufreq_acct_update_power(struct task_struct *p, u64 cputime) {} diff --git a/kernel/exit.c b/kernel/exit.c index 2d95762343af..0e21e6d21f35 100644 --- a/kernel/exit.c +++ b/kernel/exit.c @@ -62,7 +62,6 @@ #include #include #include -#include #include #include @@ -187,8 +186,6 @@ void release_task(struct task_struct *p) { struct task_struct *leader; int zap_leader; - - cpufreq_task_times_exit(p); repeat: /* don't need to get the RCU readlock here - the process is dead and * can't be modifying its own credentials. But shut RCU-lockdep up */ diff --git a/kernel/fork.c b/kernel/fork.c index f0b58479534f..65acaa274a54 100644 --- a/kernel/fork.c +++ b/kernel/fork.c @@ -91,6 +91,7 @@ #include #include #include +#include #include #include @@ -394,6 +395,8 @@ void put_task_stack(struct task_struct *tsk) void free_task(struct task_struct *tsk) { + cpufreq_task_times_exit(tsk); + #ifndef CONFIG_THREAD_INFO_IN_TASK /* * The task is finally done with both the stack and thread_info, @@ -1708,6 +1711,8 @@ static __latent_entropy struct task_struct *copy_process( if (!p) goto fork_out; + cpufreq_task_times_init(p); + /* * This _must_ happen before we call free_task(), i.e. before we jump * to any of the bad_fork_* labels. This is to avoid freeing @@ -2170,6 +2175,8 @@ long _do_fork(unsigned long clone_flags, if (IS_ERR(p)) return PTR_ERR(p); + cpufreq_task_times_alloc(p); + /* * Do this prior waking up the new thread - the thread pointer * might get invalid after that point, if the thread exits quickly. diff --git a/kernel/sched/core.c b/kernel/sched/core.c index 623c6c5c941a..ad97f3ba5ec5 100644 --- a/kernel/sched/core.c +++ b/kernel/sched/core.c @@ -6,7 +6,6 @@ * Copyright (C) 1991-2002 Linus Torvalds */ #include "sched.h" -#include #include @@ -2157,8 +2156,6 @@ static void __sched_fork(unsigned long clone_flags, struct task_struct *p) memset(&p->se.statistics, 0, sizeof(p->se.statistics)); #endif - cpufreq_task_times_init(p); - RB_CLEAR_NODE(&p->dl.rb_node); init_dl_task_timer(&p->dl); init_dl_inactive_task_timer(&p->dl); From 9323f94fb702d311eb7c18818ba2e508addea151 Mon Sep 17 00:00:00 2001 From: Daniel Rosenberg Date: Mon, 29 May 2017 16:38:16 -0700 Subject: [PATCH 0434/1103] ANDROID: mnt: Fix next_descendent next_descendent did not properly handle the case where the initial mount had no slaves. In this case, we would look for the next slave, but since don't have a master, the check for wrapping around to the start of the list will always fail. Instead, we check for this case, and ensure that we end the iteration when we come back to the root. Signed-off-by: Daniel Rosenberg Bug: 62094374 Change-Id: I43dfcee041aa3730cb4b9a1161418974ef84812e --- fs/pnode.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/fs/pnode.c b/fs/pnode.c index 386884dd6d97..56f9a28a688b 100644 --- a/fs/pnode.c +++ b/fs/pnode.c @@ -616,9 +616,14 @@ static struct mount *next_descendent(struct mount *root, struct mount *cur) if (!IS_MNT_NEW(cur) && !list_empty(&cur->mnt_slave_list)) return first_slave(cur); do { - if (cur->mnt_slave.next != &cur->mnt_master->mnt_slave_list) - return next_slave(cur); - cur = cur->mnt_master; + struct mount *master = cur->mnt_master; + + if (!master || cur->mnt_slave.next != &master->mnt_slave_list) { + struct mount *next = next_slave(cur); + + return (next == root) ? NULL : next; + } + cur = master; } while (cur != root); return NULL; } From b9391675d2bf3f50e1a87c588a8bba518fe7a903 Mon Sep 17 00:00:00 2001 From: Pavankumar Kondeti Date: Wed, 21 Jun 2017 09:22:45 +0530 Subject: [PATCH 0435/1103] ANDROID: uid_sys_stats: Replace tasklist lock with RCU in uid_cputime_show Tasklist lock is acuquired in uid_cputime_show for updating the stats for all tasks in the system. This can potentially disable preemption for several milli seconds. Replace tasklist_lock with RCU read side primitives. Change-Id: Ife69cb577bfdceaae6eb21b9bda09a0fe687e140 Signed-off-by: Pavankumar Kondeti --- drivers/misc/uid_sys_stats.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/misc/uid_sys_stats.c b/drivers/misc/uid_sys_stats.c index fbf7db598d55..88dc1cd3a204 100644 --- a/drivers/misc/uid_sys_stats.c +++ b/drivers/misc/uid_sys_stats.c @@ -346,13 +346,13 @@ static int uid_cputime_show(struct seq_file *m, void *v) uid_entry->active_utime = 0; } - read_lock(&tasklist_lock); + rcu_read_lock(); do_each_thread(temp, task) { uid = from_kuid_munged(user_ns, task_uid(task)); if (!uid_entry || uid_entry->uid != uid) uid_entry = find_or_register_uid(uid); if (!uid_entry) { - read_unlock(&tasklist_lock); + rcu_read_unlock(); rt_mutex_unlock(&uid_lock); pr_err("%s: failed to find the uid_entry for uid %d\n", __func__, uid); @@ -362,7 +362,7 @@ static int uid_cputime_show(struct seq_file *m, void *v) uid_entry->active_utime += utime; uid_entry->active_stime += stime; } while_each_thread(temp, task); - read_unlock(&tasklist_lock); + rcu_read_unlock(); hash_for_each(hash_table, bkt, uid_entry, hash) { u64 total_utime = uid_entry->utime + From 7243211fdac9cece36b40fe2006cb23e4d4a67f7 Mon Sep 17 00:00:00 2001 From: Sandeep Patil Date: Wed, 18 Jul 2018 07:16:42 -0700 Subject: [PATCH 0436/1103] ANDROID: verity: fix android-verity Kconfig dependencies Bug: 72722987 Test: Android verity now shows up in 'make menuconfig' Change-Id: I21c2f36c17f45e5eb0daa1257f5817f9d56527e7 Signed-off-by: Sandeep Patil --- drivers/md/Kconfig | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/drivers/md/Kconfig b/drivers/md/Kconfig index 8a258c688a07..49ea175c6d38 100644 --- a/drivers/md/Kconfig +++ b/drivers/md/Kconfig @@ -576,10 +576,11 @@ config DM_ZONED config DM_ANDROID_VERITY bool "Android verity target support" + depends on BLK_DEV_DM depends on DM_VERITY depends on X509_CERTIFICATE_PARSER depends on SYSTEM_TRUSTED_KEYRING - depends on PUBLIC_KEY_ALGO_RSA + depends on CRYPTO_RSA depends on KEYS depends on ASYMMETRIC_KEY_TYPE depends on ASYMMETRIC_PUBLIC_KEY_SUBTYPE @@ -594,8 +595,8 @@ config DM_ANDROID_VERITY config DM_ANDROID_VERITY_AT_MOST_ONCE_DEFAULT_ENABLED bool "Verity will validate blocks at most once" - depends on DM_VERITY - ---help--- + depends on DM_VERITY + ---help--- Default enables at_most_once option for dm-verity Verify data blocks only the first time they are read from the From 975624aa81504e8a0a7335f0f3ad39c8241f1cdd Mon Sep 17 00:00:00 2001 From: Sandeep Patil Date: Tue, 24 Jul 2018 09:40:07 -0700 Subject: [PATCH 0437/1103] ANDROID: android-verity: Add API to verify signature with builtin keys. The builtin keyring was exported prior to this which allowed android-verity to simply lookup the key in the builtin keyring and verify the signature of the verity metadata. This is now broken as the kernel expects the signature to be in pkcs#7 format (same used for module signing). Obviously, this doesn't work with the verity metadata as we just append the raw signature in the metadata .. sigh. *This one time*, add an API to accept arbitrary signature and verify that with a key from system's trusted keyring. Bug: 72722987 Test: $ adb push verity_fs.img /data/local/tmp/ $ adb root && adb shell > cd /data/local/tmp > losetup /dev/block/loop0 verity_fs.img > dmctl create verity-fs android-verity 0 4200 Android:#7e4333f9bba00adfe0ede979e28ed1920492b40f 7:0 > mount -t ext4 /dev/block/dm-0 temp/ > cat temp/foo.txt temp/bar.txt Change-Id: I0c14f3cb2b587b73a4c75907367769688756213e Signed-off-by: Sandeep Patil --- certs/system_keyring.c | 43 +++++++++++++++++++++++++++++++++++- include/linux/verification.h | 8 +++++-- 2 files changed, 48 insertions(+), 3 deletions(-) diff --git a/certs/system_keyring.c b/certs/system_keyring.c index 81728717523d..4ba922ff3db6 100644 --- a/certs/system_keyring.c +++ b/certs/system_keyring.c @@ -264,5 +264,46 @@ int verify_pkcs7_signature(const void *data, size_t len, return ret; } EXPORT_SYMBOL_GPL(verify_pkcs7_signature); - #endif /* CONFIG_SYSTEM_DATA_VERIFICATION */ + +/** + * verify_signature_one - Verify a signature with keys from given keyring + * @sig: The signature to be verified + * @trusted_keys: Trusted keys to use (NULL for builtin trusted keys only, + * (void *)1UL for all trusted keys). + * @keyid: key description (not partial) + */ +int verify_signature_one(const struct public_key_signature *sig, + struct key *trusted_keys, const char *keyid) +{ + key_ref_t ref; + struct key *key; + int ret; + + if (!sig) + return -EBADMSG; + if (!trusted_keys) { + trusted_keys = builtin_trusted_keys; + } else if (trusted_keys == (void *)1UL) { +#ifdef CONFIG_SECONDARY_TRUSTED_KEYRING + trusted_keys = secondary_trusted_keys; +#else + trusted_keys = builtin_trusted_keys; +#endif + } + + ref = keyring_search(make_key_ref(trusted_keys, 1), + &key_type_asymmetric, keyid); + if (IS_ERR(ref)) { + pr_err("Asymmetric key (%s) not found in keyring(%s)\n", + keyid, trusted_keys->description); + return -ENOKEY; + } + + key = key_ref_to_ptr(ref); + ret = verify_signature(key, sig); + key_put(key); + return ret; +} +EXPORT_SYMBOL_GPL(verify_signature_one); + diff --git a/include/linux/verification.h b/include/linux/verification.h index cfa4730d607a..60ea906b603f 100644 --- a/include/linux/verification.h +++ b/include/linux/verification.h @@ -32,9 +32,13 @@ enum key_being_used_for { }; extern const char *const key_being_used_for[NR__KEY_BEING_USED_FOR]; -#ifdef CONFIG_SYSTEM_DATA_VERIFICATION - struct key; +struct public_key_signature; + +extern int verify_signature_one(const struct public_key_signature *sig, + struct key *trusted_keys, const char *keyid); + +#ifdef CONFIG_SYSTEM_DATA_VERIFICATION extern int verify_pkcs7_signature(const void *data, size_t len, const void *raw_pkcs7, size_t pkcs7_len, From 3645039ba3d66439f27063b0f8071cc53a85be03 Mon Sep 17 00:00:00 2001 From: Sandeep Patil Date: Mon, 23 Jul 2018 16:31:32 -0700 Subject: [PATCH 0438/1103] ANDROID: android-verity: Make it work with newer kernels Fixed bio API calls as they changed from 4.4 to 4.9. Fixed the driver to use the new verify_signature_one() API. Remove the dead code resulted from the rebase. Bug: 72722987 Test: Build and boot hikey with system partition mounted as root using android-verity Signed-off-by: Sandeep Patil Change-Id: I1e29111d57b62f0451404c08d49145039dd00737 --- drivers/md/dm-android-verity.c | 194 +++++++++++++++------------------ drivers/md/dm-android-verity.h | 5 +- 2 files changed, 88 insertions(+), 111 deletions(-) diff --git a/drivers/md/dm-android-verity.c b/drivers/md/dm-android-verity.c index 0dd69244f77c..ce8dcfdee264 100644 --- a/drivers/md/dm-android-verity.c +++ b/drivers/md/dm-android-verity.c @@ -33,6 +33,7 @@ #include #include +#include #include #include #include @@ -122,75 +123,6 @@ static inline bool is_unlocked(void) return !strncmp(verifiedbootstate, unlocked, sizeof(unlocked)); } -static int table_extract_mpi_array(struct public_key_signature *pks, - const void *data, size_t len) -{ - MPI mpi = mpi_read_raw_data(data, len); - - if (!mpi) { - DMERR("Error while allocating mpi array"); - return -ENOMEM; - } - - pks->mpi[0] = mpi; - pks->nr_mpi = 1; - return 0; -} - -static struct public_key_signature *table_make_digest( - enum hash_algo hash, - const void *table, - unsigned long table_len) -{ - struct public_key_signature *pks = NULL; - struct crypto_shash *tfm; - struct shash_desc *desc; - size_t digest_size, desc_size; - int ret; - - /* Allocate the hashing algorithm we're going to need and find out how - * big the hash operational data will be. - */ - tfm = crypto_alloc_shash(hash_algo_name[hash], 0, 0); - if (IS_ERR(tfm)) - return ERR_CAST(tfm); - - desc_size = crypto_shash_descsize(tfm) + sizeof(*desc); - digest_size = crypto_shash_digestsize(tfm); - - /* We allocate the hash operational data storage on the end of out - * context data and the digest output buffer on the end of that. - */ - ret = -ENOMEM; - pks = kzalloc(digest_size + sizeof(*pks) + desc_size, GFP_KERNEL); - if (!pks) - goto error; - - pks->pkey_hash_algo = hash; - pks->digest = (u8 *)pks + sizeof(*pks) + desc_size; - pks->digest_size = digest_size; - - desc = (struct shash_desc *)(pks + 1); - desc->tfm = tfm; - desc->flags = CRYPTO_TFM_REQ_MAY_SLEEP; - - ret = crypto_shash_init(desc); - if (ret < 0) - goto error; - - ret = crypto_shash_finup(desc, table, table_len, pks->digest); - if (ret < 0) - goto error; - - crypto_free_shash(tfm); - return pks; - -error: - kfree(pks); - crypto_free_shash(tfm); - return ERR_PTR(ret); -} - static int read_block_dev(struct bio_read *payload, struct block_device *bdev, sector_t offset, int length) { @@ -205,8 +137,9 @@ static int read_block_dev(struct bio_read *payload, struct block_device *bdev, return -ENOMEM; } - bio->bi_bdev = bdev; + bio_set_dev(bio, bdev); bio->bi_iter.bi_sector = offset; + bio_set_op_attrs(bio, REQ_OP_READ, 0); payload->page_io = kzalloc(sizeof(struct page *) * payload->number_of_pages, GFP_KERNEL); @@ -230,7 +163,7 @@ static int read_block_dev(struct bio_read *payload, struct block_device *bdev, } } - if (!submit_bio_wait(READ, bio)) + if (!submit_bio_wait(bio)) /* success */ goto free_bio; DMERR("bio read failed"); @@ -567,28 +500,85 @@ static int verity_mode(void) return DM_VERITY_MODE_EIO; } +static void handle_error(void) +{ + int mode = verity_mode(); + if (mode == DM_VERITY_MODE_RESTART) { + DMERR("triggering restart"); + kernel_restart("dm-verity device corrupted"); + } else { + DMERR("Mounting verity root failed"); + } +} + +static struct public_key_signature *table_make_digest( + enum hash_algo hash, + const void *table, + unsigned long table_len) +{ + struct public_key_signature *pks = NULL; + struct crypto_shash *tfm; + struct shash_desc *desc; + size_t digest_size, desc_size; + int ret; + + /* Allocate the hashing algorithm we're going to need and find out how + * big the hash operational data will be. + */ + tfm = crypto_alloc_shash(hash_algo_name[hash], 0, 0); + if (IS_ERR(tfm)) + return ERR_CAST(tfm); + + desc_size = crypto_shash_descsize(tfm) + sizeof(*desc); + digest_size = crypto_shash_digestsize(tfm); + + /* We allocate the hash operational data storage on the end of out + * context data and the digest output buffer on the end of that. + */ + ret = -ENOMEM; + pks = kzalloc(digest_size + sizeof(*pks) + desc_size, GFP_KERNEL); + if (!pks) + goto error; + + pks->pkey_algo = "rsa"; + pks->hash_algo = hash_algo_name[hash]; + pks->digest = (u8 *)pks + sizeof(*pks) + desc_size; + pks->digest_size = digest_size; + + desc = (struct shash_desc *)(pks + 1); + desc->tfm = tfm; + desc->flags = CRYPTO_TFM_REQ_MAY_SLEEP; + + ret = crypto_shash_init(desc); + if (ret < 0) + goto error; + + ret = crypto_shash_finup(desc, table, table_len, pks->digest); + if (ret < 0) + goto error; + + crypto_free_shash(tfm); + return pks; + +error: + kfree(pks); + crypto_free_shash(tfm); + return ERR_PTR(ret); +} + + static int verify_verity_signature(char *key_id, struct android_metadata *metadata) { - key_ref_t key_ref; - struct key *key; struct public_key_signature *pks = NULL; int retval = -EINVAL; - key_ref = keyring_search(make_key_ref(system_trusted_keyring, 1), - &key_type_asymmetric, key_id); - - if (IS_ERR(key_ref)) { - DMERR("keyring: key not found"); - return -ENOKEY; - } - - key = key_ref_to_ptr(key_ref); + if (!key_id) + goto error; pks = table_make_digest(HASH_ALGO_SHA256, (const void *)metadata->verity_table, le32_to_cpu(metadata->header->table_length)); - if (IS_ERR(pks)) { DMERR("hashing failed"); retval = PTR_ERR(pks); @@ -596,33 +586,20 @@ static int verify_verity_signature(char *key_id, goto error; } - retval = table_extract_mpi_array(pks, &metadata->header->signature[0], - RSANUMBYTES); - if (retval < 0) { - DMERR("Error extracting mpi %d", retval); + pks->s = kmemdup(&metadata->header->signature[0], RSANUMBYTES, GFP_KERNEL); + if (!pks->s) { + DMERR("Error allocating memory for signature"); goto error; } + pks->s_size = RSANUMBYTES; - retval = verify_signature(key, pks); - mpi_free(pks->rsa.s); + retval = verify_signature_one(pks, NULL, key_id); + kfree(pks->s); error: kfree(pks); - key_put(key); - return retval; } -static void handle_error(void) -{ - int mode = verity_mode(); - if (mode == DM_VERITY_MODE_RESTART) { - DMERR("triggering restart"); - kernel_restart("dm-verity device corrupted"); - } else { - DMERR("Mounting verity root failed"); - } -} - static inline bool test_mult_overflow(sector_t a, u32 b) { sector_t r = (sector_t)~0ULL; @@ -696,8 +673,8 @@ static int android_verity_ctr(struct dm_target *ti, unsigned argc, char **argv) dev_t uninitialized_var(dev); struct android_metadata *metadata = NULL; int err = 0, i, mode; - char *key_id, *table_ptr, dummy, *target_device, - *verity_table_args[VERITY_TABLE_ARGS + 2 + VERITY_TABLE_OPT_FEC_ARGS]; + char *key_id = NULL, *table_ptr, dummy, *target_device; + char *verity_table_args[VERITY_TABLE_ARGS + 2 + VERITY_TABLE_OPT_FEC_ARGS]; /* One for specifying number of opt args and one for mode */ sector_t data_sectors; u32 data_block_size; @@ -879,12 +856,11 @@ static int android_verity_ctr(struct dm_target *ti, unsigned argc, char **argv) } err = verity_ctr(ti, no_of_args, verity_table_args); - - if (err) - DMERR("android-verity failed to mount as verity target"); - else { + if (err) { + DMERR("android-verity failed to create a verity target"); + } else { target_added = true; - DMINFO("android-verity mounted as verity target"); + DMINFO("android-verity created as verity target"); } free_metadata: diff --git a/drivers/md/dm-android-verity.h b/drivers/md/dm-android-verity.h index a637accefb5b..8f6f5e777187 100644 --- a/drivers/md/dm-android-verity.h +++ b/drivers/md/dm-android-verity.h @@ -120,8 +120,9 @@ extern int dm_linear_iterate_devices(struct dm_target *ti, iterate_devices_callout_fn fn, void *data); extern int dm_linear_ctr(struct dm_target *ti, unsigned int argc, char **argv); #if IS_ENABLED(CONFIG_DAX_DRIVER) -extern long dm_linear_dax_direct_access(struct dm_target *ti, sector_t sector, - void **kaddr, pfn_t *pfn, long size); +extern long dm_linear_dax_direct_access(struct dm_target *ti, pgoff_t pgoff, + long nr_pages, void **kaddr, + pfn_t *pfn); extern size_t dm_linear_dax_copy_from_iter(struct dm_target *ti, pgoff_t pgoff, void *addr, size_t bytes, struct iov_iter *i); #else From e8596f6db9d6f62a9390466f6a655a8efd57a1a7 Mon Sep 17 00:00:00 2001 From: Sandeep Patil Date: Tue, 24 Jul 2018 16:59:40 -0700 Subject: [PATCH 0439/1103] ANDROID: android-verity: Fix broken parameter handling. android-verity documentation states that the target expectets the key, followed by the backing device on the commandline as follows "dm=system none ro,0 1 android-verity " However, the code actually expects the backing device as the first parameter. Fix that. Bug: 72722987 Change-Id: Ibd56c0220f6003bdfb95aa2d611f787e75a65c97 Signed-off-by: Sandeep Patil --- drivers/md/dm-android-verity.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/md/dm-android-verity.c b/drivers/md/dm-android-verity.c index ce8dcfdee264..20e05936551f 100644 --- a/drivers/md/dm-android-verity.c +++ b/drivers/md/dm-android-verity.c @@ -693,16 +693,16 @@ static int android_verity_ctr(struct dm_target *ti, unsigned argc, char **argv) handle_error(); return -EINVAL; } - } else if (argc == 2) - key_id = argv[1]; - else { + target_device = argv[0]; + } else if (argc == 2) { + key_id = argv[0]; + target_device = argv[1]; + } else { DMERR("Incorrect number of arguments"); handle_error(); return -EINVAL; } - target_device = argv[0]; - dev = name_to_dev_t(target_device); if (!dev) { DMERR("no dev found for %s", target_device); From 0103e199af90b94089056d0b32ee8366b7967446 Mon Sep 17 00:00:00 2001 From: Alistair Strachan Date: Wed, 25 Jul 2018 16:11:38 -0700 Subject: [PATCH 0440/1103] x86_64_cuttlefish_defconfig: enable verity cert Bug: 72722987 Test: Build, boot and verify in /proc/keys Change-Id: Ia55b94d56827003a88cb6083a75340ee31347470 Signed-off-by: Alistair Strachan --- arch/x86/configs/x86_64_cuttlefish_defconfig | 5 ++++ verity_dev_keys.x509 | 24 ++++++++++++++++++++ 2 files changed, 29 insertions(+) create mode 100644 verity_dev_keys.x509 diff --git a/arch/x86/configs/x86_64_cuttlefish_defconfig b/arch/x86/configs/x86_64_cuttlefish_defconfig index 26a42b91140b..e33b351b1538 100644 --- a/arch/x86/configs/x86_64_cuttlefish_defconfig +++ b/arch/x86/configs/x86_64_cuttlefish_defconfig @@ -450,3 +450,8 @@ CONFIG_SECURITY_SELINUX_CHECKREQPROT_VALUE=1 # CONFIG_CRYPTO_MANAGER_DISABLE_TESTS is not set CONFIG_CRYPTO_SHA512=y CONFIG_CRYPTO_DEV_VIRTIO=y +CONFIG_ASYMMETRIC_KEY_TYPE=y +CONFIG_ASYMMETRIC_PUBLIC_KEY_SUBTYPE=y +CONFIG_X509_CERTIFICATE_PARSER=y +CONFIG_SYSTEM_TRUSTED_KEYRING=y +CONFIG_SYSTEM_TRUSTED_KEYS="verity_dev_keys.x509" diff --git a/verity_dev_keys.x509 b/verity_dev_keys.x509 new file mode 100644 index 000000000000..86399c3c1dd7 --- /dev/null +++ b/verity_dev_keys.x509 @@ -0,0 +1,24 @@ +-----BEGIN CERTIFICATE----- +MIID/TCCAuWgAwIBAgIJAJcPmDkJqolJMA0GCSqGSIb3DQEBBQUAMIGUMQswCQYD +VQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTEWMBQGA1UEBwwNTW91bnRhaW4g +VmlldzEQMA4GA1UECgwHQW5kcm9pZDEQMA4GA1UECwwHQW5kcm9pZDEQMA4GA1UE +AwwHQW5kcm9pZDEiMCAGCSqGSIb3DQEJARYTYW5kcm9pZEBhbmRyb2lkLmNvbTAe +Fw0xNDExMDYxOTA3NDBaFw00MjAzMjQxOTA3NDBaMIGUMQswCQYDVQQGEwJVUzET +MBEGA1UECAwKQ2FsaWZvcm5pYTEWMBQGA1UEBwwNTW91bnRhaW4gVmlldzEQMA4G +A1UECgwHQW5kcm9pZDEQMA4GA1UECwwHQW5kcm9pZDEQMA4GA1UEAwwHQW5kcm9p +ZDEiMCAGCSqGSIb3DQEJARYTYW5kcm9pZEBhbmRyb2lkLmNvbTCCASIwDQYJKoZI +hvcNAQEBBQADggEPADCCAQoCggEBAOjreE0vTVSRenuzO9vnaWfk0eQzYab0gqpi +6xAzi6dmD+ugoEKJmbPiuE5Dwf21isZ9uhUUu0dQM46dK4ocKxMRrcnmGxydFn6o +fs3ODJMXOkv2gKXL/FdbEPdDbxzdu8z3yk+W67udM/fW7WbaQ3DO0knu+izKak/3 +T41c5uoXmQ81UNtAzRGzGchNVXMmWuTGOkg6U+0I2Td7K8yvUMWhAWPPpKLtVH9r +AL5TzjYNR92izdKcz3AjRsI3CTjtpiVABGeX0TcjRSuZB7K9EK56HV+OFNS6I1NP +jdD7FIShyGlqqZdUOkAUZYanbpgeT5N7QL6uuqcGpoTOkalu6kkCAwEAAaNQME4w +HQYDVR0OBBYEFH5DM/m7oArf4O3peeKO0ZIEkrQPMB8GA1UdIwQYMBaAFH5DM/m7 +oArf4O3peeKO0ZIEkrQPMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQADggEB +AHO3NSvDE5jFvMehGGtS8BnFYdFKRIglDMc4niWSzhzOVYRH4WajxdtBWc5fx0ix +NF/+hVKVhP6AIOQa+++sk+HIi7RvioPPbhjcsVlZe7cUEGrLSSveGouQyc+j0+m6 +JF84kszIl5GGNMTnx0XRPO+g8t6h5LWfnVydgZfpGRRg+WHewk1U2HlvTjIceb0N +dcoJ8WKJAFWdcuE7VIm4w+vF/DYX/A2Oyzr2+QRhmYSv1cusgAeC1tvH4ap+J1Lg +UnOu5Kh/FqPLLSwNVQp4Bu7b9QFfqK8Moj84bj88NqRGZgDyqzuTrFxn6FW7dmyA +yttuAJAEAymk1mipd9+zp38= +-----END CERTIFICATE----- From 5fcb1522090eee9f09c11dc07f541c0ec7683a07 Mon Sep 17 00:00:00 2001 From: Alistair Strachan Date: Wed, 25 Jul 2018 16:11:09 -0700 Subject: [PATCH 0441/1103] x86_64_cuttlefish_defconfig: Enable android-verity Bug: 72722987 Test: Build & boot with x86_64_cuttlefish_defconfig Change-Id: I961e6aaa944b5ab0c005cb39604a52f8dc98fb06 Signed-off-by: Alistair Strachan --- arch/x86/configs/x86_64_cuttlefish_defconfig | 3 +++ 1 file changed, 3 insertions(+) diff --git a/arch/x86/configs/x86_64_cuttlefish_defconfig b/arch/x86/configs/x86_64_cuttlefish_defconfig index e33b351b1538..19e3a812306b 100644 --- a/arch/x86/configs/x86_64_cuttlefish_defconfig +++ b/arch/x86/configs/x86_64_cuttlefish_defconfig @@ -219,7 +219,9 @@ CONFIG_DM_MIRROR=y CONFIG_DM_ZERO=y CONFIG_DM_UEVENT=y CONFIG_DM_VERITY=y +CONFIG_DM_VERITY_HASH_PREFETCH_MIN_SIZE=1 CONFIG_DM_VERITY_FEC=y +CONFIG_DM_ANDROID_VERITY=y CONFIG_NETDEVICES=y CONFIG_NETCONSOLE=y CONFIG_NETCONSOLE_DYNAMIC=y @@ -447,6 +449,7 @@ CONFIG_SECURITY_PATH=y CONFIG_HARDENED_USERCOPY=y CONFIG_SECURITY_SELINUX=y CONFIG_SECURITY_SELINUX_CHECKREQPROT_VALUE=1 +CONFIG_CRYPTO_RSA=y # CONFIG_CRYPTO_MANAGER_DISABLE_TESTS is not set CONFIG_CRYPTO_SHA512=y CONFIG_CRYPTO_DEV_VIRTIO=y From b1af58192f30690e068f58e1fff8d022c24c3faa Mon Sep 17 00:00:00 2001 From: Alistair Strachan Date: Fri, 27 Jul 2018 09:18:28 -0700 Subject: [PATCH 0442/1103] ANDROID: verity: really fix android-verity Kconfig The change "ANDROID: verity: fix android-verity Kconfig dependencies" relaxed the dependency on DM_VERITY=y to just DM_VERITY, but this is not correct because there are parts of the verity and dm-mod API that android-verity is using but which are not exported to modules. Work around this problem by disallowing android-verity to be built-in when the dm/verity core is built modularly. Bug: 72722987 Change-Id: I3cfaa2acca8e4a4b5c459afdddd958ac9f8c1eb3 Signed-off-by: Alistair Strachan --- drivers/md/Kconfig | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/md/Kconfig b/drivers/md/Kconfig index 49ea175c6d38..be6b64ce3881 100644 --- a/drivers/md/Kconfig +++ b/drivers/md/Kconfig @@ -576,8 +576,8 @@ config DM_ZONED config DM_ANDROID_VERITY bool "Android verity target support" - depends on BLK_DEV_DM - depends on DM_VERITY + depends on BLK_DEV_DM=y + depends on DM_VERITY=y depends on X509_CERTIFICATE_PARSER depends on SYSTEM_TRUSTED_KEYRING depends on CRYPTO_RSA From c1427506d2b9cfe55c8bd08231d1a659951328e8 Mon Sep 17 00:00:00 2001 From: Daniel Rosenberg Date: Thu, 26 Jul 2018 16:32:09 -0700 Subject: [PATCH 0443/1103] ANDROID: sdcardfs: Check stacked filesystem depth bug: 111860541 Change-Id: Ia0a30b2b8956c4ada28981584cd8647713a1e993 Signed-off-by: Daniel Rosenberg --- fs/sdcardfs/main.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/fs/sdcardfs/main.c b/fs/sdcardfs/main.c index 30e0c431a1ea..27ec726e7a46 100644 --- a/fs/sdcardfs/main.c +++ b/fs/sdcardfs/main.c @@ -295,6 +295,13 @@ static int sdcardfs_read_super(struct vfsmount *mnt, struct super_block *sb, atomic_inc(&lower_sb->s_active); sdcardfs_set_lower_super(sb, lower_sb); + sb->s_stack_depth = lower_sb->s_stack_depth + 1; + if (sb->s_stack_depth > FILESYSTEM_MAX_STACK_DEPTH) { + pr_err("sdcardfs: maximum fs stacking depth exceeded\n"); + err = -EINVAL; + goto out_sput; + } + /* inherit maxbytes from lower file system */ sb->s_maxbytes = lower_sb->s_maxbytes; From 6fdffd71a28fcf3ba732b60515c29148e5046536 Mon Sep 17 00:00:00 2001 From: Sami Tolvanen Date: Thu, 10 May 2018 14:56:41 -0700 Subject: [PATCH 0444/1103] ANDROID: ftrace: fix function type mismatches This change fixes indirect call mismatches with function and function graph tracing, which trip Control-Flow Integrity (CFI) checking. Bug: 79510107 Bug: 67506682 Change-Id: I5de08c113fb970ffefedce93c58e0161f22c7ca2 Signed-off-by: Sami Tolvanen --- include/linux/ftrace.h | 8 ++++++++ kernel/trace/ftrace.c | 17 +++++++++++------ 2 files changed, 19 insertions(+), 6 deletions(-) diff --git a/include/linux/ftrace.h b/include/linux/ftrace.h index a397907e8d72..8ff2bfb22ecf 100644 --- a/include/linux/ftrace.h +++ b/include/linux/ftrace.h @@ -224,8 +224,16 @@ extern enum ftrace_tracing_type_t ftrace_tracing_type; int register_ftrace_function(struct ftrace_ops *ops); int unregister_ftrace_function(struct ftrace_ops *ops); +#ifdef CONFIG_CFI_CLANG +/* Use a C stub with the correct type for CFI */ +static inline void ftrace_stub(unsigned long a0, unsigned long a1, + struct ftrace_ops *op, struct pt_regs *regs) +{ +} +#else extern void ftrace_stub(unsigned long a0, unsigned long a1, struct ftrace_ops *op, struct pt_regs *regs); +#endif #else /* !CONFIG_FUNCTION_TRACER */ /* diff --git a/kernel/trace/ftrace.c b/kernel/trace/ftrace.c index f536f601bd46..40aec2d6051b 100644 --- a/kernel/trace/ftrace.c +++ b/kernel/trace/ftrace.c @@ -123,8 +123,9 @@ static void ftrace_ops_list_func(unsigned long ip, unsigned long parent_ip, struct ftrace_ops *op, struct pt_regs *regs); #else /* See comment below, where ftrace_ops_list_func is defined */ -static void ftrace_ops_no_ops(unsigned long ip, unsigned long parent_ip); -#define ftrace_ops_list_func ((ftrace_func_t)ftrace_ops_no_ops) +static void ftrace_ops_no_ops(unsigned long ip, unsigned long parent_ip, + struct ftrace_ops *op, struct pt_regs *regs); +#define ftrace_ops_list_func ftrace_ops_no_ops #endif /* @@ -6310,7 +6311,8 @@ static void ftrace_ops_list_func(unsigned long ip, unsigned long parent_ip, __ftrace_ops_list_func(ip, parent_ip, NULL, regs); } #else -static void ftrace_ops_no_ops(unsigned long ip, unsigned long parent_ip) +static void ftrace_ops_no_ops(unsigned long ip, unsigned long parent_ip, + struct ftrace_ops *op, struct pt_regs *regs) { __ftrace_ops_list_func(ip, parent_ip, NULL, NULL); } @@ -6771,14 +6773,17 @@ void ftrace_graph_graph_time_control(bool enable) fgraph_graph_time = enable; } +void ftrace_graph_return_stub(struct ftrace_graph_ret *trace) +{ +} + int ftrace_graph_entry_stub(struct ftrace_graph_ent *trace) { return 0; } /* The callbacks that hook a function */ -trace_func_graph_ret_t ftrace_graph_return = - (trace_func_graph_ret_t)ftrace_stub; +trace_func_graph_ret_t ftrace_graph_return = ftrace_graph_return_stub; trace_func_graph_ent_t ftrace_graph_entry = ftrace_graph_entry_stub; static trace_func_graph_ent_t __ftrace_graph_entry = ftrace_graph_entry_stub; @@ -7007,7 +7012,7 @@ void unregister_ftrace_graph(void) goto out; ftrace_graph_active--; - ftrace_graph_return = (trace_func_graph_ret_t)ftrace_stub; + ftrace_graph_return = ftrace_graph_return_stub; ftrace_graph_entry = ftrace_graph_entry_stub; __ftrace_graph_entry = ftrace_graph_entry_stub; ftrace_shutdown(&graph_ops, FTRACE_STOP_FUNC_RET); From e3975aa0eb35eb2a449510988200d837a088ded1 Mon Sep 17 00:00:00 2001 From: Steve Muckle Date: Fri, 20 Jul 2018 09:52:12 -0700 Subject: [PATCH 0445/1103] ANDROID: remove android config fragments The authoritative versions of the android kconfig fragments were moved into a separate repository some time back: https://android.googlesource.com/kernel/configs/ Change-Id: I1fc770b4f040de983c6b7a041502aef864c86cff Signed-off-by: Steve Muckle --- kernel/configs/android-base.config | 161 ---------------------- kernel/configs/android-recommended.config | 129 ----------------- 2 files changed, 290 deletions(-) delete mode 100644 kernel/configs/android-base.config delete mode 100644 kernel/configs/android-recommended.config diff --git a/kernel/configs/android-base.config b/kernel/configs/android-base.config deleted file mode 100644 index d3fd428f4b92..000000000000 --- a/kernel/configs/android-base.config +++ /dev/null @@ -1,161 +0,0 @@ -# KEEP ALPHABETICALLY SORTED -# CONFIG_DEVKMEM is not set -# CONFIG_DEVMEM is not set -# CONFIG_FHANDLE is not set -# CONFIG_INET_LRO is not set -# CONFIG_NFSD is not set -# CONFIG_NFS_FS is not set -# CONFIG_OABI_COMPAT is not set -# CONFIG_SYSVIPC is not set -# CONFIG_USELIB is not set -CONFIG_ANDROID=y -CONFIG_ANDROID_BINDER_IPC=y -CONFIG_ANDROID_BINDER_DEVICES=binder,hwbinder,vndbinder -CONFIG_ANDROID_LOW_MEMORY_KILLER=y -CONFIG_ARMV8_DEPRECATED=y -CONFIG_ASHMEM=y -CONFIG_AUDIT=y -CONFIG_BLK_DEV_INITRD=y -CONFIG_CGROUPS=y -CONFIG_CGROUP_BPF=y -CONFIG_CGROUP_CPUACCT=y -CONFIG_CGROUP_DEBUG=y -CONFIG_CGROUP_FREEZER=y -CONFIG_CGROUP_SCHED=y -CONFIG_CP15_BARRIER_EMULATION=y -CONFIG_DEFAULT_SECURITY_SELINUX=y -CONFIG_EMBEDDED=y -CONFIG_FB=y -CONFIG_HARDENED_USERCOPY=y -CONFIG_HIGH_RES_TIMERS=y -CONFIG_IKCONFIG=y -CONFIG_IKCONFIG_PROC=y -CONFIG_INET6_AH=y -CONFIG_INET6_ESP=y -CONFIG_INET6_IPCOMP=y -CONFIG_INET=y -CONFIG_INET_DIAG_DESTROY=y -CONFIG_INET_ESP=y -CONFIG_INET_XFRM_MODE_TUNNEL=y -CONFIG_IP6_NF_FILTER=y -CONFIG_IP6_NF_IPTABLES=y -CONFIG_IP6_NF_MANGLE=y -CONFIG_IP6_NF_RAW=y -CONFIG_IP6_NF_TARGET_REJECT=y -CONFIG_IPV6=y -CONFIG_IPV6_MIP6=y -CONFIG_IPV6_MULTIPLE_TABLES=y -CONFIG_IPV6_OPTIMISTIC_DAD=y -CONFIG_IPV6_ROUTER_PREF=y -CONFIG_IPV6_ROUTE_INFO=y -CONFIG_IP_ADVANCED_ROUTER=y -CONFIG_IP_MULTICAST=y -CONFIG_IP_MULTIPLE_TABLES=y -CONFIG_IP_NF_ARPFILTER=y -CONFIG_IP_NF_ARPTABLES=y -CONFIG_IP_NF_ARP_MANGLE=y -CONFIG_IP_NF_FILTER=y -CONFIG_IP_NF_IPTABLES=y -CONFIG_IP_NF_MANGLE=y -CONFIG_IP_NF_MATCH_AH=y -CONFIG_IP_NF_MATCH_ECN=y -CONFIG_IP_NF_MATCH_TTL=y -CONFIG_IP_NF_NAT=y -CONFIG_IP_NF_RAW=y -CONFIG_IP_NF_SECURITY=y -CONFIG_IP_NF_TARGET_MASQUERADE=y -CONFIG_IP_NF_TARGET_NETMAP=y -CONFIG_IP_NF_TARGET_REDIRECT=y -CONFIG_IP_NF_TARGET_REJECT=y -CONFIG_MODULES=y -CONFIG_MODULE_UNLOAD=y -CONFIG_MODVERSIONS=y -CONFIG_NET=y -CONFIG_NETDEVICES=y -CONFIG_NETFILTER=y -CONFIG_NETFILTER_TPROXY=y -CONFIG_NETFILTER_XT_MATCH_COMMENT=y -CONFIG_NETFILTER_XT_MATCH_CONNLIMIT=y -CONFIG_NETFILTER_XT_MATCH_CONNMARK=y -CONFIG_NETFILTER_XT_MATCH_CONNTRACK=y -CONFIG_NETFILTER_XT_MATCH_HASHLIMIT=y -CONFIG_NETFILTER_XT_MATCH_HELPER=y -CONFIG_NETFILTER_XT_MATCH_IPRANGE=y -CONFIG_NETFILTER_XT_MATCH_LENGTH=y -CONFIG_NETFILTER_XT_MATCH_LIMIT=y -CONFIG_NETFILTER_XT_MATCH_MAC=y -CONFIG_NETFILTER_XT_MATCH_MARK=y -CONFIG_NETFILTER_XT_MATCH_PKTTYPE=y -CONFIG_NETFILTER_XT_MATCH_POLICY=y -CONFIG_NETFILTER_XT_MATCH_QUOTA=y -CONFIG_NETFILTER_XT_MATCH_SOCKET=y -CONFIG_NETFILTER_XT_MATCH_STATE=y -CONFIG_NETFILTER_XT_MATCH_STATISTIC=y -CONFIG_NETFILTER_XT_MATCH_STRING=y -CONFIG_NETFILTER_XT_MATCH_TIME=y -CONFIG_NETFILTER_XT_MATCH_U32=y -CONFIG_NETFILTER_XT_TARGET_CLASSIFY=y -CONFIG_NETFILTER_XT_TARGET_CONNMARK=y -CONFIG_NETFILTER_XT_TARGET_CONNSECMARK=y -CONFIG_NETFILTER_XT_TARGET_IDLETIMER=y -CONFIG_NETFILTER_XT_TARGET_MARK=y -CONFIG_NETFILTER_XT_TARGET_NFLOG=y -CONFIG_NETFILTER_XT_TARGET_NFQUEUE=y -CONFIG_NETFILTER_XT_TARGET_SECMARK=y -CONFIG_NETFILTER_XT_TARGET_TCPMSS=y -CONFIG_NETFILTER_XT_TARGET_TPROXY=y -CONFIG_NETFILTER_XT_TARGET_TRACE=y -CONFIG_NET_CLS_ACT=y -CONFIG_NET_CLS_U32=y -CONFIG_NET_EMATCH=y -CONFIG_NET_EMATCH_U32=y -CONFIG_NET_KEY=y -CONFIG_NET_SCHED=y -CONFIG_NET_SCH_HTB=y -CONFIG_NF_CONNTRACK=y -CONFIG_NF_CONNTRACK_AMANDA=y -CONFIG_NF_CONNTRACK_EVENTS=y -CONFIG_NF_CONNTRACK_FTP=y -CONFIG_NF_CONNTRACK_H323=y -CONFIG_NF_CONNTRACK_IPV4=y -CONFIG_NF_CONNTRACK_IPV6=y -CONFIG_NF_CONNTRACK_IRC=y -CONFIG_NF_CONNTRACK_NETBIOS_NS=y -CONFIG_NF_CONNTRACK_PPTP=y -CONFIG_NF_CONNTRACK_SANE=y -CONFIG_NF_CONNTRACK_SECMARK=y -CONFIG_NF_CONNTRACK_TFTP=y -CONFIG_NF_CT_NETLINK=y -CONFIG_NF_CT_PROTO_DCCP=y -CONFIG_NF_CT_PROTO_SCTP=y -CONFIG_NF_CT_PROTO_UDPLITE=y -CONFIG_NF_NAT=y -CONFIG_NO_HZ=y -CONFIG_PACKET=y -CONFIG_PM_AUTOSLEEP=y -CONFIG_PM_WAKELOCKS=y -CONFIG_PPP=y -CONFIG_PPP_BSDCOMP=y -CONFIG_PPP_DEFLATE=y -CONFIG_PPP_MPPE=y -CONFIG_PREEMPT=y -CONFIG_QUOTA=y -CONFIG_RANDOMIZE_BASE=y -CONFIG_RTC_CLASS=y -CONFIG_RT_GROUP_SCHED=y -CONFIG_SECCOMP=y -CONFIG_SECURITY=y -CONFIG_SECURITY_NETWORK=y -CONFIG_SECURITY_SELINUX=y -CONFIG_SETEND_EMULATION=y -CONFIG_STAGING=y -CONFIG_SWP_EMULATION=y -CONFIG_SYNC=y -CONFIG_TUN=y -CONFIG_UNIX=y -CONFIG_USB_GADGET=y -CONFIG_USB_CONFIGFS=y -CONFIG_USB_CONFIGFS_F_FS=y -CONFIG_USB_CONFIGFS_F_MIDI=y -CONFIG_USB_OTG_WAKELOCK=y -CONFIG_XFRM_USER=y diff --git a/kernel/configs/android-recommended.config b/kernel/configs/android-recommended.config deleted file mode 100644 index 81e9af7dcec2..000000000000 --- a/kernel/configs/android-recommended.config +++ /dev/null @@ -1,129 +0,0 @@ -# KEEP ALPHABETICALLY SORTED -# CONFIG_AIO is not set -# CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set -# CONFIG_INPUT_MOUSE is not set -# CONFIG_LEGACY_PTYS is not set -# CONFIG_NF_CONNTRACK_SIP is not set -# CONFIG_PM_WAKELOCKS_GC is not set -# CONFIG_VT is not set -CONFIG_ARM64_SW_TTBR0_PAN=y -CONFIG_BACKLIGHT_LCD_SUPPORT=y -CONFIG_BLK_DEV_DM=y -CONFIG_BLK_DEV_LOOP=y -CONFIG_BLK_DEV_RAM=y -CONFIG_BLK_DEV_RAM_SIZE=8192 -CONFIG_STACKPROTECTOR_STRONG=y -CONFIG_COMPACTION=y -CONFIG_CPU_SW_DOMAIN_PAN=y -CONFIG_DM_CRYPT=y -CONFIG_DM_UEVENT=y -CONFIG_DM_VERITY=y -CONFIG_DM_VERITY_FEC=y -CONFIG_DRAGONRISE_FF=y -CONFIG_ENABLE_DEFAULT_TRACERS=y -CONFIG_EXT4_FS=y -CONFIG_EXT4_FS_SECURITY=y -CONFIG_FUSE_FS=y -CONFIG_GREENASIA_FF=y -CONFIG_HIDRAW=y -CONFIG_HID_A4TECH=y -CONFIG_HID_ACRUX=y -CONFIG_HID_ACRUX_FF=y -CONFIG_HID_APPLE=y -CONFIG_HID_BELKIN=y -CONFIG_HID_CHERRY=y -CONFIG_HID_CHICONY=y -CONFIG_HID_CYPRESS=y -CONFIG_HID_DRAGONRISE=y -CONFIG_HID_ELECOM=y -CONFIG_HID_EMS_FF=y -CONFIG_HID_EZKEY=y -CONFIG_HID_GREENASIA=y -CONFIG_HID_GYRATION=y -CONFIG_HID_HOLTEK=y -CONFIG_HID_KENSINGTON=y -CONFIG_HID_KEYTOUCH=y -CONFIG_HID_KYE=y -CONFIG_HID_LCPOWER=y -CONFIG_HID_LOGITECH=y -CONFIG_HID_LOGITECH_DJ=y -CONFIG_HID_MAGICMOUSE=y -CONFIG_HID_MICROSOFT=y -CONFIG_HID_MONTEREY=y -CONFIG_HID_MULTITOUCH=y -CONFIG_HID_NTRIG=y -CONFIG_HID_ORTEK=y -CONFIG_HID_PANTHERLORD=y -CONFIG_HID_PETALYNX=y -CONFIG_HID_PICOLCD=y -CONFIG_HID_PRIMAX=y -CONFIG_HID_PRODIKEYS=y -CONFIG_HID_ROCCAT=y -CONFIG_HID_SAITEK=y -CONFIG_HID_SAMSUNG=y -CONFIG_HID_SMARTJOYPLUS=y -CONFIG_HID_SONY=y -CONFIG_HID_SPEEDLINK=y -CONFIG_HID_SUNPLUS=y -CONFIG_HID_THRUSTMASTER=y -CONFIG_HID_TIVO=y -CONFIG_HID_TOPSEED=y -CONFIG_HID_TWINHAN=y -CONFIG_HID_UCLOGIC=y -CONFIG_HID_WACOM=y -CONFIG_HID_WALTOP=y -CONFIG_HID_WIIMOTE=y -CONFIG_HID_ZEROPLUS=y -CONFIG_HID_ZYDACRON=y -CONFIG_INPUT_EVDEV=y -CONFIG_INPUT_GPIO=y -CONFIG_INPUT_JOYSTICK=y -CONFIG_INPUT_MISC=y -CONFIG_INPUT_TABLET=y -CONFIG_INPUT_UINPUT=y -CONFIG_ION=y -CONFIG_JOYSTICK_XPAD=y -CONFIG_JOYSTICK_XPAD_FF=y -CONFIG_JOYSTICK_XPAD_LEDS=y -CONFIG_KALLSYMS_ALL=y -CONFIG_KSM=y -CONFIG_LOGIG940_FF=y -CONFIG_LOGIRUMBLEPAD2_FF=y -CONFIG_LOGITECH_FF=y -CONFIG_MD=y -CONFIG_MEDIA_SUPPORT=y -CONFIG_MSDOS_FS=y -CONFIG_PANIC_TIMEOUT=5 -CONFIG_PANTHERLORD_FF=y -CONFIG_PERF_EVENTS=y -CONFIG_PM_DEBUG=y -CONFIG_PM_RUNTIME=y -CONFIG_PM_WAKELOCKS_LIMIT=0 -CONFIG_POWER_SUPPLY=y -CONFIG_PSTORE=y -CONFIG_PSTORE_CONSOLE=y -CONFIG_PSTORE_RAM=y -CONFIG_SCHEDSTATS=y -CONFIG_SMARTJOYPLUS_FF=y -CONFIG_SND=y -CONFIG_SOUND=y -CONFIG_STRICT_KERNEL_RWX=y -CONFIG_SUSPEND_TIME=y -CONFIG_TABLET_USB_ACECAD=y -CONFIG_TABLET_USB_AIPTEK=y -CONFIG_TABLET_USB_GTCO=y -CONFIG_TABLET_USB_HANWANG=y -CONFIG_TABLET_USB_KBTAB=y -CONFIG_TASKSTATS=y -CONFIG_TASK_DELAY_ACCT=y -CONFIG_TASK_IO_ACCOUNTING=y -CONFIG_TASK_XACCT=y -CONFIG_TIMER_STATS=y -CONFIG_TMPFS=y -CONFIG_TMPFS_POSIX_ACL=y -CONFIG_UHID=y -CONFIG_USB_ANNOUNCE_NEW_DEVICES=y -CONFIG_USB_EHCI_HCD=y -CONFIG_USB_HIDDEV=y -CONFIG_USB_USBNET=y -CONFIG_VFAT_FS=y From 372d5bb1f532913342616ebc36be5104e930b904 Mon Sep 17 00:00:00 2001 From: David Zeuthen Date: Tue, 24 Jan 2017 13:17:01 -0500 Subject: [PATCH 0446/1103] ANDROID: AVB error handler to invalidate vbmeta partition. If androidboot.vbmeta.invalidate_on_error is 'yes' and androidboot.vbmeta.device is set and points to a device with vbmeta magic, this header will be overwritten upon an irrecoverable dm-verity error. The side-effect of this is that the slot will fail to verify on next reboot, effectively triggering the boot loader to fallback to another slot. This work both if the vbmeta struct is at the start of a partition or if there's an AVB footer at the end. This code is based on drivers/md/dm-verity-chromeos.c from ChromiumOS. Bug: 31622239 Test: Manually tested (other arch). Change-Id: I571b5a75461da38ad832a9bea33c298bef859e26 Signed-off-by: David Zeuthen --- drivers/md/Kconfig | 9 ++ drivers/md/Makefile | 4 + drivers/md/dm-verity-avb.c | 229 ++++++++++++++++++++++++++++++++++ drivers/md/dm-verity-target.c | 6 +- drivers/md/dm-verity.h | 1 + 5 files changed, 248 insertions(+), 1 deletion(-) create mode 100644 drivers/md/dm-verity-avb.c diff --git a/drivers/md/Kconfig b/drivers/md/Kconfig index be6b64ce3881..7a0fea9633fb 100644 --- a/drivers/md/Kconfig +++ b/drivers/md/Kconfig @@ -574,6 +574,15 @@ config DM_ZONED If unsure, say N. +config DM_VERITY_AVB + tristate "Support AVB specific verity error behavior" + depends on DM_VERITY + ---help--- + Enables Android Verified Boot platform-specific error + behavior. In particular, it will modify the vbmeta partition + specified on the kernel command-line when non-transient error + occurs (followed by a panic). + config DM_ANDROID_VERITY bool "Android verity target support" depends on BLK_DEV_DM=y diff --git a/drivers/md/Makefile b/drivers/md/Makefile index dab38ff6f542..8e371cd1d2ed 100644 --- a/drivers/md/Makefile +++ b/drivers/md/Makefile @@ -77,6 +77,10 @@ ifeq ($(CONFIG_DM_VERITY_FEC),y) dm-verity-objs += dm-verity-fec.o endif +ifeq ($(CONFIG_DM_VERITY_AVB),y) +dm-verity-objs += dm-verity-avb.o +endif + ifeq ($(CONFIG_DM_ANDROID_VERITY),y) dm-verity-objs += dm-android-verity.o endif diff --git a/drivers/md/dm-verity-avb.c b/drivers/md/dm-verity-avb.c new file mode 100644 index 000000000000..a9f102aa379e --- /dev/null +++ b/drivers/md/dm-verity-avb.c @@ -0,0 +1,229 @@ +/* + * Copyright (C) 2017 Google. + * + * This file is released under the GPLv2. + * + * Based on drivers/md/dm-verity-chromeos.c + */ + +#include +#include +#include + +#define DM_MSG_PREFIX "verity-avb" + +/* Set via module parameters. */ +static char avb_vbmeta_device[64]; +static char avb_invalidate_on_error[4]; + +static void invalidate_vbmeta_endio(struct bio *bio) +{ + if (bio->bi_status) + DMERR("invalidate_vbmeta_endio: error %d", bio->bi_status); + complete(bio->bi_private); +} + +static int invalidate_vbmeta_submit(struct bio *bio, + struct block_device *bdev, + int op, int access_last_sector, + struct page *page) +{ + DECLARE_COMPLETION_ONSTACK(wait); + + bio->bi_private = &wait; + bio->bi_end_io = invalidate_vbmeta_endio; + bio_set_dev(bio, bdev); + bio_set_op_attrs(bio, op, REQ_SYNC); + + bio->bi_iter.bi_sector = 0; + if (access_last_sector) { + sector_t last_sector; + + last_sector = (i_size_read(bdev->bd_inode)>>SECTOR_SHIFT) - 1; + bio->bi_iter.bi_sector = last_sector; + } + if (!bio_add_page(bio, page, PAGE_SIZE, 0)) { + DMERR("invalidate_vbmeta_submit: bio_add_page error"); + return -EIO; + } + + submit_bio(bio); + /* Wait up to 2 seconds for completion or fail. */ + if (!wait_for_completion_timeout(&wait, msecs_to_jiffies(2000))) + return -EIO; + return 0; +} + +static int invalidate_vbmeta(dev_t vbmeta_devt) +{ + int ret = 0; + struct block_device *bdev; + struct bio *bio; + struct page *page; + fmode_t dev_mode; + /* Ensure we do synchronous unblocked I/O. We may also need + * sync_bdev() on completion, but it really shouldn't. + */ + int access_last_sector = 0; + + DMINFO("invalidate_vbmeta: acting on device %d:%d", + MAJOR(vbmeta_devt), MINOR(vbmeta_devt)); + + /* First we open the device for reading. */ + dev_mode = FMODE_READ | FMODE_EXCL; + bdev = blkdev_get_by_dev(vbmeta_devt, dev_mode, + invalidate_vbmeta); + if (IS_ERR(bdev)) { + DMERR("invalidate_kernel: could not open device for reading"); + dev_mode = 0; + ret = -ENOENT; + goto failed_to_read; + } + + bio = bio_alloc(GFP_NOIO, 1); + if (!bio) { + ret = -ENOMEM; + goto failed_bio_alloc; + } + + page = alloc_page(GFP_NOIO); + if (!page) { + ret = -ENOMEM; + goto failed_to_alloc_page; + } + + access_last_sector = 0; + ret = invalidate_vbmeta_submit(bio, bdev, REQ_OP_READ, + access_last_sector, page); + if (ret) { + DMERR("invalidate_vbmeta: error reading"); + goto failed_to_submit_read; + } + + /* We have a page. Let's make sure it looks right. */ + if (memcmp("AVB0", page_address(page), 4) == 0) { + /* Stamp it. */ + memcpy(page_address(page), "AVE0", 4); + DMINFO("invalidate_vbmeta: found vbmeta partition"); + } else { + /* Could be this is on a AVB footer, check. Also, since the + * AVB footer is in the last 64 bytes, adjust for the fact that + * we're dealing with 512-byte sectors. + */ + size_t offset = (1<bi_remaining. + */ + bio_reset(bio); + + ret = invalidate_vbmeta_submit(bio, bdev, REQ_OP_WRITE, + access_last_sector, page); + if (ret) { + DMERR("invalidate_vbmeta: error writing"); + goto failed_to_submit_write; + } + + DMERR("invalidate_vbmeta: completed."); + ret = 0; +failed_to_submit_write: +failed_to_write: +invalid_header: + __free_page(page); +failed_to_submit_read: + /* Technically, we'll leak a page with the pending bio, but + * we're about to reboot anyway. + */ +failed_to_alloc_page: + bio_put(bio); +failed_bio_alloc: + if (dev_mode) + blkdev_put(bdev, dev_mode); +failed_to_read: + return ret; +} + +void dm_verity_avb_error_handler(void) +{ + dev_t dev; + + DMINFO("AVB error handler called for %s", avb_vbmeta_device); + + if (strcmp(avb_invalidate_on_error, "yes") != 0) { + DMINFO("Not configured to invalidate"); + return; + } + + if (avb_vbmeta_device[0] == '\0') { + DMERR("avb_vbmeta_device parameter not set"); + goto fail_no_dev; + } + + dev = name_to_dev_t(avb_vbmeta_device); + if (!dev) { + DMERR("No matching partition for device: %s", + avb_vbmeta_device); + goto fail_no_dev; + } + + invalidate_vbmeta(dev); + +fail_no_dev: + ; +} + +static int __init dm_verity_avb_init(void) +{ + DMINFO("AVB error handler initialized with vbmeta device: %s", + avb_vbmeta_device); + return 0; +} + +static void __exit dm_verity_avb_exit(void) +{ +} + +module_init(dm_verity_avb_init); +module_exit(dm_verity_avb_exit); + +MODULE_AUTHOR("David Zeuthen "); +MODULE_DESCRIPTION("AVB-specific error handler for dm-verity"); +MODULE_LICENSE("GPL"); + +/* Declare parameter with no module prefix */ +#undef MODULE_PARAM_PREFIX +#define MODULE_PARAM_PREFIX "androidboot.vbmeta." +module_param_string(device, avb_vbmeta_device, sizeof(avb_vbmeta_device), 0); +module_param_string(invalidate_on_error, avb_invalidate_on_error, + sizeof(avb_invalidate_on_error), 0); diff --git a/drivers/md/dm-verity-target.c b/drivers/md/dm-verity-target.c index 1a9f464d15c7..ea131ea0d8ea 100644 --- a/drivers/md/dm-verity-target.c +++ b/drivers/md/dm-verity-target.c @@ -251,8 +251,12 @@ static int verity_handle_err(struct dm_verity *v, enum verity_block_type type, if (v->mode == DM_VERITY_MODE_LOGGING) return 0; - if (v->mode == DM_VERITY_MODE_RESTART) + if (v->mode == DM_VERITY_MODE_RESTART) { +#ifdef CONFIG_DM_VERITY_AVB + dm_verity_avb_error_handler(); +#endif kernel_restart("dm-verity device corrupted"); + } return 1; } diff --git a/drivers/md/dm-verity.h b/drivers/md/dm-verity.h index 0f634e49f63b..233cc99d440d 100644 --- a/drivers/md/dm-verity.h +++ b/drivers/md/dm-verity.h @@ -136,4 +136,5 @@ extern void verity_io_hints(struct dm_target *ti, struct queue_limits *limits); extern void verity_dtr(struct dm_target *ti); extern int verity_ctr(struct dm_target *ti, unsigned argc, char **argv); extern int verity_map(struct dm_target *ti, struct bio *bio); +extern void dm_verity_avb_error_handler(void); #endif /* DM_VERITY_H */ From b817d45e6de2e782a6d285d31998c99077f46ce0 Mon Sep 17 00:00:00 2001 From: Peter Kalauskas Date: Thu, 23 Aug 2018 16:26:51 -0700 Subject: [PATCH 0447/1103] ANDROID: x86_64_cuttlefish_defconfig: Enable zram and zstd Signed-off-by: Peter Kalauskas Bug: 112488418 Change-Id: Ib4c5d7d9145c0258a956ce0af70d7924f3f2fd96 --- arch/x86/configs/x86_64_cuttlefish_defconfig | 3 +++ 1 file changed, 3 insertions(+) diff --git a/arch/x86/configs/x86_64_cuttlefish_defconfig b/arch/x86/configs/x86_64_cuttlefish_defconfig index 19e3a812306b..761739a11425 100644 --- a/arch/x86/configs/x86_64_cuttlefish_defconfig +++ b/arch/x86/configs/x86_64_cuttlefish_defconfig @@ -52,6 +52,7 @@ CONFIG_X86_CPUID=y CONFIG_KSM=y CONFIG_DEFAULT_MMAP_MIN_ADDR=65536 CONFIG_TRANSPARENT_HUGEPAGE=y +CONFIG_ZSMALLOC=y # CONFIG_MTRR is not set CONFIG_HZ_100=y CONFIG_KEXEC=y @@ -198,6 +199,7 @@ CONFIG_DEBUG_DEVRES=y CONFIG_OF=y CONFIG_OF_UNITTEST=y # CONFIG_PNP_DEBUG_MESSAGES is not set +CONFIG_ZRAM=y CONFIG_BLK_DEV_LOOP=y CONFIG_BLK_DEV_RAM=y CONFIG_BLK_DEV_RAM_SIZE=8192 @@ -452,6 +454,7 @@ CONFIG_SECURITY_SELINUX_CHECKREQPROT_VALUE=1 CONFIG_CRYPTO_RSA=y # CONFIG_CRYPTO_MANAGER_DISABLE_TESTS is not set CONFIG_CRYPTO_SHA512=y +CONFIG_CRYPTO_ZSTD=y CONFIG_CRYPTO_DEV_VIRTIO=y CONFIG_ASYMMETRIC_KEY_TYPE=y CONFIG_ASYMMETRIC_PUBLIC_KEY_SUBTYPE=y From eb8b982c90499daba8df0682b31235208918e65e Mon Sep 17 00:00:00 2001 From: Peter Kalauskas Date: Fri, 24 Aug 2018 12:27:25 -0700 Subject: [PATCH 0448/1103] ANDROID: x86_64_cuttlefish_defconfig: Enable lz4 compression for zram Signed-off-by: Peter Kalauskas Bug: 112488418 Change-Id: I999c604d85f3a96bea97829b98101ea865a23275 --- arch/x86/configs/x86_64_cuttlefish_defconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/x86/configs/x86_64_cuttlefish_defconfig b/arch/x86/configs/x86_64_cuttlefish_defconfig index 761739a11425..db63c91b57b7 100644 --- a/arch/x86/configs/x86_64_cuttlefish_defconfig +++ b/arch/x86/configs/x86_64_cuttlefish_defconfig @@ -454,6 +454,7 @@ CONFIG_SECURITY_SELINUX_CHECKREQPROT_VALUE=1 CONFIG_CRYPTO_RSA=y # CONFIG_CRYPTO_MANAGER_DISABLE_TESTS is not set CONFIG_CRYPTO_SHA512=y +CONFIG_CRYPTO_LZ4=y CONFIG_CRYPTO_ZSTD=y CONFIG_CRYPTO_DEV_VIRTIO=y CONFIG_ASYMMETRIC_KEY_TYPE=y From 6eacb8c548ae1ed6efa0cbea4fb74c69d2f76ca3 Mon Sep 17 00:00:00 2001 From: Martijn Coenen Date: Sat, 25 Aug 2018 13:50:56 -0700 Subject: [PATCH 0449/1103] FROMLIST: ANDROID: binder: Add BINDER_GET_NODE_INFO_FOR_REF ioctl. This allows the context manager to retrieve information about nodes that it holds a reference to, such as the current number of references to those nodes. Such information can for example be used to determine whether the servicemanager is the only process holding a reference to a node. This information can then be passed on to the process holding the node, which can in turn decide whether it wants to shut down to reduce resource usage. Signed-off-by: Martijn Coenen --- drivers/android/binder.c | 55 +++++++++++++++++++++++++++++ include/uapi/linux/android/binder.h | 10 ++++++ 2 files changed, 65 insertions(+) diff --git a/drivers/android/binder.c b/drivers/android/binder.c index 0d475a17089b..c0cd8b65fdf5 100644 --- a/drivers/android/binder.c +++ b/drivers/android/binder.c @@ -4715,6 +4715,42 @@ static int binder_ioctl_set_ctx_mgr(struct file *filp) return ret; } +static int binder_ioctl_get_node_info_for_ref(struct binder_proc *proc, + struct binder_node_info_for_ref *info) +{ + struct binder_node *node; + struct binder_context *context = proc->context; + __u32 handle = info->handle; + + if (info->strong_count || info->weak_count || info->reserved1 || + info->reserved2 || info->reserved3) { + binder_user_error("%d BINDER_GET_NODE_INFO_FOR_REF: only handle may be non-zero.", + proc->pid); + return -EINVAL; + } + + /* This ioctl may only be used by the context manager */ + mutex_lock(&context->context_mgr_node_lock); + if (!context->binder_context_mgr_node || + context->binder_context_mgr_node->proc != proc) { + mutex_unlock(&context->context_mgr_node_lock); + return -EPERM; + } + mutex_unlock(&context->context_mgr_node_lock); + + node = binder_get_node_from_ref(proc, handle, true, NULL); + if (!node) + return -EINVAL; + + info->strong_count = node->local_strong_refs + + node->internal_strong_refs; + info->weak_count = node->local_weak_refs; + + binder_put_node(node); + + return 0; +} + static int binder_ioctl_get_node_debug_info(struct binder_proc *proc, struct binder_node_debug_info *info) { @@ -4809,6 +4845,25 @@ static long binder_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) } break; } + case BINDER_GET_NODE_INFO_FOR_REF: { + struct binder_node_info_for_ref info; + + if (copy_from_user(&info, ubuf, sizeof(info))) { + ret = -EFAULT; + goto err; + } + + ret = binder_ioctl_get_node_info_for_ref(proc, &info); + if (ret < 0) + goto err; + + if (copy_to_user(ubuf, &info, sizeof(info))) { + ret = -EFAULT; + goto err; + } + + break; + } case BINDER_GET_NODE_DEBUG_INFO: { struct binder_node_debug_info info; diff --git a/include/uapi/linux/android/binder.h b/include/uapi/linux/android/binder.h index b4723e36b6cf..4a1c285b97b1 100644 --- a/include/uapi/linux/android/binder.h +++ b/include/uapi/linux/android/binder.h @@ -247,6 +247,15 @@ struct binder_node_debug_info { __u32 has_weak_ref; }; +struct binder_node_info_for_ref { + __u32 handle; + __u32 strong_count; + __u32 weak_count; + __u32 reserved1; + __u32 reserved2; + __u32 reserved3; +}; + #define BINDER_WRITE_READ _IOWR('b', 1, struct binder_write_read) #define BINDER_SET_IDLE_TIMEOUT _IOW('b', 3, __s64) #define BINDER_SET_MAX_THREADS _IOW('b', 5, __u32) @@ -255,6 +264,7 @@ struct binder_node_debug_info { #define BINDER_THREAD_EXIT _IOW('b', 8, __s32) #define BINDER_VERSION _IOWR('b', 9, struct binder_version) #define BINDER_GET_NODE_DEBUG_INFO _IOWR('b', 11, struct binder_node_debug_info) +#define BINDER_GET_NODE_INFO_FOR_REF _IOWR('b', 12, struct binder_node_info_for_ref) /* * NOTE: Two special error codes you should check for when calling From fe5c9d7ab114621304b44db3c1d9efe63c5fba8f Mon Sep 17 00:00:00 2001 From: Daniel Rosenberg Date: Thu, 19 Jul 2018 18:08:35 -0700 Subject: [PATCH 0450/1103] ANDROID: sdcardfs: Don't use OVERRIDE_CRED macro The macro hides some control flow, making it easier to run into bugs. bug: 111642636 Change-Id: I37ec207c277d97c4e7f1e8381bc9ae743ad78435 Reported-by: Jann Horn Signed-off-by: Daniel Rosenberg --- fs/sdcardfs/file.c | 24 +++-- fs/sdcardfs/inode.c | 198 +++++++++-------------------------------- fs/sdcardfs/lookup.c | 9 +- fs/sdcardfs/sdcardfs.h | 25 ------ 4 files changed, 66 insertions(+), 190 deletions(-) diff --git a/fs/sdcardfs/file.c b/fs/sdcardfs/file.c index 1461254f301d..271c4c4cb760 100644 --- a/fs/sdcardfs/file.c +++ b/fs/sdcardfs/file.c @@ -118,7 +118,11 @@ static long sdcardfs_unlocked_ioctl(struct file *file, unsigned int cmd, goto out; /* save current_cred and override it */ - OVERRIDE_CRED(sbi, saved_cred, SDCARDFS_I(file_inode(file))); + saved_cred = override_fsids(sbi, SDCARDFS_I(file_inode(file))->data); + if (!saved_cred) { + err = -ENOMEM; + goto out; + } if (lower_file->f_op->unlocked_ioctl) err = lower_file->f_op->unlocked_ioctl(lower_file, cmd, arg); @@ -127,7 +131,7 @@ static long sdcardfs_unlocked_ioctl(struct file *file, unsigned int cmd, if (!err) sdcardfs_copy_and_fix_attrs(file_inode(file), file_inode(lower_file)); - REVERT_CRED(saved_cred); + revert_fsids(saved_cred); out: return err; } @@ -149,12 +153,16 @@ static long sdcardfs_compat_ioctl(struct file *file, unsigned int cmd, goto out; /* save current_cred and override it */ - OVERRIDE_CRED(sbi, saved_cred, SDCARDFS_I(file_inode(file))); + saved_cred = override_fsids(sbi, SDCARDFS_I(file_inode(file))->data); + if (!saved_cred) { + err = -ENOMEM; + goto out; + } if (lower_file->f_op->compat_ioctl) err = lower_file->f_op->compat_ioctl(lower_file, cmd, arg); - REVERT_CRED(saved_cred); + revert_fsids(saved_cred); out: return err; } @@ -241,7 +249,11 @@ static int sdcardfs_open(struct inode *inode, struct file *file) } /* save current_cred and override it */ - OVERRIDE_CRED(sbi, saved_cred, SDCARDFS_I(inode)); + saved_cred = override_fsids(sbi, SDCARDFS_I(inode)->data); + if (!saved_cred) { + err = -ENOMEM; + goto out_err; + } file->private_data = kzalloc(sizeof(struct sdcardfs_file_info), GFP_KERNEL); @@ -271,7 +283,7 @@ static int sdcardfs_open(struct inode *inode, struct file *file) sdcardfs_copy_and_fix_attrs(inode, sdcardfs_lower_inode(inode)); out_revert_cred: - REVERT_CRED(saved_cred); + revert_fsids(saved_cred); out_err: dput(parent); return err; diff --git a/fs/sdcardfs/inode.c b/fs/sdcardfs/inode.c index 2de5a4dffa22..7fc2f083aaad 100644 --- a/fs/sdcardfs/inode.c +++ b/fs/sdcardfs/inode.c @@ -22,7 +22,6 @@ #include #include -/* Do not directly use this function. Use OVERRIDE_CRED() instead. */ const struct cred *override_fsids(struct sdcardfs_sb_info *sbi, struct sdcardfs_inode_data *data) { @@ -50,7 +49,6 @@ const struct cred *override_fsids(struct sdcardfs_sb_info *sbi, return old_cred; } -/* Do not directly use this function, use REVERT_CRED() instead. */ void revert_fsids(const struct cred *old_cred) { const struct cred *cur_cred; @@ -78,7 +76,10 @@ static int sdcardfs_create(struct inode *dir, struct dentry *dentry, } /* save current_cred and override it */ - OVERRIDE_CRED(SDCARDFS_SB(dir->i_sb), saved_cred, SDCARDFS_I(dir)); + saved_cred = override_fsids(SDCARDFS_SB(dir->i_sb), + SDCARDFS_I(dir)->data); + if (!saved_cred) + return -ENOMEM; sdcardfs_get_lower_path(dentry, &lower_path); lower_dentry = lower_path.dentry; @@ -115,53 +116,11 @@ static int sdcardfs_create(struct inode *dir, struct dentry *dentry, out_unlock: unlock_dir(lower_parent_dentry); sdcardfs_put_lower_path(dentry, &lower_path); - REVERT_CRED(saved_cred); + revert_fsids(saved_cred); out_eacces: return err; } -#if 0 -static int sdcardfs_link(struct dentry *old_dentry, struct inode *dir, - struct dentry *new_dentry) -{ - struct dentry *lower_old_dentry; - struct dentry *lower_new_dentry; - struct dentry *lower_dir_dentry; - u64 file_size_save; - int err; - struct path lower_old_path, lower_new_path; - - OVERRIDE_CRED(SDCARDFS_SB(dir->i_sb)); - - file_size_save = i_size_read(d_inode(old_dentry)); - sdcardfs_get_lower_path(old_dentry, &lower_old_path); - sdcardfs_get_lower_path(new_dentry, &lower_new_path); - lower_old_dentry = lower_old_path.dentry; - lower_new_dentry = lower_new_path.dentry; - lower_dir_dentry = lock_parent(lower_new_dentry); - - err = vfs_link(lower_old_dentry, d_inode(lower_dir_dentry), - lower_new_dentry, NULL); - if (err || !d_inode(lower_new_dentry)) - goto out; - - err = sdcardfs_interpose(new_dentry, dir->i_sb, &lower_new_path); - if (err) - goto out; - fsstack_copy_attr_times(dir, d_inode(lower_new_dentry)); - fsstack_copy_inode_size(dir, d_inode(lower_new_dentry)); - set_nlink(d_inode(old_dentry), - sdcardfs_lower_inode(d_inode(old_dentry))->i_nlink); - i_size_write(d_inode(new_dentry), file_size_save); -out: - unlock_dir(lower_dir_dentry); - sdcardfs_put_lower_path(old_dentry, &lower_old_path); - sdcardfs_put_lower_path(new_dentry, &lower_new_path); - REVERT_CRED(); - return err; -} -#endif - static int sdcardfs_unlink(struct inode *dir, struct dentry *dentry) { int err; @@ -178,7 +137,10 @@ static int sdcardfs_unlink(struct inode *dir, struct dentry *dentry) } /* save current_cred and override it */ - OVERRIDE_CRED(SDCARDFS_SB(dir->i_sb), saved_cred, SDCARDFS_I(dir)); + saved_cred = override_fsids(SDCARDFS_SB(dir->i_sb), + SDCARDFS_I(dir)->data); + if (!saved_cred) + return -ENOMEM; sdcardfs_get_lower_path(dentry, &lower_path); lower_dentry = lower_path.dentry; @@ -209,43 +171,11 @@ static int sdcardfs_unlink(struct inode *dir, struct dentry *dentry) unlock_dir(lower_dir_dentry); dput(lower_dentry); sdcardfs_put_lower_path(dentry, &lower_path); - REVERT_CRED(saved_cred); + revert_fsids(saved_cred); out_eacces: return err; } -#if 0 -static int sdcardfs_symlink(struct inode *dir, struct dentry *dentry, - const char *symname) -{ - int err; - struct dentry *lower_dentry; - struct dentry *lower_parent_dentry = NULL; - struct path lower_path; - - OVERRIDE_CRED(SDCARDFS_SB(dir->i_sb)); - - sdcardfs_get_lower_path(dentry, &lower_path); - lower_dentry = lower_path.dentry; - lower_parent_dentry = lock_parent(lower_dentry); - - err = vfs_symlink(d_inode(lower_parent_dentry), lower_dentry, symname); - if (err) - goto out; - err = sdcardfs_interpose(dentry, dir->i_sb, &lower_path); - if (err) - goto out; - fsstack_copy_attr_times(dir, sdcardfs_lower_inode(dir)); - fsstack_copy_inode_size(dir, d_inode(lower_parent_dentry)); - -out: - unlock_dir(lower_parent_dentry); - sdcardfs_put_lower_path(dentry, &lower_path); - REVERT_CRED(); - return err; -} -#endif - static int touch(char *abs_path, mode_t mode) { struct file *filp = filp_open(abs_path, O_RDWR|O_CREAT|O_EXCL|O_NOFOLLOW, mode); @@ -287,7 +217,10 @@ static int sdcardfs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode } /* save current_cred and override it */ - OVERRIDE_CRED(SDCARDFS_SB(dir->i_sb), saved_cred, SDCARDFS_I(dir)); + saved_cred = override_fsids(SDCARDFS_SB(dir->i_sb), + SDCARDFS_I(dir)->data); + if (!saved_cred) + return -ENOMEM; /* check disk space */ parent_dentry = dget_parent(dentry); @@ -366,13 +299,21 @@ static int sdcardfs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode if (make_nomedia_in_obb || ((pd->perm == PERM_ANDROID) && (qstr_case_eq(&dentry->d_name, &q_data)))) { - REVERT_CRED(saved_cred); - OVERRIDE_CRED(SDCARDFS_SB(dir->i_sb), saved_cred, SDCARDFS_I(d_inode(dentry))); + revert_fsids(saved_cred); + saved_cred = override_fsids(sbi, + SDCARDFS_I(d_inode(dentry))->data); + if (!saved_cred) { + pr_err("sdcardfs: failed to set up .nomedia in %s: %d\n", + lower_path.dentry->d_name.name, + -ENOMEM); + goto out; + } set_fs_pwd(current->fs, &lower_path); touch_err = touch(".nomedia", 0664); if (touch_err) { pr_err("sdcardfs: failed to create .nomedia in %s: %d\n", - lower_path.dentry->d_name.name, touch_err); + lower_path.dentry->d_name.name, + touch_err); goto out; } } @@ -382,7 +323,7 @@ static int sdcardfs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode out_unlock: sdcardfs_put_lower_path(dentry, &lower_path); out_revert: - REVERT_CRED(saved_cred); + revert_fsids(saved_cred); out_eacces: return err; } @@ -402,7 +343,10 @@ static int sdcardfs_rmdir(struct inode *dir, struct dentry *dentry) } /* save current_cred and override it */ - OVERRIDE_CRED(SDCARDFS_SB(dir->i_sb), saved_cred, SDCARDFS_I(dir)); + saved_cred = override_fsids(SDCARDFS_SB(dir->i_sb), + SDCARDFS_I(dir)->data); + if (!saved_cred) + return -ENOMEM; /* sdcardfs_get_real_lower(): in case of remove an user's obb dentry * the dentry on the original path should be deleted. @@ -427,44 +371,11 @@ static int sdcardfs_rmdir(struct inode *dir, struct dentry *dentry) out: unlock_dir(lower_dir_dentry); sdcardfs_put_real_lower(dentry, &lower_path); - REVERT_CRED(saved_cred); + revert_fsids(saved_cred); out_eacces: return err; } -#if 0 -static int sdcardfs_mknod(struct inode *dir, struct dentry *dentry, umode_t mode, - dev_t dev) -{ - int err; - struct dentry *lower_dentry; - struct dentry *lower_parent_dentry = NULL; - struct path lower_path; - - OVERRIDE_CRED(SDCARDFS_SB(dir->i_sb)); - - sdcardfs_get_lower_path(dentry, &lower_path); - lower_dentry = lower_path.dentry; - lower_parent_dentry = lock_parent(lower_dentry); - - err = vfs_mknod(d_inode(lower_parent_dentry), lower_dentry, mode, dev); - if (err) - goto out; - - err = sdcardfs_interpose(dentry, dir->i_sb, &lower_path); - if (err) - goto out; - fsstack_copy_attr_times(dir, sdcardfs_lower_inode(dir)); - fsstack_copy_inode_size(dir, d_inode(lower_parent_dentry)); - -out: - unlock_dir(lower_parent_dentry); - sdcardfs_put_lower_path(dentry, &lower_path); - REVERT_CRED(); - return err; -} -#endif - /* * The locking rules in sdcardfs_rename are complex. We could use a simpler * superblock-level name-space lock for renames and copy-ups. @@ -493,7 +404,10 @@ static int sdcardfs_rename(struct inode *old_dir, struct dentry *old_dentry, } /* save current_cred and override it */ - OVERRIDE_CRED(SDCARDFS_SB(old_dir->i_sb), saved_cred, SDCARDFS_I(new_dir)); + saved_cred = override_fsids(SDCARDFS_SB(old_dir->i_sb), + SDCARDFS_I(new_dir)->data); + if (!saved_cred) + return -ENOMEM; sdcardfs_get_real_lower(old_dentry, &lower_old_path); sdcardfs_get_lower_path(new_dentry, &lower_new_path); @@ -540,7 +454,7 @@ static int sdcardfs_rename(struct inode *old_dir, struct dentry *old_dentry, dput(lower_new_dir_dentry); sdcardfs_put_real_lower(old_dentry, &lower_old_path); sdcardfs_put_lower_path(new_dentry, &lower_new_path); - REVERT_CRED(saved_cred); + revert_fsids(saved_cred); out_eacces: return err; } @@ -660,33 +574,7 @@ static int sdcardfs_permission(struct vfsmount *mnt, struct inode *inode, int ma if (IS_POSIXACL(inode)) pr_warn("%s: This may be undefined behavior...\n", __func__); err = generic_permission(&tmp, mask); - /* XXX - * Original sdcardfs code calls inode_permission(lower_inode,.. ) - * for checking inode permission. But doing such things here seems - * duplicated work, because the functions called after this func, - * such as vfs_create, vfs_unlink, vfs_rename, and etc, - * does exactly same thing, i.e., they calls inode_permission(). - * So we just let they do the things. - * If there are any security hole, just uncomment following if block. - */ -#if 0 - if (!err) { - /* - * Permission check on lower_inode(=EXT4). - * we check it with AID_MEDIA_RW permission - */ - struct inode *lower_inode; - - OVERRIDE_CRED(SDCARDFS_SB(inode->sb)); - - lower_inode = sdcardfs_lower_inode(inode); - err = inode_permission(lower_inode, mask); - - REVERT_CRED(); - } -#endif return err; - } static int sdcardfs_setattr_wrn(struct dentry *dentry, struct iattr *ia) @@ -764,7 +652,10 @@ static int sdcardfs_setattr(struct vfsmount *mnt, struct dentry *dentry, struct goto out_err; /* save current_cred and override it */ - OVERRIDE_CRED(SDCARDFS_SB(dentry->d_sb), saved_cred, SDCARDFS_I(inode)); + saved_cred = override_fsids(SDCARDFS_SB(dentry->d_sb), + SDCARDFS_I(inode)->data); + if (!saved_cred) + return -ENOMEM; sdcardfs_get_lower_path(dentry, &lower_path); lower_dentry = lower_path.dentry; @@ -823,7 +714,7 @@ static int sdcardfs_setattr(struct vfsmount *mnt, struct dentry *dentry, struct out: sdcardfs_put_lower_path(dentry, &lower_path); - REVERT_CRED(saved_cred); + revert_fsids(saved_cred); out_err: return err; } @@ -907,13 +798,6 @@ const struct inode_operations sdcardfs_dir_iops = { .setattr = sdcardfs_setattr_wrn, .setattr2 = sdcardfs_setattr, .getattr = sdcardfs_getattr, - /* XXX Following operations are implemented, - * but FUSE(sdcard) or FAT does not support them - * These methods are *NOT* perfectly tested. - .symlink = sdcardfs_symlink, - .link = sdcardfs_link, - .mknod = sdcardfs_mknod, - */ }; const struct inode_operations sdcardfs_main_iops = { diff --git a/fs/sdcardfs/lookup.c b/fs/sdcardfs/lookup.c index 2ff305161882..73179ce2591f 100644 --- a/fs/sdcardfs/lookup.c +++ b/fs/sdcardfs/lookup.c @@ -427,7 +427,12 @@ struct dentry *sdcardfs_lookup(struct inode *dir, struct dentry *dentry, } /* save current_cred and override it */ - OVERRIDE_CRED_PTR(SDCARDFS_SB(dir->i_sb), saved_cred, SDCARDFS_I(dir)); + saved_cred = override_fsids(SDCARDFS_SB(dir->i_sb), + SDCARDFS_I(dir)->data); + if (!saved_cred) { + ret = ERR_PTR(-ENOMEM); + goto out_err; + } sdcardfs_get_lower_path(parent, &lower_parent_path); @@ -458,7 +463,7 @@ struct dentry *sdcardfs_lookup(struct inode *dir, struct dentry *dentry, out: sdcardfs_put_lower_path(parent, &lower_parent_path); - REVERT_CRED(saved_cred); + revert_fsids(saved_cred); out_err: dput(parent); return ret; diff --git a/fs/sdcardfs/sdcardfs.h b/fs/sdcardfs/sdcardfs.h index 826afb5c7e88..ec2290a16d3c 100644 --- a/fs/sdcardfs/sdcardfs.h +++ b/fs/sdcardfs/sdcardfs.h @@ -88,31 +88,6 @@ (x)->i_mode = ((x)->i_mode & S_IFMT) | 0775;\ } while (0) -/* OVERRIDE_CRED() and REVERT_CRED() - * OVERRIDE_CRED() - * backup original task->cred - * and modifies task->cred->fsuid/fsgid to specified value. - * REVERT_CRED() - * restore original task->cred->fsuid/fsgid. - * These two macro should be used in pair, and OVERRIDE_CRED() should be - * placed at the beginning of a function, right after variable declaration. - */ -#define OVERRIDE_CRED(sdcardfs_sbi, saved_cred, info) \ - do { \ - saved_cred = override_fsids(sdcardfs_sbi, info->data); \ - if (!saved_cred) \ - return -ENOMEM; \ - } while (0) - -#define OVERRIDE_CRED_PTR(sdcardfs_sbi, saved_cred, info) \ - do { \ - saved_cred = override_fsids(sdcardfs_sbi, info->data); \ - if (!saved_cred) \ - return ERR_PTR(-ENOMEM); \ - } while (0) - -#define REVERT_CRED(saved_cred) revert_fsids(saved_cred) - /* Android 5.0 support */ /* Permission mode for a specific node. Controls how file permissions From 25d1d66281a37e68fd7a28460b56e4099c9f7d38 Mon Sep 17 00:00:00 2001 From: Daniel Rosenberg Date: Fri, 20 Jul 2018 16:11:40 -0700 Subject: [PATCH 0451/1103] ANDROID: sdcardfs: Change current->fs under lock Adjusted from previous version to add missing include bug: 111641492 Change-Id: I321d83f5d599efb3abdfaf2f3a4900ac512beca6 Reported-by: Jann Horn Signed-off-by: Daniel Rosenberg --- fs/sdcardfs/inode.c | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/fs/sdcardfs/inode.c b/fs/sdcardfs/inode.c index 7fc2f083aaad..4dd681e0d59d 100644 --- a/fs/sdcardfs/inode.c +++ b/fs/sdcardfs/inode.c @@ -21,6 +21,7 @@ #include "sdcardfs.h" #include #include +#include const struct cred *override_fsids(struct sdcardfs_sb_info *sbi, struct sdcardfs_inode_data *data) @@ -96,8 +97,11 @@ static int sdcardfs_create(struct inode *dir, struct dentry *dentry, err = -ENOMEM; goto out_unlock; } + copied_fs->umask = 0; + task_lock(current); current->fs = copied_fs; - current->fs->umask = 0; + task_unlock(current); + err = vfs_create2(lower_dentry_mnt, d_inode(lower_parent_dentry), lower_dentry, mode, want_excl); if (err) goto out; @@ -111,7 +115,9 @@ static int sdcardfs_create(struct inode *dir, struct dentry *dentry, fixup_lower_ownership(dentry, dentry->d_name.name); out: + task_lock(current); current->fs = saved_fs; + task_unlock(current); free_fs_struct(copied_fs); out_unlock: unlock_dir(lower_parent_dentry); @@ -249,8 +255,11 @@ static int sdcardfs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode unlock_dir(lower_parent_dentry); goto out_unlock; } + copied_fs->umask = 0; + task_lock(current); current->fs = copied_fs; - current->fs->umask = 0; + task_unlock(current); + err = vfs_mkdir2(lower_mnt, d_inode(lower_parent_dentry), lower_dentry, mode); if (err) { @@ -318,7 +327,10 @@ static int sdcardfs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode } } out: + task_lock(current); current->fs = saved_fs; + task_unlock(current); + free_fs_struct(copied_fs); out_unlock: sdcardfs_put_lower_path(dentry, &lower_path); From 1e8515c57cf9243281c16b13b1fd62a41facdd76 Mon Sep 17 00:00:00 2001 From: Amit Pundir Date: Mon, 8 Jan 2018 22:51:52 +0530 Subject: [PATCH 0452/1103] RFC: ANDROID: net: ipv4: tcp: Namespace-ify sysctl_tcp_default_init_rwnd Signed-off-by: Amit Pundir --- include/net/netns/ipv4.h | 1 + include/net/tcp.h | 2 -- net/ipv4/sysctl_net_ipv4.c | 14 +++++++------- net/ipv4/tcp_input.c | 1 - net/ipv4/tcp_ipv4.c | 1 + 5 files changed, 9 insertions(+), 10 deletions(-) diff --git a/include/net/netns/ipv4.h b/include/net/netns/ipv4.h index e47503b4e4d1..9b8ab971ad93 100644 --- a/include/net/netns/ipv4.h +++ b/include/net/netns/ipv4.h @@ -159,6 +159,7 @@ struct netns_ipv4 { int sysctl_tcp_invalid_ratelimit; int sysctl_tcp_pacing_ss_ratio; int sysctl_tcp_pacing_ca_ratio; + int sysctl_tcp_default_init_rwnd; int sysctl_tcp_wmem[3]; int sysctl_tcp_rmem[3]; int sysctl_tcp_comp_sack_nr; diff --git a/include/net/tcp.h b/include/net/tcp.h index 87ed93f14c52..770917d0caa7 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -248,8 +248,6 @@ extern long sysctl_tcp_mem[3]; #define TCP_RACK_STATIC_REO_WND 0x2 /* Use static RACK reo wnd */ #define TCP_RACK_NO_DUPTHRESH 0x4 /* Do not use DUPACK threshold in RACK */ -extern int sysctl_tcp_default_init_rwnd; - extern atomic_long_t tcp_memory_allocated; extern struct percpu_counter tcp_sockets_allocated; extern unsigned long tcp_memory_pressure; diff --git a/net/ipv4/sysctl_net_ipv4.c b/net/ipv4/sysctl_net_ipv4.c index bad5a38de987..314ea3159cec 100644 --- a/net/ipv4/sysctl_net_ipv4.c +++ b/net/ipv4/sysctl_net_ipv4.c @@ -541,13 +541,6 @@ static struct ctl_table ipv4_table[] = { .mode = 0444, .proc_handler = proc_tcp_available_ulp, }, - { - .procname = "tcp_default_init_rwnd", - .data = &sysctl_tcp_default_init_rwnd, - .maxlen = sizeof(int), - .mode = 0644, - .proc_handler = proc_tcp_default_init_rwnd - }, { .procname = "icmp_msgs_per_sec", .data = &sysctl_icmp_msgs_per_sec, @@ -1198,6 +1191,13 @@ static struct ctl_table ipv4_net_table[] = { .extra1 = &zero, .extra2 = &thousand, }, + { + .procname = "tcp_default_init_rwnd", + .data = &init_net.ipv4.sysctl_tcp_default_init_rwnd, + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = proc_tcp_default_init_rwnd + }, { .procname = "tcp_wmem", .data = &init_net.ipv4.sysctl_tcp_wmem, diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c index 88db2d6f70aa..47e08c1b5bc3 100644 --- a/net/ipv4/tcp_input.c +++ b/net/ipv4/tcp_input.c @@ -81,7 +81,6 @@ #include int sysctl_tcp_max_orphans __read_mostly = NR_FILE; -int sysctl_tcp_default_init_rwnd __read_mostly = TCP_INIT_CWND * 2; #define FLAG_DATA 0x01 /* Incoming frame contained data. */ #define FLAG_WIN_UPDATE 0x02 /* Incoming ACK was a window update. */ diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c index cd426313a298..0c65fc967014 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c @@ -2583,6 +2583,7 @@ static int __net_init tcp_sk_init(struct net *net) net->ipv4.sysctl_tcp_invalid_ratelimit = HZ/2; net->ipv4.sysctl_tcp_pacing_ss_ratio = 200; net->ipv4.sysctl_tcp_pacing_ca_ratio = 120; + net->ipv4.sysctl_tcp_default_init_rwnd = TCP_INIT_CWND * 2; if (net != &init_net) { memcpy(net->ipv4.sysctl_tcp_rmem, init_net.ipv4.sysctl_tcp_rmem, From c7a1231e383220a69a09ef935c34bfe69a876e5c Mon Sep 17 00:00:00 2001 From: Amit Pundir Date: Mon, 8 Jan 2018 22:55:09 +0530 Subject: [PATCH 0453/1103] RFC: ANDROID: net: ipv4: sysfs_net_ipv4: Fix TCP window size controlling knobs Refactor /sys/kernel/ipv4/tcp_* send/receive window size controlling knobs to align with the changes of upstream commit 356d1833b638 ("tcp: Namespace-ify sysctl_tcp_rmem and sysctl_tcp_wmem"). Fixes: ("ANDROID: net: ipv4: sysfs_net_ipv4: Add sysfs-based knobs for controlling TCP window size") Signed-off-by: Amit Pundir --- include/net/tcp.h | 2 +- net/ipv4/sysfs_net_ipv4.c | 12 ++++++------ net/ipv4/tcp_input.c | 2 +- net/ipv4/tcp_output.c | 6 +++--- 4 files changed, 11 insertions(+), 11 deletions(-) diff --git a/include/net/tcp.h b/include/net/tcp.h index 770917d0caa7..a709723b4e65 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -1315,7 +1315,7 @@ static inline void tcp_sack_reset(struct tcp_options_received *rx_opt) rx_opt->num_sacks = 0; } -u32 tcp_default_init_rwnd(u32 mss); +u32 tcp_default_init_rwnd(const struct sock *sk, u32 mss); void tcp_cwnd_restart(struct sock *sk, s32 delta); static inline void tcp_slow_start_after_idle_check(struct sock *sk) diff --git a/net/ipv4/sysfs_net_ipv4.c b/net/ipv4/sysfs_net_ipv4.c index 0cbbf10026a6..35a651aaee47 100644 --- a/net/ipv4/sysfs_net_ipv4.c +++ b/net/ipv4/sysfs_net_ipv4.c @@ -45,13 +45,13 @@ static ssize_t _name##_store(struct kobject *kobj, \ static struct kobj_attribute _name##_attr = \ __ATTR(_name, 0644, _name##_show, _name##_store) -CREATE_IPV4_FILE(tcp_wmem_min, sysctl_tcp_wmem[0]); -CREATE_IPV4_FILE(tcp_wmem_def, sysctl_tcp_wmem[1]); -CREATE_IPV4_FILE(tcp_wmem_max, sysctl_tcp_wmem[2]); +CREATE_IPV4_FILE(tcp_wmem_min, init_net.ipv4.sysctl_tcp_wmem[0]); +CREATE_IPV4_FILE(tcp_wmem_def, init_net.ipv4.sysctl_tcp_wmem[1]); +CREATE_IPV4_FILE(tcp_wmem_max, init_net.ipv4.sysctl_tcp_wmem[2]); -CREATE_IPV4_FILE(tcp_rmem_min, sysctl_tcp_rmem[0]); -CREATE_IPV4_FILE(tcp_rmem_def, sysctl_tcp_rmem[1]); -CREATE_IPV4_FILE(tcp_rmem_max, sysctl_tcp_rmem[2]); +CREATE_IPV4_FILE(tcp_rmem_min, init_net.ipv4.sysctl_tcp_rmem[0]); +CREATE_IPV4_FILE(tcp_rmem_def, init_net.ipv4.sysctl_tcp_rmem[1]); +CREATE_IPV4_FILE(tcp_rmem_max, init_net.ipv4.sysctl_tcp_rmem[2]); static struct attribute *ipv4_attrs[] = { &tcp_wmem_min_attr.attr, diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c index 47e08c1b5bc3..83cadba78b7d 100644 --- a/net/ipv4/tcp_input.c +++ b/net/ipv4/tcp_input.c @@ -433,7 +433,7 @@ static void tcp_fixup_rcvbuf(struct sock *sk) int rcvmem; rcvmem = 2 * SKB_TRUESIZE(mss + MAX_TCP_HEADER) * - tcp_default_init_rwnd(mss); + tcp_default_init_rwnd(sk, mss); /* Dynamic Right Sizing (DRS) has 2 to 3 RTT latency * Allow enough cushion so that sender is not limited by our window diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c index 20abda1b6ff1..178ddcb87aec 100644 --- a/net/ipv4/tcp_output.c +++ b/net/ipv4/tcp_output.c @@ -180,14 +180,14 @@ static inline void tcp_event_ack_sent(struct sock *sk, unsigned int pkts, } -u32 tcp_default_init_rwnd(u32 mss) +u32 tcp_default_init_rwnd(const struct sock *sk, u32 mss) { /* Initial receive window should be twice of TCP_INIT_CWND to * enable proper sending of new unsent data during fast recovery * (RFC 3517, Section 4, NextSeg() rule (2)). Further place a * limit when mss is larger than 1460. */ - u32 init_rwnd = sysctl_tcp_default_init_rwnd; + u32 init_rwnd = sock_net(sk)->ipv4.sysctl_tcp_default_init_rwnd; if (mss > 1460) init_rwnd = max((1460 * init_rwnd) / mss, 2U); @@ -243,7 +243,7 @@ void tcp_select_initial_window(const struct sock *sk, int __space, __u32 mss, } if (!init_rcv_wnd) /* Use default unless specified otherwise */ - init_rcv_wnd = tcp_default_init_rwnd(mss); + init_rcv_wnd = tcp_default_init_rwnd(sk, mss); *rcv_wnd = min(*rcv_wnd, init_rcv_wnd * mss); /* Set the clamp no higher than max representable value */ From 3f1aa14bce2392f69bb96ad0333fb5350378ec4d Mon Sep 17 00:00:00 2001 From: Amit Pundir Date: Fri, 9 Mar 2018 19:51:44 +0530 Subject: [PATCH 0454/1103] RFC: ANDROID: fs: sdcardfs: Use inode iversion helpers Upstream commit f02a9ad1f15d ("fs: handle inode->i_version more efficiently") converted the inode -> i_version counter to an atomic64_t. So move to using relevant iversion helper routines for basic operations instead. Signed-off-by: Amit Pundir --- fs/sdcardfs/lookup.c | 2 +- fs/sdcardfs/sdcardfs.h | 1 + fs/sdcardfs/super.c | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/fs/sdcardfs/lookup.c b/fs/sdcardfs/lookup.c index 73179ce2591f..a5c9686090e0 100644 --- a/fs/sdcardfs/lookup.c +++ b/fs/sdcardfs/lookup.c @@ -121,7 +121,7 @@ struct inode *sdcardfs_iget(struct super_block *sb, struct inode *lower_inode, u inode->i_ino = lower_inode->i_ino; sdcardfs_set_lower_inode(inode, lower_inode); - inode->i_version++; + inode_inc_iversion_raw(inode); /* use different set of inode ops for symlinks & directories */ if (S_ISDIR(lower_inode->i_mode)) diff --git a/fs/sdcardfs/sdcardfs.h b/fs/sdcardfs/sdcardfs.h index ec2290a16d3c..b96f4c3e1f40 100644 --- a/fs/sdcardfs/sdcardfs.h +++ b/fs/sdcardfs/sdcardfs.h @@ -45,6 +45,7 @@ #include #include #include +#include #include "multiuser.h" /* the file system name */ diff --git a/fs/sdcardfs/super.c b/fs/sdcardfs/super.c index cffcdb11cb8a..76afaa97e672 100644 --- a/fs/sdcardfs/super.c +++ b/fs/sdcardfs/super.c @@ -219,7 +219,7 @@ static struct inode *sdcardfs_alloc_inode(struct super_block *sb) spin_lock_init(&i->top_lock); kref_get(&d->refcount); - i->vfs_inode.i_version = 1; + inode_set_iversion(&i->vfs_inode, 1); return &i->vfs_inode; } From 33b8aa4a49164aead867c5d7a7bb195edccd47ff Mon Sep 17 00:00:00 2001 From: Amit Pundir Date: Mon, 9 Jul 2018 10:50:01 +0530 Subject: [PATCH 0455/1103] RFC: ANDROID: proc/uid: switch instantiate_t to d_splice_alias() Fix proc_fill_cache() now that upstream commit 0168b9e38c42 ("procfs: switch instantiate_t to d_splice_alias()"), switched instantiate() callback to d_splice_alias(). Signed-off-by: Amit Pundir --- fs/proc/uid.c | 37 +++++++++++++++++++++++-------------- 1 file changed, 23 insertions(+), 14 deletions(-) diff --git a/fs/proc/uid.c b/fs/proc/uid.c index 6a096d25109d..311717ea199a 100644 --- a/fs/proc/uid.c +++ b/fs/proc/uid.c @@ -118,15 +118,26 @@ static struct inode *proc_uid_make_inode(struct super_block *sb, kuid_t kuid) return inode; } -static int proc_uident_instantiate(struct inode *dir, struct dentry *dentry, +static struct dentry *proc_uident_instantiate(struct dentry *dentry, struct task_struct *unused, const void *ptr) { const struct uid_entry *u = ptr; struct inode *inode; - inode = proc_uid_make_inode(dir->i_sb, dir->i_uid); - if (!inode) - return -ENOENT; + uid_t uid = name_to_int(&dentry->d_name); + kuid_t kuid; + bool uid_exists; + rt_mutex_lock(&proc_uid_lock); + uid_exists = uid_hash_entry_exists_locked(uid); + rt_mutex_unlock(&proc_uid_lock); + if (uid_exists) { + kuid = make_kuid(current_user_ns(), uid); + inode = proc_uid_make_inode(dentry->d_sb, kuid); + if (!inode) + return ERR_PTR(-ENOENT); + } else { + return ERR_PTR(-ENOENT); + } inode->i_mode = u->mode; if (S_ISDIR(inode->i_mode)) @@ -135,8 +146,8 @@ static int proc_uident_instantiate(struct inode *dir, struct dentry *dentry, inode->i_op = u->iop; if (u->fop) inode->i_fop = u->fop; - d_add(dentry, inode); - return 0; + + return d_splice_alias(inode, dentry); } static struct dentry *proc_uid_base_lookup(struct inode *dir, @@ -159,7 +170,7 @@ static struct dentry *proc_uid_base_lookup(struct inode *dir, if (u > last) return ERR_PTR(-ENOENT); - return ERR_PTR(proc_uident_instantiate(dir, dentry, NULL, u)); + return proc_uident_instantiate(dentry, NULL, u); } static int proc_uid_base_readdir(struct file *file, struct dir_context *ctx) @@ -195,16 +206,16 @@ static const struct file_operations proc_uid_base_operations = { .llseek = default_llseek, }; -static int proc_uid_instantiate(struct inode *dir, struct dentry *dentry, +static struct dentry *proc_uid_instantiate(struct dentry *dentry, struct task_struct *unused, const void *ptr) { unsigned int i, len; nlink_t nlinks; kuid_t *kuid = (kuid_t *)ptr; - struct inode *inode = proc_uid_make_inode(dir->i_sb, *kuid); + struct inode *inode = proc_uid_make_inode(dentry->d_sb, *kuid); if (!inode) - return -ENOENT; + return ERR_PTR(-ENOENT); inode->i_mode = S_IFDIR | 0555; inode->i_op = &proc_uid_base_inode_operations; @@ -219,9 +230,7 @@ static int proc_uid_instantiate(struct inode *dir, struct dentry *dentry, } set_nlink(inode, nlinks); - d_add(dentry, inode); - - return 0; + return d_splice_alias(inode, dentry); } static int proc_uid_readdir(struct file *file, struct dir_context *ctx) @@ -267,7 +276,7 @@ static struct dentry *proc_uid_lookup(struct inode *dir, struct dentry *dentry, if (uid_exists) { kuid_t kuid = make_kuid(current_user_ns(), uid); - result = proc_uid_instantiate(dir, dentry, NULL, &kuid); + return proc_uid_instantiate(dentry, NULL, &kuid); } return ERR_PTR(result); } From dd9e6d153f10fc96535c3723912f13f8dfcc85b9 Mon Sep 17 00:00:00 2001 From: Amit Pundir Date: Mon, 9 Jul 2018 12:20:13 +0530 Subject: [PATCH 0456/1103] RFC: ANDROID: net: ipv6: Flip FIB entries to fib6_info Convert all code paths referencing a FIB entry from rt6_info to fib6_info. Align with upstream commit 8d1c802b2815 ("net/ipv6: Flip FIB entries to fib6_info") changes. Fixes: Change-Id: I82d16e3737d9cdfa6489e649e247894d0d60cbb1 ("ANDROID: net: ipv6: autoconf routes into per-device tables") Signed-off-by: Amit Pundir --- net/ipv6/route.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/net/ipv6/route.c b/net/ipv6/route.c index 76623b84467d..d2191b8bcc47 100644 --- a/net/ipv6/route.c +++ b/net/ipv6/route.c @@ -3587,10 +3587,15 @@ struct fib6_info *rt6_add_dflt_router(struct net *net, return rt6_get_dflt_router(net, gwaddr, dev); } -int rt6_addrconf_purge(struct rt6_info *rt, void *arg) { - if (rt->rt6i_flags & (RTF_DEFAULT | RTF_ADDRCONF) && - (!rt->rt6i_idev || rt->rt6i_idev->cnf.accept_ra != 2)) +int rt6_addrconf_purge(struct fib6_info *rt, void *arg) +{ + struct net_device *dev = fib6_info_nh_dev(rt); + struct inet6_dev *idev = dev ? __in6_dev_get(dev) : NULL; + + if (rt->fib6_flags & (RTF_DEFAULT | RTF_ADDRCONF) && + (!idev || idev->cnf.accept_ra != 2)) return -1; + return 0; } From 0aa25a8b0ec1f0ca48988c04f58927903eff51bc Mon Sep 17 00:00:00 2001 From: Morten Rasmussen Date: Fri, 20 Jul 2018 14:32:31 +0100 Subject: [PATCH 0457/1103] UPSTREAM: sched/topology: Add SD_ASYM_CPUCAPACITY flag detection The SD_ASYM_CPUCAPACITY sched_domain flag is supposed to mark the sched_domain in the hierarchy where all CPU capacities are visible for any CPU's point of view on asymmetric CPU capacity systems. The scheduler can then take to take capacity asymmetry into account when balancing at this level. It also serves as an indicator for how wide task placement heuristics have to search to consider all available CPU capacities as asymmetric systems might often appear symmetric at smallest level(s) of the sched_domain hierarchy. The flag has been around for while but so far only been set by out-of-tree code in Android kernels. One solution is to let each architecture provide the flag through a custom sched_domain topology array and associated mask and flag functions. However, SD_ASYM_CPUCAPACITY is special in the sense that it depends on the capacity and presence of all CPUs in the system, i.e. when hotplugging all CPUs out except those with one particular CPU capacity the flag should disappear even if the sched_domains don't collapse. Similarly, the flag is affected by cpusets where load-balancing is turned off. Detecting when the flags should be set therefore depends not only on topology information but also the cpuset configuration and hotplug state. The arch code doesn't have easy access to the cpuset configuration. Instead, this patch implements the flag detection in generic code where cpusets and hotplug state is already taken care of. All the arch is responsible for is to implement arch_scale_cpu_capacity() and force a full rebuild of the sched_domain hierarchy if capacities are updated, e.g. later in the boot process when cpufreq has initialized. Change-Id: Iaa1723691f11834eb261ccaeb17954e7edad5b32 Signed-off-by: Morten Rasmussen Signed-off-by: Peter Zijlstra (Intel) Cc: Linus Torvalds Cc: Peter Zijlstra Cc: Thomas Gleixner Cc: dietmar.eggemann@arm.com Cc: valentin.schneider@arm.com Cc: vincent.guittot@linaro.org Link: http://lkml.kernel.org/r/1532093554-30504-2-git-send-email-morten.rasmussen@arm.com [ Fixed 'CPU' capitalization. ] Signed-off-by: Ingo Molnar (cherry picked from commit 05484e0984487d42e97c417cbb0697fa9d16e7e9 in tip/sched/core) Signed-off-by: Quentin Perret --- include/linux/sched/topology.h | 6 +-- kernel/sched/topology.c | 81 +++++++++++++++++++++++++++++++--- 2 files changed, 78 insertions(+), 9 deletions(-) diff --git a/include/linux/sched/topology.h b/include/linux/sched/topology.h index 26347741ba50..6b9976180c1e 100644 --- a/include/linux/sched/topology.h +++ b/include/linux/sched/topology.h @@ -23,10 +23,10 @@ #define SD_BALANCE_FORK 0x0008 /* Balance on fork, clone */ #define SD_BALANCE_WAKE 0x0010 /* Balance on wakeup */ #define SD_WAKE_AFFINE 0x0020 /* Wake task to waking CPU */ -#define SD_ASYM_CPUCAPACITY 0x0040 /* Groups have different max cpu capacities */ -#define SD_SHARE_CPUCAPACITY 0x0080 /* Domain members share cpu capacity */ +#define SD_ASYM_CPUCAPACITY 0x0040 /* Domain members have different CPU capacities */ +#define SD_SHARE_CPUCAPACITY 0x0080 /* Domain members share CPU capacity */ #define SD_SHARE_POWERDOMAIN 0x0100 /* Domain members share power domain */ -#define SD_SHARE_PKG_RESOURCES 0x0200 /* Domain members share cpu pkg resources */ +#define SD_SHARE_PKG_RESOURCES 0x0200 /* Domain members share CPU pkg resources */ #define SD_SERIALIZE 0x0400 /* Only a single load balancing instance */ #define SD_ASYM_PACKING 0x0800 /* Place busy groups earlier in the domain */ #define SD_PREFER_SIBLING 0x1000 /* Prefer to place tasks in a sibling domain */ diff --git a/kernel/sched/topology.c b/kernel/sched/topology.c index 505a41c42b96..5c4d583d53ee 100644 --- a/kernel/sched/topology.c +++ b/kernel/sched/topology.c @@ -1061,7 +1061,6 @@ static struct cpumask ***sched_domains_numa_masks; * SD_SHARE_PKG_RESOURCES - describes shared caches * SD_NUMA - describes NUMA topologies * SD_SHARE_POWERDOMAIN - describes shared power domain - * SD_ASYM_CPUCAPACITY - describes mixed capacity topologies * * Odd one out, which beside describing the topology has a quirk also * prescribes the desired behaviour that goes along with it: @@ -1073,13 +1072,12 @@ static struct cpumask ***sched_domains_numa_masks; SD_SHARE_PKG_RESOURCES | \ SD_NUMA | \ SD_ASYM_PACKING | \ - SD_ASYM_CPUCAPACITY | \ SD_SHARE_POWERDOMAIN) static struct sched_domain * sd_init(struct sched_domain_topology_level *tl, const struct cpumask *cpu_map, - struct sched_domain *child, int cpu) + struct sched_domain *child, int dflags, int cpu) { struct sd_data *sdd = &tl->data; struct sched_domain *sd = *per_cpu_ptr(sdd->sd, cpu); @@ -1100,6 +1098,9 @@ sd_init(struct sched_domain_topology_level *tl, "wrong sd_flags in topology description\n")) sd_flags &= ~TOPOLOGY_SD_FLAGS; + /* Apply detected topology flags */ + sd_flags |= dflags; + *sd = (struct sched_domain){ .min_interval = sd_weight, .max_interval = 2*sd_weight, @@ -1604,9 +1605,9 @@ static void __sdt_free(const struct cpumask *cpu_map) static struct sched_domain *build_sched_domain(struct sched_domain_topology_level *tl, const struct cpumask *cpu_map, struct sched_domain_attr *attr, - struct sched_domain *child, int cpu) + struct sched_domain *child, int dflags, int cpu) { - struct sched_domain *sd = sd_init(tl, cpu_map, child, cpu); + struct sched_domain *sd = sd_init(tl, cpu_map, child, dflags, cpu); if (child) { sd->level = child->level + 1; @@ -1632,6 +1633,65 @@ static struct sched_domain *build_sched_domain(struct sched_domain_topology_leve return sd; } +/* + * Find the sched_domain_topology_level where all CPU capacities are visible + * for all CPUs. + */ +static struct sched_domain_topology_level +*asym_cpu_capacity_level(const struct cpumask *cpu_map) +{ + int i, j, asym_level = 0; + bool asym = false; + struct sched_domain_topology_level *tl, *asym_tl = NULL; + unsigned long cap; + + /* Is there any asymmetry? */ + cap = arch_scale_cpu_capacity(NULL, cpumask_first(cpu_map)); + + for_each_cpu(i, cpu_map) { + if (arch_scale_cpu_capacity(NULL, i) != cap) { + asym = true; + break; + } + } + + if (!asym) + return NULL; + + /* + * Examine topology from all CPU's point of views to detect the lowest + * sched_domain_topology_level where a highest capacity CPU is visible + * to everyone. + */ + for_each_cpu(i, cpu_map) { + unsigned long max_capacity = arch_scale_cpu_capacity(NULL, i); + int tl_id = 0; + + for_each_sd_topology(tl) { + if (tl_id < asym_level) + goto next_level; + + for_each_cpu_and(j, tl->mask(i), cpu_map) { + unsigned long capacity; + + capacity = arch_scale_cpu_capacity(NULL, j); + + if (capacity <= max_capacity) + continue; + + max_capacity = capacity; + asym_level = tl_id; + asym_tl = tl; + } +next_level: + tl_id++; + } + } + + return asym_tl; +} + + /* * Build sched domains for a given set of CPUs and attach the sched domains * to the individual CPUs @@ -1644,18 +1704,27 @@ build_sched_domains(const struct cpumask *cpu_map, struct sched_domain_attr *att struct s_data d; struct rq *rq = NULL; int i, ret = -ENOMEM; + struct sched_domain_topology_level *tl_asym; alloc_state = __visit_domain_allocation_hell(&d, cpu_map); if (alloc_state != sa_rootdomain) goto error; + tl_asym = asym_cpu_capacity_level(cpu_map); + /* Set up domains for CPUs specified by the cpu_map: */ for_each_cpu(i, cpu_map) { struct sched_domain_topology_level *tl; sd = NULL; for_each_sd_topology(tl) { - sd = build_sched_domain(tl, cpu_map, attr, sd, i); + int dflags = 0; + + if (tl == tl_asym) + dflags |= SD_ASYM_CPUCAPACITY; + + sd = build_sched_domain(tl, cpu_map, attr, sd, dflags, i); + if (tl == sched_domain_topology) *per_cpu_ptr(d.sd, i) = sd; if (tl->flags & SDTL_OVERLAP) From 333a1475244670848372f60da3d896b73b09e0e9 Mon Sep 17 00:00:00 2001 From: Morten Rasmussen Date: Fri, 20 Jul 2018 14:32:32 +0100 Subject: [PATCH 0458/1103] UPSTREAM: sched/topology, drivers/base/arch_topology: Rebuild the sched_domain hierarchy when capacities change The setting of SD_ASYM_CPUCAPACITY depends on the per-CPU capacities. These might not have their final values when the hierarchy is initially built as the values depend on cpufreq to be initialized or the values being set through sysfs. To ensure that the flags are set correctly we need to rebuild the sched_domain hierarchy whenever the reported per-CPU capacity (arch_scale_cpu_capacity()) changes. This patch ensure that a full sched_domain rebuild happens when CPU capacity changes occur. Change-Id: I7d65b17bca085312db443513bc813bbc729f3445 Signed-off-by: Morten Rasmussen Signed-off-by: Peter Zijlstra (Intel) Cc: Greg Kroah-Hartman Cc: Linus Torvalds Cc: Peter Zijlstra Cc: Thomas Gleixner Cc: dietmar.eggemann@arm.com Cc: valentin.schneider@arm.com Cc: vincent.guittot@linaro.org Link: http://lkml.kernel.org/r/1532093554-30504-3-git-send-email-morten.rasmussen@arm.com Signed-off-by: Ingo Molnar (cherry picked from commit bb1fbdd3c3fd12b612c7d8cdf13bd6bfeebdefa3 in tip/sched/core) Signed-off-by: Quentin Perret --- drivers/base/arch_topology.c | 26 ++++++++++++++++++++++++++ include/linux/arch_topology.h | 1 + 2 files changed, 27 insertions(+) diff --git a/drivers/base/arch_topology.c b/drivers/base/arch_topology.c index e7cb0c6ade81..edfcf8d982e4 100644 --- a/drivers/base/arch_topology.c +++ b/drivers/base/arch_topology.c @@ -15,6 +15,7 @@ #include #include #include +#include DEFINE_PER_CPU(unsigned long, freq_scale) = SCHED_CAPACITY_SCALE; @@ -47,6 +48,9 @@ static ssize_t cpu_capacity_show(struct device *dev, return sprintf(buf, "%lu\n", topology_get_cpu_scale(NULL, cpu->dev.id)); } +static void update_topology_flags_workfn(struct work_struct *work); +static DECLARE_WORK(update_topology_flags_work, update_topology_flags_workfn); + static ssize_t cpu_capacity_store(struct device *dev, struct device_attribute *attr, const char *buf, @@ -72,6 +76,8 @@ static ssize_t cpu_capacity_store(struct device *dev, topology_set_cpu_scale(i, new_capacity); mutex_unlock(&cpu_scale_mutex); + schedule_work(&update_topology_flags_work); + return count; } @@ -96,6 +102,25 @@ static int register_cpu_capacity_sysctl(void) } subsys_initcall(register_cpu_capacity_sysctl); +static int update_topology; + +int topology_update_cpu_topology(void) +{ + return update_topology; +} + +/* + * Updating the sched_domains can't be done directly from cpufreq callbacks + * due to locking, so queue the work for later. + */ +static void update_topology_flags_workfn(struct work_struct *work) +{ + update_topology = 1; + rebuild_sched_domains(); + pr_debug("sched_domain hierarchy rebuilt, flags updated\n"); + update_topology = 0; +} + static u32 capacity_scale; static u32 *raw_capacity; @@ -201,6 +226,7 @@ init_cpu_capacity_callback(struct notifier_block *nb, if (cpumask_empty(cpus_to_visit)) { topology_normalize_cpu_scale(); + schedule_work(&update_topology_flags_work); free_raw_capacity(); pr_debug("cpu_capacity: parsing done\n"); schedule_work(&parsing_done_work); diff --git a/include/linux/arch_topology.h b/include/linux/arch_topology.h index 2b709416de05..d9bdc1a7f4e7 100644 --- a/include/linux/arch_topology.h +++ b/include/linux/arch_topology.h @@ -9,6 +9,7 @@ #include void topology_normalize_cpu_scale(void); +int topology_update_cpu_topology(void); struct device_node; bool topology_parse_cpu_capacity(struct device_node *cpu_node, int cpu); From dd6e7cfe89805bfff89be6ba74c9e13e7f2c63d6 Mon Sep 17 00:00:00 2001 From: Morten Rasmussen Date: Fri, 20 Jul 2018 14:32:33 +0100 Subject: [PATCH 0459/1103] UPSTREAM: sched/topology, arch/arm64: Rebuild the sched_domain hierarchy when the CPU capacity changes Asymmetric CPU capacity can not necessarily be determined accurately at the time the initial sched_domain hierarchy is built during boot. It is therefore necessary to be able to force a full rebuild of the hierarchy later triggered by the arch_topology driver. A full rebuild requires the arch-code to implement arch_update_cpu_topology() which isn't yet implemented for arm64. This patch points the arm64 implementation to arch_topology driver to ensure that full hierarchy rebuild happens when needed. Change-Id: If0bc210d9e091edbcc698ee4a2726c7fd0a2ec44 Signed-off-by: Morten Rasmussen Signed-off-by: Peter Zijlstra (Intel) Cc: Catalin Marinas Cc: Linus Torvalds Cc: Peter Zijlstra Cc: Thomas Gleixner Cc: Will Deacon Cc: dietmar.eggemann@arm.com Cc: valentin.schneider@arm.com Cc: vincent.guittot@linaro.org Link: http://lkml.kernel.org/r/1532093554-30504-4-git-send-email-morten.rasmussen@arm.com Signed-off-by: Ingo Molnar (cherry picked from commit 3ba09df4b8b6e3f01ed6381e8fb890840fd0bca3 in tip/sched/core) Signed-off-by: Quentin Perret --- arch/arm64/include/asm/topology.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/arch/arm64/include/asm/topology.h b/arch/arm64/include/asm/topology.h index 49a0fee4f89b..0524f2438649 100644 --- a/arch/arm64/include/asm/topology.h +++ b/arch/arm64/include/asm/topology.h @@ -45,6 +45,9 @@ int pcibus_to_node(struct pci_bus *bus); /* Replace task scheduler's default cpu-invariant accounting */ #define arch_scale_cpu_capacity topology_get_cpu_scale +/* Enable topology flag updates */ +#define arch_update_cpu_topology topology_update_cpu_topology + #include #endif /* _ASM_ARM_TOPOLOGY_H */ From 3d6d7cb3a87e174cb4edc6c989b5da2847e901fb Mon Sep 17 00:00:00 2001 From: Morten Rasmussen Date: Fri, 20 Jul 2018 14:32:34 +0100 Subject: [PATCH 0460/1103] UPSTREAM: sched/topology, arch/arm: Rebuild sched_domain hierarchy when CPU capacity changes Asymmetric CPU capacity can not necessarily be determined accurately at the time the initial sched_domain hierarchy is built during boot. It is therefore necessary to be able to force a full rebuild of the hierarchy later triggered by the arch_topology driver. A full rebuild requires the arch-code to implement arch_update_cpu_topology() which isn't yet implemented for arm. This patch points the arm implementation to arch_topology driver to ensure that full hierarchy rebuild happens when needed. Change-Id: Id533bbf86e39ff65810e9a5404e70f8abb8f5943 Signed-off-by: Morten Rasmussen Signed-off-by: Peter Zijlstra (Intel) Cc: Linus Torvalds Cc: Peter Zijlstra Cc: Russell King Cc: Thomas Gleixner Cc: dietmar.eggemann@arm.com Cc: valentin.schneider@arm.com Cc: vincent.guittot@linaro.org Link: http://lkml.kernel.org/r/1532093554-30504-5-git-send-email-morten.rasmussen@arm.com Signed-off-by: Ingo Molnar (cherry picked from commit e1799a80a4f5a463f252b7325da8bb66dfd55471 in tip/sched/core) Signed-off-by: Quentin Perret --- arch/arm/include/asm/topology.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/arch/arm/include/asm/topology.h b/arch/arm/include/asm/topology.h index 5d88d2f22b2c..2a786f54d8b8 100644 --- a/arch/arm/include/asm/topology.h +++ b/arch/arm/include/asm/topology.h @@ -33,6 +33,9 @@ const struct cpumask *cpu_coregroup_mask(int cpu); /* Replace task scheduler's default cpu-invariant accounting */ #define arch_scale_cpu_capacity topology_get_cpu_scale +/* Enable topology flag updates */ +#define arch_update_cpu_topology topology_update_cpu_topology + #else static inline void init_cpu_topology(void) { } From 2385ae00b445f6dcc7e246dc910221e5c2f23760 Mon Sep 17 00:00:00 2001 From: Morten Rasmussen Date: Wed, 4 Jul 2018 11:17:39 +0100 Subject: [PATCH 0461/1103] UPSTREAM: sched/topology: Add static_key for asymmetric CPU capacity optimizations The existing asymmetric CPU capacity code should cause minimal overhead for others. Putting it behind a static_key, it has been done for SMT optimizations, would make it easier to extend and improve without causing harm to others moving forward. Change-Id: Ibed747ab20aeb2338ac2b9766a0105777f277f57 Signed-off-by: Morten Rasmussen Signed-off-by: Peter Zijlstra (Intel) Cc: Linus Torvalds Cc: Peter Zijlstra Cc: Thomas Gleixner Cc: dietmar.eggemann@arm.com Cc: gaku.inami.xh@renesas.com Cc: valentin.schneider@arm.com Cc: vincent.guittot@linaro.org Link: http://lkml.kernel.org/r/1530699470-29808-2-git-send-email-morten.rasmussen@arm.com Signed-off-by: Ingo Molnar (cherry picked from commit df054e8445a4011e3d693c2268129c0456108663 in tip/sched/core) Signed-off-by: Quentin Perret Change-Id: I09e315ff897ffba65c7cb82715ab0d6c20b76e02 --- kernel/sched/fair.c | 3 +++ kernel/sched/sched.h | 1 + kernel/sched/topology.c | 9 ++++++++- 3 files changed, 12 insertions(+), 1 deletion(-) diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c index c24f3cfbf012..3d3cf1929208 100644 --- a/kernel/sched/fair.c +++ b/kernel/sched/fair.c @@ -6265,6 +6265,9 @@ static int wake_cap(struct task_struct *p, int cpu, int prev_cpu) { long min_cap, max_cap; + if (!static_branch_unlikely(&sched_asym_cpucapacity)) + return 0; + min_cap = min(capacity_orig_of(prev_cpu), capacity_orig_of(cpu)); max_cap = cpu_rq(cpu)->rd->max_cpu_capacity; diff --git a/kernel/sched/sched.h b/kernel/sched/sched.h index 455fa330de04..feaf39d70400 100644 --- a/kernel/sched/sched.h +++ b/kernel/sched/sched.h @@ -1186,6 +1186,7 @@ DECLARE_PER_CPU(int, sd_llc_id); DECLARE_PER_CPU(struct sched_domain_shared *, sd_llc_shared); DECLARE_PER_CPU(struct sched_domain *, sd_numa); DECLARE_PER_CPU(struct sched_domain *, sd_asym); +extern struct static_key_false sched_asym_cpucapacity; struct sched_group_capacity { atomic_t ref; diff --git a/kernel/sched/topology.c b/kernel/sched/topology.c index 5c4d583d53ee..b0cdf5e95bda 100644 --- a/kernel/sched/topology.c +++ b/kernel/sched/topology.c @@ -398,6 +398,7 @@ DEFINE_PER_CPU(int, sd_llc_id); DEFINE_PER_CPU(struct sched_domain_shared *, sd_llc_shared); DEFINE_PER_CPU(struct sched_domain *, sd_numa); DEFINE_PER_CPU(struct sched_domain *, sd_asym); +DEFINE_STATIC_KEY_FALSE(sched_asym_cpucapacity); static void update_top_cache_domain(int cpu) { @@ -1705,6 +1706,7 @@ build_sched_domains(const struct cpumask *cpu_map, struct sched_domain_attr *att struct rq *rq = NULL; int i, ret = -ENOMEM; struct sched_domain_topology_level *tl_asym; + bool has_asym = false; alloc_state = __visit_domain_allocation_hell(&d, cpu_map); if (alloc_state != sa_rootdomain) @@ -1720,8 +1722,10 @@ build_sched_domains(const struct cpumask *cpu_map, struct sched_domain_attr *att for_each_sd_topology(tl) { int dflags = 0; - if (tl == tl_asym) + if (tl == tl_asym) { dflags |= SD_ASYM_CPUCAPACITY; + has_asym = true; + } sd = build_sched_domain(tl, cpu_map, attr, sd, dflags, i); @@ -1773,6 +1777,9 @@ build_sched_domains(const struct cpumask *cpu_map, struct sched_domain_attr *att } rcu_read_unlock(); + if (has_asym) + static_branch_enable_cpuslocked(&sched_asym_cpucapacity); + if (rq && sched_debug_enabled) { pr_info("root domain span: %*pbl (max cpu_capacity = %lu)\n", cpumask_pr_args(cpu_map), rq->rd->max_cpu_capacity); From abc3db7a43daedd5f759b85eaf06537c8f87d893 Mon Sep 17 00:00:00 2001 From: Morten Rasmussen Date: Wed, 4 Jul 2018 11:17:40 +0100 Subject: [PATCH 0462/1103] UPSTREAM: sched/fair: Add 'group_misfit_task' load-balance type To maximize throughput in systems with asymmetric CPU capacities (e.g. ARM big.LITTLE) load-balancing has to consider task and CPU utilization as well as per-CPU compute capacity when load-balancing in addition to the current average load based load-balancing policy. Tasks with high utilization that are scheduled on a lower capacity CPU need to be identified and migrated to a higher capacity CPU if possible to maximize throughput. To implement this additional policy an additional group_type (load-balance scenario) is added: 'group_misfit_task'. This represents scenarios where a sched_group has one or more tasks that are not suitable for its per-CPU capacity. 'group_misfit_task' is only considered if the system is not overloaded or imbalanced ('group_imbalanced' or 'group_overloaded'). Identifying misfit tasks requires the rq lock to be held. To avoid taking remote rq locks to examine source sched_groups for misfit tasks, each CPU is responsible for tracking misfit tasks themselves and update the rq->misfit_task flag. This means checking task utilization when tasks are scheduled and on sched_tick. Change-Id: I68514339e47427b38c0b77341c662e162225ad26 Signed-off-by: Morten Rasmussen Signed-off-by: Peter Zijlstra (Intel) Cc: Linus Torvalds Cc: Peter Zijlstra Cc: Thomas Gleixner Cc: dietmar.eggemann@arm.com Cc: gaku.inami.xh@renesas.com Cc: valentin.schneider@arm.com Cc: vincent.guittot@linaro.org Link: http://lkml.kernel.org/r/1530699470-29808-3-git-send-email-morten.rasmussen@arm.com Signed-off-by: Ingo Molnar (cherry picked from commit 3b1baa6496e6b7ad016342a9d256bdfb072ce902 in tip/sched/core) Signed-off-by: Quentin Perret --- kernel/sched/fair.c | 54 +++++++++++++++++++++++++++++++++++++------- kernel/sched/sched.h | 2 ++ 2 files changed, 48 insertions(+), 8 deletions(-) diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c index 3d3cf1929208..5be89a46fe65 100644 --- a/kernel/sched/fair.c +++ b/kernel/sched/fair.c @@ -693,6 +693,7 @@ static u64 sched_vslice(struct cfs_rq *cfs_rq, struct sched_entity *se) static int select_idle_sibling(struct task_struct *p, int prev_cpu, int cpu); static unsigned long task_h_load(struct task_struct *p); +static unsigned long capacity_of(int cpu); /* Give new sched_entity start runnable values to heavy its load in infant time */ void init_entity_runnable_average(struct sched_entity *se) @@ -1457,7 +1458,6 @@ bool should_numa_migrate_memory(struct task_struct *p, struct page * page, static unsigned long weighted_cpuload(struct rq *rq); static unsigned long source_load(int cpu, int type); static unsigned long target_load(int cpu, int type); -static unsigned long capacity_of(int cpu); /* Cached statistics for all CPUs within a node */ struct numa_stats { @@ -3724,6 +3724,29 @@ util_est_dequeue(struct cfs_rq *cfs_rq, struct task_struct *p, bool task_sleep) WRITE_ONCE(p->se.avg.util_est, ue); } +static inline int task_fits_capacity(struct task_struct *p, long capacity) +{ + return capacity * 1024 > task_util_est(p) * capacity_margin; +} + +static inline void update_misfit_status(struct task_struct *p, struct rq *rq) +{ + if (!static_branch_unlikely(&sched_asym_cpucapacity)) + return; + + if (!p) { + rq->misfit_task_load = 0; + return; + } + + if (task_fits_capacity(p, capacity_of(cpu_of(rq)))) { + rq->misfit_task_load = 0; + return; + } + + rq->misfit_task_load = task_h_load(p); +} + #else /* CONFIG_SMP */ #define UPDATE_TG 0x0 @@ -3753,6 +3776,7 @@ util_est_enqueue(struct cfs_rq *cfs_rq, struct task_struct *p) {} static inline void util_est_dequeue(struct cfs_rq *cfs_rq, struct task_struct *p, bool task_sleep) {} +static inline void update_misfit_status(struct task_struct *p, struct rq *rq) {} #endif /* CONFIG_SMP */ @@ -6278,7 +6302,7 @@ static int wake_cap(struct task_struct *p, int cpu, int prev_cpu) /* Bring task utilization in sync with prev_cpu */ sync_entity_load_avg(&p->se); - return min_cap * 1024 < task_util(p) * capacity_margin; + return !task_fits_capacity(p, min_cap); } /* @@ -6697,9 +6721,12 @@ done: __maybe_unused; if (hrtick_enabled(rq)) hrtick_start_fair(rq, p); + update_misfit_status(p, rq); + return p; idle: + update_misfit_status(NULL, rq); new_tasks = idle_balance(rq, rf); /* @@ -6905,6 +6932,13 @@ static unsigned long __read_mostly max_load_balance_interval = HZ/10; enum fbq_type { regular, remote, all }; +enum group_type { + group_other = 0, + group_misfit_task, + group_imbalanced, + group_overloaded, +}; + #define LBF_ALL_PINNED 0x01 #define LBF_NEED_BREAK 0x02 #define LBF_DST_PINNED 0x04 @@ -7478,12 +7512,6 @@ static unsigned long task_h_load(struct task_struct *p) /********** Helpers for find_busiest_group ************************/ -enum group_type { - group_other = 0, - group_imbalanced, - group_overloaded, -}; - /* * sg_lb_stats - stats of a sched_group required for load_balancing */ @@ -7499,6 +7527,7 @@ struct sg_lb_stats { unsigned int group_weight; enum group_type group_type; int group_no_capacity; + unsigned long group_misfit_task_load; /* A CPU has a task too big for its capacity */ #ifdef CONFIG_NUMA_BALANCING unsigned int nr_numa_running; unsigned int nr_preferred_running; @@ -7791,6 +7820,9 @@ group_type group_classify(struct sched_group *group, if (sg_imbalanced(group)) return group_imbalanced; + if (sgs->group_misfit_task_load) + return group_misfit_task; + return group_other; } @@ -7865,6 +7897,10 @@ static inline void update_sg_lb_stats(struct lb_env *env, */ if (!nr_running && idle_cpu(i)) sgs->idle_cpus++; + + if (env->sd->flags & SD_ASYM_CPUCAPACITY && + sgs->group_misfit_task_load < rq->misfit_task_load) + sgs->group_misfit_task_load = rq->misfit_task_load; } /* Adjust by relative CPU capacity of the group */ @@ -9646,6 +9682,8 @@ static void task_tick_fair(struct rq *rq, struct task_struct *curr, int queued) if (static_branch_unlikely(&sched_numa_balancing)) task_tick_numa(rq, curr); + + update_misfit_status(curr, rq); } /* diff --git a/kernel/sched/sched.h b/kernel/sched/sched.h index feaf39d70400..9566d6a2ff33 100644 --- a/kernel/sched/sched.h +++ b/kernel/sched/sched.h @@ -843,6 +843,8 @@ struct rq { unsigned char idle_balance; + unsigned long misfit_task_load; + /* For active balancing */ int active_balance; int push_cpu; From 35377389a6506be0a7ea5405960ff9beb100489f Mon Sep 17 00:00:00 2001 From: Morten Rasmussen Date: Wed, 4 Jul 2018 11:17:41 +0100 Subject: [PATCH 0463/1103] UPSTREAM: sched/fair: Add sched_group per-CPU max capacity The current sg->min_capacity tracks the lowest per-CPU compute capacity available in the sched_group when rt/irq pressure is taken into account. Minimum capacity isn't the ideal metric for tracking if a sched_group needs offloading to another sched_group for some scenarios, e.g. a sched_group with multiple CPUs if only one is under heavy pressure. Tracking maximum capacity isn't perfect either but a better choice for some situations as it indicates that the sched_group definitely compute capacity constrained either due to rt/irq pressure on all CPUs or asymmetric CPU capacities (e.g. big.LITTLE). Change-Id: If9c2ec9b6c34d002715d08f13f795cc8f27b9c72 Signed-off-by: Morten Rasmussen Signed-off-by: Peter Zijlstra (Intel) Cc: Linus Torvalds Cc: Peter Zijlstra Cc: Thomas Gleixner Cc: dietmar.eggemann@arm.com Cc: gaku.inami.xh@renesas.com Cc: valentin.schneider@arm.com Cc: vincent.guittot@linaro.org Link: http://lkml.kernel.org/r/1530699470-29808-4-git-send-email-morten.rasmussen@arm.com Signed-off-by: Ingo Molnar (cherry picked from commit e3d6d0cb66f2351cbfd09fbae04eb9804afe9577 in tip/sched/core) Signed-off-by: Quentin Perret --- kernel/sched/fair.c | 24 ++++++++++++++++++++---- kernel/sched/sched.h | 1 + kernel/sched/topology.c | 2 ++ 3 files changed, 23 insertions(+), 4 deletions(-) diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c index 5be89a46fe65..91f5175b5c21 100644 --- a/kernel/sched/fair.c +++ b/kernel/sched/fair.c @@ -7636,13 +7636,14 @@ static void update_cpu_capacity(struct sched_domain *sd, int cpu) cpu_rq(cpu)->cpu_capacity = capacity; sdg->sgc->capacity = capacity; sdg->sgc->min_capacity = capacity; + sdg->sgc->max_capacity = capacity; } void update_group_capacity(struct sched_domain *sd, int cpu) { struct sched_domain *child = sd->child; struct sched_group *group, *sdg = sd->groups; - unsigned long capacity, min_capacity; + unsigned long capacity, min_capacity, max_capacity; unsigned long interval; interval = msecs_to_jiffies(sd->balance_interval); @@ -7656,6 +7657,7 @@ void update_group_capacity(struct sched_domain *sd, int cpu) capacity = 0; min_capacity = ULONG_MAX; + max_capacity = 0; if (child->flags & SD_OVERLAP) { /* @@ -7686,6 +7688,7 @@ void update_group_capacity(struct sched_domain *sd, int cpu) } min_capacity = min(capacity, min_capacity); + max_capacity = max(capacity, max_capacity); } } else { /* @@ -7699,12 +7702,14 @@ void update_group_capacity(struct sched_domain *sd, int cpu) capacity += sgc->capacity; min_capacity = min(sgc->min_capacity, min_capacity); + max_capacity = max(sgc->max_capacity, max_capacity); group = group->next; } while (group != child->groups); } sdg->sgc->capacity = capacity; sdg->sgc->min_capacity = min_capacity; + sdg->sgc->max_capacity = max_capacity; } /* @@ -7800,16 +7805,27 @@ group_is_overloaded(struct lb_env *env, struct sg_lb_stats *sgs) } /* - * group_smaller_cpu_capacity: Returns true if sched_group sg has smaller + * group_smaller_min_cpu_capacity: Returns true if sched_group sg has smaller * per-CPU capacity than sched_group ref. */ static inline bool -group_smaller_cpu_capacity(struct sched_group *sg, struct sched_group *ref) +group_smaller_min_cpu_capacity(struct sched_group *sg, struct sched_group *ref) { return sg->sgc->min_capacity * capacity_margin < ref->sgc->min_capacity * 1024; } +/* + * group_smaller_max_cpu_capacity: Returns true if sched_group sg has smaller + * per-CPU capacity_orig than sched_group ref. + */ +static inline bool +group_smaller_max_cpu_capacity(struct sched_group *sg, struct sched_group *ref) +{ + return sg->sgc->max_capacity * capacity_margin < + ref->sgc->max_capacity * 1024; +} + static inline enum group_type group_classify(struct sched_group *group, struct sg_lb_stats *sgs) @@ -7955,7 +7971,7 @@ static bool update_sd_pick_busiest(struct lb_env *env, * power/energy consequences are not considered. */ if (sgs->sum_nr_running <= sgs->group_weight && - group_smaller_cpu_capacity(sds->local, sg)) + group_smaller_min_cpu_capacity(sds->local, sg)) return false; asym_packing: diff --git a/kernel/sched/sched.h b/kernel/sched/sched.h index 9566d6a2ff33..4987c7b3a51a 100644 --- a/kernel/sched/sched.h +++ b/kernel/sched/sched.h @@ -1198,6 +1198,7 @@ struct sched_group_capacity { */ unsigned long capacity; unsigned long min_capacity; /* Min per-CPU capacity in group */ + unsigned long max_capacity; /* Max per-CPU capacity in group */ unsigned long next_update; int imbalance; /* XXX unrelated to capacity but shared group state */ diff --git a/kernel/sched/topology.c b/kernel/sched/topology.c index b0cdf5e95bda..2536e1b938f9 100644 --- a/kernel/sched/topology.c +++ b/kernel/sched/topology.c @@ -693,6 +693,7 @@ static void init_overlap_sched_group(struct sched_domain *sd, sg_span = sched_group_span(sg); sg->sgc->capacity = SCHED_CAPACITY_SCALE * cpumask_weight(sg_span); sg->sgc->min_capacity = SCHED_CAPACITY_SCALE; + sg->sgc->max_capacity = SCHED_CAPACITY_SCALE; } static int @@ -852,6 +853,7 @@ static struct sched_group *get_group(int cpu, struct sd_data *sdd) sg->sgc->capacity = SCHED_CAPACITY_SCALE * cpumask_weight(sched_group_span(sg)); sg->sgc->min_capacity = SCHED_CAPACITY_SCALE; + sg->sgc->max_capacity = SCHED_CAPACITY_SCALE; return sg; } From bd41f2a41d4ab85f279a423565196bca0d027b30 Mon Sep 17 00:00:00 2001 From: Morten Rasmussen Date: Wed, 4 Jul 2018 11:17:42 +0100 Subject: [PATCH 0464/1103] UPSTREAM: sched/fair: Consider misfit tasks when load-balancing On asymmetric CPU capacity systems load intensive tasks can end up on CPUs that don't suit their compute demand. In this scenarios 'misfit' tasks should be migrated to CPUs with higher compute capacity to ensure better throughput. group_misfit_task indicates this scenario, but tweaks to the load-balance code are needed to make the migrations happen. Misfit balancing only makes sense between a source group of lower per-CPU capacity and destination group of higher compute capacity. Otherwise, misfit balancing is ignored. group_misfit_task has lowest priority so any imbalance due to overload is dealt with first. The modifications are: 1. Only pick a group containing misfit tasks as the busiest group if the destination group has higher capacity and has spare capacity. 2. When the busiest group is a 'misfit' group, skip the usual average load and group capacity checks. 3. Set the imbalance for 'misfit' balancing sufficiently high for a task to be pulled ignoring average load. 4. Pick the CPU with the highest misfit load as the source CPU. 5. If the misfit task is alone on the source CPU, go for active balancing. Change-Id: I7a3b1bc27960599fdb2085584e85d6772f426d13 Signed-off-by: Morten Rasmussen Signed-off-by: Peter Zijlstra (Intel) Cc: Linus Torvalds Cc: Peter Zijlstra Cc: Thomas Gleixner Cc: dietmar.eggemann@arm.com Cc: gaku.inami.xh@renesas.com Cc: valentin.schneider@arm.com Cc: vincent.guittot@linaro.org Link: http://lkml.kernel.org/r/1530699470-29808-5-git-send-email-morten.rasmussen@arm.com Signed-off-by: Ingo Molnar (cherry picked from commit cad68e552e7774b68ae6a2c5fedb792936098b72 in tip/sched/core) Signed-off-by: Quentin Perret --- kernel/sched/fair.c | 51 +++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 49 insertions(+), 2 deletions(-) diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c index 91f5175b5c21..681a426f0196 100644 --- a/kernel/sched/fair.c +++ b/kernel/sched/fair.c @@ -6969,6 +6969,7 @@ struct lb_env { unsigned int loop_max; enum fbq_type fbq_type; + enum group_type src_grp_type; struct list_head tasks; }; @@ -7952,6 +7953,17 @@ static bool update_sd_pick_busiest(struct lb_env *env, { struct sg_lb_stats *busiest = &sds->busiest_stat; + /* + * Don't try to pull misfit tasks we can't help. + * We can use max_capacity here as reduction in capacity on some + * CPUs in the group should either be possible to resolve + * internally or be covered by avg_load imbalance (eventually). + */ + if (sgs->group_type == group_misfit_task && + (!group_smaller_max_cpu_capacity(sg, sds->local) || + !group_has_capacity(env, &sds->local_stat))) + return false; + if (sgs->group_type > busiest->group_type) return true; @@ -7974,6 +7986,13 @@ static bool update_sd_pick_busiest(struct lb_env *env, group_smaller_min_cpu_capacity(sds->local, sg)) return false; + /* + * If we have more than one misfit sg go with the biggest misfit. + */ + if (sgs->group_type == group_misfit_task && + sgs->group_misfit_task_load < busiest->group_misfit_task_load) + return false; + asym_packing: /* This is the busiest node in its class. */ if (!(env->sd->flags & SD_ASYM_PACKING)) @@ -8271,8 +8290,9 @@ static inline void calculate_imbalance(struct lb_env *env, struct sd_lb_stats *s * factors in sg capacity and sgs with smaller group_type are * skipped when updating the busiest sg: */ - if (busiest->avg_load <= sds->avg_load || - local->avg_load >= sds->avg_load) { + if (busiest->group_type != group_misfit_task && + (busiest->avg_load <= sds->avg_load || + local->avg_load >= sds->avg_load)) { env->imbalance = 0; return fix_small_imbalance(env, sds); } @@ -8306,6 +8326,12 @@ static inline void calculate_imbalance(struct lb_env *env, struct sd_lb_stats *s (sds->avg_load - local->avg_load) * local->group_capacity ) / SCHED_CAPACITY_SCALE; + /* Boost imbalance to allow misfit task to be balanced. */ + if (busiest->group_type == group_misfit_task) { + env->imbalance = max_t(long, env->imbalance, + busiest->group_misfit_task_load); + } + /* * if *imbalance is less than the average load per runnable task * there is no guarantee that any tasks will be moved so we'll have @@ -8372,6 +8398,10 @@ static struct sched_group *find_busiest_group(struct lb_env *env) busiest->group_no_capacity) goto force_balance; + /* Misfit tasks should be dealt with regardless of the avg load */ + if (busiest->group_type == group_misfit_task) + goto force_balance; + /* * If the local group is busier than the selected busiest group * don't try and pull any tasks. @@ -8409,6 +8439,7 @@ static struct sched_group *find_busiest_group(struct lb_env *env) force_balance: /* Looks like there is an imbalance. Compute it */ + env->src_grp_type = busiest->group_type; calculate_imbalance(env, &sds); return env->imbalance ? sds.busiest : NULL; @@ -8456,6 +8487,19 @@ static struct rq *find_busiest_queue(struct lb_env *env, if (rt > env->fbq_type) continue; + /* + * For ASYM_CPUCAPACITY domains with misfit tasks we simply + * seek the "biggest" misfit task. + */ + if (env->src_grp_type == group_misfit_task) { + if (rq->misfit_task_load > busiest_load) { + busiest_load = rq->misfit_task_load; + busiest = rq; + } + + continue; + } + capacity = capacity_of(i); wl = weighted_cpuload(rq); @@ -8525,6 +8569,9 @@ static int need_active_balance(struct lb_env *env) return 1; } + if (env->src_grp_type == group_misfit_task) + return 1; + return unlikely(sd->nr_balance_failed > sd->cache_nice_tries+2); } From 4f40db0bad314f104924192ccc650de213557ea1 Mon Sep 17 00:00:00 2001 From: Valentin Schneider Date: Wed, 4 Jul 2018 11:17:43 +0100 Subject: [PATCH 0465/1103] UPSTREAM: sched/fair: Kick nohz balance if rq->misfit_task_load There already are a few conditions in nohz_kick_needed() to ensure a nohz kick is triggered, but they are not enough for some misfit task scenarios. Excluding asym packing, those are: - rq->nr_running >=2: Not relevant here because we are running a misfit task, it needs to be migrated regardless and potentially through active balance. - sds->nr_busy_cpus > 1: If there is only the misfit task being run on a group of low capacity CPUs, this will be evaluated to False. - rq->cfs.h_nr_running >=1 && check_cpu_capacity(): Not relevant here, misfit task needs to be migrated regardless of rt/IRQ pressure As such, this commit adds an rq->misfit_task_load condition to trigger a nohz kick. The idea to kick a nohz balance for misfit tasks originally came from Leo Yan , and a similar patch was submitted for the Android Common Kernel - see: https://lists.linaro.org/pipermail/eas-dev/2016-September/000551.html Change-Id: I93840d0c3eb7910b044140959c260a9faf603eb5 Signed-off-by: Valentin Schneider Signed-off-by: Morten Rasmussen Signed-off-by: Peter Zijlstra (Intel) Cc: Linus Torvalds Cc: Peter Zijlstra Cc: Thomas Gleixner Cc: dietmar.eggemann@arm.com Cc: gaku.inami.xh@renesas.com Cc: vincent.guittot@linaro.org Link: http://lkml.kernel.org/r/1530699470-29808-6-git-send-email-morten.rasmussen@arm.com Signed-off-by: Ingo Molnar (cherry picked from commit 5fbdfae5221a5208ed8e7653fc1c4b31de420f74 in tip/sched/core) Signed-off-by: Quentin Perret --- kernel/sched/fair.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c index 681a426f0196..35805df12c49 100644 --- a/kernel/sched/fair.c +++ b/kernel/sched/fair.c @@ -9214,7 +9214,7 @@ static void nohz_balancer_kick(struct rq *rq) if (time_before(now, nohz.next_balance)) goto out; - if (rq->nr_running >= 2) { + if (rq->nr_running >= 2 || rq->misfit_task_load) { flags = NOHZ_KICK_MASK; goto out; } From 797a177538aa3524ef7dd49b7c8bdaeb291d8005 Mon Sep 17 00:00:00 2001 From: Valentin Schneider Date: Wed, 4 Jul 2018 11:17:44 +0100 Subject: [PATCH 0466/1103] UPSTREAM: sched/fair: Change 'prefer_sibling' type to bool This variable is entirely local to update_sd_lb_stats, so we can safely change its type and slightly clean up its initialisation. Change-Id: Iefb32ff774541d4e057b74fe8357221529e0ac7c Signed-off-by: Valentin Schneider Signed-off-by: Morten Rasmussen Signed-off-by: Peter Zijlstra (Intel) Cc: Linus Torvalds Cc: Peter Zijlstra Cc: Thomas Gleixner Cc: dietmar.eggemann@arm.com Cc: gaku.inami.xh@renesas.com Cc: vincent.guittot@linaro.org Link: http://lkml.kernel.org/r/1530699470-29808-7-git-send-email-morten.rasmussen@arm.com Signed-off-by: Ingo Molnar (cherry picked from commit dbbad719449e06d73db21598d6eee178f7a54b3b in tip/sched/core) Signed-off-by: Quentin Perret --- kernel/sched/fair.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c index 35805df12c49..5cd085c26e88 100644 --- a/kernel/sched/fair.c +++ b/kernel/sched/fair.c @@ -8061,11 +8061,9 @@ static inline void update_sd_lb_stats(struct lb_env *env, struct sd_lb_stats *sd struct sched_group *sg = env->sd->groups; struct sg_lb_stats *local = &sds->local_stat; struct sg_lb_stats tmp_sgs; - int load_idx, prefer_sibling = 0; + int load_idx; bool overload = false; - - if (child && child->flags & SD_PREFER_SIBLING) - prefer_sibling = 1; + bool prefer_sibling = child && child->flags & SD_PREFER_SIBLING; #ifdef CONFIG_NO_HZ_COMMON if (env->idle == CPU_NEWLY_IDLE && READ_ONCE(nohz.has_blocked)) From 16d83f3cb801b1b89ecf3af8369614c19bec66dc Mon Sep 17 00:00:00 2001 From: Valentin Schneider Date: Wed, 4 Jul 2018 11:17:45 +0100 Subject: [PATCH 0467/1103] UPSTREAM: sched/core: Change root_domain->overload type to int sizeof(_Bool) is implementation defined, so let's just go with 'int' as is done for other structures e.g. sched_domain_shared->has_idle_cores. The local 'overload' variable used in update_sd_lb_stats can remain bool, as it won't impact any struct layout and can be assigned to the root_domain field. Change-Id: Iaaafc128f3b6b940bb04857e5d175a5e281c759a Signed-off-by: Valentin Schneider Signed-off-by: Morten Rasmussen Signed-off-by: Peter Zijlstra (Intel) Cc: Linus Torvalds Cc: Peter Zijlstra Cc: Thomas Gleixner Cc: dietmar.eggemann@arm.com Cc: gaku.inami.xh@renesas.com Cc: vincent.guittot@linaro.org Link: http://lkml.kernel.org/r/1530699470-29808-8-git-send-email-morten.rasmussen@arm.com Signed-off-by: Ingo Molnar (cherry picked from commit 575638d1047eb057a5cdf95cc0b3c084e1279508 in tip/sched/core) Signed-off-by: Quentin Perret --- kernel/sched/sched.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/kernel/sched/sched.h b/kernel/sched/sched.h index 4987c7b3a51a..b2d8967efe44 100644 --- a/kernel/sched/sched.h +++ b/kernel/sched/sched.h @@ -716,7 +716,7 @@ struct root_domain { cpumask_var_t online; /* Indicate more than one runnable task for any CPU */ - bool overload; + int overload; /* * The bit corresponding to a CPU gets set here if such CPU has more @@ -1699,7 +1699,7 @@ static inline void add_nr_running(struct rq *rq, unsigned count) if (prev_nr < 2 && rq->nr_running >= 2) { #ifdef CONFIG_SMP if (!rq->rd->overload) - rq->rd->overload = true; + rq->rd->overload = 1; #endif } From 1090fcecd9171f5494c3a12944a782ddcde1e2b8 Mon Sep 17 00:00:00 2001 From: Valentin Schneider Date: Wed, 4 Jul 2018 11:17:46 +0100 Subject: [PATCH 0468/1103] UPSTREAM: sched/fair: Wrap rq->rd->overload accesses with READ/WRITE_ONCE() This variable can be read and set locklessly within update_sd_lb_stats(). As such, READ/WRITE_ONCE() are added to make sure nothing terribly wrong can happen because of the compiler. Change-Id: I8120c4a62474e0c8d8babdbb24880385c864f793 Signed-off-by: Valentin Schneider Signed-off-by: Morten Rasmussen Signed-off-by: Peter Zijlstra (Intel) Cc: Linus Torvalds Cc: Peter Zijlstra Cc: Thomas Gleixner Cc: dietmar.eggemann@arm.com Cc: gaku.inami.xh@renesas.com Cc: vincent.guittot@linaro.org Link: http://lkml.kernel.org/r/1530699470-29808-9-git-send-email-morten.rasmussen@arm.com Signed-off-by: Ingo Molnar (cherry picked from commit e90c8fe15a3bf93a23088bcf1a56a0fa391d4e50 in tip/sched/core) Signed-off-by: Quentin Perret --- kernel/sched/fair.c | 6 +++--- kernel/sched/sched.h | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c index 5cd085c26e88..fd0283c25a12 100644 --- a/kernel/sched/fair.c +++ b/kernel/sched/fair.c @@ -8137,8 +8137,8 @@ static inline void update_sd_lb_stats(struct lb_env *env, struct sd_lb_stats *sd if (!env->sd->parent) { /* update overload indicator if we are at root domain */ - if (env->dst_rq->rd->overload != overload) - env->dst_rq->rd->overload = overload; + if (READ_ONCE(env->dst_rq->rd->overload) != overload) + WRITE_ONCE(env->dst_rq->rd->overload, overload); } } @@ -9581,7 +9581,7 @@ static int idle_balance(struct rq *this_rq, struct rq_flags *rf) rq_unpin_lock(this_rq, rf); if (this_rq->avg_idle < sysctl_sched_migration_cost || - !this_rq->rd->overload) { + !READ_ONCE(this_rq->rd->overload)) { rcu_read_lock(); sd = rcu_dereference_check_sched_domain(this_rq->sd); diff --git a/kernel/sched/sched.h b/kernel/sched/sched.h index b2d8967efe44..1f7d176d0d4a 100644 --- a/kernel/sched/sched.h +++ b/kernel/sched/sched.h @@ -1698,8 +1698,8 @@ static inline void add_nr_running(struct rq *rq, unsigned count) if (prev_nr < 2 && rq->nr_running >= 2) { #ifdef CONFIG_SMP - if (!rq->rd->overload) - rq->rd->overload = 1; + if (!READ_ONCE(rq->rd->overload)) + WRITE_ONCE(rq->rd->overload, 1); #endif } From 3d0d2679161070650267844921686f3ec9d286e8 Mon Sep 17 00:00:00 2001 From: Valentin Schneider Date: Wed, 4 Jul 2018 11:17:47 +0100 Subject: [PATCH 0469/1103] UPSTREAM: sched/fair: Set rq->rd->overload when misfit Idle balance is a great opportunity to pull a misfit task. However, there are scenarios where misfit tasks are present but idle balance is prevented by the overload flag. A good example of this is a workload of n identical tasks. Let's suppose we have a 2+2 Arm big.LITTLE system. We then spawn 4 fairly CPU-intensive tasks - for the sake of simplicity let's say they are just CPU hogs, even when running on big CPUs. They are identical tasks, so on an SMP system they should all end at (roughly) the same time. However, in our case the LITTLE CPUs are less performing than the big CPUs, so tasks running on the LITTLEs will have a longer completion time. This means that the big CPUs will complete their work earlier, at which point they should pull the tasks from the LITTLEs. What we want to happen is summarized as follows: a,b,c,d are our CPU-hogging tasks _ signifies idling LITTLE_0 | a a a a _ _ LITTLE_1 | b b b b _ _ ---------|------------- big_0 | c c c c a a big_1 | d d d d b b ^ ^ Tasks end on the big CPUs, idle balance happens and the misfit tasks are pulled straight away This however won't happen, because currently the overload flag is only set when there is any CPU that has more than one runnable task - which may very well not be the case here if our CPU-hogging workload is all there is to run. As such, this commit sets the overload flag in update_sg_lb_stats when a group is flagged as having a misfit task. Change-Id: I8889880224c9c5a47f2f9c36809d1fafe9b3ec48 Signed-off-by: Valentin Schneider Signed-off-by: Morten Rasmussen Signed-off-by: Peter Zijlstra (Intel) Cc: Linus Torvalds Cc: Peter Zijlstra Cc: Thomas Gleixner Cc: dietmar.eggemann@arm.com Cc: gaku.inami.xh@renesas.com Cc: vincent.guittot@linaro.org Link: http://lkml.kernel.org/r/1530699470-29808-10-git-send-email-morten.rasmussen@arm.com Signed-off-by: Ingo Molnar (cherry picked from commit 757ffdd705ee942fc8150b17942d968601d2a15b in tip/sched/core) Signed-off-by: Quentin Perret --- kernel/sched/fair.c | 6 ++++-- kernel/sched/sched.h | 6 +++++- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c index fd0283c25a12..f4403b08f457 100644 --- a/kernel/sched/fair.c +++ b/kernel/sched/fair.c @@ -7872,7 +7872,7 @@ static bool update_nohz_stats(struct rq *rq, bool force) * @load_idx: Load index of sched_domain of this_cpu for load calc. * @local_group: Does group contain this_cpu. * @sgs: variable to hold the statistics for this group. - * @overload: Indicate more than one runnable task for any CPU. + * @overload: Indicate pullable load (e.g. >1 runnable task). */ static inline void update_sg_lb_stats(struct lb_env *env, struct sched_group *group, int load_idx, @@ -7916,8 +7916,10 @@ static inline void update_sg_lb_stats(struct lb_env *env, sgs->idle_cpus++; if (env->sd->flags & SD_ASYM_CPUCAPACITY && - sgs->group_misfit_task_load < rq->misfit_task_load) + sgs->group_misfit_task_load < rq->misfit_task_load) { sgs->group_misfit_task_load = rq->misfit_task_load; + *overload = 1; + } } /* Adjust by relative CPU capacity of the group */ diff --git a/kernel/sched/sched.h b/kernel/sched/sched.h index 1f7d176d0d4a..4450d0728cb1 100644 --- a/kernel/sched/sched.h +++ b/kernel/sched/sched.h @@ -715,7 +715,11 @@ struct root_domain { cpumask_var_t span; cpumask_var_t online; - /* Indicate more than one runnable task for any CPU */ + /* + * Indicate pullable load on at least one CPU, e.g: + * - More than one runnable task + * - Running task is misfit + */ int overload; /* From 0b01ea80b3d06eb964fcc79eba7afc2fc556ed70 Mon Sep 17 00:00:00 2001 From: Chris Redpath Date: Wed, 4 Jul 2018 11:17:48 +0100 Subject: [PATCH 0470/1103] UPSTREAM: sched/fair: Don't move tasks to lower capacity CPUs unless necessary When lower capacity CPUs are load balancing and considering to pull something from a higher capacity group, we should not pull tasks from a CPU with only one task running as this is guaranteed to impede progress for that task. If there is more than one task running, load balance in the higher capacity group would have already made any possible moves to resolve imbalance and we should make better use of system compute capacity by moving a task if we still have more than one running. Change-Id: Ic0358bea431012aaab57092de92c34d0ed51142f Signed-off-by: Chris Redpath Signed-off-by: Morten Rasmussen Signed-off-by: Peter Zijlstra (Intel) Cc: Linus Torvalds Cc: Peter Zijlstra Cc: Thomas Gleixner Cc: dietmar.eggemann@arm.com Cc: gaku.inami.xh@renesas.com Cc: valentin.schneider@arm.com Cc: vincent.guittot@linaro.org Link: http://lkml.kernel.org/r/1530699470-29808-11-git-send-email-morten.rasmussen@arm.com Signed-off-by: Ingo Molnar (cherry picked from commit 4ad3831a9d4af5e36da5d44a3b9c6522d0353cee in tip/sched/core) Signed-off-by: Quentin Perret --- kernel/sched/fair.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c index f4403b08f457..c5d8bbd4ebd6 100644 --- a/kernel/sched/fair.c +++ b/kernel/sched/fair.c @@ -8502,6 +8502,17 @@ static struct rq *find_busiest_queue(struct lb_env *env, capacity = capacity_of(i); + /* + * For ASYM_CPUCAPACITY domains, don't pick a CPU that could + * eventually lead to active_balancing high->low capacity. + * Higher per-CPU capacity is considered better than balancing + * average load. + */ + if (env->sd->flags & SD_ASYM_CPUCAPACITY && + capacity_of(env->dst_cpu) < capacity && + rq->nr_running == 1) + continue; + wl = weighted_cpuload(rq); /* From 94ab0d4dd5d2d66d34cf0e65e8b98d122b129ed3 Mon Sep 17 00:00:00 2001 From: Morten Rasmussen Date: Wed, 4 Jul 2018 11:17:50 +0100 Subject: [PATCH 0471/1103] UPSTREAM: sched/core: Disable SD_PREFER_SIBLING on asymmetric CPU capacity domains The 'prefer sibling' sched_domain flag is intended to encourage spreading tasks to sibling sched_domain to take advantage of more caches and core for SMT systems. It has recently been changed to be on all non-NUMA topology level. However, spreading across domains with CPU capacity asymmetry isn't desirable, e.g. spreading from high capacity to low capacity CPUs even if high capacity CPUs aren't overutilized might give access to more cache but the CPU will be slower and possibly lead to worse overall throughput. To prevent this, we need to remove SD_PREFER_SIBLING on the sched_domain level immediately below SD_ASYM_CPUCAPACITY. Change-Id: Id9f4944a71b6bbc91368ed8f0e045a9e4b234b49 Signed-off-by: Morten Rasmussen Signed-off-by: Peter Zijlstra (Intel) Cc: Linus Torvalds Cc: Peter Zijlstra Cc: Thomas Gleixner Cc: dietmar.eggemann@arm.com Cc: gaku.inami.xh@renesas.com Cc: valentin.schneider@arm.com Cc: vincent.guittot@linaro.org Link: http://lkml.kernel.org/r/1530699470-29808-13-git-send-email-morten.rasmussen@arm.com Signed-off-by: Ingo Molnar (cherry picked from commit 9c63e84db29bcf584040931ad97c2edd11e35f6c in tip/sched/core) Signed-off-by: Quentin Perret --- kernel/sched/topology.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/kernel/sched/topology.c b/kernel/sched/topology.c index 2536e1b938f9..7ffad0d3a4eb 100644 --- a/kernel/sched/topology.c +++ b/kernel/sched/topology.c @@ -1126,7 +1126,7 @@ sd_init(struct sched_domain_topology_level *tl, | 0*SD_SHARE_CPUCAPACITY | 0*SD_SHARE_PKG_RESOURCES | 0*SD_SERIALIZE - | 0*SD_PREFER_SIBLING + | 1*SD_PREFER_SIBLING | 0*SD_NUMA | sd_flags , @@ -1152,17 +1152,21 @@ sd_init(struct sched_domain_topology_level *tl, if (sd->flags & SD_ASYM_CPUCAPACITY) { struct sched_domain *t = sd; + /* + * Don't attempt to spread across CPUs of different capacities. + */ + if (sd->child) + sd->child->flags &= ~SD_PREFER_SIBLING; + for_each_lower_domain(t) t->flags |= SD_BALANCE_WAKE; } if (sd->flags & SD_SHARE_CPUCAPACITY) { - sd->flags |= SD_PREFER_SIBLING; sd->imbalance_pct = 110; sd->smt_gain = 1178; /* ~15% */ } else if (sd->flags & SD_SHARE_PKG_RESOURCES) { - sd->flags |= SD_PREFER_SIBLING; sd->imbalance_pct = 117; sd->cache_nice_tries = 1; sd->busy_idx = 2; @@ -1173,6 +1177,7 @@ sd_init(struct sched_domain_topology_level *tl, sd->busy_idx = 3; sd->idle_idx = 2; + sd->flags &= ~SD_PREFER_SIBLING; sd->flags |= SD_SERIALIZE; if (sched_domains_numa_distance[tl->numa_level] > RECLAIM_DISTANCE) { sd->flags &= ~(SD_BALANCE_EXEC | @@ -1182,7 +1187,6 @@ sd_init(struct sched_domain_topology_level *tl, #endif } else { - sd->flags |= SD_PREFER_SIBLING; sd->cache_nice_tries = 1; sd->busy_idx = 2; sd->idle_idx = 1; From 0511782ab42f447257a25d492b6edcccf0a7f49b Mon Sep 17 00:00:00 2001 From: Quentin Perret Date: Wed, 25 Apr 2018 14:12:58 +0100 Subject: [PATCH 0472/1103] FROMLIST: sched: Relocate arch_scale_cpu_capacity By default, arch_scale_cpu_capacity() is only visible from within the kernel/sched folder. Relocate it to include/linux/sched/topology.h to make it visible to other clients needing to know about the capacity of CPUs, such as the Energy Model framework. Change-Id: I8bfc9a1f85111a69102e15fcd98458de7d180d01 Cc: Ingo Molnar Cc: Peter Zijlstra (Applied from: https://lore.kernel.org/lkml/20180912091309.7551-2-quentin.perret@arm.com/ Signed-off-by: Quentin Perret --- include/linux/sched/topology.h | 19 +++++++++++++++++++ kernel/sched/sched.h | 21 --------------------- 2 files changed, 19 insertions(+), 21 deletions(-) diff --git a/include/linux/sched/topology.h b/include/linux/sched/topology.h index 6b9976180c1e..5e56d6b1e217 100644 --- a/include/linux/sched/topology.h +++ b/include/linux/sched/topology.h @@ -202,6 +202,17 @@ extern void set_sched_topology(struct sched_domain_topology_level *tl); # define SD_INIT_NAME(type) #endif +#ifndef arch_scale_cpu_capacity +static __always_inline +unsigned long arch_scale_cpu_capacity(struct sched_domain *sd, int cpu) +{ + if (sd && (sd->flags & SD_SHARE_CPUCAPACITY) && (sd->span_weight > 1)) + return sd->smt_gain / sd->span_weight; + + return SCHED_CAPACITY_SCALE; +} +#endif + #else /* CONFIG_SMP */ struct sched_domain_attr; @@ -217,6 +228,14 @@ static inline bool cpus_share_cache(int this_cpu, int that_cpu) return true; } +#ifndef arch_scale_cpu_capacity +static __always_inline +unsigned long arch_scale_cpu_capacity(void __always_unused *sd, int cpu) +{ + return SCHED_CAPACITY_SCALE; +} +#endif + #endif /* !CONFIG_SMP */ static inline int task_node(const struct task_struct *p) diff --git a/kernel/sched/sched.h b/kernel/sched/sched.h index 4450d0728cb1..074fb5ca1f73 100644 --- a/kernel/sched/sched.h +++ b/kernel/sched/sched.h @@ -1762,27 +1762,6 @@ unsigned long arch_scale_freq_capacity(int cpu) } #endif -#ifdef CONFIG_SMP -#ifndef arch_scale_cpu_capacity -static __always_inline -unsigned long arch_scale_cpu_capacity(struct sched_domain *sd, int cpu) -{ - if (sd && (sd->flags & SD_SHARE_CPUCAPACITY) && (sd->span_weight > 1)) - return sd->smt_gain / sd->span_weight; - - return SCHED_CAPACITY_SCALE; -} -#endif -#else -#ifndef arch_scale_cpu_capacity -static __always_inline -unsigned long arch_scale_cpu_capacity(void __always_unused *sd, int cpu) -{ - return SCHED_CAPACITY_SCALE; -} -#endif -#endif - struct rq *__task_rq_lock(struct task_struct *p, struct rq_flags *rf) __acquires(rq->lock); From 59e3774563c27bb30c5a813a0f9e5f972c18fc3f Mon Sep 17 00:00:00 2001 From: Quentin Perret Date: Fri, 27 Apr 2018 15:05:47 +0100 Subject: [PATCH 0473/1103] FROMLIST: sched/cpufreq: Prepare schedutil for Energy Aware Scheduling Schedutil requests frequency by aggregating utilization signals from the scheduler (CFS, RT, DL, IRQ) and applying and 25% margin on top of them. Since Energy Aware Scheduling (EAS) needs to be able to predict the frequency requests, it needs to forecast the decisions made by the governor. In order to prepare the introduction of EAS, introduce schedutil_freq_util() to centralize the aforementioned signal aggregation and make it available to both schedutil and EAS. Since frequency selection and energy estimation still need to deal with RT and DL signals slightly differently, schedutil_freq_util() is called with a different 'type' parameter in those two contexts, and returns an aggregated utilization signal accordingly. While at it, introduce the map_util_freq() function which is designed to make schedutil's 25% margin usable easily for both sugov and EAS. As EAS will be able to predict schedutil's frequency requests more accurately than any other governor by design, it'd be sensible to make sure EAS cannot be used without schedutil. This will be done later, once EAS has actually been introduced. Change-Id: I9a4a56f46c21dc8f63413d86e0f42a8c3ba98418 Cc: Ingo Molnar Cc: Peter Zijlstra Suggested-by: Peter Zijlstra (Applied from https://lore.kernel.org/lkml/20180912091309.7551-3-quentin.perret@arm.com/) Signed-off-by: Quentin Perret --- include/linux/sched/cpufreq.h | 6 +++ kernel/sched/cpufreq_schedutil.c | 89 +++++++++++++++++++++----------- kernel/sched/sched.h | 14 +++++ 3 files changed, 80 insertions(+), 29 deletions(-) diff --git a/include/linux/sched/cpufreq.h b/include/linux/sched/cpufreq.h index 59667444669f..afa940cd50dc 100644 --- a/include/linux/sched/cpufreq.h +++ b/include/linux/sched/cpufreq.h @@ -20,6 +20,12 @@ void cpufreq_add_update_util_hook(int cpu, struct update_util_data *data, void (*func)(struct update_util_data *data, u64 time, unsigned int flags)); void cpufreq_remove_update_util_hook(int cpu); + +static inline unsigned long map_util_freq(unsigned long util, + unsigned long freq, unsigned long cap) +{ + return (freq + (freq >> 2)) * util / cap; +} #endif /* CONFIG_CPU_FREQ */ #endif /* _LINUX_SCHED_CPUFREQ_H */ diff --git a/kernel/sched/cpufreq_schedutil.c b/kernel/sched/cpufreq_schedutil.c index 3fffad3bc8a8..8356cb0072a6 100644 --- a/kernel/sched/cpufreq_schedutil.c +++ b/kernel/sched/cpufreq_schedutil.c @@ -13,6 +13,7 @@ #include "sched.h" +#include #include struct sugov_tunables { @@ -167,7 +168,7 @@ static unsigned int get_next_freq(struct sugov_policy *sg_policy, unsigned int freq = arch_scale_freq_invariant() ? policy->cpuinfo.max_freq : policy->cur; - freq = (freq + (freq >> 2)) * util / max; + freq = map_util_freq(util, freq, max); if (freq == sg_policy->cached_raw_freq && !sg_policy->need_freq_update) return sg_policy->next_freq; @@ -197,15 +198,15 @@ static unsigned int get_next_freq(struct sugov_policy *sg_policy, * based on the task model parameters and gives the minimal utilization * required to meet deadlines. */ -static unsigned long sugov_get_util(struct sugov_cpu *sg_cpu) +unsigned long schedutil_freq_util(int cpu, unsigned long util_cfs, + enum schedutil_type type) { - struct rq *rq = cpu_rq(sg_cpu->cpu); + struct rq *rq = cpu_rq(cpu); unsigned long util, irq, max; - sg_cpu->max = max = arch_scale_cpu_capacity(NULL, sg_cpu->cpu); - sg_cpu->bw_dl = cpu_bw_dl(rq); + max = arch_scale_cpu_capacity(NULL, cpu); - if (rt_rq_is_runnable(&rq->rt)) + if (type == FREQUENCY_UTIL && rt_rq_is_runnable(&rq->rt)) return max; /* @@ -223,20 +224,33 @@ static unsigned long sugov_get_util(struct sugov_cpu *sg_cpu) * utilization (PELT windows are synchronized) we can directly add them * to obtain the CPU's actual utilization. */ - util = cpu_util_cfs(rq); + util = util_cfs; util += cpu_util_rt(rq); - /* - * We do not make cpu_util_dl() a permanent part of this sum because we - * want to use cpu_bw_dl() later on, but we need to check if the - * CFS+RT+DL sum is saturated (ie. no idle time) such that we select - * f_max when there is no idle time. - * - * NOTE: numerical errors or stop class might cause us to not quite hit - * saturation when we should -- something for later. - */ - if ((util + cpu_util_dl(rq)) >= max) - return max; + if (type == FREQUENCY_UTIL) { + /* + * For frequency selection we do not make cpu_util_dl() a + * permanent part of this sum because we want to use + * cpu_bw_dl() later on, but we need to check if the + * CFS+RT+DL sum is saturated (ie. no idle time) such + * that we select f_max when there is no idle time. + * + * NOTE: numerical errors or stop class might cause us + * to not quite hit saturation when we should -- + * something for later. + */ + + if ((util + cpu_util_dl(rq)) >= max) + return max; + } else { + /* + * OTOH, for energy computation we need the estimated + * running time, so include util_dl and ignore dl_bw. + */ + util += cpu_util_dl(rq); + if (util >= max) + return max; + } /* * There is still idle time; further improve the number by using the @@ -250,17 +264,34 @@ static unsigned long sugov_get_util(struct sugov_cpu *sg_cpu) util = scale_irq_capacity(util, irq, max); util += irq; - /* - * Bandwidth required by DEADLINE must always be granted while, for - * FAIR and RT, we use blocked utilization of IDLE CPUs as a mechanism - * to gracefully reduce the frequency when no tasks show up for longer - * periods of time. - * - * Ideally we would like to set bw_dl as min/guaranteed freq and util + - * bw_dl as requested freq. However, cpufreq is not yet ready for such - * an interface. So, we only do the latter for now. - */ - return min(max, util + sg_cpu->bw_dl); + if (type == FREQUENCY_UTIL) { + /* + * Bandwidth required by DEADLINE must always be granted + * while, for FAIR and RT, we use blocked utilization of + * IDLE CPUs as a mechanism to gracefully reduce the + * frequency when no tasks show up for longer periods of + * time. + * + * Ideally we would like to set bw_dl as min/guaranteed + * freq and util + bw_dl as requested freq. However, + * cpufreq is not yet ready for such an interface. So, + * we only do the latter for now. + */ + util += cpu_bw_dl(rq); + } + + return min(max, util); +} + +static unsigned long sugov_get_util(struct sugov_cpu *sg_cpu) +{ + struct rq *rq = cpu_rq(sg_cpu->cpu); + unsigned long util = cpu_util_cfs(rq); + + sg_cpu->max = arch_scale_cpu_capacity(NULL, sg_cpu->cpu); + sg_cpu->bw_dl = cpu_bw_dl(rq); + + return schedutil_freq_util(sg_cpu->cpu, util, FREQUENCY_UTIL); } /** diff --git a/kernel/sched/sched.h b/kernel/sched/sched.h index 074fb5ca1f73..576797657e35 100644 --- a/kernel/sched/sched.h +++ b/kernel/sched/sched.h @@ -2173,7 +2173,15 @@ static inline void cpufreq_update_util(struct rq *rq, unsigned int flags) {} # define arch_scale_freq_invariant() false #endif +enum schedutil_type { + FREQUENCY_UTIL, + ENERGY_UTIL, +}; + #ifdef CONFIG_CPU_FREQ_GOV_SCHEDUTIL +unsigned long schedutil_freq_util(int cpu, unsigned long util_cfs, + enum schedutil_type type); + static inline unsigned long cpu_bw_dl(struct rq *rq) { return (rq->dl.running_bw * SCHED_CAPACITY_SCALE) >> BW_SHIFT; @@ -2200,6 +2208,12 @@ static inline unsigned long cpu_util_rt(struct rq *rq) { return READ_ONCE(rq->avg_rt.util_avg); } +#else /* CONFIG_CPU_FREQ_GOV_SCHEDUTIL */ +static inline unsigned long schedutil_freq_util(int cpu, unsigned long util, + enum schedutil_type type) +{ + return util; +} #endif #ifdef HAVE_SCHED_AVG_IRQ From f3fe7a13c760c43f2c92e64b2b5d6489629efe2e Mon Sep 17 00:00:00 2001 From: Quentin Perret Date: Wed, 25 Apr 2018 18:34:11 +0100 Subject: [PATCH 0474/1103] FROMLIST: PM: Introduce an Energy Model management framework Several subsystems in the kernel (task scheduler and/or thermal at the time of writing) can benefit from knowing about the energy consumed by CPUs. Yet, this information can come from different sources (DT or firmware for example), in different formats, hence making it hard to exploit without a standard API. As an attempt to address this, introduce a centralized Energy Model (EM) management framework which aggregates the power values provided by drivers into a table for each performance domain in the system. The power cost tables are made available to interested clients (e.g. task scheduler or thermal) via platform-agnostic APIs. The overall design is represented by the diagram below (focused on Arm-related drivers as an example, but applicable to any architecture): +---------------+ +-----------------+ +-------------+ | Thermal (IPA) | | Scheduler (EAS) | | Other | +---------------+ +-----------------+ +-------------+ | | em_pd_energy() | | | em_cpu_get() | +-----------+ | +--------+ | | | v v v +---------------------+ | | | Energy Model | | | | Framework | | | +---------------------+ ^ ^ ^ | | | em_register_perf_domain() +----------+ | +---------+ | | | +---------------+ +---------------+ +--------------+ | cpufreq-dt | | arm_scmi | | Other | +---------------+ +---------------+ +--------------+ ^ ^ ^ | | | +--------------+ +---------------+ +--------------+ | Device Tree | | Firmware | | ? | +--------------+ +---------------+ +--------------+ Drivers (typically, but not limited to, CPUFreq drivers) can register data in the EM framework using the em_register_perf_domain() API. The calling driver must provide a callback function with a standardized signature that will be used by the EM framework to build the power cost tables of the performance domain. This design should offer a lot of flexibility to calling drivers which are free of reading information from any location and to use any technique to compute power costs. Moreover, the capacity states registered by drivers in the EM framework are not required to match real performance states of the target. This is particularly important on targets where the performance states are not known by the OS. The power cost coefficients managed by the EM framework are specified in milli-watts. Although the two potential users of those coefficients (IPA and EAS) only need relative correctness, IPA specifically needs to compare the power of CPUs with the power of other components (GPUs, for example), which are still expressed in absolute terms in their respective subsystems. Hence, specifying the power of CPUs in milli-watts should help transitioning IPA to using the EM framework without introducing new problems by keeping units comparable across sub-systems. On the longer term, the EM of other devices than CPUs could also be managed by the EM framework, which would enable to remove the absolute unit. However, this is not absolutely required as a first step, so this extension of the EM framework is left for later. On the client side, the EM framework offers APIs to access the power cost tables of a CPU (em_cpu_get()), and to estimate the energy consumed by the CPUs of a performance domain (em_pd_energy()). Clients such as the task scheduler can then use these APIs to access the shared data structures holding the Energy Model of CPUs. Change-Id: I40c09c52a6a4e6376daa533d3b5b965eb16d1a29 Cc: Peter Zijlstra Cc: "Rafael J. Wysocki" (Applied from https://lore.kernel.org/lkml/20180912091309.7551-4-quentin.perret@arm.com/) Signed-off-by: Quentin Perret --- include/linux/energy_model.h | 185 +++++++++++++++++++++++++++++++++ kernel/power/Kconfig | 15 +++ kernel/power/Makefile | 1 + kernel/power/energy_model.c | 195 +++++++++++++++++++++++++++++++++++ 4 files changed, 396 insertions(+) create mode 100644 include/linux/energy_model.h create mode 100644 kernel/power/energy_model.c diff --git a/include/linux/energy_model.h b/include/linux/energy_model.h new file mode 100644 index 000000000000..a472076f9c80 --- /dev/null +++ b/include/linux/energy_model.h @@ -0,0 +1,185 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _LINUX_ENERGY_MODEL_H +#define _LINUX_ENERGY_MODEL_H +#include +#include +#include +#include +#include +#include +#include + +#ifdef CONFIG_ENERGY_MODEL +/** + * em_cap_state - Capacity state of a performance domain + * @frequency: The CPU frequency in KHz, for consistency with CPUFreq + * @power: The power consumed by 1 CPU at this level, in milli-watts + * @cost: The cost coefficient associated with this level, used during + * energy calculation. Equal to: power * max_frequency / frequency + */ +struct em_cap_state { + unsigned long frequency; + unsigned long power; + unsigned long cost; +}; + +/** + * em_perf_domain - Performance domain + * @table: List of capacity states, in ascending order + * @nr_cap_states: Number of capacity states + * @cpus: Cpumask covering the CPUs of the domain + * + * A "performance domain" represents a group of CPUs whose performance is + * scaled together. All CPUs of a performance domain must have the same + * micro-architecture. Performance domains often have a 1-to-1 mapping with + * CPUFreq policies. + */ +struct em_perf_domain { + struct em_cap_state *table; + int nr_cap_states; + unsigned long cpus[0]; +}; + +#define EM_CPU_MAX_POWER 0xFFFF + +struct em_data_callback { + /** + * active_power() - Provide power at the next capacity state of a CPU + * @power : Active power at the capacity state in mW (modified) + * @freq : Frequency at the capacity state in kHz (modified) + * @cpu : CPU for which we do this operation + * + * active_power() must find the lowest capacity state of 'cpu' above + * 'freq' and update 'power' and 'freq' to the matching active power + * and frequency. + * + * The power is the one of a single CPU in the domain, expressed in + * milli-watts. It is expected to fit in the [0, EM_CPU_MAX_POWER] + * range. + * + * Return 0 on success. + */ + int (*active_power)(unsigned long *power, unsigned long *freq, int cpu); +}; +#define EM_DATA_CB(_active_power_cb) { .active_power = &_active_power_cb } + +struct em_perf_domain *em_cpu_get(int cpu); +int em_register_perf_domain(cpumask_t *span, unsigned int nr_states, + struct em_data_callback *cb); + +/** + * em_pd_energy() - Estimates the energy consumed by the CPUs of a perf. domain + * @pd : performance domain for which energy has to be estimated + * @max_util : highest utilization among CPUs of the domain + * @sum_util : sum of the utilization of all CPUs in the domain + * + * Return: the sum of the energy consumed by the CPUs of the domain assuming + * a capacity state satisfying the max utilization of the domain. + */ +static inline unsigned long em_pd_energy(struct em_perf_domain *pd, + unsigned long max_util, unsigned long sum_util) +{ + unsigned long freq, scale_cpu; + struct em_cap_state *cs; + int i, cpu; + + /* + * In order to predict the capacity state, map the utilization of the + * most utilized CPU of the performance domain to a requested frequency, + * like schedutil. + */ + cpu = cpumask_first(to_cpumask(pd->cpus)); + scale_cpu = arch_scale_cpu_capacity(NULL, cpu); + cs = &pd->table[pd->nr_cap_states - 1]; + freq = map_util_freq(max_util, cs->frequency, scale_cpu); + + /* + * Find the lowest capacity state of the Energy Model above the + * requested frequency. + */ + for (i = 0; i < pd->nr_cap_states; i++) { + cs = &pd->table[i]; + if (cs->frequency >= freq) + break; + } + + /* + * The capacity of a CPU in the domain at that capacity state (cs) + * can be computed as: + * + * cs->freq * scale_cpu + * cs->cap = -------------------- (1) + * cpu_max_freq + * + * So, the energy consumed by this CPU at that capacity state is: + * + * cs->power * cpu_util + * cpu_nrg = -------------------- (2) + * cs->cap + * + * since 'cpu_util / cs->cap' represents its percentage of busy time. + * + * NOTE: Although the result of this computation actually is in + * units of power, it can be manipulated as an energy value + * over a scheduling period, since it is assumed to be + * constant during that interval. + * + * By injecting (1) in (2), 'cpu_nrg' can be re-expressed as a product + * of two terms: + * + * cs->power * cpu_max_freq cpu_util + * cpu_nrg = ------------------------ * --------- (3) + * cs->freq scale_cpu + * + * The first term is static, and is stored in the em_cap_state struct + * as 'cs->cost'. + * + * Since all CPUs of the domain have the same micro-architecture, they + * share the same 'cs->cost', and the same CPU capacity. Hence, the + * total energy of the domain (which is the simple sum of the energy of + * all of its CPUs) can be factorized as: + * + * cs->cost * \Sum cpu_util + * pd_nrg = ------------------------ (4) + * scale_cpu + */ + return cs->cost * sum_util / scale_cpu; +} + +/** + * em_pd_nr_cap_states() - Get the number of capacity states of a perf. domain + * @pd : performance domain for which this must be done + * + * Return: the number of capacity states in the performance domain table + */ +static inline int em_pd_nr_cap_states(struct em_perf_domain *pd) +{ + return pd->nr_cap_states; +} + +#else +struct em_perf_domain {}; +struct em_data_callback {}; +#define EM_DATA_CB(_active_power_cb) { } + +static inline int em_register_perf_domain(cpumask_t *span, + unsigned int nr_states, struct em_data_callback *cb) +{ + return -EINVAL; +} +static inline struct em_perf_domain *em_cpu_get(int cpu) +{ + return NULL; +} +static inline unsigned long em_pd_energy(struct em_perf_domain *pd, + unsigned long max_util, unsigned long sum_util) +{ + return 0; +} +static inline int em_pd_nr_cap_states(struct em_perf_domain *pd) +{ + return 0; +} +#endif + +#endif diff --git a/kernel/power/Kconfig b/kernel/power/Kconfig index 3a6c2f87699e..f8fe57d1022e 100644 --- a/kernel/power/Kconfig +++ b/kernel/power/Kconfig @@ -298,3 +298,18 @@ config PM_GENERIC_DOMAINS_OF config CPU_PM bool + +config ENERGY_MODEL + bool "Energy Model for CPUs" + depends on SMP + depends on CPU_FREQ + default n + help + Several subsystems (thermal and/or the task scheduler for example) + can leverage information about the energy consumed by CPUs to make + smarter decisions. This config option enables the framework from + which subsystems can access the energy models. + + The exact usage of the energy model is subsystem-dependent. + + If in doubt, say N. diff --git a/kernel/power/Makefile b/kernel/power/Makefile index 5c1743d4d8ef..3f8db8361561 100644 --- a/kernel/power/Makefile +++ b/kernel/power/Makefile @@ -17,3 +17,4 @@ obj-$(CONFIG_PM_WAKELOCKS) += wakelock.o obj-$(CONFIG_MAGIC_SYSRQ) += poweroff.o obj-$(CONFIG_SUSPEND) += wakeup_reason.o +obj-$(CONFIG_ENERGY_MODEL) += energy_model.o diff --git a/kernel/power/energy_model.c b/kernel/power/energy_model.c new file mode 100644 index 000000000000..d77cd295f3e9 --- /dev/null +++ b/kernel/power/energy_model.c @@ -0,0 +1,195 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Energy Model of CPUs + * + * Copyright (c) 2018, Arm ltd. + * Written by: Quentin Perret, Arm ltd. + */ + +#define pr_fmt(fmt) "energy_model: " fmt + +#include +#include +#include +#include +#include + +/* Mapping of each CPU to the performance domain to which it belongs. */ +static DEFINE_PER_CPU(struct em_perf_domain *, em_data); + +/* + * Mutex serializing the registrations of performance domains and letting + * callbacks defined by drivers sleep. + */ +static DEFINE_MUTEX(em_pd_mutex); + +static struct em_perf_domain *em_create_pd(cpumask_t *span, int nr_states, + struct em_data_callback *cb) +{ + unsigned long opp_eff, prev_opp_eff = ULONG_MAX; + unsigned long power, freq, prev_freq = 0; + int i, ret, cpu = cpumask_first(span); + struct em_cap_state *table; + struct em_perf_domain *pd; + u64 fmax; + + if (!cb->active_power) + return NULL; + + pd = kzalloc(sizeof(*pd) + cpumask_size(), GFP_KERNEL); + if (!pd) + return NULL; + + table = kcalloc(nr_states, sizeof(*table), GFP_KERNEL); + if (!table) + goto free_pd; + + /* Build the list of capacity states for this performance domain */ + for (i = 0, freq = 0; i < nr_states; i++, freq++) { + /* + * active_power() is a driver callback which ceils 'freq' to + * lowest capacity state of 'cpu' above 'freq' and updates + * 'power' and 'freq' accordingly. + */ + ret = cb->active_power(&power, &freq, cpu); + if (ret) { + pr_err("pd%d: invalid cap. state: %d\n", cpu, ret); + goto free_cs_table; + } + + /* + * We expect the driver callback to increase the frequency for + * higher capacity states. + */ + if (freq <= prev_freq) { + pr_err("pd%d: non-increasing freq: %lu\n", cpu, freq); + goto free_cs_table; + } + + /* + * The power returned by active_state() is expected to be + * positive, in milli-watts and to fit into 16 bits. + */ + if (!power || power > EM_CPU_MAX_POWER) { + pr_err("pd%d: invalid power: %lu\n", cpu, power); + goto free_cs_table; + } + + table[i].power = power; + table[i].frequency = prev_freq = freq; + + /* + * The hertz/watts efficiency ratio should decrease as the + * frequency grows on sane platforms. But this isn't always + * true in practice so warn the user if a higher OPP is more + * power efficient than a lower one. + */ + opp_eff = freq / power; + if (opp_eff >= prev_opp_eff) + pr_warn("pd%d: hertz/watts ratio non-monotonically decreasing: OPP%d >= OPP%d\n", + cpu, i, i - 1); + prev_opp_eff = opp_eff; + } + + /* Compute the cost of each capacity_state. */ + fmax = (u64) table[nr_states - 1].frequency; + for (i = 0; i < nr_states; i++) { + table[i].cost = div64_u64(fmax * table[i].power, + table[i].frequency); + } + + pd->table = table; + pd->nr_cap_states = nr_states; + cpumask_copy(to_cpumask(pd->cpus), span); + + return pd; + +free_cs_table: + kfree(table); +free_pd: + kfree(pd); + + return NULL; +} + +/** + * em_cpu_get() - Return the performance domain for a CPU + * @cpu : CPU to find the performance domain for + * + * Return: the performance domain to which 'cpu' belongs, or NULL if it doesn't + * exist. + */ +struct em_perf_domain *em_cpu_get(int cpu) +{ + return READ_ONCE(per_cpu(em_data, cpu)); +} +EXPORT_SYMBOL_GPL(em_cpu_get); + +/** + * em_register_perf_domain() - Register the Energy Model of a performance domain + * @span : Mask of CPUs in the performance domain + * @nr_states : Number of capacity states to register + * @cb : Callback functions providing the data of the Energy Model + * + * Create Energy Model tables for a performance domain using the callbacks + * defined in cb. + * + * If multiple clients register the same performance domain, all but the first + * registration will be ignored. + * + * Return 0 on success + */ +int em_register_perf_domain(cpumask_t *span, unsigned int nr_states, + struct em_data_callback *cb) +{ + unsigned long cap, prev_cap = 0; + struct em_perf_domain *pd; + int cpu, ret = 0; + + if (!span || !nr_states || !cb) + return -EINVAL; + + /* + * Use a mutex to serialize the registration of performance domains and + * let the driver-defined callback functions sleep. + */ + mutex_lock(&em_pd_mutex); + + for_each_cpu(cpu, span) { + /* Make sure we don't register again an existing domain. */ + if (READ_ONCE(per_cpu(em_data, cpu))) { + ret = -EEXIST; + goto unlock; + } + + /* + * All CPUs of a domain must have the same micro-architecture + * since they all share the same table. + */ + cap = arch_scale_cpu_capacity(NULL, cpu); + if (prev_cap && prev_cap != cap) { + pr_err("CPUs of %*pbl must have the same capacity\n", + cpumask_pr_args(span)); + ret = -EINVAL; + goto unlock; + } + prev_cap = cap; + } + + /* Create the performance domain and add it to the Energy Model. */ + pd = em_create_pd(span, nr_states, cb); + if (!pd) { + ret = -EINVAL; + goto unlock; + } + + for_each_cpu(cpu, span) + WRITE_ONCE(per_cpu(em_data, cpu), pd); + + pr_debug("Created perf domain %*pbl\n", cpumask_pr_args(span)); +unlock: + mutex_unlock(&em_pd_mutex); + + return ret; +} +EXPORT_SYMBOL_GPL(em_register_perf_domain); From d70b9bee0225f505437a754db57714ce855b2e9c Mon Sep 17 00:00:00 2001 From: Quentin Perret Date: Thu, 17 May 2018 15:50:07 +0100 Subject: [PATCH 0475/1103] FROMLIST: PM / EM: Expose the Energy Model in sysfs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Expose the Energy Model (read-only) of all performance domains in sysfs for convenience. To do so, add a kobject to the CPU subsystem under the umbrella of which a kobject for each performance domain is attached. The resulting hierarchy is as follows for a platform with two performance domains for example: /sys/devices/system/cpu/energy_model ├── pd0 │   ├── cost │   ├── cpus │   ├── frequency │   └── power └── pd4 ├── cost ├── cpus ├── frequency └── power In this implementation, the kobject abstraction is only used as a convenient way of exposing data to sysfs. However, it could also be used in the future to allocate and release performance domains in a more dynamic way using reference counting. Change-Id: Ia8184e2100a38dbec0837e8efc755d0702ef86a7 Cc: Peter Zijlstra Cc: "Rafael J. Wysocki" (Applied from https://lore.kernel.org/lkml/20180912091309.7551-5-quentin.perret@arm.com/) Signed-off-by: Quentin Perret --- include/linux/energy_model.h | 2 + kernel/power/energy_model.c | 90 ++++++++++++++++++++++++++++++++++++ 2 files changed, 92 insertions(+) diff --git a/include/linux/energy_model.h b/include/linux/energy_model.h index a472076f9c80..69184f4f4ae0 100644 --- a/include/linux/energy_model.h +++ b/include/linux/energy_model.h @@ -27,6 +27,7 @@ struct em_cap_state { * em_perf_domain - Performance domain * @table: List of capacity states, in ascending order * @nr_cap_states: Number of capacity states + * @kobj: Kobject used to expose the domain in sysfs * @cpus: Cpumask covering the CPUs of the domain * * A "performance domain" represents a group of CPUs whose performance is @@ -37,6 +38,7 @@ struct em_cap_state { struct em_perf_domain { struct em_cap_state *table; int nr_cap_states; + struct kobject kobj; unsigned long cpus[0]; }; diff --git a/kernel/power/energy_model.c b/kernel/power/energy_model.c index d77cd295f3e9..2934ebb09e9d 100644 --- a/kernel/power/energy_model.c +++ b/kernel/power/energy_model.c @@ -23,6 +23,82 @@ static DEFINE_PER_CPU(struct em_perf_domain *, em_data); */ static DEFINE_MUTEX(em_pd_mutex); +static struct kobject *em_kobject; + +/* Getters for the attributes of em_perf_domain objects */ +struct em_pd_attr { + struct attribute attr; + ssize_t (*show)(struct em_perf_domain *pd, char *buf); + ssize_t (*store)(struct em_perf_domain *pd, const char *buf, size_t s); +}; + +#define EM_ATTR_LEN 13 +#define show_table_attr(_attr) \ +static ssize_t show_##_attr(struct em_perf_domain *pd, char *buf) \ +{ \ + ssize_t cnt = 0; \ + int i; \ + for (i = 0; i < pd->nr_cap_states; i++) { \ + if (cnt >= (ssize_t) (PAGE_SIZE / sizeof(char) \ + - (EM_ATTR_LEN + 2))) \ + goto out; \ + cnt += scnprintf(&buf[cnt], EM_ATTR_LEN + 1, "%lu ", \ + pd->table[i]._attr); \ + } \ +out: \ + cnt += sprintf(&buf[cnt], "\n"); \ + return cnt; \ +} + +show_table_attr(power); +show_table_attr(frequency); +show_table_attr(cost); + +static ssize_t show_cpus(struct em_perf_domain *pd, char *buf) +{ + return sprintf(buf, "%*pbl\n", cpumask_pr_args(to_cpumask(pd->cpus))); +} + +#define pd_attr(_name) em_pd_##_name##_attr +#define define_pd_attr(_name) static struct em_pd_attr pd_attr(_name) = \ + __ATTR(_name, 0444, show_##_name, NULL) + +define_pd_attr(power); +define_pd_attr(frequency); +define_pd_attr(cost); +define_pd_attr(cpus); + +static struct attribute *em_pd_default_attrs[] = { + &pd_attr(power).attr, + &pd_attr(frequency).attr, + &pd_attr(cost).attr, + &pd_attr(cpus).attr, + NULL +}; + +#define to_pd(k) container_of(k, struct em_perf_domain, kobj) +#define to_pd_attr(a) container_of(a, struct em_pd_attr, attr) + +static ssize_t show(struct kobject *kobj, struct attribute *attr, char *buf) +{ + struct em_perf_domain *pd = to_pd(kobj); + struct em_pd_attr *pd_attr = to_pd_attr(attr); + ssize_t ret; + + ret = pd_attr->show(pd, buf); + + return ret; +} + +static const struct sysfs_ops em_pd_sysfs_ops = { + .show = show, +}; + +static struct kobj_type ktype_em_pd = { + .sysfs_ops = &em_pd_sysfs_ops, + .default_attrs = em_pd_default_attrs, +}; + static struct em_perf_domain *em_create_pd(cpumask_t *span, int nr_states, struct em_data_callback *cb) { @@ -102,6 +178,11 @@ static struct em_perf_domain *em_create_pd(cpumask_t *span, int nr_states, pd->nr_cap_states = nr_states; cpumask_copy(to_cpumask(pd->cpus), span); + ret = kobject_init_and_add(&pd->kobj, &ktype_em_pd, em_kobject, + "pd%u", cpu); + if (ret) + pr_err("pd%d: failed kobject_init_and_add(): %d\n", cpu, ret); + return pd; free_cs_table: @@ -155,6 +236,15 @@ int em_register_perf_domain(cpumask_t *span, unsigned int nr_states, */ mutex_lock(&em_pd_mutex); + if (!em_kobject) { + em_kobject = kobject_create_and_add("energy_model", + &cpu_subsys.dev_root->kobj); + if (!em_kobject) { + ret = -ENODEV; + goto unlock; + } + } + for_each_cpu(cpu, span) { /* Make sure we don't register again an existing domain. */ if (READ_ONCE(per_cpu(em_data, cpu))) { From 67081b777f3a3a63506c94f4c8c1022f503107ce Mon Sep 17 00:00:00 2001 From: Quentin Perret Date: Tue, 11 Sep 2018 12:04:40 +0100 Subject: [PATCH 0476/1103] FROMLIST: sched: Introduce a sched_feat for Energy Aware Scheduling In order to make sure Energy Aware Scheduling (EAS) doesn't hurt systems not using it, add a new sched_feat, called ENERGY_AWARE, guarding the access to EAS code paths. Change-Id: I70e1ca087908acbc98fb752c40b8ff65bb28ef2b (Applied from https://lore.kernel.org/lkml/20180912091309.7551-6-quentin.perret@arm.com/) Signed-off-by: Quentin Perret --- kernel/sched/features.h | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/kernel/sched/features.h b/kernel/sched/features.h index 85ae8488039c..26ff6752e492 100644 --- a/kernel/sched/features.h +++ b/kernel/sched/features.h @@ -90,3 +90,10 @@ SCHED_FEAT(WA_BIAS, true) * UtilEstimation. Use estimated CPU utilization. */ SCHED_FEAT(UTIL_EST, true) + +/* + * Energy-Aware Scheduling. Whether or not tasks will be placed into an + * energy-aware fashion depends on this feature being enabled and on the + * root domain having an Energy Model attached. + */ +SCHED_FEAT(ENERGY_AWARE, false) From a6bacb38e7641cca641ae0a06257bd1cc09d5e3c Mon Sep 17 00:00:00 2001 From: Quentin Perret Date: Mon, 30 Apr 2018 11:23:39 +0100 Subject: [PATCH 0477/1103] FROMLIST: sched/topology: Reference the Energy Model of CPUs when available The existing scheduling domain hierarchy is defined to map to the cache topology of the system. However, Energy Aware Scheduling (EAS) requires more knowledge about the platform, and specifically needs to know about the span of Performance Domains (PD), which do not always align with caches. To address this issue, use the Energy Model (EM) of the system to extend the scheduler topology code with a representation of the PDs, alongside the scheduling domains. More specifically, a linked list of PDs is attached to each root domain. When multiple root domains are in use, each list contains only the PDs covering the CPUs of its root domain. If a PD spans over CPUs of two different root domains, it will be duplicated in both lists. The lists are fully maintained by the scheduler from partition_sched_domains() in order to cope with hotplug and cpuset changes. As for scheduling domains, the list are protected by RCU to ensure safe concurrent updates. The linked lists should be used only from code paths protected by the ENERGY_AWARE sched_feat. Change-Id: I65ba204388315cd3e7048241a7dd1c119021d63e Cc: Ingo Molnar Cc: Peter Zijlstra (Applied from https://lore.kernel.org/lkml/20180912091309.7551-7-quentin.perret@arm.com/) Signed-off-by: Quentin Perret --- kernel/sched/sched.h | 21 +++++++ kernel/sched/topology.c | 134 ++++++++++++++++++++++++++++++++++++++-- 2 files changed, 151 insertions(+), 4 deletions(-) diff --git a/kernel/sched/sched.h b/kernel/sched/sched.h index 576797657e35..b781aec416b9 100644 --- a/kernel/sched/sched.h +++ b/kernel/sched/sched.h @@ -44,6 +44,7 @@ #include #include #include +#include #include #include #include @@ -700,6 +701,12 @@ static inline bool sched_asym_prefer(int a, int b) return arch_asym_cpu_priority(a) > arch_asym_cpu_priority(b); } +struct perf_domain { + struct em_perf_domain *obj; + struct perf_domain *next; + struct rcu_head rcu; +}; + /* * We add the notion of a root-domain which will be used to define per-domain * variables. Each exclusive cpuset essentially defines an island domain by @@ -752,6 +759,12 @@ struct root_domain { struct cpupri cpupri; unsigned long max_cpu_capacity; + + /* + * NULL-terminated list of performance domains intersecting with the + * CPUs of the rd. Protected by RCU. + */ + struct perf_domain *pd; }; extern struct root_domain def_root_domain; @@ -2243,3 +2256,11 @@ unsigned long scale_irq_capacity(unsigned long util, unsigned long irq, unsigned return util; } #endif + +#ifdef CONFIG_SMP +#ifdef CONFIG_ENERGY_MODEL +#define perf_domain_span(pd) (to_cpumask(((pd)->obj->cpus))) +#else +#define perf_domain_span(pd) NULL +#endif +#endif diff --git a/kernel/sched/topology.c b/kernel/sched/topology.c index 7ffad0d3a4eb..23230b4d629f 100644 --- a/kernel/sched/topology.c +++ b/kernel/sched/topology.c @@ -201,6 +201,116 @@ sd_parent_degenerate(struct sched_domain *sd, struct sched_domain *parent) return 1; } +#ifdef CONFIG_ENERGY_MODEL +static void free_pd(struct perf_domain *pd) +{ + struct perf_domain *tmp; + + while (pd) { + tmp = pd->next; + kfree(pd); + pd = tmp; + } +} + +static struct perf_domain *find_pd(struct perf_domain *pd, int cpu) +{ + while (pd) { + if (cpumask_test_cpu(cpu, perf_domain_span(pd))) + return pd; + pd = pd->next; + } + + return NULL; +} + +static struct perf_domain *pd_init(int cpu) +{ + struct em_perf_domain *obj = em_cpu_get(cpu); + struct perf_domain *pd; + + if (!obj) { + if (sched_debug()) + pr_info("%s: no EM found for CPU%d\n", __func__, cpu); + return NULL; + } + + pd = kzalloc(sizeof(*pd), GFP_KERNEL); + if (!pd) + return NULL; + pd->obj = obj; + + return pd; +} + +static void perf_domain_debug(const struct cpumask *cpu_map, + struct perf_domain *pd) +{ + if (!sched_debug() || !pd) + return; + + printk(KERN_DEBUG "root_domain %*pbl: ", cpumask_pr_args(cpu_map)); + + while (pd) { + printk(KERN_CONT " pd%d:{ cpus=%*pbl nr_cstate=%d }", + cpumask_first(perf_domain_span(pd)), + cpumask_pr_args(perf_domain_span(pd)), + em_pd_nr_cap_states(pd->obj)); + pd = pd->next; + } + + printk(KERN_CONT "\n"); +} + +static void destroy_perf_domain_rcu(struct rcu_head *rp) +{ + struct perf_domain *pd; + + pd = container_of(rp, struct perf_domain, rcu); + free_pd(pd); +} + +static void build_perf_domains(const struct cpumask *cpu_map) +{ + struct perf_domain *pd = NULL, *tmp; + int cpu = cpumask_first(cpu_map); + struct root_domain *rd = cpu_rq(cpu)->rd; + int i; + + for_each_cpu(i, cpu_map) { + /* Skip already covered CPUs. */ + if (find_pd(pd, i)) + continue; + + /* Create the new pd and add it to the local list. */ + tmp = pd_init(i); + if (!tmp) + goto free; + tmp->next = pd; + pd = tmp; + } + + perf_domain_debug(cpu_map, pd); + + /* Attach the new list of performance domains to the root domain. */ + tmp = rd->pd; + rcu_assign_pointer(rd->pd, pd); + if (tmp) + call_rcu(&tmp->rcu, destroy_perf_domain_rcu); + + return; + +free: + free_pd(pd); + tmp = rd->pd; + rcu_assign_pointer(rd->pd, NULL); + if (tmp) + call_rcu(&tmp->rcu, destroy_perf_domain_rcu); +} +#else +static void free_pd(struct perf_domain *pd) { } +#endif /* CONFIG_ENERGY_MODEL */ + static void free_rootdomain(struct rcu_head *rcu) { struct root_domain *rd = container_of(rcu, struct root_domain, rcu); @@ -211,6 +321,7 @@ static void free_rootdomain(struct rcu_head *rcu) free_cpumask_var(rd->rto_mask); free_cpumask_var(rd->online); free_cpumask_var(rd->span); + free_pd(rd->pd); kfree(rd); } @@ -1961,8 +2072,8 @@ void partition_sched_domains(int ndoms_new, cpumask_var_t doms_new[], /* Destroy deleted domains: */ for (i = 0; i < ndoms_cur; i++) { for (j = 0; j < n && !new_topology; j++) { - if (cpumask_equal(doms_cur[i], doms_new[j]) - && dattrs_equal(dattr_cur, i, dattr_new, j)) + if (cpumask_equal(doms_cur[i], doms_new[j]) && + dattrs_equal(dattr_cur, i, dattr_new, j)) goto match1; } /* No match - a current sched domain not in new doms_new[] */ @@ -1982,8 +2093,8 @@ void partition_sched_domains(int ndoms_new, cpumask_var_t doms_new[], /* Build new domains: */ for (i = 0; i < ndoms_new; i++) { for (j = 0; j < n && !new_topology; j++) { - if (cpumask_equal(doms_new[i], doms_cur[j]) - && dattrs_equal(dattr_new, i, dattr_cur, j)) + if (cpumask_equal(doms_new[i], doms_cur[j]) && + dattrs_equal(dattr_new, i, dattr_cur, j)) goto match2; } /* No match - add a new doms_new */ @@ -1992,6 +2103,21 @@ void partition_sched_domains(int ndoms_new, cpumask_var_t doms_new[], ; } +#ifdef CONFIG_ENERGY_MODEL + /* Build perf. domains: */ + for (i = 0; i < ndoms_new; i++) { + for (j = 0; j < n; j++) { + if (cpumask_equal(doms_new[i], doms_cur[j]) && + cpu_rq(cpumask_first(doms_cur[j]))->rd->pd) + goto match3; + } + /* No match - add perf. domains for a new rd */ + build_perf_domains(doms_new[i]); +match3: + ; + } +#endif + /* Remember the new sched domains: */ if (doms_cur != &fallback_doms) free_sched_domains(doms_cur, ndoms_cur); From 636500311e8efe6b3fe28094dc070d45e18f16cd Mon Sep 17 00:00:00 2001 From: Quentin Perret Date: Fri, 18 May 2018 10:16:33 +0100 Subject: [PATCH 0478/1103] FROMLIST: sched/topology: Lowest CPU asymmetry sched_domain level pointer Add another member to the family of per-cpu sched_domain shortcut pointers. This one, sd_asym_cpucapacity, points to the lowest level at which the SD_ASYM_CPUCAPACITY flag is set. While at it, rename the sd_asym shortcut to sd_asym_packing to avoid confusions. Generally speaking, the largest opportunity to save energy via scheduling comes from a smarter exploitation of heterogeneous platforms (i.e. big.LITTLE). Consequently, the sd_asym_cpucapacity shortcut will be used at first as the lowest domain where Energy-Aware Scheduling (EAS) should be applied. For example, it is possible to apply EAS within a socket on a multi-socket system, as long as each socket has an asymmetric topology. Cross-sockets wake-up balancing will only happen when the system is over-utilized, or this_cpu and prev_cpu are in different sockets. Change-Id: I6a1585a858aa6ca0b9d8cc66f8559dfa2797a6af cc: Ingo Molnar cc: Peter Zijlstra Suggested-by: Morten Rasmussen (Applied from https://lore.kernel.org/lkml/20180912091309.7551-8-quentin.perret@arm.com/) Signed-off-by: Quentin Perret --- kernel/sched/fair.c | 2 +- kernel/sched/sched.h | 3 ++- kernel/sched/topology.c | 8 ++++++-- 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c index c5d8bbd4ebd6..84ad795d7d38 100644 --- a/kernel/sched/fair.c +++ b/kernel/sched/fair.c @@ -9254,7 +9254,7 @@ static void nohz_balancer_kick(struct rq *rq) } } - sd = rcu_dereference(per_cpu(sd_asym, cpu)); + sd = rcu_dereference(per_cpu(sd_asym_packing, cpu)); if (sd) { for_each_cpu(i, sched_domain_span(sd)) { if (i == cpu || diff --git a/kernel/sched/sched.h b/kernel/sched/sched.h index b781aec416b9..cf7aeca1f1ab 100644 --- a/kernel/sched/sched.h +++ b/kernel/sched/sched.h @@ -1204,7 +1204,8 @@ DECLARE_PER_CPU(int, sd_llc_size); DECLARE_PER_CPU(int, sd_llc_id); DECLARE_PER_CPU(struct sched_domain_shared *, sd_llc_shared); DECLARE_PER_CPU(struct sched_domain *, sd_numa); -DECLARE_PER_CPU(struct sched_domain *, sd_asym); +DECLARE_PER_CPU(struct sched_domain *, sd_asym_packing); +DECLARE_PER_CPU(struct sched_domain *, sd_asym_cpucapacity); extern struct static_key_false sched_asym_cpucapacity; struct sched_group_capacity { diff --git a/kernel/sched/topology.c b/kernel/sched/topology.c index 23230b4d629f..c63a6dee1088 100644 --- a/kernel/sched/topology.c +++ b/kernel/sched/topology.c @@ -508,7 +508,8 @@ DEFINE_PER_CPU(int, sd_llc_size); DEFINE_PER_CPU(int, sd_llc_id); DEFINE_PER_CPU(struct sched_domain_shared *, sd_llc_shared); DEFINE_PER_CPU(struct sched_domain *, sd_numa); -DEFINE_PER_CPU(struct sched_domain *, sd_asym); +DEFINE_PER_CPU(struct sched_domain *, sd_asym_packing); +DEFINE_PER_CPU(struct sched_domain *, sd_asym_cpucapacity); DEFINE_STATIC_KEY_FALSE(sched_asym_cpucapacity); static void update_top_cache_domain(int cpu) @@ -534,7 +535,10 @@ static void update_top_cache_domain(int cpu) rcu_assign_pointer(per_cpu(sd_numa, cpu), sd); sd = highest_flag_domain(cpu, SD_ASYM_PACKING); - rcu_assign_pointer(per_cpu(sd_asym, cpu), sd); + rcu_assign_pointer(per_cpu(sd_asym_packing, cpu), sd); + + sd = lowest_flag_domain(cpu, SD_ASYM_CPUCAPACITY); + rcu_assign_pointer(per_cpu(sd_asym_cpucapacity, cpu), sd); } /* From bfcbb36e64dd79011b7a4debe7f8c637f9f00552 Mon Sep 17 00:00:00 2001 From: Quentin Perret Date: Thu, 21 Jun 2018 15:34:57 +0100 Subject: [PATCH 0479/1103] FROMLIST: sched/topology: Disable EAS on inappropriate platforms Energy Aware Scheduling (EAS) in its current form is most relevant on platforms with asymmetric CPU topologies (e.g. Arm big.LITTLE) since this is where there is a lot of potential for saving energy through scheduling. This is particularly true since the Energy Model only includes the active power costs of CPUs, hence not providing enough data to compare packing-vs-spreading strategies. As such, disable EAS on root domains where the SD_ASYM_CPUCAPACITY flag is not set. While at it, disable EAS on systems where the complexity of the Energy Model is too high since that could lead to unacceptable scheduling overhead. All in all, EAS can be used on a root domain if and only if: 1. the ENERGY_AWARE sched_feat is enabled; 2. the root domain has an asymmetric CPU capacity topology; 3. the complexity of the root domain's EM is low enough to keep scheduling overheads low. Change-Id: I51ca817b328e1990790b5d801e05fc844199ac9c cc: Ingo Molnar cc: Peter Zijlstra (Applied from https://lore.kernel.org/lkml/20180912091309.7551-9-quentin.perret@arm.com/) Signed-off-by: Quentin Perret --- kernel/sched/topology.c | 50 ++++++++++++++++++++++++++++++++++++++++- 1 file changed, 49 insertions(+), 1 deletion(-) diff --git a/kernel/sched/topology.c b/kernel/sched/topology.c index c63a6dee1088..0e83747897b4 100644 --- a/kernel/sched/topology.c +++ b/kernel/sched/topology.c @@ -270,12 +270,45 @@ static void destroy_perf_domain_rcu(struct rcu_head *rp) free_pd(pd); } +/* + * EAS can be used on a root domain if it meets all the following conditions: + * 1. the ENERGY_AWARE sched_feat is enabled; + * 2. the SD_ASYM_CPUCAPACITY flag is set in the sched_domain hierarchy. + * 3. the EM complexity is low enough to keep scheduling overheads low; + * + * The complexity of the Energy Model is defined as: + * + * C = nr_pd * (nr_cpus + nr_cs) + * + * with parameters defined as: + * - nr_pd: the number of performance domains + * - nr_cpus: the number of CPUs + * - nr_cs: the sum of the number of capacity states of all performance + * domains (for example, on a system with 2 performance domains, + * with 10 capacity states each, nr_cs = 2 * 10 = 20). + * + * It is generally not a good idea to use such a model in the wake-up path on + * very complex platforms because of the associated scheduling overheads. The + * arbitrary constraint below prevents that. It makes EAS usable up to 16 CPUs + * with per-CPU DVFS and less than 8 capacity states each, for example. + */ +#define EM_MAX_COMPLEXITY 2048 + static void build_perf_domains(const struct cpumask *cpu_map) { + int i, nr_pd = 0, nr_cs = 0, nr_cpus = cpumask_weight(cpu_map); struct perf_domain *pd = NULL, *tmp; int cpu = cpumask_first(cpu_map); struct root_domain *rd = cpu_rq(cpu)->rd; - int i; + + /* EAS is enabled for asymmetric CPU capacity topologies. */ + if (!per_cpu(sd_asym_cpucapacity, cpu)) { + if (sched_debug()) { + pr_info("rd %*pbl: CPUs do not have asymmetric capacities\n", + cpumask_pr_args(cpu_map)); + } + goto free; + } for_each_cpu(i, cpu_map) { /* Skip already covered CPUs. */ @@ -288,6 +321,21 @@ static void build_perf_domains(const struct cpumask *cpu_map) goto free; tmp->next = pd; pd = tmp; + + /* + * Count performance domains and capacity states for the + * complexity check. + */ + nr_pd++; + nr_cs += em_pd_nr_cap_states(pd->obj); + } + + /* Bail out if the Energy Model complexity is too high. */ + if (nr_pd * (nr_cs + nr_cpus) > EM_MAX_COMPLEXITY) { + if (sched_debug()) + pr_info("rd %*pbl: EM complexity is too high\n ", + cpumask_pr_args(cpu_map)); + goto free; } perf_domain_debug(cpu_map, pd); From 400f5533bce14955e46b91162f88c4f16dbc3c40 Mon Sep 17 00:00:00 2001 From: Quentin Perret Date: Wed, 18 Jul 2018 12:09:49 +0100 Subject: [PATCH 0480/1103] FROMLIST: sched/fair: Clean-up update_sg_lb_stats parameters In preparation for the introduction of a new root domain flag which can be set during load balance (the 'overutilized' flag), clean-up the set of parameters passed to update_sg_lb_stats(). More specifically, the 'local_group' and 'local_idx' parameters can be removed since they can easily be reconstructed from within the function. While at it, transform the 'overload' parameter into a flag stored in the 'sg_status' parameter hence facilitating the definition of new flags when needed. Change-Id: I0a7b6766152f8c7037dde2485eee3f498cbc49c1 Cc: Ingo Molnar Cc: Peter Zijlstra Suggested-by: Peter Zijlstra Suggested-by: Valentin Schneider (Applied from https://lore.kernel.org/lkml/20180912091309.7551-10-quentin.perret@arm.com/) Signed-off-by: Quentin Perret --- kernel/sched/fair.c | 27 +++++++++++---------------- kernel/sched/sched.h | 3 +++ 2 files changed, 14 insertions(+), 16 deletions(-) diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c index 84ad795d7d38..1dfa9226ead5 100644 --- a/kernel/sched/fair.c +++ b/kernel/sched/fair.c @@ -7869,16 +7869,16 @@ static bool update_nohz_stats(struct rq *rq, bool force) * update_sg_lb_stats - Update sched_group's statistics for load balancing. * @env: The load balancing environment. * @group: sched_group whose statistics are to be updated. - * @load_idx: Load index of sched_domain of this_cpu for load calc. - * @local_group: Does group contain this_cpu. * @sgs: variable to hold the statistics for this group. - * @overload: Indicate pullable load (e.g. >1 runnable task). + * @sg_status: Holds flag indicating the status of the sched_group */ static inline void update_sg_lb_stats(struct lb_env *env, - struct sched_group *group, int load_idx, - int local_group, struct sg_lb_stats *sgs, - bool *overload) + struct sched_group *group, + struct sg_lb_stats *sgs, + int *sg_status) { + int local_group = cpumask_test_cpu(env->dst_cpu, sched_group_span(group)); + int load_idx = get_sd_load_idx(env->sd, env->idle); unsigned long load; int i, nr_running; @@ -7902,7 +7902,7 @@ static inline void update_sg_lb_stats(struct lb_env *env, nr_running = rq->nr_running; if (nr_running > 1) - *overload = true; + *sg_status |= SG_OVERLOAD; #ifdef CONFIG_NUMA_BALANCING sgs->nr_numa_running += rq->nr_numa_running; @@ -7918,7 +7918,7 @@ static inline void update_sg_lb_stats(struct lb_env *env, if (env->sd->flags & SD_ASYM_CPUCAPACITY && sgs->group_misfit_task_load < rq->misfit_task_load) { sgs->group_misfit_task_load = rq->misfit_task_load; - *overload = 1; + *sg_status |= SG_OVERLOAD; } } @@ -8063,17 +8063,14 @@ static inline void update_sd_lb_stats(struct lb_env *env, struct sd_lb_stats *sd struct sched_group *sg = env->sd->groups; struct sg_lb_stats *local = &sds->local_stat; struct sg_lb_stats tmp_sgs; - int load_idx; - bool overload = false; bool prefer_sibling = child && child->flags & SD_PREFER_SIBLING; + int sg_status = 0; #ifdef CONFIG_NO_HZ_COMMON if (env->idle == CPU_NEWLY_IDLE && READ_ONCE(nohz.has_blocked)) env->flags |= LBF_NOHZ_STATS; #endif - load_idx = get_sd_load_idx(env->sd, env->idle); - do { struct sg_lb_stats *sgs = &tmp_sgs; int local_group; @@ -8088,8 +8085,7 @@ static inline void update_sd_lb_stats(struct lb_env *env, struct sd_lb_stats *sd update_group_capacity(env->sd, env->dst_cpu); } - update_sg_lb_stats(env, sg, load_idx, local_group, sgs, - &overload); + update_sg_lb_stats(env, sg, sgs, &sg_status); if (local_group) goto next_group; @@ -8139,8 +8135,7 @@ static inline void update_sd_lb_stats(struct lb_env *env, struct sd_lb_stats *sd if (!env->sd->parent) { /* update overload indicator if we are at root domain */ - if (READ_ONCE(env->dst_rq->rd->overload) != overload) - WRITE_ONCE(env->dst_rq->rd->overload, overload); + WRITE_ONCE(env->dst_rq->rd->overload, sg_status & SG_OVERLOAD); } } diff --git a/kernel/sched/sched.h b/kernel/sched/sched.h index cf7aeca1f1ab..088f20e72b29 100644 --- a/kernel/sched/sched.h +++ b/kernel/sched/sched.h @@ -707,6 +707,9 @@ struct perf_domain { struct rcu_head rcu; }; +/* Scheduling group status flags */ +#define SG_OVERLOAD 0x1 /* More than one runnable task on a CPU. */ + /* * We add the notion of a root-domain which will be used to define per-domain * variables. Each exclusive cpuset essentially defines an island domain by From 780f46ccea49edabf62664fe0dd30fae7e54f307 Mon Sep 17 00:00:00 2001 From: Morten Rasmussen Date: Sat, 9 May 2015 16:49:57 +0100 Subject: [PATCH 0481/1103] FROMLIST: sched: Add over-utilization/tipping point indicator Energy-aware scheduling is only meant to be active while the system is _not_ over-utilized. That is, there are spare cycles available to shift tasks around based on their actual utilization to get a more energy-efficient task distribution without depriving any tasks. When above the tipping point task placement is done the traditional way based on load_avg, spreading the tasks across as many cpus as possible based on priority scaled load to preserve smp_nice. Below the tipping point we want to use util_avg instead. We need to define a criteria for when we make the switch. The util_avg for each cpu converges towards 100% regardless of how many additional tasks we may put on it. If we define over-utilized as: sum_{cpus}(rq.cfs.avg.util_avg) + margin > sum_{cpus}(rq.capacity) some individual cpus may be over-utilized running multiple tasks even when the above condition is false. That should be okay as long as we try to spread the tasks out to avoid per-cpu over-utilization as much as possible and if all tasks have the _same_ priority. If the latter isn't true, we have to consider priority to preserve smp_nice. For example, we could have n_cpus nice=-10 util_avg=55% tasks and n_cpus/2 nice=0 util_avg=60% tasks. Balancing based on util_avg we are likely to end up with nice=-10 tasks sharing cpus and nice=0 tasks getting their own as we 1.5*n_cpus tasks in total and 55%+55% is less over-utilized than 55%+60% for those cpus that have to be shared. The system utilization is only 85% of the system capacity, but we are breaking smp_nice. To be sure not to break smp_nice, we have defined over-utilization conservatively as when any cpu in the system is fully utilized at its highest frequency instead: cpu_rq(any).cfs.avg.util_avg + margin > cpu_rq(any).capacity IOW, as soon as one cpu is (nearly) 100% utilized, we switch to load_avg to factor in priority to preserve smp_nice. With this definition, we can skip periodic load-balance as no cpu has an always-running task when the system is not over-utilized. All tasks will be periodic and we can balance them at wake-up. This conservative condition does however mean that some scenarios that could benefit from energy-aware decisions even if one cpu is fully utilized would not get those benefits. For systems where some cpus might have reduced capacity on some cpus (RT-pressure and/or big.LITTLE), we want periodic load-balance checks as soon a just a single cpu is fully utilized as it might one of those with reduced capacity and in that case we want to migrate it. Change-Id: Id230f344c32cb077a578d4c1aedc532f1470a3e3 cc: Ingo Molnar cc: Peter Zijlstra Signed-off-by: Morten Rasmussen [ - Added a comment explaining why new tasks are not accounted during overutilization detection - ANDROID: applied from https://lore.kernel.org/lkml/20180912091309.7551-11-quentin.perret@arm.com/] Signed-off-by: Quentin Perret --- kernel/sched/fair.c | 59 ++++++++++++++++++++++++++++++++++++++++++-- kernel/sched/sched.h | 4 +++ 2 files changed, 61 insertions(+), 2 deletions(-) diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c index 1dfa9226ead5..1bd132cdd4ff 100644 --- a/kernel/sched/fair.c +++ b/kernel/sched/fair.c @@ -5079,6 +5079,24 @@ static inline void hrtick_update(struct rq *rq) } #endif +#ifdef CONFIG_SMP +static inline unsigned long cpu_util(int cpu); +static unsigned long capacity_of(int cpu); + +static inline bool cpu_overutilized(int cpu) +{ + return (capacity_of(cpu) * 1024) < (cpu_util(cpu) * capacity_margin); +} + +static inline void update_overutilized_status(struct rq *rq) +{ + if (!READ_ONCE(rq->rd->overutilized) && cpu_overutilized(rq->cpu)) + WRITE_ONCE(rq->rd->overutilized, SG_OVERUTILIZED); +} +#else +static inline void update_overutilized_status(struct rq *rq) { } +#endif + /* * The enqueue_task method is called before nr_running is * increased. Here we update the fair scheduling stats and @@ -5136,8 +5154,26 @@ enqueue_task_fair(struct rq *rq, struct task_struct *p, int flags) update_cfs_group(se); } - if (!se) + if (!se) { add_nr_running(rq, 1); + /* + * Since new tasks are assigned an initial util_avg equal to + * half of the spare capacity of their CPU, tiny tasks have the + * ability to cross the overutilized threshold, which will + * result in the load balancer ruining all the task placement + * done by EAS. As a way to mitigate that effect, do not account + * for the first enqueue operation of new tasks during the + * overutilized flag detection. + * + * A better way of solving this problem would be to wait for + * the PELT signals of tasks to converge before taking them + * into account, but that is not straightforward to implement, + * and the following generally works well enough in practice. + */ + if (flags & ENQUEUE_WAKEUP) + update_overutilized_status(rq); + + } hrtick_update(rq); } @@ -7904,6 +7940,9 @@ static inline void update_sg_lb_stats(struct lb_env *env, if (nr_running > 1) *sg_status |= SG_OVERLOAD; + if (cpu_overutilized(i)) + *sg_status |= SG_OVERUTILIZED; + #ifdef CONFIG_NUMA_BALANCING sgs->nr_numa_running += rq->nr_numa_running; sgs->nr_preferred_running += rq->nr_preferred_running; @@ -8134,8 +8173,15 @@ static inline void update_sd_lb_stats(struct lb_env *env, struct sd_lb_stats *sd env->fbq_type = fbq_classify_group(&sds->busiest_stat); if (!env->sd->parent) { + struct root_domain *rd = env->dst_rq->rd; + /* update overload indicator if we are at root domain */ - WRITE_ONCE(env->dst_rq->rd->overload, sg_status & SG_OVERLOAD); + WRITE_ONCE(rd->overload, sg_status & SG_OVERLOAD); + + /* Update over-utilization (tipping point, U >= 0) indicator */ + WRITE_ONCE(rd->overutilized, sg_status & SG_OVERUTILIZED); + } else if (sg_status & SG_OVERUTILIZED) { + WRITE_ONCE(env->dst_rq->rd->overutilized, SG_OVERUTILIZED); } } @@ -8362,6 +8408,14 @@ static struct sched_group *find_busiest_group(struct lb_env *env) * this level. */ update_sd_lb_stats(env, &sds); + + if (sched_feat(ENERGY_AWARE)) { + struct root_domain *rd = env->dst_rq->rd; + + if (rcu_dereference(rd->pd) && !READ_ONCE(rd->overutilized)) + goto out_balanced; + } + local = &sds.local_stat; busiest = &sds.busiest_stat; @@ -9753,6 +9807,7 @@ static void task_tick_fair(struct rq *rq, struct task_struct *curr, int queued) task_tick_numa(rq, curr); update_misfit_status(curr, rq); + update_overutilized_status(task_rq(curr)); } /* diff --git a/kernel/sched/sched.h b/kernel/sched/sched.h index 088f20e72b29..b12f7dcb67b7 100644 --- a/kernel/sched/sched.h +++ b/kernel/sched/sched.h @@ -709,6 +709,7 @@ struct perf_domain { /* Scheduling group status flags */ #define SG_OVERLOAD 0x1 /* More than one runnable task on a CPU. */ +#define SG_OVERUTILIZED 0x2 /* One or more CPUs are over-utilized. */ /* * We add the notion of a root-domain which will be used to define per-domain @@ -732,6 +733,9 @@ struct root_domain { */ int overload; + /* Indicate one or more cpus over-utilized (tipping point) */ + int overutilized; + /* * The bit corresponding to a CPU gets set here if such CPU has more * than one runnable -deadline task (as it is below for RT tasks). From 07a26ad62e4792909f97481416504bd92744f093 Mon Sep 17 00:00:00 2001 From: Quentin Perret Date: Fri, 10 Nov 2017 11:20:03 +0000 Subject: [PATCH 0482/1103] FROMLIST: sched/fair: Introduce an energy estimation helper function In preparation for the definition of an energy-aware wakeup path, introduce a helper function to estimate the consequence on system energy when a specific task wakes-up on a specific CPU. compute_energy() estimates the capacity state to be reached by all performance domains and estimates the consumption of each online CPU according to its Energy Model and its percentage of busy time. Change-Id: Id7f3cc5acbcef42dc9fa5b6045099cbeee87612c Cc: Ingo Molnar Cc: Peter Zijlstra (Applied from https://lore.kernel.org/lkml/20180912091309.7551-12-quentin.perret@arm.com/) Signed-off-by: Quentin Perret --- kernel/sched/fair.c | 77 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 77 insertions(+) diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c index 1bd132cdd4ff..ef409cd63058 100644 --- a/kernel/sched/fair.c +++ b/kernel/sched/fair.c @@ -6341,6 +6341,83 @@ static int wake_cap(struct task_struct *p, int cpu, int prev_cpu) return !task_fits_capacity(p, min_cap); } +/* + * Predicts what cpu_util(@cpu) would return if @p was migrated (and enqueued) + * to @dst_cpu. + */ +static unsigned long cpu_util_next(int cpu, struct task_struct *p, int dst_cpu) +{ + struct cfs_rq *cfs_rq = &cpu_rq(cpu)->cfs; + unsigned long util_est, util = READ_ONCE(cfs_rq->avg.util_avg); + + /* + * If @p migrates from @cpu to another, remove its contribution. Or, + * if @p migrates from another CPU to @cpu, add its contribution. In + * the other cases, @cpu is not impacted by the migration, so the + * util_avg should already be correct. + */ + if (task_cpu(p) == cpu && dst_cpu != cpu) + util = max_t(long, util - task_util(p), 0); + else if (task_cpu(p) != cpu && dst_cpu == cpu) + util += task_util(p); + + if (sched_feat(UTIL_EST)) { + util_est = READ_ONCE(cfs_rq->avg.util_est.enqueued); + + /* + * During wake-up, the task isn't enqueued yet and doesn't + * appear in the cfs_rq->avg.util_est.enqueued of any rq, + * so just add it (if needed) to "simulate" what will be + * cpu_util() after the task has been enqueued. + */ + if (dst_cpu == cpu) + util_est += _task_util_est(p); + + util = max(util, util_est); + } + + return min_t(unsigned long, util, capacity_orig_of(cpu)); +} + +/* + * compute_energy(): Estimates the energy that would be consumed if @p was + * migrated to @dst_cpu. compute_energy() predicts what will be the utilization + * landscape of the * CPUs after the task migration, and uses the Energy Model + * to compute what would be the energy if we decided to actually migrate that + * task. + */ +static long compute_energy(struct task_struct *p, int dst_cpu, + struct perf_domain *pd) +{ + long util, max_util, sum_util, energy = 0; + int cpu; + + while (pd) { + max_util = sum_util = 0; + /* + * The capacity state of CPUs of the current rd can be driven by + * CPUs of another rd if they belong to the same performance + * domain. So, account for the utilization of these CPUs too + * by masking pd with cpu_online_mask instead of the rd span. + * + * If an entire performance domain is outside of the current rd, + * it will not appear in its pd list and will not be accounted + * by compute_energy(). + */ + for_each_cpu_and(cpu, perf_domain_span(pd), cpu_online_mask) { + util = cpu_util_next(cpu, p, dst_cpu); + util = schedutil_freq_util(cpu, util, ENERGY_UTIL); + max_util = max(util, max_util); + sum_util += util; + } + + energy += em_pd_energy(pd->obj, max_util, sum_util); + pd = pd->next; + } + + return energy; +} + /* * select_task_rq_fair: Select target runqueue for the waking task in domains * that have the 'sd_flag' flag set. In practice, this is SD_BALANCE_WAKE, From b4ef90f996ae8b26d1d71775b84676ab21af8875 Mon Sep 17 00:00:00 2001 From: Quentin Perret Date: Fri, 10 Nov 2017 12:17:34 +0000 Subject: [PATCH 0483/1103] FROMLIST: sched/fair: Select an energy-efficient CPU on task wake-up If an Energy Model (EM) is available and if the system isn't overutilized, re-route waking tasks into an energy-aware placement algorithm. The selection of an energy-efficient CPU for a task is achieved by estimating the impact on system-level active energy resulting from the placement of the task on the CPU with the highest spare capacity in each performance domain. This strategy spreads tasks in a performance domain and avoids overly aggressive task packing. The best CPU energy-wise is then selected if it saves a large enough amount of energy with respect to prev_cpu. Although it has already shown significant benefits on some existing targets, this approach cannot scale to platforms with numerous CPUs. This is an attempt to do something useful as writing a fast heuristic that performs reasonably well on a broad spectrum of architectures isn't an easy task. As such, the scope of usability of the energy-aware wake-up path is restricted to systems with the SD_ASYM_CPUCAPACITY flag set, and where the EM isn't too complex. Change-Id: Idf91ad38aef203d469751497f1169e26107858f5 Cc: Ingo Molnar Cc: Peter Zijlstra (Applied from https://lore.kernel.org/lkml/20180912091309.7551-13-quentin.perret@arm.com/) Signed-off-by: Quentin Perret --- kernel/sched/fair.c | 139 +++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 136 insertions(+), 3 deletions(-) diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c index ef409cd63058..d996f4421bdc 100644 --- a/kernel/sched/fair.c +++ b/kernel/sched/fair.c @@ -6418,6 +6418,114 @@ static long compute_energy(struct task_struct *p, int dst_cpu, return energy; } +/* + * find_energy_efficient_cpu(): Find most energy-efficient target CPU for the + * waking task. find_energy_efficient_cpu() looks for the CPU with maximum + * spare capacity in each performance domain and uses it as a potential + * candidate to execute the task. Then, it uses the Energy Model to figure + * out which of the CPU candidates is the most energy-efficient. + * + * The rationale for this heuristic is as follows. In a performance domain, + * all the most energy efficient CPU candidates (according to the Energy + * Model) are those for which we'll request a low frequency. When there are + * several CPUs for which the frequency request will be the same, we don't + * have enough data to break the tie between them, because the Energy Model + * only includes active power costs. With this model, if we assume that + * frequency requests follow utilization (e.g. using schedutil), the CPU with + * the maximum spare capacity in a performance domain is guaranteed to be among + * the best candidates of the performance domain. + * + * In practice, it could be preferable from an energy standpoint to pack + * small tasks on a CPU in order to let other CPUs go in deeper idle states, + * but that could also hurt our chances to go cluster idle, and we have no + * ways to tell with the current Energy Model if this is actually a good + * idea or not. So, find_energy_efficient_cpu() basically favors + * cluster-packing, and spreading inside a cluster. That should at least be + * a good thing for latency, and this is consistent with the idea that most + * of the energy savings of EAS come from the asymmetry of the system, and + * not so much from breaking the tie between identical CPUs. That's also the + * reason why EAS is enabled in the topology code only for systems where + * SD_ASYM_CPUCAPACITY is set. + */ +static int find_energy_efficient_cpu(struct task_struct *p, int prev_cpu, + struct perf_domain *pd) +{ + unsigned long prev_energy = ULONG_MAX, best_energy = ULONG_MAX; + int cpu, best_energy_cpu = prev_cpu; + struct perf_domain *head = pd; + unsigned long cpu_cap, util; + struct sched_domain *sd; + + sync_entity_load_avg(&p->se); + + if (!task_util_est(p)) + return prev_cpu; + + /* + * Energy-aware wake-up happens on the lowest sched_domain starting + * from sd_asym_cpucapacity spanning over this_cpu and prev_cpu. + */ + sd = rcu_dereference(*this_cpu_ptr(&sd_asym_cpucapacity)); + while (sd && !cpumask_test_cpu(prev_cpu, sched_domain_span(sd))) + sd = sd->parent; + if (!sd) + return prev_cpu; + + while (pd) { + unsigned long cur_energy, spare_cap, max_spare_cap = 0; + int max_spare_cap_cpu = -1; + + for_each_cpu_and(cpu, perf_domain_span(pd), sched_domain_span(sd)) { + if (!cpumask_test_cpu(cpu, &p->cpus_allowed)) + continue; + + /* Skip CPUs that will be overutilized. */ + util = cpu_util_next(cpu, p, cpu); + cpu_cap = capacity_of(cpu); + if (cpu_cap * 1024 < util * capacity_margin) + continue; + + /* Always use prev_cpu as a candidate. */ + if (cpu == prev_cpu) { + prev_energy = compute_energy(p, prev_cpu, head); + if (prev_energy < best_energy) + best_energy = prev_energy; + continue; + } + + /* + * Find the CPU with the maximum spare capacity in + * the performance domain + */ + spare_cap = cpu_cap - util; + if (spare_cap > max_spare_cap) { + max_spare_cap = spare_cap; + max_spare_cap_cpu = cpu; + } + } + + /* Evaluate the energy impact of using this CPU. */ + if (max_spare_cap_cpu >= 0) { + cur_energy = compute_energy(p, max_spare_cap_cpu, head); + if (cur_energy < best_energy) { + best_energy = cur_energy; + best_energy_cpu = max_spare_cap_cpu; + } + } + pd = pd->next; + } + + /* + * Pick the best CPU if prev_cpu cannot be used, or if it saves at + * least 6% of the energy used by prev_cpu. + */ + if (prev_energy == ULONG_MAX || + (prev_energy - best_energy) > (prev_energy >> 4)) + return best_energy_cpu; + + return prev_cpu; +} + /* * select_task_rq_fair: Select target runqueue for the waking task in domains * that have the 'sd_flag' flag set. In practice, this is SD_BALANCE_WAKE, @@ -6439,13 +6547,37 @@ select_task_rq_fair(struct task_struct *p, int prev_cpu, int sd_flag, int wake_f int want_affine = 0; int sync = (wake_flags & WF_SYNC) && !(current->flags & PF_EXITING); + rcu_read_lock(); if (sd_flag & SD_BALANCE_WAKE) { record_wakee(p); - want_affine = !wake_wide(p) && !wake_cap(p, cpu, prev_cpu) - && cpumask_test_cpu(cpu, &p->cpus_allowed); + + /* + * Forkees are not accepted in the energy-aware wake-up path + * because they don't have any useful utilization data yet and + * it's not possible to forecast their impact on energy + * consumption. Consequently, they will be placed by + * find_idlest_cpu() on the least loaded CPU, which might turn + * out to be energy-inefficient in some use-cases. The + * alternative would be to bias new tasks towards specific types + * of CPUs first, or to try to infer their util_avg from the + * parent task, but those heuristics could hurt other use-cases + * too. So, until someone finds a better way to solve this, + * let's keep things simple by re-using the existing slow path. + */ + if (sched_feat(ENERGY_AWARE)) { + struct root_domain *rd = cpu_rq(cpu)->rd; + struct perf_domain *pd = rcu_dereference(rd->pd); + + if (pd && !READ_ONCE(rd->overutilized)) { + new_cpu = find_energy_efficient_cpu(p, prev_cpu, pd); + goto unlock; + } + } + + want_affine = !wake_wide(p) && !wake_cap(p, cpu, prev_cpu) && + cpumask_test_cpu(cpu, &p->cpus_allowed); } - rcu_read_lock(); for_each_domain(cpu, tmp) { if (!(tmp->flags & SD_LOAD_BALANCE)) break; @@ -6480,6 +6612,7 @@ select_task_rq_fair(struct task_struct *p, int prev_cpu, int sd_flag, int wake_f if (want_affine) current->recent_used_cpu = cpu; } +unlock: rcu_read_unlock(); return new_cpu; From fee08bed771a1fe7765e18c6c5bf1eb4ff8b93e8 Mon Sep 17 00:00:00 2001 From: Quentin Perret Date: Fri, 3 Aug 2018 11:19:36 +0100 Subject: [PATCH 0484/1103] FROMLIST: sched/topology: Make Energy Aware Scheduling depend on schedutil Energy Aware Scheduling (EAS) is designed with the assumption that frequencies of CPUs follow their utilization value. When using a CPUFreq governor other than schedutil, the chances of this assumption being true are small, if any. When schedutil is being used, EAS' predictions are at least consistent with the frequency requests. Although those requests have no guarantees to be honored by the hardware, they should at least guide DVFS in the right direction and provide some hope in regards to the EAS model being accurate. To make sure EAS is only used in a sane configuration, create a strong dependency on schedutil being used. Since having sugov compiled-in does not provide that guarantee, make CPUFreq call a scheduler function on on governor changes hence letting it rebuild the scheduling domains, check the governors of the online CPUs, and enable/disable EAS accordingly. Change-Id: I983ab94a22fce45376f50a892653a39d5c8cf5b1 Cc: Ingo Molnar Cc: Peter Zijlstra Cc: "Rafael J. Wysocki" (Applied from https://lore.kernel.org/lkml/20180912091309.7551-14-quentin.perret@arm.com/) Signed-off-by: Quentin Perret --- drivers/cpufreq/cpufreq.c | 2 ++ include/linux/sched/cpufreq.h | 9 ++++++++ kernel/sched/cpufreq_schedutil.c | 37 ++++++++++++++++++++++++++++++-- kernel/sched/sched.h | 4 +--- kernel/sched/topology.c | 23 ++++++++++++++++---- 5 files changed, 66 insertions(+), 9 deletions(-) diff --git a/drivers/cpufreq/cpufreq.c b/drivers/cpufreq/cpufreq.c index 98b5bac02dff..e1f8e760a5e8 100644 --- a/drivers/cpufreq/cpufreq.c +++ b/drivers/cpufreq/cpufreq.c @@ -26,6 +26,7 @@ #include #include #include +#include #include #include #include @@ -2280,6 +2281,7 @@ static int cpufreq_set_policy(struct cpufreq_policy *policy, ret = cpufreq_start_governor(policy); if (!ret) { pr_debug("cpufreq: governor change\n"); + sched_cpufreq_governor_change(policy, old_gov); return 0; } cpufreq_exit_governor(policy); diff --git a/include/linux/sched/cpufreq.h b/include/linux/sched/cpufreq.h index afa940cd50dc..a2ead52feb17 100644 --- a/include/linux/sched/cpufreq.h +++ b/include/linux/sched/cpufreq.h @@ -2,6 +2,7 @@ #ifndef _LINUX_SCHED_CPUFREQ_H #define _LINUX_SCHED_CPUFREQ_H +#include #include /* @@ -28,4 +29,12 @@ static inline unsigned long map_util_freq(unsigned long util, } #endif /* CONFIG_CPU_FREQ */ +#if defined(CONFIG_ENERGY_MODEL) && defined(CONFIG_CPU_FREQ_GOV_SCHEDUTIL) +void sched_cpufreq_governor_change(struct cpufreq_policy *policy, + struct cpufreq_governor *old_gov); +#else +static inline void sched_cpufreq_governor_change(struct cpufreq_policy *policy, + struct cpufreq_governor *old_gov) { } +#endif + #endif /* _LINUX_SCHED_CPUFREQ_H */ diff --git a/kernel/sched/cpufreq_schedutil.c b/kernel/sched/cpufreq_schedutil.c index 8356cb0072a6..d2236d1166f4 100644 --- a/kernel/sched/cpufreq_schedutil.c +++ b/kernel/sched/cpufreq_schedutil.c @@ -632,7 +632,7 @@ static struct kobj_type sugov_tunables_ktype = { /********************** cpufreq governor interface *********************/ -static struct cpufreq_governor schedutil_gov; +struct cpufreq_governor schedutil_gov; static struct sugov_policy *sugov_policy_alloc(struct cpufreq_policy *policy) { @@ -891,7 +891,7 @@ static void sugov_limits(struct cpufreq_policy *policy) sg_policy->need_freq_update = true; } -static struct cpufreq_governor schedutil_gov = { +struct cpufreq_governor schedutil_gov = { .name = "schedutil", .owner = THIS_MODULE, .dynamic_switching = true, @@ -914,3 +914,36 @@ static int __init sugov_register(void) return cpufreq_register_governor(&schedutil_gov); } fs_initcall(sugov_register); + +#ifdef CONFIG_ENERGY_MODEL +extern bool sched_energy_update; +static DEFINE_MUTEX(rebuild_sd_mutex); + +static void rebuild_sd_workfn(struct work_struct *work) +{ + mutex_lock(&rebuild_sd_mutex); + sched_energy_update = true; + rebuild_sched_domains(); + sched_energy_update = false; + mutex_unlock(&rebuild_sd_mutex); +} +static DECLARE_WORK(rebuild_sd_work, rebuild_sd_workfn); + +/* + * EAS shouldn't be attempted without sugov, so rebuild the sched_domains + * on governor changes to make sure the scheduler knows about it. + */ +void sched_cpufreq_governor_change(struct cpufreq_policy *policy, + struct cpufreq_governor *old_gov) +{ + if (old_gov == &schedutil_gov || policy->governor == &schedutil_gov) { + /* + * When called from the cpufreq_register_driver() path, the + * cpu_hotplug_lock is already held, so use a work item to + * avoid nested locking in rebuild_sched_domains(). + */ + schedule_work(&rebuild_sd_work); + } + +} +#endif diff --git a/kernel/sched/sched.h b/kernel/sched/sched.h index b12f7dcb67b7..dadb63db5a3d 100644 --- a/kernel/sched/sched.h +++ b/kernel/sched/sched.h @@ -2265,10 +2265,8 @@ unsigned long scale_irq_capacity(unsigned long util, unsigned long irq, unsigned } #endif -#ifdef CONFIG_SMP -#ifdef CONFIG_ENERGY_MODEL +#if defined(CONFIG_ENERGY_MODEL) && defined(CONFIG_CPU_FREQ_GOV_SCHEDUTIL) #define perf_domain_span(pd) (to_cpumask(((pd)->obj->cpus))) #else #define perf_domain_span(pd) NULL #endif -#endif diff --git a/kernel/sched/topology.c b/kernel/sched/topology.c index 0e83747897b4..8f7baceb150d 100644 --- a/kernel/sched/topology.c +++ b/kernel/sched/topology.c @@ -201,7 +201,9 @@ sd_parent_degenerate(struct sched_domain *sd, struct sched_domain *parent) return 1; } -#ifdef CONFIG_ENERGY_MODEL +#if defined(CONFIG_ENERGY_MODEL) && defined(CONFIG_CPU_FREQ_GOV_SCHEDUTIL) +bool sched_energy_update; + static void free_pd(struct perf_domain *pd) { struct perf_domain *tmp; @@ -275,6 +277,7 @@ static void destroy_perf_domain_rcu(struct rcu_head *rp) * 1. the ENERGY_AWARE sched_feat is enabled; * 2. the SD_ASYM_CPUCAPACITY flag is set in the sched_domain hierarchy. * 3. the EM complexity is low enough to keep scheduling overheads low; + * 4. schedutil is driving the frequency of all CPUs of the rd; * * The complexity of the Energy Model is defined as: * @@ -294,12 +297,15 @@ static void destroy_perf_domain_rcu(struct rcu_head *rp) */ #define EM_MAX_COMPLEXITY 2048 +extern struct cpufreq_governor schedutil_gov; static void build_perf_domains(const struct cpumask *cpu_map) { int i, nr_pd = 0, nr_cs = 0, nr_cpus = cpumask_weight(cpu_map); struct perf_domain *pd = NULL, *tmp; int cpu = cpumask_first(cpu_map); struct root_domain *rd = cpu_rq(cpu)->rd; + struct cpufreq_policy *policy; + struct cpufreq_governor *gov; /* EAS is enabled for asymmetric CPU capacity topologies. */ if (!per_cpu(sd_asym_cpucapacity, cpu)) { @@ -315,6 +321,15 @@ static void build_perf_domains(const struct cpumask *cpu_map) if (find_pd(pd, i)) continue; + /* Do not attempt EAS if schedutil is not being used. */ + policy = cpufreq_cpu_get(i); + if (!policy) + goto free; + gov = policy->governor; + cpufreq_cpu_put(policy); + if (gov != &schedutil_gov) + goto free; + /* Create the new pd and add it to the local list. */ tmp = pd_init(i); if (!tmp) @@ -357,7 +372,7 @@ static void build_perf_domains(const struct cpumask *cpu_map) } #else static void free_pd(struct perf_domain *pd) { } -#endif /* CONFIG_ENERGY_MODEL */ +#endif /* CONFIG_ENERGY_MODEL && CONFIG_CPU_FREQ_GOV_SCHEDUTIL*/ static void free_rootdomain(struct rcu_head *rcu) { @@ -2155,10 +2170,10 @@ void partition_sched_domains(int ndoms_new, cpumask_var_t doms_new[], ; } -#ifdef CONFIG_ENERGY_MODEL +#if defined(CONFIG_ENERGY_MODEL) && defined(CONFIG_CPU_FREQ_GOV_SCHEDUTIL) /* Build perf. domains: */ for (i = 0; i < ndoms_new; i++) { - for (j = 0; j < n; j++) { + for (j = 0; j < n && !sched_energy_update; j++) { if (cpumask_equal(doms_new[i], doms_cur[j]) && cpu_rq(cpumask_first(doms_cur[j]))->rd->pd) goto match3; From fb7788555cff1c81af96282e6e6f5880fadb0edb Mon Sep 17 00:00:00 2001 From: Morten Rasmussen Date: Thu, 2 Jul 2015 17:16:34 +0100 Subject: [PATCH 0485/1103] ANDROID: sched: Prevent unnecessary active balance of single task in sched group Scenarios with the busiest group having just one task and the local being idle on topologies with sched groups with different numbers of cpus manage to dodge all load-balance bailout conditions resulting the nr_balance_failed counter to be incremented. This eventually causes a pointless active migration of the task. This patch prevents this by not incrementing the counter when the busiest group only has one task. ASYM_PACKING migrations and migrations due to reduced capacity should still take place as these are explicitly captured by need_active_balance(). A better solution would be to not attempt the load-balance in the first place, but that requires significant changes to the order of bailout conditions and statistics gathering. cc: Ingo Molnar cc: Peter Zijlstra Signed-off-by: Morten Rasmussen Change-Id: I28f69c72febe0211decbe77b7bc3e48839d3d7b3 --- kernel/sched/fair.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c index d996f4421bdc..b337bde2d995 100644 --- a/kernel/sched/fair.c +++ b/kernel/sched/fair.c @@ -7205,6 +7205,7 @@ struct lb_env { int new_dst_cpu; enum cpu_idle_type idle; long imbalance; + unsigned int src_grp_nr_running; /* The set of CPUs under consideration for load-balancing */ struct cpumask *cpus; @@ -8382,6 +8383,8 @@ static inline void update_sd_lb_stats(struct lb_env *env, struct sd_lb_stats *sd if (env->sd->flags & SD_NUMA) env->fbq_type = fbq_classify_group(&sds->busiest_stat); + env->src_grp_nr_running = sds->busiest_stat.sum_nr_running; + if (!env->sd->parent) { struct root_domain *rd = env->dst_rq->rd; @@ -9060,7 +9063,8 @@ static int load_balance(int this_cpu, struct rq *this_rq, * excessive cache_hot migrations and active balances. */ if (idle != CPU_NEWLY_IDLE) - sd->nr_balance_failed++; + if (env.src_grp_nr_running > 1) + sd->nr_balance_failed++; if (need_active_balance(&env)) { unsigned long flags; From ac2aada0ecf44d1dd023ae823e2e5eae3a06223b Mon Sep 17 00:00:00 2001 From: Dietmar Eggemann Date: Mon, 26 Jan 2015 19:47:28 +0000 Subject: [PATCH 0486/1103] ANDROID: sched: Enable idle balance to pull single task towards cpu with higher capacity We do not want to miss out on the ability to pull a single remaining task from a potential source cpu towards an idle destination cpu. Add an extra criteria to need_active_balance() to kick off active load balance if the source cpu is over-utilized and has lower capacity than the destination cpu. cc: Ingo Molnar cc: Peter Zijlstra Signed-off-by: Morten Rasmussen Signed-off-by: Dietmar Eggemann Change-Id: Iea3b42b2a0f8d8a4252e42ba67cc33381a4a1075 --- kernel/sched/fair.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c index b337bde2d995..172b17a2b2e9 100644 --- a/kernel/sched/fair.c +++ b/kernel/sched/fair.c @@ -8845,6 +8845,13 @@ static int need_active_balance(struct lb_env *env) if (env->src_grp_type == group_misfit_task) return 1; + if ((capacity_of(env->src_cpu) < capacity_of(env->dst_cpu)) && + env->src_rq->cfs.h_nr_running == 1 && + cpu_overutilized(env->src_cpu) && + !cpu_overutilized(env->dst_cpu)) { + return 1; + } + return unlikely(sd->nr_balance_failed > sd->cache_nice_tries+2); } From a642460a6161ea0f74876da385b4b72a98abeeac Mon Sep 17 00:00:00 2001 From: Dietmar Eggemann Date: Tue, 3 Jul 2018 10:35:03 +0100 Subject: [PATCH 0487/1103] ANDROID: arm, arm64: Enable kernel config options required for EAS arm and arm64: Add Cgroups support Add Energy Model Add CpuFreq governors and make schedutil default for arm: Add Cpuset support Add Scheduler autogroups Add DIE sched domain level Signed-off-by: Dietmar Eggemann Change-Id: Ib52a0bd27702c3f2c3d692e49c9c8e2fbbea2cf7 --- arch/arm/configs/multi_v7_defconfig | 16 ++++++++++++---- arch/arm64/configs/defconfig | 10 ++++++++-- 2 files changed, 20 insertions(+), 6 deletions(-) diff --git a/arch/arm/configs/multi_v7_defconfig b/arch/arm/configs/multi_v7_defconfig index fc33444e94f0..2721877d5a11 100644 --- a/arch/arm/configs/multi_v7_defconfig +++ b/arch/arm/configs/multi_v7_defconfig @@ -2,6 +2,12 @@ CONFIG_SYSVIPC=y CONFIG_NO_HZ=y CONFIG_HIGH_RES_TIMERS=y CONFIG_CGROUPS=y +CONFIG_CGROUP_SCHED=y +CONFIG_FAIR_GROUP_SCHED=y +CONFIG_CGROUP_FREEZER=y +CONFIG_CPUSETS=y +CONFIG_PROC_PID_CPUSET=y +CONFIG_SCHED_AUTOGROUP=y CONFIG_BLK_DEV_INITRD=y CONFIG_EMBEDDED=y CONFIG_PERF_EVENTS=y @@ -116,6 +122,7 @@ CONFIG_PCI_ENDPOINT=y CONFIG_PCI_ENDPOINT_CONFIGFS=y CONFIG_PCI_EPF_TEST=m CONFIG_SMP=y +CONFIG_SCHED_MC=y CONFIG_NR_CPUS=16 CONFIG_SECCOMP=y CONFIG_ARM_APPENDED_DTB=y @@ -124,10 +131,10 @@ CONFIG_KEXEC=y CONFIG_EFI=y CONFIG_CPU_FREQ=y CONFIG_CPU_FREQ_STAT=y -CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMAND=y -CONFIG_CPU_FREQ_GOV_POWERSAVE=m -CONFIG_CPU_FREQ_GOV_USERSPACE=m -CONFIG_CPU_FREQ_GOV_CONSERVATIVE=m +CONFIG_CPU_FREQ_DEFAULT_GOV_SCHEDUTIL=y +CONFIG_CPU_FREQ_GOV_POWERSAVE=y +CONFIG_CPU_FREQ_GOV_USERSPACE=y +CONFIG_CPU_FREQ_GOV_CONSERVATIVE=y CONFIG_CPU_FREQ_GOV_SCHEDUTIL=y CONFIG_CPUFREQ_DT=y CONFIG_ARM_IMX6Q_CPUFREQ=y @@ -137,6 +144,7 @@ CONFIG_ARM_CPUIDLE=y CONFIG_ARM_ZYNQ_CPUIDLE=y CONFIG_ARM_EXYNOS_CPUIDLE=y CONFIG_KERNEL_MODE_NEON=y +CONFIG_ENERGY_MODEL=y CONFIG_NET=y CONFIG_PACKET=y CONFIG_UNIX=y diff --git a/arch/arm64/configs/defconfig b/arch/arm64/configs/defconfig index db8d364f8476..31fd24bff688 100644 --- a/arch/arm64/configs/defconfig +++ b/arch/arm64/configs/defconfig @@ -19,9 +19,13 @@ CONFIG_BLK_CGROUP=y CONFIG_CGROUP_PIDS=y CONFIG_CGROUP_HUGETLB=y CONFIG_CPUSETS=y +CONFIG_CGROUPS=y +CONFIG_FAIR_GROUP_SCHED=y +CONFIG_CGROUP_SCHED=y CONFIG_CGROUP_DEVICE=y CONFIG_CGROUP_CPUACCT=y CONFIG_CGROUP_PERF=y +CONFIG_CGROUP_FREEZER=y CONFIG_USER_NS=y CONFIG_SCHED_AUTOGROUP=y CONFIG_BLK_DEV_INITRD=y @@ -101,13 +105,15 @@ CONFIG_XEN=y CONFIG_COMPAT=y CONFIG_HIBERNATION=y CONFIG_WQ_POWER_EFFICIENT_DEFAULT=y +CONFIG_ENERGY_MODEL=y CONFIG_ARM_CPUIDLE=y CONFIG_CPU_FREQ=y CONFIG_CPU_FREQ_STAT=y -CONFIG_CPU_FREQ_GOV_POWERSAVE=m +CONFIG_CPU_FREQ_DEFAULT_GOV_SCHEDUTIL=y +CONFIG_CPU_FREQ_GOV_POWERSAVE=y CONFIG_CPU_FREQ_GOV_USERSPACE=y CONFIG_CPU_FREQ_GOV_ONDEMAND=y -CONFIG_CPU_FREQ_GOV_CONSERVATIVE=m +CONFIG_CPU_FREQ_GOV_CONSERVATIVE=y CONFIG_CPU_FREQ_GOV_SCHEDUTIL=y CONFIG_CPUFREQ_DT=y CONFIG_ACPI_CPPC_CPUFREQ=m From 13442ed64fb5011a66a989b10cac70eddffc1f98 Mon Sep 17 00:00:00 2001 From: Dietmar Eggemann Date: Sun, 18 Feb 2018 14:27:49 +0100 Subject: [PATCH 0488/1103] ANDROID: arm64: dts: juno: Add dynamic-power-coefficient properties Taken from commit cadf54148974 "arm64: dts: Add IPA parameters to soc thermal zone" wich also sets up SoC thermal zones and bind them to cpufreq cooling devices. We don't want this functionality right now. The commit is for example part of: git.linaro.org/landing-teams/working/arm/kernel-release.git lt_arm/ack-4.9-armlt-18.01 Signed-off-by: Dietmar Eggemann Change-Id: I7c23a58fa49b281ed5df2f60db0514a9b3b50c7b --- arch/arm64/boot/dts/arm/juno.dts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/arch/arm64/boot/dts/arm/juno.dts b/arch/arm64/boot/dts/arm/juno.dts index 1fb5c5a0f32e..e3069e286256 100644 --- a/arch/arm64/boot/dts/arm/juno.dts +++ b/arch/arm64/boot/dts/arm/juno.dts @@ -98,6 +98,7 @@ clocks = <&scpi_dvfs 0>; cpu-idle-states = <&CPU_SLEEP_0 &CLUSTER_SLEEP_0>; capacity-dmips-mhz = <1024>; + dynamic-power-coefficient = <530>; }; A57_1: cpu@1 { @@ -115,6 +116,7 @@ clocks = <&scpi_dvfs 0>; cpu-idle-states = <&CPU_SLEEP_0 &CLUSTER_SLEEP_0>; capacity-dmips-mhz = <1024>; + dynamic-power-coefficient = <530>; }; A53_0: cpu@100 { @@ -132,6 +134,7 @@ clocks = <&scpi_dvfs 1>; cpu-idle-states = <&CPU_SLEEP_0 &CLUSTER_SLEEP_0>; capacity-dmips-mhz = <578>; + dynamic-power-coefficient = <140>; }; A53_1: cpu@101 { @@ -149,6 +152,7 @@ clocks = <&scpi_dvfs 1>; cpu-idle-states = <&CPU_SLEEP_0 &CLUSTER_SLEEP_0>; capacity-dmips-mhz = <578>; + dynamic-power-coefficient = <140>; }; A53_2: cpu@102 { @@ -166,6 +170,7 @@ clocks = <&scpi_dvfs 1>; cpu-idle-states = <&CPU_SLEEP_0 &CLUSTER_SLEEP_0>; capacity-dmips-mhz = <578>; + dynamic-power-coefficient = <140>; }; A53_3: cpu@103 { @@ -183,6 +188,7 @@ clocks = <&scpi_dvfs 1>; cpu-idle-states = <&CPU_SLEEP_0 &CLUSTER_SLEEP_0>; capacity-dmips-mhz = <578>; + dynamic-power-coefficient = <140>; }; A57_L2: l2-cache0 { From 0e7c76c346d8f201f2bb6561c96deded5a20c42f Mon Sep 17 00:00:00 2001 From: Dietmar Eggemann Date: Tue, 22 May 2018 17:01:09 +0100 Subject: [PATCH 0489/1103] ANDROID: arm64: dts: juno-r2: Add dynamic-power-coefficient properties Taken from commit cadf54148974 "arm64: dts: Add IPA parameters to soc thermal zone" wich also sets up SoC thermal zones and bind them to cpufreq cooling devices. We don't want this functionality right now. The commit is for example part of: git.linaro.org/landing-teams/working/arm/kernel-release.git lt_arm/ack-4.9-armlt-18.01 Signed-off-by: Dietmar Eggemann Change-Id: Id1a44fb7d222d59f7d44b5f55797d407513eb7e7 --- arch/arm64/boot/dts/arm/juno-r2.dts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/arch/arm64/boot/dts/arm/juno-r2.dts b/arch/arm64/boot/dts/arm/juno-r2.dts index ab77adb4f3c2..66f0ec79c864 100644 --- a/arch/arm64/boot/dts/arm/juno-r2.dts +++ b/arch/arm64/boot/dts/arm/juno-r2.dts @@ -99,6 +99,7 @@ clocks = <&scpi_dvfs 0>; cpu-idle-states = <&CPU_SLEEP_0 &CLUSTER_SLEEP_0>; capacity-dmips-mhz = <1024>; + dynamic-power-coefficient = <450>; }; A72_1: cpu@1 { @@ -116,6 +117,7 @@ clocks = <&scpi_dvfs 0>; cpu-idle-states = <&CPU_SLEEP_0 &CLUSTER_SLEEP_0>; capacity-dmips-mhz = <1024>; + dynamic-power-coefficient = <450>; }; A53_0: cpu@100 { @@ -133,6 +135,7 @@ clocks = <&scpi_dvfs 1>; cpu-idle-states = <&CPU_SLEEP_0 &CLUSTER_SLEEP_0>; capacity-dmips-mhz = <485>; + dynamic-power-coefficient = <140>; }; A53_1: cpu@101 { @@ -150,6 +153,7 @@ clocks = <&scpi_dvfs 1>; cpu-idle-states = <&CPU_SLEEP_0 &CLUSTER_SLEEP_0>; capacity-dmips-mhz = <485>; + dynamic-power-coefficient = <140>; }; A53_2: cpu@102 { @@ -167,6 +171,7 @@ clocks = <&scpi_dvfs 1>; cpu-idle-states = <&CPU_SLEEP_0 &CLUSTER_SLEEP_0>; capacity-dmips-mhz = <485>; + dynamic-power-coefficient = <140>; }; A53_3: cpu@103 { @@ -184,6 +189,7 @@ clocks = <&scpi_dvfs 1>; cpu-idle-states = <&CPU_SLEEP_0 &CLUSTER_SLEEP_0>; capacity-dmips-mhz = <485>; + dynamic-power-coefficient = <140>; }; A72_L2: l2-cache0 { From f4ade9961c7a757a5f590f03a8cf96458cb40334 Mon Sep 17 00:00:00 2001 From: Dietmar Eggemann Date: Tue, 5 Jun 2018 10:35:18 +0100 Subject: [PATCH 0490/1103] ANDROID: arm: dts: vexpress-v2p-ca15_a7: Add dynamic-power-coefficient properties The values are computed from measuring energy over a 10 secs sysbench workload running at each frequency and affine to 1 or 2 A15's as well as 1 or 2 or 3 A7's. The Power values for individual cpus are calculated from the Energy values divided by workload runtime by taking the difference of the energy values between n+1 and n cpus. P [mW] = C * freq [Mhz] * power [mV] * power [mV] / 1000000000 C = P [mW] / freq [Mhz] * power [mV] * power [mV] * 1000000000 The actual C (dynamic-power-coefficient) value is the mean value out of all the C values of the OPP's. A15: freq power voltage dyn_pwr_coef [MhZ] [mW] [mV] 0 500.0 534.57550 900 1319.939506 1 600.0 547.15468 900 1125.832675 2 700.0 572.22060 900 1009.207407 3 800.0 607.76592 900 937.910370 4 900.0 648.50552 900 889.582332 5 1000.0 693.86776 900 856.626864 6 1100.0 916.51314 975 876.469442 7 1200.0 1198.57566 1050 905.952880 mean: 990 A7: freq power voltage dyn_pwr_coef [MhZ] [mW] [mV] 0 350.0 40.17430 900 141.708289 1 400.0 42.68700 900 131.750000 2 500.0 54.25716 900 133.968296 3 600.0 64.09914 900 131.891235 4 700.0 74.09736 900 130.683175 5 800.0 82.69694 900 127.618735 6 900.0 113.71386 975 132.911225 7 1000.0 144.94124 1050 131.465977 mean: 133 The ratio between A15 and A7 is 990/113 = 7.44 This value (7.44) is very close to mean ratio between the power value of A15 an A7 of the per sched-domain Energy Model (7.96): mV Mhz MhZ old EM ratio A7 A15 core power 900 350 500 6997/1024 6.83 900 400 600 5177/761 6.80 900 500 700 3846/549 7.01 900 600 800 3524/447 7.88 900 700 900 3125/407 7.68 900 800 1000 2756/334 8.25 975 900 1100 2312/275 8.40 1050 1000 1200 2021/187 10.80 mean: 7.96 Signed-off-by: Dietmar Eggemann Change-Id: I93ef375d05ff769481a07f2c74f061e307cb14d4 --- arch/arm/boot/dts/vexpress-v2p-ca15_a7.dts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/arch/arm/boot/dts/vexpress-v2p-ca15_a7.dts b/arch/arm/boot/dts/vexpress-v2p-ca15_a7.dts index ac6b90e9d806..75663ed4817f 100644 --- a/arch/arm/boot/dts/vexpress-v2p-ca15_a7.dts +++ b/arch/arm/boot/dts/vexpress-v2p-ca15_a7.dts @@ -42,6 +42,7 @@ cci-control-port = <&cci_control1>; cpu-idle-states = <&CLUSTER_SLEEP_BIG>; capacity-dmips-mhz = <1024>; + dynamic-power-coefficient = <990>; }; cpu1: cpu@1 { @@ -51,6 +52,7 @@ cci-control-port = <&cci_control1>; cpu-idle-states = <&CLUSTER_SLEEP_BIG>; capacity-dmips-mhz = <1024>; + dynamic-power-coefficient = <990>; }; cpu2: cpu@2 { @@ -60,6 +62,7 @@ cci-control-port = <&cci_control2>; cpu-idle-states = <&CLUSTER_SLEEP_LITTLE>; capacity-dmips-mhz = <516>; + dynamic-power-coefficient = <133>; }; cpu3: cpu@3 { @@ -69,6 +72,7 @@ cci-control-port = <&cci_control2>; cpu-idle-states = <&CLUSTER_SLEEP_LITTLE>; capacity-dmips-mhz = <516>; + dynamic-power-coefficient = <133>; }; cpu4: cpu@4 { @@ -78,6 +82,7 @@ cci-control-port = <&cci_control2>; cpu-idle-states = <&CLUSTER_SLEEP_LITTLE>; capacity-dmips-mhz = <516>; + dynamic-power-coefficient = <133>; }; idle-states { From d4b5f1e21939326dfc339f040ad3552514398793 Mon Sep 17 00:00:00 2001 From: Quentin Perret Date: Fri, 18 May 2018 11:07:59 +0100 Subject: [PATCH 0491/1103] FROMLIST: cpufreq: dt: Register an Energy Model The Energy Model framework provides an API to register the active power of CPUs. Call this API from the cpufreq-dt driver with an estimation of the power as P = C * V^2 * f with C, V, and f respectively the capacitance of the CPU and the voltage and frequency of the OPP. The CPU capacitance is read from the "dynamic-power-coefficient" DT binding (originally introduced for thermal/IPA), and the voltage and frequency values from PM_OPP. Change-Id: Id7b79ae3aafcc53574b850cb91a25240ebffbdd4 Cc: "Rafael J. Wysocki" Cc: Viresh Kumar (Removed "OPTIONAL" label from title. Applied from https://lore.kernel.org/lkml/20180912091309.7551-15-quentin.perret@arm.com/) Signed-off-by: Quentin Perret --- drivers/cpufreq/cpufreq-dt.c | 48 +++++++++++++++++++++++++++++++++++- 1 file changed, 47 insertions(+), 1 deletion(-) diff --git a/drivers/cpufreq/cpufreq-dt.c b/drivers/cpufreq/cpufreq-dt.c index 0a9ebf00be46..15ac9754afa2 100644 --- a/drivers/cpufreq/cpufreq-dt.c +++ b/drivers/cpufreq/cpufreq-dt.c @@ -16,6 +16,7 @@ #include #include #include +#include #include #include #include @@ -149,8 +150,50 @@ static int resources_available(void) return 0; } +static int __maybe_unused of_est_power(unsigned long *mW, unsigned long *KHz, + int cpu) +{ + unsigned long mV, Hz, MHz; + struct device *cpu_dev; + struct dev_pm_opp *opp; + struct device_node *np; + u32 cap; + u64 tmp; + + cpu_dev = get_cpu_device(cpu); + if (!cpu_dev) + return -ENODEV; + + np = of_node_get(cpu_dev->of_node); + if (!np) + return -EINVAL; + + if (of_property_read_u32(np, "dynamic-power-coefficient", &cap)) + return -EINVAL; + + Hz = *KHz * 1000; + opp = dev_pm_opp_find_freq_ceil(cpu_dev, &Hz); + if (IS_ERR(opp)) + return -EINVAL; + + mV = dev_pm_opp_get_voltage(opp) / 1000; + dev_pm_opp_put(opp); + if (!mV) + return -EINVAL; + + MHz = Hz / 1000000; + tmp = (u64)cap * mV * mV * MHz; + do_div(tmp, 1000000000); + + *mW = (unsigned long)tmp; + *KHz = Hz / 1000; + + return 0; +} + static int cpufreq_init(struct cpufreq_policy *policy) { + struct em_data_callback em_cb = EM_DATA_CB(of_est_power); struct cpufreq_frequency_table *freq_table; struct opp_table *opp_table = NULL; struct private_data *priv; @@ -159,7 +202,7 @@ static int cpufreq_init(struct cpufreq_policy *policy) unsigned int transition_latency; bool fallback = false; const char *name; - int ret; + int ret, nr_opp; cpu_dev = get_cpu_device(policy->cpu); if (!cpu_dev) { @@ -226,6 +269,7 @@ static int cpufreq_init(struct cpufreq_policy *policy) ret = -EPROBE_DEFER; goto out_free_opp; } + nr_opp = ret; if (fallback) { cpumask_setall(policy->cpus); @@ -278,6 +322,8 @@ static int cpufreq_init(struct cpufreq_policy *policy) policy->cpuinfo.transition_latency = transition_latency; policy->dvfs_possible_from_any_cpu = true; + em_register_perf_domain(policy->cpus, nr_opp, &em_cb); + return 0; out_free_cpufreq_table: From b13a391484842b059f798b23c6d2478e5206f134 Mon Sep 17 00:00:00 2001 From: Quentin Perret Date: Wed, 12 Sep 2018 14:49:58 +0100 Subject: [PATCH 0492/1103] ANDROID: PM / OPP: cpufreq-dt: Move power estimation function The cpufreq-dt driver has a function estimating the power of CPUs using the dynamic-power-coefficient DT binding and the parameters of PM_OPP. Since this function can be useful to other drivers, relocate it to PM_OPP which is already a dependency anyway. Change-Id: I34f8f9cd9433c622c82f23f32ae9968a096a4390 Signed-off-by: Quentin Perret --- drivers/cpufreq/cpufreq-dt.c | 43 +----------------------------------- drivers/opp/of.c | 41 ++++++++++++++++++++++++++++++++++ include/linux/pm_opp.h | 5 +++++ 3 files changed, 47 insertions(+), 42 deletions(-) diff --git a/drivers/cpufreq/cpufreq-dt.c b/drivers/cpufreq/cpufreq-dt.c index 15ac9754afa2..83ad2a60991c 100644 --- a/drivers/cpufreq/cpufreq-dt.c +++ b/drivers/cpufreq/cpufreq-dt.c @@ -150,50 +150,9 @@ static int resources_available(void) return 0; } -static int __maybe_unused of_est_power(unsigned long *mW, unsigned long *KHz, - int cpu) -{ - unsigned long mV, Hz, MHz; - struct device *cpu_dev; - struct dev_pm_opp *opp; - struct device_node *np; - u32 cap; - u64 tmp; - - cpu_dev = get_cpu_device(cpu); - if (!cpu_dev) - return -ENODEV; - - np = of_node_get(cpu_dev->of_node); - if (!np) - return -EINVAL; - - if (of_property_read_u32(np, "dynamic-power-coefficient", &cap)) - return -EINVAL; - - Hz = *KHz * 1000; - opp = dev_pm_opp_find_freq_ceil(cpu_dev, &Hz); - if (IS_ERR(opp)) - return -EINVAL; - - mV = dev_pm_opp_get_voltage(opp) / 1000; - dev_pm_opp_put(opp); - if (!mV) - return -EINVAL; - - MHz = Hz / 1000000; - tmp = (u64)cap * mV * mV * MHz; - do_div(tmp, 1000000000); - - *mW = (unsigned long)tmp; - *KHz = Hz / 1000; - - return 0; -} - static int cpufreq_init(struct cpufreq_policy *policy) { - struct em_data_callback em_cb = EM_DATA_CB(of_est_power); + struct em_data_callback em_cb = EM_DATA_CB(of_dev_pm_opp_get_cpu_power); struct cpufreq_frequency_table *freq_table; struct opp_table *opp_table = NULL; struct private_data *priv; diff --git a/drivers/opp/of.c b/drivers/opp/of.c index 7af0ddec936b..34157eac789d 100644 --- a/drivers/opp/of.c +++ b/drivers/opp/of.c @@ -777,3 +777,44 @@ struct device_node *dev_pm_opp_get_of_node(struct dev_pm_opp *opp) return of_node_get(opp->np); } EXPORT_SYMBOL_GPL(dev_pm_opp_get_of_node); + +int of_dev_pm_opp_get_cpu_power(unsigned long *mW, unsigned long *KHz, int cpu) +{ + unsigned long mV, Hz, MHz; + struct device *cpu_dev; + struct dev_pm_opp *opp; + struct device_node *np; + u32 cap; + u64 tmp; + + cpu_dev = get_cpu_device(cpu); + if (!cpu_dev) + return -ENODEV; + + np = of_node_get(cpu_dev->of_node); + if (!np) + return -EINVAL; + + if (of_property_read_u32(np, "dynamic-power-coefficient", &cap)) + return -EINVAL; + + Hz = *KHz * 1000; + opp = dev_pm_opp_find_freq_ceil(cpu_dev, &Hz); + if (IS_ERR(opp)) + return -EINVAL; + + mV = dev_pm_opp_get_voltage(opp) / 1000; + dev_pm_opp_put(opp); + if (!mV) + return -EINVAL; + + MHz = Hz / 1000000; + tmp = (u64)cap * mV * mV * MHz; + do_div(tmp, 1000000000); + + *mW = (unsigned long)tmp; + *KHz = Hz / 1000; + + return 0; +} +EXPORT_SYMBOL_GPL(of_dev_pm_opp_get_cpu_power); diff --git a/include/linux/pm_opp.h b/include/linux/pm_opp.h index 099b31960dec..11dbffc7f889 100644 --- a/include/linux/pm_opp.h +++ b/include/linux/pm_opp.h @@ -301,6 +301,7 @@ int dev_pm_opp_of_get_sharing_cpus(struct device *cpu_dev, struct cpumask *cpuma struct device_node *dev_pm_opp_of_get_opp_desc_node(struct device *dev); struct dev_pm_opp *of_dev_pm_opp_find_required_opp(struct device *dev, struct device_node *np); struct device_node *dev_pm_opp_get_of_node(struct dev_pm_opp *opp); +int of_dev_pm_opp_get_cpu_power(unsigned long *mW, unsigned long *KHz, int cpu); #else static inline int dev_pm_opp_of_add_table(struct device *dev) { @@ -343,6 +344,10 @@ static inline struct device_node *dev_pm_opp_get_of_node(struct dev_pm_opp *opp) { return NULL; } +static inline int of_dev_pm_opp_get_cpu_power(unsigned long *mW, unsigned long *KHz, int cpu) +{ + return -ENOTSUPP; +} #endif #endif /* __LINUX_OPP_H__ */ From 49329c0a017d303eef311d0445882933176ecec6 Mon Sep 17 00:00:00 2001 From: Quentin Perret Date: Fri, 18 May 2018 11:08:19 +0100 Subject: [PATCH 0493/1103] ANDROID: cpufreq: scpi: Register an Energy Model The Energy Model framework provides an API to register the active power of CPUs. This commit calls this API from the scpi-cpufreq driver which uses the power estimation helper from PM_OPP. Change-Id: I113fa2edf8201c1272c9fb5a0c6c39622ae53f94 Signed-off-by: Quentin Perret --- drivers/cpufreq/scpi-cpufreq.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/drivers/cpufreq/scpi-cpufreq.c b/drivers/cpufreq/scpi-cpufreq.c index 87a98ec77773..05fc7448f5cb 100644 --- a/drivers/cpufreq/scpi-cpufreq.c +++ b/drivers/cpufreq/scpi-cpufreq.c @@ -23,6 +23,7 @@ #include #include #include +#include #include #include #include @@ -98,11 +99,12 @@ scpi_get_sharing_cpus(struct device *cpu_dev, struct cpumask *cpumask) static int scpi_cpufreq_init(struct cpufreq_policy *policy) { - int ret; + int ret, nr_opp; unsigned int latency; struct device *cpu_dev; struct scpi_data *priv; struct cpufreq_frequency_table *freq_table; + struct em_data_callback em_cb = EM_DATA_CB(of_dev_pm_opp_get_cpu_power); cpu_dev = get_cpu_device(policy->cpu); if (!cpu_dev) { @@ -135,6 +137,7 @@ static int scpi_cpufreq_init(struct cpufreq_policy *policy) ret = -EPROBE_DEFER; goto out_free_opp; } + nr_opp = ret; priv = kzalloc(sizeof(*priv), GFP_KERNEL); if (!priv) { @@ -170,6 +173,9 @@ static int scpi_cpufreq_init(struct cpufreq_policy *policy) policy->cpuinfo.transition_latency = latency; policy->fast_switch_possible = false; + + em_register_perf_domain(policy->cpus, nr_opp, &em_cb); + return 0; out_free_cpufreq_table: From 376082656653a1fa950e2dbf64357acd3abfa785 Mon Sep 17 00:00:00 2001 From: Dietmar Eggemann Date: Fri, 1 Jun 2018 14:27:24 +0100 Subject: [PATCH 0494/1103] ANDROID: cpufreq: arm_big_little: Register an Energy Model The Energy Model framework provides an API to register the active power of CPUs. This commit calls this API from the scpi-cpufreq driver which uses the power estimation helper from PM_OPP. Todo: Check if driver can handle -EPROBE_DEFER and if the call to dev_pm_opp_get_opp_count() id realy necessary. Change-Id: Ia808262ef6c9f2cc7819a83e8eb2f602454edfa3 Signed-off-by: Dietmar Eggemann Signed-off-by: Quentin Perret --- drivers/cpufreq/arm_big_little.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/drivers/cpufreq/arm_big_little.c b/drivers/cpufreq/arm_big_little.c index cf62a1f64dd7..803d41c629c3 100644 --- a/drivers/cpufreq/arm_big_little.c +++ b/drivers/cpufreq/arm_big_little.c @@ -24,6 +24,7 @@ #include #include #include +#include #include #include #include @@ -456,6 +457,7 @@ static int get_cluster_clk_and_freq_table(struct device *cpu_dev, /* Per-CPU initialization */ static int bL_cpufreq_init(struct cpufreq_policy *policy) { + struct em_data_callback em_cb = EM_DATA_CB(of_dev_pm_opp_get_cpu_power); u32 cur_cluster = cpu_to_cluster(policy->cpu); struct device *cpu_dev; int ret; @@ -487,6 +489,14 @@ static int bL_cpufreq_init(struct cpufreq_policy *policy) policy->cpuinfo.transition_latency = arm_bL_ops->get_transition_latency(cpu_dev); + ret = dev_pm_opp_get_opp_count(cpu_dev); + if (ret <= 0) { + dev_dbg(cpu_dev, "OPP table is not ready, deferring probe\n"); + return -EPROBE_DEFER; + } + + em_register_perf_domain(policy->cpus, ret, &em_cb); + if (is_bL_switching_enabled()) per_cpu(cpu_last_req_freq, policy->cpu) = clk_get_cpu_rate(policy->cpu); From e6551f137eda9491e9cb8c3b3fddd0b3f73f9bac Mon Sep 17 00:00:00 2001 From: Quentin Perret Date: Mon, 10 Sep 2018 17:28:10 +0100 Subject: [PATCH 0495/1103] UPSTREAM: firmware: arm_scmi: add a getter for power of performance states The SCMI protocol can be used to get power estimates from firmware corresponding to each performance state of a device. Although these power costs are already managed by the SCMI firmware driver, they are not exposed to any external subsystem yet. Fix this by adding a new get_power() interface to the exisiting perf_ops defined for the SCMI protocol. Change-Id: Iae7b1b60c1955f6590764f3de459a32320eba448 Signed-off-by: Quentin Perret Signed-off-by: Sudeep Holla (cherry picked from commit 1a63fe9a2b1f47af5b2b7436b41824b14999c17a in git://git.kernel.org/pub/scm/linux/kernel/git/sudeep.holla/linux.git) Signed-off-by: Quentin Perret --- drivers/firmware/arm_scmi/perf.c | 28 ++++++++++++++++++++++++++++ include/linux/scmi_protocol.h | 4 ++++ 2 files changed, 32 insertions(+) diff --git a/drivers/firmware/arm_scmi/perf.c b/drivers/firmware/arm_scmi/perf.c index 64342944d917..c8024a39171b 100644 --- a/drivers/firmware/arm_scmi/perf.c +++ b/drivers/firmware/arm_scmi/perf.c @@ -427,6 +427,33 @@ static int scmi_dvfs_freq_get(const struct scmi_handle *handle, u32 domain, return ret; } +static int scmi_dvfs_est_power_get(const struct scmi_handle *handle, u32 domain, + unsigned long *freq, unsigned long *power) +{ + struct scmi_perf_info *pi = handle->perf_priv; + struct perf_dom_info *dom; + unsigned long opp_freq; + int idx, ret = -EINVAL; + struct scmi_opp *opp; + + dom = pi->dom_info + domain; + if (!dom) + return -EIO; + + for (opp = dom->opp, idx = 0; idx < dom->opp_count; idx++, opp++) { + opp_freq = opp->perf * dom->mult_factor; + if (opp_freq < *freq) + continue; + + *freq = opp_freq; + *power = opp->power; + ret = 0; + break; + } + + return ret; +} + static struct scmi_perf_ops perf_ops = { .limits_set = scmi_perf_limits_set, .limits_get = scmi_perf_limits_get, @@ -437,6 +464,7 @@ static struct scmi_perf_ops perf_ops = { .device_opps_add = scmi_dvfs_device_opps_add, .freq_set = scmi_dvfs_freq_set, .freq_get = scmi_dvfs_freq_get, + .est_power_get = scmi_dvfs_est_power_get, }; static int scmi_perf_protocol_init(struct scmi_handle *handle) diff --git a/include/linux/scmi_protocol.h b/include/linux/scmi_protocol.h index f4c9fc0fc755..3105055c00a7 100644 --- a/include/linux/scmi_protocol.h +++ b/include/linux/scmi_protocol.h @@ -91,6 +91,8 @@ struct scmi_clk_ops { * to sustained performance level mapping * @freq_get: gets the frequency for a given device using sustained frequency * to sustained performance level mapping + * @est_power_get: gets the estimated power cost for a given performance domain + * at a given frequency */ struct scmi_perf_ops { int (*limits_set)(const struct scmi_handle *handle, u32 domain, @@ -110,6 +112,8 @@ struct scmi_perf_ops { unsigned long rate, bool poll); int (*freq_get)(const struct scmi_handle *handle, u32 domain, unsigned long *rate, bool poll); + int (*est_power_get)(const struct scmi_handle *handle, u32 domain, + unsigned long *rate, unsigned long *power); }; /** From 39d44c2fef50b180a8ebce2f827d95fac4fe8d97 Mon Sep 17 00:00:00 2001 From: Quentin Perret Date: Fri, 18 May 2018 11:10:02 +0100 Subject: [PATCH 0496/1103] ANDROID: cpufreq: scmi: Register an Energy Model The Energy Model framework provides an API to register the active power of CPUs. This commit calls this API from the scmi-cpufreq driver which uses the power costs provided by the firmware. Change-Id: I2e6036acbf004d41f921e1396983b07e022a5399 Signed-off-by: Quentin Perret --- drivers/cpufreq/scmi-cpufreq.c | 36 +++++++++++++++++++++++++++++++++- 1 file changed, 35 insertions(+), 1 deletion(-) diff --git a/drivers/cpufreq/scmi-cpufreq.c b/drivers/cpufreq/scmi-cpufreq.c index 50b1551ba894..80a7f8da7e74 100644 --- a/drivers/cpufreq/scmi-cpufreq.c +++ b/drivers/cpufreq/scmi-cpufreq.c @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -103,13 +104,42 @@ scmi_get_sharing_cpus(struct device *cpu_dev, struct cpumask *cpumask) return 0; } +static int __maybe_unused +scmi_get_cpu_power(unsigned long *power, unsigned long *KHz, int cpu) +{ + struct device *cpu_dev = get_cpu_device(cpu); + unsigned long Hz; + int ret, domain; + + if (!cpu_dev) { + pr_err("failed to get cpu%d device\n", cpu); + return -ENODEV; + } + + domain = handle->perf_ops->device_domain_id(cpu_dev); + if (domain < 0) + return domain; + + /* Get the power cost of the performance domain. */ + Hz = *KHz * 1000; + ret = handle->perf_ops->est_power_get(handle, domain, &Hz, power); + if (ret) + return ret; + + /* The EM framework specifies the frequency in KHz. */ + *KHz = Hz / 1000; + + return 0; +} + static int scmi_cpufreq_init(struct cpufreq_policy *policy) { - int ret; + int ret, nr_opp; unsigned int latency; struct device *cpu_dev; struct scmi_data *priv; struct cpufreq_frequency_table *freq_table; + struct em_data_callback em_cb = EM_DATA_CB(scmi_get_cpu_power); cpu_dev = get_cpu_device(policy->cpu); if (!cpu_dev) { @@ -142,6 +172,7 @@ static int scmi_cpufreq_init(struct cpufreq_policy *policy) ret = -EPROBE_DEFER; goto out_free_opp; } + nr_opp = ret; priv = kzalloc(sizeof(*priv), GFP_KERNEL); if (!priv) { @@ -171,6 +202,9 @@ static int scmi_cpufreq_init(struct cpufreq_policy *policy) policy->cpuinfo.transition_latency = latency; policy->fast_switch_possible = true; + + em_register_perf_domain(policy->cpus, nr_opp, &em_cb); + return 0; out_free_priv: From 66c4da236187e24893182d3562f91be210cc77b0 Mon Sep 17 00:00:00 2001 From: Quentin Perret Date: Thu, 12 Jul 2018 16:58:37 +0100 Subject: [PATCH 0497/1103] ANDROID: drivers: Introduce a legacy Energy Model loading driver The Energy Aware Scheduler (EAS) used to rely on statically defined Energy Models (EMs) in the device tree. Now that EAS uses the EM framework, the old-style EMs are not usable by default. To address this issue, introduce a driver able to read DT-based EMs and to load them in the EM framework, hence making them available to EAS. Since EAS now uses only the active costs of CPUs, the idle cost and cluster cost of the old EM are ignored. The driver can be compiled in using the CONFIG_LEGACY_ENERGY_MODEL_DT Kconfig option (off by default). The implementation of the driver is highly inspired by the EM loading code from android-4.14 and before (written by Robin Randhawa ), and the arch_topology driver (Juri Lelli ). Signed-off-by: Quentin Perret Change-Id: I4f525dfb45113ba63f01aaf8e1e809ae6b34dd52 --- drivers/Kconfig | 1 + drivers/Makefile | 2 + drivers/energy_model/Kconfig | 16 +++ drivers/energy_model/Makefile | 3 + drivers/energy_model/legacy_em_dt.c | 193 ++++++++++++++++++++++++++++ 5 files changed, 215 insertions(+) create mode 100644 drivers/energy_model/Kconfig create mode 100644 drivers/energy_model/Makefile create mode 100644 drivers/energy_model/legacy_em_dt.c diff --git a/drivers/Kconfig b/drivers/Kconfig index ab4d43923c4d..681051a96e67 100644 --- a/drivers/Kconfig +++ b/drivers/Kconfig @@ -219,4 +219,5 @@ source "drivers/siox/Kconfig" source "drivers/slimbus/Kconfig" +source "drivers/energy_model/Kconfig" endmenu diff --git a/drivers/Makefile b/drivers/Makefile index 578f469f72fb..1e847e29661c 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -157,6 +157,8 @@ obj-$(CONFIG_REMOTEPROC) += remoteproc/ obj-$(CONFIG_RPMSG) += rpmsg/ obj-$(CONFIG_SOUNDWIRE) += soundwire/ +obj-$(CONFIG_ENERGY_MODEL) += energy_model/ + # Virtualization drivers obj-$(CONFIG_VIRT_DRIVERS) += virt/ obj-$(CONFIG_HYPERV) += hv/ diff --git a/drivers/energy_model/Kconfig b/drivers/energy_model/Kconfig new file mode 100644 index 000000000000..3fbf968926d5 --- /dev/null +++ b/drivers/energy_model/Kconfig @@ -0,0 +1,16 @@ +config LEGACY_ENERGY_MODEL_DT + bool "Legacy DT-based Energy Model of CPUs" + default n + help + The Energy Aware Scheduler (EAS) used to rely on Energy Models + (EMs) statically defined in the Device Tree. More recent + versions of EAS now rely on the EM framework to get the power + costs of CPUs. + + This driver reads old-style static EMs in DT and feeds them in + the EM framework, hence enabling to use EAS on platforms with + old DT files. Since EAS now uses only the active costs of CPUs, + the cluster-related costs and idle-costs of the old EM are + ignored. + + If in doubt, say N. diff --git a/drivers/energy_model/Makefile b/drivers/energy_model/Makefile new file mode 100644 index 000000000000..7bc0a7e502ea --- /dev/null +++ b/drivers/energy_model/Makefile @@ -0,0 +1,3 @@ +# SPDX-License-Identifier: GPL-2.0 + +obj-$(CONFIG_LEGACY_ENERGY_MODEL_DT) += legacy_em_dt.o diff --git a/drivers/energy_model/legacy_em_dt.c b/drivers/energy_model/legacy_em_dt.c new file mode 100644 index 000000000000..b608790fcc19 --- /dev/null +++ b/drivers/energy_model/legacy_em_dt.c @@ -0,0 +1,193 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Legacy Energy Model loading driver + * + * Copyright (C) 2018, ARM Ltd. + * Written by: Quentin Perret, ARM Ltd. + */ + +#define pr_fmt(fmt) "legacy-dt-em: " fmt + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static cpumask_var_t cpus_to_visit; + +static DEFINE_PER_CPU(unsigned long, nr_states) = 0; + +struct em_state { + unsigned long frequency; + unsigned long power; + unsigned long capacity; +}; +static DEFINE_PER_CPU(struct em_state*, cpu_em) = NULL; + +static void finish_em_loading_workfn(struct work_struct *work); +static DECLARE_WORK(finish_em_loading_work, finish_em_loading_workfn); + +static DEFINE_MUTEX(em_loading_mutex); + +/* + * Callback given to the EM framework. All this does is browse the table + * created by legacy_em_dt(). + */ +static int get_power(unsigned long *mW, unsigned long *KHz, int cpu) +{ + unsigned long nstates = per_cpu(nr_states, cpu); + struct em_state *em = per_cpu(cpu_em, cpu); + int i; + + if (!nstates || !em) + return -ENODEV; + + for (i = 0; i < nstates - 1; i++) { + if (em[i].frequency > *KHz) + break; + } + + *KHz = em[i].frequency; + *mW = em[i].power; + + return 0; +} + +static int init_em_dt_callback(struct notifier_block *nb, unsigned long val, + void *data) +{ + struct em_data_callback em_cb = EM_DATA_CB(get_power); + unsigned long nstates, scale_cpu, max_freq; + struct cpufreq_policy *policy = data; + const struct property *prop; + struct device_node *cn, *cp; + struct em_state *em; + int cpu, i, ret = 0; + const __be32 *tmp; + + if (val != CPUFREQ_NOTIFY) + return 0; + + mutex_lock(&em_loading_mutex); + + /* Do not register twice an energy model */ + for_each_cpu(cpu, policy->cpus) { + if (per_cpu(nr_states, cpu) || per_cpu(cpu_em, cpu)) { + pr_err("EM of CPU%d already loaded\n", cpu); + ret = -EEXIST; + goto unlock; + } + } + + max_freq = policy->cpuinfo.max_freq; + if (!max_freq) { + pr_err("No policy->max for CPU%d\n", cpu); + ret = -EINVAL; + goto unlock; + } + + cpu = cpumask_first(policy->cpus); + cn = of_get_cpu_node(cpu, NULL); + if (!cn) { + pr_err("No device_node for CPU%d\n", cpu); + ret = -ENODEV; + goto unlock; + } + + cp = of_parse_phandle(cn, "sched-energy-costs", 0); + if (!cp) { + pr_err("CPU%d node has no sched-energy-costs\n", cpu); + ret = -ENODEV; + goto unlock; + } + + prop = of_find_property(cp, "busy-cost-data", NULL); + if (!prop || !prop->value) { + pr_err("No busy-cost-data for CPU%d\n", cpu); + ret = -ENODEV; + goto unlock; + } + + nstates = (prop->length / sizeof(u32)) / 2; + em = kcalloc(nstates, sizeof(struct em_cap_state), GFP_KERNEL); + if (!em) { + ret = -ENOMEM; + goto unlock; + } + + /* Copy the capacity and power cost to the table. */ + for (i = 0, tmp = prop->value; i < nstates; i++) { + em[i].capacity = be32_to_cpup(tmp++); + em[i].power = be32_to_cpup(tmp++); + } + + /* Get the CPU capacity (according to the EM) */ + scale_cpu = em[nstates - 1].capacity; + if (!scale_cpu) { + pr_err("CPU%d: capacity cannot be 0\n", cpu); + kfree(em); + ret = -EINVAL; + goto unlock; + } + + /* Re-compute the intermediate frequencies based on the EM. */ + for (i = 0; i < nstates; i++) + em[i].frequency = em[i].capacity * max_freq / scale_cpu; + + /* Assign the table to all CPUs of this policy. */ + for_each_cpu(i, policy->cpus) { + per_cpu(nr_states, i) = nstates; + per_cpu(cpu_em, i) = em; + } + + pr_info("Registering EM of %*pbl\n", cpumask_pr_args(policy->cpus)); + em_register_perf_domain(policy->cpus, nstates, &em_cb); + + /* Finish the work when all possible CPUs have been registered. */ + cpumask_andnot(cpus_to_visit, cpus_to_visit, policy->cpus); + if (cpumask_empty(cpus_to_visit)) + schedule_work(&finish_em_loading_work); + +unlock: + mutex_unlock(&em_loading_mutex); + + return ret; +} + +static struct notifier_block init_em_dt_notifier = { + .notifier_call = init_em_dt_callback, +}; + +static void finish_em_loading_workfn(struct work_struct *work) +{ + cpufreq_unregister_notifier(&init_em_dt_notifier, + CPUFREQ_POLICY_NOTIFIER); + free_cpumask_var(cpus_to_visit); + + /* Let the scheduler know the Energy Model is ready. */ + rebuild_sched_domains(); +} + +static int __init register_cpufreq_notifier(void) +{ + int ret; + + if (!alloc_cpumask_var(&cpus_to_visit, GFP_KERNEL)) + return -ENOMEM; + + cpumask_copy(cpus_to_visit, cpu_possible_mask); + + ret = cpufreq_register_notifier(&init_em_dt_notifier, + CPUFREQ_POLICY_NOTIFIER); + + if (ret) + free_cpumask_var(cpus_to_visit); + + return ret; +} +core_initcall(register_cpufreq_notifier); From 2e4d27d3515f76db3c5beeb8c9208df24ee523b3 Mon Sep 17 00:00:00 2001 From: Quentin Perret Date: Wed, 12 Sep 2018 16:25:33 +0100 Subject: [PATCH 0498/1103] ANDROID: sched: Turn ENERGY_AWARE on by default The ENERGY_AWARE sched_feat must be ON in order to use Energy Aware Scheduling (EAS). Turn it ON by default to make sure it can be used on mobile devices. EAS also requires an Energy Model to start, in addition to the sched_feat. Providing one is the job of platform specific code. Change-Id: I5341ce7f4cc1271199ef1f62decc651312b7ae04 Signed-off-by: Quentin Perret --- kernel/sched/features.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kernel/sched/features.h b/kernel/sched/features.h index 26ff6752e492..a49c0701a2f2 100644 --- a/kernel/sched/features.h +++ b/kernel/sched/features.h @@ -96,4 +96,4 @@ SCHED_FEAT(UTIL_EST, true) * energy-aware fashion depends on this feature being enabled and on the * root domain having an Energy Model attached. */ -SCHED_FEAT(ENERGY_AWARE, false) +SCHED_FEAT(ENERGY_AWARE, true) From 3bfdcc4ed8e87d5de99c6a253d09a2319d575135 Mon Sep 17 00:00:00 2001 From: Patrick Bellasi Date: Sat, 21 Oct 2017 18:07:35 +0100 Subject: [PATCH 0499/1103] ANDROID: sched: fair/tune: Add schedtune with cgroups interface Schedtune is the framework we use in Android to allow userspace task classification and provides a CGroup controller which has two attributes per group. * schedtune.boost * schedtune.prefer_idle Schedtune itself provides task and CPU utilization boosting. EAS in the fair scheduler uses boosted utilization and prefer_idle status to control the algorithm used for wakeup task placement. Boosting: The task utilization signal, which is derived from PELT signals and properly scaled to be architecture and frequency invariant, is used by EAS as an estimation of the task requirements in terms of CPU bandwidth. Schedtune allows userspace to assign a percentage boost to each group and this boost is used to calculate an additional utilization margin. The margin added to the original utilization is: 1. computed based on the "boosting strategy" in use 2. proportional to boost value defined by the "taskgroup" value The boosted signal is used by EAS for task placement, and boosted CPU utilization (if boosted tasks are running) is given when schedutil requests utilization. Prefer_idle: When this attribute is 1 for a group, this is used as a signal from userspace that tasks in this group need to be serviced with the minimum latency possible. Previous versions of schedtune had much more functionality around allowing a more tuneable tradeoff between performand and energy, however this has not been used a lot up until now. If necessary, we can easily resurrect it based upon old code. Signed-off-by: Patrick Bellasi Signed-off-by: Chris Redpath (cherry-picked from commit 159c14f0397790405b9e8435184366b31f2ed15b) [ - Removed tracepoints (to be added in a separate patch) - Integrated boosted_cpu_util() with cpu_util_cfs() - Backported Patrick's util_est related fixes from android-4.14 ] Signed-off-by: Quentin Perret Change-Id: Ie2fd63d82f604f34bcbc7e1ca9b5af1bdcc037e0 --- Documentation/scheduler/sched-tune.txt | 413 ++++++++++++++++ include/linux/cgroup_subsys.h | 4 + init/Kconfig | 23 + kernel/sched/Makefile | 1 + kernel/sched/cpufreq_schedutil.c | 2 +- kernel/sched/fair.c | 118 +++++ kernel/sched/sched.h | 2 + kernel/sched/tune.c | 622 +++++++++++++++++++++++++ kernel/sched/tune.h | 37 ++ 9 files changed, 1221 insertions(+), 1 deletion(-) create mode 100644 Documentation/scheduler/sched-tune.txt create mode 100644 kernel/sched/tune.c create mode 100644 kernel/sched/tune.h diff --git a/Documentation/scheduler/sched-tune.txt b/Documentation/scheduler/sched-tune.txt new file mode 100644 index 000000000000..5df0ea361311 --- /dev/null +++ b/Documentation/scheduler/sched-tune.txt @@ -0,0 +1,413 @@ + Central, scheduler-driven, power-performance control + (EXPERIMENTAL) + +Abstract +======== + +The topic of a single simple power-performance tunable, that is wholly +scheduler centric, and has well defined and predictable properties has come up +on several occasions in the past [1,2]. With techniques such as a scheduler +driven DVFS [3], we now have a good framework for implementing such a tunable. +This document describes the overall ideas behind its design and implementation. + + +Table of Contents +================= + +1. Motivation +2. Introduction +3. Signal Boosting Strategy +4. OPP selection using boosted CPU utilization +5. Per task group boosting +6. Per-task wakeup-placement-strategy Selection +7. Question and Answers + - What about "auto" mode? + - What about boosting on a congested system? + - How CPUs are boosted when we have tasks with multiple boost values? +8. References + + +1. Motivation +============= + +Sched-DVFS [3] was a new event-driven cpufreq governor which allows the +scheduler to select the optimal DVFS operating point (OPP) for running a task +allocated to a CPU. Later, the cpufreq maintainers introduced a similar +governor, schedutil. The introduction of schedutil also enables running +workloads at the most energy efficient OPPs. + +However, sometimes it may be desired to intentionally boost the performance of +a workload even if that could imply a reasonable increase in energy +consumption. For example, in order to reduce the response time of a task, we +may want to run the task at a higher OPP than the one that is actually required +by it's CPU bandwidth demand. + +This last requirement is especially important if we consider that one of the +main goals of the utilization-driven governor component is to replace all +currently available CPUFreq policies. Since sched-DVFS and schedutil are event +based, as opposed to the sampling driven governors we currently have, they are +already more responsive at selecting the optimal OPP to run tasks allocated to +a CPU. However, just tracking the actual task load demand may not be enough +from a performance standpoint. For example, it is not possible to get +behaviors similar to those provided by the "performance" and "interactive" +CPUFreq governors. + +This document describes an implementation of a tunable, stacked on top of the +utilization-driven governors which extends their functionality to support task +performance boosting. + +By "performance boosting" we mean the reduction of the time required to +complete a task activation, i.e. the time elapsed from a task wakeup to its +next deactivation (e.g. because it goes back to sleep or it terminates). For +example, if we consider a simple periodic task which executes the same workload +for 5[s] every 20[s] while running at a certain OPP, a boosted execution of +that task must complete each of its activations in less than 5[s]. + +A previous attempt [5] to introduce such a boosting feature has not been +successful mainly because of the complexity of the proposed solution. Previous +versions of the approach described in this document exposed a single simple +interface to user-space. This single tunable knob allowed the tuning of +system wide scheduler behaviours ranging from energy efficiency at one end +through to incremental performance boosting at the other end. This first +tunable affects all tasks. However, that is not useful for Android products +so in this version only a more advanced extension of the concept is provided +which uses CGroups to boost the performance of only selected tasks while using +the energy efficient default for all others. + +The rest of this document introduces in more details the proposed solution +which has been named SchedTune. + + +2. Introduction +=============== + +SchedTune exposes a simple user-space interface provided through a new +CGroup controller 'stune' which provides two power-performance tunables +per group: + + //schedtune.prefer_idle + //schedtune.boost + +The CGroup implementation permits arbitrary user-space defined task +classification to tune the scheduler for different goals depending on the +specific nature of the task, e.g. background vs interactive vs low-priority. + +More details are given in section 5. + +2.1 Boosting +============ + +The boost value is expressed as an integer in the range [-100..0..100]. + +A value of 0 (default) configures the CFS scheduler for maximum energy +efficiency. This means that sched-DVFS runs the tasks at the minimum OPP +required to satisfy their workload demand. + +A value of 100 configures scheduler for maximum performance, which translates +to the selection of the maximum OPP on that CPU. + +A value of -100 configures scheduler for minimum performance, which translates +to the selection of the minimum OPP on that CPU. + +The range between -100, 0 and 100 can be set to satisfy other scenarios suitably. +For example to satisfy interactive response or depending on other system events +(battery level etc). + +The overall design of the SchedTune module is built on top of "Per-Entity Load +Tracking" (PELT) signals and sched-DVFS by introducing a bias on the Operating +Performance Point (OPP) selection. + +Each time a task is allocated on a CPU, cpufreq is given the opportunity to tune +the operating frequency of that CPU to better match the workload demand. The +selection of the actual OPP being activated is influenced by the boost value +for the task CGroup. + +This simple biasing approach leverages existing frameworks, which means minimal +modifications to the scheduler, and yet it allows to achieve a range of +different behaviours all from a single simple tunable knob. + +In EAS schedulers, we use boosted task and CPU utilization for energy +calculation and energy-aware task placement. + +2.2 prefer_idle +=============== + +This is a flag which indicates to the scheduler that userspace would like +the scheduler to focus on energy or to focus on performance. + +A value of 0 (default) signals to the CFS scheduler that tasks in this group +can be placed according to the energy-aware wakeup strategy. + +A value of 1 signals to the CFS scheduler that tasks in this group should be +placed to minimise wakeup latency. + +The value is combined with the boost value - task placement will not be +boost aware however CPU OPP selection is still boost aware. + +Android platforms typically use this flag for application tasks which the +user is currently interacting with. + + +3. Signal Boosting Strategy +=========================== + +The whole PELT machinery works based on the value of a few load tracking signals +which basically track the CPU bandwidth requirements for tasks and the capacity +of CPUs. The basic idea behind the SchedTune knob is to artificially inflate +some of these load tracking signals to make a task or RQ appears more demanding +that it actually is. + +Which signals have to be inflated depends on the specific "consumer". However, +independently from the specific (signal, consumer) pair, it is important to +define a simple and possibly consistent strategy for the concept of boosting a +signal. + +A boosting strategy defines how the "abstract" user-space defined +sched_cfs_boost value is translated into an internal "margin" value to be added +to a signal to get its inflated value: + + margin := boosting_strategy(sched_cfs_boost, signal) + boosted_signal := signal + margin + +Different boosting strategies were identified and analyzed before selecting the +one found to be most effective. + +Signal Proportional Compensation (SPC) +-------------------------------------- + +In this boosting strategy the sched_cfs_boost value is used to compute a +margin which is proportional to the complement of the original signal. +When a signal has a maximum possible value, its complement is defined as +the delta from the actual value and its possible maximum. + +Since the tunable implementation uses signals which have SCHED_LOAD_SCALE as +the maximum possible value, the margin becomes: + + margin := sched_cfs_boost * (SCHED_LOAD_SCALE - signal) + +Using this boosting strategy: +- a 100% sched_cfs_boost means that the signal is scaled to the maximum value +- each value in the range of sched_cfs_boost effectively inflates the signal in + question by a quantity which is proportional to the maximum value. + +For example, by applying the SPC boosting strategy to the selection of the OPP +to run a task it is possible to achieve these behaviors: + +- 0% boosting: run the task at the minimum OPP required by its workload +- 100% boosting: run the task at the maximum OPP available for the CPU +- 50% boosting: run at the half-way OPP between minimum and maximum + +Which means that, at 50% boosting, a task will be scheduled to run at half of +the maximum theoretically achievable performance on the specific target +platform. + +A graphical representation of an SPC boosted signal is represented in the +following figure where: + a) "-" represents the original signal + b) "b" represents a 50% boosted signal + c) "p" represents a 100% boosted signal + + + ^ + | SCHED_LOAD_SCALE + +-----------------------------------------------------------------+ + |pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp + | + | boosted_signal + | bbbbbbbbbbbbbbbbbbbbbbbb + | + | original signal + | bbbbbbbbbbbbbbbbbbbbbbbb+----------------------+ + | | + |bbbbbbbbbbbbbbbbbb | + | | + | | + | | + | +-----------------------+ + | | + | | + | | + |------------------+ + | + | + +-----------------------------------------------------------------------> + +The plot above shows a ramped load signal (titled 'original_signal') and it's +boosted equivalent. For each step of the original signal the boosted signal +corresponding to a 50% boost is midway from the original signal and the upper +bound. Boosting by 100% generates a boosted signal which is always saturated to +the upper bound. + + +4. OPP selection using boosted CPU utilization +============================================== + +It is worth calling out that the implementation does not introduce any new load +signals. Instead, it provides an API to tune existing signals. This tuning is +done on demand and only in scheduler code paths where it is sensible to do so. +The new API calls are defined to return either the default signal or a boosted +one, depending on the value of sched_cfs_boost. This is a clean an non invasive +modification of the existing existing code paths. + +The signal representing a CPU's utilization is boosted according to the +previously described SPC boosting strategy. To sched-DVFS, this allows a CPU +(ie CFS run-queue) to appear more used then it actually is. + +Thus, with the sched_cfs_boost enabled we have the following main functions to +get the current utilization of a CPU: + + cpu_util() + boosted_cpu_util() + +The new boosted_cpu_util() is similar to the first but returns a boosted +utilization signal which is a function of the sched_cfs_boost value. + +This function is used in the CFS scheduler code paths where sched-DVFS needs to +decide the OPP to run a CPU at. +For example, this allows selecting the highest OPP for a CPU which has +the boost value set to 100%. + + +5. Per task group boosting +========================== + +On battery powered devices there usually are many background services which are +long running and need energy efficient scheduling. On the other hand, some +applications are more performance sensitive and require an interactive +response and/or maximum performance, regardless of the energy cost. + +To better service such scenarios, the SchedTune implementation has an extension +that provides a more fine grained boosting interface. + +A new CGroup controller, namely "schedtune", can be enabled which allows to +defined and configure task groups with different boosting values. +Tasks that require special performance can be put into separate CGroups. +The value of the boost associated with the tasks in this group can be specified +using a single knob exposed by the CGroup controller: + + schedtune.boost + +This knob allows the definition of a boost value that is to be used for +SPC boosting of all tasks attached to this group. + +The current schedtune controller implementation is really simple and has these +main characteristics: + + 1) It is only possible to create 1 level depth hierarchies + + The root control groups define the system-wide boost value to be applied + by default to all tasks. Its direct subgroups are named "boost groups" and + they define the boost value for specific set of tasks. + Further nested subgroups are not allowed since they do not have a sensible + meaning from a user-space standpoint. + + 2) It is possible to define only a limited number of "boost groups" + + This number is defined at compile time and by default configured to 16. + This is a design decision motivated by two main reasons: + a) In a real system we do not expect utilization scenarios with more then few + boost groups. For example, a reasonable collection of groups could be + just "background", "interactive" and "performance". + b) It simplifies the implementation considerably, especially for the code + which has to compute the per CPU boosting once there are multiple + RUNNABLE tasks with different boost values. + +Such a simple design should allow servicing the main utilization scenarios identified +so far. It provides a simple interface which can be used to manage the +power-performance of all tasks or only selected tasks. +Moreover, this interface can be easily integrated by user-space run-times (e.g. +Android, ChromeOS) to implement a QoS solution for task boosting based on tasks +classification, which has been a long standing requirement. + +Setup and usage +--------------- + +0. Use a kernel with CONFIG_SCHED_TUNE support enabled + +1. Check that the "schedtune" CGroup controller is available: + + root@linaro-nano:~# cat /proc/cgroups + #subsys_name hierarchy num_cgroups enabled + cpuset 0 1 1 + cpu 0 1 1 + schedtune 0 1 1 + +2. Mount a tmpfs to create the CGroups mount point (Optional) + + root@linaro-nano:~# sudo mount -t tmpfs cgroups /sys/fs/cgroup + +3. Mount the "schedtune" controller + + root@linaro-nano:~# mkdir /sys/fs/cgroup/stune + root@linaro-nano:~# sudo mount -t cgroup -o schedtune stune /sys/fs/cgroup/stune + +4. Create task groups and configure their specific boost value (Optional) + + For example here we create a "performance" boost group configure to boost + all its tasks to 100% + + root@linaro-nano:~# mkdir /sys/fs/cgroup/stune/performance + root@linaro-nano:~# echo 100 > /sys/fs/cgroup/stune/performance/schedtune.boost + +5. Move tasks into the boost group + + For example, the following moves the tasks with PID $TASKPID (and all its + threads) into the "performance" boost group. + + root@linaro-nano:~# echo "TASKPID > /sys/fs/cgroup/stune/performance/cgroup.procs + +This simple configuration allows only the threads of the $TASKPID task to run, +when needed, at the highest OPP in the most capable CPU of the system. + + +6. Per-task wakeup-placement-strategy Selection +=============================================== + +Many devices have a number of CFS tasks in use which require an absolute +minimum wakeup latency, and many tasks for which wakeup latency is not +important. + +For touch-driven environments, removing additional wakeup latency can be +critical. + +When you use the Schedtume CGroup controller, you have access to a second +parameter which allows a group to be marked such that energy_aware task +placement is bypassed for tasks belonging to that group. + +prefer_idle=0 (default - use energy-aware task placement if available) +prefer_idle=1 (never use energy-aware task placement for these tasks) + +Since the regular wakeup task placement algorithm in CFS is biased for +performance, this has the effect of restoring minimum wakeup latency +for the desired tasks whilst still allowing energy-aware wakeup placement +to save energy for other tasks. + + +7. Question and Answers +======================= + +What about "auto" mode? +----------------------- + +The 'auto' mode as described in [5] can be implemented by interfacing SchedTune +with some suitable user-space element. This element could use the exposed +system-wide or cgroup based interface. + +How are multiple groups of tasks with different boost values managed? +--------------------------------------------------------------------- + +The current SchedTune implementation keeps track of the boosted RUNNABLE tasks +on a CPU. The CPU utilization seen by the scheduler-driven cpufreq governors +(and used to select an appropriate OPP) is boosted with a value which is the +maximum of the boost values of the currently RUNNABLE tasks in its RQ. + +This allows cpufreq to boost a CPU only while there are boosted tasks ready +to run and switch back to the energy efficient mode as soon as the last boosted +task is dequeued. + + +8. References +============= +[1] http://lwn.net/Articles/552889 +[2] http://lkml.org/lkml/2012/5/18/91 +[3] http://lkml.org/lkml/2015/6/26/620 diff --git a/include/linux/cgroup_subsys.h b/include/linux/cgroup_subsys.h index acb77dcff3b4..8996c092568b 100644 --- a/include/linux/cgroup_subsys.h +++ b/include/linux/cgroup_subsys.h @@ -21,6 +21,10 @@ SUBSYS(cpu) SUBSYS(cpuacct) #endif +#if IS_ENABLED(CONFIG_SCHED_TUNE) +SUBSYS(schedtune) +#endif + #if IS_ENABLED(CONFIG_BLK_CGROUP) SUBSYS(io) #endif diff --git a/init/Kconfig b/init/Kconfig index 01cad95346df..2a9efce3f75d 100644 --- a/init/Kconfig +++ b/init/Kconfig @@ -991,6 +991,29 @@ config SCHED_AUTOGROUP desktop applications. Task group autogeneration is currently based upon task session. +config SCHED_TUNE + bool "Boosting for CFS tasks (EXPERIMENTAL)" + depends on SMP + help + This option enables support for task classification using a new + cgroup controller, schedtune. Schedtune allows tasks to be given + a boost value and marked as latency-sensitive or not. This option + provides the "schedtune" controller. + + This new controller: + 1. allows only a two layers hierarchy, where the root defines the + system-wide boost value and its direct childrens define each one a + different "class of tasks" to be boosted with a different value + 2. supports up to 16 different task classes, each one which could be + configured with a different boost value + + Latency-sensitive tasks are not subject to energy-aware wakeup + task placement. The boost value assigned to tasks is used to + influence task placement and CPU frequency selection (if + utilization-driven frequency selection is in use). + + If unsure, say N. + config SYSFS_DEPRECATED bool "Enable deprecated sysfs features to support old userspace tools" depends on SYSFS diff --git a/kernel/sched/Makefile b/kernel/sched/Makefile index 7fe183404c38..2389350a8268 100644 --- a/kernel/sched/Makefile +++ b/kernel/sched/Makefile @@ -24,6 +24,7 @@ obj-$(CONFIG_SMP) += cpupri.o cpudeadline.o topology.o stop_task.o pelt.o obj-$(CONFIG_SCHED_AUTOGROUP) += autogroup.o obj-$(CONFIG_SCHEDSTATS) += stats.o obj-$(CONFIG_SCHED_DEBUG) += debug.o +obj-$(CONFIG_SCHED_TUNE) += tune.o obj-$(CONFIG_CGROUP_CPUACCT) += cpuacct.o obj-$(CONFIG_CPU_FREQ) += cpufreq.o obj-$(CONFIG_CPU_FREQ_GOV_SCHEDUTIL) += cpufreq_schedutil.o diff --git a/kernel/sched/cpufreq_schedutil.c b/kernel/sched/cpufreq_schedutil.c index d2236d1166f4..645f54a0c2dc 100644 --- a/kernel/sched/cpufreq_schedutil.c +++ b/kernel/sched/cpufreq_schedutil.c @@ -286,7 +286,7 @@ unsigned long schedutil_freq_util(int cpu, unsigned long util_cfs, static unsigned long sugov_get_util(struct sugov_cpu *sg_cpu) { struct rq *rq = cpu_rq(sg_cpu->cpu); - unsigned long util = cpu_util_cfs(rq); + unsigned long util = boosted_cpu_util(sg_cpu->cpu); sg_cpu->max = arch_scale_cpu_capacity(NULL, sg_cpu->cpu); sg_cpu->bw_dl = cpu_bw_dl(rq); diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c index 172b17a2b2e9..8a0cd1e9da15 100644 --- a/kernel/sched/fair.c +++ b/kernel/sched/fair.c @@ -5116,6 +5116,24 @@ enqueue_task_fair(struct rq *rq, struct task_struct *p, int flags) */ util_est_enqueue(&rq->cfs, p); + /* + * The code below (indirectly) updates schedutil which looks at + * the cfs_rq utilization to select a frequency. + * Let's update schedtune here to ensure the boost value of the + * current task is accounted for in the selection of the OPP. + * + * We do it also in the case where we enqueue a throttled task; + * we could argue that a throttled task should not boost a CPU, + * however: + * a) properly implementing CPU boosting considering throttled + * tasks will increase a lot the complexity of the solution + * b) it's not easy to quantify the benefits introduced by + * such a more complex solution. + * Thus, for the time being we go for the simple solution and boost + * also for throttled RQs. + */ + schedtune_enqueue_task(p, cpu_of(rq)); + /* * If in_iowait is set, the code below may not trigger any cpufreq * utilization updates, so do it here explicitly with the IOWAIT flag @@ -5191,6 +5209,14 @@ static void dequeue_task_fair(struct rq *rq, struct task_struct *p, int flags) struct sched_entity *se = &p->se; int task_sleep = flags & DEQUEUE_SLEEP; + /* + * The code below (indirectly) updates schedutil which looks at + * the cfs_rq utilization to select a frequency. + * Let's update schedtune here to ensure the boost value of the + * current task is not more accounted for in the selection of the OPP. + */ + schedtune_dequeue_task(p, cpu_of(rq)); + for_each_sched_entity(se) { cfs_rq = cfs_rq_of(se); dequeue_entity(cfs_rq, se, flags); @@ -5717,6 +5743,98 @@ static int wake_affine(struct sched_domain *sd, struct task_struct *p, return target; } +#ifdef CONFIG_SCHED_TUNE +struct reciprocal_value schedtune_spc_rdiv; + +static long +schedtune_margin(unsigned long signal, long boost) +{ + long long margin = 0; + + /* + * Signal proportional compensation (SPC) + * + * The Boost (B) value is used to compute a Margin (M) which is + * proportional to the complement of the original Signal (S): + * M = B * (SCHED_CAPACITY_SCALE - S) + * The obtained M could be used by the caller to "boost" S. + */ + if (boost >= 0) { + margin = SCHED_CAPACITY_SCALE - signal; + margin *= boost; + } else + margin = -signal * boost; + + margin = reciprocal_divide(margin, schedtune_spc_rdiv); + + if (boost < 0) + margin *= -1; + return margin; +} + +static inline int +schedtune_cpu_margin(unsigned long util, int cpu) +{ + int boost = schedtune_cpu_boost(cpu); + + if (boost == 0) + return 0; + + return schedtune_margin(util, boost); +} + +static inline long +schedtune_task_margin(struct task_struct *task) +{ + int boost = schedtune_task_boost(task); + unsigned long util; + long margin; + + if (boost == 0) + return 0; + + util = task_util_est(task); + margin = schedtune_margin(util, boost); + + return margin; +} + +unsigned long +boosted_cpu_util(int cpu) +{ + unsigned long util = cpu_util_cfs(cpu_rq(cpu)); + long margin = schedtune_cpu_margin(util, cpu); + + return util + margin; +} + +#else /* CONFIG_SCHED_TUNE */ + +static inline int +schedtune_cpu_margin(unsigned long util, int cpu) +{ + return 0; +} + +static inline int +schedtune_task_margin(struct task_struct *task) +{ + return 0; +} + +#endif /* CONFIG_SCHED_TUNE */ + + + +static inline unsigned long +boosted_task_util(struct task_struct *task) +{ + unsigned long util = task_util_est(task); + long margin = schedtune_task_margin(task); + + return util + margin; +} + static unsigned long cpu_util_wake(int cpu, struct task_struct *p); static unsigned long capacity_spare_wake(int cpu, struct task_struct *p) diff --git a/kernel/sched/sched.h b/kernel/sched/sched.h index dadb63db5a3d..c6898fa67cba 100644 --- a/kernel/sched/sched.h +++ b/kernel/sched/sched.h @@ -80,6 +80,8 @@ # define SCHED_WARN_ON(x) ({ (void)(x), 0; }) #endif +#include "tune.h" + struct rq; struct cpuidle_state; diff --git a/kernel/sched/tune.c b/kernel/sched/tune.c new file mode 100644 index 000000000000..af6de6d3df6d --- /dev/null +++ b/kernel/sched/tune.c @@ -0,0 +1,622 @@ +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "sched.h" + +bool schedtune_initialized = false; +extern struct reciprocal_value schedtune_spc_rdiv; + +/* + * EAS scheduler tunables for task groups. + * + * When CGroup support is enabled, we have to synchronize two different + * paths: + * - slow path: where CGroups are created/updated/removed + * - fast path: where tasks in a CGroups are accounted + * + * The slow path tracks (a limited number of) CGroups and maps each on a + * "boost_group" index. The fastpath accounts tasks currently RUNNABLE on each + * "boost_group". + * + * Once a new CGroup is created, a boost group idx is assigned and the + * corresponding "boost_group" marked as valid on each CPU. + * Once a CGroup is release, the corresponding "boost_group" is marked as + * invalid on each CPU. The CPU boost value (boost_max) is aggregated by + * considering only valid boost_groups with a non null tasks counter. + * + * .:: Locking strategy + * + * The fast path uses a spin lock for each CPU boost_group which protects the + * tasks counter. + * + * The "valid" and "boost" values of each CPU boost_group is instead + * protected by the RCU lock provided by the CGroups callbacks. Thus, only the + * slow path can access and modify the boost_group attribtues of each CPU. + * The fast path will catch up the most updated values at the next scheduling + * event (i.e. enqueue/dequeue). + * + * | + * SLOW PATH | FAST PATH + * CGroup add/update/remove | Scheduler enqueue/dequeue events + * | + * | + * | DEFINE_PER_CPU(struct boost_groups) + * | +--------------+----+---+----+----+ + * | | idle | | | | | + * | | boost_max | | | | | + * | +---->lock | | | | | + * struct schedtune allocated_groups | | | group[ ] | | | | | + * +------------------------------+ +-------+ | | +--+---------+-+----+---+----+----+ + * | idx | | | | | | valid | + * | boots / prefer_idle | | | | | | boost | + * | perf_{boost/constraints}_idx | <---------+(*) | | | | tasks | <------------+ + * | css | +-------+ | | +---------+ | + * +-+----------------------------+ | | | | | | | + * ^ | | | | | | | + * | +-------+ | | +---------+ | + * | | | | | | | | + * | | | | | | | | + * | +-------+ | | +---------+ | + * | zmalloc | | | | | | | + * | | | | | | | | + * | +-------+ | | +---------+ | + * + BOOSTGROUPS_COUNT | | BOOSTGROUPS_COUNT | + * schedtune_boostgroup_init() | + | + * | schedtune_{en,de}queue_task() | + * | + + * | schedtune_tasks_update() + * | + */ + +/* SchdTune tunables for a group of tasks */ +struct schedtune { + /* SchedTune CGroup subsystem */ + struct cgroup_subsys_state css; + + /* Boost group allocated ID */ + int idx; + + /* Boost value for tasks on that SchedTune CGroup */ + int boost; + + /* Hint to bias scheduling of tasks on that SchedTune CGroup + * towards idle CPUs */ + int prefer_idle; +}; + +static inline struct schedtune *css_st(struct cgroup_subsys_state *css) +{ + return css ? container_of(css, struct schedtune, css) : NULL; +} + +static inline struct schedtune *task_schedtune(struct task_struct *tsk) +{ + return css_st(task_css(tsk, schedtune_cgrp_id)); +} + +static inline struct schedtune *parent_st(struct schedtune *st) +{ + return css_st(st->css.parent); +} + +/* + * SchedTune root control group + * The root control group is used to defined a system-wide boosting tuning, + * which is applied to all tasks in the system. + * Task specific boost tuning could be specified by creating and + * configuring a child control group under the root one. + * By default, system-wide boosting is disabled, i.e. no boosting is applied + * to tasks which are not into a child control group. + */ +static struct schedtune +root_schedtune = { + .boost = 0, + .prefer_idle = 0, +}; + +/* + * Maximum number of boost groups to support + * When per-task boosting is used we still allow only limited number of + * boost groups for two main reasons: + * 1. on a real system we usually have only few classes of workloads which + * make sense to boost with different values (e.g. background vs foreground + * tasks, interactive vs low-priority tasks) + * 2. a limited number allows for a simpler and more memory/time efficient + * implementation especially for the computation of the per-CPU boost + * value + */ +#define BOOSTGROUPS_COUNT 5 + +/* Array of configured boostgroups */ +static struct schedtune *allocated_group[BOOSTGROUPS_COUNT] = { + &root_schedtune, + NULL, +}; + +/* SchedTune boost groups + * Keep track of all the boost groups which impact on CPU, for example when a + * CPU has two RUNNABLE tasks belonging to two different boost groups and thus + * likely with different boost values. + * Since on each system we expect only a limited number of boost groups, here + * we use a simple array to keep track of the metrics required to compute the + * maximum per-CPU boosting value. + */ +struct boost_groups { + /* Maximum boost value for all RUNNABLE tasks on a CPU */ + int boost_max; + struct { + /* True when this boost group maps an actual cgroup */ + bool valid; + /* The boost for tasks on that boost group */ + int boost; + /* Count of RUNNABLE tasks on that boost group */ + unsigned tasks; + } group[BOOSTGROUPS_COUNT]; + /* CPU's boost group locking */ + raw_spinlock_t lock; +}; + +/* Boost groups affecting each CPU in the system */ +DEFINE_PER_CPU(struct boost_groups, cpu_boost_groups); + +static void +schedtune_cpu_update(int cpu) +{ + struct boost_groups *bg = &per_cpu(cpu_boost_groups, cpu); + int boost_max; + int idx; + + /* The root boost group is always active */ + boost_max = bg->group[0].boost; + for (idx = 1; idx < BOOSTGROUPS_COUNT; ++idx) { + + /* Ignore non boostgroups not mapping a cgroup */ + if (!bg->group[idx].valid) + continue; + + /* + * A boost group affects a CPU only if it has + * RUNNABLE tasks on that CPU + */ + if (bg->group[idx].tasks == 0) + continue; + + boost_max = max(boost_max, bg->group[idx].boost); + } + + /* Ensures boost_max is non-negative when all cgroup boost values + * are neagtive. Avoids under-accounting of cpu capacity which may cause + * task stacking and frequency spikes.*/ + boost_max = max(boost_max, 0); + bg->boost_max = boost_max; +} + +static int +schedtune_boostgroup_update(int idx, int boost) +{ + struct boost_groups *bg; + int cur_boost_max; + int old_boost; + int cpu; + + /* Update per CPU boost groups */ + for_each_possible_cpu(cpu) { + bg = &per_cpu(cpu_boost_groups, cpu); + + /* CGroups are never associated to non active cgroups */ + BUG_ON(!bg->group[idx].valid); + + /* + * Keep track of current boost values to compute the per CPU + * maximum only when it has been affected by the new value of + * the updated boost group + */ + cur_boost_max = bg->boost_max; + old_boost = bg->group[idx].boost; + + /* Update the boost value of this boost group */ + bg->group[idx].boost = boost; + + /* Check if this update increase current max */ + if (boost > cur_boost_max && bg->group[idx].tasks) { + bg->boost_max = boost; + continue; + } + + /* Check if this update has decreased current max */ + if (cur_boost_max == old_boost && old_boost > boost) { + schedtune_cpu_update(cpu); + continue; + } + } + + return 0; +} + +#define ENQUEUE_TASK 1 +#define DEQUEUE_TASK -1 + +static inline void +schedtune_tasks_update(struct task_struct *p, int cpu, int idx, int task_count) +{ + struct boost_groups *bg = &per_cpu(cpu_boost_groups, cpu); + int tasks = bg->group[idx].tasks + task_count; + + /* Update boosted tasks count while avoiding to make it negative */ + bg->group[idx].tasks = max(0, tasks); + + /* Boost group activation or deactivation on that RQ */ + if (tasks == 1 || tasks == 0) + schedtune_cpu_update(cpu); +} + +/* + * NOTE: This function must be called while holding the lock on the CPU RQ + */ +void schedtune_enqueue_task(struct task_struct *p, int cpu) +{ + struct boost_groups *bg = &per_cpu(cpu_boost_groups, cpu); + unsigned long irq_flags; + struct schedtune *st; + int idx; + + if (unlikely(!schedtune_initialized)) + return; + + /* + * Boost group accouting is protected by a per-cpu lock and requires + * interrupt to be disabled to avoid race conditions for example on + * do_exit()::cgroup_exit() and task migration. + */ + raw_spin_lock_irqsave(&bg->lock, irq_flags); + rcu_read_lock(); + + st = task_schedtune(p); + idx = st->idx; + + schedtune_tasks_update(p, cpu, idx, ENQUEUE_TASK); + + rcu_read_unlock(); + raw_spin_unlock_irqrestore(&bg->lock, irq_flags); +} + +int schedtune_can_attach(struct cgroup_taskset *tset) +{ + struct task_struct *task; + struct cgroup_subsys_state *css; + struct boost_groups *bg; + struct rq_flags rq_flags; + unsigned int cpu; + struct rq *rq; + int src_bg; /* Source boost group index */ + int dst_bg; /* Destination boost group index */ + int tasks; + + if (unlikely(!schedtune_initialized)) + return 0; + + + cgroup_taskset_for_each(task, css, tset) { + + /* + * Lock the CPU's RQ the task is enqueued to avoid race + * conditions with migration code while the task is being + * accounted + */ + rq = task_rq_lock(task, &rq_flags); + + if (!task->on_rq) { + task_rq_unlock(rq, task, &rq_flags); + continue; + } + + /* + * Boost group accouting is protected by a per-cpu lock and requires + * interrupt to be disabled to avoid race conditions on... + */ + cpu = cpu_of(rq); + bg = &per_cpu(cpu_boost_groups, cpu); + raw_spin_lock(&bg->lock); + + dst_bg = css_st(css)->idx; + src_bg = task_schedtune(task)->idx; + + /* + * Current task is not changing boostgroup, which can + * happen when the new hierarchy is in use. + */ + if (unlikely(dst_bg == src_bg)) { + raw_spin_unlock(&bg->lock); + task_rq_unlock(rq, task, &rq_flags); + continue; + } + + /* + * This is the case of a RUNNABLE task which is switching its + * current boost group. + */ + + /* Move task from src to dst boost group */ + tasks = bg->group[src_bg].tasks - 1; + bg->group[src_bg].tasks = max(0, tasks); + bg->group[dst_bg].tasks += 1; + + raw_spin_unlock(&bg->lock); + task_rq_unlock(rq, task, &rq_flags); + + /* Update CPU boost group */ + if (bg->group[src_bg].tasks == 0 || bg->group[dst_bg].tasks == 1) + schedtune_cpu_update(task_cpu(task)); + + } + + return 0; +} + +void schedtune_cancel_attach(struct cgroup_taskset *tset) +{ + /* This can happen only if SchedTune controller is mounted with + * other hierarchies ane one of them fails. Since usually SchedTune is + * mouted on its own hierarcy, for the time being we do not implement + * a proper rollback mechanism */ + WARN(1, "SchedTune cancel attach not implemented"); +} + +/* + * NOTE: This function must be called while holding the lock on the CPU RQ + */ +void schedtune_dequeue_task(struct task_struct *p, int cpu) +{ + struct boost_groups *bg = &per_cpu(cpu_boost_groups, cpu); + unsigned long irq_flags; + struct schedtune *st; + int idx; + + if (unlikely(!schedtune_initialized)) + return; + + /* + * Boost group accouting is protected by a per-cpu lock and requires + * interrupt to be disabled to avoid race conditions on... + */ + raw_spin_lock_irqsave(&bg->lock, irq_flags); + rcu_read_lock(); + + st = task_schedtune(p); + idx = st->idx; + + schedtune_tasks_update(p, cpu, idx, DEQUEUE_TASK); + + rcu_read_unlock(); + raw_spin_unlock_irqrestore(&bg->lock, irq_flags); +} + +int schedtune_cpu_boost(int cpu) +{ + struct boost_groups *bg; + + bg = &per_cpu(cpu_boost_groups, cpu); + return bg->boost_max; +} + +int schedtune_task_boost(struct task_struct *p) +{ + struct schedtune *st; + int task_boost; + + if (unlikely(!schedtune_initialized)) + return 0; + + /* Get task boost value */ + rcu_read_lock(); + st = task_schedtune(p); + task_boost = st->boost; + rcu_read_unlock(); + + return task_boost; +} + +int schedtune_prefer_idle(struct task_struct *p) +{ + struct schedtune *st; + int prefer_idle; + + if (unlikely(!schedtune_initialized)) + return 0; + + /* Get prefer_idle value */ + rcu_read_lock(); + st = task_schedtune(p); + prefer_idle = st->prefer_idle; + rcu_read_unlock(); + + return prefer_idle; +} + +static u64 +prefer_idle_read(struct cgroup_subsys_state *css, struct cftype *cft) +{ + struct schedtune *st = css_st(css); + + return st->prefer_idle; +} + +static int +prefer_idle_write(struct cgroup_subsys_state *css, struct cftype *cft, + u64 prefer_idle) +{ + struct schedtune *st = css_st(css); + st->prefer_idle = !!prefer_idle; + + return 0; +} + +static s64 +boost_read(struct cgroup_subsys_state *css, struct cftype *cft) +{ + struct schedtune *st = css_st(css); + + return st->boost; +} + +static int +boost_write(struct cgroup_subsys_state *css, struct cftype *cft, + s64 boost) +{ + struct schedtune *st = css_st(css); + + if (boost < 0 || boost > 100) + return -EINVAL; + + st->boost = boost; + + /* Update CPU boost */ + schedtune_boostgroup_update(st->idx, st->boost); + + return 0; +} + +static struct cftype files[] = { + { + .name = "boost", + .read_s64 = boost_read, + .write_s64 = boost_write, + }, + { + .name = "prefer_idle", + .read_u64 = prefer_idle_read, + .write_u64 = prefer_idle_write, + }, + { } /* terminate */ +}; + +static void +schedtune_boostgroup_init(struct schedtune *st, int idx) +{ + struct boost_groups *bg; + int cpu; + + /* Initialize per CPUs boost group support */ + for_each_possible_cpu(cpu) { + bg = &per_cpu(cpu_boost_groups, cpu); + bg->group[idx].boost = 0; + bg->group[idx].valid = true; + } + + /* Keep track of allocated boost groups */ + allocated_group[idx] = st; + st->idx = idx; +} + +static struct cgroup_subsys_state * +schedtune_css_alloc(struct cgroup_subsys_state *parent_css) +{ + struct schedtune *st; + int idx; + + if (!parent_css) + return &root_schedtune.css; + + /* Allow only single level hierachies */ + if (parent_css != &root_schedtune.css) { + pr_err("Nested SchedTune boosting groups not allowed\n"); + return ERR_PTR(-ENOMEM); + } + + /* Allow only a limited number of boosting groups */ + for (idx = 1; idx < BOOSTGROUPS_COUNT; ++idx) + if (!allocated_group[idx]) + break; + if (idx == BOOSTGROUPS_COUNT) { + pr_err("Trying to create more than %d SchedTune boosting groups\n", + BOOSTGROUPS_COUNT); + return ERR_PTR(-ENOSPC); + } + + st = kzalloc(sizeof(*st), GFP_KERNEL); + if (!st) + goto out; + + /* Initialize per CPUs boost group support */ + schedtune_boostgroup_init(st, idx); + + return &st->css; + +out: + return ERR_PTR(-ENOMEM); +} + +static void +schedtune_boostgroup_release(struct schedtune *st) +{ + struct boost_groups *bg; + int cpu; + + /* Reset per CPUs boost group support */ + for_each_possible_cpu(cpu) { + bg = &per_cpu(cpu_boost_groups, cpu); + bg->group[st->idx].valid = false; + bg->group[st->idx].boost = 0; + } + + /* Keep track of allocated boost groups */ + allocated_group[st->idx] = NULL; +} + +static void +schedtune_css_free(struct cgroup_subsys_state *css) +{ + struct schedtune *st = css_st(css); + + /* Release per CPUs boost group support */ + schedtune_boostgroup_release(st); + kfree(st); +} + +struct cgroup_subsys schedtune_cgrp_subsys = { + .css_alloc = schedtune_css_alloc, + .css_free = schedtune_css_free, + .can_attach = schedtune_can_attach, + .cancel_attach = schedtune_cancel_attach, + .legacy_cftypes = files, + .early_init = 1, +}; + +static inline void +schedtune_init_cgroups(void) +{ + struct boost_groups *bg; + int cpu; + + /* Initialize the per CPU boost groups */ + for_each_possible_cpu(cpu) { + bg = &per_cpu(cpu_boost_groups, cpu); + memset(bg, 0, sizeof(struct boost_groups)); + bg->group[0].valid = true; + raw_spin_lock_init(&bg->lock); + } + + pr_info("schedtune: configured to support %d boost groups\n", + BOOSTGROUPS_COUNT); + + schedtune_initialized = true; +} + +/* + * Initialize the cgroup structures + */ +static int +schedtune_init(void) +{ + schedtune_spc_rdiv = reciprocal_value(100); + schedtune_init_cgroups(); + return 0; +} +postcore_initcall(schedtune_init); diff --git a/kernel/sched/tune.h b/kernel/sched/tune.h new file mode 100644 index 000000000000..bb187c6112a7 --- /dev/null +++ b/kernel/sched/tune.h @@ -0,0 +1,37 @@ + +#ifdef CONFIG_SCHED_TUNE + +#include + +/* + * System energy normalization constants + */ +struct target_nrg { + unsigned long min_power; + unsigned long max_power; + struct reciprocal_value rdiv; +}; + +int schedtune_cpu_boost(int cpu); +int schedtune_task_boost(struct task_struct *tsk); + +int schedtune_prefer_idle(struct task_struct *tsk); + +void schedtune_enqueue_task(struct task_struct *p, int cpu); +void schedtune_dequeue_task(struct task_struct *p, int cpu); + +unsigned long boosted_cpu_util(int cpu); + +#else /* CONFIG_SCHED_TUNE */ + +#define schedtune_cpu_boost(cpu) 0 +#define schedtune_task_boost(tsk) 0 + +#define schedtune_prefer_idle(tsk) 0 + +#define schedtune_enqueue_task(task, cpu) do { } while (0) +#define schedtune_dequeue_task(task, cpu) do { } while (0) + +#define boosted_cpu_util(cpu) cpu_util_cfs(cpu_rq(cpu)) + +#endif /* CONFIG_SCHED_TUNE */ From d310a0ee8ae46e64dad93d722f534357491c732e Mon Sep 17 00:00:00 2001 From: Morten Rasmussen Date: Tue, 27 Jan 2015 13:48:07 +0000 Subject: [PATCH 0500/1103] ANDROID: sched, cpuidle: Track cpuidle state index in the scheduler The idle-state of each cpu is currently pointed to by rq->idle_state but there isn't any information in the struct cpuidle_state that can used to look up the idle-state energy model data stored in struct sched_group_energy. For this purpose is necessary to store the idle state index as well. Ideally, the idle-state data should be unified. cc: Ingo Molnar cc: Peter Zijlstra Signed-off-by: Morten Rasmussen Change-Id: Ib3d1178512735b0e314881f73fb8ccff5a69319f Signed-off-by: Chris Redpath (cherry picked from commit a732c97420e109956c20f34c70b91e6d06f5df31) [ Fixed trivial cherry-pick conflict in kernel/sched/sched.h ] Signed-off-by: Quentin Perret --- drivers/cpuidle/cpuidle.c | 4 ++-- include/linux/cpuidle.h | 2 +- kernel/sched/idle.c | 3 ++- kernel/sched/sched.h | 21 +++++++++++++++++++++ 4 files changed, 26 insertions(+), 4 deletions(-) diff --git a/drivers/cpuidle/cpuidle.c b/drivers/cpuidle/cpuidle.c index 6df894d65d9e..96a3a9bf8b12 100644 --- a/drivers/cpuidle/cpuidle.c +++ b/drivers/cpuidle/cpuidle.c @@ -221,7 +221,7 @@ int cpuidle_enter_state(struct cpuidle_device *dev, struct cpuidle_driver *drv, } /* Take note of the planned idle state. */ - sched_idle_set_state(target_state); + sched_idle_set_state(target_state, index); trace_cpu_idle_rcuidle(index, dev->cpu); time_start = ns_to_ktime(local_clock()); @@ -235,7 +235,7 @@ int cpuidle_enter_state(struct cpuidle_device *dev, struct cpuidle_driver *drv, trace_cpu_idle_rcuidle(PWR_EVENT_EXIT, dev->cpu); /* The cpu is no longer idle or about to enter idle. */ - sched_idle_set_state(NULL); + sched_idle_set_state(NULL, -1); if (broadcast) { if (WARN_ON_ONCE(!irqs_disabled())) diff --git a/include/linux/cpuidle.h b/include/linux/cpuidle.h index 4325d6fdde9b..35b067126e9a 100644 --- a/include/linux/cpuidle.h +++ b/include/linux/cpuidle.h @@ -219,7 +219,7 @@ static inline void cpuidle_use_deepest_state(bool enable) #endif /* kernel/sched/idle.c */ -extern void sched_idle_set_state(struct cpuidle_state *idle_state); +extern void sched_idle_set_state(struct cpuidle_state *idle_state, int index); extern void default_idle_call(void); #ifdef CONFIG_ARCH_NEEDS_CPU_IDLE_COUPLED diff --git a/kernel/sched/idle.c b/kernel/sched/idle.c index 16f84142f2f4..2d3e2804c08b 100644 --- a/kernel/sched/idle.c +++ b/kernel/sched/idle.c @@ -16,9 +16,10 @@ extern char __cpuidle_text_start[], __cpuidle_text_end[]; * sched_idle_set_state - Record idle state for the current CPU. * @idle_state: State to record. */ -void sched_idle_set_state(struct cpuidle_state *idle_state) +void sched_idle_set_state(struct cpuidle_state *idle_state, int index) { idle_set_state(this_rq(), idle_state); + idle_set_state_idx(this_rq(), index); } static int __read_mostly cpu_idle_force_poll; diff --git a/kernel/sched/sched.h b/kernel/sched/sched.h index c6898fa67cba..4a368968d046 100644 --- a/kernel/sched/sched.h +++ b/kernel/sched/sched.h @@ -942,6 +942,7 @@ struct rq { #ifdef CONFIG_CPU_IDLE /* Must be inspected within a rcu lock section */ struct cpuidle_state *idle_state; + int idle_state_idx; #endif }; @@ -1642,6 +1643,17 @@ static inline struct cpuidle_state *idle_get_state(struct rq *rq) return rq->idle_state; } + +static inline void idle_set_state_idx(struct rq *rq, int idle_state_idx) +{ + rq->idle_state_idx = idle_state_idx; +} + +static inline int idle_get_state_idx(struct rq *rq) +{ + WARN_ON(!rcu_read_lock_held()); + return rq->idle_state_idx; +} #else static inline void idle_set_state(struct rq *rq, struct cpuidle_state *idle_state) @@ -1652,6 +1664,15 @@ static inline struct cpuidle_state *idle_get_state(struct rq *rq) { return NULL; } + +static inline void idle_set_state_idx(struct rq *rq, int idle_state_idx) +{ +} + +static inline int idle_get_state_idx(struct rq *rq) +{ + return -1; +} #endif extern void schedule_idle(void); From dc9417af92610d5d378f847328b373db219e16f3 Mon Sep 17 00:00:00 2001 From: Quentin Perret Date: Mon, 23 Jul 2018 15:14:59 +0100 Subject: [PATCH 0501/1103] ANDROID: sched: Introduce sysctl_sched_cstate_aware Introduce a new sysctl for this option, 'sched_cstate_aware'. When this is enabled, the scheduler can make use of the idle state indexes in order to break the tie between potential CPU candidates. This patch is based on 7f6fb825d6bc ("ANDROID: sched: EAS: take cstate into account when selecting idle core") from android-4.14. All the credits goes to the authors. Change-Id: Ia076cf32faff91e90905291fa6f7924dc3dd6458 Signed-off-by: Quentin Perret --- include/linux/sched/sysctl.h | 1 + kernel/sched/fair.c | 5 +++++ kernel/sysctl.c | 7 +++++++ 3 files changed, 13 insertions(+) diff --git a/include/linux/sched/sysctl.h b/include/linux/sched/sysctl.h index a9c32daeb9d8..3f9ae132037e 100644 --- a/include/linux/sched/sysctl.h +++ b/include/linux/sched/sysctl.h @@ -22,6 +22,7 @@ enum { sysctl_hung_task_timeout_secs = 0 }; extern unsigned int sysctl_sched_latency; extern unsigned int sysctl_sched_min_granularity; +extern unsigned int sysctl_sched_cstate_aware; extern unsigned int sysctl_sched_wakeup_granularity; extern unsigned int sysctl_sched_child_runs_first; diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c index 8a0cd1e9da15..b2eeafcf0ee0 100644 --- a/kernel/sched/fair.c +++ b/kernel/sched/fair.c @@ -40,6 +40,11 @@ unsigned int sysctl_sched_latency = 6000000ULL; unsigned int normalized_sysctl_sched_latency = 6000000ULL; +/* + * Enable/disable using cstate knowledge in idle sibling selection + */ +unsigned int sysctl_sched_cstate_aware = 1; + /* * The initial- and re-scaling of tunables is configurable * diff --git a/kernel/sysctl.c b/kernel/sysctl.c index 8c7635ecb752..3f85f43a6f4a 100644 --- a/kernel/sysctl.c +++ b/kernel/sysctl.c @@ -321,6 +321,13 @@ static struct ctl_table kern_table[] = { .proc_handler = proc_dointvec, }, #ifdef CONFIG_SCHED_DEBUG + { + .procname = "sched_cstate_aware", + .data = &sysctl_sched_cstate_aware, + .maxlen = sizeof(unsigned int), + .mode = 0644, + .proc_handler = proc_dointvec, + }, { .procname = "sched_min_granularity_ns", .data = &sysctl_sched_min_granularity, From 237996b34375f881033bd8dc0d8fffc54d8d3e17 Mon Sep 17 00:00:00 2001 From: Quentin Perret Date: Fri, 31 Aug 2018 11:24:44 +0100 Subject: [PATCH 0502/1103] ANDROID: sched/fair: Factor out CPU selection from find_energy_efficient_cpu find_energy_efficient_cpu() is composed of two steps; we first look for the CPU with the max spare capacity in each frequency domain, and then the impact on energy of each candidate is estimated. In order to make it easier to implement other CPU selection policies, let's factor the candidate selection algorithm out of find_energy_efficient_cpu(), and mark the candidates in a mask. This should result in no functional difference. Signed-off-by: Quentin Perret Change-Id: I85a28880f01fcd11d7af28f9fbc1fe0cf4f197cf --- kernel/sched/fair.c | 104 +++++++++++++++++++++++++++----------------- 1 file changed, 63 insertions(+), 41 deletions(-) diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c index b2eeafcf0ee0..5d75d80d04d9 100644 --- a/kernel/sched/fair.c +++ b/kernel/sched/fair.c @@ -6541,6 +6541,46 @@ static long compute_energy(struct task_struct *p, int dst_cpu, return energy; } +static void select_max_spare_cap_cpus(struct sched_domain *sd, cpumask_t *cpus, + struct perf_domain *pd, struct task_struct *p) +{ + unsigned long spare_cap, max_spare_cap, util, cpu_cap; + int cpu, max_spare_cap_cpu; + + while (pd) { + max_spare_cap_cpu = -1; + max_spare_cap = 0; + + for_each_cpu_and(cpu, perf_domain_span(pd), sched_domain_span(sd)) { + if (!cpumask_test_cpu(cpu, &p->cpus_allowed)) + continue; + + /* Skip CPUs that will be overutilized. */ + util = cpu_util_next(cpu, p, cpu); + cpu_cap = capacity_of(cpu); + if (cpu_cap * 1024 < util * capacity_margin) + continue; + + /* + * Find the CPU with the maximum spare capacity in + * the performance domain + */ + spare_cap = cpu_cap - util; + if (spare_cap > max_spare_cap) { + max_spare_cap = spare_cap; + max_spare_cap_cpu = cpu; + } + } + + if (max_spare_cap_cpu >= 0) + cpumask_set_cpu(max_spare_cap_cpu, cpus); + + pd = pd->next; + } +} + +static DEFINE_PER_CPU(cpumask_t, energy_cpus); + /* * find_energy_efficient_cpu(): Find most energy-efficient target CPU for the * waking task. find_energy_efficient_cpu() looks for the CPU with maximum @@ -6574,10 +6614,10 @@ static int find_energy_efficient_cpu(struct task_struct *p, int prev_cpu, struct perf_domain *pd) { unsigned long prev_energy = ULONG_MAX, best_energy = ULONG_MAX; - int cpu, best_energy_cpu = prev_cpu; - struct perf_domain *head = pd; - unsigned long cpu_cap, util; + int weight, cpu, best_energy_cpu = prev_cpu; + unsigned long cur_energy; struct sched_domain *sd; + cpumask_t *candidates; sync_entity_load_avg(&p->se); @@ -6594,48 +6634,30 @@ static int find_energy_efficient_cpu(struct task_struct *p, int prev_cpu, if (!sd) return prev_cpu; - while (pd) { - unsigned long cur_energy, spare_cap, max_spare_cap = 0; - int max_spare_cap_cpu = -1; - - for_each_cpu_and(cpu, perf_domain_span(pd), sched_domain_span(sd)) { - if (!cpumask_test_cpu(cpu, &p->cpus_allowed)) - continue; + /* Pre-select a set of candidate CPUs. */ + candidates = this_cpu_ptr(&energy_cpus); + cpumask_clear(candidates); + select_max_spare_cap_cpus(sd, candidates, pd, p); - /* Skip CPUs that will be overutilized. */ - util = cpu_util_next(cpu, p, cpu); - cpu_cap = capacity_of(cpu); - if (cpu_cap * 1024 < util * capacity_margin) - continue; - - /* Always use prev_cpu as a candidate. */ - if (cpu == prev_cpu) { - prev_energy = compute_energy(p, prev_cpu, head); - if (prev_energy < best_energy) - best_energy = prev_energy; - continue; - } + /* Bail out if there is no candidate, or if the only one is prev_cpu */ + weight = cpumask_weight(candidates); + if (!weight || (weight == 1 && cpumask_first(candidates) == prev_cpu)) + return prev_cpu; - /* - * Find the CPU with the maximum spare capacity in - * the performance domain - */ - spare_cap = cpu_cap - util; - if (spare_cap > max_spare_cap) { - max_spare_cap = spare_cap; - max_spare_cap_cpu = cpu; - } - } + if (cpumask_test_cpu(prev_cpu, &p->cpus_allowed)) + prev_energy = best_energy = compute_energy(p, prev_cpu, pd); + else + prev_energy = best_energy = ULONG_MAX; - /* Evaluate the energy impact of using this CPU. */ - if (max_spare_cap_cpu >= 0) { - cur_energy = compute_energy(p, max_spare_cap_cpu, head); - if (cur_energy < best_energy) { - best_energy = cur_energy; - best_energy_cpu = max_spare_cap_cpu; - } + /* Select the best candidate energy-wise. */ + for_each_cpu(cpu, candidates) { + if (cpu == prev_cpu) + continue; + cur_energy = compute_energy(p, cpu, pd); + if (cur_energy < best_energy) { + best_energy = cur_energy; + best_energy_cpu = cpu; } - pd = pd->next; } /* From b6fb87d860670b697ddde0e355ccb2a018d624f5 Mon Sep 17 00:00:00 2001 From: Chris Redpath Date: Tue, 19 Dec 2017 19:43:01 +0000 Subject: [PATCH 0503/1103] ANDROID: Add find_best_target to minimise energy calculation overhead find_best_target started life as an optimised energy cpu selection algorithm designed to be efficient and high performance and integrate well with schedtune and userspace cpuset configuration. It has had many many small tweaks over time, and the version added here is forward ported from the version used in android-4.4 and android-4.9. The linkage to the rest of EAS is slightly different, however. This version is split into the three main use-cases and addresses them in priority order: A) latency sensitive tasks B) non latency sensitive tasks on IDLE CPUs C) non latency sensitive tasks on ACTIVE CPUs Case A) Latency sensitive tasks Unconditionally favoring tasks that prefer idle CPU to improve latency. When we do enter here, we are looking for: - an idle CPU, whatever its idle_state is, since the first CPUs we explore are more likely to be reserved for latency sensitive tasks. - a non idle CPU where the task fits in its current capacity and has the maximum spare capacity. - a non idle CPU with lower contention from other tasks and running at the lowest possible OPP. The last two goals try to favor a non idle CPU where the task can run as if it is "almost alone". A maximum spare capacity CPU is favored since the task already fits into that CPU's capacity without waiting for an OPP change. For any case other than case A, we avoid CPUs which would become overutilized if we placed the task there. Case B) Non latency sensitive tasks on IDLE CPUs. Find an optimal backup IDLE CPU for non latency sensitive tasks. Here we are looking for: - minimizing the capacity_orig, i.e. preferring LITTLE CPUs If IDLE cpus are available, we prefer to choose one in order to spread tasks and improve performance. Case C) Non latency sensitive tasks on ACTIVE CPUs. Pack tasks in the most energy efficient capacities. This task packing strategy prefers more energy efficient CPUs (i.e. pack on smaller maximum capacity CPUs) while also trying to spread tasks to run them all at the lower OPP. This assumes for example that it's more energy efficient to run two tasks on two CPUs at a lower OPP than packing both on a single CPU but running that CPU at an higher OPP. This code has had many other contributors over the development listed here as Cc. Cc: Ke Wang Cc: Joel Fernandes Cc: Patrick Bellasi Cc: Valentin Schneider Cc: Dietmar Eggemann Cc: Srinath Sridharan Cc: Todd Kjos Cc: Juri Lelli Signed-off-by: Chris Redpath (cherry picked from commit f240e44406558b17ff7765f252b0bcdcbc15126f) [ - Removed tracepoints - Took capacity_curr_of() from "7f6fb82 ANDROID: sched: EAS: take cstate into account when selecting idle core" - Re-use sd_ea from find_energy_efficient_cpu() / removed start_cpu() - Mark candidates with a cpumask given by feec() - Squashed Ionela's tri-gear fbt fixes from android-4.14 - Squashed Patrick's changes related to util_est from android-4.14 ] Signed-off-by: Quentin Perret Change-Id: I9500d308c879dd53b08adeda8f988238e39cc392 --- kernel/sched/fair.c | 318 +++++++++++++++++++++++++++++++++++++++- kernel/sched/features.h | 5 + 2 files changed, 322 insertions(+), 1 deletion(-) diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c index 5d75d80d04d9..65627b74bf47 100644 --- a/kernel/sched/fair.c +++ b/kernel/sched/fair.c @@ -6437,6 +6437,318 @@ static unsigned long cpu_util_wake(int cpu, struct task_struct *p) return min_t(unsigned long, util, capacity_orig_of(cpu)); } +/* + * Returns the current capacity of cpu after applying both + * cpu and freq scaling. + */ +unsigned long capacity_curr_of(int cpu) +{ + unsigned long max_cap = cpu_rq(cpu)->cpu_capacity_orig; + unsigned long scale_freq = arch_scale_freq_capacity(cpu); + + return cap_scale(max_cap, scale_freq); +} + +static void find_best_target(struct sched_domain *sd, cpumask_t *cpus, + struct task_struct *p) +{ + unsigned long min_util = boosted_task_util(p); + unsigned long target_capacity = ULONG_MAX; + unsigned long min_wake_util = ULONG_MAX; + unsigned long target_max_spare_cap = 0; + unsigned long target_util = ULONG_MAX; + bool prefer_idle = schedtune_prefer_idle(p); + bool boosted = schedtune_task_boost(p) > 0; + /* Initialise with deepest possible cstate (INT_MAX) */ + int shallowest_idle_cstate = INT_MAX; + struct sched_group *sg; + int best_active_cpu = -1; + int best_idle_cpu = -1; + int target_cpu = -1; + int backup_cpu = -1; + int i; + + /* + * In most cases, target_capacity tracks capacity_orig of the most + * energy efficient CPU candidate, thus requiring to minimise + * target_capacity. For these cases target_capacity is already + * initialized to ULONG_MAX. + * However, for prefer_idle and boosted tasks we look for a high + * performance CPU, thus requiring to maximise target_capacity. In this + * case we initialise target_capacity to 0. + */ + if (prefer_idle && boosted) + target_capacity = 0; + + /* Scan CPUs in all SDs */ + sg = sd->groups; + do { + for_each_cpu_and(i, &p->cpus_allowed, sched_group_span(sg)) { + unsigned long capacity_curr = capacity_curr_of(i); + unsigned long capacity_orig = capacity_orig_of(i); + unsigned long wake_util, new_util; + long spare_cap; + int idle_idx = INT_MAX; + + if (!cpu_online(i)) + continue; + + /* + * p's blocked utilization is still accounted for on prev_cpu + * so prev_cpu will receive a negative bias due to the double + * accounting. However, the blocked utilization may be zero. + */ + wake_util = cpu_util_wake(i, p); + new_util = wake_util + task_util_est(p); + + /* + * Ensure minimum capacity to grant the required boost. + * The target CPU can be already at a capacity level higher + * than the one required to boost the task. + */ + new_util = max(min_util, new_util); + if (new_util > capacity_orig) + continue; + + /* + * Pre-compute the maximum possible capacity we expect + * to have available on this CPU once the task is + * enqueued here. + */ + spare_cap = capacity_orig - new_util; + + if (idle_cpu(i)) + idle_idx = idle_get_state_idx(cpu_rq(i)); + + + /* + * Case A) Latency sensitive tasks + * + * Unconditionally favoring tasks that prefer idle CPU to + * improve latency. + * + * Looking for: + * - an idle CPU, whatever its idle_state is, since + * the first CPUs we explore are more likely to be + * reserved for latency sensitive tasks. + * - a non idle CPU where the task fits in its current + * capacity and has the maximum spare capacity. + * - a non idle CPU with lower contention from other + * tasks and running at the lowest possible OPP. + * + * The last two goals tries to favor a non idle CPU + * where the task can run as if it is "almost alone". + * A maximum spare capacity CPU is favoured since + * the task already fits into that CPU's capacity + * without waiting for an OPP chance. + * + * The following code path is the only one in the CPUs + * exploration loop which is always used by + * prefer_idle tasks. It exits the loop with wither a + * best_active_cpu or a target_cpu which should + * represent an optimal choice for latency sensitive + * tasks. + */ + if (prefer_idle) { + + /* + * Case A.1: IDLE CPU + * Return the best IDLE CPU we find: + * - for boosted tasks: the CPU with the highest + * performance (i.e. biggest capacity_orig) + * - for !boosted tasks: the most energy + * efficient CPU (i.e. smallest capacity_orig) + */ + if (idle_cpu(i)) { + if (boosted && + capacity_orig < target_capacity) + continue; + if (!boosted && + capacity_orig > target_capacity) + continue; + /* + * Minimise value of idle state: skip + * deeper idle states and pick the + * shallowest. + */ + if (capacity_orig == target_capacity && + sysctl_sched_cstate_aware && + idle_idx >= shallowest_idle_cstate) + continue; + + target_capacity = capacity_orig; + shallowest_idle_cstate = idle_idx; + best_idle_cpu = i; + continue; + } + if (best_idle_cpu != -1) + continue; + + /* + * Case A.2: Target ACTIVE CPU + * Favor CPUs with max spare capacity. + */ + if (capacity_curr > new_util && + spare_cap > target_max_spare_cap) { + target_max_spare_cap = spare_cap; + target_cpu = i; + continue; + } + if (target_cpu != -1) + continue; + + + /* + * Case A.3: Backup ACTIVE CPU + * Favor CPUs with: + * - lower utilization due to other tasks + * - lower utilization with the task in + */ + if (wake_util > min_wake_util) + continue; + min_wake_util = wake_util; + best_active_cpu = i; + continue; + } + + /* + * Enforce EAS mode + * + * For non latency sensitive tasks, skip CPUs that + * will be overutilized by moving the task there. + * + * The goal here is to remain in EAS mode as long as + * possible at least for !prefer_idle tasks. + */ + if ((new_util * capacity_margin) > + (capacity_orig * SCHED_CAPACITY_SCALE)) + continue; + + /* + * Favor CPUs with smaller capacity for non latency + * sensitive tasks. + */ + if (capacity_orig > target_capacity) + continue; + + /* + * Case B) Non latency sensitive tasks on IDLE CPUs. + * + * Find an optimal backup IDLE CPU for non latency + * sensitive tasks. + * + * Looking for: + * - minimizing the capacity_orig, + * i.e. preferring LITTLE CPUs + * - favoring shallowest idle states + * i.e. avoid to wakeup deep-idle CPUs + * + * The following code path is used by non latency + * sensitive tasks if IDLE CPUs are available. If at + * least one of such CPUs are available it sets the + * best_idle_cpu to the most suitable idle CPU to be + * selected. + * + * If idle CPUs are available, favour these CPUs to + * improve performances by spreading tasks. + * Indeed, the energy_diff() computed by the caller + * will take care to ensure the minimization of energy + * consumptions without affecting performance. + */ + if (idle_cpu(i)) { + /* + * Skip CPUs in deeper idle state, but only + * if they are also less energy efficient. + * IOW, prefer a deep IDLE LITTLE CPU vs a + * shallow idle big CPU. + */ + if (capacity_orig == target_capacity && + sysctl_sched_cstate_aware && + idle_idx >= shallowest_idle_cstate) + continue; + + target_capacity = capacity_orig; + shallowest_idle_cstate = idle_idx; + best_idle_cpu = i; + continue; + } + + /* + * Case C) Non latency sensitive tasks on ACTIVE CPUs. + * + * Pack tasks in the most energy efficient capacities. + * + * This task packing strategy prefers more energy + * efficient CPUs (i.e. pack on smaller maximum + * capacity CPUs) while also trying to spread tasks to + * run them all at the lower OPP. + * + * This assumes for example that it's more energy + * efficient to run two tasks on two CPUs at a lower + * OPP than packing both on a single CPU but running + * that CPU at an higher OPP. + * + * Thus, this case keep track of the CPU with the + * smallest maximum capacity and highest spare maximum + * capacity. + */ + + /* Favor CPUs with maximum spare capacity */ + if (capacity_orig == target_capacity && + spare_cap < target_max_spare_cap) + continue; + + target_max_spare_cap = spare_cap; + target_capacity = capacity_orig; + target_util = new_util; + target_cpu = i; + } + + } while (sg = sg->next, sg != sd->groups); + + /* + * For non latency sensitive tasks, cases B and C in the previous loop, + * we pick the best IDLE CPU only if we was not able to find a target + * ACTIVE CPU. + * + * Policies priorities: + * + * - prefer_idle tasks: + * + * a) IDLE CPU available: best_idle_cpu + * b) ACTIVE CPU where task fits and has the bigger maximum spare + * capacity (i.e. target_cpu) + * c) ACTIVE CPU with less contention due to other tasks + * (i.e. best_active_cpu) + * + * - NON prefer_idle tasks: + * + * a) ACTIVE CPU: target_cpu + * b) IDLE CPU: best_idle_cpu + */ + + if (prefer_idle && (best_idle_cpu != -1)) { + target_cpu = best_idle_cpu; + goto target; + } + + if (target_cpu == -1) + target_cpu = prefer_idle + ? best_active_cpu + : best_idle_cpu; + else + backup_cpu = prefer_idle + ? best_active_cpu + : best_idle_cpu; + + if (backup_cpu >= 0) + cpumask_set_cpu(backup_cpu, cpus); + if (target_cpu >= 0) { +target: + cpumask_set_cpu(target_cpu, cpus); + } +} + /* * Disable WAKE_AFFINE in the case where task @p doesn't fit in the * capacity of either the waking CPU @cpu or the previous CPU @prev_cpu. @@ -6637,7 +6949,11 @@ static int find_energy_efficient_cpu(struct task_struct *p, int prev_cpu, /* Pre-select a set of candidate CPUs. */ candidates = this_cpu_ptr(&energy_cpus); cpumask_clear(candidates); - select_max_spare_cap_cpus(sd, candidates, pd, p); + + if (sched_feat(FIND_BEST_TARGET)) + find_best_target(sd, candidates, p); + else + select_max_spare_cap_cpus(sd, candidates, pd, p); /* Bail out if there is no candidate, or if the only one is prev_cpu */ weight = cpumask_weight(candidates); diff --git a/kernel/sched/features.h b/kernel/sched/features.h index a49c0701a2f2..3fe0fb0b4465 100644 --- a/kernel/sched/features.h +++ b/kernel/sched/features.h @@ -97,3 +97,8 @@ SCHED_FEAT(UTIL_EST, true) * root domain having an Energy Model attached. */ SCHED_FEAT(ENERGY_AWARE, true) + +/* + * Fast pre-selection of CPU candidates for EAS. + */ +SCHED_FEAT(FIND_BEST_TARGET, true) From f45d90a51b34b835d8188886526b0a46d6e344c4 Mon Sep 17 00:00:00 2001 From: Chris Redpath Date: Sat, 21 Oct 2017 15:56:50 +0100 Subject: [PATCH 0504/1103] ANDROID: sched: Unconditionally honor sync flag for energy-aware wakeups Since we don't do energy-aware wakeups when we are overutilized, always honoring sync wakeups in this state does not prevent wake-wide mechanics overruling the flag as normal. This patch is based upon previous work to build EAS for android products. sync-hint code taken from commit 4a5e890ec60d "sched/fair: add tunable to force selection at cpu granularity" written by Juri Lelli Signed-off-by: Chris Redpath (cherry-picked from commit f1ec666a62dec1083ed52fe1ddef093b84373aaf) [ Moved the feature to find_energy_efficient_cpu() ] Signed-off-by: Quentin Perret Change-Id: I4b3d79141fc8e53dc51cd63ac11096c2e3cb10f5 --- include/linux/sched/sysctl.h | 1 + kernel/sched/fair.c | 15 +++++++++++++-- kernel/sysctl.c | 7 +++++++ 3 files changed, 21 insertions(+), 2 deletions(-) diff --git a/include/linux/sched/sysctl.h b/include/linux/sched/sysctl.h index 3f9ae132037e..77e9fa3c5b49 100644 --- a/include/linux/sched/sysctl.h +++ b/include/linux/sched/sysctl.h @@ -22,6 +22,7 @@ enum { sysctl_hung_task_timeout_secs = 0 }; extern unsigned int sysctl_sched_latency; extern unsigned int sysctl_sched_min_granularity; +extern unsigned int sysctl_sched_sync_hint_enable; extern unsigned int sysctl_sched_cstate_aware; extern unsigned int sysctl_sched_wakeup_granularity; extern unsigned int sysctl_sched_child_runs_first; diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c index 65627b74bf47..d709d834dad5 100644 --- a/kernel/sched/fair.c +++ b/kernel/sched/fair.c @@ -40,6 +40,11 @@ unsigned int sysctl_sched_latency = 6000000ULL; unsigned int normalized_sysctl_sched_latency = 6000000ULL; +/* + * Enable/disable honoring sync flag in energy-aware wakeups. + */ +unsigned int sysctl_sched_sync_hint_enable = 1; + /* * Enable/disable using cstate knowledge in idle sibling selection */ @@ -6923,7 +6928,7 @@ static DEFINE_PER_CPU(cpumask_t, energy_cpus); * SD_ASYM_CPUCAPACITY is set. */ static int find_energy_efficient_cpu(struct task_struct *p, int prev_cpu, - struct perf_domain *pd) + struct perf_domain *pd, int sync) { unsigned long prev_energy = ULONG_MAX, best_energy = ULONG_MAX; int weight, cpu, best_energy_cpu = prev_cpu; @@ -6931,6 +6936,12 @@ static int find_energy_efficient_cpu(struct task_struct *p, int prev_cpu, struct sched_domain *sd; cpumask_t *candidates; + if (sysctl_sched_sync_hint_enable && sync) { + cpu = smp_processor_id(); + if (cpumask_test_cpu(cpu, &p->cpus_allowed)) + return cpu; + } + sync_entity_load_avg(&p->se); if (!task_util_est(p)) @@ -7030,7 +7041,7 @@ select_task_rq_fair(struct task_struct *p, int prev_cpu, int sd_flag, int wake_f struct perf_domain *pd = rcu_dereference(rd->pd); if (pd && !READ_ONCE(rd->overutilized)) { - new_cpu = find_energy_efficient_cpu(p, prev_cpu, pd); + new_cpu = find_energy_efficient_cpu(p, prev_cpu, pd, sync); goto unlock; } } diff --git a/kernel/sysctl.c b/kernel/sysctl.c index 3f85f43a6f4a..ed676ec4ddac 100644 --- a/kernel/sysctl.c +++ b/kernel/sysctl.c @@ -346,6 +346,13 @@ static struct ctl_table kern_table[] = { .extra1 = &min_sched_granularity_ns, .extra2 = &max_sched_granularity_ns, }, + { + .procname = "sched_sync_hint_enable", + .data = &sysctl_sched_sync_hint_enable, + .maxlen = sizeof(unsigned int), + .mode = 0644, + .proc_handler = proc_dointvec, + }, { .procname = "sched_wakeup_granularity_ns", .data = &sysctl_sched_wakeup_granularity, From 18f1c7c877413a9398c3d6bfb06740a6abf68857 Mon Sep 17 00:00:00 2001 From: Brendan Jackman Date: Fri, 11 Aug 2017 10:45:58 +0100 Subject: [PATCH 0505/1103] FROMLIST: sched/fair: Use wake_q length as a hint for wake_wide This patch adds a parameter to select_task_rq, sibling_count_hint allowing the caller, where it has this information, to inform the sched_class the number of tasks that are being woken up as part of the same event. The wake_q mechanism is one case where this information is available. select_task_rq_fair can then use the information to detect that it needs to widen the search space for task placement in order to avoid overloading the last-level cache domain's CPUs. * * * The reason I am investigating this change is the following use case on ARM big.LITTLE (asymmetrical CPU capacity): 1 task per CPU, which all repeatedly do X amount of work then pthread_barrier_wait (i.e. sleep until the last task finishes its X and hits the barrier). On big.LITTLE, the tasks which get a "big" CPU finish faster, and then those CPUs pull over the tasks that are still running: v CPU v ->time-> ------------- 0 (big) 11111 /333 ------------- 1 (big) 22222 /444| ------------- 2 (LITTLE) 333333/ ------------- 3 (LITTLE) 444444/ ------------- Now when task 4 hits the barrier (at |) and wakes the others up, there are 4 tasks with prev_cpu= and 0 tasks with prev_cpu=. want_affine therefore means that we'll only look in CPUs 0 and 1 (sd_llc), so tasks will be unnecessarily coscheduled on the bigs until the next load balance, something like this: v CPU v ->time-> ------------------------ 0 (big) 11111 /333 31313\33333 ------------------------ 1 (big) 22222 /444|424\4444444 ------------------------ 2 (LITTLE) 333333/ \222222 ------------------------ 3 (LITTLE) 444444/ \1111 ------------------------ ^^^ underutilization So, I'm trying to get want_affine = 0 for these tasks. I don't _think_ any incarnation of the wakee_flips mechanism can help us here because which task is waker and which tasks are wakees generally changes with each iteration. However pthread_barrier_wait (or more accurately FUTEX_WAKE) has the nice property that we know exactly how many tasks are being woken, so we can cheat. It might be a disadvantage that we "widen" _every_ task that's woken in an event, while select_idle_sibling would work fine for the first sd_llc_size - 1 tasks. IIUC, if wake_affine() behaves correctly this trick wouldn't be necessary on SMP systems, so it might be best guarded by the presence of SD_ASYM_CPUCAPACITY? * * * Final note.. In order to observe "perfect" behaviour for this use case, I also had to disable the TTWU_QUEUE sched feature. Suppose during the wakeup above we are working through the work queue and have placed tasks 3 and 2, and are about to place task 1: v CPU v ->time-> -------------- 0 (big) 11111 /333 3 -------------- 1 (big) 22222 /444|4 -------------- 2 (LITTLE) 333333/ 2 -------------- 3 (LITTLE) 444444/ <- Task 1 should go here -------------- If TTWU_QUEUE is enabled, we will not yet have enqueued task 2 (having instead sent a reschedule IPI) or attached its load to CPU 2. So we are likely to also place task 1 on cpu 2. Disabling TTWU_QUEUE means that we enqueue task 2 before placing task 1, solving this issue. TTWU_QUEUE is there to minimise rq lock contention, and I guess that this contention is less of an issue on big.LITTLE systems since they have relatively few CPUs, which suggests the trade-off makes sense here. Signed-off-by: Brendan Jackman Cc: Ingo Molnar Cc: Peter Zijlstra Cc: Josef Bacik Cc: Joel Fernandes Cc: Mike Galbraith Cc: Matt Fleming ( - Applied from https://patchwork.kernel.org/patch/9895261/ - Fixed trivial conflict in kernel/sched/core.c - Fixed select_task_rq_idle, now in kernel/sched/idle.c - Fixed trivial conflict in select_task_rq_fair ) Signed-off-by: Quentin Perret Change-Id: I3cfc4bf48c3d7feef969db4d22449f4fbb4f795d --- include/linux/sched/wake_q.h | 2 ++ kernel/sched/core.c | 34 +++++++++++++++++++++++----------- kernel/sched/deadline.c | 3 ++- kernel/sched/fair.c | 15 ++++++++++----- kernel/sched/idle.c | 3 ++- kernel/sched/rt.c | 3 ++- kernel/sched/sched.h | 3 ++- kernel/sched/stop_task.c | 3 ++- 8 files changed, 45 insertions(+), 21 deletions(-) diff --git a/include/linux/sched/wake_q.h b/include/linux/sched/wake_q.h index 10b19a192b2d..a3661e93da6f 100644 --- a/include/linux/sched/wake_q.h +++ b/include/linux/sched/wake_q.h @@ -34,6 +34,7 @@ struct wake_q_head { struct wake_q_node *first; struct wake_q_node **lastp; + int count; }; #define WAKE_Q_TAIL ((struct wake_q_node *) 0x01) @@ -45,6 +46,7 @@ static inline void wake_q_init(struct wake_q_head *head) { head->first = WAKE_Q_TAIL; head->lastp = &head->first; + head->count = 0; } extern void wake_q_add(struct wake_q_head *head, diff --git a/kernel/sched/core.c b/kernel/sched/core.c index ad97f3ba5ec5..a30556569187 100644 --- a/kernel/sched/core.c +++ b/kernel/sched/core.c @@ -412,6 +412,8 @@ void wake_q_add(struct wake_q_head *head, struct task_struct *task) if (cmpxchg(&node->next, NULL, WAKE_Q_TAIL)) return; + head->count++; + get_task_struct(task); /* @@ -421,6 +423,10 @@ void wake_q_add(struct wake_q_head *head, struct task_struct *task) head->lastp = &node->next; } +static int +try_to_wake_up(struct task_struct *p, unsigned int state, int wake_flags, + int sibling_count_hint); + void wake_up_q(struct wake_q_head *head) { struct wake_q_node *node = head->first; @@ -435,10 +441,10 @@ void wake_up_q(struct wake_q_head *head) task->wake_q.next = NULL; /* - * wake_up_process() executes a full barrier, which pairs with + * try_to_wake_up() executes a full barrier, which pairs with * the queueing in wake_q_add() so as not to miss wakeups. */ - wake_up_process(task); + try_to_wake_up(task, TASK_NORMAL, 0, head->count); put_task_struct(task); } } @@ -1523,12 +1529,14 @@ static int select_fallback_rq(int cpu, struct task_struct *p) * The caller (fork, wakeup) owns p->pi_lock, ->cpus_allowed is stable. */ static inline -int select_task_rq(struct task_struct *p, int cpu, int sd_flags, int wake_flags) +int select_task_rq(struct task_struct *p, int cpu, int sd_flags, int wake_flags, + int sibling_count_hint) { lockdep_assert_held(&p->pi_lock); if (p->nr_cpus_allowed > 1) - cpu = p->sched_class->select_task_rq(p, cpu, sd_flags, wake_flags); + cpu = p->sched_class->select_task_rq(p, cpu, sd_flags, wake_flags, + sibling_count_hint); else cpu = cpumask_any(&p->cpus_allowed); @@ -1931,6 +1939,8 @@ static void ttwu_queue(struct task_struct *p, int cpu, int wake_flags) * @p: the thread to be awakened * @state: the mask of task states that can be woken * @wake_flags: wake modifier flags (WF_*) + * @sibling_count_hint: A hint at the number of threads that are being woken up + * in this event. * * If (@state & @p->state) @p->state = TASK_RUNNING. * @@ -1946,7 +1956,8 @@ static void ttwu_queue(struct task_struct *p, int cpu, int wake_flags) * %false otherwise. */ static int -try_to_wake_up(struct task_struct *p, unsigned int state, int wake_flags) +try_to_wake_up(struct task_struct *p, unsigned int state, int wake_flags, + int sibling_count_hint) { unsigned long flags; int cpu, success = 0; @@ -2033,7 +2044,8 @@ try_to_wake_up(struct task_struct *p, unsigned int state, int wake_flags) atomic_dec(&task_rq(p)->nr_iowait); } - cpu = select_task_rq(p, p->wake_cpu, SD_BALANCE_WAKE, wake_flags); + cpu = select_task_rq(p, p->wake_cpu, SD_BALANCE_WAKE, wake_flags, + sibling_count_hint); if (task_cpu(p) != cpu) { wake_flags |= WF_MIGRATED; set_task_cpu(p, cpu); @@ -2120,13 +2132,13 @@ static void try_to_wake_up_local(struct task_struct *p, struct rq_flags *rf) */ int wake_up_process(struct task_struct *p) { - return try_to_wake_up(p, TASK_NORMAL, 0); + return try_to_wake_up(p, TASK_NORMAL, 0, 1); } EXPORT_SYMBOL(wake_up_process); int wake_up_state(struct task_struct *p, unsigned int state) { - return try_to_wake_up(p, state, 0); + return try_to_wake_up(p, state, 0, 1); } /* @@ -2408,7 +2420,7 @@ void wake_up_new_task(struct task_struct *p) * as we're not fully set-up yet. */ p->recent_used_cpu = task_cpu(p); - __set_task_cpu(p, select_task_rq(p, task_cpu(p), SD_BALANCE_FORK, 0)); + __set_task_cpu(p, select_task_rq(p, task_cpu(p), SD_BALANCE_FORK, 0, 1)); #endif rq = __task_rq_lock(p, &rf); update_rq_clock(rq); @@ -2947,7 +2959,7 @@ void sched_exec(void) int dest_cpu; raw_spin_lock_irqsave(&p->pi_lock, flags); - dest_cpu = p->sched_class->select_task_rq(p, task_cpu(p), SD_BALANCE_EXEC, 0); + dest_cpu = p->sched_class->select_task_rq(p, task_cpu(p), SD_BALANCE_EXEC, 0, 1); if (dest_cpu == smp_processor_id()) goto unlock; @@ -3708,7 +3720,7 @@ asmlinkage __visible void __sched preempt_schedule_irq(void) int default_wake_function(wait_queue_entry_t *curr, unsigned mode, int wake_flags, void *key) { - return try_to_wake_up(curr->private, mode, wake_flags); + return try_to_wake_up(curr->private, mode, wake_flags, 1); } EXPORT_SYMBOL(default_wake_function); diff --git a/kernel/sched/deadline.c b/kernel/sched/deadline.c index 91e4202b0634..921685482cc0 100644 --- a/kernel/sched/deadline.c +++ b/kernel/sched/deadline.c @@ -1567,7 +1567,8 @@ static void yield_task_dl(struct rq *rq) static int find_later_rq(struct task_struct *task); static int -select_task_rq_dl(struct task_struct *p, int cpu, int sd_flag, int flags) +select_task_rq_dl(struct task_struct *p, int cpu, int sd_flag, int flags, + int sibling_count_hint) { struct task_struct *curr; struct rq *rq; diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c index d709d834dad5..4bddcd654098 100644 --- a/kernel/sched/fair.c +++ b/kernel/sched/fair.c @@ -5641,15 +5641,18 @@ static void record_wakee(struct task_struct *p) * whatever is irrelevant, spread criteria is apparent partner count exceeds * socket size. */ -static int wake_wide(struct task_struct *p) +static int wake_wide(struct task_struct *p, int sibling_count_hint) { unsigned int master = current->wakee_flips; unsigned int slave = p->wakee_flips; - int factor = this_cpu_read(sd_llc_size); + int llc_size = this_cpu_read(sd_llc_size); + + if (sibling_count_hint >= llc_size) + return 1; if (master < slave) swap(master, slave); - if (slave < factor || master < slave * factor) + if (slave < llc_size || master < slave * llc_size) return 0; return 1; } @@ -7011,7 +7014,8 @@ static int find_energy_efficient_cpu(struct task_struct *p, int prev_cpu, * preempt must be disabled. */ static int -select_task_rq_fair(struct task_struct *p, int prev_cpu, int sd_flag, int wake_flags) +select_task_rq_fair(struct task_struct *p, int prev_cpu, int sd_flag, int wake_flags, + int sibling_count_hint) { struct sched_domain *tmp, *sd = NULL; int cpu = smp_processor_id(); @@ -7046,7 +7050,8 @@ select_task_rq_fair(struct task_struct *p, int prev_cpu, int sd_flag, int wake_f } } - want_affine = !wake_wide(p) && !wake_cap(p, cpu, prev_cpu) && + want_affine = !wake_wide(p, sibling_count_hint) && + !wake_cap(p, cpu, prev_cpu) && cpumask_test_cpu(cpu, &p->cpus_allowed); } diff --git a/kernel/sched/idle.c b/kernel/sched/idle.c index 2d3e2804c08b..2c8719f1de0a 100644 --- a/kernel/sched/idle.c +++ b/kernel/sched/idle.c @@ -375,7 +375,8 @@ void cpu_startup_entry(enum cpuhp_state state) #ifdef CONFIG_SMP static int -select_task_rq_idle(struct task_struct *p, int cpu, int sd_flag, int flags) +select_task_rq_idle(struct task_struct *p, int cpu, int sd_flag, int flags, + int sibling_count_hint) { return task_cpu(p); /* IDLE tasks as never migrated */ } diff --git a/kernel/sched/rt.c b/kernel/sched/rt.c index 2e2955a8cf8f..0be707d9c2db 100644 --- a/kernel/sched/rt.c +++ b/kernel/sched/rt.c @@ -1386,7 +1386,8 @@ static void yield_task_rt(struct rq *rq) static int find_lowest_rq(struct task_struct *task); static int -select_task_rq_rt(struct task_struct *p, int cpu, int sd_flag, int flags) +select_task_rq_rt(struct task_struct *p, int cpu, int sd_flag, int flags, + int sibling_count_hint) { struct task_struct *curr; struct rq *rq; diff --git a/kernel/sched/sched.h b/kernel/sched/sched.h index 4a368968d046..86d155add21d 100644 --- a/kernel/sched/sched.h +++ b/kernel/sched/sched.h @@ -1555,7 +1555,8 @@ struct sched_class { void (*put_prev_task)(struct rq *rq, struct task_struct *p); #ifdef CONFIG_SMP - int (*select_task_rq)(struct task_struct *p, int task_cpu, int sd_flag, int flags); + int (*select_task_rq)(struct task_struct *p, int task_cpu, int sd_flag, int flags, + int subling_count_hint); void (*migrate_task_rq)(struct task_struct *p, int new_cpu); void (*task_woken)(struct rq *this_rq, struct task_struct *task); diff --git a/kernel/sched/stop_task.c b/kernel/sched/stop_task.c index c183b790ca54..6446d6130c5d 100644 --- a/kernel/sched/stop_task.c +++ b/kernel/sched/stop_task.c @@ -11,7 +11,8 @@ #ifdef CONFIG_SMP static int -select_task_rq_stop(struct task_struct *p, int cpu, int sd_flag, int flags) +select_task_rq_stop(struct task_struct *p, int cpu, int sd_flag, int flags, + int sibling_count_hint) { return task_cpu(p); /* stop tasks as never migrate */ } From 6a3f85fa2f511ba86a17eb61c286282bee554b0a Mon Sep 17 00:00:00 2001 From: Chris Redpath Date: Tue, 19 Dec 2017 19:32:02 +0000 Subject: [PATCH 0506/1103] ANDROID: sched: fair: Bypass energy-aware wakeup for prefer-idle tasks Use the upstream slow path to find an idle cpu for prefer-idle tasks. This slow-path is actually faster than the EAS path we are currently going through (compute_energy()) which is really slow. No performance degradation is seen with this and it reduces the delta quite a bit between upstream and out of tree code. It's not clear yet if using the mainline slow path task placement when a task has the schedtune attribute prefer_idle=1 is the right thing to do for products. Put the option to disable this behind a sched feature so we can try out both options. Signed-off-by: Joel Fernandes (refactored for 4.14 version) Signed-off-by: Chris Redpath (cherry picked from commit c0ff131c88f68e4985793663144b6f9cf77be9d3) [ - Refactored for 4.17 version - Adjusted the commit header to the new function names ] Signed-off-by: Quentin Perret Change-Id: Icf762a101c92c0e3f9e61df0370247fa15455581 --- kernel/sched/fair.c | 14 ++++++++++---- kernel/sched/features.h | 9 +++++++++ 2 files changed, 19 insertions(+), 4 deletions(-) diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c index 4bddcd654098..95d4a3beea1c 100644 --- a/kernel/sched/fair.c +++ b/kernel/sched/fair.c @@ -7044,17 +7044,23 @@ select_task_rq_fair(struct task_struct *p, int prev_cpu, int sd_flag, int wake_f struct root_domain *rd = cpu_rq(cpu)->rd; struct perf_domain *pd = rcu_dereference(rd->pd); - if (pd && !READ_ONCE(rd->overutilized)) { - new_cpu = find_energy_efficient_cpu(p, prev_cpu, pd, sync); - goto unlock; - } + if (!pd || READ_ONCE(rd->overutilized)) + goto affine; + + if (schedtune_prefer_idle(p) && !sched_feat(EAS_PREFER_IDLE) && !sync) + goto sd_loop; + + new_cpu = find_energy_efficient_cpu(p, prev_cpu, pd, sync); + goto unlock; } +affine: want_affine = !wake_wide(p, sibling_count_hint) && !wake_cap(p, cpu, prev_cpu) && cpumask_test_cpu(cpu, &p->cpus_allowed); } +sd_loop: for_each_domain(cpu, tmp) { if (!(tmp->flags & SD_LOAD_BALANCE)) break; diff --git a/kernel/sched/features.h b/kernel/sched/features.h index 3fe0fb0b4465..2c33917b2067 100644 --- a/kernel/sched/features.h +++ b/kernel/sched/features.h @@ -102,3 +102,12 @@ SCHED_FEAT(ENERGY_AWARE, true) * Fast pre-selection of CPU candidates for EAS. */ SCHED_FEAT(FIND_BEST_TARGET, true) + +/* + * Energy aware scheduling algorithm choices: + * EAS_PREFER_IDLE + * Direct tasks in a schedtune.prefer_idle=1 group through + * the EAS path for wakeup task placement. Otherwise, put + * those tasks through the mainline slow path. + */ +SCHED_FEAT(EAS_PREFER_IDLE, true) From b582687429b6281d7c63d7b5e89e143e55415a8c Mon Sep 17 00:00:00 2001 From: Quentin Perret Date: Thu, 24 May 2018 17:35:02 +0100 Subject: [PATCH 0507/1103] ANDROID: sched/fair: Bypass energy computation for prefer_idle tasks If the only pre-selected candidate CPU in find_energy_efficient_cpu() happens to be prev_cpu, there is not point in computing the system energy since we have nothing to compare it against, so we currently bail out early. The same logic can be extended when prefer_idle tasks are routed in the energy-aware wake-up path: if the only candidate is idle for a prefer_idle task, just select it no matter what the energy impact is. That should help speeding-up wake-ups of prefer_idle tasks, at least when find_best_target() is used for them. Signed-off-by: Quentin Perret Change-Id: Idd0e387e4a766061cc05d2584df3a31e4dabfd09 --- kernel/sched/fair.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c index 95d4a3beea1c..2384841c5ceb 100644 --- a/kernel/sched/fair.c +++ b/kernel/sched/fair.c @@ -6969,11 +6969,17 @@ static int find_energy_efficient_cpu(struct task_struct *p, int prev_cpu, else select_max_spare_cap_cpus(sd, candidates, pd, p); - /* Bail out if there is no candidate, or if the only one is prev_cpu */ + /* Bail out if no candidate was found. */ weight = cpumask_weight(candidates); - if (!weight || (weight == 1 && cpumask_first(candidates) == prev_cpu)) + if (!weight) return prev_cpu; + /* If there is only one sensible candidate, select it now. */ + cpu = cpumask_first(candidates); + if (weight == 1 && ((schedtune_prefer_idle(p) && idle_cpu(cpu)) || + (cpu == prev_cpu))) + return cpu; + if (cpumask_test_cpu(prev_cpu, &p->cpus_allowed)) prev_energy = best_energy = compute_energy(p, prev_cpu, pd); else From 58a4195b715c4416dcb0c7d02ccd92f1d88e70ca Mon Sep 17 00:00:00 2001 From: Dietmar Eggemann Date: Thu, 10 May 2018 15:48:06 +0100 Subject: [PATCH 0508/1103] ANDROID: sched/fair: add arch scaling function for max frequency capping To be able to scale the cpu capacity by this factor introduce a call to the new arch scaling function arch_scale_max_freq_capacity() in update_cpu_capacity() and provide a default implementation which returns SCHED_CAPACITY_SCALE. Another subsystem (e.g. cpufreq) or architectural or platform specific code can overwrite this default implementation, exactly as for frequency and cpu invariance. It has to be enabled by the arch by defining arch_scale_max_freq_capacity to the actual implementation. Signed-off-by: Ionela Voinescu Signed-off-by: Dietmar Eggemann ( Fixed conflict with scaling against the PELT-based scale_rt_capacity ) Signed-off-by: Quentin Perret Change-Id: I770a8b1f4f7340e9e314f71c64a765bf880f4b4d --- kernel/sched/fair.c | 12 ++++++++---- kernel/sched/sched.h | 9 +++++++++ 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c index 2384841c5ceb..e945fa20ebbc 100644 --- a/kernel/sched/fair.c +++ b/kernel/sched/fair.c @@ -8337,10 +8337,9 @@ static inline int get_sd_load_idx(struct sched_domain *sd, return load_idx; } -static unsigned long scale_rt_capacity(struct sched_domain *sd, int cpu) +static unsigned long scale_rt_capacity(int cpu, unsigned long max) { struct rq *rq = cpu_rq(cpu); - unsigned long max = arch_scale_cpu_capacity(sd, cpu); unsigned long used, free; unsigned long irq; @@ -8362,10 +8361,15 @@ static unsigned long scale_rt_capacity(struct sched_domain *sd, int cpu) static void update_cpu_capacity(struct sched_domain *sd, int cpu) { - unsigned long capacity = scale_rt_capacity(sd, cpu); + unsigned long capacity = arch_scale_cpu_capacity(sd, cpu); struct sched_group *sdg = sd->groups; - cpu_rq(cpu)->cpu_capacity_orig = arch_scale_cpu_capacity(sd, cpu); + cpu_rq(cpu)->cpu_capacity_orig = capacity; + + capacity *= arch_scale_max_freq_capacity(sd, cpu); + capacity >>= SCHED_CAPACITY_SHIFT; + + capacity = scale_rt_capacity(cpu, capacity); if (!capacity) capacity = 1; diff --git a/kernel/sched/sched.h b/kernel/sched/sched.h index 86d155add21d..1ac8638b0aa4 100644 --- a/kernel/sched/sched.h +++ b/kernel/sched/sched.h @@ -1807,6 +1807,15 @@ unsigned long arch_scale_freq_capacity(int cpu) } #endif +#ifndef arch_scale_max_freq_capacity +struct sched_domain; +static __always_inline +unsigned long arch_scale_max_freq_capacity(struct sched_domain *sd, int cpu) +{ + return SCHED_CAPACITY_SCALE; +} +#endif + struct rq *__task_rq_lock(struct task_struct *p, struct rq_flags *rf) __acquires(rq->lock); From 571def6fd2ac99bb22a32bbd190b468d79f9df2e Mon Sep 17 00:00:00 2001 From: Dietmar Eggemann Date: Thu, 10 May 2018 16:52:33 +0100 Subject: [PATCH 0509/1103] ANDROID: implement max frequency capping Implements the Max Frequency Capping Engine (MFCE) getter function topology_get_max_freq_scale() to provide the scheduler with a maximum frequency scaling correction factor for more accurate cpu capacity handling by being able to deal with max frequency capping. This scaling factor describes the influence of running a cpu with a current maximum frequency (policy) lower than the maximum possible frequency (cpuinfo). The factor is: policy_max_freq(cpu) << SCHED_CAPACITY_SHIFT / cpuinfo_max_freq(cpu) It also implements the MFCE setter function arch_set_max_freq_scale() which is called from cpufreq_set_policy(). Signed-off-by: Ionela Voinescu Signed-off-by: Dietmar Eggemann [Trivial cherry-pick issue in cpufreq.c] Signed-off-by: Quentin Perret Change-Id: I59e52861ee260755ab0518fe1f7183a2e4e3d0fc --- drivers/base/arch_topology.c | 25 ++++++++++++++++++++++++- drivers/cpufreq/cpufreq.c | 8 ++++++++ include/linux/arch_topology.h | 8 ++++++++ include/linux/cpufreq.h | 2 ++ 4 files changed, 42 insertions(+), 1 deletion(-) diff --git a/drivers/base/arch_topology.c b/drivers/base/arch_topology.c index edfcf8d982e4..b5f61f2840d0 100644 --- a/drivers/base/arch_topology.c +++ b/drivers/base/arch_topology.c @@ -18,6 +18,8 @@ #include DEFINE_PER_CPU(unsigned long, freq_scale) = SCHED_CAPACITY_SCALE; +DEFINE_PER_CPU(unsigned long, max_cpu_freq); +DEFINE_PER_CPU(unsigned long, max_freq_scale) = SCHED_CAPACITY_SCALE; void arch_set_freq_scale(struct cpumask *cpus, unsigned long cur_freq, unsigned long max_freq) @@ -27,8 +29,29 @@ void arch_set_freq_scale(struct cpumask *cpus, unsigned long cur_freq, scale = (cur_freq << SCHED_CAPACITY_SHIFT) / max_freq; - for_each_cpu(i, cpus) + for_each_cpu(i, cpus) { per_cpu(freq_scale, i) = scale; + per_cpu(max_cpu_freq, i) = max_freq; + } +} + +void arch_set_max_freq_scale(struct cpumask *cpus, + unsigned long policy_max_freq) +{ + unsigned long scale, max_freq; + int cpu = cpumask_first(cpus); + + if (cpu > nr_cpu_ids) + return; + + max_freq = per_cpu(max_cpu_freq, cpu); + if (!max_freq) + return; + + scale = (policy_max_freq << SCHED_CAPACITY_SHIFT) / max_freq; + + for_each_cpu(cpu, cpus) + per_cpu(max_freq_scale, cpu) = scale; } static DEFINE_MUTEX(cpu_scale_mutex); diff --git a/drivers/cpufreq/cpufreq.c b/drivers/cpufreq/cpufreq.c index e1f8e760a5e8..b3c384ac6064 100644 --- a/drivers/cpufreq/cpufreq.c +++ b/drivers/cpufreq/cpufreq.c @@ -160,6 +160,12 @@ __weak void arch_set_freq_scale(struct cpumask *cpus, unsigned long cur_freq, } EXPORT_SYMBOL_GPL(arch_set_freq_scale); +__weak void arch_set_max_freq_scale(struct cpumask *cpus, + unsigned long policy_max_freq) +{ +} +EXPORT_SYMBOL_GPL(arch_set_max_freq_scale); + /* * This is a generic cpufreq init() routine which can be used by cpufreq * drivers of SMP systems. It will do following: @@ -2247,6 +2253,8 @@ static int cpufreq_set_policy(struct cpufreq_policy *policy, policy->max = new_policy->max; trace_cpu_frequency_limits(policy); + arch_set_max_freq_scale(policy->cpus, policy->max); + policy->cached_target_freq = UINT_MAX; pr_debug("new min and max freqs are %u - %u kHz\n", diff --git a/include/linux/arch_topology.h b/include/linux/arch_topology.h index d9bdc1a7f4e7..7e5a33ea8df0 100644 --- a/include/linux/arch_topology.h +++ b/include/linux/arch_topology.h @@ -33,4 +33,12 @@ unsigned long topology_get_freq_scale(int cpu) return per_cpu(freq_scale, cpu); } +DECLARE_PER_CPU(unsigned long, max_freq_scale); + +static inline +unsigned long topology_get_max_freq_scale(struct sched_domain *sd, int cpu) +{ + return per_cpu(max_freq_scale, cpu); +} + #endif /* _LINUX_ARCH_TOPOLOGY_H_ */ diff --git a/include/linux/cpufreq.h b/include/linux/cpufreq.h index 882a9b9e34bc..eb7b26f53f55 100644 --- a/include/linux/cpufreq.h +++ b/include/linux/cpufreq.h @@ -955,6 +955,8 @@ extern unsigned int arch_freq_get_on_cpu(int cpu); extern void arch_set_freq_scale(struct cpumask *cpus, unsigned long cur_freq, unsigned long max_freq); +extern void arch_set_max_freq_scale(struct cpumask *cpus, + unsigned long policy_max_freq); /* the following are really really optional */ extern struct freq_attr cpufreq_freq_attr_scaling_available_freqs; From ee9508844019c88af61ec029323c3751bec7277d Mon Sep 17 00:00:00 2001 From: Dietmar Eggemann Date: Thu, 10 May 2018 16:54:16 +0100 Subject: [PATCH 0510/1103] ANDROID: arm64: enable max frequency capping Defines arch_scale_max_freq_capacity() to use the topology driver scale function. Signed-off-by: Ionela Voinescu Signed-off-by: Dietmar Eggemann Signed-off-by: Quentin Perret Change-Id: If7565747ec862e42ac55196240522ef8d22ca67d --- arch/arm64/include/asm/topology.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/arch/arm64/include/asm/topology.h b/arch/arm64/include/asm/topology.h index 0524f2438649..8e0a96d71bd6 100644 --- a/arch/arm64/include/asm/topology.h +++ b/arch/arm64/include/asm/topology.h @@ -42,6 +42,9 @@ int pcibus_to_node(struct pci_bus *bus); /* Replace task scheduler's default frequency-invariant accounting */ #define arch_scale_freq_capacity topology_get_freq_scale +/* Replace task scheduler's default max-frequency-invariant accounting */ +#define arch_scale_max_freq_capacity topology_get_max_freq_scale + /* Replace task scheduler's default cpu-invariant accounting */ #define arch_scale_cpu_capacity topology_get_cpu_scale From 95f8d775af528a62319bc3612d3bd3140050f5af Mon Sep 17 00:00:00 2001 From: Dietmar Eggemann Date: Thu, 10 May 2018 16:58:04 +0100 Subject: [PATCH 0511/1103] ANDROID: arm: enable max frequency capping Defines arch_scale_max_freq_capacity() to use the topology driver scale function. Signed-off-by: Ionela Voinescu Signed-off-by: Dietmar Eggemann Signed-off-by: Quentin Perret Change-Id: I79f444399ea3b2948364fde80ccee52a9ece5b9a --- arch/arm/include/asm/topology.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/arch/arm/include/asm/topology.h b/arch/arm/include/asm/topology.h index 2a786f54d8b8..201dc2011c16 100644 --- a/arch/arm/include/asm/topology.h +++ b/arch/arm/include/asm/topology.h @@ -30,6 +30,9 @@ const struct cpumask *cpu_coregroup_mask(int cpu); /* Replace task scheduler's default frequency-invariant accounting */ #define arch_scale_freq_capacity topology_get_freq_scale +/* Replace task scheduler's default max-frequency-invariant accounting */ +#define arch_scale_max_freq_capacity topology_get_max_freq_scale + /* Replace task scheduler's default cpu-invariant accounting */ #define arch_scale_cpu_capacity topology_get_cpu_scale From 49cddd9b60eb22332d309cba95e950925836cb90 Mon Sep 17 00:00:00 2001 From: Dietmar Eggemann Date: Sat, 26 Sep 2015 18:19:54 +0100 Subject: [PATCH 0512/1103] ANDROID: sched: Update max cpu capacity in case of max frequency constraints Wakeup balancing uses cpu capacity awareness and needs to know the system-wide maximum cpu capacity. Patch "sched: Store system-wide maximum cpu capacity in root domain" finds the system-wide maximum cpu capacity during scheduler domain hierarchy setup. This is sufficient as long as maximum frequency invariance is not enabled. If it is enabled, the system-wide maximum cpu capacity can change between scheduler domain hierarchy setups due to frequency capping. The cpu capacity is changed in update_cpu_capacity() which is called in load balance on the lowest scheduler domain hierarchy level. To be able to know if a change in cpu capacity for a certain cpu also has an effect on the system-wide maximum cpu capacity it is normally necessary to iterate over all cpus. This would be way too costly. That's why this patch follows a different approach. The unsigned long max_cpu_capacity value in struct root_domain is replaced with a struct max_cpu_capacity, containing value (the max_cpu_capacity) and cpu (the cpu index of the cpu providing the maximum cpu_capacity). Changes to the system-wide maximum cpu capacity and the cpu index are made if: 1 System-wide maximum cpu capacity < cpu capacity 2 System-wide maximum cpu capacity > cpu capacity and cpu index == cpu There are no changes to the system-wide maximum cpu capacity in all other cases. Atomic read and write access to the pair (max_cpu_capacity.val, max_cpu_capacity.cpu) is enforced by max_cpu_capacity.lock. The access to max_cpu_capacity.val in task_fits_max() is still performed without taking the max_cpu_capacity.lock. The code to set max cpu capacity in build_sched_domains() has been removed because the whole functionality is now provided by update_cpu_capacity() instead. This approach can introduce errors temporarily, e.g. in case the cpu currently providing the max cpu capacity has its cpu capacity lowered due to frequency capping and calls update_cpu_capacity() before any cpu which might provide the max cpu now. Signed-off-by: Ionela Voinescu * Signed-off-by: Dietmar Eggemann ( Fixed cherry-pick issues ) Signed-off-by: Quentin Perret Change-Id: Idaa7a16723001e222e476de34df332558e48dd13 --- kernel/sched/fair.c | 31 ++++++++++++++++++++++++++++++- kernel/sched/sched.h | 10 +++++++++- kernel/sched/topology.c | 15 +++------------ 3 files changed, 42 insertions(+), 14 deletions(-) diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c index e945fa20ebbc..9c98447b5158 100644 --- a/kernel/sched/fair.c +++ b/kernel/sched/fair.c @@ -6772,7 +6772,7 @@ static int wake_cap(struct task_struct *p, int cpu, int prev_cpu) return 0; min_cap = min(capacity_orig_of(prev_cpu), capacity_orig_of(cpu)); - max_cap = cpu_rq(cpu)->rd->max_cpu_capacity; + max_cap = cpu_rq(cpu)->rd->max_cpu_capacity.val; /* Minimum capacity is close to max, no need to abort wake_affine */ if (max_cap - min_cap < max_cap >> 3) @@ -8359,16 +8359,45 @@ static unsigned long scale_rt_capacity(int cpu, unsigned long max) return scale_irq_capacity(free, irq, max); } +void init_max_cpu_capacity(struct max_cpu_capacity *mcc) { + raw_spin_lock_init(&mcc->lock); + mcc->val = 0; + mcc->cpu = -1; +} + static void update_cpu_capacity(struct sched_domain *sd, int cpu) { unsigned long capacity = arch_scale_cpu_capacity(sd, cpu); struct sched_group *sdg = sd->groups; + struct max_cpu_capacity *mcc; + unsigned long max_capacity; + int max_cap_cpu; + unsigned long flags; cpu_rq(cpu)->cpu_capacity_orig = capacity; capacity *= arch_scale_max_freq_capacity(sd, cpu); capacity >>= SCHED_CAPACITY_SHIFT; + mcc = &cpu_rq(cpu)->rd->max_cpu_capacity; + + raw_spin_lock_irqsave(&mcc->lock, flags); + max_capacity = mcc->val; + max_cap_cpu = mcc->cpu; + + if ((max_capacity > capacity && max_cap_cpu == cpu) || + (max_capacity < capacity)) { + mcc->val = capacity; + mcc->cpu = cpu; +#ifdef CONFIG_SCHED_DEBUG + raw_spin_unlock_irqrestore(&mcc->lock, flags); + pr_info("CPU%d: update max cpu_capacity %lu\n", cpu, capacity); + goto skip_unlock; +#endif + } + raw_spin_unlock_irqrestore(&mcc->lock, flags); + +skip_unlock: __attribute__ ((unused)); capacity = scale_rt_capacity(cpu, capacity); if (!capacity) diff --git a/kernel/sched/sched.h b/kernel/sched/sched.h index 1ac8638b0aa4..bc61dee4d0e1 100644 --- a/kernel/sched/sched.h +++ b/kernel/sched/sched.h @@ -709,6 +709,12 @@ struct perf_domain { struct rcu_head rcu; }; +struct max_cpu_capacity { + raw_spinlock_t lock; + unsigned long val; + int cpu; +}; + /* Scheduling group status flags */ #define SG_OVERLOAD 0x1 /* More than one runnable task on a CPU. */ #define SG_OVERUTILIZED 0x2 /* One or more CPUs are over-utilized. */ @@ -767,7 +773,8 @@ struct root_domain { cpumask_var_t rto_mask; struct cpupri cpupri; - unsigned long max_cpu_capacity; + /* Maximum cpu capacity in the system. */ + struct max_cpu_capacity max_cpu_capacity; /* * NULL-terminated list of performance domains intersecting with the @@ -780,6 +787,7 @@ extern struct root_domain def_root_domain; extern struct mutex sched_domains_mutex; extern void init_defrootdomain(void); +extern void init_max_cpu_capacity(struct max_cpu_capacity *mcc); extern int sched_init_domains(const struct cpumask *cpu_map); extern void rq_attach_root(struct rq *rq, struct root_domain *rd); extern void sched_get_rd(struct root_domain *rd); diff --git a/kernel/sched/topology.c b/kernel/sched/topology.c index 8f7baceb150d..7f194a3a3523 100644 --- a/kernel/sched/topology.c +++ b/kernel/sched/topology.c @@ -461,6 +461,9 @@ static int init_rootdomain(struct root_domain *rd) if (cpupri_init(&rd->cpupri) != 0) goto free_cpudl; + + init_max_cpu_capacity(&rd->max_cpu_capacity); + return 0; free_cpudl: @@ -1887,7 +1890,6 @@ build_sched_domains(const struct cpumask *cpu_map, struct sched_domain_attr *att enum s_alloc alloc_state; struct sched_domain *sd; struct s_data d; - struct rq *rq = NULL; int i, ret = -ENOMEM; struct sched_domain_topology_level *tl_asym; bool has_asym = false; @@ -1950,13 +1952,7 @@ build_sched_domains(const struct cpumask *cpu_map, struct sched_domain_attr *att /* Attach the domains */ rcu_read_lock(); for_each_cpu(i, cpu_map) { - rq = cpu_rq(i); sd = *per_cpu_ptr(d.sd, i); - - /* Use READ_ONCE()/WRITE_ONCE() to avoid load/store tearing: */ - if (rq->cpu_capacity_orig > READ_ONCE(d.rd->max_cpu_capacity)) - WRITE_ONCE(d.rd->max_cpu_capacity, rq->cpu_capacity_orig); - cpu_attach_domain(sd, d.rd, i); } rcu_read_unlock(); @@ -1964,11 +1960,6 @@ build_sched_domains(const struct cpumask *cpu_map, struct sched_domain_attr *att if (has_asym) static_branch_enable_cpuslocked(&sched_asym_cpucapacity); - if (rq && sched_debug_enabled) { - pr_info("root domain span: %*pbl (max cpu_capacity = %lu)\n", - cpumask_pr_args(cpu_map), rq->rd->max_cpu_capacity); - } - ret = 0; error: __free_domain_allocs(&d, alloc_state, cpu_map); From d10739647289ba040854a2336eeaba311f475aa9 Mon Sep 17 00:00:00 2001 From: Quentin Perret Date: Tue, 10 Jul 2018 15:44:09 +0100 Subject: [PATCH 0513/1103] ANDROID: cpufreq/schedutil: Select frequency using util_avg for RT Schedutil always requests max frequency whenever a RT task is running. Now that we have a better estimate of the utilization of RT runqueues, it is possible to make a less conservative decision and scale frequency according to the needs of the RT tasks. To do so, protect the RT-go-to-max code with a new sched_feature. The sched_feature is disabled by default, hence favoring energy savings as required in mobile environments. Signed-off-by: Quentin Perret Change-Id: Ic9f01c8703d4f843addaa0d684012a422fe9f3b8 --- kernel/sched/cpufreq_schedutil.c | 3 ++- kernel/sched/features.h | 5 +++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/kernel/sched/cpufreq_schedutil.c b/kernel/sched/cpufreq_schedutil.c index 645f54a0c2dc..b7bc4fe481fe 100644 --- a/kernel/sched/cpufreq_schedutil.c +++ b/kernel/sched/cpufreq_schedutil.c @@ -206,7 +206,8 @@ unsigned long schedutil_freq_util(int cpu, unsigned long util_cfs, max = arch_scale_cpu_capacity(NULL, cpu); - if (type == FREQUENCY_UTIL && rt_rq_is_runnable(&rq->rt)) + if (sched_feat(SUGOV_RT_MAX_FREQ) && type == FREQUENCY_UTIL && + rt_rq_is_runnable(&rq->rt)) return max; /* diff --git a/kernel/sched/features.h b/kernel/sched/features.h index 2c33917b2067..fb995c5f69ba 100644 --- a/kernel/sched/features.h +++ b/kernel/sched/features.h @@ -111,3 +111,8 @@ SCHED_FEAT(FIND_BEST_TARGET, true) * those tasks through the mainline slow path. */ SCHED_FEAT(EAS_PREFER_IDLE, true) + +/* + * Request max frequency from schedutil whenever a RT task is running. + */ +SCHED_FEAT(SUGOV_RT_MAX_FREQ, false) From e012aa99ffe961d09a41f8eea70c1195bfd849ef Mon Sep 17 00:00:00 2001 From: Steve Muckle Date: Thu, 17 Nov 2016 10:48:45 +0530 Subject: [PATCH 0514/1103] ANDROID: cpufreq/schedutil: add up/down frequency transition rate limits The rate-limit tunable in the schedutil governor applies to transitions to both lower and higher frequencies. On several platforms it is not the ideal tunable though, as it is difficult to get best power/performance figures using the same limit in both directions. It is common on mobile platforms with demanding user interfaces to want to increase frequency rapidly for example but decrease slowly. One of the example can be a case where we have short busy periods followed by similar or longer idle periods. If we keep the rate-limit high enough, we will not go to higher frequencies soon enough. On the other hand, if we keep it too low, we will have too many frequency transitions, as we will always reduce the frequency after the busy period. It would be very useful if we can set low rate-limit while increasing the frequency (so that we can respond to the short busy periods quickly) and high rate-limit while decreasing frequency (so that we don't reduce the frequency immediately after the short busy period and that may avoid frequency transitions before the next busy period). Implement separate up/down transition rate limits. Note that the governor avoids frequency recalculations for a period equal to minimum of up and down rate-limit. A global mutex is also defined to protect updates to min_rate_limit_us via two separate sysfs files. Note that this wouldn't change behavior of the schedutil governor for the platforms which wish to keep same values for both up and down rate limits. This is tested with the rt-app [1] on ARM Exynos, dual A15 processor platform. Testcase: Run a SCHED_OTHER thread on CPU0 which will emulate work-load for X ms of busy period out of the total period of Y ms, i.e. Y - X ms of idle period. The values of X/Y taken were: 20/40, 20/50, 20/70, i.e idle periods of 20, 30 and 50 ms respectively. These were tested against values of up/down rate limits as: 10/10 ms and 10/40 ms. For every test we noticed a performance increase of 5-10% with the schedutil governor, which was very much expected. [Viresh]: Simplified user interface and introduced min_rate_limit_us + mutex, rewrote commit log and included test results. [1] https://github.com/scheduler-tools/rt-app/ Signed-off-by: Steve Muckle Signed-off-by: Viresh Kumar (applied from https://marc.info/?l=linux-kernel&m=147936011103832&w=2) [trivial adaptations] Signed-off-by: Juri Lelli [updated rate limiting & fixed conflicts] Signed-off-by: Chris Redpath (cherry picked from commit 50c26fdb74563ec0cb4a83373d42667f4e83a23e) [Trivial cherry-pick conflicts] Signed-off-by: Quentin Perret Change-Id: I18720a83855b196b8e21dcdc8deae79131635b84 --- kernel/sched/cpufreq_schedutil.c | 103 ++++++++++++++++++++++++++----- 1 file changed, 89 insertions(+), 14 deletions(-) diff --git a/kernel/sched/cpufreq_schedutil.c b/kernel/sched/cpufreq_schedutil.c index b7bc4fe481fe..6704b2b3cb41 100644 --- a/kernel/sched/cpufreq_schedutil.c +++ b/kernel/sched/cpufreq_schedutil.c @@ -18,7 +18,8 @@ struct sugov_tunables { struct gov_attr_set attr_set; - unsigned int rate_limit_us; + unsigned int up_rate_limit_us; + unsigned int down_rate_limit_us; }; struct sugov_policy { @@ -29,7 +30,9 @@ struct sugov_policy { raw_spinlock_t update_lock; /* For shared policies */ u64 last_freq_update_time; - s64 freq_update_delay_ns; + s64 min_rate_limit_ns; + s64 up_rate_delay_ns; + s64 down_rate_delay_ns; unsigned int next_freq; unsigned int cached_raw_freq; @@ -94,9 +97,32 @@ static bool sugov_should_update_freq(struct sugov_policy *sg_policy, u64 time) if (unlikely(sg_policy->need_freq_update)) return true; + /* No need to recalculate next freq for min_rate_limit_us + * at least. However we might still decide to further rate + * limit once frequency change direction is decided, according + * to the separate rate limits. + */ + delta_ns = time - sg_policy->last_freq_update_time; + return delta_ns >= sg_policy->min_rate_limit_ns; +} + +static bool sugov_up_down_rate_limit(struct sugov_policy *sg_policy, u64 time, + unsigned int next_freq) +{ + s64 delta_ns; + + delta_ns = time - sg_policy->last_freq_update_time; + + if (next_freq > sg_policy->next_freq && + delta_ns < sg_policy->up_rate_delay_ns) + return true; - return delta_ns >= sg_policy->freq_update_delay_ns; + if (next_freq < sg_policy->next_freq && + delta_ns < sg_policy->down_rate_delay_ns) + return true; + + return false; } static bool sugov_update_next_freq(struct sugov_policy *sg_policy, u64 time, @@ -105,6 +131,9 @@ static bool sugov_update_next_freq(struct sugov_policy *sg_policy, u64 time, if (sg_policy->next_freq == next_freq) return false; + if (sugov_up_down_rate_limit(sg_policy, time, next_freq)) + return false; + sg_policy->next_freq = next_freq; sg_policy->last_freq_update_time = time; @@ -594,15 +623,52 @@ static inline struct sugov_tunables *to_sugov_tunables(struct gov_attr_set *attr return container_of(attr_set, struct sugov_tunables, attr_set); } -static ssize_t rate_limit_us_show(struct gov_attr_set *attr_set, char *buf) +static DEFINE_MUTEX(min_rate_lock); + +static void update_min_rate_limit_ns(struct sugov_policy *sg_policy) +{ + mutex_lock(&min_rate_lock); + sg_policy->min_rate_limit_ns = min(sg_policy->up_rate_delay_ns, + sg_policy->down_rate_delay_ns); + mutex_unlock(&min_rate_lock); +} + +static ssize_t up_rate_limit_us_show(struct gov_attr_set *attr_set, char *buf) +{ + struct sugov_tunables *tunables = to_sugov_tunables(attr_set); + + return sprintf(buf, "%u\n", tunables->up_rate_limit_us); +} + +static ssize_t down_rate_limit_us_show(struct gov_attr_set *attr_set, char *buf) { struct sugov_tunables *tunables = to_sugov_tunables(attr_set); - return sprintf(buf, "%u\n", tunables->rate_limit_us); + return sprintf(buf, "%u\n", tunables->down_rate_limit_us); +} + +static ssize_t up_rate_limit_us_store(struct gov_attr_set *attr_set, + const char *buf, size_t count) +{ + struct sugov_tunables *tunables = to_sugov_tunables(attr_set); + struct sugov_policy *sg_policy; + unsigned int rate_limit_us; + + if (kstrtouint(buf, 10, &rate_limit_us)) + return -EINVAL; + + tunables->up_rate_limit_us = rate_limit_us; + + list_for_each_entry(sg_policy, &attr_set->policy_list, tunables_hook) { + sg_policy->up_rate_delay_ns = rate_limit_us * NSEC_PER_USEC; + update_min_rate_limit_ns(sg_policy); + } + + return count; } -static ssize_t -rate_limit_us_store(struct gov_attr_set *attr_set, const char *buf, size_t count) +static ssize_t down_rate_limit_us_store(struct gov_attr_set *attr_set, + const char *buf, size_t count) { struct sugov_tunables *tunables = to_sugov_tunables(attr_set); struct sugov_policy *sg_policy; @@ -611,18 +677,22 @@ rate_limit_us_store(struct gov_attr_set *attr_set, const char *buf, size_t count if (kstrtouint(buf, 10, &rate_limit_us)) return -EINVAL; - tunables->rate_limit_us = rate_limit_us; + tunables->down_rate_limit_us = rate_limit_us; - list_for_each_entry(sg_policy, &attr_set->policy_list, tunables_hook) - sg_policy->freq_update_delay_ns = rate_limit_us * NSEC_PER_USEC; + list_for_each_entry(sg_policy, &attr_set->policy_list, tunables_hook) { + sg_policy->down_rate_delay_ns = rate_limit_us * NSEC_PER_USEC; + update_min_rate_limit_ns(sg_policy); + } return count; } -static struct governor_attr rate_limit_us = __ATTR_RW(rate_limit_us); +static struct governor_attr up_rate_limit_us = __ATTR_RW(up_rate_limit_us); +static struct governor_attr down_rate_limit_us = __ATTR_RW(down_rate_limit_us); static struct attribute *sugov_attributes[] = { - &rate_limit_us.attr, + &up_rate_limit_us.attr, + &down_rate_limit_us.attr, NULL }; @@ -778,7 +848,8 @@ static int sugov_init(struct cpufreq_policy *policy) goto stop_kthread; } - tunables->rate_limit_us = cpufreq_policy_transition_delay_us(policy); + tunables->up_rate_limit_us = cpufreq_policy_transition_delay_us(policy); + tunables->down_rate_limit_us = cpufreq_policy_transition_delay_us(policy); policy->governor_data = sg_policy; sg_policy->tunables = tunables; @@ -836,7 +907,11 @@ static int sugov_start(struct cpufreq_policy *policy) struct sugov_policy *sg_policy = policy->governor_data; unsigned int cpu; - sg_policy->freq_update_delay_ns = sg_policy->tunables->rate_limit_us * NSEC_PER_USEC; + sg_policy->up_rate_delay_ns = + sg_policy->tunables->up_rate_limit_us * NSEC_PER_USEC; + sg_policy->down_rate_delay_ns = + sg_policy->tunables->down_rate_limit_us * NSEC_PER_USEC; + update_min_rate_limit_ns(sg_policy); sg_policy->last_freq_update_time = 0; sg_policy->next_freq = 0; sg_policy->work_in_progress = false; From 1e3c1ae533e027ce5cd8e3d53e47ce677844b5a7 Mon Sep 17 00:00:00 2001 From: Chris Redpath Date: Fri, 1 Jun 2018 20:34:10 +0100 Subject: [PATCH 0515/1103] ANDROID: sched/fair: Attempt to improve throughput for asym cap systems In some systems the capacity and group weights line up to defeat all the small imbalance correction conditions in fix_small_imbalance, which can cause bad task placement. Add a new condition if the existing code can't see anything to fix: If we have asymmetric capacity, and there are more tasks than CPUs in the busiest group *and* there are less tasks than CPUs in the local group then we try to pull something. There could be transient small tasks which prevent this from working, but on the whole it is beneficial for those systems with inconvenient capacity/cluster size relationships. Signed-off-by: Chris Redpath Change-Id: Icf81cde215c082a61f816534b7990ccb70aee409 --- kernel/sched/fair.c | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c index 9c98447b5158..952b210f276f 100644 --- a/kernel/sched/fair.c +++ b/kernel/sched/fair.c @@ -9033,7 +9033,22 @@ void fix_small_imbalance(struct lb_env *env, struct sd_lb_stats *sds) capa_move /= SCHED_CAPACITY_SCALE; /* Move if we gain throughput */ - if (capa_move > capa_now) + if (capa_move > capa_now) { + env->imbalance = busiest->load_per_task; + return; + } + + /* We can't see throughput improvement with the load-based + * method, but it is possible depending upon group size and + * capacity range that there might still be an underutilized + * cpu available in an asymmetric capacity system. Do one last + * check just in case. + */ + if (env->sd->flags & SD_ASYM_CPUCAPACITY && + busiest->group_type == group_overloaded && + busiest->sum_nr_running > busiest->group_weight && + local->sum_nr_running < local->group_weight && + local->group_capacity < busiest->group_capacity) env->imbalance = busiest->load_per_task; } From e8dbb65c72fa359b8ad536c69bd46fc3be78a294 Mon Sep 17 00:00:00 2001 From: Chris Redpath Date: Tue, 5 Jun 2018 11:47:57 +0100 Subject: [PATCH 0516/1103] ANDROID: sched/fair: Don't balance misfits if it would overload local group When load balancing in a system with misfit tasks present, if we always pull a misfit task to the local group this can lead to pulling a running task from a smaller capacity CPUs to a bigger CPU which is busy. In this situation, the pulled task is likely not to get a chance to run before an idle balance on another small CPU pulls it back. This penalises the pulled task as it is stopped for a short amount of time and then likely relocated to a different CPU (since the original CPU just did a NEWLY_IDLE balance and reset the periodic interval). If we only do this unconditionally for NEWLY_IDLE balance, we can be sure that any tasks and load which are present on the local group are related to short-running tasks which we are happy to displace for a longer running task in a system with misfit tasks present. However, other balance types should only pull a task if we think that the local group is underutilized - checking the number of tasks gives us a conservative estimate here since if they were short tasks we would have been doing NEWLY_IDLE balances instead. Signed-off-by: Chris Redpath Change-Id: I710add1ab1139482620b6addc8370ad194791beb --- kernel/sched/fair.c | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c index 952b210f276f..04b198e5de4b 100644 --- a/kernel/sched/fair.c +++ b/kernel/sched/fair.c @@ -9117,8 +9117,18 @@ static inline void calculate_imbalance(struct lb_env *env, struct sd_lb_stats *s (sds->avg_load - local->avg_load) * local->group_capacity ) / SCHED_CAPACITY_SCALE; - /* Boost imbalance to allow misfit task to be balanced. */ - if (busiest->group_type == group_misfit_task) { + /* Boost imbalance to allow misfit task to be balanced. + * Always do this if we are doing a NEWLY_IDLE balance + * on the assumption that any tasks we have must not be + * long-running (and hence we cannot rely upon load). + * However if we are not idle, we should assume the tasks + * we have are longer running and not override load-based + * calculations above unless we are sure that the local + * group is underutilized. + */ + if (busiest->group_type == group_misfit_task && + (env->idle == CPU_NEWLY_IDLE || + local->sum_nr_running < local->group_weight)) { env->imbalance = max_t(long, env->imbalance, busiest->group_misfit_task_load); } From 3f6710faa63ce169dc763c97d40ae8e0641c5e46 Mon Sep 17 00:00:00 2001 From: Chris Redpath Date: Tue, 5 Jun 2018 12:21:33 +0100 Subject: [PATCH 0517/1103] ANDROID: sched/fair: Also do misfit in overloaded groups If we can classify the group as overloaded, that overrides any classification as misfit but we may still have misfit tasks present. Check the rq we're looking at to see if this is the case. Signed-off-by: Chris Redpath [Removed stray reference to rq_has_misfit] Signed-off-by: Valentin Schneider Change-Id: Ida8eb66aa625e34de3fe2ee1b0dd8a78926273d8 --- kernel/sched/fair.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c index 04b198e5de4b..f9f2024affba 100644 --- a/kernel/sched/fair.c +++ b/kernel/sched/fair.c @@ -9399,6 +9399,10 @@ static int need_active_balance(struct lb_env *env) return 1; } + if (env->src_grp_type == group_overloaded && env->src_rq->misfit_task_load) + return 1; + + return unlikely(sd->nr_balance_failed > sd->cache_nice_tries+2); } From ff8569bb62e14b5f74f84be069b9467d6c88d967 Mon Sep 17 00:00:00 2001 From: Dietmar Eggemann Date: Fri, 17 Mar 2017 19:09:03 +0000 Subject: [PATCH 0518/1103] ANDROID: sched/autogroup: Define autogroup_path() for !CONFIG_SCHED_DEBUG Define autogroup_path() even in the !CONFIG_SCHED_DEBUG case. If CONFIG_SCHED_AUTOGROUP is enabled the path of an autogroup has to be available to be printed in the load tracking trace events provided by this patch-stack regardless whether CONFIG_SCHED_DEBUG is set or not. Signed-off-by: Dietmar Eggemann Cc: Peter Zijlstra Cc: Ingo Molnar Change-Id: Iecf5466fc837f428ee545ddabe70024c152ff38d --- kernel/sched/autogroup.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/kernel/sched/autogroup.c b/kernel/sched/autogroup.c index 2d4ff5353ded..2067080bb235 100644 --- a/kernel/sched/autogroup.c +++ b/kernel/sched/autogroup.c @@ -259,7 +259,6 @@ void proc_sched_autogroup_show_task(struct task_struct *p, struct seq_file *m) } #endif /* CONFIG_PROC_FS */ -#ifdef CONFIG_SCHED_DEBUG int autogroup_path(struct task_group *tg, char *buf, int buflen) { if (!task_group_is_autogroup(tg)) @@ -267,4 +266,3 @@ int autogroup_path(struct task_group *tg, char *buf, int buflen) return snprintf(buf, buflen, "%s-%ld", "/autogroup", tg->autogroup->id); } -#endif From 44afc22e6a070d90c9ff747a945e559ddb0e3ea1 Mon Sep 17 00:00:00 2001 From: Dietmar Eggemann Date: Fri, 17 Mar 2017 20:27:06 +0000 Subject: [PATCH 0519/1103] ANDROID: sched/events: Introduce cfs_rq load tracking trace event The following trace event keys are mapped to: (1) load : cfs_rq->avg.load_avg (2) rbl_load : cfs_rq->avg.runnable_load_avg (2) util : cfs_rq->avg.util_avg To let this trace event work for configurations w/ and w/o group scheduling support for cfs (CONFIG_FAIR_GROUP_SCHED) the following special handling is necessary for a non-existent key=value pair: path = "(null)" : In case of !CONFIG_FAIR_GROUP_SCHED. The following list shows examples of the key=value pairs in different configurations for: (1) a root task_group: cpu=4 path=/ load=6 rbl_load=6 util=331 (2) a task_group: cpu=1 path=/tg1/tg11/tg111 load=538 rbl_load=538 util=522 (3) an autogroup: cpu=3 path=/autogroup-18 load=997 rbl_load=997 util=517 (4) w/o CONFIG_FAIR_GROUP_SCHED: cpu=0 path=(null) load=314 rbl_load=314 util=289 The trace event is only defined for CONFIG_SMP. The helper function __trace_sched_path() can be used to get the length parameter of the dynamic array (path == NULL) and to copy the path into it (path != NULL). Signed-off-by: Dietmar Eggemann Cc: Peter Zijlstra Cc: Ingo Molnar Cc: Steven Rostedt [ Fixed issues related to the new pelt.c file ] Signed-off-by: Quentin Perret Change-Id: I1107044c52b74ecb3df69f3a45c1e530f0e59b1b --- include/trace/events/sched.h | 66 ++++++++++++++++++++++++++++++++++++ kernel/sched/fair.c | 6 ++++ kernel/sched/pelt.c | 5 +++ 3 files changed, 77 insertions(+) diff --git a/include/trace/events/sched.h b/include/trace/events/sched.h index 4a68273b29d5..6bcb1853f889 100644 --- a/include/trace/events/sched.h +++ b/include/trace/events/sched.h @@ -596,6 +596,72 @@ TRACE_EVENT(sched_wake_idle_without_ipi, TP_printk("cpu=%d", __entry->cpu) ); + +#ifdef CONFIG_SMP +#ifdef CREATE_TRACE_POINTS +static inline +int __trace_sched_cpu(struct cfs_rq *cfs_rq) +{ +#ifdef CONFIG_FAIR_GROUP_SCHED + struct rq *rq = cfs_rq->rq; +#else + struct rq *rq = container_of(cfs_rq, struct rq, cfs); +#endif + return cpu_of(rq); +} + +static inline +int __trace_sched_path(struct cfs_rq *cfs_rq, char *path, int len) +{ +#ifdef CONFIG_FAIR_GROUP_SCHED + int l = path ? len : 0; + + if (task_group_is_autogroup(cfs_rq->tg)) + return autogroup_path(cfs_rq->tg, path, l) + 1; + else + return cgroup_path(cfs_rq->tg->css.cgroup, path, l) + 1; +#else + if (path) + strcpy(path, "(null)"); + + return strlen("(null)"); +#endif +} + +#endif /* CREATE_TRACE_POINTS */ + +/* + * Tracepoint for cfs_rq load tracking: + */ +TRACE_EVENT(sched_load_cfs_rq, + + TP_PROTO(struct cfs_rq *cfs_rq), + + TP_ARGS(cfs_rq), + + TP_STRUCT__entry( + __field( int, cpu ) + __dynamic_array(char, path, + __trace_sched_path(cfs_rq, NULL, 0) ) + __field( unsigned long, load ) + __field( unsigned long, rbl_load ) + __field( unsigned long, util ) + ), + + TP_fast_assign( + __entry->cpu = __trace_sched_cpu(cfs_rq); + __trace_sched_path(cfs_rq, __get_dynamic_array(path), + __get_dynamic_array_len(path)); + __entry->load = cfs_rq->avg.load_avg; + __entry->rbl_load = cfs_rq->avg.runnable_load_avg; + __entry->util = cfs_rq->avg.util_avg; + ), + + TP_printk("cpu=%d path=%s load=%lu rbl_load=%lu util=%lu", + __entry->cpu, __get_str(path), __entry->load, + __entry->rbl_load,__entry->util) +); +#endif /* CONFIG_SMP */ #endif /* _TRACE_SCHED_H */ /* This part must be outside protection */ diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c index f9f2024affba..a4792f1dfe3a 100644 --- a/kernel/sched/fair.c +++ b/kernel/sched/fair.c @@ -3339,6 +3339,8 @@ static inline int propagate_entity_load_avg(struct sched_entity *se) update_tg_cfs_util(cfs_rq, se, gcfs_rq); update_tg_cfs_runnable(cfs_rq, se, gcfs_rq); + trace_sched_load_cfs_rq(cfs_rq); + return 1; } @@ -3491,6 +3493,8 @@ static void attach_entity_load_avg(struct cfs_rq *cfs_rq, struct sched_entity *s add_tg_cfs_propagate(cfs_rq, se->avg.load_sum); cfs_rq_util_change(cfs_rq, flags); + + trace_sched_load_cfs_rq(cfs_rq); } /** @@ -3510,6 +3514,8 @@ static void detach_entity_load_avg(struct cfs_rq *cfs_rq, struct sched_entity *s add_tg_cfs_propagate(cfs_rq, -se->avg.load_sum); cfs_rq_util_change(cfs_rq, 0); + + trace_sched_load_cfs_rq(cfs_rq); } /* diff --git a/kernel/sched/pelt.c b/kernel/sched/pelt.c index 35475c0c5419..f042ee0f7b08 100644 --- a/kernel/sched/pelt.c +++ b/kernel/sched/pelt.c @@ -29,6 +29,8 @@ #include "sched-pelt.h" #include "pelt.h" +#include + /* * Approximate: * val * y^n, where y^32 ~= 0.5 (~1 scheduling period) @@ -304,6 +306,9 @@ int __update_load_avg_cfs_rq(u64 now, int cpu, struct cfs_rq *cfs_rq) cfs_rq->curr != NULL)) { ___update_load_avg(&cfs_rq->avg, 1, 1); + + trace_sched_load_cfs_rq(cfs_rq); + return 1; } From 2f99f21aa82feba2b4bd6ba4ee916659ec10f416 Mon Sep 17 00:00:00 2001 From: Dietmar Eggemann Date: Mon, 20 Mar 2017 17:26:47 +0000 Subject: [PATCH 0520/1103] ANDROID: sched/events: Introduce sched_entity load tracking trace event The following trace event keys are mapped to: (1) load : se->avg.load_avg (2) rbl_load : se->avg.runnable_load_avg (3) util : se->avg.util_avg To let this trace event work for configurations w/ and w/o group scheduling support for cfs (CONFIG_FAIR_GROUP_SCHED) the following special handling is necessary for non-existent key=value pairs: path = "(null)" : In case of !CONFIG_FAIR_GROUP_SCHED or the sched_entity represents a task. comm = "(null)" : In case sched_entity represents a task_group. pid = -1 : In case sched_entity represents a task_group. The following list shows examples of the key=value pairs in different configurations for: (1) a task: cpu=0 path=(null) comm=sshd pid=2206 load=102 rbl_load=102 util=102 (2) a taskgroup: cpu=1 path=/tg1/tg11/tg111 comm=(null) pid=-1 load=882 rbl_load=882 util=510 (3) an autogroup: cpu=0 path=/autogroup-13 comm=(null) pid=-1 load=49 rbl_load=49 util=48 (4) w/o CONFIG_FAIR_GROUP_SCHED: cpu=0 path=(null) comm=sshd pid=2211 load=301 rbl_load=301 util=265 The trace event is only defined for CONFIG_SMP. The helper functions __trace_sched_cpu(), __trace_sched_path() and __trace_sched_id() are extended to deal with sched_entities as well. Signed-off-by: Dietmar Eggemann Cc: Peter Zijlstra Cc: Ingo Molnar Cc: Steven Rostedt [ Fixed issues related to the new pelt.c file ] Signed-off-by: Quentin Perret Change-Id: Id2e4d1ddb79c13412c80e4fa4147b9df3b1e212a --- include/trace/events/sched.h | 67 +++++++++++++++++++++++++++++++----- kernel/sched/fair.c | 1 + kernel/sched/pelt.c | 6 ++++ 3 files changed, 65 insertions(+), 9 deletions(-) diff --git a/include/trace/events/sched.h b/include/trace/events/sched.h index 6bcb1853f889..96092f803c34 100644 --- a/include/trace/events/sched.h +++ b/include/trace/events/sched.h @@ -600,14 +600,15 @@ TRACE_EVENT(sched_wake_idle_without_ipi, #ifdef CONFIG_SMP #ifdef CREATE_TRACE_POINTS static inline -int __trace_sched_cpu(struct cfs_rq *cfs_rq) +int __trace_sched_cpu(struct cfs_rq *cfs_rq, struct sched_entity *se) { #ifdef CONFIG_FAIR_GROUP_SCHED - struct rq *rq = cfs_rq->rq; + struct rq *rq = cfs_rq ? cfs_rq->rq : NULL; #else - struct rq *rq = container_of(cfs_rq, struct rq, cfs); + struct rq *rq = cfs_rq ? container_of(cfs_rq, struct rq, cfs) : NULL; #endif - return cpu_of(rq); + return rq ? cpu_of(rq) + : task_cpu((container_of(se, struct task_struct, se))); } static inline @@ -616,18 +617,26 @@ int __trace_sched_path(struct cfs_rq *cfs_rq, char *path, int len) #ifdef CONFIG_FAIR_GROUP_SCHED int l = path ? len : 0; - if (task_group_is_autogroup(cfs_rq->tg)) + if (cfs_rq && task_group_is_autogroup(cfs_rq->tg)) return autogroup_path(cfs_rq->tg, path, l) + 1; - else + else if (cfs_rq && cfs_rq->tg->css.cgroup) return cgroup_path(cfs_rq->tg->css.cgroup, path, l) + 1; -#else +#endif if (path) strcpy(path, "(null)"); return strlen("(null)"); -#endif } +static inline +struct cfs_rq *__trace_sched_group_cfs_rq(struct sched_entity *se) +{ +#ifdef CONFIG_FAIR_GROUP_SCHED + return se->my_q; +#else + return NULL; +#endif +} #endif /* CREATE_TRACE_POINTS */ /* @@ -649,7 +658,7 @@ TRACE_EVENT(sched_load_cfs_rq, ), TP_fast_assign( - __entry->cpu = __trace_sched_cpu(cfs_rq); + __entry->cpu = __trace_sched_cpu(cfs_rq, NULL); __trace_sched_path(cfs_rq, __get_dynamic_array(path), __get_dynamic_array_len(path)); __entry->load = cfs_rq->avg.load_avg; @@ -661,6 +670,46 @@ TRACE_EVENT(sched_load_cfs_rq, __entry->cpu, __get_str(path), __entry->load, __entry->rbl_load,__entry->util) ); + +/* + * Tracepoint for sched_entity load tracking: + */ +TRACE_EVENT(sched_load_se, + + TP_PROTO(struct sched_entity *se), + + TP_ARGS(se), + + TP_STRUCT__entry( + __field( int, cpu ) + __dynamic_array(char, path, + __trace_sched_path(__trace_sched_group_cfs_rq(se), NULL, 0) ) + __array( char, comm, TASK_COMM_LEN ) + __field( pid_t, pid ) + __field( unsigned long, load ) + __field( unsigned long, rbl_load ) + __field( unsigned long, util ) + ), + + TP_fast_assign( + struct cfs_rq *gcfs_rq = __trace_sched_group_cfs_rq(se); + struct task_struct *p = gcfs_rq ? NULL + : container_of(se, struct task_struct, se); + + __entry->cpu = __trace_sched_cpu(gcfs_rq, se); + __trace_sched_path(gcfs_rq, __get_dynamic_array(path), + __get_dynamic_array_len(path)); + memcpy(__entry->comm, p ? p->comm : "(null)", TASK_COMM_LEN); + __entry->pid = p ? p->pid : -1; + __entry->load = se->avg.load_avg; + __entry->rbl_load = se->avg.runnable_load_avg; + __entry->util = se->avg.util_avg; + ), + + TP_printk("cpu=%d path=%s comm=%s pid=%d load=%lu rbl_load=%lu util=%lu", + __entry->cpu, __get_str(path), __entry->comm, __entry->pid, + __entry->load, __entry->rbl_load, __entry->util) +); #endif /* CONFIG_SMP */ #endif /* _TRACE_SCHED_H */ diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c index a4792f1dfe3a..1c33d6f758f1 100644 --- a/kernel/sched/fair.c +++ b/kernel/sched/fair.c @@ -3340,6 +3340,7 @@ static inline int propagate_entity_load_avg(struct sched_entity *se) update_tg_cfs_runnable(cfs_rq, se, gcfs_rq); trace_sched_load_cfs_rq(cfs_rq); + trace_sched_load_se(se); return 1; } diff --git a/kernel/sched/pelt.c b/kernel/sched/pelt.c index f042ee0f7b08..95923939c027 100644 --- a/kernel/sched/pelt.c +++ b/kernel/sched/pelt.c @@ -276,6 +276,9 @@ int __update_load_avg_blocked_se(u64 now, int cpu, struct sched_entity *se) if (___update_load_sum(now, cpu, &se->avg, 0, 0, 0)) { ___update_load_avg(&se->avg, se_weight(se), se_runnable(se)); + + trace_sched_load_se(se); + return 1; } @@ -292,6 +295,9 @@ int __update_load_avg_se(u64 now, int cpu, struct cfs_rq *cfs_rq, struct sched_e ___update_load_avg(&se->avg, se_weight(se), se_runnable(se)); cfs_se_util_change(&se->avg); + + trace_sched_load_se(se); + return 1; } From 593d83c774119ed33414f22d206cd1d8c097fbed Mon Sep 17 00:00:00 2001 From: Dietmar Eggemann Date: Fri, 17 Mar 2017 21:23:35 +0000 Subject: [PATCH 0521/1103] ANDROID: sched/events: Introduce task_group load tracking trace event The trace event key load is mapped to: (1) load : cfs_rq->tg->load_avg The cfs_rq owned by the task_group is used as the only parameter for the trace event because it has a reference to the taskgroup and the cpu. Using the taskgroup as a parameter instead would require the cpu as a second parameter. A task_group is global and not per-cpu data. The cpu key only tells on which cpu the value was gathered. The following list shows examples of the key=value pairs for: (1) a task group: cpu=1 path=/tg1/tg11/tg111 load=517 (2) an autogroup: cpu=1 path=/autogroup-10 load=1050 We don't maintain a load signal for a root task group. The trace event is only defined if cfs group scheduling support (CONFIG_FAIR_GROUP_SCHED) is enabled. Signed-off-by: Dietmar Eggemann Cc: Peter Zijlstra Cc: Ingo Molnar Cc: Steven Rostedt Signed-off-by: Dietmar Eggemann Change-Id: I7de38e6b30a99d7c9887c94c707ded26b383b5f8 --- include/trace/events/sched.h | 29 +++++++++++++++++++++++++++++ kernel/sched/fair.c | 2 ++ 2 files changed, 31 insertions(+) diff --git a/include/trace/events/sched.h b/include/trace/events/sched.h index 96092f803c34..1bffb9a52655 100644 --- a/include/trace/events/sched.h +++ b/include/trace/events/sched.h @@ -710,6 +710,35 @@ TRACE_EVENT(sched_load_se, __entry->cpu, __get_str(path), __entry->comm, __entry->pid, __entry->load, __entry->rbl_load, __entry->util) ); + +/* + * Tracepoint for task_group load tracking: + */ +#ifdef CONFIG_FAIR_GROUP_SCHED +TRACE_EVENT(sched_load_tg, + + TP_PROTO(struct cfs_rq *cfs_rq), + + TP_ARGS(cfs_rq), + + TP_STRUCT__entry( + __field( int, cpu ) + __dynamic_array(char, path, + __trace_sched_path(cfs_rq, NULL, 0) ) + __field( long, load ) + ), + + TP_fast_assign( + __entry->cpu = cfs_rq->rq->cpu; + __trace_sched_path(cfs_rq, __get_dynamic_array(path), + __get_dynamic_array_len(path)); + __entry->load = atomic_long_read(&cfs_rq->tg->load_avg); + ), + + TP_printk("cpu=%d path=%s load=%ld", __entry->cpu, __get_str(path), + __entry->load) +); +#endif /* CONFIG_FAIR_GROUP_SCHED */ #endif /* CONFIG_SMP */ #endif /* _TRACE_SCHED_H */ diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c index 1c33d6f758f1..f45757b45a5a 100644 --- a/kernel/sched/fair.c +++ b/kernel/sched/fair.c @@ -3097,6 +3097,8 @@ static inline void update_tg_load_avg(struct cfs_rq *cfs_rq, int force) if (force || abs(delta) > cfs_rq->tg_load_avg_contrib / 64) { atomic_long_add(delta, &cfs_rq->tg->load_avg); cfs_rq->tg_load_avg_contrib = cfs_rq->avg.load_avg; + + trace_sched_load_tg(cfs_rq); } } From 2e964cca144da82305c3d2ad7a20836ce79a032d Mon Sep 17 00:00:00 2001 From: Patrick Bellasi Date: Fri, 27 Oct 2017 16:12:51 +0100 Subject: [PATCH 0522/1103] ANDROID: sched/events: Introduce util_est trace events Signed-off-by: Patrick Bellasi Change-Id: I65e294c454369cbc15a29370d8a13ce358a95c39 --- include/trace/events/sched.h | 64 ++++++++++++++++++++++++++++++++++++ kernel/sched/fair.c | 10 ++++++ 2 files changed, 74 insertions(+) diff --git a/include/trace/events/sched.h b/include/trace/events/sched.h index 1bffb9a52655..fc9aaeb55def 100644 --- a/include/trace/events/sched.h +++ b/include/trace/events/sched.h @@ -739,6 +739,70 @@ TRACE_EVENT(sched_load_tg, __entry->load) ); #endif /* CONFIG_FAIR_GROUP_SCHED */ + +/* + * Tracepoint for tasks' estimated utilization. + */ +TRACE_EVENT(sched_util_est_task, + + TP_PROTO(struct task_struct *tsk, struct sched_avg *avg), + + TP_ARGS(tsk, avg), + + TP_STRUCT__entry( + __array( char, comm, TASK_COMM_LEN ) + __field( pid_t, pid ) + __field( int, cpu ) + __field( unsigned int, util_avg ) + __field( unsigned int, est_enqueued ) + __field( unsigned int, est_ewma ) + + ), + + TP_fast_assign( + memcpy(__entry->comm, tsk->comm, TASK_COMM_LEN); + __entry->pid = tsk->pid; + __entry->cpu = task_cpu(tsk); + __entry->util_avg = avg->util_avg; + __entry->est_enqueued = avg->util_est.enqueued; + __entry->est_ewma = avg->util_est.ewma; + ), + + TP_printk("comm=%s pid=%d cpu=%d util_avg=%u util_est_ewma=%u util_est_enqueued=%u", + __entry->comm, + __entry->pid, + __entry->cpu, + __entry->util_avg, + __entry->est_ewma, + __entry->est_enqueued) +); + +/* + * Tracepoint for root cfs_rq's estimated utilization. + */ +TRACE_EVENT(sched_util_est_cpu, + + TP_PROTO(int cpu, struct cfs_rq *cfs_rq), + + TP_ARGS(cpu, cfs_rq), + + TP_STRUCT__entry( + __field( int, cpu ) + __field( unsigned int, util_avg ) + __field( unsigned int, util_est_enqueued ) + ), + + TP_fast_assign( + __entry->cpu = cpu; + __entry->util_avg = cfs_rq->avg.util_avg; + __entry->util_est_enqueued = cfs_rq->avg.util_est.enqueued; + ), + + TP_printk("cpu=%d util_avg=%u util_est_enqueued=%u", + __entry->cpu, + __entry->util_avg, + __entry->util_est_enqueued) +); #endif /* CONFIG_SMP */ #endif /* _TRACE_SCHED_H */ diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c index f45757b45a5a..86752d71caf9 100644 --- a/kernel/sched/fair.c +++ b/kernel/sched/fair.c @@ -3666,6 +3666,10 @@ static inline void util_est_enqueue(struct cfs_rq *cfs_rq, enqueued = cfs_rq->avg.util_est.enqueued; enqueued += (_task_util_est(p) | UTIL_AVG_UNCHANGED); WRITE_ONCE(cfs_rq->avg.util_est.enqueued, enqueued); + + /* Update plots for Task and CPU estimated utilization */ + trace_sched_util_est_task(p, &p->se.avg); + trace_sched_util_est_cpu(cpu_of(rq_of(cfs_rq)), cfs_rq); } /* @@ -3696,6 +3700,9 @@ util_est_dequeue(struct cfs_rq *cfs_rq, struct task_struct *p, bool task_sleep) (_task_util_est(p) | UTIL_AVG_UNCHANGED)); WRITE_ONCE(cfs_rq->avg.util_est.enqueued, ue.enqueued); + /* Update plots for CPU's estimated utilization */ + trace_sched_util_est_cpu(cpu_of(rq_of(cfs_rq)), cfs_rq); + /* * Skip update of task's estimated utilization when the task has not * yet completed an activation, e.g. being migrated. @@ -3741,6 +3748,9 @@ util_est_dequeue(struct cfs_rq *cfs_rq, struct task_struct *p, bool task_sleep) ue.ewma += last_ewma_diff; ue.ewma >>= UTIL_EST_WEIGHT_SHIFT; WRITE_ONCE(p->se.avg.util_est, ue); + + /* Update plots for Task's estimated utilization */ + trace_sched_util_est_task(p, &p->se.avg); } static inline int task_fits_capacity(struct task_struct *p, long capacity) From 4d0d1f7c807fff70aa6d92a2f5403da1340058ec Mon Sep 17 00:00:00 2001 From: Quentin Perret Date: Thu, 31 May 2018 11:15:26 +0100 Subject: [PATCH 0523/1103] ANDROID: sched/events: Introduce find_best_target trace event Adapated from the existing trace event from android-4.14. Change-Id: I9785e692fb0af087c236906d7f47fed1b20690f5 Signed-off-by: Quentin Perret --- include/trace/events/sched.h | 41 ++++++++++++++++++++++++++++++++++++ kernel/sched/fair.c | 3 +++ 2 files changed, 44 insertions(+) diff --git a/include/trace/events/sched.h b/include/trace/events/sched.h index fc9aaeb55def..f5662789777d 100644 --- a/include/trace/events/sched.h +++ b/include/trace/events/sched.h @@ -803,6 +803,47 @@ TRACE_EVENT(sched_util_est_cpu, __entry->util_avg, __entry->util_est_enqueued) ); + +/* + * Tracepoint for find_best_target + */ +TRACE_EVENT(sched_find_best_target, + + TP_PROTO(struct task_struct *tsk, bool prefer_idle, + unsigned long min_util, int best_idle, int best_active, + int target, int backup), + + TP_ARGS(tsk, prefer_idle, min_util, best_idle, + best_active, target, backup), + + TP_STRUCT__entry( + __array( char, comm, TASK_COMM_LEN ) + __field( pid_t, pid ) + __field( unsigned long, min_util ) + __field( bool, prefer_idle ) + __field( int, best_idle ) + __field( int, best_active ) + __field( int, target ) + __field( int, backup ) + ), + + TP_fast_assign( + memcpy(__entry->comm, tsk->comm, TASK_COMM_LEN); + __entry->pid = tsk->pid; + __entry->min_util = min_util; + __entry->prefer_idle = prefer_idle; + __entry->best_idle = best_idle; + __entry->best_active = best_active; + __entry->target = target; + __entry->backup = backup; + ), + + TP_printk("pid=%d comm=%s prefer_idle=%d " + "best_idle=%d best_active=%d target=%d backup=%d", + __entry->pid, __entry->comm, __entry->prefer_idle, + __entry->best_idle, __entry->best_active, + __entry->target, __entry->backup) +); #endif /* CONFIG_SMP */ #endif /* _TRACE_SCHED_H */ diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c index 86752d71caf9..6fd45cf8fa74 100644 --- a/kernel/sched/fair.c +++ b/kernel/sched/fair.c @@ -6774,6 +6774,9 @@ static void find_best_target(struct sched_domain *sd, cpumask_t *cpus, target: cpumask_set_cpu(target_cpu, cpus); } + + trace_sched_find_best_target(p, prefer_idle, min_util, best_idle_cpu, + best_active_cpu, target_cpu, backup_cpu); } /* From dc432fb580a358031c5248fe0778392d97a5697e Mon Sep 17 00:00:00 2001 From: Quentin Perret Date: Thu, 31 May 2018 11:26:52 +0100 Subject: [PATCH 0524/1103] ANDROID: sched/events: Introduce schedtune trace events Suggested-by: Patrick Bellasi Signed-off-by: Quentin Perret Change-Id: I2c43bcb37f57844a07aa36e339da00180e65b6c2 --- include/trace/events/sched.h | 120 +++++++++++++++++++++++++++++++++++ kernel/sched/fair.c | 4 ++ kernel/sched/tune.c | 7 ++ 3 files changed, 131 insertions(+) diff --git a/include/trace/events/sched.h b/include/trace/events/sched.h index f5662789777d..278d00dcf363 100644 --- a/include/trace/events/sched.h +++ b/include/trace/events/sched.h @@ -844,6 +844,126 @@ TRACE_EVENT(sched_find_best_target, __entry->best_idle, __entry->best_active, __entry->target, __entry->backup) ); + +/* + * Tracepoint for accounting CPU boosted utilization + */ +TRACE_EVENT(sched_boost_cpu, + + TP_PROTO(int cpu, unsigned long util, long margin), + + TP_ARGS(cpu, util, margin), + + TP_STRUCT__entry( + __field( int, cpu ) + __field( unsigned long, util ) + __field(long, margin ) + ), + + TP_fast_assign( + __entry->cpu = cpu; + __entry->util = util; + __entry->margin = margin; + ), + + TP_printk("cpu=%d util=%lu margin=%ld", + __entry->cpu, + __entry->util, + __entry->margin) +); + +/* + * Tracepoint for schedtune_tasks_update + */ +TRACE_EVENT(sched_tune_tasks_update, + + TP_PROTO(struct task_struct *tsk, int cpu, int tasks, int idx, + int boost, int max_boost), + + TP_ARGS(tsk, cpu, tasks, idx, boost, max_boost), + + TP_STRUCT__entry( + __array( char, comm, TASK_COMM_LEN ) + __field( pid_t, pid ) + __field( int, cpu ) + __field( int, tasks ) + __field( int, idx ) + __field( int, boost ) + __field( int, max_boost ) + ), + + TP_fast_assign( + memcpy(__entry->comm, tsk->comm, TASK_COMM_LEN); + __entry->pid = tsk->pid; + __entry->cpu = cpu; + __entry->tasks = tasks; + __entry->idx = idx; + __entry->boost = boost; + __entry->max_boost = max_boost; + ), + + TP_printk("pid=%d comm=%s " + "cpu=%d tasks=%d idx=%d boost=%d max_boost=%d", + __entry->pid, __entry->comm, + __entry->cpu, __entry->tasks, __entry->idx, + __entry->boost, __entry->max_boost) +); + +/* + * Tracepoint for schedtune_boostgroup_update + */ +TRACE_EVENT(sched_tune_boostgroup_update, + + TP_PROTO(int cpu, int variation, int max_boost), + + TP_ARGS(cpu, variation, max_boost), + + TP_STRUCT__entry( + __field( int, cpu ) + __field( int, variation ) + __field( int, max_boost ) + ), + + TP_fast_assign( + __entry->cpu = cpu; + __entry->variation = variation; + __entry->max_boost = max_boost; + ), + + TP_printk("cpu=%d variation=%d max_boost=%d", + __entry->cpu, __entry->variation, __entry->max_boost) +); + +/* + * Tracepoint for accounting task boosted utilization + */ +TRACE_EVENT(sched_boost_task, + + TP_PROTO(struct task_struct *tsk, unsigned long util, long margin), + + TP_ARGS(tsk, util, margin), + + TP_STRUCT__entry( + __array( char, comm, TASK_COMM_LEN ) + __field( pid_t, pid ) + __field( unsigned long, util ) + __field( long, margin ) + + ), + + TP_fast_assign( + memcpy(__entry->comm, tsk->comm, TASK_COMM_LEN); + __entry->pid = tsk->pid; + __entry->util = util; + __entry->margin = margin; + ), + + TP_printk("comm=%s pid=%d util=%lu margin=%ld", + __entry->comm, __entry->pid, + __entry->util, + __entry->margin) +); + #endif /* CONFIG_SMP */ #endif /* _TRACE_SCHED_H */ diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c index 6fd45cf8fa74..bf3e7d2fc615 100644 --- a/kernel/sched/fair.c +++ b/kernel/sched/fair.c @@ -5837,6 +5837,8 @@ boosted_cpu_util(int cpu) unsigned long util = cpu_util_cfs(cpu_rq(cpu)); long margin = schedtune_cpu_margin(util, cpu); + trace_sched_boost_cpu(cpu, util, margin); + return util + margin; } @@ -5864,6 +5866,8 @@ boosted_task_util(struct task_struct *task) unsigned long util = task_util_est(task); long margin = schedtune_task_margin(task); + trace_sched_boost_task(task, util, margin); + return util + margin; } diff --git a/kernel/sched/tune.c b/kernel/sched/tune.c index af6de6d3df6d..7e0630c52b81 100644 --- a/kernel/sched/tune.c +++ b/kernel/sched/tune.c @@ -227,14 +227,18 @@ schedtune_boostgroup_update(int idx, int boost) /* Check if this update increase current max */ if (boost > cur_boost_max && bg->group[idx].tasks) { bg->boost_max = boost; + trace_sched_tune_boostgroup_update(cpu, 1, bg->boost_max); continue; } /* Check if this update has decreased current max */ if (cur_boost_max == old_boost && old_boost > boost) { schedtune_cpu_update(cpu); + trace_sched_tune_boostgroup_update(cpu, -1, bg->boost_max); continue; } + + trace_sched_tune_boostgroup_update(cpu, 0, bg->boost_max); } return 0; @@ -252,6 +256,9 @@ schedtune_tasks_update(struct task_struct *p, int cpu, int idx, int task_count) /* Update boosted tasks count while avoiding to make it negative */ bg->group[idx].tasks = max(0, tasks); + trace_sched_tune_tasks_update(p, cpu, tasks, idx, + bg->group[idx].boost, bg->boost_max); + /* Boost group activation or deactivation on that RQ */ if (tasks == 1 || tasks == 0) schedtune_cpu_update(cpu); From 097f3e78bc6488634ccb954c9e0b3474db98283d Mon Sep 17 00:00:00 2001 From: Chris Redpath Date: Fri, 27 Oct 2017 16:52:17 +0100 Subject: [PATCH 0525/1103] ANDROID: sched/events: Introduce rt_rq load tracking trace event We want to be able to track rt_rq signals same as we do for other RQs. Signed-off-by: Chris Redpath (cherry-picked from commit 574a2d189695c334ae290f522b098f05398a3765) [ - Fixed conflicts with the refactored RT util_avg tracking - Changed commit title for consistency with other tracepoint patches ] Signed-off-by: Quentin Perret Change-Id: I38b64baa50e1ff86019ca4b8b0a04af994880b35 --- include/trace/events/sched.h | 24 ++++++++++++++++++++++++ kernel/sched/pelt.c | 3 +++ 2 files changed, 27 insertions(+) diff --git a/include/trace/events/sched.h b/include/trace/events/sched.h index 278d00dcf363..97c3a46105ca 100644 --- a/include/trace/events/sched.h +++ b/include/trace/events/sched.h @@ -671,6 +671,30 @@ TRACE_EVENT(sched_load_cfs_rq, __entry->rbl_load,__entry->util) ); +/* + * Tracepoint for rt_rq load tracking: + */ +struct rq; +TRACE_EVENT(sched_load_rt_rq, + + TP_PROTO(struct rq *rq), + + TP_ARGS(rq), + + TP_STRUCT__entry( + __field( int, cpu ) + __field( unsigned long, util ) + ), + + TP_fast_assign( + __entry->cpu = rq->cpu; + __entry->util = rq->avg_rt.util_avg; + ), + + TP_printk("cpu=%d util=%lu", __entry->cpu, + __entry->util) +); + /* * Tracepoint for sched_entity load tracking: */ diff --git a/kernel/sched/pelt.c b/kernel/sched/pelt.c index 95923939c027..c116744ec44b 100644 --- a/kernel/sched/pelt.c +++ b/kernel/sched/pelt.c @@ -340,6 +340,9 @@ int update_rt_rq_load_avg(u64 now, struct rq *rq, int running) running)) { ___update_load_avg(&rq->avg_rt, 1, 1); + + trace_sched_load_rt_rq(rq); + return 1; } From b882e867d51bbdf1ce12ccc5dea8432ef8bb7971 Mon Sep 17 00:00:00 2001 From: Patrick Bellasi Date: Wed, 10 Feb 2016 09:24:36 +0000 Subject: [PATCH 0526/1103] ANDROID: sched/events: Introduce overutilized trace event Signed-off-by: Patrick Bellasi Signed-off-by: Andres Oportus (cherry picked from commit 8e45d941282039d5379f4e286e5bd0a2044e105c) [ - Trivial cherry pick issues - Changed commit title for consistency ] Signed-off-by: Quentin Perret Change-Id: I78fb5e82223558def0cf16105c233591cda81d5c --- include/trace/events/sched.h | 21 +++++++++++++++++++++ kernel/sched/fair.c | 7 ++++++- 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/include/trace/events/sched.h b/include/trace/events/sched.h index 97c3a46105ca..e3928f7286d6 100644 --- a/include/trace/events/sched.h +++ b/include/trace/events/sched.h @@ -988,6 +988,27 @@ TRACE_EVENT(sched_boost_task, __entry->margin) ); +/* + * Tracepoint for system overutilized flag +*/ +TRACE_EVENT(sched_overutilized, + + TP_PROTO(int overutilized), + + TP_ARGS(overutilized), + + TP_STRUCT__entry( + __field( int, overutilized ) + ), + + TP_fast_assign( + __entry->overutilized = overutilized; + ), + + TP_printk("overutilized=%d", + __entry->overutilized) +); + #endif /* CONFIG_SMP */ #endif /* _TRACE_SCHED_H */ diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c index bf3e7d2fc615..37e494402e1a 100644 --- a/kernel/sched/fair.c +++ b/kernel/sched/fair.c @@ -5119,8 +5119,10 @@ static inline bool cpu_overutilized(int cpu) static inline void update_overutilized_status(struct rq *rq) { - if (!READ_ONCE(rq->rd->overutilized) && cpu_overutilized(rq->cpu)) + if (!READ_ONCE(rq->rd->overutilized) && cpu_overutilized(rq->cpu)) { WRITE_ONCE(rq->rd->overutilized, SG_OVERUTILIZED); + trace_sched_overutilized(1); + } } #else static inline void update_overutilized_status(struct rq *rq) { } @@ -8941,9 +8943,12 @@ static inline void update_sd_lb_stats(struct lb_env *env, struct sd_lb_stats *sd /* Update over-utilization (tipping point, U >= 0) indicator */ WRITE_ONCE(rd->overutilized, sg_status & SG_OVERUTILIZED); + trace_sched_overutilized(!!(sg_status & SG_OVERUTILIZED)); } else if (sg_status & SG_OVERUTILIZED) { WRITE_ONCE(env->dst_rq->rd->overutilized, SG_OVERUTILIZED); + trace_sched_overutilized(1); } + } /** From e23137d7dbff2feece2473bbedbf26f13cb92838 Mon Sep 17 00:00:00 2001 From: Quentin Perret Date: Tue, 10 Jul 2018 16:57:36 +0100 Subject: [PATCH 0527/1103] ANDROID: arm64: defconfig: Enable CONFIG_SCHED_TUNE Signed-off-by: Quentin Perret Change-Id: Id31c1a0811a1ee45e5533d948dc2faef50624a5a --- arch/arm64/configs/defconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/arm64/configs/defconfig b/arch/arm64/configs/defconfig index 31fd24bff688..2fc45d5505a4 100644 --- a/arch/arm64/configs/defconfig +++ b/arch/arm64/configs/defconfig @@ -106,6 +106,7 @@ CONFIG_COMPAT=y CONFIG_HIBERNATION=y CONFIG_WQ_POWER_EFFICIENT_DEFAULT=y CONFIG_ENERGY_MODEL=y +CONFIG_SCHED_TUNE=y CONFIG_ARM_CPUIDLE=y CONFIG_CPU_FREQ=y CONFIG_CPU_FREQ_STAT=y From 02f22af8cb343f3b5caec99fb8688a03b05df52b Mon Sep 17 00:00:00 2001 From: Quentin Perret Date: Wed, 12 Sep 2018 15:34:46 +0100 Subject: [PATCH 0528/1103] ANDROID: sched: Make the cpu_util* accessors available without sugov In preparation for the introduction of the boost hold feature in SchedTune, make the cpu_util_* signals available even without sugov, hence making the stune integration a lot easier without breaking other platforms Change-Id: I77c14b6fd470a41ffb3ac510b76395ebc116ddfd Signed-off-by: Quentin Perret --- kernel/sched/sched.h | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/kernel/sched/sched.h b/kernel/sched/sched.h index bc61dee4d0e1..85d099575e34 100644 --- a/kernel/sched/sched.h +++ b/kernel/sched/sched.h @@ -2243,7 +2243,15 @@ enum schedutil_type { #ifdef CONFIG_CPU_FREQ_GOV_SCHEDUTIL unsigned long schedutil_freq_util(int cpu, unsigned long util_cfs, enum schedutil_type type); +#else /* CONFIG_CPU_FREQ_GOV_SCHEDUTIL */ +static inline unsigned long schedutil_freq_util(int cpu, unsigned long util, + enum schedutil_type type) +{ + return util; +} +#endif +#ifdef CONFIG_SMP static inline unsigned long cpu_bw_dl(struct rq *rq) { return (rq->dl.running_bw * SCHED_CAPACITY_SCALE) >> BW_SHIFT; @@ -2270,12 +2278,6 @@ static inline unsigned long cpu_util_rt(struct rq *rq) { return READ_ONCE(rq->avg_rt.util_avg); } -#else /* CONFIG_CPU_FREQ_GOV_SCHEDUTIL */ -static inline unsigned long schedutil_freq_util(int cpu, unsigned long util, - enum schedutil_type type) -{ - return util; -} #endif #ifdef HAVE_SCHED_AVG_IRQ From f296b4740ad93e99c51ce4d2617c3a21cb2170e1 Mon Sep 17 00:00:00 2001 From: Chris Redpath Date: Mon, 9 Jul 2018 16:54:02 +0100 Subject: [PATCH 0529/1103] ANDROID: sched/rt: Add schedtune accounting to rt task enqueue/dequeue rt tasks are currently not eligible for schedtune boosting. Make it so by adding enqueue/dequeue hooks. For rt tasks, schedtune only acts as a frequency boosting framework, it has no impact on placement decisions and the prefer_idle attribute is not used. Also prepare schedutil use of boosted util for rt task boosting With this change, schedtune accounting will include rt class tasks, however boosting currently only applies to the utilization provided by fair class tasks. Sum up the tracked CPU utilization applying boost to the aggregate util instead - this includes RT task util in the boosting if any tasks are runnable. Scenario 1, considering one CPU: 1x rt task running, util 250, boost 0 1x cfs task runnable, util 250, boost 50 previous util=250+(50pct_boosted_250) = 887 new util=50_pct_boosted_500 = 762 Scenario 2, considering one CPU: 1x rt task running, util 250, boost 50 1x cfs task runnable, util 250, boost 0 previous util=250+250 = 500 new util=50_pct_boosted_500 = 762 Scenario 3, considering one CPU: 1x rt task running, util 250, boost 50 1x cfs task runnable, util 250, boost 50 previous util=250+(50pct_boosted_250) = 887 new util=50_pct_boosted_500 = 762 Scenario 4: 1x rt task running, util 250, boost 50 previous util=250 = 250 new util=50_pct_boosted_250 = 637 Change-Id: Ie287cbd0692468525095b5024db9faac8b2f4878 Signed-off-by: Chris Redpath (cherry picked from commit 8e266aebf737262aeca9662254a3e61ccc7f8dec) [ - Fixed conflicts in sugov related to PELT signals of all classes - Fixed conflicts related to boosted_cpu_util being in a different location - Moved the CFS/RT util aggregation out of schedutil_freq_util to ease the integration with SchedTune ] Signed-off-by: Quentin Perret --- kernel/sched/cpufreq_schedutil.c | 21 ++++++++++----------- kernel/sched/fair.c | 7 +++---- kernel/sched/rt.c | 4 ++++ kernel/sched/sched.h | 2 +- kernel/sched/tune.h | 4 ++-- 5 files changed, 20 insertions(+), 18 deletions(-) diff --git a/kernel/sched/cpufreq_schedutil.c b/kernel/sched/cpufreq_schedutil.c index 6704b2b3cb41..e56afcf60d19 100644 --- a/kernel/sched/cpufreq_schedutil.c +++ b/kernel/sched/cpufreq_schedutil.c @@ -219,6 +219,9 @@ static unsigned int get_next_freq(struct sugov_policy *sg_policy, * Where the cfs,rt and dl util numbers are tracked with the same metric and * synchronized windows and are thus directly comparable. * + * The @util parameter passed to this function is assumed to be the aggregation + * of RT and CFS util numbers. The cases of DL and IRQ are managed here. + * * The cfs,rt,dl utilization are the running times measured with rq->clock_task * which excludes things like IRQ and steal-time. These latter are then accrued * in the irq utilization. @@ -227,11 +230,11 @@ static unsigned int get_next_freq(struct sugov_policy *sg_policy, * based on the task model parameters and gives the minimal utilization * required to meet deadlines. */ -unsigned long schedutil_freq_util(int cpu, unsigned long util_cfs, +unsigned long schedutil_freq_util(int cpu, unsigned long util, enum schedutil_type type) { struct rq *rq = cpu_rq(cpu); - unsigned long util, irq, max; + unsigned long irq, max; max = arch_scale_cpu_capacity(NULL, cpu); @@ -249,14 +252,11 @@ unsigned long schedutil_freq_util(int cpu, unsigned long util_cfs, return max; /* - * Because the time spend on RT/DL tasks is visible as 'lost' time to - * CFS tasks and we use the same metric to track the effective - * utilization (PELT windows are synchronized) we can directly add them - * to obtain the CPU's actual utilization. + * The function is called with @util defined as the aggregation (the + * sum) of RT and CFS signals, hence leaving the special case of DL + * to be delt with. The exact way of doing things depend on the calling + * context. */ - util = util_cfs; - util += cpu_util_rt(rq); - if (type == FREQUENCY_UTIL) { /* * For frequency selection we do not make cpu_util_dl() a @@ -269,7 +269,6 @@ unsigned long schedutil_freq_util(int cpu, unsigned long util_cfs, * to not quite hit saturation when we should -- * something for later. */ - if ((util + cpu_util_dl(rq)) >= max) return max; } else { @@ -316,7 +315,7 @@ unsigned long schedutil_freq_util(int cpu, unsigned long util_cfs, static unsigned long sugov_get_util(struct sugov_cpu *sg_cpu) { struct rq *rq = cpu_rq(sg_cpu->cpu); - unsigned long util = boosted_cpu_util(sg_cpu->cpu); + unsigned long util = boosted_cpu_util(sg_cpu->cpu, cpu_util_rt(rq)); sg_cpu->max = arch_scale_cpu_capacity(NULL, sg_cpu->cpu); sg_cpu->bw_dl = cpu_bw_dl(rq); diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c index 37e494402e1a..14199c9d0270 100644 --- a/kernel/sched/fair.c +++ b/kernel/sched/fair.c @@ -5834,9 +5834,9 @@ schedtune_task_margin(struct task_struct *task) } unsigned long -boosted_cpu_util(int cpu) +boosted_cpu_util(int cpu, unsigned long other_util) { - unsigned long util = cpu_util_cfs(cpu_rq(cpu)); + unsigned long util = cpu_util_cfs(cpu_rq(cpu)) + other_util; long margin = schedtune_cpu_margin(util, cpu); trace_sched_boost_cpu(cpu, util, margin); @@ -5860,8 +5860,6 @@ schedtune_task_margin(struct task_struct *task) #endif /* CONFIG_SCHED_TUNE */ - - static inline unsigned long boosted_task_util(struct task_struct *task) { @@ -6877,6 +6875,7 @@ static long compute_energy(struct task_struct *p, int dst_cpu, */ for_each_cpu_and(cpu, perf_domain_span(pd), cpu_online_mask) { util = cpu_util_next(cpu, p, dst_cpu); + util += cpu_util_rt(cpu_rq(cpu)); util = schedutil_freq_util(cpu, util, ENERGY_UTIL); max_util = max(util, max_util); sum_util += util; diff --git a/kernel/sched/rt.c b/kernel/sched/rt.c index 0be707d9c2db..3eed85fc86db 100644 --- a/kernel/sched/rt.c +++ b/kernel/sched/rt.c @@ -1329,6 +1329,8 @@ enqueue_task_rt(struct rq *rq, struct task_struct *p, int flags) { struct sched_rt_entity *rt_se = &p->rt; + schedtune_enqueue_task(p, cpu_of(rq)); + if (flags & ENQUEUE_WAKEUP) rt_se->timeout = 0; @@ -1342,6 +1344,8 @@ static void dequeue_task_rt(struct rq *rq, struct task_struct *p, int flags) { struct sched_rt_entity *rt_se = &p->rt; + schedtune_dequeue_task(p, cpu_of(rq)); + update_curr_rt(rq); dequeue_rt_entity(rt_se, flags); diff --git a/kernel/sched/sched.h b/kernel/sched/sched.h index 85d099575e34..96ae9cd82051 100644 --- a/kernel/sched/sched.h +++ b/kernel/sched/sched.h @@ -2241,7 +2241,7 @@ enum schedutil_type { }; #ifdef CONFIG_CPU_FREQ_GOV_SCHEDUTIL -unsigned long schedutil_freq_util(int cpu, unsigned long util_cfs, +unsigned long schedutil_freq_util(int cpu, unsigned long util, enum schedutil_type type); #else /* CONFIG_CPU_FREQ_GOV_SCHEDUTIL */ static inline unsigned long schedutil_freq_util(int cpu, unsigned long util, diff --git a/kernel/sched/tune.h b/kernel/sched/tune.h index bb187c6112a7..821f026b510f 100644 --- a/kernel/sched/tune.h +++ b/kernel/sched/tune.h @@ -20,7 +20,7 @@ int schedtune_prefer_idle(struct task_struct *tsk); void schedtune_enqueue_task(struct task_struct *p, int cpu); void schedtune_dequeue_task(struct task_struct *p, int cpu); -unsigned long boosted_cpu_util(int cpu); +unsigned long boosted_cpu_util(int cpu, unsigned long other_util); #else /* CONFIG_SCHED_TUNE */ @@ -32,6 +32,6 @@ unsigned long boosted_cpu_util(int cpu); #define schedtune_enqueue_task(task, cpu) do { } while (0) #define schedtune_dequeue_task(task, cpu) do { } while (0) -#define boosted_cpu_util(cpu) cpu_util_cfs(cpu_rq(cpu)) +#define boosted_cpu_util(cpu, other_util) cpu_util_cfs(cpu_rq(cpu)) #endif /* CONFIG_SCHED_TUNE */ From f063ff454d718941bfc8f01e16d3b639c60ea597 Mon Sep 17 00:00:00 2001 From: Chris Redpath Date: Mon, 9 Jul 2018 15:44:00 +0100 Subject: [PATCH 0530/1103] ANDROID: Add hold functionality to schedtune CPU boost When tasks come and go from a runqueue quickly, this can lead to boost being applied and removed quickly which sometimes means we cannot raise the CPU frequency again when we need to (due to the rate limit on frequency updates). This has proved to be a particular issue for RT tasks and alternative methods have been used in the past to work around it. This is an attempt to solve the issue for all task classes and cpufreq governors by introducing a generic mechanism in schedtune to retain the max boost level from task enqueue for a minimum period - defined here as 50ms. This timeout was determined experimentally and is not configurable. A sched_feat guards the application of this to tasks - in the default configuration, task boosting only applied to tasks which have RT policy. Change SCHEDTUNE_BOOST_HOLD_ALL to true to apply it to all tasks regardless of class. It works like so: Every task enqueue (in an allowed class) stores a cpu-local timestamp. If the task is not a member of an allowed class (all or RT depending upon feature selection), the timestamp is not updated. The boost group will stay active regardless of tasks present until 50ms beyond the last timestamp stored. We also store the timestamp of the active boost group to avoid unneccesarily revisiting the boost groups when checking CPU boost level. If the timestamp is more than 50ms in the past when we check boost then we re-evaluate the boost groups for that CPU, taking into account the timestamps associated with each group. Idea based on rt-boost-retention patches from Joel. Change-Id: I52cc2d2e82d1c5aa03550378c8836764f41630c1 Suggested-by: Joel Fernandes Reviewed-by: Patrick Bellasi Signed-off-by: Chris Redpath [forward ported from android-4.9-eas-dev proposal] (cherry picked from commit a485e8b7bf8e95759e600396feeb7bfb400b6e46) [ - Trivial cherry-pick conflicts in include/trace/events/sched.h ] Signed-off-by: Quentin Perret --- include/trace/events/sched.h | 11 +++-- kernel/sched/features.h | 11 +++++ kernel/sched/tune.c | 95 ++++++++++++++++++++++++++++++------ 3 files changed, 97 insertions(+), 20 deletions(-) diff --git a/include/trace/events/sched.h b/include/trace/events/sched.h index e3928f7286d6..ee2dcc08943e 100644 --- a/include/trace/events/sched.h +++ b/include/trace/events/sched.h @@ -902,9 +902,9 @@ TRACE_EVENT(sched_boost_cpu, TRACE_EVENT(sched_tune_tasks_update, TP_PROTO(struct task_struct *tsk, int cpu, int tasks, int idx, - int boost, int max_boost), + int boost, int max_boost, u64 group_ts), - TP_ARGS(tsk, cpu, tasks, idx, boost, max_boost), + TP_ARGS(tsk, cpu, tasks, idx, boost, max_boost, group_ts), TP_STRUCT__entry( __array( char, comm, TASK_COMM_LEN ) @@ -914,6 +914,7 @@ TRACE_EVENT(sched_tune_tasks_update, __field( int, idx ) __field( int, boost ) __field( int, max_boost ) + __field( u64, group_ts ) ), TP_fast_assign( @@ -924,13 +925,15 @@ TRACE_EVENT(sched_tune_tasks_update, __entry->idx = idx; __entry->boost = boost; __entry->max_boost = max_boost; + __entry->group_ts = group_ts; ), TP_printk("pid=%d comm=%s " - "cpu=%d tasks=%d idx=%d boost=%d max_boost=%d", + "cpu=%d tasks=%d idx=%d boost=%d max_boost=%d timeout=%llu", __entry->pid, __entry->comm, __entry->cpu, __entry->tasks, __entry->idx, - __entry->boost, __entry->max_boost) + __entry->boost, __entry->max_boost, + __entry->group_ts) ); /* diff --git a/kernel/sched/features.h b/kernel/sched/features.h index fb995c5f69ba..010dce97a308 100644 --- a/kernel/sched/features.h +++ b/kernel/sched/features.h @@ -116,3 +116,14 @@ SCHED_FEAT(EAS_PREFER_IDLE, true) * Request max frequency from schedutil whenever a RT task is running. */ SCHED_FEAT(SUGOV_RT_MAX_FREQ, false) + +/* + * Apply schedtune boost hold to tasks of all sched classes. + * If enabled, schedtune will hold the boost applied to a CPU + * for 50ms regardless of task activation - if the task is + * still running 50ms later, the boost hold expires and schedtune + * boost will expire immediately the task stops. + * If disabled, this behaviour will only apply to tasks of the + * RT class. + */ +SCHED_FEAT(SCHEDTUNE_BOOST_HOLD_ALL, false) diff --git a/kernel/sched/tune.c b/kernel/sched/tune.c index 7e0630c52b81..3b231c639fe4 100644 --- a/kernel/sched/tune.c +++ b/kernel/sched/tune.c @@ -13,6 +13,9 @@ bool schedtune_initialized = false; extern struct reciprocal_value schedtune_spc_rdiv; +/* We hold schedtune boost in effect for at least this long */ +#define SCHEDTUNE_BOOST_HOLD_NS 50000000ULL + /* * EAS scheduler tunables for task groups. * @@ -151,6 +154,7 @@ static struct schedtune *allocated_group[BOOSTGROUPS_COUNT] = { struct boost_groups { /* Maximum boost value for all RUNNABLE tasks on a CPU */ int boost_max; + u64 boost_ts; struct { /* True when this boost group maps an actual cgroup */ bool valid; @@ -158,6 +162,8 @@ struct boost_groups { int boost; /* Count of RUNNABLE tasks on that boost group */ unsigned tasks; + /* Timestamp of boost activation */ + u64 ts; } group[BOOSTGROUPS_COUNT]; /* CPU's boost group locking */ raw_spinlock_t lock; @@ -166,15 +172,31 @@ struct boost_groups { /* Boost groups affecting each CPU in the system */ DEFINE_PER_CPU(struct boost_groups, cpu_boost_groups); +static inline bool schedtune_boost_timeout(u64 now, u64 ts) +{ + return ((now - ts) > SCHEDTUNE_BOOST_HOLD_NS); +} + +static inline bool +schedtune_boost_group_active(int idx, struct boost_groups* bg, u64 now) +{ + if (bg->group[idx].tasks) + return true; + + return !schedtune_boost_timeout(now, bg->group[idx].ts); +} + static void -schedtune_cpu_update(int cpu) +schedtune_cpu_update(int cpu, u64 now) { struct boost_groups *bg = &per_cpu(cpu_boost_groups, cpu); int boost_max; + u64 boost_ts; int idx; /* The root boost group is always active */ boost_max = bg->group[0].boost; + boost_ts = now; for (idx = 1; idx < BOOSTGROUPS_COUNT; ++idx) { /* Ignore non boostgroups not mapping a cgroup */ @@ -183,12 +205,18 @@ schedtune_cpu_update(int cpu) /* * A boost group affects a CPU only if it has - * RUNNABLE tasks on that CPU + * RUNNABLE tasks on that CPU or it has hold + * in effect from a previous task. */ - if (bg->group[idx].tasks == 0) + if (!schedtune_boost_group_active(idx, bg, now)) + continue; + + /* This boost group is active */ + if (boost_max > bg->group[idx].boost) continue; - boost_max = max(boost_max, bg->group[idx].boost); + boost_max = bg->group[idx].boost; + boost_ts = bg->group[idx].ts; } /* Ensures boost_max is non-negative when all cgroup boost values @@ -196,6 +224,7 @@ schedtune_cpu_update(int cpu) * task stacking and frequency spikes.*/ boost_max = max(boost_max, 0); bg->boost_max = boost_max; + bg->boost_ts = boost_ts; } static int @@ -205,6 +234,7 @@ schedtune_boostgroup_update(int idx, int boost) int cur_boost_max; int old_boost; int cpu; + u64 now; /* Update per CPU boost groups */ for_each_possible_cpu(cpu) { @@ -225,15 +255,19 @@ schedtune_boostgroup_update(int idx, int boost) bg->group[idx].boost = boost; /* Check if this update increase current max */ - if (boost > cur_boost_max && bg->group[idx].tasks) { + now = sched_clock_cpu(cpu); + if (boost > cur_boost_max && + schedtune_boost_group_active(idx, bg, now)) { bg->boost_max = boost; + bg->boost_ts = bg->group[idx].ts; + trace_sched_tune_boostgroup_update(cpu, 1, bg->boost_max); continue; } /* Check if this update has decreased current max */ if (cur_boost_max == old_boost && old_boost > boost) { - schedtune_cpu_update(cpu); + schedtune_cpu_update(cpu, now); trace_sched_tune_boostgroup_update(cpu, -1, bg->boost_max); continue; } @@ -247,6 +281,15 @@ schedtune_boostgroup_update(int idx, int boost) #define ENQUEUE_TASK 1 #define DEQUEUE_TASK -1 +static inline bool +schedtune_update_timestamp(struct task_struct *p) +{ + if (sched_feat(SCHEDTUNE_BOOST_HOLD_ALL)) + return true; + + return task_has_rt_policy(p); +} + static inline void schedtune_tasks_update(struct task_struct *p, int cpu, int idx, int task_count) { @@ -256,12 +299,21 @@ schedtune_tasks_update(struct task_struct *p, int cpu, int idx, int task_count) /* Update boosted tasks count while avoiding to make it negative */ bg->group[idx].tasks = max(0, tasks); - trace_sched_tune_tasks_update(p, cpu, tasks, idx, - bg->group[idx].boost, bg->boost_max); + /* Update timeout on enqueue */ + if (task_count > 0) { + u64 now = sched_clock_cpu(cpu); + + if (schedtune_update_timestamp(p)) + bg->group[idx].ts = now; - /* Boost group activation or deactivation on that RQ */ - if (tasks == 1 || tasks == 0) - schedtune_cpu_update(cpu); + /* Boost group activation or deactivation on that RQ */ + if (bg->group[idx].tasks == 1) + schedtune_cpu_update(cpu, now); + } + + trace_sched_tune_tasks_update(p, cpu, tasks, idx, + bg->group[idx].boost, bg->boost_max, + bg->group[idx].ts); } /* @@ -305,6 +357,7 @@ int schedtune_can_attach(struct cgroup_taskset *tset) int src_bg; /* Source boost group index */ int dst_bg; /* Destination boost group index */ int tasks; + u64 now; if (unlikely(!schedtune_initialized)) return 0; @@ -355,13 +408,15 @@ int schedtune_can_attach(struct cgroup_taskset *tset) bg->group[src_bg].tasks = max(0, tasks); bg->group[dst_bg].tasks += 1; - raw_spin_unlock(&bg->lock); - task_rq_unlock(rq, task, &rq_flags); + /* Update boost hold start for this group */ + now = sched_clock_cpu(cpu); + bg->group[dst_bg].ts = now; - /* Update CPU boost group */ - if (bg->group[src_bg].tasks == 0 || bg->group[dst_bg].tasks == 1) - schedtune_cpu_update(task_cpu(task)); + /* Force boost group re-evaluation at next boost check */ + bg->boost_ts = now - SCHEDTUNE_BOOST_HOLD_NS; + raw_spin_unlock(&bg->lock); + task_rq_unlock(rq, task, &rq_flags); } return 0; @@ -408,8 +463,15 @@ void schedtune_dequeue_task(struct task_struct *p, int cpu) int schedtune_cpu_boost(int cpu) { struct boost_groups *bg; + u64 now; bg = &per_cpu(cpu_boost_groups, cpu); + now = sched_clock_cpu(cpu); + + /* Check to see if we have a hold in effect */ + if (schedtune_boost_timeout(now, bg->boost_ts)) + schedtune_cpu_update(cpu, now); + return bg->boost_max; } @@ -515,6 +577,7 @@ schedtune_boostgroup_init(struct schedtune *st, int idx) bg = &per_cpu(cpu_boost_groups, cpu); bg->group[idx].boost = 0; bg->group[idx].valid = true; + bg->group[idx].ts = 0; } /* Keep track of allocated boost groups */ From 9046d10e66a4f771113bb48213cc1c77f6460770 Mon Sep 17 00:00:00 2001 From: Quentin Perret Date: Tue, 4 Sep 2018 12:51:08 +0100 Subject: [PATCH 0531/1103] ANDROID: thermal: cpu_cooling: Migrate to using the EM framework The newly introduced Energy Model framework manages power cost tables in a generic way. Moreover, it supports a wide variety of models since the tables can come from DT, firmware, etc. On the other hand, the cpu_cooling subsystem manages its own power cost tables using only DT information. In order to avoid the duplication of data in the kernel, and in order to enable IPA with EMs comming from more than just DT, remove the private tables from cpu_cooling.c and migrate it to using the centralized EM framework. The case where the thermal subsystem is used without an Energy Model (cpufreq_cooling_ops) is handled by looking at CPUFreq's frequency table which is already a dependency for cpu_cooling.c anyway. Signed-off-by: Quentin Perret Change-Id: I064e4b0e56d1c52b82a4dc800a1fa90456429911 --- drivers/thermal/cpu_cooling.c | 262 +++++++++++----------------------- 1 file changed, 83 insertions(+), 179 deletions(-) diff --git a/drivers/thermal/cpu_cooling.c b/drivers/thermal/cpu_cooling.c index dfd23245f778..eb0d87f32bc7 100644 --- a/drivers/thermal/cpu_cooling.c +++ b/drivers/thermal/cpu_cooling.c @@ -31,6 +31,7 @@ #include #include #include +#include #include @@ -48,19 +49,6 @@ * ... */ -/** - * struct freq_table - frequency table along with power entries - * @frequency: frequency in KHz - * @power: power in mW - * - * This structure is built when the cooling device registers and helps - * in translating frequency to power and vice versa. - */ -struct freq_table { - u32 frequency; - u32 power; -}; - /** * struct time_in_idle - Idle time stats * @time: previous reading of the absolute time that this cpu was idle @@ -82,7 +70,7 @@ struct time_in_idle { * frequency. * @max_level: maximum cooling level. One less than total number of valid * cpufreq frequencies. - * @freq_table: Freq table in descending order of frequencies + * @em: Reference on the Energy Model of the device * @cdev: thermal_cooling_device pointer to keep track of the * registered cooling device. * @policy: cpufreq policy. @@ -98,7 +86,7 @@ struct cpufreq_cooling_device { unsigned int cpufreq_state; unsigned int clipped_freq; unsigned int max_level; - struct freq_table *freq_table; /* In descending order */ + struct em_perf_domain *em; struct thermal_cooling_device *cdev; struct cpufreq_policy *policy; struct list_head node; @@ -111,26 +99,6 @@ static LIST_HEAD(cpufreq_cdev_list); /* Below code defines functions to be used for cpufreq as cooling device */ -/** - * get_level: Find the level for a particular frequency - * @cpufreq_cdev: cpufreq_cdev for which the property is required - * @freq: Frequency - * - * Return: level corresponding to the frequency. - */ -static unsigned long get_level(struct cpufreq_cooling_device *cpufreq_cdev, - unsigned int freq) -{ - struct freq_table *freq_table = cpufreq_cdev->freq_table; - unsigned long level; - - for (level = 1; level <= cpufreq_cdev->max_level; level++) - if (freq > freq_table[level].frequency) - break; - - return level - 1; -} - /** * cpufreq_thermal_notifier - notifier callback for cpufreq policy change. * @nb: struct notifier_block * with callback info. @@ -184,105 +152,52 @@ static int cpufreq_thermal_notifier(struct notifier_block *nb, return NOTIFY_OK; } +#ifdef CONFIG_ENERGY_MODEL /** - * update_freq_table() - Update the freq table with power numbers - * @cpufreq_cdev: the cpufreq cooling device in which to update the table - * @capacitance: dynamic power coefficient for these cpus - * - * Update the freq table with power numbers. This table will be used in - * cpu_power_to_freq() and cpu_freq_to_power() to convert between power and - * frequency efficiently. Power is stored in mW, frequency in KHz. The - * resulting table is in descending order. + * get_level: Find the level for a particular frequency + * @cpufreq_cdev: cpufreq_cdev for which the property is required + * @freq: Frequency * - * Return: 0 on success, -EINVAL if there are no OPPs for any CPUs, - * or -ENOMEM if we run out of memory. + * Return: level corresponding to the frequency. */ -static int update_freq_table(struct cpufreq_cooling_device *cpufreq_cdev, - u32 capacitance) +static unsigned long get_level(struct cpufreq_cooling_device *cpufreq_cdev, + unsigned int freq) { - struct freq_table *freq_table = cpufreq_cdev->freq_table; - struct dev_pm_opp *opp; - struct device *dev = NULL; - int num_opps = 0, cpu = cpufreq_cdev->policy->cpu, i; - - dev = get_cpu_device(cpu); - if (unlikely(!dev)) { - dev_warn(&cpufreq_cdev->cdev->device, - "No cpu device for cpu %d\n", cpu); - return -ENODEV; - } - - num_opps = dev_pm_opp_get_opp_count(dev); - if (num_opps < 0) - return num_opps; - - /* - * The cpufreq table is also built from the OPP table and so the count - * should match. - */ - if (num_opps != cpufreq_cdev->max_level + 1) { - dev_warn(dev, "Number of OPPs not matching with max_levels\n"); - return -EINVAL; - } - - for (i = 0; i <= cpufreq_cdev->max_level; i++) { - unsigned long freq = freq_table[i].frequency * 1000; - u32 freq_mhz = freq_table[i].frequency / 1000; - u64 power; - u32 voltage_mv; - - /* - * Find ceil frequency as 'freq' may be slightly lower than OPP - * freq due to truncation while converting to kHz. - */ - opp = dev_pm_opp_find_freq_ceil(dev, &freq); - if (IS_ERR(opp)) { - dev_err(dev, "failed to get opp for %lu frequency\n", - freq); - return -EINVAL; - } - - voltage_mv = dev_pm_opp_get_voltage(opp) / 1000; - dev_pm_opp_put(opp); - - /* - * Do the multiplication with MHz and millivolt so as - * to not overflow. - */ - power = (u64)capacitance * freq_mhz * voltage_mv * voltage_mv; - do_div(power, 1000000000); + int i; - /* power is stored in mW */ - freq_table[i].power = power; + for (i = cpufreq_cdev->max_level - 1; i >= 0; i--) { + if (freq > cpufreq_cdev->em->table[i].frequency) + break; } - return 0; + return cpufreq_cdev->max_level - i - 1; } + static u32 cpu_freq_to_power(struct cpufreq_cooling_device *cpufreq_cdev, u32 freq) { int i; - struct freq_table *freq_table = cpufreq_cdev->freq_table; - for (i = 1; i <= cpufreq_cdev->max_level; i++) - if (freq > freq_table[i].frequency) + for (i = cpufreq_cdev->max_level - 1; i >= 0; i--) { + if (freq > cpufreq_cdev->em->table[i].frequency) break; + } - return freq_table[i - 1].power; + return cpufreq_cdev->em->table[i + 1].power; } static u32 cpu_power_to_freq(struct cpufreq_cooling_device *cpufreq_cdev, u32 power) { int i; - struct freq_table *freq_table = cpufreq_cdev->freq_table; - for (i = 1; i <= cpufreq_cdev->max_level; i++) - if (power > freq_table[i].power) + for (i = cpufreq_cdev->max_level - 1; i >= 0; i--) { + if (power > cpufreq_cdev->em->table[i].power) break; + } - return freq_table[i - 1].frequency; + return cpufreq_cdev->em->table[i + 1].frequency; } /** @@ -332,6 +247,7 @@ static u32 get_dynamic_power(struct cpufreq_cooling_device *cpufreq_cdev, raw_cpu_power = cpu_freq_to_power(cpufreq_cdev, freq); return (raw_cpu_power * cpufreq_cdev->last_load) / 100; } +#endif /* cpufreq cooling device callback functions are defined below */ @@ -374,6 +290,30 @@ static int cpufreq_get_cur_state(struct thermal_cooling_device *cdev, return 0; } +static unsigned int get_state_freq(struct cpufreq_cooling_device *cpufreq_cdev, + unsigned long state) +{ + struct cpufreq_policy *policy; + unsigned long idx; + +#ifdef CONFIG_ENERGY_MODEL + /* Use the Energy Model table if available */ + if (cpufreq_cdev->em) { + idx = cpufreq_cdev->max_level - state; + return cpufreq_cdev->em->table[idx].frequency; + } +#endif + + /* Otherwise, fallback on the CPUFreq table */ + policy = cpufreq_cdev->policy; + if (policy->freq_table_sorted == CPUFREQ_TABLE_SORTED_ASCENDING) + idx = cpufreq_cdev->max_level - state; + else + idx = state; + + return policy->freq_table[idx].frequency; +} + /** * cpufreq_set_cur_state - callback function to set the current cooling state. * @cdev: thermal cooling device pointer. @@ -398,7 +338,7 @@ static int cpufreq_set_cur_state(struct thermal_cooling_device *cdev, if (cpufreq_cdev->cpufreq_state == state) return 0; - clip_freq = cpufreq_cdev->freq_table[state].frequency; + clip_freq = get_state_freq(cpufreq_cdev, state); cpufreq_cdev->cpufreq_state = state; cpufreq_cdev->clipped_freq = clip_freq; @@ -407,6 +347,7 @@ static int cpufreq_set_cur_state(struct thermal_cooling_device *cdev, return 0; } +#ifdef CONFIG_ENERGY_MODEL /** * cpufreq_get_requested_power() - get the current power * @cdev: &thermal_cooling_device pointer @@ -497,7 +438,7 @@ static int cpufreq_state2power(struct thermal_cooling_device *cdev, struct thermal_zone_device *tz, unsigned long state, u32 *power) { - unsigned int freq, num_cpus; + unsigned int freq, num_cpus, idx; struct cpufreq_cooling_device *cpufreq_cdev = cdev->devdata; /* Request state should be less than max_level */ @@ -506,7 +447,8 @@ static int cpufreq_state2power(struct thermal_cooling_device *cdev, num_cpus = cpumask_weight(cpufreq_cdev->policy->cpus); - freq = cpufreq_cdev->freq_table[state].frequency; + idx = cpufreq_cdev->max_level - state; + freq = cpufreq_cdev->em->table[idx].frequency; *power = cpu_freq_to_power(cpufreq_cdev, freq) * num_cpus; return 0; @@ -553,14 +495,6 @@ static int cpufreq_power2state(struct thermal_cooling_device *cdev, return 0; } -/* Bind cpufreq callbacks to thermal cooling device ops */ - -static struct thermal_cooling_device_ops cpufreq_cooling_ops = { - .get_max_state = cpufreq_get_max_state, - .get_cur_state = cpufreq_get_cur_state, - .set_cur_state = cpufreq_set_cur_state, -}; - static struct thermal_cooling_device_ops cpufreq_power_cooling_ops = { .get_max_state = cpufreq_get_max_state, .get_cur_state = cpufreq_get_cur_state, @@ -569,32 +503,27 @@ static struct thermal_cooling_device_ops cpufreq_power_cooling_ops = { .state2power = cpufreq_state2power, .power2state = cpufreq_power2state, }; +#endif + +/* Bind cpufreq callbacks to thermal cooling device ops */ + +static struct thermal_cooling_device_ops cpufreq_cooling_ops = { + .get_max_state = cpufreq_get_max_state, + .get_cur_state = cpufreq_get_cur_state, + .set_cur_state = cpufreq_set_cur_state, +}; /* Notifier for cpufreq policy change */ static struct notifier_block thermal_cpufreq_notifier_block = { .notifier_call = cpufreq_thermal_notifier, }; -static unsigned int find_next_max(struct cpufreq_frequency_table *table, - unsigned int prev_max) -{ - struct cpufreq_frequency_table *pos; - unsigned int max = 0; - - cpufreq_for_each_valid_entry(pos, table) { - if (pos->frequency > max && pos->frequency < prev_max) - max = pos->frequency; - } - - return max; -} - /** * __cpufreq_cooling_register - helper function to create cpufreq cooling device * @np: a valid struct device_node to the cooling device device tree node * @policy: cpufreq policy * Normally this should be same as cpufreq policy->related_cpus. - * @capacitance: dynamic power coefficient for these cpus + * @try_model: true if a power model should be used * * This interface function registers the cpufreq cooling device with the name * "thermal-cpufreq-%x". This api can support multiple instances of cpufreq @@ -606,12 +535,12 @@ static unsigned int find_next_max(struct cpufreq_frequency_table *table, */ static struct thermal_cooling_device * __cpufreq_cooling_register(struct device_node *np, - struct cpufreq_policy *policy, u32 capacitance) + struct cpufreq_policy *policy, bool try_model) { struct thermal_cooling_device *cdev; struct cpufreq_cooling_device *cpufreq_cdev; char dev_name[THERMAL_NAME_LENGTH]; - unsigned int freq, i, num_cpus; + unsigned int i, num_cpus; int ret; struct thermal_cooling_device_ops *cooling_ops; bool first; @@ -645,54 +574,36 @@ __cpufreq_cooling_register(struct device_node *np, /* max_level is an index, not a counter */ cpufreq_cdev->max_level = i - 1; - cpufreq_cdev->freq_table = kmalloc_array(i, - sizeof(*cpufreq_cdev->freq_table), - GFP_KERNEL); - if (!cpufreq_cdev->freq_table) { - cdev = ERR_PTR(-ENOMEM); - goto free_idle_time; - } +#ifdef CONFIG_ENERGY_MODEL + if (try_model) { + struct em_perf_domain *em = em_cpu_get(policy->cpu); + + if (!em || !cpumask_equal(policy->cpus, to_cpumask(em->cpus))) { + cdev = ERR_PTR(-EINVAL); + goto free_idle_time; + } + cpufreq_cdev->em = em; + cooling_ops = &cpufreq_power_cooling_ops; + } else +#endif + cooling_ops = &cpufreq_cooling_ops; ret = ida_simple_get(&cpufreq_ida, 0, 0, GFP_KERNEL); if (ret < 0) { cdev = ERR_PTR(ret); - goto free_table; + goto free_idle_time; } cpufreq_cdev->id = ret; snprintf(dev_name, sizeof(dev_name), "thermal-cpufreq-%d", cpufreq_cdev->id); - /* Fill freq-table in descending order of frequencies */ - for (i = 0, freq = -1; i <= cpufreq_cdev->max_level; i++) { - freq = find_next_max(policy->freq_table, freq); - cpufreq_cdev->freq_table[i].frequency = freq; - - /* Warn for duplicate entries */ - if (!freq) - pr_warn("%s: table has duplicate entries\n", __func__); - else - pr_debug("%s: freq:%u KHz\n", __func__, freq); - } - - if (capacitance) { - ret = update_freq_table(cpufreq_cdev, capacitance); - if (ret) { - cdev = ERR_PTR(ret); - goto remove_ida; - } - - cooling_ops = &cpufreq_power_cooling_ops; - } else { - cooling_ops = &cpufreq_cooling_ops; - } - cdev = thermal_of_cooling_device_register(np, dev_name, cpufreq_cdev, cooling_ops); if (IS_ERR(cdev)) goto remove_ida; - cpufreq_cdev->clipped_freq = cpufreq_cdev->freq_table[0].frequency; + cpufreq_cdev->clipped_freq = get_state_freq(cpufreq_cdev, 0); cpufreq_cdev->cdev = cdev; mutex_lock(&cooling_list_lock); @@ -709,8 +620,6 @@ __cpufreq_cooling_register(struct device_node *np, remove_ida: ida_simple_remove(&cpufreq_ida, cpufreq_cdev->id); -free_table: - kfree(cpufreq_cdev->freq_table); free_idle_time: kfree(cpufreq_cdev->idle_time); free_cdev: @@ -732,7 +641,7 @@ __cpufreq_cooling_register(struct device_node *np, struct thermal_cooling_device * cpufreq_cooling_register(struct cpufreq_policy *policy) { - return __cpufreq_cooling_register(NULL, policy, 0); + return __cpufreq_cooling_register(NULL, policy, false); } EXPORT_SYMBOL_GPL(cpufreq_cooling_register); @@ -760,7 +669,6 @@ of_cpufreq_cooling_register(struct cpufreq_policy *policy) { struct device_node *np = of_get_cpu_node(policy->cpu, NULL); struct thermal_cooling_device *cdev = NULL; - u32 capacitance = 0; if (!np) { pr_err("cpu_cooling: OF node not available for cpu%d\n", @@ -769,10 +677,7 @@ of_cpufreq_cooling_register(struct cpufreq_policy *policy) } if (of_find_property(np, "#cooling-cells", NULL)) { - of_property_read_u32(np, "dynamic-power-coefficient", - &capacitance); - - cdev = __cpufreq_cooling_register(np, policy, capacitance); + cdev = __cpufreq_cooling_register(np, policy, true); if (IS_ERR(cdev)) { pr_err("cpu_cooling: cpu%d is not running as cooling device: %ld\n", policy->cpu, PTR_ERR(cdev)); @@ -814,7 +719,6 @@ void cpufreq_cooling_unregister(struct thermal_cooling_device *cdev) thermal_cooling_device_unregister(cpufreq_cdev->cdev); ida_simple_remove(&cpufreq_ida, cpufreq_cdev->id); kfree(cpufreq_cdev->idle_time); - kfree(cpufreq_cdev->freq_table); kfree(cpufreq_cdev); } EXPORT_SYMBOL_GPL(cpufreq_cooling_unregister); From af260ce985c9f6ed2f58b13cef1f5ec547a9d102 Mon Sep 17 00:00:00 2001 From: Sathyanarayanan Kuppuswamy Date: Tue, 12 Sep 2017 12:12:17 -0700 Subject: [PATCH 0532/1103] init: do_mount_dm: Add logic to wait for block device enumeration Calling dm_setup_drives() before the completion of raw block device enumeration leads to "device lookup failed" error in dm_table_add_target() function, which in turn leads to incomplete device mapper configuration for the given block device. Without proper DM setup, if we try to mount the DM device, it leads to following kernel panic in mount_root() function due to invalid DM queue setup. This patch fixes this issue by adding a logic to wait for block device enumeration to complete before performing the verity checks. [ 0.875069] device-mapper: table: 253:0: verity: Data device lookup failed [ 0.883103] BUG: unable to handle kernel NULL pointer dereference at (null) [ 0.891854] IP: (null) [ 0.895453] PGD 0 [ 0.895454] P4D 0 [ 0.897686] [ 0.901574] Oops: 0010 [#1] PREEMPT SMP [ 0.905856] Modules linked in: [ 0.909266] CPU: 1 PID: 1 Comm: swapper/0 Tainted: G U W 4.13.0-rc4-quilt-2e5dc0ac #2 [ 0.919083] task: ffff968eb6a06040 task.stack: ffff9b9f40010000 [ 0.925698] RIP: 0010: (null) [ 0.929880] RSP: 0000:ffff9b9f40013a30 EFLAGS: 00010246 [ 0.935714] RAX: 0000000000000000 RBX: ffff968eb63620c0 RCX: 0000000000000000 [ 0.943674] RDX: 0000000000000000 RSI: ffff968eb63620c0 RDI: ffff968eb4a55370 [ 0.951649] RBP: ffff9b9f40013a88 R08: 0000000000000001 R09: ffff968eb6362130 [ 0.959623] R10: fffff5b2c9d28d00 R11: ffff968eb51fbec0 R12: ffff968eb4a55370 [ 0.967586] R13: 00000000ffffffff R14: 0000000000000000 R15: 0000000000000000 [ 0.975557] FS: 0000000000000000(0000) GS:ffff968ebfc80000(0000) knlGS:0000000000000000 [ 0.984588] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 [ 0.990997] CR2: 0000000000000000 CR3: 0000000185212000 CR4: 00000000003406e0 [ 0.998958] Call Trace: [ 1.001692] ? generic_make_request+0x122/0x320 [ 1.006753] submit_bio+0x73/0x160 [ 1.010550] submit_bh_wbc.isra.44+0x113/0x140 [ 1.015518] __bread_gfp+0x67/0x120 [ 1.019414] ext4_fill_super+0x184/0x3880 [ 1.023891] ? vsnprintf+0x201/0x490 [ 1.027885] ? set_bdev_super+0x30/0x30 [ 1.032170] ? snprintf+0x43/0x60 [ 1.035869] mount_bdev+0x17d/0x1b0 [ 1.039762] ? ext4_calculate_overhead+0x430/0x430 [ 1.045113] ext4_mount+0x15/0x20 [ 1.048813] mount_fs+0x153/0x180 [ 1.052513] vfs_kern_mount+0x90/0x180 [ 1.056699] do_mount+0x1e0/0xd00 [ 1.060397] ? _copy_from_user+0x60/0xb0 [ 1.064780] ? memdup_user+0x53/0x80 [ 1.068770] SyS_mount+0x94/0xd0 [ 1.072378] mount_block_root+0x105/0x2c4 [ 1.076857] mount_root+0x6d/0x71 [ 1.080555] prepare_namespace+0x172/0x19f [ 1.085127] kernel_init_freeable+0x21f/0x243 [ 1.089990] ? rest_init+0xd0/0xd0 [ 1.093777] kernel_init+0xe/0x100 [ 1.097575] ret_from_fork+0x27/0x40 [ 1.101563] Code: Bad RIP value. [ 1.105266] RIP: (null) RSP: ffff9b9f40013a30 [ 1.111089] CR2: 0000000000000000 [ 1.114789] ---[ end trace 7a237ef06917bfe3 ]--- [ 1.123581] Kernel panic - not syncing: Fatal exception [ 1.129510] reboot: panic mode set: p,w [ 1.133784] Kernel Offset: 0x25000000 from 0xffffffff81000000 (relocation range: 0xffffffff80000000-0xffffffffbfffffff) Fixes: e97f06fcd04b ("CHROMIUM: dm: boot time specification of dm=") Change-Id: I48ac7fc8ac752b4ebac25b8b21267f9ff9ba07bd Signed-off-by: Sathyanarayanan Kuppuswamy --- include/linux/device-mapper.h | 5 ++++ init/do_mounts_dm.c | 45 +++++++++++++++++++++++++++++++++++ 2 files changed, 50 insertions(+) diff --git a/include/linux/device-mapper.h b/include/linux/device-mapper.h index b7b047709918..63974da8c1ba 100644 --- a/include/linux/device-mapper.h +++ b/include/linux/device-mapper.h @@ -526,6 +526,11 @@ struct dm_table *dm_swap_table(struct mapped_device *md, */ void *dm_vcalloc(unsigned long nmemb, unsigned long elem_size); +/* + * Helper function to parse DM arguments + */ +int dm_split_args(int *argc, char ***argvp, char *input); + /*----------------------------------------------------------------- * Macros. *---------------------------------------------------------------*/ diff --git a/init/do_mounts_dm.c b/init/do_mounts_dm.c index af84b01ccfbc..cb63c625fd0a 100644 --- a/init/do_mounts_dm.c +++ b/init/do_mounts_dm.c @@ -343,6 +343,47 @@ static int __init dm_setup(char *str) return 0; } +/* Number of milliseconds to wait before checking for drive status */ +#define DM_DRIVE_WAIT 20 +/* Number of tries allowed to wait for DM drive, before timeout */ +#define DM_DRIVE_RETRY_COUNT 100 + +static int __init dm_wait_for_drive(char *params) +{ + char *dm_params; + int ret, argc = 0, try = 0; + char **argv; + dev_t dm_dev; + + dm_params = kstrndup(params, strlen(params), GFP_KERNEL); + if (!dm_params) + return -ENOMEM; + + ret = dm_split_args(&argc, &argv, dm_params); + if (ret || argc < 2) { + DMDEBUG("failed to get dm params"); + goto free_dm_params; + } + + dm_dev = dm_get_dev_t(argv[1]); + while (!dm_dev && try++ < DM_DRIVE_RETRY_COUNT) { + DMDEBUG("Waiting for device %s\n", argv[1]); + msleep(DM_DRIVE_WAIT); + dm_dev = dm_get_dev_t(argv[1]); + } + + if (!dm_dev) { + ret = -ENODEV; + goto free_dm_params; + } + + DMDEBUG("Device %s found\n", argv[1]); + +free_dm_params: + kfree(dm_params); + return ret; +} + static void __init dm_setup_drives(void) { struct mapped_device *md = NULL; @@ -382,6 +423,10 @@ static void __init dm_setup_drives(void) (unsigned long long) target->begin, (unsigned long long) target->length, target->type, target->params); + + if (dm_wait_for_drive(target->params)) + goto add_target_fail; + if (dm_table_add_target(table, target->type, target->begin, target->length, From 90d5afa1c94826ae3d0f8dd79f60c569a647ba55 Mon Sep 17 00:00:00 2001 From: "hangyu.li" Date: Wed, 9 Aug 2017 10:41:48 +0000 Subject: [PATCH 0533/1103] staging/android: add kernel config SYNC The VTS case VtsKernelConfigTest#testKernelConfigs needs the kernel configs conformed to Android requirements as in android-base.cfg. Change-Id: Ia0e5ce86ec8864a78a49a9e5e5e43aa56d70c16f Signed-off-by: Pan, Kris --- drivers/staging/android/Kconfig | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drivers/staging/android/Kconfig b/drivers/staging/android/Kconfig index 17c5587805f5..2cf17c1dff86 100644 --- a/drivers/staging/android/Kconfig +++ b/drivers/staging/android/Kconfig @@ -23,6 +23,14 @@ config ANDROID_VSOC a 'cuttlefish' Android image inside QEmu. The driver interacts with a QEmu ivshmem device. If built as a module, it will be called vsoc. +config SYNC + bool "Synchronization framework" + default n + ---help--- + This option enables the framework for synchronization between multiple + drivers. Sync implementations can take advantage of hardware + synchronization built into devices like GPUs. + source "drivers/staging/android/ion/Kconfig" endif # if ANDROID From a3a2b70d0065535fcce563010483ce90905ba012 Mon Sep 17 00:00:00 2001 From: Yu Ning Date: Wed, 29 Nov 2017 17:05:47 +0800 Subject: [PATCH 0534/1103] staging: android: Add fwdata driver In order to enable Android first stage mount (early mount) for Intel physical platforms (Gordon Peak, etc.), import the goldfish_fwdata driver from AOSP kernel/goldfish.git (branch android-goldfish-3.18), and remove its dependency on the Goldfish virtual platform (CONFIG_GOLDFISH). For more details, see the original AOSP patch that introduced goldfish_fwdata: https://r.android.com/442393 as well as: https://r.android.com/442315 + Fix a few warnings and style issues caught by checkpatch.pl. Change-Id: Ib882cb76f26726c41b49cbf14ffde803eab5d28d Tracked-On: Signed-off-by: Yu Ning Signed-off-by: Pan, Kris --- drivers/staging/android/Kconfig | 15 ++ drivers/staging/android/Makefile | 1 + drivers/staging/android/fwdata.c | 308 +++++++++++++++++++++++++++++++ 3 files changed, 324 insertions(+) create mode 100644 drivers/staging/android/fwdata.c diff --git a/drivers/staging/android/Kconfig b/drivers/staging/android/Kconfig index 2cf17c1dff86..0c5f13dc3acf 100644 --- a/drivers/staging/android/Kconfig +++ b/drivers/staging/android/Kconfig @@ -31,6 +31,21 @@ config SYNC drivers. Sync implementations can take advantage of hardware synchronization built into devices like GPUs. +config ANDROID_FWDATA + tristate "Parser for Android-specific firmware data" + depends on ACPI + default n + ---help--- + This driver parses Android-specific data (e.g. fstab configuration) + stored in firmware (e.g. ACPI tables), and present it to user space + via sysfs. Android Oreo (8.0) and later requires some essential boot- + time configuration to be available in a directory structure organized + in Device Tree style, e.g. /proc/device-tree/firmware/android/ on + platforms that enable DT. Platforms that use ACPI instead of DT, such + as Goldfish (Ranchu) x86/x86_64, should enable this driver to ensure + the required information can be found in sysfs with the expected + layout. + source "drivers/staging/android/ion/Kconfig" endif # if ANDROID diff --git a/drivers/staging/android/Makefile b/drivers/staging/android/Makefile index 90e6154f11a4..341bfa11316e 100644 --- a/drivers/staging/android/Makefile +++ b/drivers/staging/android/Makefile @@ -4,3 +4,4 @@ obj-y += ion/ obj-$(CONFIG_ASHMEM) += ashmem.o obj-$(CONFIG_ANDROID_VSOC) += vsoc.o +obj-$(CONFIG_ANDROID_FWDATA) += fwdata.o diff --git a/drivers/staging/android/fwdata.c b/drivers/staging/android/fwdata.c new file mode 100644 index 000000000000..28595eca1781 --- /dev/null +++ b/drivers/staging/android/fwdata.c @@ -0,0 +1,308 @@ +/* + * Copyright (C) 2017 Intel, Inc. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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. + * + */ + +#include +#include +#include +#include +#include + +struct android_fwdata_state { + struct device *dev; + struct kobject *properties_kobj; + struct kobject *android_kobj; + struct kobject *fstab_kobj; + struct kobject *system_kobj; + struct kobject *vendor_kobj; +}; + +static struct android_fwdata_state state; + +/* Called when /properties// is read. */ +static ssize_t property_show(struct kobject *kobj, struct kobj_attribute *attr, + char *buf) +{ + const char *prefix = NULL; + char key[128]; + const char *value = NULL; + int ret; + + /* It would be much more convenient if show() gave us the relative path + * to the file being read, e.g. properties/android/fstab/system/dev, + * which could be easily converted to a property key. + * TODO: Infer the relative path from kobj and remove all hard-coded + * property keys. + */ + if (kobj == state.android_kobj) { + prefix = "android"; + } else if (kobj == state.fstab_kobj) { + prefix = "android.fstab"; + } else if (kobj == state.system_kobj) { + prefix = "android.fstab.system"; + } else if (kobj == state.vendor_kobj) { + prefix = "android.fstab.vendor"; + } else { + pr_err("%s: Unexpected folder\n", __func__); + return -EINVAL; + } + /* We don't put any file in properties/ directly, so prefix can't be + * empty. + */ + snprintf(key, sizeof(key), "%s.%s", prefix, attr->attr.name); + + ret = device_property_read_string(state.dev, key, &value); + if (ret) { + pr_err("%s: Failed to read property '%s', ret=%d\n", __func__, + key, ret); + return ret; + } + return scnprintf(buf, PAGE_SIZE, "%s\n", value); +} + +#define DT_COMPATIBLE_ATTR(_folder) \ + struct kobj_attribute _folder##_compatible_attr = { \ + .attr = { .name = "compatible", .mode = 0444, }, \ + .show = property_show, \ + } + +static DT_COMPATIBLE_ATTR(android); +static DT_COMPATIBLE_ATTR(fstab); +static DT_COMPATIBLE_ATTR(system); +static DT_COMPATIBLE_ATTR(vendor); + +#define FSTAB_PROPERTY_ATTR(_partition, _name) \ + struct kobj_attribute _partition##_##_name##_attr = { \ + .attr = { \ + .name = __stringify(_name), \ + .mode = 0444, \ + }, \ + .show = property_show, \ + } + +static FSTAB_PROPERTY_ATTR(system, dev); +static FSTAB_PROPERTY_ATTR(system, type); +static FSTAB_PROPERTY_ATTR(system, mnt_flags); +static FSTAB_PROPERTY_ATTR(system, fsmgr_flags); + +static FSTAB_PROPERTY_ATTR(vendor, dev); +static FSTAB_PROPERTY_ATTR(vendor, type); +static FSTAB_PROPERTY_ATTR(vendor, mnt_flags); +static FSTAB_PROPERTY_ATTR(vendor, fsmgr_flags); + +static struct attribute *system_attrs[] = { + &system_compatible_attr.attr, + &system_dev_attr.attr, + &system_type_attr.attr, + &system_mnt_flags_attr.attr, + &system_fsmgr_flags_attr.attr, + NULL, +}; + +static struct attribute_group system_group = { + .attrs = system_attrs, +}; + +static struct attribute *vendor_attrs[] = { + &vendor_compatible_attr.attr, + &vendor_dev_attr.attr, + &vendor_type_attr.attr, + &vendor_mnt_flags_attr.attr, + &vendor_fsmgr_flags_attr.attr, + NULL, +}; + +static struct attribute_group vendor_group = { + .attrs = vendor_attrs, +}; + +static struct kobject *create_folder(struct kobject *parent, const char *name) +{ + struct kobject *kobj; + + kobj = kobject_create_and_add(name, parent); + if (!kobj) { + pr_err("%s: Failed to create %s/\n", __func__, name); + return NULL; + } + return kobj; +} + +static struct kobject *create_folder_with_file(struct kobject *parent, + const char *name, + struct kobj_attribute *attr) +{ + struct kobject *kobj; + + kobj = create_folder(parent, name); + if (kobj) { + /* Note: Usually drivers should use device_create_file() rather + * than sysfs_create_file(), but the former does not support + * creating the file in a subfolder. + */ + int ret; + + ret = sysfs_create_file(kobj, &attr->attr); + if (ret) { + pr_err("%s: Failed to create %s/%s: ret=%d\n", __func__, + name, attr->attr.name, ret); + kobject_put(kobj); + return NULL; + } + } + return kobj; +} + +static void remove_folder_with_file(struct kobject *kobj, + struct kobj_attribute *attr) +{ + sysfs_remove_file(kobj, &attr->attr); + kobject_put(kobj); +} + +static struct kobject *create_folder_with_files(struct kobject *parent, + const char *name, + struct attribute_group *group) +{ + struct kobject *kobj; + + kobj = create_folder(parent, name); + if (kobj) { + /* Note: Usually drivers should use device_add_groups() rather + * than sysfs_create_group(), but the former does not support + * creating the folder in a subfolder. + */ + int ret; + + ret = sysfs_create_group(kobj, group); + if (ret) { + pr_err("%s: Failed to create %s/*: ret=%d\n", __func__, + name, ret); + kobject_put(kobj); + return NULL; + } + } + return kobj; +} + +static void remove_folder_with_files(struct kobject *kobj, + struct attribute_group *group) +{ + sysfs_remove_group(kobj, group); + kobject_put(kobj); +} + +static void clean_up(void) +{ + if (state.vendor_kobj) { + /* Delete /properties/android/fstab/vendor/ */ + remove_folder_with_files(state.vendor_kobj, &vendor_group); + state.vendor_kobj = NULL; + } + if (state.system_kobj) { + /* Delete /properties/android/fstab/system/ */ + remove_folder_with_files(state.system_kobj, &system_group); + state.system_kobj = NULL; + } + if (state.fstab_kobj) { + /* Delete /properties/android/fstab/ */ + remove_folder_with_file(state.fstab_kobj, + &fstab_compatible_attr); + state.fstab_kobj = NULL; + } + if (state.android_kobj) { + /* Delete /properties/android/ */ + remove_folder_with_file(state.android_kobj, + &android_compatible_attr); + state.android_kobj = NULL; + } + if (state.properties_kobj) { + /* Delete /properties/ */ + kobject_put(state.properties_kobj); + state.properties_kobj = NULL; + } +} + +static int android_fwdata_probe(struct platform_device *pdev) +{ + int ret = -EIO; + + state.dev = &pdev->dev; + /* Create /properties/ */ + state.properties_kobj = create_folder(&state.dev->kobj, "properties"); + if (!state.properties_kobj) + goto out; + + /* TODO: Iterate over all device properties in firmware, and dynamically + * create sysfs nodes under /properties/ + */ + + /* Create /properties/android/compatible */ + state.android_kobj = create_folder_with_file(state.properties_kobj, + "android", + &android_compatible_attr); + if (!state.android_kobj) + goto out; + + /* Create /properties/android/fstab/compatible */ + state.fstab_kobj = create_folder_with_file(state.android_kobj, "fstab", + &fstab_compatible_attr); + if (!state.fstab_kobj) + goto out; + + if (device_property_present(state.dev, "android.fstab.system.dev")) { + /* Firmware contains fstab config for early mount of /system */ + state.system_kobj = create_folder_with_files(state.fstab_kobj, + "system", + &system_group); + if (!state.system_kobj) + goto out; + } + if (device_property_present(state.dev, "android.fstab.vendor.dev")) { + /* Firmware contains fstab config for early mount of /vendor */ + state.vendor_kobj = create_folder_with_files(state.fstab_kobj, + "vendor", + &vendor_group); + if (!state.vendor_kobj) + goto out; + } + return 0; + +out: + clean_up(); + return ret; +} + +static int android_fwdata_remove(struct platform_device *pdev) +{ + clean_up(); + return 0; +} + +static const struct acpi_device_id android_fwdata_acpi_match[] = { + { "ANDR0001", 0 }, + { }, +}; +MODULE_DEVICE_TABLE(acpi, android_fwdata_acpi_match); + +static struct platform_driver android_fwdata_driver = { + .probe = android_fwdata_probe, + .remove = android_fwdata_remove, + .driver = { + .name = "android_fwdata", + .owner = THIS_MODULE, + .acpi_match_table = ACPI_PTR(android_fwdata_acpi_match), + } +}; + +module_platform_driver(android_fwdata_driver); From d91a0e24bcab7b740cf45a981942094071619ee8 Mon Sep 17 00:00:00 2001 From: Yu Ning Date: Thu, 30 Nov 2017 11:28:30 +0800 Subject: [PATCH 0535/1103] staging: android: fwdata: Support vbmeta data Besides android.fstab.*, Intel physical platforms (Gordon Peak, etc.) also encode Android Verified Boot configuration in firmware (i.e. the android.vbmeta.{compatible, parts} properties packaged in ACPI _DSD), and need the fwdata driver to present this data to user space. Refactor the macros for declaring sysfs attributes, and add a new, optional attribute group for vbmeta. Change-Id: Iabc7a4c8f6ce813bb17f4d7d8382c14299bbce31 Tracked-On: Signed-off-by: Yu Ning --- drivers/staging/android/fwdata.c | 66 ++++++++++++++++++++++---------- 1 file changed, 45 insertions(+), 21 deletions(-) diff --git a/drivers/staging/android/fwdata.c b/drivers/staging/android/fwdata.c index 28595eca1781..1f06c615a64e 100644 --- a/drivers/staging/android/fwdata.c +++ b/drivers/staging/android/fwdata.c @@ -22,6 +22,7 @@ struct android_fwdata_state { struct device *dev; struct kobject *properties_kobj; struct kobject *android_kobj; + struct kobject *vbmeta_kobj; struct kobject *fstab_kobj; struct kobject *system_kobj; struct kobject *vendor_kobj; @@ -46,6 +47,8 @@ static ssize_t property_show(struct kobject *kobj, struct kobj_attribute *attr, */ if (kobj == state.android_kobj) { prefix = "android"; + } else if (kobj == state.vbmeta_kobj) { + prefix = "android.vbmeta"; } else if (kobj == state.fstab_kobj) { prefix = "android.fstab"; } else if (kobj == state.system_kobj) { @@ -70,19 +73,8 @@ static ssize_t property_show(struct kobject *kobj, struct kobj_attribute *attr, return scnprintf(buf, PAGE_SIZE, "%s\n", value); } -#define DT_COMPATIBLE_ATTR(_folder) \ - struct kobj_attribute _folder##_compatible_attr = { \ - .attr = { .name = "compatible", .mode = 0444, }, \ - .show = property_show, \ - } - -static DT_COMPATIBLE_ATTR(android); -static DT_COMPATIBLE_ATTR(fstab); -static DT_COMPATIBLE_ATTR(system); -static DT_COMPATIBLE_ATTR(vendor); - -#define FSTAB_PROPERTY_ATTR(_partition, _name) \ - struct kobj_attribute _partition##_##_name##_attr = { \ +#define DT_SIMPLE_ATTR(_prefix, _name) \ + struct kobj_attribute _prefix##_##_name##_attr = { \ .attr = { \ .name = __stringify(_name), \ .mode = 0444, \ @@ -90,15 +82,33 @@ static DT_COMPATIBLE_ATTR(vendor); .show = property_show, \ } -static FSTAB_PROPERTY_ATTR(system, dev); -static FSTAB_PROPERTY_ATTR(system, type); -static FSTAB_PROPERTY_ATTR(system, mnt_flags); -static FSTAB_PROPERTY_ATTR(system, fsmgr_flags); +static DT_SIMPLE_ATTR(android, compatible); +static DT_SIMPLE_ATTR(vbmeta, compatible); +static DT_SIMPLE_ATTR(fstab, compatible); +static DT_SIMPLE_ATTR(system, compatible); +static DT_SIMPLE_ATTR(vendor, compatible); -static FSTAB_PROPERTY_ATTR(vendor, dev); -static FSTAB_PROPERTY_ATTR(vendor, type); -static FSTAB_PROPERTY_ATTR(vendor, mnt_flags); -static FSTAB_PROPERTY_ATTR(vendor, fsmgr_flags); +static DT_SIMPLE_ATTR(vbmeta, parts); + +static struct attribute *vbmeta_attrs[] = { + &vbmeta_compatible_attr.attr, + &vbmeta_parts_attr.attr, + NULL, +}; + +static struct attribute_group vbmeta_group = { + .attrs = vbmeta_attrs, +}; + +static DT_SIMPLE_ATTR(system, dev); +static DT_SIMPLE_ATTR(system, type); +static DT_SIMPLE_ATTR(system, mnt_flags); +static DT_SIMPLE_ATTR(system, fsmgr_flags); + +static DT_SIMPLE_ATTR(vendor, dev); +static DT_SIMPLE_ATTR(vendor, type); +static DT_SIMPLE_ATTR(vendor, mnt_flags); +static DT_SIMPLE_ATTR(vendor, fsmgr_flags); static struct attribute *system_attrs[] = { &system_compatible_attr.attr, @@ -220,6 +230,11 @@ static void clean_up(void) &fstab_compatible_attr); state.fstab_kobj = NULL; } + if (state.vbmeta_kobj) { + /* Delete /properties/android/vbmeta/ */ + remove_folder_with_files(state.vbmeta_kobj, &vbmeta_group); + state.vbmeta_kobj = NULL; + } if (state.android_kobj) { /* Delete /properties/android/ */ remove_folder_with_file(state.android_kobj, @@ -254,6 +269,15 @@ static int android_fwdata_probe(struct platform_device *pdev) if (!state.android_kobj) goto out; + if (device_property_present(state.dev, "android.vbmeta.compatible")) { + /* Firmware contains vbmeta config for AVB 2.0 */ + state.vbmeta_kobj = create_folder_with_files(state.android_kobj, + "vbmeta", + &vbmeta_group); + if (!state.vbmeta_kobj) + goto out; + } + /* Create /properties/android/fstab/compatible */ state.fstab_kobj = create_folder_with_file(state.android_kobj, "fstab", &fstab_compatible_attr); From 90f659b7512b3f567769d050c608f0f9c830663c Mon Sep 17 00:00:00 2001 From: biyilix Date: Tue, 21 Aug 2018 14:01:30 +0800 Subject: [PATCH 0536/1103] staging: android: fwdata: Support product and odm Android P need the earlymount supports the product partition and the odm partition Tracked-On: OAM-67693 Signed-off-by: biyilix --- drivers/staging/android/fwdata.c | 70 ++++++++++++++++++++++++++++++++ 1 file changed, 70 insertions(+) diff --git a/drivers/staging/android/fwdata.c b/drivers/staging/android/fwdata.c index 1f06c615a64e..525f7e92ec84 100644 --- a/drivers/staging/android/fwdata.c +++ b/drivers/staging/android/fwdata.c @@ -26,6 +26,8 @@ struct android_fwdata_state { struct kobject *fstab_kobj; struct kobject *system_kobj; struct kobject *vendor_kobj; + struct kobject *product_kobj; + struct kobject *odm_kobj; }; static struct android_fwdata_state state; @@ -55,6 +57,10 @@ static ssize_t property_show(struct kobject *kobj, struct kobj_attribute *attr, prefix = "android.fstab.system"; } else if (kobj == state.vendor_kobj) { prefix = "android.fstab.vendor"; + } else if (kobj == state.product_kobj) { + prefix = "android.fstab.product"; + } else if (kobj == state.odm_kobj) { + prefix = "android.fstab.odm"; } else { pr_err("%s: Unexpected folder\n", __func__); return -EINVAL; @@ -87,6 +93,8 @@ static DT_SIMPLE_ATTR(vbmeta, compatible); static DT_SIMPLE_ATTR(fstab, compatible); static DT_SIMPLE_ATTR(system, compatible); static DT_SIMPLE_ATTR(vendor, compatible); +static DT_SIMPLE_ATTR(product, compatible); +static DT_SIMPLE_ATTR(odm, compatible); static DT_SIMPLE_ATTR(vbmeta, parts); @@ -110,6 +118,16 @@ static DT_SIMPLE_ATTR(vendor, type); static DT_SIMPLE_ATTR(vendor, mnt_flags); static DT_SIMPLE_ATTR(vendor, fsmgr_flags); +static DT_SIMPLE_ATTR(product, dev); +static DT_SIMPLE_ATTR(product, type); +static DT_SIMPLE_ATTR(product, mnt_flags); +static DT_SIMPLE_ATTR(product, fsmgr_flags); + +static DT_SIMPLE_ATTR(odm, dev); +static DT_SIMPLE_ATTR(odm, type); +static DT_SIMPLE_ATTR(odm, mnt_flags); +static DT_SIMPLE_ATTR(odm, fsmgr_flags); + static struct attribute *system_attrs[] = { &system_compatible_attr.attr, &system_dev_attr.attr, @@ -136,6 +154,32 @@ static struct attribute_group vendor_group = { .attrs = vendor_attrs, }; +static struct attribute *product_attrs[] = { + &product_compatible_attr.attr, + &product_dev_attr.attr, + &product_type_attr.attr, + &product_mnt_flags_attr.attr, + &product_fsmgr_flags_attr.attr, + NULL, +}; + +static struct attribute_group product_group = { + .attrs = product_attrs, +}; + +static struct attribute *odm_attrs[] = { + &odm_compatible_attr.attr, + &odm_dev_attr.attr, + &odm_type_attr.attr, + &odm_mnt_flags_attr.attr, + &odm_fsmgr_flags_attr.attr, + NULL, +}; + +static struct attribute_group odm_group = { + .attrs = odm_attrs, +}; + static struct kobject *create_folder(struct kobject *parent, const char *name) { struct kobject *kobj; @@ -219,6 +263,16 @@ static void clean_up(void) remove_folder_with_files(state.vendor_kobj, &vendor_group); state.vendor_kobj = NULL; } + if (state.product_kobj) { + /* Delete /properties/android/fstab/product/ */ + remove_folder_with_files(state.product_kobj, &product_group); + state.product_kobj = NULL; + } + if (state.odm_kobj) { + /* Delete /properties/android/fstab/odm/ */ + remove_folder_with_files(state.odm_kobj, &odm_group); + state.odm_kobj = NULL; + } if (state.system_kobj) { /* Delete /properties/android/fstab/system/ */ remove_folder_with_files(state.system_kobj, &system_group); @@ -300,6 +354,22 @@ static int android_fwdata_probe(struct platform_device *pdev) if (!state.vendor_kobj) goto out; } + if (device_property_present(state.dev, "android.fstab.product.dev")) { + /* Firmware contains fstab config for early mount of /product */ + state.product_kobj = create_folder_with_files(state.fstab_kobj, + "product", + &product_group); + if (!state.product_kobj) + goto out; + } + if (device_property_present(state.dev, "android.fstab.odm.dev")) { + /* Firmware contains fstab config for early mount of /odm */ + state.odm_kobj = create_folder_with_files(state.fstab_kobj, + "odm", + &odm_group); + if (!state.odm_kobj) + goto out; + } return 0; out: From e40e8440aab9cc6d2a396dd69d16d5024d9edb11 Mon Sep 17 00:00:00 2001 From: Traian Schiau Date: Thu, 31 Mar 2016 11:30:56 +0300 Subject: [PATCH 0537/1103] dvctrace-bus: DvC-Trace bus driver DvC Trace is part of USB Debug class which provides a way of sending trace data via USB. This adds a pseudobus for devices/drivers capable of sending traces over such interface. Signed-off-by: Traian Schiau Signed-off-by: Tian, Baofeng --- Documentation/ABI/testing/sysfs-bus-dvctrace | 26 ++ MAINTAINERS | 7 + drivers/bus/Kconfig | 24 ++ drivers/bus/Makefile | 2 + drivers/bus/dvctrace.c | 278 +++++++++++++++++++ include/linux/dvctrace.h | 134 +++++++++ 6 files changed, 471 insertions(+) create mode 100644 Documentation/ABI/testing/sysfs-bus-dvctrace create mode 100644 drivers/bus/dvctrace.c create mode 100644 include/linux/dvctrace.h diff --git a/Documentation/ABI/testing/sysfs-bus-dvctrace b/Documentation/ABI/testing/sysfs-bus-dvctrace new file mode 100644 index 000000000000..0abdd3293364 --- /dev/null +++ b/Documentation/ABI/testing/sysfs-bus-dvctrace @@ -0,0 +1,26 @@ +What: /sys/bus/dvctrace +Date: May 2015 +KernelVersion: 4.0 +Contact: Traian Schiau +Description: Groups the devices and drivers registered to + to dvc-trace bus. + +What: /sys/bus/dvctrace/devices//status +Date: May 2015 +KernelVersion: 4.0 +Contact: Traian Schiau +Description: (R) The status of a dvc-trace source device with + respect to an USB function driver. + Free - The device is free + Reserved - The device is reserved by an USB + function but not in use. + In use - The device is used by an USB function. + +What: /sys/bus/dvctrace/devices//protocol +Date: May 2015 +KernelVersion: 4.0 +Contact: Traian Schiau +Description: (RW) The protocol id of a dvc-trace source device, + this will used in function driver interface + descriptors (u8). According to USB debug class + specification the protocol id is vendor specific. diff --git a/MAINTAINERS b/MAINTAINERS index 48a65c3a4189..1f22e788fffd 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -5129,6 +5129,13 @@ S: Maintained F: drivers/media/usb/dvb-usb-v2/dvb_usb* F: drivers/media/usb/dvb-usb-v2/usb_urb.c +DVC_TRACE BUS DRIVER +M: Traian Schiau +S: Maintained +F: drivers/bus/dvctrace.c +F: include/linux/dvctrace.h +F: Documentation/ABI/testing/sysfs-bus-dvctrace + DYNAMIC DEBUG M: Jason Baron S: Maintained diff --git a/drivers/bus/Kconfig b/drivers/bus/Kconfig index 1851112ccc29..16f0e0f0d59c 100644 --- a/drivers/bus/Kconfig +++ b/drivers/bus/Kconfig @@ -183,4 +183,28 @@ config DA8XX_MSTPRI source "drivers/bus/fsl-mc/Kconfig" +config DVC_TRACE_BUS + bool "DvC-Trace pseudobus" + default n + depends on USB_GADGET + help + DvC-Trace pseudobus is meant to group devices capable of sending + trace data via a USB DvC-Trace gadget function. + An usb function driver will be able to choose a source device and + provide the means to transfer the data. + + Say Y to enable it. + +config DVC_TRACE_BUS_DEBUG + bool "DvC-Trace pseudobus debug" + default n + depends on DVC_TRACE_BUS + help + DvC-Trace pseudobus is meant to group devices capable of sending + trace data via a USB DvC-Trace gadget function. + An usb function driver will be able to choose a source device and + provide the means to transfer the data. + + Say Y to enable extended debug messages in this driver. + endmenu diff --git a/drivers/bus/Makefile b/drivers/bus/Makefile index ca300b1914ce..cf118be0785f 100644 --- a/drivers/bus/Makefile +++ b/drivers/bus/Makefile @@ -32,3 +32,5 @@ obj-$(CONFIG_UNIPHIER_SYSTEM_BUS) += uniphier-system-bus.o obj-$(CONFIG_VEXPRESS_CONFIG) += vexpress-config.o obj-$(CONFIG_DA8XX_MSTPRI) += da8xx-mstpri.o +obj-$(CONFIG_DVC_TRACE_BUS) += dvctrace.o +subdir-ccflags-$(CONFIG_DVC_TRACE_BUS_DEBUG) += -DDVCT_DEBUG diff --git a/drivers/bus/dvctrace.c b/drivers/bus/dvctrace.c new file mode 100644 index 000000000000..c1770ca44422 --- /dev/null +++ b/drivers/bus/dvctrace.c @@ -0,0 +1,278 @@ +/* + * DvC-Trace(dvct) Bus driver + * + * Copyright (C) 2015, Intel Corporation. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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. + * + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": %s: " fmt, __func__ + +#include +#include +#include + +#ifdef DVCT_DEBUG +#define DVCT_IN() pr_debug("in\n") +#else +#define DVCT_IN() do {} while (0) +#endif + +static ssize_t protocol_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + DVCT_IN(); + return sprintf(buf, "%d\n", dev_to_dvct_source_device(dev)->protocol); +} + +static ssize_t protocol_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t size) +{ + struct dvct_source_device *ds_dev = dev_to_dvct_source_device(dev); + + DVCT_IN(); + if (ds_dev->instance_taken) + return -EBUSY; + + if (!kstrtou8(buf, 10, &ds_dev->protocol)) + return size; + + return -EINVAL; +} + +static DEVICE_ATTR_RW(protocol); + +static ssize_t status_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct dvct_source_device *ds_dev = dev_to_dvct_source_device(dev); + + DVCT_IN(); + if (ds_dev->instance_taken) { + if (ds_dev->function_taken) + return sprintf(buf, "In use\n"); + else + return sprintf(buf, "Reserved\n"); + } else { + return sprintf(buf, "Free\n"); + } +} + +static DEVICE_ATTR_RO(status); + +static struct attribute *dvct_source_attrs[] = { + &dev_attr_protocol.attr, + &dev_attr_status.attr, + NULL, +}; + +ATTRIBUTE_GROUPS(dvct_source); + + +static int dvct_match(struct device *dev, struct device_driver *drv) +{ + const char *devname = dev_name(dev); + + DVCT_IN(); + if (strlen(devname) <= strlen(drv->name)) + return -1; + if (strncmp(devname, drv->name, strlen(drv->name))) + return -1; + return devname[strlen(drv->name)] == '-'; +}; + +static struct bus_type dvctrace_bus_type = { + .name = "dvctrace", + .match = dvct_match, + .dev_groups = dvct_source_groups, +}; + +static struct device dvctrace_bus = { + .init_name = "dvctrace-bus", +}; + +static int dvct_match_free(struct device *dev, void *data) +{ + struct dvct_source_device *ds_dev = dev_to_dvct_source_device(dev); + + DVCT_IN(); + return !ds_dev->instance_taken; +} + +struct dvct_source_device *dvct_source_find_by_name(const char *name) +{ + struct device *dev; + + DVCT_IN(); + dev = bus_find_device_by_name(&dvctrace_bus_type, NULL, name); + if (IS_ERR_OR_NULL(dev)) + return ERR_PTR(-ENODEV); + return dev_to_dvct_source_device(dev); +} +EXPORT_SYMBOL_GPL(dvct_source_find_by_name); + +struct dvct_source_device *dvct_source_find_free_by_name(const char *name) +{ + struct dvct_source_device *ds_dev = dvct_source_find_by_name(name); + + DVCT_IN(); + if (IS_ERR_OR_NULL(ds_dev)) + return ERR_PTR(-ENODEV); + + if (ds_dev->instance_taken) + return ERR_PTR(-EBUSY); + + return ds_dev; +} +EXPORT_SYMBOL_GPL(dvct_source_find_free_by_name); + +struct dvct_source_device *dvct_source_find_free(void) +{ + struct device *dev = bus_find_device(&dvctrace_bus_type, NULL, + NULL, dvct_match_free); + DVCT_IN(); + if (IS_ERR_OR_NULL(dev)) + return ERR_PTR(-ENODEV); + + return dev_to_dvct_source_device(dev); +} +EXPORT_SYMBOL_GPL(dvct_source_find_free); + +static int fn_count_free(struct device *dev, void *data) +{ + int *count = data; + struct dvct_source_device *ds_dev = dev_to_dvct_source_device(dev); + + DVCT_IN(); + if (!ds_dev->instance_taken) + (*count)++; + return 0; +} + +int dvct_source_count_free(void) +{ + int count = 0; + + DVCT_IN(); + bus_for_each_dev(&dvctrace_bus_type, NULL, &count, fn_count_free); + return count; +} +EXPORT_SYMBOL_GPL(dvct_source_count_free); + +struct dvct_source_driver +*dvct_source_get_drv(struct dvct_source_device *ds_dev) +{ + BUG_ON(ds_dev->device.driver == NULL); + return drv_to_dvct_source_driver(ds_dev->device.driver); +} +EXPORT_SYMBOL_GPL(dvct_source_get_drv); + +int dvct_source_device_add(struct dvct_source_device *ds_dev, + struct dvct_source_driver *ds_drv) +{ + int ret; + + DVCT_IN(); + if (!ds_dev) + return -ENODEV; + if (!ds_drv) + return -EINVAL; + + spin_lock_init(&ds_dev->lock); + spin_lock(&ds_dev->lock); + ds_dev->instance_taken = 0; + ds_dev->function_taken = 0; + spin_unlock(&ds_dev->lock); + + device_initialize(&ds_dev->device); + ds_dev->device.bus = &dvctrace_bus_type; + + if (!ds_dev->device.parent) + ds_dev->device.parent = &dvctrace_bus; + + dev_set_name(&ds_dev->device, "%s-%s", ds_drv->driver.name, + ds_dev->name_add); + + ret = device_add(&ds_dev->device); + if (ret) { + dev_err(&dvctrace_bus, "Cannot add device %s %d\n", + ds_dev->name_add, ret); + return ret; + } + + dev_notice(&dvctrace_bus, "Adding device %s\n", ds_dev->name_add); + return 0; +}; +EXPORT_SYMBOL_GPL(dvct_source_device_add); + +void dvct_source_device_del(struct dvct_source_device *ds_dev) +{ + DVCT_IN(); + device_del(&ds_dev->device); +}; +EXPORT_SYMBOL_GPL(dvct_source_device_del); + +int __dvct_source_driver_register(struct dvct_source_driver *ds_drv, + struct module *owner) +{ + DVCT_IN(); + if (!ds_drv->activate || + !ds_drv->binded || + !ds_drv->start_transfer || + !ds_drv->stop_transfer || + !ds_drv->unbinded || + !ds_drv->deactivate) + return -EINVAL; + + ds_drv->driver.owner = owner; + ds_drv->driver.bus = &dvctrace_bus_type; + return driver_register(&ds_drv->driver); +} +EXPORT_SYMBOL_GPL(__dvct_source_driver_register); + +void dvct_source_driver_unregister(struct dvct_source_driver *ds_drv) +{ + DVCT_IN(); + driver_unregister(&ds_drv->driver); +} +EXPORT_SYMBOL_GPL(dvct_source_driver_unregister); + +static int __init dtb_init(void) +{ + int ret; + + DVCT_IN(); + ret = device_register(&dvctrace_bus); + if (ret) { + pr_err("Cannot register bus device %d\n", ret); + return ret; + } + + ret = bus_register(&dvctrace_bus_type); + if (ret) { + pr_err("Cannot register bus %d\n", ret); + return ret; + } + return 0; +} + +static void __exit dtb_exit(void) +{ + DVCT_IN(); + bus_unregister(&dvctrace_bus_type); +} + +subsys_initcall(dtb_init); +module_exit(dtb_exit); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("DvC-Trace bus implementation"); +MODULE_AUTHOR("Traian Schiau "); diff --git a/include/linux/dvctrace.h b/include/linux/dvctrace.h new file mode 100644 index 000000000000..18291efb18b6 --- /dev/null +++ b/include/linux/dvctrace.h @@ -0,0 +1,134 @@ +/* + * DvC.Trace(dvct) Bus + * + * Copyright (C) 2015, Intel Corporation. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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. + * + */ + +#ifndef __DVCTRACE_H +#define __DVCTRACE_H + +#include + +/** + * The function is binded to a gadget + * function-driver RW + * client-driver RO + */ +#define DVCT_MASK_ONLINE BIT(0) + +/** + * The client is currently transferring + * function-driver RO + * client-driver RW + */ +#define DVCT_MASK_TRANS BIT(1) + +/** + * An error was detected the client + * function-driver RO + * client-driver RW + */ +#define DVCT_MASK_ERR BIT(2) + +#define DVCT_MASK_ONLINE_TRANS (DVCT_MASK_ONLINE | DVCT_MASK_TRANS) +#define DVCT_MASK_ALL (DVCT_MASK_ONLINE | DVCT_MASK_TRANS | DVCT_MASK_ERR) + +/** + * Bitwise status manipulation macros + */ +#define dvct_set_status(s, m) atomic_set(s, atomic_read(s)|(m)) +#define dvct_clr_status(s, m) atomic_set(s, atomic_read(s) & (~(m))) +#define dvct_get_status(s, m) (atomic_read(s) & (m)) + +/** + * dvct_source_device - DvC Trace function to source (backend) interface + * + * @name_add: postfix used to compute the source name (-) + * @protocol: Interface protocol code (used in IAD, control and data + * + * @instance_taken, @function_taken, @lock, @device are reserved for + * internal use only. + */ +struct dvct_source_device { + const char *name_add; + u8 protocol; + u32 instance_taken:1; + u32 function_taken:1; + spinlock_t lock; + struct device device; +}; + +/** + * dvct_source_driver - DvC Trace source (backend) representation + * @activate: pre-bind callback called when the function instance linked to the + * source device is selected as a part of a configuration eg. + * ln -s cfgfs/.../functions/dvctrace.x cfgfs/.../configs/c.x/ + * atomic_t * , points to the status field of the function, the client + * should update relevant bits (DVCT_MASK_TRANS, DVCT_MASK_ERR). + * @binded: post-bind callback, at this stage the transfer endpoint is + * allocated; + * @connected: (Optional) The gadget is now connected to a host, the connection + * speed is available. + * @start_transfer: called upon receiving SET_TRACE, the host side should be + * able ready to receive data, the client could submit usb requests. + * @stop_transfer: host side request to stop the data transfer. + * @disconnected: (Optional) The gadget was disconnected from the host. + * Before calling this the function driver will call stop_trasfer if + * DVCT_MASK_TRANS is set. + * @unbinded: The USB-function is no longer used. + * @deactivate: The USB-function is no longer part of a configuration. + * + * If any of the non optional callbacks is not provided (NULL) driver + * registration will fail. + */ +struct dvct_source_driver { + struct device_driver driver; + int (*activate)(struct dvct_source_device *, atomic_t *); + int (*binded)(struct dvct_source_device *, struct usb_ep *, + struct usb_function *); + void (*connected)(struct dvct_source_device *, enum usb_device_speed); + int (*start_transfer)(struct dvct_source_device *, u8); + int (*stop_transfer)(struct dvct_source_device *); + void (*disconnected)(struct dvct_source_device *); + void (*unbinded)(struct dvct_source_device *); + void (*deactivate)(struct dvct_source_device *); +}; + +#define dev_to_dvct_source_device(ptr) \ + container_of(ptr, struct dvct_source_device, device) + +#define drv_to_dvct_source_driver(ptr) \ + container_of(ptr, struct dvct_source_driver, driver) + +extern struct dvct_source_driver +*dvct_source_get_drv(struct dvct_source_device *dev); + +#define dvct_source_driver_register(drv) \ + __dvct_source_driver_register(drv, THIS_MODULE) +extern int __dvct_source_driver_register(struct dvct_source_driver *, + struct module *); +extern void dvct_source_driver_unregister(struct dvct_source_driver *); +extern int dvct_source_device_add(struct dvct_source_device *, + struct dvct_source_driver *); +extern void dvct_source_device_del(struct dvct_source_device *); + +extern struct dvct_source_device *dvct_source_find_by_name(const char *name); + +extern struct dvct_source_device +*dvct_source_find_free_by_name(const char *name); + +extern struct dvct_source_device *dvct_source_find_free(void); + +extern int dvct_source_count_free(void); + +#endif /* __DVCTRACE_H*/ From 39de74970bd93757675ed977f4c49b2090b4c3e2 Mon Sep 17 00:00:00 2001 From: Traian Schiau Date: Thu, 31 Mar 2016 12:00:49 +0300 Subject: [PATCH 0538/1103] dvctrace-function: Implement DvC Trace USB Gadget function driver Implement DvC Trace function class driver. Instances of this function are able to connect to a dvctrace bus source driver. Signed-off-by: Traian Schiau Signed-off-by: Tian, Baofeng --- .../ABI/testing/configfs-usb-gadget-dvctrace | 9 + MAINTAINERS | 8 + drivers/usb/gadget/Kconfig | 11 + drivers/usb/gadget/function/Makefile | 9 + drivers/usb/gadget/function/f_dvctrace.c | 754 ++++++++++++++++++ drivers/usb/gadget/function/u_dvctrace.h | 72 ++ include/linux/usb/debug.h | 189 +++++ include/uapi/linux/usb/ch9.h | 1 + 8 files changed, 1053 insertions(+) create mode 100644 Documentation/ABI/testing/configfs-usb-gadget-dvctrace create mode 100644 drivers/usb/gadget/function/f_dvctrace.c create mode 100644 drivers/usb/gadget/function/u_dvctrace.h create mode 100644 include/linux/usb/debug.h diff --git a/Documentation/ABI/testing/configfs-usb-gadget-dvctrace b/Documentation/ABI/testing/configfs-usb-gadget-dvctrace new file mode 100644 index 000000000000..6391096ac151 --- /dev/null +++ b/Documentation/ABI/testing/configfs-usb-gadget-dvctrace @@ -0,0 +1,9 @@ +What: /config/usb-gadget//functions/dvctrace./source_dev +Date: Mar 2015 +KernelVersion: 4.0 +Contact: Traian Schiau +Description: (R) The name of the source device paired with this function + instance, if upon creation of the instance a source device + named exists and is free, the source device will be + associated with the current instance, otherwise the first free + source device will be used. diff --git a/MAINTAINERS b/MAINTAINERS index 1f22e788fffd..80899bee1ec8 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -5136,6 +5136,14 @@ F: drivers/bus/dvctrace.c F: include/linux/dvctrace.h F: Documentation/ABI/testing/sysfs-bus-dvctrace +DVC_TRACE USB_GADGET DRIVER +M: Traian Schiau +S: Maintained +F: drivers/usb/gadget/function/f_dvctrace.c +F: drivers/usb/gadget/function/u_dvctrace.h +F: include/linux/usb/debug.h +F: Documentation/ABI/testing/configfs-usb-gadget-dvctrace + DYNAMIC DEBUG M: Jason Baron S: Maintained diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig index 31cce7805eb2..7974363f3f10 100644 --- a/drivers/usb/gadget/Kconfig +++ b/drivers/usb/gadget/Kconfig @@ -215,6 +215,10 @@ config USB_F_PRINTER config USB_F_TCM tristate +config USB_F_DVCTRACE + tristate + select DVC_TRACE_BUS + # this first set of drivers all depend on bulk-capable hardware. config USB_CONFIGFS @@ -508,6 +512,13 @@ choice controller, and the relevant drivers for each function declared by the device. +config USB_CONFIGFS_F_DVCTRACE + bool "DvC Trace gadget" + depends on USB_CONFIGFS + select USB_F_DVCTRACE + help + USB gadget DvC Trace support + source "drivers/usb/gadget/legacy/Kconfig" endchoice diff --git a/drivers/usb/gadget/function/Makefile b/drivers/usb/gadget/function/Makefile index 5d3a6cf02218..2952adb51ebd 100644 --- a/drivers/usb/gadget/function/Makefile +++ b/drivers/usb/gadget/function/Makefile @@ -50,3 +50,12 @@ usb_f_printer-y := f_printer.o obj-$(CONFIG_USB_F_PRINTER) += usb_f_printer.o usb_f_tcm-y := f_tcm.o obj-$(CONFIG_USB_F_TCM) += usb_f_tcm.o +usb_f_mtp-y := f_mtp.o +obj-$(CONFIG_USB_F_MTP) += usb_f_mtp.o +usb_f_ptp-y := f_ptp.o +obj-$(CONFIG_USB_F_PTP) += usb_f_ptp.o +usb_f_audio_source-y := f_audio_source.o +obj-$(CONFIG_USB_F_AUDIO_SRC) += usb_f_audio_source.o +usb_f_accessory-y := f_accessory.o +obj-$(CONFIG_USB_F_ACC) += usb_f_accessory.o +obj-$(CONFIG_USB_F_DVCTRACE) += f_dvctrace.o diff --git a/drivers/usb/gadget/function/f_dvctrace.c b/drivers/usb/gadget/function/f_dvctrace.c new file mode 100644 index 000000000000..dffd06126d8f --- /dev/null +++ b/drivers/usb/gadget/function/f_dvctrace.c @@ -0,0 +1,754 @@ +/* + * Gadget Driver for DvC.Trace Function + * + * Copyright (C) 2015, Intel Corporation. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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. + * + */ +#define pr_fmt(fmt) KBUILD_MODNAME ": %s: " fmt, __func__ + +#ifdef VERBOSE_DEBUG +#define DVCT_IN() pr_debug("in\n") +#else +#define DVCT_IN() do {} while (0) +#endif + +#include +#include +#include +#include +#include "u_dvctrace.h" + +enum { + DVCT_IAD_DESC_POS, + DVCT_CITF_DESC_POS, + DVCT_DITF_DESC_POS, + DVCT_EP_DESC_POS, + DVCT_LS_NULL_DESC_POS, /*Low speed descriptors end with this one*/ + DVCT_EP_COMP_DESC_POS = DVCT_LS_NULL_DESC_POS, + DVCT_LS_DESC_COUNT, /*Count of low speed descriptors*/ + DVCT_NULL_DESC_POS = DVCT_LS_DESC_COUNT, + DVCT_HS_DESC_COUNT,/*Count of super speed descriptors*/ +}; + +enum { + DVCT_STR_IAD_IDX, + DVCT_STR_C_ITF_IDX, + DVCT_STR_D_ITF_IDX, + DVCT_STR_NULL_IDX, /*always last */ + DVCT_STR_COUNT, +}; + +static int dvct_alloc_desc(struct dvct_function *d_fun) +{ + struct dvct_function_desc *desc = &d_fun->desc; + + DVCT_IN(); + + desc->fs = + kzalloc(DVCT_LS_DESC_COUNT * sizeof(struct usb_descriptor_header *), + GFP_KERNEL); + if (!desc->fs) + goto err_fs; + + desc->hs = + kzalloc(DVCT_LS_DESC_COUNT * sizeof(struct usb_descriptor_header *), + GFP_KERNEL); + if (!desc->hs) + goto err_hs; + + desc->ss = + kzalloc(DVCT_HS_DESC_COUNT * sizeof(struct usb_descriptor_header *), + GFP_KERNEL); + if (!desc->ss) + goto err_ss; + + /*IAD */ + desc->iad = kzalloc(sizeof(*desc->iad), GFP_KERNEL); + if (!desc->iad) + goto err_iad; + + desc->iad->bLength = sizeof(*desc->iad); + desc->iad->bDescriptorType = USB_DT_INTERFACE_ASSOCIATION; + desc->iad->bInterfaceCount = 2; + desc->iad->bFunctionClass = USB_CLASS_DEBUG; + desc->iad->bFunctionSubClass = USB_SUBCLASS_DVC_TRACE; + desc->iad->bFunctionProtocol = d_fun->source_dev->protocol; + /*bFirstInterface - updated on bind */ + + desc->fs[DVCT_IAD_DESC_POS] = (struct usb_descriptor_header *)desc->iad; + desc->hs[DVCT_IAD_DESC_POS] = (struct usb_descriptor_header *)desc->iad; + desc->ss[DVCT_IAD_DESC_POS] = (struct usb_descriptor_header *)desc->iad; + + /*Control interface */ + desc->c_itf = kzalloc(sizeof(*desc->c_itf), GFP_KERNEL); + if (!desc->c_itf) + goto err_c_itf; + + desc->c_itf->bLength = USB_DT_INTERFACE_SIZE; + desc->c_itf->bDescriptorType = USB_DT_INTERFACE; + desc->c_itf->bInterfaceClass = USB_CLASS_DEBUG; + desc->c_itf->bInterfaceSubClass = USB_SUBCLASS_DEBUG_CONTROL; + desc->c_itf->bInterfaceProtocol = d_fun->source_dev->protocol; + + desc->fs[DVCT_CITF_DESC_POS] = + (struct usb_descriptor_header *)desc->c_itf; + desc->hs[DVCT_CITF_DESC_POS] = + (struct usb_descriptor_header *)desc->c_itf; + desc->ss[DVCT_CITF_DESC_POS] = + (struct usb_descriptor_header *)desc->c_itf; + + /*Data interface */ + desc->d_itf = kzalloc(sizeof(*desc->d_itf), GFP_KERNEL); + if (!desc->d_itf) + goto err_d_itf; + + desc->d_itf->bLength = USB_DT_INTERFACE_SIZE; + desc->d_itf->bDescriptorType = USB_DT_INTERFACE; + desc->d_itf->bNumEndpoints = 1; + desc->d_itf->bInterfaceClass = USB_CLASS_DEBUG; + desc->d_itf->bInterfaceSubClass = USB_SUBCLASS_DVC_TRACE; + desc->d_itf->bInterfaceProtocol = d_fun->source_dev->protocol; + + desc->fs[DVCT_DITF_DESC_POS] = + (struct usb_descriptor_header *)desc->d_itf; + desc->hs[DVCT_DITF_DESC_POS] = + (struct usb_descriptor_header *)desc->d_itf; + desc->ss[DVCT_DITF_DESC_POS] = + (struct usb_descriptor_header *)desc->d_itf; + + /*Full Speed ep */ + desc->fs_ep = kzalloc(sizeof(*desc->fs_ep), GFP_KERNEL); + if (!desc->fs_ep) + goto err_fs_ep; + + desc->fs_ep->bLength = USB_DT_ENDPOINT_SIZE; + desc->fs_ep->bDescriptorType = USB_DT_ENDPOINT; + desc->fs_ep->bEndpointAddress = USB_DIR_IN; + desc->fs_ep->bmAttributes = USB_ENDPOINT_XFER_BULK; + desc->fs_ep->wMaxPacketSize = cpu_to_le16(64); + + desc->fs[DVCT_EP_DESC_POS] = + (struct usb_descriptor_header *)desc->fs_ep; + + /*High Speed ep */ + desc->hs_ep = kzalloc(sizeof(*desc->hs_ep), GFP_KERNEL); + if (!desc->hs_ep) + goto err_hs_ep; + + desc->hs_ep->bLength = USB_DT_ENDPOINT_SIZE; + desc->hs_ep->bDescriptorType = USB_DT_ENDPOINT; + desc->hs_ep->bEndpointAddress = USB_DIR_IN; + desc->hs_ep->bmAttributes = USB_ENDPOINT_XFER_BULK; + desc->hs_ep->wMaxPacketSize = cpu_to_le16(512); + + desc->hs[DVCT_EP_DESC_POS] = + (struct usb_descriptor_header *)desc->hs_ep; + + /*Super Speed ep */ + desc->ss_ep = kzalloc(sizeof(*desc->ss_ep), GFP_KERNEL); + if (!desc->ss_ep) + goto err_ss_ep; + + desc->ss_ep->bLength = USB_DT_ENDPOINT_SIZE; + desc->ss_ep->bDescriptorType = USB_DT_ENDPOINT; + desc->ss_ep->bEndpointAddress = USB_DIR_IN; + desc->ss_ep->bmAttributes = USB_ENDPOINT_XFER_BULK; + desc->ss_ep->wMaxPacketSize = cpu_to_le16(1024); + + desc->ss[DVCT_EP_DESC_POS] = + (struct usb_descriptor_header *)desc->ss_ep; + + /*Super Speed ep comp */ + desc->ss_ep_comp = kzalloc(sizeof(*desc->ss_ep_comp), GFP_KERNEL); + if (!desc->ss_ep_comp) + goto err_ss_ep_comp; + + desc->ss_ep_comp->bLength = USB_DT_SS_EP_COMP_SIZE; + desc->ss_ep_comp->bDescriptorType = USB_DT_SS_ENDPOINT_COMP; + + desc->ss[DVCT_EP_COMP_DESC_POS] = + (struct usb_descriptor_header *)desc->ss_ep_comp; + + /* strings */ + /*the table */ + desc->str.language = 0x0409; /*en-us */ + desc->str.strings = + kzalloc(DVCT_STR_COUNT * sizeof(struct usb_string), GFP_KERNEL); + if (!desc->str.strings) + goto err_str; + + /*IAD*/ + desc->str.strings[DVCT_STR_IAD_IDX].s = + kasprintf(GFP_KERNEL, "DvC Trace (%s)", + dev_name(&d_fun->source_dev->device)); + if (!desc->str.strings[DVCT_STR_IAD_IDX].s) + goto err_str_iad; + + /*control */ + desc->str.strings[DVCT_STR_C_ITF_IDX].s = + kasprintf(GFP_KERNEL, "DvC Trace Control (%s)", + dev_name(&d_fun->source_dev->device)); + if (!desc->str.strings[DVCT_STR_C_ITF_IDX].s) + goto err_str_ctrl; + + /*data */ + desc->str.strings[DVCT_STR_D_ITF_IDX].s = + kasprintf(GFP_KERNEL, "DvC Trace Data (%s)", + dev_name(&d_fun->source_dev->device)); + if (!desc->str.strings[DVCT_STR_D_ITF_IDX].s) + goto err_str_data; + + return 0; +/*cleanup*/ +err_str_data: + kfree(desc->str.strings[DVCT_STR_C_ITF_IDX].s); +err_str_ctrl: + kfree(desc->str.strings[DVCT_STR_IAD_IDX].s); +err_str_iad: + kfree(desc->str.strings); +err_str: + kfree(desc->ss_ep_comp); +err_ss_ep_comp: + kfree(desc->ss_ep); +err_ss_ep: + kfree(desc->hs_ep); +err_hs_ep: + kfree(desc->fs_ep); +err_fs_ep: + kfree(desc->d_itf); +err_d_itf: + kfree(desc->c_itf); +err_c_itf: + kfree(desc->iad); +err_iad: + kfree(desc->ss); +err_ss: + kfree(desc->hs); +err_hs: + kfree(desc->fs); +err_fs: + pr_err("Failed OFM"); + return -ENOMEM; +} + +static void dvct_free_desc(struct dvct_function *d_fun) +{ + struct dvct_function_desc *desc = &d_fun->desc; + + DVCT_IN(); + + kfree(desc->str.strings[DVCT_STR_D_ITF_IDX].s); + kfree(desc->str.strings[DVCT_STR_C_ITF_IDX].s); + kfree(desc->str.strings[DVCT_STR_IAD_IDX].s); + kfree(desc->str.strings); + kfree(desc->ss_ep_comp); + kfree(desc->ss_ep); + kfree(desc->hs_ep); + kfree(desc->fs_ep); + kfree(desc->d_itf); + kfree(desc->c_itf); + kfree(desc->iad); + kfree(desc->ss); + kfree(desc->hs); + kfree(desc->fs); +} + +ssize_t dvct_start_transfer(struct dvct_function *d_fun, u8 config) +{ + DVCT_IN(); + if (!dvct_get_status(&d_fun->status, DVCT_MASK_ONLINE)) + return -EIO; + + d_fun->trace_config = config; + return d_fun->source_drv->start_transfer(d_fun->source_dev, config); +} +EXPORT_SYMBOL(dvct_start_transfer); + +int dvct_stop_transfer(struct dvct_function *d_fun) +{ + + DVCT_IN(); + if (!dvct_get_status(&d_fun->status, DVCT_MASK_ONLINE)) + return -EIO; + + if (dvct_get_status(&d_fun->status, DVCT_MASK_TRANS)) { + d_fun->trace_config = 0; + return d_fun->source_drv->stop_transfer(d_fun->source_dev); + } + + return 0; +} +EXPORT_SYMBOL(dvct_stop_transfer); + +static int dvct_strings_setup(struct usb_composite_dev *cdev, + struct dvct_function *f_fun) +{ + int status; + + DVCT_IN(); + if (!f_fun->desc.str.strings) + return -EINVAL; + + status = usb_string_ids_tab(cdev, f_fun->desc.str.strings); + if (status < 0) + return status; + + f_fun->desc.iad->iFunction = + f_fun->desc.str.strings[DVCT_STR_IAD_IDX].id; + f_fun->desc.c_itf->iInterface = + f_fun->desc.str.strings[DVCT_STR_C_ITF_IDX].id; + f_fun->desc.d_itf->iInterface = + f_fun->desc.str.strings[DVCT_STR_D_ITF_IDX].id; + + return 0; +} + +static int dvct_setup(struct usb_function *func, + const struct usb_ctrlrequest *ctrl) +{ + int status = -EOPNOTSUPP; + u16 w_index; + u16 w_value; + u16 w_length; + u8 b_index_value; + struct dvct_function *d_fun = to_dvct_function(func); + + DVCT_IN(); + + w_index = le16_to_cpu(ctrl->wIndex); + w_value = le16_to_cpu(ctrl->wValue); + w_length = le16_to_cpu(ctrl->wLength); + b_index_value = (u8) (w_index >> 8); + + if (ctrl->bRequestType != + (USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE)) + goto done; + + switch (ctrl->bRequest) { + case DC_REQUEST_SET_RESET: + + pr_info("SET_RESET v%04x i%04x l%u\n", + w_value, w_index, w_length); + + dvct_stop_transfer(d_fun); + status = 0; + break; + + case DC_REQUEST_SET_TRACE: + /* There are some inconsistencies in the spec regarding some of the + * control requests, like SET/GET _TRACE, where even if the message + * is defined as interface specific the wIndex field is used for + * something else, making these request unusable in a "standard" + * composite device. + * To get around this we expect the interface to be specified in + * wIndex 7:0 and any other values in wIndex 15:8. + * A "special" composite implementation is free to treat these setup + * requests "on spec" and call directly dvct_start_transfer and/or + * dvct_stop_transfer (exported in u_dvctrace.h). + */ + pr_info("SET_TRACE v%04x i%04x l%u\n", + w_value, w_index, w_length); + + if (!b_index_value) { + dvct_stop_transfer(d_fun); + status = 0; + } else { + status = dvct_start_transfer(d_fun, b_index_value); + } + break; + } + +done: + if (status >= 0) { + d_fun->cdev->req->zero = 0; + d_fun->cdev->req->length = 0; + status = + usb_ep_queue(d_fun->cdev->gadget->ep0, d_fun->cdev->req, + GFP_ATOMIC); + if (status) + pr_err("Setup response queue error\n"); + } else { + pr_debug("Unexpected request %02x.%02x v%04x i%04x l%u\n", + ctrl->bRequestType, ctrl->bRequest, w_value, w_index, + w_length); + } + + return status; +} + +static int dvct_function_bind(struct usb_configuration *cconfig, + struct usb_function *func) +{ + int id, ret; + struct usb_ep *ep; + struct dvct_function *d_fun = to_dvct_function(func); + + DVCT_IN(); + d_fun->cdev = cconfig->cdev; + + ret = dvct_strings_setup(d_fun->cdev, d_fun); + if (ret) + pr_warn("Cannot allocate function string id's\n"); + + /* allocate interface ID(s) */ + id = usb_interface_id(cconfig, func); + if (id < 0) + return id; + + d_fun->desc.c_itf->bInterfaceNumber = id; + d_fun->desc.iad->bFirstInterface = id; + + pr_debug("Setting id %d for dvc-control interface\n", id); + + id = usb_interface_id(cconfig, func); + if (id < 0) + return id; + + d_fun->desc.d_itf->bInterfaceNumber = id; + + pr_debug("Setting id %d for dvc-trace-data interface\n", id); + + /* allocate endpoints */ + d_fun->desc.ss_ep->wMaxPacketSize = 0; /*get the real max */ + ep = usb_ep_autoconfig_ss(d_fun->cdev->gadget, + d_fun->desc.ss_ep, d_fun->desc.ss_ep_comp); + + if (!ep) { + pr_err("usb_ep_autoconfig for ep_in failed\n"); + return -ENODEV; + } + + /*copy over the endpoint parameters */ + d_fun->desc.hs_ep->bEndpointAddress = + d_fun->desc.ss_ep->bEndpointAddress; + d_fun->desc.fs_ep->bEndpointAddress = + d_fun->desc.ss_ep->bEndpointAddress; + + if (le16_to_cpu(d_fun->desc.hs_ep->wMaxPacketSize) > + le16_to_cpu(d_fun->desc.ss_ep->wMaxPacketSize)) + d_fun->desc.hs_ep->wMaxPacketSize = + d_fun->desc.ss_ep->wMaxPacketSize; + + if (le16_to_cpu(d_fun->desc.fs_ep->wMaxPacketSize) > + le16_to_cpu(d_fun->desc.ss_ep->wMaxPacketSize)) + d_fun->desc.fs_ep->wMaxPacketSize = + d_fun->desc.ss_ep->wMaxPacketSize; + + pr_info("usb_ep_autoconfig %s, addr 0x%hhx, size ss=%hu hs=%hu fs=%hu\n", + ep->name, + d_fun->desc.ss_ep->bEndpointAddress, + d_fun->desc.ss_ep->wMaxPacketSize, + d_fun->desc.hs_ep->wMaxPacketSize, + d_fun->desc.fs_ep->wMaxPacketSize); + + ep->driver_data = d_fun; /* claim the endpoint */ + d_fun->ep_in = ep; + + ret = d_fun->source_drv->binded(d_fun->source_dev, ep, + &d_fun->function); + + return ret; +} + +static void dvct_function_unbind(struct usb_configuration *c, + struct usb_function *func) +{ + struct dvct_function *d_fun = to_dvct_function(func); + + DVCT_IN(); + dvct_clr_status(&d_fun->status, DVCT_MASK_ONLINE); + d_fun->online_data = 0; + d_fun->online_ctrl = 0; + + d_fun->source_drv->unbinded(d_fun->source_dev); +} + +static int dvct_function_set_alt(struct usb_function *func, + unsigned intf, unsigned alt) +{ + struct dvct_function *d_fun = to_dvct_function(func); + struct usb_composite_dev *cdev = func->config->cdev; + int ret; + + DVCT_IN(); + + if (intf == d_fun->desc.c_itf->bInterfaceNumber) { + d_fun->online_ctrl = 1; + pr_debug("dvc-control interface %u set alt %u\n", intf, alt); + } + + if (intf == d_fun->desc.d_itf->bInterfaceNumber) { + ret = config_ep_by_speed(cdev->gadget, func, d_fun->ep_in); + if (ret) { + pr_debug("intf: %d alt: %d ep_by_speed in err %d\n", + intf, alt, ret); + return ret; + } + + ret = usb_ep_enable(d_fun->ep_in); + if (ret) { + pr_debug("intf: %d alt: %d ep_enable in err %d\n", + intf, alt, ret); + return ret; + } + d_fun->online_data = 1; + } + + pr_info("dvc-trace interface %u set alt %u\n", intf, alt); + + if (unlikely(dvct_get_status(&d_fun->status, DVCT_MASK_TRANS))) + dvct_stop_transfer(d_fun); + + if (d_fun->online_data && d_fun->online_ctrl) { + dvct_set_status(&d_fun->status, DVCT_MASK_ONLINE); + if (d_fun->source_drv->connected) + d_fun->source_drv->connected(d_fun->source_dev, + cdev->gadget->speed); + } + return 0; +} + +static void dvct_function_disable(struct usb_function *func) +{ + struct dvct_function *d_fun = to_dvct_function(func); + struct usb_composite_dev *cdev; + + DVCT_IN(); + + cdev = d_fun->cdev; + + if (dvct_get_status(&d_fun->status, DVCT_MASK_TRANS)) + dvct_stop_transfer(d_fun); + + usb_ep_disable(d_fun->ep_in); + + d_fun->online_ctrl = 0; + d_fun->online_data = 0; + + if (d_fun->source_drv->disconnected) + d_fun->source_drv->disconnected(d_fun->source_dev); + + pr_debug("%s disabled\n", d_fun->function.name); +} + +CONFIGFS_ATTR_STRUCT(dvct_function_inst); + +static ssize_t dvct_attr_show(struct config_item *item, + struct configfs_attribute *attr, char *page) +{ + struct dvct_function_inst *d_inst; + struct dvct_function_inst_attribute *d_fun_attr; + ssize_t ret = 0; + + DVCT_IN(); + d_inst = container_of(to_config_group(item), struct dvct_function_inst, + instance.group); + d_fun_attr = container_of(attr, struct dvct_function_inst_attribute, + attr); + + if (d_fun_attr->show) + ret = d_fun_attr->show(d_inst, page); + + return ret; +} + +static void dvct_attr_release(struct config_item *item) +{ + struct dvct_function_inst *d_inst; + + DVCT_IN(); + d_inst = container_of(to_config_group(item), struct dvct_function_inst, + instance.group); + usb_put_function_instance(&d_inst->instance); +} + +static struct configfs_item_operations dvctrace_item_ops = { + .release = dvct_attr_release, + .show_attribute = dvct_attr_show, +}; + +static ssize_t dvct_device_show(struct dvct_function_inst *d_inst, char *page) +{ + return sprintf(page, "%s\n", d_inst->source_dev->name_add); +} + +static struct dvct_function_inst_attribute f_dvctrace_device = + __CONFIGFS_ATTR_RO(source_dev, dvct_device_show); + +static struct configfs_attribute *dvct_attrs[] = { + &f_dvctrace_device.attr, + NULL, +}; + +static struct config_item_type dvct_func_type = { + .ct_item_ops = &dvctrace_item_ops, + .ct_attrs = dvct_attrs, + .ct_owner = THIS_MODULE, +}; + +static void dvct_free_func_inst(struct usb_function_instance *inst) +{ + struct dvct_function_inst *d_inst; + + DVCT_IN(); + d_inst = to_dvct_function_inst(inst); + + spin_lock(&d_inst->source_dev->lock); + d_inst->source_dev->instance_taken = 0; + spin_unlock(&d_inst->source_dev->lock); + + kfree(d_inst); +} + +static int dvct_set_inst_name(struct usb_function_instance *inst, + const char *name) +{ + struct dvct_function_inst *d_inst; + struct dvct_source_device *new_src; + struct dvct_source_device *old_src; + + DVCT_IN(); + d_inst = to_dvct_function_inst(inst); + old_src = d_inst->source_dev; + + new_src = dvct_source_find_free_by_name(name); + + if (IS_ERR_OR_NULL(new_src)) + return -ENODEV; + + if (new_src) { + spin_lock(&new_src->lock); + spin_lock(&old_src->lock); + + d_inst->source_dev = new_src; + new_src->instance_taken = 1; + old_src->instance_taken = 0; + + spin_unlock(&old_src->lock); + spin_unlock(&new_src->lock); + } + return 0; +} + +static struct usb_function_instance *dvct_alloc_inst(void) +{ + struct dvct_function_inst *d_inst; + struct dvct_source_device *src_dev = NULL; + + DVCT_IN(); + /*get the first free source, this will change via set name + * if available */ + src_dev = dvct_source_find_free(); + + if (IS_ERR_OR_NULL(src_dev)) + return ERR_PTR(-ENODEV); + + d_inst = kzalloc(sizeof(*d_inst), GFP_KERNEL); + + if (!d_inst) + return ERR_PTR(-ENOMEM); + + d_inst->instance.free_func_inst = dvct_free_func_inst; + d_inst->instance.set_inst_name = dvct_set_inst_name; + + spin_lock(&src_dev->lock); + d_inst->source_dev = src_dev; + src_dev->instance_taken = 1; + spin_unlock(&src_dev->lock); + + config_group_init_type_name(&d_inst->instance.group, + "", &dvct_func_type); + return &d_inst->instance; +} + +static void dvct_free_func(struct usb_function *func) +{ + struct dvct_function *d_fun = to_dvct_function(func); + + DVCT_IN(); + d_fun->source_drv->deactivate(d_fun->source_dev); + + spin_lock(&d_fun->source_dev->lock); + d_fun->source_dev->function_taken = 0; + spin_unlock(&d_fun->source_dev->lock); + + dvct_free_desc(d_fun); + + kfree(d_fun); +} + +static struct usb_function *dvct_alloc_func(struct usb_function_instance *inst) +{ + int ret; + struct dvct_function *d_fun; + struct dvct_function_inst *d_inst = to_dvct_function_inst(inst); + + DVCT_IN(); + d_fun = kzalloc(sizeof(struct dvct_function), GFP_KERNEL); + if (!d_fun) + return ERR_PTR(-ENOMEM); + + d_fun->source_dev = d_inst->source_dev; + d_fun->source_drv = dvct_source_get_drv(d_fun->source_dev); + d_fun->trace_config = 0; + + spin_lock(&d_fun->source_dev->lock); + d_fun->source_dev->function_taken = 1; + spin_unlock(&d_fun->source_dev->lock); + + ret = d_fun->source_drv->activate(d_fun->source_dev, &d_fun->status); + if (ret) { + pr_err("Cannot activate source device %d\n", ret); + goto err; + } + + ret = dvct_alloc_desc(d_fun); + if (ret) + goto err_des; + + /*String table */ + d_fun->function.strings = + kzalloc(2 * sizeof(struct usb_gadget_strings), GFP_KERNEL); + + if (!d_fun->function.strings) { + ret = -ENOMEM; + goto err_string_table; + } + + d_fun->function.strings[0] = &d_fun->desc.str; + + d_fun->function.name = "dvctrace"; + d_fun->function.fs_descriptors = d_fun->desc.fs; + d_fun->function.hs_descriptors = d_fun->desc.hs; + d_fun->function.ss_descriptors = d_fun->desc.ss; + d_fun->function.bind = dvct_function_bind; + d_fun->function.unbind = dvct_function_unbind; + d_fun->function.set_alt = dvct_function_set_alt; + d_fun->function.disable = dvct_function_disable; + d_fun->function.free_func = dvct_free_func; + d_fun->function.setup = dvct_setup; + + return &d_fun->function; + +err_string_table: + dvct_free_desc(d_fun); +err_des: + d_fun->source_drv->deactivate(d_fun->source_dev); +err: + kfree(d_fun); + return ERR_PTR(ret); +} + +DECLARE_USB_FUNCTION_INIT(dvctrace, dvct_alloc_inst, dvct_alloc_func); +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("DvC-Trace function driver"); +MODULE_AUTHOR("Traian Schiau "); diff --git a/drivers/usb/gadget/function/u_dvctrace.h b/drivers/usb/gadget/function/u_dvctrace.h new file mode 100644 index 000000000000..564d6fa7525d --- /dev/null +++ b/drivers/usb/gadget/function/u_dvctrace.h @@ -0,0 +1,72 @@ + +/* + * Gadget Driver for DvC.Trace Function + * + * Copyright (C) 2015, Intel Corporation. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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. + * + */ + +#ifndef __U_DVCTRACE_H +#define __U_DVCTRACE_H + +#include +#include + +struct dvct_function_desc { + struct usb_descriptor_header **fs; + struct usb_descriptor_header **hs; + struct usb_descriptor_header **ss; + + /*special descriptors, update on bind */ + struct usb_interface_assoc_descriptor *iad; + struct usb_interface_descriptor *d_itf; + struct usb_interface_descriptor *c_itf; + struct usb_endpoint_descriptor *fs_ep; + struct usb_endpoint_descriptor *hs_ep; + struct usb_endpoint_descriptor *ss_ep; + struct usb_ss_ep_comp_descriptor *ss_ep_comp; + + /* strings */ + struct usb_gadget_strings str; +}; + +struct dvct_function { + struct usb_function function; + struct usb_composite_dev *cdev; + struct usb_ep *ep_in; + + u32 online_data:1; /*set to one when the data itf is set */ + u32 online_ctrl:1; /*set to one when the control itf is set */ + atomic_t status; + + struct dvct_source_device *source_dev; + struct dvct_source_driver *source_drv; + + u8 trace_config; + struct dvct_function_desc desc; +}; + +struct dvct_function_inst { + struct usb_function_instance instance; + struct dvct_source_device *source_dev; +}; + +#define to_dvct_function_inst(inst) \ + container_of(inst, struct dvct_function_inst, instance) + +#define to_dvct_function(func) \ + container_of(func, struct dvct_function, function) + +ssize_t dvct_start_transfer(struct dvct_function *dev, u8 config); +int dvct_stop_transfer(struct dvct_function *dev); + +#endif /*__U_DVCTRACE_H*/ diff --git a/include/linux/usb/debug.h b/include/linux/usb/debug.h new file mode 100644 index 000000000000..074b930aedf6 --- /dev/null +++ b/include/linux/usb/debug.h @@ -0,0 +1,189 @@ +/* + * -- USB Debug Class definitions. + * + * Usb debug class specific constants, based on: + * USB 3.1 Device Class Specification for Debug Devices + * Revision 1.0 – July 14, 2015 + * http://www.usb.org/developers/docs/usb_31_072715.zip + * + * Copyright (C) 2015, Intel Corporation. + * + * This software is distributed under the terms of the GNU General Public + * License ("GPL") version 2, as published by the Free Software Foundation. + * + */ + +#ifndef __LINUX_USB_DEBUG_H +#define __LINUX_USB_DEBUG_H + +#include + +/* + * USB Debug Class Rev. 1.0 + * Appendix A: Debug-Device-Class Codes + * Table 8-2: Debug Interface Sub-Class Code (SC_DEBUG) + */ +#define USB_SUBCLASS_DBC 0x02 +#define USB_SUBCLASS_DBC_DFX 0x03 +#define USB_SUBCLASS_DBC_TRACE 0x04 +#define USB_SUBCLASS_DVC_GP 0x05 +#define USB_SUBCLASS_DVC_DFX 0x06 +#define USB_SUBCLASS_DVC_TRACE 0x07 +#define USB_SUBCLASS_DEBUG_CONTROL 0x08 + +/* + * USB Debug Class Rev. 1.0 + * Appendix A: Debug-Device-Class Codes + * Table 8-3: Debug Interface Protocol Code (PC_DEBUG) + */ +#define DC_PROTOCOL_CODE_UNDEFINED 0x00 +#define DC_PROTOCOL_VENDOR_OR_GNU 0x01 + +#define DC_PROTOCOL_GP_VENDOR 0x00 +#define DC_PROTOCOL_GP_GNU 0x01 + +/* + * USB Debug Class Rev. 1.0 + * 4.4.6 Debug-Unit Descriptor + * Table 4-9: Debug Unit Descriptor - bmControl + */ +#define DC_CTL_SET_CFG_DATA_SG (1 << 0) +#define DC_CTL_SET_CFG_DATA (1 << 1) +#define DC_CTL_GET_CFG_DATA (1 << 2) +#define DC_CTL_SET_CFG_ADDR (1 << 3) +#define DC_CTL_GET_CFG_ADDR (1 << 4) +#define DC_CTL_SET_OP_MODE (1 << 7) +#define DC_CTL_GET_OP_MODE (1 << 8) +#define DC_CTL_SET_BUFF_INFO (1 << 11) +#define DC_CTL_GET_BUFF_INFO (1 << 12) +#define DC_CTL_SET_RESET (1 << 13) + + +/* + * USB Debug Class Rev. 1.0 + * Appendix A: Debug-Device-Class Codes + * Table 8-6: Debug Class-Specific Descriptor SubTypes + */ +#define DC_UNDEFINED 0x00 +#define DC_INPUT_CONNECTION 0x01 +#define DC_OUTPUT_CONNECTION 0x02 +#define DC_DEBUG_UNIT 0x03 +#define DC_DEBUG_ATTRIBUTES 0x04 + +/* + * USB Debug Class Rev. 1.0 + * + * 4.4.4 Input-Connection Descriptor + * Table 4-7: Input Connection Descriptor - bConnectionType + * + * 4.4.5 Output Connection Descriptor + * Table 4-8: Output Connection Descriptor - bConnectionType + */ +#define DC_CONNECTION_USB 0x00 +#define DC_CONNECTION_DEBUG_CONTROL 0x01 +#define DC_CONNECTION_DEBUG_DATA 0x02 +#define DC_CONNECTION_DEBUG_DATA_CONTROL 0x03 + + +/* + * USB Debug Class Rev. 1.0 + * 4.4.6 Debug-Unit Descriptor + * Table 4-11: dTraceFormat + */ +#define DC_VENDOR_FORMAT(v, f) (((v)<<24)|(f)) +/*Vendor N/A*/ +#define DC_TRACE_NOT_FORMATED_PASSTHROUGH DC_VENDOR_FORMAT(0x0, 0x0) +#define DC_TRACE_NOT_FORMATED_HEADER DC_VENDOR_FORMAT(0x0, 0x1) +#define DC_TRACE_NOT_FORMATED_FOOTER DC_VENDOR_FORMAT(0x0, 0x2) +#define DC_TRACE_NOT_FORMATED_GUID DC_VENDOR_FORMAT(0x0, 0x5) +#define DC_TRACE_NOT_FORMATED_UTF8 DC_VENDOR_FORMAT(0x0, 0x6) +/*Vendor Intel*/ +#define DC_TRACE_INTEL_FORMATED_VENDOR DC_VENDOR_FORMAT(0x1, 0x0) +/*Vendor ARM*/ +#define DC_TRACE_ARM_FORMATED_VENDOR DC_VENDOR_FORMAT(0x2, 0x0) +/*Vendor ST*/ +#define DC_TRACE_ST_FORMATED_VENDOR DC_VENDOR_FORMAT(0x3, 0x0) +/*Vendor TI*/ +#define DC_TRACE_TI_FORMATED_VENDOR DC_VENDOR_FORMAT(0x4, 0x0) +/*Vendor Qualcomm*/ +#define DC_TRACE_QCOMM_FORMATED_VENDOR DC_VENDOR_FORMAT(0x5, 0x0) +/*Vendor AMD*/ +#define DC_TRACE_AMD_FORMATED_VENDOR DC_VENDOR_FORMAT(0x6, 0x0) +/*Vendor MIPI*/ +#define DC_TRACE_MIPI_FORMATED DC_VENDOR_FORMAT(0x80, 0x0) +/*Vendor Nexus*/ +#define DC_TRACE_NEXUS_FORMATED DC_VENDOR_FORMAT(0x81, 0x0) + +/* + * USB Debug Class Rev. 1.0 + * 4.4.6 Debug-Unit Descriptor + * Table 4-9: Debug Unit Descriptor - bDebugUnitType + */ +#define DC_UNIT_TYPE_UNDEFINED 0x00 +#define DC_UNIT_TYPE_DFX 0x01 +#define DC_UNIT_TYPE_SELECT 0x02 +#define DC_UNIT_TYPE_TRACE_ROUTE 0x03 +#define DC_UNIT_TYPE_TRACE_PROC 0x04 +#define DC_UNIT_TYPE_TRACE_GEN 0x05 +#define DC_UNIT_TYPE_TRACE_SINK 0x06 +#define DC_UNIT_TYPE_CONTROL 0x07 +#define DC_UNIT_TYPE_VENDOR_FIRST 0x40 +#define DC_UNIT_TYPE_VENDOR_LAST 0x5F + +/* + * USB Debug Class Rev. 1.0 + * 4.4.6 Debug-Unit Descriptor + * Table 4-10: Debug Sub-Unit Type + */ +#define DC_UNIT_SUBTYPE_NULL 0x00 +#define DC_UNIT_SUBTYPE_CPU 0x01 +#define DC_UNIT_SUBTYPE_GFX 0x02 +#define DC_UNIT_SUBTYPE_VIDEO 0x03 +#define DC_UNIT_SUBTYPE_IMAGING 0x04 +#define DC_UNIT_SUBTYPE_AUDIO 0x05 +#define DC_UNIT_SUBTYPE_MODEM 0x06 +#define DC_UNIT_SUBTYPE_BLUETOOTH 0x07 +#define DC_UNIT_SUBTYPE_PWR_MGT 0x08 +#define DC_UNIT_SUBTYPE_SECURITY 0x09 +#define DC_UNIT_SUBTYPE_SENSOR 0x0A +#define DC_UNIT_SUBTYPE_BUSWATCH 0x0B +#define DC_UNIT_SUBTYPE_LOCATION 0x0C +#define DC_UNIT_SUBTYPE_TRACEZIP 0x0D +#define DC_UNIT_SUBTYPE_TAPCTL 0x0E +#define DC_UNIT_SUBTYPE_MEMACC 0x0F +#define DC_UNIT_SUBTYPE_OTHER 0x3F +#define DC_UNIT_SUBTYPE_SWLOGGER 0x40 +#define DC_UNIT_SUBTYPE_SWROUTER 0x41 +#define DC_UNIT_SUBTYPE_SWUNINT 0x42 +#define DC_UNIT_SUBTYPE_SWCFGUNINT 0x43 +#define DC_UNIT_SUBTYPE_SWDEBUGGER 0x44 +#define DC_UNIT_SUBTYPE_VENDOR_FIRST 0x80 +#define DC_UNIT_SUBTYPE_VENDOR_LAST 0xBF +#define DC_UNIT_SUBTYPE_STANDARDS 0xFF + +/* + * USB Debug Class Rev. 1.0 + * Appendix A: Debug-Device-Class Codes + * Table 8-5: Debug Class-Specific Commands bRequest + */ +/*Set*/ +#define DC_REQUEST_SET_CONFIG_DATA 0x01 /*S 5.4.4 T 5-6*/ +#define DC_REQUEST_SET_CONFIG_DATA_SINGLE 0x02 /*S 5.4.3 T 5-4*/ +#define DC_REQUEST_SET_CONFIG_ADDRESS 0x03 /*S 5.4.6 T 5-8*/ +#define DC_REQUEST_SET_ALT_STACK 0x04 /*S 5.4.8 T 5-10*/ +#define DC_REQUEST_SET_OPERATING_MODE 0x05 /*S 5.4.10 T 5-15*/ +#define DC_REQUEST_SET_TRACE 0x06 /*S 5.4.14 T 5-23*/ +#define DC_REQUEST_SET_BUFFER 0x09 /*S 5.4.16 T 5-25*/ +#define DC_REQUEST_SET_RESET 0x0A /*S 5.4.18 T 5-28f*/ +/*Get*/ +#define DC_REQUEST_GET_CONFIG_DATA 0x81 /*S 5.4.5 T 5-7*/ +#define DC_REQUEST_GET_CONFIG_DATA_SINGLE 0x82 +#define DC_REQUEST_GET_CONFIG_ADDRESS 0x83 /*S 5.4.7 T 5-9*/ +#define DC_REQUEST_GET_ALT_STACK 0x84 /*S 5.4.9 T 5-11*/ +#define DC_REQUEST_GET_OPERATING_MODE 0x85 /*S 5.4.11 T 5-18*/ +#define DC_REQUEST_GET_TRACE 0x86 /*S 5.4.15 T 5-24*/ +#define DC_REQUEST_GET_INFO 0x87 /*S 5.4.12 T 5-19*/ +#define DC_REQUEST_GET_ERROR 0x88 /*S 5.4.13 T 5-21*/ +#define DC_REQUEST_GET_BUFFER 0x89 /*S 5.4.17 T 5-27*/ + +#endif /* __LINUX_USB_DEBUG_H */ diff --git a/include/uapi/linux/usb/ch9.h b/include/uapi/linux/usb/ch9.h index d5a5caec8fbc..821ff8778618 100644 --- a/include/uapi/linux/usb/ch9.h +++ b/include/uapi/linux/usb/ch9.h @@ -325,6 +325,7 @@ struct usb_device_descriptor { #define USB_CLASS_CSCID 0x0b /* chip+ smart card */ #define USB_CLASS_CONTENT_SEC 0x0d /* content security */ #define USB_CLASS_VIDEO 0x0e +#define USB_CLASS_DEBUG 0xdc #define USB_CLASS_WIRELESS_CONTROLLER 0xe0 #define USB_CLASS_MISC 0xef #define USB_CLASS_APP_SPEC 0xfe From 2d41c13f307ac8789a912a4062434b3668c17649 Mon Sep 17 00:00:00 2001 From: Traian Schiau Date: Thu, 31 Mar 2016 11:32:11 +0300 Subject: [PATCH 0539/1103] dvctrace: Topology descriptors support. DvC Trace supports additional custom USB descriptors that should be provided by the source device. Signed-off-by: Traian Schiau --- Documentation/ABI/testing/sysfs-bus-dvctrace | 42 ++ drivers/bus/dvctrace.c | 441 +++++++++++++++++++ drivers/usb/gadget/function/f_dvctrace.c | 149 +++++-- drivers/usb/gadget/function/u_dvctrace.h | 1 + include/linux/dvctrace.h | 33 +- 5 files changed, 633 insertions(+), 33 deletions(-) diff --git a/Documentation/ABI/testing/sysfs-bus-dvctrace b/Documentation/ABI/testing/sysfs-bus-dvctrace index 0abdd3293364..bf2b5bb2144a 100644 --- a/Documentation/ABI/testing/sysfs-bus-dvctrace +++ b/Documentation/ABI/testing/sysfs-bus-dvctrace @@ -24,3 +24,45 @@ Description: (RW) The protocol id of a dvc-trace source device, this will used in function driver interface descriptors (u8). According to USB debug class specification the protocol id is vendor specific. + +What: /sys/bus/dvctrace/devices//descriptors +Date: May 2015 +KernelVersion: 4.0 +Contact: Traian Schiau +Description: (RW) Hex-dump of the descriptors provided by the + source device. + eg. A debug class output connection descriptor + 09 24 02 04 03 00 00 00 00 + ll tt ss xx xx xx xx xx ii + | | | +- iConnection string id. + | | +- Descriptor sub-type DC_OUTPUT_CONNECTION + | +- Descriptor type (USB_DT_CS_INTERFACE) + +- Descriptor length + Writing: + - is not allowed while the device is Reserved or In Use. + - will replace all the descriptors currently present. + - will remove any strings previously provided. + - should use the same format. + - accepts multiple descriptors separated by space or '\n'. + +What: /sys/bus/dvctrace/devices//strings +Date: May 2015 +KernelVersion: 4.0 +Contact: Traian Schiau +Description: (RW) Currently set usb descriptor strings in + .: string format. + . identifies the location where + the string id is needed. + eg. Having the same debug class output connection descriptor, + as the first descriptor. + 09 24 02 04 03 00 00 00 00 + ll tt ss xx xx xx xx xx ii + +- iConnection string id. + 0.8: My output connection - will identify the string associated + with this descriptor. + Writing: + - is not allowed while the device is Reserved or In Use. + - will replace all the strings currently present. + - should use the same format. + - accepts multiple strings separated by ";" or '\n'. + eg. "0.4: first string; 1.4: second string" diff --git a/drivers/bus/dvctrace.c b/drivers/bus/dvctrace.c index c1770ca44422..906e972103d9 100644 --- a/drivers/bus/dvctrace.c +++ b/drivers/bus/dvctrace.c @@ -19,6 +19,7 @@ #include #include #include +#include #ifdef DVCT_DEBUG #define DVCT_IN() pr_debug("in\n") @@ -26,6 +27,435 @@ #define DVCT_IN() do {} while (0) #endif +/* Count the number of USB descriptors in the given ascii hex string + * What we expect: + * ll tt ss xx xx xx + * | | | +- Fill up the descriptor + * | | +- Descriptor sub-type (1-4) + * | | DC_INPUT_CONNECTION 0x01 + * | | DC_OUTPUT_CONNECTION 0x02 + * | | DC_DEBUG_UNIT 0x03 + * | | DC_DEBUG_ATTRIBUTES 0x04 + * | +- Descriptor type (USB_DT_CS_INTERFACE) + * +- Descriptor length (check > 3 and we have the rest of it) + */ +static int count_descriptors(const char *buf, size_t size) +{ + size_t off = 0; + int i, j, count = 0; + u8 len, tmp; + + DVCT_IN(); + while (off < size) { + /*the length*/ + j = sscanf(buf + off, "%2hhx%n", &len, &i); + if (!j) + break; + if (j < 0 || len < 4) + return -EINVAL; + len--; + off += i; + + /*Type*/ + j = sscanf(buf + off, "%2hhx%n", &tmp, &i); + if (j <= 0 || tmp != USB_DT_CS_INTERFACE) + return -EINVAL; + len--; + off += i; + + /*Sub Type*/ + j = sscanf(buf + off, "%2hhx%n", &tmp, &i); + if (j <= 0 || tmp < DC_INPUT_CONNECTION + || tmp > DC_DEBUG_ATTRIBUTES) + return -EINVAL; + len--; + off += i; + + while (len) { + j = sscanf(buf + off, "%2hhx%n", &tmp, &i); + if (j <= 0) + return -EINVAL; + len--; + off += i; + } + count++; + } + return count; +} + +/* Parse @buf and get a pointer to the descriptor identified + * @idx*/ +static u8 *get_descriptor(const char *buf, size_t size, int idx) +{ + size_t off = 0; + int i, j, k, count = 0; + u8 len, tmp, *ret = NULL; + + DVCT_IN(); + while (off < size) { + j = sscanf(buf + off, "%2hhx%n", &len, &i); + if (j < 0) + return ERR_PTR(-EINVAL); + if (!j) + return ERR_PTR(-ERANGE); + + if (count == idx) { + ret = kmalloc(len, GFP_KERNEL); + if (!ret) + return ERR_PTR(-ENOMEM); + ret[0] = len; + } + off += i; + for (k = 1; k < len; k++) { + j = sscanf(buf + off, "%2hhx%n", &tmp, &i); + if (j <= 0) { + kfree(ret); + return ERR_PTR(-EINVAL); + } + if (count == idx) + ret[k] = tmp; + off += i; + } + if (count == idx) + break; + count++; + } + return ret; +} + + +static void free_strings(struct dvct_usb_descriptors *desc) +{ + struct usb_string *string; + + DVCT_IN(); + for (string = desc->str.strings; string && string->s; string++) + kfree(string->s); + + kfree(desc->str.strings); + desc->str.strings = NULL; + kfree(desc->lk_tbl); + desc->lk_tbl = NULL; +} + +static void free_descriptors(struct dvct_usb_descriptors *desc) +{ + struct usb_descriptor_header **hdr; + + DVCT_IN(); + if (desc->dvc_spec) { + for (hdr = desc->dvc_spec; *hdr; hdr++) + kfree(*hdr); + kfree(desc->dvc_spec); + desc->dvc_spec = NULL; + } + free_strings(desc); + kfree(desc); +} + +static int alloc_strings(struct dvct_usb_descriptors *desc, int count) +{ + DVCT_IN(); + desc->lk_tbl = kzalloc((count + 1) * sizeof(struct dvct_string_lookup), + GFP_KERNEL); + if (!desc->lk_tbl) + goto err; + + desc->str.strings = kzalloc(sizeof(desc->str), GFP_KERNEL); + if (!desc->str.strings) + goto err_str; + + desc->str.language = 0x0409; + + return count; +err_str: + kfree(desc->lk_tbl); + desc->lk_tbl = NULL; +err: + return -ENOMEM; +} + +static struct dvct_usb_descriptors *alloc_descriptors(int count) +{ + struct dvct_usb_descriptors *desc; + + DVCT_IN(); + desc = kzalloc(sizeof(struct dvct_usb_descriptors), GFP_KERNEL); + if (!desc) + return ERR_PTR(-ENOMEM); + + desc->dvc_spec = + kzalloc((count + 1) * sizeof(struct usb_descriptor_header *), + GFP_KERNEL); + + if (!desc->dvc_spec) { + kfree(desc); + return ERR_PTR(-ENOMEM); + } + return desc; +} + +static ssize_t descriptors_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct dvct_source_device *ds_dev = dev_to_dvct_source_device(dev); + struct usb_descriptor_header **desc; + int ret = 0; + + DVCT_IN(); + if (!ds_dev->desc || !ds_dev->desc->dvc_spec + || !*ds_dev->desc->dvc_spec) + return sprintf(buf, "No Descriptors.\n"); + + for (desc = ds_dev->desc->dvc_spec; *desc; desc++) { + u8 len, *pdesc; + int i; + + len = (*desc)->bLength; + pdesc = (u8 *)(*desc); + for (i = 0; i < len; i++) + ret += snprintf(buf + ret, PAGE_SIZE - ret, "%02hhX ", + pdesc[i]); + buf[ret - 1] = '\n'; + } + return ret; +} + +static ssize_t descriptors_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + struct dvct_source_device *ds_dev = dev_to_dvct_source_device(dev); + int desc_count, i; + u8 *hdr; + + DVCT_IN(); + + if (ds_dev->instance_taken) + return -EBUSY; + + /*count the new descriptors, exit if invalid input*/ + desc_count = count_descriptors(buf, size); + if (desc_count <= 0) { + dev_warn(dev, "Invalid descriptor input:[%zu] %s", size, buf); + return -EINVAL; + } + + if (ds_dev->desc && ds_dev->desc != &ds_dev->static_desc) + free_descriptors(ds_dev->desc); + + ds_dev->desc = alloc_descriptors(desc_count); + if (IS_ERR_OR_NULL(ds_dev->desc)) { + ds_dev->desc = NULL; + return -ENOMEM; + } + + for (i = 0; i < desc_count; i++) { + hdr = get_descriptor(buf, size, i); + if (IS_ERR_OR_NULL(hdr)) { + dev_err(dev, "Cannot get descriptor %d, %ld\n", i, + PTR_ERR(hdr)); + free_descriptors(ds_dev->desc); + ds_dev->desc = NULL; + return -EINVAL; + } + ds_dev->desc->dvc_spec[i] = (struct usb_descriptor_header *)hdr; + } + return size; +} + +static DEVICE_ATTR_RW(descriptors); + + +/*find out at which member(offset) of which descriptor the pointer + * points to */ +static int dvctrace_string_ptr_to_offset(struct usb_descriptor_header **first, + u8 *ptr, int *desc_offset, int *offset) +{ + u8 *hdr_start, *hdr_end; + int idx = 0; + + DVCT_IN(); + for (; *first; first++, idx++) { + hdr_start = (u8 *) (*first); + hdr_end = hdr_start + ((*first)->bLength - 1); + if (ptr >= hdr_start && ptr <= hdr_end) { + *desc_offset = idx; + *offset = ptr - hdr_start; + return 0; + } + } + return -EINVAL; +} + +static u8 *dvctrace_offset_to_string_ptr(struct usb_descriptor_header **first, + int desc_offset, int offset) +{ + int idx = 0; + + DVCT_IN(); + for (; *first; first++, idx++) { + if (idx == desc_offset) { + if (offset >= (*first)->bLength) + return ERR_PTR(-ERANGE); + return ((u8 *) (*first)) + offset; + } + } + return ERR_PTR(-ERANGE); +} + +static int count_strings(const char *buf, size_t size) +{ + int count = 0; + size_t off = 0, slen; + int i = 0, j, desc_offset, offset; + + DVCT_IN(); + while (off < size) { + j = sscanf(buf + off, "%d.%d: %n", &desc_offset, &offset, &i); + if (j < 2) + break; + off += i; + slen = 0; + while (off + slen < size) { + if (buf[off + slen] == ';' || buf[off + slen] == '\n') + break; + slen++; + } + off += slen; + if (buf[off] == ';' || buf[off] == '\n') + off++; + count++; + } + return count; +} + +static char *get_string(const char *buf, size_t size, int index, + int *desc_offset, int *offset) +{ + int count = 0; + size_t off = 0, slen; + int i, j; + char *ret = ERR_PTR(-EINVAL); + + DVCT_IN(); + while (off < size) { + j = sscanf(buf + off, "%d.%d: %n", desc_offset, offset, &i); + if (j < 2) + return ERR_PTR(-EINVAL); + off += i; + slen = 0; + while (off + slen < size) { + if (buf[off + slen] == ';' || buf[off + slen] == '\n') + break; + slen++; + } + + if (count == index) { + ret = kmalloc(slen+1, GFP_KERNEL); + if (!ret) + return ERR_PTR(-ENOMEM); + memcpy(ret, buf + off, slen); + ret[slen] = 0; + return ret; + } + off += slen; + if (buf[off] == ';' || buf[off] == '\n') + off++; + count++; + } + return ERR_PTR(-EINVAL); +} + +static ssize_t strings_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct dvct_string_lookup *lk_s; + struct dvct_source_device *ds_dev = dev_to_dvct_source_device(dev); + int ret = 0; + + DVCT_IN(); + if (!ds_dev->desc || !ds_dev->desc->dvc_spec + || !*ds_dev->desc->dvc_spec) + return sprintf(buf, "No Descriptors.\n"); + + if (!ds_dev->desc->lk_tbl) + return sprintf(buf, "No Strings.\n"); + + for (lk_s = ds_dev->desc->lk_tbl; lk_s->str && lk_s->id; lk_s++) { + int desc_offset, offset; + + if (dvctrace_string_ptr_to_offset(ds_dev->desc->dvc_spec, + lk_s->id, &desc_offset, + &offset)) + ret += snprintf(buf + ret, PAGE_SIZE, + "Unknown(%p): %s\n", lk_s->id, + lk_s->str->s); + else + ret += snprintf(buf + ret, PAGE_SIZE, "%d.%d: %s\n", + desc_offset, offset, lk_s->str->s); + } + return ret; +} + +static ssize_t strings_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t size) +{ + struct dvct_source_device *ds_dev = dev_to_dvct_source_device(dev); + int count, i, ret; + + DVCT_IN(); + if (ds_dev->instance_taken) + return -EBUSY; + + count = count_strings(buf, size); + if (count <= 0) { + dev_err(dev, "Invalid input string:(%zu) %s\n", size, buf); + return -EINVAL; + } + + if (ds_dev->desc == &ds_dev->static_desc) { + dev_warn(&ds_dev->device, "Cannot set strings in static descriptors\n"); + return -EINVAL; + } + + if (ds_dev->desc->str.strings) + free_strings(ds_dev->desc); + + ret = alloc_strings(ds_dev->desc, count); + if (ret < 0) { + dev_err(dev, "Cannot allocate strings %d\n", ret); + return -EINVAL; + } + + for (i = 0; i < count; i++) { + char *tmp; + int d_off, off; + u8 *pid; + + tmp = get_string(buf, size, i, &d_off, &off); + if (IS_ERR_OR_NULL(tmp)) { + free_strings(ds_dev->desc); + return -EINVAL; + } + + pid = dvctrace_offset_to_string_ptr(ds_dev->desc->dvc_spec, + d_off, off); + if (IS_ERR_OR_NULL(pid)) { + dev_warn(&ds_dev->device, "String out of bounds\n"); + free_strings(ds_dev->desc); + return -EINVAL; + } + + ds_dev->desc->lk_tbl[i].id = pid; + ds_dev->desc->lk_tbl[i].str = &ds_dev->desc->str.strings[i]; + ds_dev->desc->str.strings[i].s = tmp; + } + return size; +} + +static DEVICE_ATTR_RW(strings); + static ssize_t protocol_show(struct device *dev, struct device_attribute *attr, char *buf) { @@ -71,6 +501,8 @@ static DEVICE_ATTR_RO(status); static struct attribute *dvct_source_attrs[] = { &dev_attr_protocol.attr, &dev_attr_status.attr, + &dev_attr_strings.attr, + &dev_attr_descriptors.attr, NULL, }; @@ -208,6 +640,9 @@ int dvct_source_device_add(struct dvct_source_device *ds_dev, return ret; } + if (ds_dev->static_desc.dvc_spec) + ds_dev->desc = &ds_dev->static_desc; + dev_notice(&dvctrace_bus, "Adding device %s\n", ds_dev->name_add); return 0; }; @@ -216,6 +651,12 @@ EXPORT_SYMBOL_GPL(dvct_source_device_add); void dvct_source_device_del(struct dvct_source_device *ds_dev) { DVCT_IN(); + + if (ds_dev->desc && ds_dev->desc != &ds_dev->static_desc) { + free_descriptors(ds_dev->desc); + ds_dev->desc = NULL; + } + device_del(&ds_dev->device); }; EXPORT_SYMBOL_GPL(dvct_source_device_del); diff --git a/drivers/usb/gadget/function/f_dvctrace.c b/drivers/usb/gadget/function/f_dvctrace.c index dffd06126d8f..d5cb49db52bc 100644 --- a/drivers/usb/gadget/function/f_dvctrace.c +++ b/drivers/usb/gadget/function/f_dvctrace.c @@ -39,6 +39,26 @@ enum { DVCT_HS_DESC_COUNT,/*Count of super speed descriptors*/ }; +/*The full list of descriptors will look like: + * IAD_DESCRIPTOR -----|=> USB function specific + * CONTROL_ITF_DESCRIPTOR -----| + * SOURCE_SPECIFIC_DESCRIPTOR_0 ----| + * .... |=> s_cnt descriptors provided by the + * SOURCE_SPECIFIC_DESCRIPTOR_s_cnt----| source device. + * DATA_ITF_DESCRIPTOR -----| + * ENDPOINT_DESCRIPTOR |=> USB function specific + * .... -----| + * This makes a good part of the descriptors to shift, + * the following should help*/ +#define DVCT_IAD_DESC_DYN_POS(s_cnt) (DVCT_IAD_DESC_POS) +#define DVCT_CITF_DESC_DYN_POS(s_cnt) (DVCT_CITF_DESC_POS) +#define DVCT_SOURCE_DESC_FIRST(s_cnt) (DVCT_DITF_DESC_POS) +#define DVCT_DITF_DESC_DYN_POS(s_cnt) ((s_cnt)+DVCT_DITF_DESC_POS) +#define DVCT_EP_DESC_DYN_POS(s_cnt) ((s_cnt)+DVCT_EP_DESC_POS) +#define DVCT_EP_COMP_DESC_DYN_POS(s_cnt) ((s_cnt)+DVCT_EP_COMP_DESC_POS) +#define DVCT_LS_DESC_DYN_COUNT(s_cnt) ((s_cnt)+DVCT_LS_DESC_COUNT) +#define DVCT_HS_DESC_DYN_COUNT(s_cnt) ((s_cnt)+DVCT_HS_DESC_COUNT) + enum { DVCT_STR_IAD_IDX, DVCT_STR_C_ITF_IDX, @@ -49,25 +69,35 @@ enum { static int dvct_alloc_desc(struct dvct_function *d_fun) { + int i; + unsigned int s_desc_count = 0; + struct usb_descriptor_header **s_desc; struct dvct_function_desc *desc = &d_fun->desc; DVCT_IN(); + if (d_fun->source_dev->desc) { + for (s_desc = d_fun->source_dev->desc->dvc_spec; + s_desc && (*s_desc); s_desc++) + s_desc_count++; + } + + /*alloc the descriptors array */ desc->fs = - kzalloc(DVCT_LS_DESC_COUNT * sizeof(struct usb_descriptor_header *), - GFP_KERNEL); + kzalloc(DVCT_LS_DESC_DYN_COUNT(s_desc_count) * + sizeof(struct usb_descriptor_header *), GFP_KERNEL); if (!desc->fs) goto err_fs; desc->hs = - kzalloc(DVCT_LS_DESC_COUNT * sizeof(struct usb_descriptor_header *), - GFP_KERNEL); + kzalloc(DVCT_LS_DESC_DYN_COUNT(s_desc_count) * + sizeof(struct usb_descriptor_header *), GFP_KERNEL); if (!desc->hs) goto err_hs; desc->ss = - kzalloc(DVCT_HS_DESC_COUNT * sizeof(struct usb_descriptor_header *), - GFP_KERNEL); + kzalloc(DVCT_HS_DESC_DYN_COUNT(s_desc_count) * + sizeof(struct usb_descriptor_header *), GFP_KERNEL); if (!desc->ss) goto err_ss; @@ -84,9 +114,12 @@ static int dvct_alloc_desc(struct dvct_function *d_fun) desc->iad->bFunctionProtocol = d_fun->source_dev->protocol; /*bFirstInterface - updated on bind */ - desc->fs[DVCT_IAD_DESC_POS] = (struct usb_descriptor_header *)desc->iad; - desc->hs[DVCT_IAD_DESC_POS] = (struct usb_descriptor_header *)desc->iad; - desc->ss[DVCT_IAD_DESC_POS] = (struct usb_descriptor_header *)desc->iad; + desc->fs[DVCT_IAD_DESC_DYN_POS(s_desc_count)] = + (struct usb_descriptor_header *)desc->iad; + desc->hs[DVCT_IAD_DESC_DYN_POS(s_desc_count)] = + (struct usb_descriptor_header *)desc->iad; + desc->ss[DVCT_IAD_DESC_DYN_POS(s_desc_count)] = + (struct usb_descriptor_header *)desc->iad; /*Control interface */ desc->c_itf = kzalloc(sizeof(*desc->c_itf), GFP_KERNEL); @@ -99,13 +132,25 @@ static int dvct_alloc_desc(struct dvct_function *d_fun) desc->c_itf->bInterfaceSubClass = USB_SUBCLASS_DEBUG_CONTROL; desc->c_itf->bInterfaceProtocol = d_fun->source_dev->protocol; - desc->fs[DVCT_CITF_DESC_POS] = + desc->fs[DVCT_CITF_DESC_DYN_POS(s_desc_count)] = (struct usb_descriptor_header *)desc->c_itf; - desc->hs[DVCT_CITF_DESC_POS] = + desc->hs[DVCT_CITF_DESC_DYN_POS(s_desc_count)] = (struct usb_descriptor_header *)desc->c_itf; - desc->ss[DVCT_CITF_DESC_POS] = + desc->ss[DVCT_CITF_DESC_DYN_POS(s_desc_count)] = (struct usb_descriptor_header *)desc->c_itf; + if (d_fun->source_dev->desc) { + /*Copy whatever the source device has provided */ + s_desc = d_fun->source_dev->desc->dvc_spec; + for (i = 0; i < s_desc_count; i++) { + desc->fs[DVCT_SOURCE_DESC_FIRST(s_desc_count) + i] + = s_desc[i]; + desc->hs[DVCT_SOURCE_DESC_FIRST(s_desc_count) + i] + = s_desc[i]; + desc->ss[DVCT_SOURCE_DESC_FIRST(s_desc_count) + i] + = s_desc[i]; + } + } /*Data interface */ desc->d_itf = kzalloc(sizeof(*desc->d_itf), GFP_KERNEL); if (!desc->d_itf) @@ -118,11 +163,11 @@ static int dvct_alloc_desc(struct dvct_function *d_fun) desc->d_itf->bInterfaceSubClass = USB_SUBCLASS_DVC_TRACE; desc->d_itf->bInterfaceProtocol = d_fun->source_dev->protocol; - desc->fs[DVCT_DITF_DESC_POS] = + desc->fs[DVCT_DITF_DESC_DYN_POS(s_desc_count)] = (struct usb_descriptor_header *)desc->d_itf; - desc->hs[DVCT_DITF_DESC_POS] = + desc->hs[DVCT_DITF_DESC_DYN_POS(s_desc_count)] = (struct usb_descriptor_header *)desc->d_itf; - desc->ss[DVCT_DITF_DESC_POS] = + desc->ss[DVCT_DITF_DESC_DYN_POS(s_desc_count)] = (struct usb_descriptor_header *)desc->d_itf; /*Full Speed ep */ @@ -136,7 +181,7 @@ static int dvct_alloc_desc(struct dvct_function *d_fun) desc->fs_ep->bmAttributes = USB_ENDPOINT_XFER_BULK; desc->fs_ep->wMaxPacketSize = cpu_to_le16(64); - desc->fs[DVCT_EP_DESC_POS] = + desc->fs[DVCT_EP_DESC_DYN_POS(s_desc_count)] = (struct usb_descriptor_header *)desc->fs_ep; /*High Speed ep */ @@ -150,7 +195,7 @@ static int dvct_alloc_desc(struct dvct_function *d_fun) desc->hs_ep->bmAttributes = USB_ENDPOINT_XFER_BULK; desc->hs_ep->wMaxPacketSize = cpu_to_le16(512); - desc->hs[DVCT_EP_DESC_POS] = + desc->hs[DVCT_EP_DESC_DYN_POS(s_desc_count)] = (struct usb_descriptor_header *)desc->hs_ep; /*Super Speed ep */ @@ -164,7 +209,7 @@ static int dvct_alloc_desc(struct dvct_function *d_fun) desc->ss_ep->bmAttributes = USB_ENDPOINT_XFER_BULK; desc->ss_ep->wMaxPacketSize = cpu_to_le16(1024); - desc->ss[DVCT_EP_DESC_POS] = + desc->ss[DVCT_EP_DESC_DYN_POS(s_desc_count)] = (struct usb_descriptor_header *)desc->ss_ep; /*Super Speed ep comp */ @@ -175,7 +220,7 @@ static int dvct_alloc_desc(struct dvct_function *d_fun) desc->ss_ep_comp->bLength = USB_DT_SS_EP_COMP_SIZE; desc->ss_ep_comp->bDescriptorType = USB_DT_SS_ENDPOINT_COMP; - desc->ss[DVCT_EP_COMP_DESC_POS] = + desc->ss[DVCT_EP_COMP_DESC_DYN_POS(s_desc_count)] = (struct usb_descriptor_header *)desc->ss_ep_comp; /* strings */ @@ -186,6 +231,14 @@ static int dvct_alloc_desc(struct dvct_function *d_fun) if (!desc->str.strings) goto err_str; + /*lookup table */ + desc->lk_tbl = + kzalloc(DVCT_STR_COUNT * sizeof(struct dvct_string_lookup), + GFP_KERNEL); + if (!desc->lk_tbl) + goto err_str_lk; + + /*actual strings */ /*IAD*/ desc->str.strings[DVCT_STR_IAD_IDX].s = kasprintf(GFP_KERNEL, "DvC Trace (%s)", @@ -193,6 +246,10 @@ static int dvct_alloc_desc(struct dvct_function *d_fun) if (!desc->str.strings[DVCT_STR_IAD_IDX].s) goto err_str_iad; + desc->lk_tbl[DVCT_STR_IAD_IDX].str = + &desc->str.strings[DVCT_STR_IAD_IDX]; + desc->lk_tbl[DVCT_STR_IAD_IDX].id = &desc->iad->iFunction; + /*control */ desc->str.strings[DVCT_STR_C_ITF_IDX].s = kasprintf(GFP_KERNEL, "DvC Trace Control (%s)", @@ -200,6 +257,10 @@ static int dvct_alloc_desc(struct dvct_function *d_fun) if (!desc->str.strings[DVCT_STR_C_ITF_IDX].s) goto err_str_ctrl; + desc->lk_tbl[DVCT_STR_C_ITF_IDX].str = + &desc->str.strings[DVCT_STR_C_ITF_IDX]; + desc->lk_tbl[DVCT_STR_C_ITF_IDX].id = &desc->c_itf->iInterface; + /*data */ desc->str.strings[DVCT_STR_D_ITF_IDX].s = kasprintf(GFP_KERNEL, "DvC Trace Data (%s)", @@ -207,6 +268,10 @@ static int dvct_alloc_desc(struct dvct_function *d_fun) if (!desc->str.strings[DVCT_STR_D_ITF_IDX].s) goto err_str_data; + desc->lk_tbl[DVCT_STR_D_ITF_IDX].str = + &desc->str.strings[DVCT_STR_D_ITF_IDX]; + desc->lk_tbl[DVCT_STR_D_ITF_IDX].id = &desc->d_itf->iInterface; + return 0; /*cleanup*/ err_str_data: @@ -214,6 +279,8 @@ static int dvct_alloc_desc(struct dvct_function *d_fun) err_str_ctrl: kfree(desc->str.strings[DVCT_STR_IAD_IDX].s); err_str_iad: + kfree(desc->lk_tbl); +err_str_lk: kfree(desc->str.strings); err_str: kfree(desc->ss_ep_comp); @@ -249,6 +316,7 @@ static void dvct_free_desc(struct dvct_function *d_fun) kfree(desc->str.strings[DVCT_STR_D_ITF_IDX].s); kfree(desc->str.strings[DVCT_STR_C_ITF_IDX].s); kfree(desc->str.strings[DVCT_STR_IAD_IDX].s); + kfree(desc->lk_tbl); kfree(desc->str.strings); kfree(desc->ss_ep_comp); kfree(desc->ss_ep); @@ -290,25 +358,25 @@ int dvct_stop_transfer(struct dvct_function *d_fun) EXPORT_SYMBOL(dvct_stop_transfer); static int dvct_strings_setup(struct usb_composite_dev *cdev, - struct dvct_function *f_fun) + struct usb_string *strings, + struct dvct_string_lookup *lk_tbl) { int status; + struct dvct_string_lookup *str_lk; DVCT_IN(); - if (!f_fun->desc.str.strings) + if (!strings || !lk_tbl) return -EINVAL; - status = usb_string_ids_tab(cdev, f_fun->desc.str.strings); + status = usb_string_ids_tab(cdev, strings); if (status < 0) return status; - f_fun->desc.iad->iFunction = - f_fun->desc.str.strings[DVCT_STR_IAD_IDX].id; - f_fun->desc.c_itf->iInterface = - f_fun->desc.str.strings[DVCT_STR_C_ITF_IDX].id; - f_fun->desc.d_itf->iInterface = - f_fun->desc.str.strings[DVCT_STR_D_ITF_IDX].id; - + for (str_lk = lk_tbl; str_lk->str; str_lk++) { + *str_lk->id = str_lk->str->id; + pr_info("Setting id %d for str \"%s\"\n", str_lk->str->id, + str_lk->str->s); + } return 0; } @@ -395,7 +463,17 @@ static int dvct_function_bind(struct usb_configuration *cconfig, DVCT_IN(); d_fun->cdev = cconfig->cdev; - ret = dvct_strings_setup(d_fun->cdev, d_fun); + /*allocate id's */ + /*strings. not crucial just print on failure */ + if (d_fun->source_dev->desc && d_fun->source_dev->desc->str.strings) { + ret = dvct_strings_setup(d_fun->cdev, + d_fun->source_dev->desc->str.strings, + d_fun->source_dev->desc->lk_tbl); + if (ret) + pr_warn("Cannot allocate source device string id's\n"); + } + ret = dvct_strings_setup(d_fun->cdev, d_fun->desc.str.strings, + d_fun->desc.lk_tbl); if (ret) pr_warn("Cannot allocate function string id's\n"); @@ -715,8 +793,13 @@ static struct usb_function *dvct_alloc_func(struct usb_function_instance *inst) if (ret) goto err_des; - /*String table */ - d_fun->function.strings = + /*String table*/ + /*1 - source dev (if present) , 1 - function, 1 - NULL */ + if (d_fun->source_dev->desc && d_fun->source_dev->desc->str.strings) + d_fun->function.strings = + kzalloc(3 * sizeof(struct usb_gadget_strings), GFP_KERNEL); + else + d_fun->function.strings = kzalloc(2 * sizeof(struct usb_gadget_strings), GFP_KERNEL); if (!d_fun->function.strings) { @@ -725,6 +808,8 @@ static struct usb_function *dvct_alloc_func(struct usb_function_instance *inst) } d_fun->function.strings[0] = &d_fun->desc.str; + if (d_fun->source_dev->desc && d_fun->source_dev->desc->str.strings) + d_fun->function.strings[1] = &d_fun->source_dev->desc->str; d_fun->function.name = "dvctrace"; d_fun->function.fs_descriptors = d_fun->desc.fs; diff --git a/drivers/usb/gadget/function/u_dvctrace.h b/drivers/usb/gadget/function/u_dvctrace.h index 564d6fa7525d..56a5d0cd577a 100644 --- a/drivers/usb/gadget/function/u_dvctrace.h +++ b/drivers/usb/gadget/function/u_dvctrace.h @@ -37,6 +37,7 @@ struct dvct_function_desc { /* strings */ struct usb_gadget_strings str; + struct dvct_string_lookup *lk_tbl; }; struct dvct_function { diff --git a/include/linux/dvctrace.h b/include/linux/dvctrace.h index 18291efb18b6..4a21f58b1aae 100644 --- a/include/linux/dvctrace.h +++ b/include/linux/dvctrace.h @@ -50,22 +50,53 @@ #define dvct_clr_status(s, m) atomic_set(s, atomic_read(s) & (~(m))) #define dvct_get_status(s, m) (atomic_read(s) & (m)) +/** + * dvct_string_lookup - helper struct to better manage the string id allocation + * @str: the usb string to allocate the id for + * @id: the location in the descriptor where the id should be updated + */ +struct dvct_string_lookup { + struct usb_string *str; + u8 *id; /*points to the location of the string id */ +}; + +/** + * dvctrace_usb_descriptors - holds DvC Trace usb descriptors information + * @dvc_spec: Dvc Trace specific descriptors, will be added in the function + * usb descriptors. + * @str: Usb gadget string table for strings referenced in @dvc_spec. + * @lk_tbl: Strings lookup table. + * + * From a strict USB point of view the descriptors are optional so @dvc_spec, + * @str.strings and @lk_tbl can be null. However the dvc-trace host + * implementation might need them. + */ +struct dvct_usb_descriptors { + struct usb_descriptor_header **dvc_spec; /*null terminated*/ + struct usb_gadget_strings str; + struct dvct_string_lookup *lk_tbl;/*null terminated*/ +}; + /** * dvct_source_device - DvC Trace function to source (backend) interface * * @name_add: postfix used to compute the source name (-) * @protocol: Interface protocol code (used in IAD, control and data + * @static_desc: DvC-Trace specific usb descriptors, provided by the driver + * interface descriptors) * - * @instance_taken, @function_taken, @lock, @device are reserved for + * @instance_taken, @function_taken, @lock, @device, @desc are reserved for * internal use only. */ struct dvct_source_device { const char *name_add; u8 protocol; + struct dvct_usb_descriptors static_desc; u32 instance_taken:1; u32 function_taken:1; spinlock_t lock; struct device device; + struct dvct_usb_descriptors *desc; }; /** From e1a5b8d69f38ff808fee5835c029d05870eab8a6 Mon Sep 17 00:00:00 2001 From: Traian Schiau Date: Thu, 31 Mar 2016 11:32:37 +0300 Subject: [PATCH 0540/1103] dvctrace: Fix topology descriptor corruption. A bug was found in dvc-trace topology descriptors (strings) allocation (insufficient space) leading to memory corruption in some cases. Also improve buffer overflow checks for long attributes (descriptors and strings). Signed-off-by: Traian Schiau --- drivers/bus/dvctrace.c | 30 ++++++++++++++++++++++++------ 1 file changed, 24 insertions(+), 6 deletions(-) diff --git a/drivers/bus/dvctrace.c b/drivers/bus/dvctrace.c index 906e972103d9..915c42593edb 100644 --- a/drivers/bus/dvctrace.c +++ b/drivers/bus/dvctrace.c @@ -161,7 +161,8 @@ static int alloc_strings(struct dvct_usb_descriptors *desc, int count) if (!desc->lk_tbl) goto err; - desc->str.strings = kzalloc(sizeof(desc->str), GFP_KERNEL); + desc->str.strings = kzalloc((count + 1) * sizeof(*desc->str.strings), + GFP_KERNEL); if (!desc->str.strings) goto err_str; @@ -212,6 +213,13 @@ static ssize_t descriptors_show(struct device *dev, int i; len = (*desc)->bLength; + + /* Check if it fits, total output is 3 * len */ + if ((ret + 3 * len) > PAGE_SIZE) { + dev_warn(dev, "Descriptors attribute page overrun\n"); + break; + } + pdesc = (u8 *)(*desc); for (i = 0; i < len; i++) ret += snprintf(buf + ret, PAGE_SIZE - ret, "%02hhX ", @@ -385,15 +393,25 @@ static ssize_t strings_show(struct device *dev, struct device_attribute *attr, for (lk_s = ds_dev->desc->lk_tbl; lk_s->str && lk_s->id; lk_s++) { int desc_offset, offset; + /* + * Check if it fits, worst case is "Unknown(%p): %s\n" + * 8 + 16 + 3 + string length + 1 + */ + if ((ret + 28 + strlen(lk_s->str->s)) > PAGE_SIZE) { + dev_warn(dev, "Strings attribute page overrun\n"); + break; + } + if (dvctrace_string_ptr_to_offset(ds_dev->desc->dvc_spec, lk_s->id, &desc_offset, &offset)) - ret += snprintf(buf + ret, PAGE_SIZE, + ret += snprintf(buf + ret, PAGE_SIZE - ret, "Unknown(%p): %s\n", lk_s->id, lk_s->str->s); else - ret += snprintf(buf + ret, PAGE_SIZE, "%d.%d: %s\n", - desc_offset, offset, lk_s->str->s); + ret += snprintf(buf + ret, PAGE_SIZE - ret, + "%d.%d: %s\n", desc_offset, offset, + lk_s->str->s); } return ret; } @@ -415,7 +433,7 @@ static ssize_t strings_store(struct device *dev, struct device_attribute *attr, } if (ds_dev->desc == &ds_dev->static_desc) { - dev_warn(&ds_dev->device, "Cannot set strings in static descriptors\n"); + dev_warn(dev, "Cannot set strings in static descriptors\n"); return -EINVAL; } @@ -442,7 +460,7 @@ static ssize_t strings_store(struct device *dev, struct device_attribute *attr, pid = dvctrace_offset_to_string_ptr(ds_dev->desc->dvc_spec, d_off, off); if (IS_ERR_OR_NULL(pid)) { - dev_warn(&ds_dev->device, "String out of bounds\n"); + dev_warn(dev, "String out of bounds\n"); free_strings(ds_dev->desc); return -EINVAL; } From ab3ab43e14cd152efc75e43e121a2b95a61b8287 Mon Sep 17 00:00:00 2001 From: Traian Schiau Date: Thu, 31 Mar 2016 18:27:14 +0300 Subject: [PATCH 0541/1103] dvctrace-function: Configfs changes Adapt to the new configfs api. Minor changes in instance/function allocation and work flow. Signed-off-by: Traian Schiau --- drivers/usb/gadget/function/f_dvctrace.c | 60 +++++++++--------------- 1 file changed, 23 insertions(+), 37 deletions(-) diff --git a/drivers/usb/gadget/function/f_dvctrace.c b/drivers/usb/gadget/function/f_dvctrace.c index d5cb49db52bc..2589632a0e90 100644 --- a/drivers/usb/gadget/function/f_dvctrace.c +++ b/drivers/usb/gadget/function/f_dvctrace.c @@ -463,6 +463,10 @@ static int dvct_function_bind(struct usb_configuration *cconfig, DVCT_IN(); d_fun->cdev = cconfig->cdev; + spin_lock(&d_fun->source_dev->lock); + d_fun->source_dev->function_taken = 1; + spin_unlock(&d_fun->source_dev->lock); + /*allocate id's */ /*strings. not crucial just print on failure */ if (d_fun->source_dev->desc && d_fun->source_dev->desc->str.strings) { @@ -548,6 +552,10 @@ static void dvct_function_unbind(struct usb_configuration *c, d_fun->online_ctrl = 0; d_fun->source_drv->unbinded(d_fun->source_dev); + + spin_lock(&d_fun->source_dev->lock); + d_fun->source_dev->function_taken = 0; + spin_unlock(&d_fun->source_dev->lock); } static int dvct_function_set_alt(struct usb_function *func, @@ -618,27 +626,6 @@ static void dvct_function_disable(struct usb_function *func) pr_debug("%s disabled\n", d_fun->function.name); } -CONFIGFS_ATTR_STRUCT(dvct_function_inst); - -static ssize_t dvct_attr_show(struct config_item *item, - struct configfs_attribute *attr, char *page) -{ - struct dvct_function_inst *d_inst; - struct dvct_function_inst_attribute *d_fun_attr; - ssize_t ret = 0; - - DVCT_IN(); - d_inst = container_of(to_config_group(item), struct dvct_function_inst, - instance.group); - d_fun_attr = container_of(attr, struct dvct_function_inst_attribute, - attr); - - if (d_fun_attr->show) - ret = d_fun_attr->show(d_inst, page); - - return ret; -} - static void dvct_attr_release(struct config_item *item) { struct dvct_function_inst *d_inst; @@ -651,19 +638,23 @@ static void dvct_attr_release(struct config_item *item) static struct configfs_item_operations dvctrace_item_ops = { .release = dvct_attr_release, - .show_attribute = dvct_attr_show, }; -static ssize_t dvct_device_show(struct dvct_function_inst *d_inst, char *page) +static ssize_t f_dvctrace_device_show(struct config_item *item, char *page) { - return sprintf(page, "%s\n", d_inst->source_dev->name_add); + struct dvct_function_inst *d_inst; + + DVCT_IN(); + d_inst = container_of(to_config_group(item), struct dvct_function_inst, + instance.group); + + return sprintf(page, "%s\n", dev_name(&d_inst->source_dev->device)); } -static struct dvct_function_inst_attribute f_dvctrace_device = - __CONFIGFS_ATTR_RO(source_dev, dvct_device_show); +CONFIGFS_ATTR_RO(f_dvctrace_, device); static struct configfs_attribute *dvct_attrs[] = { - &f_dvctrace_device.attr, + &f_dvctrace_attr_device, NULL, }; @@ -698,12 +689,15 @@ static int dvct_set_inst_name(struct usb_function_instance *inst, d_inst = to_dvct_function_inst(inst); old_src = d_inst->source_dev; - new_src = dvct_source_find_free_by_name(name); + new_src = dvct_source_find_by_name(name); if (IS_ERR_OR_NULL(new_src)) return -ENODEV; - if (new_src) { + if (new_src != old_src) { + if (new_src->instance_taken) + return -EBUSY; + spin_lock(&new_src->lock); spin_lock(&old_src->lock); @@ -755,10 +749,6 @@ static void dvct_free_func(struct usb_function *func) DVCT_IN(); d_fun->source_drv->deactivate(d_fun->source_dev); - spin_lock(&d_fun->source_dev->lock); - d_fun->source_dev->function_taken = 0; - spin_unlock(&d_fun->source_dev->lock); - dvct_free_desc(d_fun); kfree(d_fun); @@ -779,10 +769,6 @@ static struct usb_function *dvct_alloc_func(struct usb_function_instance *inst) d_fun->source_drv = dvct_source_get_drv(d_fun->source_dev); d_fun->trace_config = 0; - spin_lock(&d_fun->source_dev->lock); - d_fun->source_dev->function_taken = 1; - spin_unlock(&d_fun->source_dev->lock); - ret = d_fun->source_drv->activate(d_fun->source_dev, &d_fun->status); if (ret) { pr_err("Cannot activate source device %d\n", ret); From e75936816f65f37411d1e798649bf26c859b2eeb Mon Sep 17 00:00:00 2001 From: Guillaume Betous Date: Mon, 22 Aug 2016 16:56:06 +0200 Subject: [PATCH 0542/1103] staging/android: ABL Bootloader Control driver * Set reboot target in NVRAM - register to reboot event - create CDATA ABL code following reboot target * Set capsule parameters in NVRAM - accessible through /sys/kernel/capsule/capsule_name - same syntax as "nvram" ABL command - e.g. m1:capsule.bin - m => emmc - 1 => partition #1 - capsule.bin => file name * Notify on capsule request - accessible through /sys/kernel/capsule/capsule_requested - 0 : no capsule requested - 1 : capsule requested (triggered through /sys/kernel/capsule/capsule_name access) Signed-off-by: Guillaume Betous Signed-off-by: Tian, Baofeng --- arch/x86/configs/abl_diffconfig | 1 + drivers/staging/android/Kconfig | 1 + drivers/staging/android/Makefile | 1 + drivers/staging/android/abl/Kconfig | 8 + drivers/staging/android/abl/Makefile | 1 + drivers/staging/android/abl/ablbc.c | 360 +++++++++++++++++++++++++++ 6 files changed, 372 insertions(+) create mode 100644 arch/x86/configs/abl_diffconfig create mode 100644 drivers/staging/android/abl/Kconfig create mode 100644 drivers/staging/android/abl/Makefile create mode 100644 drivers/staging/android/abl/ablbc.c diff --git a/arch/x86/configs/abl_diffconfig b/arch/x86/configs/abl_diffconfig new file mode 100644 index 000000000000..899be931801b --- /dev/null +++ b/arch/x86/configs/abl_diffconfig @@ -0,0 +1 @@ +CONFIG_ABL_BOOTLOADER_CONTROL=y diff --git a/drivers/staging/android/Kconfig b/drivers/staging/android/Kconfig index 17c5587805f5..87054ad922aa 100644 --- a/drivers/staging/android/Kconfig +++ b/drivers/staging/android/Kconfig @@ -24,6 +24,7 @@ config ANDROID_VSOC a QEmu ivshmem device. If built as a module, it will be called vsoc. source "drivers/staging/android/ion/Kconfig" +source "drivers/staging/android/abl/Kconfig" endif # if ANDROID diff --git a/drivers/staging/android/Makefile b/drivers/staging/android/Makefile index 90e6154f11a4..729d11f4e150 100644 --- a/drivers/staging/android/Makefile +++ b/drivers/staging/android/Makefile @@ -1,6 +1,7 @@ ccflags-y += -I$(src) # needed for trace events obj-y += ion/ +obj-$(CONFIG_ABL_BOOTLOADER_CONTROL) += abl/ obj-$(CONFIG_ASHMEM) += ashmem.o obj-$(CONFIG_ANDROID_VSOC) += vsoc.o diff --git a/drivers/staging/android/abl/Kconfig b/drivers/staging/android/abl/Kconfig new file mode 100644 index 000000000000..a89ac5b5efb1 --- /dev/null +++ b/drivers/staging/android/abl/Kconfig @@ -0,0 +1,8 @@ +config ABL_BOOTLOADER_CONTROL + tristate "ABL Bootloader Control module" + default n + help + This driver installs a reboot hook, such that if reboot() is + invoked with a string argument, the corresponding ABL Action + is written in CMOS data, in order to be processed by ABL on + reboot. diff --git a/drivers/staging/android/abl/Makefile b/drivers/staging/android/abl/Makefile new file mode 100644 index 000000000000..b70a05a2af6d --- /dev/null +++ b/drivers/staging/android/abl/Makefile @@ -0,0 +1 @@ +obj-$(CONFIG_ABL_BOOTLOADER_CONTROL) += ablbc.o diff --git a/drivers/staging/android/abl/ablbc.c b/drivers/staging/android/abl/ablbc.c new file mode 100644 index 000000000000..a9f82d658247 --- /dev/null +++ b/drivers/staging/android/abl/ablbc.c @@ -0,0 +1,360 @@ +/* + * ablbc: control ABL bootloaders + * Copyright (c) 2013-2016, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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. + */ + +#include +#include +#include +#include +#include +#include + +#define MODULE_NAME "ablbc" + +/* RTC read and write */ +static inline unsigned char cmos_read_ext_bank(u8 addr) +{ + outb(addr, RTC_PORT(4)); + return inb(RTC_PORT(5)); +} +#define CMOS_READ_EXT(a) cmos_read_ext_bank(a) + +static inline void cmos_write_ext_bank(u8 val, u8 addr) +{ + outb(addr, RTC_PORT(4)); + outb(val, RTC_PORT(5)); +} +#define CMOS_WRITE_EXT(v, a) cmos_write_ext_bank(v, a) + +/* ABL Conventions */ +#define NVRAM_START_ADDRESS 0x10 + +#define _USERCMD_(cmd, len) (((cmd) << 5) | ((len) & 0x1f)) +#define USERCMD_END _USERCMD_(0, 0) +#define USERCMD_ACTION _USERCMD_(7, 1) +#define USERCMD_UPDATE_IFWI(len) _USERCMD_(2, len) + +#define CDATA_TAG_USER_CMD 0x4d +#define NVRAM_VALID_FLAG 0x12 + +#define CRC32C_POLYNOMIAL 0x82F63B78 /* CRC32C Castagnoli */ + +static bool capsule_request; + +union _cdata_header { + uint32_t data; + struct { + unsigned ncond : 2; + unsigned length : 10; + unsigned flags : 4; + unsigned version: 4; + unsigned tag : 12; + }; +}; + +struct nvram_capsule_cmd { + char action; + char device; + char partition; + char file_name[1]; +} __packed; + +struct nvram_reboot_cmd { + char action; + char target; + char end; + char padding; +} __packed; + +struct name2id { + const char *name; + int id; +}; + +struct nvram_msg { + char magic; + char size; + union _cdata_header cdata_header; + char *cdata_payload; + size_t cdata_payload_size; + uint32_t crc; +} __packed; + +static const struct name2id NAME2ID[] = { + { "main", 0x00 }, + { "android", 0x00 }, + { "bootloader", 0x01 }, + { "fastboot", 0x01 }, + { "elk", 0x02 }, + { "recovery", 0x03 }, + { "cli", 0x10 }, +}; + +static size_t offset; /* memorize offset between each call */ + +static size_t write_data_to_nvram(char *data, size_t size) +{ + int i; + unsigned long flags; + + spin_lock_irqsave(&rtc_lock, flags); + for (i = 0; i < size; i++) + CMOS_WRITE_EXT(*(data + i), NVRAM_START_ADDRESS + offset + i); + + offset += size; + spin_unlock_irqrestore(&rtc_lock, flags); + + return i; +} + +static void write_msg_to_nvram(struct nvram_msg *nvram_msg) +{ + /* Ensure to start from top : only one command expected */ + offset = 0; + write_data_to_nvram(nvram_msg, + offsetof(struct nvram_msg, cdata_payload)); + write_data_to_nvram(nvram_msg->cdata_payload, + nvram_msg->cdata_payload_size); + write_data_to_nvram(&(nvram_msg->crc), sizeof(nvram_msg->crc)); +} + +/* Compute CRC for one byte (shift register-based: one bit at a time). */ +static uint32_t crc32c_byte(uint32_t crc, unsigned byte) +{ + int i; + uint32_t c; + + for (i = 0 ; i < 8 ; i += 1) { + c = (crc ^ byte) & 1; + if (c) + crc = (crc >> 1) ^ CRC32C_POLYNOMIAL; + else + crc = (crc >> 1); + byte >>= 1; + } + + return crc; +} + +/* Compute CRC for a given buffer. */ +static uint32_t crc32c_buf(uint32_t crc, const void *addr, unsigned len) +{ + unsigned i; + + for (i = 0 ; i < len ; i += 1) + crc = crc32c_byte(crc, *(uint8_t *)(addr + i)); + + return crc; +} + +static uint32_t crc32c_msg(struct nvram_msg *nvram_msg) +{ + uint32_t crc; + + crc = crc32c_buf(~0, nvram_msg, + offsetof(struct nvram_msg, cdata_payload)); + crc = crc32c_buf(crc, nvram_msg->cdata_payload, + nvram_msg->cdata_payload_size); + return crc; +} + +static struct kobject *capsule_kobject; + +static ssize_t is_capsule_requested(struct kobject *kobj, + struct kobj_attribute *attr, char *buf, size_t count) +{ + return sprintf(buf, "%d\n", capsule_request); +} + +enum capsule_device_type { + EMMC = 2, + SDCARD = 4 +}; + +static ssize_t capsule_store(struct kobject *kobj, struct kobj_attribute *attr, + char *buf, size_t count) +{ + struct nvram_msg msg; + struct nvram_capsule_cmd *capsule_cmd; + char name[32], partition; + enum capsule_device_type device; + int ret, crc, padding; + unsigned char size; + union _cdata_header cdh; + + device = (buf[0] == 'm' ? EMMC : SDCARD); + partition = buf[1] - '0'; + ret = sscanf(buf+3, "%s", name); + pr_info(MODULE_NAME " capsule parameters (%d): DEVICE=%d PARTITION=%d NAME=%s\n", + ret, device, partition, name); + + cdh.data = 0; + cdh.tag = CDATA_TAG_USER_CMD; + + /* padding of filename on next dword */ + padding = (4 - (3 + strlen(name))%4)%4; + size = 2 + sizeof(cdh) + 3 + strlen(name) + padding + 4; + cdh.length = 1 + (3 + strlen(name) + padding) / 4; + + msg.magic = NVRAM_VALID_FLAG; + msg.size = size; + msg.cdata_header.data = cdh.data; + + capsule_cmd = kmalloc(size, GFP_KERNEL); + if (!capsule_cmd) + return -ENOMEM; + + capsule_cmd->action = USERCMD_UPDATE_IFWI(strlen(name) + 2); + capsule_cmd->device = device; + capsule_cmd->partition = partition; + strncpy(capsule_cmd->file_name, name, strlen(name)); + msg.cdata_payload = capsule_cmd; + msg.cdata_payload_size = 3 + strlen(name) + padding; + msg.crc = crc32c_msg(&msg); + write_msg_to_nvram(&msg); + capsule_request = true; + + kfree(capsule_cmd); + + return count; +} + +static struct kobj_attribute capsule_name_attribute = + __ATTR(capsule_name, 0600, NULL, capsule_store); + +static struct kobj_attribute capsule_requested_attribute = + __ATTR(capsule_requested, 0400, is_capsule_requested, NULL); + +static int reboot_target_name2id(const char *name) +{ + size_t i; + + for (i = 0; i < ARRAY_SIZE(NAME2ID); i++) + if (!strcmp(NAME2ID[i].name, name)) + return NAME2ID[i].id; + + return -EINVAL; +} + +static int set_reboot_target(const char *name) +{ + int id; + struct nvram_msg msg; + struct nvram_reboot_cmd reboot_cmd; + union _cdata_header cdh; + + if (name == NULL) { + pr_err("Error in %s: NULL target\n", __func__); + return -EINVAL; + } + + id = reboot_target_name2id(name); + if (id < 0) { + pr_err("Error in %s: '%s' is not a valid target\n", + __func__, name); + return -EINVAL; + } + + cdh.data = 0; + cdh.length = 2; /* 2*32 bits, from header to padding */ + cdh.tag = CDATA_TAG_USER_CMD; + + memset(&reboot_cmd, 0, sizeof(reboot_cmd)); + memset(&msg, 0, sizeof(msg)); + msg.magic = NVRAM_VALID_FLAG; + msg.cdata_header.data = cdh.data; + reboot_cmd.action = USERCMD_ACTION; + + reboot_cmd.target = id; + msg.cdata_payload = &reboot_cmd; + msg.cdata_payload_size = sizeof(reboot_cmd); + msg.size = offsetof(struct nvram_msg, cdata_payload) + + sizeof(reboot_cmd) + sizeof(msg.crc); + msg.crc = crc32c_msg(&msg); + + write_msg_to_nvram(&msg); + + return 0; +} + +static const unsigned int DEFAULT_TARGET_INDEX; + +static int ablbc_reboot_notifier_call(struct notifier_block *notifier, + unsigned long what, void *data) +{ + const char *target = (const char *)data; + int ret; + + if (what != SYS_RESTART) + return NOTIFY_DONE; + + if (target[0] != '\0') + ret = set_reboot_target(target); + if (ret) + pr_err("%s: Failed to set reboot target, ret=%d\n", + __func__, ret); + + return NOTIFY_DONE; +} + +static struct notifier_block ablbc_reboot_notifier = { + .notifier_call = ablbc_reboot_notifier_call, +}; + +static int __init ablbc_init(void) +{ + int ret; + + ret = register_reboot_notifier(&ablbc_reboot_notifier); + if (ret) { + pr_err(MODULE_NAME ": unable to register reboot notifier\n"); + return ret; + } + + capsule_kobject = kobject_create_and_add("capsule", kernel_kobj); + if (!capsule_kobject) + return -ENOMEM; + + ret = sysfs_create_file(capsule_kobject, + &capsule_name_attribute.attr); + if (ret) { + pr_err("failed to create the foo file in /sys/kernel/capsule/capsule_name\n"); + goto err; + } + + ret = sysfs_create_file(capsule_kobject, + &capsule_requested_attribute.attr); + if (ret) { + pr_err("failed to create the foo file in /sys/kernel/capsule/capsule_requested\n"); + goto err; + } + + return 0; + +err: + kobject_put(capsule_kobject); + return ret; +} + +module_init(ablbc_init); + +static void __exit ablbc_exit(void) +{ + unregister_reboot_notifier(&ablbc_reboot_notifier); + kobject_put(capsule_kobject); +} +module_exit(ablbc_exit); + +MODULE_AUTHOR("Guillaume Betous "); +MODULE_DESCRIPTION("Automotive Bootloader boot control driver"); +MODULE_LICENSE("GPL v2"); From 33ae6500925bed97dcb16fa7a6c0360cfccc511c Mon Sep 17 00:00:00 2001 From: Guillaume Betous Date: Thu, 6 Oct 2016 12:51:07 +0200 Subject: [PATCH 0543/1103] ablbc: SLCAN commands on shutdown Moving shutdown logic to driver as we can't detect shutdown in userspace in ROS (no SIGKILL to all daemons, no logic in init.rc files) Signed-off-by: Guillaume Betous --- drivers/staging/android/abl/ablbc.c | 61 ++++++++++++++++++++++++++++- 1 file changed, 60 insertions(+), 1 deletion(-) diff --git a/drivers/staging/android/abl/ablbc.c b/drivers/staging/android/abl/ablbc.c index a9f82d658247..d02ac378fd70 100644 --- a/drivers/staging/android/abl/ablbc.c +++ b/drivers/staging/android/abl/ablbc.c @@ -18,6 +18,7 @@ #include #include #include +#include #define MODULE_NAME "ablbc" @@ -289,6 +290,48 @@ static int set_reboot_target(const char *name) static const unsigned int DEFAULT_TARGET_INDEX; +static const char * const cold_reset[] = { + "/sbin/cansend", + "slcan0", + "0000FFFF#05025555555555", + NULL}; +static const char * const cold_reset_capsule[] = { + "/sbin/cansend", + "slcan0", + "0000FFFF#05035555555555", + NULL}; +static const char * const suppress_heartbeat[] = { + "/sbin/cansend", + "slcan0", + "0000FFFF#01035555555555", + NULL}; +static const char * const reboot_request[] = { + "/sbin/cansend", + "slcan0", + "0000FFFF#03015555555555", + NULL}; + +static int execute_slcan_command(const char *cmd[]) +{ + struct subprocess_info *sub_info; + int ret = -1; + + sub_info = call_usermodehelper_setup(cmd[0], + cmd, NULL, GFP_KERNEL, + NULL, NULL, NULL); + + if (sub_info) { + ret = call_usermodehelper_exec(sub_info, + UMH_WAIT_PROC); + pr_info("Exec cmd=%s ret=%d\n", cmd[0], ret); + } + + if (!ret) + pr_err("Failure on cmd=%s ret=%d\n", cmd[0], ret); + + return ret; +} + static int ablbc_reboot_notifier_call(struct notifier_block *notifier, unsigned long what, void *data) { @@ -298,12 +341,28 @@ static int ablbc_reboot_notifier_call(struct notifier_block *notifier, if (what != SYS_RESTART) return NOTIFY_DONE; - if (target[0] != '\0') + ret = execute_slcan_command(suppress_heartbeat); + if (ret) + goto done; + + ret = execute_slcan_command(reboot_request); + if (ret) + goto done; + if (target[0] != '\0') { ret = set_reboot_target(target); if (ret) pr_err("%s: Failed to set reboot target, ret=%d\n", __func__, ret); + else { + ret = execute_slcan_command(cold_reset); + if (ret) + goto done; + } + } + if (capsule_request) + ret = execute_slcan_command(cold_reset_capsule); +done: return NOTIFY_DONE; } From 20d7d851d5612d8730d7442ad7d55f95060ff4d7 Mon Sep 17 00:00:00 2001 From: songjinguo Date: Fri, 28 Oct 2016 09:40:21 +0800 Subject: [PATCH 0544/1103] add crashmode for adb reboot crashmode function. Signed-off-by: songjinguo --- drivers/staging/android/abl/ablbc.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/staging/android/abl/ablbc.c b/drivers/staging/android/abl/ablbc.c index d02ac378fd70..ac9afbd36318 100644 --- a/drivers/staging/android/abl/ablbc.c +++ b/drivers/staging/android/abl/ablbc.c @@ -98,6 +98,7 @@ static const struct name2id NAME2ID[] = { { "fastboot", 0x01 }, { "elk", 0x02 }, { "recovery", 0x03 }, + { "crashmode", 0x04 }, { "cli", 0x10 }, }; From d9202969d7777494c118c530e39c234a13e7a916 Mon Sep 17 00:00:00 2001 From: "xihua.chen" Date: Fri, 4 Nov 2016 15:48:19 +0800 Subject: [PATCH 0545/1103] ablbc: fix debug message error Signed-off-by: xihua.chen --- drivers/staging/android/abl/ablbc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/android/abl/ablbc.c b/drivers/staging/android/abl/ablbc.c index ac9afbd36318..c7ed097b8ac1 100644 --- a/drivers/staging/android/abl/ablbc.c +++ b/drivers/staging/android/abl/ablbc.c @@ -327,7 +327,7 @@ static int execute_slcan_command(const char *cmd[]) pr_info("Exec cmd=%s ret=%d\n", cmd[0], ret); } - if (!ret) + if (ret) pr_err("Failure on cmd=%s ret=%d\n", cmd[0], ret); return ret; From 2c02217a5f0a3e0ba36082c7773bbf99edcf08e2 Mon Sep 17 00:00:00 2001 From: "Tian, Baofeng" Date: Fri, 18 Aug 2017 06:56:56 +0800 Subject: [PATCH 0546/1103] iTCO_wdt: add module parameter "force_no_reboot" Setting "force_no_reboot" parameter to true (y/Y/1) will have the effect to prevent to reset the NO_REBOOT flag thus preventing the tco to reboot the platform, if not set or set to false, then system will reboot after about 30s. Signed-off-by: Tian, Baofeng --- drivers/watchdog/iTCO_wdt.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/drivers/watchdog/iTCO_wdt.c b/drivers/watchdog/iTCO_wdt.c index 347f0389b089..255318bb425c 100644 --- a/drivers/watchdog/iTCO_wdt.c +++ b/drivers/watchdog/iTCO_wdt.c @@ -131,6 +131,11 @@ module_param(turn_SMI_watchdog_clear_off, int, 0); MODULE_PARM_DESC(turn_SMI_watchdog_clear_off, "Turn off SMI clearing watchdog (depends on TCO-version)(default=1)"); +static bool force_no_reboot; +module_param(force_no_reboot, bool, 0); +MODULE_PARM_DESC(force_no_reboot, + "Prevents the watchdog rebooting the platform (default=0)"); + /* * Some TCO specific functions */ @@ -243,6 +248,10 @@ static int iTCO_wdt_start(struct watchdog_device *wd_dev) struct iTCO_wdt_private *p = watchdog_get_drvdata(wd_dev); unsigned int val; + /* force_no_reboot will prevent to unset NO_REBOOT bit */ + if (force_no_reboot) + return -EIO; + spin_lock(&p->io_lock); iTCO_vendor_pre_start(p->smi_res, wd_dev->timeout); @@ -250,7 +259,7 @@ static int iTCO_wdt_start(struct watchdog_device *wd_dev) /* disable chipset's NO_REBOOT bit */ if (p->update_no_reboot_bit(p->no_reboot_priv, false)) { spin_unlock(&p->io_lock); - pr_err("failed to reset NO_REBOOT flag, reboot disabled by hardware/BIOS\n"); + pr_err("failed to reset NO_REBOOT flag, reboot disabled by hardware/BIOS/rc_cmd\n"); return -EIO; } From ec312e9bf407ef428bfb61f49dc643096547b4b8 Mon Sep 17 00:00:00 2001 From: "Tian, Baofeng" Date: Wed, 30 Aug 2017 07:39:49 +0800 Subject: [PATCH 0547/1103] kernel: watchdog: add NMI handler for iTCO watchdog once this NMI triggered, it will dump all cpu backtraces and call panic to continue reboot. Signed-off-by: Tian, Baofeng --- drivers/watchdog/iTCO_wdt.c | 53 +++++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) mode change 100644 => 100755 drivers/watchdog/iTCO_wdt.c diff --git a/drivers/watchdog/iTCO_wdt.c b/drivers/watchdog/iTCO_wdt.c old mode 100644 new mode 100755 index 255318bb425c..9a2a9b6b865c --- a/drivers/watchdog/iTCO_wdt.c +++ b/drivers/watchdog/iTCO_wdt.c @@ -68,6 +68,8 @@ #include /* For inb/outb/... */ #include +#include +#include #include "iTCO_vendor.h" /* Address definitions for the TCO */ @@ -86,6 +88,11 @@ #define TCO2_CNT(p) (TCOBASE(p) + 0x0a) /* TCO2 Control Register */ #define TCOv2_TMR(p) (TCOBASE(p) + 0x12) /* TCOv2 Timer Initial Value*/ +#define TCO_RLD_sub(base) (base + 0x00) /* TCO Timer Reload/Curr. Value */ +#define TCO1_STS_sub(base) (base + 0x04) /* TCO1 Status Register */ +#define TCOv2_TMR_sub(base) (base + 0x12) /* TCOv2 Timer Initial Value*/ + + /* internal variables */ struct iTCO_wdt_private { struct watchdog_device wddev; @@ -112,6 +119,14 @@ struct iTCO_wdt_private { int (*update_no_reboot_bit)(void *p, bool set); }; +static struct { + resource_size_t tco_base_address; + unsigned int iTCO_version; + bool pretimeout_occurred; + unsigned int second_to_ticks; +} iTCO_wdt_sub; + + /* module parameters */ #define WATCHDOG_TIMEOUT 30 /* 30 sec default heartbeat */ static int heartbeat = WATCHDOG_TIMEOUT; /* in seconds */ @@ -431,6 +446,33 @@ static const struct watchdog_ops iTCO_wdt_ops = { .get_timeleft = iTCO_wdt_get_timeleft, }; +static int iTCO_pretimeout(unsigned int cmd, struct pt_regs *unused_regs) +{ + resource_size_t tco_base_address; + + /* Prevent re-entrance */ + if (iTCO_wdt_sub.pretimeout_occurred) + return NMI_HANDLED; + + tco_base_address = iTCO_wdt_sub.tco_base_address; + + /* Check the NMI is from the TCO first expiration */ + if (inw(TCO1_STS_sub(tco_base_address)) & 0x8) { + iTCO_wdt_sub.pretimeout_occurred = true; + + /* Forward next expiration */ + outw(iTCO_wdt_sub.second_to_ticks, TCOv2_TMR_sub(tco_base_address)); + outw(0x01, TCO_RLD_sub(tco_base_address)); + + trigger_all_cpu_backtrace(); + panic_timeout = 0; + panic("Kernel Watchdog"); + return NMI_HANDLED; + } + + return NMI_DONE; +} + /* * Init & exit routines */ @@ -564,6 +606,17 @@ static int iTCO_wdt_probe(struct platform_device *pdev) return ret; } + /* init vars that used for nmi handler */ + iTCO_wdt_sub.iTCO_version = p->iTCO_version; + iTCO_wdt_sub.second_to_ticks = seconds_to_ticks(p, 10); + iTCO_wdt_sub.tco_base_address = TCOBASE(p); + + ret = register_nmi_handler(NMI_LOCAL, iTCO_pretimeout, 0 ,"iTCO_wdt"); + if (ret != 0) { + pr_err("cannot register nmi handler (err=%d)\n", ret); + return ret; + } + pr_info("initialized. heartbeat=%d sec (nowayout=%d)\n", heartbeat, nowayout); From 1aa83993ce6d25a57bcc0cc6cb9457b88600d9c2 Mon Sep 17 00:00:00 2001 From: Laurent FERT Date: Thu, 26 Feb 2015 18:58:04 +0100 Subject: [PATCH 0548/1103] Add sven header to stm console Signed-off-by: Laurent FERT --- drivers/hwtracing/stm/console.c | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/drivers/hwtracing/stm/console.c b/drivers/hwtracing/stm/console.c index a00f65e21747..a363467ac4a8 100644 --- a/drivers/hwtracing/stm/console.c +++ b/drivers/hwtracing/stm/console.c @@ -31,8 +31,22 @@ static void stm_console_write(struct console *con, const char *buf, unsigned len) { struct stm_console *sc = container_of(con, struct stm_console, console); + static char svenbuf[1024]; + char *p = svenbuf; + unsigned int towrite; + u16 textlen; + const u32 sven_header = 0x01000242; - stm_source_write(&sc->data, 0, buf, len); + textlen = min_t(u16, len, 1024 - sizeof(sven_header) - sizeof(textlen)); + towrite = textlen + sizeof(sven_header) + sizeof(textlen); + + memcpy(p, &sven_header, sizeof(sven_header)); + p += sizeof(sven_header); + memcpy(p, &textlen, sizeof(textlen)); + p += sizeof(textlen); + memcpy(p, buf, textlen); + + stm_source_write(&sc->data, 0, svenbuf, towrite); } static int stm_console_link(struct stm_source_data *data) From 0ee82735d7c5e8d721da061dddf2c8fd4beced7c Mon Sep 17 00:00:00 2001 From: Laurent FERT Date: Mon, 27 Apr 2015 11:39:59 +0200 Subject: [PATCH 0549/1103] Do not reset trace hub on probe Initialize gth with current registers values when probing. Signed-off-by: Laurent FERT Signed-off-by: Tian, Baofeng --- drivers/hwtracing/intel_th/gth.c | 96 ++++++++++++++------------------ 1 file changed, 42 insertions(+), 54 deletions(-) diff --git a/drivers/hwtracing/intel_th/gth.c b/drivers/hwtracing/intel_th/gth.c index 8426b7970c14..2683f0e7f993 100644 --- a/drivers/hwtracing/intel_th/gth.c +++ b/drivers/hwtracing/intel_th/gth.c @@ -139,6 +139,24 @@ gth_master_set(struct gth_device *gth, unsigned int master, int port) iowrite32(val, gth->base + reg); } +static int gth_master_get(struct gth_device *gth, unsigned int master) +{ + unsigned int reg = REG_GTH_SWDEST0 + ((master >> 1) & ~3u); + unsigned int shift = (master & 0x7) * 4; + u32 val; + + if (master >= 256) { + reg = REG_GTH_GSWTDEST; + shift = 0; + } + + val = ioread32(gth->base + reg); + val &= (0xf << shift); + val >>= shift; + + return val ? val & 0x7 : -1; +} + static ssize_t master_attr_show(struct device *dev, struct device_attribute *attr, char *buf) @@ -183,13 +201,7 @@ static ssize_t master_attr_store(struct device *dev, if (old_port >= 0) { gth->master[ma->master] = -1; clear_bit(ma->master, gth->output[old_port].master); - - /* - * if the port is active, program this setting, - * implies that runtime PM is on - */ - if (gth->output[old_port].output->active) - gth_master_set(gth, ma->master, -1); + gth_master_set(gth, ma->master, -1); } /* connect to the new output port, if any */ @@ -201,10 +213,8 @@ static ssize_t master_attr_store(struct device *dev, } set_bit(ma->master, gth->output[port].master); - - /* if the port is active, program this setting, see above */ - if (gth->output[port].output->active) - gth_master_set(gth, ma->master, port); + gth_master_set(gth, ma->master, port); + gth->master[ma->master] = port; } gth->master[ma->master] = port; @@ -275,40 +285,21 @@ gth_output_parm_get(struct gth_device *gth, int port, unsigned int parm) /* * Reset outputs and sources */ -static int intel_th_gth_reset(struct gth_device *gth) +static void intel_th_gth_reset(struct gth_device *gth) { - u32 reg; - int port, i; - - reg = ioread32(gth->base + REG_GTH_SCRPD0); - if (reg & SCRPD_DEBUGGER_IN_USE) - return -EBUSY; + u32 scratchpad; /* Always save/restore STH and TU registers in S0ix entry/exit */ - reg |= SCRPD_STH_IS_ENABLED | SCRPD_TRIGGER_IS_ENABLED; - iowrite32(reg, gth->base + REG_GTH_SCRPD0); - - /* output ports */ - for (port = 0; port < 8; port++) { - if (gth_output_parm_get(gth, port, TH_OUTPUT_PARM(port)) == - GTH_NONE) - continue; + scratchpad = ioread32(gth->base + REG_GTH_SCRPD0); + scratchpad |= SCRPD_STH_IS_ENABLED | SCRPD_TRIGGER_IS_ENABLED; + iowrite32(scratchpad, gth->base + REG_GTH_SCRPD0); - gth_output_set(gth, port, 0); - gth_smcfreq_set(gth, port, 16); - } /* disable overrides */ iowrite32(0, gth->base + REG_GTH_DESTOVR); - /* masters swdest_0~31 and gswdest */ - for (i = 0; i < 33; i++) - iowrite32(0, gth->base + REG_GTH_SWDEST0 + i * 4); - /* sources */ iowrite32(0, gth->base + REG_GTH_SCR); iowrite32(0xfc, gth->base + REG_GTH_SCR2); - - return 0; } /* @@ -530,6 +521,8 @@ static void intel_th_gth_enable(struct intel_th_device *thdev, int master; spin_lock(>h->gth_lock); + intel_th_gth_reset(gth); + for_each_set_bit(master, gth->output[output->port].master, TH_CONFIGURABLE_MASTERS + 1) { gth_master_set(gth, master, output->port); @@ -649,6 +642,7 @@ static int intel_th_gth_probe(struct intel_th_device *thdev) struct resource *res; void __iomem *base; int i, ret; + u32 scratchpad; res = intel_th_device_get_resource(thdev, IORESOURCE_MEM, 0); if (!res) @@ -666,29 +660,23 @@ static int intel_th_gth_probe(struct intel_th_device *thdev) gth->base = base; spin_lock_init(>h->gth_lock); - dev_set_drvdata(dev, gth); + dev_set_drvdata(dev, gth); - /* - * Host mode can be signalled via SW means or via SCRPD_DEBUGGER_IN_USE - * bit. Either way, don't reset HW in this case, and don't export any - * capture configuration attributes. Also, refuse to assign output - * drivers to ports, see intel_th_gth_assign(). - */ - if (thdev->host_mode) - return 0; + /* + * Host mode can be signalled via SW means or via SCRPD_DEBUGGER_IN_USE + * bit. Either way, don't reset HW in this case, and don't export any + * capture configuration attributes. Also, refuse to assign output + * drivers to ports, see intel_th_gth_assign(). + */ + if (thdev->host_mode) + return 0; - ret = intel_th_gth_reset(gth); - if (ret) { - if (ret != -EBUSY) - return ret; - - thdev->host_mode = true; - - return 0; - } + scratchpad = ioread32(gth->base + REG_GTH_SCRPD0); + if (scratchpad & SCRPD_DEBUGGER_IN_USE) + return -EBUSY; for (i = 0; i < TH_CONFIGURABLE_MASTERS + 1; i++) - gth->master[i] = -1; + gth->master[i] = gth_master_get(gth, i); for (i = 0; i < TH_POSSIBLE_OUTPUTS; i++) { gth->output[i].gth = gth; From 58f084969482d1b1a394a998e5f25a93748cbe3e Mon Sep 17 00:00:00 2001 From: Laurent FERT Date: Thu, 2 Apr 2015 11:06:34 +0200 Subject: [PATCH 0550/1103] Add switch window control from sysfs Sequence to get the MSC to switch window is to * assert a trigger * de-assert storeEn * re-assert storeEn Signed-off-by: Laurent FERT --- drivers/hwtracing/intel_th/core.c | 21 +++++ drivers/hwtracing/intel_th/gth.c | 122 +++++++++++++++++++++----- drivers/hwtracing/intel_th/gth.h | 7 ++ drivers/hwtracing/intel_th/intel_th.h | 9 +- drivers/hwtracing/intel_th/msu.c | 51 ++++++++--- 5 files changed, 176 insertions(+), 34 deletions(-) diff --git a/drivers/hwtracing/intel_th/core.c b/drivers/hwtracing/intel_th/core.c index fc6b7f8b62fb..fb7c9a5144e4 100644 --- a/drivers/hwtracing/intel_th/core.c +++ b/drivers/hwtracing/intel_th/core.c @@ -923,6 +923,27 @@ int intel_th_trace_enable(struct intel_th_device *thdev) } EXPORT_SYMBOL_GPL(intel_th_trace_enable); +/** + * intel_th_trace_switch() - execute a switch sequence + * @thdev: output device that requests tracing switch + */ +int intel_th_trace_switch(struct intel_th_device *thdev) +{ + struct intel_th_device *hub = to_intel_th_device(thdev->dev.parent); + struct intel_th_driver *hubdrv = to_intel_th_driver(hub->dev.driver); + + if (WARN_ON_ONCE(hub->type != INTEL_TH_SWITCH)) + return -EINVAL; + + if (WARN_ON_ONCE(thdev->type != INTEL_TH_OUTPUT)) + return -EINVAL; + + hubdrv->trig_switch(hub, &thdev->output); + + return 0; +} +EXPORT_SYMBOL_GPL(intel_th_trace_switch); + /** * intel_th_trace_disable() - disable tracing for an output device * @thdev: output device that requests tracing be disabled diff --git a/drivers/hwtracing/intel_th/gth.c b/drivers/hwtracing/intel_th/gth.c index 2683f0e7f993..7d1c2e9cd6b2 100644 --- a/drivers/hwtracing/intel_th/gth.c +++ b/drivers/hwtracing/intel_th/gth.c @@ -300,6 +300,10 @@ static void intel_th_gth_reset(struct gth_device *gth) /* sources */ iowrite32(0, gth->base + REG_GTH_SCR); iowrite32(0xfc, gth->base + REG_GTH_SCR2); + + /* setup CTS for single trigger */ + iowrite32(0x80000000, gth->base + REG_CTS_C0S0_EN); + iowrite32(0x40000010, gth->base + REG_CTS_C0S0_ACT); } /* @@ -447,6 +451,66 @@ static int intel_th_output_attributes(struct gth_device *gth) return sysfs_create_group(>h->dev->kobj, >h->output_group); } +/** + * intel_th_gth_stop() - stop tracing to an output device + * @gth: GTH device + * @output: output device's descriptor + * @capture_done: set when no more traces will be captured + * + * This will stop tracing using force storeEn off signal and wait for the + * pipelines to be empty for the corresponding output port. + */ +static void intel_th_gth_stop(struct gth_device *gth, + struct intel_th_output *output, + bool capture_done) +{ + struct intel_th_device *outdev = + container_of(output, struct intel_th_device, output); + unsigned long count; + u32 reg; + u32 scr2 = 0xfc | (capture_done ? 1 : 0); + + iowrite32(0, gth->base + REG_GTH_SCR); + iowrite32(scr2, gth->base + REG_GTH_SCR2); + + /* wait on pipeline empty for the given port */ + for (reg = 0, count = GTH_PLE_WAITLOOP_DEPTH; + count && !(reg & BIT(output->port)); count--) { + reg = ioread32(gth->base + REG_GTH_STAT); + cpu_relax(); + } + + if (!count) + dev_dbg(gth->dev, "timeout waiting for GTH[%d] PLE\n", + output->port); + + /* wait on output piepline empty */ + if (output->wait_empty) + output->wait_empty(outdev); + + /* clear force capture done for next captures */ + iowrite32(0xfc, gth->base + REG_GTH_SCR2); +} + +/** + * intel_th_gth_start() - start tracing to an output device + * @gth: GTH device + * @output: output device's descriptor + * + * This will start tracing using force storeEn signal. + */ +static void intel_th_gth_start(struct gth_device *gth, + struct intel_th_output *output) +{ + u32 scr = 0xfc0000; + + if (output->multiblock) + scr |= 0xff; + + iowrite32(scr, gth->base + REG_GTH_SCR); + iowrite32(0, gth->base + REG_GTH_SCR2); +} + /** * intel_th_gth_disable() - disable tracing to an output device * @thdev: GTH device @@ -460,7 +524,6 @@ static void intel_th_gth_disable(struct intel_th_device *thdev, struct intel_th_output *output) { struct gth_device *gth = dev_get_drvdata(&thdev->dev); - unsigned long count; int master; u32 reg; @@ -473,22 +536,7 @@ static void intel_th_gth_disable(struct intel_th_device *thdev, } spin_unlock(>h->gth_lock); - iowrite32(0, gth->base + REG_GTH_SCR); - iowrite32(0xfd, gth->base + REG_GTH_SCR2); - - /* wait on pipeline empty for the given port */ - for (reg = 0, count = GTH_PLE_WAITLOOP_DEPTH; - count && !(reg & BIT(output->port)); count--) { - reg = ioread32(gth->base + REG_GTH_STAT); - cpu_relax(); - } - - /* clear force capture done for next captures */ - iowrite32(0xfc, gth->base + REG_GTH_SCR2); - - if (!count) - dev_dbg(&thdev->dev, "timeout waiting for GTH[%d] PLE\n", - output->port); + intel_th_gth_stop(gth, output, true); reg = ioread32(gth->base + REG_GTH_SCRPD0); reg &= ~output->scratchpad; @@ -517,7 +565,7 @@ static void intel_th_gth_enable(struct intel_th_device *thdev, { struct gth_device *gth = dev_get_drvdata(&thdev->dev); struct intel_th *th = to_intel_th(thdev); - u32 scr = 0xfc0000, scrpd; + u32 scrpd; int master; spin_lock(>h->gth_lock); @@ -528,9 +576,6 @@ static void intel_th_gth_enable(struct intel_th_device *thdev, gth_master_set(gth, master, output->port); } - if (output->multiblock) - scr |= 0xff; - output->active = true; spin_unlock(>h->gth_lock); @@ -541,8 +586,38 @@ static void intel_th_gth_enable(struct intel_th_device *thdev, scrpd |= output->scratchpad; iowrite32(scrpd, gth->base + REG_GTH_SCRPD0); - iowrite32(scr, gth->base + REG_GTH_SCR); - iowrite32(0, gth->base + REG_GTH_SCR2); + intel_th_gth_start(gth, output); +} + +/** + * intel_th_gth_switch() - execute a switch sequence + * @thdev: GTH device + * @output: output device's descriptor + * + * This will execute a switch sequence that will trigger a switch window + * when tracing to MSC in multi-block mode. + */ +static void intel_th_gth_switch(struct intel_th_device *thdev, + struct intel_th_output *output) +{ + struct gth_device *gth = dev_get_drvdata(&thdev->dev); + unsigned long count; + u32 reg; + + /* trigger */ + iowrite32(0, gth->base + REG_CTS_CTL); + iowrite32(1, gth->base + REG_CTS_CTL); + /* wait on trigger status */ + for (reg = 0, count = CTS_TRIG_WAITLOOP_DEPTH; + count && !(reg & BIT(4)); count--) { + reg = ioread32(gth->base + REG_CTS_STAT); + cpu_relax(); + } + if (!count) + dev_dbg(&thdev->dev, "timeout waiting for CTS Trigger\n"); + + intel_th_gth_stop(gth, output, false); + intel_th_gth_start(gth, output); } /** @@ -719,6 +794,7 @@ static struct intel_th_driver intel_th_gth_driver = { .unassign = intel_th_gth_unassign, .set_output = intel_th_gth_set_output, .enable = intel_th_gth_enable, + .trig_switch = intel_th_gth_switch, .disable = intel_th_gth_disable, .driver = { .name = "gth", diff --git a/drivers/hwtracing/intel_th/gth.h b/drivers/hwtracing/intel_th/gth.h index 6f2b0b930875..1f7d0d886320 100644 --- a/drivers/hwtracing/intel_th/gth.h +++ b/drivers/hwtracing/intel_th/gth.h @@ -49,6 +49,11 @@ enum { REG_GTH_SCRPD3 = 0xec, /* ScratchPad[3] */ REG_TSCU_TSUCTRL = 0x2000, /* TSCU control register */ REG_TSCU_TSCUSTAT = 0x2004, /* TSCU status register */ + /* Common Capture Sequencer (CTS) registers */ + REG_CTS_C0S0_EN = 0x30c0, /* clause_event_enable_c0s0 */ + REG_CTS_C0S0_ACT = 0x3180, /* clause_action_control_c0s0 */ + REG_CTS_STAT = 0x32a0, /* cts_status */ + REG_CTS_CTL = 0x32a4, /* cts_control */ }; /* waiting for Pipeline Empty bit(s) to assert for GTH */ @@ -56,5 +61,7 @@ enum { #define TSUCTRL_CTCRESYNC BIT(0) #define TSCUSTAT_CTCSYNCING BIT(1) +/* waiting for Trigger status to assert for CTS */ +#define CTS_TRIG_WAITLOOP_DEPTH 10000 #endif /* __INTEL_TH_GTH_H__ */ diff --git a/drivers/hwtracing/intel_th/intel_th.h b/drivers/hwtracing/intel_th/intel_th.h index 780206dc9012..4f55d53ac761 100644 --- a/drivers/hwtracing/intel_th/intel_th.h +++ b/drivers/hwtracing/intel_th/intel_th.h @@ -18,6 +18,8 @@ enum { INTEL_TH_SWITCH, }; +struct intel_th_device; + /** * struct intel_th_output - descriptor INTEL_TH_OUTPUT type devices * @port: output port number, assigned by the switch @@ -25,6 +27,7 @@ enum { * @scratchpad: scratchpad bits to flag when this output is enabled * @multiblock: true for multiblock output configuration * @active: true when this output is enabled + * @wait_empty: wait for device pipeline to be empty * * Output port descriptor, used by switch driver to tell which output * port this output device corresponds to. Filled in at output device's @@ -37,6 +40,7 @@ struct intel_th_output { unsigned int scratchpad; bool multiblock; bool active; + void (*wait_empty)(struct intel_th_device *); }; /** @@ -157,6 +161,8 @@ struct intel_th_driver { struct intel_th_device *othdev); void (*enable)(struct intel_th_device *thdev, struct intel_th_output *output); + void (*trig_switch)(struct intel_th_device *thdev, + struct intel_th_output *output); void (*disable)(struct intel_th_device *thdev, struct intel_th_output *output); /* output ops */ @@ -220,6 +226,7 @@ int intel_th_driver_register(struct intel_th_driver *thdrv); void intel_th_driver_unregister(struct intel_th_driver *thdrv); int intel_th_trace_enable(struct intel_th_device *thdev); +int intel_th_trace_switch(struct intel_th_device *thdev); int intel_th_trace_disable(struct intel_th_device *thdev); int intel_th_set_output(struct intel_th_device *thdev, unsigned int master); @@ -290,7 +297,7 @@ to_intel_th_hub(struct intel_th_device *thdev) enum { /* Global Trace Hub (GTH) */ REG_GTH_OFFSET = 0x0000, - REG_GTH_LENGTH = 0x2000, + REG_GTH_LENGTH = 0x4000, /* Timestamp counter unit (TSCU) */ REG_TSCU_OFFSET = 0x2000, diff --git a/drivers/hwtracing/intel_th/msu.c b/drivers/hwtracing/intel_th/msu.c index d293e55553bd..6eae13f02629 100644 --- a/drivers/hwtracing/intel_th/msu.c +++ b/drivers/hwtracing/intel_th/msu.c @@ -531,23 +531,14 @@ static int msc_configure(struct msc *msc) */ static void msc_disable(struct msc *msc) { - unsigned long count; u32 reg; lockdep_assert_held(&msc->buf_mutex); intel_th_trace_disable(msc->thdev); - for (reg = 0, count = MSC_PLE_WAITLOOP_DEPTH; - count && !(reg & MSCSTS_PLE); count--) { - reg = ioread32(msc->reg_base + REG_MSU_MSC0STS); - cpu_relax(); - } - - if (!count) - dev_dbg(msc_dev(msc), "timeout waiting for MSC0 PLE\n"); - if (msc->mode == MSC_MODE_SINGLE) { + reg = ioread32(msc->reg_base + REG_MSU_MSC0STS); msc->single_wrap = !!(reg & MSCSTS_WRAPSTAT); reg = ioread32(msc->reg_base + REG_MSU_MSC0MWP); @@ -1250,6 +1241,22 @@ static const struct file_operations intel_th_msc_fops = { .owner = THIS_MODULE, }; +static void msc_wait_ple(struct intel_th_device *thdev) +{ + struct msc *msc = dev_get_drvdata(&thdev->dev); + unsigned long count; + u32 reg; + + for (reg = 0, count = MSC_PLE_WAITLOOP_DEPTH; + count && !(reg & MSCSTS_PLE); count--) { + reg = ioread32(msc->reg_base + REG_MSU_MSC0STS); + cpu_relax(); + } + + if (!count) + dev_dbg(msc_dev(msc), "timeout waiting for MSC0 PLE\n"); +} + static int intel_th_msc_init(struct msc *msc) { atomic_set(&msc->user_count, -1); @@ -1263,6 +1270,8 @@ static int intel_th_msc_init(struct msc *msc) (ioread32(msc->reg_base + REG_MSU_MSC0CTL) & MSC_LEN) >> __ffs(MSC_LEN); + msc->thdev->output.wait_empty = msc_wait_ple; + return 0; } @@ -1439,10 +1448,32 @@ nr_pages_store(struct device *dev, struct device_attribute *attr, static DEVICE_ATTR_RW(nr_pages); +static ssize_t +win_switch_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t size) +{ + struct msc *msc = dev_get_drvdata(dev); + unsigned long val; + int ret; + + ret = kstrtoul(buf, 10, &val); + if (ret) + return ret; + + if (val != 1) + return -EINVAL; + + intel_th_trace_switch(msc->thdev); + return size; +} + +static DEVICE_ATTR_WO(win_switch); + static struct attribute *msc_output_attrs[] = { &dev_attr_wrap.attr, &dev_attr_mode.attr, &dev_attr_nr_pages.attr, + &dev_attr_win_switch.attr, NULL, }; From 314c4104bc086d6e2821c181e23e0f2fffcaf098 Mon Sep 17 00:00:00 2001 From: Laurent FERT Date: Thu, 13 Aug 2015 19:31:41 +0200 Subject: [PATCH 0551/1103] intel_th: Reset before starting a capture Intel Trace Hub may be configured by firmwares or by a debugger before the driver is used. It is therefore difficult to start a capture from the OS with a well known state. Trigger a reset and re-apply all the configuration before starting a capture. Signed-off-by: Laurent FERT Signed-off-by: Tian, Baofeng --- drivers/hwtracing/intel_th/core.c | 93 ++++++++++---------- drivers/hwtracing/intel_th/gth.c | 120 ++++++++++++++------------ drivers/hwtracing/intel_th/intel_th.h | 22 +++-- drivers/hwtracing/intel_th/msu.c | 21 ++--- drivers/hwtracing/intel_th/pci.c | 31 ++++++- drivers/hwtracing/intel_th/pti.c | 2 - 6 files changed, 165 insertions(+), 124 deletions(-) diff --git a/drivers/hwtracing/intel_th/core.c b/drivers/hwtracing/intel_th/core.c index fb7c9a5144e4..db28872bba34 100644 --- a/drivers/hwtracing/intel_th/core.c +++ b/drivers/hwtracing/intel_th/core.c @@ -215,48 +215,46 @@ static ssize_t port_show(struct device *dev, struct device_attribute *attr, static DEVICE_ATTR_RO(port); -static int intel_th_output_activate(struct intel_th_device *thdev) +/** + * intel_th_output_activate() - call output initialization procedure + * @output: output to activate + */ +int intel_th_output_activate(struct intel_th_output *output) { - struct intel_th_driver *thdrv = - to_intel_th_driver_or_null(thdev->dev.driver); - struct intel_th *th = to_intel_th(thdev); - int ret = 0; - - if (!thdrv) - return -ENODEV; - - if (!try_module_get(thdrv->driver.owner)) - return -ENODEV; + struct intel_th_device *outdev = + container_of(output, struct intel_th_device, output); + struct intel_th_driver *outdrv = + to_intel_th_driver(outdev->dev.driver); - pm_runtime_get_sync(&thdev->dev); - - if (th->activate) - ret = th->activate(th); - if (ret) - goto fail_put; - - if (thdrv->activate) - ret = thdrv->activate(thdev); - else - intel_th_trace_enable(thdev); + if (WARN_ON_ONCE(outdev->type != INTEL_TH_OUTPUT)) + return -EINVAL; - if (ret) - goto fail_deactivate; + if (outdrv->activate) + return outdrv->activate(outdev); return 0; +} +EXPORT_SYMBOL_GPL(intel_th_output_activate); -fail_deactivate: - if (th->deactivate) - th->deactivate(th); + * @thdev: output device that requests tracing + */ +static int intel_th_start_trace(struct intel_th_device *thdev) +{ + struct intel_th_device *hub = to_intel_th_device(thdev->dev.parent); + struct intel_th_driver *hubdrv = to_intel_th_driver(hub->dev.driver); -fail_put: - pm_runtime_put(&thdev->dev); - module_put(thdrv->driver.owner); + if (WARN_ON_ONCE(hub->type != INTEL_TH_SWITCH)) + return -EINVAL; - return ret; + if (WARN_ON_ONCE(thdev->type != INTEL_TH_OUTPUT)) + return -EINVAL; + + /* The hub has control over Intel Trace Hub. + * Let the hub start a trace if possible and activate the output. */ + return hubdrv->enable(hub, &thdev->output); } -static void intel_th_output_deactivate(struct intel_th_device *thdev) +static void intel_th_stop_trace(struct intel_th_device *thdev) { struct intel_th_driver *thdrv = to_intel_th_driver_or_null(thdev->dev.driver); @@ -298,9 +296,9 @@ static ssize_t active_store(struct device *dev, struct device_attribute *attr, if (!!val != thdev->output.active) { if (val) - ret = intel_th_output_activate(thdev); + ret = intel_th_start_trace(thdev); else - intel_th_output_deactivate(thdev); + intel_th_stop_trace(thdev); } return ret ? ret : size; @@ -369,6 +367,7 @@ intel_th_device_alloc(struct intel_th *th, unsigned int type, const char *name, thdev->id = id; thdev->type = type; + thdev->th = th; strcpy(thdev->name, name); device_initialize(&thdev->dev); @@ -811,10 +810,11 @@ static const struct file_operations intel_th_output_fops = { * @devres: parent's resources * @ndevres: number of resources * @irq: irq number + * @reset: parent's reset function */ struct intel_th * intel_th_alloc(struct device *dev, struct intel_th_drvdata *drvdata, - struct resource *devres, unsigned int ndevres, int irq) + struct resource *devres, unsigned int ndevres, int irq, void (*reset)(struct intel_th *th)) { struct intel_th *th; int err, r; @@ -848,6 +848,7 @@ intel_th_alloc(struct device *dev, struct intel_th_drvdata *drvdata, th->resource = devres; th->num_resources = ndevres; th->irq = irq; + th->reset = reset; dev_set_drvdata(dev, th); @@ -902,26 +903,20 @@ void intel_th_free(struct intel_th *th) EXPORT_SYMBOL_GPL(intel_th_free); /** - * intel_th_trace_enable() - enable tracing for an output device - * @thdev: output device that requests tracing be enabled + * intel_th_reset() - reset hardware registers + * @hub: hub requesting the reset */ -int intel_th_trace_enable(struct intel_th_device *thdev) +void intel_th_reset(struct intel_th_device *hub) { - struct intel_th_device *hub = to_intel_th_device(thdev->dev.parent); - struct intel_th_driver *hubdrv = to_intel_th_driver(hub->dev.driver); + struct intel_th *th = hub->th; if (WARN_ON_ONCE(hub->type != INTEL_TH_SWITCH)) - return -EINVAL; - - if (WARN_ON_ONCE(thdev->type != INTEL_TH_OUTPUT)) - return -EINVAL; - - pm_runtime_get_sync(&thdev->dev); - hubdrv->enable(hub, &thdev->output); + return; - return 0; + if (th->reset) + th->reset(th); } -EXPORT_SYMBOL_GPL(intel_th_trace_enable); +EXPORT_SYMBOL_GPL(intel_th_reset); /** * intel_th_trace_switch() - execute a switch sequence diff --git a/drivers/hwtracing/intel_th/gth.c b/drivers/hwtracing/intel_th/gth.c index 7d1c2e9cd6b2..91d3ffc779b7 100644 --- a/drivers/hwtracing/intel_th/gth.c +++ b/drivers/hwtracing/intel_th/gth.c @@ -27,14 +27,16 @@ struct gth_device; * @output: link to output device's output descriptor * @index: output port number * @port_type: one of GTH_* port type values - * @master: bitmap of masters configured for this output + * @config: output configuration backup + * @smcfreq: maintenance packet frequency backup */ struct gth_output { struct gth_device *gth; struct intel_th_output *output; unsigned int index; unsigned int port_type; - DECLARE_BITMAP(master, TH_CONFIGURABLE_MASTERS + 1); + u32 config; + u32 smcfreq; }; /** @@ -65,6 +67,8 @@ static void gth_output_set(struct gth_device *gth, int port, u32 val; int shift = (port & 3) * 8; + gth->output[port].config = config; + val = ioread32(gth->base + reg); val &= ~(0xff << shift); val |= config << shift; @@ -91,6 +95,8 @@ static void gth_smcfreq_set(struct gth_device *gth, int port, int shift = (port & 1) * 16; u32 val; + gth->output[port].smcfreq = freq; + val = ioread32(gth->base + reg); val &= ~(0xffff << shift); val |= freq << shift; @@ -200,7 +206,6 @@ static ssize_t master_attr_store(struct device *dev, old_port = gth->master[ma->master]; if (old_port >= 0) { gth->master[ma->master] = -1; - clear_bit(ma->master, gth->output[old_port].master); gth_master_set(gth, ma->master, -1); } @@ -212,7 +217,6 @@ static ssize_t master_attr_store(struct device *dev, goto unlock; } - set_bit(ma->master, gth->output[port].master); gth_master_set(gth, ma->master, port); gth->master[ma->master] = port; } @@ -282,30 +286,6 @@ gth_output_parm_get(struct gth_device *gth, int port, unsigned int parm) return config; } -/* - * Reset outputs and sources - */ -static void intel_th_gth_reset(struct gth_device *gth) -{ - u32 scratchpad; - - /* Always save/restore STH and TU registers in S0ix entry/exit */ - scratchpad = ioread32(gth->base + REG_GTH_SCRPD0); - scratchpad |= SCRPD_STH_IS_ENABLED | SCRPD_TRIGGER_IS_ENABLED; - iowrite32(scratchpad, gth->base + REG_GTH_SCRPD0); - - /* disable overrides */ - iowrite32(0, gth->base + REG_GTH_DESTOVR); - - /* sources */ - iowrite32(0, gth->base + REG_GTH_SCR); - iowrite32(0xfc, gth->base + REG_GTH_SCR2); - - /* setup CTS for single trigger */ - iowrite32(0x80000000, gth->base + REG_CTS_C0S0_EN); - iowrite32(0x40000010, gth->base + REG_CTS_C0S0_ACT); -} - /* * "outputs" attribute group */ @@ -524,16 +504,16 @@ static void intel_th_gth_disable(struct intel_th_device *thdev, struct intel_th_output *output) { struct gth_device *gth = dev_get_drvdata(&thdev->dev); - int master; + int i; u32 reg; spin_lock(>h->gth_lock); output->active = false; - for_each_set_bit(master, gth->output[output->port].master, - TH_CONFIGURABLE_MASTERS) { - gth_master_set(gth, master, -1); - } + for (i = 0; i < TH_CONFIGURABLE_MASTERS + 1; i++) + if (gth->master[i] == output->port) + gth_master_set(gth, i, -1); + spin_unlock(>h->gth_lock); intel_th_gth_stop(gth, output, true); @@ -543,13 +523,25 @@ static void intel_th_gth_disable(struct intel_th_device *thdev, iowrite32(reg, gth->base + REG_GTH_SCRPD0); } -static void gth_tscu_resync(struct gth_device *gth) +/* + * Set default configuration. + */ +static void intel_th_gth_reset(struct gth_device *gth) { u32 reg; - reg = ioread32(gth->base + REG_TSCU_TSUCTRL); - reg &= ~TSUCTRL_CTCRESYNC; - iowrite32(reg, gth->base + REG_TSCU_TSUCTRL); + /* Always save/restore STH and TU registers in S0ix entry/exit */ + reg = ioread32(gth->base + REG_GTH_SCRPD0); + reg |= SCRPD_STH_IS_ENABLED | SCRPD_TRIGGER_IS_ENABLED; + iowrite32(reg, gth->base + REG_GTH_SCRPD0); + + /* Force sources off */ + iowrite32(0, gth->base + REG_GTH_SCR); + iowrite32(0xfc, gth->base + REG_GTH_SCR2); + + /* Setup CTS for single trigger */ + iowrite32(0x80000000, gth->base + REG_CTS_C0S0_EN); + iowrite32(0x40000010, gth->base + REG_CTS_C0S0_ACT); } /** @@ -560,33 +552,59 @@ static void gth_tscu_resync(struct gth_device *gth) * This will configure all masters set to output to this device and * enable tracing using force storeEn signal. */ -static void intel_th_gth_enable(struct intel_th_device *thdev, - struct intel_th_output *output) +static int intel_th_gth_enable(struct intel_th_device *thdev, + struct intel_th_output *output) { struct gth_device *gth = dev_get_drvdata(&thdev->dev); struct intel_th *th = to_intel_th(thdev); u32 scrpd; - int master; + int i; + int ret = -EBUSY; + + /* No operation allowed while a debugger is connected */ + scrpd = ioread32(gth->base + REG_GTH_SCRPD0); + if (scrpd & SCRPD_DEBUGGER_IN_USE) + return ret; spin_lock(>h->gth_lock); - intel_th_gth_reset(gth); - for_each_set_bit(master, gth->output[output->port].master, - TH_CONFIGURABLE_MASTERS + 1) { - gth_master_set(gth, master, output->port); + /* Only allow one output active at a time */ + for (i = 0; i < TH_POSSIBLE_OUTPUTS; i++) { + if (gth->output[i].output && + gth->output[i].output->active) { + spin_unlock(>h->gth_lock); + return ret; + } } + intel_th_reset(thdev); + intel_th_gth_reset(gth); + + /* Re-configure output */ + gth_output_set(gth, output->port, gth->output[output->port].config); + gth_smcfreq_set(gth, output->port, gth->output[output->port].smcfreq); + + /* Enable masters for the output, disable others */ + for (i = 0; i < TH_CONFIGURABLE_MASTERS + 1; i++) + gth_master_set(gth, i, gth->master[i] == output->port ? + output->port : -1); + output->active = true; spin_unlock(>h->gth_lock); - if (INTEL_TH_CAP(th, tscu_enable)) - gth_tscu_resync(gth); + /* Setup the output */ + ret = intel_th_output_activate(output); + if (ret) + return ret; scrpd = ioread32(gth->base + REG_GTH_SCRPD0); scrpd |= output->scratchpad; iowrite32(scrpd, gth->base + REG_GTH_SCRPD0); + /* Enable sources */ intel_th_gth_start(gth, output); + + return 0; } /** @@ -700,10 +718,9 @@ intel_th_gth_set_output(struct intel_th_device *thdev, unsigned int master) master = TH_CONFIGURABLE_MASTERS; spin_lock(>h->gth_lock); - if (gth->master[master] == -1) { - set_bit(master, gth->output[port].master); + if (gth->master[master] == -1) gth->master[master] = port; - } + spin_unlock(>h->gth_lock); return 0; @@ -717,7 +734,6 @@ static int intel_th_gth_probe(struct intel_th_device *thdev) struct resource *res; void __iomem *base; int i, ret; - u32 scratchpad; res = intel_th_device_get_resource(thdev, IORESOURCE_MEM, 0); if (!res) @@ -746,10 +762,6 @@ static int intel_th_gth_probe(struct intel_th_device *thdev) if (thdev->host_mode) return 0; - scratchpad = ioread32(gth->base + REG_GTH_SCRPD0); - if (scratchpad & SCRPD_DEBUGGER_IN_USE) - return -EBUSY; - for (i = 0; i < TH_CONFIGURABLE_MASTERS + 1; i++) gth->master[i] = gth_master_get(gth, i); diff --git a/drivers/hwtracing/intel_th/intel_th.h b/drivers/hwtracing/intel_th/intel_th.h index 4f55d53ac761..441fb9d85add 100644 --- a/drivers/hwtracing/intel_th/intel_th.h +++ b/drivers/hwtracing/intel_th/intel_th.h @@ -59,6 +59,7 @@ struct intel_th_drvdata { * struct intel_th_device - device on the intel_th bus * @dev: device * @drvdata: hardware capabilities/quirks + * @th: core device * @resource: array of resources available to this device * @num_resources: number of resources in @resource array * @type: INTEL_TH_{SOURCE,OUTPUT,SWITCH} @@ -68,12 +69,13 @@ struct intel_th_drvdata { * @name: device name to match the driver */ struct intel_th_device { - struct device dev; + struct device dev; struct intel_th_drvdata *drvdata; - struct resource *resource; - unsigned int num_resources; - unsigned int type; - int id; + struct intel_th *th; + struct resource *resource; + unsigned int num_resources; + unsigned int type; + int id; /* INTEL_TH_SWITCH specific */ bool host_mode; @@ -159,7 +161,7 @@ struct intel_th_driver { struct intel_th_device *othdev); void (*unassign)(struct intel_th_device *thdev, struct intel_th_device *othdev); - void (*enable)(struct intel_th_device *thdev, + int (*enable)(struct intel_th_device *thdev, struct intel_th_output *output); void (*trig_switch)(struct intel_th_device *thdev, struct intel_th_output *output); @@ -219,13 +221,14 @@ static inline struct intel_th *to_intel_th(struct intel_th_device *thdev) struct intel_th * intel_th_alloc(struct device *dev, struct intel_th_drvdata *drvdata, - struct resource *devres, unsigned int ndevres, int irq); + struct resource *devres, unsigned int ndevres, int irq, void (*reset)(struct intel_th *th)); void intel_th_free(struct intel_th *th); int intel_th_driver_register(struct intel_th_driver *thdrv); void intel_th_driver_unregister(struct intel_th_driver *thdrv); -int intel_th_trace_enable(struct intel_th_device *thdev); +int intel_th_output_activate(struct intel_th_output *output); +void intel_th_reset(struct intel_th_device *hub); int intel_th_trace_switch(struct intel_th_device *thdev); int intel_th_trace_disable(struct intel_th_device *thdev); int intel_th_set_output(struct intel_th_device *thdev, @@ -253,6 +256,7 @@ enum { * @num_thdevs: number of devices in the @thdev array * @num_resources: number or resources in the @resource array * @irq: irq number + * @reset: reset function of the core device * @id: this Intel TH controller's device ID in the system * @major: device node major for output devices */ @@ -270,6 +274,8 @@ struct intel_th { unsigned int num_resources; int irq; + void (*reset)(struct intel_th *th); + int id; int major; #ifdef CONFIG_MODULES diff --git a/drivers/hwtracing/intel_th/msu.c b/drivers/hwtracing/intel_th/msu.c index 6eae13f02629..47d2f7d8419b 100644 --- a/drivers/hwtracing/intel_th/msu.c +++ b/drivers/hwtracing/intel_th/msu.c @@ -89,6 +89,7 @@ struct msc_iter { * @single_wrap: single mode wrap occurred * @base: buffer's base pointer * @base_addr: buffer's base address + * @nwsa: next window start address backup * @user_count: number of users of the buffer * @mmap_count: number of mappings * @buf_mutex: mutex to serialize access to buffer-related bits @@ -109,6 +110,7 @@ struct msc { unsigned int single_wrap : 1; void *base; dma_addr_t base_addr; + unsigned long nwsa; /* <0: no buffer, 0: no users, >0: active users */ atomic_t user_count; @@ -151,8 +153,6 @@ static inline bool msc_block_is_empty(struct msc_block_desc *bdesc) static struct msc_window *msc_oldest_window(struct msc *msc) { struct msc_window *win; - u32 reg = ioread32(msc->reg_base + REG_MSU_MSC0NWSA); - unsigned long win_addr = (unsigned long)reg << PAGE_SHIFT; unsigned int found = 0; if (list_empty(&msc->win_list)) @@ -164,7 +164,7 @@ static struct msc_window *msc_oldest_window(struct msc *msc) * something like 2, in which case we're good */ list_for_each_entry(win, &msc->win_list, entry) { - if (win->block[0].addr == win_addr) + if (win->block[0].addr == msc->nwsa) found++; /* skip the empty ones */ @@ -478,9 +478,9 @@ static void msc_buffer_clear_hw_header(struct msc *msc) * msc_configure() - set up MSC hardware * @msc: the MSC device to configure * - * Program storage mode, wrapping, burst length and trace buffer address - * into a given MSC. Then, enable tracing and set msc::enabled. - * The latter is serialized on msc::buf_mutex, so make sure to hold it. + * Program all relevant registers for a given MSC. + * Programming registers must be delayed until this stage since the hardware + * will be reset before a capture is started. */ static int msc_configure(struct msc *msc) { @@ -515,10 +515,8 @@ static int msc_configure(struct msc *msc) iowrite32(reg, msc->reg_base + REG_MSU_MSC0CTL); msc->thdev->output.multiblock = msc->mode == MSC_MODE_MULTI; - intel_th_trace_enable(msc->thdev); msc->enabled = 1; - return 0; } @@ -547,6 +545,10 @@ static void msc_disable(struct msc *msc) reg, msc->single_sz, msc->single_wrap); } + /* Save next window start address before disabling */ + reg = ioread32(msc->reg_base + REG_MSU_MSC0NWSA); + msc->nwsa = (unsigned long)reg << PAGE_SHIFT; + reg = ioread32(msc->reg_base + REG_MSU_MSC0CTL); reg &= ~MSC_EN; iowrite32(reg, msc->reg_base + REG_MSU_MSC0CTL); @@ -555,8 +557,7 @@ static void msc_disable(struct msc *msc) iowrite32(0, msc->reg_base + REG_MSU_MSC0BAR); iowrite32(0, msc->reg_base + REG_MSU_MSC0SIZE); - dev_dbg(msc_dev(msc), "MSCnNWSA: %08x\n", - ioread32(msc->reg_base + REG_MSU_MSC0NWSA)); + dev_dbg(msc_dev(msc), "MSCnNWSA: %08lx\n", msc->nwsa); reg = ioread32(msc->reg_base + REG_MSU_MSC0STS); dev_dbg(msc_dev(msc), "MSCnSTS: %08x\n", reg); diff --git a/drivers/hwtracing/intel_th/pci.c b/drivers/hwtracing/intel_th/pci.c index 1cf6290d6435..3a4f5733d24d 100644 --- a/drivers/hwtracing/intel_th/pci.c +++ b/drivers/hwtracing/intel_th/pci.c @@ -61,6 +61,35 @@ static void intel_th_pci_deactivate(struct intel_th *th) if (err) dev_err(&pdev->dev, "failed to read NPKDSC register\n"); } +/* + * PCI Configuration Registers + */ +enum { + REG_PCI_NPKDSC = 0x80, /* NPK Device Specific Control */ + REG_PCI_NPKDSD = 0x90, /* NPK Device Specific Defeature */ +}; + +/* Trace Hub software reset */ +#define NPKDSC_RESET BIT(1) + +/* Force On */ +#define NPKDSD_FON BIT(0) + +static void intel_th_pci_reset(struct intel_th *th) +{ + struct pci_dev *pdev = container_of(th->dev, struct pci_dev, dev); + u32 val; + + /* Software reset */ + pci_read_config_dword(pdev, REG_PCI_NPKDSC, &val); + val |= NPKDSC_RESET; + pci_write_config_dword(pdev, REG_PCI_NPKDSC, val); + + /* Always set FON for S0ix flow */ + pci_read_config_dword(pdev, REG_PCI_NPKDSD, &val); + val |= NPKDSD_FON; + pci_write_config_dword(pdev, REG_PCI_NPKDSD, val); +} static int intel_th_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) @@ -78,7 +107,7 @@ static int intel_th_pci_probe(struct pci_dev *pdev, return err; th = intel_th_alloc(&pdev->dev, drvdata, pdev->resource, - DEVICE_COUNT_RESOURCE, pdev->irq); + DEVICE_COUNT_RESOURCE, pdev->irq, intel_th_pci_reset); if (IS_ERR(th)) return PTR_ERR(th); diff --git a/drivers/hwtracing/intel_th/pti.c b/drivers/hwtracing/intel_th/pti.c index 56694339cb06..9b6224b22a5f 100644 --- a/drivers/hwtracing/intel_th/pti.c +++ b/drivers/hwtracing/intel_th/pti.c @@ -161,8 +161,6 @@ static int intel_th_pti_activate(struct intel_th_device *thdev) iowrite32(ctl, pti->base + REG_PTI_CTL); - intel_th_trace_enable(thdev); - return 0; } From 2be7cf8d2c9d8b6664efe03232587334cc1192e7 Mon Sep 17 00:00:00 2001 From: Laurent FERT Date: Mon, 26 Oct 2015 17:54:46 +0100 Subject: [PATCH 0552/1103] intel_th: Workaround PTI pipeline not empty When stopping a capture to PTI, the driver waits for PTI pipeline empty bit to be set. If this does not happen, the trace hub will be in an inconsistent state where it is not outputting any traces but has non-empty pipeline. Such state will end up in a global reset if the platform attempts to enter low power S0ix mode. For PTI only and when PTI pipeline is not empty at the end of a capture, do a reset of the trace hub. Signed-off-by: Laurent FERT --- drivers/hwtracing/intel_th/gth.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/hwtracing/intel_th/gth.c b/drivers/hwtracing/intel_th/gth.c index 91d3ffc779b7..e95301f91627 100644 --- a/drivers/hwtracing/intel_th/gth.c +++ b/drivers/hwtracing/intel_th/gth.c @@ -521,6 +521,11 @@ static void intel_th_gth_disable(struct intel_th_device *thdev, reg = ioread32(gth->base + REG_GTH_SCRPD0); reg &= ~output->scratchpad; iowrite32(reg, gth->base + REG_GTH_SCRPD0); + + /* Workaround for PTI pipeline empty not set by hardware */ + if (output->type == GTH_PTI && + !(BIT(output->port) & ioread32(gth->base + REG_GTH_STAT))) + intel_th_reset(thdev); } /* From 5c70d3e6da4d314ebe366a983ba64b991b09efe9 Mon Sep 17 00:00:00 2001 From: Laurent FERT Date: Wed, 23 Mar 2016 09:19:49 +0100 Subject: [PATCH 0553/1103] intel_th: Implement suspend and resume Intel Trace Hub does not need to do anything when going to suspend. When going to low power or suspending, firmwares will take care of saving its current state, stopping the current trace and powering it down. When coming out of low power or suspend, firmwares will take care of powering it up restoring its state and resuming the current trace. The driver still needs to implement the suspend and resume calls so that the kernel does not disable memory access by setting BME=0 in its PCI config space before the firmwares do the proper shut down of the Trace Hub. Signed-off-by: Laurent FERT --- drivers/hwtracing/intel_th/pci.c | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/drivers/hwtracing/intel_th/pci.c b/drivers/hwtracing/intel_th/pci.c index 3a4f5733d24d..460850b16d19 100644 --- a/drivers/hwtracing/intel_th/pci.c +++ b/drivers/hwtracing/intel_th/pci.c @@ -199,11 +199,34 @@ static const struct pci_device_id intel_th_pci_id_table[] = { MODULE_DEVICE_TABLE(pci, intel_th_pci_id_table); +static int intel_th_suspend(struct device *dev) +{ + /* + * Stub the call to avoid disabling the device. + * Suspend is fully handled by firmwares. + */ + return 0; +} + +static int intel_th_resume(struct device *dev) +{ + /* Firmwares have already restored the device state. */ + return 0; +} + +static const struct dev_pm_ops intel_th_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(intel_th_suspend, + intel_th_resume) +}; + static struct pci_driver intel_th_pci_driver = { .name = DRIVER_NAME, .id_table = intel_th_pci_id_table, .probe = intel_th_pci_probe, .remove = intel_th_pci_remove, + .driver = { + .pm = &intel_th_pm_ops, + }, }; module_pci_driver(intel_th_pci_driver); From 65e9eb32d2607446ea075656168d74f6297fe3d9 Mon Sep 17 00:00:00 2001 From: Yann Fouassier Date: Fri, 29 May 2015 11:12:35 +0200 Subject: [PATCH 0554/1103] intel_th: add earlyprintk support Enable trace hub earlyprintk with cmdline parameter. Specify the software trace hub bar address and channel: earlyprintk=intelth,0x:[,keep]. Signed-off-by: Yann Fouassier Signed-off-by: Laurent FERT Signed-off-by: Tian, Baofeng --- arch/x86/include/asm/early_intel_th.h | 20 +++++ arch/x86/kernel/early_printk.c | 7 ++ drivers/hwtracing/intel_th/Kconfig | 11 +++ drivers/hwtracing/intel_th/Makefile | 3 + drivers/hwtracing/intel_th/early_printk.c | 98 +++++++++++++++++++++++ 5 files changed, 139 insertions(+) create mode 100644 arch/x86/include/asm/early_intel_th.h create mode 100644 drivers/hwtracing/intel_th/early_printk.c diff --git a/arch/x86/include/asm/early_intel_th.h b/arch/x86/include/asm/early_intel_th.h new file mode 100644 index 000000000000..bf93609995c8 --- /dev/null +++ b/arch/x86/include/asm/early_intel_th.h @@ -0,0 +1,20 @@ +/* + * early_intel_th.h: Intel Trace Hub early printk + * + * (C) Copyright 2015 Intel 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; version 2 + * of the License. + */ +#ifndef _ASM_X86_EARLY_INTEL_TH_H +#define _ASM_X86_EARLY_INTEL_TH_H + +#ifdef CONFIG_INTEL_TH_EARLY_PRINTK +extern struct console intel_th_early_console; +extern void early_intel_th_init(const char *); +#endif /* CONFIG_INTEL_TH_EARLY_PRINTK */ + +#endif /* _ASM_X86_EARLY_INTEL_TH_H */ + diff --git a/arch/x86/kernel/early_printk.c b/arch/x86/kernel/early_printk.c index 5e801c8c8ce7..f65d32c7040c 100644 --- a/arch/x86/kernel/early_printk.c +++ b/arch/x86/kernel/early_printk.c @@ -22,6 +22,7 @@ #include #include #include +#include /* Simple VGA output */ #define VGABASE (__ISA_IO_base + 0xb8000) @@ -387,6 +388,12 @@ static int __init setup_early_printk(char *buf) if (!strncmp(buf, "xdbc", 4)) early_xdbc_parse_parameter(buf + 4); #endif +#ifdef CONFIG_INTEL_TH_EARLY_PRINTK + if (!strncmp(buf, "intelth", 7)) { + early_intel_th_init(buf + 7); + early_console_register(&intel_th_early_console, keep); + } +#endif buf++; } diff --git a/drivers/hwtracing/intel_th/Kconfig b/drivers/hwtracing/intel_th/Kconfig index ca0527d588e9..4fdf936dc04b 100644 --- a/drivers/hwtracing/intel_th/Kconfig +++ b/drivers/hwtracing/intel_th/Kconfig @@ -82,4 +82,15 @@ config INTEL_TH_DEBUG help Say Y here to enable debugging. +config INTEL_TH_EARLY_PRINTK + bool "Intel TH early printk console" + depends on INTEL_TH=y + default n + ---help--- + Enables early printk console. + When the early printk console is enabled in the kernel + command line, kernel log messages are sent to Intel TH + (hence they are aggregated with the other trace messages + from the platform). + endif diff --git a/drivers/hwtracing/intel_th/Makefile b/drivers/hwtracing/intel_th/Makefile index d9252fa8d9ca..d72530116664 100644 --- a/drivers/hwtracing/intel_th/Makefile +++ b/drivers/hwtracing/intel_th/Makefile @@ -20,3 +20,6 @@ intel_th_msu-y := msu.o obj-$(CONFIG_INTEL_TH_PTI) += intel_th_pti.o intel_th_pti-y := pti.o + +obj-$(CONFIG_INTEL_TH_EARLY_PRINTK) += intel_th_early_printk.o +intel_th_early_printk-y := early_printk.o \ No newline at end of file diff --git a/drivers/hwtracing/intel_th/early_printk.c b/drivers/hwtracing/intel_th/early_printk.c new file mode 100644 index 000000000000..bbcb9f89161d --- /dev/null +++ b/drivers/hwtracing/intel_th/early_printk.c @@ -0,0 +1,98 @@ +#include +#include +#include +#include "sth.h" + +static unsigned long sth_phys_addr; + +void early_intel_th_init(const char *s) +{ + size_t n; + unsigned long addr, chan; + char buf[32] = {0, }; + char *match, *next; + + /* Expect ,0x:[,keep] */ + if (*s == ',') + ++s; + if (strncmp(s, "0x", 2)) + goto fail; + + n = strcspn(s, ","); + if (n > sizeof(buf) - 1) + goto fail; + strncpy(buf, s, n); + next = buf; + + /* Get sw_bar */ + match = strsep(&next, ":"); + if (!match) + goto fail; + + if (kstrtoul(match, 16, &addr)) + goto fail; + + /* Get channel */ + if (kstrtoul(next, 0, &chan)) + goto fail; + + sth_phys_addr = addr + chan * sizeof(struct intel_th_channel); + return; + +fail: + pr_err("%s invalid parameter %s", __func__, s); +} + +static void intel_th_early_write(struct console *con, const char *buf, + unsigned len) +{ + struct intel_th_channel *channel; + const u8 *p = buf; + const u32 sven_header = 0x01000242; + + if (WARN_ON_ONCE(!sth_phys_addr)) + return; + + /* Software can send messages to Intel TH by writing to an MMIO space + * that is divided in several Master/Channel regions. + * Write directly to the address provided through the cmdline. + */ + set_fixmap_nocache(FIX_EARLYCON_MEM_BASE, sth_phys_addr); + channel = (struct intel_th_channel *) + (__fix_to_virt(FIX_EARLYCON_MEM_BASE) + + (sth_phys_addr & (PAGE_SIZE - 1))); + + /* Add hardcoded SVEN header + * type: DEBUG_STRING + * severity: SVEN_SEVERITY_NORMAL + * length: payload size + * subtype: SVEN_DEBUGSTR_Generic + */ + iowrite32(sven_header, &channel->DnTS); + iowrite16(len, &channel->Dn); + + while (len) { + if (len >= 4) { + iowrite32(*(u32 *)p, &channel->Dn); + p += 4; + len -= 4; + } else if (len >= 2) { + iowrite16(*(u16 *)p, &channel->Dn); + p += 2; + len -= 2; + } else { + iowrite8(*(u8 *)p, &channel->Dn); + p += 1; + len -= 1; + } + } + + iowrite32(0, &channel->FLAG); +} + +struct console intel_th_early_console = { + .name = "earlyintelth", + .write = intel_th_early_write, + .flags = CON_PRINTBUFFER, + .index = -1, +}; From 35c1387ca99d89d79c7b6bd8b61c11f544fa4f09 Mon Sep 17 00:00:00 2001 From: Laurent FERT Date: Wed, 23 Dec 2015 15:56:11 +0100 Subject: [PATCH 0555/1103] intel_th: Support NPKT ACPI table in MSU When the bios is configured to trace to memory, it will * trace to MTB before memory is available * save MTB traces to a memory area when memory is available * trace to CSR in a reserved memory area In case of reboot, MTB and CSR buffers may be backed up by bios in recovery copies. The memory area for all those buffers is exposed to the kernel through NPKT ACPI table. Stop the current trace if the CSR capture is still in progress when the user either tries to read the CSR buffer or starts a new capture. Make the various buffers available to userspace through debugfs. Signed-off-by: Yann Fouassier Signed-off-by: Laurent FERT Signed-off-by: Tian, Baofeng --- drivers/hwtracing/intel_th/core.c | 29 +++ drivers/hwtracing/intel_th/intel_th.h | 1 + drivers/hwtracing/intel_th/msu.c | 319 +++++++++++++++++++++++--- 3 files changed, 319 insertions(+), 30 deletions(-) diff --git a/drivers/hwtracing/intel_th/core.c b/drivers/hwtracing/intel_th/core.c index db28872bba34..5a5464944e80 100644 --- a/drivers/hwtracing/intel_th/core.c +++ b/drivers/hwtracing/intel_th/core.c @@ -236,12 +236,38 @@ int intel_th_output_activate(struct intel_th_output *output) } EXPORT_SYMBOL_GPL(intel_th_output_activate); +/** + * intel_th_first_trace() - notification callback for first trace + * + * Notify each child device that the first capture is about to begin. + * This gives a chance to save the current data as the Trace Hub may have + * already been configured by the BIOS to trace to a given output. + * + * @dev: output device to notify + * @data: private data - unused + */ +static int intel_th_first_trace(struct device *dev, void *data) +{ + struct intel_th_device *thdev = + container_of(dev, struct intel_th_device, dev); + struct intel_th_driver *thdrv = + to_intel_th_driver(thdev->dev.driver); + + if (thdrv && thdrv->first_trace) + thdrv->first_trace(thdev); + + return 0; +} + +/** + * intel_th_start_trace() - start tracing to an output device * @thdev: output device that requests tracing */ static int intel_th_start_trace(struct intel_th_device *thdev) { struct intel_th_device *hub = to_intel_th_device(thdev->dev.parent); struct intel_th_driver *hubdrv = to_intel_th_driver(hub->dev.driver); + static atomic_t first = { .counter = 1, }; if (WARN_ON_ONCE(hub->type != INTEL_TH_SWITCH)) return -EINVAL; @@ -249,6 +275,9 @@ static int intel_th_start_trace(struct intel_th_device *thdev) if (WARN_ON_ONCE(thdev->type != INTEL_TH_OUTPUT)) return -EINVAL; + if (atomic_dec_if_positive(&first) == 0) + device_for_each_child(&hub->dev, NULL, intel_th_first_trace); + /* The hub has control over Intel Trace Hub. * Let the hub start a trace if possible and activate the output. */ return hubdrv->enable(hub, &thdev->output); diff --git a/drivers/hwtracing/intel_th/intel_th.h b/drivers/hwtracing/intel_th/intel_th.h index 441fb9d85add..7bbc75626f1d 100644 --- a/drivers/hwtracing/intel_th/intel_th.h +++ b/drivers/hwtracing/intel_th/intel_th.h @@ -154,6 +154,7 @@ intel_th_output_assigned(struct intel_th_device *thdev) */ struct intel_th_driver { struct device_driver driver; + void (*first_trace)(struct intel_th_device *thdev); int (*probe)(struct intel_th_device *thdev); void (*remove)(struct intel_th_device *thdev); /* switch (GTH) ops */ diff --git a/drivers/hwtracing/intel_th/msu.c b/drivers/hwtracing/intel_th/msu.c index 47d2f7d8419b..6e1d359ed2d5 100644 --- a/drivers/hwtracing/intel_th/msu.c +++ b/drivers/hwtracing/intel_th/msu.c @@ -23,6 +23,9 @@ #include #endif +#include +#include + #include "intel_th.h" #include "msu.h" @@ -1053,16 +1056,19 @@ static int intel_th_msc_release(struct inode *inode, struct file *file) } static ssize_t -msc_single_to_user(struct msc *msc, char __user *buf, loff_t off, size_t len) +msc_single_to_user(void *in_buf, unsigned long in_pages, + unsigned long in_sz, bool wrapped, + char __user *buf, loff_t off, size_t len) { - unsigned long size = msc->nr_pages << PAGE_SHIFT, rem = len; + unsigned long size = in_pages << PAGE_SHIFT, rem = len; unsigned long start = off, tocopy = 0; - if (msc->single_wrap) { - start += msc->single_sz; + /* With wrapping, copy the end of the buffer first */ + if (wrapped) { + start += in_sz; if (start < size) { tocopy = min(rem, size - start); - if (copy_to_user(buf, msc->base + start, tocopy)) + if (copy_to_user(buf, in_buf + start, tocopy)) return -EFAULT; buf += tocopy; @@ -1071,21 +1077,17 @@ msc_single_to_user(struct msc *msc, char __user *buf, loff_t off, size_t len) } start &= size - 1; - if (rem) { - tocopy = min(rem, msc->single_sz - start); - if (copy_to_user(buf, msc->base + start, tocopy)) - return -EFAULT; - - rem -= tocopy; - } - - return len - rem; } + /* Copy the beginning of the buffer */ + if (rem) { + tocopy = min(rem, in_sz - start); + if (copy_to_user(buf, in_buf + start, tocopy)) + return -EFAULT; - if (copy_to_user(buf, msc->base + start, rem)) - return -EFAULT; + rem -= tocopy; + } - return len; + return len - rem; } static ssize_t intel_th_msc_read(struct file *file, char __user *buf, @@ -1115,8 +1117,10 @@ static ssize_t intel_th_msc_read(struct file *file, char __user *buf, len = size - off; if (msc->mode == MSC_MODE_SINGLE) { - ret = msc_single_to_user(msc, buf, off, len); - if (ret >= 0) + ret = msc_single_to_user(msc->base, msc->nr_pages, + msc->single_sz, msc->single_wrap, + buf, off, len); + if (ret > 0) *ppos += ret; } else if (msc->mode == MSC_MODE_MULTI) { struct msc_win_to_user_struct u = { @@ -1258,6 +1262,267 @@ static void msc_wait_ple(struct intel_th_device *thdev) dev_dbg(msc_dev(msc), "timeout waiting for MSC0 PLE\n"); } +#ifdef CONFIG_ACPI +#define ACPI_SIG_NPKT "NPKT" + +/* Buffers that may be handed through NPKT ACPI table */ +enum NPKT_BUF_TYPE { + NPKT_MTB = 0, + NPKT_MTB_REC, + NPKT_CSR, + NPKT_CSR_REC, + NPKT_NBUF +}; +static const char * const npkt_buf_name[NPKT_NBUF] = { + [NPKT_MTB] = "mtb", + [NPKT_MTB_REC] = "mtb_rec", + [NPKT_CSR] = "csr", + [NPKT_CSR_REC] = "csr_rec" +}; + +/* CSR capture still active */ +#define NPKT_CSR_USED BIT(4) + +struct acpi_npkt_buf { + u64 addr; + u32 size; + u32 offset; +}; + +/* NPKT ACPI table */ +struct acpi_table_npkt { + struct acpi_table_header header; + struct acpi_npkt_buf buffers[NPKT_NBUF]; + u8 flags; +} __packed; + +/* Trace buffer obtained from NPKT table */ +struct npkt_buf { + dma_addr_t phy; + void *buf; + u32 size; + u32 offset; + bool wrapped; + atomic_t active; + struct msc *msc; +}; + +static struct npkt_buf *npkt_bufs; +static struct dentry *npkt_dump_dir; +static DEFINE_MUTEX(npkt_lock); + +/** + * Stop current trace if a buffer was marked with a capture in pogress. + * + * Update buffer write offset and wrap status after stopping the trace. + */ +static void stop_buffer_trace(struct npkt_buf *buf) +{ + u32 reg, mode; + struct msc *msc = buf->msc; + + mutex_lock(&npkt_lock); + if (!atomic_read(&buf->active)) + goto unlock; + + reg = ioread32(msc->reg_base + REG_MSU_MSC0CTL); + mode = (reg & MSC_MODE) >> __ffs(MSC_MODE); + if (!(reg & MSC_EN) || mode != MSC_MODE_SINGLE) { + /* Assume full buffer */ + pr_warn("NPKT reported CSR in use but not tracing to CSR\n"); + buf->offset = 0; + buf->wrapped = true; + atomic_set(&buf->active, 0); + goto unlock; + } + + /* The hub must be able to stop a capture not started by the driver */ + intel_th_trace_disable(msc->thdev); + + /* Update offset and wrap status */ + reg = ioread32(msc->reg_base + REG_MSU_MSC0MWP); + buf->offset = reg - (u32)buf->phy; + reg = ioread32(msc->reg_base + REG_MSU_MSC0STS); + buf->wrapped = !!(reg & MSCSTS_WRAPSTAT); + atomic_set(&buf->active, 0); + +unlock: + mutex_unlock(&npkt_lock); +} + +/** + * Copy re-ordered data from an NPKT buffer to a user buffer. + */ +static ssize_t read_npkt_dump_buf(struct file *file, char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct npkt_buf *buf = file->private_data; + size_t size = buf->size; + loff_t off = *ppos; + ssize_t ret; + + if (atomic_read(&buf->active)) + stop_buffer_trace(buf); + + if (off >= size) + return 0; + + ret = msc_single_to_user(buf->buf, size >> PAGE_SHIFT, + buf->offset, buf->wrapped, + user_buf, off, count); + if (ret > 0) + *ppos += ret; + + return ret; +} + +static const struct file_operations npkt_dump_buf_fops = { + .read = read_npkt_dump_buf, + .open = simple_open, + .llseek = noop_llseek, +}; + +/** + * Prepare a buffer with remapped address for a given NPKT buffer and add + * an entry for it in debugfs. + */ +static void npkt_bind_buffer(enum NPKT_BUF_TYPE type, + struct acpi_npkt_buf *abuf, u8 flags, + struct npkt_buf *buf, struct msc *msc) +{ + const char *name = npkt_buf_name[type]; + + /* No buffer handed through ACPI */ + if (!abuf->addr || !abuf->size) + return; + + /* Only expect multiples of page size */ + if (abuf->size & (PAGE_SIZE - 1)) { + pr_warn("invalid size 0x%x for buffer %s\n", + abuf->size, name); + return; + } + + buf->size = abuf->size; + buf->offset = abuf->offset; + buf->wrapped = !!(flags & BIT(type)); + /* CSR may still be active */ + if (type == NPKT_CSR && (flags & NPKT_CSR_USED)) { + atomic_set(&buf->active, 1); + buf->msc = msc; + } + + buf->phy = abuf->addr; + buf->buf = (__force void *)ioremap(buf->phy, buf->size); + if (!buf->buf) { + pr_err("ioremap failed for buffer %s 0x%llx size:0x%x\n", + name, buf->phy, buf->size); + return; + } + + debugfs_create_file(name, S_IRUGO, npkt_dump_dir, buf, + &npkt_dump_buf_fops); +} + +static void npkt_bind_buffers(struct acpi_table_npkt *npkt, + struct npkt_buf *bufs, struct msc *msc) +{ + int i; + + for (i = 0; i < NPKT_NBUF; i++) + npkt_bind_buffer(i, &npkt->buffers[i], npkt->flags, + &bufs[i], msc); +} + +static void npkt_unbind_buffers(struct npkt_buf *bufs) +{ + int i; + + for (i = 0; i < NPKT_NBUF; i++) + if (bufs[i].buf) + iounmap((__force void __iomem *)bufs[i].buf); +} + +/** + * Prepare debugfs access to NPKT buffers. + */ +static void intel_th_npkt_init(struct msc *msc) +{ + acpi_status status; + struct acpi_table_npkt *npkt; + + /* Associate NPKT to msc0 */ + if (npkt_bufs || msc->index != 0) + return; + + status = acpi_get_table(ACPI_SIG_NPKT, 0, + (struct acpi_table_header **)&npkt); + if (ACPI_FAILURE(status)) { + pr_warn("Failed to get NPKT table, %s\n", + acpi_format_exception(status)); + return; + } + + npkt_bufs = kzalloc(sizeof(struct npkt_buf) * NPKT_NBUF, GFP_KERNEL); + if (!npkt_bufs) + return; + + npkt_dump_dir = debugfs_create_dir("npkt_dump", NULL); + if (!npkt_dump_dir) { + pr_err("npkt_dump debugfs create dir failed\n"); + goto free_npkt_bufs; + } + + npkt_bind_buffers(npkt, npkt_bufs, msc); + + return; + +free_npkt_bufs: + kfree(npkt_bufs); + npkt_bufs = NULL; +} + +/** + * Remove debugfs access to NPKT buffers and release resources. + */ +static void intel_th_npkt_remove(struct msc *msc) +{ + /* Only clean for msc 0 if necessary */ + if (!npkt_bufs || msc->index != 0) + return; + + npkt_unbind_buffers(npkt_bufs); + debugfs_remove_recursive(npkt_dump_dir); + kfree(npkt_bufs); + npkt_bufs = NULL; +} + +/** + * First trace callback. + * + * If NPKT notified a CSR capture is in progress, stop it and update buffer + * write offset and wrap status. + */ +static void intel_th_msc_first_trace(struct intel_th_device *thdev) +{ + struct device *dev = &thdev->dev; + struct msc *msc = dev_get_drvdata(dev); + struct npkt_buf *buf; + + if (!npkt_bufs || msc->index != 0) + return; + + buf = &npkt_bufs[NPKT_CSR]; + if (atomic_read(&buf->active)) + stop_buffer_trace(buf); +} + +#else /* !CONFIG_ACPI */ +static inline void intel_th_npkt_init(struct msc *msc) {} +static inline void intel_th_npkt_remove(struct msc *msc) {} +#define intel_th_msc_first_trace NULL +#endif /* !CONFIG_ACPI */ + static int intel_th_msc_init(struct msc *msc) { atomic_set(&msc->user_count, -1); @@ -1513,26 +1778,20 @@ static int intel_th_msc_probe(struct intel_th_device *thdev) dev_set_drvdata(dev, msc); + intel_th_npkt_init(msc); + return 0; } static void intel_th_msc_remove(struct intel_th_device *thdev) { struct msc *msc = dev_get_drvdata(&thdev->dev); - int ret; - - intel_th_msc_deactivate(thdev); - - /* - * Buffers should not be used at this point except if the - * output character device is still open and the parent - * device gets detached from its bus, which is a FIXME. - */ - ret = msc_buffer_free_unless_used(msc); - WARN_ON_ONCE(ret); + intel_th_npkt_remove(msc); + sysfs_remove_group(&thdev->dev.kobj, &msc_output_group); } static struct intel_th_driver intel_th_msc_driver = { + .first_trace = intel_th_msc_first_trace, .probe = intel_th_msc_probe, .remove = intel_th_msc_remove, .activate = intel_th_msc_activate, From a17badbb6d3cabc4037aec92f095baef4e65358f Mon Sep 17 00:00:00 2001 From: Traian Schiau Date: Thu, 6 Aug 2015 16:48:34 +0300 Subject: [PATCH 0556/1103] intel_th: New MSU kernel APIs The following new kernel APIs are added: Instance management: msc_register_callbacks(), msc_unregister_callbacks() Provide the means for an external module to be notified when msc sub-devices are added/removed. Configuration helpers: msc_max_blocks(), msc_block_max_size() Transfer: msc_switch_window(), msc_current_win_bytes(), msc_sg_oldest_win() Perform a window switch, get the state of the current window used and get the oldest data. Signed-off-by: Traian Schiau --- drivers/hwtracing/intel_th/msu.c | 286 +++++++++++++++++++++++++++++++ drivers/hwtracing/intel_th/msu.h | 17 ++ 2 files changed, 303 insertions(+) diff --git a/drivers/hwtracing/intel_th/msu.c b/drivers/hwtracing/intel_th/msu.c index 6e1d359ed2d5..fa149469d3b9 100644 --- a/drivers/hwtracing/intel_th/msu.c +++ b/drivers/hwtracing/intel_th/msu.c @@ -102,6 +102,8 @@ struct msc_iter { * @mode: MSC operating mode * @burst_len: write burst length * @index: number of this MSC in the MSU + * + * @max_blocks: Maximum number of blocks in a window */ struct msc { void __iomem *reg_base; @@ -129,8 +131,99 @@ struct msc { unsigned int mode; unsigned int burst_len; unsigned int index; + unsigned int max_blocks; }; +static struct msc_probe_rem_cb msc_probe_rem_cb; + +struct msc_device_instance { + struct list_head list; + struct intel_th_device *thdev; +}; + +static LIST_HEAD(msc_dev_instances); +static DEFINE_SPINLOCK(msc_dev_reg_lock); +/** + * msc_register_callbacks() + * @cbs + */ +int msc_register_callbacks(struct msc_probe_rem_cb cbs) +{ + struct msc_device_instance *it; + + spin_lock(&msc_dev_reg_lock); + + msc_probe_rem_cb.probe = cbs.probe; + msc_probe_rem_cb.remove = cbs.remove; + /* Call the probe callback for the already existing ones*/ + list_for_each_entry(it, &msc_dev_instances, list) { + cbs.probe(it->thdev); + } + + spin_unlock(&msc_dev_reg_lock); + return 0; +} +EXPORT_SYMBOL_GPL(msc_register_callbacks); + +/** + * msc_unregister_callbacks() + */ +void msc_unregister_callbacks(void) +{ + spin_lock(&msc_dev_reg_lock); + + msc_probe_rem_cb.probe = NULL; + msc_probe_rem_cb.remove = NULL; + + spin_unlock(&msc_dev_reg_lock); +} +EXPORT_SYMBOL_GPL(msc_unregister_callbacks); + +static void msc_add_instance(struct intel_th_device *thdev) +{ + struct msc_device_instance *instance; + + instance = kmalloc(sizeof(*instance), GFP_KERNEL); + if (!instance) + return; + + spin_lock(&msc_dev_reg_lock); + + instance->thdev = thdev; + list_add(&instance->list, &msc_dev_instances); + + if (msc_probe_rem_cb.probe) + msc_probe_rem_cb.probe(thdev); + + spin_unlock(&msc_dev_reg_lock); +} + +static void msc_rm_instance(struct intel_th_device *thdev) +{ + struct msc_device_instance *instance = NULL, *it; + + spin_lock(&msc_dev_reg_lock); + + if (msc_probe_rem_cb.remove) + msc_probe_rem_cb.remove(thdev); + + list_for_each_entry(it, &msc_dev_instances, list) { + if (it->thdev == thdev) { + instance = it; + break; + } + } + + if (instance) { + list_del(&instance->list); + kfree(instance); + } else { + pr_warn("msu: cannot remove %p (not found)", thdev); + } + + spin_unlock(&msc_dev_reg_lock); +} + static inline bool msc_block_is_empty(struct msc_block_desc *bdesc) { /* header hasn't been written */ @@ -144,6 +237,37 @@ static inline bool msc_block_is_empty(struct msc_block_desc *bdesc) return false; } +/** + * msc_current_window() - locate the window in use + * @msc: MSC device + * + * This should only be used in multiblock mode. Caller should hold the + * msc::user_count reference. + * + * Return: the current output window + */ +static struct msc_window *msc_current_window(struct msc *msc) +{ + struct msc_window *win, *prev = NULL; + /*BAR is never changing, so the current one is the one before the next*/ + u32 reg = ioread32(msc->reg_base + REG_MSU_MSC0NWSA); + unsigned long win_addr = (unsigned long)reg << PAGE_SHIFT; + + if (list_empty(&msc->win_list)) + return NULL; + + list_for_each_entry(win, &msc->win_list, entry) { + if (win->block[0].addr == win_addr) + break; + prev = win; + } + if (!prev) + prev = list_entry(msc->win_list.prev, struct msc_window, entry); + + return prev; +} + + /** * msc_oldest_window() - locate the window with oldest data * @msc: MSC device @@ -210,6 +334,160 @@ static unsigned int msc_win_oldest_block(struct msc_window *win) return 0; } +/** + * msc_max_blocks() - get the maximum number of block + * @thdev: the sub-device + * + * Return: the maximum number of blocks / window + */ +unsigned int msc_max_blocks(struct intel_th_device *thdev) +{ + struct msc *msc = dev_get_drvdata(&thdev->dev); + + return msc->max_blocks; +} +EXPORT_SYMBOL_GPL(msc_max_blocks); + +/** + * msc_block_max_size() - get the size of biggest block + * @thdev: the sub-device + * + * Return: the size of biggest block + */ +unsigned int msc_block_max_size(struct intel_th_device *thdev) +{ + return PAGE_SIZE; +} +EXPORT_SYMBOL_GPL(msc_block_max_size); + +/** + * msc_switch_window() - perform a window switch + * @thdev: the sub-device + */ +int msc_switch_window(struct intel_th_device *thdev) +{ + intel_th_trace_switch(thdev); + return 0; +} +EXPORT_SYMBOL_GPL(msc_switch_window); + +/** + * msc_current_win_bytes() - get the current window data size + * @thdev: the sub-device + * + * Get the number of valid data bytes in the current window. + * Based on this the dvc-source part can decide to request a window switch. + */ +int msc_current_win_bytes(struct intel_th_device *thdev) +{ + struct msc *msc = dev_get_drvdata(&thdev->dev); + struct msc_window *win; + u32 reg_mwp, blk, offset, i; + int size = 0; + + /* proceed only if actively storing in muli-window mode */ + if (!msc->enabled || + (msc->mode != MSC_MODE_MULTI) || + !atomic_inc_unless_negative(&msc->user_count)) + return -EINVAL; + + win = msc_current_window(msc); + reg_mwp = ioread32(msc->reg_base + REG_MSU_MSC0MWP); + + if (!win) { + atomic_dec(&msc->user_count); + return -EINVAL; + } + + blk = 0; + while (blk < win->nr_blocks) { + if (win->block[blk].addr == (reg_mwp & PAGE_MASK)) + break; + blk++; + } + + if (blk >= win->nr_blocks) { + atomic_dec(&msc->user_count); + return -EINVAL; + } + + offset = (reg_mwp & (PAGE_SIZE - 1)); + + + /*if wrap*/ + if (msc_block_wrapped(win->block[blk].bdesc)) { + for (i = blk+1; i < win->nr_blocks; i++) + size += msc_data_sz(win->block[i].bdesc); + } + + for (i = 0; i < blk; i++) + size += msc_data_sz(win->block[i].bdesc); + + /*finaly the current one*/ + size += (offset - MSC_BDESC); + + atomic_dec(&msc->user_count); + return size; +} +EXPORT_SYMBOL_GPL(msc_current_win_bytes); + +/** + * msc_sg_oldest_win() - get the data from the oldest window + * @thdev: the sub-device + * @sg_array: destination sg array + * + * Return: sg count + */ +int msc_sg_oldest_win(struct intel_th_device *thdev, + struct scatterlist *sg_array) +{ + struct msc *msc = dev_get_drvdata(&thdev->dev); + struct msc_window *win, *c_win; + struct msc_block_desc *bdesc; + unsigned int blk, sg = 0; + + /* proceed only if actively storing in muli-window mode */ + if (!msc->enabled || + (msc->mode != MSC_MODE_MULTI) || + !atomic_inc_unless_negative(&msc->user_count)) + return -EINVAL; + + win = msc_oldest_window(msc); + if (!win) + return 0; + + c_win = msc_current_window(msc); + + if (win == c_win) + return 0; + + blk = msc_win_oldest_block(win); + + /* start with the first block containing only oldest data */ + if (msc_block_wrapped(win->block[blk].bdesc)) + if (++blk == win->nr_blocks) + blk = 0; + + do { + bdesc = win->block[blk].bdesc; + sg_set_buf(&sg_array[sg++], bdesc, PAGE_SIZE); + + if (bdesc->hw_tag & MSC_HW_TAG_ENDBIT) + break; + + if (++blk == win->nr_blocks) + blk = 0; + + } while (sg <= win->nr_blocks); + + sg_mark_end(&sg_array[sg - 1]); + + atomic_dec(&msc->user_count); + + return sg; +} +EXPORT_SYMBOL_GPL(msc_sg_oldest_win); + /** * msc_is_last_win() - check if a window is the last one for a given MSC * @win: window @@ -1661,6 +1939,8 @@ nr_pages_store(struct device *dev, struct device_attribute *attr, if (ret) return ret; + msc->max_blocks = 0; + /* scan the comma-separated list of allocation sizes */ end = memchr(buf, '\n', len); if (end) @@ -1695,6 +1975,9 @@ nr_pages_store(struct device *dev, struct device_attribute *attr, win = rewin; win[nr_wins - 1] = val; + msc->max_blocks = + (val > msc->max_blocks) ? val : msc->max_blocks; + if (!end) break; @@ -1776,9 +2059,11 @@ static int intel_th_msc_probe(struct intel_th_device *thdev) if (err) return err; + msc->max_blocks = 0; dev_set_drvdata(dev, msc); intel_th_npkt_init(msc); + msc_add_instance(thdev); return 0; } @@ -1787,6 +2072,7 @@ static void intel_th_msc_remove(struct intel_th_device *thdev) { struct msc *msc = dev_get_drvdata(&thdev->dev); intel_th_npkt_remove(msc); + msc_rm_instance(thdev); sysfs_remove_group(&thdev->dev.kobj, &msc_output_group); } diff --git a/drivers/hwtracing/intel_th/msu.h b/drivers/hwtracing/intel_th/msu.h index 9cc8aced6116..ccfed662a726 100644 --- a/drivers/hwtracing/intel_th/msu.h +++ b/drivers/hwtracing/intel_th/msu.h @@ -8,6 +8,8 @@ #ifndef __INTEL_TH_MSU_H__ #define __INTEL_TH_MSU_H__ +#include "intel_th.h" + enum { REG_MSU_MSUPARAMS = 0x0000, REG_MSU_MSUSTS = 0x0008, @@ -105,4 +107,19 @@ static inline bool msc_block_last_written(struct msc_block_desc *bdesc) /* waiting for Pipeline Empty bit(s) to assert for MSC */ #define MSC_PLE_WAITLOOP_DEPTH 10000 +/* API */ +struct msc_probe_rem_cb { + void (*probe)(struct intel_th_device *thdev); + void (*remove)(struct intel_th_device *thdev); +}; + +int msc_register_callbacks(struct msc_probe_rem_cb cbs); +void msc_unregister_callbacks(void); +unsigned int msc_max_blocks(struct intel_th_device *thdev); +unsigned int msc_block_max_size(struct intel_th_device *thdev); +int msc_switch_window(struct intel_th_device *thdev); +int msc_sg_oldest_win(struct intel_th_device *thdev, + struct scatterlist *sg_array); +int msc_current_win_bytes(struct intel_th_device *thdev); + #endif /* __INTEL_TH_MSU_H__ */ From 797d5e458bda5a735c0af87ebdadd33bc647f755 Mon Sep 17 00:00:00 2001 From: Traian Schiau Date: Thu, 6 Aug 2015 16:48:47 +0300 Subject: [PATCH 0557/1103] intel_th: implement dvc trace source for intel_th Add support for sending Intel Trace Hub data over USB, using DvC-Trace specification. Signed-off-by: Traian Schiau --- .../testing/sysfs-bus-dvctrace-devices-dvcith | 68 ++ MAINTAINERS | 6 + drivers/hwtracing/intel_th/Kconfig | 20 + drivers/hwtracing/intel_th/Makefile | 6 +- drivers/hwtracing/intel_th/msu-dvc.c | 1084 +++++++++++++++++ 5 files changed, 1183 insertions(+), 1 deletion(-) create mode 100644 Documentation/ABI/testing/sysfs-bus-dvctrace-devices-dvcith create mode 100644 drivers/hwtracing/intel_th/msu-dvc.c diff --git a/Documentation/ABI/testing/sysfs-bus-dvctrace-devices-dvcith b/Documentation/ABI/testing/sysfs-bus-dvctrace-devices-dvcith new file mode 100644 index 000000000000..0dda3aefb89c --- /dev/null +++ b/Documentation/ABI/testing/sysfs-bus-dvctrace-devices-dvcith @@ -0,0 +1,68 @@ +What: /sys/bus/dvctrace/devices/dvcith-/msc +Date: Aug 2015 +KernelVersion: 4.0 +Contact: Traian Schiau +Description: (R) Symbolic link to the Intel Trace Hub MSC + (Memory Storage Controller) sub-device used to get tracing data. + +What: /sys/bus/dvctrace/devices/dvcith-/mdd_min_transfer +Date: Aug 2015 +KernelVersion: 4.0 +Contact: Traian Schiau +Description: (RW) Window transfer watermark, the driver will queue a + new transfer only if at least bytes + of trace data is available. Since on every switch @48 bytes + of trace data is generated, this should not be set under this + threshold. + Default 2048 + +What: /sys/bus/dvctrace/devices/dvcith-/mdd_retry_timeout +Date: Aug 2015 +KernelVersion: 4.0 +Contact: Traian Schiau +Description: (RW) Read retry interval, If by the time the last usb transfer + is complete, there is no new data to be sent the driver will + sleep ms, before checking again. + Default: 2 ms + +What: /sys/bus/dvctrace/devices/dvcith-/mdd_max_retry +Date: Aug 2015 +KernelVersion: 4.0 +Contact: Traian Schiau +Description: (RW) the maximum retries to be bone before triggering a switch + and sending the currently available data regardless of the + available size. + Default: 150 + +What: /sys/bus/dvctrace/devices/dvcith-/mdd_proc_type +Date: Aug 2015 +KernelVersion: 4.0 +Contact: Traian Schiau +Description: (RW) Data process type, during DvC tracing the MSC is set up in + Multi Window mode (check Intel Trace Hub Developer's Manual for + details), This attribute specifies what the dvc-trace data stream + should contain. + Available values are: + - 1 - Full blocks, + - 2 - Trimmed blocks (Block header + STP data) + - 3 - STP data only. + Default 3. + +What: /sys/bus/dvctrace/devices/dvcith-/mdd_transfer_type +Date: Aug 2015 +KernelVersion: 4.0 +Contact: Traian Schiau +Description: (RW) Data transfer type, This attribute specifies how the trace data + is queued in the USB requests. + Available values are: + - 1 - Auto, + - 2 - SG-List, + - 3 - Linear buffer. + Default 1. + +What: /sys/bus/dvctrace/devices/dvcith-/mdd_stats +Date: Aug 2015 +KernelVersion: 4.0 +Contact: Traian Schiau +Description: (R) Provides statistical information regarding the latest. + trace session. Available if (CONFIG_INTEL_TH_MSU_DVC_DEBUG). diff --git a/MAINTAINERS b/MAINTAINERS index 80899bee1ec8..78ddf27c8185 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -7594,6 +7594,12 @@ S: Supported F: Documentation/trace/intel_th.rst F: drivers/hwtracing/intel_th/ +INTEL(R) TRACE HUB TO USB-DVC.TRACE +M: Traian Schiau +S: Supported +F: drivers/hwtracing/intel_th/msu-dvc.c +F: Documentation/ABI/testing/sysfs-bus-dvctrace-devices-dvcith + INTEL(R) TRUSTED EXECUTION TECHNOLOGY (TXT) M: Ning Sun L: tboot-devel@lists.sourceforge.net diff --git a/drivers/hwtracing/intel_th/Kconfig b/drivers/hwtracing/intel_th/Kconfig index 4fdf936dc04b..027edb7da72c 100644 --- a/drivers/hwtracing/intel_th/Kconfig +++ b/drivers/hwtracing/intel_th/Kconfig @@ -67,6 +67,26 @@ config INTEL_TH_MSU Say Y here to enable MSU output device for Intel TH. +config INTEL_TH_MSU_DVC + tristate "Intel Trace Hub Memory Storage Unit to USB-dvc" + help + Memory Storage Unit (MSU) trace output device enables + storing STP traces to system memory. + This provides the means to route this data over USB, + using DvC-Trace. + + Say Y here to enable DvC-Trace output device for Intel TH. + +config INTEL_TH_MSU_DVC_DEBUG + tristate "Intel Trace Hub Memory Storage Unit to USB-dvc debug" + help + Memory Storage Unit (MSU) trace output device enables + storing STP traces to system memory. + This enables extensive logging and collection of + statistical data on MSU/DvC-Trace device performance. + + Say Y to enable extended debug features on MSU-DvC. + config INTEL_TH_PTI tristate "Intel(R) Trace Hub PTI output" help diff --git a/drivers/hwtracing/intel_th/Makefile b/drivers/hwtracing/intel_th/Makefile index d72530116664..6d3dc7de0bb3 100644 --- a/drivers/hwtracing/intel_th/Makefile +++ b/drivers/hwtracing/intel_th/Makefile @@ -18,8 +18,12 @@ intel_th_sth-y := sth.o obj-$(CONFIG_INTEL_TH_MSU) += intel_th_msu.o intel_th_msu-y := msu.o +obj-$(CONFIG_INTEL_TH_MSU_DVC) += intel_th_msu_dvc.o +intel_th_msu_dvc-y := msu-dvc.o +subdir-ccflags-$(CONFIG_INTEL_TH_MSU_DVC_DEBUG) += -DMDD_DEBUG + obj-$(CONFIG_INTEL_TH_PTI) += intel_th_pti.o intel_th_pti-y := pti.o obj-$(CONFIG_INTEL_TH_EARLY_PRINTK) += intel_th_early_printk.o -intel_th_early_printk-y := early_printk.o \ No newline at end of file +intel_th_early_printk-y := early_printk.o diff --git a/drivers/hwtracing/intel_th/msu-dvc.c b/drivers/hwtracing/intel_th/msu-dvc.c new file mode 100644 index 000000000000..475f4cfe51ba --- /dev/null +++ b/drivers/hwtracing/intel_th/msu-dvc.c @@ -0,0 +1,1084 @@ +/* + * Intel Trace Hub to USB dvc-trace driver + * + * Copyright (C) 2015, Intel Corporation. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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. + * + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": %s: " fmt, __func__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "msu.h" + +#ifdef MDD_DEBUG +#define MDD_F_DEBUG() pr_debug("\n") +#else +#define MDD_F_DEBUG() do {} while (0) +#endif + +#define DTC_DRV_NAME "dvcith" + +#define MDD_MIN_TRANSFER_DEF 2048 +#define MDD_RETRY_TIMEOUT_DEF 2 +#define MDD_MAX_RETRY_CNT_DEF 150 + +#define mdd_err(mdd, ...) dev_err(&(mdd)->ddev.device, ## __VA_ARGS__) +#define mdd_warn(mdd, ...) dev_warn(&(mdd)->ddev.device, ## __VA_ARGS__) +#define mdd_info(mdd, ...) dev_info(&(mdd)->ddev.device, ## __VA_ARGS__) +#define mdd_debug(mdd, ...) dev_debug(&(mdd)->ddev.device, ## __VA_ARGS__) + +#ifdef MDD_DEBUG +struct msu_dvc_stats { + unsigned long work_start; + unsigned long work_end; + + unsigned long loop_count; + unsigned long hits; + + u64 full_block_size; + u64 valid_block_size; + u64 valid_data_size; + + u32 transfer_type:2; + u32 process_type:2; + + enum usb_device_speed speed; +}; +#endif + +enum { + MDD_TRANSFER_NO_CHANGE, + MDD_TRANSFER_AUTO, + MDD_TRANSFER_MIN = MDD_TRANSFER_AUTO, + MDD_TRANSFER_SINGLE, + MDD_TRANSFER_MULTI, + MDD_TRANSFER_MAX = MDD_TRANSFER_MULTI, +}; + +static const char *const transfer_type_name[] = { + [MDD_TRANSFER_AUTO] = "Auto", + [MDD_TRANSFER_SINGLE] = "Single", + [MDD_TRANSFER_MULTI] = "Multi", +}; + +enum { + MDD_PROC_NO_CHANGE, + MDD_PROC_NONE, + MDD_PROC_MIN = MDD_PROC_NONE, + MDD_PROC_REM_TAIL, + MDD_PROC_REM_ALL, + MDD_PROC_MAX = MDD_PROC_REM_ALL, +}; + +static const char *const process_type_name[] = { + [MDD_PROC_NONE] = "Full-Blocks", + [MDD_PROC_REM_TAIL] = "Trimmed-Blocks", + [MDD_PROC_REM_ALL] = "STP", +}; + +struct mdd_transfer_data { + u8 *buffer; + dma_addr_t buffer_dma; + size_t buffer_len; + struct scatterlist *sg_raw; + struct scatterlist *sg_proc; + struct scatterlist *sg_trans; /* not be allocated */ + size_t block_count; + size_t block_size; + spinlock_t lock; +}; + +#define mdd_lock_transfer(mdd) spin_lock(&mdd->tdata.lock) +#define mdd_unlock_transfer(mdd) spin_unlock(&mdd->tdata.lock) + +struct msu_dvc_dev { + struct dvct_source_device ddev; + atomic_t *dtc_status; + struct usb_ep *ep; + struct usb_function *func; + enum usb_device_speed speed; + struct intel_th_device *th_dev; + + struct workqueue_struct *wrq; + struct work_struct work; + struct usb_request *req; + wait_queue_head_t wq; + atomic_t req_ongoing; + + /*attributes */ + u32 retry_timeout; + u32 max_retry_count; + u32 transfer_type:2; + u32 process_type:2; + u32 min_transfer; + +#ifdef MDD_DEBUG + struct msu_dvc_stats stats; +#endif + struct mdd_transfer_data tdata; + + struct list_head mdd_list; +}; + +static LIST_HEAD(mdd_devs); +static DEFINE_SPINLOCK(mdd_devs_lock); + +static inline struct usb_gadget *mdd_gadget(struct msu_dvc_dev *mdd) +{ + BUG_ON(!mdd->func); + BUG_ON(!mdd->func->config); + BUG_ON(!mdd->func->config->cdev); + BUG_ON(!mdd->func->config->cdev->gadget); + return mdd->func->config->cdev->gadget; +} + +/* Back-cast to msu_dvc_dev */ +static inline struct msu_dvc_dev *dtc_to_mdd(struct dvct_source_device *p_dtc) +{ + return container_of(p_dtc, struct msu_dvc_dev, ddev); +} + +static ssize_t mdd_min_transfer_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct msu_dvc_dev *mdd; + + MDD_F_DEBUG(); + mdd = container_of(dev, struct msu_dvc_dev, ddev.device); + return sprintf(buf, "%u\n", mdd->min_transfer); +} + +static ssize_t mdd_min_transfer_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct msu_dvc_dev *mdd; + + MDD_F_DEBUG(); + mdd = container_of(dev, struct msu_dvc_dev, ddev.device); + + if (mdd->dtc_status + && dvct_get_status(mdd->dtc_status, DVCT_MASK_TRANS)) + return -EBUSY; + + /* 48 represents the size of sync frames that are generated by + * a window switch, from this point on we have "real data" + * Going under this value could result in unneeded switching */ + if (!kstrtou32(buf, 10, &mdd->min_transfer)) { + if (mdd->min_transfer < 48) + mdd->min_transfer = 48; + return count; + } + + return -EINVAL; +} + +static DEVICE_ATTR_RW(mdd_min_transfer); + + +static ssize_t mdd_retry_timeout_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct msu_dvc_dev *mdd; + + MDD_F_DEBUG(); + mdd = container_of(dev, struct msu_dvc_dev, ddev.device); + return sprintf(buf, "%u\n", mdd->retry_timeout); +} + +static ssize_t mdd_retry_timeout_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct msu_dvc_dev *mdd; + + MDD_F_DEBUG(); + mdd = container_of(dev, struct msu_dvc_dev, ddev.device); + if (!kstrtou32(buf, 10, &mdd->retry_timeout)) + return count; + + return -EINVAL; +} + +static DEVICE_ATTR_RW(mdd_retry_timeout); + +static ssize_t mdd_max_retry_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct msu_dvc_dev *mdd; + + MDD_F_DEBUG(); + mdd = container_of(dev, struct msu_dvc_dev, ddev.device); + return sprintf(buf, "%u\n", mdd->max_retry_count); +} + +static ssize_t mdd_max_retry_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct msu_dvc_dev *mdd; + + MDD_F_DEBUG(); + mdd = container_of(dev, struct msu_dvc_dev, ddev.device); + if (!kstrtou32(buf, 10, &mdd->max_retry_count)) + return count; + + return -EINVAL; +} + +static DEVICE_ATTR_RW(mdd_max_retry); + +static ssize_t mdd_transfer_type_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct msu_dvc_dev *mdd; + + MDD_F_DEBUG(); + mdd = container_of(dev, struct msu_dvc_dev, ddev.device); + return sprintf(buf, "%d %s\n", mdd->transfer_type, + transfer_type_name[mdd->transfer_type]); +} + +static ssize_t mdd_transfer_type_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct msu_dvc_dev *mdd; + u8 tmp; + + MDD_F_DEBUG(); + mdd = container_of(dev, struct msu_dvc_dev, ddev.device); + + if (mdd->dtc_status + && dvct_get_status(mdd->dtc_status, DVCT_MASK_TRANS)) + return -EBUSY; + + if (!kstrtou8(buf, 10, &tmp) && tmp <= MDD_TRANSFER_MAX + && tmp >= MDD_TRANSFER_MIN) { + mdd->transfer_type = tmp; + return count; + } + return -EINVAL; +} + +static DEVICE_ATTR_RW(mdd_transfer_type); + +static ssize_t mdd_proc_type_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct msu_dvc_dev *mdd; + + MDD_F_DEBUG(); + mdd = container_of(dev, struct msu_dvc_dev, ddev.device); + return sprintf(buf, "%d %s\n", mdd->process_type, + process_type_name[mdd->process_type]); +} + +static ssize_t mdd_proc_type_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct msu_dvc_dev *mdd; + u8 tmp; + + MDD_F_DEBUG(); + + mdd = container_of(dev, struct msu_dvc_dev, ddev.device); + + if (mdd->dtc_status + && dvct_get_status(mdd->dtc_status, DVCT_MASK_TRANS)) + return -EBUSY; + + if (!kstrtou8(buf, 10, &tmp) && tmp <= MDD_PROC_MAX + && tmp >= MDD_PROC_MIN) { + mdd->process_type = tmp; + return count; + } + return -EINVAL; +} + +static DEVICE_ATTR_RW(mdd_proc_type); + +#ifdef MDD_DEBUG + +static ssize_t mdd_stats_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct msu_dvc_dev *mdd; + int len = 0; + + static const char *const u_speed_names[] = { + [USB_SPEED_UNKNOWN] = "?", + [USB_SPEED_LOW] = "LS", + [USB_SPEED_FULL] = "FS", + [USB_SPEED_HIGH] = "HS", + [USB_SPEED_WIRELESS] = "WR", + [USB_SPEED_SUPER] = "SS", + }; + + MDD_F_DEBUG(); + mdd = container_of(dev, struct msu_dvc_dev, ddev.device); + + len += snprintf(buf + len, PAGE_SIZE - len, "R.count\tR.hits\t"); + + len += snprintf(buf + len, PAGE_SIZE - len, "T.tot_j\tT.tot_ms\t"); + len += + snprintf(buf + len, PAGE_SIZE - len, "D.total\tD.block\tT.stp\t"); + len += snprintf(buf + len, PAGE_SIZE - len, "Tr.type\tProc.type\t"); + + len += snprintf(buf + len, PAGE_SIZE - len, "USB.speed\n"); + + /* Actual values starts here */ + len += + snprintf(buf + len, PAGE_SIZE - len, "%lu\t%lu\t", + mdd->stats.loop_count, mdd->stats.hits); + + len += + snprintf(buf + len, PAGE_SIZE - len, "%lu\t%u\t", + (mdd->stats.work_end - mdd->stats.work_start), + jiffies_to_msecs(mdd->stats.work_end - + mdd->stats.work_start)); + len += + snprintf(buf + len, PAGE_SIZE - len, "%llu\t%llu\t%llu\t", + mdd->stats.full_block_size, mdd->stats.valid_block_size, + mdd->stats.valid_data_size); + len += + snprintf(buf + len, PAGE_SIZE - len, "%s\t", + transfer_type_name[mdd->transfer_type]); + len += + snprintf(buf + len, PAGE_SIZE - len, "%s\t", + process_type_name[mdd->process_type]); + + len += + snprintf(buf + len, PAGE_SIZE - len, "%s\n", + u_speed_names[mdd->stats.speed]); + + return len; +} + +static DEVICE_ATTR_RO(mdd_stats); + +static void init_stats_start(struct msu_dvc_dev *mdd) +{ + mdd->stats.loop_count = 0; + mdd->stats.hits = 0; + + mdd->stats.work_start = jiffies; + + mdd->stats.full_block_size = 0; + mdd->stats.valid_block_size = 0; + mdd->stats.valid_data_size = 0; + + mdd->stats.process_type = mdd->process_type; + mdd->stats.transfer_type = mdd->transfer_type; + mdd->stats.speed = mdd->speed; +} + +#define stats_loop(mdd) ((mdd)->stats.loop_count++) +#define stats_hit(mdd) ((mdd)->stats.hits++) +#else +#define init_stats_start(n) do {} while (0) +#define stats_loop(mdd) do {} while (0) +#define stats_hit(mdd) do {} while (0) +#endif + +static void mdd_complete(struct usb_ep *ep, struct usb_request *req) +{ + struct msu_dvc_dev *mdd = (struct msu_dvc_dev *)req->context; + + mdd_lock_transfer(mdd); + + if (req->status != 0) { + mdd_err(mdd, "Usb request error %d\n", req->status); + dvct_clr_status(mdd->dtc_status, DVCT_MASK_TRANS); + dvct_set_status(mdd->dtc_status, DVCT_MASK_ERR); + } + atomic_set(&mdd->req_ongoing, 0); + wake_up(&mdd->wq); + mdd_unlock_transfer(mdd); +} + +static int mdd_setup_transfer_data(struct msu_dvc_dev *mdd) +{ + int ret = -EINVAL; + + MDD_F_DEBUG(); + + if (!mdd->ep || !mdd->req) { + mdd_err(mdd, "Invalid endpoint data\n"); + goto err; + } + + mdd->tdata.block_count = msc_max_blocks(mdd->th_dev); + if (mdd->tdata.block_count <= 0) { + mdd_err(mdd, "Invalid block count %zu\n", + mdd->tdata.block_count); + goto err; + } + + mdd->tdata.block_size = msc_block_max_size(mdd->th_dev); + if (mdd->tdata.block_size <= 0) { + mdd_err(mdd, "Invalid block size %zu\n", mdd->tdata.block_size); + goto err; + } + + mdd->tdata.sg_raw = kmalloc_array(mdd->tdata.block_count, + sizeof(*mdd->tdata.sg_raw), + GFP_KERNEL); + if (!mdd->tdata.sg_raw) { + mdd_err(mdd, "Cannot allocate sg memory %zu\n", + mdd->tdata.block_size); + goto err_sg_raw; + } + + if (mdd->process_type != MDD_PROC_NONE) { + mdd->tdata.sg_proc = kmalloc_array(mdd->tdata.block_count, + sizeof(*mdd->tdata.sg_proc), + GFP_KERNEL); + if (!mdd->tdata.sg_proc) { + mdd_err(mdd, "Cannot allocate sg memory %zu\n", + mdd->tdata.block_size); + goto err_sg_proc; + } + mdd->tdata.sg_trans = mdd->tdata.sg_proc; + } else { + mdd->tdata.sg_trans = mdd->tdata.sg_raw; + } + + if (mdd->transfer_type == MDD_TRANSFER_SINGLE) { + mdd->tdata.buffer_len = + mdd->tdata.block_count * mdd->tdata.block_size; + mdd->tdata.buffer = + dma_alloc_coherent(&(mdd_gadget(mdd)->dev), + mdd->tdata.buffer_len, + &mdd->tdata.buffer_dma, GFP_KERNEL); + if (!mdd->tdata.buffer) { + mdd_err(mdd, "Cannot allocate DMA memory\n"); + goto err_l_buf; + } + } else { + mdd->tdata.buffer = NULL; + mdd->tdata.buffer_dma = 0; + mdd->tdata.buffer_len = 0; + } + return 0; +err_l_buf: + kfree(mdd->tdata.sg_proc); + mdd->tdata.sg_proc = NULL; +err_sg_proc: + kfree(mdd->tdata.sg_raw); + mdd->tdata.sg_raw = NULL; +err_sg_raw: + ret = -ENOMEM; +err: + return ret; +} + +static void mdd_reset_transfer_data(struct msu_dvc_dev *mdd) +{ + MDD_F_DEBUG(); + kfree(mdd->tdata.sg_proc); + mdd->tdata.sg_proc = NULL; + kfree(mdd->tdata.sg_raw); + mdd->tdata.sg_raw = NULL; + if (mdd->tdata.buffer) { + dma_free_coherent(&(mdd_gadget(mdd)->dev), + mdd->tdata.buffer_len, mdd->tdata.buffer, + mdd->tdata.buffer_dma); + mdd->tdata.buffer = NULL; + mdd->tdata.buffer_dma = 0; + mdd->tdata.buffer_len = 0; + } +} + +static unsigned mdd_sg_len(struct scatterlist *sgl, int nents) +{ + int i; + struct scatterlist *sg; + unsigned ret = 0; + + /*MDD_F_DEBUG(); */ + for_each_sg(sgl, sg, nents, i) { + ret += sg->length; + } + return ret; +} + +static int mdd_send_sg(struct msu_dvc_dev *mdd, int nents) +{ + /*MDD_F_DEBUG(); */ + if (nents == 1) { + mdd->req->buf = sg_virt(mdd->tdata.sg_trans); + mdd->req->length = mdd->tdata.sg_trans->length; + mdd->req->dma = 0; + mdd->req->sg = NULL; + mdd->req->num_sgs = 0; + } else { + mdd->req->buf = NULL; + mdd->req->length = mdd_sg_len(mdd->tdata.sg_trans, nents); + mdd->req->dma = 0; + mdd->req->sg = mdd->tdata.sg_trans; + mdd->req->num_sgs = nents; + } + + mdd->req->context = mdd; + mdd->req->complete = mdd_complete; + mdd->req->zero = 1; + + if (usb_ep_queue(mdd->ep, mdd->req, GFP_KERNEL)) { + mdd_err(mdd, "Cannot queue request\n"); + dvct_set_status(mdd->dtc_status, DVCT_MASK_ERR); + return -EINVAL; + } + return 0; +} + +static int mdd_send_buffer(struct msu_dvc_dev *mdd, int nents) +{ + size_t transfer_len; + + /*MDD_F_DEBUG(); */ + transfer_len = + sg_copy_to_buffer(mdd->tdata.sg_trans, nents, mdd->tdata.buffer, + mdd->tdata.buffer_len); + if (!transfer_len) { + mdd_err(mdd, "Cannot copy into nonsg memory\n"); + return -EINVAL; + } + mdd->req->buf = mdd->tdata.buffer; + mdd->req->length = transfer_len; + mdd->req->dma = mdd->tdata.buffer_dma; + mdd->req->sg = NULL; + mdd->req->num_sgs = 0; + + mdd->req->context = mdd; + mdd->req->complete = mdd_complete; + mdd->req->zero = 1; + + if (usb_ep_queue(mdd->ep, mdd->req, GFP_KERNEL)) { + mdd_err(mdd, "Cannot queue request\n"); + dvct_set_status(mdd->dtc_status, DVCT_MASK_ERR); + return -EINVAL; + } + return 0; +} + +static int mdd_send_auto(struct msu_dvc_dev *mdd, int nents) +{ + /*MDD_F_DEBUG(); */ + if (!mdd_gadget(mdd)->sg_supported) + return mdd_send_buffer(mdd, nents); + else + return mdd_send_sg(mdd, nents); +} + +static int (*send_funcs[])(struct msu_dvc_dev *, int) = { + [MDD_TRANSFER_AUTO] = mdd_send_auto, + [MDD_TRANSFER_SINGLE] = mdd_send_buffer, + [MDD_TRANSFER_MULTI] = mdd_send_sg, +}; + +#ifdef MDD_DEBUG +static int mdd_proc_add_stats(struct msu_dvc_dev *mdd, int nents) +{ + int i, count; + struct scatterlist *sg; + + /*MDD_F_DEBUG(); */ + for_each_sg(mdd->tdata.sg_raw, sg, nents, i) { + count = msc_data_sz((struct msc_block_desc *)sg_virt(sg)); + mdd->stats.full_block_size += sg->length; + mdd->stats.valid_block_size += (count + MSC_BDESC); + mdd->stats.valid_data_size += count; + } + return i; +} +#else +#define mdd_proc_add_stats(m, n) do {} while (0) +#endif + +static int mdd_proc_trimmed_blocks(struct msu_dvc_dev *mdd, int nents) +{ + u8 *ptr; + size_t len; + int i, out_cnt = 0; + struct scatterlist *sg, *sg_dest = NULL; + + /*MDD_F_DEBUG(); */ + mdd_proc_add_stats(mdd, nents); + + sg_init_table(mdd->tdata.sg_proc, nents); + + for_each_sg(mdd->tdata.sg_raw, sg, nents, i) { + ptr = sg_virt(sg); + len = msc_data_sz((struct msc_block_desc *)ptr) + MSC_BDESC; + if (!len) { + mdd_err(mdd, "Zero length block"); + } else { + if (!sg_dest) + sg_dest = mdd->tdata.sg_proc; + else + sg_dest = sg_next(sg_dest); + sg_set_buf(sg_dest, ptr, len); + out_cnt++; + } + } + sg_mark_end(sg_dest); + return out_cnt; +} + +static int mdd_proc_stp_only(struct msu_dvc_dev *mdd, int nents) +{ + u8 *ptr; + size_t len; + int i, out_cnt = 0; + struct scatterlist *sg, *sg_dest = NULL; + + /*MDD_F_DEBUG(); */ + mdd_proc_add_stats(mdd, nents); + + sg_init_table(mdd->tdata.sg_proc, nents); + + for_each_sg(mdd->tdata.sg_raw, sg, nents, i) { + ptr = sg_virt(sg); + len = msc_data_sz((struct msc_block_desc *)ptr); + ptr += MSC_BDESC; + if (!len) { + mdd_err(mdd, "Zero data length block"); + } else { + if (!sg_dest) + sg_dest = mdd->tdata.sg_proc; + else + sg_dest = sg_next(sg_dest); + sg_set_buf(sg_dest, ptr, len); + out_cnt++; + } + } + sg_mark_end(sg_dest); + return out_cnt; +} + +static int (*proc_funcs[]) (struct msu_dvc_dev *, int) = { +#ifdef MDD_DEBUG + [MDD_PROC_NONE] = mdd_proc_add_stats, +#endif + [MDD_PROC_REM_TAIL] = mdd_proc_trimmed_blocks, + [MDD_PROC_REM_ALL] = mdd_proc_stp_only, +}; + +static void mdd_work(struct work_struct *work) +{ + int nents, current_bytes, retry_cnt = 0; + struct msu_dvc_dev *mdd; + + MDD_F_DEBUG(); + mdd = container_of(work, struct msu_dvc_dev, work); + init_stats_start(mdd); + + if (mdd_setup_transfer_data(mdd)) { + mdd_err(mdd, "Cannot setup transfer data\n"); + return; + } + mdd_info(mdd, "Start transfer loop\n"); + while (atomic_read(mdd->dtc_status) == DVCT_MASK_ONLINE_TRANS) { + sg_init_table(mdd->tdata.sg_raw, mdd->tdata.block_count); + /* Maybe will be better if msc_sg_oldest_win changes the window + * if the "oldest" one contains data and is the current one..*/ + + current_bytes = msc_current_win_bytes(mdd->th_dev); + if (current_bytes > mdd->min_transfer || + (current_bytes && retry_cnt >= mdd->max_retry_count)) { + msc_switch_window(mdd->th_dev); + nents = msc_sg_oldest_win(mdd->th_dev, + mdd->tdata.sg_raw); + retry_cnt = 0; + } else { + if (unlikely(current_bytes < 0)) { + mdd_warn(mdd, "Unexpected state (%d), switch", + current_bytes); + msc_switch_window(mdd->th_dev); + } else { + if (retry_cnt < mdd->max_retry_count) + retry_cnt++; + } + nents = 0; + } + stats_loop(mdd); + + if (nents < 0) { + mdd_err(mdd, "Cannot get ith data\n"); + dvct_set_status(mdd->dtc_status, DVCT_MASK_ERR); + break; + } + + if (nents && proc_funcs[mdd->process_type]) { + nents = proc_funcs[mdd->process_type] (mdd, nents); + if (nents < 0) { + mdd_err(mdd, "Cannot process data\n"); + dvct_set_status(mdd->dtc_status, DVCT_MASK_ERR); + break; + } + } + + if (nents) { + + stats_hit(mdd); + mdd_lock_transfer(mdd); + atomic_set(&mdd->req_ongoing, 1); + if (send_funcs[mdd->transfer_type] (mdd, nents)) { + mdd_unlock_transfer(mdd); + break; + } + + mdd_unlock_transfer(mdd); + + /*wait for done stop or disable */ + wait_event(mdd->wq, + (!atomic_read(&mdd->req_ongoing) || + (atomic_read(mdd->dtc_status) != + DVCT_MASK_ONLINE_TRANS))); + } else { + /*wait for stop or timeout */ + wait_event_timeout(mdd->wq, + (atomic_read(mdd->dtc_status) != + DVCT_MASK_ONLINE_TRANS), + msecs_to_jiffies(mdd-> + retry_timeout)); + } + } + mdd_info(mdd, "End transfer loop\n"); + if (atomic_read(&mdd->req_ongoing)) { + usb_ep_dequeue(mdd->ep, mdd->req); + atomic_set(&mdd->req_ongoing, 0); + } + +#ifdef MDD_DEBUG + mdd->stats.work_end = jiffies; +#endif + mdd_reset_transfer_data(mdd); +} + +static int mdd_activate(struct dvct_source_device *client, atomic_t *status) +{ + struct msu_dvc_dev *mdd = dtc_to_mdd(client); + + MDD_F_DEBUG(); + + mdd->dtc_status = status; + + return 0; +} + +static int mdd_binded(struct dvct_source_device *client, struct usb_ep *ep, + struct usb_function *func) +{ + struct msu_dvc_dev *mdd = dtc_to_mdd(client); + + MDD_F_DEBUG(); + mdd->ep = ep; + mdd->func = func; + + mdd->req = usb_ep_alloc_request(mdd->ep, GFP_KERNEL); + if (!mdd->req) { + mdd_err(mdd, "Cannot allocate usb request\n"); + return -ENOMEM; + } + return 0; +} + +static void mdd_connected(struct dvct_source_device *client, + enum usb_device_speed speed) +{ + struct msu_dvc_dev *mdd = dtc_to_mdd(client); + + MDD_F_DEBUG(); + mdd->speed = speed; +} + +union mdd_config { + u8 config; + struct { + u8 enable:1; /* one */ + u8 tr_type:2; + u8 proc_type:2; + } params; +}; + +static int mdd_start_transfer(struct dvct_source_device *client, u8 config) +{ + struct msu_dvc_dev *mdd = dtc_to_mdd(client); + union mdd_config cfg; + + MDD_F_DEBUG(); + /* If we share the resources with node base reading this is + * the place where we should lock.*/ + + cfg.config = config; + + if (cfg.params.proc_type <= MDD_PROC_MAX + && cfg.params.proc_type >= MDD_PROC_MIN) { + mdd_info(mdd, "Set process type %d", cfg.params.proc_type); + mdd->process_type = cfg.params.proc_type; + } + + if (cfg.params.tr_type <= MDD_TRANSFER_MAX + && cfg.params.tr_type >= MDD_TRANSFER_MIN) { + mdd_info(mdd, "Set transfer type %d", cfg.params.tr_type); + mdd->transfer_type = cfg.params.tr_type; + } + + /*Force linear buffer transfer if the gadget is not supporting SGs */ + if (mdd->transfer_type != MDD_TRANSFER_SINGLE + && !mdd_gadget(mdd)->sg_supported) { + mdd_info(mdd, "Force linear buffer transfer"); + mdd->transfer_type = MDD_TRANSFER_SINGLE; + } + + dvct_clr_status(mdd->dtc_status, DVCT_MASK_ERR); + dvct_set_status(mdd->dtc_status, DVCT_MASK_TRANS); + queue_work(mdd->wrq, &mdd->work); + return 0; +} + +static int mdd_stop_transfer(struct dvct_source_device *client) +{ + struct msu_dvc_dev *mdd = dtc_to_mdd(client); + + MDD_F_DEBUG(); + dvct_clr_status(mdd->dtc_status, DVCT_MASK_TRANS); + wake_up(&mdd->wq); + + return 0; +} + +static void mdd_disconnected(struct dvct_source_device *client) +{ + struct msu_dvc_dev *mdd = dtc_to_mdd(client); + + MDD_F_DEBUG(); + mdd->speed = USB_SPEED_UNKNOWN; +} + +static void mdd_unbinded(struct dvct_source_device *client) +{ + struct msu_dvc_dev *mdd = dtc_to_mdd(client); + + MDD_F_DEBUG(); + + if (mdd->req) { + usb_ep_free_request(mdd->ep, mdd->req); + mdd->req = NULL; + } + mdd->ep = NULL; +} + +static void mdd_deactivate(struct dvct_source_device *client) +{ + struct msu_dvc_dev *mdd = dtc_to_mdd(client); + + MDD_F_DEBUG(); + mdd->th_dev = NULL; + mdd->dtc_status = NULL; +} + +/*the driver*/ +static struct dvct_source_driver mdd_drv = { + .activate = mdd_activate, + .binded = mdd_binded, + .connected = mdd_connected, + .start_transfer = mdd_start_transfer, + .stop_transfer = mdd_stop_transfer, + .disconnected = mdd_disconnected, + .unbinded = mdd_unbinded, + .deactivate = mdd_deactivate, + .driver.name = DTC_DRV_NAME, +}; + +static struct msu_dvc_dev *mdd_alloc_device(const char *name) +{ + struct msu_dvc_dev *mdd; + + mdd = kzalloc(sizeof(*mdd), GFP_KERNEL); + + if (!mdd) + return ERR_PTR(-ENOMEM); + + mdd->ddev.name_add = kstrdup(name, GFP_KERNEL); + if (!mdd->ddev.name_add) + return ERR_PTR(-ENOMEM); + + /* mdd->ddev.protocol = 0; */ + /* mdd->ddev.desc = NULL; */ + /* mdd->dtc_status = NULL; */ + /* mdd->ep = NULL; */ + mdd->speed = USB_SPEED_UNKNOWN; + /* mdd->msu_dev = NULL; */ + /* mdd->wrq = NULL; */ + mdd->retry_timeout = MDD_RETRY_TIMEOUT_DEF; + mdd->max_retry_count = MDD_MAX_RETRY_CNT_DEF; + /* mdd->req = NULL; */ + atomic_set(&mdd->req_ongoing, 0); + /*mdd->tdata is all NULL */ + mdd->transfer_type = MDD_TRANSFER_AUTO; + mdd->process_type = MDD_PROC_REM_ALL; + mdd->min_transfer = MDD_MIN_TRANSFER_DEF; + + spin_lock_init(&mdd->tdata.lock); + + return mdd; +}; + +static void mdd_free_device(struct msu_dvc_dev *mdd) +{ + kfree(mdd->ddev.name_add); + kfree(mdd); +}; + +static struct attribute *mdd_attrs[] = { + &dev_attr_mdd_min_transfer.attr, + &dev_attr_mdd_retry_timeout.attr, + &dev_attr_mdd_max_retry.attr, + &dev_attr_mdd_transfer_type.attr, + &dev_attr_mdd_proc_type.attr, +#ifdef MDD_DEBUG + &dev_attr_mdd_stats.attr, +#endif + NULL, +}; + +static struct attribute_group mdd_attrs_group = { + .attrs = mdd_attrs, +}; + +void mdd_msc_probe(struct intel_th_device *thdev) +{ + int ret; + struct msu_dvc_dev *mdd; + struct device *dev; + + pr_info("New th-msc device %s", dev_name(&thdev->dev)); + mdd = mdd_alloc_device(dev_name(&thdev->dev)); + + if (IS_ERR_OR_NULL(mdd)) { + pr_err("Cannot allocate device %s (%ld)", dev_name(&thdev->dev), + PTR_ERR(mdd)); + return; + } + + ret = dvct_source_device_add(&mdd->ddev, &mdd_drv); + if (ret) { + pr_err("Cannot register dvc device %d", ret); + mdd_free_device(mdd); + return; + } + + mdd->th_dev = thdev; + dev = &mdd->ddev.device; + + mdd->wrq = alloc_workqueue("%s_workqueue", WQ_MEM_RECLAIM | WQ_HIGHPRI, + 1, dev_name(&mdd->ddev.device)); + if (!mdd->wrq) { + mdd_err(mdd, "Cannot allocate work queue\n"); + mdd_free_device(mdd); + return; + } + + INIT_WORK(&mdd->work, mdd_work); + + init_waitqueue_head(&mdd->wq); + + /*Attributes */ + ret = sysfs_create_group(&dev->kobj, &mdd_attrs_group); + if (ret) + mdd_warn(mdd, "Cannot add attribute group %d\n", ret); + + ret = sysfs_create_link(&dev->kobj, &thdev->dev.kobj, "msc"); + if (ret) + mdd_warn(mdd, "Cannot add msc link %d\n", ret); + + spin_lock(&mdd_devs_lock); + list_add(&mdd->mdd_list, &mdd_devs); + spin_unlock(&mdd_devs_lock); +} + +void mdd_msc_remove(struct intel_th_device *thdev) +{ + struct msu_dvc_dev *mdd = NULL; + struct msu_dvc_dev *mdd_iter = NULL; + + spin_lock(&mdd_devs_lock); + list_for_each_entry(mdd_iter, &mdd_devs, mdd_list) { + if (mdd_iter->th_dev == thdev) + mdd = mdd_iter; + } + + if (!mdd) { + pr_err("No such mdd device, %s", dev_name(&thdev->dev)); + spin_unlock(&mdd_devs_lock); + return; + } + list_del(&mdd->mdd_list); + + spin_unlock(&mdd_devs_lock); + + flush_workqueue(mdd->wrq); + destroy_workqueue(mdd->wrq); + + sysfs_remove_group(&mdd->ddev.device.kobj, &mdd_attrs_group); + sysfs_remove_link(&mdd->ddev.device.kobj, "msc"); + + mdd->wrq = NULL; + + dvct_source_device_del(&mdd->ddev); + mdd_free_device(mdd); +} + +struct msc_probe_rem_cb mdd_msc_cbs = { + .probe = mdd_msc_probe, + .remove = mdd_msc_remove, +}; + +static int __init msu_dvc_init(void) +{ + int ret; + + MDD_F_DEBUG(); + ret = dvct_source_driver_register(&mdd_drv); + if (ret) { + pr_err("Cannot register dvc driver %d", ret); + return ret; + } + + msc_register_callbacks(mdd_msc_cbs); + return 0; +} + +static void __exit msu_dvc_exit(void) +{ + MDD_F_DEBUG(); + msc_unregister_callbacks(); + dvct_source_driver_unregister(&mdd_drv); +} + +module_init(msu_dvc_init); +module_exit(msu_dvc_exit); + +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Traian Schiau "); From 9ce7ea2d1735bb9c4424bf6b4e74fa1d79945928 Mon Sep 17 00:00:00 2001 From: Traian Schiau Date: Mon, 5 Oct 2015 12:37:02 +0300 Subject: [PATCH 0558/1103] intel_th: Fix msc probe/remove locking. [ 15.493015] BUG: sleeping function called from invalid .. Is genetared while msu_dvc_init/msc_register_callbacks because msc_register_callbacks is protected by spinlock and the provided callbacks (called for preexisting msu devices) might sleep. Change the protecting spinlock to mutex. Signed-off-by: Traian Schiau --- drivers/hwtracing/intel_th/msu.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/drivers/hwtracing/intel_th/msu.c b/drivers/hwtracing/intel_th/msu.c index fa149469d3b9..c179e7558c5b 100644 --- a/drivers/hwtracing/intel_th/msu.c +++ b/drivers/hwtracing/intel_th/msu.c @@ -142,7 +142,7 @@ struct msc_device_instance { }; static LIST_HEAD(msc_dev_instances); -static DEFINE_SPINLOCK(msc_dev_reg_lock); +static DEFINE_MUTEX(msc_dev_reg_lock); /** * msc_register_callbacks() * @cbs @@ -151,7 +151,7 @@ int msc_register_callbacks(struct msc_probe_rem_cb cbs) { struct msc_device_instance *it; - spin_lock(&msc_dev_reg_lock); + mutex_lock(&msc_dev_reg_lock); msc_probe_rem_cb.probe = cbs.probe; msc_probe_rem_cb.remove = cbs.remove; @@ -160,7 +160,7 @@ int msc_register_callbacks(struct msc_probe_rem_cb cbs) cbs.probe(it->thdev); } - spin_unlock(&msc_dev_reg_lock); + mutex_unlock(&msc_dev_reg_lock); return 0; } EXPORT_SYMBOL_GPL(msc_register_callbacks); @@ -170,12 +170,12 @@ EXPORT_SYMBOL_GPL(msc_register_callbacks); */ void msc_unregister_callbacks(void) { - spin_lock(&msc_dev_reg_lock); + mutex_lock(&msc_dev_reg_lock); msc_probe_rem_cb.probe = NULL; msc_probe_rem_cb.remove = NULL; - spin_unlock(&msc_dev_reg_lock); + mutex_unlock(&msc_dev_reg_lock); } EXPORT_SYMBOL_GPL(msc_unregister_callbacks); @@ -187,7 +187,7 @@ static void msc_add_instance(struct intel_th_device *thdev) if (!instance) return; - spin_lock(&msc_dev_reg_lock); + mutex_lock(&msc_dev_reg_lock); instance->thdev = thdev; list_add(&instance->list, &msc_dev_instances); @@ -195,14 +195,14 @@ static void msc_add_instance(struct intel_th_device *thdev) if (msc_probe_rem_cb.probe) msc_probe_rem_cb.probe(thdev); - spin_unlock(&msc_dev_reg_lock); + mutex_unlock(&msc_dev_reg_lock); } static void msc_rm_instance(struct intel_th_device *thdev) { struct msc_device_instance *instance = NULL, *it; - spin_lock(&msc_dev_reg_lock); + mutex_lock(&msc_dev_reg_lock); if (msc_probe_rem_cb.remove) msc_probe_rem_cb.remove(thdev); @@ -221,7 +221,7 @@ static void msc_rm_instance(struct intel_th_device *thdev) pr_warn("msu: cannot remove %p (not found)", thdev); } - spin_unlock(&msc_dev_reg_lock); + mutex_unlock(&msc_dev_reg_lock); } static inline bool msc_block_is_empty(struct msc_block_desc *bdesc) From 7f9c195e071c20959b557fb5277ff2f9f18ef12e Mon Sep 17 00:00:00 2001 From: Traian Schiau Date: Tue, 3 Nov 2015 12:50:55 +0200 Subject: [PATCH 0559/1103] intel_th: Use saved nwsa only when msc is disabled Use the saved nwsa only when msc is disabled, otherwise use the value provided by MSCxNWSA register. The nswa (next window start address) backup was introduced to have a valid starting window for post capture dumping in case of in memory tracing, but a regresion was introduced for "online" memory tracing (eg. dvc). Signed-off-by: Traian Schiau --- drivers/hwtracing/intel_th/msu.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/drivers/hwtracing/intel_th/msu.c b/drivers/hwtracing/intel_th/msu.c index c179e7558c5b..70d41a0e2e0a 100644 --- a/drivers/hwtracing/intel_th/msu.c +++ b/drivers/hwtracing/intel_th/msu.c @@ -281,17 +281,25 @@ static struct msc_window *msc_oldest_window(struct msc *msc) { struct msc_window *win; unsigned int found = 0; + unsigned long nwsa; if (list_empty(&msc->win_list)) return NULL; + if (msc->enabled) { + u32 reg = ioread32(msc->reg_base + REG_MSU_MSC0NWSA); + + nwsa = (unsigned long)reg << PAGE_SHIFT; + } else { + nwsa = msc->nwsa; + } /* * we might need a radix tree for this, depending on how * many windows a typical user would allocate; ideally it's * something like 2, in which case we're good */ list_for_each_entry(win, &msc->win_list, entry) { - if (win->block[0].addr == msc->nwsa) + if (win->block[0].addr == nwsa) found++; /* skip the empty ones */ From 3b926dfd0b214cac0e585f58230fc69ad6721c42 Mon Sep 17 00:00:00 2001 From: Traian Schiau Date: Wed, 25 Nov 2015 13:47:01 +0200 Subject: [PATCH 0560/1103] intel_th: Keep switch window as short as possible Stop/start GTH with irq's disabled. Since dvc part runs in a kernel thread, disable preemption for the time switch_window is called. Signed-off-by: Traian Schiau --- drivers/hwtracing/intel_th/gth.c | 4 +++- drivers/hwtracing/intel_th/msu-dvc.c | 2 ++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/hwtracing/intel_th/gth.c b/drivers/hwtracing/intel_th/gth.c index e95301f91627..f697528e4d1c 100644 --- a/drivers/hwtracing/intel_th/gth.c +++ b/drivers/hwtracing/intel_th/gth.c @@ -624,7 +624,7 @@ static void intel_th_gth_switch(struct intel_th_device *thdev, struct intel_th_output *output) { struct gth_device *gth = dev_get_drvdata(&thdev->dev); - unsigned long count; + unsigned long count, flags; u32 reg; /* trigger */ @@ -639,8 +639,10 @@ static void intel_th_gth_switch(struct intel_th_device *thdev, if (!count) dev_dbg(&thdev->dev, "timeout waiting for CTS Trigger\n"); + local_irq_save(flags); intel_th_gth_stop(gth, output, false); intel_th_gth_start(gth, output); + local_irq_restore(flags); } /** diff --git a/drivers/hwtracing/intel_th/msu-dvc.c b/drivers/hwtracing/intel_th/msu-dvc.c index 475f4cfe51ba..5ed280dfd36c 100644 --- a/drivers/hwtracing/intel_th/msu-dvc.c +++ b/drivers/hwtracing/intel_th/msu-dvc.c @@ -702,6 +702,7 @@ static void mdd_work(struct work_struct *work) /* Maybe will be better if msc_sg_oldest_win changes the window * if the "oldest" one contains data and is the current one..*/ + preempt_disable(); current_bytes = msc_current_win_bytes(mdd->th_dev); if (current_bytes > mdd->min_transfer || (current_bytes && retry_cnt >= mdd->max_retry_count)) { @@ -720,6 +721,7 @@ static void mdd_work(struct work_struct *work) } nents = 0; } + preempt_enable(); stats_loop(mdd); if (nents < 0) { From 55d0b7b0e10aebe77ac9270da79b721136f0a319 Mon Sep 17 00:00:00 2001 From: Traian Schiau Date: Thu, 3 Dec 2015 16:16:36 +0200 Subject: [PATCH 0561/1103] intel_th-dvc: Limit the usb TRB count. DWC3 gadget implementation is limited to accept only 32 TRBs per endpoint, if this number is exceeded in the middle of a request, the extra data is dropped without warning. Limit the maximum number of sg-s that are submitted in a single request. Signed-off-by: Traian Schiau --- drivers/hwtracing/intel_th/msu-dvc.c | 97 ++++++++++++++++++---------- 1 file changed, 63 insertions(+), 34 deletions(-) diff --git a/drivers/hwtracing/intel_th/msu-dvc.c b/drivers/hwtracing/intel_th/msu-dvc.c index 5ed280dfd36c..db8d877e6ec1 100644 --- a/drivers/hwtracing/intel_th/msu-dvc.c +++ b/drivers/hwtracing/intel_th/msu-dvc.c @@ -39,6 +39,11 @@ #define MDD_RETRY_TIMEOUT_DEF 2 #define MDD_MAX_RETRY_CNT_DEF 150 +/* The DWC3 gadget is able to handle a maximum of 32 TRBs per-ep (an sg based + * request counts as the number of sg-s). + * This should be updated in case some other UDC has a lower threshold. */ +#define MDD_MAX_TRB_CNT 32 + #define mdd_err(mdd, ...) dev_err(&(mdd)->ddev.device, ## __VA_ARGS__) #define mdd_warn(mdd, ...) dev_warn(&(mdd)->ddev.device, ## __VA_ARGS__) #define mdd_info(mdd, ...) dev_info(&(mdd)->ddev.device, ## __VA_ARGS__) @@ -523,29 +528,55 @@ static unsigned mdd_sg_len(struct scatterlist *sgl, int nents) static int mdd_send_sg(struct msu_dvc_dev *mdd, int nents) { + struct scatterlist *sgl = mdd->tdata.sg_trans; + /*MDD_F_DEBUG(); */ - if (nents == 1) { - mdd->req->buf = sg_virt(mdd->tdata.sg_trans); - mdd->req->length = mdd->tdata.sg_trans->length; - mdd->req->dma = 0; - mdd->req->sg = NULL; - mdd->req->num_sgs = 0; - } else { - mdd->req->buf = NULL; - mdd->req->length = mdd_sg_len(mdd->tdata.sg_trans, nents); - mdd->req->dma = 0; - mdd->req->sg = mdd->tdata.sg_trans; - mdd->req->num_sgs = nents; - } + while (nents) { + int trans_ents; - mdd->req->context = mdd; - mdd->req->complete = mdd_complete; - mdd->req->zero = 1; + mdd_lock_transfer(mdd); - if (usb_ep_queue(mdd->ep, mdd->req, GFP_KERNEL)) { - mdd_err(mdd, "Cannot queue request\n"); - dvct_set_status(mdd->dtc_status, DVCT_MASK_ERR); - return -EINVAL; + if (nents > MDD_MAX_TRB_CNT) { + trans_ents = MDD_MAX_TRB_CNT; + sg_mark_end(&sgl[trans_ents - 1]); + } else { + trans_ents = nents; + } + + if (trans_ents == 1) { + mdd->req->buf = sg_virt(sgl); + mdd->req->length = sgl->length; + mdd->req->dma = 0; + mdd->req->sg = NULL; + mdd->req->num_sgs = 0; + } else { + mdd->req->buf = NULL; + mdd->req->length = mdd_sg_len(sgl, trans_ents); + mdd->req->dma = 0; + mdd->req->sg = sgl; + mdd->req->num_sgs = trans_ents; + } + + mdd->req->context = mdd; + mdd->req->complete = mdd_complete; + mdd->req->zero = 1; + + if (usb_ep_queue(mdd->ep, mdd->req, GFP_KERNEL)) { + mdd_err(mdd, "Cannot queue request\n"); + dvct_set_status(mdd->dtc_status, DVCT_MASK_ERR); + mdd_unlock_transfer(mdd); + return -EINVAL; + } + + atomic_set(&mdd->req_ongoing, 1); + nents -= trans_ents; + sgl += trans_ents; + + mdd_unlock_transfer(mdd); + /*wait for done stop or disable */ + wait_event(mdd->wq, (!atomic_read(&mdd->req_ongoing) || + (atomic_read(mdd->dtc_status) != + DVCT_MASK_ONLINE_TRANS))); } return 0; } @@ -555,11 +586,13 @@ static int mdd_send_buffer(struct msu_dvc_dev *mdd, int nents) size_t transfer_len; /*MDD_F_DEBUG(); */ + mdd_lock_transfer(mdd); transfer_len = sg_copy_to_buffer(mdd->tdata.sg_trans, nents, mdd->tdata.buffer, mdd->tdata.buffer_len); if (!transfer_len) { mdd_err(mdd, "Cannot copy into nonsg memory\n"); + mdd_unlock_transfer(mdd); return -EINVAL; } mdd->req->buf = mdd->tdata.buffer; @@ -575,8 +608,16 @@ static int mdd_send_buffer(struct msu_dvc_dev *mdd, int nents) if (usb_ep_queue(mdd->ep, mdd->req, GFP_KERNEL)) { mdd_err(mdd, "Cannot queue request\n"); dvct_set_status(mdd->dtc_status, DVCT_MASK_ERR); + mdd_unlock_transfer(mdd); return -EINVAL; } + + atomic_set(&mdd->req_ongoing, 1); + mdd_unlock_transfer(mdd); + /*wait for done stop or disable */ + wait_event(mdd->wq, (!atomic_read(&mdd->req_ongoing) || + (atomic_read(mdd->dtc_status) != + DVCT_MASK_ONLINE_TRANS))); return 0; } @@ -608,6 +649,7 @@ static int mdd_proc_add_stats(struct msu_dvc_dev *mdd, int nents) mdd->stats.valid_block_size += (count + MSC_BDESC); mdd->stats.valid_data_size += count; } + return i; } #else @@ -740,22 +782,9 @@ static void mdd_work(struct work_struct *work) } if (nents) { - stats_hit(mdd); - mdd_lock_transfer(mdd); - atomic_set(&mdd->req_ongoing, 1); - if (send_funcs[mdd->transfer_type] (mdd, nents)) { - mdd_unlock_transfer(mdd); + if (send_funcs[mdd->transfer_type] (mdd, nents)) break; - } - - mdd_unlock_transfer(mdd); - - /*wait for done stop or disable */ - wait_event(mdd->wq, - (!atomic_read(&mdd->req_ongoing) || - (atomic_read(mdd->dtc_status) != - DVCT_MASK_ONLINE_TRANS))); } else { /*wait for stop or timeout */ wait_event_timeout(mdd->wq, From 3e2fd91c8fb02bf0ee3d547488a0d3d6d01d1ab6 Mon Sep 17 00:00:00 2001 From: Traian Schiau Date: Thu, 7 Jan 2016 10:23:19 +0200 Subject: [PATCH 0562/1103] intel_th-dvc: Fix deactivation issue. mdd->th_dev should not be reset upon dvc-source device deactivation. Signed-off-by: Traian Schiau --- drivers/hwtracing/intel_th/msu-dvc.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/hwtracing/intel_th/msu-dvc.c b/drivers/hwtracing/intel_th/msu-dvc.c index db8d877e6ec1..b8c700125ef7 100644 --- a/drivers/hwtracing/intel_th/msu-dvc.c +++ b/drivers/hwtracing/intel_th/msu-dvc.c @@ -925,7 +925,6 @@ static void mdd_deactivate(struct dvct_source_device *client) struct msu_dvc_dev *mdd = dtc_to_mdd(client); MDD_F_DEBUG(); - mdd->th_dev = NULL; mdd->dtc_status = NULL; } From 98136c3de79d99f98afa6f7c03c75c48c15d3022 Mon Sep 17 00:00:00 2001 From: Traian Schiau Date: Wed, 11 May 2016 10:22:36 +0300 Subject: [PATCH 0563/1103] intel_th-dvc: Fix code analysis issues Fix some issues like possible NULL dereferencing, possible memory leaks and invalid conditions, flagged by the code analysis tools. Signed-off-by: Traian Schiau --- drivers/hwtracing/intel_th/msu-dvc.c | 35 +++++++++++++++++----------- 1 file changed, 22 insertions(+), 13 deletions(-) diff --git a/drivers/hwtracing/intel_th/msu-dvc.c b/drivers/hwtracing/intel_th/msu-dvc.c index b8c700125ef7..8e5b245e64a9 100644 --- a/drivers/hwtracing/intel_th/msu-dvc.c +++ b/drivers/hwtracing/intel_th/msu-dvc.c @@ -432,14 +432,14 @@ static int mdd_setup_transfer_data(struct msu_dvc_dev *mdd) } mdd->tdata.block_count = msc_max_blocks(mdd->th_dev); - if (mdd->tdata.block_count <= 0) { + if (mdd->tdata.block_count == 0) { mdd_err(mdd, "Invalid block count %zu\n", mdd->tdata.block_count); goto err; } mdd->tdata.block_size = msc_block_max_size(mdd->th_dev); - if (mdd->tdata.block_size <= 0) { + if (mdd->tdata.block_size == 0) { mdd_err(mdd, "Invalid block size %zu\n", mdd->tdata.block_size); goto err; } @@ -670,19 +670,24 @@ static int mdd_proc_trimmed_blocks(struct msu_dvc_dev *mdd, int nents) for_each_sg(mdd->tdata.sg_raw, sg, nents, i) { ptr = sg_virt(sg); - len = msc_data_sz((struct msc_block_desc *)ptr) + MSC_BDESC; + len = msc_data_sz((struct msc_block_desc *)ptr); if (!len) { mdd_err(mdd, "Zero length block"); - } else { - if (!sg_dest) - sg_dest = mdd->tdata.sg_proc; - else - sg_dest = sg_next(sg_dest); - sg_set_buf(sg_dest, ptr, len); - out_cnt++; + continue; } + + len += MSC_BDESC; + + if (!sg_dest) + sg_dest = mdd->tdata.sg_proc; + else + sg_dest = sg_next(sg_dest); + sg_set_buf(sg_dest, ptr, len); + out_cnt++; } - sg_mark_end(sg_dest); + if (sg_dest) + sg_mark_end(sg_dest); + return out_cnt; } @@ -713,7 +718,9 @@ static int mdd_proc_stp_only(struct msu_dvc_dev *mdd, int nents) out_cnt++; } } - sg_mark_end(sg_dest); + if (sg_dest) + sg_mark_end(sg_dest); + return out_cnt; } @@ -951,8 +958,10 @@ static struct msu_dvc_dev *mdd_alloc_device(const char *name) return ERR_PTR(-ENOMEM); mdd->ddev.name_add = kstrdup(name, GFP_KERNEL); - if (!mdd->ddev.name_add) + if (!mdd->ddev.name_add) { + kfree(mdd); return ERR_PTR(-ENOMEM); + } /* mdd->ddev.protocol = 0; */ /* mdd->ddev.desc = NULL; */ From 907fc2cac1ec6c667bebb56fe757251ee26f5dd0 Mon Sep 17 00:00:00 2001 From: Jeremy Rocher Date: Wed, 4 Jun 2014 16:50:19 +0200 Subject: [PATCH 0564/1103] platform: x86: add pstore RAM backend managed by BIOS BIOS manage a persisted RAM buffer across watchdog reset, this feature is named PRAM. Buffer is reserved by BIOS and exposed as PRAM ACPI table. PRAM BIOS feature is configurable through BIOS setup or PRAM_Conf EFI variable (GUID ecb54cd9-e5ae-4fdc-a971-e877756068f7). Accepted values for this variable are 0, 1, 2 and 3 as ASCII string; will configure PRAM feature respectively as Disabled, 4 MB, 16 MB and 64 MB. This device driver bind the PRAM buffer exposed in PRAM ACPI table to a pstore ram (ramoops) driver. It also exposes similar parameters as the ramoops dummy device driver (fs/pstore/ram.c) : record_size, console_size, ftrace_size, dump_oops, ecc. Signed-off-by: Jeremy Rocher Signed-off-by: Tian, Baofeng --- drivers/platform/x86/Kconfig | 17 +++ drivers/platform/x86/Makefile | 1 + drivers/platform/x86/intel_pstore_pram.c | 135 +++++++++++++++++++++++ 3 files changed, 153 insertions(+) create mode 100644 drivers/platform/x86/intel_pstore_pram.c diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig index 0c1aa6c314f5..4d0dfd030b07 100644 --- a/drivers/platform/x86/Kconfig +++ b/drivers/platform/x86/Kconfig @@ -1229,6 +1229,23 @@ config I2C_MULTI_INSTANTIATE To compile this driver as a module, choose M here: the module will be called i2c-multi-instantiate. +config INTEL_PSTORE_PRAM + tristate "Intel pstore RAM backend driver (PRAM BIOS feature)" + depends on ACPI + depends on PSTORE_RAM + ---help--- + This driver provides RAM backend for pstore, managed by BIOS + as PRAM (Persisted RAM buffer) debug feature. + + PRAM BIOS feature is configurable through BIOS setup or PRAM_Conf + EFI variable (GUID ecb54cd9-e5ae-4fdc-a971-e877756068f7). + Accepted values for variable are 0, 1, 2 and 3 as ASCII + string; will configure PRAM feature respectively as + Disabled, 4 MB, 16 MB and 64 MB. + + Safe to say Y, will not bind if your BIOS doesn't support + this feature. + endif # X86_PLATFORM_DEVICES config PMC_ATOM diff --git a/drivers/platform/x86/Makefile b/drivers/platform/x86/Makefile index e6d1becf81ce..91fda053d781 100644 --- a/drivers/platform/x86/Makefile +++ b/drivers/platform/x86/Makefile @@ -92,3 +92,4 @@ obj-$(CONFIG_MLX_PLATFORM) += mlx-platform.o obj-$(CONFIG_INTEL_TURBO_MAX_3) += intel_turbo_max_3.o obj-$(CONFIG_INTEL_CHTDC_TI_PWRBTN) += intel_chtdc_ti_pwrbtn.o obj-$(CONFIG_I2C_MULTI_INSTANTIATE) += i2c-multi-instantiate.o +obj-$(CONFIG_INTEL_PSTORE_PRAM) += intel_pstore_pram.o diff --git a/drivers/platform/x86/intel_pstore_pram.c b/drivers/platform/x86/intel_pstore_pram.c new file mode 100644 index 000000000000..2d5205bfd204 --- /dev/null +++ b/drivers/platform/x86/intel_pstore_pram.c @@ -0,0 +1,135 @@ +/* + * Copyright (c) 2015, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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. + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include + +#define SZ_4K 0x00001000 +#define SZ_2M 0x00200000 + +/* PRAM stands for 'Persisted RAM' from BIOS point of view */ +#define ACPI_SIG_PRAM "PRAM" + +/* + * Following parameters match to those defined in fs/pstore/ram.c in + * order to keep campatibility between driver intefaces, please refer + * to it for implementaion details. + */ +static ulong pram_record_size = SZ_4K; +module_param_named(record_size, pram_record_size, ulong, 0400); +MODULE_PARM_DESC(record_size, "size of each dump done on oops/panic"); + +static ulong pram_console_size = SZ_2M; +module_param_named(console_size, pram_console_size, ulong, 0400); +MODULE_PARM_DESC(console_size, "size of kernel console log"); + +static ulong pram_ftrace_size = 2*SZ_4K; +module_param_named(ftrace_size, pram_ftrace_size, ulong, 0400); +MODULE_PARM_DESC(ftrace_size, "size of ftrace log"); + +static int pram_dump_oops = 1; +module_param_named(dump_oops, pram_dump_oops, int, 0600); +MODULE_PARM_DESC(dump_oops, + "set to 1 to dump oopses, 0 to only dump panics (default 1)"); + +static int pram_ecc; +module_param_named(ecc, pram_ecc, int, 0600); +MODULE_PARM_DESC(ecc, + "if non-zero, the option enables SW ECC support, provided by" + "fs/pstore/ram_core.c, and specifies ECC buffer size in bytes" + "(1 is a special value, means 16 bytes ECC)"); + +static struct ramoops_platform_data *pram_data; +static struct platform_device *pram_dev; + +struct acpi_table_pram { + struct acpi_table_header header; + u64 addr; + u32 size; +} __packed; + +static int register_pram_dev(unsigned long mem_address, + unsigned long mem_size) +{ + pram_data = kzalloc(sizeof(*pram_data), GFP_KERNEL); + if (!pram_data) { + pr_err("could not allocate pram_data\n"); + return -ENOMEM; + } + + pram_data->mem_address = mem_address; + pram_data->mem_size = mem_size; + pram_data->record_size = pram_record_size; + pram_data->console_size = pram_console_size; + pram_data->ftrace_size = pram_ftrace_size; + pram_data->dump_oops = pram_dump_oops; + /* + * For backwards compatibility with previous + * fs/pstore/ram_core.c implementation, + * intel_pstore_pram.ecc=1 means 16 bytes ECC. + */ + pram_data->ecc_info.ecc_size = pram_ecc == 1 ? 16 : pram_ecc; + + pram_dev = platform_device_register_data(NULL, "ramoops", -1, + pram_data, sizeof(struct ramoops_platform_data)); + if (IS_ERR(pram_dev)) { + pr_err("could not create platform device: %ld\n", + PTR_ERR(pram_dev)); + kfree(pram_data); + return PTR_ERR(pram_dev); + } + + pr_info("registered pram device, addr=0x%lx, size=0x%lx\n", + pram_data->mem_address, pram_data->mem_size); + + return 0; +} + +static int __init intel_pram_init(void) +{ + acpi_status status; + struct acpi_table_pram *pramt; + + status = acpi_get_table(ACPI_SIG_PRAM, 0, + (struct acpi_table_header **)&pramt); + if (status == AE_NOT_FOUND) { + pr_debug("PRAM table not found\n"); + return -ENODEV; + } else if (ACPI_FAILURE(status)) { + const char *msg = acpi_format_exception(status); + pr_err("Failed to get PRAM table: %s\n", msg); + return -EINVAL; + } + + if (!pramt->addr || !pramt->size) { + pr_debug("PRAM: bad address (0x%llx) or size (0x%lx)\n", + (unsigned long long)pramt->addr, + (unsigned long)pramt->size); + return -ENODEV; + } + + return register_pram_dev(pramt->addr, pramt->size); +} +device_initcall(intel_pram_init); + +static void __exit intel_pram_exit(void) +{ + platform_device_unregister(pram_dev); + kfree(pram_data); +} +module_exit(intel_pram_exit); From 50d8485841e74ba7418a645f0301dfe6d0ef5593 Mon Sep 17 00:00:00 2001 From: Jeremy Rocher Date: Mon, 18 Apr 2016 17:27:00 +0200 Subject: [PATCH 0565/1103] intel_pstore_pram: load module earlier Currently intel_pstore_pram is loaded as a normal module at device_initcall() but if a panic occurs before it is loaded, nothing is logged in pstore. Load module earlier, in postcore_initcall(), like ramoops. Signed-off-by: Jeremy Rocher --- drivers/platform/x86/intel_pstore_pram.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/platform/x86/intel_pstore_pram.c b/drivers/platform/x86/intel_pstore_pram.c index 2d5205bfd204..78dd9c3b08c5 100644 --- a/drivers/platform/x86/intel_pstore_pram.c +++ b/drivers/platform/x86/intel_pstore_pram.c @@ -125,7 +125,7 @@ static int __init intel_pram_init(void) return register_pram_dev(pramt->addr, pramt->size); } -device_initcall(intel_pram_init); +postcore_initcall(intel_pram_init); static void __exit intel_pram_exit(void) { From f4ec50b24eb6420456f55e150277f33b56acb075 Mon Sep 17 00:00:00 2001 From: "Tian, Baofeng" Date: Fri, 23 Jun 2017 03:36:20 +0800 Subject: [PATCH 0566/1103] change itco watchdog type for tristate to bool tristate will allow kernel config to M and build ko file this will cause build allmodconfig error as necessary symbols is not exported. change to bool only allow config ITCO_WDT to yes/no. Signed-off-by: Tian, Baofeng --- drivers/watchdog/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig index 5ea8909a41f9..8bf318d44a88 100644 --- a/drivers/watchdog/Kconfig +++ b/drivers/watchdog/Kconfig @@ -1059,7 +1059,7 @@ config INTEL_MID_WATCHDOG To compile this driver as a module, choose M here. config ITCO_WDT - tristate "Intel TCO Timer/Watchdog" + bool "Intel TCO Timer/Watchdog" depends on (X86 || IA64) && PCI select WATCHDOG_CORE depends on I2C || I2C=n From bea466a79a28fc6b4a6e47fcf662ab176be4d7f4 Mon Sep 17 00:00:00 2001 From: "Tian, Baofeng" Date: Wed, 23 Aug 2017 02:00:35 +0800 Subject: [PATCH 0567/1103] kernel: printk: move some definition out of config scope to avoid build error These definitions previously put in #ifdef CONFIG_PRINTK, this config is defined as default however, if you build code with allnoconfig, you will find this error, change it to out of #ifdef to avoid this error. Signed-off-by: Tian, Baofeng --- kernel/printk/printk.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/kernel/printk/printk.c b/kernel/printk/printk.c index 9bf5404397e0..578cad2f66a0 100644 --- a/kernel/printk/printk.c +++ b/kernel/printk/printk.c @@ -377,6 +377,11 @@ __packed __aligned(4) */ DEFINE_RAW_SPINLOCK(logbuf_lock); +/* Give the posibility to temporary disable slow (!CON_FAST) consoles */ +static atomic_t console_slow_suspended = ATOMIC_INIT(0); +/* Keep the number of slow suspend in check */ +#define MAX_SLOW_SUSPEND_COUNT (50) + /* * Helper macros to lock/unlock logbuf_lock and switch between * printk-safe/unsafe modes. From b58e7d69cf9458648d40676f501f2ed4be05f595 Mon Sep 17 00:00:00 2001 From: Jeremy Rocher Date: Wed, 20 Jul 2016 10:00:44 +0200 Subject: [PATCH 0568/1103] reboot: add reboot_panic parameter This "reboot_panic" kernel cmdline parameter allow to change the reboot mode in case of panic only. It use the same format as the "reboot" parameter. Example, for cf9 warm reset in case of panic: reboot_panic=p,w Signed-off-by: Jeremy Rocher Signed-off-by: Tian, Baofeng --- .../admin-guide/kernel-parameters.txt | 3 ++ kernel/reboot.c | 29 ++++++++++++++++++- 2 files changed, 31 insertions(+), 1 deletion(-) diff --git a/Documentation/admin-guide/kernel-parameters.txt b/Documentation/admin-guide/kernel-parameters.txt index 92eb1f42240d..9437bd12d879 100644 --- a/Documentation/admin-guide/kernel-parameters.txt +++ b/Documentation/admin-guide/kernel-parameters.txt @@ -3899,6 +3899,9 @@ reboot_cpu is s[mp]#### with #### being the processor to be used for rebooting. + reboot_panic= [KNL] + Same as reboot parameter above but only in case of panic. + relax_domain_level= [KNL, SMP] Set scheduler's default relax_domain_level. See Documentation/cgroup-v1/cpusets.txt. diff --git a/kernel/reboot.c b/kernel/reboot.c index 8fb44dec9ad7..ec0108927785 100644 --- a/kernel/reboot.c +++ b/kernel/reboot.c @@ -43,6 +43,7 @@ int reboot_default = 1; int reboot_cpu; enum reboot_type reboot_type = BOOT_ACPI; int reboot_force; +char *reboot_panic_param; /* * If set, this is used for preparing the system to power off. @@ -515,7 +516,7 @@ void orderly_reboot(void) } EXPORT_SYMBOL_GPL(orderly_reboot); -static int __init reboot_setup(char *str) +static int reboot_setup(char *str) { for (;;) { /* @@ -582,3 +583,29 @@ static int __init reboot_setup(char *str) return 1; } __setup("reboot=", reboot_setup); + +static int reboot_panic_notifier_call(struct notifier_block *notifier, + unsigned long what, void *data) +{ + if (!reboot_panic_param) + return NOTIFY_DONE; + + reboot_setup(reboot_panic_param); + pr_info("panic mode set: %s\n", reboot_panic_param); + + return NOTIFY_DONE; +} + +static struct notifier_block reboot_panic_notifier = { + .notifier_call = reboot_panic_notifier_call, +}; + +static int __init reboot_panic_setup(char *str) +{ + reboot_panic_param = str; + atomic_notifier_chain_register(&panic_notifier_list, + &reboot_panic_notifier); + + return 1; +} +__setup("reboot_panic=", reboot_panic_setup); From 1fdb09b8b935de89140d9e7545222f4d72e5db42 Mon Sep 17 00:00:00 2001 From: "he, bo" Date: Thu, 5 Jan 2017 10:01:37 +0800 Subject: [PATCH 0569/1103] implement the adb reboot dnx feature in kernel. there are 3 ways flash the IFWI on BXTP board: Method 1: debug card + ias-spi-programmer (Linux host only) Method 2: CSE DnX + PlatformFlashTool + switch Method 3: CSE DnX + PlatformFlashTool + ABL CLI Method 1 and Method 3 depend on the debug board, but not everyone has the debug board. Method 2 need rework the board, and also if we want to flash the IFWI, we need to switch the Jumper 4 to ON while if we want to boot to Android, we need to switch the Jumper 4 to OFF. the patch implement one feature to reboot the board to dnx mode, then we can flash the IFWI via platform tools. the reboot to dnx command is: adb reboot dnx Signed-off-by: he, bo --- drivers/staging/android/abl/ablbc.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/staging/android/abl/ablbc.c b/drivers/staging/android/abl/ablbc.c index c7ed097b8ac1..6bb5d25aab44 100644 --- a/drivers/staging/android/abl/ablbc.c +++ b/drivers/staging/android/abl/ablbc.c @@ -99,6 +99,7 @@ static const struct name2id NAME2ID[] = { { "elk", 0x02 }, { "recovery", 0x03 }, { "crashmode", 0x04 }, + { "dnx", 0x05 }, { "cli", 0x10 }, }; @@ -294,7 +295,7 @@ static const unsigned int DEFAULT_TARGET_INDEX; static const char * const cold_reset[] = { "/sbin/cansend", "slcan0", - "0000FFFF#05025555555555", + "0000FFFF#05035555555555", NULL}; static const char * const cold_reset_capsule[] = { "/sbin/cansend", From 4131160beba028377059adc02aa8fbb9a5abe10d Mon Sep 17 00:00:00 2001 From: Emmanuel Berthier Date: Thu, 28 Jul 2016 15:47:23 +0300 Subject: [PATCH 0570/1103] Panic in case of Bad page Bug Controled by CONFIG_DEBUG_PANIC_ON_BAD_PAGE config switch. Signed-off-by: Emmanuel Berthier --- mm/Kconfig | 6 ++++++ mm/page_alloc.c | 4 ++++ 2 files changed, 10 insertions(+) diff --git a/mm/Kconfig b/mm/Kconfig index de64ea658716..fe1aebb64897 100644 --- a/mm/Kconfig +++ b/mm/Kconfig @@ -604,6 +604,12 @@ config PGTABLE_MAPPING You can check speed with zsmalloc benchmark: https://github.com/spartacus06/zsmapbench +config DEBUG_PANIC_ON_BAD_PAGE + bool "Panic on Bad Page" + default n + help + Generate a panic in case of Bad Page error detection. + config ZSMALLOC_STAT bool "Export zsmalloc statistics" depends on ZSMALLOC diff --git a/mm/page_alloc.c b/mm/page_alloc.c index 706a738c0aee..ee7cc10b7b28 100644 --- a/mm/page_alloc.c +++ b/mm/page_alloc.c @@ -544,6 +544,10 @@ static void bad_page(struct page *page, const char *reason, /* Leave bad fields for debug, except PageBuddy could make trouble */ page_mapcount_reset(page); /* remove PageBuddy */ add_taint(TAINT_BAD_PAGE, LOCKDEP_NOW_UNRELIABLE); + +#ifdef CONFIG_DEBUG_PANIC_ON_BAD_PAGE + panic("Bad page"); +#endif } /* From b6becf30030d136a323b96c271dcd5d1e799beea Mon Sep 17 00:00:00 2001 From: "Li, Hongli" Date: Wed, 12 Jul 2017 11:48:01 +0800 Subject: [PATCH 0571/1103] Fix the ablbc.c compile error: incompatible pointer type [-Werror=incompatible-pointer-typesi] Signed-off-by: Li, Hongli --- drivers/staging/android/abl/ablbc.c | 30 ++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/drivers/staging/android/abl/ablbc.c b/drivers/staging/android/abl/ablbc.c index 6bb5d25aab44..428b0f478bf7 100644 --- a/drivers/staging/android/abl/ablbc.c +++ b/drivers/staging/android/abl/ablbc.c @@ -124,11 +124,11 @@ static void write_msg_to_nvram(struct nvram_msg *nvram_msg) { /* Ensure to start from top : only one command expected */ offset = 0; - write_data_to_nvram(nvram_msg, + write_data_to_nvram((void*)nvram_msg, offsetof(struct nvram_msg, cdata_payload)); - write_data_to_nvram(nvram_msg->cdata_payload, + write_data_to_nvram((void*)(nvram_msg->cdata_payload), nvram_msg->cdata_payload_size); - write_data_to_nvram(&(nvram_msg->crc), sizeof(nvram_msg->crc)); + write_data_to_nvram((void*)&(nvram_msg->crc), sizeof(nvram_msg->crc)); } /* Compute CRC for one byte (shift register-based: one bit at a time). */ @@ -174,7 +174,7 @@ static uint32_t crc32c_msg(struct nvram_msg *nvram_msg) static struct kobject *capsule_kobject; static ssize_t is_capsule_requested(struct kobject *kobj, - struct kobj_attribute *attr, char *buf, size_t count) + struct kobj_attribute *attr, char *buf) { return sprintf(buf, "%d\n", capsule_request); } @@ -185,13 +185,13 @@ enum capsule_device_type { }; static ssize_t capsule_store(struct kobject *kobj, struct kobj_attribute *attr, - char *buf, size_t count) + const char *buf, size_t count) { struct nvram_msg msg; struct nvram_capsule_cmd *capsule_cmd; char name[32], partition; enum capsule_device_type device; - int ret, crc, padding; + int ret, padding; unsigned char size; union _cdata_header cdh; @@ -221,7 +221,7 @@ static ssize_t capsule_store(struct kobject *kobj, struct kobj_attribute *attr, capsule_cmd->device = device; capsule_cmd->partition = partition; strncpy(capsule_cmd->file_name, name, strlen(name)); - msg.cdata_payload = capsule_cmd; + msg.cdata_payload = (char *)capsule_cmd; msg.cdata_payload_size = 3 + strlen(name) + padding; msg.crc = crc32c_msg(&msg); write_msg_to_nvram(&msg); @@ -279,7 +279,7 @@ static int set_reboot_target(const char *name) reboot_cmd.action = USERCMD_ACTION; reboot_cmd.target = id; - msg.cdata_payload = &reboot_cmd; + msg.cdata_payload = (void*)&reboot_cmd; msg.cdata_payload_size = sizeof(reboot_cmd); msg.size = offsetof(struct nvram_msg, cdata_payload) + sizeof(reboot_cmd) + sizeof(msg.crc); @@ -318,9 +318,9 @@ static int execute_slcan_command(const char *cmd[]) struct subprocess_info *sub_info; int ret = -1; - sub_info = call_usermodehelper_setup(cmd[0], - cmd, NULL, GFP_KERNEL, - NULL, NULL, NULL); + sub_info = call_usermodehelper_setup((char *)cmd[0], + (char **)cmd,(char **) NULL, GFP_KERNEL, + (void *)NULL, (void*)NULL, (void*)NULL); if (sub_info) { ret = call_usermodehelper_exec(sub_info, @@ -343,11 +343,11 @@ static int ablbc_reboot_notifier_call(struct notifier_block *notifier, if (what != SYS_RESTART) return NOTIFY_DONE; - ret = execute_slcan_command(suppress_heartbeat); + ret = execute_slcan_command((const char **)suppress_heartbeat); if (ret) goto done; - ret = execute_slcan_command(reboot_request); + ret = execute_slcan_command((const char **)reboot_request); if (ret) goto done; if (target[0] != '\0') { @@ -356,13 +356,13 @@ static int ablbc_reboot_notifier_call(struct notifier_block *notifier, pr_err("%s: Failed to set reboot target, ret=%d\n", __func__, ret); else { - ret = execute_slcan_command(cold_reset); + ret = execute_slcan_command((const char **)cold_reset); if (ret) goto done; } } if (capsule_request) - ret = execute_slcan_command(cold_reset_capsule); + ret = execute_slcan_command((const char **)cold_reset_capsule); done: return NOTIFY_DONE; From c7ed63ca7b956077da744634b7a23e6b135e3a84 Mon Sep 17 00:00:00 2001 From: "Tian, Baofeng" Date: Fri, 13 Oct 2017 14:38:09 +0800 Subject: [PATCH 0572/1103] intel_th: add security check to avoid null pointer happens Signed-off-by: Tian, Baofeng --- drivers/hwtracing/intel_th/gth.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/drivers/hwtracing/intel_th/gth.c b/drivers/hwtracing/intel_th/gth.c index f697528e4d1c..854411879008 100644 --- a/drivers/hwtracing/intel_th/gth.c +++ b/drivers/hwtracing/intel_th/gth.c @@ -659,9 +659,16 @@ static void intel_th_gth_switch(struct intel_th_device *thdev, static int intel_th_gth_assign(struct intel_th_device *thdev, struct intel_th_device *othdev) { - struct gth_device *gth = dev_get_drvdata(&thdev->dev); + struct gth_device *gth; int i, id; + if(thdev == NULL || othdev == NULL) + return -EINVAL; + + gth = dev_get_drvdata(&thdev->dev); + if(gth == NULL) + return -EINVAL; + if (thdev->host_mode) return -EBUSY; From a465153d924dd445710e393383fe5a1b9917fa53 Mon Sep 17 00:00:00 2001 From: jiangyao Date: Wed, 12 Jul 2017 15:13:36 +0800 Subject: [PATCH 0573/1103] Modify cansend path to adjust Treble feature Signed-off-by: jiangyao --- drivers/staging/android/abl/ablbc.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/staging/android/abl/ablbc.c b/drivers/staging/android/abl/ablbc.c index 428b0f478bf7..b4a491cba7c5 100644 --- a/drivers/staging/android/abl/ablbc.c +++ b/drivers/staging/android/abl/ablbc.c @@ -293,22 +293,22 @@ static int set_reboot_target(const char *name) static const unsigned int DEFAULT_TARGET_INDEX; static const char * const cold_reset[] = { - "/sbin/cansend", + "/vendor/bin/cansend", "slcan0", "0000FFFF#05035555555555", NULL}; static const char * const cold_reset_capsule[] = { - "/sbin/cansend", + "/vendor/bin/cansend", "slcan0", "0000FFFF#05035555555555", NULL}; static const char * const suppress_heartbeat[] = { - "/sbin/cansend", + "/vendor/bin/cansend", "slcan0", "0000FFFF#01035555555555", NULL}; static const char * const reboot_request[] = { - "/sbin/cansend", + "/vendor/bin/cansend", "slcan0", "0000FFFF#03015555555555", NULL}; From 3857c76df4b3bbf44da3fb45b72141e03e99123c Mon Sep 17 00:00:00 2001 From: Guillaume Betous Date: Thu, 23 Nov 2017 15:42:46 +0800 Subject: [PATCH 0574/1103] staging/android: SBL boot Control driver * Set reboot target in NVRAM - register to reboot event - create CDATA SBL code following reboot target * Set capsule parameters in NVRAM - accessible through /sys/kernel/capsule/capsule_name - same syntax as "nvram" SBL command - e.g. m1:capsule.bin - m => emmc - 1 => partition #1 - capsule.bin => file name * Notify on capsule request - accessible through /sys/kernel/capsule/capsule_requested - 0 : no capsule requested - 1 : capsule requested (triggered through /sys/kernel/capsule/capsule_name access) Signed-off-by: Zhou, JianfengX --- arch/x86/configs/sbl_diffconfig | 1 + drivers/staging/android/Kconfig | 1 + drivers/staging/android/Makefile | 1 + drivers/staging/android/sbl/Kconfig | 8 + drivers/staging/android/sbl/Makefile | 1 + drivers/staging/android/sbl/sblbc.c | 368 +++++++++++++++++++++++++++ 6 files changed, 380 insertions(+) create mode 100644 arch/x86/configs/sbl_diffconfig create mode 100644 drivers/staging/android/sbl/Kconfig create mode 100644 drivers/staging/android/sbl/Makefile create mode 100644 drivers/staging/android/sbl/sblbc.c diff --git a/arch/x86/configs/sbl_diffconfig b/arch/x86/configs/sbl_diffconfig new file mode 100644 index 000000000000..efbd1ae0b5e1 --- /dev/null +++ b/arch/x86/configs/sbl_diffconfig @@ -0,0 +1 @@ +CONFIG_SBL_BOOTLOADER_CONTROL=y diff --git a/drivers/staging/android/Kconfig b/drivers/staging/android/Kconfig index 87054ad922aa..9f6b54a3c90f 100644 --- a/drivers/staging/android/Kconfig +++ b/drivers/staging/android/Kconfig @@ -25,6 +25,7 @@ config ANDROID_VSOC source "drivers/staging/android/ion/Kconfig" source "drivers/staging/android/abl/Kconfig" +source "drivers/staging/android/sbl/Kconfig" endif # if ANDROID diff --git a/drivers/staging/android/Makefile b/drivers/staging/android/Makefile index 729d11f4e150..fb345067b7f0 100644 --- a/drivers/staging/android/Makefile +++ b/drivers/staging/android/Makefile @@ -2,6 +2,7 @@ ccflags-y += -I$(src) # needed for trace events obj-y += ion/ obj-$(CONFIG_ABL_BOOTLOADER_CONTROL) += abl/ +obj-$(CONFIG_SBL_BOOTLOADER_CONTROL) += sbl/ obj-$(CONFIG_ASHMEM) += ashmem.o obj-$(CONFIG_ANDROID_VSOC) += vsoc.o diff --git a/drivers/staging/android/sbl/Kconfig b/drivers/staging/android/sbl/Kconfig new file mode 100644 index 000000000000..f5f754f8d890 --- /dev/null +++ b/drivers/staging/android/sbl/Kconfig @@ -0,0 +1,8 @@ +config SBL_BOOTLOADER_CONTROL + tristate "SBL Bootloader Control module" + default n + help + This driver installs a reboot hook, such that if reboot() is + invoked with a string argument, the corresponding ABL Action + is written in CMOS data, in order to be processed by ABL on + reboot. diff --git a/drivers/staging/android/sbl/Makefile b/drivers/staging/android/sbl/Makefile new file mode 100644 index 000000000000..6d1258d7bfc1 --- /dev/null +++ b/drivers/staging/android/sbl/Makefile @@ -0,0 +1 @@ +obj-$(CONFIG_SBL_BOOTLOADER_CONTROL) += sblbc.o diff --git a/drivers/staging/android/sbl/sblbc.c b/drivers/staging/android/sbl/sblbc.c new file mode 100644 index 000000000000..6c2d7ed6c2ee --- /dev/null +++ b/drivers/staging/android/sbl/sblbc.c @@ -0,0 +1,368 @@ +/* + * sblbc: control SBL bootloaders + * Copyright (c) 2013-2017, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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. + */ + +#include +#include +#include +#include +#include +#include +#include + +#define MODULE_NAME "sblbc" + +/* RTC read and write */ +static inline unsigned char cmos_read_ext_bank(u8 addr) +{ + outb(addr, RTC_PORT(4)); + return inb(RTC_PORT(5)); +} +#define CMOS_READ_EXT(a) cmos_read_ext_bank(a) + +static inline void cmos_write_ext_bank(u8 val, u8 addr) +{ + outb(addr, RTC_PORT(4)); + outb(val, RTC_PORT(5)); +} +#define CMOS_WRITE_EXT(v, a) cmos_write_ext_bank(v, a) + +/* ABL Conventions */ +#define NVRAM_START_ADDRESS 0x10 + +#define _USERCMD_(cmd, len) (((cmd) << 5) | ((len) & 0x1f)) +#define USERCMD_END _USERCMD_(0, 0) +#define USERCMD_ACTION _USERCMD_(7, 1) +#define USERCMD_UPDATE_IFWI(len) _USERCMD_(2, len) + +#define CDATA_TAG_USER_CMD 0x4d +#define NVRAM_VALID_FLAG 0x12 + +#define CRC32C_POLYNOMIAL 0x82F63B78 /* CRC32C Castagnoli */ + +static bool capsule_request; + +union _cdata_header { + uint32_t data; + struct { + unsigned ncond : 2; + unsigned length : 10; + unsigned flags : 4; + unsigned version: 4; + unsigned tag : 12; + }; +}; + +struct nvram_capsule_cmd { + char action; + char device; + char partition; + char file_name[1]; +} __packed; + +struct nvram_reboot_cmd { + char action; + char target; + char end; + char padding; +} __packed; + +struct name2id { + const char *name; + int id; +}; + +struct nvram_msg { + char magic; + char size; + union _cdata_header cdata_header; + char *cdata_payload; + size_t cdata_payload_size; + uint32_t crc; +} __packed; + +static const struct name2id NAME2ID[] = { + { "main", 0x00 }, + { "android", 0x00 }, + { "bootloader", 0x01 }, + { "fastboot", 0x01 }, + { "elk", 0x02 }, + { "recovery", 0x03 }, + { "crashmode", 0x04 }, + { "dnx", 0x05 }, + { "cli", 0x10 }, +}; + +static size_t offset; /* memorize offset between each call */ + +static size_t write_data_to_nvram(char *data, size_t size) +{ + int i; + unsigned long flags; + + spin_lock_irqsave(&rtc_lock, flags); + for (i = 0; i < size; i++) + CMOS_WRITE_EXT(*(data + i), NVRAM_START_ADDRESS + offset + i); + + for (i = 0; i < size; i++) + { + pr_err("Kernel Addr=0x%X, data=0x%X\n", NVRAM_START_ADDRESS + offset + i, *(unsigned char *)(data + i)); + } + + offset += size; + spin_unlock_irqrestore(&rtc_lock, flags); + + return i; +} + +static void write_msg_to_nvram(struct nvram_msg *nvram_msg) +{ + /* Ensure to start from top : only one command expected */ + offset = 0; + write_data_to_nvram((void*)nvram_msg, + offsetof(struct nvram_msg, cdata_payload)); + write_data_to_nvram((void*)(nvram_msg->cdata_payload), + nvram_msg->cdata_payload_size); + write_data_to_nvram((void*)&(nvram_msg->crc), sizeof(nvram_msg->crc)); +} + +/* Compute CRC for one byte (shift register-based: one bit at a time). */ +static uint32_t crc32c_byte(uint32_t crc, unsigned byte) +{ + int i; + uint32_t c; + + for (i = 0 ; i < 8 ; i += 1) { + c = (crc ^ byte) & 1; + if (c) + crc = (crc >> 1) ^ CRC32C_POLYNOMIAL; + else + crc = (crc >> 1); + byte >>= 1; + } + + return crc; +} + +/* Compute CRC for a given buffer. */ +static uint32_t crc32c_buf(uint32_t crc, const void *addr, unsigned len) +{ + unsigned i; + + for (i = 0 ; i < len ; i += 1) + crc = crc32c_byte(crc, *(uint8_t *)(addr + i)); + + return crc; +} + +static uint32_t crc32c_msg(struct nvram_msg *nvram_msg) +{ + uint32_t crc; + + crc = crc32c_buf(~0, nvram_msg, + offsetof(struct nvram_msg, cdata_payload)); + crc = crc32c_buf(crc, nvram_msg->cdata_payload, + nvram_msg->cdata_payload_size); + return crc; +} + +static struct kobject *capsule_kobject; + +static ssize_t is_capsule_requested(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + return sprintf(buf, "%d\n", capsule_request); +} + +enum capsule_device_type { + EMMC = 2, + SDCARD = 4 +}; + +static ssize_t capsule_store(struct kobject *kobj, struct kobj_attribute *attr, + const char *buf, size_t count) +{ + struct nvram_msg msg; + struct nvram_capsule_cmd *capsule_cmd; + char name[32], partition; + enum capsule_device_type device; + int ret, padding; + unsigned char size; + union _cdata_header cdh; + + device = (buf[0] == 'm' ? EMMC : SDCARD); + partition = buf[1] - '0'; + ret = sscanf(buf+3, "%s", name); + pr_info(MODULE_NAME " capsule parameters (%d): DEVICE=%d PARTITION=%d NAME=%s\n", + ret, device, partition, name); + + cdh.data = 0; + cdh.tag = CDATA_TAG_USER_CMD; + + /* padding of filename on next dword */ + padding = (4 - (3 + strlen(name))%4)%4; + size = 2 + sizeof(cdh) + 3 + strlen(name) + padding + 4; + cdh.length = 1 + (3 + strlen(name) + padding) / 4; + + msg.magic = NVRAM_VALID_FLAG; + msg.size = size; + msg.cdata_header.data = cdh.data; + + capsule_cmd = kmalloc(size, GFP_KERNEL); + if (!capsule_cmd) + return -ENOMEM; + + capsule_cmd->action = USERCMD_UPDATE_IFWI(strlen(name) + 2); + capsule_cmd->device = device; + capsule_cmd->partition = partition; + strncpy(capsule_cmd->file_name, name, strlen(name)); + msg.cdata_payload = (char *)capsule_cmd; + msg.cdata_payload_size = 3 + strlen(name) + padding; + msg.crc = crc32c_msg(&msg); + write_msg_to_nvram(&msg); + capsule_request = true; + + kfree(capsule_cmd); + + return count; +} + +static struct kobj_attribute capsule_name_attribute = + __ATTR(capsule_name, 0600, NULL, capsule_store); + +static struct kobj_attribute capsule_requested_attribute = + __ATTR(capsule_requested, 0400, is_capsule_requested, NULL); + +static int reboot_target_name2id(const char *name) +{ + size_t i; + + for (i = 0; i < ARRAY_SIZE(NAME2ID); i++) + if (!strcmp(NAME2ID[i].name, name)) + return NAME2ID[i].id; + + return -EINVAL; +} + +static int set_reboot_target(const char *name) +{ + int id; + struct nvram_msg msg; + struct nvram_reboot_cmd reboot_cmd; + union _cdata_header cdh; + + if (name == NULL) { + pr_err("Error in %s: NULL target\n", __func__); + return -EINVAL; + } + + id = reboot_target_name2id(name); + if (id < 0) { + pr_err("Error in %s: '%s' is not a valid target\n", + __func__, name); + return -EINVAL; + } + + cdh.data = 0; + cdh.length = 2; /* 2*32 bits, from header to padding */ + cdh.tag = CDATA_TAG_USER_CMD; + + memset(&reboot_cmd, 0, sizeof(reboot_cmd)); + memset(&msg, 0, sizeof(msg)); + msg.magic = NVRAM_VALID_FLAG; + msg.cdata_header.data = cdh.data; + reboot_cmd.action = USERCMD_ACTION; + + reboot_cmd.target = id; + msg.cdata_payload = (void*)&reboot_cmd; + msg.cdata_payload_size = sizeof(reboot_cmd); + msg.size = offsetof(struct nvram_msg, cdata_payload) + + sizeof(reboot_cmd) + sizeof(msg.crc); + msg.crc = crc32c_msg(&msg); + + write_msg_to_nvram(&msg); + + return 0; +} + +static int sblbc_reboot_notifier_call(struct notifier_block *notifier, + unsigned long what, void *data) +{ + const char *target = (const char *)data; + int ret; + + if (what != SYS_RESTART) + return NOTIFY_DONE; + + if (target[0] != '\0') { + ret = set_reboot_target(target); + if (ret) + pr_err("%s: Failed to set reboot target, ret=%d\n", + __func__, ret); + } + +done: + return NOTIFY_DONE; +} + +static struct notifier_block sblbc_reboot_notifier = { + .notifier_call = sblbc_reboot_notifier_call, +}; + +static int __init sblbc_init(void) +{ + int ret; + + ret = register_reboot_notifier(&sblbc_reboot_notifier); + if (ret) { + pr_err(MODULE_NAME ": unable to register reboot notifier\n"); + return ret; + } + + capsule_kobject = kobject_create_and_add("capsule", kernel_kobj); + if (!capsule_kobject) + return -ENOMEM; + + ret = sysfs_create_file(capsule_kobject, + &capsule_name_attribute.attr); + if (ret) { + pr_err("failed to create the foo file in /sys/kernel/capsule/capsule_name\n"); + goto err; + } + + ret = sysfs_create_file(capsule_kobject, + &capsule_requested_attribute.attr); + if (ret) { + pr_err("failed to create the foo file in /sys/kernel/capsule/capsule_requested\n"); + goto err; + } + + return 0; + +err: + kobject_put(capsule_kobject); + return ret; +} + +module_init(sblbc_init); + +static void __exit sblbc_exit(void) +{ + unregister_reboot_notifier(&sblbc_reboot_notifier); + kobject_put(capsule_kobject); +} +module_exit(sblbc_exit); + +MODULE_AUTHOR("Guillaume Betous "); +MODULE_DESCRIPTION("Slimboot boot control driver"); +MODULE_LICENSE("GPL v2"); From 46875fb16f7cc7a082ab53fd77b7a9514a2fd430 Mon Sep 17 00:00:00 2001 From: "Duan, YayongX" Date: Wed, 27 Dec 2017 12:26:06 +0800 Subject: [PATCH 0575/1103] Debug: Add register dump Store CPU registers during cpu stop in order to get them with ramdump. Signed-off-by: Emmanuel Berthier Signed-off-by: Duan, YayongX --- arch/x86/kernel/smp.c | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/arch/x86/kernel/smp.c b/arch/x86/kernel/smp.c index 04adc8d60aed..9bbeec53634c 100644 --- a/arch/x86/kernel/smp.c +++ b/arch/x86/kernel/smp.c @@ -117,6 +117,19 @@ static atomic_t stopping_cpu = ATOMIC_INIT(-1); static bool smp_no_nmi_ipi = false; +static DEFINE_PER_CPU(struct pt_regs, cpu_regs); + +/* Store regs of this CPU for RAM dump decoding help */ +static inline void store_regs(struct pt_regs *regs) +{ + struct pt_regs *print_regs; + print_regs = &get_cpu_var(cpu_regs); + crash_setup_regs(print_regs, regs); + + /* Flush CPU cache */ + wbinvd(); +} + /* * this function sends a 'reschedule' IPI to another CPU. * it goes straight through and wastes no time serializing @@ -163,6 +176,7 @@ static int smp_stop_nmi_callback(unsigned int val, struct pt_regs *regs) if (raw_smp_processor_id() == atomic_read(&stopping_cpu)) return NMI_HANDLED; + store_regs(regs); cpu_emergency_vmxoff(); stop_this_cpu(NULL); @@ -173,9 +187,10 @@ static int smp_stop_nmi_callback(unsigned int val, struct pt_regs *regs) * this function calls the 'stop' function on all other CPUs in the system. */ -asmlinkage __visible void smp_reboot_interrupt(void) +__visible void smp_reboot_interrupt(struct pt_regs *regs) { ipi_entering_ack_irq(); + store_regs(regs); cpu_emergency_vmxoff(); stop_this_cpu(NULL); irq_exit(); @@ -247,6 +262,7 @@ static void native_stop_other_cpus(int wait) } finish: + store_regs(NULL); local_irq_save(flags); disable_local_APIC(); mcheck_cpu_clear(this_cpu_ptr(&cpu_info)); From 60321c283018be52ce5e640587ffd6ad55010d6e Mon Sep 17 00:00:00 2001 From: "Duan, YayongX" Date: Wed, 27 Dec 2017 14:00:27 +0800 Subject: [PATCH 0576/1103] Debug: Pstore driver: assign value to part of ramoops For optimizing kernel command line and reduce some parameter passing. We will directly assign/set below variables to a default value rather than by passing from kernel command line. pdata->record_size padta->record_size pdata->ftrace_size pdata->dump_oops Signed-off-by: Duan, YayongX --- fs/pstore/ram.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/fs/pstore/ram.c b/fs/pstore/ram.c index f4fd2e72add4..2f883a4c3420 100644 --- a/fs/pstore/ram.c +++ b/fs/pstore/ram.c @@ -38,6 +38,10 @@ #define RAMOOPS_KERNMSG_HDR "====" #define MIN_MEM_SIZE 4096UL +#define DEFAULT_RECORD_SIZE 0X4000 +#define DEFAULT_CONSOLE_SIZE 0X200000 +#define DEFAULT_FTRACE_SIZE 0x2000 +#define DEFAULT_DUMP_OOPS 1 static ulong record_size = MIN_MEM_SIZE; module_param(record_size, ulong, 0400); @@ -688,7 +692,7 @@ static int ramoops_parse_dt(struct platform_device *pdev, pdata->mem_size = resource_size(res); pdata->mem_address = res->start; pdata->mem_type = of_property_read_bool(of_node, "unbuffered"); - pdata->dump_oops = !of_property_read_bool(of_node, "no-dump-oops"); + pdata->dump_oops = DEFAULT_DUMP_OOPS; #define parse_size(name, field) { \ ret = ramoops_parse_dt_size(pdev, name, &value); \ @@ -697,9 +701,9 @@ static int ramoops_parse_dt(struct platform_device *pdev, field = value; \ } - parse_size("record-size", pdata->record_size); - parse_size("console-size", pdata->console_size); - parse_size("ftrace-size", pdata->ftrace_size); + pdata->record_size = DEFAULT_RECORD_SIZE; + pdata->console_size = DEFAULT_CONSOLE_SIZE; + pdata->ftrace_size = DEFAULT_FTRACE_SIZE; parse_size("pmsg-size", pdata->pmsg_size); parse_size("ecc-size", pdata->ecc_info.ecc_size); parse_size("flags", pdata->flags); From aa3e2a3078efe525f398ee946041110c152830db Mon Sep 17 00:00:00 2001 From: Xihua Chen Date: Thu, 28 Dec 2017 14:30:53 +0800 Subject: [PATCH 0577/1103] Edit suppress_heartbeat to 10mins and remove unnecessary commands Suppress_heartbeat and cold_reset_capsule configure are used for IFWI(Integrated firmware image) and ota update, 10mins is enough. For cold_reset configure, it is not need for normal reboot, keep this will cause reboot issue, need remove it. Signed-off-by: Xihua Chen --- drivers/staging/android/abl/ablbc.c | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/drivers/staging/android/abl/ablbc.c b/drivers/staging/android/abl/ablbc.c index b4a491cba7c5..7b15fa39a43d 100644 --- a/drivers/staging/android/abl/ablbc.c +++ b/drivers/staging/android/abl/ablbc.c @@ -292,15 +292,10 @@ static int set_reboot_target(const char *name) static const unsigned int DEFAULT_TARGET_INDEX; -static const char * const cold_reset[] = { - "/vendor/bin/cansend", - "slcan0", - "0000FFFF#05035555555555", - NULL}; static const char * const cold_reset_capsule[] = { "/vendor/bin/cansend", "slcan0", - "0000FFFF#05035555555555", + "0000FFFF#05025555555555", NULL}; static const char * const suppress_heartbeat[] = { "/vendor/bin/cansend", @@ -355,11 +350,6 @@ static int ablbc_reboot_notifier_call(struct notifier_block *notifier, if (ret) pr_err("%s: Failed to set reboot target, ret=%d\n", __func__, ret); - else { - ret = execute_slcan_command((const char **)cold_reset); - if (ret) - goto done; - } } if (capsule_request) ret = execute_slcan_command((const char **)cold_reset_capsule); From 9a38eb25e851e0459cc6c4857e217dadb4e3f199 Mon Sep 17 00:00:00 2001 From: "Tian, Baofeng" Date: Wed, 3 Jan 2018 07:23:31 +0800 Subject: [PATCH 0578/1103] debug and trace: npk: add one more buffer before send log data to usb logs data is saved with scatter list buffer, it have multiple pages( up to 32) dvc driver send this buffer to usb driver, however, in usb driver, it may not support scatter list well in current 4.14 lts code, so we add one more buffer to combine all pages data to one linear buffer, then tranfer it to usb. Signed-off-by: Tian, Baofeng --- drivers/hwtracing/intel_th/intel_th.h | 2 +- drivers/hwtracing/intel_th/msu-dvc.c | 61 +++++++++++++++++++++++++++ 2 files changed, 62 insertions(+), 1 deletion(-) mode change 100644 => 100755 drivers/hwtracing/intel_th/msu-dvc.c diff --git a/drivers/hwtracing/intel_th/intel_th.h b/drivers/hwtracing/intel_th/intel_th.h index 7bbc75626f1d..f385471fe56a 100644 --- a/drivers/hwtracing/intel_th/intel_th.h +++ b/drivers/hwtracing/intel_th/intel_th.h @@ -308,7 +308,7 @@ enum { /* Timestamp counter unit (TSCU) */ REG_TSCU_OFFSET = 0x2000, - REG_TSCU_LENGTH = 0x1000, + REG_TSCU_LENGTH = 0x2000, /* Software Trace Hub (STH) [0x4000..0x4fff] */ REG_STH_OFFSET = 0x4000, diff --git a/drivers/hwtracing/intel_th/msu-dvc.c b/drivers/hwtracing/intel_th/msu-dvc.c old mode 100644 new mode 100755 index 8e5b245e64a9..91693c0f8175 --- a/drivers/hwtracing/intel_th/msu-dvc.c +++ b/drivers/hwtracing/intel_th/msu-dvc.c @@ -100,6 +100,8 @@ static const char *const process_type_name[] = { struct mdd_transfer_data { u8 *buffer; + u8 *buffer_sg; + size_t buffer_sg_len; dma_addr_t buffer_dma; size_t buffer_len; struct scatterlist *sg_raw; @@ -479,6 +481,12 @@ static int mdd_setup_transfer_data(struct msu_dvc_dev *mdd) goto err_l_buf; } } else { + mdd->tdata.buffer_sg_len = + mdd->tdata.block_count * mdd->tdata.block_size; + mdd->tdata.buffer_sg = kmalloc(mdd->tdata.buffer_sg_len, GFP_KERNEL); + if(mdd->tdata.buffer_sg == NULL) + mdd->tdata.buffer_sg_len = 0; + mdd->tdata.buffer = NULL; mdd->tdata.buffer_dma = 0; mdd->tdata.buffer_len = 0; @@ -511,6 +519,12 @@ static void mdd_reset_transfer_data(struct msu_dvc_dev *mdd) mdd->tdata.buffer_dma = 0; mdd->tdata.buffer_len = 0; } + + if(mdd->tdata.buffer_sg != NULL) + { + mdd->tdata.buffer_sg_len = 0; + kfree(mdd->tdata.buffer_sg); + } } static unsigned mdd_sg_len(struct scatterlist *sgl, int nents) @@ -526,10 +540,57 @@ static unsigned mdd_sg_len(struct scatterlist *sgl, int nents) return ret; } +static int mdd_send_sg_buffer(struct msu_dvc_dev *mdd, int nents) +{ + size_t transfer_len; + + /*MDD_F_DEBUG(); */ + mdd_lock_transfer(mdd); + transfer_len = + sg_copy_to_buffer(mdd->tdata.sg_trans, nents, mdd->tdata.buffer_sg, + mdd->tdata.buffer_sg_len); + + if (!transfer_len) { + mdd_err(mdd, "Cannot copy into nonsg memory\n"); + mdd_unlock_transfer(mdd); + return -EINVAL; + } + + mdd->req->buf = mdd->tdata.buffer_sg; + mdd->req->length = transfer_len; + mdd->req->dma = 0; + mdd->req->sg = NULL; + mdd->req->num_sgs = 0; + + mdd->req->context = mdd; + mdd->req->complete = mdd_complete; + mdd->req->zero = 1; + + if (usb_ep_queue(mdd->ep, mdd->req, GFP_KERNEL)) { + mdd_err(mdd, "Cannot queue request\n"); + dvct_set_status(mdd->dtc_status, DVCT_MASK_ERR); + mdd_unlock_transfer(mdd); + return -EINVAL; + } + + atomic_set(&mdd->req_ongoing, 1); + mdd_unlock_transfer(mdd); + /*wait for done stop or disable */ + wait_event(mdd->wq, (!atomic_read(&mdd->req_ongoing) || + (atomic_read(mdd->dtc_status) != + DVCT_MASK_ONLINE_TRANS))); + return 0; +} + static int mdd_send_sg(struct msu_dvc_dev *mdd, int nents) { struct scatterlist *sgl = mdd->tdata.sg_trans; + if(mdd->tdata.buffer_sg != NULL) + { + return mdd_send_sg_buffer(mdd, nents); + } + /*MDD_F_DEBUG(); */ while (nents) { int trans_ents; From 14bf533f822ce4d733695f6d97191b5ce4579784 Mon Sep 17 00:00:00 2001 From: "Li, Hongli" Date: Thu, 25 Jan 2018 17:06:12 +0800 Subject: [PATCH 0579/1103] add depends for msc-dvc msc-dvc function must depend on DVC_TRACE_BUS. msc-dvc is the debug function, it based on USB DWC3 gadget function to transfer trace log. Signed-off-by: Li, Hongli --- drivers/hwtracing/intel_th/Kconfig | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/hwtracing/intel_th/Kconfig b/drivers/hwtracing/intel_th/Kconfig index 027edb7da72c..dc22562a16fc 100644 --- a/drivers/hwtracing/intel_th/Kconfig +++ b/drivers/hwtracing/intel_th/Kconfig @@ -69,6 +69,7 @@ config INTEL_TH_MSU config INTEL_TH_MSU_DVC tristate "Intel Trace Hub Memory Storage Unit to USB-dvc" + depends on DVC_TRACE_BUS help Memory Storage Unit (MSU) trace output device enables storing STP traces to system memory. @@ -79,6 +80,7 @@ config INTEL_TH_MSU_DVC config INTEL_TH_MSU_DVC_DEBUG tristate "Intel Trace Hub Memory Storage Unit to USB-dvc debug" + depends on INTEL_TH_MSU_DVC help Memory Storage Unit (MSU) trace output device enables storing STP traces to system memory. From 52fb7c4712bcd4ac249e49846d85cc507eea1fea Mon Sep 17 00:00:00 2001 From: "Jiang, YaoX" Date: Wed, 7 Feb 2018 14:27:27 +0800 Subject: [PATCH 0580/1103] Add control for ioc_slcan in kernel driver Customer has requirement to disable slcan on none IOC borad. Add slcan_diffconfig to control slcan in ablbc driver. CONFIG_SEND_SLCAN_ENABLE=y -> enable slcan CONFIG_SEND_SLCAN_ENABLE=n -> disable slcan Signed-off-by: Jiang, YaoX --- drivers/staging/android/abl/Kconfig | 9 +++++++++ drivers/staging/android/abl/ablbc.c | 4 ++++ 2 files changed, 13 insertions(+) diff --git a/drivers/staging/android/abl/Kconfig b/drivers/staging/android/abl/Kconfig index a89ac5b5efb1..a9e81e7a7a23 100644 --- a/drivers/staging/android/abl/Kconfig +++ b/drivers/staging/android/abl/Kconfig @@ -6,3 +6,12 @@ config ABL_BOOTLOADER_CONTROL invoked with a string argument, the corresponding ABL Action is written in CMOS data, in order to be processed by ABL on reboot. + +config SEND_SLCAN_ENABLE + bool "control slcan protocol" + default n + help + This option control slcan protocol enable/disable in ablbc driver + The IOC compononent on broxton IVI platform use slcan protocol to + communicate befor calling powerctl program. + If no use IOC, this option can be disabed. diff --git a/drivers/staging/android/abl/ablbc.c b/drivers/staging/android/abl/ablbc.c index 7b15fa39a43d..d9d751d806b1 100644 --- a/drivers/staging/android/abl/ablbc.c +++ b/drivers/staging/android/abl/ablbc.c @@ -310,6 +310,7 @@ static const char * const reboot_request[] = { static int execute_slcan_command(const char *cmd[]) { +#ifdef CONFIG_SEND_SLCAN_ENABLE struct subprocess_info *sub_info; int ret = -1; @@ -327,6 +328,9 @@ static int execute_slcan_command(const char *cmd[]) pr_err("Failure on cmd=%s ret=%d\n", cmd[0], ret); return ret; +#else + return 0; +#endif } static int ablbc_reboot_notifier_call(struct notifier_block *notifier, From 9bf8a925731b0ca7d011b3b560444251ff949f3a Mon Sep 17 00:00:00 2001 From: "Tang, Haoyu" Date: Thu, 22 Feb 2018 13:47:23 +0800 Subject: [PATCH 0581/1103] ablbc: panic if fail to call cansend If failed to cummunicate with IOC, IOC could take reboot as shutdown. To triger a panic will avoid this case by warm-reset. Signed-off-by: Tang, Haoyu --- drivers/staging/android/abl/ablbc.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/staging/android/abl/ablbc.c b/drivers/staging/android/abl/ablbc.c index d9d751d806b1..81c5c94ed5f7 100644 --- a/drivers/staging/android/abl/ablbc.c +++ b/drivers/staging/android/abl/ablbc.c @@ -337,7 +337,7 @@ static int ablbc_reboot_notifier_call(struct notifier_block *notifier, unsigned long what, void *data) { const char *target = (const char *)data; - int ret; + int ret = 0; if (what != SYS_RESTART) return NOTIFY_DONE; @@ -359,6 +359,10 @@ static int ablbc_reboot_notifier_call(struct notifier_block *notifier, ret = execute_slcan_command((const char **)cold_reset_capsule); done: +#ifdef CONFIG_SEND_SLCAN_ENABLE + if (ret) + panic("ablbc failed to cummnunicate with IOC."); +#endif return NOTIFY_DONE; } From 6afd0d062c1bdd90e316879395e2d9bcf2be76b0 Mon Sep 17 00:00:00 2001 From: jiangyao Date: Mon, 26 Feb 2018 13:16:04 +0800 Subject: [PATCH 0582/1103] Edit cold_reset/cold_reset_capsule commands Using HECI interface, no need keep toggle Set sus stat toggle use default 1 Just send cold_reset is enough Signed-off-by: jiangyao --- drivers/staging/android/abl/ablbc.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/staging/android/abl/ablbc.c b/drivers/staging/android/abl/ablbc.c index 81c5c94ed5f7..73a3e34958c8 100644 --- a/drivers/staging/android/abl/ablbc.c +++ b/drivers/staging/android/abl/ablbc.c @@ -292,10 +292,10 @@ static int set_reboot_target(const char *name) static const unsigned int DEFAULT_TARGET_INDEX; -static const char * const cold_reset_capsule[] = { +static const char * const cold_reset[] = { "/vendor/bin/cansend", "slcan0", - "0000FFFF#05025555555555", + "0000FFFF#05015555555555", NULL}; static const char * const suppress_heartbeat[] = { "/vendor/bin/cansend", @@ -355,8 +355,8 @@ static int ablbc_reboot_notifier_call(struct notifier_block *notifier, pr_err("%s: Failed to set reboot target, ret=%d\n", __func__, ret); } - if (capsule_request) - ret = execute_slcan_command((const char **)cold_reset_capsule); + + ret = execute_slcan_command((const char **)cold_reset); done: #ifdef CONFIG_SEND_SLCAN_ENABLE From 3f825787816b7da7ec67ea35c5c228b47c8d53af Mon Sep 17 00:00:00 2001 From: "Li, Hongli" Date: Thu, 15 Mar 2018 14:15:50 +0800 Subject: [PATCH 0583/1103] Fix the build warning MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit drivers/hwtracing/intel_th/gth.c:572:19: warning: unused variable ‘th’ [-Wunused-variable] struct intel_th *th = to_intel_th(thdev); drivers/platform/x86/intel_pstore_pram.c:97:2: warning: format ‘%lx’ expects argument of type ‘long unsigned int’, but argument 2 has type ‘phys_addr_t’ [-Wformat=] pr_info("registered pram device, addr=0x%lx, size=0x%lx\n", drivers/staging/android/sbl/sblbc.c:119:3: warning: format ‘%X’ expects argument of type ‘unsigned int’, but argument 2 has type ‘size_t’ [-Wformat=] pr_err("Kernel Addr=0x%X, data=0x%X\n", NVRAM_START_ADDRESS + offset + i, *(unsigned char *)(data + i)); drivers/staging/android/sbl/sblbc.c:314:1: warning: label ‘done’ defined but not used [-Wunused-label] done: Signed-off-by: Li, Hongli --- drivers/hwtracing/intel_th/gth.c | 1 - drivers/platform/x86/intel_pstore_pram.c | 2 +- drivers/staging/android/sbl/sblbc.c | 3 +-- 3 files changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/hwtracing/intel_th/gth.c b/drivers/hwtracing/intel_th/gth.c index 854411879008..2bc9b9f0b752 100644 --- a/drivers/hwtracing/intel_th/gth.c +++ b/drivers/hwtracing/intel_th/gth.c @@ -561,7 +561,6 @@ static int intel_th_gth_enable(struct intel_th_device *thdev, struct intel_th_output *output) { struct gth_device *gth = dev_get_drvdata(&thdev->dev); - struct intel_th *th = to_intel_th(thdev); u32 scrpd; int i; int ret = -EBUSY; diff --git a/drivers/platform/x86/intel_pstore_pram.c b/drivers/platform/x86/intel_pstore_pram.c index 78dd9c3b08c5..8c8b1291545f 100644 --- a/drivers/platform/x86/intel_pstore_pram.c +++ b/drivers/platform/x86/intel_pstore_pram.c @@ -95,7 +95,7 @@ static int register_pram_dev(unsigned long mem_address, } pr_info("registered pram device, addr=0x%lx, size=0x%lx\n", - pram_data->mem_address, pram_data->mem_size); + (unsigned long)pram_data->mem_address, (unsigned long)pram_data->mem_size); return 0; } diff --git a/drivers/staging/android/sbl/sblbc.c b/drivers/staging/android/sbl/sblbc.c index 6c2d7ed6c2ee..d77700b70e25 100644 --- a/drivers/staging/android/sbl/sblbc.c +++ b/drivers/staging/android/sbl/sblbc.c @@ -116,7 +116,7 @@ static size_t write_data_to_nvram(char *data, size_t size) for (i = 0; i < size; i++) { - pr_err("Kernel Addr=0x%X, data=0x%X\n", NVRAM_START_ADDRESS + offset + i, *(unsigned char *)(data + i)); + pr_err("Kernel Addr=0x%X, data=0x%X\n", (unsigned int)(NVRAM_START_ADDRESS + offset + i), (unsigned int)(*(unsigned char *)(data + i))); } offset += size; @@ -311,7 +311,6 @@ static int sblbc_reboot_notifier_call(struct notifier_block *notifier, __func__, ret); } -done: return NOTIFY_DONE; } From 8fc6e472ff16b8933aafe9fd9852ac6c0d163dac Mon Sep 17 00:00:00 2001 From: zhouji3x Date: Mon, 19 Mar 2018 12:50:29 +0800 Subject: [PATCH 0584/1103] staging/android: Virtual Slimboot boot Control driver Virtual Slimboot is bootloader of Android Guest OS run on hypervisor this patch Set reboot target in NVRAM - register to reboot event - create CDATA Virtual Slimboot code following reboot target Signed-off-by: zhouji3x --- drivers/staging/android/Kconfig | 1 + drivers/staging/android/Makefile | 1 + drivers/staging/android/vsbl/Kconfig | 8 + drivers/staging/android/vsbl/Makefile | 1 + drivers/staging/android/vsbl/vsblbc.c | 262 ++++++++++++++++++++++++++ 5 files changed, 273 insertions(+) create mode 100644 drivers/staging/android/vsbl/Kconfig create mode 100644 drivers/staging/android/vsbl/Makefile create mode 100644 drivers/staging/android/vsbl/vsblbc.c diff --git a/drivers/staging/android/Kconfig b/drivers/staging/android/Kconfig index 9f6b54a3c90f..0b793ab6d15f 100644 --- a/drivers/staging/android/Kconfig +++ b/drivers/staging/android/Kconfig @@ -26,6 +26,7 @@ config ANDROID_VSOC source "drivers/staging/android/ion/Kconfig" source "drivers/staging/android/abl/Kconfig" source "drivers/staging/android/sbl/Kconfig" +source "drivers/staging/android/vsbl/Kconfig" endif # if ANDROID diff --git a/drivers/staging/android/Makefile b/drivers/staging/android/Makefile index fb345067b7f0..864902159d0c 100644 --- a/drivers/staging/android/Makefile +++ b/drivers/staging/android/Makefile @@ -3,6 +3,7 @@ ccflags-y += -I$(src) # needed for trace events obj-y += ion/ obj-$(CONFIG_ABL_BOOTLOADER_CONTROL) += abl/ obj-$(CONFIG_SBL_BOOTLOADER_CONTROL) += sbl/ +obj-$(CONFIG_VSBL_BOOTLOADER_CONTROL) += vsbl/ obj-$(CONFIG_ASHMEM) += ashmem.o obj-$(CONFIG_ANDROID_VSOC) += vsoc.o diff --git a/drivers/staging/android/vsbl/Kconfig b/drivers/staging/android/vsbl/Kconfig new file mode 100644 index 000000000000..bb53cf922685 --- /dev/null +++ b/drivers/staging/android/vsbl/Kconfig @@ -0,0 +1,8 @@ +config VSBL_BOOTLOADER_CONTROL + tristate "vSBL Bootloader Control module" + default n + help + This driver installs a reboot hook, such that if reboot() is + invoked with a string argument, the corresponding ABL Action + is written in CMOS data, in order to be processed by ABL on + reboot. diff --git a/drivers/staging/android/vsbl/Makefile b/drivers/staging/android/vsbl/Makefile new file mode 100644 index 000000000000..8ce038941fc6 --- /dev/null +++ b/drivers/staging/android/vsbl/Makefile @@ -0,0 +1 @@ +obj-$(CONFIG_VSBL_BOOTLOADER_CONTROL) += vsblbc.o diff --git a/drivers/staging/android/vsbl/vsblbc.c b/drivers/staging/android/vsbl/vsblbc.c new file mode 100644 index 000000000000..527a174f0130 --- /dev/null +++ b/drivers/staging/android/vsbl/vsblbc.c @@ -0,0 +1,262 @@ +/* + * vsblbc: control vSBL bootloaders + * Copyright (c) 2013-2017, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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. + */ + +#include +#include +#include +#include +#include +#include +#include + +#define MODULE_NAME "vsblbc" + +/* RTC read and write */ +static inline unsigned char cmos_read_ext_bank(u8 addr) +{ + outb(addr, RTC_PORT(4)); + return inb(RTC_PORT(5)); +} +#define CMOS_READ_EXT(a) cmos_read_ext_bank(a) + +static inline void cmos_write_ext_bank(u8 val, u8 addr) +{ + outb(addr, RTC_PORT(4)); + outb(val, RTC_PORT(5)); +} +#define CMOS_WRITE_EXT(v, a) cmos_write_ext_bank(v, a) + +/* vSBL Conventions */ +#define NVRAM_START_ADDRESS 0x10 + +#define _USERCMD_(cmd, len) (((cmd) << 5) | ((len) & 0x1f)) +#define USERCMD_END _USERCMD_(0, 0) +#define USERCMD_ACTION _USERCMD_(7, 1) + +#define CDATA_TAG_USER_CMD 0x4d +#define NVRAM_VALID_FLAG 0x12 + +#define CRC32C_POLYNOMIAL 0x82F63B78 /* CRC32C Castagnoli */ + +union _cdata_header { + uint32_t data; + struct { + unsigned ncond : 2; + unsigned length : 10; + unsigned flags : 4; + unsigned version: 4; + unsigned tag : 12; + }; +}; + +struct nvram_reboot_cmd { + char action; + char target; + char end; + char padding; +} __packed; + +struct name2id { + const char *name; + int id; +}; + +struct nvram_msg { + char magic; + char size; + union _cdata_header cdata_header; + char *cdata_payload; + size_t cdata_payload_size; + uint32_t crc; +} __packed; + +static const struct name2id NAME2ID[] = { + { "main", 0x00 }, + { "android", 0x00 }, + { "bootloader", 0x01 }, + { "fastboot", 0x01 }, + { "elk", 0x02 }, + { "recovery", 0x03 }, + { "crashmode", 0x04 }, + { "dnx", 0x05 }, + { "cli", 0x10 }, +}; + +static size_t offset; /* memorize offset between each call */ + +static size_t write_data_to_nvram(char *data, size_t size) +{ + int i; + unsigned long flags; + + spin_lock_irqsave(&rtc_lock, flags); + for (i = 0; i < size; i++) + CMOS_WRITE_EXT(*(data + i), NVRAM_START_ADDRESS + offset + i); + + offset += size; + spin_unlock_irqrestore(&rtc_lock, flags); + + return i; +} + +static void write_msg_to_nvram(struct nvram_msg *nvram_msg) +{ + /* Ensure to start from top : only one command expected */ + offset = 0; + write_data_to_nvram((void*)nvram_msg, + offsetof(struct nvram_msg, cdata_payload)); + write_data_to_nvram((void*)(nvram_msg->cdata_payload), + nvram_msg->cdata_payload_size); + write_data_to_nvram((void*)&(nvram_msg->crc), sizeof(nvram_msg->crc)); +} + +/* Compute CRC for one byte (shift register-based: one bit at a time). */ +static uint32_t crc32c_byte(uint32_t crc, unsigned byte) +{ + int i; + uint32_t c; + + for (i = 0 ; i < 8 ; i += 1) { + c = (crc ^ byte) & 1; + if (c) + crc = (crc >> 1) ^ CRC32C_POLYNOMIAL; + else + crc = (crc >> 1); + byte >>= 1; + } + + return crc; +} + +/* Compute CRC for a given buffer. */ +static uint32_t crc32c_buf(uint32_t crc, const void *addr, unsigned len) +{ + unsigned i; + + for (i = 0 ; i < len ; i += 1) + crc = crc32c_byte(crc, *(uint8_t *)(addr + i)); + + return crc; +} + +static uint32_t crc32c_msg(struct nvram_msg *nvram_msg) +{ + uint32_t crc; + + crc = crc32c_buf(~0, nvram_msg, + offsetof(struct nvram_msg, cdata_payload)); + crc = crc32c_buf(crc, nvram_msg->cdata_payload, + nvram_msg->cdata_payload_size); + return crc; +} + +static int reboot_target_name2id(const char *name) +{ + size_t i; + + for (i = 0; i < ARRAY_SIZE(NAME2ID); i++) + if (!strcmp(NAME2ID[i].name, name)) + return NAME2ID[i].id; + + return -EINVAL; +} + +static int set_reboot_target(const char *name) +{ + int id; + struct nvram_msg msg; + struct nvram_reboot_cmd reboot_cmd; + union _cdata_header cdh; + + if (name == NULL) { + pr_err("Error in %s: NULL target\n", __func__); + return -EINVAL; + } + + id = reboot_target_name2id(name); + if (id < 0) { + pr_err("Error in %s: '%s' is not a valid target\n", + __func__, name); + return -EINVAL; + } + + cdh.data = 0; + cdh.length = 2; /* 2*32 bits, from header to padding */ + cdh.tag = CDATA_TAG_USER_CMD; + + memset(&reboot_cmd, 0, sizeof(reboot_cmd)); + memset(&msg, 0, sizeof(msg)); + msg.magic = NVRAM_VALID_FLAG; + msg.cdata_header.data = cdh.data; + reboot_cmd.action = USERCMD_ACTION; + + reboot_cmd.target = id; + msg.cdata_payload = (void*)&reboot_cmd; + msg.cdata_payload_size = sizeof(reboot_cmd); + msg.size = offsetof(struct nvram_msg, cdata_payload) + + sizeof(reboot_cmd) + sizeof(msg.crc); + msg.crc = crc32c_msg(&msg); + + write_msg_to_nvram(&msg); + + return 0; +} + +static int vsblbc_reboot_notifier_call(struct notifier_block *notifier, + unsigned long what, void *data) +{ + const char *target = (const char *)data; + int ret; + + if (what != SYS_RESTART) + return NOTIFY_DONE; + + if (target[0] != '\0') { + ret = set_reboot_target(target); + if (ret) + pr_err("%s: Failed to set reboot target, ret=%d\n", + __func__, ret); + } + + return NOTIFY_DONE; +} + +static struct notifier_block vsblbc_reboot_notifier = { + .notifier_call = vsblbc_reboot_notifier_call, +}; + +static int __init vsblbc_init(void) +{ + int ret; + + ret = register_reboot_notifier(&vsblbc_reboot_notifier); + if (ret) { + pr_err(MODULE_NAME ": unable to register reboot notifier\n"); + return ret; + } + + return 0; +} + +module_init(vsblbc_init); + +static void __exit vsblbc_exit(void) +{ + unregister_reboot_notifier(&vsblbc_reboot_notifier); +} +module_exit(vsblbc_exit); + +MODULE_AUTHOR("Guillaume Betous "); +MODULE_DESCRIPTION("Virtual Slimboot boot control driver"); +MODULE_LICENSE("GPL v2"); From 2426bcd59ef512c16daeeb7deeababd332a0f549 Mon Sep 17 00:00:00 2001 From: "Zhou, JianfengX" Date: Tue, 3 Apr 2018 11:54:57 +0800 Subject: [PATCH 0585/1103] vsblbc: only built on X86 virtual slimboot boot control driver is for X86 only. add depends on X86 to Kconfig Signed-off-by: Zhou, JianfengX --- drivers/staging/android/vsbl/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/staging/android/vsbl/Kconfig b/drivers/staging/android/vsbl/Kconfig index bb53cf922685..465fed22ca6b 100644 --- a/drivers/staging/android/vsbl/Kconfig +++ b/drivers/staging/android/vsbl/Kconfig @@ -1,5 +1,6 @@ config VSBL_BOOTLOADER_CONTROL tristate "vSBL Bootloader Control module" + depends on X86 default n help This driver installs a reboot hook, such that if reboot() is From 181c72a2ac99499a6c96179587e67210a2aed06d Mon Sep 17 00:00:00 2001 From: "Jiang, YaoX" Date: Sun, 8 Apr 2018 13:04:15 +0800 Subject: [PATCH 0586/1103] ablbc: print log target before panic If panic in ablbc due to cansend called failure, the log of dm-verity will not print. Log target before panic, so we can know the reason of restarting system. Signed-off-by: Jiang, YaoX --- drivers/staging/android/abl/ablbc.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/drivers/staging/android/abl/ablbc.c b/drivers/staging/android/abl/ablbc.c index 73a3e34958c8..1cb31fa1af9a 100644 --- a/drivers/staging/android/abl/ablbc.c +++ b/drivers/staging/android/abl/ablbc.c @@ -360,8 +360,14 @@ static int ablbc_reboot_notifier_call(struct notifier_block *notifier, done: #ifdef CONFIG_SEND_SLCAN_ENABLE - if (ret) + if (ret) { + if (!target) + pr_emerg("Restarting system\n"); + else + pr_emerg("Restarting system with command '%s'\n", target); + panic("ablbc failed to cummnunicate with IOC."); + } #endif return NOTIFY_DONE; } From d7b78c19b220b5841f6cccddbe73aa20576ae8f2 Mon Sep 17 00:00:00 2001 From: "Li, Hongli" Date: Mon, 16 Apr 2018 16:03:24 +0800 Subject: [PATCH 0587/1103] add dependent for intel trace hub Intel trace hub is only used for x86 platform. Only in X86 architecture, this function can be enabled. Signed-off-by: Li, Hongli --- drivers/hwtracing/intel_th/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/hwtracing/intel_th/Kconfig b/drivers/hwtracing/intel_th/Kconfig index dc22562a16fc..4be9d14de4fb 100644 --- a/drivers/hwtracing/intel_th/Kconfig +++ b/drivers/hwtracing/intel_th/Kconfig @@ -1,6 +1,6 @@ config INTEL_TH tristate "Intel(R) Trace Hub controller" - depends on HAS_DMA && HAS_IOMEM + depends on HAS_DMA && HAS_IOMEM && X86 help Intel(R) Trace Hub (TH) is a set of hardware blocks (subdevices) that produce, switch and output trace data from multiple hardware and From b6db6740eaca7e65785a1717800eeb1c862a416b Mon Sep 17 00:00:00 2001 From: "Li, Hongli" Date: Tue, 17 Apr 2018 14:15:05 +0800 Subject: [PATCH 0588/1103] intel_th: sync "intel_th_alloc" function for ACPI ACPI probe will call the function "intel_th_alloc" Sync it with the "intel_th_alloc" Signed-off-by: Li, Hongli --- drivers/hwtracing/intel_th/acpi.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/drivers/hwtracing/intel_th/acpi.c b/drivers/hwtracing/intel_th/acpi.c index 87bc3744755f..5aef93d75ff2 100644 --- a/drivers/hwtracing/intel_th/acpi.c +++ b/drivers/hwtracing/intel_th/acpi.c @@ -34,6 +34,13 @@ static const struct acpi_device_id intel_th_acpi_ids[] = { MODULE_DEVICE_TABLE(acpi, intel_th_acpi_ids); +static void intel_th_acpi_reset(struct intel_th *th) +{ + /* Software reset */ + + /* Always set FON for S0ix flow */ +} + static int intel_th_acpi_probe(struct platform_device *pdev) { struct acpi_device *adev = ACPI_COMPANION(&pdev->dev); @@ -45,7 +52,7 @@ static int intel_th_acpi_probe(struct platform_device *pdev) return -ENODEV; th = intel_th_alloc(&pdev->dev, (void *)id->driver_data, - pdev->resource, pdev->num_resources, -1); + pdev->resource, pdev->num_resources, -1,intel_th_acpi_reset); if (IS_ERR(th)) return PTR_ERR(th); From 403832d8691482410c490c9ed341458ab08ad234 Mon Sep 17 00:00:00 2001 From: "Li, Hongli" Date: Mon, 23 Apr 2018 10:12:52 +0800 Subject: [PATCH 0589/1103] abl and sbl are only used for x86 Automotive Boot Loader(abl) and slimboot boot loader(sbl) control driver are for X86 only. add depends on X86 to Kconfig Signed-off-by: Li, Hongli --- drivers/staging/android/abl/Kconfig | 2 ++ drivers/staging/android/sbl/Kconfig | 1 + 2 files changed, 3 insertions(+) diff --git a/drivers/staging/android/abl/Kconfig b/drivers/staging/android/abl/Kconfig index a9e81e7a7a23..3c0a2c566ba0 100644 --- a/drivers/staging/android/abl/Kconfig +++ b/drivers/staging/android/abl/Kconfig @@ -1,5 +1,6 @@ config ABL_BOOTLOADER_CONTROL tristate "ABL Bootloader Control module" + depends on X86 default n help This driver installs a reboot hook, such that if reboot() is @@ -9,6 +10,7 @@ config ABL_BOOTLOADER_CONTROL config SEND_SLCAN_ENABLE bool "control slcan protocol" + depends on X86 default n help This option control slcan protocol enable/disable in ablbc driver diff --git a/drivers/staging/android/sbl/Kconfig b/drivers/staging/android/sbl/Kconfig index f5f754f8d890..4b550cadcb40 100644 --- a/drivers/staging/android/sbl/Kconfig +++ b/drivers/staging/android/sbl/Kconfig @@ -1,5 +1,6 @@ config SBL_BOOTLOADER_CONTROL tristate "SBL Bootloader Control module" + depends on X86 default n help This driver installs a reboot hook, such that if reboot() is From fffee25edef80045805541051712261911265e44 Mon Sep 17 00:00:00 2001 From: jiangyao Date: Mon, 7 May 2018 13:10:25 +0800 Subject: [PATCH 0590/1103] Fix compile warning in watchdog Compile warnings when make it on arch i386,fix it in iTCO_wdt.c Signed-off-by: jiangyao --- drivers/watchdog/iTCO_wdt.c | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/drivers/watchdog/iTCO_wdt.c b/drivers/watchdog/iTCO_wdt.c index 9a2a9b6b865c..26cdfd4126c1 100755 --- a/drivers/watchdog/iTCO_wdt.c +++ b/drivers/watchdog/iTCO_wdt.c @@ -448,29 +448,29 @@ static const struct watchdog_ops iTCO_wdt_ops = { static int iTCO_pretimeout(unsigned int cmd, struct pt_regs *unused_regs) { - resource_size_t tco_base_address; + resource_size_t tco_base_address; - /* Prevent re-entrance */ - if (iTCO_wdt_sub.pretimeout_occurred) - return NMI_HANDLED; + /* Prevent re-entrance */ + if (iTCO_wdt_sub.pretimeout_occurred) + return NMI_HANDLED; - tco_base_address = iTCO_wdt_sub.tco_base_address; + tco_base_address = iTCO_wdt_sub.tco_base_address; - /* Check the NMI is from the TCO first expiration */ - if (inw(TCO1_STS_sub(tco_base_address)) & 0x8) { - iTCO_wdt_sub.pretimeout_occurred = true; + /* Check the NMI is from the TCO first expiration */ + if (inw(TCO1_STS_sub(tco_base_address)) & 0x8) { + iTCO_wdt_sub.pretimeout_occurred = true; - /* Forward next expiration */ - outw(iTCO_wdt_sub.second_to_ticks, TCOv2_TMR_sub(tco_base_address)); - outw(0x01, TCO_RLD_sub(tco_base_address)); + /* Forward next expiration */ + outw(iTCO_wdt_sub.second_to_ticks, TCOv2_TMR_sub(tco_base_address)); + outw(0x01, TCO_RLD_sub(tco_base_address)); - trigger_all_cpu_backtrace(); - panic_timeout = 0; - panic("Kernel Watchdog"); - return NMI_HANDLED; - } + trigger_all_cpu_backtrace(); + panic_timeout = 0; + panic("Kernel Watchdog"); + return NMI_HANDLED; + } - return NMI_DONE; + return NMI_DONE; } /* From bc99b7e09eefef689094b10d388fab1059a0f459 Mon Sep 17 00:00:00 2001 From: "Tang, Haoyu" Date: Sat, 19 May 2018 17:08:40 +0800 Subject: [PATCH 0591/1103] ablbc: revert patches of panic and revise logic Revert "ablbc: panic if fail to call cansend" Revert "ablbc: print log target before panic" Move setting boot-target before sending SLCAN message user-space service ioc_reboot send SLCAN reboot message to IOC during reboot phase also. cansend is a double safety at here, panic is not necessary for failure case. move the setting of reboot-target before sending SLCAN message in order to ensure the correct target set even failing to send SLCAN message. This reverts commit 1bbadb2ec5c7eb33a56bb77edee6ee73941bb1ea. This reverts commit bde2660bf18d560f029f759c1bb182d1b3a74119. Change-Id: Ic892a88b03c861e00198cb11211c82203f91779e Signed-off-by: Tang, Haoyu --- drivers/staging/android/abl/ablbc.c | 24 +++++++----------------- 1 file changed, 7 insertions(+), 17 deletions(-) diff --git a/drivers/staging/android/abl/ablbc.c b/drivers/staging/android/abl/ablbc.c index 1cb31fa1af9a..59154f5e10ef 100644 --- a/drivers/staging/android/abl/ablbc.c +++ b/drivers/staging/android/abl/ablbc.c @@ -337,10 +337,16 @@ static int ablbc_reboot_notifier_call(struct notifier_block *notifier, unsigned long what, void *data) { const char *target = (const char *)data; - int ret = 0; + int ret; if (what != SYS_RESTART) return NOTIFY_DONE; + if (target[0] != '\0') { + ret = set_reboot_target(target); + if (ret) + pr_err("%s: Failed to set reboot target, ret=%d\n", + __func__, ret); + } ret = execute_slcan_command((const char **)suppress_heartbeat); if (ret) @@ -349,26 +355,10 @@ static int ablbc_reboot_notifier_call(struct notifier_block *notifier, ret = execute_slcan_command((const char **)reboot_request); if (ret) goto done; - if (target[0] != '\0') { - ret = set_reboot_target(target); - if (ret) - pr_err("%s: Failed to set reboot target, ret=%d\n", - __func__, ret); - } ret = execute_slcan_command((const char **)cold_reset); done: -#ifdef CONFIG_SEND_SLCAN_ENABLE - if (ret) { - if (!target) - pr_emerg("Restarting system\n"); - else - pr_emerg("Restarting system with command '%s'\n", target); - - panic("ablbc failed to cummnunicate with IOC."); - } -#endif return NOTIFY_DONE; } From ff0b07a3ea23380b1e741e369358a15674ad6817 Mon Sep 17 00:00:00 2001 From: "Duan, YayongX" Date: Mon, 14 May 2018 21:11:48 +0000 Subject: [PATCH 0592/1103] Kernel: Fix below potential issue in dvctrace c file Issue description: 1. when "buf" is NULL, there is not a protection. 2. if there is not only one USB descriptor was saved in 'buf', below two rare issues maybe occur: 1) (descriptor length*2) > 'strlen(buf)' 'sscanf()' stops at the null terminator. No overflow, but the mismatch could be indicative of another problem resulting potentially in data loss. 2) (descriptor length*2) < 'strlen(buf)' No overflow, but data loss if there are more descriptors than accounted for in the length. Patch effect: 1. Ensuring 'buf' is not NULL. 2. Checking 'strlen(buf)' is consist of several (descriptor length*2) to make descriptor can be read correctly. 3. remove useless 'while(len)' loop that check real descriptor to enhance performance. 4. merge the code for 'lenth' 'type' and 'sub_type' checking to enhance performance. Change-Id: I4abd5194d91423b5b6201911881d0656f2055a59 Tracked-On: Signed-off-by: Duan, YayongX Signed-off-by: Tian, Baofeng --- drivers/bus/dvctrace.c | 63 +++++++++++++++++++----------------------- 1 file changed, 29 insertions(+), 34 deletions(-) diff --git a/drivers/bus/dvctrace.c b/drivers/bus/dvctrace.c index 915c42593edb..a44b1b3e5490 100644 --- a/drivers/bus/dvctrace.c +++ b/drivers/bus/dvctrace.c @@ -42,45 +42,34 @@ static int count_descriptors(const char *buf, size_t size) { size_t off = 0; - int i, j, count = 0; - u8 len, tmp; + int j, count0 = 0, count1 = 0; + u8 len, type, sub_type; + if (buf == NULL) + return -EINVAL; + /* Ensuring 'buf' only has serval correct format USB descriptors */ + while(off < size) { + j = sscanf(buf + off, "%2hhx", &len); + if (j <= 0) + return -EINVAL; + /* skip related data and space to read next 'len': aa bb cc ... */ + off += len * 3; + count0++; + } + if (off != size) + return -EINVAL; + off = 0; DVCT_IN(); - while (off < size) { + /* Check every USB descriptor type and sub_type */ + while (count0--) { /*the length*/ - j = sscanf(buf + off, "%2hhx%n", &len, &i); - if (!j) - break; - if (j < 0 || len < 4) - return -EINVAL; - len--; - off += i; - - /*Type*/ - j = sscanf(buf + off, "%2hhx%n", &tmp, &i); - if (j <= 0 || tmp != USB_DT_CS_INTERFACE) + j = sscanf(buf + off, "%2hhx%2hhx%2hhx", &len, &type, &sub_type); + if (j <= 0 || type != USB_DT_CS_INTERFACE || sub_type < DC_INPUT_CONNECTION || sub_type > DC_DEBUG_ATTRIBUTES) return -EINVAL; - len--; - off += i; - - /*Sub Type*/ - j = sscanf(buf + off, "%2hhx%n", &tmp, &i); - if (j <= 0 || tmp < DC_INPUT_CONNECTION - || tmp > DC_DEBUG_ATTRIBUTES) - return -EINVAL; - len--; - off += i; - - while (len) { - j = sscanf(buf + off, "%2hhx%n", &tmp, &i); - if (j <= 0) - return -EINVAL; - len--; - off += i; - } - count++; + off += len * 3; + count1++; } - return count; + return count1; } /* Parse @buf and get a pointer to the descriptor identified @@ -91,6 +80,8 @@ static u8 *get_descriptor(const char *buf, size_t size, int idx) int i, j, k, count = 0; u8 len, tmp, *ret = NULL; + if (buf == NULL) + return ERR_PTR(-EINVAL); DVCT_IN(); while (off < size) { j = sscanf(buf + off, "%2hhx%n", &len, &i); @@ -318,6 +309,8 @@ static int count_strings(const char *buf, size_t size) size_t off = 0, slen; int i = 0, j, desc_offset, offset; + if (buf == NULL) + return -EINVAL; DVCT_IN(); while (off < size) { j = sscanf(buf + off, "%d.%d: %n", &desc_offset, &offset, &i); @@ -346,6 +339,8 @@ static char *get_string(const char *buf, size_t size, int index, int i, j; char *ret = ERR_PTR(-EINVAL); + if (buf == NULL) + return ERR_PTR(-EINVAL); DVCT_IN(); while (off < size) { j = sscanf(buf + off, "%d.%d: %n", desc_offset, offset, &i); From 9f7d748940d51ebcbb7c51d6c186c48b9919a08d Mon Sep 17 00:00:00 2001 From: btian1 Date: Mon, 16 Jul 2018 16:21:18 +0000 Subject: [PATCH 0593/1103] kernel: ipc1: add a read-only attribute for PMC ssram base Add a read only attribute for PMC ssram base address, in ipc driver. This address will be used to retrieve intel crashlog data from PMC SSRAM. when fw crash happens, the logs are saved to ssram before reset. After reset, crashlogd read logs data out from this address for crash analysis. Signed-off-by: btian1 Signed-off-by: xinanlux --- drivers/platform/x86/intel_pmc_ipc.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/drivers/platform/x86/intel_pmc_ipc.c b/drivers/platform/x86/intel_pmc_ipc.c index e7edc8c63936..a3ee24e49e96 100644 --- a/drivers/platform/x86/intel_pmc_ipc.c +++ b/drivers/platform/x86/intel_pmc_ipc.c @@ -606,15 +606,28 @@ static ssize_t intel_pmc_ipc_northpeak_store(struct device *dev, } return (ssize_t)count; } +static ssize_t intel_ssrambase_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + if (ipcdev.telem_punit_ssram_base > TELEM_PUNIT_SSRAM_OFFSET) + return scnprintf(buf, 64, "%x\n", + ipcdev.telem_punit_ssram_base - TELEM_PUNIT_SSRAM_OFFSET); + else + return scnprintf(buf, 64, "%x\n", 0); +} static DEVICE_ATTR(simplecmd, S_IWUSR, NULL, intel_pmc_ipc_simple_cmd_store); static DEVICE_ATTR(northpeak, S_IWUSR, NULL, intel_pmc_ipc_northpeak_store); +static DEVICE_ATTR(ssrambase, S_IRUGO, + intel_ssrambase_show, NULL); static struct attribute *intel_ipc_attrs[] = { &dev_attr_northpeak.attr, &dev_attr_simplecmd.attr, + &dev_attr_ssrambase.attr, NULL }; From 2e983e5838cefd02e81587e941226bd70758a0f3 Mon Sep 17 00:00:00 2001 From: "Duan, YayongX" Date: Mon, 23 Jul 2018 09:03:57 +0000 Subject: [PATCH 0594/1103] Sbl: Klocwork: Fix the potential issue of sbl code - 'buf' is passed from a interface, which result in we cannot ensure what is involved in it. But there is not a security checking for copying 'buf' to 'name'. And this issue may cause a memory overflow of 'name'. - This patch add a checking for 'buf+3'(something will be copied to 'name' buffer) to keep no overflow occur in 'name' buffer. Change-Id: I9c57ba19bb5cec1849bd6b11aa87bd4e4e09ed52 Signed-off-by: Duan, YayongX --- drivers/staging/android/sbl/sblbc.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/staging/android/sbl/sblbc.c b/drivers/staging/android/sbl/sblbc.c index d77700b70e25..85865eb5d281 100644 --- a/drivers/staging/android/sbl/sblbc.c +++ b/drivers/staging/android/sbl/sblbc.c @@ -202,6 +202,11 @@ static ssize_t capsule_store(struct kobject *kobj, struct kobj_attribute *attr, device = (buf[0] == 'm' ? EMMC : SDCARD); partition = buf[1] - '0'; + if (strlen(buf+3) >= sizeof(name)) { + pr_err(MODULE_NAME " buf+3: %d is too long\n", strlen(buf+3)); + return -ENOMEM; + } + ret = sscanf(buf+3, "%s", name); pr_info(MODULE_NAME " capsule parameters (%d): DEVICE=%d PARTITION=%d NAME=%s\n", ret, device, partition, name); From 65c31ca59a34f47c01d6df97aabe14e221ffbefd Mon Sep 17 00:00:00 2001 From: zhouji3x Date: Mon, 30 Jul 2018 15:36:34 +0800 Subject: [PATCH 0595/1103] Flush CPU cache before reboot on panic This patch flush CPU cache when panic occurs, so data in the cache can be written back to RAM. This patch is neccessary for ramdump feature. If data in the cache is not written back to RAM before reboot, data pulled for offline analysis may be incorrect. Signed-off-by: zhouji3x --- kernel/panic.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/kernel/panic.c b/kernel/panic.c index 8b2e002d52eb..d4f06598eef8 100644 --- a/kernel/panic.c +++ b/kernel/panic.c @@ -29,6 +29,7 @@ #include #include #include +#include #define PANIC_TIMER_STEP 100 #define PANIC_BLINK_SPD 18 @@ -246,6 +247,9 @@ void panic(const char *fmt, ...) debug_locks_off(); console_flush_on_panic(); + /* Flush CPU cache */ + ACPI_FLUSH_CPU_CACHE(); + if (!panic_blink) panic_blink = no_blink; From b4dcb5295e8526c00bcd6f7624128e0d6aec149c Mon Sep 17 00:00:00 2001 From: "Duan, YayongX" Date: Thu, 2 Aug 2018 09:56:56 +0800 Subject: [PATCH 0596/1103] Debug: Warning: Only fix some build warnings - For intel_pmc_ipc.c: 'ipcdev.telem_punit_ssram_base' is long long unsigned int type, So it request a '%llx' match with it. - For sblbc.c: Function 'strlen' is 'sized_t' type, So it return a unsigned long int type, So here need a '%ld' matching. - For printk.c: Function 'console_solw_suspend' was already not used, So remove it. Change-Id: I22208302d125e8654592f45e200551eb9fa67a11 Signed-off-by: Duan, YayongX --- drivers/platform/x86/intel_pmc_ipc.c | 2 +- drivers/staging/android/sbl/sblbc.c | 2 +- kernel/printk/printk.c | 2 -- 3 files changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/platform/x86/intel_pmc_ipc.c b/drivers/platform/x86/intel_pmc_ipc.c index a3ee24e49e96..ffe0cac68694 100644 --- a/drivers/platform/x86/intel_pmc_ipc.c +++ b/drivers/platform/x86/intel_pmc_ipc.c @@ -611,7 +611,7 @@ static ssize_t intel_ssrambase_show(struct device *dev, char *buf) { if (ipcdev.telem_punit_ssram_base > TELEM_PUNIT_SSRAM_OFFSET) - return scnprintf(buf, 64, "%x\n", + return scnprintf(buf, 64, "%llx\n", ipcdev.telem_punit_ssram_base - TELEM_PUNIT_SSRAM_OFFSET); else return scnprintf(buf, 64, "%x\n", 0); diff --git a/drivers/staging/android/sbl/sblbc.c b/drivers/staging/android/sbl/sblbc.c index 85865eb5d281..3d354fd31e23 100644 --- a/drivers/staging/android/sbl/sblbc.c +++ b/drivers/staging/android/sbl/sblbc.c @@ -203,7 +203,7 @@ static ssize_t capsule_store(struct kobject *kobj, struct kobj_attribute *attr, device = (buf[0] == 'm' ? EMMC : SDCARD); partition = buf[1] - '0'; if (strlen(buf+3) >= sizeof(name)) { - pr_err(MODULE_NAME " buf+3: %d is too long\n", strlen(buf+3)); + pr_err(MODULE_NAME " buf+3: %ld is too long\n", strlen(buf+3)); return -ENOMEM; } diff --git a/kernel/printk/printk.c b/kernel/printk/printk.c index 578cad2f66a0..948187ea2f59 100644 --- a/kernel/printk/printk.c +++ b/kernel/printk/printk.c @@ -377,8 +377,6 @@ __packed __aligned(4) */ DEFINE_RAW_SPINLOCK(logbuf_lock); -/* Give the posibility to temporary disable slow (!CON_FAST) consoles */ -static atomic_t console_slow_suspended = ATOMIC_INIT(0); /* Keep the number of slow suspend in check */ #define MAX_SLOW_SUSPEND_COUNT (50) From 3d8f977307c2965921bca8e8b552ff84c1c11ec0 Mon Sep 17 00:00:00 2001 From: "Luo, XinanX" Date: Thu, 14 Jun 2018 23:29:47 +0000 Subject: [PATCH 0597/1103] wdt:reboot at second watchdog timeout This patch is to implement watchdog dump kernel logs on first timeout, and then reboot on second timeout. Only for the platforms that iTCO watchdog can't trigger NMI interrupts. Because iTCO can only trigger SMI, need BIOS/Bootloader use SMM to translate it to NMI. But on some platforms, BIOS/Bootloader doesn't support SMM. Modifications: 1, Add a perf_event to trigger periodic NMI interrupt, because some platforms' iTCO can't trigger NMI interrupts for use. 2, Disable reboot bit when first timeout, then it will not reboot after about 2s(HW default). It will waiting for a NMI interrupt and check the iTCO status. If timeout, then dump kernel log and enable the second timeout in iTCO to reboot system. 3, This implement are controlled by a config: CONFIG_ITCO_NO_NMI_INTR defaut n. If iTCO can't trigger NMI interrupts, set CONFIG_ITCO_NO_NMI_INTR=y to enable it. Change-Id: Id41cb5f5b480c1614afa53732dd92a8febf1362f Signed-off-by: Luo, XinanX --- drivers/watchdog/Kconfig | 9 ++++ drivers/watchdog/iTCO_wdt.c | 94 ++++++++++++++++++++++++++++++++++++- 2 files changed, 101 insertions(+), 2 deletions(-) diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig index 8bf318d44a88..b2dd4ac2fc63 100644 --- a/drivers/watchdog/Kconfig +++ b/drivers/watchdog/Kconfig @@ -1083,6 +1083,15 @@ config ITCO_WDT To compile this driver as a module, choose M here: the module will be called iTCO_wdt. +config ITCO_NO_NMI_INTR + bool "Intel TCO Watchdog NMI interrupt state" + depends on ITCO_WDT + default n + help + On some platforms, the iTCO watchdog can't trigger a NMI + interrupt on the first timeout. Need to set this flag to enable + another way instead. + config ITCO_VENDOR_SUPPORT bool "Intel TCO Timer/Watchdog Specific Vendor Support" depends on ITCO_WDT diff --git a/drivers/watchdog/iTCO_wdt.c b/drivers/watchdog/iTCO_wdt.c index 26cdfd4126c1..0285c0be8907 100755 --- a/drivers/watchdog/iTCO_wdt.c +++ b/drivers/watchdog/iTCO_wdt.c @@ -70,6 +70,7 @@ #include #include +#include #include "iTCO_vendor.h" /* Address definitions for the TCO */ @@ -124,6 +125,9 @@ static struct { unsigned int iTCO_version; bool pretimeout_occurred; unsigned int second_to_ticks; +#ifdef CONFIG_ITCO_NO_NMI_INTR + struct iTCO_wdt_private *wdt_priv; +#endif } iTCO_wdt_sub; @@ -257,6 +261,61 @@ static void iTCO_wdt_no_reboot_bit_setup(struct iTCO_wdt_private *p, p->no_reboot_priv = p; } +/* iTCO need to wait for a NMI interrupt on first timeout, in order to dump kernel +** logs in the interrupt handler-iTCO_pretimeout. But on some platforms iTCO timeout +** can't triger NMI interrupts.These functions will trigger periodly NMI +** interrupts by perf_event. In iTCO_pretimeout will check iTCO status, if timeout, then +** dump logs and reboot. +*/ +#ifdef CONFIG_ITCO_NO_NMI_INTR +static struct perf_event_attr wd_hw_attr = { + .type = PERF_TYPE_HARDWARE, + .config = PERF_COUNT_HW_CPU_CYCLES, + .size = sizeof(struct perf_event_attr), + .pinned = 1, + .disabled = 1, +}; +static struct perf_event *itco_perf_evt = NULL; +static unsigned int itco_perf_sample_period = 10; //default 10 seconds +static int itco_perf_event_create(void) +{ + unsigned int cpu = smp_processor_id(); + struct perf_event_attr *wd_attr; + + wd_attr = &wd_hw_attr; + wd_attr->sample_period = (u64)(cpu_khz) * 1000 * itco_perf_sample_period; + + /* Try to register using hardware perf events */ + itco_perf_evt = perf_event_create_kernel_counter(wd_attr, cpu, NULL, + NULL, NULL); + if (IS_ERR(itco_perf_evt)) { + pr_err("Perf event create on CPU %d failed with %ld\n", cpu, + PTR_ERR(itco_perf_evt)); + return PTR_ERR(itco_perf_evt); + } + return 0; +} +static void itco_perf_event_enable(void) +{ + if (itco_perf_evt) + perf_event_enable(itco_perf_evt); +} +static void itco_perf_event_disable(void) +{ + if (itco_perf_evt) + perf_event_disable(itco_perf_evt); +} +static void itco_perf_event_remove(void) +{ + if (itco_perf_evt) + perf_event_release_kernel(itco_perf_evt); +} +#else +static int itco_perf_event_create(void) { return 0; } +static void itco_perf_event_enable(void) { } +static void itco_perf_event_disable(void) { } +static void itco_perf_event_remove(void) { } +#endif static int iTCO_wdt_start(struct watchdog_device *wd_dev) { @@ -267,16 +326,27 @@ static int iTCO_wdt_start(struct watchdog_device *wd_dev) if (force_no_reboot) return -EIO; + itco_perf_event_enable(); + spin_lock(&p->io_lock); iTCO_vendor_pre_start(p->smi_res, wd_dev->timeout); +#ifdef CONFIG_ITCO_NO_NMI_INTR + /* Set the NO_REBOOT bit to prevent reboots of first timeout */ + if (p->update_no_reboot_bit(p->no_reboot_priv, true)) { + spin_unlock(&p->io_lock); + pr_err("failed to set NO_REBOOT flag\n"); + return -EIO; + } +#else /* disable chipset's NO_REBOOT bit */ if (p->update_no_reboot_bit(p->no_reboot_priv, false)) { spin_unlock(&p->io_lock); pr_err("failed to reset NO_REBOOT flag, reboot disabled by hardware/BIOS/rc_cmd\n"); return -EIO; } +#endif /* Force the timer to its reload value by writing to the TCO_RLD register */ @@ -302,6 +372,8 @@ static int iTCO_wdt_stop(struct watchdog_device *wd_dev) struct iTCO_wdt_private *p = watchdog_get_drvdata(wd_dev); unsigned int val; + itco_perf_event_disable(); + spin_lock(&p->io_lock); iTCO_vendor_pre_stop(p->smi_res); @@ -449,6 +521,9 @@ static const struct watchdog_ops iTCO_wdt_ops = { static int iTCO_pretimeout(unsigned int cmd, struct pt_regs *unused_regs) { resource_size_t tco_base_address; +#ifdef CONFIG_ITCO_NO_NMI_INTR + struct iTCO_wdt_private *p = iTCO_wdt_sub.wdt_priv; +#endif /* Prevent re-entrance */ if (iTCO_wdt_sub.pretimeout_occurred) @@ -459,6 +534,10 @@ static int iTCO_pretimeout(unsigned int cmd, struct pt_regs *unused_regs) /* Check the NMI is from the TCO first expiration */ if (inw(TCO1_STS_sub(tco_base_address)) & 0x8) { iTCO_wdt_sub.pretimeout_occurred = true; +#ifdef CONFIG_ITCO_NO_NMI_INTR + /*Enable reboot for the second timeout*/ + p->update_no_reboot_bit(p->no_reboot_priv, false); +#endif /* Forward next expiration */ outw(iTCO_wdt_sub.second_to_ticks, TCOv2_TMR_sub(tco_base_address)); @@ -606,11 +685,20 @@ static int iTCO_wdt_probe(struct platform_device *pdev) return ret; } + /*create a perf_event to generate nmi interrupt*/ + ret = itco_perf_event_create(); + if (ret != 0) { + pr_err("cannot create perf event (err=%d)\n", ret); + return ret; + } + /* init vars that used for nmi handler */ iTCO_wdt_sub.iTCO_version = p->iTCO_version; iTCO_wdt_sub.second_to_ticks = seconds_to_ticks(p, 10); iTCO_wdt_sub.tco_base_address = TCOBASE(p); - +#ifdef CONFIG_ITCO_NO_NMI_INTR + iTCO_wdt_sub.wdt_priv = p; +#endif ret = register_nmi_handler(NMI_LOCAL, iTCO_pretimeout, 0 ,"iTCO_wdt"); if (ret != 0) { pr_err("cannot register nmi handler (err=%d)\n", ret); @@ -628,8 +716,10 @@ static int iTCO_wdt_remove(struct platform_device *pdev) struct iTCO_wdt_private *p = platform_get_drvdata(pdev); /* Stop the timer before we leave */ - if (!nowayout) + if (!nowayout) { iTCO_wdt_stop(&p->wddev); + itco_perf_event_remove(); + } return 0; } From e018b9054f1d529e53dd87141bd2b7b3c250dd2f Mon Sep 17 00:00:00 2001 From: "Chen, ZhiminX" Date: Wed, 4 Jul 2018 10:47:08 +0800 Subject: [PATCH 0598/1103] Increase COMMAND_LINE_SIZE from 2048 to 4096 The real size of kernel command line exceeds 2048. Signed-off-by: Chen, ZhiminX --- arch/x86/include/asm/setup.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/x86/include/asm/setup.h b/arch/x86/include/asm/setup.h index ae13bc974416..9490cb15a275 100644 --- a/arch/x86/include/asm/setup.h +++ b/arch/x86/include/asm/setup.h @@ -4,7 +4,7 @@ #include -#define COMMAND_LINE_SIZE 2048 +#define COMMAND_LINE_SIZE 4096 #include #include From c160576dcabab0ec161ecd6e13ff99dccbbdc0e1 Mon Sep 17 00:00:00 2001 From: Ming Tan Date: Wed, 8 Aug 2018 22:04:48 +0800 Subject: [PATCH 0599/1103] When kernel crash, save the reboot reason to EFI variable. Change-Id: Ib785ffdd8991c485b1ab3733f9883dc67ec9053e Signed-off-by: Jeremy Compostella Signed-off-by: Xinanx Luo --- drivers/firmware/efi/efibc.c | 51 +++++++++++++++++++++++++++++++++--- 1 file changed, 48 insertions(+), 3 deletions(-) diff --git a/drivers/firmware/efi/efibc.c b/drivers/firmware/efi/efibc.c index 503bbe2a9d49..ff145f39e8ca 100644 --- a/drivers/firmware/efi/efibc.c +++ b/drivers/firmware/efi/efibc.c @@ -19,6 +19,15 @@ #include #include +#define REBOOT_REASON_CRASH "kernel_panic" +#define REBOOT_REASON_NORMAL "reboot" +#define REBOOT_REASON_SHUTDOWN "shutdown" +#define REBOOT_REASON_WATCHDOG "watchdog" + +#define WATCHDOG_KERNEL_H "Watchdog" +#define WATCHDOG_KERNEL_S "softlockup" +#define WATCHDOG_KERNEL_D "Software Watchdog" + static void efibc_str_to_str16(const char *str, efi_char16_t *str16) { size_t i; @@ -67,11 +76,11 @@ static int efibc_set_variable(const char *name, const char *value) static int efibc_reboot_notifier_call(struct notifier_block *notifier, unsigned long event, void *data) { - const char *reason = "shutdown"; + const char *reason = REBOOT_REASON_SHUTDOWN; int ret; if (event == SYS_RESTART) - reason = "reboot"; + reason = REBOOT_REASON_NORMAL; ret = efibc_set_variable("LoaderEntryRebootReason", reason); if (ret || !data) @@ -82,10 +91,41 @@ static int efibc_reboot_notifier_call(struct notifier_block *notifier, return NOTIFY_DONE; } +static int efibc_panic_notifier_call(struct notifier_block *notifier, + unsigned long what, void *data) +{ + int i; + char *str = data; + const char *reason = REBOOT_REASON_CRASH; + const char *watchdogs[] = { + WATCHDOG_KERNEL_H, + WATCHDOG_KERNEL_S, + WATCHDOG_KERNEL_D + }; + + + if (str) { + for (i = 0; i < ARRAY_SIZE(watchdogs); i++) { + if (strncmp(str, watchdogs[i], strlen(watchdogs[i])) == 0) { + reason = REBOOT_REASON_WATCHDOG; + break; + } + } + } + + efibc_set_variable("LoaderEntryRebootReason", reason); + + return NOTIFY_DONE; +} + static struct notifier_block efibc_reboot_notifier = { .notifier_call = efibc_reboot_notifier_call, }; +static struct notifier_block paniced = { + .notifier_call = efibc_panic_notifier_call, +}; + static int __init efibc_init(void) { int ret; @@ -94,8 +134,12 @@ static int __init efibc_init(void) return -ENODEV; ret = register_reboot_notifier(&efibc_reboot_notifier); - if (ret) + if (ret) { pr_err("unable to register reboot notifier\n"); + return ret; + } + + atomic_notifier_chain_register(&panic_notifier_list, &paniced); return ret; } @@ -104,6 +148,7 @@ module_init(efibc_init); static void __exit efibc_exit(void) { unregister_reboot_notifier(&efibc_reboot_notifier); + atomic_notifier_chain_unregister(&panic_notifier_list, &paniced); } module_exit(efibc_exit); From 0d24d87d5588febf60fdb96ada1d9d08ff948a21 Mon Sep 17 00:00:00 2001 From: Guneshwor Singh Date: Fri, 7 Jul 2017 08:40:49 +0530 Subject: [PATCH 0600/1103] [FOR CNL FPGA] Add support for CNL FPGA This includes IMR allocation Change-Id: If53609cd8626c5ab94a418b48b241f6a8572f5fb Signed-off-by: Guneshwor Singh --- sound/soc/intel/Kconfig | 7 +++ sound/soc/intel/common/sst-dsp-priv.h | 1 + sound/soc/intel/skylake/cnl-sst.c | 76 +++++++++++++++++++++++++++ 3 files changed, 84 insertions(+) diff --git a/sound/soc/intel/Kconfig b/sound/soc/intel/Kconfig index 0caa1f4eb94d..221283d83619 100644 --- a/sound/soc/intel/Kconfig +++ b/sound/soc/intel/Kconfig @@ -123,7 +123,14 @@ config SND_SOC_ACPI_INTEL_MATCH # this option controls the compilation of ACPI matching tables and # helpers and is not meant to be selected by the user. +config SND_SOC_INTEL_CNL_FPGA + tristate "Enable CNL FPGA board settings" + help + Select Y if you are using FPGA. + If unsure select "N". + endif ## SND_SOC_INTEL_SST_TOPLEVEL + # ASoC codec drivers source "sound/soc/intel/boards/Kconfig" diff --git a/sound/soc/intel/common/sst-dsp-priv.h b/sound/soc/intel/common/sst-dsp-priv.h index 363145716a6d..acf06a4f5144 100644 --- a/sound/soc/intel/common/sst-dsp-priv.h +++ b/sound/soc/intel/common/sst-dsp-priv.h @@ -322,6 +322,7 @@ struct sst_dsp { u32 intr_status; const struct firmware *fw; struct snd_dma_buffer dmab; + struct snd_dma_buffer dsp_fw_buf; }; /* Size optimised DRAM/IRAM memcpy */ diff --git a/sound/soc/intel/skylake/cnl-sst.c b/sound/soc/intel/skylake/cnl-sst.c index 245df1067ba8..f7c832b300d0 100644 --- a/sound/soc/intel/skylake/cnl-sst.c +++ b/sound/soc/intel/skylake/cnl-sst.c @@ -27,6 +27,7 @@ #include #include #include +#include #include "../common/sst-dsp.h" #include "../common/sst-dsp-priv.h" @@ -52,11 +53,69 @@ #define CNL_ADSP_FW_HDR_OFFSET 0x2000 #define CNL_ROM_CTRL_DMA_ID 0x9 +#define CNL_IMR_MEMSIZE 0x400000 /*4MB*/ +#define HDA_ADSP_REG_ADSPCS_IMR_CACHED_TLB_START 0x100 +#define HDA_ADSP_REG_ADSPCS_IMR_UNCACHED_TLB_START 0x200 +#define HDA_ADSP_REG_ADSPCS_IMR_SIZE 0x8 + +#ifndef writeq +static inline void writeq(u64 val, void __iomem *addr) +{ + writel(((u32) (val)), addr); + writel(((u32) (val >> 32)), addr + 4); +} +#endif + +/* Needed for presilicon platform based on FPGA */ +static int cnl_fpga_alloc_imr(struct sst_dsp *ctx) +{ + u32 pages; + u32 fw_size = CNL_IMR_MEMSIZE; + int ret; + + ret = ctx->dsp_ops.alloc_dma_buf(ctx->dev, &ctx->dsp_fw_buf, fw_size); + + if (ret < 0) { + dev_err(ctx->dev, "Alloc buffer for base fw failed: %x\n", ret); + return ret; + } + + pages = (fw_size + PAGE_SIZE - 1) >> PAGE_SHIFT; + + dev_dbg(ctx->dev, "sst_cnl_fpga_alloc_imr pages=0x%x\n", pages); + set_memory_uc((unsigned long)ctx->dsp_fw_buf.area, pages); + + writeq(virt_to_phys(ctx->dsp_fw_buf.area) + 1, + ctx->addr.shim + HDA_ADSP_REG_ADSPCS_IMR_CACHED_TLB_START); + writeq(virt_to_phys(ctx->dsp_fw_buf.area) + 1, + ctx->addr.shim + HDA_ADSP_REG_ADSPCS_IMR_UNCACHED_TLB_START); + + writel(CNL_IMR_MEMSIZE, ctx->addr.shim + + HDA_ADSP_REG_ADSPCS_IMR_CACHED_TLB_START + + HDA_ADSP_REG_ADSPCS_IMR_SIZE); + writel(CNL_IMR_MEMSIZE, ctx->addr.shim + + HDA_ADSP_REG_ADSPCS_IMR_UNCACHED_TLB_START + + HDA_ADSP_REG_ADSPCS_IMR_SIZE); + + memset(ctx->dsp_fw_buf.area, 0, fw_size); + + return 0; +} + +static inline void cnl_fpga_free_imr(struct sst_dsp *ctx) +{ + ctx->dsp_ops.free_dma_buf(ctx->dev, &ctx->dsp_fw_buf); +} + static int cnl_prepare_fw(struct sst_dsp *ctx, const void *fwdata, u32 fwsize) { int ret, stream_tag; + ret = cnl_fpga_alloc_imr(ctx); + if (ret < 0) + return ret; + stream_tag = ctx->dsp_ops.prepare(ctx->dev, 0x40, fwsize, &ctx->dmab); if (stream_tag <= 0) { dev_err(ctx->dev, "dma prepare failed: 0%#x\n", stream_tag); @@ -78,6 +137,21 @@ static int cnl_prepare_fw(struct sst_dsp *ctx, const void *fwdata, u32 fwsize) goto base_fw_load_failed; } + + for (ret = CNL_BASEFW_TIMEOUT; + ret > 0 && IS_ENABLED(CONFIG_SND_SOC_INTEL_CNL_FPGA); --ret) { + u32 reg = sst_dsp_shim_read(ctx, CNL_ADSP_REG_HIPCIDA); + + if (reg & CNL_ADSP_REG_HIPCIDA_DONE) { + sst_dsp_shim_update_bits_forced(ctx, + CNL_ADSP_REG_HIPCIDA, + CNL_ADSP_REG_HIPCIDA_DONE, + CNL_ADSP_REG_HIPCIDA_DONE); + break; + } + + mdelay(1); + } /* enable interrupt */ cnl_ipc_int_enable(ctx); cnl_ipc_op_int_enable(ctx); @@ -95,6 +169,7 @@ static int cnl_prepare_fw(struct sst_dsp *ctx, const void *fwdata, u32 fwsize) base_fw_load_failed: ctx->dsp_ops.cleanup(ctx->dev, &ctx->dmab, stream_tag); cnl_dsp_disable_core(ctx, SKL_DSP_CORE0_MASK); + cnl_fpga_free_imr(ctx); return ret; } @@ -490,6 +565,7 @@ void cnl_sst_dsp_cleanup(struct device *dev, struct skl_sst *ctx) cnl_ipc_free(&ctx->ipc); ctx->dsp->ops->free(ctx->dsp); + cnl_fpga_free_imr(ctx->dsp); } EXPORT_SYMBOL_GPL(cnl_sst_dsp_cleanup); From 78f77e3f3bad0a35ff8c8e486f5d2972c96f7518 Mon Sep 17 00:00:00 2001 From: Guneshwor Singh Date: Thu, 14 Apr 2016 12:02:14 +0530 Subject: [PATCH 0601/1103] [FOR CNL FPGA] Load nhlt from firmware instead of debugfs Change-Id: I316804db785699f28359b35455790c95cccacd10 Signed-off-by: Guneshwor Singh Reviewed-on: Reviewed-by: Shah, Hardik T Tested-by: Shah, Hardik T Signed-off-by: Guneshwor Singh --- sound/soc/intel/skylake/skl.c | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/sound/soc/intel/skylake/skl.c b/sound/soc/intel/skylake/skl.c index 1d17be0f78a0..2d0efe8233a4 100644 --- a/sound/soc/intel/skylake/skl.c +++ b/sound/soc/intel/skylake/skl.c @@ -886,6 +886,7 @@ static int skl_probe(struct pci_dev *pci, { struct skl *skl; struct hdac_bus *bus = NULL; + const struct firmware __maybe_unused *nhlt_fw = NULL; int err; /* we use ext core ops, so provide NULL for ops here */ @@ -903,6 +904,8 @@ static int skl_probe(struct pci_dev *pci, device_disable_async_suspend(bus->dev); +#if !IS_ENABLED(CONFIG_SND_SOC_INTEL_CNL_FPGA) + skl->nhlt_version = skl_get_nhlt_version(bus->dev); skl->nhlt = skl_nhlt_init(bus->dev); if (skl->nhlt == NULL) { @@ -916,7 +919,25 @@ static int skl_probe(struct pci_dev *pci, skl_nhlt_update_topology_bin(skl); - pci_set_drvdata(skl->pci, bus); +#else + if (request_firmware(&nhlt_fw, "intel/nhlt_blob.bin", bus->dev)) { + dev_err(bus->dev, "Request nhlt fw failed, continuing..\n"); + goto nhlt_continue; + } + + skl->nhlt = devm_kzalloc(&pci->dev, nhlt_fw->size, GFP_KERNEL); + if (skl->nhlt == NULL) + return -ENOMEM; + memcpy(skl->nhlt, nhlt_fw->data, nhlt_fw->size); + release_firmware(nhlt_fw); + +nhlt_continue: +#endif + pci_set_drvdata(skl->pci, ebus); + +#if !IS_ENABLED(CONFIG_SND_SOC_INTEL_CNL_FPGA) + skl_dmic_data.dmic_num = skl_get_dmic_geo(skl); +#endif /* check if dsp is there */ if (bus->ppcap) { From 3fdb12831660dd4454fb71073b779b473236534d Mon Sep 17 00:00:00 2001 From: Guneshwor Singh Date: Tue, 17 Nov 2015 05:27:39 +0530 Subject: [PATCH 0602/1103] [FOR CNL FPGA] Add facility to load ROM via debugfs For FPGA images without ROM inbuilt, this adds feature to load the ROM manually via debugfs Change-Id: I653ffd6d6ee735403d0137af33dc2ad701b478ed Signed-off-by: Guneshwor Singh Reviewed-on: Reviewed-by: Shah, Hardik T Tested-by: Shah, Hardik T --- sound/soc/intel/Makefile | 3 + sound/soc/intel/fpga/Makefile | 4 + sound/soc/intel/fpga/hda_intel_cnl_fpga.c | 274 ++++++++++++++++++++++ 3 files changed, 281 insertions(+) create mode 100644 sound/soc/intel/fpga/Makefile create mode 100644 sound/soc/intel/fpga/hda_intel_cnl_fpga.c diff --git a/sound/soc/intel/Makefile b/sound/soc/intel/Makefile index 8160520fd74c..ece7c2b228d1 100644 --- a/sound/soc/intel/Makefile +++ b/sound/soc/intel/Makefile @@ -10,3 +10,6 @@ obj-$(CONFIG_SND_SOC_INTEL_SKYLAKE) += skylake/ # Machine support obj-$(CONFIG_SND_SOC) += boards/ + +# FPGA support +obj-$(CONFIG_SND_SOC_INTEL_CNL_FPGA) += fpga/ diff --git a/sound/soc/intel/fpga/Makefile b/sound/soc/intel/fpga/Makefile new file mode 100644 index 000000000000..f434f7bcfd62 --- /dev/null +++ b/sound/soc/intel/fpga/Makefile @@ -0,0 +1,4 @@ +# for intel cnl fpga device +snd-hda-intel-cnl-fpga-objs := hda_intel_cnl_fpga.o +obj-$(CONFIG_SND_SOC_INTEL_CNL_FPGA) += snd-hda-intel-cnl-fpga.o + diff --git a/sound/soc/intel/fpga/hda_intel_cnl_fpga.c b/sound/soc/intel/fpga/hda_intel_cnl_fpga.c new file mode 100644 index 000000000000..934a1a98b64a --- /dev/null +++ b/sound/soc/intel/fpga/hda_intel_cnl_fpga.c @@ -0,0 +1,274 @@ +/* + * Intel HDA Cannonlake FPGA driver + * + * Copyright (C) 2014, Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License version + * 2 as published by the Free Software Foundation. + * + * 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. + * + */ + +#include +#include +#include +#include + +#define HDA_INTEL_CNL_FPGA_ROM_SIZE (16 * 1024) +#define HDA_INTEL_CNL_FPGA_ROM_BAR_IDX 2 +#define HDA_INTEL_CNL_FPGA_ROM_KEY_OFFSET 0x805c +#define HDA_INTEL_CNL_FPGA_ROM_MEM_OFFSET 0xc000 + +struct hda_intel_cnl_fpga_dev { + void __iomem *rom_mem_base; + struct pci_dev *pdev; +}; + +static u32 hda_intel_cnl_fpga_readl( + struct hda_intel_cnl_fpga_dev *fpgadev, + u32 offset) +{ + u32 val; + + val = readl((fpgadev)->rom_mem_base + offset); + printk(KERN_INFO "%s: TG@%x == %x\n", __func__, offset, val); + return val; +} + +static void hda_intel_cnl_fpga_writel( + struct hda_intel_cnl_fpga_dev *fpgadev, + u32 offset, + u32 val) +{ + printk("%s: TG@%x <= %x\n", __func__, offset, val); + writel(val, (fpgadev)->rom_mem_base + offset); +} + +static ssize_t hda_intel_cnl_fpga_rom_read(struct file *file, + char __user *buffer, size_t count, loff_t *ppos) +{ + struct hda_intel_cnl_fpga_dev *fpgadev = file->private_data; + int i, size; + u32 *buf; + + printk(KERN_INFO "hda_intel_cnl_fpga_rom_read_in\n"); + + dev_dbg(&fpgadev->pdev->dev, "%s: pbuf: %p, *ppos: 0x%llx", + __func__, buffer, *ppos); + + size = HDA_INTEL_CNL_FPGA_ROM_SIZE; + + if (*ppos >= size) + return 0; + if (*ppos + count > size) + count = size - *ppos; + + buf = kzalloc(size, GFP_KERNEL); + if (!buf) { + dev_dbg(&fpgadev->pdev->dev, " %s: kzalloc failed, aborting\n", + __func__); + return -ENOMEM; + } + size = size / sizeof(*buf); + + printk(KERN_INFO "%s: bar: 0x%p\n", __func__, fpgadev->rom_mem_base); + + for (i = 0; i < size; i++) { + hda_intel_cnl_fpga_writel( + fpgadev, HDA_INTEL_CNL_FPGA_ROM_KEY_OFFSET, 0); + + buf[i] = hda_intel_cnl_fpga_readl(fpgadev, + HDA_INTEL_CNL_FPGA_ROM_MEM_OFFSET + i * sizeof(*buf)); + } + + if (copy_to_user(buffer, buf, count)) { + dev_err(&fpgadev->pdev->dev, " %s: copy_to_user failed, aborting\n", + __func__); + return -EFAULT; + } + kfree(buf); + + *ppos += count; + + dev_dbg(&fpgadev->pdev->dev, "%s: *ppos: 0x%llx, count: %zu", + __func__, *ppos, count); + + printk(KERN_INFO "hda_intel_cnl_fpga_rom_read_out\n"); + + return count; +} + +static ssize_t hda_intel_cnl_fpga_rom_write(struct file *file, + const char __user *buffer, size_t count, loff_t *ppos) +{ + struct hda_intel_cnl_fpga_dev *fpgadev = file->private_data; + int i, size; + u32 *buf; + + printk(KERN_INFO "hda_intel_cnl_fpga_rom_write_in\n"); + + dev_dbg(&fpgadev->pdev->dev, "%s: pbuf: %p, *ppos: 0x%llx", + __func__, buffer, *ppos); + + size = HDA_INTEL_CNL_FPGA_ROM_SIZE; + + if (*ppos >= size) + return 0; + if (*ppos + count > size) + count = size - *ppos; + + buf = kzalloc(size, GFP_KERNEL); + if (!buf) { + dev_err(&fpgadev->pdev->dev, " %s: kzalloc failed, aborting\n", + __func__); + return -ENOMEM; + } + + if (copy_from_user(buf, buffer, count)) { + dev_err(&fpgadev->pdev->dev, " %s: copy_from_user failed, aborting\n", + __func__); + return -EFAULT; + } + + size = size / sizeof(*buf); + + for (i = 0; i < size; i++) { + hda_intel_cnl_fpga_writel( + fpgadev, HDA_INTEL_CNL_FPGA_ROM_KEY_OFFSET, 0); + + hda_intel_cnl_fpga_writel(fpgadev, + HDA_INTEL_CNL_FPGA_ROM_MEM_OFFSET + i * sizeof(*buf), + buf[i]); + } + + kfree(buf); + *ppos += count; + + dev_dbg(&fpgadev->pdev->dev, "%s: *ppos: 0x%llx, count: %zu", + __func__, *ppos, count); + + printk(KERN_INFO "hda_intel_cnl_fpga_rom_write_out\n"); + + return count; +} + +static const struct file_operations hda_intel_cnl_fpga_rom_fops = { + .owner = THIS_MODULE, + .open = simple_open, + .read = hda_intel_cnl_fpga_rom_read, + .write = hda_intel_cnl_fpga_rom_write, +}; + +static int hda_intel_cnl_fpga_probe(struct pci_dev *pdev, + const struct pci_device_id *pci_id) +{ + struct hda_intel_cnl_fpga_dev *fpgadev; + int ret = 0; + + ret = pci_enable_device(pdev); + if (ret) { + dev_err(&pdev->dev, "%s: pci_enable_device failed: %d\n", + __func__, ret); + return ret; + } + + fpgadev = kzalloc(sizeof(struct hda_intel_cnl_fpga_dev), GFP_KERNEL); + if (!fpgadev) { + dev_err(&pdev->dev, "%s: kzalloc failed, aborting.\n", + __func__); + ret = -ENOMEM; + goto err_alloc; + } + + printk(KERN_INFO "%s: bar: 0, start 0x%p\n", __func__, + (void *)pci_resource_start(pdev, 0)); + printk(KERN_INFO "%s: bar: 0, flags 0x%lx\n", __func__, + pci_resource_flags(pdev, 0)); + printk(KERN_INFO "%s: bar: 1, start 0x%p\n", __func__, + (void *)pci_resource_start(pdev, 1)); + printk(KERN_INFO "%s: bar: 1, flags 0x%lx\n", __func__, + pci_resource_flags(pdev, 1)); + printk(KERN_INFO "%s: bar: 2, start 0x%p\n", __func__, + (void *)pci_resource_start(pdev, 2)); + printk(KERN_INFO "%s: bar: 2, flags 0x%lx\n", __func__, + pci_resource_flags(pdev, 2)); + printk(KERN_INFO "%s: bar: 3, start 0x%p\n", __func__, + (void *)pci_resource_start(pdev, 3)); + printk(KERN_INFO "%s: bar: 3, flags 0x%lx\n", __func__, + pci_resource_flags(pdev, 3)); + + fpgadev->rom_mem_base + = pci_ioremap_bar(pdev, HDA_INTEL_CNL_FPGA_ROM_BAR_IDX); + printk("%s: bar: 0x%p\n", __func__, fpgadev->rom_mem_base); + if (!fpgadev->rom_mem_base) { + dev_err(&pdev->dev, "%s: ioremap failed, aborting\n", + __func__); + ret = -ENXIO; + goto err_map; + } + + fpgadev->pdev = pdev; + + pci_set_drvdata(pdev, fpgadev); + + if (!debugfs_create_file("cnl_oed_rom", 0644, NULL, + fpgadev, &hda_intel_cnl_fpga_rom_fops)) { + dev_err(&pdev->dev, "%s: cannot create debugfs entry.\n", + __func__); + ret = -ENODEV; + goto err_map; + } + + return ret; + +err_map: + if (fpgadev->rom_mem_base) { + iounmap(fpgadev->rom_mem_base); + fpgadev->rom_mem_base = NULL; + } + kfree(fpgadev); +err_alloc: + pci_disable_device(pdev); + return ret; +} + +static void hda_intel_cnl_fpga_remove(struct pci_dev *pdev) +{ + struct hda_intel_cnl_fpga_dev *fpgadev = pci_get_drvdata(pdev); + + printk(KERN_INFO "hda_intel_cnl_fpga_remove_in"); + /* unmap PCI memory space, mapped during device init. */ + if (fpgadev->rom_mem_base) { + iounmap(fpgadev->rom_mem_base); + fpgadev->rom_mem_base = NULL; + } + pci_disable_device(pdev); + kfree(fpgadev); + + printk(KERN_INFO "hda_intel_cnl_fpga_remove_out"); +} + +/* PCI IDs */ +static const struct pci_device_id hda_intel_cnl_fpga_ids[] = { + { PCI_DEVICE(0x8086, 0xF501) }, + { 0, } +}; +MODULE_DEVICE_TABLE(pci, hda_intel_cnl_fpga_ids); + +/* pci_driver definition */ +static struct pci_driver hda_intel_cnl_fpga_driver = { + .name = KBUILD_MODNAME, + .id_table = hda_intel_cnl_fpga_ids, + .probe = hda_intel_cnl_fpga_probe, + .remove = hda_intel_cnl_fpga_remove, +}; + +module_pci_driver(hda_intel_cnl_fpga_driver); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Intel HDA CNL FPGA driver"); From 89966bfa2eb51b8e5960b23eca9fcb7d53e23d1c Mon Sep 17 00:00:00 2001 From: Ramesh Babu Date: Thu, 30 Apr 2015 12:36:21 +0530 Subject: [PATCH 0603/1103] [FOR CNL FPGA] ASoC: mfd: Intel changes for WM8281 integration on K4.0 The changes are related to arizona MFD changes to adopt both FPGA and actual board on kernel 4.0 Change-Id: I40767f73db9f3583797218636e2102190a70cea9 Signed-off-by: Ramesh Babu Signed-off-by: Guneshwor Singh --- drivers/mfd/arizona-i2c.c | 196 +++++++++++++++++++++++++++++++++++++- drivers/mfd/arizona-irq.c | 3 +- 2 files changed, 197 insertions(+), 2 deletions(-) diff --git a/drivers/mfd/arizona-i2c.c b/drivers/mfd/arizona-i2c.c index 5fe12961cfe5..84d29dec0ae1 100644 --- a/drivers/mfd/arizona-i2c.c +++ b/drivers/mfd/arizona-i2c.c @@ -23,6 +23,141 @@ #include "arizona.h" +/************************************************************/ +#include +#include +#include +#include +#include +#include + +#ifdef CONFIG_SND_SOC_INTEL_CNL_FPGA +/***********WM8280 1.8V REGULATOR*************/ +static struct regulator_consumer_supply vflorida1_consumer[] = { + REGULATOR_SUPPLY("AVDD", "0-001a"), + REGULATOR_SUPPLY("DBVDD1", "0-001a"), + REGULATOR_SUPPLY("LDOVDD", "0-001a"), + REGULATOR_SUPPLY("CPVDD", "0-001a"), + REGULATOR_SUPPLY("DBVDD2", "0-001a"), + REGULATOR_SUPPLY("DBVDD3", "0-001a"), +}; + +/***********WM8280 5V REGULATOR*************/ +static struct regulator_consumer_supply vflorida2_consumer[] = { + REGULATOR_SUPPLY("SPKVDDL", "0-001a"), + REGULATOR_SUPPLY("SPKVDDR", "0-001a"), +}; +#else +/***********WM8280 1.8V REGULATOR*************/ +static struct regulator_consumer_supply vflorida1_consumer[] = { + REGULATOR_SUPPLY("AVDD", "i2c-INT34C1:00"), + REGULATOR_SUPPLY("DBVDD1", "i2c-INT34C1:00"), + REGULATOR_SUPPLY("LDOVDD", "i2c-INT34C1:00"), + REGULATOR_SUPPLY("CPVDD", "i2c-INT34C1:00"), + REGULATOR_SUPPLY("DBVDD2", "i2c-INT34C1:00"), + REGULATOR_SUPPLY("DBVDD3", "i2c-INT34C1:00"), +}; + +/***********WM8280 5V REGULATOR*************/ +static struct regulator_consumer_supply vflorida2_consumer[] = { + REGULATOR_SUPPLY("SPKVDDL", "i2c-INT34C1:00"), + REGULATOR_SUPPLY("SPKVDDR", "i2c-INT34C1:00"), +}; +#endif + +static struct regulator_init_data vflorida1_data = { + .constraints = { + .always_on = 1, + }, + .num_consumer_supplies = ARRAY_SIZE(vflorida1_consumer), + .consumer_supplies = vflorida1_consumer, +}; + +static struct fixed_voltage_config vflorida1_config = { + .supply_name = "DC_1V8", + .microvolts = 1800000, + .gpio = -EINVAL, + .init_data = &vflorida1_data, +}; + +static struct platform_device vflorida1_device = { + .name = "reg-fixed-voltage", + .id = PLATFORM_DEVID_AUTO, + .dev = { + .platform_data = &vflorida1_config, + }, +}; + +static struct regulator_init_data vflorida2_data = { + .constraints = { + .always_on = 1, + }, + .num_consumer_supplies = ARRAY_SIZE(vflorida2_consumer), + .consumer_supplies = vflorida2_consumer, +}; + +static struct fixed_voltage_config vflorida2_config = { + .supply_name = "DC_5V", + .microvolts = 3700000, + .gpio = -EINVAL, + .init_data = &vflorida2_data, +}; + +static struct platform_device vflorida2_device = { + .name = "reg-fixed-voltage", + .id = PLATFORM_DEVID_AUTO, + .dev = { + .platform_data = &vflorida2_config, + }, +}; + +/***********WM8280 Codec Driver platform data*************/ +static const struct arizona_micd_range micd_ctp_ranges[] = { + { .max = 11, .key = BTN_0 }, + { .max = 28, .key = BTN_1 }, + { .max = 54, .key = BTN_2 }, + { .max = 100, .key = BTN_3 }, + { .max = 186, .key = BTN_4 }, + { .max = 430, .key = BTN_5 }, +}; + +static struct arizona_micd_config micd_modes[] = { + /*{Acc Det on Micdet1, Use Micbias2 for detection, + * Set GPIO to 1 to selecte this polarity}*/ + { 0, 2, 1 }, +}; + +static struct arizona_pdata __maybe_unused florida_pdata = { + .reset = 0, /*No Reset GPIO from AP, use SW reset*/ + .irq_flags = IRQF_TRIGGER_LOW | IRQF_ONESHOT, + .clk32k_src = ARIZONA_32KZ_MCLK2, /*Onboard OSC provides 32K on MCLK2*/ + /* + * IN1 uses both MICBIAS1 and MICBIAS2 based on jack polarity, + * the below values in dmic_ref only has meaning for DMIC's and not + * AMIC's + */ +#ifdef CONFIG_SND_SOC_INTEL_CNL_FPGA + .dmic_ref = {ARIZONA_DMIC_MICBIAS1, ARIZONA_DMIC_MICBIAS3, 0, 0}, + .inmode = {ARIZONA_INMODE_DIFF, ARIZONA_INMODE_DMIC, 0, 0}, +#else + .dmic_ref = {ARIZONA_DMIC_MICBIAS1, 0, ARIZONA_DMIC_MICVDD, 0}, + .inmode = {ARIZONA_INMODE_SE, 0, ARIZONA_INMODE_DMIC, 0}, +#endif + .gpio_base = 0, /* Base allocated by gpio core */ + .micd_pol_gpio = 2, /* GPIO3 (offset 2 from gpio_base) of the codec */ + .micd_configs = micd_modes, + .num_micd_configs = ARRAY_SIZE(micd_modes), + .micd_force_micbias = true, +}; + +/************************************************************/ +#ifdef CONFIG_SND_SOC_INTEL_CNL_FPGA +static struct i2c_board_info arizona_i2c_device = { + I2C_BOARD_INFO("wm8280", 0x1A), + .platform_data = &florida_pdata, +}; +#endif + static int arizona_i2c_probe(struct i2c_client *i2c, const struct i2c_device_id *id) { @@ -31,10 +166,16 @@ static int arizona_i2c_probe(struct i2c_client *i2c, unsigned long type; int ret; + pr_debug("%s:%d\n", __func__, __LINE__); if (i2c->dev.of_node) type = arizona_of_get_type(&i2c->dev); +#ifdef CONFIG_SND_SOC_INTEL_CNL_FPGA + else + type = WM8280; +#else else type = id->driver_data; +#endif switch (type) { case WM5102: @@ -105,6 +246,13 @@ static const struct i2c_device_id arizona_i2c_id[] = { }; MODULE_DEVICE_TABLE(i2c, arizona_i2c_id); +#ifndef CONFIG_SND_SOC_INTEL_CNL_FPGA +static struct acpi_device_id __maybe_unused arizona_acpi_match[] = { + { "INT34C1", WM8280 }, + { } +}; +#endif + static struct i2c_driver arizona_i2c_driver = { .driver = { .name = "arizona", @@ -116,7 +264,53 @@ static struct i2c_driver arizona_i2c_driver = { .id_table = arizona_i2c_id, }; -module_i2c_driver(arizona_i2c_driver); +static int __init arizona_modinit(void) +{ + int ret = 0; +#ifdef CONFIG_SND_SOC_INTEL_CNL_FPGA + struct i2c_adapter *adapter; + struct i2c_client *client; +#endif + + pr_debug("%s Entry\n", __func__); + /***********WM8280 Register Regulator*************/ + platform_device_register(&vflorida1_device); + platform_device_register(&vflorida2_device); + +#ifdef CONFIG_SND_SOC_INTEL_CNL_FPGA + adapter = i2c_get_adapter(0); + pr_debug("%s:%d\n", __func__, __LINE__); + if (adapter) { + client = i2c_new_device(adapter, &arizona_i2c_device); + pr_debug("%s:%d\n", __func__, __LINE__); + if (!client) { + pr_err("can't create i2c device %s\n", + arizona_i2c_device.type); + i2c_put_adapter(adapter); + pr_debug("%s:%d\n", __func__, __LINE__); + return -ENODEV; + } + } else { + pr_err("adapter is NULL\n"); + return -ENODEV; + } +#endif + pr_debug("%s:%d\n", __func__, __LINE__); + ret = i2c_add_driver(&arizona_i2c_driver); + + pr_debug("%s Exit\n", __func__); + + return ret; +} + +module_init(arizona_modinit); + +static void __exit arizona_modexit(void) +{ + i2c_del_driver(&arizona_i2c_driver); +} + +module_exit(arizona_modexit); MODULE_DESCRIPTION("Arizona I2C bus interface"); MODULE_AUTHOR("Mark Brown "); diff --git a/drivers/mfd/arizona-irq.c b/drivers/mfd/arizona-irq.c index a307832d7e45..bf50bb41e4a7 100644 --- a/drivers/mfd/arizona-irq.c +++ b/drivers/mfd/arizona-irq.c @@ -377,7 +377,8 @@ int arizona_irq_init(struct arizona *arizona) ret = request_threaded_irq(arizona->irq, NULL, arizona_irq_thread, flags, "arizona", arizona); - if (ret != 0) { + /* FPGA board doesn't have irq line */ + if (ret != 0 && !IS_ENABLED(CONFIG_SND_SOC_INTEL_CNL_FPGA)) { dev_err(arizona->dev, "Failed to request primary IRQ %d: %d\n", arizona->irq, ret); goto err_main_irq; From dd490d74123e59bd655882c00e8cd0fcd394ee6e Mon Sep 17 00:00:00 2001 From: Guneshwor Singh Date: Wed, 30 Dec 2015 13:26:18 +0530 Subject: [PATCH 0604/1103] ASoC: Intel: Add CNL Machine Driver with code wm8281 This adds machine driver for CNL and WM8281 codec. Change-Id: Id910363272f6c1e29bb8963b33f59bf84a800a36 Signed-off-by: Guneshwor Singh --- sound/soc/intel/boards/cnl_wm8281.c | 643 ++++++++++++++++++++++++++++ 1 file changed, 643 insertions(+) create mode 100644 sound/soc/intel/boards/cnl_wm8281.c diff --git a/sound/soc/intel/boards/cnl_wm8281.c b/sound/soc/intel/boards/cnl_wm8281.c new file mode 100644 index 000000000000..a6a328f1ac7c --- /dev/null +++ b/sound/soc/intel/boards/cnl_wm8281.c @@ -0,0 +1,643 @@ +/* + * cnl_wm8281.c - ASOC Machine driver for CNL + * + * Copyright (C) 2016 Wolfson Micro + * Copyright (C) 2016 Intel Corp + * Author: Samreen Nilofer + * + * Based on + * moor_dpcm_florida.c - ASOC Machine driver for Intel Moorefield MID platform + * Copyright (C) 2014 Wolfson Micro + * Copyright (C) 2014 Intel Corp + * Author: Nikesh Oswal + * Praveen Diwakar + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * 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; version 2 of the License. + * + * 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. + * + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include "../../codecs/wm5110.h" +#include "../../codecs/wm8998.h" + + +/* Codec PLL output clk rate */ +#define CODEC_SYSCLK_RATE 49152000 +/* Input clock to codec at MCLK1 PIN */ +#define CODEC_IN_MCLK1_RATE 19200000 +/* Input clock to codec at MCLK2 PIN */ +#define CODEC_IN_MCLK2_RATE 32768 +/* Input bit clock to codec */ +#define CODEC_IN_BCLK_RATE 4800000 + +/* define to select between MCLK1 and MCLK2 input to codec as its clock */ +#define CODEC_IN_MCLK1 1 +#define CODEC_IN_MCLK2 2 +#define CODEC_IN_BCLK 3 + +#define SLOT_MASK(x) ((1 << x) - 1) + +struct cnl_mc_private { + u8 pmic_id; + void __iomem *osc_clk0_reg; + int bt_mode; +}; +static const struct snd_soc_pcm_stream dai_params_codec = { + .formats = SNDRV_PCM_FMTBIT_S24_LE, + .rate_min = 48000, + .rate_max = 48000, + .channels_min = 2, + .channels_max = 2, +}; +static const struct snd_soc_pcm_stream dai_params_modem = { + .formats = SNDRV_PCM_FMTBIT_S16_LE, + .rate_min = 48000, + .rate_max = 48000, + .channels_min = 2, + .channels_max = 2, +}; +static const struct snd_soc_pcm_stream dai_params_bt = { + .formats = SNDRV_PCM_FMTBIT_S16_LE, + .rate_min = 48000, + .rate_max = 48000, + .channels_min = 2, + .channels_max = 2, +}; + +/* set_osc_clk0- enable/disables the osc clock0 + * addr: address of the register to write to + * enable: bool to enable or disable the clock + */ +static inline void set_soc_osc_clk0(void __iomem *addr, bool enable) +{ + u32 osc_clk_ctrl; + + osc_clk_ctrl = readl(addr); + if (enable) + osc_clk_ctrl |= BIT(31); + else + osc_clk_ctrl &= ~(BIT(31)); + + pr_debug("%s: enable:%d val 0x%x\n", __func__, enable, osc_clk_ctrl); + + writel(osc_clk_ctrl, addr); +} + +static inline struct snd_soc_codec *cnl_florida_get_codec(struct snd_soc_card *card) +{ + bool found = false; + struct snd_soc_component *component; + + list_for_each_entry(component, &card->component_dev_list, card_list) { + if (!strstr(component->name, "wm5110-codec")) { + pr_debug("codec was %s", component->name); + continue; + } else { + found = true; + break; + } + } + if (found == false) { + pr_err("%s: cant find codec", __func__); + return NULL; + } + return component->codec; +} + +static struct snd_soc_dai *cnl_florida_get_codec_dai(struct snd_soc_card *card, + const char *dai_name) +{ + struct snd_soc_pcm_runtime *rtd; + + list_for_each_entry(rtd, &card->rtd_list, list) { + if (!strcmp(rtd->codec_dai->name, dai_name)) + return rtd->codec_dai; + } + pr_err("%s: unable to find codec dai\n", __func__); + /* this should never occur */ + WARN_ON(1); + return NULL; +} + +/* Function to switch the input clock for codec, When audio is in + * progress input clock to codec will be through MCLK1 which is 19.2MHz + * while in off state input clock to codec will be through 32KHz through + * MCLK2 + * card : Sound card structure + * src : Input clock source to codec + */ + +static int cnl_florida_set_codec_clk(struct snd_soc_codec *florida_codec, + int src) +{ + int ret; + + pr_debug("cnl_florida_set_codec_clk: source %d\n", src); + + /* reset FLL1 */ + snd_soc_codec_set_pll(florida_codec, WM5110_FLL1_REFCLK, + ARIZONA_FLL_SRC_NONE, 0, 0); + snd_soc_codec_set_pll(florida_codec, WM5110_FLL1, + ARIZONA_FLL_SRC_NONE, 0, 0); + + switch (src) { + case CODEC_IN_MCLK1: + /* + * Turn ON the PLL to generate required sysclk rate + * from MCLK1 + */ + ret = snd_soc_codec_set_pll(florida_codec, WM5110_FLL1, + ARIZONA_CLK_SRC_MCLK1, CODEC_IN_MCLK1_RATE, + CODEC_SYSCLK_RATE); + if (ret != 0) { + dev_err(florida_codec->dev, "Failed to enable FLL1 with Ref(MCLK) Clock Loop: %d\n", ret); + return ret; + } + break; + case CODEC_IN_BCLK: + /* + * Turn ON the PLL to generate required sysclk rate + * from BCLK + */ + ret = snd_soc_codec_set_pll(florida_codec, WM5110_FLL1, + ARIZONA_CLK_SRC_AIF1BCLK, CODEC_IN_BCLK_RATE, + CODEC_SYSCLK_RATE); + if (ret != 0) { + dev_err(florida_codec->dev, "Failed to enable FLL1 with Ref Clock Loop: %d\n", ret); + return ret; + } + + break; + default: + return -EINVAL; + } + + /*Switch to PLL*/ + ret = snd_soc_codec_set_sysclk(florida_codec, + ARIZONA_CLK_SYSCLK, ARIZONA_CLK_SRC_FLL1, + CODEC_SYSCLK_RATE, SND_SOC_CLOCK_IN); + if (ret != 0) { + dev_err(florida_codec->dev, "Failed to set SYSCLK to FLL1: %d\n", ret); + return ret; + } + + return 0; +} + + +static int cnl_clock_control(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *k, int event) +{ + + struct snd_soc_dapm_context *dapm = w->dapm; + struct snd_soc_card *card = dapm->card; + struct snd_soc_codec *florida_codec = cnl_florida_get_codec(card); + int ret = 0; + + if (!florida_codec) { + pr_err("%s: florida codec not found\n", __func__); + return -EINVAL; + } + if (SND_SOC_DAPM_EVENT_ON(event)) { + pr_info("%s %d Event On\n", __func__, __LINE__); + /* TODO: Ideally MCLK should be used to drive codec PLL + * currently we are using BCLK + */ + ret = cnl_florida_set_codec_clk(florida_codec, CODEC_IN_BCLK); + } else { + pr_info("%s %d Event Off\n", __func__, __LINE__); + /* TODO: Switch to 32K clock for saving power. */ + pr_info("Currently we are not switching to 32K PMIC clock\n"); + } + return ret; + +} +static const struct snd_soc_dapm_widget cnl_widgets[] = { + SND_SOC_DAPM_HP("Headphones", NULL), + SND_SOC_DAPM_SPK("Ext Spk", NULL), + SND_SOC_DAPM_SPK("EP", NULL), + SND_SOC_DAPM_MIC("AMIC", NULL), + SND_SOC_DAPM_MIC("DMIC", NULL), + SND_SOC_DAPM_MIC("SoC DMIC", NULL), + SND_SOC_DAPM_SUPPLY("Platform Clock", SND_SOC_NOPM, 0, 0, + cnl_clock_control, SND_SOC_DAPM_PRE_PMU| + SND_SOC_DAPM_POST_PMD), + +}; + +static int cnl_dmic_fixup(struct snd_soc_pcm_runtime *rtd, + struct snd_pcm_hw_params *params) +{ + struct snd_interval *channels = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_CHANNELS); + channels->min = channels->max = 2; + + return 0; +} + +static const struct snd_soc_dapm_route cnl_map[] = { + /* Headphones */ + { "Headphones", NULL, "HPOUT1L" }, + { "Headphones", NULL, "HPOUT1R" }, + + /* Speakers */ + {"Ext Spk", NULL, "SPKOUTLP"}, + {"Ext Spk", NULL, "SPKOUTLN"}, + {"Ext Spk", NULL, "SPKOUTRP"}, + {"Ext Spk", NULL, "SPKOUTRN"}, + + { "AMIC", NULL, "MICBIAS2" }, + { "AMIC", NULL, "MICBIAS1" }, + + { "IN1L", NULL, "AMIC" }, + { "IN1R", NULL, "AMIC" }, + + { "DMic", NULL, "SoC DMIC"}, + { "DMIC01 Rx", NULL, "Capture" }, + { "dmic01_hifi", NULL, "DMIC01 Rx" }, + + /* ssp2 path */ + {"Dummy Playback", NULL, "ssp2 Tx"}, + {"ssp2 Tx", NULL, "ssp2_out"}, + + {"ssp2 Rx", NULL, "Dummy Capture"}, + {"ssp2_in", NULL, "ssp2 Rx"}, + + /* ssp1 path */ + {"Dummy Playback", NULL, "ssp1 Tx"}, + {"ssp1 Tx", NULL, "ssp1_out"}, + + /* SWM map link the SWM outs to codec AIF */ + { "AIF1 Playback", NULL, "ssp0 Tx"}, + { "ssp0 Tx", NULL, "codec1_out"}, + { "ssp0 Tx", NULL, "codec0_out"}, + + { "ssp0 Rx", NULL, "AIF1 Capture" }, + { "codec0_in", NULL, "ssp0 Rx" }, + + {"Headphones", NULL, "Platform Clock"}, + {"AMIC", NULL, "Platform Clock"}, + {"DMIC", NULL, "Platform Clock"}, + {"Ext Spk", NULL, "Platform Clock"}, + {"EP", NULL, "Platform Clock"}, + {"Tone Generator 1", NULL, "Platform Clock" }, + {"Tone Generator 2", NULL, "Platform Clock" }, +}; + +static const struct snd_kcontrol_new cnl_controls[] = { + SOC_DAPM_PIN_SWITCH("Headphones"), + SOC_DAPM_PIN_SWITCH("Ext Spk"), + SOC_DAPM_PIN_SWITCH("EP"), + SOC_DAPM_PIN_SWITCH("AMIC"), + SOC_DAPM_PIN_SWITCH("DMIC"), +}; + +static int cnl_florida_init(struct snd_soc_pcm_runtime *runtime) +{ + int ret; + unsigned int fmt; + struct snd_soc_card *card = runtime->card; + struct snd_soc_dai *florida_dai = cnl_florida_get_codec_dai(card, "wm5110-aif1"); + + + pr_info("Entry %s\n", __func__); + + ret = snd_soc_dai_set_tdm_slot(florida_dai, 0, 0, 4, 24); + /* slot width is set as 25, SNDRV_PCM_FORMAT_S32_LE */ + if (ret < 0) { + pr_err("can't set codec pcm format %d\n", ret); + return ret; + } + + /* bit clock inverse not required */ + fmt = SND_SOC_DAIFMT_DSP_A | SND_SOC_DAIFMT_NB_NF + | SND_SOC_DAIFMT_CBS_CFS; + ret = snd_soc_dai_set_fmt(florida_dai, fmt); + if (ret < 0) { + pr_err("can't set codec DAI configuration %d\n", ret); + return ret; + } + + card->dapm.idle_bias_off = true; + + ret = snd_soc_add_card_controls(card, cnl_controls, + ARRAY_SIZE(cnl_controls)); + if (ret) { + pr_err("unable to add card controls\n"); + return ret; + } + return 0; +} + +static int cnl_florida_codec_fixup(struct snd_soc_pcm_runtime *rtd, + struct snd_pcm_hw_params *params) +{ + struct snd_interval *rate = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_RATE); + struct snd_interval *channels = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_CHANNELS); + + pr_debug("Invoked %s for dailink %s\n", __func__, rtd->dai_link->name); + rate->min = rate->max = 48000; + channels->min = channels->max = 2; + snd_mask_none(hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT)); + snd_mask_set(hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT), + SNDRV_PCM_FORMAT_S24_LE); + + pr_info("param width set to:0x%x\n", + snd_pcm_format_width(params_format(params))); + + return 0; +} + +struct snd_soc_dai_link cnl_florida_msic_dailink[] = { + /* Trace Buffer DAI links */ + { + .name = "CNL Trace Buffer0", + .stream_name = "Core 0 Trace Buffer", + .cpu_dai_name = "TraceBuffer0 Pin", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .platform_name = "0000:02:18.0", + .capture_only = true, + .ignore_suspend = 1, + }, + { + .name = "CNL Trace Buffer1", + .stream_name = "Core 1 Trace Buffer", + .cpu_dai_name = "TraceBuffer1 Pin", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .platform_name = "0000:02:18.0", + .capture_only = true, + .ignore_suspend = 1, + }, + { + .name = "CNL Trace Buffer2", + .stream_name = "Core 2 Trace Buffer", + .cpu_dai_name = "TraceBuffer2 Pin", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .platform_name = "0000:02:18.0", + .capture_only = true, + .ignore_suspend = 1, + }, + { + .name = "CNL Trace Buffer3", + .stream_name = "Core 3 Trace Buffer", + .cpu_dai_name = "TraceBuffer3 Pin", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .platform_name = "0000:02:18.0", + .capture_only = true, + .ignore_suspend = 1, + }, + /* Probe DAI-links */ + { + .name = "CNL Compress Probe playback", + .stream_name = "Probe Playback", + .cpu_dai_name = "Compress Probe0 Pin", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .platform_name = "0000:02:18.0", + .init = NULL, + .ignore_suspend = 1, + .nonatomic = 1, + }, + { + .name = "CNL Compress Probe capture", + .stream_name = "Probe Capture", + .cpu_dai_name = "Compress Probe1 Pin", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .platform_name = "0000:02:18.0", + .init = NULL, + .ignore_suspend = 1, + .nonatomic = 1, + }, + /* back ends */ + { + .name = "SSP0-Codec", + .id = 1, + .cpu_dai_name = "SSP0 Pin", + .codec_name = "wm5110-codec", + .codec_dai_name = "wm5110-aif1", + .platform_name = "0000:02:18.0", + .be_hw_params_fixup = cnl_florida_codec_fixup, + .ignore_suspend = 1, + .no_pcm = 1, + .dpcm_playback = 1, + .dpcm_capture = 1, + .init = cnl_florida_init, + }, + { + .name = "SSP1-Codec", + .id = 2, + .cpu_dai_name = "SSP1 Pin", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .platform_name = "0000:02:18.0", + .be_hw_params_fixup = cnl_florida_codec_fixup, + .ignore_suspend = 1, + .no_pcm = 1, + .dpcm_playback = 1, + }, + { + .name = "dmic01", + .id = 3, + .cpu_dai_name = "DMIC01 Pin", + .codec_name = "dmic-codec", + .codec_dai_name = "dmic-hifi", + .platform_name = "0000:02:18.0", + .ignore_suspend = 1, + .no_pcm = 1, + .dpcm_capture = 1, + .be_hw_params_fixup = cnl_dmic_fixup, + }, + /* codec-codec link */ + { + .name = "CNL SSP0-Loop Port", + .stream_name = "CNL SSP0-Loop", + .cpu_dai_name = "SSP0 Pin", + .platform_name = "0000:02:18.0", + .codec_name = "wm5110-codec", + .codec_dai_name = "wm5110-aif1", + .params = &dai_params_codec, + .dsp_loopback = true, + .dai_fmt = SND_SOC_DAIFMT_DSP_A | + SND_SOC_DAIFMT_NB_NF | + SND_SOC_DAIFMT_CBS_CFS, + }, + { + .name = "CNL SSP2-Loop Port", + .stream_name = "CNL SSP2-Loop", + .cpu_dai_name = "SSP2 Pin", + .platform_name = "0000:02:18.0", + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .params = &dai_params_modem, + .dsp_loopback = true, + }, + { + .name = "CNL SSP1-Loop Port", + .stream_name = "CNL SSP1-Loop", + .cpu_dai_name = "SSP1 Pin", + .platform_name = "0000:02:18.0", + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .params = &dai_params_bt, + .dsp_loopback = true, + }, +}; + +#ifdef CONFIG_PM_SLEEP +static int snd_cnl_florida_prepare(struct device *dev) +{ + pr_debug("In %s\n", __func__); + return snd_soc_suspend(dev); +} + +static void snd_cnl_florida_complete(struct device *dev) +{ + pr_debug("In %s\n", __func__); + snd_soc_resume(dev); +} + +static int snd_cnl_florida_poweroff(struct device *dev) +{ + pr_debug("In %s\n", __func__); + return snd_soc_poweroff(dev); +} +#else +#define snd_cnl_florida_prepare NULL +#define snd_cnl_florida_complete NULL +#define snd_cnl_florida_poweroff NULL +#endif + +static int +cnl_add_dai_link(struct snd_soc_card *card, struct snd_soc_dai_link *link) +{ + link->platform_name = "0000:02:18.0"; + link->nonatomic = 1; + + return 0; +} + +/* SoC card */ +static struct snd_soc_card snd_soc_card_cnl = { + .name = "florida-audio", + .dai_link = cnl_florida_msic_dailink, + .num_links = ARRAY_SIZE(cnl_florida_msic_dailink), + .dapm_widgets = cnl_widgets, + .num_dapm_widgets = ARRAY_SIZE(cnl_widgets), + .dapm_routes = cnl_map, + .num_dapm_routes = ARRAY_SIZE(cnl_map), + .add_dai_link = cnl_add_dai_link, +}; + +static int snd_cnl_florida_mc_probe(struct platform_device *pdev) +{ + int ret_val = 0; + struct cnl_mc_private *drv; + + pr_debug("Entry %s\n", __func__); + + drv = devm_kzalloc(&pdev->dev, sizeof(*drv), GFP_ATOMIC); + if (!drv) + return -ENOMEM; + + snd_soc_card_cnl.dev = &pdev->dev; + snd_soc_card_set_drvdata(&snd_soc_card_cnl, drv); + /* Register the card */ + ret_val = snd_soc_register_card(&snd_soc_card_cnl); + if (ret_val) { + pr_err("snd_soc_register_card failed %d\n", ret_val); + goto unalloc; + } + platform_set_drvdata(pdev, &snd_soc_card_cnl); + pr_info("%s successful\n", __func__); + return ret_val; + +unalloc: + devm_kfree(&pdev->dev, drv); + return ret_val; +} + +static int snd_cnl_florida_mc_remove(struct platform_device *pdev) +{ + struct snd_soc_card *soc_card = platform_get_drvdata(pdev); + struct cnl_mc_private *drv = snd_soc_card_get_drvdata(soc_card); + + pr_debug("In %s\n", __func__); + + devm_kfree(&pdev->dev, drv); + snd_soc_card_set_drvdata(soc_card, NULL); + snd_soc_unregister_card(soc_card); + platform_set_drvdata(pdev, NULL); + return 0; +} + +const struct dev_pm_ops snd_cnl_florida_mc_pm_ops = { + .prepare = snd_cnl_florida_prepare, + .complete = snd_cnl_florida_complete, + .poweroff = snd_cnl_florida_poweroff, +}; + +static const struct platform_device_id cnl_board_ids[] = { + { .name = "cnl_florida" }, + { .name = "glv_wm8281" }, + { .name = "icl_wm8281" }, + { } +}; +static struct platform_driver snd_cnl_florida_mc_driver = { + .driver = { + .owner = THIS_MODULE, + .name = "cnl_florida", + }, + .probe = snd_cnl_florida_mc_probe, + .remove = snd_cnl_florida_mc_remove, + .id_table = cnl_board_ids, +}; + +static int snd_cnl_florida_driver_init(void) +{ + pr_info("cnl_florida: Registering cnl_florida...\n"); + return platform_driver_register(&snd_cnl_florida_mc_driver); +} +module_init(snd_cnl_florida_driver_init); + +static void snd_cnl_florida_driver_exit(void) +{ + pr_debug("In %s\n", __func__); + platform_driver_unregister(&snd_cnl_florida_mc_driver); +} +module_exit(snd_cnl_florida_driver_exit) + +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:cnl_florida"); From 2e6a6d9b096a69f2ce35c1f04a99f8beb34624ce Mon Sep 17 00:00:00 2001 From: Guneshwor Singh Date: Thu, 17 Nov 2016 10:57:43 +0530 Subject: [PATCH 0605/1103] [FPGA] ASoC: rt274: Force load rt274 without acpi ACPI entries are not present in CNL FPGA BIOS, so force load rt274 codec driver. This is required only for FPGA. Change-Id: If901f6488c7dc7aaa3e7534152f2fe0e953f323f Signed-off-by: Guneshwor Singh Reviewed-on: Reviewed-by: Kale, Sanyog R Reviewed-by: Kp, Jeeja Tested-by: Avati, Santosh Kumar --- sound/soc/codecs/rt274.c | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/sound/soc/codecs/rt274.c b/sound/soc/codecs/rt274.c index d88e67341083..c5c3ee4d3182 100644 --- a/sound/soc/codecs/rt274.c +++ b/sound/soc/codecs/rt274.c @@ -1220,7 +1220,39 @@ static struct i2c_driver rt274_i2c_driver = { .id_table = rt274_i2c_id, }; +#if !IS_ENABLED(CONFIG_SND_SOC_INTEL_CNL_FPGA) module_i2c_driver(rt274_i2c_driver); +#else +static struct i2c_board_info rt274_i2c_device = { + I2C_BOARD_INFO("rt274", 0x1c), +}; + +static int __init rt274_modinit(void) +{ + int ret = 0; + struct i2c_adapter *adapter; + struct i2c_client *client; + + adapter = i2c_get_adapter(0); + if (adapter) { + client = i2c_new_device(adapter, &rt274_i2c_device); + if (!client) { + pr_err("can't create i2c device %s\n", + rt274_i2c_device.type); + i2c_put_adapter(adapter); + return -ENODEV; + } + } else { + pr_err("adapter is NULL\n"); + return -ENODEV; + } + + ret = i2c_add_driver(&rt274_i2c_driver); + + return ret; +} +module_init(rt274_modinit); +#endif MODULE_DESCRIPTION("ASoC RT274 driver"); MODULE_AUTHOR("Bard Liao "); From 0ab59ae24f80807adc6f164be81cecf09b208f3d Mon Sep 17 00:00:00 2001 From: Ramesh Babu Date: Tue, 17 Nov 2015 02:33:39 +0530 Subject: [PATCH 0606/1103] ASoC: HDA: EXT: Mark dma buffers as un-cacheble Driver shouldn't not assume HDA DMA has snooping enabled. Driver should always allocate non-cached memory. Change-Id: I5b21643d88d7692967c83f4696eb9e3eb7a90178 Signed-off-by: Ramesh Babu Signed-off-by: Hardik T Shah Reviewed-on: --- include/sound/hdaudio.h | 2 ++ sound/hda/ext/hdac_ext_bus.c | 37 +++++++++++++++++++++++++++++++++++- 2 files changed, 38 insertions(+), 1 deletion(-) diff --git a/include/sound/hdaudio.h b/include/sound/hdaudio.h index cd1773d0e08f..0aa0189017ba 100644 --- a/include/sound/hdaudio.h +++ b/include/sound/hdaudio.h @@ -265,6 +265,8 @@ struct hdac_io_ops { struct snd_dma_buffer *buf); void (*dma_free_pages)(struct hdac_bus *bus, struct snd_dma_buffer *buf); + /* mark memory region as non-cache */ + void (*mark_pages_uc)(struct snd_dma_buffer *buf, bool enable); }; #define HDA_UNSOL_QUEUE_SIZE 64 diff --git a/sound/hda/ext/hdac_ext_bus.c b/sound/hda/ext/hdac_ext_bus.c index 9c37d9af3023..3e63fcfd6cd8 100644 --- a/sound/hda/ext/hdac_ext_bus.c +++ b/sound/hda/ext/hdac_ext_bus.c @@ -20,6 +20,8 @@ #include #include #include +#include +#include #include MODULE_DESCRIPTION("HDA extended core"); @@ -55,14 +57,46 @@ static u8 hdac_ext_readb(u8 __iomem *addr) return readb(addr); } +static void hdac_ext_mark_pages_uc(struct snd_dma_buffer *dmab, bool enable) +{ + int pages; + + if (!dmab || !dmab->area || !dmab->bytes) + return; + +#ifdef CONFIG_SND_DMA_SGBUF + if (dmab->dev.type == SNDRV_DMA_TYPE_DEV_SG) { + struct snd_sg_buf *sgbuf = dmab->private_data; + + if (enable) + set_pages_array_uc(sgbuf->page_table, sgbuf->pages); + else + set_pages_array_wb(sgbuf->page_table, sgbuf->pages); + return; + } +#endif + pages = (dmab->bytes + PAGE_SIZE - 1) >> PAGE_SHIFT; + if (enable) + set_memory_uc((unsigned long)dmab->area, pages); + else + set_memory_wb((unsigned long)dmab->area, pages); +} + static int hdac_ext_dma_alloc_pages(struct hdac_bus *bus, int type, size_t size, struct snd_dma_buffer *buf) { - return snd_dma_alloc_pages(type, bus->dev, size, buf); + int ret; + + ret = snd_dma_alloc_pages(type, bus->dev, size, buf); + if (ret < 0) + return ret; + hdac_ext_mark_pages_uc(buf, true); + return ret; } static void hdac_ext_dma_free_pages(struct hdac_bus *bus, struct snd_dma_buffer *buf) { + hdac_ext_mark_pages_uc(buf, false); snd_dma_free_pages(buf); } @@ -75,6 +109,7 @@ static const struct hdac_io_ops hdac_ext_default_io = { .reg_readb = hdac_ext_readb, .dma_alloc_pages = hdac_ext_dma_alloc_pages, .dma_free_pages = hdac_ext_dma_free_pages, + .mark_pages_uc = hdac_ext_mark_pages_uc, }; /** From e220e599437ce5b841e38335c6f13faef1a5a339 Mon Sep 17 00:00:00 2001 From: Ramesh Babu Date: Mon, 23 Nov 2015 03:22:40 +0530 Subject: [PATCH 0607/1103] ASoC: intel: skylake: mark ring buffer as non-cacheble Change-Id: I8c08f3a91682654e5f1db95033b580258201da91 Signed-off-by: Ramesh Babu Reviewed-on: --- sound/soc/intel/skylake/skl-pcm.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/sound/soc/intel/skylake/skl-pcm.c b/sound/soc/intel/skylake/skl-pcm.c index 823e39103edd..ab31d6a1c842 100644 --- a/sound/soc/intel/skylake/skl-pcm.c +++ b/sound/soc/intel/skylake/skl-pcm.c @@ -80,17 +80,25 @@ static int skl_substream_alloc_pages(struct hdac_bus *bus, size_t size) { struct hdac_ext_stream *stream = get_hdac_ext_stream(substream); + int ret; hdac_stream(stream)->bufsize = 0; hdac_stream(stream)->period_bytes = 0; hdac_stream(stream)->format_val = 0; - return snd_pcm_lib_malloc_pages(substream, size); + ret = snd_pcm_lib_malloc_pages(substream, size); + if (ret < 0) + return ret; + ebus->bus.io_ops->mark_pages_uc(snd_pcm_get_dma_buf(substream), true); + + return ret; } static int skl_substream_free_pages(struct hdac_bus *bus, struct snd_pcm_substream *substream) { + bus->io_ops->mark_pages_uc(snd_pcm_get_dma_buf(substream), false); + return snd_pcm_lib_free_pages(substream); } From 072ebeb42f97750b9688caae6083cc13e2ae5a7b Mon Sep 17 00:00:00 2001 From: Guneshwor Singh Date: Tue, 2 Aug 2016 15:37:20 +0530 Subject: [PATCH 0608/1103] REVERTME: ASoC: Intel: CNL: Load firmware in dsp_init. This patch loads the DSP firmware as a part of dsp_init instead of dsp_fw init. SoundWire master controller doesnt get register if the DSP firmware is loaded as part of fw_init, since for master controller registration DSP power up is must, which is part of fw_load function. Change-Id: I37edac4043abca18c189b035cb1e60194e33d79d Signed-off-by: Guneshwor Singh --- sound/soc/intel/skylake/cnl-sst.c | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/sound/soc/intel/skylake/cnl-sst.c b/sound/soc/intel/skylake/cnl-sst.c index f7c832b300d0..2969e57e3e93 100644 --- a/sound/soc/intel/skylake/cnl-sst.c +++ b/sound/soc/intel/skylake/cnl-sst.c @@ -533,21 +533,24 @@ int cnl_sst_dsp_init(struct device *dev, void __iomem *mmio_base, int irq, cnl->boot_complete = false; init_waitqueue_head(&cnl->boot_wait); - return skl_dsp_acquire_irq(sst); + ret = skl_dsp_acquire_irq(sst); + if (ret < 0) + return ret; + + ret = cnl_load_base_firmware(sst); + if (ret < 0) { + dev_err(dev, "Load base fw failed: %d", ret); + return ret; + } + + return 0; } EXPORT_SYMBOL_GPL(cnl_sst_dsp_init); int cnl_sst_init_fw(struct device *dev, struct skl_sst *ctx) { - int ret; struct sst_dsp *sst = ctx->dsp; - ret = ctx->dsp->fw_ops.load_fw(sst); - if (ret < 0) { - dev_err(dev, "load base fw failed: %d", ret); - return ret; - } - skl_dsp_init_core_state(sst); ctx->is_first_boot = false; From 42c94b098e2364c8f7d04933b9aa8adf5089dbc0 Mon Sep 17 00:00:00 2001 From: Guneshwor Singh Date: Mon, 22 Aug 2016 12:13:36 +0530 Subject: [PATCH 0609/1103] ASoC: Intel: CNL: Add library loading support Cannonlake's library loading is similar to broxton's. So reuse it. Change-Id: I3a0d64da0eaef395cd5e6a95252e4025ce7921b2 Signed-off-by: Guneshwor Singh --- sound/soc/intel/skylake/bxt-sst.c | 2 +- sound/soc/intel/skylake/cnl-sst.c | 10 ++++++++++ sound/soc/intel/skylake/skl-sst-dsp.h | 1 + 3 files changed, 12 insertions(+), 1 deletion(-) diff --git a/sound/soc/intel/skylake/bxt-sst.c b/sound/soc/intel/skylake/bxt-sst.c index 440bca7afbf1..924c0dfa6ad7 100644 --- a/sound/soc/intel/skylake/bxt-sst.c +++ b/sound/soc/intel/skylake/bxt-sst.c @@ -53,7 +53,7 @@ static unsigned int bxt_get_errorcode(struct sst_dsp *ctx) return sst_dsp_shim_read(ctx, BXT_ADSP_ERROR_CODE); } -static int +int bxt_load_library(struct sst_dsp *ctx, struct skl_lib_info *linfo, int lib_count) { struct snd_dma_buffer dmab; diff --git a/sound/soc/intel/skylake/cnl-sst.c b/sound/soc/intel/skylake/cnl-sst.c index 2969e57e3e93..a66ed94870b4 100644 --- a/sound/soc/intel/skylake/cnl-sst.c +++ b/sound/soc/intel/skylake/cnl-sst.c @@ -357,6 +357,7 @@ static const struct skl_dsp_fw_ops cnl_fw_ops = { .set_state_D3 = cnl_set_dsp_D3, .load_fw = cnl_load_base_firmware, .get_fw_errcode = cnl_get_errno, + .load_library = bxt_load_library, }; static struct sst_ops cnl_ops = { @@ -550,9 +551,18 @@ EXPORT_SYMBOL_GPL(cnl_sst_dsp_init); int cnl_sst_init_fw(struct device *dev, struct skl_sst *ctx) { struct sst_dsp *sst = ctx->dsp; + int ret; skl_dsp_init_core_state(sst); + if (ctx->lib_count > 1) { + ret = sst->fw_ops.load_library(sst, ctx->lib_info, + ctx->lib_count); + if (ret) { + dev_err(dev, "Load Library failed: %#x", ret); + return ret; + } + } ctx->is_first_boot = false; return 0; diff --git a/sound/soc/intel/skylake/skl-sst-dsp.h b/sound/soc/intel/skylake/skl-sst-dsp.h index e1d6f6719f7e..33de8c939bc4 100644 --- a/sound/soc/intel/skylake/skl-sst-dsp.h +++ b/sound/soc/intel/skylake/skl-sst-dsp.h @@ -238,6 +238,7 @@ int skl_sst_init_fw(struct device *dev, struct skl_sst *ctx); int bxt_sst_init_fw(struct device *dev, struct skl_sst *ctx); void skl_sst_dsp_cleanup(struct device *dev, struct skl_sst *ctx); void bxt_sst_dsp_cleanup(struct device *dev, struct skl_sst *ctx); +int bxt_load_library(struct sst_dsp *ctx, struct skl_lib_info *linfo, int lib_count); int snd_skl_parse_uuids(struct sst_dsp *ctx, const struct firmware *fw, unsigned int offset, int index); From ccf66ec8f6129d46faa26bdef21603621d731b06 Mon Sep 17 00:00:00 2001 From: Hardik T Shah Date: Tue, 8 Mar 2016 16:00:37 +0530 Subject: [PATCH 0610/1103] Soundwire squashed commits SoundWire: Add place holder for the soundwire bus driver header file This patch creates the place holder file for the SoundWire bus driver header file. Change-Id: Ib71bbe10f5988194ee24f3793950a6c079d56aa7 Signed-off-by: Hardik T Shah SoundWire: Add #defines to the bus driver This patch adds the #defines to the SoundWire bus driver. Change-Id: Ic4dfa9582aa45c3685aee021c156cfbc3e23d817 Signed-off-by: Hardik T Shah SoundWire: Add enum to the SoundWire bus driver This patch adds the enums for the SoundWire bus driver header file. Change-Id: Id9b48b5bacc9137f62099eaa4948390b54d4aac0 Signed-off-by: Hardik T Shah SoundWire: Add forward declaration for the data structures. This patch adds the forward declarations of data structures for the SoundWire bus driver. Change-Id: I8d5d8dc885c7c8a28d68bada9d4b270c603ae4e4 Signed-off-by: Hardik T Shah SoundWire: Add device_id table for SoundWire bus. Add device_id table for the soundwire master and slave devices and driver registration Change-Id: I3848c9476459215ff1d301af69e2eb3c8934bb60 Signed-off-by: Hardik T Shah SoundWire: Add data structures for slave driver and device. This patch adds the data structure for slave driver and device registration with SoundWire bus driver. Change-Id: Ie7f2a0a39fcde3073b560fde41f6c91684975cbe Signed-off-by: Hardik T Shah SoundWire: Add data structures for master driver and device. This patch adds the data structure for master driver and device registration with SoundWire bus driver. Change-Id: I269c3afa0e94979274d0415cfbd0be4c214d1afb Signed-off-by: Hardik T Shah SoundWire: Add message transfer data structure This patch adds the data structure to do register read/write using message tranasfer. This is similar to i2c_transfer on i2c bus. Change-Id: Id10bd88b4fc02c2283334716c38e28b9af862c6e Signed-off-by: Hardik T Shah SoundWire: Add data structure for stream configuration. This patch adds data structure for stream configuration between SoundWire master and slaves. Change-Id: I3a1d06833c881d9ae6bbea38b61f9adb3d8710a0 Signed-off-by: Hardik T Shah SoundWire: Add data structure to support bulk register access This patch adds data structure to support Bulk Register Access (BRA) type of Bulk Transfer Protocol (BTP) between SoundWire master and slaves devices Change-Id: I85a22552428d1388245b9bce7b25cb32779fabf4 Signed-off-by: Hardik T Shah SoundWire: Add data structure to report slave status This patch adds data structure to report slave status to SoundWire bus driver. This is normally called by master controller driver on slave status change on bus. Change-Id: I17d51cbcedb4259015632b6fc0207c705178a19e Signed-off-by: Hardik T Shah SoundWire: Add API to register SoundWire master This patch adds support to register/unregister SoundWire master controller device and driver. Change-Id: I17a65171ea337604af5a868eceade65fb6fcaae6 Signed-off-by: Hardik T Shah SoundWire: Add API for slave driver registration. This patch add supports for registering/unregistering SoundWire slave driver. Slave device is registered by bus driver on enumeration. Change-Id: I9853409c4383e8c87ac04df97d506d0d980f760c Signed-off-by: Hardik T Shah SoundWire: Add API for slave read/write This patch adds API to do register read/write of slaves using message transfer API. This is similar to i2c_transfer for i2c. Change-Id: I7827f8df05db4c7cf264d9b5ea34801f99ab4e48 Signed-off-by: Hardik T Shah SoundWire: Add support for stream configuration This patch adds support for stream configuration APIs between the SoundWire devices. Change-Id: I9b24a937923db93e09675f179b0b62921410c2fc Signed-off-by: Hardik T Shah SoundWire: Add API to update the slave status. This patch adds the bus driver API to update slave status to bus driver. This is called by master controller driver, to update slave status to bus driver. Master controller hardware normally gets interrupt on slave status change. Change-Id: I917dbdbfd45f2172967c8637344e76eb17a53310 Signed-off-by: Hardik T Shah SoundWire: Add miscellaneous helper functions. This patch adds the miscellaneous helper functions to be used by SoundWire master controller and slave device drivers. Change-Id: I1be5d3f2e5bb695ae2b409da5e33217120cf0d3e Signed-off-by: Hardik T Shah SoundWire: Add placeholder for bus driver private file Change-Id: Ib284d063fdc9a653a18114f95feb6c8e73bf46fd Signed-off-by: Hardik T Shah SoundWire: Add #defines to the bus driver private header. This patch adds the #defines to the bus driver private header file. This needs to be exposed only between bus driver source files. These #defines are not publicly exposed. Change-Id: Ib6393566cd95648bfdc790d656f71a3ebe8aaff0 Signed-off-by: Hardik T Shah SoundWire: Add forward declarations of the data structure. This patch adds the forward declarations of the data structures to the soundwire bus driver private header file. Change-Id: I8a5dc317eb143ea0192c85d6f2080314e1a89517 Signed-off-by: Hardik T Shah SoundWire: Add enums to private header file. This patch adds the enums for the SoundWire bus driver private header file. Change-Id: I0927be849cf02da4637510ef3ae094f1cca247d0 Signed-off-by: Hardik T Shah SoundWire: Add data structures to the private header file. This patch adds data structure for the SoundWire private header file. These are used between bus driver source files. Change-Id: I3187078d4be4ef458825a4e0b211059bfa798835 Signed-off-by: Hardik T Shah SoundWire: Add function declarations in private header file. This patch adds the function declartions, to be used between SoundWire source files. Change-Id: Iceff34eafd5406b586cb87206f93012fb05a72c5 Signed-off-by: Hardik T Shah SoundWire: Add SoundWire slave register definition file. This patch adds the register definition file for SoundWire slaves. MIPI defines the register map for the SoundWire slave. This file adds the register map and corresponding bit masks and shift for MIPI defined optional and mandatory registers for slave. This file doesnt add any register definition for vendor specific/implementation defined registers. Change-Id: Ie531e50482703c6f258ae22c3125d3a1f55e4aea Signed-off-by: Hardik T Shah SoundWire: Add placeholder for SoundWire bus driver core file. Change-Id: Ifa29b24717986244ef1cf00fb9865bcf14b2287f Signed-off-by: Hardik T Shah SoundWire: Add includes in SoundWire bus driver core file. This patch adds the includes to the SoundWire bus driver core file. Change-Id: I0ee7f8baa3787a248dae7d5d649d409062fb6112 Signed-off-by: Hardik T Shah SoundWire: Add globals to bus driver core file This patch adds the global instance handling all the buses registered as SoundWire bus. Change-Id: I278e0e418321db7a2fb0aa4adc0c0d15e8b9677b Signed-off-by: Hardik T Shah SoundWire: Add "sdw" bus type registration with kernel core This patch adds the bus type registration to kernel. It also initializes the core data structure as a part of bus initialization. Change-Id: I1cc301a7d7dfe4564cacdd6c5ff37db63060d488 Signed-off-by: Hardik T Shah SoundWire: Implement "match" callback for bus driver This patch implements "match" method for the bus driver registration. Change-Id: I9fabdce35c7010b7fb5f8c52ff5eaa917f68dfc4 Signed-off-by: Hardik T Shah SoundWire: Add probe method for bus driver This patch implementes the probe method for bus driver. It calls the probe of of respective mater/slave driver matching with device name or id. Change-Id: Ie1d7bf8c3e4d86f87f534209c4002e3e28d55c01 Signed-off-by: Hardik T Shah SoundWire: Add remove method for bus driver This patch implementes the remove method for bus driver. Change-Id: Iec7096bfc4bb0017df8006e37018a261d918c514 Signed-off-by: Hardik T Shah SoundWire: Add shutdown method for bus driver This patch implements the shutdown method of the bus. Change-Id: I4c910a4784676399a90c691e32ed31c996fd3623 Signed-off-by: Hardik T Shah SoundWire: Add PM support to bus driver. This patch adds PM support to bus driver. This adds both Runtime PM and Legacy PM support. Change-Id: I9e34c4c06fe41e5da04ce58e6d335f0f29aca6b8 Signed-off-by: Hardik T Shah SoundWire: Add API to register master controller device This patch adds API to register the master controller SoundWire device with the bus as bus master. Change-Id: Id6c7aedb3d7392dcd5672606af5237fccc7d343f Signed-off-by: Hardik T Shah SoundWire: Add API to transfer messages on bus. This patch adds API to transfer register read/write messages on bus. This is used to read/write slave addresses. Change-Id: Iff011f879118440c38d221ea6153bc9eac10049d Signed-off-by: Hardik T Shah SoundWire: Add support to register slaves on enumeration. This patch add supports to register slave devices on enumeration. Master controller driver reports the slave status to bus driver. Bus driver adds the slaves on enumeration. Change-Id: I1b82850f39a15fa18756bb2b28f29bbb1141da4a Signed-off-by: Hardik T Shah SoundWire: Add API to un-register master controller device. This patch adds API to un-register the master controller device. On un-registering the master controller device, all the slaves attached to the bus also gets un-registered. Change-Id: I08eaf9162b3e7d2b7063e8f33cf5d8d79c9f67b3 Signed-off-by: Hardik T Shah SoundWire: Add API to register and un-register master driver This patch adds API to register and un-register the master controller driver controlling the master controller device. Change-Id: I65ffd42c7bd073b0fbb771cb948e24c31f5fa658 Signed-off-by: Hardik T Shah SoundWire: Add API to register/un-register slave driver This patch adds API to register/un-register SoundWire slave device driver. Change-Id: I76732b273ec31fcd34bd819a1384cbf482a997fc Signed-off-by: Hardik T Shah SoundWire: Add API to register slave capabilities to bus This patch adds the API to be used by slave driver to register the slave capabilties to the bus driver. Bus driver uses this to setup the slave for stream transfer over SoundWire interface. Change-Id: I036f01868a417c31adaa70e929b55b9b782702f9 Signed-off-by: Hardik T Shah SoundWire: Add API to alloc and release stream tag. This patch add the bus driver API to allocate and de-allocate stream tag. Stream tag is a unique stream identifier for each audio stream between master and slave. Ports of the masters and slaves part of the same stream has unique and same stream tag. All ports of master and slaves belonging to same stream tag get enabled and disabled together. Change-Id: Ia59ce7f8bfb4e81125cc5334ba4529b78b8afdd4 Signed-off-by: Hardik T Shah SoundWire: Add API to configure and release the stream. This patch adds API to configure and release the soundiwre stream. Once the stream tag is assigned, all the masters and slaves associated with the stream tag configures the stream. Stream params between the master and slave should match. Change-Id: Icdd884d56d09910fcd5e263778362e748f00381c Signed-off-by: Hardik T Shah SoundWire: Add support for masters' and slaves' port config This patch add supports for configuring the master and slave port. Once the stream is configure between master and slave, ports needs to be configured. There may be different ports getting used between master and slave for same stream. Port configuration may also differ like master may use single port to drive stereo stream, but slave may be receiving L & R channels on 2 different ports. So all the slaves and masters part of stream tag calls this function to let bus driver know about its port mapping with stream configuration. Change-Id: I466edd9b2b9430437d4c8081ff42e61f74fe25d0 Signed-off-by: Hardik T Shah SoundWire: Add API to enable and disable the stream. This patch implement bus driver API to enable and disable the stream. All the ports of all the masters and slaves part of the stream tag gets enabled or disable simultaneously with bank switch. Change-Id: Ie205402a78a1e417f49962bacef529f6e9491fac Signed-off-by: Hardik T Shah SoundWire: Add functionality to handle slave alerts This patch adds the functionality to handle slave alerts. SoundWire slave devices can generate alert event on the bus. This patch adds the functionality to handle slave alerts. It also adds functionality to mark slaves as attached or un-attached based on its enumeration status. Change-Id: I69d5af23d891e3ca6684c46ae5f2b5747324b0fe Signed-off-by: Hardik T Shah SoundWire: Add Kconfig and Makefile option This patch adds the Makefile and Kconfig option to compile the bus driver. Change-Id: I47a499e84384c7d0a311b393a64a78b476d6e69e Signed-off-by: Hardik T Shah SoundWire: Added sdw_bwcalc.c file sdw_bwcalc.c file is added in drivers/sdw/ directory. This file will contain APIs which calculates required bandwidth, clock, frameshape, computes all transport params for a given port, enable channel perform bankswitch operations. Change-Id: Ie954bdff81e0faf5ee711c9c33503c4a4d3d39ec Signed-off-by: Sanyog Kale SoundWire: Added APIs to compute bus params Added APIs to compute frame shape, hstart/hstop, block offset & switch bank. This all APIs will be called from sdw_bus_calc_bw & sdw_bus_calc_bw_dis API. Change-Id: I9177e36bda0462ccee2bbcf308b49fc65c217a8f Signed-off-by: Sanyog Kale SoundWire: Added API to configure master/slave params Added API which sets xport params of master/slave port and prepare/unprepare, enable/disable channels. This all APIs will be called from sdw_bus_calc_bw & sdw_bus_calc_bw_dis API. Change-Id: I5f94b3a1ebee827634dbc3e1475eeed88a2fc716 Signed-off-by: Sanyog Kale SoundWire: Added rest API required for bus params config Added all internal API's which are called from child functions. Change-Id: I652fbe0f52c627875caf0db90624559272bc1131 Signed-off-by: Sanyog Kale SoundWire: Added code in bandwidth calculation API's All the functionality needed to done by sdw_bus_calc_bw & sdw_bus_calc_bw_dis API's are added. Change-Id: Ie833244aea09b30483d9755ec39be5cb35244256 Signed-off-by: Sanyog Kale SoundWire: Added code in sdw_bus_bw_init & sdw_mstr_bw_init All the initializations needed to be done are added in sdw_bus_bw_init & sdw_mstr_bw_init API's Change-Id: Id1c05d994e9a737e01e47dc113b57baa05c5f546 Signed-off-by: Sanyog Kale SoundWire: API's filled to compute bus params Below APIs are modified. sdw_lcm sdw_get_clock_frmshp sdw_compute_sys_interval sdw_compute_hstart_hstop sdw_compute_blk_subblk_offset sdw_configure_frmshp_bnkswtch sdw_get_col_to_num sdw_get_row_to_num Change-Id: I02972b4d992194de3bcd87650b945652e3e9f48d Signed-off-by: Sanyog Kale SoundWire: API's filled to configure mstr/slv parameters Below API's are modified: sdw_cfg_slv_params sdw_cfg_mstr_params sdw_cfg_mstr_slv sdw_cfg_bs_params Change-Id: Id4a2695e1a67be2895414b16f9f89d3fe2366c74 Signed-off-by: Sanyog Kale SoundWire: API's filled for prep/unprep en/dis channel Below APIs are modified: sdw_cfg_slv_prep_unprep sdw_cfg_mstr_prep_unprep sdw_prep_unprep_mstr_slv sdw_cfg_slv_enable_disable sdw_cfg_mstr_activate_disable sdw_en_dis_mstr_slv sdw_en_dis_mstr_slv_state sdw_dis_chan Change-Id: Ic41c4d6bbc8765aa883f10e255f6b9fbb8d8ae64 Signed-off-by: Sanyog Kale SoundWire: Add header file for the intel master controller. This header file has public functions to be used by ASoC platform driver to expose the data path of the Intel master controller. Change-Id: I40c86da2c89c838de69c392ca4a424a3cfb32347 Signed-off-by: Hardik T Shah SoundWire:Intel: Add private file for Intel SDW controller. This file adds the register definition for the Intel SoundWire master controller. Change-Id: I00830ab1b787f486ad20d8da589528a1869ff1ae Signed-off-by: Hardik T Shah SoundWire: CNL: Add SoundWire master controller file. This patch adds the placeholder file for the Intel SoundWire master controller. Change-Id: If16ae98774d576e53007cd6a24d4ba2da7460d3c Signed-off-by: Hardik T Shah SoundWire:Intel: Add exit and init functions. This patch adds the init and exit functions of the Intel SoundWire master controller driver. Change-Id: Ia6a67e8c00786befc2e6a2e39051fdb055ec5c39 Signed-off-by: Hardik T Shah SoundWire: Intel: Add the register read/write functions. This patch adds the register read/write helper functions used by driver. Change-Id: I7fc1c07eb885ecd44a9834b09ec78c200aabe5d5 Signed-off-by: Hardik T Shah SoundWire:Intel: Add probe and remove functionality. This patch adds the probe and remove functionality to the driver. Change-Id: Ieb0bb6ebcec7fba4bbbff41eec9f30b9a544d4e6 Signed-off-by: Hardik T Shah SoundWire:Intel: Add function to power up and down the link. Change-Id: Ib47835a83963985282e7669dc0d2857f6e7eb7ca Signed-off-by: Hardik T Shah SoundWire:Intel: Add functions to initialize the hardware This patch add functions to initialize the Intel SoundWire master controller. Change-Id: I5f360513022482573f1565d7b8daa36971b29a98 Signed-off-by: Hardik T Shah SoundWire:Intel: Add port and stream initialization support. This patch adds the port and stream initialization routine the SoundWire master controller driver. Change-Id: I35c69b890e28e904940dbb91057db6c3126980a6 Signed-off-by: Hardik T Shah SoundWire:Intel: Add support for master controller ops This patch adds the master controller operations callback to bus driver. This callbacks are called by bus driver to controls the bus in device specific way. Change-Id: I962f89f4e9cc2d689cbea754ea4c5a6d1388edb9 Signed-off-by: Hardik T Shah SoundWire:Intel: Add function to the clock frequency on bus. This patch adds the method to be used by bus driver to set the bus frequency. This is used by bus driver to increase or decrease the clock frequency based on bus bandwidth requirement. Bus bandwidth is determined based on active audio streams on bus. Change-Id: Ifb2bfbeaea4cb81ae54e859b3ffa76015f8f394b Signed-off-by: Hardik T Shah SoundWire:Intel: Add method to set SSP and monitor handover. This patch adds the controller driver method to set the Stream Synchronization Point (SSP) and to handover the bus to monitor. Bus driver SSP method to set the SSP based on the active stream sample interval. While monitor handover is used to release the bus ownership to monitor hardware for debugging. Change-Id: Ie6d980717009ab4af437ca5fc494f1859d4496fa Signed-off-by: Hardik T Shah SoundWire:Intel: Add method to transfer message on bus. This patch adds the method to transfer message on bus. This is called by bus driver to accomplish register read/write on bus in hardware specific way. Change-Id: I5507652e321db967c165cf84c2c4a8affff3ff4c Signed-off-by: Hardik T Shah SoundWire: Intel: Add support to configure mater ports. This method registers the port configuration method with bus driver. Since SoundWire master controller register map is not defined by MIPI spec, master controller provides methods to be used by bus driver for master controller port setup. Port setup is done by the bus driver as part of stream setup between SoundWire devices (Master(s) and Slave(s)) Change-Id: I6592102db8c4625822a795918059fd255fbb4780 Signed-off-by: Hardik T Shah SoundWire: Intel: Implement method to set the port params This method implements method to set the SoundWire port params in device specific register map. Change-Id: I3be29f3fc05ac4f1c2d924201c02db795aca1807 Signed-off-by: Hardik T Shah SoundWire: Intel: Add method to set the transport params This patch adds the method to set the transport params of the mater controller port. Since master register map is not defined by MIPI, bus driver calls the master controller method to set the calcuated trasport params based on audio stream configuration. Change-Id: I208776976a347b5158c17c5f02c48fe885ac1c1a Signed-off-by: Hardik T Shah SoundWire:Intel: Add support to activate the port channel. This method add supports to activate the port channels of the master controller. Change-Id: Ia3ace1a3b6038f417ba7ebee2ac7b09b646ad1b8 Signed-off-by: Hardik T Shah SoundWire:Intel: Add API to allocate port Intel master controller supports bi-directional port which can be configured either as Source or Sink ports. This API provides funtionality to dynamically allocate ports for audio streaming. It picks up the free port attaches the ALH stream to it and returns. This is called by ASoC platform driver for port allocation for streaming. Change-Id: Id3747f35b0c476ebe3900115f66daad11adb0d10 Signed-off-by: Hardik T Shah SoundWire:Intel: Add support for ALH stream to port mapping. This patch adds functionality to allocate the ALH stream and map it to the master controller. There can be one-to-one mapping from ALH stream to Port. Function takes care of allocating PCM stream v/s PDM stream based on the calling function requirement. Change-Id: Ic39e6977240df905ff2e95bf9d50abbda6246a28 Signed-off-by: Hardik T Shah SoundWire:Intel: Add the interrupt handler for master controller. This patch adds the interrupt handler for the Intel master controller. It reports the slave status to the bus driver. Change-Id: I1e829c9f830010fe6b6f95a81c81ba48e3b16d79 Signed-off-by: Hardik T Shah SoundWire:Intel: Add the Kconfig and Makefile option. Change-Id: Ic71ae25526b6a00edd9b562864562481e50b3a0e Signed-off-by: Hardik T Shah SoundWire: regmap: Add regmap support for SoundWire bus. This patch adds support for the SoundWire regmap. Change-Id: I0d892f2526c8eb4231f6436e1a539f50911fcd5b Signed-off-by: Hardik T Shah SoundWire: Add API to get and put the master module. This patch implements the API to get and put the master controller module associated with the bus driver. It increments the reference count for every get and decrements for every put. Change-Id: If4f7917b41852b0696a3485fd5f7fcb4258d200f Signed-off-by: Hardik T Shah SoundWire:Maxim: Add support for FPGA based maxim slave. Change-Id: I98154827d88bf87bde49e4d5d704d0e8372fd160 Signed-off-by: Hardik T Shah SoundWire:CNL: Fix the memory corruption. This patch fixes the possible memory corruption. in SoundWire controller driver. Change-Id: I6a19dac25dee9972fb126a75f159b800c18c075a Signed-off-by: Hardik T Shah SoundWire:Maxim: Fix the maxim slave compilation. This patch fixed the Maxim compilation issue. Config option was not correct in Makefile. Change-Id: Idfb287d7c1bbec19fa707e277ee4ddbb3b375217 Signed-off-by: Hardik T Shah SoundWire:Maxim: Add support for multiple slaves. This patch adds support for multiple slaves for the Maxim codec driver. Change-Id: I27602e393e8ddee589bdaf66f833c7ae0be1cee1 Signed-off-by: Hardik T Shah SoundWire: Add support to re-enumerate slave. Slaves might get un-attached because of the link going down due to power management or due to the glitch in clock, where sync is lost. Add support to re-enumerate the slave with same slave number. Change-Id: I443d060cf96c7e7df4e05a188079443f71f0af76 Signed-off-by: Hardik T Shah SoundWire: Add power management support to bus driver 1. Add helper functions to support the power management for both master controller and slave device. 2. Call appropriate runtime PM functions for the master controller and slave device based n bus APIs. Change-Id: I6231cd7258c6d6099f486f26a68bb9bb713db9c1 Signed-off-by: Hardik T Shah SDW:Intel: Add support for PM for master controller Add power management support for the Intel SoundWire master controller driver. Change-Id: Ifa6205cfbf6cf7ec6c4b9f7b4e61ef79e4818889 Signed-off-by: Hardik T Shah SDW: CNL: Fix the PDM port allocation. 1. Fix the PDM stream allocation. 2. Fix port_ctrl and PDI configuration for PDM stream allocation Change-Id: I83f09240d6852f9730b003e607c4dae24b76423e Signed-off-by: Hardik T Shah Reviewed-on: --- drivers/Kconfig | 1 + drivers/Makefile | 1 + drivers/base/regmap/Kconfig | 11 + drivers/base/regmap/Makefile | 1 + drivers/base/regmap/regmap-sdwint.c | 252 +++ drivers/sdw/Kconfig | 17 + drivers/sdw/Makefile | 5 + drivers/sdw/sdw.c | 2345 ++++++++++++++++++++++++++ drivers/sdw/sdw_bwcalc.c | 2366 +++++++++++++++++++++++++++ drivers/sdw/sdw_cnl.c | 1583 ++++++++++++++++++ drivers/sdw/sdw_cnl_priv.h | 337 ++++ drivers/sdw/sdw_maxim.c | 146 ++ drivers/sdw/sdw_priv.h | 243 +++ drivers/soundwire/Kconfig | 1 + include/linux/mod_devicetable.h | 13 + include/linux/regmap.h | 7 + include/linux/sdw/sdw_cnl.h | 96 ++ include/linux/sdw/sdw_registers.h | 157 ++ include/linux/sdw_bus.h | 1354 +++++++++++++++ include/trace/events/sdw.h | 232 +++ 20 files changed, 9168 insertions(+) create mode 100644 drivers/base/regmap/regmap-sdwint.c create mode 100644 drivers/sdw/Kconfig create mode 100644 drivers/sdw/Makefile create mode 100644 drivers/sdw/sdw.c create mode 100644 drivers/sdw/sdw_bwcalc.c create mode 100644 drivers/sdw/sdw_cnl.c create mode 100644 drivers/sdw/sdw_cnl_priv.h create mode 100644 drivers/sdw/sdw_maxim.c create mode 100644 drivers/sdw/sdw_priv.h create mode 100644 include/linux/sdw/sdw_cnl.h create mode 100644 include/linux/sdw/sdw_registers.h create mode 100644 include/linux/sdw_bus.h create mode 100644 include/trace/events/sdw.h diff --git a/drivers/Kconfig b/drivers/Kconfig index ab4d43923c4d..5af1a08bff23 100644 --- a/drivers/Kconfig +++ b/drivers/Kconfig @@ -212,6 +212,7 @@ source "drivers/tee/Kconfig" source "drivers/mux/Kconfig" source "drivers/opp/Kconfig" +source "drivers/sdw/Kconfig" source "drivers/visorbus/Kconfig" diff --git a/drivers/Makefile b/drivers/Makefile index 578f469f72fb..a1a8bf6df4d3 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -186,3 +186,4 @@ obj-$(CONFIG_MULTIPLEXER) += mux/ obj-$(CONFIG_UNISYS_VISORBUS) += visorbus/ obj-$(CONFIG_SIOX) += siox/ obj-$(CONFIG_GNSS) += gnss/ +obj-$(CONFIG_SDW) += sdw/ diff --git a/drivers/base/regmap/Kconfig b/drivers/base/regmap/Kconfig index 6ad5ef48b61e..afa9b8459572 100644 --- a/drivers/base/regmap/Kconfig +++ b/drivers/base/regmap/Kconfig @@ -49,3 +49,14 @@ config REGMAP_SOUNDWIRE config REGMAP_SCCB tristate depends on I2C + +config REGMAP_HWSPINLOCK + bool + +config REGMAP_SDW + default n + tristate "Regmap support for soundwire" + depends on SDW + help + Enable this if regmap support is required for + soundwire slave devices. diff --git a/drivers/base/regmap/Makefile b/drivers/base/regmap/Makefile index f5b4e8851d00..d78c51ae3da2 100644 --- a/drivers/base/regmap/Makefile +++ b/drivers/base/regmap/Makefile @@ -16,3 +16,4 @@ obj-$(CONFIG_REGMAP_IRQ) += regmap-irq.o obj-$(CONFIG_REGMAP_W1) += regmap-w1.o obj-$(CONFIG_REGMAP_SOUNDWIRE) += regmap-sdw.o obj-$(CONFIG_REGMAP_SCCB) += regmap-sccb.o +obj-$(CONFIG_REGMAP_SDW) += regmap-sdwint.o diff --git a/drivers/base/regmap/regmap-sdwint.c b/drivers/base/regmap/regmap-sdwint.c new file mode 100644 index 000000000000..ed8c28db03b8 --- /dev/null +++ b/drivers/base/regmap/regmap-sdwint.c @@ -0,0 +1,252 @@ +/* + * regmap-sdw.c - Register map access API - SoundWire support + * + * Copyright (C) 2015-2016 Intel Corp + * Author: Hardik T Shah + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * 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; version 2 of the License. + * + * 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. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + */ +#include +#include +#include + +#include "internal.h" + +#define SDW_SCP_ADDRPAGE1_MASK 0xFF +#define SDW_SCP_ADDRPAGE1_SHIFT 15 + +#define SDW_SCP_ADDRPAGE2_MASK 0xFF +#define SDW_SCP_ADDRPAGE2_SHIFT 22 + +#define SDW_REGADDR_SHIFT 0x0 +#define SDW_REGADDR_MASK 0xFFFF + +#define SDW_MAX_REG_ADDR 65536 + +static int regmap_sdw_read(void *context, + const void *reg, size_t reg_size, + void *val, size_t val_size) +{ + struct device *dev = context; + struct sdw_slv *sdw = to_sdw_slave(dev); + struct sdw_msg xfer; + int ret, scp_addr1, scp_addr2; + int reg_command; + int reg_addr = *(u32 *)reg; + size_t t_val_size = 0, t_size; + int offset; + u8 *t_val; + + /* All registers are 4 byte on SoundWire bus */ + if (reg_size != 4) + return -ENOTSUPP; + + xfer.slave_addr = sdw->slv_number; + xfer.ssp_tag = 0; + xfer.flag = SDW_MSG_FLAG_READ; + xfer.len = 0; + t_val = val; + + offset = 0; + reg_command = (reg_addr >> SDW_REGADDR_SHIFT) & + SDW_REGADDR_MASK; + if (val_size > SDW_MAX_REG_ADDR) + t_size = SDW_MAX_REG_ADDR - reg_command; + else + t_size = val_size; + while (t_val_size < val_size) { + + scp_addr1 = (reg_addr >> SDW_SCP_ADDRPAGE1_SHIFT) & + SDW_SCP_ADDRPAGE1_MASK; + scp_addr2 = (reg_addr >> SDW_SCP_ADDRPAGE2_SHIFT) & + SDW_SCP_ADDRPAGE2_MASK; + xfer.addr_page1 = scp_addr1; + xfer.addr_page2 = scp_addr2; + xfer.addr = reg_command; + xfer.len += t_size; + xfer.buf = &t_val[offset]; + ret = sdw_slave_transfer(sdw->mstr, &xfer, 1); + if (ret < 0) + return ret; + else if (ret != 1) + return -EIO; + + t_val_size += t_size; + offset += t_size; + if (val_size - t_val_size > 65535) + t_size = 65535; + else + t_size = val_size - t_val_size; + reg_addr += t_size; + reg_command = (reg_addr >> SDW_REGADDR_SHIFT) & + SDW_REGADDR_MASK; + } + return 0; +} + +static int regmap_sdw_gather_write(void *context, + const void *reg, size_t reg_size, + const void *val, size_t val_size) +{ + struct device *dev = context; + struct sdw_slv *sdw = to_sdw_slave(dev); + struct sdw_msg xfer; + int ret, scp_addr1, scp_addr2; + int reg_command; + int reg_addr = *(u32 *)reg; + size_t t_val_size = 0, t_size; + int offset; + u8 *t_val; + + /* All registers are 4 byte on SoundWire bus */ + if (reg_size != 4) + return -ENOTSUPP; + + if (!sdw) + return 0; + + xfer.slave_addr = sdw->slv_number; + xfer.ssp_tag = 0; + xfer.flag = SDW_MSG_FLAG_WRITE; + xfer.len = 0; + t_val = (u8 *)val; + + offset = 0; + reg_command = (reg_addr >> SDW_REGADDR_SHIFT) & + SDW_REGADDR_MASK; + if (val_size > SDW_MAX_REG_ADDR) + t_size = SDW_MAX_REG_ADDR - reg_command; + else + t_size = val_size; + while (t_val_size < val_size) { + + scp_addr1 = (reg_addr >> SDW_SCP_ADDRPAGE1_SHIFT) & + SDW_SCP_ADDRPAGE1_MASK; + scp_addr2 = (reg_addr >> SDW_SCP_ADDRPAGE2_SHIFT) & + SDW_SCP_ADDRPAGE2_MASK; + xfer.addr_page1 = scp_addr1; + xfer.addr_page2 = scp_addr2; + xfer.addr = reg_command; + xfer.len += t_size; + xfer.buf = &t_val[offset]; + ret = sdw_slave_transfer(sdw->mstr, &xfer, 1); + if (ret < 0) + return ret; + else if (ret != 1) + return -EIO; + + t_val_size += t_size; + offset += t_size; + if (val_size - t_val_size > 65535) + t_size = 65535; + else + t_size = val_size - t_val_size; + reg_addr += t_size; + reg_command = (reg_addr >> SDW_REGADDR_SHIFT) & + SDW_REGADDR_MASK; + } + return 0; +} + +static inline void regmap_sdw_count_check(size_t count, u32 offset) +{ + BUG_ON(count <= offset); +} + +static int regmap_sdw_write(void *context, const void *data, size_t count) +{ + /* 4-byte register address for the soundwire */ + unsigned int offset = 4; + + regmap_sdw_count_check(count, offset); + return regmap_sdw_gather_write(context, data, 4, + data + offset, count - offset); +} + +static struct regmap_bus regmap_sdw = { + .write = regmap_sdw_write, + .gather_write = regmap_sdw_gather_write, + .read = regmap_sdw_read, + .reg_format_endian_default = REGMAP_ENDIAN_LITTLE, + .val_format_endian_default = REGMAP_ENDIAN_LITTLE, +}; + +static int regmap_sdw_config_check(const struct regmap_config *config) +{ + /* All register are 8-bits wide as per MIPI Soundwire 1.0 Spec */ + if (config->val_bits != 8) + return -ENOTSUPP; + /* Registers are 32 bit in size, based on SCP_ADDR1 and SCP_ADDR2 + * implementation address range may vary in slave. + */ + if (config->reg_bits != 32) + return -ENOTSUPP; + /* SoundWire register address are contiguous. */ + if (config->reg_stride != 0) + return -ENOTSUPP; + if (config->pad_bits != 0) + return -ENOTSUPP; + + + return 0; +} + +/** + * regmap_init_sdwint(): Initialise register map + * + * @sdw: Device that will be interacted with + * @config: Configuration for register map + * + * The return value will be an ERR_PTR() on error or a valid pointer to + * a struct regmap. + */ +struct regmap *regmap_init_sdwint(struct sdw_slv *sdw, + const struct regmap_config *config) +{ + int ret; + + ret = regmap_sdw_config_check(config); + if (ret) + return ERR_PTR(ret); + + return regmap_init(&sdw->dev, ®map_sdw, &sdw->dev, config); +} +EXPORT_SYMBOL_GPL(regmap_init_sdwint); + + +/** + * devm_regmap_init_sdwint(): Initialise managed register map + * + * @sdw Device that will be interacted with + * @config: Configuration for register map + * + * The return value will be an ERR_PTR() on error or a valid pointer + * to a struct regmap. The regmap will be automatically freed by the + * device management code. + */ +struct regmap *devm_regmap_init_sdwint(struct sdw_slv *sdw, + const struct regmap_config *config) +{ + int ret; + + ret = regmap_sdw_config_check(config); + if (ret) + return ERR_PTR(ret); + + return devm_regmap_init(&sdw->dev, ®map_sdw, &sdw->dev, config); +} +EXPORT_SYMBOL_GPL(devm_regmap_init_sdwint); + +MODULE_LICENSE("GPL v2"); diff --git a/drivers/sdw/Kconfig b/drivers/sdw/Kconfig new file mode 100644 index 000000000000..90e954c392e0 --- /dev/null +++ b/drivers/sdw/Kconfig @@ -0,0 +1,17 @@ +menuconfig SDW + tristate "SoundWire bus support" + help + SoundWire interface is typically used for transporting data + related to audio functions. +menuconfig SDW_CNL + tristate "Intel SoundWire master controller support" + depends on SDW + help + Intel SoundWire master controller driver +menuconfig SDW_MAXIM_SLAVE + bool "SoundWire Slave for the Intel CNL FPGA" + depends on SDW + help + SoundWire Slave on FPGA platform for Intel CNL IP + Mostly N for all the cases other than CNL Slave FPGA + diff --git a/drivers/sdw/Makefile b/drivers/sdw/Makefile new file mode 100644 index 000000000000..184682a88a1a --- /dev/null +++ b/drivers/sdw/Makefile @@ -0,0 +1,5 @@ +sdw_bus-objs := sdw.o sdw_bwcalc.o + +obj-$(CONFIG_SDW) += sdw_bus.o +obj-$(CONFIG_SDW_CNL) += sdw_cnl.o +obj-$(CONFIG_SDW_MAXIM_SLAVE) += sdw_maxim.o diff --git a/drivers/sdw/sdw.c b/drivers/sdw/sdw.c new file mode 100644 index 000000000000..6a1ff5e59401 --- /dev/null +++ b/drivers/sdw/sdw.c @@ -0,0 +1,2345 @@ +/* + * sdw.c - SoundWire Bus driver implementation + * + * Copyright (C) 2015-2016 Intel Corp + * Author: Hardik T Shah + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * 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; version 2 of the License. + * + * 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. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "sdw_priv.h" + +#define sdw_slave_attr_gr NULL +#define sdw_mstr_attr_gr NULL + +#define CREATE_TRACE_POINTS +#include + +/* Global instance handling all the SoundWire buses */ +struct sdw_core sdw_core; + +static void sdw_slave_release(struct device *dev) +{ + kfree(to_sdw_slave(dev)); +} + +static void sdw_mstr_release(struct device *dev) +{ + struct sdw_master *mstr = to_sdw_master(dev); + + complete(&mstr->slv_released); +} + +static struct device_type sdw_slv_type = { + .groups = sdw_slave_attr_gr, + .release = sdw_slave_release, +}; + +static struct device_type sdw_mstr_type = { + .groups = sdw_mstr_attr_gr, + .release = sdw_mstr_release, +}; +/** + * sdw_slave_verify - return parameter as sdw_slv, or NULL + * @dev: device, probably from some driver model iterator + * + * When traversing the driver model tree, perhaps using driver model + * iterators like @device_for_each_child(), you can't assume very much + * about the nodes you find. Use this function to avoid oopses caused + * by wrongly treating some non-SDW device as an sdw_slv. + */ +struct sdw_slv *sdw_slave_verify(struct device *dev) +{ + return (dev->type == &sdw_slv_type) + ? to_sdw_slave(dev) + : NULL; +} + +/** + * sdw_mstr_verify - return parameter as sdw_master, or NULL + * @dev: device, probably from some driver model iterator + * + * When traversing the driver model tree, perhaps using driver model + * iterators like @device_for_each_child(), you can't assume very much + * about the nodes you find. Use this function to avoid oopses caused + * by wrongly treating some non-SDW device as an sdw_slv. + */ +struct sdw_master *sdw_mstr_verify(struct device *dev) +{ + return (dev->type == &sdw_mstr_type) + ? to_sdw_master(dev) + : NULL; +} + +static const struct sdw_slv_id *sdw_match_slave(const struct sdw_slv_id *id, + const struct sdw_slv *sdw_slv) +{ + while (id->name[0]) { + if (strncmp(sdw_slv->name, id->name, SOUNDWIRE_NAME_SIZE) == 0) + return id; + id++; + } + return NULL; +} + +static const struct sdw_master_id *sdw_match_master( + const struct sdw_master_id *id, + const struct sdw_master *sdw_mstr) +{ + if (!id) + return NULL; + while (id->name[0]) { + if (strncmp(sdw_mstr->name, id->name, SOUNDWIRE_NAME_SIZE) == 0) + return id; + id++; + } + return NULL; +} + +static int sdw_slv_match(struct device *dev, struct device_driver *driver) +{ + struct sdw_slv *sdw_slv; + struct sdw_slave_driver *drv = to_sdw_slave_driver(driver); + int ret = 0; + + /* Check if driver is slave type or not, both master and slave + * driver has first field as driver_type, so if driver is not + * of slave type return + */ + if (drv->driver_type != SDW_DRIVER_TYPE_SLAVE) + return ret; + + sdw_slv = to_sdw_slave(dev); + + if (drv->id_table) + ret = (sdw_match_slave(drv->id_table, sdw_slv) != NULL); + + if (driver->name && !ret) + ret = (strncmp(sdw_slv->name, driver->name, SOUNDWIRE_NAME_SIZE) + == 0); + if (ret) + sdw_slv->driver = drv; + return ret; +} +static int sdw_mstr_match(struct device *dev, struct device_driver *driver) +{ + struct sdw_master *sdw_mstr; + struct sdw_mstr_driver *drv = to_sdw_mstr_driver(driver); + int ret = 0; + + /* Check if driver is slave type or not, both master and slave + * driver has first field as driver_type, so if driver is not + * of slave type return + */ + if (drv->driver_type != SDW_DRIVER_TYPE_MASTER) + return ret; + + sdw_mstr = to_sdw_master(dev); + + if (drv->id_table) + ret = (sdw_match_master(drv->id_table, sdw_mstr) != NULL); + + if (driver->name) + ret = (strncmp(sdw_mstr->name, driver->name, + SOUNDWIRE_NAME_SIZE) == 0); + if (ret) + sdw_mstr->driver = drv; + + return ret; +} + +static int sdw_mstr_probe(struct device *dev) +{ + const struct sdw_mstr_driver *sdrv = to_sdw_mstr_driver(dev->driver); + struct sdw_master *mstr = to_sdw_master(dev); + int ret = 0; + + if (!sdrv->probe) + return -ENODEV; + ret = dev_pm_domain_attach(dev, true); + if (ret != -EPROBE_DEFER) { + ret = sdrv->probe(mstr, sdw_match_master(sdrv->id_table, mstr)); + if (ret) + dev_pm_domain_detach(dev, true); + } + return ret; +} + +static int sdw_slv_probe(struct device *dev) +{ + const struct sdw_slave_driver *sdrv = to_sdw_slave_driver(dev->driver); + struct sdw_slv *sdwslv = to_sdw_slave(dev); + int ret = 0; + + if (!sdrv->probe) + return -ENODEV; + ret = dev_pm_domain_attach(dev, true); + if (ret != -EPROBE_DEFER) { + ret = sdrv->probe(sdwslv, sdw_match_slave(sdrv->id_table, + sdwslv)); + return 0; + if (ret) + dev_pm_domain_detach(dev, true); + } + return ret; +} + +static int sdw_mstr_remove(struct device *dev) +{ + const struct sdw_mstr_driver *sdrv = to_sdw_mstr_driver(dev->driver); + int ret = 0; + + if (sdrv->remove) + ret = sdrv->remove(to_sdw_master(dev)); + else + return -ENODEV; + + dev_pm_domain_detach(dev, true); + return ret; + +} + +static int sdw_slv_remove(struct device *dev) +{ + const struct sdw_slave_driver *sdrv = to_sdw_slave_driver(dev->driver); + int ret = 0; + + if (sdrv->remove) + ret = sdrv->remove(to_sdw_slave(dev)); + else + return -ENODEV; + + dev_pm_domain_detach(dev, true); + return ret; +} + +static void sdw_slv_shutdown(struct device *dev) +{ + const struct sdw_slave_driver *sdrv = to_sdw_slave_driver(dev->driver); + + if (sdrv->shutdown) + sdrv->shutdown(to_sdw_slave(dev)); +} + +static void sdw_mstr_shutdown(struct device *dev) +{ + const struct sdw_mstr_driver *sdrv = to_sdw_mstr_driver(dev->driver); + struct sdw_master *mstr = to_sdw_master(dev); + + if (sdrv->shutdown) + sdrv->shutdown(mstr); +} + +static void sdw_shutdown(struct device *dev) +{ + struct sdw_slv *sdw_slv; + struct sdw_master *sdw_mstr; + + sdw_slv = sdw_slave_verify(dev); + sdw_mstr = sdw_mstr_verify(dev); + if (sdw_slv) + sdw_slv_shutdown(dev); + else if (sdw_mstr) + sdw_mstr_shutdown(dev); +} + +static int sdw_remove(struct device *dev) +{ + struct sdw_slv *sdw_slv; + struct sdw_master *sdw_mstr; + + sdw_slv = sdw_slave_verify(dev); + sdw_mstr = sdw_mstr_verify(dev); + if (sdw_slv) + return sdw_slv_remove(dev); + else if (sdw_mstr) + return sdw_mstr_remove(dev); + + return 0; +} + +static int sdw_probe(struct device *dev) +{ + + struct sdw_slv *sdw_slv; + struct sdw_master *sdw_mstr; + + sdw_slv = sdw_slave_verify(dev); + sdw_mstr = sdw_mstr_verify(dev); + if (sdw_slv) + return sdw_slv_probe(dev); + else if (sdw_mstr) + return sdw_mstr_probe(dev); + + return -ENODEV; + +} + +static int sdw_match(struct device *dev, struct device_driver *driver) +{ + struct sdw_slv *sdw_slv; + struct sdw_master *sdw_mstr; + + sdw_slv = sdw_slave_verify(dev); + sdw_mstr = sdw_mstr_verify(dev); + if (sdw_slv) + return sdw_slv_match(dev, driver); + else if (sdw_mstr) + return sdw_mstr_match(dev, driver); + return 0; + +} + +#ifdef CONFIG_PM_SLEEP +static int sdw_legacy_suspend(struct device *dev, pm_message_t mesg) +{ + struct sdw_slv *sdw_slv = NULL; + struct sdw_slave_driver *driver; + + if (dev->type == &sdw_slv_type) + sdw_slv = to_sdw_slave(dev); + + if (!sdw_slv || !dev->driver) + return 0; + + driver = to_sdw_slave_driver(dev->driver); + if (!driver->suspend) + return 0; + + return driver->suspend(sdw_slv, mesg); +} + +static int sdw_legacy_resume(struct device *dev) +{ + struct sdw_slv *sdw_slv = NULL; + struct sdw_slave_driver *driver; + + if (dev->type == &sdw_slv_type) + sdw_slv = to_sdw_slave(dev); + + if (!sdw_slv || !dev->driver) + return 0; + + driver = to_sdw_slave_driver(dev->driver); + if (!driver->resume) + return 0; + + return driver->resume(sdw_slv); +} + +static int sdw_pm_suspend(struct device *dev) +{ + const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL; + + if (pm) + return pm_generic_suspend(dev); + else + return sdw_legacy_suspend(dev, PMSG_SUSPEND); +} + +static int sdw_pm_resume(struct device *dev) +{ + const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL; + + if (pm) + return pm_generic_resume(dev); + else + return sdw_legacy_resume(dev); +} + +static const struct dev_pm_ops soundwire_pm = { + .suspend = sdw_pm_suspend, + .resume = sdw_pm_resume, + .runtime_suspend = pm_generic_runtime_suspend, + .runtime_resume = pm_generic_runtime_resume, +}; + +#else +#define sdw_pm_suspend NULL +#define sdw_pm_resume NULL +#endif + +struct bus_type sdwint_bus_type = { + .name = "soundwire", + .match = sdw_match, + .probe = sdw_probe, + .remove = sdw_remove, + .shutdown = sdw_shutdown, + .pm = &soundwire_pm, +}; +EXPORT_SYMBOL_GPL(sdwint_bus_type); + +struct device sdw_slv = { + .init_name = "soundwire", +}; + +static struct static_key sdw_trace_msg = STATIC_KEY_INIT_FALSE; + +int sdw_transfer_trace_reg(void) +{ + static_key_slow_inc(&sdw_trace_msg); +} + +void sdw_transfer_trace_unreg(void) +{ + static_key_slow_dec(&sdw_trace_msg); +} + +/** + * sdw_lock_mstr - Get exclusive access to an SDW bus segment + * @mstr: Target SDW bus segment + */ +void sdw_lock_mstr(struct sdw_master *mstr) +{ + rt_mutex_lock(&mstr->bus_lock); +} +EXPORT_SYMBOL_GPL(sdw_lock_mstr); + +/** + * sdw_trylock_mstr - Try to get exclusive access to an SDW bus segment + * @mstr: Target SDW bus segment + */ +static int sdw_trylock_mstr(struct sdw_master *mstr) +{ + return rt_mutex_trylock(&mstr->bus_lock); +} + + +/** + * sdw_unlock_mstr - Release exclusive access to an SDW bus segment + * @mstr: Target SDW bus segment + */ +void sdw_unlock_mstr(struct sdw_master *mstr) +{ + rt_mutex_unlock(&mstr->bus_lock); +} +EXPORT_SYMBOL_GPL(sdw_unlock_mstr); + + +static int sdw_assign_slv_number(struct sdw_master *mstr, + struct sdw_msg *msg) +{ + int i, j, ret = -1; + + sdw_lock_mstr(mstr); + for (i = 1; i <= SOUNDWIRE_MAX_DEVICES; i++) { + if (mstr->sdw_addr[i].assigned == true) + continue; + mstr->sdw_addr[i].assigned = true; + for (j = 0; j < 6; j++) + mstr->sdw_addr[i].dev_id[j] = msg->buf[j]; + ret = i; + break; + } + sdw_unlock_mstr(mstr); + return ret; +} + +static int sdw_program_slv_address(struct sdw_master *mstr, + u8 slave_addr) +{ + struct sdw_msg msg; + u8 buf[1] = {0}; + int ret; + + buf[0] = slave_addr; + msg.ssp_tag = 0; + msg.flag = SDW_MSG_FLAG_WRITE; + msg.addr = SDW_SCP_DEVNUMBER; + msg.len = 1; + msg.buf = buf; + msg.slave_addr = 0x0; + msg.addr_page1 = 0x0; + msg.addr_page2 = 0x0; + + ret = sdw_slave_transfer(mstr, &msg, 1); + if (ret != 1) { + dev_err(&mstr->dev, "Program Slave address change\n"); + return ret; + } + return 0; +} + +static int sdw_find_slave(struct sdw_master *mstr, struct sdw_msg + *msg, bool *found) +{ + struct sdw_slv_addr *sdw_addr; + int ret = 0, i, comparison; + *found = false; + + sdw_lock_mstr(mstr); + sdw_addr = mstr->sdw_addr; + for (i = 1; i <= SOUNDWIRE_MAX_DEVICES; i++) { + comparison = memcmp(sdw_addr[i].dev_id, msg->buf, + SDW_NUM_DEV_ID_REGISTERS); + if ((!comparison) && (sdw_addr[i].assigned == true)) { + *found = true; + break; + } + } + sdw_unlock_mstr(mstr); + if (*found == true) + ret = sdw_program_slv_address(mstr, sdw_addr[i].slv_number); + return ret; +} + +static void sdw_free_slv_number(struct sdw_master *mstr, + int slv_number) +{ + int i; + + sdw_lock_mstr(mstr); + for (i = 0; i <= SOUNDWIRE_MAX_DEVICES; i++) { + if (slv_number == mstr->sdw_addr[i].slv_number) { + mstr->sdw_addr[slv_number].assigned = false; + memset(&mstr->sdw_addr[slv_number].dev_id[0], 0x0, 6); + } + } + sdw_unlock_mstr(mstr); +} + +static int sdw_register_slave(struct sdw_master *mstr) +{ + int ret = 0, i, ports; + struct sdw_msg msg; + u8 buf[6] = {0}; + struct sdw_slv *sdw_slv; + int slv_number = -1; + bool found = false; + + + msg.ssp_tag = 0; + msg.flag = SDW_MSG_FLAG_READ; + msg.addr = SDW_SCP_DEVID_0; + msg.len = 6; + msg.buf = buf; + msg.slave_addr = 0x0; + msg.addr_page1 = 0x0; + msg.addr_page2 = 0x0; + + while ((ret = (sdw_slave_transfer(mstr, &msg, 1)) == 1)) { + ret = sdw_find_slave(mstr, &msg, &found); + if (found && !ret) { + dev_info(&mstr->dev, "Slave already registered\n"); + continue; + /* Even if slave registering fails we continue for other + * slave status, but we flag error + */ + } else if (ret) { + dev_err(&mstr->dev, "Re-registering slave failed"); + continue; + } + slv_number = sdw_assign_slv_number(mstr, &msg); + if (slv_number <= 0) { + dev_err(&mstr->dev, "Failed to assign slv_number\n"); + ret = -EINVAL; + goto slv_number_assign_fail; + } + sdw_slv = kzalloc(sizeof(struct sdw_slv), GFP_KERNEL); + if (!sdw_slv) { + ret = -ENOMEM; + goto mem_alloc_failed; + } + sdw_slv->mstr = mstr; + sdw_slv->dev.parent = &sdw_slv->mstr->dev; + sdw_slv->dev.bus = &sdwint_bus_type; + sdw_slv->dev.type = &sdw_slv_type; + sdw_slv->slv_addr = &mstr->sdw_addr[slv_number]; + sdw_slv->slv_addr->slave = sdw_slv; + /* We have assigned new slave number, so its not present + * till it again attaches to bus with this new + * slave address + */ + sdw_slv->slv_addr->status = SDW_SLAVE_STAT_NOT_PRESENT; + for (i = 0; i < 6; i++) + sdw_slv->dev_id[i] = msg.buf[i]; + dev_dbg(&mstr->dev, "SDW slave slave id found with values\n"); + dev_dbg(&mstr->dev, "dev_id0 to dev_id5: %x:%x:%x:%x:%x:%x\n", + msg.buf[0], msg.buf[1], msg.buf[2], + msg.buf[3], msg.buf[4], msg.buf[5]); + dev_dbg(&mstr->dev, "Slave number assigned is %x\n", slv_number); + /* TODO: Fill the sdw_slv structre from ACPI */ + ports = sdw_slv->sdw_slv_cap.num_of_sdw_ports; + /* Add 1 for port 0 for simplicity */ + ports++; + sdw_slv->port_ready = + kzalloc((sizeof(struct completion) * ports), + GFP_KERNEL); + if (!sdw_slv->port_ready) { + ret = -ENOMEM; + goto port_alloc_mem_failed; + } + for (i = 0; i < ports; i++) + init_completion(&sdw_slv->port_ready[i]); + + dev_set_name(&sdw_slv->dev, "sdw-slave%d-%02x:%02x:%02x:%02x:%02x:%02x", + sdw_master_id(mstr), + sdw_slv->dev_id[0], + sdw_slv->dev_id[1], + sdw_slv->dev_id[2], + sdw_slv->dev_id[3], + sdw_slv->dev_id[4], + sdw_slv->dev_id[5] + mstr->nr); + /* Set name based on dev_id. This will be + * compared to load driver + */ + sprintf(sdw_slv->name, "%02x:%02x:%02x:%02x:%02x:%02x", + sdw_slv->dev_id[0], + sdw_slv->dev_id[1], + sdw_slv->dev_id[2], + sdw_slv->dev_id[3], + sdw_slv->dev_id[4], + sdw_slv->dev_id[5] + mstr->nr); + ret = device_register(&sdw_slv->dev); + if (ret) { + dev_err(&mstr->dev, "Register slave failed\n"); + goto reg_slv_failed; + } + ret = sdw_program_slv_address(mstr, slv_number); + if (ret) { + dev_err(&mstr->dev, "Programming slave address failed\n"); + goto program_slv_failed; + } + dev_dbg(&mstr->dev, "Slave registered with bus id %s\n", + dev_name(&sdw_slv->dev)); + sdw_slv->slv_number = slv_number; + mstr->num_slv++; + sdw_lock_mstr(mstr); + list_add_tail(&sdw_slv->node, &mstr->slv_list); + sdw_unlock_mstr(mstr); + + } + return 0; +program_slv_failed: + device_unregister(&sdw_slv->dev); +port_alloc_mem_failed: +reg_slv_failed: + kfree(sdw_slv); +mem_alloc_failed: + sdw_free_slv_number(mstr, slv_number); +slv_number_assign_fail: + return ret; + +} + +/** + * __sdw_transfer - unlocked flavor of sdw_slave_transfer + * @mstr: Handle to SDW bus + * @msg: One or more messages to execute before STOP is issued to + * terminate the operation; each message begins with a START. + * @num: Number of messages to be executed. + * + * Returns negative errno, else the number of messages executed. + * + * Adapter lock must be held when calling this function. No debug logging + * takes place. mstr->algo->master_xfer existence isn't checked. + */ +int __sdw_transfer(struct sdw_master *mstr, struct sdw_msg *msg, int num) +{ + unsigned long orig_jiffies; + int ret = 0, try, i; + struct sdw_slv_capabilities *slv_cap; + int program_scp_addr_page; + int addr = msg->slave_addr; + + /* sdw_trace_msg gets enabled when tracepoint sdw_slave_transfer gets + * enabled. This is an efficient way of keeping the for-loop from + * being executed when not needed. + */ + if (static_key_false(&sdw_trace_msg)) { + int i; + + for (i = 0; i < num; i++) + if (msg[i].flag & SDW_MSG_FLAG_READ) + trace_sdw_read(mstr, &msg[i], i); + else + trace_sdw_write(mstr, &msg[i], i); + } + orig_jiffies = jiffies; + for (i = 0; i < num; i++) { + for (ret = 0, try = 0; try <= mstr->retries; try++) { + if (msg->slave_addr == 0) + /* If we are enumerating slave address 0, + * we dont program scp, it should be set + * default to 0 + */ + program_scp_addr_page = 0; + else if (msg->slave_addr == 15) + /* If we are broadcasting, we need to program + * the SCP address as some slaves will be + * supporting it while some wont be. + * So it should be programmed + */ + program_scp_addr_page = 1; + + else { + slv_cap = + &mstr->sdw_addr[addr].slave->sdw_slv_cap; + program_scp_addr_page = + slv_cap->paging_supported; + } + ret = mstr->driver->mstr_ops->xfer_msg(mstr, + msg, program_scp_addr_page); + if (ret != -EAGAIN) + break; + if (time_after(jiffies, + orig_jiffies + mstr->timeout)) + break; + } + } + + if (static_key_false(&sdw_trace_msg)) { + int i; + + for (i = 0; i < msg->len; i++) + if (msg[i].flag & SDW_MSG_FLAG_READ) + trace_sdw_reply(mstr, &msg[i], i); + trace_sdw_result(mstr, i, ret); + } + if (!ret) + return i; + return ret; +} +EXPORT_SYMBOL_GPL(__sdw_transfer); + +/* NO PM version of slave transfer. Called from power management APIs + * to avoid dead locks. + */ +static int sdw_slave_transfer_nopm(struct sdw_master *mstr, struct sdw_msg *msg, + int num) +{ + int ret; + + if (mstr->driver->mstr_ops->xfer_msg) { + ret = __sdw_transfer(mstr, msg, num); + return ret; + } + dev_dbg(&mstr->dev, "SDW level transfers not supported\n"); + return -EOPNOTSUPP; +} + +/** + * sdw_slave_transfer: Transfer message between slave and mstr on the bus. + * @mstr: mstr master which will transfer the message + * @msg: Array of messages to be transferred. + * @num: Number of messages to be transferred, messages include read and write + * messages, but not the ping messages. + */ +int sdw_slave_transfer(struct sdw_master *mstr, struct sdw_msg *msg, int num) +{ + int ret; + + /* REVISIT the fault reporting model here is weak: + * + * - When we get an error after receiving N bytes from a slave, + * there is no way to report "N". + * + * - When we get a NAK after transmitting N bytes to a slave, + * there is no way to report "N" ... or to let the mstr + * continue executing the rest of this combined message, if + * that's the appropriate response. + * + * - When for example "num" is two and we successfully complete + * the first message but get an error part way through the + * second, it's unclear whether that should be reported as + * one (discarding status on the second message) or errno + * (discarding status on the first one). + */ + if (!(mstr->driver->mstr_ops->xfer_msg)) { + dev_dbg(&mstr->dev, "SDW level transfers not supported\n"); + return -EOPNOTSUPP; + } + pm_runtime_get_sync(&mstr->dev); + if (in_atomic() || irqs_disabled()) { + ret = sdw_trylock_mstr(mstr); + if (!ret) { + /* SDW activity is ongoing. */ + ret = -EAGAIN; + goto out; + } + } else { + sdw_lock_mstr(mstr); + } + ret = __sdw_transfer(mstr, msg, num); + sdw_unlock_mstr(mstr); +out: + pm_runtime_mark_last_busy(&mstr->dev); + pm_runtime_put_sync_autosuspend(&mstr->dev); + return ret; +} +EXPORT_SYMBOL_GPL(sdw_slave_transfer); + +static int sdw_handle_dp0_interrupts(struct sdw_master *mstr, + struct sdw_slave *sdw_slv) +{ + int ret = 0; + struct sdw_msg rd_msg, wr_msg; + int impl_def_mask = 0; + u8 rbuf[1] = {0}, wbuf[1] = {0}; + + /* Create message for clearing the interrupts */ + wr_msg.ssp_tag = 0; + wr_msg.flag = SDW_MSG_FLAG_WRITE; + wr_msg.addr = SDW_DP0_INTCLEAR; + wr_msg.len = 1; + wr_msg.buf = wbuf; + wr_msg.slave_addr = sdw_slv->slv_number; + wr_msg.addr_page1 = 0x0; + wr_msg.addr_page2 = 0x0; + + /* Create message for reading the interrupts for DP0 interrupts*/ + rd_msg.ssp_tag = 0; + rd_msg.flag = SDW_MSG_FLAG_READ; + rd_msg.addr = SDW_DP0_INTSTAT; + rd_msg.len = 1; + rd_msg.buf = rbuf; + rd_msg.slave_addr = sdw_slv->slv_number; + rd_msg.addr_page1 = 0x0; + rd_msg.addr_page2 = 0x0; + ret = sdw_slave_transfer(mstr, &rd_msg, 1); + if (ret != 1) { + ret = -EINVAL; + dev_err(&mstr->dev, "Interrupt status read failed for slave %x\n", sdw_slv->slv_number); + goto out; + } + if (rd_msg.buf[0] & SDW_DP0_INTSTAT_TEST_FAIL_MASK) { + dev_err(&mstr->dev, "Test fail for slave %d port 0\n", + sdw_slv->slv_number); + wr_msg.buf[0] |= SDW_DP0_INTCLEAR_TEST_FAIL_MASK; + } + if (rd_msg.buf[0] & SDW_DP0_INTSTAT_PORT_READY_MASK) { + complete(&sdw_slv->port_ready[0]); + wr_msg.buf[0] |= SDW_DP0_INTCLEAR_PORT_READY_MASK; + } + if (rd_msg.buf[0] & SDW_DP0_INTMASK_BRA_FAILURE_MASK) { + /* TODO: Handle BRA failure */ + dev_err(&mstr->dev, "BRA failed for slave %d\n", + sdw_slv->slv_number); + wr_msg.buf[0] |= SDW_DP0_INTCLEAR_BRA_FAILURE_MASK; + } + impl_def_mask = SDW_DP0_INTSTAT_IMPDEF1_MASK | + SDW_DP0_INTSTAT_IMPDEF2_MASK | + SDW_DP0_INTSTAT_IMPDEF3_MASK; + if (rd_msg.buf[0] & impl_def_mask) { + /* TODO: Handle implementation defined mask ready */ + wr_msg.buf[0] |= impl_def_mask; + } + ret = sdw_slave_transfer(mstr, &wr_msg, 1); + if (ret != 1) { + ret = -EINVAL; + dev_err(&mstr->dev, "Register transfer failed\n"); + goto out; + } +out: + return ret; + +} + +static int sdw_handle_port_interrupt(struct sdw_master *mstr, + struct sdw_slave *sdw_slv, int port_num) +{ + int ret = 0; + struct sdw_msg rd_msg, wr_msg; + u8 rbuf[1], wbuf[1]; + int impl_def_mask = 0; + + if (port_num == 0) + ret = sdw_handle_dp0_interrupts(mstr, sdw_slv); + + /* Create message for reading the port interrupts */ + wr_msg.ssp_tag = 0; + wr_msg.flag = SDW_MSG_FLAG_WRITE; + wr_msg.addr = SDW_DPN_INTCLEAR + + (SDW_NUM_DATA_PORT_REGISTERS * port_num); + wr_msg.len = 1; + wr_msg.buf = wbuf; + wr_msg.slave_addr = sdw_slv->slv_number; + wr_msg.addr_page1 = 0x0; + wr_msg.addr_page2 = 0x0; + + rd_msg.ssp_tag = 0; + rd_msg.flag = SDW_MSG_FLAG_READ; + rd_msg.addr = SDW_DPN_INTSTAT + + (SDW_NUM_DATA_PORT_REGISTERS * port_num); + rd_msg.len = 1; + rd_msg.buf = rbuf; + rd_msg.slave_addr = sdw_slv->slv_number; + rd_msg.addr_page1 = 0x0; + rd_msg.addr_page2 = 0x0; + ret = sdw_slave_transfer(mstr, &rd_msg, 1); + if (ret != 1) { + ret = -EINVAL; + dev_err(&mstr->dev, "Port Status read failed for slv %x port %x\n", + sdw_slv->slv_number, port_num); + goto out; + } + if (rd_msg.buf[0] & SDW_DPN_INTSTAT_TEST_FAIL_MASK) { + dev_err(&mstr->dev, "Test fail for slave %x port %x\n", + sdw_slv->slv_number, port_num); + wr_msg.buf[0] |= SDW_DPN_INTCLEAR_TEST_FAIL_MASK; + } + if (rd_msg.buf[0] & SDW_DPN_INTSTAT_PORT_READY_MASK) { + complete(&sdw_slv->port_ready[port_num]); + wr_msg.buf[0] |= SDW_DPN_INTCLEAR_PORT_READY_MASK; + } + impl_def_mask = SDW_DPN_INTSTAT_IMPDEF1_MASK | + SDW_DPN_INTSTAT_IMPDEF2_MASK | + SDW_DPN_INTSTAT_IMPDEF3_MASK; + if (rd_msg.buf[0] & impl_def_mask) { + /* TODO: Handle implementation defined mask ready */ + wr_msg.buf[0] |= impl_def_mask; + } + /* Clear and Ack the interrupt */ + ret = sdw_slave_transfer(mstr, &wr_msg, 1); + if (ret != 1) { + ret = -EINVAL; + dev_err(&mstr->dev, "Register transfer failed\n"); + goto out; + } +out: + return ret; + +} +static int sdw_handle_slave_alerts(struct sdw_master *mstr, + struct sdw_slv *sdw_slv) +{ + struct sdw_msg rd_msg[3], wr_msg; + u8 rbuf[3], wbuf[1]; + int i, ret = 0; + int cs_port_mask, cs_port_register, cs_port_start, cs_ports; + + + /* Read Instat 1, Instat 2 and Instat 3 registers */ + rd_msg[0].ssp_tag = 0x0; + rd_msg[0].flag = SDW_MSG_FLAG_READ; + rd_msg[0].addr = SDW_SCP_INTSTAT_1; + rd_msg[0].len = 1; + rd_msg[0].buf = &rbuf[0]; + rd_msg[0].slave_addr = sdw_slv->slv_number; + rd_msg[0].addr_page1 = 0x0; + rd_msg[0].addr_page2 = 0x0; + + rd_msg[1].ssp_tag = 0x0; + rd_msg[1].flag = SDW_MSG_FLAG_READ; + rd_msg[1].addr = SDW_SCP_INTSTAT2; + rd_msg[1].len = 1; + rd_msg[1].buf = &rbuf[1]; + rd_msg[1].slave_addr = sdw_slv->slv_number; + rd_msg[1].addr_page1 = 0x0; + rd_msg[1].addr_page2 = 0x0; + + rd_msg[2].ssp_tag = 0x0; + rd_msg[2].flag = SDW_MSG_FLAG_READ; + rd_msg[2].addr = SDW_SCP_INTSTAT3; + rd_msg[2].len = 1; + rd_msg[2].buf = &rbuf[2]; + rd_msg[2].slave_addr = sdw_slv->slv_number; + rd_msg[2].addr_page1 = 0x0; + rd_msg[2].addr_page2 = 0x0; + + wr_msg.ssp_tag = 0x0; + wr_msg.flag = SDW_MSG_FLAG_WRITE; + wr_msg.addr = SDW_SCP_INTCLEAR1; + wr_msg.len = 1; + wr_msg.buf = &wbuf[0]; + wr_msg.slave_addr = sdw_slv->slv_number; + wr_msg.addr_page1 = 0x0; + wr_msg.addr_page2 = 0x0; + + ret = sdw_slave_transfer(mstr, rd_msg, 3); + if (ret != 3) { + ret = -EINVAL; + dev_err(&mstr->dev, "Reading of register failed\n"); + goto out; + } + /* First handle parity and bus clash interrupts */ + if (rd_msg[0].buf[0] & SDW_SCP_INTSTAT1_PARITY_MASK) { + dev_err(&mstr->dev, "Parity error detected\n"); + wr_msg.buf[0] |= SDW_SCP_INTCLEAR1_PARITY_MASK; + } + /* Handle bus errors */ + if (rd_msg[0].buf[0] & SDW_SCP_INTSTAT1_BUS_CLASH_MASK) { + dev_err(&mstr->dev, "Bus clash error detected\n"); + wr_msg.buf[0] |= SDW_SCP_INTCLEAR1_BUS_CLASH_MASK; + } + /* Handle Port interrupts from Instat_1 registers */ + cs_ports = 4; + cs_port_start = 0; + cs_port_mask = 0x08; + cs_port_register = 0; + for (i = cs_port_start; i < cs_port_start + cs_ports; i++) { + if (rd_msg[cs_port_register].buf[0] & cs_port_mask) { + ret += sdw_handle_port_interrupt(mstr, + sdw_slv, cs_port_start + i); + } + cs_port_mask = cs_port_mask << 1; + } + /* Handle interrupts from instat_2 register */ + if (!(rd_msg[0].buf[0] & SDW_SCP_INTSTAT1_SCP2_CASCADE_MASK)) + goto handle_instat_3_register; + cs_ports = 7; + cs_port_start = 4; + cs_port_mask = 0x1; + cs_port_register = 1; + for (i = cs_port_start; i < cs_port_start + cs_ports; i++) { + if (rd_msg[cs_port_register].buf[0] & cs_port_mask) { + ret += sdw_handle_port_interrupt(mstr, + sdw_slv, cs_port_start + i); + } + cs_port_mask = cs_port_mask << 1; + } +handle_instat_3_register: + + if (!(rd_msg[1].buf[0] & SDW_SCP_INTSTAT2_SCP3_CASCADE_MASK)) + goto handle_instat_3_register; + cs_ports = 4; + cs_port_start = 11; + cs_port_mask = 0x1; + cs_port_register = 2; + for (i = cs_port_start; i < cs_port_start + cs_ports; i++) { + if (rd_msg[cs_port_register].buf[0] & cs_port_mask) { + ret += sdw_handle_port_interrupt(mstr, + sdw_slv, cs_port_start + i); + } + cs_port_mask = cs_port_mask << 1; + } + /* Ack the IntStat 1 interrupts */ + ret = sdw_slave_transfer(mstr, &wr_msg, 1); + if (ret != 1) { + ret = -EINVAL; + dev_err(&mstr->dev, "Register transfer failed\n"); + goto out; + } +out: + return ret; +} + +static void handle_slave_status(struct kthread_work *work) +{ + int i, ret = 0; + struct sdw_slv_status *status, *__status__; + struct sdw_bus *bus = + container_of(work, struct sdw_bus, kwork); + struct sdw_master *mstr = bus->mstr; + unsigned long flags; + + /* Handle the new attached slaves to the bus. Register new slave + * to the bus. + */ + list_for_each_entry_safe(status, __status__, &bus->status_list, node) { + if (status->status[0] == SDW_SLAVE_STAT_ATTACHED_OK) { + ret += sdw_register_slave(mstr); + if (ret) + /* Even if adding new slave fails, we will + * continue. + */ + dev_err(&mstr->dev, "Registering new slave failed\n"); + } + for (i = 1; i <= SOUNDWIRE_MAX_DEVICES; i++) { + if (status->status[i] == SDW_SLAVE_STAT_NOT_PRESENT && + mstr->sdw_addr[i].assigned == true) + /* Logical address was assigned to slave, but + * now its down, so mark it as not present + */ + mstr->sdw_addr[i].status = + SDW_SLAVE_STAT_NOT_PRESENT; + + else if (status->status[i] == SDW_SLAVE_STAT_ALERT && + mstr->sdw_addr[i].assigned == true) { + /* Handle slave alerts */ + mstr->sdw_addr[i].status = SDW_SLAVE_STAT_ALERT; + ret = sdw_handle_slave_alerts(mstr, + mstr->sdw_addr[i].slave); + if (ret) + dev_err(&mstr->dev, "Handle slave alert failed for Slave %d\n", i); + + + + } else if (status->status[i] == + SDW_SLAVE_STAT_ATTACHED_OK && + mstr->sdw_addr[i].assigned == true) + mstr->sdw_addr[i].status = + SDW_SLAVE_STAT_ATTACHED_OK; + } + spin_lock_irqsave(&bus->spinlock, flags); + list_del(&status->node); + spin_unlock_irqrestore(&bus->spinlock, flags); + kfree(status); + } +} + +static int sdw_register_master(struct sdw_master *mstr) +{ + int ret = 0; + int i; + struct sdw_bus *sdw_bus; + + /* Can't register until after driver model init */ + if (unlikely(WARN_ON(!sdw_bus_type.p))) { + ret = -EAGAIN; + goto bus_init_not_done; + } + /* Sanity checks */ + if (unlikely(mstr->name[0] == '\0')) { + pr_err("sdw-core: Attempt to register an master with no name!\n"); + ret = -EINVAL; + goto mstr_no_name; + } + for (i = 0; i <= SOUNDWIRE_MAX_DEVICES; i++) + mstr->sdw_addr[i].slv_number = i; + + rt_mutex_init(&mstr->bus_lock); + INIT_LIST_HEAD(&mstr->slv_list); + INIT_LIST_HEAD(&mstr->mstr_rt_list); + + sdw_bus = kzalloc(sizeof(struct sdw_bus), GFP_KERNEL); + if (!sdw_bus) + goto bus_alloc_failed; + sdw_bus->mstr = mstr; + + mutex_lock(&sdw_core.core_lock); + list_add_tail(&sdw_bus->bus_node, &sdw_core.bus_list); + mutex_unlock(&sdw_core.core_lock); + + dev_set_name(&mstr->dev, "sdw-%d", mstr->nr); + mstr->dev.bus = &sdw_bus_type; + mstr->dev.type = &sdw_mstr_type; + + ret = device_register(&mstr->dev); + if (ret) + goto out_list; + kthread_init_worker(&sdw_bus->kworker); + sdw_bus->status_thread = kthread_run(kthread_worker_fn, + &sdw_bus->kworker, "%s", + dev_name(&mstr->dev)); + if (IS_ERR(sdw_bus->status_thread)) { + dev_err(&mstr->dev, "error: failed to create status message task\n"); + ret = PTR_ERR(sdw_bus->status_thread); + goto task_failed; + } + kthread_init_work(&sdw_bus->kwork, handle_slave_status); + INIT_LIST_HEAD(&sdw_bus->status_list); + spin_lock_init(&sdw_bus->spinlock); + ret = sdw_mstr_bw_init(sdw_bus); + if (ret) { + dev_err(&mstr->dev, "error: Failed to init mstr bw\n"); + goto mstr_bw_init_failed; + } + dev_dbg(&mstr->dev, "master [%s] registered\n", mstr->name); + + return 0; + +mstr_bw_init_failed: +task_failed: + device_unregister(&mstr->dev); +out_list: + mutex_lock(&sdw_core.core_lock); + list_del(&sdw_bus->bus_node); + mutex_unlock(&sdw_core.core_lock); + kfree(sdw_bus); +bus_alloc_failed: +mstr_no_name: +bus_init_not_done: + mutex_lock(&sdw_core.core_lock); + idr_remove(&sdw_core.idr, mstr->nr); + mutex_unlock(&sdw_core.core_lock); + return ret; +} + +/** + * sdw_master_update_slv_status: Report the status of slave to the bus driver. + * master calls this function based on the + * interrupt it gets once the slave changes its + * state. + * @mstr: Master handle for which status is reported. + * @status: Array of status of each slave. + */ +int sdw_master_update_slv_status(struct sdw_master *mstr, + struct sdw_status *status) +{ + struct sdw_bus *bus = NULL; + struct sdw_slv_status *slv_status; + unsigned long flags; + + list_for_each_entry(bus, &sdw_core.bus_list, bus_node) { + if (bus->mstr == mstr) + break; + } + /* This is master is not registered with bus driver */ + if (!bus) { + dev_info(&mstr->dev, "Master not registered with bus\n"); + return 0; + } + slv_status = kzalloc(sizeof(struct sdw_slv_status), GFP_ATOMIC); + memcpy(slv_status->status, status, sizeof(struct sdw_status)); + + spin_lock_irqsave(&bus->spinlock, flags); + list_add_tail(&slv_status->node, &bus->status_list); + spin_unlock_irqrestore(&bus->spinlock, flags); + + kthread_queue_work(&bus->kworker, &bus->kwork); + return 0; +} +EXPORT_SYMBOL_GPL(sdw_master_update_slv_status); + +/** + * sdw_add_master_controller - declare sdw master, use dynamic bus number + * @master: the master to add + * Context: can sleep + * + * This routine is used to declare an sdw master when its bus number + * doesn't matter or when its bus number is specified by an dt alias. + * Examples of bases when the bus number doesn't matter: sdw masters + * dynamically added by USB links or PCI plugin cards. + * + * When this returns zero, a new bus number was allocated and stored + * in mstr->nr, and the specified master became available for slaves. + * Otherwise, a negative errno value is returned. + */ +int sdw_add_master_controller(struct sdw_master *mstr) +{ + int id; + + mutex_lock(&sdw_core.core_lock); + + id = idr_alloc(&sdw_core.idr, mstr, + sdw_core.first_dynamic_bus_num, 0, GFP_KERNEL); + mutex_unlock(&sdw_core.core_lock); + if (id < 0) + return id; + + mstr->nr = id; + + return sdw_register_master(mstr); +} +EXPORT_SYMBOL_GPL(sdw_add_master_controller); + +static void sdw_unregister_slave(struct sdw_slv *sdw_slv) +{ + + struct sdw_master *mstr; + + mstr = sdw_slv->mstr; + sdw_lock_mstr(mstr); + list_del(&sdw_slv->node); + sdw_unlock_mstr(mstr); + mstr->sdw_addr[sdw_slv->slv_number].assigned = false; + memset(mstr->sdw_addr[sdw_slv->slv_number].dev_id, 0x0, 6); + device_unregister(&sdw_slv->dev); + kfree(sdw_slv); +} + +static int __unregister_slave(struct device *dev, void *dummy) +{ + struct sdw_slv *slave = sdw_slave_verify(dev); + + if (slave && strcmp(slave->name, "dummy")) + sdw_unregister_slave(slave); + return 0; +} + +/** + * sdw_del_master_controller - unregister SDW master + * @mstr: the master being unregistered + * Context: can sleep + * + * This unregisters an SDW master which was previously registered + * by @sdw_add_master_controller or @sdw_add_master_controller. + */ +void sdw_del_master_controller(struct sdw_master *mstr) +{ + struct sdw_master *found; + + /* First make sure that this master was ever added */ + mutex_lock(&sdw_core.core_lock); + found = idr_find(&sdw_core.idr, mstr->nr); + mutex_unlock(&sdw_core.core_lock); + + if (found != mstr) { + pr_debug("sdw-core: attempting to delete unregistered master [%s]\n", mstr->name); + return; + } + /* Detach any active slaves. This can't fail, thus we do not + * check the returned value. + */ + device_for_each_child(&mstr->dev, NULL, __unregister_slave); + + /* device name is gone after device_unregister */ + dev_dbg(&mstr->dev, "mstrter [%s] unregistered\n", mstr->name); + + /* wait until all references to the device are gone + * + * FIXME: This is old code and should ideally be replaced by an + * alternative which results in decoupling the lifetime of the struct + * device from the sdw_master, like spi or netdev do. Any solution + * should be thoroughly tested with DEBUG_KOBJECT_RELEASE enabled! + */ + init_completion(&mstr->slv_released); + device_unregister(&mstr->dev); + wait_for_completion(&mstr->slv_released); + + /* free bus id */ + mutex_lock(&sdw_core.core_lock); + idr_remove(&sdw_core.idr, mstr->nr); + mutex_unlock(&sdw_core.core_lock); + + /* Clear the device structure in case this mstrter is ever going to be + added again */ + memset(&mstr->dev, 0, sizeof(mstr->dev)); +} +EXPORT_SYMBOL_GPL(sdw_del_master_controller); + +/* + * An sdw_driver is used with one or more sdw_slv (slave) nodes to access + * sdw slave chips, on a bus instance associated with some sdw_master. + */ +int __sdw_mstr_driver_register(struct module *owner, + struct sdw_mstr_driver *driver) +{ + int res; + + /* Can't register until after driver model init */ + if (unlikely(WARN_ON(!sdwint_bus_type.p))) + return -EAGAIN; + + /* add the driver to the list of sdw drivers in the driver core */ + driver->driver.owner = owner; + driver->driver.bus = &sdwint_bus_type; + + /* When registration returns, the driver core + * will have called probe() for all matching-but-unbound slaves. + */ + res = driver_register(&driver->driver); + if (res) + return res; + + pr_debug("sdw-core: driver [%s] registered\n", driver->driver.name); + + return 0; +} +EXPORT_SYMBOL_GPL(__sdw_mstr_driver_register); + +void sdw_mstr_driver_unregister(struct sdw_mstr_driver *driver) +{ + driver_unregister(&driver->driver); +} +EXPORT_SYMBOL_GPL(sdw_mstr_driver_unregister); + +void sdw_slave_driver_unregister(struct sdw_slave_driver *driver) +{ + driver_unregister(&driver->driver); +} +EXPORT_SYMBOL_GPL(sdw_slave_driver_unregister); + +/* + * An sdw_driver is used with one or more sdw_slv (slave) nodes to access + * sdw slave chips, on a bus instance associated with some sdw_master. + */ +int __sdw_slave_driver_register(struct module *owner, + struct sdw_slave_driver *driver) +{ + int res; + /* Can't register until after driver model init */ + if (unlikely(WARN_ON(!sdwint_bus_type.p))) + return -EAGAIN; + + /* add the driver to the list of sdw drivers in the driver core */ + driver->driver.owner = owner; + driver->driver.bus = &sdwint_bus_type; + + /* When registration returns, the driver core + * will have called probe() for all matching-but-unbound slaves. + */ + res = driver_register(&driver->driver); + if (res) + return res; + pr_debug("sdw-core: driver [%s] registered\n", driver->driver.name); + + return 0; +} +EXPORT_SYMBOL_GPL(__sdw_slave_driver_register); + +int sdw_register_slave_capabilities(struct sdw_slv *sdw, + struct sdw_slv_capabilities *cap) +{ + struct sdw_slv_capabilities *slv_cap; + struct sdw_slv_dpn_capabilities *slv_dpn_cap, *dpn_cap; + struct port_audio_mode_properties *prop, *slv_prop; + int i, j; + + slv_cap = &sdw->sdw_slv_cap; + + slv_cap->wake_up_unavailable = cap->wake_up_unavailable; + slv_cap->wake_up_unavailable = cap->wake_up_unavailable; + slv_cap->test_mode_supported = cap->test_mode_supported; + slv_cap->clock_stop1_mode_supported = cap->clock_stop1_mode_supported; + slv_cap->simplified_clock_stop_prepare = + cap->simplified_clock_stop_prepare; + slv_cap->highphy_capable = cap->highphy_capable; + slv_cap->paging_supported = cap->paging_supported; + slv_cap->bank_delay_support = cap->bank_delay_support; + slv_cap->port_15_read_behavior = cap->port_15_read_behavior; + slv_cap->sdw_dp0_supported = cap->sdw_dp0_supported; + slv_cap->num_of_sdw_ports = cap->num_of_sdw_ports; + slv_cap->sdw_dpn_cap = devm_kzalloc(&sdw->dev, + ((sizeof(struct sdw_slv_dpn_capabilities)) * + cap->num_of_sdw_ports), GFP_KERNEL); + if (!slv_cap->sdw_dpn_cap) + return -ENOMEM; + + for (i = 0; i < cap->num_of_sdw_ports; i++) { + dpn_cap = &cap->sdw_dpn_cap[i]; + slv_dpn_cap = &slv_cap->sdw_dpn_cap[i]; + slv_dpn_cap->port_direction = dpn_cap->port_direction; + slv_dpn_cap->port_number = dpn_cap->port_number; + slv_dpn_cap->max_word_length = dpn_cap->max_word_length; + slv_dpn_cap->min_word_length = dpn_cap->min_word_length; + slv_dpn_cap->num_word_length = dpn_cap->num_word_length; + if (NULL == dpn_cap->word_length_buffer) + slv_dpn_cap->word_length_buffer = + dpn_cap->word_length_buffer; + else { + slv_dpn_cap->word_length_buffer = + devm_kzalloc(&sdw->dev, + dpn_cap->num_word_length * + (sizeof(unsigned int)), GFP_KERNEL); + if (!slv_dpn_cap->word_length_buffer) + return -ENOMEM; + memcpy(slv_dpn_cap->word_length_buffer, + dpn_cap->word_length_buffer, + dpn_cap->num_word_length * + (sizeof(unsigned int))); + } + slv_dpn_cap->dpn_type = dpn_cap->dpn_type; + slv_dpn_cap->dpn_grouping = dpn_cap->dpn_grouping; + slv_dpn_cap->prepare_ch = dpn_cap->prepare_ch; + slv_dpn_cap->imp_def_intr_mask = dpn_cap->imp_def_intr_mask; + slv_dpn_cap->min_ch_num = dpn_cap->min_ch_num; + slv_dpn_cap->max_ch_num = dpn_cap->max_ch_num; + slv_dpn_cap->num_ch_supported = dpn_cap->num_ch_supported; + if (NULL == slv_dpn_cap->ch_supported) + slv_dpn_cap->ch_supported = dpn_cap->ch_supported; + else { + slv_dpn_cap->ch_supported = + devm_kzalloc(&sdw->dev, + dpn_cap->num_ch_supported * + (sizeof(unsigned int)), GFP_KERNEL); + if (!slv_dpn_cap->ch_supported) + return -ENOMEM; + memcpy(slv_dpn_cap->ch_supported, + dpn_cap->ch_supported, + dpn_cap->num_ch_supported * + (sizeof(unsigned int))); + } + slv_dpn_cap->port_flow_mode_mask = + dpn_cap->port_flow_mode_mask; + slv_dpn_cap->block_packing_mode_mask = + dpn_cap->block_packing_mode_mask; + slv_dpn_cap->port_encoding_type_mask = + dpn_cap->port_encoding_type_mask; + slv_dpn_cap->num_audio_modes = dpn_cap->num_audio_modes; + + slv_dpn_cap->mode_properties = devm_kzalloc(&sdw->dev, + ((sizeof(struct port_audio_mode_properties)) * + dpn_cap->num_audio_modes), GFP_KERNEL); + if (!slv_dpn_cap->mode_properties) + return -ENOMEM; + + for (j = 0; j < dpn_cap->num_audio_modes; j++) { + prop = &dpn_cap->mode_properties[j]; + slv_prop = &slv_dpn_cap->mode_properties[j]; + slv_prop->max_frequency = prop->max_frequency; + slv_prop->min_frequency = prop->min_frequency; + slv_prop->num_freq_configs = prop->num_freq_configs; + if (NULL == slv_prop->freq_supported) + slv_prop->freq_supported = + prop->freq_supported; + else { + slv_prop->freq_supported = + devm_kzalloc(&sdw->dev, + prop->num_freq_configs * + (sizeof(unsigned int)), GFP_KERNEL); + if (!slv_prop->freq_supported) + return -ENOMEM; + memcpy(slv_prop->freq_supported, + prop->freq_supported, + prop->num_freq_configs * + (sizeof(unsigned int))); + } + slv_prop->glitchless_transitions_mask + = prop->glitchless_transitions_mask; + slv_prop->max_sampling_frequency = + prop->max_sampling_frequency; + slv_prop->min_sampling_frequency = + prop->min_sampling_frequency; + slv_prop->num_sampling_freq_configs = + prop->num_sampling_freq_configs; + if (NULL == prop->sampling_freq_config) + slv_prop->sampling_freq_config = + prop->sampling_freq_config; + else { + slv_prop->sampling_freq_config = + devm_kzalloc(&sdw->dev, + prop->num_sampling_freq_configs * + (sizeof(unsigned int)), GFP_KERNEL); + if (!slv_prop->sampling_freq_config) + return -ENOMEM; + memcpy(slv_prop->sampling_freq_config, + prop->sampling_freq_config, + prop->num_sampling_freq_configs * + (sizeof(unsigned int))); + } + + slv_prop->ch_prepare_behavior = + prop->ch_prepare_behavior; + } + } + sdw->slave_cap_updated = true; + return 0; +} +EXPORT_SYMBOL_GPL(sdw_register_slave_capabilities); + +static int sdw_get_stream_tag(char *key, int *stream_tag) +{ + int i; + int ret = -EINVAL; + struct sdw_runtime *sdw_rt; + struct sdw_stream_tag *stream_tags = sdw_core.stream_tags; + + /* If stream tag is already allocated return that after incrementing + * reference count. This is only possible if key is provided. + */ + mutex_lock(&sdw_core.core_lock); + if (!key) + goto key_check_not_required; + for (i = 0; i < SDW_NUM_STREAM_TAGS; i++) { + if (!(strcmp(stream_tags[i].key, key))) { + stream_tags[i].ref_count++; + *stream_tag = stream_tags[i].stream_tag; + mutex_unlock(&sdw_core.core_lock); + return 0; + } + } +key_check_not_required: + for (i = 0; i < SDW_NUM_STREAM_TAGS; i++) { + if (!stream_tags[i].ref_count) { + *stream_tag = stream_tags[i].stream_tag; + mutex_init(&stream_tags[i].stream_lock); + sdw_rt = kzalloc(sizeof(struct sdw_runtime), + GFP_KERNEL); + if (!sdw_rt) { + ret = -ENOMEM; + mutex_unlock(&sdw_core.core_lock); + goto out; + } + stream_tags[i].ref_count++; + INIT_LIST_HEAD(&sdw_rt->slv_rt_list); + INIT_LIST_HEAD(&sdw_rt->mstr_rt_list); + sdw_rt->stream_state = SDW_STATE_INIT_STREAM_TAG; + stream_tags[i].sdw_rt = sdw_rt; + if (key) + strlcpy(stream_tags[i].key, key, + SDW_MAX_STREAM_TAG_KEY_SIZE); + mutex_unlock(&sdw_core.core_lock); + return 0; + } + } + mutex_unlock(&sdw_core.core_lock); +out: + return ret; +} + +void sdw_release_stream_tag(int stream_tag) +{ + int i; + struct sdw_stream_tag *stream_tags = sdw_core.stream_tags; + + mutex_lock(&sdw_core.core_lock); + for (i = 0; i < SDW_NUM_STREAM_TAGS; i++) { + if (stream_tag == stream_tags[i].stream_tag) { + stream_tags[i].ref_count--; + if (stream_tags[i].ref_count == 0) { + kfree(stream_tags[i].sdw_rt); + memset(stream_tags[i].key, 0x0, + SDW_MAX_STREAM_TAG_KEY_SIZE); + } + } + } + mutex_unlock(&sdw_core.core_lock); +} +EXPORT_SYMBOL_GPL(sdw_release_stream_tag); + +/** + * sdw_alloc_stream_tag: Assign the stream tag for the unique streams + * between master and slave device. + * Normally master master will request for the + * stream tag for the stream between master + * and slave device. It programs the same stream + * tag to the slave device. Stream tag is unique + * for all the streams between masters and slave + * across SoCs. + * @guid: Group of the device port. All the ports of the device with + * part of same stream will have same guid. + * + * @stream:tag: Stream tag returned by bus driver. + */ +int sdw_alloc_stream_tag(char *guid, int *stream_tag) +{ + int ret = 0; + + ret = sdw_get_stream_tag(guid, stream_tag); + if (ret) { + pr_err("Stream tag assignment failed\n"); + goto out; + } + +out: + return ret; +} +EXPORT_SYMBOL_GPL(sdw_alloc_stream_tag); + +static struct sdw_mstr_runtime *sdw_get_mstr_rt(struct sdw_runtime *sdw_rt, + struct sdw_master *mstr) { + + struct sdw_mstr_runtime *mstr_rt; + int ret = 0; + + list_for_each_entry(mstr_rt, &sdw_rt->mstr_rt_list, mstr_sdw_node) { + if (mstr_rt->mstr == mstr) + return mstr_rt; + } + + /* Allocate sdw_mstr_runtime structure */ + mstr_rt = kzalloc(sizeof(struct sdw_mstr_runtime), GFP_KERNEL); + if (!mstr_rt) { + ret = -ENOMEM; + goto out; + } + + /* Initialize sdw_mstr_runtime structure */ + INIT_LIST_HEAD(&mstr_rt->port_rt_list); + INIT_LIST_HEAD(&mstr_rt->slv_rt_list); + list_add_tail(&mstr_rt->mstr_sdw_node, &sdw_rt->mstr_rt_list); + list_add_tail(&mstr_rt->mstr_node, &mstr->mstr_rt_list); + mstr_rt->rt_state = SDW_STATE_INIT_RT; + mstr_rt->mstr = mstr; +out: + return mstr_rt; +} + +static struct sdw_slave_runtime *sdw_config_slave_stream( + struct sdw_slv *slave, + struct sdw_stream_config *stream_config, + struct sdw_runtime *sdw_rt) +{ + struct sdw_slave_runtime *slv_rt; + int ret = 0; + struct sdw_stream_params *str_p; + + slv_rt = kzalloc(sizeof(struct sdw_slave_runtime), GFP_KERNEL); + if (!slv_rt) { + ret = -ENOMEM; + goto out; + } + slv_rt->slave = slave; + str_p = &slv_rt->stream_params; + slv_rt->direction = stream_config->direction; + slv_rt->rt_state = SDW_STATE_CONFIG_RT; + str_p->rate = stream_config->frame_rate; + str_p->channel_count = stream_config->channel_count; + str_p->bps = stream_config->bps; + INIT_LIST_HEAD(&slv_rt->port_rt_list); +out: + return slv_rt; +} + +static void sdw_release_mstr_stream(struct sdw_master *mstr, + struct sdw_runtime *sdw_rt) +{ + struct sdw_mstr_runtime *mstr_rt, *__mstr_rt; + + list_for_each_entry_safe(mstr_rt, __mstr_rt, &sdw_rt->mstr_rt_list, + mstr_sdw_node) { + if (mstr_rt->mstr == mstr) { + list_del(&mstr_rt->mstr_sdw_node); + if (mstr_rt->direction == SDW_DATA_DIR_OUT) + sdw_rt->tx_ref_count--; + else + sdw_rt->rx_ref_count--; + list_del(&mstr_rt->mstr_node); + pm_runtime_mark_last_busy(&mstr->dev); + pm_runtime_put_sync_autosuspend(&mstr->dev); + kfree(mstr_rt); + } + } +} + +static void sdw_release_slave_stream(struct sdw_slv *slave, + struct sdw_runtime *sdw_rt) +{ + struct sdw_slave_runtime *slv_rt, *__slv_rt; + + list_for_each_entry_safe(slv_rt, __slv_rt, &sdw_rt->slv_rt_list, + slave_sdw_node) { + if (slv_rt->slave == slave) { + list_del(&slv_rt->slave_sdw_node); + if (slv_rt->direction == SDW_DATA_DIR_OUT) + sdw_rt->tx_ref_count--; + else + sdw_rt->rx_ref_count--; + pm_runtime_mark_last_busy(&slave->dev); + pm_runtime_put_sync_autosuspend(&slave->dev); + kfree(slv_rt); + } + } +} + +/** + * sdw_release_stream: De-allocates the bandwidth allocated to the + * the stream. This is reference counted, + * so for the last stream count, BW will be de-allocated + * for the stream. Normally this will be called + * as part of hw_free. + * + * @mstr: Master handle + * @slave: SoundWire slave handle. + * @stream_config: Stream configuration for the soundwire audio stream. + * @stream_tag: Unique stream tag identifier across SoC for all soundwire + * busses. + * for each audio stream between slaves. This stream tag + * will be allocated by master driver for every + * stream getting open. + */ +int sdw_release_stream(struct sdw_master *mstr, + struct sdw_slv *slave, + unsigned int stream_tag) +{ + int i; + struct sdw_runtime *sdw_rt = NULL; + struct sdw_stream_tag *stream_tags = sdw_core.stream_tags; + + for (i = 0; i < SDW_NUM_STREAM_TAGS; i++) { + if (stream_tags[i].stream_tag == stream_tag) { + sdw_rt = stream_tags[i].sdw_rt; + break; + } + } + if (!sdw_rt) { + dev_err(&mstr->dev, "Invalid stream tag\n"); + return -EINVAL; + } + if (!slave) + sdw_release_mstr_stream(mstr, sdw_rt); + else + sdw_release_slave_stream(slave, sdw_rt); + return 0; +} +EXPORT_SYMBOL_GPL(sdw_release_stream); + +/** + * sdw_configure_stream: Allocates the B/W onto the soundwire bus + * for transferring the data between slave and master. + * This is configuring the single stream of data. + * This will be called by slave, Slave stream + * configuration should match the master stream + * configuration. Normally slave would call this + * as a part of hw_params. + * + * @mstr: Master handle + * @sdw_slv: SoundWire slave handle. + * @stream_config: Stream configuration for the soundwire audio stream. + * @stream_tag: Unique stream tag identifier across the soundwire bus + * for each audio stream between slaves and master. + * This is something like stream_tag in HDA protocol, but + * here its virtual rather than being embedded into protocol. + * Further same stream tag is valid across masters also + * if some ports of the master is participating in + * stream aggregation. This is input parameters to the + * function. + */ +int sdw_config_stream(struct sdw_master *mstr, + struct sdw_slv *slave, + struct sdw_stream_config *stream_config, + unsigned int stream_tag) +{ + int i; + int ret = 0; + struct sdw_runtime *sdw_rt = NULL; + struct sdw_mstr_runtime *mstr_rt = NULL; + struct sdw_slave_runtime *slv_rt = NULL; + struct sdw_stream_tag *stream_tags = sdw_core.stream_tags; + struct sdw_stream_tag *stream = NULL; + + for (i = 0; i < SDW_NUM_STREAM_TAGS; i++) { + if (stream_tags[i].stream_tag == stream_tag) { + sdw_rt = stream_tags[i].sdw_rt; + stream = &stream_tags[i]; + break; + } + } + if (!sdw_rt) { + dev_dbg(&mstr->dev, "Valid stream tag not found\n"); + ret = -EINVAL; + goto out; + } + if (static_key_false(&sdw_trace_msg)) + trace_sdw_config_stream(mstr, slave, stream_config, + stream_tag); + + mutex_lock(&stream->stream_lock); + + mstr_rt = sdw_get_mstr_rt(sdw_rt, mstr); + if (!mstr_rt) { + dev_err(&mstr->dev, "master runtime configuration failed\n"); + ret = -EINVAL; + goto out; + } + + if (!slave) { + mstr_rt->direction = stream_config->direction; + mstr_rt->rt_state = SDW_STATE_CONFIG_RT; + sdw_rt->xport_state = SDW_STATE_ONLY_XPORT_STREAM; + + mstr_rt->stream_params.rate = stream_config->frame_rate; + mstr_rt->stream_params.channel_count = + stream_config->channel_count; + mstr_rt->stream_params.bps = stream_config->bps; + + } else + slv_rt = sdw_config_slave_stream(slave, + stream_config, sdw_rt); + /* Stream params will be stored based on Tx only, since there can + * be only one Tx and muliple Rx, There can be muliple Tx if + * there is aggregation on Tx. That is handled by adding the channels + * to stream_params for each aggregated Tx slaves + */ + if (!sdw_rt->tx_ref_count && stream_config->direction == + SDW_DATA_DIR_OUT) { + sdw_rt->stream_params.rate = stream_config->frame_rate; + sdw_rt->stream_params.channel_count = + stream_config->channel_count; + sdw_rt->stream_params.bps = stream_config->bps; + sdw_rt->tx_ref_count++; + } + + + /* Normally there will be only one Tx in system, multiple Tx + * can only be there if we support aggregation. In that case + * there may be multiple slave or masters handing different + * channels of same Tx stream. + */ + else if (sdw_rt->tx_ref_count && stream_config->direction == + SDW_DATA_DIR_OUT) { + if (sdw_rt->stream_params.rate != + stream_config->frame_rate) { + dev_err(&mstr->dev, "Frame rate for aggregated devices not matching\n"); + ret = -EINVAL; + goto free_mem; + } + if (sdw_rt->stream_params.bps != stream_config->bps) { + dev_err(&mstr->dev, "bps for aggregated devices not matching\n"); + ret = -EINVAL; + goto free_mem; + } + /* Number of channels gets added, since both devices will + * be supporting different channels. Like one Codec + * supporting L and other supporting R channel. + */ + sdw_rt->stream_params.channel_count += + stream_config->channel_count; + sdw_rt->tx_ref_count++; + } else + sdw_rt->rx_ref_count++; + + /* SRK: check with hardik */ + sdw_rt->type = stream_config->type; + sdw_rt->stream_state = SDW_STATE_CONFIG_STREAM; + + /* Slaves are added to two list, This is because BW is calculated + * for two masters individually, while Ports are enabled of all + * the aggregated masters and slaves part of the same stream tag + * simultaneously. + */ + if (slave) { + list_add_tail(&slv_rt->slave_sdw_node, &sdw_rt->slv_rt_list); + list_add_tail(&slv_rt->slave_node, &mstr_rt->slv_rt_list); + } + mutex_unlock(&stream->stream_lock); + if (slave) + pm_runtime_get_sync(&slave->dev); + else + pm_runtime_get_sync(&mstr->dev); + return ret; + +free_mem: + mutex_unlock(&stream->stream_lock); + kfree(mstr_rt); + kfree(slv_rt); +out: + return ret; + +} +EXPORT_SYMBOL_GPL(sdw_config_stream); + +static int sdw_mstr_port_configuration(struct sdw_master *mstr, + struct sdw_runtime *sdw_rt, + struct sdw_port_config *port_config) +{ + struct sdw_mstr_runtime *mstr_rt; + struct sdw_port_runtime *port_rt; + int found = 0; + int i; + + list_for_each_entry(mstr_rt, &sdw_rt->mstr_rt_list, mstr_sdw_node) { + if (mstr_rt->mstr == mstr) { + found = 1; + break; + } + } + if (!found) { + dev_err(&mstr->dev, "Master not found for this port\n"); + return -EINVAL; + } + port_rt = kzalloc((sizeof(struct sdw_port_runtime)) * + port_config->num_ports, GFP_KERNEL); + if (!port_rt) + return -EINVAL; + for (i = 0; i < port_config->num_ports; i++) { + port_rt[i].channel_mask = port_config->port_cfg[i].ch_mask; + port_rt[i].port_num = port_config->port_cfg[i].port_num; + list_add_tail(&port_rt[i].port_node, &mstr_rt->port_rt_list); + } + return 0; +} + +static int sdw_slv_port_configuration(struct sdw_slv *slave, + struct sdw_runtime *sdw_rt, + struct sdw_port_config *port_config) +{ + struct sdw_slave_runtime *slv_rt; + struct sdw_port_runtime *port_rt; + int found = 0; + int i; + + list_for_each_entry(slv_rt, &sdw_rt->slv_rt_list, slave_sdw_node) { + if (slv_rt->slave == slave) { + found = 1; + break; + } + } + if (!found) { + dev_err(&slave->mstr->dev, "Slave not found for this port\n"); + return -EINVAL; + } + port_rt = kzalloc((sizeof(struct sdw_port_runtime)) * + port_config->num_ports, GFP_KERNEL); + if (!port_rt) + return -EINVAL; + + for (i = 0; i < port_config->num_ports; i++) { + port_rt[i].channel_mask = port_config->port_cfg[i].ch_mask; + port_rt[i].port_num = port_config->port_cfg[i].port_num; + list_add_tail(&port_rt[i].port_node, &slv_rt->port_rt_list); + } + return 0; +} + +/** + * sdw_config_port: Port configuration for the SoundWire. Multiple + * soundWire ports may form single stream. Like two + * ports each transferring/receiving mono channels + * forms single stream with stereo channels. + * There will be single ASoC DAI representing + * the both ports. So stream configuration will be + * stereo, but both of the ports will be configured + * for mono channels, each with different channel + * mask. This is used to program port w.r.t to stream. + * params. So no need to de-configure, since these + * are automatically destroyed once stream gets + * destroyed. + * @mstr: Master handle where the slave is connected. + * @slave: Slave handle. + * @port_config: Port configuration for each port of soundwire slave. + * @stream_tag: Stream tag, where this port is connected. + * + */ +int sdw_config_port(struct sdw_master *mstr, + struct sdw_slv *slave, + struct sdw_port_config *port_config, + unsigned int stream_tag) +{ + int ret = 0; + int i; + struct sdw_stream_tag *stream_tags = sdw_core.stream_tags; + struct sdw_runtime *sdw_rt = NULL; + struct sdw_stream_tag *stream = NULL; + + + for (i = 0; i < SDW_NUM_STREAM_TAGS; i++) { + if (stream_tags[i].stream_tag == stream_tag) { + sdw_rt = stream_tags[i].sdw_rt; + stream = &stream_tags[i]; + break; + } + } + if (!sdw_rt) { + dev_err(&mstr->dev, "Invalid stream tag\n"); + return -EINVAL; + } + if (static_key_false(&sdw_trace_msg)) { + int i; + + for (i = 0; i < port_config->num_ports; i++) { + trace_sdw_config_port(mstr, slave, + &port_config->port_cfg[i], stream_tag); + } + } + mutex_lock(&stream->stream_lock); + if (!slave) + ret = sdw_mstr_port_configuration(mstr, sdw_rt, port_config); + else + ret = sdw_slv_port_configuration(slave, sdw_rt, port_config); + + mutex_unlock(&stream->stream_lock); + return ret; +} +EXPORT_SYMBOL_GPL(sdw_config_port); + +int sdw_prepare_and_enable(int stream_tag, bool enable) +{ + + int i, ret = 0; + struct sdw_stream_tag *stream_tags = sdw_core.stream_tags; + struct sdw_stream_tag *stream = NULL; + + /* TBD: SRK, Check with hardik whether both locks needed + * stream and core?? + */ + mutex_lock(&sdw_core.core_lock); + + for (i = 0; i < SDW_NUM_STREAM_TAGS; i++) { + if (stream_tag == stream_tags[i].stream_tag) { + stream = &stream_tags[i]; + break; + } + } + if (stream == NULL) { + mutex_unlock(&sdw_core.core_lock); + WARN_ON(1); /* Return from here after unlocking core*/ + return -EINVAL; + } + mutex_lock(&stream->stream_lock); + ret = sdw_bus_calc_bw(&stream_tags[i], enable); + if (ret) + pr_err("Bandwidth allocation failed\n"); + + mutex_unlock(&stream->stream_lock); + mutex_unlock(&sdw_core.core_lock); + return ret; +} +EXPORT_SYMBOL_GPL(sdw_prepare_and_enable); + +int sdw_disable_and_unprepare(int stream_tag, bool unprepare) +{ + int i, ret = 0; + struct sdw_stream_tag *stream_tags = sdw_core.stream_tags; + struct sdw_stream_tag *stream = NULL; + + mutex_lock(&sdw_core.core_lock); + + for (i = 0; i < SDW_NUM_STREAM_TAGS; i++) { + if (stream_tag == stream_tags[i].stream_tag) { + stream = &stream_tags[i]; + break; + } + } + if (stream == NULL) { + mutex_unlock(&sdw_core.core_lock); + WARN_ON(1); /* Return from here after unlocking core*/ + return -EINVAL; + } + mutex_lock(&stream->stream_lock); + ret = sdw_bus_calc_bw_dis(&stream_tags[i], unprepare); + if (ret) + pr_err("Bandwidth de-allocation failed\n"); + + mutex_unlock(&stream->stream_lock); + + mutex_unlock(&sdw_core.core_lock); + return ret; +} +EXPORT_SYMBOL_GPL(sdw_disable_and_unprepare); + +int sdw_stop_clock(struct sdw_master *mstr, enum sdw_clk_stop_mode mode) +{ + int ret = 0, i; + struct sdw_msg msg; + u8 buf[1] = {0}; + int slave_present = 0; + + for (i = 1; i <= SOUNDWIRE_MAX_DEVICES; i++) { + if (mstr->sdw_addr[i].assigned && + mstr->sdw_addr[i].status != + SDW_SLAVE_STAT_NOT_PRESENT) + slave_present = 1; + } + + /* Send Broadcast message to the SCP_ctrl register with + * clock stop now + */ + msg.ssp_tag = 1; + msg.flag = SDW_MSG_FLAG_WRITE; + msg.addr = SDW_SCP_CTRL; + msg.len = 1; + buf[0] |= 0x1 << SDW_SCP_CTRL_CLK_STP_NOW_SHIFT; + msg.buf = buf; + msg.slave_addr = 15; + msg.addr_page1 = 0x0; + msg.addr_page2 = 0x0; + ret = sdw_slave_transfer_nopm(mstr, &msg, 1); + if (ret != 1 && slave_present) { + dev_err(&mstr->dev, "Failed to stop clk\n"); + return -EBUSY; + } + /* If we are entering clock stop mode1, mark all the slaves un-attached. + */ + if (mode == SDW_CLOCK_STOP_MODE_1) { + for (i = 1; i <= SOUNDWIRE_MAX_DEVICES; i++) { + if (mstr->sdw_addr[i].assigned) + mstr->sdw_addr[i].status = + SDW_SLAVE_STAT_NOT_PRESENT; + } + } + return 0; +} +EXPORT_SYMBOL_GPL(sdw_stop_clock); + +int sdw_wait_for_slave_enumeration(struct sdw_master *mstr, + struct sdw_slv *slave) +{ + int timeout = 0; + + /* Wait till device gets enumerated. Wait for 2Secs before + * giving up + */ + do { + msleep(100); + timeout++; + } while ((slave->slv_addr->status == SDW_SLAVE_STAT_NOT_PRESENT) && + timeout < 20); + + if (slave->slv_addr->status == SDW_SLAVE_STAT_NOT_PRESENT) + return -EBUSY; + return 0; +} +EXPORT_SYMBOL_GPL(sdw_wait_for_slave_enumeration); + +int sdw_prepare_for_clock_change(struct sdw_master *mstr, bool stop, + enum sdw_clk_stop_mode *clck_stop_mode) +{ + int i; + struct sdw_msg msg; + u8 buf[1] = {0}; + struct sdw_slave *slave; + enum sdw_clk_stop_mode clock_stop_mode; + int timeout = 0; + int ret = 0; + int slave_dev_present = 0; + + /* Find if all slave support clock stop mode1 if all slaves support + * clock stop mode1 use mode1 else use mode0 + */ + for (i = 1; i <= SOUNDWIRE_MAX_DEVICES; i++) { + if (mstr->sdw_addr[i].assigned && + mstr->sdw_addr[i].status != SDW_SLAVE_STAT_NOT_PRESENT) { + slave_dev_present = 1; + slave = mstr->sdw_addr[i].slave; + clock_stop_mode &= + slave->sdw_slv_cap.clock_stop1_mode_supported; + if (!clock_stop_mode) + break; + } + } + if (stop) { + *clck_stop_mode = clock_stop_mode; + dev_info(&mstr->dev, "Entering Clock stop mode %x\n", + clock_stop_mode); + } + /* Slaves might have removed power during its suspend + * in that case no need to do clock stop prepare + * and return from here + */ + if (!slave_dev_present) + return 0; + /* Prepare for the clock stop mode. For simplified clock stop + * prepare only mode is to be set, For others set the ClockStop + * Prepare bit in SCP_SystemCtrl register. For all the other slaves + * set the clock stop prepare bit. For all slave set the clock + * stop mode based on what we got in earlier loop + */ + for (i = 1; i <= SOUNDWIRE_MAX_DEVICES; i++) { + if (mstr->sdw_addr[i].assigned != true) + continue; + if (mstr->sdw_addr[i].status == SDW_SLAVE_STAT_NOT_PRESENT) + continue; + slave = mstr->sdw_addr[i].slave; + msg.ssp_tag = 0; + slave = mstr->sdw_addr[i].slave; + + if (stop) { + /* Even if its simplified clock stop prepare + * setting prepare bit wont harm + */ + buf[0] |= (1 << SDW_SCP_SYSTEMCTRL_CLK_STP_PREP_SHIFT); + buf[0] |= clock_stop_mode << + SDW_SCP_SYSTEMCTRL_CLK_STP_MODE_SHIFT; + } + msg.flag = SDW_MSG_FLAG_WRITE; + msg.addr = SDW_SCP_SYSTEMCTRL; + msg.len = 1; + msg.buf = buf; + msg.slave_addr = i; + msg.addr_page1 = 0x0; + msg.addr_page2 = 0x0; + ret = sdw_slave_transfer_nopm(mstr, &msg, 1); + if (ret != 1) { + dev_err(&mstr->dev, "Clock Stop prepare failed\n"); + return -EBUSY; + } + } + /* + * Once clock stop prepare bit is set, broadcast the message to read + * ClockStop_NotFinished bit from SCP_Stat, till we read it as 11 + * we dont exit loop. We wait for definite time before retrying + * if its simple clock stop it will be always 1, while for other + * they will driver 0 on bus so we wont get 1. In total we are + * waiting 1 sec before we timeout. + */ + do { + buf[0] = 0xFF; + msg.ssp_tag = 0; + msg.flag = SDW_MSG_FLAG_READ; + msg.addr = SDW_SCP_STAT; + msg.len = 1; + msg.buf = buf; + msg.slave_addr = 15; + msg.addr_page1 = 0x0; + msg.addr_page2 = 0x0; + ret = sdw_slave_transfer_nopm(mstr, &msg, 1); + if (ret != 1) + goto prepare_failed; + + if (!(buf[0] & SDW_SCP_STAT_CLK_STP_NF_MASK)) + break; + msleep(100); + timeout++; + } while (timeout != 11); + /* If we are trying to stop and prepare failed its not ok + */ + if (!(buf[0] & SDW_SCP_STAT_CLK_STP_NF_MASK)) { + dev_info(&mstr->dev, "Clock stop prepare done\n"); + return 0; + /* If we are trying to resume and un-prepare failes its ok + * since codec might be down during suspned and will + * start afresh after resuming + */ + } else if (!stop) { + dev_info(&mstr->dev, "Some Slaves un-prepare un-successful\n"); + return 0; + } + +prepare_failed: + dev_err(&mstr->dev, "Clock Stop prepare failed\n"); + return -EBUSY; + +} +EXPORT_SYMBOL_GPL(sdw_prepare_for_clock_change); + +struct sdw_master *sdw_get_master(int nr) +{ + struct sdw_master *master; + + mutex_lock(&sdw_core.core_lock); + master = idr_find(&sdw_core.idr, nr); + if (master && !try_module_get(master->owner)) + master = NULL; + mutex_unlock(&sdw_core.core_lock); + + return master; +} +EXPORT_SYMBOL_GPL(sdw_get_master); + +void sdw_put_master(struct sdw_master *mstr) +{ + if (mstr) + module_put(mstr->owner); +} +EXPORT_SYMBOL_GPL(sdw_put_master); + +static void sdw_exit(void) +{ + device_unregister(&sdw_slv); + bus_unregister(&sdwint_bus_type); +} + +static int sdw_init(void) +{ + int retval; + int i; + + for (i = 0; i < SDW_NUM_STREAM_TAGS; i++) + sdw_core.stream_tags[i].stream_tag = i; + mutex_init(&sdw_core.core_lock); + INIT_LIST_HEAD(&sdw_core.bus_list); + idr_init(&sdw_core.idr); + retval = bus_register(&sdwint_bus_type); + + if (!retval) + retval = device_register(&sdw_slv); + + + if (retval) + bus_unregister(&sdwint_bus_type); + + retval = sdw_bus_bw_init(); + if (retval) { + device_unregister(&sdw_slv); + bus_unregister(&sdwint_bus_type); + } + + return retval; +} +postcore_initcall(sdw_init); +module_exit(sdw_exit); + +MODULE_AUTHOR("Hardik Shah "); +MODULE_LICENSE("GPL v2"); +MODULE_VERSION("0.1"); +MODULE_DESCRIPTION("SoundWire bus driver"); +MODULE_ALIAS("platform:soundwire"); diff --git a/drivers/sdw/sdw_bwcalc.c b/drivers/sdw/sdw_bwcalc.c new file mode 100644 index 000000000000..9c1ebc3297d2 --- /dev/null +++ b/drivers/sdw/sdw_bwcalc.c @@ -0,0 +1,2366 @@ +/* + * sdw_bwcalc.c - SoundWire Bus BW calculation & CHN Enabling implementation + * + * Copyright (C) 2015-2016 Intel Corp + * Author: Sanyog Kale + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * 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; version 2 of the License. + * + * 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. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ + +#include +#include +#include "sdw_priv.h" +#include +#include + + +#define MAXCLOCKFREQ 6 + +#define MAXCLOCKFREQ 6 + +/* TBD: Currently we are using 100x2 as frame shape. to be removed later */ +int rows[MAX_NUM_ROWS] = {100, 48, 50, 60, 64, 72, 75, 80, 90, + 96, 125, 144, 147, 120, 128, 150, + 160, 180, 192, 200, 240, 250, 256}; + +int cols[MAX_NUM_COLS] = {2, 4, 6, 8, 10, 12, 14, 16}; + +/* + * TBD: Get supported clock frequency from ACPI and store + * it in master data structure. + */ +/* Currently only 9.6MHz clock frequency used */ +int clock_freq[MAXCLOCKFREQ] = {9600000, 9600000, + 9600000, 9600000, + 9600000, 9600000}; + + +struct sdw_num_to_col sdw_num_col_mapping[MAX_NUM_COLS] = { + {0, 2}, {1, 4}, {2, 6}, {3, 8}, {4, 10}, {5, 12}, {6, 14}, {7, 16}, +}; + +struct sdw_num_to_row sdw_num_row_mapping[MAX_NUM_ROWS] = { + {0, 48}, {1, 50}, {2, 60}, {3, 64}, {4, 75}, {5, 80}, {6, 125}, + {7, 147}, {8, 96}, {9, 100}, {10, 120}, {11, 128}, {12, 150}, + {13, 160}, {14, 250}, {16, 192}, {17, 200}, {18, 240}, {19, 256}, + {20, 72}, {21, 144}, {22, 90}, {23, 180}, +}; + +/** + * sdw_bus_bw_init - returns Success + * + * + * This function is called from sdw_init function when bus driver + * gets intitalized. This function performs all the generic + * intializations required for BW control. + */ +int sdw_bus_bw_init(void) +{ + int r, c, rowcolcount = 0; + int control_bits = 48; + + for (c = 0; c < MAX_NUM_COLS; c++) { + + for (r = 0; r < MAX_NUM_ROWS; r++) { + sdw_core.rowcolcomb[rowcolcount].col = cols[c]; + sdw_core.rowcolcomb[rowcolcount].row = rows[r]; + sdw_core.rowcolcomb[rowcolcount].control_bits = + control_bits; + sdw_core.rowcolcomb[rowcolcount].data_bits = + (cols[c] * rows[r]) - control_bits; + rowcolcount++; + } + } + + return 0; +} +EXPORT_SYMBOL_GPL(sdw_bus_bw_init); + + +/** + * sdw_mstr_bw_init - returns Success + * + * + * This function is called from sdw_register_master function + * for each master controller gets register. This function performs + * all the intializations per master controller required for BW control. + */ +int sdw_mstr_bw_init(struct sdw_bus *sdw_bs) +{ + struct sdw_master_capabilities *sdw_mstr_cap = NULL; + + /* Initialize required parameters in bus structure */ + sdw_bs->bandwidth = 0; + sdw_bs->system_interval = 0; + sdw_bs->frame_freq = 0; + /* TBD: Base Clock frequency should be read from + * master capabilities + * Currenly hardcoding to 9.6MHz + */ + sdw_bs->clk_freq = 9.6*1000*1000; + sdw_bs->clk_state = SDW_CLK_STATE_ON; + + /* TBD: to be removed later */ + /* Assumption is these should be already filled */ + sdw_mstr_cap = &sdw_bs->mstr->mstr_capabilities; + sdw_mstr_cap->base_clk_freq = 9.6 * 1000 * 1000; + sdw_mstr_cap->monitor_handover_supported = false; + sdw_mstr_cap->highphy_capable = false; + + return 0; +} +EXPORT_SYMBOL_GPL(sdw_mstr_bw_init); + + +/** + * sdw_get_col_to_num + * + * Returns column number from the mapping. + */ +int sdw_get_col_to_num(int col) +{ + int i; + + for (i = 0; i < MAX_NUM_COLS; i++) { + if (sdw_num_col_mapping[i].col == col) + return sdw_num_col_mapping[i].num; + } + + return 0; /* Lowest Column number = 2 */ +} + + +/** + * sdw_get_row_to_num + * + * Returns row number from the mapping. + */ +int sdw_get_row_to_num(int row) +{ + int i; + + for (i = 0; i < MAX_NUM_ROWS; i++) { + if (sdw_num_row_mapping[i].row == row) + return sdw_num_row_mapping[i].num; + } + + return 0; /* Lowest Row number = 48 */ +} + +/* + * sdw_lcm - returns LCM of two numbers + * + * + * This function is called BW calculation function to find LCM + * of two numbers. + */ +int sdw_lcm(int num1, int num2) +{ + int max; + + /* maximum value is stored in variable max */ + max = (num1 > num2) ? num1 : num2; + + while (1) { + if (max%num1 == 0 && max%num2 == 0) + break; + ++max; + } + + return max; +} + + +/* + * sdw_cfg_slv_params - returns Success + * -EINVAL - In case of error. + * + * + * This function configures slave registers for + * transport and port parameters. + */ +int sdw_cfg_slv_params(struct sdw_bus *mstr_bs, + struct sdw_slave_runtime *slv_rt, + struct sdw_transport_params *t_slv_params, + struct sdw_port_params *p_slv_params) +{ + struct sdw_msg wr_msg, wr_msg1, rd_msg; + int ret = 0; + int banktouse; + u8 wbuf[8] = {0, 0, 0, 0, 0, 0, 0, 0}; + u8 wbuf1[2] = {0, 0}; + u8 rbuf[1] = {0}; + u8 rbuf1[8] = {0, 0, 0, 0, 0, 0, 0, 0}; + u8 rbuf2[2] = {0, 0}; + + /* Program slave alternate bank with all transport parameters */ + /* DPN_BlockCtrl2 */ + wbuf[0] = t_slv_params->blockgroupcontrol; + /* DPN_SampleCtrl1 */ + wbuf[1] = (t_slv_params->sample_interval - 1) & + SDW_DPN_SAMPLECTRL1_LOW_MASK; + wbuf[2] = ((t_slv_params->sample_interval - 1) >> 8) & + SDW_DPN_SAMPLECTRL1_LOW_MASK; /* DPN_SampleCtrl2 */ + wbuf[3] = t_slv_params->offset1; /* DPN_OffsetCtrl1 */ + wbuf[4] = t_slv_params->offset2; /* DPN_OffsetCtrl1 */ + /* DPN_HCtrl */ + wbuf[5] = (t_slv_params->hstop | (t_slv_params->hstart << 4)); + wbuf[6] = t_slv_params->blockpackingmode; /* DPN_BlockCtrl3 */ + wbuf[7] = t_slv_params->lanecontrol; /* DPN_LaneCtrl */ + + /* Get current bank in use from bus structure*/ + banktouse = mstr_bs->active_bank; + banktouse = !banktouse; + /* Program slave alternate bank with all port parameters */ + rd_msg.addr = SDW_DPN_PORTCTRL + + (SDW_NUM_DATA_PORT_REGISTERS * t_slv_params->num); + rd_msg.ssp_tag = 0x0; + rd_msg.flag = SDW_MSG_FLAG_READ; + rd_msg.len = 1; + rd_msg.slave_addr = slv_rt->slave->slv_number; + rd_msg.buf = rbuf; + rd_msg.addr_page1 = 0x0; + rd_msg.addr_page2 = 0x0; + + ret = sdw_slave_transfer(mstr_bs->mstr, &rd_msg, 1); + if (ret != 1) { + ret = -EINVAL; + dev_err(&mstr_bs->mstr->dev, "Register transfer failed\n"); + goto out; + } + + + wbuf1[0] = (p_slv_params->port_flow_mode | + (p_slv_params->port_data_mode << + SDW_DPN_PORTCTRL_PORTDATAMODE_SHIFT) | + (rbuf[0])); + + wbuf1[1] = (p_slv_params->word_length - 1); + + /* Check whether address computed is correct for both cases */ + wr_msg.addr = ((SDW_DPN_BLOCKCTRL2 + + (1 * (!t_slv_params->blockgroupcontrol_valid)) + + (SDW_BANK1_REGISTER_OFFSET * banktouse)) + + (SDW_NUM_DATA_PORT_REGISTERS * t_slv_params->num)); + + wr_msg1.addr = SDW_DPN_PORTCTRL + + (SDW_NUM_DATA_PORT_REGISTERS * t_slv_params->num); + + wr_msg.ssp_tag = 0x0; + wr_msg.flag = SDW_MSG_FLAG_WRITE; + wr_msg.len = (7 + (1 * (t_slv_params->blockgroupcontrol_valid))); + wr_msg.slave_addr = slv_rt->slave->slv_number; + wr_msg.buf = &wbuf[0 + (1 * (!t_slv_params->blockgroupcontrol_valid))]; + wr_msg.addr_page1 = 0x0; + wr_msg.addr_page2 = 0x0; + + wr_msg1.ssp_tag = 0x0; + wr_msg1.flag = SDW_MSG_FLAG_WRITE; + wr_msg1.len = 2; + wr_msg1.slave_addr = slv_rt->slave->slv_number; + wr_msg1.buf = &wbuf1[0]; + wr_msg1.addr_page1 = 0x0; + wr_msg1.addr_page2 = 0x0; + + ret = sdw_slave_transfer(mstr_bs->mstr, &wr_msg, 1); + if (ret != 1) { + ret = -EINVAL; + dev_err(&mstr_bs->mstr->dev, "Register transfer failed\n"); + goto out; + } + + + ret = sdw_slave_transfer(mstr_bs->mstr, &wr_msg1, 1); + if (ret != 1) { + ret = -EINVAL; + dev_err(&mstr_bs->mstr->dev, "Register transfer failed\n"); + goto out; + } + +out: + return ret; +} + + +/* + * sdw_cfg_mstr_params - returns Success + * -EINVAL - In case of error. + * + * + * This function configures master registers for + * transport and port parameters. + */ +int sdw_cfg_mstr_params(struct sdw_bus *mstr_bs, + struct sdw_transport_params *t_mstr_params, + struct sdw_port_params *p_mstr_params) +{ + struct sdw_mstr_driver *ops = mstr_bs->mstr->driver; + int banktouse, ret = 0; + + /* 1. Get current bank in use from bus structure*/ + banktouse = mstr_bs->active_bank; + banktouse = !banktouse; + + /* 2. Set Master Xport Params */ + if (ops->mstr_port_ops->dpn_set_port_transport_params) { + ret = ops->mstr_port_ops->dpn_set_port_transport_params + (mstr_bs->mstr, t_mstr_params, banktouse); + if (ret < 0) + return ret; + } + + /* 3. Set Master Port Params */ + if (ops->mstr_port_ops->dpn_set_port_params) { + ret = ops->mstr_port_ops->dpn_set_port_params + (mstr_bs->mstr, p_mstr_params, banktouse); + if (ret < 0) + return ret; + } + + return 0; +} + + +/* + * sdw_cfg_mstr_slv - returns Success + * -EINVAL - In case of error. + * + * + * This function call master/slave transport/port + * params configuration API's, called from sdw_bus_calc_bw + * & sdw_bus_calc_bw_dis API's. + */ +int sdw_cfg_mstr_slv(struct sdw_bus *sdw_mstr_bs, + struct sdw_mstr_runtime *sdw_mstr_bs_rt, + bool is_master) +{ + struct sdw_transport_params *t_params, *t_slv_params; + struct sdw_port_params *p_params, *p_slv_params; + struct sdw_slave_runtime *slv_rt = NULL; + struct sdw_port_runtime *port_rt, *port_slv_rt; + int ret = 0; + + if (is_master) { + /* should not compute any transport params */ + if (sdw_mstr_bs_rt->rt_state == SDW_STATE_UNPREPARE_RT) + return 0; + + list_for_each_entry(port_rt, + &sdw_mstr_bs_rt->port_rt_list, port_node) { + + /* Transport and port parameters */ + t_params = &port_rt->transport_params; + p_params = &port_rt->port_params; + + p_params->num = port_rt->port_num; + p_params->word_length = + sdw_mstr_bs_rt->stream_params.bps; + p_params->port_flow_mode = 0x0; /* Isochronous Mode */ + p_params->port_data_mode = 0x0; /* Normal Mode */ + + /* Configure xport params and port params for master */ + ret = sdw_cfg_mstr_params(sdw_mstr_bs, + t_params, p_params); + if (ret < 0) + return ret; + + /* Since one port per master runtime, + * breaking port_list loop + * TBD: to be extended for multiple port support + */ + + break; + } + + } else { + + + list_for_each_entry(slv_rt, + &sdw_mstr_bs_rt->slv_rt_list, slave_node) { + + if (slv_rt->slave == NULL) + break; + + /* should not compute any transport params */ + if (slv_rt->rt_state == SDW_STATE_UNPREPARE_RT) + continue; + + list_for_each_entry(port_slv_rt, + &slv_rt->port_rt_list, port_node) { + + /* Fill in port params here */ + port_slv_rt->port_params.num = + port_slv_rt->port_num; + port_slv_rt->port_params.word_length = + slv_rt->stream_params.bps; + /* Isochronous Mode */ + port_slv_rt->port_params.port_flow_mode = 0x0; + /* Normal Mode */ + port_slv_rt->port_params.port_data_mode = 0x0; + t_slv_params = &port_slv_rt->transport_params; + p_slv_params = &port_slv_rt->port_params; + + /* Configure xport & port params for slave */ + ret = sdw_cfg_slv_params(sdw_mstr_bs, + slv_rt, t_slv_params, p_slv_params); + if (ret < 0) + return ret; + + /* Since one port per slave runtime, + * breaking port_list loop + * TBD: to be extended for multiple + * port support + */ + + break; + } + } + + } + + return 0; +} + + +/* + * sdw_cpy_params_mstr_slv - returns Success + * -EINVAL - In case of error. + * + * + * This function copies/configure master/slave transport & + * port params to alternate bank. + * + */ +int sdw_cpy_params_mstr_slv(struct sdw_bus *sdw_mstr_bs, + struct sdw_mstr_runtime *sdw_mstr_bs_rt) +{ + struct sdw_slave_runtime *slv_rt = NULL; + struct sdw_port_runtime *port_rt, *port_slv_rt; + struct sdw_transport_params *t_params, *t_slv_params; + struct sdw_port_params *p_params, *p_slv_params; + int ret = 0; + + list_for_each_entry(slv_rt, + &sdw_mstr_bs_rt->slv_rt_list, slave_node) { + + if (slv_rt->slave == NULL) + break; + + list_for_each_entry(port_slv_rt, + &slv_rt->port_rt_list, port_node) { + + /* Fill in port params here */ + port_slv_rt->port_params.num = port_slv_rt->port_num; + port_slv_rt->port_params.word_length = + slv_rt->stream_params.bps; + /* Normal/Isochronous Mode */ + port_slv_rt->port_params.port_flow_mode = 0x0; + /* Normal Mode */ + port_slv_rt->port_params.port_data_mode = 0x0; + t_slv_params = &port_slv_rt->transport_params; + p_slv_params = &port_slv_rt->port_params; + + /* Configure xport & port params for slave */ + ret = sdw_cfg_slv_params(sdw_mstr_bs, + slv_rt, t_slv_params, p_slv_params); + if (ret < 0) + return ret; + + /* + * Since one port per slave runtime, + * breaking port_list loop + * TBD: to be extended for multiple port support + */ + break; + } + } + + + list_for_each_entry(port_rt, + &sdw_mstr_bs_rt->port_rt_list, port_node) { + + /* Transport and port parameters */ + t_params = &port_rt->transport_params; + p_params = &port_rt->port_params; + + + p_params->num = port_rt->port_num; + p_params->word_length = sdw_mstr_bs_rt->stream_params.bps; + p_params->port_flow_mode = 0x0; /* Normal/Isochronous Mode */ + p_params->port_data_mode = 0x0; /* Normal Mode */ + + /* Configure xport params and port params for master */ + ret = sdw_cfg_mstr_params(sdw_mstr_bs, t_params, p_params); + if (ret < 0) + return ret; + + /* Since one port per slave runtime, breaking port_list loop + * TBD: to be extended for multiple port support + */ + break; + } + + return 0; +} + + +/* + * sdw_cfg_slv_enable_disable - returns Success + * -EINVAL - In case of error. + * + * + * This function enable/disable slave port channels. + */ +int sdw_cfg_slv_enable_disable(struct sdw_bus *mstr_bs, + struct sdw_slave_runtime *slv_rt_strm, + struct sdw_port_runtime *port_slv_strm, + struct port_chn_en_state *chn_en) +{ + struct sdw_msg wr_msg, rd_msg; + int ret = 0; + int banktouse; + u8 wbuf[1] = {0}; + u8 rbuf[1] = {0}; + + /* Get current bank in use from bus structure*/ + banktouse = mstr_bs->active_bank; + if ((chn_en->is_activate) || (chn_en->is_bank_sw)) + banktouse = !banktouse; + + rd_msg.addr = wr_msg.addr = ((SDW_DPN_CHANNELEN + + (SDW_BANK1_REGISTER_OFFSET * banktouse)) + + (SDW_NUM_DATA_PORT_REGISTERS * + port_slv_strm->port_num)); + + rd_msg.ssp_tag = 0x0; + rd_msg.flag = SDW_MSG_FLAG_READ; + rd_msg.len = 1; + rd_msg.slave_addr = slv_rt_strm->slave->slv_number; + rd_msg.buf = rbuf; + rd_msg.addr_page1 = 0x0; + rd_msg.addr_page2 = 0x0; + + wr_msg.ssp_tag = 0x0; + wr_msg.flag = SDW_MSG_FLAG_WRITE; + wr_msg.len = 1; + wr_msg.slave_addr = slv_rt_strm->slave->slv_number; + wr_msg.buf = wbuf; + wr_msg.addr_page1 = 0x0; + wr_msg.addr_page2 = 0x0; + + + if (chn_en->is_activate) { + + /* + * 1. slave port enable_ch_pre + * --> callback + * --> no callback available + */ + + /* 2. slave port enable */ + ret = sdw_slave_transfer(mstr_bs->mstr, &rd_msg, 1); + if (ret != 1) { + ret = -EINVAL; + dev_err(&mstr_bs->mstr->dev, + "Register transfer failed\n"); + goto out; + } + + wbuf[0] = (rbuf[0] | port_slv_strm->channel_mask); + + ret = sdw_slave_transfer(mstr_bs->mstr, &wr_msg, 1); + if (ret != 1) { + ret = -EINVAL; + dev_err(&mstr_bs->mstr->dev, + "Register transfer failed\n"); + goto out; + } + + rbuf[0] = 0; + ret = sdw_slave_transfer(mstr_bs->mstr, &rd_msg, 1); + if (ret != 1) { + ret = -EINVAL; + dev_err(&mstr_bs->mstr->dev, + "Register transfer failed\n"); + goto out; + } + + /* + * 3. slave port enable post pre + * --> callback + * --> no callback available + */ + slv_rt_strm->rt_state = SDW_STATE_ENABLE_RT; + + } else { + + /* + * 1. slave port enable_ch_unpre + * --> callback + * --> no callback available + */ + + /* 2. slave port disable */ + ret = sdw_slave_transfer(mstr_bs->mstr, &rd_msg, 1); + if (ret != 1) { + ret = -EINVAL; + dev_err(&mstr_bs->mstr->dev, + "Register transfer failed\n"); + goto out; + } + + wbuf[0] = (rbuf[0] & ~(port_slv_strm->channel_mask)); + + ret = sdw_slave_transfer(mstr_bs->mstr, &wr_msg, 1); + if (ret != 1) { + ret = -EINVAL; + dev_err(&mstr_bs->mstr->dev, + "Register transfer failed\n"); + goto out; + } + + rbuf[0] = 0; + ret = sdw_slave_transfer(mstr_bs->mstr, &rd_msg, 1); + if (ret != 1) { + ret = -EINVAL; + dev_err(&mstr_bs->mstr->dev, + "Register transfer failed\n"); + goto out; + } + + /* + * 3. slave port enable post unpre + * --> callback + * --> no callback available + */ + if (!chn_en->is_bank_sw) + slv_rt_strm->rt_state = SDW_STATE_DISABLE_RT; + + } + +out: + return ret; + +} + + +/* + * sdw_cfg_mstr_activate_disable - returns Success + * -EINVAL - In case of error. + * + * + * This function enable/disable master port channels. + */ +int sdw_cfg_mstr_activate_disable(struct sdw_bus *mstr_bs, + struct sdw_mstr_runtime *mstr_rt_strm, + struct sdw_port_runtime *port_mstr_strm, + struct port_chn_en_state *chn_en) +{ + struct sdw_mstr_driver *ops = mstr_bs->mstr->driver; + struct sdw_activate_ch activate_ch; + int banktouse, ret = 0; + + activate_ch.num = port_mstr_strm->port_num; + activate_ch.ch_mask = port_mstr_strm->channel_mask; + activate_ch.activate = chn_en->is_activate; /* Enable/Disable */ + + /* Get current bank in use from bus structure*/ + banktouse = mstr_bs->active_bank; + if ((chn_en->is_activate) || (chn_en->is_bank_sw)) + banktouse = !banktouse; + + + /* 1. Master port enable_ch_pre */ + if (ops->mstr_port_ops->dpn_port_activate_ch_pre) { + ret = ops->mstr_port_ops->dpn_port_activate_ch_pre + (mstr_bs->mstr, &activate_ch, banktouse); + if (ret < 0) + return ret; + } + + /* 2. Master port enable */ + if (ops->mstr_port_ops->dpn_port_activate_ch) { + ret = ops->mstr_port_ops->dpn_port_activate_ch(mstr_bs->mstr, + &activate_ch, banktouse); + if (ret < 0) + return ret; + } + + /* 3. Master port enable_ch_post */ + if (ops->mstr_port_ops->dpn_port_activate_ch_post) { + ret = ops->mstr_port_ops->dpn_port_activate_ch_post + (mstr_bs->mstr, &activate_ch, banktouse); + if (ret < 0) + return ret; + } + + if (chn_en->is_activate) + mstr_rt_strm->rt_state = SDW_STATE_ENABLE_RT; + else if (!chn_en->is_bank_sw) + mstr_rt_strm->rt_state = SDW_STATE_DISABLE_RT; + + return 0; +} + + +/* + * sdw_en_dis_mstr_slv - returns Success + * -EINVAL - In case of error. + * + * + * This function call master/slave enable/disable + * channel API's. + */ +int sdw_en_dis_mstr_slv(struct sdw_bus *sdw_mstr_bs, + struct sdw_runtime *sdw_rt, bool is_act) +{ + struct sdw_slave_runtime *slv_rt_strm = NULL; + struct sdw_port_runtime *port_slv_strm, *port_mstr_strm; + struct sdw_mstr_runtime *mstr_rt_strm = NULL; + struct port_chn_en_state chn_en; + int ret = 0; + + if (is_act) + chn_en.is_bank_sw = true; + else + chn_en.is_bank_sw = false; + + chn_en.is_activate = is_act; + + list_for_each_entry(slv_rt_strm, &sdw_rt->slv_rt_list, slave_sdw_node) { + + if (slv_rt_strm->slave == NULL) + break; + + list_for_each_entry(port_slv_strm, + &slv_rt_strm->port_rt_list, port_node) { + + ret = sdw_cfg_slv_enable_disable + (sdw_mstr_bs, slv_rt_strm, + port_slv_strm, &chn_en); + if (ret < 0) + return ret; + + /* + * Since one port per slave runtime, + * breaking port_list loop + * TBD: to be extended for multiple port support + */ + break; + + } + + break; + + } + + list_for_each_entry(mstr_rt_strm, + &sdw_rt->mstr_rt_list, mstr_sdw_node) { + + if (mstr_rt_strm->mstr == NULL) + break; + + list_for_each_entry(port_mstr_strm, + &mstr_rt_strm->port_rt_list, port_node) { + + ret = sdw_cfg_mstr_activate_disable + (sdw_mstr_bs, mstr_rt_strm, + port_mstr_strm, &chn_en); + if (ret < 0) + return ret; + + /* + * Since one port per master runtime, + * breaking port_list loop + * TBD: to be extended for multiple port support + */ + break; + + } + + } + + return 0; +} + + +/* + * sdw_en_dis_mstr_slv_state - returns Success + * -EINVAL - In case of error. + * + * + * This function call master/slave enable/disable + * channel API's based on runtime state. + */ +int sdw_en_dis_mstr_slv_state(struct sdw_bus *sdw_mstr_bs, + struct sdw_mstr_runtime *sdw_mstr_bs_rt, + struct port_chn_en_state *chn_en) +{ + struct sdw_slave_runtime *slv_rt = NULL; + struct sdw_port_runtime *port_slv_rt, *port_rt; + int ret = 0; + + list_for_each_entry(slv_rt, &sdw_mstr_bs_rt->slv_rt_list, slave_node) { + + if (slv_rt->slave == NULL) + break; + + if (slv_rt->rt_state == SDW_STATE_ENABLE_RT) { + + list_for_each_entry(port_slv_rt, + &slv_rt->port_rt_list, port_node) { + + ret = sdw_cfg_slv_enable_disable + (sdw_mstr_bs, slv_rt, + port_slv_rt, chn_en); + if (ret < 0) + return ret; + + /* + * Since one port per slave runtime, + * breaking port_list loop + * TBD: to be extended for multiple + * port support + */ + break; + } + } + } + + if (sdw_mstr_bs_rt->rt_state == SDW_STATE_ENABLE_RT) { + + list_for_each_entry(port_rt, + &sdw_mstr_bs_rt->port_rt_list, port_node) { + + ret = sdw_cfg_mstr_activate_disable + (sdw_mstr_bs, sdw_mstr_bs_rt, port_rt, chn_en); + if (ret < 0) + return ret; + + /* + * Since one port per master runtime, + * breaking port_list loop + * TBD: to be extended for multiple port support + */ + + break; + } + } + + return 0; +} + + +/* + * sdw_get_clock_frmshp - returns Success + * -EINVAL - In case of error. + * + * + * This function computes clock and frame shape based on + * clock frequency. + */ +int sdw_get_clock_frmshp(struct sdw_bus *sdw_mstr_bs, int *frame_int, + int *col, int *row) +{ + int i, rc, clock_reqd = 0, frame_interval = 0, frame_frequency = 0; + int sel_row = 0, sel_col = 0; + bool clock_ok = false; + + /* + * Find nearest clock frequency needed by master for + * given bandwidth + */ + + /* + * TBD: Need to run efficient algorithm to make sure we have + * only 1 to 10 percent of control bandwidth usage + */ + for (i = 0; i < MAXCLOCKFREQ; i++) { + + /* TBD: Check why 3000 */ + if ((clock_freq[i] <= sdw_mstr_bs->bandwidth) || + ((clock_freq[i] % 3000) != 0)) + continue; + clock_reqd = clock_freq[i]; + + /* + * TBD: Check all the slave device capabilities + * here and find whether given frequency is + * supported by all slaves + */ + + /* Find frame shape based on bandwidth per controller */ + /* + * TBD: Need to run efficient algorithm to make sure we have + * only 1 to 10 percent of control bandwidth usage + */ + for (rc = 0; rc <= MAX_NUM_ROW_COLS; rc++) { + frame_interval = + sdw_core.rowcolcomb[rc].row * + sdw_core.rowcolcomb[rc].col; + frame_frequency = clock_reqd/frame_interval; + + if ((clock_reqd - + (frame_frequency * + sdw_core.rowcolcomb[rc]. + control_bits)) < + sdw_mstr_bs->bandwidth) + continue; + + break; + } + + sel_row = sdw_core.rowcolcomb[rc].row; + sel_col = sdw_core.rowcolcomb[rc].col; + sdw_mstr_bs->frame_freq = frame_frequency; + sdw_mstr_bs->clk_freq = clock_reqd; + clock_ok = false; + *frame_int = frame_interval; + *col = sel_col; + *row = sel_row; + sdw_mstr_bs->col = sel_col; + sdw_mstr_bs->row = sel_row; + + break; + + } + + return 0; +} + +/* + * sdw_compute_sys_interval - returns Success + * -EINVAL - In case of error. + * + * + * This function computes system interval. + */ +int sdw_compute_sys_interval(struct sdw_bus *sdw_mstr_bs, + struct sdw_master_capabilities *sdw_mstr_cap, + int frame_interval) +{ + struct sdw_master *sdw_mstr = sdw_mstr_bs->mstr; + struct sdw_mstr_runtime *sdw_mstr_bs_rt; + struct sdw_transport_params *t_params; + struct sdw_port_runtime *port_rt; + int lcmnum1 = 0, lcmnum2 = 0, div = 0, lcm = 0; + + /* + * once you got bandwidth frame shape for bus, + * run a loop for all the active streams running + * on bus and compute sample_interval & other transport parameters. + */ + list_for_each_entry(sdw_mstr_bs_rt, + &sdw_mstr->mstr_rt_list, mstr_node) { + + if (sdw_mstr_bs_rt->mstr == NULL) + break; + + /* should not compute any transport params */ + if (sdw_mstr_bs_rt->rt_state == SDW_STATE_UNPREPARE_RT) + continue; + + list_for_each_entry(port_rt, + &sdw_mstr_bs_rt->port_rt_list, port_node) { + + t_params = &port_rt->transport_params; + + /* + * Current Assumption: + * One port per bus runtime structure + */ + /* Calculate sample interval */ + t_params->sample_interval = + ((sdw_mstr_bs->clk_freq/ + sdw_mstr_bs_rt->stream_params.rate) * 2); + + /* Only BlockPerPort supported */ + t_params->blockpackingmode = 0; + t_params->lanecontrol = 0; + + /* Calculate LCM */ + lcmnum2 = t_params->sample_interval; + if (!lcmnum1) + lcmnum1 = sdw_lcm(lcmnum2, lcmnum2); + else + lcmnum1 = sdw_lcm(lcmnum1, lcmnum2); + + /* + * Since one port per bus runtime, breaking + * port_list loop + * TBD: to be extended for multiple port support + */ + break; + + } + } + + + /* 6. compute system_interval */ + if ((sdw_mstr_cap) && (sdw_mstr_bs->clk_freq)) { + + div = ((sdw_mstr_cap->base_clk_freq * 2) / + sdw_mstr_bs->clk_freq); + lcm = sdw_lcm(lcmnum1, frame_interval); + sdw_mstr_bs->system_interval = (div * lcm); + + } + + /* + * Something went wrong, may be sdw_lcm value may be 0, + * return error accordingly + */ + if (!sdw_mstr_bs->system_interval) + return -EINVAL; + + + return 0; +} + + +/* + * sdw_compute_hstart_hstop - returns Success + * -EINVAL - In case of error. + * + * + * This function computes hstart and hstop for running + * streams per master & slaves. + */ +int sdw_compute_hstart_hstop(struct sdw_bus *sdw_mstr_bs, int sel_col) +{ + struct sdw_master *sdw_mstr = sdw_mstr_bs->mstr; + struct sdw_mstr_runtime *sdw_mstr_bs_rt; + struct sdw_transport_params *t_params = NULL, *t_slv_params = NULL; + struct sdw_slave_runtime *slv_rt = NULL; + struct sdw_port_runtime *port_rt, *port_slv_rt; + int hstop = 0, hwidth = 0; + int payload_bw = 0, full_bw = 0, column_needed = 0; + bool hstop_flag = false; + + /* Calculate hwidth, hstart and hstop */ + list_for_each_entry(sdw_mstr_bs_rt, + &sdw_mstr->mstr_rt_list, mstr_node) { + + if (sdw_mstr_bs_rt->mstr == NULL) + break; + + /* should not compute any transport params */ + if (sdw_mstr_bs_rt->rt_state == SDW_STATE_UNPREPARE_RT) + continue; + + list_for_each_entry(port_rt, + &sdw_mstr_bs_rt->port_rt_list, port_node) { + + t_params = &port_rt->transport_params; + t_params->num = port_rt->port_num; + + /* + * 1. find full_bw and payload_bw per stream + * 2. find h_width per stream + * 3. find hstart, hstop, block_offset,sub_block_offset + * Note: full_bw is nothing but sampling interval + * of stream. + * payload_bw is serving size no. + * of channels * bps per stream + */ + full_bw = sdw_mstr_bs->clk_freq/ + sdw_mstr_bs_rt->stream_params.rate; + payload_bw = + sdw_mstr_bs_rt->stream_params.bps * + sdw_mstr_bs_rt->stream_params.channel_count; + + hwidth = (sel_col * payload_bw + full_bw - 1)/full_bw; + column_needed += hwidth; + + /* + * These needs to be done only for + * 1st entry in link list + */ + if (!hstop_flag) { + hstop = sel_col - 1; + hstop_flag = true; + } + + /* Assumption: Only block per port is supported + * For blockperport: + * offset1 value = LSB 8 bits of block_offset value + * offset2 value = MSB 8 bits of block_offset value + * For blockperchannel: + * offset1 = LSB 8 bit of block_offset value + * offset2 = MSB 8 bit of sub_block_offload value + * if hstart and hstop of different streams in + * master are different, then block_offset is zero. + * if not then block_offset value for 2nd stream + * is block_offset += payload_bw + */ + + t_params->hstop = hstop; + t_params->hstart = hstop - hwidth + 1; + + + /* + * TBD: perform this when you have 2 ports + * and accordingly configure hstart hstop for slave + * removing for now + */ +#if 0 + hstop = hstop - hwidth; +#endif + /* Since one port per bus runtime, + * breaking port_list loop + * TBD: to be extended for multiple port support + */ + break; + } + + /* + * Run loop for slave_rt_list for given master_list + * to compute hstart hstop for slave + */ + list_for_each_entry(slv_rt, + &sdw_mstr_bs_rt->slv_rt_list, slave_node) { + + if (slv_rt->slave == NULL) + break; + + if (slv_rt->rt_state == SDW_STATE_UNPREPARE_RT) + continue; + + list_for_each_entry(port_slv_rt, + &slv_rt->port_rt_list, port_node) { + + t_slv_params = &port_slv_rt->transport_params; + t_slv_params->num = port_slv_rt->port_num; + + /* + * TBD: Needs to be verifid for + * multiple combination + * 1. 1 master port, 1 slave rt, + * 1 port per slave rt --> + * In this case, use hstart hstop same as master + * for 1 slave rt + * 2. 1 master port, 2 slave rt, + * 1 port per slave rt --> + * In this case, use hstart hstop same as master + * for 2 slave rt + * only offset will change for 2nd slave rt + * Current assumption is one port per rt, + * hence no multiple port combination + * considered. + */ + t_slv_params->hstop = hstop; + t_slv_params->hstart = hstop - hwidth + 1; + + /* Only BlockPerPort supported */ + t_slv_params->blockpackingmode = 0; + t_slv_params->lanecontrol = 0; + + /* + * below copy needs to be changed when + * more than one port is supported + */ + if (t_params) + t_slv_params->sample_interval = + t_params->sample_interval; + + /* Since one port per slave runtime, + * breaking port_list loop + * TBD: to be extended for multiple + * port support + */ + break; + } + + } + } + +#if 0 + /* TBD: To be verified */ + if (column_needed > sel_col - 1) + return -EINVAL; /* Error case, check what has gone wrong */ +#endif + + return 0; +} + + +/* + * sdw_compute_blk_subblk_offset - returns Success + * + * + * This function computes block offset and sub block + * offset for running streams per master & slaves. + */ +int sdw_compute_blk_subblk_offset(struct sdw_bus *sdw_mstr_bs) +{ + struct sdw_master *sdw_mstr = sdw_mstr_bs->mstr; + struct sdw_mstr_runtime *sdw_mstr_bs_rt; + struct sdw_transport_params *t_params, *t_slv_params; + struct sdw_slave_runtime *slv_rt = NULL; + struct sdw_port_runtime *port_rt, *port_slv_rt; + int hstart1 = 0, hstop1 = 0, hstart2 = 0, hstop2 = 0; + int block_offset = 1; + + + /* Calculate block_offset and subblock_offset */ + list_for_each_entry(sdw_mstr_bs_rt, + &sdw_mstr->mstr_rt_list, mstr_node) { + + if (sdw_mstr_bs_rt->mstr == NULL) + break; + + /* should not compute any transport params */ + if (sdw_mstr_bs_rt->rt_state == SDW_STATE_UNPREPARE_RT) + continue; + + list_for_each_entry(port_rt, + &sdw_mstr_bs_rt->port_rt_list, port_node) { + + t_params = &port_rt->transport_params; + + + if ((!hstart2) && (!hstop2)) { + hstart1 = hstart2 = t_params->hstart; + hstop1 = hstop2 = t_params->hstop; + /* TBD: Verify this condition */ + block_offset = 0; + } else { + + hstart1 = t_params->hstart; + hstop1 = t_params->hstop; + + /* hstart/stop not same */ + if ((hstart1 != hstart2) && + (hstop1 != hstop2)) { + /* TBD: Harcoding to 0, to be removed*/ + block_offset = 0; + } else { + /* TBD: Harcoding to 0, to be removed*/ + block_offset = 0; + } + +#if 0 + if ((hstart1 != hstart2) && + (hstop1 != hstop2)) { + block_offset = 1; + } else { + block_offset += + (sdw_mstr_bs_rt->stream_params. + bps + * + sdw_mstr_bs_rt->stream_params. + channel_count); + } +#endif + + } + + + /* + * TBD: Hardcding block control group as true, + * to be changed later + */ + t_params->blockgroupcontrol_valid = true; + t_params->blockgroupcontrol = 0x0; /* Hardcoding to 0 */ + + /* + * Since one port per bus runtime, + * breaking port_list loop + * TBD: to be extended for multiple port support + */ + break; + } + + /* + * Run loop for slave_rt_list for given master_list + * to compute block and sub block offset for slave + */ + list_for_each_entry(slv_rt, + &sdw_mstr_bs_rt->slv_rt_list, slave_node) { + + if (slv_rt->slave == NULL) + break; + + if (slv_rt->rt_state == SDW_STATE_UNPREPARE_RT) + continue; + + list_for_each_entry(port_slv_rt, + &slv_rt->port_rt_list, port_node) { + + t_slv_params = &port_slv_rt->transport_params; + + /* + * TBD: Needs to be verifid for + * multiple combination + * 1. 1 master port, 1 slave rt, + * 1 port per slave rt --> + * In this case, use block_offset same as + * master for 1 slave rt + * 2. 1 master port, 2 slave rt, + * 1 port per slave rt --> + * In this case, use block_offset same as + * master for 1st slave rt and compute for 2nd. + */ + + /* + * Current assumption is one port per rt, + * hence no multiple port combination. + * TBD: block offset to be computed for + * more than 1 slave_rt list. + */ + t_slv_params->offset1 = block_offset; + t_slv_params->offset2 = block_offset >> 8; + + + /* + * TBD: Hardcding block control group as true, + * to be changed later + */ + t_slv_params->blockgroupcontrol_valid = true; + /* Hardcoding to 0 */ + t_slv_params->blockgroupcontrol = 0x0; + /* Since one port per slave runtime, + * breaking port_list loop + * TBD:to be extended for multiple port support + */ + break; + } + } + } + + return 0; +} + + +/* + * sdw_configure_frmshp_bnkswtch - returns Success + * -EINVAL - In case of error. + * + * + * This function broadcast frameshape on framectrl + * register and performs bank switch. + */ +int sdw_configure_frmshp_bnkswtch(struct sdw_bus *mstr_bs, int col, int row) +{ + struct sdw_msg wr_msg; + int ret = 0; + int banktouse, numcol, numrow; + u8 wbuf[1] = {0}; + + numcol = sdw_get_col_to_num(col); + numrow = sdw_get_row_to_num(row); + + wbuf[0] = numcol | (numrow << 3); + /* Get current bank in use from bus structure*/ + banktouse = mstr_bs->active_bank; + banktouse = !banktouse; + + if (banktouse) { + wr_msg.addr = (SDW_SCP_FRAMECTRL + SDW_BANK1_REGISTER_OFFSET) + + (SDW_NUM_DATA_PORT_REGISTERS * 0); /* Data port 0 */ + } else { + + wr_msg.addr = SDW_SCP_FRAMECTRL + + (SDW_NUM_DATA_PORT_REGISTERS * 0); /* Data port 0 */ + } + + wr_msg.ssp_tag = 0x1; + wr_msg.flag = SDW_MSG_FLAG_WRITE; + wr_msg.len = 1; + wr_msg.slave_addr = 0xF; /* Broadcast address*/ + wr_msg.buf = wbuf; + wr_msg.addr_page1 = 0x0; + wr_msg.addr_page2 = 0x0; + + + ret = sdw_slave_transfer(mstr_bs->mstr, &wr_msg, 1); + if (ret != 1) { + ret = -EINVAL; + dev_err(&mstr_bs->mstr->dev, "Register transfer failed\n"); + goto out; + } + + msleep(100); /* TBD: Remove this */ + + /* + * TBD: check whether we need to poll on + * mcp active bank bit to switch bank + */ + mstr_bs->active_bank = banktouse; + +out: + + return ret; +} + + +/* + * sdw_cfg_bs_params - returns Success + * -EINVAL - In case of error. + * + * + * This function performs master/slave transport + * params config, set SSP interval, set Clock + * frequency, enable channel. This API is called + * from sdw_bus_calc_bw & sdw_bus_calc_bw_dis API. + * + */ +int sdw_cfg_bs_params(struct sdw_bus *sdw_mstr_bs, + struct sdw_mstr_runtime *sdw_mstr_bs_rt, + bool is_strm_cpy) +{ + struct port_chn_en_state chn_en; + struct sdw_master *sdw_mstr = sdw_mstr_bs->mstr; + struct sdw_mstr_driver *ops; + int banktouse, ret = 0; + + list_for_each_entry(sdw_mstr_bs_rt, + &sdw_mstr->mstr_rt_list, mstr_node) { + + if (sdw_mstr_bs_rt->mstr == NULL) + continue; + + if (is_strm_cpy) { + /* + * Configure and enable all slave + * transport params first + */ + ret = sdw_cfg_mstr_slv(sdw_mstr_bs, + sdw_mstr_bs_rt, false); + if (ret < 0) { + /* TBD: Undo all the computation */ + dev_err(&sdw_mstr_bs->mstr->dev, + "slave config params failed\n"); + return ret; + } + + /* Configure and enable all master params */ + ret = sdw_cfg_mstr_slv(sdw_mstr_bs, + sdw_mstr_bs_rt, true); + if (ret < 0) { + /* TBD: Undo all the computation */ + dev_err(&sdw_mstr_bs->mstr->dev, + "master config params failed\n"); + return ret; + } + + } else { + + /* + * 7.1 Copy all slave transport and port params + * to alternate bank + * 7.2 copy all master transport and port params + * to alternate bank + */ + ret = sdw_cpy_params_mstr_slv(sdw_mstr_bs, + sdw_mstr_bs_rt); + if (ret < 0) { + /* TBD: Undo all the computation */ + dev_err(&sdw_mstr_bs->mstr->dev, + "slave/master copy params failed\n"); + return ret; + } + } + + /* Get master driver ops */ + ops = sdw_mstr_bs->mstr->driver; + + /* Configure SSP */ + banktouse = sdw_mstr_bs->active_bank; + banktouse = !banktouse; + + /* + * TBD: Currently harcoded SSP interval to 24, + * computed value to be taken from system_interval in + * bus data structure. + * Add error check. + */ + if (ops->mstr_ops->set_ssp_interval) + ops->mstr_ops->set_ssp_interval(sdw_mstr_bs->mstr, + 24, banktouse); /* hardcoding to 24 */ + /* + * Configure Clock + * TBD: Add error check + */ + if (ops->mstr_ops->set_clock_freq) + ops->mstr_ops->set_clock_freq(sdw_mstr_bs->mstr, + sdw_mstr_bs->clk_freq, banktouse); + + /* Enable channel on alternate bank for running streams */ + chn_en.is_activate = true; + chn_en.is_bank_sw = true; + ret = sdw_en_dis_mstr_slv_state + (sdw_mstr_bs, sdw_mstr_bs_rt, &chn_en); + if (ret < 0) { + /* TBD: Undo all the computation */ + dev_err(&sdw_mstr_bs->mstr->dev, + "Channel enable failed\n"); + return ret; + } + + } + + return 0; +} + +/* + * sdw_dis_chan - returns Success + * -EINVAL - In case of error. + * + * + * This function disables channel on alternate + * bank. This API is called from sdw_bus_calc_bw + * & sdw_bus_calc_bw_dis when channel on current + * bank is enabled. + * + */ +int sdw_dis_chan(struct sdw_bus *sdw_mstr_bs, + struct sdw_mstr_runtime *sdw_mstr_bs_rt) +{ + struct sdw_master *sdw_mstr = sdw_mstr_bs->mstr; + struct port_chn_en_state chn_en; + int ret = 0; + + list_for_each_entry(sdw_mstr_bs_rt, + &sdw_mstr->mstr_rt_list, mstr_node) { + + if (sdw_mstr_bs_rt->mstr == NULL) + continue; + + chn_en.is_activate = false; + chn_en.is_bank_sw = true; + ret = sdw_en_dis_mstr_slv_state(sdw_mstr_bs, + sdw_mstr_bs_rt, &chn_en); + if (ret < 0) + return ret; + } + + return 0; +} + + +/* + * sdw_cfg_slv_prep_unprep - returns Success + * -EINVAL - In case of error. + * + * + * This function prepare/unprepare slave ports. + */ +int sdw_cfg_slv_prep_unprep(struct sdw_bus *mstr_bs, + struct sdw_slave_runtime *slv_rt_strm, + struct sdw_port_runtime *port_slv_strm, + bool prep) +{ + struct sdw_slave_driver *slv_ops = slv_rt_strm->slave->driver; + struct sdw_slv_capabilities *slv_cap = + &slv_rt_strm->slave->sdw_slv_cap; + struct sdw_slv_dpn_capabilities *sdw_slv_dpn_cap = + slv_cap->sdw_dpn_cap; + + struct sdw_msg wr_msg, rd_msg, rd_msg1; + int ret = 0; + int banktouse; + u8 wbuf[1] = {0}; + u8 rbuf[1] = {0}; + u8 rbuf1[1] = {0}; + + /* Get current bank in use from bus structure*/ + banktouse = mstr_bs->active_bank; + banktouse = !banktouse; + + /* Read SDW_DPN_PREPARECTRL register */ + rd_msg.addr = wr_msg.addr = SDW_DPN_PREPARECTRL + + (SDW_NUM_DATA_PORT_REGISTERS * port_slv_strm->port_num); + + rd_msg.ssp_tag = 0x0; + rd_msg.flag = SDW_MSG_FLAG_READ; + rd_msg.len = 1; + rd_msg.slave_addr = slv_rt_strm->slave->slv_number; + rd_msg.buf = rbuf; + rd_msg.addr_page1 = 0x0; + rd_msg.addr_page2 = 0x0; + + rd_msg1.ssp_tag = 0x0; + rd_msg1.flag = SDW_MSG_FLAG_READ; + rd_msg1.len = 1; + rd_msg1.slave_addr = slv_rt_strm->slave->slv_number; + rd_msg1.buf = rbuf1; + rd_msg1.addr_page1 = 0x0; + rd_msg1.addr_page2 = 0x0; + + + rd_msg1.addr = SDW_DPN_PREPARESTATUS + + (SDW_NUM_DATA_PORT_REGISTERS * port_slv_strm->port_num); + + wr_msg.ssp_tag = 0x0; + wr_msg.flag = SDW_MSG_FLAG_WRITE; + wr_msg.len = 1; + wr_msg.slave_addr = slv_rt_strm->slave->slv_number; + wr_msg.buf = wbuf; + wr_msg.addr_page1 = 0x0; + wr_msg.addr_page2 = 0x0; + + + if (prep) { /* PREPARE */ + + /* + * 1. slave port prepare_ch_pre + * --> callback + * --> handle_pre_port_prepare + */ + if (slv_ops->handle_pre_port_prepare) { + slv_ops->handle_pre_port_prepare(slv_rt_strm->slave, + port_slv_strm->port_num, + port_slv_strm->channel_mask, + banktouse); + } + + /* 2. slave port prepare --> to write */ + if (sdw_slv_dpn_cap->prepare_ch) { + + /* NON SIMPLIFIED CM, prepare required */ + ret = sdw_slave_transfer(mstr_bs->mstr, &rd_msg, 1); + if (ret != 1) { + ret = -EINVAL; + dev_err(&mstr_bs->mstr->dev, + "Register transfer failed\n"); + goto out; + } + + ret = sdw_slave_transfer(mstr_bs->mstr, &rd_msg1, 1); + if (ret != 1) { + ret = -EINVAL; + dev_err(&mstr_bs->mstr->dev, + "Register transfer failed\n"); + goto out; + } + + wbuf[0] = (rbuf[0] | port_slv_strm->channel_mask); + + /* + * TBD: poll for prepare interrupt bit + * before calling post_prepare + * 2. check capabilities if simplified + * CM no need to prepare + */ + ret = sdw_slave_transfer(mstr_bs->mstr, &wr_msg, 1); + if (ret != 1) { + ret = -EINVAL; + dev_err(&mstr_bs->mstr->dev, + "Register transfer failed\n"); + goto out; + } + + /* + * TBD: check on port ready, + * ideally we should check on prepare + * status for port_ready + */ + + /* wait for completion on port ready*/ + msleep(100); /* TBD: Remove this */ + + ret = sdw_slave_transfer(mstr_bs->mstr, &rd_msg1, 1); + if (ret != 1) { + ret = -EINVAL; + dev_err(&mstr_bs->mstr->dev, + "Register transfer failed\n"); + goto out; + } + } + + /* + * 3. slave port post pre + * --> callback + * --> handle_post_port_prepare + */ + if (slv_ops->handle_post_port_prepare) { + slv_ops->handle_post_port_prepare + (slv_rt_strm->slave, + port_slv_strm->port_num, + port_slv_strm->channel_mask, banktouse); + } + + slv_rt_strm->rt_state = SDW_STATE_PREPARE_RT; + + } else { + /* UNPREPARE */ + /* + * 1. slave port unprepare_ch_pre + * --> callback + * --> handle_pre_port_prepare + */ + if (slv_ops->handle_pre_port_unprepare) { + slv_ops->handle_pre_port_unprepare(slv_rt_strm->slave, + port_slv_strm->port_num, + port_slv_strm->channel_mask, + banktouse); + } + + /* 2. slave port unprepare --> to write */ + if (sdw_slv_dpn_cap->prepare_ch) { + + /* NON SIMPLIFIED CM, unprepare required */ + + /* Read SDW_DPN_PREPARECTRL register */ + ret = sdw_slave_transfer(mstr_bs->mstr, &rd_msg, 1); + if (ret != 1) { + ret = -EINVAL; + dev_err(&mstr_bs->mstr->dev, + "Register transfer failed\n"); + goto out; + } + + wbuf[0] = (rbuf[0] & ~(port_slv_strm->channel_mask)); + + /* + * TBD: poll for prepare interrupt bit before + * calling post_prepare + * Does it apply for unprepare aswell? + * 2. check capabilities if simplified CM + * no need to unprepare + */ + ret = sdw_slave_transfer(mstr_bs->mstr, &wr_msg, 1); + if (ret != 1) { + ret = -EINVAL; + dev_err(&mstr_bs->mstr->dev, + "Register transfer failed\n"); + goto out; + } + } + + /* + * 3. slave port post unpre + * --> callback + * --> handle_post_port_unprepare + */ + if (slv_ops->handle_post_port_unprepare) { + slv_ops->handle_post_port_unprepare(slv_rt_strm->slave, + port_slv_strm->port_num, + port_slv_strm->channel_mask, + banktouse); + } + + slv_rt_strm->rt_state = SDW_STATE_UNPREPARE_RT; + } +out: + return ret; + +} + + +/* + * sdw_cfg_mstr_prep_unprep - returns Success + * -EINVAL - In case of error. + * + * + * This function prepare/unprepare master ports. + */ +int sdw_cfg_mstr_prep_unprep(struct sdw_bus *mstr_bs, + struct sdw_mstr_runtime *mstr_rt_strm, + struct sdw_port_runtime *port_mstr_strm, + bool prep) +{ + struct sdw_mstr_driver *ops = mstr_bs->mstr->driver; + struct sdw_prepare_ch prep_ch; + int ret = 0; + + prep_ch.num = port_mstr_strm->port_num; + prep_ch.ch_mask = port_mstr_strm->channel_mask; + prep_ch.prepare = prep; /* Prepare/Unprepare */ + + /* TBD: Bank configuration */ + + /* 1. Master port prepare_ch_pre */ + if (ops->mstr_port_ops->dpn_port_prepare_ch_pre) { + ret = ops->mstr_port_ops->dpn_port_prepare_ch_pre + (mstr_bs->mstr, &prep_ch); + if (ret < 0) + return ret; + } + + /* 2. Master port prepare */ + if (ops->mstr_port_ops->dpn_port_prepare_ch) { + ret = ops->mstr_port_ops->dpn_port_prepare_ch + (mstr_bs->mstr, &prep_ch); + if (ret < 0) + return ret; + } + + /* 3. Master port prepare_ch_post */ + if (ops->mstr_port_ops->dpn_port_prepare_ch_post) { + ret = ops->mstr_port_ops->dpn_port_prepare_ch_post + (mstr_bs->mstr, &prep_ch); + if (ret < 0) + return ret; + } + + if (prep) + mstr_rt_strm->rt_state = SDW_STATE_PREPARE_RT; + else + mstr_rt_strm->rt_state = SDW_STATE_UNPREPARE_RT; + + return 0; +} + + +/* + * sdw_prep_unprep_mstr_slv - returns Success + * -EINVAL - In case of error. + * + * + * This function call master/slave prepare/unprepare + * port configuration API's, called from sdw_bus_calc_bw + * & sdw_bus_calc_bw_dis API's. + */ +int sdw_prep_unprep_mstr_slv(struct sdw_bus *sdw_mstr_bs, + struct sdw_runtime *sdw_rt, bool is_prep) +{ + struct sdw_slave_runtime *slv_rt_strm = NULL; + struct sdw_port_runtime *port_slv_strm, *port_mstr_strm; + struct sdw_mstr_runtime *mstr_rt_strm = NULL; + int ret = 0; + + list_for_each_entry(slv_rt_strm, + &sdw_rt->slv_rt_list, slave_sdw_node) { + + if (slv_rt_strm->slave == NULL) + break; + + list_for_each_entry(port_slv_strm, + &slv_rt_strm->port_rt_list, port_node) { + + ret = sdw_cfg_slv_prep_unprep(sdw_mstr_bs, + slv_rt_strm, port_slv_strm, is_prep); + if (ret < 0) + return ret; + + /* Since one port per slave runtime, + * breaking port_list loop + * TBD: to be extended for multiple port support + */ + break; + } + + break; + } + + list_for_each_entry(mstr_rt_strm, + &sdw_rt->mstr_rt_list, mstr_sdw_node) { + + if (mstr_rt_strm->mstr == NULL) + break; + + list_for_each_entry(port_mstr_strm, + &mstr_rt_strm->port_rt_list, port_node) { + + ret = sdw_cfg_mstr_prep_unprep(sdw_mstr_bs, + mstr_rt_strm, port_mstr_strm, is_prep); + if (ret < 0) + return ret; + + /* Since one port per master runtime, + * breaking port_list loop + * TBD: to be extended for multiple port support + */ + break; + } + } + + return 0; +} + + +/** + * sdw_bus_calc_bw - returns Success + * -EINVAL - In case of error. + * + * + * This function is called from sdw_prepare_and_enable + * whenever new stream is processed. The function based + * on the stream associated with controller calculates + * required bandwidth, clock, frameshape, computes + * all transport params for a given port, enable channel + * & perform bankswitch. + */ +int sdw_bus_calc_bw(struct sdw_stream_tag *stream_tag, bool enable) +{ + + struct sdw_runtime *sdw_rt = stream_tag->sdw_rt; + struct sdw_stream_params *stream_params = &sdw_rt->stream_params; + struct sdw_mstr_runtime *sdw_mstr_rt = NULL, *sdw_mstr_bs_rt = NULL; + struct sdw_bus *sdw_mstr_bs = NULL; + struct sdw_master *sdw_mstr = NULL; + struct sdw_master_capabilities *sdw_mstr_cap = NULL; + struct sdw_stream_params *mstr_params; + int stream_frame_size; + int frame_interval = 0, sel_row = 0, sel_col = 0; + int ret = 0; + + /* TBD: Add PCM/PDM flag in sdw_config_stream */ + + /* + * TBD: check for mstr_rt is in configured state or not + * If yes, then configure masters as well + * If no, then do not configure/enable master related parameters + */ + + /* BW calulation for active master controller for given stream tag */ + list_for_each_entry(sdw_mstr_rt, &sdw_rt->mstr_rt_list, mstr_sdw_node) { + + if (sdw_mstr_rt->mstr == NULL) + break; + + /* Get bus structure for master */ + list_for_each_entry(sdw_mstr_bs, &sdw_core.bus_list, bus_node) { + + /* Match master structure pointer */ + if (sdw_mstr_bs->mstr != sdw_mstr_rt->mstr) + continue; + + + sdw_mstr = sdw_mstr_bs->mstr; + break; + } + + /* + * All data structures required available, + * lets calculate BW for master controller + */ + + /* Check for isochronous mode plus other checks if required */ + sdw_mstr_cap = &sdw_mstr_bs->mstr->mstr_capabilities; + mstr_params = &sdw_mstr_rt->stream_params; + + if ((sdw_rt->stream_state == SDW_STATE_CONFIG_STREAM) || + (sdw_rt->stream_state == + SDW_STATE_UNPREPARE_STREAM)) { + + /* we do not support asynchronous mode Return Error */ + if ((sdw_mstr_cap->base_clk_freq % mstr_params->rate) + != 0) { + /* TBD: Undo all the computation */ + dev_err(&sdw_mstr_bs->mstr->dev, + "Asynchronous mode not supported\n"); + return -EINVAL; + } + + /* Check for sampling frequency */ + if (stream_params->rate != mstr_params->rate) { + /* TBD: Undo all the computation */ + dev_err(&sdw_mstr_bs->mstr->dev, + "Sampling frequency mismatch\n"); + return -EINVAL; + } + + /* + * Calculate stream bandwidth, frame size and + * total BW required for master controller + */ + sdw_mstr_rt->stream_bw = mstr_params->rate * + mstr_params->channel_count * mstr_params->bps; + stream_frame_size = mstr_params->channel_count * + mstr_params->bps; + + sdw_mstr_bs->bandwidth += sdw_mstr_rt->stream_bw; + + ret = sdw_get_clock_frmshp(sdw_mstr_bs, + &frame_interval, &sel_col, &sel_row); + if (ret < 0) { + /* TBD: Undo all the computation */ + dev_err(&sdw_mstr_bs->mstr->dev, "clock/frameshape config failed\n"); + return ret; + } + + + /* + * TBD: find right place to run sorting on + * master rt_list. Below sorting is done based on + * bps from low to high, that means PDM streams + * will be placed before PCM. + */ + + /* + * TBD Should we also perform sorting based on rate + * for PCM stream check. if yes then how?? + * creating two different list. + */ + + /* Compute system interval */ + ret = sdw_compute_sys_interval(sdw_mstr_bs, + sdw_mstr_cap, frame_interval); + if (ret < 0) { + /* TBD: Undo all the computation */ + dev_err(&sdw_mstr_bs->mstr->dev, "compute system interval failed\n"); + return ret; + } + + /* Compute hstart/hstop */ + ret = sdw_compute_hstart_hstop(sdw_mstr_bs, sel_col); + if (ret < 0) { + /* TBD: Undo all the computation */ + dev_err(&sdw_mstr_bs->mstr->dev, + "compute hstart/hstop failed\n"); + return ret; + } + + /* Compute block offset */ + ret = sdw_compute_blk_subblk_offset(sdw_mstr_bs); + if (ret < 0) { + /* TBD: Undo all the computation */ + dev_err( + &sdw_mstr_bs->mstr->dev, + "compute block offset failed\n"); + return ret; + } + + /* Change Stream State */ + sdw_rt->stream_state = SDW_STATE_COMPUTE_STREAM; + + /* Configure bus parameters */ + ret = sdw_cfg_bs_params(sdw_mstr_bs, + sdw_mstr_bs_rt, true); + if (ret < 0) { + /* TBD: Undo all the computation */ + dev_err(&sdw_mstr_bs->mstr->dev, + "xport params config failed\n"); + return ret; + } + + sel_col = sdw_mstr_bs->col; + sel_row = sdw_mstr_bs->row; + + /* Configure Frame Shape/Switch Bank */ + ret = sdw_configure_frmshp_bnkswtch(sdw_mstr_bs, + sel_col, sel_row); + if (ret < 0) { + /* TBD: Undo all the computation */ + dev_err(&sdw_mstr_bs->mstr->dev, + "bank switch failed\n"); + return ret; + } + + /* Disable all channels enabled on previous bank */ + ret = sdw_dis_chan(sdw_mstr_bs, sdw_mstr_bs_rt); + if (ret < 0) { + /* TBD: Undo all the computation */ + dev_err(&sdw_mstr_bs->mstr->dev, + "Channel disabled failed\n"); + return ret; + } + + /* Prepare new port for master and slave */ + ret = sdw_prep_unprep_mstr_slv(sdw_mstr_bs, + sdw_rt, true); + if (ret < 0) { + /* TBD: Undo all the computation */ + dev_err(&sdw_mstr_bs->mstr->dev, + "Channel prepare failed\n"); + return ret; + } + + /* change stream state to prepare */ + sdw_rt->stream_state = SDW_STATE_PREPARE_STREAM; + } + + if ((enable) && (SDW_STATE_PREPARE_STREAM + == sdw_rt->stream_state)) { + + ret = sdw_cfg_bs_params(sdw_mstr_bs, + sdw_mstr_bs_rt, false); + if (ret < 0) { + /* TBD: Undo all the computation */ + dev_err(&sdw_mstr_bs->mstr->dev, + "xport params config failed\n"); + return ret; + } + + /* Enable new port for master and slave */ + ret = sdw_en_dis_mstr_slv(sdw_mstr_bs, sdw_rt, true); + if (ret < 0) { + /* TBD: Undo all the computation */ + dev_err(&sdw_mstr_bs->mstr->dev, + "Channel enable failed\n"); + return ret; + } + + /* change stream state to enable */ + sdw_rt->stream_state = SDW_STATE_ENABLE_STREAM; + + sel_col = sdw_mstr_bs->col; + sel_row = sdw_mstr_bs->row; + + /* Configure Frame Shape/Switch Bank */ + ret = sdw_configure_frmshp_bnkswtch(sdw_mstr_bs, + sel_col, sel_row); + if (ret < 0) { + /* TBD: Undo all the computation */ + dev_err(&sdw_mstr_bs->mstr->dev, + "bank switch failed\n"); + return ret; + } + + /* Disable all channels enabled on previous bank */ + ret = sdw_dis_chan(sdw_mstr_bs, sdw_mstr_bs_rt); + if (ret < 0) { + /* TBD: Undo all the computation */ + dev_err(&sdw_mstr_bs->mstr->dev, + "Channel disabled faile\n"); + return ret; + } + } + } + + return 0; +} +EXPORT_SYMBOL_GPL(sdw_bus_calc_bw); + + +/** + * sdw_bus_calc_bw_dis - returns Success + * -EINVAL - In case of error. + * + * + * This function is called from sdw_disable_and_unprepare + * whenever stream is ended. The function based disables/ + * unprepare port/channel of associated stream and computes + * required bandwidth, clock, frameshape, computes + * all transport params for a given port, enable channel + * & perform bankswitch for remaining streams on given + * controller. + */ +int sdw_bus_calc_bw_dis(struct sdw_stream_tag *stream_tag, bool unprepare) +{ + struct sdw_runtime *sdw_rt = stream_tag->sdw_rt; + struct sdw_mstr_runtime *sdw_mstr_rt = NULL, *sdw_mstr_bs_rt = NULL; + struct sdw_bus *sdw_mstr_bs = NULL; + struct sdw_master *sdw_mstr = NULL; + struct sdw_master_capabilities *sdw_mstr_cap = NULL; + struct sdw_stream_params *mstr_params; + int stream_frame_size; + int frame_interval = 0, sel_row = 0, sel_col = 0; + int ret = 0; + + + /* BW calulation for active master controller for given stream tag */ + list_for_each_entry(sdw_mstr_rt, &sdw_rt->mstr_rt_list, mstr_sdw_node) { + + if (sdw_mstr_rt->mstr == NULL) + break; + + /* Get bus structure for master */ + list_for_each_entry(sdw_mstr_bs, &sdw_core.bus_list, bus_node) { + + /* Match master structure pointer */ + if (sdw_mstr_bs->mstr != sdw_mstr_rt->mstr) + continue; + + + sdw_mstr = sdw_mstr_bs->mstr; + break; + } + + + sdw_mstr_cap = &sdw_mstr_bs->mstr->mstr_capabilities; + mstr_params = &sdw_mstr_rt->stream_params; + + if (sdw_rt->stream_state == SDW_STATE_ENABLE_STREAM) { + + /* Lets do disabling of port for stream to be freed */ + list_for_each_entry(sdw_mstr_bs_rt, + &sdw_mstr->mstr_rt_list, mstr_node) { + + if (sdw_mstr_bs_rt->mstr == NULL) + continue; + + /* + * Disable channel for slave and + * master on current bank + */ + ret = sdw_en_dis_mstr_slv(sdw_mstr_bs, + sdw_rt, false); + if (ret < 0) { + /* TBD: Undo all the computation */ + dev_err(&sdw_mstr_bs->mstr->dev, + "Channel dis failed\n"); + return ret; + } + + /* Change stream state to disable */ + sdw_rt->stream_state = SDW_STATE_DISABLE_STREAM; + } + + ret = sdw_cfg_bs_params(sdw_mstr_bs, + sdw_mstr_bs_rt, false); + if (ret < 0) { + /* TBD: Undo all the computation */ + dev_err(&sdw_mstr_bs->mstr->dev, + "xport params config failed\n"); + return ret; + } + + sel_col = sdw_mstr_bs->col; + sel_row = sdw_mstr_bs->row; + + /* Configure frame shape/Switch Bank */ + ret = sdw_configure_frmshp_bnkswtch(sdw_mstr_bs, + sel_col, sel_row); + if (ret < 0) { + /* TBD: Undo all the computation */ + dev_err(&sdw_mstr_bs->mstr->dev, + "bank switch failed\n"); + return ret; + } + + /* Disable all channels enabled on previous bank */ + ret = sdw_dis_chan(sdw_mstr_bs, sdw_mstr_bs_rt); + if (ret < 0) { + /* TBD: Undo all the computation */ + dev_err(&sdw_mstr_bs->mstr->dev, + "Channel disabled failed\n"); + return ret; + } + } + + if ((unprepare) && + (SDW_STATE_DISABLE_STREAM == + sdw_rt->stream_state)) { + + /* 1. Un-prepare master and slave port */ + list_for_each_entry(sdw_mstr_bs_rt, + &sdw_mstr->mstr_rt_list, mstr_node) { + + if (sdw_mstr_bs_rt->mstr == NULL) + continue; + + ret = sdw_prep_unprep_mstr_slv(sdw_mstr_bs, + sdw_rt, false); + if (ret < 0) { + /* TBD: Undo all the computation */ + dev_err(&sdw_mstr_bs->mstr->dev, + "Chan unprep failed\n"); + return ret; + } + + /* change stream state to unprepare */ + sdw_rt->stream_state = + SDW_STATE_UNPREPARE_STREAM; + } + + /* + * Calculate new bandwidth, frame size + * and total BW required for master controller + */ + sdw_mstr_rt->stream_bw = mstr_params->rate * + mstr_params->channel_count * mstr_params->bps; + stream_frame_size = mstr_params->channel_count * + mstr_params->bps; + + sdw_mstr_bs->bandwidth -= sdw_mstr_rt->stream_bw; + + /* Something went wrong in bandwidth calulation */ + if (sdw_mstr_bs->bandwidth < 0) { + dev_err(&sdw_mstr_bs->mstr->dev, + "BW calculation failed\n"); + return -EINVAL; + } + + if (!sdw_mstr_bs->bandwidth) { + /* + * Last stream on master should + * return successfully + */ + sdw_rt->stream_state = + SDW_STATE_UNCOMPUTE_STREAM; + return 0; + } + + ret = sdw_get_clock_frmshp(sdw_mstr_bs, + &frame_interval, &sel_col, &sel_row); + if (ret < 0) { + /* TBD: Undo all the computation */ + dev_err(&sdw_mstr_bs->mstr->dev, + "clock/frameshape failed\n"); + return ret; + } + + /* Compute new transport params for running streams */ + /* No sorting required here */ + + /* Compute system interval */ + ret = sdw_compute_sys_interval(sdw_mstr_bs, + sdw_mstr_cap, frame_interval); + if (ret < 0) { + /* TBD: Undo all the computation */ + dev_err(&sdw_mstr_bs->mstr->dev, + "compute SI failed\n"); + return ret; + } + + /* Compute hstart/hstop */ + ret = sdw_compute_hstart_hstop(sdw_mstr_bs, sel_col); + if (ret < 0) { + /* TBD: Undo all the computation */ + dev_err(&sdw_mstr_bs->mstr->dev, + "compute hstart/hstop fail\n"); + return ret; + } + + /* Compute block offset */ + ret = sdw_compute_blk_subblk_offset(sdw_mstr_bs); + if (ret < 0) { + /* TBD: Undo all the computation */ + dev_err(&sdw_mstr_bs->mstr->dev, + "compute block offset failed\n"); + return ret; + } + + /* Configure bus params */ + ret = sdw_cfg_bs_params(sdw_mstr_bs, + sdw_mstr_bs_rt, true); + if (ret < 0) { + /* TBD: Undo all the computation */ + dev_err(&sdw_mstr_bs->mstr->dev, + "xport params config failed\n"); + return ret; + } + + /* Configure Frame Shape/Switch Bank */ + ret = sdw_configure_frmshp_bnkswtch(sdw_mstr_bs, + sel_col, sel_row); + if (ret < 0) { + /* TBD: Undo all the computation */ + dev_err(&sdw_mstr_bs->mstr->dev, + "bank switch failed\n"); + return ret; + } + + /* Change stream state to uncompute */ + sdw_rt->stream_state = SDW_STATE_UNCOMPUTE_STREAM; + + /* Disable all channels enabled on previous bank */ + ret = sdw_dis_chan(sdw_mstr_bs, sdw_mstr_bs_rt); + if (ret < 0) { + /* TBD: Undo all the computation */ + dev_err(&sdw_mstr_bs->mstr->dev, + "Channel disabled failed\n"); + return ret; + } + } + + } + + return 0; +} +EXPORT_SYMBOL_GPL(sdw_bus_calc_bw_dis); diff --git a/drivers/sdw/sdw_cnl.c b/drivers/sdw/sdw_cnl.c new file mode 100644 index 000000000000..3f3317a6707a --- /dev/null +++ b/drivers/sdw/sdw_cnl.c @@ -0,0 +1,1583 @@ +/* + * sdw_cnl.c - Intel SoundWire master controller driver implementation. + * + * Copyright (C) 2015-2016 Intel Corp + * Author: Hardik T Shah + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * 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; version 2 of the License. + * + * 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. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "sdw_cnl_priv.h" + +static inline int cnl_sdw_reg_readl(void __iomem *base, int offset) +{ + int value; + + value = readl(base + offset); + return value; +} + +static inline void cnl_sdw_reg_writel(void __iomem *base, int offset, int value) +{ + writel(value, base + offset); +} + +static inline u16 cnl_sdw_reg_readw(void __iomem *base, int offset) +{ + int value; + + value = readw(base + offset); + return value; +} + +static inline void cnl_sdw_reg_writew(void __iomem *base, int offset, u16 value) +{ + writew(value, base + offset); +} + +static inline int cnl_sdw_port_reg_readl(void __iomem *base, int offset, + int port_num) +{ + return cnl_sdw_reg_readl(base, offset + port_num * 128); +} + +static inline void cnl_sdw_port_reg_writel(u32 __iomem *base, int offset, + int port_num, int value) +{ + return cnl_sdw_reg_writel(base, offset + port_num * 128, value); +} + +struct cnl_sdw { + struct cnl_sdw_data data; + struct sdw_master *mstr; + irqreturn_t (*thread)(int irq, void *context); + void *thread_context; + struct completion tx_complete; + struct cnl_sdw_port port[CNL_SDW_MAX_PORTS]; + int num_pcm_streams; + struct cnl_sdw_pdi_stream *pcm_streams; + int num_in_pcm_streams; + struct cnl_sdw_pdi_stream *in_pcm_streams; + int num_out_pcm_streams; + struct cnl_sdw_pdi_stream *out_pcm_streams; + int num_pdm_streams; + struct cnl_sdw_pdi_stream *pdm_streams; + int num_in_pdm_streams; + struct cnl_sdw_pdi_stream *in_pdm_streams; + int num_out_pdm_streams; + struct cnl_sdw_pdi_stream *out_pdm_streams; + struct mutex stream_lock; + spinlock_t ctrl_lock; + u32 response_buf[0x80]; + bool sdw_link_status; + +}; + +static int sdw_power_up_link(struct cnl_sdw *sdw) +{ + volatile int link_control; + struct sdw_master *mstr = sdw->mstr; + struct cnl_sdw_data *data = &sdw->data; + /* Try 10 times before timing out */ + int timeout = 10; + int spa_mask, cpa_mask; + + link_control = cnl_sdw_reg_readl(data->sdw_shim, SDW_CNL_LCTL); + spa_mask = (CNL_LCTL_SPA_MASK << (data->inst_id + CNL_LCTL_SPA_SHIFT)); + cpa_mask = (CNL_LCTL_CPA_MASK << (data->inst_id + CNL_LCTL_CPA_SHIFT)); + link_control |= spa_mask; + cnl_sdw_reg_writel(data->sdw_shim, SDW_CNL_LCTL, link_control); + do { + link_control = cnl_sdw_reg_readl(data->sdw_shim, SDW_CNL_LCTL); + if (link_control & cpa_mask) + break; + timeout--; + /* Wait 20ms before each time */ + msleep(20); + } while (timeout != 0); + /* Read once again to confirm */ + link_control = cnl_sdw_reg_readl(data->sdw_shim, SDW_CNL_LCTL); + if (link_control & cpa_mask) { + dev_info(&mstr->dev, "SoundWire ctrl %d Powered Up\n", + data->inst_id); + sdw->sdw_link_status = 1; + return 0; + } + dev_err(&mstr->dev, "Failed to Power Up the SDW ctrl %d\n", + data->inst_id); + return -EIO; +} + +static void sdw_power_down_link(struct cnl_sdw *sdw) +{ + volatile int link_control; + struct sdw_master *mstr = sdw->mstr; + struct cnl_sdw_data *data = &sdw->data; + /* Retry 10 times before giving up */ + int timeout = 10; + int spa_mask, cpa_mask; + + link_control = cnl_sdw_reg_readl(data->sdw_shim, SDW_CNL_LCTL); + spa_mask = ~(CNL_LCTL_SPA_MASK << (data->inst_id + CNL_LCTL_SPA_SHIFT)); + cpa_mask = (CNL_LCTL_CPA_MASK << (data->inst_id + CNL_LCTL_CPA_SHIFT)); + link_control &= spa_mask; + cnl_sdw_reg_writel(data->sdw_shim, SDW_CNL_LCTL, link_control); + do { + link_control = cnl_sdw_reg_readl(data->sdw_shim, SDW_CNL_LCTL); + if (!(link_control & cpa_mask)) + break; + timeout--; + /* Wait for 20ms before each retry */ + msleep(20); + } while (timeout != 0); + /* Read once again to confirm */ + link_control = cnl_sdw_reg_readl(data->sdw_shim, SDW_CNL_LCTL); + if (!(link_control & cpa_mask)) { + dev_info(&mstr->dev, "SoundWire ctrl %d Powered Down\n", + data->inst_id); + sdw->sdw_link_status = 0; + return; + } + dev_err(&mstr->dev, "Failed to Power Down the SDW ctrl %d\n", + data->inst_id); +} + +static void sdw_init_phyctrl(struct cnl_sdw *sdw) +{ + /* TODO: Initialize based on hardware requirement */ + +} + +static void sdw_switch_to_mip(struct cnl_sdw *sdw) +{ + u16 ioctl; + u16 act = 0; + struct cnl_sdw_data *data = &sdw->data; + int ioctl_offset = SDW_CNL_IOCTL + (data->inst_id * + SDW_CNL_IOCTL_REG_OFFSET); + int act_offset = SDW_CNL_CTMCTL + (data->inst_id * + SDW_CNL_CTMCTL_REG_OFFSET); + + ioctl = cnl_sdw_reg_readw(data->sdw_shim, ioctl_offset); + + ioctl &= ~(CNL_IOCTL_DOE_MASK << CNL_IOCTL_DOE_SHIFT); + cnl_sdw_reg_writew(data->sdw_shim, ioctl_offset, ioctl); + + ioctl &= ~(CNL_IOCTL_DO_MASK << CNL_IOCTL_DO_SHIFT); + cnl_sdw_reg_writew(data->sdw_shim, ioctl_offset, ioctl); + + ioctl |= CNL_IOCTL_MIF_MASK << CNL_IOCTL_MIF_SHIFT; + cnl_sdw_reg_writew(data->sdw_shim, ioctl_offset, ioctl); + + ioctl &= ~(CNL_IOCTL_BKE_MASK << CNL_IOCTL_BKE_SHIFT); + ioctl &= ~(CNL_IOCTL_COE_MASK << CNL_IOCTL_COE_SHIFT); + + cnl_sdw_reg_writew(data->sdw_shim, ioctl_offset, ioctl); + + act |= 0x1 << CNL_CTMCTL_DOAIS_SHIFT; + act |= CNL_CTMCTL_DACTQE_MASK << CNL_CTMCTL_DACTQE_SHIFT; + act |= CNL_CTMCTL_DODS_MASK << CNL_CTMCTL_DODS_SHIFT; + cnl_sdw_reg_writew(data->sdw_shim, act_offset, act); +} + +static void sdw_switch_to_glue(struct cnl_sdw *sdw) +{ + u16 ioctl; + struct cnl_sdw_data *data = &sdw->data; + int ioctl_offset = SDW_CNL_IOCTL + (data->inst_id * + SDW_CNL_IOCTL_REG_OFFSET); + + ioctl = cnl_sdw_reg_readw(data->sdw_shim, ioctl_offset); + ioctl |= CNL_IOCTL_BKE_MASK << CNL_IOCTL_BKE_SHIFT; + ioctl |= CNL_IOCTL_COE_MASK << CNL_IOCTL_COE_SHIFT; + cnl_sdw_reg_writew(data->sdw_shim, ioctl_offset, ioctl); + + ioctl &= ~(CNL_IOCTL_MIF_MASK << CNL_IOCTL_MIF_SHIFT); + cnl_sdw_reg_writew(data->sdw_shim, ioctl_offset, ioctl); +} + +static void sdw_init_shim(struct cnl_sdw *sdw) +{ + u16 ioctl = 0; + struct cnl_sdw_data *data = &sdw->data; + int ioctl_offset = SDW_CNL_IOCTL + (data->inst_id * + SDW_CNL_IOCTL_REG_OFFSET); + + + ioctl |= CNL_IOCTL_BKE_MASK << CNL_IOCTL_BKE_SHIFT; + cnl_sdw_reg_writew(data->sdw_shim, ioctl_offset, ioctl); + + ioctl |= CNL_IOCTL_WPDD_MASK << CNL_IOCTL_WPDD_SHIFT; + cnl_sdw_reg_writew(data->sdw_shim, ioctl_offset, ioctl); + + ioctl |= CNL_IOCTL_DO_MASK << CNL_IOCTL_DO_SHIFT; + cnl_sdw_reg_writew(data->sdw_shim, ioctl_offset, ioctl); + + ioctl |= CNL_IOCTL_DOE_MASK << CNL_IOCTL_DOE_SHIFT; + cnl_sdw_reg_writew(data->sdw_shim, ioctl_offset, ioctl); +} + +static int sdw_config_update(struct cnl_sdw *sdw) +{ + struct cnl_sdw_data *data = &sdw->data; + struct sdw_master *mstr = sdw->mstr; + + volatile int config_update = 0; + /* Try 10 times before giving up on configuration update */ + int timeout = 10; + int config_updated = 0; + + config_update |= MCP_CONFIGUPDATE_CONFIGUPDATE_MASK << + MCP_CONFIGUPDATE_CONFIGUPDATE_SHIFT; + /* Bit is self-cleared when configuration gets updated. */ + cnl_sdw_reg_writel(data->sdw_regs, SDW_CNL_MCP_CONFIGUPDATE, + config_update); + do { + config_update = cnl_sdw_reg_readl(data->sdw_regs, + SDW_CNL_MCP_CONFIGUPDATE); + if ((config_update & + MCP_CONFIGUPDATE_CONFIGUPDATE_MASK) == 0) { + config_updated = 1; + break; + } + timeout--; + /* Wait for 20ms between each try */ + msleep(20); + + } while (timeout != 0); + if (!config_updated) { + dev_err(&mstr->dev, "SoundWire update failed\n"); + return -EIO; + } + return 0; +} + +static void sdw_enable_interrupt(struct cnl_sdw *sdw) +{ + struct cnl_sdw_data *data = &sdw->data; + int int_mask = 0; + + cnl_sdw_reg_writel(data->sdw_regs, SDW_CNL_MCP_SLAVEINTMASK0, + MCP_SLAVEINTMASK0_MASK); + cnl_sdw_reg_writel(data->sdw_regs, SDW_CNL_MCP_SLAVEINTMASK1, + MCP_SLAVEINTMASK1_MASK); + /* Enable slave interrupt mask */ + int_mask |= MCP_INTMASK_SLAVERESERVED_MASK << + MCP_INTMASK_SLAVERESERVED_SHIFT; + int_mask |= MCP_INTMASK_SLAVEALERT_MASK << + MCP_INTMASK_SLAVEALERT_SHIFT; + int_mask |= MCP_INTMASK_SLAVEATTACHED_MASK << + MCP_INTMASK_SLAVEATTACHED_SHIFT; + int_mask |= MCP_INTMASK_SLAVENOTATTACHED_MASK << + MCP_INTMASK_SLAVENOTATTACHED_SHIFT; + int_mask |= MCP_INTMASK_CONTROLBUSCLASH_MASK << + MCP_INTMASK_CONTROLBUSCLASH_SHIFT; + int_mask |= MCP_INTMASK_DATABUSCLASH_MASK << + MCP_INTMASK_DATABUSCLASH_SHIFT; + int_mask |= MCP_INTMASK_RXWL_MASK << + MCP_INTMASK_RXWL_SHIFT; + int_mask |= MCP_INTMASK_IRQEN_MASK << + MCP_INTMASK_IRQEN_SHIFT; + int_mask |= MCP_INTMASK_DPPDIINT_MASK << + MCP_INTMASK_DPPDIINT_SHIFT; + cnl_sdw_reg_writel(data->sdw_regs, SDW_CNL_MCP_INTMASK, int_mask); +} + +static int sdw_pcm_pdi_init(struct cnl_sdw *sdw) +{ + struct sdw_master *mstr = sdw->mstr; + struct cnl_sdw_data *data = &sdw->data; + int pcm_cap; + int pcm_cap_offset = SDW_CNL_PCMSCAP + (data->inst_id * + SDW_CNL_PCMSCAP_REG_OFFSET); + int ch_cnt_offset; + int i; + + pcm_cap = cnl_sdw_reg_readw(data->sdw_shim, pcm_cap_offset); + sdw->num_pcm_streams = (pcm_cap >> CNL_PCMSCAP_BSS_SHIFT) & + CNL_PCMSCAP_BSS_MASK; + dev_info(&mstr->dev, "Number of Bidirectional PCM stream = %d\n", + sdw->num_pcm_streams); + sdw->pcm_streams = devm_kzalloc(&mstr->dev, + sdw->num_pcm_streams * sizeof(struct cnl_sdw_pdi_stream), + GFP_KERNEL); + if (!sdw->pcm_streams) + return -ENOMEM; + /* Two of the PCM streams are reserved for bulk transfers */ + sdw->pcm_streams -= SDW_CNL_PCM_PDI_NUM_OFFSET; + for (i = SDW_CNL_PCM_PDI_NUM_OFFSET; i < sdw->num_pcm_streams; i++) { + ch_cnt_offset = SDW_CNL_PCMSCHC + + (data->inst_id * SDW_CNL_PCMSCHC_REG_OFFSET) + + ((i + SDW_CNL_PCM_PDI_NUM_OFFSET) * 0x2); + + sdw->pcm_streams[i].ch_cnt = cnl_sdw_reg_readw(data->sdw_shim, + ch_cnt_offset); + /* Zero based value in register */ + sdw->pcm_streams[i].ch_cnt++; + sdw->pcm_streams[i].pdi_num = i; + sdw->pcm_streams[i].allocated = false; + dev_info(&mstr->dev, "CH Count for stream %d is %d\n", + i, sdw->pcm_streams[i].ch_cnt); + } + return 0; +} + +static int sdw_pdm_pdi_init(struct cnl_sdw *sdw) +{ + int i; + struct sdw_master *mstr = sdw->mstr; + struct cnl_sdw_data *data = &sdw->data; + int pdm_cap, pdm_ch_count, total_pdm_streams; + int pdm_cap_offset = SDW_CNL_PDMSCAP + + (data->inst_id * SDW_CNL_PDMSCAP_REG_OFFSET); + + pdm_cap = cnl_sdw_reg_readw(data->sdw_regs, pdm_cap_offset); + sdw->num_pdm_streams = (pdm_cap >> CNL_PDMSCAP_BSS_SHIFT) & + CNL_PDMSCAP_BSS_MASK; + /* Zero based value in register */ + sdw->num_pdm_streams++; + sdw->pdm_streams = devm_kzalloc(&mstr->dev, + sdw->num_pdm_streams * sizeof(struct cnl_sdw_pdi_stream), + GFP_KERNEL); + if (!sdw->pdm_streams) + return -ENOMEM; + + sdw->num_in_pdm_streams = (pdm_cap >> CNL_PDMSCAP_ISS_SHIFT) & + CNL_PDMSCAP_ISS_MASK; + /* Zero based value in register */ + sdw->num_in_pdm_streams++; + sdw->in_pdm_streams = devm_kzalloc(&mstr->dev, + sdw->num_in_pdm_streams * sizeof(struct cnl_sdw_pdi_stream), + GFP_KERNEL); + + if (!sdw->in_pdm_streams) + return -ENOMEM; + + sdw->num_out_pdm_streams = (pdm_cap >> CNL_PDMSCAP_OSS_SHIFT) & + CNL_PDMSCAP_OSS_MASK; + /* Zero based value in register */ + sdw->num_out_pdm_streams++; + sdw->out_pdm_streams = devm_kzalloc(&mstr->dev, + sdw->num_out_pdm_streams * sizeof(struct cnl_sdw_pdi_stream), + GFP_KERNEL); + if (!sdw->out_pdm_streams) + return -ENOMEM; + + total_pdm_streams = sdw->num_pdm_streams + + sdw->num_in_pdm_streams + + sdw->num_out_pdm_streams; + + pdm_ch_count = (pdm_cap >> CNL_PDMSCAP_CPSS_SHIFT) & + CNL_PDMSCAP_CPSS_MASK; + for (i = 0; i < sdw->num_pdm_streams; i++) { + sdw->pdm_streams[i].ch_cnt = pdm_ch_count; + sdw->pdm_streams[i].pdi_num = i + SDW_CNL_PDM_PDI_NUM_OFFSET; + sdw->pdm_streams[i].allocated = false; + } + for (i = 0; i < sdw->num_in_pdm_streams; i++) { + sdw->in_pdm_streams[i].ch_cnt = pdm_ch_count; + sdw->in_pdm_streams[i].pdi_num = i + SDW_CNL_PDM_PDI_NUM_OFFSET; + sdw->in_pdm_streams[i].allocated = false; + } + for (i = 0; i < sdw->num_out_pdm_streams; i++) { + sdw->out_pdm_streams[i].ch_cnt = pdm_ch_count; + sdw->out_pdm_streams[i].pdi_num = + i + SDW_CNL_PDM_PDI_NUM_OFFSET; + sdw->out_pdm_streams[i].allocated = false; + } + return 0; +} + +static int sdw_port_pdi_init(struct cnl_sdw *sdw) +{ + int i, ret = 0; + + for (i = 0; i < CNL_SDW_MAX_PORTS; i++) { + sdw->port[i].port_num = i; + sdw->port[i].allocated = false; + } + ret = sdw_pcm_pdi_init(sdw); + if (ret) + return ret; + ret = sdw_pdm_pdi_init(sdw); + + return ret; +} + +static int sdw_init(struct cnl_sdw *sdw) +{ + struct sdw_master *mstr = sdw->mstr; + struct cnl_sdw_data *data = &sdw->data; + int mcp_config, mcp_control; + int ret = 0; + + /* Power up the link controller */ + ret = sdw_power_up_link(sdw); + if (ret) + return ret; + + /* Initialize the IO control registers */ + sdw_init_shim(sdw); + + /* Switch the ownership to Master IP from glue logic */ + sdw_switch_to_mip(sdw); + + /* Set command acceptance mode. This is required because when + * Master broadcasts the clock_stop command to slaves, slaves + * might be already suspended, so this return NO ACK, in that + * case also master should go to clock stop mode. + */ + mcp_control = cnl_sdw_reg_readl(data->sdw_regs, + SDW_CNL_MCP_CONTROL); + mcp_control |= (MCP_CONTROL_CMDACCEPTMODE_MASK << + MCP_CONTROL_CMDACCEPTMODE_SHIFT); + cnl_sdw_reg_writel(data->sdw_regs, SDW_CNL_MCP_CONTROL, mcp_control); + + cnl_sdw_reg_writel(data->sdw_regs, SDW_CNL_MCP_FRAMESHAPEINIT, 0x48); + + mcp_config = cnl_sdw_reg_readl(data->sdw_regs, SDW_CNL_MCP_CONFIG); + /* Set Max cmd retry to 15 times */ + mcp_config |= (CNL_SDW_MAX_CMD_RETRIES << + MCP_CONFIG_MAXCMDRETRY_SHIFT); + + /* Set Ping request to ping delay to 15 frames. + * Spec supports 32 max frames + */ + mcp_config |= (CNL_SDW_MAX_PREQ_DELAY << + MCP_CONFIG_MAXPREQDELAY_SHIFT); + + /* If master is synchronized to some other master set Multimode */ + if (mstr->link_sync_mask) { + mcp_config |= (MCP_CONFIG_MMMODEEN_MASK << + MCP_CONFIG_MMMODEEN_SHIFT); + mcp_config |= (MCP_CONFIG_SSPMODE_MASK << + MCP_CONFIG_SSPMODE_SHIFT); + } else { + mcp_config &= ~(MCP_CONFIG_MMMODEEN_MASK << + MCP_CONFIG_MMMODEEN_SHIFT); + mcp_config &= ~(MCP_CONFIG_SSPMODE_MASK << + MCP_CONFIG_SSPMODE_SHIFT); + } + + /* Disable automatic bus release */ + mcp_config &= ~(MCP_CONFIG_BRELENABLE_MASK << + MCP_CONFIG_BRELENABLE_SHIFT); + + /* Disable sniffer mode now */ + mcp_config &= ~(MCP_CONFIG_SNIFFEREN_MASK << + MCP_CONFIG_SNIFFEREN_SHIFT); + + /* Set the command mode for Tx and Rx command */ + mcp_config &= ~(MCP_CONFIG_CMDMODE_MASK << + MCP_CONFIG_CMDMODE_SHIFT); + + /* Set operation mode to normal */ + mcp_config &= ~(MCP_CONFIG_OPERATIONMODE_MASK << + MCP_CONFIG_OPERATIONMODE_SHIFT); + mcp_config |= ((MCP_CONFIG_OPERATIONMODE_NORMAL & + MCP_CONFIG_OPERATIONMODE_MASK) << + MCP_CONFIG_OPERATIONMODE_SHIFT); + + cnl_sdw_reg_writel(data->sdw_regs, SDW_CNL_MCP_CONFIG, mcp_config); + /* Set the SSP interval to 32 for both banks */ + cnl_sdw_reg_writel(data->sdw_regs, SDW_CNL_MCP_SSPCTRL0, + SDW_CNL_DEFAULT_SSP_INTERVAL); + cnl_sdw_reg_writel(data->sdw_regs, SDW_CNL_MCP_SSPCTRL1, + SDW_CNL_DEFAULT_SSP_INTERVAL); + + /* Initialize the phy control registers. */ + sdw_init_phyctrl(sdw); + + /* Initlaize the ports */ + ret = sdw_port_pdi_init(sdw); + if (ret) { + dev_err(&mstr->dev, "SoundWire controller init failed %d\n", + data->inst_id); + sdw_power_down_link(sdw); + return ret; + } + + /* Lastly enable interrupts */ + sdw_enable_interrupt(sdw); + + /* Update soundwire configuration */ + return sdw_config_update(sdw); +} + +static int sdw_alloc_pcm_stream(struct cnl_sdw *sdw, + struct cnl_sdw_port *port, int ch_cnt, + enum sdw_data_direction direction) +{ + int num_pcm_streams, pdi_ch_map = 0, stream_id; + struct cnl_sdw_pdi_stream *stream, *pdi_stream; + unsigned int i; + unsigned int ch_map_offset, port_ctrl_offset, pdi_config_offset; + struct sdw_master *mstr = sdw->mstr; + unsigned int port_ctrl = 0, pdi_config = 0, channel_mask; + unsigned int stream_config; + + /* Currently PCM supports only bi-directional streams only */ + num_pcm_streams = sdw->num_pcm_streams; + stream = sdw->pcm_streams; + + mutex_lock(&sdw->stream_lock); + for (i = SDW_CNL_PCM_PDI_NUM_OFFSET; i < num_pcm_streams; i++) { + if (stream[i].allocated == false) { + stream[i].allocated = true; + stream[i].port_num = port->port_num; + port->pdi_stream = &stream[i]; + break; + } + } + mutex_unlock(&sdw->stream_lock); + if (!port->pdi_stream) { + dev_err(&mstr->dev, "Unable to allocate stream for PCM\n"); + return -EINVAL; + } + pdi_stream = port->pdi_stream; + /* We didnt get enough PDI streams, so free the allocated + * PDI streams. Free the port as well and return with error + */ + pdi_stream->l_ch_num = 0; + pdi_stream->h_ch_num = ch_cnt - 1; + ch_map_offset = SDW_CNL_PCMSCHM + + (SDW_CNL_PCMSCHM_REG_OFFSET * mstr->nr) + + (0x2 * pdi_stream->pdi_num); + if (port->direction == SDW_DATA_DIR_IN) + pdi_ch_map |= (CNL_PCMSYCM_DIR_MASK << CNL_PCMSYCM_DIR_SHIFT); + else + pdi_ch_map &= ~(CNL_PCMSYCM_DIR_MASK << CNL_PCMSYCM_DIR_SHIFT); + /* TODO: Remove this hardcoding */ + stream_id = mstr->nr * 16 + pdi_stream->pdi_num + 5; + pdi_stream->sdw_pdi_num = stream_id; + pdi_ch_map |= (stream_id & CNL_PCMSYCM_STREAM_MASK) << + CNL_PCMSYCM_STREAM_SHIFT; + pdi_ch_map |= (pdi_stream->l_ch_num & + CNL_PCMSYCM_LCHAN_MASK) << + CNL_PCMSYCM_LCHAN_SHIFT; + pdi_ch_map |= (0xF & CNL_PCMSYCM_HCHAN_MASK) << + CNL_PCMSYCM_HCHAN_SHIFT; + cnl_sdw_reg_writew(sdw->data.sdw_shim, ch_map_offset, + pdi_ch_map); + /* If direction is input, port is sink port*/ + if (direction == SDW_DATA_DIR_IN) + port_ctrl |= (PORTCTRL_PORT_DIRECTION_MASK << + PORTCTRL_PORT_DIRECTION_SHIFT); + else + port_ctrl &= ~(PORTCTRL_PORT_DIRECTION_MASK << + PORTCTRL_PORT_DIRECTION_SHIFT); + + port_ctrl_offset = SDW_CNL_PORTCTRL + (port->port_num * + SDW_CNL_PORT_REG_OFFSET); + cnl_sdw_reg_writel(sdw->data.sdw_regs, port_ctrl_offset, port_ctrl); + + pdi_config |= ((port->port_num & PDINCONFIG_PORT_NUMBER_MASK) << + PDINCONFIG_PORT_NUMBER_SHIFT); + + channel_mask = (1 << ch_cnt) - 1; + pdi_config |= (channel_mask << PDINCONFIG_CHANNEL_MASK_SHIFT); + /* TODO: Remove below hardcodings */ + pdi_config_offset = (SDW_CNL_PDINCONFIG0 + + (pdi_stream->pdi_num * 16)); + cnl_sdw_reg_writel(sdw->data.sdw_regs, pdi_config_offset, pdi_config); + + stream_config = cnl_sdw_reg_readl(sdw->data.alh_base, + (pdi_stream->sdw_pdi_num * ALH_CNL_STRMZCFG_OFFSET)); + stream_config |= (CNL_STRMZCFG_DMAT_VAL & CNL_STRMZCFG_DMAT_MASK) << + CNL_STRMZCFG_DMAT_SHIFT; + stream_config |= ((ch_cnt - 1) & CNL_STRMZCFG_CHAN_MASK) << + CNL_STRMZCFG_CHAN_SHIFT; + cnl_sdw_reg_writel(sdw->data.alh_base, + (pdi_stream->sdw_pdi_num * ALH_CNL_STRMZCFG_OFFSET), + stream_config); + return 0; +} + +static int sdw_alloc_pdm_stream(struct cnl_sdw *sdw, + struct cnl_sdw_port *port, int ch_cnt, int direction) +{ + int num_pdm_streams; + struct cnl_sdw_pdi_stream *stream; + int i; + unsigned int port_ctrl_offset, pdi_config_offset; + unsigned int port_ctrl = 0, pdi_config = 0, channel_mask; + + /* Currently PDM supports either Input or Output Streams */ + if (direction == SDW_DATA_DIR_IN) { + num_pdm_streams = sdw->num_in_pdm_streams; + stream = sdw->in_pdm_streams; + } else { + num_pdm_streams = sdw->num_out_pdm_streams; + stream = sdw->out_pdm_streams; + } + mutex_lock(&sdw->stream_lock); + for (i = 0; i < num_pdm_streams; i++) { + if (stream[i].allocated == false) { + stream[i].allocated = true; + stream[i].port_num = port->port_num; + port->pdi_stream = &stream[i]; + break; + } + } + mutex_unlock(&sdw->stream_lock); + if (!port->pdi_stream) + return -EINVAL; + /* If direction is input, port is sink port*/ + if (direction == SDW_DATA_DIR_IN) + port_ctrl |= (PORTCTRL_PORT_DIRECTION_MASK << + PORTCTRL_PORT_DIRECTION_SHIFT); + else + port_ctrl &= ~(PORTCTRL_PORT_DIRECTION_MASK << + PORTCTRL_PORT_DIRECTION_SHIFT); + + port_ctrl_offset = SDW_CNL_PORTCTRL + (port->port_num * + SDW_CNL_PORT_REG_OFFSET); + cnl_sdw_reg_writel(sdw->data.sdw_regs, port_ctrl_offset, port_ctrl); + + pdi_config |= ((port->port_num & PDINCONFIG_PORT_NUMBER_MASK) << + PDINCONFIG_PORT_NUMBER_SHIFT); + + channel_mask = (1 << ch_cnt) - 1; + pdi_config |= (channel_mask << PDINCONFIG_CHANNEL_MASK_SHIFT); + /* TODO: Remove below hardcodings */ + pdi_config_offset = (SDW_CNL_PDINCONFIG0 + (stream[i].pdi_num * 16)); + cnl_sdw_reg_writel(sdw->data.sdw_regs, pdi_config_offset, pdi_config); + + return 0; +} + +struct cnl_sdw_port *cnl_sdw_alloc_port(struct sdw_master *mstr, int ch_count, + enum sdw_data_direction direction, + enum cnl_sdw_pdi_stream_type stream_type) +{ + struct cnl_sdw *sdw; + struct cnl_sdw_port *port = NULL; + int i, ret = 0; + struct num_pdi_streams; + + sdw = sdw_master_get_drvdata(mstr); + + mutex_lock(&sdw->stream_lock); + for (i = 1; i < CNL_SDW_MAX_PORTS; i++) { + if (sdw->port[i].allocated == false) { + port = &sdw->port[i]; + port->allocated = true; + port->direction = direction; + port->ch_cnt = ch_count; + break; + } + } + mutex_unlock(&sdw->stream_lock); + if (!port) { + dev_err(&mstr->dev, "Unable to allocate port\n"); + return NULL; + } + port->pdi_stream = NULL; + if (stream_type == CNL_SDW_PDI_TYPE_PDM) + ret = sdw_alloc_pdm_stream(sdw, port, ch_count, direction); + else + ret = sdw_alloc_pcm_stream(sdw, port, ch_count, direction); + if (!ret) + return port; + + dev_err(&mstr->dev, "Unable to allocate stream\n"); + mutex_lock(&sdw->stream_lock); + port->allocated = false; + mutex_unlock(&sdw->stream_lock); + return NULL; +} +EXPORT_SYMBOL_GPL(cnl_sdw_alloc_port); + +void cnl_sdw_free_port(struct sdw_master *mstr, int port_num) +{ + int i; + struct cnl_sdw *sdw; + struct cnl_sdw_port *port = NULL; + + sdw = sdw_master_get_drvdata(mstr); + for (i = 1; i < CNL_SDW_MAX_PORTS; i++) { + if (sdw->port[i].port_num == port_num) { + port = &sdw->port[i]; + break; + } + } + if (!port) + return; + mutex_lock(&sdw->stream_lock); + port->pdi_stream->allocated = false; + port->pdi_stream = NULL; + port->allocated = false; + mutex_unlock(&sdw->stream_lock); +} +EXPORT_SYMBOL_GPL(cnl_sdw_free_port); + +static int cnl_sdw_update_slave_status(struct cnl_sdw *sdw, int slave_intstat0, + int slave_intstat1) +{ + int i; + struct sdw_status slave_status; + u64 slaves_stat, slave_stat; + int ret = 0; + + memset(&slave_status, 0x0, sizeof(slave_status)); + slaves_stat = (u64) slave_intstat1 << + SDW_CNL_SLAVES_STAT_UPPER_DWORD_SHIFT; + slaves_stat |= slave_intstat0; + for (i = 0; i <= SOUNDWIRE_MAX_DEVICES; i++) { + slave_stat = slaves_stat >> (i * SDW_CNL_SLAVE_STATUS_BITS); + if (slave_stat & MCP_SLAVEINTSTAT_NOT_PRESENT_MASK) + slave_status.status[i] = SDW_SLAVE_STAT_NOT_PRESENT; + else if (slave_stat & MCP_SLAVEINTSTAT_ATTACHED_MASK) + slave_status.status[i] = SDW_SLAVE_STAT_ATTACHED_OK; + else if (slave_stat & MCP_SLAVEINTSTAT_ALERT_MASK) + slave_status.status[i] = SDW_SLAVE_STAT_ALERT; + else if (slave_stat & MCP_SLAVEINTSTAT_RESERVED_MASK) + slave_status.status[i] = SDW_SLAVE_STAT_RESERVED; + } + ret = sdw_master_update_slv_status(sdw->mstr, &slave_status); + return ret; +} + +static void cnl_sdw_read_response(struct cnl_sdw *sdw) +{ + struct cnl_sdw_data *data = &sdw->data; + int num_res = 0, i; + u32 cmd_base = SDW_CNL_MCP_COMMAND_BASE; + + num_res = cnl_sdw_reg_readl(data->sdw_regs, SDW_CNL_MCP_FIFOSTAT); + num_res &= MCP_RX_FIFO_AVAIL_MASK; + for (i = 0; i < num_res; i++) { + sdw->response_buf[i] = cnl_sdw_reg_readl(data->sdw_regs, + cmd_base); + cmd_base += SDW_CNL_CMD_WORD_LEN; + } +} + +irqreturn_t cnl_sdw_irq_handler(int irq, void *context) +{ + struct cnl_sdw *sdw = context; + volatile int int_status, status, wake_sts; + + struct cnl_sdw_data *data = &sdw->data; + volatile int slave_intstat0 = 0, slave_intstat1 = 0; + struct sdw_master *mstr = sdw->mstr; + + /* + * Return if IP is in power down state. Interrupt can still come + * since its shared irq. + */ + if (!sdw->sdw_link_status) + return IRQ_NONE; + + int_status = cnl_sdw_reg_readl(data->sdw_regs, SDW_CNL_MCP_INTSTAT); + status = cnl_sdw_reg_readl(data->sdw_regs, SDW_CNL_MCP_STAT); + slave_intstat0 = cnl_sdw_reg_readl(data->sdw_regs, + SDW_CNL_MCP_SLAVEINTSTAT0); + slave_intstat1 = cnl_sdw_reg_readl(data->sdw_regs, + SDW_CNL_MCP_SLAVEINTSTAT1); + wake_sts = cnl_sdw_reg_readw(data->sdw_shim, + SDW_CNL_SNDWWAKESTS_REG_OFFSET); + cnl_sdw_reg_writew(data->sdw_shim, SDW_CNL_SNDWWAKESTS_REG_OFFSET, + wake_sts); + + if (!(int_status & (MCP_INTSTAT_IRQ_MASK << MCP_INTSTAT_IRQ_SHIFT))) + return IRQ_NONE; + + if (int_status & (MCP_INTSTAT_RXWL_MASK << MCP_INTSTAT_RXWL_SHIFT)) { + cnl_sdw_read_response(sdw); + complete(&sdw->tx_complete); + } + if (int_status & (MCP_INTSTAT_CONTROLBUSCLASH_MASK << + MCP_INTSTAT_CONTROLBUSCLASH_SHIFT)) { + /* Some slave is behaving badly, where its driving + * data line during control word bits. + */ + dev_err_ratelimited(&mstr->dev, "Bus clash detected for control word\n"); + WARN_ONCE(1, "Bus clash detected for control word\n"); + } + if (int_status & (MCP_INTSTAT_DATABUSCLASH_MASK << + MCP_INTSTAT_DATABUSCLASH_SHIFT)) { + /* More than 1 slave is trying to drive bus. There is + * some problem with ownership of bus data bits, + * or either of the + * slave is behaving badly. + */ + dev_err_ratelimited(&mstr->dev, "Bus clash detected for control word\n"); + WARN_ONCE(1, "Bus clash detected for data word\n"); + } + + if (int_status & (MCP_INTSTAT_SLAVE_STATUS_CHANGED_MASK << + MCP_INTSTAT_SLAVE_STATUS_CHANGED_SHIFT)) { + dev_info(&mstr->dev, "Slave status change\n"); + cnl_sdw_update_slave_status(sdw, slave_intstat0, + slave_intstat1); + } + cnl_sdw_reg_writel(data->sdw_regs, SDW_CNL_MCP_SLAVEINTSTAT0, + slave_intstat0); + cnl_sdw_reg_writel(data->sdw_regs, SDW_CNL_MCP_SLAVEINTSTAT1, + slave_intstat1); + cnl_sdw_reg_writel(data->sdw_regs, SDW_CNL_MCP_INTSTAT, int_status); + return IRQ_HANDLED; +} + +static enum sdw_command_response cnl_program_scp_addr(struct sdw_master *mstr, + struct sdw_msg *msg) +{ + struct cnl_sdw *sdw = sdw_master_get_drvdata(mstr); + struct cnl_sdw_data *data = &sdw->data; + u32 cmd_base = SDW_CNL_MCP_COMMAND_BASE; + u32 cmd_data[2] = {0, 0}; + unsigned long time_left; + int no_ack = 0, nack = 0; + int i; + + /* Since we are programming 2 commands, program the + * RX watermark level at 2 + */ + cnl_sdw_reg_writel(data->sdw_regs, SDW_CNL_MCP_FIFOLEVEL, 2); + /* Program device address */ + cmd_data[0] |= (msg->slave_addr & MCP_COMMAND_DEV_ADDR_MASK) << + MCP_COMMAND_DEV_ADDR_SHIFT; + /* Write command to program the scp_addr1 register */ + cmd_data[0] |= (0x3 << MCP_COMMAND_COMMAND_SHIFT); + cmd_data[1] = cmd_data[0]; + /* scp_addr1 register address */ + cmd_data[0] |= (SDW_SCP_ADDRPAGE1 << MCP_COMMAND_REG_ADDR_L_SHIFT); + cmd_data[1] |= (SDW_SCP_ADDRPAGE2 << MCP_COMMAND_REG_ADDR_L_SHIFT); + cmd_data[0] |= msg->addr_page1; + cmd_data[1] |= msg->addr_page2; + + cnl_sdw_reg_writel(data->sdw_regs, cmd_base, cmd_data[0]); + cmd_base += SDW_CNL_CMD_WORD_LEN; + cnl_sdw_reg_writel(data->sdw_regs, cmd_base, cmd_data[1]); + + time_left = wait_for_completion_timeout(&sdw->tx_complete, + 3000); + if (!time_left) { + dev_err(&mstr->dev, "Controller Timed out\n"); + msg->len = 0; + return -ETIMEDOUT; + } + + for (i = 0; i < CNL_SDW_SCP_ADDR_REGS; i++) { + if (!(MCP_RESPONSE_ACK_MASK & sdw->response_buf[i])) { + no_ack = 1; + dev_err(&mstr->dev, "Ack not recevied\n"); + if ((MCP_RESPONSE_NACK_MASK & sdw->response_buf[i])) { + nack = 1; + dev_err(&mstr->dev, "NACK recevied\n"); + } + } + } + /* We dont return error if NACK or No ACK detected for broadcast addr + * because some slave might support SCP addr, while some slaves may not + * support it. This is not correct, since we wont be able to find out + * if NACK is detected because of slave not supporting SCP_addrpage or + * its a genuine NACK because of bus errors. We are not sure what slaves + * will report, NACK or No ACK for the scp_addrpage programming if they + * dont support it. Spec is not clear about this. + * This needs to be thought through + */ + if (nack & (msg->slave_addr != 15)) { + dev_err(&mstr->dev, "SCP_addrpage write NACKed for slave %d\n", msg->slave_addr); + return -EREMOTEIO; + } else if (no_ack && (msg->slave_addr != 15)) { + dev_err(&mstr->dev, "SCP_addrpage write ignored for slave %d\n", msg->slave_addr); + return -EREMOTEIO; + } else + return 0; + +} + +static enum sdw_command_response sdw_xfer_msg(struct sdw_master *mstr, + struct sdw_msg *msg, int cmd, int offset, int count) +{ + struct cnl_sdw *sdw = sdw_master_get_drvdata(mstr); + struct cnl_sdw_data *data = &sdw->data; + int i, j; + u32 cmd_base = SDW_CNL_MCP_COMMAND_BASE; + u32 response_base = SDW_CNL_MCP_RESPONSE_BASE; + u32 cmd_data = 0, response_data; + unsigned long time_left; + int no_ack = 0, nack = 0; + u16 addr = msg->addr; + + /* Program the watermark level upto number of count */ + cnl_sdw_reg_writel(data->sdw_regs, SDW_CNL_MCP_FIFOLEVEL, count); + + cmd_base = SDW_CNL_MCP_COMMAND_BASE; + for (j = 0; j < count; j++) { + /* Program device address */ + cmd_data = 0; + cmd_data |= (msg->slave_addr & + MCP_COMMAND_DEV_ADDR_MASK) << + MCP_COMMAND_DEV_ADDR_SHIFT; + /* Program read/write command */ + cmd_data |= (cmd << MCP_COMMAND_COMMAND_SHIFT); + /* program incrementing address register */ + cmd_data |= (addr++ << MCP_COMMAND_REG_ADDR_L_SHIFT); + /* Program the data if write command */ + if (msg->flag == SDW_MSG_FLAG_WRITE) + cmd_data |= + msg->buf[j + offset]; + + cmd_data |= ((msg->ssp_tag & + MCP_COMMAND_SSP_TAG_MASK) << + MCP_COMMAND_SSP_TAG_SHIFT); + cnl_sdw_reg_writel(data->sdw_regs, + cmd_base, cmd_data); + cmd_base += SDW_CNL_CMD_WORD_LEN; + } + /* Wait for 3 second for timeout */ + time_left = wait_for_completion_timeout(&sdw->tx_complete, 3 * HZ); + if (!time_left) { + dev_err(&mstr->dev, "Controller timedout\n"); + msg->len = 0; + return -ETIMEDOUT; + } + for (i = 0; i < count; i++) { + if (!(MCP_RESPONSE_ACK_MASK & sdw->response_buf[i])) { + no_ack = 1; + dev_err(&mstr->dev, "Ack not recevied\n"); + if ((MCP_RESPONSE_NACK_MASK & + sdw->response_buf[i])) { + nack = 1; + dev_err(&mstr->dev, "NACK recevied\n"); + } + } + break; + } + if (nack) { + dev_err(&mstr->dev, "Nack detected for slave %d\n", msg->slave_addr); + msg->len = 0; + return -EREMOTEIO; + } else if (no_ack) { + dev_err(&mstr->dev, "Command ignored for slave %d\n", msg->slave_addr); + msg->len = 0; + return -EREMOTEIO; + } + if (msg->flag == SDW_MSG_FLAG_WRITE) + return 0; + /* Response and Command has same base address */ + response_base = SDW_CNL_MCP_COMMAND_BASE; + for (j = 0; j < count; j++) { + response_data = cnl_sdw_reg_readl(data->sdw_regs, + cmd_base); + msg->buf[j + offset] = + (sdw->response_buf[j] >> MCP_RESPONSE_RDATA_SHIFT); + cmd_base += 4; + } + return 0; +} + +static enum sdw_command_response cnl_sdw_xfer_msg(struct sdw_master *mstr, + struct sdw_msg *msg, bool program_scp_addr_page) +{ + int i, ret = 0, cmd; + + if (program_scp_addr_page) + ret = cnl_program_scp_addr(mstr, msg); + + if (ret) { + msg->len = 0; + return ret; + } + + switch (msg->flag) { + case SDW_MSG_FLAG_READ: + cmd = 0x2; + break; + case SDW_MSG_FLAG_WRITE: + cmd = 0x3; + break; + default: + dev_err(&mstr->dev, "Command not supported\n"); + return -EINVAL; + } + for (i = 0; i < msg->len / SDW_CNL_MCP_COMMAND_LENGTH; i++) { + ret = sdw_xfer_msg(mstr, msg, + cmd, i * SDW_CNL_MCP_COMMAND_LENGTH, + SDW_CNL_MCP_COMMAND_LENGTH); + if (ret < 0) + break; + } + if (!(msg->len % SDW_CNL_MCP_COMMAND_LENGTH)) + return ret; + ret = sdw_xfer_msg(mstr, msg, cmd, i * SDW_CNL_MCP_COMMAND_LENGTH, + msg->len % SDW_CNL_MCP_COMMAND_LENGTH); + if (ret < 0) + return -EINVAL; + return ret; +} + +static int cnl_sdw_xfer_bulk(struct sdw_master *mstr, + struct sdw_bra_block *block) +{ + return 0; +} + +static int cnl_sdw_mon_handover(struct sdw_master *mstr, + bool enable) +{ + int mcp_config; + struct cnl_sdw *sdw = sdw_master_get_drvdata(mstr); + struct cnl_sdw_data *data = &sdw->data; + + mcp_config = cnl_sdw_reg_readl(data->sdw_regs, SDW_CNL_MCP_CONFIG); + if (enable) + mcp_config |= MCP_CONFIG_BRELENABLE_MASK << + MCP_CONFIG_BRELENABLE_SHIFT; + else + mcp_config &= ~(MCP_CONFIG_BRELENABLE_MASK << + MCP_CONFIG_BRELENABLE_SHIFT); + + cnl_sdw_reg_writel(data->sdw_regs, SDW_CNL_MCP_CONFIG, mcp_config); + return 0; +} + +static int cnl_sdw_set_ssp_interval(struct sdw_master *mstr, + int ssp_interval, int bank) +{ + struct cnl_sdw *sdw = sdw_master_get_drvdata(mstr); + struct cnl_sdw_data *data = &sdw->data; + int sspctrl_offset, check; + + if (bank) + sspctrl_offset = SDW_CNL_MCP_SSPCTRL1; + else + sspctrl_offset = SDW_CNL_MCP_SSPCTRL0; + + cnl_sdw_reg_writel(data->sdw_regs, sspctrl_offset, ssp_interval); + + check = cnl_sdw_reg_readl(data->sdw_regs, sspctrl_offset); + + return 0; +} + +static int cnl_sdw_set_clock_freq(struct sdw_master *mstr, + int cur_clk_freq, int bank) +{ + struct cnl_sdw *sdw = sdw_master_get_drvdata(mstr); + struct cnl_sdw_data *data = &sdw->data; + int mcp_clockctrl_offset, mcp_clockctrl; + + + /* TODO: Retrieve divider value or get value directly from calling + * function + */ + int divider = ((9600000/cur_clk_freq) - 1); + + if (bank) { + mcp_clockctrl_offset = SDW_CNL_MCP_CLOCKCTRL1; + mcp_clockctrl = cnl_sdw_reg_readl(data->sdw_regs, + SDW_CNL_MCP_CLOCKCTRL1); + + } else { + mcp_clockctrl_offset = SDW_CNL_MCP_CLOCKCTRL0; + mcp_clockctrl = cnl_sdw_reg_readl(data->sdw_regs, + SDW_CNL_MCP_CLOCKCTRL0); + } + + mcp_clockctrl |= divider; + + /* Write value here */ + cnl_sdw_reg_writel(data->sdw_regs, mcp_clockctrl_offset, + mcp_clockctrl); + + mcp_clockctrl = cnl_sdw_reg_readl(data->sdw_regs, + mcp_clockctrl_offset); + return 0; +} + +static int cnl_sdw_set_port_params(struct sdw_master *mstr, + struct sdw_port_params *params, int bank) +{ + struct cnl_sdw *sdw = sdw_master_get_drvdata(mstr); + struct cnl_sdw_data *data = &sdw->data; + int dpn_config = 0, dpn_config_offset; + + if (bank) + dpn_config_offset = SDW_CNL_DPN_CONFIG1; + else + dpn_config_offset = SDW_CNL_DPN_CONFIG0; + + dpn_config = cnl_sdw_port_reg_readl(data->sdw_regs, + dpn_config_offset, params->num); + + dpn_config |= (((params->word_length - 1) & DPN_CONFIG_WL_MASK) << + DPN_CONFIG_WL_SHIFT); + dpn_config |= ((params->port_flow_mode & DPN_CONFIG_PF_MODE_MASK) << + DPN_CONFIG_PF_MODE_SHIFT); + dpn_config |= ((params->port_data_mode & DPN_CONFIG_PD_MODE_MASK) << + DPN_CONFIG_PD_MODE_SHIFT); + cnl_sdw_port_reg_writel(data->sdw_regs, + dpn_config_offset, params->num, dpn_config); + + cnl_sdw_port_reg_readl(data->sdw_regs, + dpn_config_offset, params->num); + return 0; +} + +static int cnl_sdw_set_port_transport_params(struct sdw_master *mstr, + struct sdw_transport_params *params, int bank) +{ +struct cnl_sdw *sdw = sdw_master_get_drvdata(mstr); + struct cnl_sdw_data *data = &sdw->data; + + int dpn_config = 0, dpn_config_offset; + int dpn_samplectrl_offset; + int dpn_offsetctrl = 0, dpn_offsetctrl_offset; + int dpn_hctrl = 0, dpn_hctrl_offset; + + if (bank) { + dpn_config_offset = SDW_CNL_DPN_CONFIG1; + dpn_samplectrl_offset = SDW_CNL_DPN_SAMPLECTRL1; + dpn_hctrl_offset = SDW_CNL_DPN_HCTRL1; + dpn_offsetctrl_offset = SDW_CNL_DPN_OFFSETCTRL1; + } else { + dpn_config_offset = SDW_CNL_DPN_CONFIG0; + dpn_samplectrl_offset = SDW_CNL_DPN_SAMPLECTRL0; + dpn_hctrl_offset = SDW_CNL_DPN_HCTRL0; + dpn_offsetctrl_offset = SDW_CNL_DPN_OFFSETCTRL0; + } + dpn_config = cnl_sdw_port_reg_readl(data->sdw_regs, + dpn_config_offset, params->num); + dpn_config |= ((params->blockgroupcontrol & DPN_CONFIG_BGC_MASK) << + DPN_CONFIG_BGC_SHIFT); + dpn_config |= ((params->blockpackingmode & DPN_CONFIG_BPM_MASK) << + DPN_CONFIG_BPM_SHIFT); + + cnl_sdw_port_reg_writel(data->sdw_regs, + dpn_config_offset, params->num, dpn_config); + + cnl_sdw_port_reg_readl(data->sdw_regs, + dpn_config_offset, params->num); + + dpn_offsetctrl |= ((params->offset1 & DPN_OFFSETCTRL0_OF1_MASK) << + DPN_OFFSETCTRL0_OF1_SHIFT); + + dpn_offsetctrl |= ((params->offset2 & DPN_OFFSETCTRL0_OF2_MASK) << + DPN_OFFSETCTRL0_OF2_SHIFT); + + cnl_sdw_port_reg_writel(data->sdw_regs, + dpn_offsetctrl_offset, params->num, dpn_offsetctrl); + + + dpn_hctrl |= ((params->hstart & DPN_HCTRL_HSTART_MASK) << + DPN_HCTRL_HSTART_SHIFT); + dpn_hctrl |= ((params->hstop & DPN_HCTRL_HSTOP_MASK) << + DPN_HCTRL_HSTOP_SHIFT); + dpn_hctrl |= ((params->lanecontrol & DPN_HCTRL_LCONTROL_MASK) << + DPN_HCTRL_LCONTROL_SHIFT); + + cnl_sdw_port_reg_writel(data->sdw_regs, + dpn_hctrl_offset, params->num, dpn_hctrl); + + cnl_sdw_port_reg_writel(data->sdw_regs, + dpn_samplectrl_offset, params->num, + (params->sample_interval - 1)); + + cnl_sdw_port_reg_readl(data->sdw_regs, + dpn_hctrl_offset, params->num); + + cnl_sdw_port_reg_readl(data->sdw_regs, + dpn_samplectrl_offset, params->num); + + return 0; +} + +static int cnl_sdw_port_activate_ch(struct sdw_master *mstr, + struct sdw_activate_ch *activate_ch, int bank) +{ + struct cnl_sdw *sdw = sdw_master_get_drvdata(mstr); + struct cnl_sdw_data *data = &sdw->data; + int dpn_channelen_offset; + int ch_mask; + + if (bank) + dpn_channelen_offset = SDW_CNL_DPN_CHANNELEN1; + else + dpn_channelen_offset = SDW_CNL_DPN_CHANNELEN0; + + if (activate_ch->activate) + ch_mask = activate_ch->ch_mask; + else + ch_mask = 0; + + cnl_sdw_port_reg_writel(data->sdw_regs, + dpn_channelen_offset, activate_ch->num, + ch_mask); + + return 0; +} + +static int cnl_sdw_port_activate_ch_pre(struct sdw_master *mstr, + struct sdw_activate_ch *activate_ch, int bank) +{ + int sync_reg; + struct cnl_sdw *sdw = sdw_master_get_drvdata(mstr); + struct cnl_sdw_data *data = &sdw->data; + + if (mstr->link_sync_mask) { + /* Check if this link is synchronized with some other link */ + sync_reg = cnl_sdw_reg_readl(data->sdw_shim, SDW_CNL_SYNC); + /* If link is synchronized with other link than + * Need to make sure that command doesnt go till + * ssync is applied + */ + sync_reg |= (1 << (data->inst_id + CNL_SYNC_CMDSYNC_SHIFT)); + cnl_sdw_reg_writel(data->sdw_shim, SDW_CNL_SYNC, sync_reg); + } + + return 0; +} +static int cnl_sdw_port_activate_ch_post(struct sdw_master *mstr, + struct sdw_activate_ch *activate_ch, int bank) +{ + int sync_reg; + struct cnl_sdw *sdw = sdw_master_get_drvdata(mstr); + struct cnl_sdw_data *data = &sdw->data; + + sync_reg = cnl_sdw_reg_readl(data->sdw_shim, SDW_CNL_SYNC); + sync_reg |= CNL_SYNC_SYNCGO_MASK << CNL_SYNC_SYNCGO_SHIFT; + cnl_sdw_reg_writel(data->sdw_shim, SDW_CNL_SYNC, sync_reg); + + return 0; +} + +static int cnl_sdw_probe(struct sdw_master *mstr, + const struct sdw_master_id *sdw_id) +{ + struct cnl_sdw *sdw; + int ret = 0; + struct cnl_sdw_data *data = mstr->dev.platform_data; + + sdw = devm_kzalloc(&mstr->dev, sizeof(*sdw), GFP_KERNEL); + if (!sdw) { + ret = -ENOMEM; + return ret; + } + dev_info(&mstr->dev, + "Controller Resources ctrl_base = %p shim=%p irq=%d inst_id=%d\n", + data->sdw_regs, data->sdw_shim, data->irq, data->inst_id); + sdw->data.sdw_regs = data->sdw_regs; + sdw->data.sdw_shim = data->sdw_shim; + sdw->data.irq = data->irq; + sdw->data.inst_id = data->inst_id; + sdw->data.alh_base = data->alh_base; + sdw->mstr = mstr; + spin_lock_init(&sdw->ctrl_lock); + sdw_master_set_drvdata(mstr, sdw); + init_completion(&sdw->tx_complete); + mutex_init(&sdw->stream_lock); + ret = sdw_init(sdw); + if (ret) { + dev_err(&mstr->dev, "SoundWire controller init failed %d\n", + data->inst_id); + return ret; + } + ret = devm_request_irq(&mstr->dev, + sdw->data.irq, cnl_sdw_irq_handler, IRQF_SHARED, "SDW", sdw); + if (ret) { + dev_err(&mstr->dev, "unable to grab IRQ %d, disabling device\n", + sdw->data.irq); + sdw_power_down_link(sdw); + return ret; + } + pm_runtime_set_autosuspend_delay(&mstr->dev, 3000); + pm_runtime_use_autosuspend(&mstr->dev); + pm_runtime_enable(&mstr->dev); + pm_runtime_get_sync(&mstr->dev); + /* Resuming the device, since its already ON, function will simply + * return doing nothing + */ + pm_runtime_mark_last_busy(&mstr->dev); + /* Suspending the device after 3 secs, by the time + * all the slave would have enumerated. Initial + * clock freq is 9.6MHz and frame shape is 48X2, so + * there are 200000 frames in second, total there are + * minimum 600000 frames before device suspends. Soundwire + * spec says slave should get attached to bus in 4096 + * error free frames after reset. So this should be + * enough to make sure device gets attached to bus. + */ + pm_runtime_put_sync_autosuspend(&mstr->dev); + return ret; +} + +static int cnl_sdw_remove(struct sdw_master *mstr) +{ + struct cnl_sdw *sdw = sdw_master_get_drvdata(mstr); + + sdw_power_down_link(sdw); + + return 0; +} + +#ifdef CONFIG_PM +static int cnl_sdw_runtime_suspend(struct device *dev) +{ + enum sdw_clk_stop_mode clock_stop_mode; + + int volatile mcp_stat; + int mcp_control; + int timeout = 0; + int ret = 0; + + struct cnl_sdw *sdw = dev_get_drvdata(dev); + struct cnl_sdw_data *data = &sdw->data; + + /* If its suspended return */ + mcp_stat = cnl_sdw_reg_readl(data->sdw_regs, + SDW_CNL_MCP_STAT); + if (mcp_stat & (MCP_STAT_CLOCKSTOPPED_MASK << + MCP_STAT_CLOCKSTOPPED_SHIFT)) { + dev_info(dev, "Clock is already stopped\n"); + return 0; + } + + /* Write the MCP Control register to prevent block wakeup */ + mcp_control = cnl_sdw_reg_readl(data->sdw_regs, + SDW_CNL_MCP_CONTROL); + mcp_control |= (MCP_CONTROL_BLOCKWAKEUP_MASK << + MCP_CONTROL_BLOCKWAKEUP_SHIFT); + cnl_sdw_reg_writel(data->sdw_regs, SDW_CNL_MCP_CONTROL, mcp_control); + + /* Prepare all the slaves for clock stop */ + ret = sdw_prepare_for_clock_change(sdw->mstr, 1, &clock_stop_mode); + if (ret) + return ret; + + /* Call bus function to broadcast the clock stop now */ + ret = sdw_stop_clock(sdw->mstr, clock_stop_mode); + if (ret) + return ret; + /* Wait for clock to be stopped, we are waiting at max 1sec now */ + while (timeout != 10) { + mcp_stat = cnl_sdw_reg_readl(data->sdw_regs, + SDW_CNL_MCP_STAT); + if (mcp_stat & (MCP_STAT_CLOCKSTOPPED_MASK << + MCP_STAT_CLOCKSTOPPED_SHIFT)) + break; + msleep(100); + timeout++; + } + mcp_stat = cnl_sdw_reg_readl(data->sdw_regs, + SDW_CNL_MCP_STAT); + if (!(mcp_stat & (MCP_STAT_CLOCKSTOPPED_MASK << + MCP_STAT_CLOCKSTOPPED_SHIFT))) { + dev_err(dev, "Clock Stop failed\n"); + ret = -EBUSY; + goto out; + } + /* Switch control from master IP to glue */ + sdw_switch_to_glue(sdw); + + sdw_power_down_link(sdw); + + /* Enable the wakeup */ + cnl_sdw_reg_writew(data->sdw_shim, + SDW_CNL_SNDWWAKEEN_REG_OFFSET, + (0x1 << data->inst_id)); +out: + return ret; +} + +static int cnl_sdw_clock_stop_exit(struct cnl_sdw *sdw) +{ + u16 wake_en, wake_sts, ioctl; + int volatile mcp_control; + int timeout = 0; + struct cnl_sdw_data *data = &sdw->data; + int ioctl_offset = SDW_CNL_IOCTL + (data->inst_id * + SDW_CNL_IOCTL_REG_OFFSET); + + /* Disable the wake up interrupt */ + wake_en = cnl_sdw_reg_readw(data->sdw_shim, + SDW_CNL_SNDWWAKEEN_REG_OFFSET); + wake_en &= ~(0x1 << data->inst_id); + cnl_sdw_reg_writew(data->sdw_shim, SDW_CNL_SNDWWAKEEN_REG_OFFSET, + wake_en); + + /* Clear wake status. This may be set if Slave requested wakeup has + * happened, or may not be if it master requested. But in any case + * this wont make any harm + */ + wake_sts = cnl_sdw_reg_readw(data->sdw_shim, + SDW_CNL_SNDWWAKESTS_REG_OFFSET); + wake_sts |= (0x1 << data->inst_id); + cnl_sdw_reg_writew(data->sdw_shim, SDW_CNL_SNDWWAKESTS_REG_OFFSET, + wake_sts); + + ioctl = cnl_sdw_reg_readw(data->sdw_shim, ioctl_offset); + ioctl |= CNL_IOCTL_DO_MASK << CNL_IOCTL_DO_SHIFT; + cnl_sdw_reg_writew(data->sdw_shim, ioctl_offset, ioctl); + ioctl |= CNL_IOCTL_DOE_MASK << CNL_IOCTL_DOE_SHIFT; + cnl_sdw_reg_writew(data->sdw_shim, ioctl_offset, ioctl); + /* Switch control back to master */ + sdw_switch_to_mip(sdw); + + mcp_control = cnl_sdw_reg_readl(data->sdw_regs, + SDW_CNL_MCP_CONTROL); + mcp_control &= ~(MCP_CONTROL_BLOCKWAKEUP_MASK << + MCP_CONTROL_BLOCKWAKEUP_SHIFT); + mcp_control |= (MCP_CONTROL_CLOCKSTOPCLEAR_MASK << + MCP_CONTROL_CLOCKSTOPCLEAR_SHIFT); + cnl_sdw_reg_writel(data->sdw_regs, SDW_CNL_MCP_CONTROL, mcp_control); + /* + * Wait for timeout to be clear to successful enabling of the clock + * We will wait for 1sec before giving up + */ + while (timeout != 10) { + mcp_control = cnl_sdw_reg_readl(data->sdw_regs, + SDW_CNL_MCP_CONTROL); + if ((mcp_control & (MCP_CONTROL_CLOCKSTOPCLEAR_MASK << + MCP_CONTROL_CLOCKSTOPCLEAR_SHIFT)) == 0) + break; + msleep(1000); + timeout++; + } + mcp_control = cnl_sdw_reg_readl(data->sdw_regs, + SDW_CNL_MCP_CONTROL); + if ((mcp_control & (MCP_CONTROL_CLOCKSTOPCLEAR_MASK << + MCP_CONTROL_CLOCKSTOPCLEAR_SHIFT)) != 0) { + dev_err(&sdw->mstr->dev, "Clop Stop Exit failed\n"); + return -EBUSY; + } + + dev_info(&sdw->mstr->dev, "Exit from clock stop successful\n"); + return 0; + +} + +static int cnl_sdw_runtime_resume(struct device *dev) +{ + struct cnl_sdw *sdw = dev_get_drvdata(dev); + struct cnl_sdw_data *data = &sdw->data; + int volatile mcp_stat; + struct sdw_master *mstr; + int ret = 0; + + mstr = sdw->mstr; + /* + * If already resumed, do nothing. This can happen because of + * wakeup enable. + */ + mcp_stat = cnl_sdw_reg_readl(data->sdw_regs, + SDW_CNL_MCP_STAT); + if (!(mcp_stat & (MCP_STAT_CLOCKSTOPPED_MASK << + MCP_STAT_CLOCKSTOPPED_SHIFT))) { + dev_info(dev, "Clock is already running\n"); + return 0; + } + dev_info(dev, "%s %d Clock is stopped\n", __func__, __LINE__); + + ret = cnl_sdw_clock_stop_exit(sdw); + if (ret) + return ret; + dev_info(&mstr->dev, "Exit from clock stop successful\n"); + + /* Prepare all the slaves to comeout of clock stop */ + ret = sdw_prepare_for_clock_change(sdw->mstr, 0, NULL); + if (ret) + return ret; + + return 0; +} + +static int cnl_sdw_sleep_resume(struct device *dev) +{ + return cnl_sdw_runtime_resume(dev); +} +static int cnl_sdw_sleep_suspend(struct device *dev) +{ + return cnl_sdw_runtime_suspend(dev); +} +#endif + +static const struct dev_pm_ops cnl_sdw_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(cnl_sdw_sleep_suspend, cnl_sdw_sleep_resume) + SET_RUNTIME_PM_OPS(cnl_sdw_runtime_suspend, + cnl_sdw_runtime_resume, NULL) +}; + +static struct sdw_master_ops cnl_sdw_master_ops = { + .xfer_msg = cnl_sdw_xfer_msg, + .xfer_bulk = cnl_sdw_xfer_bulk, + .monitor_handover = cnl_sdw_mon_handover, + .set_ssp_interval = cnl_sdw_set_ssp_interval, + .set_clock_freq = cnl_sdw_set_clock_freq, + .set_frame_shape = NULL, +}; + +static struct sdw_master_port_ops cnl_sdw_master_port_ops = { + .dpn_set_port_params = cnl_sdw_set_port_params, + .dpn_set_port_transport_params = cnl_sdw_set_port_transport_params, + .dpn_port_activate_ch = cnl_sdw_port_activate_ch, + .dpn_port_activate_ch_pre = cnl_sdw_port_activate_ch_pre, + .dpn_port_activate_ch_post = cnl_sdw_port_activate_ch_post, + .dpn_port_prepare_ch = NULL, + .dpn_port_prepare_ch_pre = NULL, + .dpn_port_prepare_ch_post = NULL, + +}; + +static struct sdw_mstr_driver cnl_sdw_mstr_driver = { + .driver_type = SDW_DRIVER_TYPE_MASTER, + .driver = { + .name = "cnl_sdw_mstr", + .pm = &cnl_sdw_pm_ops, + }, + .probe = cnl_sdw_probe, + .remove = cnl_sdw_remove, + .mstr_ops = &cnl_sdw_master_ops, + .mstr_port_ops = &cnl_sdw_master_port_ops, +}; + +static int __init cnl_sdw_init(void) +{ + return sdw_mstr_driver_register(&cnl_sdw_mstr_driver); +} +module_init(cnl_sdw_init); + +static void cnl_sdw_exit(void) +{ + sdw_mstr_driver_unregister(&cnl_sdw_mstr_driver); +} +module_exit(cnl_sdw_exit); + +MODULE_DESCRIPTION("Intel SoundWire Master Controller Driver"); +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Hardik Shah "); diff --git a/drivers/sdw/sdw_cnl_priv.h b/drivers/sdw/sdw_cnl_priv.h new file mode 100644 index 000000000000..914f7cae2b01 --- /dev/null +++ b/drivers/sdw/sdw_cnl_priv.h @@ -0,0 +1,337 @@ +/* + * sdw_cnl_priv.h - Private definition for intel master controller driver. + * + * Copyright (C) 2014-2015 Intel Corp + * Author: Hardik Shah + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * 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; version 2 of the License. + * + * 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. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + */ + +#ifndef _LINUX_SDW_CNL_PRIV_H +#define _LINUX_SDW_CNL_PRIV_H + +#define SDW_CNL_PM_TIMEOUT 3000 /* ms */ +#define SDW_CNL_SLAVES_STAT_UPPER_DWORD_SHIFT 32 +#define SDW_CNL_SLAVE_STATUS_BITS 4 +#define SDW_CNL_CMD_WORD_LEN 4 +#define SDW_CNL_DEFAULT_SSP_INTERVAL 0x32 +#define SDW_CNL_PORT_REG_OFFSET 0x80 +#define CNL_SDW_SCP_ADDR_REGS 0x2 +#define SDW_CNL_PCM_PDI_NUM_OFFSET 0x2 +#define SDW_CNL_PDM_PDI_NUM_OFFSET 0x6 + +#define SDW_CNL_CTMCTL_REG_OFFSET 0x60 +#define SDW_CNL_IOCTL_REG_OFFSET 0x60 +#define SDW_CNL_PCMSCAP_REG_OFFSET 0x60 +#define SDW_CNL_PCMSCHC_REG_OFFSET 0x60 +#define SDW_CNL_PDMSCAP_REG_OFFSET 0x60 +#define SDW_CNL_PCMSCHM_REG_OFFSET 0x60 +#define SDW_CNL_SNDWWAKEEN_REG_OFFSET 0x190 +#define SDW_CNL_SNDWWAKESTS_REG_OFFSET 0x192 + + +#define SDW_CNL_MCP_CONFIG 0x0 +#define MCP_CONFIG_BRELENABLE_MASK 0x1 +#define MCP_CONFIG_BRELENABLE_SHIFT 0x6 +#define MCP_CONFIG_MAXCMDRETRY_SHIFT 24 +#define MCP_CONFIG_MAXCMDRETRY_MASK 0xF +#define MCP_CONFIG_MAXPREQDELAY_SHIFT 16 +#define MCP_CONFIG_MAXPREQDELAY_MASK 0x1F +#define MCP_CONFIG_MMMODEEN_SHIFT 0x7 +#define MCP_CONFIG_MMMODEEN_MASK 0x1 +#define MCP_CONFIG_SNIFFEREN_SHIFT 0x5 +#define MCP_CONFIG_SNIFFEREN_MASK 0x1 +#define MCP_CONFIG_SSPMODE_SHIFT 0x4 +#define MCP_CONFIG_SSPMODE_MASK 0x1 +#define MCP_CONFIG_CMDMODE_SHIFT 0x3 +#define MCP_CONFIG_CMDMODE_MASK 0x1 + +#define MCP_CONFIG_OPERATIONMODE_MASK 0x7 +#define MCP_CONFIG_OPERATIONMODE_SHIFT 0x0 +#define MCP_CONFIG_OPERATIONMODE_NORMAL 0x0 + +#define SDW_CNL_MCP_CONTROL 0x4 +#define MCP_CONTROL_RESETDELAY_SHIFT 0x8 +#define MCP_CONTROL_CMDRST_SHIFT 0x7 +#define MCP_CONTROL_CMDRST_MASK 0x1 +#define MCP_CONTROL_SOFTRST_SHIFT 0x6 +#define MCP_CONTROL_SOFTCTRLBUSRST_SHIFT 0x5 +#define MCP_CONTROL_HARDCTRLBUSRST_SHIFT 0x4 +#define MCP_CONTROL_CLOCKPAUSEREQ_SHIFT 0x3 +#define MCP_CONTROL_CLOCKSTOPCLEAR_SHIFT 0x2 +#define MCP_CONTROL_CLOCKSTOPCLEAR_MASK 0x1 +#define MCP_CONTROL_CMDACCEPTMODE_MASK 0x1 +#define MCP_CONTROL_CMDACCEPTMODE_SHIFT 0x1 +#define MCP_CONTROL_BLOCKWAKEUP_SHIFT 0x0 +#define MCP_CONTROL_BLOCKWAKEUP_MASK 0x1 + + +#define MCP_SLAVEINTMASK0_MASK 0xFFFFFFFF +#define MCP_SLAVEINTMASK1_MASK 0x0000FFFF + +#define SDW_CNL_MCP_CMDCTRL 0x8 +#define SDW_CNL_MCP_SSPSTAT 0xC +#define SDW_CNL_MCP_FRAMESHAPE 0x10 +#define SDW_CNL_MCP_FRAMESHAPEINIT 0x14 +#define SDW_CNL_MCP_CONFIGUPDATE 0x18 +#define MCP_CONFIGUPDATE_CONFIGUPDATE_SHIFT 0x0 +#define MCP_CONFIGUPDATE_CONFIGUPDATE_MASK 0x1 + +#define SDW_CNL_MCP_PHYCTRL 0x1C +#define SDW_CNL_MCP_SSPCTRL0 0x20 +#define SDW_CNL_MCP_SSPCTRL1 0x28 +#define SDW_CNL_MCP_CLOCKCTRL0 0x30 +#define SDW_CNL_MCP_CLOCKCTRL1 0x38 +#define SDW_CNL_MCP_STAT 0x40 +#define SDW_CNL_MCP_INTSTAT 0x44 +#define MCP_INTSTAT_IRQ_SHIFT 31 +#define MCP_INTSTAT_IRQ_MASK 1 +#define MCP_INTSTAT_WAKEUP_SHIFT 16 +#define MCP_INTSTAT_SLAVE_STATUS_CHANGED_SHIFT 12 +#define MCP_INTSTAT_SLAVE_STATUS_CHANGED_MASK 0xF +#define MCP_INTSTAT_SLAVENOTATTACHED_SHIFT 12 +#define MCP_INTSTAT_SLAVEATTACHED_SHIFT 13 +#define MCP_INTSTAT_SLAVEALERT_SHIFT 14 +#define MCP_INTSTAT_SLAVERESERVED_SHIFT 15 + +#define MCP_INTSTAT_DPPDIINT_SHIFT 11 +#define MCP_INTSTAT_DPPDIINTMASK 0x1 +#define MCP_INTSTAT_CONTROLBUSCLASH_SHIFT 10 +#define MCP_INTSTAT_CONTROLBUSCLASH_MASK 0x1 +#define MCP_INTSTAT_DATABUSCLASH_SHIFT 9 +#define MCP_INTSTAT_DATABUSCLASH_MASK 0x1 +#define MCP_INTSTAT_CMDERR_SHIFT 7 +#define MCP_INTSTAT_CMDERR_MASK 0x1 +#define MCP_INTSTAT_TXE_SHIFT 1 +#define MCP_INTSTAT_TXE_MASK 0x1 +#define MCP_INTSTAT_RXWL_SHIFT 2 +#define MCP_INTSTAT_RXWL_MASK 1 + +#define SDW_CNL_MCP_INTMASK 0x48 +#define MCP_INTMASK_IRQEN_SHIFT 31 +#define MCP_INTMASK_IRQEN_MASK 0x1 +#define MCP_INTMASK_WAKEUP_SHIFT 16 +#define MCP_INTMASK_WAKEUP_MASK 0x1 +#define MCP_INTMASK_SLAVERESERVED_SHIFT 15 +#define MCP_INTMASK_SLAVERESERVED_MASK 0x1 +#define MCP_INTMASK_SLAVEALERT_SHIFT 14 +#define MCP_INTMASK_SLAVEALERT_MASK 0x1 +#define MCP_INTMASK_SLAVEATTACHED_SHIFT 13 +#define MCP_INTMASK_SLAVEATTACHED_MASK 0x1 +#define MCP_INTMASK_SLAVENOTATTACHED_SHIFT 12 +#define MCP_INTMASK_SLAVENOTATTACHED_MASK 0x1 +#define MCP_INTMASK_DPPDIINT_SHIFT 11 +#define MCP_INTMASK_DPPDIINT_MASK 0x1 +#define MCP_INTMASK_CONTROLBUSCLASH_SHIFT 10 +#define MCP_INTMASK_CONTROLBUSCLASH_MASK 1 +#define MCP_INTMASK_DATABUSCLASH_SHIFT 9 +#define MCP_INTMASK_DATABUSCLASH_MASK 1 +#define MCP_INTMASK_CMDERR_SHIFT 7 +#define MCP_INTMASK_CMDERR_MASK 0x1 +#define MCP_INTMASK_TXE_SHIFT 1 +#define MCP_INTMASK_TXE_MASK 0x1 +#define MCP_INTMASK_RXWL_SHIFT 2 +#define MCP_INTMASK_RXWL_MASK 0x1 + +#define SDW_CNL_MCP_INTSET 0x4C +#define SDW_CNL_MCP_STAT 0x40 +#define MCP_STAT_ACTIVE_BANK_MASK 0x1 +#define MCP_STAT_ACTIVE_BANK_SHIT 20 +#define MCP_STAT_CLOCKSTOPPED_MASK 0x1 +#define MCP_STAT_CLOCKSTOPPED_SHIFT 16 + +#define SDW_CNL_MCP_SLAVESTAT 0x50 +#define MCP_SLAVESTAT_MASK 0x3 + +#define SDW_CNL_MCP_SLAVEINTSTAT0 0x54 +#define MCP_SLAVEINTSTAT_NOT_PRESENT_MASK 0x1 +#define MCP_SLAVEINTSTAT_ATTACHED_MASK 0x2 +#define MCP_SLAVEINTSTAT_ALERT_MASK 0x4 +#define MCP_SLAVEINTSTAT_RESERVED_MASK 0x8 + +#define SDW_CNL_MCP_SLAVEINTSTAT1 0x58 +#define SDW_CNL_MCP_SLAVEINTMASK0 0x5C +#define SDW_CNL_MCP_SLAVEINTMASK1 0x60 +#define SDW_CNL_MCP_PORTINTSTAT 0x64 +#define SDW_CNL_MCP_PDISTAT 0x6C + +#define SDW_CNL_MCP_FIFOLEVEL 0x78 +#define SDW_CNL_MCP_FIFOSTAT 0x7C +#define MCP_RX_FIFO_AVAIL_MASK 0x3F +#define SDW_CNL_MCP_COMMAND_BASE 0x80 +#define SDW_CNL_MCP_RESPONSE_BASE 0x80 +#define SDW_CNL_MCP_COMMAND_LENGTH 0x20 + +#define MCP_COMMAND_SSP_TAG_MASK 0x1 +#define MCP_COMMAND_SSP_TAG_SHIFT 31 +#define MCP_COMMAND_COMMAND_MASK 0x7 +#define MCP_COMMAND_COMMAND_SHIFT 28 +#define MCP_COMMAND_DEV_ADDR_MASK 0xF +#define MCP_COMMAND_DEV_ADDR_SHIFT 24 +#define MCP_COMMAND_REG_ADDR_H_MASK 0x7 +#define MCP_COMMAND_REG_ADDR_H_SHIFT 16 +#define MCP_COMMAND_REG_ADDR_L_MASK 0xFF +#define MCP_COMMAND_REG_ADDR_L_SHIFT 8 +#define MCP_COMMAND_REG_DATA_MASK 0xFF +#define MCP_COMMAND_REG_DATA_SHIFT 0x0 + +#define MCP_RESPONSE_RDATA_MASK 0xFF +#define MCP_RESPONSE_RDATA_SHIFT 8 +#define MCP_RESPONSE_ACK_MASK 0x1 +#define MCP_RESPONSE_ACK_SHIFT 0 +#define MCP_RESPONSE_NACK_MASK 0x2 + +#define SDW_CNL_DPN_CONFIG0 0x100 +#define SDW_CNL_DPN_CHANNELEN0 0x104 +#define SDW_CNL_DPN_SAMPLECTRL0 0x108 +#define SDW_CNL_DPN_OFFSETCTRL0 0x10C +#define SDW_CNL_DPN_HCTRL0 0x110 +#define SDW_CNL_DPN_ASYNCCTRL0 0x114 + +#define SDW_CNL_DPN_CONFIG1 0x118 +#define SDW_CNL_DPN_CHANNELEN1 0x11C +#define SDW_CNL_DPN_SAMPLECTRL1 0x120 +#define SDW_CNL_DPN_OFFSETCTRL1 0x124 +#define SDW_CNL_DPN_HCTRL1 0x128 + +#define SDW_CNL_PORTCTRL 0x130 +#define PORTCTRL_PORT_DIRECTION_SHIFT 0x7 +#define PORTCTRL_PORT_DIRECTION_MASK 0x1 +#define PORTCTRL_BANK_INVERT_SHIFT 0x8 +#define PORTCTRL_BANK_INVERT_MASK 0x1 + +#define SDW_CNL_PDINCONFIG0 0x1100 +#define SDW_CNL_PDINCONFIG1 0x1108 +#define PDINCONFIG_CHANNEL_MASK_SHIFT 0x8 +#define PDINCONFIG_CHANNEL_MASK_MASK 0xFF +#define PDINCONFIG_PORT_NUMBER_SHIFT 0x0 +#define PDINCONFIG_PORT_NUMBER_MASK 0x1F + +#define DPN_CONFIG_WL_SHIFT 0x8 +#define DPN_CONFIG_WL_MASK 0x1F +#define DPN_CONFIG_PF_MODE_SHIFT 0x0 +#define DPN_CONFIG_PF_MODE_MASK 0x3 +#define DPN_CONFIG_PD_MODE_SHIFT 0x2 +#define DPN_CONFIG_PD_MODE_MASK 0x3 +#define DPN_CONFIG_BPM_MASK 0x1 +#define DPN_CONFIG_BPM_SHIFT 0x12 +#define DPN_CONFIG_BGC_MASK 0x3 +#define DPN_CONFIG_BGC_SHIFT 0x10 + +#define DPN_SAMPLECTRL_SI_MASK 0xFFFF +#define DPN_SAMPLECTRL_SI_SHIFT 0x0 + +#define DPN_OFFSETCTRL0_OF1_MASK 0xFF +#define DPN_OFFSETCTRL0_OF1_SHIFT 0x0 +#define DPN_OFFSETCTRL0_OF2_MASK 0xFF +#define DPN_OFFSETCTRL0_OF2_SHIFT 0x8 + +#define DPN_HCTRL_HSTOP_MASK 0xF +#define DPN_HCTRL_HSTOP_SHIFT 0x0 +#define DPN_HCTRL_HSTART_MASK 0xF +#define DPN_HCTRL_HSTART_SHIFT 0x4 +#define DPN_HCTRL_LCONTROL_MASK 0x7 +#define DPN_HCTRL_LCONTROL_SHIFT 0x8 + +/* SoundWire Shim registers */ +#define SDW_CNL_LCAP 0x0 +#define SDW_CNL_LCTL 0x4 +#define CNL_LCTL_CPA_SHIFT 8 +#define CNL_LCTL_SPA_SHIFT 0 +#define CNL_LCTL_CPA_MASK 0x1 +#define CNL_LCTL_SPA_MASK 0x1 + +#define SDW_CNL_IPPTR 0x8 +#define SDW_CNL_SYNC 0xC +#define CNL_SYNC_CMDSYNC_MASK 0x1 +#define CNL_SYNC_CMDSYNC_SHIFT 16 +#define CNL_SYNC_SYNCGO_MASK 0x1 +#define CNL_SYNC_SYNCGO_SHIFT 0x18 + +#define SDW_CNL_CTLSCAP 0x10 +#define SDW_CNL_CTLS0CM 0x12 +#define SDW_CNL_CTLS1CM 0x14 +#define SDW_CNL_CTLS2CM 0x16 +#define SDW_CNL_CTLS3CM 0x18 + +#define SDW_CNL_PCMSCAP 0x20 +#define CNL_PCMSCAP_BSS_SHIFT 8 +#define CNL_PCMSCAP_BSS_MASK 0x1F +#define CNL_PCMSCAP_OSS_SHIFT 4 +#define CNL_PCMSCAP_OSS_MASK 0xF +#define CNL_PCMSCAP_ISS_SHIFT 0 +#define CNL_PCMSCAP_ISS_MASK 0xF + +#define SDW_CNL_PCMSCHM 0x22 +#define CNL_PCMSYCM_DIR_SHIFT 15 +#define CNL_PCMSYCM_DIR_MASK 0x1 +#define CNL_PCMSYCM_STREAM_SHIFT 8 +#define CNL_PCMSYCM_STREAM_MASK 0x3F +#define CNL_PCMSYCM_HCHAN_SHIFT 4 +#define CNL_PCMSYCM_HCHAN_MASK 0xF +#define CNL_PCMSYCM_LCHAN_SHIFT 0 +#define CNL_PCMSYCM_LCHAN_MASK 0xF + +#define SDW_CNL_PCMSCHC 0x42 + +#define SDW_CNL_PDMSCAP 0x62 +#define CNL_PDMSCAP_BSS_SHIFT 8 +#define CNL_PDMSCAP_BSS_MASK 0x1F +#define CNL_PDMSCAP_OSS_SHIFT 4 +#define CNL_PDMSCAP_OSS_MASK 0xF +#define CNL_PDMSCAP_ISS_SHIFT 0 +#define CNL_PDMSCAP_ISS_MASK 0xF +#define CNL_PDMSCAP_CPSS_SHIFT 13 +#define CNL_PDMSCAP_CPSS_MASK 0x7 +#define SDW_CNL_PDMSCM + +#define SDW_CNL_IOCTL 0x6C +#define CNL_IOCTL_MIF_SHIFT 0x0 +#define CNL_IOCTL_MIF_MASK 0x1 +#define CNL_IOCTL_CO_SHIFT 0x1 +#define CNL_IOCTL_CO_MASK 0x1 +#define CNL_IOCTL_COE_SHIFT 0x2 +#define CNL_IOCTL_COE_MASK 0x1 +#define CNL_IOCTL_DO_SHIFT 0x3 +#define CNL_IOCTL_DO_MASK 0x1 +#define CNL_IOCTL_DOE_SHIFT 0x4 +#define CNL_IOCTL_DOE_MASK 0x1 +#define CNL_IOCTL_BKE_SHIFT 0x5 +#define CNL_IOCTL_BKE_MASK 0x1 +#define CNL_IOCTL_WPDD_SHIFT 0x6 +#define CNL_IOCTL_WPDD_MASK 0x1 +#define CNL_IOCTL_CIBD_SHIFT 0x8 +#define CNL_IOCTL_CIBD_MASK 0x1 +#define CNL_IOCTL_DIBD_SHIFT 0x9 +#define CNL_IOCTL_DIBD_MASK 0x1 + +#define SDW_CNL_CTMCTL_OFFSET 0x60 +#define SDW_CNL_CTMCTL 0x6E +#define CNL_CTMCTL_DACTQE_SHIFT 0x0 +#define CNL_CTMCTL_DACTQE_MASK 0x1 +#define CNL_CTMCTL_DODS_SHIFT 0x1 +#define CNL_CTMCTL_DODS_MASK 0x1 +#define CNL_CTMCTL_DOAIS_SHIFT 0x3 +#define CNL_CTMCTL_DOAIS_MASK 0x3 + +#define ALH_CNL_STRMZCFG_BASE 0x4 +#define ALH_CNL_STRMZCFG_OFFSET 0x4 +#define CNL_STRMZCFG_DMAT_SHIFT 0x0 +#define CNL_STRMZCFG_DMAT_MASK 0xFF +#define CNL_STRMZCFG_DMAT_VAL 0x3 +#define CNL_STRMZCFG_CHAN_SHIFT 16 +#define CNL_STRMZCFG_CHAN_MASK 0xF + +#endif /* _LINUX_SDW_CNL_H */ diff --git a/drivers/sdw/sdw_maxim.c b/drivers/sdw/sdw_maxim.c new file mode 100644 index 000000000000..0081c5c00497 --- /dev/null +++ b/drivers/sdw/sdw_maxim.c @@ -0,0 +1,146 @@ +/* + * sdw_maxim.c -- Maxim SoundWire slave device driver. Dummy driver + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +#include +#include +#include + + +static int maxim_register_sdw_capabilties(struct sdw_slv *sdw, + const struct sdw_slv_id *sdw_id) +{ + struct sdw_slv_capabilities cap; + struct sdw_slv_dpn_capabilities *dpn_cap = NULL; + struct port_audio_mode_properties *prop = NULL; + int i, j; + + cap.wake_up_unavailable = true; + cap.test_mode_supported = false; + cap.clock_stop1_mode_supported = false; + cap.simplified_clock_stop_prepare = false; + cap.highphy_capable = true; + cap.paging_supported = false; + cap.bank_delay_support = false; + cap.port_15_read_behavior = 0; + cap.sdw_dp0_supported = false; + cap.num_of_sdw_ports = 3; + cap.sdw_dpn_cap = devm_kzalloc(&sdw->dev, + ((sizeof(struct sdw_slv_dpn_capabilities)) * + cap.num_of_sdw_ports), GFP_KERNEL); + for (i = 0; i < cap.num_of_sdw_ports; i++) { + dpn_cap = &cap.sdw_dpn_cap[i]; + if (i == 0 || i == 2) + dpn_cap->port_direction = SDW_PORT_SOURCE; + else + dpn_cap->port_direction = SDW_PORT_SINK; + + dpn_cap->port_number = i+1; + dpn_cap->max_word_length = 24; + dpn_cap->min_word_length = 16; + dpn_cap->num_word_length = 0; + dpn_cap->word_length_buffer = NULL; + dpn_cap->dpn_type = SDW_FULL_DP; + dpn_cap->dpn_grouping = SDW_BLOCKGROUPCOUNT_1; + dpn_cap->prepare_ch = SDW_CP_SM; + dpn_cap->imp_def_intr_mask = 0x0; + dpn_cap->min_ch_num = 1; + dpn_cap->max_ch_num = 2; + dpn_cap->num_ch_supported = 0; + dpn_cap->ch_supported = NULL; + dpn_cap->port_flow_mode_mask = SDW_PORT_FLOW_MODE_ISOCHRONOUS; + dpn_cap->block_packing_mode_mask = + SDW_PORT_BLK_PKG_MODE_BLK_PER_PORT_MASK | + SDW_PORT_BLK_PKG_MODE_BLK_PER_CH_MASK; + dpn_cap->port_encoding_type_mask = + SDW_PORT_ENCODING_TYPE_TWOS_CMPLMNT | + SDW_PORT_ENCODING_TYPE_SIGN_MAGNITUDE | + SDW_PORT_ENCODING_TYPE_IEEE_32_FLOAT; + dpn_cap->num_audio_modes = 1; + + dpn_cap->mode_properties = devm_kzalloc(&sdw->dev, + ((sizeof(struct port_audio_mode_properties)) * + dpn_cap->num_audio_modes), GFP_KERNEL); + for (j = 0; j < dpn_cap->num_audio_modes; j++) { + prop = &dpn_cap->mode_properties[j]; + prop->max_frequency = 16000000; + prop->min_frequency = 1000000; + prop->num_freq_configs = 0; + prop->freq_supported = NULL; + prop->glitchless_transitions_mask = 0x1; + prop->max_sampling_frequency = 192000; + prop->min_sampling_frequency = 8000; + prop->num_sampling_freq_configs = 0; + prop->sampling_freq_config = NULL; + prop->ch_prepare_behavior = SDW_CH_PREP_ANY_TIME; + } + } + return sdw_register_slave_capabilities(sdw, &cap); + +} +static int maxim_sdw_probe(struct sdw_slv *sdw, + const struct sdw_slv_id *sdw_id) +{ + dev_info(&sdw->dev, "Maxim SoundWire Slave Registered %lx\n", sdw_id->driver_data); + return maxim_register_sdw_capabilties(sdw, sdw_id); +} + +static int maxim_sdw_remove(struct sdw_slv *sdw) +{ + dev_info(&sdw->dev, "Maxim SoundWire Slave un-Registered\n"); + return 0; +} + +static const struct sdw_slv_id maxim_id[] = { + {"03:01:9f:79:00:00", 0}, + {"09:01:9f:79:00:00", 1}, + {"04:01:9f:79:00:00", 2}, + {"0a:01:9f:79:00:00", 3}, + {"04:01:9f:79:00:00", 4}, + {"0a:01:9f:79:00:00", 5}, + {"05:01:9f:79:00:00", 6}, + {"06:01:9f:79:00:00", 7}, + {"05:01:9f:79:00:00", 8}, + {"00:01:9f:79:00:00", 9}, + {"06:01:9f:79:00:00", 10}, + {"07:01:9f:79:00:00", 11}, + {"00:01:9f:79:00:00", 12}, + {"06:01:9f:79:00:00", 13}, + {"01:01:9f:79:00:00", 14}, + {"07:01:9f:79:00:00", 15}, + {"08:01:9f:79:00:00", 16}, + {"01:01:9f:79:00:00", 17}, + {"07:01:9f:79:00:00", 18}, + {"02:01:9f:79:00:00", 19}, + {"08:01:9f:79:00:00", 20}, + {"09:01:9f:79:00:00", 21}, + {"02:01:9f:79:00:00", 22}, + {"08:01:9f:79:00:00", 23}, + {"03:01:9f:79:00:00", 24}, + {"09:01:9f:79:00:00", 25}, + {"0a:01:9f:79:00:00", 26}, + {}, +}; + +MODULE_DEVICE_TABLE(sdwint, maxim_id); + +static struct sdw_slave_driver maxim_sdw_driver = { + .driver_type = SDW_DRIVER_TYPE_SLAVE, + .driver = { + .name = "maxim", + }, + .probe = maxim_sdw_probe, + .remove = maxim_sdw_remove, + .id_table = maxim_id, +}; + +module_sdw_slave_driver(maxim_sdw_driver); + +MODULE_DESCRIPTION("SoundWire Maxim Slave Driver"); +MODULE_AUTHOR("Hardik Shah, "); +MODULE_LICENSE("GPL"); diff --git a/drivers/sdw/sdw_priv.h b/drivers/sdw/sdw_priv.h new file mode 100644 index 000000000000..5ec3edc30d27 --- /dev/null +++ b/drivers/sdw/sdw_priv.h @@ -0,0 +1,243 @@ +/* + * sdw_priv.h - Private definition for sdw bus interface. + * + * Copyright (C) 2014-2015 Intel Corp + * Author: Hardik Shah + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * 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; version 2 of the License. + * + * 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. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + */ + +#ifndef _LINUX_SDW_PRIV_H +#define _LINUX_SDW_PRIV_H + +#include /* For kthread */ +#include + +#define SDW_MAX_STREAM_TAG_KEY_SIZE 80 +#define SDW_NUM_STREAM_TAGS 100 +#define MAX_NUM_ROWS 23 /* As per SDW Spec */ +#define MAX_NUM_COLS 8/* As per SDW Spec */ +#define MAX_NUM_ROW_COLS (MAX_NUM_ROWS * MAX_NUM_COLS) + +#define SDW_STATE_INIT_STREAM_TAG 0x1 +#define SDW_STATE_ALLOC_STREAM 0x2 +#define SDW_STATE_CONFIG_STREAM 0x3 +#define SDW_STATE_COMPUTE_STREAM 0x4 +#define SDW_STATE_PREPARE_STREAM 0x5 +#define SDW_STATE_ENABLE_STREAM 0x6 +#define SDW_STATE_DISABLE_STREAM 0x7 +#define SDW_STATE_UNPREPARE_STREAM 0x8 +#define SDW_STATE_UNCOMPUTE_STREAM 0x9 +#define SDW_STATE_RELEASE_STREAM 0xa +#define SDW_STATE_FREE_STREAM 0xb +#define SDW_STATE_FREE_STREAM_TAG 0xc +#define SDW_STATE_ONLY_XPORT_STREAM 0xd + +#define SDW_STATE_INIT_RT 0x1 +#define SDW_STATE_CONFIG_RT 0x2 +#define SDW_STATE_PREPARE_RT 0x3 +#define SDW_STATE_ENABLE_RT 0x4 +#define SDW_STATE_DISABLE_RT 0x5 +#define SDW_STATE_UNPREPARE_RT 0x6 +#define SDW_STATE_RELEASE_RT 0x7 + +struct sdw_runtime; +/* Defined in sdw.c, used by multiple files of module */ +extern struct sdw_core sdw_core; + +enum sdw_port_state { + SDW_PORT_STATE_CH_READY, + SDW_PORT_STATE_CH_STOPPED, + SDW_PORT_STATE_CH_PREPARING, + SDW_PORT_STATE_CH_DEPREPARING, +}; + +enum sdw_stream_state { + SDW_STREAM_ALLOCATED, + SDW_STREAM_FREE, + SDW_STREAM_ACTIVE, + SDW_STREAM_INACTIVE, +}; + +enum sdw_clk_state { + SDW_CLK_STATE_OFF = 0, + SDW_CLK_STATE_ON = 1, +}; + +struct port_chn_en_state { + bool is_activate; + bool is_bank_sw; +}; + +struct sdw_stream_tag { + int stream_tag; + struct mutex stream_lock; + int ref_count; + enum sdw_stream_state stream_state; + char key[SDW_MAX_STREAM_TAG_KEY_SIZE]; + struct sdw_runtime *sdw_rt; +}; + +struct sdw_stream_params { + unsigned int rate; + unsigned int channel_count; + unsigned int bps; +}; + +struct sdw_port_runtime { + int port_num; + enum sdw_port_state port_state; + int channel_mask; + /* Frame params and stream params are per port based + * Single stream of audio may be split + * into mutliple port each handling + * subset of channels, channels should + * be contiguous in subset + */ + struct sdw_transport_params transport_params; + struct sdw_port_params port_params; + struct list_head port_node; +}; + +struct sdw_slave_runtime { + /* Simplified port or full port, there cannot be both types of + * data port for single stream, so data structure is kept per + * slave runtime, not per port + */ + enum sdw_dpn_type type; + struct sdw_slv *slave; + int direction; + /* Stream may be split into multiple slaves, so this is for + * this particular slave + */ + struct sdw_stream_params stream_params; + struct list_head port_rt_list; + struct list_head slave_sdw_node; + struct list_head slave_node; + int rt_state; /* State of runtime structure */ + +}; + + +struct sdw_mstr_runtime { + struct sdw_master *mstr; + int direction; + /* Stream may be split between multiple masters so this + * is for invidual master, if stream is split into multiple + * streams. For calculating the bandwidth on the particular bus + * stream params of master is taken into account. + */ + struct sdw_stream_params stream_params; + struct list_head port_rt_list; + /* Two nodes are required because BW calculation is based on master + * while stream enabling is based on stream_tag, where multiple + * masters may be involved + */ + struct list_head mstr_sdw_node; /* This is to add mstr_rt in sdw_rt */ + struct list_head mstr_node; /* This is to add mstr_rt in mstr */ + + struct list_head slv_rt_list; + /* Individual stream bandwidth on given master */ + unsigned int stream_bw; + /* State of runtime structure */ + int rt_state; +}; + +struct sdw_runtime { + int tx_ref_count; + int rx_ref_count; + /* This is stream params for whole stream + * but stream may be split between two + * masters, or two slaves. + */ + struct sdw_stream_params stream_params; + struct list_head slv_rt_list; + struct list_head mstr_rt_list; + enum sdw_stream_type type; + int stream_state; + int xport_state; + +}; + +struct sdw_slv_status { + struct list_head node; + enum sdw_slave_status status[SOUNDWIRE_MAX_DEVICES+1]; +}; + +/** Bus structure which handles bus related information */ +struct sdw_bus { + struct list_head bus_node; + struct sdw_master *mstr; + unsigned int port_grp_mask[2]; + unsigned int slave_grp_mask[2]; + unsigned int clk_state; + unsigned int active_bank; + unsigned int clk_freq; + /* Bus total Bandwidth. Initialize and reset to zero */ + unsigned int bandwidth; + unsigned int system_interval; /* Bus System Interval */ + unsigned int frame_freq; + unsigned int col; + unsigned int row; + struct task_struct *status_thread; + struct kthread_worker kworker; + struct kthread_work kwork; + struct list_head status_list; + spinlock_t spinlock; +}; + +/** Holds supported Row-Column combination related information */ +struct sdw_rowcol { + int row; + int col; + int control_bits; + int data_bits; +}; + +/** + * Global soundwire structure. It handles all the streams spawned + * across masters and has list of bus structure per every master + * registered + */ +struct sdw_core { + struct sdw_stream_tag stream_tags[SDW_NUM_STREAM_TAGS]; + struct sdw_rowcol rowcolcomb[MAX_NUM_ROW_COLS]; + struct list_head bus_list; + struct mutex core_lock; + struct idr idr; + int first_dynamic_bus_num; +}; + +/* Structure holding mapping of numbers to cols */ +struct sdw_num_to_col { + int num; + int col; +}; + +/* Structure holding mapping of numbers to rows */ +struct sdw_num_to_row { + int num; + int row; +}; + +int sdw_slave_port_config_port_params(struct sdw_slave_runtime *slv_rt); +int sdw_slave_port_prepare(struct sdw_slave_runtime, bool prepare); +int sdw_bus_bw_init(void); +int sdw_mstr_bw_init(struct sdw_bus *sdw_bs); +int sdw_bus_calc_bw(struct sdw_stream_tag *stream_tag, bool enable); +int sdw_bus_calc_bw_dis(struct sdw_stream_tag *stream_tag, bool unprepare); +int sdw_chn_enable(void); + +#endif /* _LINUX_SDW_PRIV_H */ diff --git a/drivers/soundwire/Kconfig b/drivers/soundwire/Kconfig index 19c8efb9a5ee..a4b03e8cd694 100644 --- a/drivers/soundwire/Kconfig +++ b/drivers/soundwire/Kconfig @@ -4,6 +4,7 @@ menuconfig SOUNDWIRE bool "SoundWire support" + depends on !SDW ---help--- SoundWire is a 2-Pin interface with data and clock line ratified by the MIPI Alliance. SoundWire is used for transporting data diff --git a/include/linux/mod_devicetable.h b/include/linux/mod_devicetable.h index 01797cb4587e..13619e910d6f 100644 --- a/include/linux/mod_devicetable.h +++ b/include/linux/mod_devicetable.h @@ -762,4 +762,17 @@ struct typec_device_id { kernel_ulong_t driver_data; }; +#define SOUNDWIRE_NAME_SIZE 64 +#define SOUNDWIRE_MODULE_PREFIX "sdw:" + +struct sdw_slv_id { + char name[SOUNDWIRE_NAME_SIZE]; + kernel_ulong_t driver_data; /* Data private to the driver */ +}; + +struct sdw_master_id { + char name[SOUNDWIRE_NAME_SIZE]; + kernel_ulong_t driver_data; /* Data private to the driver */ +}; + #endif /* LINUX_MOD_DEVICETABLE_H */ diff --git a/include/linux/regmap.h b/include/linux/regmap.h index 379505a53722..035129bf1ac5 100644 --- a/include/linux/regmap.h +++ b/include/linux/regmap.h @@ -34,6 +34,7 @@ struct regmap_range_cfg; struct regmap_field; struct snd_ac97; struct sdw_slave; +struct sdw_slv; /* An enum of all the supported cache types */ enum regcache_type { @@ -561,6 +562,9 @@ struct regmap *__regmap_init_sdw(struct sdw_slave *sdw, const struct regmap_config *config, struct lock_class_key *lock_key, const char *lock_name); +struct regmap *regmap_init_slave(struct sdw_slv *sdw, + const struct regmap_config *config); + struct regmap *__devm_regmap_init(struct device *dev, const struct regmap_bus *bus, @@ -610,6 +614,9 @@ struct regmap *__devm_regmap_init_slimbus(struct slim_device *slimbus, const struct regmap_config *config, struct lock_class_key *lock_key, const char *lock_name); +struct regmap *devm_regmap_init_sdwint(struct sdw_slv *sdw, + const struct regmap_config *config); + /* * Wrapper for regmap_init macros to include a unique lockdep key and name * for each call. No-op if CONFIG_LOCKDEP is not set. diff --git a/include/linux/sdw/sdw_cnl.h b/include/linux/sdw/sdw_cnl.h new file mode 100644 index 000000000000..acf223cba595 --- /dev/null +++ b/include/linux/sdw/sdw_cnl.h @@ -0,0 +1,96 @@ +/* + * sdw_cnl.h - Shared header file for intel soundwire controller driver. + * + * Copyright (C) 2014-2015 Intel Corp + * Author: Hardik Shah + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * 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; version 2 of the License. + * + * 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. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + */ + +#ifndef _LINUX_SDW_CNL_H +#define _LINUX_SDW_CNL_H + +#include + + +#define SDW_CNL_PM_TIMEOUT 3000 /* ms */ + +#define CNL_SDW_MAX_PORTS 15 + +/* Maximum number hardware tries to send command if the command failed */ +#define CNL_SDW_MAX_CMD_RETRIES 15 +/* Standard allows 32 frames delay max between PREQ and Ping command + * We kept midway in hardware + */ +#define CNL_SDW_MAX_PREQ_DELAY 15 + +/* Reset Delay for hw controlled reset + * Reset length = 4096+(ResetDelay*256) clock cycles + */ +#define CNL_SDW_RESET_DELAY 15 + +#define CNL_SDW_SHIM_OFFSET 0x2C000 +#define CNL_SDW_LINK_0_OFFSET 0x30000 +#define CNL_SDW_LINK_1_OFFSET 0x40000 +#define CNL_SDW_LINK_2_OFFSET 0x50000 +#define CNL_SDW_LINK_3_OFFSET 0x60000 + +enum cnl_sdw_pdi_stream_type { + CNL_SDW_PDI_TYPE_PCM = 0, + CNL_SDW_PDI_TYPE_PDM = 1, +}; + +struct cnl_sdw_pdi_stream { + int pdi_num; + int sdw_pdi_num; + int ch_cnt; + bool allocated; + int port_num; + enum sdw_data_direction direction; + int h_ch_num, l_ch_num; + struct list_head node; + +}; + +struct cnl_sdw_port { + int port_num; + int allocated; + bool port_type; + int ch_cnt; + enum sdw_data_direction direction; + struct cnl_sdw_pdi_stream *pdi_stream; +}; + +struct cnl_sdw_data { + /* SoundWire IP registers per instance */ + void __iomem *sdw_regs; + /* SoundWire shim registers */ + void __iomem *sdw_shim; + /* This is just for enaling SoundWire interrupts */ + void __iomem *alh_base; + /* HDA interrupt */ + int irq; + /* Instance id */ + int inst_id; +}; + +struct cnl_sdw_port *cnl_sdw_alloc_port(struct sdw_master *mstr, int ch_count, + enum sdw_data_direction direction, + enum cnl_sdw_pdi_stream_type stream_type); +void cnl_sdw_free_port(struct sdw_master *mstr, int port_num); + + +#endif + diff --git a/include/linux/sdw/sdw_registers.h b/include/linux/sdw/sdw_registers.h new file mode 100644 index 000000000000..1abdf4bf863a --- /dev/null +++ b/include/linux/sdw/sdw_registers.h @@ -0,0 +1,157 @@ +/* sdw_registers.h - SoundWire MIPI spec 1.0 defined SoundWire slave + * register definition file. This defines the register + * offsets, bit masks and shifts in accordance with + * mipi spec 1.0 + * Copyright (C) 2015-2016 Intel Corp + * Author: Hardik T Shah + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * 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. + */ + +#ifndef _LINUX_SDW_REG_H +#define _LINUX_SDW_REG_H + +#define SDW_NUM_DATA_PORT_REGISTERS 0x100 +#define SDW_BANK1_REGISTER_OFFSET 0x10 + +#define SDW_DP0_INTSTAT 0x0 +#define SDW_DP0_INTSTAT_TEST_FAIL_MASK 0x1 +#define SDW_DP0_INTSTAT_TEST_FAIL_SHIFT 0x2 +#define SDW_DP0_INTSTAT_PORT_READY_MASK 0x4 +#define SDW_DP0_INTSTAT_PORT_READY_SHIFT 0x20 +#define SDW_DP0_INTSTAT_BRA_FAILURE_MASK 0x40 +#define SDW_DP0_INTSTAT_BRA_FAILURE_SHIFT 0x80 + +#define SDW_DP0_INTCLEAR 0x0 +#define SDW_DP0_INTCLEAR_TEST_FAIL_MASK 0x1 +#define SDW_DP0_INTCLEAR_PORT_READY_MASK 0x2 +#define SDW_DP0_INTCLEAR_BRA_FAILURE_MASK 0x4 +#define SDW_DP0_INTSTAT_IMPDEF1_MASK 0x20 +#define SDW_DP0_INTSTAT_IMPDEF2_MASK 0x40 +#define SDW_DP0_INTSTAT_IMPDEF3_MASK 0x80 + +#define SDW_DP0_INTMASK 0x1 +#define SDW_DP0_INTMASK_TEST_FAIL_MASK 0x1 +#define SDW_DP0_INTMASK_PORT_READY_MASK 0x2 +#define SDW_DP0_INTMASK_BRA_FAILURE_MASK 0x4 +#define SDW_DP0_INTMASK_IMPDEF1_MASK 0x20 +#define SDW_DP0_INTMASK_IMPDEF2_MASK 0x40 +#define SDW_DP0_INTMASK_IMPDEF3_MASK 0x80 + +#define SDW_DP0_PORTCTRL 0x2 +#define SDW_DP0_PORTCTRL_PORTDATAMODE_MASK 0x3 +#define SDW_DP0_PORTCTRL_PORTDATAMODE_SHIFT 2 +#define SDW_DP0_PORTCTRL_NEXTINVERTBANK_MASK 0x1 +#define SDW_DP0_PORTCTRL_NEXTINVERTBANK_SHIFT 4 + +#define SDW_DP0_BLOCKCTRL1 0x3 + +#define SDW_DP0_PREPARESTATUS 0x4 + +#define SDW_DP0_PREPARECTRL 0x5 + +#define SDW_DP0_CHANNELEN 0x20 +#define SDW_DP0_SAMPLECTRL1 0x22 +#define SDW_DP0_SAMPLECTRL2 0x23 +#define SDW_DP0_OFFSETCTRL1 0x24 +#define SDW_DP0_OFFSETCTRL2 0x25 +#define SDW_DP0_HCTRL 0x26 +#define SDW_DP0_LANECTRL 0x28 + +#define SDW_SCP_INTSTAT_1 0x40 +#define SDW_SCP_INTSTAT1_PARITY_MASK 0x1 +#define SDW_SCP_INTSTAT1_BUS_CLASH_MASK 0x2 +#define SDW_SCP_INTSTAT1_SCP2_CASCADE_MASK 0x80 + +#define SDW_SCP_INTCLEAR1 0x40 +#define SDW_SCP_INTCLEAR1_PARITY_MASK 0x1 +#define SDW_SCP_INTCLEAR1_BUS_CLASH_MASK 0x2 +#define SDW_SCP_INTCLEAR1_SCP2_CASCADE_MASK 0x80 +#define SDW_SCP_INTMASK1 +#define SDW_SCP_INTSTAT2 0x42 +#define SDW_SCP_INTSTAT2_SCP3_CASCADE_MASK 0x80 +#define SDW_SCP_INTSTAT3 0x43 +#define SDW_SCP_CTRL 0x44 +#define SDW_SCP_CTRL_CLK_STP_NOW_MASK 0x1 +#define SDW_SCP_CTRL_CLK_STP_NOW_SHIFT 0x1 +#define SDW_SCP_STAT 0x44 +#define SDW_SCP_STAT_CLK_STP_NF_MASK 0x1 +#define SDW_SCP_SYSTEMCTRL 0x45 +#define SDW_SCP_SYSTEMCTRL_CLK_STP_PREP_SHIFT 0x0 +#define SDW_SCP_SYSTEMCTRL_CLK_STP_MODE_SHIFT 0x1 +#define SDW_SCP_SYSTEMCTRL_WAKE_UP_EN_SHIFT 0x2 +#define SDW_SCP_SYSTEMCTRL_HIGH_PHY_SHIFT 0x3 + +#define SDW_SCP_DEVNUMBER 0x46 +#define SDW_SCP_HIGH_PHY_CHECK 0x47 +#define SDW_SCP_ADDRPAGE1 0x48 +#define SDW_SCP_ADDRPAGE2 0x49 +#define SDW_SCP_KEEPEREN 0x4A +#define SDW_SCP_BANKDELAY 0x4B +#define SDW_SCP_TESTMODE 0x4F +#define SDW_SCP_DEVID_0 0x50 +#define SDW_SCP_DEVID_1 0x51 +#define SDW_SCP_DEVID_2 0x52 +#define SDW_SCP_DEVID_3 0x53 +#define SDW_SCP_DEVID_4 0x54 +#define SDW_SCP_DEVID_5 0x55 + +/* Banked Registers */ +#define SDW_SCP_FRAMECTRL 0x60 +#define SDW_SCP_NEXTFRAME 0x61 + +#define SDW_DPN_INTSTAT 0x0 +#define SDW_DPN_INTSTAT_TEST_FAIL_MASK 0x1 +#define SDW_DPN_INTSTAT_PORT_READY_MASK 0x2 +#define SDW_DPN_INTSTAT_IMPDEF1_MASK 0x20 +#define SDW_DPN_INTSTAT_IMPDEF2_MASK 0x40 +#define SDW_DPN_INTSTAT_IMPDEF3_MASK 0x80 + +#define SDW_DPN_INTCLEAR 0x0 +#define SDW_DPN_INTCLEAR_TEST_FAIL_MASK 0x1 +#define SDW_DPN_INTCLEAR_PORT_READY_MASK 0x2 +#define SDW_DPN_INTCLEAR_IMPDEF1_MASK 0x20 +#define SDW_DPN_INTCLEAR_IMPDEF2_MASK 0x40 +#define SDW_DPN_INTCLEAR_IMPDEF3_MASK 0x80 + +#define SDW_DPN_INTMASK 0x1 +#define SDW_DPN_PORTCTRL 0x2 +#define SDW_DPN_PORTCTRL_PORTFLOWMODE_MASK 0x3 +#define SDW_DPN_PORTCTRL_PORTFLOWMODE_SHIFT 0 +#define SDW_DPN_PORTCTRL_PORTDATAMODE_MASK 0x3 +#define SDW_DPN_PORTCTRL_PORTDATAMODE_SHIFT 2 +#define SDW_DPN_PORTCTRL_NEXTINVERTBANK_MASK 0x1 +#define SDW_DPN_PORTCTRL_NEXTINVERTBANK_SHIFT 4 + +#define SDW_DPN_BLOCKCTRL1 0x3 +#define SDW_DPN_BLOCKCTRL1_WORDLENGTH_MASK 0x3F +#define SDW_DPN_BLOCKCTRL1_WORDLENGTH_SHIFT 0 + +#define SDW_DPN_PREPARESTATUS 0x4 +#define SDW_DPN_PREPARECTRL 0x5 +#define SDW_DPN_PREPARECTRL_CH_PREPARE_MASK 0xFF + +#define SDW_DPN_CHANNELEN 0x20 +#define SDW_DPN_BLOCKCTRL2 0x21 +#define SDW_DPN_SAMPLECTRL1 0x22 +#define SDW_DPN_SAMPLECTRL1_LOW_MASK 0xFF +#define SDW_DPN_SAMPLECTRL2 0x23 +#define SDW_DPN_SAMPLECTRL2_LOW_MASK 0xFF00 +#define SDW_DPN_OFFSETCTRL1 0x24 +#define SDW_DPN_OFFSETCTRL2 0x25 +#define SDW_DPN_HCTRL 0x26 +#define SDW_DPN_HCTRL_HSTART_MASK 0xF +#define SDW_DPN_HCTRL_HSTOP_MASK 0xF +#define SDW_DPN_HCTRL_HSTART_SHIFT 4 +#define SDW_DPN_HCTRL_HSTOP_SHIFT 0 +#define SDW_DPN_BLOCKCTRL3 0x27 +#define SDW_DPN_LANECTRL 0x28 + +#endif diff --git a/include/linux/sdw_bus.h b/include/linux/sdw_bus.h new file mode 100644 index 000000000000..01c846b0c695 --- /dev/null +++ b/include/linux/sdw_bus.h @@ -0,0 +1,1354 @@ +/* + * sdw_bus.h - Definition for SoundWire bus interface. + * + * This header file refers to the MIPI SoundWire 1.0. The comments try to + * follow the same conventions with a capital letter for all standard + * definitions such as Master, Slave, Data Port, etc. When possible, the + * constant numeric values are kept the same as in the MIPI specifications + * + * Copyright (C) 2016 Intel Corp + * Author: Hardik Shah + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * 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; version 2 of the License. + * + * 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. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + */ +#ifndef _LINUX_SDW_BUS_H +#define _LINUX_SDW_BUS_H + +#include /* for struct device */ +#include /* For Name size */ +#include /* For rt mutex */ + + +#define SOUNDWIRE_MAX_DEVICES 11 + +#define SDW_NUM_DEV_ID_REGISTERS 6 + +/* Port flow mode, used to indicate what port flow mode + * slave supports + */ +#define SDW_PORT_FLOW_MODE_ISOCHRONOUS 0x1 +#define SDW_PORT_FLOW_MODE_TX_CONTROLLED 0x2 +#define SDW_PORT_FLOW_MODE_RX_CONTROLLED 0x4 +#define SDW_PORT_FLOW_MODE_ASYNCHRONOUS 0x8 + +/* Bit-mask used to indicate Port capability, OR both bits if + * Port is bidirectional capable + */ +#define SDW_PORT_SOURCE 0x1 +#define SDW_PORT_SINK 0x2 + +/* Mask to specify what type of sample packaging mode + * is supported by port + */ +#define SDW_PORT_BLK_PKG_MODE_BLK_PER_PORT_MASK 0x1 +#define SDW_PORT_BLK_PKG_MODE_BLK_PER_CH_MASK 0x2 + +/* Mask to specify data encoding supported by port */ +#define SDW_PORT_ENCODING_TYPE_TWOS_CMPLMNT 0x1 +#define SDW_PORT_ENCODING_TYPE_SIGN_MAGNITUDE 0x2 +#define SDW_PORT_ENCODING_TYPE_IEEE_32_FLOAT 0x4 + +/* enum sdw_driver_type: There are different driver callbacks for slave and + * master. This is to differentiate between slave driver + * and master driver. Bus driver binds master driver to + * master device and slave driver to slave device using + * this field. Driver populates this field based on whether + * its handling slave or master device. + */ +enum sdw_driver_type { + SDW_DRIVER_TYPE_MASTER = 0, + SDW_DRIVER_TYPE_SLAVE = 1, +}; + +/** + * enum sdw_block_pkg_mode: Block packing mode for the port. + * @SDW_PORT_BLK_PKG_MODE_BLK_PER_PORT: Block packing per port + * @SDW_PORT_BLK_PKG_MODE_BLK_PER_CH: Block packing per channel. + */ +enum sdw_block_pkg_mode { + SDW_PORT_BLK_PKG_MODE_BLK_PER_PORT = 0, + SDW_PORT_BLK_PKG_MODE_BLK_PER_CH = 1, +}; + + +/** + * enum sdw_command_response: Data Port type + * @SDW_COMMAND_OK: Command is Ok. + * @SDW_COMMAND_IGNORED: Command is ignored. + * @SDW_COMMAND_FAILED: Command failed. + */ +enum sdw_command_response { + SDW_COMMAND_OK = 0, + SDW_COMMAND_IGNORED = 1, + SDW_COMMAND_FAILED = 2, +}; +/** + * enum sdw_dpn_type: Data Port type + * @SDW_FULL_DP: Full Data Port supported. + * @SDW_SIMPLIFIED_DP: Simplified Data Port. Following registers are not + * implemented by simplified data port + * DPN_SampleCtrl2, DPN_OffsetCtrl2, DPN_HCtrl and + * DPN_BlockCtrl3 + */ +enum sdw_dpn_type { + SDW_FULL_DP = 0, + SDW_SIMPLIFIED_DP = 1, +}; + +/** + * enum sdw_dpn_grouping: Maximum block group count supported. + * @SDW_BLOCKGROUPCOUNT_1: Maximum Group count 1 supported. + * @SDW_BLOCKGROUPCOUNT_2: Maximum Group count 2 supported. + * @SDW_BLOCKGROUPCOUNT_3: Maximum Group count 3 supported. + * @SDW_BLOCKGROUPCOUNT_4: Maximum Group count 4 supported. + */ +enum sdw_dpn_grouping { + SDW_BLOCKGROUPCOUNT_1 = 0, + SDW_BLOCKGROUPCOUNT_2 = 1, + SDW_BLOCKGROUPCOUNT_3 = 2, + SDW_BLOCKGROUPCOUNT_4 = 3, +}; + +/** + * enum sdw_prep_ch_behavior: Specifies the dependencies between + * Channel Prepare sequence and bus + * clock configuration.This property is not + * required for ports implementing a + * Simplified ChannelPrepare State Machine (SCPSM) + * @SDW_CH_PREP_ANY_TIME: Channel Prepare can happen at any bus clock rate + * @SDW_CH_PREP_AFTER_BUS_CLK_CHANGE: : Channel Prepare sequence needs to + * happen after bus clock is changed to a + * frequency supported by this mode or + * compatible modes described by the next field. + * This may be required, e.g. when the Slave + * internal audio clocks are derived from the + * bus clock. + */ +enum sdw_prep_ch_behavior { + SDW_CH_PREP_ANY_TIME = 0, + SDW_CH_PREP_AFTER_BUS_CLK_CHANGE = 1, +}; + +/** + * enum sdw_slave_status: Slave status reported in PING frames + * @SDW_SLAVE_STAT_NOT_PRESENT: Slave is not present. + * @SDW_SLAVE_STAT_ATTACHED_OK: Slave is Attached to the bus. + * @SDW_SLAVE_STAT_ALERT: Some alert condition on the Slave. + * @SDW_SLAVE_STAT_RESERVED: Reserved. + */ +enum sdw_slave_status { + SDW_SLAVE_STAT_NOT_PRESENT = 0, + SDW_SLAVE_STAT_ATTACHED_OK = 1, + SDW_SLAVE_STAT_ALERT = 2, + SDW_SLAVE_STAT_RESERVED = 3, +}; + +enum sdw_stream_type { + SDW_STREAM_PCM = 0, + SDW_STREAM_PDM = 1, +}; + + +enum sdw_rt_state { + SDW_RT_INITIALIZED = 0, + SDW_RT_CONFIGURED = 1, +}; + +/** + * enum sdw_ch_prepare_mode: Channel prepare mode. + * @SDW_SIMPLIFIED_CP_SM: Simplified channel prepare. + * @SDW_CP_SM: Normal channel prepare. + */ +enum sdw_ch_prepare_mode { + SDW_SIMPLIFIED_CP_SM = 0, + SDW_CP_SM = 1, +}; + +/** + * enums dfw_clk_stop_prepare: Clock Stop prepare mode. + * @SDW_CLOCK_STOP_MODE_0: Clock Stop mode 0 + * @SDW_CLOCK_STOP_MODE_1: Clock Stop mode 1 + */ +enum sdw_clk_stop_mode { + SDW_CLOCK_STOP_MODE_0 = 0, + SDW_CLOCK_STOP_MODE_1 = 1, +}; + +/** + * enum sdw_data_direction: Data direction w.r.t Port. For e.g for playback + * between the Master and Slave, where Slave + * is codec, data direction for the Master + * port will be OUT, since its transmitting + * the data, while for the Slave (codec) it + * will be IN, since its receiving the data. + * @SDW_DATA_DIR_IN: Data is input to Port. + * @SDW_DATA_DIR_OUT: Data is output from Port. + */ +enum sdw_data_direction { + SDW_DATA_DIR_IN = 0, + SDW_DATA_DIR_OUT = 1, +}; + +/* Forward declaration of the data structures */ +struct sdw_master; +struct sdw_slv; +struct sdw_msg; +struct sdw_bra_block; +struct sdw_mstr_driver; + +/** + * struct port_audio_mode_properties: Audio properties for the Port + * + * @max_frequency: Maximum frequency Port can support for the clock. + * The use of max_ and min_ frequency requires num_freq_config + * to be zero + * @min_frequency: Minimum frequency Port can support for the clock. + * @num_freq_configs: Array size for the frequencies supported by Port. + * @freq_supported: Array of frequencies supported by the Port. + * @glitchless_transitions_mask: Glitch transition mask from one mode to + * other mode. Each bit refers to a mode + * number. + */ + +struct port_audio_mode_properties { + unsigned int max_frequency; + unsigned int min_frequency; + unsigned int num_freq_configs; + unsigned int *freq_supported; + unsigned int max_sampling_frequency; + unsigned int min_sampling_frequency; + unsigned int num_sampling_freq_configs; + unsigned int *sampling_freq_config; + enum sdw_prep_ch_behavior ch_prepare_behavior; + unsigned int glitchless_transitions_mask; +}; + +/** + * struct sdw_slv_addr: Structure representing the device_id and + * and SoundWire logical Slave address. + * @dev_id: 6-byte device id of the Slave + * @slv_number: Logical SoundWire Slave number, in the range [1..11] + * @assigned: Logical address is assigned to some Slave or not + * @status: What is the current state of the slave. + * + */ +struct sdw_slv_addr { + struct sdw_slv *slave; + u8 dev_id[SDW_NUM_DEV_ID_REGISTERS]; + u8 slv_number; + bool assigned; + enum sdw_slave_status status; +}; + +/** + * struct sdw_slv_dpn_capabilities: Capabilities of the Data Port, other than + * Data Port 0 for SoundWire Slave + * @port_direction: Direction of the Port. Sink or Source or bidirectional. + * Set appropriate bit mak based on port capabilities. + * @port_number: Port number. + * @max_word_length: Maximum length of the sample word. + * @min_word_length: Minimum length of sample word. + * @num_word_length: Length of supported word length buffer. + * The use of max_ and min_ word length requires + * num_word_length to be zero + * @word_length_buffer: Array of the supported word length. + * @dpn_type: Type of Data Port. Simplified or Normal data port. + * @dpn_grouping: Max Block group count supported for this Port. + * @prepare_ch: Channel prepare scheme. Simplified channel prepare or Normal + * channel prepare. + * @imp_def_intr_mask: Implementation defined interrupt mask. + * @min_ch_num: Minimum number of channels supported. + * @max_ch_num: Maximum number of channels supported. + * @num_ch_supported: Buffer length for the channels supported. + * The use of max_ and min_ ch_num requires + * num_ch_supported to be zero + * @ch_supported: Array of the channel supported. + * @port_flow_mode_mask: Transport flow modes supported by Port. + * @block_packing_mode_mask: Block packing mode mask. + * @port_encoding_type_mask: Port Data encoding type mask. + * @num_audio_modes: Number of audio modes supported by device. + * @mode_properties: Port audio mode properties buffer of size num_audio_modes + */ + +struct sdw_slv_dpn_capabilities { + unsigned int port_direction; + unsigned int port_number; + unsigned int max_word_length; + unsigned int min_word_length; + unsigned int num_word_length; + unsigned int *word_length_buffer; + enum sdw_dpn_type dpn_type; + enum sdw_dpn_grouping dpn_grouping; + enum sdw_ch_prepare_mode prepare_ch; + unsigned int imp_def_intr_mask; + unsigned int min_ch_num; + unsigned int max_ch_num; + unsigned int num_ch_supported; + unsigned int *ch_supported; + unsigned int port_flow_mode_mask; + unsigned int block_packing_mode_mask; + unsigned int port_encoding_type_mask; + unsigned int num_audio_modes; + struct port_audio_mode_properties *mode_properties; +}; + +/** + * struct sdw_slv_bra_capabilities: BRA Capabilities of the Slave. + * @max_bus_frequency: Maximum bus frequency of this mode, in Hz + * @min_bus_frequency: Minimum bus frequency of this mode, in Hz + * When using min-max properties, all values in the defined + * range are allowed. Use the config list in the next field + * if only discrete values are supported. + * @num_bus_config_frequency: Number of discrete bus frequency configurations + * @bus_config_frequencies: Array of bus frequency configs. + * @max_data_per_frame: Maximum Data payload, in bytes per frame. + * Excludes header, CRC, footer. Maximum value is 470 + * @min_us_between_transactions: Amount of delay, in microseconds, + * required to be inserted between BRA transactions. + * Use if Slave needs idle time between BRA transactions. + * @max_bandwidth: Maximum bandwidth (in bytes/s) that can be written/read + * (header, CRCs, footer excluded) + * @mode_block_alignment: Size of basic block in bytes. The Data payload + * size needs to be a multiple of this basic block and + * padding/repeating of the same value is required for + * transactions smaller than this basic block. + */ + +struct sdw_slv_bra_capabilities { + unsigned int max_bus_frequency; + unsigned int min_bus_frequency; + unsigned int num_bus_config_frequency; + unsigned int *bus_config_frequencies; + unsigned int max_data_per_frame; + unsigned int min_us_between_transactions; + unsigned int max_bandwidth; + unsigned int mode_block_alignment; +}; + +/** + * struct sdw_slv_dp0_capabilities: Capabilities of the Data Port 0 of Slave. + * + * @max_word_length: Maximum word length supported by the Data Port. + * @min_word_length: Minimum word length supported by the Data Port. + * @num_word_length: Array size of the buffer containing the supported + * word lengths. + * The use of max_ and min_ word length requires + * num_word_length to be zero + * @word_length_buffer: Array containing supported word length. + * @bra_use_flow_control: Flow control is required or not for bra block + * transfer. + * @bra_initiator_supported: Can Slave be BRA initiator. + * @ch_prepare_mode: Type of channel prepare scheme. Simplified or Normal + * channel prepare. + * @impl_def_response_supported;: If True (nonzero), implementation-defined + * response is supported. This information may be used + * by a device driver to request that a generic bus + * driver forwards the response to the client device + * driver. + * @imp_def_intr_mask: Implementation defined interrupt mask for DP0 Port. + * @impl_def_bpt_supported: If True (nonzero), implementation-defined + * Payload Type is supported. This information is used + * to bypass the BRA protocol and may only be of + * interest when a device driver is aware of the + * Capabilities of the Master controller and Slave + * devices. + * @slave_bra_cap: BRA capabilities of the Slave. + */ + +struct sdw_slv_dp0_capabilities { + unsigned int max_word_length; + unsigned int min_word_length; + unsigned int num_word_length; + unsigned int *word_length_buffer; + unsigned int bra_use_flow_control; + bool bra_initiator_supported; + enum sdw_ch_prepare_mode ch_prepare_mode; + bool impl_def_response_supported; + unsigned int imp_def_intr_mask; + bool impl_def_bpt_supported; + struct sdw_slv_bra_capabilities slave_bra_cap; +}; + +/** struct sdw_slv_capabilities: Capabilities of the SoundWire Slave. This + * is public structure for slave drivers to + * updated its capability to bus driver. + * + * @wake_up_unavailable: Slave is capable of waking up the Master. + * @test_mode_supported: Slave supports test modes. + * @clock_stop1_mode_supported: Clock stop 1 mode supported by this Slave. + * @simplified_clock_stop_prepare: Simplified clock stop prepare + * supported. + * @highphy_capable: Slave is highphy_capable or not? + * @paging_supported: Paging registers supported for Slave? + * @bank_delay_support: Bank switching delay for Slave + * @port_15_read_behavior: Slave behavior when the Master attempts a Read to + * the Port15 alias + * 0: Command_Ignored + * 1: Command_OK, Data is OR of all registers + * @sdw_dp0_supported: DP0 is supported by Slave. + * @sdw_dp0_cap: Data Port 0 Capabilities of the Slave. + * @num_of_sdw_ports: Number of SoundWire Data ports present. The representation + * assumes contiguous Port numbers starting at 1. + * @sdw_dpn_cap: Capabilities of the SoundWire Slave ports. + */ + +struct sdw_slv_capabilities { + bool wake_up_unavailable; + bool test_mode_supported; + bool clock_stop1_mode_supported; + bool simplified_clock_stop_prepare; + bool highphy_capable; + bool paging_supported; + bool bank_delay_support; + unsigned int port_15_read_behavior; + bool sdw_dp0_supported; + struct sdw_slv_dp0_capabilities *sdw_dp0_cap; + int num_of_sdw_ports; + struct sdw_slv_dpn_capabilities *sdw_dpn_cap; +}; + + +/** + * struct sdw_slv: Represents SoundWire Slave device + * (similar to 'i2c_client' on I2C) + * This is not public structure. Maintained by + * bus driver internally. + * @dev: Driver model representation of the device + * @slave_cap_updated: Did slave device driver updated slave capabilties + * to bus. + * @name: Name of the driver to use with the device. + * @dev_id: 6-byte unique device identification. + * @driver: Slave's driver, pointer to access routine. + * @mstr: SoundWire Master, managing the bus on which this Slave is + * @slv_number: Logical address of the Slave, assigned by bus driver + * @node: Node to add the Slave to the list of Slave devices managed + * by same Master. + * @port_ready: Port ready completion flag for each Port of the Slave; + * @sdw_slv_cap: Slave Capabilities. + */ +struct sdw_slv { + struct device dev; + bool slave_cap_updated; + char name[SOUNDWIRE_NAME_SIZE]; + u8 dev_id[6]; + struct sdw_slv_addr *slv_addr; + struct sdw_slave_driver *driver; + struct sdw_master *mstr; + u8 slv_number; + struct list_head node; + struct completion *port_ready; + struct sdw_slv_capabilities sdw_slv_cap; +}; +#define to_sdw_slave(d) container_of(d, struct sdw_slv, dev) + +/** + * struct sdw_bus_params: Bus params for the Slave to be ready for next + * bus changes. + * @num_rows: Number of rows in new frame to be effective. + * @num_cols: Number of columns in new frame to be effective. + * @bus_clk_freq: Clock frequency for the bus. + * @bank: Register bank, which Slave driver should program for + * implementation define Slave registers. This is the + * inverted value of the current bank. + */ + +struct sdw_bus_params { + int num_rows; + int num_cols; + int bus_clk_freq; + int bank; +}; + +/** + * struct sdw_slave_driver: Manage SoundWire generic/Slave device driver + * @driver_type: To distinguish between master and slave driver. Set and + * used by bus driver. + * @probe: Binds this driver to a SoundWire Slave. + * @remove: Unbinds this driver from the SoundWire Slave. + * @shutdown: Standard shutdown callback used during powerdown/halt. + * @suspend: Standard suspend callback used during system suspend + * @resume: Standard resume callback used during system resume + * @driver: Generic driver structure, according to driver model. + * @handle_impl_def_interrupts: Slave driver callback, for status of the + * Slave other than "REPORT_PRESENT". There may be + * jack detect, pll locked kind of status update + * interrupt required by Slave, which Slave need to + * handle in impl_defined way, using implementation + * defined interrupts. This is callback function to + * Slave to handle implementation defined interrupts. + * @handle_bus_changes: Slave callback function to let Slave configure + * implementation defined registers prior to any bus + * configuration changes. Bus configuration changes + * will be signaled by a bank switch initiated by the bus + * driver once all Slaves drivers have performed their + * imp-def configuration sequence (if any). + * If this callback is not implemented the bus driver + * will assume the Slave can tolerate bus configurations + * changes at any time. + * + * @handle_pre_port_prepare: Slave driver callback to allow Slave Port to be + * prepared by configuring impl defined register + * as part of Port prepare state machine. + * This fn is called before DPn_Prepare ctrl is + * written. Before this function is + * called Port state is un-prepared (CP_Stopped). + * This is optional based on any impl + * defined register needs to be set by Slave + * driver before Port is prepared. + * @handle_post_port_prepare: Slave driver callback to allow Slave Port to be + * prepared by configuring impl defined register + * as part of Port prepare state machine. + * This is called after DPn_Prepare + * ctrl is written, and DPn_status reports as + * Port prepared(CP_Ready). This is optional + * based on any impl defined register needs to + * be set by Slave driver once Port is ready. + * @handle_pre_port_unprepare: Slave driver callback to allow Slave Port to be + * un-prepared by configuring impl defined register + * as part of Port un-prepare state machine. + * This is called before DPn_Prepare ctrl is + * written. Before this function is called + * Port state is ready (CP_Ready). + * This is optional based on any impl + * defined register needs to be set by Slave + * driver before Port is un-prepared. + * @handle_post_port_unprepare: Slave driver callback to allow Slave Port to be + * un-prepared by configuring impl defined register + * as part of Port prepare state machine. + * This is called after DPn_Prepare + * ctrl is written, and DPn_status reports as + * Port un-prepared (CP_Stopped). + * This is optional based on any impl defined + * register needs to be set by Slave driver once + * Port is un-prepared. + * + * @id_table: List of SoundWire Slaves supported by this driver + */ +struct sdw_slave_driver { + enum sdw_driver_type driver_type; + int (*probe)(struct sdw_slv *swdev, const struct sdw_slv_id *); + int (*remove)(struct sdw_slv *swdev); + void (*shutdown)(struct sdw_slv *swdev); + int (*suspend)(struct sdw_slv *swdev, + pm_message_t pmesg); + int (*resume)(struct sdw_slv *swdev); + struct device_driver driver; + int (*handle_impl_def_interrupts)(struct sdw_slv *swdev, + struct sdw_impl_def_intr_stat *intr_status); + int (*handle_bus_changes)(struct sdw_slv *swdev, + struct sdw_bus_params *params); + int (*handle_pre_port_prepare)(struct sdw_slv *swdev, + int port, int ch_mask, int bank); + int (*handle_post_port_prepare)(struct sdw_slv *swdev, + int port, int ch_mask, int bank); + int (*handle_pre_port_unprepare)(struct sdw_slv *swdev, + int port, int ch_mask, int bank); + int (*handle_post_port_unprepare)(struct sdw_slv *swdev, + int port, int ch_mask, int bank); + const struct sdw_slv_id *id_table; +}; +#define to_sdw_slave_driver(d) container_of(d, struct sdw_slave_driver, driver) + +/** + * struct sdw_mstr_dpn_capabilities: Capabilities of the Data Port, other than + * Data Port 0 for SoundWire Master + * @port_direction: Direction of the Port. + * @port_number: Port number. + * @max_word_length: Maximum length of the sample word. + * @min_word_length: Minimum length of sample word. + * @num_word_length: Length of supported word length buffer. This should be + * 0 in order to use min and max. + * @word_length_buffer: Array of the supported word length. + * @dpn_type: Type of Data Port. + * @dpn_grouping: Max Block count grouping supported for this Port. if + * slave supports only 1 block group count, than DPN_BlockCtrl2 + * wont be programmed. + * @min_ch_num: Minimum number of channels supported. + * @max_ch_num: Maximum number of channels supported. + * @num_ch_supported: Buffer length for the channels supported.This should be + * 0 in order to use min and max. + * @ch_supported: Array of the channel supported. + * @port_mode_mask: Transport modes supported by Port. + * @block_packing_mode_mask: Block packing mode mask. + */ + +struct sdw_mstr_dpn_capabilities { + unsigned int port_direction; + unsigned int port_number; + unsigned int max_word_length; + unsigned int min_word_length; + unsigned int num_word_length; + unsigned int *word_length_buffer; + enum sdw_dpn_type dpn_type; + enum sdw_dpn_grouping dpn_grouping; + unsigned int min_ch_num; + unsigned int max_ch_num; + unsigned int num_ch_supported; + unsigned int *ch_supported; + unsigned int port_mode_mask; + unsigned int block_packing_mode_mask; +}; + +/** + * struct sdw_mstr_dp0_capabilities: Capabilities of the Data Port 0 of Slave. + * + * @max_word_length: Maximum word length supported by the Data Port. + * @min_word_length: Minimum word length supported by the Data Port. + * @num_word_length: Array size of the buffer containing the supported + * word lengths. + * @word_length_buffer: Array containing supported word length. + * @bra_max_data_per_frame: Maximum Data size per BRA. + */ +struct sdw_mstr_dp0_capabilities { + unsigned int max_word_length; + unsigned int min_word_length; + unsigned int num_word_length; + unsigned int *word_length_buffer; + unsigned int bra_max_data_per_frame; +}; + +/** + * struct sdw_master_capabilities: Capabilities of the Master. + * This is filled by the software registering Master. + * @base_clk_freq: Highest base frequency at which Master can be driven + * This is in Hz. + * @monitor_handover_supported: Does Master support monitor handover. + * @highphy_capable: Is Master Highphy capable? + * @sdw_dp0_supported: Data port0 supported? + * @sdw_dp0_cap: Capabilities of the dataport 0 of the Master. + * @num_data_ports: Array size for the number of Data ports present in + * Master. + * @sdw_dpn_cap: Array containing information about SoundWire Master + * Data Port Capabilities + * + */ +struct sdw_master_capabilities { + unsigned int base_clk_freq; + bool monitor_handover_supported; + bool highphy_capable; + bool sdw_dp0_supported; + struct sdw_mstr_dp0_capabilities sdw_dp0_cap; + unsigned int num_data_ports; + struct sdw_mstr_dpn_capabilities *sdw_dpn_cap; + +}; + +/** + * struct sdw_master: Master device controller on SoundWire bus. + * (similar to 'Master' on I2C) + * @owner: Owner of this module. Generally THIS module. + * @dev: Slave interface for this driver; + * @nr: Bus number of SoundWire Master bus. Also referred to as link number. + * @slv_list: List of SoundWire Slaves registered to the bus. + * @name: Name of the Master driver. + * @sdw_addr: Array containing Slave SoundWire bus Slave address information. + * @bus_lock: Global lock for bus functions. + * @num_slv: Number of SoundWire Slaves assigned logical address. + * @wq: Workqueue instance for Slave detection. + * @mstr_capabilities: Capabilities of the SoundWire Master controller. + * @driver: Driver handling the Master. + * @slv_released: Flag to indicate Slave release completion. Internally used + * by bus driver. + * @timeout: Timeout before getting response from Slave. + * @retries: How many times to retry before giving up on Slave response. + * @ssp_tag_synchronized: Do bus driver needs to set SSP tag based on + * sample interval of of all streams. + * @link_sync_mask: Bit mask representing all the other controller links + * with which this link is synchronized. + * + */ +struct sdw_master { + struct module *owner; + struct device dev; + unsigned int nr; + struct list_head slv_list; + char name[SOUNDWIRE_NAME_SIZE]; + struct sdw_slv_addr sdw_addr[SOUNDWIRE_MAX_DEVICES + 1]; + struct rt_mutex bus_lock; + u8 num_slv; + struct workqueue_struct *wq; + struct sdw_master_capabilities mstr_capabilities; + struct sdw_mstr_driver *driver; + struct completion slv_released; + struct list_head mstr_rt_list; + int timeout; + int retries; + bool ssp_tag_synchronized; + int link_sync_mask; +}; +#define to_sdw_master(d) container_of(d, struct sdw_master, dev) + + +/** struct sdw_port_params: This is used to program the + * Data Port based on Data Port + * stream params. These parameters cannot be changed + * dynamically + * + * @num : Port number for which params are there. + * @word_length: Word length of the Port + * @port_flow_mode: Port Data flow mode. + * @port_data_mode: Test mode or normal mode. + */ +struct sdw_port_params { + int num; + int word_length; + int port_flow_mode; + int port_data_mode; +}; + +/** struct sdw_transport_params: This is used to program the + * Data Port based on Data Port + * transport params. These parameters may be changed + * dynamically based on Frame Shape changes and bandwidth + * allocation + * + * @num : Port number for which params are there. + * @blockgroupcontrol_valid: Does Port implement block group control? + * @blockgroupcontrol: Block group control value. + * @sample_interval: Sample interval. + * @offset1: Blockoffset of the payload Data. + * @offset2: Blockoffset of the payload Data. + * @hstart: Horizontal start of the payload Data. + * @hstop: Horizontal stop of the payload Data. + * @blockpackingmode: Block per channel or block per Port. + * @lanecontrol: Data lane Port uses for Data transfer. + */ +struct sdw_transport_params { + int num; + bool blockgroupcontrol_valid; + int blockgroupcontrol; /* DPN_BlockCtrl2 */ + int sample_interval; /* DPN_SampleCtrl1 and DPN_SampleCtrl2 */ + int offset1; /* DPN_OffsetCtrl1 */ + int offset2; /* DPN_OffsetCtrl2 */ + int hstart; /* DPN_HCtrl */ + int hstop; /* DPN_HCtrl */ + int blockpackingmode; /* DPN_BlockCtrl3 */ + int lanecontrol; /* DPN_LaneCtrl */ +}; + +/** struct sdw_prepare_ch: Prepare/Un-prepare the Data Port channel. + * + * @num : Port number for which params are there. + * @ch_mask: prepare/un-prepare channels specified by ch_mask + * @prepare: Prepare/Un-prepare channel + */ +struct sdw_prepare_ch { + int num; + int ch_mask; + bool prepare; +}; + +/** struct sdw_activate_ch: Activate/Deactivate Data Port channel. + * + * @num : Port number for which params are there. + * @ch_mask: Active channel mask for this port. + * @activate: Activate/Deactivate channel + */ +struct sdw_activate_ch { + int num; + int ch_mask; + bool activate; +}; + +/** + * struct sdw_master_port_ops: Callback functions from bus driver + * to Master driver to set Master + * Data ports. Since Master registers + * are not standard, commands are passed + * to Master from bus and Master + * converts commands to register settings + * based on Master register map. + * @dpn_set_port_params: Set the Port parameters for the Master Port. + * @dpn_set_port_transport_params: Set transport parameters for the + * Master Port. + * @dpn_port_prepare_ch: Prepare/Un-prepare the Master channels of the Port + * @dpn_port_prepare_ch_pre: Called before calling dpn_port_prepare_ch, if + * Master driver needs to do update + * register settings before ch_prepare + * @dpn_port_prepare_ch_post: Called after calling dpn_port_prepare_ch, if + * Master driver needs to do some + * register settings after ch_prepare + * @dpn_port_activate_ch: Activate the channels of particular Master Port + * @dpn_port_activate_ch_pre: Called before calling dpn_port_activate_ch, if + * Master driver needs to some register + * setting before activating channel. + * @dpn_port_activate_ch_post : Called after calling dpn_port_activate_ch, if + * Master driver needs to some register + * setting after activating channel. + */ +struct sdw_master_port_ops { + int (*dpn_set_port_params)(struct sdw_master *mstr, + struct sdw_port_params *port_params, int bank); + int (*dpn_set_port_transport_params)(struct sdw_master *mstr, + struct sdw_transport_params *transport_params, + int bank); + int (*dpn_port_prepare_ch)(struct sdw_master *mstr, + struct sdw_prepare_ch *prepare_ch); + int (*dpn_port_prepare_ch_pre)(struct sdw_master *mstr, + struct sdw_prepare_ch *prepare_ch); + int (*dpn_port_prepare_ch_post)(struct sdw_master *mstr, + struct sdw_prepare_ch *prepare_ch); + int (*dpn_port_activate_ch)(struct sdw_master *mstr, + struct sdw_activate_ch *activate_ch, int bank); + int (*dpn_port_activate_ch_pre)(struct sdw_master *mstr, + struct sdw_activate_ch *activate_ch, int bank); + int (*dpn_port_activate_ch_post)(struct sdw_master *mstr, + struct sdw_activate_ch *activate_ch, int bank); +}; + +/** + * struct sdw_master_ops: Callback operations from bus driver to Master + * Master driver.Bus driver calls these + * functions to control the bus parameters + * in Master hardware specific way. Its + * like i2c_algorithm to access the bus + * in Master specific way. + * + * Slave registers are standard. + * @xfer_msg: Callback function to Master driver to read/write + * Slave registers. + * @xfer_bulk: Callback function to Master driver for bulk transfer. + * @monitor_handover: Allow monitor to be owner of command, if requested. + * @set_ssp_interval: Set SSP interval. + * @set_clock_freq: Set the clock frequency based on bandwidth requirement. + * Controller driver sets the frequency in hardware + * specific way. + * + */ + +struct sdw_master_ops { + enum sdw_command_response (*xfer_msg)(struct sdw_master *mstr, + struct sdw_msg *msg, bool program_scp_addr_page); + int (*xfer_bulk)(struct sdw_master *mstr, + struct sdw_bra_block *block); + int (*monitor_handover)(struct sdw_master *mstr, + bool handover); + int (*set_ssp_interval)(struct sdw_master *mstr, + int ssp_interval, int bank); + int (*set_clock_freq)(struct sdw_master *mstr, + int cur_clk_freq, int bank); + int (*set_frame_shape)(struct sdw_master *mstr, + int col, int row, int bank); +}; + +/** + * struct sdw_mstr_driver: Manage SoundWire Master/Master device driver + * @driver_type: To distinguish between master and slave driver. Set and + * used by bus driver. + * @probe: Binds this driver to a SoundWire Master. + * @remove: Unbinds this driver from the SoundWire Master. + * @shutdown: Standard shutdown callback used during powerdown/halt. + * @suspend: Standard suspend callback used during system suspend + * @resume: Standard resume callback used during system resume + * @driver: SoundWire device drivers should initialize name and owner field of + * this structure. + * @mstr_ops: Callback operations from bus driver to Master driver for + * programming and controlling bus parameters and to program + * Slave registers. + * @mstr_port_ops: Commands to setup the Master ports. Master register + * map is not defined by standard. So these ops represents the + * commands to setup Master ports. + * @id_table: List of SoundWire devices supported by this driver. + */ +struct sdw_mstr_driver { + enum sdw_driver_type driver_type; + int (*probe)(struct sdw_master *sdwmstr, const struct sdw_master_id *); + int (*remove)(struct sdw_master *sdwmstr); + void (*shutdown)(struct sdw_master *sdwmstr); + int (*suspend)(struct sdw_master *sdwmstr, + pm_message_t pmesg); + int (*resume)(struct sdw_master *sdwmstr); + struct device_driver driver; + struct sdw_master_ops *mstr_ops; + struct sdw_master_port_ops *mstr_port_ops; + const struct sdw_master_id *id_table; +}; +#define to_sdw_mstr_driver(d) container_of(d, struct sdw_mstr_driver, driver) + +/** + * struct sdw_msg : Message to be sent on bus. This is similar to i2c_msg + * on I2C bus. + * Actually controller sends the message on bus + * in hardware specific way. This interface is from + * bus driver to Slaves. + * @slave_addr: Slave address + * @ssp_tag: send message at ssp_tag. It should be used when a command needs + * to be issued during the next SSP. For all normal reads/writes + * this should be zero. This will be used for broadcast write + * to SCP_FrameCtrl register by bus driver only. Normally + * slave driver should always set ssp_tag to 0. + * @addr_page1: SCP address page 1 + * @addr_page2: SCP address page 2 + * @flag: Message to be read or write. + * @addr: Address of the register to be read; + * @len: Length of the message to be read. Successive increment in the + * register address for every message. + * @buf: Buf to be written or read from the register. + */ +struct sdw_msg { + u8 slave_addr; + bool ssp_tag; + u8 addr_page1; + u8 addr_page2; +#define SDW_MSG_FLAG_READ 0x0 +#define SDW_MSG_FLAG_WRITE 0x1 + u8 flag; + u16 addr; + u16 len; + u8 *buf; +}; + +/** + * sdw_stream_config: Stream configuration of the device. This includes + * Master and Slave. + * @frame_rate: Audio frame rate of the stream. + * @channel_count: Channel count of the stream. + * @bps: Number of bits per audio sample. + * @direction: Direction of the Data. What is the data direction for the + * device calling stream_config. This is w.r.t device. + * @type: Stream type PCM or PDM + * + */ +struct sdw_stream_config { + unsigned int frame_rate; + unsigned int channel_count; + unsigned int bps; + enum sdw_data_direction direction; + enum sdw_stream_type type; +}; + +/** + * sdw_port_cfg: SoundWire Port configuration, Configuration is done + * for all port of all the devices which are part of stream. + * All the ports of stream handles same pcm parameters accept + * channels. e.g Master may handle stereo channels using single + * port, but slave may handle Left and Right channel on one + * port each. + * @port_num: Port number to be configured + * @ch_mask: Which channels needs to be activated for this Port. + */ +struct sdw_port_cfg { + int port_num; + unsigned int ch_mask; +}; + +/** + * sdw_port_config: List of the ports handled by slave or master + * for particular stream. Both slave and master calls + * this with ports they trasmit/recevie onfor particular + * stream. + * @num_ports: Number of ports to be configured. + * @port_cfg : Port configuration for each Port. + */ +struct sdw_port_config { + unsigned int num_ports; + struct sdw_port_cfg *port_cfg; +}; + +/** + * struct sdw_bra_block: Data block to be sent/received using SoundWire + * bulk transfer protocol + * @slave_addr: Slave logical address from/to which transfer + * needs to take place. + * @operation: Read operation or write operation. + * @num_bytes: Number of Data bytes to be transferred. + * @reg_offset: Register offset from where the first byte to read/write. + * @values: Array containing value for write operation and to be filled + * for read operation. + */ +struct sdw_bra_block { + int slave_addr; + int cmd; + int num_bytes; + int reg_offset; + u8 *values; +}; + +/** + * Struct sdw_slave_status: Status of all the SoundWire Slave devices. + * @status: Array of status of SoundWire Slave devices. 0 is also + * a soundwire device during enumeration, so adding +1 for + * that. Actual number fo devices that can be supported are + * 11 as defined by SOUNDWIRE_MAX_DEVICES + */ +struct sdw_status { + enum sdw_slave_status status[SOUNDWIRE_MAX_DEVICES + 1]; +}; + +/** + * sdw_add_master_controller: Add SoundWire Master controller interface + * @mstr: Controller to be registered as SoundWire Master interface. + * This is to be called for each Master interface. + * This is same as I2C, where each adapter register specifies one + * pair of clock and Data lines (link). + */ +int sdw_add_master_controller(struct sdw_master *mstr); + +/** + * sdw_del_master_controller: Master tear-down. + * Master added with the "sdw_add_master_controller" API is teared down + * using this API. + * @mstr: Master to be teared down + */ +void sdw_del_master_controller(struct sdw_master *mstr); + +/** + * sdw_mstr_driver_register: SoundWire Master driver registration with SDW bus. + * This API will register the Master driver with the + * SoundWire bus. It is typically called from the + * driver's module-init function. + * @drv: Master Driver to be associated with device. + * + */ +int __sdw_mstr_driver_register(struct module *owner, + struct sdw_mstr_driver *driver); +#define sdw_mstr_driver_register(drv) \ + __sdw_mstr_driver_register(THIS_MODULE, drv) + +/** + * sdw_mstr_driver_unregister: Undo effects of sdw_mstr_driver_register + * @drv: SDW Master driver to be unregistered + */ +void sdw_mstr_driver_unregister(struct sdw_mstr_driver *drv); + +/** + * __sdw_slave_driver_register: SoundWire Slave driver registration with + * SDW bus. This API will register the Slave + * driver with the SoundWire bus. It is typically + * called from the driver's module-init function. + * @drv: Driver to be associated with Slave. + */ +int __sdw_slave_driver_register(struct module *owner, + struct sdw_slave_driver *drv); +#define sdw_slave_driver_register(drv) \ + __sdw_slave_driver_register(THIS_MODULE, drv) + +/** + * sdw_register_slave_capabilities: Register slave device capabilties to the + * bus driver. Since bus driver handles bunch + * of slave register programming it should + * be aware of slave device capabilties. + * Slave device is attached to bus based on + * enumeration. Once slave driver is attached + * to device and probe of slave driver is called + * on device and driver binding, slave driver + * should call this function to register its + * capabilties to bus. This should be the very + * first function to bus driver from slave driver + * once slave driver is registered and probed. + * @slave: SoundWire Slave handle + * @cap: Slave capabilities to be updated to bus driver. + */ +int sdw_register_slave_capabilities(struct sdw_slv *slave, + struct sdw_slv_capabilities *cap); + +/** + * sdw_slave_driver_unregister: Undo effects of sdw_slave_driver_register + * @drv: SDW Slave driver to be unregistered + */ +void sdw_slave_driver_unregister(struct sdw_slave_driver *drv); + +/** + * sdw_slave_transfer: Transfer SDW message on bus. + * @mstr: Master which will transfer the message. + * @msg: Array of messages to be transferred. + * @num: Number of messages to be transferred, messages include read and write + * messages, but not the ping messages. + */ +int sdw_slave_transfer(struct sdw_master *mstr, struct sdw_msg *msg, int num); + +/** + * sdw_alloc_stream_tag: Allocate stream_tag for each audio stream + * between SoundWire Masters and Slaves. + * (Multiple Masters and Slave in case of + * aggregation) stream_tag is + * unique across system. Stream tag represents the + * independent audio stream which can be controlled + * configure individually. Stream can be split between + * multiple Masters and Slaves. It can also be + * split between multiple ports of the Master and Slave. + * All the stream configuration for Masters and Slaves + * and ports of the Master and Slave for particular + * stream is done on stream_tag as handle. Normally + * stream is between CPU and Codec. CPU dai ops + * allocate the stream tag and programs same to the + * codec dai. If there are multiple codecs attached + * to each CPU DAI, like L and R digital speaker, both + * codecs should be programmed with same stream tag. + * + * + * @uuid: uuid is used to make sure same stream tag gets + * allocated for same uuid. If stream tag is not + * allocated for uuid, it will get allocated and + * uuid-stream tag pair will be saved, for next + * allocation based on uuid. If this is NULL, + * new stream tag will be allocated each time this + * function is called. + * + * + * @stream_tag: Stream tag returned by bus driver. + */ +int sdw_alloc_stream_tag(char *uuid, int *stream_tag); + +/** + * sdw_release_stream_tag: Free the already assigned stream tag. + * + * @stream_tag: Stream tag to be freed. + */ +void sdw_release_stream_tag(int stream_tag); + +/** + * sdw_config_stream: Configure the audio stream. Each stream between + * master and slave, or between slaves has unique + * stream tag. + * Master and Slave attaches to + * the stream using the unique stream_tag for each + * stream between Master(s) and Slave(s). Both + * Master and Slave driver call this function to + * attach to stream and let bus driver know the + * stream params. Master and Slave calls this function + * typically as part of hw_params ops of their respective + * DAIs to let bus driver know about stream parameters. + * Stream parameters between Tx and Rx direction + * should match. Function is reference counted so + * multiple Master and Slaves attached to particular + * stream can call to setup stream config. + * Master calls this function with Slave handle as + * NULL. + * @mstr: Master handle, + * @slave: SoundWire Slave handle, Null if stream configuration is called + * by Master driver. + * @stream_config: Stream configuration for the SoundWire audio stream. + * @stream_tag: Stream_tag representing the audio stream. All Masters and Slaves + * part of the same stream will have same stream tag. So bus drivers + * know which all Masters and Slaves are part of stream. + * + */ +int sdw_config_stream(struct sdw_master *mstr, + struct sdw_slv *slave, + struct sdw_stream_config *stream_config, + unsigned int stream_tag); + +/** + * sdw_release_stream: De-associates Master(s) and Slave(s) from stream. Reverse + * effect of the sdw_config_stream + * + * @mstr: Master handle, + * @slave: SoundWire Slave handle, Null if stream configuration is called + * by Master driver. + * @stream_tag: Stream_tag representing the audio stream. All Masters and Slaves + * part of the same stream has same stream tag. So bus drivers + * know which all Masters and Slaves are part of stream. + * + */ +int sdw_release_stream(struct sdw_master *mstr, + struct sdw_slv *slave, + unsigned int stream_tag); + +/** + * sdw_config_port: Master(s) and Slave(s) are associated to stream. + * Each Master and Slave can handle stream using + * different SoundWire Port number(s). + * e.g Master may handle stereo stream using single + * Data Port, while Slave may handle each channel + * of Data stream using one Port each. Bus driver + * needs to know the stream to Port association + * for each Master(s) and Slave(s) assocated with the + * stream for configuring Master and Slave ports + * based on transport params calculate by bus for a + * stream. Both Master and Slave call this function to let + * bus driver know about stream to Port mapping. + * Master driver calls this with Slave handle + * as NULL. + * @mstr: Master handle where the Slave is connected. + * @slave: Slave handle. + * @port_config: Port configuration for each Port of SoundWire Slave. + * @stream_tag: Stream tag, where this Port is connected. + * + */ +int sdw_config_port(struct sdw_master *mstr, + struct sdw_slv *slave, + struct sdw_port_config *port_config, + unsigned int stream_tag); + +/** + * sdw_prepare_and_enable: Prepare and enable all the ports of all the Master(s) + * and Slave(s) associated with this stream tag. + * Following will be done as part of prepare and + * enable by bus driver. + * 1. Calculate new bandwidth required on bus + * because of addition be this new stream. + * 2. Calculate new frameshape based on bandwidth + * 3. Calculate the new clock frequency on which + * bus will be transistioned based on new + * bandwidth and frameshape. + * 4. Calculate new transport params for the already + * active on the bus based on clock frequency, + * frameshape and bandwidth changes. + * 5. Calculate transport params for this stream + * 6. Program already active ports with new transport + * params and frame shape, + * change clock frequency of master. + * 7. Prepare ports for this stream. + * 8. Enable ports for this stream. + * @stream_tag: Audio stream to be activated. Each stream has unique + * stream_tag. All the channels of all the ports of Slave(s) + * and Master(s) attached to this stream will be activated + * deactivated simultaneously at proper SSP or gsync. + * @enable: Enable the ports as part of current call or not. If its + * false only till steps 1 to 7 will be executed as part of this + * function call. if its true, than steps 1 to 7 will be executed + * if not already done, else only step 8 will be executed. + * + */ +int sdw_prepare_and_enable(int stream_tag, bool enable); + +/** + * sdw_disable_and_unprepare: Un-Prepare and disable all the ports of all the + * Master(s) and Slave(s) associated with stream tag. + * Following will be done as part of Un-prepare and + * disable by bus driver. + * 1. Disable all the ports for this stream. + * 2. Un-Prepare ports for this stream. + * 3. Calculate new bandwidth required on bus + * because of removal of this new stream. + * 4. Calculate new frameshape based on bandwidth + * 5. Calculate the new clock frequency on which + * bus will be transistioned based on new + * bandwidth and frameshape. + * 6. Calculate new transport params for the already + * active on the bus based on clock frequency, + * frameshape and bandwidth changes. + * 7.Program already active ports with new transport + * params and frame shape, + * change clock frequency of master. + * @stream_tag: Audio stream to be disabled. Each stream has unique + * stream_tag. All the channels of all the ports of Slave(s) + * and Master(s) attached to this stream will be activated + * deactivated simultaneously at proper SSP or gsync. + * @un_prepare: Un-prepare the ports as part of current call or not. If its + * false only step 1 will be executed as part of this + * function call. if its true, than step 1 will be executed + * if not already done, else only 2 to 7 will be executed. + */ +int sdw_disable_and_unprepare(int stream_tag, bool un_prepare); + +/** + * sdw_master_update_slv_status: Update the status of the Slave to the bus + * driver. Master calls this function based on the + * interrupt it gets once the Slave changes its + * state or from interrupts for the Master hardware + * that caches status information reported in PING frames + * @mstr: Master handle for which status is reported. + * @status: Array of status of each Slave. + */ +int sdw_master_update_slv_status(struct sdw_master *mstr, + struct sdw_status *status); + +/** + * sdw_get_master: Return the Master handle from Master number. + * Increments the reference count of the module. + * Similar to i2c_get_adapter. + * nr: Master controller number. + * returns Master handle on success, else NULL + */ +struct sdw_master *sdw_get_master(int nr); + +/** + * sdw_put_master: Reverses the effect of sdw_get_master + * mstr: Master controller handle. + */ +void sdw_put_master(struct sdw_master *mstr); + + +/** + * module_sdw_slave_driver() - Helper macro for registering a sdw Slave driver + * @__sdw_slave_driver: sdw_slave_driver struct + * + * Helper macro for sdw drivers which do not do anything special in module + * init/exit. This eliminates a lot of boilerplate. Each module may only + * use this macro once, and calling it replaces module_init() and module_exit() + */ +#define module_sdw_slave_driver(__sdw_slave_driver) \ + module_driver(__sdw_slave_driver, sdw_slave_driver_register, \ + sdw_slave_driver_unregister) +/** + * sdw_prepare_for_clock_change: Prepare all the Slaves for clock stop or + * clock start. Prepares Slaves based on what they support + * simplified clock stop or normal clock stop based on + * their capabilities registered to slave driver. + * @mstr: Master handle for which clock state has to be changed. + * @start: Prepare for starting or stopping the clock + * @clk_stop_mode: Bus used which clock mode, if bus finds all the Slaves + * on the bus to be supported clock stop mode1 it prepares + * all the Slaves for mode1 else it will prepare all the + * Slaves for mode0. + */ +int sdw_prepare_for_clock_change(struct sdw_master *mstr, bool start, + enum sdw_clk_stop_mode *clck_stop_mode); + +/** + * sdw_wait_for_slave_enumeration: Wait till all the slaves are enumerated. + * Typicall this function is called by master once + * it resumes its clock. This function waits in + * loop for about 2Secs before all slaves gets enumerated + * This function returns immediately if the clock + * stop mode0 was entered earlier, where slave need + * not re-enumerated. + * + * @mstr: Master handle + * @slave: Slave handle + */ +int sdw_wait_for_slave_enumeration(struct sdw_master *mstr, + struct sdw_slv *slave); + +/** + * sdw_stop_clock: Stop the clock. This function broadcasts the SCP_CTRL + * register with clock_stop_now bit set. + * @mstr: Master handle for which clock has to be stopped. + * @clk_stop_mode: Bus used which clock mode. + */ + +int sdw_stop_clock(struct sdw_master *mstr, enum sdw_clk_stop_mode mode); + +/* Return the adapter number for a specific adapter */ +static inline int sdw_master_id(struct sdw_master *mstr) +{ + return mstr->nr; +} + +static inline void *sdw_master_get_drvdata(const struct sdw_master *mstr) +{ + return dev_get_drvdata(&mstr->dev); +} + +static inline void sdw_master_set_drvdata(struct sdw_master *mstr, + void *data) +{ + dev_set_drvdata(&mstr->dev, data); +} + +static inline void *sdw_slave_get_drvdata(const struct sdw_slv *slv) +{ + return dev_get_drvdata(&slv->dev); +} + +static inline void sdw_slave_set_drvdata(struct sdw_slv *slv, + void *data) +{ + dev_set_drvdata(&slv->dev, data); +} + +#endif /* _LINUX_SDW_BUS_H */ diff --git a/include/trace/events/sdw.h b/include/trace/events/sdw.h new file mode 100644 index 000000000000..0c08e58a18bf --- /dev/null +++ b/include/trace/events/sdw.h @@ -0,0 +1,232 @@ +/* SDW message transfer tracepoints + * + * Copyright (C) 2014-2015 Intel Corp + * Author: Hardik Shah + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * 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; version 2 of the License. + * + * 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. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ +#undef TRACE_SYSTEM +#define TRACE_SYSTEM sdw + +#if !defined(_TRACE_SDW_H) || defined(TRACE_HEADER_MULTI_READ) +#define _TRACE_SDW_H + +#include +#include +#include + +/* + * drivers/sdw/sdw.c + */ +extern int sdw_transfer_trace_reg(void); +extern void sdw_transfer_trace_unreg(void); +/* + * __sdw_transfer() write request + */ +TRACE_EVENT_FN(sdw_write, + TP_PROTO(const struct sdw_master *mstr, const struct sdw_msg *msg, + int num), + TP_ARGS(mstr, msg, num), + TP_STRUCT__entry( + __field(int, master_nr) + __field(__u16, msg_nr) + __field(__u8, addr_page1) + __field(__u8, addr_page2) + __field(__u16, addr) + __field(__u16, flag) + __field(__u16, len) + __dynamic_array(__u8, buf, msg->len)), + TP_fast_assign( + __entry->master_nr = mstr->nr; + __entry->msg_nr = num; + __entry->addr = msg->addr; + __entry->flag = msg->flag; + __entry->len = msg->len; + __entry->addr_page1 = msg->addr_page1; + __entry->addr_page2 = msg->addr_page2; + memcpy(__get_dynamic_array(buf), msg->buf, msg->len); + ), + TP_printk("sdw-%d #%u a=%03x addr_page1=%04x addr_page2=%04x f=%04x l=%u [%*phD]", + __entry->master_nr, + __entry->msg_nr, + __entry->addr, + __entry->addr_page1, + __entry->addr_page2, + __entry->flag, + __entry->len, + __entry->len, __get_dynamic_array(buf) + ), + sdw_transfer_trace_reg, + sdw_transfer_trace_unreg); + +/* + * __sdw_transfer() read request + */ +TRACE_EVENT_FN(sdw_read, + TP_PROTO(const struct sdw_master *mstr, const struct sdw_msg *msg, + int num), + TP_ARGS(mstr, msg, num), + TP_STRUCT__entry( + __field(int, master_nr) + __field(__u16, msg_nr) + __field(__u8, addr_page1) + __field(__u8, addr_page2) + __field(__u16, addr) + __field(__u16, flag) + __field(__u16, len) + __dynamic_array(__u8, buf, msg->len)), + TP_fast_assign( + __entry->master_nr = mstr->nr; + __entry->msg_nr = num; + __entry->addr = msg->addr; + __entry->flag = msg->flag; + __entry->len = msg->len; + __entry->addr_page1 = msg->addr_page1; + __entry->addr_page2 = msg->addr_page2; + memcpy(__get_dynamic_array(buf), msg->buf, msg->len); + ), + TP_printk("sdw-%d #%u a=%03x addr_page1=%04x addr_page2=%04x f=%04x l=%u [%*phD]", + __entry->master_nr, + __entry->msg_nr, + __entry->addr, + __entry->addr_page1, + __entry->addr_page2, + __entry->flag, + __entry->len, + __entry->len, __get_dynamic_array(buf) + ), + sdw_transfer_trace_reg, + sdw_transfer_trace_unreg); + +/* + * __sdw_transfer() read reply + */ +TRACE_EVENT_FN(sdw_reply, + TP_PROTO(const struct sdw_master *mstr, const struct sdw_msg *msg, + int num), + TP_ARGS(mstr, msg, num), + TP_STRUCT__entry( + __field(int, master_nr) + __field(__u16, msg_nr) + __field(__u16, addr) + __field(__u16, flag) + __field(__u16, len) + __dynamic_array(__u8, buf, msg->len)), + TP_fast_assign( + __entry->master_nr = mstr->nr; + __entry->msg_nr = num; + __entry->addr = msg->addr; + __entry->flag = msg->flag; + __entry->len = msg->len; + memcpy(__get_dynamic_array(buf), msg->buf, msg->len); + ), + TP_printk("sdw-%d #%u a=%03x f=%04x l=%u [%*phD]", + __entry->master_nr, + __entry->msg_nr, + __entry->addr, + __entry->flag, + __entry->len, + __entry->len, __get_dynamic_array(buf) + ), + sdw_transfer_trace_reg, + sdw_transfer_trace_unreg); + +/* + * __sdw_transfer() result + */ +TRACE_EVENT_FN(sdw_result, + TP_PROTO(const struct sdw_master *mstr, int num, int ret), + TP_ARGS(mstr, num, ret), + TP_STRUCT__entry( + __field(int, master_nr) + __field(__u16, nr_msgs) + __field(__s16, ret) + ), + TP_fast_assign( + __entry->master_nr = mstr->nr; + __entry->nr_msgs = num; + __entry->ret = ret; + ), + TP_printk("sdw-%d n=%u ret=%d", + __entry->master_nr, + __entry->nr_msgs, + __entry->ret + ), + sdw_transfer_trace_reg, + sdw_transfer_trace_unreg); + +/* + * sdw_stream_config() configuration + */ +TRACE_EVENT_FN(sdw_config_stream, + TP_PROTO(const struct sdw_master *mstr, const struct sdw_slv *slv, const struct sdw_stream_config *str_cfg, int stream_tag), + TP_ARGS(mstr, slv, str_cfg, stream_tag), + TP_STRUCT__entry( + __field(unsigned int, frame_rate) + __field(unsigned int, ch_cnt) + __field(unsigned int, bps) + __field(unsigned int, direction) + __field(unsigned int, stream_tag) + __array(char, name, SOUNDWIRE_NAME_SIZE) + ), + TP_fast_assign( + __entry->frame_rate = str_cfg->frame_rate; + __entry->ch_cnt = str_cfg->channel_count; + __entry->bps = str_cfg->bps; + __entry->direction = str_cfg->direction; + __entry->stream_tag = stream_tag; + slv ? strncpy(entry->name, dev_name(&slv->dev), SOUNDWIRE_NAME_SIZE) : strncpy(entry->name, dev_name(&mstr->dev), SOUNDWIRE_NAME_SIZE); + ), + TP_printk("Stream_config dev = %s stream_tag = %d, frame_rate = %d, ch_count = %d bps = %d dir = %d", + __entry->name, + __entry->stream_tag, + __entry->frame_rate, + __entry->ch_cnt, + __entry->bps, + __entry->direction + ), + sdw_transfer_trace_reg, + sdw_transfer_trace_unreg); + +/* + * sdw_port_config() configuration + */ +TRACE_EVENT_FN(sdw_config_port, + TP_PROTO(const struct sdw_master *mstr, const struct sdw_slv *slv, const struct sdw_port_cfg *port_cfg, int stream_tag), + TP_ARGS(mstr, slv, port_cfg, stream_tag), + TP_STRUCT__entry( + __field(unsigned int, port_num) + __field(unsigned int, ch_mask) + __field(unsigned int, stream_tag) + __array(char, name, SOUNDWIRE_NAME_SIZE) + ), + TP_fast_assign( + __entry->port_num = port_cfg->port_num; + __entry->ch_mask = port_cfg->ch_mask; + __entry->stream_tag = stream_tag; + slv ? strncpy(entry->name, dev_name(&slv->dev), SOUNDWIRE_NAME_SIZE) : strncpy(entry->name, dev_name(&mstr->dev), SOUNDWIRE_NAME_SIZE); + ), + TP_printk("Port_config dev = %s stream_tag = %d, port = %d, ch_mask = %d", + __entry->name, + __entry->stream_tag, + __entry->port_num, + __entry->ch_mask + ), + sdw_transfer_trace_reg, + sdw_transfer_trace_unreg); + +#endif /* _TRACE_SDW_H */ + +/* This part must be outside protection */ +#include From 130a3c6ef9ecf6e9616c2115d968bfc5c8699992 Mon Sep 17 00:00:00 2001 From: Hardik T Shah Date: Thu, 10 Mar 2016 10:16:10 +0530 Subject: [PATCH 0611/1103] ASoC: Add dai_ops to set the stream tag. Stream tag is introduced for supporting SoundWire links as part of ASoC. stream tag is unique stream identifier for each stream. Same stream tag is assigned to all the CPU and codec DAIs part of same stream tag. This function provides dai ops to be called to let the DAI know its stream tag. Normally stream tag is allocated by CPU DAI and get programmed to codec dai. This function calls all the codec DAI ops to program the stream tag allocated by CPU DAI. Change-Id: I9a74dc1329c21b34aaf7f096c68f932b537653f2 Signed-off-by: Hardik T Shah --- include/sound/soc-dai.h | 17 +++++++++++++ sound/soc/soc-core.c | 55 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 72 insertions(+) diff --git a/include/sound/soc-dai.h b/include/sound/soc-dai.h index f5d70041108f..c393f1126839 100644 --- a/include/sound/soc-dai.h +++ b/include/sound/soc-dai.h @@ -145,6 +145,13 @@ int snd_soc_dai_get_channel_map(struct snd_soc_dai *dai, int snd_soc_dai_is_dummy(struct snd_soc_dai *dai); + +/* Stream tag programming for codec and cpu dai */ +int snd_soc_dai_program_stream_tag(struct snd_pcm_substream *substream, + struct snd_soc_dai *cpu_dai, int stream_tag); +void snd_soc_dai_remove_stream_tag(struct snd_pcm_substream *substream, + struct snd_soc_dai *cpu_dai); + struct snd_soc_dai_ops { /* * DAI clocking configuration, all optional. @@ -184,6 +191,16 @@ struct snd_soc_dai_ops { int (*digital_mute)(struct snd_soc_dai *dai, int mute); int (*mute_stream)(struct snd_soc_dai *dai, int mute, int stream); + /* + * stream_tag - Optional + * Used by SoundWire and HDA driver to set same stream + * tag for both CPU and Codec DAI + */ + int (*program_stream_tag)(struct snd_pcm_substream *, + struct snd_soc_dai *, int); + int (*remove_stream_tag)(struct snd_pcm_substream *, + struct snd_soc_dai *); + /* * ALSA PCM audio operations - all optional. * Called by soc-core during audio PCM operations. diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index 473eefe8658e..29b42f8f1eff 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -2607,6 +2607,61 @@ int snd_soc_dai_set_tdm_slot(struct snd_soc_dai *dai, } EXPORT_SYMBOL_GPL(snd_soc_dai_set_tdm_slot); +/** + * snd_soc_dai_program_stream_tag - Program the stream tag allocated by + * CPU DAI to codec DAI. This will be + * used in HDA and soundwire, wherex + * audio stream between codec and + * SoC need to have same stream tag. + * substream: Substream + * cpu_dai: CPU DAI + * stream_tag: Stream tag to be programmed. + */ +int snd_soc_dai_program_stream_tag(struct snd_pcm_substream *substream, + struct snd_soc_dai *cpu_dai, int stream_tag) +{ + int i; + struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(substream); + const struct snd_soc_dai_ops *codec_dai_ops; + struct snd_soc_dai *codec_dai; + int ret = 0; + + for (i = 0; i < rtd->num_codecs; i++) { + codec_dai = rtd->codec_dais[i]; + codec_dai_ops = codec_dai->driver->ops; + if (codec_dai_ops->program_stream_tag) { + ret = codec_dai_ops->program_stream_tag(substream, + codec_dai, stream_tag); + if (ret) + return ret; + } + } + return ret; + +} +EXPORT_SYMBOL_GPL(snd_soc_dai_program_stream_tag); +/** + * snd_soc_dai_remove_stream_tag - Reverse the programmed stream tag + * substream: Substream + * cpu_dai: CPU DAI + */ +void snd_soc_dai_remove_stream_tag(struct snd_pcm_substream *substream, + struct snd_soc_dai *cpu_dai) +{ + int i; + struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(substream); + const struct snd_soc_dai_ops *codec_dai_ops; + struct snd_soc_dai *codec_dai; + + for (i = 0; i < rtd->num_codecs; i++) { + codec_dai = rtd->codec_dais[i]; + codec_dai_ops = codec_dai->driver->ops; + if (codec_dai_ops->program_stream_tag) + codec_dai_ops->remove_stream_tag(substream, codec_dai); + } +} +EXPORT_SYMBOL_GPL(snd_soc_dai_remove_stream_tag); + /** * snd_soc_dai_set_channel_map - configure DAI audio channel map * @dai: DAI From db556e862dfdd84da97feba21709844e08dfe6f6 Mon Sep 17 00:00:00 2001 From: Hardik T Shah Date: Thu, 10 Mar 2016 10:33:33 +0530 Subject: [PATCH 0612/1103] ASoC: CNL: Register soundwire controller to bus driver. This patch registers the SoundWire controller to the SoundWire bus driver. Change-Id: Iefc4a7cd30d2f5ad043fe63e82f519ebc488bf65 Signed-off-by: Hardik T Shah --- sound/soc/intel/skylake/cnl-sst-dsp.c | 6 ++ sound/soc/intel/skylake/cnl-sst-dsp.h | 13 +++ sound/soc/intel/skylake/cnl-sst.c | 121 ++++++++++++++++++++++++++ sound/soc/intel/skylake/skl-sst-ipc.h | 5 ++ 4 files changed, 145 insertions(+) diff --git a/sound/soc/intel/skylake/cnl-sst-dsp.c b/sound/soc/intel/skylake/cnl-sst-dsp.c index 2f8326707c21..2d9c86ddcdf0 100644 --- a/sound/soc/intel/skylake/cnl-sst-dsp.c +++ b/sound/soc/intel/skylake/cnl-sst-dsp.c @@ -237,6 +237,12 @@ void cnl_ipc_int_disable(struct sst_dsp *ctx) CNL_ADSPIC_IPC, 0); } +void cnl_sdw_int_enable(struct sst_dsp *ctx, bool enable) +{ + sst_dsp_shim_update_bits(ctx, CNL_ADSP_REG_ADSPIC2, + CNL_ADSPIC2_SNDW, CNL_ADSPIC2_SNDW); +} + void cnl_ipc_op_int_enable(struct sst_dsp *ctx) { /* enable IPC DONE interrupt */ diff --git a/sound/soc/intel/skylake/cnl-sst-dsp.h b/sound/soc/intel/skylake/cnl-sst-dsp.h index 09bd218df5c4..323cebc4e389 100644 --- a/sound/soc/intel/skylake/cnl-sst-dsp.h +++ b/sound/soc/intel/skylake/cnl-sst-dsp.h @@ -26,6 +26,8 @@ struct sst_generic_ipc; #define CNL_ADSP_REG_ADSPCS (CNL_ADSP_GEN_BASE + 0x04) #define CNL_ADSP_REG_ADSPIC (CNL_ADSP_GEN_BASE + 0x08) #define CNL_ADSP_REG_ADSPIS (CNL_ADSP_GEN_BASE + 0x0c) +#define CNL_ADSP_REG_ADSPIC2 (CNL_ADSP_GEN_BASE + 0x10) +#define CNL_ADSP_REG_ADSPIS2 (CNL_ADSP_GEN_BASE + 0x14) /* Intel HD Audio Inter-Processor Communication Registers */ #define CNL_ADSP_IPC_BASE 0xc0 @@ -72,6 +74,16 @@ struct sst_generic_ipc; #define CNL_ADSPIC_IPC 0x1 #define CNL_ADSPIS_IPC 0x1 +#define CNL_ADSPIC2_SNDW 0x20 + +#define CNL_SDW_SHIM_BASE 0x2C000 +#define CNL_SDW_LINK_0_BASE 0x30000 +#define CNL_SDW_LINK_1_BASE 0x40000 +#define CNL_SDW_LINK_2_BASE 0x50000 +#define CNL_SDW_LINK_3_BASE 0x60000 +#define CNL_ALH_BASE 0x2C800 + +/* ADSPCS - Audio DSP Control & Status */ #define CNL_DSP_CORES 4 #define CNL_DSP_CORES_MASK ((1 << CNL_DSP_CORES) - 1) @@ -98,6 +110,7 @@ void cnl_dsp_free(struct sst_dsp *dsp); void cnl_ipc_int_enable(struct sst_dsp *ctx); void cnl_ipc_int_disable(struct sst_dsp *ctx); +void cnl_sdw_int_enable(struct sst_dsp *ctx, bool enable); void cnl_ipc_op_int_enable(struct sst_dsp *ctx); void cnl_ipc_op_int_disable(struct sst_dsp *ctx); bool cnl_ipc_int_status(struct sst_dsp *ctx); diff --git a/sound/soc/intel/skylake/cnl-sst.c b/sound/soc/intel/skylake/cnl-sst.c index a66ed94870b4..335ec68b6a32 100644 --- a/sound/soc/intel/skylake/cnl-sst.c +++ b/sound/soc/intel/skylake/cnl-sst.c @@ -27,6 +27,8 @@ #include #include #include +#include +#include #include #include "../common/sst-dsp.h" @@ -497,6 +499,118 @@ static int cnl_ipc_init(struct device *dev, struct skl_sst *cnl) return 0; } +static int skl_register_sdw_masters(struct device *dev, struct skl_sst *dsp, + void __iomem *mmio_base, int irq) +{ + struct sdw_master_capabilities *m_cap; + struct sdw_mstr_dp0_capabilities *dp0_cap; + struct sdw_mstr_dpn_capabilities *dpn_cap; + struct sdw_master *master; + struct cnl_sdw_data *p_data; + int ret = 0, i, j; + /* TODO: This number 4 should come from ACPI */ + dsp->num_sdw_controllers = 1; + master = devm_kzalloc(dev, + (sizeof(*master) * dsp->num_sdw_controllers), + GFP_KERNEL); + if (!master) { + return -ENOMEM; + dsp->num_sdw_controllers = 0; + } + dsp->mstr = master; + /* TODO This should come from ACPI */ + for (i = 0; i < dsp->num_sdw_controllers; i++) { + p_data = devm_kzalloc(dev, sizeof(*p_data), GFP_KERNEL); + if (!p_data) + return -ENOMEM; + /* PCI Device is parent of the SoundWire master device */ + /* TODO: All these hardcoding should come from ACPI */ + master[i].dev.parent = dev; + master[i].dev.platform_data = p_data; + m_cap = &master[i].mstr_capabilities; + dp0_cap = &m_cap->sdw_dp0_cap; + master[i].nr = i; + master[i].timeout = -1; + master[i].retries = CNL_SDW_MAX_CMD_RETRIES; + m_cap->base_clk_freq = 9.6 * 1000 * 1000; + strcpy(master[i].name, "cnl_sdw_mstr"); + m_cap->highphy_capable = 0; + m_cap->sdw_dp0_supported = 1; + m_cap->num_data_ports = CNL_SDW_MAX_PORTS; + dp0_cap->max_word_length = 32; + dp0_cap->min_word_length = 1; + dp0_cap->num_word_length = 0; + dp0_cap->word_length_buffer = NULL; + dp0_cap->bra_max_data_per_frame = 0; + m_cap->sdw_dpn_cap = kzalloc(((sizeof(*dpn_cap)) * + CNL_SDW_MAX_PORTS), GFP_KERNEL); + if (!m_cap->sdw_dpn_cap) + return -ENOMEM; + for (j = 0; j < m_cap->num_data_ports; j++) { + dpn_cap = &m_cap->sdw_dpn_cap[i]; + /* Both Tx and Rx */ + dpn_cap->port_direction = 0x3; + dpn_cap->port_number = i; + dpn_cap->max_word_length = 32; + dpn_cap->min_word_length = 1; + dpn_cap->num_word_length = 0; + dpn_cap->word_length_buffer = NULL; + dpn_cap->dpn_type = SDW_FULL_DP; + dpn_cap->min_ch_num = 1; + dpn_cap->max_ch_num = 8; + dpn_cap->num_ch_supported = 0; + dpn_cap->ch_supported = NULL; + /* IP supports all, but we are going to support only + * isochronous + */ + dpn_cap->port_mode_mask = + SDW_PORT_FLOW_MODE_ISOCHRONOUS; + dpn_cap->block_packing_mode_mask = + SDW_PORT_BLK_PKG_MODE_BLK_PER_PORT | + SDW_PORT_BLK_PKG_MODE_BLK_PER_CH; + } + switch (i) { + case 0: + p_data->sdw_regs = mmio_base + CNL_SDW_LINK_0_BASE; + break; + case 1: + p_data->sdw_regs = mmio_base + CNL_SDW_LINK_1_BASE; + break; + case 2: + p_data->sdw_regs = mmio_base + CNL_SDW_LINK_2_BASE; + break; + case 3: + p_data->sdw_regs = mmio_base + CNL_SDW_LINK_3_BASE; + break; + default: + return -EINVAL; + } + p_data->sdw_shim = mmio_base + CNL_SDW_SHIM_BASE; + p_data->alh_base = mmio_base + CNL_ALH_BASE; + p_data->inst_id = i; + p_data->irq = irq; + ret = sdw_add_master_controller(&master[i]); + if (ret) { + dev_err(dev, "Failed to register soundwire master\n"); + return ret; + } + } + /* Enable the global soundwire interrupts */ + cnl_sdw_int_enable(dsp->dsp, 1); + return 0; +} + +static void skl_unregister_sdw_masters(struct skl_sst *ctx) +{ + int i; + + /* Disable global soundwire interrupts */ + cnl_sdw_int_enable(ctx->dsp, 0); + for (i = 0; i < ctx->num_sdw_controllers; i++) + sdw_del_master_controller(&ctx->mstr[i]); + +} + int cnl_sst_dsp_init(struct device *dev, void __iomem *mmio_base, int irq, const char *fw_name, struct skl_dsp_loader_ops dsp_ops, struct skl_sst **dsp) @@ -544,6 +658,12 @@ int cnl_sst_dsp_init(struct device *dev, void __iomem *mmio_base, int irq, return ret; } + ret = skl_register_sdw_masters(dev, cnl, mmio_base, irq); + if (ret) { + dev_err(cnl->dev, "%s SoundWire masters registration failed\n", __func__); + return ret; + } + return 0; } EXPORT_SYMBOL_GPL(cnl_sst_dsp_init); @@ -575,6 +695,7 @@ void cnl_sst_dsp_cleanup(struct device *dev, struct skl_sst *ctx) release_firmware(ctx->dsp->fw); skl_freeup_uuid_list(ctx); + skl_unregister_sdw_masters(ctx); cnl_ipc_free(&ctx->ipc); ctx->dsp->ops->free(ctx->dsp); diff --git a/sound/soc/intel/skylake/skl-sst-ipc.h b/sound/soc/intel/skylake/skl-sst-ipc.h index f74f040dfd83..10a486939515 100644 --- a/sound/soc/intel/skylake/skl-sst-ipc.h +++ b/sound/soc/intel/skylake/skl-sst-ipc.h @@ -121,6 +121,11 @@ struct skl_sst { /* Callback to update dynamic clock and power gating registers */ void (*clock_power_gating)(struct device *dev, bool enable); + + /* SDW Devices in DSP Space */ + int num_sdw_controllers; + /* Array of sdw masters */ + struct sdw_master *mstr; }; struct skl_ipc_init_instance_msg { From b4dcc5006bac7cd46a1b05db13ab27f18405f6ba Mon Sep 17 00:00:00 2001 From: Hardik T Shah Date: Thu, 10 Mar 2016 11:25:19 +0530 Subject: [PATCH 0613/1103] ASoC: Intel: Add support for SoundWire link in copier. This patch adds the support for the SoundWire link in copier. Copier needs to be programmed differently for different link. Change-Id: I16d811b61ac253a893c2a9afebb6d418327e4387 Signed-off-by: Hardik T Shah Signed-off-by: Guneshwor Singh --- include/uapi/sound/skl-tplg-interface.h | 7 +++++++ sound/soc/intel/skylake/skl-messages.c | 7 +++++++ sound/soc/intel/skylake/skl-topology.h | 4 ++++ 3 files changed, 18 insertions(+) diff --git a/include/uapi/sound/skl-tplg-interface.h b/include/uapi/sound/skl-tplg-interface.h index f39352cef382..68eda1a15e39 100644 --- a/include/uapi/sound/skl-tplg-interface.h +++ b/include/uapi/sound/skl-tplg-interface.h @@ -104,9 +104,16 @@ enum skl_dev_type { SKL_DEVICE_SLIMBUS = 0x3, SKL_DEVICE_HDALINK = 0x4, SKL_DEVICE_HDAHOST = 0x5, + SKL_DEVICE_SDW = 0x6, SKL_DEVICE_NONE }; +enum skl_pdi_type { + SKL_PDI_PCM = 0, + SKL_PDI_PDM = 1, + SKL_PDI_INVALID = 2 +}; + /** * enum skl_interleaving - interleaving style * diff --git a/sound/soc/intel/skylake/skl-messages.c b/sound/soc/intel/skylake/skl-messages.c index 8bfb8b0fa3d5..f63b50e43257 100644 --- a/sound/soc/intel/skylake/skl-messages.c +++ b/sound/soc/intel/skylake/skl-messages.c @@ -561,6 +561,13 @@ static u32 skl_get_node_id(struct skl_sst *ctx, SKL_DMA_HDA_HOST_INPUT_CLASS; node_id.node.vindex = params->host_dma_id; break; + case SKL_DEVICE_SDW: + node_id.node.dma_type = + (SKL_CONN_SOURCE == mconfig->hw_conn_type) ? + SKL_DMA_SDW_LINK_OUTPUT_CLASS : + SKL_DMA_SDW_LINK_INPUT_CLASS; + node_id.node.vindex = mconfig->sdw_stream_num; + break; default: node_id.val = 0xFFFFFFFF; diff --git a/sound/soc/intel/skylake/skl-topology.h b/sound/soc/intel/skylake/skl-topology.h index 82282cac9751..d94709ffd323 100644 --- a/sound/soc/intel/skylake/skl-topology.h +++ b/sound/soc/intel/skylake/skl-topology.h @@ -184,6 +184,8 @@ enum skl_dma_type { SKL_DMA_DMIC_LINK_INPUT_CLASS = 0xB, SKL_DMA_I2S_LINK_OUTPUT_CLASS = 0xC, SKL_DMA_I2S_LINK_INPUT_CLASS = 0xD, + SKL_DMA_SDW_LINK_OUTPUT_CLASS = 0x10, + SKL_DMA_SDW_LINK_INPUT_CLASS = 0x11, }; union skl_ssp_dma_node { @@ -414,6 +416,8 @@ struct skl_module_cfg { u32 mem_pages; enum d0i3_capability d0i3_caps; u32 dma_buffer_size; /* in milli seconds */ + u8 pdi_type; + u32 sdw_stream_num; struct skl_module_pin *m_in_pin; struct skl_module_pin *m_out_pin; enum skl_module_type m_type; From 8dcbd74b8559f9bd7e25f1f6aae467c6320ecb74 Mon Sep 17 00:00:00 2001 From: Hardik T Shah Date: Thu, 10 Mar 2016 11:32:51 +0530 Subject: [PATCH 0614/1103] ASoC: Intel: Skylake: Interface change between firmware and driver. Copier interface changed between the firmware and driver. This patch takes care of the firmware interface change. Change-Id: I475cde41a4a008808cf7d88fadc20639879fbff4 Signed-off-by: Hardik T Shah --- sound/soc/intel/skylake/skl-topology.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sound/soc/intel/skylake/skl-topology.h b/sound/soc/intel/skylake/skl-topology.h index d94709ffd323..353c90cbd336 100644 --- a/sound/soc/intel/skylake/skl-topology.h +++ b/sound/soc/intel/skylake/skl-topology.h @@ -200,8 +200,8 @@ union skl_connector_node_id { u32 val; struct { u32 vindex:8; - u32 dma_type:4; - u32 rsvd:20; + u32 dma_type:5; + u32 rsvd:19; } node; }; From b100001d38c88eeace0c764e869adaf4ad2f8fac Mon Sep 17 00:00:00 2001 From: Hardik T Shah Date: Thu, 10 Mar 2016 12:55:38 +0530 Subject: [PATCH 0615/1103] ASoC: Intel: Add support to bypass NHLT reading for SDW link. NHLT reading is not required for the SDW link. So bypass NHLT reading for SoundWire link. Change-Id: I4c256874ce57631e8c3e72f2f033a303acb4006a Signed-off-by: Hardik T Shah --- sound/soc/intel/skylake/skl-nhlt.h | 1 + sound/soc/intel/skylake/skl-topology.c | 6 ++++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/sound/soc/intel/skylake/skl-nhlt.h b/sound/soc/intel/skylake/skl-nhlt.h index 116534e7b3c5..fc17da503b4d 100644 --- a/sound/soc/intel/skylake/skl-nhlt.h +++ b/sound/soc/intel/skylake/skl-nhlt.h @@ -48,6 +48,7 @@ enum nhlt_link_type { NHLT_LINK_DSP = 1, NHLT_LINK_DMIC = 2, NHLT_LINK_SSP = 3, + NHLT_LINK_SDW = 4, NHLT_LINK_INVALID }; diff --git a/sound/soc/intel/skylake/skl-topology.c b/sound/soc/intel/skylake/skl-topology.c index 2620d77729c5..413a72dada50 100644 --- a/sound/soc/intel/skylake/skl-topology.c +++ b/sound/soc/intel/skylake/skl-topology.c @@ -1850,7 +1850,9 @@ static u8 skl_tplg_be_link_type(int dev_type) case SKL_DEVICE_HDALINK: ret = NHLT_LINK_HDA; break; - + case SKL_DEVICE_SDW: + ret = NHLT_LINK_SDW; + break; default: ret = NHLT_LINK_INVALID; break; @@ -1877,7 +1879,7 @@ static int skl_tplg_be_fill_pipe_params(struct snd_soc_dai *dai, skl_tplg_fill_dma_id(mconfig, params); - if (link_type == NHLT_LINK_HDA) + if (link_type == NHLT_LINK_HDA || link_type == NHLT_LINK_SDW) return 0; /* update the blob based on virtual bus_id*/ From f90a83a3a62d2b86d388fe6965624c9b0608800f Mon Sep 17 00:00:00 2001 From: Hardik T Shah Date: Thu, 10 Mar 2016 13:00:27 +0530 Subject: [PATCH 0616/1103] ASoC: Intel: Skylake: Add support for the SDW dai ops. Add support for the SoundWire dai link operation. DAI ops needs to be different for different link based on Link requirement. Add support for the DAI ops link. Change-Id: I2c3d24c3d982f339f8b7ca180079b547227c6e70 Signed-off-by: Hardik T Shah --- sound/soc/intel/skylake/Makefile | 2 +- sound/soc/intel/skylake/skl-pcm.c | 94 ++++++++++ sound/soc/intel/skylake/skl-sdw-pcm.c | 246 ++++++++++++++++++++++++++ sound/soc/intel/skylake/skl-sdw-pcm.h | 41 +++++ 4 files changed, 382 insertions(+), 1 deletion(-) create mode 100644 sound/soc/intel/skylake/skl-sdw-pcm.c create mode 100644 sound/soc/intel/skylake/skl-sdw-pcm.h diff --git a/sound/soc/intel/skylake/Makefile b/sound/soc/intel/skylake/Makefile index 86f6e1d801af..c5ee108bf5d9 100644 --- a/sound/soc/intel/skylake/Makefile +++ b/sound/soc/intel/skylake/Makefile @@ -1,5 +1,5 @@ # SPDX-License-Identifier: GPL-2.0 -snd-soc-skl-objs := skl.o skl-pcm.o skl-nhlt.o skl-messages.o \ +snd-soc-skl-objs := skl.o skl-sdw-pcm.o skl-pcm.o skl-nhlt.o skl-messages.o \ skl-topology.o ifdef CONFIG_DEBUG_FS diff --git a/sound/soc/intel/skylake/skl-pcm.c b/sound/soc/intel/skylake/skl-pcm.c index ab31d6a1c842..d411cf28dd91 100644 --- a/sound/soc/intel/skylake/skl-pcm.c +++ b/sound/soc/intel/skylake/skl-pcm.c @@ -28,6 +28,7 @@ #include "skl-topology.h" #include "skl-sst-dsp.h" #include "skl-sst-ipc.h" +#include "skl-sdw-pcm.h" #define HDA_MONO 1 #define HDA_STEREO 2 @@ -662,6 +663,67 @@ static int skl_link_hw_free(struct snd_pcm_substream *substream, return 0; } +static int skl_sdw_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + /* Find the type of DAI, Its decided based on which copier + * is connected to the DAI. All the soundwire DAIs are identical + * but some registers needs to be programmed based on its a + * PDM or PCM. Copier tells DAI is to be used as PDM or PCM + * This makes sure no change is required in code, only change + * required is in the topology to change DAI from PDM to PCM or + * vice versa. + */ + return cnl_sdw_startup(substream, dai); + +} + +static int skl_sdw_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + int ret = 0; + + ret = pm_runtime_get_sync(dai->dev); + if (!ret) + return ret; + /* Allocate the port based on hw_params. + * Allocate PDI stream based on hw_params + * Program stream params to the sdw bus driver + * program Port params to sdw bus driver + */ + return cnl_sdw_hw_params(substream, params, dai); +} + +static int skl_sdw_hw_free(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + /* De-allocate the port from master controller + * De allocate stream from bus driver + */ + return cnl_sdw_hw_free(substream, dai); +} + +static int skl_sdw_pcm_prepare(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + return cnl_sdw_pcm_prepare(substream, dai); +} + +static int skl_sdw_pcm_trigger(struct snd_pcm_substream *substream, + int cmd, struct snd_soc_dai *dai) +{ + return cnl_sdw_pcm_trigger(substream, cmd, dai); +} + +static void skl_sdw_shutdown(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + cnl_sdw_shutdown(substream, dai); + pm_runtime_mark_last_busy(dai->dev); + pm_runtime_put_autosuspend(dai->dev); +} + static const struct snd_soc_dai_ops skl_pcm_dai_ops = { .startup = skl_pcm_open, .shutdown = skl_pcm_close, @@ -686,6 +748,15 @@ static const struct snd_soc_dai_ops skl_link_dai_ops = { .trigger = skl_link_pcm_trigger, }; +static struct snd_soc_dai_ops skl_sdw_dai_ops = { + .startup = skl_sdw_startup, + .prepare = skl_sdw_pcm_prepare, + .hw_params = skl_sdw_hw_params, + .hw_free = skl_sdw_hw_free, + .trigger = skl_sdw_pcm_trigger, + .shutdown = skl_sdw_shutdown, +}; + static struct snd_soc_dai_driver skl_fe_dai[] = { { .name = "System Pin", @@ -1019,6 +1090,29 @@ static struct snd_soc_dai_driver skl_platform_dai[] = { .rates = SNDRV_PCM_RATE_48000, .formats = SNDRV_PCM_FMTBIT_S16_LE, }, +}, +{ + /* Currently adding 1 playback and 1 capture pin, ideally it + * should be coming from CLT based on endpoints to be supported + */ + .name = "SDW Pin", + .ops = &skl_sdw_dai_ops, + .playback = { + .stream_name = "SDW Tx", + .channels_min = HDA_STEREO, + .channels_max = HDA_STEREO, + .rates = SNDRV_PCM_RATE_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + }, + .capture = { + .stream_name = "SDW Rx", + .channels_min = HDA_STEREO, + .channels_max = HDA_STEREO, + .rates = SNDRV_PCM_RATE_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + }, + + }, }; diff --git a/sound/soc/intel/skylake/skl-sdw-pcm.c b/sound/soc/intel/skylake/skl-sdw-pcm.c new file mode 100644 index 000000000000..90a11f47586b --- /dev/null +++ b/sound/soc/intel/skylake/skl-sdw-pcm.c @@ -0,0 +1,246 @@ +/* + * skl-sdw-pcm.c - Handle PCM ops for soundwire DAIs. + * + * Copyright (C) 2014 Intel Corp + * Author: Hardik Shah + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * 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; version 2 of the License. + * + * 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. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ +#include +#include +#include +#include +#include +#include +#include "skl.h" +#include "skl-topology.h" + +#define STREAM_STATE_ALLOC_STREAM_TAG 0x1 +#define STREAM_STATE_ALLOC_STREAM 0x2 +#define STREAM_STATE_CONFIG_STREAM 0x3 +#define STREAM_STATE_PREPARE_STREAM 0x4 +#define STREAM_STATE_ENABLE_STREAM 0x5 +#define STREAM_STATE_DISABLE_STREAM 0x6 +#define STREAM_STATE_UNPREPARE_STREAM 0x7 +#define STREAM_STATE_RELEASE_STREAM 0x8 +#define STREAM_STATE_FREE_STREAM 0x9 +#define STREAM_STATE_FREE_STREAM_TAG 0xa + +struct sdw_dma_data { + int stream_tag; + struct cnl_sdw_port *port; + struct sdw_master *mstr; + enum cnl_sdw_pdi_stream_type stream_type; + int stream_state; +}; + + + +int cnl_sdw_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct skl_module_cfg *m_cfg; + int sdw_ctrl_nr; + struct sdw_master *mstr; + struct sdw_dma_data *dma; + int ret = 0; + + + m_cfg = skl_tplg_be_get_cpr_module(dai, substream->stream); + if (!m_cfg) { + dev_err(dai->dev, "BE Copier not found\n"); + return -EINVAL; + } + sdw_ctrl_nr = m_cfg->vbus_id; + mstr = sdw_get_master(sdw_ctrl_nr); + if (!mstr) { + dev_err(dai->dev, "Master controller not found\n"); + return -EINVAL; + } + dma = kzalloc(sizeof(*dma), GFP_KERNEL); + if (!dma) { + ret = -ENOMEM; + goto alloc_failed; + } + if (m_cfg->pdi_type == SKL_PDI_PCM) + dma->stream_type = CNL_SDW_PDI_TYPE_PCM; + else if (m_cfg->pdi_type == SKL_PDI_PDM) + dma->stream_type = CNL_SDW_PDI_TYPE_PDM; + else { + dev_err(dai->dev, "Stream type not known\n"); + return -EINVAL; + } + dma->mstr = mstr; + snd_soc_dai_set_dma_data(dai, substream, dma); + + ret = sdw_alloc_stream_tag(NULL, &dma->stream_tag); + if (ret) { + dev_err(dai->dev, "Unable to allocate stream tag"); + ret = -EINVAL; + goto alloc_stream_tag_failed; + } + ret = snd_soc_dai_program_stream_tag(substream, dai, dma->stream_tag); + + dma->stream_state = STREAM_STATE_ALLOC_STREAM_TAG; + return 0; +alloc_stream_tag_failed: + kfree(dma); +alloc_failed: + sdw_put_master(mstr); + return ret; +} + +int cnl_sdw_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct sdw_dma_data *dma; + int channels; + enum sdw_data_direction direction; + struct sdw_stream_config stream_config; + struct sdw_port_config port_config; + struct sdw_port_cfg port_cfg; + int ret = 0; + struct skl_pipe_params p_params = {0}; + struct skl_module_cfg *m_cfg; + + p_params.s_fmt = snd_pcm_format_width(params_format(params)); + p_params.ch = params_channels(params); + p_params.s_freq = params_rate(params); + p_params.stream = substream->stream; + + ret = skl_tplg_be_update_params(dai, &p_params); + if (ret) + return ret; + + + dma = snd_soc_dai_get_dma_data(dai, substream); + channels = params_channels(params); + if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) + direction = SDW_DATA_DIR_IN; + else + direction = SDW_DATA_DIR_OUT; + /* Dynamically alloc port and PDI streams for this DAI */ + dma->port = cnl_sdw_alloc_port(dma->mstr, channels, + direction, dma->stream_type); + if (!dma->port) { + dev_err(dai->dev, "Unable to allocate port\n"); + return -EINVAL; + } + dma->stream_state = STREAM_STATE_ALLOC_STREAM; + m_cfg = skl_tplg_be_get_cpr_module(dai, substream->stream); + if (!m_cfg) { + dev_err(dai->dev, "BE Copier not found\n"); + return -EINVAL; + } + m_cfg->sdw_stream_num = dma->port->pdi_stream->sdw_pdi_num; + stream_config.frame_rate = params_rate(params); + stream_config.channel_count = channels; + stream_config.bps = + snd_pcm_format_width(params_format(params)); + stream_config.direction = direction; + ret = sdw_config_stream(dma->mstr, NULL, &stream_config, + dma->stream_tag); + if (ret) { + dev_err(dai->dev, "Unable to configure the stream\n"); + return ret; + } + port_config.num_ports = 1; + port_config.port_cfg = &port_cfg; + port_cfg.port_num = dma->port->port_num; + port_cfg.ch_mask = ((1 << channels) - 1); + ret = sdw_config_port(dma->mstr, NULL, &port_config, dma->stream_tag); + if (ret) { + dev_err(dai->dev, "Unable to configure port\n"); + return ret; + } + dma->stream_state = STREAM_STATE_CONFIG_STREAM; + return 0; +} + +int cnl_sdw_hw_free(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct sdw_dma_data *dma; + int ret = 0; + + dma = snd_soc_dai_get_dma_data(dai, substream); + + if (dma->stream_state == STREAM_STATE_UNPREPARE_STREAM) { + ret = sdw_release_stream(dma->mstr, NULL, dma->stream_tag); + if (ret) + dev_err(dai->dev, "Unable to release stream\n"); + dma->stream_state = STREAM_STATE_RELEASE_STREAM; + if (dma->port && dma->stream_state == + STREAM_STATE_RELEASE_STREAM) { + /* Even if release fails, we continue, + * while winding up we have + * to continue till last one gets winded up + */ + cnl_sdw_free_port(dma->mstr, dma->port->port_num); + dma->stream_state = STREAM_STATE_FREE_STREAM; + dma->port = NULL; + } + } + return 0; +} + +int cnl_sdw_pcm_prepare(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + int ret = 0; + return ret; +} + +int cnl_sdw_pcm_trigger(struct snd_pcm_substream *substream, + int cmd, struct snd_soc_dai *dai) +{ + struct sdw_dma_data *dma; + int ret = 0; + + dma = snd_soc_dai_get_dma_data(dai, substream); + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + case SNDRV_PCM_TRIGGER_RESUME: + ret = sdw_prepare_and_enable(dma->stream_tag, true); + dma->stream_state = STREAM_STATE_ENABLE_STREAM; + break; + + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + case SNDRV_PCM_TRIGGER_SUSPEND: + case SNDRV_PCM_TRIGGER_STOP: + ret = sdw_disable_and_unprepare(dma->stream_tag, true); + dma->stream_state = STREAM_STATE_UNPREPARE_STREAM; + break; + + default: + return -EINVAL; + } + if (ret) + dev_err(dai->dev, "SoundWire commit changes failed\n"); + return ret; + +} + +void cnl_sdw_shutdown(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct sdw_dma_data *dma; + + dma = snd_soc_dai_get_dma_data(dai, substream); + snd_soc_dai_remove_stream_tag(substream, dai); + sdw_release_stream_tag(dma->stream_tag); + dma->stream_state = STREAM_STATE_FREE_STREAM_TAG; + kfree(dma); +} diff --git a/sound/soc/intel/skylake/skl-sdw-pcm.h b/sound/soc/intel/skylake/skl-sdw-pcm.h new file mode 100644 index 000000000000..ab1314a6f408 --- /dev/null +++ b/sound/soc/intel/skylake/skl-sdw-pcm.h @@ -0,0 +1,41 @@ +/* + * skl-sdw-pcm.h - Shared header file skylake PCM operations on soundwire + * + * Copyright (C) 2014-2015 Intel Corp + * Author: Hardik Shah + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * 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; version 2 of the License. + * + * 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. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + */ +#ifndef _SKL_SDW_PCM_H +#define _SKL_SDW_PCM_H +#include +#include +#include + +int cnl_sdw_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai); +int cnl_sdw_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai); +int cnl_sdw_hw_free(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai); +int cnl_sdw_pcm_prepare(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai); +int cnl_sdw_pcm_trigger(struct snd_pcm_substream *substream, + int cmd, struct snd_soc_dai *dai); +void cnl_sdw_shutdown(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai); + +#endif /* _SKL_SDW_PCM_H */ From ed15f90109eda5978e38bf85fb48c21e16e49c6e Mon Sep 17 00:00:00 2001 From: Hardik T Shah Date: Mon, 14 Mar 2016 18:07:38 +0530 Subject: [PATCH 0617/1103] ASoC:CNL: Add SoundWire machine file. This patch adds the machine driver file for the SoundWire codec cirrus logic cs4l42 and SKL platform driver. Change-Id: I9add4935f6fa8513ce21888214d790fa7d8ff4d7 Signed-off-by: Hardik T Shah Signed-off-by: Guneshwor Singh --- sound/soc/intel/boards/Kconfig | 11 ++ sound/soc/intel/boards/Makefile | 2 + sound/soc/intel/boards/cnl_cs42l42.c | 280 +++++++++++++++++++++++++++ 3 files changed, 293 insertions(+) create mode 100644 sound/soc/intel/boards/cnl_cs42l42.c diff --git a/sound/soc/intel/boards/Kconfig b/sound/soc/intel/boards/Kconfig index cccda87f4b34..35787e69f31b 100644 --- a/sound/soc/intel/boards/Kconfig +++ b/sound/soc/intel/boards/Kconfig @@ -295,6 +295,17 @@ config SND_SOC_INTEL_GLK_RT5682_MAX98357A_MACH Say Y if you have such a device. If unsure select "N". +config SND_SOC_INTEL_CNL_CS42L42_MACH + tristate "Cannonlake with CS42L42 SDW mode" + depends on MFD_INTEL_LPSS && I2C && ACPI + select SND_SOC_CS42L42 + select SND_SOC_DMIC + help + This adds support for ASoC CS42L42 codec SDW machine driver. This + will create an alsa sound card for CS42L42. + Say Y or m if you have such a device. This is a recommended option. + If unsure select "N". + endif ## SND_SOC_INTEL_SKYLAKE endif ## SND_SOC_INTEL_MACH diff --git a/sound/soc/intel/boards/Makefile b/sound/soc/intel/boards/Makefile index 87ef8b4058e5..4298c5ad8ca0 100644 --- a/sound/soc/intel/boards/Makefile +++ b/sound/soc/intel/boards/Makefile @@ -22,6 +22,7 @@ snd-soc-kbl_rt5663_rt5514_max98927-objs := kbl_rt5663_rt5514_max98927.o snd-soc-skl_rt286-objs := skl_rt286.o snd-skl_nau88l25_max98357a-objs := skl_nau88l25_max98357a.o snd-soc-skl_nau88l25_ssm4567-objs := skl_nau88l25_ssm4567.o +snd-soc-cnl_cs42l42-objs := cnl_cs42l42.o obj-$(CONFIG_SND_SOC_INTEL_HASWELL_MACH) += snd-soc-sst-haswell.o obj-$(CONFIG_SND_SOC_INTEL_BYT_RT5640_MACH) += snd-soc-sst-byt-rt5640-mach.o @@ -46,3 +47,4 @@ obj-$(CONFIG_SND_SOC_INTEL_KBL_RT5663_RT5514_MAX98927_MACH) += snd-soc-kbl_rt566 obj-$(CONFIG_SND_SOC_INTEL_SKL_RT286_MACH) += snd-soc-skl_rt286.o obj-$(CONFIG_SND_SOC_INTEL_SKL_NAU88L25_MAX98357A_MACH) += snd-skl_nau88l25_max98357a.o obj-$(CONFIG_SND_SOC_INTEL_SKL_NAU88L25_SSM4567_MACH) += snd-soc-skl_nau88l25_ssm4567.o +obj-$(CONFIG_SND_SOC_INTEL_CNL_CS42L42_MACH) += snd-soc-cnl_cs42l42.o diff --git a/sound/soc/intel/boards/cnl_cs42l42.c b/sound/soc/intel/boards/cnl_cs42l42.c new file mode 100644 index 000000000000..78fb9cc1389e --- /dev/null +++ b/sound/soc/intel/boards/cnl_cs42l42.c @@ -0,0 +1,280 @@ +/* + * cnl_cs42l42.c - ASOC Machine driver for Intel cnl_cs42l42 platform + * with CS42L42 soundwire codec. + * + * Copyright (C) 2016 Intel Corp + * Author: Hardik Shah + * + * Based on + * moor_dpcm_florida.c - ASOC Machine driver for Intel Moorefield platform + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * 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; version 2 of the License. + * + * 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. + * + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct cnl_cs42l42_mc_private { + u8 pmic_id; + void __iomem *osc_clk0_reg; + int bt_mode; +}; + +static const struct snd_soc_dapm_widget cnl_cs42l42_widgets[] = { + SND_SOC_DAPM_HP("Headphones", NULL), + SND_SOC_DAPM_MIC("AMIC", NULL), +}; + +static const struct snd_soc_dapm_route cnl_cs42l42_map[] = { + /*Headphones*/ + { "Headphones", NULL, "HP" }, + { "I2NP", NULL, "AMIC" }, + + /* SWM map link the SWM outs to codec AIF */ + { "Playback", NULL, "SDW Tx"}, + { "SDW Tx", NULL, "sdw_codec0_out"}, + + + { "sdw_codec0_in", NULL, "SDW Rx" }, + { "SDW Rx", NULL, "Capture" }, + + +}; + +static const struct snd_kcontrol_new cnl_cs42l42_controls[] = { + SOC_DAPM_PIN_SWITCH("Headphones"), +}; + +static struct snd_soc_dai *cnl_cs42l42_get_codec_dai(struct snd_soc_card *card, + const char *dai_name) +{ + struct snd_soc_pcm_runtime *rtd; + + list_for_each_entry(rtd, &card->rtd_list, list) { + if (!strncmp(rtd->codec_dai->name, dai_name, + strlen(dai_name))) + return rtd->codec_dai; + } + pr_err("%s: unable to find codec dai\n", __func__); + + return NULL; +} + +static int cnl_cs42l42_init(struct snd_soc_pcm_runtime *runtime) +{ + int ret; + struct snd_soc_card *card = runtime->card; + struct snd_soc_component *component = runtime->codec_dai->component; + struct snd_soc_dai *cs_dai = cnl_cs42l42_get_codec_dai(card, "cs42l42"); + + pr_info("Entry %s\n", __func__); + card->dapm.idle_bias_off = true; + + /*Switch to PLL */ + ret = snd_soc_dai_set_sysclk(cs_dai, 0, 9600000, 0); + if (ret != 0) { + dev_err(component->dev, "Failed to set SYSCLK to FLL1: %d\n", ret); + return ret; + } + + ret = snd_soc_add_card_controls(card, cnl_cs42l42_controls, + ARRAY_SIZE(cnl_cs42l42_controls)); + if (ret) { + pr_err("unable to add card controls\n"); + return ret; + } + return 0; +} + +static unsigned int rates_48000[] = { + 48000, + 16000, + 8000, +}; + +static struct snd_pcm_hw_constraint_list constraints_48000 = { + .count = ARRAY_SIZE(rates_48000), + .list = rates_48000, +}; + +static int cnl_cs42l42_startup(struct snd_pcm_substream *substream) +{ + return snd_pcm_hw_constraint_list(substream->runtime, 0, + SNDRV_PCM_HW_PARAM_RATE, + &constraints_48000); +} + +static struct snd_soc_ops cnl_cs42l42_ops = { + .startup = cnl_cs42l42_startup, +}; + +static int cnl_cs42l42_codec_fixup(struct snd_soc_pcm_runtime *rtd, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_dai *be_cpu_dai; + int slot_width = 24; + struct snd_interval *rate = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_RATE); + struct snd_interval *channels = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_CHANNELS); + + pr_debug("Invoked %s for dailink %s\n", __func__, rtd->dai_link->name); + slot_width = 24; + rate->min = rate->max = 48000; + channels->min = channels->max = 2; + snd_mask_none(hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT)); + snd_mask_set(hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT), + SNDRV_PCM_FORMAT_S24_LE); + + pr_info("param width set to:0x%x\n", + snd_pcm_format_width(params_format(params))); + pr_info("Slot width = %d\n", slot_width); + + be_cpu_dai = rtd->cpu_dai; + return 0; +} + +struct snd_soc_dai_link cnl_cs42l42_msic_dailink[] = { + { + .name = "Bxtn Audio Port", + .stream_name = "Audio", + .cpu_dai_name = "System Pin", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .platform_name = "0000:02:18.0", + .init = cnl_cs42l42_init, + .ignore_suspend = 1, + .nonatomic = 1, + .dynamic = 1, + .dpcm_playback = 1, + .dpcm_capture = 1, + .ops = &cnl_cs42l42_ops, + }, + { + .name = "SDW0-Codec", + .cpu_dai_name = "SDW Pin", + .platform_name = "0000:02:18.0", + .codec_name = "sdw-slave0-00:01:fa:42:42:00", + .codec_dai_name = "cs42l42", + .be_hw_params_fixup = cnl_cs42l42_codec_fixup, + .ignore_suspend = 1, + .no_pcm = 1, + .dpcm_playback = 1, + .dpcm_capture = 1, + }, + { + .name = "dmic01", + .cpu_dai_name = "DMIC01 Pin", + .codec_name = "dmic-codec", + .codec_dai_name = "dmic-hifi", + .platform_name = "0000:02:18.0", + .ignore_suspend = 1, + .no_pcm = 1, + .dpcm_capture = 1, + }, + +}; + +/* SoC card */ +static struct snd_soc_card snd_soc_card_cnl_cs42l42 = { + .name = "cnl_cs42l42-audio", + .dai_link = cnl_cs42l42_msic_dailink, + .num_links = ARRAY_SIZE(cnl_cs42l42_msic_dailink), + .dapm_widgets = cnl_cs42l42_widgets, + .num_dapm_widgets = ARRAY_SIZE(cnl_cs42l42_widgets), + .dapm_routes = cnl_cs42l42_map, + .num_dapm_routes = ARRAY_SIZE(cnl_cs42l42_map), +}; + + +static int snd_cnl_cs42l42_mc_probe(struct platform_device *pdev) +{ + int ret_val = 0; + struct cnl_cs42l42_mc_private *drv; + + pr_debug("Entry %s\n", __func__); + + drv = devm_kzalloc(&pdev->dev, sizeof(*drv), GFP_KERNEL); + if (!drv) + return -ENOMEM; + + snd_soc_card_cnl_cs42l42.dev = &pdev->dev; + snd_soc_card_set_drvdata(&snd_soc_card_cnl_cs42l42, drv); + /* Register the card */ + ret_val = snd_soc_register_card(&snd_soc_card_cnl_cs42l42); + if (ret_val && (ret_val != -EPROBE_DEFER)) { + pr_err("snd_soc_register_card failed %d\n", ret_val); + goto unalloc; + } + platform_set_drvdata(pdev, &snd_soc_card_cnl_cs42l42); + pr_info("%s successful\n", __func__); + return ret_val; + +unalloc: + return ret_val; +} + +static int snd_cnl_cs42l42_mc_remove(struct platform_device *pdev) +{ + struct snd_soc_card *soc_card = platform_get_drvdata(pdev); + struct cnl_cs42l42_mc_private *drv = snd_soc_card_get_drvdata(soc_card); + + pr_debug("In %s\n", __func__); + + devm_kfree(&pdev->dev, drv); + snd_soc_card_set_drvdata(soc_card, NULL); + snd_soc_unregister_card(soc_card); + platform_set_drvdata(pdev, NULL); + return 0; +} + +static struct platform_driver snd_cnl_cs42l42_mc_driver = { + .driver = { + .name = "cnl_cs42l42", + }, + .probe = snd_cnl_cs42l42_mc_probe, + .remove = snd_cnl_cs42l42_mc_remove, +}; + +static int snd_cnl_cs42l42_driver_init(void) +{ + pr_info("Canonlake Machine Driver cnl_cs42l42: cs42l42 registered\n"); + return platform_driver_register(&snd_cnl_cs42l42_mc_driver); +} +module_init(snd_cnl_cs42l42_driver_init); + +static void snd_cnl_cs42l42_driver_exit(void) +{ + pr_debug("In %s\n", __func__); + platform_driver_unregister(&snd_cnl_cs42l42_mc_driver); +} +module_exit(snd_cnl_cs42l42_driver_exit) + +MODULE_DESCRIPTION("ASoC CNL Machine driver"); +MODULE_AUTHOR("Hardik Shah "); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:cnl_cs42l42"); From 9bef4469b78ebbf13c733728f4eca85beb6a07b9 Mon Sep 17 00:00:00 2001 From: Hardik T Shah Date: Thu, 17 Mar 2016 10:17:13 +0530 Subject: [PATCH 0618/1103] SoundWire:Intel: Register 4 master controller to the bus. This patch registers the 4 master controller to the bus. Actually this number should come from ACPI BIOS. Change-Id: Id96731765271b72e7f0f5908e17f76996be3b886 Signed-off-by: Hardik T Shah --- sound/soc/intel/skylake/cnl-sst.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/intel/skylake/cnl-sst.c b/sound/soc/intel/skylake/cnl-sst.c index 335ec68b6a32..c90870ee8a61 100644 --- a/sound/soc/intel/skylake/cnl-sst.c +++ b/sound/soc/intel/skylake/cnl-sst.c @@ -509,7 +509,7 @@ static int skl_register_sdw_masters(struct device *dev, struct skl_sst *dsp, struct cnl_sdw_data *p_data; int ret = 0, i, j; /* TODO: This number 4 should come from ACPI */ - dsp->num_sdw_controllers = 1; + dsp->num_sdw_controllers = 4; master = devm_kzalloc(dev, (sizeof(*master) * dsp->num_sdw_controllers), GFP_KERNEL); From e49afb4009ceda9b3ffeaea61b6b1ee3b93d969e Mon Sep 17 00:00:00 2001 From: Hardik T Shah Date: Thu, 17 Mar 2016 11:19:29 +0530 Subject: [PATCH 0619/1103] REVERTME:SDW:CNL: Register only 3 master controller to bus. There is constant BUS errors from the slaves on the 4th SDW bus. This results in kernel dumps, as bus driver reports the bus errors. This is a FPGA slave issue. So when FPGA slave is connected register only 3 master controllers. Change-Id: I0c61d5611ddf51dc56ce87e7b1e389d6638698bc Signed-off-by: Hardik T Shah --- sound/soc/intel/skylake/cnl-sst.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/sound/soc/intel/skylake/cnl-sst.c b/sound/soc/intel/skylake/cnl-sst.c index c90870ee8a61..e4d531680a5a 100644 --- a/sound/soc/intel/skylake/cnl-sst.c +++ b/sound/soc/intel/skylake/cnl-sst.c @@ -509,7 +509,12 @@ static int skl_register_sdw_masters(struct device *dev, struct skl_sst *dsp, struct cnl_sdw_data *p_data; int ret = 0, i, j; /* TODO: This number 4 should come from ACPI */ +#ifdef CONFIG_SDW_MAXIM_SLAVE + + dsp->num_sdw_controllers = 3; +#else dsp->num_sdw_controllers = 4; +#endif master = devm_kzalloc(dev, (sizeof(*master) * dsp->num_sdw_controllers), GFP_KERNEL); From 59b37ba99bc3d6e4523ca72b0883a7e2f3c71a85 Mon Sep 17 00:00:00 2001 From: Hardik T Shah Date: Thu, 17 Mar 2016 17:44:39 +0530 Subject: [PATCH 0620/1103] ASoC:CNL: Add support for DMIC link in SDW machine driver. Add support for DMIC link in the SoundWire machine driver. Change-Id: Ia491792ea9561c6f4cf11250167d6eda82b8d555 Signed-off-by: Hardik T Shah --- sound/soc/intel/boards/cnl_cs42l42.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/sound/soc/intel/boards/cnl_cs42l42.c b/sound/soc/intel/boards/cnl_cs42l42.c index 78fb9cc1389e..879aa4e552f3 100644 --- a/sound/soc/intel/boards/cnl_cs42l42.c +++ b/sound/soc/intel/boards/cnl_cs42l42.c @@ -47,6 +47,7 @@ struct cnl_cs42l42_mc_private { static const struct snd_soc_dapm_widget cnl_cs42l42_widgets[] = { SND_SOC_DAPM_HP("Headphones", NULL), SND_SOC_DAPM_MIC("AMIC", NULL), + SND_SOC_DAPM_MIC("SoC DMIC", NULL), }; static const struct snd_soc_dapm_route cnl_cs42l42_map[] = { @@ -62,6 +63,9 @@ static const struct snd_soc_dapm_route cnl_cs42l42_map[] = { { "sdw_codec0_in", NULL, "SDW Rx" }, { "SDW Rx", NULL, "Capture" }, + {"DMic", NULL, "SoC DMIC"}, + {"DMIC01 Rx", NULL, "Capture"}, + {"dmic01_hifi", NULL, "DMIC01 Rx"}, }; @@ -158,6 +162,16 @@ static int cnl_cs42l42_codec_fixup(struct snd_soc_pcm_runtime *rtd, return 0; } +static int cnl_dmic_fixup(struct snd_soc_pcm_runtime *rtd, + struct snd_pcm_hw_params *params) +{ + struct snd_interval *channels = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_CHANNELS); + channels->min = channels->max = 2; + + return 0; +} + struct snd_soc_dai_link cnl_cs42l42_msic_dailink[] = { { .name = "Bxtn Audio Port", From 2bf127308d10844b11508a3d6735c8ba050c4cb5 Mon Sep 17 00:00:00 2001 From: Hardik T Shah Date: Sat, 23 Apr 2016 18:06:12 +0530 Subject: [PATCH 0621/1103] ASoC:SKL: Add DAI for the SoundWire PDM interface. Add DAI for the PDM Capture between PDM Codec and SoundWire Master. Change-Id: I6d7ba95d06ee7143cb303dcf0a8ae436f6aa1742 Signed-off-by: Hardik T Shah Reviewed-on: --- sound/soc/intel/skylake/skl-pcm.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/sound/soc/intel/skylake/skl-pcm.c b/sound/soc/intel/skylake/skl-pcm.c index d411cf28dd91..da4461762c4e 100644 --- a/sound/soc/intel/skylake/skl-pcm.c +++ b/sound/soc/intel/skylake/skl-pcm.c @@ -1111,6 +1111,21 @@ static struct snd_soc_dai_driver skl_platform_dai[] = { .rates = SNDRV_PCM_RATE_48000, .formats = SNDRV_PCM_FMTBIT_S16_LE, }, +}, +{ + /* Currently adding 1 capture pin, for PDM ideally it + * should be coming from CLT based on endpoints to be supported + */ + .name = "SDW PDM Pin", + .ops = &skl_sdw_dai_ops, + .capture = { + .stream_name = "SDW Rx1", + .channels_min = HDA_MONO, + .channels_max = HDA_QUAD, + .rates = SNDRV_PCM_RATE_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + }, + }, From 938a3ac5899305a3118e91e6173493f5631b50f5 Mon Sep 17 00:00:00 2001 From: Hardik T Shah Date: Sat, 23 Apr 2016 18:15:06 +0530 Subject: [PATCH 0622/1103] Intel:ASoc: Handle SDW PCM hw_params for PDM. Bus driver needs to be configured differently for the PCM and PDM interface between master and slave. Handle the PDM case. Currently upscale factor and the BPS should come from either NHLT or XML. Need to figure out from poland team from where its coming. Change-Id: Ic2e6080f0502f9212ab4256aecda18880248d16b Signed-off-by: Hardik T Shah Reviewed-on: --- sound/soc/intel/skylake/skl-sdw-pcm.c | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/sound/soc/intel/skylake/skl-sdw-pcm.c b/sound/soc/intel/skylake/skl-sdw-pcm.c index 90a11f47586b..af8d117c34ed 100644 --- a/sound/soc/intel/skylake/skl-sdw-pcm.c +++ b/sound/soc/intel/skylake/skl-sdw-pcm.c @@ -113,6 +113,7 @@ int cnl_sdw_hw_params(struct snd_pcm_substream *substream, int ret = 0; struct skl_pipe_params p_params = {0}; struct skl_module_cfg *m_cfg; + int upscale_factor = 16; p_params.s_fmt = snd_pcm_format_width(params_format(params)); p_params.ch = params_channels(params); @@ -145,9 +146,21 @@ int cnl_sdw_hw_params(struct snd_pcm_substream *substream, } m_cfg->sdw_stream_num = dma->port->pdi_stream->sdw_pdi_num; stream_config.frame_rate = params_rate(params); + /* TODO: Get the multiplication factor from NHLT or the XML + * to decide with Poland team from where to get it + */ + if (dma->stream_type == CNL_SDW_PDI_TYPE_PDM) + stream_config.frame_rate *= upscale_factor; stream_config.channel_count = channels; - stream_config.bps = + /* TODO: Get the PDM BPS from NHLT or the XML + * to decide with Poland team from where to get it + */ + if (dma->stream_type == CNL_SDW_PDI_TYPE_PDM) + stream_config.bps = 1; + else + stream_config.bps = snd_pcm_format_width(params_format(params)); + stream_config.direction = direction; ret = sdw_config_stream(dma->mstr, NULL, &stream_config, dma->stream_tag); From da1114e21baf51d00798e181d2bd319cf0b9fcce Mon Sep 17 00:00:00 2001 From: Hardik T Shah Date: Sat, 23 Apr 2016 18:41:07 +0530 Subject: [PATCH 0623/1103] ASoC:Codecs: Add support for SV FPGA SoundWire PDM Codec. Change-Id: I4f48659c9be3238b78ee911449557142a694bf0b Signed-off-by: Hardik T Shah Reviewed-on: --- sound/soc/codecs/Kconfig | 17 ++ sound/soc/codecs/Makefile | 6 + sound/soc/codecs/svfpga-i2c.c | 90 +++++++ sound/soc/codecs/svfpga-sdw.c | 142 ++++++++++ sound/soc/codecs/svfpga.c | 485 ++++++++++++++++++++++++++++++++++ sound/soc/codecs/svfpga.h | 398 ++++++++++++++++++++++++++++ 6 files changed, 1138 insertions(+) create mode 100644 sound/soc/codecs/svfpga-i2c.c create mode 100644 sound/soc/codecs/svfpga-sdw.c create mode 100644 sound/soc/codecs/svfpga.c create mode 100644 sound/soc/codecs/svfpga.h diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index efb095dbcd71..461f41268b68 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -234,6 +234,7 @@ config SND_SOC_ALL_CODECS select SND_SOC_WM9705 if (SND_SOC_AC97_BUS || SND_SOC_AC97_BUS_NEW) select SND_SOC_WM9712 if (SND_SOC_AC97_BUS || SND_SOC_AC97_BUS_NEW) select SND_SOC_WM9713 if (SND_SOC_AC97_BUS || SND_SOC_AC97_BUS_NEW) + select SND_SOC_SVFPGA_I2C if I2C help Normally ASoC codec drivers are only built if a machine driver which uses them is also built since they are only usable with a machine @@ -465,6 +466,22 @@ config SND_SOC_CS42L42 tristate "Cirrus Logic CS42L42 CODEC" depends on I2C +config SND_SOC_SVFPGA + tristate "Intel SVFPGA Codec" + depends on I2C + depends on SDW + +config SND_SOC_SVFPGA_SDW + tristate "Intel SVFPGA Codec - SDW" + depends on SDW + select SND_SOC_SVFPGA + select REGMAP_SDW + +config SND_SOC_SVFPGA_I2C + tristate "Intel SVFPGA Codec - I2C" + depends on I2C + select SND_SOC_SVFPGA + config SND_SOC_CS42L51 tristate diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile index 7ae7c85e8219..f37fca3b9960 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile @@ -46,6 +46,9 @@ snd-soc-cs35l33-objs := cs35l33.o snd-soc-cs35l34-objs := cs35l34.o snd-soc-cs35l35-objs := cs35l35.o snd-soc-cs42l42-objs := cs42l42.o +snd-soc-svfpga-objs := svfpga.o +snd-soc-svfpga-i2c-objs := svfpga-i2c.o +snd-soc-svfpga-sdw-objs := svfpga-sdw.o snd-soc-cs42l51-objs := cs42l51.o snd-soc-cs42l51-i2c-objs := cs42l51-i2c.o snd-soc-cs42l52-objs := cs42l52.o @@ -306,6 +309,9 @@ obj-$(CONFIG_SND_SOC_CS35L33) += snd-soc-cs35l33.o obj-$(CONFIG_SND_SOC_CS35L34) += snd-soc-cs35l34.o obj-$(CONFIG_SND_SOC_CS35L35) += snd-soc-cs35l35.o obj-$(CONFIG_SND_SOC_CS42L42) += snd-soc-cs42l42.o +obj-$(CONFIG_SND_SOC_SVFPGA) += snd-soc-svfpga.o +obj-$(CONFIG_SND_SOC_SVFPGA_I2C) += snd-soc-svfpga-i2c.o +obj-$(CONFIG_SND_SOC_SVFPGA_SDW) += snd-soc-svfpga-sdw.o obj-$(CONFIG_SND_SOC_CS42L51) += snd-soc-cs42l51.o obj-$(CONFIG_SND_SOC_CS42L51_I2C) += snd-soc-cs42l51-i2c.o obj-$(CONFIG_SND_SOC_CS42L52) += snd-soc-cs42l52.o diff --git a/sound/soc/codecs/svfpga-i2c.c b/sound/soc/codecs/svfpga-i2c.c new file mode 100644 index 000000000000..07dc09f9ec5a --- /dev/null +++ b/sound/soc/codecs/svfpga-i2c.c @@ -0,0 +1,90 @@ +/* + * svfpga-i2c.c -- SVFPGA ALSA SoC audio driver + * + * Copyright 2015 Intel, Inc. + * + * Author: Hardik Shah + * Dummy ASoC Codec Driver based Cirrus Logic CS42L42 Codec + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "svfpga.h" + +extern const struct regmap_config svfpga_i2c_regmap; +extern const struct dev_pm_ops svfpga_runtime_pm; +static int svfpga_i2c_probe(struct i2c_client *i2c, + const struct i2c_device_id *i2c_id) +{ + struct regmap *regmap; + + if (!i2c_check_functionality(i2c->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) + return -EINVAL; + + regmap = devm_regmap_init_i2c(i2c, &svfpga_i2c_regmap); + return svfpga_probe(&i2c->dev, regmap, NULL); +} + +static int svfpga_i2c_remove(struct i2c_client *i2c) +{ + return svfpga_remove(&i2c->dev); +} + +static const struct of_device_id svfpga_of_match[] = { + { .compatible = "svfpga", }, + {}, +}; +MODULE_DEVICE_TABLE(of, svfpga_of_match); + + +static const struct i2c_device_id svfpga_id[] = { + {"svfpga", 0}, + {} +}; + +MODULE_DEVICE_TABLE(i2c, svfpga_id); + +static struct i2c_driver svfpga_i2c_driver = { + .driver = { + .name = "svfpga", + .pm = &svfpga_runtime_pm, + .of_match_table = svfpga_of_match, + }, + .id_table = svfpga_id, + .probe = svfpga_i2c_probe, + .remove = svfpga_i2c_remove, +}; + +module_i2c_driver(svfpga_i2c_driver); + +MODULE_DESCRIPTION("ASoC SVFPGA driver I2C"); +MODULE_DESCRIPTION("ASoC SVFPGA driver SDW"); +MODULE_AUTHOR("Hardik shah, Intel Inc, + * Dummy ASoC Codec Driver based Cirrus Logic CS42L42 Codec + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +#include +#include +#include +#include +#include +#include +#include "svfpga.h" + +extern const struct regmap_config svfpga_sdw_regmap; +extern const struct dev_pm_ops svfpga_runtime_pm; + +static int svfpga_register_sdw_capabilties(struct sdw_slv *sdw, + const struct sdw_slv_id *sdw_id) +{ + struct sdw_slv_capabilities cap; + struct sdw_slv_dpn_capabilities *dpn_cap = NULL; + struct port_audio_mode_properties *prop = NULL; + int i, j; + + cap.wake_up_unavailable = true; + cap.test_mode_supported = false; + cap.clock_stop1_mode_supported = false; + cap.simplified_clock_stop_prepare = false; + cap.highphy_capable = true; + cap.paging_supported = false; + cap.bank_delay_support = false; + cap.port_15_read_behavior = 0; + cap.sdw_dp0_supported = false; + cap.num_of_sdw_ports = 3; + cap.sdw_dpn_cap = devm_kzalloc(&sdw->dev, + ((sizeof(struct sdw_slv_dpn_capabilities)) * + cap.num_of_sdw_ports), GFP_KERNEL); + for (i = 0; i < cap.num_of_sdw_ports; i++) { + dpn_cap = &cap.sdw_dpn_cap[i]; + if (i == 0 || i == 2) + dpn_cap->port_direction = SDW_PORT_SOURCE; + else + dpn_cap->port_direction = SDW_PORT_SINK; + + dpn_cap->port_number = i+1; + dpn_cap->max_word_length = 24; + dpn_cap->min_word_length = 16; + dpn_cap->num_word_length = 0; + dpn_cap->word_length_buffer = NULL; + dpn_cap->dpn_type = SDW_FULL_DP; + dpn_cap->dpn_grouping = SDW_BLOCKGROUPCOUNT_1; + dpn_cap->prepare_ch = SDW_SIMPLIFIED_CP_SM; + dpn_cap->imp_def_intr_mask = 0x0; + dpn_cap->min_ch_num = 1; + dpn_cap->max_ch_num = 2; + dpn_cap->num_ch_supported = 0; + dpn_cap->ch_supported = NULL; + dpn_cap->port_flow_mode_mask = SDW_PORT_FLOW_MODE_ISOCHRONOUS; + dpn_cap->block_packing_mode_mask = + SDW_PORT_BLK_PKG_MODE_BLK_PER_PORT_MASK | + SDW_PORT_BLK_PKG_MODE_BLK_PER_CH_MASK; + dpn_cap->port_encoding_type_mask = + SDW_PORT_ENCODING_TYPE_TWOS_CMPLMNT | + SDW_PORT_ENCODING_TYPE_SIGN_MAGNITUDE | + SDW_PORT_ENCODING_TYPE_IEEE_32_FLOAT; + dpn_cap->num_audio_modes = 1; + + dpn_cap->mode_properties = devm_kzalloc(&sdw->dev, + ((sizeof(struct port_audio_mode_properties)) * + dpn_cap->num_audio_modes), GFP_KERNEL); + for (j = 0; j < dpn_cap->num_audio_modes; j++) { + prop = &dpn_cap->mode_properties[j]; + prop->max_frequency = 16000000; + prop->min_frequency = 1000000; + prop->num_freq_configs = 0; + prop->freq_supported = NULL; + prop->glitchless_transitions_mask = 0x1; + prop->max_sampling_frequency = 192000; + prop->min_sampling_frequency = 8000; + prop->num_sampling_freq_configs = 0; + prop->sampling_freq_config = NULL; + prop->ch_prepare_behavior = SDW_CH_PREP_ANY_TIME; + } + } + return sdw_register_slave_capabilities(sdw, &cap); + +} +static int svfpga_sdw_probe(struct sdw_slv *sdw, + const struct sdw_slv_id *sdw_id) +{ + struct regmap *regmap; + int ret; + + /* Wait for codec internal initialization for 2.5ms minimum */ + msleep(5); + regmap = devm_regmap_init_sdwint(sdw, &svfpga_sdw_regmap); + if (!regmap) + return -EINVAL; + ret = svfpga_register_sdw_capabilties(sdw, sdw_id); + if (ret) + return ret; + return svfpga_probe(&sdw->dev, regmap, sdw); +} + +static int svfpga_sdw_remove(struct sdw_slv *sdw) +{ + return svfpga_remove(&sdw->dev); +} + +static const struct sdw_slv_id svfpga_id[] = { + {"00:01:fa:42:42:00", 0}, + {"14:13:20:05:86:80", 0}, + {} +}; + +MODULE_DEVICE_TABLE(sdwint, svfpga_id); + +static struct sdw_slave_driver svfpga_sdw_driver = { + .driver_type = SDW_DRIVER_TYPE_SLAVE, + .driver = { + .name = "svfpga-codec", + .pm = &svfpga_runtime_pm, + }, + .probe = svfpga_sdw_probe, + .remove = svfpga_sdw_remove, + .id_table = svfpga_id, +}; + +module_sdw_slave_driver(svfpga_sdw_driver); + +MODULE_DESCRIPTION("ASoC SVFPGA driver SDW"); +MODULE_AUTHOR("Hardik shah, Intel Inc, + * Dummy ASoC Codec Driver based Cirrus Logic CS42L42 Codec + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +/* #define DEBUG 1 */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "svfpga.h" + +struct sdw_stream_data { + int stream_tag; +}; + + + +static const struct reg_default svfpga_reg_defaults[] = { +}; + +static bool svfpga_readable_register(struct device *dev, unsigned int reg) +{ + return false; +} + +static bool svfpga_volatile_register(struct device *dev, unsigned int reg) +{ + return false; +} + +static const struct regmap_range_cfg svfpga_page_range = { + .name = "Pages", + .range_min = 0, + .range_max = 0, + .selector_reg = SVFPGA_PAGE_REGISTER, + .selector_mask = 0xff, + .selector_shift = 0, + .window_start = 0, + .window_len = 256, +}; + +const struct regmap_config svfpga_i2c_regmap = { + .reg_bits = 8, + .val_bits = 8, + + .readable_reg = svfpga_readable_register, + .volatile_reg = svfpga_volatile_register, + + .ranges = &svfpga_page_range, + .num_ranges = 1, + + .max_register = 0, + .reg_defaults = svfpga_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(svfpga_reg_defaults), + .cache_type = REGCACHE_RBTREE, +}; +EXPORT_SYMBOL(svfpga_i2c_regmap); + +const struct regmap_config svfpga_sdw_regmap = { + .reg_bits = 32, + .val_bits = 8, + + .readable_reg = svfpga_readable_register, + .volatile_reg = svfpga_volatile_register, + + .max_register = SVFPGA_MAX_REGISTER, + .reg_defaults = svfpga_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(svfpga_reg_defaults), + .cache_type = REGCACHE_RBTREE, +}; +EXPORT_SYMBOL(svfpga_sdw_regmap); + +static int svfpga_hpdrv_evt(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + return 0; +} + +static const struct snd_soc_dapm_widget svfpga_dapm_widgets[] = { + SND_SOC_DAPM_OUTPUT("HP"), + SND_SOC_DAPM_INPUT("I2NP"), + SND_SOC_DAPM_AIF_IN("SDIN", NULL, 0, SVFPGA_ASP_CLK_CFG, + SVFPGA_ASP_SCLK_EN_SHIFT, false), + SND_SOC_DAPM_OUT_DRV_E("HPDRV", SND_SOC_NOPM, 0, + 0, NULL, 0, svfpga_hpdrv_evt, + SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD) +}; + +static const struct snd_soc_dapm_route svfpga_audio_map[] = { + {"SDIN", NULL, "Playback"}, + {"HPDRV", NULL, "SDIN"}, + {"HP", NULL, "HPDRV"}, + {"Capture", NULL, "I2NP"}, + +}; + +static int svfpga_set_bias_level(struct snd_soc_component *component, + enum snd_soc_bias_level level) +{ + return 0; +} + +static const struct snd_soc_component_driver soc_codec_dev_svfpga = { + .set_bias_level = svfpga_set_bias_level, + .dapm_widgets = svfpga_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(svfpga_dapm_widgets), + .dapm_routes = svfpga_audio_map, + .num_dapm_routes = ARRAY_SIZE(svfpga_audio_map), +}; + +static int svfpga_program_stream_tag(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai, int stream_tag) +{ + struct sdw_stream_data *stream_data; + + stream_data = kzalloc(sizeof(*stream_data), GFP_KERNEL); + if (!stream_data) + return -ENOMEM; + stream_data->stream_tag = stream_tag; + snd_soc_dai_set_dma_data(dai, substream, stream_data); + return 0; +} + +static int svfpga_remove_stream_tag(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) + +{ + struct sdw_stream_data *stream_data; + + stream_data = snd_soc_dai_get_dma_data(dai, substream); + kfree(stream_data); + return 0; +} + + + +static int svfpga_pcm_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct svfpga_private *svfpga = snd_soc_component_get_drvdata(component); + int retval; + enum sdw_data_direction direction; + struct sdw_stream_config stream_config; + struct sdw_port_config port_config; + struct sdw_port_cfg port_cfg; + struct sdw_stream_data *stream; + int port; + int num_channels; + int upscale_factor = 16; + + stream = snd_soc_dai_get_dma_data(dai, substream); + + if (!svfpga->sdw) + return 0; + + /* SoundWire specific configuration */ + /* This code assumes port 1 for playback and port 2 for capture */ + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + direction = SDW_DATA_DIR_IN; + port = 2; + } else { + direction = SDW_DATA_DIR_OUT; + port = 0; + } + stream_config.frame_rate = params_rate(params); + stream_config.frame_rate *= upscale_factor; + stream_config.channel_count = params_channels(params); + stream_config.bps = + snd_pcm_format_width(params_format(params)); + stream_config.bps = 1; + stream_config.direction = direction; + retval = sdw_config_stream(svfpga->sdw->mstr, svfpga->sdw, &stream_config, + stream->stream_tag); + if (retval) { + dev_err(dai->dev, "Unable to configure the stream\n"); + return retval; + } + port_config.num_ports = 1; + port_config.port_cfg = &port_cfg; + port_cfg.port_num = port; + num_channels = params_channels(params); + port_cfg.ch_mask = (1 << (num_channels)) - 1; + retval = sdw_config_port(svfpga->sdw->mstr, svfpga->sdw, + &port_config, stream->stream_tag); + if (retval) { + dev_err(dai->dev, "Unable to configure port\n"); + return retval; + } + + return retval; +} + +int svfpga_pcm_hw_free(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct svfpga_private *svfpga = snd_soc_component_get_drvdata(component); + struct sdw_stream_data *stream = snd_soc_dai_get_dma_data(dai, + substream); + if (!svfpga->sdw) + return 0; + sdw_release_stream(svfpga->sdw->mstr, svfpga->sdw, stream->stream_tag); + return 0; +} + +static struct snd_soc_dai_ops svfpga_ops = { + .hw_params = svfpga_pcm_hw_params, + .hw_free = svfpga_pcm_hw_free, + .program_stream_tag = svfpga_program_stream_tag, + .remove_stream_tag = svfpga_remove_stream_tag, +}; + +static struct snd_soc_dai_driver svfpga_dai = { + .name = "svfpga", + .playback = { + .stream_name = "Playback", + .channels_min = 1, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + }, + .capture = { + .stream_name = "Capture", + .channels_min = 1, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + }, + .ops = &svfpga_ops, +}; + +int svfpga_probe(struct device *dev, struct regmap *regmap, + struct sdw_slv *slave) +{ + struct svfpga_private *svfpga; + int ret; + struct sdw_msg rd_msg, wr_msg; + u8 rbuf[1] = {0}, wbuf[1] = {0}; + + + svfpga = devm_kzalloc(dev, sizeof(struct svfpga_private), + GFP_KERNEL); + if (!svfpga) + return -ENOMEM; + + dev_set_drvdata(dev, svfpga); + + svfpga->regmap = regmap; + svfpga->sdw = slave; + ret = devm_snd_soc_register_component(dev, + &soc_codec_dev_svfpga, &svfpga_dai, 1); + dev_info(&slave->dev, "Configuring codec for pattern\n"); + + wr_msg.ssp_tag = 0; + wr_msg.flag = SDW_MSG_FLAG_WRITE; + wr_msg.addr = 0x8; + wr_msg.len = 1; + wr_msg.buf = wbuf; + wr_msg.slave_addr = slave->slv_number; + wr_msg.addr_page1 = 0x0; + wr_msg.addr_page2 = 0x0; + wbuf[0] = 0x0; + ret = sdw_slave_transfer(slave->mstr, &wr_msg, 1); + if (ret != 1) { + ret = -EINVAL; + dev_err(&slave->dev, "Interrupt status read failed for slave %x\n", slave->slv_number); + goto out; + } + wr_msg.addr = 0x1804; + wbuf[0] = 0x1; + ret = sdw_slave_transfer(slave->mstr, &wr_msg, 1); + if (ret != 1) { + ret = -EINVAL; + dev_err(&slave->dev, "Interrupt status read failed for slave %x\n", slave->slv_number); + goto out; + } + rd_msg.ssp_tag = 0; + rd_msg.flag = SDW_MSG_FLAG_READ; + rd_msg.addr = 0x180b; + rd_msg.len = 1; + rd_msg.buf = rbuf; + rd_msg.slave_addr = slave->slv_number; + rd_msg.addr_page1 = 0x0; + rd_msg.addr_page2 = 0x0; + ret = sdw_slave_transfer(slave->mstr, &rd_msg, 1); + if (ret != 1) { + ret = -EINVAL; + dev_err(&slave->dev, "Interrupt status read failed for slave %x\n", slave->slv_number); + goto out; + } + wr_msg.addr = 0x180b; + wbuf[0] = 0x1a; + ret = sdw_slave_transfer(slave->mstr, &wr_msg, 1); + if (ret != 1) { + ret = -EINVAL; + dev_err(&slave->dev, "Interrupt status read failed for slave %x\n", slave->slv_number); + goto out; + } + wr_msg.addr = 0x180c; + wbuf[0] = 0x0; + ret = sdw_slave_transfer(slave->mstr, &wr_msg, 1); + if (ret != 1) { + ret = -EINVAL; + dev_err(&slave->dev, "Interrupt status read failed for slave %x\n", slave->slv_number); + goto out; + } + wr_msg.addr = 0x180d; + wbuf[0] = 0x0; + ret = sdw_slave_transfer(slave->mstr, &wr_msg, 1); + if (ret != 1) { + ret = -EINVAL; + dev_err(&slave->dev, "Interrupt status read failed for slave %x\n", slave->slv_number); + goto out; + } + wr_msg.addr = 0x180e; + wbuf[0] = 0x0; + ret = sdw_slave_transfer(slave->mstr, &wr_msg, 1); + if (ret != 1) { + ret = -EINVAL; + dev_err(&slave->dev, "Interrupt status read failed for slave %x\n", slave->slv_number); + goto out; + } + wr_msg.addr = 0x180f; + wbuf[0] = 0x0; + ret = sdw_slave_transfer(slave->mstr, &wr_msg, 1); + if (ret != 1) { + ret = -EINVAL; + dev_err(&slave->dev, "Interrupt status read failed for slave %x\n", slave->slv_number); + goto out; + } + wr_msg.addr = 0x1810; + wbuf[0] = 0xb8; + ret = sdw_slave_transfer(slave->mstr, &wr_msg, 1); + if (ret != 1) { + ret = -EINVAL; + dev_err(&slave->dev, "Interrupt status read failed for slave %x\n", slave->slv_number); + goto out; + } + wr_msg.addr = 0x1811; + wbuf[0] = 0xd; + ret = sdw_slave_transfer(slave->mstr, &wr_msg, 1); + if (ret != 1) { + ret = -EINVAL; + dev_err(&slave->dev, "Interrupt status read failed for slave %x\n", slave->slv_number); + goto out; + } + wr_msg.addr = 0x1812; + wbuf[0] = 0x0; + ret = sdw_slave_transfer(slave->mstr, &wr_msg, 1); + if (ret != 1) { + ret = -EINVAL; + dev_err(&slave->dev, "Interrupt status read failed for slave %x\n", slave->slv_number); + goto out; + } + wr_msg.addr = 0x1813; + wbuf[0] = 0x0; + ret = sdw_slave_transfer(slave->mstr, &wr_msg, 1); + if (ret != 1) { + ret = -EINVAL; + dev_err(&slave->dev, "Interrupt status read failed for slave %x\n", slave->slv_number); + goto out; + } + wr_msg.addr = 0x181c; + wbuf[0] = 0x6e; + ret = sdw_slave_transfer(slave->mstr, &wr_msg, 1); + if (ret != 1) { + ret = -EINVAL; + dev_err(&slave->dev, "Interrupt status read failed for slave %x\n", slave->slv_number); + goto out; + } + wr_msg.addr = 0x181d; + wbuf[0] = 0x3; + ret = sdw_slave_transfer(slave->mstr, &wr_msg, 1); + if (ret != 1) { + ret = -EINVAL; + dev_err(&slave->dev, "Interrupt status read failed for slave %x\n", slave->slv_number); + goto out; + } + wr_msg.addr = 0x181e; + wbuf[0] = 0x0; + ret = sdw_slave_transfer(slave->mstr, &wr_msg, 1); + if (ret != 1) { + ret = -EINVAL; + dev_err(&slave->dev, "Interrupt status read failed for slave %x\n", slave->slv_number); + goto out; + } + wr_msg.addr = 0x181f; + wbuf[0] = 0x0; + ret = sdw_slave_transfer(slave->mstr, &wr_msg, 1); + if (ret != 1) { + ret = -EINVAL; + dev_err(&slave->dev, "Interrupt status read failed for slave %x\n", slave->slv_number); + goto out; + } + wr_msg.addr = 0x180b; + wbuf[0] = 0x1b; + ret = sdw_slave_transfer(slave->mstr, &wr_msg, 1); + if (ret != 1) { + ret = -EINVAL; + dev_err(&slave->dev, "Interrupt status read failed for slave %x\n", slave->slv_number); + goto out; + } + rd_msg.addr = 0x180b; + rbuf[0] = 0x0; + ret = sdw_slave_transfer(slave->mstr, &rd_msg, 1); + if (ret != 1) { + ret = -EINVAL; + dev_err(&slave->dev, "Interrupt status read failed for slave %x\n", slave->slv_number); + goto out; + } + dev_info(&slave->dev, "Codec programmed with Value %x\n", rbuf[0]); + return ret; +out: + return -EIO; +} +EXPORT_SYMBOL(svfpga_probe); + +int svfpga_remove(struct device *dev) +{ + + dev_info(dev, "Removing\n"); + + return 0; +} +EXPORT_SYMBOL(svfpga_remove); + +#ifdef CONFIG_PM +static int svfpga_runtime_suspend(struct device *dev) +{ + return 0; +} + +static int svfpga_runtime_resume(struct device *dev) +{ + + return 0; +} +#endif + +const struct dev_pm_ops svfpga_runtime_pm = { + SET_RUNTIME_PM_OPS(svfpga_runtime_suspend, svfpga_runtime_resume, + NULL) +}; +EXPORT_SYMBOL(svfpga_runtime_pm); + +MODULE_DESCRIPTION("ASoC SVFPGA driver"); +MODULE_DESCRIPTION("ASoC SVFPGA driver SDW"); +MODULE_AUTHOR("Hardik shah, Intel Inc, + * Dummy ASoC Codec Driver based Cirrus Logic CS42L42 Codec + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +#ifndef __SVFPGA_H__ +#define __SVFPGA_H__ + +#include + +#define SVFPGA_PAGE_REGISTER 0x00 /* Page Select Register */ +#define SVFPGA_WIN_START 0x00 +#define SVFPGA_WIN_LEN 0x100 +#define SVFPGA_RANGE_MIN 0x00 +#define SVFPGA_RANGE_MAX 0x7F + +#define SVFPGA_PAGE_10 0x9000 +#define SVFPGA_PAGE_11 0x9100 +#define SVFPGA_PAGE_12 0x9200 +#define SVFPGA_PAGE_13 0x9300 +#define SVFPGA_PAGE_15 0x9500 +#define SVFPGA_PAGE_19 0x9900 +#define SVFPGA_PAGE_1B 0x9B00 +#define SVFPGA_PAGE_1C 0x9C00 +#define SVFPGA_PAGE_1D 0x9D00 +#define SVFPGA_PAGE_1F 0x9F00 +#define SVFPGA_PAGE_20 0xA000 +#define SVFPGA_PAGE_21 0xA100 +#define SVFPGA_PAGE_23 0xA300 +#define SVFPGA_PAGE_24 0xA400 +#define SVFPGA_PAGE_25 0xA500 +#define SVFPGA_PAGE_26 0xA600 +#define SVFPGA_PAGE_28 0xA800 +#define SVFPGA_PAGE_29 0xA900 +#define SVFPGA_PAGE_2A 0xAA00 +#define SVFPGA_PAGE_30 0xB000 + +#define SVFPGA_CHIP_ID 0x42A42 + +/* Page 0x10 Global Registers */ +#define SVFPGA_DEVID_AB (SVFPGA_PAGE_10 + 0x01) +#define SVFPGA_DEVID_CD (SVFPGA_PAGE_10 + 0x02) +#define SVFPGA_DEVID_E (SVFPGA_PAGE_10 + 0x03) +#define SVFPGA_FABID (SVFPGA_PAGE_10 + 0x04) +#define SVFPGA_REVID (SVFPGA_PAGE_10 + 0x05) +#define SVFPGA_FRZ_CTL (SVFPGA_PAGE_10 + 0x06) +#define SVFPGA_SRC_CTL (SVFPGA_PAGE_10 + 0x07) +#define SVFPGA_MCLK_STAT (SVFPGA_PAGE_10 + 0x08) + +#define SVFPGA_MCLK_CTL (SVFPGA_PAGE_10 + 0x09) +#define SVFPGA_INTERNAL_FS_SHIFT 1 +#define SVFPGA_INTERNAL_FS_MASK (1 << SVFPGA_INTERNAL_FS_SHIFT) + +#define SVFPGA_SFTRAMP_RATE (SVFPGA_PAGE_10 + 0x0A) +#define SVFPGA_I2C_DEBOUNCE (SVFPGA_PAGE_10 + 0x0E) +#define SVFPGA_I2C_STRETCH (SVFPGA_PAGE_10 + 0x0F) +#define SVFPGA_I2C_TIMEOUT (SVFPGA_PAGE_10 + 0x10) + +/* Page 0x11 Power and Headset Detect Registers */ +#define SVFPGA_PWR_CTL1 (SVFPGA_PAGE_11 + 0x01) +#define SVFPGA_ASP_DAI_PDN_SHIFT 6 +#define SVFPGA_ASP_DAI_PDN_MASK (1 << SVFPGA_ASP_DAI_PDN_SHIFT) +#define SVFPGA_MIXER_PDN_SHIFT 5 +#define SVFPGA_MIXER_PDN_MASK (1 << SVFPGA_MIXER_PDN_SHIFT) +#define SVFPGA_HP_PDN_SHIFT 3 +#define SVFPGA_HP_PDN_MASK (1 << SVFPGA_HP_PDN_SHIFT) +#define SVFPGA_PDN_ALL_SHIFT 0 +#define SVFPGA_PDN_ALL_MASK (1 << SVFPGA_PDN_ALL_SHIFT) + +#define SVFPGA_PWR_CTL2 (SVFPGA_PAGE_11 + 0x02) +#define SVFPGA_PWR_CTL3 (SVFPGA_PAGE_11 + 0x03) +#define SVFPGA_RSENSE_CTL1 (SVFPGA_PAGE_11 + 0x04) +#define SVFPGA_RSENSE_CTL2 (SVFPGA_PAGE_11 + 0x05) + +#define SVFPGA_OSC_SWITCH (SVFPGA_PAGE_11 + 0x07) +#define SVFPGA_SCLK_PRESENT_SHIFT 0 +#define SVFPGA_SCLK_PRESENT_MASK (1 << SVFPGA_SCLK_PRESENT_SHIFT) + +#define SVFPGA_OSC_SWITCH_STATUS (SVFPGA_PAGE_11 + 0x09) +#define SVFPGA_RSENSE_CTL3 (SVFPGA_PAGE_11 + 0x12) +#define SVFPGA_TSENSE_CTL (SVFPGA_PAGE_11 + 0x13) +#define SVFPGA_TRSENSE_STATUS (SVFPGA_PAGE_11 + 0x15) +#define SVFPGA_HSDET_CTL1 (SVFPGA_PAGE_11 + 0x1F) +#define SVFPGA_HSDET_CTL2 (SVFPGA_PAGE_11 + 0x20) +#define SVFPGA_HS_SWITCH_CTL (SVFPGA_PAGE_11 + 0x21) +#define SVFPGA_HS_DET_STATUS (SVFPGA_PAGE_11 + 0x24) +#define SVFPGA_HS_CLAMP_DISABLE (SVFPGA_PAGE_11 + 0x29) + +/* Page 0x12 Clocking Registers */ +#define SVFPGA_MCLK_SRC_SEL (SVFPGA_PAGE_12 + 0x01) +#define SVFPGA_MCLKDIV_SHIFT 1 +#define SVFPGA_MCLKDIV_MASK (1 << SVFPGA_MCLKDIV_SHIFT) +#define SVFPGA_MCLK_SRC_SEL_SHIFT 0 +#define SVFPGA_MCLK_SRC_SEL_MASK (1 << SVFPGA_MCLK_SRC_SEL_SHIFT) + +#define SVFPGA_SPDIF_CLK_CFG (SVFPGA_PAGE_12 + 0x02) + +#define SVFPGA_FSYNC_PW_LOWER (SVFPGA_PAGE_12 + 0x03) +#define SVFPGA_FSYNC_PW_UPPER (SVFPGA_PAGE_12 + 0x04) +#define SVFPGA_FSYNC_PULSE_WIDTH_SHIFT 0 +#define SVFPGA_FSYNC_PULSE_WIDTH_MASK (0xff << SVFPGA_FSYNC_PULSE_WIDTH_SHIFT) + +#define SVFPGA_FSYNC_P_LOWER (SVFPGA_PAGE_12 + 0x05) +#define SVFPGA_FSYNC_P_UPPER (SVFPGA_PAGE_12 + 0x06) +#define SVFPGA_FSYNC_PERIOD_SHIFT 0 +#define SVFPGA_FSYNC_PERIOD_MASK (0xff << SVFPGA_FSYNC_PERIOD_SHIFT) + +#define SVFPGA_ASP_CLK_CFG (SVFPGA_PAGE_12 + 0x07) +#define SVFPGA_ASP_SCLK_EN_SHIFT 5 +#define SVFPGA_ASP_SCLK_EN_MASK (1 << SVFPGA_ASP_SCLK_EN_SHIFT) +#define SVFPGA_ASP_MASTER_MODE 0x01 +#define SVFPGA_ASP_SLAVE_MODE 0x00 +#define SVFPGA_ASP_MODE_SHIFT 4 +#define SVFPGA_ASP_MODE_MASK (1 << SVFPGA_ASP_MODE_SHIFT) +#define SVFPGA_ASP_SCPOL_IN_DAC_SHIFT 2 +#define SVFPGA_ASP_SCPOL_IN_DAC_MASK (1 << SVFPGA_ASP_SCPOL_IN_DAC_SHIFT) +#define SVFPGA_ASP_LCPOL_IN_SHIFT 0 +#define SVFPGA_ASP_LCPOL_IN_MASK (1 << SVFPGA_ASP_LCPOL_IN_SHIFT) +#define SVFPGA_ASP_POL_INV 1 + +#define SVFPGA_ASP_FRM_CFG (SVFPGA_PAGE_12 + 0x08) +#define SVFPGA_ASP_5050_SHIFT 3 +#define SVFPGA_ASP_5050_MASK (1 << SVFPGA_ASP_5050_SHIFT) +#define SVFPGA_ASP_FSD_SHIFT 0 +#define SVFPGA_ASP_FSD_MASK (7 << SVFPGA_ASP_FSD_SHIFT) +#define SVFPGA_ASP_FSD_1_0 2 + +#define SVFPGA_FS_RATE_EN (SVFPGA_PAGE_12 + 0x09) +#define SVFPGA_FS_EN_SHIFT 0 +#define SVFPGA_FS_EN_MASK (0xf << SVFPGA_FS_EN_SHIFT) +#define SVFPGA_FS_EN_IASRC_96K 0x1 +#define SVFPGA_FS_EN_OASRC_96K 0x2 + +#define SVFPGA_IN_ASRC_CLK (SVFPGA_PAGE_12 + 0x0A) +#define SVFPGA_CLK_IASRC_SEL_SHIFT 0 +#define SVFPGA_CLK_IASRC_SEL_MASK (1 << SVFPGA_CLK_IASRC_SEL_SHIFT) +#define SVFPGA_CLK_IASRC_SEL_12 1 + +#define SVFPGA_OUT_ASRC_CLK (SVFPGA_PAGE_12 + 0x0B) +#define SVFPGA_CLK_OASRC_SEL_SHIFT 0 +#define SVFPGA_CLK_OASRC_SEL_MASK (1 << SVFPGA_CLK_OASRC_SEL_SHIFT) +#define SVFPGA_CLK_OASRC_SEL_12 1 + +#define SVFPGA_PLL_DIV_CFG1 (SVFPGA_PAGE_12 + 0x0C) +#define SVFPGA_SCLK_PREDIV_SHIFT 0 +#define SVFPGA_SCLK_PREDIV_MASK (3 << SVFPGA_SCLK_PREDIV_SHIFT) + +/* Page 0x13 Interrupt Registers */ +/* Interrupts */ +#define SVFPGA_ADC_OVFL_STATUS (SVFPGA_PAGE_13 + 0x01) +#define SVFPGA_MIXER_STATUS (SVFPGA_PAGE_13 + 0x02) +#define SVFPGA_SRC_STATUS (SVFPGA_PAGE_13 + 0x03) +#define SVFPGA_ASP_RX_STATUS (SVFPGA_PAGE_13 + 0x04) +#define SVFPGA_ASP_TX_STATUS (SVFPGA_PAGE_13 + 0x05) +#define SVFPGA_CODEC_STATUS (SVFPGA_PAGE_13 + 0x08) +#define SVFPGA_ALTDET_STATUS1 (SVFPGA_PAGE_13 + 0x09) +#define SVFPGA_ALTDET_STATUS2 (SVFPGA_PAGE_13 + 0x0A) +#define SVFPGA_DACADC_LSTATUS (SVFPGA_PAGE_13 + 0x0B) +#define SVFPGA_VPMON_STATUS (SVFPGA_PAGE_13 + 0x0D) +#define SVFPGA_PLL_LOCK (SVFPGA_PAGE_13 + 0x0E) +#define SVFPGA_TSRS_PLUG_STATUS (SVFPGA_PAGE_13 + 0x0F) +/* Masks */ +#define SVFPGA_ADC_OVFL_MASK (SVFPGA_PAGE_13 + 0x16) +#define SVFPGA_MIXER_MASK (SVFPGA_PAGE_13 + 0x17) +#define SVFPGA_SRC_MASK (SVFPGA_PAGE_13 + 0x18) +#define SVFPGA_ASP_RX_MASK (SVFPGA_PAGE_13 + 0x19) +#define SVFPGA_ASP_TX_MASK (SVFPGA_PAGE_13 + 0x1A) +#define SVFPGA_CODEC_MASK (SVFPGA_PAGE_13 + 0x1B) +#define SVFPGA_DACADC_LMASK (SVFPGA_PAGE_13 + 0x1C) +#define SVFPGA_VPMON_MASK (SVFPGA_PAGE_13 + 0x1E) +#define SVFPGA_PLL_LOCK_MASK (SVFPGA_PAGE_13 + 0x1F) +#define SVFPGA_TSRS_PLUG_MASK (SVFPGA_PAGE_13 + 0x20) + +/* Page 0x15 Fractional-N PLL Registers */ +#define SVFPGA_PLL_CTL1 (SVFPGA_PAGE_15 + 0x01) +#define SVFPGA_PLL_START_SHIFT 0 +#define SVFPGA_PLL_START_MASK (1 << SVFPGA_PLL_START_SHIFT) + +#define SVFPGA_PLL_DIV_FRAC0 (SVFPGA_PAGE_15 + 0x02) +#define SVFPGA_PLL_DIV_FRAC_SHIFT 0 +#define SVFPGA_PLL_DIV_FRAC_MASK (0xff << SVFPGA_PLL_DIV_FRAC_SHIFT) +#define SVFPGA_PLL_DIV_FRAC1 (SVFPGA_PAGE_15 + 0x03) +#define SVFPGA_PLL_DIV_FRAC2 (SVFPGA_PAGE_15 + 0x04) + +#define SVFPGA_PLL_DIV_INT (SVFPGA_PAGE_15 + 0x05) +#define SVFPGA_PLL_DIV_INT_SHIFT 0 +#define SVFPGA_PLL_DIV_INT_MASK (0xff << SVFPGA_PLL_DIV_INT_SHIFT) + +#define SVFPGA_PLL_CTL3 (SVFPGA_PAGE_15 + 0x08) +#define SVFPGA_PLL_DIVOUT_SHIFT 0 +#define SVFPGA_PLL_DIVOUT_MASK (0xff << SVFPGA_PLL_DIVOUT_SHIFT) + +#define SVFPGA_PLL_CAL_RATIO (SVFPGA_PAGE_15 + 0x0A) +#define SVFPGA_PLL_CAL_RATIO_SHIFT 0 +#define SVFPGA_PLL_CAL_RATIO_MASK (0xff << SVFPGA_PLL_CAL_RATIO_SHIFT) + +#define SVFPGA_PLL_CTL4 (SVFPGA_PAGE_15 + 0x1B) +#define SVFPGA_PLL_MODE_SHIFT 0 +#define SVFPGA_PLL_MODE_MASK (3 << SVFPGA_PLL_MODE_SHIFT) + +/* Page 0x19 HP Load Detect Registers */ +#define SVFPGA_LOAD_DET_RCSTAT (SVFPGA_PAGE_19 + 0x25) +#define SVFPGA_RLA_STAT_SHIFT 0 +#define SVFPGA_RLA_STAT_MASK (3 << SVFPGA_RLA_STAT_SHIFT) +#define SVFPGA_RLA_STAT_15_OHM 0 + +#define SVFPGA_LOAD_DET_DONE (SVFPGA_PAGE_19 + 0x26) +#define SVFPGA_LOAD_DET_EN (SVFPGA_PAGE_19 + 0x27) + +/* Page 0x1B Headset Interface Registers */ +#define SVFPGA_HSBIAS_SC_AUTOCTL (SVFPGA_PAGE_1B + 0x70) +#define SVFPGA_WAKE_CTL (SVFPGA_PAGE_1B + 0x71) +#define SVFPGA_ADC_DISABLE_MUTE (SVFPGA_PAGE_1B + 0x72) +#define SVFPGA_TIPSENSE_CTL (SVFPGA_PAGE_1B + 0x73) +#define SVFPGA_MISC_DET_CTL (SVFPGA_PAGE_1B + 0x74) +#define SVFPGA_MIC_DET_CTL1 (SVFPGA_PAGE_1B + 0x75) +#define SVFPGA_MIC_DET_CTL2 (SVFPGA_PAGE_1B + 0x76) +#define SVFPGA_DET_STATUS1 (SVFPGA_PAGE_1B + 0x77) +#define SVFPGA_DET_STATUS2 (SVFPGA_PAGE_1B + 0x78) +#define SVFPGA_DET_MASK1 (SVFPGA_PAGE_1B + 0x79) +#define SVFPGA_DET_MASK2 (SVFPGA_PAGE_1B + 0x7A) + +/* Page 0x1C Headset Bias Registers */ +#define SVFPGA_HS_BIAS_CTL (SVFPGA_PAGE_1C + 0x03) + +/* Page 0x1D ADC Registers */ +#define SVFPGA_ADC_CTL (SVFPGA_PAGE_1D + 0x01) +#define SVFPGA_ADC_NOTCH_DIS_SHIFT 5 +#define SVFPGA_ADC_FORCE_WEAK_VCM_SHIFT 4 +#define SVFPGA_ADC_INV_SHIFT 2 +#define SVFPGA_ADC_DIG_BOOST_SHIFT 0 + +#define SVFPGA_ADC_VOLUME (SVFPGA_PAGE_1D + 0x03) +#define SVFPGA_ADC_VOL_SHIFT 0 + +#define SVFPGA_ADC_WNF_HPF_CTL (SVFPGA_PAGE_1D + 0x04) +#define SVFPGA_ADC_WNF_CF_SHIFT 4 +#define SVFPGA_ADC_WNF_EN_SHIFT 3 +#define SVFPGA_ADC_HPF_CF_SHIFT 1 +#define SVFPGA_ADC_HPF_EN_SHIFT 0 + +/* Page 0x1F DAC Registers */ +#define SVFPGA_DAC_CTL1 (SVFPGA_PAGE_1F + 0x01) +#define SVFPGA_DACB_INV_SHIFT 1 +#define SVFPGA_DACA_INV_SHIFT 0 + +#define SVFPGA_DAC_CTL2 (SVFPGA_PAGE_1F + 0x06) +#define SVFPGA_HPOUT_LOAD_SHIFT 3 +#define SVFPGA_HPOUT_LOAD_MASK (1 << SVFPGA_HPOUT_LOAD_SHIFT) +#define SVFPGA_HPOUT_CLAMP_SHIFT 2 +#define SVFPGA_HPOUT_CLAMP_MASK (1 << SVFPGA_HPOUT_CLAMP_SHIFT) +#define SVFPGA_DAC_HPF_EN_SHIFT 1 +#define SVFPGA_DAC_HPF_EN_MASK (1 << SVFPGA_DAC_HPF_EN_SHIFT) + +/* Page 0x20 HP CTL Registers */ +#define SVFPGA_HP_CTL (SVFPGA_PAGE_20 + 0x01) +#define SVFPGA_HP_ANA_BMUTE_SHIFT 3 +#define SVFPGA_HP_ANA_BMUTE_MASK (1 << SVFPGA_HP_ANA_BMUTE_SHIFT) +#define SVFPGA_HP_ANA_AMUTE_SHIFT 2 +#define SVFPGA_HP_ANA_AMUTE_MASK (1 << SVFPGA_HP_ANA_AMUTE_SHIFT) +#define SVFPGA_HP_FULL_SCALE_VOL_SHIFT 1 +#define SVFPGA_HP_FULL_SCALE_VOL_MASK (1 << SVFPGA_HP_FULL_SCALE_VOL_SHIFT) + +/* Page 0x21 Class H Registers */ +#define SVFPGA_CLASSH_CTL (SVFPGA_PAGE_21 + 0x01) + +/* Page 0x23 Mixer Volume Registers */ +#define SVFPGA_MIXER_CHA_VOL (SVFPGA_PAGE_23 + 0x01) +#define SVFPGA_MIXER_ADC_VOL (SVFPGA_PAGE_23 + 0x02) +#define SVFPGA_MIXER_CHB_VOL (SVFPGA_PAGE_23 + 0x03) +#define SVFPGA_MIXER_CH_VOL_SHIFT 0 +#define SVFPGA_MIXER_CH_VOL_MASK (0x3f << SVFPGA_MIXER_CH_VOL_SHIFT) + +/* Page 0x24 EQ Registers */ +#define SVFPGA_EQ_COEF_IN0 (SVFPGA_PAGE_24 + 0x01) +#define SVFPGA_EQ_COEF_IN1 (SVFPGA_PAGE_24 + 0x02) +#define SVFPGA_EQ_COEF_IN2 (SVFPGA_PAGE_24 + 0x03) +#define SVFPGA_EQ_COEF_IN3 (SVFPGA_PAGE_24 + 0x04) +#define SVFPGA_EQ_COEF_RW (SVFPGA_PAGE_24 + 0x06) +#define SVFPGA_EQ_COEF_OUT0 (SVFPGA_PAGE_24 + 0x07) +#define SVFPGA_EQ_COEF_OUT1 (SVFPGA_PAGE_24 + 0x08) +#define SVFPGA_EQ_COEF_OUT2 (SVFPGA_PAGE_24 + 0x09) +#define SVFPGA_EQ_COEF_OUT3 (SVFPGA_PAGE_24 + 0x0A) +#define SVFPGA_EQ_INIT_STAT (SVFPGA_PAGE_24 + 0x0B) +#define SVFPGA_EQ_START_FILT (SVFPGA_PAGE_24 + 0x0C) +#define SVFPGA_EQ_MUTE_CTL (SVFPGA_PAGE_24 + 0x0E) + +/* Page 0x25 Audio Port Registers */ +#define SVFPGA_SP_RX_CH_SEL (SVFPGA_PAGE_25 + 0x01) +#define SVFPGA_SP_RX_ISOC_CTL (SVFPGA_PAGE_25 + 0x02) +#define SVFPGA_SP_RX_FS (SVFPGA_PAGE_25 + 0x03) +#define CS42l42_SPDIF_CH_SEL (SVFPGA_PAGE_25 + 0x04) +#define SVFPGA_SP_TX_ISOC_CTL (SVFPGA_PAGE_25 + 0x05) +#define SVFPGA_SP_TX_FS (SVFPGA_PAGE_25 + 0x06) +#define SVFPGA_SPDIF_SW_CTL1 (SVFPGA_PAGE_25 + 0x07) + +/* Page 0x26 SRC Registers */ +#define SVFPGA_SRC_SDIN_FS (SVFPGA_PAGE_26 + 0x01) +#define SVFPGA_SRC_SDOUT_FS (SVFPGA_PAGE_26 + 0x09) + +/* Page 0x28 S/PDIF Registers */ +#define SVFPGA_SPDIF_CTL1 (SVFPGA_PAGE_28 + 0x01) +#define SVFPGA_SPDIF_CTL2 (SVFPGA_PAGE_28 + 0x02) +#define SVFPGA_SPDIF_CTL3 (SVFPGA_PAGE_28 + 0x03) +#define SVFPGA_SPDIF_CTL4 (SVFPGA_PAGE_28 + 0x04) + +/* Page 0x29 Serial Port TX Registers */ +#define SVFPGA_ASP_TX_SZ_EN (SVFPGA_PAGE_29 + 0x01) +#define SVFPGA_ASP_TX_CH_EN (SVFPGA_PAGE_29 + 0x02) +#define SVFPGA_ASP_TX_CH_AP_RES (SVFPGA_PAGE_29 + 0x03) +#define SVFPGA_ASP_TX_CH1_BIT_MSB (SVFPGA_PAGE_29 + 0x04) +#define SVFPGA_ASP_TX_CH1_BIT_LSB (SVFPGA_PAGE_29 + 0x05) +#define SVFPGA_ASP_TX_HIZ_DLY_CFG (SVFPGA_PAGE_29 + 0x06) +#define SVFPGA_ASP_TX_CH2_BIT_MSB (SVFPGA_PAGE_29 + 0x0A) +#define SVFPGA_ASP_TX_CH2_BIT_LSB (SVFPGA_PAGE_29 + 0x0B) + +/* Page 0x2A Serial Port RX Registers */ +#define SVFPGA_ASP_RX_DAI0_EN (SVFPGA_PAGE_2A + 0x01) +#define SVFPGA_ASP_RX0_CH_EN_SHIFT 2 +#define SVFPGA_ASP_RX0_CH_EN_MASK (0xf << SVFPGA_ASP_RX0_CH_EN_SHIFT) +#define SVFPGA_ASP_RX0_CH1_EN 1 +#define SVFPGA_ASP_RX0_CH2_EN 2 +#define SVFPGA_ASP_RX0_CH3_EN 4 +#define SVFPGA_ASP_RX0_CH4_EN 8 + +#define SVFPGA_ASP_RX_DAI0_CH1_AP_RES (SVFPGA_PAGE_2A + 0x02) +#define SVFPGA_ASP_RX_DAI0_CH1_BIT_MSB (SVFPGA_PAGE_2A + 0x03) +#define SVFPGA_ASP_RX_DAI0_CH1_BIT_LSB (SVFPGA_PAGE_2A + 0x04) +#define SVFPGA_ASP_RX_DAI0_CH2_AP_RES (SVFPGA_PAGE_2A + 0x05) +#define SVFPGA_ASP_RX_DAI0_CH2_BIT_MSB (SVFPGA_PAGE_2A + 0x06) +#define SVFPGA_ASP_RX_DAI0_CH2_BIT_LSB (SVFPGA_PAGE_2A + 0x07) +#define SVFPGA_ASP_RX_DAI0_CH3_AP_RES (SVFPGA_PAGE_2A + 0x08) +#define SVFPGA_ASP_RX_DAI0_CH3_BIT_MSB (SVFPGA_PAGE_2A + 0x09) +#define SVFPGA_ASP_RX_DAI0_CH3_BIT_LSB (SVFPGA_PAGE_2A + 0x0A) +#define SVFPGA_ASP_RX_DAI0_CH4_AP_RES (SVFPGA_PAGE_2A + 0x0B) +#define SVFPGA_ASP_RX_DAI0_CH4_BIT_MSB (SVFPGA_PAGE_2A + 0x0C) +#define SVFPGA_ASP_RX_DAI0_CH4_BIT_LSB (SVFPGA_PAGE_2A + 0x0D) +#define SVFPGA_ASP_RX_DAI1_CH1_AP_RES (SVFPGA_PAGE_2A + 0x0E) +#define SVFPGA_ASP_RX_DAI1_CH1_BIT_MSB (SVFPGA_PAGE_2A + 0x0F) +#define SVFPGA_ASP_RX_DAI1_CH1_BIT_LSB (SVFPGA_PAGE_2A + 0x10) +#define SVFPGA_ASP_RX_DAI1_CH2_AP_RES (SVFPGA_PAGE_2A + 0x11) +#define SVFPGA_ASP_RX_DAI1_CH2_BIT_MSB (SVFPGA_PAGE_2A + 0x12) +#define SVFPGA_ASP_RX_DAI1_CH2_BIT_LSB (SVFPGA_PAGE_2A + 0x13) + +#define SVFPGA_ASP_RX_CH_AP_SHIFT 6 +#define SVFPGA_ASP_RX_CH_AP_MASK (1 << SVFPGA_ASP_RX_CH_AP_SHIFT) +#define SVFPGA_ASP_RX_CH_AP_LOW 0 +#define SVFPGA_ASP_RX_CH_AP_HI 1 +#define SVFPGA_ASP_RX_CH_RES_SHIFT 0 +#define SVFPGA_ASP_RX_CH_RES_MASK (3 << SVFPGA_ASP_RX_CH_RES_SHIFT) +#define SVFPGA_ASP_RX_CH_RES_32 3 +#define SVFPGA_ASP_RX_CH_BIT_ST_SHIFT 0 +#define SVFPGA_ASP_RX_CH_BIT_ST_MASK (0xff << SVFPGA_ASP_RX_CH_BIT_ST_SHIFT) + +/* Page 0x30 ID Registers */ +#define SVFPGA_SUB_REVID (SVFPGA_PAGE_30 + 0x14) +#define SVFPGA_MAX_REGISTER (SVFPGA_PAGE_30 + 0x14) + +#define SVFPGA_SUPPORTED_RATE 48000 + +/* Defines for fracturing values spread across multiple registers */ +#define SVFPGA_FRAC0_VAL(val) ((val) & 0x0000ff) +#define SVFPGA_FRAC1_VAL(val) (((val) & 0x00ff00) >> 8) +#define SVFPGA_FRAC2_VAL(val) (((val) & 0xff0000) >> 16) + +#define SVFPGA_NUM_SUPPLIES 5 +static const char *const svfpga_supply_names[SVFPGA_NUM_SUPPLIES] = { + "VA", + "VP", + "VCP", + "VD_FILT", + "VL", +}; + +struct svfpga_private { + struct regmap *regmap; + struct snd_soc_codec *codec; + struct regulator_bulk_data supplies[SVFPGA_NUM_SUPPLIES]; + struct gpio_desc *reset_gpio; + u32 sclk; + u8 hpout_load; + u8 hpout_clamp; + struct sdw_slv *sdw; +}; + +int svfpga_probe(struct device *dev, struct regmap *regmap, + struct sdw_slv *slave); +int svfpga_remove(struct device *dev); +#endif /* __SVFPGA_H__ */ From 54a15ae2a8c4ec2e3c5c3b7cc310daf4bdd0d7c0 Mon Sep 17 00:00:00 2001 From: Hardik T Shah Date: Sat, 23 Apr 2016 18:58:44 +0530 Subject: [PATCH 0624/1103] ASoC:Intel: Add machine driver for SoundWire SV PDM Codec. This patch is for testing. Not to be upstreamed. Change-Id: Ia92c5543d94fb3ac045f8c13414b9af3ffa8d7d3 Signed-off-by: Hardik T Shah Reviewed-on: --- sound/soc/intel/boards/Kconfig | 11 ++ sound/soc/intel/boards/Makefile | 2 + sound/soc/intel/boards/cnl_svfpga.c | 273 ++++++++++++++++++++++++++++ 3 files changed, 286 insertions(+) create mode 100644 sound/soc/intel/boards/cnl_svfpga.c diff --git a/sound/soc/intel/boards/Kconfig b/sound/soc/intel/boards/Kconfig index 35787e69f31b..ca140a77d992 100644 --- a/sound/soc/intel/boards/Kconfig +++ b/sound/soc/intel/boards/Kconfig @@ -306,6 +306,17 @@ config SND_SOC_INTEL_CNL_CS42L42_MACH Say Y or m if you have such a device. This is a recommended option. If unsure select "N". +config SND_SOC_INTEL_CNL_SVFPGA_MACH + tristate "Cannonlake with SVFPGA PDM SDW mode" + depends on MFD_INTEL_LPSS && I2C && ACPI + select SND_SOC_SVFPGA + select SND_SOC_DMIC + help + This adds support for ASoC SVFPGA PDM codec SDW machine driver. This + will create an alsa sound card for SVFPGA. + Say Y or m if you have such a device. This is a recommended option. + If unsure select "N". + endif ## SND_SOC_INTEL_SKYLAKE endif ## SND_SOC_INTEL_MACH diff --git a/sound/soc/intel/boards/Makefile b/sound/soc/intel/boards/Makefile index 4298c5ad8ca0..4f2b9b38edd0 100644 --- a/sound/soc/intel/boards/Makefile +++ b/sound/soc/intel/boards/Makefile @@ -23,6 +23,7 @@ snd-soc-skl_rt286-objs := skl_rt286.o snd-skl_nau88l25_max98357a-objs := skl_nau88l25_max98357a.o snd-soc-skl_nau88l25_ssm4567-objs := skl_nau88l25_ssm4567.o snd-soc-cnl_cs42l42-objs := cnl_cs42l42.o +snd-soc-cnl_svfpga-objs := cnl_svfpga.o obj-$(CONFIG_SND_SOC_INTEL_HASWELL_MACH) += snd-soc-sst-haswell.o obj-$(CONFIG_SND_SOC_INTEL_BYT_RT5640_MACH) += snd-soc-sst-byt-rt5640-mach.o @@ -48,3 +49,4 @@ obj-$(CONFIG_SND_SOC_INTEL_SKL_RT286_MACH) += snd-soc-skl_rt286.o obj-$(CONFIG_SND_SOC_INTEL_SKL_NAU88L25_MAX98357A_MACH) += snd-skl_nau88l25_max98357a.o obj-$(CONFIG_SND_SOC_INTEL_SKL_NAU88L25_SSM4567_MACH) += snd-soc-skl_nau88l25_ssm4567.o obj-$(CONFIG_SND_SOC_INTEL_CNL_CS42L42_MACH) += snd-soc-cnl_cs42l42.o +obj-$(CONFIG_SND_SOC_INTEL_CNL_SVFPGA_MACH) += snd-soc-cnl_svfpga.o diff --git a/sound/soc/intel/boards/cnl_svfpga.c b/sound/soc/intel/boards/cnl_svfpga.c new file mode 100644 index 000000000000..312c0437ac15 --- /dev/null +++ b/sound/soc/intel/boards/cnl_svfpga.c @@ -0,0 +1,273 @@ +/* + * cnl_svfpga.c - ASOC Machine driver for Intel cnl_svfpga platform + * with SVFPGA PDM soundwire codec. + * + * Copyright (C) 2016 Intel Corp + * Author: Hardik Shah + * + * Based on + * moor_dpcm_florida.c - ASOC Machine driver for Intel Moorefield platform + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * 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; version 2 of the License. + * + * 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. + * + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct cnl_svfpga_mc_private { + u8 pmic_id; + void __iomem *osc_clk0_reg; + int bt_mode; +}; + +static const struct snd_soc_dapm_widget cnl_svfpga_widgets[] = { + SND_SOC_DAPM_HP("Headphones", NULL), + SND_SOC_DAPM_MIC("AMIC", NULL), + SND_SOC_DAPM_MIC("SoC DMIC", NULL), +}; + +static const struct snd_soc_dapm_route cnl_svfpga_map[] = { + /*Headphones*/ + { "Headphones", NULL, "HP" }, + { "I2NP", NULL, "AMIC" }, + + /* SWM map link the SWM outs to codec AIF */ + { "Playback", NULL, "SDW Tx"}, + { "SDW Tx", NULL, "sdw_codec0_out"}, + + + { "sdw_codec1_in", NULL, "SDW Rx1" }, + { "SDW Rx1", NULL, "Capture" }, + + {"DMic", NULL, "SoC DMIC"}, + {"DMIC01 Rx", NULL, "Capture"}, + {"dmic01_hifi", NULL, "DMIC01 Rx"}, + +}; + +static const struct snd_kcontrol_new cnl_svfpga_controls[] = { + SOC_DAPM_PIN_SWITCH("Headphones"), +}; + +static int cnl_svfpga_init(struct snd_soc_pcm_runtime *runtime) +{ + int ret; + struct snd_soc_card *card = runtime->card; + + pr_info("Entry %s\n", __func__); + card->dapm.idle_bias_off = true; + + ret = snd_soc_add_card_controls(card, cnl_svfpga_controls, + ARRAY_SIZE(cnl_svfpga_controls)); + if (ret) { + pr_err("unable to add card controls\n"); + return ret; + } + return 0; +} + +static unsigned int rates_48000[] = { + 48000, + 16000, + 8000, +}; + +static struct snd_pcm_hw_constraint_list constraints_48000 = { + .count = ARRAY_SIZE(rates_48000), + .list = rates_48000, +}; + +static int cnl_svfpga_startup(struct snd_pcm_substream *substream) +{ + return snd_pcm_hw_constraint_list(substream->runtime, 0, + SNDRV_PCM_HW_PARAM_RATE, + &constraints_48000); +} + +static struct snd_soc_ops cnl_svfpga_ops = { + .startup = cnl_svfpga_startup, +}; + +static int cnl_svfpga_codec_fixup(struct snd_soc_pcm_runtime *rtd, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_dai *be_cpu_dai; + int slot_width = 24; + struct snd_interval *rate = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_RATE); + struct snd_interval *channels = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_CHANNELS); + + pr_debug("Invoked %s for dailink %s\n", __func__, rtd->dai_link->name); + slot_width = 24; + rate->min = rate->max = 48000; + channels->min = channels->max = 1; + snd_mask_none(hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT)); + snd_mask_set(hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT), + SNDRV_PCM_FORMAT_S16_LE); + + pr_info("param width set to:0x%x\n", + snd_pcm_format_width(params_format(params))); + pr_info("Slot width = %d\n", slot_width); + + be_cpu_dai = rtd->cpu_dai; + return 0; +} + +static int cnl_dmic_fixup(struct snd_soc_pcm_runtime *rtd, + struct snd_pcm_hw_params *params) +{ + struct snd_interval *channels = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_CHANNELS); + channels->min = channels->max = 2; + + return 0; +} + +struct snd_soc_dai_link cnl_svfpga_msic_dailink[] = { + { + .name = "Bxtn Audio Port", + .stream_name = "Audio", + .cpu_dai_name = "System Pin", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .platform_name = "0000:02:18.0", + .init = cnl_svfpga_init, + .ignore_suspend = 1, + .nonatomic = 1, + .dynamic = 1, + .dpcm_playback = 1, + .dpcm_capture = 1, + .ops = &cnl_svfpga_ops, + }, + { + .name = "SDW0-Codec", + .id = 3, + .cpu_dai_name = "SDW PDM Pin", + .platform_name = "0000:02:18.0", + .codec_name = "sdw-slave0-14:13:20:05:86:80", + .codec_dai_name = "svfpga", + .be_hw_params_fixup = cnl_svfpga_codec_fixup, + .ignore_suspend = 1, + .no_pcm = 1, + .dpcm_playback = 1, + .dpcm_capture = 1, + }, + { + .name = "dmic01", + .id = 4, + .cpu_dai_name = "DMIC01 Pin", + .codec_name = "dmic-codec", + .codec_dai_name = "dmic-hifi", + .platform_name = "0000:02:18.0", + .ignore_suspend = 1, + .no_pcm = 1, + .dpcm_capture = 1, + .be_hw_params_fixup = cnl_dmic_fixup, + }, + +}; + +/* SoC card */ +static struct snd_soc_card snd_soc_card_cnl_svfpga = { + .name = "cnl_svfpga-audio", + .dai_link = cnl_svfpga_msic_dailink, + .num_links = ARRAY_SIZE(cnl_svfpga_msic_dailink), + .dapm_widgets = cnl_svfpga_widgets, + .num_dapm_widgets = ARRAY_SIZE(cnl_svfpga_widgets), + .dapm_routes = cnl_svfpga_map, + .num_dapm_routes = ARRAY_SIZE(cnl_svfpga_map), +}; + + +static int snd_cnl_svfpga_mc_probe(struct platform_device *pdev) +{ + int ret_val = 0; + struct cnl_svfpga_mc_private *drv; + + pr_debug("Entry %s\n", __func__); + + drv = devm_kzalloc(&pdev->dev, sizeof(*drv), GFP_KERNEL); + if (!drv) + return -ENOMEM; + + snd_soc_card_cnl_svfpga.dev = &pdev->dev; + snd_soc_card_set_drvdata(&snd_soc_card_cnl_svfpga, drv); + /* Register the card */ + ret_val = snd_soc_register_card(&snd_soc_card_cnl_svfpga); + if (ret_val && (ret_val != -EPROBE_DEFER)) { + pr_err("snd_soc_register_card failed %d\n", ret_val); + goto unalloc; + } + platform_set_drvdata(pdev, &snd_soc_card_cnl_svfpga); + pr_info("%s successful\n", __func__); + return ret_val; + +unalloc: + return ret_val; +} + +static int snd_cnl_svfpga_mc_remove(struct platform_device *pdev) +{ + struct snd_soc_card *soc_card = platform_get_drvdata(pdev); + struct cnl_svfpga_mc_private *drv = snd_soc_card_get_drvdata(soc_card); + + pr_debug("In %s\n", __func__); + + devm_kfree(&pdev->dev, drv); + snd_soc_card_set_drvdata(soc_card, NULL); + snd_soc_unregister_card(soc_card); + platform_set_drvdata(pdev, NULL); + return 0; +} + +static struct platform_driver snd_cnl_svfpga_mc_driver = { + .driver = { + .name = "cnl_svfpga", + }, + .probe = snd_cnl_svfpga_mc_probe, + .remove = snd_cnl_svfpga_mc_remove, +}; + +static int snd_cnl_svfpga_driver_init(void) +{ + pr_info("Canonlake Machine Driver cnl_svfpga: svfpga registered\n"); + return platform_driver_register(&snd_cnl_svfpga_mc_driver); +} +module_init(snd_cnl_svfpga_driver_init); + +static void snd_cnl_svfpga_driver_exit(void) +{ + pr_debug("In %s\n", __func__); + platform_driver_unregister(&snd_cnl_svfpga_mc_driver); +} +module_exit(snd_cnl_svfpga_driver_exit) + +MODULE_DESCRIPTION("ASoC CNL Machine driver"); +MODULE_AUTHOR("Hardik Shah "); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:cnl_svfpga"); From 50c8efcbd1899799f8aea997cdacefee44ee8776 Mon Sep 17 00:00:00 2001 From: Sanyog Kale Date: Sun, 20 Nov 2016 20:16:25 +0530 Subject: [PATCH 0625/1103] [REVERTME] SoundWire: Hardcoding in bus driver for SVFPGA PDM codec. SVFPGA PDM codec implements the codec in orthogonal way to MIPI to verify the PDM on CNL Master controller. Added hardcodings to support SVFPGA. This is just for testing. This patch wont be upstreamed. Change-Id: I42b0fd5a16e59577a89bbab7bc024aed1b04c222 Signed-off-by: Hardik T Shah Reviewed-on: Signed-off-by: Sanyog Kale --- drivers/sdw/sdw_bwcalc.c | 83 ++++++++++++++++++++++++++++++++-------- 1 file changed, 68 insertions(+), 15 deletions(-) diff --git a/drivers/sdw/sdw_bwcalc.c b/drivers/sdw/sdw_bwcalc.c index 9c1ebc3297d2..b76ace3b0de7 100644 --- a/drivers/sdw/sdw_bwcalc.c +++ b/drivers/sdw/sdw_bwcalc.c @@ -27,8 +27,19 @@ #define MAXCLOCKFREQ 6 -#define MAXCLOCKFREQ 6 +#ifdef CONFIG_SND_SOC_SVFPGA +/* For PDM Capture, frameshape used is 50x10 */ +int rows[MAX_NUM_ROWS] = {50, 100, 48, 60, 64, 72, 75, 80, 90, + 96, 125, 144, 147, 120, 128, 150, + 160, 180, 192, 200, 240, 250, 256}; + +int cols[MAX_NUM_COLS] = {10, 2, 4, 6, 8, 12, 14, 16}; +int clock_freq[MAXCLOCKFREQ] = {19200000, 19200000, + 19200000, 19200000, + 19200000, 19200000}; + +#else /* TBD: Currently we are using 100x2 as frame shape. to be removed later */ int rows[MAX_NUM_ROWS] = {100, 48, 50, 60, 64, 72, 75, 80, 90, 96, 125, 144, 147, 120, 128, 150, @@ -44,7 +55,7 @@ int cols[MAX_NUM_COLS] = {2, 4, 6, 8, 10, 12, 14, 16}; int clock_freq[MAXCLOCKFREQ] = {9600000, 9600000, 9600000, 9600000, 9600000, 9600000}; - +#endif struct sdw_num_to_col sdw_num_col_mapping[MAX_NUM_COLS] = { {0, 2}, {1, 4}, {2, 6}, {3, 8}, {4, 10}, {5, 12}, {6, 14}, {7, 16}, @@ -104,20 +115,27 @@ int sdw_mstr_bw_init(struct sdw_bus *sdw_bs) sdw_bs->bandwidth = 0; sdw_bs->system_interval = 0; sdw_bs->frame_freq = 0; - /* TBD: Base Clock frequency should be read from - * master capabilities - * Currenly hardcoding to 9.6MHz - */ - sdw_bs->clk_freq = 9.6*1000*1000; sdw_bs->clk_state = SDW_CLK_STATE_ON; /* TBD: to be removed later */ /* Assumption is these should be already filled */ sdw_mstr_cap = &sdw_bs->mstr->mstr_capabilities; - sdw_mstr_cap->base_clk_freq = 9.6 * 1000 * 1000; sdw_mstr_cap->monitor_handover_supported = false; sdw_mstr_cap->highphy_capable = false; +#ifdef CONFIG_SND_SOC_SVFPGA + /* TBD: For PDM capture to be removed later */ + sdw_bs->clk_freq = 9.6 * 1000 * 1000 * 2; + sdw_mstr_cap->base_clk_freq = 9.6 * 1000 * 1000 * 2; +#else + /* TBD: Base Clock frequency should be read from + * master capabilities + * Currenly hardcoding to 9.6MHz + */ + sdw_bs->clk_freq = 9.6 * 1000 * 1000; + sdw_mstr_cap->base_clk_freq = 9.6 * 1000 * 1000; + +#endif return 0; } EXPORT_SYMBOL_GPL(sdw_mstr_bw_init); @@ -201,8 +219,24 @@ int sdw_cfg_slv_params(struct sdw_bus *mstr_bs, u8 wbuf[8] = {0, 0, 0, 0, 0, 0, 0, 0}; u8 wbuf1[2] = {0, 0}; u8 rbuf[1] = {0}; - u8 rbuf1[8] = {0, 0, 0, 0, 0, 0, 0, 0}; - u8 rbuf2[2] = {0, 0}; + + +#ifdef CONFIG_SND_SOC_SVFPGA + /* + * The below hardcoding is required + * for running PDM capture with SV conora card + * because the transport params of card is not + * same as master parameters. Also not all + * standard registers are valid. + */ + t_slv_params->blockgroupcontrol_valid = false; + t_slv_params->sample_interval = 50; + t_slv_params->offset1 = 0; + t_slv_params->offset2 = 0; + t_slv_params->hstart = 1; + t_slv_params->hstop = 6; + p_slv_params->word_length = 30; +#endif /* Program slave alternate bank with all transport parameters */ /* DPN_BlockCtrl2 */ @@ -259,7 +293,11 @@ int sdw_cfg_slv_params(struct sdw_bus *mstr_bs, wr_msg.ssp_tag = 0x0; wr_msg.flag = SDW_MSG_FLAG_WRITE; +#ifdef CONFIG_SND_SOC_SVFPGA + wr_msg.len = (5 + (1 * (t_slv_params->blockgroupcontrol_valid))); +#else wr_msg.len = (7 + (1 * (t_slv_params->blockgroupcontrol_valid))); +#endif wr_msg.slave_addr = slv_rt->slave->slv_number; wr_msg.buf = &wbuf[0 + (1 * (!t_slv_params->blockgroupcontrol_valid))]; wr_msg.addr_page1 = 0x0; @@ -981,10 +1019,16 @@ int sdw_compute_sys_interval(struct sdw_bus *sdw_mstr_bs, * One port per bus runtime structure */ /* Calculate sample interval */ +#ifdef CONFIG_SND_SOC_SVFPGA + t_params->sample_interval = + ((sdw_mstr_bs->clk_freq/ + sdw_mstr_bs_rt->stream_params.rate)); +#else t_params->sample_interval = ((sdw_mstr_bs->clk_freq/ sdw_mstr_bs_rt->stream_params.rate) * 2); +#endif /* Only BlockPerPort supported */ t_params->blockpackingmode = 0; t_params->lanecontrol = 0; @@ -1106,8 +1150,12 @@ int sdw_compute_hstart_hstop(struct sdw_bus *sdw_mstr_bs, int sel_col) */ t_params->hstop = hstop; +#ifdef CONFIG_SND_SOC_SVFPGA + /* For PDM capture, 0th col is also used */ + t_params->hstart = 0; +#else t_params->hstart = hstop - hwidth + 1; - +#endif /* * TBD: perform this when you have 2 ports @@ -1234,12 +1282,17 @@ int sdw_compute_blk_subblk_offset(struct sdw_bus *sdw_mstr_bs) hstart1 = hstart2 = t_params->hstart; hstop1 = hstop2 = t_params->hstop; /* TBD: Verify this condition */ +#ifdef CONFIG_SND_SOC_SVFPGA + block_offset = 1; +#else block_offset = 0; +#endif } else { hstart1 = t_params->hstart; hstop1 = t_params->hstop; +#ifndef CONFIG_SND_SOC_SVFPGA /* hstart/stop not same */ if ((hstart1 != hstart2) && (hstop1 != hstop2)) { @@ -1249,8 +1302,7 @@ int sdw_compute_blk_subblk_offset(struct sdw_bus *sdw_mstr_bs) /* TBD: Harcoding to 0, to be removed*/ block_offset = 0; } - -#if 0 +#else if ((hstart1 != hstart2) && (hstop1 != hstop2)) { block_offset = 1; @@ -1481,14 +1533,15 @@ int sdw_cfg_bs_params(struct sdw_bus *sdw_mstr_bs, banktouse = !banktouse; /* - * TBD: Currently harcoded SSP interval to 24, + * TBD: Currently harcoded SSP interval to 50, * computed value to be taken from system_interval in * bus data structure. * Add error check. */ if (ops->mstr_ops->set_ssp_interval) ops->mstr_ops->set_ssp_interval(sdw_mstr_bs->mstr, - 24, banktouse); /* hardcoding to 24 */ + 50, banktouse); + /* * Configure Clock * TBD: Add error check From e34dccfca6c9bb153844cc6e6d0c60a90baff9a5 Mon Sep 17 00:00:00 2001 From: Hardik T Shah Date: Mon, 25 Apr 2016 13:39:37 +0530 Subject: [PATCH 0626/1103] SDW:Intel: Fix hardcoding for SVFPGA codec. SVFPGA codec requires special handling as its not modelled as MIPI defined SoundWire Slave. This is used for testing PDM mode on master. So hardcode clock setting for only SVFPGA codec. This patch doesnt need to be upstream. Change-Id: I723b1258d2186783a16ef7a60934a6ce7d6ffacc Signed-off-by: Hardik T Shah Reviewed-on: --- drivers/sdw/sdw_cnl.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/sdw/sdw_cnl.c b/drivers/sdw/sdw_cnl.c index 3f3317a6707a..9686aa2f9caf 100644 --- a/drivers/sdw/sdw_cnl.c +++ b/drivers/sdw/sdw_cnl.c @@ -1097,7 +1097,11 @@ static int cnl_sdw_set_clock_freq(struct sdw_master *mstr, /* TODO: Retrieve divider value or get value directly from calling * function */ +#ifdef CONFIG_SND_SOC_SVFPGA + int divider = ((9600000 * 2/cur_clk_freq) - 1); +#else int divider = ((9600000/cur_clk_freq) - 1); +#endif if (bank) { mcp_clockctrl_offset = SDW_CNL_MCP_CLOCKCTRL1; From 0d9f657b0c2fd8fa5b8ff6b4c23fe2550b555343 Mon Sep 17 00:00:00 2001 From: Hardik Shah Date: Fri, 29 Apr 2016 12:14:37 +0530 Subject: [PATCH 0627/1103] REVERTME:SDW: Increment the dev_id for every slave register to bus. Ideally every slave getting registered to bus should have separate dev_id if they are of same type but instances are different.But for maxim all the slaves have same_id, so increment the dev_id by one to register all slaves with different names. Change-Id: Iec8d21ae73bb1631803ba6faceef140d9cd41417 Signed-off-by: Hardik Shah Reviewed-on: --- drivers/sdw/sdw.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/sdw/sdw.c b/drivers/sdw/sdw.c index 6a1ff5e59401..71f1532657da 100644 --- a/drivers/sdw/sdw.c +++ b/drivers/sdw/sdw.c @@ -524,6 +524,8 @@ static void sdw_free_slv_number(struct sdw_master *mstr, sdw_unlock_mstr(mstr); } + +int count; static int sdw_register_slave(struct sdw_master *mstr) { int ret = 0, i, ports; @@ -635,6 +637,7 @@ static int sdw_register_slave(struct sdw_master *mstr) sdw_unlock_mstr(mstr); } + count++; return 0; program_slv_failed: device_unregister(&sdw_slv->dev); From 4955ca21deb4323c92073957cfa49c65a5fb9c85 Mon Sep 17 00:00:00 2001 From: Hardik Shah Date: Fri, 29 Apr 2016 14:22:16 +0530 Subject: [PATCH 0628/1103] SDW: Support async messages for bus driver. All of the messages to the master controller for read/write register are synchronous for calling function. Master controller completes the operation and return back to calling function with result. But when audio stream is split between two masters (aggregation) bus driver needs to send the message asynchronously to master controller part of aggregation. Bus driver waits for result outide master controller context, instead of master controller waiting. Change-Id: Icf5d44c372bca742da21408f074dbf90080a3ea9 Signed-off-by: Hardik Shah Reviewed-on: --- drivers/sdw/sdw.c | 51 +++++++++++++++++++++++++++++++++++------ drivers/sdw/sdw_priv.h | 7 ++++++ include/linux/sdw_bus.h | 26 +++++++++++++++++++++ 3 files changed, 77 insertions(+), 7 deletions(-) diff --git a/drivers/sdw/sdw.c b/drivers/sdw/sdw.c index 71f1532657da..5f4aea04a85b 100644 --- a/drivers/sdw/sdw.c +++ b/drivers/sdw/sdw.c @@ -419,13 +419,12 @@ void sdw_lock_mstr(struct sdw_master *mstr) { rt_mutex_lock(&mstr->bus_lock); } -EXPORT_SYMBOL_GPL(sdw_lock_mstr); /** * sdw_trylock_mstr - Try to get exclusive access to an SDW bus segment * @mstr: Target SDW bus segment */ -static int sdw_trylock_mstr(struct sdw_master *mstr) +int sdw_trylock_mstr(struct sdw_master *mstr) { return rt_mutex_trylock(&mstr->bus_lock); } @@ -439,7 +438,6 @@ void sdw_unlock_mstr(struct sdw_master *mstr) { rt_mutex_unlock(&mstr->bus_lock); } -EXPORT_SYMBOL_GPL(sdw_unlock_mstr); static int sdw_assign_slv_number(struct sdw_master *mstr, @@ -663,7 +661,8 @@ static int sdw_register_slave(struct sdw_master *mstr) * Adapter lock must be held when calling this function. No debug logging * takes place. mstr->algo->master_xfer existence isn't checked. */ -int __sdw_transfer(struct sdw_master *mstr, struct sdw_msg *msg, int num) +int __sdw_transfer(struct sdw_master *mstr, struct sdw_msg *msg, int num, + struct sdw_async_xfer_data *async_data) { unsigned long orig_jiffies; int ret = 0, try, i; @@ -707,8 +706,24 @@ int __sdw_transfer(struct sdw_master *mstr, struct sdw_msg *msg, int num) program_scp_addr_page = slv_cap->paging_supported; } - ret = mstr->driver->mstr_ops->xfer_msg(mstr, + /* Call async or sync handler based on call */ + if (!async_data) + ret = mstr->driver->mstr_ops->xfer_msg(mstr, msg, program_scp_addr_page); + /* Async transfer is not mandatory to support + * It requires only if stream is split across the + * masters, where bus driver need to send the commands + * for bank switch individually and wait for them + * to complete out side of the master context + */ + else if (mstr->driver->mstr_ops->xfer_msg_async && + async_data) + ret = mstr->driver->mstr_ops->xfer_msg_async( + mstr, msg, + program_scp_addr_page, + async_data); + else + return -ENOTSUPP; if (ret != -EAGAIN) break; if (time_after(jiffies, @@ -740,13 +755,34 @@ static int sdw_slave_transfer_nopm(struct sdw_master *mstr, struct sdw_msg *msg, int ret; if (mstr->driver->mstr_ops->xfer_msg) { - ret = __sdw_transfer(mstr, msg, num); + ret = __sdw_transfer(mstr, msg, num, NULL); return ret; } dev_dbg(&mstr->dev, "SDW level transfers not supported\n"); return -EOPNOTSUPP; } +int sdw_slave_transfer_async(struct sdw_master *mstr, struct sdw_msg *msg, + int num, + struct sdw_async_xfer_data *async_data) +{ + int ret; + /* Currently we support only message asynchronously, This is mainly + * used to do bank switch for multiple controllers + */ + if (num != 1) + return -EINVAL; + if (!(mstr->driver->mstr_ops->xfer_msg)) { + dev_dbg(&mstr->dev, "SDW level transfers not supported\n"); + return -EOPNOTSUPP; + } + pm_runtime_get_sync(&mstr->dev); + ret = __sdw_transfer(mstr, msg, num, async_data); + pm_runtime_mark_last_busy(&mstr->dev); + pm_runtime_put_sync_autosuspend(&mstr->dev); + return ret; +} + /** * sdw_slave_transfer: Transfer message between slave and mstr on the bus. * @mstr: mstr master which will transfer the message @@ -789,7 +825,7 @@ int sdw_slave_transfer(struct sdw_master *mstr, struct sdw_msg *msg, int num) } else { sdw_lock_mstr(mstr); } - ret = __sdw_transfer(mstr, msg, num); + ret = __sdw_transfer(mstr, msg, num, NULL); sdw_unlock_mstr(mstr); out: pm_runtime_mark_last_busy(&mstr->dev); @@ -1125,6 +1161,7 @@ static int sdw_register_master(struct sdw_master *mstr) if (!sdw_bus) goto bus_alloc_failed; sdw_bus->mstr = mstr; + init_completion(&sdw_bus->async_data.xfer_complete); mutex_lock(&sdw_core.core_lock); list_add_tail(&sdw_bus->bus_node, &sdw_core.bus_list); diff --git a/drivers/sdw/sdw_priv.h b/drivers/sdw/sdw_priv.h index 5ec3edc30d27..42e948440481 100644 --- a/drivers/sdw/sdw_priv.h +++ b/drivers/sdw/sdw_priv.h @@ -196,6 +196,7 @@ struct sdw_bus { struct kthread_work kwork; struct list_head status_list; spinlock_t spinlock; + struct sdw_async_xfer_data async_data; }; /** Holds supported Row-Column combination related information */ @@ -239,5 +240,11 @@ int sdw_mstr_bw_init(struct sdw_bus *sdw_bs); int sdw_bus_calc_bw(struct sdw_stream_tag *stream_tag, bool enable); int sdw_bus_calc_bw_dis(struct sdw_stream_tag *stream_tag, bool unprepare); int sdw_chn_enable(void); +void sdw_unlock_mstr(struct sdw_master *mstr); +int sdw_trylock_mstr(struct sdw_master *mstr); +void sdw_lock_mstr(struct sdw_master *mstr); +int sdw_slave_transfer_async(struct sdw_master *mstr, struct sdw_msg *msg, + int num, + struct sdw_async_xfer_data *async_data); #endif /* _LINUX_SDW_PRIV_H */ diff --git a/include/linux/sdw_bus.h b/include/linux/sdw_bus.h index 01c846b0c695..d16579b35f8a 100644 --- a/include/linux/sdw_bus.h +++ b/include/linux/sdw_bus.h @@ -337,6 +337,29 @@ struct sdw_slv_bra_capabilities { unsigned int mode_block_alignment; }; +/** + * struct sdw_async_xfer_data: Data to be provided by bus driver to + * to master controller, in case bus driver + * driver doesnt want to call synchronous + * xfer message API. This is used by bus + * driver during aggregation, where it calls + * the bank switch of multiple master + * if the stream is split between two master. + * In this case bus driver will wait outside + * master controller context for bank switch + * to happen. + * @result: Result of the asynchronous transfer. + * @xfer_complete Bus driver will wait on this. Master controller + * needs to ack on this for transfer complete. + * @msg Message to be transferred. + */ + +struct sdw_async_xfer_data { + int result; + struct completion xfer_complete; + struct sdw_msg *msg; +}; + /** * struct sdw_slv_dp0_capabilities: Capabilities of the Data Port 0 of Slave. * @@ -831,6 +854,9 @@ struct sdw_master_port_ops { struct sdw_master_ops { enum sdw_command_response (*xfer_msg)(struct sdw_master *mstr, struct sdw_msg *msg, bool program_scp_addr_page); + enum sdw_command_response (*xfer_msg_async)(struct sdw_master *mstr, + struct sdw_msg *msg, bool program_scp_addr_page, + struct sdw_async_xfer_data *data); int (*xfer_bulk)(struct sdw_master *mstr, struct sdw_bra_block *block); int (*monitor_handover)(struct sdw_master *mstr, From 19464a9e45eed8980a9e07291f0358f832bbc855 Mon Sep 17 00:00:00 2001 From: Hardik Shah Date: Fri, 29 Apr 2016 14:59:48 +0530 Subject: [PATCH 0629/1103] SDW: Change log level to error from debug. For any errors, log level should be error. By mistake it was debug. Change-Id: I81d55a2efaae172329515f6e000bbed3063400ef Signed-off-by: Hardik Shah Reviewed-on: --- drivers/sdw/sdw.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/sdw/sdw.c b/drivers/sdw/sdw.c index 5f4aea04a85b..78c8cfd32d4c 100644 --- a/drivers/sdw/sdw.c +++ b/drivers/sdw/sdw.c @@ -1844,7 +1844,7 @@ int sdw_config_stream(struct sdw_master *mstr, } } if (!sdw_rt) { - dev_dbg(&mstr->dev, "Valid stream tag not found\n"); + dev_err(&mstr->dev, "Valid stream tag not found\n"); ret = -EINVAL; goto out; } From 496a8bc652ef455f2d5a20d077145f551e5e28a5 Mon Sep 17 00:00:00 2001 From: Hardik Shah Date: Fri, 29 Apr 2016 16:26:15 +0530 Subject: [PATCH 0630/1103] SDW:Intel: Enabled the Multimode for Intel SDW master controller. Initialize the SDW controller for multimode. Set the Sync period to default Change the default SSP for multimode. Change-Id: Ie45c00aeeaea5062abe76abb91b7504e4e8b01fb Signed-off-by: Hardik Shah Reviewed-on: --- drivers/sdw/sdw_cnl.c | 25 ++++++++++++++++++++++++- drivers/sdw/sdw_cnl_priv.h | 12 +++++++++--- 2 files changed, 33 insertions(+), 4 deletions(-) diff --git a/drivers/sdw/sdw_cnl.c b/drivers/sdw/sdw_cnl.c index 9686aa2f9caf..21a61b4f010a 100644 --- a/drivers/sdw/sdw_cnl.c +++ b/drivers/sdw/sdw_cnl.c @@ -440,7 +440,11 @@ static int sdw_init(struct cnl_sdw *sdw) { struct sdw_master *mstr = sdw->mstr; struct cnl_sdw_data *data = &sdw->data; - int mcp_config, mcp_control; + int mcp_config, mcp_control, sync_reg; + + volatile int sync_update = 0; + /* Try 10 times before timing out */ + int timeout = 10; int ret = 0; /* Power up the link controller */ @@ -454,6 +458,25 @@ static int sdw_init(struct cnl_sdw *sdw) /* Switch the ownership to Master IP from glue logic */ sdw_switch_to_mip(sdw); + /* Set the Sync period to default */ + sync_reg = cnl_sdw_reg_readl(data->sdw_shim, SDW_CNL_SYNC); + sync_reg |= (SDW_CNL_DEFAULT_SYNC_PERIOD << CNL_SYNC_SYNCPRD_SHIFT); + sync_reg |= (0x1 << CNL_SYNC_SYNCCPU_SHIFT); + cnl_sdw_reg_writel(data->sdw_shim, SDW_CNL_SYNC, sync_reg); + + do { + sync_update = cnl_sdw_reg_readl(data->sdw_shim, SDW_CNL_SYNC); + if ((sync_update & CNL_SYNC_SYNCCPU_MASK) == 0) + break; + timeout--; + /* Wait 20ms before each time */ + msleep(20); + } while (timeout != 0); + if ((sync_update & CNL_SYNC_SYNCCPU_MASK) != 0) { + dev_err(&mstr->dev, "Fail to set sync period\n"); + return -EINVAL; + } + /* Set command acceptance mode. This is required because when * Master broadcasts the clock_stop command to slaves, slaves * might be already suspended, so this return NO ACK, in that diff --git a/drivers/sdw/sdw_cnl_priv.h b/drivers/sdw/sdw_cnl_priv.h index 914f7cae2b01..c99433882dff 100644 --- a/drivers/sdw/sdw_cnl_priv.h +++ b/drivers/sdw/sdw_cnl_priv.h @@ -26,11 +26,13 @@ #define SDW_CNL_SLAVES_STAT_UPPER_DWORD_SHIFT 32 #define SDW_CNL_SLAVE_STATUS_BITS 4 #define SDW_CNL_CMD_WORD_LEN 4 -#define SDW_CNL_DEFAULT_SSP_INTERVAL 0x32 +#define SDW_CNL_DEFAULT_SSP_INTERVAL 0x18 +#define SDW_CNL_DEFAULT_SYNC_PERIOD 0x257F + #define SDW_CNL_PORT_REG_OFFSET 0x80 #define CNL_SDW_SCP_ADDR_REGS 0x2 #define SDW_CNL_PCM_PDI_NUM_OFFSET 0x2 -#define SDW_CNL_PDM_PDI_NUM_OFFSET 0x6 +#define SDW_CNL_PDM_PDI_NUM_OFFSET 0x6 #define SDW_CNL_CTMCTL_REG_OFFSET 0x60 #define SDW_CNL_IOCTL_REG_OFFSET 0x60 @@ -257,8 +259,12 @@ #define SDW_CNL_SYNC 0xC #define CNL_SYNC_CMDSYNC_MASK 0x1 #define CNL_SYNC_CMDSYNC_SHIFT 16 -#define CNL_SYNC_SYNCGO_MASK 0x1 +#define CNL_SYNC_SYNCGO_MASK 0x1000000 #define CNL_SYNC_SYNCGO_SHIFT 0x18 +#define CNL_SYNC_SYNCPRD_MASK 0x7FFF +#define CNL_SYNC_SYNCPRD_SHIFT 0x0 +#define CNL_SYNC_SYNCCPU_MASK 0x8000 +#define CNL_SYNC_SYNCCPU_SHIFT 0xF #define SDW_CNL_CTLSCAP 0x10 #define SDW_CNL_CTLS0CM 0x12 From 6e1d5975f214d96c1ddcb90614cf5f23502ebfc4 Mon Sep 17 00:00:00 2001 From: Hardik Shah Date: Fri, 29 Apr 2016 16:30:53 +0530 Subject: [PATCH 0631/1103] SDW: Intel: Add the handler for async message transfer. Async message transfer is used by the bus driver in case stream is split between two masters. Else synchronous message transfer is always used. Change-Id: I7fda7fd954f41941ab054cdc0c41de1e9ceca24a Signed-off-by: Hardik Shah Reviewed-on: --- drivers/sdw/sdw_cnl.c | 148 +++++++++++++++++++++++++++++++----------- 1 file changed, 109 insertions(+), 39 deletions(-) diff --git a/drivers/sdw/sdw_cnl.c b/drivers/sdw/sdw_cnl.c index 21a61b4f010a..a5fc6db79f83 100644 --- a/drivers/sdw/sdw_cnl.c +++ b/drivers/sdw/sdw_cnl.c @@ -79,6 +79,12 @@ static inline void cnl_sdw_port_reg_writel(u32 __iomem *base, int offset, return cnl_sdw_reg_writel(base, offset + port_num * 128, value); } +struct cnl_sdw_async_msg { + struct completion *async_xfer_complete; + struct sdw_msg *msg; + int length; +}; + struct cnl_sdw { struct cnl_sdw_data data; struct sdw_master *mstr; @@ -100,6 +106,7 @@ struct cnl_sdw { struct cnl_sdw_pdi_stream *out_pdm_streams; struct mutex stream_lock; spinlock_t ctrl_lock; + struct cnl_sdw_async_msg async_msg; u32 response_buf[0x80]; bool sdw_link_status; @@ -808,6 +815,46 @@ static void cnl_sdw_read_response(struct cnl_sdw *sdw) } } +static enum sdw_command_response sdw_fill_message_response( + struct sdw_master *mstr, + struct sdw_msg *msg, + int count, int offset) +{ + int i, j; + int no_ack = 0, nack = 0; + struct cnl_sdw *sdw = sdw_master_get_drvdata(mstr); + + for (i = 0; i < count; i++) { + if (!(MCP_RESPONSE_ACK_MASK & sdw->response_buf[i])) { + no_ack = 1; + dev_err(&mstr->dev, "Ack not recevied\n"); + if ((MCP_RESPONSE_NACK_MASK & + sdw->response_buf[i])) { + nack = 1; + dev_err(&mstr->dev, "NACK recevied\n"); + } + } + break; + } + if (nack) { + dev_err(&mstr->dev, "Nack detected for slave %d\n", msg->slave_addr); + msg->len = 0; + return -EREMOTEIO; + } else if (no_ack) { + dev_err(&mstr->dev, "Command ignored for slave %d\n", msg->slave_addr); + msg->len = 0; + return -EREMOTEIO; + } + if (msg->flag == SDW_MSG_FLAG_WRITE) + return 0; + /* Response and Command has same base address */ + for (j = 0; j < count; j++) + msg->buf[j + offset] = + (sdw->response_buf[j] >> MCP_RESPONSE_RDATA_SHIFT); + return 0; +} + + irqreturn_t cnl_sdw_irq_handler(int irq, void *context) { struct cnl_sdw *sdw = context; @@ -840,7 +887,14 @@ irqreturn_t cnl_sdw_irq_handler(int irq, void *context) if (int_status & (MCP_INTSTAT_RXWL_MASK << MCP_INTSTAT_RXWL_SHIFT)) { cnl_sdw_read_response(sdw); - complete(&sdw->tx_complete); + if (sdw->async_msg.async_xfer_complete) { + sdw_fill_message_response(mstr, sdw->async_msg.msg, + sdw->async_msg.length, 0); + complete(sdw->async_msg.async_xfer_complete); + sdw->async_msg.async_xfer_complete = NULL; + sdw->async_msg.msg = NULL; + } else + complete(&sdw->tx_complete); } if (int_status & (MCP_INTSTAT_CONTROLBUSCLASH_MASK << MCP_INTSTAT_CONTROLBUSCLASH_SHIFT)) { @@ -945,16 +999,14 @@ static enum sdw_command_response cnl_program_scp_addr(struct sdw_master *mstr, } static enum sdw_command_response sdw_xfer_msg(struct sdw_master *mstr, - struct sdw_msg *msg, int cmd, int offset, int count) + struct sdw_msg *msg, int cmd, int offset, int count, bool async) { struct cnl_sdw *sdw = sdw_master_get_drvdata(mstr); struct cnl_sdw_data *data = &sdw->data; - int i, j; + int j; u32 cmd_base = SDW_CNL_MCP_COMMAND_BASE; - u32 response_base = SDW_CNL_MCP_RESPONSE_BASE; - u32 cmd_data = 0, response_data; + u32 cmd_data = 0; unsigned long time_left; - int no_ack = 0, nack = 0; u16 addr = msg->addr; /* Program the watermark level upto number of count */ @@ -983,6 +1035,10 @@ static enum sdw_command_response sdw_xfer_msg(struct sdw_master *mstr, cmd_base, cmd_data); cmd_base += SDW_CNL_CMD_WORD_LEN; } + + /* If Async dont wait for completion */ + if (async) + return 0; /* Wait for 3 second for timeout */ time_left = wait_for_completion_timeout(&sdw->tx_complete, 3 * HZ); if (!time_left) { @@ -990,39 +1046,52 @@ static enum sdw_command_response sdw_xfer_msg(struct sdw_master *mstr, msg->len = 0; return -ETIMEDOUT; } - for (i = 0; i < count; i++) { - if (!(MCP_RESPONSE_ACK_MASK & sdw->response_buf[i])) { - no_ack = 1; - dev_err(&mstr->dev, "Ack not recevied\n"); - if ((MCP_RESPONSE_NACK_MASK & - sdw->response_buf[i])) { - nack = 1; - dev_err(&mstr->dev, "NACK recevied\n"); - } - } - break; - } - if (nack) { - dev_err(&mstr->dev, "Nack detected for slave %d\n", msg->slave_addr); - msg->len = 0; - return -EREMOTEIO; - } else if (no_ack) { - dev_err(&mstr->dev, "Command ignored for slave %d\n", msg->slave_addr); - msg->len = 0; - return -EREMOTEIO; + return sdw_fill_message_response(mstr, msg, count, offset); +} + +static enum sdw_command_response cnl_sdw_xfer_msg_async(struct sdw_master *mstr, + struct sdw_msg *msg, bool program_scp_addr_page, + struct sdw_async_xfer_data *data) +{ + int ret = 0, cmd; + struct cnl_sdw *sdw = sdw_master_get_drvdata(mstr); + + /* Only 1 message can be handled in Async fashion. This is used + * only for Bank switching where during aggregation it is required + * to synchronously switch the bank on more than 1 controller + */ + if (msg->len > 1) { + ret = -EINVAL; + goto error; } - if (msg->flag == SDW_MSG_FLAG_WRITE) - return 0; - /* Response and Command has same base address */ - response_base = SDW_CNL_MCP_COMMAND_BASE; - for (j = 0; j < count; j++) { - response_data = cnl_sdw_reg_readl(data->sdw_regs, - cmd_base); - msg->buf[j + offset] = - (sdw->response_buf[j] >> MCP_RESPONSE_RDATA_SHIFT); - cmd_base += 4; + /* If scp addr programming fails goto error */ + if (program_scp_addr_page) + ret = cnl_program_scp_addr(mstr, msg); + if (ret) + goto error; + + switch (msg->flag) { + case SDW_MSG_FLAG_READ: + cmd = 0x2; + break; + case SDW_MSG_FLAG_WRITE: + cmd = 0x3; + break; + default: + dev_err(&mstr->dev, "Command not supported\n"); + return -EINVAL; } - return 0; + sdw->async_msg.async_xfer_complete = &data->xfer_complete; + sdw->async_msg.msg = msg; + sdw->async_msg.length = msg->len; + /* Dont wait for reply, calling function will wait for reply. */ + ret = sdw_xfer_msg(mstr, msg, cmd, 0, msg->len, true); + return ret; +error: + msg->len = 0; + complete(&data->xfer_complete); + return -EINVAL; + } static enum sdw_command_response cnl_sdw_xfer_msg(struct sdw_master *mstr, @@ -1052,14 +1121,14 @@ static enum sdw_command_response cnl_sdw_xfer_msg(struct sdw_master *mstr, for (i = 0; i < msg->len / SDW_CNL_MCP_COMMAND_LENGTH; i++) { ret = sdw_xfer_msg(mstr, msg, cmd, i * SDW_CNL_MCP_COMMAND_LENGTH, - SDW_CNL_MCP_COMMAND_LENGTH); + SDW_CNL_MCP_COMMAND_LENGTH, false); if (ret < 0) break; } if (!(msg->len % SDW_CNL_MCP_COMMAND_LENGTH)) return ret; ret = sdw_xfer_msg(mstr, msg, cmd, i * SDW_CNL_MCP_COMMAND_LENGTH, - msg->len % SDW_CNL_MCP_COMMAND_LENGTH); + msg->len % SDW_CNL_MCP_COMMAND_LENGTH, false); if (ret < 0) return -EINVAL; return ret; @@ -1561,6 +1630,7 @@ static const struct dev_pm_ops cnl_sdw_pm_ops = { }; static struct sdw_master_ops cnl_sdw_master_ops = { + .xfer_msg_async = cnl_sdw_xfer_msg_async, .xfer_msg = cnl_sdw_xfer_msg, .xfer_bulk = cnl_sdw_xfer_bulk, .monitor_handover = cnl_sdw_mon_handover, From 815a4d0c0e5514d801b6b1709cf8780df70eed67 Mon Sep 17 00:00:00 2001 From: Hardik Shah Date: Fri, 29 Apr 2016 16:39:23 +0530 Subject: [PATCH 0632/1103] SDW:CNL: Fix the syncgo functionality. Sync go is used to synchronously send the commands on more than 1 master to support aggregation. Fix the implementation of sync go. Change-Id: I2d62b9d11a0319a3eda3d6e5cf9a3800d31d2463 Signed-off-by: Hardik Shah Reviewed-on: --- drivers/sdw/sdw_cnl.c | 23 ++++++++++++++++++++++- drivers/sdw/sdw_cnl_priv.h | 3 ++- 2 files changed, 24 insertions(+), 2 deletions(-) diff --git a/drivers/sdw/sdw_cnl.c b/drivers/sdw/sdw_cnl.c index a5fc6db79f83..274966572499 100644 --- a/drivers/sdw/sdw_cnl.c +++ b/drivers/sdw/sdw_cnl.c @@ -1365,11 +1365,32 @@ static int cnl_sdw_port_activate_ch_post(struct sdw_master *mstr, int sync_reg; struct cnl_sdw *sdw = sdw_master_get_drvdata(mstr); struct cnl_sdw_data *data = &sdw->data; + volatile int sync_update = 0; + int timeout = 10; + sync_reg = cnl_sdw_reg_readl(data->sdw_shim, SDW_CNL_SYNC); - sync_reg |= CNL_SYNC_SYNCGO_MASK << CNL_SYNC_SYNCGO_SHIFT; + /* If waiting for synchronization set the go bit, else return */ + if (!(sync_reg & SDW_CMDSYNC_SET_MASK)) + return 0; + sync_reg |= (CNL_SYNC_SYNCGO_MASK << CNL_SYNC_SYNCGO_SHIFT); cnl_sdw_reg_writel(data->sdw_shim, SDW_CNL_SYNC, sync_reg); + do { + sync_update = cnl_sdw_reg_readl(data->sdw_shim, SDW_CNL_SYNC); + if ((sync_update & + (CNL_SYNC_SYNCGO_MASK << CNL_SYNC_SYNCGO_SHIFT)) == 0) + break; + msleep(20); + timeout--; + + } while (timeout); + + if ((sync_update & + (CNL_SYNC_SYNCGO_MASK << CNL_SYNC_SYNCGO_SHIFT)) != 0) { + dev_err(&mstr->dev, "Failed to set sync go\n"); + return -EIO; + } return 0; } diff --git a/drivers/sdw/sdw_cnl_priv.h b/drivers/sdw/sdw_cnl_priv.h index c99433882dff..8e9d68c2bc2c 100644 --- a/drivers/sdw/sdw_cnl_priv.h +++ b/drivers/sdw/sdw_cnl_priv.h @@ -255,11 +255,12 @@ #define CNL_LCTL_CPA_MASK 0x1 #define CNL_LCTL_SPA_MASK 0x1 +#define SDW_CMDSYNC_SET_MASK 0xF0000 #define SDW_CNL_IPPTR 0x8 #define SDW_CNL_SYNC 0xC #define CNL_SYNC_CMDSYNC_MASK 0x1 #define CNL_SYNC_CMDSYNC_SHIFT 16 -#define CNL_SYNC_SYNCGO_MASK 0x1000000 +#define CNL_SYNC_SYNCGO_MASK 0x1 #define CNL_SYNC_SYNCGO_SHIFT 0x18 #define CNL_SYNC_SYNCPRD_MASK 0x7FFF #define CNL_SYNC_SYNCPRD_SHIFT 0x0 From a7c872003c41da0848c14cdf0386a3d00ea3734b Mon Sep 17 00:00:00 2001 From: Sanyog Kale Date: Sun, 20 Nov 2016 20:18:36 +0530 Subject: [PATCH 0633/1103] SoundWire: Remove dead code from SoundWire BW calculation Remove some of the dead code from SoundWire BW calculation file. Change-Id: I8306e3d253c290741bbf8e1408551176bd986a80 Signed-off-by: Hardik Shah Reviewed-on: Signed-off-by: Sanyog Kale --- drivers/sdw/sdw_bwcalc.c | 23 ----------------------- 1 file changed, 23 deletions(-) diff --git a/drivers/sdw/sdw_bwcalc.c b/drivers/sdw/sdw_bwcalc.c index b76ace3b0de7..ea080f101d0f 100644 --- a/drivers/sdw/sdw_bwcalc.c +++ b/drivers/sdw/sdw_bwcalc.c @@ -117,12 +117,6 @@ int sdw_mstr_bw_init(struct sdw_bus *sdw_bs) sdw_bs->frame_freq = 0; sdw_bs->clk_state = SDW_CLK_STATE_ON; - /* TBD: to be removed later */ - /* Assumption is these should be already filled */ - sdw_mstr_cap = &sdw_bs->mstr->mstr_capabilities; - sdw_mstr_cap->monitor_handover_supported = false; - sdw_mstr_cap->highphy_capable = false; - #ifdef CONFIG_SND_SOC_SVFPGA /* TBD: For PDM capture to be removed later */ sdw_bs->clk_freq = 9.6 * 1000 * 1000 * 2; @@ -718,15 +712,6 @@ int sdw_cfg_mstr_activate_disable(struct sdw_bus *mstr_bs, if ((chn_en->is_activate) || (chn_en->is_bank_sw)) banktouse = !banktouse; - - /* 1. Master port enable_ch_pre */ - if (ops->mstr_port_ops->dpn_port_activate_ch_pre) { - ret = ops->mstr_port_ops->dpn_port_activate_ch_pre - (mstr_bs->mstr, &activate_ch, banktouse); - if (ret < 0) - return ret; - } - /* 2. Master port enable */ if (ops->mstr_port_ops->dpn_port_activate_ch) { ret = ops->mstr_port_ops->dpn_port_activate_ch(mstr_bs->mstr, @@ -735,14 +720,6 @@ int sdw_cfg_mstr_activate_disable(struct sdw_bus *mstr_bs, return ret; } - /* 3. Master port enable_ch_post */ - if (ops->mstr_port_ops->dpn_port_activate_ch_post) { - ret = ops->mstr_port_ops->dpn_port_activate_ch_post - (mstr_bs->mstr, &activate_ch, banktouse); - if (ret < 0) - return ret; - } - if (chn_en->is_activate) mstr_rt_strm->rt_state = SDW_STATE_ENABLE_RT; else if (!chn_en->is_bank_sw) From 4c684c44bf1f01e5b152984c5de348fc4b9fd6ac Mon Sep 17 00:00:00 2001 From: Hardik Shah Date: Sun, 1 May 2016 18:13:41 +0530 Subject: [PATCH 0634/1103] REVERTME:SDW: Skip the Slave programming for the stream Aggretation is tested with Maxim Slave device. This is just used to enable the aggregation test. Actually loop back test is used to demonstrate aggregation. Disable slave programming for testing aggretation with master loopback. Change-Id: Ie1fd183b5ebb155234d52ea17c78ca522df41e4d Signed-off-by: Hardik Shah Reviewed-on: --- drivers/sdw/sdw_bwcalc.c | 42 ++++++++++++++++++++++++++++++++-------- 1 file changed, 34 insertions(+), 8 deletions(-) diff --git a/drivers/sdw/sdw_bwcalc.c b/drivers/sdw/sdw_bwcalc.c index ea080f101d0f..5b362dc0a2f4 100644 --- a/drivers/sdw/sdw_bwcalc.c +++ b/drivers/sdw/sdw_bwcalc.c @@ -260,14 +260,17 @@ int sdw_cfg_slv_params(struct sdw_bus *mstr_bs, rd_msg.buf = rbuf; rd_msg.addr_page1 = 0x0; rd_msg.addr_page2 = 0x0; - +/* Dont program slave params for the Aggregation. + * Its with master loop back + */ +#ifndef CONFIG_SND_SOC_MXFPGA ret = sdw_slave_transfer(mstr_bs->mstr, &rd_msg, 1); if (ret != 1) { ret = -EINVAL; dev_err(&mstr_bs->mstr->dev, "Register transfer failed\n"); goto out; } - +#endif wbuf1[0] = (p_slv_params->port_flow_mode | (p_slv_params->port_data_mode << @@ -304,7 +307,10 @@ int sdw_cfg_slv_params(struct sdw_bus *mstr_bs, wr_msg1.buf = &wbuf1[0]; wr_msg1.addr_page1 = 0x0; wr_msg1.addr_page2 = 0x0; - +/* Dont program slave params for the Aggregation. + * Its with master loop back + */ +#ifndef CONFIG_SND_SOC_MXFPGA ret = sdw_slave_transfer(mstr_bs->mstr, &wr_msg, 1); if (ret != 1) { ret = -EINVAL; @@ -319,8 +325,9 @@ int sdw_cfg_slv_params(struct sdw_bus *mstr_bs, dev_err(&mstr_bs->mstr->dev, "Register transfer failed\n"); goto out; } - out: +#endif + return ret; } @@ -601,6 +608,10 @@ int sdw_cfg_slv_enable_disable(struct sdw_bus *mstr_bs, */ /* 2. slave port enable */ +/* Dont program slave params for the Aggregation. + * Its with master loop back + */ +#ifndef CONFIG_SND_SOC_MXFPGA ret = sdw_slave_transfer(mstr_bs->mstr, &rd_msg, 1); if (ret != 1) { ret = -EINVAL; @@ -627,7 +638,7 @@ int sdw_cfg_slv_enable_disable(struct sdw_bus *mstr_bs, "Register transfer failed\n"); goto out; } - +#endif /* * 3. slave port enable post pre * --> callback @@ -642,6 +653,10 @@ int sdw_cfg_slv_enable_disable(struct sdw_bus *mstr_bs, * --> callback * --> no callback available */ +/* Dont program slave params for the Aggregation. + * Its with master loop back + */ +#ifndef CONFIG_SND_SOC_MXFPGA /* 2. slave port disable */ ret = sdw_slave_transfer(mstr_bs->mstr, &rd_msg, 1); @@ -670,7 +685,7 @@ int sdw_cfg_slv_enable_disable(struct sdw_bus *mstr_bs, "Register transfer failed\n"); goto out; } - +#endif /* * 3. slave port enable post unpre * --> callback @@ -680,8 +695,9 @@ int sdw_cfg_slv_enable_disable(struct sdw_bus *mstr_bs, slv_rt_strm->rt_state = SDW_STATE_DISABLE_RT; } - +#ifndef CONFIG_SND_SOC_MXFPGA out: +#endif return ret; } @@ -1284,12 +1300,20 @@ int sdw_compute_blk_subblk_offset(struct sdw_bus *sdw_mstr_bs) (hstop1 != hstop2)) { block_offset = 1; } else { +/* We are doing loopback for the Aggregation so block offset should + * always remain same. This is not a requirement. This we are doing + * to test aggregation without codec. + */ +#ifdef CONFIG_SND_SOC_MXFPGA + block_offset = 1; +#else block_offset += (sdw_mstr_bs_rt->stream_params. bps * sdw_mstr_bs_rt->stream_params. channel_count); +#endif } #endif @@ -1641,7 +1665,9 @@ int sdw_cfg_slv_prep_unprep(struct sdw_bus *mstr_bs, wr_msg.addr_page1 = 0x0; wr_msg.addr_page2 = 0x0; - +#ifdef CONFIG_SND_SOC_MXFPGA + sdw_slv_dpn_cap->prepare_ch = 0; +#endif if (prep) { /* PREPARE */ /* From 2dd917036f10cf054a9a0c7c7ea25561e82a8758 Mon Sep 17 00:00:00 2001 From: Hardik Shah Date: Sun, 1 May 2016 18:29:30 +0530 Subject: [PATCH 0635/1103] SoundWire: Add support for the aggregation Add support for the master aggregation in SoundWire bus driver. For the aggregated masters bus driver calls async message transfer APIs for the bank swith to make sure that bank switch for multiple masters happen at same time. Change-Id: I4272b2ff35c6aee9ee3d1919113b2d11be766abc Signed-off-by: Hardik Shah Reviewed-on: --- drivers/sdw/sdw_bwcalc.c | 1031 ++++++++++++++++++++++++++++---------- 1 file changed, 755 insertions(+), 276 deletions(-) diff --git a/drivers/sdw/sdw_bwcalc.c b/drivers/sdw/sdw_bwcalc.c index 5b362dc0a2f4..cafaccbeea3a 100644 --- a/drivers/sdw/sdw_bwcalc.c +++ b/drivers/sdw/sdw_bwcalc.c @@ -17,7 +17,7 @@ * * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ - +#include #include #include #include "sdw_priv.h" @@ -116,7 +116,7 @@ int sdw_mstr_bw_init(struct sdw_bus *sdw_bs) sdw_bs->system_interval = 0; sdw_bs->frame_freq = 0; sdw_bs->clk_state = SDW_CLK_STATE_ON; - + sdw_mstr_cap = &sdw_bs->mstr->mstr_capabilities; #ifdef CONFIG_SND_SOC_SVFPGA /* TBD: For PDM capture to be removed later */ sdw_bs->clk_freq = 9.6 * 1000 * 1000 * 2; @@ -1457,6 +1457,103 @@ int sdw_configure_frmshp_bnkswtch(struct sdw_bus *mstr_bs, int col, int row) return ret; } +/* + * sdw_configure_frmshp_bnkswtch - returns Success + * -EINVAL - In case of error. + * + * + * This function broadcast frameshape on framectrl + * register and performs bank switch. + */ +int sdw_configure_frmshp_bnkswtch_mm(struct sdw_bus *mstr_bs, int col, int row) +{ + int ret = 0; + int banktouse, numcol, numrow; + u8 *wbuf; + struct sdw_msg *wr_msg; + + wr_msg = kzalloc(sizeof(struct sdw_msg), GFP_KERNEL); + mstr_bs->async_data.msg = wr_msg; + if (!wr_msg) + return -ENOMEM; + wbuf = kzalloc(sizeof(*wbuf), GFP_KERNEL); + if (!wbuf) + return -ENOMEM; + numcol = sdw_get_col_to_num(col); + numrow = sdw_get_row_to_num(row); + + wbuf[0] = numcol | (numrow << 3); + /* Get current bank in use from bus structure*/ + banktouse = mstr_bs->active_bank; + banktouse = !banktouse; + + if (banktouse) { + wr_msg->addr = (SDW_SCP_FRAMECTRL + SDW_BANK1_REGISTER_OFFSET) + + (SDW_NUM_DATA_PORT_REGISTERS * 0); /* Data port 0 */ + } else { + + wr_msg->addr = SDW_SCP_FRAMECTRL + + (SDW_NUM_DATA_PORT_REGISTERS * 0); /* Data port 0 */ + } + + wr_msg->ssp_tag = 0x1; + wr_msg->flag = SDW_MSG_FLAG_WRITE; + wr_msg->len = 1; + wr_msg->slave_addr = 0xF; /* Broadcast address*/ + wr_msg->buf = wbuf; + wr_msg->addr_page1 = 0x0; + wr_msg->addr_page2 = 0x0; + + if (in_atomic() || irqs_disabled()) { + ret = sdw_trylock_mstr(mstr_bs->mstr); + if (!ret) { + /* SDW activity is ongoing. */ + ret = -EAGAIN; + goto out; + } + } else { + sdw_lock_mstr(mstr_bs->mstr); + } + + ret = sdw_slave_transfer_async(mstr_bs->mstr, wr_msg, + 1, &mstr_bs->async_data); + if (ret != 1) { + ret = -EINVAL; + dev_err(&mstr_bs->mstr->dev, "Register transfer failed\n"); + goto out; + } + + msleep(100); /* TBD: Remove this */ + + /* + * TBD: check whether we need to poll on + * mcp active bank bit to switch bank + */ + mstr_bs->active_bank = banktouse; + +out: + + return ret; +} + +int sdw_configure_frmshp_bnkswtch_mm_wait(struct sdw_bus *mstr_bs) +{ + unsigned long time_left; + struct sdw_master *mstr = mstr_bs->mstr; + + time_left = wait_for_completion_timeout( + &mstr_bs->async_data.xfer_complete, + 3000); + if (!time_left) { + dev_err(&mstr->dev, "Controller Timed out\n"); + sdw_unlock_mstr(mstr); + return -ETIMEDOUT; + } + kfree(mstr_bs->async_data.msg->buf); + kfree(mstr_bs->async_data.msg); + sdw_unlock_mstr(mstr); + return 0; +} /* * sdw_cfg_bs_params - returns Success @@ -1937,6 +2034,21 @@ int sdw_prep_unprep_mstr_slv(struct sdw_bus *sdw_mstr_bs, return 0; } +struct sdw_bus *master_to_bus(struct sdw_master *mstr) +{ + struct sdw_bus *sdw_mstr_bs = NULL; + + list_for_each_entry(sdw_mstr_bs, &sdw_core.bus_list, bus_node) { + /* Match master structure pointer */ + if (sdw_mstr_bs->mstr != mstr) + continue; + return sdw_mstr_bs; + } + /* This should never happen, added to suppress warning */ + WARN_ON(1); + + return NULL; +} /** * sdw_bus_calc_bw - returns Success @@ -1956,13 +2068,16 @@ int sdw_bus_calc_bw(struct sdw_stream_tag *stream_tag, bool enable) struct sdw_runtime *sdw_rt = stream_tag->sdw_rt; struct sdw_stream_params *stream_params = &sdw_rt->stream_params; struct sdw_mstr_runtime *sdw_mstr_rt = NULL, *sdw_mstr_bs_rt = NULL; - struct sdw_bus *sdw_mstr_bs = NULL; + struct sdw_mstr_runtime *mstr_rt_act = NULL, *last_rt = NULL; + struct sdw_bus *sdw_mstr_bs = NULL, *mstr_bs_act = NULL; struct sdw_master *sdw_mstr = NULL; struct sdw_master_capabilities *sdw_mstr_cap = NULL; struct sdw_stream_params *mstr_params; int stream_frame_size; int frame_interval = 0, sel_row = 0, sel_col = 0; int ret = 0; + bool last_node = false; + struct sdw_master_port_ops *ops; /* TBD: Add PCM/PDM flag in sdw_config_stream */ @@ -1973,22 +2088,21 @@ int sdw_bus_calc_bw(struct sdw_stream_tag *stream_tag, bool enable) */ /* BW calulation for active master controller for given stream tag */ - list_for_each_entry(sdw_mstr_rt, &sdw_rt->mstr_rt_list, mstr_sdw_node) { + list_for_each_entry(sdw_mstr_rt, &sdw_rt->mstr_rt_list, + mstr_sdw_node) { if (sdw_mstr_rt->mstr == NULL) break; + last_rt = list_last_entry(&sdw_rt->mstr_rt_list, + struct sdw_mstr_runtime, mstr_sdw_node); + if (sdw_mstr_rt == last_rt) + last_node = true; + else + last_node = false; /* Get bus structure for master */ - list_for_each_entry(sdw_mstr_bs, &sdw_core.bus_list, bus_node) { - - /* Match master structure pointer */ - if (sdw_mstr_bs->mstr != sdw_mstr_rt->mstr) - continue; - - - sdw_mstr = sdw_mstr_bs->mstr; - break; - } + sdw_mstr_bs = master_to_bus(sdw_mstr_rt->mstr); + sdw_mstr = sdw_mstr_bs->mstr; /* * All data structures required available, @@ -1999,180 +2113,384 @@ int sdw_bus_calc_bw(struct sdw_stream_tag *stream_tag, bool enable) sdw_mstr_cap = &sdw_mstr_bs->mstr->mstr_capabilities; mstr_params = &sdw_mstr_rt->stream_params; - if ((sdw_rt->stream_state == SDW_STATE_CONFIG_STREAM) || - (sdw_rt->stream_state == - SDW_STATE_UNPREPARE_STREAM)) { + if ((sdw_rt->stream_state != SDW_STATE_CONFIG_STREAM) && + (sdw_rt->stream_state != SDW_STATE_UNPREPARE_STREAM)) + goto enable_stream; - /* we do not support asynchronous mode Return Error */ - if ((sdw_mstr_cap->base_clk_freq % mstr_params->rate) - != 0) { - /* TBD: Undo all the computation */ - dev_err(&sdw_mstr_bs->mstr->dev, - "Asynchronous mode not supported\n"); - return -EINVAL; - } + /* we do not support asynchronous mode Return Error */ + if ((sdw_mstr_cap->base_clk_freq % mstr_params->rate) != 0) { + /* TBD: Undo all the computation */ + dev_err(&sdw_mstr->dev, "Async mode not supported\n"); + return -EINVAL; + } - /* Check for sampling frequency */ - if (stream_params->rate != mstr_params->rate) { - /* TBD: Undo all the computation */ - dev_err(&sdw_mstr_bs->mstr->dev, - "Sampling frequency mismatch\n"); - return -EINVAL; - } + /* Check for sampling frequency */ + if (stream_params->rate != mstr_params->rate) { + /* TBD: Undo all the computation */ + dev_err(&sdw_mstr->dev, "Sample frequency mismatch\n"); + return -EINVAL; + } - /* - * Calculate stream bandwidth, frame size and - * total BW required for master controller - */ - sdw_mstr_rt->stream_bw = mstr_params->rate * - mstr_params->channel_count * mstr_params->bps; - stream_frame_size = mstr_params->channel_count * - mstr_params->bps; + /* + * Calculate stream bandwidth, frame size and + * total BW required for master controller + */ + sdw_mstr_rt->stream_bw = mstr_params->rate * + mstr_params->channel_count * mstr_params->bps; + stream_frame_size = mstr_params->channel_count * + mstr_params->bps; - sdw_mstr_bs->bandwidth += sdw_mstr_rt->stream_bw; + sdw_mstr_bs->bandwidth += sdw_mstr_rt->stream_bw; - ret = sdw_get_clock_frmshp(sdw_mstr_bs, - &frame_interval, &sel_col, &sel_row); - if (ret < 0) { - /* TBD: Undo all the computation */ - dev_err(&sdw_mstr_bs->mstr->dev, "clock/frameshape config failed\n"); - return ret; - } + ret = sdw_get_clock_frmshp(sdw_mstr_bs, + &frame_interval, &sel_col, &sel_row); + if (ret < 0) { + /* TBD: Undo all the computation */ + dev_err(&sdw_mstr->dev, "clock/frameshape config failed\n"); + return ret; + } + /* + * TBD: find right place to run sorting on + * master rt_list. Below sorting is done based on + * bps from low to high, that means PDM streams + * will be placed before PCM. + */ - /* - * TBD: find right place to run sorting on - * master rt_list. Below sorting is done based on - * bps from low to high, that means PDM streams - * will be placed before PCM. - */ + /* + * TBD Should we also perform sorting based on rate + * for PCM stream check. if yes then how?? + * creating two different list. + */ - /* - * TBD Should we also perform sorting based on rate - * for PCM stream check. if yes then how?? - * creating two different list. - */ + /* Compute system interval */ + ret = sdw_compute_sys_interval(sdw_mstr_bs, sdw_mstr_cap, + frame_interval); + if (ret < 0) { + /* TBD: Undo all the computation */ + dev_err(&sdw_mstr->dev, "compute system interval failed\n"); + return ret; + } - /* Compute system interval */ - ret = sdw_compute_sys_interval(sdw_mstr_bs, - sdw_mstr_cap, frame_interval); - if (ret < 0) { - /* TBD: Undo all the computation */ - dev_err(&sdw_mstr_bs->mstr->dev, "compute system interval failed\n"); - return ret; + /* Compute hstart/hstop */ + ret = sdw_compute_hstart_hstop(sdw_mstr_bs, sel_col); + if (ret < 0) { + /* TBD: Undo all the computation */ + dev_err(&sdw_mstr->dev, "compute hstart/hstop failed\n"); + return ret; + } + + /* Compute block offset */ + ret = sdw_compute_blk_subblk_offset(sdw_mstr_bs); + if (ret < 0) { + /* TBD: Undo all the computation */ + dev_err(&sdw_mstr->dev, "compute block offset failed\n"); + return ret; + } + + /* Change Stream State */ + if (last_node) + sdw_rt->stream_state = SDW_STATE_COMPUTE_STREAM; + + /* Configure bus parameters */ + ret = sdw_cfg_bs_params(sdw_mstr_bs, sdw_mstr_bs_rt, true); + if (ret < 0) { + /* TBD: Undo all the computation */ + dev_err(&sdw_mstr->dev, "xport param config failed\n"); + return ret; + } + + sel_col = sdw_mstr_bs->col; + sel_row = sdw_mstr_bs->row; + + if ((last_node) && (sdw_mstr->link_sync_mask)) { + + list_for_each_entry(mstr_rt_act, &sdw_rt->mstr_rt_list, + mstr_sdw_node) { + + if (mstr_rt_act->mstr == NULL) + break; + + /* Get bus structure for master */ + mstr_bs_act = master_to_bus(mstr_rt_act->mstr); + ops = mstr_bs_act->mstr->driver->mstr_port_ops; + + /* Run for all mstr_list and + * pre_activate ports + */ + if (ops->dpn_port_activate_ch_pre) { + ret = ops->dpn_port_activate_ch_pre + (mstr_bs_act->mstr, NULL, 0); + if (ret < 0) + return ret; + } } - /* Compute hstart/hstop */ - ret = sdw_compute_hstart_hstop(sdw_mstr_bs, sel_col); - if (ret < 0) { - /* TBD: Undo all the computation */ - dev_err(&sdw_mstr_bs->mstr->dev, - "compute hstart/hstop failed\n"); - return ret; + list_for_each_entry(mstr_rt_act, &sdw_rt->mstr_rt_list, + mstr_sdw_node) { + if (mstr_rt_act->mstr == NULL) + break; + + /* Get bus structure for master */ + mstr_bs_act = master_to_bus(mstr_rt_act->mstr); + + /* Configure Frame Shape/Switch Bank */ + ret = sdw_configure_frmshp_bnkswtch_mm( + mstr_bs_act, sel_col, sel_row); + if (ret < 0) { + /* TBD: Undo all the computation */ + dev_err(&sdw_mstr->dev, "bank switch failed\n"); + return ret; + } } - /* Compute block offset */ - ret = sdw_compute_blk_subblk_offset(sdw_mstr_bs); - if (ret < 0) { - /* TBD: Undo all the computation */ - dev_err( - &sdw_mstr_bs->mstr->dev, - "compute block offset failed\n"); - return ret; + list_for_each_entry(mstr_rt_act, &sdw_rt->mstr_rt_list, + mstr_sdw_node) { + + if (mstr_rt_act->mstr == NULL) + break; + + /* Get bus structure for master */ + mstr_bs_act = master_to_bus(mstr_rt_act->mstr); + + ops = mstr_bs_act->mstr->driver->mstr_port_ops; + + /* Run for all mstr_list and + * post_activate ports + */ + if (ops->dpn_port_activate_ch_post) { + ret = ops->dpn_port_activate_ch_post + (mstr_bs_act->mstr, NULL, 0); + if (ret < 0) + return ret; + } } - /* Change Stream State */ - sdw_rt->stream_state = SDW_STATE_COMPUTE_STREAM; + list_for_each_entry(mstr_rt_act, + &sdw_rt->mstr_rt_list, mstr_sdw_node) { - /* Configure bus parameters */ - ret = sdw_cfg_bs_params(sdw_mstr_bs, - sdw_mstr_bs_rt, true); - if (ret < 0) { - /* TBD: Undo all the computation */ - dev_err(&sdw_mstr_bs->mstr->dev, - "xport params config failed\n"); - return ret; + if (mstr_rt_act->mstr == NULL) + break; + + mstr_bs_act = master_to_bus( + mstr_rt_act->mstr); + ret = sdw_configure_frmshp_bnkswtch_mm_wait( + mstr_bs_act); } - sel_col = sdw_mstr_bs->col; - sel_row = sdw_mstr_bs->row; + list_for_each_entry(mstr_rt_act, &sdw_rt->mstr_rt_list, + mstr_sdw_node) { + + if (mstr_rt_act->mstr == NULL) + break; + + /* Get bus structure for master */ + mstr_bs_act = master_to_bus(mstr_rt_act->mstr); + + /* Disable all channels + * enabled on previous bank + */ + ret = sdw_dis_chan(mstr_bs_act, sdw_mstr_bs_rt); + if (ret < 0) { + /* TBD: Undo all the computation */ + dev_err(&sdw_mstr->dev, "Channel disabled faile\n"); + return ret; + } + } + } + if (!sdw_mstr->link_sync_mask) { /* Configure Frame Shape/Switch Bank */ ret = sdw_configure_frmshp_bnkswtch(sdw_mstr_bs, sel_col, sel_row); if (ret < 0) { /* TBD: Undo all the computation */ - dev_err(&sdw_mstr_bs->mstr->dev, - "bank switch failed\n"); + dev_err(&sdw_mstr->dev, "bank switch failed\n"); return ret; } - /* Disable all channels enabled on previous bank */ ret = sdw_dis_chan(sdw_mstr_bs, sdw_mstr_bs_rt); if (ret < 0) { /* TBD: Undo all the computation */ - dev_err(&sdw_mstr_bs->mstr->dev, - "Channel disabled failed\n"); - return ret; - } - - /* Prepare new port for master and slave */ - ret = sdw_prep_unprep_mstr_slv(sdw_mstr_bs, - sdw_rt, true); - if (ret < 0) { - /* TBD: Undo all the computation */ - dev_err(&sdw_mstr_bs->mstr->dev, - "Channel prepare failed\n"); + dev_err(&sdw_mstr->dev, "Channel disabled failed\n"); return ret; } + } + /* Prepare new port for master and slave */ + ret = sdw_prep_unprep_mstr_slv(sdw_mstr_bs, sdw_rt, true); + if (ret < 0) { + /* TBD: Undo all the computation */ + dev_err(&sdw_mstr->dev, "Channel prepare failed\n"); + return ret; + } - /* change stream state to prepare */ + /* change stream state to prepare */ + if (last_node) sdw_rt->stream_state = SDW_STATE_PREPARE_STREAM; + } +enable_stream: + list_for_each_entry(sdw_mstr_rt, &sdw_rt->mstr_rt_list, mstr_sdw_node) { + + + if (sdw_mstr_rt->mstr == NULL) + break; + last_rt = list_last_entry(&sdw_rt->mstr_rt_list, + struct sdw_mstr_runtime, mstr_sdw_node); + if (sdw_mstr_rt == last_rt) + last_node = true; + else + last_node = false; + + /* Get bus structure for master */ + sdw_mstr_bs = master_to_bus(sdw_mstr_rt->mstr); + sdw_mstr = sdw_mstr_bs->mstr; + + sdw_mstr_cap = &sdw_mstr_bs->mstr->mstr_capabilities; + mstr_params = &sdw_mstr_rt->stream_params; + + if ((!enable) || + (sdw_rt->stream_state != SDW_STATE_PREPARE_STREAM)) + return 0; + + ret = sdw_cfg_bs_params(sdw_mstr_bs, sdw_mstr_bs_rt, false); + if (ret < 0) { + /* TBD: Undo all the computation */ + dev_err(&sdw_mstr->dev, "xport params config failed\n"); + return ret; + } + + /* Enable new port for master and slave */ + ret = sdw_en_dis_mstr_slv(sdw_mstr_bs, sdw_rt, true); + if (ret < 0) { + /* TBD: Undo all the computation */ + dev_err(&sdw_mstr->dev, "Channel enable failed\n"); + return ret; } - if ((enable) && (SDW_STATE_PREPARE_STREAM - == sdw_rt->stream_state)) { + /* change stream state to enable */ + if (last_node) + sdw_rt->stream_state = SDW_STATE_ENABLE_STREAM; - ret = sdw_cfg_bs_params(sdw_mstr_bs, - sdw_mstr_bs_rt, false); - if (ret < 0) { - /* TBD: Undo all the computation */ - dev_err(&sdw_mstr_bs->mstr->dev, - "xport params config failed\n"); - return ret; + sel_col = sdw_mstr_bs->col; + sel_row = sdw_mstr_bs->row; + + if ((last_node) && (sdw_mstr->link_sync_mask)) { + + + list_for_each_entry(mstr_rt_act, &sdw_rt->mstr_rt_list, + mstr_sdw_node) { + + + if (mstr_rt_act->mstr == NULL) + break; + + /* Get bus structure for master */ + mstr_bs_act = master_to_bus(mstr_rt_act->mstr); + + ops = mstr_bs_act->mstr->driver->mstr_port_ops; + + /* Run for all mstr_list and + * pre_activate ports + */ + if (ops->dpn_port_activate_ch_pre) { + ret = ops->dpn_port_activate_ch_pre + (mstr_bs_act->mstr, NULL, 0); + if (ret < 0) + return ret; + } } - /* Enable new port for master and slave */ - ret = sdw_en_dis_mstr_slv(sdw_mstr_bs, sdw_rt, true); - if (ret < 0) { - /* TBD: Undo all the computation */ - dev_err(&sdw_mstr_bs->mstr->dev, - "Channel enable failed\n"); - return ret; + list_for_each_entry(mstr_rt_act, + &sdw_rt->mstr_rt_list, mstr_sdw_node) { + + + if (mstr_rt_act->mstr == NULL) + break; + + /* Get bus structure for master */ + mstr_bs_act = master_to_bus(mstr_rt_act->mstr); + + /* Configure Frame Shape/Switch Bank */ + ret = sdw_configure_frmshp_bnkswtch_mm( + mstr_bs_act, + sel_col, sel_row); + if (ret < 0) { + /* TBD: Undo all the computation */ + dev_err(&sdw_mstr->dev, "bank switch failed\n"); + return ret; + } } - /* change stream state to enable */ - sdw_rt->stream_state = SDW_STATE_ENABLE_STREAM; + list_for_each_entry(mstr_rt_act, + &sdw_rt->mstr_rt_list, mstr_sdw_node) { + + if (mstr_rt_act->mstr == NULL) + break; + + /* Get bus structure for master */ + mstr_bs_act = master_to_bus(mstr_rt_act->mstr); + + ops = mstr_bs_act->mstr->driver->mstr_port_ops; + + /* Run for all mstr_list and + * post_activate ports + */ + if (ops->dpn_port_activate_ch_post) { + ret = ops->dpn_port_activate_ch_post + (mstr_bs_act->mstr, NULL, 0); + if (ret < 0) + return ret; + } + } + + list_for_each_entry(mstr_rt_act, + &sdw_rt->mstr_rt_list, mstr_sdw_node) { - sel_col = sdw_mstr_bs->col; - sel_row = sdw_mstr_bs->row; + if (mstr_rt_act->mstr == NULL) + break; + /* Get bus structure for master */ + mstr_bs_act = master_to_bus(mstr_rt_act->mstr); + ret = sdw_configure_frmshp_bnkswtch_mm_wait( + mstr_bs_act); + } + list_for_each_entry(mstr_rt_act, + &sdw_rt->mstr_rt_list, mstr_sdw_node) { + + if (mstr_rt_act->mstr == NULL) + break; + + /* Get bus structure for master */ + mstr_bs_act = master_to_bus(mstr_rt_act->mstr); + + /* Disable all channels + * enabled on previous bank + */ + ret = sdw_dis_chan(mstr_bs_act, + sdw_mstr_bs_rt); + if (ret < 0) { + /* TBD: Undo all the computation */ + dev_err(&sdw_mstr->dev, + "Channel disabled faile\n"); + return ret; + } + } + } + if (!sdw_mstr->link_sync_mask) { /* Configure Frame Shape/Switch Bank */ - ret = sdw_configure_frmshp_bnkswtch(sdw_mstr_bs, + ret = sdw_configure_frmshp_bnkswtch( + sdw_mstr_bs, sel_col, sel_row); if (ret < 0) { /* TBD: Undo all the computation */ - dev_err(&sdw_mstr_bs->mstr->dev, - "bank switch failed\n"); + dev_err(&sdw_mstr->dev, "bank switch failed\n"); return ret; } - /* Disable all channels enabled on previous bank */ ret = sdw_dis_chan(sdw_mstr_bs, sdw_mstr_bs_rt); if (ret < 0) { /* TBD: Undo all the computation */ - dev_err(&sdw_mstr_bs->mstr->dev, - "Channel disabled faile\n"); + dev_err(&sdw_mstr->dev, "Ch disabled failed\n"); return ret; } } @@ -2182,7 +2500,6 @@ int sdw_bus_calc_bw(struct sdw_stream_tag *stream_tag, bool enable) } EXPORT_SYMBOL_GPL(sdw_bus_calc_bw); - /** * sdw_bus_calc_bw_dis - returns Success * -EINVAL - In case of error. @@ -2200,221 +2517,383 @@ int sdw_bus_calc_bw_dis(struct sdw_stream_tag *stream_tag, bool unprepare) { struct sdw_runtime *sdw_rt = stream_tag->sdw_rt; struct sdw_mstr_runtime *sdw_mstr_rt = NULL, *sdw_mstr_bs_rt = NULL; - struct sdw_bus *sdw_mstr_bs = NULL; + struct sdw_mstr_runtime *mstr_rt_act = NULL, *last_rt = NULL; + struct sdw_bus *sdw_mstr_bs = NULL, *mstr_bs_act = NULL; struct sdw_master *sdw_mstr = NULL; struct sdw_master_capabilities *sdw_mstr_cap = NULL; struct sdw_stream_params *mstr_params; int stream_frame_size; int frame_interval = 0, sel_row = 0, sel_col = 0; int ret = 0; - + bool last_node = false; + struct sdw_master_port_ops *ops; /* BW calulation for active master controller for given stream tag */ - list_for_each_entry(sdw_mstr_rt, &sdw_rt->mstr_rt_list, mstr_sdw_node) { + list_for_each_entry(sdw_mstr_rt, + &sdw_rt->mstr_rt_list, mstr_sdw_node) { + if (sdw_mstr_rt->mstr == NULL) break; + last_rt = list_last_entry(&sdw_rt->mstr_rt_list, + struct sdw_mstr_runtime, mstr_sdw_node); + if (sdw_mstr_rt == last_rt) + last_node = true; + else + last_node = false; + /* Get bus structure for master */ - list_for_each_entry(sdw_mstr_bs, &sdw_core.bus_list, bus_node) { + sdw_mstr_bs = master_to_bus(sdw_mstr_rt->mstr); + sdw_mstr = sdw_mstr_bs->mstr; - /* Match master structure pointer */ - if (sdw_mstr_bs->mstr != sdw_mstr_rt->mstr) - continue; + sdw_mstr_cap = &sdw_mstr_bs->mstr->mstr_capabilities; + mstr_params = &sdw_mstr_rt->stream_params; - sdw_mstr = sdw_mstr_bs->mstr; - break; - } + if (sdw_rt->stream_state != SDW_STATE_ENABLE_STREAM) + goto unprepare_stream; + /* Lets do disabling of port for stream to be freed */ + list_for_each_entry(sdw_mstr_bs_rt, + &sdw_mstr->mstr_rt_list, mstr_node) { - sdw_mstr_cap = &sdw_mstr_bs->mstr->mstr_capabilities; - mstr_params = &sdw_mstr_rt->stream_params; + if (sdw_mstr_bs_rt->mstr == NULL) + continue; - if (sdw_rt->stream_state == SDW_STATE_ENABLE_STREAM) { + /* + * Disable channel for slave and + * master on current bank + */ + ret = sdw_en_dis_mstr_slv(sdw_mstr_bs, sdw_rt, false); + if (ret < 0) { + /* TBD: Undo all the computation */ + dev_err(&sdw_mstr->dev, "Ch dis failed\n"); + return ret; + } - /* Lets do disabling of port for stream to be freed */ - list_for_each_entry(sdw_mstr_bs_rt, - &sdw_mstr->mstr_rt_list, mstr_node) { + /* Change stream state to disable */ + if (last_node) + sdw_rt->stream_state = SDW_STATE_DISABLE_STREAM; + } - if (sdw_mstr_bs_rt->mstr == NULL) - continue; + ret = sdw_cfg_bs_params(sdw_mstr_bs, sdw_mstr_bs_rt, false); + if (ret < 0) { + /* TBD: Undo all the computation */ + dev_err(&sdw_mstr->dev, "xport params config failed\n"); + return ret; + } - /* - * Disable channel for slave and - * master on current bank + sel_col = sdw_mstr_bs->col; + sel_row = sdw_mstr_bs->row; + + if ((last_node) && (sdw_mstr->link_sync_mask)) { + + list_for_each_entry(mstr_rt_act, &sdw_rt->mstr_rt_list, + mstr_sdw_node) { + if (mstr_rt_act->mstr == NULL) + break; + /* Get bus structure for master */ + mstr_bs_act = master_to_bus(mstr_rt_act->mstr); + ops = mstr_bs_act->mstr->driver->mstr_port_ops; + /* Run for all mstr_list and + * pre_activate ports */ - ret = sdw_en_dis_mstr_slv(sdw_mstr_bs, - sdw_rt, false); + if (ops->dpn_port_activate_ch_pre) { + ret = ops->dpn_port_activate_ch_pre + (mstr_bs_act->mstr, NULL, 0); + if (ret < 0) + return ret; + } + } + list_for_each_entry(mstr_rt_act, + &sdw_rt->mstr_rt_list, mstr_sdw_node) { + if (mstr_rt_act->mstr == NULL) + break; + + /* Get bus structure for master */ + mstr_bs_act = master_to_bus(mstr_rt_act->mstr); + /* Configure Frame Shape/Switch Bank */ + ret = sdw_configure_frmshp_bnkswtch_mm( + mstr_bs_act, + sel_col, sel_row); if (ret < 0) { /* TBD: Undo all the computation */ - dev_err(&sdw_mstr_bs->mstr->dev, - "Channel dis failed\n"); + dev_err(&sdw_mstr->dev, "bank switch failed\n"); return ret; } - - /* Change stream state to disable */ - sdw_rt->stream_state = SDW_STATE_DISABLE_STREAM; } - ret = sdw_cfg_bs_params(sdw_mstr_bs, - sdw_mstr_bs_rt, false); - if (ret < 0) { - /* TBD: Undo all the computation */ - dev_err(&sdw_mstr_bs->mstr->dev, - "xport params config failed\n"); - return ret; - } + list_for_each_entry(mstr_rt_act, &sdw_rt->mstr_rt_list, + mstr_sdw_node) { - sel_col = sdw_mstr_bs->col; - sel_row = sdw_mstr_bs->row; + if (mstr_rt_act->mstr == NULL) + break; - /* Configure frame shape/Switch Bank */ + /* Get bus structure for master */ + mstr_bs_act = master_to_bus(mstr_rt_act->mstr); + ops = mstr_bs_act->mstr->driver->mstr_port_ops; + + /* Run for all mstr_list and + * post_activate ports + */ + if (ops->dpn_port_activate_ch_post) { + ret = ops->dpn_port_activate_ch_post + (mstr_bs_act->mstr, NULL, 0); + if (ret < 0) + return ret; + } + + } + list_for_each_entry(mstr_rt_act, &sdw_rt->mstr_rt_list, + mstr_sdw_node) { + if (mstr_rt_act->mstr == NULL) + break; + + /* Get bus structure for master */ + mstr_bs_act = master_to_bus(mstr_rt_act->mstr); + ret = sdw_configure_frmshp_bnkswtch_mm_wait( + mstr_bs_act); + } + } + if (!sdw_mstr->link_sync_mask) { + + /* Configure Frame Shape/Switch Bank */ ret = sdw_configure_frmshp_bnkswtch(sdw_mstr_bs, sel_col, sel_row); if (ret < 0) { /* TBD: Undo all the computation */ - dev_err(&sdw_mstr_bs->mstr->dev, - "bank switch failed\n"); + dev_err(&sdw_mstr->dev, "bank switch failed\n"); return ret; } + } + /* Disable all channels enabled on previous bank */ + ret = sdw_dis_chan(sdw_mstr_bs, sdw_mstr_bs_rt); + if (ret < 0) { + /* TBD: Undo all the computation */ + dev_err(&sdw_mstr->dev, "Channel disabled failed\n"); + return ret; + } + } +unprepare_stream: + list_for_each_entry(sdw_mstr_rt, + &sdw_rt->mstr_rt_list, mstr_sdw_node) { + if (sdw_mstr_rt->mstr == NULL) + break; - /* Disable all channels enabled on previous bank */ - ret = sdw_dis_chan(sdw_mstr_bs, sdw_mstr_bs_rt); + + last_rt = list_last_entry(&sdw_rt->mstr_rt_list, + struct sdw_mstr_runtime, mstr_sdw_node); + if (sdw_mstr_rt == last_rt) + last_node = true; + else + last_node = false; + + /* Get bus structure for master */ + sdw_mstr_bs = master_to_bus(sdw_mstr_rt->mstr); + sdw_mstr = sdw_mstr_bs->mstr; + + + sdw_mstr_cap = &sdw_mstr_bs->mstr->mstr_capabilities; + mstr_params = &sdw_mstr_rt->stream_params; + + if ((!unprepare) || + (sdw_rt->stream_state != SDW_STATE_DISABLE_STREAM)) + return 0; + + /* 1. Un-prepare master and slave port */ + list_for_each_entry(sdw_mstr_bs_rt, &sdw_mstr->mstr_rt_list, + mstr_node) { + if (sdw_mstr_bs_rt->mstr == NULL) + continue; + ret = sdw_prep_unprep_mstr_slv(sdw_mstr_bs, + sdw_rt, false); if (ret < 0) { /* TBD: Undo all the computation */ - dev_err(&sdw_mstr_bs->mstr->dev, - "Channel disabled failed\n"); + dev_err(&sdw_mstr->dev, "Ch unprep failed\n"); return ret; } - } - if ((unprepare) && - (SDW_STATE_DISABLE_STREAM == - sdw_rt->stream_state)) { + /* change stream state to unprepare */ + if (last_node) + sdw_rt->stream_state = + SDW_STATE_UNPREPARE_STREAM; + } - /* 1. Un-prepare master and slave port */ - list_for_each_entry(sdw_mstr_bs_rt, - &sdw_mstr->mstr_rt_list, mstr_node) { + /* + * Calculate new bandwidth, frame size + * and total BW required for master controller + */ + sdw_mstr_rt->stream_bw = mstr_params->rate * + mstr_params->channel_count * mstr_params->bps; + stream_frame_size = mstr_params->channel_count * + mstr_params->bps; - if (sdw_mstr_bs_rt->mstr == NULL) - continue; + sdw_mstr_bs->bandwidth -= sdw_mstr_rt->stream_bw; - ret = sdw_prep_unprep_mstr_slv(sdw_mstr_bs, - sdw_rt, false); - if (ret < 0) { - /* TBD: Undo all the computation */ - dev_err(&sdw_mstr_bs->mstr->dev, - "Chan unprep failed\n"); - return ret; - } - - /* change stream state to unprepare */ - sdw_rt->stream_state = - SDW_STATE_UNPREPARE_STREAM; - } + /* Something went wrong in bandwidth calulation */ + if (sdw_mstr_bs->bandwidth < 0) { + dev_err(&sdw_mstr->dev, "BW calculation failed\n"); + return -EINVAL; + } + if (!sdw_mstr_bs->bandwidth) { /* - * Calculate new bandwidth, frame size - * and total BW required for master controller + * Last stream on master should + * return successfully */ - sdw_mstr_rt->stream_bw = mstr_params->rate * - mstr_params->channel_count * mstr_params->bps; - stream_frame_size = mstr_params->channel_count * - mstr_params->bps; + if (last_node) + sdw_rt->stream_state = + SDW_STATE_UNCOMPUTE_STREAM; + continue; + } - sdw_mstr_bs->bandwidth -= sdw_mstr_rt->stream_bw; - /* Something went wrong in bandwidth calulation */ - if (sdw_mstr_bs->bandwidth < 0) { - dev_err(&sdw_mstr_bs->mstr->dev, - "BW calculation failed\n"); - return -EINVAL; - } + ret = sdw_get_clock_frmshp(sdw_mstr_bs, &frame_interval, + &sel_col, &sel_row); + if (ret < 0) { + /* TBD: Undo all the computation */ + dev_err(&sdw_mstr->dev, "clock/frameshape failed\n"); + return ret; + } + + /* Compute new transport params for running streams */ + /* No sorting required here */ + + /* Compute system interval */ + ret = sdw_compute_sys_interval(sdw_mstr_bs, sdw_mstr_cap, + frame_interval); + if (ret < 0) { + /* TBD: Undo all the computation */ + dev_err(&sdw_mstr->dev, "compute SI failed\n"); + return ret; + } + + /* Compute hstart/hstop */ + ret = sdw_compute_hstart_hstop(sdw_mstr_bs, sel_col); + if (ret < 0) { + /* TBD: Undo all the computation */ + dev_err(&sdw_mstr->dev, "compute hstart/hstop fail\n"); + return ret; + } + + /* Compute block offset */ + ret = sdw_compute_blk_subblk_offset(sdw_mstr_bs); + if (ret < 0) { + /* TBD: Undo all the computation */ + dev_err(&sdw_mstr->dev, "compute block offset failed\n"); + return ret; + } + + /* Configure bus params */ + ret = sdw_cfg_bs_params(sdw_mstr_bs, sdw_mstr_bs_rt, true); + if (ret < 0) { + /* TBD: Undo all the computation */ + dev_err(&sdw_mstr->dev, "xport params config failed\n"); + return ret; + } + if ((last_node) && (sdw_mstr->link_sync_mask)) { + list_for_each_entry(mstr_rt_act, &sdw_rt->mstr_rt_list, + mstr_sdw_node) { + + if (mstr_rt_act->mstr == NULL) + break; + + /* Get bus structure for master */ + mstr_bs_act = master_to_bus(mstr_rt_act->mstr); + + ops = mstr_bs_act->mstr->driver->mstr_port_ops; - if (!sdw_mstr_bs->bandwidth) { /* - * Last stream on master should - * return successfully + * Run for all mstr_list and + * pre_activate ports */ - sdw_rt->stream_state = - SDW_STATE_UNCOMPUTE_STREAM; - return 0; + if (ops->dpn_port_activate_ch_pre) { + ret = ops->dpn_port_activate_ch_pre + (mstr_bs_act->mstr, NULL, 0); + if (ret < 0) + return ret; + } } + list_for_each_entry(mstr_rt_act, &sdw_rt->mstr_rt_list, + mstr_sdw_node) { - ret = sdw_get_clock_frmshp(sdw_mstr_bs, - &frame_interval, &sel_col, &sel_row); - if (ret < 0) { - /* TBD: Undo all the computation */ - dev_err(&sdw_mstr_bs->mstr->dev, - "clock/frameshape failed\n"); - return ret; - } + if (mstr_rt_act->mstr == NULL) + break; - /* Compute new transport params for running streams */ - /* No sorting required here */ + /* Get bus structure for master */ + mstr_bs_act = master_to_bus( + mstr_rt_act->mstr); - /* Compute system interval */ - ret = sdw_compute_sys_interval(sdw_mstr_bs, - sdw_mstr_cap, frame_interval); - if (ret < 0) { - /* TBD: Undo all the computation */ - dev_err(&sdw_mstr_bs->mstr->dev, - "compute SI failed\n"); - return ret; + /* Configure Frame Shape/Switch Bank */ + ret = sdw_configure_frmshp_bnkswtch_mm( + mstr_bs_act, + sel_col, sel_row); + if (ret < 0) { + /* TBD: Undo all the computation */ + dev_err(&sdw_mstr->dev, + "bank switch failed\n"); + return ret; + } } - /* Compute hstart/hstop */ - ret = sdw_compute_hstart_hstop(sdw_mstr_bs, sel_col); - if (ret < 0) { - /* TBD: Undo all the computation */ - dev_err(&sdw_mstr_bs->mstr->dev, - "compute hstart/hstop fail\n"); - return ret; - } + list_for_each_entry(mstr_rt_act, &sdw_rt->mstr_rt_list, + mstr_sdw_node) { - /* Compute block offset */ - ret = sdw_compute_blk_subblk_offset(sdw_mstr_bs); - if (ret < 0) { - /* TBD: Undo all the computation */ - dev_err(&sdw_mstr_bs->mstr->dev, - "compute block offset failed\n"); - return ret; - } - /* Configure bus params */ - ret = sdw_cfg_bs_params(sdw_mstr_bs, - sdw_mstr_bs_rt, true); - if (ret < 0) { - /* TBD: Undo all the computation */ - dev_err(&sdw_mstr_bs->mstr->dev, - "xport params config failed\n"); - return ret; + if (mstr_rt_act->mstr == NULL) + break; + + /* Get bus structure for master */ + mstr_bs_act = master_to_bus(mstr_rt_act->mstr); + + ops = mstr_bs_act->mstr->driver->mstr_port_ops; + + /* Run for all mstr_list and + * post_activate ports + */ + if (ops->dpn_port_activate_ch_post) { + ret = ops->dpn_port_activate_ch_post + (mstr_bs_act->mstr, NULL, 0); + if (ret < 0) + return ret; + } } + list_for_each_entry(mstr_rt_act, &sdw_rt->mstr_rt_list, + mstr_sdw_node) { + + if (mstr_rt_act->mstr == NULL) + break; + /* Get bus structure for master */ + mstr_bs_act = master_to_bus(mstr_rt_act->mstr); + ret = sdw_configure_frmshp_bnkswtch_mm_wait( + mstr_bs_act); + } + } + if (!sdw_mstr->link_sync_mask) { /* Configure Frame Shape/Switch Bank */ ret = sdw_configure_frmshp_bnkswtch(sdw_mstr_bs, sel_col, sel_row); if (ret < 0) { /* TBD: Undo all the computation */ - dev_err(&sdw_mstr_bs->mstr->dev, - "bank switch failed\n"); + dev_err(&sdw_mstr->dev, "bank switch failed\n"); return ret; } - /* Change stream state to uncompute */ + } + /* Change stream state to uncompute */ + if (last_node) sdw_rt->stream_state = SDW_STATE_UNCOMPUTE_STREAM; - /* Disable all channels enabled on previous bank */ - ret = sdw_dis_chan(sdw_mstr_bs, sdw_mstr_bs_rt); - if (ret < 0) { - /* TBD: Undo all the computation */ - dev_err(&sdw_mstr_bs->mstr->dev, - "Channel disabled failed\n"); - return ret; - } + /* Disable all channels enabled on previous bank */ + ret = sdw_dis_chan(sdw_mstr_bs, sdw_mstr_bs_rt); + if (ret < 0) { + /* TBD: Undo all the computation */ + dev_err(&sdw_mstr_bs->mstr->dev, + "Channel disabled failed\n"); + return ret; } - } return 0; From ab9b13a6670370d3c9e8cfa5f0af002bef4d79ae Mon Sep 17 00:00:00 2001 From: Hardik Shah Date: Fri, 29 Apr 2016 16:41:26 +0530 Subject: [PATCH 0636/1103] REVERTME:ASoC:CNL: Mark SDW master 1 and 2 as aggregated. This patch is to to test the aggregation. Mark the master1 and master2 as aggregated Change-Id: I0fa73fdf12bef071f0054b8844105eac1d698638 Signed-off-by: Hardik Shah Reviewed-on: --- sound/soc/intel/skylake/cnl-sst.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/sound/soc/intel/skylake/cnl-sst.c b/sound/soc/intel/skylake/cnl-sst.c index e4d531680a5a..0caf9bf070a3 100644 --- a/sound/soc/intel/skylake/cnl-sst.c +++ b/sound/soc/intel/skylake/cnl-sst.c @@ -509,8 +509,7 @@ static int skl_register_sdw_masters(struct device *dev, struct skl_sst *dsp, struct cnl_sdw_data *p_data; int ret = 0, i, j; /* TODO: This number 4 should come from ACPI */ -#ifdef CONFIG_SDW_MAXIM_SLAVE - +#if defined(CONFIG_SDW_MAXIM_SLAVE) || defined(CONFIG_SND_SOC_MXFPGA) dsp->num_sdw_controllers = 3; #else dsp->num_sdw_controllers = 4; @@ -574,12 +573,19 @@ static int skl_register_sdw_masters(struct device *dev, struct skl_sst *dsp, SDW_PORT_BLK_PKG_MODE_BLK_PER_PORT | SDW_PORT_BLK_PKG_MODE_BLK_PER_CH; } + master[i].link_sync_mask = 0x0; switch (i) { case 0: p_data->sdw_regs = mmio_base + CNL_SDW_LINK_0_BASE; +#ifdef CONFIG_SND_SOC_MXFPGA + master[i].link_sync_mask = 0x1; +#endif break; case 1: p_data->sdw_regs = mmio_base + CNL_SDW_LINK_1_BASE; +#ifdef CONFIG_SND_SOC_MXFPGA + master[i].link_sync_mask = 0x2; +#endif break; case 2: p_data->sdw_regs = mmio_base + CNL_SDW_LINK_2_BASE; From 736ed1cbef46db676e99bfcebcf4b9e7816affcc Mon Sep 17 00:00:00 2001 From: Hardik Shah Date: Mon, 2 May 2016 00:14:23 +0530 Subject: [PATCH 0637/1103] ASoC:CNL: Update capabilities fields of SDW master Capabilities fields are missing while updating master controller update the fields based on hardware/topology capability Change-Id: I65b9b6ee77a61accc21fb34fa7db8b6b15e5e431 Signed-off-by: Hardik Shah Reviewed-on: --- sound/soc/intel/skylake/cnl-sst.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/sound/soc/intel/skylake/cnl-sst.c b/sound/soc/intel/skylake/cnl-sst.c index 0caf9bf070a3..f70d70d0822a 100644 --- a/sound/soc/intel/skylake/cnl-sst.c +++ b/sound/soc/intel/skylake/cnl-sst.c @@ -538,7 +538,8 @@ static int skl_register_sdw_masters(struct device *dev, struct skl_sst *dsp, master[i].retries = CNL_SDW_MAX_CMD_RETRIES; m_cap->base_clk_freq = 9.6 * 1000 * 1000; strcpy(master[i].name, "cnl_sdw_mstr"); - m_cap->highphy_capable = 0; + m_cap->highphy_capable = false; + m_cap->monitor_handover_supported = false; m_cap->sdw_dp0_supported = 1; m_cap->num_data_ports = CNL_SDW_MAX_PORTS; dp0_cap->max_word_length = 32; From cb4f4856014fcc6e9865c999460500b5816c3e81 Mon Sep 17 00:00:00 2001 From: Hardik Shah Date: Mon, 2 May 2016 15:09:22 +0530 Subject: [PATCH 0638/1103] ASoC:CNL: Add support for aggregated gateways. Gateway Link copier needs to be programmed differently for aggregated case. Add support for SoundWire aggregated gateway programming. Change-Id: I91925fbeff68b8ecbb1dd13591f6a185a1cdd8d8 Signed-off-by: Hardik Shah Reviewed-on: Signed-off-by: Guneshwor Singh --- sound/soc/intel/skylake/skl-messages.c | 5 ++++- sound/soc/intel/skylake/skl-topology.h | 12 ++++++++++++ 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/sound/soc/intel/skylake/skl-messages.c b/sound/soc/intel/skylake/skl-messages.c index f63b50e43257..a9eaa4aed643 100644 --- a/sound/soc/intel/skylake/skl-messages.c +++ b/sound/soc/intel/skylake/skl-messages.c @@ -566,7 +566,10 @@ static u32 skl_get_node_id(struct skl_sst *ctx, (SKL_CONN_SOURCE == mconfig->hw_conn_type) ? SKL_DMA_SDW_LINK_OUTPUT_CLASS : SKL_DMA_SDW_LINK_INPUT_CLASS; - node_id.node.vindex = mconfig->sdw_stream_num; + if (mconfig->sdw_agg_enable) + node_id.node.vindex = 0x50; + else + node_id.node.vindex = mconfig->sdw_stream_num; break; default: diff --git a/sound/soc/intel/skylake/skl-topology.h b/sound/soc/intel/skylake/skl-topology.h index 353c90cbd336..44e4109ba4dc 100644 --- a/sound/soc/intel/skylake/skl-topology.h +++ b/sound/soc/intel/skylake/skl-topology.h @@ -383,6 +383,16 @@ struct skl_module { struct skl_module_iface formats[SKL_MAX_MODULE_FORMATS]; }; +struct skl_sdw_agg_data { + int alh_stream_num; + int ch_mask; +}; + +struct skl_sdw_aggregation { + int num_masters; + struct skl_sdw_agg_data agg_data[4]; +}; + struct skl_module_cfg { u8 guid[16]; struct skl_module_inst_id id; @@ -418,6 +428,8 @@ struct skl_module_cfg { u32 dma_buffer_size; /* in milli seconds */ u8 pdi_type; u32 sdw_stream_num; + bool sdw_agg_enable; + struct skl_sdw_aggregation sdw_agg; struct skl_module_pin *m_in_pin; struct skl_module_pin *m_out_pin; enum skl_module_type m_type; From b53ee8191b7e4951e8cd436e6b0d988f0b563e79 Mon Sep 17 00:00:00 2001 From: Hardik Shah Date: Mon, 2 May 2016 15:12:59 +0530 Subject: [PATCH 0639/1103] ASoC:CNL: Add DAIS for SoundWire masters. Add new DAIs for SoundWire master for aggregation. Add DAI IDs to find the master controller from DAI. Earlier DAI was found from the copier vbus, but with aggregation, copier is linked to multiple DAIs which are aggregated. So add be_ids range to idendify SDW master controller from DAIs. Change-Id: Ida7bd5893e073c3ec5d7ab7dda0c94306c42051b Signed-off-by: Hardik Shah Reviewed-on: --- sound/soc/intel/skylake/skl-pcm.c | 46 ++++++++++++++++++++++++++- sound/soc/intel/skylake/skl-sdw-pcm.h | 7 ++++ 2 files changed, 52 insertions(+), 1 deletion(-) diff --git a/sound/soc/intel/skylake/skl-pcm.c b/sound/soc/intel/skylake/skl-pcm.c index da4461762c4e..f27b7188b7c8 100644 --- a/sound/soc/intel/skylake/skl-pcm.c +++ b/sound/soc/intel/skylake/skl-pcm.c @@ -1096,6 +1096,7 @@ static struct snd_soc_dai_driver skl_platform_dai[] = { * should be coming from CLT based on endpoints to be supported */ .name = "SDW Pin", + .id = SDW_BE_DAI_ID_MSTR0, .ops = &skl_sdw_dai_ops, .playback = { .stream_name = "SDW Tx", @@ -1118,6 +1119,7 @@ static struct snd_soc_dai_driver skl_platform_dai[] = { */ .name = "SDW PDM Pin", .ops = &skl_sdw_dai_ops, + .id = SDW_BE_DAI_ID_MSTR0 + 1, .capture = { .stream_name = "SDW Rx1", .channels_min = HDA_MONO, @@ -1125,8 +1127,50 @@ static struct snd_soc_dai_driver skl_platform_dai[] = { .rates = SNDRV_PCM_RATE_48000, .formats = SNDRV_PCM_FMTBIT_S16_LE, }, +}, +{ + /* Currently adding 1 playback and 1 capture pin, ideally it + * should be coming from CLT based on endpoints to be supported + */ + .name = "SDW1 Pin", + .id = SDW_BE_DAI_ID_MSTR1, + .ops = &skl_sdw_dai_ops, + .playback = { + .stream_name = "SDW1 Tx", + .channels_min = HDA_STEREO, + .channels_max = HDA_STEREO, + .rates = SNDRV_PCM_RATE_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + }, + .capture = { + .stream_name = "SDW1 Rx", + .channels_min = HDA_STEREO, + .channels_max = HDA_STEREO, + .rates = SNDRV_PCM_RATE_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + }, - +}, +{ + /* Currently adding 1 playback and 1 capture pin, ideally it + * should be coming from CLT based on endpoints to be supported + */ + .name = "SDW3 Pin", + .ops = &skl_sdw_dai_ops, + .playback = { + .stream_name = "SDW3 Tx", + .channels_min = HDA_STEREO, + .channels_max = HDA_STEREO, + .rates = SNDRV_PCM_RATE_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + }, + .capture = { + .stream_name = "SDW3 Rx", + .channels_min = HDA_STEREO, + .channels_max = HDA_STEREO, + .rates = SNDRV_PCM_RATE_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + }, }, }; diff --git a/sound/soc/intel/skylake/skl-sdw-pcm.h b/sound/soc/intel/skylake/skl-sdw-pcm.h index ab1314a6f408..7166ef9992ab 100644 --- a/sound/soc/intel/skylake/skl-sdw-pcm.h +++ b/sound/soc/intel/skylake/skl-sdw-pcm.h @@ -24,6 +24,13 @@ #include #include +#define SDW_BE_DAI_ID_BASE 256 +#define SDW_BE_DAI_ID_MSTR0 256 +#define SDW_BE_DAI_ID_MSTR1 (SDW_BE_DAI_ID_MSTR0 + 32) +#define SDW_BE_DAI_ID_MSTR2 (SDW_BE_DAI_ID_MSTR1 + 32) +#define SDW_BE_DAI_ID_MSTR3 (SDW_BE_DAI_ID_MSTR2 + 32) + + int cnl_sdw_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *dai); int cnl_sdw_hw_params(struct snd_pcm_substream *substream, From 678c517a55644953ee390deb838064803f28e43e Mon Sep 17 00:00:00 2001 From: Jeeja KP Date: Wed, 16 Sep 2015 01:56:54 +0530 Subject: [PATCH 0640/1103] ASoC: core: Adds support for DSP loopback dai link Only codec-codec dai link is supported. This patch adds support for dsp loopback dai link Change-Id: I6163e5e572116aeac026ee1815708f7342ae1e3f Signed-off-by: Jeeja KP --- include/sound/soc.h | 3 +++ sound/soc/soc-core.c | 20 ++++++++++++++++---- 2 files changed, 19 insertions(+), 4 deletions(-) diff --git a/include/sound/soc.h b/include/sound/soc.h index 41cec42fb456..c32c6aaca529 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -920,6 +920,9 @@ struct snd_soc_dai_link { const struct snd_soc_pcm_stream *params; unsigned int num_params; + /* flag to create dsp loopback link */ + unsigned int dsp_loopback:1; + unsigned int dai_fmt; /* format to set on init */ enum snd_soc_dpcm_trigger trigger[2]; /* trigger type for DPCM */ diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index 29b42f8f1eff..2ecf3442c965 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -1444,8 +1444,14 @@ static int soc_link_dai_widgets(struct snd_soc_card *card, dev_warn(card->dev, "ASoC: Multiple codecs not supported yet\n"); /* link the DAI widgets */ - sink = codec_dai->playback_widget; - source = cpu_dai->capture_widget; + if (!dai_link->dsp_loopback) { + sink = codec_dai->playback_widget; + source = cpu_dai->capture_widget; + } else { + sink = codec_dai->playback_widget; + source = cpu_dai->playback_widget; + } + if (sink && source) { ret = snd_soc_dapm_new_pcm(card, rtd, dai_link->params, dai_link->num_params, @@ -1457,8 +1463,14 @@ static int soc_link_dai_widgets(struct snd_soc_card *card, } } - sink = cpu_dai->playback_widget; - source = codec_dai->capture_widget; + if (!dai_link->dsp_loopback) { + sink = cpu_dai->playback_widget; + source = codec_dai->capture_widget; + } else { + sink = cpu_dai->capture_widget; + source = codec_dai->capture_widget; + } + if (sink && source) { ret = snd_soc_dapm_new_pcm(card, rtd, dai_link->params, dai_link->num_params, From c0293950c3555605d31cc46cd241d5a49cab31a0 Mon Sep 17 00:00:00 2001 From: Omair M Abdullah Date: Fri, 18 Sep 2015 20:23:30 +0530 Subject: [PATCH 0641/1103] [WORKAROUND] ASoC: dapm: fix stream directions for dsp_loopback links DSP loopback links have directions reversed for the source and sink widgets. Signed-off-by: Omair M Abdullah --- sound/soc/soc-dapm.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index 461d951917c0..7e3ec95f985f 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -3716,7 +3716,8 @@ static int snd_soc_dai_link_event(struct snd_soc_dapm_widget *w, switch (event) { case SND_SOC_DAPM_PRE_PMU: - substream.stream = SNDRV_PCM_STREAM_CAPTURE; + /*substream.stream = SNDRV_PCM_STREAM_CAPTURE;*/ + substream.stream = SNDRV_PCM_STREAM_PLAYBACK; if (source->driver->ops->startup) { ret = source->driver->ops->startup(&substream, source); if (ret < 0) { @@ -3730,7 +3731,8 @@ static int snd_soc_dai_link_event(struct snd_soc_dapm_widget *w, if (ret < 0) goto out; - substream.stream = SNDRV_PCM_STREAM_PLAYBACK; + /*substream.stream = SNDRV_PCM_STREAM_PLAYBACK;*/ + substream.stream = SNDRV_PCM_STREAM_CAPTURE; if (sink->driver->ops->startup) { ret = sink->driver->ops->startup(&substream, sink); if (ret < 0) { From 8831d9690fbcc07849484205e1416561970da731 Mon Sep 17 00:00:00 2001 From: Omair Mohammed Abdullah Date: Mon, 10 Nov 2014 21:52:55 +0530 Subject: [PATCH 0642/1103] ASoC: utils: add inputs and outputs to dummy codec Add a dummy input and a dummy output to the codec, so that the platform side widgets can be triggered if a backend uses a dummy codec. Make the dummy codec stream names explicit to avoid confusion. Change-Id: I3891e7b670a413c74d71aae1feed9f04e00041e3 Tracked-On: Signed-off-by: Omair Mohammed Abdullah Reviewed-by: Koul, Vinod Tested-by: Koul, Vinod Signed-off-by: Dharageswari.R Reviewed-on: Reviewed-by: Babu, Ramesh Tested-by: Babu, Ramesh --- sound/soc/soc-utils.c | 26 ++++++++++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/sound/soc/soc-utils.c b/sound/soc/soc-utils.c index e0c93496c0cd..8503a36beff7 100644 --- a/sound/soc/soc-utils.c +++ b/sound/soc/soc-utils.c @@ -282,7 +282,28 @@ static const struct snd_soc_component_driver dummy_platform = { .ops = &dummy_dma_ops, }; +static struct snd_soc_dapm_widget dapm_widgets[] = { + SND_SOC_DAPM_INPUT("Dummy Input"), + SND_SOC_DAPM_OUTPUT("Dummy Output"), +}; + +static struct snd_soc_dapm_route intercon[] = { + { "Dummy Output", NULL, "Dummy Playback"}, + { "Dummy Capture", NULL, "Dummy Input"}, +}; + +static int dummy_codec_probe(struct snd_soc_component *codec) +{ + struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(codec); + + snd_soc_dapm_new_controls(dapm, dapm_widgets, + ARRAY_SIZE(dapm_widgets)); + snd_soc_dapm_add_routes(dapm, intercon, ARRAY_SIZE(intercon)); + return 0; +} + static const struct snd_soc_component_driver dummy_codec = { + .probe = dummy_codec_probe, .idle_bias_on = 1, .use_pmdown_time = 1, .endianness = 1, @@ -308,17 +329,18 @@ static const struct snd_soc_component_driver dummy_codec = { * which should be modelled. And the data flow graph also should be modelled * using DAPM. */ + static struct snd_soc_dai_driver dummy_dai = { .name = "snd-soc-dummy-dai", .playback = { - .stream_name = "Playback", + .stream_name = "Dummy Playback", .channels_min = 1, .channels_max = 384, .rates = STUB_RATES, .formats = STUB_FORMATS, }, .capture = { - .stream_name = "Capture", + .stream_name = "Dummy Capture", .channels_min = 1, .channels_max = 384, .rates = STUB_RATES, From 7a8b559a54e1e382c01af99e08e77020f8732edd Mon Sep 17 00:00:00 2001 From: Guneshwor Singh Date: Tue, 16 Aug 2016 16:26:31 +0530 Subject: [PATCH 0643/1103] ASoC: core: Do not return for dummy codec in soc_probe_component Change-Id: I1bcf17ab9731675e4586382054e5d44645cb18f9 Signed-off-by: Guneshwor Singh --- sound/soc/soc-core.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index 2ecf3442c965..f41ff22e1317 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -1242,10 +1242,10 @@ static int soc_probe_component(struct snd_soc_card *card, struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component); struct snd_soc_dai *dai; int ret; - +#if 0 if (!strcmp(component->name, "snd-soc-dummy")) return 0; - +#endif if (component->card) { if (component->card != card) { dev_err(component->dev, From 6d00aa22d05ab979ad292e83f27d41e56cc00157 Mon Sep 17 00:00:00 2001 From: Guneshwor Singh Date: Wed, 17 Aug 2016 13:17:48 +0530 Subject: [PATCH 0644/1103] ASoC: SKL: Fix ch_cfg when fixup is applied Change-Id: I3d1198ea3ff0120f28736a7e7a81029887164634 Signed-off-by: Guneshwor Singh --- sound/soc/intel/skylake/skl-topology.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/sound/soc/intel/skylake/skl-topology.c b/sound/soc/intel/skylake/skl-topology.c index 413a72dada50..80934faa70d8 100644 --- a/sound/soc/intel/skylake/skl-topology.c +++ b/sound/soc/intel/skylake/skl-topology.c @@ -256,6 +256,11 @@ static void skl_tplg_update_params(struct skl_module_fmt *fmt, if (fixup & SKL_CH_FIXUP_MASK) { fmt->channels = params->ch; skl_tplg_update_chmap(fmt, fmt->channels); + if (fmt->channels == 1) + fmt->ch_cfg = SKL_CH_CFG_MONO; + else if (fmt->channels == 2) + fmt->ch_cfg = SKL_CH_CFG_STEREO; + } if (fixup & SKL_FMT_FIXUP_MASK) { fmt->valid_bit_depth = skl_get_bit_depth(params->s_fmt); From f108bba23fd8ac68fe7af56177cb8f55dfbb1b9b Mon Sep 17 00:00:00 2001 From: Vinod Koul Date: Mon, 31 Aug 2015 14:05:10 +0530 Subject: [PATCH 0645/1103] ASoC: Intel: Skylake: Add NHLT override control For debugging purposes we may want to not use the BIOS values and test our own values, so allow the override by adding a control file for override method Signed-off-by: Vinod Koul Signed-off-by: Guneshwor Singh --- sound/soc/intel/skylake/skl-debug.c | 65 +++++++++++++++++++++++++++++ sound/soc/intel/skylake/skl.h | 1 + 2 files changed, 66 insertions(+) diff --git a/sound/soc/intel/skylake/skl-debug.c b/sound/soc/intel/skylake/skl-debug.c index 5d7ac2ee7a3c..09321ba98e78 100644 --- a/sound/soc/intel/skylake/skl-debug.c +++ b/sound/soc/intel/skylake/skl-debug.c @@ -33,6 +33,7 @@ struct skl_debug { struct dentry *fs; struct dentry *modules; + struct dentry *nhlt; u8 fw_read_buff[FW_REG_BUF]; }; @@ -221,6 +222,61 @@ static const struct file_operations soft_regs_ctrl_fops = { .llseek = default_llseek, }; +static ssize_t nhlt_control_read(struct file *file, + char __user *user_buf, size_t count, loff_t *ppos) +{ + struct skl_debug *d = file->private_data; + char *state; + + state = d->skl->nhlt_override ? "enable\n" : "disable\n"; + return simple_read_from_buffer(user_buf, count, ppos, + state, strlen(state)); +} + +static ssize_t nhlt_control_write(struct file *file, + const char __user *user_buf, size_t count, loff_t *ppos) +{ + struct skl_debug *d = file->private_data; + char buf[16]; + int len = min(count, (sizeof(buf) - 1)); + + + if (copy_from_user(buf, user_buf, len)) + return -EFAULT; + buf[len] = 0; + + if (!strncmp(buf, "enable\n", len)) + d->skl->nhlt_override = true; + else if (!strncmp(buf, "disable\n", len)) + d->skl->nhlt_override = false; + else + return -EINVAL; + + /* Userspace has been fiddling around behind the kernel's back */ + add_taint(TAINT_USER, LOCKDEP_NOW_UNRELIABLE); + + return len; +} + +static const struct file_operations ssp_cntrl_nhlt_fops = { + .open = simple_open, + .read = nhlt_control_read, + .write = nhlt_control_write, + .llseek = default_llseek, +}; + +static int skl_init_nhlt(struct skl_debug *d) +{ + if (!debugfs_create_file("control", + 0644, d->nhlt, + d, &ssp_cntrl_nhlt_fops)) { + dev_err(d->dev, "nhlt control debugfs init failed\n"); + return -EIO; + } + + return 0; +} + struct skl_debug *skl_debugfs_init(struct skl *skl) { struct skl_debug *d; @@ -253,6 +309,15 @@ struct skl_debug *skl_debugfs_init(struct skl *skl) goto err; } + /* now create the NHLT dir */ + d->nhlt = debugfs_create_dir("nhlt", d->fs); + if (IS_ERR(d->nhlt) || !d->nhlt) { + dev_err(&skl->pci->dev, "nhlt debugfs create failed\n"); + return NULL; + } + + skl_init_nhlt(d); + return d; err: diff --git a/sound/soc/intel/skylake/skl.h b/sound/soc/intel/skylake/skl.h index 78aa8bdcb619..28c62dda07db 100644 --- a/sound/soc/intel/skylake/skl.h +++ b/sound/soc/intel/skylake/skl.h @@ -103,6 +103,7 @@ struct skl { bool use_tplg_pcm; struct skl_fw_config cfg; struct snd_soc_acpi_mach *mach; + bool nhlt_override; }; #define skl_to_bus(s) (&(s)->hbus) From 80dc33be91b379bc7a9c453d4daca59c2906703e Mon Sep 17 00:00:00 2001 From: Vinod Koul Date: Mon, 31 Aug 2015 14:12:14 +0530 Subject: [PATCH 0646/1103] ASoC: Intel: Skylake: Add debugfs NHLT ssp override Add debugfs entries for reading and writing SSP blobs which driver can use to program DSP Signed-off-by: Vinod Koul --- sound/soc/intel/skylake/skl-debug.c | 92 +++++++++++++++++++++++++++-- 1 file changed, 88 insertions(+), 4 deletions(-) diff --git a/sound/soc/intel/skylake/skl-debug.c b/sound/soc/intel/skylake/skl-debug.c index 09321ba98e78..8485e2e4aa90 100644 --- a/sound/soc/intel/skylake/skl-debug.c +++ b/sound/soc/intel/skylake/skl-debug.c @@ -22,10 +22,17 @@ #include "skl-topology.h" #include "../common/sst-dsp.h" #include "../common/sst-dsp-priv.h" +#include "skl-nhlt.h" #define MOD_BUF PAGE_SIZE #define FW_REG_BUF PAGE_SIZE #define FW_REG_SIZE 0x60 +#define MAX_SSP 4 + +struct nhlt_blob { + size_t size; + struct nhlt_specific_cfg *cfg; +}; struct skl_debug { struct skl *skl; @@ -35,6 +42,62 @@ struct skl_debug { struct dentry *modules; struct dentry *nhlt; u8 fw_read_buff[FW_REG_BUF]; + struct nhlt_blob ssp_blob[MAX_SSP]; +}; + +static ssize_t nhlt_read(struct file *file, char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct nhlt_blob *blob = file->private_data; + + if (!blob->cfg) + return -EIO; + + return simple_read_from_buffer(user_buf, count, ppos, + blob->cfg, blob->size); +} + +static ssize_t nhlt_write(struct file *file, + const char __user *user_buf, size_t count, loff_t *ppos) +{ + struct nhlt_blob *blob = file->private_data; + struct nhlt_specific_cfg *new_cfg; + ssize_t written; + size_t size = blob->size; + + if (!blob->cfg) { + /* allocate mem for blob */ + blob->cfg = kzalloc(count, GFP_KERNEL); + if (!blob->cfg) + return -ENOMEM; + size = count; + } else if (blob->size < count) { + /* size if different, so relloc */ + new_cfg = krealloc(blob->cfg, count, GFP_KERNEL); + if (!new_cfg) + return -ENOMEM; + size = count; + blob->cfg = new_cfg; + } + + written = simple_write_to_buffer(blob->cfg, size, ppos, + user_buf, count); + blob->size = written; + + /* Userspace has been fiddling around behind the kernel's back */ + add_taint(TAINT_USER, LOCKDEP_NOW_UNRELIABLE); + + print_hex_dump(KERN_DEBUG, "Debugfs Blob:", DUMP_PREFIX_OFFSET, 8, 4, + blob->cfg, blob->size, false); + + return written; +} + +static const struct file_operations nhlt_fops = { + .open = simple_open, + .read = nhlt_read, + .write = nhlt_write, + .llseek = default_llseek, }; static ssize_t skl_print_pins(struct skl_module_pin *m_pin, char *buf, @@ -166,7 +229,6 @@ static const struct file_operations mcfg_fops = { .llseek = default_llseek, }; - void skl_debug_init_module(struct skl_debug *d, struct snd_soc_dapm_widget *w, struct skl_module_cfg *mconfig) @@ -222,6 +284,15 @@ static const struct file_operations soft_regs_ctrl_fops = { .llseek = default_llseek, }; +static void skl_exit_nhlt(struct skl_debug *d) +{ + int i; + + /* free blob memory, if allocated */ + for (i = 0; i < MAX_SSP; i++) + kfree(d->ssp_blob[i].cfg); +} + static ssize_t nhlt_control_read(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { @@ -245,12 +316,14 @@ static ssize_t nhlt_control_write(struct file *file, return -EFAULT; buf[len] = 0; - if (!strncmp(buf, "enable\n", len)) + if (!strncmp(buf, "enable\n", len)) { d->skl->nhlt_override = true; - else if (!strncmp(buf, "disable\n", len)) + } else if (!strncmp(buf, "disable\n", len)) { d->skl->nhlt_override = false; - else + skl_exit_nhlt(d); + } else { return -EINVAL; + } /* Userspace has been fiddling around behind the kernel's back */ add_taint(TAINT_USER, LOCKDEP_NOW_UNRELIABLE); @@ -267,6 +340,9 @@ static const struct file_operations ssp_cntrl_nhlt_fops = { static int skl_init_nhlt(struct skl_debug *d) { + int i; + char name[12]; + if (!debugfs_create_file("control", 0644, d->nhlt, d, &ssp_cntrl_nhlt_fops)) { @@ -274,6 +350,14 @@ static int skl_init_nhlt(struct skl_debug *d) return -EIO; } + for (i = 0; i < MAX_SSP; i++) { + snprintf(name, (sizeof(name)-1), "ssp%d", i); + if (!debugfs_create_file(name, + 0644, d->nhlt, + &d->ssp_blob[i], &nhlt_fops)) + dev_err(d->dev, "%s: debugfs init failed\n", name); + } + return 0; } From 3b96b2a755dc181652a3c49122901d3faec6347d Mon Sep 17 00:00:00 2001 From: Vinod Koul Date: Wed, 26 Aug 2015 13:13:56 +0530 Subject: [PATCH 0647/1103] ASoC: Intel: Skylake: Add debugfs NHLT dmic override Add debugfs entries for reading and writing DMIC blobs which driver can use to program DSP Signed-off-by: Vinod Koul --- sound/soc/intel/skylake/skl-debug.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/sound/soc/intel/skylake/skl-debug.c b/sound/soc/intel/skylake/skl-debug.c index 8485e2e4aa90..9266e478d3c6 100644 --- a/sound/soc/intel/skylake/skl-debug.c +++ b/sound/soc/intel/skylake/skl-debug.c @@ -43,6 +43,7 @@ struct skl_debug { struct dentry *nhlt; u8 fw_read_buff[FW_REG_BUF]; struct nhlt_blob ssp_blob[MAX_SSP]; + struct nhlt_blob dmic_blob; }; static ssize_t nhlt_read(struct file *file, char __user *user_buf, @@ -358,6 +359,11 @@ static int skl_init_nhlt(struct skl_debug *d) dev_err(d->dev, "%s: debugfs init failed\n", name); } + if (!debugfs_create_file("dmic", 0644, + d->nhlt, &d->dmic_blob, + &nhlt_fops)) + dev_err(d->dev, "%s: debugfs init failed\n", name); + return 0; } From 1184ccd34e32e81ee404336fca772f94747fd5b9 Mon Sep 17 00:00:00 2001 From: Vinod Koul Date: Wed, 26 Aug 2015 13:13:56 +0530 Subject: [PATCH 0648/1103] ASoC: Intel: Skylake: Read blobs from debugfs on override Add API to read blobs from debugfs when override is enabled and use that API when sending IPCs to DSP Signed-off-by: Vinod Koul Signed-off-by: Jeeja KP --- sound/soc/intel/skylake/skl-debug.c | 21 +++++++++++++++++++++ sound/soc/intel/skylake/skl-topology.c | 14 +++++++++++++- sound/soc/intel/skylake/skl.h | 8 ++++++++ 3 files changed, 42 insertions(+), 1 deletion(-) diff --git a/sound/soc/intel/skylake/skl-debug.c b/sound/soc/intel/skylake/skl-debug.c index 9266e478d3c6..9ae01285f399 100644 --- a/sound/soc/intel/skylake/skl-debug.c +++ b/sound/soc/intel/skylake/skl-debug.c @@ -46,6 +46,27 @@ struct skl_debug { struct nhlt_blob dmic_blob; }; +struct nhlt_specific_cfg +*skl_nhlt_get_debugfs_blob(struct skl_debug *d, u8 link_type, u32 instance) +{ + switch (link_type) { + case NHLT_LINK_DMIC: + return d->dmic_blob.cfg; + + case NHLT_LINK_SSP: + if (instance >= MAX_SSP) + return NULL; + + return d->ssp_blob[instance].cfg; + + default: + break; + } + + dev_err(d->dev, "NHLT debugfs query failed\n"); + return NULL; +} + static ssize_t nhlt_read(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { diff --git a/sound/soc/intel/skylake/skl-topology.c b/sound/soc/intel/skylake/skl-topology.c index 80934faa70d8..e62a1eb57f22 100644 --- a/sound/soc/intel/skylake/skl-topology.c +++ b/sound/soc/intel/skylake/skl-topology.c @@ -1888,10 +1888,22 @@ static int skl_tplg_be_fill_pipe_params(struct snd_soc_dai *dai, return 0; /* update the blob based on virtual bus_id*/ - cfg = skl_get_ep_blob(skl, mconfig->vbus_id, link_type, + if (!skl->nhlt_override) { + cfg = skl_get_ep_blob(skl, mconfig->vbus_id, link_type, params->s_fmt, params->ch, params->s_freq, params->stream, dev_type); + } else { + dev_warn(dai->dev, "Querying NHLT blob from Debugfs!!!!\n"); + cfg = skl_nhlt_get_debugfs_blob(skl->debugfs, + link_type, mconfig->vbus_id); + if (cfg->size > HDA_SST_CFG_MAX) { + dev_err(dai->dev, "NHLT debugfs blob is vv large\n"); + dev_err(dai->dev, "First word is size in blob!!!\n"); + dev_err(dai->dev, "Recieved size %d\n", cfg->size); + return -EIO; + } + } if (cfg) { mconfig->formats_config.caps_size = cfg->size; mconfig->formats_config.caps = (u32 *) &cfg->caps; diff --git a/sound/soc/intel/skylake/skl.h b/sound/soc/intel/skylake/skl.h index 28c62dda07db..05561be0efe6 100644 --- a/sound/soc/intel/skylake/skl.h +++ b/sound/soc/intel/skylake/skl.h @@ -165,6 +165,9 @@ struct skl_debug *skl_debugfs_init(struct skl *skl); void skl_debug_init_module(struct skl_debug *d, struct snd_soc_dapm_widget *w, struct skl_module_cfg *mconfig); +struct nhlt_specific_cfg +*skl_nhlt_get_debugfs_blob(struct skl_debug *d, u8 link_type, u32 instance); + #else static inline struct skl_debug *skl_debugfs_init(struct skl *skl) { @@ -174,6 +177,11 @@ static inline void skl_debug_init_module(struct skl_debug *d, struct snd_soc_dapm_widget *w, struct skl_module_cfg *mconfig) {} +static inline struct nhlt_specific_cfg +*skl_nhlt_get_debugfs_blob(struct skl_debug *d, u8 link_type, u32 instance) +{ + return NULL; +} #endif #endif /* __SOUND_SOC_SKL_H */ From 59f0d4adf7a19727fd13075686ba41f4a446b123 Mon Sep 17 00:00:00 2001 From: Jeeja KP Date: Tue, 8 Sep 2015 22:16:08 +0530 Subject: [PATCH 0649/1103] ASoC: Intel: Skylake: NHLT override, check cfg size in debugfs blob write When blob is updated, check the cfg size. If cfg size exceeds maximum, return error from debugfs in write. Removed check in update_params(), we will pass the pointer to cfg param instead of memcpy. Signed-off-by: Jeeja KP --- sound/soc/intel/skylake/skl-debug.c | 3 +++ sound/soc/intel/skylake/skl-topology.c | 6 ------ 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/sound/soc/intel/skylake/skl-debug.c b/sound/soc/intel/skylake/skl-debug.c index 9ae01285f399..85850b495eb4 100644 --- a/sound/soc/intel/skylake/skl-debug.c +++ b/sound/soc/intel/skylake/skl-debug.c @@ -87,6 +87,9 @@ static ssize_t nhlt_write(struct file *file, ssize_t written; size_t size = blob->size; + if (count > 2 * HDA_SST_CFG_MAX) + return -EIO; + if (!blob->cfg) { /* allocate mem for blob */ blob->cfg = kzalloc(count, GFP_KERNEL); diff --git a/sound/soc/intel/skylake/skl-topology.c b/sound/soc/intel/skylake/skl-topology.c index e62a1eb57f22..7c94fc8e5a0c 100644 --- a/sound/soc/intel/skylake/skl-topology.c +++ b/sound/soc/intel/skylake/skl-topology.c @@ -1897,12 +1897,6 @@ static int skl_tplg_be_fill_pipe_params(struct snd_soc_dai *dai, dev_warn(dai->dev, "Querying NHLT blob from Debugfs!!!!\n"); cfg = skl_nhlt_get_debugfs_blob(skl->debugfs, link_type, mconfig->vbus_id); - if (cfg->size > HDA_SST_CFG_MAX) { - dev_err(dai->dev, "NHLT debugfs blob is vv large\n"); - dev_err(dai->dev, "First word is size in blob!!!\n"); - dev_err(dai->dev, "Recieved size %d\n", cfg->size); - return -EIO; - } } if (cfg) { mconfig->formats_config.caps_size = cfg->size; From 9a60a81cecf58954cd16e8d0ca8dd6f6f7612f34 Mon Sep 17 00:00:00 2001 From: Omair M Abdullah Date: Tue, 15 Sep 2015 17:46:57 +0530 Subject: [PATCH 0650/1103] ASoC: Intel: Skylake: add ssp blob override support for capture Capture on SSP can have different blob, so add support for different blobs for PB/CAP on same SSP. Signed-off-by: Omair M Abdullah --- sound/soc/intel/skylake/skl-debug.c | 19 ++++++++++++++----- sound/soc/intel/skylake/skl-topology.c | 3 ++- sound/soc/intel/skylake/skl.h | 7 ++++--- 3 files changed, 20 insertions(+), 9 deletions(-) diff --git a/sound/soc/intel/skylake/skl-debug.c b/sound/soc/intel/skylake/skl-debug.c index 85850b495eb4..52b65bb096ab 100644 --- a/sound/soc/intel/skylake/skl-debug.c +++ b/sound/soc/intel/skylake/skl-debug.c @@ -42,12 +42,13 @@ struct skl_debug { struct dentry *modules; struct dentry *nhlt; u8 fw_read_buff[FW_REG_BUF]; - struct nhlt_blob ssp_blob[MAX_SSP]; + struct nhlt_blob ssp_blob[2*MAX_SSP]; struct nhlt_blob dmic_blob; }; struct nhlt_specific_cfg -*skl_nhlt_get_debugfs_blob(struct skl_debug *d, u8 link_type, u32 instance) +*skl_nhlt_get_debugfs_blob(struct skl_debug *d, u8 link_type, u32 instance, + u8 stream) { switch (link_type) { case NHLT_LINK_DMIC: @@ -57,7 +58,10 @@ struct nhlt_specific_cfg if (instance >= MAX_SSP) return NULL; - return d->ssp_blob[instance].cfg; + if (stream == SNDRV_PCM_STREAM_PLAYBACK) + return d->ssp_blob[instance].cfg; + else + return d->ssp_blob[MAX_SSP + instance].cfg; default: break; @@ -315,7 +319,7 @@ static void skl_exit_nhlt(struct skl_debug *d) /* free blob memory, if allocated */ for (i = 0; i < MAX_SSP; i++) - kfree(d->ssp_blob[i].cfg); + kfree(d->ssp_blob[MAX_SSP + i].cfg); } static ssize_t nhlt_control_read(struct file *file, @@ -376,11 +380,16 @@ static int skl_init_nhlt(struct skl_debug *d) } for (i = 0; i < MAX_SSP; i++) { - snprintf(name, (sizeof(name)-1), "ssp%d", i); + snprintf(name, (sizeof(name)-1), "ssp%dp", i); if (!debugfs_create_file(name, 0644, d->nhlt, &d->ssp_blob[i], &nhlt_fops)) dev_err(d->dev, "%s: debugfs init failed\n", name); + snprintf(name, (sizeof(name)-1), "ssp%dc", i); + if (!debugfs_create_file(name, + 0644, d->nhlt, + &d->ssp_blob[MAX_SSP + i], &nhlt_fops)) + dev_err(d->dev, "%s: debugfs init failed\n", name); } if (!debugfs_create_file("dmic", 0644, diff --git a/sound/soc/intel/skylake/skl-topology.c b/sound/soc/intel/skylake/skl-topology.c index 7c94fc8e5a0c..d6a3a6f43c91 100644 --- a/sound/soc/intel/skylake/skl-topology.c +++ b/sound/soc/intel/skylake/skl-topology.c @@ -1896,7 +1896,8 @@ static int skl_tplg_be_fill_pipe_params(struct snd_soc_dai *dai, } else { dev_warn(dai->dev, "Querying NHLT blob from Debugfs!!!!\n"); cfg = skl_nhlt_get_debugfs_blob(skl->debugfs, - link_type, mconfig->vbus_id); + link_type, mconfig->vbus_id, + params->stream); } if (cfg) { mconfig->formats_config.caps_size = cfg->size; diff --git a/sound/soc/intel/skylake/skl.h b/sound/soc/intel/skylake/skl.h index 05561be0efe6..4056ea3de714 100644 --- a/sound/soc/intel/skylake/skl.h +++ b/sound/soc/intel/skylake/skl.h @@ -166,8 +166,8 @@ void skl_debug_init_module(struct skl_debug *d, struct snd_soc_dapm_widget *w, struct skl_module_cfg *mconfig); struct nhlt_specific_cfg -*skl_nhlt_get_debugfs_blob(struct skl_debug *d, u8 link_type, u32 instance); - +*skl_nhlt_get_debugfs_blob(struct skl_debug *d, u8 link_type, u32 instance, + u8 stream); #else static inline struct skl_debug *skl_debugfs_init(struct skl *skl) { @@ -178,7 +178,8 @@ static inline void skl_debug_init_module(struct skl_debug *d, struct skl_module_cfg *mconfig) {} static inline struct nhlt_specific_cfg -*skl_nhlt_get_debugfs_blob(struct skl_debug *d, u8 link_type, u32 instance) +*skl_nhlt_get_debugfs_blob(struct skl_debug *d, u8 link_type, u32 instance, + u8 stream) { return NULL; } From 7be46009d8547c3c7b44724c7a39d8ff3b7e5507 Mon Sep 17 00:00:00 2001 From: Ramesh Babu Date: Mon, 2 Nov 2015 07:06:38 +0530 Subject: [PATCH 0651/1103] WORKAROUND: Remove size check for DMIC blob Change-Id: Ic7c70d4f0b1bf137c8bfbfbb9ef9962fdad8daf9 Signed-off-by: Ramesh Babu Reviewed-on: --- sound/soc/intel/skylake/skl-debug.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/sound/soc/intel/skylake/skl-debug.c b/sound/soc/intel/skylake/skl-debug.c index 52b65bb096ab..f9db7ea363ef 100644 --- a/sound/soc/intel/skylake/skl-debug.c +++ b/sound/soc/intel/skylake/skl-debug.c @@ -91,9 +91,6 @@ static ssize_t nhlt_write(struct file *file, ssize_t written; size_t size = blob->size; - if (count > 2 * HDA_SST_CFG_MAX) - return -EIO; - if (!blob->cfg) { /* allocate mem for blob */ blob->cfg = kzalloc(count, GFP_KERNEL); From 8e65fa955ae4fb7c2f6c9f22651ce9f29484b0f5 Mon Sep 17 00:00:00 2001 From: Hardik Shah Date: Fri, 20 May 2016 14:42:17 +0530 Subject: [PATCH 0652/1103] REVERTME:SKL:Topology: Add logic to create SDW aggregation blob. Blob needs to be sent to copier for SDW aggregation. Normally blob is derived from NHLT, but since in SDW blob, stream number is dynamic, blob needs to be created in driver. Blob creation logic for the SDW is implemented in this function. Actually this should go in skl-messages.c as per review. Need to move this to skl-messages.c in next patch set. Change-Id: Ifc58a2343498190f736295754be2c14e3c6d5bea Signed-off-by: Hardik Shah Reviewed-on: --- sound/soc/intel/skylake/skl-topology.c | 40 ++++++++++++++++++++++++-- 1 file changed, 38 insertions(+), 2 deletions(-) diff --git a/sound/soc/intel/skylake/skl-topology.c b/sound/soc/intel/skylake/skl-topology.c index d6a3a6f43c91..80c82d97d226 100644 --- a/sound/soc/intel/skylake/skl-topology.c +++ b/sound/soc/intel/skylake/skl-topology.c @@ -1865,6 +1865,17 @@ static u8 skl_tplg_be_link_type(int dev_type) return ret; } +struct skl_sdw_agg_data_caps { + u32 alh_stream_num; + u32 ch_mask; +} __packed; + +struct skl_sdw_caps_cfg { + u32 gw_attributes; + u32 count; + struct skl_sdw_agg_data_caps data[0]; + +} __packed; /* * Fill the BE gateway parameters @@ -1873,20 +1884,45 @@ static u8 skl_tplg_be_link_type(int dev_type) * The port can have multiple settings so pick based on the PCM * parameters */ +#define SDW_MAX_MASTERS 4 static int skl_tplg_be_fill_pipe_params(struct snd_soc_dai *dai, struct skl_module_cfg *mconfig, struct skl_pipe_params *params) { + int i; struct nhlt_specific_cfg *cfg; + struct skl_sdw_caps_cfg *sdw_cfg; struct skl *skl = get_skl_ctx(dai->dev); int link_type = skl_tplg_be_link_type(mconfig->dev_type); u8 dev_type = skl_tplg_be_dev_type(mconfig->dev_type); skl_tplg_fill_dma_id(mconfig, params); - if (link_type == NHLT_LINK_HDA || link_type == NHLT_LINK_SDW) + if (link_type == NHLT_LINK_HDA) return 0; - + if (link_type == NHLT_LINK_SDW) { + sdw_cfg = kzalloc((((sizeof(struct skl_sdw_agg_data_caps)) * + (mconfig->sdw_agg.num_masters)) + 2), + GFP_KERNEL); + if (!sdw_cfg) + return -ENOMEM; + mconfig->formats_config.caps_size = (((sizeof(u32)) * + (mconfig->sdw_agg.num_masters) * 2) + + (2 * (sizeof(u32)))); + + sdw_cfg->count = mconfig->sdw_agg.num_masters; + for (i = 0; i < SDW_MAX_MASTERS; i++) { + if (mconfig->sdw_agg.agg_data[i].ch_mask) { + sdw_cfg->data[i].ch_mask = + mconfig->sdw_agg.agg_data[i].ch_mask; + sdw_cfg->data[i].alh_stream_num = + mconfig->sdw_agg.agg_data[i].alh_stream_num; + } + } + sdw_cfg->count = mconfig->sdw_agg.num_masters; + mconfig->formats_config.caps = (u32 *) sdw_cfg; + return 0; + } /* update the blob based on virtual bus_id*/ if (!skl->nhlt_override) { cfg = skl_get_ep_blob(skl, mconfig->vbus_id, link_type, From b7d811d29768f334475f1763529e3fa2cf5042de Mon Sep 17 00:00:00 2001 From: Hardik Shah Date: Fri, 20 May 2016 14:55:05 +0530 Subject: [PATCH 0653/1103] SKL:PCM: Derive the SDW master controller number from be_id. SDW Master number or SSP instance number can be derived from VBUS ID. But for aggregation one copier is linked to two SDW master controllers, so its not feasible to get controller number from vbus_id. So fixing the range of BE_IDs for number of controllers to find for which controller PCM ops got called. Change-Id: I6cb995404e0488e660f35f943e9bfc3fdc518e07 Signed-off-by: Hardik Shah Reviewed-on: --- sound/soc/intel/skylake/skl-sdw-pcm.c | 31 ++++++++++++++++++++++++--- 1 file changed, 28 insertions(+), 3 deletions(-) diff --git a/sound/soc/intel/skylake/skl-sdw-pcm.c b/sound/soc/intel/skylake/skl-sdw-pcm.c index af8d117c34ed..504228e2cba5 100644 --- a/sound/soc/intel/skylake/skl-sdw-pcm.c +++ b/sound/soc/intel/skylake/skl-sdw-pcm.c @@ -24,6 +24,7 @@ #include #include "skl.h" #include "skl-topology.h" +#include "skl-sdw-pcm.h" #define STREAM_STATE_ALLOC_STREAM_TAG 0x1 #define STREAM_STATE_ALLOC_STREAM 0x2 @@ -42,6 +43,7 @@ struct sdw_dma_data { struct sdw_master *mstr; enum cnl_sdw_pdi_stream_type stream_type; int stream_state; + int mstr_nr; }; @@ -54,6 +56,7 @@ int cnl_sdw_startup(struct snd_pcm_substream *substream, struct sdw_master *mstr; struct sdw_dma_data *dma; int ret = 0; + char *uuid = NULL; m_cfg = skl_tplg_be_get_cpr_module(dai, substream->stream); @@ -61,7 +64,18 @@ int cnl_sdw_startup(struct snd_pcm_substream *substream, dev_err(dai->dev, "BE Copier not found\n"); return -EINVAL; } - sdw_ctrl_nr = m_cfg->vbus_id; + if (dai->id >= SDW_BE_DAI_ID_MSTR3) + sdw_ctrl_nr = 3; + + else if (dai->id >= SDW_BE_DAI_ID_MSTR2) + sdw_ctrl_nr = 2; + + else if (dai->id >= SDW_BE_DAI_ID_MSTR1) + sdw_ctrl_nr = 1; + + else + sdw_ctrl_nr = 0; + mstr = sdw_get_master(sdw_ctrl_nr); if (!mstr) { dev_err(dai->dev, "Master controller not found\n"); @@ -81,9 +95,10 @@ int cnl_sdw_startup(struct snd_pcm_substream *substream, return -EINVAL; } dma->mstr = mstr; + dma->mstr_nr = sdw_ctrl_nr; snd_soc_dai_set_dma_data(dai, substream, dma); - ret = sdw_alloc_stream_tag(NULL, &dma->stream_tag); + ret = sdw_alloc_stream_tag(uuid, &dma->stream_tag); if (ret) { dev_err(dai->dev, "Unable to allocate stream tag"); ret = -EINVAL; @@ -144,7 +159,17 @@ int cnl_sdw_hw_params(struct snd_pcm_substream *substream, dev_err(dai->dev, "BE Copier not found\n"); return -EINVAL; } - m_cfg->sdw_stream_num = dma->port->pdi_stream->sdw_pdi_num; + + if (!m_cfg->sdw_agg_enable) + m_cfg->sdw_stream_num = dma->port->pdi_stream->sdw_pdi_num; + else + m_cfg->sdw_agg.agg_data[dma->mstr_nr].alh_stream_num = + dma->port->pdi_stream->sdw_pdi_num; + ret = skl_tplg_be_update_params(dai, &p_params); + if (ret) + return ret; + + stream_config.frame_rate = params_rate(params); /* TODO: Get the multiplication factor from NHLT or the XML * to decide with Poland team from where to get it From 2679444a97caa8b6b8314fb50cd2cbad5d5cd559 Mon Sep 17 00:00:00 2001 From: Hardik Shah Date: Fri, 20 May 2016 15:02:36 +0530 Subject: [PATCH 0654/1103] REVERTME:SKL:PCM: Enable aggregation for the Maxim codec. Set the aggregation parameters if Maxim codec is enabled. Aggregation parameters should ideally come from ITT topology. ITT change is still not done, so hardcoding in driver for now. Change-Id: I9d8d7ee9f1740b0fa4bc6f20f275272a9577f733 Signed-off-by: Hardik Shah Reviewed-on: --- sound/soc/intel/skylake/skl-sdw-pcm.c | 27 ++++++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/sound/soc/intel/skylake/skl-sdw-pcm.c b/sound/soc/intel/skylake/skl-sdw-pcm.c index 504228e2cba5..e41a3b38aa81 100644 --- a/sound/soc/intel/skylake/skl-sdw-pcm.c +++ b/sound/soc/intel/skylake/skl-sdw-pcm.c @@ -46,7 +46,10 @@ struct sdw_dma_data { int mstr_nr; }; - +#ifdef CONFIG_SND_SOC_MXFPGA +static char uuid_playback[] = "Agg_p"; +static char uuid_capture[] = "Agg_c"; +#endif int cnl_sdw_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) @@ -98,6 +101,12 @@ int cnl_sdw_startup(struct snd_pcm_substream *substream, dma->mstr_nr = sdw_ctrl_nr; snd_soc_dai_set_dma_data(dai, substream, dma); +#ifdef CONFIG_SND_SOC_MXFPGA + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + uuid = uuid_playback; + else + uuid = uuid_capture; +#endif ret = sdw_alloc_stream_tag(uuid, &dma->stream_tag); if (ret) { dev_err(dai->dev, "Unable to allocate stream tag"); @@ -115,6 +124,18 @@ int cnl_sdw_startup(struct snd_pcm_substream *substream, return ret; } +#ifdef CONFIG_SND_SOC_MXFPGA +static void skl_set_agg(struct skl_module_cfg *m_cfg, int be_id) { + m_cfg->sdw_agg_enable = true; + m_cfg->sdw_agg.num_masters = 2; + if (be_id > SDW_BE_DAI_ID_MSTR0) + m_cfg->sdw_agg.agg_data[1].ch_mask = 0x2; + else + m_cfg->sdw_agg.agg_data[0].ch_mask = 0x1; + +} +#endif + int cnl_sdw_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) @@ -159,6 +180,10 @@ int cnl_sdw_hw_params(struct snd_pcm_substream *substream, dev_err(dai->dev, "BE Copier not found\n"); return -EINVAL; } +#ifdef CONFIG_SND_SOC_MXFPGA + /* Ideally this will come from DFW */ + skl_set_agg(m_cfg, dai->id); +#endif if (!m_cfg->sdw_agg_enable) m_cfg->sdw_stream_num = dma->port->pdi_stream->sdw_pdi_num; From 2c422d5ec830f88eb80e39cc515cd14f68ee2001 Mon Sep 17 00:00:00 2001 From: "Panwar, Ashish" Date: Wed, 20 Jan 2016 19:09:05 +0530 Subject: [PATCH 0655/1103] ASoC: Intel: Skylake: Driver ring buffer APIs for firmware logging. Data from trace window will be copied in this buffer and passed to user space through compress stream APIs. Assuming a single reader and single writer usecase, no locking has been implemented on this buffer and hence we use kernel kfifo API for the ring buffer implementation. Change-Id: I4d9eb40abf3d4cc7fdb63ef7626bc49aaa378777 Signed-off-by: Panwar, Ashish Reviewed-on: Reviewed-by: Babu, Ramesh Tested-by: Babu, Ramesh Signed-off-by: Guneshwor Singh --- sound/soc/intel/common/sst-dsp-priv.h | 39 +++++- sound/soc/intel/skylake/Makefile | 2 +- sound/soc/intel/skylake/skl-fwlog.c | 168 ++++++++++++++++++++++++++ sound/soc/intel/skylake/skl-fwlog.h | 20 +++ 4 files changed, 227 insertions(+), 2 deletions(-) create mode 100644 sound/soc/intel/skylake/skl-fwlog.c create mode 100644 sound/soc/intel/skylake/skl-fwlog.h diff --git a/sound/soc/intel/common/sst-dsp-priv.h b/sound/soc/intel/common/sst-dsp-priv.h index acf06a4f5144..e7151a7e6bae 100644 --- a/sound/soc/intel/common/sst-dsp-priv.h +++ b/sound/soc/intel/common/sst-dsp-priv.h @@ -21,7 +21,9 @@ #include #include #include - +#include +#include +#include #include "../skylake/skl-sst-dsp.h" struct sst_mem_block; @@ -97,6 +99,37 @@ struct sst_mailbox { size_t out_size; }; +/* + * Audio DSP Trace Buffer Configuration. +*/ +struct sst_dbg_rbuffer { + DECLARE_KFIFO_PTR(fifo_dsp, u32); + struct kref refcount; + unsigned long total_avail; + /* To set the state of the stream incase of XRUN */ + struct snd_compr_stream *stream; +}; + +/* + * DSP Trace Buffer for FW Logging + * Assumption: Each core is assigned equal proportion of memory window for fw + * logging addressed in the increasing order of core id (i.e., the first trace + * buffer belong to core 0 and so on). +*/ +struct sst_trace_window { + /* base address and size of fw logging windows */ + void __iomem *addr; + u32 size; + /* driver ringbuffer array for each DSP */ + struct sst_dbg_rbuffer **dbg_buffers; + /* fw write pointer array for each DSP */ + void __iomem **dsp_wps; + /* number of buffers within fw logging window */ + u32 nr_dsp; + /* indicates which DSPs have logging enabled */ + u32 flags; +}; + /* * Audio DSP memory block types. */ @@ -288,6 +321,9 @@ struct sst_dsp { /* mailbox */ struct sst_mailbox mailbox; + /* Trace Buffer */ + struct sst_trace_window trace_wind; + /* HSW/Byt data */ /* list of free and used ADSP memory blocks */ @@ -390,4 +426,5 @@ void sst_mem_block_unregister_all(struct sst_dsp *dsp); u32 sst_dsp_get_offset(struct sst_dsp *dsp, u32 offset, enum sst_mem_type type); + #endif diff --git a/sound/soc/intel/skylake/Makefile b/sound/soc/intel/skylake/Makefile index c5ee108bf5d9..831218ee43c8 100644 --- a/sound/soc/intel/skylake/Makefile +++ b/sound/soc/intel/skylake/Makefile @@ -11,7 +11,7 @@ obj-$(CONFIG_SND_SOC_INTEL_SKYLAKE) += snd-soc-skl.o # Skylake IPC Support snd-soc-skl-ipc-objs := skl-sst-ipc.o skl-sst-dsp.o cnl-sst-dsp.o \ skl-sst-cldma.o skl-sst.o bxt-sst.o cnl-sst.o \ - skl-sst-utils.o + skl-sst-utils.o skl-fwlog.o obj-$(CONFIG_SND_SOC_INTEL_SKYLAKE) += snd-soc-skl-ipc.o diff --git a/sound/soc/intel/skylake/skl-fwlog.c b/sound/soc/intel/skylake/skl-fwlog.c new file mode 100644 index 000000000000..bc25b9e22252 --- /dev/null +++ b/sound/soc/intel/skylake/skl-fwlog.c @@ -0,0 +1,168 @@ +/* + * Intel SST FW Log Tracing + * + * Copyright (C) 2015-16, Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License version + * 2 as published by the Free Software Foundation. + * + * 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. + * + */ + +#include +#include +#include +#include +#include +#include "../common/sst-dsp.h" +#include "../common/sst-dsp-priv.h" +#include "skl-sst-ipc.h" +#include "skl.h" +#include "skl-fwlog.h" + +/* + * Initialize trace window and firmware write pointers for the platform + */ +int skl_dsp_init_trace_window(struct sst_dsp *sst, u32 *wp, u32 offset, + u32 size, int cores) +{ + int idx, alloc_size; + void **dsp_wps; + struct sst_dbg_rbuffer **buff; + + alloc_size = sizeof(buff) * cores; + buff = devm_kzalloc(sst->dev, alloc_size, GFP_KERNEL); + if (!buff) + goto failure; + + dsp_wps = devm_kzalloc(sst->dev, sizeof(void *) * cores, GFP_KERNEL); + + if (!dsp_wps) + goto failure; + + sst->trace_wind.addr = sst->addr.lpe + offset; + sst->trace_wind.size = size; + sst->trace_wind.nr_dsp = cores; + sst->trace_wind.flags = 0; + sst->trace_wind.dbg_buffers = buff; + sst->trace_wind.dsp_wps = (void __iomem**)dsp_wps; + for (idx = 0; idx < cores; idx++) + sst->trace_wind.dsp_wps[idx] = (void __iomem*)(sst->addr.lpe + + wp[idx]); + return 0; + +failure: + dev_err(sst->dev, "Trace buffer init failed for the platform\n"); + return -ENOMEM; +} + +/* + * Initialize ring buffer for a dsp fw logging + */ +int skl_dsp_init_log_buffer(struct sst_dsp *sst, int size, int core, + struct snd_compr_stream *stream) +{ + int ret = 0; + struct sst_dbg_rbuffer *tmp; + + tmp = kzalloc(sizeof(*tmp), GFP_KERNEL); + if (!tmp) + return -ENOMEM; + + ret = kfifo_alloc(&tmp->fifo_dsp, size, GFP_KERNEL); + if (!ret) { + tmp->stream = stream; + tmp->total_avail = 0; + kref_init(&tmp->refcount); + sst->trace_wind.dbg_buffers[core] = tmp; + } else + kfree(tmp); + + return ret; +} +EXPORT_SYMBOL_GPL(skl_dsp_init_log_buffer); + +unsigned long skl_dsp_log_avail(struct sst_dsp *sst, int core) +{ + struct sst_dbg_rbuffer *buff = sst->trace_wind.dbg_buffers[core]; + + if (buff->stream->runtime->state == SNDRV_PCM_STATE_XRUN) + return 0; + + return buff->total_avail; +} +EXPORT_SYMBOL(skl_dsp_log_avail); + +void skl_dsp_write_log(struct sst_dsp *sst, void __iomem *src, int core, + int count) +{ + int i; + u32 *data = (u32 *)src; + struct sst_dbg_rbuffer *buff = sst->trace_wind.dbg_buffers[core]; + + if (buff->stream->runtime->state == SNDRV_PCM_STATE_XRUN) + return; + + for (i = 0; i < count; i += 4) { + if (!kfifo_put(&buff->fifo_dsp, *data)) { + dev_err(sst->dev, "fw log buffer overrun on dsp %d\n", + core); + buff->stream->runtime->state = SNDRV_PCM_STATE_XRUN; + break; + } + data++; + } + buff->total_avail += count; +} + +int skl_dsp_copy_log_user(struct sst_dsp *sst, int core, + void __user *dest, int count) +{ + int copied, ret; + struct sst_dbg_rbuffer *buff = sst->trace_wind.dbg_buffers[core]; + + ret = kfifo_to_user(&buff->fifo_dsp, dest, count, &copied); + + return ret ? ret : copied; +} +EXPORT_SYMBOL_GPL(skl_dsp_copy_log_user); + +static void skl_dsp_free_log_buffer(struct kref *ref) +{ + struct sst_dbg_rbuffer *buff = container_of(ref, struct sst_dbg_rbuffer, + refcount); + kfifo_free(&buff->fifo_dsp); + kfree(buff); +} + +void skl_dsp_get_log_buff(struct sst_dsp *sst, int core) +{ + struct sst_dbg_rbuffer *buff = sst->trace_wind.dbg_buffers[core]; + + kref_get(&buff->refcount); +} +EXPORT_SYMBOL_GPL(skl_dsp_get_log_buff); + +void skl_dsp_put_log_buff(struct sst_dsp *sst, int core) +{ + struct sst_dbg_rbuffer *buff = sst->trace_wind.dbg_buffers[core]; + + kref_put(&buff->refcount, skl_dsp_free_log_buffer); +} +EXPORT_SYMBOL_GPL(skl_dsp_put_log_buff); + +void skl_dsp_done_log_buffer(struct sst_dsp *sst, int core) +{ + skl_dsp_put_log_buff(sst, core); +} +EXPORT_SYMBOL_GPL(skl_dsp_done_log_buffer); + +/* Module Information */ +MODULE_AUTHOR("Ashish Panwar Date: Wed, 20 Jan 2016 19:19:21 +0530 Subject: [PATCH 0656/1103] ASoC: Intel: Skylake: Handler for firmware log buffer status notification We copy half of the firmware log buffer on this notification. We figure which half of trace buffer to copy based on the write pointer. Change-Id: I32d8f47b5eaed3d35b4d2a0761bf4878f0c14d97 Signed-off-by: Panwar, Ashish Reviewed-on: Reviewed-by: Babu, Ramesh Tested-by: Babu, Ramesh --- sound/soc/intel/skylake/skl-sst-ipc.c | 35 ++++++++++++++++++++++++++- 1 file changed, 34 insertions(+), 1 deletion(-) diff --git a/sound/soc/intel/skylake/skl-sst-ipc.c b/sound/soc/intel/skylake/skl-sst-ipc.c index 5234fafb758a..9864801e9ed1 100644 --- a/sound/soc/intel/skylake/skl-sst-ipc.c +++ b/sound/soc/intel/skylake/skl-sst-ipc.c @@ -19,9 +19,9 @@ #include "skl.h" #include "skl-sst-dsp.h" #include "skl-sst-ipc.h" +#include "skl-fwlog.h" #include "sound/hdaudio_ext.h" - #define IPC_IXC_STATUS_BITS 24 /* Global Message - Generic */ @@ -53,6 +53,10 @@ #define IPC_MSG_DIR(x) (((x) & IPC_MSG_DIR_MASK) \ << IPC_MSG_DIR_SHIFT) /* Global Notification Message */ +#define IPC_GLB_NOTIFY_CORE_SHIFT 15 +#define IPC_GLB_NOTIFY_CORE_MASK 0x1 +#define IPC_GLB_NOTIFY_CORE_ID(x) (((x) >> IPC_GLB_NOTIFY_CORE_SHIFT) \ + & IPC_GLB_NOTIFY_CORE_MASK) #define IPC_GLB_NOTIFY_TYPE_SHIFT 16 #define IPC_GLB_NOTIFY_TYPE_MASK 0xFF #define IPC_GLB_NOTIFY_TYPE(x) (((x) >> IPC_GLB_NOTIFY_TYPE_SHIFT) \ @@ -347,6 +351,31 @@ static struct ipc_message *skl_ipc_reply_get_msg(struct sst_generic_ipc *ipc, } +static void +skl_process_log_buffer(struct sst_dsp *sst, struct skl_ipc_header header) +{ + int core, size; + u32 *ptr, avail; + u8 *base; + + core = IPC_GLB_NOTIFY_CORE_ID(header.primary); + if (!(BIT(core) & sst->trace_wind.flags)) { + dev_err(sst->dev, "Logging is disabled on dsp %d\n", core); + return; + } + skl_dsp_get_log_buff(sst, core); + size = sst->trace_wind.size/sst->trace_wind.nr_dsp; + base = (u8 *)sst->trace_wind.addr; + /* move to the source dsp tracing window */ + base += (core * size); + ptr = (u32 *)sst->trace_wind.dsp_wps[core]; + avail = *ptr; + if (avail < size/2) + base += size/2; + skl_dsp_write_log(sst, (void __iomem *)base, core, size/2); + skl_dsp_put_log_buff(sst, core); +} + int skl_ipc_process_notification(struct sst_generic_ipc *ipc, struct skl_ipc_header header) { @@ -369,6 +398,10 @@ int skl_ipc_process_notification(struct sst_generic_ipc *ipc, wake_up(&skl->boot_wait); break; + case IPC_GLB_NOTIFY_LOG_BUFFER_STATUS: + skl_process_log_buffer(skl->dsp, header); + break; + case IPC_GLB_NOTIFY_PHRASE_DETECTED: dev_dbg(ipc->dev, "***** Phrase Detected **********\n"); From 2c5722dfd08d62cf21d8b5e994d69cbccde97625 Mon Sep 17 00:00:00 2001 From: "Panwar, Ashish" Date: Wed, 20 Jan 2016 19:22:05 +0530 Subject: [PATCH 0657/1103] ASoC: Intel: Skylake: Compress ops for firmware logging. Implementation of compress ops for firmware logging. These ops are used to transfer data from driver buffer to user space. Compress device to DSP core mapping is determined by the DAI name. Change-Id: I760f07873cb259a58fb0f517f958b4043719d8a6 Signed-off-by: Panwar, Ashish Reviewed-on: Reviewed-by: Babu, Ramesh Tested-by: Babu, Ramesh Signed-off-by: Guneshwor Singh --- sound/soc/intel/Kconfig | 1 + sound/soc/intel/skylake/skl-messages.c | 22 ++++ sound/soc/intel/skylake/skl-pcm.c | 157 +++++++++++++++++++++++++ sound/soc/intel/skylake/skl-sst-ipc.h | 12 ++ 4 files changed, 192 insertions(+) diff --git a/sound/soc/intel/Kconfig b/sound/soc/intel/Kconfig index 221283d83619..2f56dbfea444 100644 --- a/sound/soc/intel/Kconfig +++ b/sound/soc/intel/Kconfig @@ -111,6 +111,7 @@ config SND_SOC_INTEL_SKYLAKE select SND_HDA_DSP_LOADER select SND_SOC_TOPOLOGY select SND_SOC_INTEL_SST + select SND_SOC_COMPRESS select SND_SOC_ACPI_INTEL_MATCH help If you have a Intel Skylake/Broxton/ApolloLake/KabyLake/ diff --git a/sound/soc/intel/skylake/skl-messages.c b/sound/soc/intel/skylake/skl-messages.c index a9eaa4aed643..d0121984cf4b 100644 --- a/sound/soc/intel/skylake/skl-messages.c +++ b/sound/soc/intel/skylake/skl-messages.c @@ -66,6 +66,28 @@ void skl_dsp_set_astate_cfg(struct skl_sst *ctx, u32 cnt, void *data) skl_ipc_set_large_config(&ctx->ipc, &msg, data); } +#define ENABLE_LOGS 6 +#define DEFAULT_LOG_PRIORITY 5 + +/* set firmware logging state via IPC */ +int skl_dsp_enable_logging(struct sst_generic_ipc *ipc, int core, int enable) +{ + struct skl_log_state_msg log_msg; + struct skl_ipc_large_config_msg msg = {0}; + int ret = 0; + + log_msg.core_mask = (1 << core); + log_msg.logs_core[core].enable = enable; + log_msg.logs_core[core].priority = DEFAULT_LOG_PRIORITY; + + msg.large_param_id = ENABLE_LOGS; + msg.param_data_size = sizeof(log_msg); + + ret = skl_ipc_set_large_config(ipc, &msg, (u32 *)&log_msg); + + return ret; +} + #define NOTIFICATION_PARAM_ID 3 #define NOTIFICATION_MASK 0xf diff --git a/sound/soc/intel/skylake/skl-pcm.c b/sound/soc/intel/skylake/skl-pcm.c index f27b7188b7c8..284e34fb60f8 100644 --- a/sound/soc/intel/skylake/skl-pcm.c +++ b/sound/soc/intel/skylake/skl-pcm.c @@ -29,6 +29,7 @@ #include "skl-sst-dsp.h" #include "skl-sst-ipc.h" #include "skl-sdw-pcm.h" +#include "skl-fwlog.h" #define HDA_MONO 1 #define HDA_STEREO 2 @@ -724,6 +725,141 @@ static void skl_sdw_shutdown(struct snd_pcm_substream *substream, pm_runtime_put_autosuspend(dai->dev); } +static bool skl_is_core_valid(int core) +{ + if (core != INT_MIN) + return true; + else + return false; +} + +static int skl_get_compr_core(struct snd_compr_stream *stream) +{ + struct snd_soc_pcm_runtime *rtd = stream->private_data; + struct snd_soc_dai *dai = rtd->cpu_dai; + + if (!strcmp(dai->name, "TraceBuffer0 Pin")) + return 0; + else if (!strcmp(dai->name, "TraceBuffer1 Pin")) + return 1; + else + return INT_MIN; +} + +static int skl_is_logging_core(int core) +{ + if (core == 0 || core == 1) + return 1; + else + return 0; +} + +static struct skl_sst *skl_get_sst_compr(struct snd_compr_stream *stream) +{ + struct snd_soc_pcm_runtime *rtd = stream->private_data; + struct snd_soc_dai *dai = rtd->cpu_dai; + struct hdac_ext_bus *ebus = dev_get_drvdata(dai->dev); + struct skl *skl = ebus_to_skl(ebus); + struct skl_sst *sst = skl->skl_sst; + + return sst; +} + +static int skl_trace_compr_set_params(struct snd_compr_stream *stream, + struct snd_compr_params *params, + struct snd_soc_dai *cpu_dai) +{ + int ret; + struct skl_sst *skl_sst = skl_get_sst_compr(stream); + struct sst_dsp *sst = skl_sst->dsp; + struct sst_generic_ipc *ipc = &skl_sst->ipc; + int size = params->buffer.fragment_size * params->buffer.fragments; + int core = skl_get_compr_core(stream); + + if (!skl_is_core_valid(core)) + return -EINVAL; + + if (size & (size - 1)) { + dev_err(sst->dev, "Buffer size must be a power of 2\n"); + return -EINVAL; + } + + ret = skl_dsp_init_log_buffer(sst, size, core, stream); + if (ret) { + dev_err(sst->dev, "set params failed for dsp %d\n", core); + return ret; + } + + skl_dsp_get_log_buff(sst, core); + sst->trace_wind.flags |= BIT(core); + ret = skl_dsp_enable_logging(ipc, core, 1); + if (ret < 0) { + dev_err(sst->dev, "enable logs failed for dsp %d\n", core); + sst->trace_wind.flags &= ~BIT(core); + skl_dsp_put_log_buff(sst, core); + return ret; + } + return 0; +} + +static int skl_trace_compr_tstamp(struct snd_compr_stream *stream, + struct snd_compr_tstamp *tstamp, + struct snd_soc_dai *cpu_dai) +{ + struct skl_sst *skl_sst = skl_get_sst_compr(stream); + struct sst_dsp *sst = skl_sst->dsp; + int core = skl_get_compr_core(stream); + + if (!skl_is_core_valid(core)) + return -EINVAL; + + tstamp->copied_total = skl_dsp_log_avail(sst, core); + return 0; +} + +static int skl_trace_compr_copy(struct snd_compr_stream *stream, + char __user *dest, size_t count) +{ + struct skl_sst *skl_sst = skl_get_sst_compr(stream); + struct sst_dsp *sst = skl_sst->dsp; + int core = skl_get_compr_core(stream); + + if (skl_is_logging_core(core)) + return skl_dsp_copy_log_user(sst, core, dest, count); + else + return 0; +} + +static int skl_trace_compr_free(struct snd_compr_stream *stream, + struct snd_soc_dai *cpu_dai) +{ + struct skl_sst *skl_sst = skl_get_sst_compr(stream); + struct sst_dsp *sst = skl_sst->dsp; + struct sst_generic_ipc *ipc = &skl_sst->ipc; + int core = skl_get_compr_core(stream); + int is_enabled = sst->trace_wind.flags & BIT(core); + + if (!skl_is_core_valid(core)) + return -EINVAL; + if (is_enabled) { + sst->trace_wind.flags &= ~BIT(core); + skl_dsp_enable_logging(ipc, core, 0); + skl_dsp_put_log_buff(sst, core); + skl_dsp_done_log_buffer(sst, core); + } + return 0; +} + +static struct snd_compr_ops skl_platform_compr_ops = { + .copy = skl_trace_compr_copy, +}; + +static struct snd_soc_cdai_ops skl_trace_compr_ops = { + .shutdown = skl_trace_compr_free, + .pointer = skl_trace_compr_tstamp, + .set_params = skl_trace_compr_set_params, +}; + static const struct snd_soc_dai_ops skl_pcm_dai_ops = { .startup = skl_pcm_open, .shutdown = skl_pcm_close, @@ -758,6 +894,26 @@ static struct snd_soc_dai_ops skl_sdw_dai_ops = { }; static struct snd_soc_dai_driver skl_fe_dai[] = { +{ + .name = "TraceBuffer0 Pin", + .compress_new = snd_soc_new_compress, + .cops = &skl_trace_compr_ops, + .capture = { + .stream_name = "TraceBuffer Capture", + .channels_min = HDA_MONO, + .channels_max = HDA_MONO, + }, +}, +{ + .name = "TraceBuffer1 Pin", + .compress_new = snd_soc_new_compress, + .cops = &skl_trace_compr_ops, + .capture = { + .stream_name = "TraceBuffer1 Capture", + .channels_min = HDA_MONO, + .channels_max = HDA_MONO, + }, +}, { .name = "System Pin", .ops = &skl_pcm_dai_ops, @@ -1576,6 +1732,7 @@ static const struct snd_soc_component_driver skl_component = { .name = "pcm", .probe = skl_platform_soc_probe, .ops = &skl_platform_ops, + .compr_ops = &skl_platform_compr_ops, .pcm_new = skl_pcm_new, .pcm_free = skl_pcm_free, }; diff --git a/sound/soc/intel/skylake/skl-sst-ipc.h b/sound/soc/intel/skylake/skl-sst-ipc.h index 10a486939515..a8de8cfac6ee 100644 --- a/sound/soc/intel/skylake/skl-sst-ipc.h +++ b/sound/soc/intel/skylake/skl-sst-ipc.h @@ -161,6 +161,16 @@ struct skl_ipc_d0ix_msg { u8 wake; }; +struct skl_log_state { + u32 enable; + u32 priority; +}; + +struct skl_log_state_msg { + u32 core_mask; + struct skl_log_state logs_core[2]; +}; + #define SKL_IPC_BOOT_MSECS 3000 #define SKL_IPC_D3_MASK 0 @@ -210,6 +220,8 @@ int skl_ipc_set_d0ix(struct sst_generic_ipc *ipc, int skl_ipc_check_D0i0(struct sst_dsp *dsp, bool state); +int skl_dsp_enable_logging(struct sst_generic_ipc *ipc, int core, int enable); + void skl_ipc_int_enable(struct sst_dsp *dsp); void skl_ipc_op_int_enable(struct sst_dsp *ctx); void skl_ipc_op_int_disable(struct sst_dsp *ctx); From 7ec0da6bb8aef30548b7d076c6fb686534e6b743 Mon Sep 17 00:00:00 2001 From: "Panwar, Ashish" Date: Fri, 26 Feb 2016 02:02:48 +0530 Subject: [PATCH 0658/1103] ASoC: Intel: Skylake: Check buffer users and prevent concurrent writers More than one writer on log buffer will cause data corruption. Fix this by checking for an existing writer and dropping log buffer status notification if the prev writer is still writing. Despite the fear of loosing some data, it helps in maintaining the sanity of logs. Change-Id: Id85827221fa50b71da48087f82ac08ed488f9929 Signed-off-by: Panwar, Ashish Reviewed-on: Reviewed-by: Babu, Ramesh Tested-by: Babu, Ramesh --- sound/soc/intel/skylake/skl-fwlog.c | 7 +++++++ sound/soc/intel/skylake/skl-fwlog.h | 1 + sound/soc/intel/skylake/skl-sst-ipc.c | 6 ++++++ 3 files changed, 14 insertions(+) diff --git a/sound/soc/intel/skylake/skl-fwlog.c b/sound/soc/intel/skylake/skl-fwlog.c index bc25b9e22252..96c5d245849d 100644 --- a/sound/soc/intel/skylake/skl-fwlog.c +++ b/sound/soc/intel/skylake/skl-fwlog.c @@ -98,6 +98,13 @@ unsigned long skl_dsp_log_avail(struct sst_dsp *sst, int core) } EXPORT_SYMBOL(skl_dsp_log_avail); +int skl_dsp_get_buff_users(struct sst_dsp *sst, int core) +{ + struct sst_dbg_rbuffer *buff = sst->trace_wind.dbg_buffers[core]; + + return refcount_read(&buff->refcount.refcount); +} + void skl_dsp_write_log(struct sst_dsp *sst, void __iomem *src, int core, int count) { diff --git a/sound/soc/intel/skylake/skl-fwlog.h b/sound/soc/intel/skylake/skl-fwlog.h index 06304de3d2ab..d6307cafd156 100644 --- a/sound/soc/intel/skylake/skl-fwlog.h +++ b/sound/soc/intel/skylake/skl-fwlog.h @@ -17,4 +17,5 @@ int skl_dsp_copy_log_user(struct sst_dsp *sst, int core, void __user *dest, void skl_dsp_get_log_buff(struct sst_dsp *sst, int core); void skl_dsp_put_log_buff(struct sst_dsp *sst, int core); void skl_dsp_done_log_buffer(struct sst_dsp *sst, int core); +int skl_dsp_get_buff_users(struct sst_dsp *sst, int core); #endif /* __SKL_FWLOG_H__ */ diff --git a/sound/soc/intel/skylake/skl-sst-ipc.c b/sound/soc/intel/skylake/skl-sst-ipc.c index 9864801e9ed1..36c22c57cf90 100644 --- a/sound/soc/intel/skylake/skl-sst-ipc.c +++ b/sound/soc/intel/skylake/skl-sst-ipc.c @@ -363,6 +363,12 @@ skl_process_log_buffer(struct sst_dsp *sst, struct skl_ipc_header header) dev_err(sst->dev, "Logging is disabled on dsp %d\n", core); return; } + if (skl_dsp_get_buff_users(sst, core) > 2) { + dev_err(sst->dev, "Can't handle log buffer notification, \ + previous writer is not finished yet !\n \ + dropping log buffer\n"); + return; + } skl_dsp_get_log_buff(sst, core); size = sst->trace_wind.size/sst->trace_wind.nr_dsp; base = (u8 *)sst->trace_wind.addr; From d7be2feb32d1870d42ff90cc270a102a34ee5e7d Mon Sep 17 00:00:00 2001 From: "Panwar, Ashish" Date: Fri, 4 Mar 2016 11:14:28 +0530 Subject: [PATCH 0659/1103] ASoC: Intel: Skylake: Wake up any potential reader after copying log data Change-Id: I0f9587d0c7f6bdd83b5b0cd62afc51f767cffb92 Signed-off-by: Panwar, Ashish Reviewed-on: Reviewed-by: Babu, Ramesh Tested-by: Babu, Ramesh --- sound/soc/intel/skylake/skl-fwlog.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sound/soc/intel/skylake/skl-fwlog.c b/sound/soc/intel/skylake/skl-fwlog.c index 96c5d245849d..1aacf8625658 100644 --- a/sound/soc/intel/skylake/skl-fwlog.c +++ b/sound/soc/intel/skylake/skl-fwlog.c @@ -125,6 +125,7 @@ void skl_dsp_write_log(struct sst_dsp *sst, void __iomem *src, int core, data++; } buff->total_avail += count; + wake_up(&buff->stream->runtime->sleep); } int skl_dsp_copy_log_user(struct sst_dsp *sst, int core, From 3916c57e87c78c7e80e420a8d924dfd0ff7ba26d Mon Sep 17 00:00:00 2001 From: "Panwar, Ashish" Date: Fri, 4 Mar 2016 11:20:00 +0530 Subject: [PATCH 0660/1103] ASoC: Intel: Skylake: Convert buffer size to # of u32 elements before allocating memory While allocating memory, kfifo expects the number of elements in arguments and not the # of bytes. Due to this, kfifo is currently allocating 4 times the memory requested by user. Convert buffer size to the number of elements before allocating memory. Change-Id: Ib8938bc1645896a5b342aa7fe5602b4e415960a6 Signed-off-by: Panwar, Ashish Reviewed-on: Reviewed-by: Babu, Ramesh Tested-by: Babu, Ramesh --- sound/soc/intel/skylake/skl-pcm.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sound/soc/intel/skylake/skl-pcm.c b/sound/soc/intel/skylake/skl-pcm.c index 284e34fb60f8..d462b1b89cb4 100644 --- a/sound/soc/intel/skylake/skl-pcm.c +++ b/sound/soc/intel/skylake/skl-pcm.c @@ -779,6 +779,7 @@ static int skl_trace_compr_set_params(struct snd_compr_stream *stream, if (!skl_is_core_valid(core)) return -EINVAL; + size = size / sizeof(u32); if (size & (size - 1)) { dev_err(sst->dev, "Buffer size must be a power of 2\n"); return -EINVAL; From 8be14dd08687163faeac87b24893db6376d33c4c Mon Sep 17 00:00:00 2001 From: "Panwar, Ashish" Date: Fri, 18 Mar 2016 16:01:07 +0530 Subject: [PATCH 0661/1103] ASoC: Intel: CNL: Initialize trace buffer window for CNL Initialize address, size of tracing window and write pointers of each core for CNL platform. Change-Id: I9febfe5bd1eef76f50f5de170c6c99fc98cdc6d9 Signed-off-by: Panwar, Ashish Reviewed-on: Reviewed-by: Shah, Hardik T Tested-by: Shah, Hardik T --- sound/soc/intel/skylake/cnl-sst.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/sound/soc/intel/skylake/cnl-sst.c b/sound/soc/intel/skylake/cnl-sst.c index f70d70d0822a..b82de714c916 100644 --- a/sound/soc/intel/skylake/cnl-sst.c +++ b/sound/soc/intel/skylake/cnl-sst.c @@ -37,6 +37,7 @@ #include "cnl-sst-dsp.h" #include "skl-sst-dsp.h" #include "skl-sst-ipc.h" +#include "skl-fwlog.h" #define CNL_FW_ROM_INIT 0x1 #define CNL_FW_INIT 0x5 @@ -46,6 +47,14 @@ #define CNL_ADSP_SRAM0_BASE 0x80000 +/* Trace Buffer Window */ +#define CNL_ADSP_SRAM2_BASE 0x0C0000 +#define CNL_ADSP_W2_SIZE 0x2000 +#define CNL_ADSP_WP_DSP0 (CNL_ADSP_SRAM0_BASE+0x30) +#define CNL_ADSP_WP_DSP1 (CNL_ADSP_SRAM0_BASE+0x34) +#define CNL_ADSP_WP_DSP2 (CNL_ADSP_SRAM0_BASE+0x38) +#define CNL_ADSP_WP_DSP3 (CNL_ADSP_SRAM0_BASE+0x3C) + /* Firmware status window */ #define CNL_ADSP_FW_STATUS CNL_ADSP_SRAM0_BASE #define CNL_ADSP_ERROR_CODE (CNL_ADSP_FW_STATUS + 0x4) @@ -629,6 +638,8 @@ int cnl_sst_dsp_init(struct device *dev, void __iomem *mmio_base, int irq, { struct skl_sst *cnl; struct sst_dsp *sst; + u32 dsp_wp[] = {CNL_ADSP_WP_DSP0, CNL_ADSP_WP_DSP1, CNL_ADSP_WP_DSP2, + CNL_ADSP_WP_DSP3}; int ret; ret = skl_sst_ctx_init(dev, irq, fw_name, dsp_ops, dsp, &cnl_dev); @@ -651,6 +662,13 @@ int cnl_sst_dsp_init(struct device *dev, void __iomem *mmio_base, int irq, CNL_ADSP_W0_UP_SZ, CNL_ADSP_SRAM1_BASE, CNL_ADSP_W1_SZ); + ret = skl_dsp_init_trace_window(sst, dsp_wp, CNL_ADSP_SRAM2_BASE, + CNL_ADSP_W2_SIZE, CNL_DSP_CORES); + if (ret) { + dev_err(dev, "FW tracing init failed : %x", ret); + return ret; + } + ret = cnl_ipc_init(dev, cnl); if (ret) { skl_dsp_free(sst); From 001bcbad81191a31523f8a85c0579794b2cf8e4e Mon Sep 17 00:00:00 2001 From: "Panwar, Ashish" Date: Fri, 18 Mar 2016 16:02:19 +0530 Subject: [PATCH 0662/1103] ASoC: Intel: Skylake: Add trace buffer dais for CNL CNL needs 4 dais for logging, one for each core. Added 2 more dais here. It depends on the machine driver to make use of appropriate dais based on the hardware platform. Change-Id: If9df857895b8261cfebd61617542365187499ea1 Signed-off-by: Panwar, Ashish Reviewed-on: Reviewed-by: Shah, Hardik T Tested-by: Shah, Hardik T Signed-off-by: Guneshwor Singh --- sound/soc/intel/skylake/skl-pcm.c | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/sound/soc/intel/skylake/skl-pcm.c b/sound/soc/intel/skylake/skl-pcm.c index d462b1b89cb4..dc842e289dfb 100644 --- a/sound/soc/intel/skylake/skl-pcm.c +++ b/sound/soc/intel/skylake/skl-pcm.c @@ -742,6 +742,10 @@ static int skl_get_compr_core(struct snd_compr_stream *stream) return 0; else if (!strcmp(dai->name, "TraceBuffer1 Pin")) return 1; + else if (!strcmp(dai->name, "TraceBuffer2 Pin")) + return 2; + else if (!strcmp(dai->name, "TraceBuffer3 Pin")) + return 3; else return INT_MIN; } @@ -900,7 +904,7 @@ static struct snd_soc_dai_driver skl_fe_dai[] = { .compress_new = snd_soc_new_compress, .cops = &skl_trace_compr_ops, .capture = { - .stream_name = "TraceBuffer Capture", + .stream_name = "TraceBuffer0 Capture", .channels_min = HDA_MONO, .channels_max = HDA_MONO, }, @@ -915,6 +919,26 @@ static struct snd_soc_dai_driver skl_fe_dai[] = { .channels_max = HDA_MONO, }, }, +{ + .name = "TraceBuffer2 Pin", + .compress_new = snd_soc_new_compress, + .cops = &skl_trace_compr_ops, + .capture = { + .stream_name = "TraceBuffer2 Capture", + .channels_min = HDA_MONO, + .channels_max = HDA_MONO, + }, +}, +{ + .name = "TraceBuffer3 Pin", + .compress_new = snd_soc_new_compress, + .cops = &skl_trace_compr_ops, + .capture = { + .stream_name = "TraceBuffer3 Capture", + .channels_min = HDA_MONO, + .channels_max = HDA_MONO, + }, +}, { .name = "System Pin", .ops = &skl_pcm_dai_ops, From 559b01d71acb7764f227198a766502b3c405fcd3 Mon Sep 17 00:00:00 2001 From: "Panwar, Ashish" Date: Sat, 19 Mar 2016 08:44:11 +0530 Subject: [PATCH 0663/1103] REVERTME-1: Revert when logging is updated in driver/firmware. Core 0 is currently using entire 8KB window for logging. Revert this patch when it is shared by all 4 cores or logging infrastructure is updated in driver. Change-Id: I4798c501030ce9216918d629f6c5f898e1e16aa6 Signed-off-by: Panwar, Ashish Reviewed-on: Reviewed-by: Shah, Hardik T Tested-by: Shah, Hardik T --- sound/soc/intel/skylake/skl-messages.c | 4 ++++ sound/soc/intel/skylake/skl-sst-ipc.c | 8 ++++++++ 2 files changed, 12 insertions(+) diff --git a/sound/soc/intel/skylake/skl-messages.c b/sound/soc/intel/skylake/skl-messages.c index d0121984cf4b..3336a23d9898 100644 --- a/sound/soc/intel/skylake/skl-messages.c +++ b/sound/soc/intel/skylake/skl-messages.c @@ -67,7 +67,11 @@ void skl_dsp_set_astate_cfg(struct skl_sst *ctx, u32 cnt, void *data) } #define ENABLE_LOGS 6 +#if defined(CONFIG_SND_SOC_INTEL_CNL_FPGA) +#define DEFAULT_LOG_PRIORITY 6 +#else #define DEFAULT_LOG_PRIORITY 5 +#endif /* set firmware logging state via IPC */ int skl_dsp_enable_logging(struct sst_generic_ipc *ipc, int core, int enable) diff --git a/sound/soc/intel/skylake/skl-sst-ipc.c b/sound/soc/intel/skylake/skl-sst-ipc.c index 36c22c57cf90..d34ff3ffeced 100644 --- a/sound/soc/intel/skylake/skl-sst-ipc.c +++ b/sound/soc/intel/skylake/skl-sst-ipc.c @@ -358,7 +358,11 @@ skl_process_log_buffer(struct sst_dsp *sst, struct skl_ipc_header header) u32 *ptr, avail; u8 *base; +#if defined(CONFIG_SND_SOC_INTEL_CNL_FPGA) + core = 0; +#else core = IPC_GLB_NOTIFY_CORE_ID(header.primary); +#endif if (!(BIT(core) & sst->trace_wind.flags)) { dev_err(sst->dev, "Logging is disabled on dsp %d\n", core); return; @@ -370,7 +374,11 @@ skl_process_log_buffer(struct sst_dsp *sst, struct skl_ipc_header header) return; } skl_dsp_get_log_buff(sst, core); +#if defined(CONFIG_SND_SOC_INTEL_CNL_FPGA) + size = sst->trace_wind.size; +#else size = sst->trace_wind.size/sst->trace_wind.nr_dsp; +#endif base = (u8 *)sst->trace_wind.addr; /* move to the source dsp tracing window */ base += (core * size); From e44d1e87e169df7b749394c278316a520f059387 Mon Sep 17 00:00:00 2001 From: Guneshwor Singh Date: Wed, 5 Jul 2017 14:30:41 +0530 Subject: [PATCH 0664/1103] ASoC: Intel: Skylake: Add dsp log level selection kcontrol Add platform kcontrol for DSP log level. User can select between QUIET, CRITICAL, HIGH, MEDIUM, LOW, VERBOSE levels before starting the compressed stream for log capture. Change-Id: I2cc1965fe58ed60defaa50fa494a0d9a39e4d477 Signed-off-by: Guneshwor Singh Reviewed-on: Reviewed-by: audio_build Reviewed-by: Kale, Sanyog R Tested-by: Avati, Santosh Kumar Reviewed-by: Diwakar, Praveen Signed-off-by: Guneshwor Singh --- sound/soc/intel/common/sst-dsp-priv.h | 2 ++ sound/soc/intel/skylake/skl-fwlog.c | 23 +++++++++++++++++++++++ sound/soc/intel/skylake/skl-fwlog.h | 2 ++ sound/soc/intel/skylake/skl-messages.c | 7 +------ sound/soc/intel/skylake/skl-pcm.c | 13 +++++++++++++ sound/soc/intel/skylake/skl-topology.c | 24 ++++++++++++++++++++++++ sound/soc/intel/skylake/skl-topology.h | 4 ++++ 7 files changed, 69 insertions(+), 6 deletions(-) diff --git a/sound/soc/intel/common/sst-dsp-priv.h b/sound/soc/intel/common/sst-dsp-priv.h index e7151a7e6bae..8e80c6f177f8 100644 --- a/sound/soc/intel/common/sst-dsp-priv.h +++ b/sound/soc/intel/common/sst-dsp-priv.h @@ -128,6 +128,8 @@ struct sst_trace_window { u32 nr_dsp; /* indicates which DSPs have logging enabled */ u32 flags; + /* dsp fw log level*/ + u32 log_priority; }; /* diff --git a/sound/soc/intel/skylake/skl-fwlog.c b/sound/soc/intel/skylake/skl-fwlog.c index 1aacf8625658..f7506135e311 100644 --- a/sound/soc/intel/skylake/skl-fwlog.c +++ b/sound/soc/intel/skylake/skl-fwlog.c @@ -25,6 +25,8 @@ #include "skl.h" #include "skl-fwlog.h" +#define DEF_LOG_PRIORITY 3 + /* * Initialize trace window and firmware write pointers for the platform */ @@ -51,6 +53,7 @@ int skl_dsp_init_trace_window(struct sst_dsp *sst, u32 *wp, u32 offset, sst->trace_wind.flags = 0; sst->trace_wind.dbg_buffers = buff; sst->trace_wind.dsp_wps = (void __iomem**)dsp_wps; + sst->trace_wind.log_priority = DEF_LOG_PRIORITY; for (idx = 0; idx < cores; idx++) sst->trace_wind.dsp_wps[idx] = (void __iomem*)(sst->addr.lpe + wp[idx]); @@ -87,6 +90,26 @@ int skl_dsp_init_log_buffer(struct sst_dsp *sst, int size, int core, } EXPORT_SYMBOL_GPL(skl_dsp_init_log_buffer); +int update_dsp_log_priority(int value, struct skl *skl) +{ + int ret = 0; + struct skl_sst *ctx = skl->skl_sst; + + ctx->dsp->trace_wind.log_priority = value; + return ret; +} +EXPORT_SYMBOL_GPL(update_dsp_log_priority); + +int get_dsp_log_priority(struct skl *skl) +{ + u32 value; + struct skl_sst *ctx = skl->skl_sst; + + value = ctx->dsp->trace_wind.log_priority; + return value; +} +EXPORT_SYMBOL_GPL(get_dsp_log_priority); + unsigned long skl_dsp_log_avail(struct sst_dsp *sst, int core) { struct sst_dbg_rbuffer *buff = sst->trace_wind.dbg_buffers[core]; diff --git a/sound/soc/intel/skylake/skl-fwlog.h b/sound/soc/intel/skylake/skl-fwlog.h index d6307cafd156..df9146686ea2 100644 --- a/sound/soc/intel/skylake/skl-fwlog.h +++ b/sound/soc/intel/skylake/skl-fwlog.h @@ -18,4 +18,6 @@ void skl_dsp_get_log_buff(struct sst_dsp *sst, int core); void skl_dsp_put_log_buff(struct sst_dsp *sst, int core); void skl_dsp_done_log_buffer(struct sst_dsp *sst, int core); int skl_dsp_get_buff_users(struct sst_dsp *sst, int core); +int update_dsp_log_priority(int value, struct skl *skl); +int get_dsp_log_priority(struct skl *skl); #endif /* __SKL_FWLOG_H__ */ diff --git a/sound/soc/intel/skylake/skl-messages.c b/sound/soc/intel/skylake/skl-messages.c index 3336a23d9898..4f88595815a8 100644 --- a/sound/soc/intel/skylake/skl-messages.c +++ b/sound/soc/intel/skylake/skl-messages.c @@ -67,11 +67,6 @@ void skl_dsp_set_astate_cfg(struct skl_sst *ctx, u32 cnt, void *data) } #define ENABLE_LOGS 6 -#if defined(CONFIG_SND_SOC_INTEL_CNL_FPGA) -#define DEFAULT_LOG_PRIORITY 6 -#else -#define DEFAULT_LOG_PRIORITY 5 -#endif /* set firmware logging state via IPC */ int skl_dsp_enable_logging(struct sst_generic_ipc *ipc, int core, int enable) @@ -82,7 +77,7 @@ int skl_dsp_enable_logging(struct sst_generic_ipc *ipc, int core, int enable) log_msg.core_mask = (1 << core); log_msg.logs_core[core].enable = enable; - log_msg.logs_core[core].priority = DEFAULT_LOG_PRIORITY; + log_msg.logs_core[core].priority = ipc->dsp->trace_wind.log_priority; msg.large_param_id = ENABLE_LOGS; msg.param_data_size = sizeof(log_msg); diff --git a/sound/soc/intel/skylake/skl-pcm.c b/sound/soc/intel/skylake/skl-pcm.c index dc842e289dfb..388397d70be4 100644 --- a/sound/soc/intel/skylake/skl-pcm.c +++ b/sound/soc/intel/skylake/skl-pcm.c @@ -1753,6 +1753,17 @@ static int skl_platform_soc_probe(struct snd_soc_component *component) return 0; } +static const char* const dsp_log_text[] = + {"QUIET", "CRITICAL", "HIGH", "MEDIUM", "LOW", "VERBOSE"}; + +static const struct soc_enum dsp_log_enum = + SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(dsp_log_text), dsp_log_text); + +static struct snd_kcontrol_new skl_controls[] = { + SOC_ENUM_EXT("DSP Log Level", dsp_log_enum, skl_tplg_dsp_log_get, + skl_tplg_dsp_log_set), +}; + static const struct snd_soc_component_driver skl_component = { .name = "pcm", .probe = skl_platform_soc_probe, @@ -1760,6 +1771,8 @@ static const struct snd_soc_component_driver skl_component = { .compr_ops = &skl_platform_compr_ops, .pcm_new = skl_pcm_new, .pcm_free = skl_pcm_free, + .controls = skl_controls, + .num_controls = ARRAY_SIZE(skl_controls), }; int skl_platform_register(struct device *dev) diff --git a/sound/soc/intel/skylake/skl-topology.c b/sound/soc/intel/skylake/skl-topology.c index 80c82d97d226..5c5e3c45008d 100644 --- a/sound/soc/intel/skylake/skl-topology.c +++ b/sound/soc/intel/skylake/skl-topology.c @@ -30,6 +30,7 @@ #include "skl.h" #include "../common/sst-dsp.h" #include "../common/sst-dsp-priv.h" +#include "skl-fwlog.h" #define SKL_CH_FIXUP_MASK (1 << 0) #define SKL_RATE_FIXUP_MASK (1 << 1) @@ -1453,6 +1454,29 @@ static int skl_tplg_pga_event(struct snd_soc_dapm_widget *w, return 0; } +int skl_tplg_dsp_log_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct hdac_ext_bus *ebus = snd_soc_component_get_drvdata(component); + struct skl *skl = ebus_to_skl(ebus); + + ucontrol->value.integer.value[0] = get_dsp_log_priority(skl); + + return 0; +} + +int skl_tplg_dsp_log_set(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct hdac_ext_bus *ebus = snd_soc_component_get_drvdata(component); + struct skl *skl = ebus_to_skl(ebus); + + update_dsp_log_priority(ucontrol->value.integer.value[0], skl); + + return 0; +} static int skl_tplg_tlv_control_get(struct snd_kcontrol *kcontrol, unsigned int __user *data, unsigned int size) diff --git a/sound/soc/intel/skylake/skl-topology.h b/sound/soc/intel/skylake/skl-topology.h index 44e4109ba4dc..5998e6926df3 100644 --- a/sound/soc/intel/skylake/skl-topology.h +++ b/sound/soc/intel/skylake/skl-topology.h @@ -533,4 +533,8 @@ int skl_dai_load(struct snd_soc_component *cmp, int index, struct snd_soc_tplg_pcm *pcm, struct snd_soc_dai *dai); void skl_tplg_add_moduleid_in_bind_params(struct skl *skl, struct snd_soc_dapm_widget *w); +int skl_tplg_dsp_log_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol); +int skl_tplg_dsp_log_set(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol); #endif From 6166c869e91e4ea2340a0c92621876452c0a6f6a Mon Sep 17 00:00:00 2001 From: Guneshwor Singh Date: Thu, 9 Jun 2016 17:33:01 +0530 Subject: [PATCH 0665/1103] ASoC: Intel: CNL: Enable SDW aggregation support from ITT Change-Id: I7f1dac7aab70b340e3faa0b1d8330c0416484cb8 Signed-off-by: Guneshwor Singh --- include/uapi/sound/skl-tplg-interface.h | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/include/uapi/sound/skl-tplg-interface.h b/include/uapi/sound/skl-tplg-interface.h index 68eda1a15e39..47f033884b2f 100644 --- a/include/uapi/sound/skl-tplg-interface.h +++ b/include/uapi/sound/skl-tplg-interface.h @@ -23,6 +23,8 @@ #define MAX_IN_QUEUE 8 #define MAX_OUT_QUEUE 8 +#define SDW_MAX_MASTERS 4 + #define SKL_UUID_STR_SZ 40 /* Event types goes here */ /* Reserve event type 0 for no event handlers */ @@ -151,6 +153,11 @@ enum skl_module_param_type { SKL_PARAM_BIND }; +struct skl_dfw_sdw_aggdata { + u32 alh_stream_num; + u32 channel_mask; +} __packed; + struct skl_dfw_algo_data { __u32 set_params:2; __u32 rsvd:30; From d6eafbd8ce8953e51994813981b44ed421107fb8 Mon Sep 17 00:00:00 2001 From: Guneshwor Singh Date: Thu, 9 Jun 2016 17:35:39 +0530 Subject: [PATCH 0666/1103] ASoC: Intel: SKL: Remove SDW aggregation hardcode data Change-Id: I6601af59781950f4fcf2a13e93be6e3014957c69 Signed-off-by: Guneshwor Singh --- sound/soc/intel/skylake/skl-sdw-pcm.c | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/sound/soc/intel/skylake/skl-sdw-pcm.c b/sound/soc/intel/skylake/skl-sdw-pcm.c index e41a3b38aa81..ea9a3e14434a 100644 --- a/sound/soc/intel/skylake/skl-sdw-pcm.c +++ b/sound/soc/intel/skylake/skl-sdw-pcm.c @@ -124,18 +124,6 @@ int cnl_sdw_startup(struct snd_pcm_substream *substream, return ret; } -#ifdef CONFIG_SND_SOC_MXFPGA -static void skl_set_agg(struct skl_module_cfg *m_cfg, int be_id) { - m_cfg->sdw_agg_enable = true; - m_cfg->sdw_agg.num_masters = 2; - if (be_id > SDW_BE_DAI_ID_MSTR0) - m_cfg->sdw_agg.agg_data[1].ch_mask = 0x2; - else - m_cfg->sdw_agg.agg_data[0].ch_mask = 0x1; - -} -#endif - int cnl_sdw_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) @@ -180,10 +168,6 @@ int cnl_sdw_hw_params(struct snd_pcm_substream *substream, dev_err(dai->dev, "BE Copier not found\n"); return -EINVAL; } -#ifdef CONFIG_SND_SOC_MXFPGA - /* Ideally this will come from DFW */ - skl_set_agg(m_cfg, dai->id); -#endif if (!m_cfg->sdw_agg_enable) m_cfg->sdw_stream_num = dma->port->pdi_stream->sdw_pdi_num; From 3f4a6de0c24d4462fc3fb8a9be384fcfcd928f7a Mon Sep 17 00:00:00 2001 From: Guneshwor Singh Date: Fri, 5 Aug 2016 16:35:34 +0530 Subject: [PATCH 0667/1103] ASoC: Intel: Aggregation fixes for masters other than 0 and 1 Change-Id: Ie26608b30f75c69d039ee17c5703ad1f79e88f00 Signed-off-by: Guneshwor Singh --- sound/soc/intel/skylake/skl-topology.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/sound/soc/intel/skylake/skl-topology.c b/sound/soc/intel/skylake/skl-topology.c index 5c5e3c45008d..35c0ec938a18 100644 --- a/sound/soc/intel/skylake/skl-topology.c +++ b/sound/soc/intel/skylake/skl-topology.c @@ -1908,12 +1908,11 @@ struct skl_sdw_caps_cfg { * The port can have multiple settings so pick based on the PCM * parameters */ -#define SDW_MAX_MASTERS 4 static int skl_tplg_be_fill_pipe_params(struct snd_soc_dai *dai, struct skl_module_cfg *mconfig, struct skl_pipe_params *params) { - int i; + int i, j; struct nhlt_specific_cfg *cfg; struct skl_sdw_caps_cfg *sdw_cfg; struct skl *skl = get_skl_ctx(dai->dev); @@ -1935,12 +1934,14 @@ static int skl_tplg_be_fill_pipe_params(struct snd_soc_dai *dai, + (2 * (sizeof(u32)))); sdw_cfg->count = mconfig->sdw_agg.num_masters; + j = 0; for (i = 0; i < SDW_MAX_MASTERS; i++) { if (mconfig->sdw_agg.agg_data[i].ch_mask) { - sdw_cfg->data[i].ch_mask = + sdw_cfg->data[j].ch_mask = mconfig->sdw_agg.agg_data[i].ch_mask; - sdw_cfg->data[i].alh_stream_num = + sdw_cfg->data[j].alh_stream_num = mconfig->sdw_agg.agg_data[i].alh_stream_num; + j++; } } sdw_cfg->count = mconfig->sdw_agg.num_masters; From 72660e74982e93cfefc6ddfb3fe7070c362a6088 Mon Sep 17 00:00:00 2001 From: "Pawse, GuruprasadX" Date: Wed, 24 Feb 2016 17:03:16 +0530 Subject: [PATCH 0668/1103] ASoC: Fix TLV control size in TLV handler Size passed to TLV callback is sum of payload size and TLV header. TLV header consumes 8 bytes. The callback handler has to pass the entire data (including TLV header) to drivers. Change-Id: Ic7dcd0d3ab015e340ff907b2dc507b63677cc2ff Tracked-On: Signed-off-by: Pawse, GuruprasadX Signed-off-by: Ramesh Babu Reviewed-on: Reviewed-by: Babu, Ramesh Tested-by: Babu, Ramesh --- sound/soc/soc-ops.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/sound/soc/soc-ops.c b/sound/soc/soc-ops.c index 592efb370c44..f5b48d53bc19 100644 --- a/sound/soc/soc-ops.c +++ b/sound/soc/soc-ops.c @@ -764,11 +764,16 @@ int snd_soc_bytes_info_ext(struct snd_kcontrol *kcontrol, } EXPORT_SYMBOL_GPL(snd_soc_bytes_info_ext); +/* TLV header size*/ +#define TLV_HEADER_SIZE (2 * sizeof(unsigned int)) + int snd_soc_bytes_tlv_callback(struct snd_kcontrol *kcontrol, int op_flag, unsigned int size, unsigned int __user *tlv) { struct soc_bytes_ext *params = (void *)kcontrol->private_value; - unsigned int count = size < params->max ? size : params->max; + /* Data includes includes TLV Header */ + unsigned int count = size < (params->max + TLV_HEADER_SIZE) ? + size : (params->max + TLV_HEADER_SIZE); int ret = -ENXIO; switch (op_flag) { From 3a2f1f562256da93abf0facbf72c773d86442f51 Mon Sep 17 00:00:00 2001 From: Divya Prakash Date: Thu, 7 Apr 2016 10:32:45 +0530 Subject: [PATCH 0669/1103] ALSA: hda: Enhance HD audio framework to support compress streams Introduce APIs for compress stream DMA assignment, BDL setup and buffer allocation. Change-Id: I8cf4c8b96367cacc131a304c758d0aef53010d25 Signed-off-by: Divya Prakash Reviewed-on: Reviewed-by: Babu, Ramesh Tested-by: Babu, Ramesh --- include/sound/compress_driver.h | 6 ++++ include/sound/hdaudio.h | 2 ++ include/sound/hdaudio_ext.h | 4 +++ sound/hda/ext/hdac_ext_stream.c | 43 +++++++++++++++++++++++++++ sound/hda/hdac_stream.c | 51 ++++++++++++++++++++++----------- 5 files changed, 89 insertions(+), 17 deletions(-) diff --git a/include/sound/compress_driver.h b/include/sound/compress_driver.h index ea8c93bbb0e0..47a8af466727 100644 --- a/include/sound/compress_driver.h +++ b/include/sound/compress_driver.h @@ -45,6 +45,11 @@ struct snd_compr_runtime { u64 total_bytes_transferred; wait_queue_head_t sleep; void *private_data; + /* -- DMA -- */ + unsigned char *dma_area; /* DMA area */ + dma_addr_t dma_addr; /* physical bus address (not accessible from main CPU) */ + size_t dma_bytes; /* size of DMA area */ + struct snd_dma_buffer *dma_buffer_p; /* allocated buffer */ }; /** @@ -69,6 +74,7 @@ struct snd_compr_stream { bool metadata_set; bool next_track; void *private_data; + struct snd_dma_buffer dma_buffer; }; /** diff --git a/include/sound/hdaudio.h b/include/sound/hdaudio.h index 0aa0189017ba..5eee13a0c045 100644 --- a/include/sound/hdaudio.h +++ b/include/sound/hdaudio.h @@ -487,6 +487,7 @@ struct hdac_stream { struct snd_pcm_substream *substream; /* assigned substream, * set in PCM open */ + struct snd_compr_stream *stream; unsigned int format_val; /* format value to be set in the * controller and the codec */ @@ -500,6 +501,7 @@ struct hdac_stream { bool no_period_wakeup:1; bool locked:1; + unsigned long curr_pos; /* timestamp */ unsigned long start_wallclk; /* start + minimum wallclk */ unsigned long period_wallclk; /* wallclk for period */ diff --git a/include/sound/hdaudio_ext.h b/include/sound/hdaudio_ext.h index f34aced69ca8..2d93a039a286 100644 --- a/include/sound/hdaudio_ext.h +++ b/include/sound/hdaudio_ext.h @@ -88,6 +88,10 @@ void snd_hdac_link_free_all(struct hdac_bus *bus); struct hdac_ext_stream *snd_hdac_ext_stream_assign(struct hdac_bus *bus, struct snd_pcm_substream *substream, int type); +struct hdac_ext_stream * +hdac_ext_host_stream_compr_assign(struct hdac_ext_bus *ebus, + struct snd_compr_stream *substream, + int direction); void snd_hdac_ext_stream_release(struct hdac_ext_stream *azx_dev, int type); void snd_hdac_ext_stream_decouple(struct hdac_bus *bus, struct hdac_ext_stream *azx_dev, bool decouple); diff --git a/sound/hda/ext/hdac_ext_stream.c b/sound/hda/ext/hdac_ext_stream.c index a835558ddbc9..4bd95f4b2234 100644 --- a/sound/hda/ext/hdac_ext_stream.c +++ b/sound/hda/ext/hdac_ext_stream.c @@ -22,6 +22,8 @@ #include #include #include +#include +#include /** * snd_hdac_ext_stream_init - initialize each stream (aka device) @@ -549,3 +551,44 @@ int snd_hdac_ext_stream_set_lpib(struct hdac_ext_stream *stream, u32 value) return 0; } EXPORT_SYMBOL_GPL(snd_hdac_ext_stream_set_lpib); +struct hdac_ext_stream * +hdac_ext_host_stream_compr_assign(struct hdac_ext_bus *ebus, + struct snd_compr_stream *substream, + int direction) +{ + struct hdac_ext_stream *res = NULL; + struct hdac_stream *stream = NULL; + struct hdac_bus *hbus = &ebus->bus; + + if (!hbus->ppcap) { + dev_err(hbus->dev, "stream type not supported\n"); + return NULL; + } + + list_for_each_entry(stream, &hbus->stream_list, list) { + struct hdac_ext_stream *hstream = container_of(stream, + struct hdac_ext_stream, + hstream); + if (stream->direction != direction) + continue; + + if (!stream->opened) { + if (!hstream->decoupled) + snd_hdac_ext_stream_decouple(ebus, + hstream, true); + res = hstream; + break; + } + } + if (res) { + spin_lock_irq(&hbus->reg_lock); + res->hstream.opened = 1; + res->hstream.running = 0; + res->hstream.stream = substream; + spin_unlock_irq(&hbus->reg_lock); + } + dev_dbg(hbus->dev, "Stream tag = %d, index = %d\n", + res->hstream.stream_tag, res->hstream.index); + return res; +} +EXPORT_SYMBOL_GPL(hdac_ext_host_stream_compr_assign); diff --git a/sound/hda/hdac_stream.c b/sound/hda/hdac_stream.c index eee422390d8e..d3b1e22ac050 100644 --- a/sound/hda/hdac_stream.c +++ b/sound/hda/hdac_stream.c @@ -10,6 +10,7 @@ #include #include #include +#include #include "trace.h" /** @@ -363,11 +364,22 @@ int snd_hdac_stream_setup_periods(struct hdac_stream *azx_dev) { struct hdac_bus *bus = azx_dev->bus; struct snd_pcm_substream *substream = azx_dev->substream; - struct snd_pcm_runtime *runtime = substream->runtime; + struct snd_compr_stream *csubstream = azx_dev->stream; + struct snd_pcm_runtime *runtime = NULL; + struct snd_compr_runtime *cruntime = NULL; + struct snd_dma_buffer *dma_buffer_p = NULL; __le32 *bdl; int i, ofs, periods, period_bytes; int pos_adj, pos_align; + if (substream) { + runtime = substream->runtime; + dma_buffer_p = snd_pcm_get_dma_buf(substream); + } else if (csubstream) { + cruntime = csubstream->runtime; + dma_buffer_p = csubstream->runtime->dma_buffer_p; + } + /* reset BDL address */ snd_hdac_stream_writel(azx_dev, SD_BDLPL, 0); snd_hdac_stream_writel(azx_dev, SD_BDLPU, 0); @@ -381,7 +393,7 @@ int snd_hdac_stream_setup_periods(struct hdac_stream *azx_dev) azx_dev->frags = 0; pos_adj = bus->bdl_pos_adj; - if (!azx_dev->no_period_wakeup && pos_adj > 0) { + if (!azx_dev->no_period_wakeup && pos_adj > 0 && substream) { pos_align = pos_adj; pos_adj = (pos_adj * runtime->rate + 47999) / 48000; if (!pos_adj) @@ -395,8 +407,7 @@ int snd_hdac_stream_setup_periods(struct hdac_stream *azx_dev) pos_adj); pos_adj = 0; } else { - ofs = setup_bdle(bus, snd_pcm_get_dma_buf(substream), - azx_dev, + ofs = setup_bdle(bus, dma_buffer_p, azx_dev, &bdl, ofs, pos_adj, true); if (ofs < 0) goto error; @@ -406,14 +417,12 @@ int snd_hdac_stream_setup_periods(struct hdac_stream *azx_dev) for (i = 0; i < periods; i++) { if (i == periods - 1 && pos_adj) - ofs = setup_bdle(bus, snd_pcm_get_dma_buf(substream), - azx_dev, &bdl, ofs, - period_bytes - pos_adj, 0); + ofs = setup_bdle(bus, dma_buffer_p, azx_dev, + &bdl, ofs, period_bytes - pos_adj, 0); else - ofs = setup_bdle(bus, snd_pcm_get_dma_buf(substream), - azx_dev, &bdl, ofs, - period_bytes, - !azx_dev->no_period_wakeup); + ofs = setup_bdle(bus, dma_buffer_p, azx_dev, + &bdl, ofs, period_bytes, + !azx_dev->no_period_wakeup); if (ofs < 0) goto error; } @@ -440,14 +449,21 @@ int snd_hdac_stream_set_params(struct hdac_stream *azx_dev, unsigned int bufsize, period_bytes; struct snd_pcm_substream *substream = azx_dev->substream; - struct snd_pcm_runtime *runtime; + struct snd_compr_stream *csubstream = azx_dev->stream; + struct snd_pcm_runtime *runtime = NULL; + struct snd_compr_runtime *cruntime = NULL; int err; - if (!substream) + if (substream) { + runtime = substream->runtime; + bufsize = snd_pcm_lib_buffer_bytes(substream); + period_bytes = snd_pcm_lib_period_bytes(substream); + } else if (csubstream) { + cruntime = csubstream->runtime; + bufsize = cruntime->buffer_size; + period_bytes = cruntime->fragment_size; + } else return -EINVAL; - runtime = substream->runtime; - bufsize = snd_pcm_lib_buffer_bytes(substream); - period_bytes = snd_pcm_lib_period_bytes(substream); if (bufsize != azx_dev->bufsize || period_bytes != azx_dev->period_bytes || @@ -456,7 +472,8 @@ int snd_hdac_stream_set_params(struct hdac_stream *azx_dev, azx_dev->bufsize = bufsize; azx_dev->period_bytes = period_bytes; azx_dev->format_val = format_val; - azx_dev->no_period_wakeup = runtime->no_period_wakeup; + if (substream) + azx_dev->no_period_wakeup = runtime->no_period_wakeup; err = snd_hdac_stream_setup_periods(azx_dev); if (err < 0) return err; From 4863a5dd34bb81d1cce2c300678454a1dc955385 Mon Sep 17 00:00:00 2001 From: Divya Prakash Date: Wed, 20 Apr 2016 10:04:07 +0530 Subject: [PATCH 0670/1103] ALSA: hda: Service buffer completed interrupts for compress streams In case of the IOC bit being enabled, receive and service the buffer completed interrupts for compress streams. Change-Id: Ic391b6757c374379637f1889dbd519b4b757f708 Signed-off-by: Divya Prakash Reviewed-on: Reviewed-by: Babu, Ramesh Tested-by: Babu, Ramesh --- sound/hda/hdac_controller.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/sound/hda/hdac_controller.c b/sound/hda/hdac_controller.c index 74244d8e2909..ff841cd452fd 100644 --- a/sound/hda/hdac_controller.c +++ b/sound/hda/hdac_controller.c @@ -549,8 +549,9 @@ int snd_hdac_bus_handle_stream_irq(struct hdac_bus *bus, unsigned int status, sd_status = snd_hdac_stream_readb(azx_dev, SD_STS); snd_hdac_stream_writeb(azx_dev, SD_STS, SD_INT_MASK); handled |= 1 << azx_dev->index; - if (!azx_dev->substream || !azx_dev->running || - !(sd_status & SD_INT_COMPLETE)) + if ((!azx_dev->substream && !azx_dev->stream)|| + !azx_dev->running || + !(sd_status & SD_INT_COMPLETE)) continue; if (ack) ack(bus, azx_dev); From 74a54416866880b83ac83e16e93b745539dad96e Mon Sep 17 00:00:00 2001 From: Divya Prakash Date: Sun, 10 Apr 2016 13:36:16 +0530 Subject: [PATCH 0671/1103] ASoC: Intel: Add delete module IPC This IPC is to explicitly delete a module instance. Till now, destroying a pipe was taking care of deleting module instance in the pipe. This IPC would be needed in the case of standalone modules like probe. Change-Id: I6aa9bec8ae3b3311a3c43257cdcd354c7fd3712b Signed-off-by: Divya Prakash Reviewed-on: Reviewed-by: Babu, Ramesh Tested-by: Babu, Ramesh --- sound/soc/intel/skylake/skl-sst-ipc.c | 30 ++++++++++++++++++++++++++- sound/soc/intel/skylake/skl-sst-ipc.h | 3 +++ 2 files changed, 32 insertions(+), 1 deletion(-) diff --git a/sound/soc/intel/skylake/skl-sst-ipc.c b/sound/soc/intel/skylake/skl-sst-ipc.c index d34ff3ffeced..b83a3076a1e3 100644 --- a/sound/soc/intel/skylake/skl-sst-ipc.c +++ b/sound/soc/intel/skylake/skl-sst-ipc.c @@ -284,7 +284,8 @@ enum skl_ipc_module_msg { IPC_MOD_BIND = 5, IPC_MOD_UNBIND = 6, IPC_MOD_SET_DX = 7, - IPC_MOD_SET_D0IX = 8 + IPC_MOD_SET_D0IX = 8, + IPC_MOD_DELETE_INSTANCE = 11 }; void skl_ipc_tx_data_copy(struct ipc_message *msg, char *tx_data, @@ -802,6 +803,33 @@ int skl_ipc_set_dx(struct sst_generic_ipc *ipc, u8 instance_id, } EXPORT_SYMBOL_GPL(skl_ipc_set_dx); +int skl_ipc_delete_instance(struct sst_generic_ipc *ipc, + struct skl_ipc_init_instance_msg *msg) +{ + struct skl_ipc_header header = {0}; + u64 *ipc_header = (u64 *)(&header); + int ret; + + header.primary = IPC_MSG_TARGET(IPC_MOD_MSG); + header.primary |= IPC_MSG_DIR(IPC_MSG_REQUEST); + header.primary |= IPC_GLB_TYPE(IPC_MOD_DELETE_INSTANCE); + header.primary |= IPC_MOD_INSTANCE_ID(msg->instance_id); + header.primary |= IPC_MOD_ID(msg->module_id); + + dev_dbg(ipc->dev, "In %s primary =%x ext=%x\n", __func__, + header.primary, header.extension); + ret = sst_ipc_tx_message_wait(ipc, *ipc_header, NULL, + msg->param_data_size, NULL, 0); + + if (ret < 0) { + dev_err(ipc->dev, "ipc: delete instance failed\n"); + return ret; + } + + return ret; +} +EXPORT_SYMBOL_GPL(skl_ipc_delete_instance); + int skl_ipc_init_instance(struct sst_generic_ipc *ipc, struct skl_ipc_init_instance_msg *msg, void *param_data) { diff --git a/sound/soc/intel/skylake/skl-sst-ipc.h b/sound/soc/intel/skylake/skl-sst-ipc.h index a8de8cfac6ee..eb030dfb88ca 100644 --- a/sound/soc/intel/skylake/skl-sst-ipc.h +++ b/sound/soc/intel/skylake/skl-sst-ipc.h @@ -194,6 +194,9 @@ int skl_ipc_restore_pipeline(struct sst_generic_ipc *ipc, u8 instance_id); int skl_ipc_init_instance(struct sst_generic_ipc *sst_ipc, struct skl_ipc_init_instance_msg *msg, void *param_data); +int skl_ipc_delete_instance(struct sst_generic_ipc *sst_ipc, + struct skl_ipc_init_instance_msg *msg); + int skl_ipc_bind_unbind(struct sst_generic_ipc *sst_ipc, struct skl_ipc_bind_unbind_msg *msg); From fd60e35e636c4c4b86b18971eb66da802c24110c Mon Sep 17 00:00:00 2001 From: Divya Prakash Date: Wed, 20 Apr 2016 10:08:03 +0530 Subject: [PATCH 0672/1103] ASoC: Intel: Add Probe compress APIs Using the compress framework, introduce probe compress dai APIs to open, set parameters, start, stop, and close a probe stream. Change-Id: Id869e23c5a59602f171aadff89bf5acb0419abab Signed-off-by: Divya Prakash Reviewed-on: Reviewed-by: Babu, Ramesh Tested-by: Babu, Ramesh ASoC: Intel: Skylake: Add probe CPU dai and DAI ops Add 2 CPU dais, one each for extractor and injector and register the corresponding DAI ops Change-Id: I0df64b2b6a1e242f8e10bec833c3f5ab28aa2d84 Signed-off-by: Divya Prakash Reviewed-on: Reviewed-by: Babu, Ramesh Tested-by: Babu, Ramesh ASoC: Intel: Maintain the platform compress APIs as common code. Keep the platform driver compress APIs in a seperate file for common use across different compress DAI ops Change-Id: I62f2396f5e04007ba1454a2187c807c196547c4a Signed-off-by: Divya Prakash Reviewed-on: Reviewed-by: Babu, Ramesh Tested-by: Babu, Ramesh ASoC: Intel: Implement probe TLV handler Implement probe TLV handler to set and get probe points. The controls parameters contains following information: - whether it is a connection or disconnection - whether the probe type is injector, extractor or injector-reextract - Probe point to probe, uniquely represented by Module ID, Instance ID, queue type and index This information is parsed by the driver in the handler to send the corrensponding IPC and IPC payload.Therefore a custom TLV handler implementation is required for probe. Change-Id: I54d6dc656b0629d85d64a793d54b29f02cc43c35 Signed-off-by: Divya Prakash Signed-off-by: Mousumi Jana Reviewed-on: Reviewed-by: Babu, Ramesh Tested-by: Babu, Ramesh ASoC: Intel: Probe stream operations in alignment with new firmware interface Based on the new firmware interface and IPCs, following are implemented: - Probe as a standalone module and not as a pipeline - Probe module initialization, gateway configuration and deletion - Single extractor and multiple injector stream setup - Caching stream info such as DMA channel, buffer size, DMA type for all streams - Single probe instance running for all extractors and injectors - IPCs for connection, disconnection and DMA attach Change-Id: Icad5108227ff12a7f6cb23b3ae47358f1c8f2cf4 Signed-off-by: Divya Prakash Reviewed-on: Reviewed-by: Babu, Ramesh Tested-by: Babu, Ramesh ALSA: hda: ext: KW fixes for probe feature in hdac_ext stream This patch will add fixes for below klocwork errors, 1. Null pointer 'res' that comes from line 528 may be dereferenced at line 560. 2. Pointer 'res' checked for NULL at line 552 may be dereferenced at line 560. Change-Id: I083c779466589db36d38ccfdda2483004deaeb43 Signed-off-by: G Kranthi Reviewed-on: Reviewed-by: B, Jayachandran Reviewed-by: Babu, Ramesh Tested-by: Babu, Ramesh ALSA: hda: KW fixes for probe feature This patch will add fixes for below klocwork errors, 1. Null pointer 'dma_buffer_p' that comes from line 390 may be passed to function and can be dereferenced there by passing argument 2 to function 'setup_bdle' at line 443. 2. Null pointer 'runtime' that comes from line 472 may be dereferenced at line 490. Change-Id: Iee59594e8571c06568980204f7352583554c5bf1 Signed-off-by: G Kranthi Reviewed-on: Reviewed-by: B, Jayachandran Reviewed-by: Babu, Ramesh Tested-by: Babu, Ramesh --- include/uapi/sound/skl-tplg-interface.h | 13 + sound/hda/ext/hdac_ext_stream.c | 4 +- sound/hda/hdac_stream.c | 11 +- sound/soc/intel/skylake/Makefile | 2 +- sound/soc/intel/skylake/skl-compress.c | 132 ++++++++++ sound/soc/intel/skylake/skl-compress.h | 35 +++ sound/soc/intel/skylake/skl-messages.c | 123 +++++++++ sound/soc/intel/skylake/skl-pcm.c | 57 ++++- sound/soc/intel/skylake/skl-probe.c | 324 ++++++++++++++++++++++++ sound/soc/intel/skylake/skl-probe.h | 37 +++ sound/soc/intel/skylake/skl-sst-ipc.h | 31 +++ sound/soc/intel/skylake/skl-topology.c | 220 +++++++++++++++- sound/soc/intel/skylake/skl-topology.h | 48 +++- sound/soc/intel/skylake/skl.c | 23 +- 14 files changed, 1048 insertions(+), 12 deletions(-) create mode 100644 sound/soc/intel/skylake/skl-compress.c create mode 100644 sound/soc/intel/skylake/skl-compress.h create mode 100644 sound/soc/intel/skylake/skl-probe.c create mode 100644 sound/soc/intel/skylake/skl-probe.h diff --git a/include/uapi/sound/skl-tplg-interface.h b/include/uapi/sound/skl-tplg-interface.h index 47f033884b2f..e1a7771f4873 100644 --- a/include/uapi/sound/skl-tplg-interface.h +++ b/include/uapi/sound/skl-tplg-interface.h @@ -18,6 +18,7 @@ */ #define SKL_CONTROL_TYPE_BYTE_TLV 0x100 #define SKL_CONTROL_TYPE_MIC_SELECT 0x102 +#define SKL_CONTROL_TYPE_BYTE_PROBE 0x101 #define HDA_SST_CFG_MAX 900 /* size of copier cfg*/ #define MAX_IN_QUEUE 8 @@ -79,6 +80,7 @@ enum skl_module_type { SKL_MODULE_TYPE_BASE_OUTFMT, SKL_MODULE_TYPE_KPB, SKL_MODULE_TYPE_MIC_SELECT, + SKL_MODULE_TYPE_PROBE }; enum skl_core_affinity { @@ -153,6 +155,17 @@ enum skl_module_param_type { SKL_PARAM_BIND }; +enum skl_probe_connect_type { + SKL_PROBE_CONNECT = 3, + SKL_PROBE_DISCONNECT +}; + +enum skl_probe_direction { + SKL_PROBE_EXTRACT = 0, + SKL_PROBE_INJECT, + SKL_PROBE_INJECT_REEXTRACT +}; + struct skl_dfw_sdw_aggdata { u32 alh_stream_num; u32 channel_mask; diff --git a/sound/hda/ext/hdac_ext_stream.c b/sound/hda/ext/hdac_ext_stream.c index 4bd95f4b2234..1b2a1c996422 100644 --- a/sound/hda/ext/hdac_ext_stream.c +++ b/sound/hda/ext/hdac_ext_stream.c @@ -586,9 +586,9 @@ hdac_ext_host_stream_compr_assign(struct hdac_ext_bus *ebus, res->hstream.running = 0; res->hstream.stream = substream; spin_unlock_irq(&hbus->reg_lock); - } - dev_dbg(hbus->dev, "Stream tag = %d, index = %d\n", + dev_dbg(hbus->dev, "Stream tag = %d, index = %d\n", res->hstream.stream_tag, res->hstream.index); + } return res; } EXPORT_SYMBOL_GPL(hdac_ext_host_stream_compr_assign); diff --git a/sound/hda/hdac_stream.c b/sound/hda/hdac_stream.c index d3b1e22ac050..3c0c7c353566 100644 --- a/sound/hda/hdac_stream.c +++ b/sound/hda/hdac_stream.c @@ -378,7 +378,8 @@ int snd_hdac_stream_setup_periods(struct hdac_stream *azx_dev) } else if (csubstream) { cruntime = csubstream->runtime; dma_buffer_p = csubstream->runtime->dma_buffer_p; - } + } else + return -EINVAL; /* reset BDL address */ snd_hdac_stream_writel(azx_dev, SD_BDLPL, 0); @@ -453,27 +454,29 @@ int snd_hdac_stream_set_params(struct hdac_stream *azx_dev, struct snd_pcm_runtime *runtime = NULL; struct snd_compr_runtime *cruntime = NULL; int err; + unsigned int no_period_wakeup; if (substream) { runtime = substream->runtime; bufsize = snd_pcm_lib_buffer_bytes(substream); period_bytes = snd_pcm_lib_period_bytes(substream); + no_period_wakeup = runtime->no_period_wakeup; } else if (csubstream) { cruntime = csubstream->runtime; bufsize = cruntime->buffer_size; period_bytes = cruntime->fragment_size; + no_period_wakeup = 0; } else return -EINVAL; if (bufsize != azx_dev->bufsize || period_bytes != azx_dev->period_bytes || format_val != azx_dev->format_val || - runtime->no_period_wakeup != azx_dev->no_period_wakeup) { + no_period_wakeup != azx_dev->no_period_wakeup) { azx_dev->bufsize = bufsize; azx_dev->period_bytes = period_bytes; azx_dev->format_val = format_val; - if (substream) - azx_dev->no_period_wakeup = runtime->no_period_wakeup; + azx_dev->no_period_wakeup = no_period_wakeup; err = snd_hdac_stream_setup_periods(azx_dev); if (err < 0) return err; diff --git a/sound/soc/intel/skylake/Makefile b/sound/soc/intel/skylake/Makefile index 831218ee43c8..9a3ddb969d4e 100644 --- a/sound/soc/intel/skylake/Makefile +++ b/sound/soc/intel/skylake/Makefile @@ -1,6 +1,6 @@ # SPDX-License-Identifier: GPL-2.0 snd-soc-skl-objs := skl.o skl-sdw-pcm.o skl-pcm.o skl-nhlt.o skl-messages.o \ -skl-topology.o +skl-topology.o skl-compress.o skl-probe.o ifdef CONFIG_DEBUG_FS snd-soc-skl-objs += skl-debug.o diff --git a/sound/soc/intel/skylake/skl-compress.c b/sound/soc/intel/skylake/skl-compress.c new file mode 100644 index 000000000000..c8b26e80b974 --- /dev/null +++ b/sound/soc/intel/skylake/skl-compress.c @@ -0,0 +1,132 @@ + +/* + * skl-compress.c -ASoC HDA Platform driver file implementing compress functionality + * + * Copyright (C) 2015-2016 Intel Corp + * Author: Divya Prakash + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * 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; version 2 of the License. + * + * 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. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + */ + +#include +#include +#include +#include +#include "skl.h" +inline +struct hdac_ext_stream *get_hdac_ext_compr_stream(struct snd_compr_stream *stream) +{ + return stream->runtime->private_data; +} + +struct hdac_ext_bus *get_bus_compr_ctx(struct snd_compr_stream *substream) +{ + struct hdac_ext_stream *stream = get_hdac_ext_compr_stream(substream); + struct hdac_stream *hstream = hdac_stream(stream); + struct hdac_bus *bus = hstream->bus; + + return hbus_to_ebus(bus); +} + +void skl_set_compr_runtime_buffer(struct snd_compr_stream *substream, + struct snd_dma_buffer *bufp, size_t size) +{ + struct snd_compr_runtime *runtime = substream->runtime; + + if (bufp) { + runtime->dma_buffer_p = bufp; + runtime->dma_area = bufp->area; + runtime->dma_addr = bufp->addr; + runtime->dma_bytes = size; + } else { + runtime->dma_buffer_p = NULL; + runtime->dma_area = NULL; + runtime->dma_addr = 0; + runtime->dma_bytes = 0; + } +} + +int skl_compr_malloc_pages(struct snd_compr_stream *substream, + struct hdac_ext_bus *ebus, size_t size) +{ + struct snd_compr_runtime *runtime; + struct snd_dma_buffer *dmab = NULL; + struct skl *skl = ebus_to_skl(ebus); + + runtime = substream->runtime; + + dmab = kzalloc(sizeof(*dmab), GFP_KERNEL); + if (!dmab) + return -ENOMEM; + substream->dma_buffer.dev.type = SNDRV_DMA_TYPE_DEV_SG; + substream->dma_buffer.dev.dev = snd_dma_pci_data(skl->pci); + dmab->dev = substream->dma_buffer.dev; + if (snd_dma_alloc_pages(substream->dma_buffer.dev.type, + substream->dma_buffer.dev.dev, + size, dmab) < 0) { + dev_err(ebus_to_hbus(ebus)->dev, + "Error in snd_dma_alloc_pages\n"); + kfree(dmab); + return -ENOMEM; + } + skl_set_compr_runtime_buffer(substream, dmab, size); + + return 1; +} + +int skl_substream_alloc_compr_pages(struct hdac_ext_bus *ebus, + struct snd_compr_stream *substream, + size_t size) +{ + struct hdac_ext_stream *stream = get_hdac_ext_compr_stream(substream); + int ret; + + hdac_stream(stream)->bufsize = 0; + hdac_stream(stream)->period_bytes = 0; + hdac_stream(stream)->format_val = 0; + + ret = skl_compr_malloc_pages(substream, ebus, size); + if (ret < 0) + return ret; + ebus->bus.io_ops->mark_pages_uc(snd_pcm_get_dma_buf(substream), true); + + return ret; +} + +int skl_compr_free_pages(struct snd_compr_stream *substream) +{ + struct snd_compr_runtime *runtime; + + runtime = substream->runtime; + if (runtime->dma_area == NULL) + return 0; + + if (runtime->dma_buffer_p != &substream->dma_buffer) { + /* it's a newly allocated buffer. release it now. */ + snd_dma_free_pages(runtime->dma_buffer_p); + kfree(runtime->dma_buffer_p); + } + + skl_set_compr_runtime_buffer(substream, NULL, 0); + return 0; +} + +int skl_substream_free_compr_pages(struct hdac_bus *bus, + struct snd_compr_stream *substream) +{ + bus->io_ops->mark_pages_uc((substream)->runtime->dma_buffer_p, false); + + return skl_compr_free_pages(substream); +} diff --git a/sound/soc/intel/skylake/skl-compress.h b/sound/soc/intel/skylake/skl-compress.h new file mode 100644 index 000000000000..9fcf6c38f5b8 --- /dev/null +++ b/sound/soc/intel/skylake/skl-compress.h @@ -0,0 +1,35 @@ + +/* + * skl-compress.h - Skylake compress header file + * + * Copyright (C) 2015-16 Intel Corp + * Author: Divya Prakash + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * 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; version 2 of the License. + * + * 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. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + */ + +inline +struct hdac_ext_stream *get_hdac_ext_compr_stream(struct snd_compr_stream *stream); +struct hdac_ext_bus *get_bus_compr_ctx(struct snd_compr_stream *substream); +void skl_set_compr_runtime_buffer(struct snd_compr_stream *substream, + struct snd_dma_buffer *bufp, size_t size); +int skl_compr_malloc_pages(struct snd_compr_stream *substream, + struct hdac_ext_bus *ebus, size_t size); +int skl_substream_alloc_compr_pages(struct hdac_ext_bus *ebus, + struct snd_compr_stream *substream, + size_t size); +int skl_compr_free_pages(struct snd_compr_stream *substream); +int skl_substream_free_compr_pages(struct hdac_bus *bus, + struct snd_compr_stream *substream); + diff --git a/sound/soc/intel/skylake/skl-messages.c b/sound/soc/intel/skylake/skl-messages.c index 4f88595815a8..fa8ef710900c 100644 --- a/sound/soc/intel/skylake/skl-messages.c +++ b/sound/soc/intel/skylake/skl-messages.c @@ -787,6 +787,30 @@ static void skl_set_copier_format(struct skl_sst *ctx, skl_setup_cpr_gateway_cfg(ctx, mconfig, cpr_mconfig); } +static void skl_setup_probe_gateway_cfg(struct skl_sst *ctx, + struct skl_module_cfg *mconfig, + struct skl_probe_cfg *probe_cfg) +{ + union skl_connector_node_id node_id = {0}; + struct skl_probe_config *pconfig = &ctx->probe_config; + + node_id.node.dma_type = pconfig->edma_type; + node_id.node.vindex = pconfig->edma_id; + probe_cfg->prb_cfg.dma_buffer_size = pconfig->edma_buffsize; + + memcpy(&(probe_cfg->prb_cfg.node_id), &node_id, sizeof(u32)); +} + +static void skl_set_probe_format(struct skl_sst *ctx, + struct skl_module_cfg *mconfig, + struct skl_probe_cfg *probe_mconfig) +{ + struct skl_base_cfg *base_cfg = (struct skl_base_cfg *)probe_mconfig; + + skl_set_base_module_format(ctx, mconfig, base_cfg); + skl_setup_probe_gateway_cfg(ctx, mconfig, probe_mconfig); +} + /* * Algo module are DSP pre processing modules. Algo module take base module * configuration and params @@ -839,6 +863,9 @@ static u16 skl_get_module_param_size(struct skl_sst *ctx, param_size += mconfig->formats_config.caps_size; return param_size; + case SKL_MODULE_TYPE_PROBE: + return sizeof(struct skl_probe_cfg); + case SKL_MODULE_TYPE_SRCINT: return sizeof(struct skl_src_module_cfg); @@ -893,6 +920,10 @@ static int skl_set_module_format(struct skl_sst *ctx, skl_set_copier_format(ctx, module_config, *param_data); break; + case SKL_MODULE_TYPE_PROBE: + skl_set_probe_format(ctx, module_config, *param_data); + break; + case SKL_MODULE_TYPE_SRCINT: skl_set_src_format(ctx, module_config, *param_data); break; @@ -1060,6 +1091,70 @@ int skl_init_module(struct skl_sst *ctx, return ret; } +int skl_init_probe_module(struct skl_sst *ctx, + struct skl_module_cfg *mconfig) +{ + u16 module_config_size = 0; + void *param_data = NULL; + int ret; + struct skl_ipc_init_instance_msg msg; + + dev_dbg(ctx->dev, "%s: module_id = %d instance=%d\n", __func__, + mconfig->id.module_id, mconfig->id.instance_id); + + + ret = skl_set_module_format(ctx, mconfig, + &module_config_size, ¶m_data); + if (ret < 0) { + dev_err(ctx->dev, "Failed to set module format ret=%d\n", ret); + return ret; + } + + msg.module_id = mconfig->id.module_id; + msg.instance_id = mconfig->id.instance_id; + msg.ppl_instance_id = -1; + msg.param_data_size = module_config_size; + msg.core_id = mconfig->core_id; + msg.domain = mconfig->domain; + + ret = skl_ipc_init_instance(&ctx->ipc, &msg, param_data); + if (ret < 0) { + dev_err(ctx->dev, "Failed to init instance ret=%d\n", ret); + kfree(param_data); + return ret; + } + mconfig->m_state = SKL_MODULE_INIT_DONE; + kfree(param_data); + return ret; +} + +int skl_uninit_probe_module(struct skl_sst *ctx, + struct skl_module_cfg *mconfig) +{ + u16 module_config_size = 0; + int ret; + struct skl_ipc_init_instance_msg msg; + + dev_dbg(ctx->dev, "%s: module_id = %d instance=%d\n", __func__, + mconfig->id.module_id, mconfig->id.instance_id); + + msg.module_id = mconfig->id.module_id; + msg.instance_id = mconfig->id.instance_id; + msg.ppl_instance_id = -1; + msg.param_data_size = module_config_size; + msg.core_id = mconfig->core_id; + msg.domain = mconfig->domain; + + ret = skl_ipc_delete_instance(&ctx->ipc, &msg); + if (ret < 0) { + dev_err(ctx->dev, "Failed to delete instance ret=%d\n", ret); + return ret; + } + mconfig->m_state = SKL_MODULE_UNINIT; + + return ret; +} + static void skl_dump_bind_info(struct skl_sst *ctx, struct skl_module_cfg *src_module, struct skl_module_cfg *dst_module) { @@ -1072,6 +1167,34 @@ static void skl_dump_bind_info(struct skl_sst *ctx, struct skl_module_cfg src_module->m_state, dst_module->m_state); } +int skl_disconnect_probe_point(struct skl_sst *ctx, + struct snd_soc_dapm_widget *w) +{ + struct skl_ipc_large_config_msg msg; + struct skl_probe_config *pconfig = &ctx->probe_config; + struct skl_module_cfg *mcfg; + int probe_point[8] = {0}; + int n = 0, i; + int no_of_extractor = pconfig->no_extractor; + + dev_dbg(ctx->dev, "Disconnecting probe\n"); + mcfg = w->priv; + msg.module_id = mcfg->id.module_id; + msg.instance_id = mcfg->id.instance_id; + msg.large_param_id = SKL_PROBE_DISCONNECT; + + for (i = 0; i < no_of_extractor; i++) { + if (pconfig->eprobe[i].set) { + probe_point[n] = pconfig->eprobe[i].id; + pconfig->eprobe[i].set = -1; + n++; + } + } + + msg.param_data_size = n * sizeof(u32); + return skl_ipc_set_large_config(&ctx->ipc, &msg, + probe_point); +} /* * On module freeup, we need to unbind the module with modules * it is already bind. diff --git a/sound/soc/intel/skylake/skl-pcm.c b/sound/soc/intel/skylake/skl-pcm.c index 388397d70be4..65a110664ff6 100644 --- a/sound/soc/intel/skylake/skl-pcm.c +++ b/sound/soc/intel/skylake/skl-pcm.c @@ -30,6 +30,7 @@ #include "skl-sst-ipc.h" #include "skl-sdw-pcm.h" #include "skl-fwlog.h" +#include "skl-probe.h" #define HDA_MONO 1 #define HDA_STEREO 2 @@ -826,13 +827,15 @@ static int skl_trace_compr_copy(struct snd_compr_stream *stream, char __user *dest, size_t count) { struct skl_sst *skl_sst = skl_get_sst_compr(stream); + struct snd_soc_pcm_runtime *rtd = stream->private_data; + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; struct sst_dsp *sst = skl_sst->dsp; int core = skl_get_compr_core(stream); if (skl_is_logging_core(core)) return skl_dsp_copy_log_user(sst, core, dest, count); else - return 0; + return skl_probe_compr_copy(stream, dest, count, cpu_dai); } static int skl_trace_compr_free(struct snd_compr_stream *stream, @@ -859,6 +862,15 @@ static struct snd_compr_ops skl_platform_compr_ops = { .copy = skl_trace_compr_copy, }; +static struct snd_soc_cdai_ops skl_probe_compr_ops = { + .startup = skl_probe_compr_open, + .shutdown = skl_probe_compr_close, + .trigger = skl_probe_compr_trigger, + .ack = skl_probe_compr_ack, + .pointer = skl_probe_compr_tstamp, + .set_params = skl_probe_compr_set_params, +}; + static struct snd_soc_cdai_ops skl_trace_compr_ops = { .shutdown = skl_trace_compr_free, .pointer = skl_trace_compr_tstamp, @@ -1010,6 +1022,24 @@ static struct snd_soc_dai_driver skl_fe_dai[] = { .sig_bits = 32, }, }, +{ + .name = "Compress Probe0 Pin", + .compress_new = snd_soc_new_compress, + .cops = &skl_probe_compr_ops, + .playback = { + .stream_name = "Probe Playback", + .channels_min = HDA_MONO, + }, +}, +{ + .name = "Compress Probe1 Pin", + .compress_new = snd_soc_new_compress, + .cops = &skl_probe_compr_ops, + .capture = { + .stream_name = "Probe Capture", + .channels_min = HDA_MONO, + }, +}, { .name = "LowLatency Pin", .ops = &skl_pcm_dai_ops, @@ -1692,6 +1722,29 @@ static int skl_populate_modules(struct skl *skl) return ret; } +static int skl_get_probe_widget(struct snd_soc_component *component, + struct skl *skl) +{ + struct skl_probe_config *pconfig = &skl->skl_sst->probe_config; + struct snd_soc_dapm_widget *w; + + list_for_each_entry(w, &component->card->widgets, list) { + if (is_skl_dsp_widget_type(w, skl->skl_sst->dev) && + (strstr(w->name, "probe") != NULL)) { + pconfig->w = w; + + dev_dbg(component->dev, "widget type=%d name=%s\n", + w->id, w->name); + break; + } + } + + pconfig->probe_count = 0; + pconfig->no_injector = 6; + pconfig->no_extractor = 8; + + return 0; +} static int skl_platform_soc_probe(struct snd_soc_component *component) { @@ -1746,6 +1799,8 @@ static int skl_platform_soc_probe(struct snd_soc_component *component) skl->cfg.astate_cfg->count, skl->cfg.astate_cfg); } + + skl_get_probe_widget(component, skl); } pm_runtime_mark_last_busy(component->dev); pm_runtime_put_autosuspend(component->dev); diff --git a/sound/soc/intel/skylake/skl-probe.c b/sound/soc/intel/skylake/skl-probe.c new file mode 100644 index 000000000000..7c206ec1b6a7 --- /dev/null +++ b/sound/soc/intel/skylake/skl-probe.c @@ -0,0 +1,324 @@ +/* + * skl-probe.c -ASoC HDA Platform driver file implementing probe functionality + * + * Copyright (C) 2015-2016 Intel Corp + * Author: Divya Prakash + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * 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; version 2 of the License. + * + * 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. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + */ + +#include +#include +#include +#include +#include "skl.h" +#include "skl-topology.h" +#include "skl-sst-ipc.h" +#include "skl-compress.h" + +#define USE_SPIB 0 + +static int set_injector_stream(struct hdac_ext_stream *stream, + struct snd_soc_dai *dai) +{ + /* + * In the case of injector probe since there can be multiple + * streams, we derive the injector stream number from the dai + * that is opened. + */ + struct skl *skl = get_skl_ctx(dai->dev); + struct skl_probe_config *pconfig = &skl->skl_sst->probe_config; + int i; + + if ((i = skl_get_probe_index(dai, pconfig)) != -1) { + pconfig->iprobe[i].stream = stream; + pconfig->iprobe[i].dma_id = + hdac_stream(stream)->stream_tag - 1; + } + return 0; +} + +int skl_probe_compr_open(struct snd_compr_stream *substream, + struct snd_soc_dai *dai) +{ + struct hdac_ext_bus *ebus = dev_get_drvdata(dai->dev); + struct hdac_ext_stream *stream = NULL; + struct snd_compr_runtime *runtime = substream->runtime; + struct skl *skl = get_skl_ctx(dai->dev); + struct skl_probe_config *pconfig = &skl->skl_sst->probe_config; + + dev_dbg(dai->dev, "%s dev is %s\n", __func__, dev_name(dai->dev)); + + if (!pconfig->probe_count) { + /*TODO: Configuring the right DMA buffer size*/ + pconfig->edma_buffsize = 832; + pconfig->edma_type = SKL_DMA_HDA_HOST_INPUT_CLASS; + pconfig->estream = hdac_ext_host_stream_compr_assign(ebus, + substream, + SND_COMPRESS_CAPTURE); + pconfig->edma_id = hdac_stream(pconfig->estream)->stream_tag - 1; + } + + if (substream->direction == SND_COMPRESS_PLAYBACK) { + stream = hdac_ext_host_stream_compr_assign(ebus, substream, + SND_COMPRESS_PLAYBACK); + set_injector_stream(stream, dai); + runtime->private_data = stream; + + } else if (substream->direction == SND_COMPRESS_CAPTURE) { + stream = pconfig->estream; + runtime->private_data = pconfig->estream; + } + + if (stream == NULL) { + dev_err(dai->dev, "stream = NULL\n"); + return -EBUSY; + } + + hdac_stream(stream)->curr_pos = 0; + + return 0; +} + +int skl_probe_compr_set_params(struct snd_compr_stream *substream, + struct snd_compr_params *params, + struct snd_soc_dai *dai) +{ + + struct hdac_ext_bus *ebus = dev_get_drvdata(dai->dev); + struct hdac_ext_stream *stream = get_hdac_ext_compr_stream(substream); + struct snd_compr_runtime *runtime = substream->runtime; + struct skl *skl = get_skl_ctx(dai->dev); + struct skl_probe_config *pconfig = &skl->skl_sst->probe_config; + struct skl_module_cfg *mconfig = pconfig->w->priv; + int ret, dma_id; + unsigned int format_val = 0; + int err; + + dev_dbg(dai->dev, "%s: %s\n", __func__, dai->name); + ret = skl_substream_alloc_compr_pages(ebus, substream, + runtime->fragments*runtime->fragment_size); + if (ret < 0) + return ret; + + dma_id = hdac_stream(stream)->stream_tag - 1; + dev_dbg(dai->dev, "dma_id=%d\n", dma_id); + + if (hdac_stream(stream)->prepared) { + dev_dbg(dai->dev, "already stream is prepared - returning\n"); + return 0; + } + + snd_hdac_stream_reset(hdac_stream(stream)); + + err = snd_hdac_stream_set_params(hdac_stream(stream), format_val); + if (err < 0) + return err; + + err = snd_hdac_stream_setup(hdac_stream(stream)); + if (err < 0) { + dev_err(dai->dev, "snd_hdac_stream_setup err = %d\n", err); + return err; + } + + hdac_stream(stream)->prepared = 1; + + /* Initialize probe module only the first time */ + if (!pconfig->probe_count) { + + ret = skl_init_probe_module(skl->skl_sst, mconfig); + if (ret < 0) + return ret; + } + + if (substream->direction == SND_COMPRESS_PLAYBACK) + skl_tplg_attach_probe_dma(pconfig->w, skl->skl_sst, dai); + + skl_tplg_set_probe_params(pconfig->w, skl->skl_sst, substream->direction, dai); + pconfig->probe_count++; + +#if USE_SPIB + snd_hdac_ext_stream_spbcap_enable(ebus, 1, hdac_stream(stream)->index); +#endif + return 0; +} + +int skl_probe_compr_close(struct snd_compr_stream *substream, + struct snd_soc_dai *dai) +{ + struct hdac_ext_stream *stream = get_hdac_ext_compr_stream(substream); + struct hdac_ext_bus *ebus = dev_get_drvdata(dai->dev); + struct skl *skl = get_skl_ctx(dai->dev); + struct skl_probe_config *pconfig = &skl->skl_sst->probe_config; + int ret; + + dev_dbg(dai->dev, "%s: %s\n", __func__, dai->name); +#if USE_SPIB + snd_hdac_ext_stream_spbcap_enable(ebus, 0, hdac_stream(stream)->index); +#endif + + if (!--pconfig->probe_count) { + skl_disconnect_probe_point(skl->skl_sst, pconfig->w); + ret = skl_uninit_probe_module(skl->skl_sst, pconfig->w->priv); + if (ret < 0) + return ret; + } + + snd_hdac_stream_cleanup(hdac_stream(stream)); + hdac_stream(stream)->prepared = 0; + + skl_substream_free_compr_pages(ebus_to_hbus(ebus), substream); + + snd_hdac_ext_stream_release(stream, HDAC_EXT_STREAM_TYPE_HOST); + + return 0; +} + +int skl_probe_compr_ack(struct snd_compr_stream *substream, size_t bytes, + struct snd_soc_dai *dai) +{ + struct hdac_ext_bus *ebus = dev_get_drvdata(dai->dev); + struct hdac_bus *bus = ebus_to_hbus(ebus); + u64 new_spib_pos; + struct snd_compr_runtime *runtime = substream->runtime; + u64 spib_pos = div64_u64(runtime->total_bytes_available, + runtime->buffer_size); + + spib_pos = runtime->total_bytes_available - + (spib_pos * runtime->buffer_size); + /*SPIB position is a wrap around counter that indicates + the position relative to the buffer start address*/ + new_spib_pos = (spib_pos + bytes) % runtime->buffer_size; + + if (!bus->spbcap) { + dev_err(dai->dev, "Address of SPB capability is NULL"); + return -EINVAL; + } +#if USE_SPIB + writel(new_spib_pos, stream->spib_addr); +#endif + return 0; +} + +int skl_probe_compr_tstamp(struct snd_compr_stream *stream, + struct snd_compr_tstamp *tstamp, struct snd_soc_dai *dai) +{ + struct hdac_ext_stream *hstream = get_hdac_ext_compr_stream(stream); + + tstamp->copied_total = hstream->hstream.curr_pos; + + return 0; + +} + +int skl_probe_compr_copy(struct snd_compr_stream *stream, char __user *buf, + size_t count, struct snd_soc_dai *dai) +{ + int offset = 0, availcount = 0, retval = 0, copy; + void *dstn; + + if (stream->direction == SND_COMPRESS_CAPTURE) { + offset = stream->runtime->total_bytes_transferred % + stream->runtime->buffer_size; + dstn = stream->runtime->dma_area + offset; + availcount = (stream->runtime->buffer_size - offset); + if (count > availcount) { + + retval = copy_to_user(buf, dstn, availcount); + retval += copy_to_user(buf + availcount, + stream->runtime->dma_area, + count - availcount); + } else + retval = copy_to_user(buf, stream->runtime->dma_area + + offset, count); + + if (!retval) + retval = count; + else + retval = count - retval; + + } else if (stream->direction == SND_COMPRESS_PLAYBACK) { + + offset = stream->runtime->total_bytes_available % + stream->runtime->buffer_size; + dstn = stream->runtime->dma_area + offset; + + if (count < stream->runtime->buffer_size - offset) + retval = copy_from_user(dstn, buf, count); + else { + copy = stream->runtime->buffer_size - offset; + retval = copy_from_user(dstn, buf, copy); + retval += copy_from_user(stream->runtime->dma_area, + buf + copy, count - copy); + } + if (!retval) + retval = count; + else + retval = count - retval; + } + +#if USE_SPIB + spib_pos = (offset + retval)%stream->runtime->dma_bytes; + snd_hdac_ext_stream_set_spib(ebus, estream, spib_pos); +#endif + + return retval; + +} + +int skl_probe_compr_trigger(struct snd_compr_stream *substream, int cmd, + struct snd_soc_dai *dai) +{ + struct hdac_ext_bus *ebus = get_bus_compr_ctx(substream); + struct hdac_bus *bus = ebus_to_hbus(ebus); + struct hdac_ext_stream *stream; + struct hdac_stream *hstr; + int start; + unsigned long cookie; + + stream = get_hdac_ext_compr_stream(substream); + hstr = hdac_stream(stream); + + if (!hstr->prepared) + return -EPIPE; + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + case SNDRV_PCM_TRIGGER_RESUME: + start = 1; + break; + + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + case SNDRV_PCM_TRIGGER_SUSPEND: + case SNDRV_PCM_TRIGGER_STOP: + start = 0; + break; + + default: + return -EINVAL; + } + + spin_lock_irqsave(&bus->reg_lock, cookie); + + if (start) + snd_hdac_stream_start(hdac_stream(stream), true); + else + snd_hdac_stream_stop(hdac_stream(stream)); + + spin_unlock_irqrestore(&bus->reg_lock, cookie); + + return 0; +} diff --git a/sound/soc/intel/skylake/skl-probe.h b/sound/soc/intel/skylake/skl-probe.h new file mode 100644 index 000000000000..a7b8f34712a2 --- /dev/null +++ b/sound/soc/intel/skylake/skl-probe.h @@ -0,0 +1,37 @@ + +/* + * skl-probe.h - Skylake probe header file + * + * Copyright (C) 2015-16 Intel Corp + * Author: Divya Prakash + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * 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; version 2 of the License. + * + * 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. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + */ + +int skl_probe_compr_open(struct snd_compr_stream *substream, + struct snd_soc_dai *dai); + +int skl_probe_compr_set_params(struct snd_compr_stream *substream, + struct snd_compr_params *params, struct snd_soc_dai *dai); + +int skl_probe_compr_tstamp(struct snd_compr_stream *stream, + struct snd_compr_tstamp *tstamp, struct snd_soc_dai *dai); +int skl_probe_compr_close(struct snd_compr_stream *substream, + struct snd_soc_dai *dai); +int skl_probe_compr_ack(struct snd_compr_stream *substream, size_t bytes, + struct snd_soc_dai *dai); +int skl_probe_compr_copy(struct snd_compr_stream *stream, char __user *buf, + size_t count, struct snd_soc_dai *dai); +int skl_probe_compr_trigger(struct snd_compr_stream *substream, int cmd, + struct snd_soc_dai *dai); diff --git a/sound/soc/intel/skylake/skl-sst-ipc.h b/sound/soc/intel/skylake/skl-sst-ipc.h index eb030dfb88ca..0437e4cf1261 100644 --- a/sound/soc/intel/skylake/skl-sst-ipc.h +++ b/sound/soc/intel/skylake/skl-sst-ipc.h @@ -18,11 +18,15 @@ #include #include "../common/sst-ipc.h" +#include "skl-sst-dsp.h" struct sst_dsp; struct skl_sst; struct sst_generic_ipc; +#define NO_OF_INJECTOR 6 +#define NO_OF_EXTRACTOR 8 + enum skl_ipc_pipeline_state { PPL_INVALID_STATE = 0, PPL_UNINITIALIZED = 1, @@ -75,6 +79,32 @@ struct skl_lib_info { const struct firmware *fw; }; +struct injector_data { + int set; + int id; + struct hdac_ext_stream *stream; + int dma_id; + int dma_buf_size; +}; + +struct extractor_data { + int set; + int id; +}; + +struct skl_probe_config { + struct snd_soc_dapm_widget *w; + int probe_count; + int edma_id; + int edma_type; + int edma_buffsize; + int no_extractor; + int no_injector; + struct hdac_ext_stream *estream; + struct injector_data iprobe[NO_OF_INJECTOR]; + struct extractor_data eprobe[NO_OF_EXTRACTOR]; +}; + struct skl_sst { struct device *dev; struct sst_dsp *dsp; @@ -126,6 +156,7 @@ struct skl_sst { int num_sdw_controllers; /* Array of sdw masters */ struct sdw_master *mstr; + struct skl_probe_config probe_config; }; struct skl_ipc_init_instance_msg { diff --git a/sound/soc/intel/skylake/skl-topology.c b/sound/soc/intel/skylake/skl-topology.c index 35c0ec938a18..1d02c88da5e4 100644 --- a/sound/soc/intel/skylake/skl-topology.c +++ b/sound/soc/intel/skylake/skl-topology.c @@ -96,7 +96,7 @@ void skl_tplg_d0i3_put(struct skl *skl, enum d0i3_capability caps) * SKL DSP driver modelling uses only few DAPM widgets so for rest we will * ignore. This helpers checks if the SKL driver handles this widget type */ -static int is_skl_dsp_widget_type(struct snd_soc_dapm_widget *w, +int is_skl_dsp_widget_type(struct snd_soc_dapm_widget *w, struct device *dev) { if (w->dapm->dev != dev) @@ -479,12 +479,111 @@ static void skl_tplg_update_module_params(struct snd_soc_dapm_widget *w, skl_dump_mconfig(ctx, m_cfg); } +int skl_get_probe_index(struct snd_soc_dai *dai, + struct skl_probe_config *pconfig) +{ + int i, ret = -1; + char pos[4]; + + for (i = 0; i < pconfig->no_injector; i++) { + snprintf(pos, 4, "%d", i); + if (strstr(dai->name, pos)) + return i; + } + return ret; +} + +int skl_tplg_attach_probe_dma(struct snd_soc_dapm_widget *w, + struct skl_sst *ctx, struct snd_soc_dai *dai) +{ + int i, ret; + struct skl_module_cfg *mconfig = w->priv; + struct skl_attach_probe_dma ad; + struct skl_probe_config *pconfig = &ctx->probe_config; + + if ((i = skl_get_probe_index(dai, pconfig)) != -1) { + ad.node_id.node.vindex = pconfig->iprobe[i].dma_id; + ad.node_id.node.dma_type = SKL_DMA_HDA_HOST_OUTPUT_CLASS; + ad.node_id.node.rsvd = 0; + ad.dma_buff_size = 1536;/* TODO:Configure based on calculation*/ + } + + ret = skl_set_module_params(ctx, (u32 *)&ad, + sizeof(struct skl_attach_probe_dma), 1, mconfig); + return ret; + +} + +int skl_tplg_set_probe_params(struct snd_soc_dapm_widget *w, + struct skl_sst *ctx, int direction, + struct snd_soc_dai *dai) +{ + int i, ret = 0, n = 0; + struct skl_module_cfg *mconfig = w->priv; + const struct snd_kcontrol_new *k; + struct soc_bytes_ext *sb; + struct skl_probe_data *bc; + struct skl_probe_config *pconfig = &ctx->probe_config; + struct probe_pt_param prb_pt_param[8] = {{0}}; + + if (direction == SND_COMPRESS_PLAYBACK) { + + /* only one injector point can be set at a time*/ + n = skl_get_probe_index(dai, pconfig); + k = &w->kcontrol_news[pconfig->no_extractor + n]; + + if (k->access & SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK) { + sb = (void *) k->private_value; + bc = (struct skl_probe_data *)sb->dobj.private; + pr_debug("bc->is_ext_inj = %d, bc->params = %d, bc->is_connect = %d \n", + bc->is_ext_inj, bc->params, bc->is_connect); + if (!(bc->is_ext_inj == SKL_PROBE_INJECT || + bc->is_ext_inj == SKL_PROBE_INJECT_REEXTRACT)) + return -EINVAL; + + prb_pt_param[0].params = (int)bc->params; + prb_pt_param[0].connection = bc->is_ext_inj; + prb_pt_param[0].node_id = pconfig->iprobe[n].dma_id; + ret = skl_set_module_params(ctx, (void *)prb_pt_param, sizeof(struct probe_pt_param), + bc->is_connect, mconfig); + } + + } else if (direction == SND_COMPRESS_CAPTURE) { + + /*multiple extractor points can be set simultaneously*/ + for (i = 0; i < pconfig->no_extractor; i++) { + k = &w->kcontrol_news[i]; + if (k->access & SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK) { + sb = (void *) k->private_value; + bc = (struct skl_probe_data *)sb->dobj.private; + + pr_debug("bc->is_ext_inj = %d, bc->params = %d, bc->is_connect = %d \n", + bc->is_ext_inj, bc->params, bc->is_connect); + if (bc->is_ext_inj == SKL_PROBE_EXTRACT && + pconfig->eprobe[i].set == 1) { + pr_debug("Retrieving the exractor params \n"); + prb_pt_param[n].params = (int)bc->params; + prb_pt_param[n].connection = bc->is_ext_inj; + prb_pt_param[n].node_id = -1; + n++; + } + } + } + + if (n > 0) + ret = skl_set_module_params(ctx, (void *)prb_pt_param, n * sizeof(struct probe_pt_param), + SKL_PROBE_CONNECT, mconfig); + + } + return ret; +} + /* * some modules can have multiple params set from user control and * need to be set after module is initialized. If set_param flag is * set module params will be done after module is initialised. */ -static int skl_tplg_set_module_params(struct snd_soc_dapm_widget *w, +int skl_tplg_set_module_params(struct snd_soc_dapm_widget *w, struct skl_sst *ctx) { int i, ret; @@ -1691,6 +1790,121 @@ static void skl_tplg_fill_dma_id(struct skl_module_cfg *mcfg, memcpy(pipe->p_params, params, sizeof(*params)); } } +static int skl_cache_probe_param(struct snd_kcontrol *kctl, + struct skl_probe_data *ap, struct skl_sst *ctx) +{ + struct skl_probe_config *pconfig = &ctx->probe_config; + union skl_connector_node_id node_id = {-1}; + int index = -1, i; + char buf[20], pos[10]; + + if (ap->is_ext_inj == SKL_PROBE_EXTRACT) { + /* From the control ID get the extractor index */ + for (i = 0; i < pconfig->no_extractor; i++) { + strcpy(buf, "Extractor"); + snprintf(pos, 4, "%d", i); + if (strstr(kctl->id.name, strcat(buf, pos))) { + index = i; + break; + } + } + pr_debug("Setting extractor probe index %d\n", index); + memcpy(&ap->node_id, &node_id, sizeof(u32)); + pconfig->eprobe[index].id = ap->params; + if (ap->is_connect == SKL_PROBE_CONNECT) + pconfig->eprobe[index].set = 1; + else if (ap->is_connect == SKL_PROBE_DISCONNECT) + pconfig->eprobe[index].set = -1; + + } else { + /* From the control ID get the injector index */ + for (i = 0; i < pconfig->no_injector; i++) { + strcpy(buf, "Injector"); + snprintf(pos, 4, "%d", i); + if (strstr(kctl->id.name, strcat(buf, pos))) { + index = i; + break; + } + } + pconfig->iprobe[index].id = ap->params; + node_id.node.dma_type = SKL_DMA_HDA_HOST_OUTPUT_CLASS; + node_id.node.vindex = pconfig->iprobe[index].dma_id; + memcpy(&ap->node_id, &node_id, sizeof(u32)); + if (ap->is_connect == SKL_PROBE_CONNECT) + pconfig->iprobe[index].set = 1; + else if (ap->is_connect == SKL_PROBE_DISCONNECT) + pconfig->iprobe[index].set = -1; + } + return 0; +} + +static int skl_tplg_tlv_probe_set(struct snd_kcontrol *kcontrol, + const unsigned int __user *data, unsigned int size) +{ + struct snd_soc_dapm_context *dapm = + snd_soc_dapm_kcontrol_dapm(kcontrol); + struct snd_soc_dapm_widget *w = snd_soc_dapm_kcontrol_widget(kcontrol); + struct skl_module_cfg *mconfig = w->priv; + struct soc_bytes_ext *sb = (void *) kcontrol->private_value; + struct skl_probe_data *ap = (struct skl_probe_data *)sb->dobj.private; + struct skl *skl = get_skl_ctx(dapm->dev); + struct skl_probe_config *pconfig = &skl->skl_sst->probe_config; + struct probe_pt_param connect_point; + int disconnect_point; + void *offset; + + dev_dbg(dapm->dev, "in %s control=%s\n", __func__, kcontrol->id.name); + dev_dbg(dapm->dev, "size = %u, %#x\n", size, size); + + if (data) { + offset = (unsigned char *)data; + offset += 2 * sizeof(u32); /* To skip TLV heeader */ + if (copy_from_user(&ap->is_connect, + offset, sizeof(ap->is_connect))) + return -EIO; + + offset += sizeof(ap->is_connect); + if (copy_from_user(&ap->is_ext_inj, + offset, sizeof(ap->is_ext_inj))) + return -EIO; + + offset += sizeof(ap->is_ext_inj); + if (copy_from_user(&ap->params, + offset, sizeof(ap->params))) + return -EIO; + + dev_dbg(dapm->dev, "connect state = %d, extract_inject = %d, params = %d \n", + ap->is_connect, ap->is_ext_inj, ap->params); + + skl_cache_probe_param(kcontrol, ap, skl->skl_sst); + + if (pconfig->probe_count) { + /* In the case of extraction, additional probe points can be set when + * the stream is in progress and the driver can immediately send the + * connect IPC. But in the case of injector, for each probe point + * connection a new stream with the DAI number corresponding to that + * control has to be opened. Hence below check ensures that the + * connect IPC is sent only in case of extractor. + */ + if ((ap->is_connect == SKL_PROBE_CONNECT) + && (ap->is_ext_inj == SKL_PROBE_EXTRACT)) { + + memcpy(&connect_point.params, &ap->params, sizeof(u32)); + connect_point.connection = ap->is_ext_inj; + memcpy(&connect_point.node_id, (&ap->node_id), sizeof(u32)); + return skl_set_module_params(skl->skl_sst, (void *)&connect_point, + sizeof(struct probe_pt_param), ap->is_connect, mconfig); + + } else if (ap->is_connect == SKL_PROBE_DISCONNECT) { + + disconnect_point = (int)ap->params; + return skl_set_module_params(skl->skl_sst, (void *)&disconnect_point, + sizeof(disconnect_point), ap->is_connect, mconfig); + } + } + } + return 0; +} /* * The FE params are passed by hw_params of the DAI. @@ -2059,6 +2273,8 @@ static const struct snd_soc_tplg_widget_events skl_tplg_widget_ops[] = { static const struct snd_soc_tplg_bytes_ext_ops skl_tlv_ops[] = { {SKL_CONTROL_TYPE_BYTE_TLV, skl_tplg_tlv_control_get, skl_tplg_tlv_control_set}, + {SKL_CONTROL_TYPE_BYTE_PROBE, skl_tplg_tlv_control_get, + skl_tplg_tlv_probe_set}, }; static const struct snd_soc_tplg_kcontrol_ops skl_tplg_kcontrol_ops[] = { diff --git a/sound/soc/intel/skylake/skl-topology.h b/sound/soc/intel/skylake/skl-topology.h index 5998e6926df3..94152704e22e 100644 --- a/sound/soc/intel/skylake/skl-topology.h +++ b/sound/soc/intel/skylake/skl-topology.h @@ -96,6 +96,11 @@ enum skl_widget_type { SKL_WIDGET_PGA = 3, SKL_WIDGET_MUX = 4 }; +struct probe_pt_param { + u32 params; + u32 connection; + u32 node_id; +}; struct skl_audio_data_format { enum skl_s_freq s_freq; @@ -117,6 +122,16 @@ struct skl_base_cfg { struct skl_audio_data_format audio_fmt; }; +struct skl_probe_gtw_cfg { + u32 node_id; + u32 dma_buffer_size; +} __packed; + +struct skl_probe_cfg { + struct skl_base_cfg base_cfg; + struct skl_probe_gtw_cfg prb_cfg; +} __packed; + struct skl_cpr_gtw_cfg { u32 node_id; u32 dma_buffer_size; @@ -448,6 +463,17 @@ struct skl_algo_data { char *params; }; +struct skl_probe_data { + u8 is_connect; + u32 is_ext_inj; + u32 params; + u32 node_id; +} __packed; + +struct skl_attach_probe_dma { + union skl_connector_node_id node_id; + u32 dma_buff_size; +} __packed; struct skl_pipeline { struct skl_pipe *pipe; struct list_head node; @@ -479,6 +505,7 @@ static inline struct skl *get_skl_ctx(struct device *dev) return bus_to_skl(bus); } +struct skl_probe_config; int skl_tplg_be_update_params(struct snd_soc_dai *dai, struct skl_pipe_params *params); int skl_dsp_set_dma_control(struct skl_sst *ctx, u32 *caps, @@ -509,12 +536,28 @@ int skl_reset_pipe(struct skl_sst *ctx, struct skl_pipe *pipe); int skl_init_module(struct skl_sst *ctx, struct skl_module_cfg *module_config); +int skl_init_probe_module(struct skl_sst *ctx, struct skl_module_cfg *module_config); + +int skl_uninit_probe_module(struct skl_sst *ctx, struct skl_module_cfg *module_config); + +int skl_get_probe_index(struct snd_soc_dai *dai, + struct skl_probe_config *pconfig); + +int skl_tplg_attach_probe_dma(struct snd_soc_dapm_widget *w, + struct skl_sst *ctx, struct snd_soc_dai *dai); +int skl_tplg_set_probe_params(struct snd_soc_dapm_widget *w, + struct skl_sst *ctx, int direction, + struct snd_soc_dai *dai); +int skl_tplg_set_module_params(struct snd_soc_dapm_widget *w, + struct skl_sst *ctx); + int skl_bind_modules(struct skl_sst *ctx, struct skl_module_cfg *src_module, struct skl_module_cfg *dst_module); int skl_unbind_modules(struct skl_sst *ctx, struct skl_module_cfg *src_module, struct skl_module_cfg *dst_module); - +int skl_disconnect_probe_point(struct skl_sst *ctx, + struct snd_soc_dapm_widget *w); int skl_set_module_params(struct skl_sst *ctx, u32 *params, int size, u32 param_id, struct skl_module_cfg *mcfg); int skl_get_module_params(struct skl_sst *ctx, u32 *params, int size, @@ -522,6 +565,9 @@ int skl_get_module_params(struct skl_sst *ctx, u32 *params, int size, struct skl_module_cfg *skl_tplg_be_get_cpr_module(struct snd_soc_dai *dai, int stream); + +int is_skl_dsp_widget_type(struct snd_soc_dapm_widget *w, + struct device *dev); enum skl_bitdepth skl_get_bit_depth(int params); int skl_pcm_host_dma_prepare(struct device *dev, struct skl_pipe_params *params); diff --git a/sound/soc/intel/skylake/skl.c b/sound/soc/intel/skylake/skl.c index 2d0efe8233a4..feb4ab5fc9b4 100644 --- a/sound/soc/intel/skylake/skl.c +++ b/sound/soc/intel/skylake/skl.c @@ -33,6 +33,7 @@ #include #include #include +#include #include "skl.h" #include "skl-sst-dsp.h" #include "skl-sst-ipc.h" @@ -185,10 +186,30 @@ void skl_update_d0i3c(struct device *dev, bool enable) snd_hdac_chip_readb(bus, VS_D0I3C)); } +static void skl_get_total_bytes_transferred(struct hdac_stream *hstr) +{ + int pos, prev_pos, no_of_bytes; + + prev_pos = hstr->curr_pos % hstr->stream->runtime->buffer_size; + pos = snd_hdac_stream_get_pos_posbuf(hstr); + + if (pos < prev_pos) + no_of_bytes = (hstr->stream->runtime->buffer_size - prev_pos) + pos; + else + no_of_bytes = pos - prev_pos; + + hstr->curr_pos += no_of_bytes; +} + /* called from IRQ */ static void skl_stream_update(struct hdac_bus *bus, struct hdac_stream *hstr) { - snd_pcm_period_elapsed(hstr->substream); + if (hstr->substream) + snd_pcm_period_elapsed(hstr->substream); + else if (hstr->stream) { + skl_get_total_bytes_transferred(hstr); + snd_compr_fragment_elapsed(hstr->stream); + } } static irqreturn_t skl_interrupt(int irq, void *dev_id) From 05eb880fabe73f7c4f3ab1bc0d3e22991c76c1c5 Mon Sep 17 00:00:00 2001 From: G Kranthi Date: Tue, 10 May 2016 11:11:09 +0530 Subject: [PATCH 0673/1103] ASoC: Intel: Skylake: KW fixes for probe feature This patch will add fixes for below klocwork errors, 1. Array 'eprobe' of size 8 may use index value(s) -1. 2. Array 'iprobe' of size 6 may use index value(s) -1. 3. Pointer 'pconfig->estream' returned from call to function 'hdac_ext_host_stream_compr_assign' at line 68 may be NULL and will be dereferenced at line 71. Change-Id: I407d9b2758addfd78508f96378a00d583c5d8110 Signed-off-by: G Kranthi Reviewed-on: Reviewed-by: B, Jayachandran Reviewed-by: Babu, Ramesh Tested-by: Babu, Ramesh --- sound/soc/intel/skylake/skl-probe.c | 8 +++++++- sound/soc/intel/skylake/skl-topology.c | 16 +++++++++++++++- 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/sound/soc/intel/skylake/skl-probe.c b/sound/soc/intel/skylake/skl-probe.c index 7c206ec1b6a7..7c6e3779c213 100644 --- a/sound/soc/intel/skylake/skl-probe.c +++ b/sound/soc/intel/skylake/skl-probe.c @@ -68,6 +68,9 @@ int skl_probe_compr_open(struct snd_compr_stream *substream, pconfig->estream = hdac_ext_host_stream_compr_assign(ebus, substream, SND_COMPRESS_CAPTURE); + if (!pconfig->estream) + return -EINVAL; + pconfig->edma_id = hdac_stream(pconfig->estream)->stream_tag - 1; } @@ -146,7 +149,10 @@ int skl_probe_compr_set_params(struct snd_compr_stream *substream, if (substream->direction == SND_COMPRESS_PLAYBACK) skl_tplg_attach_probe_dma(pconfig->w, skl->skl_sst, dai); - skl_tplg_set_probe_params(pconfig->w, skl->skl_sst, substream->direction, dai); + ret = skl_tplg_set_probe_params(pconfig->w, skl->skl_sst, substream->direction, dai); + if (ret < 0) + return -EINVAL; + pconfig->probe_count++; #if USE_SPIB diff --git a/sound/soc/intel/skylake/skl-topology.c b/sound/soc/intel/skylake/skl-topology.c index 1d02c88da5e4..3ef44ba263c4 100644 --- a/sound/soc/intel/skylake/skl-topology.c +++ b/sound/soc/intel/skylake/skl-topology.c @@ -530,6 +530,9 @@ int skl_tplg_set_probe_params(struct snd_soc_dapm_widget *w, /* only one injector point can be set at a time*/ n = skl_get_probe_index(dai, pconfig); + if (n < 0) + return -EINVAL; + k = &w->kcontrol_news[pconfig->no_extractor + n]; if (k->access & SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK) { @@ -1808,6 +1811,10 @@ static int skl_cache_probe_param(struct snd_kcontrol *kctl, break; } } + + if (index < 0) + return -EINVAL; + pr_debug("Setting extractor probe index %d\n", index); memcpy(&ap->node_id, &node_id, sizeof(u32)); pconfig->eprobe[index].id = ap->params; @@ -1826,6 +1833,10 @@ static int skl_cache_probe_param(struct snd_kcontrol *kctl, break; } } + + if (index < 0) + return -EINVAL; + pconfig->iprobe[index].id = ap->params; node_id.node.dma_type = SKL_DMA_HDA_HOST_OUTPUT_CLASS; node_id.node.vindex = pconfig->iprobe[index].dma_id; @@ -1852,6 +1863,7 @@ static int skl_tplg_tlv_probe_set(struct snd_kcontrol *kcontrol, struct probe_pt_param connect_point; int disconnect_point; void *offset; + int ret; dev_dbg(dapm->dev, "in %s control=%s\n", __func__, kcontrol->id.name); dev_dbg(dapm->dev, "size = %u, %#x\n", size, size); @@ -1876,7 +1888,9 @@ static int skl_tplg_tlv_probe_set(struct snd_kcontrol *kcontrol, dev_dbg(dapm->dev, "connect state = %d, extract_inject = %d, params = %d \n", ap->is_connect, ap->is_ext_inj, ap->params); - skl_cache_probe_param(kcontrol, ap, skl->skl_sst); + ret = skl_cache_probe_param(kcontrol, ap, skl->skl_sst); + if (ret < 0) + return -EINVAL; if (pconfig->probe_count) { /* In the case of extraction, additional probe points can be set when From 3a89d30d262addd2c64b562c16bed3835676cae7 Mon Sep 17 00:00:00 2001 From: "Pawse, GuruprasadX" Date: Tue, 14 Jun 2016 18:19:44 +0530 Subject: [PATCH 0674/1103] ASoC: Intel: Skylake: Probe - Start DMA before setting probe params FW starts probe module soon after its params are set. But to avoid xruns from FW point of view, DMA has to be started before this. So, reorder the sequence as below so that there is no initial packet loss in the captured data: 1. Call skl_probe_compr_trigger(START) which starts the DMA. 2. Call skl_tplg_set_probe_params() which tells the DSP to start the probe stream. Change-Id: I0a23ded05ca60ab0e35e53c784681ebf502f138a Signed-off-by: Pawse, GuruprasadX Reviewed-on: Reviewed-by: Babu, Ramesh Tested-by: Babu, Ramesh --- sound/soc/intel/skylake/skl-probe.c | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/sound/soc/intel/skylake/skl-probe.c b/sound/soc/intel/skylake/skl-probe.c index 7c6e3779c213..7d2379806daf 100644 --- a/sound/soc/intel/skylake/skl-probe.c +++ b/sound/soc/intel/skylake/skl-probe.c @@ -149,10 +149,6 @@ int skl_probe_compr_set_params(struct snd_compr_stream *substream, if (substream->direction == SND_COMPRESS_PLAYBACK) skl_tplg_attach_probe_dma(pconfig->w, skl->skl_sst, dai); - ret = skl_tplg_set_probe_params(pconfig->w, skl->skl_sst, substream->direction, dai); - if (ret < 0) - return -EINVAL; - pconfig->probe_count++; #if USE_SPIB @@ -293,6 +289,9 @@ int skl_probe_compr_trigger(struct snd_compr_stream *substream, int cmd, struct hdac_stream *hstr; int start; unsigned long cookie; + struct skl *skl = get_skl_ctx(dai->dev); + struct skl_probe_config *pconfig = &skl->skl_sst->probe_config; + int ret; stream = get_hdac_ext_compr_stream(substream); hstr = hdac_stream(stream); @@ -326,5 +325,14 @@ int skl_probe_compr_trigger(struct snd_compr_stream *substream, int cmd, spin_unlock_irqrestore(&bus->reg_lock, cookie); + if (start) { + /* FW starts probe module soon after its params are set. + * So to avoid xruns, start DMA first and then set probe params. + */ + ret = skl_tplg_set_probe_params(pconfig->w, skl->skl_sst, substream->direction, dai); + if (ret < 0) + return -EINVAL; + } + return 0; } From 845b8bf154139d8d5efa954832f4f3ee47065f78 Mon Sep 17 00:00:00 2001 From: Jayachandran B Date: Tue, 14 Jun 2016 10:39:48 +0530 Subject: [PATCH 0675/1103] ASoC: Intel: Skylake: Probe-Limit the bytes to copy If userspace happens to issue a copy with count > ring buffer size, limit the count to the allocated ring buffer size to avoid out of bound access into the buffer. Change-Id: I7acbfb64bda299237a9d56bbac4a022d36b28bfd Signed-off-by: Jayachandran B Reviewed-on: Reviewed-by: Babu, Ramesh Tested-by: Babu, Ramesh --- sound/soc/intel/skylake/skl-probe.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/sound/soc/intel/skylake/skl-probe.c b/sound/soc/intel/skylake/skl-probe.c index 7d2379806daf..507513eabbd6 100644 --- a/sound/soc/intel/skylake/skl-probe.c +++ b/sound/soc/intel/skylake/skl-probe.c @@ -230,6 +230,12 @@ int skl_probe_compr_copy(struct snd_compr_stream *stream, char __user *buf, { int offset = 0, availcount = 0, retval = 0, copy; void *dstn; + /* + * If userspace happens to issue a copy with count > ring buffer size, + * limit the count to the allocated ring buffer size. + */ + if (count > stream->runtime->buffer_size) + count = stream->runtime->buffer_size; if (stream->direction == SND_COMPRESS_CAPTURE) { offset = stream->runtime->total_bytes_transferred % From a96a4e8c6da580cfdadb03542b7b1a6a439879c1 Mon Sep 17 00:00:00 2001 From: "Pawse, GuruprasadX" Date: Tue, 14 Jun 2016 16:37:20 +0530 Subject: [PATCH 0676/1103] ASoC: Intel: Skylake:Probe-Increase the DMA buffer size DMA buffer size needed for 48KHz, 4 channel, 32 bit data scheduled at 4ms for 2 probe packets is = 2* [ 24 + (48*4*4*32/8) + 8] = 6208. This is the worst case buffer for current set of usecases. Increase the DMA buffer size to this value for now until an optimal buffer size value is arrived at. Change-Id: I4b902f8c078a9a3c2e19c82b0ebeaf99dd99c2b1 Signed-off-by: Pawse, GuruprasadX Reviewed-on: Reviewed-by: Babu, Ramesh Tested-by: Babu, Ramesh --- sound/soc/intel/skylake/skl-probe.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/sound/soc/intel/skylake/skl-probe.c b/sound/soc/intel/skylake/skl-probe.c index 507513eabbd6..9c4d96ecc762 100644 --- a/sound/soc/intel/skylake/skl-probe.c +++ b/sound/soc/intel/skylake/skl-probe.c @@ -30,6 +30,13 @@ #define USE_SPIB 0 +/* + * DMA buffer size needed for 48KHz, 4 channel, 32 bit data + * scheduled at 4ms for 2 probe packets is + * 2* [ 24 + (48*4*4*32/8) + 8] = 6208. + */ +#define SKL_EXTRACT_PROBE_DMA_BUFF_SIZE 6208 + static int set_injector_stream(struct hdac_ext_stream *stream, struct snd_soc_dai *dai) { @@ -62,8 +69,7 @@ int skl_probe_compr_open(struct snd_compr_stream *substream, dev_dbg(dai->dev, "%s dev is %s\n", __func__, dev_name(dai->dev)); if (!pconfig->probe_count) { - /*TODO: Configuring the right DMA buffer size*/ - pconfig->edma_buffsize = 832; + pconfig->edma_buffsize = SKL_EXTRACT_PROBE_DMA_BUFF_SIZE; pconfig->edma_type = SKL_DMA_HDA_HOST_INPUT_CLASS; pconfig->estream = hdac_ext_host_stream_compr_assign(ebus, substream, From 71ce166722d9439db8b7e9755fadc1430e45b4a5 Mon Sep 17 00:00:00 2001 From: "Pawse, GuruprasadX" Date: Tue, 12 Jul 2016 10:57:10 +0530 Subject: [PATCH 0677/1103] ASoC: Intel: Skylake: Probe-Increase Injector DMA buffer size DMA buffer size needed for 48KHz, 4 channel, 32 bit data scheduled at 4ms for 2 probe packets is = 2* [ 24 + (48*4*4*32/8) + 8] = 6208. This is the worst case buffer for current set of usecases. Increase the DMA buffer size to this value for now until an optimal buffer size value is arrived at. Change-Id: Iaf7c3229c6217c10c0c6871c358e9df74b552414 Signed-off-by: Pawse, GuruprasadX Reviewed-on: Reviewed-by: Jayanti, Satya Charitardha Tested-by: Jayanti, Satya Charitardha --- sound/soc/intel/skylake/skl-topology.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/sound/soc/intel/skylake/skl-topology.c b/sound/soc/intel/skylake/skl-topology.c index 3ef44ba263c4..bb8ce1d37522 100644 --- a/sound/soc/intel/skylake/skl-topology.c +++ b/sound/soc/intel/skylake/skl-topology.c @@ -37,6 +37,12 @@ #define SKL_FMT_FIXUP_MASK (1 << 2) #define SKL_IN_DIR_BIT_MASK BIT(0) #define SKL_PIN_COUNT_MASK GENMASK(7, 4) +/* + * DMA buffer size needed for 48KHz, 4 channel, 32 bit data + * scheduled at 4ms for 2 probe packets is + * 2* [ 24 + (48*4*4*32/8) + 8] = 6208. + */ +#define SKL_INJECT_PROBE_DMA_BUFF_SIZE 6208 static const int mic_mono_list[] = { 0, 1, 2, 3, @@ -505,7 +511,7 @@ int skl_tplg_attach_probe_dma(struct snd_soc_dapm_widget *w, ad.node_id.node.vindex = pconfig->iprobe[i].dma_id; ad.node_id.node.dma_type = SKL_DMA_HDA_HOST_OUTPUT_CLASS; ad.node_id.node.rsvd = 0; - ad.dma_buff_size = 1536;/* TODO:Configure based on calculation*/ + ad.dma_buff_size = SKL_INJECT_PROBE_DMA_BUFF_SIZE; } ret = skl_set_module_params(ctx, (u32 *)&ad, From 683d638a3fb2e3b93de21b315aab647e52e3f448 Mon Sep 17 00:00:00 2001 From: "Pawse, GuruprasadX" Date: Tue, 12 Jul 2016 10:57:10 +0530 Subject: [PATCH 0678/1103] ASoC: Intel: Skylake: Probe-Increase Injector DMA buffer size DMA buffer size needed for 48KHz, 4 channel, 32 bit data scheduled at 4ms for 2 probe packets is = 2* [ 24 + (48*4*4*32/8) + 8] = 6208. This is the worst case buffer for current set of usecases. Increase the DMA buffer size to this value for now until an optimal buffer size value is arrived at. Change-Id: Iaf7c3229c6217c10c0c6871c358e9df74b552414 Signed-off-by: Pawse, GuruprasadX Reviewed-on: Reviewed-by: Jayanti, Satya Charitardha Tested-by: Jayanti, Satya Charitardha --- sound/soc/intel/skylake/skl-topology.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/sound/soc/intel/skylake/skl-topology.c b/sound/soc/intel/skylake/skl-topology.c index bb8ce1d37522..2e4a894f58a4 100644 --- a/sound/soc/intel/skylake/skl-topology.c +++ b/sound/soc/intel/skylake/skl-topology.c @@ -98,6 +98,13 @@ void skl_tplg_d0i3_put(struct skl *skl, enum d0i3_capability caps) } } +/* + * DMA buffer size needed for 48KHz, 4 channel, 32 bit data + * scheduled at 4ms for 2 probe packets is + * 2* [ 24 + (48*4*4*32/8) + 8] = 6208. + */ +#define SKL_INJECT_PROBE_DMA_BUFF_SIZE 6208 + /* * SKL DSP driver modelling uses only few DAPM widgets so for rest we will * ignore. This helpers checks if the SKL driver handles this widget type From 9377c75450fcf61cb650a59deab6515ec4519e96 Mon Sep 17 00:00:00 2001 From: Sanyog Kale Date: Sun, 20 Nov 2016 20:25:37 +0530 Subject: [PATCH 0679/1103] Soundwire squashed commits [2] SoundWire: Optimizations in BW calculation and runtime ops Includes: - Removed multiple ifdefs from code. - Clock frequency divider changes. - clock divider added for clock scaling - cleanup in row column definition - block offset adjusted for loopback test Change-Id: I6e9344d7c44681d696ffc5baa61f33ae5f3ac436 Signed-off-by: Sanyog Kale SoundWire: Underrun/Overrun fix for SDW playback and capture Includes: - Fix for playback & capture not working after underrun/overrun and pause/resume scenario. - Optimization in Master/Slave configuration. Change-Id: Id8e6ebe0083e0b5d8bf255128b43d245bb177bc9 Signed-off-by: Sanyog Kale SoundWire: Optimization in BW calculation and runtime operations. Includes: - Optimizations in APIs - Renaming of APIs - Split of bankswitch function. - Split of calc_bw & calc_bw_dis functions. - Individial APIs for prepare, enable, disable and unprepare operations. Change-Id: I5c72bc451d943ced60d1f40b15ae816a048796a6 Signed-off-by: Sanyog Kale SoundWire: Fix for assigning port capabilities for all the master ports while registering Also adds check for Master and Slave capabilities SoundWire: Fix for assigning port capabilities for all the master ports while registering2 Also adds check for Master and Slave capabilities SoundWire: Port configuration changes for Multiple port support This patch supports multiple port configuration for given stream. Signed-off-by: Ashish Panwar Signed-off-by: Sanyog Kale SoundWire: Multiple port support for Master and Slave ports Includes: - Computes transport parameters for all Master and Slave Ports. - SV codec driver changes to support multi port PCM capture. - Machine driver changes to support multi port PCM capture. - Free up resources for port runtime handle. Change-Id: I18d7247f44a9aff400bc709bd35f968ecfc66eea Signed-off-by: Sanyog Kale Soundwire: Add Interrupt Status SCP Registers Change-Id: I7a037c74861bfcce5b263fac54a07f58cac078e0 Signed-off-by: Guneshwor Singh SoundWire: Add support for getting bus params. Some Slave may require to know bus params at probe to program its registers. Provide API to get current bus params. Signed-off-by: Sanyog Kale SoundWire: Fix the Slave alert handling. 1. Return the proper status to slave for interrupt. 2. Enable to specify scp_interrupt mask register. 3. Re-enable interrupts when slave changes from unattached to attached. 4. Ack only the handled interrupts. Change-Id: If1732460e0c4ca286b8d09f5e212b4834e53b533 Signed-off-by: Sanyog Kale SoundWire: Add deprepare after clock resume. 1. According to SoundWire spec deprepare is required after resuming from clock stop mode0. Add this functionality. 2. According to SoundWire spec deprepare is optionally required after resuming from clock stop mode1. Add this functionality. 3. Add Slave callbacks to call the pre and post clock stop prepeare before doing actual clock stop. Signed-off-by: Sanyog Kale SDW: Remove hardcoding to enable normal capture Remove hardcode for loopback and enable normal capture/playback. Change-Id: If0d16c8d0d0e6409ffe5372002e2bc18b8ba0588 Signed-off-by: Shreyas NC Soundwire: Change clockstop exit sequence for losing ctx When Cavs runtime pm is enabled, the sequence for clockstop exit is also changed. Change-Id: I834aa87c65aa97172f477dced11c3610e412edc2 Signed-off-by: Guneshwor Singh Soundwire: Hard bus reset is not required in resume According to MIPI, bus reset is not required during clockstop exit. So remove bus reset in the clockstop exit sequence. Change-Id: Iea7b3a8030cb683caa97d9648ac873f8000ca072 Signed-off-by: Guneshwor Singh [CNL FPGA] Soundwire: Add #if for frameshape change in CNL RVP This is added for supporting both RVP and FPGA setups. Frameshape is different for both cases, so add #if to distinguish. Change-Id: Ib8ca64c4b0e138e8260392adeba6e27524c438aa Signed-off-by: Guneshwor Singh SoundWire: Bus header file changes for BRA feature This patch includes: - Bus API for supporting BRA feature. - BRA defines as per MIPI 1.1 spec. Signed-off-by: Sanyog Kale SoundWire: Bus implementation for BRA feature This patch includes: - Implementation of bus API sdw_slave_xfer_bra_block used for BRA transfers by SoundWire Slave(s). - Bandwidth allocation for BRA. - Data port 0 prepare/enable/de-prepare/disable ops. Signed-off-by: Sanyog Kale SoundWire: Bus header file changes for CRC8 helper function This patch adds bus CRC8 helper function used in BRA feature to compute CRC8. Signed-off-by: Sanyog Kale SoundWire: Bus CRC8 helper function implementation This patch implements helper function for calculating CRC8 values. Signed-off-by: Sanyog Kale Signed-off-by: Guneshwor Singh SoundWire: Master driver header file changes for BRA feature This patch includes: - Data structure required for BRA operations. - BRA ops definition. - Defines used by Master driver for BRA operations. Signed-off-by: Sanyog Kale SoundWire: Master driver implementation for BRA feature This patch includes: - Implementation for Master API for BRA. - Preparation of TX and RX PDI buffer. - Preparation of BRA packets. - Verification of RX packets. - PDI configuration for BRA. Signed-off-by: Sanyog Kale Soundwire: Fix build regression when PM is disabled Build regression observed when CONFIG_PM and CONFIG_PM_SLEEP is disabled. To fix this #ifdefs are added for functions specific to PM. Change-Id: Ia975415cafad536832d3383ed3e8c4314bf0d305 Signed-off-by: Anamika Lal Reviewed-on: Reviewed-by: Diwakar, Praveen Reviewed-by: Singh, Guneshwor O Reviewed-by: Koul, Vinod Tested-by: Sm, Bhadur A Soundwire: Add return value to avoid warning Since return type of sdw_transfer_trace_reg, return zero to avoid compiler warning. Signed-off-by: Guneshwor Singh SoundWire: TX and RX Host DMA & Pipeline creation support for BRA Signed-off-by: Sanyog Kale SoundWire: Creates single module for SoundWire bus framework This patch make SoundWire bus framework as single module. Change-Id: I966d42e57a9899d82ad99ec75f879a0b627afa7f Signed-off-by: Sanyog Kale Reviewed-on: Reviewed-by: Diwakar, Praveen Reviewed-by: Koul, Vinod Reviewed-by: Singh, Guneshwor O Reviewed-by: Nemallapudi, JaikrishnaX Reviewed-by: Kp, Jeeja Tested-by: Avati, Santosh Kumar SoundWire: Add export symbol for SoundWire Bus BRA API This patch adds export symbol for sdw_slave_xfer_bra_block SoundWire Bus BRA API Change-Id: I8bb8d6b1595c46077bc0914b9e8f3b9d89bcd686 Signed-off-by: Sanyog Kale Reviewed-on: Reviewed-by: Diwakar, Praveen Reviewed-by: Koul, Vinod Reviewed-by: Singh, Guneshwor O Reviewed-by: Kp, Jeeja Tested-by: Avati, Santosh Kumar Reviewed-by: Nemallapudi, JaikrishnaX SoundWire: Mask gSync pulse to avoid gSync and frame mis-aligment Cadence Master IP supports Multi-Master Mode where the IP can be configured such that its generated Frame boundary is synchronized to the periodically occurring gSync pulses. Certain versions of the IP implementation have a bug whereby if a gSync pulse collides with the register configuration update that brings up the IP into Normal operation (where the IP begins Frame tracking), then the resulting Frame boundary will misalign with the periodic gSync pulses. This patch adds gSync masking logic where gSync pulse is masked before performing register configuration and is un-masked after setting Mcp_ConfigUpdate bit. Due to this the initialization-pending Master IP SoundWire bus clock will start up synchronizing to gSync, leading to bus reset entry, subsequent exit, and 1st Frame generation aligning to gSync. Change-Id: I8e3620244de3f0c0636520db017df4296c7ae5e5 Signed-off-by: Sanyog Kale Reviewed-on: Reviewed-by: Singh, Guneshwor O Reviewed-by: Avati, Santosh Kumar Tested-by: Avati, Santosh Kumar Reviewed-by: Koul, Vinod SoundWire: Remove Maxim FPGA support from SoundWire bus Maxim codec FPGA is no more used for SoundWire use case verification, removing related code changes. Change-Id: I7584e7f81922df3f3d168d41ef7192a6449ff044 Signed-off-by: Sanyog Kale Reviewed-on: Reviewed-by: Koul, Vinod Reviewed-by: Singh, Guneshwor O Reviewed-by: Nc, Shreyas Reviewed-by: Diwakar, Praveen Tested-by: Avati, Santosh Kumar SoundWire: Remove hardcoding for SSP Interval This patch removes hardcoding for setting SSP Interval and sets default value based on the platform configuration. Change-Id: I4cd14a9a2ddda28e4b9d2a8cee931ac5eec88e03 Signed-off-by: Sanyog Kale Reviewed-on: Reviewed-by: Nc, Shreyas Reviewed-by: Singh, Guneshwor O Reviewed-by: Diwakar, Praveen Tested-by: Avati, Santosh Kumar --- drivers/sdw/Kconfig | 1 + drivers/sdw/Makefile | 2 +- drivers/sdw/sdw.c | 1600 +++++++++++--- drivers/sdw/sdw_bwcalc.c | 2858 +++++++++++++------------ drivers/sdw/sdw_cnl.c | 943 +++++++- drivers/sdw/sdw_cnl_priv.h | 40 + drivers/sdw/sdw_priv.h | 50 +- drivers/sdw/sdw_utils.c | 49 + include/linux/sdw/sdw_cnl.h | 29 + include/linux/sdw/sdw_registers.h | 7 +- include/linux/sdw_bus.h | 207 +- sound/soc/codecs/svfpga-sdw.c | 2 +- sound/soc/intel/boards/cnl_svfpga.c | 2 +- sound/soc/intel/skylake/cnl-sst.c | 16 +- sound/soc/intel/skylake/skl-sdw-pcm.c | 77 +- 15 files changed, 4151 insertions(+), 1732 deletions(-) create mode 100644 drivers/sdw/sdw_utils.c diff --git a/drivers/sdw/Kconfig b/drivers/sdw/Kconfig index 90e954c392e0..1b7e2cc2ebc3 100644 --- a/drivers/sdw/Kconfig +++ b/drivers/sdw/Kconfig @@ -1,5 +1,6 @@ menuconfig SDW tristate "SoundWire bus support" + depends on CRC8 help SoundWire interface is typically used for transporting data related to audio functions. diff --git a/drivers/sdw/Makefile b/drivers/sdw/Makefile index 184682a88a1a..e2ba440f4ef2 100644 --- a/drivers/sdw/Makefile +++ b/drivers/sdw/Makefile @@ -1,4 +1,4 @@ -sdw_bus-objs := sdw.o sdw_bwcalc.o +sdw_bus-objs := sdw.o sdw_bwcalc.o sdw_utils.o obj-$(CONFIG_SDW) += sdw_bus.o obj-$(CONFIG_SDW_CNL) += sdw_cnl.o diff --git a/drivers/sdw/sdw.c b/drivers/sdw/sdw.c index 78c8cfd32d4c..aefd25d4e393 100644 --- a/drivers/sdw/sdw.c +++ b/drivers/sdw/sdw.c @@ -210,6 +210,29 @@ static int sdw_slv_probe(struct device *dev) return ret; } + +int sdw_slave_get_bus_params(struct sdw_slv *sdw_slv, + struct sdw_bus_params *params) +{ + struct sdw_bus *bus; + struct sdw_master *mstr = sdw_slv->mstr; + + list_for_each_entry(bus, &sdw_core.bus_list, bus_node) { + if (bus->mstr == mstr) + break; + } + if (!bus) + return -EFAULT; + + params->num_rows = bus->row; + params->num_cols = bus->col; + params->bus_clk_freq = bus->clk_freq >> 1; + params->bank = bus->active_bank; + + return 0; +} +EXPORT_SYMBOL(sdw_slave_get_bus_params); + static int sdw_mstr_remove(struct device *dev) { const struct sdw_mstr_driver *sdrv = to_sdw_mstr_driver(dev->driver); @@ -373,17 +396,19 @@ static int sdw_pm_resume(struct device *dev) return sdw_legacy_resume(dev); } +#else +#define sdw_pm_suspend NULL +#define sdw_pm_resume NULL +#endif /* CONFIG_PM_SLEEP */ + static const struct dev_pm_ops soundwire_pm = { .suspend = sdw_pm_suspend, .resume = sdw_pm_resume, +#ifdef CONFIG_PM .runtime_suspend = pm_generic_runtime_suspend, .runtime_resume = pm_generic_runtime_resume, -}; - -#else -#define sdw_pm_suspend NULL -#define sdw_pm_resume NULL #endif +}; struct bus_type sdwint_bus_type = { .name = "soundwire", @@ -404,6 +429,8 @@ static struct static_key sdw_trace_msg = STATIC_KEY_INIT_FALSE; int sdw_transfer_trace_reg(void) { static_key_slow_inc(&sdw_trace_msg); + + return 0; } void sdw_transfer_trace_unreg(void) @@ -835,7 +862,7 @@ int sdw_slave_transfer(struct sdw_master *mstr, struct sdw_msg *msg, int num) EXPORT_SYMBOL_GPL(sdw_slave_transfer); static int sdw_handle_dp0_interrupts(struct sdw_master *mstr, - struct sdw_slave *sdw_slv) + struct sdw_slv *sdw_slv, u8 *status) { int ret = 0; struct sdw_msg rd_msg, wr_msg; @@ -886,8 +913,8 @@ static int sdw_handle_dp0_interrupts(struct sdw_master *mstr, SDW_DP0_INTSTAT_IMPDEF2_MASK | SDW_DP0_INTSTAT_IMPDEF3_MASK; if (rd_msg.buf[0] & impl_def_mask) { - /* TODO: Handle implementation defined mask ready */ wr_msg.buf[0] |= impl_def_mask; + *status = wr_msg.buf[0]; } ret = sdw_slave_transfer(mstr, &wr_msg, 1); if (ret != 1) { @@ -901,15 +928,20 @@ static int sdw_handle_dp0_interrupts(struct sdw_master *mstr, } static int sdw_handle_port_interrupt(struct sdw_master *mstr, - struct sdw_slave *sdw_slv, int port_num) + struct sdw_slv *sdw_slv, int port_num, + u8 *status) { int ret = 0; struct sdw_msg rd_msg, wr_msg; u8 rbuf[1], wbuf[1]; int impl_def_mask = 0; - if (port_num == 0) - ret = sdw_handle_dp0_interrupts(mstr, sdw_slv); +/* + * Handle the Data port0 interrupt separately since the interrupt + * mask and stat register is different than other DPn registers + */ + if (port_num == 0 && sdw_slv->sdw_slv_cap.sdw_dp0_supported) + return sdw_handle_dp0_interrupts(mstr, sdw_slv, status); /* Create message for reading the port interrupts */ wr_msg.ssp_tag = 0; @@ -953,6 +985,7 @@ static int sdw_handle_port_interrupt(struct sdw_master *mstr, if (rd_msg.buf[0] & impl_def_mask) { /* TODO: Handle implementation defined mask ready */ wr_msg.buf[0] |= impl_def_mask; + *status = wr_msg.buf[0]; } /* Clear and Ack the interrupt */ ret = sdw_slave_transfer(mstr, &wr_msg, 1); @@ -972,6 +1005,10 @@ static int sdw_handle_slave_alerts(struct sdw_master *mstr, u8 rbuf[3], wbuf[1]; int i, ret = 0; int cs_port_mask, cs_port_register, cs_port_start, cs_ports; + struct sdw_impl_def_intr_stat *intr_status; + struct sdw_portn_intr_stat *portn_stat; + u8 port_status[15] = {0}; + u8 control_port_stat = 0; /* Read Instat 1, Instat 2 and Instat 3 registers */ @@ -1027,214 +1064,711 @@ static int sdw_handle_slave_alerts(struct sdw_master *mstr, dev_err(&mstr->dev, "Bus clash error detected\n"); wr_msg.buf[0] |= SDW_SCP_INTCLEAR1_BUS_CLASH_MASK; } - /* Handle Port interrupts from Instat_1 registers */ + /* Handle implementation defined mask */ + if (rd_msg[0].buf[0] & SDW_SCP_INTSTAT1_IMPL_DEF_MASK) { + wr_msg.buf[0] |= SDW_SCP_INTCLEAR1_IMPL_DEF_MASK; + control_port_stat = (rd_msg[0].buf[0] & + SDW_SCP_INTSTAT1_IMPL_DEF_MASK); + } + + /* Handle Cascaded Port interrupts from Instat_1 registers */ + + /* Number of port status bits in this register */ cs_ports = 4; + /* Port number starts at in this register */ cs_port_start = 0; + /* Bit mask for the starting port intr status */ cs_port_mask = 0x08; + /* Bit mask for the starting port intr status */ cs_port_register = 0; - for (i = cs_port_start; i < cs_port_start + cs_ports; i++) { + + /* Look for cascaded port interrupts, if found handle port + * interrupts. Do this for all the Int_stat registers. + */ + for (i = cs_port_start; i < cs_port_start + cs_ports && + i <= sdw_slv->sdw_slv_cap.num_of_sdw_ports; i++) { if (rd_msg[cs_port_register].buf[0] & cs_port_mask) { ret += sdw_handle_port_interrupt(mstr, - sdw_slv, cs_port_start + i); + sdw_slv, i, &port_status[i]); } cs_port_mask = cs_port_mask << 1; } - /* Handle interrupts from instat_2 register */ + + /* + * Handle cascaded interrupts from instat_2 register, + * if no cascaded interrupt from SCP2 cascade move to SCP3 + */ if (!(rd_msg[0].buf[0] & SDW_SCP_INTSTAT1_SCP2_CASCADE_MASK)) goto handle_instat_3_register; + + cs_ports = 7; cs_port_start = 4; cs_port_mask = 0x1; cs_port_register = 1; - for (i = cs_port_start; i < cs_port_start + cs_ports; i++) { + for (i = cs_port_start; i < cs_port_start + cs_ports && + i <= sdw_slv->sdw_slv_cap.num_of_sdw_ports; i++) { + if (rd_msg[cs_port_register].buf[0] & cs_port_mask) { + ret += sdw_handle_port_interrupt(mstr, - sdw_slv, cs_port_start + i); + sdw_slv, i, &port_status[i]); } cs_port_mask = cs_port_mask << 1; } -handle_instat_3_register: + /* + * Handle cascaded interrupts from instat_2 register, + * if no cascaded interrupt from SCP2 cascade move to impl_def intrs + */ +handle_instat_3_register: if (!(rd_msg[1].buf[0] & SDW_SCP_INTSTAT2_SCP3_CASCADE_MASK)) - goto handle_instat_3_register; + goto handle_impl_def_interrupts; + cs_ports = 4; cs_port_start = 11; cs_port_mask = 0x1; cs_port_register = 2; - for (i = cs_port_start; i < cs_port_start + cs_ports; i++) { + + for (i = cs_port_start; i < cs_port_start + cs_ports && + i <= sdw_slv->sdw_slv_cap.num_of_sdw_ports; i++) { + if (rd_msg[cs_port_register].buf[0] & cs_port_mask) { + ret += sdw_handle_port_interrupt(mstr, - sdw_slv, cs_port_start + i); + sdw_slv, i, &port_status[i]); } cs_port_mask = cs_port_mask << 1; } - /* Ack the IntStat 1 interrupts */ + +handle_impl_def_interrupts: + + /* + * If slave has not registered for implementation defined + * interrupts, dont read it. + */ + if (!sdw_slv->driver->handle_impl_def_interrupts) + goto ack_interrupts; + + intr_status = kzalloc(sizeof(*intr_status), GFP_KERNEL); + if (!intr_status) + return -ENOMEM; + + portn_stat = kzalloc((sizeof(*portn_stat)) * + sdw_slv->sdw_slv_cap.num_of_sdw_ports, + GFP_KERNEL); + if (!portn_stat) + return -ENOMEM; + + intr_status->portn_stat = portn_stat; + intr_status->control_port_stat = control_port_stat; + + /* Update the implementation defined status to Slave */ + for (i = 1; i < sdw_slv->sdw_slv_cap.num_of_sdw_ports; i++) { + + intr_status->portn_stat[i].status = port_status[i]; + intr_status->portn_stat[i].num = i; + } + + intr_status->port0_stat = port_status[0]; + intr_status->control_port_stat = wr_msg.buf[0]; + + ret = sdw_slv->driver->handle_impl_def_interrupts(sdw_slv, + intr_status); + if (ret) + dev_err(&mstr->dev, "Implementation defined interrupt handling failed\n"); + + kfree(portn_stat); + kfree(intr_status); + +ack_interrupts: + /* Ack the interrupts */ ret = sdw_slave_transfer(mstr, &wr_msg, 1); if (ret != 1) { ret = -EINVAL; dev_err(&mstr->dev, "Register transfer failed\n"); - goto out; } out: - return ret; + return 0; } -static void handle_slave_status(struct kthread_work *work) +int sdw_en_intr(struct sdw_slv *sdw_slv, int port_num, int mask) { - int i, ret = 0; - struct sdw_slv_status *status, *__status__; - struct sdw_bus *bus = - container_of(work, struct sdw_bus, kwork); - struct sdw_master *mstr = bus->mstr; - unsigned long flags; - /* Handle the new attached slaves to the bus. Register new slave - * to the bus. - */ - list_for_each_entry_safe(status, __status__, &bus->status_list, node) { - if (status->status[0] == SDW_SLAVE_STAT_ATTACHED_OK) { - ret += sdw_register_slave(mstr); - if (ret) - /* Even if adding new slave fails, we will - * continue. - */ - dev_err(&mstr->dev, "Registering new slave failed\n"); - } - for (i = 1; i <= SOUNDWIRE_MAX_DEVICES; i++) { - if (status->status[i] == SDW_SLAVE_STAT_NOT_PRESENT && - mstr->sdw_addr[i].assigned == true) - /* Logical address was assigned to slave, but - * now its down, so mark it as not present - */ - mstr->sdw_addr[i].status = - SDW_SLAVE_STAT_NOT_PRESENT; + struct sdw_msg rd_msg, wr_msg; + u8 buf; + int ret; + struct sdw_master *mstr = sdw_slv->mstr; - else if (status->status[i] == SDW_SLAVE_STAT_ALERT && - mstr->sdw_addr[i].assigned == true) { - /* Handle slave alerts */ - mstr->sdw_addr[i].status = SDW_SLAVE_STAT_ALERT; - ret = sdw_handle_slave_alerts(mstr, - mstr->sdw_addr[i].slave); - if (ret) - dev_err(&mstr->dev, "Handle slave alert failed for Slave %d\n", i); + rd_msg.addr = wr_msg.addr = SDW_DPN_INTMASK + + (SDW_NUM_DATA_PORT_REGISTERS * port_num); + /* Create message for enabling the interrupts */ + wr_msg.ssp_tag = 0; + wr_msg.flag = SDW_MSG_FLAG_WRITE; + wr_msg.len = 1; + wr_msg.buf = &buf; + wr_msg.slave_addr = sdw_slv->slv_number; + wr_msg.addr_page1 = 0x0; + wr_msg.addr_page2 = 0x0; + /* Create message for reading the interrupts for DP0 interrupts*/ + rd_msg.ssp_tag = 0; + rd_msg.flag = SDW_MSG_FLAG_READ; + rd_msg.len = 1; + rd_msg.buf = &buf; + rd_msg.slave_addr = sdw_slv->slv_number; + rd_msg.addr_page1 = 0x0; + rd_msg.addr_page2 = 0x0; + ret = sdw_slave_transfer(mstr, &rd_msg, 1); + if (ret != 1) { + dev_err(&mstr->dev, "DPn Intr mask read failed for slave %x\n", + sdw_slv->slv_number); + return -EINVAL; + } - } else if (status->status[i] == - SDW_SLAVE_STAT_ATTACHED_OK && - mstr->sdw_addr[i].assigned == true) - mstr->sdw_addr[i].status = - SDW_SLAVE_STAT_ATTACHED_OK; - } - spin_lock_irqsave(&bus->spinlock, flags); - list_del(&status->node); - spin_unlock_irqrestore(&bus->spinlock, flags); - kfree(status); + buf |= mask; + + /* Set the port ready and Test fail interrupt mask as well */ + buf |= SDW_DPN_INTSTAT_TEST_FAIL_MASK; + buf |= SDW_DPN_INTSTAT_PORT_READY_MASK; + ret = sdw_slave_transfer(mstr, &wr_msg, 1); + if (ret != 1) { + dev_err(&mstr->dev, "DPn Intr mask write failed for slave %x\n", + sdw_slv->slv_number); + return -EINVAL; } + return 0; } -static int sdw_register_master(struct sdw_master *mstr) +static int sdw_en_scp_intr(struct sdw_slv *sdw_slv, int mask) { - int ret = 0; - int i; - struct sdw_bus *sdw_bus; + struct sdw_msg rd_msg, wr_msg; + u8 buf = 0; + int ret; + struct sdw_master *mstr = sdw_slv->mstr; + u16 reg_addr; - /* Can't register until after driver model init */ - if (unlikely(WARN_ON(!sdw_bus_type.p))) { - ret = -EAGAIN; - goto bus_init_not_done; - } - /* Sanity checks */ - if (unlikely(mstr->name[0] == '\0')) { - pr_err("sdw-core: Attempt to register an master with no name!\n"); - ret = -EINVAL; - goto mstr_no_name; - } - for (i = 0; i <= SOUNDWIRE_MAX_DEVICES; i++) - mstr->sdw_addr[i].slv_number = i; + reg_addr = SDW_SCP_INTMASK1; - rt_mutex_init(&mstr->bus_lock); - INIT_LIST_HEAD(&mstr->slv_list); - INIT_LIST_HEAD(&mstr->mstr_rt_list); + rd_msg.addr = wr_msg.addr = reg_addr; - sdw_bus = kzalloc(sizeof(struct sdw_bus), GFP_KERNEL); - if (!sdw_bus) - goto bus_alloc_failed; - sdw_bus->mstr = mstr; - init_completion(&sdw_bus->async_data.xfer_complete); + /* Create message for reading the interrupt mask */ + rd_msg.ssp_tag = 0; + rd_msg.flag = SDW_MSG_FLAG_READ; + rd_msg.len = 1; + rd_msg.buf = &buf; + rd_msg.slave_addr = sdw_slv->slv_number; + rd_msg.addr_page1 = 0x0; + rd_msg.addr_page2 = 0x0; + ret = sdw_slave_transfer(mstr, &rd_msg, 1); + if (ret != 1) { + dev_err(&mstr->dev, "SCP Intr mask read failed for slave %x\n", + sdw_slv->slv_number); + return -EINVAL; + } - mutex_lock(&sdw_core.core_lock); - list_add_tail(&sdw_bus->bus_node, &sdw_core.bus_list); - mutex_unlock(&sdw_core.core_lock); + /* Enable the Slave defined interrupts. */ + buf |= mask; - dev_set_name(&mstr->dev, "sdw-%d", mstr->nr); - mstr->dev.bus = &sdw_bus_type; - mstr->dev.type = &sdw_mstr_type; + /* Set the port ready and Test fail interrupt mask as well */ + buf |= SDW_SCP_INTMASK1_BUS_CLASH_MASK; + buf |= SDW_SCP_INTMASK1_PARITY_MASK; - ret = device_register(&mstr->dev); - if (ret) - goto out_list; - kthread_init_worker(&sdw_bus->kworker); - sdw_bus->status_thread = kthread_run(kthread_worker_fn, - &sdw_bus->kworker, "%s", - dev_name(&mstr->dev)); - if (IS_ERR(sdw_bus->status_thread)) { - dev_err(&mstr->dev, "error: failed to create status message task\n"); - ret = PTR_ERR(sdw_bus->status_thread); - goto task_failed; - } - kthread_init_work(&sdw_bus->kwork, handle_slave_status); - INIT_LIST_HEAD(&sdw_bus->status_list); - spin_lock_init(&sdw_bus->spinlock); - ret = sdw_mstr_bw_init(sdw_bus); - if (ret) { - dev_err(&mstr->dev, "error: Failed to init mstr bw\n"); - goto mstr_bw_init_failed; + /* Create message for enabling the interrupts */ + wr_msg.ssp_tag = 0; + wr_msg.flag = SDW_MSG_FLAG_WRITE; + wr_msg.len = 1; + wr_msg.buf = &buf; + wr_msg.slave_addr = sdw_slv->slv_number; + wr_msg.addr_page1 = 0x0; + wr_msg.addr_page2 = 0x0; + ret = sdw_slave_transfer(mstr, &wr_msg, 1); + if (ret != 1) { + dev_err(&mstr->dev, "SCP Intr mask write failed for slave %x\n", + sdw_slv->slv_number); + return -EINVAL; } - dev_dbg(&mstr->dev, "master [%s] registered\n", mstr->name); - return 0; + /* Return if DP0 is not present */ + if (!sdw_slv->sdw_slv_cap.sdw_dp0_supported) + return 0; -mstr_bw_init_failed: -task_failed: - device_unregister(&mstr->dev); -out_list: - mutex_lock(&sdw_core.core_lock); - list_del(&sdw_bus->bus_node); - mutex_unlock(&sdw_core.core_lock); - kfree(sdw_bus); -bus_alloc_failed: -mstr_no_name: -bus_init_not_done: - mutex_lock(&sdw_core.core_lock); - idr_remove(&sdw_core.idr, mstr->nr); - mutex_unlock(&sdw_core.core_lock); - return ret; -} -/** - * sdw_master_update_slv_status: Report the status of slave to the bus driver. - * master calls this function based on the - * interrupt it gets once the slave changes its - * state. - * @mstr: Master handle for which status is reported. - * @status: Array of status of each slave. - */ -int sdw_master_update_slv_status(struct sdw_master *mstr, - struct sdw_status *status) -{ - struct sdw_bus *bus = NULL; - struct sdw_slv_status *slv_status; - unsigned long flags; + reg_addr = SDW_DP0_INTMASK; + rd_msg.addr = wr_msg.addr = reg_addr; + mask = sdw_slv->sdw_slv_cap.sdw_dp0_cap->imp_def_intr_mask; + buf = 0; - list_for_each_entry(bus, &sdw_core.bus_list, bus_node) { - if (bus->mstr == mstr) - break; - } - /* This is master is not registered with bus driver */ - if (!bus) { - dev_info(&mstr->dev, "Master not registered with bus\n"); - return 0; + /* Create message for reading the interrupt mask */ + /* Create message for reading the interrupt mask */ + rd_msg.ssp_tag = 0; + rd_msg.flag = SDW_MSG_FLAG_READ; + rd_msg.len = 1; + rd_msg.buf = &buf; + rd_msg.slave_addr = sdw_slv->slv_number; + rd_msg.addr_page1 = 0x0; + rd_msg.addr_page2 = 0x0; + ret = sdw_slave_transfer(mstr, &rd_msg, 1); + if (ret != 1) { + dev_err(&mstr->dev, "DP0 Intr mask read failed for slave %x\n", + sdw_slv->slv_number); + return -EINVAL; + } + + /* Enable the Slave defined interrupts. */ + buf |= mask; + + /* Set the port ready and Test fail interrupt mask as well */ + buf |= SDW_DP0_INTSTAT_TEST_FAIL_MASK; + buf |= SDW_DP0_INTSTAT_PORT_READY_MASK; + buf |= SDW_DP0_INTSTAT_BRA_FAILURE_MASK; + + wr_msg.ssp_tag = 0; + wr_msg.flag = SDW_MSG_FLAG_WRITE; + wr_msg.len = 1; + wr_msg.buf = &buf; + wr_msg.slave_addr = sdw_slv->slv_number; + wr_msg.addr_page1 = 0x0; + wr_msg.addr_page2 = 0x0; + + ret = sdw_slave_transfer(mstr, &wr_msg, 1); + if (ret != 1) { + dev_err(&mstr->dev, "DP0 Intr mask write failed for slave %x\n", + sdw_slv->slv_number); + return -EINVAL; + } + return 0; +} + +static int sdw_prog_slv(struct sdw_slv *sdw_slv) +{ + + struct sdw_slv_capabilities *cap; + int ret, i; + struct sdw_slv_dpn_capabilities *dpn_cap; + struct sdw_master *mstr = sdw_slv->mstr; + + if (!sdw_slv->slave_cap_updated) + return 0; + cap = &sdw_slv->sdw_slv_cap; + + /* Enable DP0 and SCP interrupts */ + ret = sdw_en_scp_intr(sdw_slv, cap->scp_impl_def_intr_mask); + + /* Failure should never happen, even if it happens we continue */ + if (ret) + dev_err(&mstr->dev, "SCP program failed\n"); + + for (i = 0; i < cap->num_of_sdw_ports; i++) { + dpn_cap = &cap->sdw_dpn_cap[i]; + ret = sdw_en_intr(sdw_slv, (i + 1), + dpn_cap->imp_def_intr_mask); + + if (ret) + break; + } + return ret; +} + + +static void sdw_send_slave_status(struct sdw_slv *slave, + enum sdw_slave_status *status) +{ + struct sdw_slave_driver *slv_drv = slave->driver; + + if (slv_drv && slv_drv->update_slv_status) + slv_drv->update_slv_status(slave, status); +} + +static int sdw_wait_for_deprepare(struct sdw_slv *slave) +{ + int ret; + struct sdw_msg msg; + u8 buf[1] = {0}; + int timeout = 0; + struct sdw_master *mstr = slave->mstr; + + /* Create message to read clock stop status, its broadcast message. */ + buf[0] = 0xFF; + + msg.ssp_tag = 0; + msg.flag = SDW_MSG_FLAG_READ; + msg.len = 1; + msg.buf = &buf[0]; + msg.slave_addr = slave->slv_number; + msg.addr_page1 = 0x0; + msg.addr_page2 = 0x0; + msg.addr = SDW_SCP_STAT; + /* + * Read the ClockStopNotFinished bit from the SCP_Stat register + * of particular Slave to make sure that clock stop prepare is done + */ + do { + /* + * Ideally this should not fail, but even if it fails + * in exceptional situation, we go ahead for clock stop + */ + ret = sdw_slave_transfer_nopm(mstr, &msg, 1); + + if (ret != 1) { + WARN_ONCE(1, "Clock stop status read failed\n"); + break; + } + + if (!(buf[0] & SDW_SCP_STAT_CLK_STP_NF_MASK)) + break; + + /* + * TODO: Need to find from spec what is requirement. + * Since we are in suspend we should not sleep for more + * Ideally Slave should be ready to stop clock in less than + * few ms. + * So sleep less and increase loop time. This is not + * harmful, since if Slave is ready loop will terminate. + * + */ + msleep(2); + timeout++; + + } while (timeout != 500); + + if (!(buf[0] & SDW_SCP_STAT_CLK_STP_NF_MASK)) + + dev_info(&mstr->dev, "Clock stop prepare done\n"); + else + WARN_ONCE(1, "Clk stp deprepare failed for slave %d\n", + slave->slv_number); + + return -EINVAL; +} + +static void sdw_prep_slave_for_clk_stp(struct sdw_master *mstr, + struct sdw_slv *slave, + enum sdw_clk_stop_mode clock_stop_mode, + bool prep) +{ + bool wake_en; + struct sdw_slv_capabilities *cap; + u8 buf[1] = {0}; + struct sdw_msg msg; + int ret; + + cap = &slave->sdw_slv_cap; + + /* Set the wakeup enable based on Slave capability */ + wake_en = !cap->wake_up_unavailable; + + if (prep) { + /* Even if its simplified clock stop prepare, + * setting prepare bit wont harm + */ + buf[0] |= (1 << SDW_SCP_SYSTEMCTRL_CLK_STP_PREP_SHIFT); + buf[0] |= clock_stop_mode << + SDW_SCP_SYSTEMCTRL_CLK_STP_MODE_SHIFT; + buf[0] |= wake_en << SDW_SCP_SYSTEMCTRL_WAKE_UP_EN_SHIFT; + } else + buf[0] = 0; + + msg.ssp_tag = 0; + msg.flag = SDW_MSG_FLAG_WRITE; + msg.len = 1; + msg.buf = &buf[0]; + msg.slave_addr = slave->slv_number; + msg.addr_page1 = 0x0; + msg.addr_page2 = 0x0; + msg.addr = SDW_SCP_SYSTEMCTRL; + + /* + * We are calling NOPM version of the transfer API, because + * Master controllers calls this from the suspend handler, + * so if we call the normal transfer API, it tries to resume + * controller, which result in deadlock + */ + + ret = sdw_slave_transfer_nopm(mstr, &msg, 1); + /* We should continue even if it fails for some Slave */ + if (ret != 1) + WARN_ONCE(1, "Clock Stop prepare failed for slave %d\n", + slave->slv_number); +} + +static int sdw_check_for_prep_bit(struct sdw_slv *slave) +{ + u8 buf[1] = {0}; + struct sdw_msg msg; + int ret; + struct sdw_master *mstr = slave->mstr; + + msg.ssp_tag = 0; + msg.flag = SDW_MSG_FLAG_READ; + msg.len = 1; + msg.buf = &buf[0]; + msg.slave_addr = slave->slv_number; + msg.addr_page1 = 0x0; + msg.addr_page2 = 0x0; + msg.addr = SDW_SCP_SYSTEMCTRL; + + ret = sdw_slave_transfer_nopm(mstr, &msg, 1); + /* We should continue even if it fails for some Slave */ + if (ret != 1) { + dev_err(&mstr->dev, "SCP_SystemCtrl read failed for Slave %d\n", + slave->slv_number); + return -EINVAL; + + } + return (buf[0] & SDW_SCP_SYSTEMCTRL_CLK_STP_PREP_MASK); + +} + +static int sdw_slv_deprepare_clk_stp1(struct sdw_slv *slave) +{ + struct sdw_slv_capabilities *cap; + int ret; + struct sdw_master *mstr = slave->mstr; + + cap = &slave->sdw_slv_cap; + + /* + * Slave might have enumerated 1st time or from clock stop mode 1 + * return if Slave doesn't require deprepare + */ + if (!cap->clk_stp1_deprep_required) + return 0; + + /* + * If Slave requires de-prepare after exiting from Clock Stop + * mode 1, than check for ClockStopPrepare bit in SystemCtrl register + * if its 1, de-prepare Slave from clock stop prepare, else + * return + */ + ret = sdw_check_for_prep_bit(slave); + /* If prepare bit is not set, return without error */ + if (!ret) + return 0; + + /* If error in reading register, return with error */ + if (ret < 0) + return ret; + + /* + * Call the pre clock stop prepare, if Slave requires. + */ + if (slave->driver && slave->driver->pre_clk_stop_prep) { + ret = slave->driver->pre_clk_stop_prep(slave, + cap->clock_stop1_mode_supported, false); + if (ret) { + dev_warn(&mstr->dev, "Pre de-prepare failed for Slave %d\n", + slave->slv_number); + return ret; + } + } + + sdw_prep_slave_for_clk_stp(slave->mstr, slave, + cap->clock_stop1_mode_supported, false); + + /* Make sure NF = 0 for deprepare to complete */ + ret = sdw_wait_for_deprepare(slave); + + /* Return in de-prepare unsuccessful */ + if (ret) + return ret; + + if (slave->driver && slave->driver->post_clk_stop_prep) { + ret = slave->driver->post_clk_stop_prep(slave, + cap->clock_stop1_mode_supported, false); + + if (ret) + dev_err(&mstr->dev, "Post de-prepare failed for Slave %d\n", + slave->slv_number); + } + + return ret; +} + +static void handle_slave_status(struct kthread_work *work) +{ + int i, ret = 0; + struct sdw_slv_status *status, *__status__; + struct sdw_bus *bus = + container_of(work, struct sdw_bus, kwork); + struct sdw_master *mstr = bus->mstr; + unsigned long flags; + bool slave_present = 0; + + /* Handle the new attached slaves to the bus. Register new slave + * to the bus. + */ + list_for_each_entry_safe(status, __status__, &bus->status_list, node) { + if (status->status[0] == SDW_SLAVE_STAT_ATTACHED_OK) { + ret += sdw_register_slave(mstr); + if (ret) + /* Even if adding new slave fails, we will + * continue. + */ + dev_err(&mstr->dev, "Registering new slave failed\n"); + } + for (i = 1; i <= SOUNDWIRE_MAX_DEVICES; i++) { + slave_present = false; + if (status->status[i] == SDW_SLAVE_STAT_NOT_PRESENT && + mstr->sdw_addr[i].assigned == true) { + /* Logical address was assigned to slave, but + * now its down, so mark it as not present + */ + mstr->sdw_addr[i].status = + SDW_SLAVE_STAT_NOT_PRESENT; + slave_present = true; + } + + else if (status->status[i] == SDW_SLAVE_STAT_ALERT && + mstr->sdw_addr[i].assigned == true) { + ret = 0; + /* Handle slave alerts */ + mstr->sdw_addr[i].status = SDW_SLAVE_STAT_ALERT; + ret = sdw_handle_slave_alerts(mstr, + mstr->sdw_addr[i].slave); + if (ret) + dev_err(&mstr->dev, "Handle slave alert failed for Slave %d\n", i); + + slave_present = true; + + + } else if (status->status[i] == + SDW_SLAVE_STAT_ATTACHED_OK && + mstr->sdw_addr[i].assigned == true) { + + sdw_prog_slv(mstr->sdw_addr[i].slave); + + mstr->sdw_addr[i].status = + SDW_SLAVE_STAT_ATTACHED_OK; + ret = sdw_slv_deprepare_clk_stp1( + mstr->sdw_addr[i].slave); + + /* + * If depreparing Slave fails, no need to + * reprogram Slave, this should never happen + * in ideal case. + */ + if (ret) + continue; + slave_present = true; + } + + if (!slave_present) + continue; + + sdw_send_slave_status(mstr->sdw_addr[i].slave, + &mstr->sdw_addr[i].status); + } + spin_lock_irqsave(&bus->spinlock, flags); + list_del(&status->node); + spin_unlock_irqrestore(&bus->spinlock, flags); + kfree(status); + } +} + +static int sdw_register_master(struct sdw_master *mstr) +{ + int ret = 0; + int i; + struct sdw_bus *sdw_bus; + + /* Can't register until after driver model init */ + if (unlikely(WARN_ON(!sdwint_bus_type.p))) { + ret = -EAGAIN; + goto bus_init_not_done; + } + /* Sanity checks */ + if (unlikely(mstr->name[0] == '\0')) { + pr_err("sdw-core: Attempt to register an master with no name!\n"); + ret = -EINVAL; + goto mstr_no_name; + } + for (i = 0; i <= SOUNDWIRE_MAX_DEVICES; i++) + mstr->sdw_addr[i].slv_number = i; + + rt_mutex_init(&mstr->bus_lock); + INIT_LIST_HEAD(&mstr->slv_list); + INIT_LIST_HEAD(&mstr->mstr_rt_list); + + sdw_bus = kzalloc(sizeof(struct sdw_bus), GFP_KERNEL); + if (!sdw_bus) + goto bus_alloc_failed; + sdw_bus->mstr = mstr; + init_completion(&sdw_bus->async_data.xfer_complete); + + mutex_lock(&sdw_core.core_lock); + list_add_tail(&sdw_bus->bus_node, &sdw_core.bus_list); + mutex_unlock(&sdw_core.core_lock); + + dev_set_name(&mstr->dev, "sdw-%d", mstr->nr); + mstr->dev.bus = &sdwint_bus_type; + mstr->dev.type = &sdw_mstr_type; + + ret = device_register(&mstr->dev); + if (ret) + goto out_list; + kthread_init_worker(&sdw_bus->kworker); + sdw_bus->status_thread = kthread_run(kthread_worker_fn, + &sdw_bus->kworker, "%s", + dev_name(&mstr->dev)); + if (IS_ERR(sdw_bus->status_thread)) { + dev_err(&mstr->dev, "error: failed to create status message task\n"); + ret = PTR_ERR(sdw_bus->status_thread); + goto task_failed; + } + kthread_init_work(&sdw_bus->kwork, handle_slave_status); + INIT_LIST_HEAD(&sdw_bus->status_list); + spin_lock_init(&sdw_bus->spinlock); + ret = sdw_mstr_bw_init(sdw_bus); + if (ret) { + dev_err(&mstr->dev, "error: Failed to init mstr bw\n"); + goto mstr_bw_init_failed; + } + dev_dbg(&mstr->dev, "master [%s] registered\n", mstr->name); + + return 0; + +mstr_bw_init_failed: +task_failed: + device_unregister(&mstr->dev); +out_list: + mutex_lock(&sdw_core.core_lock); + list_del(&sdw_bus->bus_node); + mutex_unlock(&sdw_core.core_lock); + kfree(sdw_bus); +bus_alloc_failed: +mstr_no_name: +bus_init_not_done: + mutex_lock(&sdw_core.core_lock); + idr_remove(&sdw_core.idr, mstr->nr); + mutex_unlock(&sdw_core.core_lock); + return ret; +} + +/** + * sdw_master_update_slv_status: Report the status of slave to the bus driver. + * master calls this function based on the + * interrupt it gets once the slave changes its + * state. + * @mstr: Master handle for which status is reported. + * @status: Array of status of each slave. + */ +int sdw_master_update_slv_status(struct sdw_master *mstr, + struct sdw_status *status) +{ + struct sdw_bus *bus = NULL; + struct sdw_slv_status *slv_status; + unsigned long flags; + + list_for_each_entry(bus, &sdw_core.bus_list, bus_node) { + if (bus->mstr == mstr) + break; + } + /* This is master is not registered with bus driver */ + if (!bus) { + dev_info(&mstr->dev, "Master not registered with bus\n"); + return 0; } slv_status = kzalloc(sizeof(struct sdw_slv_status), GFP_ATOMIC); memcpy(slv_status->status, status, sizeof(struct sdw_status)); @@ -1355,6 +1889,106 @@ void sdw_del_master_controller(struct sdw_master *mstr) } EXPORT_SYMBOL_GPL(sdw_del_master_controller); +/** + * sdw_slave_xfer_bra_block: Transfer the data block using the BTP/BRA + * protocol. + * @mstr: SoundWire Master Master + * @block: Data block to be transferred. + */ +int sdw_slave_xfer_bra_block(struct sdw_master *mstr, + struct sdw_bra_block *block) +{ + struct sdw_bus *sdw_mstr_bs = NULL; + struct sdw_mstr_driver *ops = NULL; + int ret; + + /* + * This API will be called by slave/codec + * when it needs to xfer firmware to + * its memory or perform bulk read/writes of registers. + */ + + /* + * Acquire core lock + * TODO: Acquire Master lock inside core lock + * similar way done in upstream. currently + * keeping it as core lock + */ + mutex_lock(&sdw_core.core_lock); + + /* Get master data structure */ + list_for_each_entry(sdw_mstr_bs, &sdw_core.bus_list, bus_node) { + /* Match master structure pointer */ + if (sdw_mstr_bs->mstr != mstr) + continue; + + break; + } + + /* + * Here assumption is made that complete SDW bandwidth is used + * by BRA. So bus will return -EBUSY if any active stream + * is running on given master. + * TODO: In final implementation extra bandwidth will be always + * allocated for BRA. In that case all the computation of clock, + * frame shape, transport parameters for DP0 will be done + * considering BRA feature. + */ + if (!list_empty(&mstr->mstr_rt_list)) { + + /* + * Currently not allowing BRA when any + * active stream on master, returning -EBUSY + */ + + /* Release lock */ + mutex_unlock(&sdw_core.core_lock); + return -EBUSY; + } + + /* Get master driver ops */ + ops = sdw_mstr_bs->mstr->driver; + + /* + * Check whether Master is supporting bulk transfer. If not, then + * bus will use alternate method of performing BRA request using + * normal register read/write API. + * TODO: Currently if Master is not supporting BRA transfers, bus + * returns error. Bus driver to extend support for normal register + * read/write as alternate method. + */ + if (!ops->mstr_ops->xfer_bulk) + return -EINVAL; + + /* Data port Programming (ON) */ + ret = sdw_bus_bra_xport_config(sdw_mstr_bs, block, true); + if (ret < 0) { + dev_err(&mstr->dev, "BRA: Xport parameter config failed ret=%d\n", ret); + goto error; + } + + /* Bulk Setup */ + ret = ops->mstr_ops->xfer_bulk(mstr, block); + if (ret < 0) { + dev_err(&mstr->dev, "BRA: Transfer failed ret=%d\n", ret); + goto error; + } + + /* Data port Programming (OFF) */ + ret = sdw_bus_bra_xport_config(sdw_mstr_bs, block, false); + if (ret < 0) { + dev_err(&mstr->dev, "BRA: Xport parameter de-config failed ret=%d\n", ret); + goto error; + } + +error: + /* Release lock */ + mutex_unlock(&sdw_core.core_lock); + + return ret; +} +EXPORT_SYMBOL_GPL(sdw_slave_xfer_bra_block); + /* * An sdw_driver is used with one or more sdw_slv (slave) nodes to access * sdw slave chips, on a bus instance associated with some sdw_master. @@ -1432,6 +2066,7 @@ int sdw_register_slave_capabilities(struct sdw_slv *sdw, struct sdw_slv_dpn_capabilities *slv_dpn_cap, *dpn_cap; struct port_audio_mode_properties *prop, *slv_prop; int i, j; + int ret = 0; slv_cap = &sdw->sdw_slv_cap; @@ -1441,6 +2076,8 @@ int sdw_register_slave_capabilities(struct sdw_slv *sdw, slv_cap->clock_stop1_mode_supported = cap->clock_stop1_mode_supported; slv_cap->simplified_clock_stop_prepare = cap->simplified_clock_stop_prepare; + slv_cap->scp_impl_def_intr_mask = cap->scp_impl_def_intr_mask; + slv_cap->highphy_capable = cap->highphy_capable; slv_cap->paging_supported = cap->paging_supported; slv_cap->bank_delay_support = cap->bank_delay_support; @@ -1560,6 +2197,9 @@ int sdw_register_slave_capabilities(struct sdw_slv *sdw, prop->ch_prepare_behavior; } } + ret = sdw_prog_slv(sdw); + if (ret) + return ret; sdw->slave_cap_updated = true; return 0; } @@ -1723,10 +2363,23 @@ static void sdw_release_mstr_stream(struct sdw_master *mstr, struct sdw_runtime *sdw_rt) { struct sdw_mstr_runtime *mstr_rt, *__mstr_rt; + struct sdw_port_runtime *port_rt, *__port_rt, *first_port_rt = NULL; list_for_each_entry_safe(mstr_rt, __mstr_rt, &sdw_rt->mstr_rt_list, mstr_sdw_node) { if (mstr_rt->mstr == mstr) { + + /* Get first runtime node from port list */ + first_port_rt = list_first_entry(&mstr_rt->port_rt_list, + struct sdw_port_runtime, + port_node); + + /* Release Master port resources */ + list_for_each_entry_safe(port_rt, __port_rt, + &mstr_rt->port_rt_list, port_node) + list_del(&port_rt->port_node); + + kfree(first_port_rt); list_del(&mstr_rt->mstr_sdw_node); if (mstr_rt->direction == SDW_DATA_DIR_OUT) sdw_rt->tx_ref_count--; @@ -1744,10 +2397,23 @@ static void sdw_release_slave_stream(struct sdw_slv *slave, struct sdw_runtime *sdw_rt) { struct sdw_slave_runtime *slv_rt, *__slv_rt; + struct sdw_port_runtime *port_rt, *__port_rt, *first_port_rt = NULL; list_for_each_entry_safe(slv_rt, __slv_rt, &sdw_rt->slv_rt_list, slave_sdw_node) { if (slv_rt->slave == slave) { + + /* Get first runtime node from port list */ + first_port_rt = list_first_entry(&slv_rt->port_rt_list, + struct sdw_port_runtime, + port_node); + + /* Release Slave port resources */ + list_for_each_entry_safe(port_rt, __port_rt, + &slv_rt->port_rt_list, port_node) + list_del(&port_rt->port_node); + + kfree(first_port_rt); list_del(&slv_rt->slave_sdw_node); if (slv_rt->direction == SDW_DATA_DIR_OUT) sdw_rt->tx_ref_count--; @@ -1917,7 +2583,6 @@ int sdw_config_stream(struct sdw_master *mstr, } else sdw_rt->rx_ref_count++; - /* SRK: check with hardik */ sdw_rt->type = stream_config->type; sdw_rt->stream_state = SDW_STATE_CONFIG_STREAM; @@ -1947,6 +2612,142 @@ int sdw_config_stream(struct sdw_master *mstr, } EXPORT_SYMBOL_GPL(sdw_config_stream); +/** + * sdw_chk_slv_dpn_caps - Return success + * -EINVAL - In case of error + * + * This function checks all slave port capabilities + * for given stream parameters. If any of parameters + * is not supported in port capabilities, it returns + * error. + */ +int sdw_chk_slv_dpn_caps(struct sdw_slv_dpn_capabilities *dpn_cap, + struct sdw_stream_params *strm_prms) +{ + struct port_audio_mode_properties *mode_prop = + dpn_cap->mode_properties; + int ret = 0, i, value; + + /* Check Sampling frequency */ + if (mode_prop->num_sampling_freq_configs) { + for (i = 0; i < mode_prop->num_sampling_freq_configs; i++) { + + value = mode_prop->sampling_freq_config[i]; + if (strm_prms->rate == value) + break; + } + + if (i == mode_prop->num_sampling_freq_configs) + return -EINVAL; + + } else { + + if ((strm_prms->rate < mode_prop->min_sampling_frequency) + || (strm_prms->rate > + mode_prop->max_sampling_frequency)) + return -EINVAL; + } + + /* check for bit rate */ + if (dpn_cap->num_word_length) { + for (i = 0; i < dpn_cap->num_word_length; i++) { + + value = dpn_cap->word_length_buffer[i]; + if (strm_prms->bps == value) + break; + } + + if (i == dpn_cap->num_word_length) + return -EINVAL; + + } else { + + if ((strm_prms->bps < dpn_cap->min_word_length) + || (strm_prms->bps > dpn_cap->max_word_length)) + return -EINVAL; + } + + /* check for number of channels */ + if (dpn_cap->num_ch_supported) { + for (i = 0; i < dpn_cap->num_ch_supported; i++) { + + value = dpn_cap->ch_supported[i]; + if (strm_prms->bps == value) + break; + } + + if (i == dpn_cap->num_ch_supported) + return -EINVAL; + + } else { + + if ((strm_prms->channel_count < dpn_cap->min_ch_num) + || (strm_prms->channel_count > dpn_cap->max_ch_num)) + return -EINVAL; + } + + return ret; +} + +/** + * sdw_chk_mstr_dpn_caps - Return success + * -EINVAL - In case of error + * + * This function checks all master port capabilities + * for given stream parameters. If any of parameters + * is not supported in port capabilities, it returns + * error. + */ +int sdw_chk_mstr_dpn_caps(struct sdw_mstr_dpn_capabilities *dpn_cap, + struct sdw_stream_params *strm_prms) +{ + + int ret = 0, i, value; + + /* check for bit rate */ + if (dpn_cap->num_word_length) { + for (i = 0; i < dpn_cap->num_word_length; i++) { + + value = dpn_cap->word_length_buffer[i]; + if (strm_prms->bps == value) + break; + } + + if (i == dpn_cap->num_word_length) + return -EINVAL; + + } else { + + if ((strm_prms->bps < dpn_cap->min_word_length) + || (strm_prms->bps > dpn_cap->max_word_length)) { + return -EINVAL; + } + + + } + + /* check for number of channels */ + if (dpn_cap->num_ch_supported) { + for (i = 0; i < dpn_cap->num_ch_supported; i++) { + + value = dpn_cap->ch_supported[i]; + if (strm_prms->bps == value) + break; + } + + if (i == dpn_cap->num_ch_supported) + return -EINVAL; + + } else { + + if ((strm_prms->channel_count < dpn_cap->min_ch_num) + || (strm_prms->channel_count > dpn_cap->max_ch_num)) + return -EINVAL; + } + + return ret; +} + static int sdw_mstr_port_configuration(struct sdw_master *mstr, struct sdw_runtime *sdw_rt, struct sdw_port_config *port_config) @@ -1955,6 +2756,9 @@ static int sdw_mstr_port_configuration(struct sdw_master *mstr, struct sdw_port_runtime *port_rt; int found = 0; int i; + int ret = 0, pn = 0; + struct sdw_mstr_dpn_capabilities *dpn_cap = + mstr->mstr_capabilities.sdw_dpn_cap; list_for_each_entry(mstr_rt, &sdw_rt->mstr_rt_list, mstr_sdw_node) { if (mstr_rt->mstr == mstr) { @@ -1966,16 +2770,35 @@ static int sdw_mstr_port_configuration(struct sdw_master *mstr, dev_err(&mstr->dev, "Master not found for this port\n"); return -EINVAL; } + port_rt = kzalloc((sizeof(struct sdw_port_runtime)) * port_config->num_ports, GFP_KERNEL); if (!port_rt) return -EINVAL; + + if (!dpn_cap) + return -EINVAL; + /* + * Note: Here the assumption the configuration is not + * received for 0th port. + */ for (i = 0; i < port_config->num_ports; i++) { port_rt[i].channel_mask = port_config->port_cfg[i].ch_mask; - port_rt[i].port_num = port_config->port_cfg[i].port_num; + port_rt[i].port_num = pn = port_config->port_cfg[i].port_num; + + /* Perform capability check for master port */ + ret = sdw_chk_mstr_dpn_caps(&dpn_cap[pn], + &mstr_rt->stream_params); + if (ret < 0) { + dev_err(&mstr->dev, + "Master capabilities check failed\n"); + return -EINVAL; + } + list_add_tail(&port_rt[i].port_node, &mstr_rt->port_rt_list); } - return 0; + + return ret; } static int sdw_slv_port_configuration(struct sdw_slv *slave, @@ -1984,8 +2807,10 @@ static int sdw_slv_port_configuration(struct sdw_slv *slave, { struct sdw_slave_runtime *slv_rt; struct sdw_port_runtime *port_rt; - int found = 0; - int i; + struct sdw_slv_dpn_capabilities *dpn_cap = + slave->sdw_slv_cap.sdw_dpn_cap; + int found = 0, ret = 0; + int i, pn; list_for_each_entry(slv_rt, &sdw_rt->slv_rt_list, slave_sdw_node) { if (slv_rt->slave == slave) { @@ -1997,6 +2822,12 @@ static int sdw_slv_port_configuration(struct sdw_slv *slave, dev_err(&slave->mstr->dev, "Slave not found for this port\n"); return -EINVAL; } + + if (!slave->slave_cap_updated) { + dev_err(&slave->mstr->dev, "Slave capabilities not updated\n"); + return -EINVAL; + } + port_rt = kzalloc((sizeof(struct sdw_port_runtime)) * port_config->num_ports, GFP_KERNEL); if (!port_rt) @@ -2004,10 +2835,21 @@ static int sdw_slv_port_configuration(struct sdw_slv *slave, for (i = 0; i < port_config->num_ports; i++) { port_rt[i].channel_mask = port_config->port_cfg[i].ch_mask; - port_rt[i].port_num = port_config->port_cfg[i].port_num; + port_rt[i].port_num = pn = port_config->port_cfg[i].port_num; + + /* Perform capability check for master port */ + ret = sdw_chk_slv_dpn_caps(&dpn_cap[pn], + &slv_rt->stream_params); + if (ret < 0) { + dev_err(&slave->mstr->dev, + "Slave capabilities check failed\n"); + return -EINVAL; + } + list_add_tail(&port_rt[i].port_node, &slv_rt->port_rt_list); } - return 0; + + return ret; } /** @@ -2040,7 +2882,6 @@ int sdw_config_port(struct sdw_master *mstr, struct sdw_runtime *sdw_rt = NULL; struct sdw_stream_tag *stream = NULL; - for (i = 0; i < SDW_NUM_STREAM_TAGS; i++) { if (stream_tags[i].stream_tag == stream_tag) { sdw_rt = stream_tags[i].sdw_rt; @@ -2048,10 +2889,12 @@ int sdw_config_port(struct sdw_master *mstr, break; } } + if (!sdw_rt) { dev_err(&mstr->dev, "Invalid stream tag\n"); return -EINVAL; } + if (static_key_false(&sdw_trace_msg)) { int i; @@ -2060,13 +2903,16 @@ int sdw_config_port(struct sdw_master *mstr, &port_config->port_cfg[i], stream_tag); } } + mutex_lock(&stream->stream_lock); + if (!slave) ret = sdw_mstr_port_configuration(mstr, sdw_rt, port_config); else ret = sdw_slv_port_configuration(slave, sdw_rt, port_config); mutex_unlock(&stream->stream_lock); + return ret; } EXPORT_SYMBOL_GPL(sdw_config_port); @@ -2078,9 +2924,6 @@ int sdw_prepare_and_enable(int stream_tag, bool enable) struct sdw_stream_tag *stream_tags = sdw_core.stream_tags; struct sdw_stream_tag *stream = NULL; - /* TBD: SRK, Check with hardik whether both locks needed - * stream and core?? - */ mutex_lock(&sdw_core.core_lock); for (i = 0; i < SDW_NUM_STREAM_TAGS; i++) { @@ -2200,126 +3043,357 @@ int sdw_wait_for_slave_enumeration(struct sdw_master *mstr, } EXPORT_SYMBOL_GPL(sdw_wait_for_slave_enumeration); -int sdw_prepare_for_clock_change(struct sdw_master *mstr, bool stop, - enum sdw_clk_stop_mode *clck_stop_mode) +static enum sdw_clk_stop_mode sdw_get_clk_stp_mode(struct sdw_slv *slave) { - int i; + enum sdw_clk_stop_mode clock_stop_mode = SDW_CLOCK_STOP_MODE_0; + struct sdw_slv_capabilities *cap = &slave->sdw_slv_cap; + + if (!slave->driver) + return clock_stop_mode; + /* + * Get the dynamic value of clock stop from Slave driver + * if supported, else use the static value from + * capabilities register. Update the capabilities also + * if we have new dynamic value. + */ + if (slave->driver->get_dyn_clk_stp_mod) { + clock_stop_mode = slave->driver->get_dyn_clk_stp_mod(slave); + + if (clock_stop_mode == SDW_CLOCK_STOP_MODE_1) + cap->clock_stop1_mode_supported = true; + else + cap->clock_stop1_mode_supported = false; + } else + clock_stop_mode = cap->clock_stop1_mode_supported; + + return clock_stop_mode; +} + +/** + * sdw_master_stop_clock: Stop the clock. This function broadcasts the SCP_CTRL + * register with clock_stop_now bit set. + * + * @mstr: Master handle for which clock has to be stopped. + * + * Returns 0 on success, appropriate error code on failure. + */ +int sdw_master_stop_clock(struct sdw_master *mstr) +{ + int ret = 0, i; struct sdw_msg msg; u8 buf[1] = {0}; - struct sdw_slave *slave; - enum sdw_clk_stop_mode clock_stop_mode; - int timeout = 0; - int ret = 0; - int slave_dev_present = 0; + enum sdw_clk_stop_mode mode; - /* Find if all slave support clock stop mode1 if all slaves support - * clock stop mode1 use mode1 else use mode0 - */ - for (i = 1; i <= SOUNDWIRE_MAX_DEVICES; i++) { - if (mstr->sdw_addr[i].assigned && - mstr->sdw_addr[i].status != SDW_SLAVE_STAT_NOT_PRESENT) { - slave_dev_present = 1; - slave = mstr->sdw_addr[i].slave; - clock_stop_mode &= - slave->sdw_slv_cap.clock_stop1_mode_supported; - if (!clock_stop_mode) - break; - } - } - if (stop) { - *clck_stop_mode = clock_stop_mode; - dev_info(&mstr->dev, "Entering Clock stop mode %x\n", - clock_stop_mode); - } - /* Slaves might have removed power during its suspend - * in that case no need to do clock stop prepare - * and return from here + /* Send Broadcast message to the SCP_ctrl register with + * clock stop now. If none of the Slaves are attached, then there + * may not be ACK, flag the error about ACK not recevied but + * clock will be still stopped. */ - if (!slave_dev_present) - return 0; - /* Prepare for the clock stop mode. For simplified clock stop - * prepare only mode is to be set, For others set the ClockStop - * Prepare bit in SCP_SystemCtrl register. For all the other slaves - * set the clock stop prepare bit. For all slave set the clock - * stop mode based on what we got in earlier loop + msg.ssp_tag = 0; + msg.flag = SDW_MSG_FLAG_WRITE; + msg.len = 1; + msg.buf = &buf[0]; + msg.slave_addr = SDW_SLAVE_BDCAST_ADDR; + msg.addr_page1 = 0x0; + msg.addr_page2 = 0x0; + msg.addr = SDW_SCP_CTRL; + buf[0] |= 0x1 << SDW_SCP_CTRL_CLK_STP_NOW_SHIFT; + ret = sdw_slave_transfer_nopm(mstr, &msg, 1); + + /* Even if broadcast fails, we stop the clock and flag error */ + if (ret != 1) + dev_err(&mstr->dev, "ClockStopNow Broadcast message failed\n"); + + /* + * Mark all Slaves as un-attached which are entering clock stop + * mode1 */ for (i = 1; i <= SOUNDWIRE_MAX_DEVICES; i++) { + + if (!mstr->sdw_addr[i].assigned) + continue; + + /* Get clock stop mode for all Slaves */ + mode = sdw_get_clk_stp_mode(mstr->sdw_addr[i].slave); + if (mode == SDW_CLOCK_STOP_MODE_0) + continue; + + /* If clock stop mode 1, mark Slave as not present */ + mstr->sdw_addr[i].status = SDW_SLAVE_STAT_NOT_PRESENT; + } + return 0; +} +EXPORT_SYMBOL_GPL(sdw_master_stop_clock); + +static struct sdw_slv *get_slave_for_prep_deprep(struct sdw_master *mstr, + int *slave_index) +{ + int i; + + for (i = *slave_index; i <= SOUNDWIRE_MAX_DEVICES; i++) { if (mstr->sdw_addr[i].assigned != true) continue; + if (mstr->sdw_addr[i].status == SDW_SLAVE_STAT_NOT_PRESENT) continue; - slave = mstr->sdw_addr[i].slave; - msg.ssp_tag = 0; - slave = mstr->sdw_addr[i].slave; - if (stop) { - /* Even if its simplified clock stop prepare - * setting prepare bit wont harm - */ - buf[0] |= (1 << SDW_SCP_SYSTEMCTRL_CLK_STP_PREP_SHIFT); - buf[0] |= clock_stop_mode << - SDW_SCP_SYSTEMCTRL_CLK_STP_MODE_SHIFT; - } - msg.flag = SDW_MSG_FLAG_WRITE; - msg.addr = SDW_SCP_SYSTEMCTRL; - msg.len = 1; - msg.buf = buf; - msg.slave_addr = i; - msg.addr_page1 = 0x0; - msg.addr_page2 = 0x0; - ret = sdw_slave_transfer_nopm(mstr, &msg, 1); - if (ret != 1) { - dev_err(&mstr->dev, "Clock Stop prepare failed\n"); - return -EBUSY; - } + *slave_index = i + 1; + return mstr->sdw_addr[i].slave; } + return NULL; +} + +/* + * Wait till clock stop prepare/deprepare is finished. Prepare for all + * mode, De-prepare only for the Slaves resuming from clock stop mode 0 + */ +static void sdw_wait_for_clk_prep(struct sdw_master *mstr) +{ + int ret; + struct sdw_msg msg; + u8 buf[1] = {0}; + int timeout = 0; + + /* Create message to read clock stop status, its broadcast message. */ + msg.ssp_tag = 0; + msg.flag = SDW_MSG_FLAG_READ; + msg.len = 1; + msg.buf = &buf[0]; + msg.slave_addr = SDW_SLAVE_BDCAST_ADDR; + msg.addr_page1 = 0x0; + msg.addr_page2 = 0x0; + msg.addr = SDW_SCP_STAT; + buf[0] = 0xFF; /* - * Once clock stop prepare bit is set, broadcast the message to read - * ClockStop_NotFinished bit from SCP_Stat, till we read it as 11 - * we dont exit loop. We wait for definite time before retrying - * if its simple clock stop it will be always 1, while for other - * they will driver 0 on bus so we wont get 1. In total we are - * waiting 1 sec before we timeout. + * Once all the Slaves are written with prepare bit, + * we go ahead and broadcast the read message for the + * SCP_STAT register to read the ClockStopNotFinished bit + * Read till we get this a 0. Currently we have timeout of 1sec + * before giving up. Even if its not read as 0 after timeout, + * controller can stop the clock after warning. */ do { - buf[0] = 0xFF; - msg.ssp_tag = 0; - msg.flag = SDW_MSG_FLAG_READ; - msg.addr = SDW_SCP_STAT; - msg.len = 1; - msg.buf = buf; - msg.slave_addr = 15; - msg.addr_page1 = 0x0; - msg.addr_page2 = 0x0; + /* + * Ideally this should not fail, but even if it fails + * in exceptional situation, we go ahead for clock stop + */ ret = sdw_slave_transfer_nopm(mstr, &msg, 1); - if (ret != 1) - goto prepare_failed; + + if (ret != 1) { + WARN_ONCE(1, "Clock stop status read failed\n"); + break; + } if (!(buf[0] & SDW_SCP_STAT_CLK_STP_NF_MASK)) - break; - msleep(100); + break; + + /* + * TODO: Need to find from spec what is requirement. + * Since we are in suspend we should not sleep for more + * Ideally Slave should be ready to stop clock in less than + * few ms. + * So sleep less and increase loop time. This is not + * harmful, since if Slave is ready loop will terminate. + * + */ + msleep(2); timeout++; - } while (timeout != 11); - /* If we are trying to stop and prepare failed its not ok - */ - if (!(buf[0] & SDW_SCP_STAT_CLK_STP_NF_MASK)) { + + } while (timeout != 500); + + if (!(buf[0] & SDW_SCP_STAT_CLK_STP_NF_MASK)) + dev_info(&mstr->dev, "Clock stop prepare done\n"); - return 0; - /* If we are trying to resume and un-prepare failes its ok - * since codec might be down during suspned and will - * start afresh after resuming + else + WARN_ONCE(1, "Some Slaves prepare un-successful\n"); +} + +/** + * sdw_master_prep_for_clk_stop: Prepare all the Slaves for clock stop. + * Iterate through each of the enumerated Slave. + * Prepare each Slave according to the clock stop + * mode supported by Slave. Use dynamic value from + * Slave callback if registered, else use static values + * from Slave capabilities registered. + * 1. Get clock stop mode for each Slave. + * 2. Call pre_prepare callback of each Slave if + * registered. + * 3. Prepare each Slave for clock stop + * 4. Broadcast the Read message to make sure + * all Slaves are prepared for clock stop. + * 5. Call post_prepare callback of each Slave if + * registered. + * + * @mstr: Master handle for which clock state has to be changed. + * + * Returns 0 + */ +int sdw_master_prep_for_clk_stop(struct sdw_master *mstr) +{ + struct sdw_slv_capabilities *cap; + enum sdw_clk_stop_mode clock_stop_mode; + int ret = 0; + struct sdw_slv *slave = NULL; + int slv_index = 1; + + /* + * Get all the Slaves registered to the master driver for preparing + * for clock stop. Start from Slave with logical address as 1. */ - } else if (!stop) { - dev_info(&mstr->dev, "Some Slaves un-prepare un-successful\n"); - return 0; + while ((slave = get_slave_for_prep_deprep(mstr, &slv_index)) != NULL) { + + cap = &slave->sdw_slv_cap; + + clock_stop_mode = sdw_get_clk_stp_mode(slave); + + /* + * Call the pre clock stop prepare, if Slave requires. + */ + if (slave->driver && slave->driver->pre_clk_stop_prep) { + ret = slave->driver->pre_clk_stop_prep(slave, + clock_stop_mode, true); + + /* If it fails we still continue */ + if (ret) + dev_warn(&mstr->dev, "Pre prepare failed for Slave %d\n", + slave->slv_number); + } + + sdw_prep_slave_for_clk_stp(mstr, slave, clock_stop_mode, true); + } + + /* Wait till prepare for all Slaves is finished */ + /* + * We should continue even if the prepare fails. Clock stop + * prepare failure on Slaves, should not impact the broadcasting + * of ClockStopNow. + */ + sdw_wait_for_clk_prep(mstr); + + slv_index = 1; + while ((slave = get_slave_for_prep_deprep(mstr, &slv_index)) != NULL) { + + cap = &slave->sdw_slv_cap; + + clock_stop_mode = sdw_get_clk_stp_mode(slave); + + if (slave->driver && slave->driver->post_clk_stop_prep) { + ret = slave->driver->post_clk_stop_prep(slave, + clock_stop_mode, + true); + /* + * Even if Slave fails we continue with other + * Slaves. This should never happen ideally. + */ + if (ret) + dev_err(&mstr->dev, "Post prepare failed for Slave %d\n", + slave->slv_number); + } + } + + return 0; +} +EXPORT_SYMBOL_GPL(sdw_master_prep_for_clk_stop); + +/** + * sdw_mstr_deprep_after_clk_start: De-prepare all the Slaves + * exiting clock stop mode 0 after clock resumes. Clock + * is already resumed before this. De-prepare all the Slaves + * which were earlier in ClockStop mode0. De-prepare for the + * Slaves which were there in ClockStop mode1 is done after + * they enumerated back. Its not done here as part of master + * getting resumed. + * 1. Get clock stop mode for each Slave its exiting from + * 2. Call pre_prepare callback of each Slave exiting from + * clock stop mode 0. + * 3. De-Prepare each Slave exiting from Clock Stop mode0 + * 4. Broadcast the Read message to make sure + * all Slaves are de-prepared for clock stop. + * 5. Call post_prepare callback of each Slave exiting from + * clock stop mode0 + * + * + * @mstr: Master handle + * + * Returns 0 + */ +int sdw_mstr_deprep_after_clk_start(struct sdw_master *mstr) +{ + struct sdw_slv_capabilities *cap; + enum sdw_clk_stop_mode clock_stop_mode; + int ret = 0; + struct sdw_slv *slave = NULL; + /* We are preparing for stop */ + bool stop = false; + int slv_index = 1; + + while ((slave = get_slave_for_prep_deprep(mstr, &slv_index)) != NULL) { + + cap = &slave->sdw_slv_cap; + + /* Get the clock stop mode from which Slave is exiting */ + clock_stop_mode = sdw_get_clk_stp_mode(slave); + + /* + * Slave is exiting from Clock stop mode 1, De-prepare + * is optional based on capability, and it has to be done + * after Slave is enumerated. So nothing to be done + * here. + */ + if (clock_stop_mode == SDW_CLOCK_STOP_MODE_1) + continue; + /* + * Call the pre clock stop prepare, if Slave requires. + */ + if (slave->driver && slave->driver->pre_clk_stop_prep) + ret = slave->driver->pre_clk_stop_prep(slave, + clock_stop_mode, false); + + /* If it fails we still continue */ + if (ret) + dev_warn(&mstr->dev, "Pre de-prepare failed for Slave %d\n", + slave->slv_number); + + sdw_prep_slave_for_clk_stp(mstr, slave, clock_stop_mode, false); } -prepare_failed: - dev_err(&mstr->dev, "Clock Stop prepare failed\n"); - return -EBUSY; + /* + * Wait till prepare is finished for all the Slaves. + */ + sdw_wait_for_clk_prep(mstr); + + slv_index = 1; + while ((slave = get_slave_for_prep_deprep(mstr, &slv_index)) != NULL) { + + cap = &slave->sdw_slv_cap; + + clock_stop_mode = sdw_get_clk_stp_mode(slave); + + /* + * Slave is exiting from Clock stop mode 1, De-prepare + * is optional based on capability, and it has to be done + * after Slave is enumerated. + */ + if (clock_stop_mode == SDW_CLOCK_STOP_MODE_1) + continue; + if (slave->driver && slave->driver->post_clk_stop_prep) { + ret = slave->driver->post_clk_stop_prep(slave, + clock_stop_mode, + stop); + /* + * Even if Slave fails we continue with other + * Slaves. This should never happen ideally. + */ + if (ret) + dev_err(&mstr->dev, "Post de-prepare failed for Slave %d\n", + slave->slv_number); + } + } + return 0; } -EXPORT_SYMBOL_GPL(sdw_prepare_for_clock_change); +EXPORT_SYMBOL_GPL(sdw_mstr_deprep_after_clk_start); + struct sdw_master *sdw_get_master(int nr) { diff --git a/drivers/sdw/sdw_bwcalc.c b/drivers/sdw/sdw_bwcalc.c index cafaccbeea3a..7ebb26756f59 100644 --- a/drivers/sdw/sdw_bwcalc.c +++ b/drivers/sdw/sdw_bwcalc.c @@ -25,37 +25,38 @@ #include -#define MAXCLOCKFREQ 6 -#ifdef CONFIG_SND_SOC_SVFPGA -/* For PDM Capture, frameshape used is 50x10 */ -int rows[MAX_NUM_ROWS] = {50, 100, 48, 60, 64, 72, 75, 80, 90, - 96, 125, 144, 147, 120, 128, 150, +#ifndef CONFIG_SND_SOC_SVFPGA /* Original */ +#if IS_ENABLED(CONFIG_SND_SOC_INTEL_CNL_FPGA) +int rows[MAX_NUM_ROWS] = {48, 50, 60, 64, 72, 75, 80, 90, + 96, 125, 144, 147, 100, 120, 128, 150, 160, 180, 192, 200, 240, 250, 256}; +#define SDW_DEFAULT_SSP 50 +#else +int rows[MAX_NUM_ROWS] = {125, 64, 48, 50, 60, 72, 75, 80, 90, + 96, 144, 147, 100, 120, 128, 150, + 160, 180, 192, 200, 240, 250, 256}; +#define SDW_DEFAULT_SSP 24 +#endif /* IS_ENABLED(CONFIG_SND_SOC_INTEL_CNL_FPGA) */ -int cols[MAX_NUM_COLS] = {10, 2, 4, 6, 8, 12, 14, 16}; - -int clock_freq[MAXCLOCKFREQ] = {19200000, 19200000, - 19200000, 19200000, - 19200000, 19200000}; +int cols[MAX_NUM_COLS] = {2, 4, 6, 8, 10, 12, 14, 16}; #else -/* TBD: Currently we are using 100x2 as frame shape. to be removed later */ -int rows[MAX_NUM_ROWS] = {100, 48, 50, 60, 64, 72, 75, 80, 90, +/* For PDM Capture, frameshape used is 50x10 */ +int rows[MAX_NUM_ROWS] = {50, 100, 48, 60, 64, 72, 75, 80, 90, 96, 125, 144, 147, 120, 128, 150, 160, 180, 192, 200, 240, 250, 256}; -int cols[MAX_NUM_COLS] = {2, 4, 6, 8, 10, 12, 14, 16}; +int cols[MAX_NUM_COLS] = {10, 2, 4, 6, 8, 12, 14, 16}; +#define SDW_DEFAULT_SSP 50 +#endif /* * TBD: Get supported clock frequency from ACPI and store * it in master data structure. */ -/* Currently only 9.6MHz clock frequency used */ -int clock_freq[MAXCLOCKFREQ] = {9600000, 9600000, - 9600000, 9600000, - 9600000, 9600000}; -#endif +#define MAXCLOCKDIVS 1 +int clock_div[MAXCLOCKDIVS] = {1}; struct sdw_num_to_col sdw_num_col_mapping[MAX_NUM_COLS] = { {0, 2}, {1, 4}, {2, 6}, {3, 8}, {4, 10}, {5, 12}, {6, 14}, {7, 16}, @@ -117,19 +118,8 @@ int sdw_mstr_bw_init(struct sdw_bus *sdw_bs) sdw_bs->frame_freq = 0; sdw_bs->clk_state = SDW_CLK_STATE_ON; sdw_mstr_cap = &sdw_bs->mstr->mstr_capabilities; -#ifdef CONFIG_SND_SOC_SVFPGA - /* TBD: For PDM capture to be removed later */ - sdw_bs->clk_freq = 9.6 * 1000 * 1000 * 2; - sdw_mstr_cap->base_clk_freq = 9.6 * 1000 * 1000 * 2; -#else - /* TBD: Base Clock frequency should be read from - * master capabilities - * Currenly hardcoding to 9.6MHz - */ - sdw_bs->clk_freq = 9.6 * 1000 * 1000; - sdw_mstr_cap->base_clk_freq = 9.6 * 1000 * 1000; + sdw_bs->clk_freq = (sdw_mstr_cap->base_clk_freq * 2); -#endif return 0; } EXPORT_SYMBOL_GPL(sdw_mstr_bw_init); @@ -203,9 +193,8 @@ int sdw_lcm(int num1, int num2) * transport and port parameters. */ int sdw_cfg_slv_params(struct sdw_bus *mstr_bs, - struct sdw_slave_runtime *slv_rt, struct sdw_transport_params *t_slv_params, - struct sdw_port_params *p_slv_params) + struct sdw_port_params *p_slv_params, int slv_number) { struct sdw_msg wr_msg, wr_msg1, rd_msg; int ret = 0; @@ -241,7 +230,7 @@ int sdw_cfg_slv_params(struct sdw_bus *mstr_bs, wbuf[2] = ((t_slv_params->sample_interval - 1) >> 8) & SDW_DPN_SAMPLECTRL1_LOW_MASK; /* DPN_SampleCtrl2 */ wbuf[3] = t_slv_params->offset1; /* DPN_OffsetCtrl1 */ - wbuf[4] = t_slv_params->offset2; /* DPN_OffsetCtrl1 */ + wbuf[4] = t_slv_params->offset2; /* DPN_OffsetCtrl2 */ /* DPN_HCtrl */ wbuf[5] = (t_slv_params->hstop | (t_slv_params->hstart << 4)); wbuf[6] = t_slv_params->blockpackingmode; /* DPN_BlockCtrl3 */ @@ -256,21 +245,18 @@ int sdw_cfg_slv_params(struct sdw_bus *mstr_bs, rd_msg.ssp_tag = 0x0; rd_msg.flag = SDW_MSG_FLAG_READ; rd_msg.len = 1; - rd_msg.slave_addr = slv_rt->slave->slv_number; + rd_msg.slave_addr = slv_number; + rd_msg.buf = rbuf; rd_msg.addr_page1 = 0x0; rd_msg.addr_page2 = 0x0; -/* Dont program slave params for the Aggregation. - * Its with master loop back - */ -#ifndef CONFIG_SND_SOC_MXFPGA + ret = sdw_slave_transfer(mstr_bs->mstr, &rd_msg, 1); if (ret != 1) { ret = -EINVAL; dev_err(&mstr_bs->mstr->dev, "Register transfer failed\n"); goto out; } -#endif wbuf1[0] = (p_slv_params->port_flow_mode | (p_slv_params->port_data_mode << @@ -295,7 +281,8 @@ int sdw_cfg_slv_params(struct sdw_bus *mstr_bs, #else wr_msg.len = (7 + (1 * (t_slv_params->blockgroupcontrol_valid))); #endif - wr_msg.slave_addr = slv_rt->slave->slv_number; + + wr_msg.slave_addr = slv_number; wr_msg.buf = &wbuf[0 + (1 * (!t_slv_params->blockgroupcontrol_valid))]; wr_msg.addr_page1 = 0x0; wr_msg.addr_page2 = 0x0; @@ -303,14 +290,12 @@ int sdw_cfg_slv_params(struct sdw_bus *mstr_bs, wr_msg1.ssp_tag = 0x0; wr_msg1.flag = SDW_MSG_FLAG_WRITE; wr_msg1.len = 2; - wr_msg1.slave_addr = slv_rt->slave->slv_number; + + wr_msg1.slave_addr = slv_number; wr_msg1.buf = &wbuf1[0]; wr_msg1.addr_page1 = 0x0; wr_msg1.addr_page2 = 0x0; -/* Dont program slave params for the Aggregation. - * Its with master loop back - */ -#ifndef CONFIG_SND_SOC_MXFPGA + ret = sdw_slave_transfer(mstr_bs->mstr, &wr_msg, 1); if (ret != 1) { ret = -EINVAL; @@ -326,7 +311,6 @@ int sdw_cfg_slv_params(struct sdw_bus *mstr_bs, goto out; } out: -#endif return ret; } @@ -370,119 +354,16 @@ int sdw_cfg_mstr_params(struct sdw_bus *mstr_bs, return 0; } - -/* - * sdw_cfg_mstr_slv - returns Success - * -EINVAL - In case of error. - * - * - * This function call master/slave transport/port - * params configuration API's, called from sdw_bus_calc_bw - * & sdw_bus_calc_bw_dis API's. - */ -int sdw_cfg_mstr_slv(struct sdw_bus *sdw_mstr_bs, - struct sdw_mstr_runtime *sdw_mstr_bs_rt, - bool is_master) -{ - struct sdw_transport_params *t_params, *t_slv_params; - struct sdw_port_params *p_params, *p_slv_params; - struct sdw_slave_runtime *slv_rt = NULL; - struct sdw_port_runtime *port_rt, *port_slv_rt; - int ret = 0; - - if (is_master) { - /* should not compute any transport params */ - if (sdw_mstr_bs_rt->rt_state == SDW_STATE_UNPREPARE_RT) - return 0; - - list_for_each_entry(port_rt, - &sdw_mstr_bs_rt->port_rt_list, port_node) { - - /* Transport and port parameters */ - t_params = &port_rt->transport_params; - p_params = &port_rt->port_params; - - p_params->num = port_rt->port_num; - p_params->word_length = - sdw_mstr_bs_rt->stream_params.bps; - p_params->port_flow_mode = 0x0; /* Isochronous Mode */ - p_params->port_data_mode = 0x0; /* Normal Mode */ - - /* Configure xport params and port params for master */ - ret = sdw_cfg_mstr_params(sdw_mstr_bs, - t_params, p_params); - if (ret < 0) - return ret; - - /* Since one port per master runtime, - * breaking port_list loop - * TBD: to be extended for multiple port support - */ - - break; - } - - } else { - - - list_for_each_entry(slv_rt, - &sdw_mstr_bs_rt->slv_rt_list, slave_node) { - - if (slv_rt->slave == NULL) - break; - - /* should not compute any transport params */ - if (slv_rt->rt_state == SDW_STATE_UNPREPARE_RT) - continue; - - list_for_each_entry(port_slv_rt, - &slv_rt->port_rt_list, port_node) { - - /* Fill in port params here */ - port_slv_rt->port_params.num = - port_slv_rt->port_num; - port_slv_rt->port_params.word_length = - slv_rt->stream_params.bps; - /* Isochronous Mode */ - port_slv_rt->port_params.port_flow_mode = 0x0; - /* Normal Mode */ - port_slv_rt->port_params.port_data_mode = 0x0; - t_slv_params = &port_slv_rt->transport_params; - p_slv_params = &port_slv_rt->port_params; - - /* Configure xport & port params for slave */ - ret = sdw_cfg_slv_params(sdw_mstr_bs, - slv_rt, t_slv_params, p_slv_params); - if (ret < 0) - return ret; - - /* Since one port per slave runtime, - * breaking port_list loop - * TBD: to be extended for multiple - * port support - */ - - break; - } - } - - } - - return 0; -} - - /* - * sdw_cpy_params_mstr_slv - returns Success - * -EINVAL - In case of error. - * + * sdw_cfg_params_mstr_slv - returns Success * * This function copies/configure master/slave transport & - * port params to alternate bank. + * port params. * */ -int sdw_cpy_params_mstr_slv(struct sdw_bus *sdw_mstr_bs, - struct sdw_mstr_runtime *sdw_mstr_bs_rt) +int sdw_cfg_params_mstr_slv(struct sdw_bus *sdw_mstr_bs, + struct sdw_mstr_runtime *sdw_mstr_bs_rt, + bool state_check) { struct sdw_slave_runtime *slv_rt = NULL; struct sdw_port_runtime *port_rt, *port_slv_rt; @@ -496,6 +377,11 @@ int sdw_cpy_params_mstr_slv(struct sdw_bus *sdw_mstr_bs, if (slv_rt->slave == NULL) break; + /* configure transport params based on state */ + if ((state_check) && + (slv_rt->rt_state == SDW_STATE_UNPREPARE_RT)) + continue; + list_for_each_entry(port_slv_rt, &slv_rt->port_rt_list, port_node) { @@ -511,20 +397,17 @@ int sdw_cpy_params_mstr_slv(struct sdw_bus *sdw_mstr_bs, p_slv_params = &port_slv_rt->port_params; /* Configure xport & port params for slave */ - ret = sdw_cfg_slv_params(sdw_mstr_bs, - slv_rt, t_slv_params, p_slv_params); + ret = sdw_cfg_slv_params(sdw_mstr_bs, t_slv_params, + p_slv_params, slv_rt->slave->slv_number); if (ret < 0) return ret; - /* - * Since one port per slave runtime, - * breaking port_list loop - * TBD: to be extended for multiple port support - */ - break; } } + if ((state_check) && + (sdw_mstr_bs_rt->rt_state == SDW_STATE_UNPREPARE_RT)) + return 0; list_for_each_entry(port_rt, &sdw_mstr_bs_rt->port_rt_list, port_node) { @@ -544,10 +427,6 @@ int sdw_cpy_params_mstr_slv(struct sdw_bus *sdw_mstr_bs, if (ret < 0) return ret; - /* Since one port per slave runtime, breaking port_list loop - * TBD: to be extended for multiple port support - */ - break; } return 0; @@ -608,10 +487,6 @@ int sdw_cfg_slv_enable_disable(struct sdw_bus *mstr_bs, */ /* 2. slave port enable */ -/* Dont program slave params for the Aggregation. - * Its with master loop back - */ -#ifndef CONFIG_SND_SOC_MXFPGA ret = sdw_slave_transfer(mstr_bs->mstr, &rd_msg, 1); if (ret != 1) { ret = -EINVAL; @@ -638,7 +513,6 @@ int sdw_cfg_slv_enable_disable(struct sdw_bus *mstr_bs, "Register transfer failed\n"); goto out; } -#endif /* * 3. slave port enable post pre * --> callback @@ -653,10 +527,6 @@ int sdw_cfg_slv_enable_disable(struct sdw_bus *mstr_bs, * --> callback * --> no callback available */ -/* Dont program slave params for the Aggregation. - * Its with master loop back - */ -#ifndef CONFIG_SND_SOC_MXFPGA /* 2. slave port disable */ ret = sdw_slave_transfer(mstr_bs->mstr, &rd_msg, 1); @@ -685,7 +555,7 @@ int sdw_cfg_slv_enable_disable(struct sdw_bus *mstr_bs, "Register transfer failed\n"); goto out; } -#endif + /* * 3. slave port enable post unpre * --> callback @@ -695,9 +565,7 @@ int sdw_cfg_slv_enable_disable(struct sdw_bus *mstr_bs, slv_rt_strm->rt_state = SDW_STATE_DISABLE_RT; } -#ifndef CONFIG_SND_SOC_MXFPGA out: -#endif return ret; } @@ -783,13 +651,6 @@ int sdw_en_dis_mstr_slv(struct sdw_bus *sdw_mstr_bs, if (ret < 0) return ret; - /* - * Since one port per slave runtime, - * breaking port_list loop - * TBD: to be extended for multiple port support - */ - break; - } break; @@ -811,13 +672,6 @@ int sdw_en_dis_mstr_slv(struct sdw_bus *sdw_mstr_bs, if (ret < 0) return ret; - /* - * Since one port per master runtime, - * breaking port_list loop - * TBD: to be extended for multiple port support - */ - break; - } } @@ -858,13 +712,6 @@ int sdw_en_dis_mstr_slv_state(struct sdw_bus *sdw_mstr_bs, if (ret < 0) return ret; - /* - * Since one port per slave runtime, - * breaking port_list loop - * TBD: to be extended for multiple - * port support - */ - break; } } } @@ -879,13 +726,6 @@ int sdw_en_dis_mstr_slv_state(struct sdw_bus *sdw_mstr_bs, if (ret < 0) return ret; - /* - * Since one port per master runtime, - * breaking port_list loop - * TBD: to be extended for multiple port support - */ - - break; } } @@ -902,41 +742,109 @@ int sdw_en_dis_mstr_slv_state(struct sdw_bus *sdw_mstr_bs, * clock frequency. */ int sdw_get_clock_frmshp(struct sdw_bus *sdw_mstr_bs, int *frame_int, - int *col, int *row) + struct sdw_mstr_runtime *sdw_mstr_rt) { - int i, rc, clock_reqd = 0, frame_interval = 0, frame_frequency = 0; - int sel_row = 0, sel_col = 0; + struct sdw_master_capabilities *sdw_mstr_cap = NULL; + struct sdw_slv_dpn_capabilities *sdw_slv_dpn_cap = NULL; + struct port_audio_mode_properties *mode_prop = NULL; + struct sdw_slave_runtime *slv_rt = NULL; + struct sdw_port_runtime *port_slv_rt = NULL; + int i, j, rc; + int clock_reqd = 0, frame_interval = 0, frame_frequency = 0; + int sel_row = 0, sel_col = 0, pn = 0; + int value; bool clock_ok = false; + sdw_mstr_cap = &sdw_mstr_bs->mstr->mstr_capabilities; + /* * Find nearest clock frequency needed by master for * given bandwidth */ - - /* - * TBD: Need to run efficient algorithm to make sure we have - * only 1 to 10 percent of control bandwidth usage - */ - for (i = 0; i < MAXCLOCKFREQ; i++) { + for (i = 0; i < MAXCLOCKDIVS; i++) { /* TBD: Check why 3000 */ - if ((clock_freq[i] <= sdw_mstr_bs->bandwidth) || - ((clock_freq[i] % 3000) != 0)) + if ((((sdw_mstr_cap->base_clk_freq * 2) / clock_div[i]) <= + sdw_mstr_bs->bandwidth) || + ((((sdw_mstr_cap->base_clk_freq * 2) / clock_div[i]) + % 3000) != 0)) continue; - clock_reqd = clock_freq[i]; + + clock_reqd = ((sdw_mstr_cap->base_clk_freq * 2) / clock_div[i]); /* - * TBD: Check all the slave device capabilities + * Check all the slave device capabilities * here and find whether given frequency is * supported by all slaves */ + list_for_each_entry(slv_rt, &sdw_mstr_rt->slv_rt_list, + slave_node) { + + /* check for valid slave */ + if (slv_rt->slave == NULL) + break; + + /* check clock req for each port */ + list_for_each_entry(port_slv_rt, + &slv_rt->port_rt_list, port_node) { + + pn = port_slv_rt->port_num; + + + sdw_slv_dpn_cap = + &slv_rt->slave->sdw_slv_cap.sdw_dpn_cap[pn]; + mode_prop = sdw_slv_dpn_cap->mode_properties; + + /* + * TBD: Indentation to be fixed, + * code refactoring to be considered. + */ + if (mode_prop->num_freq_configs) { + for (j = 0; j < + mode_prop->num_freq_configs; j++) { + value = + mode_prop->freq_supported[j]; + if (clock_reqd == value) { + clock_ok = true; + break; + } + if (j == + mode_prop->num_freq_configs) { + clock_ok = false; + break; + } + + } + + } else { + if ((clock_reqd < + mode_prop->min_frequency) || + (clock_reqd > + mode_prop->max_frequency)) { + clock_ok = false; + } else + clock_ok = true; + } + + /* Go for next clock frequency */ + if (!clock_ok) + break; + } + + /* + * Dont check next slave, go for next clock + * frequency + */ + if (!clock_ok) + break; + } + + /* check for next clock divider */ + if (!clock_ok) + continue; /* Find frame shape based on bandwidth per controller */ - /* - * TBD: Need to run efficient algorithm to make sure we have - * only 1 to 10 percent of control bandwidth usage - */ - for (rc = 0; rc <= MAX_NUM_ROW_COLS; rc++) { + for (rc = 0; rc < MAX_NUM_ROW_COLS; rc++) { frame_interval = sdw_core.rowcolcomb[rc].row * sdw_core.rowcolcomb[rc].col; @@ -952,21 +860,27 @@ int sdw_get_clock_frmshp(struct sdw_bus *sdw_mstr_bs, int *frame_int, break; } + /* Valid frameshape not found, check for next clock freq */ + if (rc == MAX_NUM_ROW_COLS) + continue; + sel_row = sdw_core.rowcolcomb[rc].row; sel_col = sdw_core.rowcolcomb[rc].col; sdw_mstr_bs->frame_freq = frame_frequency; sdw_mstr_bs->clk_freq = clock_reqd; + sdw_mstr_bs->clk_div = clock_div[i]; clock_ok = false; *frame_int = frame_interval; - *col = sel_col; - *row = sel_row; sdw_mstr_bs->col = sel_col; sdw_mstr_bs->row = sel_row; - break; - + return 0; } + /* None of clock frequency matches, return error */ + if (i == MAXCLOCKDIVS) + return -EINVAL; + return 0; } @@ -982,74 +896,95 @@ int sdw_compute_sys_interval(struct sdw_bus *sdw_mstr_bs, int frame_interval) { struct sdw_master *sdw_mstr = sdw_mstr_bs->mstr; - struct sdw_mstr_runtime *sdw_mstr_bs_rt; - struct sdw_transport_params *t_params; - struct sdw_port_runtime *port_rt; + struct sdw_mstr_runtime *sdw_mstr_rt = NULL; + struct sdw_slave_runtime *slv_rt = NULL; + struct sdw_transport_params *t_params = NULL, *t_slv_params = NULL; + struct sdw_port_runtime *port_rt, *port_slv_rt; int lcmnum1 = 0, lcmnum2 = 0, div = 0, lcm = 0; + int sample_interval; /* * once you got bandwidth frame shape for bus, * run a loop for all the active streams running - * on bus and compute sample_interval & other transport parameters. + * on bus and compute stream interval & sample_interval. */ - list_for_each_entry(sdw_mstr_bs_rt, + list_for_each_entry(sdw_mstr_rt, &sdw_mstr->mstr_rt_list, mstr_node) { - if (sdw_mstr_bs_rt->mstr == NULL) + if (sdw_mstr_rt->mstr == NULL) break; - /* should not compute any transport params */ - if (sdw_mstr_bs_rt->rt_state == SDW_STATE_UNPREPARE_RT) - continue; + /* + * Calculate sample interval for stream + * running on given master. + */ + if (sdw_mstr_rt->stream_params.rate) + sample_interval = (sdw_mstr_bs->clk_freq/ + sdw_mstr_rt->stream_params.rate); + else + return -EINVAL; + /* Run port loop to assign sample interval per port */ list_for_each_entry(port_rt, - &sdw_mstr_bs_rt->port_rt_list, port_node) { + &sdw_mstr_rt->port_rt_list, port_node) { t_params = &port_rt->transport_params; /* - * Current Assumption: - * One port per bus runtime structure + * Assign sample interval each port transport + * properties. Assumption is that sample interval + * per port for given master will be same. */ - /* Calculate sample interval */ -#ifdef CONFIG_SND_SOC_SVFPGA - t_params->sample_interval = - ((sdw_mstr_bs->clk_freq/ - sdw_mstr_bs_rt->stream_params.rate)); -#else - t_params->sample_interval = - ((sdw_mstr_bs->clk_freq/ - sdw_mstr_bs_rt->stream_params.rate) * 2); + t_params->sample_interval = sample_interval; + } -#endif - /* Only BlockPerPort supported */ - t_params->blockpackingmode = 0; - t_params->lanecontrol = 0; + /* Calculate LCM */ + lcmnum2 = sample_interval; + if (!lcmnum1) + lcmnum1 = sdw_lcm(lcmnum2, lcmnum2); + else + lcmnum1 = sdw_lcm(lcmnum1, lcmnum2); - /* Calculate LCM */ - lcmnum2 = t_params->sample_interval; - if (!lcmnum1) - lcmnum1 = sdw_lcm(lcmnum2, lcmnum2); - else - lcmnum1 = sdw_lcm(lcmnum1, lcmnum2); + /* Run loop for slave per master runtime */ + list_for_each_entry(slv_rt, + &sdw_mstr_rt->slv_rt_list, slave_node) { - /* - * Since one port per bus runtime, breaking - * port_list loop - * TBD: to be extended for multiple port support - */ - break; + if (slv_rt->slave == NULL) + break; + + /* Assign sample interval for each port of slave */ + list_for_each_entry(port_slv_rt, + &slv_rt->port_rt_list, port_node) { + + t_slv_params = &port_slv_rt->transport_params; + /* Assign sample interval each port */ + t_slv_params->sample_interval = sample_interval; + } } } + /* + * If system interval already calculated + * In pause/resume, underrun scenario + */ + if (sdw_mstr_bs->system_interval) + return 0; + + /* Assign frame stream interval */ + sdw_mstr_bs->stream_interval = lcmnum1; /* 6. compute system_interval */ if ((sdw_mstr_cap) && (sdw_mstr_bs->clk_freq)) { div = ((sdw_mstr_cap->base_clk_freq * 2) / sdw_mstr_bs->clk_freq); - lcm = sdw_lcm(lcmnum1, frame_interval); + + if ((lcmnum1) && (frame_interval)) + lcm = sdw_lcm(lcmnum1, frame_interval); + else + return -EINVAL; + sdw_mstr_bs->system_interval = (div * lcm); } @@ -1065,6 +1000,25 @@ int sdw_compute_sys_interval(struct sdw_bus *sdw_mstr_bs, return 0; } +/** + * sdw_chk_first_node - returns True or false + * + * This function returns true in case of first node + * else returns false. + */ +bool sdw_chk_first_node(struct sdw_mstr_runtime *sdw_mstr_rt, + struct sdw_master *sdw_mstr) +{ + struct sdw_mstr_runtime *first_rt = NULL; + + first_rt = list_first_entry(&sdw_mstr->mstr_rt_list, + struct sdw_mstr_runtime, mstr_node); + if (sdw_mstr_rt == first_rt) + return true; + else + return false; + +} /* * sdw_compute_hstart_hstop - returns Success @@ -1074,273 +1028,199 @@ int sdw_compute_sys_interval(struct sdw_bus *sdw_mstr_bs, * This function computes hstart and hstop for running * streams per master & slaves. */ -int sdw_compute_hstart_hstop(struct sdw_bus *sdw_mstr_bs, int sel_col) +int sdw_compute_hstart_hstop(struct sdw_bus *sdw_mstr_bs) { struct sdw_master *sdw_mstr = sdw_mstr_bs->mstr; - struct sdw_mstr_runtime *sdw_mstr_bs_rt; + struct sdw_mstr_runtime *sdw_mstr_rt; struct sdw_transport_params *t_params = NULL, *t_slv_params = NULL; struct sdw_slave_runtime *slv_rt = NULL; struct sdw_port_runtime *port_rt, *port_slv_rt; - int hstop = 0, hwidth = 0; - int payload_bw = 0, full_bw = 0, column_needed = 0; - bool hstop_flag = false; - - /* Calculate hwidth, hstart and hstop */ - list_for_each_entry(sdw_mstr_bs_rt, + int hstart = 0, hstop = 0; + int column_needed = 0; + int sel_col = sdw_mstr_bs->col; + int group_count = 0, no_of_channels = 0; + struct temp_elements *temp, *element; + int rates[10]; + int num, ch_mask, block_offset, i, port_block_offset; + + /* Run loop for all master runtimes for given master */ + list_for_each_entry(sdw_mstr_rt, &sdw_mstr->mstr_rt_list, mstr_node) { - if (sdw_mstr_bs_rt->mstr == NULL) + if (sdw_mstr_rt->mstr == NULL) break; /* should not compute any transport params */ - if (sdw_mstr_bs_rt->rt_state == SDW_STATE_UNPREPARE_RT) + if (sdw_mstr_rt->rt_state == SDW_STATE_UNPREPARE_RT) continue; - list_for_each_entry(port_rt, - &sdw_mstr_bs_rt->port_rt_list, port_node) { - - t_params = &port_rt->transport_params; - t_params->num = port_rt->port_num; + /* Perform grouping of streams based on stream rate */ + if (sdw_mstr_rt == list_first_entry(&sdw_mstr->mstr_rt_list, + struct sdw_mstr_runtime, mstr_node)) + rates[group_count++] = sdw_mstr_rt->stream_params.rate; + else { + num = group_count; + for (i = 0; i < num; i++) { + if (sdw_mstr_rt->stream_params.rate == rates[i]) + break; - /* - * 1. find full_bw and payload_bw per stream - * 2. find h_width per stream - * 3. find hstart, hstop, block_offset,sub_block_offset - * Note: full_bw is nothing but sampling interval - * of stream. - * payload_bw is serving size no. - * of channels * bps per stream - */ - full_bw = sdw_mstr_bs->clk_freq/ - sdw_mstr_bs_rt->stream_params.rate; - payload_bw = - sdw_mstr_bs_rt->stream_params.bps * - sdw_mstr_bs_rt->stream_params.channel_count; + if (i == num) + rates[group_count++] = + sdw_mstr_rt->stream_params.rate; + } + } + } - hwidth = (sel_col * payload_bw + full_bw - 1)/full_bw; - column_needed += hwidth; + /* check for number of streams and number of group count */ + if (group_count == 0) + return 0; - /* - * These needs to be done only for - * 1st entry in link list - */ - if (!hstop_flag) { - hstop = sel_col - 1; - hstop_flag = true; - } + /* Allocate temporary memory holding temp variables */ + temp = kzalloc((sizeof(struct temp_elements) * group_count), + GFP_KERNEL); + if (!temp) + return -ENOMEM; - /* Assumption: Only block per port is supported - * For blockperport: - * offset1 value = LSB 8 bits of block_offset value - * offset2 value = MSB 8 bits of block_offset value - * For blockperchannel: - * offset1 = LSB 8 bit of block_offset value - * offset2 = MSB 8 bit of sub_block_offload value - * if hstart and hstop of different streams in - * master are different, then block_offset is zero. - * if not then block_offset value for 2nd stream - * is block_offset += payload_bw - */ + /* Calculate full bandwidth per group */ + for (i = 0; i < group_count; i++) { + element = &temp[i]; + element->rate = rates[i]; + element->full_bw = sdw_mstr_bs->clk_freq/element->rate; + } - t_params->hstop = hstop; -#ifdef CONFIG_SND_SOC_SVFPGA - /* For PDM capture, 0th col is also used */ - t_params->hstart = 0; -#else - t_params->hstart = hstop - hwidth + 1; -#endif + /* Calculate payload bandwidth per group */ + list_for_each_entry(sdw_mstr_rt, + &sdw_mstr->mstr_rt_list, mstr_node) { - /* - * TBD: perform this when you have 2 ports - * and accordingly configure hstart hstop for slave - * removing for now - */ -#if 0 - hstop = hstop - hwidth; -#endif - /* Since one port per bus runtime, - * breaking port_list loop - * TBD: to be extended for multiple port support - */ + if (sdw_mstr_rt->mstr == NULL) break; - } - /* - * Run loop for slave_rt_list for given master_list - * to compute hstart hstop for slave - */ - list_for_each_entry(slv_rt, - &sdw_mstr_bs_rt->slv_rt_list, slave_node) { + /* should not compute any transport params */ + if (sdw_mstr_rt->rt_state == SDW_STATE_UNPREPARE_RT) + continue; - if (slv_rt->slave == NULL) - break; + for (i = 0; i < group_count; i++) { + element = &temp[i]; + if (sdw_mstr_rt->stream_params.rate == element->rate) { + element->payload_bw += + sdw_mstr_rt->stream_params.bps * + sdw_mstr_rt->stream_params.channel_count; + } - if (slv_rt->rt_state == SDW_STATE_UNPREPARE_RT) - continue; + /* Any of stream rate should match */ + if (i == group_count) + return -EINVAL; + } + } - list_for_each_entry(port_slv_rt, - &slv_rt->port_rt_list, port_node) { + /* Calculate hwidth per group and total column needed per master */ + for (i = 0; i < group_count; i++) { + element = &temp[i]; + element->hwidth = + (sel_col * element->payload_bw + + element->full_bw - 1)/element->full_bw; + column_needed += element->hwidth; + } - t_slv_params = &port_slv_rt->transport_params; - t_slv_params->num = port_slv_rt->port_num; - - /* - * TBD: Needs to be verifid for - * multiple combination - * 1. 1 master port, 1 slave rt, - * 1 port per slave rt --> - * In this case, use hstart hstop same as master - * for 1 slave rt - * 2. 1 master port, 2 slave rt, - * 1 port per slave rt --> - * In this case, use hstart hstop same as master - * for 2 slave rt - * only offset will change for 2nd slave rt - * Current assumption is one port per rt, - * hence no multiple port combination - * considered. - */ - t_slv_params->hstop = hstop; - t_slv_params->hstart = hstop - hwidth + 1; - - /* Only BlockPerPort supported */ - t_slv_params->blockpackingmode = 0; - t_slv_params->lanecontrol = 0; - - /* - * below copy needs to be changed when - * more than one port is supported - */ - if (t_params) - t_slv_params->sample_interval = - t_params->sample_interval; - - /* Since one port per slave runtime, - * breaking port_list loop - * TBD: to be extended for multiple - * port support - */ - break; - } - - } - } - -#if 0 - /* TBD: To be verified */ + /* Check column required should not be greater than selected columns*/ if (column_needed > sel_col - 1) - return -EINVAL; /* Error case, check what has gone wrong */ -#endif + return -EINVAL; - return 0; -} + /* Compute hstop */ + hstop = sel_col - 1; + /* Run loop for all groups to compute transport parameters */ + for (i = 0; i < group_count; i++) { + port_block_offset = block_offset = 1; + element = &temp[i]; -/* - * sdw_compute_blk_subblk_offset - returns Success - * - * - * This function computes block offset and sub block - * offset for running streams per master & slaves. - */ -int sdw_compute_blk_subblk_offset(struct sdw_bus *sdw_mstr_bs) -{ - struct sdw_master *sdw_mstr = sdw_mstr_bs->mstr; - struct sdw_mstr_runtime *sdw_mstr_bs_rt; - struct sdw_transport_params *t_params, *t_slv_params; - struct sdw_slave_runtime *slv_rt = NULL; - struct sdw_port_runtime *port_rt, *port_slv_rt; - int hstart1 = 0, hstop1 = 0, hstart2 = 0, hstop2 = 0; - int block_offset = 1; + /* Find streams associated with each group */ + list_for_each_entry(sdw_mstr_rt, + &sdw_mstr->mstr_rt_list, mstr_node) { + if (sdw_mstr_rt->mstr == NULL) + break; - /* Calculate block_offset and subblock_offset */ - list_for_each_entry(sdw_mstr_bs_rt, - &sdw_mstr->mstr_rt_list, mstr_node) { + /* should not compute any transport params */ + if (sdw_mstr_rt->rt_state == SDW_STATE_UNPREPARE_RT) + continue; - if (sdw_mstr_bs_rt->mstr == NULL) - break; + if (sdw_mstr_rt->stream_params.rate != element->rate) + continue; - /* should not compute any transport params */ - if (sdw_mstr_bs_rt->rt_state == SDW_STATE_UNPREPARE_RT) - continue; + /* Compute hstart */ + sdw_mstr_rt->hstart = hstart = + hstop - element->hwidth + 1; + sdw_mstr_rt->hstop = hstop; - list_for_each_entry(port_rt, - &sdw_mstr_bs_rt->port_rt_list, port_node) { + /* Assign hstart, hstop, block offset for each port */ + list_for_each_entry(port_rt, + &sdw_mstr_rt->port_rt_list, port_node) { - t_params = &port_rt->transport_params; + t_params = &port_rt->transport_params; + t_params->num = port_rt->port_num; + t_params->hstart = hstart; + t_params->hstop = hstop; + t_params->offset1 = port_block_offset; + t_params->offset2 = port_block_offset >> 8; + /* Only BlockPerPort supported */ + t_params->blockgroupcontrol_valid = true; + t_params->blockgroupcontrol = 0x0; + t_params->lanecontrol = 0x0; + /* Copy parameters if first node */ + if (port_rt == list_first_entry + (&sdw_mstr_rt->port_rt_list, + struct sdw_port_runtime, port_node)) { - if ((!hstart2) && (!hstop2)) { - hstart1 = hstart2 = t_params->hstart; - hstop1 = hstop2 = t_params->hstop; - /* TBD: Verify this condition */ -#ifdef CONFIG_SND_SOC_SVFPGA - block_offset = 1; -#else - block_offset = 0; -#endif - } else { + sdw_mstr_rt->hstart = hstart; + sdw_mstr_rt->hstop = hstop; - hstart1 = t_params->hstart; - hstop1 = t_params->hstop; + sdw_mstr_rt->block_offset = + port_block_offset; -#ifndef CONFIG_SND_SOC_SVFPGA - /* hstart/stop not same */ - if ((hstart1 != hstart2) && - (hstop1 != hstop2)) { - /* TBD: Harcoding to 0, to be removed*/ - block_offset = 0; - } else { - /* TBD: Harcoding to 0, to be removed*/ - block_offset = 0; - } -#else - if ((hstart1 != hstart2) && - (hstop1 != hstop2)) { - block_offset = 1; - } else { -/* We are doing loopback for the Aggregation so block offset should - * always remain same. This is not a requirement. This we are doing - * to test aggregation without codec. - */ -#ifdef CONFIG_SND_SOC_MXFPGA - block_offset = 1; -#else - block_offset += - (sdw_mstr_bs_rt->stream_params. - bps - * - sdw_mstr_bs_rt->stream_params. - channel_count); -#endif } -#endif - } + /* Get no. of channels running on curr. port */ + ch_mask = port_rt->channel_mask; + no_of_channels = (((ch_mask >> 3) & 1) + + ((ch_mask >> 2) & 1) + + ((ch_mask >> 1) & 1) + + (ch_mask & 1)); - /* - * TBD: Hardcding block control group as true, - * to be changed later - */ - t_params->blockgroupcontrol_valid = true; - t_params->blockgroupcontrol = 0x0; /* Hardcoding to 0 */ + port_block_offset += + sdw_mstr_rt->stream_params.bps * + no_of_channels; + } + + /* Compute block offset */ + block_offset += sdw_mstr_rt->stream_params.bps * + sdw_mstr_rt->stream_params.channel_count; /* - * Since one port per bus runtime, - * breaking port_list loop - * TBD: to be extended for multiple port support + * Re-assign port_block_offset for next stream + * under same group */ - break; + port_block_offset = block_offset; } - /* - * Run loop for slave_rt_list for given master_list - * to compute block and sub block offset for slave - */ + /* Compute hstop for next group */ + hstop = hstop - element->hwidth; + } + + /* Compute transport params for slave */ + + /* Run loop for master runtime streams running on master */ + list_for_each_entry(sdw_mstr_rt, + &sdw_mstr->mstr_rt_list, mstr_node) { + + /* Get block offset from master runtime */ + port_block_offset = sdw_mstr_rt->block_offset; + + /* Run loop for slave per master runtime */ list_for_each_entry(slv_rt, - &sdw_mstr_bs_rt->slv_rt_list, slave_node) { + &sdw_mstr_rt->slv_rt_list, slave_node) { if (slv_rt->slave == NULL) break; @@ -1348,139 +1228,72 @@ int sdw_compute_blk_subblk_offset(struct sdw_bus *sdw_mstr_bs) if (slv_rt->rt_state == SDW_STATE_UNPREPARE_RT) continue; + /* Run loop for each port of slave */ list_for_each_entry(port_slv_rt, &slv_rt->port_rt_list, port_node) { t_slv_params = &port_slv_rt->transport_params; + t_slv_params->num = port_slv_rt->port_num; - /* - * TBD: Needs to be verifid for - * multiple combination - * 1. 1 master port, 1 slave rt, - * 1 port per slave rt --> - * In this case, use block_offset same as - * master for 1 slave rt - * 2. 1 master port, 2 slave rt, - * 1 port per slave rt --> - * In this case, use block_offset same as - * master for 1st slave rt and compute for 2nd. - */ - - /* - * Current assumption is one port per rt, - * hence no multiple port combination. - * TBD: block offset to be computed for - * more than 1 slave_rt list. - */ - t_slv_params->offset1 = block_offset; - t_slv_params->offset2 = block_offset >> 8; - + /* Assign transport parameters */ + t_slv_params->hstart = sdw_mstr_rt->hstart; + t_slv_params->hstop = sdw_mstr_rt->hstop; + t_slv_params->offset1 = port_block_offset; + t_slv_params->offset2 = port_block_offset >> 8; - /* - * TBD: Hardcding block control group as true, - * to be changed later - */ + /* Only BlockPerPort supported */ t_slv_params->blockgroupcontrol_valid = true; - /* Hardcoding to 0 */ t_slv_params->blockgroupcontrol = 0x0; - /* Since one port per slave runtime, - * breaking port_list loop - * TBD:to be extended for multiple port support - */ - break; + t_slv_params->lanecontrol = 0x0; + + /* Get no. of channels running on curr. port */ + ch_mask = port_slv_rt->channel_mask; + no_of_channels = (((ch_mask >> 3) & 1) + + ((ch_mask >> 2) & 1) + + ((ch_mask >> 1) & 1) + + (ch_mask & 1)); + + /* Increment block offset for next port/slave */ + port_block_offset += slv_rt->stream_params.bps * + no_of_channels; } } } - return 0; -} - - -/* - * sdw_configure_frmshp_bnkswtch - returns Success - * -EINVAL - In case of error. - * - * - * This function broadcast frameshape on framectrl - * register and performs bank switch. - */ -int sdw_configure_frmshp_bnkswtch(struct sdw_bus *mstr_bs, int col, int row) -{ - struct sdw_msg wr_msg; - int ret = 0; - int banktouse, numcol, numrow; - u8 wbuf[1] = {0}; - - numcol = sdw_get_col_to_num(col); - numrow = sdw_get_row_to_num(row); - - wbuf[0] = numcol | (numrow << 3); - /* Get current bank in use from bus structure*/ - banktouse = mstr_bs->active_bank; - banktouse = !banktouse; - - if (banktouse) { - wr_msg.addr = (SDW_SCP_FRAMECTRL + SDW_BANK1_REGISTER_OFFSET) + - (SDW_NUM_DATA_PORT_REGISTERS * 0); /* Data port 0 */ - } else { - - wr_msg.addr = SDW_SCP_FRAMECTRL + - (SDW_NUM_DATA_PORT_REGISTERS * 0); /* Data port 0 */ - } - - wr_msg.ssp_tag = 0x1; - wr_msg.flag = SDW_MSG_FLAG_WRITE; - wr_msg.len = 1; - wr_msg.slave_addr = 0xF; /* Broadcast address*/ - wr_msg.buf = wbuf; - wr_msg.addr_page1 = 0x0; - wr_msg.addr_page2 = 0x0; - - - ret = sdw_slave_transfer(mstr_bs->mstr, &wr_msg, 1); - if (ret != 1) { - ret = -EINVAL; - dev_err(&mstr_bs->mstr->dev, "Register transfer failed\n"); - goto out; - } - - msleep(100); /* TBD: Remove this */ - - /* - * TBD: check whether we need to poll on - * mcp active bank bit to switch bank - */ - mstr_bs->active_bank = banktouse; - -out: + kfree(temp); - return ret; + return 0; } /* - * sdw_configure_frmshp_bnkswtch - returns Success + * sdw_cfg_frmshp_bnkswtch - returns Success * -EINVAL - In case of error. + * -ENOMEM - In case of memory alloc failure. + * -EAGAIN - In case of activity ongoing. * * * This function broadcast frameshape on framectrl * register and performs bank switch. */ -int sdw_configure_frmshp_bnkswtch_mm(struct sdw_bus *mstr_bs, int col, int row) +int sdw_cfg_frmshp_bnkswtch(struct sdw_bus *mstr_bs, bool is_wait) { + struct sdw_msg *wr_msg; int ret = 0; int banktouse, numcol, numrow; u8 *wbuf; - struct sdw_msg *wr_msg; wr_msg = kzalloc(sizeof(struct sdw_msg), GFP_KERNEL); - mstr_bs->async_data.msg = wr_msg; if (!wr_msg) return -ENOMEM; + + mstr_bs->async_data.msg = wr_msg; + wbuf = kzalloc(sizeof(*wbuf), GFP_KERNEL); - if (!wbuf) - return -ENOMEM; - numcol = sdw_get_col_to_num(col); - numrow = sdw_get_row_to_num(row); + if (!wbuf) + return -ENOMEM; + + numcol = sdw_get_col_to_num(mstr_bs->col); + numrow = sdw_get_row_to_num(mstr_bs->row); wbuf[0] = numcol | (numrow << 3); /* Get current bank in use from bus structure*/ @@ -1504,23 +1317,34 @@ int sdw_configure_frmshp_bnkswtch_mm(struct sdw_bus *mstr_bs, int col, int row) wr_msg->addr_page1 = 0x0; wr_msg->addr_page2 = 0x0; - if (in_atomic() || irqs_disabled()) { - ret = sdw_trylock_mstr(mstr_bs->mstr); - if (!ret) { - /* SDW activity is ongoing. */ - ret = -EAGAIN; + if (is_wait) { + + if (in_atomic() || irqs_disabled()) { + ret = sdw_trylock_mstr(mstr_bs->mstr); + if (!ret) { + /* SDW activity is ongoing. */ + ret = -EAGAIN; + goto out; + } + } else + sdw_lock_mstr(mstr_bs->mstr); + + ret = sdw_slave_transfer_async(mstr_bs->mstr, wr_msg, + 1, &mstr_bs->async_data); + if (ret != 1) { + ret = -EINVAL; + dev_err(&mstr_bs->mstr->dev, "Register transfer failed\n"); goto out; } + } else { - sdw_lock_mstr(mstr_bs->mstr); - } + ret = sdw_slave_transfer(mstr_bs->mstr, wr_msg, 1); + if (ret != 1) { + ret = -EINVAL; + dev_err(&mstr_bs->mstr->dev, "Register transfer failed\n"); + goto out; + } - ret = sdw_slave_transfer_async(mstr_bs->mstr, wr_msg, - 1, &mstr_bs->async_data); - if (ret != 1) { - ret = -EINVAL; - dev_err(&mstr_bs->mstr->dev, "Register transfer failed\n"); - goto out; } msleep(100); /* TBD: Remove this */ @@ -1531,12 +1355,25 @@ int sdw_configure_frmshp_bnkswtch_mm(struct sdw_bus *mstr_bs, int col, int row) */ mstr_bs->active_bank = banktouse; + if (!is_wait) { + kfree(mstr_bs->async_data.msg->buf); + kfree(mstr_bs->async_data.msg); + } + + out: return ret; } -int sdw_configure_frmshp_bnkswtch_mm_wait(struct sdw_bus *mstr_bs) +/* + * sdw_cfg_frmshp_bnkswtch_wait - returns Success + * -ETIMEDOUT - In case of timeout + * + * This function waits on completion of + * bank switch. + */ +int sdw_cfg_frmshp_bnkswtch_wait(struct sdw_bus *mstr_bs) { unsigned long time_left; struct sdw_master *mstr = mstr_bs->mstr; @@ -1556,7 +1393,7 @@ int sdw_configure_frmshp_bnkswtch_mm_wait(struct sdw_bus *mstr_bs) } /* - * sdw_cfg_bs_params - returns Success + * sdw_config_bs_prms - returns Success * -EINVAL - In case of error. * * @@ -1566,61 +1403,31 @@ int sdw_configure_frmshp_bnkswtch_mm_wait(struct sdw_bus *mstr_bs) * from sdw_bus_calc_bw & sdw_bus_calc_bw_dis API. * */ -int sdw_cfg_bs_params(struct sdw_bus *sdw_mstr_bs, - struct sdw_mstr_runtime *sdw_mstr_bs_rt, - bool is_strm_cpy) +int sdw_config_bs_prms(struct sdw_bus *sdw_mstr_bs, bool state_check) { struct port_chn_en_state chn_en; struct sdw_master *sdw_mstr = sdw_mstr_bs->mstr; + struct sdw_mstr_runtime *sdw_mstr_bs_rt = NULL; struct sdw_mstr_driver *ops; int banktouse, ret = 0; list_for_each_entry(sdw_mstr_bs_rt, - &sdw_mstr->mstr_rt_list, mstr_node) { + &sdw_mstr->mstr_rt_list, mstr_node) { if (sdw_mstr_bs_rt->mstr == NULL) continue; - if (is_strm_cpy) { - /* - * Configure and enable all slave - * transport params first - */ - ret = sdw_cfg_mstr_slv(sdw_mstr_bs, - sdw_mstr_bs_rt, false); - if (ret < 0) { - /* TBD: Undo all the computation */ - dev_err(&sdw_mstr_bs->mstr->dev, - "slave config params failed\n"); - return ret; - } - - /* Configure and enable all master params */ - ret = sdw_cfg_mstr_slv(sdw_mstr_bs, - sdw_mstr_bs_rt, true); - if (ret < 0) { - /* TBD: Undo all the computation */ - dev_err(&sdw_mstr_bs->mstr->dev, - "master config params failed\n"); - return ret; - } - - } else { - - /* - * 7.1 Copy all slave transport and port params - * to alternate bank - * 7.2 copy all master transport and port params - * to alternate bank - */ - ret = sdw_cpy_params_mstr_slv(sdw_mstr_bs, - sdw_mstr_bs_rt); - if (ret < 0) { - /* TBD: Undo all the computation */ - dev_err(&sdw_mstr_bs->mstr->dev, - "slave/master copy params failed\n"); - return ret; - } + /* + * Configure transport and port params + * for master and slave ports. + */ + ret = sdw_cfg_params_mstr_slv(sdw_mstr_bs, + sdw_mstr_bs_rt, state_check); + if (ret < 0) { + /* TBD: Undo all the computation */ + dev_err(&sdw_mstr_bs->mstr->dev, + "slave/master config params failed\n"); + return ret; } /* Get master driver ops */ @@ -1631,14 +1438,14 @@ int sdw_cfg_bs_params(struct sdw_bus *sdw_mstr_bs, banktouse = !banktouse; /* - * TBD: Currently harcoded SSP interval to 50, + * TBD: Currently harcoded SSP interval, * computed value to be taken from system_interval in * bus data structure. * Add error check. */ if (ops->mstr_ops->set_ssp_interval) ops->mstr_ops->set_ssp_interval(sdw_mstr_bs->mstr, - 50, banktouse); + SDW_DEFAULT_SSP, banktouse); /* * Configure Clock @@ -1646,7 +1453,7 @@ int sdw_cfg_bs_params(struct sdw_bus *sdw_mstr_bs, */ if (ops->mstr_ops->set_clock_freq) ops->mstr_ops->set_clock_freq(sdw_mstr_bs->mstr, - sdw_mstr_bs->clk_freq, banktouse); + sdw_mstr_bs->clk_div, banktouse); /* Enable channel on alternate bank for running streams */ chn_en.is_activate = true; @@ -1676,10 +1483,10 @@ int sdw_cfg_bs_params(struct sdw_bus *sdw_mstr_bs, * bank is enabled. * */ -int sdw_dis_chan(struct sdw_bus *sdw_mstr_bs, - struct sdw_mstr_runtime *sdw_mstr_bs_rt) +int sdw_dis_chan(struct sdw_bus *sdw_mstr_bs) { struct sdw_master *sdw_mstr = sdw_mstr_bs->mstr; + struct sdw_mstr_runtime *sdw_mstr_bs_rt = NULL; struct port_chn_en_state chn_en; int ret = 0; @@ -1762,9 +1569,6 @@ int sdw_cfg_slv_prep_unprep(struct sdw_bus *mstr_bs, wr_msg.addr_page1 = 0x0; wr_msg.addr_page2 = 0x0; -#ifdef CONFIG_SND_SOC_MXFPGA - sdw_slv_dpn_cap->prepare_ch = 0; -#endif if (prep) { /* PREPARE */ /* @@ -1998,15 +1802,8 @@ int sdw_prep_unprep_mstr_slv(struct sdw_bus *sdw_mstr_bs, slv_rt_strm, port_slv_strm, is_prep); if (ret < 0) return ret; + } - /* Since one port per slave runtime, - * breaking port_list loop - * TBD: to be extended for multiple port support - */ - break; - } - - break; } list_for_each_entry(mstr_rt_strm, @@ -2022,12 +1819,6 @@ int sdw_prep_unprep_mstr_slv(struct sdw_bus *sdw_mstr_bs, mstr_rt_strm, port_mstr_strm, is_prep); if (ret < 0) return ret; - - /* Since one port per master runtime, - * breaking port_list loop - * TBD: to be extended for multiple port support - */ - break; } } @@ -2050,852 +1841,1257 @@ struct sdw_bus *master_to_bus(struct sdw_master *mstr) return NULL; } -/** - * sdw_bus_calc_bw - returns Success +/* + * sdw_chk_strm_prms - returns Success * -EINVAL - In case of error. * * - * This function is called from sdw_prepare_and_enable - * whenever new stream is processed. The function based - * on the stream associated with controller calculates - * required bandwidth, clock, frameshape, computes - * all transport params for a given port, enable channel - * & perform bankswitch. + * This function performs all the required + * check such as isynchronous mode support, + * stream rates etc. This API is called + * from sdw_bus_calc_bw API. + * */ -int sdw_bus_calc_bw(struct sdw_stream_tag *stream_tag, bool enable) +int sdw_chk_strm_prms(struct sdw_master_capabilities *sdw_mstr_cap, + struct sdw_stream_params *mstr_params, + struct sdw_stream_params *stream_params) +{ + /* Asynchronous mode not supported, return Error */ + if (((sdw_mstr_cap->base_clk_freq * 2) % mstr_params->rate) != 0) + return -EINVAL; + + /* Check for sampling frequency */ + if (stream_params->rate != mstr_params->rate) + return -EINVAL; + + return 0; +} + +/* + * sdw_compute_bs_prms - returns Success + * -EINVAL - In case of error. + * + * + * This function performs master/slave transport + * params computation. This API is called + * from sdw_bus_calc_bw & sdw_bus_calc_bw_dis API. + * + */ +int sdw_compute_bs_prms(struct sdw_bus *sdw_mstr_bs, + struct sdw_mstr_runtime *sdw_mstr_rt) { - struct sdw_runtime *sdw_rt = stream_tag->sdw_rt; - struct sdw_stream_params *stream_params = &sdw_rt->stream_params; - struct sdw_mstr_runtime *sdw_mstr_rt = NULL, *sdw_mstr_bs_rt = NULL; - struct sdw_mstr_runtime *mstr_rt_act = NULL, *last_rt = NULL; - struct sdw_bus *sdw_mstr_bs = NULL, *mstr_bs_act = NULL; - struct sdw_master *sdw_mstr = NULL; struct sdw_master_capabilities *sdw_mstr_cap = NULL; - struct sdw_stream_params *mstr_params; - int stream_frame_size; - int frame_interval = 0, sel_row = 0, sel_col = 0; - int ret = 0; - bool last_node = false; - struct sdw_master_port_ops *ops; + struct sdw_master *sdw_mstr = sdw_mstr_bs->mstr; + int ret = 0, frame_interval = 0; + + sdw_mstr_cap = &sdw_mstr->mstr_capabilities; - /* TBD: Add PCM/PDM flag in sdw_config_stream */ + ret = sdw_get_clock_frmshp(sdw_mstr_bs, &frame_interval, + sdw_mstr_rt); + if (ret < 0) { + /* TBD: Undo all the computation */ + dev_err(&sdw_mstr->dev, "clock/frameshape config failed\n"); + return ret; + } /* - * TBD: check for mstr_rt is in configured state or not - * If yes, then configure masters as well - * If no, then do not configure/enable master related parameters + * TBD: find right place to run sorting on + * master rt_list. Below sorting is done based on + * bps from low to high, that means PDM streams + * will be placed before PCM. */ - /* BW calulation for active master controller for given stream tag */ - list_for_each_entry(sdw_mstr_rt, &sdw_rt->mstr_rt_list, - mstr_sdw_node) { - - if (sdw_mstr_rt->mstr == NULL) - break; - last_rt = list_last_entry(&sdw_rt->mstr_rt_list, - struct sdw_mstr_runtime, mstr_sdw_node); - if (sdw_mstr_rt == last_rt) - last_node = true; - else - last_node = false; + /* + * TBD Should we also perform sorting based on rate + * for PCM stream check. if yes then how?? + * creating two different list. + */ - /* Get bus structure for master */ - sdw_mstr_bs = master_to_bus(sdw_mstr_rt->mstr); - sdw_mstr = sdw_mstr_bs->mstr; + /* Compute system interval */ + ret = sdw_compute_sys_interval(sdw_mstr_bs, sdw_mstr_cap, + frame_interval); + if (ret < 0) { + /* TBD: Undo all the computation */ + dev_err(&sdw_mstr->dev, "compute system interval failed\n"); + return ret; + } - /* - * All data structures required available, - * lets calculate BW for master controller - */ + /* Compute hstart/hstop */ + ret = sdw_compute_hstart_hstop(sdw_mstr_bs); + if (ret < 0) { + /* TBD: Undo all the computation */ + dev_err(&sdw_mstr->dev, "compute hstart/hstop failed\n"); + return ret; + } - /* Check for isochronous mode plus other checks if required */ - sdw_mstr_cap = &sdw_mstr_bs->mstr->mstr_capabilities; - mstr_params = &sdw_mstr_rt->stream_params; + return 0; +} - if ((sdw_rt->stream_state != SDW_STATE_CONFIG_STREAM) && - (sdw_rt->stream_state != SDW_STATE_UNPREPARE_STREAM)) - goto enable_stream; +/* + * sdw_bs_pre_bnkswtch_post - returns Success + * -EINVAL or ret value - In case of error. + * + * This API performs on of the following operation + * based on bs_state value: + * pre-activate port + * bank switch operation + * post-activate port + * bankswitch wait operation + * disable channel operation + */ +int sdw_bs_pre_bnkswtch_post(struct sdw_runtime *sdw_rt, int bs_state) +{ + struct sdw_mstr_runtime *mstr_rt_act = NULL; + struct sdw_bus *mstr_bs_act = NULL; + struct sdw_master_port_ops *ops; + int ret = 0; - /* we do not support asynchronous mode Return Error */ - if ((sdw_mstr_cap->base_clk_freq % mstr_params->rate) != 0) { - /* TBD: Undo all the computation */ - dev_err(&sdw_mstr->dev, "Async mode not supported\n"); - return -EINVAL; - } + list_for_each_entry(mstr_rt_act, &sdw_rt->mstr_rt_list, + mstr_sdw_node) { - /* Check for sampling frequency */ - if (stream_params->rate != mstr_params->rate) { - /* TBD: Undo all the computation */ - dev_err(&sdw_mstr->dev, "Sample frequency mismatch\n"); + if (mstr_rt_act->mstr == NULL) + break; + + /* Get bus structure for master */ + mstr_bs_act = master_to_bus(mstr_rt_act->mstr); + if (!mstr_bs_act) return -EINVAL; - } + + ops = mstr_bs_act->mstr->driver->mstr_port_ops; /* - * Calculate stream bandwidth, frame size and - * total BW required for master controller + * Note that current all the operations + * of pre->bankswitch->post->wait->disable + * are performed sequentially.The switch case + * is kept in order for code to scale where + * pre->bankswitch->post->wait->disable are + * not sequential and called from different + * instances. */ - sdw_mstr_rt->stream_bw = mstr_params->rate * - mstr_params->channel_count * mstr_params->bps; - stream_frame_size = mstr_params->channel_count * - mstr_params->bps; + switch (bs_state) { - sdw_mstr_bs->bandwidth += sdw_mstr_rt->stream_bw; - - ret = sdw_get_clock_frmshp(sdw_mstr_bs, - &frame_interval, &sel_col, &sel_row); - if (ret < 0) { - /* TBD: Undo all the computation */ - dev_err(&sdw_mstr->dev, "clock/frameshape config failed\n"); - return ret; + case SDW_UPDATE_BS_PRE: + /* Pre activate ports */ + if (ops->dpn_port_activate_ch_pre) { + ret = ops->dpn_port_activate_ch_pre + (mstr_bs_act->mstr, NULL, 0); + if (ret < 0) + return ret; + } + break; + case SDW_UPDATE_BS_BNKSWTCH: + /* Configure Frame Shape/Switch Bank */ + ret = sdw_cfg_frmshp_bnkswtch(mstr_bs_act, true); + if (ret < 0) + return ret; + break; + case SDW_UPDATE_BS_POST: + /* Post activate ports */ + if (ops->dpn_port_activate_ch_post) { + ret = ops->dpn_port_activate_ch_post + (mstr_bs_act->mstr, NULL, 0); + if (ret < 0) + return ret; + } + break; + case SDW_UPDATE_BS_BNKSWTCH_WAIT: + /* Post Bankswitch wait operation */ + ret = sdw_cfg_frmshp_bnkswtch_wait(mstr_bs_act); + if (ret < 0) + return ret; + break; + case SDW_UPDATE_BS_DIS_CHN: + /* Disable channel on previous bank */ + ret = sdw_dis_chan(mstr_bs_act); + if (ret < 0) + return ret; + break; + default: + return -EINVAL; + break; } + } - /* - * TBD: find right place to run sorting on - * master rt_list. Below sorting is done based on - * bps from low to high, that means PDM streams - * will be placed before PCM. - */ + return ret; - /* - * TBD Should we also perform sorting based on rate - * for PCM stream check. if yes then how?? - * creating two different list. - */ +} + +/* + * sdw_update_bs_prms - returns Success + * -EINVAL - In case of error. + * + * Once all the parameters are configured + * for ports, this function performs bankswitch + * where all the new configured parameters + * gets in effect. This function is called + * from sdw_bus_calc_bw & sdw_bus_calc_bw_dis API. + * This function also disables all the channels + * enabled on previous bank after bankswitch. + */ +int sdw_update_bs_prms(struct sdw_bus *sdw_mstr_bs, + struct sdw_runtime *sdw_rt, + int last_node) +{ + + struct sdw_master *sdw_mstr = sdw_mstr_bs->mstr; + int ret = 0; + + /* + * Optimization scope. + * Check whether we can assign function pointer + * link sync value is 1, and call that function + * if its not NULL. + */ + if ((last_node) && (sdw_mstr->link_sync_mask)) { - /* Compute system interval */ - ret = sdw_compute_sys_interval(sdw_mstr_bs, sdw_mstr_cap, - frame_interval); + /* Perform pre-activate ports */ + ret = sdw_bs_pre_bnkswtch_post(sdw_rt, SDW_UPDATE_BS_PRE); if (ret < 0) { - /* TBD: Undo all the computation */ - dev_err(&sdw_mstr->dev, "compute system interval failed\n"); + dev_err(&sdw_mstr->dev, "Pre-activate port failed\n"); return ret; } - /* Compute hstart/hstop */ - ret = sdw_compute_hstart_hstop(sdw_mstr_bs, sel_col); + /* Perform bankswitch operation*/ + ret = sdw_bs_pre_bnkswtch_post(sdw_rt, SDW_UPDATE_BS_BNKSWTCH); if (ret < 0) { - /* TBD: Undo all the computation */ - dev_err(&sdw_mstr->dev, "compute hstart/hstop failed\n"); + dev_err(&sdw_mstr->dev, "Bank Switch operation failed\n"); return ret; } - /* Compute block offset */ - ret = sdw_compute_blk_subblk_offset(sdw_mstr_bs); + /* Perform post-activate ports */ + ret = sdw_bs_pre_bnkswtch_post(sdw_rt, SDW_UPDATE_BS_POST); if (ret < 0) { - /* TBD: Undo all the computation */ - dev_err(&sdw_mstr->dev, "compute block offset failed\n"); + dev_err(&sdw_mstr->dev, "Pre-activate port failed\n"); return ret; } - /* Change Stream State */ - if (last_node) - sdw_rt->stream_state = SDW_STATE_COMPUTE_STREAM; - - /* Configure bus parameters */ - ret = sdw_cfg_bs_params(sdw_mstr_bs, sdw_mstr_bs_rt, true); + /* Perform bankswitch post wait opearation */ + ret = sdw_bs_pre_bnkswtch_post(sdw_rt, + SDW_UPDATE_BS_BNKSWTCH_WAIT); if (ret < 0) { - /* TBD: Undo all the computation */ - dev_err(&sdw_mstr->dev, "xport param config failed\n"); + dev_err(&sdw_mstr->dev, "BnkSwtch wait op failed\n"); return ret; } - sel_col = sdw_mstr_bs->col; - sel_row = sdw_mstr_bs->row; - - if ((last_node) && (sdw_mstr->link_sync_mask)) { - - list_for_each_entry(mstr_rt_act, &sdw_rt->mstr_rt_list, - mstr_sdw_node) { - - if (mstr_rt_act->mstr == NULL) - break; - - /* Get bus structure for master */ - mstr_bs_act = master_to_bus(mstr_rt_act->mstr); - ops = mstr_bs_act->mstr->driver->mstr_port_ops; - - /* Run for all mstr_list and - * pre_activate ports - */ - if (ops->dpn_port_activate_ch_pre) { - ret = ops->dpn_port_activate_ch_pre - (mstr_bs_act->mstr, NULL, 0); - if (ret < 0) - return ret; - } - } - - list_for_each_entry(mstr_rt_act, &sdw_rt->mstr_rt_list, - mstr_sdw_node) { - if (mstr_rt_act->mstr == NULL) - break; - - /* Get bus structure for master */ - mstr_bs_act = master_to_bus(mstr_rt_act->mstr); - - /* Configure Frame Shape/Switch Bank */ - ret = sdw_configure_frmshp_bnkswtch_mm( - mstr_bs_act, sel_col, sel_row); - if (ret < 0) { - /* TBD: Undo all the computation */ - dev_err(&sdw_mstr->dev, "bank switch failed\n"); - return ret; - } - } - - list_for_each_entry(mstr_rt_act, &sdw_rt->mstr_rt_list, - mstr_sdw_node) { - - if (mstr_rt_act->mstr == NULL) - break; - - /* Get bus structure for master */ - mstr_bs_act = master_to_bus(mstr_rt_act->mstr); - - ops = mstr_bs_act->mstr->driver->mstr_port_ops; - - /* Run for all mstr_list and - * post_activate ports - */ - if (ops->dpn_port_activate_ch_post) { - ret = ops->dpn_port_activate_ch_post - (mstr_bs_act->mstr, NULL, 0); - if (ret < 0) - return ret; - } - } - - list_for_each_entry(mstr_rt_act, - &sdw_rt->mstr_rt_list, mstr_sdw_node) { - - if (mstr_rt_act->mstr == NULL) - break; - - mstr_bs_act = master_to_bus( - mstr_rt_act->mstr); - ret = sdw_configure_frmshp_bnkswtch_mm_wait( - mstr_bs_act); - } - - list_for_each_entry(mstr_rt_act, &sdw_rt->mstr_rt_list, - mstr_sdw_node) { - - if (mstr_rt_act->mstr == NULL) - break; - - /* Get bus structure for master */ - mstr_bs_act = master_to_bus(mstr_rt_act->mstr); - - /* Disable all channels - * enabled on previous bank - */ - ret = sdw_dis_chan(mstr_bs_act, sdw_mstr_bs_rt); - if (ret < 0) { - /* TBD: Undo all the computation */ - dev_err(&sdw_mstr->dev, "Channel disabled faile\n"); - return ret; - } - } - } - if (!sdw_mstr->link_sync_mask) { - - /* Configure Frame Shape/Switch Bank */ - ret = sdw_configure_frmshp_bnkswtch(sdw_mstr_bs, - sel_col, sel_row); - if (ret < 0) { - /* TBD: Undo all the computation */ - dev_err(&sdw_mstr->dev, "bank switch failed\n"); - return ret; - } - /* Disable all channels enabled on previous bank */ - ret = sdw_dis_chan(sdw_mstr_bs, sdw_mstr_bs_rt); - if (ret < 0) { - /* TBD: Undo all the computation */ - dev_err(&sdw_mstr->dev, "Channel disabled failed\n"); - return ret; - } - } - /* Prepare new port for master and slave */ - ret = sdw_prep_unprep_mstr_slv(sdw_mstr_bs, sdw_rt, true); + /* Disable channels on previous bank */ + ret = sdw_bs_pre_bnkswtch_post(sdw_rt, SDW_UPDATE_BS_DIS_CHN); if (ret < 0) { - /* TBD: Undo all the computation */ - dev_err(&sdw_mstr->dev, "Channel prepare failed\n"); + dev_err(&sdw_mstr->dev, "Channel disabled failed\n"); return ret; } - /* change stream state to prepare */ - if (last_node) - sdw_rt->stream_state = SDW_STATE_PREPARE_STREAM; } -enable_stream: - list_for_each_entry(sdw_mstr_rt, &sdw_rt->mstr_rt_list, mstr_sdw_node) { - - - if (sdw_mstr_rt->mstr == NULL) - break; - last_rt = list_last_entry(&sdw_rt->mstr_rt_list, - struct sdw_mstr_runtime, mstr_sdw_node); - if (sdw_mstr_rt == last_rt) - last_node = true; - else - last_node = false; - - /* Get bus structure for master */ - sdw_mstr_bs = master_to_bus(sdw_mstr_rt->mstr); - sdw_mstr = sdw_mstr_bs->mstr; - - sdw_mstr_cap = &sdw_mstr_bs->mstr->mstr_capabilities; - mstr_params = &sdw_mstr_rt->stream_params; - if ((!enable) || - (sdw_rt->stream_state != SDW_STATE_PREPARE_STREAM)) - return 0; + if (!sdw_mstr->link_sync_mask) { - ret = sdw_cfg_bs_params(sdw_mstr_bs, sdw_mstr_bs_rt, false); + /* Configure Frame Shape/Switch Bank */ + ret = sdw_cfg_frmshp_bnkswtch(sdw_mstr_bs, false); if (ret < 0) { /* TBD: Undo all the computation */ - dev_err(&sdw_mstr->dev, "xport params config failed\n"); + dev_err(&sdw_mstr->dev, "bank switch failed\n"); return ret; } - /* Enable new port for master and slave */ - ret = sdw_en_dis_mstr_slv(sdw_mstr_bs, sdw_rt, true); + /* Disable all channels enabled on previous bank */ + ret = sdw_dis_chan(sdw_mstr_bs); if (ret < 0) { /* TBD: Undo all the computation */ - dev_err(&sdw_mstr->dev, "Channel enable failed\n"); + dev_err(&sdw_mstr->dev, "Channel disabled failed\n"); return ret; } + } - /* change stream state to enable */ - if (last_node) - sdw_rt->stream_state = SDW_STATE_ENABLE_STREAM; + return ret; +} - sel_col = sdw_mstr_bs->col; - sel_row = sdw_mstr_bs->row; +/** + * sdw_chk_last_node - returns True or false + * + * This function returns true in case of last node + * else returns false. + */ +bool sdw_chk_last_node(struct sdw_mstr_runtime *sdw_mstr_rt, + struct sdw_runtime *sdw_rt) +{ + struct sdw_mstr_runtime *last_rt = NULL; - if ((last_node) && (sdw_mstr->link_sync_mask)) { + last_rt = list_last_entry(&sdw_rt->mstr_rt_list, + struct sdw_mstr_runtime, mstr_sdw_node); + if (sdw_mstr_rt == last_rt) + return true; + else + return false; +} - list_for_each_entry(mstr_rt_act, &sdw_rt->mstr_rt_list, - mstr_sdw_node) { +/** + * sdw_unprepare_op - returns Success + * -EINVAL - In case of error. + * + * This function perform all operations required + * to unprepare ports and does recomputation of + * bus parameters. + */ +int sdw_unprepare_op(struct sdw_bus *sdw_mstr_bs, + struct sdw_mstr_runtime *sdw_mstr_rt, + struct sdw_runtime *sdw_rt) +{ + struct sdw_master *sdw_mstr = sdw_mstr_bs->mstr; + struct sdw_stream_params *mstr_params; + bool last_node = false; + int ret = 0; - if (mstr_rt_act->mstr == NULL) - break; + last_node = sdw_chk_last_node(sdw_mstr_rt, sdw_rt); + mstr_params = &sdw_mstr_rt->stream_params; - /* Get bus structure for master */ - mstr_bs_act = master_to_bus(mstr_rt_act->mstr); + /* 1. Un-prepare master and slave port */ + ret = sdw_prep_unprep_mstr_slv(sdw_mstr_bs, + sdw_rt, false); + if (ret < 0) { + /* TBD: Undo all the computation */ + dev_err(&sdw_mstr->dev, "Ch unprep failed\n"); + return ret; + } - ops = mstr_bs_act->mstr->driver->mstr_port_ops; + /* change stream state to unprepare */ + if (last_node) + sdw_rt->stream_state = + SDW_STATE_UNPREPARE_STREAM; - /* Run for all mstr_list and - * pre_activate ports - */ - if (ops->dpn_port_activate_ch_pre) { - ret = ops->dpn_port_activate_ch_pre - (mstr_bs_act->mstr, NULL, 0); - if (ret < 0) - return ret; - } - } + /* + * Calculate new bandwidth, frame size + * and total BW required for master controller + */ + sdw_mstr_rt->stream_bw = mstr_params->rate * + mstr_params->channel_count * mstr_params->bps; + sdw_mstr_bs->bandwidth -= sdw_mstr_rt->stream_bw; - list_for_each_entry(mstr_rt_act, - &sdw_rt->mstr_rt_list, mstr_sdw_node) { + /* Something went wrong in bandwidth calulation */ + if (sdw_mstr_bs->bandwidth < 0) { + dev_err(&sdw_mstr->dev, "BW calculation failed\n"); + return -EINVAL; + } + if (!sdw_mstr_bs->bandwidth) { + /* + * Last stream on master should + * return successfully + */ + sdw_mstr_bs->system_interval = 0; + sdw_mstr_bs->stream_interval = 0; + sdw_mstr_bs->frame_freq = 0; + sdw_mstr_bs->row = 0; + sdw_mstr_bs->col = 0; + return 0; + } - if (mstr_rt_act->mstr == NULL) - break; + /* Compute transport params */ + ret = sdw_compute_bs_prms(sdw_mstr_bs, sdw_mstr_rt); + if (ret < 0) { + /* TBD: Undo all the computation */ + dev_err(&sdw_mstr->dev, "Params computation failed\n"); + return -EINVAL; + } - /* Get bus structure for master */ - mstr_bs_act = master_to_bus(mstr_rt_act->mstr); + /* Configure bus params */ + ret = sdw_config_bs_prms(sdw_mstr_bs, true); + if (ret < 0) { + /* TBD: Undo all the computation */ + dev_err(&sdw_mstr->dev, "xport params config failed\n"); + return ret; + } - /* Configure Frame Shape/Switch Bank */ - ret = sdw_configure_frmshp_bnkswtch_mm( - mstr_bs_act, - sel_col, sel_row); - if (ret < 0) { - /* TBD: Undo all the computation */ - dev_err(&sdw_mstr->dev, "bank switch failed\n"); - return ret; - } - } + /* + * Perform SDW bus update + * For Aggregation flow: + * Pre-> Bankswitch -> Post -> Disable channel + * For normal flow: + * Bankswitch -> Disable channel + */ + ret = sdw_update_bs_prms(sdw_mstr_bs, sdw_rt, last_node); - list_for_each_entry(mstr_rt_act, - &sdw_rt->mstr_rt_list, mstr_sdw_node) { + return ret; +} - if (mstr_rt_act->mstr == NULL) - break; +/** + * sdw_disable_op - returns Success + * -EINVAL - In case of error. + * + * This function perform all operations required + * to disable ports. + */ +int sdw_disable_op(struct sdw_bus *sdw_mstr_bs, + struct sdw_mstr_runtime *sdw_mstr_rt, + struct sdw_runtime *sdw_rt) +{ - /* Get bus structure for master */ - mstr_bs_act = master_to_bus(mstr_rt_act->mstr); + struct sdw_master *sdw_mstr = sdw_mstr_bs->mstr; + struct sdw_master_capabilities *sdw_mstr_cap = NULL; + struct sdw_stream_params *mstr_params; + bool last_node = false; + int ret = 0; - ops = mstr_bs_act->mstr->driver->mstr_port_ops; - /* Run for all mstr_list and - * post_activate ports - */ - if (ops->dpn_port_activate_ch_post) { - ret = ops->dpn_port_activate_ch_post - (mstr_bs_act->mstr, NULL, 0); - if (ret < 0) - return ret; - } - } + last_node = sdw_chk_last_node(sdw_mstr_rt, sdw_rt); + sdw_mstr_cap = &sdw_mstr_bs->mstr->mstr_capabilities; + mstr_params = &sdw_mstr_rt->stream_params; - list_for_each_entry(mstr_rt_act, - &sdw_rt->mstr_rt_list, mstr_sdw_node) { + /* Lets do disabling of port for stream to be freed */ + ret = sdw_en_dis_mstr_slv(sdw_mstr_bs, sdw_rt, false); + if (ret < 0) { + /* TBD: Undo all the computation */ + dev_err(&sdw_mstr->dev, "Ch dis failed\n"); + return ret; + } - if (mstr_rt_act->mstr == NULL) - break; + /* Change stream state to disable */ + if (last_node) + sdw_rt->stream_state = SDW_STATE_DISABLE_STREAM; - /* Get bus structure for master */ - mstr_bs_act = master_to_bus(mstr_rt_act->mstr); - ret = sdw_configure_frmshp_bnkswtch_mm_wait( - mstr_bs_act); - } - list_for_each_entry(mstr_rt_act, - &sdw_rt->mstr_rt_list, mstr_sdw_node) { + ret = sdw_config_bs_prms(sdw_mstr_bs, false); + if (ret < 0) { + /* TBD: Undo all the computation */ + dev_err(&sdw_mstr->dev, "xport params config failed\n"); + return ret; + } - if (mstr_rt_act->mstr == NULL) - break; + /* + * Perform SDW bus update + * For Aggregation flow: + * Pre-> Bankswitch -> Post -> Disable channel + * For normal flow: + * Bankswitch -> Disable channel + */ + ret = sdw_update_bs_prms(sdw_mstr_bs, sdw_rt, last_node); - /* Get bus structure for master */ - mstr_bs_act = master_to_bus(mstr_rt_act->mstr); + return ret; +} - /* Disable all channels - * enabled on previous bank - */ - ret = sdw_dis_chan(mstr_bs_act, - sdw_mstr_bs_rt); - if (ret < 0) { - /* TBD: Undo all the computation */ - dev_err(&sdw_mstr->dev, - "Channel disabled faile\n"); - return ret; - } - } - } - if (!sdw_mstr->link_sync_mask) { - /* Configure Frame Shape/Switch Bank */ - ret = sdw_configure_frmshp_bnkswtch( - sdw_mstr_bs, - sel_col, sel_row); - if (ret < 0) { - /* TBD: Undo all the computation */ - dev_err(&sdw_mstr->dev, "bank switch failed\n"); - return ret; - } - /* Disable all channels enabled on previous bank */ - ret = sdw_dis_chan(sdw_mstr_bs, sdw_mstr_bs_rt); - if (ret < 0) { - /* TBD: Undo all the computation */ - dev_err(&sdw_mstr->dev, "Ch disabled failed\n"); - return ret; - } - } +/** + * sdw_enable_op - returns Success + * -EINVAL - In case of error. + * + * This function perform all operations required + * to enable ports. + */ +int sdw_enable_op(struct sdw_bus *sdw_mstr_bs, + struct sdw_mstr_runtime *sdw_mstr_rt, + struct sdw_runtime *sdw_rt) +{ + + struct sdw_master *sdw_mstr = sdw_mstr_bs->mstr; + bool last_node = false; + int ret = 0; + + last_node = sdw_chk_last_node(sdw_mstr_rt, sdw_rt); + + ret = sdw_config_bs_prms(sdw_mstr_bs, false); + if (ret < 0) { + /* TBD: Undo all the computation */ + dev_err(&sdw_mstr->dev, "xport params config failed\n"); + return ret; } - return 0; + /* Enable new port for master and slave */ + ret = sdw_en_dis_mstr_slv(sdw_mstr_bs, sdw_rt, true); + if (ret < 0) { + /* TBD: Undo all the computation */ + dev_err(&sdw_mstr->dev, "Channel enable failed\n"); + return ret; + } + + /* change stream state to enable */ + if (last_node) + sdw_rt->stream_state = SDW_STATE_ENABLE_STREAM; + /* + * Perform SDW bus update + * For Aggregation flow: + * Pre-> Bankswitch -> Post -> Disable channel + * For normal flow: + * Bankswitch -> Disable channel + */ + ret = sdw_update_bs_prms(sdw_mstr_bs, sdw_rt, last_node); + + return ret; } -EXPORT_SYMBOL_GPL(sdw_bus_calc_bw); /** - * sdw_bus_calc_bw_dis - returns Success + * sdw_prepare_op - returns Success * -EINVAL - In case of error. * - * - * This function is called from sdw_disable_and_unprepare - * whenever stream is ended. The function based disables/ - * unprepare port/channel of associated stream and computes - * required bandwidth, clock, frameshape, computes - * all transport params for a given port, enable channel - * & perform bankswitch for remaining streams on given - * controller. + * This function perform all operations required + * to prepare ports and does computation of + * bus parameters. */ -int sdw_bus_calc_bw_dis(struct sdw_stream_tag *stream_tag, bool unprepare) +int sdw_prepare_op(struct sdw_bus *sdw_mstr_bs, + struct sdw_mstr_runtime *sdw_mstr_rt, + struct sdw_runtime *sdw_rt) { - struct sdw_runtime *sdw_rt = stream_tag->sdw_rt; - struct sdw_mstr_runtime *sdw_mstr_rt = NULL, *sdw_mstr_bs_rt = NULL; - struct sdw_mstr_runtime *mstr_rt_act = NULL, *last_rt = NULL; - struct sdw_bus *sdw_mstr_bs = NULL, *mstr_bs_act = NULL; - struct sdw_master *sdw_mstr = NULL; + struct sdw_stream_params *stream_params = &sdw_rt->stream_params; + struct sdw_master *sdw_mstr = sdw_mstr_bs->mstr; struct sdw_master_capabilities *sdw_mstr_cap = NULL; struct sdw_stream_params *mstr_params; - int stream_frame_size; - int frame_interval = 0, sel_row = 0, sel_col = 0; - int ret = 0; + bool last_node = false; - struct sdw_master_port_ops *ops; + int ret = 0; - /* BW calulation for active master controller for given stream tag */ - list_for_each_entry(sdw_mstr_rt, - &sdw_rt->mstr_rt_list, mstr_sdw_node) { + last_node = sdw_chk_last_node(sdw_mstr_rt, sdw_rt); + sdw_mstr_cap = &sdw_mstr_bs->mstr->mstr_capabilities; + mstr_params = &sdw_mstr_rt->stream_params; + /* + * check all the stream parameters received + * Check for isochronous mode, sample rate etc + */ + ret = sdw_chk_strm_prms(sdw_mstr_cap, mstr_params, + stream_params); + if (ret < 0) { + dev_err(&sdw_mstr->dev, "Stream param check failed\n"); + return -EINVAL; + } + + /* + * Calculate stream bandwidth, frame size and + * total BW required for master controller + */ + sdw_mstr_rt->stream_bw = mstr_params->rate * + mstr_params->channel_count * mstr_params->bps; + sdw_mstr_bs->bandwidth += sdw_mstr_rt->stream_bw; + + /* Compute transport params */ + ret = sdw_compute_bs_prms(sdw_mstr_bs, sdw_mstr_rt); + if (ret < 0) { + /* TBD: Undo all the computation */ + dev_err(&sdw_mstr->dev, "Params computation failed\n"); + return -EINVAL; + } + + /* Configure bus parameters */ + ret = sdw_config_bs_prms(sdw_mstr_bs, true); + if (ret < 0) { + /* TBD: Undo all the computation */ + dev_err(&sdw_mstr->dev, "xport param config failed\n"); + return ret; + } + + /* + * Perform SDW bus update + * For Aggregation flow: + * Pre-> Bankswitch -> Post -> Disable channel + * For normal flow: + * Bankswitch -> Disable channel + */ + ret = sdw_update_bs_prms(sdw_mstr_bs, sdw_rt, last_node); + + /* Prepare new port for master and slave */ + ret = sdw_prep_unprep_mstr_slv(sdw_mstr_bs, sdw_rt, true); + if (ret < 0) { + /* TBD: Undo all the computation */ + dev_err(&sdw_mstr->dev, "Channel prepare failed\n"); + return ret; + } + + /* change stream state to prepare */ + if (last_node) + sdw_rt->stream_state = SDW_STATE_PREPARE_STREAM; + + + return ret; +} + +/** + * sdw_pre_en_dis_unprep_op - returns Success + * -EINVAL - In case of error. + * + * This function is called by sdw_bus_calc_bw + * and sdw_bus_calc_bw_dis to prepare, enable, + * unprepare and disable ports. Based on state + * value, individual APIs are called. + */ +int sdw_pre_en_dis_unprep_op(struct sdw_mstr_runtime *sdw_mstr_rt, + struct sdw_runtime *sdw_rt, int state) +{ + struct sdw_master *sdw_mstr = NULL; + struct sdw_bus *sdw_mstr_bs = NULL; + int ret = 0; + + /* Get bus structure for master */ + sdw_mstr_bs = master_to_bus(sdw_mstr_rt->mstr); + if (!sdw_mstr_bs) + return -EINVAL; + + sdw_mstr = sdw_mstr_bs->mstr; + + /* + * All data structures required available, + * lets calculate BW for master controller + */ + + switch (state) { + + case SDW_STATE_PREPARE_STREAM: /* Prepare */ + ret = sdw_prepare_op(sdw_mstr_bs, sdw_mstr_rt, sdw_rt); + break; + case SDW_STATE_ENABLE_STREAM: /* Enable */ + ret = sdw_enable_op(sdw_mstr_bs, sdw_mstr_rt, sdw_rt); + break; + case SDW_STATE_DISABLE_STREAM: /* Disable */ + ret = sdw_disable_op(sdw_mstr_bs, sdw_mstr_rt, sdw_rt); + break; + case SDW_STATE_UNPREPARE_STREAM: /* UnPrepare */ + ret = sdw_unprepare_op(sdw_mstr_bs, sdw_mstr_rt, sdw_rt); + break; + default: + ret = -EINVAL; + break; + + } + + return ret; +} + +/** + * sdw_bus_calc_bw - returns Success + * -EINVAL - In case of error. + * + * + * This function is called from sdw_prepare_and_enable + * whenever new stream is processed. The function based + * on the stream associated with controller calculates + * required bandwidth, clock, frameshape, computes + * all transport params for a given port, enable channel + * & perform bankswitch. + */ +int sdw_bus_calc_bw(struct sdw_stream_tag *stream_tag, bool enable) +{ + + struct sdw_runtime *sdw_rt = stream_tag->sdw_rt; + struct sdw_mstr_runtime *sdw_mstr_rt = NULL; + struct sdw_bus *sdw_mstr_bs = NULL; + struct sdw_master *sdw_mstr = NULL; + int ret = 0; + + + /* + * TBD: check for mstr_rt is in configured state or not + * If yes, then configure masters as well + * If no, then do not configure/enable master related parameters + */ + + /* BW calulation for active master controller for given stream tag */ + list_for_each_entry(sdw_mstr_rt, &sdw_rt->mstr_rt_list, + mstr_sdw_node) { if (sdw_mstr_rt->mstr == NULL) break; - last_rt = list_last_entry(&sdw_rt->mstr_rt_list, - struct sdw_mstr_runtime, mstr_sdw_node); - if (sdw_mstr_rt == last_rt) - last_node = true; - else - last_node = false; + if ((sdw_rt->stream_state != SDW_STATE_CONFIG_STREAM) && + (sdw_rt->stream_state != SDW_STATE_UNPREPARE_STREAM)) + goto enable_stream; /* Get bus structure for master */ sdw_mstr_bs = master_to_bus(sdw_mstr_rt->mstr); - sdw_mstr = sdw_mstr_bs->mstr; + if (!sdw_mstr_bs) + return -EINVAL; + sdw_mstr = sdw_mstr_bs->mstr; + ret = sdw_pre_en_dis_unprep_op(sdw_mstr_rt, sdw_rt, + SDW_STATE_PREPARE_STREAM); + if (ret < 0) { + dev_err(&sdw_mstr->dev, "Prepare Operation failed\n"); + return -EINVAL; + } + } - sdw_mstr_cap = &sdw_mstr_bs->mstr->mstr_capabilities; - mstr_params = &sdw_mstr_rt->stream_params; +enable_stream: - if (sdw_rt->stream_state != SDW_STATE_ENABLE_STREAM) - goto unprepare_stream; + list_for_each_entry(sdw_mstr_rt, &sdw_rt->mstr_rt_list, mstr_sdw_node) { - /* Lets do disabling of port for stream to be freed */ - list_for_each_entry(sdw_mstr_bs_rt, - &sdw_mstr->mstr_rt_list, mstr_node) { - if (sdw_mstr_bs_rt->mstr == NULL) - continue; + if (sdw_mstr_rt->mstr == NULL) + break; - /* - * Disable channel for slave and - * master on current bank - */ - ret = sdw_en_dis_mstr_slv(sdw_mstr_bs, sdw_rt, false); - if (ret < 0) { - /* TBD: Undo all the computation */ - dev_err(&sdw_mstr->dev, "Ch dis failed\n"); - return ret; - } + if ((!enable) || + (sdw_rt->stream_state != SDW_STATE_PREPARE_STREAM)) + return 0; + sdw_mstr_bs = master_to_bus(sdw_mstr_rt->mstr); + if (!sdw_mstr_bs) + return -EINVAL; - /* Change stream state to disable */ - if (last_node) - sdw_rt->stream_state = SDW_STATE_DISABLE_STREAM; - } + sdw_mstr = sdw_mstr_bs->mstr; - ret = sdw_cfg_bs_params(sdw_mstr_bs, sdw_mstr_bs_rt, false); + ret = sdw_pre_en_dis_unprep_op(sdw_mstr_rt, sdw_rt, + SDW_STATE_ENABLE_STREAM); if (ret < 0) { - /* TBD: Undo all the computation */ - dev_err(&sdw_mstr->dev, "xport params config failed\n"); - return ret; + dev_err(&sdw_mstr->dev, "Enable Operation failed\n"); + return -EINVAL; } + } - sel_col = sdw_mstr_bs->col; - sel_row = sdw_mstr_bs->row; - - if ((last_node) && (sdw_mstr->link_sync_mask)) { - - list_for_each_entry(mstr_rt_act, &sdw_rt->mstr_rt_list, - mstr_sdw_node) { - if (mstr_rt_act->mstr == NULL) - break; - /* Get bus structure for master */ - mstr_bs_act = master_to_bus(mstr_rt_act->mstr); - ops = mstr_bs_act->mstr->driver->mstr_port_ops; - /* Run for all mstr_list and - * pre_activate ports - */ - if (ops->dpn_port_activate_ch_pre) { - ret = ops->dpn_port_activate_ch_pre - (mstr_bs_act->mstr, NULL, 0); - if (ret < 0) - return ret; - } - } - list_for_each_entry(mstr_rt_act, - &sdw_rt->mstr_rt_list, mstr_sdw_node) { - if (mstr_rt_act->mstr == NULL) - break; + return 0; +} +EXPORT_SYMBOL_GPL(sdw_bus_calc_bw); - /* Get bus structure for master */ - mstr_bs_act = master_to_bus(mstr_rt_act->mstr); - /* Configure Frame Shape/Switch Bank */ - ret = sdw_configure_frmshp_bnkswtch_mm( - mstr_bs_act, - sel_col, sel_row); - if (ret < 0) { - /* TBD: Undo all the computation */ - dev_err(&sdw_mstr->dev, "bank switch failed\n"); - return ret; - } - } +/** + * sdw_bus_calc_bw_dis - returns Success + * -EINVAL - In case of error. + * + * + * This function is called from sdw_disable_and_unprepare + * whenever stream is ended. The function based disables/ + * unprepare port/channel of associated stream and computes + * required bandwidth, clock, frameshape, computes + * all transport params for a given port, enable channel + * & perform bankswitch for remaining streams on given + * controller. + */ +int sdw_bus_calc_bw_dis(struct sdw_stream_tag *stream_tag, bool unprepare) +{ + struct sdw_runtime *sdw_rt = stream_tag->sdw_rt; + struct sdw_mstr_runtime *sdw_mstr_rt = NULL; + struct sdw_bus *sdw_mstr_bs = NULL; + struct sdw_master *sdw_mstr = NULL; + int ret = 0; - list_for_each_entry(mstr_rt_act, &sdw_rt->mstr_rt_list, - mstr_sdw_node) { - if (mstr_rt_act->mstr == NULL) - break; + /* BW calulation for active master controller for given stream tag */ + list_for_each_entry(sdw_mstr_rt, + &sdw_rt->mstr_rt_list, mstr_sdw_node) { - /* Get bus structure for master */ - mstr_bs_act = master_to_bus(mstr_rt_act->mstr); - ops = mstr_bs_act->mstr->driver->mstr_port_ops; - /* Run for all mstr_list and - * post_activate ports - */ - if (ops->dpn_port_activate_ch_post) { - ret = ops->dpn_port_activate_ch_post - (mstr_bs_act->mstr, NULL, 0); - if (ret < 0) - return ret; - } + if (sdw_mstr_rt->mstr == NULL) + break; - } - list_for_each_entry(mstr_rt_act, &sdw_rt->mstr_rt_list, - mstr_sdw_node) { - if (mstr_rt_act->mstr == NULL) - break; + if (sdw_rt->stream_state != SDW_STATE_ENABLE_STREAM) + goto unprepare_stream; - /* Get bus structure for master */ - mstr_bs_act = master_to_bus(mstr_rt_act->mstr); - ret = sdw_configure_frmshp_bnkswtch_mm_wait( - mstr_bs_act); - } - } - if (!sdw_mstr->link_sync_mask) { + /* Get bus structure for master */ + sdw_mstr_bs = master_to_bus(sdw_mstr_rt->mstr); + if (!sdw_mstr_bs) + return -EINVAL; - /* Configure Frame Shape/Switch Bank */ - ret = sdw_configure_frmshp_bnkswtch(sdw_mstr_bs, - sel_col, sel_row); - if (ret < 0) { - /* TBD: Undo all the computation */ - dev_err(&sdw_mstr->dev, "bank switch failed\n"); - return ret; - } - } - /* Disable all channels enabled on previous bank */ - ret = sdw_dis_chan(sdw_mstr_bs, sdw_mstr_bs_rt); + sdw_mstr = sdw_mstr_bs->mstr; + ret = sdw_pre_en_dis_unprep_op(sdw_mstr_rt, sdw_rt, + SDW_STATE_DISABLE_STREAM); if (ret < 0) { - /* TBD: Undo all the computation */ - dev_err(&sdw_mstr->dev, "Channel disabled failed\n"); - return ret; + dev_err(&sdw_mstr->dev, "Disable Operation failed\n"); + return -EINVAL; } } + unprepare_stream: list_for_each_entry(sdw_mstr_rt, &sdw_rt->mstr_rt_list, mstr_sdw_node) { if (sdw_mstr_rt->mstr == NULL) break; + if ((!unprepare) || + (sdw_rt->stream_state != SDW_STATE_DISABLE_STREAM)) + return 0; - last_rt = list_last_entry(&sdw_rt->mstr_rt_list, - struct sdw_mstr_runtime, mstr_sdw_node); - if (sdw_mstr_rt == last_rt) - last_node = true; - else - last_node = false; - - /* Get bus structure for master */ sdw_mstr_bs = master_to_bus(sdw_mstr_rt->mstr); + if (!sdw_mstr_bs) + return -EINVAL; + sdw_mstr = sdw_mstr_bs->mstr; + ret = sdw_pre_en_dis_unprep_op(sdw_mstr_rt, sdw_rt, + SDW_STATE_UNPREPARE_STREAM); + if (ret < 0) { + dev_err(&sdw_mstr->dev, "Unprepare Operation failed\n"); + return -EINVAL; + } + } + return 0; +} +EXPORT_SYMBOL_GPL(sdw_bus_calc_bw_dis); - sdw_mstr_cap = &sdw_mstr_bs->mstr->mstr_capabilities; - mstr_params = &sdw_mstr_rt->stream_params; +/* + * sdw_slv_dp0_en_dis - returns Success + * -EINVAL - In case of error. + * + * + * This function enable/disable Slave DP0 channels. + */ +int sdw_slv_dp0_en_dis(struct sdw_bus *mstr_bs, + bool is_enable, u8 slv_number) +{ + struct sdw_msg wr_msg, rd_msg; + int ret = 0; + int banktouse; + u8 wbuf[1] = {0}; + u8 rbuf[1] = {0}; - if ((!unprepare) || - (sdw_rt->stream_state != SDW_STATE_DISABLE_STREAM)) - return 0; + /* Get current bank in use from bus structure*/ + banktouse = mstr_bs->active_bank; + banktouse = !banktouse; - /* 1. Un-prepare master and slave port */ - list_for_each_entry(sdw_mstr_bs_rt, &sdw_mstr->mstr_rt_list, - mstr_node) { - if (sdw_mstr_bs_rt->mstr == NULL) - continue; - ret = sdw_prep_unprep_mstr_slv(sdw_mstr_bs, - sdw_rt, false); - if (ret < 0) { - /* TBD: Undo all the computation */ - dev_err(&sdw_mstr->dev, "Ch unprep failed\n"); - return ret; - } + rd_msg.addr = wr_msg.addr = ((SDW_DPN_CHANNELEN + + (SDW_BANK1_REGISTER_OFFSET * banktouse)) + + (SDW_NUM_DATA_PORT_REGISTERS * + 0x0)); + rd_msg.ssp_tag = 0x0; + rd_msg.flag = SDW_MSG_FLAG_READ; + rd_msg.len = 1; + rd_msg.slave_addr = slv_number; + rd_msg.buf = rbuf; + rd_msg.addr_page1 = 0x0; + rd_msg.addr_page2 = 0x0; - /* change stream state to unprepare */ - if (last_node) - sdw_rt->stream_state = - SDW_STATE_UNPREPARE_STREAM; - } + wr_msg.ssp_tag = 0x0; + wr_msg.flag = SDW_MSG_FLAG_WRITE; + wr_msg.len = 1; + wr_msg.slave_addr = slv_number; + wr_msg.buf = wbuf; + wr_msg.addr_page1 = 0x0; + wr_msg.addr_page2 = 0x0; - /* - * Calculate new bandwidth, frame size - * and total BW required for master controller - */ - sdw_mstr_rt->stream_bw = mstr_params->rate * - mstr_params->channel_count * mstr_params->bps; - stream_frame_size = mstr_params->channel_count * - mstr_params->bps; + ret = sdw_slave_transfer(mstr_bs->mstr, &rd_msg, 1); + if (ret != 1) { + ret = -EINVAL; + dev_err(&mstr_bs->mstr->dev, + "Register transfer failed\n"); + goto out; + } - sdw_mstr_bs->bandwidth -= sdw_mstr_rt->stream_bw; + if (is_enable) + wbuf[0] = (rbuf[0] | 0x1); + else + wbuf[0] = (rbuf[0] & ~(0x1)); - /* Something went wrong in bandwidth calulation */ - if (sdw_mstr_bs->bandwidth < 0) { - dev_err(&sdw_mstr->dev, "BW calculation failed\n"); - return -EINVAL; - } + ret = sdw_slave_transfer(mstr_bs->mstr, &wr_msg, 1); + if (ret != 1) { + ret = -EINVAL; + dev_err(&mstr_bs->mstr->dev, + "Register transfer failed\n"); + goto out; + } - if (!sdw_mstr_bs->bandwidth) { - /* - * Last stream on master should - * return successfully - */ - if (last_node) - sdw_rt->stream_state = - SDW_STATE_UNCOMPUTE_STREAM; - continue; - } + rbuf[0] = 0; + /* This is just status read, can be removed later */ + ret = sdw_slave_transfer(mstr_bs->mstr, &rd_msg, 1); + if (ret != 1) { + ret = -EINVAL; + dev_err(&mstr_bs->mstr->dev, + "Register transfer failed\n"); + goto out; + } +out: + return ret; - ret = sdw_get_clock_frmshp(sdw_mstr_bs, &frame_interval, - &sel_col, &sel_row); - if (ret < 0) { - /* TBD: Undo all the computation */ - dev_err(&sdw_mstr->dev, "clock/frameshape failed\n"); +} + + +/* + * sdw_mstr_dp0_act_dis - returns Success + * -EINVAL - In case of error. + * + * + * This function enable/disable Master DP0 channels. + */ +int sdw_mstr_dp0_act_dis(struct sdw_bus *mstr_bs, bool is_enable) +{ + struct sdw_mstr_driver *ops = mstr_bs->mstr->driver; + struct sdw_activate_ch activate_ch; + int banktouse, ret = 0; + + activate_ch.num = 0; + activate_ch.ch_mask = 0x1; + activate_ch.activate = is_enable; /* Enable/Disable */ + + /* Get current bank in use from bus structure*/ + banktouse = mstr_bs->active_bank; + banktouse = !banktouse; + + /* 1. Master port enable_ch_pre */ + if (ops->mstr_port_ops->dpn_port_activate_ch_pre) { + ret = ops->mstr_port_ops->dpn_port_activate_ch_pre + (mstr_bs->mstr, &activate_ch, banktouse); + if (ret < 0) return ret; - } + } - /* Compute new transport params for running streams */ - /* No sorting required here */ + /* 2. Master port enable */ + if (ops->mstr_port_ops->dpn_port_activate_ch) { + ret = ops->mstr_port_ops->dpn_port_activate_ch(mstr_bs->mstr, + &activate_ch, banktouse); + if (ret < 0) + return ret; + } - /* Compute system interval */ - ret = sdw_compute_sys_interval(sdw_mstr_bs, sdw_mstr_cap, - frame_interval); - if (ret < 0) { - /* TBD: Undo all the computation */ - dev_err(&sdw_mstr->dev, "compute SI failed\n"); + /* 3. Master port enable_ch_post */ + if (ops->mstr_port_ops->dpn_port_activate_ch_post) { + ret = ops->mstr_port_ops->dpn_port_activate_ch_post + (mstr_bs->mstr, &activate_ch, banktouse); + if (ret < 0) return ret; - } + } - /* Compute hstart/hstop */ - ret = sdw_compute_hstart_hstop(sdw_mstr_bs, sel_col); - if (ret < 0) { - /* TBD: Undo all the computation */ - dev_err(&sdw_mstr->dev, "compute hstart/hstop fail\n"); + return 0; +} + +/* + * sdw_slv_dp0_prep_unprep - returns Success + * -EINVAL - In case of error. + * + * + * This function prepare/unprepare Slave DP0. + */ +int sdw_slv_dp0_prep_unprep(struct sdw_bus *mstr_bs, + u8 slv_number, bool prepare) +{ + struct sdw_msg wr_msg, rd_msg; + int ret = 0; + int banktouse; + u8 wbuf[1] = {0}; + u8 rbuf[1] = {0}; + + /* Get current bank in use from bus structure*/ + banktouse = mstr_bs->active_bank; + banktouse = !banktouse; + + /* Read SDW_DPN_PREPARECTRL register */ + rd_msg.addr = wr_msg.addr = SDW_DPN_PREPARECTRL + + (SDW_NUM_DATA_PORT_REGISTERS * 0x0); + rd_msg.ssp_tag = 0x0; + rd_msg.flag = SDW_MSG_FLAG_READ; + rd_msg.len = 1; + rd_msg.slave_addr = slv_number; + rd_msg.buf = rbuf; + rd_msg.addr_page1 = 0x0; + rd_msg.addr_page2 = 0x0; + + wr_msg.ssp_tag = 0x0; + wr_msg.flag = SDW_MSG_FLAG_WRITE; + wr_msg.len = 1; + wr_msg.slave_addr = slv_number; + wr_msg.buf = wbuf; + wr_msg.addr_page1 = 0x0; + wr_msg.addr_page2 = 0x0; + + ret = sdw_slave_transfer(mstr_bs->mstr, &rd_msg, 1); + if (ret != 1) { + ret = -EINVAL; + dev_err(&mstr_bs->mstr->dev, + "Register transfer failed\n"); + goto out; + } + + if (prepare) + wbuf[0] = (rbuf[0] | 0x1); + else + wbuf[0] = (rbuf[0] & ~(0x1)); + + /* + * TBD: poll for prepare interrupt bit + * before calling post_prepare + * 2. check capabilities if simplified + * CM no need to prepare + */ + ret = sdw_slave_transfer(mstr_bs->mstr, &wr_msg, 1); + if (ret != 1) { + ret = -EINVAL; + dev_err(&mstr_bs->mstr->dev, + "Register transfer failed\n"); + goto out; + } + + /* + * Sleep for 100ms. + * TODO: check on check on prepare status for port_ready + */ + msleep(100); + +out: + return ret; + +} + +/* + * sdw_mstr_dp0_prep_unprep - returns Success + * -EINVAL - In case of error. + * + * + * This function prepare/unprepare Master DP0. + */ +int sdw_mstr_dp0_prep_unprep(struct sdw_bus *mstr_bs, + bool prep) +{ + struct sdw_mstr_driver *ops = mstr_bs->mstr->driver; + struct sdw_prepare_ch prep_ch; + int ret = 0; + + prep_ch.num = 0x0; + prep_ch.ch_mask = 0x1; + prep_ch.prepare = prep; /* Prepare/Unprepare */ + + /* 1. Master port prepare_ch_pre */ + if (ops->mstr_port_ops->dpn_port_prepare_ch_pre) { + ret = ops->mstr_port_ops->dpn_port_prepare_ch_pre + (mstr_bs->mstr, &prep_ch); + if (ret < 0) return ret; - } + } + + /* 2. Master port prepare */ + if (ops->mstr_port_ops->dpn_port_prepare_ch) { + ret = ops->mstr_port_ops->dpn_port_prepare_ch + (mstr_bs->mstr, &prep_ch); + if (ret < 0) + return ret; + } + + /* 3. Master port prepare_ch_post */ + if (ops->mstr_port_ops->dpn_port_prepare_ch_post) { + ret = ops->mstr_port_ops->dpn_port_prepare_ch_post + (mstr_bs->mstr, &prep_ch); + if (ret < 0) + return ret; + } + + return 0; +} + +static int sdw_bra_config_ops(struct sdw_bus *sdw_mstr_bs, + struct sdw_bra_block *block, + struct sdw_transport_params *t_params, + struct sdw_port_params *p_params) +{ + struct sdw_mstr_driver *ops; + int ret, banktouse; + + /* configure Master transport params */ + ret = sdw_cfg_mstr_params(sdw_mstr_bs, t_params, p_params); + if (ret < 0) { + dev_err(&sdw_mstr_bs->mstr->dev, "BRA: Master xport params config failed\n"); + return ret; + } + + /* configure Slave transport params */ + ret = sdw_cfg_slv_params(sdw_mstr_bs, t_params, + p_params, block->slave_addr); + if (ret < 0) { + dev_err(&sdw_mstr_bs->mstr->dev, "BRA: Slave xport params config failed\n"); + return ret; + } + + /* Get master driver ops */ + ops = sdw_mstr_bs->mstr->driver; + + /* Configure SSP */ + banktouse = sdw_mstr_bs->active_bank; + banktouse = !banktouse; - /* Compute block offset */ - ret = sdw_compute_blk_subblk_offset(sdw_mstr_bs); + if (ops->mstr_ops->set_ssp_interval) { + ret = ops->mstr_ops->set_ssp_interval(sdw_mstr_bs->mstr, + 24, banktouse); if (ret < 0) { - /* TBD: Undo all the computation */ - dev_err(&sdw_mstr->dev, "compute block offset failed\n"); + dev_err(&sdw_mstr_bs->mstr->dev, "BRA: SSP interval config failed\n"); return ret; } + } - /* Configure bus params */ - ret = sdw_cfg_bs_params(sdw_mstr_bs, sdw_mstr_bs_rt, true); + /* Configure Clock */ + if (ops->mstr_ops->set_clock_freq) { + ret = ops->mstr_ops->set_clock_freq(sdw_mstr_bs->mstr, + sdw_mstr_bs->clk_div, banktouse); if (ret < 0) { - /* TBD: Undo all the computation */ - dev_err(&sdw_mstr->dev, "xport params config failed\n"); + dev_err(&sdw_mstr_bs->mstr->dev, "BRA: Clock config failed\n"); return ret; } - if ((last_node) && (sdw_mstr->link_sync_mask)) { - list_for_each_entry(mstr_rt_act, &sdw_rt->mstr_rt_list, - mstr_sdw_node) { + } - if (mstr_rt_act->mstr == NULL) - break; + return 0; +} - /* Get bus structure for master */ - mstr_bs_act = master_to_bus(mstr_rt_act->mstr); +static int sdw_bra_xport_config_enable(struct sdw_bus *sdw_mstr_bs, + struct sdw_bra_block *block, + struct sdw_transport_params *t_params, + struct sdw_port_params *p_params) +{ + int ret; - ops = mstr_bs_act->mstr->driver->mstr_port_ops; + /* Prepare sequence */ + ret = sdw_bra_config_ops(sdw_mstr_bs, block, t_params, p_params); + if (ret < 0) { + dev_err(&sdw_mstr_bs->mstr->dev, "BRA: config operation failed\n"); + return ret; + } - /* - * Run for all mstr_list and - * pre_activate ports - */ - if (ops->dpn_port_activate_ch_pre) { - ret = ops->dpn_port_activate_ch_pre - (mstr_bs_act->mstr, NULL, 0); - if (ret < 0) - return ret; - } - } - list_for_each_entry(mstr_rt_act, &sdw_rt->mstr_rt_list, - mstr_sdw_node) { + /* Bank Switch */ + ret = sdw_cfg_frmshp_bnkswtch(sdw_mstr_bs, false); + if (ret < 0) { + dev_err(&sdw_mstr_bs->mstr->dev, "BRA: bank switch failed\n"); + return ret; + } - if (mstr_rt_act->mstr == NULL) - break; + /* + * TODO: There may be some slave which doesn't support + * prepare for DP0. We have two options here. + * 1. Just call prepare and ignore error from those + * codec who doesn't support prepare for DP0. + * 2. Get slave capabilities and based on prepare DP0 + * support, Program Slave prepare register. + * Currently going with approach 1, not checking return + * value. + * 3. Try to use existing prep_unprep API both for master + * and slave. + */ + sdw_slv_dp0_prep_unprep(sdw_mstr_bs, block->slave_addr, true); - /* Get bus structure for master */ - mstr_bs_act = master_to_bus( - mstr_rt_act->mstr); - - /* Configure Frame Shape/Switch Bank */ - ret = sdw_configure_frmshp_bnkswtch_mm( - mstr_bs_act, - sel_col, sel_row); - if (ret < 0) { - /* TBD: Undo all the computation */ - dev_err(&sdw_mstr->dev, - "bank switch failed\n"); - return ret; - } - } + /* Prepare Master port */ + ret = sdw_mstr_dp0_prep_unprep(sdw_mstr_bs, true); + if (ret < 0) { + dev_err(&sdw_mstr_bs->mstr->dev, "BRA: Master prepare failed\n"); + return ret; + } - list_for_each_entry(mstr_rt_act, &sdw_rt->mstr_rt_list, - mstr_sdw_node) { + /* Enable sequence */ + ret = sdw_bra_config_ops(sdw_mstr_bs, block, t_params, p_params); + if (ret < 0) { + dev_err(&sdw_mstr_bs->mstr->dev, "BRA: config operation failed\n"); + return ret; + } + /* Enable DP0 channel (Slave) */ + ret = sdw_slv_dp0_en_dis(sdw_mstr_bs, true, block->slave_addr); + if (ret < 0) { + dev_err(&sdw_mstr_bs->mstr->dev, "BRA: Slave DP0 enable failed\n"); + return ret; + } - if (mstr_rt_act->mstr == NULL) - break; + /* Enable DP0 channel (Master) */ + ret = sdw_mstr_dp0_act_dis(sdw_mstr_bs, true); + if (ret < 0) { + dev_err(&sdw_mstr_bs->mstr->dev, "BRA: Master DP0 enable failed\n"); + return ret; + } - /* Get bus structure for master */ - mstr_bs_act = master_to_bus(mstr_rt_act->mstr); + /* Bank Switch */ + ret = sdw_cfg_frmshp_bnkswtch(sdw_mstr_bs, false); + if (ret < 0) { + dev_err(&sdw_mstr_bs->mstr->dev, "BRA: bank switch failed\n"); + return ret; + } - ops = mstr_bs_act->mstr->driver->mstr_port_ops; + return 0; +} - /* Run for all mstr_list and - * post_activate ports - */ - if (ops->dpn_port_activate_ch_post) { - ret = ops->dpn_port_activate_ch_post - (mstr_bs_act->mstr, NULL, 0); - if (ret < 0) - return ret; - } - } - list_for_each_entry(mstr_rt_act, &sdw_rt->mstr_rt_list, - mstr_sdw_node) { +static int sdw_bra_xport_config_disable(struct sdw_bus *sdw_mstr_bs, + struct sdw_bra_block *block) +{ + int ret; - if (mstr_rt_act->mstr == NULL) - break; + /* Disable DP0 channel (Slave) */ + ret = sdw_slv_dp0_en_dis(sdw_mstr_bs, false, block->slave_addr); + if (ret < 0) { + dev_err(&sdw_mstr_bs->mstr->dev, "BRA: Slave DP0 disable failed\n"); + return ret; + } - /* Get bus structure for master */ - mstr_bs_act = master_to_bus(mstr_rt_act->mstr); - ret = sdw_configure_frmshp_bnkswtch_mm_wait( - mstr_bs_act); - } - } - if (!sdw_mstr->link_sync_mask) { - /* Configure Frame Shape/Switch Bank */ - ret = sdw_configure_frmshp_bnkswtch(sdw_mstr_bs, - sel_col, sel_row); - if (ret < 0) { - /* TBD: Undo all the computation */ - dev_err(&sdw_mstr->dev, "bank switch failed\n"); - return ret; - } + /* Disable DP0 channel (Master) */ + ret = sdw_mstr_dp0_act_dis(sdw_mstr_bs, false); + if (ret < 0) { + dev_err(&sdw_mstr_bs->mstr->dev, "BRA: Master DP0 disable failed\n"); + return ret; + } + + /* Bank Switch */ + ret = sdw_cfg_frmshp_bnkswtch(sdw_mstr_bs, false); + if (ret < 0) { + dev_err(&sdw_mstr_bs->mstr->dev, "BRA: bank switch failed\n"); + return ret; + } + /* + * TODO: There may be some slave which doesn't support + * de-prepare for DP0. We have two options here. + * 1. Just call prepare and ignore error from those + * codec who doesn't support de-prepare for DP0. + * 2. Get slave capabilities and based on prepare DP0 + * support, Program Slave prepare register. + * Currently going with approach 1, not checking return + * value. + */ + sdw_slv_dp0_prep_unprep(sdw_mstr_bs, block->slave_addr, false); + + /* De-prepare Master port */ + ret = sdw_mstr_dp0_prep_unprep(sdw_mstr_bs, false); + if (ret < 0) { + dev_err(&sdw_mstr_bs->mstr->dev, "BRA: Master de-prepare failed\n"); + return ret; + } + + return 0; +} + +int sdw_bus_bra_xport_config(struct sdw_bus *sdw_mstr_bs, + struct sdw_bra_block *block, bool enable) +{ + struct sdw_transport_params t_params; + struct sdw_port_params p_params; + int ret; + + /* TODO: + * compute transport parameters based on current clock and + * frameshape. need to check how algorithm should be designed + * for BRA for computing clock, frameshape, SSP and transport params. + */ + + /* Transport Parameters */ + t_params.num = 0x0; /* DP 0 */ + t_params.blockpackingmode = 0x0; + t_params.blockgroupcontrol_valid = false; + t_params.blockgroupcontrol = 0x0; + t_params.lanecontrol = 0; + t_params.sample_interval = 10; + + t_params.hstart = 7; + t_params.hstop = 9; + t_params.offset1 = 0; + t_params.offset2 = 0; + + /* Port Parameters */ + p_params.num = 0x0; /* DP 0 */ + + /* Isochronous Mode */ + p_params.port_flow_mode = 0x0; + + /* Normal Mode */ + p_params.port_data_mode = 0x0; + + /* Word length */ + p_params.word_length = 3; + + /* Frameshape and clock params */ + sdw_mstr_bs->clk_div = 1; + sdw_mstr_bs->col = 10; + sdw_mstr_bs->row = 80; + +#if IS_ENABLED(CONFIG_SND_SOC_INTEL_CNL_FPGA) + sdw_mstr_bs->bandwidth = 9.6 * 1000 * 1000; +#else + sdw_mstr_bs->bandwidth = 12 * 1000 * 1000; +#endif + + if (enable) { + ret = sdw_bra_xport_config_enable(sdw_mstr_bs, block, + &t_params, &p_params); + if (ret < 0) { + dev_err(&sdw_mstr_bs->mstr->dev, "BRA: Xport params config failed\n"); + return ret; } - /* Change stream state to uncompute */ - if (last_node) - sdw_rt->stream_state = SDW_STATE_UNCOMPUTE_STREAM; - /* Disable all channels enabled on previous bank */ - ret = sdw_dis_chan(sdw_mstr_bs, sdw_mstr_bs_rt); + } else { + ret = sdw_bra_xport_config_disable(sdw_mstr_bs, block); if (ret < 0) { - /* TBD: Undo all the computation */ - dev_err(&sdw_mstr_bs->mstr->dev, - "Channel disabled failed\n"); + dev_err(&sdw_mstr_bs->mstr->dev, "BRA: Xport params de-config failed\n"); return ret; } } return 0; } -EXPORT_SYMBOL_GPL(sdw_bus_calc_bw_dis); diff --git a/drivers/sdw/sdw_cnl.c b/drivers/sdw/sdw_cnl.c index 274966572499..9f8e77c20699 100644 --- a/drivers/sdw/sdw_cnl.c +++ b/drivers/sdw/sdw_cnl.c @@ -260,8 +260,9 @@ static int sdw_config_update(struct cnl_sdw *sdw) { struct cnl_sdw_data *data = &sdw->data; struct sdw_master *mstr = sdw->mstr; - + int sync_reg, syncgo_mask; volatile int config_update = 0; + volatile int sync_update = 0; /* Try 10 times before giving up on configuration update */ int timeout = 10; int config_updated = 0; @@ -271,6 +272,44 @@ static int sdw_config_update(struct cnl_sdw *sdw) /* Bit is self-cleared when configuration gets updated. */ cnl_sdw_reg_writel(data->sdw_regs, SDW_CNL_MCP_CONFIGUPDATE, config_update); + + /* + * Set SYNCGO bit for Master(s) running in aggregated mode + * (MMModeEN = 1). This action causes all gSyncs of all Master IPs + * to be unmasked and asserted at the currently active gSync rate. + * The initialization-pending Master IP SoundWire bus clock will + * start up synchronizing to gSync, leading to bus reset entry, + * subsequent exit, and 1st Frame generation aligning to gSync. + * Note that this is done in order to overcome hardware bug related + * to mis-alignment of gSync and frame. + */ + if (mstr->link_sync_mask) { + sync_reg = cnl_sdw_reg_readl(data->sdw_shim, SDW_CNL_SYNC); + sync_reg |= (CNL_SYNC_SYNCGO_MASK << CNL_SYNC_SYNCGO_SHIFT); + cnl_sdw_reg_writel(data->sdw_shim, SDW_CNL_SYNC, sync_reg); + syncgo_mask = (CNL_SYNC_SYNCGO_MASK << CNL_SYNC_SYNCGO_SHIFT); + + do { + sync_update = cnl_sdw_reg_readl(data->sdw_shim, + SDW_CNL_SYNC); + if ((sync_update & syncgo_mask) == 0) + break; + + msleep(20); + timeout--; + + } while (timeout); + + if ((sync_update & syncgo_mask) != 0) { + dev_err(&mstr->dev, "Failed to set sync go\n"); + return -EIO; + } + + /* Reset timeout */ + timeout = 10; + } + + /* Wait for config update bit to be self cleared */ do { config_update = cnl_sdw_reg_readl(data->sdw_regs, SDW_CNL_MCP_CONFIGUPDATE); @@ -369,12 +408,10 @@ static int sdw_pdm_pdi_init(struct cnl_sdw *sdw) int pdm_cap, pdm_ch_count, total_pdm_streams; int pdm_cap_offset = SDW_CNL_PDMSCAP + (data->inst_id * SDW_CNL_PDMSCAP_REG_OFFSET); - - pdm_cap = cnl_sdw_reg_readw(data->sdw_regs, pdm_cap_offset); + pdm_cap = cnl_sdw_reg_readw(data->sdw_shim, pdm_cap_offset); sdw->num_pdm_streams = (pdm_cap >> CNL_PDMSCAP_BSS_SHIFT) & CNL_PDMSCAP_BSS_MASK; - /* Zero based value in register */ - sdw->num_pdm_streams++; + sdw->pdm_streams = devm_kzalloc(&mstr->dev, sdw->num_pdm_streams * sizeof(struct cnl_sdw_pdi_stream), GFP_KERNEL); @@ -383,8 +420,7 @@ static int sdw_pdm_pdi_init(struct cnl_sdw *sdw) sdw->num_in_pdm_streams = (pdm_cap >> CNL_PDMSCAP_ISS_SHIFT) & CNL_PDMSCAP_ISS_MASK; - /* Zero based value in register */ - sdw->num_in_pdm_streams++; + sdw->in_pdm_streams = devm_kzalloc(&mstr->dev, sdw->num_in_pdm_streams * sizeof(struct cnl_sdw_pdi_stream), GFP_KERNEL); @@ -395,7 +431,6 @@ static int sdw_pdm_pdi_init(struct cnl_sdw *sdw) sdw->num_out_pdm_streams = (pdm_cap >> CNL_PDMSCAP_OSS_SHIFT) & CNL_PDMSCAP_OSS_MASK; /* Zero based value in register */ - sdw->num_out_pdm_streams++; sdw->out_pdm_streams = devm_kzalloc(&mstr->dev, sdw->num_out_pdm_streams * sizeof(struct cnl_sdw_pdi_stream), GFP_KERNEL); @@ -443,15 +478,13 @@ static int sdw_port_pdi_init(struct cnl_sdw *sdw) return ret; } -static int sdw_init(struct cnl_sdw *sdw) +static int sdw_init(struct cnl_sdw *sdw, bool is_first_init) { struct sdw_master *mstr = sdw->mstr; struct cnl_sdw_data *data = &sdw->data; - int mcp_config, mcp_control, sync_reg; - + int mcp_config, mcp_control, sync_reg, mcp_clockctrl; volatile int sync_update = 0; - /* Try 10 times before timing out */ - int timeout = 10; + int timeout = 10; /* Try 10 times before timing out */ int ret = 0; /* Power up the link controller */ @@ -465,9 +498,11 @@ static int sdw_init(struct cnl_sdw *sdw) /* Switch the ownership to Master IP from glue logic */ sdw_switch_to_mip(sdw); - /* Set the Sync period to default */ + /* Set SyncPRD period */ sync_reg = cnl_sdw_reg_readl(data->sdw_shim, SDW_CNL_SYNC); sync_reg |= (SDW_CNL_DEFAULT_SYNC_PERIOD << CNL_SYNC_SYNCPRD_SHIFT); + + /* Set SyncPU bit */ sync_reg |= (0x1 << CNL_SYNC_SYNCCPU_SHIFT); cnl_sdw_reg_writel(data->sdw_shim, SDW_CNL_SYNC, sync_reg); @@ -484,6 +519,39 @@ static int sdw_init(struct cnl_sdw *sdw) return -EINVAL; } + /* + * Set CMDSYNC bit based on Master ID + * Note that this bit is set only for the Master which will be + * running in aggregated mode (MMModeEN = 1). By doing + * this the gSync to Master IP to be masked inactive. + * Note that this is done in order to overcome hardware bug related + * to mis-alignment of gSync and frame. + */ + if (mstr->link_sync_mask) { + + sync_reg = cnl_sdw_reg_readl(data->sdw_shim, SDW_CNL_SYNC); + sync_reg |= (1 << (data->inst_id + CNL_SYNC_CMDSYNC_SHIFT)); + cnl_sdw_reg_writel(data->sdw_shim, SDW_CNL_SYNC, sync_reg); + } + + /* Set clock divider to default value in default bank */ + mcp_clockctrl = cnl_sdw_reg_readl(data->sdw_regs, + SDW_CNL_MCP_CLOCKCTRL0); + mcp_clockctrl |= SDW_CNL_DEFAULT_CLK_DIVIDER; + cnl_sdw_reg_writel(data->sdw_regs, SDW_CNL_MCP_CLOCKCTRL0, + mcp_clockctrl); + + /* Set the Frame shape init to default value */ + cnl_sdw_reg_writel(data->sdw_regs, SDW_CNL_MCP_FRAMESHAPEINIT, + SDW_CNL_DEFAULT_FRAME_SHAPE); + + + /* Set the SSP interval to default value for both banks */ + cnl_sdw_reg_writel(data->sdw_regs, SDW_CNL_MCP_SSPCTRL0, + SDW_CNL_DEFAULT_SSP_INTERVAL); + cnl_sdw_reg_writel(data->sdw_regs, SDW_CNL_MCP_SSPCTRL1, + SDW_CNL_DEFAULT_SSP_INTERVAL); + /* Set command acceptance mode. This is required because when * Master broadcasts the clock_stop command to slaves, slaves * might be already suspended, so this return NO ACK, in that @@ -495,7 +563,6 @@ static int sdw_init(struct cnl_sdw *sdw) MCP_CONTROL_CMDACCEPTMODE_SHIFT); cnl_sdw_reg_writel(data->sdw_regs, SDW_CNL_MCP_CONTROL, mcp_control); - cnl_sdw_reg_writel(data->sdw_regs, SDW_CNL_MCP_FRAMESHAPEINIT, 0x48); mcp_config = cnl_sdw_reg_readl(data->sdw_regs, SDW_CNL_MCP_CONFIG); /* Set Max cmd retry to 15 times */ @@ -541,22 +608,19 @@ static int sdw_init(struct cnl_sdw *sdw) MCP_CONFIG_OPERATIONMODE_SHIFT); cnl_sdw_reg_writel(data->sdw_regs, SDW_CNL_MCP_CONFIG, mcp_config); - /* Set the SSP interval to 32 for both banks */ - cnl_sdw_reg_writel(data->sdw_regs, SDW_CNL_MCP_SSPCTRL0, - SDW_CNL_DEFAULT_SSP_INTERVAL); - cnl_sdw_reg_writel(data->sdw_regs, SDW_CNL_MCP_SSPCTRL1, - SDW_CNL_DEFAULT_SSP_INTERVAL); /* Initialize the phy control registers. */ sdw_init_phyctrl(sdw); - /* Initlaize the ports */ - ret = sdw_port_pdi_init(sdw); - if (ret) { - dev_err(&mstr->dev, "SoundWire controller init failed %d\n", + if (is_first_init) { + /* Initlaize the ports */ + ret = sdw_port_pdi_init(sdw); + if (ret) { + dev_err(&mstr->dev, "SoundWire controller init failed %d\n", data->inst_id); - sdw_power_down_link(sdw); - return ret; + sdw_power_down_link(sdw); + return ret; + } } /* Lastly enable interrupts */ @@ -604,7 +668,7 @@ static int sdw_alloc_pcm_stream(struct cnl_sdw *sdw, pdi_stream->h_ch_num = ch_cnt - 1; ch_map_offset = SDW_CNL_PCMSCHM + (SDW_CNL_PCMSCHM_REG_OFFSET * mstr->nr) + - (0x2 * pdi_stream->pdi_num); + (SDW_PCM_STRM_START_INDEX * pdi_stream->pdi_num); if (port->direction == SDW_DATA_DIR_IN) pdi_ch_map |= (CNL_PCMSYCM_DIR_MASK << CNL_PCMSYCM_DIR_SHIFT); else @@ -1134,10 +1198,758 @@ static enum sdw_command_response cnl_sdw_xfer_msg(struct sdw_master *mstr, return ret; } +static void cnl_sdw_bra_prep_crc(u8 *txdata_buf, + struct sdw_bra_block *block, int data_offset, int addr_offset) +{ + + int addr = addr_offset; + + txdata_buf[addr++] = sdw_bus_compute_crc8((block->values + data_offset), + block->num_bytes); + txdata_buf[addr++] = 0x0; + txdata_buf[addr++] = 0x0; + txdata_buf[addr] |= ((0x2 & SDW_BRA_SOP_EOP_PDI_MASK) + << SDW_BRA_SOP_EOP_PDI_SHIFT); +} + +static void cnl_sdw_bra_prep_data(u8 *txdata_buf, + struct sdw_bra_block *block, int data_offset, int addr_offset) +{ + + int i; + int addr = addr_offset; + + for (i = 0; i < block->num_bytes; i += 2) { + + txdata_buf[addr++] = block->values[i + data_offset]; + if ((block->num_bytes - 1) - i) + txdata_buf[addr++] = block->values[i + data_offset + 1]; + else + txdata_buf[addr++] = 0; + + txdata_buf[addr++] = 0; + txdata_buf[addr++] = 0; + } +} + +static void cnl_sdw_bra_prep_hdr(u8 *txdata_buf, + struct sdw_bra_block *block, int rolling_id, int offset) +{ + + u8 tmp_hdr[6] = {0, 0, 0, 0, 0, 0}; + u8 temp = 0x0; + + /* + * 6 bytes header + * 1st byte: b11001010 + * b11: Header is active + * b0010: Device number 2 is selected + * b1: Write operation + * b0: MSB of BRA_NumBytes is 0 + * 2nd byte: LSB of number of bytes + * 3rd byte to 6th byte: Slave register offset + */ + temp |= (SDW_BRA_HDR_ACTIVE & SDW_BRA_HDR_ACTIVE_MASK) << + SDW_BRA_HDR_ACTIVE_SHIFT; + temp |= (block->slave_addr & SDW_BRA_HDR_SLV_ADDR_MASK) << + SDW_BRA_HDR_SLV_ADDR_SHIFT; + temp |= (block->cmd & SDW_BRA_HDR_RD_WR_MASK) << + SDW_BRA_HDR_RD_WR_SHIFT; + + if (block->num_bytes > SDW_BRA_HDR_MSB_BYTE_CHK) + temp |= (SDW_BRA_HDR_MSB_BYTE_SET & SDW_BRA_HDR_MSB_BYTE_MASK); + else + temp |= (SDW_BRA_HDR_MSB_BYTE_UNSET & + SDW_BRA_HDR_MSB_BYTE_MASK); + + txdata_buf[offset + 0] = tmp_hdr[0] = temp; + txdata_buf[offset + 1] = tmp_hdr[1] = block->num_bytes; + txdata_buf[offset + 3] |= ((SDW_BRA_SOP_EOP_PDI_STRT_VALUE & + SDW_BRA_SOP_EOP_PDI_MASK) << + SDW_BRA_SOP_EOP_PDI_SHIFT); + + txdata_buf[offset + 3] |= ((rolling_id & SDW_BRA_ROLLINGID_PDI_MASK) + << SDW_BRA_ROLLINGID_PDI_SHIFT); + + txdata_buf[offset + 4] = tmp_hdr[2] = ((block->reg_offset & + SDW_BRA_HDR_SLV_REG_OFF_MASK24) + >> SDW_BRA_HDR_SLV_REG_OFF_SHIFT24); + + txdata_buf[offset + 5] = tmp_hdr[3] = ((block->reg_offset & + SDW_BRA_HDR_SLV_REG_OFF_MASK16) + >> SDW_BRA_HDR_SLV_REG_OFF_SHIFT16); + + txdata_buf[offset + 8] = tmp_hdr[4] = ((block->reg_offset & + SDW_BRA_HDR_SLV_REG_OFF_MASK8) + >> SDW_BRA_HDR_SLV_REG_OFF_SHIFT8); + + txdata_buf[offset + 9] = tmp_hdr[5] = (block->reg_offset & + SDW_BRA_HDR_SLV_REG_OFF_MASK0); + + /* CRC check */ + txdata_buf[offset + 0xc] = sdw_bus_compute_crc8(tmp_hdr, + SDW_BRA_HEADER_SIZE); + + if (!block->cmd) + txdata_buf[offset + 0xf] = ((SDW_BRA_SOP_EOP_PDI_END_VALUE & + SDW_BRA_SOP_EOP_PDI_MASK) << + SDW_BRA_SOP_EOP_PDI_SHIFT); +} + +static void cnl_sdw_bra_pdi_tx_config(struct sdw_master *mstr, + struct cnl_sdw *sdw, bool enable) +{ + struct cnl_sdw_pdi_stream tx_pdi_stream; + unsigned int tx_ch_map_offset, port_ctrl_offset, tx_pdi_config_offset; + unsigned int port_ctrl = 0, tx_pdi_config = 0, tx_stream_config; + int tx_pdi_ch_map = 0; + + if (enable) { + /* DP0 PORT CTRL REG */ + port_ctrl_offset = SDW_CNL_PORTCTRL + (SDW_BRA_PORT_ID * + SDW_CNL_PORT_REG_OFFSET); + + port_ctrl &= ~(PORTCTRL_PORT_DIRECTION_MASK << + PORTCTRL_PORT_DIRECTION_SHIFT); + + port_ctrl |= ((SDW_BRA_BULK_ENABLE & SDW_BRA_BLK_EN_MASK) << + SDW_BRA_BLK_EN_SHIFT); + + port_ctrl |= ((SDW_BRA_BPT_PAYLOAD_TYPE & + SDW_BRA_BPT_PYLD_TY_MASK) << + SDW_BRA_BPT_PYLD_TY_SHIFT); + + cnl_sdw_reg_writel(sdw->data.sdw_regs, port_ctrl_offset, + port_ctrl); + + /* PDI0 Programming */ + tx_pdi_stream.l_ch_num = 0; + tx_pdi_stream.h_ch_num = 0xF; + tx_pdi_stream.pdi_num = SDW_BRA_PDI_TX_ID; + /* TODO: Remove hardcoding */ + tx_pdi_stream.sdw_pdi_num = mstr->nr * 16 + + tx_pdi_stream.pdi_num + 3; + + /* SNDWxPCMS2CM SHIM REG */ + tx_ch_map_offset = SDW_CNL_CTLS2CM + + (SDW_CNL_PCMSCHM_REG_OFFSET * mstr->nr); + + tx_pdi_ch_map |= (tx_pdi_stream.sdw_pdi_num & + CNL_PCMSYCM_STREAM_MASK) << + CNL_PCMSYCM_STREAM_SHIFT; + + tx_pdi_ch_map |= (tx_pdi_stream.l_ch_num & + CNL_PCMSYCM_LCHAN_MASK) << + CNL_PCMSYCM_LCHAN_SHIFT; + + tx_pdi_ch_map |= (tx_pdi_stream.h_ch_num & + CNL_PCMSYCM_HCHAN_MASK) << + CNL_PCMSYCM_HCHAN_SHIFT; + + cnl_sdw_reg_writew(sdw->data.sdw_shim, tx_ch_map_offset, + tx_pdi_ch_map); + + /* TX PDI0 CONFIG REG BANK 0 */ + tx_pdi_config_offset = (SDW_CNL_PDINCONFIG0 + + (tx_pdi_stream.pdi_num * 16)); + + tx_pdi_config |= ((SDW_BRA_PORT_ID & + PDINCONFIG_PORT_NUMBER_MASK) << + PDINCONFIG_PORT_NUMBER_SHIFT); + + tx_pdi_config |= (SDW_BRA_CHN_MASK << + PDINCONFIG_CHANNEL_MASK_SHIFT); + + tx_pdi_config |= (SDW_BRA_SOFT_RESET << + PDINCONFIG_PORT_SOFT_RESET_SHIFT); + + cnl_sdw_reg_writel(sdw->data.sdw_regs, + tx_pdi_config_offset, tx_pdi_config); + + /* ALH STRMzCFG REG */ + tx_stream_config = cnl_sdw_reg_readl(sdw->data.alh_base, + (tx_pdi_stream.sdw_pdi_num * + ALH_CNL_STRMZCFG_OFFSET)); + + tx_stream_config |= (CNL_STRMZCFG_DMAT_VAL & + CNL_STRMZCFG_DMAT_MASK) << + CNL_STRMZCFG_DMAT_SHIFT; + + tx_stream_config |= (0x0 & CNL_STRMZCFG_CHAN_MASK) << + CNL_STRMZCFG_CHAN_SHIFT; + + cnl_sdw_reg_writel(sdw->data.alh_base, + (tx_pdi_stream.sdw_pdi_num * + ALH_CNL_STRMZCFG_OFFSET), + tx_stream_config); + + + } else { + + /* + * TODO: There is official workaround which needs to be + * performed for PDI config register. The workaround + * is to perform SoftRst twice in order to clear + * PDI fifo contents. + */ + + } +} + +static void cnl_sdw_bra_pdi_rx_config(struct sdw_master *mstr, + struct cnl_sdw *sdw, bool enable) +{ + + struct cnl_sdw_pdi_stream rx_pdi_stream; + unsigned int rx_ch_map_offset, rx_pdi_config_offset, rx_stream_config; + unsigned int rx_pdi_config = 0; + int rx_pdi_ch_map = 0; + + if (enable) { + + /* RX PDI1 Configuration */ + rx_pdi_stream.l_ch_num = 0; + rx_pdi_stream.h_ch_num = 0xF; + rx_pdi_stream.pdi_num = SDW_BRA_PDI_RX_ID; + rx_pdi_stream.sdw_pdi_num = mstr->nr * 16 + + rx_pdi_stream.pdi_num + 3; + + /* SNDWxPCMS3CM SHIM REG */ + rx_ch_map_offset = SDW_CNL_CTLS3CM + + (SDW_CNL_PCMSCHM_REG_OFFSET * mstr->nr); + + rx_pdi_ch_map |= (rx_pdi_stream.sdw_pdi_num & + CNL_PCMSYCM_STREAM_MASK) << + CNL_PCMSYCM_STREAM_SHIFT; + + rx_pdi_ch_map |= (rx_pdi_stream.l_ch_num & + CNL_PCMSYCM_LCHAN_MASK) << + CNL_PCMSYCM_LCHAN_SHIFT; + + rx_pdi_ch_map |= (rx_pdi_stream.h_ch_num & + CNL_PCMSYCM_HCHAN_MASK) << + CNL_PCMSYCM_HCHAN_SHIFT; + + cnl_sdw_reg_writew(sdw->data.sdw_shim, rx_ch_map_offset, + rx_pdi_ch_map); + + /* RX PDI1 CONFIG REG */ + rx_pdi_config_offset = (SDW_CNL_PDINCONFIG0 + + (rx_pdi_stream.pdi_num * 16)); + + rx_pdi_config |= ((SDW_BRA_PORT_ID & + PDINCONFIG_PORT_NUMBER_MASK) << + PDINCONFIG_PORT_NUMBER_SHIFT); + + rx_pdi_config |= (SDW_BRA_CHN_MASK << + PDINCONFIG_CHANNEL_MASK_SHIFT); + + rx_pdi_config |= (SDW_BRA_SOFT_RESET << + PDINCONFIG_PORT_SOFT_RESET_SHIFT); + + cnl_sdw_reg_writel(sdw->data.sdw_regs, + rx_pdi_config_offset, rx_pdi_config); + + + /* ALH STRMzCFG REG */ + rx_stream_config = cnl_sdw_reg_readl(sdw->data.alh_base, + (rx_pdi_stream.sdw_pdi_num * + ALH_CNL_STRMZCFG_OFFSET)); + + rx_stream_config |= (CNL_STRMZCFG_DMAT_VAL & + CNL_STRMZCFG_DMAT_MASK) << + CNL_STRMZCFG_DMAT_SHIFT; + + rx_stream_config |= (0 & CNL_STRMZCFG_CHAN_MASK) << + CNL_STRMZCFG_CHAN_SHIFT; + + cnl_sdw_reg_writel(sdw->data.alh_base, + (rx_pdi_stream.sdw_pdi_num * + ALH_CNL_STRMZCFG_OFFSET), + rx_stream_config); + + } else { + + /* + * TODO: There is official workaround which needs to be + * performed for PDI config register. The workaround + * is to perform SoftRst twice in order to clear + * PDI fifo contents. + */ + + } +} + +static void cnl_sdw_bra_pdi_config(struct sdw_master *mstr, bool enable) +{ + struct cnl_sdw *sdw; + + /* Get driver data for master */ + sdw = sdw_master_get_drvdata(mstr); + + /* PDI0 configuration */ + cnl_sdw_bra_pdi_tx_config(mstr, sdw, enable); + + /* PDI1 configuration */ + cnl_sdw_bra_pdi_rx_config(mstr, sdw, enable); +} + +static int cnl_sdw_bra_verify_footer(u8 *rx_buf, int offset) +{ + int ret = 0; + u8 ftr_response; + u8 ack_nack = 0; + u8 ftr_result = 0; + + ftr_response = rx_buf[offset]; + + /* + * ACK/NACK check + * NACK+ACK value from target: + * 00 -> Ignored + * 01 -> OK + * 10 -> Failed (Header CRC check failed) + * 11 -> Reserved + * NACK+ACK values at Target or initiator + * 00 -> Ignored + * 01 -> OK + * 10 -> Abort (Header cannot be trusted) + * 11 -> Abort (Header cannot be trusted) + */ + ack_nack = ((ftr_response >> SDW_BRA_FTR_RESP_ACK_SHIFT) & + SDW_BRA_FTR_RESP_ACK_MASK); + if (ack_nack == SDW_BRA_ACK_NAK_IGNORED) { + pr_info("BRA Packet Ignored\n"); + ret = -EINVAL; + } else if (ack_nack == SDW_BRA_ACK_NAK_OK) + pr_info("BRA: Packet OK\n"); + else if (ack_nack == SDW_BRA_ACK_NAK_FAILED_ABORT) { + pr_info("BRA: Packet Failed/Reserved\n"); + return -EINVAL; + } else if (ack_nack == SDW_BRA_ACK_NAK_RSVD_ABORT) { + pr_info("BRA: Packet Reserved/Abort\n"); + return -EINVAL; + } + + /* + * BRA footer result check + * Writes: + * 0 -> Good. Target accepted write payload + * 1 -> Bad. Target did not accept write payload + * Reads: + * 0 -> Good. Target completed read operation successfully + * 1 -> Bad. Target failed to complete read operation successfully + */ + ftr_result = (ftr_response >> SDW_BRA_FTR_RESP_RES_SHIFT) & + SDW_BRA_FTR_RESP_RES_MASK; + if (ftr_result == SDW_BRA_FTR_RESULT_BAD) { + pr_info("BRA: Read/Write operation failed on target side\n"); + /* Error scenario */ + return -EINVAL; + } + + pr_info("BRA: Read/Write operation complete on target side\n"); + + return ret; +} + +static int cnl_sdw_bra_verify_hdr(u8 *rx_buf, int offset, bool *chk_footer, + int roll_id) +{ + int ret = 0; + u8 hdr_response, rolling_id; + u8 ack_nack = 0; + u8 not_ready = 0; + + /* Match rolling ID */ + hdr_response = rx_buf[offset]; + rolling_id = rx_buf[offset + SDW_BRA_ROLLINGID_PDI_INDX]; + + rolling_id = (rolling_id & SDW_BRA_ROLLINGID_PDI_MASK); + if (roll_id != rolling_id) { + pr_info("BRA: Rolling ID doesn't match, returning error\n"); + return -EINVAL; + } + + /* + * ACK/NACK check + * NACK+ACK value from target: + * 00 -> Ignored + * 01 -> OK + * 10 -> Failed (Header CRC check failed) + * 11 -> Reserved + * NACK+ACK values at Target or initiator + * 00 -> Ignored + * 01 -> OK + * 10 -> Abort (Header cannot be trusted) + * 11 -> Abort (Header cannot be trusted) + */ + ack_nack = ((hdr_response >> SDW_BRA_HDR_RESP_ACK_SHIFT) & + SDW_BRA_HDR_RESP_ACK_MASK); + if (ack_nack == SDW_BRA_ACK_NAK_IGNORED) { + pr_info("BRA: Packet Ignored rolling_id:%d\n", rolling_id); + ret = -EINVAL; + } else if (ack_nack == SDW_BRA_ACK_NAK_OK) + pr_info("BRA: Packet OK rolling_id:%d\n", rolling_id); + else if (ack_nack == SDW_BRA_ACK_NAK_FAILED_ABORT) { + pr_info("BRA: Packet Failed/Abort rolling_id:%d\n", rolling_id); + return -EINVAL; + } else if (ack_nack == SDW_BRA_ACK_NAK_RSVD_ABORT) { + pr_info("BRA: Packet Reserved/Abort rolling_id:%d\n", rolling_id); + return -EINVAL; + } + + /* BRA not ready check */ + not_ready = (hdr_response >> SDW_BRA_HDR_RESP_NRDY_SHIFT) & + SDW_BRA_HDR_RESP_NRDY_MASK; + if (not_ready == SDW_BRA_TARGET_NOT_READY) { + pr_info("BRA: Target not ready for read/write operation rolling_id:%d\n", + rolling_id); + chk_footer = false; + return -EBUSY; + } + + pr_info("BRA: Target ready for read/write operation rolling_id:%d\n", rolling_id); + return ret; +} + +static void cnl_sdw_bra_remove_data_padding(u8 *src_buf, u8 *dst_buf, + u8 size) { + + int i; + + for (i = 0; i < size/2; i++) { + + *dst_buf++ = *src_buf++; + *dst_buf++ = *src_buf++; + src_buf++; + src_buf++; + } +} + + +static int cnl_sdw_bra_check_data(struct sdw_master *mstr, + struct sdw_bra_block *block, struct bra_info *info) { + + int offset = 0, rolling_id = 0, tmp_offset = 0; + int rx_crc_comp = 0, rx_crc_rvd = 0; + int i, ret; + bool chk_footer = true; + int rx_buf_size = info->rx_block_size; + u8 *rx_buf = info->rx_ptr; + u8 *tmp_buf = NULL; + + /* TODO: Remove below hex dump print */ + print_hex_dump(KERN_DEBUG, "BRA RX DATA:", DUMP_PREFIX_OFFSET, 8, 4, + rx_buf, rx_buf_size, false); + + /* Allocate temporary buffer in case of read request */ + if (!block->cmd) { + tmp_buf = kzalloc(block->num_bytes, GFP_KERNEL); + if (!tmp_buf) { + ret = -ENOMEM; + goto error; + } + } + + /* + * TODO: From the response header and footer there is no mention of + * read or write packet so controller needs to keep transmit packet + * information in order to verify rx packet. Also the current + * approach used for error mechanism is any of the packet response + * is not success, just report the whole transfer failed to Slave. + */ + + /* + * Verification of response packet for one known + * hardcoded configuration. This needs to be extended + * once we have dynamic algorithm integrated. + */ + + /* 2 valid read response */ + for (i = 0; i < info->valid_packets; i++) { + + + pr_info("BRA: Verifying packet number:%d with rolling id:%d\n", + info->packet_info[i].packet_num, + rolling_id); + chk_footer = true; + ret = cnl_sdw_bra_verify_hdr(rx_buf, offset, &chk_footer, + rolling_id); + if (ret < 0) { + dev_err(&mstr->dev, "BRA: Header verification failed for packet number:%d\n", + info->packet_info[i].packet_num); + goto error; + } + + /* Increment offset for header response */ + offset = offset + SDW_BRA_HEADER_RESP_SIZE_PDI; + + if (!block->cmd) { + + /* Remove PDI padding for data */ + cnl_sdw_bra_remove_data_padding(&rx_buf[offset], + &tmp_buf[tmp_offset], + info->packet_info[i].num_data_bytes); + + /* Increment offset for consumed data */ + offset = offset + + (info->packet_info[i].num_data_bytes * 2); + + rx_crc_comp = sdw_bus_compute_crc8(&tmp_buf[tmp_offset], + info->packet_info[i].num_data_bytes); + + /* Match Data CRC */ + rx_crc_rvd = rx_buf[offset]; + if (rx_crc_comp != rx_crc_rvd) { + ret = -EINVAL; + dev_err(&mstr->dev, "BRA: Data CRC doesn't match for packet number:%d\n", + info->packet_info[i].packet_num); + goto error; + } + + /* Increment destination buffer with copied data */ + tmp_offset = tmp_offset + + info->packet_info[i].num_data_bytes; + + /* Increment offset for CRC */ + offset = offset + SDW_BRA_DATA_CRC_SIZE_PDI; + } + + if (chk_footer) { + ret = cnl_sdw_bra_verify_footer(rx_buf, offset); + if (ret < 0) { + ret = -EINVAL; + dev_err(&mstr->dev, "BRA: Footer verification failed for packet number:%d\n", + info->packet_info[i].packet_num); + goto error; + } + + } + + /* Increment offset for footer response */ + offset = offset + SDW_BRA_HEADER_RESP_SIZE_PDI; + + /* Increment rolling id for next packet */ + rolling_id++; + if (rolling_id > 0xF) + rolling_id = 0; + } + + /* + * No need to check for dummy responses from codec + * Assumption made here is that dummy packets are + * added in 1ms buffer only after valid packets. + */ + + /* Copy data to codec buffer in case of read request */ + if (!block->cmd) + memcpy(block->values, tmp_buf, block->num_bytes); + +error: + /* Free up temp buffer allocated in case of read request */ + if (!block->cmd) + kfree(tmp_buf); + + /* Free up buffer allocated in cnl_sdw_bra_data_ops */ + kfree(info->tx_ptr); + kfree(info->rx_ptr); + kfree(info->packet_info); + + return ret; +} + +static int cnl_sdw_bra_data_ops(struct sdw_master *mstr, + struct sdw_bra_block *block, struct bra_info *info) +{ + + struct sdw_bra_block tmp_block; + int i; + int tx_buf_size = 384, rx_buf_size = 1152; + u8 *tx_buf = NULL, *rx_buf = NULL; + int rolling_id = 0, total_bytes = 0, offset = 0, reg_offset = 0; + int dummy_read = 0x0000; + int ret; + + /* + * TODO: Run an algorithm here to identify the buffer size + * for TX and RX buffers + number of dummy packets (read + * or write) to be added for to align buffers. + */ + + info->tx_block_size = tx_buf_size; + info->tx_ptr = tx_buf = kzalloc(tx_buf_size, GFP_KERNEL); + if (!tx_buf) { + ret = -ENOMEM; + goto error; + } + + info->rx_block_size = rx_buf_size; + info->rx_ptr = rx_buf = kzalloc(rx_buf_size, GFP_KERNEL); + if (!rx_buf) { + ret = -ENOMEM; + goto error; + } + + /* Fill valid packets transferred per millisecond buffer */ + info->valid_packets = 2; + info->packet_info = kcalloc(info->valid_packets, + sizeof(*info->packet_info), + GFP_KERNEL); + if (!info->packet_info) { + ret = -ENOMEM; + goto error; + } + + /* + * Below code performs packet preparation for one known + * configuration. + * 1. 2 Valid Read request with 18 bytes each. + * 2. 22 dummy read packets with 18 bytes each. + */ + for (i = 0; i < info->valid_packets; i++) { + tmp_block.slave_addr = block->slave_addr; + tmp_block.cmd = block->cmd; /* Read Request */ + tmp_block.num_bytes = 18; + tmp_block.reg_offset = block->reg_offset + reg_offset; + tmp_block.values = NULL; + reg_offset += tmp_block.num_bytes; + + cnl_sdw_bra_prep_hdr(tx_buf, &tmp_block, rolling_id, offset); + /* Total Header size: Header + Header CRC size on PDI */ + offset += SDW_BRA_HEADER_TOTAL_SZ_PDI; + + if (block->cmd) { + /* + * PDI data preparation in case of write request + * Assumption made here is data size from codec will + * be always an even number. + */ + cnl_sdw_bra_prep_data(tx_buf, &tmp_block, + total_bytes, offset); + offset += tmp_block.num_bytes * 2; + + /* Data CRC */ + cnl_sdw_bra_prep_crc(tx_buf, &tmp_block, + total_bytes, offset); + offset += SDW_BRA_DATA_CRC_SIZE_PDI; + } + + total_bytes += tmp_block.num_bytes; + rolling_id++; + + /* Fill packet info data structure */ + info->packet_info[i].packet_num = i + 1; + info->packet_info[i].num_data_bytes = tmp_block.num_bytes; + } + + /* Prepare dummy packets */ + for (i = 0; i < 22; i++) { + tmp_block.slave_addr = block->slave_addr; + tmp_block.cmd = 0; /* Read request */ + tmp_block.num_bytes = 18; + tmp_block.reg_offset = dummy_read++; + tmp_block.values = NULL; + + cnl_sdw_bra_prep_hdr(tx_buf, &tmp_block, rolling_id, offset); + + /* Total Header size: RD header + RD header CRC size on PDI */ + offset += SDW_BRA_HEADER_TOTAL_SZ_PDI; + + total_bytes += tmp_block.num_bytes; + rolling_id++; + } + + /* TODO: Remove below hex dump print */ + print_hex_dump(KERN_DEBUG, "BRA PDI VALID TX DATA:", + DUMP_PREFIX_OFFSET, 8, 4, tx_buf, tx_buf_size, false); + + return 0; + +error: + kfree(info->tx_ptr); + kfree(info->rx_ptr); + kfree(info->packet_info); + + return ret; +} + static int cnl_sdw_xfer_bulk(struct sdw_master *mstr, struct sdw_bra_block *block) { - return 0; + struct cnl_sdw *sdw = sdw_master_get_platdata(mstr); + struct cnl_sdw_data *data = &sdw->data; + struct cnl_bra_operation *ops = data->bra_data->bra_ops; + struct bra_info info; + int ret; + + /* + * 1. PDI Configuration + * 2. Prepare BRA packets including CRC calculation. + * 3. Configure TX and RX DMA in one shot mode. + * 4. Configure TX and RX Pipeline. + * 5. Run TX and RX DMA. + * 6. Run TX and RX pipelines. + * 7. Wait on completion for RX buffer. + * 8. Match TX and RX buffer packets and check for errors. + */ + + /* Memset bra_info data structure */ + memset(&info, 0x0, sizeof(info)); + + /* Fill master number in bra info data structure */ + info.mstr_num = mstr->nr; + + /* PDI Configuration (ON) */ + cnl_sdw_bra_pdi_config(mstr, true); + + /* Prepare TX buffer */ + ret = cnl_sdw_bra_data_ops(mstr, block, &info); + if (ret < 0) { + dev_err(&mstr->dev, "BRA: Request packet(s) creation failed\n"); + goto out; + } + + /* Pipeline Setup (ON) */ + ret = ops->bra_platform_setup(data->bra_data->drv_data, true, &info); + if (ret < 0) { + dev_err(&mstr->dev, "BRA: Pipeline setup failed\n"); + goto out; + } + + /* Trigger START host DMA and pipeline */ + ret = ops->bra_platform_xfer(data->bra_data->drv_data, true, &info); + if (ret < 0) { + dev_err(&mstr->dev, "BRA: Pipeline start failed\n"); + goto out; + } + + /* Trigger STOP host DMA and pipeline */ + ret = ops->bra_platform_xfer(data->bra_data->drv_data, false, &info); + if (ret < 0) { + dev_err(&mstr->dev, "BRA: Pipeline stop failed\n"); + goto out; + } + + /* Pipeline Setup (OFF) */ + ret = ops->bra_platform_setup(data->bra_data->drv_data, false, &info); + if (ret < 0) { + dev_err(&mstr->dev, "BRA: Pipeline de-setup failed\n"); + goto out; + } + + /* Verify RX buffer */ + ret = cnl_sdw_bra_check_data(mstr, block, &info); + if (ret < 0) { + dev_err(&mstr->dev, "BRA: Response packet(s) incorrect\n"); + goto out; + } + + /* PDI Configuration (OFF) */ + cnl_sdw_bra_pdi_config(mstr, false); + +out: + return ret; } static int cnl_sdw_mon_handover(struct sdw_master *mstr, @@ -1179,7 +1991,7 @@ static int cnl_sdw_set_ssp_interval(struct sdw_master *mstr, } static int cnl_sdw_set_clock_freq(struct sdw_master *mstr, - int cur_clk_freq, int bank) + int cur_clk_div, int bank) { struct cnl_sdw *sdw = sdw_master_get_drvdata(mstr); struct cnl_sdw_data *data = &sdw->data; @@ -1189,11 +2001,7 @@ static int cnl_sdw_set_clock_freq(struct sdw_master *mstr, /* TODO: Retrieve divider value or get value directly from calling * function */ -#ifdef CONFIG_SND_SOC_SVFPGA - int divider = ((9600000 * 2/cur_clk_freq) - 1); -#else - int divider = ((9600000/cur_clk_freq) - 1); -#endif + int divider = (cur_clk_div - 1); if (bank) { mcp_clockctrl_offset = SDW_CNL_MCP_CLOCKCTRL1; @@ -1419,7 +2227,7 @@ static int cnl_sdw_probe(struct sdw_master *mstr, sdw_master_set_drvdata(mstr, sdw); init_completion(&sdw->tx_complete); mutex_init(&sdw->stream_lock); - ret = sdw_init(sdw); + ret = sdw_init(sdw, true); if (ret) { dev_err(&mstr->dev, "SoundWire controller init failed %d\n", data->inst_id); @@ -1466,8 +2274,6 @@ static int cnl_sdw_remove(struct sdw_master *mstr) #ifdef CONFIG_PM static int cnl_sdw_runtime_suspend(struct device *dev) { - enum sdw_clk_stop_mode clock_stop_mode; - int volatile mcp_stat; int mcp_control; int timeout = 0; @@ -1493,12 +2299,12 @@ static int cnl_sdw_runtime_suspend(struct device *dev) cnl_sdw_reg_writel(data->sdw_regs, SDW_CNL_MCP_CONTROL, mcp_control); /* Prepare all the slaves for clock stop */ - ret = sdw_prepare_for_clock_change(sdw->mstr, 1, &clock_stop_mode); + ret = sdw_master_prep_for_clk_stop(sdw->mstr); if (ret) return ret; /* Call bus function to broadcast the clock stop now */ - ret = sdw_stop_clock(sdw->mstr, clock_stop_mode); + ret = sdw_master_stop_clock(sdw->mstr); if (ret) return ret; /* Wait for clock to be stopped, we are waiting at max 1sec now */ @@ -1534,12 +2340,9 @@ static int cnl_sdw_runtime_suspend(struct device *dev) static int cnl_sdw_clock_stop_exit(struct cnl_sdw *sdw) { - u16 wake_en, wake_sts, ioctl; - int volatile mcp_control; - int timeout = 0; + u16 wake_en, wake_sts; + int ret; struct cnl_sdw_data *data = &sdw->data; - int ioctl_offset = SDW_CNL_IOCTL + (data->inst_id * - SDW_CNL_IOCTL_REG_OFFSET); /* Disable the wake up interrupt */ wake_en = cnl_sdw_reg_readw(data->sdw_shim, @@ -1557,41 +2360,10 @@ static int cnl_sdw_clock_stop_exit(struct cnl_sdw *sdw) wake_sts |= (0x1 << data->inst_id); cnl_sdw_reg_writew(data->sdw_shim, SDW_CNL_SNDWWAKESTS_REG_OFFSET, wake_sts); - - ioctl = cnl_sdw_reg_readw(data->sdw_shim, ioctl_offset); - ioctl |= CNL_IOCTL_DO_MASK << CNL_IOCTL_DO_SHIFT; - cnl_sdw_reg_writew(data->sdw_shim, ioctl_offset, ioctl); - ioctl |= CNL_IOCTL_DOE_MASK << CNL_IOCTL_DOE_SHIFT; - cnl_sdw_reg_writew(data->sdw_shim, ioctl_offset, ioctl); - /* Switch control back to master */ - sdw_switch_to_mip(sdw); - - mcp_control = cnl_sdw_reg_readl(data->sdw_regs, - SDW_CNL_MCP_CONTROL); - mcp_control &= ~(MCP_CONTROL_BLOCKWAKEUP_MASK << - MCP_CONTROL_BLOCKWAKEUP_SHIFT); - mcp_control |= (MCP_CONTROL_CLOCKSTOPCLEAR_MASK << - MCP_CONTROL_CLOCKSTOPCLEAR_SHIFT); - cnl_sdw_reg_writel(data->sdw_regs, SDW_CNL_MCP_CONTROL, mcp_control); - /* - * Wait for timeout to be clear to successful enabling of the clock - * We will wait for 1sec before giving up - */ - while (timeout != 10) { - mcp_control = cnl_sdw_reg_readl(data->sdw_regs, - SDW_CNL_MCP_CONTROL); - if ((mcp_control & (MCP_CONTROL_CLOCKSTOPCLEAR_MASK << - MCP_CONTROL_CLOCKSTOPCLEAR_SHIFT)) == 0) - break; - msleep(1000); - timeout++; - } - mcp_control = cnl_sdw_reg_readl(data->sdw_regs, - SDW_CNL_MCP_CONTROL); - if ((mcp_control & (MCP_CONTROL_CLOCKSTOPCLEAR_MASK << - MCP_CONTROL_CLOCKSTOPCLEAR_SHIFT)) != 0) { - dev_err(&sdw->mstr->dev, "Clop Stop Exit failed\n"); - return -EBUSY; + ret = sdw_init(sdw, false); + if (ret < 0) { + pr_err("sdw_init fail: %d\n", ret); + return ret; } dev_info(&sdw->mstr->dev, "Exit from clock stop successful\n"); @@ -1627,13 +2399,14 @@ static int cnl_sdw_runtime_resume(struct device *dev) dev_info(&mstr->dev, "Exit from clock stop successful\n"); /* Prepare all the slaves to comeout of clock stop */ - ret = sdw_prepare_for_clock_change(sdw->mstr, 0, NULL); + ret = sdw_mstr_deprep_after_clk_start(sdw->mstr); if (ret) return ret; return 0; } +#ifdef CONFIG_PM_SLEEP static int cnl_sdw_sleep_resume(struct device *dev) { return cnl_sdw_runtime_resume(dev); @@ -1642,7 +2415,15 @@ static int cnl_sdw_sleep_suspend(struct device *dev) { return cnl_sdw_runtime_suspend(dev); } -#endif +#else +#define cnl_sdw_sleep_suspend NULL +#define cnl_sdw_sleep_resume NULL +#endif /* CONFIG_PM_SLEEP */ +#else +#define cnl_sdw_runtime_suspend NULL +#define cnl_sdw_runtime_resume NULL +#endif /* CONFIG_PM */ + static const struct dev_pm_ops cnl_sdw_pm_ops = { SET_SYSTEM_SLEEP_PM_OPS(cnl_sdw_sleep_suspend, cnl_sdw_sleep_resume) diff --git a/drivers/sdw/sdw_cnl_priv.h b/drivers/sdw/sdw_cnl_priv.h index 8e9d68c2bc2c..504df88d681a 100644 --- a/drivers/sdw/sdw_cnl_priv.h +++ b/drivers/sdw/sdw_cnl_priv.h @@ -27,7 +27,14 @@ #define SDW_CNL_SLAVE_STATUS_BITS 4 #define SDW_CNL_CMD_WORD_LEN 4 #define SDW_CNL_DEFAULT_SSP_INTERVAL 0x18 +#define SDW_CNL_DEFAULT_CLK_DIVIDER 0 +#define SDW_CNL_DEFAULT_FRAME_SHAPE 0x30 + +#if IS_ENABLED(CONFIG_SND_SOC_INTEL_CNL_FPGA) #define SDW_CNL_DEFAULT_SYNC_PERIOD 0x257F +#else +#define SDW_CNL_DEFAULT_SYNC_PERIOD 0x176F +#endif #define SDW_CNL_PORT_REG_OFFSET 0x80 #define CNL_SDW_SCP_ADDR_REGS 0x2 @@ -70,6 +77,7 @@ #define MCP_CONTROL_CMDRST_MASK 0x1 #define MCP_CONTROL_SOFTRST_SHIFT 0x6 #define MCP_CONTROL_SOFTCTRLBUSRST_SHIFT 0x5 +#define MCP_CONTROL_HARDCTRLBUSRST_MASK 0x1 #define MCP_CONTROL_HARDCTRLBUSRST_SHIFT 0x4 #define MCP_CONTROL_CLOCKPAUSEREQ_SHIFT 0x3 #define MCP_CONTROL_CLOCKSTOPCLEAR_SHIFT 0x2 @@ -220,6 +228,8 @@ #define PDINCONFIG_CHANNEL_MASK_MASK 0xFF #define PDINCONFIG_PORT_NUMBER_SHIFT 0x0 #define PDINCONFIG_PORT_NUMBER_MASK 0x1F +#define PDINCONFIG_PORT_SOFT_RESET_SHIFT 0x18 +#define PDINCONFIG_PORT_SOFT_RESET 0x1F #define DPN_CONFIG_WL_SHIFT 0x8 #define DPN_CONFIG_WL_MASK 0x1F @@ -341,4 +351,34 @@ #define CNL_STRMZCFG_CHAN_SHIFT 16 #define CNL_STRMZCFG_CHAN_MASK 0xF +#define SDW_BRA_HEADER_SIZE_PDI 12 /* In bytes */ +#define SDW_BRA_HEADER_CRC_SIZE_PDI 4 /* In bytes */ +#define SDW_BRA_DATA_CRC_SIZE_PDI 4 /* In bytes */ +#define SDW_BRA_HEADER_RESP_SIZE_PDI 4 /* In bytes */ +#define SDW_BRA_FOOTER_RESP_SIZE_PDI 4 /* In bytes */ +#define SDW_BRA_PADDING_SZ_PDI 4 /* In bytes */ +#define SDW_BRA_HEADER_TOTAL_SZ_PDI 16 /* In bytes */ + +#define SDW_BRA_SOP_EOP_PDI_STRT_VALUE 0x4 +#define SDW_BRA_SOP_EOP_PDI_END_VALUE 0x2 +#define SDW_BRA_SOP_EOP_PDI_MASK 0x1F +#define SDW_BRA_SOP_EOP_PDI_SHIFT 5 + +#define SDW_BRA_STRM_ID_BLK_OUT 3 +#define SDW_BRA_STRM_ID_BLK_IN 4 + +#define SDW_BRA_PDI_TX_ID 0 +#define SDW_BRA_PDI_RX_ID 1 + +#define SDW_BRA_SOFT_RESET 0x1 +#define SDW_BRA_BULK_ENABLE 1 +#define SDW_BRA_BLK_EN_MASK 0xFFFEFFFF +#define SDW_BRA_BLK_EN_SHIFT 16 + +#define SDW_BRA_ROLLINGID_PDI_INDX 3 +#define SDW_BRA_ROLLINGID_PDI_MASK 0xF +#define SDW_BRA_ROLLINGID_PDI_SHIFT 0 + +#define SDW_PCM_STRM_START_INDEX 0x2 + #endif /* _LINUX_SDW_CNL_H */ diff --git a/drivers/sdw/sdw_priv.h b/drivers/sdw/sdw_priv.h index 42e948440481..fd060bfa74c4 100644 --- a/drivers/sdw/sdw_priv.h +++ b/drivers/sdw/sdw_priv.h @@ -34,16 +34,14 @@ #define SDW_STATE_INIT_STREAM_TAG 0x1 #define SDW_STATE_ALLOC_STREAM 0x2 #define SDW_STATE_CONFIG_STREAM 0x3 -#define SDW_STATE_COMPUTE_STREAM 0x4 -#define SDW_STATE_PREPARE_STREAM 0x5 -#define SDW_STATE_ENABLE_STREAM 0x6 -#define SDW_STATE_DISABLE_STREAM 0x7 -#define SDW_STATE_UNPREPARE_STREAM 0x8 -#define SDW_STATE_UNCOMPUTE_STREAM 0x9 -#define SDW_STATE_RELEASE_STREAM 0xa -#define SDW_STATE_FREE_STREAM 0xb -#define SDW_STATE_FREE_STREAM_TAG 0xc -#define SDW_STATE_ONLY_XPORT_STREAM 0xd +#define SDW_STATE_PREPARE_STREAM 0x4 +#define SDW_STATE_ENABLE_STREAM 0x5 +#define SDW_STATE_DISABLE_STREAM 0x6 +#define SDW_STATE_UNPREPARE_STREAM 0x7 +#define SDW_STATE_RELEASE_STREAM 0x8 +#define SDW_STATE_FREE_STREAM 0x9 +#define SDW_STATE_FREE_STREAM_TAG 0xA +#define SDW_STATE_ONLY_XPORT_STREAM 0xB #define SDW_STATE_INIT_RT 0x1 #define SDW_STATE_CONFIG_RT 0x2 @@ -53,6 +51,8 @@ #define SDW_STATE_UNPREPARE_RT 0x6 #define SDW_STATE_RELEASE_RT 0x7 +#define SDW_SLAVE_BDCAST_ADDR 15 + struct sdw_runtime; /* Defined in sdw.c, used by multiple files of module */ extern struct sdw_core sdw_core; @@ -76,11 +76,33 @@ enum sdw_clk_state { SDW_CLK_STATE_ON = 1, }; +enum sdw_update_bs_state { + SDW_UPDATE_BS_PRE, + SDW_UPDATE_BS_BNKSWTCH, + SDW_UPDATE_BS_POST, + SDW_UPDATE_BS_BNKSWTCH_WAIT, + SDW_UPDATE_BS_DIS_CHN, +}; + +enum sdw_port_en_state { + SDW_PORT_STATE_PREPARE, + SDW_PORT_STATE_ENABLE, + SDW_PORT_STATE_DISABLE, + SDW_PORT_STATE_UNPREPARE, +}; + struct port_chn_en_state { bool is_activate; bool is_bank_sw; }; +struct temp_elements { + int rate; + int full_bw; + int payload_bw; + int hwidth; +}; + struct sdw_stream_tag { int stream_tag; struct mutex stream_lock; @@ -153,6 +175,10 @@ struct sdw_mstr_runtime { unsigned int stream_bw; /* State of runtime structure */ int rt_state; + int hstart; + int hstop; + int block_offset; + int sub_block_offset; }; struct sdw_runtime { @@ -185,8 +211,10 @@ struct sdw_bus { unsigned int clk_state; unsigned int active_bank; unsigned int clk_freq; + unsigned int clk_div; /* Bus total Bandwidth. Initialize and reset to zero */ unsigned int bandwidth; + unsigned int stream_interval; /* Stream Interval */ unsigned int system_interval; /* Bus System Interval */ unsigned int frame_freq; unsigned int col; @@ -239,6 +267,8 @@ int sdw_bus_bw_init(void); int sdw_mstr_bw_init(struct sdw_bus *sdw_bs); int sdw_bus_calc_bw(struct sdw_stream_tag *stream_tag, bool enable); int sdw_bus_calc_bw_dis(struct sdw_stream_tag *stream_tag, bool unprepare); +int sdw_bus_bra_xport_config(struct sdw_bus *sdw_mstr_bs, + struct sdw_bra_block *block, bool enable); int sdw_chn_enable(void); void sdw_unlock_mstr(struct sdw_master *mstr); int sdw_trylock_mstr(struct sdw_master *mstr); diff --git a/drivers/sdw/sdw_utils.c b/drivers/sdw/sdw_utils.c new file mode 100644 index 000000000000..724323d01993 --- /dev/null +++ b/drivers/sdw/sdw_utils.c @@ -0,0 +1,49 @@ +/* + * sdw_bwcalc.c - SoundWire Bus BW calculation & CHN Enabling implementation + * + * Copyright (C) 2015-2016 Intel Corp + * Author: Sanyog Kale + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * 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; version 2 of the License. + * + * 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. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ + +#include +#include +#include +#include + + + +/** + * sdw_bus_compute_crc8: SoundWire bus helper function to compute crc8. + * This API uses crc8 helper functions internally. + * + * @values: Data buffer. + * @num_bytes: Number of bytes. + */ +u8 sdw_bus_compute_crc8(u8 *values, u8 num_bytes) +{ + u8 table[256]; + u8 poly = 0x4D; /* polynomial = x^8 + x^6 + x^3 + x^2 + 1 */ + u8 crc = CRC8_INIT_VALUE; /* Initialize 8 bit to 11111111 */ + + /* Populate MSB */ + crc8_populate_msb(table, poly); + + /* CRC computation */ + crc = crc8(table, values, num_bytes, crc); + + return crc; +} +EXPORT_SYMBOL(sdw_bus_compute_crc8); diff --git a/include/linux/sdw/sdw_cnl.h b/include/linux/sdw/sdw_cnl.h index acf223cba595..6a9281c458b7 100644 --- a/include/linux/sdw/sdw_cnl.h +++ b/include/linux/sdw/sdw_cnl.h @@ -73,6 +73,33 @@ struct cnl_sdw_port { struct cnl_sdw_pdi_stream *pdi_stream; }; +struct bra_packet_info { + u8 packet_num; + u8 num_data_bytes; +}; + +struct bra_info { + unsigned int mstr_num; + u8 *tx_ptr; + u8 *rx_ptr; + unsigned int tx_block_size; + unsigned int rx_block_size; + u8 valid_packets; + struct bra_packet_info *packet_info; +}; + +struct cnl_bra_operation { + int (*bra_platform_setup)(void *context, bool is_enable, + struct bra_info *info); + int (*bra_platform_xfer)(void *context, bool is_enable, + struct bra_info *info); +}; + +struct cnl_sdw_bra_cfg { + void *drv_data; + struct cnl_bra_operation *bra_ops; +}; + struct cnl_sdw_data { /* SoundWire IP registers per instance */ void __iomem *sdw_regs; @@ -84,6 +111,8 @@ struct cnl_sdw_data { int irq; /* Instance id */ int inst_id; + /* BRA data pointer */ + struct cnl_sdw_bra_cfg *bra_data; }; struct cnl_sdw_port *cnl_sdw_alloc_port(struct sdw_master *mstr, int ch_count, diff --git a/include/linux/sdw/sdw_registers.h b/include/linux/sdw/sdw_registers.h index 1abdf4bf863a..2e831c0e93cf 100644 --- a/include/linux/sdw/sdw_registers.h +++ b/include/linux/sdw/sdw_registers.h @@ -68,13 +68,17 @@ #define SDW_SCP_INTSTAT_1 0x40 #define SDW_SCP_INTSTAT1_PARITY_MASK 0x1 #define SDW_SCP_INTSTAT1_BUS_CLASH_MASK 0x2 +#define SDW_SCP_INTSTAT1_IMPL_DEF_MASK 0x4 #define SDW_SCP_INTSTAT1_SCP2_CASCADE_MASK 0x80 #define SDW_SCP_INTCLEAR1 0x40 #define SDW_SCP_INTCLEAR1_PARITY_MASK 0x1 #define SDW_SCP_INTCLEAR1_BUS_CLASH_MASK 0x2 +#define SDW_SCP_INTCLEAR1_IMPL_DEF_MASK 0x4 #define SDW_SCP_INTCLEAR1_SCP2_CASCADE_MASK 0x80 -#define SDW_SCP_INTMASK1 +#define SDW_SCP_INTMASK1 0x41 +#define SDW_SCP_INTMASK1_PARITY_MASK 0x1 +#define SDW_SCP_INTMASK1_BUS_CLASH_MASK 0x2 #define SDW_SCP_INTSTAT2 0x42 #define SDW_SCP_INTSTAT2_SCP3_CASCADE_MASK 0x80 #define SDW_SCP_INTSTAT3 0x43 @@ -84,6 +88,7 @@ #define SDW_SCP_STAT 0x44 #define SDW_SCP_STAT_CLK_STP_NF_MASK 0x1 #define SDW_SCP_SYSTEMCTRL 0x45 +#define SDW_SCP_SYSTEMCTRL_CLK_STP_PREP_MASK 0x1 #define SDW_SCP_SYSTEMCTRL_CLK_STP_PREP_SHIFT 0x0 #define SDW_SCP_SYSTEMCTRL_CLK_STP_MODE_SHIFT 0x1 #define SDW_SCP_SYSTEMCTRL_WAKE_UP_EN_SHIFT 0x2 diff --git a/include/linux/sdw_bus.h b/include/linux/sdw_bus.h index d16579b35f8a..ed075fbd9a99 100644 --- a/include/linux/sdw_bus.h +++ b/include/linux/sdw_bus.h @@ -60,6 +60,66 @@ #define SDW_PORT_ENCODING_TYPE_SIGN_MAGNITUDE 0x2 #define SDW_PORT_ENCODING_TYPE_IEEE_32_FLOAT 0x4 +#define SDW_BRA_PORT_ID 0 +#define SDW_BRA_CHN_MASK 0x1 + +#define SDW_BRA_HEADER_SIZE 6 /* In bytes */ +#define SDW_BRA_HEADER_CRC_SIZE 1 /* In bytes */ +#define SDW_BRA_DATA_CRC_SIZE 1 /* In bytes */ +#define SDW_BRA_HEADER_RESP_SIZE 1 /* In bytes */ +#define SDW_BRA_FOOTER_RESP_SIZE 1 /* In bytes */ +#define SDW_BRA_PADDING_SZ 1 /* In bytes */ +#define SDW_BRA_HEADER_TOTAL_SZ 8 /* In bytes */ + +#define SDW_BRA_BPT_PAYLOAD_TYPE 0x0 +#define SDW_BRA_BPT_PYLD_TY_MASK 0xFF3FFFFF +#define SDW_BRA_BPT_PYLD_TY_SHIFT 22 + +#define SDW_BRA_HDR_ACTIVE 0x3 +#define SDW_BRA_HDR_ACTIVE_SHIFT 6 +#define SDW_BRA_HDR_ACTIVE_MASK 0x3F + +#define SDW_BRA_HDR_SLV_ADDR_SHIFT 2 +#define SDW_BRA_HDR_SLV_ADDR_MASK 0xC3 + +#define SDW_BRA_HDR_RD_WR_SHIFT 1 +#define SDW_BRA_HDR_RD_WR_MASK 0xFD + +#define SDW_BRA_HDR_MSB_BYTE_SET 1 +#define SDW_BRA_HDR_MSB_BYTE_UNSET 0 +#define SDW_BRA_HDR_MSB_BYTE_CHK 255 +#define SDW_BRA_HDR_MSB_BYTE_MASK 0xFE +#define SDW_BRA_HDR_MSB_BYTE_SHIFT 0 + +#define SDW_BRA_HDR_SLV_REG_OFF_SHIFT0 0 +#define SDW_BRA_HDR_SLV_REG_OFF_MASK0 0xFF +#define SDW_BRA_HDR_SLV_REG_OFF_SHIFT8 8 +#define SDW_BRA_HDR_SLV_REG_OFF_MASK8 0xFF00 +#define SDW_BRA_HDR_SLV_REG_OFF_SHIFT16 16 +#define SDW_BRA_HDR_SLV_REG_OFF_MASK16 0xFF0000 +#define SDW_BRA_HDR_SLV_REG_OFF_SHIFT24 24 +#define SDW_BRA_HDR_SLV_REG_OFF_MASK24 0xFF000000 + +#define SDW_BRA_HDR_RESP_ACK_SHIFT 3 +#define SDW_BRA_HDR_RESP_NRDY_SHIFT 5 +#define SDW_BRA_FTR_RESP_ACK_SHIFT 3 +#define SDW_BRA_FTR_RESP_RES_SHIFT 5 +#define SDW_BRA_HDR_RESP_ACK_MASK 0x3 +#define SDW_BRA_HDR_RESP_NRDY_MASK 0x1 +#define SDW_BRA_FTR_RESP_ACK_MASK 0x3 +#define SDW_BRA_FTR_RESP_RES_MASK 0x1 + +#define SDW_BRA_TARGET_READY 0 +#define SDW_BRA_TARGET_NOT_READY 1 + +#define SDW_BRA_ACK_NAK_IGNORED 0 +#define SDW_BRA_ACK_NAK_OK 1 +#define SDW_BRA_ACK_NAK_FAILED_ABORT 2 +#define SDW_BRA_ACK_NAK_RSVD_ABORT 3 + +#define SDW_BRA_FTR_RESULT_GOOD 0 +#define SDW_BRA_FTR_RESULT_BAD 1 + /* enum sdw_driver_type: There are different driver callbacks for slave and * master. This is to differentiate between slave driver * and master driver. Bus driver binds master driver to @@ -420,6 +480,14 @@ struct sdw_slv_dp0_capabilities { * the Port15 alias * 0: Command_Ignored * 1: Command_OK, Data is OR of all registers + * @scp_impl_def_intr_mask: Implementation defined interrupt for Slave control + * port + * @clk_stp1_deprep_required: De-prepare is required after exiting the clock + * stop mode 1. Noramlly exit from clock stop 1 is like + * hard reset, so de-prepare shouldn't be required but + * some Slave requires de-prepare after exiting from + * clock stop 1. Mark as true if Slave requires + * deprepare after exiting from clock stop mode 1. * @sdw_dp0_supported: DP0 is supported by Slave. * @sdw_dp0_cap: Data Port 0 Capabilities of the Slave. * @num_of_sdw_ports: Number of SoundWire Data ports present. The representation @@ -436,6 +504,8 @@ struct sdw_slv_capabilities { bool paging_supported; bool bank_delay_support; unsigned int port_15_read_behavior; + u8 scp_impl_def_intr_mask; + bool clk_stp1_deprep_required; bool sdw_dp0_supported; struct sdw_slv_dp0_capabilities *sdw_dp0_cap; int num_of_sdw_ports; @@ -494,6 +564,40 @@ struct sdw_bus_params { int bank; }; +/** struct sdw_portn_intr_stat: Implementation defined interrupt + * status for slave ports other than port 0 + * + * num: Port number for which status is reported. + * status: status of the implementation defined interrupts + */ +struct sdw_portn_intr_stat { + int num; + u8 status; +}; + +/** struct sdw_impl_def_intr_stat: Implementation define interrupt + * status for slave. + * + * control_port_stat: Implementation defined interrupt status mask + * for control ports. Mask Bits are exactly + * same as defined in MIPI Spec 1.0 + * port0_stat: Implementation defined interrupt status mask + * for port 0. Mask bits are exactly same as defined + * in MIPI spec 1.0. + * num_ports: Number of ports in slave other than port 0. + * portn_stat: Implementation defined status for slave ports + * other than port0. Mask bits are exactly same + * as defined in MIPI spec 1.0. Array size is + * same as number of ports in Slave. + */ +struct sdw_impl_def_intr_stat { + u8 control_port_stat; + u8 port0_stat; + int num_ports; + struct sdw_portn_intr_stat *portn_stat; +}; + + /** * struct sdw_slave_driver: Manage SoundWire generic/Slave device driver * @driver_type: To distinguish between master and slave driver. Set and @@ -580,6 +684,13 @@ struct sdw_slave_driver { int port, int ch_mask, int bank); int (*handle_post_port_unprepare)(struct sdw_slv *swdev, int port, int ch_mask, int bank); + int (*pre_clk_stop_prep)(struct sdw_slv *sdwdev, + enum sdw_clk_stop_mode mode, bool stop); + int (*post_clk_stop_prep)(struct sdw_slv *sdwdev, + enum sdw_clk_stop_mode mode, bool stop); + enum sdw_clk_stop_mode (*get_dyn_clk_stp_mod)(struct sdw_slv *swdev); + void (*update_slv_status)(struct sdw_slv *swdev, + enum sdw_slave_status *status); const struct sdw_slv_id *id_table; }; #define to_sdw_slave_driver(d) container_of(d, struct sdw_slave_driver, driver) @@ -1298,6 +1409,15 @@ struct sdw_master *sdw_get_master(int nr); */ void sdw_put_master(struct sdw_master *mstr); +/** + * sdw_slave_xfer_bra_block: Transfer the data block using the BTP/BRA + * protocol. + * @mstr: SoundWire Master Master + * @block: Data block to be transferred. + */ +int sdw_slave_xfer_bra_block(struct sdw_master *mstr, + struct sdw_bra_block *block); + /** * module_sdw_slave_driver() - Helper macro for registering a sdw Slave driver @@ -1311,19 +1431,50 @@ void sdw_put_master(struct sdw_master *mstr); module_driver(__sdw_slave_driver, sdw_slave_driver_register, \ sdw_slave_driver_unregister) /** - * sdw_prepare_for_clock_change: Prepare all the Slaves for clock stop or - * clock start. Prepares Slaves based on what they support - * simplified clock stop or normal clock stop based on - * their capabilities registered to slave driver. + * sdw_master_prep_for_clk_stop: Prepare all the Slaves for clock stop. + * Iterate through each of the enumerated Slave. + * Prepare each Slave according to the clock stop + * mode supported by Slave. Use dynamic value from + * Slave callback if registered, else use static values + * from Slave capabilities registered. + * 1. Get clock stop mode for each Slave. + * 2. Call pre_prepare callback of each Slave if + * registered. + * 3. Prepare each Slave for clock stop + * 4. Broadcast the Read message to make sure + * all Slaves are prepared for clock stop. + * 5. Call post_prepare callback of each Slave if + * registered. + * * @mstr: Master handle for which clock state has to be changed. - * @start: Prepare for starting or stopping the clock - * @clk_stop_mode: Bus used which clock mode, if bus finds all the Slaves - * on the bus to be supported clock stop mode1 it prepares - * all the Slaves for mode1 else it will prepare all the - * Slaves for mode0. + * + * Returns 0 + */ +int sdw_master_prep_for_clk_stop(struct sdw_master *mstr); + +/** + * sdw_mstr_deprep_after_clk_start: De-prepare all the Slaves + * exiting clock stop mode 0 after clock resumes. Clock + * is already resumed before this. De-prepare all the Slaves + * which were earlier in ClockStop mode0. De-prepare for the + * Slaves which were there in ClockStop mode1 is done after + * they enumerated back. Its not done here as part of master + * getting resumed. + * 1. Get clock stop mode for each Slave its exiting from + * 2. Call pre_prepare callback of each Slave exiting from + * clock stop mode 0. + * 3. De-Prepare each Slave exiting from Clock Stop mode0 + * 4. Broadcast the Read message to make sure + * all Slaves are de-prepared for clock stop. + * 5. Call post_prepare callback of each Slave exiting from + * clock stop mode0 + * + * + * @mstr: Master handle + * + * Returns 0 */ -int sdw_prepare_for_clock_change(struct sdw_master *mstr, bool start, - enum sdw_clk_stop_mode *clck_stop_mode); +int sdw_mstr_deprep_after_clk_start(struct sdw_master *mstr); /** * sdw_wait_for_slave_enumeration: Wait till all the slaves are enumerated. @@ -1341,13 +1492,14 @@ int sdw_wait_for_slave_enumeration(struct sdw_master *mstr, struct sdw_slv *slave); /** - * sdw_stop_clock: Stop the clock. This function broadcasts the SCP_CTRL + * sdw_master_stop_clock: Stop the clock. This function broadcasts the SCP_CTRL * register with clock_stop_now bit set. + * * @mstr: Master handle for which clock has to be stopped. - * @clk_stop_mode: Bus used which clock mode. + * + * Returns 0 on success, appropriate error code on failure. */ - -int sdw_stop_clock(struct sdw_master *mstr, enum sdw_clk_stop_mode mode); +int sdw_master_stop_clock(struct sdw_master *mstr); /* Return the adapter number for a specific adapter */ static inline int sdw_master_id(struct sdw_master *mstr) @@ -1377,4 +1529,29 @@ static inline void sdw_slave_set_drvdata(struct sdw_slv *slv, dev_set_drvdata(&slv->dev, data); } +static inline void *sdw_master_get_platdata(const struct sdw_master *mstr) +{ + return dev_get_platdata(&mstr->dev); +} + +/** + * sdw_slave_get_bus_params: Get the current bus params. Some Slaves + * requires bus params at the probe to program its + * registers based on bus params. This API provides + * current bus params + * + * @sdw_slv: Slave handle + * @params: Bus params + */ +int sdw_slave_get_bus_params(struct sdw_slv *sdw_slv, + struct sdw_bus_params *params); +/** + * sdw_bus_compute_crc8: SoundWire bus helper function to compute crc8. + * This API uses crc8 helper functions internally. + * + * @values: Data buffer. + * @num_bytes: Number of bytes. + */ +u8 sdw_bus_compute_crc8(u8 *values, u8 num_bytes); + #endif /* _LINUX_SDW_BUS_H */ diff --git a/sound/soc/codecs/svfpga-sdw.c b/sound/soc/codecs/svfpga-sdw.c index 1fa06ef82ab2..dec412af6d5c 100644 --- a/sound/soc/codecs/svfpga-sdw.c +++ b/sound/soc/codecs/svfpga-sdw.c @@ -79,7 +79,7 @@ static int svfpga_register_sdw_capabilties(struct sdw_slv *sdw, dpn_cap->num_audio_modes), GFP_KERNEL); for (j = 0; j < dpn_cap->num_audio_modes; j++) { prop = &dpn_cap->mode_properties[j]; - prop->max_frequency = 16000000; + prop->max_frequency = 19200000; prop->min_frequency = 1000000; prop->num_freq_configs = 0; prop->freq_supported = NULL; diff --git a/sound/soc/intel/boards/cnl_svfpga.c b/sound/soc/intel/boards/cnl_svfpga.c index 312c0437ac15..23c1f6437cda 100644 --- a/sound/soc/intel/boards/cnl_svfpga.c +++ b/sound/soc/intel/boards/cnl_svfpga.c @@ -125,7 +125,7 @@ static int cnl_svfpga_codec_fixup(struct snd_soc_pcm_runtime *rtd, pr_debug("Invoked %s for dailink %s\n", __func__, rtd->dai_link->name); slot_width = 24; rate->min = rate->max = 48000; - channels->min = channels->max = 1; + channels->min = channels->max = 2; snd_mask_none(hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT)); snd_mask_set(hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT), SNDRV_PCM_FORMAT_S16_LE); diff --git a/sound/soc/intel/skylake/cnl-sst.c b/sound/soc/intel/skylake/cnl-sst.c index b82de714c916..d888d31d0ea2 100644 --- a/sound/soc/intel/skylake/cnl-sst.c +++ b/sound/soc/intel/skylake/cnl-sst.c @@ -516,7 +516,7 @@ static int skl_register_sdw_masters(struct device *dev, struct skl_sst *dsp, struct sdw_mstr_dpn_capabilities *dpn_cap; struct sdw_master *master; struct cnl_sdw_data *p_data; - int ret = 0, i, j; + int ret = 0, i, j, k, wl = 0; /* TODO: This number 4 should come from ACPI */ #if defined(CONFIG_SDW_MAXIM_SLAVE) || defined(CONFIG_SND_SOC_MXFPGA) dsp->num_sdw_controllers = 3; @@ -561,14 +561,20 @@ static int skl_register_sdw_masters(struct device *dev, struct skl_sst *dsp, if (!m_cap->sdw_dpn_cap) return -ENOMEM; for (j = 0; j < m_cap->num_data_ports; j++) { - dpn_cap = &m_cap->sdw_dpn_cap[i]; + dpn_cap = &m_cap->sdw_dpn_cap[j]; /* Both Tx and Rx */ dpn_cap->port_direction = 0x3; - dpn_cap->port_number = i; + dpn_cap->port_number = j; dpn_cap->max_word_length = 32; dpn_cap->min_word_length = 1; - dpn_cap->num_word_length = 0; - dpn_cap->word_length_buffer = NULL; + dpn_cap->num_word_length = 4; + + dpn_cap->word_length_buffer = + kzalloc(((sizeof(unsigned int)) * + dpn_cap->num_word_length), GFP_KERNEL); + for (k = 0; k < dpn_cap->num_word_length; k++) + dpn_cap->word_length_buffer[k] = wl = wl + 8; + wl = 0; dpn_cap->dpn_type = SDW_FULL_DP; dpn_cap->min_ch_num = 1; dpn_cap->max_ch_num = 8; diff --git a/sound/soc/intel/skylake/skl-sdw-pcm.c b/sound/soc/intel/skylake/skl-sdw-pcm.c index ea9a3e14434a..564602c0ee12 100644 --- a/sound/soc/intel/skylake/skl-sdw-pcm.c +++ b/sound/soc/intel/skylake/skl-sdw-pcm.c @@ -39,7 +39,8 @@ struct sdw_dma_data { int stream_tag; - struct cnl_sdw_port *port; + int nr_ports; + struct cnl_sdw_port **port; struct sdw_master *mstr; enum cnl_sdw_pdi_stream_type stream_type; int stream_state; @@ -133,11 +134,11 @@ int cnl_sdw_hw_params(struct snd_pcm_substream *substream, enum sdw_data_direction direction; struct sdw_stream_config stream_config; struct sdw_port_config port_config; - struct sdw_port_cfg port_cfg; + struct sdw_port_cfg *port_cfg; int ret = 0; struct skl_pipe_params p_params = {0}; struct skl_module_cfg *m_cfg; - int upscale_factor = 16; + int i, upscale_factor = 16; p_params.s_fmt = snd_pcm_format_width(params_format(params)); p_params.ch = params_channels(params); @@ -155,13 +156,26 @@ int cnl_sdw_hw_params(struct snd_pcm_substream *substream, direction = SDW_DATA_DIR_IN; else direction = SDW_DATA_DIR_OUT; - /* Dynamically alloc port and PDI streams for this DAI */ - dma->port = cnl_sdw_alloc_port(dma->mstr, channels, + if (dma->stream_type == CNL_SDW_PDI_TYPE_PDM) + dma->nr_ports = channels; + else + dma->nr_ports = 1; + + dma->port = kcalloc(dma->nr_ports, sizeof(struct cnl_sdw_port), + GFP_KERNEL); + if (!dma->port) + return -ENOMEM; + + for (i = 0; i < dma->nr_ports; i++) { + /* Dynamically alloc port and PDI streams for this DAI */ + dma->port[i] = cnl_sdw_alloc_port(dma->mstr, channels, direction, dma->stream_type); - if (!dma->port) { - dev_err(dai->dev, "Unable to allocate port\n"); - return -EINVAL; + if (!dma->port[i]) { + dev_err(dai->dev, "Unable to allocate port\n"); + return -EINVAL; + } } + dma->stream_state = STREAM_STATE_ALLOC_STREAM; m_cfg = skl_tplg_be_get_cpr_module(dai, substream->stream); if (!m_cfg) { @@ -170,10 +184,10 @@ int cnl_sdw_hw_params(struct snd_pcm_substream *substream, } if (!m_cfg->sdw_agg_enable) - m_cfg->sdw_stream_num = dma->port->pdi_stream->sdw_pdi_num; + m_cfg->sdw_stream_num = dma->port[0]->pdi_stream->sdw_pdi_num; else m_cfg->sdw_agg.agg_data[dma->mstr_nr].alh_stream_num = - dma->port->pdi_stream->sdw_pdi_num; + dma->port[0]->pdi_stream->sdw_pdi_num; ret = skl_tplg_be_update_params(dai, &p_params); if (ret) return ret; @@ -202,10 +216,23 @@ int cnl_sdw_hw_params(struct snd_pcm_substream *substream, dev_err(dai->dev, "Unable to configure the stream\n"); return ret; } - port_config.num_ports = 1; - port_config.port_cfg = &port_cfg; - port_cfg.port_num = dma->port->port_num; - port_cfg.ch_mask = ((1 << channels) - 1); + port_cfg = kcalloc(dma->nr_ports, sizeof(struct sdw_port_cfg), + GFP_KERNEL); + if (!port_cfg) + return -ENOMEM; + + port_config.num_ports = dma->nr_ports; + port_config.port_cfg = port_cfg; + + for (i = 0; i < dma->nr_ports; i++) { + port_cfg[i].port_num = dma->port[i]->port_num; + + if (dma->stream_type == CNL_SDW_PDI_TYPE_PDM) + port_cfg[i].ch_mask = 0x1; + else + port_cfg[i].ch_mask = ((1 << channels) - 1); + } + ret = sdw_config_port(dma->mstr, NULL, &port_config, dma->stream_tag); if (ret) { dev_err(dai->dev, "Unable to configure port\n"); @@ -219,7 +246,7 @@ int cnl_sdw_hw_free(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { struct sdw_dma_data *dma; - int ret = 0; + int ret = 0, i; dma = snd_soc_dai_get_dma_data(dai, substream); @@ -228,16 +255,20 @@ int cnl_sdw_hw_free(struct snd_pcm_substream *substream, if (ret) dev_err(dai->dev, "Unable to release stream\n"); dma->stream_state = STREAM_STATE_RELEASE_STREAM; - if (dma->port && dma->stream_state == + for (i = 0; i < dma->nr_ports; i++) { + if (dma->port[i] && dma->stream_state == STREAM_STATE_RELEASE_STREAM) { - /* Even if release fails, we continue, - * while winding up we have - * to continue till last one gets winded up - */ - cnl_sdw_free_port(dma->mstr, dma->port->port_num); - dma->stream_state = STREAM_STATE_FREE_STREAM; - dma->port = NULL; + /* Even if release fails, we continue, + * while winding up we have + * to continue till last one gets winded up + */ + cnl_sdw_free_port(dma->mstr, + dma->port[i]->port_num); + dma->port[i] = NULL; + } } + + dma->stream_state = STREAM_STATE_FREE_STREAM; } return 0; } From 80984a286f79656c41edcc0491bae7befd09b7ef Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Tue, 17 May 2016 14:00:15 +0530 Subject: [PATCH 0680/1103] ALSA: core: let low-level driver or userspace disable rewinds Add new hw_params flag to explicitly tell driver that rewinds will never be used. This can be used by low-level driver to optimize DMA operations and reduce power consumption. Use this flag only when data written in ring buffer will never be invalidated, e.g. any update of appl_ptr is final. Note that the update of appl_ptr include both a read/write data operation as well as snd_pcm_forward() whose behavior is not modified. Signed-off-by: Pierre-Louis Bossart Signed-off-by: Ramesh Babu Signed-off-by: Subhransu S. Prusty --- include/sound/pcm.h | 1 + include/uapi/sound/asound.h | 1 + sound/core/pcm_native.c | 6 ++++++ 3 files changed, 8 insertions(+) diff --git a/include/sound/pcm.h b/include/sound/pcm.h index d6bd3caf6878..2eef65a9404e 100644 --- a/include/sound/pcm.h +++ b/include/sound/pcm.h @@ -379,6 +379,7 @@ struct snd_pcm_runtime { unsigned int rate_num; unsigned int rate_den; unsigned int no_period_wakeup: 1; + unsigned int no_rewinds:1; /* -- SW params -- */ int tstamp_mode; /* mmap timestamp is updated */ diff --git a/include/uapi/sound/asound.h b/include/uapi/sound/asound.h index ed0a120d4f08..ff57e4c89de4 100644 --- a/include/uapi/sound/asound.h +++ b/include/uapi/sound/asound.h @@ -377,6 +377,7 @@ typedef int snd_pcm_hw_param_t; #define SNDRV_PCM_HW_PARAMS_NORESAMPLE (1<<0) /* avoid rate resampling */ #define SNDRV_PCM_HW_PARAMS_EXPORT_BUFFER (1<<1) /* export buffer */ #define SNDRV_PCM_HW_PARAMS_NO_PERIOD_WAKEUP (1<<2) /* disable period wakeups */ +#define SNDRV_PCM_HW_PARAMS_NO_REWINDS (1<<3) /* disable rewinds */ struct snd_interval { unsigned int min, max; diff --git a/sound/core/pcm_native.c b/sound/core/pcm_native.c index 66c90f486af9..41beeb825240 100644 --- a/sound/core/pcm_native.c +++ b/sound/core/pcm_native.c @@ -726,6 +726,8 @@ static int snd_pcm_hw_params(struct snd_pcm_substream *substream, runtime->no_period_wakeup = (params->info & SNDRV_PCM_INFO_NO_PERIOD_WAKEUP) && (params->flags & SNDRV_PCM_HW_PARAMS_NO_PERIOD_WAKEUP); + runtime->no_rewinds = + (params->flags & SNDRV_PCM_HW_PARAMS_NO_REWINDS) ? 1 : 0; bits = snd_pcm_format_physical_width(runtime->format); runtime->sample_bits = bits; @@ -2642,11 +2644,15 @@ static snd_pcm_sframes_t rewind_appl_ptr(struct snd_pcm_substream *substream, static snd_pcm_sframes_t snd_pcm_rewind(struct snd_pcm_substream *substream, snd_pcm_uframes_t frames) { + struct snd_pcm_runtime *runtime = substream->runtime; snd_pcm_sframes_t ret; if (frames == 0) return 0; + if (runtime->no_rewinds) + return 0; + snd_pcm_stream_lock_irq(substream); ret = do_pcm_hwsync(substream); if (!ret) From b7baeba0bb05739925803f9203477662d1a1cf25 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Thu, 16 Jun 2016 16:01:00 +0530 Subject: [PATCH 0681/1103] ALSA: pcm: conditionally avoid mmap of control data. In case of mmap, by default alsa-lib mmaps both control and status data. If driver subscribes for application pointer update, driver needs to get notification whenever appl ptr changes. With the above case driver won't get appl ptr notifications. This patch check on a hw info flag and returns error when user land asks for mmaping control & status data, thus forcing user to issue IOCTL_SYNC_PTR. Change-Id: I05b83f630812face322c474d9bbb6d56cbdc08fb Suggested-by: Takashi Iwai Signed-off-by: Pierre-Louis Bossart Signed-off-by: Ramesh Babu Signed-off-by: Jaikrishna Nemallapudi Signed-off-by: Subhransu S. Prusty Signed-off-by: Mallikarjun, chippalkatti Reviewed-on: Reviewed-by: audio_build Reviewed-by: Sm, Bhadur A Tested-by: Sm, Bhadur A --- include/uapi/sound/asound.h | 1 + sound/core/pcm_native.c | 17 +++++++++++++++++ 2 files changed, 18 insertions(+) diff --git a/include/uapi/sound/asound.h b/include/uapi/sound/asound.h index ff57e4c89de4..9120cce11265 100644 --- a/include/uapi/sound/asound.h +++ b/include/uapi/sound/asound.h @@ -296,6 +296,7 @@ typedef int __bitwise snd_pcm_subformat_t; #define SNDRV_PCM_INFO_HAS_LINK_ABSOLUTE_ATIME 0x02000000 /* report absolute hardware link audio time, not reset on startup */ #define SNDRV_PCM_INFO_HAS_LINK_ESTIMATED_ATIME 0x04000000 /* report estimated link audio time */ #define SNDRV_PCM_INFO_HAS_LINK_SYNCHRONIZED_ATIME 0x08000000 /* report synchronized audio/system time */ +#define SNDRV_PCM_INFO_NO_STATUS_MMAP 0x10000000 /* status and control mmap not supported */ #define SNDRV_PCM_INFO_DRAIN_TRIGGER 0x40000000 /* internal kernel flag - trigger in drain */ #define SNDRV_PCM_INFO_FIFO_IN_FRAMES 0x80000000 /* internal kernel flag - FIFO size is in frames */ diff --git a/sound/core/pcm_native.c b/sound/core/pcm_native.c index 41beeb825240..6ba593bc673d 100644 --- a/sound/core/pcm_native.c +++ b/sound/core/pcm_native.c @@ -3460,21 +3460,38 @@ static int snd_pcm_mmap(struct file *file, struct vm_area_struct *area) struct snd_pcm_file * pcm_file; struct snd_pcm_substream *substream; unsigned long offset; + unsigned int info; pcm_file = file->private_data; substream = pcm_file->substream; if (PCM_RUNTIME_CHECK(substream)) return -ENXIO; + info = substream->runtime->hw.info; offset = area->vm_pgoff << PAGE_SHIFT; switch (offset) { case SNDRV_PCM_MMAP_OFFSET_STATUS: if (!pcm_status_mmap_allowed(pcm_file)) return -ENXIO; + /* + * force fallback to ioctl if driver doesn't support status + * and control mmap. + */ + if (info & SNDRV_PCM_INFO_NO_STATUS_MMAP) + return -ENXIO; + return snd_pcm_mmap_status(substream, file, area); case SNDRV_PCM_MMAP_OFFSET_CONTROL: if (!pcm_control_mmap_allowed(pcm_file)) return -ENXIO; + + /* + * force fallback to ioctl if driver doesn't support status + * and control mmap. + */ + if (info & SNDRV_PCM_INFO_NO_STATUS_MMAP) + return -ENXIO; + return snd_pcm_mmap_control(substream, file, area); default: return snd_pcm_mmap_data(substream, file, area); From a59d26f1988e4b1cc5074673b7ab3ad25b13723a Mon Sep 17 00:00:00 2001 From: Ramesh Babu Date: Mon, 6 Jun 2016 13:13:15 +0530 Subject: [PATCH 0682/1103] ALSA: hda: ext: add spib to stream context. Platforms like skylake support SPIB (software position index in Buffer) capability, through which application pointer can be programmed in DMA. This helps DMA stop rendering stale data. This patch saves spib values in stream context which can be restored during resume from S3. Change-Id: I2992242087ee0732b6fc571b5e65eb59aa1fa251 Signed-off-by: Ramesh Babu Signed-off-by: Subhransu S. Prusty Signed-off-by: Mallikarjun, chippalkatti Reviewed-on: Reviewed-by: audio_build Reviewed-by: Sm, Bhadur A Tested-by: Sm, Bhadur A --- include/sound/hdaudio_ext.h | 1 + sound/hda/ext/hdac_ext_stream.c | 2 ++ 2 files changed, 3 insertions(+) diff --git a/include/sound/hdaudio_ext.h b/include/sound/hdaudio_ext.h index 2d93a039a286..62181677f009 100644 --- a/include/sound/hdaudio_ext.h +++ b/include/sound/hdaudio_ext.h @@ -67,6 +67,7 @@ struct hdac_ext_stream { u32 dpib; u32 lpib; + u32 spib; bool decoupled:1; bool link_locked:1; bool link_prepared; diff --git a/sound/hda/ext/hdac_ext_stream.c b/sound/hda/ext/hdac_ext_stream.c index 1b2a1c996422..0ede36f51e2e 100644 --- a/sound/hda/ext/hdac_ext_stream.c +++ b/sound/hda/ext/hdac_ext_stream.c @@ -450,6 +450,8 @@ int snd_hdac_ext_stream_set_spib(struct hdac_bus *bus, } writel(value, stream->spib_addr); + /* save the value in stream context */ + stream->spib = value; return 0; } From 5708d3ba2a4128b022d5b03f4c1b4a93ca7d49df Mon Sep 17 00:00:00 2001 From: Ramesh Babu Date: Mon, 23 May 2016 11:39:19 +0530 Subject: [PATCH 0683/1103] ASoC: Intel: Skylake: add support for spib mode Skylake audio controller sports SPIB capability, which can be used to inform position of appl pointer to host DMA controller. When SPIB mode is enabled, driver could write the appl pointer position in SPIB register. Host DMA will make sure it won't read/write beyond bytes specified in SPIB register. SPIB mode will be useful in low power use cases, where DSP could pre-fetch large buffers to avoid frequent wakes due to interrupts. Skylake driver makes use of no_rewind flag and appl_ptr_update callback to enable and update SPIB register respectively. Signed-off-by: Ramesh Babu Signed-off-by: Subhransu S. Prusty --- sound/soc/intel/skylake/skl-pcm.c | 46 +++++++++++++++++++++++++++++-- 1 file changed, 44 insertions(+), 2 deletions(-) diff --git a/sound/soc/intel/skylake/skl-pcm.c b/sound/soc/intel/skylake/skl-pcm.c index 65a110664ff6..228f00763094 100644 --- a/sound/soc/intel/skylake/skl-pcm.c +++ b/sound/soc/intel/skylake/skl-pcm.c @@ -46,7 +46,8 @@ static const struct snd_pcm_hardware azx_pcm_hw = { SNDRV_PCM_INFO_SYNC_START | SNDRV_PCM_INFO_HAS_WALL_CLOCK | /* legacy */ SNDRV_PCM_INFO_HAS_LINK_ATIME | - SNDRV_PCM_INFO_NO_PERIOD_WAKEUP), + SNDRV_PCM_INFO_NO_PERIOD_WAKEUP | + SNDRV_PCM_INFO_NO_STATUS_MMAP), .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_S24_LE, @@ -154,6 +155,7 @@ int skl_pcm_host_dma_prepare(struct device *dev, struct skl_pipe_params *params) unsigned int format_val; struct hdac_stream *hstream; struct hdac_ext_stream *stream; + struct snd_pcm_runtime *runtime; int err; hstream = snd_hdac_get_stream(bus, params->stream, @@ -179,6 +181,11 @@ int skl_pcm_host_dma_prepare(struct device *dev, struct skl_pipe_params *params) if (err < 0) return err; + runtime = hdac_stream(stream)->substream->runtime; + /* enable SPIB if no_rewinds flag is set */ + if (runtime->no_rewinds) + snd_hdac_ext_stream_spbcap_enable(ebus, 1, hstream->index); + hdac_stream(stream)->prepared = 1; return 0; @@ -390,6 +397,8 @@ static int skl_pcm_hw_free(struct snd_pcm_substream *substream, { struct hdac_bus *bus = dev_get_drvdata(dai->dev); struct hdac_ext_stream *stream = get_hdac_ext_stream(substream); + struct hdac_stream *hstream = hdac_stream(stream); + struct snd_pcm_runtime *runtime = substream->runtime; struct skl *skl = get_skl_ctx(dai->dev); struct skl_module_cfg *mconfig; int ret; @@ -398,6 +407,10 @@ static int skl_pcm_hw_free(struct snd_pcm_substream *substream, mconfig = skl_tplg_fe_get_cpr_module(dai, substream->stream); + if (runtime->no_rewinds) { + snd_hdac_ext_stream_set_spib(ebus, stream, 0); + snd_hdac_ext_stream_spbcap_enable(ebus, 0, hstream->index); + } if (mconfig) { ret = skl_reset_pipe(skl->skl_sst, mconfig->pipe); if (ret < 0) @@ -479,6 +492,7 @@ static int skl_pcm_trigger(struct snd_pcm_substream *substream, int cmd, struct skl_module_cfg *mconfig; struct hdac_bus *bus = get_bus_ctx(substream); struct hdac_ext_stream *stream = get_hdac_ext_stream(substream); + struct snd_pcm_runtime *runtime = substream->runtime; struct snd_soc_dapm_widget *w; int ret; @@ -504,8 +518,10 @@ static int skl_pcm_trigger(struct snd_pcm_substream *substream, int cmd, snd_hdac_ext_stream_set_dpibr(bus, stream, stream->lpib); snd_hdac_ext_stream_set_lpib(stream, stream->lpib); + if (runtime->no_rewinds) + snd_hdac_ext_stream_set_spib(ebus, + stream, stream->spib); } - case SNDRV_PCM_TRIGGER_START: case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: /* @@ -1492,6 +1508,31 @@ static int skl_platform_pcm_trigger(struct snd_pcm_substream *substream, return 0; } +/* update SPIB register with appl position */ +static int skl_platform_ack(struct snd_pcm_substream *substream) +{ + struct hdac_ext_bus *ebus = get_bus_ctx(substream); + struct hdac_ext_stream *estream = get_hdac_ext_stream(substream); + struct snd_pcm_runtime *runtime = substream->runtime; + ssize_t appl_pos, buf_size; + u32 spib; + + /* Use spib mode only if no_rewind mode is set */ + if (runtime->no_rewinds == 0) + return 0; + + appl_pos = frames_to_bytes(runtime, runtime->control->appl_ptr); + buf_size = frames_to_bytes(runtime, runtime->buffer_size); + + spib = appl_pos % buf_size; + + /* Allowable value for SPIB is 1 byte to max buffer size */ + spib = (spib == 0) ? buf_size : spib; + snd_hdac_ext_stream_set_spib(ebus, estream, spib); + + return 0; +} + static snd_pcm_uframes_t skl_platform_pcm_pointer (struct snd_pcm_substream *substream) { @@ -1599,6 +1640,7 @@ static const struct snd_pcm_ops skl_platform_ops = { .get_time_info = skl_get_time_info, .mmap = snd_pcm_lib_default_mmap, .page = snd_pcm_sgbuf_ops_page, + .ack = skl_platform_ack, }; static void skl_pcm_free(struct snd_pcm *pcm) From 9717353428301d0d22d553376d387161737da7e4 Mon Sep 17 00:00:00 2001 From: Anil Bhawangirkar Date: Wed, 17 Aug 2016 12:19:51 +0530 Subject: [PATCH 0684/1103] ASoC: Intel: CNL: Fetch ACPI data for SoundWire Master The DSDT table contains information about the SoundWire capabalities for Master controller and device. This patch add API's which fetch the SoundWire capabilities for Master. Change-Id: Iddb147a48faaf67f7fb4ee378e72858cb14e721a Signed-off-by: Anil Bhawangirkar Signed-off-by: Guneshwor Singh --- include/linux/sdw/sdw_cnl.h | 8 +- sound/soc/intel/skylake/Makefile | 4 + sound/soc/intel/skylake/cnl-acpi.c | 160 +++++++++++++++++++++++++++++ 3 files changed, 169 insertions(+), 3 deletions(-) create mode 100644 sound/soc/intel/skylake/cnl-acpi.c diff --git a/include/linux/sdw/sdw_cnl.h b/include/linux/sdw/sdw_cnl.h index 6a9281c458b7..1a40244e466d 100644 --- a/include/linux/sdw/sdw_cnl.h +++ b/include/linux/sdw/sdw_cnl.h @@ -27,7 +27,7 @@ #define SDW_CNL_PM_TIMEOUT 3000 /* ms */ -#define CNL_SDW_MAX_PORTS 15 +#define CNL_SDW_MAX_PORTS 9 /* Maximum number hardware tries to send command if the command failed */ #define CNL_SDW_MAX_CMD_RETRIES 15 @@ -119,7 +119,9 @@ struct cnl_sdw_port *cnl_sdw_alloc_port(struct sdw_master *mstr, int ch_count, enum sdw_data_direction direction, enum cnl_sdw_pdi_stream_type stream_type); void cnl_sdw_free_port(struct sdw_master *mstr, int port_num); - - +int cnl_sdw_get_master_caps(struct device *dev, + struct sdw_master_capabilities *m_cap); +int cnl_sdw_get_master_dev_caps(struct device *dev, + struct sdw_master_capabilities *m_cap, int dev_port_num); #endif diff --git a/sound/soc/intel/skylake/Makefile b/sound/soc/intel/skylake/Makefile index 9a3ddb969d4e..7b8cf119aeae 100644 --- a/sound/soc/intel/skylake/Makefile +++ b/sound/soc/intel/skylake/Makefile @@ -13,6 +13,10 @@ snd-soc-skl-ipc-objs := skl-sst-ipc.o skl-sst-dsp.o cnl-sst-dsp.o \ skl-sst-cldma.o skl-sst.o bxt-sst.o cnl-sst.o \ skl-sst-utils.o skl-fwlog.o +ifdef CONFIG_SDW + snd-soc-skl-ipc-objs += cnl-acpi.o +endif + obj-$(CONFIG_SND_SOC_INTEL_SKYLAKE) += snd-soc-skl-ipc.o #Skylake Clock device support diff --git a/sound/soc/intel/skylake/cnl-acpi.c b/sound/soc/intel/skylake/cnl-acpi.c new file mode 100644 index 000000000000..1bee574f2ab8 --- /dev/null +++ b/sound/soc/intel/skylake/cnl-acpi.c @@ -0,0 +1,160 @@ +/* + * cnl-acpi.c - Intel CNL Platform ACPI parsing + * + * Copyright (C) 2016 Intel Corp + * + * Author: Anil Bhawangirkar + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * 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; version 2 of the License. + * + * 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. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + */ + +#include +#include +#include +#include +#include +#include + +/* + * This is SoundWire device path defined for Master and Slave. + */ +static acpi_string path_sdw_dev[] = {"SWD0.", "SWD1.", "SWD2.", "SWD3.", + "SWD4.", "SWD5.", "SWD6.", "SWD7.", "SWD8."}; +#define DSDT_ACPI_PATH "\\_SB.PCI0.RP01.PXSX.HDAS.SNDW" +#define SDW_PATH_CTRL_MAX 4 +#define SDW_PATH_DEV_MAX 9 +ACPI_MODULE_NAME("ACPI"); + +static union acpi_object *sdw_dpn_extract_capab(union acpi_object *obj) +{ + union acpi_object *data, *package1, *package2; + + data = &obj->package.elements[1]; + package1 = &data->package.elements[0]; + package2 = &package1->package.elements[1]; + return package2; +} + +static union acpi_object *sdw_scd_extract_capab(union acpi_object *obj) +{ + union acpi_object *data, *package1, *package2, *package3; + + data = &obj->package.elements[1]; + package1 = &data->package.elements[3]; + package2 = &package1->package.elements[1]; + package3 = &package2->package.elements[0]; + return package3; +} + +static union acpi_object *sdw_acpi_init_object(struct device *dev, char path[]) +{ + struct acpi_buffer buf = {ACPI_ALLOCATE_BUFFER, NULL}; + union acpi_object *obj; + acpi_handle handle; + acpi_status status; + + status = acpi_get_handle(NULL, DSDT_ACPI_PATH, &handle); + if (ACPI_FAILURE(status)) { + dev_err(dev, "ACPI Object evaluation is failed...\n"); + return NULL; + } + + status = acpi_evaluate_object(handle, path, NULL, &buf); + if (ACPI_FAILURE(status)) { + if (status != AE_NOT_FOUND) + ACPI_EXCEPTION((AE_INFO, status, "Invalid pathname\n")); + return NULL; + } + obj = buf.pointer; + return obj; +} + +static int sdw_fill_master_dpn_caps(struct sdw_master_capabilities *map, + union acpi_object *obj) +{ + struct sdw_mstr_dpn_capabilities *dpn_cap; + union acpi_object *ret_data; + + dpn_cap = &map->sdw_dpn_cap[0]; + ret_data = sdw_dpn_extract_capab(obj); + dpn_cap->dpn_type = ret_data->integer.value; + return 0; +} + +static int sdw_fill_master_scd_caps(struct sdw_master_capabilities *map, + union acpi_object *obj) +{ + union acpi_object *ret_data; + + ret_data = sdw_scd_extract_capab(obj); + map->base_clk_freq = ret_data->integer.value; + return 0; +} + +static int sdw_acpi_mstr_map_data(struct sdw_master_capabilities *mcap, + struct device *dev, acpi_string path_name, char path[]) +{ + union acpi_object *obj; + + obj = sdw_acpi_init_object(dev, path); + if (obj && (obj->type == ACPI_TYPE_PACKAGE)) { + if (!strcmp(path_name, "SCD")) + sdw_fill_master_scd_caps(mcap, obj); + else if (!strcmp(path_name, "DPN")) + sdw_fill_master_dpn_caps(mcap, obj); + } + + kfree(obj); + return 0; +} + +/* + * get the ACPI data for SoundWire Master contoller capablities + */ +int cnl_sdw_get_master_caps(struct device *dev, + struct sdw_master_capabilities *m_cap) +{ + acpi_string path_sdw_ctrl = {"SCD"}; + char path[SDW_PATH_CTRL_MAX]; + + strcpy(path, path_sdw_ctrl); + sdw_acpi_mstr_map_data(m_cap, dev, path_sdw_ctrl, path); + if (!m_cap) { + dev_err(dev, "SoundWire controller mapping failed...\n"); + return -EINVAL; + } + return 0; +} +EXPORT_SYMBOL_GPL(cnl_sdw_get_master_caps); + +/* + * get the ACPI data for SoundWire Master devices capabilities + */ +int cnl_sdw_get_master_dev_caps(struct device *dev, + struct sdw_master_capabilities *m_cap, int dev_port_num) +{ + acpi_string path_sdw_dpn = {"DPN"}; + char path[SDW_PATH_DEV_MAX]; + + snprintf(path, sizeof(path), "%s%s", + path_sdw_dev[dev_port_num], path_sdw_dpn); + sdw_acpi_mstr_map_data(m_cap, dev, path_sdw_dpn, path); + if (!m_cap) { + dev_err(dev, "SoundWire device mapping failed...\n"); + return -EINVAL; + } + return 0; +} +EXPORT_SYMBOL_GPL(cnl_sdw_get_master_dev_caps); From 6287980796da7abe8c64cdd3d9a36076f02bb6ab Mon Sep 17 00:00:00 2001 From: Anil Bhawangirkar Date: Tue, 23 Aug 2016 15:08:03 +0530 Subject: [PATCH 0685/1103] ASoC: Intel: CNL: get SoundWire Master capabilities and map it This patch get the SoundWire Master capabilities and map the SoundWire capabalities to Master controller and device. Change-Id: I78eaafc640bd0bb3d8a423569a2590b94c5d7de8 Signed-off-by: Anil Bhawangirkar --- sound/soc/intel/skylake/cnl-sst.c | 27 +++++++++++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) diff --git a/sound/soc/intel/skylake/cnl-sst.c b/sound/soc/intel/skylake/cnl-sst.c index d888d31d0ea2..c5ad9fde2fed 100644 --- a/sound/soc/intel/skylake/cnl-sst.c +++ b/sound/soc/intel/skylake/cnl-sst.c @@ -511,7 +511,7 @@ static int cnl_ipc_init(struct device *dev, struct skl_sst *cnl) static int skl_register_sdw_masters(struct device *dev, struct skl_sst *dsp, void __iomem *mmio_base, int irq) { - struct sdw_master_capabilities *m_cap; + struct sdw_master_capabilities *m_cap, *map_data; struct sdw_mstr_dp0_capabilities *dp0_cap; struct sdw_mstr_dpn_capabilities *dpn_cap; struct sdw_master *master; @@ -542,10 +542,21 @@ static int skl_register_sdw_masters(struct device *dev, struct skl_sst *dsp, master[i].dev.platform_data = p_data; m_cap = &master[i].mstr_capabilities; dp0_cap = &m_cap->sdw_dp0_cap; + map_data = kzalloc(sizeof(*m_cap), GFP_KERNEL); + if (!map_data) + return -ENOMEM; + /* This function retrieves the data for SoundWire controller */ + cnl_sdw_get_master_caps(dev, map_data); master[i].nr = i; master[i].timeout = -1; master[i].retries = CNL_SDW_MAX_CMD_RETRIES; - m_cap->base_clk_freq = 9.6 * 1000 * 1000; + m_cap->base_clk_freq = map_data->base_clk_freq; + /* TODO: Frequency is not read correctly in ACPI code */ +#if IS_ENABLED(CONFIG_SND_SOC_INTEL_CNL_FPGA) + m_cap->base_clk_freq = 9600000; +#else + m_cap->base_clk_freq = 12000000; +#endif strcpy(master[i].name, "cnl_sdw_mstr"); m_cap->highphy_capable = false; m_cap->monitor_handover_supported = false; @@ -562,7 +573,17 @@ static int skl_register_sdw_masters(struct device *dev, struct skl_sst *dsp, return -ENOMEM; for (j = 0; j < m_cap->num_data_ports; j++) { dpn_cap = &m_cap->sdw_dpn_cap[j]; + map_data->sdw_dpn_cap = kzalloc(sizeof(*dpn_cap), + GFP_KERNEL); + if (!map_data->sdw_dpn_cap) + return -ENOMEM; + /* + * This function retrieves the data + * for SoundWire devices. + */ + cnl_sdw_get_master_dev_caps(dev, map_data, j); /* Both Tx and Rx */ + dpn_cap->dpn_type = map_data->sdw_dpn_cap->dpn_type; dpn_cap->port_direction = 0x3; dpn_cap->port_number = j; dpn_cap->max_word_length = 32; @@ -588,7 +609,9 @@ static int skl_register_sdw_masters(struct device *dev, struct skl_sst *dsp, dpn_cap->block_packing_mode_mask = SDW_PORT_BLK_PKG_MODE_BLK_PER_PORT | SDW_PORT_BLK_PKG_MODE_BLK_PER_CH; + kfree(map_data->sdw_dpn_cap); } + kfree(map_data); master[i].link_sync_mask = 0x0; switch (i) { case 0: From be63c65429b9a006f81a74a8f8dcf157e14a08d3 Mon Sep 17 00:00:00 2001 From: Anil Bhawangirkar Date: Wed, 17 Aug 2016 12:23:35 +0530 Subject: [PATCH 0686/1103] [REVERTME]ASoC: Intel: CNL: Define DPN package for SDW1. As DPN package entries are not defined in the BIOS for SoundWire device 1(SDW1), the capability data-port-type can not be mapped for SDW1. As a workaround, hard code data-port-type for SDW1. Workaround is not required for other devices, since BIOS has entry for them. This patch can be reverted once BIOS has entry for SDW1. Change-Id: I15b0ec44731feb1f1f45466a0f9a9426d31a8dee Signed-off-by: Anil Bhawangirkar --- sound/soc/intel/skylake/cnl-sst.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/sound/soc/intel/skylake/cnl-sst.c b/sound/soc/intel/skylake/cnl-sst.c index c5ad9fde2fed..18be70b87728 100644 --- a/sound/soc/intel/skylake/cnl-sst.c +++ b/sound/soc/intel/skylake/cnl-sst.c @@ -583,7 +583,12 @@ static int skl_register_sdw_masters(struct device *dev, struct skl_sst *dsp, */ cnl_sdw_get_master_dev_caps(dev, map_data, j); /* Both Tx and Rx */ - dpn_cap->dpn_type = map_data->sdw_dpn_cap->dpn_type; + /* WORKAROUND: hard code for SDW1 */ + if (j == 1) + dpn_cap->dpn_type = SDW_FULL_DP; + else + dpn_cap->dpn_type = + map_data->sdw_dpn_cap->dpn_type; dpn_cap->port_direction = 0x3; dpn_cap->port_number = j; dpn_cap->max_word_length = 32; From a6b6951b5f7d1049db149ae159fb54f86780da77 Mon Sep 17 00:00:00 2001 From: "Yadav, PramodX K" Date: Fri, 15 Jul 2016 20:01:42 +0530 Subject: [PATCH 0687/1103] ASoC: Intel: Skylake: Support for 24KHz SoC DMIC capture Change-Id: I69a1dc19badb335747a15c2b8e0994f81ac95116 Signed-off-by: Yadav, PramodX K --- sound/soc/intel/skylake/skl-pcm.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/sound/soc/intel/skylake/skl-pcm.c b/sound/soc/intel/skylake/skl-pcm.c index 228f00763094..3ac58520240a 100644 --- a/sound/soc/intel/skylake/skl-pcm.c +++ b/sound/soc/intel/skylake/skl-pcm.c @@ -983,7 +983,9 @@ static struct snd_soc_dai_driver skl_fe_dai[] = { .stream_name = "System Capture", .channels_min = HDA_MONO, .channels_max = HDA_STEREO, - .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_16000, + .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_32000 | + SNDRV_PCM_RATE_KNOT | + SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_8000, .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE, .sig_bits = 32, }, @@ -1021,7 +1023,7 @@ static struct snd_soc_dai_driver skl_fe_dai[] = { .stream_name = "Reference Capture", .channels_min = HDA_MONO, .channels_max = HDA_QUAD, - .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_16000, + .rates = SNDRV_PCM_RATE_8000_192000 | SNDRV_PCM_RATE_KNOT, .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE, .sig_bits = 32, }, From e98233a19077b21cf714ca013aa8f6e4d1b7b7b7 Mon Sep 17 00:00:00 2001 From: bardliao Date: Tue, 28 Jun 2016 16:33:32 +0800 Subject: [PATCH 0688/1103] ASoC: add rt700 codec driver Change-Id: I1b817a96b47310091307a45e7c85aa99009f3b78 Signed-off-by: Bard Liao Signed-off-by: Hardik Shah --- sound/soc/codecs/Kconfig | 10 + sound/soc/codecs/Makefile | 4 + sound/soc/codecs/rt700-sdw.c | 387 + sound/soc/codecs/rt700-sdw.h | 53635 +++++++++++++++++++++++++++++++++ sound/soc/codecs/rt700.c | 1600 + sound/soc/codecs/rt700.h | 161 + 6 files changed, 55797 insertions(+) create mode 100644 sound/soc/codecs/rt700-sdw.c create mode 100644 sound/soc/codecs/rt700-sdw.h create mode 100644 sound/soc/codecs/rt700.c create mode 100644 sound/soc/codecs/rt700.h diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index 461f41268b68..b002d79d910e 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -894,6 +894,16 @@ config SND_SOC_RT5677_SPI config SND_SOC_RT5682 tristate +config SND_SOC_RT700 + tristate "Realtek RT700 Codec" + depends on SDW + +config SND_SOC_RT700_SDW + tristate "Realtek RT700 Codec - SDW" + depends on SDW + select SND_SOC_RT700 + select REGMAP_SDW + #Freescale sgtl5000 codec config SND_SOC_SGTL5000 tristate "Freescale SGTL5000 CODEC" diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile index f37fca3b9960..5aed74a73acf 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile @@ -151,6 +151,8 @@ snd-soc-rt5670-objs := rt5670.o snd-soc-rt5677-objs := rt5677.o snd-soc-rt5677-spi-objs := rt5677-spi.o snd-soc-rt5682-objs := rt5682.o +snd-soc-rt700-objs := rt700.o +snd-soc-rt700-sdw-objs := rt700-sdw.o snd-soc-sgtl5000-objs := sgtl5000.o snd-soc-alc5623-objs := alc5623.o snd-soc-alc5632-objs := alc5632.o @@ -415,6 +417,8 @@ obj-$(CONFIG_SND_SOC_RT5670) += snd-soc-rt5670.o obj-$(CONFIG_SND_SOC_RT5677) += snd-soc-rt5677.o obj-$(CONFIG_SND_SOC_RT5677_SPI) += snd-soc-rt5677-spi.o obj-$(CONFIG_SND_SOC_RT5682) += snd-soc-rt5682.o +obj-$(CONFIG_SND_SOC_RT700) += snd-soc-rt700.o +obj-$(CONFIG_SND_SOC_RT700_SDW) += snd-soc-rt700-sdw.o obj-$(CONFIG_SND_SOC_SGTL5000) += snd-soc-sgtl5000.o obj-$(CONFIG_SND_SOC_SIGMADSP) += snd-soc-sigmadsp.o obj-$(CONFIG_SND_SOC_SIGMADSP_I2C) += snd-soc-sigmadsp-i2c.o diff --git a/sound/soc/codecs/rt700-sdw.c b/sound/soc/codecs/rt700-sdw.c new file mode 100644 index 000000000000..a2533edfa48a --- /dev/null +++ b/sound/soc/codecs/rt700-sdw.c @@ -0,0 +1,387 @@ +/* + * rt700-sdw.c -- rt700 ALSA SoC audio driver + * + * Copyright 2016 Realtek, Inc. + * + * Author: Bard Liao + * ALC700 ASoC Codec Driver based Intel Dummy SdW codec driver + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ +#define DEBUG + +#include +#include +#include +#include +#include +#include +#include "rt700.h" +#include "rt700-sdw.h" + +static bool rt700_readable_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case 0x0020: + case 0x0030: + case 0x0060: + case 0x0070: + case 0x00e0: + case 0x00f0: + case 0x0000 ... 0x0005: + case 0x0023 ... 0x0026: + case 0x0032 ... 0x0036: + case 0x0040 ... 0x0046: + case 0x0050 ... 0x0055: + case 0x0100 ... 0x0105: + case 0x0120 ... 0x0127: + case 0x0130 ... 0x0137: + case 0x0200 ... 0x0205: + case 0x0220 ... 0x0227: + case 0x0230 ... 0x0237: + case 0x0300 ... 0x0305: + case 0x0320 ... 0x0327: + case 0x0330 ... 0x0337: + case 0x0400 ... 0x0405: + case 0x0420 ... 0x0427: + case 0x0430 ... 0x0437: + case 0x0500 ... 0x0505: + case 0x0520 ... 0x0527: + case 0x0530 ... 0x0537: + case 0x0600 ... 0x0605: + case 0x0620 ... 0x0627: + case 0x0630 ... 0x0637: + case 0x0700 ... 0x0705: + case 0x0720 ... 0x0727: + case 0x0730 ... 0x0737: + case 0x0800 ... 0x0805: + case 0x0820 ... 0x0827: + case 0x0830 ... 0x0837: + case 0x0f00 ... 0x0f27: + case 0x0f30 ... 0x0f37: + case 0x2000 ... 0x200e: + case 0x2012 ... 0x2016: + case 0x201a ... 0x2027: + case 0x2029 ... 0x202a: + case 0x202d ... 0x2034: + case 0x2100 ... 0x213f: + case 0x2200 ... 0x2204: + case 0x2206 ... 0x2212: + case 0x2220 ... 0x2223: + case 0x2230 ... 0x2231: + case 0x3000 ... 0xffff: + return true; + default: + return false; + } +} + +static bool rt700_volatile_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case 0x0000: + case 0x0001: + case 0x0004: + case 0x0005: + case 0x0040: + case 0x0042: + case 0x0043: + case 0x0044: + case 0x0045: + case 0x0104: + case 0x0204: + case 0x0304: + case 0x0404: + case 0x0504: + case 0x0604: + case 0x0704: + case 0x0804: + case 0x0105: + case 0x0205: + case 0x0305: + case 0x0405: + case 0x0505: + case 0x0605: + case 0x0705: + case 0x0805: + case 0x0f00: + case 0x0f04: + case 0x0f05: + case 0x2009: + case 0x2016: + case 0x201b: + case 0x201c: + case 0x201d: + case 0x201f: + case 0x2021: + case 0x2023: + case 0x2230: + case 0x0050 ... 0x0055: /* device id */ + case 0x200b ... 0x200e: /* i2c read */ + case 0x2012 ... 0x2015: /* HD-A read */ + case 0x202d ... 0x202f: /* BRA */ + case 0x2201 ... 0x2212: /* i2c debug */ + case 0x2220 ... 0x2223: /* decoded HD-A */ + return true; + case 0x3000 ... 0xffff: /* HD-A command */ + return false; /* should always read from cache */ + default: + return true; /*for debug*/ + } +} + +const struct regmap_config rt700_sdw_regmap = { + .reg_bits = 32, /* Total register space for SDW */ + .val_bits = 8, /* Total number of bits in register */ + + .readable_reg = rt700_readable_register, /* Readable registers */ + .volatile_reg = rt700_volatile_register, /* volatile register */ + + .max_register = 0xffff, /* Maximum number of register */ + .reg_defaults = rt700_reg_defaults, /* Defaults */ + .num_reg_defaults = ARRAY_SIZE(rt700_reg_defaults), + .cache_type = REGCACHE_RBTREE, +}; + +int hda_to_sdw(unsigned int nid, unsigned int verb, unsigned int payload, + unsigned int *sdw_addr_h, unsigned int *sdw_data_h, + unsigned int *sdw_addr_l, unsigned int *sdw_data_l) { + unsigned int offset_h, offset_l, e_verb; + + if (((verb & 0xff) != 0) || verb == 0xf00) { /* 12 bits command */ + if (verb == 0x7ff) /* special case */ + offset_h = 0; + else + offset_h = 0x3000; + + if (verb & 0x800) /* get command */ + e_verb = (verb - 0xf00) | 0x80; + else /* set command */ + e_verb = (verb - 0x700); + + *sdw_data_h = payload; /* 7 bits payload */ + *sdw_addr_l = *sdw_data_l = 0; + } else { /* 4 bits command */ + if ((verb & 0x800) == 0x800) { /* read */ + offset_h = 0x9000; + offset_l = 0xa000; + } else { /* write */ + offset_h = 0x7000; + offset_l = 0x8000; + } + e_verb = verb >> 8; + *sdw_data_h = (payload >> 8); /* 16 bits payload [15:8] */ + *sdw_addr_l = (e_verb << 8) | nid | 0x80; /* 0x80: valid bit */ + *sdw_addr_l += offset_l; + *sdw_data_l = payload & 0xff; + } + + *sdw_addr_h = (e_verb << 8) | nid; + *sdw_addr_h += offset_h; + + return 0; +} +EXPORT_SYMBOL(hda_to_sdw); + +static int rt700_register_sdw_capabilties(struct sdw_slv *sdw, + const struct sdw_slv_id *sdw_id) +{ + struct sdw_slv_capabilities cap; + struct sdw_slv_dpn_capabilities *dpn_cap = NULL; + struct port_audio_mode_properties *prop = NULL; + unsigned int bus_config_frequencies[] = { + 2400000, 4800000, 9600000, 3000000, 6000000, 12000000, 19200000}; + unsigned int freq_supported[] = { + 19200000, 2400000, 4800000, 9600000, 12000000, 12288000}; + int i, j; + + cap.wake_up_unavailable = false; + cap.test_mode_supported = true; + cap.clock_stop1_mode_supported = true; + cap.simplified_clock_stop_prepare = true; + cap.highphy_capable = false; + cap.paging_supported = false; + cap.bank_delay_support = false; + cap.port_15_read_behavior = 1; + cap.sdw_dp0_supported = false; + cap.num_of_sdw_ports = 8; /* Inport 4, Outport 4 */ + cap.scp_impl_def_intr_mask = 0x4; + cap.sdw_dp0_cap = devm_kzalloc(&sdw->dev, + sizeof(struct sdw_slv_dp0_capabilities), GFP_KERNEL); + + cap.sdw_dp0_cap->max_word_length = 64; + cap.sdw_dp0_cap->min_word_length = 1; + cap.sdw_dp0_cap->num_word_length = 0; + cap.sdw_dp0_cap->word_length_buffer = NULL; + cap.sdw_dp0_cap->bra_use_flow_control = 0; + cap.sdw_dp0_cap->bra_initiator_supported = 0; + cap.sdw_dp0_cap->ch_prepare_mode = SDW_SIMPLIFIED_CP_SM; + cap.sdw_dp0_cap->impl_def_response_supported = false; + /* Address 0x0001, bit 0: port ready, bit 2: BRA failure*/ + cap.sdw_dp0_cap->imp_def_intr_mask = 0; + cap.sdw_dp0_cap->impl_def_bpt_supported = true; + cap.sdw_dp0_cap->slave_bra_cap.max_bus_frequency = 24000000; + cap.sdw_dp0_cap->slave_bra_cap.min_bus_frequency = 2400000; + cap.sdw_dp0_cap->slave_bra_cap.num_bus_config_frequency = 7; + cap.sdw_dp0_cap->slave_bra_cap.bus_config_frequencies = + bus_config_frequencies; + cap.sdw_dp0_cap->slave_bra_cap.max_data_per_frame = 470; + cap.sdw_dp0_cap->slave_bra_cap.min_us_between_transactions = 0; + cap.sdw_dp0_cap->slave_bra_cap.max_bandwidth = (500 - 48) * 48000; + cap.sdw_dp0_cap->slave_bra_cap.mode_block_alignment = 1; + + cap.sdw_dpn_cap = devm_kzalloc(&sdw->dev, + ((sizeof(struct sdw_slv_dpn_capabilities)) * + cap.num_of_sdw_ports), GFP_KERNEL); + for (i = 1; i <= cap.num_of_sdw_ports; i++) { + dpn_cap = &cap.sdw_dpn_cap[i-1]; + if ((i % 2) == 0) + dpn_cap->port_direction = SDW_PORT_SINK; + else + dpn_cap->port_direction = SDW_PORT_SOURCE; + + dpn_cap->port_number = i; + dpn_cap->max_word_length = 64; + dpn_cap->min_word_length = 1; + dpn_cap->num_word_length = 0; + dpn_cap->word_length_buffer = NULL; + dpn_cap->dpn_type = SDW_FULL_DP; + dpn_cap->dpn_grouping = SDW_BLOCKGROUPCOUNT_1; + dpn_cap->prepare_ch = SDW_SIMPLIFIED_CP_SM; + dpn_cap->imp_def_intr_mask = 0; /* bit 0: Test Fail */ + dpn_cap->min_ch_num = 2; + dpn_cap->max_ch_num = 2; + dpn_cap->num_ch_supported = 0; + dpn_cap->ch_supported = NULL; + dpn_cap->port_flow_mode_mask = SDW_PORT_FLOW_MODE_ISOCHRONOUS; + dpn_cap->block_packing_mode_mask = + SDW_PORT_BLK_PKG_MODE_BLK_PER_PORT_MASK | + SDW_PORT_BLK_PKG_MODE_BLK_PER_CH_MASK; + dpn_cap->port_encoding_type_mask = + SDW_PORT_ENCODING_TYPE_TWOS_CMPLMNT; + dpn_cap->num_audio_modes = 1; + + dpn_cap->mode_properties = devm_kzalloc(&sdw->dev, + ((sizeof(struct port_audio_mode_properties)) * + dpn_cap->num_audio_modes), GFP_KERNEL); + for (j = 0; j < dpn_cap->num_audio_modes; j++) { + prop = &dpn_cap->mode_properties[j]; + prop->max_frequency = 24000000; + prop->min_frequency = 2400000; + prop->num_freq_configs = 0; + prop->freq_supported = devm_kzalloc(&sdw->dev, + prop->num_freq_configs * + (sizeof(unsigned int)), + GFP_KERNEL); + if (!prop->freq_supported) + return -ENOMEM; + + memcpy(prop->freq_supported, freq_supported, + prop->num_freq_configs * + (sizeof(unsigned int))); + prop->glitchless_transitions_mask = 0x0; + prop->max_sampling_frequency = 192000; + prop->min_sampling_frequency = 8000; + prop->num_sampling_freq_configs = 0; + prop->sampling_freq_config = NULL; + prop->ch_prepare_behavior = SDW_CH_PREP_ANY_TIME; + } + } + return sdw_register_slave_capabilities(sdw, &cap); + +} +static int rt700_sdw_probe(struct sdw_slv *sdw, + const struct sdw_slv_id *sdw_id) +{ + struct alc700 *alc700_priv; + struct regmap *regmap; + int ret; + + alc700_priv = kzalloc(sizeof(struct alc700), GFP_KERNEL); + if(!alc700_priv) { + return -ENOMEM; + } + dev_set_drvdata(&sdw->dev, alc700_priv); + alc700_priv->sdw = sdw; + alc700_priv->params = kzalloc(sizeof(struct sdw_bus_params), GFP_KERNEL); + if(!alc700_priv->params) { + return -ENOMEM; + } + regmap = devm_regmap_init_sdwint(sdw, &rt700_sdw_regmap); + if (!regmap) + return -EINVAL; + ret = rt700_register_sdw_capabilties(sdw, sdw_id); + if (ret) + return ret; + ret = sdw_slave_get_bus_params(sdw, alc700_priv->params); + if (ret) + return -EFAULT; + return rt700_probe(&sdw->dev, regmap, sdw); +} + +static int rt700_sdw_remove(struct sdw_slv *sdw) +{ + return rt700_remove(&sdw->dev); +} + +static const struct sdw_slv_id rt700_id[] = { + {"10:02:5d:07:00:00", 0}, + {"11:02:5d:07:00:00", 0}, + {"12:02:5d:07:00:00", 0}, + {"13:02:5d:07:00:00", 0}, + {"14:02:5d:07:00:00", 0}, + {"15:02:5d:07:00:00", 0}, + {"16:02:5d:07:00:00", 0}, + {"17:02:5d:07:00:00", 0}, + {"10:02:5d:07:01:00", 0}, + {"11:02:5d:07:01:00", 0}, + {"12:02:5d:07:01:00", 0}, + {"13:02:5d:07:01:00", 0}, + {"14:02:5d:07:01:00", 0}, + {"15:02:5d:07:01:00", 0}, + {"16:02:5d:07:01:00", 0}, + {"17:02:5d:07:01:00", 0}, + {"10:02:5d:07:00:01", 0}, + {} +}; + +MODULE_DEVICE_TABLE(sdwint, rt700_id); + + +static int rt700_sdw_handle_impl_def_interrupts(struct sdw_slv *swdev, + struct sdw_impl_def_intr_stat *intr_status) +{ + struct rt700_priv *rt700 = dev_get_drvdata(&swdev->dev); + bool hp, mic; + + pr_debug("%s control_port_stat=%x port0_stat=%x\n", __func__, + intr_status->control_port_stat, intr_status->port0_stat); + if (intr_status->control_port_stat & 0x4) { + rt700_jack_detect(rt700, &hp, &mic); + pr_info("%s hp=%d mic=%d\n", __func__, hp, mic); + } + + return 0; +} + +static struct sdw_slave_driver rt700_sdw_driver = { + .driver_type = SDW_DRIVER_TYPE_SLAVE, + .driver = { + .name = "rt700", + .pm = &rt700_runtime_pm, + }, + .probe = rt700_sdw_probe, + .remove = rt700_sdw_remove, + .handle_impl_def_interrupts = rt700_sdw_handle_impl_def_interrupts, + + .id_table = rt700_id, +}; + +module_sdw_slave_driver(rt700_sdw_driver); + +MODULE_DESCRIPTION("ASoC RT700 driver SDW"); +MODULE_AUTHOR("Bard Liao "); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/rt700-sdw.h b/sound/soc/codecs/rt700-sdw.h new file mode 100644 index 000000000000..b691ba01819d --- /dev/null +++ b/sound/soc/codecs/rt700-sdw.h @@ -0,0 +1,53635 @@ +/* + * rt700.h -- RT700 ALSA SoC audio driver header + * + * Copyright 2016 Realtek, Inc. + * + * Author: Bard Liao + * Dummy ASoC Codec Driver based Cirrus Logic CS42L42 Codec + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +#ifndef __RT700_SDW_H__ +#define __RT700_SDW_H__ + +static const struct reg_default rt700_reg_defaults[] = { + { 0x0000, 0x0000 }, + { 0x0001, 0x0000 }, + { 0x0002, 0x0000 }, + { 0x0003, 0x0000 }, + { 0x0004, 0x0000 }, + { 0x0005, 0x0001 }, + { 0x0020, 0x0000 }, + { 0x0022, 0x0000 }, + { 0x0023, 0x0000 }, + { 0x0024, 0x0000 }, + { 0x0025, 0x0000 }, + { 0x0026, 0x0000 }, + { 0x0030, 0x0000 }, + { 0x0032, 0x0000 }, + { 0x0033, 0x0000 }, + { 0x0034, 0x0000 }, + { 0x0035, 0x0000 }, + { 0x0036, 0x0000 }, + { 0x0040, 0x0000 }, + { 0x0041, 0x0000 }, + { 0x0042, 0x0000 }, + { 0x0043, 0x0000 }, + { 0x0044, 0x0020 }, + { 0x0045, 0x0001 }, + { 0x0046, 0x0000 }, + { 0x0050, 0x0000 }, + { 0x0051, 0x0000 }, + { 0x0052, 0x0000 }, + { 0x0053, 0x0000 }, + { 0x0054, 0x0000 }, + { 0x0055, 0x0000 }, + { 0x0060, 0x0000 }, + { 0x0070, 0x0000 }, + { 0x00e0, 0x0000 }, + { 0x00f0, 0x0000 }, + { 0x0100, 0x0000 }, + { 0x0101, 0x0000 }, + { 0x0102, 0x0000 }, + { 0x0103, 0x0000 }, + { 0x0104, 0x0000 }, + { 0x0105, 0x0000 }, + { 0x0120, 0x0000 }, + { 0x0121, 0x0000 }, + { 0x0122, 0x0000 }, + { 0x0123, 0x0000 }, + { 0x0124, 0x0000 }, + { 0x0125, 0x0000 }, + { 0x0126, 0x0000 }, + { 0x0127, 0x0000 }, + { 0x0130, 0x0000 }, + { 0x0131, 0x0000 }, + { 0x0132, 0x0000 }, + { 0x0133, 0x0000 }, + { 0x0134, 0x0000 }, + { 0x0135, 0x0000 }, + { 0x0136, 0x0000 }, + { 0x0137, 0x0000 }, + { 0x0200, 0x0000 }, + { 0x0201, 0x0000 }, + { 0x0202, 0x0000 }, + { 0x0203, 0x0000 }, + { 0x0204, 0x0000 }, + { 0x0205, 0x0000 }, + { 0x0220, 0x0000 }, + { 0x0221, 0x0000 }, + { 0x0222, 0x0000 }, + { 0x0223, 0x0000 }, + { 0x0224, 0x0000 }, + { 0x0225, 0x0000 }, + { 0x0226, 0x0000 }, + { 0x0227, 0x0000 }, + { 0x0230, 0x0000 }, + { 0x0231, 0x0000 }, + { 0x0232, 0x0000 }, + { 0x0233, 0x0000 }, + { 0x0234, 0x0000 }, + { 0x0235, 0x0000 }, + { 0x0236, 0x0000 }, + { 0x0237, 0x0000 }, + { 0x0300, 0x0000 }, + { 0x0301, 0x0000 }, + { 0x0302, 0x0000 }, + { 0x0303, 0x0000 }, + { 0x0304, 0x0000 }, + { 0x0305, 0x0000 }, + { 0x0320, 0x0000 }, + { 0x0321, 0x0000 }, + { 0x0322, 0x0000 }, + { 0x0323, 0x0000 }, + { 0x0324, 0x0000 }, + { 0x0325, 0x0000 }, + { 0x0326, 0x0000 }, + { 0x0327, 0x0000 }, + { 0x0330, 0x0000 }, + { 0x0331, 0x0000 }, + { 0x0332, 0x0000 }, + { 0x0333, 0x0000 }, + { 0x0334, 0x0000 }, + { 0x0335, 0x0000 }, + { 0x0336, 0x0000 }, + { 0x0337, 0x0000 }, + { 0x0400, 0x0000 }, + { 0x0401, 0x0000 }, + { 0x0402, 0x0000 }, + { 0x0403, 0x0000 }, + { 0x0404, 0x0000 }, + { 0x0405, 0x0000 }, + { 0x0420, 0x0000 }, + { 0x0421, 0x0000 }, + { 0x0422, 0x0000 }, + { 0x0423, 0x0000 }, + { 0x0424, 0x0000 }, + { 0x0425, 0x0000 }, + { 0x0426, 0x0000 }, + { 0x0427, 0x0000 }, + { 0x0430, 0x0000 }, + { 0x0431, 0x0000 }, + { 0x0432, 0x0000 }, + { 0x0433, 0x0000 }, + { 0x0434, 0x0000 }, + { 0x0435, 0x0000 }, + { 0x0436, 0x0000 }, + { 0x0437, 0x0000 }, + { 0x0500, 0x0000 }, + { 0x0501, 0x0000 }, + { 0x0502, 0x0000 }, + { 0x0503, 0x0000 }, + { 0x0504, 0x0000 }, + { 0x0505, 0x0000 }, + { 0x0520, 0x0000 }, + { 0x0521, 0x0000 }, + { 0x0522, 0x0000 }, + { 0x0523, 0x0000 }, + { 0x0524, 0x0000 }, + { 0x0525, 0x0000 }, + { 0x0526, 0x0000 }, + { 0x0527, 0x0000 }, + { 0x0530, 0x0000 }, + { 0x0531, 0x0000 }, + { 0x0532, 0x0000 }, + { 0x0533, 0x0000 }, + { 0x0534, 0x0000 }, + { 0x0535, 0x0000 }, + { 0x0536, 0x0000 }, + { 0x0537, 0x0000 }, + { 0x0600, 0x0000 }, + { 0x0601, 0x0000 }, + { 0x0602, 0x0000 }, + { 0x0603, 0x0000 }, + { 0x0604, 0x0000 }, + { 0x0605, 0x0000 }, + { 0x0620, 0x0000 }, + { 0x0621, 0x0000 }, + { 0x0622, 0x0000 }, + { 0x0623, 0x0000 }, + { 0x0624, 0x0000 }, + { 0x0625, 0x0000 }, + { 0x0626, 0x0000 }, + { 0x0627, 0x0000 }, + { 0x0630, 0x0000 }, + { 0x0631, 0x0000 }, + { 0x0632, 0x0000 }, + { 0x0633, 0x0000 }, + { 0x0634, 0x0000 }, + { 0x0635, 0x0000 }, + { 0x0636, 0x0000 }, + { 0x0637, 0x0000 }, + { 0x0700, 0x0000 }, + { 0x0701, 0x0000 }, + { 0x0702, 0x0000 }, + { 0x0703, 0x0000 }, + { 0x0704, 0x0000 }, + { 0x0705, 0x0000 }, + { 0x0720, 0x0000 }, + { 0x0721, 0x0000 }, + { 0x0722, 0x0000 }, + { 0x0723, 0x0000 }, + { 0x0724, 0x0000 }, + { 0x0725, 0x0000 }, + { 0x0726, 0x0000 }, + { 0x0727, 0x0000 }, + { 0x0730, 0x0000 }, + { 0x0731, 0x0000 }, + { 0x0732, 0x0000 }, + { 0x0733, 0x0000 }, + { 0x0734, 0x0000 }, + { 0x0735, 0x0000 }, + { 0x0736, 0x0000 }, + { 0x0737, 0x0000 }, + { 0x0800, 0x0000 }, + { 0x0801, 0x0000 }, + { 0x0802, 0x0000 }, + { 0x0803, 0x0000 }, + { 0x0804, 0x0000 }, + { 0x0805, 0x0000 }, + { 0x0820, 0x0000 }, + { 0x0821, 0x0000 }, + { 0x0822, 0x0000 }, + { 0x0823, 0x0000 }, + { 0x0824, 0x0000 }, + { 0x0825, 0x0000 }, + { 0x0826, 0x0000 }, + { 0x0827, 0x0000 }, + { 0x0830, 0x0000 }, + { 0x0831, 0x0000 }, + { 0x0832, 0x0000 }, + { 0x0833, 0x0000 }, + { 0x0834, 0x0000 }, + { 0x0835, 0x0000 }, + { 0x0836, 0x0000 }, + { 0x0837, 0x0000 }, + { 0x0f00, 0x0000 }, + { 0x0f01, 0x0000 }, + { 0x0f02, 0x0000 }, + { 0x0f03, 0x0000 }, + { 0x0f04, 0x0000 }, + { 0x0f05, 0x0000 }, + { 0x0f20, 0x0000 }, + { 0x0f21, 0x0000 }, + { 0x0f22, 0x0000 }, + { 0x0f23, 0x0000 }, + { 0x0f24, 0x0000 }, + { 0x0f25, 0x0000 }, + { 0x0f26, 0x0000 }, + { 0x0f27, 0x0000 }, + { 0x0f30, 0x0000 }, + { 0x0f31, 0x0000 }, + { 0x0f32, 0x0000 }, + { 0x0f33, 0x0000 }, + { 0x0f34, 0x0000 }, + { 0x0f35, 0x0000 }, + { 0x0f36, 0x0000 }, + { 0x0f37, 0x0000 }, + { 0x2000, 0x0000 }, + { 0x2001, 0x0000 }, + { 0x2002, 0x0000 }, + { 0x2003, 0x0000 }, + { 0x2004, 0x0000 }, + { 0x2005, 0x0000 }, + { 0x2006, 0x0000 }, + { 0x2007, 0x0000 }, + { 0x2008, 0x0000 }, + { 0x2009, 0x0003 }, + { 0x200a, 0x0003 }, + { 0x200b, 0x0000 }, + { 0x200c, 0x0000 }, + { 0x200d, 0x0000 }, + { 0x200e, 0x0000 }, + { 0x2012, 0x0000 }, + { 0x2013, 0x0000 }, + { 0x2014, 0x0000 }, + { 0x2015, 0x0000 }, + { 0x2016, 0x0000 }, + { 0x201a, 0x0000 }, + { 0x201b, 0x0000 }, + { 0x201c, 0x0000 }, + { 0x201d, 0x0000 }, + { 0x201e, 0x0000 }, + { 0x201f, 0x0000 }, + { 0x2020, 0x0000 }, + { 0x2021, 0x0000 }, + { 0x2022, 0x0000 }, + { 0x2023, 0x0000 }, + { 0x2024, 0x0000 }, + { 0x2025, 0x0002 }, + { 0x2026, 0x0000 }, + { 0x2027, 0x0000 }, + { 0x2029, 0x0000 }, + { 0x202a, 0x0000 }, + { 0x202d, 0x0000 }, + { 0x202e, 0x0000 }, + { 0x202f, 0x0000 }, + { 0x2030, 0x0000 }, + { 0x2031, 0x0000 }, + { 0x2032, 0x0000 }, + { 0x2033, 0x0000 }, + { 0x2034, 0x0000 }, + { 0x2100, 0x0000 }, + { 0x2101, 0x0000 }, + { 0x2102, 0x0000 }, + { 0x2103, 0x0000 }, + { 0x2104, 0x0000 }, + { 0x2105, 0x0000 }, + { 0x2106, 0x0000 }, + { 0x2107, 0x0000 }, + { 0x2108, 0x0000 }, + { 0x2109, 0x0000 }, + { 0x210a, 0x0000 }, + { 0x210b, 0x0000 }, + { 0x210c, 0x0000 }, + { 0x210d, 0x0000 }, + { 0x210e, 0x0000 }, + { 0x210f, 0x0000 }, + { 0x2110, 0x0000 }, + { 0x2111, 0x0000 }, + { 0x2112, 0x0000 }, + { 0x2113, 0x0000 }, + { 0x2114, 0x0000 }, + { 0x2115, 0x0000 }, + { 0x2116, 0x0000 }, + { 0x2117, 0x0000 }, + { 0x2118, 0x0000 }, + { 0x2119, 0x0000 }, + { 0x211a, 0x0000 }, + { 0x211b, 0x0000 }, + { 0x211c, 0x0000 }, + { 0x211d, 0x0000 }, + { 0x211e, 0x0000 }, + { 0x211f, 0x0000 }, + { 0x2120, 0x0000 }, + { 0x2121, 0x0000 }, + { 0x2122, 0x0000 }, + { 0x2123, 0x0000 }, + { 0x2124, 0x0000 }, + { 0x2125, 0x0000 }, + { 0x2126, 0x0000 }, + { 0x2127, 0x0000 }, + { 0x2128, 0x0000 }, + { 0x2129, 0x0000 }, + { 0x212a, 0x0000 }, + { 0x212b, 0x0000 }, + { 0x212c, 0x0000 }, + { 0x212d, 0x0000 }, + { 0x212e, 0x0000 }, + { 0x212f, 0x0000 }, + { 0x2130, 0x0000 }, + { 0x2131, 0x0000 }, + { 0x2132, 0x0000 }, + { 0x2133, 0x0000 }, + { 0x2134, 0x0000 }, + { 0x2135, 0x0000 }, + { 0x2136, 0x0000 }, + { 0x2137, 0x0000 }, + { 0x2138, 0x0000 }, + { 0x2139, 0x0000 }, + { 0x213a, 0x0000 }, + { 0x213b, 0x0000 }, + { 0x213c, 0x0000 }, + { 0x213d, 0x0000 }, + { 0x213e, 0x0000 }, + { 0x213f, 0x0000 }, + { 0x2200, 0x0000 }, + { 0x2201, 0x0000 }, + { 0x2202, 0x0000 }, + { 0x2203, 0x0000 }, + { 0x2204, 0x0000 }, + { 0x2206, 0x0000 }, + { 0x2207, 0x0000 }, + { 0x2208, 0x0000 }, + { 0x2209, 0x0000 }, + { 0x220a, 0x0000 }, + { 0x220b, 0x0000 }, + { 0x220c, 0x0000 }, + { 0x220d, 0x0000 }, + { 0x220e, 0x0000 }, + { 0x220f, 0x0000 }, + { 0x2211, 0x0000 }, + { 0x2212, 0x0000 }, + { 0x2220, 0x0000 }, + { 0x2221, 0x0000 }, + { 0x2222, 0x0000 }, + { 0x2223, 0x0000 }, + { 0x2230, 0x0000 }, + { 0x2231, 0x0000 }, + { 0x3000, 0x0000 }, + { 0x3001, 0x0000 }, + { 0x3002, 0x0000 }, + { 0x3003, 0x0000 }, + { 0x3004, 0x0000 }, + { 0x3005, 0x0000 }, + { 0x3006, 0x0000 }, + { 0x3007, 0x0000 }, + { 0x3008, 0x0000 }, + { 0x3009, 0x0000 }, + { 0x300a, 0x0000 }, + { 0x300b, 0x0000 }, + { 0x300c, 0x0000 }, + { 0x300d, 0x0000 }, + { 0x300e, 0x0000 }, + { 0x300f, 0x0000 }, + { 0x3010, 0x0000 }, + { 0x3011, 0x0000 }, + { 0x3012, 0x0000 }, + { 0x3013, 0x0000 }, + { 0x3014, 0x0000 }, + { 0x3015, 0x0000 }, + { 0x3016, 0x0000 }, + { 0x3017, 0x0000 }, + { 0x3018, 0x0000 }, + { 0x3019, 0x0000 }, + { 0x301a, 0x0000 }, + { 0x301b, 0x0000 }, + { 0x301c, 0x0000 }, + { 0x301d, 0x0000 }, + { 0x301e, 0x0000 }, + { 0x301f, 0x0000 }, + { 0x3020, 0x0000 }, + { 0x3021, 0x0000 }, + { 0x3022, 0x0000 }, + { 0x3023, 0x0000 }, + { 0x3024, 0x0000 }, + { 0x3025, 0x0000 }, + { 0x3026, 0x0000 }, + { 0x3027, 0x0000 }, + { 0x3028, 0x0000 }, + { 0x3029, 0x0000 }, + { 0x302a, 0x0000 }, + { 0x302b, 0x0000 }, + { 0x302c, 0x0000 }, + { 0x302d, 0x0000 }, + { 0x302e, 0x0000 }, + { 0x302f, 0x0000 }, + { 0x3030, 0x0000 }, + { 0x3031, 0x0000 }, + { 0x3032, 0x0000 }, + { 0x3033, 0x0000 }, + { 0x3034, 0x0000 }, + { 0x3035, 0x0000 }, + { 0x3036, 0x0000 }, + { 0x3037, 0x0000 }, + { 0x3038, 0x0000 }, + { 0x3039, 0x0000 }, + { 0x303a, 0x0000 }, + { 0x303b, 0x0000 }, + { 0x303c, 0x0000 }, + { 0x303d, 0x0000 }, + { 0x303e, 0x0000 }, + { 0x303f, 0x0000 }, + { 0x3040, 0x0000 }, + { 0x3041, 0x0000 }, + { 0x3042, 0x0000 }, + { 0x3043, 0x0000 }, + { 0x3044, 0x0000 }, + { 0x3045, 0x0000 }, + { 0x3046, 0x0000 }, + { 0x3047, 0x0000 }, + { 0x3048, 0x0000 }, + { 0x3049, 0x0000 }, + { 0x304a, 0x0000 }, + { 0x304b, 0x0000 }, + { 0x304c, 0x0000 }, + { 0x304d, 0x0000 }, + { 0x304e, 0x0000 }, + { 0x304f, 0x0000 }, + { 0x3050, 0x0000 }, + { 0x3051, 0x0000 }, + { 0x3052, 0x0000 }, + { 0x3053, 0x0000 }, + { 0x3054, 0x0000 }, + { 0x3055, 0x0000 }, + { 0x3056, 0x0000 }, + { 0x3057, 0x0000 }, + { 0x3058, 0x0000 }, + { 0x3059, 0x0000 }, + { 0x305a, 0x0000 }, + { 0x305b, 0x0000 }, + { 0x305c, 0x0000 }, + { 0x305d, 0x0000 }, + { 0x305e, 0x0000 }, + { 0x305f, 0x0000 }, + { 0x3060, 0x0000 }, + { 0x3061, 0x0000 }, + { 0x3062, 0x0000 }, + { 0x3063, 0x0000 }, + { 0x3064, 0x0000 }, + { 0x3065, 0x0000 }, + { 0x3066, 0x0000 }, + { 0x3067, 0x0000 }, + { 0x3068, 0x0000 }, + { 0x3069, 0x0000 }, + { 0x306a, 0x0000 }, + { 0x306b, 0x0000 }, + { 0x306c, 0x0000 }, + { 0x306d, 0x0000 }, + { 0x306e, 0x0000 }, + { 0x306f, 0x0000 }, + { 0x3070, 0x0000 }, + { 0x3071, 0x0000 }, + { 0x3072, 0x0000 }, + { 0x3073, 0x0000 }, + { 0x3074, 0x0000 }, + { 0x3075, 0x0000 }, + { 0x3076, 0x0000 }, + { 0x3077, 0x0000 }, + { 0x3078, 0x0000 }, + { 0x3079, 0x0000 }, + { 0x307a, 0x0000 }, + { 0x307b, 0x0000 }, + { 0x307c, 0x0000 }, + { 0x307d, 0x0000 }, + { 0x307e, 0x0000 }, + { 0x307f, 0x0000 }, + { 0x3080, 0x0000 }, + { 0x3081, 0x0000 }, + { 0x3082, 0x0000 }, + { 0x3083, 0x0000 }, + { 0x3084, 0x0000 }, + { 0x3085, 0x0000 }, + { 0x3086, 0x0000 }, + { 0x3087, 0x0000 }, + { 0x3088, 0x0000 }, + { 0x3089, 0x0000 }, + { 0x308a, 0x0000 }, + { 0x308b, 0x0000 }, + { 0x308c, 0x0000 }, + { 0x308d, 0x0000 }, + { 0x308e, 0x0000 }, + { 0x308f, 0x0000 }, + { 0x3090, 0x0000 }, + { 0x3091, 0x0000 }, + { 0x3092, 0x0000 }, + { 0x3093, 0x0000 }, + { 0x3094, 0x0000 }, + { 0x3095, 0x0000 }, + { 0x3096, 0x0000 }, + { 0x3097, 0x0000 }, + { 0x3098, 0x0000 }, + { 0x3099, 0x0000 }, + { 0x309a, 0x0000 }, + { 0x309b, 0x0000 }, + { 0x309c, 0x0000 }, + { 0x309d, 0x0000 }, + { 0x309e, 0x0000 }, + { 0x309f, 0x0000 }, + { 0x30a0, 0x0000 }, + { 0x30a1, 0x0000 }, + { 0x30a2, 0x0000 }, + { 0x30a3, 0x0000 }, + { 0x30a4, 0x0000 }, + { 0x30a5, 0x0000 }, + { 0x30a6, 0x0000 }, + { 0x30a7, 0x0000 }, + { 0x30a8, 0x0000 }, + { 0x30a9, 0x0000 }, + { 0x30aa, 0x0000 }, + { 0x30ab, 0x0000 }, + { 0x30ac, 0x0000 }, + { 0x30ad, 0x0000 }, + { 0x30ae, 0x0000 }, + { 0x30af, 0x0000 }, + { 0x30b0, 0x0000 }, + { 0x30b1, 0x0000 }, + { 0x30b2, 0x0000 }, + { 0x30b3, 0x0000 }, + { 0x30b4, 0x0000 }, + { 0x30b5, 0x0000 }, + { 0x30b6, 0x0000 }, + { 0x30b7, 0x0000 }, + { 0x30b8, 0x0000 }, + { 0x30b9, 0x0000 }, + { 0x30ba, 0x0000 }, + { 0x30bb, 0x0000 }, + { 0x30bc, 0x0000 }, + { 0x30bd, 0x0000 }, + { 0x30be, 0x0000 }, + { 0x30bf, 0x0000 }, + { 0x30c0, 0x0000 }, + { 0x30c1, 0x0000 }, + { 0x30c2, 0x0000 }, + { 0x30c3, 0x0000 }, + { 0x30c4, 0x0000 }, + { 0x30c5, 0x0000 }, + { 0x30c6, 0x0000 }, + { 0x30c7, 0x0000 }, + { 0x30c8, 0x0000 }, + { 0x30c9, 0x0000 }, + { 0x30ca, 0x0000 }, + { 0x30cb, 0x0000 }, + { 0x30cc, 0x0000 }, + { 0x30cd, 0x0000 }, + { 0x30ce, 0x0000 }, + { 0x30cf, 0x0000 }, + { 0x30d0, 0x0000 }, + { 0x30d1, 0x0000 }, + { 0x30d2, 0x0000 }, + { 0x30d3, 0x0000 }, + { 0x30d4, 0x0000 }, + { 0x30d5, 0x0000 }, + { 0x30d6, 0x0000 }, + { 0x30d7, 0x0000 }, + { 0x30d8, 0x0000 }, + { 0x30d9, 0x0000 }, + { 0x30da, 0x0000 }, + { 0x30db, 0x0000 }, + { 0x30dc, 0x0000 }, + { 0x30dd, 0x0000 }, + { 0x30de, 0x0000 }, + { 0x30df, 0x0000 }, + { 0x30e0, 0x0000 }, + { 0x30e1, 0x0000 }, + { 0x30e2, 0x0000 }, + { 0x30e3, 0x0000 }, + { 0x30e4, 0x0000 }, + { 0x30e5, 0x0000 }, + { 0x30e6, 0x0000 }, + { 0x30e7, 0x0000 }, + { 0x30e8, 0x0000 }, + { 0x30e9, 0x0000 }, + { 0x30ea, 0x0000 }, + { 0x30eb, 0x0000 }, + { 0x30ec, 0x0000 }, + { 0x30ed, 0x0000 }, + { 0x30ee, 0x0000 }, + { 0x30ef, 0x0000 }, + { 0x30f0, 0x0000 }, + { 0x30f1, 0x0000 }, + { 0x30f2, 0x0000 }, + { 0x30f3, 0x0000 }, + { 0x30f4, 0x0000 }, + { 0x30f5, 0x0000 }, + { 0x30f6, 0x0000 }, + { 0x30f7, 0x0000 }, + { 0x30f8, 0x0000 }, + { 0x30f9, 0x0000 }, + { 0x30fa, 0x0000 }, + { 0x30fb, 0x0000 }, + { 0x30fc, 0x0000 }, + { 0x30fd, 0x0000 }, + { 0x30fe, 0x0000 }, + { 0x30ff, 0x0000 }, + { 0x3100, 0x0000 }, + { 0x3101, 0x0000 }, + { 0x3102, 0x0000 }, + { 0x3103, 0x0000 }, + { 0x3104, 0x0000 }, + { 0x3105, 0x0000 }, + { 0x3106, 0x0000 }, + { 0x3107, 0x0000 }, + { 0x3108, 0x0000 }, + { 0x3109, 0x0000 }, + { 0x310a, 0x0000 }, + { 0x310b, 0x0000 }, + { 0x310c, 0x0000 }, + { 0x310d, 0x0000 }, + { 0x310e, 0x0000 }, + { 0x310f, 0x0000 }, + { 0x3110, 0x0000 }, + { 0x3111, 0x0000 }, + { 0x3112, 0x0000 }, + { 0x3113, 0x0000 }, + { 0x3114, 0x0000 }, + { 0x3115, 0x0000 }, + { 0x3116, 0x0000 }, + { 0x3117, 0x0000 }, + { 0x3118, 0x0000 }, + { 0x3119, 0x0000 }, + { 0x311a, 0x0000 }, + { 0x311b, 0x0000 }, + { 0x311c, 0x0000 }, + { 0x311d, 0x0000 }, + { 0x311e, 0x0000 }, + { 0x311f, 0x0000 }, + { 0x3120, 0x0000 }, + { 0x3121, 0x0000 }, + { 0x3122, 0x0000 }, + { 0x3123, 0x0000 }, + { 0x3124, 0x0000 }, + { 0x3125, 0x0000 }, + { 0x3126, 0x0000 }, + { 0x3127, 0x0000 }, + { 0x3128, 0x0000 }, + { 0x3129, 0x0000 }, + { 0x312a, 0x0000 }, + { 0x312b, 0x0000 }, + { 0x312c, 0x0000 }, + { 0x312d, 0x0000 }, + { 0x312e, 0x0000 }, + { 0x312f, 0x0000 }, + { 0x3130, 0x0000 }, + { 0x3131, 0x0000 }, + { 0x3132, 0x0000 }, + { 0x3133, 0x0000 }, + { 0x3134, 0x0000 }, + { 0x3135, 0x0000 }, + { 0x3136, 0x0000 }, + { 0x3137, 0x0000 }, + { 0x3138, 0x0000 }, + { 0x3139, 0x0000 }, + { 0x313a, 0x0000 }, + { 0x313b, 0x0000 }, + { 0x313c, 0x0000 }, + { 0x313d, 0x0000 }, + { 0x313e, 0x0000 }, + { 0x313f, 0x0000 }, + { 0x3140, 0x0000 }, + { 0x3141, 0x0000 }, + { 0x3142, 0x0000 }, + { 0x3143, 0x0000 }, + { 0x3144, 0x0000 }, + { 0x3145, 0x0000 }, + { 0x3146, 0x0000 }, + { 0x3147, 0x0000 }, + { 0x3148, 0x0000 }, + { 0x3149, 0x0000 }, + { 0x314a, 0x0000 }, + { 0x314b, 0x0000 }, + { 0x314c, 0x0000 }, + { 0x314d, 0x0000 }, + { 0x314e, 0x0000 }, + { 0x314f, 0x0000 }, + { 0x3150, 0x0000 }, + { 0x3151, 0x0000 }, + { 0x3152, 0x0000 }, + { 0x3153, 0x0000 }, + { 0x3154, 0x0000 }, + { 0x3155, 0x0000 }, + { 0x3156, 0x0000 }, + { 0x3157, 0x0000 }, + { 0x3158, 0x0000 }, + { 0x3159, 0x0000 }, + { 0x315a, 0x0000 }, + { 0x315b, 0x0000 }, + { 0x315c, 0x0000 }, + { 0x315d, 0x0000 }, + { 0x315e, 0x0000 }, + { 0x315f, 0x0000 }, + { 0x3160, 0x0000 }, + { 0x3161, 0x0000 }, + { 0x3162, 0x0000 }, + { 0x3163, 0x0000 }, + { 0x3164, 0x0000 }, + { 0x3165, 0x0000 }, + { 0x3166, 0x0000 }, + { 0x3167, 0x0000 }, + { 0x3168, 0x0000 }, + { 0x3169, 0x0000 }, + { 0x316a, 0x0000 }, + { 0x316b, 0x0000 }, + { 0x316c, 0x0000 }, + { 0x316d, 0x0000 }, + { 0x316e, 0x0000 }, + { 0x316f, 0x0000 }, + { 0x3170, 0x0000 }, + { 0x3171, 0x0000 }, + { 0x3172, 0x0000 }, + { 0x3173, 0x0000 }, + { 0x3174, 0x0000 }, + { 0x3175, 0x0000 }, + { 0x3176, 0x0000 }, + { 0x3177, 0x0000 }, + { 0x3178, 0x0000 }, + { 0x3179, 0x0000 }, + { 0x317a, 0x0000 }, + { 0x317b, 0x0000 }, + { 0x317c, 0x0000 }, + { 0x317d, 0x0000 }, + { 0x317e, 0x0000 }, + { 0x317f, 0x0000 }, + { 0x3180, 0x0000 }, + { 0x3181, 0x0000 }, + { 0x3182, 0x0000 }, + { 0x3183, 0x0000 }, + { 0x3184, 0x0000 }, + { 0x3185, 0x0000 }, + { 0x3186, 0x0000 }, + { 0x3187, 0x0000 }, + { 0x3188, 0x0000 }, + { 0x3189, 0x0000 }, + { 0x318a, 0x0000 }, + { 0x318b, 0x0000 }, + { 0x318c, 0x0000 }, + { 0x318d, 0x0000 }, + { 0x318e, 0x0000 }, + { 0x318f, 0x0000 }, + { 0x3190, 0x0000 }, + { 0x3191, 0x0000 }, + { 0x3192, 0x0000 }, + { 0x3193, 0x0000 }, + { 0x3194, 0x0000 }, + { 0x3195, 0x0000 }, + { 0x3196, 0x0000 }, + { 0x3197, 0x0000 }, + { 0x3198, 0x0000 }, + { 0x3199, 0x0000 }, + { 0x319a, 0x0000 }, + { 0x319b, 0x0000 }, + { 0x319c, 0x0000 }, + { 0x319d, 0x0000 }, + { 0x319e, 0x0000 }, + { 0x319f, 0x0000 }, + { 0x31a0, 0x0000 }, + { 0x31a1, 0x0000 }, + { 0x31a2, 0x0000 }, + { 0x31a3, 0x0000 }, + { 0x31a4, 0x0000 }, + { 0x31a5, 0x0000 }, + { 0x31a6, 0x0000 }, + { 0x31a7, 0x0000 }, + { 0x31a8, 0x0000 }, + { 0x31a9, 0x0000 }, + { 0x31aa, 0x0000 }, + { 0x31ab, 0x0000 }, + { 0x31ac, 0x0000 }, + { 0x31ad, 0x0000 }, + { 0x31ae, 0x0000 }, + { 0x31af, 0x0000 }, + { 0x31b0, 0x0000 }, + { 0x31b1, 0x0000 }, + { 0x31b2, 0x0000 }, + { 0x31b3, 0x0000 }, + { 0x31b4, 0x0000 }, + { 0x31b5, 0x0000 }, + { 0x31b6, 0x0000 }, + { 0x31b7, 0x0000 }, + { 0x31b8, 0x0000 }, + { 0x31b9, 0x0000 }, + { 0x31ba, 0x0000 }, + { 0x31bb, 0x0000 }, + { 0x31bc, 0x0000 }, + { 0x31bd, 0x0000 }, + { 0x31be, 0x0000 }, + { 0x31bf, 0x0000 }, + { 0x31c0, 0x0000 }, + { 0x31c1, 0x0000 }, + { 0x31c2, 0x0000 }, + { 0x31c3, 0x0000 }, + { 0x31c4, 0x0000 }, + { 0x31c5, 0x0000 }, + { 0x31c6, 0x0000 }, + { 0x31c7, 0x0000 }, + { 0x31c8, 0x0000 }, + { 0x31c9, 0x0000 }, + { 0x31ca, 0x0000 }, + { 0x31cb, 0x0000 }, + { 0x31cc, 0x0000 }, + { 0x31cd, 0x0000 }, + { 0x31ce, 0x0000 }, + { 0x31cf, 0x0000 }, + { 0x31d0, 0x0000 }, + { 0x31d1, 0x0000 }, + { 0x31d2, 0x0000 }, + { 0x31d3, 0x0000 }, + { 0x31d4, 0x0000 }, + { 0x31d5, 0x0000 }, + { 0x31d6, 0x0000 }, + { 0x31d7, 0x0000 }, + { 0x31d8, 0x0000 }, + { 0x31d9, 0x0000 }, + { 0x31da, 0x0000 }, + { 0x31db, 0x0000 }, + { 0x31dc, 0x0000 }, + { 0x31dd, 0x0000 }, + { 0x31de, 0x0000 }, + { 0x31df, 0x0000 }, + { 0x31e0, 0x0000 }, + { 0x31e1, 0x0000 }, + { 0x31e2, 0x0000 }, + { 0x31e3, 0x0000 }, + { 0x31e4, 0x0000 }, + { 0x31e5, 0x0000 }, + { 0x31e6, 0x0000 }, + { 0x31e7, 0x0000 }, + { 0x31e8, 0x0000 }, + { 0x31e9, 0x0000 }, + { 0x31ea, 0x0000 }, + { 0x31eb, 0x0000 }, + { 0x31ec, 0x0000 }, + { 0x31ed, 0x0000 }, + { 0x31ee, 0x0000 }, + { 0x31ef, 0x0000 }, + { 0x31f0, 0x0000 }, + { 0x31f1, 0x0000 }, + { 0x31f2, 0x0000 }, + { 0x31f3, 0x0000 }, + { 0x31f4, 0x0000 }, + { 0x31f5, 0x0000 }, + { 0x31f6, 0x0000 }, + { 0x31f7, 0x0000 }, + { 0x31f8, 0x0000 }, + { 0x31f9, 0x0000 }, + { 0x31fa, 0x0000 }, + { 0x31fb, 0x0000 }, + { 0x31fc, 0x0000 }, + { 0x31fd, 0x0000 }, + { 0x31fe, 0x0000 }, + { 0x31ff, 0x0000 }, + { 0x3200, 0x0000 }, + { 0x3201, 0x0000 }, + { 0x3202, 0x0000 }, + { 0x3203, 0x0000 }, + { 0x3204, 0x0000 }, + { 0x3205, 0x0000 }, + { 0x3206, 0x0000 }, + { 0x3207, 0x0000 }, + { 0x3208, 0x0000 }, + { 0x3209, 0x0000 }, + { 0x320a, 0x0000 }, + { 0x320b, 0x0000 }, + { 0x320c, 0x0000 }, + { 0x320d, 0x0000 }, + { 0x320e, 0x0000 }, + { 0x320f, 0x0000 }, + { 0x3210, 0x0000 }, + { 0x3211, 0x0000 }, + { 0x3212, 0x0000 }, + { 0x3213, 0x0000 }, + { 0x3214, 0x0000 }, + { 0x3215, 0x0000 }, + { 0x3216, 0x0000 }, + { 0x3217, 0x0000 }, + { 0x3218, 0x0000 }, + { 0x3219, 0x0000 }, + { 0x321a, 0x0000 }, + { 0x321b, 0x0000 }, + { 0x321c, 0x0000 }, + { 0x321d, 0x0000 }, + { 0x321e, 0x0000 }, + { 0x321f, 0x0000 }, + { 0x3220, 0x0000 }, + { 0x3221, 0x0000 }, + { 0x3222, 0x0000 }, + { 0x3223, 0x0000 }, + { 0x3224, 0x0000 }, + { 0x3225, 0x0000 }, + { 0x3226, 0x0000 }, + { 0x3227, 0x0000 }, + { 0x3228, 0x0000 }, + { 0x3229, 0x0000 }, + { 0x322a, 0x0000 }, + { 0x322b, 0x0000 }, + { 0x322c, 0x0000 }, + { 0x322d, 0x0000 }, + { 0x322e, 0x0000 }, + { 0x322f, 0x0000 }, + { 0x3230, 0x0000 }, + { 0x3231, 0x0000 }, + { 0x3232, 0x0000 }, + { 0x3233, 0x0000 }, + { 0x3234, 0x0000 }, + { 0x3235, 0x0000 }, + { 0x3236, 0x0000 }, + { 0x3237, 0x0000 }, + { 0x3238, 0x0000 }, + { 0x3239, 0x0000 }, + { 0x323a, 0x0000 }, + { 0x323b, 0x0000 }, + { 0x323c, 0x0000 }, + { 0x323d, 0x0000 }, + { 0x323e, 0x0000 }, + { 0x323f, 0x0000 }, + { 0x3240, 0x0000 }, + { 0x3241, 0x0000 }, + { 0x3242, 0x0000 }, + { 0x3243, 0x0000 }, + { 0x3244, 0x0000 }, + { 0x3245, 0x0000 }, + { 0x3246, 0x0000 }, + { 0x3247, 0x0000 }, + { 0x3248, 0x0000 }, + { 0x3249, 0x0000 }, + { 0x324a, 0x0000 }, + { 0x324b, 0x0000 }, + { 0x324c, 0x0000 }, + { 0x324d, 0x0000 }, + { 0x324e, 0x0000 }, + { 0x324f, 0x0000 }, + { 0x3250, 0x0000 }, + { 0x3251, 0x0000 }, + { 0x3252, 0x0000 }, + { 0x3253, 0x0000 }, + { 0x3254, 0x0000 }, + { 0x3255, 0x0000 }, + { 0x3256, 0x0000 }, + { 0x3257, 0x0000 }, + { 0x3258, 0x0000 }, + { 0x3259, 0x0000 }, + { 0x325a, 0x0000 }, + { 0x325b, 0x0000 }, + { 0x325c, 0x0000 }, + { 0x325d, 0x0000 }, + { 0x325e, 0x0000 }, + { 0x325f, 0x0000 }, + { 0x3260, 0x0000 }, + { 0x3261, 0x0000 }, + { 0x3262, 0x0000 }, + { 0x3263, 0x0000 }, + { 0x3264, 0x0000 }, + { 0x3265, 0x0000 }, + { 0x3266, 0x0000 }, + { 0x3267, 0x0000 }, + { 0x3268, 0x0000 }, + { 0x3269, 0x0000 }, + { 0x326a, 0x0000 }, + { 0x326b, 0x0000 }, + { 0x326c, 0x0000 }, + { 0x326d, 0x0000 }, + { 0x326e, 0x0000 }, + { 0x326f, 0x0000 }, + { 0x3270, 0x0000 }, + { 0x3271, 0x0000 }, + { 0x3272, 0x0000 }, + { 0x3273, 0x0000 }, + { 0x3274, 0x0000 }, + { 0x3275, 0x0000 }, + { 0x3276, 0x0000 }, + { 0x3277, 0x0000 }, + { 0x3278, 0x0000 }, + { 0x3279, 0x0000 }, + { 0x327a, 0x0000 }, + { 0x327b, 0x0000 }, + { 0x327c, 0x0000 }, + { 0x327d, 0x0000 }, + { 0x327e, 0x0000 }, + { 0x327f, 0x0000 }, + { 0x3280, 0x0000 }, + { 0x3281, 0x0000 }, + { 0x3282, 0x0000 }, + { 0x3283, 0x0000 }, + { 0x3284, 0x0000 }, + { 0x3285, 0x0000 }, + { 0x3286, 0x0000 }, + { 0x3287, 0x0000 }, + { 0x3288, 0x0000 }, + { 0x3289, 0x0000 }, + { 0x328a, 0x0000 }, + { 0x328b, 0x0000 }, + { 0x328c, 0x0000 }, + { 0x328d, 0x0000 }, + { 0x328e, 0x0000 }, + { 0x328f, 0x0000 }, + { 0x3290, 0x0000 }, + { 0x3291, 0x0000 }, + { 0x3292, 0x0000 }, + { 0x3293, 0x0000 }, + { 0x3294, 0x0000 }, + { 0x3295, 0x0000 }, + { 0x3296, 0x0000 }, + { 0x3297, 0x0000 }, + { 0x3298, 0x0000 }, + { 0x3299, 0x0000 }, + { 0x329a, 0x0000 }, + { 0x329b, 0x0000 }, + { 0x329c, 0x0000 }, + { 0x329d, 0x0000 }, + { 0x329e, 0x0000 }, + { 0x329f, 0x0000 }, + { 0x32a0, 0x0000 }, + { 0x32a1, 0x0000 }, + { 0x32a2, 0x0000 }, + { 0x32a3, 0x0000 }, + { 0x32a4, 0x0000 }, + { 0x32a5, 0x0000 }, + { 0x32a6, 0x0000 }, + { 0x32a7, 0x0000 }, + { 0x32a8, 0x0000 }, + { 0x32a9, 0x0000 }, + { 0x32aa, 0x0000 }, + { 0x32ab, 0x0000 }, + { 0x32ac, 0x0000 }, + { 0x32ad, 0x0000 }, + { 0x32ae, 0x0000 }, + { 0x32af, 0x0000 }, + { 0x32b0, 0x0000 }, + { 0x32b1, 0x0000 }, + { 0x32b2, 0x0000 }, + { 0x32b3, 0x0000 }, + { 0x32b4, 0x0000 }, + { 0x32b5, 0x0000 }, + { 0x32b6, 0x0000 }, + { 0x32b7, 0x0000 }, + { 0x32b8, 0x0000 }, + { 0x32b9, 0x0000 }, + { 0x32ba, 0x0000 }, + { 0x32bb, 0x0000 }, + { 0x32bc, 0x0000 }, + { 0x32bd, 0x0000 }, + { 0x32be, 0x0000 }, + { 0x32bf, 0x0000 }, + { 0x32c0, 0x0000 }, + { 0x32c1, 0x0000 }, + { 0x32c2, 0x0000 }, + { 0x32c3, 0x0000 }, + { 0x32c4, 0x0000 }, + { 0x32c5, 0x0000 }, + { 0x32c6, 0x0000 }, + { 0x32c7, 0x0000 }, + { 0x32c8, 0x0000 }, + { 0x32c9, 0x0000 }, + { 0x32ca, 0x0000 }, + { 0x32cb, 0x0000 }, + { 0x32cc, 0x0000 }, + { 0x32cd, 0x0000 }, + { 0x32ce, 0x0000 }, + { 0x32cf, 0x0000 }, + { 0x32d0, 0x0000 }, + { 0x32d1, 0x0000 }, + { 0x32d2, 0x0000 }, + { 0x32d3, 0x0000 }, + { 0x32d4, 0x0000 }, + { 0x32d5, 0x0000 }, + { 0x32d6, 0x0000 }, + { 0x32d7, 0x0000 }, + { 0x32d8, 0x0000 }, + { 0x32d9, 0x0000 }, + { 0x32da, 0x0000 }, + { 0x32db, 0x0000 }, + { 0x32dc, 0x0000 }, + { 0x32dd, 0x0000 }, + { 0x32de, 0x0000 }, + { 0x32df, 0x0000 }, + { 0x32e0, 0x0000 }, + { 0x32e1, 0x0000 }, + { 0x32e2, 0x0000 }, + { 0x32e3, 0x0000 }, + { 0x32e4, 0x0000 }, + { 0x32e5, 0x0000 }, + { 0x32e6, 0x0000 }, + { 0x32e7, 0x0000 }, + { 0x32e8, 0x0000 }, + { 0x32e9, 0x0000 }, + { 0x32ea, 0x0000 }, + { 0x32eb, 0x0000 }, + { 0x32ec, 0x0000 }, + { 0x32ed, 0x0000 }, + { 0x32ee, 0x0000 }, + { 0x32ef, 0x0000 }, + { 0x32f0, 0x0000 }, + { 0x32f1, 0x0000 }, + { 0x32f2, 0x0000 }, + { 0x32f3, 0x0000 }, + { 0x32f4, 0x0000 }, + { 0x32f5, 0x0000 }, + { 0x32f6, 0x0000 }, + { 0x32f7, 0x0000 }, + { 0x32f8, 0x0000 }, + { 0x32f9, 0x0000 }, + { 0x32fa, 0x0000 }, + { 0x32fb, 0x0000 }, + { 0x32fc, 0x0000 }, + { 0x32fd, 0x0000 }, + { 0x32fe, 0x0000 }, + { 0x32ff, 0x0000 }, + { 0x3300, 0x0000 }, + { 0x3301, 0x0000 }, + { 0x3302, 0x0000 }, + { 0x3303, 0x0000 }, + { 0x3304, 0x0000 }, + { 0x3305, 0x0000 }, + { 0x3306, 0x0000 }, + { 0x3307, 0x0000 }, + { 0x3308, 0x0000 }, + { 0x3309, 0x0000 }, + { 0x330a, 0x0000 }, + { 0x330b, 0x0000 }, + { 0x330c, 0x0000 }, + { 0x330d, 0x0000 }, + { 0x330e, 0x0000 }, + { 0x330f, 0x0000 }, + { 0x3310, 0x0000 }, + { 0x3311, 0x0000 }, + { 0x3312, 0x0000 }, + { 0x3313, 0x0000 }, + { 0x3314, 0x0000 }, + { 0x3315, 0x0000 }, + { 0x3316, 0x0000 }, + { 0x3317, 0x0000 }, + { 0x3318, 0x0000 }, + { 0x3319, 0x0000 }, + { 0x331a, 0x0000 }, + { 0x331b, 0x0000 }, + { 0x331c, 0x0000 }, + { 0x331d, 0x0000 }, + { 0x331e, 0x0000 }, + { 0x331f, 0x0000 }, + { 0x3320, 0x0000 }, + { 0x3321, 0x0000 }, + { 0x3322, 0x0000 }, + { 0x3323, 0x0000 }, + { 0x3324, 0x0000 }, + { 0x3325, 0x0000 }, + { 0x3326, 0x0000 }, + { 0x3327, 0x0000 }, + { 0x3328, 0x0000 }, + { 0x3329, 0x0000 }, + { 0x332a, 0x0000 }, + { 0x332b, 0x0000 }, + { 0x332c, 0x0000 }, + { 0x332d, 0x0000 }, + { 0x332e, 0x0000 }, + { 0x332f, 0x0000 }, + { 0x3330, 0x0000 }, + { 0x3331, 0x0000 }, + { 0x3332, 0x0000 }, + { 0x3333, 0x0000 }, + { 0x3334, 0x0000 }, + { 0x3335, 0x0000 }, + { 0x3336, 0x0000 }, + { 0x3337, 0x0000 }, + { 0x3338, 0x0000 }, + { 0x3339, 0x0000 }, + { 0x333a, 0x0000 }, + { 0x333b, 0x0000 }, + { 0x333c, 0x0000 }, + { 0x333d, 0x0000 }, + { 0x333e, 0x0000 }, + { 0x333f, 0x0000 }, + { 0x3340, 0x0000 }, + { 0x3341, 0x0000 }, + { 0x3342, 0x0000 }, + { 0x3343, 0x0000 }, + { 0x3344, 0x0000 }, + { 0x3345, 0x0000 }, + { 0x3346, 0x0000 }, + { 0x3347, 0x0000 }, + { 0x3348, 0x0000 }, + { 0x3349, 0x0000 }, + { 0x334a, 0x0000 }, + { 0x334b, 0x0000 }, + { 0x334c, 0x0000 }, + { 0x334d, 0x0000 }, + { 0x334e, 0x0000 }, + { 0x334f, 0x0000 }, + { 0x3350, 0x0000 }, + { 0x3351, 0x0000 }, + { 0x3352, 0x0000 }, + { 0x3353, 0x0000 }, + { 0x3354, 0x0000 }, + { 0x3355, 0x0000 }, + { 0x3356, 0x0000 }, + { 0x3357, 0x0000 }, + { 0x3358, 0x0000 }, + { 0x3359, 0x0000 }, + { 0x335a, 0x0000 }, + { 0x335b, 0x0000 }, + { 0x335c, 0x0000 }, + { 0x335d, 0x0000 }, + { 0x335e, 0x0000 }, + { 0x335f, 0x0000 }, + { 0x3360, 0x0000 }, + { 0x3361, 0x0000 }, + { 0x3362, 0x0000 }, + { 0x3363, 0x0000 }, + { 0x3364, 0x0000 }, + { 0x3365, 0x0000 }, + { 0x3366, 0x0000 }, + { 0x3367, 0x0000 }, + { 0x3368, 0x0000 }, + { 0x3369, 0x0000 }, + { 0x336a, 0x0000 }, + { 0x336b, 0x0000 }, + { 0x336c, 0x0000 }, + { 0x336d, 0x0000 }, + { 0x336e, 0x0000 }, + { 0x336f, 0x0000 }, + { 0x3370, 0x0000 }, + { 0x3371, 0x0000 }, + { 0x3372, 0x0000 }, + { 0x3373, 0x0000 }, + { 0x3374, 0x0000 }, + { 0x3375, 0x0000 }, + { 0x3376, 0x0000 }, + { 0x3377, 0x0000 }, + { 0x3378, 0x0000 }, + { 0x3379, 0x0000 }, + { 0x337a, 0x0000 }, + { 0x337b, 0x0000 }, + { 0x337c, 0x0000 }, + { 0x337d, 0x0000 }, + { 0x337e, 0x0000 }, + { 0x337f, 0x0000 }, + { 0x3380, 0x0000 }, + { 0x3381, 0x0000 }, + { 0x3382, 0x0000 }, + { 0x3383, 0x0000 }, + { 0x3384, 0x0000 }, + { 0x3385, 0x0000 }, + { 0x3386, 0x0000 }, + { 0x3387, 0x0000 }, + { 0x3388, 0x0000 }, + { 0x3389, 0x0000 }, + { 0x338a, 0x0000 }, + { 0x338b, 0x0000 }, + { 0x338c, 0x0000 }, + { 0x338d, 0x0000 }, + { 0x338e, 0x0000 }, + { 0x338f, 0x0000 }, + { 0x3390, 0x0000 }, + { 0x3391, 0x0000 }, + { 0x3392, 0x0000 }, + { 0x3393, 0x0000 }, + { 0x3394, 0x0000 }, + { 0x3395, 0x0000 }, + { 0x3396, 0x0000 }, + { 0x3397, 0x0000 }, + { 0x3398, 0x0000 }, + { 0x3399, 0x0000 }, + { 0x339a, 0x0000 }, + { 0x339b, 0x0000 }, + { 0x339c, 0x0000 }, + { 0x339d, 0x0000 }, + { 0x339e, 0x0000 }, + { 0x339f, 0x0000 }, + { 0x33a0, 0x0000 }, + { 0x33a1, 0x0000 }, + { 0x33a2, 0x0000 }, + { 0x33a3, 0x0000 }, + { 0x33a4, 0x0000 }, + { 0x33a5, 0x0000 }, + { 0x33a6, 0x0000 }, + { 0x33a7, 0x0000 }, + { 0x33a8, 0x0000 }, + { 0x33a9, 0x0000 }, + { 0x33aa, 0x0000 }, + { 0x33ab, 0x0000 }, + { 0x33ac, 0x0000 }, + { 0x33ad, 0x0000 }, + { 0x33ae, 0x0000 }, + { 0x33af, 0x0000 }, + { 0x33b0, 0x0000 }, + { 0x33b1, 0x0000 }, + { 0x33b2, 0x0000 }, + { 0x33b3, 0x0000 }, + { 0x33b4, 0x0000 }, + { 0x33b5, 0x0000 }, + { 0x33b6, 0x0000 }, + { 0x33b7, 0x0000 }, + { 0x33b8, 0x0000 }, + { 0x33b9, 0x0000 }, + { 0x33ba, 0x0000 }, + { 0x33bb, 0x0000 }, + { 0x33bc, 0x0000 }, + { 0x33bd, 0x0000 }, + { 0x33be, 0x0000 }, + { 0x33bf, 0x0000 }, + { 0x33c0, 0x0000 }, + { 0x33c1, 0x0000 }, + { 0x33c2, 0x0000 }, + { 0x33c3, 0x0000 }, + { 0x33c4, 0x0000 }, + { 0x33c5, 0x0000 }, + { 0x33c6, 0x0000 }, + { 0x33c7, 0x0000 }, + { 0x33c8, 0x0000 }, + { 0x33c9, 0x0000 }, + { 0x33ca, 0x0000 }, + { 0x33cb, 0x0000 }, + { 0x33cc, 0x0000 }, + { 0x33cd, 0x0000 }, + { 0x33ce, 0x0000 }, + { 0x33cf, 0x0000 }, + { 0x33d0, 0x0000 }, + { 0x33d1, 0x0000 }, + { 0x33d2, 0x0000 }, + { 0x33d3, 0x0000 }, + { 0x33d4, 0x0000 }, + { 0x33d5, 0x0000 }, + { 0x33d6, 0x0000 }, + { 0x33d7, 0x0000 }, + { 0x33d8, 0x0000 }, + { 0x33d9, 0x0000 }, + { 0x33da, 0x0000 }, + { 0x33db, 0x0000 }, + { 0x33dc, 0x0000 }, + { 0x33dd, 0x0000 }, + { 0x33de, 0x0000 }, + { 0x33df, 0x0000 }, + { 0x33e0, 0x0000 }, + { 0x33e1, 0x0000 }, + { 0x33e2, 0x0000 }, + { 0x33e3, 0x0000 }, + { 0x33e4, 0x0000 }, + { 0x33e5, 0x0000 }, + { 0x33e6, 0x0000 }, + { 0x33e7, 0x0000 }, + { 0x33e8, 0x0000 }, + { 0x33e9, 0x0000 }, + { 0x33ea, 0x0000 }, + { 0x33eb, 0x0000 }, + { 0x33ec, 0x0000 }, + { 0x33ed, 0x0000 }, + { 0x33ee, 0x0000 }, + { 0x33ef, 0x0000 }, + { 0x33f0, 0x0000 }, + { 0x33f1, 0x0000 }, + { 0x33f2, 0x0000 }, + { 0x33f3, 0x0000 }, + { 0x33f4, 0x0000 }, + { 0x33f5, 0x0000 }, + { 0x33f6, 0x0000 }, + { 0x33f7, 0x0000 }, + { 0x33f8, 0x0000 }, + { 0x33f9, 0x0000 }, + { 0x33fa, 0x0000 }, + { 0x33fb, 0x0000 }, + { 0x33fc, 0x0000 }, + { 0x33fd, 0x0000 }, + { 0x33fe, 0x0000 }, + { 0x33ff, 0x0000 }, + { 0x3400, 0x0000 }, + { 0x3401, 0x0000 }, + { 0x3402, 0x0000 }, + { 0x3403, 0x0000 }, + { 0x3404, 0x0000 }, + { 0x3405, 0x0000 }, + { 0x3406, 0x0000 }, + { 0x3407, 0x0000 }, + { 0x3408, 0x0000 }, + { 0x3409, 0x0000 }, + { 0x340a, 0x0000 }, + { 0x340b, 0x0000 }, + { 0x340c, 0x0000 }, + { 0x340d, 0x0000 }, + { 0x340e, 0x0000 }, + { 0x340f, 0x0000 }, + { 0x3410, 0x0000 }, + { 0x3411, 0x0000 }, + { 0x3412, 0x0000 }, + { 0x3413, 0x0000 }, + { 0x3414, 0x0000 }, + { 0x3415, 0x0000 }, + { 0x3416, 0x0000 }, + { 0x3417, 0x0000 }, + { 0x3418, 0x0000 }, + { 0x3419, 0x0000 }, + { 0x341a, 0x0000 }, + { 0x341b, 0x0000 }, + { 0x341c, 0x0000 }, + { 0x341d, 0x0000 }, + { 0x341e, 0x0000 }, + { 0x341f, 0x0000 }, + { 0x3420, 0x0000 }, + { 0x3421, 0x0000 }, + { 0x3422, 0x0000 }, + { 0x3423, 0x0000 }, + { 0x3424, 0x0000 }, + { 0x3425, 0x0000 }, + { 0x3426, 0x0000 }, + { 0x3427, 0x0000 }, + { 0x3428, 0x0000 }, + { 0x3429, 0x0000 }, + { 0x342a, 0x0000 }, + { 0x342b, 0x0000 }, + { 0x342c, 0x0000 }, + { 0x342d, 0x0000 }, + { 0x342e, 0x0000 }, + { 0x342f, 0x0000 }, + { 0x3430, 0x0000 }, + { 0x3431, 0x0000 }, + { 0x3432, 0x0000 }, + { 0x3433, 0x0000 }, + { 0x3434, 0x0000 }, + { 0x3435, 0x0000 }, + { 0x3436, 0x0000 }, + { 0x3437, 0x0000 }, + { 0x3438, 0x0000 }, + { 0x3439, 0x0000 }, + { 0x343a, 0x0000 }, + { 0x343b, 0x0000 }, + { 0x343c, 0x0000 }, + { 0x343d, 0x0000 }, + { 0x343e, 0x0000 }, + { 0x343f, 0x0000 }, + { 0x3440, 0x0000 }, + { 0x3441, 0x0000 }, + { 0x3442, 0x0000 }, + { 0x3443, 0x0000 }, + { 0x3444, 0x0000 }, + { 0x3445, 0x0000 }, + { 0x3446, 0x0000 }, + { 0x3447, 0x0000 }, + { 0x3448, 0x0000 }, + { 0x3449, 0x0000 }, + { 0x344a, 0x0000 }, + { 0x344b, 0x0000 }, + { 0x344c, 0x0000 }, + { 0x344d, 0x0000 }, + { 0x344e, 0x0000 }, + { 0x344f, 0x0000 }, + { 0x3450, 0x0000 }, + { 0x3451, 0x0000 }, + { 0x3452, 0x0000 }, + { 0x3453, 0x0000 }, + { 0x3454, 0x0000 }, + { 0x3455, 0x0000 }, + { 0x3456, 0x0000 }, + { 0x3457, 0x0000 }, + { 0x3458, 0x0000 }, + { 0x3459, 0x0000 }, + { 0x345a, 0x0000 }, + { 0x345b, 0x0000 }, + { 0x345c, 0x0000 }, + { 0x345d, 0x0000 }, + { 0x345e, 0x0000 }, + { 0x345f, 0x0000 }, + { 0x3460, 0x0000 }, + { 0x3461, 0x0000 }, + { 0x3462, 0x0000 }, + { 0x3463, 0x0000 }, + { 0x3464, 0x0000 }, + { 0x3465, 0x0000 }, + { 0x3466, 0x0000 }, + { 0x3467, 0x0000 }, + { 0x3468, 0x0000 }, + { 0x3469, 0x0000 }, + { 0x346a, 0x0000 }, + { 0x346b, 0x0000 }, + { 0x346c, 0x0000 }, + { 0x346d, 0x0000 }, + { 0x346e, 0x0000 }, + { 0x346f, 0x0000 }, + { 0x3470, 0x0000 }, + { 0x3471, 0x0000 }, + { 0x3472, 0x0000 }, + { 0x3473, 0x0000 }, + { 0x3474, 0x0000 }, + { 0x3475, 0x0000 }, + { 0x3476, 0x0000 }, + { 0x3477, 0x0000 }, + { 0x3478, 0x0000 }, + { 0x3479, 0x0000 }, + { 0x347a, 0x0000 }, + { 0x347b, 0x0000 }, + { 0x347c, 0x0000 }, + { 0x347d, 0x0000 }, + { 0x347e, 0x0000 }, + { 0x347f, 0x0000 }, + { 0x3480, 0x0000 }, + { 0x3481, 0x0000 }, + { 0x3482, 0x0000 }, + { 0x3483, 0x0000 }, + { 0x3484, 0x0000 }, + { 0x3485, 0x0000 }, + { 0x3486, 0x0000 }, + { 0x3487, 0x0000 }, + { 0x3488, 0x0000 }, + { 0x3489, 0x0000 }, + { 0x348a, 0x0000 }, + { 0x348b, 0x0000 }, + { 0x348c, 0x0000 }, + { 0x348d, 0x0000 }, + { 0x348e, 0x0000 }, + { 0x348f, 0x0000 }, + { 0x3490, 0x0000 }, + { 0x3491, 0x0000 }, + { 0x3492, 0x0000 }, + { 0x3493, 0x0000 }, + { 0x3494, 0x0000 }, + { 0x3495, 0x0000 }, + { 0x3496, 0x0000 }, + { 0x3497, 0x0000 }, + { 0x3498, 0x0000 }, + { 0x3499, 0x0000 }, + { 0x349a, 0x0000 }, + { 0x349b, 0x0000 }, + { 0x349c, 0x0000 }, + { 0x349d, 0x0000 }, + { 0x349e, 0x0000 }, + { 0x349f, 0x0000 }, + { 0x34a0, 0x0000 }, + { 0x34a1, 0x0000 }, + { 0x34a2, 0x0000 }, + { 0x34a3, 0x0000 }, + { 0x34a4, 0x0000 }, + { 0x34a5, 0x0000 }, + { 0x34a6, 0x0000 }, + { 0x34a7, 0x0000 }, + { 0x34a8, 0x0000 }, + { 0x34a9, 0x0000 }, + { 0x34aa, 0x0000 }, + { 0x34ab, 0x0000 }, + { 0x34ac, 0x0000 }, + { 0x34ad, 0x0000 }, + { 0x34ae, 0x0000 }, + { 0x34af, 0x0000 }, + { 0x34b0, 0x0000 }, + { 0x34b1, 0x0000 }, + { 0x34b2, 0x0000 }, + { 0x34b3, 0x0000 }, + { 0x34b4, 0x0000 }, + { 0x34b5, 0x0000 }, + { 0x34b6, 0x0000 }, + { 0x34b7, 0x0000 }, + { 0x34b8, 0x0000 }, + { 0x34b9, 0x0000 }, + { 0x34ba, 0x0000 }, + { 0x34bb, 0x0000 }, + { 0x34bc, 0x0000 }, + { 0x34bd, 0x0000 }, + { 0x34be, 0x0000 }, + { 0x34bf, 0x0000 }, + { 0x34c0, 0x0000 }, + { 0x34c1, 0x0000 }, + { 0x34c2, 0x0000 }, + { 0x34c3, 0x0000 }, + { 0x34c4, 0x0000 }, + { 0x34c5, 0x0000 }, + { 0x34c6, 0x0000 }, + { 0x34c7, 0x0000 }, + { 0x34c8, 0x0000 }, + { 0x34c9, 0x0000 }, + { 0x34ca, 0x0000 }, + { 0x34cb, 0x0000 }, + { 0x34cc, 0x0000 }, + { 0x34cd, 0x0000 }, + { 0x34ce, 0x0000 }, + { 0x34cf, 0x0000 }, + { 0x34d0, 0x0000 }, + { 0x34d1, 0x0000 }, + { 0x34d2, 0x0000 }, + { 0x34d3, 0x0000 }, + { 0x34d4, 0x0000 }, + { 0x34d5, 0x0000 }, + { 0x34d6, 0x0000 }, + { 0x34d7, 0x0000 }, + { 0x34d8, 0x0000 }, + { 0x34d9, 0x0000 }, + { 0x34da, 0x0000 }, + { 0x34db, 0x0000 }, + { 0x34dc, 0x0000 }, + { 0x34dd, 0x0000 }, + { 0x34de, 0x0000 }, + { 0x34df, 0x0000 }, + { 0x34e0, 0x0000 }, + { 0x34e1, 0x0000 }, + { 0x34e2, 0x0000 }, + { 0x34e3, 0x0000 }, + { 0x34e4, 0x0000 }, + { 0x34e5, 0x0000 }, + { 0x34e6, 0x0000 }, + { 0x34e7, 0x0000 }, + { 0x34e8, 0x0000 }, + { 0x34e9, 0x0000 }, + { 0x34ea, 0x0000 }, + { 0x34eb, 0x0000 }, + { 0x34ec, 0x0000 }, + { 0x34ed, 0x0000 }, + { 0x34ee, 0x0000 }, + { 0x34ef, 0x0000 }, + { 0x34f0, 0x0000 }, + { 0x34f1, 0x0000 }, + { 0x34f2, 0x0000 }, + { 0x34f3, 0x0000 }, + { 0x34f4, 0x0000 }, + { 0x34f5, 0x0000 }, + { 0x34f6, 0x0000 }, + { 0x34f7, 0x0000 }, + { 0x34f8, 0x0000 }, + { 0x34f9, 0x0000 }, + { 0x34fa, 0x0000 }, + { 0x34fb, 0x0000 }, + { 0x34fc, 0x0000 }, + { 0x34fd, 0x0000 }, + { 0x34fe, 0x0000 }, + { 0x34ff, 0x0000 }, + { 0x3500, 0x0000 }, + { 0x3501, 0x0000 }, + { 0x3502, 0x0000 }, + { 0x3503, 0x0000 }, + { 0x3504, 0x0000 }, + { 0x3505, 0x0000 }, + { 0x3506, 0x0000 }, + { 0x3507, 0x0000 }, + { 0x3508, 0x0000 }, + { 0x3509, 0x0000 }, + { 0x350a, 0x0000 }, + { 0x350b, 0x0000 }, + { 0x350c, 0x0000 }, + { 0x350d, 0x0000 }, + { 0x350e, 0x0000 }, + { 0x350f, 0x0000 }, + { 0x3510, 0x0000 }, + { 0x3511, 0x0000 }, + { 0x3512, 0x0000 }, + { 0x3513, 0x0000 }, + { 0x3514, 0x0000 }, + { 0x3515, 0x0000 }, + { 0x3516, 0x0000 }, + { 0x3517, 0x0000 }, + { 0x3518, 0x0000 }, + { 0x3519, 0x0000 }, + { 0x351a, 0x0000 }, + { 0x351b, 0x0000 }, + { 0x351c, 0x0000 }, + { 0x351d, 0x0000 }, + { 0x351e, 0x0000 }, + { 0x351f, 0x0000 }, + { 0x3520, 0x0000 }, + { 0x3521, 0x0000 }, + { 0x3522, 0x0000 }, + { 0x3523, 0x0000 }, + { 0x3524, 0x0000 }, + { 0x3525, 0x0000 }, + { 0x3526, 0x0000 }, + { 0x3527, 0x0000 }, + { 0x3528, 0x0000 }, + { 0x3529, 0x0000 }, + { 0x352a, 0x0000 }, + { 0x352b, 0x0000 }, + { 0x352c, 0x0000 }, + { 0x352d, 0x0000 }, + { 0x352e, 0x0000 }, + { 0x352f, 0x0000 }, + { 0x3530, 0x0000 }, + { 0x3531, 0x0000 }, + { 0x3532, 0x0000 }, + { 0x3533, 0x0000 }, + { 0x3534, 0x0000 }, + { 0x3535, 0x0000 }, + { 0x3536, 0x0000 }, + { 0x3537, 0x0000 }, + { 0x3538, 0x0000 }, + { 0x3539, 0x0000 }, + { 0x353a, 0x0000 }, + { 0x353b, 0x0000 }, + { 0x353c, 0x0000 }, + { 0x353d, 0x0000 }, + { 0x353e, 0x0000 }, + { 0x353f, 0x0000 }, + { 0x3540, 0x0000 }, + { 0x3541, 0x0000 }, + { 0x3542, 0x0000 }, + { 0x3543, 0x0000 }, + { 0x3544, 0x0000 }, + { 0x3545, 0x0000 }, + { 0x3546, 0x0000 }, + { 0x3547, 0x0000 }, + { 0x3548, 0x0000 }, + { 0x3549, 0x0000 }, + { 0x354a, 0x0000 }, + { 0x354b, 0x0000 }, + { 0x354c, 0x0000 }, + { 0x354d, 0x0000 }, + { 0x354e, 0x0000 }, + { 0x354f, 0x0000 }, + { 0x3550, 0x0000 }, + { 0x3551, 0x0000 }, + { 0x3552, 0x0000 }, + { 0x3553, 0x0000 }, + { 0x3554, 0x0000 }, + { 0x3555, 0x0000 }, + { 0x3556, 0x0000 }, + { 0x3557, 0x0000 }, + { 0x3558, 0x0000 }, + { 0x3559, 0x0000 }, + { 0x355a, 0x0000 }, + { 0x355b, 0x0000 }, + { 0x355c, 0x0000 }, + { 0x355d, 0x0000 }, + { 0x355e, 0x0000 }, + { 0x355f, 0x0000 }, + { 0x3560, 0x0000 }, + { 0x3561, 0x0000 }, + { 0x3562, 0x0000 }, + { 0x3563, 0x0000 }, + { 0x3564, 0x0000 }, + { 0x3565, 0x0000 }, + { 0x3566, 0x0000 }, + { 0x3567, 0x0000 }, + { 0x3568, 0x0000 }, + { 0x3569, 0x0000 }, + { 0x356a, 0x0000 }, + { 0x356b, 0x0000 }, + { 0x356c, 0x0000 }, + { 0x356d, 0x0000 }, + { 0x356e, 0x0000 }, + { 0x356f, 0x0000 }, + { 0x3570, 0x0000 }, + { 0x3571, 0x0000 }, + { 0x3572, 0x0000 }, + { 0x3573, 0x0000 }, + { 0x3574, 0x0000 }, + { 0x3575, 0x0000 }, + { 0x3576, 0x0000 }, + { 0x3577, 0x0000 }, + { 0x3578, 0x0000 }, + { 0x3579, 0x0000 }, + { 0x357a, 0x0000 }, + { 0x357b, 0x0000 }, + { 0x357c, 0x0000 }, + { 0x357d, 0x0000 }, + { 0x357e, 0x0000 }, + { 0x357f, 0x0000 }, + { 0x3580, 0x0000 }, + { 0x3581, 0x0000 }, + { 0x3582, 0x0000 }, + { 0x3583, 0x0000 }, + { 0x3584, 0x0000 }, + { 0x3585, 0x0000 }, + { 0x3586, 0x0000 }, + { 0x3587, 0x0000 }, + { 0x3588, 0x0000 }, + { 0x3589, 0x0000 }, + { 0x358a, 0x0000 }, + { 0x358b, 0x0000 }, + { 0x358c, 0x0000 }, + { 0x358d, 0x0000 }, + { 0x358e, 0x0000 }, + { 0x358f, 0x0000 }, + { 0x3590, 0x0000 }, + { 0x3591, 0x0000 }, + { 0x3592, 0x0000 }, + { 0x3593, 0x0000 }, + { 0x3594, 0x0000 }, + { 0x3595, 0x0000 }, + { 0x3596, 0x0000 }, + { 0x3597, 0x0000 }, + { 0x3598, 0x0000 }, + { 0x3599, 0x0000 }, + { 0x359a, 0x0000 }, + { 0x359b, 0x0000 }, + { 0x359c, 0x0000 }, + { 0x359d, 0x0000 }, + { 0x359e, 0x0000 }, + { 0x359f, 0x0000 }, + { 0x35a0, 0x0000 }, + { 0x35a1, 0x0000 }, + { 0x35a2, 0x0000 }, + { 0x35a3, 0x0000 }, + { 0x35a4, 0x0000 }, + { 0x35a5, 0x0000 }, + { 0x35a6, 0x0000 }, + { 0x35a7, 0x0000 }, + { 0x35a8, 0x0000 }, + { 0x35a9, 0x0000 }, + { 0x35aa, 0x0000 }, + { 0x35ab, 0x0000 }, + { 0x35ac, 0x0000 }, + { 0x35ad, 0x0000 }, + { 0x35ae, 0x0000 }, + { 0x35af, 0x0000 }, + { 0x35b0, 0x0000 }, + { 0x35b1, 0x0000 }, + { 0x35b2, 0x0000 }, + { 0x35b3, 0x0000 }, + { 0x35b4, 0x0000 }, + { 0x35b5, 0x0000 }, + { 0x35b6, 0x0000 }, + { 0x35b7, 0x0000 }, + { 0x35b8, 0x0000 }, + { 0x35b9, 0x0000 }, + { 0x35ba, 0x0000 }, + { 0x35bb, 0x0000 }, + { 0x35bc, 0x0000 }, + { 0x35bd, 0x0000 }, + { 0x35be, 0x0000 }, + { 0x35bf, 0x0000 }, + { 0x35c0, 0x0000 }, + { 0x35c1, 0x0000 }, + { 0x35c2, 0x0000 }, + { 0x35c3, 0x0000 }, + { 0x35c4, 0x0000 }, + { 0x35c5, 0x0000 }, + { 0x35c6, 0x0000 }, + { 0x35c7, 0x0000 }, + { 0x35c8, 0x0000 }, + { 0x35c9, 0x0000 }, + { 0x35ca, 0x0000 }, + { 0x35cb, 0x0000 }, + { 0x35cc, 0x0000 }, + { 0x35cd, 0x0000 }, + { 0x35ce, 0x0000 }, + { 0x35cf, 0x0000 }, + { 0x35d0, 0x0000 }, + { 0x35d1, 0x0000 }, + { 0x35d2, 0x0000 }, + { 0x35d3, 0x0000 }, + { 0x35d4, 0x0000 }, + { 0x35d5, 0x0000 }, + { 0x35d6, 0x0000 }, + { 0x35d7, 0x0000 }, + { 0x35d8, 0x0000 }, + { 0x35d9, 0x0000 }, + { 0x35da, 0x0000 }, + { 0x35db, 0x0000 }, + { 0x35dc, 0x0000 }, + { 0x35dd, 0x0000 }, + { 0x35de, 0x0000 }, + { 0x35df, 0x0000 }, + { 0x35e0, 0x0000 }, + { 0x35e1, 0x0000 }, + { 0x35e2, 0x0000 }, + { 0x35e3, 0x0000 }, + { 0x35e4, 0x0000 }, + { 0x35e5, 0x0000 }, + { 0x35e6, 0x0000 }, + { 0x35e7, 0x0000 }, + { 0x35e8, 0x0000 }, + { 0x35e9, 0x0000 }, + { 0x35ea, 0x0000 }, + { 0x35eb, 0x0000 }, + { 0x35ec, 0x0000 }, + { 0x35ed, 0x0000 }, + { 0x35ee, 0x0000 }, + { 0x35ef, 0x0000 }, + { 0x35f0, 0x0000 }, + { 0x35f1, 0x0000 }, + { 0x35f2, 0x0000 }, + { 0x35f3, 0x0000 }, + { 0x35f4, 0x0000 }, + { 0x35f5, 0x0000 }, + { 0x35f6, 0x0000 }, + { 0x35f7, 0x0000 }, + { 0x35f8, 0x0000 }, + { 0x35f9, 0x0000 }, + { 0x35fa, 0x0000 }, + { 0x35fb, 0x0000 }, + { 0x35fc, 0x0000 }, + { 0x35fd, 0x0000 }, + { 0x35fe, 0x0000 }, + { 0x35ff, 0x0000 }, + { 0x3600, 0x0000 }, + { 0x3601, 0x0000 }, + { 0x3602, 0x0000 }, + { 0x3603, 0x0000 }, + { 0x3604, 0x0000 }, + { 0x3605, 0x0000 }, + { 0x3606, 0x0000 }, + { 0x3607, 0x0000 }, + { 0x3608, 0x0000 }, + { 0x3609, 0x0000 }, + { 0x360a, 0x0000 }, + { 0x360b, 0x0000 }, + { 0x360c, 0x0000 }, + { 0x360d, 0x0000 }, + { 0x360e, 0x0000 }, + { 0x360f, 0x0000 }, + { 0x3610, 0x0000 }, + { 0x3611, 0x0000 }, + { 0x3612, 0x0000 }, + { 0x3613, 0x0000 }, + { 0x3614, 0x0000 }, + { 0x3615, 0x0000 }, + { 0x3616, 0x0000 }, + { 0x3617, 0x0000 }, + { 0x3618, 0x0000 }, + { 0x3619, 0x0000 }, + { 0x361a, 0x0000 }, + { 0x361b, 0x0000 }, + { 0x361c, 0x0000 }, + { 0x361d, 0x0000 }, + { 0x361e, 0x0000 }, + { 0x361f, 0x0000 }, + { 0x3620, 0x0000 }, + { 0x3621, 0x0000 }, + { 0x3622, 0x0000 }, + { 0x3623, 0x0000 }, + { 0x3624, 0x0000 }, + { 0x3625, 0x0000 }, + { 0x3626, 0x0000 }, + { 0x3627, 0x0000 }, + { 0x3628, 0x0000 }, + { 0x3629, 0x0000 }, + { 0x362a, 0x0000 }, + { 0x362b, 0x0000 }, + { 0x362c, 0x0000 }, + { 0x362d, 0x0000 }, + { 0x362e, 0x0000 }, + { 0x362f, 0x0000 }, + { 0x3630, 0x0000 }, + { 0x3631, 0x0000 }, + { 0x3632, 0x0000 }, + { 0x3633, 0x0000 }, + { 0x3634, 0x0000 }, + { 0x3635, 0x0000 }, + { 0x3636, 0x0000 }, + { 0x3637, 0x0000 }, + { 0x3638, 0x0000 }, + { 0x3639, 0x0000 }, + { 0x363a, 0x0000 }, + { 0x363b, 0x0000 }, + { 0x363c, 0x0000 }, + { 0x363d, 0x0000 }, + { 0x363e, 0x0000 }, + { 0x363f, 0x0000 }, + { 0x3640, 0x0000 }, + { 0x3641, 0x0000 }, + { 0x3642, 0x0000 }, + { 0x3643, 0x0000 }, + { 0x3644, 0x0000 }, + { 0x3645, 0x0000 }, + { 0x3646, 0x0000 }, + { 0x3647, 0x0000 }, + { 0x3648, 0x0000 }, + { 0x3649, 0x0000 }, + { 0x364a, 0x0000 }, + { 0x364b, 0x0000 }, + { 0x364c, 0x0000 }, + { 0x364d, 0x0000 }, + { 0x364e, 0x0000 }, + { 0x364f, 0x0000 }, + { 0x3650, 0x0000 }, + { 0x3651, 0x0000 }, + { 0x3652, 0x0000 }, + { 0x3653, 0x0000 }, + { 0x3654, 0x0000 }, + { 0x3655, 0x0000 }, + { 0x3656, 0x0000 }, + { 0x3657, 0x0000 }, + { 0x3658, 0x0000 }, + { 0x3659, 0x0000 }, + { 0x365a, 0x0000 }, + { 0x365b, 0x0000 }, + { 0x365c, 0x0000 }, + { 0x365d, 0x0000 }, + { 0x365e, 0x0000 }, + { 0x365f, 0x0000 }, + { 0x3660, 0x0000 }, + { 0x3661, 0x0000 }, + { 0x3662, 0x0000 }, + { 0x3663, 0x0000 }, + { 0x3664, 0x0000 }, + { 0x3665, 0x0000 }, + { 0x3666, 0x0000 }, + { 0x3667, 0x0000 }, + { 0x3668, 0x0000 }, + { 0x3669, 0x0000 }, + { 0x366a, 0x0000 }, + { 0x366b, 0x0000 }, + { 0x366c, 0x0000 }, + { 0x366d, 0x0000 }, + { 0x366e, 0x0000 }, + { 0x366f, 0x0000 }, + { 0x3670, 0x0000 }, + { 0x3671, 0x0000 }, + { 0x3672, 0x0000 }, + { 0x3673, 0x0000 }, + { 0x3674, 0x0000 }, + { 0x3675, 0x0000 }, + { 0x3676, 0x0000 }, + { 0x3677, 0x0000 }, + { 0x3678, 0x0000 }, + { 0x3679, 0x0000 }, + { 0x367a, 0x0000 }, + { 0x367b, 0x0000 }, + { 0x367c, 0x0000 }, + { 0x367d, 0x0000 }, + { 0x367e, 0x0000 }, + { 0x367f, 0x0000 }, + { 0x3680, 0x0000 }, + { 0x3681, 0x0000 }, + { 0x3682, 0x0000 }, + { 0x3683, 0x0000 }, + { 0x3684, 0x0000 }, + { 0x3685, 0x0000 }, + { 0x3686, 0x0000 }, + { 0x3687, 0x0000 }, + { 0x3688, 0x0000 }, + { 0x3689, 0x0000 }, + { 0x368a, 0x0000 }, + { 0x368b, 0x0000 }, + { 0x368c, 0x0000 }, + { 0x368d, 0x0000 }, + { 0x368e, 0x0000 }, + { 0x368f, 0x0000 }, + { 0x3690, 0x0000 }, + { 0x3691, 0x0000 }, + { 0x3692, 0x0000 }, + { 0x3693, 0x0000 }, + { 0x3694, 0x0000 }, + { 0x3695, 0x0000 }, + { 0x3696, 0x0000 }, + { 0x3697, 0x0000 }, + { 0x3698, 0x0000 }, + { 0x3699, 0x0000 }, + { 0x369a, 0x0000 }, + { 0x369b, 0x0000 }, + { 0x369c, 0x0000 }, + { 0x369d, 0x0000 }, + { 0x369e, 0x0000 }, + { 0x369f, 0x0000 }, + { 0x36a0, 0x0000 }, + { 0x36a1, 0x0000 }, + { 0x36a2, 0x0000 }, + { 0x36a3, 0x0000 }, + { 0x36a4, 0x0000 }, + { 0x36a5, 0x0000 }, + { 0x36a6, 0x0000 }, + { 0x36a7, 0x0000 }, + { 0x36a8, 0x0000 }, + { 0x36a9, 0x0000 }, + { 0x36aa, 0x0000 }, + { 0x36ab, 0x0000 }, + { 0x36ac, 0x0000 }, + { 0x36ad, 0x0000 }, + { 0x36ae, 0x0000 }, + { 0x36af, 0x0000 }, + { 0x36b0, 0x0000 }, + { 0x36b1, 0x0000 }, + { 0x36b2, 0x0000 }, + { 0x36b3, 0x0000 }, + { 0x36b4, 0x0000 }, + { 0x36b5, 0x0000 }, + { 0x36b6, 0x0000 }, + { 0x36b7, 0x0000 }, + { 0x36b8, 0x0000 }, + { 0x36b9, 0x0000 }, + { 0x36ba, 0x0000 }, + { 0x36bb, 0x0000 }, + { 0x36bc, 0x0000 }, + { 0x36bd, 0x0000 }, + { 0x36be, 0x0000 }, + { 0x36bf, 0x0000 }, + { 0x36c0, 0x0000 }, + { 0x36c1, 0x0000 }, + { 0x36c2, 0x0000 }, + { 0x36c3, 0x0000 }, + { 0x36c4, 0x0000 }, + { 0x36c5, 0x0000 }, + { 0x36c6, 0x0000 }, + { 0x36c7, 0x0000 }, + { 0x36c8, 0x0000 }, + { 0x36c9, 0x0000 }, + { 0x36ca, 0x0000 }, + { 0x36cb, 0x0000 }, + { 0x36cc, 0x0000 }, + { 0x36cd, 0x0000 }, + { 0x36ce, 0x0000 }, + { 0x36cf, 0x0000 }, + { 0x36d0, 0x0000 }, + { 0x36d1, 0x0000 }, + { 0x36d2, 0x0000 }, + { 0x36d3, 0x0000 }, + { 0x36d4, 0x0000 }, + { 0x36d5, 0x0000 }, + { 0x36d6, 0x0000 }, + { 0x36d7, 0x0000 }, + { 0x36d8, 0x0000 }, + { 0x36d9, 0x0000 }, + { 0x36da, 0x0000 }, + { 0x36db, 0x0000 }, + { 0x36dc, 0x0000 }, + { 0x36dd, 0x0000 }, + { 0x36de, 0x0000 }, + { 0x36df, 0x0000 }, + { 0x36e0, 0x0000 }, + { 0x36e1, 0x0000 }, + { 0x36e2, 0x0000 }, + { 0x36e3, 0x0000 }, + { 0x36e4, 0x0000 }, + { 0x36e5, 0x0000 }, + { 0x36e6, 0x0000 }, + { 0x36e7, 0x0000 }, + { 0x36e8, 0x0000 }, + { 0x36e9, 0x0000 }, + { 0x36ea, 0x0000 }, + { 0x36eb, 0x0000 }, + { 0x36ec, 0x0000 }, + { 0x36ed, 0x0000 }, + { 0x36ee, 0x0000 }, + { 0x36ef, 0x0000 }, + { 0x36f0, 0x0000 }, + { 0x36f1, 0x0000 }, + { 0x36f2, 0x0000 }, + { 0x36f3, 0x0000 }, + { 0x36f4, 0x0000 }, + { 0x36f5, 0x0000 }, + { 0x36f6, 0x0000 }, + { 0x36f7, 0x0000 }, + { 0x36f8, 0x0000 }, + { 0x36f9, 0x0000 }, + { 0x36fa, 0x0000 }, + { 0x36fb, 0x0000 }, + { 0x36fc, 0x0000 }, + { 0x36fd, 0x0000 }, + { 0x36fe, 0x0000 }, + { 0x36ff, 0x0000 }, + { 0x3700, 0x0000 }, + { 0x3701, 0x0000 }, + { 0x3702, 0x0000 }, + { 0x3703, 0x0000 }, + { 0x3704, 0x0000 }, + { 0x3705, 0x0000 }, + { 0x3706, 0x0000 }, + { 0x3707, 0x0000 }, + { 0x3708, 0x0000 }, + { 0x3709, 0x0000 }, + { 0x370a, 0x0000 }, + { 0x370b, 0x0000 }, + { 0x370c, 0x0000 }, + { 0x370d, 0x0000 }, + { 0x370e, 0x0000 }, + { 0x370f, 0x0000 }, + { 0x3710, 0x0000 }, + { 0x3711, 0x0000 }, + { 0x3712, 0x0000 }, + { 0x3713, 0x0000 }, + { 0x3714, 0x0000 }, + { 0x3715, 0x0000 }, + { 0x3716, 0x0000 }, + { 0x3717, 0x0000 }, + { 0x3718, 0x0000 }, + { 0x3719, 0x0000 }, + { 0x371a, 0x0000 }, + { 0x371b, 0x0000 }, + { 0x371c, 0x0000 }, + { 0x371d, 0x0000 }, + { 0x371e, 0x0000 }, + { 0x371f, 0x0000 }, + { 0x3720, 0x0000 }, + { 0x3721, 0x0000 }, + { 0x3722, 0x0000 }, + { 0x3723, 0x0000 }, + { 0x3724, 0x0000 }, + { 0x3725, 0x0000 }, + { 0x3726, 0x0000 }, + { 0x3727, 0x0000 }, + { 0x3728, 0x0000 }, + { 0x3729, 0x0000 }, + { 0x372a, 0x0000 }, + { 0x372b, 0x0000 }, + { 0x372c, 0x0000 }, + { 0x372d, 0x0000 }, + { 0x372e, 0x0000 }, + { 0x372f, 0x0000 }, + { 0x3730, 0x0000 }, + { 0x3731, 0x0000 }, + { 0x3732, 0x0000 }, + { 0x3733, 0x0000 }, + { 0x3734, 0x0000 }, + { 0x3735, 0x0000 }, + { 0x3736, 0x0000 }, + { 0x3737, 0x0000 }, + { 0x3738, 0x0000 }, + { 0x3739, 0x0000 }, + { 0x373a, 0x0000 }, + { 0x373b, 0x0000 }, + { 0x373c, 0x0000 }, + { 0x373d, 0x0000 }, + { 0x373e, 0x0000 }, + { 0x373f, 0x0000 }, + { 0x3740, 0x0000 }, + { 0x3741, 0x0000 }, + { 0x3742, 0x0000 }, + { 0x3743, 0x0000 }, + { 0x3744, 0x0000 }, + { 0x3745, 0x0000 }, + { 0x3746, 0x0000 }, + { 0x3747, 0x0000 }, + { 0x3748, 0x0000 }, + { 0x3749, 0x0000 }, + { 0x374a, 0x0000 }, + { 0x374b, 0x0000 }, + { 0x374c, 0x0000 }, + { 0x374d, 0x0000 }, + { 0x374e, 0x0000 }, + { 0x374f, 0x0000 }, + { 0x3750, 0x0000 }, + { 0x3751, 0x0000 }, + { 0x3752, 0x0000 }, + { 0x3753, 0x0000 }, + { 0x3754, 0x0000 }, + { 0x3755, 0x0000 }, + { 0x3756, 0x0000 }, + { 0x3757, 0x0000 }, + { 0x3758, 0x0000 }, + { 0x3759, 0x0000 }, + { 0x375a, 0x0000 }, + { 0x375b, 0x0000 }, + { 0x375c, 0x0000 }, + { 0x375d, 0x0000 }, + { 0x375e, 0x0000 }, + { 0x375f, 0x0000 }, + { 0x3760, 0x0000 }, + { 0x3761, 0x0000 }, + { 0x3762, 0x0000 }, + { 0x3763, 0x0000 }, + { 0x3764, 0x0000 }, + { 0x3765, 0x0000 }, + { 0x3766, 0x0000 }, + { 0x3767, 0x0000 }, + { 0x3768, 0x0000 }, + { 0x3769, 0x0000 }, + { 0x376a, 0x0000 }, + { 0x376b, 0x0000 }, + { 0x376c, 0x0000 }, + { 0x376d, 0x0000 }, + { 0x376e, 0x0000 }, + { 0x376f, 0x0000 }, + { 0x3770, 0x0000 }, + { 0x3771, 0x0000 }, + { 0x3772, 0x0000 }, + { 0x3773, 0x0000 }, + { 0x3774, 0x0000 }, + { 0x3775, 0x0000 }, + { 0x3776, 0x0000 }, + { 0x3777, 0x0000 }, + { 0x3778, 0x0000 }, + { 0x3779, 0x0000 }, + { 0x377a, 0x0000 }, + { 0x377b, 0x0000 }, + { 0x377c, 0x0000 }, + { 0x377d, 0x0000 }, + { 0x377e, 0x0000 }, + { 0x377f, 0x0000 }, + { 0x3780, 0x0000 }, + { 0x3781, 0x0000 }, + { 0x3782, 0x0000 }, + { 0x3783, 0x0000 }, + { 0x3784, 0x0000 }, + { 0x3785, 0x0000 }, + { 0x3786, 0x0000 }, + { 0x3787, 0x0000 }, + { 0x3788, 0x0000 }, + { 0x3789, 0x0000 }, + { 0x378a, 0x0000 }, + { 0x378b, 0x0000 }, + { 0x378c, 0x0000 }, + { 0x378d, 0x0000 }, + { 0x378e, 0x0000 }, + { 0x378f, 0x0000 }, + { 0x3790, 0x0000 }, + { 0x3791, 0x0000 }, + { 0x3792, 0x0000 }, + { 0x3793, 0x0000 }, + { 0x3794, 0x0000 }, + { 0x3795, 0x0000 }, + { 0x3796, 0x0000 }, + { 0x3797, 0x0000 }, + { 0x3798, 0x0000 }, + { 0x3799, 0x0000 }, + { 0x379a, 0x0000 }, + { 0x379b, 0x0000 }, + { 0x379c, 0x0000 }, + { 0x379d, 0x0000 }, + { 0x379e, 0x0000 }, + { 0x379f, 0x0000 }, + { 0x37a0, 0x0000 }, + { 0x37a1, 0x0000 }, + { 0x37a2, 0x0000 }, + { 0x37a3, 0x0000 }, + { 0x37a4, 0x0000 }, + { 0x37a5, 0x0000 }, + { 0x37a6, 0x0000 }, + { 0x37a7, 0x0000 }, + { 0x37a8, 0x0000 }, + { 0x37a9, 0x0000 }, + { 0x37aa, 0x0000 }, + { 0x37ab, 0x0000 }, + { 0x37ac, 0x0000 }, + { 0x37ad, 0x0000 }, + { 0x37ae, 0x0000 }, + { 0x37af, 0x0000 }, + { 0x37b0, 0x0000 }, + { 0x37b1, 0x0000 }, + { 0x37b2, 0x0000 }, + { 0x37b3, 0x0000 }, + { 0x37b4, 0x0000 }, + { 0x37b5, 0x0000 }, + { 0x37b6, 0x0000 }, + { 0x37b7, 0x0000 }, + { 0x37b8, 0x0000 }, + { 0x37b9, 0x0000 }, + { 0x37ba, 0x0000 }, + { 0x37bb, 0x0000 }, + { 0x37bc, 0x0000 }, + { 0x37bd, 0x0000 }, + { 0x37be, 0x0000 }, + { 0x37bf, 0x0000 }, + { 0x37c0, 0x0000 }, + { 0x37c1, 0x0000 }, + { 0x37c2, 0x0000 }, + { 0x37c3, 0x0000 }, + { 0x37c4, 0x0000 }, + { 0x37c5, 0x0000 }, + { 0x37c6, 0x0000 }, + { 0x37c7, 0x0000 }, + { 0x37c8, 0x0000 }, + { 0x37c9, 0x0000 }, + { 0x37ca, 0x0000 }, + { 0x37cb, 0x0000 }, + { 0x37cc, 0x0000 }, + { 0x37cd, 0x0000 }, + { 0x37ce, 0x0000 }, + { 0x37cf, 0x0000 }, + { 0x37d0, 0x0000 }, + { 0x37d1, 0x0000 }, + { 0x37d2, 0x0000 }, + { 0x37d3, 0x0000 }, + { 0x37d4, 0x0000 }, + { 0x37d5, 0x0000 }, + { 0x37d6, 0x0000 }, + { 0x37d7, 0x0000 }, + { 0x37d8, 0x0000 }, + { 0x37d9, 0x0000 }, + { 0x37da, 0x0000 }, + { 0x37db, 0x0000 }, + { 0x37dc, 0x0000 }, + { 0x37dd, 0x0000 }, + { 0x37de, 0x0000 }, + { 0x37df, 0x0000 }, + { 0x37e0, 0x0000 }, + { 0x37e1, 0x0000 }, + { 0x37e2, 0x0000 }, + { 0x37e3, 0x0000 }, + { 0x37e4, 0x0000 }, + { 0x37e5, 0x0000 }, + { 0x37e6, 0x0000 }, + { 0x37e7, 0x0000 }, + { 0x37e8, 0x0000 }, + { 0x37e9, 0x0000 }, + { 0x37ea, 0x0000 }, + { 0x37eb, 0x0000 }, + { 0x37ec, 0x0000 }, + { 0x37ed, 0x0000 }, + { 0x37ee, 0x0000 }, + { 0x37ef, 0x0000 }, + { 0x37f0, 0x0000 }, + { 0x37f1, 0x0000 }, + { 0x37f2, 0x0000 }, + { 0x37f3, 0x0000 }, + { 0x37f4, 0x0000 }, + { 0x37f5, 0x0000 }, + { 0x37f6, 0x0000 }, + { 0x37f7, 0x0000 }, + { 0x37f8, 0x0000 }, + { 0x37f9, 0x0000 }, + { 0x37fa, 0x0000 }, + { 0x37fb, 0x0000 }, + { 0x37fc, 0x0000 }, + { 0x37fd, 0x0000 }, + { 0x37fe, 0x0000 }, + { 0x37ff, 0x0000 }, + { 0x3800, 0x0000 }, + { 0x3801, 0x0000 }, + { 0x3802, 0x0000 }, + { 0x3803, 0x0000 }, + { 0x3804, 0x0000 }, + { 0x3805, 0x0000 }, + { 0x3806, 0x0000 }, + { 0x3807, 0x0000 }, + { 0x3808, 0x0000 }, + { 0x3809, 0x0000 }, + { 0x380a, 0x0000 }, + { 0x380b, 0x0000 }, + { 0x380c, 0x0000 }, + { 0x380d, 0x0000 }, + { 0x380e, 0x0000 }, + { 0x380f, 0x0000 }, + { 0x3810, 0x0000 }, + { 0x3811, 0x0000 }, + { 0x3812, 0x0000 }, + { 0x3813, 0x0000 }, + { 0x3814, 0x0000 }, + { 0x3815, 0x0000 }, + { 0x3816, 0x0000 }, + { 0x3817, 0x0000 }, + { 0x3818, 0x0000 }, + { 0x3819, 0x0000 }, + { 0x381a, 0x0000 }, + { 0x381b, 0x0000 }, + { 0x381c, 0x0000 }, + { 0x381d, 0x0000 }, + { 0x381e, 0x0000 }, + { 0x381f, 0x0000 }, + { 0x3820, 0x0000 }, + { 0x3821, 0x0000 }, + { 0x3822, 0x0000 }, + { 0x3823, 0x0000 }, + { 0x3824, 0x0000 }, + { 0x3825, 0x0000 }, + { 0x3826, 0x0000 }, + { 0x3827, 0x0000 }, + { 0x3828, 0x0000 }, + { 0x3829, 0x0000 }, + { 0x382a, 0x0000 }, + { 0x382b, 0x0000 }, + { 0x382c, 0x0000 }, + { 0x382d, 0x0000 }, + { 0x382e, 0x0000 }, + { 0x382f, 0x0000 }, + { 0x3830, 0x0000 }, + { 0x3831, 0x0000 }, + { 0x3832, 0x0000 }, + { 0x3833, 0x0000 }, + { 0x3834, 0x0000 }, + { 0x3835, 0x0000 }, + { 0x3836, 0x0000 }, + { 0x3837, 0x0000 }, + { 0x3838, 0x0000 }, + { 0x3839, 0x0000 }, + { 0x383a, 0x0000 }, + { 0x383b, 0x0000 }, + { 0x383c, 0x0000 }, + { 0x383d, 0x0000 }, + { 0x383e, 0x0000 }, + { 0x383f, 0x0000 }, + { 0x3840, 0x0000 }, + { 0x3841, 0x0000 }, + { 0x3842, 0x0000 }, + { 0x3843, 0x0000 }, + { 0x3844, 0x0000 }, + { 0x3845, 0x0000 }, + { 0x3846, 0x0000 }, + { 0x3847, 0x0000 }, + { 0x3848, 0x0000 }, + { 0x3849, 0x0000 }, + { 0x384a, 0x0000 }, + { 0x384b, 0x0000 }, + { 0x384c, 0x0000 }, + { 0x384d, 0x0000 }, + { 0x384e, 0x0000 }, + { 0x384f, 0x0000 }, + { 0x3850, 0x0000 }, + { 0x3851, 0x0000 }, + { 0x3852, 0x0000 }, + { 0x3853, 0x0000 }, + { 0x3854, 0x0000 }, + { 0x3855, 0x0000 }, + { 0x3856, 0x0000 }, + { 0x3857, 0x0000 }, + { 0x3858, 0x0000 }, + { 0x3859, 0x0000 }, + { 0x385a, 0x0000 }, + { 0x385b, 0x0000 }, + { 0x385c, 0x0000 }, + { 0x385d, 0x0000 }, + { 0x385e, 0x0000 }, + { 0x385f, 0x0000 }, + { 0x3860, 0x0000 }, + { 0x3861, 0x0000 }, + { 0x3862, 0x0000 }, + { 0x3863, 0x0000 }, + { 0x3864, 0x0000 }, + { 0x3865, 0x0000 }, + { 0x3866, 0x0000 }, + { 0x3867, 0x0000 }, + { 0x3868, 0x0000 }, + { 0x3869, 0x0000 }, + { 0x386a, 0x0000 }, + { 0x386b, 0x0000 }, + { 0x386c, 0x0000 }, + { 0x386d, 0x0000 }, + { 0x386e, 0x0000 }, + { 0x386f, 0x0000 }, + { 0x3870, 0x0000 }, + { 0x3871, 0x0000 }, + { 0x3872, 0x0000 }, + { 0x3873, 0x0000 }, + { 0x3874, 0x0000 }, + { 0x3875, 0x0000 }, + { 0x3876, 0x0000 }, + { 0x3877, 0x0000 }, + { 0x3878, 0x0000 }, + { 0x3879, 0x0000 }, + { 0x387a, 0x0000 }, + { 0x387b, 0x0000 }, + { 0x387c, 0x0000 }, + { 0x387d, 0x0000 }, + { 0x387e, 0x0000 }, + { 0x387f, 0x0000 }, + { 0x3880, 0x0000 }, + { 0x3881, 0x0000 }, + { 0x3882, 0x0000 }, + { 0x3883, 0x0000 }, + { 0x3884, 0x0000 }, + { 0x3885, 0x0000 }, + { 0x3886, 0x0000 }, + { 0x3887, 0x0000 }, + { 0x3888, 0x0000 }, + { 0x3889, 0x0000 }, + { 0x388a, 0x0000 }, + { 0x388b, 0x0000 }, + { 0x388c, 0x0000 }, + { 0x388d, 0x0000 }, + { 0x388e, 0x0000 }, + { 0x388f, 0x0000 }, + { 0x3890, 0x0000 }, + { 0x3891, 0x0000 }, + { 0x3892, 0x0000 }, + { 0x3893, 0x0000 }, + { 0x3894, 0x0000 }, + { 0x3895, 0x0000 }, + { 0x3896, 0x0000 }, + { 0x3897, 0x0000 }, + { 0x3898, 0x0000 }, + { 0x3899, 0x0000 }, + { 0x389a, 0x0000 }, + { 0x389b, 0x0000 }, + { 0x389c, 0x0000 }, + { 0x389d, 0x0000 }, + { 0x389e, 0x0000 }, + { 0x389f, 0x0000 }, + { 0x38a0, 0x0000 }, + { 0x38a1, 0x0000 }, + { 0x38a2, 0x0000 }, + { 0x38a3, 0x0000 }, + { 0x38a4, 0x0000 }, + { 0x38a5, 0x0000 }, + { 0x38a6, 0x0000 }, + { 0x38a7, 0x0000 }, + { 0x38a8, 0x0000 }, + { 0x38a9, 0x0000 }, + { 0x38aa, 0x0000 }, + { 0x38ab, 0x0000 }, + { 0x38ac, 0x0000 }, + { 0x38ad, 0x0000 }, + { 0x38ae, 0x0000 }, + { 0x38af, 0x0000 }, + { 0x38b0, 0x0000 }, + { 0x38b1, 0x0000 }, + { 0x38b2, 0x0000 }, + { 0x38b3, 0x0000 }, + { 0x38b4, 0x0000 }, + { 0x38b5, 0x0000 }, + { 0x38b6, 0x0000 }, + { 0x38b7, 0x0000 }, + { 0x38b8, 0x0000 }, + { 0x38b9, 0x0000 }, + { 0x38ba, 0x0000 }, + { 0x38bb, 0x0000 }, + { 0x38bc, 0x0000 }, + { 0x38bd, 0x0000 }, + { 0x38be, 0x0000 }, + { 0x38bf, 0x0000 }, + { 0x38c0, 0x0000 }, + { 0x38c1, 0x0000 }, + { 0x38c2, 0x0000 }, + { 0x38c3, 0x0000 }, + { 0x38c4, 0x0000 }, + { 0x38c5, 0x0000 }, + { 0x38c6, 0x0000 }, + { 0x38c7, 0x0000 }, + { 0x38c8, 0x0000 }, + { 0x38c9, 0x0000 }, + { 0x38ca, 0x0000 }, + { 0x38cb, 0x0000 }, + { 0x38cc, 0x0000 }, + { 0x38cd, 0x0000 }, + { 0x38ce, 0x0000 }, + { 0x38cf, 0x0000 }, + { 0x38d0, 0x0000 }, + { 0x38d1, 0x0000 }, + { 0x38d2, 0x0000 }, + { 0x38d3, 0x0000 }, + { 0x38d4, 0x0000 }, + { 0x38d5, 0x0000 }, + { 0x38d6, 0x0000 }, + { 0x38d7, 0x0000 }, + { 0x38d8, 0x0000 }, + { 0x38d9, 0x0000 }, + { 0x38da, 0x0000 }, + { 0x38db, 0x0000 }, + { 0x38dc, 0x0000 }, + { 0x38dd, 0x0000 }, + { 0x38de, 0x0000 }, + { 0x38df, 0x0000 }, + { 0x38e0, 0x0000 }, + { 0x38e1, 0x0000 }, + { 0x38e2, 0x0000 }, + { 0x38e3, 0x0000 }, + { 0x38e4, 0x0000 }, + { 0x38e5, 0x0000 }, + { 0x38e6, 0x0000 }, + { 0x38e7, 0x0000 }, + { 0x38e8, 0x0000 }, + { 0x38e9, 0x0000 }, + { 0x38ea, 0x0000 }, + { 0x38eb, 0x0000 }, + { 0x38ec, 0x0000 }, + { 0x38ed, 0x0000 }, + { 0x38ee, 0x0000 }, + { 0x38ef, 0x0000 }, + { 0x38f0, 0x0000 }, + { 0x38f1, 0x0000 }, + { 0x38f2, 0x0000 }, + { 0x38f3, 0x0000 }, + { 0x38f4, 0x0000 }, + { 0x38f5, 0x0000 }, + { 0x38f6, 0x0000 }, + { 0x38f7, 0x0000 }, + { 0x38f8, 0x0000 }, + { 0x38f9, 0x0000 }, + { 0x38fa, 0x0000 }, + { 0x38fb, 0x0000 }, + { 0x38fc, 0x0000 }, + { 0x38fd, 0x0000 }, + { 0x38fe, 0x0000 }, + { 0x38ff, 0x0000 }, + { 0x3900, 0x0000 }, + { 0x3901, 0x0000 }, + { 0x3902, 0x0000 }, + { 0x3903, 0x0000 }, + { 0x3904, 0x0000 }, + { 0x3905, 0x0000 }, + { 0x3906, 0x0000 }, + { 0x3907, 0x0000 }, + { 0x3908, 0x0000 }, + { 0x3909, 0x0000 }, + { 0x390a, 0x0000 }, + { 0x390b, 0x0000 }, + { 0x390c, 0x0000 }, + { 0x390d, 0x0000 }, + { 0x390e, 0x0000 }, + { 0x390f, 0x0000 }, + { 0x3910, 0x0000 }, + { 0x3911, 0x0000 }, + { 0x3912, 0x0000 }, + { 0x3913, 0x0000 }, + { 0x3914, 0x0000 }, + { 0x3915, 0x0000 }, + { 0x3916, 0x0000 }, + { 0x3917, 0x0000 }, + { 0x3918, 0x0000 }, + { 0x3919, 0x0000 }, + { 0x391a, 0x0000 }, + { 0x391b, 0x0000 }, + { 0x391c, 0x0000 }, + { 0x391d, 0x0000 }, + { 0x391e, 0x0000 }, + { 0x391f, 0x0000 }, + { 0x3920, 0x0000 }, + { 0x3921, 0x0000 }, + { 0x3922, 0x0000 }, + { 0x3923, 0x0000 }, + { 0x3924, 0x0000 }, + { 0x3925, 0x0000 }, + { 0x3926, 0x0000 }, + { 0x3927, 0x0000 }, + { 0x3928, 0x0000 }, + { 0x3929, 0x0000 }, + { 0x392a, 0x0000 }, + { 0x392b, 0x0000 }, + { 0x392c, 0x0000 }, + { 0x392d, 0x0000 }, + { 0x392e, 0x0000 }, + { 0x392f, 0x0000 }, + { 0x3930, 0x0000 }, + { 0x3931, 0x0000 }, + { 0x3932, 0x0000 }, + { 0x3933, 0x0000 }, + { 0x3934, 0x0000 }, + { 0x3935, 0x0000 }, + { 0x3936, 0x0000 }, + { 0x3937, 0x0000 }, + { 0x3938, 0x0000 }, + { 0x3939, 0x0000 }, + { 0x393a, 0x0000 }, + { 0x393b, 0x0000 }, + { 0x393c, 0x0000 }, + { 0x393d, 0x0000 }, + { 0x393e, 0x0000 }, + { 0x393f, 0x0000 }, + { 0x3940, 0x0000 }, + { 0x3941, 0x0000 }, + { 0x3942, 0x0000 }, + { 0x3943, 0x0000 }, + { 0x3944, 0x0000 }, + { 0x3945, 0x0000 }, + { 0x3946, 0x0000 }, + { 0x3947, 0x0000 }, + { 0x3948, 0x0000 }, + { 0x3949, 0x0000 }, + { 0x394a, 0x0000 }, + { 0x394b, 0x0000 }, + { 0x394c, 0x0000 }, + { 0x394d, 0x0000 }, + { 0x394e, 0x0000 }, + { 0x394f, 0x0000 }, + { 0x3950, 0x0000 }, + { 0x3951, 0x0000 }, + { 0x3952, 0x0000 }, + { 0x3953, 0x0000 }, + { 0x3954, 0x0000 }, + { 0x3955, 0x0000 }, + { 0x3956, 0x0000 }, + { 0x3957, 0x0000 }, + { 0x3958, 0x0000 }, + { 0x3959, 0x0000 }, + { 0x395a, 0x0000 }, + { 0x395b, 0x0000 }, + { 0x395c, 0x0000 }, + { 0x395d, 0x0000 }, + { 0x395e, 0x0000 }, + { 0x395f, 0x0000 }, + { 0x3960, 0x0000 }, + { 0x3961, 0x0000 }, + { 0x3962, 0x0000 }, + { 0x3963, 0x0000 }, + { 0x3964, 0x0000 }, + { 0x3965, 0x0000 }, + { 0x3966, 0x0000 }, + { 0x3967, 0x0000 }, + { 0x3968, 0x0000 }, + { 0x3969, 0x0000 }, + { 0x396a, 0x0000 }, + { 0x396b, 0x0000 }, + { 0x396c, 0x0000 }, + { 0x396d, 0x0000 }, + { 0x396e, 0x0000 }, + { 0x396f, 0x0000 }, + { 0x3970, 0x0000 }, + { 0x3971, 0x0000 }, + { 0x3972, 0x0000 }, + { 0x3973, 0x0000 }, + { 0x3974, 0x0000 }, + { 0x3975, 0x0000 }, + { 0x3976, 0x0000 }, + { 0x3977, 0x0000 }, + { 0x3978, 0x0000 }, + { 0x3979, 0x0000 }, + { 0x397a, 0x0000 }, + { 0x397b, 0x0000 }, + { 0x397c, 0x0000 }, + { 0x397d, 0x0000 }, + { 0x397e, 0x0000 }, + { 0x397f, 0x0000 }, + { 0x3980, 0x0000 }, + { 0x3981, 0x0000 }, + { 0x3982, 0x0000 }, + { 0x3983, 0x0000 }, + { 0x3984, 0x0000 }, + { 0x3985, 0x0000 }, + { 0x3986, 0x0000 }, + { 0x3987, 0x0000 }, + { 0x3988, 0x0000 }, + { 0x3989, 0x0000 }, + { 0x398a, 0x0000 }, + { 0x398b, 0x0000 }, + { 0x398c, 0x0000 }, + { 0x398d, 0x0000 }, + { 0x398e, 0x0000 }, + { 0x398f, 0x0000 }, + { 0x3990, 0x0000 }, + { 0x3991, 0x0000 }, + { 0x3992, 0x0000 }, + { 0x3993, 0x0000 }, + { 0x3994, 0x0000 }, + { 0x3995, 0x0000 }, + { 0x3996, 0x0000 }, + { 0x3997, 0x0000 }, + { 0x3998, 0x0000 }, + { 0x3999, 0x0000 }, + { 0x399a, 0x0000 }, + { 0x399b, 0x0000 }, + { 0x399c, 0x0000 }, + { 0x399d, 0x0000 }, + { 0x399e, 0x0000 }, + { 0x399f, 0x0000 }, + { 0x39a0, 0x0000 }, + { 0x39a1, 0x0000 }, + { 0x39a2, 0x0000 }, + { 0x39a3, 0x0000 }, + { 0x39a4, 0x0000 }, + { 0x39a5, 0x0000 }, + { 0x39a6, 0x0000 }, + { 0x39a7, 0x0000 }, + { 0x39a8, 0x0000 }, + { 0x39a9, 0x0000 }, + { 0x39aa, 0x0000 }, + { 0x39ab, 0x0000 }, + { 0x39ac, 0x0000 }, + { 0x39ad, 0x0000 }, + { 0x39ae, 0x0000 }, + { 0x39af, 0x0000 }, + { 0x39b0, 0x0000 }, + { 0x39b1, 0x0000 }, + { 0x39b2, 0x0000 }, + { 0x39b3, 0x0000 }, + { 0x39b4, 0x0000 }, + { 0x39b5, 0x0000 }, + { 0x39b6, 0x0000 }, + { 0x39b7, 0x0000 }, + { 0x39b8, 0x0000 }, + { 0x39b9, 0x0000 }, + { 0x39ba, 0x0000 }, + { 0x39bb, 0x0000 }, + { 0x39bc, 0x0000 }, + { 0x39bd, 0x0000 }, + { 0x39be, 0x0000 }, + { 0x39bf, 0x0000 }, + { 0x39c0, 0x0000 }, + { 0x39c1, 0x0000 }, + { 0x39c2, 0x0000 }, + { 0x39c3, 0x0000 }, + { 0x39c4, 0x0000 }, + { 0x39c5, 0x0000 }, + { 0x39c6, 0x0000 }, + { 0x39c7, 0x0000 }, + { 0x39c8, 0x0000 }, + { 0x39c9, 0x0000 }, + { 0x39ca, 0x0000 }, + { 0x39cb, 0x0000 }, + { 0x39cc, 0x0000 }, + { 0x39cd, 0x0000 }, + { 0x39ce, 0x0000 }, + { 0x39cf, 0x0000 }, + { 0x39d0, 0x0000 }, + { 0x39d1, 0x0000 }, + { 0x39d2, 0x0000 }, + { 0x39d3, 0x0000 }, + { 0x39d4, 0x0000 }, + { 0x39d5, 0x0000 }, + { 0x39d6, 0x0000 }, + { 0x39d7, 0x0000 }, + { 0x39d8, 0x0000 }, + { 0x39d9, 0x0000 }, + { 0x39da, 0x0000 }, + { 0x39db, 0x0000 }, + { 0x39dc, 0x0000 }, + { 0x39dd, 0x0000 }, + { 0x39de, 0x0000 }, + { 0x39df, 0x0000 }, + { 0x39e0, 0x0000 }, + { 0x39e1, 0x0000 }, + { 0x39e2, 0x0000 }, + { 0x39e3, 0x0000 }, + { 0x39e4, 0x0000 }, + { 0x39e5, 0x0000 }, + { 0x39e6, 0x0000 }, + { 0x39e7, 0x0000 }, + { 0x39e8, 0x0000 }, + { 0x39e9, 0x0000 }, + { 0x39ea, 0x0000 }, + { 0x39eb, 0x0000 }, + { 0x39ec, 0x0000 }, + { 0x39ed, 0x0000 }, + { 0x39ee, 0x0000 }, + { 0x39ef, 0x0000 }, + { 0x39f0, 0x0000 }, + { 0x39f1, 0x0000 }, + { 0x39f2, 0x0000 }, + { 0x39f3, 0x0000 }, + { 0x39f4, 0x0000 }, + { 0x39f5, 0x0000 }, + { 0x39f6, 0x0000 }, + { 0x39f7, 0x0000 }, + { 0x39f8, 0x0000 }, + { 0x39f9, 0x0000 }, + { 0x39fa, 0x0000 }, + { 0x39fb, 0x0000 }, + { 0x39fc, 0x0000 }, + { 0x39fd, 0x0000 }, + { 0x39fe, 0x0000 }, + { 0x39ff, 0x0000 }, + { 0x3a00, 0x0000 }, + { 0x3a01, 0x0000 }, + { 0x3a02, 0x0000 }, + { 0x3a03, 0x0000 }, + { 0x3a04, 0x0000 }, + { 0x3a05, 0x0000 }, + { 0x3a06, 0x0000 }, + { 0x3a07, 0x0000 }, + { 0x3a08, 0x0000 }, + { 0x3a09, 0x0000 }, + { 0x3a0a, 0x0000 }, + { 0x3a0b, 0x0000 }, + { 0x3a0c, 0x0000 }, + { 0x3a0d, 0x0000 }, + { 0x3a0e, 0x0000 }, + { 0x3a0f, 0x0000 }, + { 0x3a10, 0x0000 }, + { 0x3a11, 0x0000 }, + { 0x3a12, 0x0000 }, + { 0x3a13, 0x0000 }, + { 0x3a14, 0x0000 }, + { 0x3a15, 0x0000 }, + { 0x3a16, 0x0000 }, + { 0x3a17, 0x0000 }, + { 0x3a18, 0x0000 }, + { 0x3a19, 0x0000 }, + { 0x3a1a, 0x0000 }, + { 0x3a1b, 0x0000 }, + { 0x3a1c, 0x0000 }, + { 0x3a1d, 0x0000 }, + { 0x3a1e, 0x0000 }, + { 0x3a1f, 0x0000 }, + { 0x3a20, 0x0000 }, + { 0x3a21, 0x0000 }, + { 0x3a22, 0x0000 }, + { 0x3a23, 0x0000 }, + { 0x3a24, 0x0000 }, + { 0x3a25, 0x0000 }, + { 0x3a26, 0x0000 }, + { 0x3a27, 0x0000 }, + { 0x3a28, 0x0000 }, + { 0x3a29, 0x0000 }, + { 0x3a2a, 0x0000 }, + { 0x3a2b, 0x0000 }, + { 0x3a2c, 0x0000 }, + { 0x3a2d, 0x0000 }, + { 0x3a2e, 0x0000 }, + { 0x3a2f, 0x0000 }, + { 0x3a30, 0x0000 }, + { 0x3a31, 0x0000 }, + { 0x3a32, 0x0000 }, + { 0x3a33, 0x0000 }, + { 0x3a34, 0x0000 }, + { 0x3a35, 0x0000 }, + { 0x3a36, 0x0000 }, + { 0x3a37, 0x0000 }, + { 0x3a38, 0x0000 }, + { 0x3a39, 0x0000 }, + { 0x3a3a, 0x0000 }, + { 0x3a3b, 0x0000 }, + { 0x3a3c, 0x0000 }, + { 0x3a3d, 0x0000 }, + { 0x3a3e, 0x0000 }, + { 0x3a3f, 0x0000 }, + { 0x3a40, 0x0000 }, + { 0x3a41, 0x0000 }, + { 0x3a42, 0x0000 }, + { 0x3a43, 0x0000 }, + { 0x3a44, 0x0000 }, + { 0x3a45, 0x0000 }, + { 0x3a46, 0x0000 }, + { 0x3a47, 0x0000 }, + { 0x3a48, 0x0000 }, + { 0x3a49, 0x0000 }, + { 0x3a4a, 0x0000 }, + { 0x3a4b, 0x0000 }, + { 0x3a4c, 0x0000 }, + { 0x3a4d, 0x0000 }, + { 0x3a4e, 0x0000 }, + { 0x3a4f, 0x0000 }, + { 0x3a50, 0x0000 }, + { 0x3a51, 0x0000 }, + { 0x3a52, 0x0000 }, + { 0x3a53, 0x0000 }, + { 0x3a54, 0x0000 }, + { 0x3a55, 0x0000 }, + { 0x3a56, 0x0000 }, + { 0x3a57, 0x0000 }, + { 0x3a58, 0x0000 }, + { 0x3a59, 0x0000 }, + { 0x3a5a, 0x0000 }, + { 0x3a5b, 0x0000 }, + { 0x3a5c, 0x0000 }, + { 0x3a5d, 0x0000 }, + { 0x3a5e, 0x0000 }, + { 0x3a5f, 0x0000 }, + { 0x3a60, 0x0000 }, + { 0x3a61, 0x0000 }, + { 0x3a62, 0x0000 }, + { 0x3a63, 0x0000 }, + { 0x3a64, 0x0000 }, + { 0x3a65, 0x0000 }, + { 0x3a66, 0x0000 }, + { 0x3a67, 0x0000 }, + { 0x3a68, 0x0000 }, + { 0x3a69, 0x0000 }, + { 0x3a6a, 0x0000 }, + { 0x3a6b, 0x0000 }, + { 0x3a6c, 0x0000 }, + { 0x3a6d, 0x0000 }, + { 0x3a6e, 0x0000 }, + { 0x3a6f, 0x0000 }, + { 0x3a70, 0x0000 }, + { 0x3a71, 0x0000 }, + { 0x3a72, 0x0000 }, + { 0x3a73, 0x0000 }, + { 0x3a74, 0x0000 }, + { 0x3a75, 0x0000 }, + { 0x3a76, 0x0000 }, + { 0x3a77, 0x0000 }, + { 0x3a78, 0x0000 }, + { 0x3a79, 0x0000 }, + { 0x3a7a, 0x0000 }, + { 0x3a7b, 0x0000 }, + { 0x3a7c, 0x0000 }, + { 0x3a7d, 0x0000 }, + { 0x3a7e, 0x0000 }, + { 0x3a7f, 0x0000 }, + { 0x3a80, 0x0000 }, + { 0x3a81, 0x0000 }, + { 0x3a82, 0x0000 }, + { 0x3a83, 0x0000 }, + { 0x3a84, 0x0000 }, + { 0x3a85, 0x0000 }, + { 0x3a86, 0x0000 }, + { 0x3a87, 0x0000 }, + { 0x3a88, 0x0000 }, + { 0x3a89, 0x0000 }, + { 0x3a8a, 0x0000 }, + { 0x3a8b, 0x0000 }, + { 0x3a8c, 0x0000 }, + { 0x3a8d, 0x0000 }, + { 0x3a8e, 0x0000 }, + { 0x3a8f, 0x0000 }, + { 0x3a90, 0x0000 }, + { 0x3a91, 0x0000 }, + { 0x3a92, 0x0000 }, + { 0x3a93, 0x0000 }, + { 0x3a94, 0x0000 }, + { 0x3a95, 0x0000 }, + { 0x3a96, 0x0000 }, + { 0x3a97, 0x0000 }, + { 0x3a98, 0x0000 }, + { 0x3a99, 0x0000 }, + { 0x3a9a, 0x0000 }, + { 0x3a9b, 0x0000 }, + { 0x3a9c, 0x0000 }, + { 0x3a9d, 0x0000 }, + { 0x3a9e, 0x0000 }, + { 0x3a9f, 0x0000 }, + { 0x3aa0, 0x0000 }, + { 0x3aa1, 0x0000 }, + { 0x3aa2, 0x0000 }, + { 0x3aa3, 0x0000 }, + { 0x3aa4, 0x0000 }, + { 0x3aa5, 0x0000 }, + { 0x3aa6, 0x0000 }, + { 0x3aa7, 0x0000 }, + { 0x3aa8, 0x0000 }, + { 0x3aa9, 0x0000 }, + { 0x3aaa, 0x0000 }, + { 0x3aab, 0x0000 }, + { 0x3aac, 0x0000 }, + { 0x3aad, 0x0000 }, + { 0x3aae, 0x0000 }, + { 0x3aaf, 0x0000 }, + { 0x3ab0, 0x0000 }, + { 0x3ab1, 0x0000 }, + { 0x3ab2, 0x0000 }, + { 0x3ab3, 0x0000 }, + { 0x3ab4, 0x0000 }, + { 0x3ab5, 0x0000 }, + { 0x3ab6, 0x0000 }, + { 0x3ab7, 0x0000 }, + { 0x3ab8, 0x0000 }, + { 0x3ab9, 0x0000 }, + { 0x3aba, 0x0000 }, + { 0x3abb, 0x0000 }, + { 0x3abc, 0x0000 }, + { 0x3abd, 0x0000 }, + { 0x3abe, 0x0000 }, + { 0x3abf, 0x0000 }, + { 0x3ac0, 0x0000 }, + { 0x3ac1, 0x0000 }, + { 0x3ac2, 0x0000 }, + { 0x3ac3, 0x0000 }, + { 0x3ac4, 0x0000 }, + { 0x3ac5, 0x0000 }, + { 0x3ac6, 0x0000 }, + { 0x3ac7, 0x0000 }, + { 0x3ac8, 0x0000 }, + { 0x3ac9, 0x0000 }, + { 0x3aca, 0x0000 }, + { 0x3acb, 0x0000 }, + { 0x3acc, 0x0000 }, + { 0x3acd, 0x0000 }, + { 0x3ace, 0x0000 }, + { 0x3acf, 0x0000 }, + { 0x3ad0, 0x0000 }, + { 0x3ad1, 0x0000 }, + { 0x3ad2, 0x0000 }, + { 0x3ad3, 0x0000 }, + { 0x3ad4, 0x0000 }, + { 0x3ad5, 0x0000 }, + { 0x3ad6, 0x0000 }, + { 0x3ad7, 0x0000 }, + { 0x3ad8, 0x0000 }, + { 0x3ad9, 0x0000 }, + { 0x3ada, 0x0000 }, + { 0x3adb, 0x0000 }, + { 0x3adc, 0x0000 }, + { 0x3add, 0x0000 }, + { 0x3ade, 0x0000 }, + { 0x3adf, 0x0000 }, + { 0x3ae0, 0x0000 }, + { 0x3ae1, 0x0000 }, + { 0x3ae2, 0x0000 }, + { 0x3ae3, 0x0000 }, + { 0x3ae4, 0x0000 }, + { 0x3ae5, 0x0000 }, + { 0x3ae6, 0x0000 }, + { 0x3ae7, 0x0000 }, + { 0x3ae8, 0x0000 }, + { 0x3ae9, 0x0000 }, + { 0x3aea, 0x0000 }, + { 0x3aeb, 0x0000 }, + { 0x3aec, 0x0000 }, + { 0x3aed, 0x0000 }, + { 0x3aee, 0x0000 }, + { 0x3aef, 0x0000 }, + { 0x3af0, 0x0000 }, + { 0x3af1, 0x0000 }, + { 0x3af2, 0x0000 }, + { 0x3af3, 0x0000 }, + { 0x3af4, 0x0000 }, + { 0x3af5, 0x0000 }, + { 0x3af6, 0x0000 }, + { 0x3af7, 0x0000 }, + { 0x3af8, 0x0000 }, + { 0x3af9, 0x0000 }, + { 0x3afa, 0x0000 }, + { 0x3afb, 0x0000 }, + { 0x3afc, 0x0000 }, + { 0x3afd, 0x0000 }, + { 0x3afe, 0x0000 }, + { 0x3aff, 0x0000 }, + { 0x3b00, 0x0000 }, + { 0x3b01, 0x0000 }, + { 0x3b02, 0x0000 }, + { 0x3b03, 0x0000 }, + { 0x3b04, 0x0000 }, + { 0x3b05, 0x0000 }, + { 0x3b06, 0x0000 }, + { 0x3b07, 0x0000 }, + { 0x3b08, 0x0000 }, + { 0x3b09, 0x0000 }, + { 0x3b0a, 0x0000 }, + { 0x3b0b, 0x0000 }, + { 0x3b0c, 0x0000 }, + { 0x3b0d, 0x0000 }, + { 0x3b0e, 0x0000 }, + { 0x3b0f, 0x0000 }, + { 0x3b10, 0x0000 }, + { 0x3b11, 0x0000 }, + { 0x3b12, 0x0000 }, + { 0x3b13, 0x0000 }, + { 0x3b14, 0x0000 }, + { 0x3b15, 0x0000 }, + { 0x3b16, 0x0000 }, + { 0x3b17, 0x0000 }, + { 0x3b18, 0x0000 }, + { 0x3b19, 0x0000 }, + { 0x3b1a, 0x0000 }, + { 0x3b1b, 0x0000 }, + { 0x3b1c, 0x0000 }, + { 0x3b1d, 0x0000 }, + { 0x3b1e, 0x0000 }, + { 0x3b1f, 0x0000 }, + { 0x3b20, 0x0000 }, + { 0x3b21, 0x0000 }, + { 0x3b22, 0x0000 }, + { 0x3b23, 0x0000 }, + { 0x3b24, 0x0000 }, + { 0x3b25, 0x0000 }, + { 0x3b26, 0x0000 }, + { 0x3b27, 0x0000 }, + { 0x3b28, 0x0000 }, + { 0x3b29, 0x0000 }, + { 0x3b2a, 0x0000 }, + { 0x3b2b, 0x0000 }, + { 0x3b2c, 0x0000 }, + { 0x3b2d, 0x0000 }, + { 0x3b2e, 0x0000 }, + { 0x3b2f, 0x0000 }, + { 0x3b30, 0x0000 }, + { 0x3b31, 0x0000 }, + { 0x3b32, 0x0000 }, + { 0x3b33, 0x0000 }, + { 0x3b34, 0x0000 }, + { 0x3b35, 0x0000 }, + { 0x3b36, 0x0000 }, + { 0x3b37, 0x0000 }, + { 0x3b38, 0x0000 }, + { 0x3b39, 0x0000 }, + { 0x3b3a, 0x0000 }, + { 0x3b3b, 0x0000 }, + { 0x3b3c, 0x0000 }, + { 0x3b3d, 0x0000 }, + { 0x3b3e, 0x0000 }, + { 0x3b3f, 0x0000 }, + { 0x3b40, 0x0000 }, + { 0x3b41, 0x0000 }, + { 0x3b42, 0x0000 }, + { 0x3b43, 0x0000 }, + { 0x3b44, 0x0000 }, + { 0x3b45, 0x0000 }, + { 0x3b46, 0x0000 }, + { 0x3b47, 0x0000 }, + { 0x3b48, 0x0000 }, + { 0x3b49, 0x0000 }, + { 0x3b4a, 0x0000 }, + { 0x3b4b, 0x0000 }, + { 0x3b4c, 0x0000 }, + { 0x3b4d, 0x0000 }, + { 0x3b4e, 0x0000 }, + { 0x3b4f, 0x0000 }, + { 0x3b50, 0x0000 }, + { 0x3b51, 0x0000 }, + { 0x3b52, 0x0000 }, + { 0x3b53, 0x0000 }, + { 0x3b54, 0x0000 }, + { 0x3b55, 0x0000 }, + { 0x3b56, 0x0000 }, + { 0x3b57, 0x0000 }, + { 0x3b58, 0x0000 }, + { 0x3b59, 0x0000 }, + { 0x3b5a, 0x0000 }, + { 0x3b5b, 0x0000 }, + { 0x3b5c, 0x0000 }, + { 0x3b5d, 0x0000 }, + { 0x3b5e, 0x0000 }, + { 0x3b5f, 0x0000 }, + { 0x3b60, 0x0000 }, + { 0x3b61, 0x0000 }, + { 0x3b62, 0x0000 }, + { 0x3b63, 0x0000 }, + { 0x3b64, 0x0000 }, + { 0x3b65, 0x0000 }, + { 0x3b66, 0x0000 }, + { 0x3b67, 0x0000 }, + { 0x3b68, 0x0000 }, + { 0x3b69, 0x0000 }, + { 0x3b6a, 0x0000 }, + { 0x3b6b, 0x0000 }, + { 0x3b6c, 0x0000 }, + { 0x3b6d, 0x0000 }, + { 0x3b6e, 0x0000 }, + { 0x3b6f, 0x0000 }, + { 0x3b70, 0x0000 }, + { 0x3b71, 0x0000 }, + { 0x3b72, 0x0000 }, + { 0x3b73, 0x0000 }, + { 0x3b74, 0x0000 }, + { 0x3b75, 0x0000 }, + { 0x3b76, 0x0000 }, + { 0x3b77, 0x0000 }, + { 0x3b78, 0x0000 }, + { 0x3b79, 0x0000 }, + { 0x3b7a, 0x0000 }, + { 0x3b7b, 0x0000 }, + { 0x3b7c, 0x0000 }, + { 0x3b7d, 0x0000 }, + { 0x3b7e, 0x0000 }, + { 0x3b7f, 0x0000 }, + { 0x3b80, 0x0000 }, + { 0x3b81, 0x0000 }, + { 0x3b82, 0x0000 }, + { 0x3b83, 0x0000 }, + { 0x3b84, 0x0000 }, + { 0x3b85, 0x0000 }, + { 0x3b86, 0x0000 }, + { 0x3b87, 0x0000 }, + { 0x3b88, 0x0000 }, + { 0x3b89, 0x0000 }, + { 0x3b8a, 0x0000 }, + { 0x3b8b, 0x0000 }, + { 0x3b8c, 0x0000 }, + { 0x3b8d, 0x0000 }, + { 0x3b8e, 0x0000 }, + { 0x3b8f, 0x0000 }, + { 0x3b90, 0x0000 }, + { 0x3b91, 0x0000 }, + { 0x3b92, 0x0000 }, + { 0x3b93, 0x0000 }, + { 0x3b94, 0x0000 }, + { 0x3b95, 0x0000 }, + { 0x3b96, 0x0000 }, + { 0x3b97, 0x0000 }, + { 0x3b98, 0x0000 }, + { 0x3b99, 0x0000 }, + { 0x3b9a, 0x0000 }, + { 0x3b9b, 0x0000 }, + { 0x3b9c, 0x0000 }, + { 0x3b9d, 0x0000 }, + { 0x3b9e, 0x0000 }, + { 0x3b9f, 0x0000 }, + { 0x3ba0, 0x0000 }, + { 0x3ba1, 0x0000 }, + { 0x3ba2, 0x0000 }, + { 0x3ba3, 0x0000 }, + { 0x3ba4, 0x0000 }, + { 0x3ba5, 0x0000 }, + { 0x3ba6, 0x0000 }, + { 0x3ba7, 0x0000 }, + { 0x3ba8, 0x0000 }, + { 0x3ba9, 0x0000 }, + { 0x3baa, 0x0000 }, + { 0x3bab, 0x0000 }, + { 0x3bac, 0x0000 }, + { 0x3bad, 0x0000 }, + { 0x3bae, 0x0000 }, + { 0x3baf, 0x0000 }, + { 0x3bb0, 0x0000 }, + { 0x3bb1, 0x0000 }, + { 0x3bb2, 0x0000 }, + { 0x3bb3, 0x0000 }, + { 0x3bb4, 0x0000 }, + { 0x3bb5, 0x0000 }, + { 0x3bb6, 0x0000 }, + { 0x3bb7, 0x0000 }, + { 0x3bb8, 0x0000 }, + { 0x3bb9, 0x0000 }, + { 0x3bba, 0x0000 }, + { 0x3bbb, 0x0000 }, + { 0x3bbc, 0x0000 }, + { 0x3bbd, 0x0000 }, + { 0x3bbe, 0x0000 }, + { 0x3bbf, 0x0000 }, + { 0x3bc0, 0x0000 }, + { 0x3bc1, 0x0000 }, + { 0x3bc2, 0x0000 }, + { 0x3bc3, 0x0000 }, + { 0x3bc4, 0x0000 }, + { 0x3bc5, 0x0000 }, + { 0x3bc6, 0x0000 }, + { 0x3bc7, 0x0000 }, + { 0x3bc8, 0x0000 }, + { 0x3bc9, 0x0000 }, + { 0x3bca, 0x0000 }, + { 0x3bcb, 0x0000 }, + { 0x3bcc, 0x0000 }, + { 0x3bcd, 0x0000 }, + { 0x3bce, 0x0000 }, + { 0x3bcf, 0x0000 }, + { 0x3bd0, 0x0000 }, + { 0x3bd1, 0x0000 }, + { 0x3bd2, 0x0000 }, + { 0x3bd3, 0x0000 }, + { 0x3bd4, 0x0000 }, + { 0x3bd5, 0x0000 }, + { 0x3bd6, 0x0000 }, + { 0x3bd7, 0x0000 }, + { 0x3bd8, 0x0000 }, + { 0x3bd9, 0x0000 }, + { 0x3bda, 0x0000 }, + { 0x3bdb, 0x0000 }, + { 0x3bdc, 0x0000 }, + { 0x3bdd, 0x0000 }, + { 0x3bde, 0x0000 }, + { 0x3bdf, 0x0000 }, + { 0x3be0, 0x0000 }, + { 0x3be1, 0x0000 }, + { 0x3be2, 0x0000 }, + { 0x3be3, 0x0000 }, + { 0x3be4, 0x0000 }, + { 0x3be5, 0x0000 }, + { 0x3be6, 0x0000 }, + { 0x3be7, 0x0000 }, + { 0x3be8, 0x0000 }, + { 0x3be9, 0x0000 }, + { 0x3bea, 0x0000 }, + { 0x3beb, 0x0000 }, + { 0x3bec, 0x0000 }, + { 0x3bed, 0x0000 }, + { 0x3bee, 0x0000 }, + { 0x3bef, 0x0000 }, + { 0x3bf0, 0x0000 }, + { 0x3bf1, 0x0000 }, + { 0x3bf2, 0x0000 }, + { 0x3bf3, 0x0000 }, + { 0x3bf4, 0x0000 }, + { 0x3bf5, 0x0000 }, + { 0x3bf6, 0x0000 }, + { 0x3bf7, 0x0000 }, + { 0x3bf8, 0x0000 }, + { 0x3bf9, 0x0000 }, + { 0x3bfa, 0x0000 }, + { 0x3bfb, 0x0000 }, + { 0x3bfc, 0x0000 }, + { 0x3bfd, 0x0000 }, + { 0x3bfe, 0x0000 }, + { 0x3bff, 0x0000 }, + { 0x3c00, 0x0000 }, + { 0x3c01, 0x0000 }, + { 0x3c02, 0x0000 }, + { 0x3c03, 0x0000 }, + { 0x3c04, 0x0000 }, + { 0x3c05, 0x0000 }, + { 0x3c06, 0x0000 }, + { 0x3c07, 0x0000 }, + { 0x3c08, 0x0000 }, + { 0x3c09, 0x0000 }, + { 0x3c0a, 0x0000 }, + { 0x3c0b, 0x0000 }, + { 0x3c0c, 0x0000 }, + { 0x3c0d, 0x0000 }, + { 0x3c0e, 0x0000 }, + { 0x3c0f, 0x0000 }, + { 0x3c10, 0x0000 }, + { 0x3c11, 0x0000 }, + { 0x3c12, 0x0000 }, + { 0x3c13, 0x0000 }, + { 0x3c14, 0x0000 }, + { 0x3c15, 0x0000 }, + { 0x3c16, 0x0000 }, + { 0x3c17, 0x0000 }, + { 0x3c18, 0x0000 }, + { 0x3c19, 0x0000 }, + { 0x3c1a, 0x0000 }, + { 0x3c1b, 0x0000 }, + { 0x3c1c, 0x0000 }, + { 0x3c1d, 0x0000 }, + { 0x3c1e, 0x0000 }, + { 0x3c1f, 0x0000 }, + { 0x3c20, 0x0000 }, + { 0x3c21, 0x0000 }, + { 0x3c22, 0x0000 }, + { 0x3c23, 0x0000 }, + { 0x3c24, 0x0000 }, + { 0x3c25, 0x0000 }, + { 0x3c26, 0x0000 }, + { 0x3c27, 0x0000 }, + { 0x3c28, 0x0000 }, + { 0x3c29, 0x0000 }, + { 0x3c2a, 0x0000 }, + { 0x3c2b, 0x0000 }, + { 0x3c2c, 0x0000 }, + { 0x3c2d, 0x0000 }, + { 0x3c2e, 0x0000 }, + { 0x3c2f, 0x0000 }, + { 0x3c30, 0x0000 }, + { 0x3c31, 0x0000 }, + { 0x3c32, 0x0000 }, + { 0x3c33, 0x0000 }, + { 0x3c34, 0x0000 }, + { 0x3c35, 0x0000 }, + { 0x3c36, 0x0000 }, + { 0x3c37, 0x0000 }, + { 0x3c38, 0x0000 }, + { 0x3c39, 0x0000 }, + { 0x3c3a, 0x0000 }, + { 0x3c3b, 0x0000 }, + { 0x3c3c, 0x0000 }, + { 0x3c3d, 0x0000 }, + { 0x3c3e, 0x0000 }, + { 0x3c3f, 0x0000 }, + { 0x3c40, 0x0000 }, + { 0x3c41, 0x0000 }, + { 0x3c42, 0x0000 }, + { 0x3c43, 0x0000 }, + { 0x3c44, 0x0000 }, + { 0x3c45, 0x0000 }, + { 0x3c46, 0x0000 }, + { 0x3c47, 0x0000 }, + { 0x3c48, 0x0000 }, + { 0x3c49, 0x0000 }, + { 0x3c4a, 0x0000 }, + { 0x3c4b, 0x0000 }, + { 0x3c4c, 0x0000 }, + { 0x3c4d, 0x0000 }, + { 0x3c4e, 0x0000 }, + { 0x3c4f, 0x0000 }, + { 0x3c50, 0x0000 }, + { 0x3c51, 0x0000 }, + { 0x3c52, 0x0000 }, + { 0x3c53, 0x0000 }, + { 0x3c54, 0x0000 }, + { 0x3c55, 0x0000 }, + { 0x3c56, 0x0000 }, + { 0x3c57, 0x0000 }, + { 0x3c58, 0x0000 }, + { 0x3c59, 0x0000 }, + { 0x3c5a, 0x0000 }, + { 0x3c5b, 0x0000 }, + { 0x3c5c, 0x0000 }, + { 0x3c5d, 0x0000 }, + { 0x3c5e, 0x0000 }, + { 0x3c5f, 0x0000 }, + { 0x3c60, 0x0000 }, + { 0x3c61, 0x0000 }, + { 0x3c62, 0x0000 }, + { 0x3c63, 0x0000 }, + { 0x3c64, 0x0000 }, + { 0x3c65, 0x0000 }, + { 0x3c66, 0x0000 }, + { 0x3c67, 0x0000 }, + { 0x3c68, 0x0000 }, + { 0x3c69, 0x0000 }, + { 0x3c6a, 0x0000 }, + { 0x3c6b, 0x0000 }, + { 0x3c6c, 0x0000 }, + { 0x3c6d, 0x0000 }, + { 0x3c6e, 0x0000 }, + { 0x3c6f, 0x0000 }, + { 0x3c70, 0x0000 }, + { 0x3c71, 0x0000 }, + { 0x3c72, 0x0000 }, + { 0x3c73, 0x0000 }, + { 0x3c74, 0x0000 }, + { 0x3c75, 0x0000 }, + { 0x3c76, 0x0000 }, + { 0x3c77, 0x0000 }, + { 0x3c78, 0x0000 }, + { 0x3c79, 0x0000 }, + { 0x3c7a, 0x0000 }, + { 0x3c7b, 0x0000 }, + { 0x3c7c, 0x0000 }, + { 0x3c7d, 0x0000 }, + { 0x3c7e, 0x0000 }, + { 0x3c7f, 0x0000 }, + { 0x3c80, 0x0000 }, + { 0x3c81, 0x0000 }, + { 0x3c82, 0x0000 }, + { 0x3c83, 0x0000 }, + { 0x3c84, 0x0000 }, + { 0x3c85, 0x0000 }, + { 0x3c86, 0x0000 }, + { 0x3c87, 0x0000 }, + { 0x3c88, 0x0000 }, + { 0x3c89, 0x0000 }, + { 0x3c8a, 0x0000 }, + { 0x3c8b, 0x0000 }, + { 0x3c8c, 0x0000 }, + { 0x3c8d, 0x0000 }, + { 0x3c8e, 0x0000 }, + { 0x3c8f, 0x0000 }, + { 0x3c90, 0x0000 }, + { 0x3c91, 0x0000 }, + { 0x3c92, 0x0000 }, + { 0x3c93, 0x0000 }, + { 0x3c94, 0x0000 }, + { 0x3c95, 0x0000 }, + { 0x3c96, 0x0000 }, + { 0x3c97, 0x0000 }, + { 0x3c98, 0x0000 }, + { 0x3c99, 0x0000 }, + { 0x3c9a, 0x0000 }, + { 0x3c9b, 0x0000 }, + { 0x3c9c, 0x0000 }, + { 0x3c9d, 0x0000 }, + { 0x3c9e, 0x0000 }, + { 0x3c9f, 0x0000 }, + { 0x3ca0, 0x0000 }, + { 0x3ca1, 0x0000 }, + { 0x3ca2, 0x0000 }, + { 0x3ca3, 0x0000 }, + { 0x3ca4, 0x0000 }, + { 0x3ca5, 0x0000 }, + { 0x3ca6, 0x0000 }, + { 0x3ca7, 0x0000 }, + { 0x3ca8, 0x0000 }, + { 0x3ca9, 0x0000 }, + { 0x3caa, 0x0000 }, + { 0x3cab, 0x0000 }, + { 0x3cac, 0x0000 }, + { 0x3cad, 0x0000 }, + { 0x3cae, 0x0000 }, + { 0x3caf, 0x0000 }, + { 0x3cb0, 0x0000 }, + { 0x3cb1, 0x0000 }, + { 0x3cb2, 0x0000 }, + { 0x3cb3, 0x0000 }, + { 0x3cb4, 0x0000 }, + { 0x3cb5, 0x0000 }, + { 0x3cb6, 0x0000 }, + { 0x3cb7, 0x0000 }, + { 0x3cb8, 0x0000 }, + { 0x3cb9, 0x0000 }, + { 0x3cba, 0x0000 }, + { 0x3cbb, 0x0000 }, + { 0x3cbc, 0x0000 }, + { 0x3cbd, 0x0000 }, + { 0x3cbe, 0x0000 }, + { 0x3cbf, 0x0000 }, + { 0x3cc0, 0x0000 }, + { 0x3cc1, 0x0000 }, + { 0x3cc2, 0x0000 }, + { 0x3cc3, 0x0000 }, + { 0x3cc4, 0x0000 }, + { 0x3cc5, 0x0000 }, + { 0x3cc6, 0x0000 }, + { 0x3cc7, 0x0000 }, + { 0x3cc8, 0x0000 }, + { 0x3cc9, 0x0000 }, + { 0x3cca, 0x0000 }, + { 0x3ccb, 0x0000 }, + { 0x3ccc, 0x0000 }, + { 0x3ccd, 0x0000 }, + { 0x3cce, 0x0000 }, + { 0x3ccf, 0x0000 }, + { 0x3cd0, 0x0000 }, + { 0x3cd1, 0x0000 }, + { 0x3cd2, 0x0000 }, + { 0x3cd3, 0x0000 }, + { 0x3cd4, 0x0000 }, + { 0x3cd5, 0x0000 }, + { 0x3cd6, 0x0000 }, + { 0x3cd7, 0x0000 }, + { 0x3cd8, 0x0000 }, + { 0x3cd9, 0x0000 }, + { 0x3cda, 0x0000 }, + { 0x3cdb, 0x0000 }, + { 0x3cdc, 0x0000 }, + { 0x3cdd, 0x0000 }, + { 0x3cde, 0x0000 }, + { 0x3cdf, 0x0000 }, + { 0x3ce0, 0x0000 }, + { 0x3ce1, 0x0000 }, + { 0x3ce2, 0x0000 }, + { 0x3ce3, 0x0000 }, + { 0x3ce4, 0x0000 }, + { 0x3ce5, 0x0000 }, + { 0x3ce6, 0x0000 }, + { 0x3ce7, 0x0000 }, + { 0x3ce8, 0x0000 }, + { 0x3ce9, 0x0000 }, + { 0x3cea, 0x0000 }, + { 0x3ceb, 0x0000 }, + { 0x3cec, 0x0000 }, + { 0x3ced, 0x0000 }, + { 0x3cee, 0x0000 }, + { 0x3cef, 0x0000 }, + { 0x3cf0, 0x0000 }, + { 0x3cf1, 0x0000 }, + { 0x3cf2, 0x0000 }, + { 0x3cf3, 0x0000 }, + { 0x3cf4, 0x0000 }, + { 0x3cf5, 0x0000 }, + { 0x3cf6, 0x0000 }, + { 0x3cf7, 0x0000 }, + { 0x3cf8, 0x0000 }, + { 0x3cf9, 0x0000 }, + { 0x3cfa, 0x0000 }, + { 0x3cfb, 0x0000 }, + { 0x3cfc, 0x0000 }, + { 0x3cfd, 0x0000 }, + { 0x3cfe, 0x0000 }, + { 0x3cff, 0x0000 }, + { 0x3d00, 0x0000 }, + { 0x3d01, 0x0000 }, + { 0x3d02, 0x0000 }, + { 0x3d03, 0x0000 }, + { 0x3d04, 0x0000 }, + { 0x3d05, 0x0000 }, + { 0x3d06, 0x0000 }, + { 0x3d07, 0x0000 }, + { 0x3d08, 0x0000 }, + { 0x3d09, 0x0000 }, + { 0x3d0a, 0x0000 }, + { 0x3d0b, 0x0000 }, + { 0x3d0c, 0x0000 }, + { 0x3d0d, 0x0000 }, + { 0x3d0e, 0x0000 }, + { 0x3d0f, 0x0000 }, + { 0x3d10, 0x0000 }, + { 0x3d11, 0x0000 }, + { 0x3d12, 0x0000 }, + { 0x3d13, 0x0000 }, + { 0x3d14, 0x0000 }, + { 0x3d15, 0x0000 }, + { 0x3d16, 0x0000 }, + { 0x3d17, 0x0000 }, + { 0x3d18, 0x0000 }, + { 0x3d19, 0x0000 }, + { 0x3d1a, 0x0000 }, + { 0x3d1b, 0x0000 }, + { 0x3d1c, 0x0000 }, + { 0x3d1d, 0x0000 }, + { 0x3d1e, 0x0000 }, + { 0x3d1f, 0x0000 }, + { 0x3d20, 0x0000 }, + { 0x3d21, 0x0000 }, + { 0x3d22, 0x0000 }, + { 0x3d23, 0x0000 }, + { 0x3d24, 0x0000 }, + { 0x3d25, 0x0000 }, + { 0x3d26, 0x0000 }, + { 0x3d27, 0x0000 }, + { 0x3d28, 0x0000 }, + { 0x3d29, 0x0000 }, + { 0x3d2a, 0x0000 }, + { 0x3d2b, 0x0000 }, + { 0x3d2c, 0x0000 }, + { 0x3d2d, 0x0000 }, + { 0x3d2e, 0x0000 }, + { 0x3d2f, 0x0000 }, + { 0x3d30, 0x0000 }, + { 0x3d31, 0x0000 }, + { 0x3d32, 0x0000 }, + { 0x3d33, 0x0000 }, + { 0x3d34, 0x0000 }, + { 0x3d35, 0x0000 }, + { 0x3d36, 0x0000 }, + { 0x3d37, 0x0000 }, + { 0x3d38, 0x0000 }, + { 0x3d39, 0x0000 }, + { 0x3d3a, 0x0000 }, + { 0x3d3b, 0x0000 }, + { 0x3d3c, 0x0000 }, + { 0x3d3d, 0x0000 }, + { 0x3d3e, 0x0000 }, + { 0x3d3f, 0x0000 }, + { 0x3d40, 0x0000 }, + { 0x3d41, 0x0000 }, + { 0x3d42, 0x0000 }, + { 0x3d43, 0x0000 }, + { 0x3d44, 0x0000 }, + { 0x3d45, 0x0000 }, + { 0x3d46, 0x0000 }, + { 0x3d47, 0x0000 }, + { 0x3d48, 0x0000 }, + { 0x3d49, 0x0000 }, + { 0x3d4a, 0x0000 }, + { 0x3d4b, 0x0000 }, + { 0x3d4c, 0x0000 }, + { 0x3d4d, 0x0000 }, + { 0x3d4e, 0x0000 }, + { 0x3d4f, 0x0000 }, + { 0x3d50, 0x0000 }, + { 0x3d51, 0x0000 }, + { 0x3d52, 0x0000 }, + { 0x3d53, 0x0000 }, + { 0x3d54, 0x0000 }, + { 0x3d55, 0x0000 }, + { 0x3d56, 0x0000 }, + { 0x3d57, 0x0000 }, + { 0x3d58, 0x0000 }, + { 0x3d59, 0x0000 }, + { 0x3d5a, 0x0000 }, + { 0x3d5b, 0x0000 }, + { 0x3d5c, 0x0000 }, + { 0x3d5d, 0x0000 }, + { 0x3d5e, 0x0000 }, + { 0x3d5f, 0x0000 }, + { 0x3d60, 0x0000 }, + { 0x3d61, 0x0000 }, + { 0x3d62, 0x0000 }, + { 0x3d63, 0x0000 }, + { 0x3d64, 0x0000 }, + { 0x3d65, 0x0000 }, + { 0x3d66, 0x0000 }, + { 0x3d67, 0x0000 }, + { 0x3d68, 0x0000 }, + { 0x3d69, 0x0000 }, + { 0x3d6a, 0x0000 }, + { 0x3d6b, 0x0000 }, + { 0x3d6c, 0x0000 }, + { 0x3d6d, 0x0000 }, + { 0x3d6e, 0x0000 }, + { 0x3d6f, 0x0000 }, + { 0x3d70, 0x0000 }, + { 0x3d71, 0x0000 }, + { 0x3d72, 0x0000 }, + { 0x3d73, 0x0000 }, + { 0x3d74, 0x0000 }, + { 0x3d75, 0x0000 }, + { 0x3d76, 0x0000 }, + { 0x3d77, 0x0000 }, + { 0x3d78, 0x0000 }, + { 0x3d79, 0x0000 }, + { 0x3d7a, 0x0000 }, + { 0x3d7b, 0x0000 }, + { 0x3d7c, 0x0000 }, + { 0x3d7d, 0x0000 }, + { 0x3d7e, 0x0000 }, + { 0x3d7f, 0x0000 }, + { 0x3d80, 0x0000 }, + { 0x3d81, 0x0000 }, + { 0x3d82, 0x0000 }, + { 0x3d83, 0x0000 }, + { 0x3d84, 0x0000 }, + { 0x3d85, 0x0000 }, + { 0x3d86, 0x0000 }, + { 0x3d87, 0x0000 }, + { 0x3d88, 0x0000 }, + { 0x3d89, 0x0000 }, + { 0x3d8a, 0x0000 }, + { 0x3d8b, 0x0000 }, + { 0x3d8c, 0x0000 }, + { 0x3d8d, 0x0000 }, + { 0x3d8e, 0x0000 }, + { 0x3d8f, 0x0000 }, + { 0x3d90, 0x0000 }, + { 0x3d91, 0x0000 }, + { 0x3d92, 0x0000 }, + { 0x3d93, 0x0000 }, + { 0x3d94, 0x0000 }, + { 0x3d95, 0x0000 }, + { 0x3d96, 0x0000 }, + { 0x3d97, 0x0000 }, + { 0x3d98, 0x0000 }, + { 0x3d99, 0x0000 }, + { 0x3d9a, 0x0000 }, + { 0x3d9b, 0x0000 }, + { 0x3d9c, 0x0000 }, + { 0x3d9d, 0x0000 }, + { 0x3d9e, 0x0000 }, + { 0x3d9f, 0x0000 }, + { 0x3da0, 0x0000 }, + { 0x3da1, 0x0000 }, + { 0x3da2, 0x0000 }, + { 0x3da3, 0x0000 }, + { 0x3da4, 0x0000 }, + { 0x3da5, 0x0000 }, + { 0x3da6, 0x0000 }, + { 0x3da7, 0x0000 }, + { 0x3da8, 0x0000 }, + { 0x3da9, 0x0000 }, + { 0x3daa, 0x0000 }, + { 0x3dab, 0x0000 }, + { 0x3dac, 0x0000 }, + { 0x3dad, 0x0000 }, + { 0x3dae, 0x0000 }, + { 0x3daf, 0x0000 }, + { 0x3db0, 0x0000 }, + { 0x3db1, 0x0000 }, + { 0x3db2, 0x0000 }, + { 0x3db3, 0x0000 }, + { 0x3db4, 0x0000 }, + { 0x3db5, 0x0000 }, + { 0x3db6, 0x0000 }, + { 0x3db7, 0x0000 }, + { 0x3db8, 0x0000 }, + { 0x3db9, 0x0000 }, + { 0x3dba, 0x0000 }, + { 0x3dbb, 0x0000 }, + { 0x3dbc, 0x0000 }, + { 0x3dbd, 0x0000 }, + { 0x3dbe, 0x0000 }, + { 0x3dbf, 0x0000 }, + { 0x3dc0, 0x0000 }, + { 0x3dc1, 0x0000 }, + { 0x3dc2, 0x0000 }, + { 0x3dc3, 0x0000 }, + { 0x3dc4, 0x0000 }, + { 0x3dc5, 0x0000 }, + { 0x3dc6, 0x0000 }, + { 0x3dc7, 0x0000 }, + { 0x3dc8, 0x0000 }, + { 0x3dc9, 0x0000 }, + { 0x3dca, 0x0000 }, + { 0x3dcb, 0x0000 }, + { 0x3dcc, 0x0000 }, + { 0x3dcd, 0x0000 }, + { 0x3dce, 0x0000 }, + { 0x3dcf, 0x0000 }, + { 0x3dd0, 0x0000 }, + { 0x3dd1, 0x0000 }, + { 0x3dd2, 0x0000 }, + { 0x3dd3, 0x0000 }, + { 0x3dd4, 0x0000 }, + { 0x3dd5, 0x0000 }, + { 0x3dd6, 0x0000 }, + { 0x3dd7, 0x0000 }, + { 0x3dd8, 0x0000 }, + { 0x3dd9, 0x0000 }, + { 0x3dda, 0x0000 }, + { 0x3ddb, 0x0000 }, + { 0x3ddc, 0x0000 }, + { 0x3ddd, 0x0000 }, + { 0x3dde, 0x0000 }, + { 0x3ddf, 0x0000 }, + { 0x3de0, 0x0000 }, + { 0x3de1, 0x0000 }, + { 0x3de2, 0x0000 }, + { 0x3de3, 0x0000 }, + { 0x3de4, 0x0000 }, + { 0x3de5, 0x0000 }, + { 0x3de6, 0x0000 }, + { 0x3de7, 0x0000 }, + { 0x3de8, 0x0000 }, + { 0x3de9, 0x0000 }, + { 0x3dea, 0x0000 }, + { 0x3deb, 0x0000 }, + { 0x3dec, 0x0000 }, + { 0x3ded, 0x0000 }, + { 0x3dee, 0x0000 }, + { 0x3def, 0x0000 }, + { 0x3df0, 0x0000 }, + { 0x3df1, 0x0000 }, + { 0x3df2, 0x0000 }, + { 0x3df3, 0x0000 }, + { 0x3df4, 0x0000 }, + { 0x3df5, 0x0000 }, + { 0x3df6, 0x0000 }, + { 0x3df7, 0x0000 }, + { 0x3df8, 0x0000 }, + { 0x3df9, 0x0000 }, + { 0x3dfa, 0x0000 }, + { 0x3dfb, 0x0000 }, + { 0x3dfc, 0x0000 }, + { 0x3dfd, 0x0000 }, + { 0x3dfe, 0x0000 }, + { 0x3dff, 0x0000 }, + { 0x3e00, 0x0000 }, + { 0x3e01, 0x0000 }, + { 0x3e02, 0x0000 }, + { 0x3e03, 0x0000 }, + { 0x3e04, 0x0000 }, + { 0x3e05, 0x0000 }, + { 0x3e06, 0x0000 }, + { 0x3e07, 0x0000 }, + { 0x3e08, 0x0000 }, + { 0x3e09, 0x0000 }, + { 0x3e0a, 0x0000 }, + { 0x3e0b, 0x0000 }, + { 0x3e0c, 0x0000 }, + { 0x3e0d, 0x0000 }, + { 0x3e0e, 0x0000 }, + { 0x3e0f, 0x0000 }, + { 0x3e10, 0x0000 }, + { 0x3e11, 0x0000 }, + { 0x3e12, 0x0000 }, + { 0x3e13, 0x0000 }, + { 0x3e14, 0x0000 }, + { 0x3e15, 0x0000 }, + { 0x3e16, 0x0000 }, + { 0x3e17, 0x0000 }, + { 0x3e18, 0x0000 }, + { 0x3e19, 0x0000 }, + { 0x3e1a, 0x0000 }, + { 0x3e1b, 0x0000 }, + { 0x3e1c, 0x0000 }, + { 0x3e1d, 0x0000 }, + { 0x3e1e, 0x0000 }, + { 0x3e1f, 0x0000 }, + { 0x3e20, 0x0000 }, + { 0x3e21, 0x0000 }, + { 0x3e22, 0x0000 }, + { 0x3e23, 0x0000 }, + { 0x3e24, 0x0000 }, + { 0x3e25, 0x0000 }, + { 0x3e26, 0x0000 }, + { 0x3e27, 0x0000 }, + { 0x3e28, 0x0000 }, + { 0x3e29, 0x0000 }, + { 0x3e2a, 0x0000 }, + { 0x3e2b, 0x0000 }, + { 0x3e2c, 0x0000 }, + { 0x3e2d, 0x0000 }, + { 0x3e2e, 0x0000 }, + { 0x3e2f, 0x0000 }, + { 0x3e30, 0x0000 }, + { 0x3e31, 0x0000 }, + { 0x3e32, 0x0000 }, + { 0x3e33, 0x0000 }, + { 0x3e34, 0x0000 }, + { 0x3e35, 0x0000 }, + { 0x3e36, 0x0000 }, + { 0x3e37, 0x0000 }, + { 0x3e38, 0x0000 }, + { 0x3e39, 0x0000 }, + { 0x3e3a, 0x0000 }, + { 0x3e3b, 0x0000 }, + { 0x3e3c, 0x0000 }, + { 0x3e3d, 0x0000 }, + { 0x3e3e, 0x0000 }, + { 0x3e3f, 0x0000 }, + { 0x3e40, 0x0000 }, + { 0x3e41, 0x0000 }, + { 0x3e42, 0x0000 }, + { 0x3e43, 0x0000 }, + { 0x3e44, 0x0000 }, + { 0x3e45, 0x0000 }, + { 0x3e46, 0x0000 }, + { 0x3e47, 0x0000 }, + { 0x3e48, 0x0000 }, + { 0x3e49, 0x0000 }, + { 0x3e4a, 0x0000 }, + { 0x3e4b, 0x0000 }, + { 0x3e4c, 0x0000 }, + { 0x3e4d, 0x0000 }, + { 0x3e4e, 0x0000 }, + { 0x3e4f, 0x0000 }, + { 0x3e50, 0x0000 }, + { 0x3e51, 0x0000 }, + { 0x3e52, 0x0000 }, + { 0x3e53, 0x0000 }, + { 0x3e54, 0x0000 }, + { 0x3e55, 0x0000 }, + { 0x3e56, 0x0000 }, + { 0x3e57, 0x0000 }, + { 0x3e58, 0x0000 }, + { 0x3e59, 0x0000 }, + { 0x3e5a, 0x0000 }, + { 0x3e5b, 0x0000 }, + { 0x3e5c, 0x0000 }, + { 0x3e5d, 0x0000 }, + { 0x3e5e, 0x0000 }, + { 0x3e5f, 0x0000 }, + { 0x3e60, 0x0000 }, + { 0x3e61, 0x0000 }, + { 0x3e62, 0x0000 }, + { 0x3e63, 0x0000 }, + { 0x3e64, 0x0000 }, + { 0x3e65, 0x0000 }, + { 0x3e66, 0x0000 }, + { 0x3e67, 0x0000 }, + { 0x3e68, 0x0000 }, + { 0x3e69, 0x0000 }, + { 0x3e6a, 0x0000 }, + { 0x3e6b, 0x0000 }, + { 0x3e6c, 0x0000 }, + { 0x3e6d, 0x0000 }, + { 0x3e6e, 0x0000 }, + { 0x3e6f, 0x0000 }, + { 0x3e70, 0x0000 }, + { 0x3e71, 0x0000 }, + { 0x3e72, 0x0000 }, + { 0x3e73, 0x0000 }, + { 0x3e74, 0x0000 }, + { 0x3e75, 0x0000 }, + { 0x3e76, 0x0000 }, + { 0x3e77, 0x0000 }, + { 0x3e78, 0x0000 }, + { 0x3e79, 0x0000 }, + { 0x3e7a, 0x0000 }, + { 0x3e7b, 0x0000 }, + { 0x3e7c, 0x0000 }, + { 0x3e7d, 0x0000 }, + { 0x3e7e, 0x0000 }, + { 0x3e7f, 0x0000 }, + { 0x3e80, 0x0000 }, + { 0x3e81, 0x0000 }, + { 0x3e82, 0x0000 }, + { 0x3e83, 0x0000 }, + { 0x3e84, 0x0000 }, + { 0x3e85, 0x0000 }, + { 0x3e86, 0x0000 }, + { 0x3e87, 0x0000 }, + { 0x3e88, 0x0000 }, + { 0x3e89, 0x0000 }, + { 0x3e8a, 0x0000 }, + { 0x3e8b, 0x0000 }, + { 0x3e8c, 0x0000 }, + { 0x3e8d, 0x0000 }, + { 0x3e8e, 0x0000 }, + { 0x3e8f, 0x0000 }, + { 0x3e90, 0x0000 }, + { 0x3e91, 0x0000 }, + { 0x3e92, 0x0000 }, + { 0x3e93, 0x0000 }, + { 0x3e94, 0x0000 }, + { 0x3e95, 0x0000 }, + { 0x3e96, 0x0000 }, + { 0x3e97, 0x0000 }, + { 0x3e98, 0x0000 }, + { 0x3e99, 0x0000 }, + { 0x3e9a, 0x0000 }, + { 0x3e9b, 0x0000 }, + { 0x3e9c, 0x0000 }, + { 0x3e9d, 0x0000 }, + { 0x3e9e, 0x0000 }, + { 0x3e9f, 0x0000 }, + { 0x3ea0, 0x0000 }, + { 0x3ea1, 0x0000 }, + { 0x3ea2, 0x0000 }, + { 0x3ea3, 0x0000 }, + { 0x3ea4, 0x0000 }, + { 0x3ea5, 0x0000 }, + { 0x3ea6, 0x0000 }, + { 0x3ea7, 0x0000 }, + { 0x3ea8, 0x0000 }, + { 0x3ea9, 0x0000 }, + { 0x3eaa, 0x0000 }, + { 0x3eab, 0x0000 }, + { 0x3eac, 0x0000 }, + { 0x3ead, 0x0000 }, + { 0x3eae, 0x0000 }, + { 0x3eaf, 0x0000 }, + { 0x3eb0, 0x0000 }, + { 0x3eb1, 0x0000 }, + { 0x3eb2, 0x0000 }, + { 0x3eb3, 0x0000 }, + { 0x3eb4, 0x0000 }, + { 0x3eb5, 0x0000 }, + { 0x3eb6, 0x0000 }, + { 0x3eb7, 0x0000 }, + { 0x3eb8, 0x0000 }, + { 0x3eb9, 0x0000 }, + { 0x3eba, 0x0000 }, + { 0x3ebb, 0x0000 }, + { 0x3ebc, 0x0000 }, + { 0x3ebd, 0x0000 }, + { 0x3ebe, 0x0000 }, + { 0x3ebf, 0x0000 }, + { 0x3ec0, 0x0000 }, + { 0x3ec1, 0x0000 }, + { 0x3ec2, 0x0000 }, + { 0x3ec3, 0x0000 }, + { 0x3ec4, 0x0000 }, + { 0x3ec5, 0x0000 }, + { 0x3ec6, 0x0000 }, + { 0x3ec7, 0x0000 }, + { 0x3ec8, 0x0000 }, + { 0x3ec9, 0x0000 }, + { 0x3eca, 0x0000 }, + { 0x3ecb, 0x0000 }, + { 0x3ecc, 0x0000 }, + { 0x3ecd, 0x0000 }, + { 0x3ece, 0x0000 }, + { 0x3ecf, 0x0000 }, + { 0x3ed0, 0x0000 }, + { 0x3ed1, 0x0000 }, + { 0x3ed2, 0x0000 }, + { 0x3ed3, 0x0000 }, + { 0x3ed4, 0x0000 }, + { 0x3ed5, 0x0000 }, + { 0x3ed6, 0x0000 }, + { 0x3ed7, 0x0000 }, + { 0x3ed8, 0x0000 }, + { 0x3ed9, 0x0000 }, + { 0x3eda, 0x0000 }, + { 0x3edb, 0x0000 }, + { 0x3edc, 0x0000 }, + { 0x3edd, 0x0000 }, + { 0x3ede, 0x0000 }, + { 0x3edf, 0x0000 }, + { 0x3ee0, 0x0000 }, + { 0x3ee1, 0x0000 }, + { 0x3ee2, 0x0000 }, + { 0x3ee3, 0x0000 }, + { 0x3ee4, 0x0000 }, + { 0x3ee5, 0x0000 }, + { 0x3ee6, 0x0000 }, + { 0x3ee7, 0x0000 }, + { 0x3ee8, 0x0000 }, + { 0x3ee9, 0x0000 }, + { 0x3eea, 0x0000 }, + { 0x3eeb, 0x0000 }, + { 0x3eec, 0x0000 }, + { 0x3eed, 0x0000 }, + { 0x3eee, 0x0000 }, + { 0x3eef, 0x0000 }, + { 0x3ef0, 0x0000 }, + { 0x3ef1, 0x0000 }, + { 0x3ef2, 0x0000 }, + { 0x3ef3, 0x0000 }, + { 0x3ef4, 0x0000 }, + { 0x3ef5, 0x0000 }, + { 0x3ef6, 0x0000 }, + { 0x3ef7, 0x0000 }, + { 0x3ef8, 0x0000 }, + { 0x3ef9, 0x0000 }, + { 0x3efa, 0x0000 }, + { 0x3efb, 0x0000 }, + { 0x3efc, 0x0000 }, + { 0x3efd, 0x0000 }, + { 0x3efe, 0x0000 }, + { 0x3eff, 0x0000 }, + { 0x3f00, 0x0000 }, + { 0x3f01, 0x0000 }, + { 0x3f02, 0x0000 }, + { 0x3f03, 0x0000 }, + { 0x3f04, 0x0000 }, + { 0x3f05, 0x0000 }, + { 0x3f06, 0x0000 }, + { 0x3f07, 0x0000 }, + { 0x3f08, 0x0000 }, + { 0x3f09, 0x0000 }, + { 0x3f0a, 0x0000 }, + { 0x3f0b, 0x0000 }, + { 0x3f0c, 0x0000 }, + { 0x3f0d, 0x0000 }, + { 0x3f0e, 0x0000 }, + { 0x3f0f, 0x0000 }, + { 0x3f10, 0x0000 }, + { 0x3f11, 0x0000 }, + { 0x3f12, 0x0000 }, + { 0x3f13, 0x0000 }, + { 0x3f14, 0x0000 }, + { 0x3f15, 0x0000 }, + { 0x3f16, 0x0000 }, + { 0x3f17, 0x0000 }, + { 0x3f18, 0x0000 }, + { 0x3f19, 0x0000 }, + { 0x3f1a, 0x0000 }, + { 0x3f1b, 0x0000 }, + { 0x3f1c, 0x0000 }, + { 0x3f1d, 0x0000 }, + { 0x3f1e, 0x0000 }, + { 0x3f1f, 0x0000 }, + { 0x3f20, 0x0000 }, + { 0x3f21, 0x0000 }, + { 0x3f22, 0x0000 }, + { 0x3f23, 0x0000 }, + { 0x3f24, 0x0000 }, + { 0x3f25, 0x0000 }, + { 0x3f26, 0x0000 }, + { 0x3f27, 0x0000 }, + { 0x3f28, 0x0000 }, + { 0x3f29, 0x0000 }, + { 0x3f2a, 0x0000 }, + { 0x3f2b, 0x0000 }, + { 0x3f2c, 0x0000 }, + { 0x3f2d, 0x0000 }, + { 0x3f2e, 0x0000 }, + { 0x3f2f, 0x0000 }, + { 0x3f30, 0x0000 }, + { 0x3f31, 0x0000 }, + { 0x3f32, 0x0000 }, + { 0x3f33, 0x0000 }, + { 0x3f34, 0x0000 }, + { 0x3f35, 0x0000 }, + { 0x3f36, 0x0000 }, + { 0x3f37, 0x0000 }, + { 0x3f38, 0x0000 }, + { 0x3f39, 0x0000 }, + { 0x3f3a, 0x0000 }, + { 0x3f3b, 0x0000 }, + { 0x3f3c, 0x0000 }, + { 0x3f3d, 0x0000 }, + { 0x3f3e, 0x0000 }, + { 0x3f3f, 0x0000 }, + { 0x3f40, 0x0000 }, + { 0x3f41, 0x0000 }, + { 0x3f42, 0x0000 }, + { 0x3f43, 0x0000 }, + { 0x3f44, 0x0000 }, + { 0x3f45, 0x0000 }, + { 0x3f46, 0x0000 }, + { 0x3f47, 0x0000 }, + { 0x3f48, 0x0000 }, + { 0x3f49, 0x0000 }, + { 0x3f4a, 0x0000 }, + { 0x3f4b, 0x0000 }, + { 0x3f4c, 0x0000 }, + { 0x3f4d, 0x0000 }, + { 0x3f4e, 0x0000 }, + { 0x3f4f, 0x0000 }, + { 0x3f50, 0x0000 }, + { 0x3f51, 0x0000 }, + { 0x3f52, 0x0000 }, + { 0x3f53, 0x0000 }, + { 0x3f54, 0x0000 }, + { 0x3f55, 0x0000 }, + { 0x3f56, 0x0000 }, + { 0x3f57, 0x0000 }, + { 0x3f58, 0x0000 }, + { 0x3f59, 0x0000 }, + { 0x3f5a, 0x0000 }, + { 0x3f5b, 0x0000 }, + { 0x3f5c, 0x0000 }, + { 0x3f5d, 0x0000 }, + { 0x3f5e, 0x0000 }, + { 0x3f5f, 0x0000 }, + { 0x3f60, 0x0000 }, + { 0x3f61, 0x0000 }, + { 0x3f62, 0x0000 }, + { 0x3f63, 0x0000 }, + { 0x3f64, 0x0000 }, + { 0x3f65, 0x0000 }, + { 0x3f66, 0x0000 }, + { 0x3f67, 0x0000 }, + { 0x3f68, 0x0000 }, + { 0x3f69, 0x0000 }, + { 0x3f6a, 0x0000 }, + { 0x3f6b, 0x0000 }, + { 0x3f6c, 0x0000 }, + { 0x3f6d, 0x0000 }, + { 0x3f6e, 0x0000 }, + { 0x3f6f, 0x0000 }, + { 0x3f70, 0x0000 }, + { 0x3f71, 0x0000 }, + { 0x3f72, 0x0000 }, + { 0x3f73, 0x0000 }, + { 0x3f74, 0x0000 }, + { 0x3f75, 0x0000 }, + { 0x3f76, 0x0000 }, + { 0x3f77, 0x0000 }, + { 0x3f78, 0x0000 }, + { 0x3f79, 0x0000 }, + { 0x3f7a, 0x0000 }, + { 0x3f7b, 0x0000 }, + { 0x3f7c, 0x0000 }, + { 0x3f7d, 0x0000 }, + { 0x3f7e, 0x0000 }, + { 0x3f7f, 0x0000 }, + { 0x3f80, 0x0000 }, + { 0x3f81, 0x0000 }, + { 0x3f82, 0x0000 }, + { 0x3f83, 0x0000 }, + { 0x3f84, 0x0000 }, + { 0x3f85, 0x0000 }, + { 0x3f86, 0x0000 }, + { 0x3f87, 0x0000 }, + { 0x3f88, 0x0000 }, + { 0x3f89, 0x0000 }, + { 0x3f8a, 0x0000 }, + { 0x3f8b, 0x0000 }, + { 0x3f8c, 0x0000 }, + { 0x3f8d, 0x0000 }, + { 0x3f8e, 0x0000 }, + { 0x3f8f, 0x0000 }, + { 0x3f90, 0x0000 }, + { 0x3f91, 0x0000 }, + { 0x3f92, 0x0000 }, + { 0x3f93, 0x0000 }, + { 0x3f94, 0x0000 }, + { 0x3f95, 0x0000 }, + { 0x3f96, 0x0000 }, + { 0x3f97, 0x0000 }, + { 0x3f98, 0x0000 }, + { 0x3f99, 0x0000 }, + { 0x3f9a, 0x0000 }, + { 0x3f9b, 0x0000 }, + { 0x3f9c, 0x0000 }, + { 0x3f9d, 0x0000 }, + { 0x3f9e, 0x0000 }, + { 0x3f9f, 0x0000 }, + { 0x3fa0, 0x0000 }, + { 0x3fa1, 0x0000 }, + { 0x3fa2, 0x0000 }, + { 0x3fa3, 0x0000 }, + { 0x3fa4, 0x0000 }, + { 0x3fa5, 0x0000 }, + { 0x3fa6, 0x0000 }, + { 0x3fa7, 0x0000 }, + { 0x3fa8, 0x0000 }, + { 0x3fa9, 0x0000 }, + { 0x3faa, 0x0000 }, + { 0x3fab, 0x0000 }, + { 0x3fac, 0x0000 }, + { 0x3fad, 0x0000 }, + { 0x3fae, 0x0000 }, + { 0x3faf, 0x0000 }, + { 0x3fb0, 0x0000 }, + { 0x3fb1, 0x0000 }, + { 0x3fb2, 0x0000 }, + { 0x3fb3, 0x0000 }, + { 0x3fb4, 0x0000 }, + { 0x3fb5, 0x0000 }, + { 0x3fb6, 0x0000 }, + { 0x3fb7, 0x0000 }, + { 0x3fb8, 0x0000 }, + { 0x3fb9, 0x0000 }, + { 0x3fba, 0x0000 }, + { 0x3fbb, 0x0000 }, + { 0x3fbc, 0x0000 }, + { 0x3fbd, 0x0000 }, + { 0x3fbe, 0x0000 }, + { 0x3fbf, 0x0000 }, + { 0x3fc0, 0x0000 }, + { 0x3fc1, 0x0000 }, + { 0x3fc2, 0x0000 }, + { 0x3fc3, 0x0000 }, + { 0x3fc4, 0x0000 }, + { 0x3fc5, 0x0000 }, + { 0x3fc6, 0x0000 }, + { 0x3fc7, 0x0000 }, + { 0x3fc8, 0x0000 }, + { 0x3fc9, 0x0000 }, + { 0x3fca, 0x0000 }, + { 0x3fcb, 0x0000 }, + { 0x3fcc, 0x0000 }, + { 0x3fcd, 0x0000 }, + { 0x3fce, 0x0000 }, + { 0x3fcf, 0x0000 }, + { 0x3fd0, 0x0000 }, + { 0x3fd1, 0x0000 }, + { 0x3fd2, 0x0000 }, + { 0x3fd3, 0x0000 }, + { 0x3fd4, 0x0000 }, + { 0x3fd5, 0x0000 }, + { 0x3fd6, 0x0000 }, + { 0x3fd7, 0x0000 }, + { 0x3fd8, 0x0000 }, + { 0x3fd9, 0x0000 }, + { 0x3fda, 0x0000 }, + { 0x3fdb, 0x0000 }, + { 0x3fdc, 0x0000 }, + { 0x3fdd, 0x0000 }, + { 0x3fde, 0x0000 }, + { 0x3fdf, 0x0000 }, + { 0x3fe0, 0x0000 }, + { 0x3fe1, 0x0000 }, + { 0x3fe2, 0x0000 }, + { 0x3fe3, 0x0000 }, + { 0x3fe4, 0x0000 }, + { 0x3fe5, 0x0000 }, + { 0x3fe6, 0x0000 }, + { 0x3fe7, 0x0000 }, + { 0x3fe8, 0x0000 }, + { 0x3fe9, 0x0000 }, + { 0x3fea, 0x0000 }, + { 0x3feb, 0x0000 }, + { 0x3fec, 0x0000 }, + { 0x3fed, 0x0000 }, + { 0x3fee, 0x0000 }, + { 0x3fef, 0x0000 }, + { 0x3ff0, 0x0000 }, + { 0x3ff1, 0x0000 }, + { 0x3ff2, 0x0000 }, + { 0x3ff3, 0x0000 }, + { 0x3ff4, 0x0000 }, + { 0x3ff5, 0x0000 }, + { 0x3ff6, 0x0000 }, + { 0x3ff7, 0x0000 }, + { 0x3ff8, 0x0000 }, + { 0x3ff9, 0x0000 }, + { 0x3ffa, 0x0000 }, + { 0x3ffb, 0x0000 }, + { 0x3ffc, 0x0000 }, + { 0x3ffd, 0x0000 }, + { 0x3ffe, 0x0000 }, + { 0x3fff, 0x0000 }, + { 0x4000, 0x0000 }, + { 0x4001, 0x0000 }, + { 0x4002, 0x0000 }, + { 0x4003, 0x0000 }, + { 0x4004, 0x0000 }, + { 0x4005, 0x0000 }, + { 0x4006, 0x0000 }, + { 0x4007, 0x0000 }, + { 0x4008, 0x0000 }, + { 0x4009, 0x0000 }, + { 0x400a, 0x0000 }, + { 0x400b, 0x0000 }, + { 0x400c, 0x0000 }, + { 0x400d, 0x0000 }, + { 0x400e, 0x0000 }, + { 0x400f, 0x0000 }, + { 0x4010, 0x0000 }, + { 0x4011, 0x0000 }, + { 0x4012, 0x0000 }, + { 0x4013, 0x0000 }, + { 0x4014, 0x0000 }, + { 0x4015, 0x0000 }, + { 0x4016, 0x0000 }, + { 0x4017, 0x0000 }, + { 0x4018, 0x0000 }, + { 0x4019, 0x0000 }, + { 0x401a, 0x0000 }, + { 0x401b, 0x0000 }, + { 0x401c, 0x0000 }, + { 0x401d, 0x0000 }, + { 0x401e, 0x0000 }, + { 0x401f, 0x0000 }, + { 0x4020, 0x0000 }, + { 0x4021, 0x0000 }, + { 0x4022, 0x0000 }, + { 0x4023, 0x0000 }, + { 0x4024, 0x0000 }, + { 0x4025, 0x0000 }, + { 0x4026, 0x0000 }, + { 0x4027, 0x0000 }, + { 0x4028, 0x0000 }, + { 0x4029, 0x0000 }, + { 0x402a, 0x0000 }, + { 0x402b, 0x0000 }, + { 0x402c, 0x0000 }, + { 0x402d, 0x0000 }, + { 0x402e, 0x0000 }, + { 0x402f, 0x0000 }, + { 0x4030, 0x0000 }, + { 0x4031, 0x0000 }, + { 0x4032, 0x0000 }, + { 0x4033, 0x0000 }, + { 0x4034, 0x0000 }, + { 0x4035, 0x0000 }, + { 0x4036, 0x0000 }, + { 0x4037, 0x0000 }, + { 0x4038, 0x0000 }, + { 0x4039, 0x0000 }, + { 0x403a, 0x0000 }, + { 0x403b, 0x0000 }, + { 0x403c, 0x0000 }, + { 0x403d, 0x0000 }, + { 0x403e, 0x0000 }, + { 0x403f, 0x0000 }, + { 0x4040, 0x0000 }, + { 0x4041, 0x0000 }, + { 0x4042, 0x0000 }, + { 0x4043, 0x0000 }, + { 0x4044, 0x0000 }, + { 0x4045, 0x0000 }, + { 0x4046, 0x0000 }, + { 0x4047, 0x0000 }, + { 0x4048, 0x0000 }, + { 0x4049, 0x0000 }, + { 0x404a, 0x0000 }, + { 0x404b, 0x0000 }, + { 0x404c, 0x0000 }, + { 0x404d, 0x0000 }, + { 0x404e, 0x0000 }, + { 0x404f, 0x0000 }, + { 0x4050, 0x0000 }, + { 0x4051, 0x0000 }, + { 0x4052, 0x0000 }, + { 0x4053, 0x0000 }, + { 0x4054, 0x0000 }, + { 0x4055, 0x0000 }, + { 0x4056, 0x0000 }, + { 0x4057, 0x0000 }, + { 0x4058, 0x0000 }, + { 0x4059, 0x0000 }, + { 0x405a, 0x0000 }, + { 0x405b, 0x0000 }, + { 0x405c, 0x0000 }, + { 0x405d, 0x0000 }, + { 0x405e, 0x0000 }, + { 0x405f, 0x0000 }, + { 0x4060, 0x0000 }, + { 0x4061, 0x0000 }, + { 0x4062, 0x0000 }, + { 0x4063, 0x0000 }, + { 0x4064, 0x0000 }, + { 0x4065, 0x0000 }, + { 0x4066, 0x0000 }, + { 0x4067, 0x0000 }, + { 0x4068, 0x0000 }, + { 0x4069, 0x0000 }, + { 0x406a, 0x0000 }, + { 0x406b, 0x0000 }, + { 0x406c, 0x0000 }, + { 0x406d, 0x0000 }, + { 0x406e, 0x0000 }, + { 0x406f, 0x0000 }, + { 0x4070, 0x0000 }, + { 0x4071, 0x0000 }, + { 0x4072, 0x0000 }, + { 0x4073, 0x0000 }, + { 0x4074, 0x0000 }, + { 0x4075, 0x0000 }, + { 0x4076, 0x0000 }, + { 0x4077, 0x0000 }, + { 0x4078, 0x0000 }, + { 0x4079, 0x0000 }, + { 0x407a, 0x0000 }, + { 0x407b, 0x0000 }, + { 0x407c, 0x0000 }, + { 0x407d, 0x0000 }, + { 0x407e, 0x0000 }, + { 0x407f, 0x0000 }, + { 0x4080, 0x0000 }, + { 0x4081, 0x0000 }, + { 0x4082, 0x0000 }, + { 0x4083, 0x0000 }, + { 0x4084, 0x0000 }, + { 0x4085, 0x0000 }, + { 0x4086, 0x0000 }, + { 0x4087, 0x0000 }, + { 0x4088, 0x0000 }, + { 0x4089, 0x0000 }, + { 0x408a, 0x0000 }, + { 0x408b, 0x0000 }, + { 0x408c, 0x0000 }, + { 0x408d, 0x0000 }, + { 0x408e, 0x0000 }, + { 0x408f, 0x0000 }, + { 0x4090, 0x0000 }, + { 0x4091, 0x0000 }, + { 0x4092, 0x0000 }, + { 0x4093, 0x0000 }, + { 0x4094, 0x0000 }, + { 0x4095, 0x0000 }, + { 0x4096, 0x0000 }, + { 0x4097, 0x0000 }, + { 0x4098, 0x0000 }, + { 0x4099, 0x0000 }, + { 0x409a, 0x0000 }, + { 0x409b, 0x0000 }, + { 0x409c, 0x0000 }, + { 0x409d, 0x0000 }, + { 0x409e, 0x0000 }, + { 0x409f, 0x0000 }, + { 0x40a0, 0x0000 }, + { 0x40a1, 0x0000 }, + { 0x40a2, 0x0000 }, + { 0x40a3, 0x0000 }, + { 0x40a4, 0x0000 }, + { 0x40a5, 0x0000 }, + { 0x40a6, 0x0000 }, + { 0x40a7, 0x0000 }, + { 0x40a8, 0x0000 }, + { 0x40a9, 0x0000 }, + { 0x40aa, 0x0000 }, + { 0x40ab, 0x0000 }, + { 0x40ac, 0x0000 }, + { 0x40ad, 0x0000 }, + { 0x40ae, 0x0000 }, + { 0x40af, 0x0000 }, + { 0x40b0, 0x0000 }, + { 0x40b1, 0x0000 }, + { 0x40b2, 0x0000 }, + { 0x40b3, 0x0000 }, + { 0x40b4, 0x0000 }, + { 0x40b5, 0x0000 }, + { 0x40b6, 0x0000 }, + { 0x40b7, 0x0000 }, + { 0x40b8, 0x0000 }, + { 0x40b9, 0x0000 }, + { 0x40ba, 0x0000 }, + { 0x40bb, 0x0000 }, + { 0x40bc, 0x0000 }, + { 0x40bd, 0x0000 }, + { 0x40be, 0x0000 }, + { 0x40bf, 0x0000 }, + { 0x40c0, 0x0000 }, + { 0x40c1, 0x0000 }, + { 0x40c2, 0x0000 }, + { 0x40c3, 0x0000 }, + { 0x40c4, 0x0000 }, + { 0x40c5, 0x0000 }, + { 0x40c6, 0x0000 }, + { 0x40c7, 0x0000 }, + { 0x40c8, 0x0000 }, + { 0x40c9, 0x0000 }, + { 0x40ca, 0x0000 }, + { 0x40cb, 0x0000 }, + { 0x40cc, 0x0000 }, + { 0x40cd, 0x0000 }, + { 0x40ce, 0x0000 }, + { 0x40cf, 0x0000 }, + { 0x40d0, 0x0000 }, + { 0x40d1, 0x0000 }, + { 0x40d2, 0x0000 }, + { 0x40d3, 0x0000 }, + { 0x40d4, 0x0000 }, + { 0x40d5, 0x0000 }, + { 0x40d6, 0x0000 }, + { 0x40d7, 0x0000 }, + { 0x40d8, 0x0000 }, + { 0x40d9, 0x0000 }, + { 0x40da, 0x0000 }, + { 0x40db, 0x0000 }, + { 0x40dc, 0x0000 }, + { 0x40dd, 0x0000 }, + { 0x40de, 0x0000 }, + { 0x40df, 0x0000 }, + { 0x40e0, 0x0000 }, + { 0x40e1, 0x0000 }, + { 0x40e2, 0x0000 }, + { 0x40e3, 0x0000 }, + { 0x40e4, 0x0000 }, + { 0x40e5, 0x0000 }, + { 0x40e6, 0x0000 }, + { 0x40e7, 0x0000 }, + { 0x40e8, 0x0000 }, + { 0x40e9, 0x0000 }, + { 0x40ea, 0x0000 }, + { 0x40eb, 0x0000 }, + { 0x40ec, 0x0000 }, + { 0x40ed, 0x0000 }, + { 0x40ee, 0x0000 }, + { 0x40ef, 0x0000 }, + { 0x40f0, 0x0000 }, + { 0x40f1, 0x0000 }, + { 0x40f2, 0x0000 }, + { 0x40f3, 0x0000 }, + { 0x40f4, 0x0000 }, + { 0x40f5, 0x0000 }, + { 0x40f6, 0x0000 }, + { 0x40f7, 0x0000 }, + { 0x40f8, 0x0000 }, + { 0x40f9, 0x0000 }, + { 0x40fa, 0x0000 }, + { 0x40fb, 0x0000 }, + { 0x40fc, 0x0000 }, + { 0x40fd, 0x0000 }, + { 0x40fe, 0x0000 }, + { 0x40ff, 0x0000 }, + { 0x4100, 0x0000 }, + { 0x4101, 0x0000 }, + { 0x4102, 0x0000 }, + { 0x4103, 0x0000 }, + { 0x4104, 0x0000 }, + { 0x4105, 0x0000 }, + { 0x4106, 0x0000 }, + { 0x4107, 0x0000 }, + { 0x4108, 0x0000 }, + { 0x4109, 0x0000 }, + { 0x410a, 0x0000 }, + { 0x410b, 0x0000 }, + { 0x410c, 0x0000 }, + { 0x410d, 0x0000 }, + { 0x410e, 0x0000 }, + { 0x410f, 0x0000 }, + { 0x4110, 0x0000 }, + { 0x4111, 0x0000 }, + { 0x4112, 0x0000 }, + { 0x4113, 0x0000 }, + { 0x4114, 0x0000 }, + { 0x4115, 0x0000 }, + { 0x4116, 0x0000 }, + { 0x4117, 0x0000 }, + { 0x4118, 0x0000 }, + { 0x4119, 0x0000 }, + { 0x411a, 0x0000 }, + { 0x411b, 0x0000 }, + { 0x411c, 0x0000 }, + { 0x411d, 0x0000 }, + { 0x411e, 0x0000 }, + { 0x411f, 0x0000 }, + { 0x4120, 0x0000 }, + { 0x4121, 0x0000 }, + { 0x4122, 0x0000 }, + { 0x4123, 0x0000 }, + { 0x4124, 0x0000 }, + { 0x4125, 0x0000 }, + { 0x4126, 0x0000 }, + { 0x4127, 0x0000 }, + { 0x4128, 0x0000 }, + { 0x4129, 0x0000 }, + { 0x412a, 0x0000 }, + { 0x412b, 0x0000 }, + { 0x412c, 0x0000 }, + { 0x412d, 0x0000 }, + { 0x412e, 0x0000 }, + { 0x412f, 0x0000 }, + { 0x4130, 0x0000 }, + { 0x4131, 0x0000 }, + { 0x4132, 0x0000 }, + { 0x4133, 0x0000 }, + { 0x4134, 0x0000 }, + { 0x4135, 0x0000 }, + { 0x4136, 0x0000 }, + { 0x4137, 0x0000 }, + { 0x4138, 0x0000 }, + { 0x4139, 0x0000 }, + { 0x413a, 0x0000 }, + { 0x413b, 0x0000 }, + { 0x413c, 0x0000 }, + { 0x413d, 0x0000 }, + { 0x413e, 0x0000 }, + { 0x413f, 0x0000 }, + { 0x4140, 0x0000 }, + { 0x4141, 0x0000 }, + { 0x4142, 0x0000 }, + { 0x4143, 0x0000 }, + { 0x4144, 0x0000 }, + { 0x4145, 0x0000 }, + { 0x4146, 0x0000 }, + { 0x4147, 0x0000 }, + { 0x4148, 0x0000 }, + { 0x4149, 0x0000 }, + { 0x414a, 0x0000 }, + { 0x414b, 0x0000 }, + { 0x414c, 0x0000 }, + { 0x414d, 0x0000 }, + { 0x414e, 0x0000 }, + { 0x414f, 0x0000 }, + { 0x4150, 0x0000 }, + { 0x4151, 0x0000 }, + { 0x4152, 0x0000 }, + { 0x4153, 0x0000 }, + { 0x4154, 0x0000 }, + { 0x4155, 0x0000 }, + { 0x4156, 0x0000 }, + { 0x4157, 0x0000 }, + { 0x4158, 0x0000 }, + { 0x4159, 0x0000 }, + { 0x415a, 0x0000 }, + { 0x415b, 0x0000 }, + { 0x415c, 0x0000 }, + { 0x415d, 0x0000 }, + { 0x415e, 0x0000 }, + { 0x415f, 0x0000 }, + { 0x4160, 0x0000 }, + { 0x4161, 0x0000 }, + { 0x4162, 0x0000 }, + { 0x4163, 0x0000 }, + { 0x4164, 0x0000 }, + { 0x4165, 0x0000 }, + { 0x4166, 0x0000 }, + { 0x4167, 0x0000 }, + { 0x4168, 0x0000 }, + { 0x4169, 0x0000 }, + { 0x416a, 0x0000 }, + { 0x416b, 0x0000 }, + { 0x416c, 0x0000 }, + { 0x416d, 0x0000 }, + { 0x416e, 0x0000 }, + { 0x416f, 0x0000 }, + { 0x4170, 0x0000 }, + { 0x4171, 0x0000 }, + { 0x4172, 0x0000 }, + { 0x4173, 0x0000 }, + { 0x4174, 0x0000 }, + { 0x4175, 0x0000 }, + { 0x4176, 0x0000 }, + { 0x4177, 0x0000 }, + { 0x4178, 0x0000 }, + { 0x4179, 0x0000 }, + { 0x417a, 0x0000 }, + { 0x417b, 0x0000 }, + { 0x417c, 0x0000 }, + { 0x417d, 0x0000 }, + { 0x417e, 0x0000 }, + { 0x417f, 0x0000 }, + { 0x4180, 0x0000 }, + { 0x4181, 0x0000 }, + { 0x4182, 0x0000 }, + { 0x4183, 0x0000 }, + { 0x4184, 0x0000 }, + { 0x4185, 0x0000 }, + { 0x4186, 0x0000 }, + { 0x4187, 0x0000 }, + { 0x4188, 0x0000 }, + { 0x4189, 0x0000 }, + { 0x418a, 0x0000 }, + { 0x418b, 0x0000 }, + { 0x418c, 0x0000 }, + { 0x418d, 0x0000 }, + { 0x418e, 0x0000 }, + { 0x418f, 0x0000 }, + { 0x4190, 0x0000 }, + { 0x4191, 0x0000 }, + { 0x4192, 0x0000 }, + { 0x4193, 0x0000 }, + { 0x4194, 0x0000 }, + { 0x4195, 0x0000 }, + { 0x4196, 0x0000 }, + { 0x4197, 0x0000 }, + { 0x4198, 0x0000 }, + { 0x4199, 0x0000 }, + { 0x419a, 0x0000 }, + { 0x419b, 0x0000 }, + { 0x419c, 0x0000 }, + { 0x419d, 0x0000 }, + { 0x419e, 0x0000 }, + { 0x419f, 0x0000 }, + { 0x41a0, 0x0000 }, + { 0x41a1, 0x0000 }, + { 0x41a2, 0x0000 }, + { 0x41a3, 0x0000 }, + { 0x41a4, 0x0000 }, + { 0x41a5, 0x0000 }, + { 0x41a6, 0x0000 }, + { 0x41a7, 0x0000 }, + { 0x41a8, 0x0000 }, + { 0x41a9, 0x0000 }, + { 0x41aa, 0x0000 }, + { 0x41ab, 0x0000 }, + { 0x41ac, 0x0000 }, + { 0x41ad, 0x0000 }, + { 0x41ae, 0x0000 }, + { 0x41af, 0x0000 }, + { 0x41b0, 0x0000 }, + { 0x41b1, 0x0000 }, + { 0x41b2, 0x0000 }, + { 0x41b3, 0x0000 }, + { 0x41b4, 0x0000 }, + { 0x41b5, 0x0000 }, + { 0x41b6, 0x0000 }, + { 0x41b7, 0x0000 }, + { 0x41b8, 0x0000 }, + { 0x41b9, 0x0000 }, + { 0x41ba, 0x0000 }, + { 0x41bb, 0x0000 }, + { 0x41bc, 0x0000 }, + { 0x41bd, 0x0000 }, + { 0x41be, 0x0000 }, + { 0x41bf, 0x0000 }, + { 0x41c0, 0x0000 }, + { 0x41c1, 0x0000 }, + { 0x41c2, 0x0000 }, + { 0x41c3, 0x0000 }, + { 0x41c4, 0x0000 }, + { 0x41c5, 0x0000 }, + { 0x41c6, 0x0000 }, + { 0x41c7, 0x0000 }, + { 0x41c8, 0x0000 }, + { 0x41c9, 0x0000 }, + { 0x41ca, 0x0000 }, + { 0x41cb, 0x0000 }, + { 0x41cc, 0x0000 }, + { 0x41cd, 0x0000 }, + { 0x41ce, 0x0000 }, + { 0x41cf, 0x0000 }, + { 0x41d0, 0x0000 }, + { 0x41d1, 0x0000 }, + { 0x41d2, 0x0000 }, + { 0x41d3, 0x0000 }, + { 0x41d4, 0x0000 }, + { 0x41d5, 0x0000 }, + { 0x41d6, 0x0000 }, + { 0x41d7, 0x0000 }, + { 0x41d8, 0x0000 }, + { 0x41d9, 0x0000 }, + { 0x41da, 0x0000 }, + { 0x41db, 0x0000 }, + { 0x41dc, 0x0000 }, + { 0x41dd, 0x0000 }, + { 0x41de, 0x0000 }, + { 0x41df, 0x0000 }, + { 0x41e0, 0x0000 }, + { 0x41e1, 0x0000 }, + { 0x41e2, 0x0000 }, + { 0x41e3, 0x0000 }, + { 0x41e4, 0x0000 }, + { 0x41e5, 0x0000 }, + { 0x41e6, 0x0000 }, + { 0x41e7, 0x0000 }, + { 0x41e8, 0x0000 }, + { 0x41e9, 0x0000 }, + { 0x41ea, 0x0000 }, + { 0x41eb, 0x0000 }, + { 0x41ec, 0x0000 }, + { 0x41ed, 0x0000 }, + { 0x41ee, 0x0000 }, + { 0x41ef, 0x0000 }, + { 0x41f0, 0x0000 }, + { 0x41f1, 0x0000 }, + { 0x41f2, 0x0000 }, + { 0x41f3, 0x0000 }, + { 0x41f4, 0x0000 }, + { 0x41f5, 0x0000 }, + { 0x41f6, 0x0000 }, + { 0x41f7, 0x0000 }, + { 0x41f8, 0x0000 }, + { 0x41f9, 0x0000 }, + { 0x41fa, 0x0000 }, + { 0x41fb, 0x0000 }, + { 0x41fc, 0x0000 }, + { 0x41fd, 0x0000 }, + { 0x41fe, 0x0000 }, + { 0x41ff, 0x0000 }, + { 0x4200, 0x0000 }, + { 0x4201, 0x0000 }, + { 0x4202, 0x0000 }, + { 0x4203, 0x0000 }, + { 0x4204, 0x0000 }, + { 0x4205, 0x0000 }, + { 0x4206, 0x0000 }, + { 0x4207, 0x0000 }, + { 0x4208, 0x0000 }, + { 0x4209, 0x0000 }, + { 0x420a, 0x0000 }, + { 0x420b, 0x0000 }, + { 0x420c, 0x0000 }, + { 0x420d, 0x0000 }, + { 0x420e, 0x0000 }, + { 0x420f, 0x0000 }, + { 0x4210, 0x0000 }, + { 0x4211, 0x0000 }, + { 0x4212, 0x0000 }, + { 0x4213, 0x0000 }, + { 0x4214, 0x0000 }, + { 0x4215, 0x0000 }, + { 0x4216, 0x0000 }, + { 0x4217, 0x0000 }, + { 0x4218, 0x0000 }, + { 0x4219, 0x0000 }, + { 0x421a, 0x0000 }, + { 0x421b, 0x0000 }, + { 0x421c, 0x0000 }, + { 0x421d, 0x0000 }, + { 0x421e, 0x0000 }, + { 0x421f, 0x0000 }, + { 0x4220, 0x0000 }, + { 0x4221, 0x0000 }, + { 0x4222, 0x0000 }, + { 0x4223, 0x0000 }, + { 0x4224, 0x0000 }, + { 0x4225, 0x0000 }, + { 0x4226, 0x0000 }, + { 0x4227, 0x0000 }, + { 0x4228, 0x0000 }, + { 0x4229, 0x0000 }, + { 0x422a, 0x0000 }, + { 0x422b, 0x0000 }, + { 0x422c, 0x0000 }, + { 0x422d, 0x0000 }, + { 0x422e, 0x0000 }, + { 0x422f, 0x0000 }, + { 0x4230, 0x0000 }, + { 0x4231, 0x0000 }, + { 0x4232, 0x0000 }, + { 0x4233, 0x0000 }, + { 0x4234, 0x0000 }, + { 0x4235, 0x0000 }, + { 0x4236, 0x0000 }, + { 0x4237, 0x0000 }, + { 0x4238, 0x0000 }, + { 0x4239, 0x0000 }, + { 0x423a, 0x0000 }, + { 0x423b, 0x0000 }, + { 0x423c, 0x0000 }, + { 0x423d, 0x0000 }, + { 0x423e, 0x0000 }, + { 0x423f, 0x0000 }, + { 0x4240, 0x0000 }, + { 0x4241, 0x0000 }, + { 0x4242, 0x0000 }, + { 0x4243, 0x0000 }, + { 0x4244, 0x0000 }, + { 0x4245, 0x0000 }, + { 0x4246, 0x0000 }, + { 0x4247, 0x0000 }, + { 0x4248, 0x0000 }, + { 0x4249, 0x0000 }, + { 0x424a, 0x0000 }, + { 0x424b, 0x0000 }, + { 0x424c, 0x0000 }, + { 0x424d, 0x0000 }, + { 0x424e, 0x0000 }, + { 0x424f, 0x0000 }, + { 0x4250, 0x0000 }, + { 0x4251, 0x0000 }, + { 0x4252, 0x0000 }, + { 0x4253, 0x0000 }, + { 0x4254, 0x0000 }, + { 0x4255, 0x0000 }, + { 0x4256, 0x0000 }, + { 0x4257, 0x0000 }, + { 0x4258, 0x0000 }, + { 0x4259, 0x0000 }, + { 0x425a, 0x0000 }, + { 0x425b, 0x0000 }, + { 0x425c, 0x0000 }, + { 0x425d, 0x0000 }, + { 0x425e, 0x0000 }, + { 0x425f, 0x0000 }, + { 0x4260, 0x0000 }, + { 0x4261, 0x0000 }, + { 0x4262, 0x0000 }, + { 0x4263, 0x0000 }, + { 0x4264, 0x0000 }, + { 0x4265, 0x0000 }, + { 0x4266, 0x0000 }, + { 0x4267, 0x0000 }, + { 0x4268, 0x0000 }, + { 0x4269, 0x0000 }, + { 0x426a, 0x0000 }, + { 0x426b, 0x0000 }, + { 0x426c, 0x0000 }, + { 0x426d, 0x0000 }, + { 0x426e, 0x0000 }, + { 0x426f, 0x0000 }, + { 0x4270, 0x0000 }, + { 0x4271, 0x0000 }, + { 0x4272, 0x0000 }, + { 0x4273, 0x0000 }, + { 0x4274, 0x0000 }, + { 0x4275, 0x0000 }, + { 0x4276, 0x0000 }, + { 0x4277, 0x0000 }, + { 0x4278, 0x0000 }, + { 0x4279, 0x0000 }, + { 0x427a, 0x0000 }, + { 0x427b, 0x0000 }, + { 0x427c, 0x0000 }, + { 0x427d, 0x0000 }, + { 0x427e, 0x0000 }, + { 0x427f, 0x0000 }, + { 0x4280, 0x0000 }, + { 0x4281, 0x0000 }, + { 0x4282, 0x0000 }, + { 0x4283, 0x0000 }, + { 0x4284, 0x0000 }, + { 0x4285, 0x0000 }, + { 0x4286, 0x0000 }, + { 0x4287, 0x0000 }, + { 0x4288, 0x0000 }, + { 0x4289, 0x0000 }, + { 0x428a, 0x0000 }, + { 0x428b, 0x0000 }, + { 0x428c, 0x0000 }, + { 0x428d, 0x0000 }, + { 0x428e, 0x0000 }, + { 0x428f, 0x0000 }, + { 0x4290, 0x0000 }, + { 0x4291, 0x0000 }, + { 0x4292, 0x0000 }, + { 0x4293, 0x0000 }, + { 0x4294, 0x0000 }, + { 0x4295, 0x0000 }, + { 0x4296, 0x0000 }, + { 0x4297, 0x0000 }, + { 0x4298, 0x0000 }, + { 0x4299, 0x0000 }, + { 0x429a, 0x0000 }, + { 0x429b, 0x0000 }, + { 0x429c, 0x0000 }, + { 0x429d, 0x0000 }, + { 0x429e, 0x0000 }, + { 0x429f, 0x0000 }, + { 0x42a0, 0x0000 }, + { 0x42a1, 0x0000 }, + { 0x42a2, 0x0000 }, + { 0x42a3, 0x0000 }, + { 0x42a4, 0x0000 }, + { 0x42a5, 0x0000 }, + { 0x42a6, 0x0000 }, + { 0x42a7, 0x0000 }, + { 0x42a8, 0x0000 }, + { 0x42a9, 0x0000 }, + { 0x42aa, 0x0000 }, + { 0x42ab, 0x0000 }, + { 0x42ac, 0x0000 }, + { 0x42ad, 0x0000 }, + { 0x42ae, 0x0000 }, + { 0x42af, 0x0000 }, + { 0x42b0, 0x0000 }, + { 0x42b1, 0x0000 }, + { 0x42b2, 0x0000 }, + { 0x42b3, 0x0000 }, + { 0x42b4, 0x0000 }, + { 0x42b5, 0x0000 }, + { 0x42b6, 0x0000 }, + { 0x42b7, 0x0000 }, + { 0x42b8, 0x0000 }, + { 0x42b9, 0x0000 }, + { 0x42ba, 0x0000 }, + { 0x42bb, 0x0000 }, + { 0x42bc, 0x0000 }, + { 0x42bd, 0x0000 }, + { 0x42be, 0x0000 }, + { 0x42bf, 0x0000 }, + { 0x42c0, 0x0000 }, + { 0x42c1, 0x0000 }, + { 0x42c2, 0x0000 }, + { 0x42c3, 0x0000 }, + { 0x42c4, 0x0000 }, + { 0x42c5, 0x0000 }, + { 0x42c6, 0x0000 }, + { 0x42c7, 0x0000 }, + { 0x42c8, 0x0000 }, + { 0x42c9, 0x0000 }, + { 0x42ca, 0x0000 }, + { 0x42cb, 0x0000 }, + { 0x42cc, 0x0000 }, + { 0x42cd, 0x0000 }, + { 0x42ce, 0x0000 }, + { 0x42cf, 0x0000 }, + { 0x42d0, 0x0000 }, + { 0x42d1, 0x0000 }, + { 0x42d2, 0x0000 }, + { 0x42d3, 0x0000 }, + { 0x42d4, 0x0000 }, + { 0x42d5, 0x0000 }, + { 0x42d6, 0x0000 }, + { 0x42d7, 0x0000 }, + { 0x42d8, 0x0000 }, + { 0x42d9, 0x0000 }, + { 0x42da, 0x0000 }, + { 0x42db, 0x0000 }, + { 0x42dc, 0x0000 }, + { 0x42dd, 0x0000 }, + { 0x42de, 0x0000 }, + { 0x42df, 0x0000 }, + { 0x42e0, 0x0000 }, + { 0x42e1, 0x0000 }, + { 0x42e2, 0x0000 }, + { 0x42e3, 0x0000 }, + { 0x42e4, 0x0000 }, + { 0x42e5, 0x0000 }, + { 0x42e6, 0x0000 }, + { 0x42e7, 0x0000 }, + { 0x42e8, 0x0000 }, + { 0x42e9, 0x0000 }, + { 0x42ea, 0x0000 }, + { 0x42eb, 0x0000 }, + { 0x42ec, 0x0000 }, + { 0x42ed, 0x0000 }, + { 0x42ee, 0x0000 }, + { 0x42ef, 0x0000 }, + { 0x42f0, 0x0000 }, + { 0x42f1, 0x0000 }, + { 0x42f2, 0x0000 }, + { 0x42f3, 0x0000 }, + { 0x42f4, 0x0000 }, + { 0x42f5, 0x0000 }, + { 0x42f6, 0x0000 }, + { 0x42f7, 0x0000 }, + { 0x42f8, 0x0000 }, + { 0x42f9, 0x0000 }, + { 0x42fa, 0x0000 }, + { 0x42fb, 0x0000 }, + { 0x42fc, 0x0000 }, + { 0x42fd, 0x0000 }, + { 0x42fe, 0x0000 }, + { 0x42ff, 0x0000 }, + { 0x4300, 0x0000 }, + { 0x4301, 0x0000 }, + { 0x4302, 0x0000 }, + { 0x4303, 0x0000 }, + { 0x4304, 0x0000 }, + { 0x4305, 0x0000 }, + { 0x4306, 0x0000 }, + { 0x4307, 0x0000 }, + { 0x4308, 0x0000 }, + { 0x4309, 0x0000 }, + { 0x430a, 0x0000 }, + { 0x430b, 0x0000 }, + { 0x430c, 0x0000 }, + { 0x430d, 0x0000 }, + { 0x430e, 0x0000 }, + { 0x430f, 0x0000 }, + { 0x4310, 0x0000 }, + { 0x4311, 0x0000 }, + { 0x4312, 0x0000 }, + { 0x4313, 0x0000 }, + { 0x4314, 0x0000 }, + { 0x4315, 0x0000 }, + { 0x4316, 0x0000 }, + { 0x4317, 0x0000 }, + { 0x4318, 0x0000 }, + { 0x4319, 0x0000 }, + { 0x431a, 0x0000 }, + { 0x431b, 0x0000 }, + { 0x431c, 0x0000 }, + { 0x431d, 0x0000 }, + { 0x431e, 0x0000 }, + { 0x431f, 0x0000 }, + { 0x4320, 0x0000 }, + { 0x4321, 0x0000 }, + { 0x4322, 0x0000 }, + { 0x4323, 0x0000 }, + { 0x4324, 0x0000 }, + { 0x4325, 0x0000 }, + { 0x4326, 0x0000 }, + { 0x4327, 0x0000 }, + { 0x4328, 0x0000 }, + { 0x4329, 0x0000 }, + { 0x432a, 0x0000 }, + { 0x432b, 0x0000 }, + { 0x432c, 0x0000 }, + { 0x432d, 0x0000 }, + { 0x432e, 0x0000 }, + { 0x432f, 0x0000 }, + { 0x4330, 0x0000 }, + { 0x4331, 0x0000 }, + { 0x4332, 0x0000 }, + { 0x4333, 0x0000 }, + { 0x4334, 0x0000 }, + { 0x4335, 0x0000 }, + { 0x4336, 0x0000 }, + { 0x4337, 0x0000 }, + { 0x4338, 0x0000 }, + { 0x4339, 0x0000 }, + { 0x433a, 0x0000 }, + { 0x433b, 0x0000 }, + { 0x433c, 0x0000 }, + { 0x433d, 0x0000 }, + { 0x433e, 0x0000 }, + { 0x433f, 0x0000 }, + { 0x4340, 0x0000 }, + { 0x4341, 0x0000 }, + { 0x4342, 0x0000 }, + { 0x4343, 0x0000 }, + { 0x4344, 0x0000 }, + { 0x4345, 0x0000 }, + { 0x4346, 0x0000 }, + { 0x4347, 0x0000 }, + { 0x4348, 0x0000 }, + { 0x4349, 0x0000 }, + { 0x434a, 0x0000 }, + { 0x434b, 0x0000 }, + { 0x434c, 0x0000 }, + { 0x434d, 0x0000 }, + { 0x434e, 0x0000 }, + { 0x434f, 0x0000 }, + { 0x4350, 0x0000 }, + { 0x4351, 0x0000 }, + { 0x4352, 0x0000 }, + { 0x4353, 0x0000 }, + { 0x4354, 0x0000 }, + { 0x4355, 0x0000 }, + { 0x4356, 0x0000 }, + { 0x4357, 0x0000 }, + { 0x4358, 0x0000 }, + { 0x4359, 0x0000 }, + { 0x435a, 0x0000 }, + { 0x435b, 0x0000 }, + { 0x435c, 0x0000 }, + { 0x435d, 0x0000 }, + { 0x435e, 0x0000 }, + { 0x435f, 0x0000 }, + { 0x4360, 0x0000 }, + { 0x4361, 0x0000 }, + { 0x4362, 0x0000 }, + { 0x4363, 0x0000 }, + { 0x4364, 0x0000 }, + { 0x4365, 0x0000 }, + { 0x4366, 0x0000 }, + { 0x4367, 0x0000 }, + { 0x4368, 0x0000 }, + { 0x4369, 0x0000 }, + { 0x436a, 0x0000 }, + { 0x436b, 0x0000 }, + { 0x436c, 0x0000 }, + { 0x436d, 0x0000 }, + { 0x436e, 0x0000 }, + { 0x436f, 0x0000 }, + { 0x4370, 0x0000 }, + { 0x4371, 0x0000 }, + { 0x4372, 0x0000 }, + { 0x4373, 0x0000 }, + { 0x4374, 0x0000 }, + { 0x4375, 0x0000 }, + { 0x4376, 0x0000 }, + { 0x4377, 0x0000 }, + { 0x4378, 0x0000 }, + { 0x4379, 0x0000 }, + { 0x437a, 0x0000 }, + { 0x437b, 0x0000 }, + { 0x437c, 0x0000 }, + { 0x437d, 0x0000 }, + { 0x437e, 0x0000 }, + { 0x437f, 0x0000 }, + { 0x4380, 0x0000 }, + { 0x4381, 0x0000 }, + { 0x4382, 0x0000 }, + { 0x4383, 0x0000 }, + { 0x4384, 0x0000 }, + { 0x4385, 0x0000 }, + { 0x4386, 0x0000 }, + { 0x4387, 0x0000 }, + { 0x4388, 0x0000 }, + { 0x4389, 0x0000 }, + { 0x438a, 0x0000 }, + { 0x438b, 0x0000 }, + { 0x438c, 0x0000 }, + { 0x438d, 0x0000 }, + { 0x438e, 0x0000 }, + { 0x438f, 0x0000 }, + { 0x4390, 0x0000 }, + { 0x4391, 0x0000 }, + { 0x4392, 0x0000 }, + { 0x4393, 0x0000 }, + { 0x4394, 0x0000 }, + { 0x4395, 0x0000 }, + { 0x4396, 0x0000 }, + { 0x4397, 0x0000 }, + { 0x4398, 0x0000 }, + { 0x4399, 0x0000 }, + { 0x439a, 0x0000 }, + { 0x439b, 0x0000 }, + { 0x439c, 0x0000 }, + { 0x439d, 0x0000 }, + { 0x439e, 0x0000 }, + { 0x439f, 0x0000 }, + { 0x43a0, 0x0000 }, + { 0x43a1, 0x0000 }, + { 0x43a2, 0x0000 }, + { 0x43a3, 0x0000 }, + { 0x43a4, 0x0000 }, + { 0x43a5, 0x0000 }, + { 0x43a6, 0x0000 }, + { 0x43a7, 0x0000 }, + { 0x43a8, 0x0000 }, + { 0x43a9, 0x0000 }, + { 0x43aa, 0x0000 }, + { 0x43ab, 0x0000 }, + { 0x43ac, 0x0000 }, + { 0x43ad, 0x0000 }, + { 0x43ae, 0x0000 }, + { 0x43af, 0x0000 }, + { 0x43b0, 0x0000 }, + { 0x43b1, 0x0000 }, + { 0x43b2, 0x0000 }, + { 0x43b3, 0x0000 }, + { 0x43b4, 0x0000 }, + { 0x43b5, 0x0000 }, + { 0x43b6, 0x0000 }, + { 0x43b7, 0x0000 }, + { 0x43b8, 0x0000 }, + { 0x43b9, 0x0000 }, + { 0x43ba, 0x0000 }, + { 0x43bb, 0x0000 }, + { 0x43bc, 0x0000 }, + { 0x43bd, 0x0000 }, + { 0x43be, 0x0000 }, + { 0x43bf, 0x0000 }, + { 0x43c0, 0x0000 }, + { 0x43c1, 0x0000 }, + { 0x43c2, 0x0000 }, + { 0x43c3, 0x0000 }, + { 0x43c4, 0x0000 }, + { 0x43c5, 0x0000 }, + { 0x43c6, 0x0000 }, + { 0x43c7, 0x0000 }, + { 0x43c8, 0x0000 }, + { 0x43c9, 0x0000 }, + { 0x43ca, 0x0000 }, + { 0x43cb, 0x0000 }, + { 0x43cc, 0x0000 }, + { 0x43cd, 0x0000 }, + { 0x43ce, 0x0000 }, + { 0x43cf, 0x0000 }, + { 0x43d0, 0x0000 }, + { 0x43d1, 0x0000 }, + { 0x43d2, 0x0000 }, + { 0x43d3, 0x0000 }, + { 0x43d4, 0x0000 }, + { 0x43d5, 0x0000 }, + { 0x43d6, 0x0000 }, + { 0x43d7, 0x0000 }, + { 0x43d8, 0x0000 }, + { 0x43d9, 0x0000 }, + { 0x43da, 0x0000 }, + { 0x43db, 0x0000 }, + { 0x43dc, 0x0000 }, + { 0x43dd, 0x0000 }, + { 0x43de, 0x0000 }, + { 0x43df, 0x0000 }, + { 0x43e0, 0x0000 }, + { 0x43e1, 0x0000 }, + { 0x43e2, 0x0000 }, + { 0x43e3, 0x0000 }, + { 0x43e4, 0x0000 }, + { 0x43e5, 0x0000 }, + { 0x43e6, 0x0000 }, + { 0x43e7, 0x0000 }, + { 0x43e8, 0x0000 }, + { 0x43e9, 0x0000 }, + { 0x43ea, 0x0000 }, + { 0x43eb, 0x0000 }, + { 0x43ec, 0x0000 }, + { 0x43ed, 0x0000 }, + { 0x43ee, 0x0000 }, + { 0x43ef, 0x0000 }, + { 0x43f0, 0x0000 }, + { 0x43f1, 0x0000 }, + { 0x43f2, 0x0000 }, + { 0x43f3, 0x0000 }, + { 0x43f4, 0x0000 }, + { 0x43f5, 0x0000 }, + { 0x43f6, 0x0000 }, + { 0x43f7, 0x0000 }, + { 0x43f8, 0x0000 }, + { 0x43f9, 0x0000 }, + { 0x43fa, 0x0000 }, + { 0x43fb, 0x0000 }, + { 0x43fc, 0x0000 }, + { 0x43fd, 0x0000 }, + { 0x43fe, 0x0000 }, + { 0x43ff, 0x0000 }, + { 0x4400, 0x0000 }, + { 0x4401, 0x0000 }, + { 0x4402, 0x0000 }, + { 0x4403, 0x0000 }, + { 0x4404, 0x0000 }, + { 0x4405, 0x0000 }, + { 0x4406, 0x0000 }, + { 0x4407, 0x0000 }, + { 0x4408, 0x0000 }, + { 0x4409, 0x0000 }, + { 0x440a, 0x0000 }, + { 0x440b, 0x0000 }, + { 0x440c, 0x0000 }, + { 0x440d, 0x0000 }, + { 0x440e, 0x0000 }, + { 0x440f, 0x0000 }, + { 0x4410, 0x0000 }, + { 0x4411, 0x0000 }, + { 0x4412, 0x0000 }, + { 0x4413, 0x0000 }, + { 0x4414, 0x0000 }, + { 0x4415, 0x0000 }, + { 0x4416, 0x0000 }, + { 0x4417, 0x0000 }, + { 0x4418, 0x0000 }, + { 0x4419, 0x0000 }, + { 0x441a, 0x0000 }, + { 0x441b, 0x0000 }, + { 0x441c, 0x0000 }, + { 0x441d, 0x0000 }, + { 0x441e, 0x0000 }, + { 0x441f, 0x0000 }, + { 0x4420, 0x0000 }, + { 0x4421, 0x0000 }, + { 0x4422, 0x0000 }, + { 0x4423, 0x0000 }, + { 0x4424, 0x0000 }, + { 0x4425, 0x0000 }, + { 0x4426, 0x0000 }, + { 0x4427, 0x0000 }, + { 0x4428, 0x0000 }, + { 0x4429, 0x0000 }, + { 0x442a, 0x0000 }, + { 0x442b, 0x0000 }, + { 0x442c, 0x0000 }, + { 0x442d, 0x0000 }, + { 0x442e, 0x0000 }, + { 0x442f, 0x0000 }, + { 0x4430, 0x0000 }, + { 0x4431, 0x0000 }, + { 0x4432, 0x0000 }, + { 0x4433, 0x0000 }, + { 0x4434, 0x0000 }, + { 0x4435, 0x0000 }, + { 0x4436, 0x0000 }, + { 0x4437, 0x0000 }, + { 0x4438, 0x0000 }, + { 0x4439, 0x0000 }, + { 0x443a, 0x0000 }, + { 0x443b, 0x0000 }, + { 0x443c, 0x0000 }, + { 0x443d, 0x0000 }, + { 0x443e, 0x0000 }, + { 0x443f, 0x0000 }, + { 0x4440, 0x0000 }, + { 0x4441, 0x0000 }, + { 0x4442, 0x0000 }, + { 0x4443, 0x0000 }, + { 0x4444, 0x0000 }, + { 0x4445, 0x0000 }, + { 0x4446, 0x0000 }, + { 0x4447, 0x0000 }, + { 0x4448, 0x0000 }, + { 0x4449, 0x0000 }, + { 0x444a, 0x0000 }, + { 0x444b, 0x0000 }, + { 0x444c, 0x0000 }, + { 0x444d, 0x0000 }, + { 0x444e, 0x0000 }, + { 0x444f, 0x0000 }, + { 0x4450, 0x0000 }, + { 0x4451, 0x0000 }, + { 0x4452, 0x0000 }, + { 0x4453, 0x0000 }, + { 0x4454, 0x0000 }, + { 0x4455, 0x0000 }, + { 0x4456, 0x0000 }, + { 0x4457, 0x0000 }, + { 0x4458, 0x0000 }, + { 0x4459, 0x0000 }, + { 0x445a, 0x0000 }, + { 0x445b, 0x0000 }, + { 0x445c, 0x0000 }, + { 0x445d, 0x0000 }, + { 0x445e, 0x0000 }, + { 0x445f, 0x0000 }, + { 0x4460, 0x0000 }, + { 0x4461, 0x0000 }, + { 0x4462, 0x0000 }, + { 0x4463, 0x0000 }, + { 0x4464, 0x0000 }, + { 0x4465, 0x0000 }, + { 0x4466, 0x0000 }, + { 0x4467, 0x0000 }, + { 0x4468, 0x0000 }, + { 0x4469, 0x0000 }, + { 0x446a, 0x0000 }, + { 0x446b, 0x0000 }, + { 0x446c, 0x0000 }, + { 0x446d, 0x0000 }, + { 0x446e, 0x0000 }, + { 0x446f, 0x0000 }, + { 0x4470, 0x0000 }, + { 0x4471, 0x0000 }, + { 0x4472, 0x0000 }, + { 0x4473, 0x0000 }, + { 0x4474, 0x0000 }, + { 0x4475, 0x0000 }, + { 0x4476, 0x0000 }, + { 0x4477, 0x0000 }, + { 0x4478, 0x0000 }, + { 0x4479, 0x0000 }, + { 0x447a, 0x0000 }, + { 0x447b, 0x0000 }, + { 0x447c, 0x0000 }, + { 0x447d, 0x0000 }, + { 0x447e, 0x0000 }, + { 0x447f, 0x0000 }, + { 0x4480, 0x0000 }, + { 0x4481, 0x0000 }, + { 0x4482, 0x0000 }, + { 0x4483, 0x0000 }, + { 0x4484, 0x0000 }, + { 0x4485, 0x0000 }, + { 0x4486, 0x0000 }, + { 0x4487, 0x0000 }, + { 0x4488, 0x0000 }, + { 0x4489, 0x0000 }, + { 0x448a, 0x0000 }, + { 0x448b, 0x0000 }, + { 0x448c, 0x0000 }, + { 0x448d, 0x0000 }, + { 0x448e, 0x0000 }, + { 0x448f, 0x0000 }, + { 0x4490, 0x0000 }, + { 0x4491, 0x0000 }, + { 0x4492, 0x0000 }, + { 0x4493, 0x0000 }, + { 0x4494, 0x0000 }, + { 0x4495, 0x0000 }, + { 0x4496, 0x0000 }, + { 0x4497, 0x0000 }, + { 0x4498, 0x0000 }, + { 0x4499, 0x0000 }, + { 0x449a, 0x0000 }, + { 0x449b, 0x0000 }, + { 0x449c, 0x0000 }, + { 0x449d, 0x0000 }, + { 0x449e, 0x0000 }, + { 0x449f, 0x0000 }, + { 0x44a0, 0x0000 }, + { 0x44a1, 0x0000 }, + { 0x44a2, 0x0000 }, + { 0x44a3, 0x0000 }, + { 0x44a4, 0x0000 }, + { 0x44a5, 0x0000 }, + { 0x44a6, 0x0000 }, + { 0x44a7, 0x0000 }, + { 0x44a8, 0x0000 }, + { 0x44a9, 0x0000 }, + { 0x44aa, 0x0000 }, + { 0x44ab, 0x0000 }, + { 0x44ac, 0x0000 }, + { 0x44ad, 0x0000 }, + { 0x44ae, 0x0000 }, + { 0x44af, 0x0000 }, + { 0x44b0, 0x0000 }, + { 0x44b1, 0x0000 }, + { 0x44b2, 0x0000 }, + { 0x44b3, 0x0000 }, + { 0x44b4, 0x0000 }, + { 0x44b5, 0x0000 }, + { 0x44b6, 0x0000 }, + { 0x44b7, 0x0000 }, + { 0x44b8, 0x0000 }, + { 0x44b9, 0x0000 }, + { 0x44ba, 0x0000 }, + { 0x44bb, 0x0000 }, + { 0x44bc, 0x0000 }, + { 0x44bd, 0x0000 }, + { 0x44be, 0x0000 }, + { 0x44bf, 0x0000 }, + { 0x44c0, 0x0000 }, + { 0x44c1, 0x0000 }, + { 0x44c2, 0x0000 }, + { 0x44c3, 0x0000 }, + { 0x44c4, 0x0000 }, + { 0x44c5, 0x0000 }, + { 0x44c6, 0x0000 }, + { 0x44c7, 0x0000 }, + { 0x44c8, 0x0000 }, + { 0x44c9, 0x0000 }, + { 0x44ca, 0x0000 }, + { 0x44cb, 0x0000 }, + { 0x44cc, 0x0000 }, + { 0x44cd, 0x0000 }, + { 0x44ce, 0x0000 }, + { 0x44cf, 0x0000 }, + { 0x44d0, 0x0000 }, + { 0x44d1, 0x0000 }, + { 0x44d2, 0x0000 }, + { 0x44d3, 0x0000 }, + { 0x44d4, 0x0000 }, + { 0x44d5, 0x0000 }, + { 0x44d6, 0x0000 }, + { 0x44d7, 0x0000 }, + { 0x44d8, 0x0000 }, + { 0x44d9, 0x0000 }, + { 0x44da, 0x0000 }, + { 0x44db, 0x0000 }, + { 0x44dc, 0x0000 }, + { 0x44dd, 0x0000 }, + { 0x44de, 0x0000 }, + { 0x44df, 0x0000 }, + { 0x44e0, 0x0000 }, + { 0x44e1, 0x0000 }, + { 0x44e2, 0x0000 }, + { 0x44e3, 0x0000 }, + { 0x44e4, 0x0000 }, + { 0x44e5, 0x0000 }, + { 0x44e6, 0x0000 }, + { 0x44e7, 0x0000 }, + { 0x44e8, 0x0000 }, + { 0x44e9, 0x0000 }, + { 0x44ea, 0x0000 }, + { 0x44eb, 0x0000 }, + { 0x44ec, 0x0000 }, + { 0x44ed, 0x0000 }, + { 0x44ee, 0x0000 }, + { 0x44ef, 0x0000 }, + { 0x44f0, 0x0000 }, + { 0x44f1, 0x0000 }, + { 0x44f2, 0x0000 }, + { 0x44f3, 0x0000 }, + { 0x44f4, 0x0000 }, + { 0x44f5, 0x0000 }, + { 0x44f6, 0x0000 }, + { 0x44f7, 0x0000 }, + { 0x44f8, 0x0000 }, + { 0x44f9, 0x0000 }, + { 0x44fa, 0x0000 }, + { 0x44fb, 0x0000 }, + { 0x44fc, 0x0000 }, + { 0x44fd, 0x0000 }, + { 0x44fe, 0x0000 }, + { 0x44ff, 0x0000 }, + { 0x4500, 0x0000 }, + { 0x4501, 0x0000 }, + { 0x4502, 0x0000 }, + { 0x4503, 0x0000 }, + { 0x4504, 0x0000 }, + { 0x4505, 0x0000 }, + { 0x4506, 0x0000 }, + { 0x4507, 0x0000 }, + { 0x4508, 0x0000 }, + { 0x4509, 0x0000 }, + { 0x450a, 0x0000 }, + { 0x450b, 0x0000 }, + { 0x450c, 0x0000 }, + { 0x450d, 0x0000 }, + { 0x450e, 0x0000 }, + { 0x450f, 0x0000 }, + { 0x4510, 0x0000 }, + { 0x4511, 0x0000 }, + { 0x4512, 0x0000 }, + { 0x4513, 0x0000 }, + { 0x4514, 0x0000 }, + { 0x4515, 0x0000 }, + { 0x4516, 0x0000 }, + { 0x4517, 0x0000 }, + { 0x4518, 0x0000 }, + { 0x4519, 0x0000 }, + { 0x451a, 0x0000 }, + { 0x451b, 0x0000 }, + { 0x451c, 0x0000 }, + { 0x451d, 0x0000 }, + { 0x451e, 0x0000 }, + { 0x451f, 0x0000 }, + { 0x4520, 0x0000 }, + { 0x4521, 0x0000 }, + { 0x4522, 0x0000 }, + { 0x4523, 0x0000 }, + { 0x4524, 0x0000 }, + { 0x4525, 0x0000 }, + { 0x4526, 0x0000 }, + { 0x4527, 0x0000 }, + { 0x4528, 0x0000 }, + { 0x4529, 0x0000 }, + { 0x452a, 0x0000 }, + { 0x452b, 0x0000 }, + { 0x452c, 0x0000 }, + { 0x452d, 0x0000 }, + { 0x452e, 0x0000 }, + { 0x452f, 0x0000 }, + { 0x4530, 0x0000 }, + { 0x4531, 0x0000 }, + { 0x4532, 0x0000 }, + { 0x4533, 0x0000 }, + { 0x4534, 0x0000 }, + { 0x4535, 0x0000 }, + { 0x4536, 0x0000 }, + { 0x4537, 0x0000 }, + { 0x4538, 0x0000 }, + { 0x4539, 0x0000 }, + { 0x453a, 0x0000 }, + { 0x453b, 0x0000 }, + { 0x453c, 0x0000 }, + { 0x453d, 0x0000 }, + { 0x453e, 0x0000 }, + { 0x453f, 0x0000 }, + { 0x4540, 0x0000 }, + { 0x4541, 0x0000 }, + { 0x4542, 0x0000 }, + { 0x4543, 0x0000 }, + { 0x4544, 0x0000 }, + { 0x4545, 0x0000 }, + { 0x4546, 0x0000 }, + { 0x4547, 0x0000 }, + { 0x4548, 0x0000 }, + { 0x4549, 0x0000 }, + { 0x454a, 0x0000 }, + { 0x454b, 0x0000 }, + { 0x454c, 0x0000 }, + { 0x454d, 0x0000 }, + { 0x454e, 0x0000 }, + { 0x454f, 0x0000 }, + { 0x4550, 0x0000 }, + { 0x4551, 0x0000 }, + { 0x4552, 0x0000 }, + { 0x4553, 0x0000 }, + { 0x4554, 0x0000 }, + { 0x4555, 0x0000 }, + { 0x4556, 0x0000 }, + { 0x4557, 0x0000 }, + { 0x4558, 0x0000 }, + { 0x4559, 0x0000 }, + { 0x455a, 0x0000 }, + { 0x455b, 0x0000 }, + { 0x455c, 0x0000 }, + { 0x455d, 0x0000 }, + { 0x455e, 0x0000 }, + { 0x455f, 0x0000 }, + { 0x4560, 0x0000 }, + { 0x4561, 0x0000 }, + { 0x4562, 0x0000 }, + { 0x4563, 0x0000 }, + { 0x4564, 0x0000 }, + { 0x4565, 0x0000 }, + { 0x4566, 0x0000 }, + { 0x4567, 0x0000 }, + { 0x4568, 0x0000 }, + { 0x4569, 0x0000 }, + { 0x456a, 0x0000 }, + { 0x456b, 0x0000 }, + { 0x456c, 0x0000 }, + { 0x456d, 0x0000 }, + { 0x456e, 0x0000 }, + { 0x456f, 0x0000 }, + { 0x4570, 0x0000 }, + { 0x4571, 0x0000 }, + { 0x4572, 0x0000 }, + { 0x4573, 0x0000 }, + { 0x4574, 0x0000 }, + { 0x4575, 0x0000 }, + { 0x4576, 0x0000 }, + { 0x4577, 0x0000 }, + { 0x4578, 0x0000 }, + { 0x4579, 0x0000 }, + { 0x457a, 0x0000 }, + { 0x457b, 0x0000 }, + { 0x457c, 0x0000 }, + { 0x457d, 0x0000 }, + { 0x457e, 0x0000 }, + { 0x457f, 0x0000 }, + { 0x4580, 0x0000 }, + { 0x4581, 0x0000 }, + { 0x4582, 0x0000 }, + { 0x4583, 0x0000 }, + { 0x4584, 0x0000 }, + { 0x4585, 0x0000 }, + { 0x4586, 0x0000 }, + { 0x4587, 0x0000 }, + { 0x4588, 0x0000 }, + { 0x4589, 0x0000 }, + { 0x458a, 0x0000 }, + { 0x458b, 0x0000 }, + { 0x458c, 0x0000 }, + { 0x458d, 0x0000 }, + { 0x458e, 0x0000 }, + { 0x458f, 0x0000 }, + { 0x4590, 0x0000 }, + { 0x4591, 0x0000 }, + { 0x4592, 0x0000 }, + { 0x4593, 0x0000 }, + { 0x4594, 0x0000 }, + { 0x4595, 0x0000 }, + { 0x4596, 0x0000 }, + { 0x4597, 0x0000 }, + { 0x4598, 0x0000 }, + { 0x4599, 0x0000 }, + { 0x459a, 0x0000 }, + { 0x459b, 0x0000 }, + { 0x459c, 0x0000 }, + { 0x459d, 0x0000 }, + { 0x459e, 0x0000 }, + { 0x459f, 0x0000 }, + { 0x45a0, 0x0000 }, + { 0x45a1, 0x0000 }, + { 0x45a2, 0x0000 }, + { 0x45a3, 0x0000 }, + { 0x45a4, 0x0000 }, + { 0x45a5, 0x0000 }, + { 0x45a6, 0x0000 }, + { 0x45a7, 0x0000 }, + { 0x45a8, 0x0000 }, + { 0x45a9, 0x0000 }, + { 0x45aa, 0x0000 }, + { 0x45ab, 0x0000 }, + { 0x45ac, 0x0000 }, + { 0x45ad, 0x0000 }, + { 0x45ae, 0x0000 }, + { 0x45af, 0x0000 }, + { 0x45b0, 0x0000 }, + { 0x45b1, 0x0000 }, + { 0x45b2, 0x0000 }, + { 0x45b3, 0x0000 }, + { 0x45b4, 0x0000 }, + { 0x45b5, 0x0000 }, + { 0x45b6, 0x0000 }, + { 0x45b7, 0x0000 }, + { 0x45b8, 0x0000 }, + { 0x45b9, 0x0000 }, + { 0x45ba, 0x0000 }, + { 0x45bb, 0x0000 }, + { 0x45bc, 0x0000 }, + { 0x45bd, 0x0000 }, + { 0x45be, 0x0000 }, + { 0x45bf, 0x0000 }, + { 0x45c0, 0x0000 }, + { 0x45c1, 0x0000 }, + { 0x45c2, 0x0000 }, + { 0x45c3, 0x0000 }, + { 0x45c4, 0x0000 }, + { 0x45c5, 0x0000 }, + { 0x45c6, 0x0000 }, + { 0x45c7, 0x0000 }, + { 0x45c8, 0x0000 }, + { 0x45c9, 0x0000 }, + { 0x45ca, 0x0000 }, + { 0x45cb, 0x0000 }, + { 0x45cc, 0x0000 }, + { 0x45cd, 0x0000 }, + { 0x45ce, 0x0000 }, + { 0x45cf, 0x0000 }, + { 0x45d0, 0x0000 }, + { 0x45d1, 0x0000 }, + { 0x45d2, 0x0000 }, + { 0x45d3, 0x0000 }, + { 0x45d4, 0x0000 }, + { 0x45d5, 0x0000 }, + { 0x45d6, 0x0000 }, + { 0x45d7, 0x0000 }, + { 0x45d8, 0x0000 }, + { 0x45d9, 0x0000 }, + { 0x45da, 0x0000 }, + { 0x45db, 0x0000 }, + { 0x45dc, 0x0000 }, + { 0x45dd, 0x0000 }, + { 0x45de, 0x0000 }, + { 0x45df, 0x0000 }, + { 0x45e0, 0x0000 }, + { 0x45e1, 0x0000 }, + { 0x45e2, 0x0000 }, + { 0x45e3, 0x0000 }, + { 0x45e4, 0x0000 }, + { 0x45e5, 0x0000 }, + { 0x45e6, 0x0000 }, + { 0x45e7, 0x0000 }, + { 0x45e8, 0x0000 }, + { 0x45e9, 0x0000 }, + { 0x45ea, 0x0000 }, + { 0x45eb, 0x0000 }, + { 0x45ec, 0x0000 }, + { 0x45ed, 0x0000 }, + { 0x45ee, 0x0000 }, + { 0x45ef, 0x0000 }, + { 0x45f0, 0x0000 }, + { 0x45f1, 0x0000 }, + { 0x45f2, 0x0000 }, + { 0x45f3, 0x0000 }, + { 0x45f4, 0x0000 }, + { 0x45f5, 0x0000 }, + { 0x45f6, 0x0000 }, + { 0x45f7, 0x0000 }, + { 0x45f8, 0x0000 }, + { 0x45f9, 0x0000 }, + { 0x45fa, 0x0000 }, + { 0x45fb, 0x0000 }, + { 0x45fc, 0x0000 }, + { 0x45fd, 0x0000 }, + { 0x45fe, 0x0000 }, + { 0x45ff, 0x0000 }, + { 0x4600, 0x0000 }, + { 0x4601, 0x0000 }, + { 0x4602, 0x0000 }, + { 0x4603, 0x0000 }, + { 0x4604, 0x0000 }, + { 0x4605, 0x0000 }, + { 0x4606, 0x0000 }, + { 0x4607, 0x0000 }, + { 0x4608, 0x0000 }, + { 0x4609, 0x0000 }, + { 0x460a, 0x0000 }, + { 0x460b, 0x0000 }, + { 0x460c, 0x0000 }, + { 0x460d, 0x0000 }, + { 0x460e, 0x0000 }, + { 0x460f, 0x0000 }, + { 0x4610, 0x0000 }, + { 0x4611, 0x0000 }, + { 0x4612, 0x0000 }, + { 0x4613, 0x0000 }, + { 0x4614, 0x0000 }, + { 0x4615, 0x0000 }, + { 0x4616, 0x0000 }, + { 0x4617, 0x0000 }, + { 0x4618, 0x0000 }, + { 0x4619, 0x0000 }, + { 0x461a, 0x0000 }, + { 0x461b, 0x0000 }, + { 0x461c, 0x0000 }, + { 0x461d, 0x0000 }, + { 0x461e, 0x0000 }, + { 0x461f, 0x0000 }, + { 0x4620, 0x0000 }, + { 0x4621, 0x0000 }, + { 0x4622, 0x0000 }, + { 0x4623, 0x0000 }, + { 0x4624, 0x0000 }, + { 0x4625, 0x0000 }, + { 0x4626, 0x0000 }, + { 0x4627, 0x0000 }, + { 0x4628, 0x0000 }, + { 0x4629, 0x0000 }, + { 0x462a, 0x0000 }, + { 0x462b, 0x0000 }, + { 0x462c, 0x0000 }, + { 0x462d, 0x0000 }, + { 0x462e, 0x0000 }, + { 0x462f, 0x0000 }, + { 0x4630, 0x0000 }, + { 0x4631, 0x0000 }, + { 0x4632, 0x0000 }, + { 0x4633, 0x0000 }, + { 0x4634, 0x0000 }, + { 0x4635, 0x0000 }, + { 0x4636, 0x0000 }, + { 0x4637, 0x0000 }, + { 0x4638, 0x0000 }, + { 0x4639, 0x0000 }, + { 0x463a, 0x0000 }, + { 0x463b, 0x0000 }, + { 0x463c, 0x0000 }, + { 0x463d, 0x0000 }, + { 0x463e, 0x0000 }, + { 0x463f, 0x0000 }, + { 0x4640, 0x0000 }, + { 0x4641, 0x0000 }, + { 0x4642, 0x0000 }, + { 0x4643, 0x0000 }, + { 0x4644, 0x0000 }, + { 0x4645, 0x0000 }, + { 0x4646, 0x0000 }, + { 0x4647, 0x0000 }, + { 0x4648, 0x0000 }, + { 0x4649, 0x0000 }, + { 0x464a, 0x0000 }, + { 0x464b, 0x0000 }, + { 0x464c, 0x0000 }, + { 0x464d, 0x0000 }, + { 0x464e, 0x0000 }, + { 0x464f, 0x0000 }, + { 0x4650, 0x0000 }, + { 0x4651, 0x0000 }, + { 0x4652, 0x0000 }, + { 0x4653, 0x0000 }, + { 0x4654, 0x0000 }, + { 0x4655, 0x0000 }, + { 0x4656, 0x0000 }, + { 0x4657, 0x0000 }, + { 0x4658, 0x0000 }, + { 0x4659, 0x0000 }, + { 0x465a, 0x0000 }, + { 0x465b, 0x0000 }, + { 0x465c, 0x0000 }, + { 0x465d, 0x0000 }, + { 0x465e, 0x0000 }, + { 0x465f, 0x0000 }, + { 0x4660, 0x0000 }, + { 0x4661, 0x0000 }, + { 0x4662, 0x0000 }, + { 0x4663, 0x0000 }, + { 0x4664, 0x0000 }, + { 0x4665, 0x0000 }, + { 0x4666, 0x0000 }, + { 0x4667, 0x0000 }, + { 0x4668, 0x0000 }, + { 0x4669, 0x0000 }, + { 0x466a, 0x0000 }, + { 0x466b, 0x0000 }, + { 0x466c, 0x0000 }, + { 0x466d, 0x0000 }, + { 0x466e, 0x0000 }, + { 0x466f, 0x0000 }, + { 0x4670, 0x0000 }, + { 0x4671, 0x0000 }, + { 0x4672, 0x0000 }, + { 0x4673, 0x0000 }, + { 0x4674, 0x0000 }, + { 0x4675, 0x0000 }, + { 0x4676, 0x0000 }, + { 0x4677, 0x0000 }, + { 0x4678, 0x0000 }, + { 0x4679, 0x0000 }, + { 0x467a, 0x0000 }, + { 0x467b, 0x0000 }, + { 0x467c, 0x0000 }, + { 0x467d, 0x0000 }, + { 0x467e, 0x0000 }, + { 0x467f, 0x0000 }, + { 0x4680, 0x0000 }, + { 0x4681, 0x0000 }, + { 0x4682, 0x0000 }, + { 0x4683, 0x0000 }, + { 0x4684, 0x0000 }, + { 0x4685, 0x0000 }, + { 0x4686, 0x0000 }, + { 0x4687, 0x0000 }, + { 0x4688, 0x0000 }, + { 0x4689, 0x0000 }, + { 0x468a, 0x0000 }, + { 0x468b, 0x0000 }, + { 0x468c, 0x0000 }, + { 0x468d, 0x0000 }, + { 0x468e, 0x0000 }, + { 0x468f, 0x0000 }, + { 0x4690, 0x0000 }, + { 0x4691, 0x0000 }, + { 0x4692, 0x0000 }, + { 0x4693, 0x0000 }, + { 0x4694, 0x0000 }, + { 0x4695, 0x0000 }, + { 0x4696, 0x0000 }, + { 0x4697, 0x0000 }, + { 0x4698, 0x0000 }, + { 0x4699, 0x0000 }, + { 0x469a, 0x0000 }, + { 0x469b, 0x0000 }, + { 0x469c, 0x0000 }, + { 0x469d, 0x0000 }, + { 0x469e, 0x0000 }, + { 0x469f, 0x0000 }, + { 0x46a0, 0x0000 }, + { 0x46a1, 0x0000 }, + { 0x46a2, 0x0000 }, + { 0x46a3, 0x0000 }, + { 0x46a4, 0x0000 }, + { 0x46a5, 0x0000 }, + { 0x46a6, 0x0000 }, + { 0x46a7, 0x0000 }, + { 0x46a8, 0x0000 }, + { 0x46a9, 0x0000 }, + { 0x46aa, 0x0000 }, + { 0x46ab, 0x0000 }, + { 0x46ac, 0x0000 }, + { 0x46ad, 0x0000 }, + { 0x46ae, 0x0000 }, + { 0x46af, 0x0000 }, + { 0x46b0, 0x0000 }, + { 0x46b1, 0x0000 }, + { 0x46b2, 0x0000 }, + { 0x46b3, 0x0000 }, + { 0x46b4, 0x0000 }, + { 0x46b5, 0x0000 }, + { 0x46b6, 0x0000 }, + { 0x46b7, 0x0000 }, + { 0x46b8, 0x0000 }, + { 0x46b9, 0x0000 }, + { 0x46ba, 0x0000 }, + { 0x46bb, 0x0000 }, + { 0x46bc, 0x0000 }, + { 0x46bd, 0x0000 }, + { 0x46be, 0x0000 }, + { 0x46bf, 0x0000 }, + { 0x46c0, 0x0000 }, + { 0x46c1, 0x0000 }, + { 0x46c2, 0x0000 }, + { 0x46c3, 0x0000 }, + { 0x46c4, 0x0000 }, + { 0x46c5, 0x0000 }, + { 0x46c6, 0x0000 }, + { 0x46c7, 0x0000 }, + { 0x46c8, 0x0000 }, + { 0x46c9, 0x0000 }, + { 0x46ca, 0x0000 }, + { 0x46cb, 0x0000 }, + { 0x46cc, 0x0000 }, + { 0x46cd, 0x0000 }, + { 0x46ce, 0x0000 }, + { 0x46cf, 0x0000 }, + { 0x46d0, 0x0000 }, + { 0x46d1, 0x0000 }, + { 0x46d2, 0x0000 }, + { 0x46d3, 0x0000 }, + { 0x46d4, 0x0000 }, + { 0x46d5, 0x0000 }, + { 0x46d6, 0x0000 }, + { 0x46d7, 0x0000 }, + { 0x46d8, 0x0000 }, + { 0x46d9, 0x0000 }, + { 0x46da, 0x0000 }, + { 0x46db, 0x0000 }, + { 0x46dc, 0x0000 }, + { 0x46dd, 0x0000 }, + { 0x46de, 0x0000 }, + { 0x46df, 0x0000 }, + { 0x46e0, 0x0000 }, + { 0x46e1, 0x0000 }, + { 0x46e2, 0x0000 }, + { 0x46e3, 0x0000 }, + { 0x46e4, 0x0000 }, + { 0x46e5, 0x0000 }, + { 0x46e6, 0x0000 }, + { 0x46e7, 0x0000 }, + { 0x46e8, 0x0000 }, + { 0x46e9, 0x0000 }, + { 0x46ea, 0x0000 }, + { 0x46eb, 0x0000 }, + { 0x46ec, 0x0000 }, + { 0x46ed, 0x0000 }, + { 0x46ee, 0x0000 }, + { 0x46ef, 0x0000 }, + { 0x46f0, 0x0000 }, + { 0x46f1, 0x0000 }, + { 0x46f2, 0x0000 }, + { 0x46f3, 0x0000 }, + { 0x46f4, 0x0000 }, + { 0x46f5, 0x0000 }, + { 0x46f6, 0x0000 }, + { 0x46f7, 0x0000 }, + { 0x46f8, 0x0000 }, + { 0x46f9, 0x0000 }, + { 0x46fa, 0x0000 }, + { 0x46fb, 0x0000 }, + { 0x46fc, 0x0000 }, + { 0x46fd, 0x0000 }, + { 0x46fe, 0x0000 }, + { 0x46ff, 0x0000 }, + { 0x4700, 0x0000 }, + { 0x4701, 0x0000 }, + { 0x4702, 0x0000 }, + { 0x4703, 0x0000 }, + { 0x4704, 0x0000 }, + { 0x4705, 0x0000 }, + { 0x4706, 0x0000 }, + { 0x4707, 0x0000 }, + { 0x4708, 0x0000 }, + { 0x4709, 0x0000 }, + { 0x470a, 0x0000 }, + { 0x470b, 0x0000 }, + { 0x470c, 0x0000 }, + { 0x470d, 0x0000 }, + { 0x470e, 0x0000 }, + { 0x470f, 0x0000 }, + { 0x4710, 0x0000 }, + { 0x4711, 0x0000 }, + { 0x4712, 0x0000 }, + { 0x4713, 0x0000 }, + { 0x4714, 0x0000 }, + { 0x4715, 0x0000 }, + { 0x4716, 0x0000 }, + { 0x4717, 0x0000 }, + { 0x4718, 0x0000 }, + { 0x4719, 0x0000 }, + { 0x471a, 0x0000 }, + { 0x471b, 0x0000 }, + { 0x471c, 0x0000 }, + { 0x471d, 0x0000 }, + { 0x471e, 0x0000 }, + { 0x471f, 0x0000 }, + { 0x4720, 0x0000 }, + { 0x4721, 0x0000 }, + { 0x4722, 0x0000 }, + { 0x4723, 0x0000 }, + { 0x4724, 0x0000 }, + { 0x4725, 0x0000 }, + { 0x4726, 0x0000 }, + { 0x4727, 0x0000 }, + { 0x4728, 0x0000 }, + { 0x4729, 0x0000 }, + { 0x472a, 0x0000 }, + { 0x472b, 0x0000 }, + { 0x472c, 0x0000 }, + { 0x472d, 0x0000 }, + { 0x472e, 0x0000 }, + { 0x472f, 0x0000 }, + { 0x4730, 0x0000 }, + { 0x4731, 0x0000 }, + { 0x4732, 0x0000 }, + { 0x4733, 0x0000 }, + { 0x4734, 0x0000 }, + { 0x4735, 0x0000 }, + { 0x4736, 0x0000 }, + { 0x4737, 0x0000 }, + { 0x4738, 0x0000 }, + { 0x4739, 0x0000 }, + { 0x473a, 0x0000 }, + { 0x473b, 0x0000 }, + { 0x473c, 0x0000 }, + { 0x473d, 0x0000 }, + { 0x473e, 0x0000 }, + { 0x473f, 0x0000 }, + { 0x4740, 0x0000 }, + { 0x4741, 0x0000 }, + { 0x4742, 0x0000 }, + { 0x4743, 0x0000 }, + { 0x4744, 0x0000 }, + { 0x4745, 0x0000 }, + { 0x4746, 0x0000 }, + { 0x4747, 0x0000 }, + { 0x4748, 0x0000 }, + { 0x4749, 0x0000 }, + { 0x474a, 0x0000 }, + { 0x474b, 0x0000 }, + { 0x474c, 0x0000 }, + { 0x474d, 0x0000 }, + { 0x474e, 0x0000 }, + { 0x474f, 0x0000 }, + { 0x4750, 0x0000 }, + { 0x4751, 0x0000 }, + { 0x4752, 0x0000 }, + { 0x4753, 0x0000 }, + { 0x4754, 0x0000 }, + { 0x4755, 0x0000 }, + { 0x4756, 0x0000 }, + { 0x4757, 0x0000 }, + { 0x4758, 0x0000 }, + { 0x4759, 0x0000 }, + { 0x475a, 0x0000 }, + { 0x475b, 0x0000 }, + { 0x475c, 0x0000 }, + { 0x475d, 0x0000 }, + { 0x475e, 0x0000 }, + { 0x475f, 0x0000 }, + { 0x4760, 0x0000 }, + { 0x4761, 0x0000 }, + { 0x4762, 0x0000 }, + { 0x4763, 0x0000 }, + { 0x4764, 0x0000 }, + { 0x4765, 0x0000 }, + { 0x4766, 0x0000 }, + { 0x4767, 0x0000 }, + { 0x4768, 0x0000 }, + { 0x4769, 0x0000 }, + { 0x476a, 0x0000 }, + { 0x476b, 0x0000 }, + { 0x476c, 0x0000 }, + { 0x476d, 0x0000 }, + { 0x476e, 0x0000 }, + { 0x476f, 0x0000 }, + { 0x4770, 0x0000 }, + { 0x4771, 0x0000 }, + { 0x4772, 0x0000 }, + { 0x4773, 0x0000 }, + { 0x4774, 0x0000 }, + { 0x4775, 0x0000 }, + { 0x4776, 0x0000 }, + { 0x4777, 0x0000 }, + { 0x4778, 0x0000 }, + { 0x4779, 0x0000 }, + { 0x477a, 0x0000 }, + { 0x477b, 0x0000 }, + { 0x477c, 0x0000 }, + { 0x477d, 0x0000 }, + { 0x477e, 0x0000 }, + { 0x477f, 0x0000 }, + { 0x4780, 0x0000 }, + { 0x4781, 0x0000 }, + { 0x4782, 0x0000 }, + { 0x4783, 0x0000 }, + { 0x4784, 0x0000 }, + { 0x4785, 0x0000 }, + { 0x4786, 0x0000 }, + { 0x4787, 0x0000 }, + { 0x4788, 0x0000 }, + { 0x4789, 0x0000 }, + { 0x478a, 0x0000 }, + { 0x478b, 0x0000 }, + { 0x478c, 0x0000 }, + { 0x478d, 0x0000 }, + { 0x478e, 0x0000 }, + { 0x478f, 0x0000 }, + { 0x4790, 0x0000 }, + { 0x4791, 0x0000 }, + { 0x4792, 0x0000 }, + { 0x4793, 0x0000 }, + { 0x4794, 0x0000 }, + { 0x4795, 0x0000 }, + { 0x4796, 0x0000 }, + { 0x4797, 0x0000 }, + { 0x4798, 0x0000 }, + { 0x4799, 0x0000 }, + { 0x479a, 0x0000 }, + { 0x479b, 0x0000 }, + { 0x479c, 0x0000 }, + { 0x479d, 0x0000 }, + { 0x479e, 0x0000 }, + { 0x479f, 0x0000 }, + { 0x47a0, 0x0000 }, + { 0x47a1, 0x0000 }, + { 0x47a2, 0x0000 }, + { 0x47a3, 0x0000 }, + { 0x47a4, 0x0000 }, + { 0x47a5, 0x0000 }, + { 0x47a6, 0x0000 }, + { 0x47a7, 0x0000 }, + { 0x47a8, 0x0000 }, + { 0x47a9, 0x0000 }, + { 0x47aa, 0x0000 }, + { 0x47ab, 0x0000 }, + { 0x47ac, 0x0000 }, + { 0x47ad, 0x0000 }, + { 0x47ae, 0x0000 }, + { 0x47af, 0x0000 }, + { 0x47b0, 0x0000 }, + { 0x47b1, 0x0000 }, + { 0x47b2, 0x0000 }, + { 0x47b3, 0x0000 }, + { 0x47b4, 0x0000 }, + { 0x47b5, 0x0000 }, + { 0x47b6, 0x0000 }, + { 0x47b7, 0x0000 }, + { 0x47b8, 0x0000 }, + { 0x47b9, 0x0000 }, + { 0x47ba, 0x0000 }, + { 0x47bb, 0x0000 }, + { 0x47bc, 0x0000 }, + { 0x47bd, 0x0000 }, + { 0x47be, 0x0000 }, + { 0x47bf, 0x0000 }, + { 0x47c0, 0x0000 }, + { 0x47c1, 0x0000 }, + { 0x47c2, 0x0000 }, + { 0x47c3, 0x0000 }, + { 0x47c4, 0x0000 }, + { 0x47c5, 0x0000 }, + { 0x47c6, 0x0000 }, + { 0x47c7, 0x0000 }, + { 0x47c8, 0x0000 }, + { 0x47c9, 0x0000 }, + { 0x47ca, 0x0000 }, + { 0x47cb, 0x0000 }, + { 0x47cc, 0x0000 }, + { 0x47cd, 0x0000 }, + { 0x47ce, 0x0000 }, + { 0x47cf, 0x0000 }, + { 0x47d0, 0x0000 }, + { 0x47d1, 0x0000 }, + { 0x47d2, 0x0000 }, + { 0x47d3, 0x0000 }, + { 0x47d4, 0x0000 }, + { 0x47d5, 0x0000 }, + { 0x47d6, 0x0000 }, + { 0x47d7, 0x0000 }, + { 0x47d8, 0x0000 }, + { 0x47d9, 0x0000 }, + { 0x47da, 0x0000 }, + { 0x47db, 0x0000 }, + { 0x47dc, 0x0000 }, + { 0x47dd, 0x0000 }, + { 0x47de, 0x0000 }, + { 0x47df, 0x0000 }, + { 0x47e0, 0x0000 }, + { 0x47e1, 0x0000 }, + { 0x47e2, 0x0000 }, + { 0x47e3, 0x0000 }, + { 0x47e4, 0x0000 }, + { 0x47e5, 0x0000 }, + { 0x47e6, 0x0000 }, + { 0x47e7, 0x0000 }, + { 0x47e8, 0x0000 }, + { 0x47e9, 0x0000 }, + { 0x47ea, 0x0000 }, + { 0x47eb, 0x0000 }, + { 0x47ec, 0x0000 }, + { 0x47ed, 0x0000 }, + { 0x47ee, 0x0000 }, + { 0x47ef, 0x0000 }, + { 0x47f0, 0x0000 }, + { 0x47f1, 0x0000 }, + { 0x47f2, 0x0000 }, + { 0x47f3, 0x0000 }, + { 0x47f4, 0x0000 }, + { 0x47f5, 0x0000 }, + { 0x47f6, 0x0000 }, + { 0x47f7, 0x0000 }, + { 0x47f8, 0x0000 }, + { 0x47f9, 0x0000 }, + { 0x47fa, 0x0000 }, + { 0x47fb, 0x0000 }, + { 0x47fc, 0x0000 }, + { 0x47fd, 0x0000 }, + { 0x47fe, 0x0000 }, + { 0x47ff, 0x0000 }, + { 0x4800, 0x0000 }, + { 0x4801, 0x0000 }, + { 0x4802, 0x0000 }, + { 0x4803, 0x0000 }, + { 0x4804, 0x0000 }, + { 0x4805, 0x0000 }, + { 0x4806, 0x0000 }, + { 0x4807, 0x0000 }, + { 0x4808, 0x0000 }, + { 0x4809, 0x0000 }, + { 0x480a, 0x0000 }, + { 0x480b, 0x0000 }, + { 0x480c, 0x0000 }, + { 0x480d, 0x0000 }, + { 0x480e, 0x0000 }, + { 0x480f, 0x0000 }, + { 0x4810, 0x0000 }, + { 0x4811, 0x0000 }, + { 0x4812, 0x0000 }, + { 0x4813, 0x0000 }, + { 0x4814, 0x0000 }, + { 0x4815, 0x0000 }, + { 0x4816, 0x0000 }, + { 0x4817, 0x0000 }, + { 0x4818, 0x0000 }, + { 0x4819, 0x0000 }, + { 0x481a, 0x0000 }, + { 0x481b, 0x0000 }, + { 0x481c, 0x0000 }, + { 0x481d, 0x0000 }, + { 0x481e, 0x0000 }, + { 0x481f, 0x0000 }, + { 0x4820, 0x0000 }, + { 0x4821, 0x0000 }, + { 0x4822, 0x0000 }, + { 0x4823, 0x0000 }, + { 0x4824, 0x0000 }, + { 0x4825, 0x0000 }, + { 0x4826, 0x0000 }, + { 0x4827, 0x0000 }, + { 0x4828, 0x0000 }, + { 0x4829, 0x0000 }, + { 0x482a, 0x0000 }, + { 0x482b, 0x0000 }, + { 0x482c, 0x0000 }, + { 0x482d, 0x0000 }, + { 0x482e, 0x0000 }, + { 0x482f, 0x0000 }, + { 0x4830, 0x0000 }, + { 0x4831, 0x0000 }, + { 0x4832, 0x0000 }, + { 0x4833, 0x0000 }, + { 0x4834, 0x0000 }, + { 0x4835, 0x0000 }, + { 0x4836, 0x0000 }, + { 0x4837, 0x0000 }, + { 0x4838, 0x0000 }, + { 0x4839, 0x0000 }, + { 0x483a, 0x0000 }, + { 0x483b, 0x0000 }, + { 0x483c, 0x0000 }, + { 0x483d, 0x0000 }, + { 0x483e, 0x0000 }, + { 0x483f, 0x0000 }, + { 0x4840, 0x0000 }, + { 0x4841, 0x0000 }, + { 0x4842, 0x0000 }, + { 0x4843, 0x0000 }, + { 0x4844, 0x0000 }, + { 0x4845, 0x0000 }, + { 0x4846, 0x0000 }, + { 0x4847, 0x0000 }, + { 0x4848, 0x0000 }, + { 0x4849, 0x0000 }, + { 0x484a, 0x0000 }, + { 0x484b, 0x0000 }, + { 0x484c, 0x0000 }, + { 0x484d, 0x0000 }, + { 0x484e, 0x0000 }, + { 0x484f, 0x0000 }, + { 0x4850, 0x0000 }, + { 0x4851, 0x0000 }, + { 0x4852, 0x0000 }, + { 0x4853, 0x0000 }, + { 0x4854, 0x0000 }, + { 0x4855, 0x0000 }, + { 0x4856, 0x0000 }, + { 0x4857, 0x0000 }, + { 0x4858, 0x0000 }, + { 0x4859, 0x0000 }, + { 0x485a, 0x0000 }, + { 0x485b, 0x0000 }, + { 0x485c, 0x0000 }, + { 0x485d, 0x0000 }, + { 0x485e, 0x0000 }, + { 0x485f, 0x0000 }, + { 0x4860, 0x0000 }, + { 0x4861, 0x0000 }, + { 0x4862, 0x0000 }, + { 0x4863, 0x0000 }, + { 0x4864, 0x0000 }, + { 0x4865, 0x0000 }, + { 0x4866, 0x0000 }, + { 0x4867, 0x0000 }, + { 0x4868, 0x0000 }, + { 0x4869, 0x0000 }, + { 0x486a, 0x0000 }, + { 0x486b, 0x0000 }, + { 0x486c, 0x0000 }, + { 0x486d, 0x0000 }, + { 0x486e, 0x0000 }, + { 0x486f, 0x0000 }, + { 0x4870, 0x0000 }, + { 0x4871, 0x0000 }, + { 0x4872, 0x0000 }, + { 0x4873, 0x0000 }, + { 0x4874, 0x0000 }, + { 0x4875, 0x0000 }, + { 0x4876, 0x0000 }, + { 0x4877, 0x0000 }, + { 0x4878, 0x0000 }, + { 0x4879, 0x0000 }, + { 0x487a, 0x0000 }, + { 0x487b, 0x0000 }, + { 0x487c, 0x0000 }, + { 0x487d, 0x0000 }, + { 0x487e, 0x0000 }, + { 0x487f, 0x0000 }, + { 0x4880, 0x0000 }, + { 0x4881, 0x0000 }, + { 0x4882, 0x0000 }, + { 0x4883, 0x0000 }, + { 0x4884, 0x0000 }, + { 0x4885, 0x0000 }, + { 0x4886, 0x0000 }, + { 0x4887, 0x0000 }, + { 0x4888, 0x0000 }, + { 0x4889, 0x0000 }, + { 0x488a, 0x0000 }, + { 0x488b, 0x0000 }, + { 0x488c, 0x0000 }, + { 0x488d, 0x0000 }, + { 0x488e, 0x0000 }, + { 0x488f, 0x0000 }, + { 0x4890, 0x0000 }, + { 0x4891, 0x0000 }, + { 0x4892, 0x0000 }, + { 0x4893, 0x0000 }, + { 0x4894, 0x0000 }, + { 0x4895, 0x0000 }, + { 0x4896, 0x0000 }, + { 0x4897, 0x0000 }, + { 0x4898, 0x0000 }, + { 0x4899, 0x0000 }, + { 0x489a, 0x0000 }, + { 0x489b, 0x0000 }, + { 0x489c, 0x0000 }, + { 0x489d, 0x0000 }, + { 0x489e, 0x0000 }, + { 0x489f, 0x0000 }, + { 0x48a0, 0x0000 }, + { 0x48a1, 0x0000 }, + { 0x48a2, 0x0000 }, + { 0x48a3, 0x0000 }, + { 0x48a4, 0x0000 }, + { 0x48a5, 0x0000 }, + { 0x48a6, 0x0000 }, + { 0x48a7, 0x0000 }, + { 0x48a8, 0x0000 }, + { 0x48a9, 0x0000 }, + { 0x48aa, 0x0000 }, + { 0x48ab, 0x0000 }, + { 0x48ac, 0x0000 }, + { 0x48ad, 0x0000 }, + { 0x48ae, 0x0000 }, + { 0x48af, 0x0000 }, + { 0x48b0, 0x0000 }, + { 0x48b1, 0x0000 }, + { 0x48b2, 0x0000 }, + { 0x48b3, 0x0000 }, + { 0x48b4, 0x0000 }, + { 0x48b5, 0x0000 }, + { 0x48b6, 0x0000 }, + { 0x48b7, 0x0000 }, + { 0x48b8, 0x0000 }, + { 0x48b9, 0x0000 }, + { 0x48ba, 0x0000 }, + { 0x48bb, 0x0000 }, + { 0x48bc, 0x0000 }, + { 0x48bd, 0x0000 }, + { 0x48be, 0x0000 }, + { 0x48bf, 0x0000 }, + { 0x48c0, 0x0000 }, + { 0x48c1, 0x0000 }, + { 0x48c2, 0x0000 }, + { 0x48c3, 0x0000 }, + { 0x48c4, 0x0000 }, + { 0x48c5, 0x0000 }, + { 0x48c6, 0x0000 }, + { 0x48c7, 0x0000 }, + { 0x48c8, 0x0000 }, + { 0x48c9, 0x0000 }, + { 0x48ca, 0x0000 }, + { 0x48cb, 0x0000 }, + { 0x48cc, 0x0000 }, + { 0x48cd, 0x0000 }, + { 0x48ce, 0x0000 }, + { 0x48cf, 0x0000 }, + { 0x48d0, 0x0000 }, + { 0x48d1, 0x0000 }, + { 0x48d2, 0x0000 }, + { 0x48d3, 0x0000 }, + { 0x48d4, 0x0000 }, + { 0x48d5, 0x0000 }, + { 0x48d6, 0x0000 }, + { 0x48d7, 0x0000 }, + { 0x48d8, 0x0000 }, + { 0x48d9, 0x0000 }, + { 0x48da, 0x0000 }, + { 0x48db, 0x0000 }, + { 0x48dc, 0x0000 }, + { 0x48dd, 0x0000 }, + { 0x48de, 0x0000 }, + { 0x48df, 0x0000 }, + { 0x48e0, 0x0000 }, + { 0x48e1, 0x0000 }, + { 0x48e2, 0x0000 }, + { 0x48e3, 0x0000 }, + { 0x48e4, 0x0000 }, + { 0x48e5, 0x0000 }, + { 0x48e6, 0x0000 }, + { 0x48e7, 0x0000 }, + { 0x48e8, 0x0000 }, + { 0x48e9, 0x0000 }, + { 0x48ea, 0x0000 }, + { 0x48eb, 0x0000 }, + { 0x48ec, 0x0000 }, + { 0x48ed, 0x0000 }, + { 0x48ee, 0x0000 }, + { 0x48ef, 0x0000 }, + { 0x48f0, 0x0000 }, + { 0x48f1, 0x0000 }, + { 0x48f2, 0x0000 }, + { 0x48f3, 0x0000 }, + { 0x48f4, 0x0000 }, + { 0x48f5, 0x0000 }, + { 0x48f6, 0x0000 }, + { 0x48f7, 0x0000 }, + { 0x48f8, 0x0000 }, + { 0x48f9, 0x0000 }, + { 0x48fa, 0x0000 }, + { 0x48fb, 0x0000 }, + { 0x48fc, 0x0000 }, + { 0x48fd, 0x0000 }, + { 0x48fe, 0x0000 }, + { 0x48ff, 0x0000 }, + { 0x4900, 0x0000 }, + { 0x4901, 0x0000 }, + { 0x4902, 0x0000 }, + { 0x4903, 0x0000 }, + { 0x4904, 0x0000 }, + { 0x4905, 0x0000 }, + { 0x4906, 0x0000 }, + { 0x4907, 0x0000 }, + { 0x4908, 0x0000 }, + { 0x4909, 0x0000 }, + { 0x490a, 0x0000 }, + { 0x490b, 0x0000 }, + { 0x490c, 0x0000 }, + { 0x490d, 0x0000 }, + { 0x490e, 0x0000 }, + { 0x490f, 0x0000 }, + { 0x4910, 0x0000 }, + { 0x4911, 0x0000 }, + { 0x4912, 0x0000 }, + { 0x4913, 0x0000 }, + { 0x4914, 0x0000 }, + { 0x4915, 0x0000 }, + { 0x4916, 0x0000 }, + { 0x4917, 0x0000 }, + { 0x4918, 0x0000 }, + { 0x4919, 0x0000 }, + { 0x491a, 0x0000 }, + { 0x491b, 0x0000 }, + { 0x491c, 0x0000 }, + { 0x491d, 0x0000 }, + { 0x491e, 0x0000 }, + { 0x491f, 0x0000 }, + { 0x4920, 0x0000 }, + { 0x4921, 0x0000 }, + { 0x4922, 0x0000 }, + { 0x4923, 0x0000 }, + { 0x4924, 0x0000 }, + { 0x4925, 0x0000 }, + { 0x4926, 0x0000 }, + { 0x4927, 0x0000 }, + { 0x4928, 0x0000 }, + { 0x4929, 0x0000 }, + { 0x492a, 0x0000 }, + { 0x492b, 0x0000 }, + { 0x492c, 0x0000 }, + { 0x492d, 0x0000 }, + { 0x492e, 0x0000 }, + { 0x492f, 0x0000 }, + { 0x4930, 0x0000 }, + { 0x4931, 0x0000 }, + { 0x4932, 0x0000 }, + { 0x4933, 0x0000 }, + { 0x4934, 0x0000 }, + { 0x4935, 0x0000 }, + { 0x4936, 0x0000 }, + { 0x4937, 0x0000 }, + { 0x4938, 0x0000 }, + { 0x4939, 0x0000 }, + { 0x493a, 0x0000 }, + { 0x493b, 0x0000 }, + { 0x493c, 0x0000 }, + { 0x493d, 0x0000 }, + { 0x493e, 0x0000 }, + { 0x493f, 0x0000 }, + { 0x4940, 0x0000 }, + { 0x4941, 0x0000 }, + { 0x4942, 0x0000 }, + { 0x4943, 0x0000 }, + { 0x4944, 0x0000 }, + { 0x4945, 0x0000 }, + { 0x4946, 0x0000 }, + { 0x4947, 0x0000 }, + { 0x4948, 0x0000 }, + { 0x4949, 0x0000 }, + { 0x494a, 0x0000 }, + { 0x494b, 0x0000 }, + { 0x494c, 0x0000 }, + { 0x494d, 0x0000 }, + { 0x494e, 0x0000 }, + { 0x494f, 0x0000 }, + { 0x4950, 0x0000 }, + { 0x4951, 0x0000 }, + { 0x4952, 0x0000 }, + { 0x4953, 0x0000 }, + { 0x4954, 0x0000 }, + { 0x4955, 0x0000 }, + { 0x4956, 0x0000 }, + { 0x4957, 0x0000 }, + { 0x4958, 0x0000 }, + { 0x4959, 0x0000 }, + { 0x495a, 0x0000 }, + { 0x495b, 0x0000 }, + { 0x495c, 0x0000 }, + { 0x495d, 0x0000 }, + { 0x495e, 0x0000 }, + { 0x495f, 0x0000 }, + { 0x4960, 0x0000 }, + { 0x4961, 0x0000 }, + { 0x4962, 0x0000 }, + { 0x4963, 0x0000 }, + { 0x4964, 0x0000 }, + { 0x4965, 0x0000 }, + { 0x4966, 0x0000 }, + { 0x4967, 0x0000 }, + { 0x4968, 0x0000 }, + { 0x4969, 0x0000 }, + { 0x496a, 0x0000 }, + { 0x496b, 0x0000 }, + { 0x496c, 0x0000 }, + { 0x496d, 0x0000 }, + { 0x496e, 0x0000 }, + { 0x496f, 0x0000 }, + { 0x4970, 0x0000 }, + { 0x4971, 0x0000 }, + { 0x4972, 0x0000 }, + { 0x4973, 0x0000 }, + { 0x4974, 0x0000 }, + { 0x4975, 0x0000 }, + { 0x4976, 0x0000 }, + { 0x4977, 0x0000 }, + { 0x4978, 0x0000 }, + { 0x4979, 0x0000 }, + { 0x497a, 0x0000 }, + { 0x497b, 0x0000 }, + { 0x497c, 0x0000 }, + { 0x497d, 0x0000 }, + { 0x497e, 0x0000 }, + { 0x497f, 0x0000 }, + { 0x4980, 0x0000 }, + { 0x4981, 0x0000 }, + { 0x4982, 0x0000 }, + { 0x4983, 0x0000 }, + { 0x4984, 0x0000 }, + { 0x4985, 0x0000 }, + { 0x4986, 0x0000 }, + { 0x4987, 0x0000 }, + { 0x4988, 0x0000 }, + { 0x4989, 0x0000 }, + { 0x498a, 0x0000 }, + { 0x498b, 0x0000 }, + { 0x498c, 0x0000 }, + { 0x498d, 0x0000 }, + { 0x498e, 0x0000 }, + { 0x498f, 0x0000 }, + { 0x4990, 0x0000 }, + { 0x4991, 0x0000 }, + { 0x4992, 0x0000 }, + { 0x4993, 0x0000 }, + { 0x4994, 0x0000 }, + { 0x4995, 0x0000 }, + { 0x4996, 0x0000 }, + { 0x4997, 0x0000 }, + { 0x4998, 0x0000 }, + { 0x4999, 0x0000 }, + { 0x499a, 0x0000 }, + { 0x499b, 0x0000 }, + { 0x499c, 0x0000 }, + { 0x499d, 0x0000 }, + { 0x499e, 0x0000 }, + { 0x499f, 0x0000 }, + { 0x49a0, 0x0000 }, + { 0x49a1, 0x0000 }, + { 0x49a2, 0x0000 }, + { 0x49a3, 0x0000 }, + { 0x49a4, 0x0000 }, + { 0x49a5, 0x0000 }, + { 0x49a6, 0x0000 }, + { 0x49a7, 0x0000 }, + { 0x49a8, 0x0000 }, + { 0x49a9, 0x0000 }, + { 0x49aa, 0x0000 }, + { 0x49ab, 0x0000 }, + { 0x49ac, 0x0000 }, + { 0x49ad, 0x0000 }, + { 0x49ae, 0x0000 }, + { 0x49af, 0x0000 }, + { 0x49b0, 0x0000 }, + { 0x49b1, 0x0000 }, + { 0x49b2, 0x0000 }, + { 0x49b3, 0x0000 }, + { 0x49b4, 0x0000 }, + { 0x49b5, 0x0000 }, + { 0x49b6, 0x0000 }, + { 0x49b7, 0x0000 }, + { 0x49b8, 0x0000 }, + { 0x49b9, 0x0000 }, + { 0x49ba, 0x0000 }, + { 0x49bb, 0x0000 }, + { 0x49bc, 0x0000 }, + { 0x49bd, 0x0000 }, + { 0x49be, 0x0000 }, + { 0x49bf, 0x0000 }, + { 0x49c0, 0x0000 }, + { 0x49c1, 0x0000 }, + { 0x49c2, 0x0000 }, + { 0x49c3, 0x0000 }, + { 0x49c4, 0x0000 }, + { 0x49c5, 0x0000 }, + { 0x49c6, 0x0000 }, + { 0x49c7, 0x0000 }, + { 0x49c8, 0x0000 }, + { 0x49c9, 0x0000 }, + { 0x49ca, 0x0000 }, + { 0x49cb, 0x0000 }, + { 0x49cc, 0x0000 }, + { 0x49cd, 0x0000 }, + { 0x49ce, 0x0000 }, + { 0x49cf, 0x0000 }, + { 0x49d0, 0x0000 }, + { 0x49d1, 0x0000 }, + { 0x49d2, 0x0000 }, + { 0x49d3, 0x0000 }, + { 0x49d4, 0x0000 }, + { 0x49d5, 0x0000 }, + { 0x49d6, 0x0000 }, + { 0x49d7, 0x0000 }, + { 0x49d8, 0x0000 }, + { 0x49d9, 0x0000 }, + { 0x49da, 0x0000 }, + { 0x49db, 0x0000 }, + { 0x49dc, 0x0000 }, + { 0x49dd, 0x0000 }, + { 0x49de, 0x0000 }, + { 0x49df, 0x0000 }, + { 0x49e0, 0x0000 }, + { 0x49e1, 0x0000 }, + { 0x49e2, 0x0000 }, + { 0x49e3, 0x0000 }, + { 0x49e4, 0x0000 }, + { 0x49e5, 0x0000 }, + { 0x49e6, 0x0000 }, + { 0x49e7, 0x0000 }, + { 0x49e8, 0x0000 }, + { 0x49e9, 0x0000 }, + { 0x49ea, 0x0000 }, + { 0x49eb, 0x0000 }, + { 0x49ec, 0x0000 }, + { 0x49ed, 0x0000 }, + { 0x49ee, 0x0000 }, + { 0x49ef, 0x0000 }, + { 0x49f0, 0x0000 }, + { 0x49f1, 0x0000 }, + { 0x49f2, 0x0000 }, + { 0x49f3, 0x0000 }, + { 0x49f4, 0x0000 }, + { 0x49f5, 0x0000 }, + { 0x49f6, 0x0000 }, + { 0x49f7, 0x0000 }, + { 0x49f8, 0x0000 }, + { 0x49f9, 0x0000 }, + { 0x49fa, 0x0000 }, + { 0x49fb, 0x0000 }, + { 0x49fc, 0x0000 }, + { 0x49fd, 0x0000 }, + { 0x49fe, 0x0000 }, + { 0x49ff, 0x0000 }, + { 0x4a00, 0x0000 }, + { 0x4a01, 0x0000 }, + { 0x4a02, 0x0000 }, + { 0x4a03, 0x0000 }, + { 0x4a04, 0x0000 }, + { 0x4a05, 0x0000 }, + { 0x4a06, 0x0000 }, + { 0x4a07, 0x0000 }, + { 0x4a08, 0x0000 }, + { 0x4a09, 0x0000 }, + { 0x4a0a, 0x0000 }, + { 0x4a0b, 0x0000 }, + { 0x4a0c, 0x0000 }, + { 0x4a0d, 0x0000 }, + { 0x4a0e, 0x0000 }, + { 0x4a0f, 0x0000 }, + { 0x4a10, 0x0000 }, + { 0x4a11, 0x0000 }, + { 0x4a12, 0x0000 }, + { 0x4a13, 0x0000 }, + { 0x4a14, 0x0000 }, + { 0x4a15, 0x0000 }, + { 0x4a16, 0x0000 }, + { 0x4a17, 0x0000 }, + { 0x4a18, 0x0000 }, + { 0x4a19, 0x0000 }, + { 0x4a1a, 0x0000 }, + { 0x4a1b, 0x0000 }, + { 0x4a1c, 0x0000 }, + { 0x4a1d, 0x0000 }, + { 0x4a1e, 0x0000 }, + { 0x4a1f, 0x0000 }, + { 0x4a20, 0x0000 }, + { 0x4a21, 0x0000 }, + { 0x4a22, 0x0000 }, + { 0x4a23, 0x0000 }, + { 0x4a24, 0x0000 }, + { 0x4a25, 0x0000 }, + { 0x4a26, 0x0000 }, + { 0x4a27, 0x0000 }, + { 0x4a28, 0x0000 }, + { 0x4a29, 0x0000 }, + { 0x4a2a, 0x0000 }, + { 0x4a2b, 0x0000 }, + { 0x4a2c, 0x0000 }, + { 0x4a2d, 0x0000 }, + { 0x4a2e, 0x0000 }, + { 0x4a2f, 0x0000 }, + { 0x4a30, 0x0000 }, + { 0x4a31, 0x0000 }, + { 0x4a32, 0x0000 }, + { 0x4a33, 0x0000 }, + { 0x4a34, 0x0000 }, + { 0x4a35, 0x0000 }, + { 0x4a36, 0x0000 }, + { 0x4a37, 0x0000 }, + { 0x4a38, 0x0000 }, + { 0x4a39, 0x0000 }, + { 0x4a3a, 0x0000 }, + { 0x4a3b, 0x0000 }, + { 0x4a3c, 0x0000 }, + { 0x4a3d, 0x0000 }, + { 0x4a3e, 0x0000 }, + { 0x4a3f, 0x0000 }, + { 0x4a40, 0x0000 }, + { 0x4a41, 0x0000 }, + { 0x4a42, 0x0000 }, + { 0x4a43, 0x0000 }, + { 0x4a44, 0x0000 }, + { 0x4a45, 0x0000 }, + { 0x4a46, 0x0000 }, + { 0x4a47, 0x0000 }, + { 0x4a48, 0x0000 }, + { 0x4a49, 0x0000 }, + { 0x4a4a, 0x0000 }, + { 0x4a4b, 0x0000 }, + { 0x4a4c, 0x0000 }, + { 0x4a4d, 0x0000 }, + { 0x4a4e, 0x0000 }, + { 0x4a4f, 0x0000 }, + { 0x4a50, 0x0000 }, + { 0x4a51, 0x0000 }, + { 0x4a52, 0x0000 }, + { 0x4a53, 0x0000 }, + { 0x4a54, 0x0000 }, + { 0x4a55, 0x0000 }, + { 0x4a56, 0x0000 }, + { 0x4a57, 0x0000 }, + { 0x4a58, 0x0000 }, + { 0x4a59, 0x0000 }, + { 0x4a5a, 0x0000 }, + { 0x4a5b, 0x0000 }, + { 0x4a5c, 0x0000 }, + { 0x4a5d, 0x0000 }, + { 0x4a5e, 0x0000 }, + { 0x4a5f, 0x0000 }, + { 0x4a60, 0x0000 }, + { 0x4a61, 0x0000 }, + { 0x4a62, 0x0000 }, + { 0x4a63, 0x0000 }, + { 0x4a64, 0x0000 }, + { 0x4a65, 0x0000 }, + { 0x4a66, 0x0000 }, + { 0x4a67, 0x0000 }, + { 0x4a68, 0x0000 }, + { 0x4a69, 0x0000 }, + { 0x4a6a, 0x0000 }, + { 0x4a6b, 0x0000 }, + { 0x4a6c, 0x0000 }, + { 0x4a6d, 0x0000 }, + { 0x4a6e, 0x0000 }, + { 0x4a6f, 0x0000 }, + { 0x4a70, 0x0000 }, + { 0x4a71, 0x0000 }, + { 0x4a72, 0x0000 }, + { 0x4a73, 0x0000 }, + { 0x4a74, 0x0000 }, + { 0x4a75, 0x0000 }, + { 0x4a76, 0x0000 }, + { 0x4a77, 0x0000 }, + { 0x4a78, 0x0000 }, + { 0x4a79, 0x0000 }, + { 0x4a7a, 0x0000 }, + { 0x4a7b, 0x0000 }, + { 0x4a7c, 0x0000 }, + { 0x4a7d, 0x0000 }, + { 0x4a7e, 0x0000 }, + { 0x4a7f, 0x0000 }, + { 0x4a80, 0x0000 }, + { 0x4a81, 0x0000 }, + { 0x4a82, 0x0000 }, + { 0x4a83, 0x0000 }, + { 0x4a84, 0x0000 }, + { 0x4a85, 0x0000 }, + { 0x4a86, 0x0000 }, + { 0x4a87, 0x0000 }, + { 0x4a88, 0x0000 }, + { 0x4a89, 0x0000 }, + { 0x4a8a, 0x0000 }, + { 0x4a8b, 0x0000 }, + { 0x4a8c, 0x0000 }, + { 0x4a8d, 0x0000 }, + { 0x4a8e, 0x0000 }, + { 0x4a8f, 0x0000 }, + { 0x4a90, 0x0000 }, + { 0x4a91, 0x0000 }, + { 0x4a92, 0x0000 }, + { 0x4a93, 0x0000 }, + { 0x4a94, 0x0000 }, + { 0x4a95, 0x0000 }, + { 0x4a96, 0x0000 }, + { 0x4a97, 0x0000 }, + { 0x4a98, 0x0000 }, + { 0x4a99, 0x0000 }, + { 0x4a9a, 0x0000 }, + { 0x4a9b, 0x0000 }, + { 0x4a9c, 0x0000 }, + { 0x4a9d, 0x0000 }, + { 0x4a9e, 0x0000 }, + { 0x4a9f, 0x0000 }, + { 0x4aa0, 0x0000 }, + { 0x4aa1, 0x0000 }, + { 0x4aa2, 0x0000 }, + { 0x4aa3, 0x0000 }, + { 0x4aa4, 0x0000 }, + { 0x4aa5, 0x0000 }, + { 0x4aa6, 0x0000 }, + { 0x4aa7, 0x0000 }, + { 0x4aa8, 0x0000 }, + { 0x4aa9, 0x0000 }, + { 0x4aaa, 0x0000 }, + { 0x4aab, 0x0000 }, + { 0x4aac, 0x0000 }, + { 0x4aad, 0x0000 }, + { 0x4aae, 0x0000 }, + { 0x4aaf, 0x0000 }, + { 0x4ab0, 0x0000 }, + { 0x4ab1, 0x0000 }, + { 0x4ab2, 0x0000 }, + { 0x4ab3, 0x0000 }, + { 0x4ab4, 0x0000 }, + { 0x4ab5, 0x0000 }, + { 0x4ab6, 0x0000 }, + { 0x4ab7, 0x0000 }, + { 0x4ab8, 0x0000 }, + { 0x4ab9, 0x0000 }, + { 0x4aba, 0x0000 }, + { 0x4abb, 0x0000 }, + { 0x4abc, 0x0000 }, + { 0x4abd, 0x0000 }, + { 0x4abe, 0x0000 }, + { 0x4abf, 0x0000 }, + { 0x4ac0, 0x0000 }, + { 0x4ac1, 0x0000 }, + { 0x4ac2, 0x0000 }, + { 0x4ac3, 0x0000 }, + { 0x4ac4, 0x0000 }, + { 0x4ac5, 0x0000 }, + { 0x4ac6, 0x0000 }, + { 0x4ac7, 0x0000 }, + { 0x4ac8, 0x0000 }, + { 0x4ac9, 0x0000 }, + { 0x4aca, 0x0000 }, + { 0x4acb, 0x0000 }, + { 0x4acc, 0x0000 }, + { 0x4acd, 0x0000 }, + { 0x4ace, 0x0000 }, + { 0x4acf, 0x0000 }, + { 0x4ad0, 0x0000 }, + { 0x4ad1, 0x0000 }, + { 0x4ad2, 0x0000 }, + { 0x4ad3, 0x0000 }, + { 0x4ad4, 0x0000 }, + { 0x4ad5, 0x0000 }, + { 0x4ad6, 0x0000 }, + { 0x4ad7, 0x0000 }, + { 0x4ad8, 0x0000 }, + { 0x4ad9, 0x0000 }, + { 0x4ada, 0x0000 }, + { 0x4adb, 0x0000 }, + { 0x4adc, 0x0000 }, + { 0x4add, 0x0000 }, + { 0x4ade, 0x0000 }, + { 0x4adf, 0x0000 }, + { 0x4ae0, 0x0000 }, + { 0x4ae1, 0x0000 }, + { 0x4ae2, 0x0000 }, + { 0x4ae3, 0x0000 }, + { 0x4ae4, 0x0000 }, + { 0x4ae5, 0x0000 }, + { 0x4ae6, 0x0000 }, + { 0x4ae7, 0x0000 }, + { 0x4ae8, 0x0000 }, + { 0x4ae9, 0x0000 }, + { 0x4aea, 0x0000 }, + { 0x4aeb, 0x0000 }, + { 0x4aec, 0x0000 }, + { 0x4aed, 0x0000 }, + { 0x4aee, 0x0000 }, + { 0x4aef, 0x0000 }, + { 0x4af0, 0x0000 }, + { 0x4af1, 0x0000 }, + { 0x4af2, 0x0000 }, + { 0x4af3, 0x0000 }, + { 0x4af4, 0x0000 }, + { 0x4af5, 0x0000 }, + { 0x4af6, 0x0000 }, + { 0x4af7, 0x0000 }, + { 0x4af8, 0x0000 }, + { 0x4af9, 0x0000 }, + { 0x4afa, 0x0000 }, + { 0x4afb, 0x0000 }, + { 0x4afc, 0x0000 }, + { 0x4afd, 0x0000 }, + { 0x4afe, 0x0000 }, + { 0x4aff, 0x0000 }, + { 0x4b00, 0x0000 }, + { 0x4b01, 0x0000 }, + { 0x4b02, 0x0000 }, + { 0x4b03, 0x0000 }, + { 0x4b04, 0x0000 }, + { 0x4b05, 0x0000 }, + { 0x4b06, 0x0000 }, + { 0x4b07, 0x0000 }, + { 0x4b08, 0x0000 }, + { 0x4b09, 0x0000 }, + { 0x4b0a, 0x0000 }, + { 0x4b0b, 0x0000 }, + { 0x4b0c, 0x0000 }, + { 0x4b0d, 0x0000 }, + { 0x4b0e, 0x0000 }, + { 0x4b0f, 0x0000 }, + { 0x4b10, 0x0000 }, + { 0x4b11, 0x0000 }, + { 0x4b12, 0x0000 }, + { 0x4b13, 0x0000 }, + { 0x4b14, 0x0000 }, + { 0x4b15, 0x0000 }, + { 0x4b16, 0x0000 }, + { 0x4b17, 0x0000 }, + { 0x4b18, 0x0000 }, + { 0x4b19, 0x0000 }, + { 0x4b1a, 0x0000 }, + { 0x4b1b, 0x0000 }, + { 0x4b1c, 0x0000 }, + { 0x4b1d, 0x0000 }, + { 0x4b1e, 0x0000 }, + { 0x4b1f, 0x0000 }, + { 0x4b20, 0x0000 }, + { 0x4b21, 0x0000 }, + { 0x4b22, 0x0000 }, + { 0x4b23, 0x0000 }, + { 0x4b24, 0x0000 }, + { 0x4b25, 0x0000 }, + { 0x4b26, 0x0000 }, + { 0x4b27, 0x0000 }, + { 0x4b28, 0x0000 }, + { 0x4b29, 0x0000 }, + { 0x4b2a, 0x0000 }, + { 0x4b2b, 0x0000 }, + { 0x4b2c, 0x0000 }, + { 0x4b2d, 0x0000 }, + { 0x4b2e, 0x0000 }, + { 0x4b2f, 0x0000 }, + { 0x4b30, 0x0000 }, + { 0x4b31, 0x0000 }, + { 0x4b32, 0x0000 }, + { 0x4b33, 0x0000 }, + { 0x4b34, 0x0000 }, + { 0x4b35, 0x0000 }, + { 0x4b36, 0x0000 }, + { 0x4b37, 0x0000 }, + { 0x4b38, 0x0000 }, + { 0x4b39, 0x0000 }, + { 0x4b3a, 0x0000 }, + { 0x4b3b, 0x0000 }, + { 0x4b3c, 0x0000 }, + { 0x4b3d, 0x0000 }, + { 0x4b3e, 0x0000 }, + { 0x4b3f, 0x0000 }, + { 0x4b40, 0x0000 }, + { 0x4b41, 0x0000 }, + { 0x4b42, 0x0000 }, + { 0x4b43, 0x0000 }, + { 0x4b44, 0x0000 }, + { 0x4b45, 0x0000 }, + { 0x4b46, 0x0000 }, + { 0x4b47, 0x0000 }, + { 0x4b48, 0x0000 }, + { 0x4b49, 0x0000 }, + { 0x4b4a, 0x0000 }, + { 0x4b4b, 0x0000 }, + { 0x4b4c, 0x0000 }, + { 0x4b4d, 0x0000 }, + { 0x4b4e, 0x0000 }, + { 0x4b4f, 0x0000 }, + { 0x4b50, 0x0000 }, + { 0x4b51, 0x0000 }, + { 0x4b52, 0x0000 }, + { 0x4b53, 0x0000 }, + { 0x4b54, 0x0000 }, + { 0x4b55, 0x0000 }, + { 0x4b56, 0x0000 }, + { 0x4b57, 0x0000 }, + { 0x4b58, 0x0000 }, + { 0x4b59, 0x0000 }, + { 0x4b5a, 0x0000 }, + { 0x4b5b, 0x0000 }, + { 0x4b5c, 0x0000 }, + { 0x4b5d, 0x0000 }, + { 0x4b5e, 0x0000 }, + { 0x4b5f, 0x0000 }, + { 0x4b60, 0x0000 }, + { 0x4b61, 0x0000 }, + { 0x4b62, 0x0000 }, + { 0x4b63, 0x0000 }, + { 0x4b64, 0x0000 }, + { 0x4b65, 0x0000 }, + { 0x4b66, 0x0000 }, + { 0x4b67, 0x0000 }, + { 0x4b68, 0x0000 }, + { 0x4b69, 0x0000 }, + { 0x4b6a, 0x0000 }, + { 0x4b6b, 0x0000 }, + { 0x4b6c, 0x0000 }, + { 0x4b6d, 0x0000 }, + { 0x4b6e, 0x0000 }, + { 0x4b6f, 0x0000 }, + { 0x4b70, 0x0000 }, + { 0x4b71, 0x0000 }, + { 0x4b72, 0x0000 }, + { 0x4b73, 0x0000 }, + { 0x4b74, 0x0000 }, + { 0x4b75, 0x0000 }, + { 0x4b76, 0x0000 }, + { 0x4b77, 0x0000 }, + { 0x4b78, 0x0000 }, + { 0x4b79, 0x0000 }, + { 0x4b7a, 0x0000 }, + { 0x4b7b, 0x0000 }, + { 0x4b7c, 0x0000 }, + { 0x4b7d, 0x0000 }, + { 0x4b7e, 0x0000 }, + { 0x4b7f, 0x0000 }, + { 0x4b80, 0x0000 }, + { 0x4b81, 0x0000 }, + { 0x4b82, 0x0000 }, + { 0x4b83, 0x0000 }, + { 0x4b84, 0x0000 }, + { 0x4b85, 0x0000 }, + { 0x4b86, 0x0000 }, + { 0x4b87, 0x0000 }, + { 0x4b88, 0x0000 }, + { 0x4b89, 0x0000 }, + { 0x4b8a, 0x0000 }, + { 0x4b8b, 0x0000 }, + { 0x4b8c, 0x0000 }, + { 0x4b8d, 0x0000 }, + { 0x4b8e, 0x0000 }, + { 0x4b8f, 0x0000 }, + { 0x4b90, 0x0000 }, + { 0x4b91, 0x0000 }, + { 0x4b92, 0x0000 }, + { 0x4b93, 0x0000 }, + { 0x4b94, 0x0000 }, + { 0x4b95, 0x0000 }, + { 0x4b96, 0x0000 }, + { 0x4b97, 0x0000 }, + { 0x4b98, 0x0000 }, + { 0x4b99, 0x0000 }, + { 0x4b9a, 0x0000 }, + { 0x4b9b, 0x0000 }, + { 0x4b9c, 0x0000 }, + { 0x4b9d, 0x0000 }, + { 0x4b9e, 0x0000 }, + { 0x4b9f, 0x0000 }, + { 0x4ba0, 0x0000 }, + { 0x4ba1, 0x0000 }, + { 0x4ba2, 0x0000 }, + { 0x4ba3, 0x0000 }, + { 0x4ba4, 0x0000 }, + { 0x4ba5, 0x0000 }, + { 0x4ba6, 0x0000 }, + { 0x4ba7, 0x0000 }, + { 0x4ba8, 0x0000 }, + { 0x4ba9, 0x0000 }, + { 0x4baa, 0x0000 }, + { 0x4bab, 0x0000 }, + { 0x4bac, 0x0000 }, + { 0x4bad, 0x0000 }, + { 0x4bae, 0x0000 }, + { 0x4baf, 0x0000 }, + { 0x4bb0, 0x0000 }, + { 0x4bb1, 0x0000 }, + { 0x4bb2, 0x0000 }, + { 0x4bb3, 0x0000 }, + { 0x4bb4, 0x0000 }, + { 0x4bb5, 0x0000 }, + { 0x4bb6, 0x0000 }, + { 0x4bb7, 0x0000 }, + { 0x4bb8, 0x0000 }, + { 0x4bb9, 0x0000 }, + { 0x4bba, 0x0000 }, + { 0x4bbb, 0x0000 }, + { 0x4bbc, 0x0000 }, + { 0x4bbd, 0x0000 }, + { 0x4bbe, 0x0000 }, + { 0x4bbf, 0x0000 }, + { 0x4bc0, 0x0000 }, + { 0x4bc1, 0x0000 }, + { 0x4bc2, 0x0000 }, + { 0x4bc3, 0x0000 }, + { 0x4bc4, 0x0000 }, + { 0x4bc5, 0x0000 }, + { 0x4bc6, 0x0000 }, + { 0x4bc7, 0x0000 }, + { 0x4bc8, 0x0000 }, + { 0x4bc9, 0x0000 }, + { 0x4bca, 0x0000 }, + { 0x4bcb, 0x0000 }, + { 0x4bcc, 0x0000 }, + { 0x4bcd, 0x0000 }, + { 0x4bce, 0x0000 }, + { 0x4bcf, 0x0000 }, + { 0x4bd0, 0x0000 }, + { 0x4bd1, 0x0000 }, + { 0x4bd2, 0x0000 }, + { 0x4bd3, 0x0000 }, + { 0x4bd4, 0x0000 }, + { 0x4bd5, 0x0000 }, + { 0x4bd6, 0x0000 }, + { 0x4bd7, 0x0000 }, + { 0x4bd8, 0x0000 }, + { 0x4bd9, 0x0000 }, + { 0x4bda, 0x0000 }, + { 0x4bdb, 0x0000 }, + { 0x4bdc, 0x0000 }, + { 0x4bdd, 0x0000 }, + { 0x4bde, 0x0000 }, + { 0x4bdf, 0x0000 }, + { 0x4be0, 0x0000 }, + { 0x4be1, 0x0000 }, + { 0x4be2, 0x0000 }, + { 0x4be3, 0x0000 }, + { 0x4be4, 0x0000 }, + { 0x4be5, 0x0000 }, + { 0x4be6, 0x0000 }, + { 0x4be7, 0x0000 }, + { 0x4be8, 0x0000 }, + { 0x4be9, 0x0000 }, + { 0x4bea, 0x0000 }, + { 0x4beb, 0x0000 }, + { 0x4bec, 0x0000 }, + { 0x4bed, 0x0000 }, + { 0x4bee, 0x0000 }, + { 0x4bef, 0x0000 }, + { 0x4bf0, 0x0000 }, + { 0x4bf1, 0x0000 }, + { 0x4bf2, 0x0000 }, + { 0x4bf3, 0x0000 }, + { 0x4bf4, 0x0000 }, + { 0x4bf5, 0x0000 }, + { 0x4bf6, 0x0000 }, + { 0x4bf7, 0x0000 }, + { 0x4bf8, 0x0000 }, + { 0x4bf9, 0x0000 }, + { 0x4bfa, 0x0000 }, + { 0x4bfb, 0x0000 }, + { 0x4bfc, 0x0000 }, + { 0x4bfd, 0x0000 }, + { 0x4bfe, 0x0000 }, + { 0x4bff, 0x0000 }, + { 0x4c00, 0x0000 }, + { 0x4c01, 0x0000 }, + { 0x4c02, 0x0000 }, + { 0x4c03, 0x0000 }, + { 0x4c04, 0x0000 }, + { 0x4c05, 0x0000 }, + { 0x4c06, 0x0000 }, + { 0x4c07, 0x0000 }, + { 0x4c08, 0x0000 }, + { 0x4c09, 0x0000 }, + { 0x4c0a, 0x0000 }, + { 0x4c0b, 0x0000 }, + { 0x4c0c, 0x0000 }, + { 0x4c0d, 0x0000 }, + { 0x4c0e, 0x0000 }, + { 0x4c0f, 0x0000 }, + { 0x4c10, 0x0000 }, + { 0x4c11, 0x0000 }, + { 0x4c12, 0x0000 }, + { 0x4c13, 0x0000 }, + { 0x4c14, 0x0000 }, + { 0x4c15, 0x0000 }, + { 0x4c16, 0x0000 }, + { 0x4c17, 0x0000 }, + { 0x4c18, 0x0000 }, + { 0x4c19, 0x0000 }, + { 0x4c1a, 0x0000 }, + { 0x4c1b, 0x0000 }, + { 0x4c1c, 0x0000 }, + { 0x4c1d, 0x0000 }, + { 0x4c1e, 0x0000 }, + { 0x4c1f, 0x0000 }, + { 0x4c20, 0x0000 }, + { 0x4c21, 0x0000 }, + { 0x4c22, 0x0000 }, + { 0x4c23, 0x0000 }, + { 0x4c24, 0x0000 }, + { 0x4c25, 0x0000 }, + { 0x4c26, 0x0000 }, + { 0x4c27, 0x0000 }, + { 0x4c28, 0x0000 }, + { 0x4c29, 0x0000 }, + { 0x4c2a, 0x0000 }, + { 0x4c2b, 0x0000 }, + { 0x4c2c, 0x0000 }, + { 0x4c2d, 0x0000 }, + { 0x4c2e, 0x0000 }, + { 0x4c2f, 0x0000 }, + { 0x4c30, 0x0000 }, + { 0x4c31, 0x0000 }, + { 0x4c32, 0x0000 }, + { 0x4c33, 0x0000 }, + { 0x4c34, 0x0000 }, + { 0x4c35, 0x0000 }, + { 0x4c36, 0x0000 }, + { 0x4c37, 0x0000 }, + { 0x4c38, 0x0000 }, + { 0x4c39, 0x0000 }, + { 0x4c3a, 0x0000 }, + { 0x4c3b, 0x0000 }, + { 0x4c3c, 0x0000 }, + { 0x4c3d, 0x0000 }, + { 0x4c3e, 0x0000 }, + { 0x4c3f, 0x0000 }, + { 0x4c40, 0x0000 }, + { 0x4c41, 0x0000 }, + { 0x4c42, 0x0000 }, + { 0x4c43, 0x0000 }, + { 0x4c44, 0x0000 }, + { 0x4c45, 0x0000 }, + { 0x4c46, 0x0000 }, + { 0x4c47, 0x0000 }, + { 0x4c48, 0x0000 }, + { 0x4c49, 0x0000 }, + { 0x4c4a, 0x0000 }, + { 0x4c4b, 0x0000 }, + { 0x4c4c, 0x0000 }, + { 0x4c4d, 0x0000 }, + { 0x4c4e, 0x0000 }, + { 0x4c4f, 0x0000 }, + { 0x4c50, 0x0000 }, + { 0x4c51, 0x0000 }, + { 0x4c52, 0x0000 }, + { 0x4c53, 0x0000 }, + { 0x4c54, 0x0000 }, + { 0x4c55, 0x0000 }, + { 0x4c56, 0x0000 }, + { 0x4c57, 0x0000 }, + { 0x4c58, 0x0000 }, + { 0x4c59, 0x0000 }, + { 0x4c5a, 0x0000 }, + { 0x4c5b, 0x0000 }, + { 0x4c5c, 0x0000 }, + { 0x4c5d, 0x0000 }, + { 0x4c5e, 0x0000 }, + { 0x4c5f, 0x0000 }, + { 0x4c60, 0x0000 }, + { 0x4c61, 0x0000 }, + { 0x4c62, 0x0000 }, + { 0x4c63, 0x0000 }, + { 0x4c64, 0x0000 }, + { 0x4c65, 0x0000 }, + { 0x4c66, 0x0000 }, + { 0x4c67, 0x0000 }, + { 0x4c68, 0x0000 }, + { 0x4c69, 0x0000 }, + { 0x4c6a, 0x0000 }, + { 0x4c6b, 0x0000 }, + { 0x4c6c, 0x0000 }, + { 0x4c6d, 0x0000 }, + { 0x4c6e, 0x0000 }, + { 0x4c6f, 0x0000 }, + { 0x4c70, 0x0000 }, + { 0x4c71, 0x0000 }, + { 0x4c72, 0x0000 }, + { 0x4c73, 0x0000 }, + { 0x4c74, 0x0000 }, + { 0x4c75, 0x0000 }, + { 0x4c76, 0x0000 }, + { 0x4c77, 0x0000 }, + { 0x4c78, 0x0000 }, + { 0x4c79, 0x0000 }, + { 0x4c7a, 0x0000 }, + { 0x4c7b, 0x0000 }, + { 0x4c7c, 0x0000 }, + { 0x4c7d, 0x0000 }, + { 0x4c7e, 0x0000 }, + { 0x4c7f, 0x0000 }, + { 0x4c80, 0x0000 }, + { 0x4c81, 0x0000 }, + { 0x4c82, 0x0000 }, + { 0x4c83, 0x0000 }, + { 0x4c84, 0x0000 }, + { 0x4c85, 0x0000 }, + { 0x4c86, 0x0000 }, + { 0x4c87, 0x0000 }, + { 0x4c88, 0x0000 }, + { 0x4c89, 0x0000 }, + { 0x4c8a, 0x0000 }, + { 0x4c8b, 0x0000 }, + { 0x4c8c, 0x0000 }, + { 0x4c8d, 0x0000 }, + { 0x4c8e, 0x0000 }, + { 0x4c8f, 0x0000 }, + { 0x4c90, 0x0000 }, + { 0x4c91, 0x0000 }, + { 0x4c92, 0x0000 }, + { 0x4c93, 0x0000 }, + { 0x4c94, 0x0000 }, + { 0x4c95, 0x0000 }, + { 0x4c96, 0x0000 }, + { 0x4c97, 0x0000 }, + { 0x4c98, 0x0000 }, + { 0x4c99, 0x0000 }, + { 0x4c9a, 0x0000 }, + { 0x4c9b, 0x0000 }, + { 0x4c9c, 0x0000 }, + { 0x4c9d, 0x0000 }, + { 0x4c9e, 0x0000 }, + { 0x4c9f, 0x0000 }, + { 0x4ca0, 0x0000 }, + { 0x4ca1, 0x0000 }, + { 0x4ca2, 0x0000 }, + { 0x4ca3, 0x0000 }, + { 0x4ca4, 0x0000 }, + { 0x4ca5, 0x0000 }, + { 0x4ca6, 0x0000 }, + { 0x4ca7, 0x0000 }, + { 0x4ca8, 0x0000 }, + { 0x4ca9, 0x0000 }, + { 0x4caa, 0x0000 }, + { 0x4cab, 0x0000 }, + { 0x4cac, 0x0000 }, + { 0x4cad, 0x0000 }, + { 0x4cae, 0x0000 }, + { 0x4caf, 0x0000 }, + { 0x4cb0, 0x0000 }, + { 0x4cb1, 0x0000 }, + { 0x4cb2, 0x0000 }, + { 0x4cb3, 0x0000 }, + { 0x4cb4, 0x0000 }, + { 0x4cb5, 0x0000 }, + { 0x4cb6, 0x0000 }, + { 0x4cb7, 0x0000 }, + { 0x4cb8, 0x0000 }, + { 0x4cb9, 0x0000 }, + { 0x4cba, 0x0000 }, + { 0x4cbb, 0x0000 }, + { 0x4cbc, 0x0000 }, + { 0x4cbd, 0x0000 }, + { 0x4cbe, 0x0000 }, + { 0x4cbf, 0x0000 }, + { 0x4cc0, 0x0000 }, + { 0x4cc1, 0x0000 }, + { 0x4cc2, 0x0000 }, + { 0x4cc3, 0x0000 }, + { 0x4cc4, 0x0000 }, + { 0x4cc5, 0x0000 }, + { 0x4cc6, 0x0000 }, + { 0x4cc7, 0x0000 }, + { 0x4cc8, 0x0000 }, + { 0x4cc9, 0x0000 }, + { 0x4cca, 0x0000 }, + { 0x4ccb, 0x0000 }, + { 0x4ccc, 0x0000 }, + { 0x4ccd, 0x0000 }, + { 0x4cce, 0x0000 }, + { 0x4ccf, 0x0000 }, + { 0x4cd0, 0x0000 }, + { 0x4cd1, 0x0000 }, + { 0x4cd2, 0x0000 }, + { 0x4cd3, 0x0000 }, + { 0x4cd4, 0x0000 }, + { 0x4cd5, 0x0000 }, + { 0x4cd6, 0x0000 }, + { 0x4cd7, 0x0000 }, + { 0x4cd8, 0x0000 }, + { 0x4cd9, 0x0000 }, + { 0x4cda, 0x0000 }, + { 0x4cdb, 0x0000 }, + { 0x4cdc, 0x0000 }, + { 0x4cdd, 0x0000 }, + { 0x4cde, 0x0000 }, + { 0x4cdf, 0x0000 }, + { 0x4ce0, 0x0000 }, + { 0x4ce1, 0x0000 }, + { 0x4ce2, 0x0000 }, + { 0x4ce3, 0x0000 }, + { 0x4ce4, 0x0000 }, + { 0x4ce5, 0x0000 }, + { 0x4ce6, 0x0000 }, + { 0x4ce7, 0x0000 }, + { 0x4ce8, 0x0000 }, + { 0x4ce9, 0x0000 }, + { 0x4cea, 0x0000 }, + { 0x4ceb, 0x0000 }, + { 0x4cec, 0x0000 }, + { 0x4ced, 0x0000 }, + { 0x4cee, 0x0000 }, + { 0x4cef, 0x0000 }, + { 0x4cf0, 0x0000 }, + { 0x4cf1, 0x0000 }, + { 0x4cf2, 0x0000 }, + { 0x4cf3, 0x0000 }, + { 0x4cf4, 0x0000 }, + { 0x4cf5, 0x0000 }, + { 0x4cf6, 0x0000 }, + { 0x4cf7, 0x0000 }, + { 0x4cf8, 0x0000 }, + { 0x4cf9, 0x0000 }, + { 0x4cfa, 0x0000 }, + { 0x4cfb, 0x0000 }, + { 0x4cfc, 0x0000 }, + { 0x4cfd, 0x0000 }, + { 0x4cfe, 0x0000 }, + { 0x4cff, 0x0000 }, + { 0x4d00, 0x0000 }, + { 0x4d01, 0x0000 }, + { 0x4d02, 0x0000 }, + { 0x4d03, 0x0000 }, + { 0x4d04, 0x0000 }, + { 0x4d05, 0x0000 }, + { 0x4d06, 0x0000 }, + { 0x4d07, 0x0000 }, + { 0x4d08, 0x0000 }, + { 0x4d09, 0x0000 }, + { 0x4d0a, 0x0000 }, + { 0x4d0b, 0x0000 }, + { 0x4d0c, 0x0000 }, + { 0x4d0d, 0x0000 }, + { 0x4d0e, 0x0000 }, + { 0x4d0f, 0x0000 }, + { 0x4d10, 0x0000 }, + { 0x4d11, 0x0000 }, + { 0x4d12, 0x0000 }, + { 0x4d13, 0x0000 }, + { 0x4d14, 0x0000 }, + { 0x4d15, 0x0000 }, + { 0x4d16, 0x0000 }, + { 0x4d17, 0x0000 }, + { 0x4d18, 0x0000 }, + { 0x4d19, 0x0000 }, + { 0x4d1a, 0x0000 }, + { 0x4d1b, 0x0000 }, + { 0x4d1c, 0x0000 }, + { 0x4d1d, 0x0000 }, + { 0x4d1e, 0x0000 }, + { 0x4d1f, 0x0000 }, + { 0x4d20, 0x0000 }, + { 0x4d21, 0x0000 }, + { 0x4d22, 0x0000 }, + { 0x4d23, 0x0000 }, + { 0x4d24, 0x0000 }, + { 0x4d25, 0x0000 }, + { 0x4d26, 0x0000 }, + { 0x4d27, 0x0000 }, + { 0x4d28, 0x0000 }, + { 0x4d29, 0x0000 }, + { 0x4d2a, 0x0000 }, + { 0x4d2b, 0x0000 }, + { 0x4d2c, 0x0000 }, + { 0x4d2d, 0x0000 }, + { 0x4d2e, 0x0000 }, + { 0x4d2f, 0x0000 }, + { 0x4d30, 0x0000 }, + { 0x4d31, 0x0000 }, + { 0x4d32, 0x0000 }, + { 0x4d33, 0x0000 }, + { 0x4d34, 0x0000 }, + { 0x4d35, 0x0000 }, + { 0x4d36, 0x0000 }, + { 0x4d37, 0x0000 }, + { 0x4d38, 0x0000 }, + { 0x4d39, 0x0000 }, + { 0x4d3a, 0x0000 }, + { 0x4d3b, 0x0000 }, + { 0x4d3c, 0x0000 }, + { 0x4d3d, 0x0000 }, + { 0x4d3e, 0x0000 }, + { 0x4d3f, 0x0000 }, + { 0x4d40, 0x0000 }, + { 0x4d41, 0x0000 }, + { 0x4d42, 0x0000 }, + { 0x4d43, 0x0000 }, + { 0x4d44, 0x0000 }, + { 0x4d45, 0x0000 }, + { 0x4d46, 0x0000 }, + { 0x4d47, 0x0000 }, + { 0x4d48, 0x0000 }, + { 0x4d49, 0x0000 }, + { 0x4d4a, 0x0000 }, + { 0x4d4b, 0x0000 }, + { 0x4d4c, 0x0000 }, + { 0x4d4d, 0x0000 }, + { 0x4d4e, 0x0000 }, + { 0x4d4f, 0x0000 }, + { 0x4d50, 0x0000 }, + { 0x4d51, 0x0000 }, + { 0x4d52, 0x0000 }, + { 0x4d53, 0x0000 }, + { 0x4d54, 0x0000 }, + { 0x4d55, 0x0000 }, + { 0x4d56, 0x0000 }, + { 0x4d57, 0x0000 }, + { 0x4d58, 0x0000 }, + { 0x4d59, 0x0000 }, + { 0x4d5a, 0x0000 }, + { 0x4d5b, 0x0000 }, + { 0x4d5c, 0x0000 }, + { 0x4d5d, 0x0000 }, + { 0x4d5e, 0x0000 }, + { 0x4d5f, 0x0000 }, + { 0x4d60, 0x0000 }, + { 0x4d61, 0x0000 }, + { 0x4d62, 0x0000 }, + { 0x4d63, 0x0000 }, + { 0x4d64, 0x0000 }, + { 0x4d65, 0x0000 }, + { 0x4d66, 0x0000 }, + { 0x4d67, 0x0000 }, + { 0x4d68, 0x0000 }, + { 0x4d69, 0x0000 }, + { 0x4d6a, 0x0000 }, + { 0x4d6b, 0x0000 }, + { 0x4d6c, 0x0000 }, + { 0x4d6d, 0x0000 }, + { 0x4d6e, 0x0000 }, + { 0x4d6f, 0x0000 }, + { 0x4d70, 0x0000 }, + { 0x4d71, 0x0000 }, + { 0x4d72, 0x0000 }, + { 0x4d73, 0x0000 }, + { 0x4d74, 0x0000 }, + { 0x4d75, 0x0000 }, + { 0x4d76, 0x0000 }, + { 0x4d77, 0x0000 }, + { 0x4d78, 0x0000 }, + { 0x4d79, 0x0000 }, + { 0x4d7a, 0x0000 }, + { 0x4d7b, 0x0000 }, + { 0x4d7c, 0x0000 }, + { 0x4d7d, 0x0000 }, + { 0x4d7e, 0x0000 }, + { 0x4d7f, 0x0000 }, + { 0x4d80, 0x0000 }, + { 0x4d81, 0x0000 }, + { 0x4d82, 0x0000 }, + { 0x4d83, 0x0000 }, + { 0x4d84, 0x0000 }, + { 0x4d85, 0x0000 }, + { 0x4d86, 0x0000 }, + { 0x4d87, 0x0000 }, + { 0x4d88, 0x0000 }, + { 0x4d89, 0x0000 }, + { 0x4d8a, 0x0000 }, + { 0x4d8b, 0x0000 }, + { 0x4d8c, 0x0000 }, + { 0x4d8d, 0x0000 }, + { 0x4d8e, 0x0000 }, + { 0x4d8f, 0x0000 }, + { 0x4d90, 0x0000 }, + { 0x4d91, 0x0000 }, + { 0x4d92, 0x0000 }, + { 0x4d93, 0x0000 }, + { 0x4d94, 0x0000 }, + { 0x4d95, 0x0000 }, + { 0x4d96, 0x0000 }, + { 0x4d97, 0x0000 }, + { 0x4d98, 0x0000 }, + { 0x4d99, 0x0000 }, + { 0x4d9a, 0x0000 }, + { 0x4d9b, 0x0000 }, + { 0x4d9c, 0x0000 }, + { 0x4d9d, 0x0000 }, + { 0x4d9e, 0x0000 }, + { 0x4d9f, 0x0000 }, + { 0x4da0, 0x0000 }, + { 0x4da1, 0x0000 }, + { 0x4da2, 0x0000 }, + { 0x4da3, 0x0000 }, + { 0x4da4, 0x0000 }, + { 0x4da5, 0x0000 }, + { 0x4da6, 0x0000 }, + { 0x4da7, 0x0000 }, + { 0x4da8, 0x0000 }, + { 0x4da9, 0x0000 }, + { 0x4daa, 0x0000 }, + { 0x4dab, 0x0000 }, + { 0x4dac, 0x0000 }, + { 0x4dad, 0x0000 }, + { 0x4dae, 0x0000 }, + { 0x4daf, 0x0000 }, + { 0x4db0, 0x0000 }, + { 0x4db1, 0x0000 }, + { 0x4db2, 0x0000 }, + { 0x4db3, 0x0000 }, + { 0x4db4, 0x0000 }, + { 0x4db5, 0x0000 }, + { 0x4db6, 0x0000 }, + { 0x4db7, 0x0000 }, + { 0x4db8, 0x0000 }, + { 0x4db9, 0x0000 }, + { 0x4dba, 0x0000 }, + { 0x4dbb, 0x0000 }, + { 0x4dbc, 0x0000 }, + { 0x4dbd, 0x0000 }, + { 0x4dbe, 0x0000 }, + { 0x4dbf, 0x0000 }, + { 0x4dc0, 0x0000 }, + { 0x4dc1, 0x0000 }, + { 0x4dc2, 0x0000 }, + { 0x4dc3, 0x0000 }, + { 0x4dc4, 0x0000 }, + { 0x4dc5, 0x0000 }, + { 0x4dc6, 0x0000 }, + { 0x4dc7, 0x0000 }, + { 0x4dc8, 0x0000 }, + { 0x4dc9, 0x0000 }, + { 0x4dca, 0x0000 }, + { 0x4dcb, 0x0000 }, + { 0x4dcc, 0x0000 }, + { 0x4dcd, 0x0000 }, + { 0x4dce, 0x0000 }, + { 0x4dcf, 0x0000 }, + { 0x4dd0, 0x0000 }, + { 0x4dd1, 0x0000 }, + { 0x4dd2, 0x0000 }, + { 0x4dd3, 0x0000 }, + { 0x4dd4, 0x0000 }, + { 0x4dd5, 0x0000 }, + { 0x4dd6, 0x0000 }, + { 0x4dd7, 0x0000 }, + { 0x4dd8, 0x0000 }, + { 0x4dd9, 0x0000 }, + { 0x4dda, 0x0000 }, + { 0x4ddb, 0x0000 }, + { 0x4ddc, 0x0000 }, + { 0x4ddd, 0x0000 }, + { 0x4dde, 0x0000 }, + { 0x4ddf, 0x0000 }, + { 0x4de0, 0x0000 }, + { 0x4de1, 0x0000 }, + { 0x4de2, 0x0000 }, + { 0x4de3, 0x0000 }, + { 0x4de4, 0x0000 }, + { 0x4de5, 0x0000 }, + { 0x4de6, 0x0000 }, + { 0x4de7, 0x0000 }, + { 0x4de8, 0x0000 }, + { 0x4de9, 0x0000 }, + { 0x4dea, 0x0000 }, + { 0x4deb, 0x0000 }, + { 0x4dec, 0x0000 }, + { 0x4ded, 0x0000 }, + { 0x4dee, 0x0000 }, + { 0x4def, 0x0000 }, + { 0x4df0, 0x0000 }, + { 0x4df1, 0x0000 }, + { 0x4df2, 0x0000 }, + { 0x4df3, 0x0000 }, + { 0x4df4, 0x0000 }, + { 0x4df5, 0x0000 }, + { 0x4df6, 0x0000 }, + { 0x4df7, 0x0000 }, + { 0x4df8, 0x0000 }, + { 0x4df9, 0x0000 }, + { 0x4dfa, 0x0000 }, + { 0x4dfb, 0x0000 }, + { 0x4dfc, 0x0000 }, + { 0x4dfd, 0x0000 }, + { 0x4dfe, 0x0000 }, + { 0x4dff, 0x0000 }, + { 0x4e00, 0x0000 }, + { 0x4e01, 0x0000 }, + { 0x4e02, 0x0000 }, + { 0x4e03, 0x0000 }, + { 0x4e04, 0x0000 }, + { 0x4e05, 0x0000 }, + { 0x4e06, 0x0000 }, + { 0x4e07, 0x0000 }, + { 0x4e08, 0x0000 }, + { 0x4e09, 0x0000 }, + { 0x4e0a, 0x0000 }, + { 0x4e0b, 0x0000 }, + { 0x4e0c, 0x0000 }, + { 0x4e0d, 0x0000 }, + { 0x4e0e, 0x0000 }, + { 0x4e0f, 0x0000 }, + { 0x4e10, 0x0000 }, + { 0x4e11, 0x0000 }, + { 0x4e12, 0x0000 }, + { 0x4e13, 0x0000 }, + { 0x4e14, 0x0000 }, + { 0x4e15, 0x0000 }, + { 0x4e16, 0x0000 }, + { 0x4e17, 0x0000 }, + { 0x4e18, 0x0000 }, + { 0x4e19, 0x0000 }, + { 0x4e1a, 0x0000 }, + { 0x4e1b, 0x0000 }, + { 0x4e1c, 0x0000 }, + { 0x4e1d, 0x0000 }, + { 0x4e1e, 0x0000 }, + { 0x4e1f, 0x0000 }, + { 0x4e20, 0x0000 }, + { 0x4e21, 0x0000 }, + { 0x4e22, 0x0000 }, + { 0x4e23, 0x0000 }, + { 0x4e24, 0x0000 }, + { 0x4e25, 0x0000 }, + { 0x4e26, 0x0000 }, + { 0x4e27, 0x0000 }, + { 0x4e28, 0x0000 }, + { 0x4e29, 0x0000 }, + { 0x4e2a, 0x0000 }, + { 0x4e2b, 0x0000 }, + { 0x4e2c, 0x0000 }, + { 0x4e2d, 0x0000 }, + { 0x4e2e, 0x0000 }, + { 0x4e2f, 0x0000 }, + { 0x4e30, 0x0000 }, + { 0x4e31, 0x0000 }, + { 0x4e32, 0x0000 }, + { 0x4e33, 0x0000 }, + { 0x4e34, 0x0000 }, + { 0x4e35, 0x0000 }, + { 0x4e36, 0x0000 }, + { 0x4e37, 0x0000 }, + { 0x4e38, 0x0000 }, + { 0x4e39, 0x0000 }, + { 0x4e3a, 0x0000 }, + { 0x4e3b, 0x0000 }, + { 0x4e3c, 0x0000 }, + { 0x4e3d, 0x0000 }, + { 0x4e3e, 0x0000 }, + { 0x4e3f, 0x0000 }, + { 0x4e40, 0x0000 }, + { 0x4e41, 0x0000 }, + { 0x4e42, 0x0000 }, + { 0x4e43, 0x0000 }, + { 0x4e44, 0x0000 }, + { 0x4e45, 0x0000 }, + { 0x4e46, 0x0000 }, + { 0x4e47, 0x0000 }, + { 0x4e48, 0x0000 }, + { 0x4e49, 0x0000 }, + { 0x4e4a, 0x0000 }, + { 0x4e4b, 0x0000 }, + { 0x4e4c, 0x0000 }, + { 0x4e4d, 0x0000 }, + { 0x4e4e, 0x0000 }, + { 0x4e4f, 0x0000 }, + { 0x4e50, 0x0000 }, + { 0x4e51, 0x0000 }, + { 0x4e52, 0x0000 }, + { 0x4e53, 0x0000 }, + { 0x4e54, 0x0000 }, + { 0x4e55, 0x0000 }, + { 0x4e56, 0x0000 }, + { 0x4e57, 0x0000 }, + { 0x4e58, 0x0000 }, + { 0x4e59, 0x0000 }, + { 0x4e5a, 0x0000 }, + { 0x4e5b, 0x0000 }, + { 0x4e5c, 0x0000 }, + { 0x4e5d, 0x0000 }, + { 0x4e5e, 0x0000 }, + { 0x4e5f, 0x0000 }, + { 0x4e60, 0x0000 }, + { 0x4e61, 0x0000 }, + { 0x4e62, 0x0000 }, + { 0x4e63, 0x0000 }, + { 0x4e64, 0x0000 }, + { 0x4e65, 0x0000 }, + { 0x4e66, 0x0000 }, + { 0x4e67, 0x0000 }, + { 0x4e68, 0x0000 }, + { 0x4e69, 0x0000 }, + { 0x4e6a, 0x0000 }, + { 0x4e6b, 0x0000 }, + { 0x4e6c, 0x0000 }, + { 0x4e6d, 0x0000 }, + { 0x4e6e, 0x0000 }, + { 0x4e6f, 0x0000 }, + { 0x4e70, 0x0000 }, + { 0x4e71, 0x0000 }, + { 0x4e72, 0x0000 }, + { 0x4e73, 0x0000 }, + { 0x4e74, 0x0000 }, + { 0x4e75, 0x0000 }, + { 0x4e76, 0x0000 }, + { 0x4e77, 0x0000 }, + { 0x4e78, 0x0000 }, + { 0x4e79, 0x0000 }, + { 0x4e7a, 0x0000 }, + { 0x4e7b, 0x0000 }, + { 0x4e7c, 0x0000 }, + { 0x4e7d, 0x0000 }, + { 0x4e7e, 0x0000 }, + { 0x4e7f, 0x0000 }, + { 0x4e80, 0x0000 }, + { 0x4e81, 0x0000 }, + { 0x4e82, 0x0000 }, + { 0x4e83, 0x0000 }, + { 0x4e84, 0x0000 }, + { 0x4e85, 0x0000 }, + { 0x4e86, 0x0000 }, + { 0x4e87, 0x0000 }, + { 0x4e88, 0x0000 }, + { 0x4e89, 0x0000 }, + { 0x4e8a, 0x0000 }, + { 0x4e8b, 0x0000 }, + { 0x4e8c, 0x0000 }, + { 0x4e8d, 0x0000 }, + { 0x4e8e, 0x0000 }, + { 0x4e8f, 0x0000 }, + { 0x4e90, 0x0000 }, + { 0x4e91, 0x0000 }, + { 0x4e92, 0x0000 }, + { 0x4e93, 0x0000 }, + { 0x4e94, 0x0000 }, + { 0x4e95, 0x0000 }, + { 0x4e96, 0x0000 }, + { 0x4e97, 0x0000 }, + { 0x4e98, 0x0000 }, + { 0x4e99, 0x0000 }, + { 0x4e9a, 0x0000 }, + { 0x4e9b, 0x0000 }, + { 0x4e9c, 0x0000 }, + { 0x4e9d, 0x0000 }, + { 0x4e9e, 0x0000 }, + { 0x4e9f, 0x0000 }, + { 0x4ea0, 0x0000 }, + { 0x4ea1, 0x0000 }, + { 0x4ea2, 0x0000 }, + { 0x4ea3, 0x0000 }, + { 0x4ea4, 0x0000 }, + { 0x4ea5, 0x0000 }, + { 0x4ea6, 0x0000 }, + { 0x4ea7, 0x0000 }, + { 0x4ea8, 0x0000 }, + { 0x4ea9, 0x0000 }, + { 0x4eaa, 0x0000 }, + { 0x4eab, 0x0000 }, + { 0x4eac, 0x0000 }, + { 0x4ead, 0x0000 }, + { 0x4eae, 0x0000 }, + { 0x4eaf, 0x0000 }, + { 0x4eb0, 0x0000 }, + { 0x4eb1, 0x0000 }, + { 0x4eb2, 0x0000 }, + { 0x4eb3, 0x0000 }, + { 0x4eb4, 0x0000 }, + { 0x4eb5, 0x0000 }, + { 0x4eb6, 0x0000 }, + { 0x4eb7, 0x0000 }, + { 0x4eb8, 0x0000 }, + { 0x4eb9, 0x0000 }, + { 0x4eba, 0x0000 }, + { 0x4ebb, 0x0000 }, + { 0x4ebc, 0x0000 }, + { 0x4ebd, 0x0000 }, + { 0x4ebe, 0x0000 }, + { 0x4ebf, 0x0000 }, + { 0x4ec0, 0x0000 }, + { 0x4ec1, 0x0000 }, + { 0x4ec2, 0x0000 }, + { 0x4ec3, 0x0000 }, + { 0x4ec4, 0x0000 }, + { 0x4ec5, 0x0000 }, + { 0x4ec6, 0x0000 }, + { 0x4ec7, 0x0000 }, + { 0x4ec8, 0x0000 }, + { 0x4ec9, 0x0000 }, + { 0x4eca, 0x0000 }, + { 0x4ecb, 0x0000 }, + { 0x4ecc, 0x0000 }, + { 0x4ecd, 0x0000 }, + { 0x4ece, 0x0000 }, + { 0x4ecf, 0x0000 }, + { 0x4ed0, 0x0000 }, + { 0x4ed1, 0x0000 }, + { 0x4ed2, 0x0000 }, + { 0x4ed3, 0x0000 }, + { 0x4ed4, 0x0000 }, + { 0x4ed5, 0x0000 }, + { 0x4ed6, 0x0000 }, + { 0x4ed7, 0x0000 }, + { 0x4ed8, 0x0000 }, + { 0x4ed9, 0x0000 }, + { 0x4eda, 0x0000 }, + { 0x4edb, 0x0000 }, + { 0x4edc, 0x0000 }, + { 0x4edd, 0x0000 }, + { 0x4ede, 0x0000 }, + { 0x4edf, 0x0000 }, + { 0x4ee0, 0x0000 }, + { 0x4ee1, 0x0000 }, + { 0x4ee2, 0x0000 }, + { 0x4ee3, 0x0000 }, + { 0x4ee4, 0x0000 }, + { 0x4ee5, 0x0000 }, + { 0x4ee6, 0x0000 }, + { 0x4ee7, 0x0000 }, + { 0x4ee8, 0x0000 }, + { 0x4ee9, 0x0000 }, + { 0x4eea, 0x0000 }, + { 0x4eeb, 0x0000 }, + { 0x4eec, 0x0000 }, + { 0x4eed, 0x0000 }, + { 0x4eee, 0x0000 }, + { 0x4eef, 0x0000 }, + { 0x4ef0, 0x0000 }, + { 0x4ef1, 0x0000 }, + { 0x4ef2, 0x0000 }, + { 0x4ef3, 0x0000 }, + { 0x4ef4, 0x0000 }, + { 0x4ef5, 0x0000 }, + { 0x4ef6, 0x0000 }, + { 0x4ef7, 0x0000 }, + { 0x4ef8, 0x0000 }, + { 0x4ef9, 0x0000 }, + { 0x4efa, 0x0000 }, + { 0x4efb, 0x0000 }, + { 0x4efc, 0x0000 }, + { 0x4efd, 0x0000 }, + { 0x4efe, 0x0000 }, + { 0x4eff, 0x0000 }, + { 0x4f00, 0x0000 }, + { 0x4f01, 0x0000 }, + { 0x4f02, 0x0000 }, + { 0x4f03, 0x0000 }, + { 0x4f04, 0x0000 }, + { 0x4f05, 0x0000 }, + { 0x4f06, 0x0000 }, + { 0x4f07, 0x0000 }, + { 0x4f08, 0x0000 }, + { 0x4f09, 0x0000 }, + { 0x4f0a, 0x0000 }, + { 0x4f0b, 0x0000 }, + { 0x4f0c, 0x0000 }, + { 0x4f0d, 0x0000 }, + { 0x4f0e, 0x0000 }, + { 0x4f0f, 0x0000 }, + { 0x4f10, 0x0000 }, + { 0x4f11, 0x0000 }, + { 0x4f12, 0x0000 }, + { 0x4f13, 0x0000 }, + { 0x4f14, 0x0000 }, + { 0x4f15, 0x0000 }, + { 0x4f16, 0x0000 }, + { 0x4f17, 0x0000 }, + { 0x4f18, 0x0000 }, + { 0x4f19, 0x0000 }, + { 0x4f1a, 0x0000 }, + { 0x4f1b, 0x0000 }, + { 0x4f1c, 0x0000 }, + { 0x4f1d, 0x0000 }, + { 0x4f1e, 0x0000 }, + { 0x4f1f, 0x0000 }, + { 0x4f20, 0x0000 }, + { 0x4f21, 0x0000 }, + { 0x4f22, 0x0000 }, + { 0x4f23, 0x0000 }, + { 0x4f24, 0x0000 }, + { 0x4f25, 0x0000 }, + { 0x4f26, 0x0000 }, + { 0x4f27, 0x0000 }, + { 0x4f28, 0x0000 }, + { 0x4f29, 0x0000 }, + { 0x4f2a, 0x0000 }, + { 0x4f2b, 0x0000 }, + { 0x4f2c, 0x0000 }, + { 0x4f2d, 0x0000 }, + { 0x4f2e, 0x0000 }, + { 0x4f2f, 0x0000 }, + { 0x4f30, 0x0000 }, + { 0x4f31, 0x0000 }, + { 0x4f32, 0x0000 }, + { 0x4f33, 0x0000 }, + { 0x4f34, 0x0000 }, + { 0x4f35, 0x0000 }, + { 0x4f36, 0x0000 }, + { 0x4f37, 0x0000 }, + { 0x4f38, 0x0000 }, + { 0x4f39, 0x0000 }, + { 0x4f3a, 0x0000 }, + { 0x4f3b, 0x0000 }, + { 0x4f3c, 0x0000 }, + { 0x4f3d, 0x0000 }, + { 0x4f3e, 0x0000 }, + { 0x4f3f, 0x0000 }, + { 0x4f40, 0x0000 }, + { 0x4f41, 0x0000 }, + { 0x4f42, 0x0000 }, + { 0x4f43, 0x0000 }, + { 0x4f44, 0x0000 }, + { 0x4f45, 0x0000 }, + { 0x4f46, 0x0000 }, + { 0x4f47, 0x0000 }, + { 0x4f48, 0x0000 }, + { 0x4f49, 0x0000 }, + { 0x4f4a, 0x0000 }, + { 0x4f4b, 0x0000 }, + { 0x4f4c, 0x0000 }, + { 0x4f4d, 0x0000 }, + { 0x4f4e, 0x0000 }, + { 0x4f4f, 0x0000 }, + { 0x4f50, 0x0000 }, + { 0x4f51, 0x0000 }, + { 0x4f52, 0x0000 }, + { 0x4f53, 0x0000 }, + { 0x4f54, 0x0000 }, + { 0x4f55, 0x0000 }, + { 0x4f56, 0x0000 }, + { 0x4f57, 0x0000 }, + { 0x4f58, 0x0000 }, + { 0x4f59, 0x0000 }, + { 0x4f5a, 0x0000 }, + { 0x4f5b, 0x0000 }, + { 0x4f5c, 0x0000 }, + { 0x4f5d, 0x0000 }, + { 0x4f5e, 0x0000 }, + { 0x4f5f, 0x0000 }, + { 0x4f60, 0x0000 }, + { 0x4f61, 0x0000 }, + { 0x4f62, 0x0000 }, + { 0x4f63, 0x0000 }, + { 0x4f64, 0x0000 }, + { 0x4f65, 0x0000 }, + { 0x4f66, 0x0000 }, + { 0x4f67, 0x0000 }, + { 0x4f68, 0x0000 }, + { 0x4f69, 0x0000 }, + { 0x4f6a, 0x0000 }, + { 0x4f6b, 0x0000 }, + { 0x4f6c, 0x0000 }, + { 0x4f6d, 0x0000 }, + { 0x4f6e, 0x0000 }, + { 0x4f6f, 0x0000 }, + { 0x4f70, 0x0000 }, + { 0x4f71, 0x0000 }, + { 0x4f72, 0x0000 }, + { 0x4f73, 0x0000 }, + { 0x4f74, 0x0000 }, + { 0x4f75, 0x0000 }, + { 0x4f76, 0x0000 }, + { 0x4f77, 0x0000 }, + { 0x4f78, 0x0000 }, + { 0x4f79, 0x0000 }, + { 0x4f7a, 0x0000 }, + { 0x4f7b, 0x0000 }, + { 0x4f7c, 0x0000 }, + { 0x4f7d, 0x0000 }, + { 0x4f7e, 0x0000 }, + { 0x4f7f, 0x0000 }, + { 0x4f80, 0x0000 }, + { 0x4f81, 0x0000 }, + { 0x4f82, 0x0000 }, + { 0x4f83, 0x0000 }, + { 0x4f84, 0x0000 }, + { 0x4f85, 0x0000 }, + { 0x4f86, 0x0000 }, + { 0x4f87, 0x0000 }, + { 0x4f88, 0x0000 }, + { 0x4f89, 0x0000 }, + { 0x4f8a, 0x0000 }, + { 0x4f8b, 0x0000 }, + { 0x4f8c, 0x0000 }, + { 0x4f8d, 0x0000 }, + { 0x4f8e, 0x0000 }, + { 0x4f8f, 0x0000 }, + { 0x4f90, 0x0000 }, + { 0x4f91, 0x0000 }, + { 0x4f92, 0x0000 }, + { 0x4f93, 0x0000 }, + { 0x4f94, 0x0000 }, + { 0x4f95, 0x0000 }, + { 0x4f96, 0x0000 }, + { 0x4f97, 0x0000 }, + { 0x4f98, 0x0000 }, + { 0x4f99, 0x0000 }, + { 0x4f9a, 0x0000 }, + { 0x4f9b, 0x0000 }, + { 0x4f9c, 0x0000 }, + { 0x4f9d, 0x0000 }, + { 0x4f9e, 0x0000 }, + { 0x4f9f, 0x0000 }, + { 0x4fa0, 0x0000 }, + { 0x4fa1, 0x0000 }, + { 0x4fa2, 0x0000 }, + { 0x4fa3, 0x0000 }, + { 0x4fa4, 0x0000 }, + { 0x4fa5, 0x0000 }, + { 0x4fa6, 0x0000 }, + { 0x4fa7, 0x0000 }, + { 0x4fa8, 0x0000 }, + { 0x4fa9, 0x0000 }, + { 0x4faa, 0x0000 }, + { 0x4fab, 0x0000 }, + { 0x4fac, 0x0000 }, + { 0x4fad, 0x0000 }, + { 0x4fae, 0x0000 }, + { 0x4faf, 0x0000 }, + { 0x4fb0, 0x0000 }, + { 0x4fb1, 0x0000 }, + { 0x4fb2, 0x0000 }, + { 0x4fb3, 0x0000 }, + { 0x4fb4, 0x0000 }, + { 0x4fb5, 0x0000 }, + { 0x4fb6, 0x0000 }, + { 0x4fb7, 0x0000 }, + { 0x4fb8, 0x0000 }, + { 0x4fb9, 0x0000 }, + { 0x4fba, 0x0000 }, + { 0x4fbb, 0x0000 }, + { 0x4fbc, 0x0000 }, + { 0x4fbd, 0x0000 }, + { 0x4fbe, 0x0000 }, + { 0x4fbf, 0x0000 }, + { 0x4fc0, 0x0000 }, + { 0x4fc1, 0x0000 }, + { 0x4fc2, 0x0000 }, + { 0x4fc3, 0x0000 }, + { 0x4fc4, 0x0000 }, + { 0x4fc5, 0x0000 }, + { 0x4fc6, 0x0000 }, + { 0x4fc7, 0x0000 }, + { 0x4fc8, 0x0000 }, + { 0x4fc9, 0x0000 }, + { 0x4fca, 0x0000 }, + { 0x4fcb, 0x0000 }, + { 0x4fcc, 0x0000 }, + { 0x4fcd, 0x0000 }, + { 0x4fce, 0x0000 }, + { 0x4fcf, 0x0000 }, + { 0x4fd0, 0x0000 }, + { 0x4fd1, 0x0000 }, + { 0x4fd2, 0x0000 }, + { 0x4fd3, 0x0000 }, + { 0x4fd4, 0x0000 }, + { 0x4fd5, 0x0000 }, + { 0x4fd6, 0x0000 }, + { 0x4fd7, 0x0000 }, + { 0x4fd8, 0x0000 }, + { 0x4fd9, 0x0000 }, + { 0x4fda, 0x0000 }, + { 0x4fdb, 0x0000 }, + { 0x4fdc, 0x0000 }, + { 0x4fdd, 0x0000 }, + { 0x4fde, 0x0000 }, + { 0x4fdf, 0x0000 }, + { 0x4fe0, 0x0000 }, + { 0x4fe1, 0x0000 }, + { 0x4fe2, 0x0000 }, + { 0x4fe3, 0x0000 }, + { 0x4fe4, 0x0000 }, + { 0x4fe5, 0x0000 }, + { 0x4fe6, 0x0000 }, + { 0x4fe7, 0x0000 }, + { 0x4fe8, 0x0000 }, + { 0x4fe9, 0x0000 }, + { 0x4fea, 0x0000 }, + { 0x4feb, 0x0000 }, + { 0x4fec, 0x0000 }, + { 0x4fed, 0x0000 }, + { 0x4fee, 0x0000 }, + { 0x4fef, 0x0000 }, + { 0x4ff0, 0x0000 }, + { 0x4ff1, 0x0000 }, + { 0x4ff2, 0x0000 }, + { 0x4ff3, 0x0000 }, + { 0x4ff4, 0x0000 }, + { 0x4ff5, 0x0000 }, + { 0x4ff6, 0x0000 }, + { 0x4ff7, 0x0000 }, + { 0x4ff8, 0x0000 }, + { 0x4ff9, 0x0000 }, + { 0x4ffa, 0x0000 }, + { 0x4ffb, 0x0000 }, + { 0x4ffc, 0x0000 }, + { 0x4ffd, 0x0000 }, + { 0x4ffe, 0x0000 }, + { 0x4fff, 0x0000 }, + { 0x5000, 0x0000 }, + { 0x5001, 0x0000 }, + { 0x5002, 0x0000 }, + { 0x5003, 0x0000 }, + { 0x5004, 0x0000 }, + { 0x5005, 0x0000 }, + { 0x5006, 0x0000 }, + { 0x5007, 0x0000 }, + { 0x5008, 0x0000 }, + { 0x5009, 0x0000 }, + { 0x500a, 0x0000 }, + { 0x500b, 0x0000 }, + { 0x500c, 0x0000 }, + { 0x500d, 0x0000 }, + { 0x500e, 0x0000 }, + { 0x500f, 0x0000 }, + { 0x5010, 0x0000 }, + { 0x5011, 0x0000 }, + { 0x5012, 0x0000 }, + { 0x5013, 0x0000 }, + { 0x5014, 0x0000 }, + { 0x5015, 0x0000 }, + { 0x5016, 0x0000 }, + { 0x5017, 0x0000 }, + { 0x5018, 0x0000 }, + { 0x5019, 0x0000 }, + { 0x501a, 0x0000 }, + { 0x501b, 0x0000 }, + { 0x501c, 0x0000 }, + { 0x501d, 0x0000 }, + { 0x501e, 0x0000 }, + { 0x501f, 0x0000 }, + { 0x5020, 0x0000 }, + { 0x5021, 0x0000 }, + { 0x5022, 0x0000 }, + { 0x5023, 0x0000 }, + { 0x5024, 0x0000 }, + { 0x5025, 0x0000 }, + { 0x5026, 0x0000 }, + { 0x5027, 0x0000 }, + { 0x5028, 0x0000 }, + { 0x5029, 0x0000 }, + { 0x502a, 0x0000 }, + { 0x502b, 0x0000 }, + { 0x502c, 0x0000 }, + { 0x502d, 0x0000 }, + { 0x502e, 0x0000 }, + { 0x502f, 0x0000 }, + { 0x5030, 0x0000 }, + { 0x5031, 0x0000 }, + { 0x5032, 0x0000 }, + { 0x5033, 0x0000 }, + { 0x5034, 0x0000 }, + { 0x5035, 0x0000 }, + { 0x5036, 0x0000 }, + { 0x5037, 0x0000 }, + { 0x5038, 0x0000 }, + { 0x5039, 0x0000 }, + { 0x503a, 0x0000 }, + { 0x503b, 0x0000 }, + { 0x503c, 0x0000 }, + { 0x503d, 0x0000 }, + { 0x503e, 0x0000 }, + { 0x503f, 0x0000 }, + { 0x5040, 0x0000 }, + { 0x5041, 0x0000 }, + { 0x5042, 0x0000 }, + { 0x5043, 0x0000 }, + { 0x5044, 0x0000 }, + { 0x5045, 0x0000 }, + { 0x5046, 0x0000 }, + { 0x5047, 0x0000 }, + { 0x5048, 0x0000 }, + { 0x5049, 0x0000 }, + { 0x504a, 0x0000 }, + { 0x504b, 0x0000 }, + { 0x504c, 0x0000 }, + { 0x504d, 0x0000 }, + { 0x504e, 0x0000 }, + { 0x504f, 0x0000 }, + { 0x5050, 0x0000 }, + { 0x5051, 0x0000 }, + { 0x5052, 0x0000 }, + { 0x5053, 0x0000 }, + { 0x5054, 0x0000 }, + { 0x5055, 0x0000 }, + { 0x5056, 0x0000 }, + { 0x5057, 0x0000 }, + { 0x5058, 0x0000 }, + { 0x5059, 0x0000 }, + { 0x505a, 0x0000 }, + { 0x505b, 0x0000 }, + { 0x505c, 0x0000 }, + { 0x505d, 0x0000 }, + { 0x505e, 0x0000 }, + { 0x505f, 0x0000 }, + { 0x5060, 0x0000 }, + { 0x5061, 0x0000 }, + { 0x5062, 0x0000 }, + { 0x5063, 0x0000 }, + { 0x5064, 0x0000 }, + { 0x5065, 0x0000 }, + { 0x5066, 0x0000 }, + { 0x5067, 0x0000 }, + { 0x5068, 0x0000 }, + { 0x5069, 0x0000 }, + { 0x506a, 0x0000 }, + { 0x506b, 0x0000 }, + { 0x506c, 0x0000 }, + { 0x506d, 0x0000 }, + { 0x506e, 0x0000 }, + { 0x506f, 0x0000 }, + { 0x5070, 0x0000 }, + { 0x5071, 0x0000 }, + { 0x5072, 0x0000 }, + { 0x5073, 0x0000 }, + { 0x5074, 0x0000 }, + { 0x5075, 0x0000 }, + { 0x5076, 0x0000 }, + { 0x5077, 0x0000 }, + { 0x5078, 0x0000 }, + { 0x5079, 0x0000 }, + { 0x507a, 0x0000 }, + { 0x507b, 0x0000 }, + { 0x507c, 0x0000 }, + { 0x507d, 0x0000 }, + { 0x507e, 0x0000 }, + { 0x507f, 0x0000 }, + { 0x5080, 0x0000 }, + { 0x5081, 0x0000 }, + { 0x5082, 0x0000 }, + { 0x5083, 0x0000 }, + { 0x5084, 0x0000 }, + { 0x5085, 0x0000 }, + { 0x5086, 0x0000 }, + { 0x5087, 0x0000 }, + { 0x5088, 0x0000 }, + { 0x5089, 0x0000 }, + { 0x508a, 0x0000 }, + { 0x508b, 0x0000 }, + { 0x508c, 0x0000 }, + { 0x508d, 0x0000 }, + { 0x508e, 0x0000 }, + { 0x508f, 0x0000 }, + { 0x5090, 0x0000 }, + { 0x5091, 0x0000 }, + { 0x5092, 0x0000 }, + { 0x5093, 0x0000 }, + { 0x5094, 0x0000 }, + { 0x5095, 0x0000 }, + { 0x5096, 0x0000 }, + { 0x5097, 0x0000 }, + { 0x5098, 0x0000 }, + { 0x5099, 0x0000 }, + { 0x509a, 0x0000 }, + { 0x509b, 0x0000 }, + { 0x509c, 0x0000 }, + { 0x509d, 0x0000 }, + { 0x509e, 0x0000 }, + { 0x509f, 0x0000 }, + { 0x50a0, 0x0000 }, + { 0x50a1, 0x0000 }, + { 0x50a2, 0x0000 }, + { 0x50a3, 0x0000 }, + { 0x50a4, 0x0000 }, + { 0x50a5, 0x0000 }, + { 0x50a6, 0x0000 }, + { 0x50a7, 0x0000 }, + { 0x50a8, 0x0000 }, + { 0x50a9, 0x0000 }, + { 0x50aa, 0x0000 }, + { 0x50ab, 0x0000 }, + { 0x50ac, 0x0000 }, + { 0x50ad, 0x0000 }, + { 0x50ae, 0x0000 }, + { 0x50af, 0x0000 }, + { 0x50b0, 0x0000 }, + { 0x50b1, 0x0000 }, + { 0x50b2, 0x0000 }, + { 0x50b3, 0x0000 }, + { 0x50b4, 0x0000 }, + { 0x50b5, 0x0000 }, + { 0x50b6, 0x0000 }, + { 0x50b7, 0x0000 }, + { 0x50b8, 0x0000 }, + { 0x50b9, 0x0000 }, + { 0x50ba, 0x0000 }, + { 0x50bb, 0x0000 }, + { 0x50bc, 0x0000 }, + { 0x50bd, 0x0000 }, + { 0x50be, 0x0000 }, + { 0x50bf, 0x0000 }, + { 0x50c0, 0x0000 }, + { 0x50c1, 0x0000 }, + { 0x50c2, 0x0000 }, + { 0x50c3, 0x0000 }, + { 0x50c4, 0x0000 }, + { 0x50c5, 0x0000 }, + { 0x50c6, 0x0000 }, + { 0x50c7, 0x0000 }, + { 0x50c8, 0x0000 }, + { 0x50c9, 0x0000 }, + { 0x50ca, 0x0000 }, + { 0x50cb, 0x0000 }, + { 0x50cc, 0x0000 }, + { 0x50cd, 0x0000 }, + { 0x50ce, 0x0000 }, + { 0x50cf, 0x0000 }, + { 0x50d0, 0x0000 }, + { 0x50d1, 0x0000 }, + { 0x50d2, 0x0000 }, + { 0x50d3, 0x0000 }, + { 0x50d4, 0x0000 }, + { 0x50d5, 0x0000 }, + { 0x50d6, 0x0000 }, + { 0x50d7, 0x0000 }, + { 0x50d8, 0x0000 }, + { 0x50d9, 0x0000 }, + { 0x50da, 0x0000 }, + { 0x50db, 0x0000 }, + { 0x50dc, 0x0000 }, + { 0x50dd, 0x0000 }, + { 0x50de, 0x0000 }, + { 0x50df, 0x0000 }, + { 0x50e0, 0x0000 }, + { 0x50e1, 0x0000 }, + { 0x50e2, 0x0000 }, + { 0x50e3, 0x0000 }, + { 0x50e4, 0x0000 }, + { 0x50e5, 0x0000 }, + { 0x50e6, 0x0000 }, + { 0x50e7, 0x0000 }, + { 0x50e8, 0x0000 }, + { 0x50e9, 0x0000 }, + { 0x50ea, 0x0000 }, + { 0x50eb, 0x0000 }, + { 0x50ec, 0x0000 }, + { 0x50ed, 0x0000 }, + { 0x50ee, 0x0000 }, + { 0x50ef, 0x0000 }, + { 0x50f0, 0x0000 }, + { 0x50f1, 0x0000 }, + { 0x50f2, 0x0000 }, + { 0x50f3, 0x0000 }, + { 0x50f4, 0x0000 }, + { 0x50f5, 0x0000 }, + { 0x50f6, 0x0000 }, + { 0x50f7, 0x0000 }, + { 0x50f8, 0x0000 }, + { 0x50f9, 0x0000 }, + { 0x50fa, 0x0000 }, + { 0x50fb, 0x0000 }, + { 0x50fc, 0x0000 }, + { 0x50fd, 0x0000 }, + { 0x50fe, 0x0000 }, + { 0x50ff, 0x0000 }, + { 0x5100, 0x0000 }, + { 0x5101, 0x0000 }, + { 0x5102, 0x0000 }, + { 0x5103, 0x0000 }, + { 0x5104, 0x0000 }, + { 0x5105, 0x0000 }, + { 0x5106, 0x0000 }, + { 0x5107, 0x0000 }, + { 0x5108, 0x0000 }, + { 0x5109, 0x0000 }, + { 0x510a, 0x0000 }, + { 0x510b, 0x0000 }, + { 0x510c, 0x0000 }, + { 0x510d, 0x0000 }, + { 0x510e, 0x0000 }, + { 0x510f, 0x0000 }, + { 0x5110, 0x0000 }, + { 0x5111, 0x0000 }, + { 0x5112, 0x0000 }, + { 0x5113, 0x0000 }, + { 0x5114, 0x0000 }, + { 0x5115, 0x0000 }, + { 0x5116, 0x0000 }, + { 0x5117, 0x0000 }, + { 0x5118, 0x0000 }, + { 0x5119, 0x0000 }, + { 0x511a, 0x0000 }, + { 0x511b, 0x0000 }, + { 0x511c, 0x0000 }, + { 0x511d, 0x0000 }, + { 0x511e, 0x0000 }, + { 0x511f, 0x0000 }, + { 0x5120, 0x0000 }, + { 0x5121, 0x0000 }, + { 0x5122, 0x0000 }, + { 0x5123, 0x0000 }, + { 0x5124, 0x0000 }, + { 0x5125, 0x0000 }, + { 0x5126, 0x0000 }, + { 0x5127, 0x0000 }, + { 0x5128, 0x0000 }, + { 0x5129, 0x0000 }, + { 0x512a, 0x0000 }, + { 0x512b, 0x0000 }, + { 0x512c, 0x0000 }, + { 0x512d, 0x0000 }, + { 0x512e, 0x0000 }, + { 0x512f, 0x0000 }, + { 0x5130, 0x0000 }, + { 0x5131, 0x0000 }, + { 0x5132, 0x0000 }, + { 0x5133, 0x0000 }, + { 0x5134, 0x0000 }, + { 0x5135, 0x0000 }, + { 0x5136, 0x0000 }, + { 0x5137, 0x0000 }, + { 0x5138, 0x0000 }, + { 0x5139, 0x0000 }, + { 0x513a, 0x0000 }, + { 0x513b, 0x0000 }, + { 0x513c, 0x0000 }, + { 0x513d, 0x0000 }, + { 0x513e, 0x0000 }, + { 0x513f, 0x0000 }, + { 0x5140, 0x0000 }, + { 0x5141, 0x0000 }, + { 0x5142, 0x0000 }, + { 0x5143, 0x0000 }, + { 0x5144, 0x0000 }, + { 0x5145, 0x0000 }, + { 0x5146, 0x0000 }, + { 0x5147, 0x0000 }, + { 0x5148, 0x0000 }, + { 0x5149, 0x0000 }, + { 0x514a, 0x0000 }, + { 0x514b, 0x0000 }, + { 0x514c, 0x0000 }, + { 0x514d, 0x0000 }, + { 0x514e, 0x0000 }, + { 0x514f, 0x0000 }, + { 0x5150, 0x0000 }, + { 0x5151, 0x0000 }, + { 0x5152, 0x0000 }, + { 0x5153, 0x0000 }, + { 0x5154, 0x0000 }, + { 0x5155, 0x0000 }, + { 0x5156, 0x0000 }, + { 0x5157, 0x0000 }, + { 0x5158, 0x0000 }, + { 0x5159, 0x0000 }, + { 0x515a, 0x0000 }, + { 0x515b, 0x0000 }, + { 0x515c, 0x0000 }, + { 0x515d, 0x0000 }, + { 0x515e, 0x0000 }, + { 0x515f, 0x0000 }, + { 0x5160, 0x0000 }, + { 0x5161, 0x0000 }, + { 0x5162, 0x0000 }, + { 0x5163, 0x0000 }, + { 0x5164, 0x0000 }, + { 0x5165, 0x0000 }, + { 0x5166, 0x0000 }, + { 0x5167, 0x0000 }, + { 0x5168, 0x0000 }, + { 0x5169, 0x0000 }, + { 0x516a, 0x0000 }, + { 0x516b, 0x0000 }, + { 0x516c, 0x0000 }, + { 0x516d, 0x0000 }, + { 0x516e, 0x0000 }, + { 0x516f, 0x0000 }, + { 0x5170, 0x0000 }, + { 0x5171, 0x0000 }, + { 0x5172, 0x0000 }, + { 0x5173, 0x0000 }, + { 0x5174, 0x0000 }, + { 0x5175, 0x0000 }, + { 0x5176, 0x0000 }, + { 0x5177, 0x0000 }, + { 0x5178, 0x0000 }, + { 0x5179, 0x0000 }, + { 0x517a, 0x0000 }, + { 0x517b, 0x0000 }, + { 0x517c, 0x0000 }, + { 0x517d, 0x0000 }, + { 0x517e, 0x0000 }, + { 0x517f, 0x0000 }, + { 0x5180, 0x0000 }, + { 0x5181, 0x0000 }, + { 0x5182, 0x0000 }, + { 0x5183, 0x0000 }, + { 0x5184, 0x0000 }, + { 0x5185, 0x0000 }, + { 0x5186, 0x0000 }, + { 0x5187, 0x0000 }, + { 0x5188, 0x0000 }, + { 0x5189, 0x0000 }, + { 0x518a, 0x0000 }, + { 0x518b, 0x0000 }, + { 0x518c, 0x0000 }, + { 0x518d, 0x0000 }, + { 0x518e, 0x0000 }, + { 0x518f, 0x0000 }, + { 0x5190, 0x0000 }, + { 0x5191, 0x0000 }, + { 0x5192, 0x0000 }, + { 0x5193, 0x0000 }, + { 0x5194, 0x0000 }, + { 0x5195, 0x0000 }, + { 0x5196, 0x0000 }, + { 0x5197, 0x0000 }, + { 0x5198, 0x0000 }, + { 0x5199, 0x0000 }, + { 0x519a, 0x0000 }, + { 0x519b, 0x0000 }, + { 0x519c, 0x0000 }, + { 0x519d, 0x0000 }, + { 0x519e, 0x0000 }, + { 0x519f, 0x0000 }, + { 0x51a0, 0x0000 }, + { 0x51a1, 0x0000 }, + { 0x51a2, 0x0000 }, + { 0x51a3, 0x0000 }, + { 0x51a4, 0x0000 }, + { 0x51a5, 0x0000 }, + { 0x51a6, 0x0000 }, + { 0x51a7, 0x0000 }, + { 0x51a8, 0x0000 }, + { 0x51a9, 0x0000 }, + { 0x51aa, 0x0000 }, + { 0x51ab, 0x0000 }, + { 0x51ac, 0x0000 }, + { 0x51ad, 0x0000 }, + { 0x51ae, 0x0000 }, + { 0x51af, 0x0000 }, + { 0x51b0, 0x0000 }, + { 0x51b1, 0x0000 }, + { 0x51b2, 0x0000 }, + { 0x51b3, 0x0000 }, + { 0x51b4, 0x0000 }, + { 0x51b5, 0x0000 }, + { 0x51b6, 0x0000 }, + { 0x51b7, 0x0000 }, + { 0x51b8, 0x0000 }, + { 0x51b9, 0x0000 }, + { 0x51ba, 0x0000 }, + { 0x51bb, 0x0000 }, + { 0x51bc, 0x0000 }, + { 0x51bd, 0x0000 }, + { 0x51be, 0x0000 }, + { 0x51bf, 0x0000 }, + { 0x51c0, 0x0000 }, + { 0x51c1, 0x0000 }, + { 0x51c2, 0x0000 }, + { 0x51c3, 0x0000 }, + { 0x51c4, 0x0000 }, + { 0x51c5, 0x0000 }, + { 0x51c6, 0x0000 }, + { 0x51c7, 0x0000 }, + { 0x51c8, 0x0000 }, + { 0x51c9, 0x0000 }, + { 0x51ca, 0x0000 }, + { 0x51cb, 0x0000 }, + { 0x51cc, 0x0000 }, + { 0x51cd, 0x0000 }, + { 0x51ce, 0x0000 }, + { 0x51cf, 0x0000 }, + { 0x51d0, 0x0000 }, + { 0x51d1, 0x0000 }, + { 0x51d2, 0x0000 }, + { 0x51d3, 0x0000 }, + { 0x51d4, 0x0000 }, + { 0x51d5, 0x0000 }, + { 0x51d6, 0x0000 }, + { 0x51d7, 0x0000 }, + { 0x51d8, 0x0000 }, + { 0x51d9, 0x0000 }, + { 0x51da, 0x0000 }, + { 0x51db, 0x0000 }, + { 0x51dc, 0x0000 }, + { 0x51dd, 0x0000 }, + { 0x51de, 0x0000 }, + { 0x51df, 0x0000 }, + { 0x51e0, 0x0000 }, + { 0x51e1, 0x0000 }, + { 0x51e2, 0x0000 }, + { 0x51e3, 0x0000 }, + { 0x51e4, 0x0000 }, + { 0x51e5, 0x0000 }, + { 0x51e6, 0x0000 }, + { 0x51e7, 0x0000 }, + { 0x51e8, 0x0000 }, + { 0x51e9, 0x0000 }, + { 0x51ea, 0x0000 }, + { 0x51eb, 0x0000 }, + { 0x51ec, 0x0000 }, + { 0x51ed, 0x0000 }, + { 0x51ee, 0x0000 }, + { 0x51ef, 0x0000 }, + { 0x51f0, 0x0000 }, + { 0x51f1, 0x0000 }, + { 0x51f2, 0x0000 }, + { 0x51f3, 0x0000 }, + { 0x51f4, 0x0000 }, + { 0x51f5, 0x0000 }, + { 0x51f6, 0x0000 }, + { 0x51f7, 0x0000 }, + { 0x51f8, 0x0000 }, + { 0x51f9, 0x0000 }, + { 0x51fa, 0x0000 }, + { 0x51fb, 0x0000 }, + { 0x51fc, 0x0000 }, + { 0x51fd, 0x0000 }, + { 0x51fe, 0x0000 }, + { 0x51ff, 0x0000 }, + { 0x5200, 0x0000 }, + { 0x5201, 0x0000 }, + { 0x5202, 0x0000 }, + { 0x5203, 0x0000 }, + { 0x5204, 0x0000 }, + { 0x5205, 0x0000 }, + { 0x5206, 0x0000 }, + { 0x5207, 0x0000 }, + { 0x5208, 0x0000 }, + { 0x5209, 0x0000 }, + { 0x520a, 0x0000 }, + { 0x520b, 0x0000 }, + { 0x520c, 0x0000 }, + { 0x520d, 0x0000 }, + { 0x520e, 0x0000 }, + { 0x520f, 0x0000 }, + { 0x5210, 0x0000 }, + { 0x5211, 0x0000 }, + { 0x5212, 0x0000 }, + { 0x5213, 0x0000 }, + { 0x5214, 0x0000 }, + { 0x5215, 0x0000 }, + { 0x5216, 0x0000 }, + { 0x5217, 0x0000 }, + { 0x5218, 0x0000 }, + { 0x5219, 0x0000 }, + { 0x521a, 0x0000 }, + { 0x521b, 0x0000 }, + { 0x521c, 0x0000 }, + { 0x521d, 0x0000 }, + { 0x521e, 0x0000 }, + { 0x521f, 0x0000 }, + { 0x5220, 0x0000 }, + { 0x5221, 0x0000 }, + { 0x5222, 0x0000 }, + { 0x5223, 0x0000 }, + { 0x5224, 0x0000 }, + { 0x5225, 0x0000 }, + { 0x5226, 0x0000 }, + { 0x5227, 0x0000 }, + { 0x5228, 0x0000 }, + { 0x5229, 0x0000 }, + { 0x522a, 0x0000 }, + { 0x522b, 0x0000 }, + { 0x522c, 0x0000 }, + { 0x522d, 0x0000 }, + { 0x522e, 0x0000 }, + { 0x522f, 0x0000 }, + { 0x5230, 0x0000 }, + { 0x5231, 0x0000 }, + { 0x5232, 0x0000 }, + { 0x5233, 0x0000 }, + { 0x5234, 0x0000 }, + { 0x5235, 0x0000 }, + { 0x5236, 0x0000 }, + { 0x5237, 0x0000 }, + { 0x5238, 0x0000 }, + { 0x5239, 0x0000 }, + { 0x523a, 0x0000 }, + { 0x523b, 0x0000 }, + { 0x523c, 0x0000 }, + { 0x523d, 0x0000 }, + { 0x523e, 0x0000 }, + { 0x523f, 0x0000 }, + { 0x5240, 0x0000 }, + { 0x5241, 0x0000 }, + { 0x5242, 0x0000 }, + { 0x5243, 0x0000 }, + { 0x5244, 0x0000 }, + { 0x5245, 0x0000 }, + { 0x5246, 0x0000 }, + { 0x5247, 0x0000 }, + { 0x5248, 0x0000 }, + { 0x5249, 0x0000 }, + { 0x524a, 0x0000 }, + { 0x524b, 0x0000 }, + { 0x524c, 0x0000 }, + { 0x524d, 0x0000 }, + { 0x524e, 0x0000 }, + { 0x524f, 0x0000 }, + { 0x5250, 0x0000 }, + { 0x5251, 0x0000 }, + { 0x5252, 0x0000 }, + { 0x5253, 0x0000 }, + { 0x5254, 0x0000 }, + { 0x5255, 0x0000 }, + { 0x5256, 0x0000 }, + { 0x5257, 0x0000 }, + { 0x5258, 0x0000 }, + { 0x5259, 0x0000 }, + { 0x525a, 0x0000 }, + { 0x525b, 0x0000 }, + { 0x525c, 0x0000 }, + { 0x525d, 0x0000 }, + { 0x525e, 0x0000 }, + { 0x525f, 0x0000 }, + { 0x5260, 0x0000 }, + { 0x5261, 0x0000 }, + { 0x5262, 0x0000 }, + { 0x5263, 0x0000 }, + { 0x5264, 0x0000 }, + { 0x5265, 0x0000 }, + { 0x5266, 0x0000 }, + { 0x5267, 0x0000 }, + { 0x5268, 0x0000 }, + { 0x5269, 0x0000 }, + { 0x526a, 0x0000 }, + { 0x526b, 0x0000 }, + { 0x526c, 0x0000 }, + { 0x526d, 0x0000 }, + { 0x526e, 0x0000 }, + { 0x526f, 0x0000 }, + { 0x5270, 0x0000 }, + { 0x5271, 0x0000 }, + { 0x5272, 0x0000 }, + { 0x5273, 0x0000 }, + { 0x5274, 0x0000 }, + { 0x5275, 0x0000 }, + { 0x5276, 0x0000 }, + { 0x5277, 0x0000 }, + { 0x5278, 0x0000 }, + { 0x5279, 0x0000 }, + { 0x527a, 0x0000 }, + { 0x527b, 0x0000 }, + { 0x527c, 0x0000 }, + { 0x527d, 0x0000 }, + { 0x527e, 0x0000 }, + { 0x527f, 0x0000 }, + { 0x5280, 0x0000 }, + { 0x5281, 0x0000 }, + { 0x5282, 0x0000 }, + { 0x5283, 0x0000 }, + { 0x5284, 0x0000 }, + { 0x5285, 0x0000 }, + { 0x5286, 0x0000 }, + { 0x5287, 0x0000 }, + { 0x5288, 0x0000 }, + { 0x5289, 0x0000 }, + { 0x528a, 0x0000 }, + { 0x528b, 0x0000 }, + { 0x528c, 0x0000 }, + { 0x528d, 0x0000 }, + { 0x528e, 0x0000 }, + { 0x528f, 0x0000 }, + { 0x5290, 0x0000 }, + { 0x5291, 0x0000 }, + { 0x5292, 0x0000 }, + { 0x5293, 0x0000 }, + { 0x5294, 0x0000 }, + { 0x5295, 0x0000 }, + { 0x5296, 0x0000 }, + { 0x5297, 0x0000 }, + { 0x5298, 0x0000 }, + { 0x5299, 0x0000 }, + { 0x529a, 0x0000 }, + { 0x529b, 0x0000 }, + { 0x529c, 0x0000 }, + { 0x529d, 0x0000 }, + { 0x529e, 0x0000 }, + { 0x529f, 0x0000 }, + { 0x52a0, 0x0000 }, + { 0x52a1, 0x0000 }, + { 0x52a2, 0x0000 }, + { 0x52a3, 0x0000 }, + { 0x52a4, 0x0000 }, + { 0x52a5, 0x0000 }, + { 0x52a6, 0x0000 }, + { 0x52a7, 0x0000 }, + { 0x52a8, 0x0000 }, + { 0x52a9, 0x0000 }, + { 0x52aa, 0x0000 }, + { 0x52ab, 0x0000 }, + { 0x52ac, 0x0000 }, + { 0x52ad, 0x0000 }, + { 0x52ae, 0x0000 }, + { 0x52af, 0x0000 }, + { 0x52b0, 0x0000 }, + { 0x52b1, 0x0000 }, + { 0x52b2, 0x0000 }, + { 0x52b3, 0x0000 }, + { 0x52b4, 0x0000 }, + { 0x52b5, 0x0000 }, + { 0x52b6, 0x0000 }, + { 0x52b7, 0x0000 }, + { 0x52b8, 0x0000 }, + { 0x52b9, 0x0000 }, + { 0x52ba, 0x0000 }, + { 0x52bb, 0x0000 }, + { 0x52bc, 0x0000 }, + { 0x52bd, 0x0000 }, + { 0x52be, 0x0000 }, + { 0x52bf, 0x0000 }, + { 0x52c0, 0x0000 }, + { 0x52c1, 0x0000 }, + { 0x52c2, 0x0000 }, + { 0x52c3, 0x0000 }, + { 0x52c4, 0x0000 }, + { 0x52c5, 0x0000 }, + { 0x52c6, 0x0000 }, + { 0x52c7, 0x0000 }, + { 0x52c8, 0x0000 }, + { 0x52c9, 0x0000 }, + { 0x52ca, 0x0000 }, + { 0x52cb, 0x0000 }, + { 0x52cc, 0x0000 }, + { 0x52cd, 0x0000 }, + { 0x52ce, 0x0000 }, + { 0x52cf, 0x0000 }, + { 0x52d0, 0x0000 }, + { 0x52d1, 0x0000 }, + { 0x52d2, 0x0000 }, + { 0x52d3, 0x0000 }, + { 0x52d4, 0x0000 }, + { 0x52d5, 0x0000 }, + { 0x52d6, 0x0000 }, + { 0x52d7, 0x0000 }, + { 0x52d8, 0x0000 }, + { 0x52d9, 0x0000 }, + { 0x52da, 0x0000 }, + { 0x52db, 0x0000 }, + { 0x52dc, 0x0000 }, + { 0x52dd, 0x0000 }, + { 0x52de, 0x0000 }, + { 0x52df, 0x0000 }, + { 0x52e0, 0x0000 }, + { 0x52e1, 0x0000 }, + { 0x52e2, 0x0000 }, + { 0x52e3, 0x0000 }, + { 0x52e4, 0x0000 }, + { 0x52e5, 0x0000 }, + { 0x52e6, 0x0000 }, + { 0x52e7, 0x0000 }, + { 0x52e8, 0x0000 }, + { 0x52e9, 0x0000 }, + { 0x52ea, 0x0000 }, + { 0x52eb, 0x0000 }, + { 0x52ec, 0x0000 }, + { 0x52ed, 0x0000 }, + { 0x52ee, 0x0000 }, + { 0x52ef, 0x0000 }, + { 0x52f0, 0x0000 }, + { 0x52f1, 0x0000 }, + { 0x52f2, 0x0000 }, + { 0x52f3, 0x0000 }, + { 0x52f4, 0x0000 }, + { 0x52f5, 0x0000 }, + { 0x52f6, 0x0000 }, + { 0x52f7, 0x0000 }, + { 0x52f8, 0x0000 }, + { 0x52f9, 0x0000 }, + { 0x52fa, 0x0000 }, + { 0x52fb, 0x0000 }, + { 0x52fc, 0x0000 }, + { 0x52fd, 0x0000 }, + { 0x52fe, 0x0000 }, + { 0x52ff, 0x0000 }, + { 0x5300, 0x0000 }, + { 0x5301, 0x0000 }, + { 0x5302, 0x0000 }, + { 0x5303, 0x0000 }, + { 0x5304, 0x0000 }, + { 0x5305, 0x0000 }, + { 0x5306, 0x0000 }, + { 0x5307, 0x0000 }, + { 0x5308, 0x0000 }, + { 0x5309, 0x0000 }, + { 0x530a, 0x0000 }, + { 0x530b, 0x0000 }, + { 0x530c, 0x0000 }, + { 0x530d, 0x0000 }, + { 0x530e, 0x0000 }, + { 0x530f, 0x0000 }, + { 0x5310, 0x0000 }, + { 0x5311, 0x0000 }, + { 0x5312, 0x0000 }, + { 0x5313, 0x0000 }, + { 0x5314, 0x0000 }, + { 0x5315, 0x0000 }, + { 0x5316, 0x0000 }, + { 0x5317, 0x0000 }, + { 0x5318, 0x0000 }, + { 0x5319, 0x0000 }, + { 0x531a, 0x0000 }, + { 0x531b, 0x0000 }, + { 0x531c, 0x0000 }, + { 0x531d, 0x0000 }, + { 0x531e, 0x0000 }, + { 0x531f, 0x0000 }, + { 0x5320, 0x0000 }, + { 0x5321, 0x0000 }, + { 0x5322, 0x0000 }, + { 0x5323, 0x0000 }, + { 0x5324, 0x0000 }, + { 0x5325, 0x0000 }, + { 0x5326, 0x0000 }, + { 0x5327, 0x0000 }, + { 0x5328, 0x0000 }, + { 0x5329, 0x0000 }, + { 0x532a, 0x0000 }, + { 0x532b, 0x0000 }, + { 0x532c, 0x0000 }, + { 0x532d, 0x0000 }, + { 0x532e, 0x0000 }, + { 0x532f, 0x0000 }, + { 0x5330, 0x0000 }, + { 0x5331, 0x0000 }, + { 0x5332, 0x0000 }, + { 0x5333, 0x0000 }, + { 0x5334, 0x0000 }, + { 0x5335, 0x0000 }, + { 0x5336, 0x0000 }, + { 0x5337, 0x0000 }, + { 0x5338, 0x0000 }, + { 0x5339, 0x0000 }, + { 0x533a, 0x0000 }, + { 0x533b, 0x0000 }, + { 0x533c, 0x0000 }, + { 0x533d, 0x0000 }, + { 0x533e, 0x0000 }, + { 0x533f, 0x0000 }, + { 0x5340, 0x0000 }, + { 0x5341, 0x0000 }, + { 0x5342, 0x0000 }, + { 0x5343, 0x0000 }, + { 0x5344, 0x0000 }, + { 0x5345, 0x0000 }, + { 0x5346, 0x0000 }, + { 0x5347, 0x0000 }, + { 0x5348, 0x0000 }, + { 0x5349, 0x0000 }, + { 0x534a, 0x0000 }, + { 0x534b, 0x0000 }, + { 0x534c, 0x0000 }, + { 0x534d, 0x0000 }, + { 0x534e, 0x0000 }, + { 0x534f, 0x0000 }, + { 0x5350, 0x0000 }, + { 0x5351, 0x0000 }, + { 0x5352, 0x0000 }, + { 0x5353, 0x0000 }, + { 0x5354, 0x0000 }, + { 0x5355, 0x0000 }, + { 0x5356, 0x0000 }, + { 0x5357, 0x0000 }, + { 0x5358, 0x0000 }, + { 0x5359, 0x0000 }, + { 0x535a, 0x0000 }, + { 0x535b, 0x0000 }, + { 0x535c, 0x0000 }, + { 0x535d, 0x0000 }, + { 0x535e, 0x0000 }, + { 0x535f, 0x0000 }, + { 0x5360, 0x0000 }, + { 0x5361, 0x0000 }, + { 0x5362, 0x0000 }, + { 0x5363, 0x0000 }, + { 0x5364, 0x0000 }, + { 0x5365, 0x0000 }, + { 0x5366, 0x0000 }, + { 0x5367, 0x0000 }, + { 0x5368, 0x0000 }, + { 0x5369, 0x0000 }, + { 0x536a, 0x0000 }, + { 0x536b, 0x0000 }, + { 0x536c, 0x0000 }, + { 0x536d, 0x0000 }, + { 0x536e, 0x0000 }, + { 0x536f, 0x0000 }, + { 0x5370, 0x0000 }, + { 0x5371, 0x0000 }, + { 0x5372, 0x0000 }, + { 0x5373, 0x0000 }, + { 0x5374, 0x0000 }, + { 0x5375, 0x0000 }, + { 0x5376, 0x0000 }, + { 0x5377, 0x0000 }, + { 0x5378, 0x0000 }, + { 0x5379, 0x0000 }, + { 0x537a, 0x0000 }, + { 0x537b, 0x0000 }, + { 0x537c, 0x0000 }, + { 0x537d, 0x0000 }, + { 0x537e, 0x0000 }, + { 0x537f, 0x0000 }, + { 0x5380, 0x0000 }, + { 0x5381, 0x0000 }, + { 0x5382, 0x0000 }, + { 0x5383, 0x0000 }, + { 0x5384, 0x0000 }, + { 0x5385, 0x0000 }, + { 0x5386, 0x0000 }, + { 0x5387, 0x0000 }, + { 0x5388, 0x0000 }, + { 0x5389, 0x0000 }, + { 0x538a, 0x0000 }, + { 0x538b, 0x0000 }, + { 0x538c, 0x0000 }, + { 0x538d, 0x0000 }, + { 0x538e, 0x0000 }, + { 0x538f, 0x0000 }, + { 0x5390, 0x0000 }, + { 0x5391, 0x0000 }, + { 0x5392, 0x0000 }, + { 0x5393, 0x0000 }, + { 0x5394, 0x0000 }, + { 0x5395, 0x0000 }, + { 0x5396, 0x0000 }, + { 0x5397, 0x0000 }, + { 0x5398, 0x0000 }, + { 0x5399, 0x0000 }, + { 0x539a, 0x0000 }, + { 0x539b, 0x0000 }, + { 0x539c, 0x0000 }, + { 0x539d, 0x0000 }, + { 0x539e, 0x0000 }, + { 0x539f, 0x0000 }, + { 0x53a0, 0x0000 }, + { 0x53a1, 0x0000 }, + { 0x53a2, 0x0000 }, + { 0x53a3, 0x0000 }, + { 0x53a4, 0x0000 }, + { 0x53a5, 0x0000 }, + { 0x53a6, 0x0000 }, + { 0x53a7, 0x0000 }, + { 0x53a8, 0x0000 }, + { 0x53a9, 0x0000 }, + { 0x53aa, 0x0000 }, + { 0x53ab, 0x0000 }, + { 0x53ac, 0x0000 }, + { 0x53ad, 0x0000 }, + { 0x53ae, 0x0000 }, + { 0x53af, 0x0000 }, + { 0x53b0, 0x0000 }, + { 0x53b1, 0x0000 }, + { 0x53b2, 0x0000 }, + { 0x53b3, 0x0000 }, + { 0x53b4, 0x0000 }, + { 0x53b5, 0x0000 }, + { 0x53b6, 0x0000 }, + { 0x53b7, 0x0000 }, + { 0x53b8, 0x0000 }, + { 0x53b9, 0x0000 }, + { 0x53ba, 0x0000 }, + { 0x53bb, 0x0000 }, + { 0x53bc, 0x0000 }, + { 0x53bd, 0x0000 }, + { 0x53be, 0x0000 }, + { 0x53bf, 0x0000 }, + { 0x53c0, 0x0000 }, + { 0x53c1, 0x0000 }, + { 0x53c2, 0x0000 }, + { 0x53c3, 0x0000 }, + { 0x53c4, 0x0000 }, + { 0x53c5, 0x0000 }, + { 0x53c6, 0x0000 }, + { 0x53c7, 0x0000 }, + { 0x53c8, 0x0000 }, + { 0x53c9, 0x0000 }, + { 0x53ca, 0x0000 }, + { 0x53cb, 0x0000 }, + { 0x53cc, 0x0000 }, + { 0x53cd, 0x0000 }, + { 0x53ce, 0x0000 }, + { 0x53cf, 0x0000 }, + { 0x53d0, 0x0000 }, + { 0x53d1, 0x0000 }, + { 0x53d2, 0x0000 }, + { 0x53d3, 0x0000 }, + { 0x53d4, 0x0000 }, + { 0x53d5, 0x0000 }, + { 0x53d6, 0x0000 }, + { 0x53d7, 0x0000 }, + { 0x53d8, 0x0000 }, + { 0x53d9, 0x0000 }, + { 0x53da, 0x0000 }, + { 0x53db, 0x0000 }, + { 0x53dc, 0x0000 }, + { 0x53dd, 0x0000 }, + { 0x53de, 0x0000 }, + { 0x53df, 0x0000 }, + { 0x53e0, 0x0000 }, + { 0x53e1, 0x0000 }, + { 0x53e2, 0x0000 }, + { 0x53e3, 0x0000 }, + { 0x53e4, 0x0000 }, + { 0x53e5, 0x0000 }, + { 0x53e6, 0x0000 }, + { 0x53e7, 0x0000 }, + { 0x53e8, 0x0000 }, + { 0x53e9, 0x0000 }, + { 0x53ea, 0x0000 }, + { 0x53eb, 0x0000 }, + { 0x53ec, 0x0000 }, + { 0x53ed, 0x0000 }, + { 0x53ee, 0x0000 }, + { 0x53ef, 0x0000 }, + { 0x53f0, 0x0000 }, + { 0x53f1, 0x0000 }, + { 0x53f2, 0x0000 }, + { 0x53f3, 0x0000 }, + { 0x53f4, 0x0000 }, + { 0x53f5, 0x0000 }, + { 0x53f6, 0x0000 }, + { 0x53f7, 0x0000 }, + { 0x53f8, 0x0000 }, + { 0x53f9, 0x0000 }, + { 0x53fa, 0x0000 }, + { 0x53fb, 0x0000 }, + { 0x53fc, 0x0000 }, + { 0x53fd, 0x0000 }, + { 0x53fe, 0x0000 }, + { 0x53ff, 0x0000 }, + { 0x5400, 0x0000 }, + { 0x5401, 0x0000 }, + { 0x5402, 0x0000 }, + { 0x5403, 0x0000 }, + { 0x5404, 0x0000 }, + { 0x5405, 0x0000 }, + { 0x5406, 0x0000 }, + { 0x5407, 0x0000 }, + { 0x5408, 0x0000 }, + { 0x5409, 0x0000 }, + { 0x540a, 0x0000 }, + { 0x540b, 0x0000 }, + { 0x540c, 0x0000 }, + { 0x540d, 0x0000 }, + { 0x540e, 0x0000 }, + { 0x540f, 0x0000 }, + { 0x5410, 0x0000 }, + { 0x5411, 0x0000 }, + { 0x5412, 0x0000 }, + { 0x5413, 0x0000 }, + { 0x5414, 0x0000 }, + { 0x5415, 0x0000 }, + { 0x5416, 0x0000 }, + { 0x5417, 0x0000 }, + { 0x5418, 0x0000 }, + { 0x5419, 0x0000 }, + { 0x541a, 0x0000 }, + { 0x541b, 0x0000 }, + { 0x541c, 0x0000 }, + { 0x541d, 0x0000 }, + { 0x541e, 0x0000 }, + { 0x541f, 0x0000 }, + { 0x5420, 0x0000 }, + { 0x5421, 0x0000 }, + { 0x5422, 0x0000 }, + { 0x5423, 0x0000 }, + { 0x5424, 0x0000 }, + { 0x5425, 0x0000 }, + { 0x5426, 0x0000 }, + { 0x5427, 0x0000 }, + { 0x5428, 0x0000 }, + { 0x5429, 0x0000 }, + { 0x542a, 0x0000 }, + { 0x542b, 0x0000 }, + { 0x542c, 0x0000 }, + { 0x542d, 0x0000 }, + { 0x542e, 0x0000 }, + { 0x542f, 0x0000 }, + { 0x5430, 0x0000 }, + { 0x5431, 0x0000 }, + { 0x5432, 0x0000 }, + { 0x5433, 0x0000 }, + { 0x5434, 0x0000 }, + { 0x5435, 0x0000 }, + { 0x5436, 0x0000 }, + { 0x5437, 0x0000 }, + { 0x5438, 0x0000 }, + { 0x5439, 0x0000 }, + { 0x543a, 0x0000 }, + { 0x543b, 0x0000 }, + { 0x543c, 0x0000 }, + { 0x543d, 0x0000 }, + { 0x543e, 0x0000 }, + { 0x543f, 0x0000 }, + { 0x5440, 0x0000 }, + { 0x5441, 0x0000 }, + { 0x5442, 0x0000 }, + { 0x5443, 0x0000 }, + { 0x5444, 0x0000 }, + { 0x5445, 0x0000 }, + { 0x5446, 0x0000 }, + { 0x5447, 0x0000 }, + { 0x5448, 0x0000 }, + { 0x5449, 0x0000 }, + { 0x544a, 0x0000 }, + { 0x544b, 0x0000 }, + { 0x544c, 0x0000 }, + { 0x544d, 0x0000 }, + { 0x544e, 0x0000 }, + { 0x544f, 0x0000 }, + { 0x5450, 0x0000 }, + { 0x5451, 0x0000 }, + { 0x5452, 0x0000 }, + { 0x5453, 0x0000 }, + { 0x5454, 0x0000 }, + { 0x5455, 0x0000 }, + { 0x5456, 0x0000 }, + { 0x5457, 0x0000 }, + { 0x5458, 0x0000 }, + { 0x5459, 0x0000 }, + { 0x545a, 0x0000 }, + { 0x545b, 0x0000 }, + { 0x545c, 0x0000 }, + { 0x545d, 0x0000 }, + { 0x545e, 0x0000 }, + { 0x545f, 0x0000 }, + { 0x5460, 0x0000 }, + { 0x5461, 0x0000 }, + { 0x5462, 0x0000 }, + { 0x5463, 0x0000 }, + { 0x5464, 0x0000 }, + { 0x5465, 0x0000 }, + { 0x5466, 0x0000 }, + { 0x5467, 0x0000 }, + { 0x5468, 0x0000 }, + { 0x5469, 0x0000 }, + { 0x546a, 0x0000 }, + { 0x546b, 0x0000 }, + { 0x546c, 0x0000 }, + { 0x546d, 0x0000 }, + { 0x546e, 0x0000 }, + { 0x546f, 0x0000 }, + { 0x5470, 0x0000 }, + { 0x5471, 0x0000 }, + { 0x5472, 0x0000 }, + { 0x5473, 0x0000 }, + { 0x5474, 0x0000 }, + { 0x5475, 0x0000 }, + { 0x5476, 0x0000 }, + { 0x5477, 0x0000 }, + { 0x5478, 0x0000 }, + { 0x5479, 0x0000 }, + { 0x547a, 0x0000 }, + { 0x547b, 0x0000 }, + { 0x547c, 0x0000 }, + { 0x547d, 0x0000 }, + { 0x547e, 0x0000 }, + { 0x547f, 0x0000 }, + { 0x5480, 0x0000 }, + { 0x5481, 0x0000 }, + { 0x5482, 0x0000 }, + { 0x5483, 0x0000 }, + { 0x5484, 0x0000 }, + { 0x5485, 0x0000 }, + { 0x5486, 0x0000 }, + { 0x5487, 0x0000 }, + { 0x5488, 0x0000 }, + { 0x5489, 0x0000 }, + { 0x548a, 0x0000 }, + { 0x548b, 0x0000 }, + { 0x548c, 0x0000 }, + { 0x548d, 0x0000 }, + { 0x548e, 0x0000 }, + { 0x548f, 0x0000 }, + { 0x5490, 0x0000 }, + { 0x5491, 0x0000 }, + { 0x5492, 0x0000 }, + { 0x5493, 0x0000 }, + { 0x5494, 0x0000 }, + { 0x5495, 0x0000 }, + { 0x5496, 0x0000 }, + { 0x5497, 0x0000 }, + { 0x5498, 0x0000 }, + { 0x5499, 0x0000 }, + { 0x549a, 0x0000 }, + { 0x549b, 0x0000 }, + { 0x549c, 0x0000 }, + { 0x549d, 0x0000 }, + { 0x549e, 0x0000 }, + { 0x549f, 0x0000 }, + { 0x54a0, 0x0000 }, + { 0x54a1, 0x0000 }, + { 0x54a2, 0x0000 }, + { 0x54a3, 0x0000 }, + { 0x54a4, 0x0000 }, + { 0x54a5, 0x0000 }, + { 0x54a6, 0x0000 }, + { 0x54a7, 0x0000 }, + { 0x54a8, 0x0000 }, + { 0x54a9, 0x0000 }, + { 0x54aa, 0x0000 }, + { 0x54ab, 0x0000 }, + { 0x54ac, 0x0000 }, + { 0x54ad, 0x0000 }, + { 0x54ae, 0x0000 }, + { 0x54af, 0x0000 }, + { 0x54b0, 0x0000 }, + { 0x54b1, 0x0000 }, + { 0x54b2, 0x0000 }, + { 0x54b3, 0x0000 }, + { 0x54b4, 0x0000 }, + { 0x54b5, 0x0000 }, + { 0x54b6, 0x0000 }, + { 0x54b7, 0x0000 }, + { 0x54b8, 0x0000 }, + { 0x54b9, 0x0000 }, + { 0x54ba, 0x0000 }, + { 0x54bb, 0x0000 }, + { 0x54bc, 0x0000 }, + { 0x54bd, 0x0000 }, + { 0x54be, 0x0000 }, + { 0x54bf, 0x0000 }, + { 0x54c0, 0x0000 }, + { 0x54c1, 0x0000 }, + { 0x54c2, 0x0000 }, + { 0x54c3, 0x0000 }, + { 0x54c4, 0x0000 }, + { 0x54c5, 0x0000 }, + { 0x54c6, 0x0000 }, + { 0x54c7, 0x0000 }, + { 0x54c8, 0x0000 }, + { 0x54c9, 0x0000 }, + { 0x54ca, 0x0000 }, + { 0x54cb, 0x0000 }, + { 0x54cc, 0x0000 }, + { 0x54cd, 0x0000 }, + { 0x54ce, 0x0000 }, + { 0x54cf, 0x0000 }, + { 0x54d0, 0x0000 }, + { 0x54d1, 0x0000 }, + { 0x54d2, 0x0000 }, + { 0x54d3, 0x0000 }, + { 0x54d4, 0x0000 }, + { 0x54d5, 0x0000 }, + { 0x54d6, 0x0000 }, + { 0x54d7, 0x0000 }, + { 0x54d8, 0x0000 }, + { 0x54d9, 0x0000 }, + { 0x54da, 0x0000 }, + { 0x54db, 0x0000 }, + { 0x54dc, 0x0000 }, + { 0x54dd, 0x0000 }, + { 0x54de, 0x0000 }, + { 0x54df, 0x0000 }, + { 0x54e0, 0x0000 }, + { 0x54e1, 0x0000 }, + { 0x54e2, 0x0000 }, + { 0x54e3, 0x0000 }, + { 0x54e4, 0x0000 }, + { 0x54e5, 0x0000 }, + { 0x54e6, 0x0000 }, + { 0x54e7, 0x0000 }, + { 0x54e8, 0x0000 }, + { 0x54e9, 0x0000 }, + { 0x54ea, 0x0000 }, + { 0x54eb, 0x0000 }, + { 0x54ec, 0x0000 }, + { 0x54ed, 0x0000 }, + { 0x54ee, 0x0000 }, + { 0x54ef, 0x0000 }, + { 0x54f0, 0x0000 }, + { 0x54f1, 0x0000 }, + { 0x54f2, 0x0000 }, + { 0x54f3, 0x0000 }, + { 0x54f4, 0x0000 }, + { 0x54f5, 0x0000 }, + { 0x54f6, 0x0000 }, + { 0x54f7, 0x0000 }, + { 0x54f8, 0x0000 }, + { 0x54f9, 0x0000 }, + { 0x54fa, 0x0000 }, + { 0x54fb, 0x0000 }, + { 0x54fc, 0x0000 }, + { 0x54fd, 0x0000 }, + { 0x54fe, 0x0000 }, + { 0x54ff, 0x0000 }, + { 0x5500, 0x0000 }, + { 0x5501, 0x0000 }, + { 0x5502, 0x0000 }, + { 0x5503, 0x0000 }, + { 0x5504, 0x0000 }, + { 0x5505, 0x0000 }, + { 0x5506, 0x0000 }, + { 0x5507, 0x0000 }, + { 0x5508, 0x0000 }, + { 0x5509, 0x0000 }, + { 0x550a, 0x0000 }, + { 0x550b, 0x0000 }, + { 0x550c, 0x0000 }, + { 0x550d, 0x0000 }, + { 0x550e, 0x0000 }, + { 0x550f, 0x0000 }, + { 0x5510, 0x0000 }, + { 0x5511, 0x0000 }, + { 0x5512, 0x0000 }, + { 0x5513, 0x0000 }, + { 0x5514, 0x0000 }, + { 0x5515, 0x0000 }, + { 0x5516, 0x0000 }, + { 0x5517, 0x0000 }, + { 0x5518, 0x0000 }, + { 0x5519, 0x0000 }, + { 0x551a, 0x0000 }, + { 0x551b, 0x0000 }, + { 0x551c, 0x0000 }, + { 0x551d, 0x0000 }, + { 0x551e, 0x0000 }, + { 0x551f, 0x0000 }, + { 0x5520, 0x0000 }, + { 0x5521, 0x0000 }, + { 0x5522, 0x0000 }, + { 0x5523, 0x0000 }, + { 0x5524, 0x0000 }, + { 0x5525, 0x0000 }, + { 0x5526, 0x0000 }, + { 0x5527, 0x0000 }, + { 0x5528, 0x0000 }, + { 0x5529, 0x0000 }, + { 0x552a, 0x0000 }, + { 0x552b, 0x0000 }, + { 0x552c, 0x0000 }, + { 0x552d, 0x0000 }, + { 0x552e, 0x0000 }, + { 0x552f, 0x0000 }, + { 0x5530, 0x0000 }, + { 0x5531, 0x0000 }, + { 0x5532, 0x0000 }, + { 0x5533, 0x0000 }, + { 0x5534, 0x0000 }, + { 0x5535, 0x0000 }, + { 0x5536, 0x0000 }, + { 0x5537, 0x0000 }, + { 0x5538, 0x0000 }, + { 0x5539, 0x0000 }, + { 0x553a, 0x0000 }, + { 0x553b, 0x0000 }, + { 0x553c, 0x0000 }, + { 0x553d, 0x0000 }, + { 0x553e, 0x0000 }, + { 0x553f, 0x0000 }, + { 0x5540, 0x0000 }, + { 0x5541, 0x0000 }, + { 0x5542, 0x0000 }, + { 0x5543, 0x0000 }, + { 0x5544, 0x0000 }, + { 0x5545, 0x0000 }, + { 0x5546, 0x0000 }, + { 0x5547, 0x0000 }, + { 0x5548, 0x0000 }, + { 0x5549, 0x0000 }, + { 0x554a, 0x0000 }, + { 0x554b, 0x0000 }, + { 0x554c, 0x0000 }, + { 0x554d, 0x0000 }, + { 0x554e, 0x0000 }, + { 0x554f, 0x0000 }, + { 0x5550, 0x0000 }, + { 0x5551, 0x0000 }, + { 0x5552, 0x0000 }, + { 0x5553, 0x0000 }, + { 0x5554, 0x0000 }, + { 0x5555, 0x0000 }, + { 0x5556, 0x0000 }, + { 0x5557, 0x0000 }, + { 0x5558, 0x0000 }, + { 0x5559, 0x0000 }, + { 0x555a, 0x0000 }, + { 0x555b, 0x0000 }, + { 0x555c, 0x0000 }, + { 0x555d, 0x0000 }, + { 0x555e, 0x0000 }, + { 0x555f, 0x0000 }, + { 0x5560, 0x0000 }, + { 0x5561, 0x0000 }, + { 0x5562, 0x0000 }, + { 0x5563, 0x0000 }, + { 0x5564, 0x0000 }, + { 0x5565, 0x0000 }, + { 0x5566, 0x0000 }, + { 0x5567, 0x0000 }, + { 0x5568, 0x0000 }, + { 0x5569, 0x0000 }, + { 0x556a, 0x0000 }, + { 0x556b, 0x0000 }, + { 0x556c, 0x0000 }, + { 0x556d, 0x0000 }, + { 0x556e, 0x0000 }, + { 0x556f, 0x0000 }, + { 0x5570, 0x0000 }, + { 0x5571, 0x0000 }, + { 0x5572, 0x0000 }, + { 0x5573, 0x0000 }, + { 0x5574, 0x0000 }, + { 0x5575, 0x0000 }, + { 0x5576, 0x0000 }, + { 0x5577, 0x0000 }, + { 0x5578, 0x0000 }, + { 0x5579, 0x0000 }, + { 0x557a, 0x0000 }, + { 0x557b, 0x0000 }, + { 0x557c, 0x0000 }, + { 0x557d, 0x0000 }, + { 0x557e, 0x0000 }, + { 0x557f, 0x0000 }, + { 0x5580, 0x0000 }, + { 0x5581, 0x0000 }, + { 0x5582, 0x0000 }, + { 0x5583, 0x0000 }, + { 0x5584, 0x0000 }, + { 0x5585, 0x0000 }, + { 0x5586, 0x0000 }, + { 0x5587, 0x0000 }, + { 0x5588, 0x0000 }, + { 0x5589, 0x0000 }, + { 0x558a, 0x0000 }, + { 0x558b, 0x0000 }, + { 0x558c, 0x0000 }, + { 0x558d, 0x0000 }, + { 0x558e, 0x0000 }, + { 0x558f, 0x0000 }, + { 0x5590, 0x0000 }, + { 0x5591, 0x0000 }, + { 0x5592, 0x0000 }, + { 0x5593, 0x0000 }, + { 0x5594, 0x0000 }, + { 0x5595, 0x0000 }, + { 0x5596, 0x0000 }, + { 0x5597, 0x0000 }, + { 0x5598, 0x0000 }, + { 0x5599, 0x0000 }, + { 0x559a, 0x0000 }, + { 0x559b, 0x0000 }, + { 0x559c, 0x0000 }, + { 0x559d, 0x0000 }, + { 0x559e, 0x0000 }, + { 0x559f, 0x0000 }, + { 0x55a0, 0x0000 }, + { 0x55a1, 0x0000 }, + { 0x55a2, 0x0000 }, + { 0x55a3, 0x0000 }, + { 0x55a4, 0x0000 }, + { 0x55a5, 0x0000 }, + { 0x55a6, 0x0000 }, + { 0x55a7, 0x0000 }, + { 0x55a8, 0x0000 }, + { 0x55a9, 0x0000 }, + { 0x55aa, 0x0000 }, + { 0x55ab, 0x0000 }, + { 0x55ac, 0x0000 }, + { 0x55ad, 0x0000 }, + { 0x55ae, 0x0000 }, + { 0x55af, 0x0000 }, + { 0x55b0, 0x0000 }, + { 0x55b1, 0x0000 }, + { 0x55b2, 0x0000 }, + { 0x55b3, 0x0000 }, + { 0x55b4, 0x0000 }, + { 0x55b5, 0x0000 }, + { 0x55b6, 0x0000 }, + { 0x55b7, 0x0000 }, + { 0x55b8, 0x0000 }, + { 0x55b9, 0x0000 }, + { 0x55ba, 0x0000 }, + { 0x55bb, 0x0000 }, + { 0x55bc, 0x0000 }, + { 0x55bd, 0x0000 }, + { 0x55be, 0x0000 }, + { 0x55bf, 0x0000 }, + { 0x55c0, 0x0000 }, + { 0x55c1, 0x0000 }, + { 0x55c2, 0x0000 }, + { 0x55c3, 0x0000 }, + { 0x55c4, 0x0000 }, + { 0x55c5, 0x0000 }, + { 0x55c6, 0x0000 }, + { 0x55c7, 0x0000 }, + { 0x55c8, 0x0000 }, + { 0x55c9, 0x0000 }, + { 0x55ca, 0x0000 }, + { 0x55cb, 0x0000 }, + { 0x55cc, 0x0000 }, + { 0x55cd, 0x0000 }, + { 0x55ce, 0x0000 }, + { 0x55cf, 0x0000 }, + { 0x55d0, 0x0000 }, + { 0x55d1, 0x0000 }, + { 0x55d2, 0x0000 }, + { 0x55d3, 0x0000 }, + { 0x55d4, 0x0000 }, + { 0x55d5, 0x0000 }, + { 0x55d6, 0x0000 }, + { 0x55d7, 0x0000 }, + { 0x55d8, 0x0000 }, + { 0x55d9, 0x0000 }, + { 0x55da, 0x0000 }, + { 0x55db, 0x0000 }, + { 0x55dc, 0x0000 }, + { 0x55dd, 0x0000 }, + { 0x55de, 0x0000 }, + { 0x55df, 0x0000 }, + { 0x55e0, 0x0000 }, + { 0x55e1, 0x0000 }, + { 0x55e2, 0x0000 }, + { 0x55e3, 0x0000 }, + { 0x55e4, 0x0000 }, + { 0x55e5, 0x0000 }, + { 0x55e6, 0x0000 }, + { 0x55e7, 0x0000 }, + { 0x55e8, 0x0000 }, + { 0x55e9, 0x0000 }, + { 0x55ea, 0x0000 }, + { 0x55eb, 0x0000 }, + { 0x55ec, 0x0000 }, + { 0x55ed, 0x0000 }, + { 0x55ee, 0x0000 }, + { 0x55ef, 0x0000 }, + { 0x55f0, 0x0000 }, + { 0x55f1, 0x0000 }, + { 0x55f2, 0x0000 }, + { 0x55f3, 0x0000 }, + { 0x55f4, 0x0000 }, + { 0x55f5, 0x0000 }, + { 0x55f6, 0x0000 }, + { 0x55f7, 0x0000 }, + { 0x55f8, 0x0000 }, + { 0x55f9, 0x0000 }, + { 0x55fa, 0x0000 }, + { 0x55fb, 0x0000 }, + { 0x55fc, 0x0000 }, + { 0x55fd, 0x0000 }, + { 0x55fe, 0x0000 }, + { 0x55ff, 0x0000 }, + { 0x5600, 0x0000 }, + { 0x5601, 0x0000 }, + { 0x5602, 0x0000 }, + { 0x5603, 0x0000 }, + { 0x5604, 0x0000 }, + { 0x5605, 0x0000 }, + { 0x5606, 0x0000 }, + { 0x5607, 0x0000 }, + { 0x5608, 0x0000 }, + { 0x5609, 0x0000 }, + { 0x560a, 0x0000 }, + { 0x560b, 0x0000 }, + { 0x560c, 0x0000 }, + { 0x560d, 0x0000 }, + { 0x560e, 0x0000 }, + { 0x560f, 0x0000 }, + { 0x5610, 0x0000 }, + { 0x5611, 0x0000 }, + { 0x5612, 0x0000 }, + { 0x5613, 0x0000 }, + { 0x5614, 0x0000 }, + { 0x5615, 0x0000 }, + { 0x5616, 0x0000 }, + { 0x5617, 0x0000 }, + { 0x5618, 0x0000 }, + { 0x5619, 0x0000 }, + { 0x561a, 0x0000 }, + { 0x561b, 0x0000 }, + { 0x561c, 0x0000 }, + { 0x561d, 0x0000 }, + { 0x561e, 0x0000 }, + { 0x561f, 0x0000 }, + { 0x5620, 0x0000 }, + { 0x5621, 0x0000 }, + { 0x5622, 0x0000 }, + { 0x5623, 0x0000 }, + { 0x5624, 0x0000 }, + { 0x5625, 0x0000 }, + { 0x5626, 0x0000 }, + { 0x5627, 0x0000 }, + { 0x5628, 0x0000 }, + { 0x5629, 0x0000 }, + { 0x562a, 0x0000 }, + { 0x562b, 0x0000 }, + { 0x562c, 0x0000 }, + { 0x562d, 0x0000 }, + { 0x562e, 0x0000 }, + { 0x562f, 0x0000 }, + { 0x5630, 0x0000 }, + { 0x5631, 0x0000 }, + { 0x5632, 0x0000 }, + { 0x5633, 0x0000 }, + { 0x5634, 0x0000 }, + { 0x5635, 0x0000 }, + { 0x5636, 0x0000 }, + { 0x5637, 0x0000 }, + { 0x5638, 0x0000 }, + { 0x5639, 0x0000 }, + { 0x563a, 0x0000 }, + { 0x563b, 0x0000 }, + { 0x563c, 0x0000 }, + { 0x563d, 0x0000 }, + { 0x563e, 0x0000 }, + { 0x563f, 0x0000 }, + { 0x5640, 0x0000 }, + { 0x5641, 0x0000 }, + { 0x5642, 0x0000 }, + { 0x5643, 0x0000 }, + { 0x5644, 0x0000 }, + { 0x5645, 0x0000 }, + { 0x5646, 0x0000 }, + { 0x5647, 0x0000 }, + { 0x5648, 0x0000 }, + { 0x5649, 0x0000 }, + { 0x564a, 0x0000 }, + { 0x564b, 0x0000 }, + { 0x564c, 0x0000 }, + { 0x564d, 0x0000 }, + { 0x564e, 0x0000 }, + { 0x564f, 0x0000 }, + { 0x5650, 0x0000 }, + { 0x5651, 0x0000 }, + { 0x5652, 0x0000 }, + { 0x5653, 0x0000 }, + { 0x5654, 0x0000 }, + { 0x5655, 0x0000 }, + { 0x5656, 0x0000 }, + { 0x5657, 0x0000 }, + { 0x5658, 0x0000 }, + { 0x5659, 0x0000 }, + { 0x565a, 0x0000 }, + { 0x565b, 0x0000 }, + { 0x565c, 0x0000 }, + { 0x565d, 0x0000 }, + { 0x565e, 0x0000 }, + { 0x565f, 0x0000 }, + { 0x5660, 0x0000 }, + { 0x5661, 0x0000 }, + { 0x5662, 0x0000 }, + { 0x5663, 0x0000 }, + { 0x5664, 0x0000 }, + { 0x5665, 0x0000 }, + { 0x5666, 0x0000 }, + { 0x5667, 0x0000 }, + { 0x5668, 0x0000 }, + { 0x5669, 0x0000 }, + { 0x566a, 0x0000 }, + { 0x566b, 0x0000 }, + { 0x566c, 0x0000 }, + { 0x566d, 0x0000 }, + { 0x566e, 0x0000 }, + { 0x566f, 0x0000 }, + { 0x5670, 0x0000 }, + { 0x5671, 0x0000 }, + { 0x5672, 0x0000 }, + { 0x5673, 0x0000 }, + { 0x5674, 0x0000 }, + { 0x5675, 0x0000 }, + { 0x5676, 0x0000 }, + { 0x5677, 0x0000 }, + { 0x5678, 0x0000 }, + { 0x5679, 0x0000 }, + { 0x567a, 0x0000 }, + { 0x567b, 0x0000 }, + { 0x567c, 0x0000 }, + { 0x567d, 0x0000 }, + { 0x567e, 0x0000 }, + { 0x567f, 0x0000 }, + { 0x5680, 0x0000 }, + { 0x5681, 0x0000 }, + { 0x5682, 0x0000 }, + { 0x5683, 0x0000 }, + { 0x5684, 0x0000 }, + { 0x5685, 0x0000 }, + { 0x5686, 0x0000 }, + { 0x5687, 0x0000 }, + { 0x5688, 0x0000 }, + { 0x5689, 0x0000 }, + { 0x568a, 0x0000 }, + { 0x568b, 0x0000 }, + { 0x568c, 0x0000 }, + { 0x568d, 0x0000 }, + { 0x568e, 0x0000 }, + { 0x568f, 0x0000 }, + { 0x5690, 0x0000 }, + { 0x5691, 0x0000 }, + { 0x5692, 0x0000 }, + { 0x5693, 0x0000 }, + { 0x5694, 0x0000 }, + { 0x5695, 0x0000 }, + { 0x5696, 0x0000 }, + { 0x5697, 0x0000 }, + { 0x5698, 0x0000 }, + { 0x5699, 0x0000 }, + { 0x569a, 0x0000 }, + { 0x569b, 0x0000 }, + { 0x569c, 0x0000 }, + { 0x569d, 0x0000 }, + { 0x569e, 0x0000 }, + { 0x569f, 0x0000 }, + { 0x56a0, 0x0000 }, + { 0x56a1, 0x0000 }, + { 0x56a2, 0x0000 }, + { 0x56a3, 0x0000 }, + { 0x56a4, 0x0000 }, + { 0x56a5, 0x0000 }, + { 0x56a6, 0x0000 }, + { 0x56a7, 0x0000 }, + { 0x56a8, 0x0000 }, + { 0x56a9, 0x0000 }, + { 0x56aa, 0x0000 }, + { 0x56ab, 0x0000 }, + { 0x56ac, 0x0000 }, + { 0x56ad, 0x0000 }, + { 0x56ae, 0x0000 }, + { 0x56af, 0x0000 }, + { 0x56b0, 0x0000 }, + { 0x56b1, 0x0000 }, + { 0x56b2, 0x0000 }, + { 0x56b3, 0x0000 }, + { 0x56b4, 0x0000 }, + { 0x56b5, 0x0000 }, + { 0x56b6, 0x0000 }, + { 0x56b7, 0x0000 }, + { 0x56b8, 0x0000 }, + { 0x56b9, 0x0000 }, + { 0x56ba, 0x0000 }, + { 0x56bb, 0x0000 }, + { 0x56bc, 0x0000 }, + { 0x56bd, 0x0000 }, + { 0x56be, 0x0000 }, + { 0x56bf, 0x0000 }, + { 0x56c0, 0x0000 }, + { 0x56c1, 0x0000 }, + { 0x56c2, 0x0000 }, + { 0x56c3, 0x0000 }, + { 0x56c4, 0x0000 }, + { 0x56c5, 0x0000 }, + { 0x56c6, 0x0000 }, + { 0x56c7, 0x0000 }, + { 0x56c8, 0x0000 }, + { 0x56c9, 0x0000 }, + { 0x56ca, 0x0000 }, + { 0x56cb, 0x0000 }, + { 0x56cc, 0x0000 }, + { 0x56cd, 0x0000 }, + { 0x56ce, 0x0000 }, + { 0x56cf, 0x0000 }, + { 0x56d0, 0x0000 }, + { 0x56d1, 0x0000 }, + { 0x56d2, 0x0000 }, + { 0x56d3, 0x0000 }, + { 0x56d4, 0x0000 }, + { 0x56d5, 0x0000 }, + { 0x56d6, 0x0000 }, + { 0x56d7, 0x0000 }, + { 0x56d8, 0x0000 }, + { 0x56d9, 0x0000 }, + { 0x56da, 0x0000 }, + { 0x56db, 0x0000 }, + { 0x56dc, 0x0000 }, + { 0x56dd, 0x0000 }, + { 0x56de, 0x0000 }, + { 0x56df, 0x0000 }, + { 0x56e0, 0x0000 }, + { 0x56e1, 0x0000 }, + { 0x56e2, 0x0000 }, + { 0x56e3, 0x0000 }, + { 0x56e4, 0x0000 }, + { 0x56e5, 0x0000 }, + { 0x56e6, 0x0000 }, + { 0x56e7, 0x0000 }, + { 0x56e8, 0x0000 }, + { 0x56e9, 0x0000 }, + { 0x56ea, 0x0000 }, + { 0x56eb, 0x0000 }, + { 0x56ec, 0x0000 }, + { 0x56ed, 0x0000 }, + { 0x56ee, 0x0000 }, + { 0x56ef, 0x0000 }, + { 0x56f0, 0x0000 }, + { 0x56f1, 0x0000 }, + { 0x56f2, 0x0000 }, + { 0x56f3, 0x0000 }, + { 0x56f4, 0x0000 }, + { 0x56f5, 0x0000 }, + { 0x56f6, 0x0000 }, + { 0x56f7, 0x0000 }, + { 0x56f8, 0x0000 }, + { 0x56f9, 0x0000 }, + { 0x56fa, 0x0000 }, + { 0x56fb, 0x0000 }, + { 0x56fc, 0x0000 }, + { 0x56fd, 0x0000 }, + { 0x56fe, 0x0000 }, + { 0x56ff, 0x0000 }, + { 0x5700, 0x0000 }, + { 0x5701, 0x0000 }, + { 0x5702, 0x0000 }, + { 0x5703, 0x0000 }, + { 0x5704, 0x0000 }, + { 0x5705, 0x0000 }, + { 0x5706, 0x0000 }, + { 0x5707, 0x0000 }, + { 0x5708, 0x0000 }, + { 0x5709, 0x0000 }, + { 0x570a, 0x0000 }, + { 0x570b, 0x0000 }, + { 0x570c, 0x0000 }, + { 0x570d, 0x0000 }, + { 0x570e, 0x0000 }, + { 0x570f, 0x0000 }, + { 0x5710, 0x0000 }, + { 0x5711, 0x0000 }, + { 0x5712, 0x0000 }, + { 0x5713, 0x0000 }, + { 0x5714, 0x0000 }, + { 0x5715, 0x0000 }, + { 0x5716, 0x0000 }, + { 0x5717, 0x0000 }, + { 0x5718, 0x0000 }, + { 0x5719, 0x0000 }, + { 0x571a, 0x0000 }, + { 0x571b, 0x0000 }, + { 0x571c, 0x0000 }, + { 0x571d, 0x0000 }, + { 0x571e, 0x0000 }, + { 0x571f, 0x0000 }, + { 0x5720, 0x0000 }, + { 0x5721, 0x0000 }, + { 0x5722, 0x0000 }, + { 0x5723, 0x0000 }, + { 0x5724, 0x0000 }, + { 0x5725, 0x0000 }, + { 0x5726, 0x0000 }, + { 0x5727, 0x0000 }, + { 0x5728, 0x0000 }, + { 0x5729, 0x0000 }, + { 0x572a, 0x0000 }, + { 0x572b, 0x0000 }, + { 0x572c, 0x0000 }, + { 0x572d, 0x0000 }, + { 0x572e, 0x0000 }, + { 0x572f, 0x0000 }, + { 0x5730, 0x0000 }, + { 0x5731, 0x0000 }, + { 0x5732, 0x0000 }, + { 0x5733, 0x0000 }, + { 0x5734, 0x0000 }, + { 0x5735, 0x0000 }, + { 0x5736, 0x0000 }, + { 0x5737, 0x0000 }, + { 0x5738, 0x0000 }, + { 0x5739, 0x0000 }, + { 0x573a, 0x0000 }, + { 0x573b, 0x0000 }, + { 0x573c, 0x0000 }, + { 0x573d, 0x0000 }, + { 0x573e, 0x0000 }, + { 0x573f, 0x0000 }, + { 0x5740, 0x0000 }, + { 0x5741, 0x0000 }, + { 0x5742, 0x0000 }, + { 0x5743, 0x0000 }, + { 0x5744, 0x0000 }, + { 0x5745, 0x0000 }, + { 0x5746, 0x0000 }, + { 0x5747, 0x0000 }, + { 0x5748, 0x0000 }, + { 0x5749, 0x0000 }, + { 0x574a, 0x0000 }, + { 0x574b, 0x0000 }, + { 0x574c, 0x0000 }, + { 0x574d, 0x0000 }, + { 0x574e, 0x0000 }, + { 0x574f, 0x0000 }, + { 0x5750, 0x0000 }, + { 0x5751, 0x0000 }, + { 0x5752, 0x0000 }, + { 0x5753, 0x0000 }, + { 0x5754, 0x0000 }, + { 0x5755, 0x0000 }, + { 0x5756, 0x0000 }, + { 0x5757, 0x0000 }, + { 0x5758, 0x0000 }, + { 0x5759, 0x0000 }, + { 0x575a, 0x0000 }, + { 0x575b, 0x0000 }, + { 0x575c, 0x0000 }, + { 0x575d, 0x0000 }, + { 0x575e, 0x0000 }, + { 0x575f, 0x0000 }, + { 0x5760, 0x0000 }, + { 0x5761, 0x0000 }, + { 0x5762, 0x0000 }, + { 0x5763, 0x0000 }, + { 0x5764, 0x0000 }, + { 0x5765, 0x0000 }, + { 0x5766, 0x0000 }, + { 0x5767, 0x0000 }, + { 0x5768, 0x0000 }, + { 0x5769, 0x0000 }, + { 0x576a, 0x0000 }, + { 0x576b, 0x0000 }, + { 0x576c, 0x0000 }, + { 0x576d, 0x0000 }, + { 0x576e, 0x0000 }, + { 0x576f, 0x0000 }, + { 0x5770, 0x0000 }, + { 0x5771, 0x0000 }, + { 0x5772, 0x0000 }, + { 0x5773, 0x0000 }, + { 0x5774, 0x0000 }, + { 0x5775, 0x0000 }, + { 0x5776, 0x0000 }, + { 0x5777, 0x0000 }, + { 0x5778, 0x0000 }, + { 0x5779, 0x0000 }, + { 0x577a, 0x0000 }, + { 0x577b, 0x0000 }, + { 0x577c, 0x0000 }, + { 0x577d, 0x0000 }, + { 0x577e, 0x0000 }, + { 0x577f, 0x0000 }, + { 0x5780, 0x0000 }, + { 0x5781, 0x0000 }, + { 0x5782, 0x0000 }, + { 0x5783, 0x0000 }, + { 0x5784, 0x0000 }, + { 0x5785, 0x0000 }, + { 0x5786, 0x0000 }, + { 0x5787, 0x0000 }, + { 0x5788, 0x0000 }, + { 0x5789, 0x0000 }, + { 0x578a, 0x0000 }, + { 0x578b, 0x0000 }, + { 0x578c, 0x0000 }, + { 0x578d, 0x0000 }, + { 0x578e, 0x0000 }, + { 0x578f, 0x0000 }, + { 0x5790, 0x0000 }, + { 0x5791, 0x0000 }, + { 0x5792, 0x0000 }, + { 0x5793, 0x0000 }, + { 0x5794, 0x0000 }, + { 0x5795, 0x0000 }, + { 0x5796, 0x0000 }, + { 0x5797, 0x0000 }, + { 0x5798, 0x0000 }, + { 0x5799, 0x0000 }, + { 0x579a, 0x0000 }, + { 0x579b, 0x0000 }, + { 0x579c, 0x0000 }, + { 0x579d, 0x0000 }, + { 0x579e, 0x0000 }, + { 0x579f, 0x0000 }, + { 0x57a0, 0x0000 }, + { 0x57a1, 0x0000 }, + { 0x57a2, 0x0000 }, + { 0x57a3, 0x0000 }, + { 0x57a4, 0x0000 }, + { 0x57a5, 0x0000 }, + { 0x57a6, 0x0000 }, + { 0x57a7, 0x0000 }, + { 0x57a8, 0x0000 }, + { 0x57a9, 0x0000 }, + { 0x57aa, 0x0000 }, + { 0x57ab, 0x0000 }, + { 0x57ac, 0x0000 }, + { 0x57ad, 0x0000 }, + { 0x57ae, 0x0000 }, + { 0x57af, 0x0000 }, + { 0x57b0, 0x0000 }, + { 0x57b1, 0x0000 }, + { 0x57b2, 0x0000 }, + { 0x57b3, 0x0000 }, + { 0x57b4, 0x0000 }, + { 0x57b5, 0x0000 }, + { 0x57b6, 0x0000 }, + { 0x57b7, 0x0000 }, + { 0x57b8, 0x0000 }, + { 0x57b9, 0x0000 }, + { 0x57ba, 0x0000 }, + { 0x57bb, 0x0000 }, + { 0x57bc, 0x0000 }, + { 0x57bd, 0x0000 }, + { 0x57be, 0x0000 }, + { 0x57bf, 0x0000 }, + { 0x57c0, 0x0000 }, + { 0x57c1, 0x0000 }, + { 0x57c2, 0x0000 }, + { 0x57c3, 0x0000 }, + { 0x57c4, 0x0000 }, + { 0x57c5, 0x0000 }, + { 0x57c6, 0x0000 }, + { 0x57c7, 0x0000 }, + { 0x57c8, 0x0000 }, + { 0x57c9, 0x0000 }, + { 0x57ca, 0x0000 }, + { 0x57cb, 0x0000 }, + { 0x57cc, 0x0000 }, + { 0x57cd, 0x0000 }, + { 0x57ce, 0x0000 }, + { 0x57cf, 0x0000 }, + { 0x57d0, 0x0000 }, + { 0x57d1, 0x0000 }, + { 0x57d2, 0x0000 }, + { 0x57d3, 0x0000 }, + { 0x57d4, 0x0000 }, + { 0x57d5, 0x0000 }, + { 0x57d6, 0x0000 }, + { 0x57d7, 0x0000 }, + { 0x57d8, 0x0000 }, + { 0x57d9, 0x0000 }, + { 0x57da, 0x0000 }, + { 0x57db, 0x0000 }, + { 0x57dc, 0x0000 }, + { 0x57dd, 0x0000 }, + { 0x57de, 0x0000 }, + { 0x57df, 0x0000 }, + { 0x57e0, 0x0000 }, + { 0x57e1, 0x0000 }, + { 0x57e2, 0x0000 }, + { 0x57e3, 0x0000 }, + { 0x57e4, 0x0000 }, + { 0x57e5, 0x0000 }, + { 0x57e6, 0x0000 }, + { 0x57e7, 0x0000 }, + { 0x57e8, 0x0000 }, + { 0x57e9, 0x0000 }, + { 0x57ea, 0x0000 }, + { 0x57eb, 0x0000 }, + { 0x57ec, 0x0000 }, + { 0x57ed, 0x0000 }, + { 0x57ee, 0x0000 }, + { 0x57ef, 0x0000 }, + { 0x57f0, 0x0000 }, + { 0x57f1, 0x0000 }, + { 0x57f2, 0x0000 }, + { 0x57f3, 0x0000 }, + { 0x57f4, 0x0000 }, + { 0x57f5, 0x0000 }, + { 0x57f6, 0x0000 }, + { 0x57f7, 0x0000 }, + { 0x57f8, 0x0000 }, + { 0x57f9, 0x0000 }, + { 0x57fa, 0x0000 }, + { 0x57fb, 0x0000 }, + { 0x57fc, 0x0000 }, + { 0x57fd, 0x0000 }, + { 0x57fe, 0x0000 }, + { 0x57ff, 0x0000 }, + { 0x5800, 0x0000 }, + { 0x5801, 0x0000 }, + { 0x5802, 0x0000 }, + { 0x5803, 0x0000 }, + { 0x5804, 0x0000 }, + { 0x5805, 0x0000 }, + { 0x5806, 0x0000 }, + { 0x5807, 0x0000 }, + { 0x5808, 0x0000 }, + { 0x5809, 0x0000 }, + { 0x580a, 0x0000 }, + { 0x580b, 0x0000 }, + { 0x580c, 0x0000 }, + { 0x580d, 0x0000 }, + { 0x580e, 0x0000 }, + { 0x580f, 0x0000 }, + { 0x5810, 0x0000 }, + { 0x5811, 0x0000 }, + { 0x5812, 0x0000 }, + { 0x5813, 0x0000 }, + { 0x5814, 0x0000 }, + { 0x5815, 0x0000 }, + { 0x5816, 0x0000 }, + { 0x5817, 0x0000 }, + { 0x5818, 0x0000 }, + { 0x5819, 0x0000 }, + { 0x581a, 0x0000 }, + { 0x581b, 0x0000 }, + { 0x581c, 0x0000 }, + { 0x581d, 0x0000 }, + { 0x581e, 0x0000 }, + { 0x581f, 0x0000 }, + { 0x5820, 0x0000 }, + { 0x5821, 0x0000 }, + { 0x5822, 0x0000 }, + { 0x5823, 0x0000 }, + { 0x5824, 0x0000 }, + { 0x5825, 0x0000 }, + { 0x5826, 0x0000 }, + { 0x5827, 0x0000 }, + { 0x5828, 0x0000 }, + { 0x5829, 0x0000 }, + { 0x582a, 0x0000 }, + { 0x582b, 0x0000 }, + { 0x582c, 0x0000 }, + { 0x582d, 0x0000 }, + { 0x582e, 0x0000 }, + { 0x582f, 0x0000 }, + { 0x5830, 0x0000 }, + { 0x5831, 0x0000 }, + { 0x5832, 0x0000 }, + { 0x5833, 0x0000 }, + { 0x5834, 0x0000 }, + { 0x5835, 0x0000 }, + { 0x5836, 0x0000 }, + { 0x5837, 0x0000 }, + { 0x5838, 0x0000 }, + { 0x5839, 0x0000 }, + { 0x583a, 0x0000 }, + { 0x583b, 0x0000 }, + { 0x583c, 0x0000 }, + { 0x583d, 0x0000 }, + { 0x583e, 0x0000 }, + { 0x583f, 0x0000 }, + { 0x5840, 0x0000 }, + { 0x5841, 0x0000 }, + { 0x5842, 0x0000 }, + { 0x5843, 0x0000 }, + { 0x5844, 0x0000 }, + { 0x5845, 0x0000 }, + { 0x5846, 0x0000 }, + { 0x5847, 0x0000 }, + { 0x5848, 0x0000 }, + { 0x5849, 0x0000 }, + { 0x584a, 0x0000 }, + { 0x584b, 0x0000 }, + { 0x584c, 0x0000 }, + { 0x584d, 0x0000 }, + { 0x584e, 0x0000 }, + { 0x584f, 0x0000 }, + { 0x5850, 0x0000 }, + { 0x5851, 0x0000 }, + { 0x5852, 0x0000 }, + { 0x5853, 0x0000 }, + { 0x5854, 0x0000 }, + { 0x5855, 0x0000 }, + { 0x5856, 0x0000 }, + { 0x5857, 0x0000 }, + { 0x5858, 0x0000 }, + { 0x5859, 0x0000 }, + { 0x585a, 0x0000 }, + { 0x585b, 0x0000 }, + { 0x585c, 0x0000 }, + { 0x585d, 0x0000 }, + { 0x585e, 0x0000 }, + { 0x585f, 0x0000 }, + { 0x5860, 0x0000 }, + { 0x5861, 0x0000 }, + { 0x5862, 0x0000 }, + { 0x5863, 0x0000 }, + { 0x5864, 0x0000 }, + { 0x5865, 0x0000 }, + { 0x5866, 0x0000 }, + { 0x5867, 0x0000 }, + { 0x5868, 0x0000 }, + { 0x5869, 0x0000 }, + { 0x586a, 0x0000 }, + { 0x586b, 0x0000 }, + { 0x586c, 0x0000 }, + { 0x586d, 0x0000 }, + { 0x586e, 0x0000 }, + { 0x586f, 0x0000 }, + { 0x5870, 0x0000 }, + { 0x5871, 0x0000 }, + { 0x5872, 0x0000 }, + { 0x5873, 0x0000 }, + { 0x5874, 0x0000 }, + { 0x5875, 0x0000 }, + { 0x5876, 0x0000 }, + { 0x5877, 0x0000 }, + { 0x5878, 0x0000 }, + { 0x5879, 0x0000 }, + { 0x587a, 0x0000 }, + { 0x587b, 0x0000 }, + { 0x587c, 0x0000 }, + { 0x587d, 0x0000 }, + { 0x587e, 0x0000 }, + { 0x587f, 0x0000 }, + { 0x5880, 0x0000 }, + { 0x5881, 0x0000 }, + { 0x5882, 0x0000 }, + { 0x5883, 0x0000 }, + { 0x5884, 0x0000 }, + { 0x5885, 0x0000 }, + { 0x5886, 0x0000 }, + { 0x5887, 0x0000 }, + { 0x5888, 0x0000 }, + { 0x5889, 0x0000 }, + { 0x588a, 0x0000 }, + { 0x588b, 0x0000 }, + { 0x588c, 0x0000 }, + { 0x588d, 0x0000 }, + { 0x588e, 0x0000 }, + { 0x588f, 0x0000 }, + { 0x5890, 0x0000 }, + { 0x5891, 0x0000 }, + { 0x5892, 0x0000 }, + { 0x5893, 0x0000 }, + { 0x5894, 0x0000 }, + { 0x5895, 0x0000 }, + { 0x5896, 0x0000 }, + { 0x5897, 0x0000 }, + { 0x5898, 0x0000 }, + { 0x5899, 0x0000 }, + { 0x589a, 0x0000 }, + { 0x589b, 0x0000 }, + { 0x589c, 0x0000 }, + { 0x589d, 0x0000 }, + { 0x589e, 0x0000 }, + { 0x589f, 0x0000 }, + { 0x58a0, 0x0000 }, + { 0x58a1, 0x0000 }, + { 0x58a2, 0x0000 }, + { 0x58a3, 0x0000 }, + { 0x58a4, 0x0000 }, + { 0x58a5, 0x0000 }, + { 0x58a6, 0x0000 }, + { 0x58a7, 0x0000 }, + { 0x58a8, 0x0000 }, + { 0x58a9, 0x0000 }, + { 0x58aa, 0x0000 }, + { 0x58ab, 0x0000 }, + { 0x58ac, 0x0000 }, + { 0x58ad, 0x0000 }, + { 0x58ae, 0x0000 }, + { 0x58af, 0x0000 }, + { 0x58b0, 0x0000 }, + { 0x58b1, 0x0000 }, + { 0x58b2, 0x0000 }, + { 0x58b3, 0x0000 }, + { 0x58b4, 0x0000 }, + { 0x58b5, 0x0000 }, + { 0x58b6, 0x0000 }, + { 0x58b7, 0x0000 }, + { 0x58b8, 0x0000 }, + { 0x58b9, 0x0000 }, + { 0x58ba, 0x0000 }, + { 0x58bb, 0x0000 }, + { 0x58bc, 0x0000 }, + { 0x58bd, 0x0000 }, + { 0x58be, 0x0000 }, + { 0x58bf, 0x0000 }, + { 0x58c0, 0x0000 }, + { 0x58c1, 0x0000 }, + { 0x58c2, 0x0000 }, + { 0x58c3, 0x0000 }, + { 0x58c4, 0x0000 }, + { 0x58c5, 0x0000 }, + { 0x58c6, 0x0000 }, + { 0x58c7, 0x0000 }, + { 0x58c8, 0x0000 }, + { 0x58c9, 0x0000 }, + { 0x58ca, 0x0000 }, + { 0x58cb, 0x0000 }, + { 0x58cc, 0x0000 }, + { 0x58cd, 0x0000 }, + { 0x58ce, 0x0000 }, + { 0x58cf, 0x0000 }, + { 0x58d0, 0x0000 }, + { 0x58d1, 0x0000 }, + { 0x58d2, 0x0000 }, + { 0x58d3, 0x0000 }, + { 0x58d4, 0x0000 }, + { 0x58d5, 0x0000 }, + { 0x58d6, 0x0000 }, + { 0x58d7, 0x0000 }, + { 0x58d8, 0x0000 }, + { 0x58d9, 0x0000 }, + { 0x58da, 0x0000 }, + { 0x58db, 0x0000 }, + { 0x58dc, 0x0000 }, + { 0x58dd, 0x0000 }, + { 0x58de, 0x0000 }, + { 0x58df, 0x0000 }, + { 0x58e0, 0x0000 }, + { 0x58e1, 0x0000 }, + { 0x58e2, 0x0000 }, + { 0x58e3, 0x0000 }, + { 0x58e4, 0x0000 }, + { 0x58e5, 0x0000 }, + { 0x58e6, 0x0000 }, + { 0x58e7, 0x0000 }, + { 0x58e8, 0x0000 }, + { 0x58e9, 0x0000 }, + { 0x58ea, 0x0000 }, + { 0x58eb, 0x0000 }, + { 0x58ec, 0x0000 }, + { 0x58ed, 0x0000 }, + { 0x58ee, 0x0000 }, + { 0x58ef, 0x0000 }, + { 0x58f0, 0x0000 }, + { 0x58f1, 0x0000 }, + { 0x58f2, 0x0000 }, + { 0x58f3, 0x0000 }, + { 0x58f4, 0x0000 }, + { 0x58f5, 0x0000 }, + { 0x58f6, 0x0000 }, + { 0x58f7, 0x0000 }, + { 0x58f8, 0x0000 }, + { 0x58f9, 0x0000 }, + { 0x58fa, 0x0000 }, + { 0x58fb, 0x0000 }, + { 0x58fc, 0x0000 }, + { 0x58fd, 0x0000 }, + { 0x58fe, 0x0000 }, + { 0x58ff, 0x0000 }, + { 0x5900, 0x0000 }, + { 0x5901, 0x0000 }, + { 0x5902, 0x0000 }, + { 0x5903, 0x0000 }, + { 0x5904, 0x0000 }, + { 0x5905, 0x0000 }, + { 0x5906, 0x0000 }, + { 0x5907, 0x0000 }, + { 0x5908, 0x0000 }, + { 0x5909, 0x0000 }, + { 0x590a, 0x0000 }, + { 0x590b, 0x0000 }, + { 0x590c, 0x0000 }, + { 0x590d, 0x0000 }, + { 0x590e, 0x0000 }, + { 0x590f, 0x0000 }, + { 0x5910, 0x0000 }, + { 0x5911, 0x0000 }, + { 0x5912, 0x0000 }, + { 0x5913, 0x0000 }, + { 0x5914, 0x0000 }, + { 0x5915, 0x0000 }, + { 0x5916, 0x0000 }, + { 0x5917, 0x0000 }, + { 0x5918, 0x0000 }, + { 0x5919, 0x0000 }, + { 0x591a, 0x0000 }, + { 0x591b, 0x0000 }, + { 0x591c, 0x0000 }, + { 0x591d, 0x0000 }, + { 0x591e, 0x0000 }, + { 0x591f, 0x0000 }, + { 0x5920, 0x0000 }, + { 0x5921, 0x0000 }, + { 0x5922, 0x0000 }, + { 0x5923, 0x0000 }, + { 0x5924, 0x0000 }, + { 0x5925, 0x0000 }, + { 0x5926, 0x0000 }, + { 0x5927, 0x0000 }, + { 0x5928, 0x0000 }, + { 0x5929, 0x0000 }, + { 0x592a, 0x0000 }, + { 0x592b, 0x0000 }, + { 0x592c, 0x0000 }, + { 0x592d, 0x0000 }, + { 0x592e, 0x0000 }, + { 0x592f, 0x0000 }, + { 0x5930, 0x0000 }, + { 0x5931, 0x0000 }, + { 0x5932, 0x0000 }, + { 0x5933, 0x0000 }, + { 0x5934, 0x0000 }, + { 0x5935, 0x0000 }, + { 0x5936, 0x0000 }, + { 0x5937, 0x0000 }, + { 0x5938, 0x0000 }, + { 0x5939, 0x0000 }, + { 0x593a, 0x0000 }, + { 0x593b, 0x0000 }, + { 0x593c, 0x0000 }, + { 0x593d, 0x0000 }, + { 0x593e, 0x0000 }, + { 0x593f, 0x0000 }, + { 0x5940, 0x0000 }, + { 0x5941, 0x0000 }, + { 0x5942, 0x0000 }, + { 0x5943, 0x0000 }, + { 0x5944, 0x0000 }, + { 0x5945, 0x0000 }, + { 0x5946, 0x0000 }, + { 0x5947, 0x0000 }, + { 0x5948, 0x0000 }, + { 0x5949, 0x0000 }, + { 0x594a, 0x0000 }, + { 0x594b, 0x0000 }, + { 0x594c, 0x0000 }, + { 0x594d, 0x0000 }, + { 0x594e, 0x0000 }, + { 0x594f, 0x0000 }, + { 0x5950, 0x0000 }, + { 0x5951, 0x0000 }, + { 0x5952, 0x0000 }, + { 0x5953, 0x0000 }, + { 0x5954, 0x0000 }, + { 0x5955, 0x0000 }, + { 0x5956, 0x0000 }, + { 0x5957, 0x0000 }, + { 0x5958, 0x0000 }, + { 0x5959, 0x0000 }, + { 0x595a, 0x0000 }, + { 0x595b, 0x0000 }, + { 0x595c, 0x0000 }, + { 0x595d, 0x0000 }, + { 0x595e, 0x0000 }, + { 0x595f, 0x0000 }, + { 0x5960, 0x0000 }, + { 0x5961, 0x0000 }, + { 0x5962, 0x0000 }, + { 0x5963, 0x0000 }, + { 0x5964, 0x0000 }, + { 0x5965, 0x0000 }, + { 0x5966, 0x0000 }, + { 0x5967, 0x0000 }, + { 0x5968, 0x0000 }, + { 0x5969, 0x0000 }, + { 0x596a, 0x0000 }, + { 0x596b, 0x0000 }, + { 0x596c, 0x0000 }, + { 0x596d, 0x0000 }, + { 0x596e, 0x0000 }, + { 0x596f, 0x0000 }, + { 0x5970, 0x0000 }, + { 0x5971, 0x0000 }, + { 0x5972, 0x0000 }, + { 0x5973, 0x0000 }, + { 0x5974, 0x0000 }, + { 0x5975, 0x0000 }, + { 0x5976, 0x0000 }, + { 0x5977, 0x0000 }, + { 0x5978, 0x0000 }, + { 0x5979, 0x0000 }, + { 0x597a, 0x0000 }, + { 0x597b, 0x0000 }, + { 0x597c, 0x0000 }, + { 0x597d, 0x0000 }, + { 0x597e, 0x0000 }, + { 0x597f, 0x0000 }, + { 0x5980, 0x0000 }, + { 0x5981, 0x0000 }, + { 0x5982, 0x0000 }, + { 0x5983, 0x0000 }, + { 0x5984, 0x0000 }, + { 0x5985, 0x0000 }, + { 0x5986, 0x0000 }, + { 0x5987, 0x0000 }, + { 0x5988, 0x0000 }, + { 0x5989, 0x0000 }, + { 0x598a, 0x0000 }, + { 0x598b, 0x0000 }, + { 0x598c, 0x0000 }, + { 0x598d, 0x0000 }, + { 0x598e, 0x0000 }, + { 0x598f, 0x0000 }, + { 0x5990, 0x0000 }, + { 0x5991, 0x0000 }, + { 0x5992, 0x0000 }, + { 0x5993, 0x0000 }, + { 0x5994, 0x0000 }, + { 0x5995, 0x0000 }, + { 0x5996, 0x0000 }, + { 0x5997, 0x0000 }, + { 0x5998, 0x0000 }, + { 0x5999, 0x0000 }, + { 0x599a, 0x0000 }, + { 0x599b, 0x0000 }, + { 0x599c, 0x0000 }, + { 0x599d, 0x0000 }, + { 0x599e, 0x0000 }, + { 0x599f, 0x0000 }, + { 0x59a0, 0x0000 }, + { 0x59a1, 0x0000 }, + { 0x59a2, 0x0000 }, + { 0x59a3, 0x0000 }, + { 0x59a4, 0x0000 }, + { 0x59a5, 0x0000 }, + { 0x59a6, 0x0000 }, + { 0x59a7, 0x0000 }, + { 0x59a8, 0x0000 }, + { 0x59a9, 0x0000 }, + { 0x59aa, 0x0000 }, + { 0x59ab, 0x0000 }, + { 0x59ac, 0x0000 }, + { 0x59ad, 0x0000 }, + { 0x59ae, 0x0000 }, + { 0x59af, 0x0000 }, + { 0x59b0, 0x0000 }, + { 0x59b1, 0x0000 }, + { 0x59b2, 0x0000 }, + { 0x59b3, 0x0000 }, + { 0x59b4, 0x0000 }, + { 0x59b5, 0x0000 }, + { 0x59b6, 0x0000 }, + { 0x59b7, 0x0000 }, + { 0x59b8, 0x0000 }, + { 0x59b9, 0x0000 }, + { 0x59ba, 0x0000 }, + { 0x59bb, 0x0000 }, + { 0x59bc, 0x0000 }, + { 0x59bd, 0x0000 }, + { 0x59be, 0x0000 }, + { 0x59bf, 0x0000 }, + { 0x59c0, 0x0000 }, + { 0x59c1, 0x0000 }, + { 0x59c2, 0x0000 }, + { 0x59c3, 0x0000 }, + { 0x59c4, 0x0000 }, + { 0x59c5, 0x0000 }, + { 0x59c6, 0x0000 }, + { 0x59c7, 0x0000 }, + { 0x59c8, 0x0000 }, + { 0x59c9, 0x0000 }, + { 0x59ca, 0x0000 }, + { 0x59cb, 0x0000 }, + { 0x59cc, 0x0000 }, + { 0x59cd, 0x0000 }, + { 0x59ce, 0x0000 }, + { 0x59cf, 0x0000 }, + { 0x59d0, 0x0000 }, + { 0x59d1, 0x0000 }, + { 0x59d2, 0x0000 }, + { 0x59d3, 0x0000 }, + { 0x59d4, 0x0000 }, + { 0x59d5, 0x0000 }, + { 0x59d6, 0x0000 }, + { 0x59d7, 0x0000 }, + { 0x59d8, 0x0000 }, + { 0x59d9, 0x0000 }, + { 0x59da, 0x0000 }, + { 0x59db, 0x0000 }, + { 0x59dc, 0x0000 }, + { 0x59dd, 0x0000 }, + { 0x59de, 0x0000 }, + { 0x59df, 0x0000 }, + { 0x59e0, 0x0000 }, + { 0x59e1, 0x0000 }, + { 0x59e2, 0x0000 }, + { 0x59e3, 0x0000 }, + { 0x59e4, 0x0000 }, + { 0x59e5, 0x0000 }, + { 0x59e6, 0x0000 }, + { 0x59e7, 0x0000 }, + { 0x59e8, 0x0000 }, + { 0x59e9, 0x0000 }, + { 0x59ea, 0x0000 }, + { 0x59eb, 0x0000 }, + { 0x59ec, 0x0000 }, + { 0x59ed, 0x0000 }, + { 0x59ee, 0x0000 }, + { 0x59ef, 0x0000 }, + { 0x59f0, 0x0000 }, + { 0x59f1, 0x0000 }, + { 0x59f2, 0x0000 }, + { 0x59f3, 0x0000 }, + { 0x59f4, 0x0000 }, + { 0x59f5, 0x0000 }, + { 0x59f6, 0x0000 }, + { 0x59f7, 0x0000 }, + { 0x59f8, 0x0000 }, + { 0x59f9, 0x0000 }, + { 0x59fa, 0x0000 }, + { 0x59fb, 0x0000 }, + { 0x59fc, 0x0000 }, + { 0x59fd, 0x0000 }, + { 0x59fe, 0x0000 }, + { 0x59ff, 0x0000 }, + { 0x5a00, 0x0000 }, + { 0x5a01, 0x0000 }, + { 0x5a02, 0x0000 }, + { 0x5a03, 0x0000 }, + { 0x5a04, 0x0000 }, + { 0x5a05, 0x0000 }, + { 0x5a06, 0x0000 }, + { 0x5a07, 0x0000 }, + { 0x5a08, 0x0000 }, + { 0x5a09, 0x0000 }, + { 0x5a0a, 0x0000 }, + { 0x5a0b, 0x0000 }, + { 0x5a0c, 0x0000 }, + { 0x5a0d, 0x0000 }, + { 0x5a0e, 0x0000 }, + { 0x5a0f, 0x0000 }, + { 0x5a10, 0x0000 }, + { 0x5a11, 0x0000 }, + { 0x5a12, 0x0000 }, + { 0x5a13, 0x0000 }, + { 0x5a14, 0x0000 }, + { 0x5a15, 0x0000 }, + { 0x5a16, 0x0000 }, + { 0x5a17, 0x0000 }, + { 0x5a18, 0x0000 }, + { 0x5a19, 0x0000 }, + { 0x5a1a, 0x0000 }, + { 0x5a1b, 0x0000 }, + { 0x5a1c, 0x0000 }, + { 0x5a1d, 0x0000 }, + { 0x5a1e, 0x0000 }, + { 0x5a1f, 0x0000 }, + { 0x5a20, 0x0000 }, + { 0x5a21, 0x0000 }, + { 0x5a22, 0x0000 }, + { 0x5a23, 0x0000 }, + { 0x5a24, 0x0000 }, + { 0x5a25, 0x0000 }, + { 0x5a26, 0x0000 }, + { 0x5a27, 0x0000 }, + { 0x5a28, 0x0000 }, + { 0x5a29, 0x0000 }, + { 0x5a2a, 0x0000 }, + { 0x5a2b, 0x0000 }, + { 0x5a2c, 0x0000 }, + { 0x5a2d, 0x0000 }, + { 0x5a2e, 0x0000 }, + { 0x5a2f, 0x0000 }, + { 0x5a30, 0x0000 }, + { 0x5a31, 0x0000 }, + { 0x5a32, 0x0000 }, + { 0x5a33, 0x0000 }, + { 0x5a34, 0x0000 }, + { 0x5a35, 0x0000 }, + { 0x5a36, 0x0000 }, + { 0x5a37, 0x0000 }, + { 0x5a38, 0x0000 }, + { 0x5a39, 0x0000 }, + { 0x5a3a, 0x0000 }, + { 0x5a3b, 0x0000 }, + { 0x5a3c, 0x0000 }, + { 0x5a3d, 0x0000 }, + { 0x5a3e, 0x0000 }, + { 0x5a3f, 0x0000 }, + { 0x5a40, 0x0000 }, + { 0x5a41, 0x0000 }, + { 0x5a42, 0x0000 }, + { 0x5a43, 0x0000 }, + { 0x5a44, 0x0000 }, + { 0x5a45, 0x0000 }, + { 0x5a46, 0x0000 }, + { 0x5a47, 0x0000 }, + { 0x5a48, 0x0000 }, + { 0x5a49, 0x0000 }, + { 0x5a4a, 0x0000 }, + { 0x5a4b, 0x0000 }, + { 0x5a4c, 0x0000 }, + { 0x5a4d, 0x0000 }, + { 0x5a4e, 0x0000 }, + { 0x5a4f, 0x0000 }, + { 0x5a50, 0x0000 }, + { 0x5a51, 0x0000 }, + { 0x5a52, 0x0000 }, + { 0x5a53, 0x0000 }, + { 0x5a54, 0x0000 }, + { 0x5a55, 0x0000 }, + { 0x5a56, 0x0000 }, + { 0x5a57, 0x0000 }, + { 0x5a58, 0x0000 }, + { 0x5a59, 0x0000 }, + { 0x5a5a, 0x0000 }, + { 0x5a5b, 0x0000 }, + { 0x5a5c, 0x0000 }, + { 0x5a5d, 0x0000 }, + { 0x5a5e, 0x0000 }, + { 0x5a5f, 0x0000 }, + { 0x5a60, 0x0000 }, + { 0x5a61, 0x0000 }, + { 0x5a62, 0x0000 }, + { 0x5a63, 0x0000 }, + { 0x5a64, 0x0000 }, + { 0x5a65, 0x0000 }, + { 0x5a66, 0x0000 }, + { 0x5a67, 0x0000 }, + { 0x5a68, 0x0000 }, + { 0x5a69, 0x0000 }, + { 0x5a6a, 0x0000 }, + { 0x5a6b, 0x0000 }, + { 0x5a6c, 0x0000 }, + { 0x5a6d, 0x0000 }, + { 0x5a6e, 0x0000 }, + { 0x5a6f, 0x0000 }, + { 0x5a70, 0x0000 }, + { 0x5a71, 0x0000 }, + { 0x5a72, 0x0000 }, + { 0x5a73, 0x0000 }, + { 0x5a74, 0x0000 }, + { 0x5a75, 0x0000 }, + { 0x5a76, 0x0000 }, + { 0x5a77, 0x0000 }, + { 0x5a78, 0x0000 }, + { 0x5a79, 0x0000 }, + { 0x5a7a, 0x0000 }, + { 0x5a7b, 0x0000 }, + { 0x5a7c, 0x0000 }, + { 0x5a7d, 0x0000 }, + { 0x5a7e, 0x0000 }, + { 0x5a7f, 0x0000 }, + { 0x5a80, 0x0000 }, + { 0x5a81, 0x0000 }, + { 0x5a82, 0x0000 }, + { 0x5a83, 0x0000 }, + { 0x5a84, 0x0000 }, + { 0x5a85, 0x0000 }, + { 0x5a86, 0x0000 }, + { 0x5a87, 0x0000 }, + { 0x5a88, 0x0000 }, + { 0x5a89, 0x0000 }, + { 0x5a8a, 0x0000 }, + { 0x5a8b, 0x0000 }, + { 0x5a8c, 0x0000 }, + { 0x5a8d, 0x0000 }, + { 0x5a8e, 0x0000 }, + { 0x5a8f, 0x0000 }, + { 0x5a90, 0x0000 }, + { 0x5a91, 0x0000 }, + { 0x5a92, 0x0000 }, + { 0x5a93, 0x0000 }, + { 0x5a94, 0x0000 }, + { 0x5a95, 0x0000 }, + { 0x5a96, 0x0000 }, + { 0x5a97, 0x0000 }, + { 0x5a98, 0x0000 }, + { 0x5a99, 0x0000 }, + { 0x5a9a, 0x0000 }, + { 0x5a9b, 0x0000 }, + { 0x5a9c, 0x0000 }, + { 0x5a9d, 0x0000 }, + { 0x5a9e, 0x0000 }, + { 0x5a9f, 0x0000 }, + { 0x5aa0, 0x0000 }, + { 0x5aa1, 0x0000 }, + { 0x5aa2, 0x0000 }, + { 0x5aa3, 0x0000 }, + { 0x5aa4, 0x0000 }, + { 0x5aa5, 0x0000 }, + { 0x5aa6, 0x0000 }, + { 0x5aa7, 0x0000 }, + { 0x5aa8, 0x0000 }, + { 0x5aa9, 0x0000 }, + { 0x5aaa, 0x0000 }, + { 0x5aab, 0x0000 }, + { 0x5aac, 0x0000 }, + { 0x5aad, 0x0000 }, + { 0x5aae, 0x0000 }, + { 0x5aaf, 0x0000 }, + { 0x5ab0, 0x0000 }, + { 0x5ab1, 0x0000 }, + { 0x5ab2, 0x0000 }, + { 0x5ab3, 0x0000 }, + { 0x5ab4, 0x0000 }, + { 0x5ab5, 0x0000 }, + { 0x5ab6, 0x0000 }, + { 0x5ab7, 0x0000 }, + { 0x5ab8, 0x0000 }, + { 0x5ab9, 0x0000 }, + { 0x5aba, 0x0000 }, + { 0x5abb, 0x0000 }, + { 0x5abc, 0x0000 }, + { 0x5abd, 0x0000 }, + { 0x5abe, 0x0000 }, + { 0x5abf, 0x0000 }, + { 0x5ac0, 0x0000 }, + { 0x5ac1, 0x0000 }, + { 0x5ac2, 0x0000 }, + { 0x5ac3, 0x0000 }, + { 0x5ac4, 0x0000 }, + { 0x5ac5, 0x0000 }, + { 0x5ac6, 0x0000 }, + { 0x5ac7, 0x0000 }, + { 0x5ac8, 0x0000 }, + { 0x5ac9, 0x0000 }, + { 0x5aca, 0x0000 }, + { 0x5acb, 0x0000 }, + { 0x5acc, 0x0000 }, + { 0x5acd, 0x0000 }, + { 0x5ace, 0x0000 }, + { 0x5acf, 0x0000 }, + { 0x5ad0, 0x0000 }, + { 0x5ad1, 0x0000 }, + { 0x5ad2, 0x0000 }, + { 0x5ad3, 0x0000 }, + { 0x5ad4, 0x0000 }, + { 0x5ad5, 0x0000 }, + { 0x5ad6, 0x0000 }, + { 0x5ad7, 0x0000 }, + { 0x5ad8, 0x0000 }, + { 0x5ad9, 0x0000 }, + { 0x5ada, 0x0000 }, + { 0x5adb, 0x0000 }, + { 0x5adc, 0x0000 }, + { 0x5add, 0x0000 }, + { 0x5ade, 0x0000 }, + { 0x5adf, 0x0000 }, + { 0x5ae0, 0x0000 }, + { 0x5ae1, 0x0000 }, + { 0x5ae2, 0x0000 }, + { 0x5ae3, 0x0000 }, + { 0x5ae4, 0x0000 }, + { 0x5ae5, 0x0000 }, + { 0x5ae6, 0x0000 }, + { 0x5ae7, 0x0000 }, + { 0x5ae8, 0x0000 }, + { 0x5ae9, 0x0000 }, + { 0x5aea, 0x0000 }, + { 0x5aeb, 0x0000 }, + { 0x5aec, 0x0000 }, + { 0x5aed, 0x0000 }, + { 0x5aee, 0x0000 }, + { 0x5aef, 0x0000 }, + { 0x5af0, 0x0000 }, + { 0x5af1, 0x0000 }, + { 0x5af2, 0x0000 }, + { 0x5af3, 0x0000 }, + { 0x5af4, 0x0000 }, + { 0x5af5, 0x0000 }, + { 0x5af6, 0x0000 }, + { 0x5af7, 0x0000 }, + { 0x5af8, 0x0000 }, + { 0x5af9, 0x0000 }, + { 0x5afa, 0x0000 }, + { 0x5afb, 0x0000 }, + { 0x5afc, 0x0000 }, + { 0x5afd, 0x0000 }, + { 0x5afe, 0x0000 }, + { 0x5aff, 0x0000 }, + { 0x5b00, 0x0000 }, + { 0x5b01, 0x0000 }, + { 0x5b02, 0x0000 }, + { 0x5b03, 0x0000 }, + { 0x5b04, 0x0000 }, + { 0x5b05, 0x0000 }, + { 0x5b06, 0x0000 }, + { 0x5b07, 0x0000 }, + { 0x5b08, 0x0000 }, + { 0x5b09, 0x0000 }, + { 0x5b0a, 0x0000 }, + { 0x5b0b, 0x0000 }, + { 0x5b0c, 0x0000 }, + { 0x5b0d, 0x0000 }, + { 0x5b0e, 0x0000 }, + { 0x5b0f, 0x0000 }, + { 0x5b10, 0x0000 }, + { 0x5b11, 0x0000 }, + { 0x5b12, 0x0000 }, + { 0x5b13, 0x0000 }, + { 0x5b14, 0x0000 }, + { 0x5b15, 0x0000 }, + { 0x5b16, 0x0000 }, + { 0x5b17, 0x0000 }, + { 0x5b18, 0x0000 }, + { 0x5b19, 0x0000 }, + { 0x5b1a, 0x0000 }, + { 0x5b1b, 0x0000 }, + { 0x5b1c, 0x0000 }, + { 0x5b1d, 0x0000 }, + { 0x5b1e, 0x0000 }, + { 0x5b1f, 0x0000 }, + { 0x5b20, 0x0000 }, + { 0x5b21, 0x0000 }, + { 0x5b22, 0x0000 }, + { 0x5b23, 0x0000 }, + { 0x5b24, 0x0000 }, + { 0x5b25, 0x0000 }, + { 0x5b26, 0x0000 }, + { 0x5b27, 0x0000 }, + { 0x5b28, 0x0000 }, + { 0x5b29, 0x0000 }, + { 0x5b2a, 0x0000 }, + { 0x5b2b, 0x0000 }, + { 0x5b2c, 0x0000 }, + { 0x5b2d, 0x0000 }, + { 0x5b2e, 0x0000 }, + { 0x5b2f, 0x0000 }, + { 0x5b30, 0x0000 }, + { 0x5b31, 0x0000 }, + { 0x5b32, 0x0000 }, + { 0x5b33, 0x0000 }, + { 0x5b34, 0x0000 }, + { 0x5b35, 0x0000 }, + { 0x5b36, 0x0000 }, + { 0x5b37, 0x0000 }, + { 0x5b38, 0x0000 }, + { 0x5b39, 0x0000 }, + { 0x5b3a, 0x0000 }, + { 0x5b3b, 0x0000 }, + { 0x5b3c, 0x0000 }, + { 0x5b3d, 0x0000 }, + { 0x5b3e, 0x0000 }, + { 0x5b3f, 0x0000 }, + { 0x5b40, 0x0000 }, + { 0x5b41, 0x0000 }, + { 0x5b42, 0x0000 }, + { 0x5b43, 0x0000 }, + { 0x5b44, 0x0000 }, + { 0x5b45, 0x0000 }, + { 0x5b46, 0x0000 }, + { 0x5b47, 0x0000 }, + { 0x5b48, 0x0000 }, + { 0x5b49, 0x0000 }, + { 0x5b4a, 0x0000 }, + { 0x5b4b, 0x0000 }, + { 0x5b4c, 0x0000 }, + { 0x5b4d, 0x0000 }, + { 0x5b4e, 0x0000 }, + { 0x5b4f, 0x0000 }, + { 0x5b50, 0x0000 }, + { 0x5b51, 0x0000 }, + { 0x5b52, 0x0000 }, + { 0x5b53, 0x0000 }, + { 0x5b54, 0x0000 }, + { 0x5b55, 0x0000 }, + { 0x5b56, 0x0000 }, + { 0x5b57, 0x0000 }, + { 0x5b58, 0x0000 }, + { 0x5b59, 0x0000 }, + { 0x5b5a, 0x0000 }, + { 0x5b5b, 0x0000 }, + { 0x5b5c, 0x0000 }, + { 0x5b5d, 0x0000 }, + { 0x5b5e, 0x0000 }, + { 0x5b5f, 0x0000 }, + { 0x5b60, 0x0000 }, + { 0x5b61, 0x0000 }, + { 0x5b62, 0x0000 }, + { 0x5b63, 0x0000 }, + { 0x5b64, 0x0000 }, + { 0x5b65, 0x0000 }, + { 0x5b66, 0x0000 }, + { 0x5b67, 0x0000 }, + { 0x5b68, 0x0000 }, + { 0x5b69, 0x0000 }, + { 0x5b6a, 0x0000 }, + { 0x5b6b, 0x0000 }, + { 0x5b6c, 0x0000 }, + { 0x5b6d, 0x0000 }, + { 0x5b6e, 0x0000 }, + { 0x5b6f, 0x0000 }, + { 0x5b70, 0x0000 }, + { 0x5b71, 0x0000 }, + { 0x5b72, 0x0000 }, + { 0x5b73, 0x0000 }, + { 0x5b74, 0x0000 }, + { 0x5b75, 0x0000 }, + { 0x5b76, 0x0000 }, + { 0x5b77, 0x0000 }, + { 0x5b78, 0x0000 }, + { 0x5b79, 0x0000 }, + { 0x5b7a, 0x0000 }, + { 0x5b7b, 0x0000 }, + { 0x5b7c, 0x0000 }, + { 0x5b7d, 0x0000 }, + { 0x5b7e, 0x0000 }, + { 0x5b7f, 0x0000 }, + { 0x5b80, 0x0000 }, + { 0x5b81, 0x0000 }, + { 0x5b82, 0x0000 }, + { 0x5b83, 0x0000 }, + { 0x5b84, 0x0000 }, + { 0x5b85, 0x0000 }, + { 0x5b86, 0x0000 }, + { 0x5b87, 0x0000 }, + { 0x5b88, 0x0000 }, + { 0x5b89, 0x0000 }, + { 0x5b8a, 0x0000 }, + { 0x5b8b, 0x0000 }, + { 0x5b8c, 0x0000 }, + { 0x5b8d, 0x0000 }, + { 0x5b8e, 0x0000 }, + { 0x5b8f, 0x0000 }, + { 0x5b90, 0x0000 }, + { 0x5b91, 0x0000 }, + { 0x5b92, 0x0000 }, + { 0x5b93, 0x0000 }, + { 0x5b94, 0x0000 }, + { 0x5b95, 0x0000 }, + { 0x5b96, 0x0000 }, + { 0x5b97, 0x0000 }, + { 0x5b98, 0x0000 }, + { 0x5b99, 0x0000 }, + { 0x5b9a, 0x0000 }, + { 0x5b9b, 0x0000 }, + { 0x5b9c, 0x0000 }, + { 0x5b9d, 0x0000 }, + { 0x5b9e, 0x0000 }, + { 0x5b9f, 0x0000 }, + { 0x5ba0, 0x0000 }, + { 0x5ba1, 0x0000 }, + { 0x5ba2, 0x0000 }, + { 0x5ba3, 0x0000 }, + { 0x5ba4, 0x0000 }, + { 0x5ba5, 0x0000 }, + { 0x5ba6, 0x0000 }, + { 0x5ba7, 0x0000 }, + { 0x5ba8, 0x0000 }, + { 0x5ba9, 0x0000 }, + { 0x5baa, 0x0000 }, + { 0x5bab, 0x0000 }, + { 0x5bac, 0x0000 }, + { 0x5bad, 0x0000 }, + { 0x5bae, 0x0000 }, + { 0x5baf, 0x0000 }, + { 0x5bb0, 0x0000 }, + { 0x5bb1, 0x0000 }, + { 0x5bb2, 0x0000 }, + { 0x5bb3, 0x0000 }, + { 0x5bb4, 0x0000 }, + { 0x5bb5, 0x0000 }, + { 0x5bb6, 0x0000 }, + { 0x5bb7, 0x0000 }, + { 0x5bb8, 0x0000 }, + { 0x5bb9, 0x0000 }, + { 0x5bba, 0x0000 }, + { 0x5bbb, 0x0000 }, + { 0x5bbc, 0x0000 }, + { 0x5bbd, 0x0000 }, + { 0x5bbe, 0x0000 }, + { 0x5bbf, 0x0000 }, + { 0x5bc0, 0x0000 }, + { 0x5bc1, 0x0000 }, + { 0x5bc2, 0x0000 }, + { 0x5bc3, 0x0000 }, + { 0x5bc4, 0x0000 }, + { 0x5bc5, 0x0000 }, + { 0x5bc6, 0x0000 }, + { 0x5bc7, 0x0000 }, + { 0x5bc8, 0x0000 }, + { 0x5bc9, 0x0000 }, + { 0x5bca, 0x0000 }, + { 0x5bcb, 0x0000 }, + { 0x5bcc, 0x0000 }, + { 0x5bcd, 0x0000 }, + { 0x5bce, 0x0000 }, + { 0x5bcf, 0x0000 }, + { 0x5bd0, 0x0000 }, + { 0x5bd1, 0x0000 }, + { 0x5bd2, 0x0000 }, + { 0x5bd3, 0x0000 }, + { 0x5bd4, 0x0000 }, + { 0x5bd5, 0x0000 }, + { 0x5bd6, 0x0000 }, + { 0x5bd7, 0x0000 }, + { 0x5bd8, 0x0000 }, + { 0x5bd9, 0x0000 }, + { 0x5bda, 0x0000 }, + { 0x5bdb, 0x0000 }, + { 0x5bdc, 0x0000 }, + { 0x5bdd, 0x0000 }, + { 0x5bde, 0x0000 }, + { 0x5bdf, 0x0000 }, + { 0x5be0, 0x0000 }, + { 0x5be1, 0x0000 }, + { 0x5be2, 0x0000 }, + { 0x5be3, 0x0000 }, + { 0x5be4, 0x0000 }, + { 0x5be5, 0x0000 }, + { 0x5be6, 0x0000 }, + { 0x5be7, 0x0000 }, + { 0x5be8, 0x0000 }, + { 0x5be9, 0x0000 }, + { 0x5bea, 0x0000 }, + { 0x5beb, 0x0000 }, + { 0x5bec, 0x0000 }, + { 0x5bed, 0x0000 }, + { 0x5bee, 0x0000 }, + { 0x5bef, 0x0000 }, + { 0x5bf0, 0x0000 }, + { 0x5bf1, 0x0000 }, + { 0x5bf2, 0x0000 }, + { 0x5bf3, 0x0000 }, + { 0x5bf4, 0x0000 }, + { 0x5bf5, 0x0000 }, + { 0x5bf6, 0x0000 }, + { 0x5bf7, 0x0000 }, + { 0x5bf8, 0x0000 }, + { 0x5bf9, 0x0000 }, + { 0x5bfa, 0x0000 }, + { 0x5bfb, 0x0000 }, + { 0x5bfc, 0x0000 }, + { 0x5bfd, 0x0000 }, + { 0x5bfe, 0x0000 }, + { 0x5bff, 0x0000 }, + { 0x5c00, 0x0000 }, + { 0x5c01, 0x0000 }, + { 0x5c02, 0x0000 }, + { 0x5c03, 0x0000 }, + { 0x5c04, 0x0000 }, + { 0x5c05, 0x0000 }, + { 0x5c06, 0x0000 }, + { 0x5c07, 0x0000 }, + { 0x5c08, 0x0000 }, + { 0x5c09, 0x0000 }, + { 0x5c0a, 0x0000 }, + { 0x5c0b, 0x0000 }, + { 0x5c0c, 0x0000 }, + { 0x5c0d, 0x0000 }, + { 0x5c0e, 0x0000 }, + { 0x5c0f, 0x0000 }, + { 0x5c10, 0x0000 }, + { 0x5c11, 0x0000 }, + { 0x5c12, 0x0000 }, + { 0x5c13, 0x0000 }, + { 0x5c14, 0x0000 }, + { 0x5c15, 0x0000 }, + { 0x5c16, 0x0000 }, + { 0x5c17, 0x0000 }, + { 0x5c18, 0x0000 }, + { 0x5c19, 0x0000 }, + { 0x5c1a, 0x0000 }, + { 0x5c1b, 0x0000 }, + { 0x5c1c, 0x0000 }, + { 0x5c1d, 0x0000 }, + { 0x5c1e, 0x0000 }, + { 0x5c1f, 0x0000 }, + { 0x5c20, 0x0000 }, + { 0x5c21, 0x0000 }, + { 0x5c22, 0x0000 }, + { 0x5c23, 0x0000 }, + { 0x5c24, 0x0000 }, + { 0x5c25, 0x0000 }, + { 0x5c26, 0x0000 }, + { 0x5c27, 0x0000 }, + { 0x5c28, 0x0000 }, + { 0x5c29, 0x0000 }, + { 0x5c2a, 0x0000 }, + { 0x5c2b, 0x0000 }, + { 0x5c2c, 0x0000 }, + { 0x5c2d, 0x0000 }, + { 0x5c2e, 0x0000 }, + { 0x5c2f, 0x0000 }, + { 0x5c30, 0x0000 }, + { 0x5c31, 0x0000 }, + { 0x5c32, 0x0000 }, + { 0x5c33, 0x0000 }, + { 0x5c34, 0x0000 }, + { 0x5c35, 0x0000 }, + { 0x5c36, 0x0000 }, + { 0x5c37, 0x0000 }, + { 0x5c38, 0x0000 }, + { 0x5c39, 0x0000 }, + { 0x5c3a, 0x0000 }, + { 0x5c3b, 0x0000 }, + { 0x5c3c, 0x0000 }, + { 0x5c3d, 0x0000 }, + { 0x5c3e, 0x0000 }, + { 0x5c3f, 0x0000 }, + { 0x5c40, 0x0000 }, + { 0x5c41, 0x0000 }, + { 0x5c42, 0x0000 }, + { 0x5c43, 0x0000 }, + { 0x5c44, 0x0000 }, + { 0x5c45, 0x0000 }, + { 0x5c46, 0x0000 }, + { 0x5c47, 0x0000 }, + { 0x5c48, 0x0000 }, + { 0x5c49, 0x0000 }, + { 0x5c4a, 0x0000 }, + { 0x5c4b, 0x0000 }, + { 0x5c4c, 0x0000 }, + { 0x5c4d, 0x0000 }, + { 0x5c4e, 0x0000 }, + { 0x5c4f, 0x0000 }, + { 0x5c50, 0x0000 }, + { 0x5c51, 0x0000 }, + { 0x5c52, 0x0000 }, + { 0x5c53, 0x0000 }, + { 0x5c54, 0x0000 }, + { 0x5c55, 0x0000 }, + { 0x5c56, 0x0000 }, + { 0x5c57, 0x0000 }, + { 0x5c58, 0x0000 }, + { 0x5c59, 0x0000 }, + { 0x5c5a, 0x0000 }, + { 0x5c5b, 0x0000 }, + { 0x5c5c, 0x0000 }, + { 0x5c5d, 0x0000 }, + { 0x5c5e, 0x0000 }, + { 0x5c5f, 0x0000 }, + { 0x5c60, 0x0000 }, + { 0x5c61, 0x0000 }, + { 0x5c62, 0x0000 }, + { 0x5c63, 0x0000 }, + { 0x5c64, 0x0000 }, + { 0x5c65, 0x0000 }, + { 0x5c66, 0x0000 }, + { 0x5c67, 0x0000 }, + { 0x5c68, 0x0000 }, + { 0x5c69, 0x0000 }, + { 0x5c6a, 0x0000 }, + { 0x5c6b, 0x0000 }, + { 0x5c6c, 0x0000 }, + { 0x5c6d, 0x0000 }, + { 0x5c6e, 0x0000 }, + { 0x5c6f, 0x0000 }, + { 0x5c70, 0x0000 }, + { 0x5c71, 0x0000 }, + { 0x5c72, 0x0000 }, + { 0x5c73, 0x0000 }, + { 0x5c74, 0x0000 }, + { 0x5c75, 0x0000 }, + { 0x5c76, 0x0000 }, + { 0x5c77, 0x0000 }, + { 0x5c78, 0x0000 }, + { 0x5c79, 0x0000 }, + { 0x5c7a, 0x0000 }, + { 0x5c7b, 0x0000 }, + { 0x5c7c, 0x0000 }, + { 0x5c7d, 0x0000 }, + { 0x5c7e, 0x0000 }, + { 0x5c7f, 0x0000 }, + { 0x5c80, 0x0000 }, + { 0x5c81, 0x0000 }, + { 0x5c82, 0x0000 }, + { 0x5c83, 0x0000 }, + { 0x5c84, 0x0000 }, + { 0x5c85, 0x0000 }, + { 0x5c86, 0x0000 }, + { 0x5c87, 0x0000 }, + { 0x5c88, 0x0000 }, + { 0x5c89, 0x0000 }, + { 0x5c8a, 0x0000 }, + { 0x5c8b, 0x0000 }, + { 0x5c8c, 0x0000 }, + { 0x5c8d, 0x0000 }, + { 0x5c8e, 0x0000 }, + { 0x5c8f, 0x0000 }, + { 0x5c90, 0x0000 }, + { 0x5c91, 0x0000 }, + { 0x5c92, 0x0000 }, + { 0x5c93, 0x0000 }, + { 0x5c94, 0x0000 }, + { 0x5c95, 0x0000 }, + { 0x5c96, 0x0000 }, + { 0x5c97, 0x0000 }, + { 0x5c98, 0x0000 }, + { 0x5c99, 0x0000 }, + { 0x5c9a, 0x0000 }, + { 0x5c9b, 0x0000 }, + { 0x5c9c, 0x0000 }, + { 0x5c9d, 0x0000 }, + { 0x5c9e, 0x0000 }, + { 0x5c9f, 0x0000 }, + { 0x5ca0, 0x0000 }, + { 0x5ca1, 0x0000 }, + { 0x5ca2, 0x0000 }, + { 0x5ca3, 0x0000 }, + { 0x5ca4, 0x0000 }, + { 0x5ca5, 0x0000 }, + { 0x5ca6, 0x0000 }, + { 0x5ca7, 0x0000 }, + { 0x5ca8, 0x0000 }, + { 0x5ca9, 0x0000 }, + { 0x5caa, 0x0000 }, + { 0x5cab, 0x0000 }, + { 0x5cac, 0x0000 }, + { 0x5cad, 0x0000 }, + { 0x5cae, 0x0000 }, + { 0x5caf, 0x0000 }, + { 0x5cb0, 0x0000 }, + { 0x5cb1, 0x0000 }, + { 0x5cb2, 0x0000 }, + { 0x5cb3, 0x0000 }, + { 0x5cb4, 0x0000 }, + { 0x5cb5, 0x0000 }, + { 0x5cb6, 0x0000 }, + { 0x5cb7, 0x0000 }, + { 0x5cb8, 0x0000 }, + { 0x5cb9, 0x0000 }, + { 0x5cba, 0x0000 }, + { 0x5cbb, 0x0000 }, + { 0x5cbc, 0x0000 }, + { 0x5cbd, 0x0000 }, + { 0x5cbe, 0x0000 }, + { 0x5cbf, 0x0000 }, + { 0x5cc0, 0x0000 }, + { 0x5cc1, 0x0000 }, + { 0x5cc2, 0x0000 }, + { 0x5cc3, 0x0000 }, + { 0x5cc4, 0x0000 }, + { 0x5cc5, 0x0000 }, + { 0x5cc6, 0x0000 }, + { 0x5cc7, 0x0000 }, + { 0x5cc8, 0x0000 }, + { 0x5cc9, 0x0000 }, + { 0x5cca, 0x0000 }, + { 0x5ccb, 0x0000 }, + { 0x5ccc, 0x0000 }, + { 0x5ccd, 0x0000 }, + { 0x5cce, 0x0000 }, + { 0x5ccf, 0x0000 }, + { 0x5cd0, 0x0000 }, + { 0x5cd1, 0x0000 }, + { 0x5cd2, 0x0000 }, + { 0x5cd3, 0x0000 }, + { 0x5cd4, 0x0000 }, + { 0x5cd5, 0x0000 }, + { 0x5cd6, 0x0000 }, + { 0x5cd7, 0x0000 }, + { 0x5cd8, 0x0000 }, + { 0x5cd9, 0x0000 }, + { 0x5cda, 0x0000 }, + { 0x5cdb, 0x0000 }, + { 0x5cdc, 0x0000 }, + { 0x5cdd, 0x0000 }, + { 0x5cde, 0x0000 }, + { 0x5cdf, 0x0000 }, + { 0x5ce0, 0x0000 }, + { 0x5ce1, 0x0000 }, + { 0x5ce2, 0x0000 }, + { 0x5ce3, 0x0000 }, + { 0x5ce4, 0x0000 }, + { 0x5ce5, 0x0000 }, + { 0x5ce6, 0x0000 }, + { 0x5ce7, 0x0000 }, + { 0x5ce8, 0x0000 }, + { 0x5ce9, 0x0000 }, + { 0x5cea, 0x0000 }, + { 0x5ceb, 0x0000 }, + { 0x5cec, 0x0000 }, + { 0x5ced, 0x0000 }, + { 0x5cee, 0x0000 }, + { 0x5cef, 0x0000 }, + { 0x5cf0, 0x0000 }, + { 0x5cf1, 0x0000 }, + { 0x5cf2, 0x0000 }, + { 0x5cf3, 0x0000 }, + { 0x5cf4, 0x0000 }, + { 0x5cf5, 0x0000 }, + { 0x5cf6, 0x0000 }, + { 0x5cf7, 0x0000 }, + { 0x5cf8, 0x0000 }, + { 0x5cf9, 0x0000 }, + { 0x5cfa, 0x0000 }, + { 0x5cfb, 0x0000 }, + { 0x5cfc, 0x0000 }, + { 0x5cfd, 0x0000 }, + { 0x5cfe, 0x0000 }, + { 0x5cff, 0x0000 }, + { 0x5d00, 0x0000 }, + { 0x5d01, 0x0000 }, + { 0x5d02, 0x0000 }, + { 0x5d03, 0x0000 }, + { 0x5d04, 0x0000 }, + { 0x5d05, 0x0000 }, + { 0x5d06, 0x0000 }, + { 0x5d07, 0x0000 }, + { 0x5d08, 0x0000 }, + { 0x5d09, 0x0000 }, + { 0x5d0a, 0x0000 }, + { 0x5d0b, 0x0000 }, + { 0x5d0c, 0x0000 }, + { 0x5d0d, 0x0000 }, + { 0x5d0e, 0x0000 }, + { 0x5d0f, 0x0000 }, + { 0x5d10, 0x0000 }, + { 0x5d11, 0x0000 }, + { 0x5d12, 0x0000 }, + { 0x5d13, 0x0000 }, + { 0x5d14, 0x0000 }, + { 0x5d15, 0x0000 }, + { 0x5d16, 0x0000 }, + { 0x5d17, 0x0000 }, + { 0x5d18, 0x0000 }, + { 0x5d19, 0x0000 }, + { 0x5d1a, 0x0000 }, + { 0x5d1b, 0x0000 }, + { 0x5d1c, 0x0000 }, + { 0x5d1d, 0x0000 }, + { 0x5d1e, 0x0000 }, + { 0x5d1f, 0x0000 }, + { 0x5d20, 0x0000 }, + { 0x5d21, 0x0000 }, + { 0x5d22, 0x0000 }, + { 0x5d23, 0x0000 }, + { 0x5d24, 0x0000 }, + { 0x5d25, 0x0000 }, + { 0x5d26, 0x0000 }, + { 0x5d27, 0x0000 }, + { 0x5d28, 0x0000 }, + { 0x5d29, 0x0000 }, + { 0x5d2a, 0x0000 }, + { 0x5d2b, 0x0000 }, + { 0x5d2c, 0x0000 }, + { 0x5d2d, 0x0000 }, + { 0x5d2e, 0x0000 }, + { 0x5d2f, 0x0000 }, + { 0x5d30, 0x0000 }, + { 0x5d31, 0x0000 }, + { 0x5d32, 0x0000 }, + { 0x5d33, 0x0000 }, + { 0x5d34, 0x0000 }, + { 0x5d35, 0x0000 }, + { 0x5d36, 0x0000 }, + { 0x5d37, 0x0000 }, + { 0x5d38, 0x0000 }, + { 0x5d39, 0x0000 }, + { 0x5d3a, 0x0000 }, + { 0x5d3b, 0x0000 }, + { 0x5d3c, 0x0000 }, + { 0x5d3d, 0x0000 }, + { 0x5d3e, 0x0000 }, + { 0x5d3f, 0x0000 }, + { 0x5d40, 0x0000 }, + { 0x5d41, 0x0000 }, + { 0x5d42, 0x0000 }, + { 0x5d43, 0x0000 }, + { 0x5d44, 0x0000 }, + { 0x5d45, 0x0000 }, + { 0x5d46, 0x0000 }, + { 0x5d47, 0x0000 }, + { 0x5d48, 0x0000 }, + { 0x5d49, 0x0000 }, + { 0x5d4a, 0x0000 }, + { 0x5d4b, 0x0000 }, + { 0x5d4c, 0x0000 }, + { 0x5d4d, 0x0000 }, + { 0x5d4e, 0x0000 }, + { 0x5d4f, 0x0000 }, + { 0x5d50, 0x0000 }, + { 0x5d51, 0x0000 }, + { 0x5d52, 0x0000 }, + { 0x5d53, 0x0000 }, + { 0x5d54, 0x0000 }, + { 0x5d55, 0x0000 }, + { 0x5d56, 0x0000 }, + { 0x5d57, 0x0000 }, + { 0x5d58, 0x0000 }, + { 0x5d59, 0x0000 }, + { 0x5d5a, 0x0000 }, + { 0x5d5b, 0x0000 }, + { 0x5d5c, 0x0000 }, + { 0x5d5d, 0x0000 }, + { 0x5d5e, 0x0000 }, + { 0x5d5f, 0x0000 }, + { 0x5d60, 0x0000 }, + { 0x5d61, 0x0000 }, + { 0x5d62, 0x0000 }, + { 0x5d63, 0x0000 }, + { 0x5d64, 0x0000 }, + { 0x5d65, 0x0000 }, + { 0x5d66, 0x0000 }, + { 0x5d67, 0x0000 }, + { 0x5d68, 0x0000 }, + { 0x5d69, 0x0000 }, + { 0x5d6a, 0x0000 }, + { 0x5d6b, 0x0000 }, + { 0x5d6c, 0x0000 }, + { 0x5d6d, 0x0000 }, + { 0x5d6e, 0x0000 }, + { 0x5d6f, 0x0000 }, + { 0x5d70, 0x0000 }, + { 0x5d71, 0x0000 }, + { 0x5d72, 0x0000 }, + { 0x5d73, 0x0000 }, + { 0x5d74, 0x0000 }, + { 0x5d75, 0x0000 }, + { 0x5d76, 0x0000 }, + { 0x5d77, 0x0000 }, + { 0x5d78, 0x0000 }, + { 0x5d79, 0x0000 }, + { 0x5d7a, 0x0000 }, + { 0x5d7b, 0x0000 }, + { 0x5d7c, 0x0000 }, + { 0x5d7d, 0x0000 }, + { 0x5d7e, 0x0000 }, + { 0x5d7f, 0x0000 }, + { 0x5d80, 0x0000 }, + { 0x5d81, 0x0000 }, + { 0x5d82, 0x0000 }, + { 0x5d83, 0x0000 }, + { 0x5d84, 0x0000 }, + { 0x5d85, 0x0000 }, + { 0x5d86, 0x0000 }, + { 0x5d87, 0x0000 }, + { 0x5d88, 0x0000 }, + { 0x5d89, 0x0000 }, + { 0x5d8a, 0x0000 }, + { 0x5d8b, 0x0000 }, + { 0x5d8c, 0x0000 }, + { 0x5d8d, 0x0000 }, + { 0x5d8e, 0x0000 }, + { 0x5d8f, 0x0000 }, + { 0x5d90, 0x0000 }, + { 0x5d91, 0x0000 }, + { 0x5d92, 0x0000 }, + { 0x5d93, 0x0000 }, + { 0x5d94, 0x0000 }, + { 0x5d95, 0x0000 }, + { 0x5d96, 0x0000 }, + { 0x5d97, 0x0000 }, + { 0x5d98, 0x0000 }, + { 0x5d99, 0x0000 }, + { 0x5d9a, 0x0000 }, + { 0x5d9b, 0x0000 }, + { 0x5d9c, 0x0000 }, + { 0x5d9d, 0x0000 }, + { 0x5d9e, 0x0000 }, + { 0x5d9f, 0x0000 }, + { 0x5da0, 0x0000 }, + { 0x5da1, 0x0000 }, + { 0x5da2, 0x0000 }, + { 0x5da3, 0x0000 }, + { 0x5da4, 0x0000 }, + { 0x5da5, 0x0000 }, + { 0x5da6, 0x0000 }, + { 0x5da7, 0x0000 }, + { 0x5da8, 0x0000 }, + { 0x5da9, 0x0000 }, + { 0x5daa, 0x0000 }, + { 0x5dab, 0x0000 }, + { 0x5dac, 0x0000 }, + { 0x5dad, 0x0000 }, + { 0x5dae, 0x0000 }, + { 0x5daf, 0x0000 }, + { 0x5db0, 0x0000 }, + { 0x5db1, 0x0000 }, + { 0x5db2, 0x0000 }, + { 0x5db3, 0x0000 }, + { 0x5db4, 0x0000 }, + { 0x5db5, 0x0000 }, + { 0x5db6, 0x0000 }, + { 0x5db7, 0x0000 }, + { 0x5db8, 0x0000 }, + { 0x5db9, 0x0000 }, + { 0x5dba, 0x0000 }, + { 0x5dbb, 0x0000 }, + { 0x5dbc, 0x0000 }, + { 0x5dbd, 0x0000 }, + { 0x5dbe, 0x0000 }, + { 0x5dbf, 0x0000 }, + { 0x5dc0, 0x0000 }, + { 0x5dc1, 0x0000 }, + { 0x5dc2, 0x0000 }, + { 0x5dc3, 0x0000 }, + { 0x5dc4, 0x0000 }, + { 0x5dc5, 0x0000 }, + { 0x5dc6, 0x0000 }, + { 0x5dc7, 0x0000 }, + { 0x5dc8, 0x0000 }, + { 0x5dc9, 0x0000 }, + { 0x5dca, 0x0000 }, + { 0x5dcb, 0x0000 }, + { 0x5dcc, 0x0000 }, + { 0x5dcd, 0x0000 }, + { 0x5dce, 0x0000 }, + { 0x5dcf, 0x0000 }, + { 0x5dd0, 0x0000 }, + { 0x5dd1, 0x0000 }, + { 0x5dd2, 0x0000 }, + { 0x5dd3, 0x0000 }, + { 0x5dd4, 0x0000 }, + { 0x5dd5, 0x0000 }, + { 0x5dd6, 0x0000 }, + { 0x5dd7, 0x0000 }, + { 0x5dd8, 0x0000 }, + { 0x5dd9, 0x0000 }, + { 0x5dda, 0x0000 }, + { 0x5ddb, 0x0000 }, + { 0x5ddc, 0x0000 }, + { 0x5ddd, 0x0000 }, + { 0x5dde, 0x0000 }, + { 0x5ddf, 0x0000 }, + { 0x5de0, 0x0000 }, + { 0x5de1, 0x0000 }, + { 0x5de2, 0x0000 }, + { 0x5de3, 0x0000 }, + { 0x5de4, 0x0000 }, + { 0x5de5, 0x0000 }, + { 0x5de6, 0x0000 }, + { 0x5de7, 0x0000 }, + { 0x5de8, 0x0000 }, + { 0x5de9, 0x0000 }, + { 0x5dea, 0x0000 }, + { 0x5deb, 0x0000 }, + { 0x5dec, 0x0000 }, + { 0x5ded, 0x0000 }, + { 0x5dee, 0x0000 }, + { 0x5def, 0x0000 }, + { 0x5df0, 0x0000 }, + { 0x5df1, 0x0000 }, + { 0x5df2, 0x0000 }, + { 0x5df3, 0x0000 }, + { 0x5df4, 0x0000 }, + { 0x5df5, 0x0000 }, + { 0x5df6, 0x0000 }, + { 0x5df7, 0x0000 }, + { 0x5df8, 0x0000 }, + { 0x5df9, 0x0000 }, + { 0x5dfa, 0x0000 }, + { 0x5dfb, 0x0000 }, + { 0x5dfc, 0x0000 }, + { 0x5dfd, 0x0000 }, + { 0x5dfe, 0x0000 }, + { 0x5dff, 0x0000 }, + { 0x5e00, 0x0000 }, + { 0x5e01, 0x0000 }, + { 0x5e02, 0x0000 }, + { 0x5e03, 0x0000 }, + { 0x5e04, 0x0000 }, + { 0x5e05, 0x0000 }, + { 0x5e06, 0x0000 }, + { 0x5e07, 0x0000 }, + { 0x5e08, 0x0000 }, + { 0x5e09, 0x0000 }, + { 0x5e0a, 0x0000 }, + { 0x5e0b, 0x0000 }, + { 0x5e0c, 0x0000 }, + { 0x5e0d, 0x0000 }, + { 0x5e0e, 0x0000 }, + { 0x5e0f, 0x0000 }, + { 0x5e10, 0x0000 }, + { 0x5e11, 0x0000 }, + { 0x5e12, 0x0000 }, + { 0x5e13, 0x0000 }, + { 0x5e14, 0x0000 }, + { 0x5e15, 0x0000 }, + { 0x5e16, 0x0000 }, + { 0x5e17, 0x0000 }, + { 0x5e18, 0x0000 }, + { 0x5e19, 0x0000 }, + { 0x5e1a, 0x0000 }, + { 0x5e1b, 0x0000 }, + { 0x5e1c, 0x0000 }, + { 0x5e1d, 0x0000 }, + { 0x5e1e, 0x0000 }, + { 0x5e1f, 0x0000 }, + { 0x5e20, 0x0000 }, + { 0x5e21, 0x0000 }, + { 0x5e22, 0x0000 }, + { 0x5e23, 0x0000 }, + { 0x5e24, 0x0000 }, + { 0x5e25, 0x0000 }, + { 0x5e26, 0x0000 }, + { 0x5e27, 0x0000 }, + { 0x5e28, 0x0000 }, + { 0x5e29, 0x0000 }, + { 0x5e2a, 0x0000 }, + { 0x5e2b, 0x0000 }, + { 0x5e2c, 0x0000 }, + { 0x5e2d, 0x0000 }, + { 0x5e2e, 0x0000 }, + { 0x5e2f, 0x0000 }, + { 0x5e30, 0x0000 }, + { 0x5e31, 0x0000 }, + { 0x5e32, 0x0000 }, + { 0x5e33, 0x0000 }, + { 0x5e34, 0x0000 }, + { 0x5e35, 0x0000 }, + { 0x5e36, 0x0000 }, + { 0x5e37, 0x0000 }, + { 0x5e38, 0x0000 }, + { 0x5e39, 0x0000 }, + { 0x5e3a, 0x0000 }, + { 0x5e3b, 0x0000 }, + { 0x5e3c, 0x0000 }, + { 0x5e3d, 0x0000 }, + { 0x5e3e, 0x0000 }, + { 0x5e3f, 0x0000 }, + { 0x5e40, 0x0000 }, + { 0x5e41, 0x0000 }, + { 0x5e42, 0x0000 }, + { 0x5e43, 0x0000 }, + { 0x5e44, 0x0000 }, + { 0x5e45, 0x0000 }, + { 0x5e46, 0x0000 }, + { 0x5e47, 0x0000 }, + { 0x5e48, 0x0000 }, + { 0x5e49, 0x0000 }, + { 0x5e4a, 0x0000 }, + { 0x5e4b, 0x0000 }, + { 0x5e4c, 0x0000 }, + { 0x5e4d, 0x0000 }, + { 0x5e4e, 0x0000 }, + { 0x5e4f, 0x0000 }, + { 0x5e50, 0x0000 }, + { 0x5e51, 0x0000 }, + { 0x5e52, 0x0000 }, + { 0x5e53, 0x0000 }, + { 0x5e54, 0x0000 }, + { 0x5e55, 0x0000 }, + { 0x5e56, 0x0000 }, + { 0x5e57, 0x0000 }, + { 0x5e58, 0x0000 }, + { 0x5e59, 0x0000 }, + { 0x5e5a, 0x0000 }, + { 0x5e5b, 0x0000 }, + { 0x5e5c, 0x0000 }, + { 0x5e5d, 0x0000 }, + { 0x5e5e, 0x0000 }, + { 0x5e5f, 0x0000 }, + { 0x5e60, 0x0000 }, + { 0x5e61, 0x0000 }, + { 0x5e62, 0x0000 }, + { 0x5e63, 0x0000 }, + { 0x5e64, 0x0000 }, + { 0x5e65, 0x0000 }, + { 0x5e66, 0x0000 }, + { 0x5e67, 0x0000 }, + { 0x5e68, 0x0000 }, + { 0x5e69, 0x0000 }, + { 0x5e6a, 0x0000 }, + { 0x5e6b, 0x0000 }, + { 0x5e6c, 0x0000 }, + { 0x5e6d, 0x0000 }, + { 0x5e6e, 0x0000 }, + { 0x5e6f, 0x0000 }, + { 0x5e70, 0x0000 }, + { 0x5e71, 0x0000 }, + { 0x5e72, 0x0000 }, + { 0x5e73, 0x0000 }, + { 0x5e74, 0x0000 }, + { 0x5e75, 0x0000 }, + { 0x5e76, 0x0000 }, + { 0x5e77, 0x0000 }, + { 0x5e78, 0x0000 }, + { 0x5e79, 0x0000 }, + { 0x5e7a, 0x0000 }, + { 0x5e7b, 0x0000 }, + { 0x5e7c, 0x0000 }, + { 0x5e7d, 0x0000 }, + { 0x5e7e, 0x0000 }, + { 0x5e7f, 0x0000 }, + { 0x5e80, 0x0000 }, + { 0x5e81, 0x0000 }, + { 0x5e82, 0x0000 }, + { 0x5e83, 0x0000 }, + { 0x5e84, 0x0000 }, + { 0x5e85, 0x0000 }, + { 0x5e86, 0x0000 }, + { 0x5e87, 0x0000 }, + { 0x5e88, 0x0000 }, + { 0x5e89, 0x0000 }, + { 0x5e8a, 0x0000 }, + { 0x5e8b, 0x0000 }, + { 0x5e8c, 0x0000 }, + { 0x5e8d, 0x0000 }, + { 0x5e8e, 0x0000 }, + { 0x5e8f, 0x0000 }, + { 0x5e90, 0x0000 }, + { 0x5e91, 0x0000 }, + { 0x5e92, 0x0000 }, + { 0x5e93, 0x0000 }, + { 0x5e94, 0x0000 }, + { 0x5e95, 0x0000 }, + { 0x5e96, 0x0000 }, + { 0x5e97, 0x0000 }, + { 0x5e98, 0x0000 }, + { 0x5e99, 0x0000 }, + { 0x5e9a, 0x0000 }, + { 0x5e9b, 0x0000 }, + { 0x5e9c, 0x0000 }, + { 0x5e9d, 0x0000 }, + { 0x5e9e, 0x0000 }, + { 0x5e9f, 0x0000 }, + { 0x5ea0, 0x0000 }, + { 0x5ea1, 0x0000 }, + { 0x5ea2, 0x0000 }, + { 0x5ea3, 0x0000 }, + { 0x5ea4, 0x0000 }, + { 0x5ea5, 0x0000 }, + { 0x5ea6, 0x0000 }, + { 0x5ea7, 0x0000 }, + { 0x5ea8, 0x0000 }, + { 0x5ea9, 0x0000 }, + { 0x5eaa, 0x0000 }, + { 0x5eab, 0x0000 }, + { 0x5eac, 0x0000 }, + { 0x5ead, 0x0000 }, + { 0x5eae, 0x0000 }, + { 0x5eaf, 0x0000 }, + { 0x5eb0, 0x0000 }, + { 0x5eb1, 0x0000 }, + { 0x5eb2, 0x0000 }, + { 0x5eb3, 0x0000 }, + { 0x5eb4, 0x0000 }, + { 0x5eb5, 0x0000 }, + { 0x5eb6, 0x0000 }, + { 0x5eb7, 0x0000 }, + { 0x5eb8, 0x0000 }, + { 0x5eb9, 0x0000 }, + { 0x5eba, 0x0000 }, + { 0x5ebb, 0x0000 }, + { 0x5ebc, 0x0000 }, + { 0x5ebd, 0x0000 }, + { 0x5ebe, 0x0000 }, + { 0x5ebf, 0x0000 }, + { 0x5ec0, 0x0000 }, + { 0x5ec1, 0x0000 }, + { 0x5ec2, 0x0000 }, + { 0x5ec3, 0x0000 }, + { 0x5ec4, 0x0000 }, + { 0x5ec5, 0x0000 }, + { 0x5ec6, 0x0000 }, + { 0x5ec7, 0x0000 }, + { 0x5ec8, 0x0000 }, + { 0x5ec9, 0x0000 }, + { 0x5eca, 0x0000 }, + { 0x5ecb, 0x0000 }, + { 0x5ecc, 0x0000 }, + { 0x5ecd, 0x0000 }, + { 0x5ece, 0x0000 }, + { 0x5ecf, 0x0000 }, + { 0x5ed0, 0x0000 }, + { 0x5ed1, 0x0000 }, + { 0x5ed2, 0x0000 }, + { 0x5ed3, 0x0000 }, + { 0x5ed4, 0x0000 }, + { 0x5ed5, 0x0000 }, + { 0x5ed6, 0x0000 }, + { 0x5ed7, 0x0000 }, + { 0x5ed8, 0x0000 }, + { 0x5ed9, 0x0000 }, + { 0x5eda, 0x0000 }, + { 0x5edb, 0x0000 }, + { 0x5edc, 0x0000 }, + { 0x5edd, 0x0000 }, + { 0x5ede, 0x0000 }, + { 0x5edf, 0x0000 }, + { 0x5ee0, 0x0000 }, + { 0x5ee1, 0x0000 }, + { 0x5ee2, 0x0000 }, + { 0x5ee3, 0x0000 }, + { 0x5ee4, 0x0000 }, + { 0x5ee5, 0x0000 }, + { 0x5ee6, 0x0000 }, + { 0x5ee7, 0x0000 }, + { 0x5ee8, 0x0000 }, + { 0x5ee9, 0x0000 }, + { 0x5eea, 0x0000 }, + { 0x5eeb, 0x0000 }, + { 0x5eec, 0x0000 }, + { 0x5eed, 0x0000 }, + { 0x5eee, 0x0000 }, + { 0x5eef, 0x0000 }, + { 0x5ef0, 0x0000 }, + { 0x5ef1, 0x0000 }, + { 0x5ef2, 0x0000 }, + { 0x5ef3, 0x0000 }, + { 0x5ef4, 0x0000 }, + { 0x5ef5, 0x0000 }, + { 0x5ef6, 0x0000 }, + { 0x5ef7, 0x0000 }, + { 0x5ef8, 0x0000 }, + { 0x5ef9, 0x0000 }, + { 0x5efa, 0x0000 }, + { 0x5efb, 0x0000 }, + { 0x5efc, 0x0000 }, + { 0x5efd, 0x0000 }, + { 0x5efe, 0x0000 }, + { 0x5eff, 0x0000 }, + { 0x5f00, 0x0000 }, + { 0x5f01, 0x0000 }, + { 0x5f02, 0x0000 }, + { 0x5f03, 0x0000 }, + { 0x5f04, 0x0000 }, + { 0x5f05, 0x0000 }, + { 0x5f06, 0x0000 }, + { 0x5f07, 0x0000 }, + { 0x5f08, 0x0000 }, + { 0x5f09, 0x0000 }, + { 0x5f0a, 0x0000 }, + { 0x5f0b, 0x0000 }, + { 0x5f0c, 0x0000 }, + { 0x5f0d, 0x0000 }, + { 0x5f0e, 0x0000 }, + { 0x5f0f, 0x0000 }, + { 0x5f10, 0x0000 }, + { 0x5f11, 0x0000 }, + { 0x5f12, 0x0000 }, + { 0x5f13, 0x0000 }, + { 0x5f14, 0x0000 }, + { 0x5f15, 0x0000 }, + { 0x5f16, 0x0000 }, + { 0x5f17, 0x0000 }, + { 0x5f18, 0x0000 }, + { 0x5f19, 0x0000 }, + { 0x5f1a, 0x0000 }, + { 0x5f1b, 0x0000 }, + { 0x5f1c, 0x0000 }, + { 0x5f1d, 0x0000 }, + { 0x5f1e, 0x0000 }, + { 0x5f1f, 0x0000 }, + { 0x5f20, 0x0000 }, + { 0x5f21, 0x0000 }, + { 0x5f22, 0x0000 }, + { 0x5f23, 0x0000 }, + { 0x5f24, 0x0000 }, + { 0x5f25, 0x0000 }, + { 0x5f26, 0x0000 }, + { 0x5f27, 0x0000 }, + { 0x5f28, 0x0000 }, + { 0x5f29, 0x0000 }, + { 0x5f2a, 0x0000 }, + { 0x5f2b, 0x0000 }, + { 0x5f2c, 0x0000 }, + { 0x5f2d, 0x0000 }, + { 0x5f2e, 0x0000 }, + { 0x5f2f, 0x0000 }, + { 0x5f30, 0x0000 }, + { 0x5f31, 0x0000 }, + { 0x5f32, 0x0000 }, + { 0x5f33, 0x0000 }, + { 0x5f34, 0x0000 }, + { 0x5f35, 0x0000 }, + { 0x5f36, 0x0000 }, + { 0x5f37, 0x0000 }, + { 0x5f38, 0x0000 }, + { 0x5f39, 0x0000 }, + { 0x5f3a, 0x0000 }, + { 0x5f3b, 0x0000 }, + { 0x5f3c, 0x0000 }, + { 0x5f3d, 0x0000 }, + { 0x5f3e, 0x0000 }, + { 0x5f3f, 0x0000 }, + { 0x5f40, 0x0000 }, + { 0x5f41, 0x0000 }, + { 0x5f42, 0x0000 }, + { 0x5f43, 0x0000 }, + { 0x5f44, 0x0000 }, + { 0x5f45, 0x0000 }, + { 0x5f46, 0x0000 }, + { 0x5f47, 0x0000 }, + { 0x5f48, 0x0000 }, + { 0x5f49, 0x0000 }, + { 0x5f4a, 0x0000 }, + { 0x5f4b, 0x0000 }, + { 0x5f4c, 0x0000 }, + { 0x5f4d, 0x0000 }, + { 0x5f4e, 0x0000 }, + { 0x5f4f, 0x0000 }, + { 0x5f50, 0x0000 }, + { 0x5f51, 0x0000 }, + { 0x5f52, 0x0000 }, + { 0x5f53, 0x0000 }, + { 0x5f54, 0x0000 }, + { 0x5f55, 0x0000 }, + { 0x5f56, 0x0000 }, + { 0x5f57, 0x0000 }, + { 0x5f58, 0x0000 }, + { 0x5f59, 0x0000 }, + { 0x5f5a, 0x0000 }, + { 0x5f5b, 0x0000 }, + { 0x5f5c, 0x0000 }, + { 0x5f5d, 0x0000 }, + { 0x5f5e, 0x0000 }, + { 0x5f5f, 0x0000 }, + { 0x5f60, 0x0000 }, + { 0x5f61, 0x0000 }, + { 0x5f62, 0x0000 }, + { 0x5f63, 0x0000 }, + { 0x5f64, 0x0000 }, + { 0x5f65, 0x0000 }, + { 0x5f66, 0x0000 }, + { 0x5f67, 0x0000 }, + { 0x5f68, 0x0000 }, + { 0x5f69, 0x0000 }, + { 0x5f6a, 0x0000 }, + { 0x5f6b, 0x0000 }, + { 0x5f6c, 0x0000 }, + { 0x5f6d, 0x0000 }, + { 0x5f6e, 0x0000 }, + { 0x5f6f, 0x0000 }, + { 0x5f70, 0x0000 }, + { 0x5f71, 0x0000 }, + { 0x5f72, 0x0000 }, + { 0x5f73, 0x0000 }, + { 0x5f74, 0x0000 }, + { 0x5f75, 0x0000 }, + { 0x5f76, 0x0000 }, + { 0x5f77, 0x0000 }, + { 0x5f78, 0x0000 }, + { 0x5f79, 0x0000 }, + { 0x5f7a, 0x0000 }, + { 0x5f7b, 0x0000 }, + { 0x5f7c, 0x0000 }, + { 0x5f7d, 0x0000 }, + { 0x5f7e, 0x0000 }, + { 0x5f7f, 0x0000 }, + { 0x5f80, 0x0000 }, + { 0x5f81, 0x0000 }, + { 0x5f82, 0x0000 }, + { 0x5f83, 0x0000 }, + { 0x5f84, 0x0000 }, + { 0x5f85, 0x0000 }, + { 0x5f86, 0x0000 }, + { 0x5f87, 0x0000 }, + { 0x5f88, 0x0000 }, + { 0x5f89, 0x0000 }, + { 0x5f8a, 0x0000 }, + { 0x5f8b, 0x0000 }, + { 0x5f8c, 0x0000 }, + { 0x5f8d, 0x0000 }, + { 0x5f8e, 0x0000 }, + { 0x5f8f, 0x0000 }, + { 0x5f90, 0x0000 }, + { 0x5f91, 0x0000 }, + { 0x5f92, 0x0000 }, + { 0x5f93, 0x0000 }, + { 0x5f94, 0x0000 }, + { 0x5f95, 0x0000 }, + { 0x5f96, 0x0000 }, + { 0x5f97, 0x0000 }, + { 0x5f98, 0x0000 }, + { 0x5f99, 0x0000 }, + { 0x5f9a, 0x0000 }, + { 0x5f9b, 0x0000 }, + { 0x5f9c, 0x0000 }, + { 0x5f9d, 0x0000 }, + { 0x5f9e, 0x0000 }, + { 0x5f9f, 0x0000 }, + { 0x5fa0, 0x0000 }, + { 0x5fa1, 0x0000 }, + { 0x5fa2, 0x0000 }, + { 0x5fa3, 0x0000 }, + { 0x5fa4, 0x0000 }, + { 0x5fa5, 0x0000 }, + { 0x5fa6, 0x0000 }, + { 0x5fa7, 0x0000 }, + { 0x5fa8, 0x0000 }, + { 0x5fa9, 0x0000 }, + { 0x5faa, 0x0000 }, + { 0x5fab, 0x0000 }, + { 0x5fac, 0x0000 }, + { 0x5fad, 0x0000 }, + { 0x5fae, 0x0000 }, + { 0x5faf, 0x0000 }, + { 0x5fb0, 0x0000 }, + { 0x5fb1, 0x0000 }, + { 0x5fb2, 0x0000 }, + { 0x5fb3, 0x0000 }, + { 0x5fb4, 0x0000 }, + { 0x5fb5, 0x0000 }, + { 0x5fb6, 0x0000 }, + { 0x5fb7, 0x0000 }, + { 0x5fb8, 0x0000 }, + { 0x5fb9, 0x0000 }, + { 0x5fba, 0x0000 }, + { 0x5fbb, 0x0000 }, + { 0x5fbc, 0x0000 }, + { 0x5fbd, 0x0000 }, + { 0x5fbe, 0x0000 }, + { 0x5fbf, 0x0000 }, + { 0x5fc0, 0x0000 }, + { 0x5fc1, 0x0000 }, + { 0x5fc2, 0x0000 }, + { 0x5fc3, 0x0000 }, + { 0x5fc4, 0x0000 }, + { 0x5fc5, 0x0000 }, + { 0x5fc6, 0x0000 }, + { 0x5fc7, 0x0000 }, + { 0x5fc8, 0x0000 }, + { 0x5fc9, 0x0000 }, + { 0x5fca, 0x0000 }, + { 0x5fcb, 0x0000 }, + { 0x5fcc, 0x0000 }, + { 0x5fcd, 0x0000 }, + { 0x5fce, 0x0000 }, + { 0x5fcf, 0x0000 }, + { 0x5fd0, 0x0000 }, + { 0x5fd1, 0x0000 }, + { 0x5fd2, 0x0000 }, + { 0x5fd3, 0x0000 }, + { 0x5fd4, 0x0000 }, + { 0x5fd5, 0x0000 }, + { 0x5fd6, 0x0000 }, + { 0x5fd7, 0x0000 }, + { 0x5fd8, 0x0000 }, + { 0x5fd9, 0x0000 }, + { 0x5fda, 0x0000 }, + { 0x5fdb, 0x0000 }, + { 0x5fdc, 0x0000 }, + { 0x5fdd, 0x0000 }, + { 0x5fde, 0x0000 }, + { 0x5fdf, 0x0000 }, + { 0x5fe0, 0x0000 }, + { 0x5fe1, 0x0000 }, + { 0x5fe2, 0x0000 }, + { 0x5fe3, 0x0000 }, + { 0x5fe4, 0x0000 }, + { 0x5fe5, 0x0000 }, + { 0x5fe6, 0x0000 }, + { 0x5fe7, 0x0000 }, + { 0x5fe8, 0x0000 }, + { 0x5fe9, 0x0000 }, + { 0x5fea, 0x0000 }, + { 0x5feb, 0x0000 }, + { 0x5fec, 0x0000 }, + { 0x5fed, 0x0000 }, + { 0x5fee, 0x0000 }, + { 0x5fef, 0x0000 }, + { 0x5ff0, 0x0000 }, + { 0x5ff1, 0x0000 }, + { 0x5ff2, 0x0000 }, + { 0x5ff3, 0x0000 }, + { 0x5ff4, 0x0000 }, + { 0x5ff5, 0x0000 }, + { 0x5ff6, 0x0000 }, + { 0x5ff7, 0x0000 }, + { 0x5ff8, 0x0000 }, + { 0x5ff9, 0x0000 }, + { 0x5ffa, 0x0000 }, + { 0x5ffb, 0x0000 }, + { 0x5ffc, 0x0000 }, + { 0x5ffd, 0x0000 }, + { 0x5ffe, 0x0000 }, + { 0x5fff, 0x0000 }, + { 0x6000, 0x0000 }, + { 0x6001, 0x0000 }, + { 0x6002, 0x0000 }, + { 0x6003, 0x0000 }, + { 0x6004, 0x0000 }, + { 0x6005, 0x0000 }, + { 0x6006, 0x0000 }, + { 0x6007, 0x0000 }, + { 0x6008, 0x0000 }, + { 0x6009, 0x0000 }, + { 0x600a, 0x0000 }, + { 0x600b, 0x0000 }, + { 0x600c, 0x0000 }, + { 0x600d, 0x0000 }, + { 0x600e, 0x0000 }, + { 0x600f, 0x0000 }, + { 0x6010, 0x0000 }, + { 0x6011, 0x0000 }, + { 0x6012, 0x0000 }, + { 0x6013, 0x0000 }, + { 0x6014, 0x0000 }, + { 0x6015, 0x0000 }, + { 0x6016, 0x0000 }, + { 0x6017, 0x0000 }, + { 0x6018, 0x0000 }, + { 0x6019, 0x0000 }, + { 0x601a, 0x0000 }, + { 0x601b, 0x0000 }, + { 0x601c, 0x0000 }, + { 0x601d, 0x0000 }, + { 0x601e, 0x0000 }, + { 0x601f, 0x0000 }, + { 0x6020, 0x0000 }, + { 0x6021, 0x0000 }, + { 0x6022, 0x0000 }, + { 0x6023, 0x0000 }, + { 0x6024, 0x0000 }, + { 0x6025, 0x0000 }, + { 0x6026, 0x0000 }, + { 0x6027, 0x0000 }, + { 0x6028, 0x0000 }, + { 0x6029, 0x0000 }, + { 0x602a, 0x0000 }, + { 0x602b, 0x0000 }, + { 0x602c, 0x0000 }, + { 0x602d, 0x0000 }, + { 0x602e, 0x0000 }, + { 0x602f, 0x0000 }, + { 0x6030, 0x0000 }, + { 0x6031, 0x0000 }, + { 0x6032, 0x0000 }, + { 0x6033, 0x0000 }, + { 0x6034, 0x0000 }, + { 0x6035, 0x0000 }, + { 0x6036, 0x0000 }, + { 0x6037, 0x0000 }, + { 0x6038, 0x0000 }, + { 0x6039, 0x0000 }, + { 0x603a, 0x0000 }, + { 0x603b, 0x0000 }, + { 0x603c, 0x0000 }, + { 0x603d, 0x0000 }, + { 0x603e, 0x0000 }, + { 0x603f, 0x0000 }, + { 0x6040, 0x0000 }, + { 0x6041, 0x0000 }, + { 0x6042, 0x0000 }, + { 0x6043, 0x0000 }, + { 0x6044, 0x0000 }, + { 0x6045, 0x0000 }, + { 0x6046, 0x0000 }, + { 0x6047, 0x0000 }, + { 0x6048, 0x0000 }, + { 0x6049, 0x0000 }, + { 0x604a, 0x0000 }, + { 0x604b, 0x0000 }, + { 0x604c, 0x0000 }, + { 0x604d, 0x0000 }, + { 0x604e, 0x0000 }, + { 0x604f, 0x0000 }, + { 0x6050, 0x0000 }, + { 0x6051, 0x0000 }, + { 0x6052, 0x0000 }, + { 0x6053, 0x0000 }, + { 0x6054, 0x0000 }, + { 0x6055, 0x0000 }, + { 0x6056, 0x0000 }, + { 0x6057, 0x0000 }, + { 0x6058, 0x0000 }, + { 0x6059, 0x0000 }, + { 0x605a, 0x0000 }, + { 0x605b, 0x0000 }, + { 0x605c, 0x0000 }, + { 0x605d, 0x0000 }, + { 0x605e, 0x0000 }, + { 0x605f, 0x0000 }, + { 0x6060, 0x0000 }, + { 0x6061, 0x0000 }, + { 0x6062, 0x0000 }, + { 0x6063, 0x0000 }, + { 0x6064, 0x0000 }, + { 0x6065, 0x0000 }, + { 0x6066, 0x0000 }, + { 0x6067, 0x0000 }, + { 0x6068, 0x0000 }, + { 0x6069, 0x0000 }, + { 0x606a, 0x0000 }, + { 0x606b, 0x0000 }, + { 0x606c, 0x0000 }, + { 0x606d, 0x0000 }, + { 0x606e, 0x0000 }, + { 0x606f, 0x0000 }, + { 0x6070, 0x0000 }, + { 0x6071, 0x0000 }, + { 0x6072, 0x0000 }, + { 0x6073, 0x0000 }, + { 0x6074, 0x0000 }, + { 0x6075, 0x0000 }, + { 0x6076, 0x0000 }, + { 0x6077, 0x0000 }, + { 0x6078, 0x0000 }, + { 0x6079, 0x0000 }, + { 0x607a, 0x0000 }, + { 0x607b, 0x0000 }, + { 0x607c, 0x0000 }, + { 0x607d, 0x0000 }, + { 0x607e, 0x0000 }, + { 0x607f, 0x0000 }, + { 0x6080, 0x0000 }, + { 0x6081, 0x0000 }, + { 0x6082, 0x0000 }, + { 0x6083, 0x0000 }, + { 0x6084, 0x0000 }, + { 0x6085, 0x0000 }, + { 0x6086, 0x0000 }, + { 0x6087, 0x0000 }, + { 0x6088, 0x0000 }, + { 0x6089, 0x0000 }, + { 0x608a, 0x0000 }, + { 0x608b, 0x0000 }, + { 0x608c, 0x0000 }, + { 0x608d, 0x0000 }, + { 0x608e, 0x0000 }, + { 0x608f, 0x0000 }, + { 0x6090, 0x0000 }, + { 0x6091, 0x0000 }, + { 0x6092, 0x0000 }, + { 0x6093, 0x0000 }, + { 0x6094, 0x0000 }, + { 0x6095, 0x0000 }, + { 0x6096, 0x0000 }, + { 0x6097, 0x0000 }, + { 0x6098, 0x0000 }, + { 0x6099, 0x0000 }, + { 0x609a, 0x0000 }, + { 0x609b, 0x0000 }, + { 0x609c, 0x0000 }, + { 0x609d, 0x0000 }, + { 0x609e, 0x0000 }, + { 0x609f, 0x0000 }, + { 0x60a0, 0x0000 }, + { 0x60a1, 0x0000 }, + { 0x60a2, 0x0000 }, + { 0x60a3, 0x0000 }, + { 0x60a4, 0x0000 }, + { 0x60a5, 0x0000 }, + { 0x60a6, 0x0000 }, + { 0x60a7, 0x0000 }, + { 0x60a8, 0x0000 }, + { 0x60a9, 0x0000 }, + { 0x60aa, 0x0000 }, + { 0x60ab, 0x0000 }, + { 0x60ac, 0x0000 }, + { 0x60ad, 0x0000 }, + { 0x60ae, 0x0000 }, + { 0x60af, 0x0000 }, + { 0x60b0, 0x0000 }, + { 0x60b1, 0x0000 }, + { 0x60b2, 0x0000 }, + { 0x60b3, 0x0000 }, + { 0x60b4, 0x0000 }, + { 0x60b5, 0x0000 }, + { 0x60b6, 0x0000 }, + { 0x60b7, 0x0000 }, + { 0x60b8, 0x0000 }, + { 0x60b9, 0x0000 }, + { 0x60ba, 0x0000 }, + { 0x60bb, 0x0000 }, + { 0x60bc, 0x0000 }, + { 0x60bd, 0x0000 }, + { 0x60be, 0x0000 }, + { 0x60bf, 0x0000 }, + { 0x60c0, 0x0000 }, + { 0x60c1, 0x0000 }, + { 0x60c2, 0x0000 }, + { 0x60c3, 0x0000 }, + { 0x60c4, 0x0000 }, + { 0x60c5, 0x0000 }, + { 0x60c6, 0x0000 }, + { 0x60c7, 0x0000 }, + { 0x60c8, 0x0000 }, + { 0x60c9, 0x0000 }, + { 0x60ca, 0x0000 }, + { 0x60cb, 0x0000 }, + { 0x60cc, 0x0000 }, + { 0x60cd, 0x0000 }, + { 0x60ce, 0x0000 }, + { 0x60cf, 0x0000 }, + { 0x60d0, 0x0000 }, + { 0x60d1, 0x0000 }, + { 0x60d2, 0x0000 }, + { 0x60d3, 0x0000 }, + { 0x60d4, 0x0000 }, + { 0x60d5, 0x0000 }, + { 0x60d6, 0x0000 }, + { 0x60d7, 0x0000 }, + { 0x60d8, 0x0000 }, + { 0x60d9, 0x0000 }, + { 0x60da, 0x0000 }, + { 0x60db, 0x0000 }, + { 0x60dc, 0x0000 }, + { 0x60dd, 0x0000 }, + { 0x60de, 0x0000 }, + { 0x60df, 0x0000 }, + { 0x60e0, 0x0000 }, + { 0x60e1, 0x0000 }, + { 0x60e2, 0x0000 }, + { 0x60e3, 0x0000 }, + { 0x60e4, 0x0000 }, + { 0x60e5, 0x0000 }, + { 0x60e6, 0x0000 }, + { 0x60e7, 0x0000 }, + { 0x60e8, 0x0000 }, + { 0x60e9, 0x0000 }, + { 0x60ea, 0x0000 }, + { 0x60eb, 0x0000 }, + { 0x60ec, 0x0000 }, + { 0x60ed, 0x0000 }, + { 0x60ee, 0x0000 }, + { 0x60ef, 0x0000 }, + { 0x60f0, 0x0000 }, + { 0x60f1, 0x0000 }, + { 0x60f2, 0x0000 }, + { 0x60f3, 0x0000 }, + { 0x60f4, 0x0000 }, + { 0x60f5, 0x0000 }, + { 0x60f6, 0x0000 }, + { 0x60f7, 0x0000 }, + { 0x60f8, 0x0000 }, + { 0x60f9, 0x0000 }, + { 0x60fa, 0x0000 }, + { 0x60fb, 0x0000 }, + { 0x60fc, 0x0000 }, + { 0x60fd, 0x0000 }, + { 0x60fe, 0x0000 }, + { 0x60ff, 0x0000 }, + { 0x6100, 0x0000 }, + { 0x6101, 0x0000 }, + { 0x6102, 0x0000 }, + { 0x6103, 0x0000 }, + { 0x6104, 0x0000 }, + { 0x6105, 0x0000 }, + { 0x6106, 0x0000 }, + { 0x6107, 0x0000 }, + { 0x6108, 0x0000 }, + { 0x6109, 0x0000 }, + { 0x610a, 0x0000 }, + { 0x610b, 0x0000 }, + { 0x610c, 0x0000 }, + { 0x610d, 0x0000 }, + { 0x610e, 0x0000 }, + { 0x610f, 0x0000 }, + { 0x6110, 0x0000 }, + { 0x6111, 0x0000 }, + { 0x6112, 0x0000 }, + { 0x6113, 0x0000 }, + { 0x6114, 0x0000 }, + { 0x6115, 0x0000 }, + { 0x6116, 0x0000 }, + { 0x6117, 0x0000 }, + { 0x6118, 0x0000 }, + { 0x6119, 0x0000 }, + { 0x611a, 0x0000 }, + { 0x611b, 0x0000 }, + { 0x611c, 0x0000 }, + { 0x611d, 0x0000 }, + { 0x611e, 0x0000 }, + { 0x611f, 0x0000 }, + { 0x6120, 0x0000 }, + { 0x6121, 0x0000 }, + { 0x6122, 0x0000 }, + { 0x6123, 0x0000 }, + { 0x6124, 0x0000 }, + { 0x6125, 0x0000 }, + { 0x6126, 0x0000 }, + { 0x6127, 0x0000 }, + { 0x6128, 0x0000 }, + { 0x6129, 0x0000 }, + { 0x612a, 0x0000 }, + { 0x612b, 0x0000 }, + { 0x612c, 0x0000 }, + { 0x612d, 0x0000 }, + { 0x612e, 0x0000 }, + { 0x612f, 0x0000 }, + { 0x6130, 0x0000 }, + { 0x6131, 0x0000 }, + { 0x6132, 0x0000 }, + { 0x6133, 0x0000 }, + { 0x6134, 0x0000 }, + { 0x6135, 0x0000 }, + { 0x6136, 0x0000 }, + { 0x6137, 0x0000 }, + { 0x6138, 0x0000 }, + { 0x6139, 0x0000 }, + { 0x613a, 0x0000 }, + { 0x613b, 0x0000 }, + { 0x613c, 0x0000 }, + { 0x613d, 0x0000 }, + { 0x613e, 0x0000 }, + { 0x613f, 0x0000 }, + { 0x6140, 0x0000 }, + { 0x6141, 0x0000 }, + { 0x6142, 0x0000 }, + { 0x6143, 0x0000 }, + { 0x6144, 0x0000 }, + { 0x6145, 0x0000 }, + { 0x6146, 0x0000 }, + { 0x6147, 0x0000 }, + { 0x6148, 0x0000 }, + { 0x6149, 0x0000 }, + { 0x614a, 0x0000 }, + { 0x614b, 0x0000 }, + { 0x614c, 0x0000 }, + { 0x614d, 0x0000 }, + { 0x614e, 0x0000 }, + { 0x614f, 0x0000 }, + { 0x6150, 0x0000 }, + { 0x6151, 0x0000 }, + { 0x6152, 0x0000 }, + { 0x6153, 0x0000 }, + { 0x6154, 0x0000 }, + { 0x6155, 0x0000 }, + { 0x6156, 0x0000 }, + { 0x6157, 0x0000 }, + { 0x6158, 0x0000 }, + { 0x6159, 0x0000 }, + { 0x615a, 0x0000 }, + { 0x615b, 0x0000 }, + { 0x615c, 0x0000 }, + { 0x615d, 0x0000 }, + { 0x615e, 0x0000 }, + { 0x615f, 0x0000 }, + { 0x6160, 0x0000 }, + { 0x6161, 0x0000 }, + { 0x6162, 0x0000 }, + { 0x6163, 0x0000 }, + { 0x6164, 0x0000 }, + { 0x6165, 0x0000 }, + { 0x6166, 0x0000 }, + { 0x6167, 0x0000 }, + { 0x6168, 0x0000 }, + { 0x6169, 0x0000 }, + { 0x616a, 0x0000 }, + { 0x616b, 0x0000 }, + { 0x616c, 0x0000 }, + { 0x616d, 0x0000 }, + { 0x616e, 0x0000 }, + { 0x616f, 0x0000 }, + { 0x6170, 0x0000 }, + { 0x6171, 0x0000 }, + { 0x6172, 0x0000 }, + { 0x6173, 0x0000 }, + { 0x6174, 0x0000 }, + { 0x6175, 0x0000 }, + { 0x6176, 0x0000 }, + { 0x6177, 0x0000 }, + { 0x6178, 0x0000 }, + { 0x6179, 0x0000 }, + { 0x617a, 0x0000 }, + { 0x617b, 0x0000 }, + { 0x617c, 0x0000 }, + { 0x617d, 0x0000 }, + { 0x617e, 0x0000 }, + { 0x617f, 0x0000 }, + { 0x6180, 0x0000 }, + { 0x6181, 0x0000 }, + { 0x6182, 0x0000 }, + { 0x6183, 0x0000 }, + { 0x6184, 0x0000 }, + { 0x6185, 0x0000 }, + { 0x6186, 0x0000 }, + { 0x6187, 0x0000 }, + { 0x6188, 0x0000 }, + { 0x6189, 0x0000 }, + { 0x618a, 0x0000 }, + { 0x618b, 0x0000 }, + { 0x618c, 0x0000 }, + { 0x618d, 0x0000 }, + { 0x618e, 0x0000 }, + { 0x618f, 0x0000 }, + { 0x6190, 0x0000 }, + { 0x6191, 0x0000 }, + { 0x6192, 0x0000 }, + { 0x6193, 0x0000 }, + { 0x6194, 0x0000 }, + { 0x6195, 0x0000 }, + { 0x6196, 0x0000 }, + { 0x6197, 0x0000 }, + { 0x6198, 0x0000 }, + { 0x6199, 0x0000 }, + { 0x619a, 0x0000 }, + { 0x619b, 0x0000 }, + { 0x619c, 0x0000 }, + { 0x619d, 0x0000 }, + { 0x619e, 0x0000 }, + { 0x619f, 0x0000 }, + { 0x61a0, 0x0000 }, + { 0x61a1, 0x0000 }, + { 0x61a2, 0x0000 }, + { 0x61a3, 0x0000 }, + { 0x61a4, 0x0000 }, + { 0x61a5, 0x0000 }, + { 0x61a6, 0x0000 }, + { 0x61a7, 0x0000 }, + { 0x61a8, 0x0000 }, + { 0x61a9, 0x0000 }, + { 0x61aa, 0x0000 }, + { 0x61ab, 0x0000 }, + { 0x61ac, 0x0000 }, + { 0x61ad, 0x0000 }, + { 0x61ae, 0x0000 }, + { 0x61af, 0x0000 }, + { 0x61b0, 0x0000 }, + { 0x61b1, 0x0000 }, + { 0x61b2, 0x0000 }, + { 0x61b3, 0x0000 }, + { 0x61b4, 0x0000 }, + { 0x61b5, 0x0000 }, + { 0x61b6, 0x0000 }, + { 0x61b7, 0x0000 }, + { 0x61b8, 0x0000 }, + { 0x61b9, 0x0000 }, + { 0x61ba, 0x0000 }, + { 0x61bb, 0x0000 }, + { 0x61bc, 0x0000 }, + { 0x61bd, 0x0000 }, + { 0x61be, 0x0000 }, + { 0x61bf, 0x0000 }, + { 0x61c0, 0x0000 }, + { 0x61c1, 0x0000 }, + { 0x61c2, 0x0000 }, + { 0x61c3, 0x0000 }, + { 0x61c4, 0x0000 }, + { 0x61c5, 0x0000 }, + { 0x61c6, 0x0000 }, + { 0x61c7, 0x0000 }, + { 0x61c8, 0x0000 }, + { 0x61c9, 0x0000 }, + { 0x61ca, 0x0000 }, + { 0x61cb, 0x0000 }, + { 0x61cc, 0x0000 }, + { 0x61cd, 0x0000 }, + { 0x61ce, 0x0000 }, + { 0x61cf, 0x0000 }, + { 0x61d0, 0x0000 }, + { 0x61d1, 0x0000 }, + { 0x61d2, 0x0000 }, + { 0x61d3, 0x0000 }, + { 0x61d4, 0x0000 }, + { 0x61d5, 0x0000 }, + { 0x61d6, 0x0000 }, + { 0x61d7, 0x0000 }, + { 0x61d8, 0x0000 }, + { 0x61d9, 0x0000 }, + { 0x61da, 0x0000 }, + { 0x61db, 0x0000 }, + { 0x61dc, 0x0000 }, + { 0x61dd, 0x0000 }, + { 0x61de, 0x0000 }, + { 0x61df, 0x0000 }, + { 0x61e0, 0x0000 }, + { 0x61e1, 0x0000 }, + { 0x61e2, 0x0000 }, + { 0x61e3, 0x0000 }, + { 0x61e4, 0x0000 }, + { 0x61e5, 0x0000 }, + { 0x61e6, 0x0000 }, + { 0x61e7, 0x0000 }, + { 0x61e8, 0x0000 }, + { 0x61e9, 0x0000 }, + { 0x61ea, 0x0000 }, + { 0x61eb, 0x0000 }, + { 0x61ec, 0x0000 }, + { 0x61ed, 0x0000 }, + { 0x61ee, 0x0000 }, + { 0x61ef, 0x0000 }, + { 0x61f0, 0x0000 }, + { 0x61f1, 0x0000 }, + { 0x61f2, 0x0000 }, + { 0x61f3, 0x0000 }, + { 0x61f4, 0x0000 }, + { 0x61f5, 0x0000 }, + { 0x61f6, 0x0000 }, + { 0x61f7, 0x0000 }, + { 0x61f8, 0x0000 }, + { 0x61f9, 0x0000 }, + { 0x61fa, 0x0000 }, + { 0x61fb, 0x0000 }, + { 0x61fc, 0x0000 }, + { 0x61fd, 0x0000 }, + { 0x61fe, 0x0000 }, + { 0x61ff, 0x0000 }, + { 0x6200, 0x0000 }, + { 0x6201, 0x0000 }, + { 0x6202, 0x0000 }, + { 0x6203, 0x0000 }, + { 0x6204, 0x0000 }, + { 0x6205, 0x0000 }, + { 0x6206, 0x0000 }, + { 0x6207, 0x0000 }, + { 0x6208, 0x0000 }, + { 0x6209, 0x0000 }, + { 0x620a, 0x0000 }, + { 0x620b, 0x0000 }, + { 0x620c, 0x0000 }, + { 0x620d, 0x0000 }, + { 0x620e, 0x0000 }, + { 0x620f, 0x0000 }, + { 0x6210, 0x0000 }, + { 0x6211, 0x0000 }, + { 0x6212, 0x0000 }, + { 0x6213, 0x0000 }, + { 0x6214, 0x0000 }, + { 0x6215, 0x0000 }, + { 0x6216, 0x0000 }, + { 0x6217, 0x0000 }, + { 0x6218, 0x0000 }, + { 0x6219, 0x0000 }, + { 0x621a, 0x0000 }, + { 0x621b, 0x0000 }, + { 0x621c, 0x0000 }, + { 0x621d, 0x0000 }, + { 0x621e, 0x0000 }, + { 0x621f, 0x0000 }, + { 0x6220, 0x0000 }, + { 0x6221, 0x0000 }, + { 0x6222, 0x0000 }, + { 0x6223, 0x0000 }, + { 0x6224, 0x0000 }, + { 0x6225, 0x0000 }, + { 0x6226, 0x0000 }, + { 0x6227, 0x0000 }, + { 0x6228, 0x0000 }, + { 0x6229, 0x0000 }, + { 0x622a, 0x0000 }, + { 0x622b, 0x0000 }, + { 0x622c, 0x0000 }, + { 0x622d, 0x0000 }, + { 0x622e, 0x0000 }, + { 0x622f, 0x0000 }, + { 0x6230, 0x0000 }, + { 0x6231, 0x0000 }, + { 0x6232, 0x0000 }, + { 0x6233, 0x0000 }, + { 0x6234, 0x0000 }, + { 0x6235, 0x0000 }, + { 0x6236, 0x0000 }, + { 0x6237, 0x0000 }, + { 0x6238, 0x0000 }, + { 0x6239, 0x0000 }, + { 0x623a, 0x0000 }, + { 0x623b, 0x0000 }, + { 0x623c, 0x0000 }, + { 0x623d, 0x0000 }, + { 0x623e, 0x0000 }, + { 0x623f, 0x0000 }, + { 0x6240, 0x0000 }, + { 0x6241, 0x0000 }, + { 0x6242, 0x0000 }, + { 0x6243, 0x0000 }, + { 0x6244, 0x0000 }, + { 0x6245, 0x0000 }, + { 0x6246, 0x0000 }, + { 0x6247, 0x0000 }, + { 0x6248, 0x0000 }, + { 0x6249, 0x0000 }, + { 0x624a, 0x0000 }, + { 0x624b, 0x0000 }, + { 0x624c, 0x0000 }, + { 0x624d, 0x0000 }, + { 0x624e, 0x0000 }, + { 0x624f, 0x0000 }, + { 0x6250, 0x0000 }, + { 0x6251, 0x0000 }, + { 0x6252, 0x0000 }, + { 0x6253, 0x0000 }, + { 0x6254, 0x0000 }, + { 0x6255, 0x0000 }, + { 0x6256, 0x0000 }, + { 0x6257, 0x0000 }, + { 0x6258, 0x0000 }, + { 0x6259, 0x0000 }, + { 0x625a, 0x0000 }, + { 0x625b, 0x0000 }, + { 0x625c, 0x0000 }, + { 0x625d, 0x0000 }, + { 0x625e, 0x0000 }, + { 0x625f, 0x0000 }, + { 0x6260, 0x0000 }, + { 0x6261, 0x0000 }, + { 0x6262, 0x0000 }, + { 0x6263, 0x0000 }, + { 0x6264, 0x0000 }, + { 0x6265, 0x0000 }, + { 0x6266, 0x0000 }, + { 0x6267, 0x0000 }, + { 0x6268, 0x0000 }, + { 0x6269, 0x0000 }, + { 0x626a, 0x0000 }, + { 0x626b, 0x0000 }, + { 0x626c, 0x0000 }, + { 0x626d, 0x0000 }, + { 0x626e, 0x0000 }, + { 0x626f, 0x0000 }, + { 0x6270, 0x0000 }, + { 0x6271, 0x0000 }, + { 0x6272, 0x0000 }, + { 0x6273, 0x0000 }, + { 0x6274, 0x0000 }, + { 0x6275, 0x0000 }, + { 0x6276, 0x0000 }, + { 0x6277, 0x0000 }, + { 0x6278, 0x0000 }, + { 0x6279, 0x0000 }, + { 0x627a, 0x0000 }, + { 0x627b, 0x0000 }, + { 0x627c, 0x0000 }, + { 0x627d, 0x0000 }, + { 0x627e, 0x0000 }, + { 0x627f, 0x0000 }, + { 0x6280, 0x0000 }, + { 0x6281, 0x0000 }, + { 0x6282, 0x0000 }, + { 0x6283, 0x0000 }, + { 0x6284, 0x0000 }, + { 0x6285, 0x0000 }, + { 0x6286, 0x0000 }, + { 0x6287, 0x0000 }, + { 0x6288, 0x0000 }, + { 0x6289, 0x0000 }, + { 0x628a, 0x0000 }, + { 0x628b, 0x0000 }, + { 0x628c, 0x0000 }, + { 0x628d, 0x0000 }, + { 0x628e, 0x0000 }, + { 0x628f, 0x0000 }, + { 0x6290, 0x0000 }, + { 0x6291, 0x0000 }, + { 0x6292, 0x0000 }, + { 0x6293, 0x0000 }, + { 0x6294, 0x0000 }, + { 0x6295, 0x0000 }, + { 0x6296, 0x0000 }, + { 0x6297, 0x0000 }, + { 0x6298, 0x0000 }, + { 0x6299, 0x0000 }, + { 0x629a, 0x0000 }, + { 0x629b, 0x0000 }, + { 0x629c, 0x0000 }, + { 0x629d, 0x0000 }, + { 0x629e, 0x0000 }, + { 0x629f, 0x0000 }, + { 0x62a0, 0x0000 }, + { 0x62a1, 0x0000 }, + { 0x62a2, 0x0000 }, + { 0x62a3, 0x0000 }, + { 0x62a4, 0x0000 }, + { 0x62a5, 0x0000 }, + { 0x62a6, 0x0000 }, + { 0x62a7, 0x0000 }, + { 0x62a8, 0x0000 }, + { 0x62a9, 0x0000 }, + { 0x62aa, 0x0000 }, + { 0x62ab, 0x0000 }, + { 0x62ac, 0x0000 }, + { 0x62ad, 0x0000 }, + { 0x62ae, 0x0000 }, + { 0x62af, 0x0000 }, + { 0x62b0, 0x0000 }, + { 0x62b1, 0x0000 }, + { 0x62b2, 0x0000 }, + { 0x62b3, 0x0000 }, + { 0x62b4, 0x0000 }, + { 0x62b5, 0x0000 }, + { 0x62b6, 0x0000 }, + { 0x62b7, 0x0000 }, + { 0x62b8, 0x0000 }, + { 0x62b9, 0x0000 }, + { 0x62ba, 0x0000 }, + { 0x62bb, 0x0000 }, + { 0x62bc, 0x0000 }, + { 0x62bd, 0x0000 }, + { 0x62be, 0x0000 }, + { 0x62bf, 0x0000 }, + { 0x62c0, 0x0000 }, + { 0x62c1, 0x0000 }, + { 0x62c2, 0x0000 }, + { 0x62c3, 0x0000 }, + { 0x62c4, 0x0000 }, + { 0x62c5, 0x0000 }, + { 0x62c6, 0x0000 }, + { 0x62c7, 0x0000 }, + { 0x62c8, 0x0000 }, + { 0x62c9, 0x0000 }, + { 0x62ca, 0x0000 }, + { 0x62cb, 0x0000 }, + { 0x62cc, 0x0000 }, + { 0x62cd, 0x0000 }, + { 0x62ce, 0x0000 }, + { 0x62cf, 0x0000 }, + { 0x62d0, 0x0000 }, + { 0x62d1, 0x0000 }, + { 0x62d2, 0x0000 }, + { 0x62d3, 0x0000 }, + { 0x62d4, 0x0000 }, + { 0x62d5, 0x0000 }, + { 0x62d6, 0x0000 }, + { 0x62d7, 0x0000 }, + { 0x62d8, 0x0000 }, + { 0x62d9, 0x0000 }, + { 0x62da, 0x0000 }, + { 0x62db, 0x0000 }, + { 0x62dc, 0x0000 }, + { 0x62dd, 0x0000 }, + { 0x62de, 0x0000 }, + { 0x62df, 0x0000 }, + { 0x62e0, 0x0000 }, + { 0x62e1, 0x0000 }, + { 0x62e2, 0x0000 }, + { 0x62e3, 0x0000 }, + { 0x62e4, 0x0000 }, + { 0x62e5, 0x0000 }, + { 0x62e6, 0x0000 }, + { 0x62e7, 0x0000 }, + { 0x62e8, 0x0000 }, + { 0x62e9, 0x0000 }, + { 0x62ea, 0x0000 }, + { 0x62eb, 0x0000 }, + { 0x62ec, 0x0000 }, + { 0x62ed, 0x0000 }, + { 0x62ee, 0x0000 }, + { 0x62ef, 0x0000 }, + { 0x62f0, 0x0000 }, + { 0x62f1, 0x0000 }, + { 0x62f2, 0x0000 }, + { 0x62f3, 0x0000 }, + { 0x62f4, 0x0000 }, + { 0x62f5, 0x0000 }, + { 0x62f6, 0x0000 }, + { 0x62f7, 0x0000 }, + { 0x62f8, 0x0000 }, + { 0x62f9, 0x0000 }, + { 0x62fa, 0x0000 }, + { 0x62fb, 0x0000 }, + { 0x62fc, 0x0000 }, + { 0x62fd, 0x0000 }, + { 0x62fe, 0x0000 }, + { 0x62ff, 0x0000 }, + { 0x6300, 0x0000 }, + { 0x6301, 0x0000 }, + { 0x6302, 0x0000 }, + { 0x6303, 0x0000 }, + { 0x6304, 0x0000 }, + { 0x6305, 0x0000 }, + { 0x6306, 0x0000 }, + { 0x6307, 0x0000 }, + { 0x6308, 0x0000 }, + { 0x6309, 0x0000 }, + { 0x630a, 0x0000 }, + { 0x630b, 0x0000 }, + { 0x630c, 0x0000 }, + { 0x630d, 0x0000 }, + { 0x630e, 0x0000 }, + { 0x630f, 0x0000 }, + { 0x6310, 0x0000 }, + { 0x6311, 0x0000 }, + { 0x6312, 0x0000 }, + { 0x6313, 0x0000 }, + { 0x6314, 0x0000 }, + { 0x6315, 0x0000 }, + { 0x6316, 0x0000 }, + { 0x6317, 0x0000 }, + { 0x6318, 0x0000 }, + { 0x6319, 0x0000 }, + { 0x631a, 0x0000 }, + { 0x631b, 0x0000 }, + { 0x631c, 0x0000 }, + { 0x631d, 0x0000 }, + { 0x631e, 0x0000 }, + { 0x631f, 0x0000 }, + { 0x6320, 0x0000 }, + { 0x6321, 0x0000 }, + { 0x6322, 0x0000 }, + { 0x6323, 0x0000 }, + { 0x6324, 0x0000 }, + { 0x6325, 0x0000 }, + { 0x6326, 0x0000 }, + { 0x6327, 0x0000 }, + { 0x6328, 0x0000 }, + { 0x6329, 0x0000 }, + { 0x632a, 0x0000 }, + { 0x632b, 0x0000 }, + { 0x632c, 0x0000 }, + { 0x632d, 0x0000 }, + { 0x632e, 0x0000 }, + { 0x632f, 0x0000 }, + { 0x6330, 0x0000 }, + { 0x6331, 0x0000 }, + { 0x6332, 0x0000 }, + { 0x6333, 0x0000 }, + { 0x6334, 0x0000 }, + { 0x6335, 0x0000 }, + { 0x6336, 0x0000 }, + { 0x6337, 0x0000 }, + { 0x6338, 0x0000 }, + { 0x6339, 0x0000 }, + { 0x633a, 0x0000 }, + { 0x633b, 0x0000 }, + { 0x633c, 0x0000 }, + { 0x633d, 0x0000 }, + { 0x633e, 0x0000 }, + { 0x633f, 0x0000 }, + { 0x6340, 0x0000 }, + { 0x6341, 0x0000 }, + { 0x6342, 0x0000 }, + { 0x6343, 0x0000 }, + { 0x6344, 0x0000 }, + { 0x6345, 0x0000 }, + { 0x6346, 0x0000 }, + { 0x6347, 0x0000 }, + { 0x6348, 0x0000 }, + { 0x6349, 0x0000 }, + { 0x634a, 0x0000 }, + { 0x634b, 0x0000 }, + { 0x634c, 0x0000 }, + { 0x634d, 0x0000 }, + { 0x634e, 0x0000 }, + { 0x634f, 0x0000 }, + { 0x6350, 0x0000 }, + { 0x6351, 0x0000 }, + { 0x6352, 0x0000 }, + { 0x6353, 0x0000 }, + { 0x6354, 0x0000 }, + { 0x6355, 0x0000 }, + { 0x6356, 0x0000 }, + { 0x6357, 0x0000 }, + { 0x6358, 0x0000 }, + { 0x6359, 0x0000 }, + { 0x635a, 0x0000 }, + { 0x635b, 0x0000 }, + { 0x635c, 0x0000 }, + { 0x635d, 0x0000 }, + { 0x635e, 0x0000 }, + { 0x635f, 0x0000 }, + { 0x6360, 0x0000 }, + { 0x6361, 0x0000 }, + { 0x6362, 0x0000 }, + { 0x6363, 0x0000 }, + { 0x6364, 0x0000 }, + { 0x6365, 0x0000 }, + { 0x6366, 0x0000 }, + { 0x6367, 0x0000 }, + { 0x6368, 0x0000 }, + { 0x6369, 0x0000 }, + { 0x636a, 0x0000 }, + { 0x636b, 0x0000 }, + { 0x636c, 0x0000 }, + { 0x636d, 0x0000 }, + { 0x636e, 0x0000 }, + { 0x636f, 0x0000 }, + { 0x6370, 0x0000 }, + { 0x6371, 0x0000 }, + { 0x6372, 0x0000 }, + { 0x6373, 0x0000 }, + { 0x6374, 0x0000 }, + { 0x6375, 0x0000 }, + { 0x6376, 0x0000 }, + { 0x6377, 0x0000 }, + { 0x6378, 0x0000 }, + { 0x6379, 0x0000 }, + { 0x637a, 0x0000 }, + { 0x637b, 0x0000 }, + { 0x637c, 0x0000 }, + { 0x637d, 0x0000 }, + { 0x637e, 0x0000 }, + { 0x637f, 0x0000 }, + { 0x6380, 0x0000 }, + { 0x6381, 0x0000 }, + { 0x6382, 0x0000 }, + { 0x6383, 0x0000 }, + { 0x6384, 0x0000 }, + { 0x6385, 0x0000 }, + { 0x6386, 0x0000 }, + { 0x6387, 0x0000 }, + { 0x6388, 0x0000 }, + { 0x6389, 0x0000 }, + { 0x638a, 0x0000 }, + { 0x638b, 0x0000 }, + { 0x638c, 0x0000 }, + { 0x638d, 0x0000 }, + { 0x638e, 0x0000 }, + { 0x638f, 0x0000 }, + { 0x6390, 0x0000 }, + { 0x6391, 0x0000 }, + { 0x6392, 0x0000 }, + { 0x6393, 0x0000 }, + { 0x6394, 0x0000 }, + { 0x6395, 0x0000 }, + { 0x6396, 0x0000 }, + { 0x6397, 0x0000 }, + { 0x6398, 0x0000 }, + { 0x6399, 0x0000 }, + { 0x639a, 0x0000 }, + { 0x639b, 0x0000 }, + { 0x639c, 0x0000 }, + { 0x639d, 0x0000 }, + { 0x639e, 0x0000 }, + { 0x639f, 0x0000 }, + { 0x63a0, 0x0000 }, + { 0x63a1, 0x0000 }, + { 0x63a2, 0x0000 }, + { 0x63a3, 0x0000 }, + { 0x63a4, 0x0000 }, + { 0x63a5, 0x0000 }, + { 0x63a6, 0x0000 }, + { 0x63a7, 0x0000 }, + { 0x63a8, 0x0000 }, + { 0x63a9, 0x0000 }, + { 0x63aa, 0x0000 }, + { 0x63ab, 0x0000 }, + { 0x63ac, 0x0000 }, + { 0x63ad, 0x0000 }, + { 0x63ae, 0x0000 }, + { 0x63af, 0x0000 }, + { 0x63b0, 0x0000 }, + { 0x63b1, 0x0000 }, + { 0x63b2, 0x0000 }, + { 0x63b3, 0x0000 }, + { 0x63b4, 0x0000 }, + { 0x63b5, 0x0000 }, + { 0x63b6, 0x0000 }, + { 0x63b7, 0x0000 }, + { 0x63b8, 0x0000 }, + { 0x63b9, 0x0000 }, + { 0x63ba, 0x0000 }, + { 0x63bb, 0x0000 }, + { 0x63bc, 0x0000 }, + { 0x63bd, 0x0000 }, + { 0x63be, 0x0000 }, + { 0x63bf, 0x0000 }, + { 0x63c0, 0x0000 }, + { 0x63c1, 0x0000 }, + { 0x63c2, 0x0000 }, + { 0x63c3, 0x0000 }, + { 0x63c4, 0x0000 }, + { 0x63c5, 0x0000 }, + { 0x63c6, 0x0000 }, + { 0x63c7, 0x0000 }, + { 0x63c8, 0x0000 }, + { 0x63c9, 0x0000 }, + { 0x63ca, 0x0000 }, + { 0x63cb, 0x0000 }, + { 0x63cc, 0x0000 }, + { 0x63cd, 0x0000 }, + { 0x63ce, 0x0000 }, + { 0x63cf, 0x0000 }, + { 0x63d0, 0x0000 }, + { 0x63d1, 0x0000 }, + { 0x63d2, 0x0000 }, + { 0x63d3, 0x0000 }, + { 0x63d4, 0x0000 }, + { 0x63d5, 0x0000 }, + { 0x63d6, 0x0000 }, + { 0x63d7, 0x0000 }, + { 0x63d8, 0x0000 }, + { 0x63d9, 0x0000 }, + { 0x63da, 0x0000 }, + { 0x63db, 0x0000 }, + { 0x63dc, 0x0000 }, + { 0x63dd, 0x0000 }, + { 0x63de, 0x0000 }, + { 0x63df, 0x0000 }, + { 0x63e0, 0x0000 }, + { 0x63e1, 0x0000 }, + { 0x63e2, 0x0000 }, + { 0x63e3, 0x0000 }, + { 0x63e4, 0x0000 }, + { 0x63e5, 0x0000 }, + { 0x63e6, 0x0000 }, + { 0x63e7, 0x0000 }, + { 0x63e8, 0x0000 }, + { 0x63e9, 0x0000 }, + { 0x63ea, 0x0000 }, + { 0x63eb, 0x0000 }, + { 0x63ec, 0x0000 }, + { 0x63ed, 0x0000 }, + { 0x63ee, 0x0000 }, + { 0x63ef, 0x0000 }, + { 0x63f0, 0x0000 }, + { 0x63f1, 0x0000 }, + { 0x63f2, 0x0000 }, + { 0x63f3, 0x0000 }, + { 0x63f4, 0x0000 }, + { 0x63f5, 0x0000 }, + { 0x63f6, 0x0000 }, + { 0x63f7, 0x0000 }, + { 0x63f8, 0x0000 }, + { 0x63f9, 0x0000 }, + { 0x63fa, 0x0000 }, + { 0x63fb, 0x0000 }, + { 0x63fc, 0x0000 }, + { 0x63fd, 0x0000 }, + { 0x63fe, 0x0000 }, + { 0x63ff, 0x0000 }, + { 0x6400, 0x0000 }, + { 0x6401, 0x0000 }, + { 0x6402, 0x0000 }, + { 0x6403, 0x0000 }, + { 0x6404, 0x0000 }, + { 0x6405, 0x0000 }, + { 0x6406, 0x0000 }, + { 0x6407, 0x0000 }, + { 0x6408, 0x0000 }, + { 0x6409, 0x0000 }, + { 0x640a, 0x0000 }, + { 0x640b, 0x0000 }, + { 0x640c, 0x0000 }, + { 0x640d, 0x0000 }, + { 0x640e, 0x0000 }, + { 0x640f, 0x0000 }, + { 0x6410, 0x0000 }, + { 0x6411, 0x0000 }, + { 0x6412, 0x0000 }, + { 0x6413, 0x0000 }, + { 0x6414, 0x0000 }, + { 0x6415, 0x0000 }, + { 0x6416, 0x0000 }, + { 0x6417, 0x0000 }, + { 0x6418, 0x0000 }, + { 0x6419, 0x0000 }, + { 0x641a, 0x0000 }, + { 0x641b, 0x0000 }, + { 0x641c, 0x0000 }, + { 0x641d, 0x0000 }, + { 0x641e, 0x0000 }, + { 0x641f, 0x0000 }, + { 0x6420, 0x0000 }, + { 0x6421, 0x0000 }, + { 0x6422, 0x0000 }, + { 0x6423, 0x0000 }, + { 0x6424, 0x0000 }, + { 0x6425, 0x0000 }, + { 0x6426, 0x0000 }, + { 0x6427, 0x0000 }, + { 0x6428, 0x0000 }, + { 0x6429, 0x0000 }, + { 0x642a, 0x0000 }, + { 0x642b, 0x0000 }, + { 0x642c, 0x0000 }, + { 0x642d, 0x0000 }, + { 0x642e, 0x0000 }, + { 0x642f, 0x0000 }, + { 0x6430, 0x0000 }, + { 0x6431, 0x0000 }, + { 0x6432, 0x0000 }, + { 0x6433, 0x0000 }, + { 0x6434, 0x0000 }, + { 0x6435, 0x0000 }, + { 0x6436, 0x0000 }, + { 0x6437, 0x0000 }, + { 0x6438, 0x0000 }, + { 0x6439, 0x0000 }, + { 0x643a, 0x0000 }, + { 0x643b, 0x0000 }, + { 0x643c, 0x0000 }, + { 0x643d, 0x0000 }, + { 0x643e, 0x0000 }, + { 0x643f, 0x0000 }, + { 0x6440, 0x0000 }, + { 0x6441, 0x0000 }, + { 0x6442, 0x0000 }, + { 0x6443, 0x0000 }, + { 0x6444, 0x0000 }, + { 0x6445, 0x0000 }, + { 0x6446, 0x0000 }, + { 0x6447, 0x0000 }, + { 0x6448, 0x0000 }, + { 0x6449, 0x0000 }, + { 0x644a, 0x0000 }, + { 0x644b, 0x0000 }, + { 0x644c, 0x0000 }, + { 0x644d, 0x0000 }, + { 0x644e, 0x0000 }, + { 0x644f, 0x0000 }, + { 0x6450, 0x0000 }, + { 0x6451, 0x0000 }, + { 0x6452, 0x0000 }, + { 0x6453, 0x0000 }, + { 0x6454, 0x0000 }, + { 0x6455, 0x0000 }, + { 0x6456, 0x0000 }, + { 0x6457, 0x0000 }, + { 0x6458, 0x0000 }, + { 0x6459, 0x0000 }, + { 0x645a, 0x0000 }, + { 0x645b, 0x0000 }, + { 0x645c, 0x0000 }, + { 0x645d, 0x0000 }, + { 0x645e, 0x0000 }, + { 0x645f, 0x0000 }, + { 0x6460, 0x0000 }, + { 0x6461, 0x0000 }, + { 0x6462, 0x0000 }, + { 0x6463, 0x0000 }, + { 0x6464, 0x0000 }, + { 0x6465, 0x0000 }, + { 0x6466, 0x0000 }, + { 0x6467, 0x0000 }, + { 0x6468, 0x0000 }, + { 0x6469, 0x0000 }, + { 0x646a, 0x0000 }, + { 0x646b, 0x0000 }, + { 0x646c, 0x0000 }, + { 0x646d, 0x0000 }, + { 0x646e, 0x0000 }, + { 0x646f, 0x0000 }, + { 0x6470, 0x0000 }, + { 0x6471, 0x0000 }, + { 0x6472, 0x0000 }, + { 0x6473, 0x0000 }, + { 0x6474, 0x0000 }, + { 0x6475, 0x0000 }, + { 0x6476, 0x0000 }, + { 0x6477, 0x0000 }, + { 0x6478, 0x0000 }, + { 0x6479, 0x0000 }, + { 0x647a, 0x0000 }, + { 0x647b, 0x0000 }, + { 0x647c, 0x0000 }, + { 0x647d, 0x0000 }, + { 0x647e, 0x0000 }, + { 0x647f, 0x0000 }, + { 0x6480, 0x0000 }, + { 0x6481, 0x0000 }, + { 0x6482, 0x0000 }, + { 0x6483, 0x0000 }, + { 0x6484, 0x0000 }, + { 0x6485, 0x0000 }, + { 0x6486, 0x0000 }, + { 0x6487, 0x0000 }, + { 0x6488, 0x0000 }, + { 0x6489, 0x0000 }, + { 0x648a, 0x0000 }, + { 0x648b, 0x0000 }, + { 0x648c, 0x0000 }, + { 0x648d, 0x0000 }, + { 0x648e, 0x0000 }, + { 0x648f, 0x0000 }, + { 0x6490, 0x0000 }, + { 0x6491, 0x0000 }, + { 0x6492, 0x0000 }, + { 0x6493, 0x0000 }, + { 0x6494, 0x0000 }, + { 0x6495, 0x0000 }, + { 0x6496, 0x0000 }, + { 0x6497, 0x0000 }, + { 0x6498, 0x0000 }, + { 0x6499, 0x0000 }, + { 0x649a, 0x0000 }, + { 0x649b, 0x0000 }, + { 0x649c, 0x0000 }, + { 0x649d, 0x0000 }, + { 0x649e, 0x0000 }, + { 0x649f, 0x0000 }, + { 0x64a0, 0x0000 }, + { 0x64a1, 0x0000 }, + { 0x64a2, 0x0000 }, + { 0x64a3, 0x0000 }, + { 0x64a4, 0x0000 }, + { 0x64a5, 0x0000 }, + { 0x64a6, 0x0000 }, + { 0x64a7, 0x0000 }, + { 0x64a8, 0x0000 }, + { 0x64a9, 0x0000 }, + { 0x64aa, 0x0000 }, + { 0x64ab, 0x0000 }, + { 0x64ac, 0x0000 }, + { 0x64ad, 0x0000 }, + { 0x64ae, 0x0000 }, + { 0x64af, 0x0000 }, + { 0x64b0, 0x0000 }, + { 0x64b1, 0x0000 }, + { 0x64b2, 0x0000 }, + { 0x64b3, 0x0000 }, + { 0x64b4, 0x0000 }, + { 0x64b5, 0x0000 }, + { 0x64b6, 0x0000 }, + { 0x64b7, 0x0000 }, + { 0x64b8, 0x0000 }, + { 0x64b9, 0x0000 }, + { 0x64ba, 0x0000 }, + { 0x64bb, 0x0000 }, + { 0x64bc, 0x0000 }, + { 0x64bd, 0x0000 }, + { 0x64be, 0x0000 }, + { 0x64bf, 0x0000 }, + { 0x64c0, 0x0000 }, + { 0x64c1, 0x0000 }, + { 0x64c2, 0x0000 }, + { 0x64c3, 0x0000 }, + { 0x64c4, 0x0000 }, + { 0x64c5, 0x0000 }, + { 0x64c6, 0x0000 }, + { 0x64c7, 0x0000 }, + { 0x64c8, 0x0000 }, + { 0x64c9, 0x0000 }, + { 0x64ca, 0x0000 }, + { 0x64cb, 0x0000 }, + { 0x64cc, 0x0000 }, + { 0x64cd, 0x0000 }, + { 0x64ce, 0x0000 }, + { 0x64cf, 0x0000 }, + { 0x64d0, 0x0000 }, + { 0x64d1, 0x0000 }, + { 0x64d2, 0x0000 }, + { 0x64d3, 0x0000 }, + { 0x64d4, 0x0000 }, + { 0x64d5, 0x0000 }, + { 0x64d6, 0x0000 }, + { 0x64d7, 0x0000 }, + { 0x64d8, 0x0000 }, + { 0x64d9, 0x0000 }, + { 0x64da, 0x0000 }, + { 0x64db, 0x0000 }, + { 0x64dc, 0x0000 }, + { 0x64dd, 0x0000 }, + { 0x64de, 0x0000 }, + { 0x64df, 0x0000 }, + { 0x64e0, 0x0000 }, + { 0x64e1, 0x0000 }, + { 0x64e2, 0x0000 }, + { 0x64e3, 0x0000 }, + { 0x64e4, 0x0000 }, + { 0x64e5, 0x0000 }, + { 0x64e6, 0x0000 }, + { 0x64e7, 0x0000 }, + { 0x64e8, 0x0000 }, + { 0x64e9, 0x0000 }, + { 0x64ea, 0x0000 }, + { 0x64eb, 0x0000 }, + { 0x64ec, 0x0000 }, + { 0x64ed, 0x0000 }, + { 0x64ee, 0x0000 }, + { 0x64ef, 0x0000 }, + { 0x64f0, 0x0000 }, + { 0x64f1, 0x0000 }, + { 0x64f2, 0x0000 }, + { 0x64f3, 0x0000 }, + { 0x64f4, 0x0000 }, + { 0x64f5, 0x0000 }, + { 0x64f6, 0x0000 }, + { 0x64f7, 0x0000 }, + { 0x64f8, 0x0000 }, + { 0x64f9, 0x0000 }, + { 0x64fa, 0x0000 }, + { 0x64fb, 0x0000 }, + { 0x64fc, 0x0000 }, + { 0x64fd, 0x0000 }, + { 0x64fe, 0x0000 }, + { 0x64ff, 0x0000 }, + { 0x6500, 0x0000 }, + { 0x6501, 0x0000 }, + { 0x6502, 0x0000 }, + { 0x6503, 0x0000 }, + { 0x6504, 0x0000 }, + { 0x6505, 0x0000 }, + { 0x6506, 0x0000 }, + { 0x6507, 0x0000 }, + { 0x6508, 0x0000 }, + { 0x6509, 0x0000 }, + { 0x650a, 0x0000 }, + { 0x650b, 0x0000 }, + { 0x650c, 0x0000 }, + { 0x650d, 0x0000 }, + { 0x650e, 0x0000 }, + { 0x650f, 0x0000 }, + { 0x6510, 0x0000 }, + { 0x6511, 0x0000 }, + { 0x6512, 0x0000 }, + { 0x6513, 0x0000 }, + { 0x6514, 0x0000 }, + { 0x6515, 0x0000 }, + { 0x6516, 0x0000 }, + { 0x6517, 0x0000 }, + { 0x6518, 0x0000 }, + { 0x6519, 0x0000 }, + { 0x651a, 0x0000 }, + { 0x651b, 0x0000 }, + { 0x651c, 0x0000 }, + { 0x651d, 0x0000 }, + { 0x651e, 0x0000 }, + { 0x651f, 0x0000 }, + { 0x6520, 0x0000 }, + { 0x6521, 0x0000 }, + { 0x6522, 0x0000 }, + { 0x6523, 0x0000 }, + { 0x6524, 0x0000 }, + { 0x6525, 0x0000 }, + { 0x6526, 0x0000 }, + { 0x6527, 0x0000 }, + { 0x6528, 0x0000 }, + { 0x6529, 0x0000 }, + { 0x652a, 0x0000 }, + { 0x652b, 0x0000 }, + { 0x652c, 0x0000 }, + { 0x652d, 0x0000 }, + { 0x652e, 0x0000 }, + { 0x652f, 0x0000 }, + { 0x6530, 0x0000 }, + { 0x6531, 0x0000 }, + { 0x6532, 0x0000 }, + { 0x6533, 0x0000 }, + { 0x6534, 0x0000 }, + { 0x6535, 0x0000 }, + { 0x6536, 0x0000 }, + { 0x6537, 0x0000 }, + { 0x6538, 0x0000 }, + { 0x6539, 0x0000 }, + { 0x653a, 0x0000 }, + { 0x653b, 0x0000 }, + { 0x653c, 0x0000 }, + { 0x653d, 0x0000 }, + { 0x653e, 0x0000 }, + { 0x653f, 0x0000 }, + { 0x6540, 0x0000 }, + { 0x6541, 0x0000 }, + { 0x6542, 0x0000 }, + { 0x6543, 0x0000 }, + { 0x6544, 0x0000 }, + { 0x6545, 0x0000 }, + { 0x6546, 0x0000 }, + { 0x6547, 0x0000 }, + { 0x6548, 0x0000 }, + { 0x6549, 0x0000 }, + { 0x654a, 0x0000 }, + { 0x654b, 0x0000 }, + { 0x654c, 0x0000 }, + { 0x654d, 0x0000 }, + { 0x654e, 0x0000 }, + { 0x654f, 0x0000 }, + { 0x6550, 0x0000 }, + { 0x6551, 0x0000 }, + { 0x6552, 0x0000 }, + { 0x6553, 0x0000 }, + { 0x6554, 0x0000 }, + { 0x6555, 0x0000 }, + { 0x6556, 0x0000 }, + { 0x6557, 0x0000 }, + { 0x6558, 0x0000 }, + { 0x6559, 0x0000 }, + { 0x655a, 0x0000 }, + { 0x655b, 0x0000 }, + { 0x655c, 0x0000 }, + { 0x655d, 0x0000 }, + { 0x655e, 0x0000 }, + { 0x655f, 0x0000 }, + { 0x6560, 0x0000 }, + { 0x6561, 0x0000 }, + { 0x6562, 0x0000 }, + { 0x6563, 0x0000 }, + { 0x6564, 0x0000 }, + { 0x6565, 0x0000 }, + { 0x6566, 0x0000 }, + { 0x6567, 0x0000 }, + { 0x6568, 0x0000 }, + { 0x6569, 0x0000 }, + { 0x656a, 0x0000 }, + { 0x656b, 0x0000 }, + { 0x656c, 0x0000 }, + { 0x656d, 0x0000 }, + { 0x656e, 0x0000 }, + { 0x656f, 0x0000 }, + { 0x6570, 0x0000 }, + { 0x6571, 0x0000 }, + { 0x6572, 0x0000 }, + { 0x6573, 0x0000 }, + { 0x6574, 0x0000 }, + { 0x6575, 0x0000 }, + { 0x6576, 0x0000 }, + { 0x6577, 0x0000 }, + { 0x6578, 0x0000 }, + { 0x6579, 0x0000 }, + { 0x657a, 0x0000 }, + { 0x657b, 0x0000 }, + { 0x657c, 0x0000 }, + { 0x657d, 0x0000 }, + { 0x657e, 0x0000 }, + { 0x657f, 0x0000 }, + { 0x6580, 0x0000 }, + { 0x6581, 0x0000 }, + { 0x6582, 0x0000 }, + { 0x6583, 0x0000 }, + { 0x6584, 0x0000 }, + { 0x6585, 0x0000 }, + { 0x6586, 0x0000 }, + { 0x6587, 0x0000 }, + { 0x6588, 0x0000 }, + { 0x6589, 0x0000 }, + { 0x658a, 0x0000 }, + { 0x658b, 0x0000 }, + { 0x658c, 0x0000 }, + { 0x658d, 0x0000 }, + { 0x658e, 0x0000 }, + { 0x658f, 0x0000 }, + { 0x6590, 0x0000 }, + { 0x6591, 0x0000 }, + { 0x6592, 0x0000 }, + { 0x6593, 0x0000 }, + { 0x6594, 0x0000 }, + { 0x6595, 0x0000 }, + { 0x6596, 0x0000 }, + { 0x6597, 0x0000 }, + { 0x6598, 0x0000 }, + { 0x6599, 0x0000 }, + { 0x659a, 0x0000 }, + { 0x659b, 0x0000 }, + { 0x659c, 0x0000 }, + { 0x659d, 0x0000 }, + { 0x659e, 0x0000 }, + { 0x659f, 0x0000 }, + { 0x65a0, 0x0000 }, + { 0x65a1, 0x0000 }, + { 0x65a2, 0x0000 }, + { 0x65a3, 0x0000 }, + { 0x65a4, 0x0000 }, + { 0x65a5, 0x0000 }, + { 0x65a6, 0x0000 }, + { 0x65a7, 0x0000 }, + { 0x65a8, 0x0000 }, + { 0x65a9, 0x0000 }, + { 0x65aa, 0x0000 }, + { 0x65ab, 0x0000 }, + { 0x65ac, 0x0000 }, + { 0x65ad, 0x0000 }, + { 0x65ae, 0x0000 }, + { 0x65af, 0x0000 }, + { 0x65b0, 0x0000 }, + { 0x65b1, 0x0000 }, + { 0x65b2, 0x0000 }, + { 0x65b3, 0x0000 }, + { 0x65b4, 0x0000 }, + { 0x65b5, 0x0000 }, + { 0x65b6, 0x0000 }, + { 0x65b7, 0x0000 }, + { 0x65b8, 0x0000 }, + { 0x65b9, 0x0000 }, + { 0x65ba, 0x0000 }, + { 0x65bb, 0x0000 }, + { 0x65bc, 0x0000 }, + { 0x65bd, 0x0000 }, + { 0x65be, 0x0000 }, + { 0x65bf, 0x0000 }, + { 0x65c0, 0x0000 }, + { 0x65c1, 0x0000 }, + { 0x65c2, 0x0000 }, + { 0x65c3, 0x0000 }, + { 0x65c4, 0x0000 }, + { 0x65c5, 0x0000 }, + { 0x65c6, 0x0000 }, + { 0x65c7, 0x0000 }, + { 0x65c8, 0x0000 }, + { 0x65c9, 0x0000 }, + { 0x65ca, 0x0000 }, + { 0x65cb, 0x0000 }, + { 0x65cc, 0x0000 }, + { 0x65cd, 0x0000 }, + { 0x65ce, 0x0000 }, + { 0x65cf, 0x0000 }, + { 0x65d0, 0x0000 }, + { 0x65d1, 0x0000 }, + { 0x65d2, 0x0000 }, + { 0x65d3, 0x0000 }, + { 0x65d4, 0x0000 }, + { 0x65d5, 0x0000 }, + { 0x65d6, 0x0000 }, + { 0x65d7, 0x0000 }, + { 0x65d8, 0x0000 }, + { 0x65d9, 0x0000 }, + { 0x65da, 0x0000 }, + { 0x65db, 0x0000 }, + { 0x65dc, 0x0000 }, + { 0x65dd, 0x0000 }, + { 0x65de, 0x0000 }, + { 0x65df, 0x0000 }, + { 0x65e0, 0x0000 }, + { 0x65e1, 0x0000 }, + { 0x65e2, 0x0000 }, + { 0x65e3, 0x0000 }, + { 0x65e4, 0x0000 }, + { 0x65e5, 0x0000 }, + { 0x65e6, 0x0000 }, + { 0x65e7, 0x0000 }, + { 0x65e8, 0x0000 }, + { 0x65e9, 0x0000 }, + { 0x65ea, 0x0000 }, + { 0x65eb, 0x0000 }, + { 0x65ec, 0x0000 }, + { 0x65ed, 0x0000 }, + { 0x65ee, 0x0000 }, + { 0x65ef, 0x0000 }, + { 0x65f0, 0x0000 }, + { 0x65f1, 0x0000 }, + { 0x65f2, 0x0000 }, + { 0x65f3, 0x0000 }, + { 0x65f4, 0x0000 }, + { 0x65f5, 0x0000 }, + { 0x65f6, 0x0000 }, + { 0x65f7, 0x0000 }, + { 0x65f8, 0x0000 }, + { 0x65f9, 0x0000 }, + { 0x65fa, 0x0000 }, + { 0x65fb, 0x0000 }, + { 0x65fc, 0x0000 }, + { 0x65fd, 0x0000 }, + { 0x65fe, 0x0000 }, + { 0x65ff, 0x0000 }, + { 0x6600, 0x0000 }, + { 0x6601, 0x0000 }, + { 0x6602, 0x0000 }, + { 0x6603, 0x0000 }, + { 0x6604, 0x0000 }, + { 0x6605, 0x0000 }, + { 0x6606, 0x0000 }, + { 0x6607, 0x0000 }, + { 0x6608, 0x0000 }, + { 0x6609, 0x0000 }, + { 0x660a, 0x0000 }, + { 0x660b, 0x0000 }, + { 0x660c, 0x0000 }, + { 0x660d, 0x0000 }, + { 0x660e, 0x0000 }, + { 0x660f, 0x0000 }, + { 0x6610, 0x0000 }, + { 0x6611, 0x0000 }, + { 0x6612, 0x0000 }, + { 0x6613, 0x0000 }, + { 0x6614, 0x0000 }, + { 0x6615, 0x0000 }, + { 0x6616, 0x0000 }, + { 0x6617, 0x0000 }, + { 0x6618, 0x0000 }, + { 0x6619, 0x0000 }, + { 0x661a, 0x0000 }, + { 0x661b, 0x0000 }, + { 0x661c, 0x0000 }, + { 0x661d, 0x0000 }, + { 0x661e, 0x0000 }, + { 0x661f, 0x0000 }, + { 0x6620, 0x0000 }, + { 0x6621, 0x0000 }, + { 0x6622, 0x0000 }, + { 0x6623, 0x0000 }, + { 0x6624, 0x0000 }, + { 0x6625, 0x0000 }, + { 0x6626, 0x0000 }, + { 0x6627, 0x0000 }, + { 0x6628, 0x0000 }, + { 0x6629, 0x0000 }, + { 0x662a, 0x0000 }, + { 0x662b, 0x0000 }, + { 0x662c, 0x0000 }, + { 0x662d, 0x0000 }, + { 0x662e, 0x0000 }, + { 0x662f, 0x0000 }, + { 0x6630, 0x0000 }, + { 0x6631, 0x0000 }, + { 0x6632, 0x0000 }, + { 0x6633, 0x0000 }, + { 0x6634, 0x0000 }, + { 0x6635, 0x0000 }, + { 0x6636, 0x0000 }, + { 0x6637, 0x0000 }, + { 0x6638, 0x0000 }, + { 0x6639, 0x0000 }, + { 0x663a, 0x0000 }, + { 0x663b, 0x0000 }, + { 0x663c, 0x0000 }, + { 0x663d, 0x0000 }, + { 0x663e, 0x0000 }, + { 0x663f, 0x0000 }, + { 0x6640, 0x0000 }, + { 0x6641, 0x0000 }, + { 0x6642, 0x0000 }, + { 0x6643, 0x0000 }, + { 0x6644, 0x0000 }, + { 0x6645, 0x0000 }, + { 0x6646, 0x0000 }, + { 0x6647, 0x0000 }, + { 0x6648, 0x0000 }, + { 0x6649, 0x0000 }, + { 0x664a, 0x0000 }, + { 0x664b, 0x0000 }, + { 0x664c, 0x0000 }, + { 0x664d, 0x0000 }, + { 0x664e, 0x0000 }, + { 0x664f, 0x0000 }, + { 0x6650, 0x0000 }, + { 0x6651, 0x0000 }, + { 0x6652, 0x0000 }, + { 0x6653, 0x0000 }, + { 0x6654, 0x0000 }, + { 0x6655, 0x0000 }, + { 0x6656, 0x0000 }, + { 0x6657, 0x0000 }, + { 0x6658, 0x0000 }, + { 0x6659, 0x0000 }, + { 0x665a, 0x0000 }, + { 0x665b, 0x0000 }, + { 0x665c, 0x0000 }, + { 0x665d, 0x0000 }, + { 0x665e, 0x0000 }, + { 0x665f, 0x0000 }, + { 0x6660, 0x0000 }, + { 0x6661, 0x0000 }, + { 0x6662, 0x0000 }, + { 0x6663, 0x0000 }, + { 0x6664, 0x0000 }, + { 0x6665, 0x0000 }, + { 0x6666, 0x0000 }, + { 0x6667, 0x0000 }, + { 0x6668, 0x0000 }, + { 0x6669, 0x0000 }, + { 0x666a, 0x0000 }, + { 0x666b, 0x0000 }, + { 0x666c, 0x0000 }, + { 0x666d, 0x0000 }, + { 0x666e, 0x0000 }, + { 0x666f, 0x0000 }, + { 0x6670, 0x0000 }, + { 0x6671, 0x0000 }, + { 0x6672, 0x0000 }, + { 0x6673, 0x0000 }, + { 0x6674, 0x0000 }, + { 0x6675, 0x0000 }, + { 0x6676, 0x0000 }, + { 0x6677, 0x0000 }, + { 0x6678, 0x0000 }, + { 0x6679, 0x0000 }, + { 0x667a, 0x0000 }, + { 0x667b, 0x0000 }, + { 0x667c, 0x0000 }, + { 0x667d, 0x0000 }, + { 0x667e, 0x0000 }, + { 0x667f, 0x0000 }, + { 0x6680, 0x0000 }, + { 0x6681, 0x0000 }, + { 0x6682, 0x0000 }, + { 0x6683, 0x0000 }, + { 0x6684, 0x0000 }, + { 0x6685, 0x0000 }, + { 0x6686, 0x0000 }, + { 0x6687, 0x0000 }, + { 0x6688, 0x0000 }, + { 0x6689, 0x0000 }, + { 0x668a, 0x0000 }, + { 0x668b, 0x0000 }, + { 0x668c, 0x0000 }, + { 0x668d, 0x0000 }, + { 0x668e, 0x0000 }, + { 0x668f, 0x0000 }, + { 0x6690, 0x0000 }, + { 0x6691, 0x0000 }, + { 0x6692, 0x0000 }, + { 0x6693, 0x0000 }, + { 0x6694, 0x0000 }, + { 0x6695, 0x0000 }, + { 0x6696, 0x0000 }, + { 0x6697, 0x0000 }, + { 0x6698, 0x0000 }, + { 0x6699, 0x0000 }, + { 0x669a, 0x0000 }, + { 0x669b, 0x0000 }, + { 0x669c, 0x0000 }, + { 0x669d, 0x0000 }, + { 0x669e, 0x0000 }, + { 0x669f, 0x0000 }, + { 0x66a0, 0x0000 }, + { 0x66a1, 0x0000 }, + { 0x66a2, 0x0000 }, + { 0x66a3, 0x0000 }, + { 0x66a4, 0x0000 }, + { 0x66a5, 0x0000 }, + { 0x66a6, 0x0000 }, + { 0x66a7, 0x0000 }, + { 0x66a8, 0x0000 }, + { 0x66a9, 0x0000 }, + { 0x66aa, 0x0000 }, + { 0x66ab, 0x0000 }, + { 0x66ac, 0x0000 }, + { 0x66ad, 0x0000 }, + { 0x66ae, 0x0000 }, + { 0x66af, 0x0000 }, + { 0x66b0, 0x0000 }, + { 0x66b1, 0x0000 }, + { 0x66b2, 0x0000 }, + { 0x66b3, 0x0000 }, + { 0x66b4, 0x0000 }, + { 0x66b5, 0x0000 }, + { 0x66b6, 0x0000 }, + { 0x66b7, 0x0000 }, + { 0x66b8, 0x0000 }, + { 0x66b9, 0x0000 }, + { 0x66ba, 0x0000 }, + { 0x66bb, 0x0000 }, + { 0x66bc, 0x0000 }, + { 0x66bd, 0x0000 }, + { 0x66be, 0x0000 }, + { 0x66bf, 0x0000 }, + { 0x66c0, 0x0000 }, + { 0x66c1, 0x0000 }, + { 0x66c2, 0x0000 }, + { 0x66c3, 0x0000 }, + { 0x66c4, 0x0000 }, + { 0x66c5, 0x0000 }, + { 0x66c6, 0x0000 }, + { 0x66c7, 0x0000 }, + { 0x66c8, 0x0000 }, + { 0x66c9, 0x0000 }, + { 0x66ca, 0x0000 }, + { 0x66cb, 0x0000 }, + { 0x66cc, 0x0000 }, + { 0x66cd, 0x0000 }, + { 0x66ce, 0x0000 }, + { 0x66cf, 0x0000 }, + { 0x66d0, 0x0000 }, + { 0x66d1, 0x0000 }, + { 0x66d2, 0x0000 }, + { 0x66d3, 0x0000 }, + { 0x66d4, 0x0000 }, + { 0x66d5, 0x0000 }, + { 0x66d6, 0x0000 }, + { 0x66d7, 0x0000 }, + { 0x66d8, 0x0000 }, + { 0x66d9, 0x0000 }, + { 0x66da, 0x0000 }, + { 0x66db, 0x0000 }, + { 0x66dc, 0x0000 }, + { 0x66dd, 0x0000 }, + { 0x66de, 0x0000 }, + { 0x66df, 0x0000 }, + { 0x66e0, 0x0000 }, + { 0x66e1, 0x0000 }, + { 0x66e2, 0x0000 }, + { 0x66e3, 0x0000 }, + { 0x66e4, 0x0000 }, + { 0x66e5, 0x0000 }, + { 0x66e6, 0x0000 }, + { 0x66e7, 0x0000 }, + { 0x66e8, 0x0000 }, + { 0x66e9, 0x0000 }, + { 0x66ea, 0x0000 }, + { 0x66eb, 0x0000 }, + { 0x66ec, 0x0000 }, + { 0x66ed, 0x0000 }, + { 0x66ee, 0x0000 }, + { 0x66ef, 0x0000 }, + { 0x66f0, 0x0000 }, + { 0x66f1, 0x0000 }, + { 0x66f2, 0x0000 }, + { 0x66f3, 0x0000 }, + { 0x66f4, 0x0000 }, + { 0x66f5, 0x0000 }, + { 0x66f6, 0x0000 }, + { 0x66f7, 0x0000 }, + { 0x66f8, 0x0000 }, + { 0x66f9, 0x0000 }, + { 0x66fa, 0x0000 }, + { 0x66fb, 0x0000 }, + { 0x66fc, 0x0000 }, + { 0x66fd, 0x0000 }, + { 0x66fe, 0x0000 }, + { 0x66ff, 0x0000 }, + { 0x6700, 0x0000 }, + { 0x6701, 0x0000 }, + { 0x6702, 0x0000 }, + { 0x6703, 0x0000 }, + { 0x6704, 0x0000 }, + { 0x6705, 0x0000 }, + { 0x6706, 0x0000 }, + { 0x6707, 0x0000 }, + { 0x6708, 0x0000 }, + { 0x6709, 0x0000 }, + { 0x670a, 0x0000 }, + { 0x670b, 0x0000 }, + { 0x670c, 0x0000 }, + { 0x670d, 0x0000 }, + { 0x670e, 0x0000 }, + { 0x670f, 0x0000 }, + { 0x6710, 0x0000 }, + { 0x6711, 0x0000 }, + { 0x6712, 0x0000 }, + { 0x6713, 0x0000 }, + { 0x6714, 0x0000 }, + { 0x6715, 0x0000 }, + { 0x6716, 0x0000 }, + { 0x6717, 0x0000 }, + { 0x6718, 0x0000 }, + { 0x6719, 0x0000 }, + { 0x671a, 0x0000 }, + { 0x671b, 0x0000 }, + { 0x671c, 0x0000 }, + { 0x671d, 0x0000 }, + { 0x671e, 0x0000 }, + { 0x671f, 0x0000 }, + { 0x6720, 0x0000 }, + { 0x6721, 0x0000 }, + { 0x6722, 0x0000 }, + { 0x6723, 0x0000 }, + { 0x6724, 0x0000 }, + { 0x6725, 0x0000 }, + { 0x6726, 0x0000 }, + { 0x6727, 0x0000 }, + { 0x6728, 0x0000 }, + { 0x6729, 0x0000 }, + { 0x672a, 0x0000 }, + { 0x672b, 0x0000 }, + { 0x672c, 0x0000 }, + { 0x672d, 0x0000 }, + { 0x672e, 0x0000 }, + { 0x672f, 0x0000 }, + { 0x6730, 0x0000 }, + { 0x6731, 0x0000 }, + { 0x6732, 0x0000 }, + { 0x6733, 0x0000 }, + { 0x6734, 0x0000 }, + { 0x6735, 0x0000 }, + { 0x6736, 0x0000 }, + { 0x6737, 0x0000 }, + { 0x6738, 0x0000 }, + { 0x6739, 0x0000 }, + { 0x673a, 0x0000 }, + { 0x673b, 0x0000 }, + { 0x673c, 0x0000 }, + { 0x673d, 0x0000 }, + { 0x673e, 0x0000 }, + { 0x673f, 0x0000 }, + { 0x6740, 0x0000 }, + { 0x6741, 0x0000 }, + { 0x6742, 0x0000 }, + { 0x6743, 0x0000 }, + { 0x6744, 0x0000 }, + { 0x6745, 0x0000 }, + { 0x6746, 0x0000 }, + { 0x6747, 0x0000 }, + { 0x6748, 0x0000 }, + { 0x6749, 0x0000 }, + { 0x674a, 0x0000 }, + { 0x674b, 0x0000 }, + { 0x674c, 0x0000 }, + { 0x674d, 0x0000 }, + { 0x674e, 0x0000 }, + { 0x674f, 0x0000 }, + { 0x6750, 0x0000 }, + { 0x6751, 0x0000 }, + { 0x6752, 0x0000 }, + { 0x6753, 0x0000 }, + { 0x6754, 0x0000 }, + { 0x6755, 0x0000 }, + { 0x6756, 0x0000 }, + { 0x6757, 0x0000 }, + { 0x6758, 0x0000 }, + { 0x6759, 0x0000 }, + { 0x675a, 0x0000 }, + { 0x675b, 0x0000 }, + { 0x675c, 0x0000 }, + { 0x675d, 0x0000 }, + { 0x675e, 0x0000 }, + { 0x675f, 0x0000 }, + { 0x6760, 0x0000 }, + { 0x6761, 0x0000 }, + { 0x6762, 0x0000 }, + { 0x6763, 0x0000 }, + { 0x6764, 0x0000 }, + { 0x6765, 0x0000 }, + { 0x6766, 0x0000 }, + { 0x6767, 0x0000 }, + { 0x6768, 0x0000 }, + { 0x6769, 0x0000 }, + { 0x676a, 0x0000 }, + { 0x676b, 0x0000 }, + { 0x676c, 0x0000 }, + { 0x676d, 0x0000 }, + { 0x676e, 0x0000 }, + { 0x676f, 0x0000 }, + { 0x6770, 0x0000 }, + { 0x6771, 0x0000 }, + { 0x6772, 0x0000 }, + { 0x6773, 0x0000 }, + { 0x6774, 0x0000 }, + { 0x6775, 0x0000 }, + { 0x6776, 0x0000 }, + { 0x6777, 0x0000 }, + { 0x6778, 0x0000 }, + { 0x6779, 0x0000 }, + { 0x677a, 0x0000 }, + { 0x677b, 0x0000 }, + { 0x677c, 0x0000 }, + { 0x677d, 0x0000 }, + { 0x677e, 0x0000 }, + { 0x677f, 0x0000 }, + { 0x6780, 0x0000 }, + { 0x6781, 0x0000 }, + { 0x6782, 0x0000 }, + { 0x6783, 0x0000 }, + { 0x6784, 0x0000 }, + { 0x6785, 0x0000 }, + { 0x6786, 0x0000 }, + { 0x6787, 0x0000 }, + { 0x6788, 0x0000 }, + { 0x6789, 0x0000 }, + { 0x678a, 0x0000 }, + { 0x678b, 0x0000 }, + { 0x678c, 0x0000 }, + { 0x678d, 0x0000 }, + { 0x678e, 0x0000 }, + { 0x678f, 0x0000 }, + { 0x6790, 0x0000 }, + { 0x6791, 0x0000 }, + { 0x6792, 0x0000 }, + { 0x6793, 0x0000 }, + { 0x6794, 0x0000 }, + { 0x6795, 0x0000 }, + { 0x6796, 0x0000 }, + { 0x6797, 0x0000 }, + { 0x6798, 0x0000 }, + { 0x6799, 0x0000 }, + { 0x679a, 0x0000 }, + { 0x679b, 0x0000 }, + { 0x679c, 0x0000 }, + { 0x679d, 0x0000 }, + { 0x679e, 0x0000 }, + { 0x679f, 0x0000 }, + { 0x67a0, 0x0000 }, + { 0x67a1, 0x0000 }, + { 0x67a2, 0x0000 }, + { 0x67a3, 0x0000 }, + { 0x67a4, 0x0000 }, + { 0x67a5, 0x0000 }, + { 0x67a6, 0x0000 }, + { 0x67a7, 0x0000 }, + { 0x67a8, 0x0000 }, + { 0x67a9, 0x0000 }, + { 0x67aa, 0x0000 }, + { 0x67ab, 0x0000 }, + { 0x67ac, 0x0000 }, + { 0x67ad, 0x0000 }, + { 0x67ae, 0x0000 }, + { 0x67af, 0x0000 }, + { 0x67b0, 0x0000 }, + { 0x67b1, 0x0000 }, + { 0x67b2, 0x0000 }, + { 0x67b3, 0x0000 }, + { 0x67b4, 0x0000 }, + { 0x67b5, 0x0000 }, + { 0x67b6, 0x0000 }, + { 0x67b7, 0x0000 }, + { 0x67b8, 0x0000 }, + { 0x67b9, 0x0000 }, + { 0x67ba, 0x0000 }, + { 0x67bb, 0x0000 }, + { 0x67bc, 0x0000 }, + { 0x67bd, 0x0000 }, + { 0x67be, 0x0000 }, + { 0x67bf, 0x0000 }, + { 0x67c0, 0x0000 }, + { 0x67c1, 0x0000 }, + { 0x67c2, 0x0000 }, + { 0x67c3, 0x0000 }, + { 0x67c4, 0x0000 }, + { 0x67c5, 0x0000 }, + { 0x67c6, 0x0000 }, + { 0x67c7, 0x0000 }, + { 0x67c8, 0x0000 }, + { 0x67c9, 0x0000 }, + { 0x67ca, 0x0000 }, + { 0x67cb, 0x0000 }, + { 0x67cc, 0x0000 }, + { 0x67cd, 0x0000 }, + { 0x67ce, 0x0000 }, + { 0x67cf, 0x0000 }, + { 0x67d0, 0x0000 }, + { 0x67d1, 0x0000 }, + { 0x67d2, 0x0000 }, + { 0x67d3, 0x0000 }, + { 0x67d4, 0x0000 }, + { 0x67d5, 0x0000 }, + { 0x67d6, 0x0000 }, + { 0x67d7, 0x0000 }, + { 0x67d8, 0x0000 }, + { 0x67d9, 0x0000 }, + { 0x67da, 0x0000 }, + { 0x67db, 0x0000 }, + { 0x67dc, 0x0000 }, + { 0x67dd, 0x0000 }, + { 0x67de, 0x0000 }, + { 0x67df, 0x0000 }, + { 0x67e0, 0x0000 }, + { 0x67e1, 0x0000 }, + { 0x67e2, 0x0000 }, + { 0x67e3, 0x0000 }, + { 0x67e4, 0x0000 }, + { 0x67e5, 0x0000 }, + { 0x67e6, 0x0000 }, + { 0x67e7, 0x0000 }, + { 0x67e8, 0x0000 }, + { 0x67e9, 0x0000 }, + { 0x67ea, 0x0000 }, + { 0x67eb, 0x0000 }, + { 0x67ec, 0x0000 }, + { 0x67ed, 0x0000 }, + { 0x67ee, 0x0000 }, + { 0x67ef, 0x0000 }, + { 0x67f0, 0x0000 }, + { 0x67f1, 0x0000 }, + { 0x67f2, 0x0000 }, + { 0x67f3, 0x0000 }, + { 0x67f4, 0x0000 }, + { 0x67f5, 0x0000 }, + { 0x67f6, 0x0000 }, + { 0x67f7, 0x0000 }, + { 0x67f8, 0x0000 }, + { 0x67f9, 0x0000 }, + { 0x67fa, 0x0000 }, + { 0x67fb, 0x0000 }, + { 0x67fc, 0x0000 }, + { 0x67fd, 0x0000 }, + { 0x67fe, 0x0000 }, + { 0x67ff, 0x0000 }, + { 0x6800, 0x0000 }, + { 0x6801, 0x0000 }, + { 0x6802, 0x0000 }, + { 0x6803, 0x0000 }, + { 0x6804, 0x0000 }, + { 0x6805, 0x0000 }, + { 0x6806, 0x0000 }, + { 0x6807, 0x0000 }, + { 0x6808, 0x0000 }, + { 0x6809, 0x0000 }, + { 0x680a, 0x0000 }, + { 0x680b, 0x0000 }, + { 0x680c, 0x0000 }, + { 0x680d, 0x0000 }, + { 0x680e, 0x0000 }, + { 0x680f, 0x0000 }, + { 0x6810, 0x0000 }, + { 0x6811, 0x0000 }, + { 0x6812, 0x0000 }, + { 0x6813, 0x0000 }, + { 0x6814, 0x0000 }, + { 0x6815, 0x0000 }, + { 0x6816, 0x0000 }, + { 0x6817, 0x0000 }, + { 0x6818, 0x0000 }, + { 0x6819, 0x0000 }, + { 0x681a, 0x0000 }, + { 0x681b, 0x0000 }, + { 0x681c, 0x0000 }, + { 0x681d, 0x0000 }, + { 0x681e, 0x0000 }, + { 0x681f, 0x0000 }, + { 0x6820, 0x0000 }, + { 0x6821, 0x0000 }, + { 0x6822, 0x0000 }, + { 0x6823, 0x0000 }, + { 0x6824, 0x0000 }, + { 0x6825, 0x0000 }, + { 0x6826, 0x0000 }, + { 0x6827, 0x0000 }, + { 0x6828, 0x0000 }, + { 0x6829, 0x0000 }, + { 0x682a, 0x0000 }, + { 0x682b, 0x0000 }, + { 0x682c, 0x0000 }, + { 0x682d, 0x0000 }, + { 0x682e, 0x0000 }, + { 0x682f, 0x0000 }, + { 0x6830, 0x0000 }, + { 0x6831, 0x0000 }, + { 0x6832, 0x0000 }, + { 0x6833, 0x0000 }, + { 0x6834, 0x0000 }, + { 0x6835, 0x0000 }, + { 0x6836, 0x0000 }, + { 0x6837, 0x0000 }, + { 0x6838, 0x0000 }, + { 0x6839, 0x0000 }, + { 0x683a, 0x0000 }, + { 0x683b, 0x0000 }, + { 0x683c, 0x0000 }, + { 0x683d, 0x0000 }, + { 0x683e, 0x0000 }, + { 0x683f, 0x0000 }, + { 0x6840, 0x0000 }, + { 0x6841, 0x0000 }, + { 0x6842, 0x0000 }, + { 0x6843, 0x0000 }, + { 0x6844, 0x0000 }, + { 0x6845, 0x0000 }, + { 0x6846, 0x0000 }, + { 0x6847, 0x0000 }, + { 0x6848, 0x0000 }, + { 0x6849, 0x0000 }, + { 0x684a, 0x0000 }, + { 0x684b, 0x0000 }, + { 0x684c, 0x0000 }, + { 0x684d, 0x0000 }, + { 0x684e, 0x0000 }, + { 0x684f, 0x0000 }, + { 0x6850, 0x0000 }, + { 0x6851, 0x0000 }, + { 0x6852, 0x0000 }, + { 0x6853, 0x0000 }, + { 0x6854, 0x0000 }, + { 0x6855, 0x0000 }, + { 0x6856, 0x0000 }, + { 0x6857, 0x0000 }, + { 0x6858, 0x0000 }, + { 0x6859, 0x0000 }, + { 0x685a, 0x0000 }, + { 0x685b, 0x0000 }, + { 0x685c, 0x0000 }, + { 0x685d, 0x0000 }, + { 0x685e, 0x0000 }, + { 0x685f, 0x0000 }, + { 0x6860, 0x0000 }, + { 0x6861, 0x0000 }, + { 0x6862, 0x0000 }, + { 0x6863, 0x0000 }, + { 0x6864, 0x0000 }, + { 0x6865, 0x0000 }, + { 0x6866, 0x0000 }, + { 0x6867, 0x0000 }, + { 0x6868, 0x0000 }, + { 0x6869, 0x0000 }, + { 0x686a, 0x0000 }, + { 0x686b, 0x0000 }, + { 0x686c, 0x0000 }, + { 0x686d, 0x0000 }, + { 0x686e, 0x0000 }, + { 0x686f, 0x0000 }, + { 0x6870, 0x0000 }, + { 0x6871, 0x0000 }, + { 0x6872, 0x0000 }, + { 0x6873, 0x0000 }, + { 0x6874, 0x0000 }, + { 0x6875, 0x0000 }, + { 0x6876, 0x0000 }, + { 0x6877, 0x0000 }, + { 0x6878, 0x0000 }, + { 0x6879, 0x0000 }, + { 0x687a, 0x0000 }, + { 0x687b, 0x0000 }, + { 0x687c, 0x0000 }, + { 0x687d, 0x0000 }, + { 0x687e, 0x0000 }, + { 0x687f, 0x0000 }, + { 0x6880, 0x0000 }, + { 0x6881, 0x0000 }, + { 0x6882, 0x0000 }, + { 0x6883, 0x0000 }, + { 0x6884, 0x0000 }, + { 0x6885, 0x0000 }, + { 0x6886, 0x0000 }, + { 0x6887, 0x0000 }, + { 0x6888, 0x0000 }, + { 0x6889, 0x0000 }, + { 0x688a, 0x0000 }, + { 0x688b, 0x0000 }, + { 0x688c, 0x0000 }, + { 0x688d, 0x0000 }, + { 0x688e, 0x0000 }, + { 0x688f, 0x0000 }, + { 0x6890, 0x0000 }, + { 0x6891, 0x0000 }, + { 0x6892, 0x0000 }, + { 0x6893, 0x0000 }, + { 0x6894, 0x0000 }, + { 0x6895, 0x0000 }, + { 0x6896, 0x0000 }, + { 0x6897, 0x0000 }, + { 0x6898, 0x0000 }, + { 0x6899, 0x0000 }, + { 0x689a, 0x0000 }, + { 0x689b, 0x0000 }, + { 0x689c, 0x0000 }, + { 0x689d, 0x0000 }, + { 0x689e, 0x0000 }, + { 0x689f, 0x0000 }, + { 0x68a0, 0x0000 }, + { 0x68a1, 0x0000 }, + { 0x68a2, 0x0000 }, + { 0x68a3, 0x0000 }, + { 0x68a4, 0x0000 }, + { 0x68a5, 0x0000 }, + { 0x68a6, 0x0000 }, + { 0x68a7, 0x0000 }, + { 0x68a8, 0x0000 }, + { 0x68a9, 0x0000 }, + { 0x68aa, 0x0000 }, + { 0x68ab, 0x0000 }, + { 0x68ac, 0x0000 }, + { 0x68ad, 0x0000 }, + { 0x68ae, 0x0000 }, + { 0x68af, 0x0000 }, + { 0x68b0, 0x0000 }, + { 0x68b1, 0x0000 }, + { 0x68b2, 0x0000 }, + { 0x68b3, 0x0000 }, + { 0x68b4, 0x0000 }, + { 0x68b5, 0x0000 }, + { 0x68b6, 0x0000 }, + { 0x68b7, 0x0000 }, + { 0x68b8, 0x0000 }, + { 0x68b9, 0x0000 }, + { 0x68ba, 0x0000 }, + { 0x68bb, 0x0000 }, + { 0x68bc, 0x0000 }, + { 0x68bd, 0x0000 }, + { 0x68be, 0x0000 }, + { 0x68bf, 0x0000 }, + { 0x68c0, 0x0000 }, + { 0x68c1, 0x0000 }, + { 0x68c2, 0x0000 }, + { 0x68c3, 0x0000 }, + { 0x68c4, 0x0000 }, + { 0x68c5, 0x0000 }, + { 0x68c6, 0x0000 }, + { 0x68c7, 0x0000 }, + { 0x68c8, 0x0000 }, + { 0x68c9, 0x0000 }, + { 0x68ca, 0x0000 }, + { 0x68cb, 0x0000 }, + { 0x68cc, 0x0000 }, + { 0x68cd, 0x0000 }, + { 0x68ce, 0x0000 }, + { 0x68cf, 0x0000 }, + { 0x68d0, 0x0000 }, + { 0x68d1, 0x0000 }, + { 0x68d2, 0x0000 }, + { 0x68d3, 0x0000 }, + { 0x68d4, 0x0000 }, + { 0x68d5, 0x0000 }, + { 0x68d6, 0x0000 }, + { 0x68d7, 0x0000 }, + { 0x68d8, 0x0000 }, + { 0x68d9, 0x0000 }, + { 0x68da, 0x0000 }, + { 0x68db, 0x0000 }, + { 0x68dc, 0x0000 }, + { 0x68dd, 0x0000 }, + { 0x68de, 0x0000 }, + { 0x68df, 0x0000 }, + { 0x68e0, 0x0000 }, + { 0x68e1, 0x0000 }, + { 0x68e2, 0x0000 }, + { 0x68e3, 0x0000 }, + { 0x68e4, 0x0000 }, + { 0x68e5, 0x0000 }, + { 0x68e6, 0x0000 }, + { 0x68e7, 0x0000 }, + { 0x68e8, 0x0000 }, + { 0x68e9, 0x0000 }, + { 0x68ea, 0x0000 }, + { 0x68eb, 0x0000 }, + { 0x68ec, 0x0000 }, + { 0x68ed, 0x0000 }, + { 0x68ee, 0x0000 }, + { 0x68ef, 0x0000 }, + { 0x68f0, 0x0000 }, + { 0x68f1, 0x0000 }, + { 0x68f2, 0x0000 }, + { 0x68f3, 0x0000 }, + { 0x68f4, 0x0000 }, + { 0x68f5, 0x0000 }, + { 0x68f6, 0x0000 }, + { 0x68f7, 0x0000 }, + { 0x68f8, 0x0000 }, + { 0x68f9, 0x0000 }, + { 0x68fa, 0x0000 }, + { 0x68fb, 0x0000 }, + { 0x68fc, 0x0000 }, + { 0x68fd, 0x0000 }, + { 0x68fe, 0x0000 }, + { 0x68ff, 0x0000 }, + { 0x6900, 0x0000 }, + { 0x6901, 0x0000 }, + { 0x6902, 0x0000 }, + { 0x6903, 0x0000 }, + { 0x6904, 0x0000 }, + { 0x6905, 0x0000 }, + { 0x6906, 0x0000 }, + { 0x6907, 0x0000 }, + { 0x6908, 0x0000 }, + { 0x6909, 0x0000 }, + { 0x690a, 0x0000 }, + { 0x690b, 0x0000 }, + { 0x690c, 0x0000 }, + { 0x690d, 0x0000 }, + { 0x690e, 0x0000 }, + { 0x690f, 0x0000 }, + { 0x6910, 0x0000 }, + { 0x6911, 0x0000 }, + { 0x6912, 0x0000 }, + { 0x6913, 0x0000 }, + { 0x6914, 0x0000 }, + { 0x6915, 0x0000 }, + { 0x6916, 0x0000 }, + { 0x6917, 0x0000 }, + { 0x6918, 0x0000 }, + { 0x6919, 0x0000 }, + { 0x691a, 0x0000 }, + { 0x691b, 0x0000 }, + { 0x691c, 0x0000 }, + { 0x691d, 0x0000 }, + { 0x691e, 0x0000 }, + { 0x691f, 0x0000 }, + { 0x6920, 0x0000 }, + { 0x6921, 0x0000 }, + { 0x6922, 0x0000 }, + { 0x6923, 0x0000 }, + { 0x6924, 0x0000 }, + { 0x6925, 0x0000 }, + { 0x6926, 0x0000 }, + { 0x6927, 0x0000 }, + { 0x6928, 0x0000 }, + { 0x6929, 0x0000 }, + { 0x692a, 0x0000 }, + { 0x692b, 0x0000 }, + { 0x692c, 0x0000 }, + { 0x692d, 0x0000 }, + { 0x692e, 0x0000 }, + { 0x692f, 0x0000 }, + { 0x6930, 0x0000 }, + { 0x6931, 0x0000 }, + { 0x6932, 0x0000 }, + { 0x6933, 0x0000 }, + { 0x6934, 0x0000 }, + { 0x6935, 0x0000 }, + { 0x6936, 0x0000 }, + { 0x6937, 0x0000 }, + { 0x6938, 0x0000 }, + { 0x6939, 0x0000 }, + { 0x693a, 0x0000 }, + { 0x693b, 0x0000 }, + { 0x693c, 0x0000 }, + { 0x693d, 0x0000 }, + { 0x693e, 0x0000 }, + { 0x693f, 0x0000 }, + { 0x6940, 0x0000 }, + { 0x6941, 0x0000 }, + { 0x6942, 0x0000 }, + { 0x6943, 0x0000 }, + { 0x6944, 0x0000 }, + { 0x6945, 0x0000 }, + { 0x6946, 0x0000 }, + { 0x6947, 0x0000 }, + { 0x6948, 0x0000 }, + { 0x6949, 0x0000 }, + { 0x694a, 0x0000 }, + { 0x694b, 0x0000 }, + { 0x694c, 0x0000 }, + { 0x694d, 0x0000 }, + { 0x694e, 0x0000 }, + { 0x694f, 0x0000 }, + { 0x6950, 0x0000 }, + { 0x6951, 0x0000 }, + { 0x6952, 0x0000 }, + { 0x6953, 0x0000 }, + { 0x6954, 0x0000 }, + { 0x6955, 0x0000 }, + { 0x6956, 0x0000 }, + { 0x6957, 0x0000 }, + { 0x6958, 0x0000 }, + { 0x6959, 0x0000 }, + { 0x695a, 0x0000 }, + { 0x695b, 0x0000 }, + { 0x695c, 0x0000 }, + { 0x695d, 0x0000 }, + { 0x695e, 0x0000 }, + { 0x695f, 0x0000 }, + { 0x6960, 0x0000 }, + { 0x6961, 0x0000 }, + { 0x6962, 0x0000 }, + { 0x6963, 0x0000 }, + { 0x6964, 0x0000 }, + { 0x6965, 0x0000 }, + { 0x6966, 0x0000 }, + { 0x6967, 0x0000 }, + { 0x6968, 0x0000 }, + { 0x6969, 0x0000 }, + { 0x696a, 0x0000 }, + { 0x696b, 0x0000 }, + { 0x696c, 0x0000 }, + { 0x696d, 0x0000 }, + { 0x696e, 0x0000 }, + { 0x696f, 0x0000 }, + { 0x6970, 0x0000 }, + { 0x6971, 0x0000 }, + { 0x6972, 0x0000 }, + { 0x6973, 0x0000 }, + { 0x6974, 0x0000 }, + { 0x6975, 0x0000 }, + { 0x6976, 0x0000 }, + { 0x6977, 0x0000 }, + { 0x6978, 0x0000 }, + { 0x6979, 0x0000 }, + { 0x697a, 0x0000 }, + { 0x697b, 0x0000 }, + { 0x697c, 0x0000 }, + { 0x697d, 0x0000 }, + { 0x697e, 0x0000 }, + { 0x697f, 0x0000 }, + { 0x6980, 0x0000 }, + { 0x6981, 0x0000 }, + { 0x6982, 0x0000 }, + { 0x6983, 0x0000 }, + { 0x6984, 0x0000 }, + { 0x6985, 0x0000 }, + { 0x6986, 0x0000 }, + { 0x6987, 0x0000 }, + { 0x6988, 0x0000 }, + { 0x6989, 0x0000 }, + { 0x698a, 0x0000 }, + { 0x698b, 0x0000 }, + { 0x698c, 0x0000 }, + { 0x698d, 0x0000 }, + { 0x698e, 0x0000 }, + { 0x698f, 0x0000 }, + { 0x6990, 0x0000 }, + { 0x6991, 0x0000 }, + { 0x6992, 0x0000 }, + { 0x6993, 0x0000 }, + { 0x6994, 0x0000 }, + { 0x6995, 0x0000 }, + { 0x6996, 0x0000 }, + { 0x6997, 0x0000 }, + { 0x6998, 0x0000 }, + { 0x6999, 0x0000 }, + { 0x699a, 0x0000 }, + { 0x699b, 0x0000 }, + { 0x699c, 0x0000 }, + { 0x699d, 0x0000 }, + { 0x699e, 0x0000 }, + { 0x699f, 0x0000 }, + { 0x69a0, 0x0000 }, + { 0x69a1, 0x0000 }, + { 0x69a2, 0x0000 }, + { 0x69a3, 0x0000 }, + { 0x69a4, 0x0000 }, + { 0x69a5, 0x0000 }, + { 0x69a6, 0x0000 }, + { 0x69a7, 0x0000 }, + { 0x69a8, 0x0000 }, + { 0x69a9, 0x0000 }, + { 0x69aa, 0x0000 }, + { 0x69ab, 0x0000 }, + { 0x69ac, 0x0000 }, + { 0x69ad, 0x0000 }, + { 0x69ae, 0x0000 }, + { 0x69af, 0x0000 }, + { 0x69b0, 0x0000 }, + { 0x69b1, 0x0000 }, + { 0x69b2, 0x0000 }, + { 0x69b3, 0x0000 }, + { 0x69b4, 0x0000 }, + { 0x69b5, 0x0000 }, + { 0x69b6, 0x0000 }, + { 0x69b7, 0x0000 }, + { 0x69b8, 0x0000 }, + { 0x69b9, 0x0000 }, + { 0x69ba, 0x0000 }, + { 0x69bb, 0x0000 }, + { 0x69bc, 0x0000 }, + { 0x69bd, 0x0000 }, + { 0x69be, 0x0000 }, + { 0x69bf, 0x0000 }, + { 0x69c0, 0x0000 }, + { 0x69c1, 0x0000 }, + { 0x69c2, 0x0000 }, + { 0x69c3, 0x0000 }, + { 0x69c4, 0x0000 }, + { 0x69c5, 0x0000 }, + { 0x69c6, 0x0000 }, + { 0x69c7, 0x0000 }, + { 0x69c8, 0x0000 }, + { 0x69c9, 0x0000 }, + { 0x69ca, 0x0000 }, + { 0x69cb, 0x0000 }, + { 0x69cc, 0x0000 }, + { 0x69cd, 0x0000 }, + { 0x69ce, 0x0000 }, + { 0x69cf, 0x0000 }, + { 0x69d0, 0x0000 }, + { 0x69d1, 0x0000 }, + { 0x69d2, 0x0000 }, + { 0x69d3, 0x0000 }, + { 0x69d4, 0x0000 }, + { 0x69d5, 0x0000 }, + { 0x69d6, 0x0000 }, + { 0x69d7, 0x0000 }, + { 0x69d8, 0x0000 }, + { 0x69d9, 0x0000 }, + { 0x69da, 0x0000 }, + { 0x69db, 0x0000 }, + { 0x69dc, 0x0000 }, + { 0x69dd, 0x0000 }, + { 0x69de, 0x0000 }, + { 0x69df, 0x0000 }, + { 0x69e0, 0x0000 }, + { 0x69e1, 0x0000 }, + { 0x69e2, 0x0000 }, + { 0x69e3, 0x0000 }, + { 0x69e4, 0x0000 }, + { 0x69e5, 0x0000 }, + { 0x69e6, 0x0000 }, + { 0x69e7, 0x0000 }, + { 0x69e8, 0x0000 }, + { 0x69e9, 0x0000 }, + { 0x69ea, 0x0000 }, + { 0x69eb, 0x0000 }, + { 0x69ec, 0x0000 }, + { 0x69ed, 0x0000 }, + { 0x69ee, 0x0000 }, + { 0x69ef, 0x0000 }, + { 0x69f0, 0x0000 }, + { 0x69f1, 0x0000 }, + { 0x69f2, 0x0000 }, + { 0x69f3, 0x0000 }, + { 0x69f4, 0x0000 }, + { 0x69f5, 0x0000 }, + { 0x69f6, 0x0000 }, + { 0x69f7, 0x0000 }, + { 0x69f8, 0x0000 }, + { 0x69f9, 0x0000 }, + { 0x69fa, 0x0000 }, + { 0x69fb, 0x0000 }, + { 0x69fc, 0x0000 }, + { 0x69fd, 0x0000 }, + { 0x69fe, 0x0000 }, + { 0x69ff, 0x0000 }, + { 0x6a00, 0x0000 }, + { 0x6a01, 0x0000 }, + { 0x6a02, 0x0000 }, + { 0x6a03, 0x0000 }, + { 0x6a04, 0x0000 }, + { 0x6a05, 0x0000 }, + { 0x6a06, 0x0000 }, + { 0x6a07, 0x0000 }, + { 0x6a08, 0x0000 }, + { 0x6a09, 0x0000 }, + { 0x6a0a, 0x0000 }, + { 0x6a0b, 0x0000 }, + { 0x6a0c, 0x0000 }, + { 0x6a0d, 0x0000 }, + { 0x6a0e, 0x0000 }, + { 0x6a0f, 0x0000 }, + { 0x6a10, 0x0000 }, + { 0x6a11, 0x0000 }, + { 0x6a12, 0x0000 }, + { 0x6a13, 0x0000 }, + { 0x6a14, 0x0000 }, + { 0x6a15, 0x0000 }, + { 0x6a16, 0x0000 }, + { 0x6a17, 0x0000 }, + { 0x6a18, 0x0000 }, + { 0x6a19, 0x0000 }, + { 0x6a1a, 0x0000 }, + { 0x6a1b, 0x0000 }, + { 0x6a1c, 0x0000 }, + { 0x6a1d, 0x0000 }, + { 0x6a1e, 0x0000 }, + { 0x6a1f, 0x0000 }, + { 0x6a20, 0x0000 }, + { 0x6a21, 0x0000 }, + { 0x6a22, 0x0000 }, + { 0x6a23, 0x0000 }, + { 0x6a24, 0x0000 }, + { 0x6a25, 0x0000 }, + { 0x6a26, 0x0000 }, + { 0x6a27, 0x0000 }, + { 0x6a28, 0x0000 }, + { 0x6a29, 0x0000 }, + { 0x6a2a, 0x0000 }, + { 0x6a2b, 0x0000 }, + { 0x6a2c, 0x0000 }, + { 0x6a2d, 0x0000 }, + { 0x6a2e, 0x0000 }, + { 0x6a2f, 0x0000 }, + { 0x6a30, 0x0000 }, + { 0x6a31, 0x0000 }, + { 0x6a32, 0x0000 }, + { 0x6a33, 0x0000 }, + { 0x6a34, 0x0000 }, + { 0x6a35, 0x0000 }, + { 0x6a36, 0x0000 }, + { 0x6a37, 0x0000 }, + { 0x6a38, 0x0000 }, + { 0x6a39, 0x0000 }, + { 0x6a3a, 0x0000 }, + { 0x6a3b, 0x0000 }, + { 0x6a3c, 0x0000 }, + { 0x6a3d, 0x0000 }, + { 0x6a3e, 0x0000 }, + { 0x6a3f, 0x0000 }, + { 0x6a40, 0x0000 }, + { 0x6a41, 0x0000 }, + { 0x6a42, 0x0000 }, + { 0x6a43, 0x0000 }, + { 0x6a44, 0x0000 }, + { 0x6a45, 0x0000 }, + { 0x6a46, 0x0000 }, + { 0x6a47, 0x0000 }, + { 0x6a48, 0x0000 }, + { 0x6a49, 0x0000 }, + { 0x6a4a, 0x0000 }, + { 0x6a4b, 0x0000 }, + { 0x6a4c, 0x0000 }, + { 0x6a4d, 0x0000 }, + { 0x6a4e, 0x0000 }, + { 0x6a4f, 0x0000 }, + { 0x6a50, 0x0000 }, + { 0x6a51, 0x0000 }, + { 0x6a52, 0x0000 }, + { 0x6a53, 0x0000 }, + { 0x6a54, 0x0000 }, + { 0x6a55, 0x0000 }, + { 0x6a56, 0x0000 }, + { 0x6a57, 0x0000 }, + { 0x6a58, 0x0000 }, + { 0x6a59, 0x0000 }, + { 0x6a5a, 0x0000 }, + { 0x6a5b, 0x0000 }, + { 0x6a5c, 0x0000 }, + { 0x6a5d, 0x0000 }, + { 0x6a5e, 0x0000 }, + { 0x6a5f, 0x0000 }, + { 0x6a60, 0x0000 }, + { 0x6a61, 0x0000 }, + { 0x6a62, 0x0000 }, + { 0x6a63, 0x0000 }, + { 0x6a64, 0x0000 }, + { 0x6a65, 0x0000 }, + { 0x6a66, 0x0000 }, + { 0x6a67, 0x0000 }, + { 0x6a68, 0x0000 }, + { 0x6a69, 0x0000 }, + { 0x6a6a, 0x0000 }, + { 0x6a6b, 0x0000 }, + { 0x6a6c, 0x0000 }, + { 0x6a6d, 0x0000 }, + { 0x6a6e, 0x0000 }, + { 0x6a6f, 0x0000 }, + { 0x6a70, 0x0000 }, + { 0x6a71, 0x0000 }, + { 0x6a72, 0x0000 }, + { 0x6a73, 0x0000 }, + { 0x6a74, 0x0000 }, + { 0x6a75, 0x0000 }, + { 0x6a76, 0x0000 }, + { 0x6a77, 0x0000 }, + { 0x6a78, 0x0000 }, + { 0x6a79, 0x0000 }, + { 0x6a7a, 0x0000 }, + { 0x6a7b, 0x0000 }, + { 0x6a7c, 0x0000 }, + { 0x6a7d, 0x0000 }, + { 0x6a7e, 0x0000 }, + { 0x6a7f, 0x0000 }, + { 0x6a80, 0x0000 }, + { 0x6a81, 0x0000 }, + { 0x6a82, 0x0000 }, + { 0x6a83, 0x0000 }, + { 0x6a84, 0x0000 }, + { 0x6a85, 0x0000 }, + { 0x6a86, 0x0000 }, + { 0x6a87, 0x0000 }, + { 0x6a88, 0x0000 }, + { 0x6a89, 0x0000 }, + { 0x6a8a, 0x0000 }, + { 0x6a8b, 0x0000 }, + { 0x6a8c, 0x0000 }, + { 0x6a8d, 0x0000 }, + { 0x6a8e, 0x0000 }, + { 0x6a8f, 0x0000 }, + { 0x6a90, 0x0000 }, + { 0x6a91, 0x0000 }, + { 0x6a92, 0x0000 }, + { 0x6a93, 0x0000 }, + { 0x6a94, 0x0000 }, + { 0x6a95, 0x0000 }, + { 0x6a96, 0x0000 }, + { 0x6a97, 0x0000 }, + { 0x6a98, 0x0000 }, + { 0x6a99, 0x0000 }, + { 0x6a9a, 0x0000 }, + { 0x6a9b, 0x0000 }, + { 0x6a9c, 0x0000 }, + { 0x6a9d, 0x0000 }, + { 0x6a9e, 0x0000 }, + { 0x6a9f, 0x0000 }, + { 0x6aa0, 0x0000 }, + { 0x6aa1, 0x0000 }, + { 0x6aa2, 0x0000 }, + { 0x6aa3, 0x0000 }, + { 0x6aa4, 0x0000 }, + { 0x6aa5, 0x0000 }, + { 0x6aa6, 0x0000 }, + { 0x6aa7, 0x0000 }, + { 0x6aa8, 0x0000 }, + { 0x6aa9, 0x0000 }, + { 0x6aaa, 0x0000 }, + { 0x6aab, 0x0000 }, + { 0x6aac, 0x0000 }, + { 0x6aad, 0x0000 }, + { 0x6aae, 0x0000 }, + { 0x6aaf, 0x0000 }, + { 0x6ab0, 0x0000 }, + { 0x6ab1, 0x0000 }, + { 0x6ab2, 0x0000 }, + { 0x6ab3, 0x0000 }, + { 0x6ab4, 0x0000 }, + { 0x6ab5, 0x0000 }, + { 0x6ab6, 0x0000 }, + { 0x6ab7, 0x0000 }, + { 0x6ab8, 0x0000 }, + { 0x6ab9, 0x0000 }, + { 0x6aba, 0x0000 }, + { 0x6abb, 0x0000 }, + { 0x6abc, 0x0000 }, + { 0x6abd, 0x0000 }, + { 0x6abe, 0x0000 }, + { 0x6abf, 0x0000 }, + { 0x6ac0, 0x0000 }, + { 0x6ac1, 0x0000 }, + { 0x6ac2, 0x0000 }, + { 0x6ac3, 0x0000 }, + { 0x6ac4, 0x0000 }, + { 0x6ac5, 0x0000 }, + { 0x6ac6, 0x0000 }, + { 0x6ac7, 0x0000 }, + { 0x6ac8, 0x0000 }, + { 0x6ac9, 0x0000 }, + { 0x6aca, 0x0000 }, + { 0x6acb, 0x0000 }, + { 0x6acc, 0x0000 }, + { 0x6acd, 0x0000 }, + { 0x6ace, 0x0000 }, + { 0x6acf, 0x0000 }, + { 0x6ad0, 0x0000 }, + { 0x6ad1, 0x0000 }, + { 0x6ad2, 0x0000 }, + { 0x6ad3, 0x0000 }, + { 0x6ad4, 0x0000 }, + { 0x6ad5, 0x0000 }, + { 0x6ad6, 0x0000 }, + { 0x6ad7, 0x0000 }, + { 0x6ad8, 0x0000 }, + { 0x6ad9, 0x0000 }, + { 0x6ada, 0x0000 }, + { 0x6adb, 0x0000 }, + { 0x6adc, 0x0000 }, + { 0x6add, 0x0000 }, + { 0x6ade, 0x0000 }, + { 0x6adf, 0x0000 }, + { 0x6ae0, 0x0000 }, + { 0x6ae1, 0x0000 }, + { 0x6ae2, 0x0000 }, + { 0x6ae3, 0x0000 }, + { 0x6ae4, 0x0000 }, + { 0x6ae5, 0x0000 }, + { 0x6ae6, 0x0000 }, + { 0x6ae7, 0x0000 }, + { 0x6ae8, 0x0000 }, + { 0x6ae9, 0x0000 }, + { 0x6aea, 0x0000 }, + { 0x6aeb, 0x0000 }, + { 0x6aec, 0x0000 }, + { 0x6aed, 0x0000 }, + { 0x6aee, 0x0000 }, + { 0x6aef, 0x0000 }, + { 0x6af0, 0x0000 }, + { 0x6af1, 0x0000 }, + { 0x6af2, 0x0000 }, + { 0x6af3, 0x0000 }, + { 0x6af4, 0x0000 }, + { 0x6af5, 0x0000 }, + { 0x6af6, 0x0000 }, + { 0x6af7, 0x0000 }, + { 0x6af8, 0x0000 }, + { 0x6af9, 0x0000 }, + { 0x6afa, 0x0000 }, + { 0x6afb, 0x0000 }, + { 0x6afc, 0x0000 }, + { 0x6afd, 0x0000 }, + { 0x6afe, 0x0000 }, + { 0x6aff, 0x0000 }, + { 0x6b00, 0x0000 }, + { 0x6b01, 0x0000 }, + { 0x6b02, 0x0000 }, + { 0x6b03, 0x0000 }, + { 0x6b04, 0x0000 }, + { 0x6b05, 0x0000 }, + { 0x6b06, 0x0000 }, + { 0x6b07, 0x0000 }, + { 0x6b08, 0x0000 }, + { 0x6b09, 0x0000 }, + { 0x6b0a, 0x0000 }, + { 0x6b0b, 0x0000 }, + { 0x6b0c, 0x0000 }, + { 0x6b0d, 0x0000 }, + { 0x6b0e, 0x0000 }, + { 0x6b0f, 0x0000 }, + { 0x6b10, 0x0000 }, + { 0x6b11, 0x0000 }, + { 0x6b12, 0x0000 }, + { 0x6b13, 0x0000 }, + { 0x6b14, 0x0000 }, + { 0x6b15, 0x0000 }, + { 0x6b16, 0x0000 }, + { 0x6b17, 0x0000 }, + { 0x6b18, 0x0000 }, + { 0x6b19, 0x0000 }, + { 0x6b1a, 0x0000 }, + { 0x6b1b, 0x0000 }, + { 0x6b1c, 0x0000 }, + { 0x6b1d, 0x0000 }, + { 0x6b1e, 0x0000 }, + { 0x6b1f, 0x0000 }, + { 0x6b20, 0x0000 }, + { 0x6b21, 0x0000 }, + { 0x6b22, 0x0000 }, + { 0x6b23, 0x0000 }, + { 0x6b24, 0x0000 }, + { 0x6b25, 0x0000 }, + { 0x6b26, 0x0000 }, + { 0x6b27, 0x0000 }, + { 0x6b28, 0x0000 }, + { 0x6b29, 0x0000 }, + { 0x6b2a, 0x0000 }, + { 0x6b2b, 0x0000 }, + { 0x6b2c, 0x0000 }, + { 0x6b2d, 0x0000 }, + { 0x6b2e, 0x0000 }, + { 0x6b2f, 0x0000 }, + { 0x6b30, 0x0000 }, + { 0x6b31, 0x0000 }, + { 0x6b32, 0x0000 }, + { 0x6b33, 0x0000 }, + { 0x6b34, 0x0000 }, + { 0x6b35, 0x0000 }, + { 0x6b36, 0x0000 }, + { 0x6b37, 0x0000 }, + { 0x6b38, 0x0000 }, + { 0x6b39, 0x0000 }, + { 0x6b3a, 0x0000 }, + { 0x6b3b, 0x0000 }, + { 0x6b3c, 0x0000 }, + { 0x6b3d, 0x0000 }, + { 0x6b3e, 0x0000 }, + { 0x6b3f, 0x0000 }, + { 0x6b40, 0x0000 }, + { 0x6b41, 0x0000 }, + { 0x6b42, 0x0000 }, + { 0x6b43, 0x0000 }, + { 0x6b44, 0x0000 }, + { 0x6b45, 0x0000 }, + { 0x6b46, 0x0000 }, + { 0x6b47, 0x0000 }, + { 0x6b48, 0x0000 }, + { 0x6b49, 0x0000 }, + { 0x6b4a, 0x0000 }, + { 0x6b4b, 0x0000 }, + { 0x6b4c, 0x0000 }, + { 0x6b4d, 0x0000 }, + { 0x6b4e, 0x0000 }, + { 0x6b4f, 0x0000 }, + { 0x6b50, 0x0000 }, + { 0x6b51, 0x0000 }, + { 0x6b52, 0x0000 }, + { 0x6b53, 0x0000 }, + { 0x6b54, 0x0000 }, + { 0x6b55, 0x0000 }, + { 0x6b56, 0x0000 }, + { 0x6b57, 0x0000 }, + { 0x6b58, 0x0000 }, + { 0x6b59, 0x0000 }, + { 0x6b5a, 0x0000 }, + { 0x6b5b, 0x0000 }, + { 0x6b5c, 0x0000 }, + { 0x6b5d, 0x0000 }, + { 0x6b5e, 0x0000 }, + { 0x6b5f, 0x0000 }, + { 0x6b60, 0x0000 }, + { 0x6b61, 0x0000 }, + { 0x6b62, 0x0000 }, + { 0x6b63, 0x0000 }, + { 0x6b64, 0x0000 }, + { 0x6b65, 0x0000 }, + { 0x6b66, 0x0000 }, + { 0x6b67, 0x0000 }, + { 0x6b68, 0x0000 }, + { 0x6b69, 0x0000 }, + { 0x6b6a, 0x0000 }, + { 0x6b6b, 0x0000 }, + { 0x6b6c, 0x0000 }, + { 0x6b6d, 0x0000 }, + { 0x6b6e, 0x0000 }, + { 0x6b6f, 0x0000 }, + { 0x6b70, 0x0000 }, + { 0x6b71, 0x0000 }, + { 0x6b72, 0x0000 }, + { 0x6b73, 0x0000 }, + { 0x6b74, 0x0000 }, + { 0x6b75, 0x0000 }, + { 0x6b76, 0x0000 }, + { 0x6b77, 0x0000 }, + { 0x6b78, 0x0000 }, + { 0x6b79, 0x0000 }, + { 0x6b7a, 0x0000 }, + { 0x6b7b, 0x0000 }, + { 0x6b7c, 0x0000 }, + { 0x6b7d, 0x0000 }, + { 0x6b7e, 0x0000 }, + { 0x6b7f, 0x0000 }, + { 0x6b80, 0x0000 }, + { 0x6b81, 0x0000 }, + { 0x6b82, 0x0000 }, + { 0x6b83, 0x0000 }, + { 0x6b84, 0x0000 }, + { 0x6b85, 0x0000 }, + { 0x6b86, 0x0000 }, + { 0x6b87, 0x0000 }, + { 0x6b88, 0x0000 }, + { 0x6b89, 0x0000 }, + { 0x6b8a, 0x0000 }, + { 0x6b8b, 0x0000 }, + { 0x6b8c, 0x0000 }, + { 0x6b8d, 0x0000 }, + { 0x6b8e, 0x0000 }, + { 0x6b8f, 0x0000 }, + { 0x6b90, 0x0000 }, + { 0x6b91, 0x0000 }, + { 0x6b92, 0x0000 }, + { 0x6b93, 0x0000 }, + { 0x6b94, 0x0000 }, + { 0x6b95, 0x0000 }, + { 0x6b96, 0x0000 }, + { 0x6b97, 0x0000 }, + { 0x6b98, 0x0000 }, + { 0x6b99, 0x0000 }, + { 0x6b9a, 0x0000 }, + { 0x6b9b, 0x0000 }, + { 0x6b9c, 0x0000 }, + { 0x6b9d, 0x0000 }, + { 0x6b9e, 0x0000 }, + { 0x6b9f, 0x0000 }, + { 0x6ba0, 0x0000 }, + { 0x6ba1, 0x0000 }, + { 0x6ba2, 0x0000 }, + { 0x6ba3, 0x0000 }, + { 0x6ba4, 0x0000 }, + { 0x6ba5, 0x0000 }, + { 0x6ba6, 0x0000 }, + { 0x6ba7, 0x0000 }, + { 0x6ba8, 0x0000 }, + { 0x6ba9, 0x0000 }, + { 0x6baa, 0x0000 }, + { 0x6bab, 0x0000 }, + { 0x6bac, 0x0000 }, + { 0x6bad, 0x0000 }, + { 0x6bae, 0x0000 }, + { 0x6baf, 0x0000 }, + { 0x6bb0, 0x0000 }, + { 0x6bb1, 0x0000 }, + { 0x6bb2, 0x0000 }, + { 0x6bb3, 0x0000 }, + { 0x6bb4, 0x0000 }, + { 0x6bb5, 0x0000 }, + { 0x6bb6, 0x0000 }, + { 0x6bb7, 0x0000 }, + { 0x6bb8, 0x0000 }, + { 0x6bb9, 0x0000 }, + { 0x6bba, 0x0000 }, + { 0x6bbb, 0x0000 }, + { 0x6bbc, 0x0000 }, + { 0x6bbd, 0x0000 }, + { 0x6bbe, 0x0000 }, + { 0x6bbf, 0x0000 }, + { 0x6bc0, 0x0000 }, + { 0x6bc1, 0x0000 }, + { 0x6bc2, 0x0000 }, + { 0x6bc3, 0x0000 }, + { 0x6bc4, 0x0000 }, + { 0x6bc5, 0x0000 }, + { 0x6bc6, 0x0000 }, + { 0x6bc7, 0x0000 }, + { 0x6bc8, 0x0000 }, + { 0x6bc9, 0x0000 }, + { 0x6bca, 0x0000 }, + { 0x6bcb, 0x0000 }, + { 0x6bcc, 0x0000 }, + { 0x6bcd, 0x0000 }, + { 0x6bce, 0x0000 }, + { 0x6bcf, 0x0000 }, + { 0x6bd0, 0x0000 }, + { 0x6bd1, 0x0000 }, + { 0x6bd2, 0x0000 }, + { 0x6bd3, 0x0000 }, + { 0x6bd4, 0x0000 }, + { 0x6bd5, 0x0000 }, + { 0x6bd6, 0x0000 }, + { 0x6bd7, 0x0000 }, + { 0x6bd8, 0x0000 }, + { 0x6bd9, 0x0000 }, + { 0x6bda, 0x0000 }, + { 0x6bdb, 0x0000 }, + { 0x6bdc, 0x0000 }, + { 0x6bdd, 0x0000 }, + { 0x6bde, 0x0000 }, + { 0x6bdf, 0x0000 }, + { 0x6be0, 0x0000 }, + { 0x6be1, 0x0000 }, + { 0x6be2, 0x0000 }, + { 0x6be3, 0x0000 }, + { 0x6be4, 0x0000 }, + { 0x6be5, 0x0000 }, + { 0x6be6, 0x0000 }, + { 0x6be7, 0x0000 }, + { 0x6be8, 0x0000 }, + { 0x6be9, 0x0000 }, + { 0x6bea, 0x0000 }, + { 0x6beb, 0x0000 }, + { 0x6bec, 0x0000 }, + { 0x6bed, 0x0000 }, + { 0x6bee, 0x0000 }, + { 0x6bef, 0x0000 }, + { 0x6bf0, 0x0000 }, + { 0x6bf1, 0x0000 }, + { 0x6bf2, 0x0000 }, + { 0x6bf3, 0x0000 }, + { 0x6bf4, 0x0000 }, + { 0x6bf5, 0x0000 }, + { 0x6bf6, 0x0000 }, + { 0x6bf7, 0x0000 }, + { 0x6bf8, 0x0000 }, + { 0x6bf9, 0x0000 }, + { 0x6bfa, 0x0000 }, + { 0x6bfb, 0x0000 }, + { 0x6bfc, 0x0000 }, + { 0x6bfd, 0x0000 }, + { 0x6bfe, 0x0000 }, + { 0x6bff, 0x0000 }, + { 0x6c00, 0x0000 }, + { 0x6c01, 0x0000 }, + { 0x6c02, 0x0000 }, + { 0x6c03, 0x0000 }, + { 0x6c04, 0x0000 }, + { 0x6c05, 0x0000 }, + { 0x6c06, 0x0000 }, + { 0x6c07, 0x0000 }, + { 0x6c08, 0x0000 }, + { 0x6c09, 0x0000 }, + { 0x6c0a, 0x0000 }, + { 0x6c0b, 0x0000 }, + { 0x6c0c, 0x0000 }, + { 0x6c0d, 0x0000 }, + { 0x6c0e, 0x0000 }, + { 0x6c0f, 0x0000 }, + { 0x6c10, 0x0000 }, + { 0x6c11, 0x0000 }, + { 0x6c12, 0x0000 }, + { 0x6c13, 0x0000 }, + { 0x6c14, 0x0000 }, + { 0x6c15, 0x0000 }, + { 0x6c16, 0x0000 }, + { 0x6c17, 0x0000 }, + { 0x6c18, 0x0000 }, + { 0x6c19, 0x0000 }, + { 0x6c1a, 0x0000 }, + { 0x6c1b, 0x0000 }, + { 0x6c1c, 0x0000 }, + { 0x6c1d, 0x0000 }, + { 0x6c1e, 0x0000 }, + { 0x6c1f, 0x0000 }, + { 0x6c20, 0x0000 }, + { 0x6c21, 0x0000 }, + { 0x6c22, 0x0000 }, + { 0x6c23, 0x0000 }, + { 0x6c24, 0x0000 }, + { 0x6c25, 0x0000 }, + { 0x6c26, 0x0000 }, + { 0x6c27, 0x0000 }, + { 0x6c28, 0x0000 }, + { 0x6c29, 0x0000 }, + { 0x6c2a, 0x0000 }, + { 0x6c2b, 0x0000 }, + { 0x6c2c, 0x0000 }, + { 0x6c2d, 0x0000 }, + { 0x6c2e, 0x0000 }, + { 0x6c2f, 0x0000 }, + { 0x6c30, 0x0000 }, + { 0x6c31, 0x0000 }, + { 0x6c32, 0x0000 }, + { 0x6c33, 0x0000 }, + { 0x6c34, 0x0000 }, + { 0x6c35, 0x0000 }, + { 0x6c36, 0x0000 }, + { 0x6c37, 0x0000 }, + { 0x6c38, 0x0000 }, + { 0x6c39, 0x0000 }, + { 0x6c3a, 0x0000 }, + { 0x6c3b, 0x0000 }, + { 0x6c3c, 0x0000 }, + { 0x6c3d, 0x0000 }, + { 0x6c3e, 0x0000 }, + { 0x6c3f, 0x0000 }, + { 0x6c40, 0x0000 }, + { 0x6c41, 0x0000 }, + { 0x6c42, 0x0000 }, + { 0x6c43, 0x0000 }, + { 0x6c44, 0x0000 }, + { 0x6c45, 0x0000 }, + { 0x6c46, 0x0000 }, + { 0x6c47, 0x0000 }, + { 0x6c48, 0x0000 }, + { 0x6c49, 0x0000 }, + { 0x6c4a, 0x0000 }, + { 0x6c4b, 0x0000 }, + { 0x6c4c, 0x0000 }, + { 0x6c4d, 0x0000 }, + { 0x6c4e, 0x0000 }, + { 0x6c4f, 0x0000 }, + { 0x6c50, 0x0000 }, + { 0x6c51, 0x0000 }, + { 0x6c52, 0x0000 }, + { 0x6c53, 0x0000 }, + { 0x6c54, 0x0000 }, + { 0x6c55, 0x0000 }, + { 0x6c56, 0x0000 }, + { 0x6c57, 0x0000 }, + { 0x6c58, 0x0000 }, + { 0x6c59, 0x0000 }, + { 0x6c5a, 0x0000 }, + { 0x6c5b, 0x0000 }, + { 0x6c5c, 0x0000 }, + { 0x6c5d, 0x0000 }, + { 0x6c5e, 0x0000 }, + { 0x6c5f, 0x0000 }, + { 0x6c60, 0x0000 }, + { 0x6c61, 0x0000 }, + { 0x6c62, 0x0000 }, + { 0x6c63, 0x0000 }, + { 0x6c64, 0x0000 }, + { 0x6c65, 0x0000 }, + { 0x6c66, 0x0000 }, + { 0x6c67, 0x0000 }, + { 0x6c68, 0x0000 }, + { 0x6c69, 0x0000 }, + { 0x6c6a, 0x0000 }, + { 0x6c6b, 0x0000 }, + { 0x6c6c, 0x0000 }, + { 0x6c6d, 0x0000 }, + { 0x6c6e, 0x0000 }, + { 0x6c6f, 0x0000 }, + { 0x6c70, 0x0000 }, + { 0x6c71, 0x0000 }, + { 0x6c72, 0x0000 }, + { 0x6c73, 0x0000 }, + { 0x6c74, 0x0000 }, + { 0x6c75, 0x0000 }, + { 0x6c76, 0x0000 }, + { 0x6c77, 0x0000 }, + { 0x6c78, 0x0000 }, + { 0x6c79, 0x0000 }, + { 0x6c7a, 0x0000 }, + { 0x6c7b, 0x0000 }, + { 0x6c7c, 0x0000 }, + { 0x6c7d, 0x0000 }, + { 0x6c7e, 0x0000 }, + { 0x6c7f, 0x0000 }, + { 0x6c80, 0x0000 }, + { 0x6c81, 0x0000 }, + { 0x6c82, 0x0000 }, + { 0x6c83, 0x0000 }, + { 0x6c84, 0x0000 }, + { 0x6c85, 0x0000 }, + { 0x6c86, 0x0000 }, + { 0x6c87, 0x0000 }, + { 0x6c88, 0x0000 }, + { 0x6c89, 0x0000 }, + { 0x6c8a, 0x0000 }, + { 0x6c8b, 0x0000 }, + { 0x6c8c, 0x0000 }, + { 0x6c8d, 0x0000 }, + { 0x6c8e, 0x0000 }, + { 0x6c8f, 0x0000 }, + { 0x6c90, 0x0000 }, + { 0x6c91, 0x0000 }, + { 0x6c92, 0x0000 }, + { 0x6c93, 0x0000 }, + { 0x6c94, 0x0000 }, + { 0x6c95, 0x0000 }, + { 0x6c96, 0x0000 }, + { 0x6c97, 0x0000 }, + { 0x6c98, 0x0000 }, + { 0x6c99, 0x0000 }, + { 0x6c9a, 0x0000 }, + { 0x6c9b, 0x0000 }, + { 0x6c9c, 0x0000 }, + { 0x6c9d, 0x0000 }, + { 0x6c9e, 0x0000 }, + { 0x6c9f, 0x0000 }, + { 0x6ca0, 0x0000 }, + { 0x6ca1, 0x0000 }, + { 0x6ca2, 0x0000 }, + { 0x6ca3, 0x0000 }, + { 0x6ca4, 0x0000 }, + { 0x6ca5, 0x0000 }, + { 0x6ca6, 0x0000 }, + { 0x6ca7, 0x0000 }, + { 0x6ca8, 0x0000 }, + { 0x6ca9, 0x0000 }, + { 0x6caa, 0x0000 }, + { 0x6cab, 0x0000 }, + { 0x6cac, 0x0000 }, + { 0x6cad, 0x0000 }, + { 0x6cae, 0x0000 }, + { 0x6caf, 0x0000 }, + { 0x6cb0, 0x0000 }, + { 0x6cb1, 0x0000 }, + { 0x6cb2, 0x0000 }, + { 0x6cb3, 0x0000 }, + { 0x6cb4, 0x0000 }, + { 0x6cb5, 0x0000 }, + { 0x6cb6, 0x0000 }, + { 0x6cb7, 0x0000 }, + { 0x6cb8, 0x0000 }, + { 0x6cb9, 0x0000 }, + { 0x6cba, 0x0000 }, + { 0x6cbb, 0x0000 }, + { 0x6cbc, 0x0000 }, + { 0x6cbd, 0x0000 }, + { 0x6cbe, 0x0000 }, + { 0x6cbf, 0x0000 }, + { 0x6cc0, 0x0000 }, + { 0x6cc1, 0x0000 }, + { 0x6cc2, 0x0000 }, + { 0x6cc3, 0x0000 }, + { 0x6cc4, 0x0000 }, + { 0x6cc5, 0x0000 }, + { 0x6cc6, 0x0000 }, + { 0x6cc7, 0x0000 }, + { 0x6cc8, 0x0000 }, + { 0x6cc9, 0x0000 }, + { 0x6cca, 0x0000 }, + { 0x6ccb, 0x0000 }, + { 0x6ccc, 0x0000 }, + { 0x6ccd, 0x0000 }, + { 0x6cce, 0x0000 }, + { 0x6ccf, 0x0000 }, + { 0x6cd0, 0x0000 }, + { 0x6cd1, 0x0000 }, + { 0x6cd2, 0x0000 }, + { 0x6cd3, 0x0000 }, + { 0x6cd4, 0x0000 }, + { 0x6cd5, 0x0000 }, + { 0x6cd6, 0x0000 }, + { 0x6cd7, 0x0000 }, + { 0x6cd8, 0x0000 }, + { 0x6cd9, 0x0000 }, + { 0x6cda, 0x0000 }, + { 0x6cdb, 0x0000 }, + { 0x6cdc, 0x0000 }, + { 0x6cdd, 0x0000 }, + { 0x6cde, 0x0000 }, + { 0x6cdf, 0x0000 }, + { 0x6ce0, 0x0000 }, + { 0x6ce1, 0x0000 }, + { 0x6ce2, 0x0000 }, + { 0x6ce3, 0x0000 }, + { 0x6ce4, 0x0000 }, + { 0x6ce5, 0x0000 }, + { 0x6ce6, 0x0000 }, + { 0x6ce7, 0x0000 }, + { 0x6ce8, 0x0000 }, + { 0x6ce9, 0x0000 }, + { 0x6cea, 0x0000 }, + { 0x6ceb, 0x0000 }, + { 0x6cec, 0x0000 }, + { 0x6ced, 0x0000 }, + { 0x6cee, 0x0000 }, + { 0x6cef, 0x0000 }, + { 0x6cf0, 0x0000 }, + { 0x6cf1, 0x0000 }, + { 0x6cf2, 0x0000 }, + { 0x6cf3, 0x0000 }, + { 0x6cf4, 0x0000 }, + { 0x6cf5, 0x0000 }, + { 0x6cf6, 0x0000 }, + { 0x6cf7, 0x0000 }, + { 0x6cf8, 0x0000 }, + { 0x6cf9, 0x0000 }, + { 0x6cfa, 0x0000 }, + { 0x6cfb, 0x0000 }, + { 0x6cfc, 0x0000 }, + { 0x6cfd, 0x0000 }, + { 0x6cfe, 0x0000 }, + { 0x6cff, 0x0000 }, + { 0x6d00, 0x0000 }, + { 0x6d01, 0x0000 }, + { 0x6d02, 0x0000 }, + { 0x6d03, 0x0000 }, + { 0x6d04, 0x0000 }, + { 0x6d05, 0x0000 }, + { 0x6d06, 0x0000 }, + { 0x6d07, 0x0000 }, + { 0x6d08, 0x0000 }, + { 0x6d09, 0x0000 }, + { 0x6d0a, 0x0000 }, + { 0x6d0b, 0x0000 }, + { 0x6d0c, 0x0000 }, + { 0x6d0d, 0x0000 }, + { 0x6d0e, 0x0000 }, + { 0x6d0f, 0x0000 }, + { 0x6d10, 0x0000 }, + { 0x6d11, 0x0000 }, + { 0x6d12, 0x0000 }, + { 0x6d13, 0x0000 }, + { 0x6d14, 0x0000 }, + { 0x6d15, 0x0000 }, + { 0x6d16, 0x0000 }, + { 0x6d17, 0x0000 }, + { 0x6d18, 0x0000 }, + { 0x6d19, 0x0000 }, + { 0x6d1a, 0x0000 }, + { 0x6d1b, 0x0000 }, + { 0x6d1c, 0x0000 }, + { 0x6d1d, 0x0000 }, + { 0x6d1e, 0x0000 }, + { 0x6d1f, 0x0000 }, + { 0x6d20, 0x0000 }, + { 0x6d21, 0x0000 }, + { 0x6d22, 0x0000 }, + { 0x6d23, 0x0000 }, + { 0x6d24, 0x0000 }, + { 0x6d25, 0x0000 }, + { 0x6d26, 0x0000 }, + { 0x6d27, 0x0000 }, + { 0x6d28, 0x0000 }, + { 0x6d29, 0x0000 }, + { 0x6d2a, 0x0000 }, + { 0x6d2b, 0x0000 }, + { 0x6d2c, 0x0000 }, + { 0x6d2d, 0x0000 }, + { 0x6d2e, 0x0000 }, + { 0x6d2f, 0x0000 }, + { 0x6d30, 0x0000 }, + { 0x6d31, 0x0000 }, + { 0x6d32, 0x0000 }, + { 0x6d33, 0x0000 }, + { 0x6d34, 0x0000 }, + { 0x6d35, 0x0000 }, + { 0x6d36, 0x0000 }, + { 0x6d37, 0x0000 }, + { 0x6d38, 0x0000 }, + { 0x6d39, 0x0000 }, + { 0x6d3a, 0x0000 }, + { 0x6d3b, 0x0000 }, + { 0x6d3c, 0x0000 }, + { 0x6d3d, 0x0000 }, + { 0x6d3e, 0x0000 }, + { 0x6d3f, 0x0000 }, + { 0x6d40, 0x0000 }, + { 0x6d41, 0x0000 }, + { 0x6d42, 0x0000 }, + { 0x6d43, 0x0000 }, + { 0x6d44, 0x0000 }, + { 0x6d45, 0x0000 }, + { 0x6d46, 0x0000 }, + { 0x6d47, 0x0000 }, + { 0x6d48, 0x0000 }, + { 0x6d49, 0x0000 }, + { 0x6d4a, 0x0000 }, + { 0x6d4b, 0x0000 }, + { 0x6d4c, 0x0000 }, + { 0x6d4d, 0x0000 }, + { 0x6d4e, 0x0000 }, + { 0x6d4f, 0x0000 }, + { 0x6d50, 0x0000 }, + { 0x6d51, 0x0000 }, + { 0x6d52, 0x0000 }, + { 0x6d53, 0x0000 }, + { 0x6d54, 0x0000 }, + { 0x6d55, 0x0000 }, + { 0x6d56, 0x0000 }, + { 0x6d57, 0x0000 }, + { 0x6d58, 0x0000 }, + { 0x6d59, 0x0000 }, + { 0x6d5a, 0x0000 }, + { 0x6d5b, 0x0000 }, + { 0x6d5c, 0x0000 }, + { 0x6d5d, 0x0000 }, + { 0x6d5e, 0x0000 }, + { 0x6d5f, 0x0000 }, + { 0x6d60, 0x0000 }, + { 0x6d61, 0x0000 }, + { 0x6d62, 0x0000 }, + { 0x6d63, 0x0000 }, + { 0x6d64, 0x0000 }, + { 0x6d65, 0x0000 }, + { 0x6d66, 0x0000 }, + { 0x6d67, 0x0000 }, + { 0x6d68, 0x0000 }, + { 0x6d69, 0x0000 }, + { 0x6d6a, 0x0000 }, + { 0x6d6b, 0x0000 }, + { 0x6d6c, 0x0000 }, + { 0x6d6d, 0x0000 }, + { 0x6d6e, 0x0000 }, + { 0x6d6f, 0x0000 }, + { 0x6d70, 0x0000 }, + { 0x6d71, 0x0000 }, + { 0x6d72, 0x0000 }, + { 0x6d73, 0x0000 }, + { 0x6d74, 0x0000 }, + { 0x6d75, 0x0000 }, + { 0x6d76, 0x0000 }, + { 0x6d77, 0x0000 }, + { 0x6d78, 0x0000 }, + { 0x6d79, 0x0000 }, + { 0x6d7a, 0x0000 }, + { 0x6d7b, 0x0000 }, + { 0x6d7c, 0x0000 }, + { 0x6d7d, 0x0000 }, + { 0x6d7e, 0x0000 }, + { 0x6d7f, 0x0000 }, + { 0x6d80, 0x0000 }, + { 0x6d81, 0x0000 }, + { 0x6d82, 0x0000 }, + { 0x6d83, 0x0000 }, + { 0x6d84, 0x0000 }, + { 0x6d85, 0x0000 }, + { 0x6d86, 0x0000 }, + { 0x6d87, 0x0000 }, + { 0x6d88, 0x0000 }, + { 0x6d89, 0x0000 }, + { 0x6d8a, 0x0000 }, + { 0x6d8b, 0x0000 }, + { 0x6d8c, 0x0000 }, + { 0x6d8d, 0x0000 }, + { 0x6d8e, 0x0000 }, + { 0x6d8f, 0x0000 }, + { 0x6d90, 0x0000 }, + { 0x6d91, 0x0000 }, + { 0x6d92, 0x0000 }, + { 0x6d93, 0x0000 }, + { 0x6d94, 0x0000 }, + { 0x6d95, 0x0000 }, + { 0x6d96, 0x0000 }, + { 0x6d97, 0x0000 }, + { 0x6d98, 0x0000 }, + { 0x6d99, 0x0000 }, + { 0x6d9a, 0x0000 }, + { 0x6d9b, 0x0000 }, + { 0x6d9c, 0x0000 }, + { 0x6d9d, 0x0000 }, + { 0x6d9e, 0x0000 }, + { 0x6d9f, 0x0000 }, + { 0x6da0, 0x0000 }, + { 0x6da1, 0x0000 }, + { 0x6da2, 0x0000 }, + { 0x6da3, 0x0000 }, + { 0x6da4, 0x0000 }, + { 0x6da5, 0x0000 }, + { 0x6da6, 0x0000 }, + { 0x6da7, 0x0000 }, + { 0x6da8, 0x0000 }, + { 0x6da9, 0x0000 }, + { 0x6daa, 0x0000 }, + { 0x6dab, 0x0000 }, + { 0x6dac, 0x0000 }, + { 0x6dad, 0x0000 }, + { 0x6dae, 0x0000 }, + { 0x6daf, 0x0000 }, + { 0x6db0, 0x0000 }, + { 0x6db1, 0x0000 }, + { 0x6db2, 0x0000 }, + { 0x6db3, 0x0000 }, + { 0x6db4, 0x0000 }, + { 0x6db5, 0x0000 }, + { 0x6db6, 0x0000 }, + { 0x6db7, 0x0000 }, + { 0x6db8, 0x0000 }, + { 0x6db9, 0x0000 }, + { 0x6dba, 0x0000 }, + { 0x6dbb, 0x0000 }, + { 0x6dbc, 0x0000 }, + { 0x6dbd, 0x0000 }, + { 0x6dbe, 0x0000 }, + { 0x6dbf, 0x0000 }, + { 0x6dc0, 0x0000 }, + { 0x6dc1, 0x0000 }, + { 0x6dc2, 0x0000 }, + { 0x6dc3, 0x0000 }, + { 0x6dc4, 0x0000 }, + { 0x6dc5, 0x0000 }, + { 0x6dc6, 0x0000 }, + { 0x6dc7, 0x0000 }, + { 0x6dc8, 0x0000 }, + { 0x6dc9, 0x0000 }, + { 0x6dca, 0x0000 }, + { 0x6dcb, 0x0000 }, + { 0x6dcc, 0x0000 }, + { 0x6dcd, 0x0000 }, + { 0x6dce, 0x0000 }, + { 0x6dcf, 0x0000 }, + { 0x6dd0, 0x0000 }, + { 0x6dd1, 0x0000 }, + { 0x6dd2, 0x0000 }, + { 0x6dd3, 0x0000 }, + { 0x6dd4, 0x0000 }, + { 0x6dd5, 0x0000 }, + { 0x6dd6, 0x0000 }, + { 0x6dd7, 0x0000 }, + { 0x6dd8, 0x0000 }, + { 0x6dd9, 0x0000 }, + { 0x6dda, 0x0000 }, + { 0x6ddb, 0x0000 }, + { 0x6ddc, 0x0000 }, + { 0x6ddd, 0x0000 }, + { 0x6dde, 0x0000 }, + { 0x6ddf, 0x0000 }, + { 0x6de0, 0x0000 }, + { 0x6de1, 0x0000 }, + { 0x6de2, 0x0000 }, + { 0x6de3, 0x0000 }, + { 0x6de4, 0x0000 }, + { 0x6de5, 0x0000 }, + { 0x6de6, 0x0000 }, + { 0x6de7, 0x0000 }, + { 0x6de8, 0x0000 }, + { 0x6de9, 0x0000 }, + { 0x6dea, 0x0000 }, + { 0x6deb, 0x0000 }, + { 0x6dec, 0x0000 }, + { 0x6ded, 0x0000 }, + { 0x6dee, 0x0000 }, + { 0x6def, 0x0000 }, + { 0x6df0, 0x0000 }, + { 0x6df1, 0x0000 }, + { 0x6df2, 0x0000 }, + { 0x6df3, 0x0000 }, + { 0x6df4, 0x0000 }, + { 0x6df5, 0x0000 }, + { 0x6df6, 0x0000 }, + { 0x6df7, 0x0000 }, + { 0x6df8, 0x0000 }, + { 0x6df9, 0x0000 }, + { 0x6dfa, 0x0000 }, + { 0x6dfb, 0x0000 }, + { 0x6dfc, 0x0000 }, + { 0x6dfd, 0x0000 }, + { 0x6dfe, 0x0000 }, + { 0x6dff, 0x0000 }, + { 0x6e00, 0x0000 }, + { 0x6e01, 0x0000 }, + { 0x6e02, 0x0000 }, + { 0x6e03, 0x0000 }, + { 0x6e04, 0x0000 }, + { 0x6e05, 0x0000 }, + { 0x6e06, 0x0000 }, + { 0x6e07, 0x0000 }, + { 0x6e08, 0x0000 }, + { 0x6e09, 0x0000 }, + { 0x6e0a, 0x0000 }, + { 0x6e0b, 0x0000 }, + { 0x6e0c, 0x0000 }, + { 0x6e0d, 0x0000 }, + { 0x6e0e, 0x0000 }, + { 0x6e0f, 0x0000 }, + { 0x6e10, 0x0000 }, + { 0x6e11, 0x0000 }, + { 0x6e12, 0x0000 }, + { 0x6e13, 0x0000 }, + { 0x6e14, 0x0000 }, + { 0x6e15, 0x0000 }, + { 0x6e16, 0x0000 }, + { 0x6e17, 0x0000 }, + { 0x6e18, 0x0000 }, + { 0x6e19, 0x0000 }, + { 0x6e1a, 0x0000 }, + { 0x6e1b, 0x0000 }, + { 0x6e1c, 0x0000 }, + { 0x6e1d, 0x0000 }, + { 0x6e1e, 0x0000 }, + { 0x6e1f, 0x0000 }, + { 0x6e20, 0x0000 }, + { 0x6e21, 0x0000 }, + { 0x6e22, 0x0000 }, + { 0x6e23, 0x0000 }, + { 0x6e24, 0x0000 }, + { 0x6e25, 0x0000 }, + { 0x6e26, 0x0000 }, + { 0x6e27, 0x0000 }, + { 0x6e28, 0x0000 }, + { 0x6e29, 0x0000 }, + { 0x6e2a, 0x0000 }, + { 0x6e2b, 0x0000 }, + { 0x6e2c, 0x0000 }, + { 0x6e2d, 0x0000 }, + { 0x6e2e, 0x0000 }, + { 0x6e2f, 0x0000 }, + { 0x6e30, 0x0000 }, + { 0x6e31, 0x0000 }, + { 0x6e32, 0x0000 }, + { 0x6e33, 0x0000 }, + { 0x6e34, 0x0000 }, + { 0x6e35, 0x0000 }, + { 0x6e36, 0x0000 }, + { 0x6e37, 0x0000 }, + { 0x6e38, 0x0000 }, + { 0x6e39, 0x0000 }, + { 0x6e3a, 0x0000 }, + { 0x6e3b, 0x0000 }, + { 0x6e3c, 0x0000 }, + { 0x6e3d, 0x0000 }, + { 0x6e3e, 0x0000 }, + { 0x6e3f, 0x0000 }, + { 0x6e40, 0x0000 }, + { 0x6e41, 0x0000 }, + { 0x6e42, 0x0000 }, + { 0x6e43, 0x0000 }, + { 0x6e44, 0x0000 }, + { 0x6e45, 0x0000 }, + { 0x6e46, 0x0000 }, + { 0x6e47, 0x0000 }, + { 0x6e48, 0x0000 }, + { 0x6e49, 0x0000 }, + { 0x6e4a, 0x0000 }, + { 0x6e4b, 0x0000 }, + { 0x6e4c, 0x0000 }, + { 0x6e4d, 0x0000 }, + { 0x6e4e, 0x0000 }, + { 0x6e4f, 0x0000 }, + { 0x6e50, 0x0000 }, + { 0x6e51, 0x0000 }, + { 0x6e52, 0x0000 }, + { 0x6e53, 0x0000 }, + { 0x6e54, 0x0000 }, + { 0x6e55, 0x0000 }, + { 0x6e56, 0x0000 }, + { 0x6e57, 0x0000 }, + { 0x6e58, 0x0000 }, + { 0x6e59, 0x0000 }, + { 0x6e5a, 0x0000 }, + { 0x6e5b, 0x0000 }, + { 0x6e5c, 0x0000 }, + { 0x6e5d, 0x0000 }, + { 0x6e5e, 0x0000 }, + { 0x6e5f, 0x0000 }, + { 0x6e60, 0x0000 }, + { 0x6e61, 0x0000 }, + { 0x6e62, 0x0000 }, + { 0x6e63, 0x0000 }, + { 0x6e64, 0x0000 }, + { 0x6e65, 0x0000 }, + { 0x6e66, 0x0000 }, + { 0x6e67, 0x0000 }, + { 0x6e68, 0x0000 }, + { 0x6e69, 0x0000 }, + { 0x6e6a, 0x0000 }, + { 0x6e6b, 0x0000 }, + { 0x6e6c, 0x0000 }, + { 0x6e6d, 0x0000 }, + { 0x6e6e, 0x0000 }, + { 0x6e6f, 0x0000 }, + { 0x6e70, 0x0000 }, + { 0x6e71, 0x0000 }, + { 0x6e72, 0x0000 }, + { 0x6e73, 0x0000 }, + { 0x6e74, 0x0000 }, + { 0x6e75, 0x0000 }, + { 0x6e76, 0x0000 }, + { 0x6e77, 0x0000 }, + { 0x6e78, 0x0000 }, + { 0x6e79, 0x0000 }, + { 0x6e7a, 0x0000 }, + { 0x6e7b, 0x0000 }, + { 0x6e7c, 0x0000 }, + { 0x6e7d, 0x0000 }, + { 0x6e7e, 0x0000 }, + { 0x6e7f, 0x0000 }, + { 0x6e80, 0x0000 }, + { 0x6e81, 0x0000 }, + { 0x6e82, 0x0000 }, + { 0x6e83, 0x0000 }, + { 0x6e84, 0x0000 }, + { 0x6e85, 0x0000 }, + { 0x6e86, 0x0000 }, + { 0x6e87, 0x0000 }, + { 0x6e88, 0x0000 }, + { 0x6e89, 0x0000 }, + { 0x6e8a, 0x0000 }, + { 0x6e8b, 0x0000 }, + { 0x6e8c, 0x0000 }, + { 0x6e8d, 0x0000 }, + { 0x6e8e, 0x0000 }, + { 0x6e8f, 0x0000 }, + { 0x6e90, 0x0000 }, + { 0x6e91, 0x0000 }, + { 0x6e92, 0x0000 }, + { 0x6e93, 0x0000 }, + { 0x6e94, 0x0000 }, + { 0x6e95, 0x0000 }, + { 0x6e96, 0x0000 }, + { 0x6e97, 0x0000 }, + { 0x6e98, 0x0000 }, + { 0x6e99, 0x0000 }, + { 0x6e9a, 0x0000 }, + { 0x6e9b, 0x0000 }, + { 0x6e9c, 0x0000 }, + { 0x6e9d, 0x0000 }, + { 0x6e9e, 0x0000 }, + { 0x6e9f, 0x0000 }, + { 0x6ea0, 0x0000 }, + { 0x6ea1, 0x0000 }, + { 0x6ea2, 0x0000 }, + { 0x6ea3, 0x0000 }, + { 0x6ea4, 0x0000 }, + { 0x6ea5, 0x0000 }, + { 0x6ea6, 0x0000 }, + { 0x6ea7, 0x0000 }, + { 0x6ea8, 0x0000 }, + { 0x6ea9, 0x0000 }, + { 0x6eaa, 0x0000 }, + { 0x6eab, 0x0000 }, + { 0x6eac, 0x0000 }, + { 0x6ead, 0x0000 }, + { 0x6eae, 0x0000 }, + { 0x6eaf, 0x0000 }, + { 0x6eb0, 0x0000 }, + { 0x6eb1, 0x0000 }, + { 0x6eb2, 0x0000 }, + { 0x6eb3, 0x0000 }, + { 0x6eb4, 0x0000 }, + { 0x6eb5, 0x0000 }, + { 0x6eb6, 0x0000 }, + { 0x6eb7, 0x0000 }, + { 0x6eb8, 0x0000 }, + { 0x6eb9, 0x0000 }, + { 0x6eba, 0x0000 }, + { 0x6ebb, 0x0000 }, + { 0x6ebc, 0x0000 }, + { 0x6ebd, 0x0000 }, + { 0x6ebe, 0x0000 }, + { 0x6ebf, 0x0000 }, + { 0x6ec0, 0x0000 }, + { 0x6ec1, 0x0000 }, + { 0x6ec2, 0x0000 }, + { 0x6ec3, 0x0000 }, + { 0x6ec4, 0x0000 }, + { 0x6ec5, 0x0000 }, + { 0x6ec6, 0x0000 }, + { 0x6ec7, 0x0000 }, + { 0x6ec8, 0x0000 }, + { 0x6ec9, 0x0000 }, + { 0x6eca, 0x0000 }, + { 0x6ecb, 0x0000 }, + { 0x6ecc, 0x0000 }, + { 0x6ecd, 0x0000 }, + { 0x6ece, 0x0000 }, + { 0x6ecf, 0x0000 }, + { 0x6ed0, 0x0000 }, + { 0x6ed1, 0x0000 }, + { 0x6ed2, 0x0000 }, + { 0x6ed3, 0x0000 }, + { 0x6ed4, 0x0000 }, + { 0x6ed5, 0x0000 }, + { 0x6ed6, 0x0000 }, + { 0x6ed7, 0x0000 }, + { 0x6ed8, 0x0000 }, + { 0x6ed9, 0x0000 }, + { 0x6eda, 0x0000 }, + { 0x6edb, 0x0000 }, + { 0x6edc, 0x0000 }, + { 0x6edd, 0x0000 }, + { 0x6ede, 0x0000 }, + { 0x6edf, 0x0000 }, + { 0x6ee0, 0x0000 }, + { 0x6ee1, 0x0000 }, + { 0x6ee2, 0x0000 }, + { 0x6ee3, 0x0000 }, + { 0x6ee4, 0x0000 }, + { 0x6ee5, 0x0000 }, + { 0x6ee6, 0x0000 }, + { 0x6ee7, 0x0000 }, + { 0x6ee8, 0x0000 }, + { 0x6ee9, 0x0000 }, + { 0x6eea, 0x0000 }, + { 0x6eeb, 0x0000 }, + { 0x6eec, 0x0000 }, + { 0x6eed, 0x0000 }, + { 0x6eee, 0x0000 }, + { 0x6eef, 0x0000 }, + { 0x6ef0, 0x0000 }, + { 0x6ef1, 0x0000 }, + { 0x6ef2, 0x0000 }, + { 0x6ef3, 0x0000 }, + { 0x6ef4, 0x0000 }, + { 0x6ef5, 0x0000 }, + { 0x6ef6, 0x0000 }, + { 0x6ef7, 0x0000 }, + { 0x6ef8, 0x0000 }, + { 0x6ef9, 0x0000 }, + { 0x6efa, 0x0000 }, + { 0x6efb, 0x0000 }, + { 0x6efc, 0x0000 }, + { 0x6efd, 0x0000 }, + { 0x6efe, 0x0000 }, + { 0x6eff, 0x0000 }, + { 0x6f00, 0x0000 }, + { 0x6f01, 0x0000 }, + { 0x6f02, 0x0000 }, + { 0x6f03, 0x0000 }, + { 0x6f04, 0x0000 }, + { 0x6f05, 0x0000 }, + { 0x6f06, 0x0000 }, + { 0x6f07, 0x0000 }, + { 0x6f08, 0x0000 }, + { 0x6f09, 0x0000 }, + { 0x6f0a, 0x0000 }, + { 0x6f0b, 0x0000 }, + { 0x6f0c, 0x0000 }, + { 0x6f0d, 0x0000 }, + { 0x6f0e, 0x0000 }, + { 0x6f0f, 0x0000 }, + { 0x6f10, 0x0000 }, + { 0x6f11, 0x0000 }, + { 0x6f12, 0x0000 }, + { 0x6f13, 0x0000 }, + { 0x6f14, 0x0000 }, + { 0x6f15, 0x0000 }, + { 0x6f16, 0x0000 }, + { 0x6f17, 0x0000 }, + { 0x6f18, 0x0000 }, + { 0x6f19, 0x0000 }, + { 0x6f1a, 0x0000 }, + { 0x6f1b, 0x0000 }, + { 0x6f1c, 0x0000 }, + { 0x6f1d, 0x0000 }, + { 0x6f1e, 0x0000 }, + { 0x6f1f, 0x0000 }, + { 0x6f20, 0x0000 }, + { 0x6f21, 0x0000 }, + { 0x6f22, 0x0000 }, + { 0x6f23, 0x0000 }, + { 0x6f24, 0x0000 }, + { 0x6f25, 0x0000 }, + { 0x6f26, 0x0000 }, + { 0x6f27, 0x0000 }, + { 0x6f28, 0x0000 }, + { 0x6f29, 0x0000 }, + { 0x6f2a, 0x0000 }, + { 0x6f2b, 0x0000 }, + { 0x6f2c, 0x0000 }, + { 0x6f2d, 0x0000 }, + { 0x6f2e, 0x0000 }, + { 0x6f2f, 0x0000 }, + { 0x6f30, 0x0000 }, + { 0x6f31, 0x0000 }, + { 0x6f32, 0x0000 }, + { 0x6f33, 0x0000 }, + { 0x6f34, 0x0000 }, + { 0x6f35, 0x0000 }, + { 0x6f36, 0x0000 }, + { 0x6f37, 0x0000 }, + { 0x6f38, 0x0000 }, + { 0x6f39, 0x0000 }, + { 0x6f3a, 0x0000 }, + { 0x6f3b, 0x0000 }, + { 0x6f3c, 0x0000 }, + { 0x6f3d, 0x0000 }, + { 0x6f3e, 0x0000 }, + { 0x6f3f, 0x0000 }, + { 0x6f40, 0x0000 }, + { 0x6f41, 0x0000 }, + { 0x6f42, 0x0000 }, + { 0x6f43, 0x0000 }, + { 0x6f44, 0x0000 }, + { 0x6f45, 0x0000 }, + { 0x6f46, 0x0000 }, + { 0x6f47, 0x0000 }, + { 0x6f48, 0x0000 }, + { 0x6f49, 0x0000 }, + { 0x6f4a, 0x0000 }, + { 0x6f4b, 0x0000 }, + { 0x6f4c, 0x0000 }, + { 0x6f4d, 0x0000 }, + { 0x6f4e, 0x0000 }, + { 0x6f4f, 0x0000 }, + { 0x6f50, 0x0000 }, + { 0x6f51, 0x0000 }, + { 0x6f52, 0x0000 }, + { 0x6f53, 0x0000 }, + { 0x6f54, 0x0000 }, + { 0x6f55, 0x0000 }, + { 0x6f56, 0x0000 }, + { 0x6f57, 0x0000 }, + { 0x6f58, 0x0000 }, + { 0x6f59, 0x0000 }, + { 0x6f5a, 0x0000 }, + { 0x6f5b, 0x0000 }, + { 0x6f5c, 0x0000 }, + { 0x6f5d, 0x0000 }, + { 0x6f5e, 0x0000 }, + { 0x6f5f, 0x0000 }, + { 0x6f60, 0x0000 }, + { 0x6f61, 0x0000 }, + { 0x6f62, 0x0000 }, + { 0x6f63, 0x0000 }, + { 0x6f64, 0x0000 }, + { 0x6f65, 0x0000 }, + { 0x6f66, 0x0000 }, + { 0x6f67, 0x0000 }, + { 0x6f68, 0x0000 }, + { 0x6f69, 0x0000 }, + { 0x6f6a, 0x0000 }, + { 0x6f6b, 0x0000 }, + { 0x6f6c, 0x0000 }, + { 0x6f6d, 0x0000 }, + { 0x6f6e, 0x0000 }, + { 0x6f6f, 0x0000 }, + { 0x6f70, 0x0000 }, + { 0x6f71, 0x0000 }, + { 0x6f72, 0x0000 }, + { 0x6f73, 0x0000 }, + { 0x6f74, 0x0000 }, + { 0x6f75, 0x0000 }, + { 0x6f76, 0x0000 }, + { 0x6f77, 0x0000 }, + { 0x6f78, 0x0000 }, + { 0x6f79, 0x0000 }, + { 0x6f7a, 0x0000 }, + { 0x6f7b, 0x0000 }, + { 0x6f7c, 0x0000 }, + { 0x6f7d, 0x0000 }, + { 0x6f7e, 0x0000 }, + { 0x6f7f, 0x0000 }, + { 0x6f80, 0x0000 }, + { 0x6f81, 0x0000 }, + { 0x6f82, 0x0000 }, + { 0x6f83, 0x0000 }, + { 0x6f84, 0x0000 }, + { 0x6f85, 0x0000 }, + { 0x6f86, 0x0000 }, + { 0x6f87, 0x0000 }, + { 0x6f88, 0x0000 }, + { 0x6f89, 0x0000 }, + { 0x6f8a, 0x0000 }, + { 0x6f8b, 0x0000 }, + { 0x6f8c, 0x0000 }, + { 0x6f8d, 0x0000 }, + { 0x6f8e, 0x0000 }, + { 0x6f8f, 0x0000 }, + { 0x6f90, 0x0000 }, + { 0x6f91, 0x0000 }, + { 0x6f92, 0x0000 }, + { 0x6f93, 0x0000 }, + { 0x6f94, 0x0000 }, + { 0x6f95, 0x0000 }, + { 0x6f96, 0x0000 }, + { 0x6f97, 0x0000 }, + { 0x6f98, 0x0000 }, + { 0x6f99, 0x0000 }, + { 0x6f9a, 0x0000 }, + { 0x6f9b, 0x0000 }, + { 0x6f9c, 0x0000 }, + { 0x6f9d, 0x0000 }, + { 0x6f9e, 0x0000 }, + { 0x6f9f, 0x0000 }, + { 0x6fa0, 0x0000 }, + { 0x6fa1, 0x0000 }, + { 0x6fa2, 0x0000 }, + { 0x6fa3, 0x0000 }, + { 0x6fa4, 0x0000 }, + { 0x6fa5, 0x0000 }, + { 0x6fa6, 0x0000 }, + { 0x6fa7, 0x0000 }, + { 0x6fa8, 0x0000 }, + { 0x6fa9, 0x0000 }, + { 0x6faa, 0x0000 }, + { 0x6fab, 0x0000 }, + { 0x6fac, 0x0000 }, + { 0x6fad, 0x0000 }, + { 0x6fae, 0x0000 }, + { 0x6faf, 0x0000 }, + { 0x6fb0, 0x0000 }, + { 0x6fb1, 0x0000 }, + { 0x6fb2, 0x0000 }, + { 0x6fb3, 0x0000 }, + { 0x6fb4, 0x0000 }, + { 0x6fb5, 0x0000 }, + { 0x6fb6, 0x0000 }, + { 0x6fb7, 0x0000 }, + { 0x6fb8, 0x0000 }, + { 0x6fb9, 0x0000 }, + { 0x6fba, 0x0000 }, + { 0x6fbb, 0x0000 }, + { 0x6fbc, 0x0000 }, + { 0x6fbd, 0x0000 }, + { 0x6fbe, 0x0000 }, + { 0x6fbf, 0x0000 }, + { 0x6fc0, 0x0000 }, + { 0x6fc1, 0x0000 }, + { 0x6fc2, 0x0000 }, + { 0x6fc3, 0x0000 }, + { 0x6fc4, 0x0000 }, + { 0x6fc5, 0x0000 }, + { 0x6fc6, 0x0000 }, + { 0x6fc7, 0x0000 }, + { 0x6fc8, 0x0000 }, + { 0x6fc9, 0x0000 }, + { 0x6fca, 0x0000 }, + { 0x6fcb, 0x0000 }, + { 0x6fcc, 0x0000 }, + { 0x6fcd, 0x0000 }, + { 0x6fce, 0x0000 }, + { 0x6fcf, 0x0000 }, + { 0x6fd0, 0x0000 }, + { 0x6fd1, 0x0000 }, + { 0x6fd2, 0x0000 }, + { 0x6fd3, 0x0000 }, + { 0x6fd4, 0x0000 }, + { 0x6fd5, 0x0000 }, + { 0x6fd6, 0x0000 }, + { 0x6fd7, 0x0000 }, + { 0x6fd8, 0x0000 }, + { 0x6fd9, 0x0000 }, + { 0x6fda, 0x0000 }, + { 0x6fdb, 0x0000 }, + { 0x6fdc, 0x0000 }, + { 0x6fdd, 0x0000 }, + { 0x6fde, 0x0000 }, + { 0x6fdf, 0x0000 }, + { 0x6fe0, 0x0000 }, + { 0x6fe1, 0x0000 }, + { 0x6fe2, 0x0000 }, + { 0x6fe3, 0x0000 }, + { 0x6fe4, 0x0000 }, + { 0x6fe5, 0x0000 }, + { 0x6fe6, 0x0000 }, + { 0x6fe7, 0x0000 }, + { 0x6fe8, 0x0000 }, + { 0x6fe9, 0x0000 }, + { 0x6fea, 0x0000 }, + { 0x6feb, 0x0000 }, + { 0x6fec, 0x0000 }, + { 0x6fed, 0x0000 }, + { 0x6fee, 0x0000 }, + { 0x6fef, 0x0000 }, + { 0x6ff0, 0x0000 }, + { 0x6ff1, 0x0000 }, + { 0x6ff2, 0x0000 }, + { 0x6ff3, 0x0000 }, + { 0x6ff4, 0x0000 }, + { 0x6ff5, 0x0000 }, + { 0x6ff6, 0x0000 }, + { 0x6ff7, 0x0000 }, + { 0x6ff8, 0x0000 }, + { 0x6ff9, 0x0000 }, + { 0x6ffa, 0x0000 }, + { 0x6ffb, 0x0000 }, + { 0x6ffc, 0x0000 }, + { 0x6ffd, 0x0000 }, + { 0x6ffe, 0x0000 }, + { 0x6fff, 0x0000 }, + { 0x7000, 0x0000 }, + { 0x7001, 0x0000 }, + { 0x7002, 0x0000 }, + { 0x7003, 0x0000 }, + { 0x7004, 0x0000 }, + { 0x7005, 0x0000 }, + { 0x7006, 0x0000 }, + { 0x7007, 0x0000 }, + { 0x7008, 0x0000 }, + { 0x7009, 0x0000 }, + { 0x700a, 0x0000 }, + { 0x700b, 0x0000 }, + { 0x700c, 0x0000 }, + { 0x700d, 0x0000 }, + { 0x700e, 0x0000 }, + { 0x700f, 0x0000 }, + { 0x7010, 0x0000 }, + { 0x7011, 0x0000 }, + { 0x7012, 0x0000 }, + { 0x7013, 0x0000 }, + { 0x7014, 0x0000 }, + { 0x7015, 0x0000 }, + { 0x7016, 0x0000 }, + { 0x7017, 0x0000 }, + { 0x7018, 0x0000 }, + { 0x7019, 0x0000 }, + { 0x701a, 0x0000 }, + { 0x701b, 0x0000 }, + { 0x701c, 0x0000 }, + { 0x701d, 0x0000 }, + { 0x701e, 0x0000 }, + { 0x701f, 0x0000 }, + { 0x7020, 0x0000 }, + { 0x7021, 0x0000 }, + { 0x7022, 0x0000 }, + { 0x7023, 0x0000 }, + { 0x7024, 0x0000 }, + { 0x7025, 0x0000 }, + { 0x7026, 0x0000 }, + { 0x7027, 0x0000 }, + { 0x7028, 0x0000 }, + { 0x7029, 0x0000 }, + { 0x702a, 0x0000 }, + { 0x702b, 0x0000 }, + { 0x702c, 0x0000 }, + { 0x702d, 0x0000 }, + { 0x702e, 0x0000 }, + { 0x702f, 0x0000 }, + { 0x7030, 0x0000 }, + { 0x7031, 0x0000 }, + { 0x7032, 0x0000 }, + { 0x7033, 0x0000 }, + { 0x7034, 0x0000 }, + { 0x7035, 0x0000 }, + { 0x7036, 0x0000 }, + { 0x7037, 0x0000 }, + { 0x7038, 0x0000 }, + { 0x7039, 0x0000 }, + { 0x703a, 0x0000 }, + { 0x703b, 0x0000 }, + { 0x703c, 0x0000 }, + { 0x703d, 0x0000 }, + { 0x703e, 0x0000 }, + { 0x703f, 0x0000 }, + { 0x7040, 0x0000 }, + { 0x7041, 0x0000 }, + { 0x7042, 0x0000 }, + { 0x7043, 0x0000 }, + { 0x7044, 0x0000 }, + { 0x7045, 0x0000 }, + { 0x7046, 0x0000 }, + { 0x7047, 0x0000 }, + { 0x7048, 0x0000 }, + { 0x7049, 0x0000 }, + { 0x704a, 0x0000 }, + { 0x704b, 0x0000 }, + { 0x704c, 0x0000 }, + { 0x704d, 0x0000 }, + { 0x704e, 0x0000 }, + { 0x704f, 0x0000 }, + { 0x7050, 0x0000 }, + { 0x7051, 0x0000 }, + { 0x7052, 0x0000 }, + { 0x7053, 0x0000 }, + { 0x7054, 0x0000 }, + { 0x7055, 0x0000 }, + { 0x7056, 0x0000 }, + { 0x7057, 0x0000 }, + { 0x7058, 0x0000 }, + { 0x7059, 0x0000 }, + { 0x705a, 0x0000 }, + { 0x705b, 0x0000 }, + { 0x705c, 0x0000 }, + { 0x705d, 0x0000 }, + { 0x705e, 0x0000 }, + { 0x705f, 0x0000 }, + { 0x7060, 0x0000 }, + { 0x7061, 0x0000 }, + { 0x7062, 0x0000 }, + { 0x7063, 0x0000 }, + { 0x7064, 0x0000 }, + { 0x7065, 0x0000 }, + { 0x7066, 0x0000 }, + { 0x7067, 0x0000 }, + { 0x7068, 0x0000 }, + { 0x7069, 0x0000 }, + { 0x706a, 0x0000 }, + { 0x706b, 0x0000 }, + { 0x706c, 0x0000 }, + { 0x706d, 0x0000 }, + { 0x706e, 0x0000 }, + { 0x706f, 0x0000 }, + { 0x7070, 0x0000 }, + { 0x7071, 0x0000 }, + { 0x7072, 0x0000 }, + { 0x7073, 0x0000 }, + { 0x7074, 0x0000 }, + { 0x7075, 0x0000 }, + { 0x7076, 0x0000 }, + { 0x7077, 0x0000 }, + { 0x7078, 0x0000 }, + { 0x7079, 0x0000 }, + { 0x707a, 0x0000 }, + { 0x707b, 0x0000 }, + { 0x707c, 0x0000 }, + { 0x707d, 0x0000 }, + { 0x707e, 0x0000 }, + { 0x707f, 0x0000 }, + { 0x7080, 0x0000 }, + { 0x7081, 0x0000 }, + { 0x7082, 0x0000 }, + { 0x7083, 0x0000 }, + { 0x7084, 0x0000 }, + { 0x7085, 0x0000 }, + { 0x7086, 0x0000 }, + { 0x7087, 0x0000 }, + { 0x7088, 0x0000 }, + { 0x7089, 0x0000 }, + { 0x708a, 0x0000 }, + { 0x708b, 0x0000 }, + { 0x708c, 0x0000 }, + { 0x708d, 0x0000 }, + { 0x708e, 0x0000 }, + { 0x708f, 0x0000 }, + { 0x7090, 0x0000 }, + { 0x7091, 0x0000 }, + { 0x7092, 0x0000 }, + { 0x7093, 0x0000 }, + { 0x7094, 0x0000 }, + { 0x7095, 0x0000 }, + { 0x7096, 0x0000 }, + { 0x7097, 0x0000 }, + { 0x7098, 0x0000 }, + { 0x7099, 0x0000 }, + { 0x709a, 0x0000 }, + { 0x709b, 0x0000 }, + { 0x709c, 0x0000 }, + { 0x709d, 0x0000 }, + { 0x709e, 0x0000 }, + { 0x709f, 0x0000 }, + { 0x70a0, 0x0000 }, + { 0x70a1, 0x0000 }, + { 0x70a2, 0x0000 }, + { 0x70a3, 0x0000 }, + { 0x70a4, 0x0000 }, + { 0x70a5, 0x0000 }, + { 0x70a6, 0x0000 }, + { 0x70a7, 0x0000 }, + { 0x70a8, 0x0000 }, + { 0x70a9, 0x0000 }, + { 0x70aa, 0x0000 }, + { 0x70ab, 0x0000 }, + { 0x70ac, 0x0000 }, + { 0x70ad, 0x0000 }, + { 0x70ae, 0x0000 }, + { 0x70af, 0x0000 }, + { 0x70b0, 0x0000 }, + { 0x70b1, 0x0000 }, + { 0x70b2, 0x0000 }, + { 0x70b3, 0x0000 }, + { 0x70b4, 0x0000 }, + { 0x70b5, 0x0000 }, + { 0x70b6, 0x0000 }, + { 0x70b7, 0x0000 }, + { 0x70b8, 0x0000 }, + { 0x70b9, 0x0000 }, + { 0x70ba, 0x0000 }, + { 0x70bb, 0x0000 }, + { 0x70bc, 0x0000 }, + { 0x70bd, 0x0000 }, + { 0x70be, 0x0000 }, + { 0x70bf, 0x0000 }, + { 0x70c0, 0x0000 }, + { 0x70c1, 0x0000 }, + { 0x70c2, 0x0000 }, + { 0x70c3, 0x0000 }, + { 0x70c4, 0x0000 }, + { 0x70c5, 0x0000 }, + { 0x70c6, 0x0000 }, + { 0x70c7, 0x0000 }, + { 0x70c8, 0x0000 }, + { 0x70c9, 0x0000 }, + { 0x70ca, 0x0000 }, + { 0x70cb, 0x0000 }, + { 0x70cc, 0x0000 }, + { 0x70cd, 0x0000 }, + { 0x70ce, 0x0000 }, + { 0x70cf, 0x0000 }, + { 0x70d0, 0x0000 }, + { 0x70d1, 0x0000 }, + { 0x70d2, 0x0000 }, + { 0x70d3, 0x0000 }, + { 0x70d4, 0x0000 }, + { 0x70d5, 0x0000 }, + { 0x70d6, 0x0000 }, + { 0x70d7, 0x0000 }, + { 0x70d8, 0x0000 }, + { 0x70d9, 0x0000 }, + { 0x70da, 0x0000 }, + { 0x70db, 0x0000 }, + { 0x70dc, 0x0000 }, + { 0x70dd, 0x0000 }, + { 0x70de, 0x0000 }, + { 0x70df, 0x0000 }, + { 0x70e0, 0x0000 }, + { 0x70e1, 0x0000 }, + { 0x70e2, 0x0000 }, + { 0x70e3, 0x0000 }, + { 0x70e4, 0x0000 }, + { 0x70e5, 0x0000 }, + { 0x70e6, 0x0000 }, + { 0x70e7, 0x0000 }, + { 0x70e8, 0x0000 }, + { 0x70e9, 0x0000 }, + { 0x70ea, 0x0000 }, + { 0x70eb, 0x0000 }, + { 0x70ec, 0x0000 }, + { 0x70ed, 0x0000 }, + { 0x70ee, 0x0000 }, + { 0x70ef, 0x0000 }, + { 0x70f0, 0x0000 }, + { 0x70f1, 0x0000 }, + { 0x70f2, 0x0000 }, + { 0x70f3, 0x0000 }, + { 0x70f4, 0x0000 }, + { 0x70f5, 0x0000 }, + { 0x70f6, 0x0000 }, + { 0x70f7, 0x0000 }, + { 0x70f8, 0x0000 }, + { 0x70f9, 0x0000 }, + { 0x70fa, 0x0000 }, + { 0x70fb, 0x0000 }, + { 0x70fc, 0x0000 }, + { 0x70fd, 0x0000 }, + { 0x70fe, 0x0000 }, + { 0x70ff, 0x0000 }, + { 0x7100, 0x0000 }, + { 0x7101, 0x0000 }, + { 0x7102, 0x0000 }, + { 0x7103, 0x0000 }, + { 0x7104, 0x0000 }, + { 0x7105, 0x0000 }, + { 0x7106, 0x0000 }, + { 0x7107, 0x0000 }, + { 0x7108, 0x0000 }, + { 0x7109, 0x0000 }, + { 0x710a, 0x0000 }, + { 0x710b, 0x0000 }, + { 0x710c, 0x0000 }, + { 0x710d, 0x0000 }, + { 0x710e, 0x0000 }, + { 0x710f, 0x0000 }, + { 0x7110, 0x0000 }, + { 0x7111, 0x0000 }, + { 0x7112, 0x0000 }, + { 0x7113, 0x0000 }, + { 0x7114, 0x0000 }, + { 0x7115, 0x0000 }, + { 0x7116, 0x0000 }, + { 0x7117, 0x0000 }, + { 0x7118, 0x0000 }, + { 0x7119, 0x0000 }, + { 0x711a, 0x0000 }, + { 0x711b, 0x0000 }, + { 0x711c, 0x0000 }, + { 0x711d, 0x0000 }, + { 0x711e, 0x0000 }, + { 0x711f, 0x0000 }, + { 0x7120, 0x0000 }, + { 0x7121, 0x0000 }, + { 0x7122, 0x0000 }, + { 0x7123, 0x0000 }, + { 0x7124, 0x0000 }, + { 0x7125, 0x0000 }, + { 0x7126, 0x0000 }, + { 0x7127, 0x0000 }, + { 0x7128, 0x0000 }, + { 0x7129, 0x0000 }, + { 0x712a, 0x0000 }, + { 0x712b, 0x0000 }, + { 0x712c, 0x0000 }, + { 0x712d, 0x0000 }, + { 0x712e, 0x0000 }, + { 0x712f, 0x0000 }, + { 0x7130, 0x0000 }, + { 0x7131, 0x0000 }, + { 0x7132, 0x0000 }, + { 0x7133, 0x0000 }, + { 0x7134, 0x0000 }, + { 0x7135, 0x0000 }, + { 0x7136, 0x0000 }, + { 0x7137, 0x0000 }, + { 0x7138, 0x0000 }, + { 0x7139, 0x0000 }, + { 0x713a, 0x0000 }, + { 0x713b, 0x0000 }, + { 0x713c, 0x0000 }, + { 0x713d, 0x0000 }, + { 0x713e, 0x0000 }, + { 0x713f, 0x0000 }, + { 0x7140, 0x0000 }, + { 0x7141, 0x0000 }, + { 0x7142, 0x0000 }, + { 0x7143, 0x0000 }, + { 0x7144, 0x0000 }, + { 0x7145, 0x0000 }, + { 0x7146, 0x0000 }, + { 0x7147, 0x0000 }, + { 0x7148, 0x0000 }, + { 0x7149, 0x0000 }, + { 0x714a, 0x0000 }, + { 0x714b, 0x0000 }, + { 0x714c, 0x0000 }, + { 0x714d, 0x0000 }, + { 0x714e, 0x0000 }, + { 0x714f, 0x0000 }, + { 0x7150, 0x0000 }, + { 0x7151, 0x0000 }, + { 0x7152, 0x0000 }, + { 0x7153, 0x0000 }, + { 0x7154, 0x0000 }, + { 0x7155, 0x0000 }, + { 0x7156, 0x0000 }, + { 0x7157, 0x0000 }, + { 0x7158, 0x0000 }, + { 0x7159, 0x0000 }, + { 0x715a, 0x0000 }, + { 0x715b, 0x0000 }, + { 0x715c, 0x0000 }, + { 0x715d, 0x0000 }, + { 0x715e, 0x0000 }, + { 0x715f, 0x0000 }, + { 0x7160, 0x0000 }, + { 0x7161, 0x0000 }, + { 0x7162, 0x0000 }, + { 0x7163, 0x0000 }, + { 0x7164, 0x0000 }, + { 0x7165, 0x0000 }, + { 0x7166, 0x0000 }, + { 0x7167, 0x0000 }, + { 0x7168, 0x0000 }, + { 0x7169, 0x0000 }, + { 0x716a, 0x0000 }, + { 0x716b, 0x0000 }, + { 0x716c, 0x0000 }, + { 0x716d, 0x0000 }, + { 0x716e, 0x0000 }, + { 0x716f, 0x0000 }, + { 0x7170, 0x0000 }, + { 0x7171, 0x0000 }, + { 0x7172, 0x0000 }, + { 0x7173, 0x0000 }, + { 0x7174, 0x0000 }, + { 0x7175, 0x0000 }, + { 0x7176, 0x0000 }, + { 0x7177, 0x0000 }, + { 0x7178, 0x0000 }, + { 0x7179, 0x0000 }, + { 0x717a, 0x0000 }, + { 0x717b, 0x0000 }, + { 0x717c, 0x0000 }, + { 0x717d, 0x0000 }, + { 0x717e, 0x0000 }, + { 0x717f, 0x0000 }, + { 0x7180, 0x0000 }, + { 0x7181, 0x0000 }, + { 0x7182, 0x0000 }, + { 0x7183, 0x0000 }, + { 0x7184, 0x0000 }, + { 0x7185, 0x0000 }, + { 0x7186, 0x0000 }, + { 0x7187, 0x0000 }, + { 0x7188, 0x0000 }, + { 0x7189, 0x0000 }, + { 0x718a, 0x0000 }, + { 0x718b, 0x0000 }, + { 0x718c, 0x0000 }, + { 0x718d, 0x0000 }, + { 0x718e, 0x0000 }, + { 0x718f, 0x0000 }, + { 0x7190, 0x0000 }, + { 0x7191, 0x0000 }, + { 0x7192, 0x0000 }, + { 0x7193, 0x0000 }, + { 0x7194, 0x0000 }, + { 0x7195, 0x0000 }, + { 0x7196, 0x0000 }, + { 0x7197, 0x0000 }, + { 0x7198, 0x0000 }, + { 0x7199, 0x0000 }, + { 0x719a, 0x0000 }, + { 0x719b, 0x0000 }, + { 0x719c, 0x0000 }, + { 0x719d, 0x0000 }, + { 0x719e, 0x0000 }, + { 0x719f, 0x0000 }, + { 0x71a0, 0x0000 }, + { 0x71a1, 0x0000 }, + { 0x71a2, 0x0000 }, + { 0x71a3, 0x0000 }, + { 0x71a4, 0x0000 }, + { 0x71a5, 0x0000 }, + { 0x71a6, 0x0000 }, + { 0x71a7, 0x0000 }, + { 0x71a8, 0x0000 }, + { 0x71a9, 0x0000 }, + { 0x71aa, 0x0000 }, + { 0x71ab, 0x0000 }, + { 0x71ac, 0x0000 }, + { 0x71ad, 0x0000 }, + { 0x71ae, 0x0000 }, + { 0x71af, 0x0000 }, + { 0x71b0, 0x0000 }, + { 0x71b1, 0x0000 }, + { 0x71b2, 0x0000 }, + { 0x71b3, 0x0000 }, + { 0x71b4, 0x0000 }, + { 0x71b5, 0x0000 }, + { 0x71b6, 0x0000 }, + { 0x71b7, 0x0000 }, + { 0x71b8, 0x0000 }, + { 0x71b9, 0x0000 }, + { 0x71ba, 0x0000 }, + { 0x71bb, 0x0000 }, + { 0x71bc, 0x0000 }, + { 0x71bd, 0x0000 }, + { 0x71be, 0x0000 }, + { 0x71bf, 0x0000 }, + { 0x71c0, 0x0000 }, + { 0x71c1, 0x0000 }, + { 0x71c2, 0x0000 }, + { 0x71c3, 0x0000 }, + { 0x71c4, 0x0000 }, + { 0x71c5, 0x0000 }, + { 0x71c6, 0x0000 }, + { 0x71c7, 0x0000 }, + { 0x71c8, 0x0000 }, + { 0x71c9, 0x0000 }, + { 0x71ca, 0x0000 }, + { 0x71cb, 0x0000 }, + { 0x71cc, 0x0000 }, + { 0x71cd, 0x0000 }, + { 0x71ce, 0x0000 }, + { 0x71cf, 0x0000 }, + { 0x71d0, 0x0000 }, + { 0x71d1, 0x0000 }, + { 0x71d2, 0x0000 }, + { 0x71d3, 0x0000 }, + { 0x71d4, 0x0000 }, + { 0x71d5, 0x0000 }, + { 0x71d6, 0x0000 }, + { 0x71d7, 0x0000 }, + { 0x71d8, 0x0000 }, + { 0x71d9, 0x0000 }, + { 0x71da, 0x0000 }, + { 0x71db, 0x0000 }, + { 0x71dc, 0x0000 }, + { 0x71dd, 0x0000 }, + { 0x71de, 0x0000 }, + { 0x71df, 0x0000 }, + { 0x71e0, 0x0000 }, + { 0x71e1, 0x0000 }, + { 0x71e2, 0x0000 }, + { 0x71e3, 0x0000 }, + { 0x71e4, 0x0000 }, + { 0x71e5, 0x0000 }, + { 0x71e6, 0x0000 }, + { 0x71e7, 0x0000 }, + { 0x71e8, 0x0000 }, + { 0x71e9, 0x0000 }, + { 0x71ea, 0x0000 }, + { 0x71eb, 0x0000 }, + { 0x71ec, 0x0000 }, + { 0x71ed, 0x0000 }, + { 0x71ee, 0x0000 }, + { 0x71ef, 0x0000 }, + { 0x71f0, 0x0000 }, + { 0x71f1, 0x0000 }, + { 0x71f2, 0x0000 }, + { 0x71f3, 0x0000 }, + { 0x71f4, 0x0000 }, + { 0x71f5, 0x0000 }, + { 0x71f6, 0x0000 }, + { 0x71f7, 0x0000 }, + { 0x71f8, 0x0000 }, + { 0x71f9, 0x0000 }, + { 0x71fa, 0x0000 }, + { 0x71fb, 0x0000 }, + { 0x71fc, 0x0000 }, + { 0x71fd, 0x0000 }, + { 0x71fe, 0x0000 }, + { 0x71ff, 0x0000 }, + { 0x7200, 0x0000 }, + { 0x7201, 0x0000 }, + { 0x7202, 0x0000 }, + { 0x7203, 0x0000 }, + { 0x7204, 0x0000 }, + { 0x7205, 0x0000 }, + { 0x7206, 0x0000 }, + { 0x7207, 0x0000 }, + { 0x7208, 0x0000 }, + { 0x7209, 0x0000 }, + { 0x720a, 0x0000 }, + { 0x720b, 0x0000 }, + { 0x720c, 0x0000 }, + { 0x720d, 0x0000 }, + { 0x720e, 0x0000 }, + { 0x720f, 0x0000 }, + { 0x7210, 0x0000 }, + { 0x7211, 0x0000 }, + { 0x7212, 0x0000 }, + { 0x7213, 0x0000 }, + { 0x7214, 0x0000 }, + { 0x7215, 0x0000 }, + { 0x7216, 0x0000 }, + { 0x7217, 0x0000 }, + { 0x7218, 0x0000 }, + { 0x7219, 0x0000 }, + { 0x721a, 0x0000 }, + { 0x721b, 0x0000 }, + { 0x721c, 0x0000 }, + { 0x721d, 0x0000 }, + { 0x721e, 0x0000 }, + { 0x721f, 0x0000 }, + { 0x7220, 0x0000 }, + { 0x7221, 0x0000 }, + { 0x7222, 0x0000 }, + { 0x7223, 0x0000 }, + { 0x7224, 0x0000 }, + { 0x7225, 0x0000 }, + { 0x7226, 0x0000 }, + { 0x7227, 0x0000 }, + { 0x7228, 0x0000 }, + { 0x7229, 0x0000 }, + { 0x722a, 0x0000 }, + { 0x722b, 0x0000 }, + { 0x722c, 0x0000 }, + { 0x722d, 0x0000 }, + { 0x722e, 0x0000 }, + { 0x722f, 0x0000 }, + { 0x7230, 0x0000 }, + { 0x7231, 0x0000 }, + { 0x7232, 0x0000 }, + { 0x7233, 0x0000 }, + { 0x7234, 0x0000 }, + { 0x7235, 0x0000 }, + { 0x7236, 0x0000 }, + { 0x7237, 0x0000 }, + { 0x7238, 0x0000 }, + { 0x7239, 0x0000 }, + { 0x723a, 0x0000 }, + { 0x723b, 0x0000 }, + { 0x723c, 0x0000 }, + { 0x723d, 0x0000 }, + { 0x723e, 0x0000 }, + { 0x723f, 0x0000 }, + { 0x7240, 0x0000 }, + { 0x7241, 0x0000 }, + { 0x7242, 0x0000 }, + { 0x7243, 0x0000 }, + { 0x7244, 0x0000 }, + { 0x7245, 0x0000 }, + { 0x7246, 0x0000 }, + { 0x7247, 0x0000 }, + { 0x7248, 0x0000 }, + { 0x7249, 0x0000 }, + { 0x724a, 0x0000 }, + { 0x724b, 0x0000 }, + { 0x724c, 0x0000 }, + { 0x724d, 0x0000 }, + { 0x724e, 0x0000 }, + { 0x724f, 0x0000 }, + { 0x7250, 0x0000 }, + { 0x7251, 0x0000 }, + { 0x7252, 0x0000 }, + { 0x7253, 0x0000 }, + { 0x7254, 0x0000 }, + { 0x7255, 0x0000 }, + { 0x7256, 0x0000 }, + { 0x7257, 0x0000 }, + { 0x7258, 0x0000 }, + { 0x7259, 0x0000 }, + { 0x725a, 0x0000 }, + { 0x725b, 0x0000 }, + { 0x725c, 0x0000 }, + { 0x725d, 0x0000 }, + { 0x725e, 0x0000 }, + { 0x725f, 0x0000 }, + { 0x7260, 0x0000 }, + { 0x7261, 0x0000 }, + { 0x7262, 0x0000 }, + { 0x7263, 0x0000 }, + { 0x7264, 0x0000 }, + { 0x7265, 0x0000 }, + { 0x7266, 0x0000 }, + { 0x7267, 0x0000 }, + { 0x7268, 0x0000 }, + { 0x7269, 0x0000 }, + { 0x726a, 0x0000 }, + { 0x726b, 0x0000 }, + { 0x726c, 0x0000 }, + { 0x726d, 0x0000 }, + { 0x726e, 0x0000 }, + { 0x726f, 0x0000 }, + { 0x7270, 0x0000 }, + { 0x7271, 0x0000 }, + { 0x7272, 0x0000 }, + { 0x7273, 0x0000 }, + { 0x7274, 0x0000 }, + { 0x7275, 0x0000 }, + { 0x7276, 0x0000 }, + { 0x7277, 0x0000 }, + { 0x7278, 0x0000 }, + { 0x7279, 0x0000 }, + { 0x727a, 0x0000 }, + { 0x727b, 0x0000 }, + { 0x727c, 0x0000 }, + { 0x727d, 0x0000 }, + { 0x727e, 0x0000 }, + { 0x727f, 0x0000 }, + { 0x7280, 0x0000 }, + { 0x7281, 0x0000 }, + { 0x7282, 0x0000 }, + { 0x7283, 0x0000 }, + { 0x7284, 0x0000 }, + { 0x7285, 0x0000 }, + { 0x7286, 0x0000 }, + { 0x7287, 0x0000 }, + { 0x7288, 0x0000 }, + { 0x7289, 0x0000 }, + { 0x728a, 0x0000 }, + { 0x728b, 0x0000 }, + { 0x728c, 0x0000 }, + { 0x728d, 0x0000 }, + { 0x728e, 0x0000 }, + { 0x728f, 0x0000 }, + { 0x7290, 0x0000 }, + { 0x7291, 0x0000 }, + { 0x7292, 0x0000 }, + { 0x7293, 0x0000 }, + { 0x7294, 0x0000 }, + { 0x7295, 0x0000 }, + { 0x7296, 0x0000 }, + { 0x7297, 0x0000 }, + { 0x7298, 0x0000 }, + { 0x7299, 0x0000 }, + { 0x729a, 0x0000 }, + { 0x729b, 0x0000 }, + { 0x729c, 0x0000 }, + { 0x729d, 0x0000 }, + { 0x729e, 0x0000 }, + { 0x729f, 0x0000 }, + { 0x72a0, 0x0000 }, + { 0x72a1, 0x0000 }, + { 0x72a2, 0x0000 }, + { 0x72a3, 0x0000 }, + { 0x72a4, 0x0000 }, + { 0x72a5, 0x0000 }, + { 0x72a6, 0x0000 }, + { 0x72a7, 0x0000 }, + { 0x72a8, 0x0000 }, + { 0x72a9, 0x0000 }, + { 0x72aa, 0x0000 }, + { 0x72ab, 0x0000 }, + { 0x72ac, 0x0000 }, + { 0x72ad, 0x0000 }, + { 0x72ae, 0x0000 }, + { 0x72af, 0x0000 }, + { 0x72b0, 0x0000 }, + { 0x72b1, 0x0000 }, + { 0x72b2, 0x0000 }, + { 0x72b3, 0x0000 }, + { 0x72b4, 0x0000 }, + { 0x72b5, 0x0000 }, + { 0x72b6, 0x0000 }, + { 0x72b7, 0x0000 }, + { 0x72b8, 0x0000 }, + { 0x72b9, 0x0000 }, + { 0x72ba, 0x0000 }, + { 0x72bb, 0x0000 }, + { 0x72bc, 0x0000 }, + { 0x72bd, 0x0000 }, + { 0x72be, 0x0000 }, + { 0x72bf, 0x0000 }, + { 0x72c0, 0x0000 }, + { 0x72c1, 0x0000 }, + { 0x72c2, 0x0000 }, + { 0x72c3, 0x0000 }, + { 0x72c4, 0x0000 }, + { 0x72c5, 0x0000 }, + { 0x72c6, 0x0000 }, + { 0x72c7, 0x0000 }, + { 0x72c8, 0x0000 }, + { 0x72c9, 0x0000 }, + { 0x72ca, 0x0000 }, + { 0x72cb, 0x0000 }, + { 0x72cc, 0x0000 }, + { 0x72cd, 0x0000 }, + { 0x72ce, 0x0000 }, + { 0x72cf, 0x0000 }, + { 0x72d0, 0x0000 }, + { 0x72d1, 0x0000 }, + { 0x72d2, 0x0000 }, + { 0x72d3, 0x0000 }, + { 0x72d4, 0x0000 }, + { 0x72d5, 0x0000 }, + { 0x72d6, 0x0000 }, + { 0x72d7, 0x0000 }, + { 0x72d8, 0x0000 }, + { 0x72d9, 0x0000 }, + { 0x72da, 0x0000 }, + { 0x72db, 0x0000 }, + { 0x72dc, 0x0000 }, + { 0x72dd, 0x0000 }, + { 0x72de, 0x0000 }, + { 0x72df, 0x0000 }, + { 0x72e0, 0x0000 }, + { 0x72e1, 0x0000 }, + { 0x72e2, 0x0000 }, + { 0x72e3, 0x0000 }, + { 0x72e4, 0x0000 }, + { 0x72e5, 0x0000 }, + { 0x72e6, 0x0000 }, + { 0x72e7, 0x0000 }, + { 0x72e8, 0x0000 }, + { 0x72e9, 0x0000 }, + { 0x72ea, 0x0000 }, + { 0x72eb, 0x0000 }, + { 0x72ec, 0x0000 }, + { 0x72ed, 0x0000 }, + { 0x72ee, 0x0000 }, + { 0x72ef, 0x0000 }, + { 0x72f0, 0x0000 }, + { 0x72f1, 0x0000 }, + { 0x72f2, 0x0000 }, + { 0x72f3, 0x0000 }, + { 0x72f4, 0x0000 }, + { 0x72f5, 0x0000 }, + { 0x72f6, 0x0000 }, + { 0x72f7, 0x0000 }, + { 0x72f8, 0x0000 }, + { 0x72f9, 0x0000 }, + { 0x72fa, 0x0000 }, + { 0x72fb, 0x0000 }, + { 0x72fc, 0x0000 }, + { 0x72fd, 0x0000 }, + { 0x72fe, 0x0000 }, + { 0x72ff, 0x0000 }, + { 0x7300, 0x0000 }, + { 0x7301, 0x0000 }, + { 0x7302, 0x0000 }, + { 0x7303, 0x0000 }, + { 0x7304, 0x0000 }, + { 0x7305, 0x0000 }, + { 0x7306, 0x0000 }, + { 0x7307, 0x0000 }, + { 0x7308, 0x0000 }, + { 0x7309, 0x0000 }, + { 0x730a, 0x0000 }, + { 0x730b, 0x0000 }, + { 0x730c, 0x0000 }, + { 0x730d, 0x0000 }, + { 0x730e, 0x0000 }, + { 0x730f, 0x0000 }, + { 0x7310, 0x0000 }, + { 0x7311, 0x0000 }, + { 0x7312, 0x0000 }, + { 0x7313, 0x0000 }, + { 0x7314, 0x0000 }, + { 0x7315, 0x0000 }, + { 0x7316, 0x0000 }, + { 0x7317, 0x0000 }, + { 0x7318, 0x0000 }, + { 0x7319, 0x0000 }, + { 0x731a, 0x0000 }, + { 0x731b, 0x0000 }, + { 0x731c, 0x0000 }, + { 0x731d, 0x0000 }, + { 0x731e, 0x0000 }, + { 0x731f, 0x0000 }, + { 0x7320, 0x0000 }, + { 0x7321, 0x0000 }, + { 0x7322, 0x0000 }, + { 0x7323, 0x0000 }, + { 0x7324, 0x0000 }, + { 0x7325, 0x0000 }, + { 0x7326, 0x0000 }, + { 0x7327, 0x0000 }, + { 0x7328, 0x0000 }, + { 0x7329, 0x0000 }, + { 0x732a, 0x0000 }, + { 0x732b, 0x0000 }, + { 0x732c, 0x0000 }, + { 0x732d, 0x0000 }, + { 0x732e, 0x0000 }, + { 0x732f, 0x0000 }, + { 0x7330, 0x0000 }, + { 0x7331, 0x0000 }, + { 0x7332, 0x0000 }, + { 0x7333, 0x0000 }, + { 0x7334, 0x0000 }, + { 0x7335, 0x0000 }, + { 0x7336, 0x0000 }, + { 0x7337, 0x0000 }, + { 0x7338, 0x0000 }, + { 0x7339, 0x0000 }, + { 0x733a, 0x0000 }, + { 0x733b, 0x0000 }, + { 0x733c, 0x0000 }, + { 0x733d, 0x0000 }, + { 0x733e, 0x0000 }, + { 0x733f, 0x0000 }, + { 0x7340, 0x0000 }, + { 0x7341, 0x0000 }, + { 0x7342, 0x0000 }, + { 0x7343, 0x0000 }, + { 0x7344, 0x0000 }, + { 0x7345, 0x0000 }, + { 0x7346, 0x0000 }, + { 0x7347, 0x0000 }, + { 0x7348, 0x0000 }, + { 0x7349, 0x0000 }, + { 0x734a, 0x0000 }, + { 0x734b, 0x0000 }, + { 0x734c, 0x0000 }, + { 0x734d, 0x0000 }, + { 0x734e, 0x0000 }, + { 0x734f, 0x0000 }, + { 0x7350, 0x0000 }, + { 0x7351, 0x0000 }, + { 0x7352, 0x0000 }, + { 0x7353, 0x0000 }, + { 0x7354, 0x0000 }, + { 0x7355, 0x0000 }, + { 0x7356, 0x0000 }, + { 0x7357, 0x0000 }, + { 0x7358, 0x0000 }, + { 0x7359, 0x0000 }, + { 0x735a, 0x0000 }, + { 0x735b, 0x0000 }, + { 0x735c, 0x0000 }, + { 0x735d, 0x0000 }, + { 0x735e, 0x0000 }, + { 0x735f, 0x0000 }, + { 0x7360, 0x0000 }, + { 0x7361, 0x0000 }, + { 0x7362, 0x0000 }, + { 0x7363, 0x0000 }, + { 0x7364, 0x0000 }, + { 0x7365, 0x0000 }, + { 0x7366, 0x0000 }, + { 0x7367, 0x0000 }, + { 0x7368, 0x0000 }, + { 0x7369, 0x0000 }, + { 0x736a, 0x0000 }, + { 0x736b, 0x0000 }, + { 0x736c, 0x0000 }, + { 0x736d, 0x0000 }, + { 0x736e, 0x0000 }, + { 0x736f, 0x0000 }, + { 0x7370, 0x0000 }, + { 0x7371, 0x0000 }, + { 0x7372, 0x0000 }, + { 0x7373, 0x0000 }, + { 0x7374, 0x0000 }, + { 0x7375, 0x0000 }, + { 0x7376, 0x0000 }, + { 0x7377, 0x0000 }, + { 0x7378, 0x0000 }, + { 0x7379, 0x0000 }, + { 0x737a, 0x0000 }, + { 0x737b, 0x0000 }, + { 0x737c, 0x0000 }, + { 0x737d, 0x0000 }, + { 0x737e, 0x0000 }, + { 0x737f, 0x0000 }, + { 0x7380, 0x0000 }, + { 0x7381, 0x0000 }, + { 0x7382, 0x0000 }, + { 0x7383, 0x0000 }, + { 0x7384, 0x0000 }, + { 0x7385, 0x0000 }, + { 0x7386, 0x0000 }, + { 0x7387, 0x0000 }, + { 0x7388, 0x0000 }, + { 0x7389, 0x0000 }, + { 0x738a, 0x0000 }, + { 0x738b, 0x0000 }, + { 0x738c, 0x0000 }, + { 0x738d, 0x0000 }, + { 0x738e, 0x0000 }, + { 0x738f, 0x0000 }, + { 0x7390, 0x0000 }, + { 0x7391, 0x0000 }, + { 0x7392, 0x0000 }, + { 0x7393, 0x0000 }, + { 0x7394, 0x0000 }, + { 0x7395, 0x0000 }, + { 0x7396, 0x0000 }, + { 0x7397, 0x0000 }, + { 0x7398, 0x0000 }, + { 0x7399, 0x0000 }, + { 0x739a, 0x0000 }, + { 0x739b, 0x0000 }, + { 0x739c, 0x0000 }, + { 0x739d, 0x0000 }, + { 0x739e, 0x0000 }, + { 0x739f, 0x0000 }, + { 0x73a0, 0x0000 }, + { 0x73a1, 0x0000 }, + { 0x73a2, 0x0000 }, + { 0x73a3, 0x0000 }, + { 0x73a4, 0x0000 }, + { 0x73a5, 0x0000 }, + { 0x73a6, 0x0000 }, + { 0x73a7, 0x0000 }, + { 0x73a8, 0x0000 }, + { 0x73a9, 0x0000 }, + { 0x73aa, 0x0000 }, + { 0x73ab, 0x0000 }, + { 0x73ac, 0x0000 }, + { 0x73ad, 0x0000 }, + { 0x73ae, 0x0000 }, + { 0x73af, 0x0000 }, + { 0x73b0, 0x0000 }, + { 0x73b1, 0x0000 }, + { 0x73b2, 0x0000 }, + { 0x73b3, 0x0000 }, + { 0x73b4, 0x0000 }, + { 0x73b5, 0x0000 }, + { 0x73b6, 0x0000 }, + { 0x73b7, 0x0000 }, + { 0x73b8, 0x0000 }, + { 0x73b9, 0x0000 }, + { 0x73ba, 0x0000 }, + { 0x73bb, 0x0000 }, + { 0x73bc, 0x0000 }, + { 0x73bd, 0x0000 }, + { 0x73be, 0x0000 }, + { 0x73bf, 0x0000 }, + { 0x73c0, 0x0000 }, + { 0x73c1, 0x0000 }, + { 0x73c2, 0x0000 }, + { 0x73c3, 0x0000 }, + { 0x73c4, 0x0000 }, + { 0x73c5, 0x0000 }, + { 0x73c6, 0x0000 }, + { 0x73c7, 0x0000 }, + { 0x73c8, 0x0000 }, + { 0x73c9, 0x0000 }, + { 0x73ca, 0x0000 }, + { 0x73cb, 0x0000 }, + { 0x73cc, 0x0000 }, + { 0x73cd, 0x0000 }, + { 0x73ce, 0x0000 }, + { 0x73cf, 0x0000 }, + { 0x73d0, 0x0000 }, + { 0x73d1, 0x0000 }, + { 0x73d2, 0x0000 }, + { 0x73d3, 0x0000 }, + { 0x73d4, 0x0000 }, + { 0x73d5, 0x0000 }, + { 0x73d6, 0x0000 }, + { 0x73d7, 0x0000 }, + { 0x73d8, 0x0000 }, + { 0x73d9, 0x0000 }, + { 0x73da, 0x0000 }, + { 0x73db, 0x0000 }, + { 0x73dc, 0x0000 }, + { 0x73dd, 0x0000 }, + { 0x73de, 0x0000 }, + { 0x73df, 0x0000 }, + { 0x73e0, 0x0000 }, + { 0x73e1, 0x0000 }, + { 0x73e2, 0x0000 }, + { 0x73e3, 0x0000 }, + { 0x73e4, 0x0000 }, + { 0x73e5, 0x0000 }, + { 0x73e6, 0x0000 }, + { 0x73e7, 0x0000 }, + { 0x73e8, 0x0000 }, + { 0x73e9, 0x0000 }, + { 0x73ea, 0x0000 }, + { 0x73eb, 0x0000 }, + { 0x73ec, 0x0000 }, + { 0x73ed, 0x0000 }, + { 0x73ee, 0x0000 }, + { 0x73ef, 0x0000 }, + { 0x73f0, 0x0000 }, + { 0x73f1, 0x0000 }, + { 0x73f2, 0x0000 }, + { 0x73f3, 0x0000 }, + { 0x73f4, 0x0000 }, + { 0x73f5, 0x0000 }, + { 0x73f6, 0x0000 }, + { 0x73f7, 0x0000 }, + { 0x73f8, 0x0000 }, + { 0x73f9, 0x0000 }, + { 0x73fa, 0x0000 }, + { 0x73fb, 0x0000 }, + { 0x73fc, 0x0000 }, + { 0x73fd, 0x0000 }, + { 0x73fe, 0x0000 }, + { 0x73ff, 0x0000 }, + { 0x7400, 0x0000 }, + { 0x7401, 0x0000 }, + { 0x7402, 0x0000 }, + { 0x7403, 0x0000 }, + { 0x7404, 0x0000 }, + { 0x7405, 0x0000 }, + { 0x7406, 0x0000 }, + { 0x7407, 0x0000 }, + { 0x7408, 0x0000 }, + { 0x7409, 0x0000 }, + { 0x740a, 0x0000 }, + { 0x740b, 0x0000 }, + { 0x740c, 0x0000 }, + { 0x740d, 0x0000 }, + { 0x740e, 0x0000 }, + { 0x740f, 0x0000 }, + { 0x7410, 0x0000 }, + { 0x7411, 0x0000 }, + { 0x7412, 0x0000 }, + { 0x7413, 0x0000 }, + { 0x7414, 0x0000 }, + { 0x7415, 0x0000 }, + { 0x7416, 0x0000 }, + { 0x7417, 0x0000 }, + { 0x7418, 0x0000 }, + { 0x7419, 0x0000 }, + { 0x741a, 0x0000 }, + { 0x741b, 0x0000 }, + { 0x741c, 0x0000 }, + { 0x741d, 0x0000 }, + { 0x741e, 0x0000 }, + { 0x741f, 0x0000 }, + { 0x7420, 0x0000 }, + { 0x7421, 0x0000 }, + { 0x7422, 0x0000 }, + { 0x7423, 0x0000 }, + { 0x7424, 0x0000 }, + { 0x7425, 0x0000 }, + { 0x7426, 0x0000 }, + { 0x7427, 0x0000 }, + { 0x7428, 0x0000 }, + { 0x7429, 0x0000 }, + { 0x742a, 0x0000 }, + { 0x742b, 0x0000 }, + { 0x742c, 0x0000 }, + { 0x742d, 0x0000 }, + { 0x742e, 0x0000 }, + { 0x742f, 0x0000 }, + { 0x7430, 0x0000 }, + { 0x7431, 0x0000 }, + { 0x7432, 0x0000 }, + { 0x7433, 0x0000 }, + { 0x7434, 0x0000 }, + { 0x7435, 0x0000 }, + { 0x7436, 0x0000 }, + { 0x7437, 0x0000 }, + { 0x7438, 0x0000 }, + { 0x7439, 0x0000 }, + { 0x743a, 0x0000 }, + { 0x743b, 0x0000 }, + { 0x743c, 0x0000 }, + { 0x743d, 0x0000 }, + { 0x743e, 0x0000 }, + { 0x743f, 0x0000 }, + { 0x7440, 0x0000 }, + { 0x7441, 0x0000 }, + { 0x7442, 0x0000 }, + { 0x7443, 0x0000 }, + { 0x7444, 0x0000 }, + { 0x7445, 0x0000 }, + { 0x7446, 0x0000 }, + { 0x7447, 0x0000 }, + { 0x7448, 0x0000 }, + { 0x7449, 0x0000 }, + { 0x744a, 0x0000 }, + { 0x744b, 0x0000 }, + { 0x744c, 0x0000 }, + { 0x744d, 0x0000 }, + { 0x744e, 0x0000 }, + { 0x744f, 0x0000 }, + { 0x7450, 0x0000 }, + { 0x7451, 0x0000 }, + { 0x7452, 0x0000 }, + { 0x7453, 0x0000 }, + { 0x7454, 0x0000 }, + { 0x7455, 0x0000 }, + { 0x7456, 0x0000 }, + { 0x7457, 0x0000 }, + { 0x7458, 0x0000 }, + { 0x7459, 0x0000 }, + { 0x745a, 0x0000 }, + { 0x745b, 0x0000 }, + { 0x745c, 0x0000 }, + { 0x745d, 0x0000 }, + { 0x745e, 0x0000 }, + { 0x745f, 0x0000 }, + { 0x7460, 0x0000 }, + { 0x7461, 0x0000 }, + { 0x7462, 0x0000 }, + { 0x7463, 0x0000 }, + { 0x7464, 0x0000 }, + { 0x7465, 0x0000 }, + { 0x7466, 0x0000 }, + { 0x7467, 0x0000 }, + { 0x7468, 0x0000 }, + { 0x7469, 0x0000 }, + { 0x746a, 0x0000 }, + { 0x746b, 0x0000 }, + { 0x746c, 0x0000 }, + { 0x746d, 0x0000 }, + { 0x746e, 0x0000 }, + { 0x746f, 0x0000 }, + { 0x7470, 0x0000 }, + { 0x7471, 0x0000 }, + { 0x7472, 0x0000 }, + { 0x7473, 0x0000 }, + { 0x7474, 0x0000 }, + { 0x7475, 0x0000 }, + { 0x7476, 0x0000 }, + { 0x7477, 0x0000 }, + { 0x7478, 0x0000 }, + { 0x7479, 0x0000 }, + { 0x747a, 0x0000 }, + { 0x747b, 0x0000 }, + { 0x747c, 0x0000 }, + { 0x747d, 0x0000 }, + { 0x747e, 0x0000 }, + { 0x747f, 0x0000 }, + { 0x7480, 0x0000 }, + { 0x7481, 0x0000 }, + { 0x7482, 0x0000 }, + { 0x7483, 0x0000 }, + { 0x7484, 0x0000 }, + { 0x7485, 0x0000 }, + { 0x7486, 0x0000 }, + { 0x7487, 0x0000 }, + { 0x7488, 0x0000 }, + { 0x7489, 0x0000 }, + { 0x748a, 0x0000 }, + { 0x748b, 0x0000 }, + { 0x748c, 0x0000 }, + { 0x748d, 0x0000 }, + { 0x748e, 0x0000 }, + { 0x748f, 0x0000 }, + { 0x7490, 0x0000 }, + { 0x7491, 0x0000 }, + { 0x7492, 0x0000 }, + { 0x7493, 0x0000 }, + { 0x7494, 0x0000 }, + { 0x7495, 0x0000 }, + { 0x7496, 0x0000 }, + { 0x7497, 0x0000 }, + { 0x7498, 0x0000 }, + { 0x7499, 0x0000 }, + { 0x749a, 0x0000 }, + { 0x749b, 0x0000 }, + { 0x749c, 0x0000 }, + { 0x749d, 0x0000 }, + { 0x749e, 0x0000 }, + { 0x749f, 0x0000 }, + { 0x74a0, 0x0000 }, + { 0x74a1, 0x0000 }, + { 0x74a2, 0x0000 }, + { 0x74a3, 0x0000 }, + { 0x74a4, 0x0000 }, + { 0x74a5, 0x0000 }, + { 0x74a6, 0x0000 }, + { 0x74a7, 0x0000 }, + { 0x74a8, 0x0000 }, + { 0x74a9, 0x0000 }, + { 0x74aa, 0x0000 }, + { 0x74ab, 0x0000 }, + { 0x74ac, 0x0000 }, + { 0x74ad, 0x0000 }, + { 0x74ae, 0x0000 }, + { 0x74af, 0x0000 }, + { 0x74b0, 0x0000 }, + { 0x74b1, 0x0000 }, + { 0x74b2, 0x0000 }, + { 0x74b3, 0x0000 }, + { 0x74b4, 0x0000 }, + { 0x74b5, 0x0000 }, + { 0x74b6, 0x0000 }, + { 0x74b7, 0x0000 }, + { 0x74b8, 0x0000 }, + { 0x74b9, 0x0000 }, + { 0x74ba, 0x0000 }, + { 0x74bb, 0x0000 }, + { 0x74bc, 0x0000 }, + { 0x74bd, 0x0000 }, + { 0x74be, 0x0000 }, + { 0x74bf, 0x0000 }, + { 0x74c0, 0x0000 }, + { 0x74c1, 0x0000 }, + { 0x74c2, 0x0000 }, + { 0x74c3, 0x0000 }, + { 0x74c4, 0x0000 }, + { 0x74c5, 0x0000 }, + { 0x74c6, 0x0000 }, + { 0x74c7, 0x0000 }, + { 0x74c8, 0x0000 }, + { 0x74c9, 0x0000 }, + { 0x74ca, 0x0000 }, + { 0x74cb, 0x0000 }, + { 0x74cc, 0x0000 }, + { 0x74cd, 0x0000 }, + { 0x74ce, 0x0000 }, + { 0x74cf, 0x0000 }, + { 0x74d0, 0x0000 }, + { 0x74d1, 0x0000 }, + { 0x74d2, 0x0000 }, + { 0x74d3, 0x0000 }, + { 0x74d4, 0x0000 }, + { 0x74d5, 0x0000 }, + { 0x74d6, 0x0000 }, + { 0x74d7, 0x0000 }, + { 0x74d8, 0x0000 }, + { 0x74d9, 0x0000 }, + { 0x74da, 0x0000 }, + { 0x74db, 0x0000 }, + { 0x74dc, 0x0000 }, + { 0x74dd, 0x0000 }, + { 0x74de, 0x0000 }, + { 0x74df, 0x0000 }, + { 0x74e0, 0x0000 }, + { 0x74e1, 0x0000 }, + { 0x74e2, 0x0000 }, + { 0x74e3, 0x0000 }, + { 0x74e4, 0x0000 }, + { 0x74e5, 0x0000 }, + { 0x74e6, 0x0000 }, + { 0x74e7, 0x0000 }, + { 0x74e8, 0x0000 }, + { 0x74e9, 0x0000 }, + { 0x74ea, 0x0000 }, + { 0x74eb, 0x0000 }, + { 0x74ec, 0x0000 }, + { 0x74ed, 0x0000 }, + { 0x74ee, 0x0000 }, + { 0x74ef, 0x0000 }, + { 0x74f0, 0x0000 }, + { 0x74f1, 0x0000 }, + { 0x74f2, 0x0000 }, + { 0x74f3, 0x0000 }, + { 0x74f4, 0x0000 }, + { 0x74f5, 0x0000 }, + { 0x74f6, 0x0000 }, + { 0x74f7, 0x0000 }, + { 0x74f8, 0x0000 }, + { 0x74f9, 0x0000 }, + { 0x74fa, 0x0000 }, + { 0x74fb, 0x0000 }, + { 0x74fc, 0x0000 }, + { 0x74fd, 0x0000 }, + { 0x74fe, 0x0000 }, + { 0x74ff, 0x0000 }, + { 0x7500, 0x0000 }, + { 0x7501, 0x0000 }, + { 0x7502, 0x0000 }, + { 0x7503, 0x0000 }, + { 0x7504, 0x0000 }, + { 0x7505, 0x0000 }, + { 0x7506, 0x0000 }, + { 0x7507, 0x0000 }, + { 0x7508, 0x0000 }, + { 0x7509, 0x0000 }, + { 0x750a, 0x0000 }, + { 0x750b, 0x0000 }, + { 0x750c, 0x0000 }, + { 0x750d, 0x0000 }, + { 0x750e, 0x0000 }, + { 0x750f, 0x0000 }, + { 0x7510, 0x0000 }, + { 0x7511, 0x0000 }, + { 0x7512, 0x0000 }, + { 0x7513, 0x0000 }, + { 0x7514, 0x0000 }, + { 0x7515, 0x0000 }, + { 0x7516, 0x0000 }, + { 0x7517, 0x0000 }, + { 0x7518, 0x0000 }, + { 0x7519, 0x0000 }, + { 0x751a, 0x0000 }, + { 0x751b, 0x0000 }, + { 0x751c, 0x0000 }, + { 0x751d, 0x0000 }, + { 0x751e, 0x0000 }, + { 0x751f, 0x0000 }, + { 0x7520, 0x0000 }, + { 0x7521, 0x0000 }, + { 0x7522, 0x0000 }, + { 0x7523, 0x0000 }, + { 0x7524, 0x0000 }, + { 0x7525, 0x0000 }, + { 0x7526, 0x0000 }, + { 0x7527, 0x0000 }, + { 0x7528, 0x0000 }, + { 0x7529, 0x0000 }, + { 0x752a, 0x0000 }, + { 0x752b, 0x0000 }, + { 0x752c, 0x0000 }, + { 0x752d, 0x0000 }, + { 0x752e, 0x0000 }, + { 0x752f, 0x0000 }, + { 0x7530, 0x0000 }, + { 0x7531, 0x0000 }, + { 0x7532, 0x0000 }, + { 0x7533, 0x0000 }, + { 0x7534, 0x0000 }, + { 0x7535, 0x0000 }, + { 0x7536, 0x0000 }, + { 0x7537, 0x0000 }, + { 0x7538, 0x0000 }, + { 0x7539, 0x0000 }, + { 0x753a, 0x0000 }, + { 0x753b, 0x0000 }, + { 0x753c, 0x0000 }, + { 0x753d, 0x0000 }, + { 0x753e, 0x0000 }, + { 0x753f, 0x0000 }, + { 0x7540, 0x0000 }, + { 0x7541, 0x0000 }, + { 0x7542, 0x0000 }, + { 0x7543, 0x0000 }, + { 0x7544, 0x0000 }, + { 0x7545, 0x0000 }, + { 0x7546, 0x0000 }, + { 0x7547, 0x0000 }, + { 0x7548, 0x0000 }, + { 0x7549, 0x0000 }, + { 0x754a, 0x0000 }, + { 0x754b, 0x0000 }, + { 0x754c, 0x0000 }, + { 0x754d, 0x0000 }, + { 0x754e, 0x0000 }, + { 0x754f, 0x0000 }, + { 0x7550, 0x0000 }, + { 0x7551, 0x0000 }, + { 0x7552, 0x0000 }, + { 0x7553, 0x0000 }, + { 0x7554, 0x0000 }, + { 0x7555, 0x0000 }, + { 0x7556, 0x0000 }, + { 0x7557, 0x0000 }, + { 0x7558, 0x0000 }, + { 0x7559, 0x0000 }, + { 0x755a, 0x0000 }, + { 0x755b, 0x0000 }, + { 0x755c, 0x0000 }, + { 0x755d, 0x0000 }, + { 0x755e, 0x0000 }, + { 0x755f, 0x0000 }, + { 0x7560, 0x0000 }, + { 0x7561, 0x0000 }, + { 0x7562, 0x0000 }, + { 0x7563, 0x0000 }, + { 0x7564, 0x0000 }, + { 0x7565, 0x0000 }, + { 0x7566, 0x0000 }, + { 0x7567, 0x0000 }, + { 0x7568, 0x0000 }, + { 0x7569, 0x0000 }, + { 0x756a, 0x0000 }, + { 0x756b, 0x0000 }, + { 0x756c, 0x0000 }, + { 0x756d, 0x0000 }, + { 0x756e, 0x0000 }, + { 0x756f, 0x0000 }, + { 0x7570, 0x0000 }, + { 0x7571, 0x0000 }, + { 0x7572, 0x0000 }, + { 0x7573, 0x0000 }, + { 0x7574, 0x0000 }, + { 0x7575, 0x0000 }, + { 0x7576, 0x0000 }, + { 0x7577, 0x0000 }, + { 0x7578, 0x0000 }, + { 0x7579, 0x0000 }, + { 0x757a, 0x0000 }, + { 0x757b, 0x0000 }, + { 0x757c, 0x0000 }, + { 0x757d, 0x0000 }, + { 0x757e, 0x0000 }, + { 0x757f, 0x0000 }, + { 0x7580, 0x0000 }, + { 0x7581, 0x0000 }, + { 0x7582, 0x0000 }, + { 0x7583, 0x0000 }, + { 0x7584, 0x0000 }, + { 0x7585, 0x0000 }, + { 0x7586, 0x0000 }, + { 0x7587, 0x0000 }, + { 0x7588, 0x0000 }, + { 0x7589, 0x0000 }, + { 0x758a, 0x0000 }, + { 0x758b, 0x0000 }, + { 0x758c, 0x0000 }, + { 0x758d, 0x0000 }, + { 0x758e, 0x0000 }, + { 0x758f, 0x0000 }, + { 0x7590, 0x0000 }, + { 0x7591, 0x0000 }, + { 0x7592, 0x0000 }, + { 0x7593, 0x0000 }, + { 0x7594, 0x0000 }, + { 0x7595, 0x0000 }, + { 0x7596, 0x0000 }, + { 0x7597, 0x0000 }, + { 0x7598, 0x0000 }, + { 0x7599, 0x0000 }, + { 0x759a, 0x0000 }, + { 0x759b, 0x0000 }, + { 0x759c, 0x0000 }, + { 0x759d, 0x0000 }, + { 0x759e, 0x0000 }, + { 0x759f, 0x0000 }, + { 0x75a0, 0x0000 }, + { 0x75a1, 0x0000 }, + { 0x75a2, 0x0000 }, + { 0x75a3, 0x0000 }, + { 0x75a4, 0x0000 }, + { 0x75a5, 0x0000 }, + { 0x75a6, 0x0000 }, + { 0x75a7, 0x0000 }, + { 0x75a8, 0x0000 }, + { 0x75a9, 0x0000 }, + { 0x75aa, 0x0000 }, + { 0x75ab, 0x0000 }, + { 0x75ac, 0x0000 }, + { 0x75ad, 0x0000 }, + { 0x75ae, 0x0000 }, + { 0x75af, 0x0000 }, + { 0x75b0, 0x0000 }, + { 0x75b1, 0x0000 }, + { 0x75b2, 0x0000 }, + { 0x75b3, 0x0000 }, + { 0x75b4, 0x0000 }, + { 0x75b5, 0x0000 }, + { 0x75b6, 0x0000 }, + { 0x75b7, 0x0000 }, + { 0x75b8, 0x0000 }, + { 0x75b9, 0x0000 }, + { 0x75ba, 0x0000 }, + { 0x75bb, 0x0000 }, + { 0x75bc, 0x0000 }, + { 0x75bd, 0x0000 }, + { 0x75be, 0x0000 }, + { 0x75bf, 0x0000 }, + { 0x75c0, 0x0000 }, + { 0x75c1, 0x0000 }, + { 0x75c2, 0x0000 }, + { 0x75c3, 0x0000 }, + { 0x75c4, 0x0000 }, + { 0x75c5, 0x0000 }, + { 0x75c6, 0x0000 }, + { 0x75c7, 0x0000 }, + { 0x75c8, 0x0000 }, + { 0x75c9, 0x0000 }, + { 0x75ca, 0x0000 }, + { 0x75cb, 0x0000 }, + { 0x75cc, 0x0000 }, + { 0x75cd, 0x0000 }, + { 0x75ce, 0x0000 }, + { 0x75cf, 0x0000 }, + { 0x75d0, 0x0000 }, + { 0x75d1, 0x0000 }, + { 0x75d2, 0x0000 }, + { 0x75d3, 0x0000 }, + { 0x75d4, 0x0000 }, + { 0x75d5, 0x0000 }, + { 0x75d6, 0x0000 }, + { 0x75d7, 0x0000 }, + { 0x75d8, 0x0000 }, + { 0x75d9, 0x0000 }, + { 0x75da, 0x0000 }, + { 0x75db, 0x0000 }, + { 0x75dc, 0x0000 }, + { 0x75dd, 0x0000 }, + { 0x75de, 0x0000 }, + { 0x75df, 0x0000 }, + { 0x75e0, 0x0000 }, + { 0x75e1, 0x0000 }, + { 0x75e2, 0x0000 }, + { 0x75e3, 0x0000 }, + { 0x75e4, 0x0000 }, + { 0x75e5, 0x0000 }, + { 0x75e6, 0x0000 }, + { 0x75e7, 0x0000 }, + { 0x75e8, 0x0000 }, + { 0x75e9, 0x0000 }, + { 0x75ea, 0x0000 }, + { 0x75eb, 0x0000 }, + { 0x75ec, 0x0000 }, + { 0x75ed, 0x0000 }, + { 0x75ee, 0x0000 }, + { 0x75ef, 0x0000 }, + { 0x75f0, 0x0000 }, + { 0x75f1, 0x0000 }, + { 0x75f2, 0x0000 }, + { 0x75f3, 0x0000 }, + { 0x75f4, 0x0000 }, + { 0x75f5, 0x0000 }, + { 0x75f6, 0x0000 }, + { 0x75f7, 0x0000 }, + { 0x75f8, 0x0000 }, + { 0x75f9, 0x0000 }, + { 0x75fa, 0x0000 }, + { 0x75fb, 0x0000 }, + { 0x75fc, 0x0000 }, + { 0x75fd, 0x0000 }, + { 0x75fe, 0x0000 }, + { 0x75ff, 0x0000 }, + { 0x7600, 0x0000 }, + { 0x7601, 0x0000 }, + { 0x7602, 0x0000 }, + { 0x7603, 0x0000 }, + { 0x7604, 0x0000 }, + { 0x7605, 0x0000 }, + { 0x7606, 0x0000 }, + { 0x7607, 0x0000 }, + { 0x7608, 0x0000 }, + { 0x7609, 0x0000 }, + { 0x760a, 0x0000 }, + { 0x760b, 0x0000 }, + { 0x760c, 0x0000 }, + { 0x760d, 0x0000 }, + { 0x760e, 0x0000 }, + { 0x760f, 0x0000 }, + { 0x7610, 0x0000 }, + { 0x7611, 0x0000 }, + { 0x7612, 0x0000 }, + { 0x7613, 0x0000 }, + { 0x7614, 0x0000 }, + { 0x7615, 0x0000 }, + { 0x7616, 0x0000 }, + { 0x7617, 0x0000 }, + { 0x7618, 0x0000 }, + { 0x7619, 0x0000 }, + { 0x761a, 0x0000 }, + { 0x761b, 0x0000 }, + { 0x761c, 0x0000 }, + { 0x761d, 0x0000 }, + { 0x761e, 0x0000 }, + { 0x761f, 0x0000 }, + { 0x7620, 0x0000 }, + { 0x7621, 0x0000 }, + { 0x7622, 0x0000 }, + { 0x7623, 0x0000 }, + { 0x7624, 0x0000 }, + { 0x7625, 0x0000 }, + { 0x7626, 0x0000 }, + { 0x7627, 0x0000 }, + { 0x7628, 0x0000 }, + { 0x7629, 0x0000 }, + { 0x762a, 0x0000 }, + { 0x762b, 0x0000 }, + { 0x762c, 0x0000 }, + { 0x762d, 0x0000 }, + { 0x762e, 0x0000 }, + { 0x762f, 0x0000 }, + { 0x7630, 0x0000 }, + { 0x7631, 0x0000 }, + { 0x7632, 0x0000 }, + { 0x7633, 0x0000 }, + { 0x7634, 0x0000 }, + { 0x7635, 0x0000 }, + { 0x7636, 0x0000 }, + { 0x7637, 0x0000 }, + { 0x7638, 0x0000 }, + { 0x7639, 0x0000 }, + { 0x763a, 0x0000 }, + { 0x763b, 0x0000 }, + { 0x763c, 0x0000 }, + { 0x763d, 0x0000 }, + { 0x763e, 0x0000 }, + { 0x763f, 0x0000 }, + { 0x7640, 0x0000 }, + { 0x7641, 0x0000 }, + { 0x7642, 0x0000 }, + { 0x7643, 0x0000 }, + { 0x7644, 0x0000 }, + { 0x7645, 0x0000 }, + { 0x7646, 0x0000 }, + { 0x7647, 0x0000 }, + { 0x7648, 0x0000 }, + { 0x7649, 0x0000 }, + { 0x764a, 0x0000 }, + { 0x764b, 0x0000 }, + { 0x764c, 0x0000 }, + { 0x764d, 0x0000 }, + { 0x764e, 0x0000 }, + { 0x764f, 0x0000 }, + { 0x7650, 0x0000 }, + { 0x7651, 0x0000 }, + { 0x7652, 0x0000 }, + { 0x7653, 0x0000 }, + { 0x7654, 0x0000 }, + { 0x7655, 0x0000 }, + { 0x7656, 0x0000 }, + { 0x7657, 0x0000 }, + { 0x7658, 0x0000 }, + { 0x7659, 0x0000 }, + { 0x765a, 0x0000 }, + { 0x765b, 0x0000 }, + { 0x765c, 0x0000 }, + { 0x765d, 0x0000 }, + { 0x765e, 0x0000 }, + { 0x765f, 0x0000 }, + { 0x7660, 0x0000 }, + { 0x7661, 0x0000 }, + { 0x7662, 0x0000 }, + { 0x7663, 0x0000 }, + { 0x7664, 0x0000 }, + { 0x7665, 0x0000 }, + { 0x7666, 0x0000 }, + { 0x7667, 0x0000 }, + { 0x7668, 0x0000 }, + { 0x7669, 0x0000 }, + { 0x766a, 0x0000 }, + { 0x766b, 0x0000 }, + { 0x766c, 0x0000 }, + { 0x766d, 0x0000 }, + { 0x766e, 0x0000 }, + { 0x766f, 0x0000 }, + { 0x7670, 0x0000 }, + { 0x7671, 0x0000 }, + { 0x7672, 0x0000 }, + { 0x7673, 0x0000 }, + { 0x7674, 0x0000 }, + { 0x7675, 0x0000 }, + { 0x7676, 0x0000 }, + { 0x7677, 0x0000 }, + { 0x7678, 0x0000 }, + { 0x7679, 0x0000 }, + { 0x767a, 0x0000 }, + { 0x767b, 0x0000 }, + { 0x767c, 0x0000 }, + { 0x767d, 0x0000 }, + { 0x767e, 0x0000 }, + { 0x767f, 0x0000 }, + { 0x7680, 0x0000 }, + { 0x7681, 0x0000 }, + { 0x7682, 0x0000 }, + { 0x7683, 0x0000 }, + { 0x7684, 0x0000 }, + { 0x7685, 0x0000 }, + { 0x7686, 0x0000 }, + { 0x7687, 0x0000 }, + { 0x7688, 0x0000 }, + { 0x7689, 0x0000 }, + { 0x768a, 0x0000 }, + { 0x768b, 0x0000 }, + { 0x768c, 0x0000 }, + { 0x768d, 0x0000 }, + { 0x768e, 0x0000 }, + { 0x768f, 0x0000 }, + { 0x7690, 0x0000 }, + { 0x7691, 0x0000 }, + { 0x7692, 0x0000 }, + { 0x7693, 0x0000 }, + { 0x7694, 0x0000 }, + { 0x7695, 0x0000 }, + { 0x7696, 0x0000 }, + { 0x7697, 0x0000 }, + { 0x7698, 0x0000 }, + { 0x7699, 0x0000 }, + { 0x769a, 0x0000 }, + { 0x769b, 0x0000 }, + { 0x769c, 0x0000 }, + { 0x769d, 0x0000 }, + { 0x769e, 0x0000 }, + { 0x769f, 0x0000 }, + { 0x76a0, 0x0000 }, + { 0x76a1, 0x0000 }, + { 0x76a2, 0x0000 }, + { 0x76a3, 0x0000 }, + { 0x76a4, 0x0000 }, + { 0x76a5, 0x0000 }, + { 0x76a6, 0x0000 }, + { 0x76a7, 0x0000 }, + { 0x76a8, 0x0000 }, + { 0x76a9, 0x0000 }, + { 0x76aa, 0x0000 }, + { 0x76ab, 0x0000 }, + { 0x76ac, 0x0000 }, + { 0x76ad, 0x0000 }, + { 0x76ae, 0x0000 }, + { 0x76af, 0x0000 }, + { 0x76b0, 0x0000 }, + { 0x76b1, 0x0000 }, + { 0x76b2, 0x0000 }, + { 0x76b3, 0x0000 }, + { 0x76b4, 0x0000 }, + { 0x76b5, 0x0000 }, + { 0x76b6, 0x0000 }, + { 0x76b7, 0x0000 }, + { 0x76b8, 0x0000 }, + { 0x76b9, 0x0000 }, + { 0x76ba, 0x0000 }, + { 0x76bb, 0x0000 }, + { 0x76bc, 0x0000 }, + { 0x76bd, 0x0000 }, + { 0x76be, 0x0000 }, + { 0x76bf, 0x0000 }, + { 0x76c0, 0x0000 }, + { 0x76c1, 0x0000 }, + { 0x76c2, 0x0000 }, + { 0x76c3, 0x0000 }, + { 0x76c4, 0x0000 }, + { 0x76c5, 0x0000 }, + { 0x76c6, 0x0000 }, + { 0x76c7, 0x0000 }, + { 0x76c8, 0x0000 }, + { 0x76c9, 0x0000 }, + { 0x76ca, 0x0000 }, + { 0x76cb, 0x0000 }, + { 0x76cc, 0x0000 }, + { 0x76cd, 0x0000 }, + { 0x76ce, 0x0000 }, + { 0x76cf, 0x0000 }, + { 0x76d0, 0x0000 }, + { 0x76d1, 0x0000 }, + { 0x76d2, 0x0000 }, + { 0x76d3, 0x0000 }, + { 0x76d4, 0x0000 }, + { 0x76d5, 0x0000 }, + { 0x76d6, 0x0000 }, + { 0x76d7, 0x0000 }, + { 0x76d8, 0x0000 }, + { 0x76d9, 0x0000 }, + { 0x76da, 0x0000 }, + { 0x76db, 0x0000 }, + { 0x76dc, 0x0000 }, + { 0x76dd, 0x0000 }, + { 0x76de, 0x0000 }, + { 0x76df, 0x0000 }, + { 0x76e0, 0x0000 }, + { 0x76e1, 0x0000 }, + { 0x76e2, 0x0000 }, + { 0x76e3, 0x0000 }, + { 0x76e4, 0x0000 }, + { 0x76e5, 0x0000 }, + { 0x76e6, 0x0000 }, + { 0x76e7, 0x0000 }, + { 0x76e8, 0x0000 }, + { 0x76e9, 0x0000 }, + { 0x76ea, 0x0000 }, + { 0x76eb, 0x0000 }, + { 0x76ec, 0x0000 }, + { 0x76ed, 0x0000 }, + { 0x76ee, 0x0000 }, + { 0x76ef, 0x0000 }, + { 0x76f0, 0x0000 }, + { 0x76f1, 0x0000 }, + { 0x76f2, 0x0000 }, + { 0x76f3, 0x0000 }, + { 0x76f4, 0x0000 }, + { 0x76f5, 0x0000 }, + { 0x76f6, 0x0000 }, + { 0x76f7, 0x0000 }, + { 0x76f8, 0x0000 }, + { 0x76f9, 0x0000 }, + { 0x76fa, 0x0000 }, + { 0x76fb, 0x0000 }, + { 0x76fc, 0x0000 }, + { 0x76fd, 0x0000 }, + { 0x76fe, 0x0000 }, + { 0x76ff, 0x0000 }, + { 0x7700, 0x0000 }, + { 0x7701, 0x0000 }, + { 0x7702, 0x0000 }, + { 0x7703, 0x0000 }, + { 0x7704, 0x0000 }, + { 0x7705, 0x0000 }, + { 0x7706, 0x0000 }, + { 0x7707, 0x0000 }, + { 0x7708, 0x0000 }, + { 0x7709, 0x0000 }, + { 0x770a, 0x0000 }, + { 0x770b, 0x0000 }, + { 0x770c, 0x0000 }, + { 0x770d, 0x0000 }, + { 0x770e, 0x0000 }, + { 0x770f, 0x0000 }, + { 0x7710, 0x0000 }, + { 0x7711, 0x0000 }, + { 0x7712, 0x0000 }, + { 0x7713, 0x0000 }, + { 0x7714, 0x0000 }, + { 0x7715, 0x0000 }, + { 0x7716, 0x0000 }, + { 0x7717, 0x0000 }, + { 0x7718, 0x0000 }, + { 0x7719, 0x0000 }, + { 0x771a, 0x0000 }, + { 0x771b, 0x0000 }, + { 0x771c, 0x0000 }, + { 0x771d, 0x0000 }, + { 0x771e, 0x0000 }, + { 0x771f, 0x0000 }, + { 0x7720, 0x0000 }, + { 0x7721, 0x0000 }, + { 0x7722, 0x0000 }, + { 0x7723, 0x0000 }, + { 0x7724, 0x0000 }, + { 0x7725, 0x0000 }, + { 0x7726, 0x0000 }, + { 0x7727, 0x0000 }, + { 0x7728, 0x0000 }, + { 0x7729, 0x0000 }, + { 0x772a, 0x0000 }, + { 0x772b, 0x0000 }, + { 0x772c, 0x0000 }, + { 0x772d, 0x0000 }, + { 0x772e, 0x0000 }, + { 0x772f, 0x0000 }, + { 0x7730, 0x0000 }, + { 0x7731, 0x0000 }, + { 0x7732, 0x0000 }, + { 0x7733, 0x0000 }, + { 0x7734, 0x0000 }, + { 0x7735, 0x0000 }, + { 0x7736, 0x0000 }, + { 0x7737, 0x0000 }, + { 0x7738, 0x0000 }, + { 0x7739, 0x0000 }, + { 0x773a, 0x0000 }, + { 0x773b, 0x0000 }, + { 0x773c, 0x0000 }, + { 0x773d, 0x0000 }, + { 0x773e, 0x0000 }, + { 0x773f, 0x0000 }, + { 0x7740, 0x0000 }, + { 0x7741, 0x0000 }, + { 0x7742, 0x0000 }, + { 0x7743, 0x0000 }, + { 0x7744, 0x0000 }, + { 0x7745, 0x0000 }, + { 0x7746, 0x0000 }, + { 0x7747, 0x0000 }, + { 0x7748, 0x0000 }, + { 0x7749, 0x0000 }, + { 0x774a, 0x0000 }, + { 0x774b, 0x0000 }, + { 0x774c, 0x0000 }, + { 0x774d, 0x0000 }, + { 0x774e, 0x0000 }, + { 0x774f, 0x0000 }, + { 0x7750, 0x0000 }, + { 0x7751, 0x0000 }, + { 0x7752, 0x0000 }, + { 0x7753, 0x0000 }, + { 0x7754, 0x0000 }, + { 0x7755, 0x0000 }, + { 0x7756, 0x0000 }, + { 0x7757, 0x0000 }, + { 0x7758, 0x0000 }, + { 0x7759, 0x0000 }, + { 0x775a, 0x0000 }, + { 0x775b, 0x0000 }, + { 0x775c, 0x0000 }, + { 0x775d, 0x0000 }, + { 0x775e, 0x0000 }, + { 0x775f, 0x0000 }, + { 0x7760, 0x0000 }, + { 0x7761, 0x0000 }, + { 0x7762, 0x0000 }, + { 0x7763, 0x0000 }, + { 0x7764, 0x0000 }, + { 0x7765, 0x0000 }, + { 0x7766, 0x0000 }, + { 0x7767, 0x0000 }, + { 0x7768, 0x0000 }, + { 0x7769, 0x0000 }, + { 0x776a, 0x0000 }, + { 0x776b, 0x0000 }, + { 0x776c, 0x0000 }, + { 0x776d, 0x0000 }, + { 0x776e, 0x0000 }, + { 0x776f, 0x0000 }, + { 0x7770, 0x0000 }, + { 0x7771, 0x0000 }, + { 0x7772, 0x0000 }, + { 0x7773, 0x0000 }, + { 0x7774, 0x0000 }, + { 0x7775, 0x0000 }, + { 0x7776, 0x0000 }, + { 0x7777, 0x0000 }, + { 0x7778, 0x0000 }, + { 0x7779, 0x0000 }, + { 0x777a, 0x0000 }, + { 0x777b, 0x0000 }, + { 0x777c, 0x0000 }, + { 0x777d, 0x0000 }, + { 0x777e, 0x0000 }, + { 0x777f, 0x0000 }, + { 0x7780, 0x0000 }, + { 0x7781, 0x0000 }, + { 0x7782, 0x0000 }, + { 0x7783, 0x0000 }, + { 0x7784, 0x0000 }, + { 0x7785, 0x0000 }, + { 0x7786, 0x0000 }, + { 0x7787, 0x0000 }, + { 0x7788, 0x0000 }, + { 0x7789, 0x0000 }, + { 0x778a, 0x0000 }, + { 0x778b, 0x0000 }, + { 0x778c, 0x0000 }, + { 0x778d, 0x0000 }, + { 0x778e, 0x0000 }, + { 0x778f, 0x0000 }, + { 0x7790, 0x0000 }, + { 0x7791, 0x0000 }, + { 0x7792, 0x0000 }, + { 0x7793, 0x0000 }, + { 0x7794, 0x0000 }, + { 0x7795, 0x0000 }, + { 0x7796, 0x0000 }, + { 0x7797, 0x0000 }, + { 0x7798, 0x0000 }, + { 0x7799, 0x0000 }, + { 0x779a, 0x0000 }, + { 0x779b, 0x0000 }, + { 0x779c, 0x0000 }, + { 0x779d, 0x0000 }, + { 0x779e, 0x0000 }, + { 0x779f, 0x0000 }, + { 0x77a0, 0x0000 }, + { 0x77a1, 0x0000 }, + { 0x77a2, 0x0000 }, + { 0x77a3, 0x0000 }, + { 0x77a4, 0x0000 }, + { 0x77a5, 0x0000 }, + { 0x77a6, 0x0000 }, + { 0x77a7, 0x0000 }, + { 0x77a8, 0x0000 }, + { 0x77a9, 0x0000 }, + { 0x77aa, 0x0000 }, + { 0x77ab, 0x0000 }, + { 0x77ac, 0x0000 }, + { 0x77ad, 0x0000 }, + { 0x77ae, 0x0000 }, + { 0x77af, 0x0000 }, + { 0x77b0, 0x0000 }, + { 0x77b1, 0x0000 }, + { 0x77b2, 0x0000 }, + { 0x77b3, 0x0000 }, + { 0x77b4, 0x0000 }, + { 0x77b5, 0x0000 }, + { 0x77b6, 0x0000 }, + { 0x77b7, 0x0000 }, + { 0x77b8, 0x0000 }, + { 0x77b9, 0x0000 }, + { 0x77ba, 0x0000 }, + { 0x77bb, 0x0000 }, + { 0x77bc, 0x0000 }, + { 0x77bd, 0x0000 }, + { 0x77be, 0x0000 }, + { 0x77bf, 0x0000 }, + { 0x77c0, 0x0000 }, + { 0x77c1, 0x0000 }, + { 0x77c2, 0x0000 }, + { 0x77c3, 0x0000 }, + { 0x77c4, 0x0000 }, + { 0x77c5, 0x0000 }, + { 0x77c6, 0x0000 }, + { 0x77c7, 0x0000 }, + { 0x77c8, 0x0000 }, + { 0x77c9, 0x0000 }, + { 0x77ca, 0x0000 }, + { 0x77cb, 0x0000 }, + { 0x77cc, 0x0000 }, + { 0x77cd, 0x0000 }, + { 0x77ce, 0x0000 }, + { 0x77cf, 0x0000 }, + { 0x77d0, 0x0000 }, + { 0x77d1, 0x0000 }, + { 0x77d2, 0x0000 }, + { 0x77d3, 0x0000 }, + { 0x77d4, 0x0000 }, + { 0x77d5, 0x0000 }, + { 0x77d6, 0x0000 }, + { 0x77d7, 0x0000 }, + { 0x77d8, 0x0000 }, + { 0x77d9, 0x0000 }, + { 0x77da, 0x0000 }, + { 0x77db, 0x0000 }, + { 0x77dc, 0x0000 }, + { 0x77dd, 0x0000 }, + { 0x77de, 0x0000 }, + { 0x77df, 0x0000 }, + { 0x77e0, 0x0000 }, + { 0x77e1, 0x0000 }, + { 0x77e2, 0x0000 }, + { 0x77e3, 0x0000 }, + { 0x77e4, 0x0000 }, + { 0x77e5, 0x0000 }, + { 0x77e6, 0x0000 }, + { 0x77e7, 0x0000 }, + { 0x77e8, 0x0000 }, + { 0x77e9, 0x0000 }, + { 0x77ea, 0x0000 }, + { 0x77eb, 0x0000 }, + { 0x77ec, 0x0000 }, + { 0x77ed, 0x0000 }, + { 0x77ee, 0x0000 }, + { 0x77ef, 0x0000 }, + { 0x77f0, 0x0000 }, + { 0x77f1, 0x0000 }, + { 0x77f2, 0x0000 }, + { 0x77f3, 0x0000 }, + { 0x77f4, 0x0000 }, + { 0x77f5, 0x0000 }, + { 0x77f6, 0x0000 }, + { 0x77f7, 0x0000 }, + { 0x77f8, 0x0000 }, + { 0x77f9, 0x0000 }, + { 0x77fa, 0x0000 }, + { 0x77fb, 0x0000 }, + { 0x77fc, 0x0000 }, + { 0x77fd, 0x0000 }, + { 0x77fe, 0x0000 }, + { 0x77ff, 0x0000 }, + { 0x7800, 0x0000 }, + { 0x7801, 0x0000 }, + { 0x7802, 0x0000 }, + { 0x7803, 0x0000 }, + { 0x7804, 0x0000 }, + { 0x7805, 0x0000 }, + { 0x7806, 0x0000 }, + { 0x7807, 0x0000 }, + { 0x7808, 0x0000 }, + { 0x7809, 0x0000 }, + { 0x780a, 0x0000 }, + { 0x780b, 0x0000 }, + { 0x780c, 0x0000 }, + { 0x780d, 0x0000 }, + { 0x780e, 0x0000 }, + { 0x780f, 0x0000 }, + { 0x7810, 0x0000 }, + { 0x7811, 0x0000 }, + { 0x7812, 0x0000 }, + { 0x7813, 0x0000 }, + { 0x7814, 0x0000 }, + { 0x7815, 0x0000 }, + { 0x7816, 0x0000 }, + { 0x7817, 0x0000 }, + { 0x7818, 0x0000 }, + { 0x7819, 0x0000 }, + { 0x781a, 0x0000 }, + { 0x781b, 0x0000 }, + { 0x781c, 0x0000 }, + { 0x781d, 0x0000 }, + { 0x781e, 0x0000 }, + { 0x781f, 0x0000 }, + { 0x7820, 0x0000 }, + { 0x7821, 0x0000 }, + { 0x7822, 0x0000 }, + { 0x7823, 0x0000 }, + { 0x7824, 0x0000 }, + { 0x7825, 0x0000 }, + { 0x7826, 0x0000 }, + { 0x7827, 0x0000 }, + { 0x7828, 0x0000 }, + { 0x7829, 0x0000 }, + { 0x782a, 0x0000 }, + { 0x782b, 0x0000 }, + { 0x782c, 0x0000 }, + { 0x782d, 0x0000 }, + { 0x782e, 0x0000 }, + { 0x782f, 0x0000 }, + { 0x7830, 0x0000 }, + { 0x7831, 0x0000 }, + { 0x7832, 0x0000 }, + { 0x7833, 0x0000 }, + { 0x7834, 0x0000 }, + { 0x7835, 0x0000 }, + { 0x7836, 0x0000 }, + { 0x7837, 0x0000 }, + { 0x7838, 0x0000 }, + { 0x7839, 0x0000 }, + { 0x783a, 0x0000 }, + { 0x783b, 0x0000 }, + { 0x783c, 0x0000 }, + { 0x783d, 0x0000 }, + { 0x783e, 0x0000 }, + { 0x783f, 0x0000 }, + { 0x7840, 0x0000 }, + { 0x7841, 0x0000 }, + { 0x7842, 0x0000 }, + { 0x7843, 0x0000 }, + { 0x7844, 0x0000 }, + { 0x7845, 0x0000 }, + { 0x7846, 0x0000 }, + { 0x7847, 0x0000 }, + { 0x7848, 0x0000 }, + { 0x7849, 0x0000 }, + { 0x784a, 0x0000 }, + { 0x784b, 0x0000 }, + { 0x784c, 0x0000 }, + { 0x784d, 0x0000 }, + { 0x784e, 0x0000 }, + { 0x784f, 0x0000 }, + { 0x7850, 0x0000 }, + { 0x7851, 0x0000 }, + { 0x7852, 0x0000 }, + { 0x7853, 0x0000 }, + { 0x7854, 0x0000 }, + { 0x7855, 0x0000 }, + { 0x7856, 0x0000 }, + { 0x7857, 0x0000 }, + { 0x7858, 0x0000 }, + { 0x7859, 0x0000 }, + { 0x785a, 0x0000 }, + { 0x785b, 0x0000 }, + { 0x785c, 0x0000 }, + { 0x785d, 0x0000 }, + { 0x785e, 0x0000 }, + { 0x785f, 0x0000 }, + { 0x7860, 0x0000 }, + { 0x7861, 0x0000 }, + { 0x7862, 0x0000 }, + { 0x7863, 0x0000 }, + { 0x7864, 0x0000 }, + { 0x7865, 0x0000 }, + { 0x7866, 0x0000 }, + { 0x7867, 0x0000 }, + { 0x7868, 0x0000 }, + { 0x7869, 0x0000 }, + { 0x786a, 0x0000 }, + { 0x786b, 0x0000 }, + { 0x786c, 0x0000 }, + { 0x786d, 0x0000 }, + { 0x786e, 0x0000 }, + { 0x786f, 0x0000 }, + { 0x7870, 0x0000 }, + { 0x7871, 0x0000 }, + { 0x7872, 0x0000 }, + { 0x7873, 0x0000 }, + { 0x7874, 0x0000 }, + { 0x7875, 0x0000 }, + { 0x7876, 0x0000 }, + { 0x7877, 0x0000 }, + { 0x7878, 0x0000 }, + { 0x7879, 0x0000 }, + { 0x787a, 0x0000 }, + { 0x787b, 0x0000 }, + { 0x787c, 0x0000 }, + { 0x787d, 0x0000 }, + { 0x787e, 0x0000 }, + { 0x787f, 0x0000 }, + { 0x7880, 0x0000 }, + { 0x7881, 0x0000 }, + { 0x7882, 0x0000 }, + { 0x7883, 0x0000 }, + { 0x7884, 0x0000 }, + { 0x7885, 0x0000 }, + { 0x7886, 0x0000 }, + { 0x7887, 0x0000 }, + { 0x7888, 0x0000 }, + { 0x7889, 0x0000 }, + { 0x788a, 0x0000 }, + { 0x788b, 0x0000 }, + { 0x788c, 0x0000 }, + { 0x788d, 0x0000 }, + { 0x788e, 0x0000 }, + { 0x788f, 0x0000 }, + { 0x7890, 0x0000 }, + { 0x7891, 0x0000 }, + { 0x7892, 0x0000 }, + { 0x7893, 0x0000 }, + { 0x7894, 0x0000 }, + { 0x7895, 0x0000 }, + { 0x7896, 0x0000 }, + { 0x7897, 0x0000 }, + { 0x7898, 0x0000 }, + { 0x7899, 0x0000 }, + { 0x789a, 0x0000 }, + { 0x789b, 0x0000 }, + { 0x789c, 0x0000 }, + { 0x789d, 0x0000 }, + { 0x789e, 0x0000 }, + { 0x789f, 0x0000 }, + { 0x78a0, 0x0000 }, + { 0x78a1, 0x0000 }, + { 0x78a2, 0x0000 }, + { 0x78a3, 0x0000 }, + { 0x78a4, 0x0000 }, + { 0x78a5, 0x0000 }, + { 0x78a6, 0x0000 }, + { 0x78a7, 0x0000 }, + { 0x78a8, 0x0000 }, + { 0x78a9, 0x0000 }, + { 0x78aa, 0x0000 }, + { 0x78ab, 0x0000 }, + { 0x78ac, 0x0000 }, + { 0x78ad, 0x0000 }, + { 0x78ae, 0x0000 }, + { 0x78af, 0x0000 }, + { 0x78b0, 0x0000 }, + { 0x78b1, 0x0000 }, + { 0x78b2, 0x0000 }, + { 0x78b3, 0x0000 }, + { 0x78b4, 0x0000 }, + { 0x78b5, 0x0000 }, + { 0x78b6, 0x0000 }, + { 0x78b7, 0x0000 }, + { 0x78b8, 0x0000 }, + { 0x78b9, 0x0000 }, + { 0x78ba, 0x0000 }, + { 0x78bb, 0x0000 }, + { 0x78bc, 0x0000 }, + { 0x78bd, 0x0000 }, + { 0x78be, 0x0000 }, + { 0x78bf, 0x0000 }, + { 0x78c0, 0x0000 }, + { 0x78c1, 0x0000 }, + { 0x78c2, 0x0000 }, + { 0x78c3, 0x0000 }, + { 0x78c4, 0x0000 }, + { 0x78c5, 0x0000 }, + { 0x78c6, 0x0000 }, + { 0x78c7, 0x0000 }, + { 0x78c8, 0x0000 }, + { 0x78c9, 0x0000 }, + { 0x78ca, 0x0000 }, + { 0x78cb, 0x0000 }, + { 0x78cc, 0x0000 }, + { 0x78cd, 0x0000 }, + { 0x78ce, 0x0000 }, + { 0x78cf, 0x0000 }, + { 0x78d0, 0x0000 }, + { 0x78d1, 0x0000 }, + { 0x78d2, 0x0000 }, + { 0x78d3, 0x0000 }, + { 0x78d4, 0x0000 }, + { 0x78d5, 0x0000 }, + { 0x78d6, 0x0000 }, + { 0x78d7, 0x0000 }, + { 0x78d8, 0x0000 }, + { 0x78d9, 0x0000 }, + { 0x78da, 0x0000 }, + { 0x78db, 0x0000 }, + { 0x78dc, 0x0000 }, + { 0x78dd, 0x0000 }, + { 0x78de, 0x0000 }, + { 0x78df, 0x0000 }, + { 0x78e0, 0x0000 }, + { 0x78e1, 0x0000 }, + { 0x78e2, 0x0000 }, + { 0x78e3, 0x0000 }, + { 0x78e4, 0x0000 }, + { 0x78e5, 0x0000 }, + { 0x78e6, 0x0000 }, + { 0x78e7, 0x0000 }, + { 0x78e8, 0x0000 }, + { 0x78e9, 0x0000 }, + { 0x78ea, 0x0000 }, + { 0x78eb, 0x0000 }, + { 0x78ec, 0x0000 }, + { 0x78ed, 0x0000 }, + { 0x78ee, 0x0000 }, + { 0x78ef, 0x0000 }, + { 0x78f0, 0x0000 }, + { 0x78f1, 0x0000 }, + { 0x78f2, 0x0000 }, + { 0x78f3, 0x0000 }, + { 0x78f4, 0x0000 }, + { 0x78f5, 0x0000 }, + { 0x78f6, 0x0000 }, + { 0x78f7, 0x0000 }, + { 0x78f8, 0x0000 }, + { 0x78f9, 0x0000 }, + { 0x78fa, 0x0000 }, + { 0x78fb, 0x0000 }, + { 0x78fc, 0x0000 }, + { 0x78fd, 0x0000 }, + { 0x78fe, 0x0000 }, + { 0x78ff, 0x0000 }, + { 0x7900, 0x0000 }, + { 0x7901, 0x0000 }, + { 0x7902, 0x0000 }, + { 0x7903, 0x0000 }, + { 0x7904, 0x0000 }, + { 0x7905, 0x0000 }, + { 0x7906, 0x0000 }, + { 0x7907, 0x0000 }, + { 0x7908, 0x0000 }, + { 0x7909, 0x0000 }, + { 0x790a, 0x0000 }, + { 0x790b, 0x0000 }, + { 0x790c, 0x0000 }, + { 0x790d, 0x0000 }, + { 0x790e, 0x0000 }, + { 0x790f, 0x0000 }, + { 0x7910, 0x0000 }, + { 0x7911, 0x0000 }, + { 0x7912, 0x0000 }, + { 0x7913, 0x0000 }, + { 0x7914, 0x0000 }, + { 0x7915, 0x0000 }, + { 0x7916, 0x0000 }, + { 0x7917, 0x0000 }, + { 0x7918, 0x0000 }, + { 0x7919, 0x0000 }, + { 0x791a, 0x0000 }, + { 0x791b, 0x0000 }, + { 0x791c, 0x0000 }, + { 0x791d, 0x0000 }, + { 0x791e, 0x0000 }, + { 0x791f, 0x0000 }, + { 0x7920, 0x0000 }, + { 0x7921, 0x0000 }, + { 0x7922, 0x0000 }, + { 0x7923, 0x0000 }, + { 0x7924, 0x0000 }, + { 0x7925, 0x0000 }, + { 0x7926, 0x0000 }, + { 0x7927, 0x0000 }, + { 0x7928, 0x0000 }, + { 0x7929, 0x0000 }, + { 0x792a, 0x0000 }, + { 0x792b, 0x0000 }, + { 0x792c, 0x0000 }, + { 0x792d, 0x0000 }, + { 0x792e, 0x0000 }, + { 0x792f, 0x0000 }, + { 0x7930, 0x0000 }, + { 0x7931, 0x0000 }, + { 0x7932, 0x0000 }, + { 0x7933, 0x0000 }, + { 0x7934, 0x0000 }, + { 0x7935, 0x0000 }, + { 0x7936, 0x0000 }, + { 0x7937, 0x0000 }, + { 0x7938, 0x0000 }, + { 0x7939, 0x0000 }, + { 0x793a, 0x0000 }, + { 0x793b, 0x0000 }, + { 0x793c, 0x0000 }, + { 0x793d, 0x0000 }, + { 0x793e, 0x0000 }, + { 0x793f, 0x0000 }, + { 0x7940, 0x0000 }, + { 0x7941, 0x0000 }, + { 0x7942, 0x0000 }, + { 0x7943, 0x0000 }, + { 0x7944, 0x0000 }, + { 0x7945, 0x0000 }, + { 0x7946, 0x0000 }, + { 0x7947, 0x0000 }, + { 0x7948, 0x0000 }, + { 0x7949, 0x0000 }, + { 0x794a, 0x0000 }, + { 0x794b, 0x0000 }, + { 0x794c, 0x0000 }, + { 0x794d, 0x0000 }, + { 0x794e, 0x0000 }, + { 0x794f, 0x0000 }, + { 0x7950, 0x0000 }, + { 0x7951, 0x0000 }, + { 0x7952, 0x0000 }, + { 0x7953, 0x0000 }, + { 0x7954, 0x0000 }, + { 0x7955, 0x0000 }, + { 0x7956, 0x0000 }, + { 0x7957, 0x0000 }, + { 0x7958, 0x0000 }, + { 0x7959, 0x0000 }, + { 0x795a, 0x0000 }, + { 0x795b, 0x0000 }, + { 0x795c, 0x0000 }, + { 0x795d, 0x0000 }, + { 0x795e, 0x0000 }, + { 0x795f, 0x0000 }, + { 0x7960, 0x0000 }, + { 0x7961, 0x0000 }, + { 0x7962, 0x0000 }, + { 0x7963, 0x0000 }, + { 0x7964, 0x0000 }, + { 0x7965, 0x0000 }, + { 0x7966, 0x0000 }, + { 0x7967, 0x0000 }, + { 0x7968, 0x0000 }, + { 0x7969, 0x0000 }, + { 0x796a, 0x0000 }, + { 0x796b, 0x0000 }, + { 0x796c, 0x0000 }, + { 0x796d, 0x0000 }, + { 0x796e, 0x0000 }, + { 0x796f, 0x0000 }, + { 0x7970, 0x0000 }, + { 0x7971, 0x0000 }, + { 0x7972, 0x0000 }, + { 0x7973, 0x0000 }, + { 0x7974, 0x0000 }, + { 0x7975, 0x0000 }, + { 0x7976, 0x0000 }, + { 0x7977, 0x0000 }, + { 0x7978, 0x0000 }, + { 0x7979, 0x0000 }, + { 0x797a, 0x0000 }, + { 0x797b, 0x0000 }, + { 0x797c, 0x0000 }, + { 0x797d, 0x0000 }, + { 0x797e, 0x0000 }, + { 0x797f, 0x0000 }, + { 0x7980, 0x0000 }, + { 0x7981, 0x0000 }, + { 0x7982, 0x0000 }, + { 0x7983, 0x0000 }, + { 0x7984, 0x0000 }, + { 0x7985, 0x0000 }, + { 0x7986, 0x0000 }, + { 0x7987, 0x0000 }, + { 0x7988, 0x0000 }, + { 0x7989, 0x0000 }, + { 0x798a, 0x0000 }, + { 0x798b, 0x0000 }, + { 0x798c, 0x0000 }, + { 0x798d, 0x0000 }, + { 0x798e, 0x0000 }, + { 0x798f, 0x0000 }, + { 0x7990, 0x0000 }, + { 0x7991, 0x0000 }, + { 0x7992, 0x0000 }, + { 0x7993, 0x0000 }, + { 0x7994, 0x0000 }, + { 0x7995, 0x0000 }, + { 0x7996, 0x0000 }, + { 0x7997, 0x0000 }, + { 0x7998, 0x0000 }, + { 0x7999, 0x0000 }, + { 0x799a, 0x0000 }, + { 0x799b, 0x0000 }, + { 0x799c, 0x0000 }, + { 0x799d, 0x0000 }, + { 0x799e, 0x0000 }, + { 0x799f, 0x0000 }, + { 0x79a0, 0x0000 }, + { 0x79a1, 0x0000 }, + { 0x79a2, 0x0000 }, + { 0x79a3, 0x0000 }, + { 0x79a4, 0x0000 }, + { 0x79a5, 0x0000 }, + { 0x79a6, 0x0000 }, + { 0x79a7, 0x0000 }, + { 0x79a8, 0x0000 }, + { 0x79a9, 0x0000 }, + { 0x79aa, 0x0000 }, + { 0x79ab, 0x0000 }, + { 0x79ac, 0x0000 }, + { 0x79ad, 0x0000 }, + { 0x79ae, 0x0000 }, + { 0x79af, 0x0000 }, + { 0x79b0, 0x0000 }, + { 0x79b1, 0x0000 }, + { 0x79b2, 0x0000 }, + { 0x79b3, 0x0000 }, + { 0x79b4, 0x0000 }, + { 0x79b5, 0x0000 }, + { 0x79b6, 0x0000 }, + { 0x79b7, 0x0000 }, + { 0x79b8, 0x0000 }, + { 0x79b9, 0x0000 }, + { 0x79ba, 0x0000 }, + { 0x79bb, 0x0000 }, + { 0x79bc, 0x0000 }, + { 0x79bd, 0x0000 }, + { 0x79be, 0x0000 }, + { 0x79bf, 0x0000 }, + { 0x79c0, 0x0000 }, + { 0x79c1, 0x0000 }, + { 0x79c2, 0x0000 }, + { 0x79c3, 0x0000 }, + { 0x79c4, 0x0000 }, + { 0x79c5, 0x0000 }, + { 0x79c6, 0x0000 }, + { 0x79c7, 0x0000 }, + { 0x79c8, 0x0000 }, + { 0x79c9, 0x0000 }, + { 0x79ca, 0x0000 }, + { 0x79cb, 0x0000 }, + { 0x79cc, 0x0000 }, + { 0x79cd, 0x0000 }, + { 0x79ce, 0x0000 }, + { 0x79cf, 0x0000 }, + { 0x79d0, 0x0000 }, + { 0x79d1, 0x0000 }, + { 0x79d2, 0x0000 }, + { 0x79d3, 0x0000 }, + { 0x79d4, 0x0000 }, + { 0x79d5, 0x0000 }, + { 0x79d6, 0x0000 }, + { 0x79d7, 0x0000 }, + { 0x79d8, 0x0000 }, + { 0x79d9, 0x0000 }, + { 0x79da, 0x0000 }, + { 0x79db, 0x0000 }, + { 0x79dc, 0x0000 }, + { 0x79dd, 0x0000 }, + { 0x79de, 0x0000 }, + { 0x79df, 0x0000 }, + { 0x79e0, 0x0000 }, + { 0x79e1, 0x0000 }, + { 0x79e2, 0x0000 }, + { 0x79e3, 0x0000 }, + { 0x79e4, 0x0000 }, + { 0x79e5, 0x0000 }, + { 0x79e6, 0x0000 }, + { 0x79e7, 0x0000 }, + { 0x79e8, 0x0000 }, + { 0x79e9, 0x0000 }, + { 0x79ea, 0x0000 }, + { 0x79eb, 0x0000 }, + { 0x79ec, 0x0000 }, + { 0x79ed, 0x0000 }, + { 0x79ee, 0x0000 }, + { 0x79ef, 0x0000 }, + { 0x79f0, 0x0000 }, + { 0x79f1, 0x0000 }, + { 0x79f2, 0x0000 }, + { 0x79f3, 0x0000 }, + { 0x79f4, 0x0000 }, + { 0x79f5, 0x0000 }, + { 0x79f6, 0x0000 }, + { 0x79f7, 0x0000 }, + { 0x79f8, 0x0000 }, + { 0x79f9, 0x0000 }, + { 0x79fa, 0x0000 }, + { 0x79fb, 0x0000 }, + { 0x79fc, 0x0000 }, + { 0x79fd, 0x0000 }, + { 0x79fe, 0x0000 }, + { 0x79ff, 0x0000 }, + { 0x7a00, 0x0000 }, + { 0x7a01, 0x0000 }, + { 0x7a02, 0x0000 }, + { 0x7a03, 0x0000 }, + { 0x7a04, 0x0000 }, + { 0x7a05, 0x0000 }, + { 0x7a06, 0x0000 }, + { 0x7a07, 0x0000 }, + { 0x7a08, 0x0000 }, + { 0x7a09, 0x0000 }, + { 0x7a0a, 0x0000 }, + { 0x7a0b, 0x0000 }, + { 0x7a0c, 0x0000 }, + { 0x7a0d, 0x0000 }, + { 0x7a0e, 0x0000 }, + { 0x7a0f, 0x0000 }, + { 0x7a10, 0x0000 }, + { 0x7a11, 0x0000 }, + { 0x7a12, 0x0000 }, + { 0x7a13, 0x0000 }, + { 0x7a14, 0x0000 }, + { 0x7a15, 0x0000 }, + { 0x7a16, 0x0000 }, + { 0x7a17, 0x0000 }, + { 0x7a18, 0x0000 }, + { 0x7a19, 0x0000 }, + { 0x7a1a, 0x0000 }, + { 0x7a1b, 0x0000 }, + { 0x7a1c, 0x0000 }, + { 0x7a1d, 0x0000 }, + { 0x7a1e, 0x0000 }, + { 0x7a1f, 0x0000 }, + { 0x7a20, 0x0000 }, + { 0x7a21, 0x0000 }, + { 0x7a22, 0x0000 }, + { 0x7a23, 0x0000 }, + { 0x7a24, 0x0000 }, + { 0x7a25, 0x0000 }, + { 0x7a26, 0x0000 }, + { 0x7a27, 0x0000 }, + { 0x7a28, 0x0000 }, + { 0x7a29, 0x0000 }, + { 0x7a2a, 0x0000 }, + { 0x7a2b, 0x0000 }, + { 0x7a2c, 0x0000 }, + { 0x7a2d, 0x0000 }, + { 0x7a2e, 0x0000 }, + { 0x7a2f, 0x0000 }, + { 0x7a30, 0x0000 }, + { 0x7a31, 0x0000 }, + { 0x7a32, 0x0000 }, + { 0x7a33, 0x0000 }, + { 0x7a34, 0x0000 }, + { 0x7a35, 0x0000 }, + { 0x7a36, 0x0000 }, + { 0x7a37, 0x0000 }, + { 0x7a38, 0x0000 }, + { 0x7a39, 0x0000 }, + { 0x7a3a, 0x0000 }, + { 0x7a3b, 0x0000 }, + { 0x7a3c, 0x0000 }, + { 0x7a3d, 0x0000 }, + { 0x7a3e, 0x0000 }, + { 0x7a3f, 0x0000 }, + { 0x7a40, 0x0000 }, + { 0x7a41, 0x0000 }, + { 0x7a42, 0x0000 }, + { 0x7a43, 0x0000 }, + { 0x7a44, 0x0000 }, + { 0x7a45, 0x0000 }, + { 0x7a46, 0x0000 }, + { 0x7a47, 0x0000 }, + { 0x7a48, 0x0000 }, + { 0x7a49, 0x0000 }, + { 0x7a4a, 0x0000 }, + { 0x7a4b, 0x0000 }, + { 0x7a4c, 0x0000 }, + { 0x7a4d, 0x0000 }, + { 0x7a4e, 0x0000 }, + { 0x7a4f, 0x0000 }, + { 0x7a50, 0x0000 }, + { 0x7a51, 0x0000 }, + { 0x7a52, 0x0000 }, + { 0x7a53, 0x0000 }, + { 0x7a54, 0x0000 }, + { 0x7a55, 0x0000 }, + { 0x7a56, 0x0000 }, + { 0x7a57, 0x0000 }, + { 0x7a58, 0x0000 }, + { 0x7a59, 0x0000 }, + { 0x7a5a, 0x0000 }, + { 0x7a5b, 0x0000 }, + { 0x7a5c, 0x0000 }, + { 0x7a5d, 0x0000 }, + { 0x7a5e, 0x0000 }, + { 0x7a5f, 0x0000 }, + { 0x7a60, 0x0000 }, + { 0x7a61, 0x0000 }, + { 0x7a62, 0x0000 }, + { 0x7a63, 0x0000 }, + { 0x7a64, 0x0000 }, + { 0x7a65, 0x0000 }, + { 0x7a66, 0x0000 }, + { 0x7a67, 0x0000 }, + { 0x7a68, 0x0000 }, + { 0x7a69, 0x0000 }, + { 0x7a6a, 0x0000 }, + { 0x7a6b, 0x0000 }, + { 0x7a6c, 0x0000 }, + { 0x7a6d, 0x0000 }, + { 0x7a6e, 0x0000 }, + { 0x7a6f, 0x0000 }, + { 0x7a70, 0x0000 }, + { 0x7a71, 0x0000 }, + { 0x7a72, 0x0000 }, + { 0x7a73, 0x0000 }, + { 0x7a74, 0x0000 }, + { 0x7a75, 0x0000 }, + { 0x7a76, 0x0000 }, + { 0x7a77, 0x0000 }, + { 0x7a78, 0x0000 }, + { 0x7a79, 0x0000 }, + { 0x7a7a, 0x0000 }, + { 0x7a7b, 0x0000 }, + { 0x7a7c, 0x0000 }, + { 0x7a7d, 0x0000 }, + { 0x7a7e, 0x0000 }, + { 0x7a7f, 0x0000 }, + { 0x7a80, 0x0000 }, + { 0x7a81, 0x0000 }, + { 0x7a82, 0x0000 }, + { 0x7a83, 0x0000 }, + { 0x7a84, 0x0000 }, + { 0x7a85, 0x0000 }, + { 0x7a86, 0x0000 }, + { 0x7a87, 0x0000 }, + { 0x7a88, 0x0000 }, + { 0x7a89, 0x0000 }, + { 0x7a8a, 0x0000 }, + { 0x7a8b, 0x0000 }, + { 0x7a8c, 0x0000 }, + { 0x7a8d, 0x0000 }, + { 0x7a8e, 0x0000 }, + { 0x7a8f, 0x0000 }, + { 0x7a90, 0x0000 }, + { 0x7a91, 0x0000 }, + { 0x7a92, 0x0000 }, + { 0x7a93, 0x0000 }, + { 0x7a94, 0x0000 }, + { 0x7a95, 0x0000 }, + { 0x7a96, 0x0000 }, + { 0x7a97, 0x0000 }, + { 0x7a98, 0x0000 }, + { 0x7a99, 0x0000 }, + { 0x7a9a, 0x0000 }, + { 0x7a9b, 0x0000 }, + { 0x7a9c, 0x0000 }, + { 0x7a9d, 0x0000 }, + { 0x7a9e, 0x0000 }, + { 0x7a9f, 0x0000 }, + { 0x7aa0, 0x0000 }, + { 0x7aa1, 0x0000 }, + { 0x7aa2, 0x0000 }, + { 0x7aa3, 0x0000 }, + { 0x7aa4, 0x0000 }, + { 0x7aa5, 0x0000 }, + { 0x7aa6, 0x0000 }, + { 0x7aa7, 0x0000 }, + { 0x7aa8, 0x0000 }, + { 0x7aa9, 0x0000 }, + { 0x7aaa, 0x0000 }, + { 0x7aab, 0x0000 }, + { 0x7aac, 0x0000 }, + { 0x7aad, 0x0000 }, + { 0x7aae, 0x0000 }, + { 0x7aaf, 0x0000 }, + { 0x7ab0, 0x0000 }, + { 0x7ab1, 0x0000 }, + { 0x7ab2, 0x0000 }, + { 0x7ab3, 0x0000 }, + { 0x7ab4, 0x0000 }, + { 0x7ab5, 0x0000 }, + { 0x7ab6, 0x0000 }, + { 0x7ab7, 0x0000 }, + { 0x7ab8, 0x0000 }, + { 0x7ab9, 0x0000 }, + { 0x7aba, 0x0000 }, + { 0x7abb, 0x0000 }, + { 0x7abc, 0x0000 }, + { 0x7abd, 0x0000 }, + { 0x7abe, 0x0000 }, + { 0x7abf, 0x0000 }, + { 0x7ac0, 0x0000 }, + { 0x7ac1, 0x0000 }, + { 0x7ac2, 0x0000 }, + { 0x7ac3, 0x0000 }, + { 0x7ac4, 0x0000 }, + { 0x7ac5, 0x0000 }, + { 0x7ac6, 0x0000 }, + { 0x7ac7, 0x0000 }, + { 0x7ac8, 0x0000 }, + { 0x7ac9, 0x0000 }, + { 0x7aca, 0x0000 }, + { 0x7acb, 0x0000 }, + { 0x7acc, 0x0000 }, + { 0x7acd, 0x0000 }, + { 0x7ace, 0x0000 }, + { 0x7acf, 0x0000 }, + { 0x7ad0, 0x0000 }, + { 0x7ad1, 0x0000 }, + { 0x7ad2, 0x0000 }, + { 0x7ad3, 0x0000 }, + { 0x7ad4, 0x0000 }, + { 0x7ad5, 0x0000 }, + { 0x7ad6, 0x0000 }, + { 0x7ad7, 0x0000 }, + { 0x7ad8, 0x0000 }, + { 0x7ad9, 0x0000 }, + { 0x7ada, 0x0000 }, + { 0x7adb, 0x0000 }, + { 0x7adc, 0x0000 }, + { 0x7add, 0x0000 }, + { 0x7ade, 0x0000 }, + { 0x7adf, 0x0000 }, + { 0x7ae0, 0x0000 }, + { 0x7ae1, 0x0000 }, + { 0x7ae2, 0x0000 }, + { 0x7ae3, 0x0000 }, + { 0x7ae4, 0x0000 }, + { 0x7ae5, 0x0000 }, + { 0x7ae6, 0x0000 }, + { 0x7ae7, 0x0000 }, + { 0x7ae8, 0x0000 }, + { 0x7ae9, 0x0000 }, + { 0x7aea, 0x0000 }, + { 0x7aeb, 0x0000 }, + { 0x7aec, 0x0000 }, + { 0x7aed, 0x0000 }, + { 0x7aee, 0x0000 }, + { 0x7aef, 0x0000 }, + { 0x7af0, 0x0000 }, + { 0x7af1, 0x0000 }, + { 0x7af2, 0x0000 }, + { 0x7af3, 0x0000 }, + { 0x7af4, 0x0000 }, + { 0x7af5, 0x0000 }, + { 0x7af6, 0x0000 }, + { 0x7af7, 0x0000 }, + { 0x7af8, 0x0000 }, + { 0x7af9, 0x0000 }, + { 0x7afa, 0x0000 }, + { 0x7afb, 0x0000 }, + { 0x7afc, 0x0000 }, + { 0x7afd, 0x0000 }, + { 0x7afe, 0x0000 }, + { 0x7aff, 0x0000 }, + { 0x7b00, 0x0000 }, + { 0x7b01, 0x0000 }, + { 0x7b02, 0x0000 }, + { 0x7b03, 0x0000 }, + { 0x7b04, 0x0000 }, + { 0x7b05, 0x0000 }, + { 0x7b06, 0x0000 }, + { 0x7b07, 0x0000 }, + { 0x7b08, 0x0000 }, + { 0x7b09, 0x0000 }, + { 0x7b0a, 0x0000 }, + { 0x7b0b, 0x0000 }, + { 0x7b0c, 0x0000 }, + { 0x7b0d, 0x0000 }, + { 0x7b0e, 0x0000 }, + { 0x7b0f, 0x0000 }, + { 0x7b10, 0x0000 }, + { 0x7b11, 0x0000 }, + { 0x7b12, 0x0000 }, + { 0x7b13, 0x0000 }, + { 0x7b14, 0x0000 }, + { 0x7b15, 0x0000 }, + { 0x7b16, 0x0000 }, + { 0x7b17, 0x0000 }, + { 0x7b18, 0x0000 }, + { 0x7b19, 0x0000 }, + { 0x7b1a, 0x0000 }, + { 0x7b1b, 0x0000 }, + { 0x7b1c, 0x0000 }, + { 0x7b1d, 0x0000 }, + { 0x7b1e, 0x0000 }, + { 0x7b1f, 0x0000 }, + { 0x7b20, 0x0000 }, + { 0x7b21, 0x0000 }, + { 0x7b22, 0x0000 }, + { 0x7b23, 0x0000 }, + { 0x7b24, 0x0000 }, + { 0x7b25, 0x0000 }, + { 0x7b26, 0x0000 }, + { 0x7b27, 0x0000 }, + { 0x7b28, 0x0000 }, + { 0x7b29, 0x0000 }, + { 0x7b2a, 0x0000 }, + { 0x7b2b, 0x0000 }, + { 0x7b2c, 0x0000 }, + { 0x7b2d, 0x0000 }, + { 0x7b2e, 0x0000 }, + { 0x7b2f, 0x0000 }, + { 0x7b30, 0x0000 }, + { 0x7b31, 0x0000 }, + { 0x7b32, 0x0000 }, + { 0x7b33, 0x0000 }, + { 0x7b34, 0x0000 }, + { 0x7b35, 0x0000 }, + { 0x7b36, 0x0000 }, + { 0x7b37, 0x0000 }, + { 0x7b38, 0x0000 }, + { 0x7b39, 0x0000 }, + { 0x7b3a, 0x0000 }, + { 0x7b3b, 0x0000 }, + { 0x7b3c, 0x0000 }, + { 0x7b3d, 0x0000 }, + { 0x7b3e, 0x0000 }, + { 0x7b3f, 0x0000 }, + { 0x7b40, 0x0000 }, + { 0x7b41, 0x0000 }, + { 0x7b42, 0x0000 }, + { 0x7b43, 0x0000 }, + { 0x7b44, 0x0000 }, + { 0x7b45, 0x0000 }, + { 0x7b46, 0x0000 }, + { 0x7b47, 0x0000 }, + { 0x7b48, 0x0000 }, + { 0x7b49, 0x0000 }, + { 0x7b4a, 0x0000 }, + { 0x7b4b, 0x0000 }, + { 0x7b4c, 0x0000 }, + { 0x7b4d, 0x0000 }, + { 0x7b4e, 0x0000 }, + { 0x7b4f, 0x0000 }, + { 0x7b50, 0x0000 }, + { 0x7b51, 0x0000 }, + { 0x7b52, 0x0000 }, + { 0x7b53, 0x0000 }, + { 0x7b54, 0x0000 }, + { 0x7b55, 0x0000 }, + { 0x7b56, 0x0000 }, + { 0x7b57, 0x0000 }, + { 0x7b58, 0x0000 }, + { 0x7b59, 0x0000 }, + { 0x7b5a, 0x0000 }, + { 0x7b5b, 0x0000 }, + { 0x7b5c, 0x0000 }, + { 0x7b5d, 0x0000 }, + { 0x7b5e, 0x0000 }, + { 0x7b5f, 0x0000 }, + { 0x7b60, 0x0000 }, + { 0x7b61, 0x0000 }, + { 0x7b62, 0x0000 }, + { 0x7b63, 0x0000 }, + { 0x7b64, 0x0000 }, + { 0x7b65, 0x0000 }, + { 0x7b66, 0x0000 }, + { 0x7b67, 0x0000 }, + { 0x7b68, 0x0000 }, + { 0x7b69, 0x0000 }, + { 0x7b6a, 0x0000 }, + { 0x7b6b, 0x0000 }, + { 0x7b6c, 0x0000 }, + { 0x7b6d, 0x0000 }, + { 0x7b6e, 0x0000 }, + { 0x7b6f, 0x0000 }, + { 0x7b70, 0x0000 }, + { 0x7b71, 0x0000 }, + { 0x7b72, 0x0000 }, + { 0x7b73, 0x0000 }, + { 0x7b74, 0x0000 }, + { 0x7b75, 0x0000 }, + { 0x7b76, 0x0000 }, + { 0x7b77, 0x0000 }, + { 0x7b78, 0x0000 }, + { 0x7b79, 0x0000 }, + { 0x7b7a, 0x0000 }, + { 0x7b7b, 0x0000 }, + { 0x7b7c, 0x0000 }, + { 0x7b7d, 0x0000 }, + { 0x7b7e, 0x0000 }, + { 0x7b7f, 0x0000 }, + { 0x7b80, 0x0000 }, + { 0x7b81, 0x0000 }, + { 0x7b82, 0x0000 }, + { 0x7b83, 0x0000 }, + { 0x7b84, 0x0000 }, + { 0x7b85, 0x0000 }, + { 0x7b86, 0x0000 }, + { 0x7b87, 0x0000 }, + { 0x7b88, 0x0000 }, + { 0x7b89, 0x0000 }, + { 0x7b8a, 0x0000 }, + { 0x7b8b, 0x0000 }, + { 0x7b8c, 0x0000 }, + { 0x7b8d, 0x0000 }, + { 0x7b8e, 0x0000 }, + { 0x7b8f, 0x0000 }, + { 0x7b90, 0x0000 }, + { 0x7b91, 0x0000 }, + { 0x7b92, 0x0000 }, + { 0x7b93, 0x0000 }, + { 0x7b94, 0x0000 }, + { 0x7b95, 0x0000 }, + { 0x7b96, 0x0000 }, + { 0x7b97, 0x0000 }, + { 0x7b98, 0x0000 }, + { 0x7b99, 0x0000 }, + { 0x7b9a, 0x0000 }, + { 0x7b9b, 0x0000 }, + { 0x7b9c, 0x0000 }, + { 0x7b9d, 0x0000 }, + { 0x7b9e, 0x0000 }, + { 0x7b9f, 0x0000 }, + { 0x7ba0, 0x0000 }, + { 0x7ba1, 0x0000 }, + { 0x7ba2, 0x0000 }, + { 0x7ba3, 0x0000 }, + { 0x7ba4, 0x0000 }, + { 0x7ba5, 0x0000 }, + { 0x7ba6, 0x0000 }, + { 0x7ba7, 0x0000 }, + { 0x7ba8, 0x0000 }, + { 0x7ba9, 0x0000 }, + { 0x7baa, 0x0000 }, + { 0x7bab, 0x0000 }, + { 0x7bac, 0x0000 }, + { 0x7bad, 0x0000 }, + { 0x7bae, 0x0000 }, + { 0x7baf, 0x0000 }, + { 0x7bb0, 0x0000 }, + { 0x7bb1, 0x0000 }, + { 0x7bb2, 0x0000 }, + { 0x7bb3, 0x0000 }, + { 0x7bb4, 0x0000 }, + { 0x7bb5, 0x0000 }, + { 0x7bb6, 0x0000 }, + { 0x7bb7, 0x0000 }, + { 0x7bb8, 0x0000 }, + { 0x7bb9, 0x0000 }, + { 0x7bba, 0x0000 }, + { 0x7bbb, 0x0000 }, + { 0x7bbc, 0x0000 }, + { 0x7bbd, 0x0000 }, + { 0x7bbe, 0x0000 }, + { 0x7bbf, 0x0000 }, + { 0x7bc0, 0x0000 }, + { 0x7bc1, 0x0000 }, + { 0x7bc2, 0x0000 }, + { 0x7bc3, 0x0000 }, + { 0x7bc4, 0x0000 }, + { 0x7bc5, 0x0000 }, + { 0x7bc6, 0x0000 }, + { 0x7bc7, 0x0000 }, + { 0x7bc8, 0x0000 }, + { 0x7bc9, 0x0000 }, + { 0x7bca, 0x0000 }, + { 0x7bcb, 0x0000 }, + { 0x7bcc, 0x0000 }, + { 0x7bcd, 0x0000 }, + { 0x7bce, 0x0000 }, + { 0x7bcf, 0x0000 }, + { 0x7bd0, 0x0000 }, + { 0x7bd1, 0x0000 }, + { 0x7bd2, 0x0000 }, + { 0x7bd3, 0x0000 }, + { 0x7bd4, 0x0000 }, + { 0x7bd5, 0x0000 }, + { 0x7bd6, 0x0000 }, + { 0x7bd7, 0x0000 }, + { 0x7bd8, 0x0000 }, + { 0x7bd9, 0x0000 }, + { 0x7bda, 0x0000 }, + { 0x7bdb, 0x0000 }, + { 0x7bdc, 0x0000 }, + { 0x7bdd, 0x0000 }, + { 0x7bde, 0x0000 }, + { 0x7bdf, 0x0000 }, + { 0x7be0, 0x0000 }, + { 0x7be1, 0x0000 }, + { 0x7be2, 0x0000 }, + { 0x7be3, 0x0000 }, + { 0x7be4, 0x0000 }, + { 0x7be5, 0x0000 }, + { 0x7be6, 0x0000 }, + { 0x7be7, 0x0000 }, + { 0x7be8, 0x0000 }, + { 0x7be9, 0x0000 }, + { 0x7bea, 0x0000 }, + { 0x7beb, 0x0000 }, + { 0x7bec, 0x0000 }, + { 0x7bed, 0x0000 }, + { 0x7bee, 0x0000 }, + { 0x7bef, 0x0000 }, + { 0x7bf0, 0x0000 }, + { 0x7bf1, 0x0000 }, + { 0x7bf2, 0x0000 }, + { 0x7bf3, 0x0000 }, + { 0x7bf4, 0x0000 }, + { 0x7bf5, 0x0000 }, + { 0x7bf6, 0x0000 }, + { 0x7bf7, 0x0000 }, + { 0x7bf8, 0x0000 }, + { 0x7bf9, 0x0000 }, + { 0x7bfa, 0x0000 }, + { 0x7bfb, 0x0000 }, + { 0x7bfc, 0x0000 }, + { 0x7bfd, 0x0000 }, + { 0x7bfe, 0x0000 }, + { 0x7bff, 0x0000 }, + { 0x7c00, 0x0000 }, + { 0x7c01, 0x0000 }, + { 0x7c02, 0x0000 }, + { 0x7c03, 0x0000 }, + { 0x7c04, 0x0000 }, + { 0x7c05, 0x0000 }, + { 0x7c06, 0x0000 }, + { 0x7c07, 0x0000 }, + { 0x7c08, 0x0000 }, + { 0x7c09, 0x0000 }, + { 0x7c0a, 0x0000 }, + { 0x7c0b, 0x0000 }, + { 0x7c0c, 0x0000 }, + { 0x7c0d, 0x0000 }, + { 0x7c0e, 0x0000 }, + { 0x7c0f, 0x0000 }, + { 0x7c10, 0x0000 }, + { 0x7c11, 0x0000 }, + { 0x7c12, 0x0000 }, + { 0x7c13, 0x0000 }, + { 0x7c14, 0x0000 }, + { 0x7c15, 0x0000 }, + { 0x7c16, 0x0000 }, + { 0x7c17, 0x0000 }, + { 0x7c18, 0x0000 }, + { 0x7c19, 0x0000 }, + { 0x7c1a, 0x0000 }, + { 0x7c1b, 0x0000 }, + { 0x7c1c, 0x0000 }, + { 0x7c1d, 0x0000 }, + { 0x7c1e, 0x0000 }, + { 0x7c1f, 0x0000 }, + { 0x7c20, 0x0000 }, + { 0x7c21, 0x0000 }, + { 0x7c22, 0x0000 }, + { 0x7c23, 0x0000 }, + { 0x7c24, 0x0000 }, + { 0x7c25, 0x0000 }, + { 0x7c26, 0x0000 }, + { 0x7c27, 0x0000 }, + { 0x7c28, 0x0000 }, + { 0x7c29, 0x0000 }, + { 0x7c2a, 0x0000 }, + { 0x7c2b, 0x0000 }, + { 0x7c2c, 0x0000 }, + { 0x7c2d, 0x0000 }, + { 0x7c2e, 0x0000 }, + { 0x7c2f, 0x0000 }, + { 0x7c30, 0x0000 }, + { 0x7c31, 0x0000 }, + { 0x7c32, 0x0000 }, + { 0x7c33, 0x0000 }, + { 0x7c34, 0x0000 }, + { 0x7c35, 0x0000 }, + { 0x7c36, 0x0000 }, + { 0x7c37, 0x0000 }, + { 0x7c38, 0x0000 }, + { 0x7c39, 0x0000 }, + { 0x7c3a, 0x0000 }, + { 0x7c3b, 0x0000 }, + { 0x7c3c, 0x0000 }, + { 0x7c3d, 0x0000 }, + { 0x7c3e, 0x0000 }, + { 0x7c3f, 0x0000 }, + { 0x7c40, 0x0000 }, + { 0x7c41, 0x0000 }, + { 0x7c42, 0x0000 }, + { 0x7c43, 0x0000 }, + { 0x7c44, 0x0000 }, + { 0x7c45, 0x0000 }, + { 0x7c46, 0x0000 }, + { 0x7c47, 0x0000 }, + { 0x7c48, 0x0000 }, + { 0x7c49, 0x0000 }, + { 0x7c4a, 0x0000 }, + { 0x7c4b, 0x0000 }, + { 0x7c4c, 0x0000 }, + { 0x7c4d, 0x0000 }, + { 0x7c4e, 0x0000 }, + { 0x7c4f, 0x0000 }, + { 0x7c50, 0x0000 }, + { 0x7c51, 0x0000 }, + { 0x7c52, 0x0000 }, + { 0x7c53, 0x0000 }, + { 0x7c54, 0x0000 }, + { 0x7c55, 0x0000 }, + { 0x7c56, 0x0000 }, + { 0x7c57, 0x0000 }, + { 0x7c58, 0x0000 }, + { 0x7c59, 0x0000 }, + { 0x7c5a, 0x0000 }, + { 0x7c5b, 0x0000 }, + { 0x7c5c, 0x0000 }, + { 0x7c5d, 0x0000 }, + { 0x7c5e, 0x0000 }, + { 0x7c5f, 0x0000 }, + { 0x7c60, 0x0000 }, + { 0x7c61, 0x0000 }, + { 0x7c62, 0x0000 }, + { 0x7c63, 0x0000 }, + { 0x7c64, 0x0000 }, + { 0x7c65, 0x0000 }, + { 0x7c66, 0x0000 }, + { 0x7c67, 0x0000 }, + { 0x7c68, 0x0000 }, + { 0x7c69, 0x0000 }, + { 0x7c6a, 0x0000 }, + { 0x7c6b, 0x0000 }, + { 0x7c6c, 0x0000 }, + { 0x7c6d, 0x0000 }, + { 0x7c6e, 0x0000 }, + { 0x7c6f, 0x0000 }, + { 0x7c70, 0x0000 }, + { 0x7c71, 0x0000 }, + { 0x7c72, 0x0000 }, + { 0x7c73, 0x0000 }, + { 0x7c74, 0x0000 }, + { 0x7c75, 0x0000 }, + { 0x7c76, 0x0000 }, + { 0x7c77, 0x0000 }, + { 0x7c78, 0x0000 }, + { 0x7c79, 0x0000 }, + { 0x7c7a, 0x0000 }, + { 0x7c7b, 0x0000 }, + { 0x7c7c, 0x0000 }, + { 0x7c7d, 0x0000 }, + { 0x7c7e, 0x0000 }, + { 0x7c7f, 0x0000 }, + { 0x7c80, 0x0000 }, + { 0x7c81, 0x0000 }, + { 0x7c82, 0x0000 }, + { 0x7c83, 0x0000 }, + { 0x7c84, 0x0000 }, + { 0x7c85, 0x0000 }, + { 0x7c86, 0x0000 }, + { 0x7c87, 0x0000 }, + { 0x7c88, 0x0000 }, + { 0x7c89, 0x0000 }, + { 0x7c8a, 0x0000 }, + { 0x7c8b, 0x0000 }, + { 0x7c8c, 0x0000 }, + { 0x7c8d, 0x0000 }, + { 0x7c8e, 0x0000 }, + { 0x7c8f, 0x0000 }, + { 0x7c90, 0x0000 }, + { 0x7c91, 0x0000 }, + { 0x7c92, 0x0000 }, + { 0x7c93, 0x0000 }, + { 0x7c94, 0x0000 }, + { 0x7c95, 0x0000 }, + { 0x7c96, 0x0000 }, + { 0x7c97, 0x0000 }, + { 0x7c98, 0x0000 }, + { 0x7c99, 0x0000 }, + { 0x7c9a, 0x0000 }, + { 0x7c9b, 0x0000 }, + { 0x7c9c, 0x0000 }, + { 0x7c9d, 0x0000 }, + { 0x7c9e, 0x0000 }, + { 0x7c9f, 0x0000 }, + { 0x7ca0, 0x0000 }, + { 0x7ca1, 0x0000 }, + { 0x7ca2, 0x0000 }, + { 0x7ca3, 0x0000 }, + { 0x7ca4, 0x0000 }, + { 0x7ca5, 0x0000 }, + { 0x7ca6, 0x0000 }, + { 0x7ca7, 0x0000 }, + { 0x7ca8, 0x0000 }, + { 0x7ca9, 0x0000 }, + { 0x7caa, 0x0000 }, + { 0x7cab, 0x0000 }, + { 0x7cac, 0x0000 }, + { 0x7cad, 0x0000 }, + { 0x7cae, 0x0000 }, + { 0x7caf, 0x0000 }, + { 0x7cb0, 0x0000 }, + { 0x7cb1, 0x0000 }, + { 0x7cb2, 0x0000 }, + { 0x7cb3, 0x0000 }, + { 0x7cb4, 0x0000 }, + { 0x7cb5, 0x0000 }, + { 0x7cb6, 0x0000 }, + { 0x7cb7, 0x0000 }, + { 0x7cb8, 0x0000 }, + { 0x7cb9, 0x0000 }, + { 0x7cba, 0x0000 }, + { 0x7cbb, 0x0000 }, + { 0x7cbc, 0x0000 }, + { 0x7cbd, 0x0000 }, + { 0x7cbe, 0x0000 }, + { 0x7cbf, 0x0000 }, + { 0x7cc0, 0x0000 }, + { 0x7cc1, 0x0000 }, + { 0x7cc2, 0x0000 }, + { 0x7cc3, 0x0000 }, + { 0x7cc4, 0x0000 }, + { 0x7cc5, 0x0000 }, + { 0x7cc6, 0x0000 }, + { 0x7cc7, 0x0000 }, + { 0x7cc8, 0x0000 }, + { 0x7cc9, 0x0000 }, + { 0x7cca, 0x0000 }, + { 0x7ccb, 0x0000 }, + { 0x7ccc, 0x0000 }, + { 0x7ccd, 0x0000 }, + { 0x7cce, 0x0000 }, + { 0x7ccf, 0x0000 }, + { 0x7cd0, 0x0000 }, + { 0x7cd1, 0x0000 }, + { 0x7cd2, 0x0000 }, + { 0x7cd3, 0x0000 }, + { 0x7cd4, 0x0000 }, + { 0x7cd5, 0x0000 }, + { 0x7cd6, 0x0000 }, + { 0x7cd7, 0x0000 }, + { 0x7cd8, 0x0000 }, + { 0x7cd9, 0x0000 }, + { 0x7cda, 0x0000 }, + { 0x7cdb, 0x0000 }, + { 0x7cdc, 0x0000 }, + { 0x7cdd, 0x0000 }, + { 0x7cde, 0x0000 }, + { 0x7cdf, 0x0000 }, + { 0x7ce0, 0x0000 }, + { 0x7ce1, 0x0000 }, + { 0x7ce2, 0x0000 }, + { 0x7ce3, 0x0000 }, + { 0x7ce4, 0x0000 }, + { 0x7ce5, 0x0000 }, + { 0x7ce6, 0x0000 }, + { 0x7ce7, 0x0000 }, + { 0x7ce8, 0x0000 }, + { 0x7ce9, 0x0000 }, + { 0x7cea, 0x0000 }, + { 0x7ceb, 0x0000 }, + { 0x7cec, 0x0000 }, + { 0x7ced, 0x0000 }, + { 0x7cee, 0x0000 }, + { 0x7cef, 0x0000 }, + { 0x7cf0, 0x0000 }, + { 0x7cf1, 0x0000 }, + { 0x7cf2, 0x0000 }, + { 0x7cf3, 0x0000 }, + { 0x7cf4, 0x0000 }, + { 0x7cf5, 0x0000 }, + { 0x7cf6, 0x0000 }, + { 0x7cf7, 0x0000 }, + { 0x7cf8, 0x0000 }, + { 0x7cf9, 0x0000 }, + { 0x7cfa, 0x0000 }, + { 0x7cfb, 0x0000 }, + { 0x7cfc, 0x0000 }, + { 0x7cfd, 0x0000 }, + { 0x7cfe, 0x0000 }, + { 0x7cff, 0x0000 }, + { 0x7d00, 0x0000 }, + { 0x7d01, 0x0000 }, + { 0x7d02, 0x0000 }, + { 0x7d03, 0x0000 }, + { 0x7d04, 0x0000 }, + { 0x7d05, 0x0000 }, + { 0x7d06, 0x0000 }, + { 0x7d07, 0x0000 }, + { 0x7d08, 0x0000 }, + { 0x7d09, 0x0000 }, + { 0x7d0a, 0x0000 }, + { 0x7d0b, 0x0000 }, + { 0x7d0c, 0x0000 }, + { 0x7d0d, 0x0000 }, + { 0x7d0e, 0x0000 }, + { 0x7d0f, 0x0000 }, + { 0x7d10, 0x0000 }, + { 0x7d11, 0x0000 }, + { 0x7d12, 0x0000 }, + { 0x7d13, 0x0000 }, + { 0x7d14, 0x0000 }, + { 0x7d15, 0x0000 }, + { 0x7d16, 0x0000 }, + { 0x7d17, 0x0000 }, + { 0x7d18, 0x0000 }, + { 0x7d19, 0x0000 }, + { 0x7d1a, 0x0000 }, + { 0x7d1b, 0x0000 }, + { 0x7d1c, 0x0000 }, + { 0x7d1d, 0x0000 }, + { 0x7d1e, 0x0000 }, + { 0x7d1f, 0x0000 }, + { 0x7d20, 0x0000 }, + { 0x7d21, 0x0000 }, + { 0x7d22, 0x0000 }, + { 0x7d23, 0x0000 }, + { 0x7d24, 0x0000 }, + { 0x7d25, 0x0000 }, + { 0x7d26, 0x0000 }, + { 0x7d27, 0x0000 }, + { 0x7d28, 0x0000 }, + { 0x7d29, 0x0000 }, + { 0x7d2a, 0x0000 }, + { 0x7d2b, 0x0000 }, + { 0x7d2c, 0x0000 }, + { 0x7d2d, 0x0000 }, + { 0x7d2e, 0x0000 }, + { 0x7d2f, 0x0000 }, + { 0x7d30, 0x0000 }, + { 0x7d31, 0x0000 }, + { 0x7d32, 0x0000 }, + { 0x7d33, 0x0000 }, + { 0x7d34, 0x0000 }, + { 0x7d35, 0x0000 }, + { 0x7d36, 0x0000 }, + { 0x7d37, 0x0000 }, + { 0x7d38, 0x0000 }, + { 0x7d39, 0x0000 }, + { 0x7d3a, 0x0000 }, + { 0x7d3b, 0x0000 }, + { 0x7d3c, 0x0000 }, + { 0x7d3d, 0x0000 }, + { 0x7d3e, 0x0000 }, + { 0x7d3f, 0x0000 }, + { 0x7d40, 0x0000 }, + { 0x7d41, 0x0000 }, + { 0x7d42, 0x0000 }, + { 0x7d43, 0x0000 }, + { 0x7d44, 0x0000 }, + { 0x7d45, 0x0000 }, + { 0x7d46, 0x0000 }, + { 0x7d47, 0x0000 }, + { 0x7d48, 0x0000 }, + { 0x7d49, 0x0000 }, + { 0x7d4a, 0x0000 }, + { 0x7d4b, 0x0000 }, + { 0x7d4c, 0x0000 }, + { 0x7d4d, 0x0000 }, + { 0x7d4e, 0x0000 }, + { 0x7d4f, 0x0000 }, + { 0x7d50, 0x0000 }, + { 0x7d51, 0x0000 }, + { 0x7d52, 0x0000 }, + { 0x7d53, 0x0000 }, + { 0x7d54, 0x0000 }, + { 0x7d55, 0x0000 }, + { 0x7d56, 0x0000 }, + { 0x7d57, 0x0000 }, + { 0x7d58, 0x0000 }, + { 0x7d59, 0x0000 }, + { 0x7d5a, 0x0000 }, + { 0x7d5b, 0x0000 }, + { 0x7d5c, 0x0000 }, + { 0x7d5d, 0x0000 }, + { 0x7d5e, 0x0000 }, + { 0x7d5f, 0x0000 }, + { 0x7d60, 0x0000 }, + { 0x7d61, 0x0000 }, + { 0x7d62, 0x0000 }, + { 0x7d63, 0x0000 }, + { 0x7d64, 0x0000 }, + { 0x7d65, 0x0000 }, + { 0x7d66, 0x0000 }, + { 0x7d67, 0x0000 }, + { 0x7d68, 0x0000 }, + { 0x7d69, 0x0000 }, + { 0x7d6a, 0x0000 }, + { 0x7d6b, 0x0000 }, + { 0x7d6c, 0x0000 }, + { 0x7d6d, 0x0000 }, + { 0x7d6e, 0x0000 }, + { 0x7d6f, 0x0000 }, + { 0x7d70, 0x0000 }, + { 0x7d71, 0x0000 }, + { 0x7d72, 0x0000 }, + { 0x7d73, 0x0000 }, + { 0x7d74, 0x0000 }, + { 0x7d75, 0x0000 }, + { 0x7d76, 0x0000 }, + { 0x7d77, 0x0000 }, + { 0x7d78, 0x0000 }, + { 0x7d79, 0x0000 }, + { 0x7d7a, 0x0000 }, + { 0x7d7b, 0x0000 }, + { 0x7d7c, 0x0000 }, + { 0x7d7d, 0x0000 }, + { 0x7d7e, 0x0000 }, + { 0x7d7f, 0x0000 }, + { 0x7d80, 0x0000 }, + { 0x7d81, 0x0000 }, + { 0x7d82, 0x0000 }, + { 0x7d83, 0x0000 }, + { 0x7d84, 0x0000 }, + { 0x7d85, 0x0000 }, + { 0x7d86, 0x0000 }, + { 0x7d87, 0x0000 }, + { 0x7d88, 0x0000 }, + { 0x7d89, 0x0000 }, + { 0x7d8a, 0x0000 }, + { 0x7d8b, 0x0000 }, + { 0x7d8c, 0x0000 }, + { 0x7d8d, 0x0000 }, + { 0x7d8e, 0x0000 }, + { 0x7d8f, 0x0000 }, + { 0x7d90, 0x0000 }, + { 0x7d91, 0x0000 }, + { 0x7d92, 0x0000 }, + { 0x7d93, 0x0000 }, + { 0x7d94, 0x0000 }, + { 0x7d95, 0x0000 }, + { 0x7d96, 0x0000 }, + { 0x7d97, 0x0000 }, + { 0x7d98, 0x0000 }, + { 0x7d99, 0x0000 }, + { 0x7d9a, 0x0000 }, + { 0x7d9b, 0x0000 }, + { 0x7d9c, 0x0000 }, + { 0x7d9d, 0x0000 }, + { 0x7d9e, 0x0000 }, + { 0x7d9f, 0x0000 }, + { 0x7da0, 0x0000 }, + { 0x7da1, 0x0000 }, + { 0x7da2, 0x0000 }, + { 0x7da3, 0x0000 }, + { 0x7da4, 0x0000 }, + { 0x7da5, 0x0000 }, + { 0x7da6, 0x0000 }, + { 0x7da7, 0x0000 }, + { 0x7da8, 0x0000 }, + { 0x7da9, 0x0000 }, + { 0x7daa, 0x0000 }, + { 0x7dab, 0x0000 }, + { 0x7dac, 0x0000 }, + { 0x7dad, 0x0000 }, + { 0x7dae, 0x0000 }, + { 0x7daf, 0x0000 }, + { 0x7db0, 0x0000 }, + { 0x7db1, 0x0000 }, + { 0x7db2, 0x0000 }, + { 0x7db3, 0x0000 }, + { 0x7db4, 0x0000 }, + { 0x7db5, 0x0000 }, + { 0x7db6, 0x0000 }, + { 0x7db7, 0x0000 }, + { 0x7db8, 0x0000 }, + { 0x7db9, 0x0000 }, + { 0x7dba, 0x0000 }, + { 0x7dbb, 0x0000 }, + { 0x7dbc, 0x0000 }, + { 0x7dbd, 0x0000 }, + { 0x7dbe, 0x0000 }, + { 0x7dbf, 0x0000 }, + { 0x7dc0, 0x0000 }, + { 0x7dc1, 0x0000 }, + { 0x7dc2, 0x0000 }, + { 0x7dc3, 0x0000 }, + { 0x7dc4, 0x0000 }, + { 0x7dc5, 0x0000 }, + { 0x7dc6, 0x0000 }, + { 0x7dc7, 0x0000 }, + { 0x7dc8, 0x0000 }, + { 0x7dc9, 0x0000 }, + { 0x7dca, 0x0000 }, + { 0x7dcb, 0x0000 }, + { 0x7dcc, 0x0000 }, + { 0x7dcd, 0x0000 }, + { 0x7dce, 0x0000 }, + { 0x7dcf, 0x0000 }, + { 0x7dd0, 0x0000 }, + { 0x7dd1, 0x0000 }, + { 0x7dd2, 0x0000 }, + { 0x7dd3, 0x0000 }, + { 0x7dd4, 0x0000 }, + { 0x7dd5, 0x0000 }, + { 0x7dd6, 0x0000 }, + { 0x7dd7, 0x0000 }, + { 0x7dd8, 0x0000 }, + { 0x7dd9, 0x0000 }, + { 0x7dda, 0x0000 }, + { 0x7ddb, 0x0000 }, + { 0x7ddc, 0x0000 }, + { 0x7ddd, 0x0000 }, + { 0x7dde, 0x0000 }, + { 0x7ddf, 0x0000 }, + { 0x7de0, 0x0000 }, + { 0x7de1, 0x0000 }, + { 0x7de2, 0x0000 }, + { 0x7de3, 0x0000 }, + { 0x7de4, 0x0000 }, + { 0x7de5, 0x0000 }, + { 0x7de6, 0x0000 }, + { 0x7de7, 0x0000 }, + { 0x7de8, 0x0000 }, + { 0x7de9, 0x0000 }, + { 0x7dea, 0x0000 }, + { 0x7deb, 0x0000 }, + { 0x7dec, 0x0000 }, + { 0x7ded, 0x0000 }, + { 0x7dee, 0x0000 }, + { 0x7def, 0x0000 }, + { 0x7df0, 0x0000 }, + { 0x7df1, 0x0000 }, + { 0x7df2, 0x0000 }, + { 0x7df3, 0x0000 }, + { 0x7df4, 0x0000 }, + { 0x7df5, 0x0000 }, + { 0x7df6, 0x0000 }, + { 0x7df7, 0x0000 }, + { 0x7df8, 0x0000 }, + { 0x7df9, 0x0000 }, + { 0x7dfa, 0x0000 }, + { 0x7dfb, 0x0000 }, + { 0x7dfc, 0x0000 }, + { 0x7dfd, 0x0000 }, + { 0x7dfe, 0x0000 }, + { 0x7dff, 0x0000 }, + { 0x7e00, 0x0000 }, + { 0x7e01, 0x0000 }, + { 0x7e02, 0x0000 }, + { 0x7e03, 0x0000 }, + { 0x7e04, 0x0000 }, + { 0x7e05, 0x0000 }, + { 0x7e06, 0x0000 }, + { 0x7e07, 0x0000 }, + { 0x7e08, 0x0000 }, + { 0x7e09, 0x0000 }, + { 0x7e0a, 0x0000 }, + { 0x7e0b, 0x0000 }, + { 0x7e0c, 0x0000 }, + { 0x7e0d, 0x0000 }, + { 0x7e0e, 0x0000 }, + { 0x7e0f, 0x0000 }, + { 0x7e10, 0x0000 }, + { 0x7e11, 0x0000 }, + { 0x7e12, 0x0000 }, + { 0x7e13, 0x0000 }, + { 0x7e14, 0x0000 }, + { 0x7e15, 0x0000 }, + { 0x7e16, 0x0000 }, + { 0x7e17, 0x0000 }, + { 0x7e18, 0x0000 }, + { 0x7e19, 0x0000 }, + { 0x7e1a, 0x0000 }, + { 0x7e1b, 0x0000 }, + { 0x7e1c, 0x0000 }, + { 0x7e1d, 0x0000 }, + { 0x7e1e, 0x0000 }, + { 0x7e1f, 0x0000 }, + { 0x7e20, 0x0000 }, + { 0x7e21, 0x0000 }, + { 0x7e22, 0x0000 }, + { 0x7e23, 0x0000 }, + { 0x7e24, 0x0000 }, + { 0x7e25, 0x0000 }, + { 0x7e26, 0x0000 }, + { 0x7e27, 0x0000 }, + { 0x7e28, 0x0000 }, + { 0x7e29, 0x0000 }, + { 0x7e2a, 0x0000 }, + { 0x7e2b, 0x0000 }, + { 0x7e2c, 0x0000 }, + { 0x7e2d, 0x0000 }, + { 0x7e2e, 0x0000 }, + { 0x7e2f, 0x0000 }, + { 0x7e30, 0x0000 }, + { 0x7e31, 0x0000 }, + { 0x7e32, 0x0000 }, + { 0x7e33, 0x0000 }, + { 0x7e34, 0x0000 }, + { 0x7e35, 0x0000 }, + { 0x7e36, 0x0000 }, + { 0x7e37, 0x0000 }, + { 0x7e38, 0x0000 }, + { 0x7e39, 0x0000 }, + { 0x7e3a, 0x0000 }, + { 0x7e3b, 0x0000 }, + { 0x7e3c, 0x0000 }, + { 0x7e3d, 0x0000 }, + { 0x7e3e, 0x0000 }, + { 0x7e3f, 0x0000 }, + { 0x7e40, 0x0000 }, + { 0x7e41, 0x0000 }, + { 0x7e42, 0x0000 }, + { 0x7e43, 0x0000 }, + { 0x7e44, 0x0000 }, + { 0x7e45, 0x0000 }, + { 0x7e46, 0x0000 }, + { 0x7e47, 0x0000 }, + { 0x7e48, 0x0000 }, + { 0x7e49, 0x0000 }, + { 0x7e4a, 0x0000 }, + { 0x7e4b, 0x0000 }, + { 0x7e4c, 0x0000 }, + { 0x7e4d, 0x0000 }, + { 0x7e4e, 0x0000 }, + { 0x7e4f, 0x0000 }, + { 0x7e50, 0x0000 }, + { 0x7e51, 0x0000 }, + { 0x7e52, 0x0000 }, + { 0x7e53, 0x0000 }, + { 0x7e54, 0x0000 }, + { 0x7e55, 0x0000 }, + { 0x7e56, 0x0000 }, + { 0x7e57, 0x0000 }, + { 0x7e58, 0x0000 }, + { 0x7e59, 0x0000 }, + { 0x7e5a, 0x0000 }, + { 0x7e5b, 0x0000 }, + { 0x7e5c, 0x0000 }, + { 0x7e5d, 0x0000 }, + { 0x7e5e, 0x0000 }, + { 0x7e5f, 0x0000 }, + { 0x7e60, 0x0000 }, + { 0x7e61, 0x0000 }, + { 0x7e62, 0x0000 }, + { 0x7e63, 0x0000 }, + { 0x7e64, 0x0000 }, + { 0x7e65, 0x0000 }, + { 0x7e66, 0x0000 }, + { 0x7e67, 0x0000 }, + { 0x7e68, 0x0000 }, + { 0x7e69, 0x0000 }, + { 0x7e6a, 0x0000 }, + { 0x7e6b, 0x0000 }, + { 0x7e6c, 0x0000 }, + { 0x7e6d, 0x0000 }, + { 0x7e6e, 0x0000 }, + { 0x7e6f, 0x0000 }, + { 0x7e70, 0x0000 }, + { 0x7e71, 0x0000 }, + { 0x7e72, 0x0000 }, + { 0x7e73, 0x0000 }, + { 0x7e74, 0x0000 }, + { 0x7e75, 0x0000 }, + { 0x7e76, 0x0000 }, + { 0x7e77, 0x0000 }, + { 0x7e78, 0x0000 }, + { 0x7e79, 0x0000 }, + { 0x7e7a, 0x0000 }, + { 0x7e7b, 0x0000 }, + { 0x7e7c, 0x0000 }, + { 0x7e7d, 0x0000 }, + { 0x7e7e, 0x0000 }, + { 0x7e7f, 0x0000 }, + { 0x7e80, 0x0000 }, + { 0x7e81, 0x0000 }, + { 0x7e82, 0x0000 }, + { 0x7e83, 0x0000 }, + { 0x7e84, 0x0000 }, + { 0x7e85, 0x0000 }, + { 0x7e86, 0x0000 }, + { 0x7e87, 0x0000 }, + { 0x7e88, 0x0000 }, + { 0x7e89, 0x0000 }, + { 0x7e8a, 0x0000 }, + { 0x7e8b, 0x0000 }, + { 0x7e8c, 0x0000 }, + { 0x7e8d, 0x0000 }, + { 0x7e8e, 0x0000 }, + { 0x7e8f, 0x0000 }, + { 0x7e90, 0x0000 }, + { 0x7e91, 0x0000 }, + { 0x7e92, 0x0000 }, + { 0x7e93, 0x0000 }, + { 0x7e94, 0x0000 }, + { 0x7e95, 0x0000 }, + { 0x7e96, 0x0000 }, + { 0x7e97, 0x0000 }, + { 0x7e98, 0x0000 }, + { 0x7e99, 0x0000 }, + { 0x7e9a, 0x0000 }, + { 0x7e9b, 0x0000 }, + { 0x7e9c, 0x0000 }, + { 0x7e9d, 0x0000 }, + { 0x7e9e, 0x0000 }, + { 0x7e9f, 0x0000 }, + { 0x7ea0, 0x0000 }, + { 0x7ea1, 0x0000 }, + { 0x7ea2, 0x0000 }, + { 0x7ea3, 0x0000 }, + { 0x7ea4, 0x0000 }, + { 0x7ea5, 0x0000 }, + { 0x7ea6, 0x0000 }, + { 0x7ea7, 0x0000 }, + { 0x7ea8, 0x0000 }, + { 0x7ea9, 0x0000 }, + { 0x7eaa, 0x0000 }, + { 0x7eab, 0x0000 }, + { 0x7eac, 0x0000 }, + { 0x7ead, 0x0000 }, + { 0x7eae, 0x0000 }, + { 0x7eaf, 0x0000 }, + { 0x7eb0, 0x0000 }, + { 0x7eb1, 0x0000 }, + { 0x7eb2, 0x0000 }, + { 0x7eb3, 0x0000 }, + { 0x7eb4, 0x0000 }, + { 0x7eb5, 0x0000 }, + { 0x7eb6, 0x0000 }, + { 0x7eb7, 0x0000 }, + { 0x7eb8, 0x0000 }, + { 0x7eb9, 0x0000 }, + { 0x7eba, 0x0000 }, + { 0x7ebb, 0x0000 }, + { 0x7ebc, 0x0000 }, + { 0x7ebd, 0x0000 }, + { 0x7ebe, 0x0000 }, + { 0x7ebf, 0x0000 }, + { 0x7ec0, 0x0000 }, + { 0x7ec1, 0x0000 }, + { 0x7ec2, 0x0000 }, + { 0x7ec3, 0x0000 }, + { 0x7ec4, 0x0000 }, + { 0x7ec5, 0x0000 }, + { 0x7ec6, 0x0000 }, + { 0x7ec7, 0x0000 }, + { 0x7ec8, 0x0000 }, + { 0x7ec9, 0x0000 }, + { 0x7eca, 0x0000 }, + { 0x7ecb, 0x0000 }, + { 0x7ecc, 0x0000 }, + { 0x7ecd, 0x0000 }, + { 0x7ece, 0x0000 }, + { 0x7ecf, 0x0000 }, + { 0x7ed0, 0x0000 }, + { 0x7ed1, 0x0000 }, + { 0x7ed2, 0x0000 }, + { 0x7ed3, 0x0000 }, + { 0x7ed4, 0x0000 }, + { 0x7ed5, 0x0000 }, + { 0x7ed6, 0x0000 }, + { 0x7ed7, 0x0000 }, + { 0x7ed8, 0x0000 }, + { 0x7ed9, 0x0000 }, + { 0x7eda, 0x0000 }, + { 0x7edb, 0x0000 }, + { 0x7edc, 0x0000 }, + { 0x7edd, 0x0000 }, + { 0x7ede, 0x0000 }, + { 0x7edf, 0x0000 }, + { 0x7ee0, 0x0000 }, + { 0x7ee1, 0x0000 }, + { 0x7ee2, 0x0000 }, + { 0x7ee3, 0x0000 }, + { 0x7ee4, 0x0000 }, + { 0x7ee5, 0x0000 }, + { 0x7ee6, 0x0000 }, + { 0x7ee7, 0x0000 }, + { 0x7ee8, 0x0000 }, + { 0x7ee9, 0x0000 }, + { 0x7eea, 0x0000 }, + { 0x7eeb, 0x0000 }, + { 0x7eec, 0x0000 }, + { 0x7eed, 0x0000 }, + { 0x7eee, 0x0000 }, + { 0x7eef, 0x0000 }, + { 0x7ef0, 0x0000 }, + { 0x7ef1, 0x0000 }, + { 0x7ef2, 0x0000 }, + { 0x7ef3, 0x0000 }, + { 0x7ef4, 0x0000 }, + { 0x7ef5, 0x0000 }, + { 0x7ef6, 0x0000 }, + { 0x7ef7, 0x0000 }, + { 0x7ef8, 0x0000 }, + { 0x7ef9, 0x0000 }, + { 0x7efa, 0x0000 }, + { 0x7efb, 0x0000 }, + { 0x7efc, 0x0000 }, + { 0x7efd, 0x0000 }, + { 0x7efe, 0x0000 }, + { 0x7eff, 0x0000 }, + { 0x7f00, 0x0000 }, + { 0x7f01, 0x0000 }, + { 0x7f02, 0x0000 }, + { 0x7f03, 0x0000 }, + { 0x7f04, 0x0000 }, + { 0x7f05, 0x0000 }, + { 0x7f06, 0x0000 }, + { 0x7f07, 0x0000 }, + { 0x7f08, 0x0000 }, + { 0x7f09, 0x0000 }, + { 0x7f0a, 0x0000 }, + { 0x7f0b, 0x0000 }, + { 0x7f0c, 0x0000 }, + { 0x7f0d, 0x0000 }, + { 0x7f0e, 0x0000 }, + { 0x7f0f, 0x0000 }, + { 0x7f10, 0x0000 }, + { 0x7f11, 0x0000 }, + { 0x7f12, 0x0000 }, + { 0x7f13, 0x0000 }, + { 0x7f14, 0x0000 }, + { 0x7f15, 0x0000 }, + { 0x7f16, 0x0000 }, + { 0x7f17, 0x0000 }, + { 0x7f18, 0x0000 }, + { 0x7f19, 0x0000 }, + { 0x7f1a, 0x0000 }, + { 0x7f1b, 0x0000 }, + { 0x7f1c, 0x0000 }, + { 0x7f1d, 0x0000 }, + { 0x7f1e, 0x0000 }, + { 0x7f1f, 0x0000 }, + { 0x7f20, 0x0000 }, + { 0x7f21, 0x0000 }, + { 0x7f22, 0x0000 }, + { 0x7f23, 0x0000 }, + { 0x7f24, 0x0000 }, + { 0x7f25, 0x0000 }, + { 0x7f26, 0x0000 }, + { 0x7f27, 0x0000 }, + { 0x7f28, 0x0000 }, + { 0x7f29, 0x0000 }, + { 0x7f2a, 0x0000 }, + { 0x7f2b, 0x0000 }, + { 0x7f2c, 0x0000 }, + { 0x7f2d, 0x0000 }, + { 0x7f2e, 0x0000 }, + { 0x7f2f, 0x0000 }, + { 0x7f30, 0x0000 }, + { 0x7f31, 0x0000 }, + { 0x7f32, 0x0000 }, + { 0x7f33, 0x0000 }, + { 0x7f34, 0x0000 }, + { 0x7f35, 0x0000 }, + { 0x7f36, 0x0000 }, + { 0x7f37, 0x0000 }, + { 0x7f38, 0x0000 }, + { 0x7f39, 0x0000 }, + { 0x7f3a, 0x0000 }, + { 0x7f3b, 0x0000 }, + { 0x7f3c, 0x0000 }, + { 0x7f3d, 0x0000 }, + { 0x7f3e, 0x0000 }, + { 0x7f3f, 0x0000 }, + { 0x7f40, 0x0000 }, + { 0x7f41, 0x0000 }, + { 0x7f42, 0x0000 }, + { 0x7f43, 0x0000 }, + { 0x7f44, 0x0000 }, + { 0x7f45, 0x0000 }, + { 0x7f46, 0x0000 }, + { 0x7f47, 0x0000 }, + { 0x7f48, 0x0000 }, + { 0x7f49, 0x0000 }, + { 0x7f4a, 0x0000 }, + { 0x7f4b, 0x0000 }, + { 0x7f4c, 0x0000 }, + { 0x7f4d, 0x0000 }, + { 0x7f4e, 0x0000 }, + { 0x7f4f, 0x0000 }, + { 0x7f50, 0x0000 }, + { 0x7f51, 0x0000 }, + { 0x7f52, 0x0000 }, + { 0x7f53, 0x0000 }, + { 0x7f54, 0x0000 }, + { 0x7f55, 0x0000 }, + { 0x7f56, 0x0000 }, + { 0x7f57, 0x0000 }, + { 0x7f58, 0x0000 }, + { 0x7f59, 0x0000 }, + { 0x7f5a, 0x0000 }, + { 0x7f5b, 0x0000 }, + { 0x7f5c, 0x0000 }, + { 0x7f5d, 0x0000 }, + { 0x7f5e, 0x0000 }, + { 0x7f5f, 0x0000 }, + { 0x7f60, 0x0000 }, + { 0x7f61, 0x0000 }, + { 0x7f62, 0x0000 }, + { 0x7f63, 0x0000 }, + { 0x7f64, 0x0000 }, + { 0x7f65, 0x0000 }, + { 0x7f66, 0x0000 }, + { 0x7f67, 0x0000 }, + { 0x7f68, 0x0000 }, + { 0x7f69, 0x0000 }, + { 0x7f6a, 0x0000 }, + { 0x7f6b, 0x0000 }, + { 0x7f6c, 0x0000 }, + { 0x7f6d, 0x0000 }, + { 0x7f6e, 0x0000 }, + { 0x7f6f, 0x0000 }, + { 0x7f70, 0x0000 }, + { 0x7f71, 0x0000 }, + { 0x7f72, 0x0000 }, + { 0x7f73, 0x0000 }, + { 0x7f74, 0x0000 }, + { 0x7f75, 0x0000 }, + { 0x7f76, 0x0000 }, + { 0x7f77, 0x0000 }, + { 0x7f78, 0x0000 }, + { 0x7f79, 0x0000 }, + { 0x7f7a, 0x0000 }, + { 0x7f7b, 0x0000 }, + { 0x7f7c, 0x0000 }, + { 0x7f7d, 0x0000 }, + { 0x7f7e, 0x0000 }, + { 0x7f7f, 0x0000 }, + { 0x7f80, 0x0000 }, + { 0x7f81, 0x0000 }, + { 0x7f82, 0x0000 }, + { 0x7f83, 0x0000 }, + { 0x7f84, 0x0000 }, + { 0x7f85, 0x0000 }, + { 0x7f86, 0x0000 }, + { 0x7f87, 0x0000 }, + { 0x7f88, 0x0000 }, + { 0x7f89, 0x0000 }, + { 0x7f8a, 0x0000 }, + { 0x7f8b, 0x0000 }, + { 0x7f8c, 0x0000 }, + { 0x7f8d, 0x0000 }, + { 0x7f8e, 0x0000 }, + { 0x7f8f, 0x0000 }, + { 0x7f90, 0x0000 }, + { 0x7f91, 0x0000 }, + { 0x7f92, 0x0000 }, + { 0x7f93, 0x0000 }, + { 0x7f94, 0x0000 }, + { 0x7f95, 0x0000 }, + { 0x7f96, 0x0000 }, + { 0x7f97, 0x0000 }, + { 0x7f98, 0x0000 }, + { 0x7f99, 0x0000 }, + { 0x7f9a, 0x0000 }, + { 0x7f9b, 0x0000 }, + { 0x7f9c, 0x0000 }, + { 0x7f9d, 0x0000 }, + { 0x7f9e, 0x0000 }, + { 0x7f9f, 0x0000 }, + { 0x7fa0, 0x0000 }, + { 0x7fa1, 0x0000 }, + { 0x7fa2, 0x0000 }, + { 0x7fa3, 0x0000 }, + { 0x7fa4, 0x0000 }, + { 0x7fa5, 0x0000 }, + { 0x7fa6, 0x0000 }, + { 0x7fa7, 0x0000 }, + { 0x7fa8, 0x0000 }, + { 0x7fa9, 0x0000 }, + { 0x7faa, 0x0000 }, + { 0x7fab, 0x0000 }, + { 0x7fac, 0x0000 }, + { 0x7fad, 0x0000 }, + { 0x7fae, 0x0000 }, + { 0x7faf, 0x0000 }, + { 0x7fb0, 0x0000 }, + { 0x7fb1, 0x0000 }, + { 0x7fb2, 0x0000 }, + { 0x7fb3, 0x0000 }, + { 0x7fb4, 0x0000 }, + { 0x7fb5, 0x0000 }, + { 0x7fb6, 0x0000 }, + { 0x7fb7, 0x0000 }, + { 0x7fb8, 0x0000 }, + { 0x7fb9, 0x0000 }, + { 0x7fba, 0x0000 }, + { 0x7fbb, 0x0000 }, + { 0x7fbc, 0x0000 }, + { 0x7fbd, 0x0000 }, + { 0x7fbe, 0x0000 }, + { 0x7fbf, 0x0000 }, + { 0x7fc0, 0x0000 }, + { 0x7fc1, 0x0000 }, + { 0x7fc2, 0x0000 }, + { 0x7fc3, 0x0000 }, + { 0x7fc4, 0x0000 }, + { 0x7fc5, 0x0000 }, + { 0x7fc6, 0x0000 }, + { 0x7fc7, 0x0000 }, + { 0x7fc8, 0x0000 }, + { 0x7fc9, 0x0000 }, + { 0x7fca, 0x0000 }, + { 0x7fcb, 0x0000 }, + { 0x7fcc, 0x0000 }, + { 0x7fcd, 0x0000 }, + { 0x7fce, 0x0000 }, + { 0x7fcf, 0x0000 }, + { 0x7fd0, 0x0000 }, + { 0x7fd1, 0x0000 }, + { 0x7fd2, 0x0000 }, + { 0x7fd3, 0x0000 }, + { 0x7fd4, 0x0000 }, + { 0x7fd5, 0x0000 }, + { 0x7fd6, 0x0000 }, + { 0x7fd7, 0x0000 }, + { 0x7fd8, 0x0000 }, + { 0x7fd9, 0x0000 }, + { 0x7fda, 0x0000 }, + { 0x7fdb, 0x0000 }, + { 0x7fdc, 0x0000 }, + { 0x7fdd, 0x0000 }, + { 0x7fde, 0x0000 }, + { 0x7fdf, 0x0000 }, + { 0x7fe0, 0x0000 }, + { 0x7fe1, 0x0000 }, + { 0x7fe2, 0x0000 }, + { 0x7fe3, 0x0000 }, + { 0x7fe4, 0x0000 }, + { 0x7fe5, 0x0000 }, + { 0x7fe6, 0x0000 }, + { 0x7fe7, 0x0000 }, + { 0x7fe8, 0x0000 }, + { 0x7fe9, 0x0000 }, + { 0x7fea, 0x0000 }, + { 0x7feb, 0x0000 }, + { 0x7fec, 0x0000 }, + { 0x7fed, 0x0000 }, + { 0x7fee, 0x0000 }, + { 0x7fef, 0x0000 }, + { 0x7ff0, 0x0000 }, + { 0x7ff1, 0x0000 }, + { 0x7ff2, 0x0000 }, + { 0x7ff3, 0x0000 }, + { 0x7ff4, 0x0000 }, + { 0x7ff5, 0x0000 }, + { 0x7ff6, 0x0000 }, + { 0x7ff7, 0x0000 }, + { 0x7ff8, 0x0000 }, + { 0x7ff9, 0x0000 }, + { 0x7ffa, 0x0000 }, + { 0x7ffb, 0x0000 }, + { 0x7ffc, 0x0000 }, + { 0x7ffd, 0x0000 }, + { 0x7ffe, 0x0000 }, + { 0x7fff, 0x0000 }, + { 0x8000, 0x0000 }, + { 0x8001, 0x0000 }, + { 0x8002, 0x0000 }, + { 0x8003, 0x0000 }, + { 0x8004, 0x0000 }, + { 0x8005, 0x0000 }, + { 0x8006, 0x0000 }, + { 0x8007, 0x0000 }, + { 0x8008, 0x0000 }, + { 0x8009, 0x0000 }, + { 0x800a, 0x0000 }, + { 0x800b, 0x0000 }, + { 0x800c, 0x0000 }, + { 0x800d, 0x0000 }, + { 0x800e, 0x0000 }, + { 0x800f, 0x0000 }, + { 0x8010, 0x0000 }, + { 0x8011, 0x0000 }, + { 0x8012, 0x0000 }, + { 0x8013, 0x0000 }, + { 0x8014, 0x0000 }, + { 0x8015, 0x0000 }, + { 0x8016, 0x0000 }, + { 0x8017, 0x0000 }, + { 0x8018, 0x0000 }, + { 0x8019, 0x0000 }, + { 0x801a, 0x0000 }, + { 0x801b, 0x0000 }, + { 0x801c, 0x0000 }, + { 0x801d, 0x0000 }, + { 0x801e, 0x0000 }, + { 0x801f, 0x0000 }, + { 0x8020, 0x0000 }, + { 0x8021, 0x0000 }, + { 0x8022, 0x0000 }, + { 0x8023, 0x0000 }, + { 0x8024, 0x0000 }, + { 0x8025, 0x0000 }, + { 0x8026, 0x0000 }, + { 0x8027, 0x0000 }, + { 0x8028, 0x0000 }, + { 0x8029, 0x0000 }, + { 0x802a, 0x0000 }, + { 0x802b, 0x0000 }, + { 0x802c, 0x0000 }, + { 0x802d, 0x0000 }, + { 0x802e, 0x0000 }, + { 0x802f, 0x0000 }, + { 0x8030, 0x0000 }, + { 0x8031, 0x0000 }, + { 0x8032, 0x0000 }, + { 0x8033, 0x0000 }, + { 0x8034, 0x0000 }, + { 0x8035, 0x0000 }, + { 0x8036, 0x0000 }, + { 0x8037, 0x0000 }, + { 0x8038, 0x0000 }, + { 0x8039, 0x0000 }, + { 0x803a, 0x0000 }, + { 0x803b, 0x0000 }, + { 0x803c, 0x0000 }, + { 0x803d, 0x0000 }, + { 0x803e, 0x0000 }, + { 0x803f, 0x0000 }, + { 0x8040, 0x0000 }, + { 0x8041, 0x0000 }, + { 0x8042, 0x0000 }, + { 0x8043, 0x0000 }, + { 0x8044, 0x0000 }, + { 0x8045, 0x0000 }, + { 0x8046, 0x0000 }, + { 0x8047, 0x0000 }, + { 0x8048, 0x0000 }, + { 0x8049, 0x0000 }, + { 0x804a, 0x0000 }, + { 0x804b, 0x0000 }, + { 0x804c, 0x0000 }, + { 0x804d, 0x0000 }, + { 0x804e, 0x0000 }, + { 0x804f, 0x0000 }, + { 0x8050, 0x0000 }, + { 0x8051, 0x0000 }, + { 0x8052, 0x0000 }, + { 0x8053, 0x0000 }, + { 0x8054, 0x0000 }, + { 0x8055, 0x0000 }, + { 0x8056, 0x0000 }, + { 0x8057, 0x0000 }, + { 0x8058, 0x0000 }, + { 0x8059, 0x0000 }, + { 0x805a, 0x0000 }, + { 0x805b, 0x0000 }, + { 0x805c, 0x0000 }, + { 0x805d, 0x0000 }, + { 0x805e, 0x0000 }, + { 0x805f, 0x0000 }, + { 0x8060, 0x0000 }, + { 0x8061, 0x0000 }, + { 0x8062, 0x0000 }, + { 0x8063, 0x0000 }, + { 0x8064, 0x0000 }, + { 0x8065, 0x0000 }, + { 0x8066, 0x0000 }, + { 0x8067, 0x0000 }, + { 0x8068, 0x0000 }, + { 0x8069, 0x0000 }, + { 0x806a, 0x0000 }, + { 0x806b, 0x0000 }, + { 0x806c, 0x0000 }, + { 0x806d, 0x0000 }, + { 0x806e, 0x0000 }, + { 0x806f, 0x0000 }, + { 0x8070, 0x0000 }, + { 0x8071, 0x0000 }, + { 0x8072, 0x0000 }, + { 0x8073, 0x0000 }, + { 0x8074, 0x0000 }, + { 0x8075, 0x0000 }, + { 0x8076, 0x0000 }, + { 0x8077, 0x0000 }, + { 0x8078, 0x0000 }, + { 0x8079, 0x0000 }, + { 0x807a, 0x0000 }, + { 0x807b, 0x0000 }, + { 0x807c, 0x0000 }, + { 0x807d, 0x0000 }, + { 0x807e, 0x0000 }, + { 0x807f, 0x0000 }, + { 0x8080, 0x0000 }, + { 0x8081, 0x0000 }, + { 0x8082, 0x0000 }, + { 0x8083, 0x0000 }, + { 0x8084, 0x0000 }, + { 0x8085, 0x0000 }, + { 0x8086, 0x0000 }, + { 0x8087, 0x0000 }, + { 0x8088, 0x0000 }, + { 0x8089, 0x0000 }, + { 0x808a, 0x0000 }, + { 0x808b, 0x0000 }, + { 0x808c, 0x0000 }, + { 0x808d, 0x0000 }, + { 0x808e, 0x0000 }, + { 0x808f, 0x0000 }, + { 0x8090, 0x0000 }, + { 0x8091, 0x0000 }, + { 0x8092, 0x0000 }, + { 0x8093, 0x0000 }, + { 0x8094, 0x0000 }, + { 0x8095, 0x0000 }, + { 0x8096, 0x0000 }, + { 0x8097, 0x0000 }, + { 0x8098, 0x0000 }, + { 0x8099, 0x0000 }, + { 0x809a, 0x0000 }, + { 0x809b, 0x0000 }, + { 0x809c, 0x0000 }, + { 0x809d, 0x0000 }, + { 0x809e, 0x0000 }, + { 0x809f, 0x0000 }, + { 0x80a0, 0x0000 }, + { 0x80a1, 0x0000 }, + { 0x80a2, 0x0000 }, + { 0x80a3, 0x0000 }, + { 0x80a4, 0x0000 }, + { 0x80a5, 0x0000 }, + { 0x80a6, 0x0000 }, + { 0x80a7, 0x0000 }, + { 0x80a8, 0x0000 }, + { 0x80a9, 0x0000 }, + { 0x80aa, 0x0000 }, + { 0x80ab, 0x0000 }, + { 0x80ac, 0x0000 }, + { 0x80ad, 0x0000 }, + { 0x80ae, 0x0000 }, + { 0x80af, 0x0000 }, + { 0x80b0, 0x0000 }, + { 0x80b1, 0x0000 }, + { 0x80b2, 0x0000 }, + { 0x80b3, 0x0000 }, + { 0x80b4, 0x0000 }, + { 0x80b5, 0x0000 }, + { 0x80b6, 0x0000 }, + { 0x80b7, 0x0000 }, + { 0x80b8, 0x0000 }, + { 0x80b9, 0x0000 }, + { 0x80ba, 0x0000 }, + { 0x80bb, 0x0000 }, + { 0x80bc, 0x0000 }, + { 0x80bd, 0x0000 }, + { 0x80be, 0x0000 }, + { 0x80bf, 0x0000 }, + { 0x80c0, 0x0000 }, + { 0x80c1, 0x0000 }, + { 0x80c2, 0x0000 }, + { 0x80c3, 0x0000 }, + { 0x80c4, 0x0000 }, + { 0x80c5, 0x0000 }, + { 0x80c6, 0x0000 }, + { 0x80c7, 0x0000 }, + { 0x80c8, 0x0000 }, + { 0x80c9, 0x0000 }, + { 0x80ca, 0x0000 }, + { 0x80cb, 0x0000 }, + { 0x80cc, 0x0000 }, + { 0x80cd, 0x0000 }, + { 0x80ce, 0x0000 }, + { 0x80cf, 0x0000 }, + { 0x80d0, 0x0000 }, + { 0x80d1, 0x0000 }, + { 0x80d2, 0x0000 }, + { 0x80d3, 0x0000 }, + { 0x80d4, 0x0000 }, + { 0x80d5, 0x0000 }, + { 0x80d6, 0x0000 }, + { 0x80d7, 0x0000 }, + { 0x80d8, 0x0000 }, + { 0x80d9, 0x0000 }, + { 0x80da, 0x0000 }, + { 0x80db, 0x0000 }, + { 0x80dc, 0x0000 }, + { 0x80dd, 0x0000 }, + { 0x80de, 0x0000 }, + { 0x80df, 0x0000 }, + { 0x80e0, 0x0000 }, + { 0x80e1, 0x0000 }, + { 0x80e2, 0x0000 }, + { 0x80e3, 0x0000 }, + { 0x80e4, 0x0000 }, + { 0x80e5, 0x0000 }, + { 0x80e6, 0x0000 }, + { 0x80e7, 0x0000 }, + { 0x80e8, 0x0000 }, + { 0x80e9, 0x0000 }, + { 0x80ea, 0x0000 }, + { 0x80eb, 0x0000 }, + { 0x80ec, 0x0000 }, + { 0x80ed, 0x0000 }, + { 0x80ee, 0x0000 }, + { 0x80ef, 0x0000 }, + { 0x80f0, 0x0000 }, + { 0x80f1, 0x0000 }, + { 0x80f2, 0x0000 }, + { 0x80f3, 0x0000 }, + { 0x80f4, 0x0000 }, + { 0x80f5, 0x0000 }, + { 0x80f6, 0x0000 }, + { 0x80f7, 0x0000 }, + { 0x80f8, 0x0000 }, + { 0x80f9, 0x0000 }, + { 0x80fa, 0x0000 }, + { 0x80fb, 0x0000 }, + { 0x80fc, 0x0000 }, + { 0x80fd, 0x0000 }, + { 0x80fe, 0x0000 }, + { 0x80ff, 0x0000 }, + { 0x8100, 0x0000 }, + { 0x8101, 0x0000 }, + { 0x8102, 0x0000 }, + { 0x8103, 0x0000 }, + { 0x8104, 0x0000 }, + { 0x8105, 0x0000 }, + { 0x8106, 0x0000 }, + { 0x8107, 0x0000 }, + { 0x8108, 0x0000 }, + { 0x8109, 0x0000 }, + { 0x810a, 0x0000 }, + { 0x810b, 0x0000 }, + { 0x810c, 0x0000 }, + { 0x810d, 0x0000 }, + { 0x810e, 0x0000 }, + { 0x810f, 0x0000 }, + { 0x8110, 0x0000 }, + { 0x8111, 0x0000 }, + { 0x8112, 0x0000 }, + { 0x8113, 0x0000 }, + { 0x8114, 0x0000 }, + { 0x8115, 0x0000 }, + { 0x8116, 0x0000 }, + { 0x8117, 0x0000 }, + { 0x8118, 0x0000 }, + { 0x8119, 0x0000 }, + { 0x811a, 0x0000 }, + { 0x811b, 0x0000 }, + { 0x811c, 0x0000 }, + { 0x811d, 0x0000 }, + { 0x811e, 0x0000 }, + { 0x811f, 0x0000 }, + { 0x8120, 0x0000 }, + { 0x8121, 0x0000 }, + { 0x8122, 0x0000 }, + { 0x8123, 0x0000 }, + { 0x8124, 0x0000 }, + { 0x8125, 0x0000 }, + { 0x8126, 0x0000 }, + { 0x8127, 0x0000 }, + { 0x8128, 0x0000 }, + { 0x8129, 0x0000 }, + { 0x812a, 0x0000 }, + { 0x812b, 0x0000 }, + { 0x812c, 0x0000 }, + { 0x812d, 0x0000 }, + { 0x812e, 0x0000 }, + { 0x812f, 0x0000 }, + { 0x8130, 0x0000 }, + { 0x8131, 0x0000 }, + { 0x8132, 0x0000 }, + { 0x8133, 0x0000 }, + { 0x8134, 0x0000 }, + { 0x8135, 0x0000 }, + { 0x8136, 0x0000 }, + { 0x8137, 0x0000 }, + { 0x8138, 0x0000 }, + { 0x8139, 0x0000 }, + { 0x813a, 0x0000 }, + { 0x813b, 0x0000 }, + { 0x813c, 0x0000 }, + { 0x813d, 0x0000 }, + { 0x813e, 0x0000 }, + { 0x813f, 0x0000 }, + { 0x8140, 0x0000 }, + { 0x8141, 0x0000 }, + { 0x8142, 0x0000 }, + { 0x8143, 0x0000 }, + { 0x8144, 0x0000 }, + { 0x8145, 0x0000 }, + { 0x8146, 0x0000 }, + { 0x8147, 0x0000 }, + { 0x8148, 0x0000 }, + { 0x8149, 0x0000 }, + { 0x814a, 0x0000 }, + { 0x814b, 0x0000 }, + { 0x814c, 0x0000 }, + { 0x814d, 0x0000 }, + { 0x814e, 0x0000 }, + { 0x814f, 0x0000 }, + { 0x8150, 0x0000 }, + { 0x8151, 0x0000 }, + { 0x8152, 0x0000 }, + { 0x8153, 0x0000 }, + { 0x8154, 0x0000 }, + { 0x8155, 0x0000 }, + { 0x8156, 0x0000 }, + { 0x8157, 0x0000 }, + { 0x8158, 0x0000 }, + { 0x8159, 0x0000 }, + { 0x815a, 0x0000 }, + { 0x815b, 0x0000 }, + { 0x815c, 0x0000 }, + { 0x815d, 0x0000 }, + { 0x815e, 0x0000 }, + { 0x815f, 0x0000 }, + { 0x8160, 0x0000 }, + { 0x8161, 0x0000 }, + { 0x8162, 0x0000 }, + { 0x8163, 0x0000 }, + { 0x8164, 0x0000 }, + { 0x8165, 0x0000 }, + { 0x8166, 0x0000 }, + { 0x8167, 0x0000 }, + { 0x8168, 0x0000 }, + { 0x8169, 0x0000 }, + { 0x816a, 0x0000 }, + { 0x816b, 0x0000 }, + { 0x816c, 0x0000 }, + { 0x816d, 0x0000 }, + { 0x816e, 0x0000 }, + { 0x816f, 0x0000 }, + { 0x8170, 0x0000 }, + { 0x8171, 0x0000 }, + { 0x8172, 0x0000 }, + { 0x8173, 0x0000 }, + { 0x8174, 0x0000 }, + { 0x8175, 0x0000 }, + { 0x8176, 0x0000 }, + { 0x8177, 0x0000 }, + { 0x8178, 0x0000 }, + { 0x8179, 0x0000 }, + { 0x817a, 0x0000 }, + { 0x817b, 0x0000 }, + { 0x817c, 0x0000 }, + { 0x817d, 0x0000 }, + { 0x817e, 0x0000 }, + { 0x817f, 0x0000 }, + { 0x8180, 0x0000 }, + { 0x8181, 0x0000 }, + { 0x8182, 0x0000 }, + { 0x8183, 0x0000 }, + { 0x8184, 0x0000 }, + { 0x8185, 0x0000 }, + { 0x8186, 0x0000 }, + { 0x8187, 0x0000 }, + { 0x8188, 0x0000 }, + { 0x8189, 0x0000 }, + { 0x818a, 0x0000 }, + { 0x818b, 0x0000 }, + { 0x818c, 0x0000 }, + { 0x818d, 0x0000 }, + { 0x818e, 0x0000 }, + { 0x818f, 0x0000 }, + { 0x8190, 0x0000 }, + { 0x8191, 0x0000 }, + { 0x8192, 0x0000 }, + { 0x8193, 0x0000 }, + { 0x8194, 0x0000 }, + { 0x8195, 0x0000 }, + { 0x8196, 0x0000 }, + { 0x8197, 0x0000 }, + { 0x8198, 0x0000 }, + { 0x8199, 0x0000 }, + { 0x819a, 0x0000 }, + { 0x819b, 0x0000 }, + { 0x819c, 0x0000 }, + { 0x819d, 0x0000 }, + { 0x819e, 0x0000 }, + { 0x819f, 0x0000 }, + { 0x81a0, 0x0000 }, + { 0x81a1, 0x0000 }, + { 0x81a2, 0x0000 }, + { 0x81a3, 0x0000 }, + { 0x81a4, 0x0000 }, + { 0x81a5, 0x0000 }, + { 0x81a6, 0x0000 }, + { 0x81a7, 0x0000 }, + { 0x81a8, 0x0000 }, + { 0x81a9, 0x0000 }, + { 0x81aa, 0x0000 }, + { 0x81ab, 0x0000 }, + { 0x81ac, 0x0000 }, + { 0x81ad, 0x0000 }, + { 0x81ae, 0x0000 }, + { 0x81af, 0x0000 }, + { 0x81b0, 0x0000 }, + { 0x81b1, 0x0000 }, + { 0x81b2, 0x0000 }, + { 0x81b3, 0x0000 }, + { 0x81b4, 0x0000 }, + { 0x81b5, 0x0000 }, + { 0x81b6, 0x0000 }, + { 0x81b7, 0x0000 }, + { 0x81b8, 0x0000 }, + { 0x81b9, 0x0000 }, + { 0x81ba, 0x0000 }, + { 0x81bb, 0x0000 }, + { 0x81bc, 0x0000 }, + { 0x81bd, 0x0000 }, + { 0x81be, 0x0000 }, + { 0x81bf, 0x0000 }, + { 0x81c0, 0x0000 }, + { 0x81c1, 0x0000 }, + { 0x81c2, 0x0000 }, + { 0x81c3, 0x0000 }, + { 0x81c4, 0x0000 }, + { 0x81c5, 0x0000 }, + { 0x81c6, 0x0000 }, + { 0x81c7, 0x0000 }, + { 0x81c8, 0x0000 }, + { 0x81c9, 0x0000 }, + { 0x81ca, 0x0000 }, + { 0x81cb, 0x0000 }, + { 0x81cc, 0x0000 }, + { 0x81cd, 0x0000 }, + { 0x81ce, 0x0000 }, + { 0x81cf, 0x0000 }, + { 0x81d0, 0x0000 }, + { 0x81d1, 0x0000 }, + { 0x81d2, 0x0000 }, + { 0x81d3, 0x0000 }, + { 0x81d4, 0x0000 }, + { 0x81d5, 0x0000 }, + { 0x81d6, 0x0000 }, + { 0x81d7, 0x0000 }, + { 0x81d8, 0x0000 }, + { 0x81d9, 0x0000 }, + { 0x81da, 0x0000 }, + { 0x81db, 0x0000 }, + { 0x81dc, 0x0000 }, + { 0x81dd, 0x0000 }, + { 0x81de, 0x0000 }, + { 0x81df, 0x0000 }, + { 0x81e0, 0x0000 }, + { 0x81e1, 0x0000 }, + { 0x81e2, 0x0000 }, + { 0x81e3, 0x0000 }, + { 0x81e4, 0x0000 }, + { 0x81e5, 0x0000 }, + { 0x81e6, 0x0000 }, + { 0x81e7, 0x0000 }, + { 0x81e8, 0x0000 }, + { 0x81e9, 0x0000 }, + { 0x81ea, 0x0000 }, + { 0x81eb, 0x0000 }, + { 0x81ec, 0x0000 }, + { 0x81ed, 0x0000 }, + { 0x81ee, 0x0000 }, + { 0x81ef, 0x0000 }, + { 0x81f0, 0x0000 }, + { 0x81f1, 0x0000 }, + { 0x81f2, 0x0000 }, + { 0x81f3, 0x0000 }, + { 0x81f4, 0x0000 }, + { 0x81f5, 0x0000 }, + { 0x81f6, 0x0000 }, + { 0x81f7, 0x0000 }, + { 0x81f8, 0x0000 }, + { 0x81f9, 0x0000 }, + { 0x81fa, 0x0000 }, + { 0x81fb, 0x0000 }, + { 0x81fc, 0x0000 }, + { 0x81fd, 0x0000 }, + { 0x81fe, 0x0000 }, + { 0x81ff, 0x0000 }, + { 0x8200, 0x0000 }, + { 0x8201, 0x0000 }, + { 0x8202, 0x0000 }, + { 0x8203, 0x0000 }, + { 0x8204, 0x0000 }, + { 0x8205, 0x0000 }, + { 0x8206, 0x0000 }, + { 0x8207, 0x0000 }, + { 0x8208, 0x0000 }, + { 0x8209, 0x0000 }, + { 0x820a, 0x0000 }, + { 0x820b, 0x0000 }, + { 0x820c, 0x0000 }, + { 0x820d, 0x0000 }, + { 0x820e, 0x0000 }, + { 0x820f, 0x0000 }, + { 0x8210, 0x0000 }, + { 0x8211, 0x0000 }, + { 0x8212, 0x0000 }, + { 0x8213, 0x0000 }, + { 0x8214, 0x0000 }, + { 0x8215, 0x0000 }, + { 0x8216, 0x0000 }, + { 0x8217, 0x0000 }, + { 0x8218, 0x0000 }, + { 0x8219, 0x0000 }, + { 0x821a, 0x0000 }, + { 0x821b, 0x0000 }, + { 0x821c, 0x0000 }, + { 0x821d, 0x0000 }, + { 0x821e, 0x0000 }, + { 0x821f, 0x0000 }, + { 0x8220, 0x0000 }, + { 0x8221, 0x0000 }, + { 0x8222, 0x0000 }, + { 0x8223, 0x0000 }, + { 0x8224, 0x0000 }, + { 0x8225, 0x0000 }, + { 0x8226, 0x0000 }, + { 0x8227, 0x0000 }, + { 0x8228, 0x0000 }, + { 0x8229, 0x0000 }, + { 0x822a, 0x0000 }, + { 0x822b, 0x0000 }, + { 0x822c, 0x0000 }, + { 0x822d, 0x0000 }, + { 0x822e, 0x0000 }, + { 0x822f, 0x0000 }, + { 0x8230, 0x0000 }, + { 0x8231, 0x0000 }, + { 0x8232, 0x0000 }, + { 0x8233, 0x0000 }, + { 0x8234, 0x0000 }, + { 0x8235, 0x0000 }, + { 0x8236, 0x0000 }, + { 0x8237, 0x0000 }, + { 0x8238, 0x0000 }, + { 0x8239, 0x0000 }, + { 0x823a, 0x0000 }, + { 0x823b, 0x0000 }, + { 0x823c, 0x0000 }, + { 0x823d, 0x0000 }, + { 0x823e, 0x0000 }, + { 0x823f, 0x0000 }, + { 0x8240, 0x0000 }, + { 0x8241, 0x0000 }, + { 0x8242, 0x0000 }, + { 0x8243, 0x0000 }, + { 0x8244, 0x0000 }, + { 0x8245, 0x0000 }, + { 0x8246, 0x0000 }, + { 0x8247, 0x0000 }, + { 0x8248, 0x0000 }, + { 0x8249, 0x0000 }, + { 0x824a, 0x0000 }, + { 0x824b, 0x0000 }, + { 0x824c, 0x0000 }, + { 0x824d, 0x0000 }, + { 0x824e, 0x0000 }, + { 0x824f, 0x0000 }, + { 0x8250, 0x0000 }, + { 0x8251, 0x0000 }, + { 0x8252, 0x0000 }, + { 0x8253, 0x0000 }, + { 0x8254, 0x0000 }, + { 0x8255, 0x0000 }, + { 0x8256, 0x0000 }, + { 0x8257, 0x0000 }, + { 0x8258, 0x0000 }, + { 0x8259, 0x0000 }, + { 0x825a, 0x0000 }, + { 0x825b, 0x0000 }, + { 0x825c, 0x0000 }, + { 0x825d, 0x0000 }, + { 0x825e, 0x0000 }, + { 0x825f, 0x0000 }, + { 0x8260, 0x0000 }, + { 0x8261, 0x0000 }, + { 0x8262, 0x0000 }, + { 0x8263, 0x0000 }, + { 0x8264, 0x0000 }, + { 0x8265, 0x0000 }, + { 0x8266, 0x0000 }, + { 0x8267, 0x0000 }, + { 0x8268, 0x0000 }, + { 0x8269, 0x0000 }, + { 0x826a, 0x0000 }, + { 0x826b, 0x0000 }, + { 0x826c, 0x0000 }, + { 0x826d, 0x0000 }, + { 0x826e, 0x0000 }, + { 0x826f, 0x0000 }, + { 0x8270, 0x0000 }, + { 0x8271, 0x0000 }, + { 0x8272, 0x0000 }, + { 0x8273, 0x0000 }, + { 0x8274, 0x0000 }, + { 0x8275, 0x0000 }, + { 0x8276, 0x0000 }, + { 0x8277, 0x0000 }, + { 0x8278, 0x0000 }, + { 0x8279, 0x0000 }, + { 0x827a, 0x0000 }, + { 0x827b, 0x0000 }, + { 0x827c, 0x0000 }, + { 0x827d, 0x0000 }, + { 0x827e, 0x0000 }, + { 0x827f, 0x0000 }, + { 0x8280, 0x0000 }, + { 0x8281, 0x0000 }, + { 0x8282, 0x0000 }, + { 0x8283, 0x0000 }, + { 0x8284, 0x0000 }, + { 0x8285, 0x0000 }, + { 0x8286, 0x0000 }, + { 0x8287, 0x0000 }, + { 0x8288, 0x0000 }, + { 0x8289, 0x0000 }, + { 0x828a, 0x0000 }, + { 0x828b, 0x0000 }, + { 0x828c, 0x0000 }, + { 0x828d, 0x0000 }, + { 0x828e, 0x0000 }, + { 0x828f, 0x0000 }, + { 0x8290, 0x0000 }, + { 0x8291, 0x0000 }, + { 0x8292, 0x0000 }, + { 0x8293, 0x0000 }, + { 0x8294, 0x0000 }, + { 0x8295, 0x0000 }, + { 0x8296, 0x0000 }, + { 0x8297, 0x0000 }, + { 0x8298, 0x0000 }, + { 0x8299, 0x0000 }, + { 0x829a, 0x0000 }, + { 0x829b, 0x0000 }, + { 0x829c, 0x0000 }, + { 0x829d, 0x0000 }, + { 0x829e, 0x0000 }, + { 0x829f, 0x0000 }, + { 0x82a0, 0x0000 }, + { 0x82a1, 0x0000 }, + { 0x82a2, 0x0000 }, + { 0x82a3, 0x0000 }, + { 0x82a4, 0x0000 }, + { 0x82a5, 0x0000 }, + { 0x82a6, 0x0000 }, + { 0x82a7, 0x0000 }, + { 0x82a8, 0x0000 }, + { 0x82a9, 0x0000 }, + { 0x82aa, 0x0000 }, + { 0x82ab, 0x0000 }, + { 0x82ac, 0x0000 }, + { 0x82ad, 0x0000 }, + { 0x82ae, 0x0000 }, + { 0x82af, 0x0000 }, + { 0x82b0, 0x0000 }, + { 0x82b1, 0x0000 }, + { 0x82b2, 0x0000 }, + { 0x82b3, 0x0000 }, + { 0x82b4, 0x0000 }, + { 0x82b5, 0x0000 }, + { 0x82b6, 0x0000 }, + { 0x82b7, 0x0000 }, + { 0x82b8, 0x0000 }, + { 0x82b9, 0x0000 }, + { 0x82ba, 0x0000 }, + { 0x82bb, 0x0000 }, + { 0x82bc, 0x0000 }, + { 0x82bd, 0x0000 }, + { 0x82be, 0x0000 }, + { 0x82bf, 0x0000 }, + { 0x82c0, 0x0000 }, + { 0x82c1, 0x0000 }, + { 0x82c2, 0x0000 }, + { 0x82c3, 0x0000 }, + { 0x82c4, 0x0000 }, + { 0x82c5, 0x0000 }, + { 0x82c6, 0x0000 }, + { 0x82c7, 0x0000 }, + { 0x82c8, 0x0000 }, + { 0x82c9, 0x0000 }, + { 0x82ca, 0x0000 }, + { 0x82cb, 0x0000 }, + { 0x82cc, 0x0000 }, + { 0x82cd, 0x0000 }, + { 0x82ce, 0x0000 }, + { 0x82cf, 0x0000 }, + { 0x82d0, 0x0000 }, + { 0x82d1, 0x0000 }, + { 0x82d2, 0x0000 }, + { 0x82d3, 0x0000 }, + { 0x82d4, 0x0000 }, + { 0x82d5, 0x0000 }, + { 0x82d6, 0x0000 }, + { 0x82d7, 0x0000 }, + { 0x82d8, 0x0000 }, + { 0x82d9, 0x0000 }, + { 0x82da, 0x0000 }, + { 0x82db, 0x0000 }, + { 0x82dc, 0x0000 }, + { 0x82dd, 0x0000 }, + { 0x82de, 0x0000 }, + { 0x82df, 0x0000 }, + { 0x82e0, 0x0000 }, + { 0x82e1, 0x0000 }, + { 0x82e2, 0x0000 }, + { 0x82e3, 0x0000 }, + { 0x82e4, 0x0000 }, + { 0x82e5, 0x0000 }, + { 0x82e6, 0x0000 }, + { 0x82e7, 0x0000 }, + { 0x82e8, 0x0000 }, + { 0x82e9, 0x0000 }, + { 0x82ea, 0x0000 }, + { 0x82eb, 0x0000 }, + { 0x82ec, 0x0000 }, + { 0x82ed, 0x0000 }, + { 0x82ee, 0x0000 }, + { 0x82ef, 0x0000 }, + { 0x82f0, 0x0000 }, + { 0x82f1, 0x0000 }, + { 0x82f2, 0x0000 }, + { 0x82f3, 0x0000 }, + { 0x82f4, 0x0000 }, + { 0x82f5, 0x0000 }, + { 0x82f6, 0x0000 }, + { 0x82f7, 0x0000 }, + { 0x82f8, 0x0000 }, + { 0x82f9, 0x0000 }, + { 0x82fa, 0x0000 }, + { 0x82fb, 0x0000 }, + { 0x82fc, 0x0000 }, + { 0x82fd, 0x0000 }, + { 0x82fe, 0x0000 }, + { 0x82ff, 0x0000 }, + { 0x8300, 0x0000 }, + { 0x8301, 0x0000 }, + { 0x8302, 0x0000 }, + { 0x8303, 0x0000 }, + { 0x8304, 0x0000 }, + { 0x8305, 0x0000 }, + { 0x8306, 0x0000 }, + { 0x8307, 0x0000 }, + { 0x8308, 0x0000 }, + { 0x8309, 0x0000 }, + { 0x830a, 0x0000 }, + { 0x830b, 0x0000 }, + { 0x830c, 0x0000 }, + { 0x830d, 0x0000 }, + { 0x830e, 0x0000 }, + { 0x830f, 0x0000 }, + { 0x8310, 0x0000 }, + { 0x8311, 0x0000 }, + { 0x8312, 0x0000 }, + { 0x8313, 0x0000 }, + { 0x8314, 0x0000 }, + { 0x8315, 0x0000 }, + { 0x8316, 0x0000 }, + { 0x8317, 0x0000 }, + { 0x8318, 0x0000 }, + { 0x8319, 0x0000 }, + { 0x831a, 0x0000 }, + { 0x831b, 0x0000 }, + { 0x831c, 0x0000 }, + { 0x831d, 0x0000 }, + { 0x831e, 0x0000 }, + { 0x831f, 0x0000 }, + { 0x8320, 0x0000 }, + { 0x8321, 0x0000 }, + { 0x8322, 0x0000 }, + { 0x8323, 0x0000 }, + { 0x8324, 0x0000 }, + { 0x8325, 0x0000 }, + { 0x8326, 0x0000 }, + { 0x8327, 0x0000 }, + { 0x8328, 0x0000 }, + { 0x8329, 0x0000 }, + { 0x832a, 0x0000 }, + { 0x832b, 0x0000 }, + { 0x832c, 0x0000 }, + { 0x832d, 0x0000 }, + { 0x832e, 0x0000 }, + { 0x832f, 0x0000 }, + { 0x8330, 0x0000 }, + { 0x8331, 0x0000 }, + { 0x8332, 0x0000 }, + { 0x8333, 0x0000 }, + { 0x8334, 0x0000 }, + { 0x8335, 0x0000 }, + { 0x8336, 0x0000 }, + { 0x8337, 0x0000 }, + { 0x8338, 0x0000 }, + { 0x8339, 0x0000 }, + { 0x833a, 0x0000 }, + { 0x833b, 0x0000 }, + { 0x833c, 0x0000 }, + { 0x833d, 0x0000 }, + { 0x833e, 0x0000 }, + { 0x833f, 0x0000 }, + { 0x8340, 0x0000 }, + { 0x8341, 0x0000 }, + { 0x8342, 0x0000 }, + { 0x8343, 0x0000 }, + { 0x8344, 0x0000 }, + { 0x8345, 0x0000 }, + { 0x8346, 0x0000 }, + { 0x8347, 0x0000 }, + { 0x8348, 0x0000 }, + { 0x8349, 0x0000 }, + { 0x834a, 0x0000 }, + { 0x834b, 0x0000 }, + { 0x834c, 0x0000 }, + { 0x834d, 0x0000 }, + { 0x834e, 0x0000 }, + { 0x834f, 0x0000 }, + { 0x8350, 0x0000 }, + { 0x8351, 0x0000 }, + { 0x8352, 0x0000 }, + { 0x8353, 0x0000 }, + { 0x8354, 0x0000 }, + { 0x8355, 0x0000 }, + { 0x8356, 0x0000 }, + { 0x8357, 0x0000 }, + { 0x8358, 0x0000 }, + { 0x8359, 0x0000 }, + { 0x835a, 0x0000 }, + { 0x835b, 0x0000 }, + { 0x835c, 0x0000 }, + { 0x835d, 0x0000 }, + { 0x835e, 0x0000 }, + { 0x835f, 0x0000 }, + { 0x8360, 0x0000 }, + { 0x8361, 0x0000 }, + { 0x8362, 0x0000 }, + { 0x8363, 0x0000 }, + { 0x8364, 0x0000 }, + { 0x8365, 0x0000 }, + { 0x8366, 0x0000 }, + { 0x8367, 0x0000 }, + { 0x8368, 0x0000 }, + { 0x8369, 0x0000 }, + { 0x836a, 0x0000 }, + { 0x836b, 0x0000 }, + { 0x836c, 0x0000 }, + { 0x836d, 0x0000 }, + { 0x836e, 0x0000 }, + { 0x836f, 0x0000 }, + { 0x8370, 0x0000 }, + { 0x8371, 0x0000 }, + { 0x8372, 0x0000 }, + { 0x8373, 0x0000 }, + { 0x8374, 0x0000 }, + { 0x8375, 0x0000 }, + { 0x8376, 0x0000 }, + { 0x8377, 0x0000 }, + { 0x8378, 0x0000 }, + { 0x8379, 0x0000 }, + { 0x837a, 0x0000 }, + { 0x837b, 0x0000 }, + { 0x837c, 0x0000 }, + { 0x837d, 0x0000 }, + { 0x837e, 0x0000 }, + { 0x837f, 0x0000 }, + { 0x8380, 0x0000 }, + { 0x8381, 0x0000 }, + { 0x8382, 0x0000 }, + { 0x8383, 0x0000 }, + { 0x8384, 0x0000 }, + { 0x8385, 0x0000 }, + { 0x8386, 0x0000 }, + { 0x8387, 0x0000 }, + { 0x8388, 0x0000 }, + { 0x8389, 0x0000 }, + { 0x838a, 0x0000 }, + { 0x838b, 0x0000 }, + { 0x838c, 0x0000 }, + { 0x838d, 0x0000 }, + { 0x838e, 0x0000 }, + { 0x838f, 0x0000 }, + { 0x8390, 0x0000 }, + { 0x8391, 0x0000 }, + { 0x8392, 0x0000 }, + { 0x8393, 0x0000 }, + { 0x8394, 0x0000 }, + { 0x8395, 0x0000 }, + { 0x8396, 0x0000 }, + { 0x8397, 0x0000 }, + { 0x8398, 0x0000 }, + { 0x8399, 0x0000 }, + { 0x839a, 0x0000 }, + { 0x839b, 0x0000 }, + { 0x839c, 0x0000 }, + { 0x839d, 0x0000 }, + { 0x839e, 0x0000 }, + { 0x839f, 0x0000 }, + { 0x83a0, 0x0000 }, + { 0x83a1, 0x0000 }, + { 0x83a2, 0x0000 }, + { 0x83a3, 0x0000 }, + { 0x83a4, 0x0000 }, + { 0x83a5, 0x0000 }, + { 0x83a6, 0x0000 }, + { 0x83a7, 0x0000 }, + { 0x83a8, 0x0000 }, + { 0x83a9, 0x0000 }, + { 0x83aa, 0x0000 }, + { 0x83ab, 0x0000 }, + { 0x83ac, 0x0000 }, + { 0x83ad, 0x0000 }, + { 0x83ae, 0x0000 }, + { 0x83af, 0x0000 }, + { 0x83b0, 0x0000 }, + { 0x83b1, 0x0000 }, + { 0x83b2, 0x0000 }, + { 0x83b3, 0x0000 }, + { 0x83b4, 0x0000 }, + { 0x83b5, 0x0000 }, + { 0x83b6, 0x0000 }, + { 0x83b7, 0x0000 }, + { 0x83b8, 0x0000 }, + { 0x83b9, 0x0000 }, + { 0x83ba, 0x0000 }, + { 0x83bb, 0x0000 }, + { 0x83bc, 0x0000 }, + { 0x83bd, 0x0000 }, + { 0x83be, 0x0000 }, + { 0x83bf, 0x0000 }, + { 0x83c0, 0x0000 }, + { 0x83c1, 0x0000 }, + { 0x83c2, 0x0000 }, + { 0x83c3, 0x0000 }, + { 0x83c4, 0x0000 }, + { 0x83c5, 0x0000 }, + { 0x83c6, 0x0000 }, + { 0x83c7, 0x0000 }, + { 0x83c8, 0x0000 }, + { 0x83c9, 0x0000 }, + { 0x83ca, 0x0000 }, + { 0x83cb, 0x0000 }, + { 0x83cc, 0x0000 }, + { 0x83cd, 0x0000 }, + { 0x83ce, 0x0000 }, + { 0x83cf, 0x0000 }, + { 0x83d0, 0x0000 }, + { 0x83d1, 0x0000 }, + { 0x83d2, 0x0000 }, + { 0x83d3, 0x0000 }, + { 0x83d4, 0x0000 }, + { 0x83d5, 0x0000 }, + { 0x83d6, 0x0000 }, + { 0x83d7, 0x0000 }, + { 0x83d8, 0x0000 }, + { 0x83d9, 0x0000 }, + { 0x83da, 0x0000 }, + { 0x83db, 0x0000 }, + { 0x83dc, 0x0000 }, + { 0x83dd, 0x0000 }, + { 0x83de, 0x0000 }, + { 0x83df, 0x0000 }, + { 0x83e0, 0x0000 }, + { 0x83e1, 0x0000 }, + { 0x83e2, 0x0000 }, + { 0x83e3, 0x0000 }, + { 0x83e4, 0x0000 }, + { 0x83e5, 0x0000 }, + { 0x83e6, 0x0000 }, + { 0x83e7, 0x0000 }, + { 0x83e8, 0x0000 }, + { 0x83e9, 0x0000 }, + { 0x83ea, 0x0000 }, + { 0x83eb, 0x0000 }, + { 0x83ec, 0x0000 }, + { 0x83ed, 0x0000 }, + { 0x83ee, 0x0000 }, + { 0x83ef, 0x0000 }, + { 0x83f0, 0x0000 }, + { 0x83f1, 0x0000 }, + { 0x83f2, 0x0000 }, + { 0x83f3, 0x0000 }, + { 0x83f4, 0x0000 }, + { 0x83f5, 0x0000 }, + { 0x83f6, 0x0000 }, + { 0x83f7, 0x0000 }, + { 0x83f8, 0x0000 }, + { 0x83f9, 0x0000 }, + { 0x83fa, 0x0000 }, + { 0x83fb, 0x0000 }, + { 0x83fc, 0x0000 }, + { 0x83fd, 0x0000 }, + { 0x83fe, 0x0000 }, + { 0x83ff, 0x0000 }, + { 0x8400, 0x0000 }, + { 0x8401, 0x0000 }, + { 0x8402, 0x0000 }, + { 0x8403, 0x0000 }, + { 0x8404, 0x0000 }, + { 0x8405, 0x0000 }, + { 0x8406, 0x0000 }, + { 0x8407, 0x0000 }, + { 0x8408, 0x0000 }, + { 0x8409, 0x0000 }, + { 0x840a, 0x0000 }, + { 0x840b, 0x0000 }, + { 0x840c, 0x0000 }, + { 0x840d, 0x0000 }, + { 0x840e, 0x0000 }, + { 0x840f, 0x0000 }, + { 0x8410, 0x0000 }, + { 0x8411, 0x0000 }, + { 0x8412, 0x0000 }, + { 0x8413, 0x0000 }, + { 0x8414, 0x0000 }, + { 0x8415, 0x0000 }, + { 0x8416, 0x0000 }, + { 0x8417, 0x0000 }, + { 0x8418, 0x0000 }, + { 0x8419, 0x0000 }, + { 0x841a, 0x0000 }, + { 0x841b, 0x0000 }, + { 0x841c, 0x0000 }, + { 0x841d, 0x0000 }, + { 0x841e, 0x0000 }, + { 0x841f, 0x0000 }, + { 0x8420, 0x0000 }, + { 0x8421, 0x0000 }, + { 0x8422, 0x0000 }, + { 0x8423, 0x0000 }, + { 0x8424, 0x0000 }, + { 0x8425, 0x0000 }, + { 0x8426, 0x0000 }, + { 0x8427, 0x0000 }, + { 0x8428, 0x0000 }, + { 0x8429, 0x0000 }, + { 0x842a, 0x0000 }, + { 0x842b, 0x0000 }, + { 0x842c, 0x0000 }, + { 0x842d, 0x0000 }, + { 0x842e, 0x0000 }, + { 0x842f, 0x0000 }, + { 0x8430, 0x0000 }, + { 0x8431, 0x0000 }, + { 0x8432, 0x0000 }, + { 0x8433, 0x0000 }, + { 0x8434, 0x0000 }, + { 0x8435, 0x0000 }, + { 0x8436, 0x0000 }, + { 0x8437, 0x0000 }, + { 0x8438, 0x0000 }, + { 0x8439, 0x0000 }, + { 0x843a, 0x0000 }, + { 0x843b, 0x0000 }, + { 0x843c, 0x0000 }, + { 0x843d, 0x0000 }, + { 0x843e, 0x0000 }, + { 0x843f, 0x0000 }, + { 0x8440, 0x0000 }, + { 0x8441, 0x0000 }, + { 0x8442, 0x0000 }, + { 0x8443, 0x0000 }, + { 0x8444, 0x0000 }, + { 0x8445, 0x0000 }, + { 0x8446, 0x0000 }, + { 0x8447, 0x0000 }, + { 0x8448, 0x0000 }, + { 0x8449, 0x0000 }, + { 0x844a, 0x0000 }, + { 0x844b, 0x0000 }, + { 0x844c, 0x0000 }, + { 0x844d, 0x0000 }, + { 0x844e, 0x0000 }, + { 0x844f, 0x0000 }, + { 0x8450, 0x0000 }, + { 0x8451, 0x0000 }, + { 0x8452, 0x0000 }, + { 0x8453, 0x0000 }, + { 0x8454, 0x0000 }, + { 0x8455, 0x0000 }, + { 0x8456, 0x0000 }, + { 0x8457, 0x0000 }, + { 0x8458, 0x0000 }, + { 0x8459, 0x0000 }, + { 0x845a, 0x0000 }, + { 0x845b, 0x0000 }, + { 0x845c, 0x0000 }, + { 0x845d, 0x0000 }, + { 0x845e, 0x0000 }, + { 0x845f, 0x0000 }, + { 0x8460, 0x0000 }, + { 0x8461, 0x0000 }, + { 0x8462, 0x0000 }, + { 0x8463, 0x0000 }, + { 0x8464, 0x0000 }, + { 0x8465, 0x0000 }, + { 0x8466, 0x0000 }, + { 0x8467, 0x0000 }, + { 0x8468, 0x0000 }, + { 0x8469, 0x0000 }, + { 0x846a, 0x0000 }, + { 0x846b, 0x0000 }, + { 0x846c, 0x0000 }, + { 0x846d, 0x0000 }, + { 0x846e, 0x0000 }, + { 0x846f, 0x0000 }, + { 0x8470, 0x0000 }, + { 0x8471, 0x0000 }, + { 0x8472, 0x0000 }, + { 0x8473, 0x0000 }, + { 0x8474, 0x0000 }, + { 0x8475, 0x0000 }, + { 0x8476, 0x0000 }, + { 0x8477, 0x0000 }, + { 0x8478, 0x0000 }, + { 0x8479, 0x0000 }, + { 0x847a, 0x0000 }, + { 0x847b, 0x0000 }, + { 0x847c, 0x0000 }, + { 0x847d, 0x0000 }, + { 0x847e, 0x0000 }, + { 0x847f, 0x0000 }, + { 0x8480, 0x0000 }, + { 0x8481, 0x0000 }, + { 0x8482, 0x0000 }, + { 0x8483, 0x0000 }, + { 0x8484, 0x0000 }, + { 0x8485, 0x0000 }, + { 0x8486, 0x0000 }, + { 0x8487, 0x0000 }, + { 0x8488, 0x0000 }, + { 0x8489, 0x0000 }, + { 0x848a, 0x0000 }, + { 0x848b, 0x0000 }, + { 0x848c, 0x0000 }, + { 0x848d, 0x0000 }, + { 0x848e, 0x0000 }, + { 0x848f, 0x0000 }, + { 0x8490, 0x0000 }, + { 0x8491, 0x0000 }, + { 0x8492, 0x0000 }, + { 0x8493, 0x0000 }, + { 0x8494, 0x0000 }, + { 0x8495, 0x0000 }, + { 0x8496, 0x0000 }, + { 0x8497, 0x0000 }, + { 0x8498, 0x0000 }, + { 0x8499, 0x0000 }, + { 0x849a, 0x0000 }, + { 0x849b, 0x0000 }, + { 0x849c, 0x0000 }, + { 0x849d, 0x0000 }, + { 0x849e, 0x0000 }, + { 0x849f, 0x0000 }, + { 0x84a0, 0x0000 }, + { 0x84a1, 0x0000 }, + { 0x84a2, 0x0000 }, + { 0x84a3, 0x0000 }, + { 0x84a4, 0x0000 }, + { 0x84a5, 0x0000 }, + { 0x84a6, 0x0000 }, + { 0x84a7, 0x0000 }, + { 0x84a8, 0x0000 }, + { 0x84a9, 0x0000 }, + { 0x84aa, 0x0000 }, + { 0x84ab, 0x0000 }, + { 0x84ac, 0x0000 }, + { 0x84ad, 0x0000 }, + { 0x84ae, 0x0000 }, + { 0x84af, 0x0000 }, + { 0x84b0, 0x0000 }, + { 0x84b1, 0x0000 }, + { 0x84b2, 0x0000 }, + { 0x84b3, 0x0000 }, + { 0x84b4, 0x0000 }, + { 0x84b5, 0x0000 }, + { 0x84b6, 0x0000 }, + { 0x84b7, 0x0000 }, + { 0x84b8, 0x0000 }, + { 0x84b9, 0x0000 }, + { 0x84ba, 0x0000 }, + { 0x84bb, 0x0000 }, + { 0x84bc, 0x0000 }, + { 0x84bd, 0x0000 }, + { 0x84be, 0x0000 }, + { 0x84bf, 0x0000 }, + { 0x84c0, 0x0000 }, + { 0x84c1, 0x0000 }, + { 0x84c2, 0x0000 }, + { 0x84c3, 0x0000 }, + { 0x84c4, 0x0000 }, + { 0x84c5, 0x0000 }, + { 0x84c6, 0x0000 }, + { 0x84c7, 0x0000 }, + { 0x84c8, 0x0000 }, + { 0x84c9, 0x0000 }, + { 0x84ca, 0x0000 }, + { 0x84cb, 0x0000 }, + { 0x84cc, 0x0000 }, + { 0x84cd, 0x0000 }, + { 0x84ce, 0x0000 }, + { 0x84cf, 0x0000 }, + { 0x84d0, 0x0000 }, + { 0x84d1, 0x0000 }, + { 0x84d2, 0x0000 }, + { 0x84d3, 0x0000 }, + { 0x84d4, 0x0000 }, + { 0x84d5, 0x0000 }, + { 0x84d6, 0x0000 }, + { 0x84d7, 0x0000 }, + { 0x84d8, 0x0000 }, + { 0x84d9, 0x0000 }, + { 0x84da, 0x0000 }, + { 0x84db, 0x0000 }, + { 0x84dc, 0x0000 }, + { 0x84dd, 0x0000 }, + { 0x84de, 0x0000 }, + { 0x84df, 0x0000 }, + { 0x84e0, 0x0000 }, + { 0x84e1, 0x0000 }, + { 0x84e2, 0x0000 }, + { 0x84e3, 0x0000 }, + { 0x84e4, 0x0000 }, + { 0x84e5, 0x0000 }, + { 0x84e6, 0x0000 }, + { 0x84e7, 0x0000 }, + { 0x84e8, 0x0000 }, + { 0x84e9, 0x0000 }, + { 0x84ea, 0x0000 }, + { 0x84eb, 0x0000 }, + { 0x84ec, 0x0000 }, + { 0x84ed, 0x0000 }, + { 0x84ee, 0x0000 }, + { 0x84ef, 0x0000 }, + { 0x84f0, 0x0000 }, + { 0x84f1, 0x0000 }, + { 0x84f2, 0x0000 }, + { 0x84f3, 0x0000 }, + { 0x84f4, 0x0000 }, + { 0x84f5, 0x0000 }, + { 0x84f6, 0x0000 }, + { 0x84f7, 0x0000 }, + { 0x84f8, 0x0000 }, + { 0x84f9, 0x0000 }, + { 0x84fa, 0x0000 }, + { 0x84fb, 0x0000 }, + { 0x84fc, 0x0000 }, + { 0x84fd, 0x0000 }, + { 0x84fe, 0x0000 }, + { 0x84ff, 0x0000 }, + { 0x8500, 0x0000 }, + { 0x8501, 0x0000 }, + { 0x8502, 0x0000 }, + { 0x8503, 0x0000 }, + { 0x8504, 0x0000 }, + { 0x8505, 0x0000 }, + { 0x8506, 0x0000 }, + { 0x8507, 0x0000 }, + { 0x8508, 0x0000 }, + { 0x8509, 0x0000 }, + { 0x850a, 0x0000 }, + { 0x850b, 0x0000 }, + { 0x850c, 0x0000 }, + { 0x850d, 0x0000 }, + { 0x850e, 0x0000 }, + { 0x850f, 0x0000 }, + { 0x8510, 0x0000 }, + { 0x8511, 0x0000 }, + { 0x8512, 0x0000 }, + { 0x8513, 0x0000 }, + { 0x8514, 0x0000 }, + { 0x8515, 0x0000 }, + { 0x8516, 0x0000 }, + { 0x8517, 0x0000 }, + { 0x8518, 0x0000 }, + { 0x8519, 0x0000 }, + { 0x851a, 0x0000 }, + { 0x851b, 0x0000 }, + { 0x851c, 0x0000 }, + { 0x851d, 0x0000 }, + { 0x851e, 0x0000 }, + { 0x851f, 0x0000 }, + { 0x8520, 0x0000 }, + { 0x8521, 0x0000 }, + { 0x8522, 0x0000 }, + { 0x8523, 0x0000 }, + { 0x8524, 0x0000 }, + { 0x8525, 0x0000 }, + { 0x8526, 0x0000 }, + { 0x8527, 0x0000 }, + { 0x8528, 0x0000 }, + { 0x8529, 0x0000 }, + { 0x852a, 0x0000 }, + { 0x852b, 0x0000 }, + { 0x852c, 0x0000 }, + { 0x852d, 0x0000 }, + { 0x852e, 0x0000 }, + { 0x852f, 0x0000 }, + { 0x8530, 0x0000 }, + { 0x8531, 0x0000 }, + { 0x8532, 0x0000 }, + { 0x8533, 0x0000 }, + { 0x8534, 0x0000 }, + { 0x8535, 0x0000 }, + { 0x8536, 0x0000 }, + { 0x8537, 0x0000 }, + { 0x8538, 0x0000 }, + { 0x8539, 0x0000 }, + { 0x853a, 0x0000 }, + { 0x853b, 0x0000 }, + { 0x853c, 0x0000 }, + { 0x853d, 0x0000 }, + { 0x853e, 0x0000 }, + { 0x853f, 0x0000 }, + { 0x8540, 0x0000 }, + { 0x8541, 0x0000 }, + { 0x8542, 0x0000 }, + { 0x8543, 0x0000 }, + { 0x8544, 0x0000 }, + { 0x8545, 0x0000 }, + { 0x8546, 0x0000 }, + { 0x8547, 0x0000 }, + { 0x8548, 0x0000 }, + { 0x8549, 0x0000 }, + { 0x854a, 0x0000 }, + { 0x854b, 0x0000 }, + { 0x854c, 0x0000 }, + { 0x854d, 0x0000 }, + { 0x854e, 0x0000 }, + { 0x854f, 0x0000 }, + { 0x8550, 0x0000 }, + { 0x8551, 0x0000 }, + { 0x8552, 0x0000 }, + { 0x8553, 0x0000 }, + { 0x8554, 0x0000 }, + { 0x8555, 0x0000 }, + { 0x8556, 0x0000 }, + { 0x8557, 0x0000 }, + { 0x8558, 0x0000 }, + { 0x8559, 0x0000 }, + { 0x855a, 0x0000 }, + { 0x855b, 0x0000 }, + { 0x855c, 0x0000 }, + { 0x855d, 0x0000 }, + { 0x855e, 0x0000 }, + { 0x855f, 0x0000 }, + { 0x8560, 0x0000 }, + { 0x8561, 0x0000 }, + { 0x8562, 0x0000 }, + { 0x8563, 0x0000 }, + { 0x8564, 0x0000 }, + { 0x8565, 0x0000 }, + { 0x8566, 0x0000 }, + { 0x8567, 0x0000 }, + { 0x8568, 0x0000 }, + { 0x8569, 0x0000 }, + { 0x856a, 0x0000 }, + { 0x856b, 0x0000 }, + { 0x856c, 0x0000 }, + { 0x856d, 0x0000 }, + { 0x856e, 0x0000 }, + { 0x856f, 0x0000 }, + { 0x8570, 0x0000 }, + { 0x8571, 0x0000 }, + { 0x8572, 0x0000 }, + { 0x8573, 0x0000 }, + { 0x8574, 0x0000 }, + { 0x8575, 0x0000 }, + { 0x8576, 0x0000 }, + { 0x8577, 0x0000 }, + { 0x8578, 0x0000 }, + { 0x8579, 0x0000 }, + { 0x857a, 0x0000 }, + { 0x857b, 0x0000 }, + { 0x857c, 0x0000 }, + { 0x857d, 0x0000 }, + { 0x857e, 0x0000 }, + { 0x857f, 0x0000 }, + { 0x8580, 0x0000 }, + { 0x8581, 0x0000 }, + { 0x8582, 0x0000 }, + { 0x8583, 0x0000 }, + { 0x8584, 0x0000 }, + { 0x8585, 0x0000 }, + { 0x8586, 0x0000 }, + { 0x8587, 0x0000 }, + { 0x8588, 0x0000 }, + { 0x8589, 0x0000 }, + { 0x858a, 0x0000 }, + { 0x858b, 0x0000 }, + { 0x858c, 0x0000 }, + { 0x858d, 0x0000 }, + { 0x858e, 0x0000 }, + { 0x858f, 0x0000 }, + { 0x8590, 0x0000 }, + { 0x8591, 0x0000 }, + { 0x8592, 0x0000 }, + { 0x8593, 0x0000 }, + { 0x8594, 0x0000 }, + { 0x8595, 0x0000 }, + { 0x8596, 0x0000 }, + { 0x8597, 0x0000 }, + { 0x8598, 0x0000 }, + { 0x8599, 0x0000 }, + { 0x859a, 0x0000 }, + { 0x859b, 0x0000 }, + { 0x859c, 0x0000 }, + { 0x859d, 0x0000 }, + { 0x859e, 0x0000 }, + { 0x859f, 0x0000 }, + { 0x85a0, 0x0000 }, + { 0x85a1, 0x0000 }, + { 0x85a2, 0x0000 }, + { 0x85a3, 0x0000 }, + { 0x85a4, 0x0000 }, + { 0x85a5, 0x0000 }, + { 0x85a6, 0x0000 }, + { 0x85a7, 0x0000 }, + { 0x85a8, 0x0000 }, + { 0x85a9, 0x0000 }, + { 0x85aa, 0x0000 }, + { 0x85ab, 0x0000 }, + { 0x85ac, 0x0000 }, + { 0x85ad, 0x0000 }, + { 0x85ae, 0x0000 }, + { 0x85af, 0x0000 }, + { 0x85b0, 0x0000 }, + { 0x85b1, 0x0000 }, + { 0x85b2, 0x0000 }, + { 0x85b3, 0x0000 }, + { 0x85b4, 0x0000 }, + { 0x85b5, 0x0000 }, + { 0x85b6, 0x0000 }, + { 0x85b7, 0x0000 }, + { 0x85b8, 0x0000 }, + { 0x85b9, 0x0000 }, + { 0x85ba, 0x0000 }, + { 0x85bb, 0x0000 }, + { 0x85bc, 0x0000 }, + { 0x85bd, 0x0000 }, + { 0x85be, 0x0000 }, + { 0x85bf, 0x0000 }, + { 0x85c0, 0x0000 }, + { 0x85c1, 0x0000 }, + { 0x85c2, 0x0000 }, + { 0x85c3, 0x0000 }, + { 0x85c4, 0x0000 }, + { 0x85c5, 0x0000 }, + { 0x85c6, 0x0000 }, + { 0x85c7, 0x0000 }, + { 0x85c8, 0x0000 }, + { 0x85c9, 0x0000 }, + { 0x85ca, 0x0000 }, + { 0x85cb, 0x0000 }, + { 0x85cc, 0x0000 }, + { 0x85cd, 0x0000 }, + { 0x85ce, 0x0000 }, + { 0x85cf, 0x0000 }, + { 0x85d0, 0x0000 }, + { 0x85d1, 0x0000 }, + { 0x85d2, 0x0000 }, + { 0x85d3, 0x0000 }, + { 0x85d4, 0x0000 }, + { 0x85d5, 0x0000 }, + { 0x85d6, 0x0000 }, + { 0x85d7, 0x0000 }, + { 0x85d8, 0x0000 }, + { 0x85d9, 0x0000 }, + { 0x85da, 0x0000 }, + { 0x85db, 0x0000 }, + { 0x85dc, 0x0000 }, + { 0x85dd, 0x0000 }, + { 0x85de, 0x0000 }, + { 0x85df, 0x0000 }, + { 0x85e0, 0x0000 }, + { 0x85e1, 0x0000 }, + { 0x85e2, 0x0000 }, + { 0x85e3, 0x0000 }, + { 0x85e4, 0x0000 }, + { 0x85e5, 0x0000 }, + { 0x85e6, 0x0000 }, + { 0x85e7, 0x0000 }, + { 0x85e8, 0x0000 }, + { 0x85e9, 0x0000 }, + { 0x85ea, 0x0000 }, + { 0x85eb, 0x0000 }, + { 0x85ec, 0x0000 }, + { 0x85ed, 0x0000 }, + { 0x85ee, 0x0000 }, + { 0x85ef, 0x0000 }, + { 0x85f0, 0x0000 }, + { 0x85f1, 0x0000 }, + { 0x85f2, 0x0000 }, + { 0x85f3, 0x0000 }, + { 0x85f4, 0x0000 }, + { 0x85f5, 0x0000 }, + { 0x85f6, 0x0000 }, + { 0x85f7, 0x0000 }, + { 0x85f8, 0x0000 }, + { 0x85f9, 0x0000 }, + { 0x85fa, 0x0000 }, + { 0x85fb, 0x0000 }, + { 0x85fc, 0x0000 }, + { 0x85fd, 0x0000 }, + { 0x85fe, 0x0000 }, + { 0x85ff, 0x0000 }, + { 0x8600, 0x0000 }, + { 0x8601, 0x0000 }, + { 0x8602, 0x0000 }, + { 0x8603, 0x0000 }, + { 0x8604, 0x0000 }, + { 0x8605, 0x0000 }, + { 0x8606, 0x0000 }, + { 0x8607, 0x0000 }, + { 0x8608, 0x0000 }, + { 0x8609, 0x0000 }, + { 0x860a, 0x0000 }, + { 0x860b, 0x0000 }, + { 0x860c, 0x0000 }, + { 0x860d, 0x0000 }, + { 0x860e, 0x0000 }, + { 0x860f, 0x0000 }, + { 0x8610, 0x0000 }, + { 0x8611, 0x0000 }, + { 0x8612, 0x0000 }, + { 0x8613, 0x0000 }, + { 0x8614, 0x0000 }, + { 0x8615, 0x0000 }, + { 0x8616, 0x0000 }, + { 0x8617, 0x0000 }, + { 0x8618, 0x0000 }, + { 0x8619, 0x0000 }, + { 0x861a, 0x0000 }, + { 0x861b, 0x0000 }, + { 0x861c, 0x0000 }, + { 0x861d, 0x0000 }, + { 0x861e, 0x0000 }, + { 0x861f, 0x0000 }, + { 0x8620, 0x0000 }, + { 0x8621, 0x0000 }, + { 0x8622, 0x0000 }, + { 0x8623, 0x0000 }, + { 0x8624, 0x0000 }, + { 0x8625, 0x0000 }, + { 0x8626, 0x0000 }, + { 0x8627, 0x0000 }, + { 0x8628, 0x0000 }, + { 0x8629, 0x0000 }, + { 0x862a, 0x0000 }, + { 0x862b, 0x0000 }, + { 0x862c, 0x0000 }, + { 0x862d, 0x0000 }, + { 0x862e, 0x0000 }, + { 0x862f, 0x0000 }, + { 0x8630, 0x0000 }, + { 0x8631, 0x0000 }, + { 0x8632, 0x0000 }, + { 0x8633, 0x0000 }, + { 0x8634, 0x0000 }, + { 0x8635, 0x0000 }, + { 0x8636, 0x0000 }, + { 0x8637, 0x0000 }, + { 0x8638, 0x0000 }, + { 0x8639, 0x0000 }, + { 0x863a, 0x0000 }, + { 0x863b, 0x0000 }, + { 0x863c, 0x0000 }, + { 0x863d, 0x0000 }, + { 0x863e, 0x0000 }, + { 0x863f, 0x0000 }, + { 0x8640, 0x0000 }, + { 0x8641, 0x0000 }, + { 0x8642, 0x0000 }, + { 0x8643, 0x0000 }, + { 0x8644, 0x0000 }, + { 0x8645, 0x0000 }, + { 0x8646, 0x0000 }, + { 0x8647, 0x0000 }, + { 0x8648, 0x0000 }, + { 0x8649, 0x0000 }, + { 0x864a, 0x0000 }, + { 0x864b, 0x0000 }, + { 0x864c, 0x0000 }, + { 0x864d, 0x0000 }, + { 0x864e, 0x0000 }, + { 0x864f, 0x0000 }, + { 0x8650, 0x0000 }, + { 0x8651, 0x0000 }, + { 0x8652, 0x0000 }, + { 0x8653, 0x0000 }, + { 0x8654, 0x0000 }, + { 0x8655, 0x0000 }, + { 0x8656, 0x0000 }, + { 0x8657, 0x0000 }, + { 0x8658, 0x0000 }, + { 0x8659, 0x0000 }, + { 0x865a, 0x0000 }, + { 0x865b, 0x0000 }, + { 0x865c, 0x0000 }, + { 0x865d, 0x0000 }, + { 0x865e, 0x0000 }, + { 0x865f, 0x0000 }, + { 0x8660, 0x0000 }, + { 0x8661, 0x0000 }, + { 0x8662, 0x0000 }, + { 0x8663, 0x0000 }, + { 0x8664, 0x0000 }, + { 0x8665, 0x0000 }, + { 0x8666, 0x0000 }, + { 0x8667, 0x0000 }, + { 0x8668, 0x0000 }, + { 0x8669, 0x0000 }, + { 0x866a, 0x0000 }, + { 0x866b, 0x0000 }, + { 0x866c, 0x0000 }, + { 0x866d, 0x0000 }, + { 0x866e, 0x0000 }, + { 0x866f, 0x0000 }, + { 0x8670, 0x0000 }, + { 0x8671, 0x0000 }, + { 0x8672, 0x0000 }, + { 0x8673, 0x0000 }, + { 0x8674, 0x0000 }, + { 0x8675, 0x0000 }, + { 0x8676, 0x0000 }, + { 0x8677, 0x0000 }, + { 0x8678, 0x0000 }, + { 0x8679, 0x0000 }, + { 0x867a, 0x0000 }, + { 0x867b, 0x0000 }, + { 0x867c, 0x0000 }, + { 0x867d, 0x0000 }, + { 0x867e, 0x0000 }, + { 0x867f, 0x0000 }, + { 0x8680, 0x0000 }, + { 0x8681, 0x0000 }, + { 0x8682, 0x0000 }, + { 0x8683, 0x0000 }, + { 0x8684, 0x0000 }, + { 0x8685, 0x0000 }, + { 0x8686, 0x0000 }, + { 0x8687, 0x0000 }, + { 0x8688, 0x0000 }, + { 0x8689, 0x0000 }, + { 0x868a, 0x0000 }, + { 0x868b, 0x0000 }, + { 0x868c, 0x0000 }, + { 0x868d, 0x0000 }, + { 0x868e, 0x0000 }, + { 0x868f, 0x0000 }, + { 0x8690, 0x0000 }, + { 0x8691, 0x0000 }, + { 0x8692, 0x0000 }, + { 0x8693, 0x0000 }, + { 0x8694, 0x0000 }, + { 0x8695, 0x0000 }, + { 0x8696, 0x0000 }, + { 0x8697, 0x0000 }, + { 0x8698, 0x0000 }, + { 0x8699, 0x0000 }, + { 0x869a, 0x0000 }, + { 0x869b, 0x0000 }, + { 0x869c, 0x0000 }, + { 0x869d, 0x0000 }, + { 0x869e, 0x0000 }, + { 0x869f, 0x0000 }, + { 0x86a0, 0x0000 }, + { 0x86a1, 0x0000 }, + { 0x86a2, 0x0000 }, + { 0x86a3, 0x0000 }, + { 0x86a4, 0x0000 }, + { 0x86a5, 0x0000 }, + { 0x86a6, 0x0000 }, + { 0x86a7, 0x0000 }, + { 0x86a8, 0x0000 }, + { 0x86a9, 0x0000 }, + { 0x86aa, 0x0000 }, + { 0x86ab, 0x0000 }, + { 0x86ac, 0x0000 }, + { 0x86ad, 0x0000 }, + { 0x86ae, 0x0000 }, + { 0x86af, 0x0000 }, + { 0x86b0, 0x0000 }, + { 0x86b1, 0x0000 }, + { 0x86b2, 0x0000 }, + { 0x86b3, 0x0000 }, + { 0x86b4, 0x0000 }, + { 0x86b5, 0x0000 }, + { 0x86b6, 0x0000 }, + { 0x86b7, 0x0000 }, + { 0x86b8, 0x0000 }, + { 0x86b9, 0x0000 }, + { 0x86ba, 0x0000 }, + { 0x86bb, 0x0000 }, + { 0x86bc, 0x0000 }, + { 0x86bd, 0x0000 }, + { 0x86be, 0x0000 }, + { 0x86bf, 0x0000 }, + { 0x86c0, 0x0000 }, + { 0x86c1, 0x0000 }, + { 0x86c2, 0x0000 }, + { 0x86c3, 0x0000 }, + { 0x86c4, 0x0000 }, + { 0x86c5, 0x0000 }, + { 0x86c6, 0x0000 }, + { 0x86c7, 0x0000 }, + { 0x86c8, 0x0000 }, + { 0x86c9, 0x0000 }, + { 0x86ca, 0x0000 }, + { 0x86cb, 0x0000 }, + { 0x86cc, 0x0000 }, + { 0x86cd, 0x0000 }, + { 0x86ce, 0x0000 }, + { 0x86cf, 0x0000 }, + { 0x86d0, 0x0000 }, + { 0x86d1, 0x0000 }, + { 0x86d2, 0x0000 }, + { 0x86d3, 0x0000 }, + { 0x86d4, 0x0000 }, + { 0x86d5, 0x0000 }, + { 0x86d6, 0x0000 }, + { 0x86d7, 0x0000 }, + { 0x86d8, 0x0000 }, + { 0x86d9, 0x0000 }, + { 0x86da, 0x0000 }, + { 0x86db, 0x0000 }, + { 0x86dc, 0x0000 }, + { 0x86dd, 0x0000 }, + { 0x86de, 0x0000 }, + { 0x86df, 0x0000 }, + { 0x86e0, 0x0000 }, + { 0x86e1, 0x0000 }, + { 0x86e2, 0x0000 }, + { 0x86e3, 0x0000 }, + { 0x86e4, 0x0000 }, + { 0x86e5, 0x0000 }, + { 0x86e6, 0x0000 }, + { 0x86e7, 0x0000 }, + { 0x86e8, 0x0000 }, + { 0x86e9, 0x0000 }, + { 0x86ea, 0x0000 }, + { 0x86eb, 0x0000 }, + { 0x86ec, 0x0000 }, + { 0x86ed, 0x0000 }, + { 0x86ee, 0x0000 }, + { 0x86ef, 0x0000 }, + { 0x86f0, 0x0000 }, + { 0x86f1, 0x0000 }, + { 0x86f2, 0x0000 }, + { 0x86f3, 0x0000 }, + { 0x86f4, 0x0000 }, + { 0x86f5, 0x0000 }, + { 0x86f6, 0x0000 }, + { 0x86f7, 0x0000 }, + { 0x86f8, 0x0000 }, + { 0x86f9, 0x0000 }, + { 0x86fa, 0x0000 }, + { 0x86fb, 0x0000 }, + { 0x86fc, 0x0000 }, + { 0x86fd, 0x0000 }, + { 0x86fe, 0x0000 }, + { 0x86ff, 0x0000 }, + { 0x8700, 0x0000 }, + { 0x8701, 0x0000 }, + { 0x8702, 0x0000 }, + { 0x8703, 0x0000 }, + { 0x8704, 0x0000 }, + { 0x8705, 0x0000 }, + { 0x8706, 0x0000 }, + { 0x8707, 0x0000 }, + { 0x8708, 0x0000 }, + { 0x8709, 0x0000 }, + { 0x870a, 0x0000 }, + { 0x870b, 0x0000 }, + { 0x870c, 0x0000 }, + { 0x870d, 0x0000 }, + { 0x870e, 0x0000 }, + { 0x870f, 0x0000 }, + { 0x8710, 0x0000 }, + { 0x8711, 0x0000 }, + { 0x8712, 0x0000 }, + { 0x8713, 0x0000 }, + { 0x8714, 0x0000 }, + { 0x8715, 0x0000 }, + { 0x8716, 0x0000 }, + { 0x8717, 0x0000 }, + { 0x8718, 0x0000 }, + { 0x8719, 0x0000 }, + { 0x871a, 0x0000 }, + { 0x871b, 0x0000 }, + { 0x871c, 0x0000 }, + { 0x871d, 0x0000 }, + { 0x871e, 0x0000 }, + { 0x871f, 0x0000 }, + { 0x8720, 0x0000 }, + { 0x8721, 0x0000 }, + { 0x8722, 0x0000 }, + { 0x8723, 0x0000 }, + { 0x8724, 0x0000 }, + { 0x8725, 0x0000 }, + { 0x8726, 0x0000 }, + { 0x8727, 0x0000 }, + { 0x8728, 0x0000 }, + { 0x8729, 0x0000 }, + { 0x872a, 0x0000 }, + { 0x872b, 0x0000 }, + { 0x872c, 0x0000 }, + { 0x872d, 0x0000 }, + { 0x872e, 0x0000 }, + { 0x872f, 0x0000 }, + { 0x8730, 0x0000 }, + { 0x8731, 0x0000 }, + { 0x8732, 0x0000 }, + { 0x8733, 0x0000 }, + { 0x8734, 0x0000 }, + { 0x8735, 0x0000 }, + { 0x8736, 0x0000 }, + { 0x8737, 0x0000 }, + { 0x8738, 0x0000 }, + { 0x8739, 0x0000 }, + { 0x873a, 0x0000 }, + { 0x873b, 0x0000 }, + { 0x873c, 0x0000 }, + { 0x873d, 0x0000 }, + { 0x873e, 0x0000 }, + { 0x873f, 0x0000 }, + { 0x8740, 0x0000 }, + { 0x8741, 0x0000 }, + { 0x8742, 0x0000 }, + { 0x8743, 0x0000 }, + { 0x8744, 0x0000 }, + { 0x8745, 0x0000 }, + { 0x8746, 0x0000 }, + { 0x8747, 0x0000 }, + { 0x8748, 0x0000 }, + { 0x8749, 0x0000 }, + { 0x874a, 0x0000 }, + { 0x874b, 0x0000 }, + { 0x874c, 0x0000 }, + { 0x874d, 0x0000 }, + { 0x874e, 0x0000 }, + { 0x874f, 0x0000 }, + { 0x8750, 0x0000 }, + { 0x8751, 0x0000 }, + { 0x8752, 0x0000 }, + { 0x8753, 0x0000 }, + { 0x8754, 0x0000 }, + { 0x8755, 0x0000 }, + { 0x8756, 0x0000 }, + { 0x8757, 0x0000 }, + { 0x8758, 0x0000 }, + { 0x8759, 0x0000 }, + { 0x875a, 0x0000 }, + { 0x875b, 0x0000 }, + { 0x875c, 0x0000 }, + { 0x875d, 0x0000 }, + { 0x875e, 0x0000 }, + { 0x875f, 0x0000 }, + { 0x8760, 0x0000 }, + { 0x8761, 0x0000 }, + { 0x8762, 0x0000 }, + { 0x8763, 0x0000 }, + { 0x8764, 0x0000 }, + { 0x8765, 0x0000 }, + { 0x8766, 0x0000 }, + { 0x8767, 0x0000 }, + { 0x8768, 0x0000 }, + { 0x8769, 0x0000 }, + { 0x876a, 0x0000 }, + { 0x876b, 0x0000 }, + { 0x876c, 0x0000 }, + { 0x876d, 0x0000 }, + { 0x876e, 0x0000 }, + { 0x876f, 0x0000 }, + { 0x8770, 0x0000 }, + { 0x8771, 0x0000 }, + { 0x8772, 0x0000 }, + { 0x8773, 0x0000 }, + { 0x8774, 0x0000 }, + { 0x8775, 0x0000 }, + { 0x8776, 0x0000 }, + { 0x8777, 0x0000 }, + { 0x8778, 0x0000 }, + { 0x8779, 0x0000 }, + { 0x877a, 0x0000 }, + { 0x877b, 0x0000 }, + { 0x877c, 0x0000 }, + { 0x877d, 0x0000 }, + { 0x877e, 0x0000 }, + { 0x877f, 0x0000 }, + { 0x8780, 0x0000 }, + { 0x8781, 0x0000 }, + { 0x8782, 0x0000 }, + { 0x8783, 0x0000 }, + { 0x8784, 0x0000 }, + { 0x8785, 0x0000 }, + { 0x8786, 0x0000 }, + { 0x8787, 0x0000 }, + { 0x8788, 0x0000 }, + { 0x8789, 0x0000 }, + { 0x878a, 0x0000 }, + { 0x878b, 0x0000 }, + { 0x878c, 0x0000 }, + { 0x878d, 0x0000 }, + { 0x878e, 0x0000 }, + { 0x878f, 0x0000 }, + { 0x8790, 0x0000 }, + { 0x8791, 0x0000 }, + { 0x8792, 0x0000 }, + { 0x8793, 0x0000 }, + { 0x8794, 0x0000 }, + { 0x8795, 0x0000 }, + { 0x8796, 0x0000 }, + { 0x8797, 0x0000 }, + { 0x8798, 0x0000 }, + { 0x8799, 0x0000 }, + { 0x879a, 0x0000 }, + { 0x879b, 0x0000 }, + { 0x879c, 0x0000 }, + { 0x879d, 0x0000 }, + { 0x879e, 0x0000 }, + { 0x879f, 0x0000 }, + { 0x87a0, 0x0000 }, + { 0x87a1, 0x0000 }, + { 0x87a2, 0x0000 }, + { 0x87a3, 0x0000 }, + { 0x87a4, 0x0000 }, + { 0x87a5, 0x0000 }, + { 0x87a6, 0x0000 }, + { 0x87a7, 0x0000 }, + { 0x87a8, 0x0000 }, + { 0x87a9, 0x0000 }, + { 0x87aa, 0x0000 }, + { 0x87ab, 0x0000 }, + { 0x87ac, 0x0000 }, + { 0x87ad, 0x0000 }, + { 0x87ae, 0x0000 }, + { 0x87af, 0x0000 }, + { 0x87b0, 0x0000 }, + { 0x87b1, 0x0000 }, + { 0x87b2, 0x0000 }, + { 0x87b3, 0x0000 }, + { 0x87b4, 0x0000 }, + { 0x87b5, 0x0000 }, + { 0x87b6, 0x0000 }, + { 0x87b7, 0x0000 }, + { 0x87b8, 0x0000 }, + { 0x87b9, 0x0000 }, + { 0x87ba, 0x0000 }, + { 0x87bb, 0x0000 }, + { 0x87bc, 0x0000 }, + { 0x87bd, 0x0000 }, + { 0x87be, 0x0000 }, + { 0x87bf, 0x0000 }, + { 0x87c0, 0x0000 }, + { 0x87c1, 0x0000 }, + { 0x87c2, 0x0000 }, + { 0x87c3, 0x0000 }, + { 0x87c4, 0x0000 }, + { 0x87c5, 0x0000 }, + { 0x87c6, 0x0000 }, + { 0x87c7, 0x0000 }, + { 0x87c8, 0x0000 }, + { 0x87c9, 0x0000 }, + { 0x87ca, 0x0000 }, + { 0x87cb, 0x0000 }, + { 0x87cc, 0x0000 }, + { 0x87cd, 0x0000 }, + { 0x87ce, 0x0000 }, + { 0x87cf, 0x0000 }, + { 0x87d0, 0x0000 }, + { 0x87d1, 0x0000 }, + { 0x87d2, 0x0000 }, + { 0x87d3, 0x0000 }, + { 0x87d4, 0x0000 }, + { 0x87d5, 0x0000 }, + { 0x87d6, 0x0000 }, + { 0x87d7, 0x0000 }, + { 0x87d8, 0x0000 }, + { 0x87d9, 0x0000 }, + { 0x87da, 0x0000 }, + { 0x87db, 0x0000 }, + { 0x87dc, 0x0000 }, + { 0x87dd, 0x0000 }, + { 0x87de, 0x0000 }, + { 0x87df, 0x0000 }, + { 0x87e0, 0x0000 }, + { 0x87e1, 0x0000 }, + { 0x87e2, 0x0000 }, + { 0x87e3, 0x0000 }, + { 0x87e4, 0x0000 }, + { 0x87e5, 0x0000 }, + { 0x87e6, 0x0000 }, + { 0x87e7, 0x0000 }, + { 0x87e8, 0x0000 }, + { 0x87e9, 0x0000 }, + { 0x87ea, 0x0000 }, + { 0x87eb, 0x0000 }, + { 0x87ec, 0x0000 }, + { 0x87ed, 0x0000 }, + { 0x87ee, 0x0000 }, + { 0x87ef, 0x0000 }, + { 0x87f0, 0x0000 }, + { 0x87f1, 0x0000 }, + { 0x87f2, 0x0000 }, + { 0x87f3, 0x0000 }, + { 0x87f4, 0x0000 }, + { 0x87f5, 0x0000 }, + { 0x87f6, 0x0000 }, + { 0x87f7, 0x0000 }, + { 0x87f8, 0x0000 }, + { 0x87f9, 0x0000 }, + { 0x87fa, 0x0000 }, + { 0x87fb, 0x0000 }, + { 0x87fc, 0x0000 }, + { 0x87fd, 0x0000 }, + { 0x87fe, 0x0000 }, + { 0x87ff, 0x0000 }, + { 0x8800, 0x0000 }, + { 0x8801, 0x0000 }, + { 0x8802, 0x0000 }, + { 0x8803, 0x0000 }, + { 0x8804, 0x0000 }, + { 0x8805, 0x0000 }, + { 0x8806, 0x0000 }, + { 0x8807, 0x0000 }, + { 0x8808, 0x0000 }, + { 0x8809, 0x0000 }, + { 0x880a, 0x0000 }, + { 0x880b, 0x0000 }, + { 0x880c, 0x0000 }, + { 0x880d, 0x0000 }, + { 0x880e, 0x0000 }, + { 0x880f, 0x0000 }, + { 0x8810, 0x0000 }, + { 0x8811, 0x0000 }, + { 0x8812, 0x0000 }, + { 0x8813, 0x0000 }, + { 0x8814, 0x0000 }, + { 0x8815, 0x0000 }, + { 0x8816, 0x0000 }, + { 0x8817, 0x0000 }, + { 0x8818, 0x0000 }, + { 0x8819, 0x0000 }, + { 0x881a, 0x0000 }, + { 0x881b, 0x0000 }, + { 0x881c, 0x0000 }, + { 0x881d, 0x0000 }, + { 0x881e, 0x0000 }, + { 0x881f, 0x0000 }, + { 0x8820, 0x0000 }, + { 0x8821, 0x0000 }, + { 0x8822, 0x0000 }, + { 0x8823, 0x0000 }, + { 0x8824, 0x0000 }, + { 0x8825, 0x0000 }, + { 0x8826, 0x0000 }, + { 0x8827, 0x0000 }, + { 0x8828, 0x0000 }, + { 0x8829, 0x0000 }, + { 0x882a, 0x0000 }, + { 0x882b, 0x0000 }, + { 0x882c, 0x0000 }, + { 0x882d, 0x0000 }, + { 0x882e, 0x0000 }, + { 0x882f, 0x0000 }, + { 0x8830, 0x0000 }, + { 0x8831, 0x0000 }, + { 0x8832, 0x0000 }, + { 0x8833, 0x0000 }, + { 0x8834, 0x0000 }, + { 0x8835, 0x0000 }, + { 0x8836, 0x0000 }, + { 0x8837, 0x0000 }, + { 0x8838, 0x0000 }, + { 0x8839, 0x0000 }, + { 0x883a, 0x0000 }, + { 0x883b, 0x0000 }, + { 0x883c, 0x0000 }, + { 0x883d, 0x0000 }, + { 0x883e, 0x0000 }, + { 0x883f, 0x0000 }, + { 0x8840, 0x0000 }, + { 0x8841, 0x0000 }, + { 0x8842, 0x0000 }, + { 0x8843, 0x0000 }, + { 0x8844, 0x0000 }, + { 0x8845, 0x0000 }, + { 0x8846, 0x0000 }, + { 0x8847, 0x0000 }, + { 0x8848, 0x0000 }, + { 0x8849, 0x0000 }, + { 0x884a, 0x0000 }, + { 0x884b, 0x0000 }, + { 0x884c, 0x0000 }, + { 0x884d, 0x0000 }, + { 0x884e, 0x0000 }, + { 0x884f, 0x0000 }, + { 0x8850, 0x0000 }, + { 0x8851, 0x0000 }, + { 0x8852, 0x0000 }, + { 0x8853, 0x0000 }, + { 0x8854, 0x0000 }, + { 0x8855, 0x0000 }, + { 0x8856, 0x0000 }, + { 0x8857, 0x0000 }, + { 0x8858, 0x0000 }, + { 0x8859, 0x0000 }, + { 0x885a, 0x0000 }, + { 0x885b, 0x0000 }, + { 0x885c, 0x0000 }, + { 0x885d, 0x0000 }, + { 0x885e, 0x0000 }, + { 0x885f, 0x0000 }, + { 0x8860, 0x0000 }, + { 0x8861, 0x0000 }, + { 0x8862, 0x0000 }, + { 0x8863, 0x0000 }, + { 0x8864, 0x0000 }, + { 0x8865, 0x0000 }, + { 0x8866, 0x0000 }, + { 0x8867, 0x0000 }, + { 0x8868, 0x0000 }, + { 0x8869, 0x0000 }, + { 0x886a, 0x0000 }, + { 0x886b, 0x0000 }, + { 0x886c, 0x0000 }, + { 0x886d, 0x0000 }, + { 0x886e, 0x0000 }, + { 0x886f, 0x0000 }, + { 0x8870, 0x0000 }, + { 0x8871, 0x0000 }, + { 0x8872, 0x0000 }, + { 0x8873, 0x0000 }, + { 0x8874, 0x0000 }, + { 0x8875, 0x0000 }, + { 0x8876, 0x0000 }, + { 0x8877, 0x0000 }, + { 0x8878, 0x0000 }, + { 0x8879, 0x0000 }, + { 0x887a, 0x0000 }, + { 0x887b, 0x0000 }, + { 0x887c, 0x0000 }, + { 0x887d, 0x0000 }, + { 0x887e, 0x0000 }, + { 0x887f, 0x0000 }, + { 0x8880, 0x0000 }, + { 0x8881, 0x0000 }, + { 0x8882, 0x0000 }, + { 0x8883, 0x0000 }, + { 0x8884, 0x0000 }, + { 0x8885, 0x0000 }, + { 0x8886, 0x0000 }, + { 0x8887, 0x0000 }, + { 0x8888, 0x0000 }, + { 0x8889, 0x0000 }, + { 0x888a, 0x0000 }, + { 0x888b, 0x0000 }, + { 0x888c, 0x0000 }, + { 0x888d, 0x0000 }, + { 0x888e, 0x0000 }, + { 0x888f, 0x0000 }, + { 0x8890, 0x0000 }, + { 0x8891, 0x0000 }, + { 0x8892, 0x0000 }, + { 0x8893, 0x0000 }, + { 0x8894, 0x0000 }, + { 0x8895, 0x0000 }, + { 0x8896, 0x0000 }, + { 0x8897, 0x0000 }, + { 0x8898, 0x0000 }, + { 0x8899, 0x0000 }, + { 0x889a, 0x0000 }, + { 0x889b, 0x0000 }, + { 0x889c, 0x0000 }, + { 0x889d, 0x0000 }, + { 0x889e, 0x0000 }, + { 0x889f, 0x0000 }, + { 0x88a0, 0x0000 }, + { 0x88a1, 0x0000 }, + { 0x88a2, 0x0000 }, + { 0x88a3, 0x0000 }, + { 0x88a4, 0x0000 }, + { 0x88a5, 0x0000 }, + { 0x88a6, 0x0000 }, + { 0x88a7, 0x0000 }, + { 0x88a8, 0x0000 }, + { 0x88a9, 0x0000 }, + { 0x88aa, 0x0000 }, + { 0x88ab, 0x0000 }, + { 0x88ac, 0x0000 }, + { 0x88ad, 0x0000 }, + { 0x88ae, 0x0000 }, + { 0x88af, 0x0000 }, + { 0x88b0, 0x0000 }, + { 0x88b1, 0x0000 }, + { 0x88b2, 0x0000 }, + { 0x88b3, 0x0000 }, + { 0x88b4, 0x0000 }, + { 0x88b5, 0x0000 }, + { 0x88b6, 0x0000 }, + { 0x88b7, 0x0000 }, + { 0x88b8, 0x0000 }, + { 0x88b9, 0x0000 }, + { 0x88ba, 0x0000 }, + { 0x88bb, 0x0000 }, + { 0x88bc, 0x0000 }, + { 0x88bd, 0x0000 }, + { 0x88be, 0x0000 }, + { 0x88bf, 0x0000 }, + { 0x88c0, 0x0000 }, + { 0x88c1, 0x0000 }, + { 0x88c2, 0x0000 }, + { 0x88c3, 0x0000 }, + { 0x88c4, 0x0000 }, + { 0x88c5, 0x0000 }, + { 0x88c6, 0x0000 }, + { 0x88c7, 0x0000 }, + { 0x88c8, 0x0000 }, + { 0x88c9, 0x0000 }, + { 0x88ca, 0x0000 }, + { 0x88cb, 0x0000 }, + { 0x88cc, 0x0000 }, + { 0x88cd, 0x0000 }, + { 0x88ce, 0x0000 }, + { 0x88cf, 0x0000 }, + { 0x88d0, 0x0000 }, + { 0x88d1, 0x0000 }, + { 0x88d2, 0x0000 }, + { 0x88d3, 0x0000 }, + { 0x88d4, 0x0000 }, + { 0x88d5, 0x0000 }, + { 0x88d6, 0x0000 }, + { 0x88d7, 0x0000 }, + { 0x88d8, 0x0000 }, + { 0x88d9, 0x0000 }, + { 0x88da, 0x0000 }, + { 0x88db, 0x0000 }, + { 0x88dc, 0x0000 }, + { 0x88dd, 0x0000 }, + { 0x88de, 0x0000 }, + { 0x88df, 0x0000 }, + { 0x88e0, 0x0000 }, + { 0x88e1, 0x0000 }, + { 0x88e2, 0x0000 }, + { 0x88e3, 0x0000 }, + { 0x88e4, 0x0000 }, + { 0x88e5, 0x0000 }, + { 0x88e6, 0x0000 }, + { 0x88e7, 0x0000 }, + { 0x88e8, 0x0000 }, + { 0x88e9, 0x0000 }, + { 0x88ea, 0x0000 }, + { 0x88eb, 0x0000 }, + { 0x88ec, 0x0000 }, + { 0x88ed, 0x0000 }, + { 0x88ee, 0x0000 }, + { 0x88ef, 0x0000 }, + { 0x88f0, 0x0000 }, + { 0x88f1, 0x0000 }, + { 0x88f2, 0x0000 }, + { 0x88f3, 0x0000 }, + { 0x88f4, 0x0000 }, + { 0x88f5, 0x0000 }, + { 0x88f6, 0x0000 }, + { 0x88f7, 0x0000 }, + { 0x88f8, 0x0000 }, + { 0x88f9, 0x0000 }, + { 0x88fa, 0x0000 }, + { 0x88fb, 0x0000 }, + { 0x88fc, 0x0000 }, + { 0x88fd, 0x0000 }, + { 0x88fe, 0x0000 }, + { 0x88ff, 0x0000 }, + { 0x8900, 0x0000 }, + { 0x8901, 0x0000 }, + { 0x8902, 0x0000 }, + { 0x8903, 0x0000 }, + { 0x8904, 0x0000 }, + { 0x8905, 0x0000 }, + { 0x8906, 0x0000 }, + { 0x8907, 0x0000 }, + { 0x8908, 0x0000 }, + { 0x8909, 0x0000 }, + { 0x890a, 0x0000 }, + { 0x890b, 0x0000 }, + { 0x890c, 0x0000 }, + { 0x890d, 0x0000 }, + { 0x890e, 0x0000 }, + { 0x890f, 0x0000 }, + { 0x8910, 0x0000 }, + { 0x8911, 0x0000 }, + { 0x8912, 0x0000 }, + { 0x8913, 0x0000 }, + { 0x8914, 0x0000 }, + { 0x8915, 0x0000 }, + { 0x8916, 0x0000 }, + { 0x8917, 0x0000 }, + { 0x8918, 0x0000 }, + { 0x8919, 0x0000 }, + { 0x891a, 0x0000 }, + { 0x891b, 0x0000 }, + { 0x891c, 0x0000 }, + { 0x891d, 0x0000 }, + { 0x891e, 0x0000 }, + { 0x891f, 0x0000 }, + { 0x8920, 0x0000 }, + { 0x8921, 0x0000 }, + { 0x8922, 0x0000 }, + { 0x8923, 0x0000 }, + { 0x8924, 0x0000 }, + { 0x8925, 0x0000 }, + { 0x8926, 0x0000 }, + { 0x8927, 0x0000 }, + { 0x8928, 0x0000 }, + { 0x8929, 0x0000 }, + { 0x892a, 0x0000 }, + { 0x892b, 0x0000 }, + { 0x892c, 0x0000 }, + { 0x892d, 0x0000 }, + { 0x892e, 0x0000 }, + { 0x892f, 0x0000 }, + { 0x8930, 0x0000 }, + { 0x8931, 0x0000 }, + { 0x8932, 0x0000 }, + { 0x8933, 0x0000 }, + { 0x8934, 0x0000 }, + { 0x8935, 0x0000 }, + { 0x8936, 0x0000 }, + { 0x8937, 0x0000 }, + { 0x8938, 0x0000 }, + { 0x8939, 0x0000 }, + { 0x893a, 0x0000 }, + { 0x893b, 0x0000 }, + { 0x893c, 0x0000 }, + { 0x893d, 0x0000 }, + { 0x893e, 0x0000 }, + { 0x893f, 0x0000 }, + { 0x8940, 0x0000 }, + { 0x8941, 0x0000 }, + { 0x8942, 0x0000 }, + { 0x8943, 0x0000 }, + { 0x8944, 0x0000 }, + { 0x8945, 0x0000 }, + { 0x8946, 0x0000 }, + { 0x8947, 0x0000 }, + { 0x8948, 0x0000 }, + { 0x8949, 0x0000 }, + { 0x894a, 0x0000 }, + { 0x894b, 0x0000 }, + { 0x894c, 0x0000 }, + { 0x894d, 0x0000 }, + { 0x894e, 0x0000 }, + { 0x894f, 0x0000 }, + { 0x8950, 0x0000 }, + { 0x8951, 0x0000 }, + { 0x8952, 0x0000 }, + { 0x8953, 0x0000 }, + { 0x8954, 0x0000 }, + { 0x8955, 0x0000 }, + { 0x8956, 0x0000 }, + { 0x8957, 0x0000 }, + { 0x8958, 0x0000 }, + { 0x8959, 0x0000 }, + { 0x895a, 0x0000 }, + { 0x895b, 0x0000 }, + { 0x895c, 0x0000 }, + { 0x895d, 0x0000 }, + { 0x895e, 0x0000 }, + { 0x895f, 0x0000 }, + { 0x8960, 0x0000 }, + { 0x8961, 0x0000 }, + { 0x8962, 0x0000 }, + { 0x8963, 0x0000 }, + { 0x8964, 0x0000 }, + { 0x8965, 0x0000 }, + { 0x8966, 0x0000 }, + { 0x8967, 0x0000 }, + { 0x8968, 0x0000 }, + { 0x8969, 0x0000 }, + { 0x896a, 0x0000 }, + { 0x896b, 0x0000 }, + { 0x896c, 0x0000 }, + { 0x896d, 0x0000 }, + { 0x896e, 0x0000 }, + { 0x896f, 0x0000 }, + { 0x8970, 0x0000 }, + { 0x8971, 0x0000 }, + { 0x8972, 0x0000 }, + { 0x8973, 0x0000 }, + { 0x8974, 0x0000 }, + { 0x8975, 0x0000 }, + { 0x8976, 0x0000 }, + { 0x8977, 0x0000 }, + { 0x8978, 0x0000 }, + { 0x8979, 0x0000 }, + { 0x897a, 0x0000 }, + { 0x897b, 0x0000 }, + { 0x897c, 0x0000 }, + { 0x897d, 0x0000 }, + { 0x897e, 0x0000 }, + { 0x897f, 0x0000 }, + { 0x8980, 0x0000 }, + { 0x8981, 0x0000 }, + { 0x8982, 0x0000 }, + { 0x8983, 0x0000 }, + { 0x8984, 0x0000 }, + { 0x8985, 0x0000 }, + { 0x8986, 0x0000 }, + { 0x8987, 0x0000 }, + { 0x8988, 0x0000 }, + { 0x8989, 0x0000 }, + { 0x898a, 0x0000 }, + { 0x898b, 0x0000 }, + { 0x898c, 0x0000 }, + { 0x898d, 0x0000 }, + { 0x898e, 0x0000 }, + { 0x898f, 0x0000 }, + { 0x8990, 0x0000 }, + { 0x8991, 0x0000 }, + { 0x8992, 0x0000 }, + { 0x8993, 0x0000 }, + { 0x8994, 0x0000 }, + { 0x8995, 0x0000 }, + { 0x8996, 0x0000 }, + { 0x8997, 0x0000 }, + { 0x8998, 0x0000 }, + { 0x8999, 0x0000 }, + { 0x899a, 0x0000 }, + { 0x899b, 0x0000 }, + { 0x899c, 0x0000 }, + { 0x899d, 0x0000 }, + { 0x899e, 0x0000 }, + { 0x899f, 0x0000 }, + { 0x89a0, 0x0000 }, + { 0x89a1, 0x0000 }, + { 0x89a2, 0x0000 }, + { 0x89a3, 0x0000 }, + { 0x89a4, 0x0000 }, + { 0x89a5, 0x0000 }, + { 0x89a6, 0x0000 }, + { 0x89a7, 0x0000 }, + { 0x89a8, 0x0000 }, + { 0x89a9, 0x0000 }, + { 0x89aa, 0x0000 }, + { 0x89ab, 0x0000 }, + { 0x89ac, 0x0000 }, + { 0x89ad, 0x0000 }, + { 0x89ae, 0x0000 }, + { 0x89af, 0x0000 }, + { 0x89b0, 0x0000 }, + { 0x89b1, 0x0000 }, + { 0x89b2, 0x0000 }, + { 0x89b3, 0x0000 }, + { 0x89b4, 0x0000 }, + { 0x89b5, 0x0000 }, + { 0x89b6, 0x0000 }, + { 0x89b7, 0x0000 }, + { 0x89b8, 0x0000 }, + { 0x89b9, 0x0000 }, + { 0x89ba, 0x0000 }, + { 0x89bb, 0x0000 }, + { 0x89bc, 0x0000 }, + { 0x89bd, 0x0000 }, + { 0x89be, 0x0000 }, + { 0x89bf, 0x0000 }, + { 0x89c0, 0x0000 }, + { 0x89c1, 0x0000 }, + { 0x89c2, 0x0000 }, + { 0x89c3, 0x0000 }, + { 0x89c4, 0x0000 }, + { 0x89c5, 0x0000 }, + { 0x89c6, 0x0000 }, + { 0x89c7, 0x0000 }, + { 0x89c8, 0x0000 }, + { 0x89c9, 0x0000 }, + { 0x89ca, 0x0000 }, + { 0x89cb, 0x0000 }, + { 0x89cc, 0x0000 }, + { 0x89cd, 0x0000 }, + { 0x89ce, 0x0000 }, + { 0x89cf, 0x0000 }, + { 0x89d0, 0x0000 }, + { 0x89d1, 0x0000 }, + { 0x89d2, 0x0000 }, + { 0x89d3, 0x0000 }, + { 0x89d4, 0x0000 }, + { 0x89d5, 0x0000 }, + { 0x89d6, 0x0000 }, + { 0x89d7, 0x0000 }, + { 0x89d8, 0x0000 }, + { 0x89d9, 0x0000 }, + { 0x89da, 0x0000 }, + { 0x89db, 0x0000 }, + { 0x89dc, 0x0000 }, + { 0x89dd, 0x0000 }, + { 0x89de, 0x0000 }, + { 0x89df, 0x0000 }, + { 0x89e0, 0x0000 }, + { 0x89e1, 0x0000 }, + { 0x89e2, 0x0000 }, + { 0x89e3, 0x0000 }, + { 0x89e4, 0x0000 }, + { 0x89e5, 0x0000 }, + { 0x89e6, 0x0000 }, + { 0x89e7, 0x0000 }, + { 0x89e8, 0x0000 }, + { 0x89e9, 0x0000 }, + { 0x89ea, 0x0000 }, + { 0x89eb, 0x0000 }, + { 0x89ec, 0x0000 }, + { 0x89ed, 0x0000 }, + { 0x89ee, 0x0000 }, + { 0x89ef, 0x0000 }, + { 0x89f0, 0x0000 }, + { 0x89f1, 0x0000 }, + { 0x89f2, 0x0000 }, + { 0x89f3, 0x0000 }, + { 0x89f4, 0x0000 }, + { 0x89f5, 0x0000 }, + { 0x89f6, 0x0000 }, + { 0x89f7, 0x0000 }, + { 0x89f8, 0x0000 }, + { 0x89f9, 0x0000 }, + { 0x89fa, 0x0000 }, + { 0x89fb, 0x0000 }, + { 0x89fc, 0x0000 }, + { 0x89fd, 0x0000 }, + { 0x89fe, 0x0000 }, + { 0x89ff, 0x0000 }, + { 0x8a00, 0x0000 }, + { 0x8a01, 0x0000 }, + { 0x8a02, 0x0000 }, + { 0x8a03, 0x0000 }, + { 0x8a04, 0x0000 }, + { 0x8a05, 0x0000 }, + { 0x8a06, 0x0000 }, + { 0x8a07, 0x0000 }, + { 0x8a08, 0x0000 }, + { 0x8a09, 0x0000 }, + { 0x8a0a, 0x0000 }, + { 0x8a0b, 0x0000 }, + { 0x8a0c, 0x0000 }, + { 0x8a0d, 0x0000 }, + { 0x8a0e, 0x0000 }, + { 0x8a0f, 0x0000 }, + { 0x8a10, 0x0000 }, + { 0x8a11, 0x0000 }, + { 0x8a12, 0x0000 }, + { 0x8a13, 0x0000 }, + { 0x8a14, 0x0000 }, + { 0x8a15, 0x0000 }, + { 0x8a16, 0x0000 }, + { 0x8a17, 0x0000 }, + { 0x8a18, 0x0000 }, + { 0x8a19, 0x0000 }, + { 0x8a1a, 0x0000 }, + { 0x8a1b, 0x0000 }, + { 0x8a1c, 0x0000 }, + { 0x8a1d, 0x0000 }, + { 0x8a1e, 0x0000 }, + { 0x8a1f, 0x0000 }, + { 0x8a20, 0x0000 }, + { 0x8a21, 0x0000 }, + { 0x8a22, 0x0000 }, + { 0x8a23, 0x0000 }, + { 0x8a24, 0x0000 }, + { 0x8a25, 0x0000 }, + { 0x8a26, 0x0000 }, + { 0x8a27, 0x0000 }, + { 0x8a28, 0x0000 }, + { 0x8a29, 0x0000 }, + { 0x8a2a, 0x0000 }, + { 0x8a2b, 0x0000 }, + { 0x8a2c, 0x0000 }, + { 0x8a2d, 0x0000 }, + { 0x8a2e, 0x0000 }, + { 0x8a2f, 0x0000 }, + { 0x8a30, 0x0000 }, + { 0x8a31, 0x0000 }, + { 0x8a32, 0x0000 }, + { 0x8a33, 0x0000 }, + { 0x8a34, 0x0000 }, + { 0x8a35, 0x0000 }, + { 0x8a36, 0x0000 }, + { 0x8a37, 0x0000 }, + { 0x8a38, 0x0000 }, + { 0x8a39, 0x0000 }, + { 0x8a3a, 0x0000 }, + { 0x8a3b, 0x0000 }, + { 0x8a3c, 0x0000 }, + { 0x8a3d, 0x0000 }, + { 0x8a3e, 0x0000 }, + { 0x8a3f, 0x0000 }, + { 0x8a40, 0x0000 }, + { 0x8a41, 0x0000 }, + { 0x8a42, 0x0000 }, + { 0x8a43, 0x0000 }, + { 0x8a44, 0x0000 }, + { 0x8a45, 0x0000 }, + { 0x8a46, 0x0000 }, + { 0x8a47, 0x0000 }, + { 0x8a48, 0x0000 }, + { 0x8a49, 0x0000 }, + { 0x8a4a, 0x0000 }, + { 0x8a4b, 0x0000 }, + { 0x8a4c, 0x0000 }, + { 0x8a4d, 0x0000 }, + { 0x8a4e, 0x0000 }, + { 0x8a4f, 0x0000 }, + { 0x8a50, 0x0000 }, + { 0x8a51, 0x0000 }, + { 0x8a52, 0x0000 }, + { 0x8a53, 0x0000 }, + { 0x8a54, 0x0000 }, + { 0x8a55, 0x0000 }, + { 0x8a56, 0x0000 }, + { 0x8a57, 0x0000 }, + { 0x8a58, 0x0000 }, + { 0x8a59, 0x0000 }, + { 0x8a5a, 0x0000 }, + { 0x8a5b, 0x0000 }, + { 0x8a5c, 0x0000 }, + { 0x8a5d, 0x0000 }, + { 0x8a5e, 0x0000 }, + { 0x8a5f, 0x0000 }, + { 0x8a60, 0x0000 }, + { 0x8a61, 0x0000 }, + { 0x8a62, 0x0000 }, + { 0x8a63, 0x0000 }, + { 0x8a64, 0x0000 }, + { 0x8a65, 0x0000 }, + { 0x8a66, 0x0000 }, + { 0x8a67, 0x0000 }, + { 0x8a68, 0x0000 }, + { 0x8a69, 0x0000 }, + { 0x8a6a, 0x0000 }, + { 0x8a6b, 0x0000 }, + { 0x8a6c, 0x0000 }, + { 0x8a6d, 0x0000 }, + { 0x8a6e, 0x0000 }, + { 0x8a6f, 0x0000 }, + { 0x8a70, 0x0000 }, + { 0x8a71, 0x0000 }, + { 0x8a72, 0x0000 }, + { 0x8a73, 0x0000 }, + { 0x8a74, 0x0000 }, + { 0x8a75, 0x0000 }, + { 0x8a76, 0x0000 }, + { 0x8a77, 0x0000 }, + { 0x8a78, 0x0000 }, + { 0x8a79, 0x0000 }, + { 0x8a7a, 0x0000 }, + { 0x8a7b, 0x0000 }, + { 0x8a7c, 0x0000 }, + { 0x8a7d, 0x0000 }, + { 0x8a7e, 0x0000 }, + { 0x8a7f, 0x0000 }, + { 0x8a80, 0x0000 }, + { 0x8a81, 0x0000 }, + { 0x8a82, 0x0000 }, + { 0x8a83, 0x0000 }, + { 0x8a84, 0x0000 }, + { 0x8a85, 0x0000 }, + { 0x8a86, 0x0000 }, + { 0x8a87, 0x0000 }, + { 0x8a88, 0x0000 }, + { 0x8a89, 0x0000 }, + { 0x8a8a, 0x0000 }, + { 0x8a8b, 0x0000 }, + { 0x8a8c, 0x0000 }, + { 0x8a8d, 0x0000 }, + { 0x8a8e, 0x0000 }, + { 0x8a8f, 0x0000 }, + { 0x8a90, 0x0000 }, + { 0x8a91, 0x0000 }, + { 0x8a92, 0x0000 }, + { 0x8a93, 0x0000 }, + { 0x8a94, 0x0000 }, + { 0x8a95, 0x0000 }, + { 0x8a96, 0x0000 }, + { 0x8a97, 0x0000 }, + { 0x8a98, 0x0000 }, + { 0x8a99, 0x0000 }, + { 0x8a9a, 0x0000 }, + { 0x8a9b, 0x0000 }, + { 0x8a9c, 0x0000 }, + { 0x8a9d, 0x0000 }, + { 0x8a9e, 0x0000 }, + { 0x8a9f, 0x0000 }, + { 0x8aa0, 0x0000 }, + { 0x8aa1, 0x0000 }, + { 0x8aa2, 0x0000 }, + { 0x8aa3, 0x0000 }, + { 0x8aa4, 0x0000 }, + { 0x8aa5, 0x0000 }, + { 0x8aa6, 0x0000 }, + { 0x8aa7, 0x0000 }, + { 0x8aa8, 0x0000 }, + { 0x8aa9, 0x0000 }, + { 0x8aaa, 0x0000 }, + { 0x8aab, 0x0000 }, + { 0x8aac, 0x0000 }, + { 0x8aad, 0x0000 }, + { 0x8aae, 0x0000 }, + { 0x8aaf, 0x0000 }, + { 0x8ab0, 0x0000 }, + { 0x8ab1, 0x0000 }, + { 0x8ab2, 0x0000 }, + { 0x8ab3, 0x0000 }, + { 0x8ab4, 0x0000 }, + { 0x8ab5, 0x0000 }, + { 0x8ab6, 0x0000 }, + { 0x8ab7, 0x0000 }, + { 0x8ab8, 0x0000 }, + { 0x8ab9, 0x0000 }, + { 0x8aba, 0x0000 }, + { 0x8abb, 0x0000 }, + { 0x8abc, 0x0000 }, + { 0x8abd, 0x0000 }, + { 0x8abe, 0x0000 }, + { 0x8abf, 0x0000 }, + { 0x8ac0, 0x0000 }, + { 0x8ac1, 0x0000 }, + { 0x8ac2, 0x0000 }, + { 0x8ac3, 0x0000 }, + { 0x8ac4, 0x0000 }, + { 0x8ac5, 0x0000 }, + { 0x8ac6, 0x0000 }, + { 0x8ac7, 0x0000 }, + { 0x8ac8, 0x0000 }, + { 0x8ac9, 0x0000 }, + { 0x8aca, 0x0000 }, + { 0x8acb, 0x0000 }, + { 0x8acc, 0x0000 }, + { 0x8acd, 0x0000 }, + { 0x8ace, 0x0000 }, + { 0x8acf, 0x0000 }, + { 0x8ad0, 0x0000 }, + { 0x8ad1, 0x0000 }, + { 0x8ad2, 0x0000 }, + { 0x8ad3, 0x0000 }, + { 0x8ad4, 0x0000 }, + { 0x8ad5, 0x0000 }, + { 0x8ad6, 0x0000 }, + { 0x8ad7, 0x0000 }, + { 0x8ad8, 0x0000 }, + { 0x8ad9, 0x0000 }, + { 0x8ada, 0x0000 }, + { 0x8adb, 0x0000 }, + { 0x8adc, 0x0000 }, + { 0x8add, 0x0000 }, + { 0x8ade, 0x0000 }, + { 0x8adf, 0x0000 }, + { 0x8ae0, 0x0000 }, + { 0x8ae1, 0x0000 }, + { 0x8ae2, 0x0000 }, + { 0x8ae3, 0x0000 }, + { 0x8ae4, 0x0000 }, + { 0x8ae5, 0x0000 }, + { 0x8ae6, 0x0000 }, + { 0x8ae7, 0x0000 }, + { 0x8ae8, 0x0000 }, + { 0x8ae9, 0x0000 }, + { 0x8aea, 0x0000 }, + { 0x8aeb, 0x0000 }, + { 0x8aec, 0x0000 }, + { 0x8aed, 0x0000 }, + { 0x8aee, 0x0000 }, + { 0x8aef, 0x0000 }, + { 0x8af0, 0x0000 }, + { 0x8af1, 0x0000 }, + { 0x8af2, 0x0000 }, + { 0x8af3, 0x0000 }, + { 0x8af4, 0x0000 }, + { 0x8af5, 0x0000 }, + { 0x8af6, 0x0000 }, + { 0x8af7, 0x0000 }, + { 0x8af8, 0x0000 }, + { 0x8af9, 0x0000 }, + { 0x8afa, 0x0000 }, + { 0x8afb, 0x0000 }, + { 0x8afc, 0x0000 }, + { 0x8afd, 0x0000 }, + { 0x8afe, 0x0000 }, + { 0x8aff, 0x0000 }, + { 0x8b00, 0x0000 }, + { 0x8b01, 0x0000 }, + { 0x8b02, 0x0000 }, + { 0x8b03, 0x0000 }, + { 0x8b04, 0x0000 }, + { 0x8b05, 0x0000 }, + { 0x8b06, 0x0000 }, + { 0x8b07, 0x0000 }, + { 0x8b08, 0x0000 }, + { 0x8b09, 0x0000 }, + { 0x8b0a, 0x0000 }, + { 0x8b0b, 0x0000 }, + { 0x8b0c, 0x0000 }, + { 0x8b0d, 0x0000 }, + { 0x8b0e, 0x0000 }, + { 0x8b0f, 0x0000 }, + { 0x8b10, 0x0000 }, + { 0x8b11, 0x0000 }, + { 0x8b12, 0x0000 }, + { 0x8b13, 0x0000 }, + { 0x8b14, 0x0000 }, + { 0x8b15, 0x0000 }, + { 0x8b16, 0x0000 }, + { 0x8b17, 0x0000 }, + { 0x8b18, 0x0000 }, + { 0x8b19, 0x0000 }, + { 0x8b1a, 0x0000 }, + { 0x8b1b, 0x0000 }, + { 0x8b1c, 0x0000 }, + { 0x8b1d, 0x0000 }, + { 0x8b1e, 0x0000 }, + { 0x8b1f, 0x0000 }, + { 0x8b20, 0x0000 }, + { 0x8b21, 0x0000 }, + { 0x8b22, 0x0000 }, + { 0x8b23, 0x0000 }, + { 0x8b24, 0x0000 }, + { 0x8b25, 0x0000 }, + { 0x8b26, 0x0000 }, + { 0x8b27, 0x0000 }, + { 0x8b28, 0x0000 }, + { 0x8b29, 0x0000 }, + { 0x8b2a, 0x0000 }, + { 0x8b2b, 0x0000 }, + { 0x8b2c, 0x0000 }, + { 0x8b2d, 0x0000 }, + { 0x8b2e, 0x0000 }, + { 0x8b2f, 0x0000 }, + { 0x8b30, 0x0000 }, + { 0x8b31, 0x0000 }, + { 0x8b32, 0x0000 }, + { 0x8b33, 0x0000 }, + { 0x8b34, 0x0000 }, + { 0x8b35, 0x0000 }, + { 0x8b36, 0x0000 }, + { 0x8b37, 0x0000 }, + { 0x8b38, 0x0000 }, + { 0x8b39, 0x0000 }, + { 0x8b3a, 0x0000 }, + { 0x8b3b, 0x0000 }, + { 0x8b3c, 0x0000 }, + { 0x8b3d, 0x0000 }, + { 0x8b3e, 0x0000 }, + { 0x8b3f, 0x0000 }, + { 0x8b40, 0x0000 }, + { 0x8b41, 0x0000 }, + { 0x8b42, 0x0000 }, + { 0x8b43, 0x0000 }, + { 0x8b44, 0x0000 }, + { 0x8b45, 0x0000 }, + { 0x8b46, 0x0000 }, + { 0x8b47, 0x0000 }, + { 0x8b48, 0x0000 }, + { 0x8b49, 0x0000 }, + { 0x8b4a, 0x0000 }, + { 0x8b4b, 0x0000 }, + { 0x8b4c, 0x0000 }, + { 0x8b4d, 0x0000 }, + { 0x8b4e, 0x0000 }, + { 0x8b4f, 0x0000 }, + { 0x8b50, 0x0000 }, + { 0x8b51, 0x0000 }, + { 0x8b52, 0x0000 }, + { 0x8b53, 0x0000 }, + { 0x8b54, 0x0000 }, + { 0x8b55, 0x0000 }, + { 0x8b56, 0x0000 }, + { 0x8b57, 0x0000 }, + { 0x8b58, 0x0000 }, + { 0x8b59, 0x0000 }, + { 0x8b5a, 0x0000 }, + { 0x8b5b, 0x0000 }, + { 0x8b5c, 0x0000 }, + { 0x8b5d, 0x0000 }, + { 0x8b5e, 0x0000 }, + { 0x8b5f, 0x0000 }, + { 0x8b60, 0x0000 }, + { 0x8b61, 0x0000 }, + { 0x8b62, 0x0000 }, + { 0x8b63, 0x0000 }, + { 0x8b64, 0x0000 }, + { 0x8b65, 0x0000 }, + { 0x8b66, 0x0000 }, + { 0x8b67, 0x0000 }, + { 0x8b68, 0x0000 }, + { 0x8b69, 0x0000 }, + { 0x8b6a, 0x0000 }, + { 0x8b6b, 0x0000 }, + { 0x8b6c, 0x0000 }, + { 0x8b6d, 0x0000 }, + { 0x8b6e, 0x0000 }, + { 0x8b6f, 0x0000 }, + { 0x8b70, 0x0000 }, + { 0x8b71, 0x0000 }, + { 0x8b72, 0x0000 }, + { 0x8b73, 0x0000 }, + { 0x8b74, 0x0000 }, + { 0x8b75, 0x0000 }, + { 0x8b76, 0x0000 }, + { 0x8b77, 0x0000 }, + { 0x8b78, 0x0000 }, + { 0x8b79, 0x0000 }, + { 0x8b7a, 0x0000 }, + { 0x8b7b, 0x0000 }, + { 0x8b7c, 0x0000 }, + { 0x8b7d, 0x0000 }, + { 0x8b7e, 0x0000 }, + { 0x8b7f, 0x0000 }, + { 0x8b80, 0x0000 }, + { 0x8b81, 0x0000 }, + { 0x8b82, 0x0000 }, + { 0x8b83, 0x0000 }, + { 0x8b84, 0x0000 }, + { 0x8b85, 0x0000 }, + { 0x8b86, 0x0000 }, + { 0x8b87, 0x0000 }, + { 0x8b88, 0x0000 }, + { 0x8b89, 0x0000 }, + { 0x8b8a, 0x0000 }, + { 0x8b8b, 0x0000 }, + { 0x8b8c, 0x0000 }, + { 0x8b8d, 0x0000 }, + { 0x8b8e, 0x0000 }, + { 0x8b8f, 0x0000 }, + { 0x8b90, 0x0000 }, + { 0x8b91, 0x0000 }, + { 0x8b92, 0x0000 }, + { 0x8b93, 0x0000 }, + { 0x8b94, 0x0000 }, + { 0x8b95, 0x0000 }, + { 0x8b96, 0x0000 }, + { 0x8b97, 0x0000 }, + { 0x8b98, 0x0000 }, + { 0x8b99, 0x0000 }, + { 0x8b9a, 0x0000 }, + { 0x8b9b, 0x0000 }, + { 0x8b9c, 0x0000 }, + { 0x8b9d, 0x0000 }, + { 0x8b9e, 0x0000 }, + { 0x8b9f, 0x0000 }, + { 0x8ba0, 0x0000 }, + { 0x8ba1, 0x0000 }, + { 0x8ba2, 0x0000 }, + { 0x8ba3, 0x0000 }, + { 0x8ba4, 0x0000 }, + { 0x8ba5, 0x0000 }, + { 0x8ba6, 0x0000 }, + { 0x8ba7, 0x0000 }, + { 0x8ba8, 0x0000 }, + { 0x8ba9, 0x0000 }, + { 0x8baa, 0x0000 }, + { 0x8bab, 0x0000 }, + { 0x8bac, 0x0000 }, + { 0x8bad, 0x0000 }, + { 0x8bae, 0x0000 }, + { 0x8baf, 0x0000 }, + { 0x8bb0, 0x0000 }, + { 0x8bb1, 0x0000 }, + { 0x8bb2, 0x0000 }, + { 0x8bb3, 0x0000 }, + { 0x8bb4, 0x0000 }, + { 0x8bb5, 0x0000 }, + { 0x8bb6, 0x0000 }, + { 0x8bb7, 0x0000 }, + { 0x8bb8, 0x0000 }, + { 0x8bb9, 0x0000 }, + { 0x8bba, 0x0000 }, + { 0x8bbb, 0x0000 }, + { 0x8bbc, 0x0000 }, + { 0x8bbd, 0x0000 }, + { 0x8bbe, 0x0000 }, + { 0x8bbf, 0x0000 }, + { 0x8bc0, 0x0000 }, + { 0x8bc1, 0x0000 }, + { 0x8bc2, 0x0000 }, + { 0x8bc3, 0x0000 }, + { 0x8bc4, 0x0000 }, + { 0x8bc5, 0x0000 }, + { 0x8bc6, 0x0000 }, + { 0x8bc7, 0x0000 }, + { 0x8bc8, 0x0000 }, + { 0x8bc9, 0x0000 }, + { 0x8bca, 0x0000 }, + { 0x8bcb, 0x0000 }, + { 0x8bcc, 0x0000 }, + { 0x8bcd, 0x0000 }, + { 0x8bce, 0x0000 }, + { 0x8bcf, 0x0000 }, + { 0x8bd0, 0x0000 }, + { 0x8bd1, 0x0000 }, + { 0x8bd2, 0x0000 }, + { 0x8bd3, 0x0000 }, + { 0x8bd4, 0x0000 }, + { 0x8bd5, 0x0000 }, + { 0x8bd6, 0x0000 }, + { 0x8bd7, 0x0000 }, + { 0x8bd8, 0x0000 }, + { 0x8bd9, 0x0000 }, + { 0x8bda, 0x0000 }, + { 0x8bdb, 0x0000 }, + { 0x8bdc, 0x0000 }, + { 0x8bdd, 0x0000 }, + { 0x8bde, 0x0000 }, + { 0x8bdf, 0x0000 }, + { 0x8be0, 0x0000 }, + { 0x8be1, 0x0000 }, + { 0x8be2, 0x0000 }, + { 0x8be3, 0x0000 }, + { 0x8be4, 0x0000 }, + { 0x8be5, 0x0000 }, + { 0x8be6, 0x0000 }, + { 0x8be7, 0x0000 }, + { 0x8be8, 0x0000 }, + { 0x8be9, 0x0000 }, + { 0x8bea, 0x0000 }, + { 0x8beb, 0x0000 }, + { 0x8bec, 0x0000 }, + { 0x8bed, 0x0000 }, + { 0x8bee, 0x0000 }, + { 0x8bef, 0x0000 }, + { 0x8bf0, 0x0000 }, + { 0x8bf1, 0x0000 }, + { 0x8bf2, 0x0000 }, + { 0x8bf3, 0x0000 }, + { 0x8bf4, 0x0000 }, + { 0x8bf5, 0x0000 }, + { 0x8bf6, 0x0000 }, + { 0x8bf7, 0x0000 }, + { 0x8bf8, 0x0000 }, + { 0x8bf9, 0x0000 }, + { 0x8bfa, 0x0000 }, + { 0x8bfb, 0x0000 }, + { 0x8bfc, 0x0000 }, + { 0x8bfd, 0x0000 }, + { 0x8bfe, 0x0000 }, + { 0x8bff, 0x0000 }, + { 0x8c00, 0x0000 }, + { 0x8c01, 0x0000 }, + { 0x8c02, 0x0000 }, + { 0x8c03, 0x0000 }, + { 0x8c04, 0x0000 }, + { 0x8c05, 0x0000 }, + { 0x8c06, 0x0000 }, + { 0x8c07, 0x0000 }, + { 0x8c08, 0x0000 }, + { 0x8c09, 0x0000 }, + { 0x8c0a, 0x0000 }, + { 0x8c0b, 0x0000 }, + { 0x8c0c, 0x0000 }, + { 0x8c0d, 0x0000 }, + { 0x8c0e, 0x0000 }, + { 0x8c0f, 0x0000 }, + { 0x8c10, 0x0000 }, + { 0x8c11, 0x0000 }, + { 0x8c12, 0x0000 }, + { 0x8c13, 0x0000 }, + { 0x8c14, 0x0000 }, + { 0x8c15, 0x0000 }, + { 0x8c16, 0x0000 }, + { 0x8c17, 0x0000 }, + { 0x8c18, 0x0000 }, + { 0x8c19, 0x0000 }, + { 0x8c1a, 0x0000 }, + { 0x8c1b, 0x0000 }, + { 0x8c1c, 0x0000 }, + { 0x8c1d, 0x0000 }, + { 0x8c1e, 0x0000 }, + { 0x8c1f, 0x0000 }, + { 0x8c20, 0x0000 }, + { 0x8c21, 0x0000 }, + { 0x8c22, 0x0000 }, + { 0x8c23, 0x0000 }, + { 0x8c24, 0x0000 }, + { 0x8c25, 0x0000 }, + { 0x8c26, 0x0000 }, + { 0x8c27, 0x0000 }, + { 0x8c28, 0x0000 }, + { 0x8c29, 0x0000 }, + { 0x8c2a, 0x0000 }, + { 0x8c2b, 0x0000 }, + { 0x8c2c, 0x0000 }, + { 0x8c2d, 0x0000 }, + { 0x8c2e, 0x0000 }, + { 0x8c2f, 0x0000 }, + { 0x8c30, 0x0000 }, + { 0x8c31, 0x0000 }, + { 0x8c32, 0x0000 }, + { 0x8c33, 0x0000 }, + { 0x8c34, 0x0000 }, + { 0x8c35, 0x0000 }, + { 0x8c36, 0x0000 }, + { 0x8c37, 0x0000 }, + { 0x8c38, 0x0000 }, + { 0x8c39, 0x0000 }, + { 0x8c3a, 0x0000 }, + { 0x8c3b, 0x0000 }, + { 0x8c3c, 0x0000 }, + { 0x8c3d, 0x0000 }, + { 0x8c3e, 0x0000 }, + { 0x8c3f, 0x0000 }, + { 0x8c40, 0x0000 }, + { 0x8c41, 0x0000 }, + { 0x8c42, 0x0000 }, + { 0x8c43, 0x0000 }, + { 0x8c44, 0x0000 }, + { 0x8c45, 0x0000 }, + { 0x8c46, 0x0000 }, + { 0x8c47, 0x0000 }, + { 0x8c48, 0x0000 }, + { 0x8c49, 0x0000 }, + { 0x8c4a, 0x0000 }, + { 0x8c4b, 0x0000 }, + { 0x8c4c, 0x0000 }, + { 0x8c4d, 0x0000 }, + { 0x8c4e, 0x0000 }, + { 0x8c4f, 0x0000 }, + { 0x8c50, 0x0000 }, + { 0x8c51, 0x0000 }, + { 0x8c52, 0x0000 }, + { 0x8c53, 0x0000 }, + { 0x8c54, 0x0000 }, + { 0x8c55, 0x0000 }, + { 0x8c56, 0x0000 }, + { 0x8c57, 0x0000 }, + { 0x8c58, 0x0000 }, + { 0x8c59, 0x0000 }, + { 0x8c5a, 0x0000 }, + { 0x8c5b, 0x0000 }, + { 0x8c5c, 0x0000 }, + { 0x8c5d, 0x0000 }, + { 0x8c5e, 0x0000 }, + { 0x8c5f, 0x0000 }, + { 0x8c60, 0x0000 }, + { 0x8c61, 0x0000 }, + { 0x8c62, 0x0000 }, + { 0x8c63, 0x0000 }, + { 0x8c64, 0x0000 }, + { 0x8c65, 0x0000 }, + { 0x8c66, 0x0000 }, + { 0x8c67, 0x0000 }, + { 0x8c68, 0x0000 }, + { 0x8c69, 0x0000 }, + { 0x8c6a, 0x0000 }, + { 0x8c6b, 0x0000 }, + { 0x8c6c, 0x0000 }, + { 0x8c6d, 0x0000 }, + { 0x8c6e, 0x0000 }, + { 0x8c6f, 0x0000 }, + { 0x8c70, 0x0000 }, + { 0x8c71, 0x0000 }, + { 0x8c72, 0x0000 }, + { 0x8c73, 0x0000 }, + { 0x8c74, 0x0000 }, + { 0x8c75, 0x0000 }, + { 0x8c76, 0x0000 }, + { 0x8c77, 0x0000 }, + { 0x8c78, 0x0000 }, + { 0x8c79, 0x0000 }, + { 0x8c7a, 0x0000 }, + { 0x8c7b, 0x0000 }, + { 0x8c7c, 0x0000 }, + { 0x8c7d, 0x0000 }, + { 0x8c7e, 0x0000 }, + { 0x8c7f, 0x0000 }, + { 0x8c80, 0x0000 }, + { 0x8c81, 0x0000 }, + { 0x8c82, 0x0000 }, + { 0x8c83, 0x0000 }, + { 0x8c84, 0x0000 }, + { 0x8c85, 0x0000 }, + { 0x8c86, 0x0000 }, + { 0x8c87, 0x0000 }, + { 0x8c88, 0x0000 }, + { 0x8c89, 0x0000 }, + { 0x8c8a, 0x0000 }, + { 0x8c8b, 0x0000 }, + { 0x8c8c, 0x0000 }, + { 0x8c8d, 0x0000 }, + { 0x8c8e, 0x0000 }, + { 0x8c8f, 0x0000 }, + { 0x8c90, 0x0000 }, + { 0x8c91, 0x0000 }, + { 0x8c92, 0x0000 }, + { 0x8c93, 0x0000 }, + { 0x8c94, 0x0000 }, + { 0x8c95, 0x0000 }, + { 0x8c96, 0x0000 }, + { 0x8c97, 0x0000 }, + { 0x8c98, 0x0000 }, + { 0x8c99, 0x0000 }, + { 0x8c9a, 0x0000 }, + { 0x8c9b, 0x0000 }, + { 0x8c9c, 0x0000 }, + { 0x8c9d, 0x0000 }, + { 0x8c9e, 0x0000 }, + { 0x8c9f, 0x0000 }, + { 0x8ca0, 0x0000 }, + { 0x8ca1, 0x0000 }, + { 0x8ca2, 0x0000 }, + { 0x8ca3, 0x0000 }, + { 0x8ca4, 0x0000 }, + { 0x8ca5, 0x0000 }, + { 0x8ca6, 0x0000 }, + { 0x8ca7, 0x0000 }, + { 0x8ca8, 0x0000 }, + { 0x8ca9, 0x0000 }, + { 0x8caa, 0x0000 }, + { 0x8cab, 0x0000 }, + { 0x8cac, 0x0000 }, + { 0x8cad, 0x0000 }, + { 0x8cae, 0x0000 }, + { 0x8caf, 0x0000 }, + { 0x8cb0, 0x0000 }, + { 0x8cb1, 0x0000 }, + { 0x8cb2, 0x0000 }, + { 0x8cb3, 0x0000 }, + { 0x8cb4, 0x0000 }, + { 0x8cb5, 0x0000 }, + { 0x8cb6, 0x0000 }, + { 0x8cb7, 0x0000 }, + { 0x8cb8, 0x0000 }, + { 0x8cb9, 0x0000 }, + { 0x8cba, 0x0000 }, + { 0x8cbb, 0x0000 }, + { 0x8cbc, 0x0000 }, + { 0x8cbd, 0x0000 }, + { 0x8cbe, 0x0000 }, + { 0x8cbf, 0x0000 }, + { 0x8cc0, 0x0000 }, + { 0x8cc1, 0x0000 }, + { 0x8cc2, 0x0000 }, + { 0x8cc3, 0x0000 }, + { 0x8cc4, 0x0000 }, + { 0x8cc5, 0x0000 }, + { 0x8cc6, 0x0000 }, + { 0x8cc7, 0x0000 }, + { 0x8cc8, 0x0000 }, + { 0x8cc9, 0x0000 }, + { 0x8cca, 0x0000 }, + { 0x8ccb, 0x0000 }, + { 0x8ccc, 0x0000 }, + { 0x8ccd, 0x0000 }, + { 0x8cce, 0x0000 }, + { 0x8ccf, 0x0000 }, + { 0x8cd0, 0x0000 }, + { 0x8cd1, 0x0000 }, + { 0x8cd2, 0x0000 }, + { 0x8cd3, 0x0000 }, + { 0x8cd4, 0x0000 }, + { 0x8cd5, 0x0000 }, + { 0x8cd6, 0x0000 }, + { 0x8cd7, 0x0000 }, + { 0x8cd8, 0x0000 }, + { 0x8cd9, 0x0000 }, + { 0x8cda, 0x0000 }, + { 0x8cdb, 0x0000 }, + { 0x8cdc, 0x0000 }, + { 0x8cdd, 0x0000 }, + { 0x8cde, 0x0000 }, + { 0x8cdf, 0x0000 }, + { 0x8ce0, 0x0000 }, + { 0x8ce1, 0x0000 }, + { 0x8ce2, 0x0000 }, + { 0x8ce3, 0x0000 }, + { 0x8ce4, 0x0000 }, + { 0x8ce5, 0x0000 }, + { 0x8ce6, 0x0000 }, + { 0x8ce7, 0x0000 }, + { 0x8ce8, 0x0000 }, + { 0x8ce9, 0x0000 }, + { 0x8cea, 0x0000 }, + { 0x8ceb, 0x0000 }, + { 0x8cec, 0x0000 }, + { 0x8ced, 0x0000 }, + { 0x8cee, 0x0000 }, + { 0x8cef, 0x0000 }, + { 0x8cf0, 0x0000 }, + { 0x8cf1, 0x0000 }, + { 0x8cf2, 0x0000 }, + { 0x8cf3, 0x0000 }, + { 0x8cf4, 0x0000 }, + { 0x8cf5, 0x0000 }, + { 0x8cf6, 0x0000 }, + { 0x8cf7, 0x0000 }, + { 0x8cf8, 0x0000 }, + { 0x8cf9, 0x0000 }, + { 0x8cfa, 0x0000 }, + { 0x8cfb, 0x0000 }, + { 0x8cfc, 0x0000 }, + { 0x8cfd, 0x0000 }, + { 0x8cfe, 0x0000 }, + { 0x8cff, 0x0000 }, + { 0x8d00, 0x0000 }, + { 0x8d01, 0x0000 }, + { 0x8d02, 0x0000 }, + { 0x8d03, 0x0000 }, + { 0x8d04, 0x0000 }, + { 0x8d05, 0x0000 }, + { 0x8d06, 0x0000 }, + { 0x8d07, 0x0000 }, + { 0x8d08, 0x0000 }, + { 0x8d09, 0x0000 }, + { 0x8d0a, 0x0000 }, + { 0x8d0b, 0x0000 }, + { 0x8d0c, 0x0000 }, + { 0x8d0d, 0x0000 }, + { 0x8d0e, 0x0000 }, + { 0x8d0f, 0x0000 }, + { 0x8d10, 0x0000 }, + { 0x8d11, 0x0000 }, + { 0x8d12, 0x0000 }, + { 0x8d13, 0x0000 }, + { 0x8d14, 0x0000 }, + { 0x8d15, 0x0000 }, + { 0x8d16, 0x0000 }, + { 0x8d17, 0x0000 }, + { 0x8d18, 0x0000 }, + { 0x8d19, 0x0000 }, + { 0x8d1a, 0x0000 }, + { 0x8d1b, 0x0000 }, + { 0x8d1c, 0x0000 }, + { 0x8d1d, 0x0000 }, + { 0x8d1e, 0x0000 }, + { 0x8d1f, 0x0000 }, + { 0x8d20, 0x0000 }, + { 0x8d21, 0x0000 }, + { 0x8d22, 0x0000 }, + { 0x8d23, 0x0000 }, + { 0x8d24, 0x0000 }, + { 0x8d25, 0x0000 }, + { 0x8d26, 0x0000 }, + { 0x8d27, 0x0000 }, + { 0x8d28, 0x0000 }, + { 0x8d29, 0x0000 }, + { 0x8d2a, 0x0000 }, + { 0x8d2b, 0x0000 }, + { 0x8d2c, 0x0000 }, + { 0x8d2d, 0x0000 }, + { 0x8d2e, 0x0000 }, + { 0x8d2f, 0x0000 }, + { 0x8d30, 0x0000 }, + { 0x8d31, 0x0000 }, + { 0x8d32, 0x0000 }, + { 0x8d33, 0x0000 }, + { 0x8d34, 0x0000 }, + { 0x8d35, 0x0000 }, + { 0x8d36, 0x0000 }, + { 0x8d37, 0x0000 }, + { 0x8d38, 0x0000 }, + { 0x8d39, 0x0000 }, + { 0x8d3a, 0x0000 }, + { 0x8d3b, 0x0000 }, + { 0x8d3c, 0x0000 }, + { 0x8d3d, 0x0000 }, + { 0x8d3e, 0x0000 }, + { 0x8d3f, 0x0000 }, + { 0x8d40, 0x0000 }, + { 0x8d41, 0x0000 }, + { 0x8d42, 0x0000 }, + { 0x8d43, 0x0000 }, + { 0x8d44, 0x0000 }, + { 0x8d45, 0x0000 }, + { 0x8d46, 0x0000 }, + { 0x8d47, 0x0000 }, + { 0x8d48, 0x0000 }, + { 0x8d49, 0x0000 }, + { 0x8d4a, 0x0000 }, + { 0x8d4b, 0x0000 }, + { 0x8d4c, 0x0000 }, + { 0x8d4d, 0x0000 }, + { 0x8d4e, 0x0000 }, + { 0x8d4f, 0x0000 }, + { 0x8d50, 0x0000 }, + { 0x8d51, 0x0000 }, + { 0x8d52, 0x0000 }, + { 0x8d53, 0x0000 }, + { 0x8d54, 0x0000 }, + { 0x8d55, 0x0000 }, + { 0x8d56, 0x0000 }, + { 0x8d57, 0x0000 }, + { 0x8d58, 0x0000 }, + { 0x8d59, 0x0000 }, + { 0x8d5a, 0x0000 }, + { 0x8d5b, 0x0000 }, + { 0x8d5c, 0x0000 }, + { 0x8d5d, 0x0000 }, + { 0x8d5e, 0x0000 }, + { 0x8d5f, 0x0000 }, + { 0x8d60, 0x0000 }, + { 0x8d61, 0x0000 }, + { 0x8d62, 0x0000 }, + { 0x8d63, 0x0000 }, + { 0x8d64, 0x0000 }, + { 0x8d65, 0x0000 }, + { 0x8d66, 0x0000 }, + { 0x8d67, 0x0000 }, + { 0x8d68, 0x0000 }, + { 0x8d69, 0x0000 }, + { 0x8d6a, 0x0000 }, + { 0x8d6b, 0x0000 }, + { 0x8d6c, 0x0000 }, + { 0x8d6d, 0x0000 }, + { 0x8d6e, 0x0000 }, + { 0x8d6f, 0x0000 }, + { 0x8d70, 0x0000 }, + { 0x8d71, 0x0000 }, + { 0x8d72, 0x0000 }, + { 0x8d73, 0x0000 }, + { 0x8d74, 0x0000 }, + { 0x8d75, 0x0000 }, + { 0x8d76, 0x0000 }, + { 0x8d77, 0x0000 }, + { 0x8d78, 0x0000 }, + { 0x8d79, 0x0000 }, + { 0x8d7a, 0x0000 }, + { 0x8d7b, 0x0000 }, + { 0x8d7c, 0x0000 }, + { 0x8d7d, 0x0000 }, + { 0x8d7e, 0x0000 }, + { 0x8d7f, 0x0000 }, + { 0x8d80, 0x0000 }, + { 0x8d81, 0x0000 }, + { 0x8d82, 0x0000 }, + { 0x8d83, 0x0000 }, + { 0x8d84, 0x0000 }, + { 0x8d85, 0x0000 }, + { 0x8d86, 0x0000 }, + { 0x8d87, 0x0000 }, + { 0x8d88, 0x0000 }, + { 0x8d89, 0x0000 }, + { 0x8d8a, 0x0000 }, + { 0x8d8b, 0x0000 }, + { 0x8d8c, 0x0000 }, + { 0x8d8d, 0x0000 }, + { 0x8d8e, 0x0000 }, + { 0x8d8f, 0x0000 }, + { 0x8d90, 0x0000 }, + { 0x8d91, 0x0000 }, + { 0x8d92, 0x0000 }, + { 0x8d93, 0x0000 }, + { 0x8d94, 0x0000 }, + { 0x8d95, 0x0000 }, + { 0x8d96, 0x0000 }, + { 0x8d97, 0x0000 }, + { 0x8d98, 0x0000 }, + { 0x8d99, 0x0000 }, + { 0x8d9a, 0x0000 }, + { 0x8d9b, 0x0000 }, + { 0x8d9c, 0x0000 }, + { 0x8d9d, 0x0000 }, + { 0x8d9e, 0x0000 }, + { 0x8d9f, 0x0000 }, + { 0x8da0, 0x0000 }, + { 0x8da1, 0x0000 }, + { 0x8da2, 0x0000 }, + { 0x8da3, 0x0000 }, + { 0x8da4, 0x0000 }, + { 0x8da5, 0x0000 }, + { 0x8da6, 0x0000 }, + { 0x8da7, 0x0000 }, + { 0x8da8, 0x0000 }, + { 0x8da9, 0x0000 }, + { 0x8daa, 0x0000 }, + { 0x8dab, 0x0000 }, + { 0x8dac, 0x0000 }, + { 0x8dad, 0x0000 }, + { 0x8dae, 0x0000 }, + { 0x8daf, 0x0000 }, + { 0x8db0, 0x0000 }, + { 0x8db1, 0x0000 }, + { 0x8db2, 0x0000 }, + { 0x8db3, 0x0000 }, + { 0x8db4, 0x0000 }, + { 0x8db5, 0x0000 }, + { 0x8db6, 0x0000 }, + { 0x8db7, 0x0000 }, + { 0x8db8, 0x0000 }, + { 0x8db9, 0x0000 }, + { 0x8dba, 0x0000 }, + { 0x8dbb, 0x0000 }, + { 0x8dbc, 0x0000 }, + { 0x8dbd, 0x0000 }, + { 0x8dbe, 0x0000 }, + { 0x8dbf, 0x0000 }, + { 0x8dc0, 0x0000 }, + { 0x8dc1, 0x0000 }, + { 0x8dc2, 0x0000 }, + { 0x8dc3, 0x0000 }, + { 0x8dc4, 0x0000 }, + { 0x8dc5, 0x0000 }, + { 0x8dc6, 0x0000 }, + { 0x8dc7, 0x0000 }, + { 0x8dc8, 0x0000 }, + { 0x8dc9, 0x0000 }, + { 0x8dca, 0x0000 }, + { 0x8dcb, 0x0000 }, + { 0x8dcc, 0x0000 }, + { 0x8dcd, 0x0000 }, + { 0x8dce, 0x0000 }, + { 0x8dcf, 0x0000 }, + { 0x8dd0, 0x0000 }, + { 0x8dd1, 0x0000 }, + { 0x8dd2, 0x0000 }, + { 0x8dd3, 0x0000 }, + { 0x8dd4, 0x0000 }, + { 0x8dd5, 0x0000 }, + { 0x8dd6, 0x0000 }, + { 0x8dd7, 0x0000 }, + { 0x8dd8, 0x0000 }, + { 0x8dd9, 0x0000 }, + { 0x8dda, 0x0000 }, + { 0x8ddb, 0x0000 }, + { 0x8ddc, 0x0000 }, + { 0x8ddd, 0x0000 }, + { 0x8dde, 0x0000 }, + { 0x8ddf, 0x0000 }, + { 0x8de0, 0x0000 }, + { 0x8de1, 0x0000 }, + { 0x8de2, 0x0000 }, + { 0x8de3, 0x0000 }, + { 0x8de4, 0x0000 }, + { 0x8de5, 0x0000 }, + { 0x8de6, 0x0000 }, + { 0x8de7, 0x0000 }, + { 0x8de8, 0x0000 }, + { 0x8de9, 0x0000 }, + { 0x8dea, 0x0000 }, + { 0x8deb, 0x0000 }, + { 0x8dec, 0x0000 }, + { 0x8ded, 0x0000 }, + { 0x8dee, 0x0000 }, + { 0x8def, 0x0000 }, + { 0x8df0, 0x0000 }, + { 0x8df1, 0x0000 }, + { 0x8df2, 0x0000 }, + { 0x8df3, 0x0000 }, + { 0x8df4, 0x0000 }, + { 0x8df5, 0x0000 }, + { 0x8df6, 0x0000 }, + { 0x8df7, 0x0000 }, + { 0x8df8, 0x0000 }, + { 0x8df9, 0x0000 }, + { 0x8dfa, 0x0000 }, + { 0x8dfb, 0x0000 }, + { 0x8dfc, 0x0000 }, + { 0x8dfd, 0x0000 }, + { 0x8dfe, 0x0000 }, + { 0x8dff, 0x0000 }, + { 0x8e00, 0x0000 }, + { 0x8e01, 0x0000 }, + { 0x8e02, 0x0000 }, + { 0x8e03, 0x0000 }, + { 0x8e04, 0x0000 }, + { 0x8e05, 0x0000 }, + { 0x8e06, 0x0000 }, + { 0x8e07, 0x0000 }, + { 0x8e08, 0x0000 }, + { 0x8e09, 0x0000 }, + { 0x8e0a, 0x0000 }, + { 0x8e0b, 0x0000 }, + { 0x8e0c, 0x0000 }, + { 0x8e0d, 0x0000 }, + { 0x8e0e, 0x0000 }, + { 0x8e0f, 0x0000 }, + { 0x8e10, 0x0000 }, + { 0x8e11, 0x0000 }, + { 0x8e12, 0x0000 }, + { 0x8e13, 0x0000 }, + { 0x8e14, 0x0000 }, + { 0x8e15, 0x0000 }, + { 0x8e16, 0x0000 }, + { 0x8e17, 0x0000 }, + { 0x8e18, 0x0000 }, + { 0x8e19, 0x0000 }, + { 0x8e1a, 0x0000 }, + { 0x8e1b, 0x0000 }, + { 0x8e1c, 0x0000 }, + { 0x8e1d, 0x0000 }, + { 0x8e1e, 0x0000 }, + { 0x8e1f, 0x0000 }, + { 0x8e20, 0x0000 }, + { 0x8e21, 0x0000 }, + { 0x8e22, 0x0000 }, + { 0x8e23, 0x0000 }, + { 0x8e24, 0x0000 }, + { 0x8e25, 0x0000 }, + { 0x8e26, 0x0000 }, + { 0x8e27, 0x0000 }, + { 0x8e28, 0x0000 }, + { 0x8e29, 0x0000 }, + { 0x8e2a, 0x0000 }, + { 0x8e2b, 0x0000 }, + { 0x8e2c, 0x0000 }, + { 0x8e2d, 0x0000 }, + { 0x8e2e, 0x0000 }, + { 0x8e2f, 0x0000 }, + { 0x8e30, 0x0000 }, + { 0x8e31, 0x0000 }, + { 0x8e32, 0x0000 }, + { 0x8e33, 0x0000 }, + { 0x8e34, 0x0000 }, + { 0x8e35, 0x0000 }, + { 0x8e36, 0x0000 }, + { 0x8e37, 0x0000 }, + { 0x8e38, 0x0000 }, + { 0x8e39, 0x0000 }, + { 0x8e3a, 0x0000 }, + { 0x8e3b, 0x0000 }, + { 0x8e3c, 0x0000 }, + { 0x8e3d, 0x0000 }, + { 0x8e3e, 0x0000 }, + { 0x8e3f, 0x0000 }, + { 0x8e40, 0x0000 }, + { 0x8e41, 0x0000 }, + { 0x8e42, 0x0000 }, + { 0x8e43, 0x0000 }, + { 0x8e44, 0x0000 }, + { 0x8e45, 0x0000 }, + { 0x8e46, 0x0000 }, + { 0x8e47, 0x0000 }, + { 0x8e48, 0x0000 }, + { 0x8e49, 0x0000 }, + { 0x8e4a, 0x0000 }, + { 0x8e4b, 0x0000 }, + { 0x8e4c, 0x0000 }, + { 0x8e4d, 0x0000 }, + { 0x8e4e, 0x0000 }, + { 0x8e4f, 0x0000 }, + { 0x8e50, 0x0000 }, + { 0x8e51, 0x0000 }, + { 0x8e52, 0x0000 }, + { 0x8e53, 0x0000 }, + { 0x8e54, 0x0000 }, + { 0x8e55, 0x0000 }, + { 0x8e56, 0x0000 }, + { 0x8e57, 0x0000 }, + { 0x8e58, 0x0000 }, + { 0x8e59, 0x0000 }, + { 0x8e5a, 0x0000 }, + { 0x8e5b, 0x0000 }, + { 0x8e5c, 0x0000 }, + { 0x8e5d, 0x0000 }, + { 0x8e5e, 0x0000 }, + { 0x8e5f, 0x0000 }, + { 0x8e60, 0x0000 }, + { 0x8e61, 0x0000 }, + { 0x8e62, 0x0000 }, + { 0x8e63, 0x0000 }, + { 0x8e64, 0x0000 }, + { 0x8e65, 0x0000 }, + { 0x8e66, 0x0000 }, + { 0x8e67, 0x0000 }, + { 0x8e68, 0x0000 }, + { 0x8e69, 0x0000 }, + { 0x8e6a, 0x0000 }, + { 0x8e6b, 0x0000 }, + { 0x8e6c, 0x0000 }, + { 0x8e6d, 0x0000 }, + { 0x8e6e, 0x0000 }, + { 0x8e6f, 0x0000 }, + { 0x8e70, 0x0000 }, + { 0x8e71, 0x0000 }, + { 0x8e72, 0x0000 }, + { 0x8e73, 0x0000 }, + { 0x8e74, 0x0000 }, + { 0x8e75, 0x0000 }, + { 0x8e76, 0x0000 }, + { 0x8e77, 0x0000 }, + { 0x8e78, 0x0000 }, + { 0x8e79, 0x0000 }, + { 0x8e7a, 0x0000 }, + { 0x8e7b, 0x0000 }, + { 0x8e7c, 0x0000 }, + { 0x8e7d, 0x0000 }, + { 0x8e7e, 0x0000 }, + { 0x8e7f, 0x0000 }, + { 0x8e80, 0x0000 }, + { 0x8e81, 0x0000 }, + { 0x8e82, 0x0000 }, + { 0x8e83, 0x0000 }, + { 0x8e84, 0x0000 }, + { 0x8e85, 0x0000 }, + { 0x8e86, 0x0000 }, + { 0x8e87, 0x0000 }, + { 0x8e88, 0x0000 }, + { 0x8e89, 0x0000 }, + { 0x8e8a, 0x0000 }, + { 0x8e8b, 0x0000 }, + { 0x8e8c, 0x0000 }, + { 0x8e8d, 0x0000 }, + { 0x8e8e, 0x0000 }, + { 0x8e8f, 0x0000 }, + { 0x8e90, 0x0000 }, + { 0x8e91, 0x0000 }, + { 0x8e92, 0x0000 }, + { 0x8e93, 0x0000 }, + { 0x8e94, 0x0000 }, + { 0x8e95, 0x0000 }, + { 0x8e96, 0x0000 }, + { 0x8e97, 0x0000 }, + { 0x8e98, 0x0000 }, + { 0x8e99, 0x0000 }, + { 0x8e9a, 0x0000 }, + { 0x8e9b, 0x0000 }, + { 0x8e9c, 0x0000 }, + { 0x8e9d, 0x0000 }, + { 0x8e9e, 0x0000 }, + { 0x8e9f, 0x0000 }, + { 0x8ea0, 0x0000 }, + { 0x8ea1, 0x0000 }, + { 0x8ea2, 0x0000 }, + { 0x8ea3, 0x0000 }, + { 0x8ea4, 0x0000 }, + { 0x8ea5, 0x0000 }, + { 0x8ea6, 0x0000 }, + { 0x8ea7, 0x0000 }, + { 0x8ea8, 0x0000 }, + { 0x8ea9, 0x0000 }, + { 0x8eaa, 0x0000 }, + { 0x8eab, 0x0000 }, + { 0x8eac, 0x0000 }, + { 0x8ead, 0x0000 }, + { 0x8eae, 0x0000 }, + { 0x8eaf, 0x0000 }, + { 0x8eb0, 0x0000 }, + { 0x8eb1, 0x0000 }, + { 0x8eb2, 0x0000 }, + { 0x8eb3, 0x0000 }, + { 0x8eb4, 0x0000 }, + { 0x8eb5, 0x0000 }, + { 0x8eb6, 0x0000 }, + { 0x8eb7, 0x0000 }, + { 0x8eb8, 0x0000 }, + { 0x8eb9, 0x0000 }, + { 0x8eba, 0x0000 }, + { 0x8ebb, 0x0000 }, + { 0x8ebc, 0x0000 }, + { 0x8ebd, 0x0000 }, + { 0x8ebe, 0x0000 }, + { 0x8ebf, 0x0000 }, + { 0x8ec0, 0x0000 }, + { 0x8ec1, 0x0000 }, + { 0x8ec2, 0x0000 }, + { 0x8ec3, 0x0000 }, + { 0x8ec4, 0x0000 }, + { 0x8ec5, 0x0000 }, + { 0x8ec6, 0x0000 }, + { 0x8ec7, 0x0000 }, + { 0x8ec8, 0x0000 }, + { 0x8ec9, 0x0000 }, + { 0x8eca, 0x0000 }, + { 0x8ecb, 0x0000 }, + { 0x8ecc, 0x0000 }, + { 0x8ecd, 0x0000 }, + { 0x8ece, 0x0000 }, + { 0x8ecf, 0x0000 }, + { 0x8ed0, 0x0000 }, + { 0x8ed1, 0x0000 }, + { 0x8ed2, 0x0000 }, + { 0x8ed3, 0x0000 }, + { 0x8ed4, 0x0000 }, + { 0x8ed5, 0x0000 }, + { 0x8ed6, 0x0000 }, + { 0x8ed7, 0x0000 }, + { 0x8ed8, 0x0000 }, + { 0x8ed9, 0x0000 }, + { 0x8eda, 0x0000 }, + { 0x8edb, 0x0000 }, + { 0x8edc, 0x0000 }, + { 0x8edd, 0x0000 }, + { 0x8ede, 0x0000 }, + { 0x8edf, 0x0000 }, + { 0x8ee0, 0x0000 }, + { 0x8ee1, 0x0000 }, + { 0x8ee2, 0x0000 }, + { 0x8ee3, 0x0000 }, + { 0x8ee4, 0x0000 }, + { 0x8ee5, 0x0000 }, + { 0x8ee6, 0x0000 }, + { 0x8ee7, 0x0000 }, + { 0x8ee8, 0x0000 }, + { 0x8ee9, 0x0000 }, + { 0x8eea, 0x0000 }, + { 0x8eeb, 0x0000 }, + { 0x8eec, 0x0000 }, + { 0x8eed, 0x0000 }, + { 0x8eee, 0x0000 }, + { 0x8eef, 0x0000 }, + { 0x8ef0, 0x0000 }, + { 0x8ef1, 0x0000 }, + { 0x8ef2, 0x0000 }, + { 0x8ef3, 0x0000 }, + { 0x8ef4, 0x0000 }, + { 0x8ef5, 0x0000 }, + { 0x8ef6, 0x0000 }, + { 0x8ef7, 0x0000 }, + { 0x8ef8, 0x0000 }, + { 0x8ef9, 0x0000 }, + { 0x8efa, 0x0000 }, + { 0x8efb, 0x0000 }, + { 0x8efc, 0x0000 }, + { 0x8efd, 0x0000 }, + { 0x8efe, 0x0000 }, + { 0x8eff, 0x0000 }, + { 0x8f00, 0x0000 }, + { 0x8f01, 0x0000 }, + { 0x8f02, 0x0000 }, + { 0x8f03, 0x0000 }, + { 0x8f04, 0x0000 }, + { 0x8f05, 0x0000 }, + { 0x8f06, 0x0000 }, + { 0x8f07, 0x0000 }, + { 0x8f08, 0x0000 }, + { 0x8f09, 0x0000 }, + { 0x8f0a, 0x0000 }, + { 0x8f0b, 0x0000 }, + { 0x8f0c, 0x0000 }, + { 0x8f0d, 0x0000 }, + { 0x8f0e, 0x0000 }, + { 0x8f0f, 0x0000 }, + { 0x8f10, 0x0000 }, + { 0x8f11, 0x0000 }, + { 0x8f12, 0x0000 }, + { 0x8f13, 0x0000 }, + { 0x8f14, 0x0000 }, + { 0x8f15, 0x0000 }, + { 0x8f16, 0x0000 }, + { 0x8f17, 0x0000 }, + { 0x8f18, 0x0000 }, + { 0x8f19, 0x0000 }, + { 0x8f1a, 0x0000 }, + { 0x8f1b, 0x0000 }, + { 0x8f1c, 0x0000 }, + { 0x8f1d, 0x0000 }, + { 0x8f1e, 0x0000 }, + { 0x8f1f, 0x0000 }, + { 0x8f20, 0x0000 }, + { 0x8f21, 0x0000 }, + { 0x8f22, 0x0000 }, + { 0x8f23, 0x0000 }, + { 0x8f24, 0x0000 }, + { 0x8f25, 0x0000 }, + { 0x8f26, 0x0000 }, + { 0x8f27, 0x0000 }, + { 0x8f28, 0x0000 }, + { 0x8f29, 0x0000 }, + { 0x8f2a, 0x0000 }, + { 0x8f2b, 0x0000 }, + { 0x8f2c, 0x0000 }, + { 0x8f2d, 0x0000 }, + { 0x8f2e, 0x0000 }, + { 0x8f2f, 0x0000 }, + { 0x8f30, 0x0000 }, + { 0x8f31, 0x0000 }, + { 0x8f32, 0x0000 }, + { 0x8f33, 0x0000 }, + { 0x8f34, 0x0000 }, + { 0x8f35, 0x0000 }, + { 0x8f36, 0x0000 }, + { 0x8f37, 0x0000 }, + { 0x8f38, 0x0000 }, + { 0x8f39, 0x0000 }, + { 0x8f3a, 0x0000 }, + { 0x8f3b, 0x0000 }, + { 0x8f3c, 0x0000 }, + { 0x8f3d, 0x0000 }, + { 0x8f3e, 0x0000 }, + { 0x8f3f, 0x0000 }, + { 0x8f40, 0x0000 }, + { 0x8f41, 0x0000 }, + { 0x8f42, 0x0000 }, + { 0x8f43, 0x0000 }, + { 0x8f44, 0x0000 }, + { 0x8f45, 0x0000 }, + { 0x8f46, 0x0000 }, + { 0x8f47, 0x0000 }, + { 0x8f48, 0x0000 }, + { 0x8f49, 0x0000 }, + { 0x8f4a, 0x0000 }, + { 0x8f4b, 0x0000 }, + { 0x8f4c, 0x0000 }, + { 0x8f4d, 0x0000 }, + { 0x8f4e, 0x0000 }, + { 0x8f4f, 0x0000 }, + { 0x8f50, 0x0000 }, + { 0x8f51, 0x0000 }, + { 0x8f52, 0x0000 }, + { 0x8f53, 0x0000 }, + { 0x8f54, 0x0000 }, + { 0x8f55, 0x0000 }, + { 0x8f56, 0x0000 }, + { 0x8f57, 0x0000 }, + { 0x8f58, 0x0000 }, + { 0x8f59, 0x0000 }, + { 0x8f5a, 0x0000 }, + { 0x8f5b, 0x0000 }, + { 0x8f5c, 0x0000 }, + { 0x8f5d, 0x0000 }, + { 0x8f5e, 0x0000 }, + { 0x8f5f, 0x0000 }, + { 0x8f60, 0x0000 }, + { 0x8f61, 0x0000 }, + { 0x8f62, 0x0000 }, + { 0x8f63, 0x0000 }, + { 0x8f64, 0x0000 }, + { 0x8f65, 0x0000 }, + { 0x8f66, 0x0000 }, + { 0x8f67, 0x0000 }, + { 0x8f68, 0x0000 }, + { 0x8f69, 0x0000 }, + { 0x8f6a, 0x0000 }, + { 0x8f6b, 0x0000 }, + { 0x8f6c, 0x0000 }, + { 0x8f6d, 0x0000 }, + { 0x8f6e, 0x0000 }, + { 0x8f6f, 0x0000 }, + { 0x8f70, 0x0000 }, + { 0x8f71, 0x0000 }, + { 0x8f72, 0x0000 }, + { 0x8f73, 0x0000 }, + { 0x8f74, 0x0000 }, + { 0x8f75, 0x0000 }, + { 0x8f76, 0x0000 }, + { 0x8f77, 0x0000 }, + { 0x8f78, 0x0000 }, + { 0x8f79, 0x0000 }, + { 0x8f7a, 0x0000 }, + { 0x8f7b, 0x0000 }, + { 0x8f7c, 0x0000 }, + { 0x8f7d, 0x0000 }, + { 0x8f7e, 0x0000 }, + { 0x8f7f, 0x0000 }, + { 0x8f80, 0x0000 }, + { 0x8f81, 0x0000 }, + { 0x8f82, 0x0000 }, + { 0x8f83, 0x0000 }, + { 0x8f84, 0x0000 }, + { 0x8f85, 0x0000 }, + { 0x8f86, 0x0000 }, + { 0x8f87, 0x0000 }, + { 0x8f88, 0x0000 }, + { 0x8f89, 0x0000 }, + { 0x8f8a, 0x0000 }, + { 0x8f8b, 0x0000 }, + { 0x8f8c, 0x0000 }, + { 0x8f8d, 0x0000 }, + { 0x8f8e, 0x0000 }, + { 0x8f8f, 0x0000 }, + { 0x8f90, 0x0000 }, + { 0x8f91, 0x0000 }, + { 0x8f92, 0x0000 }, + { 0x8f93, 0x0000 }, + { 0x8f94, 0x0000 }, + { 0x8f95, 0x0000 }, + { 0x8f96, 0x0000 }, + { 0x8f97, 0x0000 }, + { 0x8f98, 0x0000 }, + { 0x8f99, 0x0000 }, + { 0x8f9a, 0x0000 }, + { 0x8f9b, 0x0000 }, + { 0x8f9c, 0x0000 }, + { 0x8f9d, 0x0000 }, + { 0x8f9e, 0x0000 }, + { 0x8f9f, 0x0000 }, + { 0x8fa0, 0x0000 }, + { 0x8fa1, 0x0000 }, + { 0x8fa2, 0x0000 }, + { 0x8fa3, 0x0000 }, + { 0x8fa4, 0x0000 }, + { 0x8fa5, 0x0000 }, + { 0x8fa6, 0x0000 }, + { 0x8fa7, 0x0000 }, + { 0x8fa8, 0x0000 }, + { 0x8fa9, 0x0000 }, + { 0x8faa, 0x0000 }, + { 0x8fab, 0x0000 }, + { 0x8fac, 0x0000 }, + { 0x8fad, 0x0000 }, + { 0x8fae, 0x0000 }, + { 0x8faf, 0x0000 }, + { 0x8fb0, 0x0000 }, + { 0x8fb1, 0x0000 }, + { 0x8fb2, 0x0000 }, + { 0x8fb3, 0x0000 }, + { 0x8fb4, 0x0000 }, + { 0x8fb5, 0x0000 }, + { 0x8fb6, 0x0000 }, + { 0x8fb7, 0x0000 }, + { 0x8fb8, 0x0000 }, + { 0x8fb9, 0x0000 }, + { 0x8fba, 0x0000 }, + { 0x8fbb, 0x0000 }, + { 0x8fbc, 0x0000 }, + { 0x8fbd, 0x0000 }, + { 0x8fbe, 0x0000 }, + { 0x8fbf, 0x0000 }, + { 0x8fc0, 0x0000 }, + { 0x8fc1, 0x0000 }, + { 0x8fc2, 0x0000 }, + { 0x8fc3, 0x0000 }, + { 0x8fc4, 0x0000 }, + { 0x8fc5, 0x0000 }, + { 0x8fc6, 0x0000 }, + { 0x8fc7, 0x0000 }, + { 0x8fc8, 0x0000 }, + { 0x8fc9, 0x0000 }, + { 0x8fca, 0x0000 }, + { 0x8fcb, 0x0000 }, + { 0x8fcc, 0x0000 }, + { 0x8fcd, 0x0000 }, + { 0x8fce, 0x0000 }, + { 0x8fcf, 0x0000 }, + { 0x8fd0, 0x0000 }, + { 0x8fd1, 0x0000 }, + { 0x8fd2, 0x0000 }, + { 0x8fd3, 0x0000 }, + { 0x8fd4, 0x0000 }, + { 0x8fd5, 0x0000 }, + { 0x8fd6, 0x0000 }, + { 0x8fd7, 0x0000 }, + { 0x8fd8, 0x0000 }, + { 0x8fd9, 0x0000 }, + { 0x8fda, 0x0000 }, + { 0x8fdb, 0x0000 }, + { 0x8fdc, 0x0000 }, + { 0x8fdd, 0x0000 }, + { 0x8fde, 0x0000 }, + { 0x8fdf, 0x0000 }, + { 0x8fe0, 0x0000 }, + { 0x8fe1, 0x0000 }, + { 0x8fe2, 0x0000 }, + { 0x8fe3, 0x0000 }, + { 0x8fe4, 0x0000 }, + { 0x8fe5, 0x0000 }, + { 0x8fe6, 0x0000 }, + { 0x8fe7, 0x0000 }, + { 0x8fe8, 0x0000 }, + { 0x8fe9, 0x0000 }, + { 0x8fea, 0x0000 }, + { 0x8feb, 0x0000 }, + { 0x8fec, 0x0000 }, + { 0x8fed, 0x0000 }, + { 0x8fee, 0x0000 }, + { 0x8fef, 0x0000 }, + { 0x8ff0, 0x0000 }, + { 0x8ff1, 0x0000 }, + { 0x8ff2, 0x0000 }, + { 0x8ff3, 0x0000 }, + { 0x8ff4, 0x0000 }, + { 0x8ff5, 0x0000 }, + { 0x8ff6, 0x0000 }, + { 0x8ff7, 0x0000 }, + { 0x8ff8, 0x0000 }, + { 0x8ff9, 0x0000 }, + { 0x8ffa, 0x0000 }, + { 0x8ffb, 0x0000 }, + { 0x8ffc, 0x0000 }, + { 0x8ffd, 0x0000 }, + { 0x8ffe, 0x0000 }, + { 0x8fff, 0x0000 }, + { 0x9000, 0x0000 }, + { 0x9001, 0x0000 }, + { 0x9002, 0x0000 }, + { 0x9003, 0x0000 }, + { 0x9004, 0x0000 }, + { 0x9005, 0x0000 }, + { 0x9006, 0x0000 }, + { 0x9007, 0x0000 }, + { 0x9008, 0x0000 }, + { 0x9009, 0x0000 }, + { 0x900a, 0x0000 }, + { 0x900b, 0x0000 }, + { 0x900c, 0x0000 }, + { 0x900d, 0x0000 }, + { 0x900e, 0x0000 }, + { 0x900f, 0x0000 }, + { 0x9010, 0x0000 }, + { 0x9011, 0x0000 }, + { 0x9012, 0x0000 }, + { 0x9013, 0x0000 }, + { 0x9014, 0x0000 }, + { 0x9015, 0x0000 }, + { 0x9016, 0x0000 }, + { 0x9017, 0x0000 }, + { 0x9018, 0x0000 }, + { 0x9019, 0x0000 }, + { 0x901a, 0x0000 }, + { 0x901b, 0x0000 }, + { 0x901c, 0x0000 }, + { 0x901d, 0x0000 }, + { 0x901e, 0x0000 }, + { 0x901f, 0x0000 }, + { 0x9020, 0x0000 }, + { 0x9021, 0x0000 }, + { 0x9022, 0x0000 }, + { 0x9023, 0x0000 }, + { 0x9024, 0x0000 }, + { 0x9025, 0x0000 }, + { 0x9026, 0x0000 }, + { 0x9027, 0x0000 }, + { 0x9028, 0x0000 }, + { 0x9029, 0x0000 }, + { 0x902a, 0x0000 }, + { 0x902b, 0x0000 }, + { 0x902c, 0x0000 }, + { 0x902d, 0x0000 }, + { 0x902e, 0x0000 }, + { 0x902f, 0x0000 }, + { 0x9030, 0x0000 }, + { 0x9031, 0x0000 }, + { 0x9032, 0x0000 }, + { 0x9033, 0x0000 }, + { 0x9034, 0x0000 }, + { 0x9035, 0x0000 }, + { 0x9036, 0x0000 }, + { 0x9037, 0x0000 }, + { 0x9038, 0x0000 }, + { 0x9039, 0x0000 }, + { 0x903a, 0x0000 }, + { 0x903b, 0x0000 }, + { 0x903c, 0x0000 }, + { 0x903d, 0x0000 }, + { 0x903e, 0x0000 }, + { 0x903f, 0x0000 }, + { 0x9040, 0x0000 }, + { 0x9041, 0x0000 }, + { 0x9042, 0x0000 }, + { 0x9043, 0x0000 }, + { 0x9044, 0x0000 }, + { 0x9045, 0x0000 }, + { 0x9046, 0x0000 }, + { 0x9047, 0x0000 }, + { 0x9048, 0x0000 }, + { 0x9049, 0x0000 }, + { 0x904a, 0x0000 }, + { 0x904b, 0x0000 }, + { 0x904c, 0x0000 }, + { 0x904d, 0x0000 }, + { 0x904e, 0x0000 }, + { 0x904f, 0x0000 }, + { 0x9050, 0x0000 }, + { 0x9051, 0x0000 }, + { 0x9052, 0x0000 }, + { 0x9053, 0x0000 }, + { 0x9054, 0x0000 }, + { 0x9055, 0x0000 }, + { 0x9056, 0x0000 }, + { 0x9057, 0x0000 }, + { 0x9058, 0x0000 }, + { 0x9059, 0x0000 }, + { 0x905a, 0x0000 }, + { 0x905b, 0x0000 }, + { 0x905c, 0x0000 }, + { 0x905d, 0x0000 }, + { 0x905e, 0x0000 }, + { 0x905f, 0x0000 }, + { 0x9060, 0x0000 }, + { 0x9061, 0x0000 }, + { 0x9062, 0x0000 }, + { 0x9063, 0x0000 }, + { 0x9064, 0x0000 }, + { 0x9065, 0x0000 }, + { 0x9066, 0x0000 }, + { 0x9067, 0x0000 }, + { 0x9068, 0x0000 }, + { 0x9069, 0x0000 }, + { 0x906a, 0x0000 }, + { 0x906b, 0x0000 }, + { 0x906c, 0x0000 }, + { 0x906d, 0x0000 }, + { 0x906e, 0x0000 }, + { 0x906f, 0x0000 }, + { 0x9070, 0x0000 }, + { 0x9071, 0x0000 }, + { 0x9072, 0x0000 }, + { 0x9073, 0x0000 }, + { 0x9074, 0x0000 }, + { 0x9075, 0x0000 }, + { 0x9076, 0x0000 }, + { 0x9077, 0x0000 }, + { 0x9078, 0x0000 }, + { 0x9079, 0x0000 }, + { 0x907a, 0x0000 }, + { 0x907b, 0x0000 }, + { 0x907c, 0x0000 }, + { 0x907d, 0x0000 }, + { 0x907e, 0x0000 }, + { 0x907f, 0x0000 }, + { 0x9080, 0x0000 }, + { 0x9081, 0x0000 }, + { 0x9082, 0x0000 }, + { 0x9083, 0x0000 }, + { 0x9084, 0x0000 }, + { 0x9085, 0x0000 }, + { 0x9086, 0x0000 }, + { 0x9087, 0x0000 }, + { 0x9088, 0x0000 }, + { 0x9089, 0x0000 }, + { 0x908a, 0x0000 }, + { 0x908b, 0x0000 }, + { 0x908c, 0x0000 }, + { 0x908d, 0x0000 }, + { 0x908e, 0x0000 }, + { 0x908f, 0x0000 }, + { 0x9090, 0x0000 }, + { 0x9091, 0x0000 }, + { 0x9092, 0x0000 }, + { 0x9093, 0x0000 }, + { 0x9094, 0x0000 }, + { 0x9095, 0x0000 }, + { 0x9096, 0x0000 }, + { 0x9097, 0x0000 }, + { 0x9098, 0x0000 }, + { 0x9099, 0x0000 }, + { 0x909a, 0x0000 }, + { 0x909b, 0x0000 }, + { 0x909c, 0x0000 }, + { 0x909d, 0x0000 }, + { 0x909e, 0x0000 }, + { 0x909f, 0x0000 }, + { 0x90a0, 0x0000 }, + { 0x90a1, 0x0000 }, + { 0x90a2, 0x0000 }, + { 0x90a3, 0x0000 }, + { 0x90a4, 0x0000 }, + { 0x90a5, 0x0000 }, + { 0x90a6, 0x0000 }, + { 0x90a7, 0x0000 }, + { 0x90a8, 0x0000 }, + { 0x90a9, 0x0000 }, + { 0x90aa, 0x0000 }, + { 0x90ab, 0x0000 }, + { 0x90ac, 0x0000 }, + { 0x90ad, 0x0000 }, + { 0x90ae, 0x0000 }, + { 0x90af, 0x0000 }, + { 0x90b0, 0x0000 }, + { 0x90b1, 0x0000 }, + { 0x90b2, 0x0000 }, + { 0x90b3, 0x0000 }, + { 0x90b4, 0x0000 }, + { 0x90b5, 0x0000 }, + { 0x90b6, 0x0000 }, + { 0x90b7, 0x0000 }, + { 0x90b8, 0x0000 }, + { 0x90b9, 0x0000 }, + { 0x90ba, 0x0000 }, + { 0x90bb, 0x0000 }, + { 0x90bc, 0x0000 }, + { 0x90bd, 0x0000 }, + { 0x90be, 0x0000 }, + { 0x90bf, 0x0000 }, + { 0x90c0, 0x0000 }, + { 0x90c1, 0x0000 }, + { 0x90c2, 0x0000 }, + { 0x90c3, 0x0000 }, + { 0x90c4, 0x0000 }, + { 0x90c5, 0x0000 }, + { 0x90c6, 0x0000 }, + { 0x90c7, 0x0000 }, + { 0x90c8, 0x0000 }, + { 0x90c9, 0x0000 }, + { 0x90ca, 0x0000 }, + { 0x90cb, 0x0000 }, + { 0x90cc, 0x0000 }, + { 0x90cd, 0x0000 }, + { 0x90ce, 0x0000 }, + { 0x90cf, 0x0000 }, + { 0x90d0, 0x0000 }, + { 0x90d1, 0x0000 }, + { 0x90d2, 0x0000 }, + { 0x90d3, 0x0000 }, + { 0x90d4, 0x0000 }, + { 0x90d5, 0x0000 }, + { 0x90d6, 0x0000 }, + { 0x90d7, 0x0000 }, + { 0x90d8, 0x0000 }, + { 0x90d9, 0x0000 }, + { 0x90da, 0x0000 }, + { 0x90db, 0x0000 }, + { 0x90dc, 0x0000 }, + { 0x90dd, 0x0000 }, + { 0x90de, 0x0000 }, + { 0x90df, 0x0000 }, + { 0x90e0, 0x0000 }, + { 0x90e1, 0x0000 }, + { 0x90e2, 0x0000 }, + { 0x90e3, 0x0000 }, + { 0x90e4, 0x0000 }, + { 0x90e5, 0x0000 }, + { 0x90e6, 0x0000 }, + { 0x90e7, 0x0000 }, + { 0x90e8, 0x0000 }, + { 0x90e9, 0x0000 }, + { 0x90ea, 0x0000 }, + { 0x90eb, 0x0000 }, + { 0x90ec, 0x0000 }, + { 0x90ed, 0x0000 }, + { 0x90ee, 0x0000 }, + { 0x90ef, 0x0000 }, + { 0x90f0, 0x0000 }, + { 0x90f1, 0x0000 }, + { 0x90f2, 0x0000 }, + { 0x90f3, 0x0000 }, + { 0x90f4, 0x0000 }, + { 0x90f5, 0x0000 }, + { 0x90f6, 0x0000 }, + { 0x90f7, 0x0000 }, + { 0x90f8, 0x0000 }, + { 0x90f9, 0x0000 }, + { 0x90fa, 0x0000 }, + { 0x90fb, 0x0000 }, + { 0x90fc, 0x0000 }, + { 0x90fd, 0x0000 }, + { 0x90fe, 0x0000 }, + { 0x90ff, 0x0000 }, + { 0x9100, 0x0000 }, + { 0x9101, 0x0000 }, + { 0x9102, 0x0000 }, + { 0x9103, 0x0000 }, + { 0x9104, 0x0000 }, + { 0x9105, 0x0000 }, + { 0x9106, 0x0000 }, + { 0x9107, 0x0000 }, + { 0x9108, 0x0000 }, + { 0x9109, 0x0000 }, + { 0x910a, 0x0000 }, + { 0x910b, 0x0000 }, + { 0x910c, 0x0000 }, + { 0x910d, 0x0000 }, + { 0x910e, 0x0000 }, + { 0x910f, 0x0000 }, + { 0x9110, 0x0000 }, + { 0x9111, 0x0000 }, + { 0x9112, 0x0000 }, + { 0x9113, 0x0000 }, + { 0x9114, 0x0000 }, + { 0x9115, 0x0000 }, + { 0x9116, 0x0000 }, + { 0x9117, 0x0000 }, + { 0x9118, 0x0000 }, + { 0x9119, 0x0000 }, + { 0x911a, 0x0000 }, + { 0x911b, 0x0000 }, + { 0x911c, 0x0000 }, + { 0x911d, 0x0000 }, + { 0x911e, 0x0000 }, + { 0x911f, 0x0000 }, + { 0x9120, 0x0000 }, + { 0x9121, 0x0000 }, + { 0x9122, 0x0000 }, + { 0x9123, 0x0000 }, + { 0x9124, 0x0000 }, + { 0x9125, 0x0000 }, + { 0x9126, 0x0000 }, + { 0x9127, 0x0000 }, + { 0x9128, 0x0000 }, + { 0x9129, 0x0000 }, + { 0x912a, 0x0000 }, + { 0x912b, 0x0000 }, + { 0x912c, 0x0000 }, + { 0x912d, 0x0000 }, + { 0x912e, 0x0000 }, + { 0x912f, 0x0000 }, + { 0x9130, 0x0000 }, + { 0x9131, 0x0000 }, + { 0x9132, 0x0000 }, + { 0x9133, 0x0000 }, + { 0x9134, 0x0000 }, + { 0x9135, 0x0000 }, + { 0x9136, 0x0000 }, + { 0x9137, 0x0000 }, + { 0x9138, 0x0000 }, + { 0x9139, 0x0000 }, + { 0x913a, 0x0000 }, + { 0x913b, 0x0000 }, + { 0x913c, 0x0000 }, + { 0x913d, 0x0000 }, + { 0x913e, 0x0000 }, + { 0x913f, 0x0000 }, + { 0x9140, 0x0000 }, + { 0x9141, 0x0000 }, + { 0x9142, 0x0000 }, + { 0x9143, 0x0000 }, + { 0x9144, 0x0000 }, + { 0x9145, 0x0000 }, + { 0x9146, 0x0000 }, + { 0x9147, 0x0000 }, + { 0x9148, 0x0000 }, + { 0x9149, 0x0000 }, + { 0x914a, 0x0000 }, + { 0x914b, 0x0000 }, + { 0x914c, 0x0000 }, + { 0x914d, 0x0000 }, + { 0x914e, 0x0000 }, + { 0x914f, 0x0000 }, + { 0x9150, 0x0000 }, + { 0x9151, 0x0000 }, + { 0x9152, 0x0000 }, + { 0x9153, 0x0000 }, + { 0x9154, 0x0000 }, + { 0x9155, 0x0000 }, + { 0x9156, 0x0000 }, + { 0x9157, 0x0000 }, + { 0x9158, 0x0000 }, + { 0x9159, 0x0000 }, + { 0x915a, 0x0000 }, + { 0x915b, 0x0000 }, + { 0x915c, 0x0000 }, + { 0x915d, 0x0000 }, + { 0x915e, 0x0000 }, + { 0x915f, 0x0000 }, + { 0x9160, 0x0000 }, + { 0x9161, 0x0000 }, + { 0x9162, 0x0000 }, + { 0x9163, 0x0000 }, + { 0x9164, 0x0000 }, + { 0x9165, 0x0000 }, + { 0x9166, 0x0000 }, + { 0x9167, 0x0000 }, + { 0x9168, 0x0000 }, + { 0x9169, 0x0000 }, + { 0x916a, 0x0000 }, + { 0x916b, 0x0000 }, + { 0x916c, 0x0000 }, + { 0x916d, 0x0000 }, + { 0x916e, 0x0000 }, + { 0x916f, 0x0000 }, + { 0x9170, 0x0000 }, + { 0x9171, 0x0000 }, + { 0x9172, 0x0000 }, + { 0x9173, 0x0000 }, + { 0x9174, 0x0000 }, + { 0x9175, 0x0000 }, + { 0x9176, 0x0000 }, + { 0x9177, 0x0000 }, + { 0x9178, 0x0000 }, + { 0x9179, 0x0000 }, + { 0x917a, 0x0000 }, + { 0x917b, 0x0000 }, + { 0x917c, 0x0000 }, + { 0x917d, 0x0000 }, + { 0x917e, 0x0000 }, + { 0x917f, 0x0000 }, + { 0x9180, 0x0000 }, + { 0x9181, 0x0000 }, + { 0x9182, 0x0000 }, + { 0x9183, 0x0000 }, + { 0x9184, 0x0000 }, + { 0x9185, 0x0000 }, + { 0x9186, 0x0000 }, + { 0x9187, 0x0000 }, + { 0x9188, 0x0000 }, + { 0x9189, 0x0000 }, + { 0x918a, 0x0000 }, + { 0x918b, 0x0000 }, + { 0x918c, 0x0000 }, + { 0x918d, 0x0000 }, + { 0x918e, 0x0000 }, + { 0x918f, 0x0000 }, + { 0x9190, 0x0000 }, + { 0x9191, 0x0000 }, + { 0x9192, 0x0000 }, + { 0x9193, 0x0000 }, + { 0x9194, 0x0000 }, + { 0x9195, 0x0000 }, + { 0x9196, 0x0000 }, + { 0x9197, 0x0000 }, + { 0x9198, 0x0000 }, + { 0x9199, 0x0000 }, + { 0x919a, 0x0000 }, + { 0x919b, 0x0000 }, + { 0x919c, 0x0000 }, + { 0x919d, 0x0000 }, + { 0x919e, 0x0000 }, + { 0x919f, 0x0000 }, + { 0x91a0, 0x0000 }, + { 0x91a1, 0x0000 }, + { 0x91a2, 0x0000 }, + { 0x91a3, 0x0000 }, + { 0x91a4, 0x0000 }, + { 0x91a5, 0x0000 }, + { 0x91a6, 0x0000 }, + { 0x91a7, 0x0000 }, + { 0x91a8, 0x0000 }, + { 0x91a9, 0x0000 }, + { 0x91aa, 0x0000 }, + { 0x91ab, 0x0000 }, + { 0x91ac, 0x0000 }, + { 0x91ad, 0x0000 }, + { 0x91ae, 0x0000 }, + { 0x91af, 0x0000 }, + { 0x91b0, 0x0000 }, + { 0x91b1, 0x0000 }, + { 0x91b2, 0x0000 }, + { 0x91b3, 0x0000 }, + { 0x91b4, 0x0000 }, + { 0x91b5, 0x0000 }, + { 0x91b6, 0x0000 }, + { 0x91b7, 0x0000 }, + { 0x91b8, 0x0000 }, + { 0x91b9, 0x0000 }, + { 0x91ba, 0x0000 }, + { 0x91bb, 0x0000 }, + { 0x91bc, 0x0000 }, + { 0x91bd, 0x0000 }, + { 0x91be, 0x0000 }, + { 0x91bf, 0x0000 }, + { 0x91c0, 0x0000 }, + { 0x91c1, 0x0000 }, + { 0x91c2, 0x0000 }, + { 0x91c3, 0x0000 }, + { 0x91c4, 0x0000 }, + { 0x91c5, 0x0000 }, + { 0x91c6, 0x0000 }, + { 0x91c7, 0x0000 }, + { 0x91c8, 0x0000 }, + { 0x91c9, 0x0000 }, + { 0x91ca, 0x0000 }, + { 0x91cb, 0x0000 }, + { 0x91cc, 0x0000 }, + { 0x91cd, 0x0000 }, + { 0x91ce, 0x0000 }, + { 0x91cf, 0x0000 }, + { 0x91d0, 0x0000 }, + { 0x91d1, 0x0000 }, + { 0x91d2, 0x0000 }, + { 0x91d3, 0x0000 }, + { 0x91d4, 0x0000 }, + { 0x91d5, 0x0000 }, + { 0x91d6, 0x0000 }, + { 0x91d7, 0x0000 }, + { 0x91d8, 0x0000 }, + { 0x91d9, 0x0000 }, + { 0x91da, 0x0000 }, + { 0x91db, 0x0000 }, + { 0x91dc, 0x0000 }, + { 0x91dd, 0x0000 }, + { 0x91de, 0x0000 }, + { 0x91df, 0x0000 }, + { 0x91e0, 0x0000 }, + { 0x91e1, 0x0000 }, + { 0x91e2, 0x0000 }, + { 0x91e3, 0x0000 }, + { 0x91e4, 0x0000 }, + { 0x91e5, 0x0000 }, + { 0x91e6, 0x0000 }, + { 0x91e7, 0x0000 }, + { 0x91e8, 0x0000 }, + { 0x91e9, 0x0000 }, + { 0x91ea, 0x0000 }, + { 0x91eb, 0x0000 }, + { 0x91ec, 0x0000 }, + { 0x91ed, 0x0000 }, + { 0x91ee, 0x0000 }, + { 0x91ef, 0x0000 }, + { 0x91f0, 0x0000 }, + { 0x91f1, 0x0000 }, + { 0x91f2, 0x0000 }, + { 0x91f3, 0x0000 }, + { 0x91f4, 0x0000 }, + { 0x91f5, 0x0000 }, + { 0x91f6, 0x0000 }, + { 0x91f7, 0x0000 }, + { 0x91f8, 0x0000 }, + { 0x91f9, 0x0000 }, + { 0x91fa, 0x0000 }, + { 0x91fb, 0x0000 }, + { 0x91fc, 0x0000 }, + { 0x91fd, 0x0000 }, + { 0x91fe, 0x0000 }, + { 0x91ff, 0x0000 }, + { 0x9200, 0x0000 }, + { 0x9201, 0x0000 }, + { 0x9202, 0x0000 }, + { 0x9203, 0x0000 }, + { 0x9204, 0x0000 }, + { 0x9205, 0x0000 }, + { 0x9206, 0x0000 }, + { 0x9207, 0x0000 }, + { 0x9208, 0x0000 }, + { 0x9209, 0x0000 }, + { 0x920a, 0x0000 }, + { 0x920b, 0x0000 }, + { 0x920c, 0x0000 }, + { 0x920d, 0x0000 }, + { 0x920e, 0x0000 }, + { 0x920f, 0x0000 }, + { 0x9210, 0x0000 }, + { 0x9211, 0x0000 }, + { 0x9212, 0x0000 }, + { 0x9213, 0x0000 }, + { 0x9214, 0x0000 }, + { 0x9215, 0x0000 }, + { 0x9216, 0x0000 }, + { 0x9217, 0x0000 }, + { 0x9218, 0x0000 }, + { 0x9219, 0x0000 }, + { 0x921a, 0x0000 }, + { 0x921b, 0x0000 }, + { 0x921c, 0x0000 }, + { 0x921d, 0x0000 }, + { 0x921e, 0x0000 }, + { 0x921f, 0x0000 }, + { 0x9220, 0x0000 }, + { 0x9221, 0x0000 }, + { 0x9222, 0x0000 }, + { 0x9223, 0x0000 }, + { 0x9224, 0x0000 }, + { 0x9225, 0x0000 }, + { 0x9226, 0x0000 }, + { 0x9227, 0x0000 }, + { 0x9228, 0x0000 }, + { 0x9229, 0x0000 }, + { 0x922a, 0x0000 }, + { 0x922b, 0x0000 }, + { 0x922c, 0x0000 }, + { 0x922d, 0x0000 }, + { 0x922e, 0x0000 }, + { 0x922f, 0x0000 }, + { 0x9230, 0x0000 }, + { 0x9231, 0x0000 }, + { 0x9232, 0x0000 }, + { 0x9233, 0x0000 }, + { 0x9234, 0x0000 }, + { 0x9235, 0x0000 }, + { 0x9236, 0x0000 }, + { 0x9237, 0x0000 }, + { 0x9238, 0x0000 }, + { 0x9239, 0x0000 }, + { 0x923a, 0x0000 }, + { 0x923b, 0x0000 }, + { 0x923c, 0x0000 }, + { 0x923d, 0x0000 }, + { 0x923e, 0x0000 }, + { 0x923f, 0x0000 }, + { 0x9240, 0x0000 }, + { 0x9241, 0x0000 }, + { 0x9242, 0x0000 }, + { 0x9243, 0x0000 }, + { 0x9244, 0x0000 }, + { 0x9245, 0x0000 }, + { 0x9246, 0x0000 }, + { 0x9247, 0x0000 }, + { 0x9248, 0x0000 }, + { 0x9249, 0x0000 }, + { 0x924a, 0x0000 }, + { 0x924b, 0x0000 }, + { 0x924c, 0x0000 }, + { 0x924d, 0x0000 }, + { 0x924e, 0x0000 }, + { 0x924f, 0x0000 }, + { 0x9250, 0x0000 }, + { 0x9251, 0x0000 }, + { 0x9252, 0x0000 }, + { 0x9253, 0x0000 }, + { 0x9254, 0x0000 }, + { 0x9255, 0x0000 }, + { 0x9256, 0x0000 }, + { 0x9257, 0x0000 }, + { 0x9258, 0x0000 }, + { 0x9259, 0x0000 }, + { 0x925a, 0x0000 }, + { 0x925b, 0x0000 }, + { 0x925c, 0x0000 }, + { 0x925d, 0x0000 }, + { 0x925e, 0x0000 }, + { 0x925f, 0x0000 }, + { 0x9260, 0x0000 }, + { 0x9261, 0x0000 }, + { 0x9262, 0x0000 }, + { 0x9263, 0x0000 }, + { 0x9264, 0x0000 }, + { 0x9265, 0x0000 }, + { 0x9266, 0x0000 }, + { 0x9267, 0x0000 }, + { 0x9268, 0x0000 }, + { 0x9269, 0x0000 }, + { 0x926a, 0x0000 }, + { 0x926b, 0x0000 }, + { 0x926c, 0x0000 }, + { 0x926d, 0x0000 }, + { 0x926e, 0x0000 }, + { 0x926f, 0x0000 }, + { 0x9270, 0x0000 }, + { 0x9271, 0x0000 }, + { 0x9272, 0x0000 }, + { 0x9273, 0x0000 }, + { 0x9274, 0x0000 }, + { 0x9275, 0x0000 }, + { 0x9276, 0x0000 }, + { 0x9277, 0x0000 }, + { 0x9278, 0x0000 }, + { 0x9279, 0x0000 }, + { 0x927a, 0x0000 }, + { 0x927b, 0x0000 }, + { 0x927c, 0x0000 }, + { 0x927d, 0x0000 }, + { 0x927e, 0x0000 }, + { 0x927f, 0x0000 }, + { 0x9280, 0x0000 }, + { 0x9281, 0x0000 }, + { 0x9282, 0x0000 }, + { 0x9283, 0x0000 }, + { 0x9284, 0x0000 }, + { 0x9285, 0x0000 }, + { 0x9286, 0x0000 }, + { 0x9287, 0x0000 }, + { 0x9288, 0x0000 }, + { 0x9289, 0x0000 }, + { 0x928a, 0x0000 }, + { 0x928b, 0x0000 }, + { 0x928c, 0x0000 }, + { 0x928d, 0x0000 }, + { 0x928e, 0x0000 }, + { 0x928f, 0x0000 }, + { 0x9290, 0x0000 }, + { 0x9291, 0x0000 }, + { 0x9292, 0x0000 }, + { 0x9293, 0x0000 }, + { 0x9294, 0x0000 }, + { 0x9295, 0x0000 }, + { 0x9296, 0x0000 }, + { 0x9297, 0x0000 }, + { 0x9298, 0x0000 }, + { 0x9299, 0x0000 }, + { 0x929a, 0x0000 }, + { 0x929b, 0x0000 }, + { 0x929c, 0x0000 }, + { 0x929d, 0x0000 }, + { 0x929e, 0x0000 }, + { 0x929f, 0x0000 }, + { 0x92a0, 0x0000 }, + { 0x92a1, 0x0000 }, + { 0x92a2, 0x0000 }, + { 0x92a3, 0x0000 }, + { 0x92a4, 0x0000 }, + { 0x92a5, 0x0000 }, + { 0x92a6, 0x0000 }, + { 0x92a7, 0x0000 }, + { 0x92a8, 0x0000 }, + { 0x92a9, 0x0000 }, + { 0x92aa, 0x0000 }, + { 0x92ab, 0x0000 }, + { 0x92ac, 0x0000 }, + { 0x92ad, 0x0000 }, + { 0x92ae, 0x0000 }, + { 0x92af, 0x0000 }, + { 0x92b0, 0x0000 }, + { 0x92b1, 0x0000 }, + { 0x92b2, 0x0000 }, + { 0x92b3, 0x0000 }, + { 0x92b4, 0x0000 }, + { 0x92b5, 0x0000 }, + { 0x92b6, 0x0000 }, + { 0x92b7, 0x0000 }, + { 0x92b8, 0x0000 }, + { 0x92b9, 0x0000 }, + { 0x92ba, 0x0000 }, + { 0x92bb, 0x0000 }, + { 0x92bc, 0x0000 }, + { 0x92bd, 0x0000 }, + { 0x92be, 0x0000 }, + { 0x92bf, 0x0000 }, + { 0x92c0, 0x0000 }, + { 0x92c1, 0x0000 }, + { 0x92c2, 0x0000 }, + { 0x92c3, 0x0000 }, + { 0x92c4, 0x0000 }, + { 0x92c5, 0x0000 }, + { 0x92c6, 0x0000 }, + { 0x92c7, 0x0000 }, + { 0x92c8, 0x0000 }, + { 0x92c9, 0x0000 }, + { 0x92ca, 0x0000 }, + { 0x92cb, 0x0000 }, + { 0x92cc, 0x0000 }, + { 0x92cd, 0x0000 }, + { 0x92ce, 0x0000 }, + { 0x92cf, 0x0000 }, + { 0x92d0, 0x0000 }, + { 0x92d1, 0x0000 }, + { 0x92d2, 0x0000 }, + { 0x92d3, 0x0000 }, + { 0x92d4, 0x0000 }, + { 0x92d5, 0x0000 }, + { 0x92d6, 0x0000 }, + { 0x92d7, 0x0000 }, + { 0x92d8, 0x0000 }, + { 0x92d9, 0x0000 }, + { 0x92da, 0x0000 }, + { 0x92db, 0x0000 }, + { 0x92dc, 0x0000 }, + { 0x92dd, 0x0000 }, + { 0x92de, 0x0000 }, + { 0x92df, 0x0000 }, + { 0x92e0, 0x0000 }, + { 0x92e1, 0x0000 }, + { 0x92e2, 0x0000 }, + { 0x92e3, 0x0000 }, + { 0x92e4, 0x0000 }, + { 0x92e5, 0x0000 }, + { 0x92e6, 0x0000 }, + { 0x92e7, 0x0000 }, + { 0x92e8, 0x0000 }, + { 0x92e9, 0x0000 }, + { 0x92ea, 0x0000 }, + { 0x92eb, 0x0000 }, + { 0x92ec, 0x0000 }, + { 0x92ed, 0x0000 }, + { 0x92ee, 0x0000 }, + { 0x92ef, 0x0000 }, + { 0x92f0, 0x0000 }, + { 0x92f1, 0x0000 }, + { 0x92f2, 0x0000 }, + { 0x92f3, 0x0000 }, + { 0x92f4, 0x0000 }, + { 0x92f5, 0x0000 }, + { 0x92f6, 0x0000 }, + { 0x92f7, 0x0000 }, + { 0x92f8, 0x0000 }, + { 0x92f9, 0x0000 }, + { 0x92fa, 0x0000 }, + { 0x92fb, 0x0000 }, + { 0x92fc, 0x0000 }, + { 0x92fd, 0x0000 }, + { 0x92fe, 0x0000 }, + { 0x92ff, 0x0000 }, + { 0x9300, 0x0000 }, + { 0x9301, 0x0000 }, + { 0x9302, 0x0000 }, + { 0x9303, 0x0000 }, + { 0x9304, 0x0000 }, + { 0x9305, 0x0000 }, + { 0x9306, 0x0000 }, + { 0x9307, 0x0000 }, + { 0x9308, 0x0000 }, + { 0x9309, 0x0000 }, + { 0x930a, 0x0000 }, + { 0x930b, 0x0000 }, + { 0x930c, 0x0000 }, + { 0x930d, 0x0000 }, + { 0x930e, 0x0000 }, + { 0x930f, 0x0000 }, + { 0x9310, 0x0000 }, + { 0x9311, 0x0000 }, + { 0x9312, 0x0000 }, + { 0x9313, 0x0000 }, + { 0x9314, 0x0000 }, + { 0x9315, 0x0000 }, + { 0x9316, 0x0000 }, + { 0x9317, 0x0000 }, + { 0x9318, 0x0000 }, + { 0x9319, 0x0000 }, + { 0x931a, 0x0000 }, + { 0x931b, 0x0000 }, + { 0x931c, 0x0000 }, + { 0x931d, 0x0000 }, + { 0x931e, 0x0000 }, + { 0x931f, 0x0000 }, + { 0x9320, 0x0000 }, + { 0x9321, 0x0000 }, + { 0x9322, 0x0000 }, + { 0x9323, 0x0000 }, + { 0x9324, 0x0000 }, + { 0x9325, 0x0000 }, + { 0x9326, 0x0000 }, + { 0x9327, 0x0000 }, + { 0x9328, 0x0000 }, + { 0x9329, 0x0000 }, + { 0x932a, 0x0000 }, + { 0x932b, 0x0000 }, + { 0x932c, 0x0000 }, + { 0x932d, 0x0000 }, + { 0x932e, 0x0000 }, + { 0x932f, 0x0000 }, + { 0x9330, 0x0000 }, + { 0x9331, 0x0000 }, + { 0x9332, 0x0000 }, + { 0x9333, 0x0000 }, + { 0x9334, 0x0000 }, + { 0x9335, 0x0000 }, + { 0x9336, 0x0000 }, + { 0x9337, 0x0000 }, + { 0x9338, 0x0000 }, + { 0x9339, 0x0000 }, + { 0x933a, 0x0000 }, + { 0x933b, 0x0000 }, + { 0x933c, 0x0000 }, + { 0x933d, 0x0000 }, + { 0x933e, 0x0000 }, + { 0x933f, 0x0000 }, + { 0x9340, 0x0000 }, + { 0x9341, 0x0000 }, + { 0x9342, 0x0000 }, + { 0x9343, 0x0000 }, + { 0x9344, 0x0000 }, + { 0x9345, 0x0000 }, + { 0x9346, 0x0000 }, + { 0x9347, 0x0000 }, + { 0x9348, 0x0000 }, + { 0x9349, 0x0000 }, + { 0x934a, 0x0000 }, + { 0x934b, 0x0000 }, + { 0x934c, 0x0000 }, + { 0x934d, 0x0000 }, + { 0x934e, 0x0000 }, + { 0x934f, 0x0000 }, + { 0x9350, 0x0000 }, + { 0x9351, 0x0000 }, + { 0x9352, 0x0000 }, + { 0x9353, 0x0000 }, + { 0x9354, 0x0000 }, + { 0x9355, 0x0000 }, + { 0x9356, 0x0000 }, + { 0x9357, 0x0000 }, + { 0x9358, 0x0000 }, + { 0x9359, 0x0000 }, + { 0x935a, 0x0000 }, + { 0x935b, 0x0000 }, + { 0x935c, 0x0000 }, + { 0x935d, 0x0000 }, + { 0x935e, 0x0000 }, + { 0x935f, 0x0000 }, + { 0x9360, 0x0000 }, + { 0x9361, 0x0000 }, + { 0x9362, 0x0000 }, + { 0x9363, 0x0000 }, + { 0x9364, 0x0000 }, + { 0x9365, 0x0000 }, + { 0x9366, 0x0000 }, + { 0x9367, 0x0000 }, + { 0x9368, 0x0000 }, + { 0x9369, 0x0000 }, + { 0x936a, 0x0000 }, + { 0x936b, 0x0000 }, + { 0x936c, 0x0000 }, + { 0x936d, 0x0000 }, + { 0x936e, 0x0000 }, + { 0x936f, 0x0000 }, + { 0x9370, 0x0000 }, + { 0x9371, 0x0000 }, + { 0x9372, 0x0000 }, + { 0x9373, 0x0000 }, + { 0x9374, 0x0000 }, + { 0x9375, 0x0000 }, + { 0x9376, 0x0000 }, + { 0x9377, 0x0000 }, + { 0x9378, 0x0000 }, + { 0x9379, 0x0000 }, + { 0x937a, 0x0000 }, + { 0x937b, 0x0000 }, + { 0x937c, 0x0000 }, + { 0x937d, 0x0000 }, + { 0x937e, 0x0000 }, + { 0x937f, 0x0000 }, + { 0x9380, 0x0000 }, + { 0x9381, 0x0000 }, + { 0x9382, 0x0000 }, + { 0x9383, 0x0000 }, + { 0x9384, 0x0000 }, + { 0x9385, 0x0000 }, + { 0x9386, 0x0000 }, + { 0x9387, 0x0000 }, + { 0x9388, 0x0000 }, + { 0x9389, 0x0000 }, + { 0x938a, 0x0000 }, + { 0x938b, 0x0000 }, + { 0x938c, 0x0000 }, + { 0x938d, 0x0000 }, + { 0x938e, 0x0000 }, + { 0x938f, 0x0000 }, + { 0x9390, 0x0000 }, + { 0x9391, 0x0000 }, + { 0x9392, 0x0000 }, + { 0x9393, 0x0000 }, + { 0x9394, 0x0000 }, + { 0x9395, 0x0000 }, + { 0x9396, 0x0000 }, + { 0x9397, 0x0000 }, + { 0x9398, 0x0000 }, + { 0x9399, 0x0000 }, + { 0x939a, 0x0000 }, + { 0x939b, 0x0000 }, + { 0x939c, 0x0000 }, + { 0x939d, 0x0000 }, + { 0x939e, 0x0000 }, + { 0x939f, 0x0000 }, + { 0x93a0, 0x0000 }, + { 0x93a1, 0x0000 }, + { 0x93a2, 0x0000 }, + { 0x93a3, 0x0000 }, + { 0x93a4, 0x0000 }, + { 0x93a5, 0x0000 }, + { 0x93a6, 0x0000 }, + { 0x93a7, 0x0000 }, + { 0x93a8, 0x0000 }, + { 0x93a9, 0x0000 }, + { 0x93aa, 0x0000 }, + { 0x93ab, 0x0000 }, + { 0x93ac, 0x0000 }, + { 0x93ad, 0x0000 }, + { 0x93ae, 0x0000 }, + { 0x93af, 0x0000 }, + { 0x93b0, 0x0000 }, + { 0x93b1, 0x0000 }, + { 0x93b2, 0x0000 }, + { 0x93b3, 0x0000 }, + { 0x93b4, 0x0000 }, + { 0x93b5, 0x0000 }, + { 0x93b6, 0x0000 }, + { 0x93b7, 0x0000 }, + { 0x93b8, 0x0000 }, + { 0x93b9, 0x0000 }, + { 0x93ba, 0x0000 }, + { 0x93bb, 0x0000 }, + { 0x93bc, 0x0000 }, + { 0x93bd, 0x0000 }, + { 0x93be, 0x0000 }, + { 0x93bf, 0x0000 }, + { 0x93c0, 0x0000 }, + { 0x93c1, 0x0000 }, + { 0x93c2, 0x0000 }, + { 0x93c3, 0x0000 }, + { 0x93c4, 0x0000 }, + { 0x93c5, 0x0000 }, + { 0x93c6, 0x0000 }, + { 0x93c7, 0x0000 }, + { 0x93c8, 0x0000 }, + { 0x93c9, 0x0000 }, + { 0x93ca, 0x0000 }, + { 0x93cb, 0x0000 }, + { 0x93cc, 0x0000 }, + { 0x93cd, 0x0000 }, + { 0x93ce, 0x0000 }, + { 0x93cf, 0x0000 }, + { 0x93d0, 0x0000 }, + { 0x93d1, 0x0000 }, + { 0x93d2, 0x0000 }, + { 0x93d3, 0x0000 }, + { 0x93d4, 0x0000 }, + { 0x93d5, 0x0000 }, + { 0x93d6, 0x0000 }, + { 0x93d7, 0x0000 }, + { 0x93d8, 0x0000 }, + { 0x93d9, 0x0000 }, + { 0x93da, 0x0000 }, + { 0x93db, 0x0000 }, + { 0x93dc, 0x0000 }, + { 0x93dd, 0x0000 }, + { 0x93de, 0x0000 }, + { 0x93df, 0x0000 }, + { 0x93e0, 0x0000 }, + { 0x93e1, 0x0000 }, + { 0x93e2, 0x0000 }, + { 0x93e3, 0x0000 }, + { 0x93e4, 0x0000 }, + { 0x93e5, 0x0000 }, + { 0x93e6, 0x0000 }, + { 0x93e7, 0x0000 }, + { 0x93e8, 0x0000 }, + { 0x93e9, 0x0000 }, + { 0x93ea, 0x0000 }, + { 0x93eb, 0x0000 }, + { 0x93ec, 0x0000 }, + { 0x93ed, 0x0000 }, + { 0x93ee, 0x0000 }, + { 0x93ef, 0x0000 }, + { 0x93f0, 0x0000 }, + { 0x93f1, 0x0000 }, + { 0x93f2, 0x0000 }, + { 0x93f3, 0x0000 }, + { 0x93f4, 0x0000 }, + { 0x93f5, 0x0000 }, + { 0x93f6, 0x0000 }, + { 0x93f7, 0x0000 }, + { 0x93f8, 0x0000 }, + { 0x93f9, 0x0000 }, + { 0x93fa, 0x0000 }, + { 0x93fb, 0x0000 }, + { 0x93fc, 0x0000 }, + { 0x93fd, 0x0000 }, + { 0x93fe, 0x0000 }, + { 0x93ff, 0x0000 }, + { 0x9400, 0x0000 }, + { 0x9401, 0x0000 }, + { 0x9402, 0x0000 }, + { 0x9403, 0x0000 }, + { 0x9404, 0x0000 }, + { 0x9405, 0x0000 }, + { 0x9406, 0x0000 }, + { 0x9407, 0x0000 }, + { 0x9408, 0x0000 }, + { 0x9409, 0x0000 }, + { 0x940a, 0x0000 }, + { 0x940b, 0x0000 }, + { 0x940c, 0x0000 }, + { 0x940d, 0x0000 }, + { 0x940e, 0x0000 }, + { 0x940f, 0x0000 }, + { 0x9410, 0x0000 }, + { 0x9411, 0x0000 }, + { 0x9412, 0x0000 }, + { 0x9413, 0x0000 }, + { 0x9414, 0x0000 }, + { 0x9415, 0x0000 }, + { 0x9416, 0x0000 }, + { 0x9417, 0x0000 }, + { 0x9418, 0x0000 }, + { 0x9419, 0x0000 }, + { 0x941a, 0x0000 }, + { 0x941b, 0x0000 }, + { 0x941c, 0x0000 }, + { 0x941d, 0x0000 }, + { 0x941e, 0x0000 }, + { 0x941f, 0x0000 }, + { 0x9420, 0x0000 }, + { 0x9421, 0x0000 }, + { 0x9422, 0x0000 }, + { 0x9423, 0x0000 }, + { 0x9424, 0x0000 }, + { 0x9425, 0x0000 }, + { 0x9426, 0x0000 }, + { 0x9427, 0x0000 }, + { 0x9428, 0x0000 }, + { 0x9429, 0x0000 }, + { 0x942a, 0x0000 }, + { 0x942b, 0x0000 }, + { 0x942c, 0x0000 }, + { 0x942d, 0x0000 }, + { 0x942e, 0x0000 }, + { 0x942f, 0x0000 }, + { 0x9430, 0x0000 }, + { 0x9431, 0x0000 }, + { 0x9432, 0x0000 }, + { 0x9433, 0x0000 }, + { 0x9434, 0x0000 }, + { 0x9435, 0x0000 }, + { 0x9436, 0x0000 }, + { 0x9437, 0x0000 }, + { 0x9438, 0x0000 }, + { 0x9439, 0x0000 }, + { 0x943a, 0x0000 }, + { 0x943b, 0x0000 }, + { 0x943c, 0x0000 }, + { 0x943d, 0x0000 }, + { 0x943e, 0x0000 }, + { 0x943f, 0x0000 }, + { 0x9440, 0x0000 }, + { 0x9441, 0x0000 }, + { 0x9442, 0x0000 }, + { 0x9443, 0x0000 }, + { 0x9444, 0x0000 }, + { 0x9445, 0x0000 }, + { 0x9446, 0x0000 }, + { 0x9447, 0x0000 }, + { 0x9448, 0x0000 }, + { 0x9449, 0x0000 }, + { 0x944a, 0x0000 }, + { 0x944b, 0x0000 }, + { 0x944c, 0x0000 }, + { 0x944d, 0x0000 }, + { 0x944e, 0x0000 }, + { 0x944f, 0x0000 }, + { 0x9450, 0x0000 }, + { 0x9451, 0x0000 }, + { 0x9452, 0x0000 }, + { 0x9453, 0x0000 }, + { 0x9454, 0x0000 }, + { 0x9455, 0x0000 }, + { 0x9456, 0x0000 }, + { 0x9457, 0x0000 }, + { 0x9458, 0x0000 }, + { 0x9459, 0x0000 }, + { 0x945a, 0x0000 }, + { 0x945b, 0x0000 }, + { 0x945c, 0x0000 }, + { 0x945d, 0x0000 }, + { 0x945e, 0x0000 }, + { 0x945f, 0x0000 }, + { 0x9460, 0x0000 }, + { 0x9461, 0x0000 }, + { 0x9462, 0x0000 }, + { 0x9463, 0x0000 }, + { 0x9464, 0x0000 }, + { 0x9465, 0x0000 }, + { 0x9466, 0x0000 }, + { 0x9467, 0x0000 }, + { 0x9468, 0x0000 }, + { 0x9469, 0x0000 }, + { 0x946a, 0x0000 }, + { 0x946b, 0x0000 }, + { 0x946c, 0x0000 }, + { 0x946d, 0x0000 }, + { 0x946e, 0x0000 }, + { 0x946f, 0x0000 }, + { 0x9470, 0x0000 }, + { 0x9471, 0x0000 }, + { 0x9472, 0x0000 }, + { 0x9473, 0x0000 }, + { 0x9474, 0x0000 }, + { 0x9475, 0x0000 }, + { 0x9476, 0x0000 }, + { 0x9477, 0x0000 }, + { 0x9478, 0x0000 }, + { 0x9479, 0x0000 }, + { 0x947a, 0x0000 }, + { 0x947b, 0x0000 }, + { 0x947c, 0x0000 }, + { 0x947d, 0x0000 }, + { 0x947e, 0x0000 }, + { 0x947f, 0x0000 }, + { 0x9480, 0x0000 }, + { 0x9481, 0x0000 }, + { 0x9482, 0x0000 }, + { 0x9483, 0x0000 }, + { 0x9484, 0x0000 }, + { 0x9485, 0x0000 }, + { 0x9486, 0x0000 }, + { 0x9487, 0x0000 }, + { 0x9488, 0x0000 }, + { 0x9489, 0x0000 }, + { 0x948a, 0x0000 }, + { 0x948b, 0x0000 }, + { 0x948c, 0x0000 }, + { 0x948d, 0x0000 }, + { 0x948e, 0x0000 }, + { 0x948f, 0x0000 }, + { 0x9490, 0x0000 }, + { 0x9491, 0x0000 }, + { 0x9492, 0x0000 }, + { 0x9493, 0x0000 }, + { 0x9494, 0x0000 }, + { 0x9495, 0x0000 }, + { 0x9496, 0x0000 }, + { 0x9497, 0x0000 }, + { 0x9498, 0x0000 }, + { 0x9499, 0x0000 }, + { 0x949a, 0x0000 }, + { 0x949b, 0x0000 }, + { 0x949c, 0x0000 }, + { 0x949d, 0x0000 }, + { 0x949e, 0x0000 }, + { 0x949f, 0x0000 }, + { 0x94a0, 0x0000 }, + { 0x94a1, 0x0000 }, + { 0x94a2, 0x0000 }, + { 0x94a3, 0x0000 }, + { 0x94a4, 0x0000 }, + { 0x94a5, 0x0000 }, + { 0x94a6, 0x0000 }, + { 0x94a7, 0x0000 }, + { 0x94a8, 0x0000 }, + { 0x94a9, 0x0000 }, + { 0x94aa, 0x0000 }, + { 0x94ab, 0x0000 }, + { 0x94ac, 0x0000 }, + { 0x94ad, 0x0000 }, + { 0x94ae, 0x0000 }, + { 0x94af, 0x0000 }, + { 0x94b0, 0x0000 }, + { 0x94b1, 0x0000 }, + { 0x94b2, 0x0000 }, + { 0x94b3, 0x0000 }, + { 0x94b4, 0x0000 }, + { 0x94b5, 0x0000 }, + { 0x94b6, 0x0000 }, + { 0x94b7, 0x0000 }, + { 0x94b8, 0x0000 }, + { 0x94b9, 0x0000 }, + { 0x94ba, 0x0000 }, + { 0x94bb, 0x0000 }, + { 0x94bc, 0x0000 }, + { 0x94bd, 0x0000 }, + { 0x94be, 0x0000 }, + { 0x94bf, 0x0000 }, + { 0x94c0, 0x0000 }, + { 0x94c1, 0x0000 }, + { 0x94c2, 0x0000 }, + { 0x94c3, 0x0000 }, + { 0x94c4, 0x0000 }, + { 0x94c5, 0x0000 }, + { 0x94c6, 0x0000 }, + { 0x94c7, 0x0000 }, + { 0x94c8, 0x0000 }, + { 0x94c9, 0x0000 }, + { 0x94ca, 0x0000 }, + { 0x94cb, 0x0000 }, + { 0x94cc, 0x0000 }, + { 0x94cd, 0x0000 }, + { 0x94ce, 0x0000 }, + { 0x94cf, 0x0000 }, + { 0x94d0, 0x0000 }, + { 0x94d1, 0x0000 }, + { 0x94d2, 0x0000 }, + { 0x94d3, 0x0000 }, + { 0x94d4, 0x0000 }, + { 0x94d5, 0x0000 }, + { 0x94d6, 0x0000 }, + { 0x94d7, 0x0000 }, + { 0x94d8, 0x0000 }, + { 0x94d9, 0x0000 }, + { 0x94da, 0x0000 }, + { 0x94db, 0x0000 }, + { 0x94dc, 0x0000 }, + { 0x94dd, 0x0000 }, + { 0x94de, 0x0000 }, + { 0x94df, 0x0000 }, + { 0x94e0, 0x0000 }, + { 0x94e1, 0x0000 }, + { 0x94e2, 0x0000 }, + { 0x94e3, 0x0000 }, + { 0x94e4, 0x0000 }, + { 0x94e5, 0x0000 }, + { 0x94e6, 0x0000 }, + { 0x94e7, 0x0000 }, + { 0x94e8, 0x0000 }, + { 0x94e9, 0x0000 }, + { 0x94ea, 0x0000 }, + { 0x94eb, 0x0000 }, + { 0x94ec, 0x0000 }, + { 0x94ed, 0x0000 }, + { 0x94ee, 0x0000 }, + { 0x94ef, 0x0000 }, + { 0x94f0, 0x0000 }, + { 0x94f1, 0x0000 }, + { 0x94f2, 0x0000 }, + { 0x94f3, 0x0000 }, + { 0x94f4, 0x0000 }, + { 0x94f5, 0x0000 }, + { 0x94f6, 0x0000 }, + { 0x94f7, 0x0000 }, + { 0x94f8, 0x0000 }, + { 0x94f9, 0x0000 }, + { 0x94fa, 0x0000 }, + { 0x94fb, 0x0000 }, + { 0x94fc, 0x0000 }, + { 0x94fd, 0x0000 }, + { 0x94fe, 0x0000 }, + { 0x94ff, 0x0000 }, + { 0x9500, 0x0000 }, + { 0x9501, 0x0000 }, + { 0x9502, 0x0000 }, + { 0x9503, 0x0000 }, + { 0x9504, 0x0000 }, + { 0x9505, 0x0000 }, + { 0x9506, 0x0000 }, + { 0x9507, 0x0000 }, + { 0x9508, 0x0000 }, + { 0x9509, 0x0000 }, + { 0x950a, 0x0000 }, + { 0x950b, 0x0000 }, + { 0x950c, 0x0000 }, + { 0x950d, 0x0000 }, + { 0x950e, 0x0000 }, + { 0x950f, 0x0000 }, + { 0x9510, 0x0000 }, + { 0x9511, 0x0000 }, + { 0x9512, 0x0000 }, + { 0x9513, 0x0000 }, + { 0x9514, 0x0000 }, + { 0x9515, 0x0000 }, + { 0x9516, 0x0000 }, + { 0x9517, 0x0000 }, + { 0x9518, 0x0000 }, + { 0x9519, 0x0000 }, + { 0x951a, 0x0000 }, + { 0x951b, 0x0000 }, + { 0x951c, 0x0000 }, + { 0x951d, 0x0000 }, + { 0x951e, 0x0000 }, + { 0x951f, 0x0000 }, + { 0x9520, 0x0000 }, + { 0x9521, 0x0000 }, + { 0x9522, 0x0000 }, + { 0x9523, 0x0000 }, + { 0x9524, 0x0000 }, + { 0x9525, 0x0000 }, + { 0x9526, 0x0000 }, + { 0x9527, 0x0000 }, + { 0x9528, 0x0000 }, + { 0x9529, 0x0000 }, + { 0x952a, 0x0000 }, + { 0x952b, 0x0000 }, + { 0x952c, 0x0000 }, + { 0x952d, 0x0000 }, + { 0x952e, 0x0000 }, + { 0x952f, 0x0000 }, + { 0x9530, 0x0000 }, + { 0x9531, 0x0000 }, + { 0x9532, 0x0000 }, + { 0x9533, 0x0000 }, + { 0x9534, 0x0000 }, + { 0x9535, 0x0000 }, + { 0x9536, 0x0000 }, + { 0x9537, 0x0000 }, + { 0x9538, 0x0000 }, + { 0x9539, 0x0000 }, + { 0x953a, 0x0000 }, + { 0x953b, 0x0000 }, + { 0x953c, 0x0000 }, + { 0x953d, 0x0000 }, + { 0x953e, 0x0000 }, + { 0x953f, 0x0000 }, + { 0x9540, 0x0000 }, + { 0x9541, 0x0000 }, + { 0x9542, 0x0000 }, + { 0x9543, 0x0000 }, + { 0x9544, 0x0000 }, + { 0x9545, 0x0000 }, + { 0x9546, 0x0000 }, + { 0x9547, 0x0000 }, + { 0x9548, 0x0000 }, + { 0x9549, 0x0000 }, + { 0x954a, 0x0000 }, + { 0x954b, 0x0000 }, + { 0x954c, 0x0000 }, + { 0x954d, 0x0000 }, + { 0x954e, 0x0000 }, + { 0x954f, 0x0000 }, + { 0x9550, 0x0000 }, + { 0x9551, 0x0000 }, + { 0x9552, 0x0000 }, + { 0x9553, 0x0000 }, + { 0x9554, 0x0000 }, + { 0x9555, 0x0000 }, + { 0x9556, 0x0000 }, + { 0x9557, 0x0000 }, + { 0x9558, 0x0000 }, + { 0x9559, 0x0000 }, + { 0x955a, 0x0000 }, + { 0x955b, 0x0000 }, + { 0x955c, 0x0000 }, + { 0x955d, 0x0000 }, + { 0x955e, 0x0000 }, + { 0x955f, 0x0000 }, + { 0x9560, 0x0000 }, + { 0x9561, 0x0000 }, + { 0x9562, 0x0000 }, + { 0x9563, 0x0000 }, + { 0x9564, 0x0000 }, + { 0x9565, 0x0000 }, + { 0x9566, 0x0000 }, + { 0x9567, 0x0000 }, + { 0x9568, 0x0000 }, + { 0x9569, 0x0000 }, + { 0x956a, 0x0000 }, + { 0x956b, 0x0000 }, + { 0x956c, 0x0000 }, + { 0x956d, 0x0000 }, + { 0x956e, 0x0000 }, + { 0x956f, 0x0000 }, + { 0x9570, 0x0000 }, + { 0x9571, 0x0000 }, + { 0x9572, 0x0000 }, + { 0x9573, 0x0000 }, + { 0x9574, 0x0000 }, + { 0x9575, 0x0000 }, + { 0x9576, 0x0000 }, + { 0x9577, 0x0000 }, + { 0x9578, 0x0000 }, + { 0x9579, 0x0000 }, + { 0x957a, 0x0000 }, + { 0x957b, 0x0000 }, + { 0x957c, 0x0000 }, + { 0x957d, 0x0000 }, + { 0x957e, 0x0000 }, + { 0x957f, 0x0000 }, + { 0x9580, 0x0000 }, + { 0x9581, 0x0000 }, + { 0x9582, 0x0000 }, + { 0x9583, 0x0000 }, + { 0x9584, 0x0000 }, + { 0x9585, 0x0000 }, + { 0x9586, 0x0000 }, + { 0x9587, 0x0000 }, + { 0x9588, 0x0000 }, + { 0x9589, 0x0000 }, + { 0x958a, 0x0000 }, + { 0x958b, 0x0000 }, + { 0x958c, 0x0000 }, + { 0x958d, 0x0000 }, + { 0x958e, 0x0000 }, + { 0x958f, 0x0000 }, + { 0x9590, 0x0000 }, + { 0x9591, 0x0000 }, + { 0x9592, 0x0000 }, + { 0x9593, 0x0000 }, + { 0x9594, 0x0000 }, + { 0x9595, 0x0000 }, + { 0x9596, 0x0000 }, + { 0x9597, 0x0000 }, + { 0x9598, 0x0000 }, + { 0x9599, 0x0000 }, + { 0x959a, 0x0000 }, + { 0x959b, 0x0000 }, + { 0x959c, 0x0000 }, + { 0x959d, 0x0000 }, + { 0x959e, 0x0000 }, + { 0x959f, 0x0000 }, + { 0x95a0, 0x0000 }, + { 0x95a1, 0x0000 }, + { 0x95a2, 0x0000 }, + { 0x95a3, 0x0000 }, + { 0x95a4, 0x0000 }, + { 0x95a5, 0x0000 }, + { 0x95a6, 0x0000 }, + { 0x95a7, 0x0000 }, + { 0x95a8, 0x0000 }, + { 0x95a9, 0x0000 }, + { 0x95aa, 0x0000 }, + { 0x95ab, 0x0000 }, + { 0x95ac, 0x0000 }, + { 0x95ad, 0x0000 }, + { 0x95ae, 0x0000 }, + { 0x95af, 0x0000 }, + { 0x95b0, 0x0000 }, + { 0x95b1, 0x0000 }, + { 0x95b2, 0x0000 }, + { 0x95b3, 0x0000 }, + { 0x95b4, 0x0000 }, + { 0x95b5, 0x0000 }, + { 0x95b6, 0x0000 }, + { 0x95b7, 0x0000 }, + { 0x95b8, 0x0000 }, + { 0x95b9, 0x0000 }, + { 0x95ba, 0x0000 }, + { 0x95bb, 0x0000 }, + { 0x95bc, 0x0000 }, + { 0x95bd, 0x0000 }, + { 0x95be, 0x0000 }, + { 0x95bf, 0x0000 }, + { 0x95c0, 0x0000 }, + { 0x95c1, 0x0000 }, + { 0x95c2, 0x0000 }, + { 0x95c3, 0x0000 }, + { 0x95c4, 0x0000 }, + { 0x95c5, 0x0000 }, + { 0x95c6, 0x0000 }, + { 0x95c7, 0x0000 }, + { 0x95c8, 0x0000 }, + { 0x95c9, 0x0000 }, + { 0x95ca, 0x0000 }, + { 0x95cb, 0x0000 }, + { 0x95cc, 0x0000 }, + { 0x95cd, 0x0000 }, + { 0x95ce, 0x0000 }, + { 0x95cf, 0x0000 }, + { 0x95d0, 0x0000 }, + { 0x95d1, 0x0000 }, + { 0x95d2, 0x0000 }, + { 0x95d3, 0x0000 }, + { 0x95d4, 0x0000 }, + { 0x95d5, 0x0000 }, + { 0x95d6, 0x0000 }, + { 0x95d7, 0x0000 }, + { 0x95d8, 0x0000 }, + { 0x95d9, 0x0000 }, + { 0x95da, 0x0000 }, + { 0x95db, 0x0000 }, + { 0x95dc, 0x0000 }, + { 0x95dd, 0x0000 }, + { 0x95de, 0x0000 }, + { 0x95df, 0x0000 }, + { 0x95e0, 0x0000 }, + { 0x95e1, 0x0000 }, + { 0x95e2, 0x0000 }, + { 0x95e3, 0x0000 }, + { 0x95e4, 0x0000 }, + { 0x95e5, 0x0000 }, + { 0x95e6, 0x0000 }, + { 0x95e7, 0x0000 }, + { 0x95e8, 0x0000 }, + { 0x95e9, 0x0000 }, + { 0x95ea, 0x0000 }, + { 0x95eb, 0x0000 }, + { 0x95ec, 0x0000 }, + { 0x95ed, 0x0000 }, + { 0x95ee, 0x0000 }, + { 0x95ef, 0x0000 }, + { 0x95f0, 0x0000 }, + { 0x95f1, 0x0000 }, + { 0x95f2, 0x0000 }, + { 0x95f3, 0x0000 }, + { 0x95f4, 0x0000 }, + { 0x95f5, 0x0000 }, + { 0x95f6, 0x0000 }, + { 0x95f7, 0x0000 }, + { 0x95f8, 0x0000 }, + { 0x95f9, 0x0000 }, + { 0x95fa, 0x0000 }, + { 0x95fb, 0x0000 }, + { 0x95fc, 0x0000 }, + { 0x95fd, 0x0000 }, + { 0x95fe, 0x0000 }, + { 0x95ff, 0x0000 }, + { 0x9600, 0x0000 }, + { 0x9601, 0x0000 }, + { 0x9602, 0x0000 }, + { 0x9603, 0x0000 }, + { 0x9604, 0x0000 }, + { 0x9605, 0x0000 }, + { 0x9606, 0x0000 }, + { 0x9607, 0x0000 }, + { 0x9608, 0x0000 }, + { 0x9609, 0x0000 }, + { 0x960a, 0x0000 }, + { 0x960b, 0x0000 }, + { 0x960c, 0x0000 }, + { 0x960d, 0x0000 }, + { 0x960e, 0x0000 }, + { 0x960f, 0x0000 }, + { 0x9610, 0x0000 }, + { 0x9611, 0x0000 }, + { 0x9612, 0x0000 }, + { 0x9613, 0x0000 }, + { 0x9614, 0x0000 }, + { 0x9615, 0x0000 }, + { 0x9616, 0x0000 }, + { 0x9617, 0x0000 }, + { 0x9618, 0x0000 }, + { 0x9619, 0x0000 }, + { 0x961a, 0x0000 }, + { 0x961b, 0x0000 }, + { 0x961c, 0x0000 }, + { 0x961d, 0x0000 }, + { 0x961e, 0x0000 }, + { 0x961f, 0x0000 }, + { 0x9620, 0x0000 }, + { 0x9621, 0x0000 }, + { 0x9622, 0x0000 }, + { 0x9623, 0x0000 }, + { 0x9624, 0x0000 }, + { 0x9625, 0x0000 }, + { 0x9626, 0x0000 }, + { 0x9627, 0x0000 }, + { 0x9628, 0x0000 }, + { 0x9629, 0x0000 }, + { 0x962a, 0x0000 }, + { 0x962b, 0x0000 }, + { 0x962c, 0x0000 }, + { 0x962d, 0x0000 }, + { 0x962e, 0x0000 }, + { 0x962f, 0x0000 }, + { 0x9630, 0x0000 }, + { 0x9631, 0x0000 }, + { 0x9632, 0x0000 }, + { 0x9633, 0x0000 }, + { 0x9634, 0x0000 }, + { 0x9635, 0x0000 }, + { 0x9636, 0x0000 }, + { 0x9637, 0x0000 }, + { 0x9638, 0x0000 }, + { 0x9639, 0x0000 }, + { 0x963a, 0x0000 }, + { 0x963b, 0x0000 }, + { 0x963c, 0x0000 }, + { 0x963d, 0x0000 }, + { 0x963e, 0x0000 }, + { 0x963f, 0x0000 }, + { 0x9640, 0x0000 }, + { 0x9641, 0x0000 }, + { 0x9642, 0x0000 }, + { 0x9643, 0x0000 }, + { 0x9644, 0x0000 }, + { 0x9645, 0x0000 }, + { 0x9646, 0x0000 }, + { 0x9647, 0x0000 }, + { 0x9648, 0x0000 }, + { 0x9649, 0x0000 }, + { 0x964a, 0x0000 }, + { 0x964b, 0x0000 }, + { 0x964c, 0x0000 }, + { 0x964d, 0x0000 }, + { 0x964e, 0x0000 }, + { 0x964f, 0x0000 }, + { 0x9650, 0x0000 }, + { 0x9651, 0x0000 }, + { 0x9652, 0x0000 }, + { 0x9653, 0x0000 }, + { 0x9654, 0x0000 }, + { 0x9655, 0x0000 }, + { 0x9656, 0x0000 }, + { 0x9657, 0x0000 }, + { 0x9658, 0x0000 }, + { 0x9659, 0x0000 }, + { 0x965a, 0x0000 }, + { 0x965b, 0x0000 }, + { 0x965c, 0x0000 }, + { 0x965d, 0x0000 }, + { 0x965e, 0x0000 }, + { 0x965f, 0x0000 }, + { 0x9660, 0x0000 }, + { 0x9661, 0x0000 }, + { 0x9662, 0x0000 }, + { 0x9663, 0x0000 }, + { 0x9664, 0x0000 }, + { 0x9665, 0x0000 }, + { 0x9666, 0x0000 }, + { 0x9667, 0x0000 }, + { 0x9668, 0x0000 }, + { 0x9669, 0x0000 }, + { 0x966a, 0x0000 }, + { 0x966b, 0x0000 }, + { 0x966c, 0x0000 }, + { 0x966d, 0x0000 }, + { 0x966e, 0x0000 }, + { 0x966f, 0x0000 }, + { 0x9670, 0x0000 }, + { 0x9671, 0x0000 }, + { 0x9672, 0x0000 }, + { 0x9673, 0x0000 }, + { 0x9674, 0x0000 }, + { 0x9675, 0x0000 }, + { 0x9676, 0x0000 }, + { 0x9677, 0x0000 }, + { 0x9678, 0x0000 }, + { 0x9679, 0x0000 }, + { 0x967a, 0x0000 }, + { 0x967b, 0x0000 }, + { 0x967c, 0x0000 }, + { 0x967d, 0x0000 }, + { 0x967e, 0x0000 }, + { 0x967f, 0x0000 }, + { 0x9680, 0x0000 }, + { 0x9681, 0x0000 }, + { 0x9682, 0x0000 }, + { 0x9683, 0x0000 }, + { 0x9684, 0x0000 }, + { 0x9685, 0x0000 }, + { 0x9686, 0x0000 }, + { 0x9687, 0x0000 }, + { 0x9688, 0x0000 }, + { 0x9689, 0x0000 }, + { 0x968a, 0x0000 }, + { 0x968b, 0x0000 }, + { 0x968c, 0x0000 }, + { 0x968d, 0x0000 }, + { 0x968e, 0x0000 }, + { 0x968f, 0x0000 }, + { 0x9690, 0x0000 }, + { 0x9691, 0x0000 }, + { 0x9692, 0x0000 }, + { 0x9693, 0x0000 }, + { 0x9694, 0x0000 }, + { 0x9695, 0x0000 }, + { 0x9696, 0x0000 }, + { 0x9697, 0x0000 }, + { 0x9698, 0x0000 }, + { 0x9699, 0x0000 }, + { 0x969a, 0x0000 }, + { 0x969b, 0x0000 }, + { 0x969c, 0x0000 }, + { 0x969d, 0x0000 }, + { 0x969e, 0x0000 }, + { 0x969f, 0x0000 }, + { 0x96a0, 0x0000 }, + { 0x96a1, 0x0000 }, + { 0x96a2, 0x0000 }, + { 0x96a3, 0x0000 }, + { 0x96a4, 0x0000 }, + { 0x96a5, 0x0000 }, + { 0x96a6, 0x0000 }, + { 0x96a7, 0x0000 }, + { 0x96a8, 0x0000 }, + { 0x96a9, 0x0000 }, + { 0x96aa, 0x0000 }, + { 0x96ab, 0x0000 }, + { 0x96ac, 0x0000 }, + { 0x96ad, 0x0000 }, + { 0x96ae, 0x0000 }, + { 0x96af, 0x0000 }, + { 0x96b0, 0x0000 }, + { 0x96b1, 0x0000 }, + { 0x96b2, 0x0000 }, + { 0x96b3, 0x0000 }, + { 0x96b4, 0x0000 }, + { 0x96b5, 0x0000 }, + { 0x96b6, 0x0000 }, + { 0x96b7, 0x0000 }, + { 0x96b8, 0x0000 }, + { 0x96b9, 0x0000 }, + { 0x96ba, 0x0000 }, + { 0x96bb, 0x0000 }, + { 0x96bc, 0x0000 }, + { 0x96bd, 0x0000 }, + { 0x96be, 0x0000 }, + { 0x96bf, 0x0000 }, + { 0x96c0, 0x0000 }, + { 0x96c1, 0x0000 }, + { 0x96c2, 0x0000 }, + { 0x96c3, 0x0000 }, + { 0x96c4, 0x0000 }, + { 0x96c5, 0x0000 }, + { 0x96c6, 0x0000 }, + { 0x96c7, 0x0000 }, + { 0x96c8, 0x0000 }, + { 0x96c9, 0x0000 }, + { 0x96ca, 0x0000 }, + { 0x96cb, 0x0000 }, + { 0x96cc, 0x0000 }, + { 0x96cd, 0x0000 }, + { 0x96ce, 0x0000 }, + { 0x96cf, 0x0000 }, + { 0x96d0, 0x0000 }, + { 0x96d1, 0x0000 }, + { 0x96d2, 0x0000 }, + { 0x96d3, 0x0000 }, + { 0x96d4, 0x0000 }, + { 0x96d5, 0x0000 }, + { 0x96d6, 0x0000 }, + { 0x96d7, 0x0000 }, + { 0x96d8, 0x0000 }, + { 0x96d9, 0x0000 }, + { 0x96da, 0x0000 }, + { 0x96db, 0x0000 }, + { 0x96dc, 0x0000 }, + { 0x96dd, 0x0000 }, + { 0x96de, 0x0000 }, + { 0x96df, 0x0000 }, + { 0x96e0, 0x0000 }, + { 0x96e1, 0x0000 }, + { 0x96e2, 0x0000 }, + { 0x96e3, 0x0000 }, + { 0x96e4, 0x0000 }, + { 0x96e5, 0x0000 }, + { 0x96e6, 0x0000 }, + { 0x96e7, 0x0000 }, + { 0x96e8, 0x0000 }, + { 0x96e9, 0x0000 }, + { 0x96ea, 0x0000 }, + { 0x96eb, 0x0000 }, + { 0x96ec, 0x0000 }, + { 0x96ed, 0x0000 }, + { 0x96ee, 0x0000 }, + { 0x96ef, 0x0000 }, + { 0x96f0, 0x0000 }, + { 0x96f1, 0x0000 }, + { 0x96f2, 0x0000 }, + { 0x96f3, 0x0000 }, + { 0x96f4, 0x0000 }, + { 0x96f5, 0x0000 }, + { 0x96f6, 0x0000 }, + { 0x96f7, 0x0000 }, + { 0x96f8, 0x0000 }, + { 0x96f9, 0x0000 }, + { 0x96fa, 0x0000 }, + { 0x96fb, 0x0000 }, + { 0x96fc, 0x0000 }, + { 0x96fd, 0x0000 }, + { 0x96fe, 0x0000 }, + { 0x96ff, 0x0000 }, + { 0x9700, 0x0000 }, + { 0x9701, 0x0000 }, + { 0x9702, 0x0000 }, + { 0x9703, 0x0000 }, + { 0x9704, 0x0000 }, + { 0x9705, 0x0000 }, + { 0x9706, 0x0000 }, + { 0x9707, 0x0000 }, + { 0x9708, 0x0000 }, + { 0x9709, 0x0000 }, + { 0x970a, 0x0000 }, + { 0x970b, 0x0000 }, + { 0x970c, 0x0000 }, + { 0x970d, 0x0000 }, + { 0x970e, 0x0000 }, + { 0x970f, 0x0000 }, + { 0x9710, 0x0000 }, + { 0x9711, 0x0000 }, + { 0x9712, 0x0000 }, + { 0x9713, 0x0000 }, + { 0x9714, 0x0000 }, + { 0x9715, 0x0000 }, + { 0x9716, 0x0000 }, + { 0x9717, 0x0000 }, + { 0x9718, 0x0000 }, + { 0x9719, 0x0000 }, + { 0x971a, 0x0000 }, + { 0x971b, 0x0000 }, + { 0x971c, 0x0000 }, + { 0x971d, 0x0000 }, + { 0x971e, 0x0000 }, + { 0x971f, 0x0000 }, + { 0x9720, 0x0000 }, + { 0x9721, 0x0000 }, + { 0x9722, 0x0000 }, + { 0x9723, 0x0000 }, + { 0x9724, 0x0000 }, + { 0x9725, 0x0000 }, + { 0x9726, 0x0000 }, + { 0x9727, 0x0000 }, + { 0x9728, 0x0000 }, + { 0x9729, 0x0000 }, + { 0x972a, 0x0000 }, + { 0x972b, 0x0000 }, + { 0x972c, 0x0000 }, + { 0x972d, 0x0000 }, + { 0x972e, 0x0000 }, + { 0x972f, 0x0000 }, + { 0x9730, 0x0000 }, + { 0x9731, 0x0000 }, + { 0x9732, 0x0000 }, + { 0x9733, 0x0000 }, + { 0x9734, 0x0000 }, + { 0x9735, 0x0000 }, + { 0x9736, 0x0000 }, + { 0x9737, 0x0000 }, + { 0x9738, 0x0000 }, + { 0x9739, 0x0000 }, + { 0x973a, 0x0000 }, + { 0x973b, 0x0000 }, + { 0x973c, 0x0000 }, + { 0x973d, 0x0000 }, + { 0x973e, 0x0000 }, + { 0x973f, 0x0000 }, + { 0x9740, 0x0000 }, + { 0x9741, 0x0000 }, + { 0x9742, 0x0000 }, + { 0x9743, 0x0000 }, + { 0x9744, 0x0000 }, + { 0x9745, 0x0000 }, + { 0x9746, 0x0000 }, + { 0x9747, 0x0000 }, + { 0x9748, 0x0000 }, + { 0x9749, 0x0000 }, + { 0x974a, 0x0000 }, + { 0x974b, 0x0000 }, + { 0x974c, 0x0000 }, + { 0x974d, 0x0000 }, + { 0x974e, 0x0000 }, + { 0x974f, 0x0000 }, + { 0x9750, 0x0000 }, + { 0x9751, 0x0000 }, + { 0x9752, 0x0000 }, + { 0x9753, 0x0000 }, + { 0x9754, 0x0000 }, + { 0x9755, 0x0000 }, + { 0x9756, 0x0000 }, + { 0x9757, 0x0000 }, + { 0x9758, 0x0000 }, + { 0x9759, 0x0000 }, + { 0x975a, 0x0000 }, + { 0x975b, 0x0000 }, + { 0x975c, 0x0000 }, + { 0x975d, 0x0000 }, + { 0x975e, 0x0000 }, + { 0x975f, 0x0000 }, + { 0x9760, 0x0000 }, + { 0x9761, 0x0000 }, + { 0x9762, 0x0000 }, + { 0x9763, 0x0000 }, + { 0x9764, 0x0000 }, + { 0x9765, 0x0000 }, + { 0x9766, 0x0000 }, + { 0x9767, 0x0000 }, + { 0x9768, 0x0000 }, + { 0x9769, 0x0000 }, + { 0x976a, 0x0000 }, + { 0x976b, 0x0000 }, + { 0x976c, 0x0000 }, + { 0x976d, 0x0000 }, + { 0x976e, 0x0000 }, + { 0x976f, 0x0000 }, + { 0x9770, 0x0000 }, + { 0x9771, 0x0000 }, + { 0x9772, 0x0000 }, + { 0x9773, 0x0000 }, + { 0x9774, 0x0000 }, + { 0x9775, 0x0000 }, + { 0x9776, 0x0000 }, + { 0x9777, 0x0000 }, + { 0x9778, 0x0000 }, + { 0x9779, 0x0000 }, + { 0x977a, 0x0000 }, + { 0x977b, 0x0000 }, + { 0x977c, 0x0000 }, + { 0x977d, 0x0000 }, + { 0x977e, 0x0000 }, + { 0x977f, 0x0000 }, + { 0x9780, 0x0000 }, + { 0x9781, 0x0000 }, + { 0x9782, 0x0000 }, + { 0x9783, 0x0000 }, + { 0x9784, 0x0000 }, + { 0x9785, 0x0000 }, + { 0x9786, 0x0000 }, + { 0x9787, 0x0000 }, + { 0x9788, 0x0000 }, + { 0x9789, 0x0000 }, + { 0x978a, 0x0000 }, + { 0x978b, 0x0000 }, + { 0x978c, 0x0000 }, + { 0x978d, 0x0000 }, + { 0x978e, 0x0000 }, + { 0x978f, 0x0000 }, + { 0x9790, 0x0000 }, + { 0x9791, 0x0000 }, + { 0x9792, 0x0000 }, + { 0x9793, 0x0000 }, + { 0x9794, 0x0000 }, + { 0x9795, 0x0000 }, + { 0x9796, 0x0000 }, + { 0x9797, 0x0000 }, + { 0x9798, 0x0000 }, + { 0x9799, 0x0000 }, + { 0x979a, 0x0000 }, + { 0x979b, 0x0000 }, + { 0x979c, 0x0000 }, + { 0x979d, 0x0000 }, + { 0x979e, 0x0000 }, + { 0x979f, 0x0000 }, + { 0x97a0, 0x0000 }, + { 0x97a1, 0x0000 }, + { 0x97a2, 0x0000 }, + { 0x97a3, 0x0000 }, + { 0x97a4, 0x0000 }, + { 0x97a5, 0x0000 }, + { 0x97a6, 0x0000 }, + { 0x97a7, 0x0000 }, + { 0x97a8, 0x0000 }, + { 0x97a9, 0x0000 }, + { 0x97aa, 0x0000 }, + { 0x97ab, 0x0000 }, + { 0x97ac, 0x0000 }, + { 0x97ad, 0x0000 }, + { 0x97ae, 0x0000 }, + { 0x97af, 0x0000 }, + { 0x97b0, 0x0000 }, + { 0x97b1, 0x0000 }, + { 0x97b2, 0x0000 }, + { 0x97b3, 0x0000 }, + { 0x97b4, 0x0000 }, + { 0x97b5, 0x0000 }, + { 0x97b6, 0x0000 }, + { 0x97b7, 0x0000 }, + { 0x97b8, 0x0000 }, + { 0x97b9, 0x0000 }, + { 0x97ba, 0x0000 }, + { 0x97bb, 0x0000 }, + { 0x97bc, 0x0000 }, + { 0x97bd, 0x0000 }, + { 0x97be, 0x0000 }, + { 0x97bf, 0x0000 }, + { 0x97c0, 0x0000 }, + { 0x97c1, 0x0000 }, + { 0x97c2, 0x0000 }, + { 0x97c3, 0x0000 }, + { 0x97c4, 0x0000 }, + { 0x97c5, 0x0000 }, + { 0x97c6, 0x0000 }, + { 0x97c7, 0x0000 }, + { 0x97c8, 0x0000 }, + { 0x97c9, 0x0000 }, + { 0x97ca, 0x0000 }, + { 0x97cb, 0x0000 }, + { 0x97cc, 0x0000 }, + { 0x97cd, 0x0000 }, + { 0x97ce, 0x0000 }, + { 0x97cf, 0x0000 }, + { 0x97d0, 0x0000 }, + { 0x97d1, 0x0000 }, + { 0x97d2, 0x0000 }, + { 0x97d3, 0x0000 }, + { 0x97d4, 0x0000 }, + { 0x97d5, 0x0000 }, + { 0x97d6, 0x0000 }, + { 0x97d7, 0x0000 }, + { 0x97d8, 0x0000 }, + { 0x97d9, 0x0000 }, + { 0x97da, 0x0000 }, + { 0x97db, 0x0000 }, + { 0x97dc, 0x0000 }, + { 0x97dd, 0x0000 }, + { 0x97de, 0x0000 }, + { 0x97df, 0x0000 }, + { 0x97e0, 0x0000 }, + { 0x97e1, 0x0000 }, + { 0x97e2, 0x0000 }, + { 0x97e3, 0x0000 }, + { 0x97e4, 0x0000 }, + { 0x97e5, 0x0000 }, + { 0x97e6, 0x0000 }, + { 0x97e7, 0x0000 }, + { 0x97e8, 0x0000 }, + { 0x97e9, 0x0000 }, + { 0x97ea, 0x0000 }, + { 0x97eb, 0x0000 }, + { 0x97ec, 0x0000 }, + { 0x97ed, 0x0000 }, + { 0x97ee, 0x0000 }, + { 0x97ef, 0x0000 }, + { 0x97f0, 0x0000 }, + { 0x97f1, 0x0000 }, + { 0x97f2, 0x0000 }, + { 0x97f3, 0x0000 }, + { 0x97f4, 0x0000 }, + { 0x97f5, 0x0000 }, + { 0x97f6, 0x0000 }, + { 0x97f7, 0x0000 }, + { 0x97f8, 0x0000 }, + { 0x97f9, 0x0000 }, + { 0x97fa, 0x0000 }, + { 0x97fb, 0x0000 }, + { 0x97fc, 0x0000 }, + { 0x97fd, 0x0000 }, + { 0x97fe, 0x0000 }, + { 0x97ff, 0x0000 }, + { 0x9800, 0x0000 }, + { 0x9801, 0x0000 }, + { 0x9802, 0x0000 }, + { 0x9803, 0x0000 }, + { 0x9804, 0x0000 }, + { 0x9805, 0x0000 }, + { 0x9806, 0x0000 }, + { 0x9807, 0x0000 }, + { 0x9808, 0x0000 }, + { 0x9809, 0x0000 }, + { 0x980a, 0x0000 }, + { 0x980b, 0x0000 }, + { 0x980c, 0x0000 }, + { 0x980d, 0x0000 }, + { 0x980e, 0x0000 }, + { 0x980f, 0x0000 }, + { 0x9810, 0x0000 }, + { 0x9811, 0x0000 }, + { 0x9812, 0x0000 }, + { 0x9813, 0x0000 }, + { 0x9814, 0x0000 }, + { 0x9815, 0x0000 }, + { 0x9816, 0x0000 }, + { 0x9817, 0x0000 }, + { 0x9818, 0x0000 }, + { 0x9819, 0x0000 }, + { 0x981a, 0x0000 }, + { 0x981b, 0x0000 }, + { 0x981c, 0x0000 }, + { 0x981d, 0x0000 }, + { 0x981e, 0x0000 }, + { 0x981f, 0x0000 }, + { 0x9820, 0x0000 }, + { 0x9821, 0x0000 }, + { 0x9822, 0x0000 }, + { 0x9823, 0x0000 }, + { 0x9824, 0x0000 }, + { 0x9825, 0x0000 }, + { 0x9826, 0x0000 }, + { 0x9827, 0x0000 }, + { 0x9828, 0x0000 }, + { 0x9829, 0x0000 }, + { 0x982a, 0x0000 }, + { 0x982b, 0x0000 }, + { 0x982c, 0x0000 }, + { 0x982d, 0x0000 }, + { 0x982e, 0x0000 }, + { 0x982f, 0x0000 }, + { 0x9830, 0x0000 }, + { 0x9831, 0x0000 }, + { 0x9832, 0x0000 }, + { 0x9833, 0x0000 }, + { 0x9834, 0x0000 }, + { 0x9835, 0x0000 }, + { 0x9836, 0x0000 }, + { 0x9837, 0x0000 }, + { 0x9838, 0x0000 }, + { 0x9839, 0x0000 }, + { 0x983a, 0x0000 }, + { 0x983b, 0x0000 }, + { 0x983c, 0x0000 }, + { 0x983d, 0x0000 }, + { 0x983e, 0x0000 }, + { 0x983f, 0x0000 }, + { 0x9840, 0x0000 }, + { 0x9841, 0x0000 }, + { 0x9842, 0x0000 }, + { 0x9843, 0x0000 }, + { 0x9844, 0x0000 }, + { 0x9845, 0x0000 }, + { 0x9846, 0x0000 }, + { 0x9847, 0x0000 }, + { 0x9848, 0x0000 }, + { 0x9849, 0x0000 }, + { 0x984a, 0x0000 }, + { 0x984b, 0x0000 }, + { 0x984c, 0x0000 }, + { 0x984d, 0x0000 }, + { 0x984e, 0x0000 }, + { 0x984f, 0x0000 }, + { 0x9850, 0x0000 }, + { 0x9851, 0x0000 }, + { 0x9852, 0x0000 }, + { 0x9853, 0x0000 }, + { 0x9854, 0x0000 }, + { 0x9855, 0x0000 }, + { 0x9856, 0x0000 }, + { 0x9857, 0x0000 }, + { 0x9858, 0x0000 }, + { 0x9859, 0x0000 }, + { 0x985a, 0x0000 }, + { 0x985b, 0x0000 }, + { 0x985c, 0x0000 }, + { 0x985d, 0x0000 }, + { 0x985e, 0x0000 }, + { 0x985f, 0x0000 }, + { 0x9860, 0x0000 }, + { 0x9861, 0x0000 }, + { 0x9862, 0x0000 }, + { 0x9863, 0x0000 }, + { 0x9864, 0x0000 }, + { 0x9865, 0x0000 }, + { 0x9866, 0x0000 }, + { 0x9867, 0x0000 }, + { 0x9868, 0x0000 }, + { 0x9869, 0x0000 }, + { 0x986a, 0x0000 }, + { 0x986b, 0x0000 }, + { 0x986c, 0x0000 }, + { 0x986d, 0x0000 }, + { 0x986e, 0x0000 }, + { 0x986f, 0x0000 }, + { 0x9870, 0x0000 }, + { 0x9871, 0x0000 }, + { 0x9872, 0x0000 }, + { 0x9873, 0x0000 }, + { 0x9874, 0x0000 }, + { 0x9875, 0x0000 }, + { 0x9876, 0x0000 }, + { 0x9877, 0x0000 }, + { 0x9878, 0x0000 }, + { 0x9879, 0x0000 }, + { 0x987a, 0x0000 }, + { 0x987b, 0x0000 }, + { 0x987c, 0x0000 }, + { 0x987d, 0x0000 }, + { 0x987e, 0x0000 }, + { 0x987f, 0x0000 }, + { 0x9880, 0x0000 }, + { 0x9881, 0x0000 }, + { 0x9882, 0x0000 }, + { 0x9883, 0x0000 }, + { 0x9884, 0x0000 }, + { 0x9885, 0x0000 }, + { 0x9886, 0x0000 }, + { 0x9887, 0x0000 }, + { 0x9888, 0x0000 }, + { 0x9889, 0x0000 }, + { 0x988a, 0x0000 }, + { 0x988b, 0x0000 }, + { 0x988c, 0x0000 }, + { 0x988d, 0x0000 }, + { 0x988e, 0x0000 }, + { 0x988f, 0x0000 }, + { 0x9890, 0x0000 }, + { 0x9891, 0x0000 }, + { 0x9892, 0x0000 }, + { 0x9893, 0x0000 }, + { 0x9894, 0x0000 }, + { 0x9895, 0x0000 }, + { 0x9896, 0x0000 }, + { 0x9897, 0x0000 }, + { 0x9898, 0x0000 }, + { 0x9899, 0x0000 }, + { 0x989a, 0x0000 }, + { 0x989b, 0x0000 }, + { 0x989c, 0x0000 }, + { 0x989d, 0x0000 }, + { 0x989e, 0x0000 }, + { 0x989f, 0x0000 }, + { 0x98a0, 0x0000 }, + { 0x98a1, 0x0000 }, + { 0x98a2, 0x0000 }, + { 0x98a3, 0x0000 }, + { 0x98a4, 0x0000 }, + { 0x98a5, 0x0000 }, + { 0x98a6, 0x0000 }, + { 0x98a7, 0x0000 }, + { 0x98a8, 0x0000 }, + { 0x98a9, 0x0000 }, + { 0x98aa, 0x0000 }, + { 0x98ab, 0x0000 }, + { 0x98ac, 0x0000 }, + { 0x98ad, 0x0000 }, + { 0x98ae, 0x0000 }, + { 0x98af, 0x0000 }, + { 0x98b0, 0x0000 }, + { 0x98b1, 0x0000 }, + { 0x98b2, 0x0000 }, + { 0x98b3, 0x0000 }, + { 0x98b4, 0x0000 }, + { 0x98b5, 0x0000 }, + { 0x98b6, 0x0000 }, + { 0x98b7, 0x0000 }, + { 0x98b8, 0x0000 }, + { 0x98b9, 0x0000 }, + { 0x98ba, 0x0000 }, + { 0x98bb, 0x0000 }, + { 0x98bc, 0x0000 }, + { 0x98bd, 0x0000 }, + { 0x98be, 0x0000 }, + { 0x98bf, 0x0000 }, + { 0x98c0, 0x0000 }, + { 0x98c1, 0x0000 }, + { 0x98c2, 0x0000 }, + { 0x98c3, 0x0000 }, + { 0x98c4, 0x0000 }, + { 0x98c5, 0x0000 }, + { 0x98c6, 0x0000 }, + { 0x98c7, 0x0000 }, + { 0x98c8, 0x0000 }, + { 0x98c9, 0x0000 }, + { 0x98ca, 0x0000 }, + { 0x98cb, 0x0000 }, + { 0x98cc, 0x0000 }, + { 0x98cd, 0x0000 }, + { 0x98ce, 0x0000 }, + { 0x98cf, 0x0000 }, + { 0x98d0, 0x0000 }, + { 0x98d1, 0x0000 }, + { 0x98d2, 0x0000 }, + { 0x98d3, 0x0000 }, + { 0x98d4, 0x0000 }, + { 0x98d5, 0x0000 }, + { 0x98d6, 0x0000 }, + { 0x98d7, 0x0000 }, + { 0x98d8, 0x0000 }, + { 0x98d9, 0x0000 }, + { 0x98da, 0x0000 }, + { 0x98db, 0x0000 }, + { 0x98dc, 0x0000 }, + { 0x98dd, 0x0000 }, + { 0x98de, 0x0000 }, + { 0x98df, 0x0000 }, + { 0x98e0, 0x0000 }, + { 0x98e1, 0x0000 }, + { 0x98e2, 0x0000 }, + { 0x98e3, 0x0000 }, + { 0x98e4, 0x0000 }, + { 0x98e5, 0x0000 }, + { 0x98e6, 0x0000 }, + { 0x98e7, 0x0000 }, + { 0x98e8, 0x0000 }, + { 0x98e9, 0x0000 }, + { 0x98ea, 0x0000 }, + { 0x98eb, 0x0000 }, + { 0x98ec, 0x0000 }, + { 0x98ed, 0x0000 }, + { 0x98ee, 0x0000 }, + { 0x98ef, 0x0000 }, + { 0x98f0, 0x0000 }, + { 0x98f1, 0x0000 }, + { 0x98f2, 0x0000 }, + { 0x98f3, 0x0000 }, + { 0x98f4, 0x0000 }, + { 0x98f5, 0x0000 }, + { 0x98f6, 0x0000 }, + { 0x98f7, 0x0000 }, + { 0x98f8, 0x0000 }, + { 0x98f9, 0x0000 }, + { 0x98fa, 0x0000 }, + { 0x98fb, 0x0000 }, + { 0x98fc, 0x0000 }, + { 0x98fd, 0x0000 }, + { 0x98fe, 0x0000 }, + { 0x98ff, 0x0000 }, + { 0x9900, 0x0000 }, + { 0x9901, 0x0000 }, + { 0x9902, 0x0000 }, + { 0x9903, 0x0000 }, + { 0x9904, 0x0000 }, + { 0x9905, 0x0000 }, + { 0x9906, 0x0000 }, + { 0x9907, 0x0000 }, + { 0x9908, 0x0000 }, + { 0x9909, 0x0000 }, + { 0x990a, 0x0000 }, + { 0x990b, 0x0000 }, + { 0x990c, 0x0000 }, + { 0x990d, 0x0000 }, + { 0x990e, 0x0000 }, + { 0x990f, 0x0000 }, + { 0x9910, 0x0000 }, + { 0x9911, 0x0000 }, + { 0x9912, 0x0000 }, + { 0x9913, 0x0000 }, + { 0x9914, 0x0000 }, + { 0x9915, 0x0000 }, + { 0x9916, 0x0000 }, + { 0x9917, 0x0000 }, + { 0x9918, 0x0000 }, + { 0x9919, 0x0000 }, + { 0x991a, 0x0000 }, + { 0x991b, 0x0000 }, + { 0x991c, 0x0000 }, + { 0x991d, 0x0000 }, + { 0x991e, 0x0000 }, + { 0x991f, 0x0000 }, + { 0x9920, 0x0000 }, + { 0x9921, 0x0000 }, + { 0x9922, 0x0000 }, + { 0x9923, 0x0000 }, + { 0x9924, 0x0000 }, + { 0x9925, 0x0000 }, + { 0x9926, 0x0000 }, + { 0x9927, 0x0000 }, + { 0x9928, 0x0000 }, + { 0x9929, 0x0000 }, + { 0x992a, 0x0000 }, + { 0x992b, 0x0000 }, + { 0x992c, 0x0000 }, + { 0x992d, 0x0000 }, + { 0x992e, 0x0000 }, + { 0x992f, 0x0000 }, + { 0x9930, 0x0000 }, + { 0x9931, 0x0000 }, + { 0x9932, 0x0000 }, + { 0x9933, 0x0000 }, + { 0x9934, 0x0000 }, + { 0x9935, 0x0000 }, + { 0x9936, 0x0000 }, + { 0x9937, 0x0000 }, + { 0x9938, 0x0000 }, + { 0x9939, 0x0000 }, + { 0x993a, 0x0000 }, + { 0x993b, 0x0000 }, + { 0x993c, 0x0000 }, + { 0x993d, 0x0000 }, + { 0x993e, 0x0000 }, + { 0x993f, 0x0000 }, + { 0x9940, 0x0000 }, + { 0x9941, 0x0000 }, + { 0x9942, 0x0000 }, + { 0x9943, 0x0000 }, + { 0x9944, 0x0000 }, + { 0x9945, 0x0000 }, + { 0x9946, 0x0000 }, + { 0x9947, 0x0000 }, + { 0x9948, 0x0000 }, + { 0x9949, 0x0000 }, + { 0x994a, 0x0000 }, + { 0x994b, 0x0000 }, + { 0x994c, 0x0000 }, + { 0x994d, 0x0000 }, + { 0x994e, 0x0000 }, + { 0x994f, 0x0000 }, + { 0x9950, 0x0000 }, + { 0x9951, 0x0000 }, + { 0x9952, 0x0000 }, + { 0x9953, 0x0000 }, + { 0x9954, 0x0000 }, + { 0x9955, 0x0000 }, + { 0x9956, 0x0000 }, + { 0x9957, 0x0000 }, + { 0x9958, 0x0000 }, + { 0x9959, 0x0000 }, + { 0x995a, 0x0000 }, + { 0x995b, 0x0000 }, + { 0x995c, 0x0000 }, + { 0x995d, 0x0000 }, + { 0x995e, 0x0000 }, + { 0x995f, 0x0000 }, + { 0x9960, 0x0000 }, + { 0x9961, 0x0000 }, + { 0x9962, 0x0000 }, + { 0x9963, 0x0000 }, + { 0x9964, 0x0000 }, + { 0x9965, 0x0000 }, + { 0x9966, 0x0000 }, + { 0x9967, 0x0000 }, + { 0x9968, 0x0000 }, + { 0x9969, 0x0000 }, + { 0x996a, 0x0000 }, + { 0x996b, 0x0000 }, + { 0x996c, 0x0000 }, + { 0x996d, 0x0000 }, + { 0x996e, 0x0000 }, + { 0x996f, 0x0000 }, + { 0x9970, 0x0000 }, + { 0x9971, 0x0000 }, + { 0x9972, 0x0000 }, + { 0x9973, 0x0000 }, + { 0x9974, 0x0000 }, + { 0x9975, 0x0000 }, + { 0x9976, 0x0000 }, + { 0x9977, 0x0000 }, + { 0x9978, 0x0000 }, + { 0x9979, 0x0000 }, + { 0x997a, 0x0000 }, + { 0x997b, 0x0000 }, + { 0x997c, 0x0000 }, + { 0x997d, 0x0000 }, + { 0x997e, 0x0000 }, + { 0x997f, 0x0000 }, + { 0x9980, 0x0000 }, + { 0x9981, 0x0000 }, + { 0x9982, 0x0000 }, + { 0x9983, 0x0000 }, + { 0x9984, 0x0000 }, + { 0x9985, 0x0000 }, + { 0x9986, 0x0000 }, + { 0x9987, 0x0000 }, + { 0x9988, 0x0000 }, + { 0x9989, 0x0000 }, + { 0x998a, 0x0000 }, + { 0x998b, 0x0000 }, + { 0x998c, 0x0000 }, + { 0x998d, 0x0000 }, + { 0x998e, 0x0000 }, + { 0x998f, 0x0000 }, + { 0x9990, 0x0000 }, + { 0x9991, 0x0000 }, + { 0x9992, 0x0000 }, + { 0x9993, 0x0000 }, + { 0x9994, 0x0000 }, + { 0x9995, 0x0000 }, + { 0x9996, 0x0000 }, + { 0x9997, 0x0000 }, + { 0x9998, 0x0000 }, + { 0x9999, 0x0000 }, + { 0x999a, 0x0000 }, + { 0x999b, 0x0000 }, + { 0x999c, 0x0000 }, + { 0x999d, 0x0000 }, + { 0x999e, 0x0000 }, + { 0x999f, 0x0000 }, + { 0x99a0, 0x0000 }, + { 0x99a1, 0x0000 }, + { 0x99a2, 0x0000 }, + { 0x99a3, 0x0000 }, + { 0x99a4, 0x0000 }, + { 0x99a5, 0x0000 }, + { 0x99a6, 0x0000 }, + { 0x99a7, 0x0000 }, + { 0x99a8, 0x0000 }, + { 0x99a9, 0x0000 }, + { 0x99aa, 0x0000 }, + { 0x99ab, 0x0000 }, + { 0x99ac, 0x0000 }, + { 0x99ad, 0x0000 }, + { 0x99ae, 0x0000 }, + { 0x99af, 0x0000 }, + { 0x99b0, 0x0000 }, + { 0x99b1, 0x0000 }, + { 0x99b2, 0x0000 }, + { 0x99b3, 0x0000 }, + { 0x99b4, 0x0000 }, + { 0x99b5, 0x0000 }, + { 0x99b6, 0x0000 }, + { 0x99b7, 0x0000 }, + { 0x99b8, 0x0000 }, + { 0x99b9, 0x0000 }, + { 0x99ba, 0x0000 }, + { 0x99bb, 0x0000 }, + { 0x99bc, 0x0000 }, + { 0x99bd, 0x0000 }, + { 0x99be, 0x0000 }, + { 0x99bf, 0x0000 }, + { 0x99c0, 0x0000 }, + { 0x99c1, 0x0000 }, + { 0x99c2, 0x0000 }, + { 0x99c3, 0x0000 }, + { 0x99c4, 0x0000 }, + { 0x99c5, 0x0000 }, + { 0x99c6, 0x0000 }, + { 0x99c7, 0x0000 }, + { 0x99c8, 0x0000 }, + { 0x99c9, 0x0000 }, + { 0x99ca, 0x0000 }, + { 0x99cb, 0x0000 }, + { 0x99cc, 0x0000 }, + { 0x99cd, 0x0000 }, + { 0x99ce, 0x0000 }, + { 0x99cf, 0x0000 }, + { 0x99d0, 0x0000 }, + { 0x99d1, 0x0000 }, + { 0x99d2, 0x0000 }, + { 0x99d3, 0x0000 }, + { 0x99d4, 0x0000 }, + { 0x99d5, 0x0000 }, + { 0x99d6, 0x0000 }, + { 0x99d7, 0x0000 }, + { 0x99d8, 0x0000 }, + { 0x99d9, 0x0000 }, + { 0x99da, 0x0000 }, + { 0x99db, 0x0000 }, + { 0x99dc, 0x0000 }, + { 0x99dd, 0x0000 }, + { 0x99de, 0x0000 }, + { 0x99df, 0x0000 }, + { 0x99e0, 0x0000 }, + { 0x99e1, 0x0000 }, + { 0x99e2, 0x0000 }, + { 0x99e3, 0x0000 }, + { 0x99e4, 0x0000 }, + { 0x99e5, 0x0000 }, + { 0x99e6, 0x0000 }, + { 0x99e7, 0x0000 }, + { 0x99e8, 0x0000 }, + { 0x99e9, 0x0000 }, + { 0x99ea, 0x0000 }, + { 0x99eb, 0x0000 }, + { 0x99ec, 0x0000 }, + { 0x99ed, 0x0000 }, + { 0x99ee, 0x0000 }, + { 0x99ef, 0x0000 }, + { 0x99f0, 0x0000 }, + { 0x99f1, 0x0000 }, + { 0x99f2, 0x0000 }, + { 0x99f3, 0x0000 }, + { 0x99f4, 0x0000 }, + { 0x99f5, 0x0000 }, + { 0x99f6, 0x0000 }, + { 0x99f7, 0x0000 }, + { 0x99f8, 0x0000 }, + { 0x99f9, 0x0000 }, + { 0x99fa, 0x0000 }, + { 0x99fb, 0x0000 }, + { 0x99fc, 0x0000 }, + { 0x99fd, 0x0000 }, + { 0x99fe, 0x0000 }, + { 0x99ff, 0x0000 }, + { 0x9a00, 0x0000 }, + { 0x9a01, 0x0000 }, + { 0x9a02, 0x0000 }, + { 0x9a03, 0x0000 }, + { 0x9a04, 0x0000 }, + { 0x9a05, 0x0000 }, + { 0x9a06, 0x0000 }, + { 0x9a07, 0x0000 }, + { 0x9a08, 0x0000 }, + { 0x9a09, 0x0000 }, + { 0x9a0a, 0x0000 }, + { 0x9a0b, 0x0000 }, + { 0x9a0c, 0x0000 }, + { 0x9a0d, 0x0000 }, + { 0x9a0e, 0x0000 }, + { 0x9a0f, 0x0000 }, + { 0x9a10, 0x0000 }, + { 0x9a11, 0x0000 }, + { 0x9a12, 0x0000 }, + { 0x9a13, 0x0000 }, + { 0x9a14, 0x0000 }, + { 0x9a15, 0x0000 }, + { 0x9a16, 0x0000 }, + { 0x9a17, 0x0000 }, + { 0x9a18, 0x0000 }, + { 0x9a19, 0x0000 }, + { 0x9a1a, 0x0000 }, + { 0x9a1b, 0x0000 }, + { 0x9a1c, 0x0000 }, + { 0x9a1d, 0x0000 }, + { 0x9a1e, 0x0000 }, + { 0x9a1f, 0x0000 }, + { 0x9a20, 0x0000 }, + { 0x9a21, 0x0000 }, + { 0x9a22, 0x0000 }, + { 0x9a23, 0x0000 }, + { 0x9a24, 0x0000 }, + { 0x9a25, 0x0000 }, + { 0x9a26, 0x0000 }, + { 0x9a27, 0x0000 }, + { 0x9a28, 0x0000 }, + { 0x9a29, 0x0000 }, + { 0x9a2a, 0x0000 }, + { 0x9a2b, 0x0000 }, + { 0x9a2c, 0x0000 }, + { 0x9a2d, 0x0000 }, + { 0x9a2e, 0x0000 }, + { 0x9a2f, 0x0000 }, + { 0x9a30, 0x0000 }, + { 0x9a31, 0x0000 }, + { 0x9a32, 0x0000 }, + { 0x9a33, 0x0000 }, + { 0x9a34, 0x0000 }, + { 0x9a35, 0x0000 }, + { 0x9a36, 0x0000 }, + { 0x9a37, 0x0000 }, + { 0x9a38, 0x0000 }, + { 0x9a39, 0x0000 }, + { 0x9a3a, 0x0000 }, + { 0x9a3b, 0x0000 }, + { 0x9a3c, 0x0000 }, + { 0x9a3d, 0x0000 }, + { 0x9a3e, 0x0000 }, + { 0x9a3f, 0x0000 }, + { 0x9a40, 0x0000 }, + { 0x9a41, 0x0000 }, + { 0x9a42, 0x0000 }, + { 0x9a43, 0x0000 }, + { 0x9a44, 0x0000 }, + { 0x9a45, 0x0000 }, + { 0x9a46, 0x0000 }, + { 0x9a47, 0x0000 }, + { 0x9a48, 0x0000 }, + { 0x9a49, 0x0000 }, + { 0x9a4a, 0x0000 }, + { 0x9a4b, 0x0000 }, + { 0x9a4c, 0x0000 }, + { 0x9a4d, 0x0000 }, + { 0x9a4e, 0x0000 }, + { 0x9a4f, 0x0000 }, + { 0x9a50, 0x0000 }, + { 0x9a51, 0x0000 }, + { 0x9a52, 0x0000 }, + { 0x9a53, 0x0000 }, + { 0x9a54, 0x0000 }, + { 0x9a55, 0x0000 }, + { 0x9a56, 0x0000 }, + { 0x9a57, 0x0000 }, + { 0x9a58, 0x0000 }, + { 0x9a59, 0x0000 }, + { 0x9a5a, 0x0000 }, + { 0x9a5b, 0x0000 }, + { 0x9a5c, 0x0000 }, + { 0x9a5d, 0x0000 }, + { 0x9a5e, 0x0000 }, + { 0x9a5f, 0x0000 }, + { 0x9a60, 0x0000 }, + { 0x9a61, 0x0000 }, + { 0x9a62, 0x0000 }, + { 0x9a63, 0x0000 }, + { 0x9a64, 0x0000 }, + { 0x9a65, 0x0000 }, + { 0x9a66, 0x0000 }, + { 0x9a67, 0x0000 }, + { 0x9a68, 0x0000 }, + { 0x9a69, 0x0000 }, + { 0x9a6a, 0x0000 }, + { 0x9a6b, 0x0000 }, + { 0x9a6c, 0x0000 }, + { 0x9a6d, 0x0000 }, + { 0x9a6e, 0x0000 }, + { 0x9a6f, 0x0000 }, + { 0x9a70, 0x0000 }, + { 0x9a71, 0x0000 }, + { 0x9a72, 0x0000 }, + { 0x9a73, 0x0000 }, + { 0x9a74, 0x0000 }, + { 0x9a75, 0x0000 }, + { 0x9a76, 0x0000 }, + { 0x9a77, 0x0000 }, + { 0x9a78, 0x0000 }, + { 0x9a79, 0x0000 }, + { 0x9a7a, 0x0000 }, + { 0x9a7b, 0x0000 }, + { 0x9a7c, 0x0000 }, + { 0x9a7d, 0x0000 }, + { 0x9a7e, 0x0000 }, + { 0x9a7f, 0x0000 }, + { 0x9a80, 0x0000 }, + { 0x9a81, 0x0000 }, + { 0x9a82, 0x0000 }, + { 0x9a83, 0x0000 }, + { 0x9a84, 0x0000 }, + { 0x9a85, 0x0000 }, + { 0x9a86, 0x0000 }, + { 0x9a87, 0x0000 }, + { 0x9a88, 0x0000 }, + { 0x9a89, 0x0000 }, + { 0x9a8a, 0x0000 }, + { 0x9a8b, 0x0000 }, + { 0x9a8c, 0x0000 }, + { 0x9a8d, 0x0000 }, + { 0x9a8e, 0x0000 }, + { 0x9a8f, 0x0000 }, + { 0x9a90, 0x0000 }, + { 0x9a91, 0x0000 }, + { 0x9a92, 0x0000 }, + { 0x9a93, 0x0000 }, + { 0x9a94, 0x0000 }, + { 0x9a95, 0x0000 }, + { 0x9a96, 0x0000 }, + { 0x9a97, 0x0000 }, + { 0x9a98, 0x0000 }, + { 0x9a99, 0x0000 }, + { 0x9a9a, 0x0000 }, + { 0x9a9b, 0x0000 }, + { 0x9a9c, 0x0000 }, + { 0x9a9d, 0x0000 }, + { 0x9a9e, 0x0000 }, + { 0x9a9f, 0x0000 }, + { 0x9aa0, 0x0000 }, + { 0x9aa1, 0x0000 }, + { 0x9aa2, 0x0000 }, + { 0x9aa3, 0x0000 }, + { 0x9aa4, 0x0000 }, + { 0x9aa5, 0x0000 }, + { 0x9aa6, 0x0000 }, + { 0x9aa7, 0x0000 }, + { 0x9aa8, 0x0000 }, + { 0x9aa9, 0x0000 }, + { 0x9aaa, 0x0000 }, + { 0x9aab, 0x0000 }, + { 0x9aac, 0x0000 }, + { 0x9aad, 0x0000 }, + { 0x9aae, 0x0000 }, + { 0x9aaf, 0x0000 }, + { 0x9ab0, 0x0000 }, + { 0x9ab1, 0x0000 }, + { 0x9ab2, 0x0000 }, + { 0x9ab3, 0x0000 }, + { 0x9ab4, 0x0000 }, + { 0x9ab5, 0x0000 }, + { 0x9ab6, 0x0000 }, + { 0x9ab7, 0x0000 }, + { 0x9ab8, 0x0000 }, + { 0x9ab9, 0x0000 }, + { 0x9aba, 0x0000 }, + { 0x9abb, 0x0000 }, + { 0x9abc, 0x0000 }, + { 0x9abd, 0x0000 }, + { 0x9abe, 0x0000 }, + { 0x9abf, 0x0000 }, + { 0x9ac0, 0x0000 }, + { 0x9ac1, 0x0000 }, + { 0x9ac2, 0x0000 }, + { 0x9ac3, 0x0000 }, + { 0x9ac4, 0x0000 }, + { 0x9ac5, 0x0000 }, + { 0x9ac6, 0x0000 }, + { 0x9ac7, 0x0000 }, + { 0x9ac8, 0x0000 }, + { 0x9ac9, 0x0000 }, + { 0x9aca, 0x0000 }, + { 0x9acb, 0x0000 }, + { 0x9acc, 0x0000 }, + { 0x9acd, 0x0000 }, + { 0x9ace, 0x0000 }, + { 0x9acf, 0x0000 }, + { 0x9ad0, 0x0000 }, + { 0x9ad1, 0x0000 }, + { 0x9ad2, 0x0000 }, + { 0x9ad3, 0x0000 }, + { 0x9ad4, 0x0000 }, + { 0x9ad5, 0x0000 }, + { 0x9ad6, 0x0000 }, + { 0x9ad7, 0x0000 }, + { 0x9ad8, 0x0000 }, + { 0x9ad9, 0x0000 }, + { 0x9ada, 0x0000 }, + { 0x9adb, 0x0000 }, + { 0x9adc, 0x0000 }, + { 0x9add, 0x0000 }, + { 0x9ade, 0x0000 }, + { 0x9adf, 0x0000 }, + { 0x9ae0, 0x0000 }, + { 0x9ae1, 0x0000 }, + { 0x9ae2, 0x0000 }, + { 0x9ae3, 0x0000 }, + { 0x9ae4, 0x0000 }, + { 0x9ae5, 0x0000 }, + { 0x9ae6, 0x0000 }, + { 0x9ae7, 0x0000 }, + { 0x9ae8, 0x0000 }, + { 0x9ae9, 0x0000 }, + { 0x9aea, 0x0000 }, + { 0x9aeb, 0x0000 }, + { 0x9aec, 0x0000 }, + { 0x9aed, 0x0000 }, + { 0x9aee, 0x0000 }, + { 0x9aef, 0x0000 }, + { 0x9af0, 0x0000 }, + { 0x9af1, 0x0000 }, + { 0x9af2, 0x0000 }, + { 0x9af3, 0x0000 }, + { 0x9af4, 0x0000 }, + { 0x9af5, 0x0000 }, + { 0x9af6, 0x0000 }, + { 0x9af7, 0x0000 }, + { 0x9af8, 0x0000 }, + { 0x9af9, 0x0000 }, + { 0x9afa, 0x0000 }, + { 0x9afb, 0x0000 }, + { 0x9afc, 0x0000 }, + { 0x9afd, 0x0000 }, + { 0x9afe, 0x0000 }, + { 0x9aff, 0x0000 }, + { 0x9b00, 0x0000 }, + { 0x9b01, 0x0000 }, + { 0x9b02, 0x0000 }, + { 0x9b03, 0x0000 }, + { 0x9b04, 0x0000 }, + { 0x9b05, 0x0000 }, + { 0x9b06, 0x0000 }, + { 0x9b07, 0x0000 }, + { 0x9b08, 0x0000 }, + { 0x9b09, 0x0000 }, + { 0x9b0a, 0x0000 }, + { 0x9b0b, 0x0000 }, + { 0x9b0c, 0x0000 }, + { 0x9b0d, 0x0000 }, + { 0x9b0e, 0x0000 }, + { 0x9b0f, 0x0000 }, + { 0x9b10, 0x0000 }, + { 0x9b11, 0x0000 }, + { 0x9b12, 0x0000 }, + { 0x9b13, 0x0000 }, + { 0x9b14, 0x0000 }, + { 0x9b15, 0x0000 }, + { 0x9b16, 0x0000 }, + { 0x9b17, 0x0000 }, + { 0x9b18, 0x0000 }, + { 0x9b19, 0x0000 }, + { 0x9b1a, 0x0000 }, + { 0x9b1b, 0x0000 }, + { 0x9b1c, 0x0000 }, + { 0x9b1d, 0x0000 }, + { 0x9b1e, 0x0000 }, + { 0x9b1f, 0x0000 }, + { 0x9b20, 0x0000 }, + { 0x9b21, 0x0000 }, + { 0x9b22, 0x0000 }, + { 0x9b23, 0x0000 }, + { 0x9b24, 0x0000 }, + { 0x9b25, 0x0000 }, + { 0x9b26, 0x0000 }, + { 0x9b27, 0x0000 }, + { 0x9b28, 0x0000 }, + { 0x9b29, 0x0000 }, + { 0x9b2a, 0x0000 }, + { 0x9b2b, 0x0000 }, + { 0x9b2c, 0x0000 }, + { 0x9b2d, 0x0000 }, + { 0x9b2e, 0x0000 }, + { 0x9b2f, 0x0000 }, + { 0x9b30, 0x0000 }, + { 0x9b31, 0x0000 }, + { 0x9b32, 0x0000 }, + { 0x9b33, 0x0000 }, + { 0x9b34, 0x0000 }, + { 0x9b35, 0x0000 }, + { 0x9b36, 0x0000 }, + { 0x9b37, 0x0000 }, + { 0x9b38, 0x0000 }, + { 0x9b39, 0x0000 }, + { 0x9b3a, 0x0000 }, + { 0x9b3b, 0x0000 }, + { 0x9b3c, 0x0000 }, + { 0x9b3d, 0x0000 }, + { 0x9b3e, 0x0000 }, + { 0x9b3f, 0x0000 }, + { 0x9b40, 0x0000 }, + { 0x9b41, 0x0000 }, + { 0x9b42, 0x0000 }, + { 0x9b43, 0x0000 }, + { 0x9b44, 0x0000 }, + { 0x9b45, 0x0000 }, + { 0x9b46, 0x0000 }, + { 0x9b47, 0x0000 }, + { 0x9b48, 0x0000 }, + { 0x9b49, 0x0000 }, + { 0x9b4a, 0x0000 }, + { 0x9b4b, 0x0000 }, + { 0x9b4c, 0x0000 }, + { 0x9b4d, 0x0000 }, + { 0x9b4e, 0x0000 }, + { 0x9b4f, 0x0000 }, + { 0x9b50, 0x0000 }, + { 0x9b51, 0x0000 }, + { 0x9b52, 0x0000 }, + { 0x9b53, 0x0000 }, + { 0x9b54, 0x0000 }, + { 0x9b55, 0x0000 }, + { 0x9b56, 0x0000 }, + { 0x9b57, 0x0000 }, + { 0x9b58, 0x0000 }, + { 0x9b59, 0x0000 }, + { 0x9b5a, 0x0000 }, + { 0x9b5b, 0x0000 }, + { 0x9b5c, 0x0000 }, + { 0x9b5d, 0x0000 }, + { 0x9b5e, 0x0000 }, + { 0x9b5f, 0x0000 }, + { 0x9b60, 0x0000 }, + { 0x9b61, 0x0000 }, + { 0x9b62, 0x0000 }, + { 0x9b63, 0x0000 }, + { 0x9b64, 0x0000 }, + { 0x9b65, 0x0000 }, + { 0x9b66, 0x0000 }, + { 0x9b67, 0x0000 }, + { 0x9b68, 0x0000 }, + { 0x9b69, 0x0000 }, + { 0x9b6a, 0x0000 }, + { 0x9b6b, 0x0000 }, + { 0x9b6c, 0x0000 }, + { 0x9b6d, 0x0000 }, + { 0x9b6e, 0x0000 }, + { 0x9b6f, 0x0000 }, + { 0x9b70, 0x0000 }, + { 0x9b71, 0x0000 }, + { 0x9b72, 0x0000 }, + { 0x9b73, 0x0000 }, + { 0x9b74, 0x0000 }, + { 0x9b75, 0x0000 }, + { 0x9b76, 0x0000 }, + { 0x9b77, 0x0000 }, + { 0x9b78, 0x0000 }, + { 0x9b79, 0x0000 }, + { 0x9b7a, 0x0000 }, + { 0x9b7b, 0x0000 }, + { 0x9b7c, 0x0000 }, + { 0x9b7d, 0x0000 }, + { 0x9b7e, 0x0000 }, + { 0x9b7f, 0x0000 }, + { 0x9b80, 0x0000 }, + { 0x9b81, 0x0000 }, + { 0x9b82, 0x0000 }, + { 0x9b83, 0x0000 }, + { 0x9b84, 0x0000 }, + { 0x9b85, 0x0000 }, + { 0x9b86, 0x0000 }, + { 0x9b87, 0x0000 }, + { 0x9b88, 0x0000 }, + { 0x9b89, 0x0000 }, + { 0x9b8a, 0x0000 }, + { 0x9b8b, 0x0000 }, + { 0x9b8c, 0x0000 }, + { 0x9b8d, 0x0000 }, + { 0x9b8e, 0x0000 }, + { 0x9b8f, 0x0000 }, + { 0x9b90, 0x0000 }, + { 0x9b91, 0x0000 }, + { 0x9b92, 0x0000 }, + { 0x9b93, 0x0000 }, + { 0x9b94, 0x0000 }, + { 0x9b95, 0x0000 }, + { 0x9b96, 0x0000 }, + { 0x9b97, 0x0000 }, + { 0x9b98, 0x0000 }, + { 0x9b99, 0x0000 }, + { 0x9b9a, 0x0000 }, + { 0x9b9b, 0x0000 }, + { 0x9b9c, 0x0000 }, + { 0x9b9d, 0x0000 }, + { 0x9b9e, 0x0000 }, + { 0x9b9f, 0x0000 }, + { 0x9ba0, 0x0000 }, + { 0x9ba1, 0x0000 }, + { 0x9ba2, 0x0000 }, + { 0x9ba3, 0x0000 }, + { 0x9ba4, 0x0000 }, + { 0x9ba5, 0x0000 }, + { 0x9ba6, 0x0000 }, + { 0x9ba7, 0x0000 }, + { 0x9ba8, 0x0000 }, + { 0x9ba9, 0x0000 }, + { 0x9baa, 0x0000 }, + { 0x9bab, 0x0000 }, + { 0x9bac, 0x0000 }, + { 0x9bad, 0x0000 }, + { 0x9bae, 0x0000 }, + { 0x9baf, 0x0000 }, + { 0x9bb0, 0x0000 }, + { 0x9bb1, 0x0000 }, + { 0x9bb2, 0x0000 }, + { 0x9bb3, 0x0000 }, + { 0x9bb4, 0x0000 }, + { 0x9bb5, 0x0000 }, + { 0x9bb6, 0x0000 }, + { 0x9bb7, 0x0000 }, + { 0x9bb8, 0x0000 }, + { 0x9bb9, 0x0000 }, + { 0x9bba, 0x0000 }, + { 0x9bbb, 0x0000 }, + { 0x9bbc, 0x0000 }, + { 0x9bbd, 0x0000 }, + { 0x9bbe, 0x0000 }, + { 0x9bbf, 0x0000 }, + { 0x9bc0, 0x0000 }, + { 0x9bc1, 0x0000 }, + { 0x9bc2, 0x0000 }, + { 0x9bc3, 0x0000 }, + { 0x9bc4, 0x0000 }, + { 0x9bc5, 0x0000 }, + { 0x9bc6, 0x0000 }, + { 0x9bc7, 0x0000 }, + { 0x9bc8, 0x0000 }, + { 0x9bc9, 0x0000 }, + { 0x9bca, 0x0000 }, + { 0x9bcb, 0x0000 }, + { 0x9bcc, 0x0000 }, + { 0x9bcd, 0x0000 }, + { 0x9bce, 0x0000 }, + { 0x9bcf, 0x0000 }, + { 0x9bd0, 0x0000 }, + { 0x9bd1, 0x0000 }, + { 0x9bd2, 0x0000 }, + { 0x9bd3, 0x0000 }, + { 0x9bd4, 0x0000 }, + { 0x9bd5, 0x0000 }, + { 0x9bd6, 0x0000 }, + { 0x9bd7, 0x0000 }, + { 0x9bd8, 0x0000 }, + { 0x9bd9, 0x0000 }, + { 0x9bda, 0x0000 }, + { 0x9bdb, 0x0000 }, + { 0x9bdc, 0x0000 }, + { 0x9bdd, 0x0000 }, + { 0x9bde, 0x0000 }, + { 0x9bdf, 0x0000 }, + { 0x9be0, 0x0000 }, + { 0x9be1, 0x0000 }, + { 0x9be2, 0x0000 }, + { 0x9be3, 0x0000 }, + { 0x9be4, 0x0000 }, + { 0x9be5, 0x0000 }, + { 0x9be6, 0x0000 }, + { 0x9be7, 0x0000 }, + { 0x9be8, 0x0000 }, + { 0x9be9, 0x0000 }, + { 0x9bea, 0x0000 }, + { 0x9beb, 0x0000 }, + { 0x9bec, 0x0000 }, + { 0x9bed, 0x0000 }, + { 0x9bee, 0x0000 }, + { 0x9bef, 0x0000 }, + { 0x9bf0, 0x0000 }, + { 0x9bf1, 0x0000 }, + { 0x9bf2, 0x0000 }, + { 0x9bf3, 0x0000 }, + { 0x9bf4, 0x0000 }, + { 0x9bf5, 0x0000 }, + { 0x9bf6, 0x0000 }, + { 0x9bf7, 0x0000 }, + { 0x9bf8, 0x0000 }, + { 0x9bf9, 0x0000 }, + { 0x9bfa, 0x0000 }, + { 0x9bfb, 0x0000 }, + { 0x9bfc, 0x0000 }, + { 0x9bfd, 0x0000 }, + { 0x9bfe, 0x0000 }, + { 0x9bff, 0x0000 }, + { 0x9c00, 0x0000 }, + { 0x9c01, 0x0000 }, + { 0x9c02, 0x0000 }, + { 0x9c03, 0x0000 }, + { 0x9c04, 0x0000 }, + { 0x9c05, 0x0000 }, + { 0x9c06, 0x0000 }, + { 0x9c07, 0x0000 }, + { 0x9c08, 0x0000 }, + { 0x9c09, 0x0000 }, + { 0x9c0a, 0x0000 }, + { 0x9c0b, 0x0000 }, + { 0x9c0c, 0x0000 }, + { 0x9c0d, 0x0000 }, + { 0x9c0e, 0x0000 }, + { 0x9c0f, 0x0000 }, + { 0x9c10, 0x0000 }, + { 0x9c11, 0x0000 }, + { 0x9c12, 0x0000 }, + { 0x9c13, 0x0000 }, + { 0x9c14, 0x0000 }, + { 0x9c15, 0x0000 }, + { 0x9c16, 0x0000 }, + { 0x9c17, 0x0000 }, + { 0x9c18, 0x0000 }, + { 0x9c19, 0x0000 }, + { 0x9c1a, 0x0000 }, + { 0x9c1b, 0x0000 }, + { 0x9c1c, 0x0000 }, + { 0x9c1d, 0x0000 }, + { 0x9c1e, 0x0000 }, + { 0x9c1f, 0x0000 }, + { 0x9c20, 0x0000 }, + { 0x9c21, 0x0000 }, + { 0x9c22, 0x0000 }, + { 0x9c23, 0x0000 }, + { 0x9c24, 0x0000 }, + { 0x9c25, 0x0000 }, + { 0x9c26, 0x0000 }, + { 0x9c27, 0x0000 }, + { 0x9c28, 0x0000 }, + { 0x9c29, 0x0000 }, + { 0x9c2a, 0x0000 }, + { 0x9c2b, 0x0000 }, + { 0x9c2c, 0x0000 }, + { 0x9c2d, 0x0000 }, + { 0x9c2e, 0x0000 }, + { 0x9c2f, 0x0000 }, + { 0x9c30, 0x0000 }, + { 0x9c31, 0x0000 }, + { 0x9c32, 0x0000 }, + { 0x9c33, 0x0000 }, + { 0x9c34, 0x0000 }, + { 0x9c35, 0x0000 }, + { 0x9c36, 0x0000 }, + { 0x9c37, 0x0000 }, + { 0x9c38, 0x0000 }, + { 0x9c39, 0x0000 }, + { 0x9c3a, 0x0000 }, + { 0x9c3b, 0x0000 }, + { 0x9c3c, 0x0000 }, + { 0x9c3d, 0x0000 }, + { 0x9c3e, 0x0000 }, + { 0x9c3f, 0x0000 }, + { 0x9c40, 0x0000 }, + { 0x9c41, 0x0000 }, + { 0x9c42, 0x0000 }, + { 0x9c43, 0x0000 }, + { 0x9c44, 0x0000 }, + { 0x9c45, 0x0000 }, + { 0x9c46, 0x0000 }, + { 0x9c47, 0x0000 }, + { 0x9c48, 0x0000 }, + { 0x9c49, 0x0000 }, + { 0x9c4a, 0x0000 }, + { 0x9c4b, 0x0000 }, + { 0x9c4c, 0x0000 }, + { 0x9c4d, 0x0000 }, + { 0x9c4e, 0x0000 }, + { 0x9c4f, 0x0000 }, + { 0x9c50, 0x0000 }, + { 0x9c51, 0x0000 }, + { 0x9c52, 0x0000 }, + { 0x9c53, 0x0000 }, + { 0x9c54, 0x0000 }, + { 0x9c55, 0x0000 }, + { 0x9c56, 0x0000 }, + { 0x9c57, 0x0000 }, + { 0x9c58, 0x0000 }, + { 0x9c59, 0x0000 }, + { 0x9c5a, 0x0000 }, + { 0x9c5b, 0x0000 }, + { 0x9c5c, 0x0000 }, + { 0x9c5d, 0x0000 }, + { 0x9c5e, 0x0000 }, + { 0x9c5f, 0x0000 }, + { 0x9c60, 0x0000 }, + { 0x9c61, 0x0000 }, + { 0x9c62, 0x0000 }, + { 0x9c63, 0x0000 }, + { 0x9c64, 0x0000 }, + { 0x9c65, 0x0000 }, + { 0x9c66, 0x0000 }, + { 0x9c67, 0x0000 }, + { 0x9c68, 0x0000 }, + { 0x9c69, 0x0000 }, + { 0x9c6a, 0x0000 }, + { 0x9c6b, 0x0000 }, + { 0x9c6c, 0x0000 }, + { 0x9c6d, 0x0000 }, + { 0x9c6e, 0x0000 }, + { 0x9c6f, 0x0000 }, + { 0x9c70, 0x0000 }, + { 0x9c71, 0x0000 }, + { 0x9c72, 0x0000 }, + { 0x9c73, 0x0000 }, + { 0x9c74, 0x0000 }, + { 0x9c75, 0x0000 }, + { 0x9c76, 0x0000 }, + { 0x9c77, 0x0000 }, + { 0x9c78, 0x0000 }, + { 0x9c79, 0x0000 }, + { 0x9c7a, 0x0000 }, + { 0x9c7b, 0x0000 }, + { 0x9c7c, 0x0000 }, + { 0x9c7d, 0x0000 }, + { 0x9c7e, 0x0000 }, + { 0x9c7f, 0x0000 }, + { 0x9c80, 0x0000 }, + { 0x9c81, 0x0000 }, + { 0x9c82, 0x0000 }, + { 0x9c83, 0x0000 }, + { 0x9c84, 0x0000 }, + { 0x9c85, 0x0000 }, + { 0x9c86, 0x0000 }, + { 0x9c87, 0x0000 }, + { 0x9c88, 0x0000 }, + { 0x9c89, 0x0000 }, + { 0x9c8a, 0x0000 }, + { 0x9c8b, 0x0000 }, + { 0x9c8c, 0x0000 }, + { 0x9c8d, 0x0000 }, + { 0x9c8e, 0x0000 }, + { 0x9c8f, 0x0000 }, + { 0x9c90, 0x0000 }, + { 0x9c91, 0x0000 }, + { 0x9c92, 0x0000 }, + { 0x9c93, 0x0000 }, + { 0x9c94, 0x0000 }, + { 0x9c95, 0x0000 }, + { 0x9c96, 0x0000 }, + { 0x9c97, 0x0000 }, + { 0x9c98, 0x0000 }, + { 0x9c99, 0x0000 }, + { 0x9c9a, 0x0000 }, + { 0x9c9b, 0x0000 }, + { 0x9c9c, 0x0000 }, + { 0x9c9d, 0x0000 }, + { 0x9c9e, 0x0000 }, + { 0x9c9f, 0x0000 }, + { 0x9ca0, 0x0000 }, + { 0x9ca1, 0x0000 }, + { 0x9ca2, 0x0000 }, + { 0x9ca3, 0x0000 }, + { 0x9ca4, 0x0000 }, + { 0x9ca5, 0x0000 }, + { 0x9ca6, 0x0000 }, + { 0x9ca7, 0x0000 }, + { 0x9ca8, 0x0000 }, + { 0x9ca9, 0x0000 }, + { 0x9caa, 0x0000 }, + { 0x9cab, 0x0000 }, + { 0x9cac, 0x0000 }, + { 0x9cad, 0x0000 }, + { 0x9cae, 0x0000 }, + { 0x9caf, 0x0000 }, + { 0x9cb0, 0x0000 }, + { 0x9cb1, 0x0000 }, + { 0x9cb2, 0x0000 }, + { 0x9cb3, 0x0000 }, + { 0x9cb4, 0x0000 }, + { 0x9cb5, 0x0000 }, + { 0x9cb6, 0x0000 }, + { 0x9cb7, 0x0000 }, + { 0x9cb8, 0x0000 }, + { 0x9cb9, 0x0000 }, + { 0x9cba, 0x0000 }, + { 0x9cbb, 0x0000 }, + { 0x9cbc, 0x0000 }, + { 0x9cbd, 0x0000 }, + { 0x9cbe, 0x0000 }, + { 0x9cbf, 0x0000 }, + { 0x9cc0, 0x0000 }, + { 0x9cc1, 0x0000 }, + { 0x9cc2, 0x0000 }, + { 0x9cc3, 0x0000 }, + { 0x9cc4, 0x0000 }, + { 0x9cc5, 0x0000 }, + { 0x9cc6, 0x0000 }, + { 0x9cc7, 0x0000 }, + { 0x9cc8, 0x0000 }, + { 0x9cc9, 0x0000 }, + { 0x9cca, 0x0000 }, + { 0x9ccb, 0x0000 }, + { 0x9ccc, 0x0000 }, + { 0x9ccd, 0x0000 }, + { 0x9cce, 0x0000 }, + { 0x9ccf, 0x0000 }, + { 0x9cd0, 0x0000 }, + { 0x9cd1, 0x0000 }, + { 0x9cd2, 0x0000 }, + { 0x9cd3, 0x0000 }, + { 0x9cd4, 0x0000 }, + { 0x9cd5, 0x0000 }, + { 0x9cd6, 0x0000 }, + { 0x9cd7, 0x0000 }, + { 0x9cd8, 0x0000 }, + { 0x9cd9, 0x0000 }, + { 0x9cda, 0x0000 }, + { 0x9cdb, 0x0000 }, + { 0x9cdc, 0x0000 }, + { 0x9cdd, 0x0000 }, + { 0x9cde, 0x0000 }, + { 0x9cdf, 0x0000 }, + { 0x9ce0, 0x0000 }, + { 0x9ce1, 0x0000 }, + { 0x9ce2, 0x0000 }, + { 0x9ce3, 0x0000 }, + { 0x9ce4, 0x0000 }, + { 0x9ce5, 0x0000 }, + { 0x9ce6, 0x0000 }, + { 0x9ce7, 0x0000 }, + { 0x9ce8, 0x0000 }, + { 0x9ce9, 0x0000 }, + { 0x9cea, 0x0000 }, + { 0x9ceb, 0x0000 }, + { 0x9cec, 0x0000 }, + { 0x9ced, 0x0000 }, + { 0x9cee, 0x0000 }, + { 0x9cef, 0x0000 }, + { 0x9cf0, 0x0000 }, + { 0x9cf1, 0x0000 }, + { 0x9cf2, 0x0000 }, + { 0x9cf3, 0x0000 }, + { 0x9cf4, 0x0000 }, + { 0x9cf5, 0x0000 }, + { 0x9cf6, 0x0000 }, + { 0x9cf7, 0x0000 }, + { 0x9cf8, 0x0000 }, + { 0x9cf9, 0x0000 }, + { 0x9cfa, 0x0000 }, + { 0x9cfb, 0x0000 }, + { 0x9cfc, 0x0000 }, + { 0x9cfd, 0x0000 }, + { 0x9cfe, 0x0000 }, + { 0x9cff, 0x0000 }, + { 0x9d00, 0x0000 }, + { 0x9d01, 0x0000 }, + { 0x9d02, 0x0000 }, + { 0x9d03, 0x0000 }, + { 0x9d04, 0x0000 }, + { 0x9d05, 0x0000 }, + { 0x9d06, 0x0000 }, + { 0x9d07, 0x0000 }, + { 0x9d08, 0x0000 }, + { 0x9d09, 0x0000 }, + { 0x9d0a, 0x0000 }, + { 0x9d0b, 0x0000 }, + { 0x9d0c, 0x0000 }, + { 0x9d0d, 0x0000 }, + { 0x9d0e, 0x0000 }, + { 0x9d0f, 0x0000 }, + { 0x9d10, 0x0000 }, + { 0x9d11, 0x0000 }, + { 0x9d12, 0x0000 }, + { 0x9d13, 0x0000 }, + { 0x9d14, 0x0000 }, + { 0x9d15, 0x0000 }, + { 0x9d16, 0x0000 }, + { 0x9d17, 0x0000 }, + { 0x9d18, 0x0000 }, + { 0x9d19, 0x0000 }, + { 0x9d1a, 0x0000 }, + { 0x9d1b, 0x0000 }, + { 0x9d1c, 0x0000 }, + { 0x9d1d, 0x0000 }, + { 0x9d1e, 0x0000 }, + { 0x9d1f, 0x0000 }, + { 0x9d20, 0x0000 }, + { 0x9d21, 0x0000 }, + { 0x9d22, 0x0000 }, + { 0x9d23, 0x0000 }, + { 0x9d24, 0x0000 }, + { 0x9d25, 0x0000 }, + { 0x9d26, 0x0000 }, + { 0x9d27, 0x0000 }, + { 0x9d28, 0x0000 }, + { 0x9d29, 0x0000 }, + { 0x9d2a, 0x0000 }, + { 0x9d2b, 0x0000 }, + { 0x9d2c, 0x0000 }, + { 0x9d2d, 0x0000 }, + { 0x9d2e, 0x0000 }, + { 0x9d2f, 0x0000 }, + { 0x9d30, 0x0000 }, + { 0x9d31, 0x0000 }, + { 0x9d32, 0x0000 }, + { 0x9d33, 0x0000 }, + { 0x9d34, 0x0000 }, + { 0x9d35, 0x0000 }, + { 0x9d36, 0x0000 }, + { 0x9d37, 0x0000 }, + { 0x9d38, 0x0000 }, + { 0x9d39, 0x0000 }, + { 0x9d3a, 0x0000 }, + { 0x9d3b, 0x0000 }, + { 0x9d3c, 0x0000 }, + { 0x9d3d, 0x0000 }, + { 0x9d3e, 0x0000 }, + { 0x9d3f, 0x0000 }, + { 0x9d40, 0x0000 }, + { 0x9d41, 0x0000 }, + { 0x9d42, 0x0000 }, + { 0x9d43, 0x0000 }, + { 0x9d44, 0x0000 }, + { 0x9d45, 0x0000 }, + { 0x9d46, 0x0000 }, + { 0x9d47, 0x0000 }, + { 0x9d48, 0x0000 }, + { 0x9d49, 0x0000 }, + { 0x9d4a, 0x0000 }, + { 0x9d4b, 0x0000 }, + { 0x9d4c, 0x0000 }, + { 0x9d4d, 0x0000 }, + { 0x9d4e, 0x0000 }, + { 0x9d4f, 0x0000 }, + { 0x9d50, 0x0000 }, + { 0x9d51, 0x0000 }, + { 0x9d52, 0x0000 }, + { 0x9d53, 0x0000 }, + { 0x9d54, 0x0000 }, + { 0x9d55, 0x0000 }, + { 0x9d56, 0x0000 }, + { 0x9d57, 0x0000 }, + { 0x9d58, 0x0000 }, + { 0x9d59, 0x0000 }, + { 0x9d5a, 0x0000 }, + { 0x9d5b, 0x0000 }, + { 0x9d5c, 0x0000 }, + { 0x9d5d, 0x0000 }, + { 0x9d5e, 0x0000 }, + { 0x9d5f, 0x0000 }, + { 0x9d60, 0x0000 }, + { 0x9d61, 0x0000 }, + { 0x9d62, 0x0000 }, + { 0x9d63, 0x0000 }, + { 0x9d64, 0x0000 }, + { 0x9d65, 0x0000 }, + { 0x9d66, 0x0000 }, + { 0x9d67, 0x0000 }, + { 0x9d68, 0x0000 }, + { 0x9d69, 0x0000 }, + { 0x9d6a, 0x0000 }, + { 0x9d6b, 0x0000 }, + { 0x9d6c, 0x0000 }, + { 0x9d6d, 0x0000 }, + { 0x9d6e, 0x0000 }, + { 0x9d6f, 0x0000 }, + { 0x9d70, 0x0000 }, + { 0x9d71, 0x0000 }, + { 0x9d72, 0x0000 }, + { 0x9d73, 0x0000 }, + { 0x9d74, 0x0000 }, + { 0x9d75, 0x0000 }, + { 0x9d76, 0x0000 }, + { 0x9d77, 0x0000 }, + { 0x9d78, 0x0000 }, + { 0x9d79, 0x0000 }, + { 0x9d7a, 0x0000 }, + { 0x9d7b, 0x0000 }, + { 0x9d7c, 0x0000 }, + { 0x9d7d, 0x0000 }, + { 0x9d7e, 0x0000 }, + { 0x9d7f, 0x0000 }, + { 0x9d80, 0x0000 }, + { 0x9d81, 0x0000 }, + { 0x9d82, 0x0000 }, + { 0x9d83, 0x0000 }, + { 0x9d84, 0x0000 }, + { 0x9d85, 0x0000 }, + { 0x9d86, 0x0000 }, + { 0x9d87, 0x0000 }, + { 0x9d88, 0x0000 }, + { 0x9d89, 0x0000 }, + { 0x9d8a, 0x0000 }, + { 0x9d8b, 0x0000 }, + { 0x9d8c, 0x0000 }, + { 0x9d8d, 0x0000 }, + { 0x9d8e, 0x0000 }, + { 0x9d8f, 0x0000 }, + { 0x9d90, 0x0000 }, + { 0x9d91, 0x0000 }, + { 0x9d92, 0x0000 }, + { 0x9d93, 0x0000 }, + { 0x9d94, 0x0000 }, + { 0x9d95, 0x0000 }, + { 0x9d96, 0x0000 }, + { 0x9d97, 0x0000 }, + { 0x9d98, 0x0000 }, + { 0x9d99, 0x0000 }, + { 0x9d9a, 0x0000 }, + { 0x9d9b, 0x0000 }, + { 0x9d9c, 0x0000 }, + { 0x9d9d, 0x0000 }, + { 0x9d9e, 0x0000 }, + { 0x9d9f, 0x0000 }, + { 0x9da0, 0x0000 }, + { 0x9da1, 0x0000 }, + { 0x9da2, 0x0000 }, + { 0x9da3, 0x0000 }, + { 0x9da4, 0x0000 }, + { 0x9da5, 0x0000 }, + { 0x9da6, 0x0000 }, + { 0x9da7, 0x0000 }, + { 0x9da8, 0x0000 }, + { 0x9da9, 0x0000 }, + { 0x9daa, 0x0000 }, + { 0x9dab, 0x0000 }, + { 0x9dac, 0x0000 }, + { 0x9dad, 0x0000 }, + { 0x9dae, 0x0000 }, + { 0x9daf, 0x0000 }, + { 0x9db0, 0x0000 }, + { 0x9db1, 0x0000 }, + { 0x9db2, 0x0000 }, + { 0x9db3, 0x0000 }, + { 0x9db4, 0x0000 }, + { 0x9db5, 0x0000 }, + { 0x9db6, 0x0000 }, + { 0x9db7, 0x0000 }, + { 0x9db8, 0x0000 }, + { 0x9db9, 0x0000 }, + { 0x9dba, 0x0000 }, + { 0x9dbb, 0x0000 }, + { 0x9dbc, 0x0000 }, + { 0x9dbd, 0x0000 }, + { 0x9dbe, 0x0000 }, + { 0x9dbf, 0x0000 }, + { 0x9dc0, 0x0000 }, + { 0x9dc1, 0x0000 }, + { 0x9dc2, 0x0000 }, + { 0x9dc3, 0x0000 }, + { 0x9dc4, 0x0000 }, + { 0x9dc5, 0x0000 }, + { 0x9dc6, 0x0000 }, + { 0x9dc7, 0x0000 }, + { 0x9dc8, 0x0000 }, + { 0x9dc9, 0x0000 }, + { 0x9dca, 0x0000 }, + { 0x9dcb, 0x0000 }, + { 0x9dcc, 0x0000 }, + { 0x9dcd, 0x0000 }, + { 0x9dce, 0x0000 }, + { 0x9dcf, 0x0000 }, + { 0x9dd0, 0x0000 }, + { 0x9dd1, 0x0000 }, + { 0x9dd2, 0x0000 }, + { 0x9dd3, 0x0000 }, + { 0x9dd4, 0x0000 }, + { 0x9dd5, 0x0000 }, + { 0x9dd6, 0x0000 }, + { 0x9dd7, 0x0000 }, + { 0x9dd8, 0x0000 }, + { 0x9dd9, 0x0000 }, + { 0x9dda, 0x0000 }, + { 0x9ddb, 0x0000 }, + { 0x9ddc, 0x0000 }, + { 0x9ddd, 0x0000 }, + { 0x9dde, 0x0000 }, + { 0x9ddf, 0x0000 }, + { 0x9de0, 0x0000 }, + { 0x9de1, 0x0000 }, + { 0x9de2, 0x0000 }, + { 0x9de3, 0x0000 }, + { 0x9de4, 0x0000 }, + { 0x9de5, 0x0000 }, + { 0x9de6, 0x0000 }, + { 0x9de7, 0x0000 }, + { 0x9de8, 0x0000 }, + { 0x9de9, 0x0000 }, + { 0x9dea, 0x0000 }, + { 0x9deb, 0x0000 }, + { 0x9dec, 0x0000 }, + { 0x9ded, 0x0000 }, + { 0x9dee, 0x0000 }, + { 0x9def, 0x0000 }, + { 0x9df0, 0x0000 }, + { 0x9df1, 0x0000 }, + { 0x9df2, 0x0000 }, + { 0x9df3, 0x0000 }, + { 0x9df4, 0x0000 }, + { 0x9df5, 0x0000 }, + { 0x9df6, 0x0000 }, + { 0x9df7, 0x0000 }, + { 0x9df8, 0x0000 }, + { 0x9df9, 0x0000 }, + { 0x9dfa, 0x0000 }, + { 0x9dfb, 0x0000 }, + { 0x9dfc, 0x0000 }, + { 0x9dfd, 0x0000 }, + { 0x9dfe, 0x0000 }, + { 0x9dff, 0x0000 }, + { 0x9e00, 0x0000 }, + { 0x9e01, 0x0000 }, + { 0x9e02, 0x0000 }, + { 0x9e03, 0x0000 }, + { 0x9e04, 0x0000 }, + { 0x9e05, 0x0000 }, + { 0x9e06, 0x0000 }, + { 0x9e07, 0x0000 }, + { 0x9e08, 0x0000 }, + { 0x9e09, 0x0000 }, + { 0x9e0a, 0x0000 }, + { 0x9e0b, 0x0000 }, + { 0x9e0c, 0x0000 }, + { 0x9e0d, 0x0000 }, + { 0x9e0e, 0x0000 }, + { 0x9e0f, 0x0000 }, + { 0x9e10, 0x0000 }, + { 0x9e11, 0x0000 }, + { 0x9e12, 0x0000 }, + { 0x9e13, 0x0000 }, + { 0x9e14, 0x0000 }, + { 0x9e15, 0x0000 }, + { 0x9e16, 0x0000 }, + { 0x9e17, 0x0000 }, + { 0x9e18, 0x0000 }, + { 0x9e19, 0x0000 }, + { 0x9e1a, 0x0000 }, + { 0x9e1b, 0x0000 }, + { 0x9e1c, 0x0000 }, + { 0x9e1d, 0x0000 }, + { 0x9e1e, 0x0000 }, + { 0x9e1f, 0x0000 }, + { 0x9e20, 0x0000 }, + { 0x9e21, 0x0000 }, + { 0x9e22, 0x0000 }, + { 0x9e23, 0x0000 }, + { 0x9e24, 0x0000 }, + { 0x9e25, 0x0000 }, + { 0x9e26, 0x0000 }, + { 0x9e27, 0x0000 }, + { 0x9e28, 0x0000 }, + { 0x9e29, 0x0000 }, + { 0x9e2a, 0x0000 }, + { 0x9e2b, 0x0000 }, + { 0x9e2c, 0x0000 }, + { 0x9e2d, 0x0000 }, + { 0x9e2e, 0x0000 }, + { 0x9e2f, 0x0000 }, + { 0x9e30, 0x0000 }, + { 0x9e31, 0x0000 }, + { 0x9e32, 0x0000 }, + { 0x9e33, 0x0000 }, + { 0x9e34, 0x0000 }, + { 0x9e35, 0x0000 }, + { 0x9e36, 0x0000 }, + { 0x9e37, 0x0000 }, + { 0x9e38, 0x0000 }, + { 0x9e39, 0x0000 }, + { 0x9e3a, 0x0000 }, + { 0x9e3b, 0x0000 }, + { 0x9e3c, 0x0000 }, + { 0x9e3d, 0x0000 }, + { 0x9e3e, 0x0000 }, + { 0x9e3f, 0x0000 }, + { 0x9e40, 0x0000 }, + { 0x9e41, 0x0000 }, + { 0x9e42, 0x0000 }, + { 0x9e43, 0x0000 }, + { 0x9e44, 0x0000 }, + { 0x9e45, 0x0000 }, + { 0x9e46, 0x0000 }, + { 0x9e47, 0x0000 }, + { 0x9e48, 0x0000 }, + { 0x9e49, 0x0000 }, + { 0x9e4a, 0x0000 }, + { 0x9e4b, 0x0000 }, + { 0x9e4c, 0x0000 }, + { 0x9e4d, 0x0000 }, + { 0x9e4e, 0x0000 }, + { 0x9e4f, 0x0000 }, + { 0x9e50, 0x0000 }, + { 0x9e51, 0x0000 }, + { 0x9e52, 0x0000 }, + { 0x9e53, 0x0000 }, + { 0x9e54, 0x0000 }, + { 0x9e55, 0x0000 }, + { 0x9e56, 0x0000 }, + { 0x9e57, 0x0000 }, + { 0x9e58, 0x0000 }, + { 0x9e59, 0x0000 }, + { 0x9e5a, 0x0000 }, + { 0x9e5b, 0x0000 }, + { 0x9e5c, 0x0000 }, + { 0x9e5d, 0x0000 }, + { 0x9e5e, 0x0000 }, + { 0x9e5f, 0x0000 }, + { 0x9e60, 0x0000 }, + { 0x9e61, 0x0000 }, + { 0x9e62, 0x0000 }, + { 0x9e63, 0x0000 }, + { 0x9e64, 0x0000 }, + { 0x9e65, 0x0000 }, + { 0x9e66, 0x0000 }, + { 0x9e67, 0x0000 }, + { 0x9e68, 0x0000 }, + { 0x9e69, 0x0000 }, + { 0x9e6a, 0x0000 }, + { 0x9e6b, 0x0000 }, + { 0x9e6c, 0x0000 }, + { 0x9e6d, 0x0000 }, + { 0x9e6e, 0x0000 }, + { 0x9e6f, 0x0000 }, + { 0x9e70, 0x0000 }, + { 0x9e71, 0x0000 }, + { 0x9e72, 0x0000 }, + { 0x9e73, 0x0000 }, + { 0x9e74, 0x0000 }, + { 0x9e75, 0x0000 }, + { 0x9e76, 0x0000 }, + { 0x9e77, 0x0000 }, + { 0x9e78, 0x0000 }, + { 0x9e79, 0x0000 }, + { 0x9e7a, 0x0000 }, + { 0x9e7b, 0x0000 }, + { 0x9e7c, 0x0000 }, + { 0x9e7d, 0x0000 }, + { 0x9e7e, 0x0000 }, + { 0x9e7f, 0x0000 }, + { 0x9e80, 0x0000 }, + { 0x9e81, 0x0000 }, + { 0x9e82, 0x0000 }, + { 0x9e83, 0x0000 }, + { 0x9e84, 0x0000 }, + { 0x9e85, 0x0000 }, + { 0x9e86, 0x0000 }, + { 0x9e87, 0x0000 }, + { 0x9e88, 0x0000 }, + { 0x9e89, 0x0000 }, + { 0x9e8a, 0x0000 }, + { 0x9e8b, 0x0000 }, + { 0x9e8c, 0x0000 }, + { 0x9e8d, 0x0000 }, + { 0x9e8e, 0x0000 }, + { 0x9e8f, 0x0000 }, + { 0x9e90, 0x0000 }, + { 0x9e91, 0x0000 }, + { 0x9e92, 0x0000 }, + { 0x9e93, 0x0000 }, + { 0x9e94, 0x0000 }, + { 0x9e95, 0x0000 }, + { 0x9e96, 0x0000 }, + { 0x9e97, 0x0000 }, + { 0x9e98, 0x0000 }, + { 0x9e99, 0x0000 }, + { 0x9e9a, 0x0000 }, + { 0x9e9b, 0x0000 }, + { 0x9e9c, 0x0000 }, + { 0x9e9d, 0x0000 }, + { 0x9e9e, 0x0000 }, + { 0x9e9f, 0x0000 }, + { 0x9ea0, 0x0000 }, + { 0x9ea1, 0x0000 }, + { 0x9ea2, 0x0000 }, + { 0x9ea3, 0x0000 }, + { 0x9ea4, 0x0000 }, + { 0x9ea5, 0x0000 }, + { 0x9ea6, 0x0000 }, + { 0x9ea7, 0x0000 }, + { 0x9ea8, 0x0000 }, + { 0x9ea9, 0x0000 }, + { 0x9eaa, 0x0000 }, + { 0x9eab, 0x0000 }, + { 0x9eac, 0x0000 }, + { 0x9ead, 0x0000 }, + { 0x9eae, 0x0000 }, + { 0x9eaf, 0x0000 }, + { 0x9eb0, 0x0000 }, + { 0x9eb1, 0x0000 }, + { 0x9eb2, 0x0000 }, + { 0x9eb3, 0x0000 }, + { 0x9eb4, 0x0000 }, + { 0x9eb5, 0x0000 }, + { 0x9eb6, 0x0000 }, + { 0x9eb7, 0x0000 }, + { 0x9eb8, 0x0000 }, + { 0x9eb9, 0x0000 }, + { 0x9eba, 0x0000 }, + { 0x9ebb, 0x0000 }, + { 0x9ebc, 0x0000 }, + { 0x9ebd, 0x0000 }, + { 0x9ebe, 0x0000 }, + { 0x9ebf, 0x0000 }, + { 0x9ec0, 0x0000 }, + { 0x9ec1, 0x0000 }, + { 0x9ec2, 0x0000 }, + { 0x9ec3, 0x0000 }, + { 0x9ec4, 0x0000 }, + { 0x9ec5, 0x0000 }, + { 0x9ec6, 0x0000 }, + { 0x9ec7, 0x0000 }, + { 0x9ec8, 0x0000 }, + { 0x9ec9, 0x0000 }, + { 0x9eca, 0x0000 }, + { 0x9ecb, 0x0000 }, + { 0x9ecc, 0x0000 }, + { 0x9ecd, 0x0000 }, + { 0x9ece, 0x0000 }, + { 0x9ecf, 0x0000 }, + { 0x9ed0, 0x0000 }, + { 0x9ed1, 0x0000 }, + { 0x9ed2, 0x0000 }, + { 0x9ed3, 0x0000 }, + { 0x9ed4, 0x0000 }, + { 0x9ed5, 0x0000 }, + { 0x9ed6, 0x0000 }, + { 0x9ed7, 0x0000 }, + { 0x9ed8, 0x0000 }, + { 0x9ed9, 0x0000 }, + { 0x9eda, 0x0000 }, + { 0x9edb, 0x0000 }, + { 0x9edc, 0x0000 }, + { 0x9edd, 0x0000 }, + { 0x9ede, 0x0000 }, + { 0x9edf, 0x0000 }, + { 0x9ee0, 0x0000 }, + { 0x9ee1, 0x0000 }, + { 0x9ee2, 0x0000 }, + { 0x9ee3, 0x0000 }, + { 0x9ee4, 0x0000 }, + { 0x9ee5, 0x0000 }, + { 0x9ee6, 0x0000 }, + { 0x9ee7, 0x0000 }, + { 0x9ee8, 0x0000 }, + { 0x9ee9, 0x0000 }, + { 0x9eea, 0x0000 }, + { 0x9eeb, 0x0000 }, + { 0x9eec, 0x0000 }, + { 0x9eed, 0x0000 }, + { 0x9eee, 0x0000 }, + { 0x9eef, 0x0000 }, + { 0x9ef0, 0x0000 }, + { 0x9ef1, 0x0000 }, + { 0x9ef2, 0x0000 }, + { 0x9ef3, 0x0000 }, + { 0x9ef4, 0x0000 }, + { 0x9ef5, 0x0000 }, + { 0x9ef6, 0x0000 }, + { 0x9ef7, 0x0000 }, + { 0x9ef8, 0x0000 }, + { 0x9ef9, 0x0000 }, + { 0x9efa, 0x0000 }, + { 0x9efb, 0x0000 }, + { 0x9efc, 0x0000 }, + { 0x9efd, 0x0000 }, + { 0x9efe, 0x0000 }, + { 0x9eff, 0x0000 }, + { 0x9f00, 0x0000 }, + { 0x9f01, 0x0000 }, + { 0x9f02, 0x0000 }, + { 0x9f03, 0x0000 }, + { 0x9f04, 0x0000 }, + { 0x9f05, 0x0000 }, + { 0x9f06, 0x0000 }, + { 0x9f07, 0x0000 }, + { 0x9f08, 0x0000 }, + { 0x9f09, 0x0000 }, + { 0x9f0a, 0x0000 }, + { 0x9f0b, 0x0000 }, + { 0x9f0c, 0x0000 }, + { 0x9f0d, 0x0000 }, + { 0x9f0e, 0x0000 }, + { 0x9f0f, 0x0000 }, + { 0x9f10, 0x0000 }, + { 0x9f11, 0x0000 }, + { 0x9f12, 0x0000 }, + { 0x9f13, 0x0000 }, + { 0x9f14, 0x0000 }, + { 0x9f15, 0x0000 }, + { 0x9f16, 0x0000 }, + { 0x9f17, 0x0000 }, + { 0x9f18, 0x0000 }, + { 0x9f19, 0x0000 }, + { 0x9f1a, 0x0000 }, + { 0x9f1b, 0x0000 }, + { 0x9f1c, 0x0000 }, + { 0x9f1d, 0x0000 }, + { 0x9f1e, 0x0000 }, + { 0x9f1f, 0x0000 }, + { 0x9f20, 0x0000 }, + { 0x9f21, 0x0000 }, + { 0x9f22, 0x0000 }, + { 0x9f23, 0x0000 }, + { 0x9f24, 0x0000 }, + { 0x9f25, 0x0000 }, + { 0x9f26, 0x0000 }, + { 0x9f27, 0x0000 }, + { 0x9f28, 0x0000 }, + { 0x9f29, 0x0000 }, + { 0x9f2a, 0x0000 }, + { 0x9f2b, 0x0000 }, + { 0x9f2c, 0x0000 }, + { 0x9f2d, 0x0000 }, + { 0x9f2e, 0x0000 }, + { 0x9f2f, 0x0000 }, + { 0x9f30, 0x0000 }, + { 0x9f31, 0x0000 }, + { 0x9f32, 0x0000 }, + { 0x9f33, 0x0000 }, + { 0x9f34, 0x0000 }, + { 0x9f35, 0x0000 }, + { 0x9f36, 0x0000 }, + { 0x9f37, 0x0000 }, + { 0x9f38, 0x0000 }, + { 0x9f39, 0x0000 }, + { 0x9f3a, 0x0000 }, + { 0x9f3b, 0x0000 }, + { 0x9f3c, 0x0000 }, + { 0x9f3d, 0x0000 }, + { 0x9f3e, 0x0000 }, + { 0x9f3f, 0x0000 }, + { 0x9f40, 0x0000 }, + { 0x9f41, 0x0000 }, + { 0x9f42, 0x0000 }, + { 0x9f43, 0x0000 }, + { 0x9f44, 0x0000 }, + { 0x9f45, 0x0000 }, + { 0x9f46, 0x0000 }, + { 0x9f47, 0x0000 }, + { 0x9f48, 0x0000 }, + { 0x9f49, 0x0000 }, + { 0x9f4a, 0x0000 }, + { 0x9f4b, 0x0000 }, + { 0x9f4c, 0x0000 }, + { 0x9f4d, 0x0000 }, + { 0x9f4e, 0x0000 }, + { 0x9f4f, 0x0000 }, + { 0x9f50, 0x0000 }, + { 0x9f51, 0x0000 }, + { 0x9f52, 0x0000 }, + { 0x9f53, 0x0000 }, + { 0x9f54, 0x0000 }, + { 0x9f55, 0x0000 }, + { 0x9f56, 0x0000 }, + { 0x9f57, 0x0000 }, + { 0x9f58, 0x0000 }, + { 0x9f59, 0x0000 }, + { 0x9f5a, 0x0000 }, + { 0x9f5b, 0x0000 }, + { 0x9f5c, 0x0000 }, + { 0x9f5d, 0x0000 }, + { 0x9f5e, 0x0000 }, + { 0x9f5f, 0x0000 }, + { 0x9f60, 0x0000 }, + { 0x9f61, 0x0000 }, + { 0x9f62, 0x0000 }, + { 0x9f63, 0x0000 }, + { 0x9f64, 0x0000 }, + { 0x9f65, 0x0000 }, + { 0x9f66, 0x0000 }, + { 0x9f67, 0x0000 }, + { 0x9f68, 0x0000 }, + { 0x9f69, 0x0000 }, + { 0x9f6a, 0x0000 }, + { 0x9f6b, 0x0000 }, + { 0x9f6c, 0x0000 }, + { 0x9f6d, 0x0000 }, + { 0x9f6e, 0x0000 }, + { 0x9f6f, 0x0000 }, + { 0x9f70, 0x0000 }, + { 0x9f71, 0x0000 }, + { 0x9f72, 0x0000 }, + { 0x9f73, 0x0000 }, + { 0x9f74, 0x0000 }, + { 0x9f75, 0x0000 }, + { 0x9f76, 0x0000 }, + { 0x9f77, 0x0000 }, + { 0x9f78, 0x0000 }, + { 0x9f79, 0x0000 }, + { 0x9f7a, 0x0000 }, + { 0x9f7b, 0x0000 }, + { 0x9f7c, 0x0000 }, + { 0x9f7d, 0x0000 }, + { 0x9f7e, 0x0000 }, + { 0x9f7f, 0x0000 }, + { 0x9f80, 0x0000 }, + { 0x9f81, 0x0000 }, + { 0x9f82, 0x0000 }, + { 0x9f83, 0x0000 }, + { 0x9f84, 0x0000 }, + { 0x9f85, 0x0000 }, + { 0x9f86, 0x0000 }, + { 0x9f87, 0x0000 }, + { 0x9f88, 0x0000 }, + { 0x9f89, 0x0000 }, + { 0x9f8a, 0x0000 }, + { 0x9f8b, 0x0000 }, + { 0x9f8c, 0x0000 }, + { 0x9f8d, 0x0000 }, + { 0x9f8e, 0x0000 }, + { 0x9f8f, 0x0000 }, + { 0x9f90, 0x0000 }, + { 0x9f91, 0x0000 }, + { 0x9f92, 0x0000 }, + { 0x9f93, 0x0000 }, + { 0x9f94, 0x0000 }, + { 0x9f95, 0x0000 }, + { 0x9f96, 0x0000 }, + { 0x9f97, 0x0000 }, + { 0x9f98, 0x0000 }, + { 0x9f99, 0x0000 }, + { 0x9f9a, 0x0000 }, + { 0x9f9b, 0x0000 }, + { 0x9f9c, 0x0000 }, + { 0x9f9d, 0x0000 }, + { 0x9f9e, 0x0000 }, + { 0x9f9f, 0x0000 }, + { 0x9fa0, 0x0000 }, + { 0x9fa1, 0x0000 }, + { 0x9fa2, 0x0000 }, + { 0x9fa3, 0x0000 }, + { 0x9fa4, 0x0000 }, + { 0x9fa5, 0x0000 }, + { 0x9fa6, 0x0000 }, + { 0x9fa7, 0x0000 }, + { 0x9fa8, 0x0000 }, + { 0x9fa9, 0x0000 }, + { 0x9faa, 0x0000 }, + { 0x9fab, 0x0000 }, + { 0x9fac, 0x0000 }, + { 0x9fad, 0x0000 }, + { 0x9fae, 0x0000 }, + { 0x9faf, 0x0000 }, + { 0x9fb0, 0x0000 }, + { 0x9fb1, 0x0000 }, + { 0x9fb2, 0x0000 }, + { 0x9fb3, 0x0000 }, + { 0x9fb4, 0x0000 }, + { 0x9fb5, 0x0000 }, + { 0x9fb6, 0x0000 }, + { 0x9fb7, 0x0000 }, + { 0x9fb8, 0x0000 }, + { 0x9fb9, 0x0000 }, + { 0x9fba, 0x0000 }, + { 0x9fbb, 0x0000 }, + { 0x9fbc, 0x0000 }, + { 0x9fbd, 0x0000 }, + { 0x9fbe, 0x0000 }, + { 0x9fbf, 0x0000 }, + { 0x9fc0, 0x0000 }, + { 0x9fc1, 0x0000 }, + { 0x9fc2, 0x0000 }, + { 0x9fc3, 0x0000 }, + { 0x9fc4, 0x0000 }, + { 0x9fc5, 0x0000 }, + { 0x9fc6, 0x0000 }, + { 0x9fc7, 0x0000 }, + { 0x9fc8, 0x0000 }, + { 0x9fc9, 0x0000 }, + { 0x9fca, 0x0000 }, + { 0x9fcb, 0x0000 }, + { 0x9fcc, 0x0000 }, + { 0x9fcd, 0x0000 }, + { 0x9fce, 0x0000 }, + { 0x9fcf, 0x0000 }, + { 0x9fd0, 0x0000 }, + { 0x9fd1, 0x0000 }, + { 0x9fd2, 0x0000 }, + { 0x9fd3, 0x0000 }, + { 0x9fd4, 0x0000 }, + { 0x9fd5, 0x0000 }, + { 0x9fd6, 0x0000 }, + { 0x9fd7, 0x0000 }, + { 0x9fd8, 0x0000 }, + { 0x9fd9, 0x0000 }, + { 0x9fda, 0x0000 }, + { 0x9fdb, 0x0000 }, + { 0x9fdc, 0x0000 }, + { 0x9fdd, 0x0000 }, + { 0x9fde, 0x0000 }, + { 0x9fdf, 0x0000 }, + { 0x9fe0, 0x0000 }, + { 0x9fe1, 0x0000 }, + { 0x9fe2, 0x0000 }, + { 0x9fe3, 0x0000 }, + { 0x9fe4, 0x0000 }, + { 0x9fe5, 0x0000 }, + { 0x9fe6, 0x0000 }, + { 0x9fe7, 0x0000 }, + { 0x9fe8, 0x0000 }, + { 0x9fe9, 0x0000 }, + { 0x9fea, 0x0000 }, + { 0x9feb, 0x0000 }, + { 0x9fec, 0x0000 }, + { 0x9fed, 0x0000 }, + { 0x9fee, 0x0000 }, + { 0x9fef, 0x0000 }, + { 0x9ff0, 0x0000 }, + { 0x9ff1, 0x0000 }, + { 0x9ff2, 0x0000 }, + { 0x9ff3, 0x0000 }, + { 0x9ff4, 0x0000 }, + { 0x9ff5, 0x0000 }, + { 0x9ff6, 0x0000 }, + { 0x9ff7, 0x0000 }, + { 0x9ff8, 0x0000 }, + { 0x9ff9, 0x0000 }, + { 0x9ffa, 0x0000 }, + { 0x9ffb, 0x0000 }, + { 0x9ffc, 0x0000 }, + { 0x9ffd, 0x0000 }, + { 0x9ffe, 0x0000 }, + { 0x9fff, 0x0000 }, + { 0xa000, 0x0000 }, + { 0xa001, 0x0000 }, + { 0xa002, 0x0000 }, + { 0xa003, 0x0000 }, + { 0xa004, 0x0000 }, + { 0xa005, 0x0000 }, + { 0xa006, 0x0000 }, + { 0xa007, 0x0000 }, + { 0xa008, 0x0000 }, + { 0xa009, 0x0000 }, + { 0xa00a, 0x0000 }, + { 0xa00b, 0x0000 }, + { 0xa00c, 0x0000 }, + { 0xa00d, 0x0000 }, + { 0xa00e, 0x0000 }, + { 0xa00f, 0x0000 }, + { 0xa010, 0x0000 }, + { 0xa011, 0x0000 }, + { 0xa012, 0x0000 }, + { 0xa013, 0x0000 }, + { 0xa014, 0x0000 }, + { 0xa015, 0x0000 }, + { 0xa016, 0x0000 }, + { 0xa017, 0x0000 }, + { 0xa018, 0x0000 }, + { 0xa019, 0x0000 }, + { 0xa01a, 0x0000 }, + { 0xa01b, 0x0000 }, + { 0xa01c, 0x0000 }, + { 0xa01d, 0x0000 }, + { 0xa01e, 0x0000 }, + { 0xa01f, 0x0000 }, + { 0xa020, 0x0000 }, + { 0xa021, 0x0000 }, + { 0xa022, 0x0000 }, + { 0xa023, 0x0000 }, + { 0xa024, 0x0000 }, + { 0xa025, 0x0000 }, + { 0xa026, 0x0000 }, + { 0xa027, 0x0000 }, + { 0xa028, 0x0000 }, + { 0xa029, 0x0000 }, + { 0xa02a, 0x0000 }, + { 0xa02b, 0x0000 }, + { 0xa02c, 0x0000 }, + { 0xa02d, 0x0000 }, + { 0xa02e, 0x0000 }, + { 0xa02f, 0x0000 }, + { 0xa030, 0x0000 }, + { 0xa031, 0x0000 }, + { 0xa032, 0x0000 }, + { 0xa033, 0x0000 }, + { 0xa034, 0x0000 }, + { 0xa035, 0x0000 }, + { 0xa036, 0x0000 }, + { 0xa037, 0x0000 }, + { 0xa038, 0x0000 }, + { 0xa039, 0x0000 }, + { 0xa03a, 0x0000 }, + { 0xa03b, 0x0000 }, + { 0xa03c, 0x0000 }, + { 0xa03d, 0x0000 }, + { 0xa03e, 0x0000 }, + { 0xa03f, 0x0000 }, + { 0xa040, 0x0000 }, + { 0xa041, 0x0000 }, + { 0xa042, 0x0000 }, + { 0xa043, 0x0000 }, + { 0xa044, 0x0000 }, + { 0xa045, 0x0000 }, + { 0xa046, 0x0000 }, + { 0xa047, 0x0000 }, + { 0xa048, 0x0000 }, + { 0xa049, 0x0000 }, + { 0xa04a, 0x0000 }, + { 0xa04b, 0x0000 }, + { 0xa04c, 0x0000 }, + { 0xa04d, 0x0000 }, + { 0xa04e, 0x0000 }, + { 0xa04f, 0x0000 }, + { 0xa050, 0x0000 }, + { 0xa051, 0x0000 }, + { 0xa052, 0x0000 }, + { 0xa053, 0x0000 }, + { 0xa054, 0x0000 }, + { 0xa055, 0x0000 }, + { 0xa056, 0x0000 }, + { 0xa057, 0x0000 }, + { 0xa058, 0x0000 }, + { 0xa059, 0x0000 }, + { 0xa05a, 0x0000 }, + { 0xa05b, 0x0000 }, + { 0xa05c, 0x0000 }, + { 0xa05d, 0x0000 }, + { 0xa05e, 0x0000 }, + { 0xa05f, 0x0000 }, + { 0xa060, 0x0000 }, + { 0xa061, 0x0000 }, + { 0xa062, 0x0000 }, + { 0xa063, 0x0000 }, + { 0xa064, 0x0000 }, + { 0xa065, 0x0000 }, + { 0xa066, 0x0000 }, + { 0xa067, 0x0000 }, + { 0xa068, 0x0000 }, + { 0xa069, 0x0000 }, + { 0xa06a, 0x0000 }, + { 0xa06b, 0x0000 }, + { 0xa06c, 0x0000 }, + { 0xa06d, 0x0000 }, + { 0xa06e, 0x0000 }, + { 0xa06f, 0x0000 }, + { 0xa070, 0x0000 }, + { 0xa071, 0x0000 }, + { 0xa072, 0x0000 }, + { 0xa073, 0x0000 }, + { 0xa074, 0x0000 }, + { 0xa075, 0x0000 }, + { 0xa076, 0x0000 }, + { 0xa077, 0x0000 }, + { 0xa078, 0x0000 }, + { 0xa079, 0x0000 }, + { 0xa07a, 0x0000 }, + { 0xa07b, 0x0000 }, + { 0xa07c, 0x0000 }, + { 0xa07d, 0x0000 }, + { 0xa07e, 0x0000 }, + { 0xa07f, 0x0000 }, + { 0xa080, 0x0000 }, + { 0xa081, 0x0000 }, + { 0xa082, 0x0000 }, + { 0xa083, 0x0000 }, + { 0xa084, 0x0000 }, + { 0xa085, 0x0000 }, + { 0xa086, 0x0000 }, + { 0xa087, 0x0000 }, + { 0xa088, 0x0000 }, + { 0xa089, 0x0000 }, + { 0xa08a, 0x0000 }, + { 0xa08b, 0x0000 }, + { 0xa08c, 0x0000 }, + { 0xa08d, 0x0000 }, + { 0xa08e, 0x0000 }, + { 0xa08f, 0x0000 }, + { 0xa090, 0x0000 }, + { 0xa091, 0x0000 }, + { 0xa092, 0x0000 }, + { 0xa093, 0x0000 }, + { 0xa094, 0x0000 }, + { 0xa095, 0x0000 }, + { 0xa096, 0x0000 }, + { 0xa097, 0x0000 }, + { 0xa098, 0x0000 }, + { 0xa099, 0x0000 }, + { 0xa09a, 0x0000 }, + { 0xa09b, 0x0000 }, + { 0xa09c, 0x0000 }, + { 0xa09d, 0x0000 }, + { 0xa09e, 0x0000 }, + { 0xa09f, 0x0000 }, + { 0xa0a0, 0x0000 }, + { 0xa0a1, 0x0000 }, + { 0xa0a2, 0x0000 }, + { 0xa0a3, 0x0000 }, + { 0xa0a4, 0x0000 }, + { 0xa0a5, 0x0000 }, + { 0xa0a6, 0x0000 }, + { 0xa0a7, 0x0000 }, + { 0xa0a8, 0x0000 }, + { 0xa0a9, 0x0000 }, + { 0xa0aa, 0x0000 }, + { 0xa0ab, 0x0000 }, + { 0xa0ac, 0x0000 }, + { 0xa0ad, 0x0000 }, + { 0xa0ae, 0x0000 }, + { 0xa0af, 0x0000 }, + { 0xa0b0, 0x0000 }, + { 0xa0b1, 0x0000 }, + { 0xa0b2, 0x0000 }, + { 0xa0b3, 0x0000 }, + { 0xa0b4, 0x0000 }, + { 0xa0b5, 0x0000 }, + { 0xa0b6, 0x0000 }, + { 0xa0b7, 0x0000 }, + { 0xa0b8, 0x0000 }, + { 0xa0b9, 0x0000 }, + { 0xa0ba, 0x0000 }, + { 0xa0bb, 0x0000 }, + { 0xa0bc, 0x0000 }, + { 0xa0bd, 0x0000 }, + { 0xa0be, 0x0000 }, + { 0xa0bf, 0x0000 }, + { 0xa0c0, 0x0000 }, + { 0xa0c1, 0x0000 }, + { 0xa0c2, 0x0000 }, + { 0xa0c3, 0x0000 }, + { 0xa0c4, 0x0000 }, + { 0xa0c5, 0x0000 }, + { 0xa0c6, 0x0000 }, + { 0xa0c7, 0x0000 }, + { 0xa0c8, 0x0000 }, + { 0xa0c9, 0x0000 }, + { 0xa0ca, 0x0000 }, + { 0xa0cb, 0x0000 }, + { 0xa0cc, 0x0000 }, + { 0xa0cd, 0x0000 }, + { 0xa0ce, 0x0000 }, + { 0xa0cf, 0x0000 }, + { 0xa0d0, 0x0000 }, + { 0xa0d1, 0x0000 }, + { 0xa0d2, 0x0000 }, + { 0xa0d3, 0x0000 }, + { 0xa0d4, 0x0000 }, + { 0xa0d5, 0x0000 }, + { 0xa0d6, 0x0000 }, + { 0xa0d7, 0x0000 }, + { 0xa0d8, 0x0000 }, + { 0xa0d9, 0x0000 }, + { 0xa0da, 0x0000 }, + { 0xa0db, 0x0000 }, + { 0xa0dc, 0x0000 }, + { 0xa0dd, 0x0000 }, + { 0xa0de, 0x0000 }, + { 0xa0df, 0x0000 }, + { 0xa0e0, 0x0000 }, + { 0xa0e1, 0x0000 }, + { 0xa0e2, 0x0000 }, + { 0xa0e3, 0x0000 }, + { 0xa0e4, 0x0000 }, + { 0xa0e5, 0x0000 }, + { 0xa0e6, 0x0000 }, + { 0xa0e7, 0x0000 }, + { 0xa0e8, 0x0000 }, + { 0xa0e9, 0x0000 }, + { 0xa0ea, 0x0000 }, + { 0xa0eb, 0x0000 }, + { 0xa0ec, 0x0000 }, + { 0xa0ed, 0x0000 }, + { 0xa0ee, 0x0000 }, + { 0xa0ef, 0x0000 }, + { 0xa0f0, 0x0000 }, + { 0xa0f1, 0x0000 }, + { 0xa0f2, 0x0000 }, + { 0xa0f3, 0x0000 }, + { 0xa0f4, 0x0000 }, + { 0xa0f5, 0x0000 }, + { 0xa0f6, 0x0000 }, + { 0xa0f7, 0x0000 }, + { 0xa0f8, 0x0000 }, + { 0xa0f9, 0x0000 }, + { 0xa0fa, 0x0000 }, + { 0xa0fb, 0x0000 }, + { 0xa0fc, 0x0000 }, + { 0xa0fd, 0x0000 }, + { 0xa0fe, 0x0000 }, + { 0xa0ff, 0x0000 }, + { 0xa100, 0x0000 }, + { 0xa101, 0x0000 }, + { 0xa102, 0x0000 }, + { 0xa103, 0x0000 }, + { 0xa104, 0x0000 }, + { 0xa105, 0x0000 }, + { 0xa106, 0x0000 }, + { 0xa107, 0x0000 }, + { 0xa108, 0x0000 }, + { 0xa109, 0x0000 }, + { 0xa10a, 0x0000 }, + { 0xa10b, 0x0000 }, + { 0xa10c, 0x0000 }, + { 0xa10d, 0x0000 }, + { 0xa10e, 0x0000 }, + { 0xa10f, 0x0000 }, + { 0xa110, 0x0000 }, + { 0xa111, 0x0000 }, + { 0xa112, 0x0000 }, + { 0xa113, 0x0000 }, + { 0xa114, 0x0000 }, + { 0xa115, 0x0000 }, + { 0xa116, 0x0000 }, + { 0xa117, 0x0000 }, + { 0xa118, 0x0000 }, + { 0xa119, 0x0000 }, + { 0xa11a, 0x0000 }, + { 0xa11b, 0x0000 }, + { 0xa11c, 0x0000 }, + { 0xa11d, 0x0000 }, + { 0xa11e, 0x0000 }, + { 0xa11f, 0x0000 }, + { 0xa120, 0x0000 }, + { 0xa121, 0x0000 }, + { 0xa122, 0x0000 }, + { 0xa123, 0x0000 }, + { 0xa124, 0x0000 }, + { 0xa125, 0x0000 }, + { 0xa126, 0x0000 }, + { 0xa127, 0x0000 }, + { 0xa128, 0x0000 }, + { 0xa129, 0x0000 }, + { 0xa12a, 0x0000 }, + { 0xa12b, 0x0000 }, + { 0xa12c, 0x0000 }, + { 0xa12d, 0x0000 }, + { 0xa12e, 0x0000 }, + { 0xa12f, 0x0000 }, + { 0xa130, 0x0000 }, + { 0xa131, 0x0000 }, + { 0xa132, 0x0000 }, + { 0xa133, 0x0000 }, + { 0xa134, 0x0000 }, + { 0xa135, 0x0000 }, + { 0xa136, 0x0000 }, + { 0xa137, 0x0000 }, + { 0xa138, 0x0000 }, + { 0xa139, 0x0000 }, + { 0xa13a, 0x0000 }, + { 0xa13b, 0x0000 }, + { 0xa13c, 0x0000 }, + { 0xa13d, 0x0000 }, + { 0xa13e, 0x0000 }, + { 0xa13f, 0x0000 }, + { 0xa140, 0x0000 }, + { 0xa141, 0x0000 }, + { 0xa142, 0x0000 }, + { 0xa143, 0x0000 }, + { 0xa144, 0x0000 }, + { 0xa145, 0x0000 }, + { 0xa146, 0x0000 }, + { 0xa147, 0x0000 }, + { 0xa148, 0x0000 }, + { 0xa149, 0x0000 }, + { 0xa14a, 0x0000 }, + { 0xa14b, 0x0000 }, + { 0xa14c, 0x0000 }, + { 0xa14d, 0x0000 }, + { 0xa14e, 0x0000 }, + { 0xa14f, 0x0000 }, + { 0xa150, 0x0000 }, + { 0xa151, 0x0000 }, + { 0xa152, 0x0000 }, + { 0xa153, 0x0000 }, + { 0xa154, 0x0000 }, + { 0xa155, 0x0000 }, + { 0xa156, 0x0000 }, + { 0xa157, 0x0000 }, + { 0xa158, 0x0000 }, + { 0xa159, 0x0000 }, + { 0xa15a, 0x0000 }, + { 0xa15b, 0x0000 }, + { 0xa15c, 0x0000 }, + { 0xa15d, 0x0000 }, + { 0xa15e, 0x0000 }, + { 0xa15f, 0x0000 }, + { 0xa160, 0x0000 }, + { 0xa161, 0x0000 }, + { 0xa162, 0x0000 }, + { 0xa163, 0x0000 }, + { 0xa164, 0x0000 }, + { 0xa165, 0x0000 }, + { 0xa166, 0x0000 }, + { 0xa167, 0x0000 }, + { 0xa168, 0x0000 }, + { 0xa169, 0x0000 }, + { 0xa16a, 0x0000 }, + { 0xa16b, 0x0000 }, + { 0xa16c, 0x0000 }, + { 0xa16d, 0x0000 }, + { 0xa16e, 0x0000 }, + { 0xa16f, 0x0000 }, + { 0xa170, 0x0000 }, + { 0xa171, 0x0000 }, + { 0xa172, 0x0000 }, + { 0xa173, 0x0000 }, + { 0xa174, 0x0000 }, + { 0xa175, 0x0000 }, + { 0xa176, 0x0000 }, + { 0xa177, 0x0000 }, + { 0xa178, 0x0000 }, + { 0xa179, 0x0000 }, + { 0xa17a, 0x0000 }, + { 0xa17b, 0x0000 }, + { 0xa17c, 0x0000 }, + { 0xa17d, 0x0000 }, + { 0xa17e, 0x0000 }, + { 0xa17f, 0x0000 }, + { 0xa180, 0x0000 }, + { 0xa181, 0x0000 }, + { 0xa182, 0x0000 }, + { 0xa183, 0x0000 }, + { 0xa184, 0x0000 }, + { 0xa185, 0x0000 }, + { 0xa186, 0x0000 }, + { 0xa187, 0x0000 }, + { 0xa188, 0x0000 }, + { 0xa189, 0x0000 }, + { 0xa18a, 0x0000 }, + { 0xa18b, 0x0000 }, + { 0xa18c, 0x0000 }, + { 0xa18d, 0x0000 }, + { 0xa18e, 0x0000 }, + { 0xa18f, 0x0000 }, + { 0xa190, 0x0000 }, + { 0xa191, 0x0000 }, + { 0xa192, 0x0000 }, + { 0xa193, 0x0000 }, + { 0xa194, 0x0000 }, + { 0xa195, 0x0000 }, + { 0xa196, 0x0000 }, + { 0xa197, 0x0000 }, + { 0xa198, 0x0000 }, + { 0xa199, 0x0000 }, + { 0xa19a, 0x0000 }, + { 0xa19b, 0x0000 }, + { 0xa19c, 0x0000 }, + { 0xa19d, 0x0000 }, + { 0xa19e, 0x0000 }, + { 0xa19f, 0x0000 }, + { 0xa1a0, 0x0000 }, + { 0xa1a1, 0x0000 }, + { 0xa1a2, 0x0000 }, + { 0xa1a3, 0x0000 }, + { 0xa1a4, 0x0000 }, + { 0xa1a5, 0x0000 }, + { 0xa1a6, 0x0000 }, + { 0xa1a7, 0x0000 }, + { 0xa1a8, 0x0000 }, + { 0xa1a9, 0x0000 }, + { 0xa1aa, 0x0000 }, + { 0xa1ab, 0x0000 }, + { 0xa1ac, 0x0000 }, + { 0xa1ad, 0x0000 }, + { 0xa1ae, 0x0000 }, + { 0xa1af, 0x0000 }, + { 0xa1b0, 0x0000 }, + { 0xa1b1, 0x0000 }, + { 0xa1b2, 0x0000 }, + { 0xa1b3, 0x0000 }, + { 0xa1b4, 0x0000 }, + { 0xa1b5, 0x0000 }, + { 0xa1b6, 0x0000 }, + { 0xa1b7, 0x0000 }, + { 0xa1b8, 0x0000 }, + { 0xa1b9, 0x0000 }, + { 0xa1ba, 0x0000 }, + { 0xa1bb, 0x0000 }, + { 0xa1bc, 0x0000 }, + { 0xa1bd, 0x0000 }, + { 0xa1be, 0x0000 }, + { 0xa1bf, 0x0000 }, + { 0xa1c0, 0x0000 }, + { 0xa1c1, 0x0000 }, + { 0xa1c2, 0x0000 }, + { 0xa1c3, 0x0000 }, + { 0xa1c4, 0x0000 }, + { 0xa1c5, 0x0000 }, + { 0xa1c6, 0x0000 }, + { 0xa1c7, 0x0000 }, + { 0xa1c8, 0x0000 }, + { 0xa1c9, 0x0000 }, + { 0xa1ca, 0x0000 }, + { 0xa1cb, 0x0000 }, + { 0xa1cc, 0x0000 }, + { 0xa1cd, 0x0000 }, + { 0xa1ce, 0x0000 }, + { 0xa1cf, 0x0000 }, + { 0xa1d0, 0x0000 }, + { 0xa1d1, 0x0000 }, + { 0xa1d2, 0x0000 }, + { 0xa1d3, 0x0000 }, + { 0xa1d4, 0x0000 }, + { 0xa1d5, 0x0000 }, + { 0xa1d6, 0x0000 }, + { 0xa1d7, 0x0000 }, + { 0xa1d8, 0x0000 }, + { 0xa1d9, 0x0000 }, + { 0xa1da, 0x0000 }, + { 0xa1db, 0x0000 }, + { 0xa1dc, 0x0000 }, + { 0xa1dd, 0x0000 }, + { 0xa1de, 0x0000 }, + { 0xa1df, 0x0000 }, + { 0xa1e0, 0x0000 }, + { 0xa1e1, 0x0000 }, + { 0xa1e2, 0x0000 }, + { 0xa1e3, 0x0000 }, + { 0xa1e4, 0x0000 }, + { 0xa1e5, 0x0000 }, + { 0xa1e6, 0x0000 }, + { 0xa1e7, 0x0000 }, + { 0xa1e8, 0x0000 }, + { 0xa1e9, 0x0000 }, + { 0xa1ea, 0x0000 }, + { 0xa1eb, 0x0000 }, + { 0xa1ec, 0x0000 }, + { 0xa1ed, 0x0000 }, + { 0xa1ee, 0x0000 }, + { 0xa1ef, 0x0000 }, + { 0xa1f0, 0x0000 }, + { 0xa1f1, 0x0000 }, + { 0xa1f2, 0x0000 }, + { 0xa1f3, 0x0000 }, + { 0xa1f4, 0x0000 }, + { 0xa1f5, 0x0000 }, + { 0xa1f6, 0x0000 }, + { 0xa1f7, 0x0000 }, + { 0xa1f8, 0x0000 }, + { 0xa1f9, 0x0000 }, + { 0xa1fa, 0x0000 }, + { 0xa1fb, 0x0000 }, + { 0xa1fc, 0x0000 }, + { 0xa1fd, 0x0000 }, + { 0xa1fe, 0x0000 }, + { 0xa1ff, 0x0000 }, + { 0xa200, 0x0000 }, + { 0xa201, 0x0000 }, + { 0xa202, 0x0000 }, + { 0xa203, 0x0000 }, + { 0xa204, 0x0000 }, + { 0xa205, 0x0000 }, + { 0xa206, 0x0000 }, + { 0xa207, 0x0000 }, + { 0xa208, 0x0000 }, + { 0xa209, 0x0000 }, + { 0xa20a, 0x0000 }, + { 0xa20b, 0x0000 }, + { 0xa20c, 0x0000 }, + { 0xa20d, 0x0000 }, + { 0xa20e, 0x0000 }, + { 0xa20f, 0x0000 }, + { 0xa210, 0x0000 }, + { 0xa211, 0x0000 }, + { 0xa212, 0x0000 }, + { 0xa213, 0x0000 }, + { 0xa214, 0x0000 }, + { 0xa215, 0x0000 }, + { 0xa216, 0x0000 }, + { 0xa217, 0x0000 }, + { 0xa218, 0x0000 }, + { 0xa219, 0x0000 }, + { 0xa21a, 0x0000 }, + { 0xa21b, 0x0000 }, + { 0xa21c, 0x0000 }, + { 0xa21d, 0x0000 }, + { 0xa21e, 0x0000 }, + { 0xa21f, 0x0000 }, + { 0xa220, 0x0000 }, + { 0xa221, 0x0000 }, + { 0xa222, 0x0000 }, + { 0xa223, 0x0000 }, + { 0xa224, 0x0000 }, + { 0xa225, 0x0000 }, + { 0xa226, 0x0000 }, + { 0xa227, 0x0000 }, + { 0xa228, 0x0000 }, + { 0xa229, 0x0000 }, + { 0xa22a, 0x0000 }, + { 0xa22b, 0x0000 }, + { 0xa22c, 0x0000 }, + { 0xa22d, 0x0000 }, + { 0xa22e, 0x0000 }, + { 0xa22f, 0x0000 }, + { 0xa230, 0x0000 }, + { 0xa231, 0x0000 }, + { 0xa232, 0x0000 }, + { 0xa233, 0x0000 }, + { 0xa234, 0x0000 }, + { 0xa235, 0x0000 }, + { 0xa236, 0x0000 }, + { 0xa237, 0x0000 }, + { 0xa238, 0x0000 }, + { 0xa239, 0x0000 }, + { 0xa23a, 0x0000 }, + { 0xa23b, 0x0000 }, + { 0xa23c, 0x0000 }, + { 0xa23d, 0x0000 }, + { 0xa23e, 0x0000 }, + { 0xa23f, 0x0000 }, + { 0xa240, 0x0000 }, + { 0xa241, 0x0000 }, + { 0xa242, 0x0000 }, + { 0xa243, 0x0000 }, + { 0xa244, 0x0000 }, + { 0xa245, 0x0000 }, + { 0xa246, 0x0000 }, + { 0xa247, 0x0000 }, + { 0xa248, 0x0000 }, + { 0xa249, 0x0000 }, + { 0xa24a, 0x0000 }, + { 0xa24b, 0x0000 }, + { 0xa24c, 0x0000 }, + { 0xa24d, 0x0000 }, + { 0xa24e, 0x0000 }, + { 0xa24f, 0x0000 }, + { 0xa250, 0x0000 }, + { 0xa251, 0x0000 }, + { 0xa252, 0x0000 }, + { 0xa253, 0x0000 }, + { 0xa254, 0x0000 }, + { 0xa255, 0x0000 }, + { 0xa256, 0x0000 }, + { 0xa257, 0x0000 }, + { 0xa258, 0x0000 }, + { 0xa259, 0x0000 }, + { 0xa25a, 0x0000 }, + { 0xa25b, 0x0000 }, + { 0xa25c, 0x0000 }, + { 0xa25d, 0x0000 }, + { 0xa25e, 0x0000 }, + { 0xa25f, 0x0000 }, + { 0xa260, 0x0000 }, + { 0xa261, 0x0000 }, + { 0xa262, 0x0000 }, + { 0xa263, 0x0000 }, + { 0xa264, 0x0000 }, + { 0xa265, 0x0000 }, + { 0xa266, 0x0000 }, + { 0xa267, 0x0000 }, + { 0xa268, 0x0000 }, + { 0xa269, 0x0000 }, + { 0xa26a, 0x0000 }, + { 0xa26b, 0x0000 }, + { 0xa26c, 0x0000 }, + { 0xa26d, 0x0000 }, + { 0xa26e, 0x0000 }, + { 0xa26f, 0x0000 }, + { 0xa270, 0x0000 }, + { 0xa271, 0x0000 }, + { 0xa272, 0x0000 }, + { 0xa273, 0x0000 }, + { 0xa274, 0x0000 }, + { 0xa275, 0x0000 }, + { 0xa276, 0x0000 }, + { 0xa277, 0x0000 }, + { 0xa278, 0x0000 }, + { 0xa279, 0x0000 }, + { 0xa27a, 0x0000 }, + { 0xa27b, 0x0000 }, + { 0xa27c, 0x0000 }, + { 0xa27d, 0x0000 }, + { 0xa27e, 0x0000 }, + { 0xa27f, 0x0000 }, + { 0xa280, 0x0000 }, + { 0xa281, 0x0000 }, + { 0xa282, 0x0000 }, + { 0xa283, 0x0000 }, + { 0xa284, 0x0000 }, + { 0xa285, 0x0000 }, + { 0xa286, 0x0000 }, + { 0xa287, 0x0000 }, + { 0xa288, 0x0000 }, + { 0xa289, 0x0000 }, + { 0xa28a, 0x0000 }, + { 0xa28b, 0x0000 }, + { 0xa28c, 0x0000 }, + { 0xa28d, 0x0000 }, + { 0xa28e, 0x0000 }, + { 0xa28f, 0x0000 }, + { 0xa290, 0x0000 }, + { 0xa291, 0x0000 }, + { 0xa292, 0x0000 }, + { 0xa293, 0x0000 }, + { 0xa294, 0x0000 }, + { 0xa295, 0x0000 }, + { 0xa296, 0x0000 }, + { 0xa297, 0x0000 }, + { 0xa298, 0x0000 }, + { 0xa299, 0x0000 }, + { 0xa29a, 0x0000 }, + { 0xa29b, 0x0000 }, + { 0xa29c, 0x0000 }, + { 0xa29d, 0x0000 }, + { 0xa29e, 0x0000 }, + { 0xa29f, 0x0000 }, + { 0xa2a0, 0x0000 }, + { 0xa2a1, 0x0000 }, + { 0xa2a2, 0x0000 }, + { 0xa2a3, 0x0000 }, + { 0xa2a4, 0x0000 }, + { 0xa2a5, 0x0000 }, + { 0xa2a6, 0x0000 }, + { 0xa2a7, 0x0000 }, + { 0xa2a8, 0x0000 }, + { 0xa2a9, 0x0000 }, + { 0xa2aa, 0x0000 }, + { 0xa2ab, 0x0000 }, + { 0xa2ac, 0x0000 }, + { 0xa2ad, 0x0000 }, + { 0xa2ae, 0x0000 }, + { 0xa2af, 0x0000 }, + { 0xa2b0, 0x0000 }, + { 0xa2b1, 0x0000 }, + { 0xa2b2, 0x0000 }, + { 0xa2b3, 0x0000 }, + { 0xa2b4, 0x0000 }, + { 0xa2b5, 0x0000 }, + { 0xa2b6, 0x0000 }, + { 0xa2b7, 0x0000 }, + { 0xa2b8, 0x0000 }, + { 0xa2b9, 0x0000 }, + { 0xa2ba, 0x0000 }, + { 0xa2bb, 0x0000 }, + { 0xa2bc, 0x0000 }, + { 0xa2bd, 0x0000 }, + { 0xa2be, 0x0000 }, + { 0xa2bf, 0x0000 }, + { 0xa2c0, 0x0000 }, + { 0xa2c1, 0x0000 }, + { 0xa2c2, 0x0000 }, + { 0xa2c3, 0x0000 }, + { 0xa2c4, 0x0000 }, + { 0xa2c5, 0x0000 }, + { 0xa2c6, 0x0000 }, + { 0xa2c7, 0x0000 }, + { 0xa2c8, 0x0000 }, + { 0xa2c9, 0x0000 }, + { 0xa2ca, 0x0000 }, + { 0xa2cb, 0x0000 }, + { 0xa2cc, 0x0000 }, + { 0xa2cd, 0x0000 }, + { 0xa2ce, 0x0000 }, + { 0xa2cf, 0x0000 }, + { 0xa2d0, 0x0000 }, + { 0xa2d1, 0x0000 }, + { 0xa2d2, 0x0000 }, + { 0xa2d3, 0x0000 }, + { 0xa2d4, 0x0000 }, + { 0xa2d5, 0x0000 }, + { 0xa2d6, 0x0000 }, + { 0xa2d7, 0x0000 }, + { 0xa2d8, 0x0000 }, + { 0xa2d9, 0x0000 }, + { 0xa2da, 0x0000 }, + { 0xa2db, 0x0000 }, + { 0xa2dc, 0x0000 }, + { 0xa2dd, 0x0000 }, + { 0xa2de, 0x0000 }, + { 0xa2df, 0x0000 }, + { 0xa2e0, 0x0000 }, + { 0xa2e1, 0x0000 }, + { 0xa2e2, 0x0000 }, + { 0xa2e3, 0x0000 }, + { 0xa2e4, 0x0000 }, + { 0xa2e5, 0x0000 }, + { 0xa2e6, 0x0000 }, + { 0xa2e7, 0x0000 }, + { 0xa2e8, 0x0000 }, + { 0xa2e9, 0x0000 }, + { 0xa2ea, 0x0000 }, + { 0xa2eb, 0x0000 }, + { 0xa2ec, 0x0000 }, + { 0xa2ed, 0x0000 }, + { 0xa2ee, 0x0000 }, + { 0xa2ef, 0x0000 }, + { 0xa2f0, 0x0000 }, + { 0xa2f1, 0x0000 }, + { 0xa2f2, 0x0000 }, + { 0xa2f3, 0x0000 }, + { 0xa2f4, 0x0000 }, + { 0xa2f5, 0x0000 }, + { 0xa2f6, 0x0000 }, + { 0xa2f7, 0x0000 }, + { 0xa2f8, 0x0000 }, + { 0xa2f9, 0x0000 }, + { 0xa2fa, 0x0000 }, + { 0xa2fb, 0x0000 }, + { 0xa2fc, 0x0000 }, + { 0xa2fd, 0x0000 }, + { 0xa2fe, 0x0000 }, + { 0xa2ff, 0x0000 }, + { 0xa300, 0x0000 }, + { 0xa301, 0x0000 }, + { 0xa302, 0x0000 }, + { 0xa303, 0x0000 }, + { 0xa304, 0x0000 }, + { 0xa305, 0x0000 }, + { 0xa306, 0x0000 }, + { 0xa307, 0x0000 }, + { 0xa308, 0x0000 }, + { 0xa309, 0x0000 }, + { 0xa30a, 0x0000 }, + { 0xa30b, 0x0000 }, + { 0xa30c, 0x0000 }, + { 0xa30d, 0x0000 }, + { 0xa30e, 0x0000 }, + { 0xa30f, 0x0000 }, + { 0xa310, 0x0000 }, + { 0xa311, 0x0000 }, + { 0xa312, 0x0000 }, + { 0xa313, 0x0000 }, + { 0xa314, 0x0000 }, + { 0xa315, 0x0000 }, + { 0xa316, 0x0000 }, + { 0xa317, 0x0000 }, + { 0xa318, 0x0000 }, + { 0xa319, 0x0000 }, + { 0xa31a, 0x0000 }, + { 0xa31b, 0x0000 }, + { 0xa31c, 0x0000 }, + { 0xa31d, 0x0000 }, + { 0xa31e, 0x0000 }, + { 0xa31f, 0x0000 }, + { 0xa320, 0x0000 }, + { 0xa321, 0x0000 }, + { 0xa322, 0x0000 }, + { 0xa323, 0x0000 }, + { 0xa324, 0x0000 }, + { 0xa325, 0x0000 }, + { 0xa326, 0x0000 }, + { 0xa327, 0x0000 }, + { 0xa328, 0x0000 }, + { 0xa329, 0x0000 }, + { 0xa32a, 0x0000 }, + { 0xa32b, 0x0000 }, + { 0xa32c, 0x0000 }, + { 0xa32d, 0x0000 }, + { 0xa32e, 0x0000 }, + { 0xa32f, 0x0000 }, + { 0xa330, 0x0000 }, + { 0xa331, 0x0000 }, + { 0xa332, 0x0000 }, + { 0xa333, 0x0000 }, + { 0xa334, 0x0000 }, + { 0xa335, 0x0000 }, + { 0xa336, 0x0000 }, + { 0xa337, 0x0000 }, + { 0xa338, 0x0000 }, + { 0xa339, 0x0000 }, + { 0xa33a, 0x0000 }, + { 0xa33b, 0x0000 }, + { 0xa33c, 0x0000 }, + { 0xa33d, 0x0000 }, + { 0xa33e, 0x0000 }, + { 0xa33f, 0x0000 }, + { 0xa340, 0x0000 }, + { 0xa341, 0x0000 }, + { 0xa342, 0x0000 }, + { 0xa343, 0x0000 }, + { 0xa344, 0x0000 }, + { 0xa345, 0x0000 }, + { 0xa346, 0x0000 }, + { 0xa347, 0x0000 }, + { 0xa348, 0x0000 }, + { 0xa349, 0x0000 }, + { 0xa34a, 0x0000 }, + { 0xa34b, 0x0000 }, + { 0xa34c, 0x0000 }, + { 0xa34d, 0x0000 }, + { 0xa34e, 0x0000 }, + { 0xa34f, 0x0000 }, + { 0xa350, 0x0000 }, + { 0xa351, 0x0000 }, + { 0xa352, 0x0000 }, + { 0xa353, 0x0000 }, + { 0xa354, 0x0000 }, + { 0xa355, 0x0000 }, + { 0xa356, 0x0000 }, + { 0xa357, 0x0000 }, + { 0xa358, 0x0000 }, + { 0xa359, 0x0000 }, + { 0xa35a, 0x0000 }, + { 0xa35b, 0x0000 }, + { 0xa35c, 0x0000 }, + { 0xa35d, 0x0000 }, + { 0xa35e, 0x0000 }, + { 0xa35f, 0x0000 }, + { 0xa360, 0x0000 }, + { 0xa361, 0x0000 }, + { 0xa362, 0x0000 }, + { 0xa363, 0x0000 }, + { 0xa364, 0x0000 }, + { 0xa365, 0x0000 }, + { 0xa366, 0x0000 }, + { 0xa367, 0x0000 }, + { 0xa368, 0x0000 }, + { 0xa369, 0x0000 }, + { 0xa36a, 0x0000 }, + { 0xa36b, 0x0000 }, + { 0xa36c, 0x0000 }, + { 0xa36d, 0x0000 }, + { 0xa36e, 0x0000 }, + { 0xa36f, 0x0000 }, + { 0xa370, 0x0000 }, + { 0xa371, 0x0000 }, + { 0xa372, 0x0000 }, + { 0xa373, 0x0000 }, + { 0xa374, 0x0000 }, + { 0xa375, 0x0000 }, + { 0xa376, 0x0000 }, + { 0xa377, 0x0000 }, + { 0xa378, 0x0000 }, + { 0xa379, 0x0000 }, + { 0xa37a, 0x0000 }, + { 0xa37b, 0x0000 }, + { 0xa37c, 0x0000 }, + { 0xa37d, 0x0000 }, + { 0xa37e, 0x0000 }, + { 0xa37f, 0x0000 }, + { 0xa380, 0x0000 }, + { 0xa381, 0x0000 }, + { 0xa382, 0x0000 }, + { 0xa383, 0x0000 }, + { 0xa384, 0x0000 }, + { 0xa385, 0x0000 }, + { 0xa386, 0x0000 }, + { 0xa387, 0x0000 }, + { 0xa388, 0x0000 }, + { 0xa389, 0x0000 }, + { 0xa38a, 0x0000 }, + { 0xa38b, 0x0000 }, + { 0xa38c, 0x0000 }, + { 0xa38d, 0x0000 }, + { 0xa38e, 0x0000 }, + { 0xa38f, 0x0000 }, + { 0xa390, 0x0000 }, + { 0xa391, 0x0000 }, + { 0xa392, 0x0000 }, + { 0xa393, 0x0000 }, + { 0xa394, 0x0000 }, + { 0xa395, 0x0000 }, + { 0xa396, 0x0000 }, + { 0xa397, 0x0000 }, + { 0xa398, 0x0000 }, + { 0xa399, 0x0000 }, + { 0xa39a, 0x0000 }, + { 0xa39b, 0x0000 }, + { 0xa39c, 0x0000 }, + { 0xa39d, 0x0000 }, + { 0xa39e, 0x0000 }, + { 0xa39f, 0x0000 }, + { 0xa3a0, 0x0000 }, + { 0xa3a1, 0x0000 }, + { 0xa3a2, 0x0000 }, + { 0xa3a3, 0x0000 }, + { 0xa3a4, 0x0000 }, + { 0xa3a5, 0x0000 }, + { 0xa3a6, 0x0000 }, + { 0xa3a7, 0x0000 }, + { 0xa3a8, 0x0000 }, + { 0xa3a9, 0x0000 }, + { 0xa3aa, 0x0000 }, + { 0xa3ab, 0x0000 }, + { 0xa3ac, 0x0000 }, + { 0xa3ad, 0x0000 }, + { 0xa3ae, 0x0000 }, + { 0xa3af, 0x0000 }, + { 0xa3b0, 0x0000 }, + { 0xa3b1, 0x0000 }, + { 0xa3b2, 0x0000 }, + { 0xa3b3, 0x0000 }, + { 0xa3b4, 0x0000 }, + { 0xa3b5, 0x0000 }, + { 0xa3b6, 0x0000 }, + { 0xa3b7, 0x0000 }, + { 0xa3b8, 0x0000 }, + { 0xa3b9, 0x0000 }, + { 0xa3ba, 0x0000 }, + { 0xa3bb, 0x0000 }, + { 0xa3bc, 0x0000 }, + { 0xa3bd, 0x0000 }, + { 0xa3be, 0x0000 }, + { 0xa3bf, 0x0000 }, + { 0xa3c0, 0x0000 }, + { 0xa3c1, 0x0000 }, + { 0xa3c2, 0x0000 }, + { 0xa3c3, 0x0000 }, + { 0xa3c4, 0x0000 }, + { 0xa3c5, 0x0000 }, + { 0xa3c6, 0x0000 }, + { 0xa3c7, 0x0000 }, + { 0xa3c8, 0x0000 }, + { 0xa3c9, 0x0000 }, + { 0xa3ca, 0x0000 }, + { 0xa3cb, 0x0000 }, + { 0xa3cc, 0x0000 }, + { 0xa3cd, 0x0000 }, + { 0xa3ce, 0x0000 }, + { 0xa3cf, 0x0000 }, + { 0xa3d0, 0x0000 }, + { 0xa3d1, 0x0000 }, + { 0xa3d2, 0x0000 }, + { 0xa3d3, 0x0000 }, + { 0xa3d4, 0x0000 }, + { 0xa3d5, 0x0000 }, + { 0xa3d6, 0x0000 }, + { 0xa3d7, 0x0000 }, + { 0xa3d8, 0x0000 }, + { 0xa3d9, 0x0000 }, + { 0xa3da, 0x0000 }, + { 0xa3db, 0x0000 }, + { 0xa3dc, 0x0000 }, + { 0xa3dd, 0x0000 }, + { 0xa3de, 0x0000 }, + { 0xa3df, 0x0000 }, + { 0xa3e0, 0x0000 }, + { 0xa3e1, 0x0000 }, + { 0xa3e2, 0x0000 }, + { 0xa3e3, 0x0000 }, + { 0xa3e4, 0x0000 }, + { 0xa3e5, 0x0000 }, + { 0xa3e6, 0x0000 }, + { 0xa3e7, 0x0000 }, + { 0xa3e8, 0x0000 }, + { 0xa3e9, 0x0000 }, + { 0xa3ea, 0x0000 }, + { 0xa3eb, 0x0000 }, + { 0xa3ec, 0x0000 }, + { 0xa3ed, 0x0000 }, + { 0xa3ee, 0x0000 }, + { 0xa3ef, 0x0000 }, + { 0xa3f0, 0x0000 }, + { 0xa3f1, 0x0000 }, + { 0xa3f2, 0x0000 }, + { 0xa3f3, 0x0000 }, + { 0xa3f4, 0x0000 }, + { 0xa3f5, 0x0000 }, + { 0xa3f6, 0x0000 }, + { 0xa3f7, 0x0000 }, + { 0xa3f8, 0x0000 }, + { 0xa3f9, 0x0000 }, + { 0xa3fa, 0x0000 }, + { 0xa3fb, 0x0000 }, + { 0xa3fc, 0x0000 }, + { 0xa3fd, 0x0000 }, + { 0xa3fe, 0x0000 }, + { 0xa3ff, 0x0000 }, + { 0xa400, 0x0000 }, + { 0xa401, 0x0000 }, + { 0xa402, 0x0000 }, + { 0xa403, 0x0000 }, + { 0xa404, 0x0000 }, + { 0xa405, 0x0000 }, + { 0xa406, 0x0000 }, + { 0xa407, 0x0000 }, + { 0xa408, 0x0000 }, + { 0xa409, 0x0000 }, + { 0xa40a, 0x0000 }, + { 0xa40b, 0x0000 }, + { 0xa40c, 0x0000 }, + { 0xa40d, 0x0000 }, + { 0xa40e, 0x0000 }, + { 0xa40f, 0x0000 }, + { 0xa410, 0x0000 }, + { 0xa411, 0x0000 }, + { 0xa412, 0x0000 }, + { 0xa413, 0x0000 }, + { 0xa414, 0x0000 }, + { 0xa415, 0x0000 }, + { 0xa416, 0x0000 }, + { 0xa417, 0x0000 }, + { 0xa418, 0x0000 }, + { 0xa419, 0x0000 }, + { 0xa41a, 0x0000 }, + { 0xa41b, 0x0000 }, + { 0xa41c, 0x0000 }, + { 0xa41d, 0x0000 }, + { 0xa41e, 0x0000 }, + { 0xa41f, 0x0000 }, + { 0xa420, 0x0000 }, + { 0xa421, 0x0000 }, + { 0xa422, 0x0000 }, + { 0xa423, 0x0000 }, + { 0xa424, 0x0000 }, + { 0xa425, 0x0000 }, + { 0xa426, 0x0000 }, + { 0xa427, 0x0000 }, + { 0xa428, 0x0000 }, + { 0xa429, 0x0000 }, + { 0xa42a, 0x0000 }, + { 0xa42b, 0x0000 }, + { 0xa42c, 0x0000 }, + { 0xa42d, 0x0000 }, + { 0xa42e, 0x0000 }, + { 0xa42f, 0x0000 }, + { 0xa430, 0x0000 }, + { 0xa431, 0x0000 }, + { 0xa432, 0x0000 }, + { 0xa433, 0x0000 }, + { 0xa434, 0x0000 }, + { 0xa435, 0x0000 }, + { 0xa436, 0x0000 }, + { 0xa437, 0x0000 }, + { 0xa438, 0x0000 }, + { 0xa439, 0x0000 }, + { 0xa43a, 0x0000 }, + { 0xa43b, 0x0000 }, + { 0xa43c, 0x0000 }, + { 0xa43d, 0x0000 }, + { 0xa43e, 0x0000 }, + { 0xa43f, 0x0000 }, + { 0xa440, 0x0000 }, + { 0xa441, 0x0000 }, + { 0xa442, 0x0000 }, + { 0xa443, 0x0000 }, + { 0xa444, 0x0000 }, + { 0xa445, 0x0000 }, + { 0xa446, 0x0000 }, + { 0xa447, 0x0000 }, + { 0xa448, 0x0000 }, + { 0xa449, 0x0000 }, + { 0xa44a, 0x0000 }, + { 0xa44b, 0x0000 }, + { 0xa44c, 0x0000 }, + { 0xa44d, 0x0000 }, + { 0xa44e, 0x0000 }, + { 0xa44f, 0x0000 }, + { 0xa450, 0x0000 }, + { 0xa451, 0x0000 }, + { 0xa452, 0x0000 }, + { 0xa453, 0x0000 }, + { 0xa454, 0x0000 }, + { 0xa455, 0x0000 }, + { 0xa456, 0x0000 }, + { 0xa457, 0x0000 }, + { 0xa458, 0x0000 }, + { 0xa459, 0x0000 }, + { 0xa45a, 0x0000 }, + { 0xa45b, 0x0000 }, + { 0xa45c, 0x0000 }, + { 0xa45d, 0x0000 }, + { 0xa45e, 0x0000 }, + { 0xa45f, 0x0000 }, + { 0xa460, 0x0000 }, + { 0xa461, 0x0000 }, + { 0xa462, 0x0000 }, + { 0xa463, 0x0000 }, + { 0xa464, 0x0000 }, + { 0xa465, 0x0000 }, + { 0xa466, 0x0000 }, + { 0xa467, 0x0000 }, + { 0xa468, 0x0000 }, + { 0xa469, 0x0000 }, + { 0xa46a, 0x0000 }, + { 0xa46b, 0x0000 }, + { 0xa46c, 0x0000 }, + { 0xa46d, 0x0000 }, + { 0xa46e, 0x0000 }, + { 0xa46f, 0x0000 }, + { 0xa470, 0x0000 }, + { 0xa471, 0x0000 }, + { 0xa472, 0x0000 }, + { 0xa473, 0x0000 }, + { 0xa474, 0x0000 }, + { 0xa475, 0x0000 }, + { 0xa476, 0x0000 }, + { 0xa477, 0x0000 }, + { 0xa478, 0x0000 }, + { 0xa479, 0x0000 }, + { 0xa47a, 0x0000 }, + { 0xa47b, 0x0000 }, + { 0xa47c, 0x0000 }, + { 0xa47d, 0x0000 }, + { 0xa47e, 0x0000 }, + { 0xa47f, 0x0000 }, + { 0xa480, 0x0000 }, + { 0xa481, 0x0000 }, + { 0xa482, 0x0000 }, + { 0xa483, 0x0000 }, + { 0xa484, 0x0000 }, + { 0xa485, 0x0000 }, + { 0xa486, 0x0000 }, + { 0xa487, 0x0000 }, + { 0xa488, 0x0000 }, + { 0xa489, 0x0000 }, + { 0xa48a, 0x0000 }, + { 0xa48b, 0x0000 }, + { 0xa48c, 0x0000 }, + { 0xa48d, 0x0000 }, + { 0xa48e, 0x0000 }, + { 0xa48f, 0x0000 }, + { 0xa490, 0x0000 }, + { 0xa491, 0x0000 }, + { 0xa492, 0x0000 }, + { 0xa493, 0x0000 }, + { 0xa494, 0x0000 }, + { 0xa495, 0x0000 }, + { 0xa496, 0x0000 }, + { 0xa497, 0x0000 }, + { 0xa498, 0x0000 }, + { 0xa499, 0x0000 }, + { 0xa49a, 0x0000 }, + { 0xa49b, 0x0000 }, + { 0xa49c, 0x0000 }, + { 0xa49d, 0x0000 }, + { 0xa49e, 0x0000 }, + { 0xa49f, 0x0000 }, + { 0xa4a0, 0x0000 }, + { 0xa4a1, 0x0000 }, + { 0xa4a2, 0x0000 }, + { 0xa4a3, 0x0000 }, + { 0xa4a4, 0x0000 }, + { 0xa4a5, 0x0000 }, + { 0xa4a6, 0x0000 }, + { 0xa4a7, 0x0000 }, + { 0xa4a8, 0x0000 }, + { 0xa4a9, 0x0000 }, + { 0xa4aa, 0x0000 }, + { 0xa4ab, 0x0000 }, + { 0xa4ac, 0x0000 }, + { 0xa4ad, 0x0000 }, + { 0xa4ae, 0x0000 }, + { 0xa4af, 0x0000 }, + { 0xa4b0, 0x0000 }, + { 0xa4b1, 0x0000 }, + { 0xa4b2, 0x0000 }, + { 0xa4b3, 0x0000 }, + { 0xa4b4, 0x0000 }, + { 0xa4b5, 0x0000 }, + { 0xa4b6, 0x0000 }, + { 0xa4b7, 0x0000 }, + { 0xa4b8, 0x0000 }, + { 0xa4b9, 0x0000 }, + { 0xa4ba, 0x0000 }, + { 0xa4bb, 0x0000 }, + { 0xa4bc, 0x0000 }, + { 0xa4bd, 0x0000 }, + { 0xa4be, 0x0000 }, + { 0xa4bf, 0x0000 }, + { 0xa4c0, 0x0000 }, + { 0xa4c1, 0x0000 }, + { 0xa4c2, 0x0000 }, + { 0xa4c3, 0x0000 }, + { 0xa4c4, 0x0000 }, + { 0xa4c5, 0x0000 }, + { 0xa4c6, 0x0000 }, + { 0xa4c7, 0x0000 }, + { 0xa4c8, 0x0000 }, + { 0xa4c9, 0x0000 }, + { 0xa4ca, 0x0000 }, + { 0xa4cb, 0x0000 }, + { 0xa4cc, 0x0000 }, + { 0xa4cd, 0x0000 }, + { 0xa4ce, 0x0000 }, + { 0xa4cf, 0x0000 }, + { 0xa4d0, 0x0000 }, + { 0xa4d1, 0x0000 }, + { 0xa4d2, 0x0000 }, + { 0xa4d3, 0x0000 }, + { 0xa4d4, 0x0000 }, + { 0xa4d5, 0x0000 }, + { 0xa4d6, 0x0000 }, + { 0xa4d7, 0x0000 }, + { 0xa4d8, 0x0000 }, + { 0xa4d9, 0x0000 }, + { 0xa4da, 0x0000 }, + { 0xa4db, 0x0000 }, + { 0xa4dc, 0x0000 }, + { 0xa4dd, 0x0000 }, + { 0xa4de, 0x0000 }, + { 0xa4df, 0x0000 }, + { 0xa4e0, 0x0000 }, + { 0xa4e1, 0x0000 }, + { 0xa4e2, 0x0000 }, + { 0xa4e3, 0x0000 }, + { 0xa4e4, 0x0000 }, + { 0xa4e5, 0x0000 }, + { 0xa4e6, 0x0000 }, + { 0xa4e7, 0x0000 }, + { 0xa4e8, 0x0000 }, + { 0xa4e9, 0x0000 }, + { 0xa4ea, 0x0000 }, + { 0xa4eb, 0x0000 }, + { 0xa4ec, 0x0000 }, + { 0xa4ed, 0x0000 }, + { 0xa4ee, 0x0000 }, + { 0xa4ef, 0x0000 }, + { 0xa4f0, 0x0000 }, + { 0xa4f1, 0x0000 }, + { 0xa4f2, 0x0000 }, + { 0xa4f3, 0x0000 }, + { 0xa4f4, 0x0000 }, + { 0xa4f5, 0x0000 }, + { 0xa4f6, 0x0000 }, + { 0xa4f7, 0x0000 }, + { 0xa4f8, 0x0000 }, + { 0xa4f9, 0x0000 }, + { 0xa4fa, 0x0000 }, + { 0xa4fb, 0x0000 }, + { 0xa4fc, 0x0000 }, + { 0xa4fd, 0x0000 }, + { 0xa4fe, 0x0000 }, + { 0xa4ff, 0x0000 }, + { 0xa500, 0x0000 }, + { 0xa501, 0x0000 }, + { 0xa502, 0x0000 }, + { 0xa503, 0x0000 }, + { 0xa504, 0x0000 }, + { 0xa505, 0x0000 }, + { 0xa506, 0x0000 }, + { 0xa507, 0x0000 }, + { 0xa508, 0x0000 }, + { 0xa509, 0x0000 }, + { 0xa50a, 0x0000 }, + { 0xa50b, 0x0000 }, + { 0xa50c, 0x0000 }, + { 0xa50d, 0x0000 }, + { 0xa50e, 0x0000 }, + { 0xa50f, 0x0000 }, + { 0xa510, 0x0000 }, + { 0xa511, 0x0000 }, + { 0xa512, 0x0000 }, + { 0xa513, 0x0000 }, + { 0xa514, 0x0000 }, + { 0xa515, 0x0000 }, + { 0xa516, 0x0000 }, + { 0xa517, 0x0000 }, + { 0xa518, 0x0000 }, + { 0xa519, 0x0000 }, + { 0xa51a, 0x0000 }, + { 0xa51b, 0x0000 }, + { 0xa51c, 0x0000 }, + { 0xa51d, 0x0000 }, + { 0xa51e, 0x0000 }, + { 0xa51f, 0x0000 }, + { 0xa520, 0x0000 }, + { 0xa521, 0x0000 }, + { 0xa522, 0x0000 }, + { 0xa523, 0x0000 }, + { 0xa524, 0x0000 }, + { 0xa525, 0x0000 }, + { 0xa526, 0x0000 }, + { 0xa527, 0x0000 }, + { 0xa528, 0x0000 }, + { 0xa529, 0x0000 }, + { 0xa52a, 0x0000 }, + { 0xa52b, 0x0000 }, + { 0xa52c, 0x0000 }, + { 0xa52d, 0x0000 }, + { 0xa52e, 0x0000 }, + { 0xa52f, 0x0000 }, + { 0xa530, 0x0000 }, + { 0xa531, 0x0000 }, + { 0xa532, 0x0000 }, + { 0xa533, 0x0000 }, + { 0xa534, 0x0000 }, + { 0xa535, 0x0000 }, + { 0xa536, 0x0000 }, + { 0xa537, 0x0000 }, + { 0xa538, 0x0000 }, + { 0xa539, 0x0000 }, + { 0xa53a, 0x0000 }, + { 0xa53b, 0x0000 }, + { 0xa53c, 0x0000 }, + { 0xa53d, 0x0000 }, + { 0xa53e, 0x0000 }, + { 0xa53f, 0x0000 }, + { 0xa540, 0x0000 }, + { 0xa541, 0x0000 }, + { 0xa542, 0x0000 }, + { 0xa543, 0x0000 }, + { 0xa544, 0x0000 }, + { 0xa545, 0x0000 }, + { 0xa546, 0x0000 }, + { 0xa547, 0x0000 }, + { 0xa548, 0x0000 }, + { 0xa549, 0x0000 }, + { 0xa54a, 0x0000 }, + { 0xa54b, 0x0000 }, + { 0xa54c, 0x0000 }, + { 0xa54d, 0x0000 }, + { 0xa54e, 0x0000 }, + { 0xa54f, 0x0000 }, + { 0xa550, 0x0000 }, + { 0xa551, 0x0000 }, + { 0xa552, 0x0000 }, + { 0xa553, 0x0000 }, + { 0xa554, 0x0000 }, + { 0xa555, 0x0000 }, + { 0xa556, 0x0000 }, + { 0xa557, 0x0000 }, + { 0xa558, 0x0000 }, + { 0xa559, 0x0000 }, + { 0xa55a, 0x0000 }, + { 0xa55b, 0x0000 }, + { 0xa55c, 0x0000 }, + { 0xa55d, 0x0000 }, + { 0xa55e, 0x0000 }, + { 0xa55f, 0x0000 }, + { 0xa560, 0x0000 }, + { 0xa561, 0x0000 }, + { 0xa562, 0x0000 }, + { 0xa563, 0x0000 }, + { 0xa564, 0x0000 }, + { 0xa565, 0x0000 }, + { 0xa566, 0x0000 }, + { 0xa567, 0x0000 }, + { 0xa568, 0x0000 }, + { 0xa569, 0x0000 }, + { 0xa56a, 0x0000 }, + { 0xa56b, 0x0000 }, + { 0xa56c, 0x0000 }, + { 0xa56d, 0x0000 }, + { 0xa56e, 0x0000 }, + { 0xa56f, 0x0000 }, + { 0xa570, 0x0000 }, + { 0xa571, 0x0000 }, + { 0xa572, 0x0000 }, + { 0xa573, 0x0000 }, + { 0xa574, 0x0000 }, + { 0xa575, 0x0000 }, + { 0xa576, 0x0000 }, + { 0xa577, 0x0000 }, + { 0xa578, 0x0000 }, + { 0xa579, 0x0000 }, + { 0xa57a, 0x0000 }, + { 0xa57b, 0x0000 }, + { 0xa57c, 0x0000 }, + { 0xa57d, 0x0000 }, + { 0xa57e, 0x0000 }, + { 0xa57f, 0x0000 }, + { 0xa580, 0x0000 }, + { 0xa581, 0x0000 }, + { 0xa582, 0x0000 }, + { 0xa583, 0x0000 }, + { 0xa584, 0x0000 }, + { 0xa585, 0x0000 }, + { 0xa586, 0x0000 }, + { 0xa587, 0x0000 }, + { 0xa588, 0x0000 }, + { 0xa589, 0x0000 }, + { 0xa58a, 0x0000 }, + { 0xa58b, 0x0000 }, + { 0xa58c, 0x0000 }, + { 0xa58d, 0x0000 }, + { 0xa58e, 0x0000 }, + { 0xa58f, 0x0000 }, + { 0xa590, 0x0000 }, + { 0xa591, 0x0000 }, + { 0xa592, 0x0000 }, + { 0xa593, 0x0000 }, + { 0xa594, 0x0000 }, + { 0xa595, 0x0000 }, + { 0xa596, 0x0000 }, + { 0xa597, 0x0000 }, + { 0xa598, 0x0000 }, + { 0xa599, 0x0000 }, + { 0xa59a, 0x0000 }, + { 0xa59b, 0x0000 }, + { 0xa59c, 0x0000 }, + { 0xa59d, 0x0000 }, + { 0xa59e, 0x0000 }, + { 0xa59f, 0x0000 }, + { 0xa5a0, 0x0000 }, + { 0xa5a1, 0x0000 }, + { 0xa5a2, 0x0000 }, + { 0xa5a3, 0x0000 }, + { 0xa5a4, 0x0000 }, + { 0xa5a5, 0x0000 }, + { 0xa5a6, 0x0000 }, + { 0xa5a7, 0x0000 }, + { 0xa5a8, 0x0000 }, + { 0xa5a9, 0x0000 }, + { 0xa5aa, 0x0000 }, + { 0xa5ab, 0x0000 }, + { 0xa5ac, 0x0000 }, + { 0xa5ad, 0x0000 }, + { 0xa5ae, 0x0000 }, + { 0xa5af, 0x0000 }, + { 0xa5b0, 0x0000 }, + { 0xa5b1, 0x0000 }, + { 0xa5b2, 0x0000 }, + { 0xa5b3, 0x0000 }, + { 0xa5b4, 0x0000 }, + { 0xa5b5, 0x0000 }, + { 0xa5b6, 0x0000 }, + { 0xa5b7, 0x0000 }, + { 0xa5b8, 0x0000 }, + { 0xa5b9, 0x0000 }, + { 0xa5ba, 0x0000 }, + { 0xa5bb, 0x0000 }, + { 0xa5bc, 0x0000 }, + { 0xa5bd, 0x0000 }, + { 0xa5be, 0x0000 }, + { 0xa5bf, 0x0000 }, + { 0xa5c0, 0x0000 }, + { 0xa5c1, 0x0000 }, + { 0xa5c2, 0x0000 }, + { 0xa5c3, 0x0000 }, + { 0xa5c4, 0x0000 }, + { 0xa5c5, 0x0000 }, + { 0xa5c6, 0x0000 }, + { 0xa5c7, 0x0000 }, + { 0xa5c8, 0x0000 }, + { 0xa5c9, 0x0000 }, + { 0xa5ca, 0x0000 }, + { 0xa5cb, 0x0000 }, + { 0xa5cc, 0x0000 }, + { 0xa5cd, 0x0000 }, + { 0xa5ce, 0x0000 }, + { 0xa5cf, 0x0000 }, + { 0xa5d0, 0x0000 }, + { 0xa5d1, 0x0000 }, + { 0xa5d2, 0x0000 }, + { 0xa5d3, 0x0000 }, + { 0xa5d4, 0x0000 }, + { 0xa5d5, 0x0000 }, + { 0xa5d6, 0x0000 }, + { 0xa5d7, 0x0000 }, + { 0xa5d8, 0x0000 }, + { 0xa5d9, 0x0000 }, + { 0xa5da, 0x0000 }, + { 0xa5db, 0x0000 }, + { 0xa5dc, 0x0000 }, + { 0xa5dd, 0x0000 }, + { 0xa5de, 0x0000 }, + { 0xa5df, 0x0000 }, + { 0xa5e0, 0x0000 }, + { 0xa5e1, 0x0000 }, + { 0xa5e2, 0x0000 }, + { 0xa5e3, 0x0000 }, + { 0xa5e4, 0x0000 }, + { 0xa5e5, 0x0000 }, + { 0xa5e6, 0x0000 }, + { 0xa5e7, 0x0000 }, + { 0xa5e8, 0x0000 }, + { 0xa5e9, 0x0000 }, + { 0xa5ea, 0x0000 }, + { 0xa5eb, 0x0000 }, + { 0xa5ec, 0x0000 }, + { 0xa5ed, 0x0000 }, + { 0xa5ee, 0x0000 }, + { 0xa5ef, 0x0000 }, + { 0xa5f0, 0x0000 }, + { 0xa5f1, 0x0000 }, + { 0xa5f2, 0x0000 }, + { 0xa5f3, 0x0000 }, + { 0xa5f4, 0x0000 }, + { 0xa5f5, 0x0000 }, + { 0xa5f6, 0x0000 }, + { 0xa5f7, 0x0000 }, + { 0xa5f8, 0x0000 }, + { 0xa5f9, 0x0000 }, + { 0xa5fa, 0x0000 }, + { 0xa5fb, 0x0000 }, + { 0xa5fc, 0x0000 }, + { 0xa5fd, 0x0000 }, + { 0xa5fe, 0x0000 }, + { 0xa5ff, 0x0000 }, + { 0xa600, 0x0000 }, + { 0xa601, 0x0000 }, + { 0xa602, 0x0000 }, + { 0xa603, 0x0000 }, + { 0xa604, 0x0000 }, + { 0xa605, 0x0000 }, + { 0xa606, 0x0000 }, + { 0xa607, 0x0000 }, + { 0xa608, 0x0000 }, + { 0xa609, 0x0000 }, + { 0xa60a, 0x0000 }, + { 0xa60b, 0x0000 }, + { 0xa60c, 0x0000 }, + { 0xa60d, 0x0000 }, + { 0xa60e, 0x0000 }, + { 0xa60f, 0x0000 }, + { 0xa610, 0x0000 }, + { 0xa611, 0x0000 }, + { 0xa612, 0x0000 }, + { 0xa613, 0x0000 }, + { 0xa614, 0x0000 }, + { 0xa615, 0x0000 }, + { 0xa616, 0x0000 }, + { 0xa617, 0x0000 }, + { 0xa618, 0x0000 }, + { 0xa619, 0x0000 }, + { 0xa61a, 0x0000 }, + { 0xa61b, 0x0000 }, + { 0xa61c, 0x0000 }, + { 0xa61d, 0x0000 }, + { 0xa61e, 0x0000 }, + { 0xa61f, 0x0000 }, + { 0xa620, 0x0000 }, + { 0xa621, 0x0000 }, + { 0xa622, 0x0000 }, + { 0xa623, 0x0000 }, + { 0xa624, 0x0000 }, + { 0xa625, 0x0000 }, + { 0xa626, 0x0000 }, + { 0xa627, 0x0000 }, + { 0xa628, 0x0000 }, + { 0xa629, 0x0000 }, + { 0xa62a, 0x0000 }, + { 0xa62b, 0x0000 }, + { 0xa62c, 0x0000 }, + { 0xa62d, 0x0000 }, + { 0xa62e, 0x0000 }, + { 0xa62f, 0x0000 }, + { 0xa630, 0x0000 }, + { 0xa631, 0x0000 }, + { 0xa632, 0x0000 }, + { 0xa633, 0x0000 }, + { 0xa634, 0x0000 }, + { 0xa635, 0x0000 }, + { 0xa636, 0x0000 }, + { 0xa637, 0x0000 }, + { 0xa638, 0x0000 }, + { 0xa639, 0x0000 }, + { 0xa63a, 0x0000 }, + { 0xa63b, 0x0000 }, + { 0xa63c, 0x0000 }, + { 0xa63d, 0x0000 }, + { 0xa63e, 0x0000 }, + { 0xa63f, 0x0000 }, + { 0xa640, 0x0000 }, + { 0xa641, 0x0000 }, + { 0xa642, 0x0000 }, + { 0xa643, 0x0000 }, + { 0xa644, 0x0000 }, + { 0xa645, 0x0000 }, + { 0xa646, 0x0000 }, + { 0xa647, 0x0000 }, + { 0xa648, 0x0000 }, + { 0xa649, 0x0000 }, + { 0xa64a, 0x0000 }, + { 0xa64b, 0x0000 }, + { 0xa64c, 0x0000 }, + { 0xa64d, 0x0000 }, + { 0xa64e, 0x0000 }, + { 0xa64f, 0x0000 }, + { 0xa650, 0x0000 }, + { 0xa651, 0x0000 }, + { 0xa652, 0x0000 }, + { 0xa653, 0x0000 }, + { 0xa654, 0x0000 }, + { 0xa655, 0x0000 }, + { 0xa656, 0x0000 }, + { 0xa657, 0x0000 }, + { 0xa658, 0x0000 }, + { 0xa659, 0x0000 }, + { 0xa65a, 0x0000 }, + { 0xa65b, 0x0000 }, + { 0xa65c, 0x0000 }, + { 0xa65d, 0x0000 }, + { 0xa65e, 0x0000 }, + { 0xa65f, 0x0000 }, + { 0xa660, 0x0000 }, + { 0xa661, 0x0000 }, + { 0xa662, 0x0000 }, + { 0xa663, 0x0000 }, + { 0xa664, 0x0000 }, + { 0xa665, 0x0000 }, + { 0xa666, 0x0000 }, + { 0xa667, 0x0000 }, + { 0xa668, 0x0000 }, + { 0xa669, 0x0000 }, + { 0xa66a, 0x0000 }, + { 0xa66b, 0x0000 }, + { 0xa66c, 0x0000 }, + { 0xa66d, 0x0000 }, + { 0xa66e, 0x0000 }, + { 0xa66f, 0x0000 }, + { 0xa670, 0x0000 }, + { 0xa671, 0x0000 }, + { 0xa672, 0x0000 }, + { 0xa673, 0x0000 }, + { 0xa674, 0x0000 }, + { 0xa675, 0x0000 }, + { 0xa676, 0x0000 }, + { 0xa677, 0x0000 }, + { 0xa678, 0x0000 }, + { 0xa679, 0x0000 }, + { 0xa67a, 0x0000 }, + { 0xa67b, 0x0000 }, + { 0xa67c, 0x0000 }, + { 0xa67d, 0x0000 }, + { 0xa67e, 0x0000 }, + { 0xa67f, 0x0000 }, + { 0xa680, 0x0000 }, + { 0xa681, 0x0000 }, + { 0xa682, 0x0000 }, + { 0xa683, 0x0000 }, + { 0xa684, 0x0000 }, + { 0xa685, 0x0000 }, + { 0xa686, 0x0000 }, + { 0xa687, 0x0000 }, + { 0xa688, 0x0000 }, + { 0xa689, 0x0000 }, + { 0xa68a, 0x0000 }, + { 0xa68b, 0x0000 }, + { 0xa68c, 0x0000 }, + { 0xa68d, 0x0000 }, + { 0xa68e, 0x0000 }, + { 0xa68f, 0x0000 }, + { 0xa690, 0x0000 }, + { 0xa691, 0x0000 }, + { 0xa692, 0x0000 }, + { 0xa693, 0x0000 }, + { 0xa694, 0x0000 }, + { 0xa695, 0x0000 }, + { 0xa696, 0x0000 }, + { 0xa697, 0x0000 }, + { 0xa698, 0x0000 }, + { 0xa699, 0x0000 }, + { 0xa69a, 0x0000 }, + { 0xa69b, 0x0000 }, + { 0xa69c, 0x0000 }, + { 0xa69d, 0x0000 }, + { 0xa69e, 0x0000 }, + { 0xa69f, 0x0000 }, + { 0xa6a0, 0x0000 }, + { 0xa6a1, 0x0000 }, + { 0xa6a2, 0x0000 }, + { 0xa6a3, 0x0000 }, + { 0xa6a4, 0x0000 }, + { 0xa6a5, 0x0000 }, + { 0xa6a6, 0x0000 }, + { 0xa6a7, 0x0000 }, + { 0xa6a8, 0x0000 }, + { 0xa6a9, 0x0000 }, + { 0xa6aa, 0x0000 }, + { 0xa6ab, 0x0000 }, + { 0xa6ac, 0x0000 }, + { 0xa6ad, 0x0000 }, + { 0xa6ae, 0x0000 }, + { 0xa6af, 0x0000 }, + { 0xa6b0, 0x0000 }, + { 0xa6b1, 0x0000 }, + { 0xa6b2, 0x0000 }, + { 0xa6b3, 0x0000 }, + { 0xa6b4, 0x0000 }, + { 0xa6b5, 0x0000 }, + { 0xa6b6, 0x0000 }, + { 0xa6b7, 0x0000 }, + { 0xa6b8, 0x0000 }, + { 0xa6b9, 0x0000 }, + { 0xa6ba, 0x0000 }, + { 0xa6bb, 0x0000 }, + { 0xa6bc, 0x0000 }, + { 0xa6bd, 0x0000 }, + { 0xa6be, 0x0000 }, + { 0xa6bf, 0x0000 }, + { 0xa6c0, 0x0000 }, + { 0xa6c1, 0x0000 }, + { 0xa6c2, 0x0000 }, + { 0xa6c3, 0x0000 }, + { 0xa6c4, 0x0000 }, + { 0xa6c5, 0x0000 }, + { 0xa6c6, 0x0000 }, + { 0xa6c7, 0x0000 }, + { 0xa6c8, 0x0000 }, + { 0xa6c9, 0x0000 }, + { 0xa6ca, 0x0000 }, + { 0xa6cb, 0x0000 }, + { 0xa6cc, 0x0000 }, + { 0xa6cd, 0x0000 }, + { 0xa6ce, 0x0000 }, + { 0xa6cf, 0x0000 }, + { 0xa6d0, 0x0000 }, + { 0xa6d1, 0x0000 }, + { 0xa6d2, 0x0000 }, + { 0xa6d3, 0x0000 }, + { 0xa6d4, 0x0000 }, + { 0xa6d5, 0x0000 }, + { 0xa6d6, 0x0000 }, + { 0xa6d7, 0x0000 }, + { 0xa6d8, 0x0000 }, + { 0xa6d9, 0x0000 }, + { 0xa6da, 0x0000 }, + { 0xa6db, 0x0000 }, + { 0xa6dc, 0x0000 }, + { 0xa6dd, 0x0000 }, + { 0xa6de, 0x0000 }, + { 0xa6df, 0x0000 }, + { 0xa6e0, 0x0000 }, + { 0xa6e1, 0x0000 }, + { 0xa6e2, 0x0000 }, + { 0xa6e3, 0x0000 }, + { 0xa6e4, 0x0000 }, + { 0xa6e5, 0x0000 }, + { 0xa6e6, 0x0000 }, + { 0xa6e7, 0x0000 }, + { 0xa6e8, 0x0000 }, + { 0xa6e9, 0x0000 }, + { 0xa6ea, 0x0000 }, + { 0xa6eb, 0x0000 }, + { 0xa6ec, 0x0000 }, + { 0xa6ed, 0x0000 }, + { 0xa6ee, 0x0000 }, + { 0xa6ef, 0x0000 }, + { 0xa6f0, 0x0000 }, + { 0xa6f1, 0x0000 }, + { 0xa6f2, 0x0000 }, + { 0xa6f3, 0x0000 }, + { 0xa6f4, 0x0000 }, + { 0xa6f5, 0x0000 }, + { 0xa6f6, 0x0000 }, + { 0xa6f7, 0x0000 }, + { 0xa6f8, 0x0000 }, + { 0xa6f9, 0x0000 }, + { 0xa6fa, 0x0000 }, + { 0xa6fb, 0x0000 }, + { 0xa6fc, 0x0000 }, + { 0xa6fd, 0x0000 }, + { 0xa6fe, 0x0000 }, + { 0xa6ff, 0x0000 }, + { 0xa700, 0x0000 }, + { 0xa701, 0x0000 }, + { 0xa702, 0x0000 }, + { 0xa703, 0x0000 }, + { 0xa704, 0x0000 }, + { 0xa705, 0x0000 }, + { 0xa706, 0x0000 }, + { 0xa707, 0x0000 }, + { 0xa708, 0x0000 }, + { 0xa709, 0x0000 }, + { 0xa70a, 0x0000 }, + { 0xa70b, 0x0000 }, + { 0xa70c, 0x0000 }, + { 0xa70d, 0x0000 }, + { 0xa70e, 0x0000 }, + { 0xa70f, 0x0000 }, + { 0xa710, 0x0000 }, + { 0xa711, 0x0000 }, + { 0xa712, 0x0000 }, + { 0xa713, 0x0000 }, + { 0xa714, 0x0000 }, + { 0xa715, 0x0000 }, + { 0xa716, 0x0000 }, + { 0xa717, 0x0000 }, + { 0xa718, 0x0000 }, + { 0xa719, 0x0000 }, + { 0xa71a, 0x0000 }, + { 0xa71b, 0x0000 }, + { 0xa71c, 0x0000 }, + { 0xa71d, 0x0000 }, + { 0xa71e, 0x0000 }, + { 0xa71f, 0x0000 }, + { 0xa720, 0x0000 }, + { 0xa721, 0x0000 }, + { 0xa722, 0x0000 }, + { 0xa723, 0x0000 }, + { 0xa724, 0x0000 }, + { 0xa725, 0x0000 }, + { 0xa726, 0x0000 }, + { 0xa727, 0x0000 }, + { 0xa728, 0x0000 }, + { 0xa729, 0x0000 }, + { 0xa72a, 0x0000 }, + { 0xa72b, 0x0000 }, + { 0xa72c, 0x0000 }, + { 0xa72d, 0x0000 }, + { 0xa72e, 0x0000 }, + { 0xa72f, 0x0000 }, + { 0xa730, 0x0000 }, + { 0xa731, 0x0000 }, + { 0xa732, 0x0000 }, + { 0xa733, 0x0000 }, + { 0xa734, 0x0000 }, + { 0xa735, 0x0000 }, + { 0xa736, 0x0000 }, + { 0xa737, 0x0000 }, + { 0xa738, 0x0000 }, + { 0xa739, 0x0000 }, + { 0xa73a, 0x0000 }, + { 0xa73b, 0x0000 }, + { 0xa73c, 0x0000 }, + { 0xa73d, 0x0000 }, + { 0xa73e, 0x0000 }, + { 0xa73f, 0x0000 }, + { 0xa740, 0x0000 }, + { 0xa741, 0x0000 }, + { 0xa742, 0x0000 }, + { 0xa743, 0x0000 }, + { 0xa744, 0x0000 }, + { 0xa745, 0x0000 }, + { 0xa746, 0x0000 }, + { 0xa747, 0x0000 }, + { 0xa748, 0x0000 }, + { 0xa749, 0x0000 }, + { 0xa74a, 0x0000 }, + { 0xa74b, 0x0000 }, + { 0xa74c, 0x0000 }, + { 0xa74d, 0x0000 }, + { 0xa74e, 0x0000 }, + { 0xa74f, 0x0000 }, + { 0xa750, 0x0000 }, + { 0xa751, 0x0000 }, + { 0xa752, 0x0000 }, + { 0xa753, 0x0000 }, + { 0xa754, 0x0000 }, + { 0xa755, 0x0000 }, + { 0xa756, 0x0000 }, + { 0xa757, 0x0000 }, + { 0xa758, 0x0000 }, + { 0xa759, 0x0000 }, + { 0xa75a, 0x0000 }, + { 0xa75b, 0x0000 }, + { 0xa75c, 0x0000 }, + { 0xa75d, 0x0000 }, + { 0xa75e, 0x0000 }, + { 0xa75f, 0x0000 }, + { 0xa760, 0x0000 }, + { 0xa761, 0x0000 }, + { 0xa762, 0x0000 }, + { 0xa763, 0x0000 }, + { 0xa764, 0x0000 }, + { 0xa765, 0x0000 }, + { 0xa766, 0x0000 }, + { 0xa767, 0x0000 }, + { 0xa768, 0x0000 }, + { 0xa769, 0x0000 }, + { 0xa76a, 0x0000 }, + { 0xa76b, 0x0000 }, + { 0xa76c, 0x0000 }, + { 0xa76d, 0x0000 }, + { 0xa76e, 0x0000 }, + { 0xa76f, 0x0000 }, + { 0xa770, 0x0000 }, + { 0xa771, 0x0000 }, + { 0xa772, 0x0000 }, + { 0xa773, 0x0000 }, + { 0xa774, 0x0000 }, + { 0xa775, 0x0000 }, + { 0xa776, 0x0000 }, + { 0xa777, 0x0000 }, + { 0xa778, 0x0000 }, + { 0xa779, 0x0000 }, + { 0xa77a, 0x0000 }, + { 0xa77b, 0x0000 }, + { 0xa77c, 0x0000 }, + { 0xa77d, 0x0000 }, + { 0xa77e, 0x0000 }, + { 0xa77f, 0x0000 }, + { 0xa780, 0x0000 }, + { 0xa781, 0x0000 }, + { 0xa782, 0x0000 }, + { 0xa783, 0x0000 }, + { 0xa784, 0x0000 }, + { 0xa785, 0x0000 }, + { 0xa786, 0x0000 }, + { 0xa787, 0x0000 }, + { 0xa788, 0x0000 }, + { 0xa789, 0x0000 }, + { 0xa78a, 0x0000 }, + { 0xa78b, 0x0000 }, + { 0xa78c, 0x0000 }, + { 0xa78d, 0x0000 }, + { 0xa78e, 0x0000 }, + { 0xa78f, 0x0000 }, + { 0xa790, 0x0000 }, + { 0xa791, 0x0000 }, + { 0xa792, 0x0000 }, + { 0xa793, 0x0000 }, + { 0xa794, 0x0000 }, + { 0xa795, 0x0000 }, + { 0xa796, 0x0000 }, + { 0xa797, 0x0000 }, + { 0xa798, 0x0000 }, + { 0xa799, 0x0000 }, + { 0xa79a, 0x0000 }, + { 0xa79b, 0x0000 }, + { 0xa79c, 0x0000 }, + { 0xa79d, 0x0000 }, + { 0xa79e, 0x0000 }, + { 0xa79f, 0x0000 }, + { 0xa7a0, 0x0000 }, + { 0xa7a1, 0x0000 }, + { 0xa7a2, 0x0000 }, + { 0xa7a3, 0x0000 }, + { 0xa7a4, 0x0000 }, + { 0xa7a5, 0x0000 }, + { 0xa7a6, 0x0000 }, + { 0xa7a7, 0x0000 }, + { 0xa7a8, 0x0000 }, + { 0xa7a9, 0x0000 }, + { 0xa7aa, 0x0000 }, + { 0xa7ab, 0x0000 }, + { 0xa7ac, 0x0000 }, + { 0xa7ad, 0x0000 }, + { 0xa7ae, 0x0000 }, + { 0xa7af, 0x0000 }, + { 0xa7b0, 0x0000 }, + { 0xa7b1, 0x0000 }, + { 0xa7b2, 0x0000 }, + { 0xa7b3, 0x0000 }, + { 0xa7b4, 0x0000 }, + { 0xa7b5, 0x0000 }, + { 0xa7b6, 0x0000 }, + { 0xa7b7, 0x0000 }, + { 0xa7b8, 0x0000 }, + { 0xa7b9, 0x0000 }, + { 0xa7ba, 0x0000 }, + { 0xa7bb, 0x0000 }, + { 0xa7bc, 0x0000 }, + { 0xa7bd, 0x0000 }, + { 0xa7be, 0x0000 }, + { 0xa7bf, 0x0000 }, + { 0xa7c0, 0x0000 }, + { 0xa7c1, 0x0000 }, + { 0xa7c2, 0x0000 }, + { 0xa7c3, 0x0000 }, + { 0xa7c4, 0x0000 }, + { 0xa7c5, 0x0000 }, + { 0xa7c6, 0x0000 }, + { 0xa7c7, 0x0000 }, + { 0xa7c8, 0x0000 }, + { 0xa7c9, 0x0000 }, + { 0xa7ca, 0x0000 }, + { 0xa7cb, 0x0000 }, + { 0xa7cc, 0x0000 }, + { 0xa7cd, 0x0000 }, + { 0xa7ce, 0x0000 }, + { 0xa7cf, 0x0000 }, + { 0xa7d0, 0x0000 }, + { 0xa7d1, 0x0000 }, + { 0xa7d2, 0x0000 }, + { 0xa7d3, 0x0000 }, + { 0xa7d4, 0x0000 }, + { 0xa7d5, 0x0000 }, + { 0xa7d6, 0x0000 }, + { 0xa7d7, 0x0000 }, + { 0xa7d8, 0x0000 }, + { 0xa7d9, 0x0000 }, + { 0xa7da, 0x0000 }, + { 0xa7db, 0x0000 }, + { 0xa7dc, 0x0000 }, + { 0xa7dd, 0x0000 }, + { 0xa7de, 0x0000 }, + { 0xa7df, 0x0000 }, + { 0xa7e0, 0x0000 }, + { 0xa7e1, 0x0000 }, + { 0xa7e2, 0x0000 }, + { 0xa7e3, 0x0000 }, + { 0xa7e4, 0x0000 }, + { 0xa7e5, 0x0000 }, + { 0xa7e6, 0x0000 }, + { 0xa7e7, 0x0000 }, + { 0xa7e8, 0x0000 }, + { 0xa7e9, 0x0000 }, + { 0xa7ea, 0x0000 }, + { 0xa7eb, 0x0000 }, + { 0xa7ec, 0x0000 }, + { 0xa7ed, 0x0000 }, + { 0xa7ee, 0x0000 }, + { 0xa7ef, 0x0000 }, + { 0xa7f0, 0x0000 }, + { 0xa7f1, 0x0000 }, + { 0xa7f2, 0x0000 }, + { 0xa7f3, 0x0000 }, + { 0xa7f4, 0x0000 }, + { 0xa7f5, 0x0000 }, + { 0xa7f6, 0x0000 }, + { 0xa7f7, 0x0000 }, + { 0xa7f8, 0x0000 }, + { 0xa7f9, 0x0000 }, + { 0xa7fa, 0x0000 }, + { 0xa7fb, 0x0000 }, + { 0xa7fc, 0x0000 }, + { 0xa7fd, 0x0000 }, + { 0xa7fe, 0x0000 }, + { 0xa7ff, 0x0000 }, + { 0xa800, 0x0000 }, + { 0xa801, 0x0000 }, + { 0xa802, 0x0000 }, + { 0xa803, 0x0000 }, + { 0xa804, 0x0000 }, + { 0xa805, 0x0000 }, + { 0xa806, 0x0000 }, + { 0xa807, 0x0000 }, + { 0xa808, 0x0000 }, + { 0xa809, 0x0000 }, + { 0xa80a, 0x0000 }, + { 0xa80b, 0x0000 }, + { 0xa80c, 0x0000 }, + { 0xa80d, 0x0000 }, + { 0xa80e, 0x0000 }, + { 0xa80f, 0x0000 }, + { 0xa810, 0x0000 }, + { 0xa811, 0x0000 }, + { 0xa812, 0x0000 }, + { 0xa813, 0x0000 }, + { 0xa814, 0x0000 }, + { 0xa815, 0x0000 }, + { 0xa816, 0x0000 }, + { 0xa817, 0x0000 }, + { 0xa818, 0x0000 }, + { 0xa819, 0x0000 }, + { 0xa81a, 0x0000 }, + { 0xa81b, 0x0000 }, + { 0xa81c, 0x0000 }, + { 0xa81d, 0x0000 }, + { 0xa81e, 0x0000 }, + { 0xa81f, 0x0000 }, + { 0xa820, 0x0000 }, + { 0xa821, 0x0000 }, + { 0xa822, 0x0000 }, + { 0xa823, 0x0000 }, + { 0xa824, 0x0000 }, + { 0xa825, 0x0000 }, + { 0xa826, 0x0000 }, + { 0xa827, 0x0000 }, + { 0xa828, 0x0000 }, + { 0xa829, 0x0000 }, + { 0xa82a, 0x0000 }, + { 0xa82b, 0x0000 }, + { 0xa82c, 0x0000 }, + { 0xa82d, 0x0000 }, + { 0xa82e, 0x0000 }, + { 0xa82f, 0x0000 }, + { 0xa830, 0x0000 }, + { 0xa831, 0x0000 }, + { 0xa832, 0x0000 }, + { 0xa833, 0x0000 }, + { 0xa834, 0x0000 }, + { 0xa835, 0x0000 }, + { 0xa836, 0x0000 }, + { 0xa837, 0x0000 }, + { 0xa838, 0x0000 }, + { 0xa839, 0x0000 }, + { 0xa83a, 0x0000 }, + { 0xa83b, 0x0000 }, + { 0xa83c, 0x0000 }, + { 0xa83d, 0x0000 }, + { 0xa83e, 0x0000 }, + { 0xa83f, 0x0000 }, + { 0xa840, 0x0000 }, + { 0xa841, 0x0000 }, + { 0xa842, 0x0000 }, + { 0xa843, 0x0000 }, + { 0xa844, 0x0000 }, + { 0xa845, 0x0000 }, + { 0xa846, 0x0000 }, + { 0xa847, 0x0000 }, + { 0xa848, 0x0000 }, + { 0xa849, 0x0000 }, + { 0xa84a, 0x0000 }, + { 0xa84b, 0x0000 }, + { 0xa84c, 0x0000 }, + { 0xa84d, 0x0000 }, + { 0xa84e, 0x0000 }, + { 0xa84f, 0x0000 }, + { 0xa850, 0x0000 }, + { 0xa851, 0x0000 }, + { 0xa852, 0x0000 }, + { 0xa853, 0x0000 }, + { 0xa854, 0x0000 }, + { 0xa855, 0x0000 }, + { 0xa856, 0x0000 }, + { 0xa857, 0x0000 }, + { 0xa858, 0x0000 }, + { 0xa859, 0x0000 }, + { 0xa85a, 0x0000 }, + { 0xa85b, 0x0000 }, + { 0xa85c, 0x0000 }, + { 0xa85d, 0x0000 }, + { 0xa85e, 0x0000 }, + { 0xa85f, 0x0000 }, + { 0xa860, 0x0000 }, + { 0xa861, 0x0000 }, + { 0xa862, 0x0000 }, + { 0xa863, 0x0000 }, + { 0xa864, 0x0000 }, + { 0xa865, 0x0000 }, + { 0xa866, 0x0000 }, + { 0xa867, 0x0000 }, + { 0xa868, 0x0000 }, + { 0xa869, 0x0000 }, + { 0xa86a, 0x0000 }, + { 0xa86b, 0x0000 }, + { 0xa86c, 0x0000 }, + { 0xa86d, 0x0000 }, + { 0xa86e, 0x0000 }, + { 0xa86f, 0x0000 }, + { 0xa870, 0x0000 }, + { 0xa871, 0x0000 }, + { 0xa872, 0x0000 }, + { 0xa873, 0x0000 }, + { 0xa874, 0x0000 }, + { 0xa875, 0x0000 }, + { 0xa876, 0x0000 }, + { 0xa877, 0x0000 }, + { 0xa878, 0x0000 }, + { 0xa879, 0x0000 }, + { 0xa87a, 0x0000 }, + { 0xa87b, 0x0000 }, + { 0xa87c, 0x0000 }, + { 0xa87d, 0x0000 }, + { 0xa87e, 0x0000 }, + { 0xa87f, 0x0000 }, + { 0xa880, 0x0000 }, + { 0xa881, 0x0000 }, + { 0xa882, 0x0000 }, + { 0xa883, 0x0000 }, + { 0xa884, 0x0000 }, + { 0xa885, 0x0000 }, + { 0xa886, 0x0000 }, + { 0xa887, 0x0000 }, + { 0xa888, 0x0000 }, + { 0xa889, 0x0000 }, + { 0xa88a, 0x0000 }, + { 0xa88b, 0x0000 }, + { 0xa88c, 0x0000 }, + { 0xa88d, 0x0000 }, + { 0xa88e, 0x0000 }, + { 0xa88f, 0x0000 }, + { 0xa890, 0x0000 }, + { 0xa891, 0x0000 }, + { 0xa892, 0x0000 }, + { 0xa893, 0x0000 }, + { 0xa894, 0x0000 }, + { 0xa895, 0x0000 }, + { 0xa896, 0x0000 }, + { 0xa897, 0x0000 }, + { 0xa898, 0x0000 }, + { 0xa899, 0x0000 }, + { 0xa89a, 0x0000 }, + { 0xa89b, 0x0000 }, + { 0xa89c, 0x0000 }, + { 0xa89d, 0x0000 }, + { 0xa89e, 0x0000 }, + { 0xa89f, 0x0000 }, + { 0xa8a0, 0x0000 }, + { 0xa8a1, 0x0000 }, + { 0xa8a2, 0x0000 }, + { 0xa8a3, 0x0000 }, + { 0xa8a4, 0x0000 }, + { 0xa8a5, 0x0000 }, + { 0xa8a6, 0x0000 }, + { 0xa8a7, 0x0000 }, + { 0xa8a8, 0x0000 }, + { 0xa8a9, 0x0000 }, + { 0xa8aa, 0x0000 }, + { 0xa8ab, 0x0000 }, + { 0xa8ac, 0x0000 }, + { 0xa8ad, 0x0000 }, + { 0xa8ae, 0x0000 }, + { 0xa8af, 0x0000 }, + { 0xa8b0, 0x0000 }, + { 0xa8b1, 0x0000 }, + { 0xa8b2, 0x0000 }, + { 0xa8b3, 0x0000 }, + { 0xa8b4, 0x0000 }, + { 0xa8b5, 0x0000 }, + { 0xa8b6, 0x0000 }, + { 0xa8b7, 0x0000 }, + { 0xa8b8, 0x0000 }, + { 0xa8b9, 0x0000 }, + { 0xa8ba, 0x0000 }, + { 0xa8bb, 0x0000 }, + { 0xa8bc, 0x0000 }, + { 0xa8bd, 0x0000 }, + { 0xa8be, 0x0000 }, + { 0xa8bf, 0x0000 }, + { 0xa8c0, 0x0000 }, + { 0xa8c1, 0x0000 }, + { 0xa8c2, 0x0000 }, + { 0xa8c3, 0x0000 }, + { 0xa8c4, 0x0000 }, + { 0xa8c5, 0x0000 }, + { 0xa8c6, 0x0000 }, + { 0xa8c7, 0x0000 }, + { 0xa8c8, 0x0000 }, + { 0xa8c9, 0x0000 }, + { 0xa8ca, 0x0000 }, + { 0xa8cb, 0x0000 }, + { 0xa8cc, 0x0000 }, + { 0xa8cd, 0x0000 }, + { 0xa8ce, 0x0000 }, + { 0xa8cf, 0x0000 }, + { 0xa8d0, 0x0000 }, + { 0xa8d1, 0x0000 }, + { 0xa8d2, 0x0000 }, + { 0xa8d3, 0x0000 }, + { 0xa8d4, 0x0000 }, + { 0xa8d5, 0x0000 }, + { 0xa8d6, 0x0000 }, + { 0xa8d7, 0x0000 }, + { 0xa8d8, 0x0000 }, + { 0xa8d9, 0x0000 }, + { 0xa8da, 0x0000 }, + { 0xa8db, 0x0000 }, + { 0xa8dc, 0x0000 }, + { 0xa8dd, 0x0000 }, + { 0xa8de, 0x0000 }, + { 0xa8df, 0x0000 }, + { 0xa8e0, 0x0000 }, + { 0xa8e1, 0x0000 }, + { 0xa8e2, 0x0000 }, + { 0xa8e3, 0x0000 }, + { 0xa8e4, 0x0000 }, + { 0xa8e5, 0x0000 }, + { 0xa8e6, 0x0000 }, + { 0xa8e7, 0x0000 }, + { 0xa8e8, 0x0000 }, + { 0xa8e9, 0x0000 }, + { 0xa8ea, 0x0000 }, + { 0xa8eb, 0x0000 }, + { 0xa8ec, 0x0000 }, + { 0xa8ed, 0x0000 }, + { 0xa8ee, 0x0000 }, + { 0xa8ef, 0x0000 }, + { 0xa8f0, 0x0000 }, + { 0xa8f1, 0x0000 }, + { 0xa8f2, 0x0000 }, + { 0xa8f3, 0x0000 }, + { 0xa8f4, 0x0000 }, + { 0xa8f5, 0x0000 }, + { 0xa8f6, 0x0000 }, + { 0xa8f7, 0x0000 }, + { 0xa8f8, 0x0000 }, + { 0xa8f9, 0x0000 }, + { 0xa8fa, 0x0000 }, + { 0xa8fb, 0x0000 }, + { 0xa8fc, 0x0000 }, + { 0xa8fd, 0x0000 }, + { 0xa8fe, 0x0000 }, + { 0xa8ff, 0x0000 }, + { 0xa900, 0x0000 }, + { 0xa901, 0x0000 }, + { 0xa902, 0x0000 }, + { 0xa903, 0x0000 }, + { 0xa904, 0x0000 }, + { 0xa905, 0x0000 }, + { 0xa906, 0x0000 }, + { 0xa907, 0x0000 }, + { 0xa908, 0x0000 }, + { 0xa909, 0x0000 }, + { 0xa90a, 0x0000 }, + { 0xa90b, 0x0000 }, + { 0xa90c, 0x0000 }, + { 0xa90d, 0x0000 }, + { 0xa90e, 0x0000 }, + { 0xa90f, 0x0000 }, + { 0xa910, 0x0000 }, + { 0xa911, 0x0000 }, + { 0xa912, 0x0000 }, + { 0xa913, 0x0000 }, + { 0xa914, 0x0000 }, + { 0xa915, 0x0000 }, + { 0xa916, 0x0000 }, + { 0xa917, 0x0000 }, + { 0xa918, 0x0000 }, + { 0xa919, 0x0000 }, + { 0xa91a, 0x0000 }, + { 0xa91b, 0x0000 }, + { 0xa91c, 0x0000 }, + { 0xa91d, 0x0000 }, + { 0xa91e, 0x0000 }, + { 0xa91f, 0x0000 }, + { 0xa920, 0x0000 }, + { 0xa921, 0x0000 }, + { 0xa922, 0x0000 }, + { 0xa923, 0x0000 }, + { 0xa924, 0x0000 }, + { 0xa925, 0x0000 }, + { 0xa926, 0x0000 }, + { 0xa927, 0x0000 }, + { 0xa928, 0x0000 }, + { 0xa929, 0x0000 }, + { 0xa92a, 0x0000 }, + { 0xa92b, 0x0000 }, + { 0xa92c, 0x0000 }, + { 0xa92d, 0x0000 }, + { 0xa92e, 0x0000 }, + { 0xa92f, 0x0000 }, + { 0xa930, 0x0000 }, + { 0xa931, 0x0000 }, + { 0xa932, 0x0000 }, + { 0xa933, 0x0000 }, + { 0xa934, 0x0000 }, + { 0xa935, 0x0000 }, + { 0xa936, 0x0000 }, + { 0xa937, 0x0000 }, + { 0xa938, 0x0000 }, + { 0xa939, 0x0000 }, + { 0xa93a, 0x0000 }, + { 0xa93b, 0x0000 }, + { 0xa93c, 0x0000 }, + { 0xa93d, 0x0000 }, + { 0xa93e, 0x0000 }, + { 0xa93f, 0x0000 }, + { 0xa940, 0x0000 }, + { 0xa941, 0x0000 }, + { 0xa942, 0x0000 }, + { 0xa943, 0x0000 }, + { 0xa944, 0x0000 }, + { 0xa945, 0x0000 }, + { 0xa946, 0x0000 }, + { 0xa947, 0x0000 }, + { 0xa948, 0x0000 }, + { 0xa949, 0x0000 }, + { 0xa94a, 0x0000 }, + { 0xa94b, 0x0000 }, + { 0xa94c, 0x0000 }, + { 0xa94d, 0x0000 }, + { 0xa94e, 0x0000 }, + { 0xa94f, 0x0000 }, + { 0xa950, 0x0000 }, + { 0xa951, 0x0000 }, + { 0xa952, 0x0000 }, + { 0xa953, 0x0000 }, + { 0xa954, 0x0000 }, + { 0xa955, 0x0000 }, + { 0xa956, 0x0000 }, + { 0xa957, 0x0000 }, + { 0xa958, 0x0000 }, + { 0xa959, 0x0000 }, + { 0xa95a, 0x0000 }, + { 0xa95b, 0x0000 }, + { 0xa95c, 0x0000 }, + { 0xa95d, 0x0000 }, + { 0xa95e, 0x0000 }, + { 0xa95f, 0x0000 }, + { 0xa960, 0x0000 }, + { 0xa961, 0x0000 }, + { 0xa962, 0x0000 }, + { 0xa963, 0x0000 }, + { 0xa964, 0x0000 }, + { 0xa965, 0x0000 }, + { 0xa966, 0x0000 }, + { 0xa967, 0x0000 }, + { 0xa968, 0x0000 }, + { 0xa969, 0x0000 }, + { 0xa96a, 0x0000 }, + { 0xa96b, 0x0000 }, + { 0xa96c, 0x0000 }, + { 0xa96d, 0x0000 }, + { 0xa96e, 0x0000 }, + { 0xa96f, 0x0000 }, + { 0xa970, 0x0000 }, + { 0xa971, 0x0000 }, + { 0xa972, 0x0000 }, + { 0xa973, 0x0000 }, + { 0xa974, 0x0000 }, + { 0xa975, 0x0000 }, + { 0xa976, 0x0000 }, + { 0xa977, 0x0000 }, + { 0xa978, 0x0000 }, + { 0xa979, 0x0000 }, + { 0xa97a, 0x0000 }, + { 0xa97b, 0x0000 }, + { 0xa97c, 0x0000 }, + { 0xa97d, 0x0000 }, + { 0xa97e, 0x0000 }, + { 0xa97f, 0x0000 }, + { 0xa980, 0x0000 }, + { 0xa981, 0x0000 }, + { 0xa982, 0x0000 }, + { 0xa983, 0x0000 }, + { 0xa984, 0x0000 }, + { 0xa985, 0x0000 }, + { 0xa986, 0x0000 }, + { 0xa987, 0x0000 }, + { 0xa988, 0x0000 }, + { 0xa989, 0x0000 }, + { 0xa98a, 0x0000 }, + { 0xa98b, 0x0000 }, + { 0xa98c, 0x0000 }, + { 0xa98d, 0x0000 }, + { 0xa98e, 0x0000 }, + { 0xa98f, 0x0000 }, + { 0xa990, 0x0000 }, + { 0xa991, 0x0000 }, + { 0xa992, 0x0000 }, + { 0xa993, 0x0000 }, + { 0xa994, 0x0000 }, + { 0xa995, 0x0000 }, + { 0xa996, 0x0000 }, + { 0xa997, 0x0000 }, + { 0xa998, 0x0000 }, + { 0xa999, 0x0000 }, + { 0xa99a, 0x0000 }, + { 0xa99b, 0x0000 }, + { 0xa99c, 0x0000 }, + { 0xa99d, 0x0000 }, + { 0xa99e, 0x0000 }, + { 0xa99f, 0x0000 }, + { 0xa9a0, 0x0000 }, + { 0xa9a1, 0x0000 }, + { 0xa9a2, 0x0000 }, + { 0xa9a3, 0x0000 }, + { 0xa9a4, 0x0000 }, + { 0xa9a5, 0x0000 }, + { 0xa9a6, 0x0000 }, + { 0xa9a7, 0x0000 }, + { 0xa9a8, 0x0000 }, + { 0xa9a9, 0x0000 }, + { 0xa9aa, 0x0000 }, + { 0xa9ab, 0x0000 }, + { 0xa9ac, 0x0000 }, + { 0xa9ad, 0x0000 }, + { 0xa9ae, 0x0000 }, + { 0xa9af, 0x0000 }, + { 0xa9b0, 0x0000 }, + { 0xa9b1, 0x0000 }, + { 0xa9b2, 0x0000 }, + { 0xa9b3, 0x0000 }, + { 0xa9b4, 0x0000 }, + { 0xa9b5, 0x0000 }, + { 0xa9b6, 0x0000 }, + { 0xa9b7, 0x0000 }, + { 0xa9b8, 0x0000 }, + { 0xa9b9, 0x0000 }, + { 0xa9ba, 0x0000 }, + { 0xa9bb, 0x0000 }, + { 0xa9bc, 0x0000 }, + { 0xa9bd, 0x0000 }, + { 0xa9be, 0x0000 }, + { 0xa9bf, 0x0000 }, + { 0xa9c0, 0x0000 }, + { 0xa9c1, 0x0000 }, + { 0xa9c2, 0x0000 }, + { 0xa9c3, 0x0000 }, + { 0xa9c4, 0x0000 }, + { 0xa9c5, 0x0000 }, + { 0xa9c6, 0x0000 }, + { 0xa9c7, 0x0000 }, + { 0xa9c8, 0x0000 }, + { 0xa9c9, 0x0000 }, + { 0xa9ca, 0x0000 }, + { 0xa9cb, 0x0000 }, + { 0xa9cc, 0x0000 }, + { 0xa9cd, 0x0000 }, + { 0xa9ce, 0x0000 }, + { 0xa9cf, 0x0000 }, + { 0xa9d0, 0x0000 }, + { 0xa9d1, 0x0000 }, + { 0xa9d2, 0x0000 }, + { 0xa9d3, 0x0000 }, + { 0xa9d4, 0x0000 }, + { 0xa9d5, 0x0000 }, + { 0xa9d6, 0x0000 }, + { 0xa9d7, 0x0000 }, + { 0xa9d8, 0x0000 }, + { 0xa9d9, 0x0000 }, + { 0xa9da, 0x0000 }, + { 0xa9db, 0x0000 }, + { 0xa9dc, 0x0000 }, + { 0xa9dd, 0x0000 }, + { 0xa9de, 0x0000 }, + { 0xa9df, 0x0000 }, + { 0xa9e0, 0x0000 }, + { 0xa9e1, 0x0000 }, + { 0xa9e2, 0x0000 }, + { 0xa9e3, 0x0000 }, + { 0xa9e4, 0x0000 }, + { 0xa9e5, 0x0000 }, + { 0xa9e6, 0x0000 }, + { 0xa9e7, 0x0000 }, + { 0xa9e8, 0x0000 }, + { 0xa9e9, 0x0000 }, + { 0xa9ea, 0x0000 }, + { 0xa9eb, 0x0000 }, + { 0xa9ec, 0x0000 }, + { 0xa9ed, 0x0000 }, + { 0xa9ee, 0x0000 }, + { 0xa9ef, 0x0000 }, + { 0xa9f0, 0x0000 }, + { 0xa9f1, 0x0000 }, + { 0xa9f2, 0x0000 }, + { 0xa9f3, 0x0000 }, + { 0xa9f4, 0x0000 }, + { 0xa9f5, 0x0000 }, + { 0xa9f6, 0x0000 }, + { 0xa9f7, 0x0000 }, + { 0xa9f8, 0x0000 }, + { 0xa9f9, 0x0000 }, + { 0xa9fa, 0x0000 }, + { 0xa9fb, 0x0000 }, + { 0xa9fc, 0x0000 }, + { 0xa9fd, 0x0000 }, + { 0xa9fe, 0x0000 }, + { 0xa9ff, 0x0000 }, + { 0xaa00, 0x0000 }, + { 0xaa01, 0x0000 }, + { 0xaa02, 0x0000 }, + { 0xaa03, 0x0000 }, + { 0xaa04, 0x0000 }, + { 0xaa05, 0x0000 }, + { 0xaa06, 0x0000 }, + { 0xaa07, 0x0000 }, + { 0xaa08, 0x0000 }, + { 0xaa09, 0x0000 }, + { 0xaa0a, 0x0000 }, + { 0xaa0b, 0x0000 }, + { 0xaa0c, 0x0000 }, + { 0xaa0d, 0x0000 }, + { 0xaa0e, 0x0000 }, + { 0xaa0f, 0x0000 }, + { 0xaa10, 0x0000 }, + { 0xaa11, 0x0000 }, + { 0xaa12, 0x0000 }, + { 0xaa13, 0x0000 }, + { 0xaa14, 0x0000 }, + { 0xaa15, 0x0000 }, + { 0xaa16, 0x0000 }, + { 0xaa17, 0x0000 }, + { 0xaa18, 0x0000 }, + { 0xaa19, 0x0000 }, + { 0xaa1a, 0x0000 }, + { 0xaa1b, 0x0000 }, + { 0xaa1c, 0x0000 }, + { 0xaa1d, 0x0000 }, + { 0xaa1e, 0x0000 }, + { 0xaa1f, 0x0000 }, + { 0xaa20, 0x0000 }, + { 0xaa21, 0x0000 }, + { 0xaa22, 0x0000 }, + { 0xaa23, 0x0000 }, + { 0xaa24, 0x0000 }, + { 0xaa25, 0x0000 }, + { 0xaa26, 0x0000 }, + { 0xaa27, 0x0000 }, + { 0xaa28, 0x0000 }, + { 0xaa29, 0x0000 }, + { 0xaa2a, 0x0000 }, + { 0xaa2b, 0x0000 }, + { 0xaa2c, 0x0000 }, + { 0xaa2d, 0x0000 }, + { 0xaa2e, 0x0000 }, + { 0xaa2f, 0x0000 }, + { 0xaa30, 0x0000 }, + { 0xaa31, 0x0000 }, + { 0xaa32, 0x0000 }, + { 0xaa33, 0x0000 }, + { 0xaa34, 0x0000 }, + { 0xaa35, 0x0000 }, + { 0xaa36, 0x0000 }, + { 0xaa37, 0x0000 }, + { 0xaa38, 0x0000 }, + { 0xaa39, 0x0000 }, + { 0xaa3a, 0x0000 }, + { 0xaa3b, 0x0000 }, + { 0xaa3c, 0x0000 }, + { 0xaa3d, 0x0000 }, + { 0xaa3e, 0x0000 }, + { 0xaa3f, 0x0000 }, + { 0xaa40, 0x0000 }, + { 0xaa41, 0x0000 }, + { 0xaa42, 0x0000 }, + { 0xaa43, 0x0000 }, + { 0xaa44, 0x0000 }, + { 0xaa45, 0x0000 }, + { 0xaa46, 0x0000 }, + { 0xaa47, 0x0000 }, + { 0xaa48, 0x0000 }, + { 0xaa49, 0x0000 }, + { 0xaa4a, 0x0000 }, + { 0xaa4b, 0x0000 }, + { 0xaa4c, 0x0000 }, + { 0xaa4d, 0x0000 }, + { 0xaa4e, 0x0000 }, + { 0xaa4f, 0x0000 }, + { 0xaa50, 0x0000 }, + { 0xaa51, 0x0000 }, + { 0xaa52, 0x0000 }, + { 0xaa53, 0x0000 }, + { 0xaa54, 0x0000 }, + { 0xaa55, 0x0000 }, + { 0xaa56, 0x0000 }, + { 0xaa57, 0x0000 }, + { 0xaa58, 0x0000 }, + { 0xaa59, 0x0000 }, + { 0xaa5a, 0x0000 }, + { 0xaa5b, 0x0000 }, + { 0xaa5c, 0x0000 }, + { 0xaa5d, 0x0000 }, + { 0xaa5e, 0x0000 }, + { 0xaa5f, 0x0000 }, + { 0xaa60, 0x0000 }, + { 0xaa61, 0x0000 }, + { 0xaa62, 0x0000 }, + { 0xaa63, 0x0000 }, + { 0xaa64, 0x0000 }, + { 0xaa65, 0x0000 }, + { 0xaa66, 0x0000 }, + { 0xaa67, 0x0000 }, + { 0xaa68, 0x0000 }, + { 0xaa69, 0x0000 }, + { 0xaa6a, 0x0000 }, + { 0xaa6b, 0x0000 }, + { 0xaa6c, 0x0000 }, + { 0xaa6d, 0x0000 }, + { 0xaa6e, 0x0000 }, + { 0xaa6f, 0x0000 }, + { 0xaa70, 0x0000 }, + { 0xaa71, 0x0000 }, + { 0xaa72, 0x0000 }, + { 0xaa73, 0x0000 }, + { 0xaa74, 0x0000 }, + { 0xaa75, 0x0000 }, + { 0xaa76, 0x0000 }, + { 0xaa77, 0x0000 }, + { 0xaa78, 0x0000 }, + { 0xaa79, 0x0000 }, + { 0xaa7a, 0x0000 }, + { 0xaa7b, 0x0000 }, + { 0xaa7c, 0x0000 }, + { 0xaa7d, 0x0000 }, + { 0xaa7e, 0x0000 }, + { 0xaa7f, 0x0000 }, + { 0xaa80, 0x0000 }, + { 0xaa81, 0x0000 }, + { 0xaa82, 0x0000 }, + { 0xaa83, 0x0000 }, + { 0xaa84, 0x0000 }, + { 0xaa85, 0x0000 }, + { 0xaa86, 0x0000 }, + { 0xaa87, 0x0000 }, + { 0xaa88, 0x0000 }, + { 0xaa89, 0x0000 }, + { 0xaa8a, 0x0000 }, + { 0xaa8b, 0x0000 }, + { 0xaa8c, 0x0000 }, + { 0xaa8d, 0x0000 }, + { 0xaa8e, 0x0000 }, + { 0xaa8f, 0x0000 }, + { 0xaa90, 0x0000 }, + { 0xaa91, 0x0000 }, + { 0xaa92, 0x0000 }, + { 0xaa93, 0x0000 }, + { 0xaa94, 0x0000 }, + { 0xaa95, 0x0000 }, + { 0xaa96, 0x0000 }, + { 0xaa97, 0x0000 }, + { 0xaa98, 0x0000 }, + { 0xaa99, 0x0000 }, + { 0xaa9a, 0x0000 }, + { 0xaa9b, 0x0000 }, + { 0xaa9c, 0x0000 }, + { 0xaa9d, 0x0000 }, + { 0xaa9e, 0x0000 }, + { 0xaa9f, 0x0000 }, + { 0xaaa0, 0x0000 }, + { 0xaaa1, 0x0000 }, + { 0xaaa2, 0x0000 }, + { 0xaaa3, 0x0000 }, + { 0xaaa4, 0x0000 }, + { 0xaaa5, 0x0000 }, + { 0xaaa6, 0x0000 }, + { 0xaaa7, 0x0000 }, + { 0xaaa8, 0x0000 }, + { 0xaaa9, 0x0000 }, + { 0xaaaa, 0x0000 }, + { 0xaaab, 0x0000 }, + { 0xaaac, 0x0000 }, + { 0xaaad, 0x0000 }, + { 0xaaae, 0x0000 }, + { 0xaaaf, 0x0000 }, + { 0xaab0, 0x0000 }, + { 0xaab1, 0x0000 }, + { 0xaab2, 0x0000 }, + { 0xaab3, 0x0000 }, + { 0xaab4, 0x0000 }, + { 0xaab5, 0x0000 }, + { 0xaab6, 0x0000 }, + { 0xaab7, 0x0000 }, + { 0xaab8, 0x0000 }, + { 0xaab9, 0x0000 }, + { 0xaaba, 0x0000 }, + { 0xaabb, 0x0000 }, + { 0xaabc, 0x0000 }, + { 0xaabd, 0x0000 }, + { 0xaabe, 0x0000 }, + { 0xaabf, 0x0000 }, + { 0xaac0, 0x0000 }, + { 0xaac1, 0x0000 }, + { 0xaac2, 0x0000 }, + { 0xaac3, 0x0000 }, + { 0xaac4, 0x0000 }, + { 0xaac5, 0x0000 }, + { 0xaac6, 0x0000 }, + { 0xaac7, 0x0000 }, + { 0xaac8, 0x0000 }, + { 0xaac9, 0x0000 }, + { 0xaaca, 0x0000 }, + { 0xaacb, 0x0000 }, + { 0xaacc, 0x0000 }, + { 0xaacd, 0x0000 }, + { 0xaace, 0x0000 }, + { 0xaacf, 0x0000 }, + { 0xaad0, 0x0000 }, + { 0xaad1, 0x0000 }, + { 0xaad2, 0x0000 }, + { 0xaad3, 0x0000 }, + { 0xaad4, 0x0000 }, + { 0xaad5, 0x0000 }, + { 0xaad6, 0x0000 }, + { 0xaad7, 0x0000 }, + { 0xaad8, 0x0000 }, + { 0xaad9, 0x0000 }, + { 0xaada, 0x0000 }, + { 0xaadb, 0x0000 }, + { 0xaadc, 0x0000 }, + { 0xaadd, 0x0000 }, + { 0xaade, 0x0000 }, + { 0xaadf, 0x0000 }, + { 0xaae0, 0x0000 }, + { 0xaae1, 0x0000 }, + { 0xaae2, 0x0000 }, + { 0xaae3, 0x0000 }, + { 0xaae4, 0x0000 }, + { 0xaae5, 0x0000 }, + { 0xaae6, 0x0000 }, + { 0xaae7, 0x0000 }, + { 0xaae8, 0x0000 }, + { 0xaae9, 0x0000 }, + { 0xaaea, 0x0000 }, + { 0xaaeb, 0x0000 }, + { 0xaaec, 0x0000 }, + { 0xaaed, 0x0000 }, + { 0xaaee, 0x0000 }, + { 0xaaef, 0x0000 }, + { 0xaaf0, 0x0000 }, + { 0xaaf1, 0x0000 }, + { 0xaaf2, 0x0000 }, + { 0xaaf3, 0x0000 }, + { 0xaaf4, 0x0000 }, + { 0xaaf5, 0x0000 }, + { 0xaaf6, 0x0000 }, + { 0xaaf7, 0x0000 }, + { 0xaaf8, 0x0000 }, + { 0xaaf9, 0x0000 }, + { 0xaafa, 0x0000 }, + { 0xaafb, 0x0000 }, + { 0xaafc, 0x0000 }, + { 0xaafd, 0x0000 }, + { 0xaafe, 0x0000 }, + { 0xaaff, 0x0000 }, + { 0xab00, 0x0000 }, + { 0xab01, 0x0000 }, + { 0xab02, 0x0000 }, + { 0xab03, 0x0000 }, + { 0xab04, 0x0000 }, + { 0xab05, 0x0000 }, + { 0xab06, 0x0000 }, + { 0xab07, 0x0000 }, + { 0xab08, 0x0000 }, + { 0xab09, 0x0000 }, + { 0xab0a, 0x0000 }, + { 0xab0b, 0x0000 }, + { 0xab0c, 0x0000 }, + { 0xab0d, 0x0000 }, + { 0xab0e, 0x0000 }, + { 0xab0f, 0x0000 }, + { 0xab10, 0x0000 }, + { 0xab11, 0x0000 }, + { 0xab12, 0x0000 }, + { 0xab13, 0x0000 }, + { 0xab14, 0x0000 }, + { 0xab15, 0x0000 }, + { 0xab16, 0x0000 }, + { 0xab17, 0x0000 }, + { 0xab18, 0x0000 }, + { 0xab19, 0x0000 }, + { 0xab1a, 0x0000 }, + { 0xab1b, 0x0000 }, + { 0xab1c, 0x0000 }, + { 0xab1d, 0x0000 }, + { 0xab1e, 0x0000 }, + { 0xab1f, 0x0000 }, + { 0xab20, 0x0000 }, + { 0xab21, 0x0000 }, + { 0xab22, 0x0000 }, + { 0xab23, 0x0000 }, + { 0xab24, 0x0000 }, + { 0xab25, 0x0000 }, + { 0xab26, 0x0000 }, + { 0xab27, 0x0000 }, + { 0xab28, 0x0000 }, + { 0xab29, 0x0000 }, + { 0xab2a, 0x0000 }, + { 0xab2b, 0x0000 }, + { 0xab2c, 0x0000 }, + { 0xab2d, 0x0000 }, + { 0xab2e, 0x0000 }, + { 0xab2f, 0x0000 }, + { 0xab30, 0x0000 }, + { 0xab31, 0x0000 }, + { 0xab32, 0x0000 }, + { 0xab33, 0x0000 }, + { 0xab34, 0x0000 }, + { 0xab35, 0x0000 }, + { 0xab36, 0x0000 }, + { 0xab37, 0x0000 }, + { 0xab38, 0x0000 }, + { 0xab39, 0x0000 }, + { 0xab3a, 0x0000 }, + { 0xab3b, 0x0000 }, + { 0xab3c, 0x0000 }, + { 0xab3d, 0x0000 }, + { 0xab3e, 0x0000 }, + { 0xab3f, 0x0000 }, + { 0xab40, 0x0000 }, + { 0xab41, 0x0000 }, + { 0xab42, 0x0000 }, + { 0xab43, 0x0000 }, + { 0xab44, 0x0000 }, + { 0xab45, 0x0000 }, + { 0xab46, 0x0000 }, + { 0xab47, 0x0000 }, + { 0xab48, 0x0000 }, + { 0xab49, 0x0000 }, + { 0xab4a, 0x0000 }, + { 0xab4b, 0x0000 }, + { 0xab4c, 0x0000 }, + { 0xab4d, 0x0000 }, + { 0xab4e, 0x0000 }, + { 0xab4f, 0x0000 }, + { 0xab50, 0x0000 }, + { 0xab51, 0x0000 }, + { 0xab52, 0x0000 }, + { 0xab53, 0x0000 }, + { 0xab54, 0x0000 }, + { 0xab55, 0x0000 }, + { 0xab56, 0x0000 }, + { 0xab57, 0x0000 }, + { 0xab58, 0x0000 }, + { 0xab59, 0x0000 }, + { 0xab5a, 0x0000 }, + { 0xab5b, 0x0000 }, + { 0xab5c, 0x0000 }, + { 0xab5d, 0x0000 }, + { 0xab5e, 0x0000 }, + { 0xab5f, 0x0000 }, + { 0xab60, 0x0000 }, + { 0xab61, 0x0000 }, + { 0xab62, 0x0000 }, + { 0xab63, 0x0000 }, + { 0xab64, 0x0000 }, + { 0xab65, 0x0000 }, + { 0xab66, 0x0000 }, + { 0xab67, 0x0000 }, + { 0xab68, 0x0000 }, + { 0xab69, 0x0000 }, + { 0xab6a, 0x0000 }, + { 0xab6b, 0x0000 }, + { 0xab6c, 0x0000 }, + { 0xab6d, 0x0000 }, + { 0xab6e, 0x0000 }, + { 0xab6f, 0x0000 }, + { 0xab70, 0x0000 }, + { 0xab71, 0x0000 }, + { 0xab72, 0x0000 }, + { 0xab73, 0x0000 }, + { 0xab74, 0x0000 }, + { 0xab75, 0x0000 }, + { 0xab76, 0x0000 }, + { 0xab77, 0x0000 }, + { 0xab78, 0x0000 }, + { 0xab79, 0x0000 }, + { 0xab7a, 0x0000 }, + { 0xab7b, 0x0000 }, + { 0xab7c, 0x0000 }, + { 0xab7d, 0x0000 }, + { 0xab7e, 0x0000 }, + { 0xab7f, 0x0000 }, + { 0xab80, 0x0000 }, + { 0xab81, 0x0000 }, + { 0xab82, 0x0000 }, + { 0xab83, 0x0000 }, + { 0xab84, 0x0000 }, + { 0xab85, 0x0000 }, + { 0xab86, 0x0000 }, + { 0xab87, 0x0000 }, + { 0xab88, 0x0000 }, + { 0xab89, 0x0000 }, + { 0xab8a, 0x0000 }, + { 0xab8b, 0x0000 }, + { 0xab8c, 0x0000 }, + { 0xab8d, 0x0000 }, + { 0xab8e, 0x0000 }, + { 0xab8f, 0x0000 }, + { 0xab90, 0x0000 }, + { 0xab91, 0x0000 }, + { 0xab92, 0x0000 }, + { 0xab93, 0x0000 }, + { 0xab94, 0x0000 }, + { 0xab95, 0x0000 }, + { 0xab96, 0x0000 }, + { 0xab97, 0x0000 }, + { 0xab98, 0x0000 }, + { 0xab99, 0x0000 }, + { 0xab9a, 0x0000 }, + { 0xab9b, 0x0000 }, + { 0xab9c, 0x0000 }, + { 0xab9d, 0x0000 }, + { 0xab9e, 0x0000 }, + { 0xab9f, 0x0000 }, + { 0xaba0, 0x0000 }, + { 0xaba1, 0x0000 }, + { 0xaba2, 0x0000 }, + { 0xaba3, 0x0000 }, + { 0xaba4, 0x0000 }, + { 0xaba5, 0x0000 }, + { 0xaba6, 0x0000 }, + { 0xaba7, 0x0000 }, + { 0xaba8, 0x0000 }, + { 0xaba9, 0x0000 }, + { 0xabaa, 0x0000 }, + { 0xabab, 0x0000 }, + { 0xabac, 0x0000 }, + { 0xabad, 0x0000 }, + { 0xabae, 0x0000 }, + { 0xabaf, 0x0000 }, + { 0xabb0, 0x0000 }, + { 0xabb1, 0x0000 }, + { 0xabb2, 0x0000 }, + { 0xabb3, 0x0000 }, + { 0xabb4, 0x0000 }, + { 0xabb5, 0x0000 }, + { 0xabb6, 0x0000 }, + { 0xabb7, 0x0000 }, + { 0xabb8, 0x0000 }, + { 0xabb9, 0x0000 }, + { 0xabba, 0x0000 }, + { 0xabbb, 0x0000 }, + { 0xabbc, 0x0000 }, + { 0xabbd, 0x0000 }, + { 0xabbe, 0x0000 }, + { 0xabbf, 0x0000 }, + { 0xabc0, 0x0000 }, + { 0xabc1, 0x0000 }, + { 0xabc2, 0x0000 }, + { 0xabc3, 0x0000 }, + { 0xabc4, 0x0000 }, + { 0xabc5, 0x0000 }, + { 0xabc6, 0x0000 }, + { 0xabc7, 0x0000 }, + { 0xabc8, 0x0000 }, + { 0xabc9, 0x0000 }, + { 0xabca, 0x0000 }, + { 0xabcb, 0x0000 }, + { 0xabcc, 0x0000 }, + { 0xabcd, 0x0000 }, + { 0xabce, 0x0000 }, + { 0xabcf, 0x0000 }, + { 0xabd0, 0x0000 }, + { 0xabd1, 0x0000 }, + { 0xabd2, 0x0000 }, + { 0xabd3, 0x0000 }, + { 0xabd4, 0x0000 }, + { 0xabd5, 0x0000 }, + { 0xabd6, 0x0000 }, + { 0xabd7, 0x0000 }, + { 0xabd8, 0x0000 }, + { 0xabd9, 0x0000 }, + { 0xabda, 0x0000 }, + { 0xabdb, 0x0000 }, + { 0xabdc, 0x0000 }, + { 0xabdd, 0x0000 }, + { 0xabde, 0x0000 }, + { 0xabdf, 0x0000 }, + { 0xabe0, 0x0000 }, + { 0xabe1, 0x0000 }, + { 0xabe2, 0x0000 }, + { 0xabe3, 0x0000 }, + { 0xabe4, 0x0000 }, + { 0xabe5, 0x0000 }, + { 0xabe6, 0x0000 }, + { 0xabe7, 0x0000 }, + { 0xabe8, 0x0000 }, + { 0xabe9, 0x0000 }, + { 0xabea, 0x0000 }, + { 0xabeb, 0x0000 }, + { 0xabec, 0x0000 }, + { 0xabed, 0x0000 }, + { 0xabee, 0x0000 }, + { 0xabef, 0x0000 }, + { 0xabf0, 0x0000 }, + { 0xabf1, 0x0000 }, + { 0xabf2, 0x0000 }, + { 0xabf3, 0x0000 }, + { 0xabf4, 0x0000 }, + { 0xabf5, 0x0000 }, + { 0xabf6, 0x0000 }, + { 0xabf7, 0x0000 }, + { 0xabf8, 0x0000 }, + { 0xabf9, 0x0000 }, + { 0xabfa, 0x0000 }, + { 0xabfb, 0x0000 }, + { 0xabfc, 0x0000 }, + { 0xabfd, 0x0000 }, + { 0xabfe, 0x0000 }, + { 0xabff, 0x0000 }, + { 0xac00, 0x0000 }, + { 0xac01, 0x0000 }, + { 0xac02, 0x0000 }, + { 0xac03, 0x0000 }, + { 0xac04, 0x0000 }, + { 0xac05, 0x0000 }, + { 0xac06, 0x0000 }, + { 0xac07, 0x0000 }, + { 0xac08, 0x0000 }, + { 0xac09, 0x0000 }, + { 0xac0a, 0x0000 }, + { 0xac0b, 0x0000 }, + { 0xac0c, 0x0000 }, + { 0xac0d, 0x0000 }, + { 0xac0e, 0x0000 }, + { 0xac0f, 0x0000 }, + { 0xac10, 0x0000 }, + { 0xac11, 0x0000 }, + { 0xac12, 0x0000 }, + { 0xac13, 0x0000 }, + { 0xac14, 0x0000 }, + { 0xac15, 0x0000 }, + { 0xac16, 0x0000 }, + { 0xac17, 0x0000 }, + { 0xac18, 0x0000 }, + { 0xac19, 0x0000 }, + { 0xac1a, 0x0000 }, + { 0xac1b, 0x0000 }, + { 0xac1c, 0x0000 }, + { 0xac1d, 0x0000 }, + { 0xac1e, 0x0000 }, + { 0xac1f, 0x0000 }, + { 0xac20, 0x0000 }, + { 0xac21, 0x0000 }, + { 0xac22, 0x0000 }, + { 0xac23, 0x0000 }, + { 0xac24, 0x0000 }, + { 0xac25, 0x0000 }, + { 0xac26, 0x0000 }, + { 0xac27, 0x0000 }, + { 0xac28, 0x0000 }, + { 0xac29, 0x0000 }, + { 0xac2a, 0x0000 }, + { 0xac2b, 0x0000 }, + { 0xac2c, 0x0000 }, + { 0xac2d, 0x0000 }, + { 0xac2e, 0x0000 }, + { 0xac2f, 0x0000 }, + { 0xac30, 0x0000 }, + { 0xac31, 0x0000 }, + { 0xac32, 0x0000 }, + { 0xac33, 0x0000 }, + { 0xac34, 0x0000 }, + { 0xac35, 0x0000 }, + { 0xac36, 0x0000 }, + { 0xac37, 0x0000 }, + { 0xac38, 0x0000 }, + { 0xac39, 0x0000 }, + { 0xac3a, 0x0000 }, + { 0xac3b, 0x0000 }, + { 0xac3c, 0x0000 }, + { 0xac3d, 0x0000 }, + { 0xac3e, 0x0000 }, + { 0xac3f, 0x0000 }, + { 0xac40, 0x0000 }, + { 0xac41, 0x0000 }, + { 0xac42, 0x0000 }, + { 0xac43, 0x0000 }, + { 0xac44, 0x0000 }, + { 0xac45, 0x0000 }, + { 0xac46, 0x0000 }, + { 0xac47, 0x0000 }, + { 0xac48, 0x0000 }, + { 0xac49, 0x0000 }, + { 0xac4a, 0x0000 }, + { 0xac4b, 0x0000 }, + { 0xac4c, 0x0000 }, + { 0xac4d, 0x0000 }, + { 0xac4e, 0x0000 }, + { 0xac4f, 0x0000 }, + { 0xac50, 0x0000 }, + { 0xac51, 0x0000 }, + { 0xac52, 0x0000 }, + { 0xac53, 0x0000 }, + { 0xac54, 0x0000 }, + { 0xac55, 0x0000 }, + { 0xac56, 0x0000 }, + { 0xac57, 0x0000 }, + { 0xac58, 0x0000 }, + { 0xac59, 0x0000 }, + { 0xac5a, 0x0000 }, + { 0xac5b, 0x0000 }, + { 0xac5c, 0x0000 }, + { 0xac5d, 0x0000 }, + { 0xac5e, 0x0000 }, + { 0xac5f, 0x0000 }, + { 0xac60, 0x0000 }, + { 0xac61, 0x0000 }, + { 0xac62, 0x0000 }, + { 0xac63, 0x0000 }, + { 0xac64, 0x0000 }, + { 0xac65, 0x0000 }, + { 0xac66, 0x0000 }, + { 0xac67, 0x0000 }, + { 0xac68, 0x0000 }, + { 0xac69, 0x0000 }, + { 0xac6a, 0x0000 }, + { 0xac6b, 0x0000 }, + { 0xac6c, 0x0000 }, + { 0xac6d, 0x0000 }, + { 0xac6e, 0x0000 }, + { 0xac6f, 0x0000 }, + { 0xac70, 0x0000 }, + { 0xac71, 0x0000 }, + { 0xac72, 0x0000 }, + { 0xac73, 0x0000 }, + { 0xac74, 0x0000 }, + { 0xac75, 0x0000 }, + { 0xac76, 0x0000 }, + { 0xac77, 0x0000 }, + { 0xac78, 0x0000 }, + { 0xac79, 0x0000 }, + { 0xac7a, 0x0000 }, + { 0xac7b, 0x0000 }, + { 0xac7c, 0x0000 }, + { 0xac7d, 0x0000 }, + { 0xac7e, 0x0000 }, + { 0xac7f, 0x0000 }, + { 0xac80, 0x0000 }, + { 0xac81, 0x0000 }, + { 0xac82, 0x0000 }, + { 0xac83, 0x0000 }, + { 0xac84, 0x0000 }, + { 0xac85, 0x0000 }, + { 0xac86, 0x0000 }, + { 0xac87, 0x0000 }, + { 0xac88, 0x0000 }, + { 0xac89, 0x0000 }, + { 0xac8a, 0x0000 }, + { 0xac8b, 0x0000 }, + { 0xac8c, 0x0000 }, + { 0xac8d, 0x0000 }, + { 0xac8e, 0x0000 }, + { 0xac8f, 0x0000 }, + { 0xac90, 0x0000 }, + { 0xac91, 0x0000 }, + { 0xac92, 0x0000 }, + { 0xac93, 0x0000 }, + { 0xac94, 0x0000 }, + { 0xac95, 0x0000 }, + { 0xac96, 0x0000 }, + { 0xac97, 0x0000 }, + { 0xac98, 0x0000 }, + { 0xac99, 0x0000 }, + { 0xac9a, 0x0000 }, + { 0xac9b, 0x0000 }, + { 0xac9c, 0x0000 }, + { 0xac9d, 0x0000 }, + { 0xac9e, 0x0000 }, + { 0xac9f, 0x0000 }, + { 0xaca0, 0x0000 }, + { 0xaca1, 0x0000 }, + { 0xaca2, 0x0000 }, + { 0xaca3, 0x0000 }, + { 0xaca4, 0x0000 }, + { 0xaca5, 0x0000 }, + { 0xaca6, 0x0000 }, + { 0xaca7, 0x0000 }, + { 0xaca8, 0x0000 }, + { 0xaca9, 0x0000 }, + { 0xacaa, 0x0000 }, + { 0xacab, 0x0000 }, + { 0xacac, 0x0000 }, + { 0xacad, 0x0000 }, + { 0xacae, 0x0000 }, + { 0xacaf, 0x0000 }, + { 0xacb0, 0x0000 }, + { 0xacb1, 0x0000 }, + { 0xacb2, 0x0000 }, + { 0xacb3, 0x0000 }, + { 0xacb4, 0x0000 }, + { 0xacb5, 0x0000 }, + { 0xacb6, 0x0000 }, + { 0xacb7, 0x0000 }, + { 0xacb8, 0x0000 }, + { 0xacb9, 0x0000 }, + { 0xacba, 0x0000 }, + { 0xacbb, 0x0000 }, + { 0xacbc, 0x0000 }, + { 0xacbd, 0x0000 }, + { 0xacbe, 0x0000 }, + { 0xacbf, 0x0000 }, + { 0xacc0, 0x0000 }, + { 0xacc1, 0x0000 }, + { 0xacc2, 0x0000 }, + { 0xacc3, 0x0000 }, + { 0xacc4, 0x0000 }, + { 0xacc5, 0x0000 }, + { 0xacc6, 0x0000 }, + { 0xacc7, 0x0000 }, + { 0xacc8, 0x0000 }, + { 0xacc9, 0x0000 }, + { 0xacca, 0x0000 }, + { 0xaccb, 0x0000 }, + { 0xaccc, 0x0000 }, + { 0xaccd, 0x0000 }, + { 0xacce, 0x0000 }, + { 0xaccf, 0x0000 }, + { 0xacd0, 0x0000 }, + { 0xacd1, 0x0000 }, + { 0xacd2, 0x0000 }, + { 0xacd3, 0x0000 }, + { 0xacd4, 0x0000 }, + { 0xacd5, 0x0000 }, + { 0xacd6, 0x0000 }, + { 0xacd7, 0x0000 }, + { 0xacd8, 0x0000 }, + { 0xacd9, 0x0000 }, + { 0xacda, 0x0000 }, + { 0xacdb, 0x0000 }, + { 0xacdc, 0x0000 }, + { 0xacdd, 0x0000 }, + { 0xacde, 0x0000 }, + { 0xacdf, 0x0000 }, + { 0xace0, 0x0000 }, + { 0xace1, 0x0000 }, + { 0xace2, 0x0000 }, + { 0xace3, 0x0000 }, + { 0xace4, 0x0000 }, + { 0xace5, 0x0000 }, + { 0xace6, 0x0000 }, + { 0xace7, 0x0000 }, + { 0xace8, 0x0000 }, + { 0xace9, 0x0000 }, + { 0xacea, 0x0000 }, + { 0xaceb, 0x0000 }, + { 0xacec, 0x0000 }, + { 0xaced, 0x0000 }, + { 0xacee, 0x0000 }, + { 0xacef, 0x0000 }, + { 0xacf0, 0x0000 }, + { 0xacf1, 0x0000 }, + { 0xacf2, 0x0000 }, + { 0xacf3, 0x0000 }, + { 0xacf4, 0x0000 }, + { 0xacf5, 0x0000 }, + { 0xacf6, 0x0000 }, + { 0xacf7, 0x0000 }, + { 0xacf8, 0x0000 }, + { 0xacf9, 0x0000 }, + { 0xacfa, 0x0000 }, + { 0xacfb, 0x0000 }, + { 0xacfc, 0x0000 }, + { 0xacfd, 0x0000 }, + { 0xacfe, 0x0000 }, + { 0xacff, 0x0000 }, + { 0xad00, 0x0000 }, + { 0xad01, 0x0000 }, + { 0xad02, 0x0000 }, + { 0xad03, 0x0000 }, + { 0xad04, 0x0000 }, + { 0xad05, 0x0000 }, + { 0xad06, 0x0000 }, + { 0xad07, 0x0000 }, + { 0xad08, 0x0000 }, + { 0xad09, 0x0000 }, + { 0xad0a, 0x0000 }, + { 0xad0b, 0x0000 }, + { 0xad0c, 0x0000 }, + { 0xad0d, 0x0000 }, + { 0xad0e, 0x0000 }, + { 0xad0f, 0x0000 }, + { 0xad10, 0x0000 }, + { 0xad11, 0x0000 }, + { 0xad12, 0x0000 }, + { 0xad13, 0x0000 }, + { 0xad14, 0x0000 }, + { 0xad15, 0x0000 }, + { 0xad16, 0x0000 }, + { 0xad17, 0x0000 }, + { 0xad18, 0x0000 }, + { 0xad19, 0x0000 }, + { 0xad1a, 0x0000 }, + { 0xad1b, 0x0000 }, + { 0xad1c, 0x0000 }, + { 0xad1d, 0x0000 }, + { 0xad1e, 0x0000 }, + { 0xad1f, 0x0000 }, + { 0xad20, 0x0000 }, + { 0xad21, 0x0000 }, + { 0xad22, 0x0000 }, + { 0xad23, 0x0000 }, + { 0xad24, 0x0000 }, + { 0xad25, 0x0000 }, + { 0xad26, 0x0000 }, + { 0xad27, 0x0000 }, + { 0xad28, 0x0000 }, + { 0xad29, 0x0000 }, + { 0xad2a, 0x0000 }, + { 0xad2b, 0x0000 }, + { 0xad2c, 0x0000 }, + { 0xad2d, 0x0000 }, + { 0xad2e, 0x0000 }, + { 0xad2f, 0x0000 }, + { 0xad30, 0x0000 }, + { 0xad31, 0x0000 }, + { 0xad32, 0x0000 }, + { 0xad33, 0x0000 }, + { 0xad34, 0x0000 }, + { 0xad35, 0x0000 }, + { 0xad36, 0x0000 }, + { 0xad37, 0x0000 }, + { 0xad38, 0x0000 }, + { 0xad39, 0x0000 }, + { 0xad3a, 0x0000 }, + { 0xad3b, 0x0000 }, + { 0xad3c, 0x0000 }, + { 0xad3d, 0x0000 }, + { 0xad3e, 0x0000 }, + { 0xad3f, 0x0000 }, + { 0xad40, 0x0000 }, + { 0xad41, 0x0000 }, + { 0xad42, 0x0000 }, + { 0xad43, 0x0000 }, + { 0xad44, 0x0000 }, + { 0xad45, 0x0000 }, + { 0xad46, 0x0000 }, + { 0xad47, 0x0000 }, + { 0xad48, 0x0000 }, + { 0xad49, 0x0000 }, + { 0xad4a, 0x0000 }, + { 0xad4b, 0x0000 }, + { 0xad4c, 0x0000 }, + { 0xad4d, 0x0000 }, + { 0xad4e, 0x0000 }, + { 0xad4f, 0x0000 }, + { 0xad50, 0x0000 }, + { 0xad51, 0x0000 }, + { 0xad52, 0x0000 }, + { 0xad53, 0x0000 }, + { 0xad54, 0x0000 }, + { 0xad55, 0x0000 }, + { 0xad56, 0x0000 }, + { 0xad57, 0x0000 }, + { 0xad58, 0x0000 }, + { 0xad59, 0x0000 }, + { 0xad5a, 0x0000 }, + { 0xad5b, 0x0000 }, + { 0xad5c, 0x0000 }, + { 0xad5d, 0x0000 }, + { 0xad5e, 0x0000 }, + { 0xad5f, 0x0000 }, + { 0xad60, 0x0000 }, + { 0xad61, 0x0000 }, + { 0xad62, 0x0000 }, + { 0xad63, 0x0000 }, + { 0xad64, 0x0000 }, + { 0xad65, 0x0000 }, + { 0xad66, 0x0000 }, + { 0xad67, 0x0000 }, + { 0xad68, 0x0000 }, + { 0xad69, 0x0000 }, + { 0xad6a, 0x0000 }, + { 0xad6b, 0x0000 }, + { 0xad6c, 0x0000 }, + { 0xad6d, 0x0000 }, + { 0xad6e, 0x0000 }, + { 0xad6f, 0x0000 }, + { 0xad70, 0x0000 }, + { 0xad71, 0x0000 }, + { 0xad72, 0x0000 }, + { 0xad73, 0x0000 }, + { 0xad74, 0x0000 }, + { 0xad75, 0x0000 }, + { 0xad76, 0x0000 }, + { 0xad77, 0x0000 }, + { 0xad78, 0x0000 }, + { 0xad79, 0x0000 }, + { 0xad7a, 0x0000 }, + { 0xad7b, 0x0000 }, + { 0xad7c, 0x0000 }, + { 0xad7d, 0x0000 }, + { 0xad7e, 0x0000 }, + { 0xad7f, 0x0000 }, + { 0xad80, 0x0000 }, + { 0xad81, 0x0000 }, + { 0xad82, 0x0000 }, + { 0xad83, 0x0000 }, + { 0xad84, 0x0000 }, + { 0xad85, 0x0000 }, + { 0xad86, 0x0000 }, + { 0xad87, 0x0000 }, + { 0xad88, 0x0000 }, + { 0xad89, 0x0000 }, + { 0xad8a, 0x0000 }, + { 0xad8b, 0x0000 }, + { 0xad8c, 0x0000 }, + { 0xad8d, 0x0000 }, + { 0xad8e, 0x0000 }, + { 0xad8f, 0x0000 }, + { 0xad90, 0x0000 }, + { 0xad91, 0x0000 }, + { 0xad92, 0x0000 }, + { 0xad93, 0x0000 }, + { 0xad94, 0x0000 }, + { 0xad95, 0x0000 }, + { 0xad96, 0x0000 }, + { 0xad97, 0x0000 }, + { 0xad98, 0x0000 }, + { 0xad99, 0x0000 }, + { 0xad9a, 0x0000 }, + { 0xad9b, 0x0000 }, + { 0xad9c, 0x0000 }, + { 0xad9d, 0x0000 }, + { 0xad9e, 0x0000 }, + { 0xad9f, 0x0000 }, + { 0xada0, 0x0000 }, + { 0xada1, 0x0000 }, + { 0xada2, 0x0000 }, + { 0xada3, 0x0000 }, + { 0xada4, 0x0000 }, + { 0xada5, 0x0000 }, + { 0xada6, 0x0000 }, + { 0xada7, 0x0000 }, + { 0xada8, 0x0000 }, + { 0xada9, 0x0000 }, + { 0xadaa, 0x0000 }, + { 0xadab, 0x0000 }, + { 0xadac, 0x0000 }, + { 0xadad, 0x0000 }, + { 0xadae, 0x0000 }, + { 0xadaf, 0x0000 }, + { 0xadb0, 0x0000 }, + { 0xadb1, 0x0000 }, + { 0xadb2, 0x0000 }, + { 0xadb3, 0x0000 }, + { 0xadb4, 0x0000 }, + { 0xadb5, 0x0000 }, + { 0xadb6, 0x0000 }, + { 0xadb7, 0x0000 }, + { 0xadb8, 0x0000 }, + { 0xadb9, 0x0000 }, + { 0xadba, 0x0000 }, + { 0xadbb, 0x0000 }, + { 0xadbc, 0x0000 }, + { 0xadbd, 0x0000 }, + { 0xadbe, 0x0000 }, + { 0xadbf, 0x0000 }, + { 0xadc0, 0x0000 }, + { 0xadc1, 0x0000 }, + { 0xadc2, 0x0000 }, + { 0xadc3, 0x0000 }, + { 0xadc4, 0x0000 }, + { 0xadc5, 0x0000 }, + { 0xadc6, 0x0000 }, + { 0xadc7, 0x0000 }, + { 0xadc8, 0x0000 }, + { 0xadc9, 0x0000 }, + { 0xadca, 0x0000 }, + { 0xadcb, 0x0000 }, + { 0xadcc, 0x0000 }, + { 0xadcd, 0x0000 }, + { 0xadce, 0x0000 }, + { 0xadcf, 0x0000 }, + { 0xadd0, 0x0000 }, + { 0xadd1, 0x0000 }, + { 0xadd2, 0x0000 }, + { 0xadd3, 0x0000 }, + { 0xadd4, 0x0000 }, + { 0xadd5, 0x0000 }, + { 0xadd6, 0x0000 }, + { 0xadd7, 0x0000 }, + { 0xadd8, 0x0000 }, + { 0xadd9, 0x0000 }, + { 0xadda, 0x0000 }, + { 0xaddb, 0x0000 }, + { 0xaddc, 0x0000 }, + { 0xaddd, 0x0000 }, + { 0xadde, 0x0000 }, + { 0xaddf, 0x0000 }, + { 0xade0, 0x0000 }, + { 0xade1, 0x0000 }, + { 0xade2, 0x0000 }, + { 0xade3, 0x0000 }, + { 0xade4, 0x0000 }, + { 0xade5, 0x0000 }, + { 0xade6, 0x0000 }, + { 0xade7, 0x0000 }, + { 0xade8, 0x0000 }, + { 0xade9, 0x0000 }, + { 0xadea, 0x0000 }, + { 0xadeb, 0x0000 }, + { 0xadec, 0x0000 }, + { 0xaded, 0x0000 }, + { 0xadee, 0x0000 }, + { 0xadef, 0x0000 }, + { 0xadf0, 0x0000 }, + { 0xadf1, 0x0000 }, + { 0xadf2, 0x0000 }, + { 0xadf3, 0x0000 }, + { 0xadf4, 0x0000 }, + { 0xadf5, 0x0000 }, + { 0xadf6, 0x0000 }, + { 0xadf7, 0x0000 }, + { 0xadf8, 0x0000 }, + { 0xadf9, 0x0000 }, + { 0xadfa, 0x0000 }, + { 0xadfb, 0x0000 }, + { 0xadfc, 0x0000 }, + { 0xadfd, 0x0000 }, + { 0xadfe, 0x0000 }, + { 0xadff, 0x0000 }, + { 0xae00, 0x0000 }, + { 0xae01, 0x0000 }, + { 0xae02, 0x0000 }, + { 0xae03, 0x0000 }, + { 0xae04, 0x0000 }, + { 0xae05, 0x0000 }, + { 0xae06, 0x0000 }, + { 0xae07, 0x0000 }, + { 0xae08, 0x0000 }, + { 0xae09, 0x0000 }, + { 0xae0a, 0x0000 }, + { 0xae0b, 0x0000 }, + { 0xae0c, 0x0000 }, + { 0xae0d, 0x0000 }, + { 0xae0e, 0x0000 }, + { 0xae0f, 0x0000 }, + { 0xae10, 0x0000 }, + { 0xae11, 0x0000 }, + { 0xae12, 0x0000 }, + { 0xae13, 0x0000 }, + { 0xae14, 0x0000 }, + { 0xae15, 0x0000 }, + { 0xae16, 0x0000 }, + { 0xae17, 0x0000 }, + { 0xae18, 0x0000 }, + { 0xae19, 0x0000 }, + { 0xae1a, 0x0000 }, + { 0xae1b, 0x0000 }, + { 0xae1c, 0x0000 }, + { 0xae1d, 0x0000 }, + { 0xae1e, 0x0000 }, + { 0xae1f, 0x0000 }, + { 0xae20, 0x0000 }, + { 0xae21, 0x0000 }, + { 0xae22, 0x0000 }, + { 0xae23, 0x0000 }, + { 0xae24, 0x0000 }, + { 0xae25, 0x0000 }, + { 0xae26, 0x0000 }, + { 0xae27, 0x0000 }, + { 0xae28, 0x0000 }, + { 0xae29, 0x0000 }, + { 0xae2a, 0x0000 }, + { 0xae2b, 0x0000 }, + { 0xae2c, 0x0000 }, + { 0xae2d, 0x0000 }, + { 0xae2e, 0x0000 }, + { 0xae2f, 0x0000 }, + { 0xae30, 0x0000 }, + { 0xae31, 0x0000 }, + { 0xae32, 0x0000 }, + { 0xae33, 0x0000 }, + { 0xae34, 0x0000 }, + { 0xae35, 0x0000 }, + { 0xae36, 0x0000 }, + { 0xae37, 0x0000 }, + { 0xae38, 0x0000 }, + { 0xae39, 0x0000 }, + { 0xae3a, 0x0000 }, + { 0xae3b, 0x0000 }, + { 0xae3c, 0x0000 }, + { 0xae3d, 0x0000 }, + { 0xae3e, 0x0000 }, + { 0xae3f, 0x0000 }, + { 0xae40, 0x0000 }, + { 0xae41, 0x0000 }, + { 0xae42, 0x0000 }, + { 0xae43, 0x0000 }, + { 0xae44, 0x0000 }, + { 0xae45, 0x0000 }, + { 0xae46, 0x0000 }, + { 0xae47, 0x0000 }, + { 0xae48, 0x0000 }, + { 0xae49, 0x0000 }, + { 0xae4a, 0x0000 }, + { 0xae4b, 0x0000 }, + { 0xae4c, 0x0000 }, + { 0xae4d, 0x0000 }, + { 0xae4e, 0x0000 }, + { 0xae4f, 0x0000 }, + { 0xae50, 0x0000 }, + { 0xae51, 0x0000 }, + { 0xae52, 0x0000 }, + { 0xae53, 0x0000 }, + { 0xae54, 0x0000 }, + { 0xae55, 0x0000 }, + { 0xae56, 0x0000 }, + { 0xae57, 0x0000 }, + { 0xae58, 0x0000 }, + { 0xae59, 0x0000 }, + { 0xae5a, 0x0000 }, + { 0xae5b, 0x0000 }, + { 0xae5c, 0x0000 }, + { 0xae5d, 0x0000 }, + { 0xae5e, 0x0000 }, + { 0xae5f, 0x0000 }, + { 0xae60, 0x0000 }, + { 0xae61, 0x0000 }, + { 0xae62, 0x0000 }, + { 0xae63, 0x0000 }, + { 0xae64, 0x0000 }, + { 0xae65, 0x0000 }, + { 0xae66, 0x0000 }, + { 0xae67, 0x0000 }, + { 0xae68, 0x0000 }, + { 0xae69, 0x0000 }, + { 0xae6a, 0x0000 }, + { 0xae6b, 0x0000 }, + { 0xae6c, 0x0000 }, + { 0xae6d, 0x0000 }, + { 0xae6e, 0x0000 }, + { 0xae6f, 0x0000 }, + { 0xae70, 0x0000 }, + { 0xae71, 0x0000 }, + { 0xae72, 0x0000 }, + { 0xae73, 0x0000 }, + { 0xae74, 0x0000 }, + { 0xae75, 0x0000 }, + { 0xae76, 0x0000 }, + { 0xae77, 0x0000 }, + { 0xae78, 0x0000 }, + { 0xae79, 0x0000 }, + { 0xae7a, 0x0000 }, + { 0xae7b, 0x0000 }, + { 0xae7c, 0x0000 }, + { 0xae7d, 0x0000 }, + { 0xae7e, 0x0000 }, + { 0xae7f, 0x0000 }, + { 0xae80, 0x0000 }, + { 0xae81, 0x0000 }, + { 0xae82, 0x0000 }, + { 0xae83, 0x0000 }, + { 0xae84, 0x0000 }, + { 0xae85, 0x0000 }, + { 0xae86, 0x0000 }, + { 0xae87, 0x0000 }, + { 0xae88, 0x0000 }, + { 0xae89, 0x0000 }, + { 0xae8a, 0x0000 }, + { 0xae8b, 0x0000 }, + { 0xae8c, 0x0000 }, + { 0xae8d, 0x0000 }, + { 0xae8e, 0x0000 }, + { 0xae8f, 0x0000 }, + { 0xae90, 0x0000 }, + { 0xae91, 0x0000 }, + { 0xae92, 0x0000 }, + { 0xae93, 0x0000 }, + { 0xae94, 0x0000 }, + { 0xae95, 0x0000 }, + { 0xae96, 0x0000 }, + { 0xae97, 0x0000 }, + { 0xae98, 0x0000 }, + { 0xae99, 0x0000 }, + { 0xae9a, 0x0000 }, + { 0xae9b, 0x0000 }, + { 0xae9c, 0x0000 }, + { 0xae9d, 0x0000 }, + { 0xae9e, 0x0000 }, + { 0xae9f, 0x0000 }, + { 0xaea0, 0x0000 }, + { 0xaea1, 0x0000 }, + { 0xaea2, 0x0000 }, + { 0xaea3, 0x0000 }, + { 0xaea4, 0x0000 }, + { 0xaea5, 0x0000 }, + { 0xaea6, 0x0000 }, + { 0xaea7, 0x0000 }, + { 0xaea8, 0x0000 }, + { 0xaea9, 0x0000 }, + { 0xaeaa, 0x0000 }, + { 0xaeab, 0x0000 }, + { 0xaeac, 0x0000 }, + { 0xaead, 0x0000 }, + { 0xaeae, 0x0000 }, + { 0xaeaf, 0x0000 }, + { 0xaeb0, 0x0000 }, + { 0xaeb1, 0x0000 }, + { 0xaeb2, 0x0000 }, + { 0xaeb3, 0x0000 }, + { 0xaeb4, 0x0000 }, + { 0xaeb5, 0x0000 }, + { 0xaeb6, 0x0000 }, + { 0xaeb7, 0x0000 }, + { 0xaeb8, 0x0000 }, + { 0xaeb9, 0x0000 }, + { 0xaeba, 0x0000 }, + { 0xaebb, 0x0000 }, + { 0xaebc, 0x0000 }, + { 0xaebd, 0x0000 }, + { 0xaebe, 0x0000 }, + { 0xaebf, 0x0000 }, + { 0xaec0, 0x0000 }, + { 0xaec1, 0x0000 }, + { 0xaec2, 0x0000 }, + { 0xaec3, 0x0000 }, + { 0xaec4, 0x0000 }, + { 0xaec5, 0x0000 }, + { 0xaec6, 0x0000 }, + { 0xaec7, 0x0000 }, + { 0xaec8, 0x0000 }, + { 0xaec9, 0x0000 }, + { 0xaeca, 0x0000 }, + { 0xaecb, 0x0000 }, + { 0xaecc, 0x0000 }, + { 0xaecd, 0x0000 }, + { 0xaece, 0x0000 }, + { 0xaecf, 0x0000 }, + { 0xaed0, 0x0000 }, + { 0xaed1, 0x0000 }, + { 0xaed2, 0x0000 }, + { 0xaed3, 0x0000 }, + { 0xaed4, 0x0000 }, + { 0xaed5, 0x0000 }, + { 0xaed6, 0x0000 }, + { 0xaed7, 0x0000 }, + { 0xaed8, 0x0000 }, + { 0xaed9, 0x0000 }, + { 0xaeda, 0x0000 }, + { 0xaedb, 0x0000 }, + { 0xaedc, 0x0000 }, + { 0xaedd, 0x0000 }, + { 0xaede, 0x0000 }, + { 0xaedf, 0x0000 }, + { 0xaee0, 0x0000 }, + { 0xaee1, 0x0000 }, + { 0xaee2, 0x0000 }, + { 0xaee3, 0x0000 }, + { 0xaee4, 0x0000 }, + { 0xaee5, 0x0000 }, + { 0xaee6, 0x0000 }, + { 0xaee7, 0x0000 }, + { 0xaee8, 0x0000 }, + { 0xaee9, 0x0000 }, + { 0xaeea, 0x0000 }, + { 0xaeeb, 0x0000 }, + { 0xaeec, 0x0000 }, + { 0xaeed, 0x0000 }, + { 0xaeee, 0x0000 }, + { 0xaeef, 0x0000 }, + { 0xaef0, 0x0000 }, + { 0xaef1, 0x0000 }, + { 0xaef2, 0x0000 }, + { 0xaef3, 0x0000 }, + { 0xaef4, 0x0000 }, + { 0xaef5, 0x0000 }, + { 0xaef6, 0x0000 }, + { 0xaef7, 0x0000 }, + { 0xaef8, 0x0000 }, + { 0xaef9, 0x0000 }, + { 0xaefa, 0x0000 }, + { 0xaefb, 0x0000 }, + { 0xaefc, 0x0000 }, + { 0xaefd, 0x0000 }, + { 0xaefe, 0x0000 }, + { 0xaeff, 0x0000 }, + { 0xaf00, 0x0000 }, + { 0xaf01, 0x0000 }, + { 0xaf02, 0x0000 }, + { 0xaf03, 0x0000 }, + { 0xaf04, 0x0000 }, + { 0xaf05, 0x0000 }, + { 0xaf06, 0x0000 }, + { 0xaf07, 0x0000 }, + { 0xaf08, 0x0000 }, + { 0xaf09, 0x0000 }, + { 0xaf0a, 0x0000 }, + { 0xaf0b, 0x0000 }, + { 0xaf0c, 0x0000 }, + { 0xaf0d, 0x0000 }, + { 0xaf0e, 0x0000 }, + { 0xaf0f, 0x0000 }, + { 0xaf10, 0x0000 }, + { 0xaf11, 0x0000 }, + { 0xaf12, 0x0000 }, + { 0xaf13, 0x0000 }, + { 0xaf14, 0x0000 }, + { 0xaf15, 0x0000 }, + { 0xaf16, 0x0000 }, + { 0xaf17, 0x0000 }, + { 0xaf18, 0x0000 }, + { 0xaf19, 0x0000 }, + { 0xaf1a, 0x0000 }, + { 0xaf1b, 0x0000 }, + { 0xaf1c, 0x0000 }, + { 0xaf1d, 0x0000 }, + { 0xaf1e, 0x0000 }, + { 0xaf1f, 0x0000 }, + { 0xaf20, 0x0000 }, + { 0xaf21, 0x0000 }, + { 0xaf22, 0x0000 }, + { 0xaf23, 0x0000 }, + { 0xaf24, 0x0000 }, + { 0xaf25, 0x0000 }, + { 0xaf26, 0x0000 }, + { 0xaf27, 0x0000 }, + { 0xaf28, 0x0000 }, + { 0xaf29, 0x0000 }, + { 0xaf2a, 0x0000 }, + { 0xaf2b, 0x0000 }, + { 0xaf2c, 0x0000 }, + { 0xaf2d, 0x0000 }, + { 0xaf2e, 0x0000 }, + { 0xaf2f, 0x0000 }, + { 0xaf30, 0x0000 }, + { 0xaf31, 0x0000 }, + { 0xaf32, 0x0000 }, + { 0xaf33, 0x0000 }, + { 0xaf34, 0x0000 }, + { 0xaf35, 0x0000 }, + { 0xaf36, 0x0000 }, + { 0xaf37, 0x0000 }, + { 0xaf38, 0x0000 }, + { 0xaf39, 0x0000 }, + { 0xaf3a, 0x0000 }, + { 0xaf3b, 0x0000 }, + { 0xaf3c, 0x0000 }, + { 0xaf3d, 0x0000 }, + { 0xaf3e, 0x0000 }, + { 0xaf3f, 0x0000 }, + { 0xaf40, 0x0000 }, + { 0xaf41, 0x0000 }, + { 0xaf42, 0x0000 }, + { 0xaf43, 0x0000 }, + { 0xaf44, 0x0000 }, + { 0xaf45, 0x0000 }, + { 0xaf46, 0x0000 }, + { 0xaf47, 0x0000 }, + { 0xaf48, 0x0000 }, + { 0xaf49, 0x0000 }, + { 0xaf4a, 0x0000 }, + { 0xaf4b, 0x0000 }, + { 0xaf4c, 0x0000 }, + { 0xaf4d, 0x0000 }, + { 0xaf4e, 0x0000 }, + { 0xaf4f, 0x0000 }, + { 0xaf50, 0x0000 }, + { 0xaf51, 0x0000 }, + { 0xaf52, 0x0000 }, + { 0xaf53, 0x0000 }, + { 0xaf54, 0x0000 }, + { 0xaf55, 0x0000 }, + { 0xaf56, 0x0000 }, + { 0xaf57, 0x0000 }, + { 0xaf58, 0x0000 }, + { 0xaf59, 0x0000 }, + { 0xaf5a, 0x0000 }, + { 0xaf5b, 0x0000 }, + { 0xaf5c, 0x0000 }, + { 0xaf5d, 0x0000 }, + { 0xaf5e, 0x0000 }, + { 0xaf5f, 0x0000 }, + { 0xaf60, 0x0000 }, + { 0xaf61, 0x0000 }, + { 0xaf62, 0x0000 }, + { 0xaf63, 0x0000 }, + { 0xaf64, 0x0000 }, + { 0xaf65, 0x0000 }, + { 0xaf66, 0x0000 }, + { 0xaf67, 0x0000 }, + { 0xaf68, 0x0000 }, + { 0xaf69, 0x0000 }, + { 0xaf6a, 0x0000 }, + { 0xaf6b, 0x0000 }, + { 0xaf6c, 0x0000 }, + { 0xaf6d, 0x0000 }, + { 0xaf6e, 0x0000 }, + { 0xaf6f, 0x0000 }, + { 0xaf70, 0x0000 }, + { 0xaf71, 0x0000 }, + { 0xaf72, 0x0000 }, + { 0xaf73, 0x0000 }, + { 0xaf74, 0x0000 }, + { 0xaf75, 0x0000 }, + { 0xaf76, 0x0000 }, + { 0xaf77, 0x0000 }, + { 0xaf78, 0x0000 }, + { 0xaf79, 0x0000 }, + { 0xaf7a, 0x0000 }, + { 0xaf7b, 0x0000 }, + { 0xaf7c, 0x0000 }, + { 0xaf7d, 0x0000 }, + { 0xaf7e, 0x0000 }, + { 0xaf7f, 0x0000 }, + { 0xaf80, 0x0000 }, + { 0xaf81, 0x0000 }, + { 0xaf82, 0x0000 }, + { 0xaf83, 0x0000 }, + { 0xaf84, 0x0000 }, + { 0xaf85, 0x0000 }, + { 0xaf86, 0x0000 }, + { 0xaf87, 0x0000 }, + { 0xaf88, 0x0000 }, + { 0xaf89, 0x0000 }, + { 0xaf8a, 0x0000 }, + { 0xaf8b, 0x0000 }, + { 0xaf8c, 0x0000 }, + { 0xaf8d, 0x0000 }, + { 0xaf8e, 0x0000 }, + { 0xaf8f, 0x0000 }, + { 0xaf90, 0x0000 }, + { 0xaf91, 0x0000 }, + { 0xaf92, 0x0000 }, + { 0xaf93, 0x0000 }, + { 0xaf94, 0x0000 }, + { 0xaf95, 0x0000 }, + { 0xaf96, 0x0000 }, + { 0xaf97, 0x0000 }, + { 0xaf98, 0x0000 }, + { 0xaf99, 0x0000 }, + { 0xaf9a, 0x0000 }, + { 0xaf9b, 0x0000 }, + { 0xaf9c, 0x0000 }, + { 0xaf9d, 0x0000 }, + { 0xaf9e, 0x0000 }, + { 0xaf9f, 0x0000 }, + { 0xafa0, 0x0000 }, + { 0xafa1, 0x0000 }, + { 0xafa2, 0x0000 }, + { 0xafa3, 0x0000 }, + { 0xafa4, 0x0000 }, + { 0xafa5, 0x0000 }, + { 0xafa6, 0x0000 }, + { 0xafa7, 0x0000 }, + { 0xafa8, 0x0000 }, + { 0xafa9, 0x0000 }, + { 0xafaa, 0x0000 }, + { 0xafab, 0x0000 }, + { 0xafac, 0x0000 }, + { 0xafad, 0x0000 }, + { 0xafae, 0x0000 }, + { 0xafaf, 0x0000 }, + { 0xafb0, 0x0000 }, + { 0xafb1, 0x0000 }, + { 0xafb2, 0x0000 }, + { 0xafb3, 0x0000 }, + { 0xafb4, 0x0000 }, + { 0xafb5, 0x0000 }, + { 0xafb6, 0x0000 }, + { 0xafb7, 0x0000 }, + { 0xafb8, 0x0000 }, + { 0xafb9, 0x0000 }, + { 0xafba, 0x0000 }, + { 0xafbb, 0x0000 }, + { 0xafbc, 0x0000 }, + { 0xafbd, 0x0000 }, + { 0xafbe, 0x0000 }, + { 0xafbf, 0x0000 }, + { 0xafc0, 0x0000 }, + { 0xafc1, 0x0000 }, + { 0xafc2, 0x0000 }, + { 0xafc3, 0x0000 }, + { 0xafc4, 0x0000 }, + { 0xafc5, 0x0000 }, + { 0xafc6, 0x0000 }, + { 0xafc7, 0x0000 }, + { 0xafc8, 0x0000 }, + { 0xafc9, 0x0000 }, + { 0xafca, 0x0000 }, + { 0xafcb, 0x0000 }, + { 0xafcc, 0x0000 }, + { 0xafcd, 0x0000 }, + { 0xafce, 0x0000 }, + { 0xafcf, 0x0000 }, + { 0xafd0, 0x0000 }, + { 0xafd1, 0x0000 }, + { 0xafd2, 0x0000 }, + { 0xafd3, 0x0000 }, + { 0xafd4, 0x0000 }, + { 0xafd5, 0x0000 }, + { 0xafd6, 0x0000 }, + { 0xafd7, 0x0000 }, + { 0xafd8, 0x0000 }, + { 0xafd9, 0x0000 }, + { 0xafda, 0x0000 }, + { 0xafdb, 0x0000 }, + { 0xafdc, 0x0000 }, + { 0xafdd, 0x0000 }, + { 0xafde, 0x0000 }, + { 0xafdf, 0x0000 }, + { 0xafe0, 0x0000 }, + { 0xafe1, 0x0000 }, + { 0xafe2, 0x0000 }, + { 0xafe3, 0x0000 }, + { 0xafe4, 0x0000 }, + { 0xafe5, 0x0000 }, + { 0xafe6, 0x0000 }, + { 0xafe7, 0x0000 }, + { 0xafe8, 0x0000 }, + { 0xafe9, 0x0000 }, + { 0xafea, 0x0000 }, + { 0xafeb, 0x0000 }, + { 0xafec, 0x0000 }, + { 0xafed, 0x0000 }, + { 0xafee, 0x0000 }, + { 0xafef, 0x0000 }, + { 0xaff0, 0x0000 }, + { 0xaff1, 0x0000 }, + { 0xaff2, 0x0000 }, + { 0xaff3, 0x0000 }, + { 0xaff4, 0x0000 }, + { 0xaff5, 0x0000 }, + { 0xaff6, 0x0000 }, + { 0xaff7, 0x0000 }, + { 0xaff8, 0x0000 }, + { 0xaff9, 0x0000 }, + { 0xaffa, 0x0000 }, + { 0xaffb, 0x0000 }, + { 0xaffc, 0x0000 }, + { 0xaffd, 0x0000 }, + { 0xaffe, 0x0000 }, + { 0xafff, 0x0000 }, + { 0xb000, 0x0000 }, + { 0xb001, 0x0000 }, + { 0xb002, 0x0000 }, + { 0xb003, 0x0000 }, + { 0xb004, 0x0000 }, + { 0xb005, 0x0000 }, + { 0xb006, 0x0000 }, + { 0xb007, 0x0000 }, + { 0xb008, 0x0000 }, + { 0xb009, 0x0000 }, + { 0xb00a, 0x0000 }, + { 0xb00b, 0x0000 }, + { 0xb00c, 0x0000 }, + { 0xb00d, 0x0000 }, + { 0xb00e, 0x0000 }, + { 0xb00f, 0x0000 }, + { 0xb010, 0x0000 }, + { 0xb011, 0x0000 }, + { 0xb012, 0x0000 }, + { 0xb013, 0x0000 }, + { 0xb014, 0x0000 }, + { 0xb015, 0x0000 }, + { 0xb016, 0x0000 }, + { 0xb017, 0x0000 }, + { 0xb018, 0x0000 }, + { 0xb019, 0x0000 }, + { 0xb01a, 0x0000 }, + { 0xb01b, 0x0000 }, + { 0xb01c, 0x0000 }, + { 0xb01d, 0x0000 }, + { 0xb01e, 0x0000 }, + { 0xb01f, 0x0000 }, + { 0xb020, 0x0000 }, + { 0xb021, 0x0000 }, + { 0xb022, 0x0000 }, + { 0xb023, 0x0000 }, + { 0xb024, 0x0000 }, + { 0xb025, 0x0000 }, + { 0xb026, 0x0000 }, + { 0xb027, 0x0000 }, + { 0xb028, 0x0000 }, + { 0xb029, 0x0000 }, + { 0xb02a, 0x0000 }, + { 0xb02b, 0x0000 }, + { 0xb02c, 0x0000 }, + { 0xb02d, 0x0000 }, + { 0xb02e, 0x0000 }, + { 0xb02f, 0x0000 }, + { 0xb030, 0x0000 }, + { 0xb031, 0x0000 }, + { 0xb032, 0x0000 }, + { 0xb033, 0x0000 }, + { 0xb034, 0x0000 }, + { 0xb035, 0x0000 }, + { 0xb036, 0x0000 }, + { 0xb037, 0x0000 }, + { 0xb038, 0x0000 }, + { 0xb039, 0x0000 }, + { 0xb03a, 0x0000 }, + { 0xb03b, 0x0000 }, + { 0xb03c, 0x0000 }, + { 0xb03d, 0x0000 }, + { 0xb03e, 0x0000 }, + { 0xb03f, 0x0000 }, + { 0xb040, 0x0000 }, + { 0xb041, 0x0000 }, + { 0xb042, 0x0000 }, + { 0xb043, 0x0000 }, + { 0xb044, 0x0000 }, + { 0xb045, 0x0000 }, + { 0xb046, 0x0000 }, + { 0xb047, 0x0000 }, + { 0xb048, 0x0000 }, + { 0xb049, 0x0000 }, + { 0xb04a, 0x0000 }, + { 0xb04b, 0x0000 }, + { 0xb04c, 0x0000 }, + { 0xb04d, 0x0000 }, + { 0xb04e, 0x0000 }, + { 0xb04f, 0x0000 }, + { 0xb050, 0x0000 }, + { 0xb051, 0x0000 }, + { 0xb052, 0x0000 }, + { 0xb053, 0x0000 }, + { 0xb054, 0x0000 }, + { 0xb055, 0x0000 }, + { 0xb056, 0x0000 }, + { 0xb057, 0x0000 }, + { 0xb058, 0x0000 }, + { 0xb059, 0x0000 }, + { 0xb05a, 0x0000 }, + { 0xb05b, 0x0000 }, + { 0xb05c, 0x0000 }, + { 0xb05d, 0x0000 }, + { 0xb05e, 0x0000 }, + { 0xb05f, 0x0000 }, + { 0xb060, 0x0000 }, + { 0xb061, 0x0000 }, + { 0xb062, 0x0000 }, + { 0xb063, 0x0000 }, + { 0xb064, 0x0000 }, + { 0xb065, 0x0000 }, + { 0xb066, 0x0000 }, + { 0xb067, 0x0000 }, + { 0xb068, 0x0000 }, + { 0xb069, 0x0000 }, + { 0xb06a, 0x0000 }, + { 0xb06b, 0x0000 }, + { 0xb06c, 0x0000 }, + { 0xb06d, 0x0000 }, + { 0xb06e, 0x0000 }, + { 0xb06f, 0x0000 }, + { 0xb070, 0x0000 }, + { 0xb071, 0x0000 }, + { 0xb072, 0x0000 }, + { 0xb073, 0x0000 }, + { 0xb074, 0x0000 }, + { 0xb075, 0x0000 }, + { 0xb076, 0x0000 }, + { 0xb077, 0x0000 }, + { 0xb078, 0x0000 }, + { 0xb079, 0x0000 }, + { 0xb07a, 0x0000 }, + { 0xb07b, 0x0000 }, + { 0xb07c, 0x0000 }, + { 0xb07d, 0x0000 }, + { 0xb07e, 0x0000 }, + { 0xb07f, 0x0000 }, + { 0xb080, 0x0000 }, + { 0xb081, 0x0000 }, + { 0xb082, 0x0000 }, + { 0xb083, 0x0000 }, + { 0xb084, 0x0000 }, + { 0xb085, 0x0000 }, + { 0xb086, 0x0000 }, + { 0xb087, 0x0000 }, + { 0xb088, 0x0000 }, + { 0xb089, 0x0000 }, + { 0xb08a, 0x0000 }, + { 0xb08b, 0x0000 }, + { 0xb08c, 0x0000 }, + { 0xb08d, 0x0000 }, + { 0xb08e, 0x0000 }, + { 0xb08f, 0x0000 }, + { 0xb090, 0x0000 }, + { 0xb091, 0x0000 }, + { 0xb092, 0x0000 }, + { 0xb093, 0x0000 }, + { 0xb094, 0x0000 }, + { 0xb095, 0x0000 }, + { 0xb096, 0x0000 }, + { 0xb097, 0x0000 }, + { 0xb098, 0x0000 }, + { 0xb099, 0x0000 }, + { 0xb09a, 0x0000 }, + { 0xb09b, 0x0000 }, + { 0xb09c, 0x0000 }, + { 0xb09d, 0x0000 }, + { 0xb09e, 0x0000 }, + { 0xb09f, 0x0000 }, + { 0xb0a0, 0x0000 }, + { 0xb0a1, 0x0000 }, + { 0xb0a2, 0x0000 }, + { 0xb0a3, 0x0000 }, + { 0xb0a4, 0x0000 }, + { 0xb0a5, 0x0000 }, + { 0xb0a6, 0x0000 }, + { 0xb0a7, 0x0000 }, + { 0xb0a8, 0x0000 }, + { 0xb0a9, 0x0000 }, + { 0xb0aa, 0x0000 }, + { 0xb0ab, 0x0000 }, + { 0xb0ac, 0x0000 }, + { 0xb0ad, 0x0000 }, + { 0xb0ae, 0x0000 }, + { 0xb0af, 0x0000 }, + { 0xb0b0, 0x0000 }, + { 0xb0b1, 0x0000 }, + { 0xb0b2, 0x0000 }, + { 0xb0b3, 0x0000 }, + { 0xb0b4, 0x0000 }, + { 0xb0b5, 0x0000 }, + { 0xb0b6, 0x0000 }, + { 0xb0b7, 0x0000 }, + { 0xb0b8, 0x0000 }, + { 0xb0b9, 0x0000 }, + { 0xb0ba, 0x0000 }, + { 0xb0bb, 0x0000 }, + { 0xb0bc, 0x0000 }, + { 0xb0bd, 0x0000 }, + { 0xb0be, 0x0000 }, + { 0xb0bf, 0x0000 }, + { 0xb0c0, 0x0000 }, + { 0xb0c1, 0x0000 }, + { 0xb0c2, 0x0000 }, + { 0xb0c3, 0x0000 }, + { 0xb0c4, 0x0000 }, + { 0xb0c5, 0x0000 }, + { 0xb0c6, 0x0000 }, + { 0xb0c7, 0x0000 }, + { 0xb0c8, 0x0000 }, + { 0xb0c9, 0x0000 }, + { 0xb0ca, 0x0000 }, + { 0xb0cb, 0x0000 }, + { 0xb0cc, 0x0000 }, + { 0xb0cd, 0x0000 }, + { 0xb0ce, 0x0000 }, + { 0xb0cf, 0x0000 }, + { 0xb0d0, 0x0000 }, + { 0xb0d1, 0x0000 }, + { 0xb0d2, 0x0000 }, + { 0xb0d3, 0x0000 }, + { 0xb0d4, 0x0000 }, + { 0xb0d5, 0x0000 }, + { 0xb0d6, 0x0000 }, + { 0xb0d7, 0x0000 }, + { 0xb0d8, 0x0000 }, + { 0xb0d9, 0x0000 }, + { 0xb0da, 0x0000 }, + { 0xb0db, 0x0000 }, + { 0xb0dc, 0x0000 }, + { 0xb0dd, 0x0000 }, + { 0xb0de, 0x0000 }, + { 0xb0df, 0x0000 }, + { 0xb0e0, 0x0000 }, + { 0xb0e1, 0x0000 }, + { 0xb0e2, 0x0000 }, + { 0xb0e3, 0x0000 }, + { 0xb0e4, 0x0000 }, + { 0xb0e5, 0x0000 }, + { 0xb0e6, 0x0000 }, + { 0xb0e7, 0x0000 }, + { 0xb0e8, 0x0000 }, + { 0xb0e9, 0x0000 }, + { 0xb0ea, 0x0000 }, + { 0xb0eb, 0x0000 }, + { 0xb0ec, 0x0000 }, + { 0xb0ed, 0x0000 }, + { 0xb0ee, 0x0000 }, + { 0xb0ef, 0x0000 }, + { 0xb0f0, 0x0000 }, + { 0xb0f1, 0x0000 }, + { 0xb0f2, 0x0000 }, + { 0xb0f3, 0x0000 }, + { 0xb0f4, 0x0000 }, + { 0xb0f5, 0x0000 }, + { 0xb0f6, 0x0000 }, + { 0xb0f7, 0x0000 }, + { 0xb0f8, 0x0000 }, + { 0xb0f9, 0x0000 }, + { 0xb0fa, 0x0000 }, + { 0xb0fb, 0x0000 }, + { 0xb0fc, 0x0000 }, + { 0xb0fd, 0x0000 }, + { 0xb0fe, 0x0000 }, + { 0xb0ff, 0x0000 }, + { 0xb100, 0x0000 }, + { 0xb101, 0x0000 }, + { 0xb102, 0x0000 }, + { 0xb103, 0x0000 }, + { 0xb104, 0x0000 }, + { 0xb105, 0x0000 }, + { 0xb106, 0x0000 }, + { 0xb107, 0x0000 }, + { 0xb108, 0x0000 }, + { 0xb109, 0x0000 }, + { 0xb10a, 0x0000 }, + { 0xb10b, 0x0000 }, + { 0xb10c, 0x0000 }, + { 0xb10d, 0x0000 }, + { 0xb10e, 0x0000 }, + { 0xb10f, 0x0000 }, + { 0xb110, 0x0000 }, + { 0xb111, 0x0000 }, + { 0xb112, 0x0000 }, + { 0xb113, 0x0000 }, + { 0xb114, 0x0000 }, + { 0xb115, 0x0000 }, + { 0xb116, 0x0000 }, + { 0xb117, 0x0000 }, + { 0xb118, 0x0000 }, + { 0xb119, 0x0000 }, + { 0xb11a, 0x0000 }, + { 0xb11b, 0x0000 }, + { 0xb11c, 0x0000 }, + { 0xb11d, 0x0000 }, + { 0xb11e, 0x0000 }, + { 0xb11f, 0x0000 }, + { 0xb120, 0x0000 }, + { 0xb121, 0x0000 }, + { 0xb122, 0x0000 }, + { 0xb123, 0x0000 }, + { 0xb124, 0x0000 }, + { 0xb125, 0x0000 }, + { 0xb126, 0x0000 }, + { 0xb127, 0x0000 }, + { 0xb128, 0x0000 }, + { 0xb129, 0x0000 }, + { 0xb12a, 0x0000 }, + { 0xb12b, 0x0000 }, + { 0xb12c, 0x0000 }, + { 0xb12d, 0x0000 }, + { 0xb12e, 0x0000 }, + { 0xb12f, 0x0000 }, + { 0xb130, 0x0000 }, + { 0xb131, 0x0000 }, + { 0xb132, 0x0000 }, + { 0xb133, 0x0000 }, + { 0xb134, 0x0000 }, + { 0xb135, 0x0000 }, + { 0xb136, 0x0000 }, + { 0xb137, 0x0000 }, + { 0xb138, 0x0000 }, + { 0xb139, 0x0000 }, + { 0xb13a, 0x0000 }, + { 0xb13b, 0x0000 }, + { 0xb13c, 0x0000 }, + { 0xb13d, 0x0000 }, + { 0xb13e, 0x0000 }, + { 0xb13f, 0x0000 }, + { 0xb140, 0x0000 }, + { 0xb141, 0x0000 }, + { 0xb142, 0x0000 }, + { 0xb143, 0x0000 }, + { 0xb144, 0x0000 }, + { 0xb145, 0x0000 }, + { 0xb146, 0x0000 }, + { 0xb147, 0x0000 }, + { 0xb148, 0x0000 }, + { 0xb149, 0x0000 }, + { 0xb14a, 0x0000 }, + { 0xb14b, 0x0000 }, + { 0xb14c, 0x0000 }, + { 0xb14d, 0x0000 }, + { 0xb14e, 0x0000 }, + { 0xb14f, 0x0000 }, + { 0xb150, 0x0000 }, + { 0xb151, 0x0000 }, + { 0xb152, 0x0000 }, + { 0xb153, 0x0000 }, + { 0xb154, 0x0000 }, + { 0xb155, 0x0000 }, + { 0xb156, 0x0000 }, + { 0xb157, 0x0000 }, + { 0xb158, 0x0000 }, + { 0xb159, 0x0000 }, + { 0xb15a, 0x0000 }, + { 0xb15b, 0x0000 }, + { 0xb15c, 0x0000 }, + { 0xb15d, 0x0000 }, + { 0xb15e, 0x0000 }, + { 0xb15f, 0x0000 }, + { 0xb160, 0x0000 }, + { 0xb161, 0x0000 }, + { 0xb162, 0x0000 }, + { 0xb163, 0x0000 }, + { 0xb164, 0x0000 }, + { 0xb165, 0x0000 }, + { 0xb166, 0x0000 }, + { 0xb167, 0x0000 }, + { 0xb168, 0x0000 }, + { 0xb169, 0x0000 }, + { 0xb16a, 0x0000 }, + { 0xb16b, 0x0000 }, + { 0xb16c, 0x0000 }, + { 0xb16d, 0x0000 }, + { 0xb16e, 0x0000 }, + { 0xb16f, 0x0000 }, + { 0xb170, 0x0000 }, + { 0xb171, 0x0000 }, + { 0xb172, 0x0000 }, + { 0xb173, 0x0000 }, + { 0xb174, 0x0000 }, + { 0xb175, 0x0000 }, + { 0xb176, 0x0000 }, + { 0xb177, 0x0000 }, + { 0xb178, 0x0000 }, + { 0xb179, 0x0000 }, + { 0xb17a, 0x0000 }, + { 0xb17b, 0x0000 }, + { 0xb17c, 0x0000 }, + { 0xb17d, 0x0000 }, + { 0xb17e, 0x0000 }, + { 0xb17f, 0x0000 }, + { 0xb180, 0x0000 }, + { 0xb181, 0x0000 }, + { 0xb182, 0x0000 }, + { 0xb183, 0x0000 }, + { 0xb184, 0x0000 }, + { 0xb185, 0x0000 }, + { 0xb186, 0x0000 }, + { 0xb187, 0x0000 }, + { 0xb188, 0x0000 }, + { 0xb189, 0x0000 }, + { 0xb18a, 0x0000 }, + { 0xb18b, 0x0000 }, + { 0xb18c, 0x0000 }, + { 0xb18d, 0x0000 }, + { 0xb18e, 0x0000 }, + { 0xb18f, 0x0000 }, + { 0xb190, 0x0000 }, + { 0xb191, 0x0000 }, + { 0xb192, 0x0000 }, + { 0xb193, 0x0000 }, + { 0xb194, 0x0000 }, + { 0xb195, 0x0000 }, + { 0xb196, 0x0000 }, + { 0xb197, 0x0000 }, + { 0xb198, 0x0000 }, + { 0xb199, 0x0000 }, + { 0xb19a, 0x0000 }, + { 0xb19b, 0x0000 }, + { 0xb19c, 0x0000 }, + { 0xb19d, 0x0000 }, + { 0xb19e, 0x0000 }, + { 0xb19f, 0x0000 }, + { 0xb1a0, 0x0000 }, + { 0xb1a1, 0x0000 }, + { 0xb1a2, 0x0000 }, + { 0xb1a3, 0x0000 }, + { 0xb1a4, 0x0000 }, + { 0xb1a5, 0x0000 }, + { 0xb1a6, 0x0000 }, + { 0xb1a7, 0x0000 }, + { 0xb1a8, 0x0000 }, + { 0xb1a9, 0x0000 }, + { 0xb1aa, 0x0000 }, + { 0xb1ab, 0x0000 }, + { 0xb1ac, 0x0000 }, + { 0xb1ad, 0x0000 }, + { 0xb1ae, 0x0000 }, + { 0xb1af, 0x0000 }, + { 0xb1b0, 0x0000 }, + { 0xb1b1, 0x0000 }, + { 0xb1b2, 0x0000 }, + { 0xb1b3, 0x0000 }, + { 0xb1b4, 0x0000 }, + { 0xb1b5, 0x0000 }, + { 0xb1b6, 0x0000 }, + { 0xb1b7, 0x0000 }, + { 0xb1b8, 0x0000 }, + { 0xb1b9, 0x0000 }, + { 0xb1ba, 0x0000 }, + { 0xb1bb, 0x0000 }, + { 0xb1bc, 0x0000 }, + { 0xb1bd, 0x0000 }, + { 0xb1be, 0x0000 }, + { 0xb1bf, 0x0000 }, + { 0xb1c0, 0x0000 }, + { 0xb1c1, 0x0000 }, + { 0xb1c2, 0x0000 }, + { 0xb1c3, 0x0000 }, + { 0xb1c4, 0x0000 }, + { 0xb1c5, 0x0000 }, + { 0xb1c6, 0x0000 }, + { 0xb1c7, 0x0000 }, + { 0xb1c8, 0x0000 }, + { 0xb1c9, 0x0000 }, + { 0xb1ca, 0x0000 }, + { 0xb1cb, 0x0000 }, + { 0xb1cc, 0x0000 }, + { 0xb1cd, 0x0000 }, + { 0xb1ce, 0x0000 }, + { 0xb1cf, 0x0000 }, + { 0xb1d0, 0x0000 }, + { 0xb1d1, 0x0000 }, + { 0xb1d2, 0x0000 }, + { 0xb1d3, 0x0000 }, + { 0xb1d4, 0x0000 }, + { 0xb1d5, 0x0000 }, + { 0xb1d6, 0x0000 }, + { 0xb1d7, 0x0000 }, + { 0xb1d8, 0x0000 }, + { 0xb1d9, 0x0000 }, + { 0xb1da, 0x0000 }, + { 0xb1db, 0x0000 }, + { 0xb1dc, 0x0000 }, + { 0xb1dd, 0x0000 }, + { 0xb1de, 0x0000 }, + { 0xb1df, 0x0000 }, + { 0xb1e0, 0x0000 }, + { 0xb1e1, 0x0000 }, + { 0xb1e2, 0x0000 }, + { 0xb1e3, 0x0000 }, + { 0xb1e4, 0x0000 }, + { 0xb1e5, 0x0000 }, + { 0xb1e6, 0x0000 }, + { 0xb1e7, 0x0000 }, + { 0xb1e8, 0x0000 }, + { 0xb1e9, 0x0000 }, + { 0xb1ea, 0x0000 }, + { 0xb1eb, 0x0000 }, + { 0xb1ec, 0x0000 }, + { 0xb1ed, 0x0000 }, + { 0xb1ee, 0x0000 }, + { 0xb1ef, 0x0000 }, + { 0xb1f0, 0x0000 }, + { 0xb1f1, 0x0000 }, + { 0xb1f2, 0x0000 }, + { 0xb1f3, 0x0000 }, + { 0xb1f4, 0x0000 }, + { 0xb1f5, 0x0000 }, + { 0xb1f6, 0x0000 }, + { 0xb1f7, 0x0000 }, + { 0xb1f8, 0x0000 }, + { 0xb1f9, 0x0000 }, + { 0xb1fa, 0x0000 }, + { 0xb1fb, 0x0000 }, + { 0xb1fc, 0x0000 }, + { 0xb1fd, 0x0000 }, + { 0xb1fe, 0x0000 }, + { 0xb1ff, 0x0000 }, + { 0xb200, 0x0000 }, + { 0xb201, 0x0000 }, + { 0xb202, 0x0000 }, + { 0xb203, 0x0000 }, + { 0xb204, 0x0000 }, + { 0xb205, 0x0000 }, + { 0xb206, 0x0000 }, + { 0xb207, 0x0000 }, + { 0xb208, 0x0000 }, + { 0xb209, 0x0000 }, + { 0xb20a, 0x0000 }, + { 0xb20b, 0x0000 }, + { 0xb20c, 0x0000 }, + { 0xb20d, 0x0000 }, + { 0xb20e, 0x0000 }, + { 0xb20f, 0x0000 }, + { 0xb210, 0x0000 }, + { 0xb211, 0x0000 }, + { 0xb212, 0x0000 }, + { 0xb213, 0x0000 }, + { 0xb214, 0x0000 }, + { 0xb215, 0x0000 }, + { 0xb216, 0x0000 }, + { 0xb217, 0x0000 }, + { 0xb218, 0x0000 }, + { 0xb219, 0x0000 }, + { 0xb21a, 0x0000 }, + { 0xb21b, 0x0000 }, + { 0xb21c, 0x0000 }, + { 0xb21d, 0x0000 }, + { 0xb21e, 0x0000 }, + { 0xb21f, 0x0000 }, + { 0xb220, 0x0000 }, + { 0xb221, 0x0000 }, + { 0xb222, 0x0000 }, + { 0xb223, 0x0000 }, + { 0xb224, 0x0000 }, + { 0xb225, 0x0000 }, + { 0xb226, 0x0000 }, + { 0xb227, 0x0000 }, + { 0xb228, 0x0000 }, + { 0xb229, 0x0000 }, + { 0xb22a, 0x0000 }, + { 0xb22b, 0x0000 }, + { 0xb22c, 0x0000 }, + { 0xb22d, 0x0000 }, + { 0xb22e, 0x0000 }, + { 0xb22f, 0x0000 }, + { 0xb230, 0x0000 }, + { 0xb231, 0x0000 }, + { 0xb232, 0x0000 }, + { 0xb233, 0x0000 }, + { 0xb234, 0x0000 }, + { 0xb235, 0x0000 }, + { 0xb236, 0x0000 }, + { 0xb237, 0x0000 }, + { 0xb238, 0x0000 }, + { 0xb239, 0x0000 }, + { 0xb23a, 0x0000 }, + { 0xb23b, 0x0000 }, + { 0xb23c, 0x0000 }, + { 0xb23d, 0x0000 }, + { 0xb23e, 0x0000 }, + { 0xb23f, 0x0000 }, + { 0xb240, 0x0000 }, + { 0xb241, 0x0000 }, + { 0xb242, 0x0000 }, + { 0xb243, 0x0000 }, + { 0xb244, 0x0000 }, + { 0xb245, 0x0000 }, + { 0xb246, 0x0000 }, + { 0xb247, 0x0000 }, + { 0xb248, 0x0000 }, + { 0xb249, 0x0000 }, + { 0xb24a, 0x0000 }, + { 0xb24b, 0x0000 }, + { 0xb24c, 0x0000 }, + { 0xb24d, 0x0000 }, + { 0xb24e, 0x0000 }, + { 0xb24f, 0x0000 }, + { 0xb250, 0x0000 }, + { 0xb251, 0x0000 }, + { 0xb252, 0x0000 }, + { 0xb253, 0x0000 }, + { 0xb254, 0x0000 }, + { 0xb255, 0x0000 }, + { 0xb256, 0x0000 }, + { 0xb257, 0x0000 }, + { 0xb258, 0x0000 }, + { 0xb259, 0x0000 }, + { 0xb25a, 0x0000 }, + { 0xb25b, 0x0000 }, + { 0xb25c, 0x0000 }, + { 0xb25d, 0x0000 }, + { 0xb25e, 0x0000 }, + { 0xb25f, 0x0000 }, + { 0xb260, 0x0000 }, + { 0xb261, 0x0000 }, + { 0xb262, 0x0000 }, + { 0xb263, 0x0000 }, + { 0xb264, 0x0000 }, + { 0xb265, 0x0000 }, + { 0xb266, 0x0000 }, + { 0xb267, 0x0000 }, + { 0xb268, 0x0000 }, + { 0xb269, 0x0000 }, + { 0xb26a, 0x0000 }, + { 0xb26b, 0x0000 }, + { 0xb26c, 0x0000 }, + { 0xb26d, 0x0000 }, + { 0xb26e, 0x0000 }, + { 0xb26f, 0x0000 }, + { 0xb270, 0x0000 }, + { 0xb271, 0x0000 }, + { 0xb272, 0x0000 }, + { 0xb273, 0x0000 }, + { 0xb274, 0x0000 }, + { 0xb275, 0x0000 }, + { 0xb276, 0x0000 }, + { 0xb277, 0x0000 }, + { 0xb278, 0x0000 }, + { 0xb279, 0x0000 }, + { 0xb27a, 0x0000 }, + { 0xb27b, 0x0000 }, + { 0xb27c, 0x0000 }, + { 0xb27d, 0x0000 }, + { 0xb27e, 0x0000 }, + { 0xb27f, 0x0000 }, + { 0xb280, 0x0000 }, + { 0xb281, 0x0000 }, + { 0xb282, 0x0000 }, + { 0xb283, 0x0000 }, + { 0xb284, 0x0000 }, + { 0xb285, 0x0000 }, + { 0xb286, 0x0000 }, + { 0xb287, 0x0000 }, + { 0xb288, 0x0000 }, + { 0xb289, 0x0000 }, + { 0xb28a, 0x0000 }, + { 0xb28b, 0x0000 }, + { 0xb28c, 0x0000 }, + { 0xb28d, 0x0000 }, + { 0xb28e, 0x0000 }, + { 0xb28f, 0x0000 }, + { 0xb290, 0x0000 }, + { 0xb291, 0x0000 }, + { 0xb292, 0x0000 }, + { 0xb293, 0x0000 }, + { 0xb294, 0x0000 }, + { 0xb295, 0x0000 }, + { 0xb296, 0x0000 }, + { 0xb297, 0x0000 }, + { 0xb298, 0x0000 }, + { 0xb299, 0x0000 }, + { 0xb29a, 0x0000 }, + { 0xb29b, 0x0000 }, + { 0xb29c, 0x0000 }, + { 0xb29d, 0x0000 }, + { 0xb29e, 0x0000 }, + { 0xb29f, 0x0000 }, + { 0xb2a0, 0x0000 }, + { 0xb2a1, 0x0000 }, + { 0xb2a2, 0x0000 }, + { 0xb2a3, 0x0000 }, + { 0xb2a4, 0x0000 }, + { 0xb2a5, 0x0000 }, + { 0xb2a6, 0x0000 }, + { 0xb2a7, 0x0000 }, + { 0xb2a8, 0x0000 }, + { 0xb2a9, 0x0000 }, + { 0xb2aa, 0x0000 }, + { 0xb2ab, 0x0000 }, + { 0xb2ac, 0x0000 }, + { 0xb2ad, 0x0000 }, + { 0xb2ae, 0x0000 }, + { 0xb2af, 0x0000 }, + { 0xb2b0, 0x0000 }, + { 0xb2b1, 0x0000 }, + { 0xb2b2, 0x0000 }, + { 0xb2b3, 0x0000 }, + { 0xb2b4, 0x0000 }, + { 0xb2b5, 0x0000 }, + { 0xb2b6, 0x0000 }, + { 0xb2b7, 0x0000 }, + { 0xb2b8, 0x0000 }, + { 0xb2b9, 0x0000 }, + { 0xb2ba, 0x0000 }, + { 0xb2bb, 0x0000 }, + { 0xb2bc, 0x0000 }, + { 0xb2bd, 0x0000 }, + { 0xb2be, 0x0000 }, + { 0xb2bf, 0x0000 }, + { 0xb2c0, 0x0000 }, + { 0xb2c1, 0x0000 }, + { 0xb2c2, 0x0000 }, + { 0xb2c3, 0x0000 }, + { 0xb2c4, 0x0000 }, + { 0xb2c5, 0x0000 }, + { 0xb2c6, 0x0000 }, + { 0xb2c7, 0x0000 }, + { 0xb2c8, 0x0000 }, + { 0xb2c9, 0x0000 }, + { 0xb2ca, 0x0000 }, + { 0xb2cb, 0x0000 }, + { 0xb2cc, 0x0000 }, + { 0xb2cd, 0x0000 }, + { 0xb2ce, 0x0000 }, + { 0xb2cf, 0x0000 }, + { 0xb2d0, 0x0000 }, + { 0xb2d1, 0x0000 }, + { 0xb2d2, 0x0000 }, + { 0xb2d3, 0x0000 }, + { 0xb2d4, 0x0000 }, + { 0xb2d5, 0x0000 }, + { 0xb2d6, 0x0000 }, + { 0xb2d7, 0x0000 }, + { 0xb2d8, 0x0000 }, + { 0xb2d9, 0x0000 }, + { 0xb2da, 0x0000 }, + { 0xb2db, 0x0000 }, + { 0xb2dc, 0x0000 }, + { 0xb2dd, 0x0000 }, + { 0xb2de, 0x0000 }, + { 0xb2df, 0x0000 }, + { 0xb2e0, 0x0000 }, + { 0xb2e1, 0x0000 }, + { 0xb2e2, 0x0000 }, + { 0xb2e3, 0x0000 }, + { 0xb2e4, 0x0000 }, + { 0xb2e5, 0x0000 }, + { 0xb2e6, 0x0000 }, + { 0xb2e7, 0x0000 }, + { 0xb2e8, 0x0000 }, + { 0xb2e9, 0x0000 }, + { 0xb2ea, 0x0000 }, + { 0xb2eb, 0x0000 }, + { 0xb2ec, 0x0000 }, + { 0xb2ed, 0x0000 }, + { 0xb2ee, 0x0000 }, + { 0xb2ef, 0x0000 }, + { 0xb2f0, 0x0000 }, + { 0xb2f1, 0x0000 }, + { 0xb2f2, 0x0000 }, + { 0xb2f3, 0x0000 }, + { 0xb2f4, 0x0000 }, + { 0xb2f5, 0x0000 }, + { 0xb2f6, 0x0000 }, + { 0xb2f7, 0x0000 }, + { 0xb2f8, 0x0000 }, + { 0xb2f9, 0x0000 }, + { 0xb2fa, 0x0000 }, + { 0xb2fb, 0x0000 }, + { 0xb2fc, 0x0000 }, + { 0xb2fd, 0x0000 }, + { 0xb2fe, 0x0000 }, + { 0xb2ff, 0x0000 }, + { 0xb300, 0x0000 }, + { 0xb301, 0x0000 }, + { 0xb302, 0x0000 }, + { 0xb303, 0x0000 }, + { 0xb304, 0x0000 }, + { 0xb305, 0x0000 }, + { 0xb306, 0x0000 }, + { 0xb307, 0x0000 }, + { 0xb308, 0x0000 }, + { 0xb309, 0x0000 }, + { 0xb30a, 0x0000 }, + { 0xb30b, 0x0000 }, + { 0xb30c, 0x0000 }, + { 0xb30d, 0x0000 }, + { 0xb30e, 0x0000 }, + { 0xb30f, 0x0000 }, + { 0xb310, 0x0000 }, + { 0xb311, 0x0000 }, + { 0xb312, 0x0000 }, + { 0xb313, 0x0000 }, + { 0xb314, 0x0000 }, + { 0xb315, 0x0000 }, + { 0xb316, 0x0000 }, + { 0xb317, 0x0000 }, + { 0xb318, 0x0000 }, + { 0xb319, 0x0000 }, + { 0xb31a, 0x0000 }, + { 0xb31b, 0x0000 }, + { 0xb31c, 0x0000 }, + { 0xb31d, 0x0000 }, + { 0xb31e, 0x0000 }, + { 0xb31f, 0x0000 }, + { 0xb320, 0x0000 }, + { 0xb321, 0x0000 }, + { 0xb322, 0x0000 }, + { 0xb323, 0x0000 }, + { 0xb324, 0x0000 }, + { 0xb325, 0x0000 }, + { 0xb326, 0x0000 }, + { 0xb327, 0x0000 }, + { 0xb328, 0x0000 }, + { 0xb329, 0x0000 }, + { 0xb32a, 0x0000 }, + { 0xb32b, 0x0000 }, + { 0xb32c, 0x0000 }, + { 0xb32d, 0x0000 }, + { 0xb32e, 0x0000 }, + { 0xb32f, 0x0000 }, + { 0xb330, 0x0000 }, + { 0xb331, 0x0000 }, + { 0xb332, 0x0000 }, + { 0xb333, 0x0000 }, + { 0xb334, 0x0000 }, + { 0xb335, 0x0000 }, + { 0xb336, 0x0000 }, + { 0xb337, 0x0000 }, + { 0xb338, 0x0000 }, + { 0xb339, 0x0000 }, + { 0xb33a, 0x0000 }, + { 0xb33b, 0x0000 }, + { 0xb33c, 0x0000 }, + { 0xb33d, 0x0000 }, + { 0xb33e, 0x0000 }, + { 0xb33f, 0x0000 }, + { 0xb340, 0x0000 }, + { 0xb341, 0x0000 }, + { 0xb342, 0x0000 }, + { 0xb343, 0x0000 }, + { 0xb344, 0x0000 }, + { 0xb345, 0x0000 }, + { 0xb346, 0x0000 }, + { 0xb347, 0x0000 }, + { 0xb348, 0x0000 }, + { 0xb349, 0x0000 }, + { 0xb34a, 0x0000 }, + { 0xb34b, 0x0000 }, + { 0xb34c, 0x0000 }, + { 0xb34d, 0x0000 }, + { 0xb34e, 0x0000 }, + { 0xb34f, 0x0000 }, + { 0xb350, 0x0000 }, + { 0xb351, 0x0000 }, + { 0xb352, 0x0000 }, + { 0xb353, 0x0000 }, + { 0xb354, 0x0000 }, + { 0xb355, 0x0000 }, + { 0xb356, 0x0000 }, + { 0xb357, 0x0000 }, + { 0xb358, 0x0000 }, + { 0xb359, 0x0000 }, + { 0xb35a, 0x0000 }, + { 0xb35b, 0x0000 }, + { 0xb35c, 0x0000 }, + { 0xb35d, 0x0000 }, + { 0xb35e, 0x0000 }, + { 0xb35f, 0x0000 }, + { 0xb360, 0x0000 }, + { 0xb361, 0x0000 }, + { 0xb362, 0x0000 }, + { 0xb363, 0x0000 }, + { 0xb364, 0x0000 }, + { 0xb365, 0x0000 }, + { 0xb366, 0x0000 }, + { 0xb367, 0x0000 }, + { 0xb368, 0x0000 }, + { 0xb369, 0x0000 }, + { 0xb36a, 0x0000 }, + { 0xb36b, 0x0000 }, + { 0xb36c, 0x0000 }, + { 0xb36d, 0x0000 }, + { 0xb36e, 0x0000 }, + { 0xb36f, 0x0000 }, + { 0xb370, 0x0000 }, + { 0xb371, 0x0000 }, + { 0xb372, 0x0000 }, + { 0xb373, 0x0000 }, + { 0xb374, 0x0000 }, + { 0xb375, 0x0000 }, + { 0xb376, 0x0000 }, + { 0xb377, 0x0000 }, + { 0xb378, 0x0000 }, + { 0xb379, 0x0000 }, + { 0xb37a, 0x0000 }, + { 0xb37b, 0x0000 }, + { 0xb37c, 0x0000 }, + { 0xb37d, 0x0000 }, + { 0xb37e, 0x0000 }, + { 0xb37f, 0x0000 }, + { 0xb380, 0x0000 }, + { 0xb381, 0x0000 }, + { 0xb382, 0x0000 }, + { 0xb383, 0x0000 }, + { 0xb384, 0x0000 }, + { 0xb385, 0x0000 }, + { 0xb386, 0x0000 }, + { 0xb387, 0x0000 }, + { 0xb388, 0x0000 }, + { 0xb389, 0x0000 }, + { 0xb38a, 0x0000 }, + { 0xb38b, 0x0000 }, + { 0xb38c, 0x0000 }, + { 0xb38d, 0x0000 }, + { 0xb38e, 0x0000 }, + { 0xb38f, 0x0000 }, + { 0xb390, 0x0000 }, + { 0xb391, 0x0000 }, + { 0xb392, 0x0000 }, + { 0xb393, 0x0000 }, + { 0xb394, 0x0000 }, + { 0xb395, 0x0000 }, + { 0xb396, 0x0000 }, + { 0xb397, 0x0000 }, + { 0xb398, 0x0000 }, + { 0xb399, 0x0000 }, + { 0xb39a, 0x0000 }, + { 0xb39b, 0x0000 }, + { 0xb39c, 0x0000 }, + { 0xb39d, 0x0000 }, + { 0xb39e, 0x0000 }, + { 0xb39f, 0x0000 }, + { 0xb3a0, 0x0000 }, + { 0xb3a1, 0x0000 }, + { 0xb3a2, 0x0000 }, + { 0xb3a3, 0x0000 }, + { 0xb3a4, 0x0000 }, + { 0xb3a5, 0x0000 }, + { 0xb3a6, 0x0000 }, + { 0xb3a7, 0x0000 }, + { 0xb3a8, 0x0000 }, + { 0xb3a9, 0x0000 }, + { 0xb3aa, 0x0000 }, + { 0xb3ab, 0x0000 }, + { 0xb3ac, 0x0000 }, + { 0xb3ad, 0x0000 }, + { 0xb3ae, 0x0000 }, + { 0xb3af, 0x0000 }, + { 0xb3b0, 0x0000 }, + { 0xb3b1, 0x0000 }, + { 0xb3b2, 0x0000 }, + { 0xb3b3, 0x0000 }, + { 0xb3b4, 0x0000 }, + { 0xb3b5, 0x0000 }, + { 0xb3b6, 0x0000 }, + { 0xb3b7, 0x0000 }, + { 0xb3b8, 0x0000 }, + { 0xb3b9, 0x0000 }, + { 0xb3ba, 0x0000 }, + { 0xb3bb, 0x0000 }, + { 0xb3bc, 0x0000 }, + { 0xb3bd, 0x0000 }, + { 0xb3be, 0x0000 }, + { 0xb3bf, 0x0000 }, + { 0xb3c0, 0x0000 }, + { 0xb3c1, 0x0000 }, + { 0xb3c2, 0x0000 }, + { 0xb3c3, 0x0000 }, + { 0xb3c4, 0x0000 }, + { 0xb3c5, 0x0000 }, + { 0xb3c6, 0x0000 }, + { 0xb3c7, 0x0000 }, + { 0xb3c8, 0x0000 }, + { 0xb3c9, 0x0000 }, + { 0xb3ca, 0x0000 }, + { 0xb3cb, 0x0000 }, + { 0xb3cc, 0x0000 }, + { 0xb3cd, 0x0000 }, + { 0xb3ce, 0x0000 }, + { 0xb3cf, 0x0000 }, + { 0xb3d0, 0x0000 }, + { 0xb3d1, 0x0000 }, + { 0xb3d2, 0x0000 }, + { 0xb3d3, 0x0000 }, + { 0xb3d4, 0x0000 }, + { 0xb3d5, 0x0000 }, + { 0xb3d6, 0x0000 }, + { 0xb3d7, 0x0000 }, + { 0xb3d8, 0x0000 }, + { 0xb3d9, 0x0000 }, + { 0xb3da, 0x0000 }, + { 0xb3db, 0x0000 }, + { 0xb3dc, 0x0000 }, + { 0xb3dd, 0x0000 }, + { 0xb3de, 0x0000 }, + { 0xb3df, 0x0000 }, + { 0xb3e0, 0x0000 }, + { 0xb3e1, 0x0000 }, + { 0xb3e2, 0x0000 }, + { 0xb3e3, 0x0000 }, + { 0xb3e4, 0x0000 }, + { 0xb3e5, 0x0000 }, + { 0xb3e6, 0x0000 }, + { 0xb3e7, 0x0000 }, + { 0xb3e8, 0x0000 }, + { 0xb3e9, 0x0000 }, + { 0xb3ea, 0x0000 }, + { 0xb3eb, 0x0000 }, + { 0xb3ec, 0x0000 }, + { 0xb3ed, 0x0000 }, + { 0xb3ee, 0x0000 }, + { 0xb3ef, 0x0000 }, + { 0xb3f0, 0x0000 }, + { 0xb3f1, 0x0000 }, + { 0xb3f2, 0x0000 }, + { 0xb3f3, 0x0000 }, + { 0xb3f4, 0x0000 }, + { 0xb3f5, 0x0000 }, + { 0xb3f6, 0x0000 }, + { 0xb3f7, 0x0000 }, + { 0xb3f8, 0x0000 }, + { 0xb3f9, 0x0000 }, + { 0xb3fa, 0x0000 }, + { 0xb3fb, 0x0000 }, + { 0xb3fc, 0x0000 }, + { 0xb3fd, 0x0000 }, + { 0xb3fe, 0x0000 }, + { 0xb3ff, 0x0000 }, + { 0xb400, 0x0000 }, + { 0xb401, 0x0000 }, + { 0xb402, 0x0000 }, + { 0xb403, 0x0000 }, + { 0xb404, 0x0000 }, + { 0xb405, 0x0000 }, + { 0xb406, 0x0000 }, + { 0xb407, 0x0000 }, + { 0xb408, 0x0000 }, + { 0xb409, 0x0000 }, + { 0xb40a, 0x0000 }, + { 0xb40b, 0x0000 }, + { 0xb40c, 0x0000 }, + { 0xb40d, 0x0000 }, + { 0xb40e, 0x0000 }, + { 0xb40f, 0x0000 }, + { 0xb410, 0x0000 }, + { 0xb411, 0x0000 }, + { 0xb412, 0x0000 }, + { 0xb413, 0x0000 }, + { 0xb414, 0x0000 }, + { 0xb415, 0x0000 }, + { 0xb416, 0x0000 }, + { 0xb417, 0x0000 }, + { 0xb418, 0x0000 }, + { 0xb419, 0x0000 }, + { 0xb41a, 0x0000 }, + { 0xb41b, 0x0000 }, + { 0xb41c, 0x0000 }, + { 0xb41d, 0x0000 }, + { 0xb41e, 0x0000 }, + { 0xb41f, 0x0000 }, + { 0xb420, 0x0000 }, + { 0xb421, 0x0000 }, + { 0xb422, 0x0000 }, + { 0xb423, 0x0000 }, + { 0xb424, 0x0000 }, + { 0xb425, 0x0000 }, + { 0xb426, 0x0000 }, + { 0xb427, 0x0000 }, + { 0xb428, 0x0000 }, + { 0xb429, 0x0000 }, + { 0xb42a, 0x0000 }, + { 0xb42b, 0x0000 }, + { 0xb42c, 0x0000 }, + { 0xb42d, 0x0000 }, + { 0xb42e, 0x0000 }, + { 0xb42f, 0x0000 }, + { 0xb430, 0x0000 }, + { 0xb431, 0x0000 }, + { 0xb432, 0x0000 }, + { 0xb433, 0x0000 }, + { 0xb434, 0x0000 }, + { 0xb435, 0x0000 }, + { 0xb436, 0x0000 }, + { 0xb437, 0x0000 }, + { 0xb438, 0x0000 }, + { 0xb439, 0x0000 }, + { 0xb43a, 0x0000 }, + { 0xb43b, 0x0000 }, + { 0xb43c, 0x0000 }, + { 0xb43d, 0x0000 }, + { 0xb43e, 0x0000 }, + { 0xb43f, 0x0000 }, + { 0xb440, 0x0000 }, + { 0xb441, 0x0000 }, + { 0xb442, 0x0000 }, + { 0xb443, 0x0000 }, + { 0xb444, 0x0000 }, + { 0xb445, 0x0000 }, + { 0xb446, 0x0000 }, + { 0xb447, 0x0000 }, + { 0xb448, 0x0000 }, + { 0xb449, 0x0000 }, + { 0xb44a, 0x0000 }, + { 0xb44b, 0x0000 }, + { 0xb44c, 0x0000 }, + { 0xb44d, 0x0000 }, + { 0xb44e, 0x0000 }, + { 0xb44f, 0x0000 }, + { 0xb450, 0x0000 }, + { 0xb451, 0x0000 }, + { 0xb452, 0x0000 }, + { 0xb453, 0x0000 }, + { 0xb454, 0x0000 }, + { 0xb455, 0x0000 }, + { 0xb456, 0x0000 }, + { 0xb457, 0x0000 }, + { 0xb458, 0x0000 }, + { 0xb459, 0x0000 }, + { 0xb45a, 0x0000 }, + { 0xb45b, 0x0000 }, + { 0xb45c, 0x0000 }, + { 0xb45d, 0x0000 }, + { 0xb45e, 0x0000 }, + { 0xb45f, 0x0000 }, + { 0xb460, 0x0000 }, + { 0xb461, 0x0000 }, + { 0xb462, 0x0000 }, + { 0xb463, 0x0000 }, + { 0xb464, 0x0000 }, + { 0xb465, 0x0000 }, + { 0xb466, 0x0000 }, + { 0xb467, 0x0000 }, + { 0xb468, 0x0000 }, + { 0xb469, 0x0000 }, + { 0xb46a, 0x0000 }, + { 0xb46b, 0x0000 }, + { 0xb46c, 0x0000 }, + { 0xb46d, 0x0000 }, + { 0xb46e, 0x0000 }, + { 0xb46f, 0x0000 }, + { 0xb470, 0x0000 }, + { 0xb471, 0x0000 }, + { 0xb472, 0x0000 }, + { 0xb473, 0x0000 }, + { 0xb474, 0x0000 }, + { 0xb475, 0x0000 }, + { 0xb476, 0x0000 }, + { 0xb477, 0x0000 }, + { 0xb478, 0x0000 }, + { 0xb479, 0x0000 }, + { 0xb47a, 0x0000 }, + { 0xb47b, 0x0000 }, + { 0xb47c, 0x0000 }, + { 0xb47d, 0x0000 }, + { 0xb47e, 0x0000 }, + { 0xb47f, 0x0000 }, + { 0xb480, 0x0000 }, + { 0xb481, 0x0000 }, + { 0xb482, 0x0000 }, + { 0xb483, 0x0000 }, + { 0xb484, 0x0000 }, + { 0xb485, 0x0000 }, + { 0xb486, 0x0000 }, + { 0xb487, 0x0000 }, + { 0xb488, 0x0000 }, + { 0xb489, 0x0000 }, + { 0xb48a, 0x0000 }, + { 0xb48b, 0x0000 }, + { 0xb48c, 0x0000 }, + { 0xb48d, 0x0000 }, + { 0xb48e, 0x0000 }, + { 0xb48f, 0x0000 }, + { 0xb490, 0x0000 }, + { 0xb491, 0x0000 }, + { 0xb492, 0x0000 }, + { 0xb493, 0x0000 }, + { 0xb494, 0x0000 }, + { 0xb495, 0x0000 }, + { 0xb496, 0x0000 }, + { 0xb497, 0x0000 }, + { 0xb498, 0x0000 }, + { 0xb499, 0x0000 }, + { 0xb49a, 0x0000 }, + { 0xb49b, 0x0000 }, + { 0xb49c, 0x0000 }, + { 0xb49d, 0x0000 }, + { 0xb49e, 0x0000 }, + { 0xb49f, 0x0000 }, + { 0xb4a0, 0x0000 }, + { 0xb4a1, 0x0000 }, + { 0xb4a2, 0x0000 }, + { 0xb4a3, 0x0000 }, + { 0xb4a4, 0x0000 }, + { 0xb4a5, 0x0000 }, + { 0xb4a6, 0x0000 }, + { 0xb4a7, 0x0000 }, + { 0xb4a8, 0x0000 }, + { 0xb4a9, 0x0000 }, + { 0xb4aa, 0x0000 }, + { 0xb4ab, 0x0000 }, + { 0xb4ac, 0x0000 }, + { 0xb4ad, 0x0000 }, + { 0xb4ae, 0x0000 }, + { 0xb4af, 0x0000 }, + { 0xb4b0, 0x0000 }, + { 0xb4b1, 0x0000 }, + { 0xb4b2, 0x0000 }, + { 0xb4b3, 0x0000 }, + { 0xb4b4, 0x0000 }, + { 0xb4b5, 0x0000 }, + { 0xb4b6, 0x0000 }, + { 0xb4b7, 0x0000 }, + { 0xb4b8, 0x0000 }, + { 0xb4b9, 0x0000 }, + { 0xb4ba, 0x0000 }, + { 0xb4bb, 0x0000 }, + { 0xb4bc, 0x0000 }, + { 0xb4bd, 0x0000 }, + { 0xb4be, 0x0000 }, + { 0xb4bf, 0x0000 }, + { 0xb4c0, 0x0000 }, + { 0xb4c1, 0x0000 }, + { 0xb4c2, 0x0000 }, + { 0xb4c3, 0x0000 }, + { 0xb4c4, 0x0000 }, + { 0xb4c5, 0x0000 }, + { 0xb4c6, 0x0000 }, + { 0xb4c7, 0x0000 }, + { 0xb4c8, 0x0000 }, + { 0xb4c9, 0x0000 }, + { 0xb4ca, 0x0000 }, + { 0xb4cb, 0x0000 }, + { 0xb4cc, 0x0000 }, + { 0xb4cd, 0x0000 }, + { 0xb4ce, 0x0000 }, + { 0xb4cf, 0x0000 }, + { 0xb4d0, 0x0000 }, + { 0xb4d1, 0x0000 }, + { 0xb4d2, 0x0000 }, + { 0xb4d3, 0x0000 }, + { 0xb4d4, 0x0000 }, + { 0xb4d5, 0x0000 }, + { 0xb4d6, 0x0000 }, + { 0xb4d7, 0x0000 }, + { 0xb4d8, 0x0000 }, + { 0xb4d9, 0x0000 }, + { 0xb4da, 0x0000 }, + { 0xb4db, 0x0000 }, + { 0xb4dc, 0x0000 }, + { 0xb4dd, 0x0000 }, + { 0xb4de, 0x0000 }, + { 0xb4df, 0x0000 }, + { 0xb4e0, 0x0000 }, + { 0xb4e1, 0x0000 }, + { 0xb4e2, 0x0000 }, + { 0xb4e3, 0x0000 }, + { 0xb4e4, 0x0000 }, + { 0xb4e5, 0x0000 }, + { 0xb4e6, 0x0000 }, + { 0xb4e7, 0x0000 }, + { 0xb4e8, 0x0000 }, + { 0xb4e9, 0x0000 }, + { 0xb4ea, 0x0000 }, + { 0xb4eb, 0x0000 }, + { 0xb4ec, 0x0000 }, + { 0xb4ed, 0x0000 }, + { 0xb4ee, 0x0000 }, + { 0xb4ef, 0x0000 }, + { 0xb4f0, 0x0000 }, + { 0xb4f1, 0x0000 }, + { 0xb4f2, 0x0000 }, + { 0xb4f3, 0x0000 }, + { 0xb4f4, 0x0000 }, + { 0xb4f5, 0x0000 }, + { 0xb4f6, 0x0000 }, + { 0xb4f7, 0x0000 }, + { 0xb4f8, 0x0000 }, + { 0xb4f9, 0x0000 }, + { 0xb4fa, 0x0000 }, + { 0xb4fb, 0x0000 }, + { 0xb4fc, 0x0000 }, + { 0xb4fd, 0x0000 }, + { 0xb4fe, 0x0000 }, + { 0xb4ff, 0x0000 }, + { 0xb500, 0x0000 }, + { 0xb501, 0x0000 }, + { 0xb502, 0x0000 }, + { 0xb503, 0x0000 }, + { 0xb504, 0x0000 }, + { 0xb505, 0x0000 }, + { 0xb506, 0x0000 }, + { 0xb507, 0x0000 }, + { 0xb508, 0x0000 }, + { 0xb509, 0x0000 }, + { 0xb50a, 0x0000 }, + { 0xb50b, 0x0000 }, + { 0xb50c, 0x0000 }, + { 0xb50d, 0x0000 }, + { 0xb50e, 0x0000 }, + { 0xb50f, 0x0000 }, + { 0xb510, 0x0000 }, + { 0xb511, 0x0000 }, + { 0xb512, 0x0000 }, + { 0xb513, 0x0000 }, + { 0xb514, 0x0000 }, + { 0xb515, 0x0000 }, + { 0xb516, 0x0000 }, + { 0xb517, 0x0000 }, + { 0xb518, 0x0000 }, + { 0xb519, 0x0000 }, + { 0xb51a, 0x0000 }, + { 0xb51b, 0x0000 }, + { 0xb51c, 0x0000 }, + { 0xb51d, 0x0000 }, + { 0xb51e, 0x0000 }, + { 0xb51f, 0x0000 }, + { 0xb520, 0x0000 }, + { 0xb521, 0x0000 }, + { 0xb522, 0x0000 }, + { 0xb523, 0x0000 }, + { 0xb524, 0x0000 }, + { 0xb525, 0x0000 }, + { 0xb526, 0x0000 }, + { 0xb527, 0x0000 }, + { 0xb528, 0x0000 }, + { 0xb529, 0x0000 }, + { 0xb52a, 0x0000 }, + { 0xb52b, 0x0000 }, + { 0xb52c, 0x0000 }, + { 0xb52d, 0x0000 }, + { 0xb52e, 0x0000 }, + { 0xb52f, 0x0000 }, + { 0xb530, 0x0000 }, + { 0xb531, 0x0000 }, + { 0xb532, 0x0000 }, + { 0xb533, 0x0000 }, + { 0xb534, 0x0000 }, + { 0xb535, 0x0000 }, + { 0xb536, 0x0000 }, + { 0xb537, 0x0000 }, + { 0xb538, 0x0000 }, + { 0xb539, 0x0000 }, + { 0xb53a, 0x0000 }, + { 0xb53b, 0x0000 }, + { 0xb53c, 0x0000 }, + { 0xb53d, 0x0000 }, + { 0xb53e, 0x0000 }, + { 0xb53f, 0x0000 }, + { 0xb540, 0x0000 }, + { 0xb541, 0x0000 }, + { 0xb542, 0x0000 }, + { 0xb543, 0x0000 }, + { 0xb544, 0x0000 }, + { 0xb545, 0x0000 }, + { 0xb546, 0x0000 }, + { 0xb547, 0x0000 }, + { 0xb548, 0x0000 }, + { 0xb549, 0x0000 }, + { 0xb54a, 0x0000 }, + { 0xb54b, 0x0000 }, + { 0xb54c, 0x0000 }, + { 0xb54d, 0x0000 }, + { 0xb54e, 0x0000 }, + { 0xb54f, 0x0000 }, + { 0xb550, 0x0000 }, + { 0xb551, 0x0000 }, + { 0xb552, 0x0000 }, + { 0xb553, 0x0000 }, + { 0xb554, 0x0000 }, + { 0xb555, 0x0000 }, + { 0xb556, 0x0000 }, + { 0xb557, 0x0000 }, + { 0xb558, 0x0000 }, + { 0xb559, 0x0000 }, + { 0xb55a, 0x0000 }, + { 0xb55b, 0x0000 }, + { 0xb55c, 0x0000 }, + { 0xb55d, 0x0000 }, + { 0xb55e, 0x0000 }, + { 0xb55f, 0x0000 }, + { 0xb560, 0x0000 }, + { 0xb561, 0x0000 }, + { 0xb562, 0x0000 }, + { 0xb563, 0x0000 }, + { 0xb564, 0x0000 }, + { 0xb565, 0x0000 }, + { 0xb566, 0x0000 }, + { 0xb567, 0x0000 }, + { 0xb568, 0x0000 }, + { 0xb569, 0x0000 }, + { 0xb56a, 0x0000 }, + { 0xb56b, 0x0000 }, + { 0xb56c, 0x0000 }, + { 0xb56d, 0x0000 }, + { 0xb56e, 0x0000 }, + { 0xb56f, 0x0000 }, + { 0xb570, 0x0000 }, + { 0xb571, 0x0000 }, + { 0xb572, 0x0000 }, + { 0xb573, 0x0000 }, + { 0xb574, 0x0000 }, + { 0xb575, 0x0000 }, + { 0xb576, 0x0000 }, + { 0xb577, 0x0000 }, + { 0xb578, 0x0000 }, + { 0xb579, 0x0000 }, + { 0xb57a, 0x0000 }, + { 0xb57b, 0x0000 }, + { 0xb57c, 0x0000 }, + { 0xb57d, 0x0000 }, + { 0xb57e, 0x0000 }, + { 0xb57f, 0x0000 }, + { 0xb580, 0x0000 }, + { 0xb581, 0x0000 }, + { 0xb582, 0x0000 }, + { 0xb583, 0x0000 }, + { 0xb584, 0x0000 }, + { 0xb585, 0x0000 }, + { 0xb586, 0x0000 }, + { 0xb587, 0x0000 }, + { 0xb588, 0x0000 }, + { 0xb589, 0x0000 }, + { 0xb58a, 0x0000 }, + { 0xb58b, 0x0000 }, + { 0xb58c, 0x0000 }, + { 0xb58d, 0x0000 }, + { 0xb58e, 0x0000 }, + { 0xb58f, 0x0000 }, + { 0xb590, 0x0000 }, + { 0xb591, 0x0000 }, + { 0xb592, 0x0000 }, + { 0xb593, 0x0000 }, + { 0xb594, 0x0000 }, + { 0xb595, 0x0000 }, + { 0xb596, 0x0000 }, + { 0xb597, 0x0000 }, + { 0xb598, 0x0000 }, + { 0xb599, 0x0000 }, + { 0xb59a, 0x0000 }, + { 0xb59b, 0x0000 }, + { 0xb59c, 0x0000 }, + { 0xb59d, 0x0000 }, + { 0xb59e, 0x0000 }, + { 0xb59f, 0x0000 }, + { 0xb5a0, 0x0000 }, + { 0xb5a1, 0x0000 }, + { 0xb5a2, 0x0000 }, + { 0xb5a3, 0x0000 }, + { 0xb5a4, 0x0000 }, + { 0xb5a5, 0x0000 }, + { 0xb5a6, 0x0000 }, + { 0xb5a7, 0x0000 }, + { 0xb5a8, 0x0000 }, + { 0xb5a9, 0x0000 }, + { 0xb5aa, 0x0000 }, + { 0xb5ab, 0x0000 }, + { 0xb5ac, 0x0000 }, + { 0xb5ad, 0x0000 }, + { 0xb5ae, 0x0000 }, + { 0xb5af, 0x0000 }, + { 0xb5b0, 0x0000 }, + { 0xb5b1, 0x0000 }, + { 0xb5b2, 0x0000 }, + { 0xb5b3, 0x0000 }, + { 0xb5b4, 0x0000 }, + { 0xb5b5, 0x0000 }, + { 0xb5b6, 0x0000 }, + { 0xb5b7, 0x0000 }, + { 0xb5b8, 0x0000 }, + { 0xb5b9, 0x0000 }, + { 0xb5ba, 0x0000 }, + { 0xb5bb, 0x0000 }, + { 0xb5bc, 0x0000 }, + { 0xb5bd, 0x0000 }, + { 0xb5be, 0x0000 }, + { 0xb5bf, 0x0000 }, + { 0xb5c0, 0x0000 }, + { 0xb5c1, 0x0000 }, + { 0xb5c2, 0x0000 }, + { 0xb5c3, 0x0000 }, + { 0xb5c4, 0x0000 }, + { 0xb5c5, 0x0000 }, + { 0xb5c6, 0x0000 }, + { 0xb5c7, 0x0000 }, + { 0xb5c8, 0x0000 }, + { 0xb5c9, 0x0000 }, + { 0xb5ca, 0x0000 }, + { 0xb5cb, 0x0000 }, + { 0xb5cc, 0x0000 }, + { 0xb5cd, 0x0000 }, + { 0xb5ce, 0x0000 }, + { 0xb5cf, 0x0000 }, + { 0xb5d0, 0x0000 }, + { 0xb5d1, 0x0000 }, + { 0xb5d2, 0x0000 }, + { 0xb5d3, 0x0000 }, + { 0xb5d4, 0x0000 }, + { 0xb5d5, 0x0000 }, + { 0xb5d6, 0x0000 }, + { 0xb5d7, 0x0000 }, + { 0xb5d8, 0x0000 }, + { 0xb5d9, 0x0000 }, + { 0xb5da, 0x0000 }, + { 0xb5db, 0x0000 }, + { 0xb5dc, 0x0000 }, + { 0xb5dd, 0x0000 }, + { 0xb5de, 0x0000 }, + { 0xb5df, 0x0000 }, + { 0xb5e0, 0x0000 }, + { 0xb5e1, 0x0000 }, + { 0xb5e2, 0x0000 }, + { 0xb5e3, 0x0000 }, + { 0xb5e4, 0x0000 }, + { 0xb5e5, 0x0000 }, + { 0xb5e6, 0x0000 }, + { 0xb5e7, 0x0000 }, + { 0xb5e8, 0x0000 }, + { 0xb5e9, 0x0000 }, + { 0xb5ea, 0x0000 }, + { 0xb5eb, 0x0000 }, + { 0xb5ec, 0x0000 }, + { 0xb5ed, 0x0000 }, + { 0xb5ee, 0x0000 }, + { 0xb5ef, 0x0000 }, + { 0xb5f0, 0x0000 }, + { 0xb5f1, 0x0000 }, + { 0xb5f2, 0x0000 }, + { 0xb5f3, 0x0000 }, + { 0xb5f4, 0x0000 }, + { 0xb5f5, 0x0000 }, + { 0xb5f6, 0x0000 }, + { 0xb5f7, 0x0000 }, + { 0xb5f8, 0x0000 }, + { 0xb5f9, 0x0000 }, + { 0xb5fa, 0x0000 }, + { 0xb5fb, 0x0000 }, + { 0xb5fc, 0x0000 }, + { 0xb5fd, 0x0000 }, + { 0xb5fe, 0x0000 }, + { 0xb5ff, 0x0000 }, + { 0xb600, 0x0000 }, + { 0xb601, 0x0000 }, + { 0xb602, 0x0000 }, + { 0xb603, 0x0000 }, + { 0xb604, 0x0000 }, + { 0xb605, 0x0000 }, + { 0xb606, 0x0000 }, + { 0xb607, 0x0000 }, + { 0xb608, 0x0000 }, + { 0xb609, 0x0000 }, + { 0xb60a, 0x0000 }, + { 0xb60b, 0x0000 }, + { 0xb60c, 0x0000 }, + { 0xb60d, 0x0000 }, + { 0xb60e, 0x0000 }, + { 0xb60f, 0x0000 }, + { 0xb610, 0x0000 }, + { 0xb611, 0x0000 }, + { 0xb612, 0x0000 }, + { 0xb613, 0x0000 }, + { 0xb614, 0x0000 }, + { 0xb615, 0x0000 }, + { 0xb616, 0x0000 }, + { 0xb617, 0x0000 }, + { 0xb618, 0x0000 }, + { 0xb619, 0x0000 }, + { 0xb61a, 0x0000 }, + { 0xb61b, 0x0000 }, + { 0xb61c, 0x0000 }, + { 0xb61d, 0x0000 }, + { 0xb61e, 0x0000 }, + { 0xb61f, 0x0000 }, + { 0xb620, 0x0000 }, + { 0xb621, 0x0000 }, + { 0xb622, 0x0000 }, + { 0xb623, 0x0000 }, + { 0xb624, 0x0000 }, + { 0xb625, 0x0000 }, + { 0xb626, 0x0000 }, + { 0xb627, 0x0000 }, + { 0xb628, 0x0000 }, + { 0xb629, 0x0000 }, + { 0xb62a, 0x0000 }, + { 0xb62b, 0x0000 }, + { 0xb62c, 0x0000 }, + { 0xb62d, 0x0000 }, + { 0xb62e, 0x0000 }, + { 0xb62f, 0x0000 }, + { 0xb630, 0x0000 }, + { 0xb631, 0x0000 }, + { 0xb632, 0x0000 }, + { 0xb633, 0x0000 }, + { 0xb634, 0x0000 }, + { 0xb635, 0x0000 }, + { 0xb636, 0x0000 }, + { 0xb637, 0x0000 }, + { 0xb638, 0x0000 }, + { 0xb639, 0x0000 }, + { 0xb63a, 0x0000 }, + { 0xb63b, 0x0000 }, + { 0xb63c, 0x0000 }, + { 0xb63d, 0x0000 }, + { 0xb63e, 0x0000 }, + { 0xb63f, 0x0000 }, + { 0xb640, 0x0000 }, + { 0xb641, 0x0000 }, + { 0xb642, 0x0000 }, + { 0xb643, 0x0000 }, + { 0xb644, 0x0000 }, + { 0xb645, 0x0000 }, + { 0xb646, 0x0000 }, + { 0xb647, 0x0000 }, + { 0xb648, 0x0000 }, + { 0xb649, 0x0000 }, + { 0xb64a, 0x0000 }, + { 0xb64b, 0x0000 }, + { 0xb64c, 0x0000 }, + { 0xb64d, 0x0000 }, + { 0xb64e, 0x0000 }, + { 0xb64f, 0x0000 }, + { 0xb650, 0x0000 }, + { 0xb651, 0x0000 }, + { 0xb652, 0x0000 }, + { 0xb653, 0x0000 }, + { 0xb654, 0x0000 }, + { 0xb655, 0x0000 }, + { 0xb656, 0x0000 }, + { 0xb657, 0x0000 }, + { 0xb658, 0x0000 }, + { 0xb659, 0x0000 }, + { 0xb65a, 0x0000 }, + { 0xb65b, 0x0000 }, + { 0xb65c, 0x0000 }, + { 0xb65d, 0x0000 }, + { 0xb65e, 0x0000 }, + { 0xb65f, 0x0000 }, + { 0xb660, 0x0000 }, + { 0xb661, 0x0000 }, + { 0xb662, 0x0000 }, + { 0xb663, 0x0000 }, + { 0xb664, 0x0000 }, + { 0xb665, 0x0000 }, + { 0xb666, 0x0000 }, + { 0xb667, 0x0000 }, + { 0xb668, 0x0000 }, + { 0xb669, 0x0000 }, + { 0xb66a, 0x0000 }, + { 0xb66b, 0x0000 }, + { 0xb66c, 0x0000 }, + { 0xb66d, 0x0000 }, + { 0xb66e, 0x0000 }, + { 0xb66f, 0x0000 }, + { 0xb670, 0x0000 }, + { 0xb671, 0x0000 }, + { 0xb672, 0x0000 }, + { 0xb673, 0x0000 }, + { 0xb674, 0x0000 }, + { 0xb675, 0x0000 }, + { 0xb676, 0x0000 }, + { 0xb677, 0x0000 }, + { 0xb678, 0x0000 }, + { 0xb679, 0x0000 }, + { 0xb67a, 0x0000 }, + { 0xb67b, 0x0000 }, + { 0xb67c, 0x0000 }, + { 0xb67d, 0x0000 }, + { 0xb67e, 0x0000 }, + { 0xb67f, 0x0000 }, + { 0xb680, 0x0000 }, + { 0xb681, 0x0000 }, + { 0xb682, 0x0000 }, + { 0xb683, 0x0000 }, + { 0xb684, 0x0000 }, + { 0xb685, 0x0000 }, + { 0xb686, 0x0000 }, + { 0xb687, 0x0000 }, + { 0xb688, 0x0000 }, + { 0xb689, 0x0000 }, + { 0xb68a, 0x0000 }, + { 0xb68b, 0x0000 }, + { 0xb68c, 0x0000 }, + { 0xb68d, 0x0000 }, + { 0xb68e, 0x0000 }, + { 0xb68f, 0x0000 }, + { 0xb690, 0x0000 }, + { 0xb691, 0x0000 }, + { 0xb692, 0x0000 }, + { 0xb693, 0x0000 }, + { 0xb694, 0x0000 }, + { 0xb695, 0x0000 }, + { 0xb696, 0x0000 }, + { 0xb697, 0x0000 }, + { 0xb698, 0x0000 }, + { 0xb699, 0x0000 }, + { 0xb69a, 0x0000 }, + { 0xb69b, 0x0000 }, + { 0xb69c, 0x0000 }, + { 0xb69d, 0x0000 }, + { 0xb69e, 0x0000 }, + { 0xb69f, 0x0000 }, + { 0xb6a0, 0x0000 }, + { 0xb6a1, 0x0000 }, + { 0xb6a2, 0x0000 }, + { 0xb6a3, 0x0000 }, + { 0xb6a4, 0x0000 }, + { 0xb6a5, 0x0000 }, + { 0xb6a6, 0x0000 }, + { 0xb6a7, 0x0000 }, + { 0xb6a8, 0x0000 }, + { 0xb6a9, 0x0000 }, + { 0xb6aa, 0x0000 }, + { 0xb6ab, 0x0000 }, + { 0xb6ac, 0x0000 }, + { 0xb6ad, 0x0000 }, + { 0xb6ae, 0x0000 }, + { 0xb6af, 0x0000 }, + { 0xb6b0, 0x0000 }, + { 0xb6b1, 0x0000 }, + { 0xb6b2, 0x0000 }, + { 0xb6b3, 0x0000 }, + { 0xb6b4, 0x0000 }, + { 0xb6b5, 0x0000 }, + { 0xb6b6, 0x0000 }, + { 0xb6b7, 0x0000 }, + { 0xb6b8, 0x0000 }, + { 0xb6b9, 0x0000 }, + { 0xb6ba, 0x0000 }, + { 0xb6bb, 0x0000 }, + { 0xb6bc, 0x0000 }, + { 0xb6bd, 0x0000 }, + { 0xb6be, 0x0000 }, + { 0xb6bf, 0x0000 }, + { 0xb6c0, 0x0000 }, + { 0xb6c1, 0x0000 }, + { 0xb6c2, 0x0000 }, + { 0xb6c3, 0x0000 }, + { 0xb6c4, 0x0000 }, + { 0xb6c5, 0x0000 }, + { 0xb6c6, 0x0000 }, + { 0xb6c7, 0x0000 }, + { 0xb6c8, 0x0000 }, + { 0xb6c9, 0x0000 }, + { 0xb6ca, 0x0000 }, + { 0xb6cb, 0x0000 }, + { 0xb6cc, 0x0000 }, + { 0xb6cd, 0x0000 }, + { 0xb6ce, 0x0000 }, + { 0xb6cf, 0x0000 }, + { 0xb6d0, 0x0000 }, + { 0xb6d1, 0x0000 }, + { 0xb6d2, 0x0000 }, + { 0xb6d3, 0x0000 }, + { 0xb6d4, 0x0000 }, + { 0xb6d5, 0x0000 }, + { 0xb6d6, 0x0000 }, + { 0xb6d7, 0x0000 }, + { 0xb6d8, 0x0000 }, + { 0xb6d9, 0x0000 }, + { 0xb6da, 0x0000 }, + { 0xb6db, 0x0000 }, + { 0xb6dc, 0x0000 }, + { 0xb6dd, 0x0000 }, + { 0xb6de, 0x0000 }, + { 0xb6df, 0x0000 }, + { 0xb6e0, 0x0000 }, + { 0xb6e1, 0x0000 }, + { 0xb6e2, 0x0000 }, + { 0xb6e3, 0x0000 }, + { 0xb6e4, 0x0000 }, + { 0xb6e5, 0x0000 }, + { 0xb6e6, 0x0000 }, + { 0xb6e7, 0x0000 }, + { 0xb6e8, 0x0000 }, + { 0xb6e9, 0x0000 }, + { 0xb6ea, 0x0000 }, + { 0xb6eb, 0x0000 }, + { 0xb6ec, 0x0000 }, + { 0xb6ed, 0x0000 }, + { 0xb6ee, 0x0000 }, + { 0xb6ef, 0x0000 }, + { 0xb6f0, 0x0000 }, + { 0xb6f1, 0x0000 }, + { 0xb6f2, 0x0000 }, + { 0xb6f3, 0x0000 }, + { 0xb6f4, 0x0000 }, + { 0xb6f5, 0x0000 }, + { 0xb6f6, 0x0000 }, + { 0xb6f7, 0x0000 }, + { 0xb6f8, 0x0000 }, + { 0xb6f9, 0x0000 }, + { 0xb6fa, 0x0000 }, + { 0xb6fb, 0x0000 }, + { 0xb6fc, 0x0000 }, + { 0xb6fd, 0x0000 }, + { 0xb6fe, 0x0000 }, + { 0xb6ff, 0x0000 }, + { 0xb700, 0x0000 }, + { 0xb701, 0x0000 }, + { 0xb702, 0x0000 }, + { 0xb703, 0x0000 }, + { 0xb704, 0x0000 }, + { 0xb705, 0x0000 }, + { 0xb706, 0x0000 }, + { 0xb707, 0x0000 }, + { 0xb708, 0x0000 }, + { 0xb709, 0x0000 }, + { 0xb70a, 0x0000 }, + { 0xb70b, 0x0000 }, + { 0xb70c, 0x0000 }, + { 0xb70d, 0x0000 }, + { 0xb70e, 0x0000 }, + { 0xb70f, 0x0000 }, + { 0xb710, 0x0000 }, + { 0xb711, 0x0000 }, + { 0xb712, 0x0000 }, + { 0xb713, 0x0000 }, + { 0xb714, 0x0000 }, + { 0xb715, 0x0000 }, + { 0xb716, 0x0000 }, + { 0xb717, 0x0000 }, + { 0xb718, 0x0000 }, + { 0xb719, 0x0000 }, + { 0xb71a, 0x0000 }, + { 0xb71b, 0x0000 }, + { 0xb71c, 0x0000 }, + { 0xb71d, 0x0000 }, + { 0xb71e, 0x0000 }, + { 0xb71f, 0x0000 }, + { 0xb720, 0x0000 }, + { 0xb721, 0x0000 }, + { 0xb722, 0x0000 }, + { 0xb723, 0x0000 }, + { 0xb724, 0x0000 }, + { 0xb725, 0x0000 }, + { 0xb726, 0x0000 }, + { 0xb727, 0x0000 }, + { 0xb728, 0x0000 }, + { 0xb729, 0x0000 }, + { 0xb72a, 0x0000 }, + { 0xb72b, 0x0000 }, + { 0xb72c, 0x0000 }, + { 0xb72d, 0x0000 }, + { 0xb72e, 0x0000 }, + { 0xb72f, 0x0000 }, + { 0xb730, 0x0000 }, + { 0xb731, 0x0000 }, + { 0xb732, 0x0000 }, + { 0xb733, 0x0000 }, + { 0xb734, 0x0000 }, + { 0xb735, 0x0000 }, + { 0xb736, 0x0000 }, + { 0xb737, 0x0000 }, + { 0xb738, 0x0000 }, + { 0xb739, 0x0000 }, + { 0xb73a, 0x0000 }, + { 0xb73b, 0x0000 }, + { 0xb73c, 0x0000 }, + { 0xb73d, 0x0000 }, + { 0xb73e, 0x0000 }, + { 0xb73f, 0x0000 }, + { 0xb740, 0x0000 }, + { 0xb741, 0x0000 }, + { 0xb742, 0x0000 }, + { 0xb743, 0x0000 }, + { 0xb744, 0x0000 }, + { 0xb745, 0x0000 }, + { 0xb746, 0x0000 }, + { 0xb747, 0x0000 }, + { 0xb748, 0x0000 }, + { 0xb749, 0x0000 }, + { 0xb74a, 0x0000 }, + { 0xb74b, 0x0000 }, + { 0xb74c, 0x0000 }, + { 0xb74d, 0x0000 }, + { 0xb74e, 0x0000 }, + { 0xb74f, 0x0000 }, + { 0xb750, 0x0000 }, + { 0xb751, 0x0000 }, + { 0xb752, 0x0000 }, + { 0xb753, 0x0000 }, + { 0xb754, 0x0000 }, + { 0xb755, 0x0000 }, + { 0xb756, 0x0000 }, + { 0xb757, 0x0000 }, + { 0xb758, 0x0000 }, + { 0xb759, 0x0000 }, + { 0xb75a, 0x0000 }, + { 0xb75b, 0x0000 }, + { 0xb75c, 0x0000 }, + { 0xb75d, 0x0000 }, + { 0xb75e, 0x0000 }, + { 0xb75f, 0x0000 }, + { 0xb760, 0x0000 }, + { 0xb761, 0x0000 }, + { 0xb762, 0x0000 }, + { 0xb763, 0x0000 }, + { 0xb764, 0x0000 }, + { 0xb765, 0x0000 }, + { 0xb766, 0x0000 }, + { 0xb767, 0x0000 }, + { 0xb768, 0x0000 }, + { 0xb769, 0x0000 }, + { 0xb76a, 0x0000 }, + { 0xb76b, 0x0000 }, + { 0xb76c, 0x0000 }, + { 0xb76d, 0x0000 }, + { 0xb76e, 0x0000 }, + { 0xb76f, 0x0000 }, + { 0xb770, 0x0000 }, + { 0xb771, 0x0000 }, + { 0xb772, 0x0000 }, + { 0xb773, 0x0000 }, + { 0xb774, 0x0000 }, + { 0xb775, 0x0000 }, + { 0xb776, 0x0000 }, + { 0xb777, 0x0000 }, + { 0xb778, 0x0000 }, + { 0xb779, 0x0000 }, + { 0xb77a, 0x0000 }, + { 0xb77b, 0x0000 }, + { 0xb77c, 0x0000 }, + { 0xb77d, 0x0000 }, + { 0xb77e, 0x0000 }, + { 0xb77f, 0x0000 }, + { 0xb780, 0x0000 }, + { 0xb781, 0x0000 }, + { 0xb782, 0x0000 }, + { 0xb783, 0x0000 }, + { 0xb784, 0x0000 }, + { 0xb785, 0x0000 }, + { 0xb786, 0x0000 }, + { 0xb787, 0x0000 }, + { 0xb788, 0x0000 }, + { 0xb789, 0x0000 }, + { 0xb78a, 0x0000 }, + { 0xb78b, 0x0000 }, + { 0xb78c, 0x0000 }, + { 0xb78d, 0x0000 }, + { 0xb78e, 0x0000 }, + { 0xb78f, 0x0000 }, + { 0xb790, 0x0000 }, + { 0xb791, 0x0000 }, + { 0xb792, 0x0000 }, + { 0xb793, 0x0000 }, + { 0xb794, 0x0000 }, + { 0xb795, 0x0000 }, + { 0xb796, 0x0000 }, + { 0xb797, 0x0000 }, + { 0xb798, 0x0000 }, + { 0xb799, 0x0000 }, + { 0xb79a, 0x0000 }, + { 0xb79b, 0x0000 }, + { 0xb79c, 0x0000 }, + { 0xb79d, 0x0000 }, + { 0xb79e, 0x0000 }, + { 0xb79f, 0x0000 }, + { 0xb7a0, 0x0000 }, + { 0xb7a1, 0x0000 }, + { 0xb7a2, 0x0000 }, + { 0xb7a3, 0x0000 }, + { 0xb7a4, 0x0000 }, + { 0xb7a5, 0x0000 }, + { 0xb7a6, 0x0000 }, + { 0xb7a7, 0x0000 }, + { 0xb7a8, 0x0000 }, + { 0xb7a9, 0x0000 }, + { 0xb7aa, 0x0000 }, + { 0xb7ab, 0x0000 }, + { 0xb7ac, 0x0000 }, + { 0xb7ad, 0x0000 }, + { 0xb7ae, 0x0000 }, + { 0xb7af, 0x0000 }, + { 0xb7b0, 0x0000 }, + { 0xb7b1, 0x0000 }, + { 0xb7b2, 0x0000 }, + { 0xb7b3, 0x0000 }, + { 0xb7b4, 0x0000 }, + { 0xb7b5, 0x0000 }, + { 0xb7b6, 0x0000 }, + { 0xb7b7, 0x0000 }, + { 0xb7b8, 0x0000 }, + { 0xb7b9, 0x0000 }, + { 0xb7ba, 0x0000 }, + { 0xb7bb, 0x0000 }, + { 0xb7bc, 0x0000 }, + { 0xb7bd, 0x0000 }, + { 0xb7be, 0x0000 }, + { 0xb7bf, 0x0000 }, + { 0xb7c0, 0x0000 }, + { 0xb7c1, 0x0000 }, + { 0xb7c2, 0x0000 }, + { 0xb7c3, 0x0000 }, + { 0xb7c4, 0x0000 }, + { 0xb7c5, 0x0000 }, + { 0xb7c6, 0x0000 }, + { 0xb7c7, 0x0000 }, + { 0xb7c8, 0x0000 }, + { 0xb7c9, 0x0000 }, + { 0xb7ca, 0x0000 }, + { 0xb7cb, 0x0000 }, + { 0xb7cc, 0x0000 }, + { 0xb7cd, 0x0000 }, + { 0xb7ce, 0x0000 }, + { 0xb7cf, 0x0000 }, + { 0xb7d0, 0x0000 }, + { 0xb7d1, 0x0000 }, + { 0xb7d2, 0x0000 }, + { 0xb7d3, 0x0000 }, + { 0xb7d4, 0x0000 }, + { 0xb7d5, 0x0000 }, + { 0xb7d6, 0x0000 }, + { 0xb7d7, 0x0000 }, + { 0xb7d8, 0x0000 }, + { 0xb7d9, 0x0000 }, + { 0xb7da, 0x0000 }, + { 0xb7db, 0x0000 }, + { 0xb7dc, 0x0000 }, + { 0xb7dd, 0x0000 }, + { 0xb7de, 0x0000 }, + { 0xb7df, 0x0000 }, + { 0xb7e0, 0x0000 }, + { 0xb7e1, 0x0000 }, + { 0xb7e2, 0x0000 }, + { 0xb7e3, 0x0000 }, + { 0xb7e4, 0x0000 }, + { 0xb7e5, 0x0000 }, + { 0xb7e6, 0x0000 }, + { 0xb7e7, 0x0000 }, + { 0xb7e8, 0x0000 }, + { 0xb7e9, 0x0000 }, + { 0xb7ea, 0x0000 }, + { 0xb7eb, 0x0000 }, + { 0xb7ec, 0x0000 }, + { 0xb7ed, 0x0000 }, + { 0xb7ee, 0x0000 }, + { 0xb7ef, 0x0000 }, + { 0xb7f0, 0x0000 }, + { 0xb7f1, 0x0000 }, + { 0xb7f2, 0x0000 }, + { 0xb7f3, 0x0000 }, + { 0xb7f4, 0x0000 }, + { 0xb7f5, 0x0000 }, + { 0xb7f6, 0x0000 }, + { 0xb7f7, 0x0000 }, + { 0xb7f8, 0x0000 }, + { 0xb7f9, 0x0000 }, + { 0xb7fa, 0x0000 }, + { 0xb7fb, 0x0000 }, + { 0xb7fc, 0x0000 }, + { 0xb7fd, 0x0000 }, + { 0xb7fe, 0x0000 }, + { 0xb7ff, 0x0000 }, + { 0xb800, 0x0000 }, + { 0xb801, 0x0000 }, + { 0xb802, 0x0000 }, + { 0xb803, 0x0000 }, + { 0xb804, 0x0000 }, + { 0xb805, 0x0000 }, + { 0xb806, 0x0000 }, + { 0xb807, 0x0000 }, + { 0xb808, 0x0000 }, + { 0xb809, 0x0000 }, + { 0xb80a, 0x0000 }, + { 0xb80b, 0x0000 }, + { 0xb80c, 0x0000 }, + { 0xb80d, 0x0000 }, + { 0xb80e, 0x0000 }, + { 0xb80f, 0x0000 }, + { 0xb810, 0x0000 }, + { 0xb811, 0x0000 }, + { 0xb812, 0x0000 }, + { 0xb813, 0x0000 }, + { 0xb814, 0x0000 }, + { 0xb815, 0x0000 }, + { 0xb816, 0x0000 }, + { 0xb817, 0x0000 }, + { 0xb818, 0x0000 }, + { 0xb819, 0x0000 }, + { 0xb81a, 0x0000 }, + { 0xb81b, 0x0000 }, + { 0xb81c, 0x0000 }, + { 0xb81d, 0x0000 }, + { 0xb81e, 0x0000 }, + { 0xb81f, 0x0000 }, + { 0xb820, 0x0000 }, + { 0xb821, 0x0000 }, + { 0xb822, 0x0000 }, + { 0xb823, 0x0000 }, + { 0xb824, 0x0000 }, + { 0xb825, 0x0000 }, + { 0xb826, 0x0000 }, + { 0xb827, 0x0000 }, + { 0xb828, 0x0000 }, + { 0xb829, 0x0000 }, + { 0xb82a, 0x0000 }, + { 0xb82b, 0x0000 }, + { 0xb82c, 0x0000 }, + { 0xb82d, 0x0000 }, + { 0xb82e, 0x0000 }, + { 0xb82f, 0x0000 }, + { 0xb830, 0x0000 }, + { 0xb831, 0x0000 }, + { 0xb832, 0x0000 }, + { 0xb833, 0x0000 }, + { 0xb834, 0x0000 }, + { 0xb835, 0x0000 }, + { 0xb836, 0x0000 }, + { 0xb837, 0x0000 }, + { 0xb838, 0x0000 }, + { 0xb839, 0x0000 }, + { 0xb83a, 0x0000 }, + { 0xb83b, 0x0000 }, + { 0xb83c, 0x0000 }, + { 0xb83d, 0x0000 }, + { 0xb83e, 0x0000 }, + { 0xb83f, 0x0000 }, + { 0xb840, 0x0000 }, + { 0xb841, 0x0000 }, + { 0xb842, 0x0000 }, + { 0xb843, 0x0000 }, + { 0xb844, 0x0000 }, + { 0xb845, 0x0000 }, + { 0xb846, 0x0000 }, + { 0xb847, 0x0000 }, + { 0xb848, 0x0000 }, + { 0xb849, 0x0000 }, + { 0xb84a, 0x0000 }, + { 0xb84b, 0x0000 }, + { 0xb84c, 0x0000 }, + { 0xb84d, 0x0000 }, + { 0xb84e, 0x0000 }, + { 0xb84f, 0x0000 }, + { 0xb850, 0x0000 }, + { 0xb851, 0x0000 }, + { 0xb852, 0x0000 }, + { 0xb853, 0x0000 }, + { 0xb854, 0x0000 }, + { 0xb855, 0x0000 }, + { 0xb856, 0x0000 }, + { 0xb857, 0x0000 }, + { 0xb858, 0x0000 }, + { 0xb859, 0x0000 }, + { 0xb85a, 0x0000 }, + { 0xb85b, 0x0000 }, + { 0xb85c, 0x0000 }, + { 0xb85d, 0x0000 }, + { 0xb85e, 0x0000 }, + { 0xb85f, 0x0000 }, + { 0xb860, 0x0000 }, + { 0xb861, 0x0000 }, + { 0xb862, 0x0000 }, + { 0xb863, 0x0000 }, + { 0xb864, 0x0000 }, + { 0xb865, 0x0000 }, + { 0xb866, 0x0000 }, + { 0xb867, 0x0000 }, + { 0xb868, 0x0000 }, + { 0xb869, 0x0000 }, + { 0xb86a, 0x0000 }, + { 0xb86b, 0x0000 }, + { 0xb86c, 0x0000 }, + { 0xb86d, 0x0000 }, + { 0xb86e, 0x0000 }, + { 0xb86f, 0x0000 }, + { 0xb870, 0x0000 }, + { 0xb871, 0x0000 }, + { 0xb872, 0x0000 }, + { 0xb873, 0x0000 }, + { 0xb874, 0x0000 }, + { 0xb875, 0x0000 }, + { 0xb876, 0x0000 }, + { 0xb877, 0x0000 }, + { 0xb878, 0x0000 }, + { 0xb879, 0x0000 }, + { 0xb87a, 0x0000 }, + { 0xb87b, 0x0000 }, + { 0xb87c, 0x0000 }, + { 0xb87d, 0x0000 }, + { 0xb87e, 0x0000 }, + { 0xb87f, 0x0000 }, + { 0xb880, 0x0000 }, + { 0xb881, 0x0000 }, + { 0xb882, 0x0000 }, + { 0xb883, 0x0000 }, + { 0xb884, 0x0000 }, + { 0xb885, 0x0000 }, + { 0xb886, 0x0000 }, + { 0xb887, 0x0000 }, + { 0xb888, 0x0000 }, + { 0xb889, 0x0000 }, + { 0xb88a, 0x0000 }, + { 0xb88b, 0x0000 }, + { 0xb88c, 0x0000 }, + { 0xb88d, 0x0000 }, + { 0xb88e, 0x0000 }, + { 0xb88f, 0x0000 }, + { 0xb890, 0x0000 }, + { 0xb891, 0x0000 }, + { 0xb892, 0x0000 }, + { 0xb893, 0x0000 }, + { 0xb894, 0x0000 }, + { 0xb895, 0x0000 }, + { 0xb896, 0x0000 }, + { 0xb897, 0x0000 }, + { 0xb898, 0x0000 }, + { 0xb899, 0x0000 }, + { 0xb89a, 0x0000 }, + { 0xb89b, 0x0000 }, + { 0xb89c, 0x0000 }, + { 0xb89d, 0x0000 }, + { 0xb89e, 0x0000 }, + { 0xb89f, 0x0000 }, + { 0xb8a0, 0x0000 }, + { 0xb8a1, 0x0000 }, + { 0xb8a2, 0x0000 }, + { 0xb8a3, 0x0000 }, + { 0xb8a4, 0x0000 }, + { 0xb8a5, 0x0000 }, + { 0xb8a6, 0x0000 }, + { 0xb8a7, 0x0000 }, + { 0xb8a8, 0x0000 }, + { 0xb8a9, 0x0000 }, + { 0xb8aa, 0x0000 }, + { 0xb8ab, 0x0000 }, + { 0xb8ac, 0x0000 }, + { 0xb8ad, 0x0000 }, + { 0xb8ae, 0x0000 }, + { 0xb8af, 0x0000 }, + { 0xb8b0, 0x0000 }, + { 0xb8b1, 0x0000 }, + { 0xb8b2, 0x0000 }, + { 0xb8b3, 0x0000 }, + { 0xb8b4, 0x0000 }, + { 0xb8b5, 0x0000 }, + { 0xb8b6, 0x0000 }, + { 0xb8b7, 0x0000 }, + { 0xb8b8, 0x0000 }, + { 0xb8b9, 0x0000 }, + { 0xb8ba, 0x0000 }, + { 0xb8bb, 0x0000 }, + { 0xb8bc, 0x0000 }, + { 0xb8bd, 0x0000 }, + { 0xb8be, 0x0000 }, + { 0xb8bf, 0x0000 }, + { 0xb8c0, 0x0000 }, + { 0xb8c1, 0x0000 }, + { 0xb8c2, 0x0000 }, + { 0xb8c3, 0x0000 }, + { 0xb8c4, 0x0000 }, + { 0xb8c5, 0x0000 }, + { 0xb8c6, 0x0000 }, + { 0xb8c7, 0x0000 }, + { 0xb8c8, 0x0000 }, + { 0xb8c9, 0x0000 }, + { 0xb8ca, 0x0000 }, + { 0xb8cb, 0x0000 }, + { 0xb8cc, 0x0000 }, + { 0xb8cd, 0x0000 }, + { 0xb8ce, 0x0000 }, + { 0xb8cf, 0x0000 }, + { 0xb8d0, 0x0000 }, + { 0xb8d1, 0x0000 }, + { 0xb8d2, 0x0000 }, + { 0xb8d3, 0x0000 }, + { 0xb8d4, 0x0000 }, + { 0xb8d5, 0x0000 }, + { 0xb8d6, 0x0000 }, + { 0xb8d7, 0x0000 }, + { 0xb8d8, 0x0000 }, + { 0xb8d9, 0x0000 }, + { 0xb8da, 0x0000 }, + { 0xb8db, 0x0000 }, + { 0xb8dc, 0x0000 }, + { 0xb8dd, 0x0000 }, + { 0xb8de, 0x0000 }, + { 0xb8df, 0x0000 }, + { 0xb8e0, 0x0000 }, + { 0xb8e1, 0x0000 }, + { 0xb8e2, 0x0000 }, + { 0xb8e3, 0x0000 }, + { 0xb8e4, 0x0000 }, + { 0xb8e5, 0x0000 }, + { 0xb8e6, 0x0000 }, + { 0xb8e7, 0x0000 }, + { 0xb8e8, 0x0000 }, + { 0xb8e9, 0x0000 }, + { 0xb8ea, 0x0000 }, + { 0xb8eb, 0x0000 }, + { 0xb8ec, 0x0000 }, + { 0xb8ed, 0x0000 }, + { 0xb8ee, 0x0000 }, + { 0xb8ef, 0x0000 }, + { 0xb8f0, 0x0000 }, + { 0xb8f1, 0x0000 }, + { 0xb8f2, 0x0000 }, + { 0xb8f3, 0x0000 }, + { 0xb8f4, 0x0000 }, + { 0xb8f5, 0x0000 }, + { 0xb8f6, 0x0000 }, + { 0xb8f7, 0x0000 }, + { 0xb8f8, 0x0000 }, + { 0xb8f9, 0x0000 }, + { 0xb8fa, 0x0000 }, + { 0xb8fb, 0x0000 }, + { 0xb8fc, 0x0000 }, + { 0xb8fd, 0x0000 }, + { 0xb8fe, 0x0000 }, + { 0xb8ff, 0x0000 }, + { 0xb900, 0x0000 }, + { 0xb901, 0x0000 }, + { 0xb902, 0x0000 }, + { 0xb903, 0x0000 }, + { 0xb904, 0x0000 }, + { 0xb905, 0x0000 }, + { 0xb906, 0x0000 }, + { 0xb907, 0x0000 }, + { 0xb908, 0x0000 }, + { 0xb909, 0x0000 }, + { 0xb90a, 0x0000 }, + { 0xb90b, 0x0000 }, + { 0xb90c, 0x0000 }, + { 0xb90d, 0x0000 }, + { 0xb90e, 0x0000 }, + { 0xb90f, 0x0000 }, + { 0xb910, 0x0000 }, + { 0xb911, 0x0000 }, + { 0xb912, 0x0000 }, + { 0xb913, 0x0000 }, + { 0xb914, 0x0000 }, + { 0xb915, 0x0000 }, + { 0xb916, 0x0000 }, + { 0xb917, 0x0000 }, + { 0xb918, 0x0000 }, + { 0xb919, 0x0000 }, + { 0xb91a, 0x0000 }, + { 0xb91b, 0x0000 }, + { 0xb91c, 0x0000 }, + { 0xb91d, 0x0000 }, + { 0xb91e, 0x0000 }, + { 0xb91f, 0x0000 }, + { 0xb920, 0x0000 }, + { 0xb921, 0x0000 }, + { 0xb922, 0x0000 }, + { 0xb923, 0x0000 }, + { 0xb924, 0x0000 }, + { 0xb925, 0x0000 }, + { 0xb926, 0x0000 }, + { 0xb927, 0x0000 }, + { 0xb928, 0x0000 }, + { 0xb929, 0x0000 }, + { 0xb92a, 0x0000 }, + { 0xb92b, 0x0000 }, + { 0xb92c, 0x0000 }, + { 0xb92d, 0x0000 }, + { 0xb92e, 0x0000 }, + { 0xb92f, 0x0000 }, + { 0xb930, 0x0000 }, + { 0xb931, 0x0000 }, + { 0xb932, 0x0000 }, + { 0xb933, 0x0000 }, + { 0xb934, 0x0000 }, + { 0xb935, 0x0000 }, + { 0xb936, 0x0000 }, + { 0xb937, 0x0000 }, + { 0xb938, 0x0000 }, + { 0xb939, 0x0000 }, + { 0xb93a, 0x0000 }, + { 0xb93b, 0x0000 }, + { 0xb93c, 0x0000 }, + { 0xb93d, 0x0000 }, + { 0xb93e, 0x0000 }, + { 0xb93f, 0x0000 }, + { 0xb940, 0x0000 }, + { 0xb941, 0x0000 }, + { 0xb942, 0x0000 }, + { 0xb943, 0x0000 }, + { 0xb944, 0x0000 }, + { 0xb945, 0x0000 }, + { 0xb946, 0x0000 }, + { 0xb947, 0x0000 }, + { 0xb948, 0x0000 }, + { 0xb949, 0x0000 }, + { 0xb94a, 0x0000 }, + { 0xb94b, 0x0000 }, + { 0xb94c, 0x0000 }, + { 0xb94d, 0x0000 }, + { 0xb94e, 0x0000 }, + { 0xb94f, 0x0000 }, + { 0xb950, 0x0000 }, + { 0xb951, 0x0000 }, + { 0xb952, 0x0000 }, + { 0xb953, 0x0000 }, + { 0xb954, 0x0000 }, + { 0xb955, 0x0000 }, + { 0xb956, 0x0000 }, + { 0xb957, 0x0000 }, + { 0xb958, 0x0000 }, + { 0xb959, 0x0000 }, + { 0xb95a, 0x0000 }, + { 0xb95b, 0x0000 }, + { 0xb95c, 0x0000 }, + { 0xb95d, 0x0000 }, + { 0xb95e, 0x0000 }, + { 0xb95f, 0x0000 }, + { 0xb960, 0x0000 }, + { 0xb961, 0x0000 }, + { 0xb962, 0x0000 }, + { 0xb963, 0x0000 }, + { 0xb964, 0x0000 }, + { 0xb965, 0x0000 }, + { 0xb966, 0x0000 }, + { 0xb967, 0x0000 }, + { 0xb968, 0x0000 }, + { 0xb969, 0x0000 }, + { 0xb96a, 0x0000 }, + { 0xb96b, 0x0000 }, + { 0xb96c, 0x0000 }, + { 0xb96d, 0x0000 }, + { 0xb96e, 0x0000 }, + { 0xb96f, 0x0000 }, + { 0xb970, 0x0000 }, + { 0xb971, 0x0000 }, + { 0xb972, 0x0000 }, + { 0xb973, 0x0000 }, + { 0xb974, 0x0000 }, + { 0xb975, 0x0000 }, + { 0xb976, 0x0000 }, + { 0xb977, 0x0000 }, + { 0xb978, 0x0000 }, + { 0xb979, 0x0000 }, + { 0xb97a, 0x0000 }, + { 0xb97b, 0x0000 }, + { 0xb97c, 0x0000 }, + { 0xb97d, 0x0000 }, + { 0xb97e, 0x0000 }, + { 0xb97f, 0x0000 }, + { 0xb980, 0x0000 }, + { 0xb981, 0x0000 }, + { 0xb982, 0x0000 }, + { 0xb983, 0x0000 }, + { 0xb984, 0x0000 }, + { 0xb985, 0x0000 }, + { 0xb986, 0x0000 }, + { 0xb987, 0x0000 }, + { 0xb988, 0x0000 }, + { 0xb989, 0x0000 }, + { 0xb98a, 0x0000 }, + { 0xb98b, 0x0000 }, + { 0xb98c, 0x0000 }, + { 0xb98d, 0x0000 }, + { 0xb98e, 0x0000 }, + { 0xb98f, 0x0000 }, + { 0xb990, 0x0000 }, + { 0xb991, 0x0000 }, + { 0xb992, 0x0000 }, + { 0xb993, 0x0000 }, + { 0xb994, 0x0000 }, + { 0xb995, 0x0000 }, + { 0xb996, 0x0000 }, + { 0xb997, 0x0000 }, + { 0xb998, 0x0000 }, + { 0xb999, 0x0000 }, + { 0xb99a, 0x0000 }, + { 0xb99b, 0x0000 }, + { 0xb99c, 0x0000 }, + { 0xb99d, 0x0000 }, + { 0xb99e, 0x0000 }, + { 0xb99f, 0x0000 }, + { 0xb9a0, 0x0000 }, + { 0xb9a1, 0x0000 }, + { 0xb9a2, 0x0000 }, + { 0xb9a3, 0x0000 }, + { 0xb9a4, 0x0000 }, + { 0xb9a5, 0x0000 }, + { 0xb9a6, 0x0000 }, + { 0xb9a7, 0x0000 }, + { 0xb9a8, 0x0000 }, + { 0xb9a9, 0x0000 }, + { 0xb9aa, 0x0000 }, + { 0xb9ab, 0x0000 }, + { 0xb9ac, 0x0000 }, + { 0xb9ad, 0x0000 }, + { 0xb9ae, 0x0000 }, + { 0xb9af, 0x0000 }, + { 0xb9b0, 0x0000 }, + { 0xb9b1, 0x0000 }, + { 0xb9b2, 0x0000 }, + { 0xb9b3, 0x0000 }, + { 0xb9b4, 0x0000 }, + { 0xb9b5, 0x0000 }, + { 0xb9b6, 0x0000 }, + { 0xb9b7, 0x0000 }, + { 0xb9b8, 0x0000 }, + { 0xb9b9, 0x0000 }, + { 0xb9ba, 0x0000 }, + { 0xb9bb, 0x0000 }, + { 0xb9bc, 0x0000 }, + { 0xb9bd, 0x0000 }, + { 0xb9be, 0x0000 }, + { 0xb9bf, 0x0000 }, + { 0xb9c0, 0x0000 }, + { 0xb9c1, 0x0000 }, + { 0xb9c2, 0x0000 }, + { 0xb9c3, 0x0000 }, + { 0xb9c4, 0x0000 }, + { 0xb9c5, 0x0000 }, + { 0xb9c6, 0x0000 }, + { 0xb9c7, 0x0000 }, + { 0xb9c8, 0x0000 }, + { 0xb9c9, 0x0000 }, + { 0xb9ca, 0x0000 }, + { 0xb9cb, 0x0000 }, + { 0xb9cc, 0x0000 }, + { 0xb9cd, 0x0000 }, + { 0xb9ce, 0x0000 }, + { 0xb9cf, 0x0000 }, + { 0xb9d0, 0x0000 }, + { 0xb9d1, 0x0000 }, + { 0xb9d2, 0x0000 }, + { 0xb9d3, 0x0000 }, + { 0xb9d4, 0x0000 }, + { 0xb9d5, 0x0000 }, + { 0xb9d6, 0x0000 }, + { 0xb9d7, 0x0000 }, + { 0xb9d8, 0x0000 }, + { 0xb9d9, 0x0000 }, + { 0xb9da, 0x0000 }, + { 0xb9db, 0x0000 }, + { 0xb9dc, 0x0000 }, + { 0xb9dd, 0x0000 }, + { 0xb9de, 0x0000 }, + { 0xb9df, 0x0000 }, + { 0xb9e0, 0x0000 }, + { 0xb9e1, 0x0000 }, + { 0xb9e2, 0x0000 }, + { 0xb9e3, 0x0000 }, + { 0xb9e4, 0x0000 }, + { 0xb9e5, 0x0000 }, + { 0xb9e6, 0x0000 }, + { 0xb9e7, 0x0000 }, + { 0xb9e8, 0x0000 }, + { 0xb9e9, 0x0000 }, + { 0xb9ea, 0x0000 }, + { 0xb9eb, 0x0000 }, + { 0xb9ec, 0x0000 }, + { 0xb9ed, 0x0000 }, + { 0xb9ee, 0x0000 }, + { 0xb9ef, 0x0000 }, + { 0xb9f0, 0x0000 }, + { 0xb9f1, 0x0000 }, + { 0xb9f2, 0x0000 }, + { 0xb9f3, 0x0000 }, + { 0xb9f4, 0x0000 }, + { 0xb9f5, 0x0000 }, + { 0xb9f6, 0x0000 }, + { 0xb9f7, 0x0000 }, + { 0xb9f8, 0x0000 }, + { 0xb9f9, 0x0000 }, + { 0xb9fa, 0x0000 }, + { 0xb9fb, 0x0000 }, + { 0xb9fc, 0x0000 }, + { 0xb9fd, 0x0000 }, + { 0xb9fe, 0x0000 }, + { 0xb9ff, 0x0000 }, + { 0xba00, 0x0000 }, + { 0xba01, 0x0000 }, + { 0xba02, 0x0000 }, + { 0xba03, 0x0000 }, + { 0xba04, 0x0000 }, + { 0xba05, 0x0000 }, + { 0xba06, 0x0000 }, + { 0xba07, 0x0000 }, + { 0xba08, 0x0000 }, + { 0xba09, 0x0000 }, + { 0xba0a, 0x0000 }, + { 0xba0b, 0x0000 }, + { 0xba0c, 0x0000 }, + { 0xba0d, 0x0000 }, + { 0xba0e, 0x0000 }, + { 0xba0f, 0x0000 }, + { 0xba10, 0x0000 }, + { 0xba11, 0x0000 }, + { 0xba12, 0x0000 }, + { 0xba13, 0x0000 }, + { 0xba14, 0x0000 }, + { 0xba15, 0x0000 }, + { 0xba16, 0x0000 }, + { 0xba17, 0x0000 }, + { 0xba18, 0x0000 }, + { 0xba19, 0x0000 }, + { 0xba1a, 0x0000 }, + { 0xba1b, 0x0000 }, + { 0xba1c, 0x0000 }, + { 0xba1d, 0x0000 }, + { 0xba1e, 0x0000 }, + { 0xba1f, 0x0000 }, + { 0xba20, 0x0000 }, + { 0xba21, 0x0000 }, + { 0xba22, 0x0000 }, + { 0xba23, 0x0000 }, + { 0xba24, 0x0000 }, + { 0xba25, 0x0000 }, + { 0xba26, 0x0000 }, + { 0xba27, 0x0000 }, + { 0xba28, 0x0000 }, + { 0xba29, 0x0000 }, + { 0xba2a, 0x0000 }, + { 0xba2b, 0x0000 }, + { 0xba2c, 0x0000 }, + { 0xba2d, 0x0000 }, + { 0xba2e, 0x0000 }, + { 0xba2f, 0x0000 }, + { 0xba30, 0x0000 }, + { 0xba31, 0x0000 }, + { 0xba32, 0x0000 }, + { 0xba33, 0x0000 }, + { 0xba34, 0x0000 }, + { 0xba35, 0x0000 }, + { 0xba36, 0x0000 }, + { 0xba37, 0x0000 }, + { 0xba38, 0x0000 }, + { 0xba39, 0x0000 }, + { 0xba3a, 0x0000 }, + { 0xba3b, 0x0000 }, + { 0xba3c, 0x0000 }, + { 0xba3d, 0x0000 }, + { 0xba3e, 0x0000 }, + { 0xba3f, 0x0000 }, + { 0xba40, 0x0000 }, + { 0xba41, 0x0000 }, + { 0xba42, 0x0000 }, + { 0xba43, 0x0000 }, + { 0xba44, 0x0000 }, + { 0xba45, 0x0000 }, + { 0xba46, 0x0000 }, + { 0xba47, 0x0000 }, + { 0xba48, 0x0000 }, + { 0xba49, 0x0000 }, + { 0xba4a, 0x0000 }, + { 0xba4b, 0x0000 }, + { 0xba4c, 0x0000 }, + { 0xba4d, 0x0000 }, + { 0xba4e, 0x0000 }, + { 0xba4f, 0x0000 }, + { 0xba50, 0x0000 }, + { 0xba51, 0x0000 }, + { 0xba52, 0x0000 }, + { 0xba53, 0x0000 }, + { 0xba54, 0x0000 }, + { 0xba55, 0x0000 }, + { 0xba56, 0x0000 }, + { 0xba57, 0x0000 }, + { 0xba58, 0x0000 }, + { 0xba59, 0x0000 }, + { 0xba5a, 0x0000 }, + { 0xba5b, 0x0000 }, + { 0xba5c, 0x0000 }, + { 0xba5d, 0x0000 }, + { 0xba5e, 0x0000 }, + { 0xba5f, 0x0000 }, + { 0xba60, 0x0000 }, + { 0xba61, 0x0000 }, + { 0xba62, 0x0000 }, + { 0xba63, 0x0000 }, + { 0xba64, 0x0000 }, + { 0xba65, 0x0000 }, + { 0xba66, 0x0000 }, + { 0xba67, 0x0000 }, + { 0xba68, 0x0000 }, + { 0xba69, 0x0000 }, + { 0xba6a, 0x0000 }, + { 0xba6b, 0x0000 }, + { 0xba6c, 0x0000 }, + { 0xba6d, 0x0000 }, + { 0xba6e, 0x0000 }, + { 0xba6f, 0x0000 }, + { 0xba70, 0x0000 }, + { 0xba71, 0x0000 }, + { 0xba72, 0x0000 }, + { 0xba73, 0x0000 }, + { 0xba74, 0x0000 }, + { 0xba75, 0x0000 }, + { 0xba76, 0x0000 }, + { 0xba77, 0x0000 }, + { 0xba78, 0x0000 }, + { 0xba79, 0x0000 }, + { 0xba7a, 0x0000 }, + { 0xba7b, 0x0000 }, + { 0xba7c, 0x0000 }, + { 0xba7d, 0x0000 }, + { 0xba7e, 0x0000 }, + { 0xba7f, 0x0000 }, + { 0xba80, 0x0000 }, + { 0xba81, 0x0000 }, + { 0xba82, 0x0000 }, + { 0xba83, 0x0000 }, + { 0xba84, 0x0000 }, + { 0xba85, 0x0000 }, + { 0xba86, 0x0000 }, + { 0xba87, 0x0000 }, + { 0xba88, 0x0000 }, + { 0xba89, 0x0000 }, + { 0xba8a, 0x0000 }, + { 0xba8b, 0x0000 }, + { 0xba8c, 0x0000 }, + { 0xba8d, 0x0000 }, + { 0xba8e, 0x0000 }, + { 0xba8f, 0x0000 }, + { 0xba90, 0x0000 }, + { 0xba91, 0x0000 }, + { 0xba92, 0x0000 }, + { 0xba93, 0x0000 }, + { 0xba94, 0x0000 }, + { 0xba95, 0x0000 }, + { 0xba96, 0x0000 }, + { 0xba97, 0x0000 }, + { 0xba98, 0x0000 }, + { 0xba99, 0x0000 }, + { 0xba9a, 0x0000 }, + { 0xba9b, 0x0000 }, + { 0xba9c, 0x0000 }, + { 0xba9d, 0x0000 }, + { 0xba9e, 0x0000 }, + { 0xba9f, 0x0000 }, + { 0xbaa0, 0x0000 }, + { 0xbaa1, 0x0000 }, + { 0xbaa2, 0x0000 }, + { 0xbaa3, 0x0000 }, + { 0xbaa4, 0x0000 }, + { 0xbaa5, 0x0000 }, + { 0xbaa6, 0x0000 }, + { 0xbaa7, 0x0000 }, + { 0xbaa8, 0x0000 }, + { 0xbaa9, 0x0000 }, + { 0xbaaa, 0x0000 }, + { 0xbaab, 0x0000 }, + { 0xbaac, 0x0000 }, + { 0xbaad, 0x0000 }, + { 0xbaae, 0x0000 }, + { 0xbaaf, 0x0000 }, + { 0xbab0, 0x0000 }, + { 0xbab1, 0x0000 }, + { 0xbab2, 0x0000 }, + { 0xbab3, 0x0000 }, + { 0xbab4, 0x0000 }, + { 0xbab5, 0x0000 }, + { 0xbab6, 0x0000 }, + { 0xbab7, 0x0000 }, + { 0xbab8, 0x0000 }, + { 0xbab9, 0x0000 }, + { 0xbaba, 0x0000 }, + { 0xbabb, 0x0000 }, + { 0xbabc, 0x0000 }, + { 0xbabd, 0x0000 }, + { 0xbabe, 0x0000 }, + { 0xbabf, 0x0000 }, + { 0xbac0, 0x0000 }, + { 0xbac1, 0x0000 }, + { 0xbac2, 0x0000 }, + { 0xbac3, 0x0000 }, + { 0xbac4, 0x0000 }, + { 0xbac5, 0x0000 }, + { 0xbac6, 0x0000 }, + { 0xbac7, 0x0000 }, + { 0xbac8, 0x0000 }, + { 0xbac9, 0x0000 }, + { 0xbaca, 0x0000 }, + { 0xbacb, 0x0000 }, + { 0xbacc, 0x0000 }, + { 0xbacd, 0x0000 }, + { 0xbace, 0x0000 }, + { 0xbacf, 0x0000 }, + { 0xbad0, 0x0000 }, + { 0xbad1, 0x0000 }, + { 0xbad2, 0x0000 }, + { 0xbad3, 0x0000 }, + { 0xbad4, 0x0000 }, + { 0xbad5, 0x0000 }, + { 0xbad6, 0x0000 }, + { 0xbad7, 0x0000 }, + { 0xbad8, 0x0000 }, + { 0xbad9, 0x0000 }, + { 0xbada, 0x0000 }, + { 0xbadb, 0x0000 }, + { 0xbadc, 0x0000 }, + { 0xbadd, 0x0000 }, + { 0xbade, 0x0000 }, + { 0xbadf, 0x0000 }, + { 0xbae0, 0x0000 }, + { 0xbae1, 0x0000 }, + { 0xbae2, 0x0000 }, + { 0xbae3, 0x0000 }, + { 0xbae4, 0x0000 }, + { 0xbae5, 0x0000 }, + { 0xbae6, 0x0000 }, + { 0xbae7, 0x0000 }, + { 0xbae8, 0x0000 }, + { 0xbae9, 0x0000 }, + { 0xbaea, 0x0000 }, + { 0xbaeb, 0x0000 }, + { 0xbaec, 0x0000 }, + { 0xbaed, 0x0000 }, + { 0xbaee, 0x0000 }, + { 0xbaef, 0x0000 }, + { 0xbaf0, 0x0000 }, + { 0xbaf1, 0x0000 }, + { 0xbaf2, 0x0000 }, + { 0xbaf3, 0x0000 }, + { 0xbaf4, 0x0000 }, + { 0xbaf5, 0x0000 }, + { 0xbaf6, 0x0000 }, + { 0xbaf7, 0x0000 }, + { 0xbaf8, 0x0000 }, + { 0xbaf9, 0x0000 }, + { 0xbafa, 0x0000 }, + { 0xbafb, 0x0000 }, + { 0xbafc, 0x0000 }, + { 0xbafd, 0x0000 }, + { 0xbafe, 0x0000 }, + { 0xbaff, 0x0000 }, + { 0xbb00, 0x0000 }, + { 0xbb01, 0x0000 }, + { 0xbb02, 0x0000 }, + { 0xbb03, 0x0000 }, + { 0xbb04, 0x0000 }, + { 0xbb05, 0x0000 }, + { 0xbb06, 0x0000 }, + { 0xbb07, 0x0000 }, + { 0xbb08, 0x0000 }, + { 0xbb09, 0x0000 }, + { 0xbb0a, 0x0000 }, + { 0xbb0b, 0x0000 }, + { 0xbb0c, 0x0000 }, + { 0xbb0d, 0x0000 }, + { 0xbb0e, 0x0000 }, + { 0xbb0f, 0x0000 }, + { 0xbb10, 0x0000 }, + { 0xbb11, 0x0000 }, + { 0xbb12, 0x0000 }, + { 0xbb13, 0x0000 }, + { 0xbb14, 0x0000 }, + { 0xbb15, 0x0000 }, + { 0xbb16, 0x0000 }, + { 0xbb17, 0x0000 }, + { 0xbb18, 0x0000 }, + { 0xbb19, 0x0000 }, + { 0xbb1a, 0x0000 }, + { 0xbb1b, 0x0000 }, + { 0xbb1c, 0x0000 }, + { 0xbb1d, 0x0000 }, + { 0xbb1e, 0x0000 }, + { 0xbb1f, 0x0000 }, + { 0xbb20, 0x0000 }, + { 0xbb21, 0x0000 }, + { 0xbb22, 0x0000 }, + { 0xbb23, 0x0000 }, + { 0xbb24, 0x0000 }, + { 0xbb25, 0x0000 }, + { 0xbb26, 0x0000 }, + { 0xbb27, 0x0000 }, + { 0xbb28, 0x0000 }, + { 0xbb29, 0x0000 }, + { 0xbb2a, 0x0000 }, + { 0xbb2b, 0x0000 }, + { 0xbb2c, 0x0000 }, + { 0xbb2d, 0x0000 }, + { 0xbb2e, 0x0000 }, + { 0xbb2f, 0x0000 }, + { 0xbb30, 0x0000 }, + { 0xbb31, 0x0000 }, + { 0xbb32, 0x0000 }, + { 0xbb33, 0x0000 }, + { 0xbb34, 0x0000 }, + { 0xbb35, 0x0000 }, + { 0xbb36, 0x0000 }, + { 0xbb37, 0x0000 }, + { 0xbb38, 0x0000 }, + { 0xbb39, 0x0000 }, + { 0xbb3a, 0x0000 }, + { 0xbb3b, 0x0000 }, + { 0xbb3c, 0x0000 }, + { 0xbb3d, 0x0000 }, + { 0xbb3e, 0x0000 }, + { 0xbb3f, 0x0000 }, + { 0xbb40, 0x0000 }, + { 0xbb41, 0x0000 }, + { 0xbb42, 0x0000 }, + { 0xbb43, 0x0000 }, + { 0xbb44, 0x0000 }, + { 0xbb45, 0x0000 }, + { 0xbb46, 0x0000 }, + { 0xbb47, 0x0000 }, + { 0xbb48, 0x0000 }, + { 0xbb49, 0x0000 }, + { 0xbb4a, 0x0000 }, + { 0xbb4b, 0x0000 }, + { 0xbb4c, 0x0000 }, + { 0xbb4d, 0x0000 }, + { 0xbb4e, 0x0000 }, + { 0xbb4f, 0x0000 }, + { 0xbb50, 0x0000 }, + { 0xbb51, 0x0000 }, + { 0xbb52, 0x0000 }, + { 0xbb53, 0x0000 }, + { 0xbb54, 0x0000 }, + { 0xbb55, 0x0000 }, + { 0xbb56, 0x0000 }, + { 0xbb57, 0x0000 }, + { 0xbb58, 0x0000 }, + { 0xbb59, 0x0000 }, + { 0xbb5a, 0x0000 }, + { 0xbb5b, 0x0000 }, + { 0xbb5c, 0x0000 }, + { 0xbb5d, 0x0000 }, + { 0xbb5e, 0x0000 }, + { 0xbb5f, 0x0000 }, + { 0xbb60, 0x0000 }, + { 0xbb61, 0x0000 }, + { 0xbb62, 0x0000 }, + { 0xbb63, 0x0000 }, + { 0xbb64, 0x0000 }, + { 0xbb65, 0x0000 }, + { 0xbb66, 0x0000 }, + { 0xbb67, 0x0000 }, + { 0xbb68, 0x0000 }, + { 0xbb69, 0x0000 }, + { 0xbb6a, 0x0000 }, + { 0xbb6b, 0x0000 }, + { 0xbb6c, 0x0000 }, + { 0xbb6d, 0x0000 }, + { 0xbb6e, 0x0000 }, + { 0xbb6f, 0x0000 }, + { 0xbb70, 0x0000 }, + { 0xbb71, 0x0000 }, + { 0xbb72, 0x0000 }, + { 0xbb73, 0x0000 }, + { 0xbb74, 0x0000 }, + { 0xbb75, 0x0000 }, + { 0xbb76, 0x0000 }, + { 0xbb77, 0x0000 }, + { 0xbb78, 0x0000 }, + { 0xbb79, 0x0000 }, + { 0xbb7a, 0x0000 }, + { 0xbb7b, 0x0000 }, + { 0xbb7c, 0x0000 }, + { 0xbb7d, 0x0000 }, + { 0xbb7e, 0x0000 }, + { 0xbb7f, 0x0000 }, + { 0xbb80, 0x0000 }, + { 0xbb81, 0x0000 }, + { 0xbb82, 0x0000 }, + { 0xbb83, 0x0000 }, + { 0xbb84, 0x0000 }, + { 0xbb85, 0x0000 }, + { 0xbb86, 0x0000 }, + { 0xbb87, 0x0000 }, + { 0xbb88, 0x0000 }, + { 0xbb89, 0x0000 }, + { 0xbb8a, 0x0000 }, + { 0xbb8b, 0x0000 }, + { 0xbb8c, 0x0000 }, + { 0xbb8d, 0x0000 }, + { 0xbb8e, 0x0000 }, + { 0xbb8f, 0x0000 }, + { 0xbb90, 0x0000 }, + { 0xbb91, 0x0000 }, + { 0xbb92, 0x0000 }, + { 0xbb93, 0x0000 }, + { 0xbb94, 0x0000 }, + { 0xbb95, 0x0000 }, + { 0xbb96, 0x0000 }, + { 0xbb97, 0x0000 }, + { 0xbb98, 0x0000 }, + { 0xbb99, 0x0000 }, + { 0xbb9a, 0x0000 }, + { 0xbb9b, 0x0000 }, + { 0xbb9c, 0x0000 }, + { 0xbb9d, 0x0000 }, + { 0xbb9e, 0x0000 }, + { 0xbb9f, 0x0000 }, + { 0xbba0, 0x0000 }, + { 0xbba1, 0x0000 }, + { 0xbba2, 0x0000 }, + { 0xbba3, 0x0000 }, + { 0xbba4, 0x0000 }, + { 0xbba5, 0x0000 }, + { 0xbba6, 0x0000 }, + { 0xbba7, 0x0000 }, + { 0xbba8, 0x0000 }, + { 0xbba9, 0x0000 }, + { 0xbbaa, 0x0000 }, + { 0xbbab, 0x0000 }, + { 0xbbac, 0x0000 }, + { 0xbbad, 0x0000 }, + { 0xbbae, 0x0000 }, + { 0xbbaf, 0x0000 }, + { 0xbbb0, 0x0000 }, + { 0xbbb1, 0x0000 }, + { 0xbbb2, 0x0000 }, + { 0xbbb3, 0x0000 }, + { 0xbbb4, 0x0000 }, + { 0xbbb5, 0x0000 }, + { 0xbbb6, 0x0000 }, + { 0xbbb7, 0x0000 }, + { 0xbbb8, 0x0000 }, + { 0xbbb9, 0x0000 }, + { 0xbbba, 0x0000 }, + { 0xbbbb, 0x0000 }, + { 0xbbbc, 0x0000 }, + { 0xbbbd, 0x0000 }, + { 0xbbbe, 0x0000 }, + { 0xbbbf, 0x0000 }, + { 0xbbc0, 0x0000 }, + { 0xbbc1, 0x0000 }, + { 0xbbc2, 0x0000 }, + { 0xbbc3, 0x0000 }, + { 0xbbc4, 0x0000 }, + { 0xbbc5, 0x0000 }, + { 0xbbc6, 0x0000 }, + { 0xbbc7, 0x0000 }, + { 0xbbc8, 0x0000 }, + { 0xbbc9, 0x0000 }, + { 0xbbca, 0x0000 }, + { 0xbbcb, 0x0000 }, + { 0xbbcc, 0x0000 }, + { 0xbbcd, 0x0000 }, + { 0xbbce, 0x0000 }, + { 0xbbcf, 0x0000 }, + { 0xbbd0, 0x0000 }, + { 0xbbd1, 0x0000 }, + { 0xbbd2, 0x0000 }, + { 0xbbd3, 0x0000 }, + { 0xbbd4, 0x0000 }, + { 0xbbd5, 0x0000 }, + { 0xbbd6, 0x0000 }, + { 0xbbd7, 0x0000 }, + { 0xbbd8, 0x0000 }, + { 0xbbd9, 0x0000 }, + { 0xbbda, 0x0000 }, + { 0xbbdb, 0x0000 }, + { 0xbbdc, 0x0000 }, + { 0xbbdd, 0x0000 }, + { 0xbbde, 0x0000 }, + { 0xbbdf, 0x0000 }, + { 0xbbe0, 0x0000 }, + { 0xbbe1, 0x0000 }, + { 0xbbe2, 0x0000 }, + { 0xbbe3, 0x0000 }, + { 0xbbe4, 0x0000 }, + { 0xbbe5, 0x0000 }, + { 0xbbe6, 0x0000 }, + { 0xbbe7, 0x0000 }, + { 0xbbe8, 0x0000 }, + { 0xbbe9, 0x0000 }, + { 0xbbea, 0x0000 }, + { 0xbbeb, 0x0000 }, + { 0xbbec, 0x0000 }, + { 0xbbed, 0x0000 }, + { 0xbbee, 0x0000 }, + { 0xbbef, 0x0000 }, + { 0xbbf0, 0x0000 }, + { 0xbbf1, 0x0000 }, + { 0xbbf2, 0x0000 }, + { 0xbbf3, 0x0000 }, + { 0xbbf4, 0x0000 }, + { 0xbbf5, 0x0000 }, + { 0xbbf6, 0x0000 }, + { 0xbbf7, 0x0000 }, + { 0xbbf8, 0x0000 }, + { 0xbbf9, 0x0000 }, + { 0xbbfa, 0x0000 }, + { 0xbbfb, 0x0000 }, + { 0xbbfc, 0x0000 }, + { 0xbbfd, 0x0000 }, + { 0xbbfe, 0x0000 }, + { 0xbbff, 0x0000 }, + { 0xbc00, 0x0000 }, + { 0xbc01, 0x0000 }, + { 0xbc02, 0x0000 }, + { 0xbc03, 0x0000 }, + { 0xbc04, 0x0000 }, + { 0xbc05, 0x0000 }, + { 0xbc06, 0x0000 }, + { 0xbc07, 0x0000 }, + { 0xbc08, 0x0000 }, + { 0xbc09, 0x0000 }, + { 0xbc0a, 0x0000 }, + { 0xbc0b, 0x0000 }, + { 0xbc0c, 0x0000 }, + { 0xbc0d, 0x0000 }, + { 0xbc0e, 0x0000 }, + { 0xbc0f, 0x0000 }, + { 0xbc10, 0x0000 }, + { 0xbc11, 0x0000 }, + { 0xbc12, 0x0000 }, + { 0xbc13, 0x0000 }, + { 0xbc14, 0x0000 }, + { 0xbc15, 0x0000 }, + { 0xbc16, 0x0000 }, + { 0xbc17, 0x0000 }, + { 0xbc18, 0x0000 }, + { 0xbc19, 0x0000 }, + { 0xbc1a, 0x0000 }, + { 0xbc1b, 0x0000 }, + { 0xbc1c, 0x0000 }, + { 0xbc1d, 0x0000 }, + { 0xbc1e, 0x0000 }, + { 0xbc1f, 0x0000 }, + { 0xbc20, 0x0000 }, + { 0xbc21, 0x0000 }, + { 0xbc22, 0x0000 }, + { 0xbc23, 0x0000 }, + { 0xbc24, 0x0000 }, + { 0xbc25, 0x0000 }, + { 0xbc26, 0x0000 }, + { 0xbc27, 0x0000 }, + { 0xbc28, 0x0000 }, + { 0xbc29, 0x0000 }, + { 0xbc2a, 0x0000 }, + { 0xbc2b, 0x0000 }, + { 0xbc2c, 0x0000 }, + { 0xbc2d, 0x0000 }, + { 0xbc2e, 0x0000 }, + { 0xbc2f, 0x0000 }, + { 0xbc30, 0x0000 }, + { 0xbc31, 0x0000 }, + { 0xbc32, 0x0000 }, + { 0xbc33, 0x0000 }, + { 0xbc34, 0x0000 }, + { 0xbc35, 0x0000 }, + { 0xbc36, 0x0000 }, + { 0xbc37, 0x0000 }, + { 0xbc38, 0x0000 }, + { 0xbc39, 0x0000 }, + { 0xbc3a, 0x0000 }, + { 0xbc3b, 0x0000 }, + { 0xbc3c, 0x0000 }, + { 0xbc3d, 0x0000 }, + { 0xbc3e, 0x0000 }, + { 0xbc3f, 0x0000 }, + { 0xbc40, 0x0000 }, + { 0xbc41, 0x0000 }, + { 0xbc42, 0x0000 }, + { 0xbc43, 0x0000 }, + { 0xbc44, 0x0000 }, + { 0xbc45, 0x0000 }, + { 0xbc46, 0x0000 }, + { 0xbc47, 0x0000 }, + { 0xbc48, 0x0000 }, + { 0xbc49, 0x0000 }, + { 0xbc4a, 0x0000 }, + { 0xbc4b, 0x0000 }, + { 0xbc4c, 0x0000 }, + { 0xbc4d, 0x0000 }, + { 0xbc4e, 0x0000 }, + { 0xbc4f, 0x0000 }, + { 0xbc50, 0x0000 }, + { 0xbc51, 0x0000 }, + { 0xbc52, 0x0000 }, + { 0xbc53, 0x0000 }, + { 0xbc54, 0x0000 }, + { 0xbc55, 0x0000 }, + { 0xbc56, 0x0000 }, + { 0xbc57, 0x0000 }, + { 0xbc58, 0x0000 }, + { 0xbc59, 0x0000 }, + { 0xbc5a, 0x0000 }, + { 0xbc5b, 0x0000 }, + { 0xbc5c, 0x0000 }, + { 0xbc5d, 0x0000 }, + { 0xbc5e, 0x0000 }, + { 0xbc5f, 0x0000 }, + { 0xbc60, 0x0000 }, + { 0xbc61, 0x0000 }, + { 0xbc62, 0x0000 }, + { 0xbc63, 0x0000 }, + { 0xbc64, 0x0000 }, + { 0xbc65, 0x0000 }, + { 0xbc66, 0x0000 }, + { 0xbc67, 0x0000 }, + { 0xbc68, 0x0000 }, + { 0xbc69, 0x0000 }, + { 0xbc6a, 0x0000 }, + { 0xbc6b, 0x0000 }, + { 0xbc6c, 0x0000 }, + { 0xbc6d, 0x0000 }, + { 0xbc6e, 0x0000 }, + { 0xbc6f, 0x0000 }, + { 0xbc70, 0x0000 }, + { 0xbc71, 0x0000 }, + { 0xbc72, 0x0000 }, + { 0xbc73, 0x0000 }, + { 0xbc74, 0x0000 }, + { 0xbc75, 0x0000 }, + { 0xbc76, 0x0000 }, + { 0xbc77, 0x0000 }, + { 0xbc78, 0x0000 }, + { 0xbc79, 0x0000 }, + { 0xbc7a, 0x0000 }, + { 0xbc7b, 0x0000 }, + { 0xbc7c, 0x0000 }, + { 0xbc7d, 0x0000 }, + { 0xbc7e, 0x0000 }, + { 0xbc7f, 0x0000 }, + { 0xbc80, 0x0000 }, + { 0xbc81, 0x0000 }, + { 0xbc82, 0x0000 }, + { 0xbc83, 0x0000 }, + { 0xbc84, 0x0000 }, + { 0xbc85, 0x0000 }, + { 0xbc86, 0x0000 }, + { 0xbc87, 0x0000 }, + { 0xbc88, 0x0000 }, + { 0xbc89, 0x0000 }, + { 0xbc8a, 0x0000 }, + { 0xbc8b, 0x0000 }, + { 0xbc8c, 0x0000 }, + { 0xbc8d, 0x0000 }, + { 0xbc8e, 0x0000 }, + { 0xbc8f, 0x0000 }, + { 0xbc90, 0x0000 }, + { 0xbc91, 0x0000 }, + { 0xbc92, 0x0000 }, + { 0xbc93, 0x0000 }, + { 0xbc94, 0x0000 }, + { 0xbc95, 0x0000 }, + { 0xbc96, 0x0000 }, + { 0xbc97, 0x0000 }, + { 0xbc98, 0x0000 }, + { 0xbc99, 0x0000 }, + { 0xbc9a, 0x0000 }, + { 0xbc9b, 0x0000 }, + { 0xbc9c, 0x0000 }, + { 0xbc9d, 0x0000 }, + { 0xbc9e, 0x0000 }, + { 0xbc9f, 0x0000 }, + { 0xbca0, 0x0000 }, + { 0xbca1, 0x0000 }, + { 0xbca2, 0x0000 }, + { 0xbca3, 0x0000 }, + { 0xbca4, 0x0000 }, + { 0xbca5, 0x0000 }, + { 0xbca6, 0x0000 }, + { 0xbca7, 0x0000 }, + { 0xbca8, 0x0000 }, + { 0xbca9, 0x0000 }, + { 0xbcaa, 0x0000 }, + { 0xbcab, 0x0000 }, + { 0xbcac, 0x0000 }, + { 0xbcad, 0x0000 }, + { 0xbcae, 0x0000 }, + { 0xbcaf, 0x0000 }, + { 0xbcb0, 0x0000 }, + { 0xbcb1, 0x0000 }, + { 0xbcb2, 0x0000 }, + { 0xbcb3, 0x0000 }, + { 0xbcb4, 0x0000 }, + { 0xbcb5, 0x0000 }, + { 0xbcb6, 0x0000 }, + { 0xbcb7, 0x0000 }, + { 0xbcb8, 0x0000 }, + { 0xbcb9, 0x0000 }, + { 0xbcba, 0x0000 }, + { 0xbcbb, 0x0000 }, + { 0xbcbc, 0x0000 }, + { 0xbcbd, 0x0000 }, + { 0xbcbe, 0x0000 }, + { 0xbcbf, 0x0000 }, + { 0xbcc0, 0x0000 }, + { 0xbcc1, 0x0000 }, + { 0xbcc2, 0x0000 }, + { 0xbcc3, 0x0000 }, + { 0xbcc4, 0x0000 }, + { 0xbcc5, 0x0000 }, + { 0xbcc6, 0x0000 }, + { 0xbcc7, 0x0000 }, + { 0xbcc8, 0x0000 }, + { 0xbcc9, 0x0000 }, + { 0xbcca, 0x0000 }, + { 0xbccb, 0x0000 }, + { 0xbccc, 0x0000 }, + { 0xbccd, 0x0000 }, + { 0xbcce, 0x0000 }, + { 0xbccf, 0x0000 }, + { 0xbcd0, 0x0000 }, + { 0xbcd1, 0x0000 }, + { 0xbcd2, 0x0000 }, + { 0xbcd3, 0x0000 }, + { 0xbcd4, 0x0000 }, + { 0xbcd5, 0x0000 }, + { 0xbcd6, 0x0000 }, + { 0xbcd7, 0x0000 }, + { 0xbcd8, 0x0000 }, + { 0xbcd9, 0x0000 }, + { 0xbcda, 0x0000 }, + { 0xbcdb, 0x0000 }, + { 0xbcdc, 0x0000 }, + { 0xbcdd, 0x0000 }, + { 0xbcde, 0x0000 }, + { 0xbcdf, 0x0000 }, + { 0xbce0, 0x0000 }, + { 0xbce1, 0x0000 }, + { 0xbce2, 0x0000 }, + { 0xbce3, 0x0000 }, + { 0xbce4, 0x0000 }, + { 0xbce5, 0x0000 }, + { 0xbce6, 0x0000 }, + { 0xbce7, 0x0000 }, + { 0xbce8, 0x0000 }, + { 0xbce9, 0x0000 }, + { 0xbcea, 0x0000 }, + { 0xbceb, 0x0000 }, + { 0xbcec, 0x0000 }, + { 0xbced, 0x0000 }, + { 0xbcee, 0x0000 }, + { 0xbcef, 0x0000 }, + { 0xbcf0, 0x0000 }, + { 0xbcf1, 0x0000 }, + { 0xbcf2, 0x0000 }, + { 0xbcf3, 0x0000 }, + { 0xbcf4, 0x0000 }, + { 0xbcf5, 0x0000 }, + { 0xbcf6, 0x0000 }, + { 0xbcf7, 0x0000 }, + { 0xbcf8, 0x0000 }, + { 0xbcf9, 0x0000 }, + { 0xbcfa, 0x0000 }, + { 0xbcfb, 0x0000 }, + { 0xbcfc, 0x0000 }, + { 0xbcfd, 0x0000 }, + { 0xbcfe, 0x0000 }, + { 0xbcff, 0x0000 }, + { 0xbd00, 0x0000 }, + { 0xbd01, 0x0000 }, + { 0xbd02, 0x0000 }, + { 0xbd03, 0x0000 }, + { 0xbd04, 0x0000 }, + { 0xbd05, 0x0000 }, + { 0xbd06, 0x0000 }, + { 0xbd07, 0x0000 }, + { 0xbd08, 0x0000 }, + { 0xbd09, 0x0000 }, + { 0xbd0a, 0x0000 }, + { 0xbd0b, 0x0000 }, + { 0xbd0c, 0x0000 }, + { 0xbd0d, 0x0000 }, + { 0xbd0e, 0x0000 }, + { 0xbd0f, 0x0000 }, + { 0xbd10, 0x0000 }, + { 0xbd11, 0x0000 }, + { 0xbd12, 0x0000 }, + { 0xbd13, 0x0000 }, + { 0xbd14, 0x0000 }, + { 0xbd15, 0x0000 }, + { 0xbd16, 0x0000 }, + { 0xbd17, 0x0000 }, + { 0xbd18, 0x0000 }, + { 0xbd19, 0x0000 }, + { 0xbd1a, 0x0000 }, + { 0xbd1b, 0x0000 }, + { 0xbd1c, 0x0000 }, + { 0xbd1d, 0x0000 }, + { 0xbd1e, 0x0000 }, + { 0xbd1f, 0x0000 }, + { 0xbd20, 0x0000 }, + { 0xbd21, 0x0000 }, + { 0xbd22, 0x0000 }, + { 0xbd23, 0x0000 }, + { 0xbd24, 0x0000 }, + { 0xbd25, 0x0000 }, + { 0xbd26, 0x0000 }, + { 0xbd27, 0x0000 }, + { 0xbd28, 0x0000 }, + { 0xbd29, 0x0000 }, + { 0xbd2a, 0x0000 }, + { 0xbd2b, 0x0000 }, + { 0xbd2c, 0x0000 }, + { 0xbd2d, 0x0000 }, + { 0xbd2e, 0x0000 }, + { 0xbd2f, 0x0000 }, + { 0xbd30, 0x0000 }, + { 0xbd31, 0x0000 }, + { 0xbd32, 0x0000 }, + { 0xbd33, 0x0000 }, + { 0xbd34, 0x0000 }, + { 0xbd35, 0x0000 }, + { 0xbd36, 0x0000 }, + { 0xbd37, 0x0000 }, + { 0xbd38, 0x0000 }, + { 0xbd39, 0x0000 }, + { 0xbd3a, 0x0000 }, + { 0xbd3b, 0x0000 }, + { 0xbd3c, 0x0000 }, + { 0xbd3d, 0x0000 }, + { 0xbd3e, 0x0000 }, + { 0xbd3f, 0x0000 }, + { 0xbd40, 0x0000 }, + { 0xbd41, 0x0000 }, + { 0xbd42, 0x0000 }, + { 0xbd43, 0x0000 }, + { 0xbd44, 0x0000 }, + { 0xbd45, 0x0000 }, + { 0xbd46, 0x0000 }, + { 0xbd47, 0x0000 }, + { 0xbd48, 0x0000 }, + { 0xbd49, 0x0000 }, + { 0xbd4a, 0x0000 }, + { 0xbd4b, 0x0000 }, + { 0xbd4c, 0x0000 }, + { 0xbd4d, 0x0000 }, + { 0xbd4e, 0x0000 }, + { 0xbd4f, 0x0000 }, + { 0xbd50, 0x0000 }, + { 0xbd51, 0x0000 }, + { 0xbd52, 0x0000 }, + { 0xbd53, 0x0000 }, + { 0xbd54, 0x0000 }, + { 0xbd55, 0x0000 }, + { 0xbd56, 0x0000 }, + { 0xbd57, 0x0000 }, + { 0xbd58, 0x0000 }, + { 0xbd59, 0x0000 }, + { 0xbd5a, 0x0000 }, + { 0xbd5b, 0x0000 }, + { 0xbd5c, 0x0000 }, + { 0xbd5d, 0x0000 }, + { 0xbd5e, 0x0000 }, + { 0xbd5f, 0x0000 }, + { 0xbd60, 0x0000 }, + { 0xbd61, 0x0000 }, + { 0xbd62, 0x0000 }, + { 0xbd63, 0x0000 }, + { 0xbd64, 0x0000 }, + { 0xbd65, 0x0000 }, + { 0xbd66, 0x0000 }, + { 0xbd67, 0x0000 }, + { 0xbd68, 0x0000 }, + { 0xbd69, 0x0000 }, + { 0xbd6a, 0x0000 }, + { 0xbd6b, 0x0000 }, + { 0xbd6c, 0x0000 }, + { 0xbd6d, 0x0000 }, + { 0xbd6e, 0x0000 }, + { 0xbd6f, 0x0000 }, + { 0xbd70, 0x0000 }, + { 0xbd71, 0x0000 }, + { 0xbd72, 0x0000 }, + { 0xbd73, 0x0000 }, + { 0xbd74, 0x0000 }, + { 0xbd75, 0x0000 }, + { 0xbd76, 0x0000 }, + { 0xbd77, 0x0000 }, + { 0xbd78, 0x0000 }, + { 0xbd79, 0x0000 }, + { 0xbd7a, 0x0000 }, + { 0xbd7b, 0x0000 }, + { 0xbd7c, 0x0000 }, + { 0xbd7d, 0x0000 }, + { 0xbd7e, 0x0000 }, + { 0xbd7f, 0x0000 }, + { 0xbd80, 0x0000 }, + { 0xbd81, 0x0000 }, + { 0xbd82, 0x0000 }, + { 0xbd83, 0x0000 }, + { 0xbd84, 0x0000 }, + { 0xbd85, 0x0000 }, + { 0xbd86, 0x0000 }, + { 0xbd87, 0x0000 }, + { 0xbd88, 0x0000 }, + { 0xbd89, 0x0000 }, + { 0xbd8a, 0x0000 }, + { 0xbd8b, 0x0000 }, + { 0xbd8c, 0x0000 }, + { 0xbd8d, 0x0000 }, + { 0xbd8e, 0x0000 }, + { 0xbd8f, 0x0000 }, + { 0xbd90, 0x0000 }, + { 0xbd91, 0x0000 }, + { 0xbd92, 0x0000 }, + { 0xbd93, 0x0000 }, + { 0xbd94, 0x0000 }, + { 0xbd95, 0x0000 }, + { 0xbd96, 0x0000 }, + { 0xbd97, 0x0000 }, + { 0xbd98, 0x0000 }, + { 0xbd99, 0x0000 }, + { 0xbd9a, 0x0000 }, + { 0xbd9b, 0x0000 }, + { 0xbd9c, 0x0000 }, + { 0xbd9d, 0x0000 }, + { 0xbd9e, 0x0000 }, + { 0xbd9f, 0x0000 }, + { 0xbda0, 0x0000 }, + { 0xbda1, 0x0000 }, + { 0xbda2, 0x0000 }, + { 0xbda3, 0x0000 }, + { 0xbda4, 0x0000 }, + { 0xbda5, 0x0000 }, + { 0xbda6, 0x0000 }, + { 0xbda7, 0x0000 }, + { 0xbda8, 0x0000 }, + { 0xbda9, 0x0000 }, + { 0xbdaa, 0x0000 }, + { 0xbdab, 0x0000 }, + { 0xbdac, 0x0000 }, + { 0xbdad, 0x0000 }, + { 0xbdae, 0x0000 }, + { 0xbdaf, 0x0000 }, + { 0xbdb0, 0x0000 }, + { 0xbdb1, 0x0000 }, + { 0xbdb2, 0x0000 }, + { 0xbdb3, 0x0000 }, + { 0xbdb4, 0x0000 }, + { 0xbdb5, 0x0000 }, + { 0xbdb6, 0x0000 }, + { 0xbdb7, 0x0000 }, + { 0xbdb8, 0x0000 }, + { 0xbdb9, 0x0000 }, + { 0xbdba, 0x0000 }, + { 0xbdbb, 0x0000 }, + { 0xbdbc, 0x0000 }, + { 0xbdbd, 0x0000 }, + { 0xbdbe, 0x0000 }, + { 0xbdbf, 0x0000 }, + { 0xbdc0, 0x0000 }, + { 0xbdc1, 0x0000 }, + { 0xbdc2, 0x0000 }, + { 0xbdc3, 0x0000 }, + { 0xbdc4, 0x0000 }, + { 0xbdc5, 0x0000 }, + { 0xbdc6, 0x0000 }, + { 0xbdc7, 0x0000 }, + { 0xbdc8, 0x0000 }, + { 0xbdc9, 0x0000 }, + { 0xbdca, 0x0000 }, + { 0xbdcb, 0x0000 }, + { 0xbdcc, 0x0000 }, + { 0xbdcd, 0x0000 }, + { 0xbdce, 0x0000 }, + { 0xbdcf, 0x0000 }, + { 0xbdd0, 0x0000 }, + { 0xbdd1, 0x0000 }, + { 0xbdd2, 0x0000 }, + { 0xbdd3, 0x0000 }, + { 0xbdd4, 0x0000 }, + { 0xbdd5, 0x0000 }, + { 0xbdd6, 0x0000 }, + { 0xbdd7, 0x0000 }, + { 0xbdd8, 0x0000 }, + { 0xbdd9, 0x0000 }, + { 0xbdda, 0x0000 }, + { 0xbddb, 0x0000 }, + { 0xbddc, 0x0000 }, + { 0xbddd, 0x0000 }, + { 0xbdde, 0x0000 }, + { 0xbddf, 0x0000 }, + { 0xbde0, 0x0000 }, + { 0xbde1, 0x0000 }, + { 0xbde2, 0x0000 }, + { 0xbde3, 0x0000 }, + { 0xbde4, 0x0000 }, + { 0xbde5, 0x0000 }, + { 0xbde6, 0x0000 }, + { 0xbde7, 0x0000 }, + { 0xbde8, 0x0000 }, + { 0xbde9, 0x0000 }, + { 0xbdea, 0x0000 }, + { 0xbdeb, 0x0000 }, + { 0xbdec, 0x0000 }, + { 0xbded, 0x0000 }, + { 0xbdee, 0x0000 }, + { 0xbdef, 0x0000 }, + { 0xbdf0, 0x0000 }, + { 0xbdf1, 0x0000 }, + { 0xbdf2, 0x0000 }, + { 0xbdf3, 0x0000 }, + { 0xbdf4, 0x0000 }, + { 0xbdf5, 0x0000 }, + { 0xbdf6, 0x0000 }, + { 0xbdf7, 0x0000 }, + { 0xbdf8, 0x0000 }, + { 0xbdf9, 0x0000 }, + { 0xbdfa, 0x0000 }, + { 0xbdfb, 0x0000 }, + { 0xbdfc, 0x0000 }, + { 0xbdfd, 0x0000 }, + { 0xbdfe, 0x0000 }, + { 0xbdff, 0x0000 }, + { 0xbe00, 0x0000 }, + { 0xbe01, 0x0000 }, + { 0xbe02, 0x0000 }, + { 0xbe03, 0x0000 }, + { 0xbe04, 0x0000 }, + { 0xbe05, 0x0000 }, + { 0xbe06, 0x0000 }, + { 0xbe07, 0x0000 }, + { 0xbe08, 0x0000 }, + { 0xbe09, 0x0000 }, + { 0xbe0a, 0x0000 }, + { 0xbe0b, 0x0000 }, + { 0xbe0c, 0x0000 }, + { 0xbe0d, 0x0000 }, + { 0xbe0e, 0x0000 }, + { 0xbe0f, 0x0000 }, + { 0xbe10, 0x0000 }, + { 0xbe11, 0x0000 }, + { 0xbe12, 0x0000 }, + { 0xbe13, 0x0000 }, + { 0xbe14, 0x0000 }, + { 0xbe15, 0x0000 }, + { 0xbe16, 0x0000 }, + { 0xbe17, 0x0000 }, + { 0xbe18, 0x0000 }, + { 0xbe19, 0x0000 }, + { 0xbe1a, 0x0000 }, + { 0xbe1b, 0x0000 }, + { 0xbe1c, 0x0000 }, + { 0xbe1d, 0x0000 }, + { 0xbe1e, 0x0000 }, + { 0xbe1f, 0x0000 }, + { 0xbe20, 0x0000 }, + { 0xbe21, 0x0000 }, + { 0xbe22, 0x0000 }, + { 0xbe23, 0x0000 }, + { 0xbe24, 0x0000 }, + { 0xbe25, 0x0000 }, + { 0xbe26, 0x0000 }, + { 0xbe27, 0x0000 }, + { 0xbe28, 0x0000 }, + { 0xbe29, 0x0000 }, + { 0xbe2a, 0x0000 }, + { 0xbe2b, 0x0000 }, + { 0xbe2c, 0x0000 }, + { 0xbe2d, 0x0000 }, + { 0xbe2e, 0x0000 }, + { 0xbe2f, 0x0000 }, + { 0xbe30, 0x0000 }, + { 0xbe31, 0x0000 }, + { 0xbe32, 0x0000 }, + { 0xbe33, 0x0000 }, + { 0xbe34, 0x0000 }, + { 0xbe35, 0x0000 }, + { 0xbe36, 0x0000 }, + { 0xbe37, 0x0000 }, + { 0xbe38, 0x0000 }, + { 0xbe39, 0x0000 }, + { 0xbe3a, 0x0000 }, + { 0xbe3b, 0x0000 }, + { 0xbe3c, 0x0000 }, + { 0xbe3d, 0x0000 }, + { 0xbe3e, 0x0000 }, + { 0xbe3f, 0x0000 }, + { 0xbe40, 0x0000 }, + { 0xbe41, 0x0000 }, + { 0xbe42, 0x0000 }, + { 0xbe43, 0x0000 }, + { 0xbe44, 0x0000 }, + { 0xbe45, 0x0000 }, + { 0xbe46, 0x0000 }, + { 0xbe47, 0x0000 }, + { 0xbe48, 0x0000 }, + { 0xbe49, 0x0000 }, + { 0xbe4a, 0x0000 }, + { 0xbe4b, 0x0000 }, + { 0xbe4c, 0x0000 }, + { 0xbe4d, 0x0000 }, + { 0xbe4e, 0x0000 }, + { 0xbe4f, 0x0000 }, + { 0xbe50, 0x0000 }, + { 0xbe51, 0x0000 }, + { 0xbe52, 0x0000 }, + { 0xbe53, 0x0000 }, + { 0xbe54, 0x0000 }, + { 0xbe55, 0x0000 }, + { 0xbe56, 0x0000 }, + { 0xbe57, 0x0000 }, + { 0xbe58, 0x0000 }, + { 0xbe59, 0x0000 }, + { 0xbe5a, 0x0000 }, + { 0xbe5b, 0x0000 }, + { 0xbe5c, 0x0000 }, + { 0xbe5d, 0x0000 }, + { 0xbe5e, 0x0000 }, + { 0xbe5f, 0x0000 }, + { 0xbe60, 0x0000 }, + { 0xbe61, 0x0000 }, + { 0xbe62, 0x0000 }, + { 0xbe63, 0x0000 }, + { 0xbe64, 0x0000 }, + { 0xbe65, 0x0000 }, + { 0xbe66, 0x0000 }, + { 0xbe67, 0x0000 }, + { 0xbe68, 0x0000 }, + { 0xbe69, 0x0000 }, + { 0xbe6a, 0x0000 }, + { 0xbe6b, 0x0000 }, + { 0xbe6c, 0x0000 }, + { 0xbe6d, 0x0000 }, + { 0xbe6e, 0x0000 }, + { 0xbe6f, 0x0000 }, + { 0xbe70, 0x0000 }, + { 0xbe71, 0x0000 }, + { 0xbe72, 0x0000 }, + { 0xbe73, 0x0000 }, + { 0xbe74, 0x0000 }, + { 0xbe75, 0x0000 }, + { 0xbe76, 0x0000 }, + { 0xbe77, 0x0000 }, + { 0xbe78, 0x0000 }, + { 0xbe79, 0x0000 }, + { 0xbe7a, 0x0000 }, + { 0xbe7b, 0x0000 }, + { 0xbe7c, 0x0000 }, + { 0xbe7d, 0x0000 }, + { 0xbe7e, 0x0000 }, + { 0xbe7f, 0x0000 }, + { 0xbe80, 0x0000 }, + { 0xbe81, 0x0000 }, + { 0xbe82, 0x0000 }, + { 0xbe83, 0x0000 }, + { 0xbe84, 0x0000 }, + { 0xbe85, 0x0000 }, + { 0xbe86, 0x0000 }, + { 0xbe87, 0x0000 }, + { 0xbe88, 0x0000 }, + { 0xbe89, 0x0000 }, + { 0xbe8a, 0x0000 }, + { 0xbe8b, 0x0000 }, + { 0xbe8c, 0x0000 }, + { 0xbe8d, 0x0000 }, + { 0xbe8e, 0x0000 }, + { 0xbe8f, 0x0000 }, + { 0xbe90, 0x0000 }, + { 0xbe91, 0x0000 }, + { 0xbe92, 0x0000 }, + { 0xbe93, 0x0000 }, + { 0xbe94, 0x0000 }, + { 0xbe95, 0x0000 }, + { 0xbe96, 0x0000 }, + { 0xbe97, 0x0000 }, + { 0xbe98, 0x0000 }, + { 0xbe99, 0x0000 }, + { 0xbe9a, 0x0000 }, + { 0xbe9b, 0x0000 }, + { 0xbe9c, 0x0000 }, + { 0xbe9d, 0x0000 }, + { 0xbe9e, 0x0000 }, + { 0xbe9f, 0x0000 }, + { 0xbea0, 0x0000 }, + { 0xbea1, 0x0000 }, + { 0xbea2, 0x0000 }, + { 0xbea3, 0x0000 }, + { 0xbea4, 0x0000 }, + { 0xbea5, 0x0000 }, + { 0xbea6, 0x0000 }, + { 0xbea7, 0x0000 }, + { 0xbea8, 0x0000 }, + { 0xbea9, 0x0000 }, + { 0xbeaa, 0x0000 }, + { 0xbeab, 0x0000 }, + { 0xbeac, 0x0000 }, + { 0xbead, 0x0000 }, + { 0xbeae, 0x0000 }, + { 0xbeaf, 0x0000 }, + { 0xbeb0, 0x0000 }, + { 0xbeb1, 0x0000 }, + { 0xbeb2, 0x0000 }, + { 0xbeb3, 0x0000 }, + { 0xbeb4, 0x0000 }, + { 0xbeb5, 0x0000 }, + { 0xbeb6, 0x0000 }, + { 0xbeb7, 0x0000 }, + { 0xbeb8, 0x0000 }, + { 0xbeb9, 0x0000 }, + { 0xbeba, 0x0000 }, + { 0xbebb, 0x0000 }, + { 0xbebc, 0x0000 }, + { 0xbebd, 0x0000 }, + { 0xbebe, 0x0000 }, + { 0xbebf, 0x0000 }, + { 0xbec0, 0x0000 }, + { 0xbec1, 0x0000 }, + { 0xbec2, 0x0000 }, + { 0xbec3, 0x0000 }, + { 0xbec4, 0x0000 }, + { 0xbec5, 0x0000 }, + { 0xbec6, 0x0000 }, + { 0xbec7, 0x0000 }, + { 0xbec8, 0x0000 }, + { 0xbec9, 0x0000 }, + { 0xbeca, 0x0000 }, + { 0xbecb, 0x0000 }, + { 0xbecc, 0x0000 }, + { 0xbecd, 0x0000 }, + { 0xbece, 0x0000 }, + { 0xbecf, 0x0000 }, + { 0xbed0, 0x0000 }, + { 0xbed1, 0x0000 }, + { 0xbed2, 0x0000 }, + { 0xbed3, 0x0000 }, + { 0xbed4, 0x0000 }, + { 0xbed5, 0x0000 }, + { 0xbed6, 0x0000 }, + { 0xbed7, 0x0000 }, + { 0xbed8, 0x0000 }, + { 0xbed9, 0x0000 }, + { 0xbeda, 0x0000 }, + { 0xbedb, 0x0000 }, + { 0xbedc, 0x0000 }, + { 0xbedd, 0x0000 }, + { 0xbede, 0x0000 }, + { 0xbedf, 0x0000 }, + { 0xbee0, 0x0000 }, + { 0xbee1, 0x0000 }, + { 0xbee2, 0x0000 }, + { 0xbee3, 0x0000 }, + { 0xbee4, 0x0000 }, + { 0xbee5, 0x0000 }, + { 0xbee6, 0x0000 }, + { 0xbee7, 0x0000 }, + { 0xbee8, 0x0000 }, + { 0xbee9, 0x0000 }, + { 0xbeea, 0x0000 }, + { 0xbeeb, 0x0000 }, + { 0xbeec, 0x0000 }, + { 0xbeed, 0x0000 }, + { 0xbeee, 0x0000 }, + { 0xbeef, 0x0000 }, + { 0xbef0, 0x0000 }, + { 0xbef1, 0x0000 }, + { 0xbef2, 0x0000 }, + { 0xbef3, 0x0000 }, + { 0xbef4, 0x0000 }, + { 0xbef5, 0x0000 }, + { 0xbef6, 0x0000 }, + { 0xbef7, 0x0000 }, + { 0xbef8, 0x0000 }, + { 0xbef9, 0x0000 }, + { 0xbefa, 0x0000 }, + { 0xbefb, 0x0000 }, + { 0xbefc, 0x0000 }, + { 0xbefd, 0x0000 }, + { 0xbefe, 0x0000 }, + { 0xbeff, 0x0000 }, + { 0xbf00, 0x0000 }, + { 0xbf01, 0x0000 }, + { 0xbf02, 0x0000 }, + { 0xbf03, 0x0000 }, + { 0xbf04, 0x0000 }, + { 0xbf05, 0x0000 }, + { 0xbf06, 0x0000 }, + { 0xbf07, 0x0000 }, + { 0xbf08, 0x0000 }, + { 0xbf09, 0x0000 }, + { 0xbf0a, 0x0000 }, + { 0xbf0b, 0x0000 }, + { 0xbf0c, 0x0000 }, + { 0xbf0d, 0x0000 }, + { 0xbf0e, 0x0000 }, + { 0xbf0f, 0x0000 }, + { 0xbf10, 0x0000 }, + { 0xbf11, 0x0000 }, + { 0xbf12, 0x0000 }, + { 0xbf13, 0x0000 }, + { 0xbf14, 0x0000 }, + { 0xbf15, 0x0000 }, + { 0xbf16, 0x0000 }, + { 0xbf17, 0x0000 }, + { 0xbf18, 0x0000 }, + { 0xbf19, 0x0000 }, + { 0xbf1a, 0x0000 }, + { 0xbf1b, 0x0000 }, + { 0xbf1c, 0x0000 }, + { 0xbf1d, 0x0000 }, + { 0xbf1e, 0x0000 }, + { 0xbf1f, 0x0000 }, + { 0xbf20, 0x0000 }, + { 0xbf21, 0x0000 }, + { 0xbf22, 0x0000 }, + { 0xbf23, 0x0000 }, + { 0xbf24, 0x0000 }, + { 0xbf25, 0x0000 }, + { 0xbf26, 0x0000 }, + { 0xbf27, 0x0000 }, + { 0xbf28, 0x0000 }, + { 0xbf29, 0x0000 }, + { 0xbf2a, 0x0000 }, + { 0xbf2b, 0x0000 }, + { 0xbf2c, 0x0000 }, + { 0xbf2d, 0x0000 }, + { 0xbf2e, 0x0000 }, + { 0xbf2f, 0x0000 }, + { 0xbf30, 0x0000 }, + { 0xbf31, 0x0000 }, + { 0xbf32, 0x0000 }, + { 0xbf33, 0x0000 }, + { 0xbf34, 0x0000 }, + { 0xbf35, 0x0000 }, + { 0xbf36, 0x0000 }, + { 0xbf37, 0x0000 }, + { 0xbf38, 0x0000 }, + { 0xbf39, 0x0000 }, + { 0xbf3a, 0x0000 }, + { 0xbf3b, 0x0000 }, + { 0xbf3c, 0x0000 }, + { 0xbf3d, 0x0000 }, + { 0xbf3e, 0x0000 }, + { 0xbf3f, 0x0000 }, + { 0xbf40, 0x0000 }, + { 0xbf41, 0x0000 }, + { 0xbf42, 0x0000 }, + { 0xbf43, 0x0000 }, + { 0xbf44, 0x0000 }, + { 0xbf45, 0x0000 }, + { 0xbf46, 0x0000 }, + { 0xbf47, 0x0000 }, + { 0xbf48, 0x0000 }, + { 0xbf49, 0x0000 }, + { 0xbf4a, 0x0000 }, + { 0xbf4b, 0x0000 }, + { 0xbf4c, 0x0000 }, + { 0xbf4d, 0x0000 }, + { 0xbf4e, 0x0000 }, + { 0xbf4f, 0x0000 }, + { 0xbf50, 0x0000 }, + { 0xbf51, 0x0000 }, + { 0xbf52, 0x0000 }, + { 0xbf53, 0x0000 }, + { 0xbf54, 0x0000 }, + { 0xbf55, 0x0000 }, + { 0xbf56, 0x0000 }, + { 0xbf57, 0x0000 }, + { 0xbf58, 0x0000 }, + { 0xbf59, 0x0000 }, + { 0xbf5a, 0x0000 }, + { 0xbf5b, 0x0000 }, + { 0xbf5c, 0x0000 }, + { 0xbf5d, 0x0000 }, + { 0xbf5e, 0x0000 }, + { 0xbf5f, 0x0000 }, + { 0xbf60, 0x0000 }, + { 0xbf61, 0x0000 }, + { 0xbf62, 0x0000 }, + { 0xbf63, 0x0000 }, + { 0xbf64, 0x0000 }, + { 0xbf65, 0x0000 }, + { 0xbf66, 0x0000 }, + { 0xbf67, 0x0000 }, + { 0xbf68, 0x0000 }, + { 0xbf69, 0x0000 }, + { 0xbf6a, 0x0000 }, + { 0xbf6b, 0x0000 }, + { 0xbf6c, 0x0000 }, + { 0xbf6d, 0x0000 }, + { 0xbf6e, 0x0000 }, + { 0xbf6f, 0x0000 }, + { 0xbf70, 0x0000 }, + { 0xbf71, 0x0000 }, + { 0xbf72, 0x0000 }, + { 0xbf73, 0x0000 }, + { 0xbf74, 0x0000 }, + { 0xbf75, 0x0000 }, + { 0xbf76, 0x0000 }, + { 0xbf77, 0x0000 }, + { 0xbf78, 0x0000 }, + { 0xbf79, 0x0000 }, + { 0xbf7a, 0x0000 }, + { 0xbf7b, 0x0000 }, + { 0xbf7c, 0x0000 }, + { 0xbf7d, 0x0000 }, + { 0xbf7e, 0x0000 }, + { 0xbf7f, 0x0000 }, + { 0xbf80, 0x0000 }, + { 0xbf81, 0x0000 }, + { 0xbf82, 0x0000 }, + { 0xbf83, 0x0000 }, + { 0xbf84, 0x0000 }, + { 0xbf85, 0x0000 }, + { 0xbf86, 0x0000 }, + { 0xbf87, 0x0000 }, + { 0xbf88, 0x0000 }, + { 0xbf89, 0x0000 }, + { 0xbf8a, 0x0000 }, + { 0xbf8b, 0x0000 }, + { 0xbf8c, 0x0000 }, + { 0xbf8d, 0x0000 }, + { 0xbf8e, 0x0000 }, + { 0xbf8f, 0x0000 }, + { 0xbf90, 0x0000 }, + { 0xbf91, 0x0000 }, + { 0xbf92, 0x0000 }, + { 0xbf93, 0x0000 }, + { 0xbf94, 0x0000 }, + { 0xbf95, 0x0000 }, + { 0xbf96, 0x0000 }, + { 0xbf97, 0x0000 }, + { 0xbf98, 0x0000 }, + { 0xbf99, 0x0000 }, + { 0xbf9a, 0x0000 }, + { 0xbf9b, 0x0000 }, + { 0xbf9c, 0x0000 }, + { 0xbf9d, 0x0000 }, + { 0xbf9e, 0x0000 }, + { 0xbf9f, 0x0000 }, + { 0xbfa0, 0x0000 }, + { 0xbfa1, 0x0000 }, + { 0xbfa2, 0x0000 }, + { 0xbfa3, 0x0000 }, + { 0xbfa4, 0x0000 }, + { 0xbfa5, 0x0000 }, + { 0xbfa6, 0x0000 }, + { 0xbfa7, 0x0000 }, + { 0xbfa8, 0x0000 }, + { 0xbfa9, 0x0000 }, + { 0xbfaa, 0x0000 }, + { 0xbfab, 0x0000 }, + { 0xbfac, 0x0000 }, + { 0xbfad, 0x0000 }, + { 0xbfae, 0x0000 }, + { 0xbfaf, 0x0000 }, + { 0xbfb0, 0x0000 }, + { 0xbfb1, 0x0000 }, + { 0xbfb2, 0x0000 }, + { 0xbfb3, 0x0000 }, + { 0xbfb4, 0x0000 }, + { 0xbfb5, 0x0000 }, + { 0xbfb6, 0x0000 }, + { 0xbfb7, 0x0000 }, + { 0xbfb8, 0x0000 }, + { 0xbfb9, 0x0000 }, + { 0xbfba, 0x0000 }, + { 0xbfbb, 0x0000 }, + { 0xbfbc, 0x0000 }, + { 0xbfbd, 0x0000 }, + { 0xbfbe, 0x0000 }, + { 0xbfbf, 0x0000 }, + { 0xbfc0, 0x0000 }, + { 0xbfc1, 0x0000 }, + { 0xbfc2, 0x0000 }, + { 0xbfc3, 0x0000 }, + { 0xbfc4, 0x0000 }, + { 0xbfc5, 0x0000 }, + { 0xbfc6, 0x0000 }, + { 0xbfc7, 0x0000 }, + { 0xbfc8, 0x0000 }, + { 0xbfc9, 0x0000 }, + { 0xbfca, 0x0000 }, + { 0xbfcb, 0x0000 }, + { 0xbfcc, 0x0000 }, + { 0xbfcd, 0x0000 }, + { 0xbfce, 0x0000 }, + { 0xbfcf, 0x0000 }, + { 0xbfd0, 0x0000 }, + { 0xbfd1, 0x0000 }, + { 0xbfd2, 0x0000 }, + { 0xbfd3, 0x0000 }, + { 0xbfd4, 0x0000 }, + { 0xbfd5, 0x0000 }, + { 0xbfd6, 0x0000 }, + { 0xbfd7, 0x0000 }, + { 0xbfd8, 0x0000 }, + { 0xbfd9, 0x0000 }, + { 0xbfda, 0x0000 }, + { 0xbfdb, 0x0000 }, + { 0xbfdc, 0x0000 }, + { 0xbfdd, 0x0000 }, + { 0xbfde, 0x0000 }, + { 0xbfdf, 0x0000 }, + { 0xbfe0, 0x0000 }, + { 0xbfe1, 0x0000 }, + { 0xbfe2, 0x0000 }, + { 0xbfe3, 0x0000 }, + { 0xbfe4, 0x0000 }, + { 0xbfe5, 0x0000 }, + { 0xbfe6, 0x0000 }, + { 0xbfe7, 0x0000 }, + { 0xbfe8, 0x0000 }, + { 0xbfe9, 0x0000 }, + { 0xbfea, 0x0000 }, + { 0xbfeb, 0x0000 }, + { 0xbfec, 0x0000 }, + { 0xbfed, 0x0000 }, + { 0xbfee, 0x0000 }, + { 0xbfef, 0x0000 }, + { 0xbff0, 0x0000 }, + { 0xbff1, 0x0000 }, + { 0xbff2, 0x0000 }, + { 0xbff3, 0x0000 }, + { 0xbff4, 0x0000 }, + { 0xbff5, 0x0000 }, + { 0xbff6, 0x0000 }, + { 0xbff7, 0x0000 }, + { 0xbff8, 0x0000 }, + { 0xbff9, 0x0000 }, + { 0xbffa, 0x0000 }, + { 0xbffb, 0x0000 }, + { 0xbffc, 0x0000 }, + { 0xbffd, 0x0000 }, + { 0xbffe, 0x0000 }, + { 0xbfff, 0x0000 }, + { 0xc000, 0x0000 }, + { 0xc001, 0x0000 }, + { 0xc002, 0x0000 }, + { 0xc003, 0x0000 }, + { 0xc004, 0x0000 }, + { 0xc005, 0x0000 }, + { 0xc006, 0x0000 }, + { 0xc007, 0x0000 }, + { 0xc008, 0x0000 }, + { 0xc009, 0x0000 }, + { 0xc00a, 0x0000 }, + { 0xc00b, 0x0000 }, + { 0xc00c, 0x0000 }, + { 0xc00d, 0x0000 }, + { 0xc00e, 0x0000 }, + { 0xc00f, 0x0000 }, + { 0xc010, 0x0000 }, + { 0xc011, 0x0000 }, + { 0xc012, 0x0000 }, + { 0xc013, 0x0000 }, + { 0xc014, 0x0000 }, + { 0xc015, 0x0000 }, + { 0xc016, 0x0000 }, + { 0xc017, 0x0000 }, + { 0xc018, 0x0000 }, + { 0xc019, 0x0000 }, + { 0xc01a, 0x0000 }, + { 0xc01b, 0x0000 }, + { 0xc01c, 0x0000 }, + { 0xc01d, 0x0000 }, + { 0xc01e, 0x0000 }, + { 0xc01f, 0x0000 }, + { 0xc020, 0x0000 }, + { 0xc021, 0x0000 }, + { 0xc022, 0x0000 }, + { 0xc023, 0x0000 }, + { 0xc024, 0x0000 }, + { 0xc025, 0x0000 }, + { 0xc026, 0x0000 }, + { 0xc027, 0x0000 }, + { 0xc028, 0x0000 }, + { 0xc029, 0x0000 }, + { 0xc02a, 0x0000 }, + { 0xc02b, 0x0000 }, + { 0xc02c, 0x0000 }, + { 0xc02d, 0x0000 }, + { 0xc02e, 0x0000 }, + { 0xc02f, 0x0000 }, + { 0xc030, 0x0000 }, + { 0xc031, 0x0000 }, + { 0xc032, 0x0000 }, + { 0xc033, 0x0000 }, + { 0xc034, 0x0000 }, + { 0xc035, 0x0000 }, + { 0xc036, 0x0000 }, + { 0xc037, 0x0000 }, + { 0xc038, 0x0000 }, + { 0xc039, 0x0000 }, + { 0xc03a, 0x0000 }, + { 0xc03b, 0x0000 }, + { 0xc03c, 0x0000 }, + { 0xc03d, 0x0000 }, + { 0xc03e, 0x0000 }, + { 0xc03f, 0x0000 }, + { 0xc040, 0x0000 }, + { 0xc041, 0x0000 }, + { 0xc042, 0x0000 }, + { 0xc043, 0x0000 }, + { 0xc044, 0x0000 }, + { 0xc045, 0x0000 }, + { 0xc046, 0x0000 }, + { 0xc047, 0x0000 }, + { 0xc048, 0x0000 }, + { 0xc049, 0x0000 }, + { 0xc04a, 0x0000 }, + { 0xc04b, 0x0000 }, + { 0xc04c, 0x0000 }, + { 0xc04d, 0x0000 }, + { 0xc04e, 0x0000 }, + { 0xc04f, 0x0000 }, + { 0xc050, 0x0000 }, + { 0xc051, 0x0000 }, + { 0xc052, 0x0000 }, + { 0xc053, 0x0000 }, + { 0xc054, 0x0000 }, + { 0xc055, 0x0000 }, + { 0xc056, 0x0000 }, + { 0xc057, 0x0000 }, + { 0xc058, 0x0000 }, + { 0xc059, 0x0000 }, + { 0xc05a, 0x0000 }, + { 0xc05b, 0x0000 }, + { 0xc05c, 0x0000 }, + { 0xc05d, 0x0000 }, + { 0xc05e, 0x0000 }, + { 0xc05f, 0x0000 }, + { 0xc060, 0x0000 }, + { 0xc061, 0x0000 }, + { 0xc062, 0x0000 }, + { 0xc063, 0x0000 }, + { 0xc064, 0x0000 }, + { 0xc065, 0x0000 }, + { 0xc066, 0x0000 }, + { 0xc067, 0x0000 }, + { 0xc068, 0x0000 }, + { 0xc069, 0x0000 }, + { 0xc06a, 0x0000 }, + { 0xc06b, 0x0000 }, + { 0xc06c, 0x0000 }, + { 0xc06d, 0x0000 }, + { 0xc06e, 0x0000 }, + { 0xc06f, 0x0000 }, + { 0xc070, 0x0000 }, + { 0xc071, 0x0000 }, + { 0xc072, 0x0000 }, + { 0xc073, 0x0000 }, + { 0xc074, 0x0000 }, + { 0xc075, 0x0000 }, + { 0xc076, 0x0000 }, + { 0xc077, 0x0000 }, + { 0xc078, 0x0000 }, + { 0xc079, 0x0000 }, + { 0xc07a, 0x0000 }, + { 0xc07b, 0x0000 }, + { 0xc07c, 0x0000 }, + { 0xc07d, 0x0000 }, + { 0xc07e, 0x0000 }, + { 0xc07f, 0x0000 }, + { 0xc080, 0x0000 }, + { 0xc081, 0x0000 }, + { 0xc082, 0x0000 }, + { 0xc083, 0x0000 }, + { 0xc084, 0x0000 }, + { 0xc085, 0x0000 }, + { 0xc086, 0x0000 }, + { 0xc087, 0x0000 }, + { 0xc088, 0x0000 }, + { 0xc089, 0x0000 }, + { 0xc08a, 0x0000 }, + { 0xc08b, 0x0000 }, + { 0xc08c, 0x0000 }, + { 0xc08d, 0x0000 }, + { 0xc08e, 0x0000 }, + { 0xc08f, 0x0000 }, + { 0xc090, 0x0000 }, + { 0xc091, 0x0000 }, + { 0xc092, 0x0000 }, + { 0xc093, 0x0000 }, + { 0xc094, 0x0000 }, + { 0xc095, 0x0000 }, + { 0xc096, 0x0000 }, + { 0xc097, 0x0000 }, + { 0xc098, 0x0000 }, + { 0xc099, 0x0000 }, + { 0xc09a, 0x0000 }, + { 0xc09b, 0x0000 }, + { 0xc09c, 0x0000 }, + { 0xc09d, 0x0000 }, + { 0xc09e, 0x0000 }, + { 0xc09f, 0x0000 }, + { 0xc0a0, 0x0000 }, + { 0xc0a1, 0x0000 }, + { 0xc0a2, 0x0000 }, + { 0xc0a3, 0x0000 }, + { 0xc0a4, 0x0000 }, + { 0xc0a5, 0x0000 }, + { 0xc0a6, 0x0000 }, + { 0xc0a7, 0x0000 }, + { 0xc0a8, 0x0000 }, + { 0xc0a9, 0x0000 }, + { 0xc0aa, 0x0000 }, + { 0xc0ab, 0x0000 }, + { 0xc0ac, 0x0000 }, + { 0xc0ad, 0x0000 }, + { 0xc0ae, 0x0000 }, + { 0xc0af, 0x0000 }, + { 0xc0b0, 0x0000 }, + { 0xc0b1, 0x0000 }, + { 0xc0b2, 0x0000 }, + { 0xc0b3, 0x0000 }, + { 0xc0b4, 0x0000 }, + { 0xc0b5, 0x0000 }, + { 0xc0b6, 0x0000 }, + { 0xc0b7, 0x0000 }, + { 0xc0b8, 0x0000 }, + { 0xc0b9, 0x0000 }, + { 0xc0ba, 0x0000 }, + { 0xc0bb, 0x0000 }, + { 0xc0bc, 0x0000 }, + { 0xc0bd, 0x0000 }, + { 0xc0be, 0x0000 }, + { 0xc0bf, 0x0000 }, + { 0xc0c0, 0x0000 }, + { 0xc0c1, 0x0000 }, + { 0xc0c2, 0x0000 }, + { 0xc0c3, 0x0000 }, + { 0xc0c4, 0x0000 }, + { 0xc0c5, 0x0000 }, + { 0xc0c6, 0x0000 }, + { 0xc0c7, 0x0000 }, + { 0xc0c8, 0x0000 }, + { 0xc0c9, 0x0000 }, + { 0xc0ca, 0x0000 }, + { 0xc0cb, 0x0000 }, + { 0xc0cc, 0x0000 }, + { 0xc0cd, 0x0000 }, + { 0xc0ce, 0x0000 }, + { 0xc0cf, 0x0000 }, + { 0xc0d0, 0x0000 }, + { 0xc0d1, 0x0000 }, + { 0xc0d2, 0x0000 }, + { 0xc0d3, 0x0000 }, + { 0xc0d4, 0x0000 }, + { 0xc0d5, 0x0000 }, + { 0xc0d6, 0x0000 }, + { 0xc0d7, 0x0000 }, + { 0xc0d8, 0x0000 }, + { 0xc0d9, 0x0000 }, + { 0xc0da, 0x0000 }, + { 0xc0db, 0x0000 }, + { 0xc0dc, 0x0000 }, + { 0xc0dd, 0x0000 }, + { 0xc0de, 0x0000 }, + { 0xc0df, 0x0000 }, + { 0xc0e0, 0x0000 }, + { 0xc0e1, 0x0000 }, + { 0xc0e2, 0x0000 }, + { 0xc0e3, 0x0000 }, + { 0xc0e4, 0x0000 }, + { 0xc0e5, 0x0000 }, + { 0xc0e6, 0x0000 }, + { 0xc0e7, 0x0000 }, + { 0xc0e8, 0x0000 }, + { 0xc0e9, 0x0000 }, + { 0xc0ea, 0x0000 }, + { 0xc0eb, 0x0000 }, + { 0xc0ec, 0x0000 }, + { 0xc0ed, 0x0000 }, + { 0xc0ee, 0x0000 }, + { 0xc0ef, 0x0000 }, + { 0xc0f0, 0x0000 }, + { 0xc0f1, 0x0000 }, + { 0xc0f2, 0x0000 }, + { 0xc0f3, 0x0000 }, + { 0xc0f4, 0x0000 }, + { 0xc0f5, 0x0000 }, + { 0xc0f6, 0x0000 }, + { 0xc0f7, 0x0000 }, + { 0xc0f8, 0x0000 }, + { 0xc0f9, 0x0000 }, + { 0xc0fa, 0x0000 }, + { 0xc0fb, 0x0000 }, + { 0xc0fc, 0x0000 }, + { 0xc0fd, 0x0000 }, + { 0xc0fe, 0x0000 }, + { 0xc0ff, 0x0000 }, + { 0xc100, 0x0000 }, + { 0xc101, 0x0000 }, + { 0xc102, 0x0000 }, + { 0xc103, 0x0000 }, + { 0xc104, 0x0000 }, + { 0xc105, 0x0000 }, + { 0xc106, 0x0000 }, + { 0xc107, 0x0000 }, + { 0xc108, 0x0000 }, + { 0xc109, 0x0000 }, + { 0xc10a, 0x0000 }, + { 0xc10b, 0x0000 }, + { 0xc10c, 0x0000 }, + { 0xc10d, 0x0000 }, + { 0xc10e, 0x0000 }, + { 0xc10f, 0x0000 }, + { 0xc110, 0x0000 }, + { 0xc111, 0x0000 }, + { 0xc112, 0x0000 }, + { 0xc113, 0x0000 }, + { 0xc114, 0x0000 }, + { 0xc115, 0x0000 }, + { 0xc116, 0x0000 }, + { 0xc117, 0x0000 }, + { 0xc118, 0x0000 }, + { 0xc119, 0x0000 }, + { 0xc11a, 0x0000 }, + { 0xc11b, 0x0000 }, + { 0xc11c, 0x0000 }, + { 0xc11d, 0x0000 }, + { 0xc11e, 0x0000 }, + { 0xc11f, 0x0000 }, + { 0xc120, 0x0000 }, + { 0xc121, 0x0000 }, + { 0xc122, 0x0000 }, + { 0xc123, 0x0000 }, + { 0xc124, 0x0000 }, + { 0xc125, 0x0000 }, + { 0xc126, 0x0000 }, + { 0xc127, 0x0000 }, + { 0xc128, 0x0000 }, + { 0xc129, 0x0000 }, + { 0xc12a, 0x0000 }, + { 0xc12b, 0x0000 }, + { 0xc12c, 0x0000 }, + { 0xc12d, 0x0000 }, + { 0xc12e, 0x0000 }, + { 0xc12f, 0x0000 }, + { 0xc130, 0x0000 }, + { 0xc131, 0x0000 }, + { 0xc132, 0x0000 }, + { 0xc133, 0x0000 }, + { 0xc134, 0x0000 }, + { 0xc135, 0x0000 }, + { 0xc136, 0x0000 }, + { 0xc137, 0x0000 }, + { 0xc138, 0x0000 }, + { 0xc139, 0x0000 }, + { 0xc13a, 0x0000 }, + { 0xc13b, 0x0000 }, + { 0xc13c, 0x0000 }, + { 0xc13d, 0x0000 }, + { 0xc13e, 0x0000 }, + { 0xc13f, 0x0000 }, + { 0xc140, 0x0000 }, + { 0xc141, 0x0000 }, + { 0xc142, 0x0000 }, + { 0xc143, 0x0000 }, + { 0xc144, 0x0000 }, + { 0xc145, 0x0000 }, + { 0xc146, 0x0000 }, + { 0xc147, 0x0000 }, + { 0xc148, 0x0000 }, + { 0xc149, 0x0000 }, + { 0xc14a, 0x0000 }, + { 0xc14b, 0x0000 }, + { 0xc14c, 0x0000 }, + { 0xc14d, 0x0000 }, + { 0xc14e, 0x0000 }, + { 0xc14f, 0x0000 }, + { 0xc150, 0x0000 }, + { 0xc151, 0x0000 }, + { 0xc152, 0x0000 }, + { 0xc153, 0x0000 }, + { 0xc154, 0x0000 }, + { 0xc155, 0x0000 }, + { 0xc156, 0x0000 }, + { 0xc157, 0x0000 }, + { 0xc158, 0x0000 }, + { 0xc159, 0x0000 }, + { 0xc15a, 0x0000 }, + { 0xc15b, 0x0000 }, + { 0xc15c, 0x0000 }, + { 0xc15d, 0x0000 }, + { 0xc15e, 0x0000 }, + { 0xc15f, 0x0000 }, + { 0xc160, 0x0000 }, + { 0xc161, 0x0000 }, + { 0xc162, 0x0000 }, + { 0xc163, 0x0000 }, + { 0xc164, 0x0000 }, + { 0xc165, 0x0000 }, + { 0xc166, 0x0000 }, + { 0xc167, 0x0000 }, + { 0xc168, 0x0000 }, + { 0xc169, 0x0000 }, + { 0xc16a, 0x0000 }, + { 0xc16b, 0x0000 }, + { 0xc16c, 0x0000 }, + { 0xc16d, 0x0000 }, + { 0xc16e, 0x0000 }, + { 0xc16f, 0x0000 }, + { 0xc170, 0x0000 }, + { 0xc171, 0x0000 }, + { 0xc172, 0x0000 }, + { 0xc173, 0x0000 }, + { 0xc174, 0x0000 }, + { 0xc175, 0x0000 }, + { 0xc176, 0x0000 }, + { 0xc177, 0x0000 }, + { 0xc178, 0x0000 }, + { 0xc179, 0x0000 }, + { 0xc17a, 0x0000 }, + { 0xc17b, 0x0000 }, + { 0xc17c, 0x0000 }, + { 0xc17d, 0x0000 }, + { 0xc17e, 0x0000 }, + { 0xc17f, 0x0000 }, + { 0xc180, 0x0000 }, + { 0xc181, 0x0000 }, + { 0xc182, 0x0000 }, + { 0xc183, 0x0000 }, + { 0xc184, 0x0000 }, + { 0xc185, 0x0000 }, + { 0xc186, 0x0000 }, + { 0xc187, 0x0000 }, + { 0xc188, 0x0000 }, + { 0xc189, 0x0000 }, + { 0xc18a, 0x0000 }, + { 0xc18b, 0x0000 }, + { 0xc18c, 0x0000 }, + { 0xc18d, 0x0000 }, + { 0xc18e, 0x0000 }, + { 0xc18f, 0x0000 }, + { 0xc190, 0x0000 }, + { 0xc191, 0x0000 }, + { 0xc192, 0x0000 }, + { 0xc193, 0x0000 }, + { 0xc194, 0x0000 }, + { 0xc195, 0x0000 }, + { 0xc196, 0x0000 }, + { 0xc197, 0x0000 }, + { 0xc198, 0x0000 }, + { 0xc199, 0x0000 }, + { 0xc19a, 0x0000 }, + { 0xc19b, 0x0000 }, + { 0xc19c, 0x0000 }, + { 0xc19d, 0x0000 }, + { 0xc19e, 0x0000 }, + { 0xc19f, 0x0000 }, + { 0xc1a0, 0x0000 }, + { 0xc1a1, 0x0000 }, + { 0xc1a2, 0x0000 }, + { 0xc1a3, 0x0000 }, + { 0xc1a4, 0x0000 }, + { 0xc1a5, 0x0000 }, + { 0xc1a6, 0x0000 }, + { 0xc1a7, 0x0000 }, + { 0xc1a8, 0x0000 }, + { 0xc1a9, 0x0000 }, + { 0xc1aa, 0x0000 }, + { 0xc1ab, 0x0000 }, + { 0xc1ac, 0x0000 }, + { 0xc1ad, 0x0000 }, + { 0xc1ae, 0x0000 }, + { 0xc1af, 0x0000 }, + { 0xc1b0, 0x0000 }, + { 0xc1b1, 0x0000 }, + { 0xc1b2, 0x0000 }, + { 0xc1b3, 0x0000 }, + { 0xc1b4, 0x0000 }, + { 0xc1b5, 0x0000 }, + { 0xc1b6, 0x0000 }, + { 0xc1b7, 0x0000 }, + { 0xc1b8, 0x0000 }, + { 0xc1b9, 0x0000 }, + { 0xc1ba, 0x0000 }, + { 0xc1bb, 0x0000 }, + { 0xc1bc, 0x0000 }, + { 0xc1bd, 0x0000 }, + { 0xc1be, 0x0000 }, + { 0xc1bf, 0x0000 }, + { 0xc1c0, 0x0000 }, + { 0xc1c1, 0x0000 }, + { 0xc1c2, 0x0000 }, + { 0xc1c3, 0x0000 }, + { 0xc1c4, 0x0000 }, + { 0xc1c5, 0x0000 }, + { 0xc1c6, 0x0000 }, + { 0xc1c7, 0x0000 }, + { 0xc1c8, 0x0000 }, + { 0xc1c9, 0x0000 }, + { 0xc1ca, 0x0000 }, + { 0xc1cb, 0x0000 }, + { 0xc1cc, 0x0000 }, + { 0xc1cd, 0x0000 }, + { 0xc1ce, 0x0000 }, + { 0xc1cf, 0x0000 }, + { 0xc1d0, 0x0000 }, + { 0xc1d1, 0x0000 }, + { 0xc1d2, 0x0000 }, + { 0xc1d3, 0x0000 }, + { 0xc1d4, 0x0000 }, + { 0xc1d5, 0x0000 }, + { 0xc1d6, 0x0000 }, + { 0xc1d7, 0x0000 }, + { 0xc1d8, 0x0000 }, + { 0xc1d9, 0x0000 }, + { 0xc1da, 0x0000 }, + { 0xc1db, 0x0000 }, + { 0xc1dc, 0x0000 }, + { 0xc1dd, 0x0000 }, + { 0xc1de, 0x0000 }, + { 0xc1df, 0x0000 }, + { 0xc1e0, 0x0000 }, + { 0xc1e1, 0x0000 }, + { 0xc1e2, 0x0000 }, + { 0xc1e3, 0x0000 }, + { 0xc1e4, 0x0000 }, + { 0xc1e5, 0x0000 }, + { 0xc1e6, 0x0000 }, + { 0xc1e7, 0x0000 }, + { 0xc1e8, 0x0000 }, + { 0xc1e9, 0x0000 }, + { 0xc1ea, 0x0000 }, + { 0xc1eb, 0x0000 }, + { 0xc1ec, 0x0000 }, + { 0xc1ed, 0x0000 }, + { 0xc1ee, 0x0000 }, + { 0xc1ef, 0x0000 }, + { 0xc1f0, 0x0000 }, + { 0xc1f1, 0x0000 }, + { 0xc1f2, 0x0000 }, + { 0xc1f3, 0x0000 }, + { 0xc1f4, 0x0000 }, + { 0xc1f5, 0x0000 }, + { 0xc1f6, 0x0000 }, + { 0xc1f7, 0x0000 }, + { 0xc1f8, 0x0000 }, + { 0xc1f9, 0x0000 }, + { 0xc1fa, 0x0000 }, + { 0xc1fb, 0x0000 }, + { 0xc1fc, 0x0000 }, + { 0xc1fd, 0x0000 }, + { 0xc1fe, 0x0000 }, + { 0xc1ff, 0x0000 }, + { 0xc200, 0x0000 }, + { 0xc201, 0x0000 }, + { 0xc202, 0x0000 }, + { 0xc203, 0x0000 }, + { 0xc204, 0x0000 }, + { 0xc205, 0x0000 }, + { 0xc206, 0x0000 }, + { 0xc207, 0x0000 }, + { 0xc208, 0x0000 }, + { 0xc209, 0x0000 }, + { 0xc20a, 0x0000 }, + { 0xc20b, 0x0000 }, + { 0xc20c, 0x0000 }, + { 0xc20d, 0x0000 }, + { 0xc20e, 0x0000 }, + { 0xc20f, 0x0000 }, + { 0xc210, 0x0000 }, + { 0xc211, 0x0000 }, + { 0xc212, 0x0000 }, + { 0xc213, 0x0000 }, + { 0xc214, 0x0000 }, + { 0xc215, 0x0000 }, + { 0xc216, 0x0000 }, + { 0xc217, 0x0000 }, + { 0xc218, 0x0000 }, + { 0xc219, 0x0000 }, + { 0xc21a, 0x0000 }, + { 0xc21b, 0x0000 }, + { 0xc21c, 0x0000 }, + { 0xc21d, 0x0000 }, + { 0xc21e, 0x0000 }, + { 0xc21f, 0x0000 }, + { 0xc220, 0x0000 }, + { 0xc221, 0x0000 }, + { 0xc222, 0x0000 }, + { 0xc223, 0x0000 }, + { 0xc224, 0x0000 }, + { 0xc225, 0x0000 }, + { 0xc226, 0x0000 }, + { 0xc227, 0x0000 }, + { 0xc228, 0x0000 }, + { 0xc229, 0x0000 }, + { 0xc22a, 0x0000 }, + { 0xc22b, 0x0000 }, + { 0xc22c, 0x0000 }, + { 0xc22d, 0x0000 }, + { 0xc22e, 0x0000 }, + { 0xc22f, 0x0000 }, + { 0xc230, 0x0000 }, + { 0xc231, 0x0000 }, + { 0xc232, 0x0000 }, + { 0xc233, 0x0000 }, + { 0xc234, 0x0000 }, + { 0xc235, 0x0000 }, + { 0xc236, 0x0000 }, + { 0xc237, 0x0000 }, + { 0xc238, 0x0000 }, + { 0xc239, 0x0000 }, + { 0xc23a, 0x0000 }, + { 0xc23b, 0x0000 }, + { 0xc23c, 0x0000 }, + { 0xc23d, 0x0000 }, + { 0xc23e, 0x0000 }, + { 0xc23f, 0x0000 }, + { 0xc240, 0x0000 }, + { 0xc241, 0x0000 }, + { 0xc242, 0x0000 }, + { 0xc243, 0x0000 }, + { 0xc244, 0x0000 }, + { 0xc245, 0x0000 }, + { 0xc246, 0x0000 }, + { 0xc247, 0x0000 }, + { 0xc248, 0x0000 }, + { 0xc249, 0x0000 }, + { 0xc24a, 0x0000 }, + { 0xc24b, 0x0000 }, + { 0xc24c, 0x0000 }, + { 0xc24d, 0x0000 }, + { 0xc24e, 0x0000 }, + { 0xc24f, 0x0000 }, + { 0xc250, 0x0000 }, + { 0xc251, 0x0000 }, + { 0xc252, 0x0000 }, + { 0xc253, 0x0000 }, + { 0xc254, 0x0000 }, + { 0xc255, 0x0000 }, + { 0xc256, 0x0000 }, + { 0xc257, 0x0000 }, + { 0xc258, 0x0000 }, + { 0xc259, 0x0000 }, + { 0xc25a, 0x0000 }, + { 0xc25b, 0x0000 }, + { 0xc25c, 0x0000 }, + { 0xc25d, 0x0000 }, + { 0xc25e, 0x0000 }, + { 0xc25f, 0x0000 }, + { 0xc260, 0x0000 }, + { 0xc261, 0x0000 }, + { 0xc262, 0x0000 }, + { 0xc263, 0x0000 }, + { 0xc264, 0x0000 }, + { 0xc265, 0x0000 }, + { 0xc266, 0x0000 }, + { 0xc267, 0x0000 }, + { 0xc268, 0x0000 }, + { 0xc269, 0x0000 }, + { 0xc26a, 0x0000 }, + { 0xc26b, 0x0000 }, + { 0xc26c, 0x0000 }, + { 0xc26d, 0x0000 }, + { 0xc26e, 0x0000 }, + { 0xc26f, 0x0000 }, + { 0xc270, 0x0000 }, + { 0xc271, 0x0000 }, + { 0xc272, 0x0000 }, + { 0xc273, 0x0000 }, + { 0xc274, 0x0000 }, + { 0xc275, 0x0000 }, + { 0xc276, 0x0000 }, + { 0xc277, 0x0000 }, + { 0xc278, 0x0000 }, + { 0xc279, 0x0000 }, + { 0xc27a, 0x0000 }, + { 0xc27b, 0x0000 }, + { 0xc27c, 0x0000 }, + { 0xc27d, 0x0000 }, + { 0xc27e, 0x0000 }, + { 0xc27f, 0x0000 }, + { 0xc280, 0x0000 }, + { 0xc281, 0x0000 }, + { 0xc282, 0x0000 }, + { 0xc283, 0x0000 }, + { 0xc284, 0x0000 }, + { 0xc285, 0x0000 }, + { 0xc286, 0x0000 }, + { 0xc287, 0x0000 }, + { 0xc288, 0x0000 }, + { 0xc289, 0x0000 }, + { 0xc28a, 0x0000 }, + { 0xc28b, 0x0000 }, + { 0xc28c, 0x0000 }, + { 0xc28d, 0x0000 }, + { 0xc28e, 0x0000 }, + { 0xc28f, 0x0000 }, + { 0xc290, 0x0000 }, + { 0xc291, 0x0000 }, + { 0xc292, 0x0000 }, + { 0xc293, 0x0000 }, + { 0xc294, 0x0000 }, + { 0xc295, 0x0000 }, + { 0xc296, 0x0000 }, + { 0xc297, 0x0000 }, + { 0xc298, 0x0000 }, + { 0xc299, 0x0000 }, + { 0xc29a, 0x0000 }, + { 0xc29b, 0x0000 }, + { 0xc29c, 0x0000 }, + { 0xc29d, 0x0000 }, + { 0xc29e, 0x0000 }, + { 0xc29f, 0x0000 }, + { 0xc2a0, 0x0000 }, + { 0xc2a1, 0x0000 }, + { 0xc2a2, 0x0000 }, + { 0xc2a3, 0x0000 }, + { 0xc2a4, 0x0000 }, + { 0xc2a5, 0x0000 }, + { 0xc2a6, 0x0000 }, + { 0xc2a7, 0x0000 }, + { 0xc2a8, 0x0000 }, + { 0xc2a9, 0x0000 }, + { 0xc2aa, 0x0000 }, + { 0xc2ab, 0x0000 }, + { 0xc2ac, 0x0000 }, + { 0xc2ad, 0x0000 }, + { 0xc2ae, 0x0000 }, + { 0xc2af, 0x0000 }, + { 0xc2b0, 0x0000 }, + { 0xc2b1, 0x0000 }, + { 0xc2b2, 0x0000 }, + { 0xc2b3, 0x0000 }, + { 0xc2b4, 0x0000 }, + { 0xc2b5, 0x0000 }, + { 0xc2b6, 0x0000 }, + { 0xc2b7, 0x0000 }, + { 0xc2b8, 0x0000 }, + { 0xc2b9, 0x0000 }, + { 0xc2ba, 0x0000 }, + { 0xc2bb, 0x0000 }, + { 0xc2bc, 0x0000 }, + { 0xc2bd, 0x0000 }, + { 0xc2be, 0x0000 }, + { 0xc2bf, 0x0000 }, + { 0xc2c0, 0x0000 }, + { 0xc2c1, 0x0000 }, + { 0xc2c2, 0x0000 }, + { 0xc2c3, 0x0000 }, + { 0xc2c4, 0x0000 }, + { 0xc2c5, 0x0000 }, + { 0xc2c6, 0x0000 }, + { 0xc2c7, 0x0000 }, + { 0xc2c8, 0x0000 }, + { 0xc2c9, 0x0000 }, + { 0xc2ca, 0x0000 }, + { 0xc2cb, 0x0000 }, + { 0xc2cc, 0x0000 }, + { 0xc2cd, 0x0000 }, + { 0xc2ce, 0x0000 }, + { 0xc2cf, 0x0000 }, + { 0xc2d0, 0x0000 }, + { 0xc2d1, 0x0000 }, + { 0xc2d2, 0x0000 }, + { 0xc2d3, 0x0000 }, + { 0xc2d4, 0x0000 }, + { 0xc2d5, 0x0000 }, + { 0xc2d6, 0x0000 }, + { 0xc2d7, 0x0000 }, + { 0xc2d8, 0x0000 }, + { 0xc2d9, 0x0000 }, + { 0xc2da, 0x0000 }, + { 0xc2db, 0x0000 }, + { 0xc2dc, 0x0000 }, + { 0xc2dd, 0x0000 }, + { 0xc2de, 0x0000 }, + { 0xc2df, 0x0000 }, + { 0xc2e0, 0x0000 }, + { 0xc2e1, 0x0000 }, + { 0xc2e2, 0x0000 }, + { 0xc2e3, 0x0000 }, + { 0xc2e4, 0x0000 }, + { 0xc2e5, 0x0000 }, + { 0xc2e6, 0x0000 }, + { 0xc2e7, 0x0000 }, + { 0xc2e8, 0x0000 }, + { 0xc2e9, 0x0000 }, + { 0xc2ea, 0x0000 }, + { 0xc2eb, 0x0000 }, + { 0xc2ec, 0x0000 }, + { 0xc2ed, 0x0000 }, + { 0xc2ee, 0x0000 }, + { 0xc2ef, 0x0000 }, + { 0xc2f0, 0x0000 }, + { 0xc2f1, 0x0000 }, + { 0xc2f2, 0x0000 }, + { 0xc2f3, 0x0000 }, + { 0xc2f4, 0x0000 }, + { 0xc2f5, 0x0000 }, + { 0xc2f6, 0x0000 }, + { 0xc2f7, 0x0000 }, + { 0xc2f8, 0x0000 }, + { 0xc2f9, 0x0000 }, + { 0xc2fa, 0x0000 }, + { 0xc2fb, 0x0000 }, + { 0xc2fc, 0x0000 }, + { 0xc2fd, 0x0000 }, + { 0xc2fe, 0x0000 }, + { 0xc2ff, 0x0000 }, + { 0xc300, 0x0000 }, + { 0xc301, 0x0000 }, + { 0xc302, 0x0000 }, + { 0xc303, 0x0000 }, + { 0xc304, 0x0000 }, + { 0xc305, 0x0000 }, + { 0xc306, 0x0000 }, + { 0xc307, 0x0000 }, + { 0xc308, 0x0000 }, + { 0xc309, 0x0000 }, + { 0xc30a, 0x0000 }, + { 0xc30b, 0x0000 }, + { 0xc30c, 0x0000 }, + { 0xc30d, 0x0000 }, + { 0xc30e, 0x0000 }, + { 0xc30f, 0x0000 }, + { 0xc310, 0x0000 }, + { 0xc311, 0x0000 }, + { 0xc312, 0x0000 }, + { 0xc313, 0x0000 }, + { 0xc314, 0x0000 }, + { 0xc315, 0x0000 }, + { 0xc316, 0x0000 }, + { 0xc317, 0x0000 }, + { 0xc318, 0x0000 }, + { 0xc319, 0x0000 }, + { 0xc31a, 0x0000 }, + { 0xc31b, 0x0000 }, + { 0xc31c, 0x0000 }, + { 0xc31d, 0x0000 }, + { 0xc31e, 0x0000 }, + { 0xc31f, 0x0000 }, + { 0xc320, 0x0000 }, + { 0xc321, 0x0000 }, + { 0xc322, 0x0000 }, + { 0xc323, 0x0000 }, + { 0xc324, 0x0000 }, + { 0xc325, 0x0000 }, + { 0xc326, 0x0000 }, + { 0xc327, 0x0000 }, + { 0xc328, 0x0000 }, + { 0xc329, 0x0000 }, + { 0xc32a, 0x0000 }, + { 0xc32b, 0x0000 }, + { 0xc32c, 0x0000 }, + { 0xc32d, 0x0000 }, + { 0xc32e, 0x0000 }, + { 0xc32f, 0x0000 }, + { 0xc330, 0x0000 }, + { 0xc331, 0x0000 }, + { 0xc332, 0x0000 }, + { 0xc333, 0x0000 }, + { 0xc334, 0x0000 }, + { 0xc335, 0x0000 }, + { 0xc336, 0x0000 }, + { 0xc337, 0x0000 }, + { 0xc338, 0x0000 }, + { 0xc339, 0x0000 }, + { 0xc33a, 0x0000 }, + { 0xc33b, 0x0000 }, + { 0xc33c, 0x0000 }, + { 0xc33d, 0x0000 }, + { 0xc33e, 0x0000 }, + { 0xc33f, 0x0000 }, + { 0xc340, 0x0000 }, + { 0xc341, 0x0000 }, + { 0xc342, 0x0000 }, + { 0xc343, 0x0000 }, + { 0xc344, 0x0000 }, + { 0xc345, 0x0000 }, + { 0xc346, 0x0000 }, + { 0xc347, 0x0000 }, + { 0xc348, 0x0000 }, + { 0xc349, 0x0000 }, + { 0xc34a, 0x0000 }, + { 0xc34b, 0x0000 }, + { 0xc34c, 0x0000 }, + { 0xc34d, 0x0000 }, + { 0xc34e, 0x0000 }, + { 0xc34f, 0x0000 }, + { 0xc350, 0x0000 }, + { 0xc351, 0x0000 }, + { 0xc352, 0x0000 }, + { 0xc353, 0x0000 }, + { 0xc354, 0x0000 }, + { 0xc355, 0x0000 }, + { 0xc356, 0x0000 }, + { 0xc357, 0x0000 }, + { 0xc358, 0x0000 }, + { 0xc359, 0x0000 }, + { 0xc35a, 0x0000 }, + { 0xc35b, 0x0000 }, + { 0xc35c, 0x0000 }, + { 0xc35d, 0x0000 }, + { 0xc35e, 0x0000 }, + { 0xc35f, 0x0000 }, + { 0xc360, 0x0000 }, + { 0xc361, 0x0000 }, + { 0xc362, 0x0000 }, + { 0xc363, 0x0000 }, + { 0xc364, 0x0000 }, + { 0xc365, 0x0000 }, + { 0xc366, 0x0000 }, + { 0xc367, 0x0000 }, + { 0xc368, 0x0000 }, + { 0xc369, 0x0000 }, + { 0xc36a, 0x0000 }, + { 0xc36b, 0x0000 }, + { 0xc36c, 0x0000 }, + { 0xc36d, 0x0000 }, + { 0xc36e, 0x0000 }, + { 0xc36f, 0x0000 }, + { 0xc370, 0x0000 }, + { 0xc371, 0x0000 }, + { 0xc372, 0x0000 }, + { 0xc373, 0x0000 }, + { 0xc374, 0x0000 }, + { 0xc375, 0x0000 }, + { 0xc376, 0x0000 }, + { 0xc377, 0x0000 }, + { 0xc378, 0x0000 }, + { 0xc379, 0x0000 }, + { 0xc37a, 0x0000 }, + { 0xc37b, 0x0000 }, + { 0xc37c, 0x0000 }, + { 0xc37d, 0x0000 }, + { 0xc37e, 0x0000 }, + { 0xc37f, 0x0000 }, + { 0xc380, 0x0000 }, + { 0xc381, 0x0000 }, + { 0xc382, 0x0000 }, + { 0xc383, 0x0000 }, + { 0xc384, 0x0000 }, + { 0xc385, 0x0000 }, + { 0xc386, 0x0000 }, + { 0xc387, 0x0000 }, + { 0xc388, 0x0000 }, + { 0xc389, 0x0000 }, + { 0xc38a, 0x0000 }, + { 0xc38b, 0x0000 }, + { 0xc38c, 0x0000 }, + { 0xc38d, 0x0000 }, + { 0xc38e, 0x0000 }, + { 0xc38f, 0x0000 }, + { 0xc390, 0x0000 }, + { 0xc391, 0x0000 }, + { 0xc392, 0x0000 }, + { 0xc393, 0x0000 }, + { 0xc394, 0x0000 }, + { 0xc395, 0x0000 }, + { 0xc396, 0x0000 }, + { 0xc397, 0x0000 }, + { 0xc398, 0x0000 }, + { 0xc399, 0x0000 }, + { 0xc39a, 0x0000 }, + { 0xc39b, 0x0000 }, + { 0xc39c, 0x0000 }, + { 0xc39d, 0x0000 }, + { 0xc39e, 0x0000 }, + { 0xc39f, 0x0000 }, + { 0xc3a0, 0x0000 }, + { 0xc3a1, 0x0000 }, + { 0xc3a2, 0x0000 }, + { 0xc3a3, 0x0000 }, + { 0xc3a4, 0x0000 }, + { 0xc3a5, 0x0000 }, + { 0xc3a6, 0x0000 }, + { 0xc3a7, 0x0000 }, + { 0xc3a8, 0x0000 }, + { 0xc3a9, 0x0000 }, + { 0xc3aa, 0x0000 }, + { 0xc3ab, 0x0000 }, + { 0xc3ac, 0x0000 }, + { 0xc3ad, 0x0000 }, + { 0xc3ae, 0x0000 }, + { 0xc3af, 0x0000 }, + { 0xc3b0, 0x0000 }, + { 0xc3b1, 0x0000 }, + { 0xc3b2, 0x0000 }, + { 0xc3b3, 0x0000 }, + { 0xc3b4, 0x0000 }, + { 0xc3b5, 0x0000 }, + { 0xc3b6, 0x0000 }, + { 0xc3b7, 0x0000 }, + { 0xc3b8, 0x0000 }, + { 0xc3b9, 0x0000 }, + { 0xc3ba, 0x0000 }, + { 0xc3bb, 0x0000 }, + { 0xc3bc, 0x0000 }, + { 0xc3bd, 0x0000 }, + { 0xc3be, 0x0000 }, + { 0xc3bf, 0x0000 }, + { 0xc3c0, 0x0000 }, + { 0xc3c1, 0x0000 }, + { 0xc3c2, 0x0000 }, + { 0xc3c3, 0x0000 }, + { 0xc3c4, 0x0000 }, + { 0xc3c5, 0x0000 }, + { 0xc3c6, 0x0000 }, + { 0xc3c7, 0x0000 }, + { 0xc3c8, 0x0000 }, + { 0xc3c9, 0x0000 }, + { 0xc3ca, 0x0000 }, + { 0xc3cb, 0x0000 }, + { 0xc3cc, 0x0000 }, + { 0xc3cd, 0x0000 }, + { 0xc3ce, 0x0000 }, + { 0xc3cf, 0x0000 }, + { 0xc3d0, 0x0000 }, + { 0xc3d1, 0x0000 }, + { 0xc3d2, 0x0000 }, + { 0xc3d3, 0x0000 }, + { 0xc3d4, 0x0000 }, + { 0xc3d5, 0x0000 }, + { 0xc3d6, 0x0000 }, + { 0xc3d7, 0x0000 }, + { 0xc3d8, 0x0000 }, + { 0xc3d9, 0x0000 }, + { 0xc3da, 0x0000 }, + { 0xc3db, 0x0000 }, + { 0xc3dc, 0x0000 }, + { 0xc3dd, 0x0000 }, + { 0xc3de, 0x0000 }, + { 0xc3df, 0x0000 }, + { 0xc3e0, 0x0000 }, + { 0xc3e1, 0x0000 }, + { 0xc3e2, 0x0000 }, + { 0xc3e3, 0x0000 }, + { 0xc3e4, 0x0000 }, + { 0xc3e5, 0x0000 }, + { 0xc3e6, 0x0000 }, + { 0xc3e7, 0x0000 }, + { 0xc3e8, 0x0000 }, + { 0xc3e9, 0x0000 }, + { 0xc3ea, 0x0000 }, + { 0xc3eb, 0x0000 }, + { 0xc3ec, 0x0000 }, + { 0xc3ed, 0x0000 }, + { 0xc3ee, 0x0000 }, + { 0xc3ef, 0x0000 }, + { 0xc3f0, 0x0000 }, + { 0xc3f1, 0x0000 }, + { 0xc3f2, 0x0000 }, + { 0xc3f3, 0x0000 }, + { 0xc3f4, 0x0000 }, + { 0xc3f5, 0x0000 }, + { 0xc3f6, 0x0000 }, + { 0xc3f7, 0x0000 }, + { 0xc3f8, 0x0000 }, + { 0xc3f9, 0x0000 }, + { 0xc3fa, 0x0000 }, + { 0xc3fb, 0x0000 }, + { 0xc3fc, 0x0000 }, + { 0xc3fd, 0x0000 }, + { 0xc3fe, 0x0000 }, + { 0xc3ff, 0x0000 }, + { 0xc400, 0x0000 }, + { 0xc401, 0x0000 }, + { 0xc402, 0x0000 }, + { 0xc403, 0x0000 }, + { 0xc404, 0x0000 }, + { 0xc405, 0x0000 }, + { 0xc406, 0x0000 }, + { 0xc407, 0x0000 }, + { 0xc408, 0x0000 }, + { 0xc409, 0x0000 }, + { 0xc40a, 0x0000 }, + { 0xc40b, 0x0000 }, + { 0xc40c, 0x0000 }, + { 0xc40d, 0x0000 }, + { 0xc40e, 0x0000 }, + { 0xc40f, 0x0000 }, + { 0xc410, 0x0000 }, + { 0xc411, 0x0000 }, + { 0xc412, 0x0000 }, + { 0xc413, 0x0000 }, + { 0xc414, 0x0000 }, + { 0xc415, 0x0000 }, + { 0xc416, 0x0000 }, + { 0xc417, 0x0000 }, + { 0xc418, 0x0000 }, + { 0xc419, 0x0000 }, + { 0xc41a, 0x0000 }, + { 0xc41b, 0x0000 }, + { 0xc41c, 0x0000 }, + { 0xc41d, 0x0000 }, + { 0xc41e, 0x0000 }, + { 0xc41f, 0x0000 }, + { 0xc420, 0x0000 }, + { 0xc421, 0x0000 }, + { 0xc422, 0x0000 }, + { 0xc423, 0x0000 }, + { 0xc424, 0x0000 }, + { 0xc425, 0x0000 }, + { 0xc426, 0x0000 }, + { 0xc427, 0x0000 }, + { 0xc428, 0x0000 }, + { 0xc429, 0x0000 }, + { 0xc42a, 0x0000 }, + { 0xc42b, 0x0000 }, + { 0xc42c, 0x0000 }, + { 0xc42d, 0x0000 }, + { 0xc42e, 0x0000 }, + { 0xc42f, 0x0000 }, + { 0xc430, 0x0000 }, + { 0xc431, 0x0000 }, + { 0xc432, 0x0000 }, + { 0xc433, 0x0000 }, + { 0xc434, 0x0000 }, + { 0xc435, 0x0000 }, + { 0xc436, 0x0000 }, + { 0xc437, 0x0000 }, + { 0xc438, 0x0000 }, + { 0xc439, 0x0000 }, + { 0xc43a, 0x0000 }, + { 0xc43b, 0x0000 }, + { 0xc43c, 0x0000 }, + { 0xc43d, 0x0000 }, + { 0xc43e, 0x0000 }, + { 0xc43f, 0x0000 }, + { 0xc440, 0x0000 }, + { 0xc441, 0x0000 }, + { 0xc442, 0x0000 }, + { 0xc443, 0x0000 }, + { 0xc444, 0x0000 }, + { 0xc445, 0x0000 }, + { 0xc446, 0x0000 }, + { 0xc447, 0x0000 }, + { 0xc448, 0x0000 }, + { 0xc449, 0x0000 }, + { 0xc44a, 0x0000 }, + { 0xc44b, 0x0000 }, + { 0xc44c, 0x0000 }, + { 0xc44d, 0x0000 }, + { 0xc44e, 0x0000 }, + { 0xc44f, 0x0000 }, + { 0xc450, 0x0000 }, + { 0xc451, 0x0000 }, + { 0xc452, 0x0000 }, + { 0xc453, 0x0000 }, + { 0xc454, 0x0000 }, + { 0xc455, 0x0000 }, + { 0xc456, 0x0000 }, + { 0xc457, 0x0000 }, + { 0xc458, 0x0000 }, + { 0xc459, 0x0000 }, + { 0xc45a, 0x0000 }, + { 0xc45b, 0x0000 }, + { 0xc45c, 0x0000 }, + { 0xc45d, 0x0000 }, + { 0xc45e, 0x0000 }, + { 0xc45f, 0x0000 }, + { 0xc460, 0x0000 }, + { 0xc461, 0x0000 }, + { 0xc462, 0x0000 }, + { 0xc463, 0x0000 }, + { 0xc464, 0x0000 }, + { 0xc465, 0x0000 }, + { 0xc466, 0x0000 }, + { 0xc467, 0x0000 }, + { 0xc468, 0x0000 }, + { 0xc469, 0x0000 }, + { 0xc46a, 0x0000 }, + { 0xc46b, 0x0000 }, + { 0xc46c, 0x0000 }, + { 0xc46d, 0x0000 }, + { 0xc46e, 0x0000 }, + { 0xc46f, 0x0000 }, + { 0xc470, 0x0000 }, + { 0xc471, 0x0000 }, + { 0xc472, 0x0000 }, + { 0xc473, 0x0000 }, + { 0xc474, 0x0000 }, + { 0xc475, 0x0000 }, + { 0xc476, 0x0000 }, + { 0xc477, 0x0000 }, + { 0xc478, 0x0000 }, + { 0xc479, 0x0000 }, + { 0xc47a, 0x0000 }, + { 0xc47b, 0x0000 }, + { 0xc47c, 0x0000 }, + { 0xc47d, 0x0000 }, + { 0xc47e, 0x0000 }, + { 0xc47f, 0x0000 }, + { 0xc480, 0x0000 }, + { 0xc481, 0x0000 }, + { 0xc482, 0x0000 }, + { 0xc483, 0x0000 }, + { 0xc484, 0x0000 }, + { 0xc485, 0x0000 }, + { 0xc486, 0x0000 }, + { 0xc487, 0x0000 }, + { 0xc488, 0x0000 }, + { 0xc489, 0x0000 }, + { 0xc48a, 0x0000 }, + { 0xc48b, 0x0000 }, + { 0xc48c, 0x0000 }, + { 0xc48d, 0x0000 }, + { 0xc48e, 0x0000 }, + { 0xc48f, 0x0000 }, + { 0xc490, 0x0000 }, + { 0xc491, 0x0000 }, + { 0xc492, 0x0000 }, + { 0xc493, 0x0000 }, + { 0xc494, 0x0000 }, + { 0xc495, 0x0000 }, + { 0xc496, 0x0000 }, + { 0xc497, 0x0000 }, + { 0xc498, 0x0000 }, + { 0xc499, 0x0000 }, + { 0xc49a, 0x0000 }, + { 0xc49b, 0x0000 }, + { 0xc49c, 0x0000 }, + { 0xc49d, 0x0000 }, + { 0xc49e, 0x0000 }, + { 0xc49f, 0x0000 }, + { 0xc4a0, 0x0000 }, + { 0xc4a1, 0x0000 }, + { 0xc4a2, 0x0000 }, + { 0xc4a3, 0x0000 }, + { 0xc4a4, 0x0000 }, + { 0xc4a5, 0x0000 }, + { 0xc4a6, 0x0000 }, + { 0xc4a7, 0x0000 }, + { 0xc4a8, 0x0000 }, + { 0xc4a9, 0x0000 }, + { 0xc4aa, 0x0000 }, + { 0xc4ab, 0x0000 }, + { 0xc4ac, 0x0000 }, + { 0xc4ad, 0x0000 }, + { 0xc4ae, 0x0000 }, + { 0xc4af, 0x0000 }, + { 0xc4b0, 0x0000 }, + { 0xc4b1, 0x0000 }, + { 0xc4b2, 0x0000 }, + { 0xc4b3, 0x0000 }, + { 0xc4b4, 0x0000 }, + { 0xc4b5, 0x0000 }, + { 0xc4b6, 0x0000 }, + { 0xc4b7, 0x0000 }, + { 0xc4b8, 0x0000 }, + { 0xc4b9, 0x0000 }, + { 0xc4ba, 0x0000 }, + { 0xc4bb, 0x0000 }, + { 0xc4bc, 0x0000 }, + { 0xc4bd, 0x0000 }, + { 0xc4be, 0x0000 }, + { 0xc4bf, 0x0000 }, + { 0xc4c0, 0x0000 }, + { 0xc4c1, 0x0000 }, + { 0xc4c2, 0x0000 }, + { 0xc4c3, 0x0000 }, + { 0xc4c4, 0x0000 }, + { 0xc4c5, 0x0000 }, + { 0xc4c6, 0x0000 }, + { 0xc4c7, 0x0000 }, + { 0xc4c8, 0x0000 }, + { 0xc4c9, 0x0000 }, + { 0xc4ca, 0x0000 }, + { 0xc4cb, 0x0000 }, + { 0xc4cc, 0x0000 }, + { 0xc4cd, 0x0000 }, + { 0xc4ce, 0x0000 }, + { 0xc4cf, 0x0000 }, + { 0xc4d0, 0x0000 }, + { 0xc4d1, 0x0000 }, + { 0xc4d2, 0x0000 }, + { 0xc4d3, 0x0000 }, + { 0xc4d4, 0x0000 }, + { 0xc4d5, 0x0000 }, + { 0xc4d6, 0x0000 }, + { 0xc4d7, 0x0000 }, + { 0xc4d8, 0x0000 }, + { 0xc4d9, 0x0000 }, + { 0xc4da, 0x0000 }, + { 0xc4db, 0x0000 }, + { 0xc4dc, 0x0000 }, + { 0xc4dd, 0x0000 }, + { 0xc4de, 0x0000 }, + { 0xc4df, 0x0000 }, + { 0xc4e0, 0x0000 }, + { 0xc4e1, 0x0000 }, + { 0xc4e2, 0x0000 }, + { 0xc4e3, 0x0000 }, + { 0xc4e4, 0x0000 }, + { 0xc4e5, 0x0000 }, + { 0xc4e6, 0x0000 }, + { 0xc4e7, 0x0000 }, + { 0xc4e8, 0x0000 }, + { 0xc4e9, 0x0000 }, + { 0xc4ea, 0x0000 }, + { 0xc4eb, 0x0000 }, + { 0xc4ec, 0x0000 }, + { 0xc4ed, 0x0000 }, + { 0xc4ee, 0x0000 }, + { 0xc4ef, 0x0000 }, + { 0xc4f0, 0x0000 }, + { 0xc4f1, 0x0000 }, + { 0xc4f2, 0x0000 }, + { 0xc4f3, 0x0000 }, + { 0xc4f4, 0x0000 }, + { 0xc4f5, 0x0000 }, + { 0xc4f6, 0x0000 }, + { 0xc4f7, 0x0000 }, + { 0xc4f8, 0x0000 }, + { 0xc4f9, 0x0000 }, + { 0xc4fa, 0x0000 }, + { 0xc4fb, 0x0000 }, + { 0xc4fc, 0x0000 }, + { 0xc4fd, 0x0000 }, + { 0xc4fe, 0x0000 }, + { 0xc4ff, 0x0000 }, + { 0xc500, 0x0000 }, + { 0xc501, 0x0000 }, + { 0xc502, 0x0000 }, + { 0xc503, 0x0000 }, + { 0xc504, 0x0000 }, + { 0xc505, 0x0000 }, + { 0xc506, 0x0000 }, + { 0xc507, 0x0000 }, + { 0xc508, 0x0000 }, + { 0xc509, 0x0000 }, + { 0xc50a, 0x0000 }, + { 0xc50b, 0x0000 }, + { 0xc50c, 0x0000 }, + { 0xc50d, 0x0000 }, + { 0xc50e, 0x0000 }, + { 0xc50f, 0x0000 }, + { 0xc510, 0x0000 }, + { 0xc511, 0x0000 }, + { 0xc512, 0x0000 }, + { 0xc513, 0x0000 }, + { 0xc514, 0x0000 }, + { 0xc515, 0x0000 }, + { 0xc516, 0x0000 }, + { 0xc517, 0x0000 }, + { 0xc518, 0x0000 }, + { 0xc519, 0x0000 }, + { 0xc51a, 0x0000 }, + { 0xc51b, 0x0000 }, + { 0xc51c, 0x0000 }, + { 0xc51d, 0x0000 }, + { 0xc51e, 0x0000 }, + { 0xc51f, 0x0000 }, + { 0xc520, 0x0000 }, + { 0xc521, 0x0000 }, + { 0xc522, 0x0000 }, + { 0xc523, 0x0000 }, + { 0xc524, 0x0000 }, + { 0xc525, 0x0000 }, + { 0xc526, 0x0000 }, + { 0xc527, 0x0000 }, + { 0xc528, 0x0000 }, + { 0xc529, 0x0000 }, + { 0xc52a, 0x0000 }, + { 0xc52b, 0x0000 }, + { 0xc52c, 0x0000 }, + { 0xc52d, 0x0000 }, + { 0xc52e, 0x0000 }, + { 0xc52f, 0x0000 }, + { 0xc530, 0x0000 }, + { 0xc531, 0x0000 }, + { 0xc532, 0x0000 }, + { 0xc533, 0x0000 }, + { 0xc534, 0x0000 }, + { 0xc535, 0x0000 }, + { 0xc536, 0x0000 }, + { 0xc537, 0x0000 }, + { 0xc538, 0x0000 }, + { 0xc539, 0x0000 }, + { 0xc53a, 0x0000 }, + { 0xc53b, 0x0000 }, + { 0xc53c, 0x0000 }, + { 0xc53d, 0x0000 }, + { 0xc53e, 0x0000 }, + { 0xc53f, 0x0000 }, + { 0xc540, 0x0000 }, + { 0xc541, 0x0000 }, + { 0xc542, 0x0000 }, + { 0xc543, 0x0000 }, + { 0xc544, 0x0000 }, + { 0xc545, 0x0000 }, + { 0xc546, 0x0000 }, + { 0xc547, 0x0000 }, + { 0xc548, 0x0000 }, + { 0xc549, 0x0000 }, + { 0xc54a, 0x0000 }, + { 0xc54b, 0x0000 }, + { 0xc54c, 0x0000 }, + { 0xc54d, 0x0000 }, + { 0xc54e, 0x0000 }, + { 0xc54f, 0x0000 }, + { 0xc550, 0x0000 }, + { 0xc551, 0x0000 }, + { 0xc552, 0x0000 }, + { 0xc553, 0x0000 }, + { 0xc554, 0x0000 }, + { 0xc555, 0x0000 }, + { 0xc556, 0x0000 }, + { 0xc557, 0x0000 }, + { 0xc558, 0x0000 }, + { 0xc559, 0x0000 }, + { 0xc55a, 0x0000 }, + { 0xc55b, 0x0000 }, + { 0xc55c, 0x0000 }, + { 0xc55d, 0x0000 }, + { 0xc55e, 0x0000 }, + { 0xc55f, 0x0000 }, + { 0xc560, 0x0000 }, + { 0xc561, 0x0000 }, + { 0xc562, 0x0000 }, + { 0xc563, 0x0000 }, + { 0xc564, 0x0000 }, + { 0xc565, 0x0000 }, + { 0xc566, 0x0000 }, + { 0xc567, 0x0000 }, + { 0xc568, 0x0000 }, + { 0xc569, 0x0000 }, + { 0xc56a, 0x0000 }, + { 0xc56b, 0x0000 }, + { 0xc56c, 0x0000 }, + { 0xc56d, 0x0000 }, + { 0xc56e, 0x0000 }, + { 0xc56f, 0x0000 }, + { 0xc570, 0x0000 }, + { 0xc571, 0x0000 }, + { 0xc572, 0x0000 }, + { 0xc573, 0x0000 }, + { 0xc574, 0x0000 }, + { 0xc575, 0x0000 }, + { 0xc576, 0x0000 }, + { 0xc577, 0x0000 }, + { 0xc578, 0x0000 }, + { 0xc579, 0x0000 }, + { 0xc57a, 0x0000 }, + { 0xc57b, 0x0000 }, + { 0xc57c, 0x0000 }, + { 0xc57d, 0x0000 }, + { 0xc57e, 0x0000 }, + { 0xc57f, 0x0000 }, + { 0xc580, 0x0000 }, + { 0xc581, 0x0000 }, + { 0xc582, 0x0000 }, + { 0xc583, 0x0000 }, + { 0xc584, 0x0000 }, + { 0xc585, 0x0000 }, + { 0xc586, 0x0000 }, + { 0xc587, 0x0000 }, + { 0xc588, 0x0000 }, + { 0xc589, 0x0000 }, + { 0xc58a, 0x0000 }, + { 0xc58b, 0x0000 }, + { 0xc58c, 0x0000 }, + { 0xc58d, 0x0000 }, + { 0xc58e, 0x0000 }, + { 0xc58f, 0x0000 }, + { 0xc590, 0x0000 }, + { 0xc591, 0x0000 }, + { 0xc592, 0x0000 }, + { 0xc593, 0x0000 }, + { 0xc594, 0x0000 }, + { 0xc595, 0x0000 }, + { 0xc596, 0x0000 }, + { 0xc597, 0x0000 }, + { 0xc598, 0x0000 }, + { 0xc599, 0x0000 }, + { 0xc59a, 0x0000 }, + { 0xc59b, 0x0000 }, + { 0xc59c, 0x0000 }, + { 0xc59d, 0x0000 }, + { 0xc59e, 0x0000 }, + { 0xc59f, 0x0000 }, + { 0xc5a0, 0x0000 }, + { 0xc5a1, 0x0000 }, + { 0xc5a2, 0x0000 }, + { 0xc5a3, 0x0000 }, + { 0xc5a4, 0x0000 }, + { 0xc5a5, 0x0000 }, + { 0xc5a6, 0x0000 }, + { 0xc5a7, 0x0000 }, + { 0xc5a8, 0x0000 }, + { 0xc5a9, 0x0000 }, + { 0xc5aa, 0x0000 }, + { 0xc5ab, 0x0000 }, + { 0xc5ac, 0x0000 }, + { 0xc5ad, 0x0000 }, + { 0xc5ae, 0x0000 }, + { 0xc5af, 0x0000 }, + { 0xc5b0, 0x0000 }, + { 0xc5b1, 0x0000 }, + { 0xc5b2, 0x0000 }, + { 0xc5b3, 0x0000 }, + { 0xc5b4, 0x0000 }, + { 0xc5b5, 0x0000 }, + { 0xc5b6, 0x0000 }, + { 0xc5b7, 0x0000 }, + { 0xc5b8, 0x0000 }, + { 0xc5b9, 0x0000 }, + { 0xc5ba, 0x0000 }, + { 0xc5bb, 0x0000 }, + { 0xc5bc, 0x0000 }, + { 0xc5bd, 0x0000 }, + { 0xc5be, 0x0000 }, + { 0xc5bf, 0x0000 }, + { 0xc5c0, 0x0000 }, + { 0xc5c1, 0x0000 }, + { 0xc5c2, 0x0000 }, + { 0xc5c3, 0x0000 }, + { 0xc5c4, 0x0000 }, + { 0xc5c5, 0x0000 }, + { 0xc5c6, 0x0000 }, + { 0xc5c7, 0x0000 }, + { 0xc5c8, 0x0000 }, + { 0xc5c9, 0x0000 }, + { 0xc5ca, 0x0000 }, + { 0xc5cb, 0x0000 }, + { 0xc5cc, 0x0000 }, + { 0xc5cd, 0x0000 }, + { 0xc5ce, 0x0000 }, + { 0xc5cf, 0x0000 }, + { 0xc5d0, 0x0000 }, + { 0xc5d1, 0x0000 }, + { 0xc5d2, 0x0000 }, + { 0xc5d3, 0x0000 }, + { 0xc5d4, 0x0000 }, + { 0xc5d5, 0x0000 }, + { 0xc5d6, 0x0000 }, + { 0xc5d7, 0x0000 }, + { 0xc5d8, 0x0000 }, + { 0xc5d9, 0x0000 }, + { 0xc5da, 0x0000 }, + { 0xc5db, 0x0000 }, + { 0xc5dc, 0x0000 }, + { 0xc5dd, 0x0000 }, + { 0xc5de, 0x0000 }, + { 0xc5df, 0x0000 }, + { 0xc5e0, 0x0000 }, + { 0xc5e1, 0x0000 }, + { 0xc5e2, 0x0000 }, + { 0xc5e3, 0x0000 }, + { 0xc5e4, 0x0000 }, + { 0xc5e5, 0x0000 }, + { 0xc5e6, 0x0000 }, + { 0xc5e7, 0x0000 }, + { 0xc5e8, 0x0000 }, + { 0xc5e9, 0x0000 }, + { 0xc5ea, 0x0000 }, + { 0xc5eb, 0x0000 }, + { 0xc5ec, 0x0000 }, + { 0xc5ed, 0x0000 }, + { 0xc5ee, 0x0000 }, + { 0xc5ef, 0x0000 }, + { 0xc5f0, 0x0000 }, + { 0xc5f1, 0x0000 }, + { 0xc5f2, 0x0000 }, + { 0xc5f3, 0x0000 }, + { 0xc5f4, 0x0000 }, + { 0xc5f5, 0x0000 }, + { 0xc5f6, 0x0000 }, + { 0xc5f7, 0x0000 }, + { 0xc5f8, 0x0000 }, + { 0xc5f9, 0x0000 }, + { 0xc5fa, 0x0000 }, + { 0xc5fb, 0x0000 }, + { 0xc5fc, 0x0000 }, + { 0xc5fd, 0x0000 }, + { 0xc5fe, 0x0000 }, + { 0xc5ff, 0x0000 }, + { 0xc600, 0x0000 }, + { 0xc601, 0x0000 }, + { 0xc602, 0x0000 }, + { 0xc603, 0x0000 }, + { 0xc604, 0x0000 }, + { 0xc605, 0x0000 }, + { 0xc606, 0x0000 }, + { 0xc607, 0x0000 }, + { 0xc608, 0x0000 }, + { 0xc609, 0x0000 }, + { 0xc60a, 0x0000 }, + { 0xc60b, 0x0000 }, + { 0xc60c, 0x0000 }, + { 0xc60d, 0x0000 }, + { 0xc60e, 0x0000 }, + { 0xc60f, 0x0000 }, + { 0xc610, 0x0000 }, + { 0xc611, 0x0000 }, + { 0xc612, 0x0000 }, + { 0xc613, 0x0000 }, + { 0xc614, 0x0000 }, + { 0xc615, 0x0000 }, + { 0xc616, 0x0000 }, + { 0xc617, 0x0000 }, + { 0xc618, 0x0000 }, + { 0xc619, 0x0000 }, + { 0xc61a, 0x0000 }, + { 0xc61b, 0x0000 }, + { 0xc61c, 0x0000 }, + { 0xc61d, 0x0000 }, + { 0xc61e, 0x0000 }, + { 0xc61f, 0x0000 }, + { 0xc620, 0x0000 }, + { 0xc621, 0x0000 }, + { 0xc622, 0x0000 }, + { 0xc623, 0x0000 }, + { 0xc624, 0x0000 }, + { 0xc625, 0x0000 }, + { 0xc626, 0x0000 }, + { 0xc627, 0x0000 }, + { 0xc628, 0x0000 }, + { 0xc629, 0x0000 }, + { 0xc62a, 0x0000 }, + { 0xc62b, 0x0000 }, + { 0xc62c, 0x0000 }, + { 0xc62d, 0x0000 }, + { 0xc62e, 0x0000 }, + { 0xc62f, 0x0000 }, + { 0xc630, 0x0000 }, + { 0xc631, 0x0000 }, + { 0xc632, 0x0000 }, + { 0xc633, 0x0000 }, + { 0xc634, 0x0000 }, + { 0xc635, 0x0000 }, + { 0xc636, 0x0000 }, + { 0xc637, 0x0000 }, + { 0xc638, 0x0000 }, + { 0xc639, 0x0000 }, + { 0xc63a, 0x0000 }, + { 0xc63b, 0x0000 }, + { 0xc63c, 0x0000 }, + { 0xc63d, 0x0000 }, + { 0xc63e, 0x0000 }, + { 0xc63f, 0x0000 }, + { 0xc640, 0x0000 }, + { 0xc641, 0x0000 }, + { 0xc642, 0x0000 }, + { 0xc643, 0x0000 }, + { 0xc644, 0x0000 }, + { 0xc645, 0x0000 }, + { 0xc646, 0x0000 }, + { 0xc647, 0x0000 }, + { 0xc648, 0x0000 }, + { 0xc649, 0x0000 }, + { 0xc64a, 0x0000 }, + { 0xc64b, 0x0000 }, + { 0xc64c, 0x0000 }, + { 0xc64d, 0x0000 }, + { 0xc64e, 0x0000 }, + { 0xc64f, 0x0000 }, + { 0xc650, 0x0000 }, + { 0xc651, 0x0000 }, + { 0xc652, 0x0000 }, + { 0xc653, 0x0000 }, + { 0xc654, 0x0000 }, + { 0xc655, 0x0000 }, + { 0xc656, 0x0000 }, + { 0xc657, 0x0000 }, + { 0xc658, 0x0000 }, + { 0xc659, 0x0000 }, + { 0xc65a, 0x0000 }, + { 0xc65b, 0x0000 }, + { 0xc65c, 0x0000 }, + { 0xc65d, 0x0000 }, + { 0xc65e, 0x0000 }, + { 0xc65f, 0x0000 }, + { 0xc660, 0x0000 }, + { 0xc661, 0x0000 }, + { 0xc662, 0x0000 }, + { 0xc663, 0x0000 }, + { 0xc664, 0x0000 }, + { 0xc665, 0x0000 }, + { 0xc666, 0x0000 }, + { 0xc667, 0x0000 }, + { 0xc668, 0x0000 }, + { 0xc669, 0x0000 }, + { 0xc66a, 0x0000 }, + { 0xc66b, 0x0000 }, + { 0xc66c, 0x0000 }, + { 0xc66d, 0x0000 }, + { 0xc66e, 0x0000 }, + { 0xc66f, 0x0000 }, + { 0xc670, 0x0000 }, + { 0xc671, 0x0000 }, + { 0xc672, 0x0000 }, + { 0xc673, 0x0000 }, + { 0xc674, 0x0000 }, + { 0xc675, 0x0000 }, + { 0xc676, 0x0000 }, + { 0xc677, 0x0000 }, + { 0xc678, 0x0000 }, + { 0xc679, 0x0000 }, + { 0xc67a, 0x0000 }, + { 0xc67b, 0x0000 }, + { 0xc67c, 0x0000 }, + { 0xc67d, 0x0000 }, + { 0xc67e, 0x0000 }, + { 0xc67f, 0x0000 }, + { 0xc680, 0x0000 }, + { 0xc681, 0x0000 }, + { 0xc682, 0x0000 }, + { 0xc683, 0x0000 }, + { 0xc684, 0x0000 }, + { 0xc685, 0x0000 }, + { 0xc686, 0x0000 }, + { 0xc687, 0x0000 }, + { 0xc688, 0x0000 }, + { 0xc689, 0x0000 }, + { 0xc68a, 0x0000 }, + { 0xc68b, 0x0000 }, + { 0xc68c, 0x0000 }, + { 0xc68d, 0x0000 }, + { 0xc68e, 0x0000 }, + { 0xc68f, 0x0000 }, + { 0xc690, 0x0000 }, + { 0xc691, 0x0000 }, + { 0xc692, 0x0000 }, + { 0xc693, 0x0000 }, + { 0xc694, 0x0000 }, + { 0xc695, 0x0000 }, + { 0xc696, 0x0000 }, + { 0xc697, 0x0000 }, + { 0xc698, 0x0000 }, + { 0xc699, 0x0000 }, + { 0xc69a, 0x0000 }, + { 0xc69b, 0x0000 }, + { 0xc69c, 0x0000 }, + { 0xc69d, 0x0000 }, + { 0xc69e, 0x0000 }, + { 0xc69f, 0x0000 }, + { 0xc6a0, 0x0000 }, + { 0xc6a1, 0x0000 }, + { 0xc6a2, 0x0000 }, + { 0xc6a3, 0x0000 }, + { 0xc6a4, 0x0000 }, + { 0xc6a5, 0x0000 }, + { 0xc6a6, 0x0000 }, + { 0xc6a7, 0x0000 }, + { 0xc6a8, 0x0000 }, + { 0xc6a9, 0x0000 }, + { 0xc6aa, 0x0000 }, + { 0xc6ab, 0x0000 }, + { 0xc6ac, 0x0000 }, + { 0xc6ad, 0x0000 }, + { 0xc6ae, 0x0000 }, + { 0xc6af, 0x0000 }, + { 0xc6b0, 0x0000 }, + { 0xc6b1, 0x0000 }, + { 0xc6b2, 0x0000 }, + { 0xc6b3, 0x0000 }, + { 0xc6b4, 0x0000 }, + { 0xc6b5, 0x0000 }, + { 0xc6b6, 0x0000 }, + { 0xc6b7, 0x0000 }, + { 0xc6b8, 0x0000 }, + { 0xc6b9, 0x0000 }, + { 0xc6ba, 0x0000 }, + { 0xc6bb, 0x0000 }, + { 0xc6bc, 0x0000 }, + { 0xc6bd, 0x0000 }, + { 0xc6be, 0x0000 }, + { 0xc6bf, 0x0000 }, + { 0xc6c0, 0x0000 }, + { 0xc6c1, 0x0000 }, + { 0xc6c2, 0x0000 }, + { 0xc6c3, 0x0000 }, + { 0xc6c4, 0x0000 }, + { 0xc6c5, 0x0000 }, + { 0xc6c6, 0x0000 }, + { 0xc6c7, 0x0000 }, + { 0xc6c8, 0x0000 }, + { 0xc6c9, 0x0000 }, + { 0xc6ca, 0x0000 }, + { 0xc6cb, 0x0000 }, + { 0xc6cc, 0x0000 }, + { 0xc6cd, 0x0000 }, + { 0xc6ce, 0x0000 }, + { 0xc6cf, 0x0000 }, + { 0xc6d0, 0x0000 }, + { 0xc6d1, 0x0000 }, + { 0xc6d2, 0x0000 }, + { 0xc6d3, 0x0000 }, + { 0xc6d4, 0x0000 }, + { 0xc6d5, 0x0000 }, + { 0xc6d6, 0x0000 }, + { 0xc6d7, 0x0000 }, + { 0xc6d8, 0x0000 }, + { 0xc6d9, 0x0000 }, + { 0xc6da, 0x0000 }, + { 0xc6db, 0x0000 }, + { 0xc6dc, 0x0000 }, + { 0xc6dd, 0x0000 }, + { 0xc6de, 0x0000 }, + { 0xc6df, 0x0000 }, + { 0xc6e0, 0x0000 }, + { 0xc6e1, 0x0000 }, + { 0xc6e2, 0x0000 }, + { 0xc6e3, 0x0000 }, + { 0xc6e4, 0x0000 }, + { 0xc6e5, 0x0000 }, + { 0xc6e6, 0x0000 }, + { 0xc6e7, 0x0000 }, + { 0xc6e8, 0x0000 }, + { 0xc6e9, 0x0000 }, + { 0xc6ea, 0x0000 }, + { 0xc6eb, 0x0000 }, + { 0xc6ec, 0x0000 }, + { 0xc6ed, 0x0000 }, + { 0xc6ee, 0x0000 }, + { 0xc6ef, 0x0000 }, + { 0xc6f0, 0x0000 }, + { 0xc6f1, 0x0000 }, + { 0xc6f2, 0x0000 }, + { 0xc6f3, 0x0000 }, + { 0xc6f4, 0x0000 }, + { 0xc6f5, 0x0000 }, + { 0xc6f6, 0x0000 }, + { 0xc6f7, 0x0000 }, + { 0xc6f8, 0x0000 }, + { 0xc6f9, 0x0000 }, + { 0xc6fa, 0x0000 }, + { 0xc6fb, 0x0000 }, + { 0xc6fc, 0x0000 }, + { 0xc6fd, 0x0000 }, + { 0xc6fe, 0x0000 }, + { 0xc6ff, 0x0000 }, + { 0xc700, 0x0000 }, + { 0xc701, 0x0000 }, + { 0xc702, 0x0000 }, + { 0xc703, 0x0000 }, + { 0xc704, 0x0000 }, + { 0xc705, 0x0000 }, + { 0xc706, 0x0000 }, + { 0xc707, 0x0000 }, + { 0xc708, 0x0000 }, + { 0xc709, 0x0000 }, + { 0xc70a, 0x0000 }, + { 0xc70b, 0x0000 }, + { 0xc70c, 0x0000 }, + { 0xc70d, 0x0000 }, + { 0xc70e, 0x0000 }, + { 0xc70f, 0x0000 }, + { 0xc710, 0x0000 }, + { 0xc711, 0x0000 }, + { 0xc712, 0x0000 }, + { 0xc713, 0x0000 }, + { 0xc714, 0x0000 }, + { 0xc715, 0x0000 }, + { 0xc716, 0x0000 }, + { 0xc717, 0x0000 }, + { 0xc718, 0x0000 }, + { 0xc719, 0x0000 }, + { 0xc71a, 0x0000 }, + { 0xc71b, 0x0000 }, + { 0xc71c, 0x0000 }, + { 0xc71d, 0x0000 }, + { 0xc71e, 0x0000 }, + { 0xc71f, 0x0000 }, + { 0xc720, 0x0000 }, + { 0xc721, 0x0000 }, + { 0xc722, 0x0000 }, + { 0xc723, 0x0000 }, + { 0xc724, 0x0000 }, + { 0xc725, 0x0000 }, + { 0xc726, 0x0000 }, + { 0xc727, 0x0000 }, + { 0xc728, 0x0000 }, + { 0xc729, 0x0000 }, + { 0xc72a, 0x0000 }, + { 0xc72b, 0x0000 }, + { 0xc72c, 0x0000 }, + { 0xc72d, 0x0000 }, + { 0xc72e, 0x0000 }, + { 0xc72f, 0x0000 }, + { 0xc730, 0x0000 }, + { 0xc731, 0x0000 }, + { 0xc732, 0x0000 }, + { 0xc733, 0x0000 }, + { 0xc734, 0x0000 }, + { 0xc735, 0x0000 }, + { 0xc736, 0x0000 }, + { 0xc737, 0x0000 }, + { 0xc738, 0x0000 }, + { 0xc739, 0x0000 }, + { 0xc73a, 0x0000 }, + { 0xc73b, 0x0000 }, + { 0xc73c, 0x0000 }, + { 0xc73d, 0x0000 }, + { 0xc73e, 0x0000 }, + { 0xc73f, 0x0000 }, + { 0xc740, 0x0000 }, + { 0xc741, 0x0000 }, + { 0xc742, 0x0000 }, + { 0xc743, 0x0000 }, + { 0xc744, 0x0000 }, + { 0xc745, 0x0000 }, + { 0xc746, 0x0000 }, + { 0xc747, 0x0000 }, + { 0xc748, 0x0000 }, + { 0xc749, 0x0000 }, + { 0xc74a, 0x0000 }, + { 0xc74b, 0x0000 }, + { 0xc74c, 0x0000 }, + { 0xc74d, 0x0000 }, + { 0xc74e, 0x0000 }, + { 0xc74f, 0x0000 }, + { 0xc750, 0x0000 }, + { 0xc751, 0x0000 }, + { 0xc752, 0x0000 }, + { 0xc753, 0x0000 }, + { 0xc754, 0x0000 }, + { 0xc755, 0x0000 }, + { 0xc756, 0x0000 }, + { 0xc757, 0x0000 }, + { 0xc758, 0x0000 }, + { 0xc759, 0x0000 }, + { 0xc75a, 0x0000 }, + { 0xc75b, 0x0000 }, + { 0xc75c, 0x0000 }, + { 0xc75d, 0x0000 }, + { 0xc75e, 0x0000 }, + { 0xc75f, 0x0000 }, + { 0xc760, 0x0000 }, + { 0xc761, 0x0000 }, + { 0xc762, 0x0000 }, + { 0xc763, 0x0000 }, + { 0xc764, 0x0000 }, + { 0xc765, 0x0000 }, + { 0xc766, 0x0000 }, + { 0xc767, 0x0000 }, + { 0xc768, 0x0000 }, + { 0xc769, 0x0000 }, + { 0xc76a, 0x0000 }, + { 0xc76b, 0x0000 }, + { 0xc76c, 0x0000 }, + { 0xc76d, 0x0000 }, + { 0xc76e, 0x0000 }, + { 0xc76f, 0x0000 }, + { 0xc770, 0x0000 }, + { 0xc771, 0x0000 }, + { 0xc772, 0x0000 }, + { 0xc773, 0x0000 }, + { 0xc774, 0x0000 }, + { 0xc775, 0x0000 }, + { 0xc776, 0x0000 }, + { 0xc777, 0x0000 }, + { 0xc778, 0x0000 }, + { 0xc779, 0x0000 }, + { 0xc77a, 0x0000 }, + { 0xc77b, 0x0000 }, + { 0xc77c, 0x0000 }, + { 0xc77d, 0x0000 }, + { 0xc77e, 0x0000 }, + { 0xc77f, 0x0000 }, + { 0xc780, 0x0000 }, + { 0xc781, 0x0000 }, + { 0xc782, 0x0000 }, + { 0xc783, 0x0000 }, + { 0xc784, 0x0000 }, + { 0xc785, 0x0000 }, + { 0xc786, 0x0000 }, + { 0xc787, 0x0000 }, + { 0xc788, 0x0000 }, + { 0xc789, 0x0000 }, + { 0xc78a, 0x0000 }, + { 0xc78b, 0x0000 }, + { 0xc78c, 0x0000 }, + { 0xc78d, 0x0000 }, + { 0xc78e, 0x0000 }, + { 0xc78f, 0x0000 }, + { 0xc790, 0x0000 }, + { 0xc791, 0x0000 }, + { 0xc792, 0x0000 }, + { 0xc793, 0x0000 }, + { 0xc794, 0x0000 }, + { 0xc795, 0x0000 }, + { 0xc796, 0x0000 }, + { 0xc797, 0x0000 }, + { 0xc798, 0x0000 }, + { 0xc799, 0x0000 }, + { 0xc79a, 0x0000 }, + { 0xc79b, 0x0000 }, + { 0xc79c, 0x0000 }, + { 0xc79d, 0x0000 }, + { 0xc79e, 0x0000 }, + { 0xc79f, 0x0000 }, + { 0xc7a0, 0x0000 }, + { 0xc7a1, 0x0000 }, + { 0xc7a2, 0x0000 }, + { 0xc7a3, 0x0000 }, + { 0xc7a4, 0x0000 }, + { 0xc7a5, 0x0000 }, + { 0xc7a6, 0x0000 }, + { 0xc7a7, 0x0000 }, + { 0xc7a8, 0x0000 }, + { 0xc7a9, 0x0000 }, + { 0xc7aa, 0x0000 }, + { 0xc7ab, 0x0000 }, + { 0xc7ac, 0x0000 }, + { 0xc7ad, 0x0000 }, + { 0xc7ae, 0x0000 }, + { 0xc7af, 0x0000 }, + { 0xc7b0, 0x0000 }, + { 0xc7b1, 0x0000 }, + { 0xc7b2, 0x0000 }, + { 0xc7b3, 0x0000 }, + { 0xc7b4, 0x0000 }, + { 0xc7b5, 0x0000 }, + { 0xc7b6, 0x0000 }, + { 0xc7b7, 0x0000 }, + { 0xc7b8, 0x0000 }, + { 0xc7b9, 0x0000 }, + { 0xc7ba, 0x0000 }, + { 0xc7bb, 0x0000 }, + { 0xc7bc, 0x0000 }, + { 0xc7bd, 0x0000 }, + { 0xc7be, 0x0000 }, + { 0xc7bf, 0x0000 }, + { 0xc7c0, 0x0000 }, + { 0xc7c1, 0x0000 }, + { 0xc7c2, 0x0000 }, + { 0xc7c3, 0x0000 }, + { 0xc7c4, 0x0000 }, + { 0xc7c5, 0x0000 }, + { 0xc7c6, 0x0000 }, + { 0xc7c7, 0x0000 }, + { 0xc7c8, 0x0000 }, + { 0xc7c9, 0x0000 }, + { 0xc7ca, 0x0000 }, + { 0xc7cb, 0x0000 }, + { 0xc7cc, 0x0000 }, + { 0xc7cd, 0x0000 }, + { 0xc7ce, 0x0000 }, + { 0xc7cf, 0x0000 }, + { 0xc7d0, 0x0000 }, + { 0xc7d1, 0x0000 }, + { 0xc7d2, 0x0000 }, + { 0xc7d3, 0x0000 }, + { 0xc7d4, 0x0000 }, + { 0xc7d5, 0x0000 }, + { 0xc7d6, 0x0000 }, + { 0xc7d7, 0x0000 }, + { 0xc7d8, 0x0000 }, + { 0xc7d9, 0x0000 }, + { 0xc7da, 0x0000 }, + { 0xc7db, 0x0000 }, + { 0xc7dc, 0x0000 }, + { 0xc7dd, 0x0000 }, + { 0xc7de, 0x0000 }, + { 0xc7df, 0x0000 }, + { 0xc7e0, 0x0000 }, + { 0xc7e1, 0x0000 }, + { 0xc7e2, 0x0000 }, + { 0xc7e3, 0x0000 }, + { 0xc7e4, 0x0000 }, + { 0xc7e5, 0x0000 }, + { 0xc7e6, 0x0000 }, + { 0xc7e7, 0x0000 }, + { 0xc7e8, 0x0000 }, + { 0xc7e9, 0x0000 }, + { 0xc7ea, 0x0000 }, + { 0xc7eb, 0x0000 }, + { 0xc7ec, 0x0000 }, + { 0xc7ed, 0x0000 }, + { 0xc7ee, 0x0000 }, + { 0xc7ef, 0x0000 }, + { 0xc7f0, 0x0000 }, + { 0xc7f1, 0x0000 }, + { 0xc7f2, 0x0000 }, + { 0xc7f3, 0x0000 }, + { 0xc7f4, 0x0000 }, + { 0xc7f5, 0x0000 }, + { 0xc7f6, 0x0000 }, + { 0xc7f7, 0x0000 }, + { 0xc7f8, 0x0000 }, + { 0xc7f9, 0x0000 }, + { 0xc7fa, 0x0000 }, + { 0xc7fb, 0x0000 }, + { 0xc7fc, 0x0000 }, + { 0xc7fd, 0x0000 }, + { 0xc7fe, 0x0000 }, + { 0xc7ff, 0x0000 }, + { 0xc800, 0x0000 }, + { 0xc801, 0x0000 }, + { 0xc802, 0x0000 }, + { 0xc803, 0x0000 }, + { 0xc804, 0x0000 }, + { 0xc805, 0x0000 }, + { 0xc806, 0x0000 }, + { 0xc807, 0x0000 }, + { 0xc808, 0x0000 }, + { 0xc809, 0x0000 }, + { 0xc80a, 0x0000 }, + { 0xc80b, 0x0000 }, + { 0xc80c, 0x0000 }, + { 0xc80d, 0x0000 }, + { 0xc80e, 0x0000 }, + { 0xc80f, 0x0000 }, + { 0xc810, 0x0000 }, + { 0xc811, 0x0000 }, + { 0xc812, 0x0000 }, + { 0xc813, 0x0000 }, + { 0xc814, 0x0000 }, + { 0xc815, 0x0000 }, + { 0xc816, 0x0000 }, + { 0xc817, 0x0000 }, + { 0xc818, 0x0000 }, + { 0xc819, 0x0000 }, + { 0xc81a, 0x0000 }, + { 0xc81b, 0x0000 }, + { 0xc81c, 0x0000 }, + { 0xc81d, 0x0000 }, + { 0xc81e, 0x0000 }, + { 0xc81f, 0x0000 }, + { 0xc820, 0x0000 }, + { 0xc821, 0x0000 }, + { 0xc822, 0x0000 }, + { 0xc823, 0x0000 }, + { 0xc824, 0x0000 }, + { 0xc825, 0x0000 }, + { 0xc826, 0x0000 }, + { 0xc827, 0x0000 }, + { 0xc828, 0x0000 }, + { 0xc829, 0x0000 }, + { 0xc82a, 0x0000 }, + { 0xc82b, 0x0000 }, + { 0xc82c, 0x0000 }, + { 0xc82d, 0x0000 }, + { 0xc82e, 0x0000 }, + { 0xc82f, 0x0000 }, + { 0xc830, 0x0000 }, + { 0xc831, 0x0000 }, + { 0xc832, 0x0000 }, + { 0xc833, 0x0000 }, + { 0xc834, 0x0000 }, + { 0xc835, 0x0000 }, + { 0xc836, 0x0000 }, + { 0xc837, 0x0000 }, + { 0xc838, 0x0000 }, + { 0xc839, 0x0000 }, + { 0xc83a, 0x0000 }, + { 0xc83b, 0x0000 }, + { 0xc83c, 0x0000 }, + { 0xc83d, 0x0000 }, + { 0xc83e, 0x0000 }, + { 0xc83f, 0x0000 }, + { 0xc840, 0x0000 }, + { 0xc841, 0x0000 }, + { 0xc842, 0x0000 }, + { 0xc843, 0x0000 }, + { 0xc844, 0x0000 }, + { 0xc845, 0x0000 }, + { 0xc846, 0x0000 }, + { 0xc847, 0x0000 }, + { 0xc848, 0x0000 }, + { 0xc849, 0x0000 }, + { 0xc84a, 0x0000 }, + { 0xc84b, 0x0000 }, + { 0xc84c, 0x0000 }, + { 0xc84d, 0x0000 }, + { 0xc84e, 0x0000 }, + { 0xc84f, 0x0000 }, + { 0xc850, 0x0000 }, + { 0xc851, 0x0000 }, + { 0xc852, 0x0000 }, + { 0xc853, 0x0000 }, + { 0xc854, 0x0000 }, + { 0xc855, 0x0000 }, + { 0xc856, 0x0000 }, + { 0xc857, 0x0000 }, + { 0xc858, 0x0000 }, + { 0xc859, 0x0000 }, + { 0xc85a, 0x0000 }, + { 0xc85b, 0x0000 }, + { 0xc85c, 0x0000 }, + { 0xc85d, 0x0000 }, + { 0xc85e, 0x0000 }, + { 0xc85f, 0x0000 }, + { 0xc860, 0x0000 }, + { 0xc861, 0x0000 }, + { 0xc862, 0x0000 }, + { 0xc863, 0x0000 }, + { 0xc864, 0x0000 }, + { 0xc865, 0x0000 }, + { 0xc866, 0x0000 }, + { 0xc867, 0x0000 }, + { 0xc868, 0x0000 }, + { 0xc869, 0x0000 }, + { 0xc86a, 0x0000 }, + { 0xc86b, 0x0000 }, + { 0xc86c, 0x0000 }, + { 0xc86d, 0x0000 }, + { 0xc86e, 0x0000 }, + { 0xc86f, 0x0000 }, + { 0xc870, 0x0000 }, + { 0xc871, 0x0000 }, + { 0xc872, 0x0000 }, + { 0xc873, 0x0000 }, + { 0xc874, 0x0000 }, + { 0xc875, 0x0000 }, + { 0xc876, 0x0000 }, + { 0xc877, 0x0000 }, + { 0xc878, 0x0000 }, + { 0xc879, 0x0000 }, + { 0xc87a, 0x0000 }, + { 0xc87b, 0x0000 }, + { 0xc87c, 0x0000 }, + { 0xc87d, 0x0000 }, + { 0xc87e, 0x0000 }, + { 0xc87f, 0x0000 }, + { 0xc880, 0x0000 }, + { 0xc881, 0x0000 }, + { 0xc882, 0x0000 }, + { 0xc883, 0x0000 }, + { 0xc884, 0x0000 }, + { 0xc885, 0x0000 }, + { 0xc886, 0x0000 }, + { 0xc887, 0x0000 }, + { 0xc888, 0x0000 }, + { 0xc889, 0x0000 }, + { 0xc88a, 0x0000 }, + { 0xc88b, 0x0000 }, + { 0xc88c, 0x0000 }, + { 0xc88d, 0x0000 }, + { 0xc88e, 0x0000 }, + { 0xc88f, 0x0000 }, + { 0xc890, 0x0000 }, + { 0xc891, 0x0000 }, + { 0xc892, 0x0000 }, + { 0xc893, 0x0000 }, + { 0xc894, 0x0000 }, + { 0xc895, 0x0000 }, + { 0xc896, 0x0000 }, + { 0xc897, 0x0000 }, + { 0xc898, 0x0000 }, + { 0xc899, 0x0000 }, + { 0xc89a, 0x0000 }, + { 0xc89b, 0x0000 }, + { 0xc89c, 0x0000 }, + { 0xc89d, 0x0000 }, + { 0xc89e, 0x0000 }, + { 0xc89f, 0x0000 }, + { 0xc8a0, 0x0000 }, + { 0xc8a1, 0x0000 }, + { 0xc8a2, 0x0000 }, + { 0xc8a3, 0x0000 }, + { 0xc8a4, 0x0000 }, + { 0xc8a5, 0x0000 }, + { 0xc8a6, 0x0000 }, + { 0xc8a7, 0x0000 }, + { 0xc8a8, 0x0000 }, + { 0xc8a9, 0x0000 }, + { 0xc8aa, 0x0000 }, + { 0xc8ab, 0x0000 }, + { 0xc8ac, 0x0000 }, + { 0xc8ad, 0x0000 }, + { 0xc8ae, 0x0000 }, + { 0xc8af, 0x0000 }, + { 0xc8b0, 0x0000 }, + { 0xc8b1, 0x0000 }, + { 0xc8b2, 0x0000 }, + { 0xc8b3, 0x0000 }, + { 0xc8b4, 0x0000 }, + { 0xc8b5, 0x0000 }, + { 0xc8b6, 0x0000 }, + { 0xc8b7, 0x0000 }, + { 0xc8b8, 0x0000 }, + { 0xc8b9, 0x0000 }, + { 0xc8ba, 0x0000 }, + { 0xc8bb, 0x0000 }, + { 0xc8bc, 0x0000 }, + { 0xc8bd, 0x0000 }, + { 0xc8be, 0x0000 }, + { 0xc8bf, 0x0000 }, + { 0xc8c0, 0x0000 }, + { 0xc8c1, 0x0000 }, + { 0xc8c2, 0x0000 }, + { 0xc8c3, 0x0000 }, + { 0xc8c4, 0x0000 }, + { 0xc8c5, 0x0000 }, + { 0xc8c6, 0x0000 }, + { 0xc8c7, 0x0000 }, + { 0xc8c8, 0x0000 }, + { 0xc8c9, 0x0000 }, + { 0xc8ca, 0x0000 }, + { 0xc8cb, 0x0000 }, + { 0xc8cc, 0x0000 }, + { 0xc8cd, 0x0000 }, + { 0xc8ce, 0x0000 }, + { 0xc8cf, 0x0000 }, + { 0xc8d0, 0x0000 }, + { 0xc8d1, 0x0000 }, + { 0xc8d2, 0x0000 }, + { 0xc8d3, 0x0000 }, + { 0xc8d4, 0x0000 }, + { 0xc8d5, 0x0000 }, + { 0xc8d6, 0x0000 }, + { 0xc8d7, 0x0000 }, + { 0xc8d8, 0x0000 }, + { 0xc8d9, 0x0000 }, + { 0xc8da, 0x0000 }, + { 0xc8db, 0x0000 }, + { 0xc8dc, 0x0000 }, + { 0xc8dd, 0x0000 }, + { 0xc8de, 0x0000 }, + { 0xc8df, 0x0000 }, + { 0xc8e0, 0x0000 }, + { 0xc8e1, 0x0000 }, + { 0xc8e2, 0x0000 }, + { 0xc8e3, 0x0000 }, + { 0xc8e4, 0x0000 }, + { 0xc8e5, 0x0000 }, + { 0xc8e6, 0x0000 }, + { 0xc8e7, 0x0000 }, + { 0xc8e8, 0x0000 }, + { 0xc8e9, 0x0000 }, + { 0xc8ea, 0x0000 }, + { 0xc8eb, 0x0000 }, + { 0xc8ec, 0x0000 }, + { 0xc8ed, 0x0000 }, + { 0xc8ee, 0x0000 }, + { 0xc8ef, 0x0000 }, + { 0xc8f0, 0x0000 }, + { 0xc8f1, 0x0000 }, + { 0xc8f2, 0x0000 }, + { 0xc8f3, 0x0000 }, + { 0xc8f4, 0x0000 }, + { 0xc8f5, 0x0000 }, + { 0xc8f6, 0x0000 }, + { 0xc8f7, 0x0000 }, + { 0xc8f8, 0x0000 }, + { 0xc8f9, 0x0000 }, + { 0xc8fa, 0x0000 }, + { 0xc8fb, 0x0000 }, + { 0xc8fc, 0x0000 }, + { 0xc8fd, 0x0000 }, + { 0xc8fe, 0x0000 }, + { 0xc8ff, 0x0000 }, + { 0xc900, 0x0000 }, + { 0xc901, 0x0000 }, + { 0xc902, 0x0000 }, + { 0xc903, 0x0000 }, + { 0xc904, 0x0000 }, + { 0xc905, 0x0000 }, + { 0xc906, 0x0000 }, + { 0xc907, 0x0000 }, + { 0xc908, 0x0000 }, + { 0xc909, 0x0000 }, + { 0xc90a, 0x0000 }, + { 0xc90b, 0x0000 }, + { 0xc90c, 0x0000 }, + { 0xc90d, 0x0000 }, + { 0xc90e, 0x0000 }, + { 0xc90f, 0x0000 }, + { 0xc910, 0x0000 }, + { 0xc911, 0x0000 }, + { 0xc912, 0x0000 }, + { 0xc913, 0x0000 }, + { 0xc914, 0x0000 }, + { 0xc915, 0x0000 }, + { 0xc916, 0x0000 }, + { 0xc917, 0x0000 }, + { 0xc918, 0x0000 }, + { 0xc919, 0x0000 }, + { 0xc91a, 0x0000 }, + { 0xc91b, 0x0000 }, + { 0xc91c, 0x0000 }, + { 0xc91d, 0x0000 }, + { 0xc91e, 0x0000 }, + { 0xc91f, 0x0000 }, + { 0xc920, 0x0000 }, + { 0xc921, 0x0000 }, + { 0xc922, 0x0000 }, + { 0xc923, 0x0000 }, + { 0xc924, 0x0000 }, + { 0xc925, 0x0000 }, + { 0xc926, 0x0000 }, + { 0xc927, 0x0000 }, + { 0xc928, 0x0000 }, + { 0xc929, 0x0000 }, + { 0xc92a, 0x0000 }, + { 0xc92b, 0x0000 }, + { 0xc92c, 0x0000 }, + { 0xc92d, 0x0000 }, + { 0xc92e, 0x0000 }, + { 0xc92f, 0x0000 }, + { 0xc930, 0x0000 }, + { 0xc931, 0x0000 }, + { 0xc932, 0x0000 }, + { 0xc933, 0x0000 }, + { 0xc934, 0x0000 }, + { 0xc935, 0x0000 }, + { 0xc936, 0x0000 }, + { 0xc937, 0x0000 }, + { 0xc938, 0x0000 }, + { 0xc939, 0x0000 }, + { 0xc93a, 0x0000 }, + { 0xc93b, 0x0000 }, + { 0xc93c, 0x0000 }, + { 0xc93d, 0x0000 }, + { 0xc93e, 0x0000 }, + { 0xc93f, 0x0000 }, + { 0xc940, 0x0000 }, + { 0xc941, 0x0000 }, + { 0xc942, 0x0000 }, + { 0xc943, 0x0000 }, + { 0xc944, 0x0000 }, + { 0xc945, 0x0000 }, + { 0xc946, 0x0000 }, + { 0xc947, 0x0000 }, + { 0xc948, 0x0000 }, + { 0xc949, 0x0000 }, + { 0xc94a, 0x0000 }, + { 0xc94b, 0x0000 }, + { 0xc94c, 0x0000 }, + { 0xc94d, 0x0000 }, + { 0xc94e, 0x0000 }, + { 0xc94f, 0x0000 }, + { 0xc950, 0x0000 }, + { 0xc951, 0x0000 }, + { 0xc952, 0x0000 }, + { 0xc953, 0x0000 }, + { 0xc954, 0x0000 }, + { 0xc955, 0x0000 }, + { 0xc956, 0x0000 }, + { 0xc957, 0x0000 }, + { 0xc958, 0x0000 }, + { 0xc959, 0x0000 }, + { 0xc95a, 0x0000 }, + { 0xc95b, 0x0000 }, + { 0xc95c, 0x0000 }, + { 0xc95d, 0x0000 }, + { 0xc95e, 0x0000 }, + { 0xc95f, 0x0000 }, + { 0xc960, 0x0000 }, + { 0xc961, 0x0000 }, + { 0xc962, 0x0000 }, + { 0xc963, 0x0000 }, + { 0xc964, 0x0000 }, + { 0xc965, 0x0000 }, + { 0xc966, 0x0000 }, + { 0xc967, 0x0000 }, + { 0xc968, 0x0000 }, + { 0xc969, 0x0000 }, + { 0xc96a, 0x0000 }, + { 0xc96b, 0x0000 }, + { 0xc96c, 0x0000 }, + { 0xc96d, 0x0000 }, + { 0xc96e, 0x0000 }, + { 0xc96f, 0x0000 }, + { 0xc970, 0x0000 }, + { 0xc971, 0x0000 }, + { 0xc972, 0x0000 }, + { 0xc973, 0x0000 }, + { 0xc974, 0x0000 }, + { 0xc975, 0x0000 }, + { 0xc976, 0x0000 }, + { 0xc977, 0x0000 }, + { 0xc978, 0x0000 }, + { 0xc979, 0x0000 }, + { 0xc97a, 0x0000 }, + { 0xc97b, 0x0000 }, + { 0xc97c, 0x0000 }, + { 0xc97d, 0x0000 }, + { 0xc97e, 0x0000 }, + { 0xc97f, 0x0000 }, + { 0xc980, 0x0000 }, + { 0xc981, 0x0000 }, + { 0xc982, 0x0000 }, + { 0xc983, 0x0000 }, + { 0xc984, 0x0000 }, + { 0xc985, 0x0000 }, + { 0xc986, 0x0000 }, + { 0xc987, 0x0000 }, + { 0xc988, 0x0000 }, + { 0xc989, 0x0000 }, + { 0xc98a, 0x0000 }, + { 0xc98b, 0x0000 }, + { 0xc98c, 0x0000 }, + { 0xc98d, 0x0000 }, + { 0xc98e, 0x0000 }, + { 0xc98f, 0x0000 }, + { 0xc990, 0x0000 }, + { 0xc991, 0x0000 }, + { 0xc992, 0x0000 }, + { 0xc993, 0x0000 }, + { 0xc994, 0x0000 }, + { 0xc995, 0x0000 }, + { 0xc996, 0x0000 }, + { 0xc997, 0x0000 }, + { 0xc998, 0x0000 }, + { 0xc999, 0x0000 }, + { 0xc99a, 0x0000 }, + { 0xc99b, 0x0000 }, + { 0xc99c, 0x0000 }, + { 0xc99d, 0x0000 }, + { 0xc99e, 0x0000 }, + { 0xc99f, 0x0000 }, + { 0xc9a0, 0x0000 }, + { 0xc9a1, 0x0000 }, + { 0xc9a2, 0x0000 }, + { 0xc9a3, 0x0000 }, + { 0xc9a4, 0x0000 }, + { 0xc9a5, 0x0000 }, + { 0xc9a6, 0x0000 }, + { 0xc9a7, 0x0000 }, + { 0xc9a8, 0x0000 }, + { 0xc9a9, 0x0000 }, + { 0xc9aa, 0x0000 }, + { 0xc9ab, 0x0000 }, + { 0xc9ac, 0x0000 }, + { 0xc9ad, 0x0000 }, + { 0xc9ae, 0x0000 }, + { 0xc9af, 0x0000 }, + { 0xc9b0, 0x0000 }, + { 0xc9b1, 0x0000 }, + { 0xc9b2, 0x0000 }, + { 0xc9b3, 0x0000 }, + { 0xc9b4, 0x0000 }, + { 0xc9b5, 0x0000 }, + { 0xc9b6, 0x0000 }, + { 0xc9b7, 0x0000 }, + { 0xc9b8, 0x0000 }, + { 0xc9b9, 0x0000 }, + { 0xc9ba, 0x0000 }, + { 0xc9bb, 0x0000 }, + { 0xc9bc, 0x0000 }, + { 0xc9bd, 0x0000 }, + { 0xc9be, 0x0000 }, + { 0xc9bf, 0x0000 }, + { 0xc9c0, 0x0000 }, + { 0xc9c1, 0x0000 }, + { 0xc9c2, 0x0000 }, + { 0xc9c3, 0x0000 }, + { 0xc9c4, 0x0000 }, + { 0xc9c5, 0x0000 }, + { 0xc9c6, 0x0000 }, + { 0xc9c7, 0x0000 }, + { 0xc9c8, 0x0000 }, + { 0xc9c9, 0x0000 }, + { 0xc9ca, 0x0000 }, + { 0xc9cb, 0x0000 }, + { 0xc9cc, 0x0000 }, + { 0xc9cd, 0x0000 }, + { 0xc9ce, 0x0000 }, + { 0xc9cf, 0x0000 }, + { 0xc9d0, 0x0000 }, + { 0xc9d1, 0x0000 }, + { 0xc9d2, 0x0000 }, + { 0xc9d3, 0x0000 }, + { 0xc9d4, 0x0000 }, + { 0xc9d5, 0x0000 }, + { 0xc9d6, 0x0000 }, + { 0xc9d7, 0x0000 }, + { 0xc9d8, 0x0000 }, + { 0xc9d9, 0x0000 }, + { 0xc9da, 0x0000 }, + { 0xc9db, 0x0000 }, + { 0xc9dc, 0x0000 }, + { 0xc9dd, 0x0000 }, + { 0xc9de, 0x0000 }, + { 0xc9df, 0x0000 }, + { 0xc9e0, 0x0000 }, + { 0xc9e1, 0x0000 }, + { 0xc9e2, 0x0000 }, + { 0xc9e3, 0x0000 }, + { 0xc9e4, 0x0000 }, + { 0xc9e5, 0x0000 }, + { 0xc9e6, 0x0000 }, + { 0xc9e7, 0x0000 }, + { 0xc9e8, 0x0000 }, + { 0xc9e9, 0x0000 }, + { 0xc9ea, 0x0000 }, + { 0xc9eb, 0x0000 }, + { 0xc9ec, 0x0000 }, + { 0xc9ed, 0x0000 }, + { 0xc9ee, 0x0000 }, + { 0xc9ef, 0x0000 }, + { 0xc9f0, 0x0000 }, + { 0xc9f1, 0x0000 }, + { 0xc9f2, 0x0000 }, + { 0xc9f3, 0x0000 }, + { 0xc9f4, 0x0000 }, + { 0xc9f5, 0x0000 }, + { 0xc9f6, 0x0000 }, + { 0xc9f7, 0x0000 }, + { 0xc9f8, 0x0000 }, + { 0xc9f9, 0x0000 }, + { 0xc9fa, 0x0000 }, + { 0xc9fb, 0x0000 }, + { 0xc9fc, 0x0000 }, + { 0xc9fd, 0x0000 }, + { 0xc9fe, 0x0000 }, + { 0xc9ff, 0x0000 }, + { 0xca00, 0x0000 }, + { 0xca01, 0x0000 }, + { 0xca02, 0x0000 }, + { 0xca03, 0x0000 }, + { 0xca04, 0x0000 }, + { 0xca05, 0x0000 }, + { 0xca06, 0x0000 }, + { 0xca07, 0x0000 }, + { 0xca08, 0x0000 }, + { 0xca09, 0x0000 }, + { 0xca0a, 0x0000 }, + { 0xca0b, 0x0000 }, + { 0xca0c, 0x0000 }, + { 0xca0d, 0x0000 }, + { 0xca0e, 0x0000 }, + { 0xca0f, 0x0000 }, + { 0xca10, 0x0000 }, + { 0xca11, 0x0000 }, + { 0xca12, 0x0000 }, + { 0xca13, 0x0000 }, + { 0xca14, 0x0000 }, + { 0xca15, 0x0000 }, + { 0xca16, 0x0000 }, + { 0xca17, 0x0000 }, + { 0xca18, 0x0000 }, + { 0xca19, 0x0000 }, + { 0xca1a, 0x0000 }, + { 0xca1b, 0x0000 }, + { 0xca1c, 0x0000 }, + { 0xca1d, 0x0000 }, + { 0xca1e, 0x0000 }, + { 0xca1f, 0x0000 }, + { 0xca20, 0x0000 }, + { 0xca21, 0x0000 }, + { 0xca22, 0x0000 }, + { 0xca23, 0x0000 }, + { 0xca24, 0x0000 }, + { 0xca25, 0x0000 }, + { 0xca26, 0x0000 }, + { 0xca27, 0x0000 }, + { 0xca28, 0x0000 }, + { 0xca29, 0x0000 }, + { 0xca2a, 0x0000 }, + { 0xca2b, 0x0000 }, + { 0xca2c, 0x0000 }, + { 0xca2d, 0x0000 }, + { 0xca2e, 0x0000 }, + { 0xca2f, 0x0000 }, + { 0xca30, 0x0000 }, + { 0xca31, 0x0000 }, + { 0xca32, 0x0000 }, + { 0xca33, 0x0000 }, + { 0xca34, 0x0000 }, + { 0xca35, 0x0000 }, + { 0xca36, 0x0000 }, + { 0xca37, 0x0000 }, + { 0xca38, 0x0000 }, + { 0xca39, 0x0000 }, + { 0xca3a, 0x0000 }, + { 0xca3b, 0x0000 }, + { 0xca3c, 0x0000 }, + { 0xca3d, 0x0000 }, + { 0xca3e, 0x0000 }, + { 0xca3f, 0x0000 }, + { 0xca40, 0x0000 }, + { 0xca41, 0x0000 }, + { 0xca42, 0x0000 }, + { 0xca43, 0x0000 }, + { 0xca44, 0x0000 }, + { 0xca45, 0x0000 }, + { 0xca46, 0x0000 }, + { 0xca47, 0x0000 }, + { 0xca48, 0x0000 }, + { 0xca49, 0x0000 }, + { 0xca4a, 0x0000 }, + { 0xca4b, 0x0000 }, + { 0xca4c, 0x0000 }, + { 0xca4d, 0x0000 }, + { 0xca4e, 0x0000 }, + { 0xca4f, 0x0000 }, + { 0xca50, 0x0000 }, + { 0xca51, 0x0000 }, + { 0xca52, 0x0000 }, + { 0xca53, 0x0000 }, + { 0xca54, 0x0000 }, + { 0xca55, 0x0000 }, + { 0xca56, 0x0000 }, + { 0xca57, 0x0000 }, + { 0xca58, 0x0000 }, + { 0xca59, 0x0000 }, + { 0xca5a, 0x0000 }, + { 0xca5b, 0x0000 }, + { 0xca5c, 0x0000 }, + { 0xca5d, 0x0000 }, + { 0xca5e, 0x0000 }, + { 0xca5f, 0x0000 }, + { 0xca60, 0x0000 }, + { 0xca61, 0x0000 }, + { 0xca62, 0x0000 }, + { 0xca63, 0x0000 }, + { 0xca64, 0x0000 }, + { 0xca65, 0x0000 }, + { 0xca66, 0x0000 }, + { 0xca67, 0x0000 }, + { 0xca68, 0x0000 }, + { 0xca69, 0x0000 }, + { 0xca6a, 0x0000 }, + { 0xca6b, 0x0000 }, + { 0xca6c, 0x0000 }, + { 0xca6d, 0x0000 }, + { 0xca6e, 0x0000 }, + { 0xca6f, 0x0000 }, + { 0xca70, 0x0000 }, + { 0xca71, 0x0000 }, + { 0xca72, 0x0000 }, + { 0xca73, 0x0000 }, + { 0xca74, 0x0000 }, + { 0xca75, 0x0000 }, + { 0xca76, 0x0000 }, + { 0xca77, 0x0000 }, + { 0xca78, 0x0000 }, + { 0xca79, 0x0000 }, + { 0xca7a, 0x0000 }, + { 0xca7b, 0x0000 }, + { 0xca7c, 0x0000 }, + { 0xca7d, 0x0000 }, + { 0xca7e, 0x0000 }, + { 0xca7f, 0x0000 }, + { 0xca80, 0x0000 }, + { 0xca81, 0x0000 }, + { 0xca82, 0x0000 }, + { 0xca83, 0x0000 }, + { 0xca84, 0x0000 }, + { 0xca85, 0x0000 }, + { 0xca86, 0x0000 }, + { 0xca87, 0x0000 }, + { 0xca88, 0x0000 }, + { 0xca89, 0x0000 }, + { 0xca8a, 0x0000 }, + { 0xca8b, 0x0000 }, + { 0xca8c, 0x0000 }, + { 0xca8d, 0x0000 }, + { 0xca8e, 0x0000 }, + { 0xca8f, 0x0000 }, + { 0xca90, 0x0000 }, + { 0xca91, 0x0000 }, + { 0xca92, 0x0000 }, + { 0xca93, 0x0000 }, + { 0xca94, 0x0000 }, + { 0xca95, 0x0000 }, + { 0xca96, 0x0000 }, + { 0xca97, 0x0000 }, + { 0xca98, 0x0000 }, + { 0xca99, 0x0000 }, + { 0xca9a, 0x0000 }, + { 0xca9b, 0x0000 }, + { 0xca9c, 0x0000 }, + { 0xca9d, 0x0000 }, + { 0xca9e, 0x0000 }, + { 0xca9f, 0x0000 }, + { 0xcaa0, 0x0000 }, + { 0xcaa1, 0x0000 }, + { 0xcaa2, 0x0000 }, + { 0xcaa3, 0x0000 }, + { 0xcaa4, 0x0000 }, + { 0xcaa5, 0x0000 }, + { 0xcaa6, 0x0000 }, + { 0xcaa7, 0x0000 }, + { 0xcaa8, 0x0000 }, + { 0xcaa9, 0x0000 }, + { 0xcaaa, 0x0000 }, + { 0xcaab, 0x0000 }, + { 0xcaac, 0x0000 }, + { 0xcaad, 0x0000 }, + { 0xcaae, 0x0000 }, + { 0xcaaf, 0x0000 }, + { 0xcab0, 0x0000 }, + { 0xcab1, 0x0000 }, + { 0xcab2, 0x0000 }, + { 0xcab3, 0x0000 }, + { 0xcab4, 0x0000 }, + { 0xcab5, 0x0000 }, + { 0xcab6, 0x0000 }, + { 0xcab7, 0x0000 }, + { 0xcab8, 0x0000 }, + { 0xcab9, 0x0000 }, + { 0xcaba, 0x0000 }, + { 0xcabb, 0x0000 }, + { 0xcabc, 0x0000 }, + { 0xcabd, 0x0000 }, + { 0xcabe, 0x0000 }, + { 0xcabf, 0x0000 }, + { 0xcac0, 0x0000 }, + { 0xcac1, 0x0000 }, + { 0xcac2, 0x0000 }, + { 0xcac3, 0x0000 }, + { 0xcac4, 0x0000 }, + { 0xcac5, 0x0000 }, + { 0xcac6, 0x0000 }, + { 0xcac7, 0x0000 }, + { 0xcac8, 0x0000 }, + { 0xcac9, 0x0000 }, + { 0xcaca, 0x0000 }, + { 0xcacb, 0x0000 }, + { 0xcacc, 0x0000 }, + { 0xcacd, 0x0000 }, + { 0xcace, 0x0000 }, + { 0xcacf, 0x0000 }, + { 0xcad0, 0x0000 }, + { 0xcad1, 0x0000 }, + { 0xcad2, 0x0000 }, + { 0xcad3, 0x0000 }, + { 0xcad4, 0x0000 }, + { 0xcad5, 0x0000 }, + { 0xcad6, 0x0000 }, + { 0xcad7, 0x0000 }, + { 0xcad8, 0x0000 }, + { 0xcad9, 0x0000 }, + { 0xcada, 0x0000 }, + { 0xcadb, 0x0000 }, + { 0xcadc, 0x0000 }, + { 0xcadd, 0x0000 }, + { 0xcade, 0x0000 }, + { 0xcadf, 0x0000 }, + { 0xcae0, 0x0000 }, + { 0xcae1, 0x0000 }, + { 0xcae2, 0x0000 }, + { 0xcae3, 0x0000 }, + { 0xcae4, 0x0000 }, + { 0xcae5, 0x0000 }, + { 0xcae6, 0x0000 }, + { 0xcae7, 0x0000 }, + { 0xcae8, 0x0000 }, + { 0xcae9, 0x0000 }, + { 0xcaea, 0x0000 }, + { 0xcaeb, 0x0000 }, + { 0xcaec, 0x0000 }, + { 0xcaed, 0x0000 }, + { 0xcaee, 0x0000 }, + { 0xcaef, 0x0000 }, + { 0xcaf0, 0x0000 }, + { 0xcaf1, 0x0000 }, + { 0xcaf2, 0x0000 }, + { 0xcaf3, 0x0000 }, + { 0xcaf4, 0x0000 }, + { 0xcaf5, 0x0000 }, + { 0xcaf6, 0x0000 }, + { 0xcaf7, 0x0000 }, + { 0xcaf8, 0x0000 }, + { 0xcaf9, 0x0000 }, + { 0xcafa, 0x0000 }, + { 0xcafb, 0x0000 }, + { 0xcafc, 0x0000 }, + { 0xcafd, 0x0000 }, + { 0xcafe, 0x0000 }, + { 0xcaff, 0x0000 }, + { 0xcb00, 0x0000 }, + { 0xcb01, 0x0000 }, + { 0xcb02, 0x0000 }, + { 0xcb03, 0x0000 }, + { 0xcb04, 0x0000 }, + { 0xcb05, 0x0000 }, + { 0xcb06, 0x0000 }, + { 0xcb07, 0x0000 }, + { 0xcb08, 0x0000 }, + { 0xcb09, 0x0000 }, + { 0xcb0a, 0x0000 }, + { 0xcb0b, 0x0000 }, + { 0xcb0c, 0x0000 }, + { 0xcb0d, 0x0000 }, + { 0xcb0e, 0x0000 }, + { 0xcb0f, 0x0000 }, + { 0xcb10, 0x0000 }, + { 0xcb11, 0x0000 }, + { 0xcb12, 0x0000 }, + { 0xcb13, 0x0000 }, + { 0xcb14, 0x0000 }, + { 0xcb15, 0x0000 }, + { 0xcb16, 0x0000 }, + { 0xcb17, 0x0000 }, + { 0xcb18, 0x0000 }, + { 0xcb19, 0x0000 }, + { 0xcb1a, 0x0000 }, + { 0xcb1b, 0x0000 }, + { 0xcb1c, 0x0000 }, + { 0xcb1d, 0x0000 }, + { 0xcb1e, 0x0000 }, + { 0xcb1f, 0x0000 }, + { 0xcb20, 0x0000 }, + { 0xcb21, 0x0000 }, + { 0xcb22, 0x0000 }, + { 0xcb23, 0x0000 }, + { 0xcb24, 0x0000 }, + { 0xcb25, 0x0000 }, + { 0xcb26, 0x0000 }, + { 0xcb27, 0x0000 }, + { 0xcb28, 0x0000 }, + { 0xcb29, 0x0000 }, + { 0xcb2a, 0x0000 }, + { 0xcb2b, 0x0000 }, + { 0xcb2c, 0x0000 }, + { 0xcb2d, 0x0000 }, + { 0xcb2e, 0x0000 }, + { 0xcb2f, 0x0000 }, + { 0xcb30, 0x0000 }, + { 0xcb31, 0x0000 }, + { 0xcb32, 0x0000 }, + { 0xcb33, 0x0000 }, + { 0xcb34, 0x0000 }, + { 0xcb35, 0x0000 }, + { 0xcb36, 0x0000 }, + { 0xcb37, 0x0000 }, + { 0xcb38, 0x0000 }, + { 0xcb39, 0x0000 }, + { 0xcb3a, 0x0000 }, + { 0xcb3b, 0x0000 }, + { 0xcb3c, 0x0000 }, + { 0xcb3d, 0x0000 }, + { 0xcb3e, 0x0000 }, + { 0xcb3f, 0x0000 }, + { 0xcb40, 0x0000 }, + { 0xcb41, 0x0000 }, + { 0xcb42, 0x0000 }, + { 0xcb43, 0x0000 }, + { 0xcb44, 0x0000 }, + { 0xcb45, 0x0000 }, + { 0xcb46, 0x0000 }, + { 0xcb47, 0x0000 }, + { 0xcb48, 0x0000 }, + { 0xcb49, 0x0000 }, + { 0xcb4a, 0x0000 }, + { 0xcb4b, 0x0000 }, + { 0xcb4c, 0x0000 }, + { 0xcb4d, 0x0000 }, + { 0xcb4e, 0x0000 }, + { 0xcb4f, 0x0000 }, + { 0xcb50, 0x0000 }, + { 0xcb51, 0x0000 }, + { 0xcb52, 0x0000 }, + { 0xcb53, 0x0000 }, + { 0xcb54, 0x0000 }, + { 0xcb55, 0x0000 }, + { 0xcb56, 0x0000 }, + { 0xcb57, 0x0000 }, + { 0xcb58, 0x0000 }, + { 0xcb59, 0x0000 }, + { 0xcb5a, 0x0000 }, + { 0xcb5b, 0x0000 }, + { 0xcb5c, 0x0000 }, + { 0xcb5d, 0x0000 }, + { 0xcb5e, 0x0000 }, + { 0xcb5f, 0x0000 }, + { 0xcb60, 0x0000 }, + { 0xcb61, 0x0000 }, + { 0xcb62, 0x0000 }, + { 0xcb63, 0x0000 }, + { 0xcb64, 0x0000 }, + { 0xcb65, 0x0000 }, + { 0xcb66, 0x0000 }, + { 0xcb67, 0x0000 }, + { 0xcb68, 0x0000 }, + { 0xcb69, 0x0000 }, + { 0xcb6a, 0x0000 }, + { 0xcb6b, 0x0000 }, + { 0xcb6c, 0x0000 }, + { 0xcb6d, 0x0000 }, + { 0xcb6e, 0x0000 }, + { 0xcb6f, 0x0000 }, + { 0xcb70, 0x0000 }, + { 0xcb71, 0x0000 }, + { 0xcb72, 0x0000 }, + { 0xcb73, 0x0000 }, + { 0xcb74, 0x0000 }, + { 0xcb75, 0x0000 }, + { 0xcb76, 0x0000 }, + { 0xcb77, 0x0000 }, + { 0xcb78, 0x0000 }, + { 0xcb79, 0x0000 }, + { 0xcb7a, 0x0000 }, + { 0xcb7b, 0x0000 }, + { 0xcb7c, 0x0000 }, + { 0xcb7d, 0x0000 }, + { 0xcb7e, 0x0000 }, + { 0xcb7f, 0x0000 }, + { 0xcb80, 0x0000 }, + { 0xcb81, 0x0000 }, + { 0xcb82, 0x0000 }, + { 0xcb83, 0x0000 }, + { 0xcb84, 0x0000 }, + { 0xcb85, 0x0000 }, + { 0xcb86, 0x0000 }, + { 0xcb87, 0x0000 }, + { 0xcb88, 0x0000 }, + { 0xcb89, 0x0000 }, + { 0xcb8a, 0x0000 }, + { 0xcb8b, 0x0000 }, + { 0xcb8c, 0x0000 }, + { 0xcb8d, 0x0000 }, + { 0xcb8e, 0x0000 }, + { 0xcb8f, 0x0000 }, + { 0xcb90, 0x0000 }, + { 0xcb91, 0x0000 }, + { 0xcb92, 0x0000 }, + { 0xcb93, 0x0000 }, + { 0xcb94, 0x0000 }, + { 0xcb95, 0x0000 }, + { 0xcb96, 0x0000 }, + { 0xcb97, 0x0000 }, + { 0xcb98, 0x0000 }, + { 0xcb99, 0x0000 }, + { 0xcb9a, 0x0000 }, + { 0xcb9b, 0x0000 }, + { 0xcb9c, 0x0000 }, + { 0xcb9d, 0x0000 }, + { 0xcb9e, 0x0000 }, + { 0xcb9f, 0x0000 }, + { 0xcba0, 0x0000 }, + { 0xcba1, 0x0000 }, + { 0xcba2, 0x0000 }, + { 0xcba3, 0x0000 }, + { 0xcba4, 0x0000 }, + { 0xcba5, 0x0000 }, + { 0xcba6, 0x0000 }, + { 0xcba7, 0x0000 }, + { 0xcba8, 0x0000 }, + { 0xcba9, 0x0000 }, + { 0xcbaa, 0x0000 }, + { 0xcbab, 0x0000 }, + { 0xcbac, 0x0000 }, + { 0xcbad, 0x0000 }, + { 0xcbae, 0x0000 }, + { 0xcbaf, 0x0000 }, + { 0xcbb0, 0x0000 }, + { 0xcbb1, 0x0000 }, + { 0xcbb2, 0x0000 }, + { 0xcbb3, 0x0000 }, + { 0xcbb4, 0x0000 }, + { 0xcbb5, 0x0000 }, + { 0xcbb6, 0x0000 }, + { 0xcbb7, 0x0000 }, + { 0xcbb8, 0x0000 }, + { 0xcbb9, 0x0000 }, + { 0xcbba, 0x0000 }, + { 0xcbbb, 0x0000 }, + { 0xcbbc, 0x0000 }, + { 0xcbbd, 0x0000 }, + { 0xcbbe, 0x0000 }, + { 0xcbbf, 0x0000 }, + { 0xcbc0, 0x0000 }, + { 0xcbc1, 0x0000 }, + { 0xcbc2, 0x0000 }, + { 0xcbc3, 0x0000 }, + { 0xcbc4, 0x0000 }, + { 0xcbc5, 0x0000 }, + { 0xcbc6, 0x0000 }, + { 0xcbc7, 0x0000 }, + { 0xcbc8, 0x0000 }, + { 0xcbc9, 0x0000 }, + { 0xcbca, 0x0000 }, + { 0xcbcb, 0x0000 }, + { 0xcbcc, 0x0000 }, + { 0xcbcd, 0x0000 }, + { 0xcbce, 0x0000 }, + { 0xcbcf, 0x0000 }, + { 0xcbd0, 0x0000 }, + { 0xcbd1, 0x0000 }, + { 0xcbd2, 0x0000 }, + { 0xcbd3, 0x0000 }, + { 0xcbd4, 0x0000 }, + { 0xcbd5, 0x0000 }, + { 0xcbd6, 0x0000 }, + { 0xcbd7, 0x0000 }, + { 0xcbd8, 0x0000 }, + { 0xcbd9, 0x0000 }, + { 0xcbda, 0x0000 }, + { 0xcbdb, 0x0000 }, + { 0xcbdc, 0x0000 }, + { 0xcbdd, 0x0000 }, + { 0xcbde, 0x0000 }, + { 0xcbdf, 0x0000 }, + { 0xcbe0, 0x0000 }, + { 0xcbe1, 0x0000 }, + { 0xcbe2, 0x0000 }, + { 0xcbe3, 0x0000 }, + { 0xcbe4, 0x0000 }, + { 0xcbe5, 0x0000 }, + { 0xcbe6, 0x0000 }, + { 0xcbe7, 0x0000 }, + { 0xcbe8, 0x0000 }, + { 0xcbe9, 0x0000 }, + { 0xcbea, 0x0000 }, + { 0xcbeb, 0x0000 }, + { 0xcbec, 0x0000 }, + { 0xcbed, 0x0000 }, + { 0xcbee, 0x0000 }, + { 0xcbef, 0x0000 }, + { 0xcbf0, 0x0000 }, + { 0xcbf1, 0x0000 }, + { 0xcbf2, 0x0000 }, + { 0xcbf3, 0x0000 }, + { 0xcbf4, 0x0000 }, + { 0xcbf5, 0x0000 }, + { 0xcbf6, 0x0000 }, + { 0xcbf7, 0x0000 }, + { 0xcbf8, 0x0000 }, + { 0xcbf9, 0x0000 }, + { 0xcbfa, 0x0000 }, + { 0xcbfb, 0x0000 }, + { 0xcbfc, 0x0000 }, + { 0xcbfd, 0x0000 }, + { 0xcbfe, 0x0000 }, + { 0xcbff, 0x0000 }, + { 0xcc00, 0x0000 }, + { 0xcc01, 0x0000 }, + { 0xcc02, 0x0000 }, + { 0xcc03, 0x0000 }, + { 0xcc04, 0x0000 }, + { 0xcc05, 0x0000 }, + { 0xcc06, 0x0000 }, + { 0xcc07, 0x0000 }, + { 0xcc08, 0x0000 }, + { 0xcc09, 0x0000 }, + { 0xcc0a, 0x0000 }, + { 0xcc0b, 0x0000 }, + { 0xcc0c, 0x0000 }, + { 0xcc0d, 0x0000 }, + { 0xcc0e, 0x0000 }, + { 0xcc0f, 0x0000 }, + { 0xcc10, 0x0000 }, + { 0xcc11, 0x0000 }, + { 0xcc12, 0x0000 }, + { 0xcc13, 0x0000 }, + { 0xcc14, 0x0000 }, + { 0xcc15, 0x0000 }, + { 0xcc16, 0x0000 }, + { 0xcc17, 0x0000 }, + { 0xcc18, 0x0000 }, + { 0xcc19, 0x0000 }, + { 0xcc1a, 0x0000 }, + { 0xcc1b, 0x0000 }, + { 0xcc1c, 0x0000 }, + { 0xcc1d, 0x0000 }, + { 0xcc1e, 0x0000 }, + { 0xcc1f, 0x0000 }, + { 0xcc20, 0x0000 }, + { 0xcc21, 0x0000 }, + { 0xcc22, 0x0000 }, + { 0xcc23, 0x0000 }, + { 0xcc24, 0x0000 }, + { 0xcc25, 0x0000 }, + { 0xcc26, 0x0000 }, + { 0xcc27, 0x0000 }, + { 0xcc28, 0x0000 }, + { 0xcc29, 0x0000 }, + { 0xcc2a, 0x0000 }, + { 0xcc2b, 0x0000 }, + { 0xcc2c, 0x0000 }, + { 0xcc2d, 0x0000 }, + { 0xcc2e, 0x0000 }, + { 0xcc2f, 0x0000 }, + { 0xcc30, 0x0000 }, + { 0xcc31, 0x0000 }, + { 0xcc32, 0x0000 }, + { 0xcc33, 0x0000 }, + { 0xcc34, 0x0000 }, + { 0xcc35, 0x0000 }, + { 0xcc36, 0x0000 }, + { 0xcc37, 0x0000 }, + { 0xcc38, 0x0000 }, + { 0xcc39, 0x0000 }, + { 0xcc3a, 0x0000 }, + { 0xcc3b, 0x0000 }, + { 0xcc3c, 0x0000 }, + { 0xcc3d, 0x0000 }, + { 0xcc3e, 0x0000 }, + { 0xcc3f, 0x0000 }, + { 0xcc40, 0x0000 }, + { 0xcc41, 0x0000 }, + { 0xcc42, 0x0000 }, + { 0xcc43, 0x0000 }, + { 0xcc44, 0x0000 }, + { 0xcc45, 0x0000 }, + { 0xcc46, 0x0000 }, + { 0xcc47, 0x0000 }, + { 0xcc48, 0x0000 }, + { 0xcc49, 0x0000 }, + { 0xcc4a, 0x0000 }, + { 0xcc4b, 0x0000 }, + { 0xcc4c, 0x0000 }, + { 0xcc4d, 0x0000 }, + { 0xcc4e, 0x0000 }, + { 0xcc4f, 0x0000 }, + { 0xcc50, 0x0000 }, + { 0xcc51, 0x0000 }, + { 0xcc52, 0x0000 }, + { 0xcc53, 0x0000 }, + { 0xcc54, 0x0000 }, + { 0xcc55, 0x0000 }, + { 0xcc56, 0x0000 }, + { 0xcc57, 0x0000 }, + { 0xcc58, 0x0000 }, + { 0xcc59, 0x0000 }, + { 0xcc5a, 0x0000 }, + { 0xcc5b, 0x0000 }, + { 0xcc5c, 0x0000 }, + { 0xcc5d, 0x0000 }, + { 0xcc5e, 0x0000 }, + { 0xcc5f, 0x0000 }, + { 0xcc60, 0x0000 }, + { 0xcc61, 0x0000 }, + { 0xcc62, 0x0000 }, + { 0xcc63, 0x0000 }, + { 0xcc64, 0x0000 }, + { 0xcc65, 0x0000 }, + { 0xcc66, 0x0000 }, + { 0xcc67, 0x0000 }, + { 0xcc68, 0x0000 }, + { 0xcc69, 0x0000 }, + { 0xcc6a, 0x0000 }, + { 0xcc6b, 0x0000 }, + { 0xcc6c, 0x0000 }, + { 0xcc6d, 0x0000 }, + { 0xcc6e, 0x0000 }, + { 0xcc6f, 0x0000 }, + { 0xcc70, 0x0000 }, + { 0xcc71, 0x0000 }, + { 0xcc72, 0x0000 }, + { 0xcc73, 0x0000 }, + { 0xcc74, 0x0000 }, + { 0xcc75, 0x0000 }, + { 0xcc76, 0x0000 }, + { 0xcc77, 0x0000 }, + { 0xcc78, 0x0000 }, + { 0xcc79, 0x0000 }, + { 0xcc7a, 0x0000 }, + { 0xcc7b, 0x0000 }, + { 0xcc7c, 0x0000 }, + { 0xcc7d, 0x0000 }, + { 0xcc7e, 0x0000 }, + { 0xcc7f, 0x0000 }, + { 0xcc80, 0x0000 }, + { 0xcc81, 0x0000 }, + { 0xcc82, 0x0000 }, + { 0xcc83, 0x0000 }, + { 0xcc84, 0x0000 }, + { 0xcc85, 0x0000 }, + { 0xcc86, 0x0000 }, + { 0xcc87, 0x0000 }, + { 0xcc88, 0x0000 }, + { 0xcc89, 0x0000 }, + { 0xcc8a, 0x0000 }, + { 0xcc8b, 0x0000 }, + { 0xcc8c, 0x0000 }, + { 0xcc8d, 0x0000 }, + { 0xcc8e, 0x0000 }, + { 0xcc8f, 0x0000 }, + { 0xcc90, 0x0000 }, + { 0xcc91, 0x0000 }, + { 0xcc92, 0x0000 }, + { 0xcc93, 0x0000 }, + { 0xcc94, 0x0000 }, + { 0xcc95, 0x0000 }, + { 0xcc96, 0x0000 }, + { 0xcc97, 0x0000 }, + { 0xcc98, 0x0000 }, + { 0xcc99, 0x0000 }, + { 0xcc9a, 0x0000 }, + { 0xcc9b, 0x0000 }, + { 0xcc9c, 0x0000 }, + { 0xcc9d, 0x0000 }, + { 0xcc9e, 0x0000 }, + { 0xcc9f, 0x0000 }, + { 0xcca0, 0x0000 }, + { 0xcca1, 0x0000 }, + { 0xcca2, 0x0000 }, + { 0xcca3, 0x0000 }, + { 0xcca4, 0x0000 }, + { 0xcca5, 0x0000 }, + { 0xcca6, 0x0000 }, + { 0xcca7, 0x0000 }, + { 0xcca8, 0x0000 }, + { 0xcca9, 0x0000 }, + { 0xccaa, 0x0000 }, + { 0xccab, 0x0000 }, + { 0xccac, 0x0000 }, + { 0xccad, 0x0000 }, + { 0xccae, 0x0000 }, + { 0xccaf, 0x0000 }, + { 0xccb0, 0x0000 }, + { 0xccb1, 0x0000 }, + { 0xccb2, 0x0000 }, + { 0xccb3, 0x0000 }, + { 0xccb4, 0x0000 }, + { 0xccb5, 0x0000 }, + { 0xccb6, 0x0000 }, + { 0xccb7, 0x0000 }, + { 0xccb8, 0x0000 }, + { 0xccb9, 0x0000 }, + { 0xccba, 0x0000 }, + { 0xccbb, 0x0000 }, + { 0xccbc, 0x0000 }, + { 0xccbd, 0x0000 }, + { 0xccbe, 0x0000 }, + { 0xccbf, 0x0000 }, + { 0xccc0, 0x0000 }, + { 0xccc1, 0x0000 }, + { 0xccc2, 0x0000 }, + { 0xccc3, 0x0000 }, + { 0xccc4, 0x0000 }, + { 0xccc5, 0x0000 }, + { 0xccc6, 0x0000 }, + { 0xccc7, 0x0000 }, + { 0xccc8, 0x0000 }, + { 0xccc9, 0x0000 }, + { 0xccca, 0x0000 }, + { 0xcccb, 0x0000 }, + { 0xcccc, 0x0000 }, + { 0xcccd, 0x0000 }, + { 0xccce, 0x0000 }, + { 0xcccf, 0x0000 }, + { 0xccd0, 0x0000 }, + { 0xccd1, 0x0000 }, + { 0xccd2, 0x0000 }, + { 0xccd3, 0x0000 }, + { 0xccd4, 0x0000 }, + { 0xccd5, 0x0000 }, + { 0xccd6, 0x0000 }, + { 0xccd7, 0x0000 }, + { 0xccd8, 0x0000 }, + { 0xccd9, 0x0000 }, + { 0xccda, 0x0000 }, + { 0xccdb, 0x0000 }, + { 0xccdc, 0x0000 }, + { 0xccdd, 0x0000 }, + { 0xccde, 0x0000 }, + { 0xccdf, 0x0000 }, + { 0xcce0, 0x0000 }, + { 0xcce1, 0x0000 }, + { 0xcce2, 0x0000 }, + { 0xcce3, 0x0000 }, + { 0xcce4, 0x0000 }, + { 0xcce5, 0x0000 }, + { 0xcce6, 0x0000 }, + { 0xcce7, 0x0000 }, + { 0xcce8, 0x0000 }, + { 0xcce9, 0x0000 }, + { 0xccea, 0x0000 }, + { 0xcceb, 0x0000 }, + { 0xccec, 0x0000 }, + { 0xcced, 0x0000 }, + { 0xccee, 0x0000 }, + { 0xccef, 0x0000 }, + { 0xccf0, 0x0000 }, + { 0xccf1, 0x0000 }, + { 0xccf2, 0x0000 }, + { 0xccf3, 0x0000 }, + { 0xccf4, 0x0000 }, + { 0xccf5, 0x0000 }, + { 0xccf6, 0x0000 }, + { 0xccf7, 0x0000 }, + { 0xccf8, 0x0000 }, + { 0xccf9, 0x0000 }, + { 0xccfa, 0x0000 }, + { 0xccfb, 0x0000 }, + { 0xccfc, 0x0000 }, + { 0xccfd, 0x0000 }, + { 0xccfe, 0x0000 }, + { 0xccff, 0x0000 }, + { 0xcd00, 0x0000 }, + { 0xcd01, 0x0000 }, + { 0xcd02, 0x0000 }, + { 0xcd03, 0x0000 }, + { 0xcd04, 0x0000 }, + { 0xcd05, 0x0000 }, + { 0xcd06, 0x0000 }, + { 0xcd07, 0x0000 }, + { 0xcd08, 0x0000 }, + { 0xcd09, 0x0000 }, + { 0xcd0a, 0x0000 }, + { 0xcd0b, 0x0000 }, + { 0xcd0c, 0x0000 }, + { 0xcd0d, 0x0000 }, + { 0xcd0e, 0x0000 }, + { 0xcd0f, 0x0000 }, + { 0xcd10, 0x0000 }, + { 0xcd11, 0x0000 }, + { 0xcd12, 0x0000 }, + { 0xcd13, 0x0000 }, + { 0xcd14, 0x0000 }, + { 0xcd15, 0x0000 }, + { 0xcd16, 0x0000 }, + { 0xcd17, 0x0000 }, + { 0xcd18, 0x0000 }, + { 0xcd19, 0x0000 }, + { 0xcd1a, 0x0000 }, + { 0xcd1b, 0x0000 }, + { 0xcd1c, 0x0000 }, + { 0xcd1d, 0x0000 }, + { 0xcd1e, 0x0000 }, + { 0xcd1f, 0x0000 }, + { 0xcd20, 0x0000 }, + { 0xcd21, 0x0000 }, + { 0xcd22, 0x0000 }, + { 0xcd23, 0x0000 }, + { 0xcd24, 0x0000 }, + { 0xcd25, 0x0000 }, + { 0xcd26, 0x0000 }, + { 0xcd27, 0x0000 }, + { 0xcd28, 0x0000 }, + { 0xcd29, 0x0000 }, + { 0xcd2a, 0x0000 }, + { 0xcd2b, 0x0000 }, + { 0xcd2c, 0x0000 }, + { 0xcd2d, 0x0000 }, + { 0xcd2e, 0x0000 }, + { 0xcd2f, 0x0000 }, + { 0xcd30, 0x0000 }, + { 0xcd31, 0x0000 }, + { 0xcd32, 0x0000 }, + { 0xcd33, 0x0000 }, + { 0xcd34, 0x0000 }, + { 0xcd35, 0x0000 }, + { 0xcd36, 0x0000 }, + { 0xcd37, 0x0000 }, + { 0xcd38, 0x0000 }, + { 0xcd39, 0x0000 }, + { 0xcd3a, 0x0000 }, + { 0xcd3b, 0x0000 }, + { 0xcd3c, 0x0000 }, + { 0xcd3d, 0x0000 }, + { 0xcd3e, 0x0000 }, + { 0xcd3f, 0x0000 }, + { 0xcd40, 0x0000 }, + { 0xcd41, 0x0000 }, + { 0xcd42, 0x0000 }, + { 0xcd43, 0x0000 }, + { 0xcd44, 0x0000 }, + { 0xcd45, 0x0000 }, + { 0xcd46, 0x0000 }, + { 0xcd47, 0x0000 }, + { 0xcd48, 0x0000 }, + { 0xcd49, 0x0000 }, + { 0xcd4a, 0x0000 }, + { 0xcd4b, 0x0000 }, + { 0xcd4c, 0x0000 }, + { 0xcd4d, 0x0000 }, + { 0xcd4e, 0x0000 }, + { 0xcd4f, 0x0000 }, + { 0xcd50, 0x0000 }, + { 0xcd51, 0x0000 }, + { 0xcd52, 0x0000 }, + { 0xcd53, 0x0000 }, + { 0xcd54, 0x0000 }, + { 0xcd55, 0x0000 }, + { 0xcd56, 0x0000 }, + { 0xcd57, 0x0000 }, + { 0xcd58, 0x0000 }, + { 0xcd59, 0x0000 }, + { 0xcd5a, 0x0000 }, + { 0xcd5b, 0x0000 }, + { 0xcd5c, 0x0000 }, + { 0xcd5d, 0x0000 }, + { 0xcd5e, 0x0000 }, + { 0xcd5f, 0x0000 }, + { 0xcd60, 0x0000 }, + { 0xcd61, 0x0000 }, + { 0xcd62, 0x0000 }, + { 0xcd63, 0x0000 }, + { 0xcd64, 0x0000 }, + { 0xcd65, 0x0000 }, + { 0xcd66, 0x0000 }, + { 0xcd67, 0x0000 }, + { 0xcd68, 0x0000 }, + { 0xcd69, 0x0000 }, + { 0xcd6a, 0x0000 }, + { 0xcd6b, 0x0000 }, + { 0xcd6c, 0x0000 }, + { 0xcd6d, 0x0000 }, + { 0xcd6e, 0x0000 }, + { 0xcd6f, 0x0000 }, + { 0xcd70, 0x0000 }, + { 0xcd71, 0x0000 }, + { 0xcd72, 0x0000 }, + { 0xcd73, 0x0000 }, + { 0xcd74, 0x0000 }, + { 0xcd75, 0x0000 }, + { 0xcd76, 0x0000 }, + { 0xcd77, 0x0000 }, + { 0xcd78, 0x0000 }, + { 0xcd79, 0x0000 }, + { 0xcd7a, 0x0000 }, + { 0xcd7b, 0x0000 }, + { 0xcd7c, 0x0000 }, + { 0xcd7d, 0x0000 }, + { 0xcd7e, 0x0000 }, + { 0xcd7f, 0x0000 }, + { 0xcd80, 0x0000 }, + { 0xcd81, 0x0000 }, + { 0xcd82, 0x0000 }, + { 0xcd83, 0x0000 }, + { 0xcd84, 0x0000 }, + { 0xcd85, 0x0000 }, + { 0xcd86, 0x0000 }, + { 0xcd87, 0x0000 }, + { 0xcd88, 0x0000 }, + { 0xcd89, 0x0000 }, + { 0xcd8a, 0x0000 }, + { 0xcd8b, 0x0000 }, + { 0xcd8c, 0x0000 }, + { 0xcd8d, 0x0000 }, + { 0xcd8e, 0x0000 }, + { 0xcd8f, 0x0000 }, + { 0xcd90, 0x0000 }, + { 0xcd91, 0x0000 }, + { 0xcd92, 0x0000 }, + { 0xcd93, 0x0000 }, + { 0xcd94, 0x0000 }, + { 0xcd95, 0x0000 }, + { 0xcd96, 0x0000 }, + { 0xcd97, 0x0000 }, + { 0xcd98, 0x0000 }, + { 0xcd99, 0x0000 }, + { 0xcd9a, 0x0000 }, + { 0xcd9b, 0x0000 }, + { 0xcd9c, 0x0000 }, + { 0xcd9d, 0x0000 }, + { 0xcd9e, 0x0000 }, + { 0xcd9f, 0x0000 }, + { 0xcda0, 0x0000 }, + { 0xcda1, 0x0000 }, + { 0xcda2, 0x0000 }, + { 0xcda3, 0x0000 }, + { 0xcda4, 0x0000 }, + { 0xcda5, 0x0000 }, + { 0xcda6, 0x0000 }, + { 0xcda7, 0x0000 }, + { 0xcda8, 0x0000 }, + { 0xcda9, 0x0000 }, + { 0xcdaa, 0x0000 }, + { 0xcdab, 0x0000 }, + { 0xcdac, 0x0000 }, + { 0xcdad, 0x0000 }, + { 0xcdae, 0x0000 }, + { 0xcdaf, 0x0000 }, + { 0xcdb0, 0x0000 }, + { 0xcdb1, 0x0000 }, + { 0xcdb2, 0x0000 }, + { 0xcdb3, 0x0000 }, + { 0xcdb4, 0x0000 }, + { 0xcdb5, 0x0000 }, + { 0xcdb6, 0x0000 }, + { 0xcdb7, 0x0000 }, + { 0xcdb8, 0x0000 }, + { 0xcdb9, 0x0000 }, + { 0xcdba, 0x0000 }, + { 0xcdbb, 0x0000 }, + { 0xcdbc, 0x0000 }, + { 0xcdbd, 0x0000 }, + { 0xcdbe, 0x0000 }, + { 0xcdbf, 0x0000 }, + { 0xcdc0, 0x0000 }, + { 0xcdc1, 0x0000 }, + { 0xcdc2, 0x0000 }, + { 0xcdc3, 0x0000 }, + { 0xcdc4, 0x0000 }, + { 0xcdc5, 0x0000 }, + { 0xcdc6, 0x0000 }, + { 0xcdc7, 0x0000 }, + { 0xcdc8, 0x0000 }, + { 0xcdc9, 0x0000 }, + { 0xcdca, 0x0000 }, + { 0xcdcb, 0x0000 }, + { 0xcdcc, 0x0000 }, + { 0xcdcd, 0x0000 }, + { 0xcdce, 0x0000 }, + { 0xcdcf, 0x0000 }, + { 0xcdd0, 0x0000 }, + { 0xcdd1, 0x0000 }, + { 0xcdd2, 0x0000 }, + { 0xcdd3, 0x0000 }, + { 0xcdd4, 0x0000 }, + { 0xcdd5, 0x0000 }, + { 0xcdd6, 0x0000 }, + { 0xcdd7, 0x0000 }, + { 0xcdd8, 0x0000 }, + { 0xcdd9, 0x0000 }, + { 0xcdda, 0x0000 }, + { 0xcddb, 0x0000 }, + { 0xcddc, 0x0000 }, + { 0xcddd, 0x0000 }, + { 0xcdde, 0x0000 }, + { 0xcddf, 0x0000 }, + { 0xcde0, 0x0000 }, + { 0xcde1, 0x0000 }, + { 0xcde2, 0x0000 }, + { 0xcde3, 0x0000 }, + { 0xcde4, 0x0000 }, + { 0xcde5, 0x0000 }, + { 0xcde6, 0x0000 }, + { 0xcde7, 0x0000 }, + { 0xcde8, 0x0000 }, + { 0xcde9, 0x0000 }, + { 0xcdea, 0x0000 }, + { 0xcdeb, 0x0000 }, + { 0xcdec, 0x0000 }, + { 0xcded, 0x0000 }, + { 0xcdee, 0x0000 }, + { 0xcdef, 0x0000 }, + { 0xcdf0, 0x0000 }, + { 0xcdf1, 0x0000 }, + { 0xcdf2, 0x0000 }, + { 0xcdf3, 0x0000 }, + { 0xcdf4, 0x0000 }, + { 0xcdf5, 0x0000 }, + { 0xcdf6, 0x0000 }, + { 0xcdf7, 0x0000 }, + { 0xcdf8, 0x0000 }, + { 0xcdf9, 0x0000 }, + { 0xcdfa, 0x0000 }, + { 0xcdfb, 0x0000 }, + { 0xcdfc, 0x0000 }, + { 0xcdfd, 0x0000 }, + { 0xcdfe, 0x0000 }, + { 0xcdff, 0x0000 }, + { 0xce00, 0x0000 }, + { 0xce01, 0x0000 }, + { 0xce02, 0x0000 }, + { 0xce03, 0x0000 }, + { 0xce04, 0x0000 }, + { 0xce05, 0x0000 }, + { 0xce06, 0x0000 }, + { 0xce07, 0x0000 }, + { 0xce08, 0x0000 }, + { 0xce09, 0x0000 }, + { 0xce0a, 0x0000 }, + { 0xce0b, 0x0000 }, + { 0xce0c, 0x0000 }, + { 0xce0d, 0x0000 }, + { 0xce0e, 0x0000 }, + { 0xce0f, 0x0000 }, + { 0xce10, 0x0000 }, + { 0xce11, 0x0000 }, + { 0xce12, 0x0000 }, + { 0xce13, 0x0000 }, + { 0xce14, 0x0000 }, + { 0xce15, 0x0000 }, + { 0xce16, 0x0000 }, + { 0xce17, 0x0000 }, + { 0xce18, 0x0000 }, + { 0xce19, 0x0000 }, + { 0xce1a, 0x0000 }, + { 0xce1b, 0x0000 }, + { 0xce1c, 0x0000 }, + { 0xce1d, 0x0000 }, + { 0xce1e, 0x0000 }, + { 0xce1f, 0x0000 }, + { 0xce20, 0x0000 }, + { 0xce21, 0x0000 }, + { 0xce22, 0x0000 }, + { 0xce23, 0x0000 }, + { 0xce24, 0x0000 }, + { 0xce25, 0x0000 }, + { 0xce26, 0x0000 }, + { 0xce27, 0x0000 }, + { 0xce28, 0x0000 }, + { 0xce29, 0x0000 }, + { 0xce2a, 0x0000 }, + { 0xce2b, 0x0000 }, + { 0xce2c, 0x0000 }, + { 0xce2d, 0x0000 }, + { 0xce2e, 0x0000 }, + { 0xce2f, 0x0000 }, + { 0xce30, 0x0000 }, + { 0xce31, 0x0000 }, + { 0xce32, 0x0000 }, + { 0xce33, 0x0000 }, + { 0xce34, 0x0000 }, + { 0xce35, 0x0000 }, + { 0xce36, 0x0000 }, + { 0xce37, 0x0000 }, + { 0xce38, 0x0000 }, + { 0xce39, 0x0000 }, + { 0xce3a, 0x0000 }, + { 0xce3b, 0x0000 }, + { 0xce3c, 0x0000 }, + { 0xce3d, 0x0000 }, + { 0xce3e, 0x0000 }, + { 0xce3f, 0x0000 }, + { 0xce40, 0x0000 }, + { 0xce41, 0x0000 }, + { 0xce42, 0x0000 }, + { 0xce43, 0x0000 }, + { 0xce44, 0x0000 }, + { 0xce45, 0x0000 }, + { 0xce46, 0x0000 }, + { 0xce47, 0x0000 }, + { 0xce48, 0x0000 }, + { 0xce49, 0x0000 }, + { 0xce4a, 0x0000 }, + { 0xce4b, 0x0000 }, + { 0xce4c, 0x0000 }, + { 0xce4d, 0x0000 }, + { 0xce4e, 0x0000 }, + { 0xce4f, 0x0000 }, + { 0xce50, 0x0000 }, + { 0xce51, 0x0000 }, + { 0xce52, 0x0000 }, + { 0xce53, 0x0000 }, + { 0xce54, 0x0000 }, + { 0xce55, 0x0000 }, + { 0xce56, 0x0000 }, + { 0xce57, 0x0000 }, + { 0xce58, 0x0000 }, + { 0xce59, 0x0000 }, + { 0xce5a, 0x0000 }, + { 0xce5b, 0x0000 }, + { 0xce5c, 0x0000 }, + { 0xce5d, 0x0000 }, + { 0xce5e, 0x0000 }, + { 0xce5f, 0x0000 }, + { 0xce60, 0x0000 }, + { 0xce61, 0x0000 }, + { 0xce62, 0x0000 }, + { 0xce63, 0x0000 }, + { 0xce64, 0x0000 }, + { 0xce65, 0x0000 }, + { 0xce66, 0x0000 }, + { 0xce67, 0x0000 }, + { 0xce68, 0x0000 }, + { 0xce69, 0x0000 }, + { 0xce6a, 0x0000 }, + { 0xce6b, 0x0000 }, + { 0xce6c, 0x0000 }, + { 0xce6d, 0x0000 }, + { 0xce6e, 0x0000 }, + { 0xce6f, 0x0000 }, + { 0xce70, 0x0000 }, + { 0xce71, 0x0000 }, + { 0xce72, 0x0000 }, + { 0xce73, 0x0000 }, + { 0xce74, 0x0000 }, + { 0xce75, 0x0000 }, + { 0xce76, 0x0000 }, + { 0xce77, 0x0000 }, + { 0xce78, 0x0000 }, + { 0xce79, 0x0000 }, + { 0xce7a, 0x0000 }, + { 0xce7b, 0x0000 }, + { 0xce7c, 0x0000 }, + { 0xce7d, 0x0000 }, + { 0xce7e, 0x0000 }, + { 0xce7f, 0x0000 }, + { 0xce80, 0x0000 }, + { 0xce81, 0x0000 }, + { 0xce82, 0x0000 }, + { 0xce83, 0x0000 }, + { 0xce84, 0x0000 }, + { 0xce85, 0x0000 }, + { 0xce86, 0x0000 }, + { 0xce87, 0x0000 }, + { 0xce88, 0x0000 }, + { 0xce89, 0x0000 }, + { 0xce8a, 0x0000 }, + { 0xce8b, 0x0000 }, + { 0xce8c, 0x0000 }, + { 0xce8d, 0x0000 }, + { 0xce8e, 0x0000 }, + { 0xce8f, 0x0000 }, + { 0xce90, 0x0000 }, + { 0xce91, 0x0000 }, + { 0xce92, 0x0000 }, + { 0xce93, 0x0000 }, + { 0xce94, 0x0000 }, + { 0xce95, 0x0000 }, + { 0xce96, 0x0000 }, + { 0xce97, 0x0000 }, + { 0xce98, 0x0000 }, + { 0xce99, 0x0000 }, + { 0xce9a, 0x0000 }, + { 0xce9b, 0x0000 }, + { 0xce9c, 0x0000 }, + { 0xce9d, 0x0000 }, + { 0xce9e, 0x0000 }, + { 0xce9f, 0x0000 }, + { 0xcea0, 0x0000 }, + { 0xcea1, 0x0000 }, + { 0xcea2, 0x0000 }, + { 0xcea3, 0x0000 }, + { 0xcea4, 0x0000 }, + { 0xcea5, 0x0000 }, + { 0xcea6, 0x0000 }, + { 0xcea7, 0x0000 }, + { 0xcea8, 0x0000 }, + { 0xcea9, 0x0000 }, + { 0xceaa, 0x0000 }, + { 0xceab, 0x0000 }, + { 0xceac, 0x0000 }, + { 0xcead, 0x0000 }, + { 0xceae, 0x0000 }, + { 0xceaf, 0x0000 }, + { 0xceb0, 0x0000 }, + { 0xceb1, 0x0000 }, + { 0xceb2, 0x0000 }, + { 0xceb3, 0x0000 }, + { 0xceb4, 0x0000 }, + { 0xceb5, 0x0000 }, + { 0xceb6, 0x0000 }, + { 0xceb7, 0x0000 }, + { 0xceb8, 0x0000 }, + { 0xceb9, 0x0000 }, + { 0xceba, 0x0000 }, + { 0xcebb, 0x0000 }, + { 0xcebc, 0x0000 }, + { 0xcebd, 0x0000 }, + { 0xcebe, 0x0000 }, + { 0xcebf, 0x0000 }, + { 0xcec0, 0x0000 }, + { 0xcec1, 0x0000 }, + { 0xcec2, 0x0000 }, + { 0xcec3, 0x0000 }, + { 0xcec4, 0x0000 }, + { 0xcec5, 0x0000 }, + { 0xcec6, 0x0000 }, + { 0xcec7, 0x0000 }, + { 0xcec8, 0x0000 }, + { 0xcec9, 0x0000 }, + { 0xceca, 0x0000 }, + { 0xcecb, 0x0000 }, + { 0xcecc, 0x0000 }, + { 0xcecd, 0x0000 }, + { 0xcece, 0x0000 }, + { 0xcecf, 0x0000 }, + { 0xced0, 0x0000 }, + { 0xced1, 0x0000 }, + { 0xced2, 0x0000 }, + { 0xced3, 0x0000 }, + { 0xced4, 0x0000 }, + { 0xced5, 0x0000 }, + { 0xced6, 0x0000 }, + { 0xced7, 0x0000 }, + { 0xced8, 0x0000 }, + { 0xced9, 0x0000 }, + { 0xceda, 0x0000 }, + { 0xcedb, 0x0000 }, + { 0xcedc, 0x0000 }, + { 0xcedd, 0x0000 }, + { 0xcede, 0x0000 }, + { 0xcedf, 0x0000 }, + { 0xcee0, 0x0000 }, + { 0xcee1, 0x0000 }, + { 0xcee2, 0x0000 }, + { 0xcee3, 0x0000 }, + { 0xcee4, 0x0000 }, + { 0xcee5, 0x0000 }, + { 0xcee6, 0x0000 }, + { 0xcee7, 0x0000 }, + { 0xcee8, 0x0000 }, + { 0xcee9, 0x0000 }, + { 0xceea, 0x0000 }, + { 0xceeb, 0x0000 }, + { 0xceec, 0x0000 }, + { 0xceed, 0x0000 }, + { 0xceee, 0x0000 }, + { 0xceef, 0x0000 }, + { 0xcef0, 0x0000 }, + { 0xcef1, 0x0000 }, + { 0xcef2, 0x0000 }, + { 0xcef3, 0x0000 }, + { 0xcef4, 0x0000 }, + { 0xcef5, 0x0000 }, + { 0xcef6, 0x0000 }, + { 0xcef7, 0x0000 }, + { 0xcef8, 0x0000 }, + { 0xcef9, 0x0000 }, + { 0xcefa, 0x0000 }, + { 0xcefb, 0x0000 }, + { 0xcefc, 0x0000 }, + { 0xcefd, 0x0000 }, + { 0xcefe, 0x0000 }, + { 0xceff, 0x0000 }, + { 0xcf00, 0x0000 }, + { 0xcf01, 0x0000 }, + { 0xcf02, 0x0000 }, + { 0xcf03, 0x0000 }, + { 0xcf04, 0x0000 }, + { 0xcf05, 0x0000 }, + { 0xcf06, 0x0000 }, + { 0xcf07, 0x0000 }, + { 0xcf08, 0x0000 }, + { 0xcf09, 0x0000 }, + { 0xcf0a, 0x0000 }, + { 0xcf0b, 0x0000 }, + { 0xcf0c, 0x0000 }, + { 0xcf0d, 0x0000 }, + { 0xcf0e, 0x0000 }, + { 0xcf0f, 0x0000 }, + { 0xcf10, 0x0000 }, + { 0xcf11, 0x0000 }, + { 0xcf12, 0x0000 }, + { 0xcf13, 0x0000 }, + { 0xcf14, 0x0000 }, + { 0xcf15, 0x0000 }, + { 0xcf16, 0x0000 }, + { 0xcf17, 0x0000 }, + { 0xcf18, 0x0000 }, + { 0xcf19, 0x0000 }, + { 0xcf1a, 0x0000 }, + { 0xcf1b, 0x0000 }, + { 0xcf1c, 0x0000 }, + { 0xcf1d, 0x0000 }, + { 0xcf1e, 0x0000 }, + { 0xcf1f, 0x0000 }, + { 0xcf20, 0x0000 }, + { 0xcf21, 0x0000 }, + { 0xcf22, 0x0000 }, + { 0xcf23, 0x0000 }, + { 0xcf24, 0x0000 }, + { 0xcf25, 0x0000 }, + { 0xcf26, 0x0000 }, + { 0xcf27, 0x0000 }, + { 0xcf28, 0x0000 }, + { 0xcf29, 0x0000 }, + { 0xcf2a, 0x0000 }, + { 0xcf2b, 0x0000 }, + { 0xcf2c, 0x0000 }, + { 0xcf2d, 0x0000 }, + { 0xcf2e, 0x0000 }, + { 0xcf2f, 0x0000 }, + { 0xcf30, 0x0000 }, + { 0xcf31, 0x0000 }, + { 0xcf32, 0x0000 }, + { 0xcf33, 0x0000 }, + { 0xcf34, 0x0000 }, + { 0xcf35, 0x0000 }, + { 0xcf36, 0x0000 }, + { 0xcf37, 0x0000 }, + { 0xcf38, 0x0000 }, + { 0xcf39, 0x0000 }, + { 0xcf3a, 0x0000 }, + { 0xcf3b, 0x0000 }, + { 0xcf3c, 0x0000 }, + { 0xcf3d, 0x0000 }, + { 0xcf3e, 0x0000 }, + { 0xcf3f, 0x0000 }, + { 0xcf40, 0x0000 }, + { 0xcf41, 0x0000 }, + { 0xcf42, 0x0000 }, + { 0xcf43, 0x0000 }, + { 0xcf44, 0x0000 }, + { 0xcf45, 0x0000 }, + { 0xcf46, 0x0000 }, + { 0xcf47, 0x0000 }, + { 0xcf48, 0x0000 }, + { 0xcf49, 0x0000 }, + { 0xcf4a, 0x0000 }, + { 0xcf4b, 0x0000 }, + { 0xcf4c, 0x0000 }, + { 0xcf4d, 0x0000 }, + { 0xcf4e, 0x0000 }, + { 0xcf4f, 0x0000 }, + { 0xcf50, 0x0000 }, + { 0xcf51, 0x0000 }, + { 0xcf52, 0x0000 }, + { 0xcf53, 0x0000 }, + { 0xcf54, 0x0000 }, + { 0xcf55, 0x0000 }, + { 0xcf56, 0x0000 }, + { 0xcf57, 0x0000 }, + { 0xcf58, 0x0000 }, + { 0xcf59, 0x0000 }, + { 0xcf5a, 0x0000 }, + { 0xcf5b, 0x0000 }, + { 0xcf5c, 0x0000 }, + { 0xcf5d, 0x0000 }, + { 0xcf5e, 0x0000 }, + { 0xcf5f, 0x0000 }, + { 0xcf60, 0x0000 }, + { 0xcf61, 0x0000 }, + { 0xcf62, 0x0000 }, + { 0xcf63, 0x0000 }, + { 0xcf64, 0x0000 }, + { 0xcf65, 0x0000 }, + { 0xcf66, 0x0000 }, + { 0xcf67, 0x0000 }, + { 0xcf68, 0x0000 }, + { 0xcf69, 0x0000 }, + { 0xcf6a, 0x0000 }, + { 0xcf6b, 0x0000 }, + { 0xcf6c, 0x0000 }, + { 0xcf6d, 0x0000 }, + { 0xcf6e, 0x0000 }, + { 0xcf6f, 0x0000 }, + { 0xcf70, 0x0000 }, + { 0xcf71, 0x0000 }, + { 0xcf72, 0x0000 }, + { 0xcf73, 0x0000 }, + { 0xcf74, 0x0000 }, + { 0xcf75, 0x0000 }, + { 0xcf76, 0x0000 }, + { 0xcf77, 0x0000 }, + { 0xcf78, 0x0000 }, + { 0xcf79, 0x0000 }, + { 0xcf7a, 0x0000 }, + { 0xcf7b, 0x0000 }, + { 0xcf7c, 0x0000 }, + { 0xcf7d, 0x0000 }, + { 0xcf7e, 0x0000 }, + { 0xcf7f, 0x0000 }, + { 0xcf80, 0x0000 }, + { 0xcf81, 0x0000 }, + { 0xcf82, 0x0000 }, + { 0xcf83, 0x0000 }, + { 0xcf84, 0x0000 }, + { 0xcf85, 0x0000 }, + { 0xcf86, 0x0000 }, + { 0xcf87, 0x0000 }, + { 0xcf88, 0x0000 }, + { 0xcf89, 0x0000 }, + { 0xcf8a, 0x0000 }, + { 0xcf8b, 0x0000 }, + { 0xcf8c, 0x0000 }, + { 0xcf8d, 0x0000 }, + { 0xcf8e, 0x0000 }, + { 0xcf8f, 0x0000 }, + { 0xcf90, 0x0000 }, + { 0xcf91, 0x0000 }, + { 0xcf92, 0x0000 }, + { 0xcf93, 0x0000 }, + { 0xcf94, 0x0000 }, + { 0xcf95, 0x0000 }, + { 0xcf96, 0x0000 }, + { 0xcf97, 0x0000 }, + { 0xcf98, 0x0000 }, + { 0xcf99, 0x0000 }, + { 0xcf9a, 0x0000 }, + { 0xcf9b, 0x0000 }, + { 0xcf9c, 0x0000 }, + { 0xcf9d, 0x0000 }, + { 0xcf9e, 0x0000 }, + { 0xcf9f, 0x0000 }, + { 0xcfa0, 0x0000 }, + { 0xcfa1, 0x0000 }, + { 0xcfa2, 0x0000 }, + { 0xcfa3, 0x0000 }, + { 0xcfa4, 0x0000 }, + { 0xcfa5, 0x0000 }, + { 0xcfa6, 0x0000 }, + { 0xcfa7, 0x0000 }, + { 0xcfa8, 0x0000 }, + { 0xcfa9, 0x0000 }, + { 0xcfaa, 0x0000 }, + { 0xcfab, 0x0000 }, + { 0xcfac, 0x0000 }, + { 0xcfad, 0x0000 }, + { 0xcfae, 0x0000 }, + { 0xcfaf, 0x0000 }, + { 0xcfb0, 0x0000 }, + { 0xcfb1, 0x0000 }, + { 0xcfb2, 0x0000 }, + { 0xcfb3, 0x0000 }, + { 0xcfb4, 0x0000 }, + { 0xcfb5, 0x0000 }, + { 0xcfb6, 0x0000 }, + { 0xcfb7, 0x0000 }, + { 0xcfb8, 0x0000 }, + { 0xcfb9, 0x0000 }, + { 0xcfba, 0x0000 }, + { 0xcfbb, 0x0000 }, + { 0xcfbc, 0x0000 }, + { 0xcfbd, 0x0000 }, + { 0xcfbe, 0x0000 }, + { 0xcfbf, 0x0000 }, + { 0xcfc0, 0x0000 }, + { 0xcfc1, 0x0000 }, + { 0xcfc2, 0x0000 }, + { 0xcfc3, 0x0000 }, + { 0xcfc4, 0x0000 }, + { 0xcfc5, 0x0000 }, + { 0xcfc6, 0x0000 }, + { 0xcfc7, 0x0000 }, + { 0xcfc8, 0x0000 }, + { 0xcfc9, 0x0000 }, + { 0xcfca, 0x0000 }, + { 0xcfcb, 0x0000 }, + { 0xcfcc, 0x0000 }, + { 0xcfcd, 0x0000 }, + { 0xcfce, 0x0000 }, + { 0xcfcf, 0x0000 }, + { 0xcfd0, 0x0000 }, + { 0xcfd1, 0x0000 }, + { 0xcfd2, 0x0000 }, + { 0xcfd3, 0x0000 }, + { 0xcfd4, 0x0000 }, + { 0xcfd5, 0x0000 }, + { 0xcfd6, 0x0000 }, + { 0xcfd7, 0x0000 }, + { 0xcfd8, 0x0000 }, + { 0xcfd9, 0x0000 }, + { 0xcfda, 0x0000 }, + { 0xcfdb, 0x0000 }, + { 0xcfdc, 0x0000 }, + { 0xcfdd, 0x0000 }, + { 0xcfde, 0x0000 }, + { 0xcfdf, 0x0000 }, + { 0xcfe0, 0x0000 }, + { 0xcfe1, 0x0000 }, + { 0xcfe2, 0x0000 }, + { 0xcfe3, 0x0000 }, + { 0xcfe4, 0x0000 }, + { 0xcfe5, 0x0000 }, + { 0xcfe6, 0x0000 }, + { 0xcfe7, 0x0000 }, + { 0xcfe8, 0x0000 }, + { 0xcfe9, 0x0000 }, + { 0xcfea, 0x0000 }, + { 0xcfeb, 0x0000 }, + { 0xcfec, 0x0000 }, + { 0xcfed, 0x0000 }, + { 0xcfee, 0x0000 }, + { 0xcfef, 0x0000 }, + { 0xcff0, 0x0000 }, + { 0xcff1, 0x0000 }, + { 0xcff2, 0x0000 }, + { 0xcff3, 0x0000 }, + { 0xcff4, 0x0000 }, + { 0xcff5, 0x0000 }, + { 0xcff6, 0x0000 }, + { 0xcff7, 0x0000 }, + { 0xcff8, 0x0000 }, + { 0xcff9, 0x0000 }, + { 0xcffa, 0x0000 }, + { 0xcffb, 0x0000 }, + { 0xcffc, 0x0000 }, + { 0xcffd, 0x0000 }, + { 0xcffe, 0x0000 }, + { 0xcfff, 0x0000 }, + { 0xd000, 0x0000 }, + { 0xd001, 0x0000 }, + { 0xd002, 0x0000 }, + { 0xd003, 0x0000 }, + { 0xd004, 0x0000 }, + { 0xd005, 0x0000 }, + { 0xd006, 0x0000 }, + { 0xd007, 0x0000 }, + { 0xd008, 0x0000 }, + { 0xd009, 0x0000 }, + { 0xd00a, 0x0000 }, + { 0xd00b, 0x0000 }, + { 0xd00c, 0x0000 }, + { 0xd00d, 0x0000 }, + { 0xd00e, 0x0000 }, + { 0xd00f, 0x0000 }, + { 0xd010, 0x0000 }, + { 0xd011, 0x0000 }, + { 0xd012, 0x0000 }, + { 0xd013, 0x0000 }, + { 0xd014, 0x0000 }, + { 0xd015, 0x0000 }, + { 0xd016, 0x0000 }, + { 0xd017, 0x0000 }, + { 0xd018, 0x0000 }, + { 0xd019, 0x0000 }, + { 0xd01a, 0x0000 }, + { 0xd01b, 0x0000 }, + { 0xd01c, 0x0000 }, + { 0xd01d, 0x0000 }, + { 0xd01e, 0x0000 }, + { 0xd01f, 0x0000 }, + { 0xd020, 0x0000 }, + { 0xd021, 0x0000 }, + { 0xd022, 0x0000 }, + { 0xd023, 0x0000 }, + { 0xd024, 0x0000 }, + { 0xd025, 0x0000 }, + { 0xd026, 0x0000 }, + { 0xd027, 0x0000 }, + { 0xd028, 0x0000 }, + { 0xd029, 0x0000 }, + { 0xd02a, 0x0000 }, + { 0xd02b, 0x0000 }, + { 0xd02c, 0x0000 }, + { 0xd02d, 0x0000 }, + { 0xd02e, 0x0000 }, + { 0xd02f, 0x0000 }, + { 0xd030, 0x0000 }, + { 0xd031, 0x0000 }, + { 0xd032, 0x0000 }, + { 0xd033, 0x0000 }, + { 0xd034, 0x0000 }, + { 0xd035, 0x0000 }, + { 0xd036, 0x0000 }, + { 0xd037, 0x0000 }, + { 0xd038, 0x0000 }, + { 0xd039, 0x0000 }, + { 0xd03a, 0x0000 }, + { 0xd03b, 0x0000 }, + { 0xd03c, 0x0000 }, + { 0xd03d, 0x0000 }, + { 0xd03e, 0x0000 }, + { 0xd03f, 0x0000 }, + { 0xd040, 0x0000 }, + { 0xd041, 0x0000 }, + { 0xd042, 0x0000 }, + { 0xd043, 0x0000 }, + { 0xd044, 0x0000 }, + { 0xd045, 0x0000 }, + { 0xd046, 0x0000 }, + { 0xd047, 0x0000 }, + { 0xd048, 0x0000 }, + { 0xd049, 0x0000 }, + { 0xd04a, 0x0000 }, + { 0xd04b, 0x0000 }, + { 0xd04c, 0x0000 }, + { 0xd04d, 0x0000 }, + { 0xd04e, 0x0000 }, + { 0xd04f, 0x0000 }, + { 0xd050, 0x0000 }, + { 0xd051, 0x0000 }, + { 0xd052, 0x0000 }, + { 0xd053, 0x0000 }, + { 0xd054, 0x0000 }, + { 0xd055, 0x0000 }, + { 0xd056, 0x0000 }, + { 0xd057, 0x0000 }, + { 0xd058, 0x0000 }, + { 0xd059, 0x0000 }, + { 0xd05a, 0x0000 }, + { 0xd05b, 0x0000 }, + { 0xd05c, 0x0000 }, + { 0xd05d, 0x0000 }, + { 0xd05e, 0x0000 }, + { 0xd05f, 0x0000 }, + { 0xd060, 0x0000 }, + { 0xd061, 0x0000 }, + { 0xd062, 0x0000 }, + { 0xd063, 0x0000 }, + { 0xd064, 0x0000 }, + { 0xd065, 0x0000 }, + { 0xd066, 0x0000 }, + { 0xd067, 0x0000 }, + { 0xd068, 0x0000 }, + { 0xd069, 0x0000 }, + { 0xd06a, 0x0000 }, + { 0xd06b, 0x0000 }, + { 0xd06c, 0x0000 }, + { 0xd06d, 0x0000 }, + { 0xd06e, 0x0000 }, + { 0xd06f, 0x0000 }, + { 0xd070, 0x0000 }, + { 0xd071, 0x0000 }, + { 0xd072, 0x0000 }, + { 0xd073, 0x0000 }, + { 0xd074, 0x0000 }, + { 0xd075, 0x0000 }, + { 0xd076, 0x0000 }, + { 0xd077, 0x0000 }, + { 0xd078, 0x0000 }, + { 0xd079, 0x0000 }, + { 0xd07a, 0x0000 }, + { 0xd07b, 0x0000 }, + { 0xd07c, 0x0000 }, + { 0xd07d, 0x0000 }, + { 0xd07e, 0x0000 }, + { 0xd07f, 0x0000 }, + { 0xd080, 0x0000 }, + { 0xd081, 0x0000 }, + { 0xd082, 0x0000 }, + { 0xd083, 0x0000 }, + { 0xd084, 0x0000 }, + { 0xd085, 0x0000 }, + { 0xd086, 0x0000 }, + { 0xd087, 0x0000 }, + { 0xd088, 0x0000 }, + { 0xd089, 0x0000 }, + { 0xd08a, 0x0000 }, + { 0xd08b, 0x0000 }, + { 0xd08c, 0x0000 }, + { 0xd08d, 0x0000 }, + { 0xd08e, 0x0000 }, + { 0xd08f, 0x0000 }, + { 0xd090, 0x0000 }, + { 0xd091, 0x0000 }, + { 0xd092, 0x0000 }, + { 0xd093, 0x0000 }, + { 0xd094, 0x0000 }, + { 0xd095, 0x0000 }, + { 0xd096, 0x0000 }, + { 0xd097, 0x0000 }, + { 0xd098, 0x0000 }, + { 0xd099, 0x0000 }, + { 0xd09a, 0x0000 }, + { 0xd09b, 0x0000 }, + { 0xd09c, 0x0000 }, + { 0xd09d, 0x0000 }, + { 0xd09e, 0x0000 }, + { 0xd09f, 0x0000 }, + { 0xd0a0, 0x0000 }, + { 0xd0a1, 0x0000 }, + { 0xd0a2, 0x0000 }, + { 0xd0a3, 0x0000 }, + { 0xd0a4, 0x0000 }, + { 0xd0a5, 0x0000 }, + { 0xd0a6, 0x0000 }, + { 0xd0a7, 0x0000 }, + { 0xd0a8, 0x0000 }, + { 0xd0a9, 0x0000 }, + { 0xd0aa, 0x0000 }, + { 0xd0ab, 0x0000 }, + { 0xd0ac, 0x0000 }, + { 0xd0ad, 0x0000 }, + { 0xd0ae, 0x0000 }, + { 0xd0af, 0x0000 }, + { 0xd0b0, 0x0000 }, + { 0xd0b1, 0x0000 }, + { 0xd0b2, 0x0000 }, + { 0xd0b3, 0x0000 }, + { 0xd0b4, 0x0000 }, + { 0xd0b5, 0x0000 }, + { 0xd0b6, 0x0000 }, + { 0xd0b7, 0x0000 }, + { 0xd0b8, 0x0000 }, + { 0xd0b9, 0x0000 }, + { 0xd0ba, 0x0000 }, + { 0xd0bb, 0x0000 }, + { 0xd0bc, 0x0000 }, + { 0xd0bd, 0x0000 }, + { 0xd0be, 0x0000 }, + { 0xd0bf, 0x0000 }, + { 0xd0c0, 0x0000 }, + { 0xd0c1, 0x0000 }, + { 0xd0c2, 0x0000 }, + { 0xd0c3, 0x0000 }, + { 0xd0c4, 0x0000 }, + { 0xd0c5, 0x0000 }, + { 0xd0c6, 0x0000 }, + { 0xd0c7, 0x0000 }, + { 0xd0c8, 0x0000 }, + { 0xd0c9, 0x0000 }, + { 0xd0ca, 0x0000 }, + { 0xd0cb, 0x0000 }, + { 0xd0cc, 0x0000 }, + { 0xd0cd, 0x0000 }, + { 0xd0ce, 0x0000 }, + { 0xd0cf, 0x0000 }, + { 0xd0d0, 0x0000 }, + { 0xd0d1, 0x0000 }, + { 0xd0d2, 0x0000 }, + { 0xd0d3, 0x0000 }, + { 0xd0d4, 0x0000 }, + { 0xd0d5, 0x0000 }, + { 0xd0d6, 0x0000 }, + { 0xd0d7, 0x0000 }, + { 0xd0d8, 0x0000 }, + { 0xd0d9, 0x0000 }, + { 0xd0da, 0x0000 }, + { 0xd0db, 0x0000 }, + { 0xd0dc, 0x0000 }, + { 0xd0dd, 0x0000 }, + { 0xd0de, 0x0000 }, + { 0xd0df, 0x0000 }, + { 0xd0e0, 0x0000 }, + { 0xd0e1, 0x0000 }, + { 0xd0e2, 0x0000 }, + { 0xd0e3, 0x0000 }, + { 0xd0e4, 0x0000 }, + { 0xd0e5, 0x0000 }, + { 0xd0e6, 0x0000 }, + { 0xd0e7, 0x0000 }, + { 0xd0e8, 0x0000 }, + { 0xd0e9, 0x0000 }, + { 0xd0ea, 0x0000 }, + { 0xd0eb, 0x0000 }, + { 0xd0ec, 0x0000 }, + { 0xd0ed, 0x0000 }, + { 0xd0ee, 0x0000 }, + { 0xd0ef, 0x0000 }, + { 0xd0f0, 0x0000 }, + { 0xd0f1, 0x0000 }, + { 0xd0f2, 0x0000 }, + { 0xd0f3, 0x0000 }, + { 0xd0f4, 0x0000 }, + { 0xd0f5, 0x0000 }, + { 0xd0f6, 0x0000 }, + { 0xd0f7, 0x0000 }, + { 0xd0f8, 0x0000 }, + { 0xd0f9, 0x0000 }, + { 0xd0fa, 0x0000 }, + { 0xd0fb, 0x0000 }, + { 0xd0fc, 0x0000 }, + { 0xd0fd, 0x0000 }, + { 0xd0fe, 0x0000 }, + { 0xd0ff, 0x0000 }, + { 0xd100, 0x0000 }, + { 0xd101, 0x0000 }, + { 0xd102, 0x0000 }, + { 0xd103, 0x0000 }, + { 0xd104, 0x0000 }, + { 0xd105, 0x0000 }, + { 0xd106, 0x0000 }, + { 0xd107, 0x0000 }, + { 0xd108, 0x0000 }, + { 0xd109, 0x0000 }, + { 0xd10a, 0x0000 }, + { 0xd10b, 0x0000 }, + { 0xd10c, 0x0000 }, + { 0xd10d, 0x0000 }, + { 0xd10e, 0x0000 }, + { 0xd10f, 0x0000 }, + { 0xd110, 0x0000 }, + { 0xd111, 0x0000 }, + { 0xd112, 0x0000 }, + { 0xd113, 0x0000 }, + { 0xd114, 0x0000 }, + { 0xd115, 0x0000 }, + { 0xd116, 0x0000 }, + { 0xd117, 0x0000 }, + { 0xd118, 0x0000 }, + { 0xd119, 0x0000 }, + { 0xd11a, 0x0000 }, + { 0xd11b, 0x0000 }, + { 0xd11c, 0x0000 }, + { 0xd11d, 0x0000 }, + { 0xd11e, 0x0000 }, + { 0xd11f, 0x0000 }, + { 0xd120, 0x0000 }, + { 0xd121, 0x0000 }, + { 0xd122, 0x0000 }, + { 0xd123, 0x0000 }, + { 0xd124, 0x0000 }, + { 0xd125, 0x0000 }, + { 0xd126, 0x0000 }, + { 0xd127, 0x0000 }, + { 0xd128, 0x0000 }, + { 0xd129, 0x0000 }, + { 0xd12a, 0x0000 }, + { 0xd12b, 0x0000 }, + { 0xd12c, 0x0000 }, + { 0xd12d, 0x0000 }, + { 0xd12e, 0x0000 }, + { 0xd12f, 0x0000 }, + { 0xd130, 0x0000 }, + { 0xd131, 0x0000 }, + { 0xd132, 0x0000 }, + { 0xd133, 0x0000 }, + { 0xd134, 0x0000 }, + { 0xd135, 0x0000 }, + { 0xd136, 0x0000 }, + { 0xd137, 0x0000 }, + { 0xd138, 0x0000 }, + { 0xd139, 0x0000 }, + { 0xd13a, 0x0000 }, + { 0xd13b, 0x0000 }, + { 0xd13c, 0x0000 }, + { 0xd13d, 0x0000 }, + { 0xd13e, 0x0000 }, + { 0xd13f, 0x0000 }, + { 0xd140, 0x0000 }, + { 0xd141, 0x0000 }, + { 0xd142, 0x0000 }, + { 0xd143, 0x0000 }, + { 0xd144, 0x0000 }, + { 0xd145, 0x0000 }, + { 0xd146, 0x0000 }, + { 0xd147, 0x0000 }, + { 0xd148, 0x0000 }, + { 0xd149, 0x0000 }, + { 0xd14a, 0x0000 }, + { 0xd14b, 0x0000 }, + { 0xd14c, 0x0000 }, + { 0xd14d, 0x0000 }, + { 0xd14e, 0x0000 }, + { 0xd14f, 0x0000 }, + { 0xd150, 0x0000 }, + { 0xd151, 0x0000 }, + { 0xd152, 0x0000 }, + { 0xd153, 0x0000 }, + { 0xd154, 0x0000 }, + { 0xd155, 0x0000 }, + { 0xd156, 0x0000 }, + { 0xd157, 0x0000 }, + { 0xd158, 0x0000 }, + { 0xd159, 0x0000 }, + { 0xd15a, 0x0000 }, + { 0xd15b, 0x0000 }, + { 0xd15c, 0x0000 }, + { 0xd15d, 0x0000 }, + { 0xd15e, 0x0000 }, + { 0xd15f, 0x0000 }, + { 0xd160, 0x0000 }, + { 0xd161, 0x0000 }, + { 0xd162, 0x0000 }, + { 0xd163, 0x0000 }, + { 0xd164, 0x0000 }, + { 0xd165, 0x0000 }, + { 0xd166, 0x0000 }, + { 0xd167, 0x0000 }, + { 0xd168, 0x0000 }, + { 0xd169, 0x0000 }, + { 0xd16a, 0x0000 }, + { 0xd16b, 0x0000 }, + { 0xd16c, 0x0000 }, + { 0xd16d, 0x0000 }, + { 0xd16e, 0x0000 }, + { 0xd16f, 0x0000 }, + { 0xd170, 0x0000 }, + { 0xd171, 0x0000 }, + { 0xd172, 0x0000 }, + { 0xd173, 0x0000 }, + { 0xd174, 0x0000 }, + { 0xd175, 0x0000 }, + { 0xd176, 0x0000 }, + { 0xd177, 0x0000 }, + { 0xd178, 0x0000 }, + { 0xd179, 0x0000 }, + { 0xd17a, 0x0000 }, + { 0xd17b, 0x0000 }, + { 0xd17c, 0x0000 }, + { 0xd17d, 0x0000 }, + { 0xd17e, 0x0000 }, + { 0xd17f, 0x0000 }, + { 0xd180, 0x0000 }, + { 0xd181, 0x0000 }, + { 0xd182, 0x0000 }, + { 0xd183, 0x0000 }, + { 0xd184, 0x0000 }, + { 0xd185, 0x0000 }, + { 0xd186, 0x0000 }, + { 0xd187, 0x0000 }, + { 0xd188, 0x0000 }, + { 0xd189, 0x0000 }, + { 0xd18a, 0x0000 }, + { 0xd18b, 0x0000 }, + { 0xd18c, 0x0000 }, + { 0xd18d, 0x0000 }, + { 0xd18e, 0x0000 }, + { 0xd18f, 0x0000 }, + { 0xd190, 0x0000 }, + { 0xd191, 0x0000 }, + { 0xd192, 0x0000 }, + { 0xd193, 0x0000 }, + { 0xd194, 0x0000 }, + { 0xd195, 0x0000 }, + { 0xd196, 0x0000 }, + { 0xd197, 0x0000 }, + { 0xd198, 0x0000 }, + { 0xd199, 0x0000 }, + { 0xd19a, 0x0000 }, + { 0xd19b, 0x0000 }, + { 0xd19c, 0x0000 }, + { 0xd19d, 0x0000 }, + { 0xd19e, 0x0000 }, + { 0xd19f, 0x0000 }, + { 0xd1a0, 0x0000 }, + { 0xd1a1, 0x0000 }, + { 0xd1a2, 0x0000 }, + { 0xd1a3, 0x0000 }, + { 0xd1a4, 0x0000 }, + { 0xd1a5, 0x0000 }, + { 0xd1a6, 0x0000 }, + { 0xd1a7, 0x0000 }, + { 0xd1a8, 0x0000 }, + { 0xd1a9, 0x0000 }, + { 0xd1aa, 0x0000 }, + { 0xd1ab, 0x0000 }, + { 0xd1ac, 0x0000 }, + { 0xd1ad, 0x0000 }, + { 0xd1ae, 0x0000 }, + { 0xd1af, 0x0000 }, + { 0xd1b0, 0x0000 }, + { 0xd1b1, 0x0000 }, + { 0xd1b2, 0x0000 }, + { 0xd1b3, 0x0000 }, + { 0xd1b4, 0x0000 }, + { 0xd1b5, 0x0000 }, + { 0xd1b6, 0x0000 }, + { 0xd1b7, 0x0000 }, + { 0xd1b8, 0x0000 }, + { 0xd1b9, 0x0000 }, + { 0xd1ba, 0x0000 }, + { 0xd1bb, 0x0000 }, + { 0xd1bc, 0x0000 }, + { 0xd1bd, 0x0000 }, + { 0xd1be, 0x0000 }, + { 0xd1bf, 0x0000 }, + { 0xd1c0, 0x0000 }, + { 0xd1c1, 0x0000 }, + { 0xd1c2, 0x0000 }, + { 0xd1c3, 0x0000 }, + { 0xd1c4, 0x0000 }, + { 0xd1c5, 0x0000 }, + { 0xd1c6, 0x0000 }, + { 0xd1c7, 0x0000 }, + { 0xd1c8, 0x0000 }, + { 0xd1c9, 0x0000 }, + { 0xd1ca, 0x0000 }, + { 0xd1cb, 0x0000 }, + { 0xd1cc, 0x0000 }, + { 0xd1cd, 0x0000 }, + { 0xd1ce, 0x0000 }, + { 0xd1cf, 0x0000 }, + { 0xd1d0, 0x0000 }, + { 0xd1d1, 0x0000 }, + { 0xd1d2, 0x0000 }, + { 0xd1d3, 0x0000 }, + { 0xd1d4, 0x0000 }, + { 0xd1d5, 0x0000 }, + { 0xd1d6, 0x0000 }, + { 0xd1d7, 0x0000 }, + { 0xd1d8, 0x0000 }, + { 0xd1d9, 0x0000 }, + { 0xd1da, 0x0000 }, + { 0xd1db, 0x0000 }, + { 0xd1dc, 0x0000 }, + { 0xd1dd, 0x0000 }, + { 0xd1de, 0x0000 }, + { 0xd1df, 0x0000 }, + { 0xd1e0, 0x0000 }, + { 0xd1e1, 0x0000 }, + { 0xd1e2, 0x0000 }, + { 0xd1e3, 0x0000 }, + { 0xd1e4, 0x0000 }, + { 0xd1e5, 0x0000 }, + { 0xd1e6, 0x0000 }, + { 0xd1e7, 0x0000 }, + { 0xd1e8, 0x0000 }, + { 0xd1e9, 0x0000 }, + { 0xd1ea, 0x0000 }, + { 0xd1eb, 0x0000 }, + { 0xd1ec, 0x0000 }, + { 0xd1ed, 0x0000 }, + { 0xd1ee, 0x0000 }, + { 0xd1ef, 0x0000 }, + { 0xd1f0, 0x0000 }, + { 0xd1f1, 0x0000 }, + { 0xd1f2, 0x0000 }, + { 0xd1f3, 0x0000 }, + { 0xd1f4, 0x0000 }, + { 0xd1f5, 0x0000 }, + { 0xd1f6, 0x0000 }, + { 0xd1f7, 0x0000 }, + { 0xd1f8, 0x0000 }, + { 0xd1f9, 0x0000 }, + { 0xd1fa, 0x0000 }, + { 0xd1fb, 0x0000 }, + { 0xd1fc, 0x0000 }, + { 0xd1fd, 0x0000 }, + { 0xd1fe, 0x0000 }, + { 0xd1ff, 0x0000 }, + { 0xd200, 0x0000 }, + { 0xd201, 0x0000 }, + { 0xd202, 0x0000 }, + { 0xd203, 0x0000 }, + { 0xd204, 0x0000 }, + { 0xd205, 0x0000 }, + { 0xd206, 0x0000 }, + { 0xd207, 0x0000 }, + { 0xd208, 0x0000 }, + { 0xd209, 0x0000 }, + { 0xd20a, 0x0000 }, + { 0xd20b, 0x0000 }, + { 0xd20c, 0x0000 }, + { 0xd20d, 0x0000 }, + { 0xd20e, 0x0000 }, + { 0xd20f, 0x0000 }, + { 0xd210, 0x0000 }, + { 0xd211, 0x0000 }, + { 0xd212, 0x0000 }, + { 0xd213, 0x0000 }, + { 0xd214, 0x0000 }, + { 0xd215, 0x0000 }, + { 0xd216, 0x0000 }, + { 0xd217, 0x0000 }, + { 0xd218, 0x0000 }, + { 0xd219, 0x0000 }, + { 0xd21a, 0x0000 }, + { 0xd21b, 0x0000 }, + { 0xd21c, 0x0000 }, + { 0xd21d, 0x0000 }, + { 0xd21e, 0x0000 }, + { 0xd21f, 0x0000 }, + { 0xd220, 0x0000 }, + { 0xd221, 0x0000 }, + { 0xd222, 0x0000 }, + { 0xd223, 0x0000 }, + { 0xd224, 0x0000 }, + { 0xd225, 0x0000 }, + { 0xd226, 0x0000 }, + { 0xd227, 0x0000 }, + { 0xd228, 0x0000 }, + { 0xd229, 0x0000 }, + { 0xd22a, 0x0000 }, + { 0xd22b, 0x0000 }, + { 0xd22c, 0x0000 }, + { 0xd22d, 0x0000 }, + { 0xd22e, 0x0000 }, + { 0xd22f, 0x0000 }, + { 0xd230, 0x0000 }, + { 0xd231, 0x0000 }, + { 0xd232, 0x0000 }, + { 0xd233, 0x0000 }, + { 0xd234, 0x0000 }, + { 0xd235, 0x0000 }, + { 0xd236, 0x0000 }, + { 0xd237, 0x0000 }, + { 0xd238, 0x0000 }, + { 0xd239, 0x0000 }, + { 0xd23a, 0x0000 }, + { 0xd23b, 0x0000 }, + { 0xd23c, 0x0000 }, + { 0xd23d, 0x0000 }, + { 0xd23e, 0x0000 }, + { 0xd23f, 0x0000 }, + { 0xd240, 0x0000 }, + { 0xd241, 0x0000 }, + { 0xd242, 0x0000 }, + { 0xd243, 0x0000 }, + { 0xd244, 0x0000 }, + { 0xd245, 0x0000 }, + { 0xd246, 0x0000 }, + { 0xd247, 0x0000 }, + { 0xd248, 0x0000 }, + { 0xd249, 0x0000 }, + { 0xd24a, 0x0000 }, + { 0xd24b, 0x0000 }, + { 0xd24c, 0x0000 }, + { 0xd24d, 0x0000 }, + { 0xd24e, 0x0000 }, + { 0xd24f, 0x0000 }, + { 0xd250, 0x0000 }, + { 0xd251, 0x0000 }, + { 0xd252, 0x0000 }, + { 0xd253, 0x0000 }, + { 0xd254, 0x0000 }, + { 0xd255, 0x0000 }, + { 0xd256, 0x0000 }, + { 0xd257, 0x0000 }, + { 0xd258, 0x0000 }, + { 0xd259, 0x0000 }, + { 0xd25a, 0x0000 }, + { 0xd25b, 0x0000 }, + { 0xd25c, 0x0000 }, + { 0xd25d, 0x0000 }, + { 0xd25e, 0x0000 }, + { 0xd25f, 0x0000 }, + { 0xd260, 0x0000 }, + { 0xd261, 0x0000 }, + { 0xd262, 0x0000 }, + { 0xd263, 0x0000 }, + { 0xd264, 0x0000 }, + { 0xd265, 0x0000 }, + { 0xd266, 0x0000 }, + { 0xd267, 0x0000 }, + { 0xd268, 0x0000 }, + { 0xd269, 0x0000 }, + { 0xd26a, 0x0000 }, + { 0xd26b, 0x0000 }, + { 0xd26c, 0x0000 }, + { 0xd26d, 0x0000 }, + { 0xd26e, 0x0000 }, + { 0xd26f, 0x0000 }, + { 0xd270, 0x0000 }, + { 0xd271, 0x0000 }, + { 0xd272, 0x0000 }, + { 0xd273, 0x0000 }, + { 0xd274, 0x0000 }, + { 0xd275, 0x0000 }, + { 0xd276, 0x0000 }, + { 0xd277, 0x0000 }, + { 0xd278, 0x0000 }, + { 0xd279, 0x0000 }, + { 0xd27a, 0x0000 }, + { 0xd27b, 0x0000 }, + { 0xd27c, 0x0000 }, + { 0xd27d, 0x0000 }, + { 0xd27e, 0x0000 }, + { 0xd27f, 0x0000 }, + { 0xd280, 0x0000 }, + { 0xd281, 0x0000 }, + { 0xd282, 0x0000 }, + { 0xd283, 0x0000 }, + { 0xd284, 0x0000 }, + { 0xd285, 0x0000 }, + { 0xd286, 0x0000 }, + { 0xd287, 0x0000 }, + { 0xd288, 0x0000 }, + { 0xd289, 0x0000 }, + { 0xd28a, 0x0000 }, + { 0xd28b, 0x0000 }, + { 0xd28c, 0x0000 }, + { 0xd28d, 0x0000 }, + { 0xd28e, 0x0000 }, + { 0xd28f, 0x0000 }, + { 0xd290, 0x0000 }, + { 0xd291, 0x0000 }, + { 0xd292, 0x0000 }, + { 0xd293, 0x0000 }, + { 0xd294, 0x0000 }, + { 0xd295, 0x0000 }, + { 0xd296, 0x0000 }, + { 0xd297, 0x0000 }, + { 0xd298, 0x0000 }, + { 0xd299, 0x0000 }, + { 0xd29a, 0x0000 }, + { 0xd29b, 0x0000 }, + { 0xd29c, 0x0000 }, + { 0xd29d, 0x0000 }, + { 0xd29e, 0x0000 }, + { 0xd29f, 0x0000 }, + { 0xd2a0, 0x0000 }, + { 0xd2a1, 0x0000 }, + { 0xd2a2, 0x0000 }, + { 0xd2a3, 0x0000 }, + { 0xd2a4, 0x0000 }, + { 0xd2a5, 0x0000 }, + { 0xd2a6, 0x0000 }, + { 0xd2a7, 0x0000 }, + { 0xd2a8, 0x0000 }, + { 0xd2a9, 0x0000 }, + { 0xd2aa, 0x0000 }, + { 0xd2ab, 0x0000 }, + { 0xd2ac, 0x0000 }, + { 0xd2ad, 0x0000 }, + { 0xd2ae, 0x0000 }, + { 0xd2af, 0x0000 }, + { 0xd2b0, 0x0000 }, + { 0xd2b1, 0x0000 }, + { 0xd2b2, 0x0000 }, + { 0xd2b3, 0x0000 }, + { 0xd2b4, 0x0000 }, + { 0xd2b5, 0x0000 }, + { 0xd2b6, 0x0000 }, + { 0xd2b7, 0x0000 }, + { 0xd2b8, 0x0000 }, + { 0xd2b9, 0x0000 }, + { 0xd2ba, 0x0000 }, + { 0xd2bb, 0x0000 }, + { 0xd2bc, 0x0000 }, + { 0xd2bd, 0x0000 }, + { 0xd2be, 0x0000 }, + { 0xd2bf, 0x0000 }, + { 0xd2c0, 0x0000 }, + { 0xd2c1, 0x0000 }, + { 0xd2c2, 0x0000 }, + { 0xd2c3, 0x0000 }, + { 0xd2c4, 0x0000 }, + { 0xd2c5, 0x0000 }, + { 0xd2c6, 0x0000 }, + { 0xd2c7, 0x0000 }, + { 0xd2c8, 0x0000 }, + { 0xd2c9, 0x0000 }, + { 0xd2ca, 0x0000 }, + { 0xd2cb, 0x0000 }, + { 0xd2cc, 0x0000 }, + { 0xd2cd, 0x0000 }, + { 0xd2ce, 0x0000 }, + { 0xd2cf, 0x0000 }, + { 0xd2d0, 0x0000 }, + { 0xd2d1, 0x0000 }, + { 0xd2d2, 0x0000 }, + { 0xd2d3, 0x0000 }, + { 0xd2d4, 0x0000 }, + { 0xd2d5, 0x0000 }, + { 0xd2d6, 0x0000 }, + { 0xd2d7, 0x0000 }, + { 0xd2d8, 0x0000 }, + { 0xd2d9, 0x0000 }, + { 0xd2da, 0x0000 }, + { 0xd2db, 0x0000 }, + { 0xd2dc, 0x0000 }, + { 0xd2dd, 0x0000 }, + { 0xd2de, 0x0000 }, + { 0xd2df, 0x0000 }, + { 0xd2e0, 0x0000 }, + { 0xd2e1, 0x0000 }, + { 0xd2e2, 0x0000 }, + { 0xd2e3, 0x0000 }, + { 0xd2e4, 0x0000 }, + { 0xd2e5, 0x0000 }, + { 0xd2e6, 0x0000 }, + { 0xd2e7, 0x0000 }, + { 0xd2e8, 0x0000 }, + { 0xd2e9, 0x0000 }, + { 0xd2ea, 0x0000 }, + { 0xd2eb, 0x0000 }, + { 0xd2ec, 0x0000 }, + { 0xd2ed, 0x0000 }, + { 0xd2ee, 0x0000 }, + { 0xd2ef, 0x0000 }, + { 0xd2f0, 0x0000 }, + { 0xd2f1, 0x0000 }, + { 0xd2f2, 0x0000 }, + { 0xd2f3, 0x0000 }, + { 0xd2f4, 0x0000 }, + { 0xd2f5, 0x0000 }, + { 0xd2f6, 0x0000 }, + { 0xd2f7, 0x0000 }, + { 0xd2f8, 0x0000 }, + { 0xd2f9, 0x0000 }, + { 0xd2fa, 0x0000 }, + { 0xd2fb, 0x0000 }, + { 0xd2fc, 0x0000 }, + { 0xd2fd, 0x0000 }, + { 0xd2fe, 0x0000 }, + { 0xd2ff, 0x0000 }, + { 0xd300, 0x0000 }, + { 0xd301, 0x0000 }, + { 0xd302, 0x0000 }, + { 0xd303, 0x0000 }, + { 0xd304, 0x0000 }, + { 0xd305, 0x0000 }, + { 0xd306, 0x0000 }, + { 0xd307, 0x0000 }, + { 0xd308, 0x0000 }, + { 0xd309, 0x0000 }, + { 0xd30a, 0x0000 }, + { 0xd30b, 0x0000 }, + { 0xd30c, 0x0000 }, + { 0xd30d, 0x0000 }, + { 0xd30e, 0x0000 }, + { 0xd30f, 0x0000 }, + { 0xd310, 0x0000 }, + { 0xd311, 0x0000 }, + { 0xd312, 0x0000 }, + { 0xd313, 0x0000 }, + { 0xd314, 0x0000 }, + { 0xd315, 0x0000 }, + { 0xd316, 0x0000 }, + { 0xd317, 0x0000 }, + { 0xd318, 0x0000 }, + { 0xd319, 0x0000 }, + { 0xd31a, 0x0000 }, + { 0xd31b, 0x0000 }, + { 0xd31c, 0x0000 }, + { 0xd31d, 0x0000 }, + { 0xd31e, 0x0000 }, + { 0xd31f, 0x0000 }, + { 0xd320, 0x0000 }, + { 0xd321, 0x0000 }, + { 0xd322, 0x0000 }, + { 0xd323, 0x0000 }, + { 0xd324, 0x0000 }, + { 0xd325, 0x0000 }, + { 0xd326, 0x0000 }, + { 0xd327, 0x0000 }, + { 0xd328, 0x0000 }, + { 0xd329, 0x0000 }, + { 0xd32a, 0x0000 }, + { 0xd32b, 0x0000 }, + { 0xd32c, 0x0000 }, + { 0xd32d, 0x0000 }, + { 0xd32e, 0x0000 }, + { 0xd32f, 0x0000 }, + { 0xd330, 0x0000 }, + { 0xd331, 0x0000 }, + { 0xd332, 0x0000 }, + { 0xd333, 0x0000 }, + { 0xd334, 0x0000 }, + { 0xd335, 0x0000 }, + { 0xd336, 0x0000 }, + { 0xd337, 0x0000 }, + { 0xd338, 0x0000 }, + { 0xd339, 0x0000 }, + { 0xd33a, 0x0000 }, + { 0xd33b, 0x0000 }, + { 0xd33c, 0x0000 }, + { 0xd33d, 0x0000 }, + { 0xd33e, 0x0000 }, + { 0xd33f, 0x0000 }, + { 0xd340, 0x0000 }, + { 0xd341, 0x0000 }, + { 0xd342, 0x0000 }, + { 0xd343, 0x0000 }, + { 0xd344, 0x0000 }, + { 0xd345, 0x0000 }, + { 0xd346, 0x0000 }, + { 0xd347, 0x0000 }, + { 0xd348, 0x0000 }, + { 0xd349, 0x0000 }, + { 0xd34a, 0x0000 }, + { 0xd34b, 0x0000 }, + { 0xd34c, 0x0000 }, + { 0xd34d, 0x0000 }, + { 0xd34e, 0x0000 }, + { 0xd34f, 0x0000 }, + { 0xd350, 0x0000 }, + { 0xd351, 0x0000 }, + { 0xd352, 0x0000 }, + { 0xd353, 0x0000 }, + { 0xd354, 0x0000 }, + { 0xd355, 0x0000 }, + { 0xd356, 0x0000 }, + { 0xd357, 0x0000 }, + { 0xd358, 0x0000 }, + { 0xd359, 0x0000 }, + { 0xd35a, 0x0000 }, + { 0xd35b, 0x0000 }, + { 0xd35c, 0x0000 }, + { 0xd35d, 0x0000 }, + { 0xd35e, 0x0000 }, + { 0xd35f, 0x0000 }, + { 0xd360, 0x0000 }, + { 0xd361, 0x0000 }, + { 0xd362, 0x0000 }, + { 0xd363, 0x0000 }, + { 0xd364, 0x0000 }, + { 0xd365, 0x0000 }, + { 0xd366, 0x0000 }, + { 0xd367, 0x0000 }, + { 0xd368, 0x0000 }, + { 0xd369, 0x0000 }, + { 0xd36a, 0x0000 }, + { 0xd36b, 0x0000 }, + { 0xd36c, 0x0000 }, + { 0xd36d, 0x0000 }, + { 0xd36e, 0x0000 }, + { 0xd36f, 0x0000 }, + { 0xd370, 0x0000 }, + { 0xd371, 0x0000 }, + { 0xd372, 0x0000 }, + { 0xd373, 0x0000 }, + { 0xd374, 0x0000 }, + { 0xd375, 0x0000 }, + { 0xd376, 0x0000 }, + { 0xd377, 0x0000 }, + { 0xd378, 0x0000 }, + { 0xd379, 0x0000 }, + { 0xd37a, 0x0000 }, + { 0xd37b, 0x0000 }, + { 0xd37c, 0x0000 }, + { 0xd37d, 0x0000 }, + { 0xd37e, 0x0000 }, + { 0xd37f, 0x0000 }, + { 0xd380, 0x0000 }, + { 0xd381, 0x0000 }, + { 0xd382, 0x0000 }, + { 0xd383, 0x0000 }, + { 0xd384, 0x0000 }, + { 0xd385, 0x0000 }, + { 0xd386, 0x0000 }, + { 0xd387, 0x0000 }, + { 0xd388, 0x0000 }, + { 0xd389, 0x0000 }, + { 0xd38a, 0x0000 }, + { 0xd38b, 0x0000 }, + { 0xd38c, 0x0000 }, + { 0xd38d, 0x0000 }, + { 0xd38e, 0x0000 }, + { 0xd38f, 0x0000 }, + { 0xd390, 0x0000 }, + { 0xd391, 0x0000 }, + { 0xd392, 0x0000 }, + { 0xd393, 0x0000 }, + { 0xd394, 0x0000 }, + { 0xd395, 0x0000 }, + { 0xd396, 0x0000 }, + { 0xd397, 0x0000 }, + { 0xd398, 0x0000 }, + { 0xd399, 0x0000 }, + { 0xd39a, 0x0000 }, + { 0xd39b, 0x0000 }, + { 0xd39c, 0x0000 }, + { 0xd39d, 0x0000 }, + { 0xd39e, 0x0000 }, + { 0xd39f, 0x0000 }, + { 0xd3a0, 0x0000 }, + { 0xd3a1, 0x0000 }, + { 0xd3a2, 0x0000 }, + { 0xd3a3, 0x0000 }, + { 0xd3a4, 0x0000 }, + { 0xd3a5, 0x0000 }, + { 0xd3a6, 0x0000 }, + { 0xd3a7, 0x0000 }, + { 0xd3a8, 0x0000 }, + { 0xd3a9, 0x0000 }, + { 0xd3aa, 0x0000 }, + { 0xd3ab, 0x0000 }, + { 0xd3ac, 0x0000 }, + { 0xd3ad, 0x0000 }, + { 0xd3ae, 0x0000 }, + { 0xd3af, 0x0000 }, + { 0xd3b0, 0x0000 }, + { 0xd3b1, 0x0000 }, + { 0xd3b2, 0x0000 }, + { 0xd3b3, 0x0000 }, + { 0xd3b4, 0x0000 }, + { 0xd3b5, 0x0000 }, + { 0xd3b6, 0x0000 }, + { 0xd3b7, 0x0000 }, + { 0xd3b8, 0x0000 }, + { 0xd3b9, 0x0000 }, + { 0xd3ba, 0x0000 }, + { 0xd3bb, 0x0000 }, + { 0xd3bc, 0x0000 }, + { 0xd3bd, 0x0000 }, + { 0xd3be, 0x0000 }, + { 0xd3bf, 0x0000 }, + { 0xd3c0, 0x0000 }, + { 0xd3c1, 0x0000 }, + { 0xd3c2, 0x0000 }, + { 0xd3c3, 0x0000 }, + { 0xd3c4, 0x0000 }, + { 0xd3c5, 0x0000 }, + { 0xd3c6, 0x0000 }, + { 0xd3c7, 0x0000 }, + { 0xd3c8, 0x0000 }, + { 0xd3c9, 0x0000 }, + { 0xd3ca, 0x0000 }, + { 0xd3cb, 0x0000 }, + { 0xd3cc, 0x0000 }, + { 0xd3cd, 0x0000 }, + { 0xd3ce, 0x0000 }, + { 0xd3cf, 0x0000 }, + { 0xd3d0, 0x0000 }, + { 0xd3d1, 0x0000 }, + { 0xd3d2, 0x0000 }, + { 0xd3d3, 0x0000 }, + { 0xd3d4, 0x0000 }, + { 0xd3d5, 0x0000 }, + { 0xd3d6, 0x0000 }, + { 0xd3d7, 0x0000 }, + { 0xd3d8, 0x0000 }, + { 0xd3d9, 0x0000 }, + { 0xd3da, 0x0000 }, + { 0xd3db, 0x0000 }, + { 0xd3dc, 0x0000 }, + { 0xd3dd, 0x0000 }, + { 0xd3de, 0x0000 }, + { 0xd3df, 0x0000 }, + { 0xd3e0, 0x0000 }, + { 0xd3e1, 0x0000 }, + { 0xd3e2, 0x0000 }, + { 0xd3e3, 0x0000 }, + { 0xd3e4, 0x0000 }, + { 0xd3e5, 0x0000 }, + { 0xd3e6, 0x0000 }, + { 0xd3e7, 0x0000 }, + { 0xd3e8, 0x0000 }, + { 0xd3e9, 0x0000 }, + { 0xd3ea, 0x0000 }, + { 0xd3eb, 0x0000 }, + { 0xd3ec, 0x0000 }, + { 0xd3ed, 0x0000 }, + { 0xd3ee, 0x0000 }, + { 0xd3ef, 0x0000 }, + { 0xd3f0, 0x0000 }, + { 0xd3f1, 0x0000 }, + { 0xd3f2, 0x0000 }, + { 0xd3f3, 0x0000 }, + { 0xd3f4, 0x0000 }, + { 0xd3f5, 0x0000 }, + { 0xd3f6, 0x0000 }, + { 0xd3f7, 0x0000 }, + { 0xd3f8, 0x0000 }, + { 0xd3f9, 0x0000 }, + { 0xd3fa, 0x0000 }, + { 0xd3fb, 0x0000 }, + { 0xd3fc, 0x0000 }, + { 0xd3fd, 0x0000 }, + { 0xd3fe, 0x0000 }, + { 0xd3ff, 0x0000 }, + { 0xd400, 0x0000 }, + { 0xd401, 0x0000 }, + { 0xd402, 0x0000 }, + { 0xd403, 0x0000 }, + { 0xd404, 0x0000 }, + { 0xd405, 0x0000 }, + { 0xd406, 0x0000 }, + { 0xd407, 0x0000 }, + { 0xd408, 0x0000 }, + { 0xd409, 0x0000 }, + { 0xd40a, 0x0000 }, + { 0xd40b, 0x0000 }, + { 0xd40c, 0x0000 }, + { 0xd40d, 0x0000 }, + { 0xd40e, 0x0000 }, + { 0xd40f, 0x0000 }, + { 0xd410, 0x0000 }, + { 0xd411, 0x0000 }, + { 0xd412, 0x0000 }, + { 0xd413, 0x0000 }, + { 0xd414, 0x0000 }, + { 0xd415, 0x0000 }, + { 0xd416, 0x0000 }, + { 0xd417, 0x0000 }, + { 0xd418, 0x0000 }, + { 0xd419, 0x0000 }, + { 0xd41a, 0x0000 }, + { 0xd41b, 0x0000 }, + { 0xd41c, 0x0000 }, + { 0xd41d, 0x0000 }, + { 0xd41e, 0x0000 }, + { 0xd41f, 0x0000 }, + { 0xd420, 0x0000 }, + { 0xd421, 0x0000 }, + { 0xd422, 0x0000 }, + { 0xd423, 0x0000 }, + { 0xd424, 0x0000 }, + { 0xd425, 0x0000 }, + { 0xd426, 0x0000 }, + { 0xd427, 0x0000 }, + { 0xd428, 0x0000 }, + { 0xd429, 0x0000 }, + { 0xd42a, 0x0000 }, + { 0xd42b, 0x0000 }, + { 0xd42c, 0x0000 }, + { 0xd42d, 0x0000 }, + { 0xd42e, 0x0000 }, + { 0xd42f, 0x0000 }, + { 0xd430, 0x0000 }, + { 0xd431, 0x0000 }, + { 0xd432, 0x0000 }, + { 0xd433, 0x0000 }, + { 0xd434, 0x0000 }, + { 0xd435, 0x0000 }, + { 0xd436, 0x0000 }, + { 0xd437, 0x0000 }, + { 0xd438, 0x0000 }, + { 0xd439, 0x0000 }, + { 0xd43a, 0x0000 }, + { 0xd43b, 0x0000 }, + { 0xd43c, 0x0000 }, + { 0xd43d, 0x0000 }, + { 0xd43e, 0x0000 }, + { 0xd43f, 0x0000 }, + { 0xd440, 0x0000 }, + { 0xd441, 0x0000 }, + { 0xd442, 0x0000 }, + { 0xd443, 0x0000 }, + { 0xd444, 0x0000 }, + { 0xd445, 0x0000 }, + { 0xd446, 0x0000 }, + { 0xd447, 0x0000 }, + { 0xd448, 0x0000 }, + { 0xd449, 0x0000 }, + { 0xd44a, 0x0000 }, + { 0xd44b, 0x0000 }, + { 0xd44c, 0x0000 }, + { 0xd44d, 0x0000 }, + { 0xd44e, 0x0000 }, + { 0xd44f, 0x0000 }, + { 0xd450, 0x0000 }, + { 0xd451, 0x0000 }, + { 0xd452, 0x0000 }, + { 0xd453, 0x0000 }, + { 0xd454, 0x0000 }, + { 0xd455, 0x0000 }, + { 0xd456, 0x0000 }, + { 0xd457, 0x0000 }, + { 0xd458, 0x0000 }, + { 0xd459, 0x0000 }, + { 0xd45a, 0x0000 }, + { 0xd45b, 0x0000 }, + { 0xd45c, 0x0000 }, + { 0xd45d, 0x0000 }, + { 0xd45e, 0x0000 }, + { 0xd45f, 0x0000 }, + { 0xd460, 0x0000 }, + { 0xd461, 0x0000 }, + { 0xd462, 0x0000 }, + { 0xd463, 0x0000 }, + { 0xd464, 0x0000 }, + { 0xd465, 0x0000 }, + { 0xd466, 0x0000 }, + { 0xd467, 0x0000 }, + { 0xd468, 0x0000 }, + { 0xd469, 0x0000 }, + { 0xd46a, 0x0000 }, + { 0xd46b, 0x0000 }, + { 0xd46c, 0x0000 }, + { 0xd46d, 0x0000 }, + { 0xd46e, 0x0000 }, + { 0xd46f, 0x0000 }, + { 0xd470, 0x0000 }, + { 0xd471, 0x0000 }, + { 0xd472, 0x0000 }, + { 0xd473, 0x0000 }, + { 0xd474, 0x0000 }, + { 0xd475, 0x0000 }, + { 0xd476, 0x0000 }, + { 0xd477, 0x0000 }, + { 0xd478, 0x0000 }, + { 0xd479, 0x0000 }, + { 0xd47a, 0x0000 }, + { 0xd47b, 0x0000 }, + { 0xd47c, 0x0000 }, + { 0xd47d, 0x0000 }, + { 0xd47e, 0x0000 }, + { 0xd47f, 0x0000 }, + { 0xd480, 0x0000 }, + { 0xd481, 0x0000 }, + { 0xd482, 0x0000 }, + { 0xd483, 0x0000 }, + { 0xd484, 0x0000 }, + { 0xd485, 0x0000 }, + { 0xd486, 0x0000 }, + { 0xd487, 0x0000 }, + { 0xd488, 0x0000 }, + { 0xd489, 0x0000 }, + { 0xd48a, 0x0000 }, + { 0xd48b, 0x0000 }, + { 0xd48c, 0x0000 }, + { 0xd48d, 0x0000 }, + { 0xd48e, 0x0000 }, + { 0xd48f, 0x0000 }, + { 0xd490, 0x0000 }, + { 0xd491, 0x0000 }, + { 0xd492, 0x0000 }, + { 0xd493, 0x0000 }, + { 0xd494, 0x0000 }, + { 0xd495, 0x0000 }, + { 0xd496, 0x0000 }, + { 0xd497, 0x0000 }, + { 0xd498, 0x0000 }, + { 0xd499, 0x0000 }, + { 0xd49a, 0x0000 }, + { 0xd49b, 0x0000 }, + { 0xd49c, 0x0000 }, + { 0xd49d, 0x0000 }, + { 0xd49e, 0x0000 }, + { 0xd49f, 0x0000 }, + { 0xd4a0, 0x0000 }, + { 0xd4a1, 0x0000 }, + { 0xd4a2, 0x0000 }, + { 0xd4a3, 0x0000 }, + { 0xd4a4, 0x0000 }, + { 0xd4a5, 0x0000 }, + { 0xd4a6, 0x0000 }, + { 0xd4a7, 0x0000 }, + { 0xd4a8, 0x0000 }, + { 0xd4a9, 0x0000 }, + { 0xd4aa, 0x0000 }, + { 0xd4ab, 0x0000 }, + { 0xd4ac, 0x0000 }, + { 0xd4ad, 0x0000 }, + { 0xd4ae, 0x0000 }, + { 0xd4af, 0x0000 }, + { 0xd4b0, 0x0000 }, + { 0xd4b1, 0x0000 }, + { 0xd4b2, 0x0000 }, + { 0xd4b3, 0x0000 }, + { 0xd4b4, 0x0000 }, + { 0xd4b5, 0x0000 }, + { 0xd4b6, 0x0000 }, + { 0xd4b7, 0x0000 }, + { 0xd4b8, 0x0000 }, + { 0xd4b9, 0x0000 }, + { 0xd4ba, 0x0000 }, + { 0xd4bb, 0x0000 }, + { 0xd4bc, 0x0000 }, + { 0xd4bd, 0x0000 }, + { 0xd4be, 0x0000 }, + { 0xd4bf, 0x0000 }, + { 0xd4c0, 0x0000 }, + { 0xd4c1, 0x0000 }, + { 0xd4c2, 0x0000 }, + { 0xd4c3, 0x0000 }, + { 0xd4c4, 0x0000 }, + { 0xd4c5, 0x0000 }, + { 0xd4c6, 0x0000 }, + { 0xd4c7, 0x0000 }, + { 0xd4c8, 0x0000 }, + { 0xd4c9, 0x0000 }, + { 0xd4ca, 0x0000 }, + { 0xd4cb, 0x0000 }, + { 0xd4cc, 0x0000 }, + { 0xd4cd, 0x0000 }, + { 0xd4ce, 0x0000 }, + { 0xd4cf, 0x0000 }, + { 0xd4d0, 0x0000 }, + { 0xd4d1, 0x0000 }, + { 0xd4d2, 0x0000 }, + { 0xd4d3, 0x0000 }, + { 0xd4d4, 0x0000 }, + { 0xd4d5, 0x0000 }, + { 0xd4d6, 0x0000 }, + { 0xd4d7, 0x0000 }, + { 0xd4d8, 0x0000 }, + { 0xd4d9, 0x0000 }, + { 0xd4da, 0x0000 }, + { 0xd4db, 0x0000 }, + { 0xd4dc, 0x0000 }, + { 0xd4dd, 0x0000 }, + { 0xd4de, 0x0000 }, + { 0xd4df, 0x0000 }, + { 0xd4e0, 0x0000 }, + { 0xd4e1, 0x0000 }, + { 0xd4e2, 0x0000 }, + { 0xd4e3, 0x0000 }, + { 0xd4e4, 0x0000 }, + { 0xd4e5, 0x0000 }, + { 0xd4e6, 0x0000 }, + { 0xd4e7, 0x0000 }, + { 0xd4e8, 0x0000 }, + { 0xd4e9, 0x0000 }, + { 0xd4ea, 0x0000 }, + { 0xd4eb, 0x0000 }, + { 0xd4ec, 0x0000 }, + { 0xd4ed, 0x0000 }, + { 0xd4ee, 0x0000 }, + { 0xd4ef, 0x0000 }, + { 0xd4f0, 0x0000 }, + { 0xd4f1, 0x0000 }, + { 0xd4f2, 0x0000 }, + { 0xd4f3, 0x0000 }, + { 0xd4f4, 0x0000 }, + { 0xd4f5, 0x0000 }, + { 0xd4f6, 0x0000 }, + { 0xd4f7, 0x0000 }, + { 0xd4f8, 0x0000 }, + { 0xd4f9, 0x0000 }, + { 0xd4fa, 0x0000 }, + { 0xd4fb, 0x0000 }, + { 0xd4fc, 0x0000 }, + { 0xd4fd, 0x0000 }, + { 0xd4fe, 0x0000 }, + { 0xd4ff, 0x0000 }, + { 0xd500, 0x0000 }, + { 0xd501, 0x0000 }, + { 0xd502, 0x0000 }, + { 0xd503, 0x0000 }, + { 0xd504, 0x0000 }, + { 0xd505, 0x0000 }, + { 0xd506, 0x0000 }, + { 0xd507, 0x0000 }, + { 0xd508, 0x0000 }, + { 0xd509, 0x0000 }, + { 0xd50a, 0x0000 }, + { 0xd50b, 0x0000 }, + { 0xd50c, 0x0000 }, + { 0xd50d, 0x0000 }, + { 0xd50e, 0x0000 }, + { 0xd50f, 0x0000 }, + { 0xd510, 0x0000 }, + { 0xd511, 0x0000 }, + { 0xd512, 0x0000 }, + { 0xd513, 0x0000 }, + { 0xd514, 0x0000 }, + { 0xd515, 0x0000 }, + { 0xd516, 0x0000 }, + { 0xd517, 0x0000 }, + { 0xd518, 0x0000 }, + { 0xd519, 0x0000 }, + { 0xd51a, 0x0000 }, + { 0xd51b, 0x0000 }, + { 0xd51c, 0x0000 }, + { 0xd51d, 0x0000 }, + { 0xd51e, 0x0000 }, + { 0xd51f, 0x0000 }, + { 0xd520, 0x0000 }, + { 0xd521, 0x0000 }, + { 0xd522, 0x0000 }, + { 0xd523, 0x0000 }, + { 0xd524, 0x0000 }, + { 0xd525, 0x0000 }, + { 0xd526, 0x0000 }, + { 0xd527, 0x0000 }, + { 0xd528, 0x0000 }, + { 0xd529, 0x0000 }, + { 0xd52a, 0x0000 }, + { 0xd52b, 0x0000 }, + { 0xd52c, 0x0000 }, + { 0xd52d, 0x0000 }, + { 0xd52e, 0x0000 }, + { 0xd52f, 0x0000 }, + { 0xd530, 0x0000 }, + { 0xd531, 0x0000 }, + { 0xd532, 0x0000 }, + { 0xd533, 0x0000 }, + { 0xd534, 0x0000 }, + { 0xd535, 0x0000 }, + { 0xd536, 0x0000 }, + { 0xd537, 0x0000 }, + { 0xd538, 0x0000 }, + { 0xd539, 0x0000 }, + { 0xd53a, 0x0000 }, + { 0xd53b, 0x0000 }, + { 0xd53c, 0x0000 }, + { 0xd53d, 0x0000 }, + { 0xd53e, 0x0000 }, + { 0xd53f, 0x0000 }, + { 0xd540, 0x0000 }, + { 0xd541, 0x0000 }, + { 0xd542, 0x0000 }, + { 0xd543, 0x0000 }, + { 0xd544, 0x0000 }, + { 0xd545, 0x0000 }, + { 0xd546, 0x0000 }, + { 0xd547, 0x0000 }, + { 0xd548, 0x0000 }, + { 0xd549, 0x0000 }, + { 0xd54a, 0x0000 }, + { 0xd54b, 0x0000 }, + { 0xd54c, 0x0000 }, + { 0xd54d, 0x0000 }, + { 0xd54e, 0x0000 }, + { 0xd54f, 0x0000 }, + { 0xd550, 0x0000 }, + { 0xd551, 0x0000 }, + { 0xd552, 0x0000 }, + { 0xd553, 0x0000 }, + { 0xd554, 0x0000 }, + { 0xd555, 0x0000 }, + { 0xd556, 0x0000 }, + { 0xd557, 0x0000 }, + { 0xd558, 0x0000 }, + { 0xd559, 0x0000 }, + { 0xd55a, 0x0000 }, + { 0xd55b, 0x0000 }, + { 0xd55c, 0x0000 }, + { 0xd55d, 0x0000 }, + { 0xd55e, 0x0000 }, + { 0xd55f, 0x0000 }, + { 0xd560, 0x0000 }, + { 0xd561, 0x0000 }, + { 0xd562, 0x0000 }, + { 0xd563, 0x0000 }, + { 0xd564, 0x0000 }, + { 0xd565, 0x0000 }, + { 0xd566, 0x0000 }, + { 0xd567, 0x0000 }, + { 0xd568, 0x0000 }, + { 0xd569, 0x0000 }, + { 0xd56a, 0x0000 }, + { 0xd56b, 0x0000 }, + { 0xd56c, 0x0000 }, + { 0xd56d, 0x0000 }, + { 0xd56e, 0x0000 }, + { 0xd56f, 0x0000 }, + { 0xd570, 0x0000 }, + { 0xd571, 0x0000 }, + { 0xd572, 0x0000 }, + { 0xd573, 0x0000 }, + { 0xd574, 0x0000 }, + { 0xd575, 0x0000 }, + { 0xd576, 0x0000 }, + { 0xd577, 0x0000 }, + { 0xd578, 0x0000 }, + { 0xd579, 0x0000 }, + { 0xd57a, 0x0000 }, + { 0xd57b, 0x0000 }, + { 0xd57c, 0x0000 }, + { 0xd57d, 0x0000 }, + { 0xd57e, 0x0000 }, + { 0xd57f, 0x0000 }, + { 0xd580, 0x0000 }, + { 0xd581, 0x0000 }, + { 0xd582, 0x0000 }, + { 0xd583, 0x0000 }, + { 0xd584, 0x0000 }, + { 0xd585, 0x0000 }, + { 0xd586, 0x0000 }, + { 0xd587, 0x0000 }, + { 0xd588, 0x0000 }, + { 0xd589, 0x0000 }, + { 0xd58a, 0x0000 }, + { 0xd58b, 0x0000 }, + { 0xd58c, 0x0000 }, + { 0xd58d, 0x0000 }, + { 0xd58e, 0x0000 }, + { 0xd58f, 0x0000 }, + { 0xd590, 0x0000 }, + { 0xd591, 0x0000 }, + { 0xd592, 0x0000 }, + { 0xd593, 0x0000 }, + { 0xd594, 0x0000 }, + { 0xd595, 0x0000 }, + { 0xd596, 0x0000 }, + { 0xd597, 0x0000 }, + { 0xd598, 0x0000 }, + { 0xd599, 0x0000 }, + { 0xd59a, 0x0000 }, + { 0xd59b, 0x0000 }, + { 0xd59c, 0x0000 }, + { 0xd59d, 0x0000 }, + { 0xd59e, 0x0000 }, + { 0xd59f, 0x0000 }, + { 0xd5a0, 0x0000 }, + { 0xd5a1, 0x0000 }, + { 0xd5a2, 0x0000 }, + { 0xd5a3, 0x0000 }, + { 0xd5a4, 0x0000 }, + { 0xd5a5, 0x0000 }, + { 0xd5a6, 0x0000 }, + { 0xd5a7, 0x0000 }, + { 0xd5a8, 0x0000 }, + { 0xd5a9, 0x0000 }, + { 0xd5aa, 0x0000 }, + { 0xd5ab, 0x0000 }, + { 0xd5ac, 0x0000 }, + { 0xd5ad, 0x0000 }, + { 0xd5ae, 0x0000 }, + { 0xd5af, 0x0000 }, + { 0xd5b0, 0x0000 }, + { 0xd5b1, 0x0000 }, + { 0xd5b2, 0x0000 }, + { 0xd5b3, 0x0000 }, + { 0xd5b4, 0x0000 }, + { 0xd5b5, 0x0000 }, + { 0xd5b6, 0x0000 }, + { 0xd5b7, 0x0000 }, + { 0xd5b8, 0x0000 }, + { 0xd5b9, 0x0000 }, + { 0xd5ba, 0x0000 }, + { 0xd5bb, 0x0000 }, + { 0xd5bc, 0x0000 }, + { 0xd5bd, 0x0000 }, + { 0xd5be, 0x0000 }, + { 0xd5bf, 0x0000 }, + { 0xd5c0, 0x0000 }, + { 0xd5c1, 0x0000 }, + { 0xd5c2, 0x0000 }, + { 0xd5c3, 0x0000 }, + { 0xd5c4, 0x0000 }, + { 0xd5c5, 0x0000 }, + { 0xd5c6, 0x0000 }, + { 0xd5c7, 0x0000 }, + { 0xd5c8, 0x0000 }, + { 0xd5c9, 0x0000 }, + { 0xd5ca, 0x0000 }, + { 0xd5cb, 0x0000 }, + { 0xd5cc, 0x0000 }, + { 0xd5cd, 0x0000 }, + { 0xd5ce, 0x0000 }, + { 0xd5cf, 0x0000 }, + { 0xd5d0, 0x0000 }, + { 0xd5d1, 0x0000 }, + { 0xd5d2, 0x0000 }, + { 0xd5d3, 0x0000 }, + { 0xd5d4, 0x0000 }, + { 0xd5d5, 0x0000 }, + { 0xd5d6, 0x0000 }, + { 0xd5d7, 0x0000 }, + { 0xd5d8, 0x0000 }, + { 0xd5d9, 0x0000 }, + { 0xd5da, 0x0000 }, + { 0xd5db, 0x0000 }, + { 0xd5dc, 0x0000 }, + { 0xd5dd, 0x0000 }, + { 0xd5de, 0x0000 }, + { 0xd5df, 0x0000 }, + { 0xd5e0, 0x0000 }, + { 0xd5e1, 0x0000 }, + { 0xd5e2, 0x0000 }, + { 0xd5e3, 0x0000 }, + { 0xd5e4, 0x0000 }, + { 0xd5e5, 0x0000 }, + { 0xd5e6, 0x0000 }, + { 0xd5e7, 0x0000 }, + { 0xd5e8, 0x0000 }, + { 0xd5e9, 0x0000 }, + { 0xd5ea, 0x0000 }, + { 0xd5eb, 0x0000 }, + { 0xd5ec, 0x0000 }, + { 0xd5ed, 0x0000 }, + { 0xd5ee, 0x0000 }, + { 0xd5ef, 0x0000 }, + { 0xd5f0, 0x0000 }, + { 0xd5f1, 0x0000 }, + { 0xd5f2, 0x0000 }, + { 0xd5f3, 0x0000 }, + { 0xd5f4, 0x0000 }, + { 0xd5f5, 0x0000 }, + { 0xd5f6, 0x0000 }, + { 0xd5f7, 0x0000 }, + { 0xd5f8, 0x0000 }, + { 0xd5f9, 0x0000 }, + { 0xd5fa, 0x0000 }, + { 0xd5fb, 0x0000 }, + { 0xd5fc, 0x0000 }, + { 0xd5fd, 0x0000 }, + { 0xd5fe, 0x0000 }, + { 0xd5ff, 0x0000 }, + { 0xd600, 0x0000 }, + { 0xd601, 0x0000 }, + { 0xd602, 0x0000 }, + { 0xd603, 0x0000 }, + { 0xd604, 0x0000 }, + { 0xd605, 0x0000 }, + { 0xd606, 0x0000 }, + { 0xd607, 0x0000 }, + { 0xd608, 0x0000 }, + { 0xd609, 0x0000 }, + { 0xd60a, 0x0000 }, + { 0xd60b, 0x0000 }, + { 0xd60c, 0x0000 }, + { 0xd60d, 0x0000 }, + { 0xd60e, 0x0000 }, + { 0xd60f, 0x0000 }, + { 0xd610, 0x0000 }, + { 0xd611, 0x0000 }, + { 0xd612, 0x0000 }, + { 0xd613, 0x0000 }, + { 0xd614, 0x0000 }, + { 0xd615, 0x0000 }, + { 0xd616, 0x0000 }, + { 0xd617, 0x0000 }, + { 0xd618, 0x0000 }, + { 0xd619, 0x0000 }, + { 0xd61a, 0x0000 }, + { 0xd61b, 0x0000 }, + { 0xd61c, 0x0000 }, + { 0xd61d, 0x0000 }, + { 0xd61e, 0x0000 }, + { 0xd61f, 0x0000 }, + { 0xd620, 0x0000 }, + { 0xd621, 0x0000 }, + { 0xd622, 0x0000 }, + { 0xd623, 0x0000 }, + { 0xd624, 0x0000 }, + { 0xd625, 0x0000 }, + { 0xd626, 0x0000 }, + { 0xd627, 0x0000 }, + { 0xd628, 0x0000 }, + { 0xd629, 0x0000 }, + { 0xd62a, 0x0000 }, + { 0xd62b, 0x0000 }, + { 0xd62c, 0x0000 }, + { 0xd62d, 0x0000 }, + { 0xd62e, 0x0000 }, + { 0xd62f, 0x0000 }, + { 0xd630, 0x0000 }, + { 0xd631, 0x0000 }, + { 0xd632, 0x0000 }, + { 0xd633, 0x0000 }, + { 0xd634, 0x0000 }, + { 0xd635, 0x0000 }, + { 0xd636, 0x0000 }, + { 0xd637, 0x0000 }, + { 0xd638, 0x0000 }, + { 0xd639, 0x0000 }, + { 0xd63a, 0x0000 }, + { 0xd63b, 0x0000 }, + { 0xd63c, 0x0000 }, + { 0xd63d, 0x0000 }, + { 0xd63e, 0x0000 }, + { 0xd63f, 0x0000 }, + { 0xd640, 0x0000 }, + { 0xd641, 0x0000 }, + { 0xd642, 0x0000 }, + { 0xd643, 0x0000 }, + { 0xd644, 0x0000 }, + { 0xd645, 0x0000 }, + { 0xd646, 0x0000 }, + { 0xd647, 0x0000 }, + { 0xd648, 0x0000 }, + { 0xd649, 0x0000 }, + { 0xd64a, 0x0000 }, + { 0xd64b, 0x0000 }, + { 0xd64c, 0x0000 }, + { 0xd64d, 0x0000 }, + { 0xd64e, 0x0000 }, + { 0xd64f, 0x0000 }, + { 0xd650, 0x0000 }, + { 0xd651, 0x0000 }, + { 0xd652, 0x0000 }, + { 0xd653, 0x0000 }, + { 0xd654, 0x0000 }, + { 0xd655, 0x0000 }, + { 0xd656, 0x0000 }, + { 0xd657, 0x0000 }, + { 0xd658, 0x0000 }, + { 0xd659, 0x0000 }, + { 0xd65a, 0x0000 }, + { 0xd65b, 0x0000 }, + { 0xd65c, 0x0000 }, + { 0xd65d, 0x0000 }, + { 0xd65e, 0x0000 }, + { 0xd65f, 0x0000 }, + { 0xd660, 0x0000 }, + { 0xd661, 0x0000 }, + { 0xd662, 0x0000 }, + { 0xd663, 0x0000 }, + { 0xd664, 0x0000 }, + { 0xd665, 0x0000 }, + { 0xd666, 0x0000 }, + { 0xd667, 0x0000 }, + { 0xd668, 0x0000 }, + { 0xd669, 0x0000 }, + { 0xd66a, 0x0000 }, + { 0xd66b, 0x0000 }, + { 0xd66c, 0x0000 }, + { 0xd66d, 0x0000 }, + { 0xd66e, 0x0000 }, + { 0xd66f, 0x0000 }, + { 0xd670, 0x0000 }, + { 0xd671, 0x0000 }, + { 0xd672, 0x0000 }, + { 0xd673, 0x0000 }, + { 0xd674, 0x0000 }, + { 0xd675, 0x0000 }, + { 0xd676, 0x0000 }, + { 0xd677, 0x0000 }, + { 0xd678, 0x0000 }, + { 0xd679, 0x0000 }, + { 0xd67a, 0x0000 }, + { 0xd67b, 0x0000 }, + { 0xd67c, 0x0000 }, + { 0xd67d, 0x0000 }, + { 0xd67e, 0x0000 }, + { 0xd67f, 0x0000 }, + { 0xd680, 0x0000 }, + { 0xd681, 0x0000 }, + { 0xd682, 0x0000 }, + { 0xd683, 0x0000 }, + { 0xd684, 0x0000 }, + { 0xd685, 0x0000 }, + { 0xd686, 0x0000 }, + { 0xd687, 0x0000 }, + { 0xd688, 0x0000 }, + { 0xd689, 0x0000 }, + { 0xd68a, 0x0000 }, + { 0xd68b, 0x0000 }, + { 0xd68c, 0x0000 }, + { 0xd68d, 0x0000 }, + { 0xd68e, 0x0000 }, + { 0xd68f, 0x0000 }, + { 0xd690, 0x0000 }, + { 0xd691, 0x0000 }, + { 0xd692, 0x0000 }, + { 0xd693, 0x0000 }, + { 0xd694, 0x0000 }, + { 0xd695, 0x0000 }, + { 0xd696, 0x0000 }, + { 0xd697, 0x0000 }, + { 0xd698, 0x0000 }, + { 0xd699, 0x0000 }, + { 0xd69a, 0x0000 }, + { 0xd69b, 0x0000 }, + { 0xd69c, 0x0000 }, + { 0xd69d, 0x0000 }, + { 0xd69e, 0x0000 }, + { 0xd69f, 0x0000 }, + { 0xd6a0, 0x0000 }, + { 0xd6a1, 0x0000 }, + { 0xd6a2, 0x0000 }, + { 0xd6a3, 0x0000 }, + { 0xd6a4, 0x0000 }, + { 0xd6a5, 0x0000 }, + { 0xd6a6, 0x0000 }, + { 0xd6a7, 0x0000 }, + { 0xd6a8, 0x0000 }, + { 0xd6a9, 0x0000 }, + { 0xd6aa, 0x0000 }, + { 0xd6ab, 0x0000 }, + { 0xd6ac, 0x0000 }, + { 0xd6ad, 0x0000 }, + { 0xd6ae, 0x0000 }, + { 0xd6af, 0x0000 }, + { 0xd6b0, 0x0000 }, + { 0xd6b1, 0x0000 }, + { 0xd6b2, 0x0000 }, + { 0xd6b3, 0x0000 }, + { 0xd6b4, 0x0000 }, + { 0xd6b5, 0x0000 }, + { 0xd6b6, 0x0000 }, + { 0xd6b7, 0x0000 }, + { 0xd6b8, 0x0000 }, + { 0xd6b9, 0x0000 }, + { 0xd6ba, 0x0000 }, + { 0xd6bb, 0x0000 }, + { 0xd6bc, 0x0000 }, + { 0xd6bd, 0x0000 }, + { 0xd6be, 0x0000 }, + { 0xd6bf, 0x0000 }, + { 0xd6c0, 0x0000 }, + { 0xd6c1, 0x0000 }, + { 0xd6c2, 0x0000 }, + { 0xd6c3, 0x0000 }, + { 0xd6c4, 0x0000 }, + { 0xd6c5, 0x0000 }, + { 0xd6c6, 0x0000 }, + { 0xd6c7, 0x0000 }, + { 0xd6c8, 0x0000 }, + { 0xd6c9, 0x0000 }, + { 0xd6ca, 0x0000 }, + { 0xd6cb, 0x0000 }, + { 0xd6cc, 0x0000 }, + { 0xd6cd, 0x0000 }, + { 0xd6ce, 0x0000 }, + { 0xd6cf, 0x0000 }, + { 0xd6d0, 0x0000 }, + { 0xd6d1, 0x0000 }, + { 0xd6d2, 0x0000 }, + { 0xd6d3, 0x0000 }, + { 0xd6d4, 0x0000 }, + { 0xd6d5, 0x0000 }, + { 0xd6d6, 0x0000 }, + { 0xd6d7, 0x0000 }, + { 0xd6d8, 0x0000 }, + { 0xd6d9, 0x0000 }, + { 0xd6da, 0x0000 }, + { 0xd6db, 0x0000 }, + { 0xd6dc, 0x0000 }, + { 0xd6dd, 0x0000 }, + { 0xd6de, 0x0000 }, + { 0xd6df, 0x0000 }, + { 0xd6e0, 0x0000 }, + { 0xd6e1, 0x0000 }, + { 0xd6e2, 0x0000 }, + { 0xd6e3, 0x0000 }, + { 0xd6e4, 0x0000 }, + { 0xd6e5, 0x0000 }, + { 0xd6e6, 0x0000 }, + { 0xd6e7, 0x0000 }, + { 0xd6e8, 0x0000 }, + { 0xd6e9, 0x0000 }, + { 0xd6ea, 0x0000 }, + { 0xd6eb, 0x0000 }, + { 0xd6ec, 0x0000 }, + { 0xd6ed, 0x0000 }, + { 0xd6ee, 0x0000 }, + { 0xd6ef, 0x0000 }, + { 0xd6f0, 0x0000 }, + { 0xd6f1, 0x0000 }, + { 0xd6f2, 0x0000 }, + { 0xd6f3, 0x0000 }, + { 0xd6f4, 0x0000 }, + { 0xd6f5, 0x0000 }, + { 0xd6f6, 0x0000 }, + { 0xd6f7, 0x0000 }, + { 0xd6f8, 0x0000 }, + { 0xd6f9, 0x0000 }, + { 0xd6fa, 0x0000 }, + { 0xd6fb, 0x0000 }, + { 0xd6fc, 0x0000 }, + { 0xd6fd, 0x0000 }, + { 0xd6fe, 0x0000 }, + { 0xd6ff, 0x0000 }, + { 0xd700, 0x0000 }, + { 0xd701, 0x0000 }, + { 0xd702, 0x0000 }, + { 0xd703, 0x0000 }, + { 0xd704, 0x0000 }, + { 0xd705, 0x0000 }, + { 0xd706, 0x0000 }, + { 0xd707, 0x0000 }, + { 0xd708, 0x0000 }, + { 0xd709, 0x0000 }, + { 0xd70a, 0x0000 }, + { 0xd70b, 0x0000 }, + { 0xd70c, 0x0000 }, + { 0xd70d, 0x0000 }, + { 0xd70e, 0x0000 }, + { 0xd70f, 0x0000 }, + { 0xd710, 0x0000 }, + { 0xd711, 0x0000 }, + { 0xd712, 0x0000 }, + { 0xd713, 0x0000 }, + { 0xd714, 0x0000 }, + { 0xd715, 0x0000 }, + { 0xd716, 0x0000 }, + { 0xd717, 0x0000 }, + { 0xd718, 0x0000 }, + { 0xd719, 0x0000 }, + { 0xd71a, 0x0000 }, + { 0xd71b, 0x0000 }, + { 0xd71c, 0x0000 }, + { 0xd71d, 0x0000 }, + { 0xd71e, 0x0000 }, + { 0xd71f, 0x0000 }, + { 0xd720, 0x0000 }, + { 0xd721, 0x0000 }, + { 0xd722, 0x0000 }, + { 0xd723, 0x0000 }, + { 0xd724, 0x0000 }, + { 0xd725, 0x0000 }, + { 0xd726, 0x0000 }, + { 0xd727, 0x0000 }, + { 0xd728, 0x0000 }, + { 0xd729, 0x0000 }, + { 0xd72a, 0x0000 }, + { 0xd72b, 0x0000 }, + { 0xd72c, 0x0000 }, + { 0xd72d, 0x0000 }, + { 0xd72e, 0x0000 }, + { 0xd72f, 0x0000 }, + { 0xd730, 0x0000 }, + { 0xd731, 0x0000 }, + { 0xd732, 0x0000 }, + { 0xd733, 0x0000 }, + { 0xd734, 0x0000 }, + { 0xd735, 0x0000 }, + { 0xd736, 0x0000 }, + { 0xd737, 0x0000 }, + { 0xd738, 0x0000 }, + { 0xd739, 0x0000 }, + { 0xd73a, 0x0000 }, + { 0xd73b, 0x0000 }, + { 0xd73c, 0x0000 }, + { 0xd73d, 0x0000 }, + { 0xd73e, 0x0000 }, + { 0xd73f, 0x0000 }, + { 0xd740, 0x0000 }, + { 0xd741, 0x0000 }, + { 0xd742, 0x0000 }, + { 0xd743, 0x0000 }, + { 0xd744, 0x0000 }, + { 0xd745, 0x0000 }, + { 0xd746, 0x0000 }, + { 0xd747, 0x0000 }, + { 0xd748, 0x0000 }, + { 0xd749, 0x0000 }, + { 0xd74a, 0x0000 }, + { 0xd74b, 0x0000 }, + { 0xd74c, 0x0000 }, + { 0xd74d, 0x0000 }, + { 0xd74e, 0x0000 }, + { 0xd74f, 0x0000 }, + { 0xd750, 0x0000 }, + { 0xd751, 0x0000 }, + { 0xd752, 0x0000 }, + { 0xd753, 0x0000 }, + { 0xd754, 0x0000 }, + { 0xd755, 0x0000 }, + { 0xd756, 0x0000 }, + { 0xd757, 0x0000 }, + { 0xd758, 0x0000 }, + { 0xd759, 0x0000 }, + { 0xd75a, 0x0000 }, + { 0xd75b, 0x0000 }, + { 0xd75c, 0x0000 }, + { 0xd75d, 0x0000 }, + { 0xd75e, 0x0000 }, + { 0xd75f, 0x0000 }, + { 0xd760, 0x0000 }, + { 0xd761, 0x0000 }, + { 0xd762, 0x0000 }, + { 0xd763, 0x0000 }, + { 0xd764, 0x0000 }, + { 0xd765, 0x0000 }, + { 0xd766, 0x0000 }, + { 0xd767, 0x0000 }, + { 0xd768, 0x0000 }, + { 0xd769, 0x0000 }, + { 0xd76a, 0x0000 }, + { 0xd76b, 0x0000 }, + { 0xd76c, 0x0000 }, + { 0xd76d, 0x0000 }, + { 0xd76e, 0x0000 }, + { 0xd76f, 0x0000 }, + { 0xd770, 0x0000 }, + { 0xd771, 0x0000 }, + { 0xd772, 0x0000 }, + { 0xd773, 0x0000 }, + { 0xd774, 0x0000 }, + { 0xd775, 0x0000 }, + { 0xd776, 0x0000 }, + { 0xd777, 0x0000 }, + { 0xd778, 0x0000 }, + { 0xd779, 0x0000 }, + { 0xd77a, 0x0000 }, + { 0xd77b, 0x0000 }, + { 0xd77c, 0x0000 }, + { 0xd77d, 0x0000 }, + { 0xd77e, 0x0000 }, + { 0xd77f, 0x0000 }, + { 0xd780, 0x0000 }, + { 0xd781, 0x0000 }, + { 0xd782, 0x0000 }, + { 0xd783, 0x0000 }, + { 0xd784, 0x0000 }, + { 0xd785, 0x0000 }, + { 0xd786, 0x0000 }, + { 0xd787, 0x0000 }, + { 0xd788, 0x0000 }, + { 0xd789, 0x0000 }, + { 0xd78a, 0x0000 }, + { 0xd78b, 0x0000 }, + { 0xd78c, 0x0000 }, + { 0xd78d, 0x0000 }, + { 0xd78e, 0x0000 }, + { 0xd78f, 0x0000 }, + { 0xd790, 0x0000 }, + { 0xd791, 0x0000 }, + { 0xd792, 0x0000 }, + { 0xd793, 0x0000 }, + { 0xd794, 0x0000 }, + { 0xd795, 0x0000 }, + { 0xd796, 0x0000 }, + { 0xd797, 0x0000 }, + { 0xd798, 0x0000 }, + { 0xd799, 0x0000 }, + { 0xd79a, 0x0000 }, + { 0xd79b, 0x0000 }, + { 0xd79c, 0x0000 }, + { 0xd79d, 0x0000 }, + { 0xd79e, 0x0000 }, + { 0xd79f, 0x0000 }, + { 0xd7a0, 0x0000 }, + { 0xd7a1, 0x0000 }, + { 0xd7a2, 0x0000 }, + { 0xd7a3, 0x0000 }, + { 0xd7a4, 0x0000 }, + { 0xd7a5, 0x0000 }, + { 0xd7a6, 0x0000 }, + { 0xd7a7, 0x0000 }, + { 0xd7a8, 0x0000 }, + { 0xd7a9, 0x0000 }, + { 0xd7aa, 0x0000 }, + { 0xd7ab, 0x0000 }, + { 0xd7ac, 0x0000 }, + { 0xd7ad, 0x0000 }, + { 0xd7ae, 0x0000 }, + { 0xd7af, 0x0000 }, + { 0xd7b0, 0x0000 }, + { 0xd7b1, 0x0000 }, + { 0xd7b2, 0x0000 }, + { 0xd7b3, 0x0000 }, + { 0xd7b4, 0x0000 }, + { 0xd7b5, 0x0000 }, + { 0xd7b6, 0x0000 }, + { 0xd7b7, 0x0000 }, + { 0xd7b8, 0x0000 }, + { 0xd7b9, 0x0000 }, + { 0xd7ba, 0x0000 }, + { 0xd7bb, 0x0000 }, + { 0xd7bc, 0x0000 }, + { 0xd7bd, 0x0000 }, + { 0xd7be, 0x0000 }, + { 0xd7bf, 0x0000 }, + { 0xd7c0, 0x0000 }, + { 0xd7c1, 0x0000 }, + { 0xd7c2, 0x0000 }, + { 0xd7c3, 0x0000 }, + { 0xd7c4, 0x0000 }, + { 0xd7c5, 0x0000 }, + { 0xd7c6, 0x0000 }, + { 0xd7c7, 0x0000 }, + { 0xd7c8, 0x0000 }, + { 0xd7c9, 0x0000 }, + { 0xd7ca, 0x0000 }, + { 0xd7cb, 0x0000 }, + { 0xd7cc, 0x0000 }, + { 0xd7cd, 0x0000 }, + { 0xd7ce, 0x0000 }, + { 0xd7cf, 0x0000 }, + { 0xd7d0, 0x0000 }, + { 0xd7d1, 0x0000 }, + { 0xd7d2, 0x0000 }, + { 0xd7d3, 0x0000 }, + { 0xd7d4, 0x0000 }, + { 0xd7d5, 0x0000 }, + { 0xd7d6, 0x0000 }, + { 0xd7d7, 0x0000 }, + { 0xd7d8, 0x0000 }, + { 0xd7d9, 0x0000 }, + { 0xd7da, 0x0000 }, + { 0xd7db, 0x0000 }, + { 0xd7dc, 0x0000 }, + { 0xd7dd, 0x0000 }, + { 0xd7de, 0x0000 }, + { 0xd7df, 0x0000 }, + { 0xd7e0, 0x0000 }, + { 0xd7e1, 0x0000 }, + { 0xd7e2, 0x0000 }, + { 0xd7e3, 0x0000 }, + { 0xd7e4, 0x0000 }, + { 0xd7e5, 0x0000 }, + { 0xd7e6, 0x0000 }, + { 0xd7e7, 0x0000 }, + { 0xd7e8, 0x0000 }, + { 0xd7e9, 0x0000 }, + { 0xd7ea, 0x0000 }, + { 0xd7eb, 0x0000 }, + { 0xd7ec, 0x0000 }, + { 0xd7ed, 0x0000 }, + { 0xd7ee, 0x0000 }, + { 0xd7ef, 0x0000 }, + { 0xd7f0, 0x0000 }, + { 0xd7f1, 0x0000 }, + { 0xd7f2, 0x0000 }, + { 0xd7f3, 0x0000 }, + { 0xd7f4, 0x0000 }, + { 0xd7f5, 0x0000 }, + { 0xd7f6, 0x0000 }, + { 0xd7f7, 0x0000 }, + { 0xd7f8, 0x0000 }, + { 0xd7f9, 0x0000 }, + { 0xd7fa, 0x0000 }, + { 0xd7fb, 0x0000 }, + { 0xd7fc, 0x0000 }, + { 0xd7fd, 0x0000 }, + { 0xd7fe, 0x0000 }, + { 0xd7ff, 0x0000 }, + { 0xd800, 0x0000 }, + { 0xd801, 0x0000 }, + { 0xd802, 0x0000 }, + { 0xd803, 0x0000 }, + { 0xd804, 0x0000 }, + { 0xd805, 0x0000 }, + { 0xd806, 0x0000 }, + { 0xd807, 0x0000 }, + { 0xd808, 0x0000 }, + { 0xd809, 0x0000 }, + { 0xd80a, 0x0000 }, + { 0xd80b, 0x0000 }, + { 0xd80c, 0x0000 }, + { 0xd80d, 0x0000 }, + { 0xd80e, 0x0000 }, + { 0xd80f, 0x0000 }, + { 0xd810, 0x0000 }, + { 0xd811, 0x0000 }, + { 0xd812, 0x0000 }, + { 0xd813, 0x0000 }, + { 0xd814, 0x0000 }, + { 0xd815, 0x0000 }, + { 0xd816, 0x0000 }, + { 0xd817, 0x0000 }, + { 0xd818, 0x0000 }, + { 0xd819, 0x0000 }, + { 0xd81a, 0x0000 }, + { 0xd81b, 0x0000 }, + { 0xd81c, 0x0000 }, + { 0xd81d, 0x0000 }, + { 0xd81e, 0x0000 }, + { 0xd81f, 0x0000 }, + { 0xd820, 0x0000 }, + { 0xd821, 0x0000 }, + { 0xd822, 0x0000 }, + { 0xd823, 0x0000 }, + { 0xd824, 0x0000 }, + { 0xd825, 0x0000 }, + { 0xd826, 0x0000 }, + { 0xd827, 0x0000 }, + { 0xd828, 0x0000 }, + { 0xd829, 0x0000 }, + { 0xd82a, 0x0000 }, + { 0xd82b, 0x0000 }, + { 0xd82c, 0x0000 }, + { 0xd82d, 0x0000 }, + { 0xd82e, 0x0000 }, + { 0xd82f, 0x0000 }, + { 0xd830, 0x0000 }, + { 0xd831, 0x0000 }, + { 0xd832, 0x0000 }, + { 0xd833, 0x0000 }, + { 0xd834, 0x0000 }, + { 0xd835, 0x0000 }, + { 0xd836, 0x0000 }, + { 0xd837, 0x0000 }, + { 0xd838, 0x0000 }, + { 0xd839, 0x0000 }, + { 0xd83a, 0x0000 }, + { 0xd83b, 0x0000 }, + { 0xd83c, 0x0000 }, + { 0xd83d, 0x0000 }, + { 0xd83e, 0x0000 }, + { 0xd83f, 0x0000 }, + { 0xd840, 0x0000 }, + { 0xd841, 0x0000 }, + { 0xd842, 0x0000 }, + { 0xd843, 0x0000 }, + { 0xd844, 0x0000 }, + { 0xd845, 0x0000 }, + { 0xd846, 0x0000 }, + { 0xd847, 0x0000 }, + { 0xd848, 0x0000 }, + { 0xd849, 0x0000 }, + { 0xd84a, 0x0000 }, + { 0xd84b, 0x0000 }, + { 0xd84c, 0x0000 }, + { 0xd84d, 0x0000 }, + { 0xd84e, 0x0000 }, + { 0xd84f, 0x0000 }, + { 0xd850, 0x0000 }, + { 0xd851, 0x0000 }, + { 0xd852, 0x0000 }, + { 0xd853, 0x0000 }, + { 0xd854, 0x0000 }, + { 0xd855, 0x0000 }, + { 0xd856, 0x0000 }, + { 0xd857, 0x0000 }, + { 0xd858, 0x0000 }, + { 0xd859, 0x0000 }, + { 0xd85a, 0x0000 }, + { 0xd85b, 0x0000 }, + { 0xd85c, 0x0000 }, + { 0xd85d, 0x0000 }, + { 0xd85e, 0x0000 }, + { 0xd85f, 0x0000 }, + { 0xd860, 0x0000 }, + { 0xd861, 0x0000 }, + { 0xd862, 0x0000 }, + { 0xd863, 0x0000 }, + { 0xd864, 0x0000 }, + { 0xd865, 0x0000 }, + { 0xd866, 0x0000 }, + { 0xd867, 0x0000 }, + { 0xd868, 0x0000 }, + { 0xd869, 0x0000 }, + { 0xd86a, 0x0000 }, + { 0xd86b, 0x0000 }, + { 0xd86c, 0x0000 }, + { 0xd86d, 0x0000 }, + { 0xd86e, 0x0000 }, + { 0xd86f, 0x0000 }, + { 0xd870, 0x0000 }, + { 0xd871, 0x0000 }, + { 0xd872, 0x0000 }, + { 0xd873, 0x0000 }, + { 0xd874, 0x0000 }, + { 0xd875, 0x0000 }, + { 0xd876, 0x0000 }, + { 0xd877, 0x0000 }, + { 0xd878, 0x0000 }, + { 0xd879, 0x0000 }, + { 0xd87a, 0x0000 }, + { 0xd87b, 0x0000 }, + { 0xd87c, 0x0000 }, + { 0xd87d, 0x0000 }, + { 0xd87e, 0x0000 }, + { 0xd87f, 0x0000 }, + { 0xd880, 0x0000 }, + { 0xd881, 0x0000 }, + { 0xd882, 0x0000 }, + { 0xd883, 0x0000 }, + { 0xd884, 0x0000 }, + { 0xd885, 0x0000 }, + { 0xd886, 0x0000 }, + { 0xd887, 0x0000 }, + { 0xd888, 0x0000 }, + { 0xd889, 0x0000 }, + { 0xd88a, 0x0000 }, + { 0xd88b, 0x0000 }, + { 0xd88c, 0x0000 }, + { 0xd88d, 0x0000 }, + { 0xd88e, 0x0000 }, + { 0xd88f, 0x0000 }, + { 0xd890, 0x0000 }, + { 0xd891, 0x0000 }, + { 0xd892, 0x0000 }, + { 0xd893, 0x0000 }, + { 0xd894, 0x0000 }, + { 0xd895, 0x0000 }, + { 0xd896, 0x0000 }, + { 0xd897, 0x0000 }, + { 0xd898, 0x0000 }, + { 0xd899, 0x0000 }, + { 0xd89a, 0x0000 }, + { 0xd89b, 0x0000 }, + { 0xd89c, 0x0000 }, + { 0xd89d, 0x0000 }, + { 0xd89e, 0x0000 }, + { 0xd89f, 0x0000 }, + { 0xd8a0, 0x0000 }, + { 0xd8a1, 0x0000 }, + { 0xd8a2, 0x0000 }, + { 0xd8a3, 0x0000 }, + { 0xd8a4, 0x0000 }, + { 0xd8a5, 0x0000 }, + { 0xd8a6, 0x0000 }, + { 0xd8a7, 0x0000 }, + { 0xd8a8, 0x0000 }, + { 0xd8a9, 0x0000 }, + { 0xd8aa, 0x0000 }, + { 0xd8ab, 0x0000 }, + { 0xd8ac, 0x0000 }, + { 0xd8ad, 0x0000 }, + { 0xd8ae, 0x0000 }, + { 0xd8af, 0x0000 }, + { 0xd8b0, 0x0000 }, + { 0xd8b1, 0x0000 }, + { 0xd8b2, 0x0000 }, + { 0xd8b3, 0x0000 }, + { 0xd8b4, 0x0000 }, + { 0xd8b5, 0x0000 }, + { 0xd8b6, 0x0000 }, + { 0xd8b7, 0x0000 }, + { 0xd8b8, 0x0000 }, + { 0xd8b9, 0x0000 }, + { 0xd8ba, 0x0000 }, + { 0xd8bb, 0x0000 }, + { 0xd8bc, 0x0000 }, + { 0xd8bd, 0x0000 }, + { 0xd8be, 0x0000 }, + { 0xd8bf, 0x0000 }, + { 0xd8c0, 0x0000 }, + { 0xd8c1, 0x0000 }, + { 0xd8c2, 0x0000 }, + { 0xd8c3, 0x0000 }, + { 0xd8c4, 0x0000 }, + { 0xd8c5, 0x0000 }, + { 0xd8c6, 0x0000 }, + { 0xd8c7, 0x0000 }, + { 0xd8c8, 0x0000 }, + { 0xd8c9, 0x0000 }, + { 0xd8ca, 0x0000 }, + { 0xd8cb, 0x0000 }, + { 0xd8cc, 0x0000 }, + { 0xd8cd, 0x0000 }, + { 0xd8ce, 0x0000 }, + { 0xd8cf, 0x0000 }, + { 0xd8d0, 0x0000 }, + { 0xd8d1, 0x0000 }, + { 0xd8d2, 0x0000 }, + { 0xd8d3, 0x0000 }, + { 0xd8d4, 0x0000 }, + { 0xd8d5, 0x0000 }, + { 0xd8d6, 0x0000 }, + { 0xd8d7, 0x0000 }, + { 0xd8d8, 0x0000 }, + { 0xd8d9, 0x0000 }, + { 0xd8da, 0x0000 }, + { 0xd8db, 0x0000 }, + { 0xd8dc, 0x0000 }, + { 0xd8dd, 0x0000 }, + { 0xd8de, 0x0000 }, + { 0xd8df, 0x0000 }, + { 0xd8e0, 0x0000 }, + { 0xd8e1, 0x0000 }, + { 0xd8e2, 0x0000 }, + { 0xd8e3, 0x0000 }, + { 0xd8e4, 0x0000 }, + { 0xd8e5, 0x0000 }, + { 0xd8e6, 0x0000 }, + { 0xd8e7, 0x0000 }, + { 0xd8e8, 0x0000 }, + { 0xd8e9, 0x0000 }, + { 0xd8ea, 0x0000 }, + { 0xd8eb, 0x0000 }, + { 0xd8ec, 0x0000 }, + { 0xd8ed, 0x0000 }, + { 0xd8ee, 0x0000 }, + { 0xd8ef, 0x0000 }, + { 0xd8f0, 0x0000 }, + { 0xd8f1, 0x0000 }, + { 0xd8f2, 0x0000 }, + { 0xd8f3, 0x0000 }, + { 0xd8f4, 0x0000 }, + { 0xd8f5, 0x0000 }, + { 0xd8f6, 0x0000 }, + { 0xd8f7, 0x0000 }, + { 0xd8f8, 0x0000 }, + { 0xd8f9, 0x0000 }, + { 0xd8fa, 0x0000 }, + { 0xd8fb, 0x0000 }, + { 0xd8fc, 0x0000 }, + { 0xd8fd, 0x0000 }, + { 0xd8fe, 0x0000 }, + { 0xd8ff, 0x0000 }, + { 0xd900, 0x0000 }, + { 0xd901, 0x0000 }, + { 0xd902, 0x0000 }, + { 0xd903, 0x0000 }, + { 0xd904, 0x0000 }, + { 0xd905, 0x0000 }, + { 0xd906, 0x0000 }, + { 0xd907, 0x0000 }, + { 0xd908, 0x0000 }, + { 0xd909, 0x0000 }, + { 0xd90a, 0x0000 }, + { 0xd90b, 0x0000 }, + { 0xd90c, 0x0000 }, + { 0xd90d, 0x0000 }, + { 0xd90e, 0x0000 }, + { 0xd90f, 0x0000 }, + { 0xd910, 0x0000 }, + { 0xd911, 0x0000 }, + { 0xd912, 0x0000 }, + { 0xd913, 0x0000 }, + { 0xd914, 0x0000 }, + { 0xd915, 0x0000 }, + { 0xd916, 0x0000 }, + { 0xd917, 0x0000 }, + { 0xd918, 0x0000 }, + { 0xd919, 0x0000 }, + { 0xd91a, 0x0000 }, + { 0xd91b, 0x0000 }, + { 0xd91c, 0x0000 }, + { 0xd91d, 0x0000 }, + { 0xd91e, 0x0000 }, + { 0xd91f, 0x0000 }, + { 0xd920, 0x0000 }, + { 0xd921, 0x0000 }, + { 0xd922, 0x0000 }, + { 0xd923, 0x0000 }, + { 0xd924, 0x0000 }, + { 0xd925, 0x0000 }, + { 0xd926, 0x0000 }, + { 0xd927, 0x0000 }, + { 0xd928, 0x0000 }, + { 0xd929, 0x0000 }, + { 0xd92a, 0x0000 }, + { 0xd92b, 0x0000 }, + { 0xd92c, 0x0000 }, + { 0xd92d, 0x0000 }, + { 0xd92e, 0x0000 }, + { 0xd92f, 0x0000 }, + { 0xd930, 0x0000 }, + { 0xd931, 0x0000 }, + { 0xd932, 0x0000 }, + { 0xd933, 0x0000 }, + { 0xd934, 0x0000 }, + { 0xd935, 0x0000 }, + { 0xd936, 0x0000 }, + { 0xd937, 0x0000 }, + { 0xd938, 0x0000 }, + { 0xd939, 0x0000 }, + { 0xd93a, 0x0000 }, + { 0xd93b, 0x0000 }, + { 0xd93c, 0x0000 }, + { 0xd93d, 0x0000 }, + { 0xd93e, 0x0000 }, + { 0xd93f, 0x0000 }, + { 0xd940, 0x0000 }, + { 0xd941, 0x0000 }, + { 0xd942, 0x0000 }, + { 0xd943, 0x0000 }, + { 0xd944, 0x0000 }, + { 0xd945, 0x0000 }, + { 0xd946, 0x0000 }, + { 0xd947, 0x0000 }, + { 0xd948, 0x0000 }, + { 0xd949, 0x0000 }, + { 0xd94a, 0x0000 }, + { 0xd94b, 0x0000 }, + { 0xd94c, 0x0000 }, + { 0xd94d, 0x0000 }, + { 0xd94e, 0x0000 }, + { 0xd94f, 0x0000 }, + { 0xd950, 0x0000 }, + { 0xd951, 0x0000 }, + { 0xd952, 0x0000 }, + { 0xd953, 0x0000 }, + { 0xd954, 0x0000 }, + { 0xd955, 0x0000 }, + { 0xd956, 0x0000 }, + { 0xd957, 0x0000 }, + { 0xd958, 0x0000 }, + { 0xd959, 0x0000 }, + { 0xd95a, 0x0000 }, + { 0xd95b, 0x0000 }, + { 0xd95c, 0x0000 }, + { 0xd95d, 0x0000 }, + { 0xd95e, 0x0000 }, + { 0xd95f, 0x0000 }, + { 0xd960, 0x0000 }, + { 0xd961, 0x0000 }, + { 0xd962, 0x0000 }, + { 0xd963, 0x0000 }, + { 0xd964, 0x0000 }, + { 0xd965, 0x0000 }, + { 0xd966, 0x0000 }, + { 0xd967, 0x0000 }, + { 0xd968, 0x0000 }, + { 0xd969, 0x0000 }, + { 0xd96a, 0x0000 }, + { 0xd96b, 0x0000 }, + { 0xd96c, 0x0000 }, + { 0xd96d, 0x0000 }, + { 0xd96e, 0x0000 }, + { 0xd96f, 0x0000 }, + { 0xd970, 0x0000 }, + { 0xd971, 0x0000 }, + { 0xd972, 0x0000 }, + { 0xd973, 0x0000 }, + { 0xd974, 0x0000 }, + { 0xd975, 0x0000 }, + { 0xd976, 0x0000 }, + { 0xd977, 0x0000 }, + { 0xd978, 0x0000 }, + { 0xd979, 0x0000 }, + { 0xd97a, 0x0000 }, + { 0xd97b, 0x0000 }, + { 0xd97c, 0x0000 }, + { 0xd97d, 0x0000 }, + { 0xd97e, 0x0000 }, + { 0xd97f, 0x0000 }, + { 0xd980, 0x0000 }, + { 0xd981, 0x0000 }, + { 0xd982, 0x0000 }, + { 0xd983, 0x0000 }, + { 0xd984, 0x0000 }, + { 0xd985, 0x0000 }, + { 0xd986, 0x0000 }, + { 0xd987, 0x0000 }, + { 0xd988, 0x0000 }, + { 0xd989, 0x0000 }, + { 0xd98a, 0x0000 }, + { 0xd98b, 0x0000 }, + { 0xd98c, 0x0000 }, + { 0xd98d, 0x0000 }, + { 0xd98e, 0x0000 }, + { 0xd98f, 0x0000 }, + { 0xd990, 0x0000 }, + { 0xd991, 0x0000 }, + { 0xd992, 0x0000 }, + { 0xd993, 0x0000 }, + { 0xd994, 0x0000 }, + { 0xd995, 0x0000 }, + { 0xd996, 0x0000 }, + { 0xd997, 0x0000 }, + { 0xd998, 0x0000 }, + { 0xd999, 0x0000 }, + { 0xd99a, 0x0000 }, + { 0xd99b, 0x0000 }, + { 0xd99c, 0x0000 }, + { 0xd99d, 0x0000 }, + { 0xd99e, 0x0000 }, + { 0xd99f, 0x0000 }, + { 0xd9a0, 0x0000 }, + { 0xd9a1, 0x0000 }, + { 0xd9a2, 0x0000 }, + { 0xd9a3, 0x0000 }, + { 0xd9a4, 0x0000 }, + { 0xd9a5, 0x0000 }, + { 0xd9a6, 0x0000 }, + { 0xd9a7, 0x0000 }, + { 0xd9a8, 0x0000 }, + { 0xd9a9, 0x0000 }, + { 0xd9aa, 0x0000 }, + { 0xd9ab, 0x0000 }, + { 0xd9ac, 0x0000 }, + { 0xd9ad, 0x0000 }, + { 0xd9ae, 0x0000 }, + { 0xd9af, 0x0000 }, + { 0xd9b0, 0x0000 }, + { 0xd9b1, 0x0000 }, + { 0xd9b2, 0x0000 }, + { 0xd9b3, 0x0000 }, + { 0xd9b4, 0x0000 }, + { 0xd9b5, 0x0000 }, + { 0xd9b6, 0x0000 }, + { 0xd9b7, 0x0000 }, + { 0xd9b8, 0x0000 }, + { 0xd9b9, 0x0000 }, + { 0xd9ba, 0x0000 }, + { 0xd9bb, 0x0000 }, + { 0xd9bc, 0x0000 }, + { 0xd9bd, 0x0000 }, + { 0xd9be, 0x0000 }, + { 0xd9bf, 0x0000 }, + { 0xd9c0, 0x0000 }, + { 0xd9c1, 0x0000 }, + { 0xd9c2, 0x0000 }, + { 0xd9c3, 0x0000 }, + { 0xd9c4, 0x0000 }, + { 0xd9c5, 0x0000 }, + { 0xd9c6, 0x0000 }, + { 0xd9c7, 0x0000 }, + { 0xd9c8, 0x0000 }, + { 0xd9c9, 0x0000 }, + { 0xd9ca, 0x0000 }, + { 0xd9cb, 0x0000 }, + { 0xd9cc, 0x0000 }, + { 0xd9cd, 0x0000 }, + { 0xd9ce, 0x0000 }, + { 0xd9cf, 0x0000 }, + { 0xd9d0, 0x0000 }, + { 0xd9d1, 0x0000 }, + { 0xd9d2, 0x0000 }, + { 0xd9d3, 0x0000 }, + { 0xd9d4, 0x0000 }, + { 0xd9d5, 0x0000 }, + { 0xd9d6, 0x0000 }, + { 0xd9d7, 0x0000 }, + { 0xd9d8, 0x0000 }, + { 0xd9d9, 0x0000 }, + { 0xd9da, 0x0000 }, + { 0xd9db, 0x0000 }, + { 0xd9dc, 0x0000 }, + { 0xd9dd, 0x0000 }, + { 0xd9de, 0x0000 }, + { 0xd9df, 0x0000 }, + { 0xd9e0, 0x0000 }, + { 0xd9e1, 0x0000 }, + { 0xd9e2, 0x0000 }, + { 0xd9e3, 0x0000 }, + { 0xd9e4, 0x0000 }, + { 0xd9e5, 0x0000 }, + { 0xd9e6, 0x0000 }, + { 0xd9e7, 0x0000 }, + { 0xd9e8, 0x0000 }, + { 0xd9e9, 0x0000 }, + { 0xd9ea, 0x0000 }, + { 0xd9eb, 0x0000 }, + { 0xd9ec, 0x0000 }, + { 0xd9ed, 0x0000 }, + { 0xd9ee, 0x0000 }, + { 0xd9ef, 0x0000 }, + { 0xd9f0, 0x0000 }, + { 0xd9f1, 0x0000 }, + { 0xd9f2, 0x0000 }, + { 0xd9f3, 0x0000 }, + { 0xd9f4, 0x0000 }, + { 0xd9f5, 0x0000 }, + { 0xd9f6, 0x0000 }, + { 0xd9f7, 0x0000 }, + { 0xd9f8, 0x0000 }, + { 0xd9f9, 0x0000 }, + { 0xd9fa, 0x0000 }, + { 0xd9fb, 0x0000 }, + { 0xd9fc, 0x0000 }, + { 0xd9fd, 0x0000 }, + { 0xd9fe, 0x0000 }, + { 0xd9ff, 0x0000 }, + { 0xda00, 0x0000 }, + { 0xda01, 0x0000 }, + { 0xda02, 0x0000 }, + { 0xda03, 0x0000 }, + { 0xda04, 0x0000 }, + { 0xda05, 0x0000 }, + { 0xda06, 0x0000 }, + { 0xda07, 0x0000 }, + { 0xda08, 0x0000 }, + { 0xda09, 0x0000 }, + { 0xda0a, 0x0000 }, + { 0xda0b, 0x0000 }, + { 0xda0c, 0x0000 }, + { 0xda0d, 0x0000 }, + { 0xda0e, 0x0000 }, + { 0xda0f, 0x0000 }, + { 0xda10, 0x0000 }, + { 0xda11, 0x0000 }, + { 0xda12, 0x0000 }, + { 0xda13, 0x0000 }, + { 0xda14, 0x0000 }, + { 0xda15, 0x0000 }, + { 0xda16, 0x0000 }, + { 0xda17, 0x0000 }, + { 0xda18, 0x0000 }, + { 0xda19, 0x0000 }, + { 0xda1a, 0x0000 }, + { 0xda1b, 0x0000 }, + { 0xda1c, 0x0000 }, + { 0xda1d, 0x0000 }, + { 0xda1e, 0x0000 }, + { 0xda1f, 0x0000 }, + { 0xda20, 0x0000 }, + { 0xda21, 0x0000 }, + { 0xda22, 0x0000 }, + { 0xda23, 0x0000 }, + { 0xda24, 0x0000 }, + { 0xda25, 0x0000 }, + { 0xda26, 0x0000 }, + { 0xda27, 0x0000 }, + { 0xda28, 0x0000 }, + { 0xda29, 0x0000 }, + { 0xda2a, 0x0000 }, + { 0xda2b, 0x0000 }, + { 0xda2c, 0x0000 }, + { 0xda2d, 0x0000 }, + { 0xda2e, 0x0000 }, + { 0xda2f, 0x0000 }, + { 0xda30, 0x0000 }, + { 0xda31, 0x0000 }, + { 0xda32, 0x0000 }, + { 0xda33, 0x0000 }, + { 0xda34, 0x0000 }, + { 0xda35, 0x0000 }, + { 0xda36, 0x0000 }, + { 0xda37, 0x0000 }, + { 0xda38, 0x0000 }, + { 0xda39, 0x0000 }, + { 0xda3a, 0x0000 }, + { 0xda3b, 0x0000 }, + { 0xda3c, 0x0000 }, + { 0xda3d, 0x0000 }, + { 0xda3e, 0x0000 }, + { 0xda3f, 0x0000 }, + { 0xda40, 0x0000 }, + { 0xda41, 0x0000 }, + { 0xda42, 0x0000 }, + { 0xda43, 0x0000 }, + { 0xda44, 0x0000 }, + { 0xda45, 0x0000 }, + { 0xda46, 0x0000 }, + { 0xda47, 0x0000 }, + { 0xda48, 0x0000 }, + { 0xda49, 0x0000 }, + { 0xda4a, 0x0000 }, + { 0xda4b, 0x0000 }, + { 0xda4c, 0x0000 }, + { 0xda4d, 0x0000 }, + { 0xda4e, 0x0000 }, + { 0xda4f, 0x0000 }, + { 0xda50, 0x0000 }, + { 0xda51, 0x0000 }, + { 0xda52, 0x0000 }, + { 0xda53, 0x0000 }, + { 0xda54, 0x0000 }, + { 0xda55, 0x0000 }, + { 0xda56, 0x0000 }, + { 0xda57, 0x0000 }, + { 0xda58, 0x0000 }, + { 0xda59, 0x0000 }, + { 0xda5a, 0x0000 }, + { 0xda5b, 0x0000 }, + { 0xda5c, 0x0000 }, + { 0xda5d, 0x0000 }, + { 0xda5e, 0x0000 }, + { 0xda5f, 0x0000 }, + { 0xda60, 0x0000 }, + { 0xda61, 0x0000 }, + { 0xda62, 0x0000 }, + { 0xda63, 0x0000 }, + { 0xda64, 0x0000 }, + { 0xda65, 0x0000 }, + { 0xda66, 0x0000 }, + { 0xda67, 0x0000 }, + { 0xda68, 0x0000 }, + { 0xda69, 0x0000 }, + { 0xda6a, 0x0000 }, + { 0xda6b, 0x0000 }, + { 0xda6c, 0x0000 }, + { 0xda6d, 0x0000 }, + { 0xda6e, 0x0000 }, + { 0xda6f, 0x0000 }, + { 0xda70, 0x0000 }, + { 0xda71, 0x0000 }, + { 0xda72, 0x0000 }, + { 0xda73, 0x0000 }, + { 0xda74, 0x0000 }, + { 0xda75, 0x0000 }, + { 0xda76, 0x0000 }, + { 0xda77, 0x0000 }, + { 0xda78, 0x0000 }, + { 0xda79, 0x0000 }, + { 0xda7a, 0x0000 }, + { 0xda7b, 0x0000 }, + { 0xda7c, 0x0000 }, + { 0xda7d, 0x0000 }, + { 0xda7e, 0x0000 }, + { 0xda7f, 0x0000 }, + { 0xda80, 0x0000 }, + { 0xda81, 0x0000 }, + { 0xda82, 0x0000 }, + { 0xda83, 0x0000 }, + { 0xda84, 0x0000 }, + { 0xda85, 0x0000 }, + { 0xda86, 0x0000 }, + { 0xda87, 0x0000 }, + { 0xda88, 0x0000 }, + { 0xda89, 0x0000 }, + { 0xda8a, 0x0000 }, + { 0xda8b, 0x0000 }, + { 0xda8c, 0x0000 }, + { 0xda8d, 0x0000 }, + { 0xda8e, 0x0000 }, + { 0xda8f, 0x0000 }, + { 0xda90, 0x0000 }, + { 0xda91, 0x0000 }, + { 0xda92, 0x0000 }, + { 0xda93, 0x0000 }, + { 0xda94, 0x0000 }, + { 0xda95, 0x0000 }, + { 0xda96, 0x0000 }, + { 0xda97, 0x0000 }, + { 0xda98, 0x0000 }, + { 0xda99, 0x0000 }, + { 0xda9a, 0x0000 }, + { 0xda9b, 0x0000 }, + { 0xda9c, 0x0000 }, + { 0xda9d, 0x0000 }, + { 0xda9e, 0x0000 }, + { 0xda9f, 0x0000 }, + { 0xdaa0, 0x0000 }, + { 0xdaa1, 0x0000 }, + { 0xdaa2, 0x0000 }, + { 0xdaa3, 0x0000 }, + { 0xdaa4, 0x0000 }, + { 0xdaa5, 0x0000 }, + { 0xdaa6, 0x0000 }, + { 0xdaa7, 0x0000 }, + { 0xdaa8, 0x0000 }, + { 0xdaa9, 0x0000 }, + { 0xdaaa, 0x0000 }, + { 0xdaab, 0x0000 }, + { 0xdaac, 0x0000 }, + { 0xdaad, 0x0000 }, + { 0xdaae, 0x0000 }, + { 0xdaaf, 0x0000 }, + { 0xdab0, 0x0000 }, + { 0xdab1, 0x0000 }, + { 0xdab2, 0x0000 }, + { 0xdab3, 0x0000 }, + { 0xdab4, 0x0000 }, + { 0xdab5, 0x0000 }, + { 0xdab6, 0x0000 }, + { 0xdab7, 0x0000 }, + { 0xdab8, 0x0000 }, + { 0xdab9, 0x0000 }, + { 0xdaba, 0x0000 }, + { 0xdabb, 0x0000 }, + { 0xdabc, 0x0000 }, + { 0xdabd, 0x0000 }, + { 0xdabe, 0x0000 }, + { 0xdabf, 0x0000 }, + { 0xdac0, 0x0000 }, + { 0xdac1, 0x0000 }, + { 0xdac2, 0x0000 }, + { 0xdac3, 0x0000 }, + { 0xdac4, 0x0000 }, + { 0xdac5, 0x0000 }, + { 0xdac6, 0x0000 }, + { 0xdac7, 0x0000 }, + { 0xdac8, 0x0000 }, + { 0xdac9, 0x0000 }, + { 0xdaca, 0x0000 }, + { 0xdacb, 0x0000 }, + { 0xdacc, 0x0000 }, + { 0xdacd, 0x0000 }, + { 0xdace, 0x0000 }, + { 0xdacf, 0x0000 }, + { 0xdad0, 0x0000 }, + { 0xdad1, 0x0000 }, + { 0xdad2, 0x0000 }, + { 0xdad3, 0x0000 }, + { 0xdad4, 0x0000 }, + { 0xdad5, 0x0000 }, + { 0xdad6, 0x0000 }, + { 0xdad7, 0x0000 }, + { 0xdad8, 0x0000 }, + { 0xdad9, 0x0000 }, + { 0xdada, 0x0000 }, + { 0xdadb, 0x0000 }, + { 0xdadc, 0x0000 }, + { 0xdadd, 0x0000 }, + { 0xdade, 0x0000 }, + { 0xdadf, 0x0000 }, + { 0xdae0, 0x0000 }, + { 0xdae1, 0x0000 }, + { 0xdae2, 0x0000 }, + { 0xdae3, 0x0000 }, + { 0xdae4, 0x0000 }, + { 0xdae5, 0x0000 }, + { 0xdae6, 0x0000 }, + { 0xdae7, 0x0000 }, + { 0xdae8, 0x0000 }, + { 0xdae9, 0x0000 }, + { 0xdaea, 0x0000 }, + { 0xdaeb, 0x0000 }, + { 0xdaec, 0x0000 }, + { 0xdaed, 0x0000 }, + { 0xdaee, 0x0000 }, + { 0xdaef, 0x0000 }, + { 0xdaf0, 0x0000 }, + { 0xdaf1, 0x0000 }, + { 0xdaf2, 0x0000 }, + { 0xdaf3, 0x0000 }, + { 0xdaf4, 0x0000 }, + { 0xdaf5, 0x0000 }, + { 0xdaf6, 0x0000 }, + { 0xdaf7, 0x0000 }, + { 0xdaf8, 0x0000 }, + { 0xdaf9, 0x0000 }, + { 0xdafa, 0x0000 }, + { 0xdafb, 0x0000 }, + { 0xdafc, 0x0000 }, + { 0xdafd, 0x0000 }, + { 0xdafe, 0x0000 }, + { 0xdaff, 0x0000 }, + { 0xdb00, 0x0000 }, + { 0xdb01, 0x0000 }, + { 0xdb02, 0x0000 }, + { 0xdb03, 0x0000 }, + { 0xdb04, 0x0000 }, + { 0xdb05, 0x0000 }, + { 0xdb06, 0x0000 }, + { 0xdb07, 0x0000 }, + { 0xdb08, 0x0000 }, + { 0xdb09, 0x0000 }, + { 0xdb0a, 0x0000 }, + { 0xdb0b, 0x0000 }, + { 0xdb0c, 0x0000 }, + { 0xdb0d, 0x0000 }, + { 0xdb0e, 0x0000 }, + { 0xdb0f, 0x0000 }, + { 0xdb10, 0x0000 }, + { 0xdb11, 0x0000 }, + { 0xdb12, 0x0000 }, + { 0xdb13, 0x0000 }, + { 0xdb14, 0x0000 }, + { 0xdb15, 0x0000 }, + { 0xdb16, 0x0000 }, + { 0xdb17, 0x0000 }, + { 0xdb18, 0x0000 }, + { 0xdb19, 0x0000 }, + { 0xdb1a, 0x0000 }, + { 0xdb1b, 0x0000 }, + { 0xdb1c, 0x0000 }, + { 0xdb1d, 0x0000 }, + { 0xdb1e, 0x0000 }, + { 0xdb1f, 0x0000 }, + { 0xdb20, 0x0000 }, + { 0xdb21, 0x0000 }, + { 0xdb22, 0x0000 }, + { 0xdb23, 0x0000 }, + { 0xdb24, 0x0000 }, + { 0xdb25, 0x0000 }, + { 0xdb26, 0x0000 }, + { 0xdb27, 0x0000 }, + { 0xdb28, 0x0000 }, + { 0xdb29, 0x0000 }, + { 0xdb2a, 0x0000 }, + { 0xdb2b, 0x0000 }, + { 0xdb2c, 0x0000 }, + { 0xdb2d, 0x0000 }, + { 0xdb2e, 0x0000 }, + { 0xdb2f, 0x0000 }, + { 0xdb30, 0x0000 }, + { 0xdb31, 0x0000 }, + { 0xdb32, 0x0000 }, + { 0xdb33, 0x0000 }, + { 0xdb34, 0x0000 }, + { 0xdb35, 0x0000 }, + { 0xdb36, 0x0000 }, + { 0xdb37, 0x0000 }, + { 0xdb38, 0x0000 }, + { 0xdb39, 0x0000 }, + { 0xdb3a, 0x0000 }, + { 0xdb3b, 0x0000 }, + { 0xdb3c, 0x0000 }, + { 0xdb3d, 0x0000 }, + { 0xdb3e, 0x0000 }, + { 0xdb3f, 0x0000 }, + { 0xdb40, 0x0000 }, + { 0xdb41, 0x0000 }, + { 0xdb42, 0x0000 }, + { 0xdb43, 0x0000 }, + { 0xdb44, 0x0000 }, + { 0xdb45, 0x0000 }, + { 0xdb46, 0x0000 }, + { 0xdb47, 0x0000 }, + { 0xdb48, 0x0000 }, + { 0xdb49, 0x0000 }, + { 0xdb4a, 0x0000 }, + { 0xdb4b, 0x0000 }, + { 0xdb4c, 0x0000 }, + { 0xdb4d, 0x0000 }, + { 0xdb4e, 0x0000 }, + { 0xdb4f, 0x0000 }, + { 0xdb50, 0x0000 }, + { 0xdb51, 0x0000 }, + { 0xdb52, 0x0000 }, + { 0xdb53, 0x0000 }, + { 0xdb54, 0x0000 }, + { 0xdb55, 0x0000 }, + { 0xdb56, 0x0000 }, + { 0xdb57, 0x0000 }, + { 0xdb58, 0x0000 }, + { 0xdb59, 0x0000 }, + { 0xdb5a, 0x0000 }, + { 0xdb5b, 0x0000 }, + { 0xdb5c, 0x0000 }, + { 0xdb5d, 0x0000 }, + { 0xdb5e, 0x0000 }, + { 0xdb5f, 0x0000 }, + { 0xdb60, 0x0000 }, + { 0xdb61, 0x0000 }, + { 0xdb62, 0x0000 }, + { 0xdb63, 0x0000 }, + { 0xdb64, 0x0000 }, + { 0xdb65, 0x0000 }, + { 0xdb66, 0x0000 }, + { 0xdb67, 0x0000 }, + { 0xdb68, 0x0000 }, + { 0xdb69, 0x0000 }, + { 0xdb6a, 0x0000 }, + { 0xdb6b, 0x0000 }, + { 0xdb6c, 0x0000 }, + { 0xdb6d, 0x0000 }, + { 0xdb6e, 0x0000 }, + { 0xdb6f, 0x0000 }, + { 0xdb70, 0x0000 }, + { 0xdb71, 0x0000 }, + { 0xdb72, 0x0000 }, + { 0xdb73, 0x0000 }, + { 0xdb74, 0x0000 }, + { 0xdb75, 0x0000 }, + { 0xdb76, 0x0000 }, + { 0xdb77, 0x0000 }, + { 0xdb78, 0x0000 }, + { 0xdb79, 0x0000 }, + { 0xdb7a, 0x0000 }, + { 0xdb7b, 0x0000 }, + { 0xdb7c, 0x0000 }, + { 0xdb7d, 0x0000 }, + { 0xdb7e, 0x0000 }, + { 0xdb7f, 0x0000 }, + { 0xdb80, 0x0000 }, + { 0xdb81, 0x0000 }, + { 0xdb82, 0x0000 }, + { 0xdb83, 0x0000 }, + { 0xdb84, 0x0000 }, + { 0xdb85, 0x0000 }, + { 0xdb86, 0x0000 }, + { 0xdb87, 0x0000 }, + { 0xdb88, 0x0000 }, + { 0xdb89, 0x0000 }, + { 0xdb8a, 0x0000 }, + { 0xdb8b, 0x0000 }, + { 0xdb8c, 0x0000 }, + { 0xdb8d, 0x0000 }, + { 0xdb8e, 0x0000 }, + { 0xdb8f, 0x0000 }, + { 0xdb90, 0x0000 }, + { 0xdb91, 0x0000 }, + { 0xdb92, 0x0000 }, + { 0xdb93, 0x0000 }, + { 0xdb94, 0x0000 }, + { 0xdb95, 0x0000 }, + { 0xdb96, 0x0000 }, + { 0xdb97, 0x0000 }, + { 0xdb98, 0x0000 }, + { 0xdb99, 0x0000 }, + { 0xdb9a, 0x0000 }, + { 0xdb9b, 0x0000 }, + { 0xdb9c, 0x0000 }, + { 0xdb9d, 0x0000 }, + { 0xdb9e, 0x0000 }, + { 0xdb9f, 0x0000 }, + { 0xdba0, 0x0000 }, + { 0xdba1, 0x0000 }, + { 0xdba2, 0x0000 }, + { 0xdba3, 0x0000 }, + { 0xdba4, 0x0000 }, + { 0xdba5, 0x0000 }, + { 0xdba6, 0x0000 }, + { 0xdba7, 0x0000 }, + { 0xdba8, 0x0000 }, + { 0xdba9, 0x0000 }, + { 0xdbaa, 0x0000 }, + { 0xdbab, 0x0000 }, + { 0xdbac, 0x0000 }, + { 0xdbad, 0x0000 }, + { 0xdbae, 0x0000 }, + { 0xdbaf, 0x0000 }, + { 0xdbb0, 0x0000 }, + { 0xdbb1, 0x0000 }, + { 0xdbb2, 0x0000 }, + { 0xdbb3, 0x0000 }, + { 0xdbb4, 0x0000 }, + { 0xdbb5, 0x0000 }, + { 0xdbb6, 0x0000 }, + { 0xdbb7, 0x0000 }, + { 0xdbb8, 0x0000 }, + { 0xdbb9, 0x0000 }, + { 0xdbba, 0x0000 }, + { 0xdbbb, 0x0000 }, + { 0xdbbc, 0x0000 }, + { 0xdbbd, 0x0000 }, + { 0xdbbe, 0x0000 }, + { 0xdbbf, 0x0000 }, + { 0xdbc0, 0x0000 }, + { 0xdbc1, 0x0000 }, + { 0xdbc2, 0x0000 }, + { 0xdbc3, 0x0000 }, + { 0xdbc4, 0x0000 }, + { 0xdbc5, 0x0000 }, + { 0xdbc6, 0x0000 }, + { 0xdbc7, 0x0000 }, + { 0xdbc8, 0x0000 }, + { 0xdbc9, 0x0000 }, + { 0xdbca, 0x0000 }, + { 0xdbcb, 0x0000 }, + { 0xdbcc, 0x0000 }, + { 0xdbcd, 0x0000 }, + { 0xdbce, 0x0000 }, + { 0xdbcf, 0x0000 }, + { 0xdbd0, 0x0000 }, + { 0xdbd1, 0x0000 }, + { 0xdbd2, 0x0000 }, + { 0xdbd3, 0x0000 }, + { 0xdbd4, 0x0000 }, + { 0xdbd5, 0x0000 }, + { 0xdbd6, 0x0000 }, + { 0xdbd7, 0x0000 }, + { 0xdbd8, 0x0000 }, + { 0xdbd9, 0x0000 }, + { 0xdbda, 0x0000 }, + { 0xdbdb, 0x0000 }, + { 0xdbdc, 0x0000 }, + { 0xdbdd, 0x0000 }, + { 0xdbde, 0x0000 }, + { 0xdbdf, 0x0000 }, + { 0xdbe0, 0x0000 }, + { 0xdbe1, 0x0000 }, + { 0xdbe2, 0x0000 }, + { 0xdbe3, 0x0000 }, + { 0xdbe4, 0x0000 }, + { 0xdbe5, 0x0000 }, + { 0xdbe6, 0x0000 }, + { 0xdbe7, 0x0000 }, + { 0xdbe8, 0x0000 }, + { 0xdbe9, 0x0000 }, + { 0xdbea, 0x0000 }, + { 0xdbeb, 0x0000 }, + { 0xdbec, 0x0000 }, + { 0xdbed, 0x0000 }, + { 0xdbee, 0x0000 }, + { 0xdbef, 0x0000 }, + { 0xdbf0, 0x0000 }, + { 0xdbf1, 0x0000 }, + { 0xdbf2, 0x0000 }, + { 0xdbf3, 0x0000 }, + { 0xdbf4, 0x0000 }, + { 0xdbf5, 0x0000 }, + { 0xdbf6, 0x0000 }, + { 0xdbf7, 0x0000 }, + { 0xdbf8, 0x0000 }, + { 0xdbf9, 0x0000 }, + { 0xdbfa, 0x0000 }, + { 0xdbfb, 0x0000 }, + { 0xdbfc, 0x0000 }, + { 0xdbfd, 0x0000 }, + { 0xdbfe, 0x0000 }, + { 0xdbff, 0x0000 }, + { 0xdc00, 0x0000 }, + { 0xdc01, 0x0000 }, + { 0xdc02, 0x0000 }, + { 0xdc03, 0x0000 }, + { 0xdc04, 0x0000 }, + { 0xdc05, 0x0000 }, + { 0xdc06, 0x0000 }, + { 0xdc07, 0x0000 }, + { 0xdc08, 0x0000 }, + { 0xdc09, 0x0000 }, + { 0xdc0a, 0x0000 }, + { 0xdc0b, 0x0000 }, + { 0xdc0c, 0x0000 }, + { 0xdc0d, 0x0000 }, + { 0xdc0e, 0x0000 }, + { 0xdc0f, 0x0000 }, + { 0xdc10, 0x0000 }, + { 0xdc11, 0x0000 }, + { 0xdc12, 0x0000 }, + { 0xdc13, 0x0000 }, + { 0xdc14, 0x0000 }, + { 0xdc15, 0x0000 }, + { 0xdc16, 0x0000 }, + { 0xdc17, 0x0000 }, + { 0xdc18, 0x0000 }, + { 0xdc19, 0x0000 }, + { 0xdc1a, 0x0000 }, + { 0xdc1b, 0x0000 }, + { 0xdc1c, 0x0000 }, + { 0xdc1d, 0x0000 }, + { 0xdc1e, 0x0000 }, + { 0xdc1f, 0x0000 }, + { 0xdc20, 0x0000 }, + { 0xdc21, 0x0000 }, + { 0xdc22, 0x0000 }, + { 0xdc23, 0x0000 }, + { 0xdc24, 0x0000 }, + { 0xdc25, 0x0000 }, + { 0xdc26, 0x0000 }, + { 0xdc27, 0x0000 }, + { 0xdc28, 0x0000 }, + { 0xdc29, 0x0000 }, + { 0xdc2a, 0x0000 }, + { 0xdc2b, 0x0000 }, + { 0xdc2c, 0x0000 }, + { 0xdc2d, 0x0000 }, + { 0xdc2e, 0x0000 }, + { 0xdc2f, 0x0000 }, + { 0xdc30, 0x0000 }, + { 0xdc31, 0x0000 }, + { 0xdc32, 0x0000 }, + { 0xdc33, 0x0000 }, + { 0xdc34, 0x0000 }, + { 0xdc35, 0x0000 }, + { 0xdc36, 0x0000 }, + { 0xdc37, 0x0000 }, + { 0xdc38, 0x0000 }, + { 0xdc39, 0x0000 }, + { 0xdc3a, 0x0000 }, + { 0xdc3b, 0x0000 }, + { 0xdc3c, 0x0000 }, + { 0xdc3d, 0x0000 }, + { 0xdc3e, 0x0000 }, + { 0xdc3f, 0x0000 }, + { 0xdc40, 0x0000 }, + { 0xdc41, 0x0000 }, + { 0xdc42, 0x0000 }, + { 0xdc43, 0x0000 }, + { 0xdc44, 0x0000 }, + { 0xdc45, 0x0000 }, + { 0xdc46, 0x0000 }, + { 0xdc47, 0x0000 }, + { 0xdc48, 0x0000 }, + { 0xdc49, 0x0000 }, + { 0xdc4a, 0x0000 }, + { 0xdc4b, 0x0000 }, + { 0xdc4c, 0x0000 }, + { 0xdc4d, 0x0000 }, + { 0xdc4e, 0x0000 }, + { 0xdc4f, 0x0000 }, + { 0xdc50, 0x0000 }, + { 0xdc51, 0x0000 }, + { 0xdc52, 0x0000 }, + { 0xdc53, 0x0000 }, + { 0xdc54, 0x0000 }, + { 0xdc55, 0x0000 }, + { 0xdc56, 0x0000 }, + { 0xdc57, 0x0000 }, + { 0xdc58, 0x0000 }, + { 0xdc59, 0x0000 }, + { 0xdc5a, 0x0000 }, + { 0xdc5b, 0x0000 }, + { 0xdc5c, 0x0000 }, + { 0xdc5d, 0x0000 }, + { 0xdc5e, 0x0000 }, + { 0xdc5f, 0x0000 }, + { 0xdc60, 0x0000 }, + { 0xdc61, 0x0000 }, + { 0xdc62, 0x0000 }, + { 0xdc63, 0x0000 }, + { 0xdc64, 0x0000 }, + { 0xdc65, 0x0000 }, + { 0xdc66, 0x0000 }, + { 0xdc67, 0x0000 }, + { 0xdc68, 0x0000 }, + { 0xdc69, 0x0000 }, + { 0xdc6a, 0x0000 }, + { 0xdc6b, 0x0000 }, + { 0xdc6c, 0x0000 }, + { 0xdc6d, 0x0000 }, + { 0xdc6e, 0x0000 }, + { 0xdc6f, 0x0000 }, + { 0xdc70, 0x0000 }, + { 0xdc71, 0x0000 }, + { 0xdc72, 0x0000 }, + { 0xdc73, 0x0000 }, + { 0xdc74, 0x0000 }, + { 0xdc75, 0x0000 }, + { 0xdc76, 0x0000 }, + { 0xdc77, 0x0000 }, + { 0xdc78, 0x0000 }, + { 0xdc79, 0x0000 }, + { 0xdc7a, 0x0000 }, + { 0xdc7b, 0x0000 }, + { 0xdc7c, 0x0000 }, + { 0xdc7d, 0x0000 }, + { 0xdc7e, 0x0000 }, + { 0xdc7f, 0x0000 }, + { 0xdc80, 0x0000 }, + { 0xdc81, 0x0000 }, + { 0xdc82, 0x0000 }, + { 0xdc83, 0x0000 }, + { 0xdc84, 0x0000 }, + { 0xdc85, 0x0000 }, + { 0xdc86, 0x0000 }, + { 0xdc87, 0x0000 }, + { 0xdc88, 0x0000 }, + { 0xdc89, 0x0000 }, + { 0xdc8a, 0x0000 }, + { 0xdc8b, 0x0000 }, + { 0xdc8c, 0x0000 }, + { 0xdc8d, 0x0000 }, + { 0xdc8e, 0x0000 }, + { 0xdc8f, 0x0000 }, + { 0xdc90, 0x0000 }, + { 0xdc91, 0x0000 }, + { 0xdc92, 0x0000 }, + { 0xdc93, 0x0000 }, + { 0xdc94, 0x0000 }, + { 0xdc95, 0x0000 }, + { 0xdc96, 0x0000 }, + { 0xdc97, 0x0000 }, + { 0xdc98, 0x0000 }, + { 0xdc99, 0x0000 }, + { 0xdc9a, 0x0000 }, + { 0xdc9b, 0x0000 }, + { 0xdc9c, 0x0000 }, + { 0xdc9d, 0x0000 }, + { 0xdc9e, 0x0000 }, + { 0xdc9f, 0x0000 }, + { 0xdca0, 0x0000 }, + { 0xdca1, 0x0000 }, + { 0xdca2, 0x0000 }, + { 0xdca3, 0x0000 }, + { 0xdca4, 0x0000 }, + { 0xdca5, 0x0000 }, + { 0xdca6, 0x0000 }, + { 0xdca7, 0x0000 }, + { 0xdca8, 0x0000 }, + { 0xdca9, 0x0000 }, + { 0xdcaa, 0x0000 }, + { 0xdcab, 0x0000 }, + { 0xdcac, 0x0000 }, + { 0xdcad, 0x0000 }, + { 0xdcae, 0x0000 }, + { 0xdcaf, 0x0000 }, + { 0xdcb0, 0x0000 }, + { 0xdcb1, 0x0000 }, + { 0xdcb2, 0x0000 }, + { 0xdcb3, 0x0000 }, + { 0xdcb4, 0x0000 }, + { 0xdcb5, 0x0000 }, + { 0xdcb6, 0x0000 }, + { 0xdcb7, 0x0000 }, + { 0xdcb8, 0x0000 }, + { 0xdcb9, 0x0000 }, + { 0xdcba, 0x0000 }, + { 0xdcbb, 0x0000 }, + { 0xdcbc, 0x0000 }, + { 0xdcbd, 0x0000 }, + { 0xdcbe, 0x0000 }, + { 0xdcbf, 0x0000 }, + { 0xdcc0, 0x0000 }, + { 0xdcc1, 0x0000 }, + { 0xdcc2, 0x0000 }, + { 0xdcc3, 0x0000 }, + { 0xdcc4, 0x0000 }, + { 0xdcc5, 0x0000 }, + { 0xdcc6, 0x0000 }, + { 0xdcc7, 0x0000 }, + { 0xdcc8, 0x0000 }, + { 0xdcc9, 0x0000 }, + { 0xdcca, 0x0000 }, + { 0xdccb, 0x0000 }, + { 0xdccc, 0x0000 }, + { 0xdccd, 0x0000 }, + { 0xdcce, 0x0000 }, + { 0xdccf, 0x0000 }, + { 0xdcd0, 0x0000 }, + { 0xdcd1, 0x0000 }, + { 0xdcd2, 0x0000 }, + { 0xdcd3, 0x0000 }, + { 0xdcd4, 0x0000 }, + { 0xdcd5, 0x0000 }, + { 0xdcd6, 0x0000 }, + { 0xdcd7, 0x0000 }, + { 0xdcd8, 0x0000 }, + { 0xdcd9, 0x0000 }, + { 0xdcda, 0x0000 }, + { 0xdcdb, 0x0000 }, + { 0xdcdc, 0x0000 }, + { 0xdcdd, 0x0000 }, + { 0xdcde, 0x0000 }, + { 0xdcdf, 0x0000 }, + { 0xdce0, 0x0000 }, + { 0xdce1, 0x0000 }, + { 0xdce2, 0x0000 }, + { 0xdce3, 0x0000 }, + { 0xdce4, 0x0000 }, + { 0xdce5, 0x0000 }, + { 0xdce6, 0x0000 }, + { 0xdce7, 0x0000 }, + { 0xdce8, 0x0000 }, + { 0xdce9, 0x0000 }, + { 0xdcea, 0x0000 }, + { 0xdceb, 0x0000 }, + { 0xdcec, 0x0000 }, + { 0xdced, 0x0000 }, + { 0xdcee, 0x0000 }, + { 0xdcef, 0x0000 }, + { 0xdcf0, 0x0000 }, + { 0xdcf1, 0x0000 }, + { 0xdcf2, 0x0000 }, + { 0xdcf3, 0x0000 }, + { 0xdcf4, 0x0000 }, + { 0xdcf5, 0x0000 }, + { 0xdcf6, 0x0000 }, + { 0xdcf7, 0x0000 }, + { 0xdcf8, 0x0000 }, + { 0xdcf9, 0x0000 }, + { 0xdcfa, 0x0000 }, + { 0xdcfb, 0x0000 }, + { 0xdcfc, 0x0000 }, + { 0xdcfd, 0x0000 }, + { 0xdcfe, 0x0000 }, + { 0xdcff, 0x0000 }, + { 0xdd00, 0x0000 }, + { 0xdd01, 0x0000 }, + { 0xdd02, 0x0000 }, + { 0xdd03, 0x0000 }, + { 0xdd04, 0x0000 }, + { 0xdd05, 0x0000 }, + { 0xdd06, 0x0000 }, + { 0xdd07, 0x0000 }, + { 0xdd08, 0x0000 }, + { 0xdd09, 0x0000 }, + { 0xdd0a, 0x0000 }, + { 0xdd0b, 0x0000 }, + { 0xdd0c, 0x0000 }, + { 0xdd0d, 0x0000 }, + { 0xdd0e, 0x0000 }, + { 0xdd0f, 0x0000 }, + { 0xdd10, 0x0000 }, + { 0xdd11, 0x0000 }, + { 0xdd12, 0x0000 }, + { 0xdd13, 0x0000 }, + { 0xdd14, 0x0000 }, + { 0xdd15, 0x0000 }, + { 0xdd16, 0x0000 }, + { 0xdd17, 0x0000 }, + { 0xdd18, 0x0000 }, + { 0xdd19, 0x0000 }, + { 0xdd1a, 0x0000 }, + { 0xdd1b, 0x0000 }, + { 0xdd1c, 0x0000 }, + { 0xdd1d, 0x0000 }, + { 0xdd1e, 0x0000 }, + { 0xdd1f, 0x0000 }, + { 0xdd20, 0x0000 }, + { 0xdd21, 0x0000 }, + { 0xdd22, 0x0000 }, + { 0xdd23, 0x0000 }, + { 0xdd24, 0x0000 }, + { 0xdd25, 0x0000 }, + { 0xdd26, 0x0000 }, + { 0xdd27, 0x0000 }, + { 0xdd28, 0x0000 }, + { 0xdd29, 0x0000 }, + { 0xdd2a, 0x0000 }, + { 0xdd2b, 0x0000 }, + { 0xdd2c, 0x0000 }, + { 0xdd2d, 0x0000 }, + { 0xdd2e, 0x0000 }, + { 0xdd2f, 0x0000 }, + { 0xdd30, 0x0000 }, + { 0xdd31, 0x0000 }, + { 0xdd32, 0x0000 }, + { 0xdd33, 0x0000 }, + { 0xdd34, 0x0000 }, + { 0xdd35, 0x0000 }, + { 0xdd36, 0x0000 }, + { 0xdd37, 0x0000 }, + { 0xdd38, 0x0000 }, + { 0xdd39, 0x0000 }, + { 0xdd3a, 0x0000 }, + { 0xdd3b, 0x0000 }, + { 0xdd3c, 0x0000 }, + { 0xdd3d, 0x0000 }, + { 0xdd3e, 0x0000 }, + { 0xdd3f, 0x0000 }, + { 0xdd40, 0x0000 }, + { 0xdd41, 0x0000 }, + { 0xdd42, 0x0000 }, + { 0xdd43, 0x0000 }, + { 0xdd44, 0x0000 }, + { 0xdd45, 0x0000 }, + { 0xdd46, 0x0000 }, + { 0xdd47, 0x0000 }, + { 0xdd48, 0x0000 }, + { 0xdd49, 0x0000 }, + { 0xdd4a, 0x0000 }, + { 0xdd4b, 0x0000 }, + { 0xdd4c, 0x0000 }, + { 0xdd4d, 0x0000 }, + { 0xdd4e, 0x0000 }, + { 0xdd4f, 0x0000 }, + { 0xdd50, 0x0000 }, + { 0xdd51, 0x0000 }, + { 0xdd52, 0x0000 }, + { 0xdd53, 0x0000 }, + { 0xdd54, 0x0000 }, + { 0xdd55, 0x0000 }, + { 0xdd56, 0x0000 }, + { 0xdd57, 0x0000 }, + { 0xdd58, 0x0000 }, + { 0xdd59, 0x0000 }, + { 0xdd5a, 0x0000 }, + { 0xdd5b, 0x0000 }, + { 0xdd5c, 0x0000 }, + { 0xdd5d, 0x0000 }, + { 0xdd5e, 0x0000 }, + { 0xdd5f, 0x0000 }, + { 0xdd60, 0x0000 }, + { 0xdd61, 0x0000 }, + { 0xdd62, 0x0000 }, + { 0xdd63, 0x0000 }, + { 0xdd64, 0x0000 }, + { 0xdd65, 0x0000 }, + { 0xdd66, 0x0000 }, + { 0xdd67, 0x0000 }, + { 0xdd68, 0x0000 }, + { 0xdd69, 0x0000 }, + { 0xdd6a, 0x0000 }, + { 0xdd6b, 0x0000 }, + { 0xdd6c, 0x0000 }, + { 0xdd6d, 0x0000 }, + { 0xdd6e, 0x0000 }, + { 0xdd6f, 0x0000 }, + { 0xdd70, 0x0000 }, + { 0xdd71, 0x0000 }, + { 0xdd72, 0x0000 }, + { 0xdd73, 0x0000 }, + { 0xdd74, 0x0000 }, + { 0xdd75, 0x0000 }, + { 0xdd76, 0x0000 }, + { 0xdd77, 0x0000 }, + { 0xdd78, 0x0000 }, + { 0xdd79, 0x0000 }, + { 0xdd7a, 0x0000 }, + { 0xdd7b, 0x0000 }, + { 0xdd7c, 0x0000 }, + { 0xdd7d, 0x0000 }, + { 0xdd7e, 0x0000 }, + { 0xdd7f, 0x0000 }, + { 0xdd80, 0x0000 }, + { 0xdd81, 0x0000 }, + { 0xdd82, 0x0000 }, + { 0xdd83, 0x0000 }, + { 0xdd84, 0x0000 }, + { 0xdd85, 0x0000 }, + { 0xdd86, 0x0000 }, + { 0xdd87, 0x0000 }, + { 0xdd88, 0x0000 }, + { 0xdd89, 0x0000 }, + { 0xdd8a, 0x0000 }, + { 0xdd8b, 0x0000 }, + { 0xdd8c, 0x0000 }, + { 0xdd8d, 0x0000 }, + { 0xdd8e, 0x0000 }, + { 0xdd8f, 0x0000 }, + { 0xdd90, 0x0000 }, + { 0xdd91, 0x0000 }, + { 0xdd92, 0x0000 }, + { 0xdd93, 0x0000 }, + { 0xdd94, 0x0000 }, + { 0xdd95, 0x0000 }, + { 0xdd96, 0x0000 }, + { 0xdd97, 0x0000 }, + { 0xdd98, 0x0000 }, + { 0xdd99, 0x0000 }, + { 0xdd9a, 0x0000 }, + { 0xdd9b, 0x0000 }, + { 0xdd9c, 0x0000 }, + { 0xdd9d, 0x0000 }, + { 0xdd9e, 0x0000 }, + { 0xdd9f, 0x0000 }, + { 0xdda0, 0x0000 }, + { 0xdda1, 0x0000 }, + { 0xdda2, 0x0000 }, + { 0xdda3, 0x0000 }, + { 0xdda4, 0x0000 }, + { 0xdda5, 0x0000 }, + { 0xdda6, 0x0000 }, + { 0xdda7, 0x0000 }, + { 0xdda8, 0x0000 }, + { 0xdda9, 0x0000 }, + { 0xddaa, 0x0000 }, + { 0xddab, 0x0000 }, + { 0xddac, 0x0000 }, + { 0xddad, 0x0000 }, + { 0xddae, 0x0000 }, + { 0xddaf, 0x0000 }, + { 0xddb0, 0x0000 }, + { 0xddb1, 0x0000 }, + { 0xddb2, 0x0000 }, + { 0xddb3, 0x0000 }, + { 0xddb4, 0x0000 }, + { 0xddb5, 0x0000 }, + { 0xddb6, 0x0000 }, + { 0xddb7, 0x0000 }, + { 0xddb8, 0x0000 }, + { 0xddb9, 0x0000 }, + { 0xddba, 0x0000 }, + { 0xddbb, 0x0000 }, + { 0xddbc, 0x0000 }, + { 0xddbd, 0x0000 }, + { 0xddbe, 0x0000 }, + { 0xddbf, 0x0000 }, + { 0xddc0, 0x0000 }, + { 0xddc1, 0x0000 }, + { 0xddc2, 0x0000 }, + { 0xddc3, 0x0000 }, + { 0xddc4, 0x0000 }, + { 0xddc5, 0x0000 }, + { 0xddc6, 0x0000 }, + { 0xddc7, 0x0000 }, + { 0xddc8, 0x0000 }, + { 0xddc9, 0x0000 }, + { 0xddca, 0x0000 }, + { 0xddcb, 0x0000 }, + { 0xddcc, 0x0000 }, + { 0xddcd, 0x0000 }, + { 0xddce, 0x0000 }, + { 0xddcf, 0x0000 }, + { 0xddd0, 0x0000 }, + { 0xddd1, 0x0000 }, + { 0xddd2, 0x0000 }, + { 0xddd3, 0x0000 }, + { 0xddd4, 0x0000 }, + { 0xddd5, 0x0000 }, + { 0xddd6, 0x0000 }, + { 0xddd7, 0x0000 }, + { 0xddd8, 0x0000 }, + { 0xddd9, 0x0000 }, + { 0xddda, 0x0000 }, + { 0xdddb, 0x0000 }, + { 0xdddc, 0x0000 }, + { 0xdddd, 0x0000 }, + { 0xddde, 0x0000 }, + { 0xdddf, 0x0000 }, + { 0xdde0, 0x0000 }, + { 0xdde1, 0x0000 }, + { 0xdde2, 0x0000 }, + { 0xdde3, 0x0000 }, + { 0xdde4, 0x0000 }, + { 0xdde5, 0x0000 }, + { 0xdde6, 0x0000 }, + { 0xdde7, 0x0000 }, + { 0xdde8, 0x0000 }, + { 0xdde9, 0x0000 }, + { 0xddea, 0x0000 }, + { 0xddeb, 0x0000 }, + { 0xddec, 0x0000 }, + { 0xdded, 0x0000 }, + { 0xddee, 0x0000 }, + { 0xddef, 0x0000 }, + { 0xddf0, 0x0000 }, + { 0xddf1, 0x0000 }, + { 0xddf2, 0x0000 }, + { 0xddf3, 0x0000 }, + { 0xddf4, 0x0000 }, + { 0xddf5, 0x0000 }, + { 0xddf6, 0x0000 }, + { 0xddf7, 0x0000 }, + { 0xddf8, 0x0000 }, + { 0xddf9, 0x0000 }, + { 0xddfa, 0x0000 }, + { 0xddfb, 0x0000 }, + { 0xddfc, 0x0000 }, + { 0xddfd, 0x0000 }, + { 0xddfe, 0x0000 }, + { 0xddff, 0x0000 }, + { 0xde00, 0x0000 }, + { 0xde01, 0x0000 }, + { 0xde02, 0x0000 }, + { 0xde03, 0x0000 }, + { 0xde04, 0x0000 }, + { 0xde05, 0x0000 }, + { 0xde06, 0x0000 }, + { 0xde07, 0x0000 }, + { 0xde08, 0x0000 }, + { 0xde09, 0x0000 }, + { 0xde0a, 0x0000 }, + { 0xde0b, 0x0000 }, + { 0xde0c, 0x0000 }, + { 0xde0d, 0x0000 }, + { 0xde0e, 0x0000 }, + { 0xde0f, 0x0000 }, + { 0xde10, 0x0000 }, + { 0xde11, 0x0000 }, + { 0xde12, 0x0000 }, + { 0xde13, 0x0000 }, + { 0xde14, 0x0000 }, + { 0xde15, 0x0000 }, + { 0xde16, 0x0000 }, + { 0xde17, 0x0000 }, + { 0xde18, 0x0000 }, + { 0xde19, 0x0000 }, + { 0xde1a, 0x0000 }, + { 0xde1b, 0x0000 }, + { 0xde1c, 0x0000 }, + { 0xde1d, 0x0000 }, + { 0xde1e, 0x0000 }, + { 0xde1f, 0x0000 }, + { 0xde20, 0x0000 }, + { 0xde21, 0x0000 }, + { 0xde22, 0x0000 }, + { 0xde23, 0x0000 }, + { 0xde24, 0x0000 }, + { 0xde25, 0x0000 }, + { 0xde26, 0x0000 }, + { 0xde27, 0x0000 }, + { 0xde28, 0x0000 }, + { 0xde29, 0x0000 }, + { 0xde2a, 0x0000 }, + { 0xde2b, 0x0000 }, + { 0xde2c, 0x0000 }, + { 0xde2d, 0x0000 }, + { 0xde2e, 0x0000 }, + { 0xde2f, 0x0000 }, + { 0xde30, 0x0000 }, + { 0xde31, 0x0000 }, + { 0xde32, 0x0000 }, + { 0xde33, 0x0000 }, + { 0xde34, 0x0000 }, + { 0xde35, 0x0000 }, + { 0xde36, 0x0000 }, + { 0xde37, 0x0000 }, + { 0xde38, 0x0000 }, + { 0xde39, 0x0000 }, + { 0xde3a, 0x0000 }, + { 0xde3b, 0x0000 }, + { 0xde3c, 0x0000 }, + { 0xde3d, 0x0000 }, + { 0xde3e, 0x0000 }, + { 0xde3f, 0x0000 }, + { 0xde40, 0x0000 }, + { 0xde41, 0x0000 }, + { 0xde42, 0x0000 }, + { 0xde43, 0x0000 }, + { 0xde44, 0x0000 }, + { 0xde45, 0x0000 }, + { 0xde46, 0x0000 }, + { 0xde47, 0x0000 }, + { 0xde48, 0x0000 }, + { 0xde49, 0x0000 }, + { 0xde4a, 0x0000 }, + { 0xde4b, 0x0000 }, + { 0xde4c, 0x0000 }, + { 0xde4d, 0x0000 }, + { 0xde4e, 0x0000 }, + { 0xde4f, 0x0000 }, + { 0xde50, 0x0000 }, + { 0xde51, 0x0000 }, + { 0xde52, 0x0000 }, + { 0xde53, 0x0000 }, + { 0xde54, 0x0000 }, + { 0xde55, 0x0000 }, + { 0xde56, 0x0000 }, + { 0xde57, 0x0000 }, + { 0xde58, 0x0000 }, + { 0xde59, 0x0000 }, + { 0xde5a, 0x0000 }, + { 0xde5b, 0x0000 }, + { 0xde5c, 0x0000 }, + { 0xde5d, 0x0000 }, + { 0xde5e, 0x0000 }, + { 0xde5f, 0x0000 }, + { 0xde60, 0x0000 }, + { 0xde61, 0x0000 }, + { 0xde62, 0x0000 }, + { 0xde63, 0x0000 }, + { 0xde64, 0x0000 }, + { 0xde65, 0x0000 }, + { 0xde66, 0x0000 }, + { 0xde67, 0x0000 }, + { 0xde68, 0x0000 }, + { 0xde69, 0x0000 }, + { 0xde6a, 0x0000 }, + { 0xde6b, 0x0000 }, + { 0xde6c, 0x0000 }, + { 0xde6d, 0x0000 }, + { 0xde6e, 0x0000 }, + { 0xde6f, 0x0000 }, + { 0xde70, 0x0000 }, + { 0xde71, 0x0000 }, + { 0xde72, 0x0000 }, + { 0xde73, 0x0000 }, + { 0xde74, 0x0000 }, + { 0xde75, 0x0000 }, + { 0xde76, 0x0000 }, + { 0xde77, 0x0000 }, + { 0xde78, 0x0000 }, + { 0xde79, 0x0000 }, + { 0xde7a, 0x0000 }, + { 0xde7b, 0x0000 }, + { 0xde7c, 0x0000 }, + { 0xde7d, 0x0000 }, + { 0xde7e, 0x0000 }, + { 0xde7f, 0x0000 }, + { 0xde80, 0x0000 }, + { 0xde81, 0x0000 }, + { 0xde82, 0x0000 }, + { 0xde83, 0x0000 }, + { 0xde84, 0x0000 }, + { 0xde85, 0x0000 }, + { 0xde86, 0x0000 }, + { 0xde87, 0x0000 }, + { 0xde88, 0x0000 }, + { 0xde89, 0x0000 }, + { 0xde8a, 0x0000 }, + { 0xde8b, 0x0000 }, + { 0xde8c, 0x0000 }, + { 0xde8d, 0x0000 }, + { 0xde8e, 0x0000 }, + { 0xde8f, 0x0000 }, + { 0xde90, 0x0000 }, + { 0xde91, 0x0000 }, + { 0xde92, 0x0000 }, + { 0xde93, 0x0000 }, + { 0xde94, 0x0000 }, + { 0xde95, 0x0000 }, + { 0xde96, 0x0000 }, + { 0xde97, 0x0000 }, + { 0xde98, 0x0000 }, + { 0xde99, 0x0000 }, + { 0xde9a, 0x0000 }, + { 0xde9b, 0x0000 }, + { 0xde9c, 0x0000 }, + { 0xde9d, 0x0000 }, + { 0xde9e, 0x0000 }, + { 0xde9f, 0x0000 }, + { 0xdea0, 0x0000 }, + { 0xdea1, 0x0000 }, + { 0xdea2, 0x0000 }, + { 0xdea3, 0x0000 }, + { 0xdea4, 0x0000 }, + { 0xdea5, 0x0000 }, + { 0xdea6, 0x0000 }, + { 0xdea7, 0x0000 }, + { 0xdea8, 0x0000 }, + { 0xdea9, 0x0000 }, + { 0xdeaa, 0x0000 }, + { 0xdeab, 0x0000 }, + { 0xdeac, 0x0000 }, + { 0xdead, 0x0000 }, + { 0xdeae, 0x0000 }, + { 0xdeaf, 0x0000 }, + { 0xdeb0, 0x0000 }, + { 0xdeb1, 0x0000 }, + { 0xdeb2, 0x0000 }, + { 0xdeb3, 0x0000 }, + { 0xdeb4, 0x0000 }, + { 0xdeb5, 0x0000 }, + { 0xdeb6, 0x0000 }, + { 0xdeb7, 0x0000 }, + { 0xdeb8, 0x0000 }, + { 0xdeb9, 0x0000 }, + { 0xdeba, 0x0000 }, + { 0xdebb, 0x0000 }, + { 0xdebc, 0x0000 }, + { 0xdebd, 0x0000 }, + { 0xdebe, 0x0000 }, + { 0xdebf, 0x0000 }, + { 0xdec0, 0x0000 }, + { 0xdec1, 0x0000 }, + { 0xdec2, 0x0000 }, + { 0xdec3, 0x0000 }, + { 0xdec4, 0x0000 }, + { 0xdec5, 0x0000 }, + { 0xdec6, 0x0000 }, + { 0xdec7, 0x0000 }, + { 0xdec8, 0x0000 }, + { 0xdec9, 0x0000 }, + { 0xdeca, 0x0000 }, + { 0xdecb, 0x0000 }, + { 0xdecc, 0x0000 }, + { 0xdecd, 0x0000 }, + { 0xdece, 0x0000 }, + { 0xdecf, 0x0000 }, + { 0xded0, 0x0000 }, + { 0xded1, 0x0000 }, + { 0xded2, 0x0000 }, + { 0xded3, 0x0000 }, + { 0xded4, 0x0000 }, + { 0xded5, 0x0000 }, + { 0xded6, 0x0000 }, + { 0xded7, 0x0000 }, + { 0xded8, 0x0000 }, + { 0xded9, 0x0000 }, + { 0xdeda, 0x0000 }, + { 0xdedb, 0x0000 }, + { 0xdedc, 0x0000 }, + { 0xdedd, 0x0000 }, + { 0xdede, 0x0000 }, + { 0xdedf, 0x0000 }, + { 0xdee0, 0x0000 }, + { 0xdee1, 0x0000 }, + { 0xdee2, 0x0000 }, + { 0xdee3, 0x0000 }, + { 0xdee4, 0x0000 }, + { 0xdee5, 0x0000 }, + { 0xdee6, 0x0000 }, + { 0xdee7, 0x0000 }, + { 0xdee8, 0x0000 }, + { 0xdee9, 0x0000 }, + { 0xdeea, 0x0000 }, + { 0xdeeb, 0x0000 }, + { 0xdeec, 0x0000 }, + { 0xdeed, 0x0000 }, + { 0xdeee, 0x0000 }, + { 0xdeef, 0x0000 }, + { 0xdef0, 0x0000 }, + { 0xdef1, 0x0000 }, + { 0xdef2, 0x0000 }, + { 0xdef3, 0x0000 }, + { 0xdef4, 0x0000 }, + { 0xdef5, 0x0000 }, + { 0xdef6, 0x0000 }, + { 0xdef7, 0x0000 }, + { 0xdef8, 0x0000 }, + { 0xdef9, 0x0000 }, + { 0xdefa, 0x0000 }, + { 0xdefb, 0x0000 }, + { 0xdefc, 0x0000 }, + { 0xdefd, 0x0000 }, + { 0xdefe, 0x0000 }, + { 0xdeff, 0x0000 }, + { 0xdf00, 0x0000 }, + { 0xdf01, 0x0000 }, + { 0xdf02, 0x0000 }, + { 0xdf03, 0x0000 }, + { 0xdf04, 0x0000 }, + { 0xdf05, 0x0000 }, + { 0xdf06, 0x0000 }, + { 0xdf07, 0x0000 }, + { 0xdf08, 0x0000 }, + { 0xdf09, 0x0000 }, + { 0xdf0a, 0x0000 }, + { 0xdf0b, 0x0000 }, + { 0xdf0c, 0x0000 }, + { 0xdf0d, 0x0000 }, + { 0xdf0e, 0x0000 }, + { 0xdf0f, 0x0000 }, + { 0xdf10, 0x0000 }, + { 0xdf11, 0x0000 }, + { 0xdf12, 0x0000 }, + { 0xdf13, 0x0000 }, + { 0xdf14, 0x0000 }, + { 0xdf15, 0x0000 }, + { 0xdf16, 0x0000 }, + { 0xdf17, 0x0000 }, + { 0xdf18, 0x0000 }, + { 0xdf19, 0x0000 }, + { 0xdf1a, 0x0000 }, + { 0xdf1b, 0x0000 }, + { 0xdf1c, 0x0000 }, + { 0xdf1d, 0x0000 }, + { 0xdf1e, 0x0000 }, + { 0xdf1f, 0x0000 }, + { 0xdf20, 0x0000 }, + { 0xdf21, 0x0000 }, + { 0xdf22, 0x0000 }, + { 0xdf23, 0x0000 }, + { 0xdf24, 0x0000 }, + { 0xdf25, 0x0000 }, + { 0xdf26, 0x0000 }, + { 0xdf27, 0x0000 }, + { 0xdf28, 0x0000 }, + { 0xdf29, 0x0000 }, + { 0xdf2a, 0x0000 }, + { 0xdf2b, 0x0000 }, + { 0xdf2c, 0x0000 }, + { 0xdf2d, 0x0000 }, + { 0xdf2e, 0x0000 }, + { 0xdf2f, 0x0000 }, + { 0xdf30, 0x0000 }, + { 0xdf31, 0x0000 }, + { 0xdf32, 0x0000 }, + { 0xdf33, 0x0000 }, + { 0xdf34, 0x0000 }, + { 0xdf35, 0x0000 }, + { 0xdf36, 0x0000 }, + { 0xdf37, 0x0000 }, + { 0xdf38, 0x0000 }, + { 0xdf39, 0x0000 }, + { 0xdf3a, 0x0000 }, + { 0xdf3b, 0x0000 }, + { 0xdf3c, 0x0000 }, + { 0xdf3d, 0x0000 }, + { 0xdf3e, 0x0000 }, + { 0xdf3f, 0x0000 }, + { 0xdf40, 0x0000 }, + { 0xdf41, 0x0000 }, + { 0xdf42, 0x0000 }, + { 0xdf43, 0x0000 }, + { 0xdf44, 0x0000 }, + { 0xdf45, 0x0000 }, + { 0xdf46, 0x0000 }, + { 0xdf47, 0x0000 }, + { 0xdf48, 0x0000 }, + { 0xdf49, 0x0000 }, + { 0xdf4a, 0x0000 }, + { 0xdf4b, 0x0000 }, + { 0xdf4c, 0x0000 }, + { 0xdf4d, 0x0000 }, + { 0xdf4e, 0x0000 }, + { 0xdf4f, 0x0000 }, + { 0xdf50, 0x0000 }, + { 0xdf51, 0x0000 }, + { 0xdf52, 0x0000 }, + { 0xdf53, 0x0000 }, + { 0xdf54, 0x0000 }, + { 0xdf55, 0x0000 }, + { 0xdf56, 0x0000 }, + { 0xdf57, 0x0000 }, + { 0xdf58, 0x0000 }, + { 0xdf59, 0x0000 }, + { 0xdf5a, 0x0000 }, + { 0xdf5b, 0x0000 }, + { 0xdf5c, 0x0000 }, + { 0xdf5d, 0x0000 }, + { 0xdf5e, 0x0000 }, + { 0xdf5f, 0x0000 }, + { 0xdf60, 0x0000 }, + { 0xdf61, 0x0000 }, + { 0xdf62, 0x0000 }, + { 0xdf63, 0x0000 }, + { 0xdf64, 0x0000 }, + { 0xdf65, 0x0000 }, + { 0xdf66, 0x0000 }, + { 0xdf67, 0x0000 }, + { 0xdf68, 0x0000 }, + { 0xdf69, 0x0000 }, + { 0xdf6a, 0x0000 }, + { 0xdf6b, 0x0000 }, + { 0xdf6c, 0x0000 }, + { 0xdf6d, 0x0000 }, + { 0xdf6e, 0x0000 }, + { 0xdf6f, 0x0000 }, + { 0xdf70, 0x0000 }, + { 0xdf71, 0x0000 }, + { 0xdf72, 0x0000 }, + { 0xdf73, 0x0000 }, + { 0xdf74, 0x0000 }, + { 0xdf75, 0x0000 }, + { 0xdf76, 0x0000 }, + { 0xdf77, 0x0000 }, + { 0xdf78, 0x0000 }, + { 0xdf79, 0x0000 }, + { 0xdf7a, 0x0000 }, + { 0xdf7b, 0x0000 }, + { 0xdf7c, 0x0000 }, + { 0xdf7d, 0x0000 }, + { 0xdf7e, 0x0000 }, + { 0xdf7f, 0x0000 }, + { 0xdf80, 0x0000 }, + { 0xdf81, 0x0000 }, + { 0xdf82, 0x0000 }, + { 0xdf83, 0x0000 }, + { 0xdf84, 0x0000 }, + { 0xdf85, 0x0000 }, + { 0xdf86, 0x0000 }, + { 0xdf87, 0x0000 }, + { 0xdf88, 0x0000 }, + { 0xdf89, 0x0000 }, + { 0xdf8a, 0x0000 }, + { 0xdf8b, 0x0000 }, + { 0xdf8c, 0x0000 }, + { 0xdf8d, 0x0000 }, + { 0xdf8e, 0x0000 }, + { 0xdf8f, 0x0000 }, + { 0xdf90, 0x0000 }, + { 0xdf91, 0x0000 }, + { 0xdf92, 0x0000 }, + { 0xdf93, 0x0000 }, + { 0xdf94, 0x0000 }, + { 0xdf95, 0x0000 }, + { 0xdf96, 0x0000 }, + { 0xdf97, 0x0000 }, + { 0xdf98, 0x0000 }, + { 0xdf99, 0x0000 }, + { 0xdf9a, 0x0000 }, + { 0xdf9b, 0x0000 }, + { 0xdf9c, 0x0000 }, + { 0xdf9d, 0x0000 }, + { 0xdf9e, 0x0000 }, + { 0xdf9f, 0x0000 }, + { 0xdfa0, 0x0000 }, + { 0xdfa1, 0x0000 }, + { 0xdfa2, 0x0000 }, + { 0xdfa3, 0x0000 }, + { 0xdfa4, 0x0000 }, + { 0xdfa5, 0x0000 }, + { 0xdfa6, 0x0000 }, + { 0xdfa7, 0x0000 }, + { 0xdfa8, 0x0000 }, + { 0xdfa9, 0x0000 }, + { 0xdfaa, 0x0000 }, + { 0xdfab, 0x0000 }, + { 0xdfac, 0x0000 }, + { 0xdfad, 0x0000 }, + { 0xdfae, 0x0000 }, + { 0xdfaf, 0x0000 }, + { 0xdfb0, 0x0000 }, + { 0xdfb1, 0x0000 }, + { 0xdfb2, 0x0000 }, + { 0xdfb3, 0x0000 }, + { 0xdfb4, 0x0000 }, + { 0xdfb5, 0x0000 }, + { 0xdfb6, 0x0000 }, + { 0xdfb7, 0x0000 }, + { 0xdfb8, 0x0000 }, + { 0xdfb9, 0x0000 }, + { 0xdfba, 0x0000 }, + { 0xdfbb, 0x0000 }, + { 0xdfbc, 0x0000 }, + { 0xdfbd, 0x0000 }, + { 0xdfbe, 0x0000 }, + { 0xdfbf, 0x0000 }, + { 0xdfc0, 0x0000 }, + { 0xdfc1, 0x0000 }, + { 0xdfc2, 0x0000 }, + { 0xdfc3, 0x0000 }, + { 0xdfc4, 0x0000 }, + { 0xdfc5, 0x0000 }, + { 0xdfc6, 0x0000 }, + { 0xdfc7, 0x0000 }, + { 0xdfc8, 0x0000 }, + { 0xdfc9, 0x0000 }, + { 0xdfca, 0x0000 }, + { 0xdfcb, 0x0000 }, + { 0xdfcc, 0x0000 }, + { 0xdfcd, 0x0000 }, + { 0xdfce, 0x0000 }, + { 0xdfcf, 0x0000 }, + { 0xdfd0, 0x0000 }, + { 0xdfd1, 0x0000 }, + { 0xdfd2, 0x0000 }, + { 0xdfd3, 0x0000 }, + { 0xdfd4, 0x0000 }, + { 0xdfd5, 0x0000 }, + { 0xdfd6, 0x0000 }, + { 0xdfd7, 0x0000 }, + { 0xdfd8, 0x0000 }, + { 0xdfd9, 0x0000 }, + { 0xdfda, 0x0000 }, + { 0xdfdb, 0x0000 }, + { 0xdfdc, 0x0000 }, + { 0xdfdd, 0x0000 }, + { 0xdfde, 0x0000 }, + { 0xdfdf, 0x0000 }, + { 0xdfe0, 0x0000 }, + { 0xdfe1, 0x0000 }, + { 0xdfe2, 0x0000 }, + { 0xdfe3, 0x0000 }, + { 0xdfe4, 0x0000 }, + { 0xdfe5, 0x0000 }, + { 0xdfe6, 0x0000 }, + { 0xdfe7, 0x0000 }, + { 0xdfe8, 0x0000 }, + { 0xdfe9, 0x0000 }, + { 0xdfea, 0x0000 }, + { 0xdfeb, 0x0000 }, + { 0xdfec, 0x0000 }, + { 0xdfed, 0x0000 }, + { 0xdfee, 0x0000 }, + { 0xdfef, 0x0000 }, + { 0xdff0, 0x0000 }, + { 0xdff1, 0x0000 }, + { 0xdff2, 0x0000 }, + { 0xdff3, 0x0000 }, + { 0xdff4, 0x0000 }, + { 0xdff5, 0x0000 }, + { 0xdff6, 0x0000 }, + { 0xdff7, 0x0000 }, + { 0xdff8, 0x0000 }, + { 0xdff9, 0x0000 }, + { 0xdffa, 0x0000 }, + { 0xdffb, 0x0000 }, + { 0xdffc, 0x0000 }, + { 0xdffd, 0x0000 }, + { 0xdffe, 0x0000 }, + { 0xdfff, 0x0000 }, + { 0xe000, 0x0000 }, + { 0xe001, 0x0000 }, + { 0xe002, 0x0000 }, + { 0xe003, 0x0000 }, + { 0xe004, 0x0000 }, + { 0xe005, 0x0000 }, + { 0xe006, 0x0000 }, + { 0xe007, 0x0000 }, + { 0xe008, 0x0000 }, + { 0xe009, 0x0000 }, + { 0xe00a, 0x0000 }, + { 0xe00b, 0x0000 }, + { 0xe00c, 0x0000 }, + { 0xe00d, 0x0000 }, + { 0xe00e, 0x0000 }, + { 0xe00f, 0x0000 }, + { 0xe010, 0x0000 }, + { 0xe011, 0x0000 }, + { 0xe012, 0x0000 }, + { 0xe013, 0x0000 }, + { 0xe014, 0x0000 }, + { 0xe015, 0x0000 }, + { 0xe016, 0x0000 }, + { 0xe017, 0x0000 }, + { 0xe018, 0x0000 }, + { 0xe019, 0x0000 }, + { 0xe01a, 0x0000 }, + { 0xe01b, 0x0000 }, + { 0xe01c, 0x0000 }, + { 0xe01d, 0x0000 }, + { 0xe01e, 0x0000 }, + { 0xe01f, 0x0000 }, + { 0xe020, 0x0000 }, + { 0xe021, 0x0000 }, + { 0xe022, 0x0000 }, + { 0xe023, 0x0000 }, + { 0xe024, 0x0000 }, + { 0xe025, 0x0000 }, + { 0xe026, 0x0000 }, + { 0xe027, 0x0000 }, + { 0xe028, 0x0000 }, + { 0xe029, 0x0000 }, + { 0xe02a, 0x0000 }, + { 0xe02b, 0x0000 }, + { 0xe02c, 0x0000 }, + { 0xe02d, 0x0000 }, + { 0xe02e, 0x0000 }, + { 0xe02f, 0x0000 }, + { 0xe030, 0x0000 }, + { 0xe031, 0x0000 }, + { 0xe032, 0x0000 }, + { 0xe033, 0x0000 }, + { 0xe034, 0x0000 }, + { 0xe035, 0x0000 }, + { 0xe036, 0x0000 }, + { 0xe037, 0x0000 }, + { 0xe038, 0x0000 }, + { 0xe039, 0x0000 }, + { 0xe03a, 0x0000 }, + { 0xe03b, 0x0000 }, + { 0xe03c, 0x0000 }, + { 0xe03d, 0x0000 }, + { 0xe03e, 0x0000 }, + { 0xe03f, 0x0000 }, + { 0xe040, 0x0000 }, + { 0xe041, 0x0000 }, + { 0xe042, 0x0000 }, + { 0xe043, 0x0000 }, + { 0xe044, 0x0000 }, + { 0xe045, 0x0000 }, + { 0xe046, 0x0000 }, + { 0xe047, 0x0000 }, + { 0xe048, 0x0000 }, + { 0xe049, 0x0000 }, + { 0xe04a, 0x0000 }, + { 0xe04b, 0x0000 }, + { 0xe04c, 0x0000 }, + { 0xe04d, 0x0000 }, + { 0xe04e, 0x0000 }, + { 0xe04f, 0x0000 }, + { 0xe050, 0x0000 }, + { 0xe051, 0x0000 }, + { 0xe052, 0x0000 }, + { 0xe053, 0x0000 }, + { 0xe054, 0x0000 }, + { 0xe055, 0x0000 }, + { 0xe056, 0x0000 }, + { 0xe057, 0x0000 }, + { 0xe058, 0x0000 }, + { 0xe059, 0x0000 }, + { 0xe05a, 0x0000 }, + { 0xe05b, 0x0000 }, + { 0xe05c, 0x0000 }, + { 0xe05d, 0x0000 }, + { 0xe05e, 0x0000 }, + { 0xe05f, 0x0000 }, + { 0xe060, 0x0000 }, + { 0xe061, 0x0000 }, + { 0xe062, 0x0000 }, + { 0xe063, 0x0000 }, + { 0xe064, 0x0000 }, + { 0xe065, 0x0000 }, + { 0xe066, 0x0000 }, + { 0xe067, 0x0000 }, + { 0xe068, 0x0000 }, + { 0xe069, 0x0000 }, + { 0xe06a, 0x0000 }, + { 0xe06b, 0x0000 }, + { 0xe06c, 0x0000 }, + { 0xe06d, 0x0000 }, + { 0xe06e, 0x0000 }, + { 0xe06f, 0x0000 }, + { 0xe070, 0x0000 }, + { 0xe071, 0x0000 }, + { 0xe072, 0x0000 }, + { 0xe073, 0x0000 }, + { 0xe074, 0x0000 }, + { 0xe075, 0x0000 }, + { 0xe076, 0x0000 }, + { 0xe077, 0x0000 }, + { 0xe078, 0x0000 }, + { 0xe079, 0x0000 }, + { 0xe07a, 0x0000 }, + { 0xe07b, 0x0000 }, + { 0xe07c, 0x0000 }, + { 0xe07d, 0x0000 }, + { 0xe07e, 0x0000 }, + { 0xe07f, 0x0000 }, + { 0xe080, 0x0000 }, + { 0xe081, 0x0000 }, + { 0xe082, 0x0000 }, + { 0xe083, 0x0000 }, + { 0xe084, 0x0000 }, + { 0xe085, 0x0000 }, + { 0xe086, 0x0000 }, + { 0xe087, 0x0000 }, + { 0xe088, 0x0000 }, + { 0xe089, 0x0000 }, + { 0xe08a, 0x0000 }, + { 0xe08b, 0x0000 }, + { 0xe08c, 0x0000 }, + { 0xe08d, 0x0000 }, + { 0xe08e, 0x0000 }, + { 0xe08f, 0x0000 }, + { 0xe090, 0x0000 }, + { 0xe091, 0x0000 }, + { 0xe092, 0x0000 }, + { 0xe093, 0x0000 }, + { 0xe094, 0x0000 }, + { 0xe095, 0x0000 }, + { 0xe096, 0x0000 }, + { 0xe097, 0x0000 }, + { 0xe098, 0x0000 }, + { 0xe099, 0x0000 }, + { 0xe09a, 0x0000 }, + { 0xe09b, 0x0000 }, + { 0xe09c, 0x0000 }, + { 0xe09d, 0x0000 }, + { 0xe09e, 0x0000 }, + { 0xe09f, 0x0000 }, + { 0xe0a0, 0x0000 }, + { 0xe0a1, 0x0000 }, + { 0xe0a2, 0x0000 }, + { 0xe0a3, 0x0000 }, + { 0xe0a4, 0x0000 }, + { 0xe0a5, 0x0000 }, + { 0xe0a6, 0x0000 }, + { 0xe0a7, 0x0000 }, + { 0xe0a8, 0x0000 }, + { 0xe0a9, 0x0000 }, + { 0xe0aa, 0x0000 }, + { 0xe0ab, 0x0000 }, + { 0xe0ac, 0x0000 }, + { 0xe0ad, 0x0000 }, + { 0xe0ae, 0x0000 }, + { 0xe0af, 0x0000 }, + { 0xe0b0, 0x0000 }, + { 0xe0b1, 0x0000 }, + { 0xe0b2, 0x0000 }, + { 0xe0b3, 0x0000 }, + { 0xe0b4, 0x0000 }, + { 0xe0b5, 0x0000 }, + { 0xe0b6, 0x0000 }, + { 0xe0b7, 0x0000 }, + { 0xe0b8, 0x0000 }, + { 0xe0b9, 0x0000 }, + { 0xe0ba, 0x0000 }, + { 0xe0bb, 0x0000 }, + { 0xe0bc, 0x0000 }, + { 0xe0bd, 0x0000 }, + { 0xe0be, 0x0000 }, + { 0xe0bf, 0x0000 }, + { 0xe0c0, 0x0000 }, + { 0xe0c1, 0x0000 }, + { 0xe0c2, 0x0000 }, + { 0xe0c3, 0x0000 }, + { 0xe0c4, 0x0000 }, + { 0xe0c5, 0x0000 }, + { 0xe0c6, 0x0000 }, + { 0xe0c7, 0x0000 }, + { 0xe0c8, 0x0000 }, + { 0xe0c9, 0x0000 }, + { 0xe0ca, 0x0000 }, + { 0xe0cb, 0x0000 }, + { 0xe0cc, 0x0000 }, + { 0xe0cd, 0x0000 }, + { 0xe0ce, 0x0000 }, + { 0xe0cf, 0x0000 }, + { 0xe0d0, 0x0000 }, + { 0xe0d1, 0x0000 }, + { 0xe0d2, 0x0000 }, + { 0xe0d3, 0x0000 }, + { 0xe0d4, 0x0000 }, + { 0xe0d5, 0x0000 }, + { 0xe0d6, 0x0000 }, + { 0xe0d7, 0x0000 }, + { 0xe0d8, 0x0000 }, + { 0xe0d9, 0x0000 }, + { 0xe0da, 0x0000 }, + { 0xe0db, 0x0000 }, + { 0xe0dc, 0x0000 }, + { 0xe0dd, 0x0000 }, + { 0xe0de, 0x0000 }, + { 0xe0df, 0x0000 }, + { 0xe0e0, 0x0000 }, + { 0xe0e1, 0x0000 }, + { 0xe0e2, 0x0000 }, + { 0xe0e3, 0x0000 }, + { 0xe0e4, 0x0000 }, + { 0xe0e5, 0x0000 }, + { 0xe0e6, 0x0000 }, + { 0xe0e7, 0x0000 }, + { 0xe0e8, 0x0000 }, + { 0xe0e9, 0x0000 }, + { 0xe0ea, 0x0000 }, + { 0xe0eb, 0x0000 }, + { 0xe0ec, 0x0000 }, + { 0xe0ed, 0x0000 }, + { 0xe0ee, 0x0000 }, + { 0xe0ef, 0x0000 }, + { 0xe0f0, 0x0000 }, + { 0xe0f1, 0x0000 }, + { 0xe0f2, 0x0000 }, + { 0xe0f3, 0x0000 }, + { 0xe0f4, 0x0000 }, + { 0xe0f5, 0x0000 }, + { 0xe0f6, 0x0000 }, + { 0xe0f7, 0x0000 }, + { 0xe0f8, 0x0000 }, + { 0xe0f9, 0x0000 }, + { 0xe0fa, 0x0000 }, + { 0xe0fb, 0x0000 }, + { 0xe0fc, 0x0000 }, + { 0xe0fd, 0x0000 }, + { 0xe0fe, 0x0000 }, + { 0xe0ff, 0x0000 }, + { 0xe100, 0x0000 }, + { 0xe101, 0x0000 }, + { 0xe102, 0x0000 }, + { 0xe103, 0x0000 }, + { 0xe104, 0x0000 }, + { 0xe105, 0x0000 }, + { 0xe106, 0x0000 }, + { 0xe107, 0x0000 }, + { 0xe108, 0x0000 }, + { 0xe109, 0x0000 }, + { 0xe10a, 0x0000 }, + { 0xe10b, 0x0000 }, + { 0xe10c, 0x0000 }, + { 0xe10d, 0x0000 }, + { 0xe10e, 0x0000 }, + { 0xe10f, 0x0000 }, + { 0xe110, 0x0000 }, + { 0xe111, 0x0000 }, + { 0xe112, 0x0000 }, + { 0xe113, 0x0000 }, + { 0xe114, 0x0000 }, + { 0xe115, 0x0000 }, + { 0xe116, 0x0000 }, + { 0xe117, 0x0000 }, + { 0xe118, 0x0000 }, + { 0xe119, 0x0000 }, + { 0xe11a, 0x0000 }, + { 0xe11b, 0x0000 }, + { 0xe11c, 0x0000 }, + { 0xe11d, 0x0000 }, + { 0xe11e, 0x0000 }, + { 0xe11f, 0x0000 }, + { 0xe120, 0x0000 }, + { 0xe121, 0x0000 }, + { 0xe122, 0x0000 }, + { 0xe123, 0x0000 }, + { 0xe124, 0x0000 }, + { 0xe125, 0x0000 }, + { 0xe126, 0x0000 }, + { 0xe127, 0x0000 }, + { 0xe128, 0x0000 }, + { 0xe129, 0x0000 }, + { 0xe12a, 0x0000 }, + { 0xe12b, 0x0000 }, + { 0xe12c, 0x0000 }, + { 0xe12d, 0x0000 }, + { 0xe12e, 0x0000 }, + { 0xe12f, 0x0000 }, + { 0xe130, 0x0000 }, + { 0xe131, 0x0000 }, + { 0xe132, 0x0000 }, + { 0xe133, 0x0000 }, + { 0xe134, 0x0000 }, + { 0xe135, 0x0000 }, + { 0xe136, 0x0000 }, + { 0xe137, 0x0000 }, + { 0xe138, 0x0000 }, + { 0xe139, 0x0000 }, + { 0xe13a, 0x0000 }, + { 0xe13b, 0x0000 }, + { 0xe13c, 0x0000 }, + { 0xe13d, 0x0000 }, + { 0xe13e, 0x0000 }, + { 0xe13f, 0x0000 }, + { 0xe140, 0x0000 }, + { 0xe141, 0x0000 }, + { 0xe142, 0x0000 }, + { 0xe143, 0x0000 }, + { 0xe144, 0x0000 }, + { 0xe145, 0x0000 }, + { 0xe146, 0x0000 }, + { 0xe147, 0x0000 }, + { 0xe148, 0x0000 }, + { 0xe149, 0x0000 }, + { 0xe14a, 0x0000 }, + { 0xe14b, 0x0000 }, + { 0xe14c, 0x0000 }, + { 0xe14d, 0x0000 }, + { 0xe14e, 0x0000 }, + { 0xe14f, 0x0000 }, + { 0xe150, 0x0000 }, + { 0xe151, 0x0000 }, + { 0xe152, 0x0000 }, + { 0xe153, 0x0000 }, + { 0xe154, 0x0000 }, + { 0xe155, 0x0000 }, + { 0xe156, 0x0000 }, + { 0xe157, 0x0000 }, + { 0xe158, 0x0000 }, + { 0xe159, 0x0000 }, + { 0xe15a, 0x0000 }, + { 0xe15b, 0x0000 }, + { 0xe15c, 0x0000 }, + { 0xe15d, 0x0000 }, + { 0xe15e, 0x0000 }, + { 0xe15f, 0x0000 }, + { 0xe160, 0x0000 }, + { 0xe161, 0x0000 }, + { 0xe162, 0x0000 }, + { 0xe163, 0x0000 }, + { 0xe164, 0x0000 }, + { 0xe165, 0x0000 }, + { 0xe166, 0x0000 }, + { 0xe167, 0x0000 }, + { 0xe168, 0x0000 }, + { 0xe169, 0x0000 }, + { 0xe16a, 0x0000 }, + { 0xe16b, 0x0000 }, + { 0xe16c, 0x0000 }, + { 0xe16d, 0x0000 }, + { 0xe16e, 0x0000 }, + { 0xe16f, 0x0000 }, + { 0xe170, 0x0000 }, + { 0xe171, 0x0000 }, + { 0xe172, 0x0000 }, + { 0xe173, 0x0000 }, + { 0xe174, 0x0000 }, + { 0xe175, 0x0000 }, + { 0xe176, 0x0000 }, + { 0xe177, 0x0000 }, + { 0xe178, 0x0000 }, + { 0xe179, 0x0000 }, + { 0xe17a, 0x0000 }, + { 0xe17b, 0x0000 }, + { 0xe17c, 0x0000 }, + { 0xe17d, 0x0000 }, + { 0xe17e, 0x0000 }, + { 0xe17f, 0x0000 }, + { 0xe180, 0x0000 }, + { 0xe181, 0x0000 }, + { 0xe182, 0x0000 }, + { 0xe183, 0x0000 }, + { 0xe184, 0x0000 }, + { 0xe185, 0x0000 }, + { 0xe186, 0x0000 }, + { 0xe187, 0x0000 }, + { 0xe188, 0x0000 }, + { 0xe189, 0x0000 }, + { 0xe18a, 0x0000 }, + { 0xe18b, 0x0000 }, + { 0xe18c, 0x0000 }, + { 0xe18d, 0x0000 }, + { 0xe18e, 0x0000 }, + { 0xe18f, 0x0000 }, + { 0xe190, 0x0000 }, + { 0xe191, 0x0000 }, + { 0xe192, 0x0000 }, + { 0xe193, 0x0000 }, + { 0xe194, 0x0000 }, + { 0xe195, 0x0000 }, + { 0xe196, 0x0000 }, + { 0xe197, 0x0000 }, + { 0xe198, 0x0000 }, + { 0xe199, 0x0000 }, + { 0xe19a, 0x0000 }, + { 0xe19b, 0x0000 }, + { 0xe19c, 0x0000 }, + { 0xe19d, 0x0000 }, + { 0xe19e, 0x0000 }, + { 0xe19f, 0x0000 }, + { 0xe1a0, 0x0000 }, + { 0xe1a1, 0x0000 }, + { 0xe1a2, 0x0000 }, + { 0xe1a3, 0x0000 }, + { 0xe1a4, 0x0000 }, + { 0xe1a5, 0x0000 }, + { 0xe1a6, 0x0000 }, + { 0xe1a7, 0x0000 }, + { 0xe1a8, 0x0000 }, + { 0xe1a9, 0x0000 }, + { 0xe1aa, 0x0000 }, + { 0xe1ab, 0x0000 }, + { 0xe1ac, 0x0000 }, + { 0xe1ad, 0x0000 }, + { 0xe1ae, 0x0000 }, + { 0xe1af, 0x0000 }, + { 0xe1b0, 0x0000 }, + { 0xe1b1, 0x0000 }, + { 0xe1b2, 0x0000 }, + { 0xe1b3, 0x0000 }, + { 0xe1b4, 0x0000 }, + { 0xe1b5, 0x0000 }, + { 0xe1b6, 0x0000 }, + { 0xe1b7, 0x0000 }, + { 0xe1b8, 0x0000 }, + { 0xe1b9, 0x0000 }, + { 0xe1ba, 0x0000 }, + { 0xe1bb, 0x0000 }, + { 0xe1bc, 0x0000 }, + { 0xe1bd, 0x0000 }, + { 0xe1be, 0x0000 }, + { 0xe1bf, 0x0000 }, + { 0xe1c0, 0x0000 }, + { 0xe1c1, 0x0000 }, + { 0xe1c2, 0x0000 }, + { 0xe1c3, 0x0000 }, + { 0xe1c4, 0x0000 }, + { 0xe1c5, 0x0000 }, + { 0xe1c6, 0x0000 }, + { 0xe1c7, 0x0000 }, + { 0xe1c8, 0x0000 }, + { 0xe1c9, 0x0000 }, + { 0xe1ca, 0x0000 }, + { 0xe1cb, 0x0000 }, + { 0xe1cc, 0x0000 }, + { 0xe1cd, 0x0000 }, + { 0xe1ce, 0x0000 }, + { 0xe1cf, 0x0000 }, + { 0xe1d0, 0x0000 }, + { 0xe1d1, 0x0000 }, + { 0xe1d2, 0x0000 }, + { 0xe1d3, 0x0000 }, + { 0xe1d4, 0x0000 }, + { 0xe1d5, 0x0000 }, + { 0xe1d6, 0x0000 }, + { 0xe1d7, 0x0000 }, + { 0xe1d8, 0x0000 }, + { 0xe1d9, 0x0000 }, + { 0xe1da, 0x0000 }, + { 0xe1db, 0x0000 }, + { 0xe1dc, 0x0000 }, + { 0xe1dd, 0x0000 }, + { 0xe1de, 0x0000 }, + { 0xe1df, 0x0000 }, + { 0xe1e0, 0x0000 }, + { 0xe1e1, 0x0000 }, + { 0xe1e2, 0x0000 }, + { 0xe1e3, 0x0000 }, + { 0xe1e4, 0x0000 }, + { 0xe1e5, 0x0000 }, + { 0xe1e6, 0x0000 }, + { 0xe1e7, 0x0000 }, + { 0xe1e8, 0x0000 }, + { 0xe1e9, 0x0000 }, + { 0xe1ea, 0x0000 }, + { 0xe1eb, 0x0000 }, + { 0xe1ec, 0x0000 }, + { 0xe1ed, 0x0000 }, + { 0xe1ee, 0x0000 }, + { 0xe1ef, 0x0000 }, + { 0xe1f0, 0x0000 }, + { 0xe1f1, 0x0000 }, + { 0xe1f2, 0x0000 }, + { 0xe1f3, 0x0000 }, + { 0xe1f4, 0x0000 }, + { 0xe1f5, 0x0000 }, + { 0xe1f6, 0x0000 }, + { 0xe1f7, 0x0000 }, + { 0xe1f8, 0x0000 }, + { 0xe1f9, 0x0000 }, + { 0xe1fa, 0x0000 }, + { 0xe1fb, 0x0000 }, + { 0xe1fc, 0x0000 }, + { 0xe1fd, 0x0000 }, + { 0xe1fe, 0x0000 }, + { 0xe1ff, 0x0000 }, + { 0xe200, 0x0000 }, + { 0xe201, 0x0000 }, + { 0xe202, 0x0000 }, + { 0xe203, 0x0000 }, + { 0xe204, 0x0000 }, + { 0xe205, 0x0000 }, + { 0xe206, 0x0000 }, + { 0xe207, 0x0000 }, + { 0xe208, 0x0000 }, + { 0xe209, 0x0000 }, + { 0xe20a, 0x0000 }, + { 0xe20b, 0x0000 }, + { 0xe20c, 0x0000 }, + { 0xe20d, 0x0000 }, + { 0xe20e, 0x0000 }, + { 0xe20f, 0x0000 }, + { 0xe210, 0x0000 }, + { 0xe211, 0x0000 }, + { 0xe212, 0x0000 }, + { 0xe213, 0x0000 }, + { 0xe214, 0x0000 }, + { 0xe215, 0x0000 }, + { 0xe216, 0x0000 }, + { 0xe217, 0x0000 }, + { 0xe218, 0x0000 }, + { 0xe219, 0x0000 }, + { 0xe21a, 0x0000 }, + { 0xe21b, 0x0000 }, + { 0xe21c, 0x0000 }, + { 0xe21d, 0x0000 }, + { 0xe21e, 0x0000 }, + { 0xe21f, 0x0000 }, + { 0xe220, 0x0000 }, + { 0xe221, 0x0000 }, + { 0xe222, 0x0000 }, + { 0xe223, 0x0000 }, + { 0xe224, 0x0000 }, + { 0xe225, 0x0000 }, + { 0xe226, 0x0000 }, + { 0xe227, 0x0000 }, + { 0xe228, 0x0000 }, + { 0xe229, 0x0000 }, + { 0xe22a, 0x0000 }, + { 0xe22b, 0x0000 }, + { 0xe22c, 0x0000 }, + { 0xe22d, 0x0000 }, + { 0xe22e, 0x0000 }, + { 0xe22f, 0x0000 }, + { 0xe230, 0x0000 }, + { 0xe231, 0x0000 }, + { 0xe232, 0x0000 }, + { 0xe233, 0x0000 }, + { 0xe234, 0x0000 }, + { 0xe235, 0x0000 }, + { 0xe236, 0x0000 }, + { 0xe237, 0x0000 }, + { 0xe238, 0x0000 }, + { 0xe239, 0x0000 }, + { 0xe23a, 0x0000 }, + { 0xe23b, 0x0000 }, + { 0xe23c, 0x0000 }, + { 0xe23d, 0x0000 }, + { 0xe23e, 0x0000 }, + { 0xe23f, 0x0000 }, + { 0xe240, 0x0000 }, + { 0xe241, 0x0000 }, + { 0xe242, 0x0000 }, + { 0xe243, 0x0000 }, + { 0xe244, 0x0000 }, + { 0xe245, 0x0000 }, + { 0xe246, 0x0000 }, + { 0xe247, 0x0000 }, + { 0xe248, 0x0000 }, + { 0xe249, 0x0000 }, + { 0xe24a, 0x0000 }, + { 0xe24b, 0x0000 }, + { 0xe24c, 0x0000 }, + { 0xe24d, 0x0000 }, + { 0xe24e, 0x0000 }, + { 0xe24f, 0x0000 }, + { 0xe250, 0x0000 }, + { 0xe251, 0x0000 }, + { 0xe252, 0x0000 }, + { 0xe253, 0x0000 }, + { 0xe254, 0x0000 }, + { 0xe255, 0x0000 }, + { 0xe256, 0x0000 }, + { 0xe257, 0x0000 }, + { 0xe258, 0x0000 }, + { 0xe259, 0x0000 }, + { 0xe25a, 0x0000 }, + { 0xe25b, 0x0000 }, + { 0xe25c, 0x0000 }, + { 0xe25d, 0x0000 }, + { 0xe25e, 0x0000 }, + { 0xe25f, 0x0000 }, + { 0xe260, 0x0000 }, + { 0xe261, 0x0000 }, + { 0xe262, 0x0000 }, + { 0xe263, 0x0000 }, + { 0xe264, 0x0000 }, + { 0xe265, 0x0000 }, + { 0xe266, 0x0000 }, + { 0xe267, 0x0000 }, + { 0xe268, 0x0000 }, + { 0xe269, 0x0000 }, + { 0xe26a, 0x0000 }, + { 0xe26b, 0x0000 }, + { 0xe26c, 0x0000 }, + { 0xe26d, 0x0000 }, + { 0xe26e, 0x0000 }, + { 0xe26f, 0x0000 }, + { 0xe270, 0x0000 }, + { 0xe271, 0x0000 }, + { 0xe272, 0x0000 }, + { 0xe273, 0x0000 }, + { 0xe274, 0x0000 }, + { 0xe275, 0x0000 }, + { 0xe276, 0x0000 }, + { 0xe277, 0x0000 }, + { 0xe278, 0x0000 }, + { 0xe279, 0x0000 }, + { 0xe27a, 0x0000 }, + { 0xe27b, 0x0000 }, + { 0xe27c, 0x0000 }, + { 0xe27d, 0x0000 }, + { 0xe27e, 0x0000 }, + { 0xe27f, 0x0000 }, + { 0xe280, 0x0000 }, + { 0xe281, 0x0000 }, + { 0xe282, 0x0000 }, + { 0xe283, 0x0000 }, + { 0xe284, 0x0000 }, + { 0xe285, 0x0000 }, + { 0xe286, 0x0000 }, + { 0xe287, 0x0000 }, + { 0xe288, 0x0000 }, + { 0xe289, 0x0000 }, + { 0xe28a, 0x0000 }, + { 0xe28b, 0x0000 }, + { 0xe28c, 0x0000 }, + { 0xe28d, 0x0000 }, + { 0xe28e, 0x0000 }, + { 0xe28f, 0x0000 }, + { 0xe290, 0x0000 }, + { 0xe291, 0x0000 }, + { 0xe292, 0x0000 }, + { 0xe293, 0x0000 }, + { 0xe294, 0x0000 }, + { 0xe295, 0x0000 }, + { 0xe296, 0x0000 }, + { 0xe297, 0x0000 }, + { 0xe298, 0x0000 }, + { 0xe299, 0x0000 }, + { 0xe29a, 0x0000 }, + { 0xe29b, 0x0000 }, + { 0xe29c, 0x0000 }, + { 0xe29d, 0x0000 }, + { 0xe29e, 0x0000 }, + { 0xe29f, 0x0000 }, + { 0xe2a0, 0x0000 }, + { 0xe2a1, 0x0000 }, + { 0xe2a2, 0x0000 }, + { 0xe2a3, 0x0000 }, + { 0xe2a4, 0x0000 }, + { 0xe2a5, 0x0000 }, + { 0xe2a6, 0x0000 }, + { 0xe2a7, 0x0000 }, + { 0xe2a8, 0x0000 }, + { 0xe2a9, 0x0000 }, + { 0xe2aa, 0x0000 }, + { 0xe2ab, 0x0000 }, + { 0xe2ac, 0x0000 }, + { 0xe2ad, 0x0000 }, + { 0xe2ae, 0x0000 }, + { 0xe2af, 0x0000 }, + { 0xe2b0, 0x0000 }, + { 0xe2b1, 0x0000 }, + { 0xe2b2, 0x0000 }, + { 0xe2b3, 0x0000 }, + { 0xe2b4, 0x0000 }, + { 0xe2b5, 0x0000 }, + { 0xe2b6, 0x0000 }, + { 0xe2b7, 0x0000 }, + { 0xe2b8, 0x0000 }, + { 0xe2b9, 0x0000 }, + { 0xe2ba, 0x0000 }, + { 0xe2bb, 0x0000 }, + { 0xe2bc, 0x0000 }, + { 0xe2bd, 0x0000 }, + { 0xe2be, 0x0000 }, + { 0xe2bf, 0x0000 }, + { 0xe2c0, 0x0000 }, + { 0xe2c1, 0x0000 }, + { 0xe2c2, 0x0000 }, + { 0xe2c3, 0x0000 }, + { 0xe2c4, 0x0000 }, + { 0xe2c5, 0x0000 }, + { 0xe2c6, 0x0000 }, + { 0xe2c7, 0x0000 }, + { 0xe2c8, 0x0000 }, + { 0xe2c9, 0x0000 }, + { 0xe2ca, 0x0000 }, + { 0xe2cb, 0x0000 }, + { 0xe2cc, 0x0000 }, + { 0xe2cd, 0x0000 }, + { 0xe2ce, 0x0000 }, + { 0xe2cf, 0x0000 }, + { 0xe2d0, 0x0000 }, + { 0xe2d1, 0x0000 }, + { 0xe2d2, 0x0000 }, + { 0xe2d3, 0x0000 }, + { 0xe2d4, 0x0000 }, + { 0xe2d5, 0x0000 }, + { 0xe2d6, 0x0000 }, + { 0xe2d7, 0x0000 }, + { 0xe2d8, 0x0000 }, + { 0xe2d9, 0x0000 }, + { 0xe2da, 0x0000 }, + { 0xe2db, 0x0000 }, + { 0xe2dc, 0x0000 }, + { 0xe2dd, 0x0000 }, + { 0xe2de, 0x0000 }, + { 0xe2df, 0x0000 }, + { 0xe2e0, 0x0000 }, + { 0xe2e1, 0x0000 }, + { 0xe2e2, 0x0000 }, + { 0xe2e3, 0x0000 }, + { 0xe2e4, 0x0000 }, + { 0xe2e5, 0x0000 }, + { 0xe2e6, 0x0000 }, + { 0xe2e7, 0x0000 }, + { 0xe2e8, 0x0000 }, + { 0xe2e9, 0x0000 }, + { 0xe2ea, 0x0000 }, + { 0xe2eb, 0x0000 }, + { 0xe2ec, 0x0000 }, + { 0xe2ed, 0x0000 }, + { 0xe2ee, 0x0000 }, + { 0xe2ef, 0x0000 }, + { 0xe2f0, 0x0000 }, + { 0xe2f1, 0x0000 }, + { 0xe2f2, 0x0000 }, + { 0xe2f3, 0x0000 }, + { 0xe2f4, 0x0000 }, + { 0xe2f5, 0x0000 }, + { 0xe2f6, 0x0000 }, + { 0xe2f7, 0x0000 }, + { 0xe2f8, 0x0000 }, + { 0xe2f9, 0x0000 }, + { 0xe2fa, 0x0000 }, + { 0xe2fb, 0x0000 }, + { 0xe2fc, 0x0000 }, + { 0xe2fd, 0x0000 }, + { 0xe2fe, 0x0000 }, + { 0xe2ff, 0x0000 }, + { 0xe300, 0x0000 }, + { 0xe301, 0x0000 }, + { 0xe302, 0x0000 }, + { 0xe303, 0x0000 }, + { 0xe304, 0x0000 }, + { 0xe305, 0x0000 }, + { 0xe306, 0x0000 }, + { 0xe307, 0x0000 }, + { 0xe308, 0x0000 }, + { 0xe309, 0x0000 }, + { 0xe30a, 0x0000 }, + { 0xe30b, 0x0000 }, + { 0xe30c, 0x0000 }, + { 0xe30d, 0x0000 }, + { 0xe30e, 0x0000 }, + { 0xe30f, 0x0000 }, + { 0xe310, 0x0000 }, + { 0xe311, 0x0000 }, + { 0xe312, 0x0000 }, + { 0xe313, 0x0000 }, + { 0xe314, 0x0000 }, + { 0xe315, 0x0000 }, + { 0xe316, 0x0000 }, + { 0xe317, 0x0000 }, + { 0xe318, 0x0000 }, + { 0xe319, 0x0000 }, + { 0xe31a, 0x0000 }, + { 0xe31b, 0x0000 }, + { 0xe31c, 0x0000 }, + { 0xe31d, 0x0000 }, + { 0xe31e, 0x0000 }, + { 0xe31f, 0x0000 }, + { 0xe320, 0x0000 }, + { 0xe321, 0x0000 }, + { 0xe322, 0x0000 }, + { 0xe323, 0x0000 }, + { 0xe324, 0x0000 }, + { 0xe325, 0x0000 }, + { 0xe326, 0x0000 }, + { 0xe327, 0x0000 }, + { 0xe328, 0x0000 }, + { 0xe329, 0x0000 }, + { 0xe32a, 0x0000 }, + { 0xe32b, 0x0000 }, + { 0xe32c, 0x0000 }, + { 0xe32d, 0x0000 }, + { 0xe32e, 0x0000 }, + { 0xe32f, 0x0000 }, + { 0xe330, 0x0000 }, + { 0xe331, 0x0000 }, + { 0xe332, 0x0000 }, + { 0xe333, 0x0000 }, + { 0xe334, 0x0000 }, + { 0xe335, 0x0000 }, + { 0xe336, 0x0000 }, + { 0xe337, 0x0000 }, + { 0xe338, 0x0000 }, + { 0xe339, 0x0000 }, + { 0xe33a, 0x0000 }, + { 0xe33b, 0x0000 }, + { 0xe33c, 0x0000 }, + { 0xe33d, 0x0000 }, + { 0xe33e, 0x0000 }, + { 0xe33f, 0x0000 }, + { 0xe340, 0x0000 }, + { 0xe341, 0x0000 }, + { 0xe342, 0x0000 }, + { 0xe343, 0x0000 }, + { 0xe344, 0x0000 }, + { 0xe345, 0x0000 }, + { 0xe346, 0x0000 }, + { 0xe347, 0x0000 }, + { 0xe348, 0x0000 }, + { 0xe349, 0x0000 }, + { 0xe34a, 0x0000 }, + { 0xe34b, 0x0000 }, + { 0xe34c, 0x0000 }, + { 0xe34d, 0x0000 }, + { 0xe34e, 0x0000 }, + { 0xe34f, 0x0000 }, + { 0xe350, 0x0000 }, + { 0xe351, 0x0000 }, + { 0xe352, 0x0000 }, + { 0xe353, 0x0000 }, + { 0xe354, 0x0000 }, + { 0xe355, 0x0000 }, + { 0xe356, 0x0000 }, + { 0xe357, 0x0000 }, + { 0xe358, 0x0000 }, + { 0xe359, 0x0000 }, + { 0xe35a, 0x0000 }, + { 0xe35b, 0x0000 }, + { 0xe35c, 0x0000 }, + { 0xe35d, 0x0000 }, + { 0xe35e, 0x0000 }, + { 0xe35f, 0x0000 }, + { 0xe360, 0x0000 }, + { 0xe361, 0x0000 }, + { 0xe362, 0x0000 }, + { 0xe363, 0x0000 }, + { 0xe364, 0x0000 }, + { 0xe365, 0x0000 }, + { 0xe366, 0x0000 }, + { 0xe367, 0x0000 }, + { 0xe368, 0x0000 }, + { 0xe369, 0x0000 }, + { 0xe36a, 0x0000 }, + { 0xe36b, 0x0000 }, + { 0xe36c, 0x0000 }, + { 0xe36d, 0x0000 }, + { 0xe36e, 0x0000 }, + { 0xe36f, 0x0000 }, + { 0xe370, 0x0000 }, + { 0xe371, 0x0000 }, + { 0xe372, 0x0000 }, + { 0xe373, 0x0000 }, + { 0xe374, 0x0000 }, + { 0xe375, 0x0000 }, + { 0xe376, 0x0000 }, + { 0xe377, 0x0000 }, + { 0xe378, 0x0000 }, + { 0xe379, 0x0000 }, + { 0xe37a, 0x0000 }, + { 0xe37b, 0x0000 }, + { 0xe37c, 0x0000 }, + { 0xe37d, 0x0000 }, + { 0xe37e, 0x0000 }, + { 0xe37f, 0x0000 }, + { 0xe380, 0x0000 }, + { 0xe381, 0x0000 }, + { 0xe382, 0x0000 }, + { 0xe383, 0x0000 }, + { 0xe384, 0x0000 }, + { 0xe385, 0x0000 }, + { 0xe386, 0x0000 }, + { 0xe387, 0x0000 }, + { 0xe388, 0x0000 }, + { 0xe389, 0x0000 }, + { 0xe38a, 0x0000 }, + { 0xe38b, 0x0000 }, + { 0xe38c, 0x0000 }, + { 0xe38d, 0x0000 }, + { 0xe38e, 0x0000 }, + { 0xe38f, 0x0000 }, + { 0xe390, 0x0000 }, + { 0xe391, 0x0000 }, + { 0xe392, 0x0000 }, + { 0xe393, 0x0000 }, + { 0xe394, 0x0000 }, + { 0xe395, 0x0000 }, + { 0xe396, 0x0000 }, + { 0xe397, 0x0000 }, + { 0xe398, 0x0000 }, + { 0xe399, 0x0000 }, + { 0xe39a, 0x0000 }, + { 0xe39b, 0x0000 }, + { 0xe39c, 0x0000 }, + { 0xe39d, 0x0000 }, + { 0xe39e, 0x0000 }, + { 0xe39f, 0x0000 }, + { 0xe3a0, 0x0000 }, + { 0xe3a1, 0x0000 }, + { 0xe3a2, 0x0000 }, + { 0xe3a3, 0x0000 }, + { 0xe3a4, 0x0000 }, + { 0xe3a5, 0x0000 }, + { 0xe3a6, 0x0000 }, + { 0xe3a7, 0x0000 }, + { 0xe3a8, 0x0000 }, + { 0xe3a9, 0x0000 }, + { 0xe3aa, 0x0000 }, + { 0xe3ab, 0x0000 }, + { 0xe3ac, 0x0000 }, + { 0xe3ad, 0x0000 }, + { 0xe3ae, 0x0000 }, + { 0xe3af, 0x0000 }, + { 0xe3b0, 0x0000 }, + { 0xe3b1, 0x0000 }, + { 0xe3b2, 0x0000 }, + { 0xe3b3, 0x0000 }, + { 0xe3b4, 0x0000 }, + { 0xe3b5, 0x0000 }, + { 0xe3b6, 0x0000 }, + { 0xe3b7, 0x0000 }, + { 0xe3b8, 0x0000 }, + { 0xe3b9, 0x0000 }, + { 0xe3ba, 0x0000 }, + { 0xe3bb, 0x0000 }, + { 0xe3bc, 0x0000 }, + { 0xe3bd, 0x0000 }, + { 0xe3be, 0x0000 }, + { 0xe3bf, 0x0000 }, + { 0xe3c0, 0x0000 }, + { 0xe3c1, 0x0000 }, + { 0xe3c2, 0x0000 }, + { 0xe3c3, 0x0000 }, + { 0xe3c4, 0x0000 }, + { 0xe3c5, 0x0000 }, + { 0xe3c6, 0x0000 }, + { 0xe3c7, 0x0000 }, + { 0xe3c8, 0x0000 }, + { 0xe3c9, 0x0000 }, + { 0xe3ca, 0x0000 }, + { 0xe3cb, 0x0000 }, + { 0xe3cc, 0x0000 }, + { 0xe3cd, 0x0000 }, + { 0xe3ce, 0x0000 }, + { 0xe3cf, 0x0000 }, + { 0xe3d0, 0x0000 }, + { 0xe3d1, 0x0000 }, + { 0xe3d2, 0x0000 }, + { 0xe3d3, 0x0000 }, + { 0xe3d4, 0x0000 }, + { 0xe3d5, 0x0000 }, + { 0xe3d6, 0x0000 }, + { 0xe3d7, 0x0000 }, + { 0xe3d8, 0x0000 }, + { 0xe3d9, 0x0000 }, + { 0xe3da, 0x0000 }, + { 0xe3db, 0x0000 }, + { 0xe3dc, 0x0000 }, + { 0xe3dd, 0x0000 }, + { 0xe3de, 0x0000 }, + { 0xe3df, 0x0000 }, + { 0xe3e0, 0x0000 }, + { 0xe3e1, 0x0000 }, + { 0xe3e2, 0x0000 }, + { 0xe3e3, 0x0000 }, + { 0xe3e4, 0x0000 }, + { 0xe3e5, 0x0000 }, + { 0xe3e6, 0x0000 }, + { 0xe3e7, 0x0000 }, + { 0xe3e8, 0x0000 }, + { 0xe3e9, 0x0000 }, + { 0xe3ea, 0x0000 }, + { 0xe3eb, 0x0000 }, + { 0xe3ec, 0x0000 }, + { 0xe3ed, 0x0000 }, + { 0xe3ee, 0x0000 }, + { 0xe3ef, 0x0000 }, + { 0xe3f0, 0x0000 }, + { 0xe3f1, 0x0000 }, + { 0xe3f2, 0x0000 }, + { 0xe3f3, 0x0000 }, + { 0xe3f4, 0x0000 }, + { 0xe3f5, 0x0000 }, + { 0xe3f6, 0x0000 }, + { 0xe3f7, 0x0000 }, + { 0xe3f8, 0x0000 }, + { 0xe3f9, 0x0000 }, + { 0xe3fa, 0x0000 }, + { 0xe3fb, 0x0000 }, + { 0xe3fc, 0x0000 }, + { 0xe3fd, 0x0000 }, + { 0xe3fe, 0x0000 }, + { 0xe3ff, 0x0000 }, + { 0xe400, 0x0000 }, + { 0xe401, 0x0000 }, + { 0xe402, 0x0000 }, + { 0xe403, 0x0000 }, + { 0xe404, 0x0000 }, + { 0xe405, 0x0000 }, + { 0xe406, 0x0000 }, + { 0xe407, 0x0000 }, + { 0xe408, 0x0000 }, + { 0xe409, 0x0000 }, + { 0xe40a, 0x0000 }, + { 0xe40b, 0x0000 }, + { 0xe40c, 0x0000 }, + { 0xe40d, 0x0000 }, + { 0xe40e, 0x0000 }, + { 0xe40f, 0x0000 }, + { 0xe410, 0x0000 }, + { 0xe411, 0x0000 }, + { 0xe412, 0x0000 }, + { 0xe413, 0x0000 }, + { 0xe414, 0x0000 }, + { 0xe415, 0x0000 }, + { 0xe416, 0x0000 }, + { 0xe417, 0x0000 }, + { 0xe418, 0x0000 }, + { 0xe419, 0x0000 }, + { 0xe41a, 0x0000 }, + { 0xe41b, 0x0000 }, + { 0xe41c, 0x0000 }, + { 0xe41d, 0x0000 }, + { 0xe41e, 0x0000 }, + { 0xe41f, 0x0000 }, + { 0xe420, 0x0000 }, + { 0xe421, 0x0000 }, + { 0xe422, 0x0000 }, + { 0xe423, 0x0000 }, + { 0xe424, 0x0000 }, + { 0xe425, 0x0000 }, + { 0xe426, 0x0000 }, + { 0xe427, 0x0000 }, + { 0xe428, 0x0000 }, + { 0xe429, 0x0000 }, + { 0xe42a, 0x0000 }, + { 0xe42b, 0x0000 }, + { 0xe42c, 0x0000 }, + { 0xe42d, 0x0000 }, + { 0xe42e, 0x0000 }, + { 0xe42f, 0x0000 }, + { 0xe430, 0x0000 }, + { 0xe431, 0x0000 }, + { 0xe432, 0x0000 }, + { 0xe433, 0x0000 }, + { 0xe434, 0x0000 }, + { 0xe435, 0x0000 }, + { 0xe436, 0x0000 }, + { 0xe437, 0x0000 }, + { 0xe438, 0x0000 }, + { 0xe439, 0x0000 }, + { 0xe43a, 0x0000 }, + { 0xe43b, 0x0000 }, + { 0xe43c, 0x0000 }, + { 0xe43d, 0x0000 }, + { 0xe43e, 0x0000 }, + { 0xe43f, 0x0000 }, + { 0xe440, 0x0000 }, + { 0xe441, 0x0000 }, + { 0xe442, 0x0000 }, + { 0xe443, 0x0000 }, + { 0xe444, 0x0000 }, + { 0xe445, 0x0000 }, + { 0xe446, 0x0000 }, + { 0xe447, 0x0000 }, + { 0xe448, 0x0000 }, + { 0xe449, 0x0000 }, + { 0xe44a, 0x0000 }, + { 0xe44b, 0x0000 }, + { 0xe44c, 0x0000 }, + { 0xe44d, 0x0000 }, + { 0xe44e, 0x0000 }, + { 0xe44f, 0x0000 }, + { 0xe450, 0x0000 }, + { 0xe451, 0x0000 }, + { 0xe452, 0x0000 }, + { 0xe453, 0x0000 }, + { 0xe454, 0x0000 }, + { 0xe455, 0x0000 }, + { 0xe456, 0x0000 }, + { 0xe457, 0x0000 }, + { 0xe458, 0x0000 }, + { 0xe459, 0x0000 }, + { 0xe45a, 0x0000 }, + { 0xe45b, 0x0000 }, + { 0xe45c, 0x0000 }, + { 0xe45d, 0x0000 }, + { 0xe45e, 0x0000 }, + { 0xe45f, 0x0000 }, + { 0xe460, 0x0000 }, + { 0xe461, 0x0000 }, + { 0xe462, 0x0000 }, + { 0xe463, 0x0000 }, + { 0xe464, 0x0000 }, + { 0xe465, 0x0000 }, + { 0xe466, 0x0000 }, + { 0xe467, 0x0000 }, + { 0xe468, 0x0000 }, + { 0xe469, 0x0000 }, + { 0xe46a, 0x0000 }, + { 0xe46b, 0x0000 }, + { 0xe46c, 0x0000 }, + { 0xe46d, 0x0000 }, + { 0xe46e, 0x0000 }, + { 0xe46f, 0x0000 }, + { 0xe470, 0x0000 }, + { 0xe471, 0x0000 }, + { 0xe472, 0x0000 }, + { 0xe473, 0x0000 }, + { 0xe474, 0x0000 }, + { 0xe475, 0x0000 }, + { 0xe476, 0x0000 }, + { 0xe477, 0x0000 }, + { 0xe478, 0x0000 }, + { 0xe479, 0x0000 }, + { 0xe47a, 0x0000 }, + { 0xe47b, 0x0000 }, + { 0xe47c, 0x0000 }, + { 0xe47d, 0x0000 }, + { 0xe47e, 0x0000 }, + { 0xe47f, 0x0000 }, + { 0xe480, 0x0000 }, + { 0xe481, 0x0000 }, + { 0xe482, 0x0000 }, + { 0xe483, 0x0000 }, + { 0xe484, 0x0000 }, + { 0xe485, 0x0000 }, + { 0xe486, 0x0000 }, + { 0xe487, 0x0000 }, + { 0xe488, 0x0000 }, + { 0xe489, 0x0000 }, + { 0xe48a, 0x0000 }, + { 0xe48b, 0x0000 }, + { 0xe48c, 0x0000 }, + { 0xe48d, 0x0000 }, + { 0xe48e, 0x0000 }, + { 0xe48f, 0x0000 }, + { 0xe490, 0x0000 }, + { 0xe491, 0x0000 }, + { 0xe492, 0x0000 }, + { 0xe493, 0x0000 }, + { 0xe494, 0x0000 }, + { 0xe495, 0x0000 }, + { 0xe496, 0x0000 }, + { 0xe497, 0x0000 }, + { 0xe498, 0x0000 }, + { 0xe499, 0x0000 }, + { 0xe49a, 0x0000 }, + { 0xe49b, 0x0000 }, + { 0xe49c, 0x0000 }, + { 0xe49d, 0x0000 }, + { 0xe49e, 0x0000 }, + { 0xe49f, 0x0000 }, + { 0xe4a0, 0x0000 }, + { 0xe4a1, 0x0000 }, + { 0xe4a2, 0x0000 }, + { 0xe4a3, 0x0000 }, + { 0xe4a4, 0x0000 }, + { 0xe4a5, 0x0000 }, + { 0xe4a6, 0x0000 }, + { 0xe4a7, 0x0000 }, + { 0xe4a8, 0x0000 }, + { 0xe4a9, 0x0000 }, + { 0xe4aa, 0x0000 }, + { 0xe4ab, 0x0000 }, + { 0xe4ac, 0x0000 }, + { 0xe4ad, 0x0000 }, + { 0xe4ae, 0x0000 }, + { 0xe4af, 0x0000 }, + { 0xe4b0, 0x0000 }, + { 0xe4b1, 0x0000 }, + { 0xe4b2, 0x0000 }, + { 0xe4b3, 0x0000 }, + { 0xe4b4, 0x0000 }, + { 0xe4b5, 0x0000 }, + { 0xe4b6, 0x0000 }, + { 0xe4b7, 0x0000 }, + { 0xe4b8, 0x0000 }, + { 0xe4b9, 0x0000 }, + { 0xe4ba, 0x0000 }, + { 0xe4bb, 0x0000 }, + { 0xe4bc, 0x0000 }, + { 0xe4bd, 0x0000 }, + { 0xe4be, 0x0000 }, + { 0xe4bf, 0x0000 }, + { 0xe4c0, 0x0000 }, + { 0xe4c1, 0x0000 }, + { 0xe4c2, 0x0000 }, + { 0xe4c3, 0x0000 }, + { 0xe4c4, 0x0000 }, + { 0xe4c5, 0x0000 }, + { 0xe4c6, 0x0000 }, + { 0xe4c7, 0x0000 }, + { 0xe4c8, 0x0000 }, + { 0xe4c9, 0x0000 }, + { 0xe4ca, 0x0000 }, + { 0xe4cb, 0x0000 }, + { 0xe4cc, 0x0000 }, + { 0xe4cd, 0x0000 }, + { 0xe4ce, 0x0000 }, + { 0xe4cf, 0x0000 }, + { 0xe4d0, 0x0000 }, + { 0xe4d1, 0x0000 }, + { 0xe4d2, 0x0000 }, + { 0xe4d3, 0x0000 }, + { 0xe4d4, 0x0000 }, + { 0xe4d5, 0x0000 }, + { 0xe4d6, 0x0000 }, + { 0xe4d7, 0x0000 }, + { 0xe4d8, 0x0000 }, + { 0xe4d9, 0x0000 }, + { 0xe4da, 0x0000 }, + { 0xe4db, 0x0000 }, + { 0xe4dc, 0x0000 }, + { 0xe4dd, 0x0000 }, + { 0xe4de, 0x0000 }, + { 0xe4df, 0x0000 }, + { 0xe4e0, 0x0000 }, + { 0xe4e1, 0x0000 }, + { 0xe4e2, 0x0000 }, + { 0xe4e3, 0x0000 }, + { 0xe4e4, 0x0000 }, + { 0xe4e5, 0x0000 }, + { 0xe4e6, 0x0000 }, + { 0xe4e7, 0x0000 }, + { 0xe4e8, 0x0000 }, + { 0xe4e9, 0x0000 }, + { 0xe4ea, 0x0000 }, + { 0xe4eb, 0x0000 }, + { 0xe4ec, 0x0000 }, + { 0xe4ed, 0x0000 }, + { 0xe4ee, 0x0000 }, + { 0xe4ef, 0x0000 }, + { 0xe4f0, 0x0000 }, + { 0xe4f1, 0x0000 }, + { 0xe4f2, 0x0000 }, + { 0xe4f3, 0x0000 }, + { 0xe4f4, 0x0000 }, + { 0xe4f5, 0x0000 }, + { 0xe4f6, 0x0000 }, + { 0xe4f7, 0x0000 }, + { 0xe4f8, 0x0000 }, + { 0xe4f9, 0x0000 }, + { 0xe4fa, 0x0000 }, + { 0xe4fb, 0x0000 }, + { 0xe4fc, 0x0000 }, + { 0xe4fd, 0x0000 }, + { 0xe4fe, 0x0000 }, + { 0xe4ff, 0x0000 }, + { 0xe500, 0x0000 }, + { 0xe501, 0x0000 }, + { 0xe502, 0x0000 }, + { 0xe503, 0x0000 }, + { 0xe504, 0x0000 }, + { 0xe505, 0x0000 }, + { 0xe506, 0x0000 }, + { 0xe507, 0x0000 }, + { 0xe508, 0x0000 }, + { 0xe509, 0x0000 }, + { 0xe50a, 0x0000 }, + { 0xe50b, 0x0000 }, + { 0xe50c, 0x0000 }, + { 0xe50d, 0x0000 }, + { 0xe50e, 0x0000 }, + { 0xe50f, 0x0000 }, + { 0xe510, 0x0000 }, + { 0xe511, 0x0000 }, + { 0xe512, 0x0000 }, + { 0xe513, 0x0000 }, + { 0xe514, 0x0000 }, + { 0xe515, 0x0000 }, + { 0xe516, 0x0000 }, + { 0xe517, 0x0000 }, + { 0xe518, 0x0000 }, + { 0xe519, 0x0000 }, + { 0xe51a, 0x0000 }, + { 0xe51b, 0x0000 }, + { 0xe51c, 0x0000 }, + { 0xe51d, 0x0000 }, + { 0xe51e, 0x0000 }, + { 0xe51f, 0x0000 }, + { 0xe520, 0x0000 }, + { 0xe521, 0x0000 }, + { 0xe522, 0x0000 }, + { 0xe523, 0x0000 }, + { 0xe524, 0x0000 }, + { 0xe525, 0x0000 }, + { 0xe526, 0x0000 }, + { 0xe527, 0x0000 }, + { 0xe528, 0x0000 }, + { 0xe529, 0x0000 }, + { 0xe52a, 0x0000 }, + { 0xe52b, 0x0000 }, + { 0xe52c, 0x0000 }, + { 0xe52d, 0x0000 }, + { 0xe52e, 0x0000 }, + { 0xe52f, 0x0000 }, + { 0xe530, 0x0000 }, + { 0xe531, 0x0000 }, + { 0xe532, 0x0000 }, + { 0xe533, 0x0000 }, + { 0xe534, 0x0000 }, + { 0xe535, 0x0000 }, + { 0xe536, 0x0000 }, + { 0xe537, 0x0000 }, + { 0xe538, 0x0000 }, + { 0xe539, 0x0000 }, + { 0xe53a, 0x0000 }, + { 0xe53b, 0x0000 }, + { 0xe53c, 0x0000 }, + { 0xe53d, 0x0000 }, + { 0xe53e, 0x0000 }, + { 0xe53f, 0x0000 }, + { 0xe540, 0x0000 }, + { 0xe541, 0x0000 }, + { 0xe542, 0x0000 }, + { 0xe543, 0x0000 }, + { 0xe544, 0x0000 }, + { 0xe545, 0x0000 }, + { 0xe546, 0x0000 }, + { 0xe547, 0x0000 }, + { 0xe548, 0x0000 }, + { 0xe549, 0x0000 }, + { 0xe54a, 0x0000 }, + { 0xe54b, 0x0000 }, + { 0xe54c, 0x0000 }, + { 0xe54d, 0x0000 }, + { 0xe54e, 0x0000 }, + { 0xe54f, 0x0000 }, + { 0xe550, 0x0000 }, + { 0xe551, 0x0000 }, + { 0xe552, 0x0000 }, + { 0xe553, 0x0000 }, + { 0xe554, 0x0000 }, + { 0xe555, 0x0000 }, + { 0xe556, 0x0000 }, + { 0xe557, 0x0000 }, + { 0xe558, 0x0000 }, + { 0xe559, 0x0000 }, + { 0xe55a, 0x0000 }, + { 0xe55b, 0x0000 }, + { 0xe55c, 0x0000 }, + { 0xe55d, 0x0000 }, + { 0xe55e, 0x0000 }, + { 0xe55f, 0x0000 }, + { 0xe560, 0x0000 }, + { 0xe561, 0x0000 }, + { 0xe562, 0x0000 }, + { 0xe563, 0x0000 }, + { 0xe564, 0x0000 }, + { 0xe565, 0x0000 }, + { 0xe566, 0x0000 }, + { 0xe567, 0x0000 }, + { 0xe568, 0x0000 }, + { 0xe569, 0x0000 }, + { 0xe56a, 0x0000 }, + { 0xe56b, 0x0000 }, + { 0xe56c, 0x0000 }, + { 0xe56d, 0x0000 }, + { 0xe56e, 0x0000 }, + { 0xe56f, 0x0000 }, + { 0xe570, 0x0000 }, + { 0xe571, 0x0000 }, + { 0xe572, 0x0000 }, + { 0xe573, 0x0000 }, + { 0xe574, 0x0000 }, + { 0xe575, 0x0000 }, + { 0xe576, 0x0000 }, + { 0xe577, 0x0000 }, + { 0xe578, 0x0000 }, + { 0xe579, 0x0000 }, + { 0xe57a, 0x0000 }, + { 0xe57b, 0x0000 }, + { 0xe57c, 0x0000 }, + { 0xe57d, 0x0000 }, + { 0xe57e, 0x0000 }, + { 0xe57f, 0x0000 }, + { 0xe580, 0x0000 }, + { 0xe581, 0x0000 }, + { 0xe582, 0x0000 }, + { 0xe583, 0x0000 }, + { 0xe584, 0x0000 }, + { 0xe585, 0x0000 }, + { 0xe586, 0x0000 }, + { 0xe587, 0x0000 }, + { 0xe588, 0x0000 }, + { 0xe589, 0x0000 }, + { 0xe58a, 0x0000 }, + { 0xe58b, 0x0000 }, + { 0xe58c, 0x0000 }, + { 0xe58d, 0x0000 }, + { 0xe58e, 0x0000 }, + { 0xe58f, 0x0000 }, + { 0xe590, 0x0000 }, + { 0xe591, 0x0000 }, + { 0xe592, 0x0000 }, + { 0xe593, 0x0000 }, + { 0xe594, 0x0000 }, + { 0xe595, 0x0000 }, + { 0xe596, 0x0000 }, + { 0xe597, 0x0000 }, + { 0xe598, 0x0000 }, + { 0xe599, 0x0000 }, + { 0xe59a, 0x0000 }, + { 0xe59b, 0x0000 }, + { 0xe59c, 0x0000 }, + { 0xe59d, 0x0000 }, + { 0xe59e, 0x0000 }, + { 0xe59f, 0x0000 }, + { 0xe5a0, 0x0000 }, + { 0xe5a1, 0x0000 }, + { 0xe5a2, 0x0000 }, + { 0xe5a3, 0x0000 }, + { 0xe5a4, 0x0000 }, + { 0xe5a5, 0x0000 }, + { 0xe5a6, 0x0000 }, + { 0xe5a7, 0x0000 }, + { 0xe5a8, 0x0000 }, + { 0xe5a9, 0x0000 }, + { 0xe5aa, 0x0000 }, + { 0xe5ab, 0x0000 }, + { 0xe5ac, 0x0000 }, + { 0xe5ad, 0x0000 }, + { 0xe5ae, 0x0000 }, + { 0xe5af, 0x0000 }, + { 0xe5b0, 0x0000 }, + { 0xe5b1, 0x0000 }, + { 0xe5b2, 0x0000 }, + { 0xe5b3, 0x0000 }, + { 0xe5b4, 0x0000 }, + { 0xe5b5, 0x0000 }, + { 0xe5b6, 0x0000 }, + { 0xe5b7, 0x0000 }, + { 0xe5b8, 0x0000 }, + { 0xe5b9, 0x0000 }, + { 0xe5ba, 0x0000 }, + { 0xe5bb, 0x0000 }, + { 0xe5bc, 0x0000 }, + { 0xe5bd, 0x0000 }, + { 0xe5be, 0x0000 }, + { 0xe5bf, 0x0000 }, + { 0xe5c0, 0x0000 }, + { 0xe5c1, 0x0000 }, + { 0xe5c2, 0x0000 }, + { 0xe5c3, 0x0000 }, + { 0xe5c4, 0x0000 }, + { 0xe5c5, 0x0000 }, + { 0xe5c6, 0x0000 }, + { 0xe5c7, 0x0000 }, + { 0xe5c8, 0x0000 }, + { 0xe5c9, 0x0000 }, + { 0xe5ca, 0x0000 }, + { 0xe5cb, 0x0000 }, + { 0xe5cc, 0x0000 }, + { 0xe5cd, 0x0000 }, + { 0xe5ce, 0x0000 }, + { 0xe5cf, 0x0000 }, + { 0xe5d0, 0x0000 }, + { 0xe5d1, 0x0000 }, + { 0xe5d2, 0x0000 }, + { 0xe5d3, 0x0000 }, + { 0xe5d4, 0x0000 }, + { 0xe5d5, 0x0000 }, + { 0xe5d6, 0x0000 }, + { 0xe5d7, 0x0000 }, + { 0xe5d8, 0x0000 }, + { 0xe5d9, 0x0000 }, + { 0xe5da, 0x0000 }, + { 0xe5db, 0x0000 }, + { 0xe5dc, 0x0000 }, + { 0xe5dd, 0x0000 }, + { 0xe5de, 0x0000 }, + { 0xe5df, 0x0000 }, + { 0xe5e0, 0x0000 }, + { 0xe5e1, 0x0000 }, + { 0xe5e2, 0x0000 }, + { 0xe5e3, 0x0000 }, + { 0xe5e4, 0x0000 }, + { 0xe5e5, 0x0000 }, + { 0xe5e6, 0x0000 }, + { 0xe5e7, 0x0000 }, + { 0xe5e8, 0x0000 }, + { 0xe5e9, 0x0000 }, + { 0xe5ea, 0x0000 }, + { 0xe5eb, 0x0000 }, + { 0xe5ec, 0x0000 }, + { 0xe5ed, 0x0000 }, + { 0xe5ee, 0x0000 }, + { 0xe5ef, 0x0000 }, + { 0xe5f0, 0x0000 }, + { 0xe5f1, 0x0000 }, + { 0xe5f2, 0x0000 }, + { 0xe5f3, 0x0000 }, + { 0xe5f4, 0x0000 }, + { 0xe5f5, 0x0000 }, + { 0xe5f6, 0x0000 }, + { 0xe5f7, 0x0000 }, + { 0xe5f8, 0x0000 }, + { 0xe5f9, 0x0000 }, + { 0xe5fa, 0x0000 }, + { 0xe5fb, 0x0000 }, + { 0xe5fc, 0x0000 }, + { 0xe5fd, 0x0000 }, + { 0xe5fe, 0x0000 }, + { 0xe5ff, 0x0000 }, + { 0xe600, 0x0000 }, + { 0xe601, 0x0000 }, + { 0xe602, 0x0000 }, + { 0xe603, 0x0000 }, + { 0xe604, 0x0000 }, + { 0xe605, 0x0000 }, + { 0xe606, 0x0000 }, + { 0xe607, 0x0000 }, + { 0xe608, 0x0000 }, + { 0xe609, 0x0000 }, + { 0xe60a, 0x0000 }, + { 0xe60b, 0x0000 }, + { 0xe60c, 0x0000 }, + { 0xe60d, 0x0000 }, + { 0xe60e, 0x0000 }, + { 0xe60f, 0x0000 }, + { 0xe610, 0x0000 }, + { 0xe611, 0x0000 }, + { 0xe612, 0x0000 }, + { 0xe613, 0x0000 }, + { 0xe614, 0x0000 }, + { 0xe615, 0x0000 }, + { 0xe616, 0x0000 }, + { 0xe617, 0x0000 }, + { 0xe618, 0x0000 }, + { 0xe619, 0x0000 }, + { 0xe61a, 0x0000 }, + { 0xe61b, 0x0000 }, + { 0xe61c, 0x0000 }, + { 0xe61d, 0x0000 }, + { 0xe61e, 0x0000 }, + { 0xe61f, 0x0000 }, + { 0xe620, 0x0000 }, + { 0xe621, 0x0000 }, + { 0xe622, 0x0000 }, + { 0xe623, 0x0000 }, + { 0xe624, 0x0000 }, + { 0xe625, 0x0000 }, + { 0xe626, 0x0000 }, + { 0xe627, 0x0000 }, + { 0xe628, 0x0000 }, + { 0xe629, 0x0000 }, + { 0xe62a, 0x0000 }, + { 0xe62b, 0x0000 }, + { 0xe62c, 0x0000 }, + { 0xe62d, 0x0000 }, + { 0xe62e, 0x0000 }, + { 0xe62f, 0x0000 }, + { 0xe630, 0x0000 }, + { 0xe631, 0x0000 }, + { 0xe632, 0x0000 }, + { 0xe633, 0x0000 }, + { 0xe634, 0x0000 }, + { 0xe635, 0x0000 }, + { 0xe636, 0x0000 }, + { 0xe637, 0x0000 }, + { 0xe638, 0x0000 }, + { 0xe639, 0x0000 }, + { 0xe63a, 0x0000 }, + { 0xe63b, 0x0000 }, + { 0xe63c, 0x0000 }, + { 0xe63d, 0x0000 }, + { 0xe63e, 0x0000 }, + { 0xe63f, 0x0000 }, + { 0xe640, 0x0000 }, + { 0xe641, 0x0000 }, + { 0xe642, 0x0000 }, + { 0xe643, 0x0000 }, + { 0xe644, 0x0000 }, + { 0xe645, 0x0000 }, + { 0xe646, 0x0000 }, + { 0xe647, 0x0000 }, + { 0xe648, 0x0000 }, + { 0xe649, 0x0000 }, + { 0xe64a, 0x0000 }, + { 0xe64b, 0x0000 }, + { 0xe64c, 0x0000 }, + { 0xe64d, 0x0000 }, + { 0xe64e, 0x0000 }, + { 0xe64f, 0x0000 }, + { 0xe650, 0x0000 }, + { 0xe651, 0x0000 }, + { 0xe652, 0x0000 }, + { 0xe653, 0x0000 }, + { 0xe654, 0x0000 }, + { 0xe655, 0x0000 }, + { 0xe656, 0x0000 }, + { 0xe657, 0x0000 }, + { 0xe658, 0x0000 }, + { 0xe659, 0x0000 }, + { 0xe65a, 0x0000 }, + { 0xe65b, 0x0000 }, + { 0xe65c, 0x0000 }, + { 0xe65d, 0x0000 }, + { 0xe65e, 0x0000 }, + { 0xe65f, 0x0000 }, + { 0xe660, 0x0000 }, + { 0xe661, 0x0000 }, + { 0xe662, 0x0000 }, + { 0xe663, 0x0000 }, + { 0xe664, 0x0000 }, + { 0xe665, 0x0000 }, + { 0xe666, 0x0000 }, + { 0xe667, 0x0000 }, + { 0xe668, 0x0000 }, + { 0xe669, 0x0000 }, + { 0xe66a, 0x0000 }, + { 0xe66b, 0x0000 }, + { 0xe66c, 0x0000 }, + { 0xe66d, 0x0000 }, + { 0xe66e, 0x0000 }, + { 0xe66f, 0x0000 }, + { 0xe670, 0x0000 }, + { 0xe671, 0x0000 }, + { 0xe672, 0x0000 }, + { 0xe673, 0x0000 }, + { 0xe674, 0x0000 }, + { 0xe675, 0x0000 }, + { 0xe676, 0x0000 }, + { 0xe677, 0x0000 }, + { 0xe678, 0x0000 }, + { 0xe679, 0x0000 }, + { 0xe67a, 0x0000 }, + { 0xe67b, 0x0000 }, + { 0xe67c, 0x0000 }, + { 0xe67d, 0x0000 }, + { 0xe67e, 0x0000 }, + { 0xe67f, 0x0000 }, + { 0xe680, 0x0000 }, + { 0xe681, 0x0000 }, + { 0xe682, 0x0000 }, + { 0xe683, 0x0000 }, + { 0xe684, 0x0000 }, + { 0xe685, 0x0000 }, + { 0xe686, 0x0000 }, + { 0xe687, 0x0000 }, + { 0xe688, 0x0000 }, + { 0xe689, 0x0000 }, + { 0xe68a, 0x0000 }, + { 0xe68b, 0x0000 }, + { 0xe68c, 0x0000 }, + { 0xe68d, 0x0000 }, + { 0xe68e, 0x0000 }, + { 0xe68f, 0x0000 }, + { 0xe690, 0x0000 }, + { 0xe691, 0x0000 }, + { 0xe692, 0x0000 }, + { 0xe693, 0x0000 }, + { 0xe694, 0x0000 }, + { 0xe695, 0x0000 }, + { 0xe696, 0x0000 }, + { 0xe697, 0x0000 }, + { 0xe698, 0x0000 }, + { 0xe699, 0x0000 }, + { 0xe69a, 0x0000 }, + { 0xe69b, 0x0000 }, + { 0xe69c, 0x0000 }, + { 0xe69d, 0x0000 }, + { 0xe69e, 0x0000 }, + { 0xe69f, 0x0000 }, + { 0xe6a0, 0x0000 }, + { 0xe6a1, 0x0000 }, + { 0xe6a2, 0x0000 }, + { 0xe6a3, 0x0000 }, + { 0xe6a4, 0x0000 }, + { 0xe6a5, 0x0000 }, + { 0xe6a6, 0x0000 }, + { 0xe6a7, 0x0000 }, + { 0xe6a8, 0x0000 }, + { 0xe6a9, 0x0000 }, + { 0xe6aa, 0x0000 }, + { 0xe6ab, 0x0000 }, + { 0xe6ac, 0x0000 }, + { 0xe6ad, 0x0000 }, + { 0xe6ae, 0x0000 }, + { 0xe6af, 0x0000 }, + { 0xe6b0, 0x0000 }, + { 0xe6b1, 0x0000 }, + { 0xe6b2, 0x0000 }, + { 0xe6b3, 0x0000 }, + { 0xe6b4, 0x0000 }, + { 0xe6b5, 0x0000 }, + { 0xe6b6, 0x0000 }, + { 0xe6b7, 0x0000 }, + { 0xe6b8, 0x0000 }, + { 0xe6b9, 0x0000 }, + { 0xe6ba, 0x0000 }, + { 0xe6bb, 0x0000 }, + { 0xe6bc, 0x0000 }, + { 0xe6bd, 0x0000 }, + { 0xe6be, 0x0000 }, + { 0xe6bf, 0x0000 }, + { 0xe6c0, 0x0000 }, + { 0xe6c1, 0x0000 }, + { 0xe6c2, 0x0000 }, + { 0xe6c3, 0x0000 }, + { 0xe6c4, 0x0000 }, + { 0xe6c5, 0x0000 }, + { 0xe6c6, 0x0000 }, + { 0xe6c7, 0x0000 }, + { 0xe6c8, 0x0000 }, + { 0xe6c9, 0x0000 }, + { 0xe6ca, 0x0000 }, + { 0xe6cb, 0x0000 }, + { 0xe6cc, 0x0000 }, + { 0xe6cd, 0x0000 }, + { 0xe6ce, 0x0000 }, + { 0xe6cf, 0x0000 }, + { 0xe6d0, 0x0000 }, + { 0xe6d1, 0x0000 }, + { 0xe6d2, 0x0000 }, + { 0xe6d3, 0x0000 }, + { 0xe6d4, 0x0000 }, + { 0xe6d5, 0x0000 }, + { 0xe6d6, 0x0000 }, + { 0xe6d7, 0x0000 }, + { 0xe6d8, 0x0000 }, + { 0xe6d9, 0x0000 }, + { 0xe6da, 0x0000 }, + { 0xe6db, 0x0000 }, + { 0xe6dc, 0x0000 }, + { 0xe6dd, 0x0000 }, + { 0xe6de, 0x0000 }, + { 0xe6df, 0x0000 }, + { 0xe6e0, 0x0000 }, + { 0xe6e1, 0x0000 }, + { 0xe6e2, 0x0000 }, + { 0xe6e3, 0x0000 }, + { 0xe6e4, 0x0000 }, + { 0xe6e5, 0x0000 }, + { 0xe6e6, 0x0000 }, + { 0xe6e7, 0x0000 }, + { 0xe6e8, 0x0000 }, + { 0xe6e9, 0x0000 }, + { 0xe6ea, 0x0000 }, + { 0xe6eb, 0x0000 }, + { 0xe6ec, 0x0000 }, + { 0xe6ed, 0x0000 }, + { 0xe6ee, 0x0000 }, + { 0xe6ef, 0x0000 }, + { 0xe6f0, 0x0000 }, + { 0xe6f1, 0x0000 }, + { 0xe6f2, 0x0000 }, + { 0xe6f3, 0x0000 }, + { 0xe6f4, 0x0000 }, + { 0xe6f5, 0x0000 }, + { 0xe6f6, 0x0000 }, + { 0xe6f7, 0x0000 }, + { 0xe6f8, 0x0000 }, + { 0xe6f9, 0x0000 }, + { 0xe6fa, 0x0000 }, + { 0xe6fb, 0x0000 }, + { 0xe6fc, 0x0000 }, + { 0xe6fd, 0x0000 }, + { 0xe6fe, 0x0000 }, + { 0xe6ff, 0x0000 }, + { 0xe700, 0x0000 }, + { 0xe701, 0x0000 }, + { 0xe702, 0x0000 }, + { 0xe703, 0x0000 }, + { 0xe704, 0x0000 }, + { 0xe705, 0x0000 }, + { 0xe706, 0x0000 }, + { 0xe707, 0x0000 }, + { 0xe708, 0x0000 }, + { 0xe709, 0x0000 }, + { 0xe70a, 0x0000 }, + { 0xe70b, 0x0000 }, + { 0xe70c, 0x0000 }, + { 0xe70d, 0x0000 }, + { 0xe70e, 0x0000 }, + { 0xe70f, 0x0000 }, + { 0xe710, 0x0000 }, + { 0xe711, 0x0000 }, + { 0xe712, 0x0000 }, + { 0xe713, 0x0000 }, + { 0xe714, 0x0000 }, + { 0xe715, 0x0000 }, + { 0xe716, 0x0000 }, + { 0xe717, 0x0000 }, + { 0xe718, 0x0000 }, + { 0xe719, 0x0000 }, + { 0xe71a, 0x0000 }, + { 0xe71b, 0x0000 }, + { 0xe71c, 0x0000 }, + { 0xe71d, 0x0000 }, + { 0xe71e, 0x0000 }, + { 0xe71f, 0x0000 }, + { 0xe720, 0x0000 }, + { 0xe721, 0x0000 }, + { 0xe722, 0x0000 }, + { 0xe723, 0x0000 }, + { 0xe724, 0x0000 }, + { 0xe725, 0x0000 }, + { 0xe726, 0x0000 }, + { 0xe727, 0x0000 }, + { 0xe728, 0x0000 }, + { 0xe729, 0x0000 }, + { 0xe72a, 0x0000 }, + { 0xe72b, 0x0000 }, + { 0xe72c, 0x0000 }, + { 0xe72d, 0x0000 }, + { 0xe72e, 0x0000 }, + { 0xe72f, 0x0000 }, + { 0xe730, 0x0000 }, + { 0xe731, 0x0000 }, + { 0xe732, 0x0000 }, + { 0xe733, 0x0000 }, + { 0xe734, 0x0000 }, + { 0xe735, 0x0000 }, + { 0xe736, 0x0000 }, + { 0xe737, 0x0000 }, + { 0xe738, 0x0000 }, + { 0xe739, 0x0000 }, + { 0xe73a, 0x0000 }, + { 0xe73b, 0x0000 }, + { 0xe73c, 0x0000 }, + { 0xe73d, 0x0000 }, + { 0xe73e, 0x0000 }, + { 0xe73f, 0x0000 }, + { 0xe740, 0x0000 }, + { 0xe741, 0x0000 }, + { 0xe742, 0x0000 }, + { 0xe743, 0x0000 }, + { 0xe744, 0x0000 }, + { 0xe745, 0x0000 }, + { 0xe746, 0x0000 }, + { 0xe747, 0x0000 }, + { 0xe748, 0x0000 }, + { 0xe749, 0x0000 }, + { 0xe74a, 0x0000 }, + { 0xe74b, 0x0000 }, + { 0xe74c, 0x0000 }, + { 0xe74d, 0x0000 }, + { 0xe74e, 0x0000 }, + { 0xe74f, 0x0000 }, + { 0xe750, 0x0000 }, + { 0xe751, 0x0000 }, + { 0xe752, 0x0000 }, + { 0xe753, 0x0000 }, + { 0xe754, 0x0000 }, + { 0xe755, 0x0000 }, + { 0xe756, 0x0000 }, + { 0xe757, 0x0000 }, + { 0xe758, 0x0000 }, + { 0xe759, 0x0000 }, + { 0xe75a, 0x0000 }, + { 0xe75b, 0x0000 }, + { 0xe75c, 0x0000 }, + { 0xe75d, 0x0000 }, + { 0xe75e, 0x0000 }, + { 0xe75f, 0x0000 }, + { 0xe760, 0x0000 }, + { 0xe761, 0x0000 }, + { 0xe762, 0x0000 }, + { 0xe763, 0x0000 }, + { 0xe764, 0x0000 }, + { 0xe765, 0x0000 }, + { 0xe766, 0x0000 }, + { 0xe767, 0x0000 }, + { 0xe768, 0x0000 }, + { 0xe769, 0x0000 }, + { 0xe76a, 0x0000 }, + { 0xe76b, 0x0000 }, + { 0xe76c, 0x0000 }, + { 0xe76d, 0x0000 }, + { 0xe76e, 0x0000 }, + { 0xe76f, 0x0000 }, + { 0xe770, 0x0000 }, + { 0xe771, 0x0000 }, + { 0xe772, 0x0000 }, + { 0xe773, 0x0000 }, + { 0xe774, 0x0000 }, + { 0xe775, 0x0000 }, + { 0xe776, 0x0000 }, + { 0xe777, 0x0000 }, + { 0xe778, 0x0000 }, + { 0xe779, 0x0000 }, + { 0xe77a, 0x0000 }, + { 0xe77b, 0x0000 }, + { 0xe77c, 0x0000 }, + { 0xe77d, 0x0000 }, + { 0xe77e, 0x0000 }, + { 0xe77f, 0x0000 }, + { 0xe780, 0x0000 }, + { 0xe781, 0x0000 }, + { 0xe782, 0x0000 }, + { 0xe783, 0x0000 }, + { 0xe784, 0x0000 }, + { 0xe785, 0x0000 }, + { 0xe786, 0x0000 }, + { 0xe787, 0x0000 }, + { 0xe788, 0x0000 }, + { 0xe789, 0x0000 }, + { 0xe78a, 0x0000 }, + { 0xe78b, 0x0000 }, + { 0xe78c, 0x0000 }, + { 0xe78d, 0x0000 }, + { 0xe78e, 0x0000 }, + { 0xe78f, 0x0000 }, + { 0xe790, 0x0000 }, + { 0xe791, 0x0000 }, + { 0xe792, 0x0000 }, + { 0xe793, 0x0000 }, + { 0xe794, 0x0000 }, + { 0xe795, 0x0000 }, + { 0xe796, 0x0000 }, + { 0xe797, 0x0000 }, + { 0xe798, 0x0000 }, + { 0xe799, 0x0000 }, + { 0xe79a, 0x0000 }, + { 0xe79b, 0x0000 }, + { 0xe79c, 0x0000 }, + { 0xe79d, 0x0000 }, + { 0xe79e, 0x0000 }, + { 0xe79f, 0x0000 }, + { 0xe7a0, 0x0000 }, + { 0xe7a1, 0x0000 }, + { 0xe7a2, 0x0000 }, + { 0xe7a3, 0x0000 }, + { 0xe7a4, 0x0000 }, + { 0xe7a5, 0x0000 }, + { 0xe7a6, 0x0000 }, + { 0xe7a7, 0x0000 }, + { 0xe7a8, 0x0000 }, + { 0xe7a9, 0x0000 }, + { 0xe7aa, 0x0000 }, + { 0xe7ab, 0x0000 }, + { 0xe7ac, 0x0000 }, + { 0xe7ad, 0x0000 }, + { 0xe7ae, 0x0000 }, + { 0xe7af, 0x0000 }, + { 0xe7b0, 0x0000 }, + { 0xe7b1, 0x0000 }, + { 0xe7b2, 0x0000 }, + { 0xe7b3, 0x0000 }, + { 0xe7b4, 0x0000 }, + { 0xe7b5, 0x0000 }, + { 0xe7b6, 0x0000 }, + { 0xe7b7, 0x0000 }, + { 0xe7b8, 0x0000 }, + { 0xe7b9, 0x0000 }, + { 0xe7ba, 0x0000 }, + { 0xe7bb, 0x0000 }, + { 0xe7bc, 0x0000 }, + { 0xe7bd, 0x0000 }, + { 0xe7be, 0x0000 }, + { 0xe7bf, 0x0000 }, + { 0xe7c0, 0x0000 }, + { 0xe7c1, 0x0000 }, + { 0xe7c2, 0x0000 }, + { 0xe7c3, 0x0000 }, + { 0xe7c4, 0x0000 }, + { 0xe7c5, 0x0000 }, + { 0xe7c6, 0x0000 }, + { 0xe7c7, 0x0000 }, + { 0xe7c8, 0x0000 }, + { 0xe7c9, 0x0000 }, + { 0xe7ca, 0x0000 }, + { 0xe7cb, 0x0000 }, + { 0xe7cc, 0x0000 }, + { 0xe7cd, 0x0000 }, + { 0xe7ce, 0x0000 }, + { 0xe7cf, 0x0000 }, + { 0xe7d0, 0x0000 }, + { 0xe7d1, 0x0000 }, + { 0xe7d2, 0x0000 }, + { 0xe7d3, 0x0000 }, + { 0xe7d4, 0x0000 }, + { 0xe7d5, 0x0000 }, + { 0xe7d6, 0x0000 }, + { 0xe7d7, 0x0000 }, + { 0xe7d8, 0x0000 }, + { 0xe7d9, 0x0000 }, + { 0xe7da, 0x0000 }, + { 0xe7db, 0x0000 }, + { 0xe7dc, 0x0000 }, + { 0xe7dd, 0x0000 }, + { 0xe7de, 0x0000 }, + { 0xe7df, 0x0000 }, + { 0xe7e0, 0x0000 }, + { 0xe7e1, 0x0000 }, + { 0xe7e2, 0x0000 }, + { 0xe7e3, 0x0000 }, + { 0xe7e4, 0x0000 }, + { 0xe7e5, 0x0000 }, + { 0xe7e6, 0x0000 }, + { 0xe7e7, 0x0000 }, + { 0xe7e8, 0x0000 }, + { 0xe7e9, 0x0000 }, + { 0xe7ea, 0x0000 }, + { 0xe7eb, 0x0000 }, + { 0xe7ec, 0x0000 }, + { 0xe7ed, 0x0000 }, + { 0xe7ee, 0x0000 }, + { 0xe7ef, 0x0000 }, + { 0xe7f0, 0x0000 }, + { 0xe7f1, 0x0000 }, + { 0xe7f2, 0x0000 }, + { 0xe7f3, 0x0000 }, + { 0xe7f4, 0x0000 }, + { 0xe7f5, 0x0000 }, + { 0xe7f6, 0x0000 }, + { 0xe7f7, 0x0000 }, + { 0xe7f8, 0x0000 }, + { 0xe7f9, 0x0000 }, + { 0xe7fa, 0x0000 }, + { 0xe7fb, 0x0000 }, + { 0xe7fc, 0x0000 }, + { 0xe7fd, 0x0000 }, + { 0xe7fe, 0x0000 }, + { 0xe7ff, 0x0000 }, + { 0xe800, 0x0000 }, + { 0xe801, 0x0000 }, + { 0xe802, 0x0000 }, + { 0xe803, 0x0000 }, + { 0xe804, 0x0000 }, + { 0xe805, 0x0000 }, + { 0xe806, 0x0000 }, + { 0xe807, 0x0000 }, + { 0xe808, 0x0000 }, + { 0xe809, 0x0000 }, + { 0xe80a, 0x0000 }, + { 0xe80b, 0x0000 }, + { 0xe80c, 0x0000 }, + { 0xe80d, 0x0000 }, + { 0xe80e, 0x0000 }, + { 0xe80f, 0x0000 }, + { 0xe810, 0x0000 }, + { 0xe811, 0x0000 }, + { 0xe812, 0x0000 }, + { 0xe813, 0x0000 }, + { 0xe814, 0x0000 }, + { 0xe815, 0x0000 }, + { 0xe816, 0x0000 }, + { 0xe817, 0x0000 }, + { 0xe818, 0x0000 }, + { 0xe819, 0x0000 }, + { 0xe81a, 0x0000 }, + { 0xe81b, 0x0000 }, + { 0xe81c, 0x0000 }, + { 0xe81d, 0x0000 }, + { 0xe81e, 0x0000 }, + { 0xe81f, 0x0000 }, + { 0xe820, 0x0000 }, + { 0xe821, 0x0000 }, + { 0xe822, 0x0000 }, + { 0xe823, 0x0000 }, + { 0xe824, 0x0000 }, + { 0xe825, 0x0000 }, + { 0xe826, 0x0000 }, + { 0xe827, 0x0000 }, + { 0xe828, 0x0000 }, + { 0xe829, 0x0000 }, + { 0xe82a, 0x0000 }, + { 0xe82b, 0x0000 }, + { 0xe82c, 0x0000 }, + { 0xe82d, 0x0000 }, + { 0xe82e, 0x0000 }, + { 0xe82f, 0x0000 }, + { 0xe830, 0x0000 }, + { 0xe831, 0x0000 }, + { 0xe832, 0x0000 }, + { 0xe833, 0x0000 }, + { 0xe834, 0x0000 }, + { 0xe835, 0x0000 }, + { 0xe836, 0x0000 }, + { 0xe837, 0x0000 }, + { 0xe838, 0x0000 }, + { 0xe839, 0x0000 }, + { 0xe83a, 0x0000 }, + { 0xe83b, 0x0000 }, + { 0xe83c, 0x0000 }, + { 0xe83d, 0x0000 }, + { 0xe83e, 0x0000 }, + { 0xe83f, 0x0000 }, + { 0xe840, 0x0000 }, + { 0xe841, 0x0000 }, + { 0xe842, 0x0000 }, + { 0xe843, 0x0000 }, + { 0xe844, 0x0000 }, + { 0xe845, 0x0000 }, + { 0xe846, 0x0000 }, + { 0xe847, 0x0000 }, + { 0xe848, 0x0000 }, + { 0xe849, 0x0000 }, + { 0xe84a, 0x0000 }, + { 0xe84b, 0x0000 }, + { 0xe84c, 0x0000 }, + { 0xe84d, 0x0000 }, + { 0xe84e, 0x0000 }, + { 0xe84f, 0x0000 }, + { 0xe850, 0x0000 }, + { 0xe851, 0x0000 }, + { 0xe852, 0x0000 }, + { 0xe853, 0x0000 }, + { 0xe854, 0x0000 }, + { 0xe855, 0x0000 }, + { 0xe856, 0x0000 }, + { 0xe857, 0x0000 }, + { 0xe858, 0x0000 }, + { 0xe859, 0x0000 }, + { 0xe85a, 0x0000 }, + { 0xe85b, 0x0000 }, + { 0xe85c, 0x0000 }, + { 0xe85d, 0x0000 }, + { 0xe85e, 0x0000 }, + { 0xe85f, 0x0000 }, + { 0xe860, 0x0000 }, + { 0xe861, 0x0000 }, + { 0xe862, 0x0000 }, + { 0xe863, 0x0000 }, + { 0xe864, 0x0000 }, + { 0xe865, 0x0000 }, + { 0xe866, 0x0000 }, + { 0xe867, 0x0000 }, + { 0xe868, 0x0000 }, + { 0xe869, 0x0000 }, + { 0xe86a, 0x0000 }, + { 0xe86b, 0x0000 }, + { 0xe86c, 0x0000 }, + { 0xe86d, 0x0000 }, + { 0xe86e, 0x0000 }, + { 0xe86f, 0x0000 }, + { 0xe870, 0x0000 }, + { 0xe871, 0x0000 }, + { 0xe872, 0x0000 }, + { 0xe873, 0x0000 }, + { 0xe874, 0x0000 }, + { 0xe875, 0x0000 }, + { 0xe876, 0x0000 }, + { 0xe877, 0x0000 }, + { 0xe878, 0x0000 }, + { 0xe879, 0x0000 }, + { 0xe87a, 0x0000 }, + { 0xe87b, 0x0000 }, + { 0xe87c, 0x0000 }, + { 0xe87d, 0x0000 }, + { 0xe87e, 0x0000 }, + { 0xe87f, 0x0000 }, + { 0xe880, 0x0000 }, + { 0xe881, 0x0000 }, + { 0xe882, 0x0000 }, + { 0xe883, 0x0000 }, + { 0xe884, 0x0000 }, + { 0xe885, 0x0000 }, + { 0xe886, 0x0000 }, + { 0xe887, 0x0000 }, + { 0xe888, 0x0000 }, + { 0xe889, 0x0000 }, + { 0xe88a, 0x0000 }, + { 0xe88b, 0x0000 }, + { 0xe88c, 0x0000 }, + { 0xe88d, 0x0000 }, + { 0xe88e, 0x0000 }, + { 0xe88f, 0x0000 }, + { 0xe890, 0x0000 }, + { 0xe891, 0x0000 }, + { 0xe892, 0x0000 }, + { 0xe893, 0x0000 }, + { 0xe894, 0x0000 }, + { 0xe895, 0x0000 }, + { 0xe896, 0x0000 }, + { 0xe897, 0x0000 }, + { 0xe898, 0x0000 }, + { 0xe899, 0x0000 }, + { 0xe89a, 0x0000 }, + { 0xe89b, 0x0000 }, + { 0xe89c, 0x0000 }, + { 0xe89d, 0x0000 }, + { 0xe89e, 0x0000 }, + { 0xe89f, 0x0000 }, + { 0xe8a0, 0x0000 }, + { 0xe8a1, 0x0000 }, + { 0xe8a2, 0x0000 }, + { 0xe8a3, 0x0000 }, + { 0xe8a4, 0x0000 }, + { 0xe8a5, 0x0000 }, + { 0xe8a6, 0x0000 }, + { 0xe8a7, 0x0000 }, + { 0xe8a8, 0x0000 }, + { 0xe8a9, 0x0000 }, + { 0xe8aa, 0x0000 }, + { 0xe8ab, 0x0000 }, + { 0xe8ac, 0x0000 }, + { 0xe8ad, 0x0000 }, + { 0xe8ae, 0x0000 }, + { 0xe8af, 0x0000 }, + { 0xe8b0, 0x0000 }, + { 0xe8b1, 0x0000 }, + { 0xe8b2, 0x0000 }, + { 0xe8b3, 0x0000 }, + { 0xe8b4, 0x0000 }, + { 0xe8b5, 0x0000 }, + { 0xe8b6, 0x0000 }, + { 0xe8b7, 0x0000 }, + { 0xe8b8, 0x0000 }, + { 0xe8b9, 0x0000 }, + { 0xe8ba, 0x0000 }, + { 0xe8bb, 0x0000 }, + { 0xe8bc, 0x0000 }, + { 0xe8bd, 0x0000 }, + { 0xe8be, 0x0000 }, + { 0xe8bf, 0x0000 }, + { 0xe8c0, 0x0000 }, + { 0xe8c1, 0x0000 }, + { 0xe8c2, 0x0000 }, + { 0xe8c3, 0x0000 }, + { 0xe8c4, 0x0000 }, + { 0xe8c5, 0x0000 }, + { 0xe8c6, 0x0000 }, + { 0xe8c7, 0x0000 }, + { 0xe8c8, 0x0000 }, + { 0xe8c9, 0x0000 }, + { 0xe8ca, 0x0000 }, + { 0xe8cb, 0x0000 }, + { 0xe8cc, 0x0000 }, + { 0xe8cd, 0x0000 }, + { 0xe8ce, 0x0000 }, + { 0xe8cf, 0x0000 }, + { 0xe8d0, 0x0000 }, + { 0xe8d1, 0x0000 }, + { 0xe8d2, 0x0000 }, + { 0xe8d3, 0x0000 }, + { 0xe8d4, 0x0000 }, + { 0xe8d5, 0x0000 }, + { 0xe8d6, 0x0000 }, + { 0xe8d7, 0x0000 }, + { 0xe8d8, 0x0000 }, + { 0xe8d9, 0x0000 }, + { 0xe8da, 0x0000 }, + { 0xe8db, 0x0000 }, + { 0xe8dc, 0x0000 }, + { 0xe8dd, 0x0000 }, + { 0xe8de, 0x0000 }, + { 0xe8df, 0x0000 }, + { 0xe8e0, 0x0000 }, + { 0xe8e1, 0x0000 }, + { 0xe8e2, 0x0000 }, + { 0xe8e3, 0x0000 }, + { 0xe8e4, 0x0000 }, + { 0xe8e5, 0x0000 }, + { 0xe8e6, 0x0000 }, + { 0xe8e7, 0x0000 }, + { 0xe8e8, 0x0000 }, + { 0xe8e9, 0x0000 }, + { 0xe8ea, 0x0000 }, + { 0xe8eb, 0x0000 }, + { 0xe8ec, 0x0000 }, + { 0xe8ed, 0x0000 }, + { 0xe8ee, 0x0000 }, + { 0xe8ef, 0x0000 }, + { 0xe8f0, 0x0000 }, + { 0xe8f1, 0x0000 }, + { 0xe8f2, 0x0000 }, + { 0xe8f3, 0x0000 }, + { 0xe8f4, 0x0000 }, + { 0xe8f5, 0x0000 }, + { 0xe8f6, 0x0000 }, + { 0xe8f7, 0x0000 }, + { 0xe8f8, 0x0000 }, + { 0xe8f9, 0x0000 }, + { 0xe8fa, 0x0000 }, + { 0xe8fb, 0x0000 }, + { 0xe8fc, 0x0000 }, + { 0xe8fd, 0x0000 }, + { 0xe8fe, 0x0000 }, + { 0xe8ff, 0x0000 }, + { 0xe900, 0x0000 }, + { 0xe901, 0x0000 }, + { 0xe902, 0x0000 }, + { 0xe903, 0x0000 }, + { 0xe904, 0x0000 }, + { 0xe905, 0x0000 }, + { 0xe906, 0x0000 }, + { 0xe907, 0x0000 }, + { 0xe908, 0x0000 }, + { 0xe909, 0x0000 }, + { 0xe90a, 0x0000 }, + { 0xe90b, 0x0000 }, + { 0xe90c, 0x0000 }, + { 0xe90d, 0x0000 }, + { 0xe90e, 0x0000 }, + { 0xe90f, 0x0000 }, + { 0xe910, 0x0000 }, + { 0xe911, 0x0000 }, + { 0xe912, 0x0000 }, + { 0xe913, 0x0000 }, + { 0xe914, 0x0000 }, + { 0xe915, 0x0000 }, + { 0xe916, 0x0000 }, + { 0xe917, 0x0000 }, + { 0xe918, 0x0000 }, + { 0xe919, 0x0000 }, + { 0xe91a, 0x0000 }, + { 0xe91b, 0x0000 }, + { 0xe91c, 0x0000 }, + { 0xe91d, 0x0000 }, + { 0xe91e, 0x0000 }, + { 0xe91f, 0x0000 }, + { 0xe920, 0x0000 }, + { 0xe921, 0x0000 }, + { 0xe922, 0x0000 }, + { 0xe923, 0x0000 }, + { 0xe924, 0x0000 }, + { 0xe925, 0x0000 }, + { 0xe926, 0x0000 }, + { 0xe927, 0x0000 }, + { 0xe928, 0x0000 }, + { 0xe929, 0x0000 }, + { 0xe92a, 0x0000 }, + { 0xe92b, 0x0000 }, + { 0xe92c, 0x0000 }, + { 0xe92d, 0x0000 }, + { 0xe92e, 0x0000 }, + { 0xe92f, 0x0000 }, + { 0xe930, 0x0000 }, + { 0xe931, 0x0000 }, + { 0xe932, 0x0000 }, + { 0xe933, 0x0000 }, + { 0xe934, 0x0000 }, + { 0xe935, 0x0000 }, + { 0xe936, 0x0000 }, + { 0xe937, 0x0000 }, + { 0xe938, 0x0000 }, + { 0xe939, 0x0000 }, + { 0xe93a, 0x0000 }, + { 0xe93b, 0x0000 }, + { 0xe93c, 0x0000 }, + { 0xe93d, 0x0000 }, + { 0xe93e, 0x0000 }, + { 0xe93f, 0x0000 }, + { 0xe940, 0x0000 }, + { 0xe941, 0x0000 }, + { 0xe942, 0x0000 }, + { 0xe943, 0x0000 }, + { 0xe944, 0x0000 }, + { 0xe945, 0x0000 }, + { 0xe946, 0x0000 }, + { 0xe947, 0x0000 }, + { 0xe948, 0x0000 }, + { 0xe949, 0x0000 }, + { 0xe94a, 0x0000 }, + { 0xe94b, 0x0000 }, + { 0xe94c, 0x0000 }, + { 0xe94d, 0x0000 }, + { 0xe94e, 0x0000 }, + { 0xe94f, 0x0000 }, + { 0xe950, 0x0000 }, + { 0xe951, 0x0000 }, + { 0xe952, 0x0000 }, + { 0xe953, 0x0000 }, + { 0xe954, 0x0000 }, + { 0xe955, 0x0000 }, + { 0xe956, 0x0000 }, + { 0xe957, 0x0000 }, + { 0xe958, 0x0000 }, + { 0xe959, 0x0000 }, + { 0xe95a, 0x0000 }, + { 0xe95b, 0x0000 }, + { 0xe95c, 0x0000 }, + { 0xe95d, 0x0000 }, + { 0xe95e, 0x0000 }, + { 0xe95f, 0x0000 }, + { 0xe960, 0x0000 }, + { 0xe961, 0x0000 }, + { 0xe962, 0x0000 }, + { 0xe963, 0x0000 }, + { 0xe964, 0x0000 }, + { 0xe965, 0x0000 }, + { 0xe966, 0x0000 }, + { 0xe967, 0x0000 }, + { 0xe968, 0x0000 }, + { 0xe969, 0x0000 }, + { 0xe96a, 0x0000 }, + { 0xe96b, 0x0000 }, + { 0xe96c, 0x0000 }, + { 0xe96d, 0x0000 }, + { 0xe96e, 0x0000 }, + { 0xe96f, 0x0000 }, + { 0xe970, 0x0000 }, + { 0xe971, 0x0000 }, + { 0xe972, 0x0000 }, + { 0xe973, 0x0000 }, + { 0xe974, 0x0000 }, + { 0xe975, 0x0000 }, + { 0xe976, 0x0000 }, + { 0xe977, 0x0000 }, + { 0xe978, 0x0000 }, + { 0xe979, 0x0000 }, + { 0xe97a, 0x0000 }, + { 0xe97b, 0x0000 }, + { 0xe97c, 0x0000 }, + { 0xe97d, 0x0000 }, + { 0xe97e, 0x0000 }, + { 0xe97f, 0x0000 }, + { 0xe980, 0x0000 }, + { 0xe981, 0x0000 }, + { 0xe982, 0x0000 }, + { 0xe983, 0x0000 }, + { 0xe984, 0x0000 }, + { 0xe985, 0x0000 }, + { 0xe986, 0x0000 }, + { 0xe987, 0x0000 }, + { 0xe988, 0x0000 }, + { 0xe989, 0x0000 }, + { 0xe98a, 0x0000 }, + { 0xe98b, 0x0000 }, + { 0xe98c, 0x0000 }, + { 0xe98d, 0x0000 }, + { 0xe98e, 0x0000 }, + { 0xe98f, 0x0000 }, + { 0xe990, 0x0000 }, + { 0xe991, 0x0000 }, + { 0xe992, 0x0000 }, + { 0xe993, 0x0000 }, + { 0xe994, 0x0000 }, + { 0xe995, 0x0000 }, + { 0xe996, 0x0000 }, + { 0xe997, 0x0000 }, + { 0xe998, 0x0000 }, + { 0xe999, 0x0000 }, + { 0xe99a, 0x0000 }, + { 0xe99b, 0x0000 }, + { 0xe99c, 0x0000 }, + { 0xe99d, 0x0000 }, + { 0xe99e, 0x0000 }, + { 0xe99f, 0x0000 }, + { 0xe9a0, 0x0000 }, + { 0xe9a1, 0x0000 }, + { 0xe9a2, 0x0000 }, + { 0xe9a3, 0x0000 }, + { 0xe9a4, 0x0000 }, + { 0xe9a5, 0x0000 }, + { 0xe9a6, 0x0000 }, + { 0xe9a7, 0x0000 }, + { 0xe9a8, 0x0000 }, + { 0xe9a9, 0x0000 }, + { 0xe9aa, 0x0000 }, + { 0xe9ab, 0x0000 }, + { 0xe9ac, 0x0000 }, + { 0xe9ad, 0x0000 }, + { 0xe9ae, 0x0000 }, + { 0xe9af, 0x0000 }, + { 0xe9b0, 0x0000 }, + { 0xe9b1, 0x0000 }, + { 0xe9b2, 0x0000 }, + { 0xe9b3, 0x0000 }, + { 0xe9b4, 0x0000 }, + { 0xe9b5, 0x0000 }, + { 0xe9b6, 0x0000 }, + { 0xe9b7, 0x0000 }, + { 0xe9b8, 0x0000 }, + { 0xe9b9, 0x0000 }, + { 0xe9ba, 0x0000 }, + { 0xe9bb, 0x0000 }, + { 0xe9bc, 0x0000 }, + { 0xe9bd, 0x0000 }, + { 0xe9be, 0x0000 }, + { 0xe9bf, 0x0000 }, + { 0xe9c0, 0x0000 }, + { 0xe9c1, 0x0000 }, + { 0xe9c2, 0x0000 }, + { 0xe9c3, 0x0000 }, + { 0xe9c4, 0x0000 }, + { 0xe9c5, 0x0000 }, + { 0xe9c6, 0x0000 }, + { 0xe9c7, 0x0000 }, + { 0xe9c8, 0x0000 }, + { 0xe9c9, 0x0000 }, + { 0xe9ca, 0x0000 }, + { 0xe9cb, 0x0000 }, + { 0xe9cc, 0x0000 }, + { 0xe9cd, 0x0000 }, + { 0xe9ce, 0x0000 }, + { 0xe9cf, 0x0000 }, + { 0xe9d0, 0x0000 }, + { 0xe9d1, 0x0000 }, + { 0xe9d2, 0x0000 }, + { 0xe9d3, 0x0000 }, + { 0xe9d4, 0x0000 }, + { 0xe9d5, 0x0000 }, + { 0xe9d6, 0x0000 }, + { 0xe9d7, 0x0000 }, + { 0xe9d8, 0x0000 }, + { 0xe9d9, 0x0000 }, + { 0xe9da, 0x0000 }, + { 0xe9db, 0x0000 }, + { 0xe9dc, 0x0000 }, + { 0xe9dd, 0x0000 }, + { 0xe9de, 0x0000 }, + { 0xe9df, 0x0000 }, + { 0xe9e0, 0x0000 }, + { 0xe9e1, 0x0000 }, + { 0xe9e2, 0x0000 }, + { 0xe9e3, 0x0000 }, + { 0xe9e4, 0x0000 }, + { 0xe9e5, 0x0000 }, + { 0xe9e6, 0x0000 }, + { 0xe9e7, 0x0000 }, + { 0xe9e8, 0x0000 }, + { 0xe9e9, 0x0000 }, + { 0xe9ea, 0x0000 }, + { 0xe9eb, 0x0000 }, + { 0xe9ec, 0x0000 }, + { 0xe9ed, 0x0000 }, + { 0xe9ee, 0x0000 }, + { 0xe9ef, 0x0000 }, + { 0xe9f0, 0x0000 }, + { 0xe9f1, 0x0000 }, + { 0xe9f2, 0x0000 }, + { 0xe9f3, 0x0000 }, + { 0xe9f4, 0x0000 }, + { 0xe9f5, 0x0000 }, + { 0xe9f6, 0x0000 }, + { 0xe9f7, 0x0000 }, + { 0xe9f8, 0x0000 }, + { 0xe9f9, 0x0000 }, + { 0xe9fa, 0x0000 }, + { 0xe9fb, 0x0000 }, + { 0xe9fc, 0x0000 }, + { 0xe9fd, 0x0000 }, + { 0xe9fe, 0x0000 }, + { 0xe9ff, 0x0000 }, + { 0xea00, 0x0000 }, + { 0xea01, 0x0000 }, + { 0xea02, 0x0000 }, + { 0xea03, 0x0000 }, + { 0xea04, 0x0000 }, + { 0xea05, 0x0000 }, + { 0xea06, 0x0000 }, + { 0xea07, 0x0000 }, + { 0xea08, 0x0000 }, + { 0xea09, 0x0000 }, + { 0xea0a, 0x0000 }, + { 0xea0b, 0x0000 }, + { 0xea0c, 0x0000 }, + { 0xea0d, 0x0000 }, + { 0xea0e, 0x0000 }, + { 0xea0f, 0x0000 }, + { 0xea10, 0x0000 }, + { 0xea11, 0x0000 }, + { 0xea12, 0x0000 }, + { 0xea13, 0x0000 }, + { 0xea14, 0x0000 }, + { 0xea15, 0x0000 }, + { 0xea16, 0x0000 }, + { 0xea17, 0x0000 }, + { 0xea18, 0x0000 }, + { 0xea19, 0x0000 }, + { 0xea1a, 0x0000 }, + { 0xea1b, 0x0000 }, + { 0xea1c, 0x0000 }, + { 0xea1d, 0x0000 }, + { 0xea1e, 0x0000 }, + { 0xea1f, 0x0000 }, + { 0xea20, 0x0000 }, + { 0xea21, 0x0000 }, + { 0xea22, 0x0000 }, + { 0xea23, 0x0000 }, + { 0xea24, 0x0000 }, + { 0xea25, 0x0000 }, + { 0xea26, 0x0000 }, + { 0xea27, 0x0000 }, + { 0xea28, 0x0000 }, + { 0xea29, 0x0000 }, + { 0xea2a, 0x0000 }, + { 0xea2b, 0x0000 }, + { 0xea2c, 0x0000 }, + { 0xea2d, 0x0000 }, + { 0xea2e, 0x0000 }, + { 0xea2f, 0x0000 }, + { 0xea30, 0x0000 }, + { 0xea31, 0x0000 }, + { 0xea32, 0x0000 }, + { 0xea33, 0x0000 }, + { 0xea34, 0x0000 }, + { 0xea35, 0x0000 }, + { 0xea36, 0x0000 }, + { 0xea37, 0x0000 }, + { 0xea38, 0x0000 }, + { 0xea39, 0x0000 }, + { 0xea3a, 0x0000 }, + { 0xea3b, 0x0000 }, + { 0xea3c, 0x0000 }, + { 0xea3d, 0x0000 }, + { 0xea3e, 0x0000 }, + { 0xea3f, 0x0000 }, + { 0xea40, 0x0000 }, + { 0xea41, 0x0000 }, + { 0xea42, 0x0000 }, + { 0xea43, 0x0000 }, + { 0xea44, 0x0000 }, + { 0xea45, 0x0000 }, + { 0xea46, 0x0000 }, + { 0xea47, 0x0000 }, + { 0xea48, 0x0000 }, + { 0xea49, 0x0000 }, + { 0xea4a, 0x0000 }, + { 0xea4b, 0x0000 }, + { 0xea4c, 0x0000 }, + { 0xea4d, 0x0000 }, + { 0xea4e, 0x0000 }, + { 0xea4f, 0x0000 }, + { 0xea50, 0x0000 }, + { 0xea51, 0x0000 }, + { 0xea52, 0x0000 }, + { 0xea53, 0x0000 }, + { 0xea54, 0x0000 }, + { 0xea55, 0x0000 }, + { 0xea56, 0x0000 }, + { 0xea57, 0x0000 }, + { 0xea58, 0x0000 }, + { 0xea59, 0x0000 }, + { 0xea5a, 0x0000 }, + { 0xea5b, 0x0000 }, + { 0xea5c, 0x0000 }, + { 0xea5d, 0x0000 }, + { 0xea5e, 0x0000 }, + { 0xea5f, 0x0000 }, + { 0xea60, 0x0000 }, + { 0xea61, 0x0000 }, + { 0xea62, 0x0000 }, + { 0xea63, 0x0000 }, + { 0xea64, 0x0000 }, + { 0xea65, 0x0000 }, + { 0xea66, 0x0000 }, + { 0xea67, 0x0000 }, + { 0xea68, 0x0000 }, + { 0xea69, 0x0000 }, + { 0xea6a, 0x0000 }, + { 0xea6b, 0x0000 }, + { 0xea6c, 0x0000 }, + { 0xea6d, 0x0000 }, + { 0xea6e, 0x0000 }, + { 0xea6f, 0x0000 }, + { 0xea70, 0x0000 }, + { 0xea71, 0x0000 }, + { 0xea72, 0x0000 }, + { 0xea73, 0x0000 }, + { 0xea74, 0x0000 }, + { 0xea75, 0x0000 }, + { 0xea76, 0x0000 }, + { 0xea77, 0x0000 }, + { 0xea78, 0x0000 }, + { 0xea79, 0x0000 }, + { 0xea7a, 0x0000 }, + { 0xea7b, 0x0000 }, + { 0xea7c, 0x0000 }, + { 0xea7d, 0x0000 }, + { 0xea7e, 0x0000 }, + { 0xea7f, 0x0000 }, + { 0xea80, 0x0000 }, + { 0xea81, 0x0000 }, + { 0xea82, 0x0000 }, + { 0xea83, 0x0000 }, + { 0xea84, 0x0000 }, + { 0xea85, 0x0000 }, + { 0xea86, 0x0000 }, + { 0xea87, 0x0000 }, + { 0xea88, 0x0000 }, + { 0xea89, 0x0000 }, + { 0xea8a, 0x0000 }, + { 0xea8b, 0x0000 }, + { 0xea8c, 0x0000 }, + { 0xea8d, 0x0000 }, + { 0xea8e, 0x0000 }, + { 0xea8f, 0x0000 }, + { 0xea90, 0x0000 }, + { 0xea91, 0x0000 }, + { 0xea92, 0x0000 }, + { 0xea93, 0x0000 }, + { 0xea94, 0x0000 }, + { 0xea95, 0x0000 }, + { 0xea96, 0x0000 }, + { 0xea97, 0x0000 }, + { 0xea98, 0x0000 }, + { 0xea99, 0x0000 }, + { 0xea9a, 0x0000 }, + { 0xea9b, 0x0000 }, + { 0xea9c, 0x0000 }, + { 0xea9d, 0x0000 }, + { 0xea9e, 0x0000 }, + { 0xea9f, 0x0000 }, + { 0xeaa0, 0x0000 }, + { 0xeaa1, 0x0000 }, + { 0xeaa2, 0x0000 }, + { 0xeaa3, 0x0000 }, + { 0xeaa4, 0x0000 }, + { 0xeaa5, 0x0000 }, + { 0xeaa6, 0x0000 }, + { 0xeaa7, 0x0000 }, + { 0xeaa8, 0x0000 }, + { 0xeaa9, 0x0000 }, + { 0xeaaa, 0x0000 }, + { 0xeaab, 0x0000 }, + { 0xeaac, 0x0000 }, + { 0xeaad, 0x0000 }, + { 0xeaae, 0x0000 }, + { 0xeaaf, 0x0000 }, + { 0xeab0, 0x0000 }, + { 0xeab1, 0x0000 }, + { 0xeab2, 0x0000 }, + { 0xeab3, 0x0000 }, + { 0xeab4, 0x0000 }, + { 0xeab5, 0x0000 }, + { 0xeab6, 0x0000 }, + { 0xeab7, 0x0000 }, + { 0xeab8, 0x0000 }, + { 0xeab9, 0x0000 }, + { 0xeaba, 0x0000 }, + { 0xeabb, 0x0000 }, + { 0xeabc, 0x0000 }, + { 0xeabd, 0x0000 }, + { 0xeabe, 0x0000 }, + { 0xeabf, 0x0000 }, + { 0xeac0, 0x0000 }, + { 0xeac1, 0x0000 }, + { 0xeac2, 0x0000 }, + { 0xeac3, 0x0000 }, + { 0xeac4, 0x0000 }, + { 0xeac5, 0x0000 }, + { 0xeac6, 0x0000 }, + { 0xeac7, 0x0000 }, + { 0xeac8, 0x0000 }, + { 0xeac9, 0x0000 }, + { 0xeaca, 0x0000 }, + { 0xeacb, 0x0000 }, + { 0xeacc, 0x0000 }, + { 0xeacd, 0x0000 }, + { 0xeace, 0x0000 }, + { 0xeacf, 0x0000 }, + { 0xead0, 0x0000 }, + { 0xead1, 0x0000 }, + { 0xead2, 0x0000 }, + { 0xead3, 0x0000 }, + { 0xead4, 0x0000 }, + { 0xead5, 0x0000 }, + { 0xead6, 0x0000 }, + { 0xead7, 0x0000 }, + { 0xead8, 0x0000 }, + { 0xead9, 0x0000 }, + { 0xeada, 0x0000 }, + { 0xeadb, 0x0000 }, + { 0xeadc, 0x0000 }, + { 0xeadd, 0x0000 }, + { 0xeade, 0x0000 }, + { 0xeadf, 0x0000 }, + { 0xeae0, 0x0000 }, + { 0xeae1, 0x0000 }, + { 0xeae2, 0x0000 }, + { 0xeae3, 0x0000 }, + { 0xeae4, 0x0000 }, + { 0xeae5, 0x0000 }, + { 0xeae6, 0x0000 }, + { 0xeae7, 0x0000 }, + { 0xeae8, 0x0000 }, + { 0xeae9, 0x0000 }, + { 0xeaea, 0x0000 }, + { 0xeaeb, 0x0000 }, + { 0xeaec, 0x0000 }, + { 0xeaed, 0x0000 }, + { 0xeaee, 0x0000 }, + { 0xeaef, 0x0000 }, + { 0xeaf0, 0x0000 }, + { 0xeaf1, 0x0000 }, + { 0xeaf2, 0x0000 }, + { 0xeaf3, 0x0000 }, + { 0xeaf4, 0x0000 }, + { 0xeaf5, 0x0000 }, + { 0xeaf6, 0x0000 }, + { 0xeaf7, 0x0000 }, + { 0xeaf8, 0x0000 }, + { 0xeaf9, 0x0000 }, + { 0xeafa, 0x0000 }, + { 0xeafb, 0x0000 }, + { 0xeafc, 0x0000 }, + { 0xeafd, 0x0000 }, + { 0xeafe, 0x0000 }, + { 0xeaff, 0x0000 }, + { 0xeb00, 0x0000 }, + { 0xeb01, 0x0000 }, + { 0xeb02, 0x0000 }, + { 0xeb03, 0x0000 }, + { 0xeb04, 0x0000 }, + { 0xeb05, 0x0000 }, + { 0xeb06, 0x0000 }, + { 0xeb07, 0x0000 }, + { 0xeb08, 0x0000 }, + { 0xeb09, 0x0000 }, + { 0xeb0a, 0x0000 }, + { 0xeb0b, 0x0000 }, + { 0xeb0c, 0x0000 }, + { 0xeb0d, 0x0000 }, + { 0xeb0e, 0x0000 }, + { 0xeb0f, 0x0000 }, + { 0xeb10, 0x0000 }, + { 0xeb11, 0x0000 }, + { 0xeb12, 0x0000 }, + { 0xeb13, 0x0000 }, + { 0xeb14, 0x0000 }, + { 0xeb15, 0x0000 }, + { 0xeb16, 0x0000 }, + { 0xeb17, 0x0000 }, + { 0xeb18, 0x0000 }, + { 0xeb19, 0x0000 }, + { 0xeb1a, 0x0000 }, + { 0xeb1b, 0x0000 }, + { 0xeb1c, 0x0000 }, + { 0xeb1d, 0x0000 }, + { 0xeb1e, 0x0000 }, + { 0xeb1f, 0x0000 }, + { 0xeb20, 0x0000 }, + { 0xeb21, 0x0000 }, + { 0xeb22, 0x0000 }, + { 0xeb23, 0x0000 }, + { 0xeb24, 0x0000 }, + { 0xeb25, 0x0000 }, + { 0xeb26, 0x0000 }, + { 0xeb27, 0x0000 }, + { 0xeb28, 0x0000 }, + { 0xeb29, 0x0000 }, + { 0xeb2a, 0x0000 }, + { 0xeb2b, 0x0000 }, + { 0xeb2c, 0x0000 }, + { 0xeb2d, 0x0000 }, + { 0xeb2e, 0x0000 }, + { 0xeb2f, 0x0000 }, + { 0xeb30, 0x0000 }, + { 0xeb31, 0x0000 }, + { 0xeb32, 0x0000 }, + { 0xeb33, 0x0000 }, + { 0xeb34, 0x0000 }, + { 0xeb35, 0x0000 }, + { 0xeb36, 0x0000 }, + { 0xeb37, 0x0000 }, + { 0xeb38, 0x0000 }, + { 0xeb39, 0x0000 }, + { 0xeb3a, 0x0000 }, + { 0xeb3b, 0x0000 }, + { 0xeb3c, 0x0000 }, + { 0xeb3d, 0x0000 }, + { 0xeb3e, 0x0000 }, + { 0xeb3f, 0x0000 }, + { 0xeb40, 0x0000 }, + { 0xeb41, 0x0000 }, + { 0xeb42, 0x0000 }, + { 0xeb43, 0x0000 }, + { 0xeb44, 0x0000 }, + { 0xeb45, 0x0000 }, + { 0xeb46, 0x0000 }, + { 0xeb47, 0x0000 }, + { 0xeb48, 0x0000 }, + { 0xeb49, 0x0000 }, + { 0xeb4a, 0x0000 }, + { 0xeb4b, 0x0000 }, + { 0xeb4c, 0x0000 }, + { 0xeb4d, 0x0000 }, + { 0xeb4e, 0x0000 }, + { 0xeb4f, 0x0000 }, + { 0xeb50, 0x0000 }, + { 0xeb51, 0x0000 }, + { 0xeb52, 0x0000 }, + { 0xeb53, 0x0000 }, + { 0xeb54, 0x0000 }, + { 0xeb55, 0x0000 }, + { 0xeb56, 0x0000 }, + { 0xeb57, 0x0000 }, + { 0xeb58, 0x0000 }, + { 0xeb59, 0x0000 }, + { 0xeb5a, 0x0000 }, + { 0xeb5b, 0x0000 }, + { 0xeb5c, 0x0000 }, + { 0xeb5d, 0x0000 }, + { 0xeb5e, 0x0000 }, + { 0xeb5f, 0x0000 }, + { 0xeb60, 0x0000 }, + { 0xeb61, 0x0000 }, + { 0xeb62, 0x0000 }, + { 0xeb63, 0x0000 }, + { 0xeb64, 0x0000 }, + { 0xeb65, 0x0000 }, + { 0xeb66, 0x0000 }, + { 0xeb67, 0x0000 }, + { 0xeb68, 0x0000 }, + { 0xeb69, 0x0000 }, + { 0xeb6a, 0x0000 }, + { 0xeb6b, 0x0000 }, + { 0xeb6c, 0x0000 }, + { 0xeb6d, 0x0000 }, + { 0xeb6e, 0x0000 }, + { 0xeb6f, 0x0000 }, + { 0xeb70, 0x0000 }, + { 0xeb71, 0x0000 }, + { 0xeb72, 0x0000 }, + { 0xeb73, 0x0000 }, + { 0xeb74, 0x0000 }, + { 0xeb75, 0x0000 }, + { 0xeb76, 0x0000 }, + { 0xeb77, 0x0000 }, + { 0xeb78, 0x0000 }, + { 0xeb79, 0x0000 }, + { 0xeb7a, 0x0000 }, + { 0xeb7b, 0x0000 }, + { 0xeb7c, 0x0000 }, + { 0xeb7d, 0x0000 }, + { 0xeb7e, 0x0000 }, + { 0xeb7f, 0x0000 }, + { 0xeb80, 0x0000 }, + { 0xeb81, 0x0000 }, + { 0xeb82, 0x0000 }, + { 0xeb83, 0x0000 }, + { 0xeb84, 0x0000 }, + { 0xeb85, 0x0000 }, + { 0xeb86, 0x0000 }, + { 0xeb87, 0x0000 }, + { 0xeb88, 0x0000 }, + { 0xeb89, 0x0000 }, + { 0xeb8a, 0x0000 }, + { 0xeb8b, 0x0000 }, + { 0xeb8c, 0x0000 }, + { 0xeb8d, 0x0000 }, + { 0xeb8e, 0x0000 }, + { 0xeb8f, 0x0000 }, + { 0xeb90, 0x0000 }, + { 0xeb91, 0x0000 }, + { 0xeb92, 0x0000 }, + { 0xeb93, 0x0000 }, + { 0xeb94, 0x0000 }, + { 0xeb95, 0x0000 }, + { 0xeb96, 0x0000 }, + { 0xeb97, 0x0000 }, + { 0xeb98, 0x0000 }, + { 0xeb99, 0x0000 }, + { 0xeb9a, 0x0000 }, + { 0xeb9b, 0x0000 }, + { 0xeb9c, 0x0000 }, + { 0xeb9d, 0x0000 }, + { 0xeb9e, 0x0000 }, + { 0xeb9f, 0x0000 }, + { 0xeba0, 0x0000 }, + { 0xeba1, 0x0000 }, + { 0xeba2, 0x0000 }, + { 0xeba3, 0x0000 }, + { 0xeba4, 0x0000 }, + { 0xeba5, 0x0000 }, + { 0xeba6, 0x0000 }, + { 0xeba7, 0x0000 }, + { 0xeba8, 0x0000 }, + { 0xeba9, 0x0000 }, + { 0xebaa, 0x0000 }, + { 0xebab, 0x0000 }, + { 0xebac, 0x0000 }, + { 0xebad, 0x0000 }, + { 0xebae, 0x0000 }, + { 0xebaf, 0x0000 }, + { 0xebb0, 0x0000 }, + { 0xebb1, 0x0000 }, + { 0xebb2, 0x0000 }, + { 0xebb3, 0x0000 }, + { 0xebb4, 0x0000 }, + { 0xebb5, 0x0000 }, + { 0xebb6, 0x0000 }, + { 0xebb7, 0x0000 }, + { 0xebb8, 0x0000 }, + { 0xebb9, 0x0000 }, + { 0xebba, 0x0000 }, + { 0xebbb, 0x0000 }, + { 0xebbc, 0x0000 }, + { 0xebbd, 0x0000 }, + { 0xebbe, 0x0000 }, + { 0xebbf, 0x0000 }, + { 0xebc0, 0x0000 }, + { 0xebc1, 0x0000 }, + { 0xebc2, 0x0000 }, + { 0xebc3, 0x0000 }, + { 0xebc4, 0x0000 }, + { 0xebc5, 0x0000 }, + { 0xebc6, 0x0000 }, + { 0xebc7, 0x0000 }, + { 0xebc8, 0x0000 }, + { 0xebc9, 0x0000 }, + { 0xebca, 0x0000 }, + { 0xebcb, 0x0000 }, + { 0xebcc, 0x0000 }, + { 0xebcd, 0x0000 }, + { 0xebce, 0x0000 }, + { 0xebcf, 0x0000 }, + { 0xebd0, 0x0000 }, + { 0xebd1, 0x0000 }, + { 0xebd2, 0x0000 }, + { 0xebd3, 0x0000 }, + { 0xebd4, 0x0000 }, + { 0xebd5, 0x0000 }, + { 0xebd6, 0x0000 }, + { 0xebd7, 0x0000 }, + { 0xebd8, 0x0000 }, + { 0xebd9, 0x0000 }, + { 0xebda, 0x0000 }, + { 0xebdb, 0x0000 }, + { 0xebdc, 0x0000 }, + { 0xebdd, 0x0000 }, + { 0xebde, 0x0000 }, + { 0xebdf, 0x0000 }, + { 0xebe0, 0x0000 }, + { 0xebe1, 0x0000 }, + { 0xebe2, 0x0000 }, + { 0xebe3, 0x0000 }, + { 0xebe4, 0x0000 }, + { 0xebe5, 0x0000 }, + { 0xebe6, 0x0000 }, + { 0xebe7, 0x0000 }, + { 0xebe8, 0x0000 }, + { 0xebe9, 0x0000 }, + { 0xebea, 0x0000 }, + { 0xebeb, 0x0000 }, + { 0xebec, 0x0000 }, + { 0xebed, 0x0000 }, + { 0xebee, 0x0000 }, + { 0xebef, 0x0000 }, + { 0xebf0, 0x0000 }, + { 0xebf1, 0x0000 }, + { 0xebf2, 0x0000 }, + { 0xebf3, 0x0000 }, + { 0xebf4, 0x0000 }, + { 0xebf5, 0x0000 }, + { 0xebf6, 0x0000 }, + { 0xebf7, 0x0000 }, + { 0xebf8, 0x0000 }, + { 0xebf9, 0x0000 }, + { 0xebfa, 0x0000 }, + { 0xebfb, 0x0000 }, + { 0xebfc, 0x0000 }, + { 0xebfd, 0x0000 }, + { 0xebfe, 0x0000 }, + { 0xebff, 0x0000 }, + { 0xec00, 0x0000 }, + { 0xec01, 0x0000 }, + { 0xec02, 0x0000 }, + { 0xec03, 0x0000 }, + { 0xec04, 0x0000 }, + { 0xec05, 0x0000 }, + { 0xec06, 0x0000 }, + { 0xec07, 0x0000 }, + { 0xec08, 0x0000 }, + { 0xec09, 0x0000 }, + { 0xec0a, 0x0000 }, + { 0xec0b, 0x0000 }, + { 0xec0c, 0x0000 }, + { 0xec0d, 0x0000 }, + { 0xec0e, 0x0000 }, + { 0xec0f, 0x0000 }, + { 0xec10, 0x0000 }, + { 0xec11, 0x0000 }, + { 0xec12, 0x0000 }, + { 0xec13, 0x0000 }, + { 0xec14, 0x0000 }, + { 0xec15, 0x0000 }, + { 0xec16, 0x0000 }, + { 0xec17, 0x0000 }, + { 0xec18, 0x0000 }, + { 0xec19, 0x0000 }, + { 0xec1a, 0x0000 }, + { 0xec1b, 0x0000 }, + { 0xec1c, 0x0000 }, + { 0xec1d, 0x0000 }, + { 0xec1e, 0x0000 }, + { 0xec1f, 0x0000 }, + { 0xec20, 0x0000 }, + { 0xec21, 0x0000 }, + { 0xec22, 0x0000 }, + { 0xec23, 0x0000 }, + { 0xec24, 0x0000 }, + { 0xec25, 0x0000 }, + { 0xec26, 0x0000 }, + { 0xec27, 0x0000 }, + { 0xec28, 0x0000 }, + { 0xec29, 0x0000 }, + { 0xec2a, 0x0000 }, + { 0xec2b, 0x0000 }, + { 0xec2c, 0x0000 }, + { 0xec2d, 0x0000 }, + { 0xec2e, 0x0000 }, + { 0xec2f, 0x0000 }, + { 0xec30, 0x0000 }, + { 0xec31, 0x0000 }, + { 0xec32, 0x0000 }, + { 0xec33, 0x0000 }, + { 0xec34, 0x0000 }, + { 0xec35, 0x0000 }, + { 0xec36, 0x0000 }, + { 0xec37, 0x0000 }, + { 0xec38, 0x0000 }, + { 0xec39, 0x0000 }, + { 0xec3a, 0x0000 }, + { 0xec3b, 0x0000 }, + { 0xec3c, 0x0000 }, + { 0xec3d, 0x0000 }, + { 0xec3e, 0x0000 }, + { 0xec3f, 0x0000 }, + { 0xec40, 0x0000 }, + { 0xec41, 0x0000 }, + { 0xec42, 0x0000 }, + { 0xec43, 0x0000 }, + { 0xec44, 0x0000 }, + { 0xec45, 0x0000 }, + { 0xec46, 0x0000 }, + { 0xec47, 0x0000 }, + { 0xec48, 0x0000 }, + { 0xec49, 0x0000 }, + { 0xec4a, 0x0000 }, + { 0xec4b, 0x0000 }, + { 0xec4c, 0x0000 }, + { 0xec4d, 0x0000 }, + { 0xec4e, 0x0000 }, + { 0xec4f, 0x0000 }, + { 0xec50, 0x0000 }, + { 0xec51, 0x0000 }, + { 0xec52, 0x0000 }, + { 0xec53, 0x0000 }, + { 0xec54, 0x0000 }, + { 0xec55, 0x0000 }, + { 0xec56, 0x0000 }, + { 0xec57, 0x0000 }, + { 0xec58, 0x0000 }, + { 0xec59, 0x0000 }, + { 0xec5a, 0x0000 }, + { 0xec5b, 0x0000 }, + { 0xec5c, 0x0000 }, + { 0xec5d, 0x0000 }, + { 0xec5e, 0x0000 }, + { 0xec5f, 0x0000 }, + { 0xec60, 0x0000 }, + { 0xec61, 0x0000 }, + { 0xec62, 0x0000 }, + { 0xec63, 0x0000 }, + { 0xec64, 0x0000 }, + { 0xec65, 0x0000 }, + { 0xec66, 0x0000 }, + { 0xec67, 0x0000 }, + { 0xec68, 0x0000 }, + { 0xec69, 0x0000 }, + { 0xec6a, 0x0000 }, + { 0xec6b, 0x0000 }, + { 0xec6c, 0x0000 }, + { 0xec6d, 0x0000 }, + { 0xec6e, 0x0000 }, + { 0xec6f, 0x0000 }, + { 0xec70, 0x0000 }, + { 0xec71, 0x0000 }, + { 0xec72, 0x0000 }, + { 0xec73, 0x0000 }, + { 0xec74, 0x0000 }, + { 0xec75, 0x0000 }, + { 0xec76, 0x0000 }, + { 0xec77, 0x0000 }, + { 0xec78, 0x0000 }, + { 0xec79, 0x0000 }, + { 0xec7a, 0x0000 }, + { 0xec7b, 0x0000 }, + { 0xec7c, 0x0000 }, + { 0xec7d, 0x0000 }, + { 0xec7e, 0x0000 }, + { 0xec7f, 0x0000 }, + { 0xec80, 0x0000 }, + { 0xec81, 0x0000 }, + { 0xec82, 0x0000 }, + { 0xec83, 0x0000 }, + { 0xec84, 0x0000 }, + { 0xec85, 0x0000 }, + { 0xec86, 0x0000 }, + { 0xec87, 0x0000 }, + { 0xec88, 0x0000 }, + { 0xec89, 0x0000 }, + { 0xec8a, 0x0000 }, + { 0xec8b, 0x0000 }, + { 0xec8c, 0x0000 }, + { 0xec8d, 0x0000 }, + { 0xec8e, 0x0000 }, + { 0xec8f, 0x0000 }, + { 0xec90, 0x0000 }, + { 0xec91, 0x0000 }, + { 0xec92, 0x0000 }, + { 0xec93, 0x0000 }, + { 0xec94, 0x0000 }, + { 0xec95, 0x0000 }, + { 0xec96, 0x0000 }, + { 0xec97, 0x0000 }, + { 0xec98, 0x0000 }, + { 0xec99, 0x0000 }, + { 0xec9a, 0x0000 }, + { 0xec9b, 0x0000 }, + { 0xec9c, 0x0000 }, + { 0xec9d, 0x0000 }, + { 0xec9e, 0x0000 }, + { 0xec9f, 0x0000 }, + { 0xeca0, 0x0000 }, + { 0xeca1, 0x0000 }, + { 0xeca2, 0x0000 }, + { 0xeca3, 0x0000 }, + { 0xeca4, 0x0000 }, + { 0xeca5, 0x0000 }, + { 0xeca6, 0x0000 }, + { 0xeca7, 0x0000 }, + { 0xeca8, 0x0000 }, + { 0xeca9, 0x0000 }, + { 0xecaa, 0x0000 }, + { 0xecab, 0x0000 }, + { 0xecac, 0x0000 }, + { 0xecad, 0x0000 }, + { 0xecae, 0x0000 }, + { 0xecaf, 0x0000 }, + { 0xecb0, 0x0000 }, + { 0xecb1, 0x0000 }, + { 0xecb2, 0x0000 }, + { 0xecb3, 0x0000 }, + { 0xecb4, 0x0000 }, + { 0xecb5, 0x0000 }, + { 0xecb6, 0x0000 }, + { 0xecb7, 0x0000 }, + { 0xecb8, 0x0000 }, + { 0xecb9, 0x0000 }, + { 0xecba, 0x0000 }, + { 0xecbb, 0x0000 }, + { 0xecbc, 0x0000 }, + { 0xecbd, 0x0000 }, + { 0xecbe, 0x0000 }, + { 0xecbf, 0x0000 }, + { 0xecc0, 0x0000 }, + { 0xecc1, 0x0000 }, + { 0xecc2, 0x0000 }, + { 0xecc3, 0x0000 }, + { 0xecc4, 0x0000 }, + { 0xecc5, 0x0000 }, + { 0xecc6, 0x0000 }, + { 0xecc7, 0x0000 }, + { 0xecc8, 0x0000 }, + { 0xecc9, 0x0000 }, + { 0xecca, 0x0000 }, + { 0xeccb, 0x0000 }, + { 0xeccc, 0x0000 }, + { 0xeccd, 0x0000 }, + { 0xecce, 0x0000 }, + { 0xeccf, 0x0000 }, + { 0xecd0, 0x0000 }, + { 0xecd1, 0x0000 }, + { 0xecd2, 0x0000 }, + { 0xecd3, 0x0000 }, + { 0xecd4, 0x0000 }, + { 0xecd5, 0x0000 }, + { 0xecd6, 0x0000 }, + { 0xecd7, 0x0000 }, + { 0xecd8, 0x0000 }, + { 0xecd9, 0x0000 }, + { 0xecda, 0x0000 }, + { 0xecdb, 0x0000 }, + { 0xecdc, 0x0000 }, + { 0xecdd, 0x0000 }, + { 0xecde, 0x0000 }, + { 0xecdf, 0x0000 }, + { 0xece0, 0x0000 }, + { 0xece1, 0x0000 }, + { 0xece2, 0x0000 }, + { 0xece3, 0x0000 }, + { 0xece4, 0x0000 }, + { 0xece5, 0x0000 }, + { 0xece6, 0x0000 }, + { 0xece7, 0x0000 }, + { 0xece8, 0x0000 }, + { 0xece9, 0x0000 }, + { 0xecea, 0x0000 }, + { 0xeceb, 0x0000 }, + { 0xecec, 0x0000 }, + { 0xeced, 0x0000 }, + { 0xecee, 0x0000 }, + { 0xecef, 0x0000 }, + { 0xecf0, 0x0000 }, + { 0xecf1, 0x0000 }, + { 0xecf2, 0x0000 }, + { 0xecf3, 0x0000 }, + { 0xecf4, 0x0000 }, + { 0xecf5, 0x0000 }, + { 0xecf6, 0x0000 }, + { 0xecf7, 0x0000 }, + { 0xecf8, 0x0000 }, + { 0xecf9, 0x0000 }, + { 0xecfa, 0x0000 }, + { 0xecfb, 0x0000 }, + { 0xecfc, 0x0000 }, + { 0xecfd, 0x0000 }, + { 0xecfe, 0x0000 }, + { 0xecff, 0x0000 }, + { 0xed00, 0x0000 }, + { 0xed01, 0x0000 }, + { 0xed02, 0x0000 }, + { 0xed03, 0x0000 }, + { 0xed04, 0x0000 }, + { 0xed05, 0x0000 }, + { 0xed06, 0x0000 }, + { 0xed07, 0x0000 }, + { 0xed08, 0x0000 }, + { 0xed09, 0x0000 }, + { 0xed0a, 0x0000 }, + { 0xed0b, 0x0000 }, + { 0xed0c, 0x0000 }, + { 0xed0d, 0x0000 }, + { 0xed0e, 0x0000 }, + { 0xed0f, 0x0000 }, + { 0xed10, 0x0000 }, + { 0xed11, 0x0000 }, + { 0xed12, 0x0000 }, + { 0xed13, 0x0000 }, + { 0xed14, 0x0000 }, + { 0xed15, 0x0000 }, + { 0xed16, 0x0000 }, + { 0xed17, 0x0000 }, + { 0xed18, 0x0000 }, + { 0xed19, 0x0000 }, + { 0xed1a, 0x0000 }, + { 0xed1b, 0x0000 }, + { 0xed1c, 0x0000 }, + { 0xed1d, 0x0000 }, + { 0xed1e, 0x0000 }, + { 0xed1f, 0x0000 }, + { 0xed20, 0x0000 }, + { 0xed21, 0x0000 }, + { 0xed22, 0x0000 }, + { 0xed23, 0x0000 }, + { 0xed24, 0x0000 }, + { 0xed25, 0x0000 }, + { 0xed26, 0x0000 }, + { 0xed27, 0x0000 }, + { 0xed28, 0x0000 }, + { 0xed29, 0x0000 }, + { 0xed2a, 0x0000 }, + { 0xed2b, 0x0000 }, + { 0xed2c, 0x0000 }, + { 0xed2d, 0x0000 }, + { 0xed2e, 0x0000 }, + { 0xed2f, 0x0000 }, + { 0xed30, 0x0000 }, + { 0xed31, 0x0000 }, + { 0xed32, 0x0000 }, + { 0xed33, 0x0000 }, + { 0xed34, 0x0000 }, + { 0xed35, 0x0000 }, + { 0xed36, 0x0000 }, + { 0xed37, 0x0000 }, + { 0xed38, 0x0000 }, + { 0xed39, 0x0000 }, + { 0xed3a, 0x0000 }, + { 0xed3b, 0x0000 }, + { 0xed3c, 0x0000 }, + { 0xed3d, 0x0000 }, + { 0xed3e, 0x0000 }, + { 0xed3f, 0x0000 }, + { 0xed40, 0x0000 }, + { 0xed41, 0x0000 }, + { 0xed42, 0x0000 }, + { 0xed43, 0x0000 }, + { 0xed44, 0x0000 }, + { 0xed45, 0x0000 }, + { 0xed46, 0x0000 }, + { 0xed47, 0x0000 }, + { 0xed48, 0x0000 }, + { 0xed49, 0x0000 }, + { 0xed4a, 0x0000 }, + { 0xed4b, 0x0000 }, + { 0xed4c, 0x0000 }, + { 0xed4d, 0x0000 }, + { 0xed4e, 0x0000 }, + { 0xed4f, 0x0000 }, + { 0xed50, 0x0000 }, + { 0xed51, 0x0000 }, + { 0xed52, 0x0000 }, + { 0xed53, 0x0000 }, + { 0xed54, 0x0000 }, + { 0xed55, 0x0000 }, + { 0xed56, 0x0000 }, + { 0xed57, 0x0000 }, + { 0xed58, 0x0000 }, + { 0xed59, 0x0000 }, + { 0xed5a, 0x0000 }, + { 0xed5b, 0x0000 }, + { 0xed5c, 0x0000 }, + { 0xed5d, 0x0000 }, + { 0xed5e, 0x0000 }, + { 0xed5f, 0x0000 }, + { 0xed60, 0x0000 }, + { 0xed61, 0x0000 }, + { 0xed62, 0x0000 }, + { 0xed63, 0x0000 }, + { 0xed64, 0x0000 }, + { 0xed65, 0x0000 }, + { 0xed66, 0x0000 }, + { 0xed67, 0x0000 }, + { 0xed68, 0x0000 }, + { 0xed69, 0x0000 }, + { 0xed6a, 0x0000 }, + { 0xed6b, 0x0000 }, + { 0xed6c, 0x0000 }, + { 0xed6d, 0x0000 }, + { 0xed6e, 0x0000 }, + { 0xed6f, 0x0000 }, + { 0xed70, 0x0000 }, + { 0xed71, 0x0000 }, + { 0xed72, 0x0000 }, + { 0xed73, 0x0000 }, + { 0xed74, 0x0000 }, + { 0xed75, 0x0000 }, + { 0xed76, 0x0000 }, + { 0xed77, 0x0000 }, + { 0xed78, 0x0000 }, + { 0xed79, 0x0000 }, + { 0xed7a, 0x0000 }, + { 0xed7b, 0x0000 }, + { 0xed7c, 0x0000 }, + { 0xed7d, 0x0000 }, + { 0xed7e, 0x0000 }, + { 0xed7f, 0x0000 }, + { 0xed80, 0x0000 }, + { 0xed81, 0x0000 }, + { 0xed82, 0x0000 }, + { 0xed83, 0x0000 }, + { 0xed84, 0x0000 }, + { 0xed85, 0x0000 }, + { 0xed86, 0x0000 }, + { 0xed87, 0x0000 }, + { 0xed88, 0x0000 }, + { 0xed89, 0x0000 }, + { 0xed8a, 0x0000 }, + { 0xed8b, 0x0000 }, + { 0xed8c, 0x0000 }, + { 0xed8d, 0x0000 }, + { 0xed8e, 0x0000 }, + { 0xed8f, 0x0000 }, + { 0xed90, 0x0000 }, + { 0xed91, 0x0000 }, + { 0xed92, 0x0000 }, + { 0xed93, 0x0000 }, + { 0xed94, 0x0000 }, + { 0xed95, 0x0000 }, + { 0xed96, 0x0000 }, + { 0xed97, 0x0000 }, + { 0xed98, 0x0000 }, + { 0xed99, 0x0000 }, + { 0xed9a, 0x0000 }, + { 0xed9b, 0x0000 }, + { 0xed9c, 0x0000 }, + { 0xed9d, 0x0000 }, + { 0xed9e, 0x0000 }, + { 0xed9f, 0x0000 }, + { 0xeda0, 0x0000 }, + { 0xeda1, 0x0000 }, + { 0xeda2, 0x0000 }, + { 0xeda3, 0x0000 }, + { 0xeda4, 0x0000 }, + { 0xeda5, 0x0000 }, + { 0xeda6, 0x0000 }, + { 0xeda7, 0x0000 }, + { 0xeda8, 0x0000 }, + { 0xeda9, 0x0000 }, + { 0xedaa, 0x0000 }, + { 0xedab, 0x0000 }, + { 0xedac, 0x0000 }, + { 0xedad, 0x0000 }, + { 0xedae, 0x0000 }, + { 0xedaf, 0x0000 }, + { 0xedb0, 0x0000 }, + { 0xedb1, 0x0000 }, + { 0xedb2, 0x0000 }, + { 0xedb3, 0x0000 }, + { 0xedb4, 0x0000 }, + { 0xedb5, 0x0000 }, + { 0xedb6, 0x0000 }, + { 0xedb7, 0x0000 }, + { 0xedb8, 0x0000 }, + { 0xedb9, 0x0000 }, + { 0xedba, 0x0000 }, + { 0xedbb, 0x0000 }, + { 0xedbc, 0x0000 }, + { 0xedbd, 0x0000 }, + { 0xedbe, 0x0000 }, + { 0xedbf, 0x0000 }, + { 0xedc0, 0x0000 }, + { 0xedc1, 0x0000 }, + { 0xedc2, 0x0000 }, + { 0xedc3, 0x0000 }, + { 0xedc4, 0x0000 }, + { 0xedc5, 0x0000 }, + { 0xedc6, 0x0000 }, + { 0xedc7, 0x0000 }, + { 0xedc8, 0x0000 }, + { 0xedc9, 0x0000 }, + { 0xedca, 0x0000 }, + { 0xedcb, 0x0000 }, + { 0xedcc, 0x0000 }, + { 0xedcd, 0x0000 }, + { 0xedce, 0x0000 }, + { 0xedcf, 0x0000 }, + { 0xedd0, 0x0000 }, + { 0xedd1, 0x0000 }, + { 0xedd2, 0x0000 }, + { 0xedd3, 0x0000 }, + { 0xedd4, 0x0000 }, + { 0xedd5, 0x0000 }, + { 0xedd6, 0x0000 }, + { 0xedd7, 0x0000 }, + { 0xedd8, 0x0000 }, + { 0xedd9, 0x0000 }, + { 0xedda, 0x0000 }, + { 0xeddb, 0x0000 }, + { 0xeddc, 0x0000 }, + { 0xeddd, 0x0000 }, + { 0xedde, 0x0000 }, + { 0xeddf, 0x0000 }, + { 0xede0, 0x0000 }, + { 0xede1, 0x0000 }, + { 0xede2, 0x0000 }, + { 0xede3, 0x0000 }, + { 0xede4, 0x0000 }, + { 0xede5, 0x0000 }, + { 0xede6, 0x0000 }, + { 0xede7, 0x0000 }, + { 0xede8, 0x0000 }, + { 0xede9, 0x0000 }, + { 0xedea, 0x0000 }, + { 0xedeb, 0x0000 }, + { 0xedec, 0x0000 }, + { 0xeded, 0x0000 }, + { 0xedee, 0x0000 }, + { 0xedef, 0x0000 }, + { 0xedf0, 0x0000 }, + { 0xedf1, 0x0000 }, + { 0xedf2, 0x0000 }, + { 0xedf3, 0x0000 }, + { 0xedf4, 0x0000 }, + { 0xedf5, 0x0000 }, + { 0xedf6, 0x0000 }, + { 0xedf7, 0x0000 }, + { 0xedf8, 0x0000 }, + { 0xedf9, 0x0000 }, + { 0xedfa, 0x0000 }, + { 0xedfb, 0x0000 }, + { 0xedfc, 0x0000 }, + { 0xedfd, 0x0000 }, + { 0xedfe, 0x0000 }, + { 0xedff, 0x0000 }, + { 0xee00, 0x0000 }, + { 0xee01, 0x0000 }, + { 0xee02, 0x0000 }, + { 0xee03, 0x0000 }, + { 0xee04, 0x0000 }, + { 0xee05, 0x0000 }, + { 0xee06, 0x0000 }, + { 0xee07, 0x0000 }, + { 0xee08, 0x0000 }, + { 0xee09, 0x0000 }, + { 0xee0a, 0x0000 }, + { 0xee0b, 0x0000 }, + { 0xee0c, 0x0000 }, + { 0xee0d, 0x0000 }, + { 0xee0e, 0x0000 }, + { 0xee0f, 0x0000 }, + { 0xee10, 0x0000 }, + { 0xee11, 0x0000 }, + { 0xee12, 0x0000 }, + { 0xee13, 0x0000 }, + { 0xee14, 0x0000 }, + { 0xee15, 0x0000 }, + { 0xee16, 0x0000 }, + { 0xee17, 0x0000 }, + { 0xee18, 0x0000 }, + { 0xee19, 0x0000 }, + { 0xee1a, 0x0000 }, + { 0xee1b, 0x0000 }, + { 0xee1c, 0x0000 }, + { 0xee1d, 0x0000 }, + { 0xee1e, 0x0000 }, + { 0xee1f, 0x0000 }, + { 0xee20, 0x0000 }, + { 0xee21, 0x0000 }, + { 0xee22, 0x0000 }, + { 0xee23, 0x0000 }, + { 0xee24, 0x0000 }, + { 0xee25, 0x0000 }, + { 0xee26, 0x0000 }, + { 0xee27, 0x0000 }, + { 0xee28, 0x0000 }, + { 0xee29, 0x0000 }, + { 0xee2a, 0x0000 }, + { 0xee2b, 0x0000 }, + { 0xee2c, 0x0000 }, + { 0xee2d, 0x0000 }, + { 0xee2e, 0x0000 }, + { 0xee2f, 0x0000 }, + { 0xee30, 0x0000 }, + { 0xee31, 0x0000 }, + { 0xee32, 0x0000 }, + { 0xee33, 0x0000 }, + { 0xee34, 0x0000 }, + { 0xee35, 0x0000 }, + { 0xee36, 0x0000 }, + { 0xee37, 0x0000 }, + { 0xee38, 0x0000 }, + { 0xee39, 0x0000 }, + { 0xee3a, 0x0000 }, + { 0xee3b, 0x0000 }, + { 0xee3c, 0x0000 }, + { 0xee3d, 0x0000 }, + { 0xee3e, 0x0000 }, + { 0xee3f, 0x0000 }, + { 0xee40, 0x0000 }, + { 0xee41, 0x0000 }, + { 0xee42, 0x0000 }, + { 0xee43, 0x0000 }, + { 0xee44, 0x0000 }, + { 0xee45, 0x0000 }, + { 0xee46, 0x0000 }, + { 0xee47, 0x0000 }, + { 0xee48, 0x0000 }, + { 0xee49, 0x0000 }, + { 0xee4a, 0x0000 }, + { 0xee4b, 0x0000 }, + { 0xee4c, 0x0000 }, + { 0xee4d, 0x0000 }, + { 0xee4e, 0x0000 }, + { 0xee4f, 0x0000 }, + { 0xee50, 0x0000 }, + { 0xee51, 0x0000 }, + { 0xee52, 0x0000 }, + { 0xee53, 0x0000 }, + { 0xee54, 0x0000 }, + { 0xee55, 0x0000 }, + { 0xee56, 0x0000 }, + { 0xee57, 0x0000 }, + { 0xee58, 0x0000 }, + { 0xee59, 0x0000 }, + { 0xee5a, 0x0000 }, + { 0xee5b, 0x0000 }, + { 0xee5c, 0x0000 }, + { 0xee5d, 0x0000 }, + { 0xee5e, 0x0000 }, + { 0xee5f, 0x0000 }, + { 0xee60, 0x0000 }, + { 0xee61, 0x0000 }, + { 0xee62, 0x0000 }, + { 0xee63, 0x0000 }, + { 0xee64, 0x0000 }, + { 0xee65, 0x0000 }, + { 0xee66, 0x0000 }, + { 0xee67, 0x0000 }, + { 0xee68, 0x0000 }, + { 0xee69, 0x0000 }, + { 0xee6a, 0x0000 }, + { 0xee6b, 0x0000 }, + { 0xee6c, 0x0000 }, + { 0xee6d, 0x0000 }, + { 0xee6e, 0x0000 }, + { 0xee6f, 0x0000 }, + { 0xee70, 0x0000 }, + { 0xee71, 0x0000 }, + { 0xee72, 0x0000 }, + { 0xee73, 0x0000 }, + { 0xee74, 0x0000 }, + { 0xee75, 0x0000 }, + { 0xee76, 0x0000 }, + { 0xee77, 0x0000 }, + { 0xee78, 0x0000 }, + { 0xee79, 0x0000 }, + { 0xee7a, 0x0000 }, + { 0xee7b, 0x0000 }, + { 0xee7c, 0x0000 }, + { 0xee7d, 0x0000 }, + { 0xee7e, 0x0000 }, + { 0xee7f, 0x0000 }, + { 0xee80, 0x0000 }, + { 0xee81, 0x0000 }, + { 0xee82, 0x0000 }, + { 0xee83, 0x0000 }, + { 0xee84, 0x0000 }, + { 0xee85, 0x0000 }, + { 0xee86, 0x0000 }, + { 0xee87, 0x0000 }, + { 0xee88, 0x0000 }, + { 0xee89, 0x0000 }, + { 0xee8a, 0x0000 }, + { 0xee8b, 0x0000 }, + { 0xee8c, 0x0000 }, + { 0xee8d, 0x0000 }, + { 0xee8e, 0x0000 }, + { 0xee8f, 0x0000 }, + { 0xee90, 0x0000 }, + { 0xee91, 0x0000 }, + { 0xee92, 0x0000 }, + { 0xee93, 0x0000 }, + { 0xee94, 0x0000 }, + { 0xee95, 0x0000 }, + { 0xee96, 0x0000 }, + { 0xee97, 0x0000 }, + { 0xee98, 0x0000 }, + { 0xee99, 0x0000 }, + { 0xee9a, 0x0000 }, + { 0xee9b, 0x0000 }, + { 0xee9c, 0x0000 }, + { 0xee9d, 0x0000 }, + { 0xee9e, 0x0000 }, + { 0xee9f, 0x0000 }, + { 0xeea0, 0x0000 }, + { 0xeea1, 0x0000 }, + { 0xeea2, 0x0000 }, + { 0xeea3, 0x0000 }, + { 0xeea4, 0x0000 }, + { 0xeea5, 0x0000 }, + { 0xeea6, 0x0000 }, + { 0xeea7, 0x0000 }, + { 0xeea8, 0x0000 }, + { 0xeea9, 0x0000 }, + { 0xeeaa, 0x0000 }, + { 0xeeab, 0x0000 }, + { 0xeeac, 0x0000 }, + { 0xeead, 0x0000 }, + { 0xeeae, 0x0000 }, + { 0xeeaf, 0x0000 }, + { 0xeeb0, 0x0000 }, + { 0xeeb1, 0x0000 }, + { 0xeeb2, 0x0000 }, + { 0xeeb3, 0x0000 }, + { 0xeeb4, 0x0000 }, + { 0xeeb5, 0x0000 }, + { 0xeeb6, 0x0000 }, + { 0xeeb7, 0x0000 }, + { 0xeeb8, 0x0000 }, + { 0xeeb9, 0x0000 }, + { 0xeeba, 0x0000 }, + { 0xeebb, 0x0000 }, + { 0xeebc, 0x0000 }, + { 0xeebd, 0x0000 }, + { 0xeebe, 0x0000 }, + { 0xeebf, 0x0000 }, + { 0xeec0, 0x0000 }, + { 0xeec1, 0x0000 }, + { 0xeec2, 0x0000 }, + { 0xeec3, 0x0000 }, + { 0xeec4, 0x0000 }, + { 0xeec5, 0x0000 }, + { 0xeec6, 0x0000 }, + { 0xeec7, 0x0000 }, + { 0xeec8, 0x0000 }, + { 0xeec9, 0x0000 }, + { 0xeeca, 0x0000 }, + { 0xeecb, 0x0000 }, + { 0xeecc, 0x0000 }, + { 0xeecd, 0x0000 }, + { 0xeece, 0x0000 }, + { 0xeecf, 0x0000 }, + { 0xeed0, 0x0000 }, + { 0xeed1, 0x0000 }, + { 0xeed2, 0x0000 }, + { 0xeed3, 0x0000 }, + { 0xeed4, 0x0000 }, + { 0xeed5, 0x0000 }, + { 0xeed6, 0x0000 }, + { 0xeed7, 0x0000 }, + { 0xeed8, 0x0000 }, + { 0xeed9, 0x0000 }, + { 0xeeda, 0x0000 }, + { 0xeedb, 0x0000 }, + { 0xeedc, 0x0000 }, + { 0xeedd, 0x0000 }, + { 0xeede, 0x0000 }, + { 0xeedf, 0x0000 }, + { 0xeee0, 0x0000 }, + { 0xeee1, 0x0000 }, + { 0xeee2, 0x0000 }, + { 0xeee3, 0x0000 }, + { 0xeee4, 0x0000 }, + { 0xeee5, 0x0000 }, + { 0xeee6, 0x0000 }, + { 0xeee7, 0x0000 }, + { 0xeee8, 0x0000 }, + { 0xeee9, 0x0000 }, + { 0xeeea, 0x0000 }, + { 0xeeeb, 0x0000 }, + { 0xeeec, 0x0000 }, + { 0xeeed, 0x0000 }, + { 0xeeee, 0x0000 }, + { 0xeeef, 0x0000 }, + { 0xeef0, 0x0000 }, + { 0xeef1, 0x0000 }, + { 0xeef2, 0x0000 }, + { 0xeef3, 0x0000 }, + { 0xeef4, 0x0000 }, + { 0xeef5, 0x0000 }, + { 0xeef6, 0x0000 }, + { 0xeef7, 0x0000 }, + { 0xeef8, 0x0000 }, + { 0xeef9, 0x0000 }, + { 0xeefa, 0x0000 }, + { 0xeefb, 0x0000 }, + { 0xeefc, 0x0000 }, + { 0xeefd, 0x0000 }, + { 0xeefe, 0x0000 }, + { 0xeeff, 0x0000 }, + { 0xef00, 0x0000 }, + { 0xef01, 0x0000 }, + { 0xef02, 0x0000 }, + { 0xef03, 0x0000 }, + { 0xef04, 0x0000 }, + { 0xef05, 0x0000 }, + { 0xef06, 0x0000 }, + { 0xef07, 0x0000 }, + { 0xef08, 0x0000 }, + { 0xef09, 0x0000 }, + { 0xef0a, 0x0000 }, + { 0xef0b, 0x0000 }, + { 0xef0c, 0x0000 }, + { 0xef0d, 0x0000 }, + { 0xef0e, 0x0000 }, + { 0xef0f, 0x0000 }, + { 0xef10, 0x0000 }, + { 0xef11, 0x0000 }, + { 0xef12, 0x0000 }, + { 0xef13, 0x0000 }, + { 0xef14, 0x0000 }, + { 0xef15, 0x0000 }, + { 0xef16, 0x0000 }, + { 0xef17, 0x0000 }, + { 0xef18, 0x0000 }, + { 0xef19, 0x0000 }, + { 0xef1a, 0x0000 }, + { 0xef1b, 0x0000 }, + { 0xef1c, 0x0000 }, + { 0xef1d, 0x0000 }, + { 0xef1e, 0x0000 }, + { 0xef1f, 0x0000 }, + { 0xef20, 0x0000 }, + { 0xef21, 0x0000 }, + { 0xef22, 0x0000 }, + { 0xef23, 0x0000 }, + { 0xef24, 0x0000 }, + { 0xef25, 0x0000 }, + { 0xef26, 0x0000 }, + { 0xef27, 0x0000 }, + { 0xef28, 0x0000 }, + { 0xef29, 0x0000 }, + { 0xef2a, 0x0000 }, + { 0xef2b, 0x0000 }, + { 0xef2c, 0x0000 }, + { 0xef2d, 0x0000 }, + { 0xef2e, 0x0000 }, + { 0xef2f, 0x0000 }, + { 0xef30, 0x0000 }, + { 0xef31, 0x0000 }, + { 0xef32, 0x0000 }, + { 0xef33, 0x0000 }, + { 0xef34, 0x0000 }, + { 0xef35, 0x0000 }, + { 0xef36, 0x0000 }, + { 0xef37, 0x0000 }, + { 0xef38, 0x0000 }, + { 0xef39, 0x0000 }, + { 0xef3a, 0x0000 }, + { 0xef3b, 0x0000 }, + { 0xef3c, 0x0000 }, + { 0xef3d, 0x0000 }, + { 0xef3e, 0x0000 }, + { 0xef3f, 0x0000 }, + { 0xef40, 0x0000 }, + { 0xef41, 0x0000 }, + { 0xef42, 0x0000 }, + { 0xef43, 0x0000 }, + { 0xef44, 0x0000 }, + { 0xef45, 0x0000 }, + { 0xef46, 0x0000 }, + { 0xef47, 0x0000 }, + { 0xef48, 0x0000 }, + { 0xef49, 0x0000 }, + { 0xef4a, 0x0000 }, + { 0xef4b, 0x0000 }, + { 0xef4c, 0x0000 }, + { 0xef4d, 0x0000 }, + { 0xef4e, 0x0000 }, + { 0xef4f, 0x0000 }, + { 0xef50, 0x0000 }, + { 0xef51, 0x0000 }, + { 0xef52, 0x0000 }, + { 0xef53, 0x0000 }, + { 0xef54, 0x0000 }, + { 0xef55, 0x0000 }, + { 0xef56, 0x0000 }, + { 0xef57, 0x0000 }, + { 0xef58, 0x0000 }, + { 0xef59, 0x0000 }, + { 0xef5a, 0x0000 }, + { 0xef5b, 0x0000 }, + { 0xef5c, 0x0000 }, + { 0xef5d, 0x0000 }, + { 0xef5e, 0x0000 }, + { 0xef5f, 0x0000 }, + { 0xef60, 0x0000 }, + { 0xef61, 0x0000 }, + { 0xef62, 0x0000 }, + { 0xef63, 0x0000 }, + { 0xef64, 0x0000 }, + { 0xef65, 0x0000 }, + { 0xef66, 0x0000 }, + { 0xef67, 0x0000 }, + { 0xef68, 0x0000 }, + { 0xef69, 0x0000 }, + { 0xef6a, 0x0000 }, + { 0xef6b, 0x0000 }, + { 0xef6c, 0x0000 }, + { 0xef6d, 0x0000 }, + { 0xef6e, 0x0000 }, + { 0xef6f, 0x0000 }, + { 0xef70, 0x0000 }, + { 0xef71, 0x0000 }, + { 0xef72, 0x0000 }, + { 0xef73, 0x0000 }, + { 0xef74, 0x0000 }, + { 0xef75, 0x0000 }, + { 0xef76, 0x0000 }, + { 0xef77, 0x0000 }, + { 0xef78, 0x0000 }, + { 0xef79, 0x0000 }, + { 0xef7a, 0x0000 }, + { 0xef7b, 0x0000 }, + { 0xef7c, 0x0000 }, + { 0xef7d, 0x0000 }, + { 0xef7e, 0x0000 }, + { 0xef7f, 0x0000 }, + { 0xef80, 0x0000 }, + { 0xef81, 0x0000 }, + { 0xef82, 0x0000 }, + { 0xef83, 0x0000 }, + { 0xef84, 0x0000 }, + { 0xef85, 0x0000 }, + { 0xef86, 0x0000 }, + { 0xef87, 0x0000 }, + { 0xef88, 0x0000 }, + { 0xef89, 0x0000 }, + { 0xef8a, 0x0000 }, + { 0xef8b, 0x0000 }, + { 0xef8c, 0x0000 }, + { 0xef8d, 0x0000 }, + { 0xef8e, 0x0000 }, + { 0xef8f, 0x0000 }, + { 0xef90, 0x0000 }, + { 0xef91, 0x0000 }, + { 0xef92, 0x0000 }, + { 0xef93, 0x0000 }, + { 0xef94, 0x0000 }, + { 0xef95, 0x0000 }, + { 0xef96, 0x0000 }, + { 0xef97, 0x0000 }, + { 0xef98, 0x0000 }, + { 0xef99, 0x0000 }, + { 0xef9a, 0x0000 }, + { 0xef9b, 0x0000 }, + { 0xef9c, 0x0000 }, + { 0xef9d, 0x0000 }, + { 0xef9e, 0x0000 }, + { 0xef9f, 0x0000 }, + { 0xefa0, 0x0000 }, + { 0xefa1, 0x0000 }, + { 0xefa2, 0x0000 }, + { 0xefa3, 0x0000 }, + { 0xefa4, 0x0000 }, + { 0xefa5, 0x0000 }, + { 0xefa6, 0x0000 }, + { 0xefa7, 0x0000 }, + { 0xefa8, 0x0000 }, + { 0xefa9, 0x0000 }, + { 0xefaa, 0x0000 }, + { 0xefab, 0x0000 }, + { 0xefac, 0x0000 }, + { 0xefad, 0x0000 }, + { 0xefae, 0x0000 }, + { 0xefaf, 0x0000 }, + { 0xefb0, 0x0000 }, + { 0xefb1, 0x0000 }, + { 0xefb2, 0x0000 }, + { 0xefb3, 0x0000 }, + { 0xefb4, 0x0000 }, + { 0xefb5, 0x0000 }, + { 0xefb6, 0x0000 }, + { 0xefb7, 0x0000 }, + { 0xefb8, 0x0000 }, + { 0xefb9, 0x0000 }, + { 0xefba, 0x0000 }, + { 0xefbb, 0x0000 }, + { 0xefbc, 0x0000 }, + { 0xefbd, 0x0000 }, + { 0xefbe, 0x0000 }, + { 0xefbf, 0x0000 }, + { 0xefc0, 0x0000 }, + { 0xefc1, 0x0000 }, + { 0xefc2, 0x0000 }, + { 0xefc3, 0x0000 }, + { 0xefc4, 0x0000 }, + { 0xefc5, 0x0000 }, + { 0xefc6, 0x0000 }, + { 0xefc7, 0x0000 }, + { 0xefc8, 0x0000 }, + { 0xefc9, 0x0000 }, + { 0xefca, 0x0000 }, + { 0xefcb, 0x0000 }, + { 0xefcc, 0x0000 }, + { 0xefcd, 0x0000 }, + { 0xefce, 0x0000 }, + { 0xefcf, 0x0000 }, + { 0xefd0, 0x0000 }, + { 0xefd1, 0x0000 }, + { 0xefd2, 0x0000 }, + { 0xefd3, 0x0000 }, + { 0xefd4, 0x0000 }, + { 0xefd5, 0x0000 }, + { 0xefd6, 0x0000 }, + { 0xefd7, 0x0000 }, + { 0xefd8, 0x0000 }, + { 0xefd9, 0x0000 }, + { 0xefda, 0x0000 }, + { 0xefdb, 0x0000 }, + { 0xefdc, 0x0000 }, + { 0xefdd, 0x0000 }, + { 0xefde, 0x0000 }, + { 0xefdf, 0x0000 }, + { 0xefe0, 0x0000 }, + { 0xefe1, 0x0000 }, + { 0xefe2, 0x0000 }, + { 0xefe3, 0x0000 }, + { 0xefe4, 0x0000 }, + { 0xefe5, 0x0000 }, + { 0xefe6, 0x0000 }, + { 0xefe7, 0x0000 }, + { 0xefe8, 0x0000 }, + { 0xefe9, 0x0000 }, + { 0xefea, 0x0000 }, + { 0xefeb, 0x0000 }, + { 0xefec, 0x0000 }, + { 0xefed, 0x0000 }, + { 0xefee, 0x0000 }, + { 0xefef, 0x0000 }, + { 0xeff0, 0x0000 }, + { 0xeff1, 0x0000 }, + { 0xeff2, 0x0000 }, + { 0xeff3, 0x0000 }, + { 0xeff4, 0x0000 }, + { 0xeff5, 0x0000 }, + { 0xeff6, 0x0000 }, + { 0xeff7, 0x0000 }, + { 0xeff8, 0x0000 }, + { 0xeff9, 0x0000 }, + { 0xeffa, 0x0000 }, + { 0xeffb, 0x0000 }, + { 0xeffc, 0x0000 }, + { 0xeffd, 0x0000 }, + { 0xeffe, 0x0000 }, + { 0xefff, 0x0000 }, + { 0xf000, 0x0000 }, + { 0xf001, 0x0000 }, + { 0xf002, 0x0000 }, + { 0xf003, 0x0000 }, + { 0xf004, 0x0000 }, + { 0xf005, 0x0000 }, + { 0xf006, 0x0000 }, + { 0xf007, 0x0000 }, + { 0xf008, 0x0000 }, + { 0xf009, 0x0000 }, + { 0xf00a, 0x0000 }, + { 0xf00b, 0x0000 }, + { 0xf00c, 0x0000 }, + { 0xf00d, 0x0000 }, + { 0xf00e, 0x0000 }, + { 0xf00f, 0x0000 }, + { 0xf010, 0x0000 }, + { 0xf011, 0x0000 }, + { 0xf012, 0x0000 }, + { 0xf013, 0x0000 }, + { 0xf014, 0x0000 }, + { 0xf015, 0x0000 }, + { 0xf016, 0x0000 }, + { 0xf017, 0x0000 }, + { 0xf018, 0x0000 }, + { 0xf019, 0x0000 }, + { 0xf01a, 0x0000 }, + { 0xf01b, 0x0000 }, + { 0xf01c, 0x0000 }, + { 0xf01d, 0x0000 }, + { 0xf01e, 0x0000 }, + { 0xf01f, 0x0000 }, + { 0xf020, 0x0000 }, + { 0xf021, 0x0000 }, + { 0xf022, 0x0000 }, + { 0xf023, 0x0000 }, + { 0xf024, 0x0000 }, + { 0xf025, 0x0000 }, + { 0xf026, 0x0000 }, + { 0xf027, 0x0000 }, + { 0xf028, 0x0000 }, + { 0xf029, 0x0000 }, + { 0xf02a, 0x0000 }, + { 0xf02b, 0x0000 }, + { 0xf02c, 0x0000 }, + { 0xf02d, 0x0000 }, + { 0xf02e, 0x0000 }, + { 0xf02f, 0x0000 }, + { 0xf030, 0x0000 }, + { 0xf031, 0x0000 }, + { 0xf032, 0x0000 }, + { 0xf033, 0x0000 }, + { 0xf034, 0x0000 }, + { 0xf035, 0x0000 }, + { 0xf036, 0x0000 }, + { 0xf037, 0x0000 }, + { 0xf038, 0x0000 }, + { 0xf039, 0x0000 }, + { 0xf03a, 0x0000 }, + { 0xf03b, 0x0000 }, + { 0xf03c, 0x0000 }, + { 0xf03d, 0x0000 }, + { 0xf03e, 0x0000 }, + { 0xf03f, 0x0000 }, + { 0xf040, 0x0000 }, + { 0xf041, 0x0000 }, + { 0xf042, 0x0000 }, + { 0xf043, 0x0000 }, + { 0xf044, 0x0000 }, + { 0xf045, 0x0000 }, + { 0xf046, 0x0000 }, + { 0xf047, 0x0000 }, + { 0xf048, 0x0000 }, + { 0xf049, 0x0000 }, + { 0xf04a, 0x0000 }, + { 0xf04b, 0x0000 }, + { 0xf04c, 0x0000 }, + { 0xf04d, 0x0000 }, + { 0xf04e, 0x0000 }, + { 0xf04f, 0x0000 }, + { 0xf050, 0x0000 }, + { 0xf051, 0x0000 }, + { 0xf052, 0x0000 }, + { 0xf053, 0x0000 }, + { 0xf054, 0x0000 }, + { 0xf055, 0x0000 }, + { 0xf056, 0x0000 }, + { 0xf057, 0x0000 }, + { 0xf058, 0x0000 }, + { 0xf059, 0x0000 }, + { 0xf05a, 0x0000 }, + { 0xf05b, 0x0000 }, + { 0xf05c, 0x0000 }, + { 0xf05d, 0x0000 }, + { 0xf05e, 0x0000 }, + { 0xf05f, 0x0000 }, + { 0xf060, 0x0000 }, + { 0xf061, 0x0000 }, + { 0xf062, 0x0000 }, + { 0xf063, 0x0000 }, + { 0xf064, 0x0000 }, + { 0xf065, 0x0000 }, + { 0xf066, 0x0000 }, + { 0xf067, 0x0000 }, + { 0xf068, 0x0000 }, + { 0xf069, 0x0000 }, + { 0xf06a, 0x0000 }, + { 0xf06b, 0x0000 }, + { 0xf06c, 0x0000 }, + { 0xf06d, 0x0000 }, + { 0xf06e, 0x0000 }, + { 0xf06f, 0x0000 }, + { 0xf070, 0x0000 }, + { 0xf071, 0x0000 }, + { 0xf072, 0x0000 }, + { 0xf073, 0x0000 }, + { 0xf074, 0x0000 }, + { 0xf075, 0x0000 }, + { 0xf076, 0x0000 }, + { 0xf077, 0x0000 }, + { 0xf078, 0x0000 }, + { 0xf079, 0x0000 }, + { 0xf07a, 0x0000 }, + { 0xf07b, 0x0000 }, + { 0xf07c, 0x0000 }, + { 0xf07d, 0x0000 }, + { 0xf07e, 0x0000 }, + { 0xf07f, 0x0000 }, + { 0xf080, 0x0000 }, + { 0xf081, 0x0000 }, + { 0xf082, 0x0000 }, + { 0xf083, 0x0000 }, + { 0xf084, 0x0000 }, + { 0xf085, 0x0000 }, + { 0xf086, 0x0000 }, + { 0xf087, 0x0000 }, + { 0xf088, 0x0000 }, + { 0xf089, 0x0000 }, + { 0xf08a, 0x0000 }, + { 0xf08b, 0x0000 }, + { 0xf08c, 0x0000 }, + { 0xf08d, 0x0000 }, + { 0xf08e, 0x0000 }, + { 0xf08f, 0x0000 }, + { 0xf090, 0x0000 }, + { 0xf091, 0x0000 }, + { 0xf092, 0x0000 }, + { 0xf093, 0x0000 }, + { 0xf094, 0x0000 }, + { 0xf095, 0x0000 }, + { 0xf096, 0x0000 }, + { 0xf097, 0x0000 }, + { 0xf098, 0x0000 }, + { 0xf099, 0x0000 }, + { 0xf09a, 0x0000 }, + { 0xf09b, 0x0000 }, + { 0xf09c, 0x0000 }, + { 0xf09d, 0x0000 }, + { 0xf09e, 0x0000 }, + { 0xf09f, 0x0000 }, + { 0xf0a0, 0x0000 }, + { 0xf0a1, 0x0000 }, + { 0xf0a2, 0x0000 }, + { 0xf0a3, 0x0000 }, + { 0xf0a4, 0x0000 }, + { 0xf0a5, 0x0000 }, + { 0xf0a6, 0x0000 }, + { 0xf0a7, 0x0000 }, + { 0xf0a8, 0x0000 }, + { 0xf0a9, 0x0000 }, + { 0xf0aa, 0x0000 }, + { 0xf0ab, 0x0000 }, + { 0xf0ac, 0x0000 }, + { 0xf0ad, 0x0000 }, + { 0xf0ae, 0x0000 }, + { 0xf0af, 0x0000 }, + { 0xf0b0, 0x0000 }, + { 0xf0b1, 0x0000 }, + { 0xf0b2, 0x0000 }, + { 0xf0b3, 0x0000 }, + { 0xf0b4, 0x0000 }, + { 0xf0b5, 0x0000 }, + { 0xf0b6, 0x0000 }, + { 0xf0b7, 0x0000 }, + { 0xf0b8, 0x0000 }, + { 0xf0b9, 0x0000 }, + { 0xf0ba, 0x0000 }, + { 0xf0bb, 0x0000 }, + { 0xf0bc, 0x0000 }, + { 0xf0bd, 0x0000 }, + { 0xf0be, 0x0000 }, + { 0xf0bf, 0x0000 }, + { 0xf0c0, 0x0000 }, + { 0xf0c1, 0x0000 }, + { 0xf0c2, 0x0000 }, + { 0xf0c3, 0x0000 }, + { 0xf0c4, 0x0000 }, + { 0xf0c5, 0x0000 }, + { 0xf0c6, 0x0000 }, + { 0xf0c7, 0x0000 }, + { 0xf0c8, 0x0000 }, + { 0xf0c9, 0x0000 }, + { 0xf0ca, 0x0000 }, + { 0xf0cb, 0x0000 }, + { 0xf0cc, 0x0000 }, + { 0xf0cd, 0x0000 }, + { 0xf0ce, 0x0000 }, + { 0xf0cf, 0x0000 }, + { 0xf0d0, 0x0000 }, + { 0xf0d1, 0x0000 }, + { 0xf0d2, 0x0000 }, + { 0xf0d3, 0x0000 }, + { 0xf0d4, 0x0000 }, + { 0xf0d5, 0x0000 }, + { 0xf0d6, 0x0000 }, + { 0xf0d7, 0x0000 }, + { 0xf0d8, 0x0000 }, + { 0xf0d9, 0x0000 }, + { 0xf0da, 0x0000 }, + { 0xf0db, 0x0000 }, + { 0xf0dc, 0x0000 }, + { 0xf0dd, 0x0000 }, + { 0xf0de, 0x0000 }, + { 0xf0df, 0x0000 }, + { 0xf0e0, 0x0000 }, + { 0xf0e1, 0x0000 }, + { 0xf0e2, 0x0000 }, + { 0xf0e3, 0x0000 }, + { 0xf0e4, 0x0000 }, + { 0xf0e5, 0x0000 }, + { 0xf0e6, 0x0000 }, + { 0xf0e7, 0x0000 }, + { 0xf0e8, 0x0000 }, + { 0xf0e9, 0x0000 }, + { 0xf0ea, 0x0000 }, + { 0xf0eb, 0x0000 }, + { 0xf0ec, 0x0000 }, + { 0xf0ed, 0x0000 }, + { 0xf0ee, 0x0000 }, + { 0xf0ef, 0x0000 }, + { 0xf0f0, 0x0000 }, + { 0xf0f1, 0x0000 }, + { 0xf0f2, 0x0000 }, + { 0xf0f3, 0x0000 }, + { 0xf0f4, 0x0000 }, + { 0xf0f5, 0x0000 }, + { 0xf0f6, 0x0000 }, + { 0xf0f7, 0x0000 }, + { 0xf0f8, 0x0000 }, + { 0xf0f9, 0x0000 }, + { 0xf0fa, 0x0000 }, + { 0xf0fb, 0x0000 }, + { 0xf0fc, 0x0000 }, + { 0xf0fd, 0x0000 }, + { 0xf0fe, 0x0000 }, + { 0xf0ff, 0x0000 }, + { 0xf100, 0x0000 }, + { 0xf101, 0x0000 }, + { 0xf102, 0x0000 }, + { 0xf103, 0x0000 }, + { 0xf104, 0x0000 }, + { 0xf105, 0x0000 }, + { 0xf106, 0x0000 }, + { 0xf107, 0x0000 }, + { 0xf108, 0x0000 }, + { 0xf109, 0x0000 }, + { 0xf10a, 0x0000 }, + { 0xf10b, 0x0000 }, + { 0xf10c, 0x0000 }, + { 0xf10d, 0x0000 }, + { 0xf10e, 0x0000 }, + { 0xf10f, 0x0000 }, + { 0xf110, 0x0000 }, + { 0xf111, 0x0000 }, + { 0xf112, 0x0000 }, + { 0xf113, 0x0000 }, + { 0xf114, 0x0000 }, + { 0xf115, 0x0000 }, + { 0xf116, 0x0000 }, + { 0xf117, 0x0000 }, + { 0xf118, 0x0000 }, + { 0xf119, 0x0000 }, + { 0xf11a, 0x0000 }, + { 0xf11b, 0x0000 }, + { 0xf11c, 0x0000 }, + { 0xf11d, 0x0000 }, + { 0xf11e, 0x0000 }, + { 0xf11f, 0x0000 }, + { 0xf120, 0x0000 }, + { 0xf121, 0x0000 }, + { 0xf122, 0x0000 }, + { 0xf123, 0x0000 }, + { 0xf124, 0x0000 }, + { 0xf125, 0x0000 }, + { 0xf126, 0x0000 }, + { 0xf127, 0x0000 }, + { 0xf128, 0x0000 }, + { 0xf129, 0x0000 }, + { 0xf12a, 0x0000 }, + { 0xf12b, 0x0000 }, + { 0xf12c, 0x0000 }, + { 0xf12d, 0x0000 }, + { 0xf12e, 0x0000 }, + { 0xf12f, 0x0000 }, + { 0xf130, 0x0000 }, + { 0xf131, 0x0000 }, + { 0xf132, 0x0000 }, + { 0xf133, 0x0000 }, + { 0xf134, 0x0000 }, + { 0xf135, 0x0000 }, + { 0xf136, 0x0000 }, + { 0xf137, 0x0000 }, + { 0xf138, 0x0000 }, + { 0xf139, 0x0000 }, + { 0xf13a, 0x0000 }, + { 0xf13b, 0x0000 }, + { 0xf13c, 0x0000 }, + { 0xf13d, 0x0000 }, + { 0xf13e, 0x0000 }, + { 0xf13f, 0x0000 }, + { 0xf140, 0x0000 }, + { 0xf141, 0x0000 }, + { 0xf142, 0x0000 }, + { 0xf143, 0x0000 }, + { 0xf144, 0x0000 }, + { 0xf145, 0x0000 }, + { 0xf146, 0x0000 }, + { 0xf147, 0x0000 }, + { 0xf148, 0x0000 }, + { 0xf149, 0x0000 }, + { 0xf14a, 0x0000 }, + { 0xf14b, 0x0000 }, + { 0xf14c, 0x0000 }, + { 0xf14d, 0x0000 }, + { 0xf14e, 0x0000 }, + { 0xf14f, 0x0000 }, + { 0xf150, 0x0000 }, + { 0xf151, 0x0000 }, + { 0xf152, 0x0000 }, + { 0xf153, 0x0000 }, + { 0xf154, 0x0000 }, + { 0xf155, 0x0000 }, + { 0xf156, 0x0000 }, + { 0xf157, 0x0000 }, + { 0xf158, 0x0000 }, + { 0xf159, 0x0000 }, + { 0xf15a, 0x0000 }, + { 0xf15b, 0x0000 }, + { 0xf15c, 0x0000 }, + { 0xf15d, 0x0000 }, + { 0xf15e, 0x0000 }, + { 0xf15f, 0x0000 }, + { 0xf160, 0x0000 }, + { 0xf161, 0x0000 }, + { 0xf162, 0x0000 }, + { 0xf163, 0x0000 }, + { 0xf164, 0x0000 }, + { 0xf165, 0x0000 }, + { 0xf166, 0x0000 }, + { 0xf167, 0x0000 }, + { 0xf168, 0x0000 }, + { 0xf169, 0x0000 }, + { 0xf16a, 0x0000 }, + { 0xf16b, 0x0000 }, + { 0xf16c, 0x0000 }, + { 0xf16d, 0x0000 }, + { 0xf16e, 0x0000 }, + { 0xf16f, 0x0000 }, + { 0xf170, 0x0000 }, + { 0xf171, 0x0000 }, + { 0xf172, 0x0000 }, + { 0xf173, 0x0000 }, + { 0xf174, 0x0000 }, + { 0xf175, 0x0000 }, + { 0xf176, 0x0000 }, + { 0xf177, 0x0000 }, + { 0xf178, 0x0000 }, + { 0xf179, 0x0000 }, + { 0xf17a, 0x0000 }, + { 0xf17b, 0x0000 }, + { 0xf17c, 0x0000 }, + { 0xf17d, 0x0000 }, + { 0xf17e, 0x0000 }, + { 0xf17f, 0x0000 }, + { 0xf180, 0x0000 }, + { 0xf181, 0x0000 }, + { 0xf182, 0x0000 }, + { 0xf183, 0x0000 }, + { 0xf184, 0x0000 }, + { 0xf185, 0x0000 }, + { 0xf186, 0x0000 }, + { 0xf187, 0x0000 }, + { 0xf188, 0x0000 }, + { 0xf189, 0x0000 }, + { 0xf18a, 0x0000 }, + { 0xf18b, 0x0000 }, + { 0xf18c, 0x0000 }, + { 0xf18d, 0x0000 }, + { 0xf18e, 0x0000 }, + { 0xf18f, 0x0000 }, + { 0xf190, 0x0000 }, + { 0xf191, 0x0000 }, + { 0xf192, 0x0000 }, + { 0xf193, 0x0000 }, + { 0xf194, 0x0000 }, + { 0xf195, 0x0000 }, + { 0xf196, 0x0000 }, + { 0xf197, 0x0000 }, + { 0xf198, 0x0000 }, + { 0xf199, 0x0000 }, + { 0xf19a, 0x0000 }, + { 0xf19b, 0x0000 }, + { 0xf19c, 0x0000 }, + { 0xf19d, 0x0000 }, + { 0xf19e, 0x0000 }, + { 0xf19f, 0x0000 }, + { 0xf1a0, 0x0000 }, + { 0xf1a1, 0x0000 }, + { 0xf1a2, 0x0000 }, + { 0xf1a3, 0x0000 }, + { 0xf1a4, 0x0000 }, + { 0xf1a5, 0x0000 }, + { 0xf1a6, 0x0000 }, + { 0xf1a7, 0x0000 }, + { 0xf1a8, 0x0000 }, + { 0xf1a9, 0x0000 }, + { 0xf1aa, 0x0000 }, + { 0xf1ab, 0x0000 }, + { 0xf1ac, 0x0000 }, + { 0xf1ad, 0x0000 }, + { 0xf1ae, 0x0000 }, + { 0xf1af, 0x0000 }, + { 0xf1b0, 0x0000 }, + { 0xf1b1, 0x0000 }, + { 0xf1b2, 0x0000 }, + { 0xf1b3, 0x0000 }, + { 0xf1b4, 0x0000 }, + { 0xf1b5, 0x0000 }, + { 0xf1b6, 0x0000 }, + { 0xf1b7, 0x0000 }, + { 0xf1b8, 0x0000 }, + { 0xf1b9, 0x0000 }, + { 0xf1ba, 0x0000 }, + { 0xf1bb, 0x0000 }, + { 0xf1bc, 0x0000 }, + { 0xf1bd, 0x0000 }, + { 0xf1be, 0x0000 }, + { 0xf1bf, 0x0000 }, + { 0xf1c0, 0x0000 }, + { 0xf1c1, 0x0000 }, + { 0xf1c2, 0x0000 }, + { 0xf1c3, 0x0000 }, + { 0xf1c4, 0x0000 }, + { 0xf1c5, 0x0000 }, + { 0xf1c6, 0x0000 }, + { 0xf1c7, 0x0000 }, + { 0xf1c8, 0x0000 }, + { 0xf1c9, 0x0000 }, + { 0xf1ca, 0x0000 }, + { 0xf1cb, 0x0000 }, + { 0xf1cc, 0x0000 }, + { 0xf1cd, 0x0000 }, + { 0xf1ce, 0x0000 }, + { 0xf1cf, 0x0000 }, + { 0xf1d0, 0x0000 }, + { 0xf1d1, 0x0000 }, + { 0xf1d2, 0x0000 }, + { 0xf1d3, 0x0000 }, + { 0xf1d4, 0x0000 }, + { 0xf1d5, 0x0000 }, + { 0xf1d6, 0x0000 }, + { 0xf1d7, 0x0000 }, + { 0xf1d8, 0x0000 }, + { 0xf1d9, 0x0000 }, + { 0xf1da, 0x0000 }, + { 0xf1db, 0x0000 }, + { 0xf1dc, 0x0000 }, + { 0xf1dd, 0x0000 }, + { 0xf1de, 0x0000 }, + { 0xf1df, 0x0000 }, + { 0xf1e0, 0x0000 }, + { 0xf1e1, 0x0000 }, + { 0xf1e2, 0x0000 }, + { 0xf1e3, 0x0000 }, + { 0xf1e4, 0x0000 }, + { 0xf1e5, 0x0000 }, + { 0xf1e6, 0x0000 }, + { 0xf1e7, 0x0000 }, + { 0xf1e8, 0x0000 }, + { 0xf1e9, 0x0000 }, + { 0xf1ea, 0x0000 }, + { 0xf1eb, 0x0000 }, + { 0xf1ec, 0x0000 }, + { 0xf1ed, 0x0000 }, + { 0xf1ee, 0x0000 }, + { 0xf1ef, 0x0000 }, + { 0xf1f0, 0x0000 }, + { 0xf1f1, 0x0000 }, + { 0xf1f2, 0x0000 }, + { 0xf1f3, 0x0000 }, + { 0xf1f4, 0x0000 }, + { 0xf1f5, 0x0000 }, + { 0xf1f6, 0x0000 }, + { 0xf1f7, 0x0000 }, + { 0xf1f8, 0x0000 }, + { 0xf1f9, 0x0000 }, + { 0xf1fa, 0x0000 }, + { 0xf1fb, 0x0000 }, + { 0xf1fc, 0x0000 }, + { 0xf1fd, 0x0000 }, + { 0xf1fe, 0x0000 }, + { 0xf1ff, 0x0000 }, + { 0xf200, 0x0000 }, + { 0xf201, 0x0000 }, + { 0xf202, 0x0000 }, + { 0xf203, 0x0000 }, + { 0xf204, 0x0000 }, + { 0xf205, 0x0000 }, + { 0xf206, 0x0000 }, + { 0xf207, 0x0000 }, + { 0xf208, 0x0000 }, + { 0xf209, 0x0000 }, + { 0xf20a, 0x0000 }, + { 0xf20b, 0x0000 }, + { 0xf20c, 0x0000 }, + { 0xf20d, 0x0000 }, + { 0xf20e, 0x0000 }, + { 0xf20f, 0x0000 }, + { 0xf210, 0x0000 }, + { 0xf211, 0x0000 }, + { 0xf212, 0x0000 }, + { 0xf213, 0x0000 }, + { 0xf214, 0x0000 }, + { 0xf215, 0x0000 }, + { 0xf216, 0x0000 }, + { 0xf217, 0x0000 }, + { 0xf218, 0x0000 }, + { 0xf219, 0x0000 }, + { 0xf21a, 0x0000 }, + { 0xf21b, 0x0000 }, + { 0xf21c, 0x0000 }, + { 0xf21d, 0x0000 }, + { 0xf21e, 0x0000 }, + { 0xf21f, 0x0000 }, + { 0xf220, 0x0000 }, + { 0xf221, 0x0000 }, + { 0xf222, 0x0000 }, + { 0xf223, 0x0000 }, + { 0xf224, 0x0000 }, + { 0xf225, 0x0000 }, + { 0xf226, 0x0000 }, + { 0xf227, 0x0000 }, + { 0xf228, 0x0000 }, + { 0xf229, 0x0000 }, + { 0xf22a, 0x0000 }, + { 0xf22b, 0x0000 }, + { 0xf22c, 0x0000 }, + { 0xf22d, 0x0000 }, + { 0xf22e, 0x0000 }, + { 0xf22f, 0x0000 }, + { 0xf230, 0x0000 }, + { 0xf231, 0x0000 }, + { 0xf232, 0x0000 }, + { 0xf233, 0x0000 }, + { 0xf234, 0x0000 }, + { 0xf235, 0x0000 }, + { 0xf236, 0x0000 }, + { 0xf237, 0x0000 }, + { 0xf238, 0x0000 }, + { 0xf239, 0x0000 }, + { 0xf23a, 0x0000 }, + { 0xf23b, 0x0000 }, + { 0xf23c, 0x0000 }, + { 0xf23d, 0x0000 }, + { 0xf23e, 0x0000 }, + { 0xf23f, 0x0000 }, + { 0xf240, 0x0000 }, + { 0xf241, 0x0000 }, + { 0xf242, 0x0000 }, + { 0xf243, 0x0000 }, + { 0xf244, 0x0000 }, + { 0xf245, 0x0000 }, + { 0xf246, 0x0000 }, + { 0xf247, 0x0000 }, + { 0xf248, 0x0000 }, + { 0xf249, 0x0000 }, + { 0xf24a, 0x0000 }, + { 0xf24b, 0x0000 }, + { 0xf24c, 0x0000 }, + { 0xf24d, 0x0000 }, + { 0xf24e, 0x0000 }, + { 0xf24f, 0x0000 }, + { 0xf250, 0x0000 }, + { 0xf251, 0x0000 }, + { 0xf252, 0x0000 }, + { 0xf253, 0x0000 }, + { 0xf254, 0x0000 }, + { 0xf255, 0x0000 }, + { 0xf256, 0x0000 }, + { 0xf257, 0x0000 }, + { 0xf258, 0x0000 }, + { 0xf259, 0x0000 }, + { 0xf25a, 0x0000 }, + { 0xf25b, 0x0000 }, + { 0xf25c, 0x0000 }, + { 0xf25d, 0x0000 }, + { 0xf25e, 0x0000 }, + { 0xf25f, 0x0000 }, + { 0xf260, 0x0000 }, + { 0xf261, 0x0000 }, + { 0xf262, 0x0000 }, + { 0xf263, 0x0000 }, + { 0xf264, 0x0000 }, + { 0xf265, 0x0000 }, + { 0xf266, 0x0000 }, + { 0xf267, 0x0000 }, + { 0xf268, 0x0000 }, + { 0xf269, 0x0000 }, + { 0xf26a, 0x0000 }, + { 0xf26b, 0x0000 }, + { 0xf26c, 0x0000 }, + { 0xf26d, 0x0000 }, + { 0xf26e, 0x0000 }, + { 0xf26f, 0x0000 }, + { 0xf270, 0x0000 }, + { 0xf271, 0x0000 }, + { 0xf272, 0x0000 }, + { 0xf273, 0x0000 }, + { 0xf274, 0x0000 }, + { 0xf275, 0x0000 }, + { 0xf276, 0x0000 }, + { 0xf277, 0x0000 }, + { 0xf278, 0x0000 }, + { 0xf279, 0x0000 }, + { 0xf27a, 0x0000 }, + { 0xf27b, 0x0000 }, + { 0xf27c, 0x0000 }, + { 0xf27d, 0x0000 }, + { 0xf27e, 0x0000 }, + { 0xf27f, 0x0000 }, + { 0xf280, 0x0000 }, + { 0xf281, 0x0000 }, + { 0xf282, 0x0000 }, + { 0xf283, 0x0000 }, + { 0xf284, 0x0000 }, + { 0xf285, 0x0000 }, + { 0xf286, 0x0000 }, + { 0xf287, 0x0000 }, + { 0xf288, 0x0000 }, + { 0xf289, 0x0000 }, + { 0xf28a, 0x0000 }, + { 0xf28b, 0x0000 }, + { 0xf28c, 0x0000 }, + { 0xf28d, 0x0000 }, + { 0xf28e, 0x0000 }, + { 0xf28f, 0x0000 }, + { 0xf290, 0x0000 }, + { 0xf291, 0x0000 }, + { 0xf292, 0x0000 }, + { 0xf293, 0x0000 }, + { 0xf294, 0x0000 }, + { 0xf295, 0x0000 }, + { 0xf296, 0x0000 }, + { 0xf297, 0x0000 }, + { 0xf298, 0x0000 }, + { 0xf299, 0x0000 }, + { 0xf29a, 0x0000 }, + { 0xf29b, 0x0000 }, + { 0xf29c, 0x0000 }, + { 0xf29d, 0x0000 }, + { 0xf29e, 0x0000 }, + { 0xf29f, 0x0000 }, + { 0xf2a0, 0x0000 }, + { 0xf2a1, 0x0000 }, + { 0xf2a2, 0x0000 }, + { 0xf2a3, 0x0000 }, + { 0xf2a4, 0x0000 }, + { 0xf2a5, 0x0000 }, + { 0xf2a6, 0x0000 }, + { 0xf2a7, 0x0000 }, + { 0xf2a8, 0x0000 }, + { 0xf2a9, 0x0000 }, + { 0xf2aa, 0x0000 }, + { 0xf2ab, 0x0000 }, + { 0xf2ac, 0x0000 }, + { 0xf2ad, 0x0000 }, + { 0xf2ae, 0x0000 }, + { 0xf2af, 0x0000 }, + { 0xf2b0, 0x0000 }, + { 0xf2b1, 0x0000 }, + { 0xf2b2, 0x0000 }, + { 0xf2b3, 0x0000 }, + { 0xf2b4, 0x0000 }, + { 0xf2b5, 0x0000 }, + { 0xf2b6, 0x0000 }, + { 0xf2b7, 0x0000 }, + { 0xf2b8, 0x0000 }, + { 0xf2b9, 0x0000 }, + { 0xf2ba, 0x0000 }, + { 0xf2bb, 0x0000 }, + { 0xf2bc, 0x0000 }, + { 0xf2bd, 0x0000 }, + { 0xf2be, 0x0000 }, + { 0xf2bf, 0x0000 }, + { 0xf2c0, 0x0000 }, + { 0xf2c1, 0x0000 }, + { 0xf2c2, 0x0000 }, + { 0xf2c3, 0x0000 }, + { 0xf2c4, 0x0000 }, + { 0xf2c5, 0x0000 }, + { 0xf2c6, 0x0000 }, + { 0xf2c7, 0x0000 }, + { 0xf2c8, 0x0000 }, + { 0xf2c9, 0x0000 }, + { 0xf2ca, 0x0000 }, + { 0xf2cb, 0x0000 }, + { 0xf2cc, 0x0000 }, + { 0xf2cd, 0x0000 }, + { 0xf2ce, 0x0000 }, + { 0xf2cf, 0x0000 }, + { 0xf2d0, 0x0000 }, + { 0xf2d1, 0x0000 }, + { 0xf2d2, 0x0000 }, + { 0xf2d3, 0x0000 }, + { 0xf2d4, 0x0000 }, + { 0xf2d5, 0x0000 }, + { 0xf2d6, 0x0000 }, + { 0xf2d7, 0x0000 }, + { 0xf2d8, 0x0000 }, + { 0xf2d9, 0x0000 }, + { 0xf2da, 0x0000 }, + { 0xf2db, 0x0000 }, + { 0xf2dc, 0x0000 }, + { 0xf2dd, 0x0000 }, + { 0xf2de, 0x0000 }, + { 0xf2df, 0x0000 }, + { 0xf2e0, 0x0000 }, + { 0xf2e1, 0x0000 }, + { 0xf2e2, 0x0000 }, + { 0xf2e3, 0x0000 }, + { 0xf2e4, 0x0000 }, + { 0xf2e5, 0x0000 }, + { 0xf2e6, 0x0000 }, + { 0xf2e7, 0x0000 }, + { 0xf2e8, 0x0000 }, + { 0xf2e9, 0x0000 }, + { 0xf2ea, 0x0000 }, + { 0xf2eb, 0x0000 }, + { 0xf2ec, 0x0000 }, + { 0xf2ed, 0x0000 }, + { 0xf2ee, 0x0000 }, + { 0xf2ef, 0x0000 }, + { 0xf2f0, 0x0000 }, + { 0xf2f1, 0x0000 }, + { 0xf2f2, 0x0000 }, + { 0xf2f3, 0x0000 }, + { 0xf2f4, 0x0000 }, + { 0xf2f5, 0x0000 }, + { 0xf2f6, 0x0000 }, + { 0xf2f7, 0x0000 }, + { 0xf2f8, 0x0000 }, + { 0xf2f9, 0x0000 }, + { 0xf2fa, 0x0000 }, + { 0xf2fb, 0x0000 }, + { 0xf2fc, 0x0000 }, + { 0xf2fd, 0x0000 }, + { 0xf2fe, 0x0000 }, + { 0xf2ff, 0x0000 }, + { 0xf300, 0x0000 }, + { 0xf301, 0x0000 }, + { 0xf302, 0x0000 }, + { 0xf303, 0x0000 }, + { 0xf304, 0x0000 }, + { 0xf305, 0x0000 }, + { 0xf306, 0x0000 }, + { 0xf307, 0x0000 }, + { 0xf308, 0x0000 }, + { 0xf309, 0x0000 }, + { 0xf30a, 0x0000 }, + { 0xf30b, 0x0000 }, + { 0xf30c, 0x0000 }, + { 0xf30d, 0x0000 }, + { 0xf30e, 0x0000 }, + { 0xf30f, 0x0000 }, + { 0xf310, 0x0000 }, + { 0xf311, 0x0000 }, + { 0xf312, 0x0000 }, + { 0xf313, 0x0000 }, + { 0xf314, 0x0000 }, + { 0xf315, 0x0000 }, + { 0xf316, 0x0000 }, + { 0xf317, 0x0000 }, + { 0xf318, 0x0000 }, + { 0xf319, 0x0000 }, + { 0xf31a, 0x0000 }, + { 0xf31b, 0x0000 }, + { 0xf31c, 0x0000 }, + { 0xf31d, 0x0000 }, + { 0xf31e, 0x0000 }, + { 0xf31f, 0x0000 }, + { 0xf320, 0x0000 }, + { 0xf321, 0x0000 }, + { 0xf322, 0x0000 }, + { 0xf323, 0x0000 }, + { 0xf324, 0x0000 }, + { 0xf325, 0x0000 }, + { 0xf326, 0x0000 }, + { 0xf327, 0x0000 }, + { 0xf328, 0x0000 }, + { 0xf329, 0x0000 }, + { 0xf32a, 0x0000 }, + { 0xf32b, 0x0000 }, + { 0xf32c, 0x0000 }, + { 0xf32d, 0x0000 }, + { 0xf32e, 0x0000 }, + { 0xf32f, 0x0000 }, + { 0xf330, 0x0000 }, + { 0xf331, 0x0000 }, + { 0xf332, 0x0000 }, + { 0xf333, 0x0000 }, + { 0xf334, 0x0000 }, + { 0xf335, 0x0000 }, + { 0xf336, 0x0000 }, + { 0xf337, 0x0000 }, + { 0xf338, 0x0000 }, + { 0xf339, 0x0000 }, + { 0xf33a, 0x0000 }, + { 0xf33b, 0x0000 }, + { 0xf33c, 0x0000 }, + { 0xf33d, 0x0000 }, + { 0xf33e, 0x0000 }, + { 0xf33f, 0x0000 }, + { 0xf340, 0x0000 }, + { 0xf341, 0x0000 }, + { 0xf342, 0x0000 }, + { 0xf343, 0x0000 }, + { 0xf344, 0x0000 }, + { 0xf345, 0x0000 }, + { 0xf346, 0x0000 }, + { 0xf347, 0x0000 }, + { 0xf348, 0x0000 }, + { 0xf349, 0x0000 }, + { 0xf34a, 0x0000 }, + { 0xf34b, 0x0000 }, + { 0xf34c, 0x0000 }, + { 0xf34d, 0x0000 }, + { 0xf34e, 0x0000 }, + { 0xf34f, 0x0000 }, + { 0xf350, 0x0000 }, + { 0xf351, 0x0000 }, + { 0xf352, 0x0000 }, + { 0xf353, 0x0000 }, + { 0xf354, 0x0000 }, + { 0xf355, 0x0000 }, + { 0xf356, 0x0000 }, + { 0xf357, 0x0000 }, + { 0xf358, 0x0000 }, + { 0xf359, 0x0000 }, + { 0xf35a, 0x0000 }, + { 0xf35b, 0x0000 }, + { 0xf35c, 0x0000 }, + { 0xf35d, 0x0000 }, + { 0xf35e, 0x0000 }, + { 0xf35f, 0x0000 }, + { 0xf360, 0x0000 }, + { 0xf361, 0x0000 }, + { 0xf362, 0x0000 }, + { 0xf363, 0x0000 }, + { 0xf364, 0x0000 }, + { 0xf365, 0x0000 }, + { 0xf366, 0x0000 }, + { 0xf367, 0x0000 }, + { 0xf368, 0x0000 }, + { 0xf369, 0x0000 }, + { 0xf36a, 0x0000 }, + { 0xf36b, 0x0000 }, + { 0xf36c, 0x0000 }, + { 0xf36d, 0x0000 }, + { 0xf36e, 0x0000 }, + { 0xf36f, 0x0000 }, + { 0xf370, 0x0000 }, + { 0xf371, 0x0000 }, + { 0xf372, 0x0000 }, + { 0xf373, 0x0000 }, + { 0xf374, 0x0000 }, + { 0xf375, 0x0000 }, + { 0xf376, 0x0000 }, + { 0xf377, 0x0000 }, + { 0xf378, 0x0000 }, + { 0xf379, 0x0000 }, + { 0xf37a, 0x0000 }, + { 0xf37b, 0x0000 }, + { 0xf37c, 0x0000 }, + { 0xf37d, 0x0000 }, + { 0xf37e, 0x0000 }, + { 0xf37f, 0x0000 }, + { 0xf380, 0x0000 }, + { 0xf381, 0x0000 }, + { 0xf382, 0x0000 }, + { 0xf383, 0x0000 }, + { 0xf384, 0x0000 }, + { 0xf385, 0x0000 }, + { 0xf386, 0x0000 }, + { 0xf387, 0x0000 }, + { 0xf388, 0x0000 }, + { 0xf389, 0x0000 }, + { 0xf38a, 0x0000 }, + { 0xf38b, 0x0000 }, + { 0xf38c, 0x0000 }, + { 0xf38d, 0x0000 }, + { 0xf38e, 0x0000 }, + { 0xf38f, 0x0000 }, + { 0xf390, 0x0000 }, + { 0xf391, 0x0000 }, + { 0xf392, 0x0000 }, + { 0xf393, 0x0000 }, + { 0xf394, 0x0000 }, + { 0xf395, 0x0000 }, + { 0xf396, 0x0000 }, + { 0xf397, 0x0000 }, + { 0xf398, 0x0000 }, + { 0xf399, 0x0000 }, + { 0xf39a, 0x0000 }, + { 0xf39b, 0x0000 }, + { 0xf39c, 0x0000 }, + { 0xf39d, 0x0000 }, + { 0xf39e, 0x0000 }, + { 0xf39f, 0x0000 }, + { 0xf3a0, 0x0000 }, + { 0xf3a1, 0x0000 }, + { 0xf3a2, 0x0000 }, + { 0xf3a3, 0x0000 }, + { 0xf3a4, 0x0000 }, + { 0xf3a5, 0x0000 }, + { 0xf3a6, 0x0000 }, + { 0xf3a7, 0x0000 }, + { 0xf3a8, 0x0000 }, + { 0xf3a9, 0x0000 }, + { 0xf3aa, 0x0000 }, + { 0xf3ab, 0x0000 }, + { 0xf3ac, 0x0000 }, + { 0xf3ad, 0x0000 }, + { 0xf3ae, 0x0000 }, + { 0xf3af, 0x0000 }, + { 0xf3b0, 0x0000 }, + { 0xf3b1, 0x0000 }, + { 0xf3b2, 0x0000 }, + { 0xf3b3, 0x0000 }, + { 0xf3b4, 0x0000 }, + { 0xf3b5, 0x0000 }, + { 0xf3b6, 0x0000 }, + { 0xf3b7, 0x0000 }, + { 0xf3b8, 0x0000 }, + { 0xf3b9, 0x0000 }, + { 0xf3ba, 0x0000 }, + { 0xf3bb, 0x0000 }, + { 0xf3bc, 0x0000 }, + { 0xf3bd, 0x0000 }, + { 0xf3be, 0x0000 }, + { 0xf3bf, 0x0000 }, + { 0xf3c0, 0x0000 }, + { 0xf3c1, 0x0000 }, + { 0xf3c2, 0x0000 }, + { 0xf3c3, 0x0000 }, + { 0xf3c4, 0x0000 }, + { 0xf3c5, 0x0000 }, + { 0xf3c6, 0x0000 }, + { 0xf3c7, 0x0000 }, + { 0xf3c8, 0x0000 }, + { 0xf3c9, 0x0000 }, + { 0xf3ca, 0x0000 }, + { 0xf3cb, 0x0000 }, + { 0xf3cc, 0x0000 }, + { 0xf3cd, 0x0000 }, + { 0xf3ce, 0x0000 }, + { 0xf3cf, 0x0000 }, + { 0xf3d0, 0x0000 }, + { 0xf3d1, 0x0000 }, + { 0xf3d2, 0x0000 }, + { 0xf3d3, 0x0000 }, + { 0xf3d4, 0x0000 }, + { 0xf3d5, 0x0000 }, + { 0xf3d6, 0x0000 }, + { 0xf3d7, 0x0000 }, + { 0xf3d8, 0x0000 }, + { 0xf3d9, 0x0000 }, + { 0xf3da, 0x0000 }, + { 0xf3db, 0x0000 }, + { 0xf3dc, 0x0000 }, + { 0xf3dd, 0x0000 }, + { 0xf3de, 0x0000 }, + { 0xf3df, 0x0000 }, + { 0xf3e0, 0x0000 }, + { 0xf3e1, 0x0000 }, + { 0xf3e2, 0x0000 }, + { 0xf3e3, 0x0000 }, + { 0xf3e4, 0x0000 }, + { 0xf3e5, 0x0000 }, + { 0xf3e6, 0x0000 }, + { 0xf3e7, 0x0000 }, + { 0xf3e8, 0x0000 }, + { 0xf3e9, 0x0000 }, + { 0xf3ea, 0x0000 }, + { 0xf3eb, 0x0000 }, + { 0xf3ec, 0x0000 }, + { 0xf3ed, 0x0000 }, + { 0xf3ee, 0x0000 }, + { 0xf3ef, 0x0000 }, + { 0xf3f0, 0x0000 }, + { 0xf3f1, 0x0000 }, + { 0xf3f2, 0x0000 }, + { 0xf3f3, 0x0000 }, + { 0xf3f4, 0x0000 }, + { 0xf3f5, 0x0000 }, + { 0xf3f6, 0x0000 }, + { 0xf3f7, 0x0000 }, + { 0xf3f8, 0x0000 }, + { 0xf3f9, 0x0000 }, + { 0xf3fa, 0x0000 }, + { 0xf3fb, 0x0000 }, + { 0xf3fc, 0x0000 }, + { 0xf3fd, 0x0000 }, + { 0xf3fe, 0x0000 }, + { 0xf3ff, 0x0000 }, + { 0xf400, 0x0000 }, + { 0xf401, 0x0000 }, + { 0xf402, 0x0000 }, + { 0xf403, 0x0000 }, + { 0xf404, 0x0000 }, + { 0xf405, 0x0000 }, + { 0xf406, 0x0000 }, + { 0xf407, 0x0000 }, + { 0xf408, 0x0000 }, + { 0xf409, 0x0000 }, + { 0xf40a, 0x0000 }, + { 0xf40b, 0x0000 }, + { 0xf40c, 0x0000 }, + { 0xf40d, 0x0000 }, + { 0xf40e, 0x0000 }, + { 0xf40f, 0x0000 }, + { 0xf410, 0x0000 }, + { 0xf411, 0x0000 }, + { 0xf412, 0x0000 }, + { 0xf413, 0x0000 }, + { 0xf414, 0x0000 }, + { 0xf415, 0x0000 }, + { 0xf416, 0x0000 }, + { 0xf417, 0x0000 }, + { 0xf418, 0x0000 }, + { 0xf419, 0x0000 }, + { 0xf41a, 0x0000 }, + { 0xf41b, 0x0000 }, + { 0xf41c, 0x0000 }, + { 0xf41d, 0x0000 }, + { 0xf41e, 0x0000 }, + { 0xf41f, 0x0000 }, + { 0xf420, 0x0000 }, + { 0xf421, 0x0000 }, + { 0xf422, 0x0000 }, + { 0xf423, 0x0000 }, + { 0xf424, 0x0000 }, + { 0xf425, 0x0000 }, + { 0xf426, 0x0000 }, + { 0xf427, 0x0000 }, + { 0xf428, 0x0000 }, + { 0xf429, 0x0000 }, + { 0xf42a, 0x0000 }, + { 0xf42b, 0x0000 }, + { 0xf42c, 0x0000 }, + { 0xf42d, 0x0000 }, + { 0xf42e, 0x0000 }, + { 0xf42f, 0x0000 }, + { 0xf430, 0x0000 }, + { 0xf431, 0x0000 }, + { 0xf432, 0x0000 }, + { 0xf433, 0x0000 }, + { 0xf434, 0x0000 }, + { 0xf435, 0x0000 }, + { 0xf436, 0x0000 }, + { 0xf437, 0x0000 }, + { 0xf438, 0x0000 }, + { 0xf439, 0x0000 }, + { 0xf43a, 0x0000 }, + { 0xf43b, 0x0000 }, + { 0xf43c, 0x0000 }, + { 0xf43d, 0x0000 }, + { 0xf43e, 0x0000 }, + { 0xf43f, 0x0000 }, + { 0xf440, 0x0000 }, + { 0xf441, 0x0000 }, + { 0xf442, 0x0000 }, + { 0xf443, 0x0000 }, + { 0xf444, 0x0000 }, + { 0xf445, 0x0000 }, + { 0xf446, 0x0000 }, + { 0xf447, 0x0000 }, + { 0xf448, 0x0000 }, + { 0xf449, 0x0000 }, + { 0xf44a, 0x0000 }, + { 0xf44b, 0x0000 }, + { 0xf44c, 0x0000 }, + { 0xf44d, 0x0000 }, + { 0xf44e, 0x0000 }, + { 0xf44f, 0x0000 }, + { 0xf450, 0x0000 }, + { 0xf451, 0x0000 }, + { 0xf452, 0x0000 }, + { 0xf453, 0x0000 }, + { 0xf454, 0x0000 }, + { 0xf455, 0x0000 }, + { 0xf456, 0x0000 }, + { 0xf457, 0x0000 }, + { 0xf458, 0x0000 }, + { 0xf459, 0x0000 }, + { 0xf45a, 0x0000 }, + { 0xf45b, 0x0000 }, + { 0xf45c, 0x0000 }, + { 0xf45d, 0x0000 }, + { 0xf45e, 0x0000 }, + { 0xf45f, 0x0000 }, + { 0xf460, 0x0000 }, + { 0xf461, 0x0000 }, + { 0xf462, 0x0000 }, + { 0xf463, 0x0000 }, + { 0xf464, 0x0000 }, + { 0xf465, 0x0000 }, + { 0xf466, 0x0000 }, + { 0xf467, 0x0000 }, + { 0xf468, 0x0000 }, + { 0xf469, 0x0000 }, + { 0xf46a, 0x0000 }, + { 0xf46b, 0x0000 }, + { 0xf46c, 0x0000 }, + { 0xf46d, 0x0000 }, + { 0xf46e, 0x0000 }, + { 0xf46f, 0x0000 }, + { 0xf470, 0x0000 }, + { 0xf471, 0x0000 }, + { 0xf472, 0x0000 }, + { 0xf473, 0x0000 }, + { 0xf474, 0x0000 }, + { 0xf475, 0x0000 }, + { 0xf476, 0x0000 }, + { 0xf477, 0x0000 }, + { 0xf478, 0x0000 }, + { 0xf479, 0x0000 }, + { 0xf47a, 0x0000 }, + { 0xf47b, 0x0000 }, + { 0xf47c, 0x0000 }, + { 0xf47d, 0x0000 }, + { 0xf47e, 0x0000 }, + { 0xf47f, 0x0000 }, + { 0xf480, 0x0000 }, + { 0xf481, 0x0000 }, + { 0xf482, 0x0000 }, + { 0xf483, 0x0000 }, + { 0xf484, 0x0000 }, + { 0xf485, 0x0000 }, + { 0xf486, 0x0000 }, + { 0xf487, 0x0000 }, + { 0xf488, 0x0000 }, + { 0xf489, 0x0000 }, + { 0xf48a, 0x0000 }, + { 0xf48b, 0x0000 }, + { 0xf48c, 0x0000 }, + { 0xf48d, 0x0000 }, + { 0xf48e, 0x0000 }, + { 0xf48f, 0x0000 }, + { 0xf490, 0x0000 }, + { 0xf491, 0x0000 }, + { 0xf492, 0x0000 }, + { 0xf493, 0x0000 }, + { 0xf494, 0x0000 }, + { 0xf495, 0x0000 }, + { 0xf496, 0x0000 }, + { 0xf497, 0x0000 }, + { 0xf498, 0x0000 }, + { 0xf499, 0x0000 }, + { 0xf49a, 0x0000 }, + { 0xf49b, 0x0000 }, + { 0xf49c, 0x0000 }, + { 0xf49d, 0x0000 }, + { 0xf49e, 0x0000 }, + { 0xf49f, 0x0000 }, + { 0xf4a0, 0x0000 }, + { 0xf4a1, 0x0000 }, + { 0xf4a2, 0x0000 }, + { 0xf4a3, 0x0000 }, + { 0xf4a4, 0x0000 }, + { 0xf4a5, 0x0000 }, + { 0xf4a6, 0x0000 }, + { 0xf4a7, 0x0000 }, + { 0xf4a8, 0x0000 }, + { 0xf4a9, 0x0000 }, + { 0xf4aa, 0x0000 }, + { 0xf4ab, 0x0000 }, + { 0xf4ac, 0x0000 }, + { 0xf4ad, 0x0000 }, + { 0xf4ae, 0x0000 }, + { 0xf4af, 0x0000 }, + { 0xf4b0, 0x0000 }, + { 0xf4b1, 0x0000 }, + { 0xf4b2, 0x0000 }, + { 0xf4b3, 0x0000 }, + { 0xf4b4, 0x0000 }, + { 0xf4b5, 0x0000 }, + { 0xf4b6, 0x0000 }, + { 0xf4b7, 0x0000 }, + { 0xf4b8, 0x0000 }, + { 0xf4b9, 0x0000 }, + { 0xf4ba, 0x0000 }, + { 0xf4bb, 0x0000 }, + { 0xf4bc, 0x0000 }, + { 0xf4bd, 0x0000 }, + { 0xf4be, 0x0000 }, + { 0xf4bf, 0x0000 }, + { 0xf4c0, 0x0000 }, + { 0xf4c1, 0x0000 }, + { 0xf4c2, 0x0000 }, + { 0xf4c3, 0x0000 }, + { 0xf4c4, 0x0000 }, + { 0xf4c5, 0x0000 }, + { 0xf4c6, 0x0000 }, + { 0xf4c7, 0x0000 }, + { 0xf4c8, 0x0000 }, + { 0xf4c9, 0x0000 }, + { 0xf4ca, 0x0000 }, + { 0xf4cb, 0x0000 }, + { 0xf4cc, 0x0000 }, + { 0xf4cd, 0x0000 }, + { 0xf4ce, 0x0000 }, + { 0xf4cf, 0x0000 }, + { 0xf4d0, 0x0000 }, + { 0xf4d1, 0x0000 }, + { 0xf4d2, 0x0000 }, + { 0xf4d3, 0x0000 }, + { 0xf4d4, 0x0000 }, + { 0xf4d5, 0x0000 }, + { 0xf4d6, 0x0000 }, + { 0xf4d7, 0x0000 }, + { 0xf4d8, 0x0000 }, + { 0xf4d9, 0x0000 }, + { 0xf4da, 0x0000 }, + { 0xf4db, 0x0000 }, + { 0xf4dc, 0x0000 }, + { 0xf4dd, 0x0000 }, + { 0xf4de, 0x0000 }, + { 0xf4df, 0x0000 }, + { 0xf4e0, 0x0000 }, + { 0xf4e1, 0x0000 }, + { 0xf4e2, 0x0000 }, + { 0xf4e3, 0x0000 }, + { 0xf4e4, 0x0000 }, + { 0xf4e5, 0x0000 }, + { 0xf4e6, 0x0000 }, + { 0xf4e7, 0x0000 }, + { 0xf4e8, 0x0000 }, + { 0xf4e9, 0x0000 }, + { 0xf4ea, 0x0000 }, + { 0xf4eb, 0x0000 }, + { 0xf4ec, 0x0000 }, + { 0xf4ed, 0x0000 }, + { 0xf4ee, 0x0000 }, + { 0xf4ef, 0x0000 }, + { 0xf4f0, 0x0000 }, + { 0xf4f1, 0x0000 }, + { 0xf4f2, 0x0000 }, + { 0xf4f3, 0x0000 }, + { 0xf4f4, 0x0000 }, + { 0xf4f5, 0x0000 }, + { 0xf4f6, 0x0000 }, + { 0xf4f7, 0x0000 }, + { 0xf4f8, 0x0000 }, + { 0xf4f9, 0x0000 }, + { 0xf4fa, 0x0000 }, + { 0xf4fb, 0x0000 }, + { 0xf4fc, 0x0000 }, + { 0xf4fd, 0x0000 }, + { 0xf4fe, 0x0000 }, + { 0xf4ff, 0x0000 }, + { 0xf500, 0x0000 }, + { 0xf501, 0x0000 }, + { 0xf502, 0x0000 }, + { 0xf503, 0x0000 }, + { 0xf504, 0x0000 }, + { 0xf505, 0x0000 }, + { 0xf506, 0x0000 }, + { 0xf507, 0x0000 }, + { 0xf508, 0x0000 }, + { 0xf509, 0x0000 }, + { 0xf50a, 0x0000 }, + { 0xf50b, 0x0000 }, + { 0xf50c, 0x0000 }, + { 0xf50d, 0x0000 }, + { 0xf50e, 0x0000 }, + { 0xf50f, 0x0000 }, + { 0xf510, 0x0000 }, + { 0xf511, 0x0000 }, + { 0xf512, 0x0000 }, + { 0xf513, 0x0000 }, + { 0xf514, 0x0000 }, + { 0xf515, 0x0000 }, + { 0xf516, 0x0000 }, + { 0xf517, 0x0000 }, + { 0xf518, 0x0000 }, + { 0xf519, 0x0000 }, + { 0xf51a, 0x0000 }, + { 0xf51b, 0x0000 }, + { 0xf51c, 0x0000 }, + { 0xf51d, 0x0000 }, + { 0xf51e, 0x0000 }, + { 0xf51f, 0x0000 }, + { 0xf520, 0x0000 }, + { 0xf521, 0x0000 }, + { 0xf522, 0x0000 }, + { 0xf523, 0x0000 }, + { 0xf524, 0x0000 }, + { 0xf525, 0x0000 }, + { 0xf526, 0x0000 }, + { 0xf527, 0x0000 }, + { 0xf528, 0x0000 }, + { 0xf529, 0x0000 }, + { 0xf52a, 0x0000 }, + { 0xf52b, 0x0000 }, + { 0xf52c, 0x0000 }, + { 0xf52d, 0x0000 }, + { 0xf52e, 0x0000 }, + { 0xf52f, 0x0000 }, + { 0xf530, 0x0000 }, + { 0xf531, 0x0000 }, + { 0xf532, 0x0000 }, + { 0xf533, 0x0000 }, + { 0xf534, 0x0000 }, + { 0xf535, 0x0000 }, + { 0xf536, 0x0000 }, + { 0xf537, 0x0000 }, + { 0xf538, 0x0000 }, + { 0xf539, 0x0000 }, + { 0xf53a, 0x0000 }, + { 0xf53b, 0x0000 }, + { 0xf53c, 0x0000 }, + { 0xf53d, 0x0000 }, + { 0xf53e, 0x0000 }, + { 0xf53f, 0x0000 }, + { 0xf540, 0x0000 }, + { 0xf541, 0x0000 }, + { 0xf542, 0x0000 }, + { 0xf543, 0x0000 }, + { 0xf544, 0x0000 }, + { 0xf545, 0x0000 }, + { 0xf546, 0x0000 }, + { 0xf547, 0x0000 }, + { 0xf548, 0x0000 }, + { 0xf549, 0x0000 }, + { 0xf54a, 0x0000 }, + { 0xf54b, 0x0000 }, + { 0xf54c, 0x0000 }, + { 0xf54d, 0x0000 }, + { 0xf54e, 0x0000 }, + { 0xf54f, 0x0000 }, + { 0xf550, 0x0000 }, + { 0xf551, 0x0000 }, + { 0xf552, 0x0000 }, + { 0xf553, 0x0000 }, + { 0xf554, 0x0000 }, + { 0xf555, 0x0000 }, + { 0xf556, 0x0000 }, + { 0xf557, 0x0000 }, + { 0xf558, 0x0000 }, + { 0xf559, 0x0000 }, + { 0xf55a, 0x0000 }, + { 0xf55b, 0x0000 }, + { 0xf55c, 0x0000 }, + { 0xf55d, 0x0000 }, + { 0xf55e, 0x0000 }, + { 0xf55f, 0x0000 }, + { 0xf560, 0x0000 }, + { 0xf561, 0x0000 }, + { 0xf562, 0x0000 }, + { 0xf563, 0x0000 }, + { 0xf564, 0x0000 }, + { 0xf565, 0x0000 }, + { 0xf566, 0x0000 }, + { 0xf567, 0x0000 }, + { 0xf568, 0x0000 }, + { 0xf569, 0x0000 }, + { 0xf56a, 0x0000 }, + { 0xf56b, 0x0000 }, + { 0xf56c, 0x0000 }, + { 0xf56d, 0x0000 }, + { 0xf56e, 0x0000 }, + { 0xf56f, 0x0000 }, + { 0xf570, 0x0000 }, + { 0xf571, 0x0000 }, + { 0xf572, 0x0000 }, + { 0xf573, 0x0000 }, + { 0xf574, 0x0000 }, + { 0xf575, 0x0000 }, + { 0xf576, 0x0000 }, + { 0xf577, 0x0000 }, + { 0xf578, 0x0000 }, + { 0xf579, 0x0000 }, + { 0xf57a, 0x0000 }, + { 0xf57b, 0x0000 }, + { 0xf57c, 0x0000 }, + { 0xf57d, 0x0000 }, + { 0xf57e, 0x0000 }, + { 0xf57f, 0x0000 }, + { 0xf580, 0x0000 }, + { 0xf581, 0x0000 }, + { 0xf582, 0x0000 }, + { 0xf583, 0x0000 }, + { 0xf584, 0x0000 }, + { 0xf585, 0x0000 }, + { 0xf586, 0x0000 }, + { 0xf587, 0x0000 }, + { 0xf588, 0x0000 }, + { 0xf589, 0x0000 }, + { 0xf58a, 0x0000 }, + { 0xf58b, 0x0000 }, + { 0xf58c, 0x0000 }, + { 0xf58d, 0x0000 }, + { 0xf58e, 0x0000 }, + { 0xf58f, 0x0000 }, + { 0xf590, 0x0000 }, + { 0xf591, 0x0000 }, + { 0xf592, 0x0000 }, + { 0xf593, 0x0000 }, + { 0xf594, 0x0000 }, + { 0xf595, 0x0000 }, + { 0xf596, 0x0000 }, + { 0xf597, 0x0000 }, + { 0xf598, 0x0000 }, + { 0xf599, 0x0000 }, + { 0xf59a, 0x0000 }, + { 0xf59b, 0x0000 }, + { 0xf59c, 0x0000 }, + { 0xf59d, 0x0000 }, + { 0xf59e, 0x0000 }, + { 0xf59f, 0x0000 }, + { 0xf5a0, 0x0000 }, + { 0xf5a1, 0x0000 }, + { 0xf5a2, 0x0000 }, + { 0xf5a3, 0x0000 }, + { 0xf5a4, 0x0000 }, + { 0xf5a5, 0x0000 }, + { 0xf5a6, 0x0000 }, + { 0xf5a7, 0x0000 }, + { 0xf5a8, 0x0000 }, + { 0xf5a9, 0x0000 }, + { 0xf5aa, 0x0000 }, + { 0xf5ab, 0x0000 }, + { 0xf5ac, 0x0000 }, + { 0xf5ad, 0x0000 }, + { 0xf5ae, 0x0000 }, + { 0xf5af, 0x0000 }, + { 0xf5b0, 0x0000 }, + { 0xf5b1, 0x0000 }, + { 0xf5b2, 0x0000 }, + { 0xf5b3, 0x0000 }, + { 0xf5b4, 0x0000 }, + { 0xf5b5, 0x0000 }, + { 0xf5b6, 0x0000 }, + { 0xf5b7, 0x0000 }, + { 0xf5b8, 0x0000 }, + { 0xf5b9, 0x0000 }, + { 0xf5ba, 0x0000 }, + { 0xf5bb, 0x0000 }, + { 0xf5bc, 0x0000 }, + { 0xf5bd, 0x0000 }, + { 0xf5be, 0x0000 }, + { 0xf5bf, 0x0000 }, + { 0xf5c0, 0x0000 }, + { 0xf5c1, 0x0000 }, + { 0xf5c2, 0x0000 }, + { 0xf5c3, 0x0000 }, + { 0xf5c4, 0x0000 }, + { 0xf5c5, 0x0000 }, + { 0xf5c6, 0x0000 }, + { 0xf5c7, 0x0000 }, + { 0xf5c8, 0x0000 }, + { 0xf5c9, 0x0000 }, + { 0xf5ca, 0x0000 }, + { 0xf5cb, 0x0000 }, + { 0xf5cc, 0x0000 }, + { 0xf5cd, 0x0000 }, + { 0xf5ce, 0x0000 }, + { 0xf5cf, 0x0000 }, + { 0xf5d0, 0x0000 }, + { 0xf5d1, 0x0000 }, + { 0xf5d2, 0x0000 }, + { 0xf5d3, 0x0000 }, + { 0xf5d4, 0x0000 }, + { 0xf5d5, 0x0000 }, + { 0xf5d6, 0x0000 }, + { 0xf5d7, 0x0000 }, + { 0xf5d8, 0x0000 }, + { 0xf5d9, 0x0000 }, + { 0xf5da, 0x0000 }, + { 0xf5db, 0x0000 }, + { 0xf5dc, 0x0000 }, + { 0xf5dd, 0x0000 }, + { 0xf5de, 0x0000 }, + { 0xf5df, 0x0000 }, + { 0xf5e0, 0x0000 }, + { 0xf5e1, 0x0000 }, + { 0xf5e2, 0x0000 }, + { 0xf5e3, 0x0000 }, + { 0xf5e4, 0x0000 }, + { 0xf5e5, 0x0000 }, + { 0xf5e6, 0x0000 }, + { 0xf5e7, 0x0000 }, + { 0xf5e8, 0x0000 }, + { 0xf5e9, 0x0000 }, + { 0xf5ea, 0x0000 }, + { 0xf5eb, 0x0000 }, + { 0xf5ec, 0x0000 }, + { 0xf5ed, 0x0000 }, + { 0xf5ee, 0x0000 }, + { 0xf5ef, 0x0000 }, + { 0xf5f0, 0x0000 }, + { 0xf5f1, 0x0000 }, + { 0xf5f2, 0x0000 }, + { 0xf5f3, 0x0000 }, + { 0xf5f4, 0x0000 }, + { 0xf5f5, 0x0000 }, + { 0xf5f6, 0x0000 }, + { 0xf5f7, 0x0000 }, + { 0xf5f8, 0x0000 }, + { 0xf5f9, 0x0000 }, + { 0xf5fa, 0x0000 }, + { 0xf5fb, 0x0000 }, + { 0xf5fc, 0x0000 }, + { 0xf5fd, 0x0000 }, + { 0xf5fe, 0x0000 }, + { 0xf5ff, 0x0000 }, + { 0xf600, 0x0000 }, + { 0xf601, 0x0000 }, + { 0xf602, 0x0000 }, + { 0xf603, 0x0000 }, + { 0xf604, 0x0000 }, + { 0xf605, 0x0000 }, + { 0xf606, 0x0000 }, + { 0xf607, 0x0000 }, + { 0xf608, 0x0000 }, + { 0xf609, 0x0000 }, + { 0xf60a, 0x0000 }, + { 0xf60b, 0x0000 }, + { 0xf60c, 0x0000 }, + { 0xf60d, 0x0000 }, + { 0xf60e, 0x0000 }, + { 0xf60f, 0x0000 }, + { 0xf610, 0x0000 }, + { 0xf611, 0x0000 }, + { 0xf612, 0x0000 }, + { 0xf613, 0x0000 }, + { 0xf614, 0x0000 }, + { 0xf615, 0x0000 }, + { 0xf616, 0x0000 }, + { 0xf617, 0x0000 }, + { 0xf618, 0x0000 }, + { 0xf619, 0x0000 }, + { 0xf61a, 0x0000 }, + { 0xf61b, 0x0000 }, + { 0xf61c, 0x0000 }, + { 0xf61d, 0x0000 }, + { 0xf61e, 0x0000 }, + { 0xf61f, 0x0000 }, + { 0xf620, 0x0000 }, + { 0xf621, 0x0000 }, + { 0xf622, 0x0000 }, + { 0xf623, 0x0000 }, + { 0xf624, 0x0000 }, + { 0xf625, 0x0000 }, + { 0xf626, 0x0000 }, + { 0xf627, 0x0000 }, + { 0xf628, 0x0000 }, + { 0xf629, 0x0000 }, + { 0xf62a, 0x0000 }, + { 0xf62b, 0x0000 }, + { 0xf62c, 0x0000 }, + { 0xf62d, 0x0000 }, + { 0xf62e, 0x0000 }, + { 0xf62f, 0x0000 }, + { 0xf630, 0x0000 }, + { 0xf631, 0x0000 }, + { 0xf632, 0x0000 }, + { 0xf633, 0x0000 }, + { 0xf634, 0x0000 }, + { 0xf635, 0x0000 }, + { 0xf636, 0x0000 }, + { 0xf637, 0x0000 }, + { 0xf638, 0x0000 }, + { 0xf639, 0x0000 }, + { 0xf63a, 0x0000 }, + { 0xf63b, 0x0000 }, + { 0xf63c, 0x0000 }, + { 0xf63d, 0x0000 }, + { 0xf63e, 0x0000 }, + { 0xf63f, 0x0000 }, + { 0xf640, 0x0000 }, + { 0xf641, 0x0000 }, + { 0xf642, 0x0000 }, + { 0xf643, 0x0000 }, + { 0xf644, 0x0000 }, + { 0xf645, 0x0000 }, + { 0xf646, 0x0000 }, + { 0xf647, 0x0000 }, + { 0xf648, 0x0000 }, + { 0xf649, 0x0000 }, + { 0xf64a, 0x0000 }, + { 0xf64b, 0x0000 }, + { 0xf64c, 0x0000 }, + { 0xf64d, 0x0000 }, + { 0xf64e, 0x0000 }, + { 0xf64f, 0x0000 }, + { 0xf650, 0x0000 }, + { 0xf651, 0x0000 }, + { 0xf652, 0x0000 }, + { 0xf653, 0x0000 }, + { 0xf654, 0x0000 }, + { 0xf655, 0x0000 }, + { 0xf656, 0x0000 }, + { 0xf657, 0x0000 }, + { 0xf658, 0x0000 }, + { 0xf659, 0x0000 }, + { 0xf65a, 0x0000 }, + { 0xf65b, 0x0000 }, + { 0xf65c, 0x0000 }, + { 0xf65d, 0x0000 }, + { 0xf65e, 0x0000 }, + { 0xf65f, 0x0000 }, + { 0xf660, 0x0000 }, + { 0xf661, 0x0000 }, + { 0xf662, 0x0000 }, + { 0xf663, 0x0000 }, + { 0xf664, 0x0000 }, + { 0xf665, 0x0000 }, + { 0xf666, 0x0000 }, + { 0xf667, 0x0000 }, + { 0xf668, 0x0000 }, + { 0xf669, 0x0000 }, + { 0xf66a, 0x0000 }, + { 0xf66b, 0x0000 }, + { 0xf66c, 0x0000 }, + { 0xf66d, 0x0000 }, + { 0xf66e, 0x0000 }, + { 0xf66f, 0x0000 }, + { 0xf670, 0x0000 }, + { 0xf671, 0x0000 }, + { 0xf672, 0x0000 }, + { 0xf673, 0x0000 }, + { 0xf674, 0x0000 }, + { 0xf675, 0x0000 }, + { 0xf676, 0x0000 }, + { 0xf677, 0x0000 }, + { 0xf678, 0x0000 }, + { 0xf679, 0x0000 }, + { 0xf67a, 0x0000 }, + { 0xf67b, 0x0000 }, + { 0xf67c, 0x0000 }, + { 0xf67d, 0x0000 }, + { 0xf67e, 0x0000 }, + { 0xf67f, 0x0000 }, + { 0xf680, 0x0000 }, + { 0xf681, 0x0000 }, + { 0xf682, 0x0000 }, + { 0xf683, 0x0000 }, + { 0xf684, 0x0000 }, + { 0xf685, 0x0000 }, + { 0xf686, 0x0000 }, + { 0xf687, 0x0000 }, + { 0xf688, 0x0000 }, + { 0xf689, 0x0000 }, + { 0xf68a, 0x0000 }, + { 0xf68b, 0x0000 }, + { 0xf68c, 0x0000 }, + { 0xf68d, 0x0000 }, + { 0xf68e, 0x0000 }, + { 0xf68f, 0x0000 }, + { 0xf690, 0x0000 }, + { 0xf691, 0x0000 }, + { 0xf692, 0x0000 }, + { 0xf693, 0x0000 }, + { 0xf694, 0x0000 }, + { 0xf695, 0x0000 }, + { 0xf696, 0x0000 }, + { 0xf697, 0x0000 }, + { 0xf698, 0x0000 }, + { 0xf699, 0x0000 }, + { 0xf69a, 0x0000 }, + { 0xf69b, 0x0000 }, + { 0xf69c, 0x0000 }, + { 0xf69d, 0x0000 }, + { 0xf69e, 0x0000 }, + { 0xf69f, 0x0000 }, + { 0xf6a0, 0x0000 }, + { 0xf6a1, 0x0000 }, + { 0xf6a2, 0x0000 }, + { 0xf6a3, 0x0000 }, + { 0xf6a4, 0x0000 }, + { 0xf6a5, 0x0000 }, + { 0xf6a6, 0x0000 }, + { 0xf6a7, 0x0000 }, + { 0xf6a8, 0x0000 }, + { 0xf6a9, 0x0000 }, + { 0xf6aa, 0x0000 }, + { 0xf6ab, 0x0000 }, + { 0xf6ac, 0x0000 }, + { 0xf6ad, 0x0000 }, + { 0xf6ae, 0x0000 }, + { 0xf6af, 0x0000 }, + { 0xf6b0, 0x0000 }, + { 0xf6b1, 0x0000 }, + { 0xf6b2, 0x0000 }, + { 0xf6b3, 0x0000 }, + { 0xf6b4, 0x0000 }, + { 0xf6b5, 0x0000 }, + { 0xf6b6, 0x0000 }, + { 0xf6b7, 0x0000 }, + { 0xf6b8, 0x0000 }, + { 0xf6b9, 0x0000 }, + { 0xf6ba, 0x0000 }, + { 0xf6bb, 0x0000 }, + { 0xf6bc, 0x0000 }, + { 0xf6bd, 0x0000 }, + { 0xf6be, 0x0000 }, + { 0xf6bf, 0x0000 }, + { 0xf6c0, 0x0000 }, + { 0xf6c1, 0x0000 }, + { 0xf6c2, 0x0000 }, + { 0xf6c3, 0x0000 }, + { 0xf6c4, 0x0000 }, + { 0xf6c5, 0x0000 }, + { 0xf6c6, 0x0000 }, + { 0xf6c7, 0x0000 }, + { 0xf6c8, 0x0000 }, + { 0xf6c9, 0x0000 }, + { 0xf6ca, 0x0000 }, + { 0xf6cb, 0x0000 }, + { 0xf6cc, 0x0000 }, + { 0xf6cd, 0x0000 }, + { 0xf6ce, 0x0000 }, + { 0xf6cf, 0x0000 }, + { 0xf6d0, 0x0000 }, + { 0xf6d1, 0x0000 }, + { 0xf6d2, 0x0000 }, + { 0xf6d3, 0x0000 }, + { 0xf6d4, 0x0000 }, + { 0xf6d5, 0x0000 }, + { 0xf6d6, 0x0000 }, + { 0xf6d7, 0x0000 }, + { 0xf6d8, 0x0000 }, + { 0xf6d9, 0x0000 }, + { 0xf6da, 0x0000 }, + { 0xf6db, 0x0000 }, + { 0xf6dc, 0x0000 }, + { 0xf6dd, 0x0000 }, + { 0xf6de, 0x0000 }, + { 0xf6df, 0x0000 }, + { 0xf6e0, 0x0000 }, + { 0xf6e1, 0x0000 }, + { 0xf6e2, 0x0000 }, + { 0xf6e3, 0x0000 }, + { 0xf6e4, 0x0000 }, + { 0xf6e5, 0x0000 }, + { 0xf6e6, 0x0000 }, + { 0xf6e7, 0x0000 }, + { 0xf6e8, 0x0000 }, + { 0xf6e9, 0x0000 }, + { 0xf6ea, 0x0000 }, + { 0xf6eb, 0x0000 }, + { 0xf6ec, 0x0000 }, + { 0xf6ed, 0x0000 }, + { 0xf6ee, 0x0000 }, + { 0xf6ef, 0x0000 }, + { 0xf6f0, 0x0000 }, + { 0xf6f1, 0x0000 }, + { 0xf6f2, 0x0000 }, + { 0xf6f3, 0x0000 }, + { 0xf6f4, 0x0000 }, + { 0xf6f5, 0x0000 }, + { 0xf6f6, 0x0000 }, + { 0xf6f7, 0x0000 }, + { 0xf6f8, 0x0000 }, + { 0xf6f9, 0x0000 }, + { 0xf6fa, 0x0000 }, + { 0xf6fb, 0x0000 }, + { 0xf6fc, 0x0000 }, + { 0xf6fd, 0x0000 }, + { 0xf6fe, 0x0000 }, + { 0xf6ff, 0x0000 }, + { 0xf700, 0x0000 }, + { 0xf701, 0x0000 }, + { 0xf702, 0x0000 }, + { 0xf703, 0x0000 }, + { 0xf704, 0x0000 }, + { 0xf705, 0x0000 }, + { 0xf706, 0x0000 }, + { 0xf707, 0x0000 }, + { 0xf708, 0x0000 }, + { 0xf709, 0x0000 }, + { 0xf70a, 0x0000 }, + { 0xf70b, 0x0000 }, + { 0xf70c, 0x0000 }, + { 0xf70d, 0x0000 }, + { 0xf70e, 0x0000 }, + { 0xf70f, 0x0000 }, + { 0xf710, 0x0000 }, + { 0xf711, 0x0000 }, + { 0xf712, 0x0000 }, + { 0xf713, 0x0000 }, + { 0xf714, 0x0000 }, + { 0xf715, 0x0000 }, + { 0xf716, 0x0000 }, + { 0xf717, 0x0000 }, + { 0xf718, 0x0000 }, + { 0xf719, 0x0000 }, + { 0xf71a, 0x0000 }, + { 0xf71b, 0x0000 }, + { 0xf71c, 0x0000 }, + { 0xf71d, 0x0000 }, + { 0xf71e, 0x0000 }, + { 0xf71f, 0x0000 }, + { 0xf720, 0x0000 }, + { 0xf721, 0x0000 }, + { 0xf722, 0x0000 }, + { 0xf723, 0x0000 }, + { 0xf724, 0x0000 }, + { 0xf725, 0x0000 }, + { 0xf726, 0x0000 }, + { 0xf727, 0x0000 }, + { 0xf728, 0x0000 }, + { 0xf729, 0x0000 }, + { 0xf72a, 0x0000 }, + { 0xf72b, 0x0000 }, + { 0xf72c, 0x0000 }, + { 0xf72d, 0x0000 }, + { 0xf72e, 0x0000 }, + { 0xf72f, 0x0000 }, + { 0xf730, 0x0000 }, + { 0xf731, 0x0000 }, + { 0xf732, 0x0000 }, + { 0xf733, 0x0000 }, + { 0xf734, 0x0000 }, + { 0xf735, 0x0000 }, + { 0xf736, 0x0000 }, + { 0xf737, 0x0000 }, + { 0xf738, 0x0000 }, + { 0xf739, 0x0000 }, + { 0xf73a, 0x0000 }, + { 0xf73b, 0x0000 }, + { 0xf73c, 0x0000 }, + { 0xf73d, 0x0000 }, + { 0xf73e, 0x0000 }, + { 0xf73f, 0x0000 }, + { 0xf740, 0x0000 }, + { 0xf741, 0x0000 }, + { 0xf742, 0x0000 }, + { 0xf743, 0x0000 }, + { 0xf744, 0x0000 }, + { 0xf745, 0x0000 }, + { 0xf746, 0x0000 }, + { 0xf747, 0x0000 }, + { 0xf748, 0x0000 }, + { 0xf749, 0x0000 }, + { 0xf74a, 0x0000 }, + { 0xf74b, 0x0000 }, + { 0xf74c, 0x0000 }, + { 0xf74d, 0x0000 }, + { 0xf74e, 0x0000 }, + { 0xf74f, 0x0000 }, + { 0xf750, 0x0000 }, + { 0xf751, 0x0000 }, + { 0xf752, 0x0000 }, + { 0xf753, 0x0000 }, + { 0xf754, 0x0000 }, + { 0xf755, 0x0000 }, + { 0xf756, 0x0000 }, + { 0xf757, 0x0000 }, + { 0xf758, 0x0000 }, + { 0xf759, 0x0000 }, + { 0xf75a, 0x0000 }, + { 0xf75b, 0x0000 }, + { 0xf75c, 0x0000 }, + { 0xf75d, 0x0000 }, + { 0xf75e, 0x0000 }, + { 0xf75f, 0x0000 }, + { 0xf760, 0x0000 }, + { 0xf761, 0x0000 }, + { 0xf762, 0x0000 }, + { 0xf763, 0x0000 }, + { 0xf764, 0x0000 }, + { 0xf765, 0x0000 }, + { 0xf766, 0x0000 }, + { 0xf767, 0x0000 }, + { 0xf768, 0x0000 }, + { 0xf769, 0x0000 }, + { 0xf76a, 0x0000 }, + { 0xf76b, 0x0000 }, + { 0xf76c, 0x0000 }, + { 0xf76d, 0x0000 }, + { 0xf76e, 0x0000 }, + { 0xf76f, 0x0000 }, + { 0xf770, 0x0000 }, + { 0xf771, 0x0000 }, + { 0xf772, 0x0000 }, + { 0xf773, 0x0000 }, + { 0xf774, 0x0000 }, + { 0xf775, 0x0000 }, + { 0xf776, 0x0000 }, + { 0xf777, 0x0000 }, + { 0xf778, 0x0000 }, + { 0xf779, 0x0000 }, + { 0xf77a, 0x0000 }, + { 0xf77b, 0x0000 }, + { 0xf77c, 0x0000 }, + { 0xf77d, 0x0000 }, + { 0xf77e, 0x0000 }, + { 0xf77f, 0x0000 }, + { 0xf780, 0x0000 }, + { 0xf781, 0x0000 }, + { 0xf782, 0x0000 }, + { 0xf783, 0x0000 }, + { 0xf784, 0x0000 }, + { 0xf785, 0x0000 }, + { 0xf786, 0x0000 }, + { 0xf787, 0x0000 }, + { 0xf788, 0x0000 }, + { 0xf789, 0x0000 }, + { 0xf78a, 0x0000 }, + { 0xf78b, 0x0000 }, + { 0xf78c, 0x0000 }, + { 0xf78d, 0x0000 }, + { 0xf78e, 0x0000 }, + { 0xf78f, 0x0000 }, + { 0xf790, 0x0000 }, + { 0xf791, 0x0000 }, + { 0xf792, 0x0000 }, + { 0xf793, 0x0000 }, + { 0xf794, 0x0000 }, + { 0xf795, 0x0000 }, + { 0xf796, 0x0000 }, + { 0xf797, 0x0000 }, + { 0xf798, 0x0000 }, + { 0xf799, 0x0000 }, + { 0xf79a, 0x0000 }, + { 0xf79b, 0x0000 }, + { 0xf79c, 0x0000 }, + { 0xf79d, 0x0000 }, + { 0xf79e, 0x0000 }, + { 0xf79f, 0x0000 }, + { 0xf7a0, 0x0000 }, + { 0xf7a1, 0x0000 }, + { 0xf7a2, 0x0000 }, + { 0xf7a3, 0x0000 }, + { 0xf7a4, 0x0000 }, + { 0xf7a5, 0x0000 }, + { 0xf7a6, 0x0000 }, + { 0xf7a7, 0x0000 }, + { 0xf7a8, 0x0000 }, + { 0xf7a9, 0x0000 }, + { 0xf7aa, 0x0000 }, + { 0xf7ab, 0x0000 }, + { 0xf7ac, 0x0000 }, + { 0xf7ad, 0x0000 }, + { 0xf7ae, 0x0000 }, + { 0xf7af, 0x0000 }, + { 0xf7b0, 0x0000 }, + { 0xf7b1, 0x0000 }, + { 0xf7b2, 0x0000 }, + { 0xf7b3, 0x0000 }, + { 0xf7b4, 0x0000 }, + { 0xf7b5, 0x0000 }, + { 0xf7b6, 0x0000 }, + { 0xf7b7, 0x0000 }, + { 0xf7b8, 0x0000 }, + { 0xf7b9, 0x0000 }, + { 0xf7ba, 0x0000 }, + { 0xf7bb, 0x0000 }, + { 0xf7bc, 0x0000 }, + { 0xf7bd, 0x0000 }, + { 0xf7be, 0x0000 }, + { 0xf7bf, 0x0000 }, + { 0xf7c0, 0x0000 }, + { 0xf7c1, 0x0000 }, + { 0xf7c2, 0x0000 }, + { 0xf7c3, 0x0000 }, + { 0xf7c4, 0x0000 }, + { 0xf7c5, 0x0000 }, + { 0xf7c6, 0x0000 }, + { 0xf7c7, 0x0000 }, + { 0xf7c8, 0x0000 }, + { 0xf7c9, 0x0000 }, + { 0xf7ca, 0x0000 }, + { 0xf7cb, 0x0000 }, + { 0xf7cc, 0x0000 }, + { 0xf7cd, 0x0000 }, + { 0xf7ce, 0x0000 }, + { 0xf7cf, 0x0000 }, + { 0xf7d0, 0x0000 }, + { 0xf7d1, 0x0000 }, + { 0xf7d2, 0x0000 }, + { 0xf7d3, 0x0000 }, + { 0xf7d4, 0x0000 }, + { 0xf7d5, 0x0000 }, + { 0xf7d6, 0x0000 }, + { 0xf7d7, 0x0000 }, + { 0xf7d8, 0x0000 }, + { 0xf7d9, 0x0000 }, + { 0xf7da, 0x0000 }, + { 0xf7db, 0x0000 }, + { 0xf7dc, 0x0000 }, + { 0xf7dd, 0x0000 }, + { 0xf7de, 0x0000 }, + { 0xf7df, 0x0000 }, + { 0xf7e0, 0x0000 }, + { 0xf7e1, 0x0000 }, + { 0xf7e2, 0x0000 }, + { 0xf7e3, 0x0000 }, + { 0xf7e4, 0x0000 }, + { 0xf7e5, 0x0000 }, + { 0xf7e6, 0x0000 }, + { 0xf7e7, 0x0000 }, + { 0xf7e8, 0x0000 }, + { 0xf7e9, 0x0000 }, + { 0xf7ea, 0x0000 }, + { 0xf7eb, 0x0000 }, + { 0xf7ec, 0x0000 }, + { 0xf7ed, 0x0000 }, + { 0xf7ee, 0x0000 }, + { 0xf7ef, 0x0000 }, + { 0xf7f0, 0x0000 }, + { 0xf7f1, 0x0000 }, + { 0xf7f2, 0x0000 }, + { 0xf7f3, 0x0000 }, + { 0xf7f4, 0x0000 }, + { 0xf7f5, 0x0000 }, + { 0xf7f6, 0x0000 }, + { 0xf7f7, 0x0000 }, + { 0xf7f8, 0x0000 }, + { 0xf7f9, 0x0000 }, + { 0xf7fa, 0x0000 }, + { 0xf7fb, 0x0000 }, + { 0xf7fc, 0x0000 }, + { 0xf7fd, 0x0000 }, + { 0xf7fe, 0x0000 }, + { 0xf7ff, 0x0000 }, + { 0xf800, 0x0000 }, + { 0xf801, 0x0000 }, + { 0xf802, 0x0000 }, + { 0xf803, 0x0000 }, + { 0xf804, 0x0000 }, + { 0xf805, 0x0000 }, + { 0xf806, 0x0000 }, + { 0xf807, 0x0000 }, + { 0xf808, 0x0000 }, + { 0xf809, 0x0000 }, + { 0xf80a, 0x0000 }, + { 0xf80b, 0x0000 }, + { 0xf80c, 0x0000 }, + { 0xf80d, 0x0000 }, + { 0xf80e, 0x0000 }, + { 0xf80f, 0x0000 }, + { 0xf810, 0x0000 }, + { 0xf811, 0x0000 }, + { 0xf812, 0x0000 }, + { 0xf813, 0x0000 }, + { 0xf814, 0x0000 }, + { 0xf815, 0x0000 }, + { 0xf816, 0x0000 }, + { 0xf817, 0x0000 }, + { 0xf818, 0x0000 }, + { 0xf819, 0x0000 }, + { 0xf81a, 0x0000 }, + { 0xf81b, 0x0000 }, + { 0xf81c, 0x0000 }, + { 0xf81d, 0x0000 }, + { 0xf81e, 0x0000 }, + { 0xf81f, 0x0000 }, + { 0xf820, 0x0000 }, + { 0xf821, 0x0000 }, + { 0xf822, 0x0000 }, + { 0xf823, 0x0000 }, + { 0xf824, 0x0000 }, + { 0xf825, 0x0000 }, + { 0xf826, 0x0000 }, + { 0xf827, 0x0000 }, + { 0xf828, 0x0000 }, + { 0xf829, 0x0000 }, + { 0xf82a, 0x0000 }, + { 0xf82b, 0x0000 }, + { 0xf82c, 0x0000 }, + { 0xf82d, 0x0000 }, + { 0xf82e, 0x0000 }, + { 0xf82f, 0x0000 }, + { 0xf830, 0x0000 }, + { 0xf831, 0x0000 }, + { 0xf832, 0x0000 }, + { 0xf833, 0x0000 }, + { 0xf834, 0x0000 }, + { 0xf835, 0x0000 }, + { 0xf836, 0x0000 }, + { 0xf837, 0x0000 }, + { 0xf838, 0x0000 }, + { 0xf839, 0x0000 }, + { 0xf83a, 0x0000 }, + { 0xf83b, 0x0000 }, + { 0xf83c, 0x0000 }, + { 0xf83d, 0x0000 }, + { 0xf83e, 0x0000 }, + { 0xf83f, 0x0000 }, + { 0xf840, 0x0000 }, + { 0xf841, 0x0000 }, + { 0xf842, 0x0000 }, + { 0xf843, 0x0000 }, + { 0xf844, 0x0000 }, + { 0xf845, 0x0000 }, + { 0xf846, 0x0000 }, + { 0xf847, 0x0000 }, + { 0xf848, 0x0000 }, + { 0xf849, 0x0000 }, + { 0xf84a, 0x0000 }, + { 0xf84b, 0x0000 }, + { 0xf84c, 0x0000 }, + { 0xf84d, 0x0000 }, + { 0xf84e, 0x0000 }, + { 0xf84f, 0x0000 }, + { 0xf850, 0x0000 }, + { 0xf851, 0x0000 }, + { 0xf852, 0x0000 }, + { 0xf853, 0x0000 }, + { 0xf854, 0x0000 }, + { 0xf855, 0x0000 }, + { 0xf856, 0x0000 }, + { 0xf857, 0x0000 }, + { 0xf858, 0x0000 }, + { 0xf859, 0x0000 }, + { 0xf85a, 0x0000 }, + { 0xf85b, 0x0000 }, + { 0xf85c, 0x0000 }, + { 0xf85d, 0x0000 }, + { 0xf85e, 0x0000 }, + { 0xf85f, 0x0000 }, + { 0xf860, 0x0000 }, + { 0xf861, 0x0000 }, + { 0xf862, 0x0000 }, + { 0xf863, 0x0000 }, + { 0xf864, 0x0000 }, + { 0xf865, 0x0000 }, + { 0xf866, 0x0000 }, + { 0xf867, 0x0000 }, + { 0xf868, 0x0000 }, + { 0xf869, 0x0000 }, + { 0xf86a, 0x0000 }, + { 0xf86b, 0x0000 }, + { 0xf86c, 0x0000 }, + { 0xf86d, 0x0000 }, + { 0xf86e, 0x0000 }, + { 0xf86f, 0x0000 }, + { 0xf870, 0x0000 }, + { 0xf871, 0x0000 }, + { 0xf872, 0x0000 }, + { 0xf873, 0x0000 }, + { 0xf874, 0x0000 }, + { 0xf875, 0x0000 }, + { 0xf876, 0x0000 }, + { 0xf877, 0x0000 }, + { 0xf878, 0x0000 }, + { 0xf879, 0x0000 }, + { 0xf87a, 0x0000 }, + { 0xf87b, 0x0000 }, + { 0xf87c, 0x0000 }, + { 0xf87d, 0x0000 }, + { 0xf87e, 0x0000 }, + { 0xf87f, 0x0000 }, + { 0xf880, 0x0000 }, + { 0xf881, 0x0000 }, + { 0xf882, 0x0000 }, + { 0xf883, 0x0000 }, + { 0xf884, 0x0000 }, + { 0xf885, 0x0000 }, + { 0xf886, 0x0000 }, + { 0xf887, 0x0000 }, + { 0xf888, 0x0000 }, + { 0xf889, 0x0000 }, + { 0xf88a, 0x0000 }, + { 0xf88b, 0x0000 }, + { 0xf88c, 0x0000 }, + { 0xf88d, 0x0000 }, + { 0xf88e, 0x0000 }, + { 0xf88f, 0x0000 }, + { 0xf890, 0x0000 }, + { 0xf891, 0x0000 }, + { 0xf892, 0x0000 }, + { 0xf893, 0x0000 }, + { 0xf894, 0x0000 }, + { 0xf895, 0x0000 }, + { 0xf896, 0x0000 }, + { 0xf897, 0x0000 }, + { 0xf898, 0x0000 }, + { 0xf899, 0x0000 }, + { 0xf89a, 0x0000 }, + { 0xf89b, 0x0000 }, + { 0xf89c, 0x0000 }, + { 0xf89d, 0x0000 }, + { 0xf89e, 0x0000 }, + { 0xf89f, 0x0000 }, + { 0xf8a0, 0x0000 }, + { 0xf8a1, 0x0000 }, + { 0xf8a2, 0x0000 }, + { 0xf8a3, 0x0000 }, + { 0xf8a4, 0x0000 }, + { 0xf8a5, 0x0000 }, + { 0xf8a6, 0x0000 }, + { 0xf8a7, 0x0000 }, + { 0xf8a8, 0x0000 }, + { 0xf8a9, 0x0000 }, + { 0xf8aa, 0x0000 }, + { 0xf8ab, 0x0000 }, + { 0xf8ac, 0x0000 }, + { 0xf8ad, 0x0000 }, + { 0xf8ae, 0x0000 }, + { 0xf8af, 0x0000 }, + { 0xf8b0, 0x0000 }, + { 0xf8b1, 0x0000 }, + { 0xf8b2, 0x0000 }, + { 0xf8b3, 0x0000 }, + { 0xf8b4, 0x0000 }, + { 0xf8b5, 0x0000 }, + { 0xf8b6, 0x0000 }, + { 0xf8b7, 0x0000 }, + { 0xf8b8, 0x0000 }, + { 0xf8b9, 0x0000 }, + { 0xf8ba, 0x0000 }, + { 0xf8bb, 0x0000 }, + { 0xf8bc, 0x0000 }, + { 0xf8bd, 0x0000 }, + { 0xf8be, 0x0000 }, + { 0xf8bf, 0x0000 }, + { 0xf8c0, 0x0000 }, + { 0xf8c1, 0x0000 }, + { 0xf8c2, 0x0000 }, + { 0xf8c3, 0x0000 }, + { 0xf8c4, 0x0000 }, + { 0xf8c5, 0x0000 }, + { 0xf8c6, 0x0000 }, + { 0xf8c7, 0x0000 }, + { 0xf8c8, 0x0000 }, + { 0xf8c9, 0x0000 }, + { 0xf8ca, 0x0000 }, + { 0xf8cb, 0x0000 }, + { 0xf8cc, 0x0000 }, + { 0xf8cd, 0x0000 }, + { 0xf8ce, 0x0000 }, + { 0xf8cf, 0x0000 }, + { 0xf8d0, 0x0000 }, + { 0xf8d1, 0x0000 }, + { 0xf8d2, 0x0000 }, + { 0xf8d3, 0x0000 }, + { 0xf8d4, 0x0000 }, + { 0xf8d5, 0x0000 }, + { 0xf8d6, 0x0000 }, + { 0xf8d7, 0x0000 }, + { 0xf8d8, 0x0000 }, + { 0xf8d9, 0x0000 }, + { 0xf8da, 0x0000 }, + { 0xf8db, 0x0000 }, + { 0xf8dc, 0x0000 }, + { 0xf8dd, 0x0000 }, + { 0xf8de, 0x0000 }, + { 0xf8df, 0x0000 }, + { 0xf8e0, 0x0000 }, + { 0xf8e1, 0x0000 }, + { 0xf8e2, 0x0000 }, + { 0xf8e3, 0x0000 }, + { 0xf8e4, 0x0000 }, + { 0xf8e5, 0x0000 }, + { 0xf8e6, 0x0000 }, + { 0xf8e7, 0x0000 }, + { 0xf8e8, 0x0000 }, + { 0xf8e9, 0x0000 }, + { 0xf8ea, 0x0000 }, + { 0xf8eb, 0x0000 }, + { 0xf8ec, 0x0000 }, + { 0xf8ed, 0x0000 }, + { 0xf8ee, 0x0000 }, + { 0xf8ef, 0x0000 }, + { 0xf8f0, 0x0000 }, + { 0xf8f1, 0x0000 }, + { 0xf8f2, 0x0000 }, + { 0xf8f3, 0x0000 }, + { 0xf8f4, 0x0000 }, + { 0xf8f5, 0x0000 }, + { 0xf8f6, 0x0000 }, + { 0xf8f7, 0x0000 }, + { 0xf8f8, 0x0000 }, + { 0xf8f9, 0x0000 }, + { 0xf8fa, 0x0000 }, + { 0xf8fb, 0x0000 }, + { 0xf8fc, 0x0000 }, + { 0xf8fd, 0x0000 }, + { 0xf8fe, 0x0000 }, + { 0xf8ff, 0x0000 }, + { 0xf900, 0x0000 }, + { 0xf901, 0x0000 }, + { 0xf902, 0x0000 }, + { 0xf903, 0x0000 }, + { 0xf904, 0x0000 }, + { 0xf905, 0x0000 }, + { 0xf906, 0x0000 }, + { 0xf907, 0x0000 }, + { 0xf908, 0x0000 }, + { 0xf909, 0x0000 }, + { 0xf90a, 0x0000 }, + { 0xf90b, 0x0000 }, + { 0xf90c, 0x0000 }, + { 0xf90d, 0x0000 }, + { 0xf90e, 0x0000 }, + { 0xf90f, 0x0000 }, + { 0xf910, 0x0000 }, + { 0xf911, 0x0000 }, + { 0xf912, 0x0000 }, + { 0xf913, 0x0000 }, + { 0xf914, 0x0000 }, + { 0xf915, 0x0000 }, + { 0xf916, 0x0000 }, + { 0xf917, 0x0000 }, + { 0xf918, 0x0000 }, + { 0xf919, 0x0000 }, + { 0xf91a, 0x0000 }, + { 0xf91b, 0x0000 }, + { 0xf91c, 0x0000 }, + { 0xf91d, 0x0000 }, + { 0xf91e, 0x0000 }, + { 0xf91f, 0x0000 }, + { 0xf920, 0x0000 }, + { 0xf921, 0x0000 }, + { 0xf922, 0x0000 }, + { 0xf923, 0x0000 }, + { 0xf924, 0x0000 }, + { 0xf925, 0x0000 }, + { 0xf926, 0x0000 }, + { 0xf927, 0x0000 }, + { 0xf928, 0x0000 }, + { 0xf929, 0x0000 }, + { 0xf92a, 0x0000 }, + { 0xf92b, 0x0000 }, + { 0xf92c, 0x0000 }, + { 0xf92d, 0x0000 }, + { 0xf92e, 0x0000 }, + { 0xf92f, 0x0000 }, + { 0xf930, 0x0000 }, + { 0xf931, 0x0000 }, + { 0xf932, 0x0000 }, + { 0xf933, 0x0000 }, + { 0xf934, 0x0000 }, + { 0xf935, 0x0000 }, + { 0xf936, 0x0000 }, + { 0xf937, 0x0000 }, + { 0xf938, 0x0000 }, + { 0xf939, 0x0000 }, + { 0xf93a, 0x0000 }, + { 0xf93b, 0x0000 }, + { 0xf93c, 0x0000 }, + { 0xf93d, 0x0000 }, + { 0xf93e, 0x0000 }, + { 0xf93f, 0x0000 }, + { 0xf940, 0x0000 }, + { 0xf941, 0x0000 }, + { 0xf942, 0x0000 }, + { 0xf943, 0x0000 }, + { 0xf944, 0x0000 }, + { 0xf945, 0x0000 }, + { 0xf946, 0x0000 }, + { 0xf947, 0x0000 }, + { 0xf948, 0x0000 }, + { 0xf949, 0x0000 }, + { 0xf94a, 0x0000 }, + { 0xf94b, 0x0000 }, + { 0xf94c, 0x0000 }, + { 0xf94d, 0x0000 }, + { 0xf94e, 0x0000 }, + { 0xf94f, 0x0000 }, + { 0xf950, 0x0000 }, + { 0xf951, 0x0000 }, + { 0xf952, 0x0000 }, + { 0xf953, 0x0000 }, + { 0xf954, 0x0000 }, + { 0xf955, 0x0000 }, + { 0xf956, 0x0000 }, + { 0xf957, 0x0000 }, + { 0xf958, 0x0000 }, + { 0xf959, 0x0000 }, + { 0xf95a, 0x0000 }, + { 0xf95b, 0x0000 }, + { 0xf95c, 0x0000 }, + { 0xf95d, 0x0000 }, + { 0xf95e, 0x0000 }, + { 0xf95f, 0x0000 }, + { 0xf960, 0x0000 }, + { 0xf961, 0x0000 }, + { 0xf962, 0x0000 }, + { 0xf963, 0x0000 }, + { 0xf964, 0x0000 }, + { 0xf965, 0x0000 }, + { 0xf966, 0x0000 }, + { 0xf967, 0x0000 }, + { 0xf968, 0x0000 }, + { 0xf969, 0x0000 }, + { 0xf96a, 0x0000 }, + { 0xf96b, 0x0000 }, + { 0xf96c, 0x0000 }, + { 0xf96d, 0x0000 }, + { 0xf96e, 0x0000 }, + { 0xf96f, 0x0000 }, + { 0xf970, 0x0000 }, + { 0xf971, 0x0000 }, + { 0xf972, 0x0000 }, + { 0xf973, 0x0000 }, + { 0xf974, 0x0000 }, + { 0xf975, 0x0000 }, + { 0xf976, 0x0000 }, + { 0xf977, 0x0000 }, + { 0xf978, 0x0000 }, + { 0xf979, 0x0000 }, + { 0xf97a, 0x0000 }, + { 0xf97b, 0x0000 }, + { 0xf97c, 0x0000 }, + { 0xf97d, 0x0000 }, + { 0xf97e, 0x0000 }, + { 0xf97f, 0x0000 }, + { 0xf980, 0x0000 }, + { 0xf981, 0x0000 }, + { 0xf982, 0x0000 }, + { 0xf983, 0x0000 }, + { 0xf984, 0x0000 }, + { 0xf985, 0x0000 }, + { 0xf986, 0x0000 }, + { 0xf987, 0x0000 }, + { 0xf988, 0x0000 }, + { 0xf989, 0x0000 }, + { 0xf98a, 0x0000 }, + { 0xf98b, 0x0000 }, + { 0xf98c, 0x0000 }, + { 0xf98d, 0x0000 }, + { 0xf98e, 0x0000 }, + { 0xf98f, 0x0000 }, + { 0xf990, 0x0000 }, + { 0xf991, 0x0000 }, + { 0xf992, 0x0000 }, + { 0xf993, 0x0000 }, + { 0xf994, 0x0000 }, + { 0xf995, 0x0000 }, + { 0xf996, 0x0000 }, + { 0xf997, 0x0000 }, + { 0xf998, 0x0000 }, + { 0xf999, 0x0000 }, + { 0xf99a, 0x0000 }, + { 0xf99b, 0x0000 }, + { 0xf99c, 0x0000 }, + { 0xf99d, 0x0000 }, + { 0xf99e, 0x0000 }, + { 0xf99f, 0x0000 }, + { 0xf9a0, 0x0000 }, + { 0xf9a1, 0x0000 }, + { 0xf9a2, 0x0000 }, + { 0xf9a3, 0x0000 }, + { 0xf9a4, 0x0000 }, + { 0xf9a5, 0x0000 }, + { 0xf9a6, 0x0000 }, + { 0xf9a7, 0x0000 }, + { 0xf9a8, 0x0000 }, + { 0xf9a9, 0x0000 }, + { 0xf9aa, 0x0000 }, + { 0xf9ab, 0x0000 }, + { 0xf9ac, 0x0000 }, + { 0xf9ad, 0x0000 }, + { 0xf9ae, 0x0000 }, + { 0xf9af, 0x0000 }, + { 0xf9b0, 0x0000 }, + { 0xf9b1, 0x0000 }, + { 0xf9b2, 0x0000 }, + { 0xf9b3, 0x0000 }, + { 0xf9b4, 0x0000 }, + { 0xf9b5, 0x0000 }, + { 0xf9b6, 0x0000 }, + { 0xf9b7, 0x0000 }, + { 0xf9b8, 0x0000 }, + { 0xf9b9, 0x0000 }, + { 0xf9ba, 0x0000 }, + { 0xf9bb, 0x0000 }, + { 0xf9bc, 0x0000 }, + { 0xf9bd, 0x0000 }, + { 0xf9be, 0x0000 }, + { 0xf9bf, 0x0000 }, + { 0xf9c0, 0x0000 }, + { 0xf9c1, 0x0000 }, + { 0xf9c2, 0x0000 }, + { 0xf9c3, 0x0000 }, + { 0xf9c4, 0x0000 }, + { 0xf9c5, 0x0000 }, + { 0xf9c6, 0x0000 }, + { 0xf9c7, 0x0000 }, + { 0xf9c8, 0x0000 }, + { 0xf9c9, 0x0000 }, + { 0xf9ca, 0x0000 }, + { 0xf9cb, 0x0000 }, + { 0xf9cc, 0x0000 }, + { 0xf9cd, 0x0000 }, + { 0xf9ce, 0x0000 }, + { 0xf9cf, 0x0000 }, + { 0xf9d0, 0x0000 }, + { 0xf9d1, 0x0000 }, + { 0xf9d2, 0x0000 }, + { 0xf9d3, 0x0000 }, + { 0xf9d4, 0x0000 }, + { 0xf9d5, 0x0000 }, + { 0xf9d6, 0x0000 }, + { 0xf9d7, 0x0000 }, + { 0xf9d8, 0x0000 }, + { 0xf9d9, 0x0000 }, + { 0xf9da, 0x0000 }, + { 0xf9db, 0x0000 }, + { 0xf9dc, 0x0000 }, + { 0xf9dd, 0x0000 }, + { 0xf9de, 0x0000 }, + { 0xf9df, 0x0000 }, + { 0xf9e0, 0x0000 }, + { 0xf9e1, 0x0000 }, + { 0xf9e2, 0x0000 }, + { 0xf9e3, 0x0000 }, + { 0xf9e4, 0x0000 }, + { 0xf9e5, 0x0000 }, + { 0xf9e6, 0x0000 }, + { 0xf9e7, 0x0000 }, + { 0xf9e8, 0x0000 }, + { 0xf9e9, 0x0000 }, + { 0xf9ea, 0x0000 }, + { 0xf9eb, 0x0000 }, + { 0xf9ec, 0x0000 }, + { 0xf9ed, 0x0000 }, + { 0xf9ee, 0x0000 }, + { 0xf9ef, 0x0000 }, + { 0xf9f0, 0x0000 }, + { 0xf9f1, 0x0000 }, + { 0xf9f2, 0x0000 }, + { 0xf9f3, 0x0000 }, + { 0xf9f4, 0x0000 }, + { 0xf9f5, 0x0000 }, + { 0xf9f6, 0x0000 }, + { 0xf9f7, 0x0000 }, + { 0xf9f8, 0x0000 }, + { 0xf9f9, 0x0000 }, + { 0xf9fa, 0x0000 }, + { 0xf9fb, 0x0000 }, + { 0xf9fc, 0x0000 }, + { 0xf9fd, 0x0000 }, + { 0xf9fe, 0x0000 }, + { 0xf9ff, 0x0000 }, + { 0xfa00, 0x0000 }, + { 0xfa01, 0x0000 }, + { 0xfa02, 0x0000 }, + { 0xfa03, 0x0000 }, + { 0xfa04, 0x0000 }, + { 0xfa05, 0x0000 }, + { 0xfa06, 0x0000 }, + { 0xfa07, 0x0000 }, + { 0xfa08, 0x0000 }, + { 0xfa09, 0x0000 }, + { 0xfa0a, 0x0000 }, + { 0xfa0b, 0x0000 }, + { 0xfa0c, 0x0000 }, + { 0xfa0d, 0x0000 }, + { 0xfa0e, 0x0000 }, + { 0xfa0f, 0x0000 }, + { 0xfa10, 0x0000 }, + { 0xfa11, 0x0000 }, + { 0xfa12, 0x0000 }, + { 0xfa13, 0x0000 }, + { 0xfa14, 0x0000 }, + { 0xfa15, 0x0000 }, + { 0xfa16, 0x0000 }, + { 0xfa17, 0x0000 }, + { 0xfa18, 0x0000 }, + { 0xfa19, 0x0000 }, + { 0xfa1a, 0x0000 }, + { 0xfa1b, 0x0000 }, + { 0xfa1c, 0x0000 }, + { 0xfa1d, 0x0000 }, + { 0xfa1e, 0x0000 }, + { 0xfa1f, 0x0000 }, + { 0xfa20, 0x0000 }, + { 0xfa21, 0x0000 }, + { 0xfa22, 0x0000 }, + { 0xfa23, 0x0000 }, + { 0xfa24, 0x0000 }, + { 0xfa25, 0x0000 }, + { 0xfa26, 0x0000 }, + { 0xfa27, 0x0000 }, + { 0xfa28, 0x0000 }, + { 0xfa29, 0x0000 }, + { 0xfa2a, 0x0000 }, + { 0xfa2b, 0x0000 }, + { 0xfa2c, 0x0000 }, + { 0xfa2d, 0x0000 }, + { 0xfa2e, 0x0000 }, + { 0xfa2f, 0x0000 }, + { 0xfa30, 0x0000 }, + { 0xfa31, 0x0000 }, + { 0xfa32, 0x0000 }, + { 0xfa33, 0x0000 }, + { 0xfa34, 0x0000 }, + { 0xfa35, 0x0000 }, + { 0xfa36, 0x0000 }, + { 0xfa37, 0x0000 }, + { 0xfa38, 0x0000 }, + { 0xfa39, 0x0000 }, + { 0xfa3a, 0x0000 }, + { 0xfa3b, 0x0000 }, + { 0xfa3c, 0x0000 }, + { 0xfa3d, 0x0000 }, + { 0xfa3e, 0x0000 }, + { 0xfa3f, 0x0000 }, + { 0xfa40, 0x0000 }, + { 0xfa41, 0x0000 }, + { 0xfa42, 0x0000 }, + { 0xfa43, 0x0000 }, + { 0xfa44, 0x0000 }, + { 0xfa45, 0x0000 }, + { 0xfa46, 0x0000 }, + { 0xfa47, 0x0000 }, + { 0xfa48, 0x0000 }, + { 0xfa49, 0x0000 }, + { 0xfa4a, 0x0000 }, + { 0xfa4b, 0x0000 }, + { 0xfa4c, 0x0000 }, + { 0xfa4d, 0x0000 }, + { 0xfa4e, 0x0000 }, + { 0xfa4f, 0x0000 }, + { 0xfa50, 0x0000 }, + { 0xfa51, 0x0000 }, + { 0xfa52, 0x0000 }, + { 0xfa53, 0x0000 }, + { 0xfa54, 0x0000 }, + { 0xfa55, 0x0000 }, + { 0xfa56, 0x0000 }, + { 0xfa57, 0x0000 }, + { 0xfa58, 0x0000 }, + { 0xfa59, 0x0000 }, + { 0xfa5a, 0x0000 }, + { 0xfa5b, 0x0000 }, + { 0xfa5c, 0x0000 }, + { 0xfa5d, 0x0000 }, + { 0xfa5e, 0x0000 }, + { 0xfa5f, 0x0000 }, + { 0xfa60, 0x0000 }, + { 0xfa61, 0x0000 }, + { 0xfa62, 0x0000 }, + { 0xfa63, 0x0000 }, + { 0xfa64, 0x0000 }, + { 0xfa65, 0x0000 }, + { 0xfa66, 0x0000 }, + { 0xfa67, 0x0000 }, + { 0xfa68, 0x0000 }, + { 0xfa69, 0x0000 }, + { 0xfa6a, 0x0000 }, + { 0xfa6b, 0x0000 }, + { 0xfa6c, 0x0000 }, + { 0xfa6d, 0x0000 }, + { 0xfa6e, 0x0000 }, + { 0xfa6f, 0x0000 }, + { 0xfa70, 0x0000 }, + { 0xfa71, 0x0000 }, + { 0xfa72, 0x0000 }, + { 0xfa73, 0x0000 }, + { 0xfa74, 0x0000 }, + { 0xfa75, 0x0000 }, + { 0xfa76, 0x0000 }, + { 0xfa77, 0x0000 }, + { 0xfa78, 0x0000 }, + { 0xfa79, 0x0000 }, + { 0xfa7a, 0x0000 }, + { 0xfa7b, 0x0000 }, + { 0xfa7c, 0x0000 }, + { 0xfa7d, 0x0000 }, + { 0xfa7e, 0x0000 }, + { 0xfa7f, 0x0000 }, + { 0xfa80, 0x0000 }, + { 0xfa81, 0x0000 }, + { 0xfa82, 0x0000 }, + { 0xfa83, 0x0000 }, + { 0xfa84, 0x0000 }, + { 0xfa85, 0x0000 }, + { 0xfa86, 0x0000 }, + { 0xfa87, 0x0000 }, + { 0xfa88, 0x0000 }, + { 0xfa89, 0x0000 }, + { 0xfa8a, 0x0000 }, + { 0xfa8b, 0x0000 }, + { 0xfa8c, 0x0000 }, + { 0xfa8d, 0x0000 }, + { 0xfa8e, 0x0000 }, + { 0xfa8f, 0x0000 }, + { 0xfa90, 0x0000 }, + { 0xfa91, 0x0000 }, + { 0xfa92, 0x0000 }, + { 0xfa93, 0x0000 }, + { 0xfa94, 0x0000 }, + { 0xfa95, 0x0000 }, + { 0xfa96, 0x0000 }, + { 0xfa97, 0x0000 }, + { 0xfa98, 0x0000 }, + { 0xfa99, 0x0000 }, + { 0xfa9a, 0x0000 }, + { 0xfa9b, 0x0000 }, + { 0xfa9c, 0x0000 }, + { 0xfa9d, 0x0000 }, + { 0xfa9e, 0x0000 }, + { 0xfa9f, 0x0000 }, + { 0xfaa0, 0x0000 }, + { 0xfaa1, 0x0000 }, + { 0xfaa2, 0x0000 }, + { 0xfaa3, 0x0000 }, + { 0xfaa4, 0x0000 }, + { 0xfaa5, 0x0000 }, + { 0xfaa6, 0x0000 }, + { 0xfaa7, 0x0000 }, + { 0xfaa8, 0x0000 }, + { 0xfaa9, 0x0000 }, + { 0xfaaa, 0x0000 }, + { 0xfaab, 0x0000 }, + { 0xfaac, 0x0000 }, + { 0xfaad, 0x0000 }, + { 0xfaae, 0x0000 }, + { 0xfaaf, 0x0000 }, + { 0xfab0, 0x0000 }, + { 0xfab1, 0x0000 }, + { 0xfab2, 0x0000 }, + { 0xfab3, 0x0000 }, + { 0xfab4, 0x0000 }, + { 0xfab5, 0x0000 }, + { 0xfab6, 0x0000 }, + { 0xfab7, 0x0000 }, + { 0xfab8, 0x0000 }, + { 0xfab9, 0x0000 }, + { 0xfaba, 0x0000 }, + { 0xfabb, 0x0000 }, + { 0xfabc, 0x0000 }, + { 0xfabd, 0x0000 }, + { 0xfabe, 0x0000 }, + { 0xfabf, 0x0000 }, + { 0xfac0, 0x0000 }, + { 0xfac1, 0x0000 }, + { 0xfac2, 0x0000 }, + { 0xfac3, 0x0000 }, + { 0xfac4, 0x0000 }, + { 0xfac5, 0x0000 }, + { 0xfac6, 0x0000 }, + { 0xfac7, 0x0000 }, + { 0xfac8, 0x0000 }, + { 0xfac9, 0x0000 }, + { 0xfaca, 0x0000 }, + { 0xfacb, 0x0000 }, + { 0xfacc, 0x0000 }, + { 0xfacd, 0x0000 }, + { 0xface, 0x0000 }, + { 0xfacf, 0x0000 }, + { 0xfad0, 0x0000 }, + { 0xfad1, 0x0000 }, + { 0xfad2, 0x0000 }, + { 0xfad3, 0x0000 }, + { 0xfad4, 0x0000 }, + { 0xfad5, 0x0000 }, + { 0xfad6, 0x0000 }, + { 0xfad7, 0x0000 }, + { 0xfad8, 0x0000 }, + { 0xfad9, 0x0000 }, + { 0xfada, 0x0000 }, + { 0xfadb, 0x0000 }, + { 0xfadc, 0x0000 }, + { 0xfadd, 0x0000 }, + { 0xfade, 0x0000 }, + { 0xfadf, 0x0000 }, + { 0xfae0, 0x0000 }, + { 0xfae1, 0x0000 }, + { 0xfae2, 0x0000 }, + { 0xfae3, 0x0000 }, + { 0xfae4, 0x0000 }, + { 0xfae5, 0x0000 }, + { 0xfae6, 0x0000 }, + { 0xfae7, 0x0000 }, + { 0xfae8, 0x0000 }, + { 0xfae9, 0x0000 }, + { 0xfaea, 0x0000 }, + { 0xfaeb, 0x0000 }, + { 0xfaec, 0x0000 }, + { 0xfaed, 0x0000 }, + { 0xfaee, 0x0000 }, + { 0xfaef, 0x0000 }, + { 0xfaf0, 0x0000 }, + { 0xfaf1, 0x0000 }, + { 0xfaf2, 0x0000 }, + { 0xfaf3, 0x0000 }, + { 0xfaf4, 0x0000 }, + { 0xfaf5, 0x0000 }, + { 0xfaf6, 0x0000 }, + { 0xfaf7, 0x0000 }, + { 0xfaf8, 0x0000 }, + { 0xfaf9, 0x0000 }, + { 0xfafa, 0x0000 }, + { 0xfafb, 0x0000 }, + { 0xfafc, 0x0000 }, + { 0xfafd, 0x0000 }, + { 0xfafe, 0x0000 }, + { 0xfaff, 0x0000 }, + { 0xfb00, 0x0000 }, + { 0xfb01, 0x0000 }, + { 0xfb02, 0x0000 }, + { 0xfb03, 0x0000 }, + { 0xfb04, 0x0000 }, + { 0xfb05, 0x0000 }, + { 0xfb06, 0x0000 }, + { 0xfb07, 0x0000 }, + { 0xfb08, 0x0000 }, + { 0xfb09, 0x0000 }, + { 0xfb0a, 0x0000 }, + { 0xfb0b, 0x0000 }, + { 0xfb0c, 0x0000 }, + { 0xfb0d, 0x0000 }, + { 0xfb0e, 0x0000 }, + { 0xfb0f, 0x0000 }, + { 0xfb10, 0x0000 }, + { 0xfb11, 0x0000 }, + { 0xfb12, 0x0000 }, + { 0xfb13, 0x0000 }, + { 0xfb14, 0x0000 }, + { 0xfb15, 0x0000 }, + { 0xfb16, 0x0000 }, + { 0xfb17, 0x0000 }, + { 0xfb18, 0x0000 }, + { 0xfb19, 0x0000 }, + { 0xfb1a, 0x0000 }, + { 0xfb1b, 0x0000 }, + { 0xfb1c, 0x0000 }, + { 0xfb1d, 0x0000 }, + { 0xfb1e, 0x0000 }, + { 0xfb1f, 0x0000 }, + { 0xfb20, 0x0000 }, + { 0xfb21, 0x0000 }, + { 0xfb22, 0x0000 }, + { 0xfb23, 0x0000 }, + { 0xfb24, 0x0000 }, + { 0xfb25, 0x0000 }, + { 0xfb26, 0x0000 }, + { 0xfb27, 0x0000 }, + { 0xfb28, 0x0000 }, + { 0xfb29, 0x0000 }, + { 0xfb2a, 0x0000 }, + { 0xfb2b, 0x0000 }, + { 0xfb2c, 0x0000 }, + { 0xfb2d, 0x0000 }, + { 0xfb2e, 0x0000 }, + { 0xfb2f, 0x0000 }, + { 0xfb30, 0x0000 }, + { 0xfb31, 0x0000 }, + { 0xfb32, 0x0000 }, + { 0xfb33, 0x0000 }, + { 0xfb34, 0x0000 }, + { 0xfb35, 0x0000 }, + { 0xfb36, 0x0000 }, + { 0xfb37, 0x0000 }, + { 0xfb38, 0x0000 }, + { 0xfb39, 0x0000 }, + { 0xfb3a, 0x0000 }, + { 0xfb3b, 0x0000 }, + { 0xfb3c, 0x0000 }, + { 0xfb3d, 0x0000 }, + { 0xfb3e, 0x0000 }, + { 0xfb3f, 0x0000 }, + { 0xfb40, 0x0000 }, + { 0xfb41, 0x0000 }, + { 0xfb42, 0x0000 }, + { 0xfb43, 0x0000 }, + { 0xfb44, 0x0000 }, + { 0xfb45, 0x0000 }, + { 0xfb46, 0x0000 }, + { 0xfb47, 0x0000 }, + { 0xfb48, 0x0000 }, + { 0xfb49, 0x0000 }, + { 0xfb4a, 0x0000 }, + { 0xfb4b, 0x0000 }, + { 0xfb4c, 0x0000 }, + { 0xfb4d, 0x0000 }, + { 0xfb4e, 0x0000 }, + { 0xfb4f, 0x0000 }, + { 0xfb50, 0x0000 }, + { 0xfb51, 0x0000 }, + { 0xfb52, 0x0000 }, + { 0xfb53, 0x0000 }, + { 0xfb54, 0x0000 }, + { 0xfb55, 0x0000 }, + { 0xfb56, 0x0000 }, + { 0xfb57, 0x0000 }, + { 0xfb58, 0x0000 }, + { 0xfb59, 0x0000 }, + { 0xfb5a, 0x0000 }, + { 0xfb5b, 0x0000 }, + { 0xfb5c, 0x0000 }, + { 0xfb5d, 0x0000 }, + { 0xfb5e, 0x0000 }, + { 0xfb5f, 0x0000 }, + { 0xfb60, 0x0000 }, + { 0xfb61, 0x0000 }, + { 0xfb62, 0x0000 }, + { 0xfb63, 0x0000 }, + { 0xfb64, 0x0000 }, + { 0xfb65, 0x0000 }, + { 0xfb66, 0x0000 }, + { 0xfb67, 0x0000 }, + { 0xfb68, 0x0000 }, + { 0xfb69, 0x0000 }, + { 0xfb6a, 0x0000 }, + { 0xfb6b, 0x0000 }, + { 0xfb6c, 0x0000 }, + { 0xfb6d, 0x0000 }, + { 0xfb6e, 0x0000 }, + { 0xfb6f, 0x0000 }, + { 0xfb70, 0x0000 }, + { 0xfb71, 0x0000 }, + { 0xfb72, 0x0000 }, + { 0xfb73, 0x0000 }, + { 0xfb74, 0x0000 }, + { 0xfb75, 0x0000 }, + { 0xfb76, 0x0000 }, + { 0xfb77, 0x0000 }, + { 0xfb78, 0x0000 }, + { 0xfb79, 0x0000 }, + { 0xfb7a, 0x0000 }, + { 0xfb7b, 0x0000 }, + { 0xfb7c, 0x0000 }, + { 0xfb7d, 0x0000 }, + { 0xfb7e, 0x0000 }, + { 0xfb7f, 0x0000 }, + { 0xfb80, 0x0000 }, + { 0xfb81, 0x0000 }, + { 0xfb82, 0x0000 }, + { 0xfb83, 0x0000 }, + { 0xfb84, 0x0000 }, + { 0xfb85, 0x0000 }, + { 0xfb86, 0x0000 }, + { 0xfb87, 0x0000 }, + { 0xfb88, 0x0000 }, + { 0xfb89, 0x0000 }, + { 0xfb8a, 0x0000 }, + { 0xfb8b, 0x0000 }, + { 0xfb8c, 0x0000 }, + { 0xfb8d, 0x0000 }, + { 0xfb8e, 0x0000 }, + { 0xfb8f, 0x0000 }, + { 0xfb90, 0x0000 }, + { 0xfb91, 0x0000 }, + { 0xfb92, 0x0000 }, + { 0xfb93, 0x0000 }, + { 0xfb94, 0x0000 }, + { 0xfb95, 0x0000 }, + { 0xfb96, 0x0000 }, + { 0xfb97, 0x0000 }, + { 0xfb98, 0x0000 }, + { 0xfb99, 0x0000 }, + { 0xfb9a, 0x0000 }, + { 0xfb9b, 0x0000 }, + { 0xfb9c, 0x0000 }, + { 0xfb9d, 0x0000 }, + { 0xfb9e, 0x0000 }, + { 0xfb9f, 0x0000 }, + { 0xfba0, 0x0000 }, + { 0xfba1, 0x0000 }, + { 0xfba2, 0x0000 }, + { 0xfba3, 0x0000 }, + { 0xfba4, 0x0000 }, + { 0xfba5, 0x0000 }, + { 0xfba6, 0x0000 }, + { 0xfba7, 0x0000 }, + { 0xfba8, 0x0000 }, + { 0xfba9, 0x0000 }, + { 0xfbaa, 0x0000 }, + { 0xfbab, 0x0000 }, + { 0xfbac, 0x0000 }, + { 0xfbad, 0x0000 }, + { 0xfbae, 0x0000 }, + { 0xfbaf, 0x0000 }, + { 0xfbb0, 0x0000 }, + { 0xfbb1, 0x0000 }, + { 0xfbb2, 0x0000 }, + { 0xfbb3, 0x0000 }, + { 0xfbb4, 0x0000 }, + { 0xfbb5, 0x0000 }, + { 0xfbb6, 0x0000 }, + { 0xfbb7, 0x0000 }, + { 0xfbb8, 0x0000 }, + { 0xfbb9, 0x0000 }, + { 0xfbba, 0x0000 }, + { 0xfbbb, 0x0000 }, + { 0xfbbc, 0x0000 }, + { 0xfbbd, 0x0000 }, + { 0xfbbe, 0x0000 }, + { 0xfbbf, 0x0000 }, + { 0xfbc0, 0x0000 }, + { 0xfbc1, 0x0000 }, + { 0xfbc2, 0x0000 }, + { 0xfbc3, 0x0000 }, + { 0xfbc4, 0x0000 }, + { 0xfbc5, 0x0000 }, + { 0xfbc6, 0x0000 }, + { 0xfbc7, 0x0000 }, + { 0xfbc8, 0x0000 }, + { 0xfbc9, 0x0000 }, + { 0xfbca, 0x0000 }, + { 0xfbcb, 0x0000 }, + { 0xfbcc, 0x0000 }, + { 0xfbcd, 0x0000 }, + { 0xfbce, 0x0000 }, + { 0xfbcf, 0x0000 }, + { 0xfbd0, 0x0000 }, + { 0xfbd1, 0x0000 }, + { 0xfbd2, 0x0000 }, + { 0xfbd3, 0x0000 }, + { 0xfbd4, 0x0000 }, + { 0xfbd5, 0x0000 }, + { 0xfbd6, 0x0000 }, + { 0xfbd7, 0x0000 }, + { 0xfbd8, 0x0000 }, + { 0xfbd9, 0x0000 }, + { 0xfbda, 0x0000 }, + { 0xfbdb, 0x0000 }, + { 0xfbdc, 0x0000 }, + { 0xfbdd, 0x0000 }, + { 0xfbde, 0x0000 }, + { 0xfbdf, 0x0000 }, + { 0xfbe0, 0x0000 }, + { 0xfbe1, 0x0000 }, + { 0xfbe2, 0x0000 }, + { 0xfbe3, 0x0000 }, + { 0xfbe4, 0x0000 }, + { 0xfbe5, 0x0000 }, + { 0xfbe6, 0x0000 }, + { 0xfbe7, 0x0000 }, + { 0xfbe8, 0x0000 }, + { 0xfbe9, 0x0000 }, + { 0xfbea, 0x0000 }, + { 0xfbeb, 0x0000 }, + { 0xfbec, 0x0000 }, + { 0xfbed, 0x0000 }, + { 0xfbee, 0x0000 }, + { 0xfbef, 0x0000 }, + { 0xfbf0, 0x0000 }, + { 0xfbf1, 0x0000 }, + { 0xfbf2, 0x0000 }, + { 0xfbf3, 0x0000 }, + { 0xfbf4, 0x0000 }, + { 0xfbf5, 0x0000 }, + { 0xfbf6, 0x0000 }, + { 0xfbf7, 0x0000 }, + { 0xfbf8, 0x0000 }, + { 0xfbf9, 0x0000 }, + { 0xfbfa, 0x0000 }, + { 0xfbfb, 0x0000 }, + { 0xfbfc, 0x0000 }, + { 0xfbfd, 0x0000 }, + { 0xfbfe, 0x0000 }, + { 0xfbff, 0x0000 }, + { 0xfc00, 0x0000 }, + { 0xfc01, 0x0000 }, + { 0xfc02, 0x0000 }, + { 0xfc03, 0x0000 }, + { 0xfc04, 0x0000 }, + { 0xfc05, 0x0000 }, + { 0xfc06, 0x0000 }, + { 0xfc07, 0x0000 }, + { 0xfc08, 0x0000 }, + { 0xfc09, 0x0000 }, + { 0xfc0a, 0x0000 }, + { 0xfc0b, 0x0000 }, + { 0xfc0c, 0x0000 }, + { 0xfc0d, 0x0000 }, + { 0xfc0e, 0x0000 }, + { 0xfc0f, 0x0000 }, + { 0xfc10, 0x0000 }, + { 0xfc11, 0x0000 }, + { 0xfc12, 0x0000 }, + { 0xfc13, 0x0000 }, + { 0xfc14, 0x0000 }, + { 0xfc15, 0x0000 }, + { 0xfc16, 0x0000 }, + { 0xfc17, 0x0000 }, + { 0xfc18, 0x0000 }, + { 0xfc19, 0x0000 }, + { 0xfc1a, 0x0000 }, + { 0xfc1b, 0x0000 }, + { 0xfc1c, 0x0000 }, + { 0xfc1d, 0x0000 }, + { 0xfc1e, 0x0000 }, + { 0xfc1f, 0x0000 }, + { 0xfc20, 0x0000 }, + { 0xfc21, 0x0000 }, + { 0xfc22, 0x0000 }, + { 0xfc23, 0x0000 }, + { 0xfc24, 0x0000 }, + { 0xfc25, 0x0000 }, + { 0xfc26, 0x0000 }, + { 0xfc27, 0x0000 }, + { 0xfc28, 0x0000 }, + { 0xfc29, 0x0000 }, + { 0xfc2a, 0x0000 }, + { 0xfc2b, 0x0000 }, + { 0xfc2c, 0x0000 }, + { 0xfc2d, 0x0000 }, + { 0xfc2e, 0x0000 }, + { 0xfc2f, 0x0000 }, + { 0xfc30, 0x0000 }, + { 0xfc31, 0x0000 }, + { 0xfc32, 0x0000 }, + { 0xfc33, 0x0000 }, + { 0xfc34, 0x0000 }, + { 0xfc35, 0x0000 }, + { 0xfc36, 0x0000 }, + { 0xfc37, 0x0000 }, + { 0xfc38, 0x0000 }, + { 0xfc39, 0x0000 }, + { 0xfc3a, 0x0000 }, + { 0xfc3b, 0x0000 }, + { 0xfc3c, 0x0000 }, + { 0xfc3d, 0x0000 }, + { 0xfc3e, 0x0000 }, + { 0xfc3f, 0x0000 }, + { 0xfc40, 0x0000 }, + { 0xfc41, 0x0000 }, + { 0xfc42, 0x0000 }, + { 0xfc43, 0x0000 }, + { 0xfc44, 0x0000 }, + { 0xfc45, 0x0000 }, + { 0xfc46, 0x0000 }, + { 0xfc47, 0x0000 }, + { 0xfc48, 0x0000 }, + { 0xfc49, 0x0000 }, + { 0xfc4a, 0x0000 }, + { 0xfc4b, 0x0000 }, + { 0xfc4c, 0x0000 }, + { 0xfc4d, 0x0000 }, + { 0xfc4e, 0x0000 }, + { 0xfc4f, 0x0000 }, + { 0xfc50, 0x0000 }, + { 0xfc51, 0x0000 }, + { 0xfc52, 0x0000 }, + { 0xfc53, 0x0000 }, + { 0xfc54, 0x0000 }, + { 0xfc55, 0x0000 }, + { 0xfc56, 0x0000 }, + { 0xfc57, 0x0000 }, + { 0xfc58, 0x0000 }, + { 0xfc59, 0x0000 }, + { 0xfc5a, 0x0000 }, + { 0xfc5b, 0x0000 }, + { 0xfc5c, 0x0000 }, + { 0xfc5d, 0x0000 }, + { 0xfc5e, 0x0000 }, + { 0xfc5f, 0x0000 }, + { 0xfc60, 0x0000 }, + { 0xfc61, 0x0000 }, + { 0xfc62, 0x0000 }, + { 0xfc63, 0x0000 }, + { 0xfc64, 0x0000 }, + { 0xfc65, 0x0000 }, + { 0xfc66, 0x0000 }, + { 0xfc67, 0x0000 }, + { 0xfc68, 0x0000 }, + { 0xfc69, 0x0000 }, + { 0xfc6a, 0x0000 }, + { 0xfc6b, 0x0000 }, + { 0xfc6c, 0x0000 }, + { 0xfc6d, 0x0000 }, + { 0xfc6e, 0x0000 }, + { 0xfc6f, 0x0000 }, + { 0xfc70, 0x0000 }, + { 0xfc71, 0x0000 }, + { 0xfc72, 0x0000 }, + { 0xfc73, 0x0000 }, + { 0xfc74, 0x0000 }, + { 0xfc75, 0x0000 }, + { 0xfc76, 0x0000 }, + { 0xfc77, 0x0000 }, + { 0xfc78, 0x0000 }, + { 0xfc79, 0x0000 }, + { 0xfc7a, 0x0000 }, + { 0xfc7b, 0x0000 }, + { 0xfc7c, 0x0000 }, + { 0xfc7d, 0x0000 }, + { 0xfc7e, 0x0000 }, + { 0xfc7f, 0x0000 }, + { 0xfc80, 0x0000 }, + { 0xfc81, 0x0000 }, + { 0xfc82, 0x0000 }, + { 0xfc83, 0x0000 }, + { 0xfc84, 0x0000 }, + { 0xfc85, 0x0000 }, + { 0xfc86, 0x0000 }, + { 0xfc87, 0x0000 }, + { 0xfc88, 0x0000 }, + { 0xfc89, 0x0000 }, + { 0xfc8a, 0x0000 }, + { 0xfc8b, 0x0000 }, + { 0xfc8c, 0x0000 }, + { 0xfc8d, 0x0000 }, + { 0xfc8e, 0x0000 }, + { 0xfc8f, 0x0000 }, + { 0xfc90, 0x0000 }, + { 0xfc91, 0x0000 }, + { 0xfc92, 0x0000 }, + { 0xfc93, 0x0000 }, + { 0xfc94, 0x0000 }, + { 0xfc95, 0x0000 }, + { 0xfc96, 0x0000 }, + { 0xfc97, 0x0000 }, + { 0xfc98, 0x0000 }, + { 0xfc99, 0x0000 }, + { 0xfc9a, 0x0000 }, + { 0xfc9b, 0x0000 }, + { 0xfc9c, 0x0000 }, + { 0xfc9d, 0x0000 }, + { 0xfc9e, 0x0000 }, + { 0xfc9f, 0x0000 }, + { 0xfca0, 0x0000 }, + { 0xfca1, 0x0000 }, + { 0xfca2, 0x0000 }, + { 0xfca3, 0x0000 }, + { 0xfca4, 0x0000 }, + { 0xfca5, 0x0000 }, + { 0xfca6, 0x0000 }, + { 0xfca7, 0x0000 }, + { 0xfca8, 0x0000 }, + { 0xfca9, 0x0000 }, + { 0xfcaa, 0x0000 }, + { 0xfcab, 0x0000 }, + { 0xfcac, 0x0000 }, + { 0xfcad, 0x0000 }, + { 0xfcae, 0x0000 }, + { 0xfcaf, 0x0000 }, + { 0xfcb0, 0x0000 }, + { 0xfcb1, 0x0000 }, + { 0xfcb2, 0x0000 }, + { 0xfcb3, 0x0000 }, + { 0xfcb4, 0x0000 }, + { 0xfcb5, 0x0000 }, + { 0xfcb6, 0x0000 }, + { 0xfcb7, 0x0000 }, + { 0xfcb8, 0x0000 }, + { 0xfcb9, 0x0000 }, + { 0xfcba, 0x0000 }, + { 0xfcbb, 0x0000 }, + { 0xfcbc, 0x0000 }, + { 0xfcbd, 0x0000 }, + { 0xfcbe, 0x0000 }, + { 0xfcbf, 0x0000 }, + { 0xfcc0, 0x0000 }, + { 0xfcc1, 0x0000 }, + { 0xfcc2, 0x0000 }, + { 0xfcc3, 0x0000 }, + { 0xfcc4, 0x0000 }, + { 0xfcc5, 0x0000 }, + { 0xfcc6, 0x0000 }, + { 0xfcc7, 0x0000 }, + { 0xfcc8, 0x0000 }, + { 0xfcc9, 0x0000 }, + { 0xfcca, 0x0000 }, + { 0xfccb, 0x0000 }, + { 0xfccc, 0x0000 }, + { 0xfccd, 0x0000 }, + { 0xfcce, 0x0000 }, + { 0xfccf, 0x0000 }, + { 0xfcd0, 0x0000 }, + { 0xfcd1, 0x0000 }, + { 0xfcd2, 0x0000 }, + { 0xfcd3, 0x0000 }, + { 0xfcd4, 0x0000 }, + { 0xfcd5, 0x0000 }, + { 0xfcd6, 0x0000 }, + { 0xfcd7, 0x0000 }, + { 0xfcd8, 0x0000 }, + { 0xfcd9, 0x0000 }, + { 0xfcda, 0x0000 }, + { 0xfcdb, 0x0000 }, + { 0xfcdc, 0x0000 }, + { 0xfcdd, 0x0000 }, + { 0xfcde, 0x0000 }, + { 0xfcdf, 0x0000 }, + { 0xfce0, 0x0000 }, + { 0xfce1, 0x0000 }, + { 0xfce2, 0x0000 }, + { 0xfce3, 0x0000 }, + { 0xfce4, 0x0000 }, + { 0xfce5, 0x0000 }, + { 0xfce6, 0x0000 }, + { 0xfce7, 0x0000 }, + { 0xfce8, 0x0000 }, + { 0xfce9, 0x0000 }, + { 0xfcea, 0x0000 }, + { 0xfceb, 0x0000 }, + { 0xfcec, 0x0000 }, + { 0xfced, 0x0000 }, + { 0xfcee, 0x0000 }, + { 0xfcef, 0x0000 }, + { 0xfcf0, 0x0000 }, + { 0xfcf1, 0x0000 }, + { 0xfcf2, 0x0000 }, + { 0xfcf3, 0x0000 }, + { 0xfcf4, 0x0000 }, + { 0xfcf5, 0x0000 }, + { 0xfcf6, 0x0000 }, + { 0xfcf7, 0x0000 }, + { 0xfcf8, 0x0000 }, + { 0xfcf9, 0x0000 }, + { 0xfcfa, 0x0000 }, + { 0xfcfb, 0x0000 }, + { 0xfcfc, 0x0000 }, + { 0xfcfd, 0x0000 }, + { 0xfcfe, 0x0000 }, + { 0xfcff, 0x0000 }, + { 0xfd00, 0x0000 }, + { 0xfd01, 0x0000 }, + { 0xfd02, 0x0000 }, + { 0xfd03, 0x0000 }, + { 0xfd04, 0x0000 }, + { 0xfd05, 0x0000 }, + { 0xfd06, 0x0000 }, + { 0xfd07, 0x0000 }, + { 0xfd08, 0x0000 }, + { 0xfd09, 0x0000 }, + { 0xfd0a, 0x0000 }, + { 0xfd0b, 0x0000 }, + { 0xfd0c, 0x0000 }, + { 0xfd0d, 0x0000 }, + { 0xfd0e, 0x0000 }, + { 0xfd0f, 0x0000 }, + { 0xfd10, 0x0000 }, + { 0xfd11, 0x0000 }, + { 0xfd12, 0x0000 }, + { 0xfd13, 0x0000 }, + { 0xfd14, 0x0000 }, + { 0xfd15, 0x0000 }, + { 0xfd16, 0x0000 }, + { 0xfd17, 0x0000 }, + { 0xfd18, 0x0000 }, + { 0xfd19, 0x0000 }, + { 0xfd1a, 0x0000 }, + { 0xfd1b, 0x0000 }, + { 0xfd1c, 0x0000 }, + { 0xfd1d, 0x0000 }, + { 0xfd1e, 0x0000 }, + { 0xfd1f, 0x0000 }, + { 0xfd20, 0x0000 }, + { 0xfd21, 0x0000 }, + { 0xfd22, 0x0000 }, + { 0xfd23, 0x0000 }, + { 0xfd24, 0x0000 }, + { 0xfd25, 0x0000 }, + { 0xfd26, 0x0000 }, + { 0xfd27, 0x0000 }, + { 0xfd28, 0x0000 }, + { 0xfd29, 0x0000 }, + { 0xfd2a, 0x0000 }, + { 0xfd2b, 0x0000 }, + { 0xfd2c, 0x0000 }, + { 0xfd2d, 0x0000 }, + { 0xfd2e, 0x0000 }, + { 0xfd2f, 0x0000 }, + { 0xfd30, 0x0000 }, + { 0xfd31, 0x0000 }, + { 0xfd32, 0x0000 }, + { 0xfd33, 0x0000 }, + { 0xfd34, 0x0000 }, + { 0xfd35, 0x0000 }, + { 0xfd36, 0x0000 }, + { 0xfd37, 0x0000 }, + { 0xfd38, 0x0000 }, + { 0xfd39, 0x0000 }, + { 0xfd3a, 0x0000 }, + { 0xfd3b, 0x0000 }, + { 0xfd3c, 0x0000 }, + { 0xfd3d, 0x0000 }, + { 0xfd3e, 0x0000 }, + { 0xfd3f, 0x0000 }, + { 0xfd40, 0x0000 }, + { 0xfd41, 0x0000 }, + { 0xfd42, 0x0000 }, + { 0xfd43, 0x0000 }, + { 0xfd44, 0x0000 }, + { 0xfd45, 0x0000 }, + { 0xfd46, 0x0000 }, + { 0xfd47, 0x0000 }, + { 0xfd48, 0x0000 }, + { 0xfd49, 0x0000 }, + { 0xfd4a, 0x0000 }, + { 0xfd4b, 0x0000 }, + { 0xfd4c, 0x0000 }, + { 0xfd4d, 0x0000 }, + { 0xfd4e, 0x0000 }, + { 0xfd4f, 0x0000 }, + { 0xfd50, 0x0000 }, + { 0xfd51, 0x0000 }, + { 0xfd52, 0x0000 }, + { 0xfd53, 0x0000 }, + { 0xfd54, 0x0000 }, + { 0xfd55, 0x0000 }, + { 0xfd56, 0x0000 }, + { 0xfd57, 0x0000 }, + { 0xfd58, 0x0000 }, + { 0xfd59, 0x0000 }, + { 0xfd5a, 0x0000 }, + { 0xfd5b, 0x0000 }, + { 0xfd5c, 0x0000 }, + { 0xfd5d, 0x0000 }, + { 0xfd5e, 0x0000 }, + { 0xfd5f, 0x0000 }, + { 0xfd60, 0x0000 }, + { 0xfd61, 0x0000 }, + { 0xfd62, 0x0000 }, + { 0xfd63, 0x0000 }, + { 0xfd64, 0x0000 }, + { 0xfd65, 0x0000 }, + { 0xfd66, 0x0000 }, + { 0xfd67, 0x0000 }, + { 0xfd68, 0x0000 }, + { 0xfd69, 0x0000 }, + { 0xfd6a, 0x0000 }, + { 0xfd6b, 0x0000 }, + { 0xfd6c, 0x0000 }, + { 0xfd6d, 0x0000 }, + { 0xfd6e, 0x0000 }, + { 0xfd6f, 0x0000 }, + { 0xfd70, 0x0000 }, + { 0xfd71, 0x0000 }, + { 0xfd72, 0x0000 }, + { 0xfd73, 0x0000 }, + { 0xfd74, 0x0000 }, + { 0xfd75, 0x0000 }, + { 0xfd76, 0x0000 }, + { 0xfd77, 0x0000 }, + { 0xfd78, 0x0000 }, + { 0xfd79, 0x0000 }, + { 0xfd7a, 0x0000 }, + { 0xfd7b, 0x0000 }, + { 0xfd7c, 0x0000 }, + { 0xfd7d, 0x0000 }, + { 0xfd7e, 0x0000 }, + { 0xfd7f, 0x0000 }, + { 0xfd80, 0x0000 }, + { 0xfd81, 0x0000 }, + { 0xfd82, 0x0000 }, + { 0xfd83, 0x0000 }, + { 0xfd84, 0x0000 }, + { 0xfd85, 0x0000 }, + { 0xfd86, 0x0000 }, + { 0xfd87, 0x0000 }, + { 0xfd88, 0x0000 }, + { 0xfd89, 0x0000 }, + { 0xfd8a, 0x0000 }, + { 0xfd8b, 0x0000 }, + { 0xfd8c, 0x0000 }, + { 0xfd8d, 0x0000 }, + { 0xfd8e, 0x0000 }, + { 0xfd8f, 0x0000 }, + { 0xfd90, 0x0000 }, + { 0xfd91, 0x0000 }, + { 0xfd92, 0x0000 }, + { 0xfd93, 0x0000 }, + { 0xfd94, 0x0000 }, + { 0xfd95, 0x0000 }, + { 0xfd96, 0x0000 }, + { 0xfd97, 0x0000 }, + { 0xfd98, 0x0000 }, + { 0xfd99, 0x0000 }, + { 0xfd9a, 0x0000 }, + { 0xfd9b, 0x0000 }, + { 0xfd9c, 0x0000 }, + { 0xfd9d, 0x0000 }, + { 0xfd9e, 0x0000 }, + { 0xfd9f, 0x0000 }, + { 0xfda0, 0x0000 }, + { 0xfda1, 0x0000 }, + { 0xfda2, 0x0000 }, + { 0xfda3, 0x0000 }, + { 0xfda4, 0x0000 }, + { 0xfda5, 0x0000 }, + { 0xfda6, 0x0000 }, + { 0xfda7, 0x0000 }, + { 0xfda8, 0x0000 }, + { 0xfda9, 0x0000 }, + { 0xfdaa, 0x0000 }, + { 0xfdab, 0x0000 }, + { 0xfdac, 0x0000 }, + { 0xfdad, 0x0000 }, + { 0xfdae, 0x0000 }, + { 0xfdaf, 0x0000 }, + { 0xfdb0, 0x0000 }, + { 0xfdb1, 0x0000 }, + { 0xfdb2, 0x0000 }, + { 0xfdb3, 0x0000 }, + { 0xfdb4, 0x0000 }, + { 0xfdb5, 0x0000 }, + { 0xfdb6, 0x0000 }, + { 0xfdb7, 0x0000 }, + { 0xfdb8, 0x0000 }, + { 0xfdb9, 0x0000 }, + { 0xfdba, 0x0000 }, + { 0xfdbb, 0x0000 }, + { 0xfdbc, 0x0000 }, + { 0xfdbd, 0x0000 }, + { 0xfdbe, 0x0000 }, + { 0xfdbf, 0x0000 }, + { 0xfdc0, 0x0000 }, + { 0xfdc1, 0x0000 }, + { 0xfdc2, 0x0000 }, + { 0xfdc3, 0x0000 }, + { 0xfdc4, 0x0000 }, + { 0xfdc5, 0x0000 }, + { 0xfdc6, 0x0000 }, + { 0xfdc7, 0x0000 }, + { 0xfdc8, 0x0000 }, + { 0xfdc9, 0x0000 }, + { 0xfdca, 0x0000 }, + { 0xfdcb, 0x0000 }, + { 0xfdcc, 0x0000 }, + { 0xfdcd, 0x0000 }, + { 0xfdce, 0x0000 }, + { 0xfdcf, 0x0000 }, + { 0xfdd0, 0x0000 }, + { 0xfdd1, 0x0000 }, + { 0xfdd2, 0x0000 }, + { 0xfdd3, 0x0000 }, + { 0xfdd4, 0x0000 }, + { 0xfdd5, 0x0000 }, + { 0xfdd6, 0x0000 }, + { 0xfdd7, 0x0000 }, + { 0xfdd8, 0x0000 }, + { 0xfdd9, 0x0000 }, + { 0xfdda, 0x0000 }, + { 0xfddb, 0x0000 }, + { 0xfddc, 0x0000 }, + { 0xfddd, 0x0000 }, + { 0xfdde, 0x0000 }, + { 0xfddf, 0x0000 }, + { 0xfde0, 0x0000 }, + { 0xfde1, 0x0000 }, + { 0xfde2, 0x0000 }, + { 0xfde3, 0x0000 }, + { 0xfde4, 0x0000 }, + { 0xfde5, 0x0000 }, + { 0xfde6, 0x0000 }, + { 0xfde7, 0x0000 }, + { 0xfde8, 0x0000 }, + { 0xfde9, 0x0000 }, + { 0xfdea, 0x0000 }, + { 0xfdeb, 0x0000 }, + { 0xfdec, 0x0000 }, + { 0xfded, 0x0000 }, + { 0xfdee, 0x0000 }, + { 0xfdef, 0x0000 }, + { 0xfdf0, 0x0000 }, + { 0xfdf1, 0x0000 }, + { 0xfdf2, 0x0000 }, + { 0xfdf3, 0x0000 }, + { 0xfdf4, 0x0000 }, + { 0xfdf5, 0x0000 }, + { 0xfdf6, 0x0000 }, + { 0xfdf7, 0x0000 }, + { 0xfdf8, 0x0000 }, + { 0xfdf9, 0x0000 }, + { 0xfdfa, 0x0000 }, + { 0xfdfb, 0x0000 }, + { 0xfdfc, 0x0000 }, + { 0xfdfd, 0x0000 }, + { 0xfdfe, 0x0000 }, + { 0xfdff, 0x0000 }, + { 0xfe00, 0x0000 }, + { 0xfe01, 0x0000 }, + { 0xfe02, 0x0000 }, + { 0xfe03, 0x0000 }, + { 0xfe04, 0x0000 }, + { 0xfe05, 0x0000 }, + { 0xfe06, 0x0000 }, + { 0xfe07, 0x0000 }, + { 0xfe08, 0x0000 }, + { 0xfe09, 0x0000 }, + { 0xfe0a, 0x0000 }, + { 0xfe0b, 0x0000 }, + { 0xfe0c, 0x0000 }, + { 0xfe0d, 0x0000 }, + { 0xfe0e, 0x0000 }, + { 0xfe0f, 0x0000 }, + { 0xfe10, 0x0000 }, + { 0xfe11, 0x0000 }, + { 0xfe12, 0x0000 }, + { 0xfe13, 0x0000 }, + { 0xfe14, 0x0000 }, + { 0xfe15, 0x0000 }, + { 0xfe16, 0x0000 }, + { 0xfe17, 0x0000 }, + { 0xfe18, 0x0000 }, + { 0xfe19, 0x0000 }, + { 0xfe1a, 0x0000 }, + { 0xfe1b, 0x0000 }, + { 0xfe1c, 0x0000 }, + { 0xfe1d, 0x0000 }, + { 0xfe1e, 0x0000 }, + { 0xfe1f, 0x0000 }, + { 0xfe20, 0x0000 }, + { 0xfe21, 0x0000 }, + { 0xfe22, 0x0000 }, + { 0xfe23, 0x0000 }, + { 0xfe24, 0x0000 }, + { 0xfe25, 0x0000 }, + { 0xfe26, 0x0000 }, + { 0xfe27, 0x0000 }, + { 0xfe28, 0x0000 }, + { 0xfe29, 0x0000 }, + { 0xfe2a, 0x0000 }, + { 0xfe2b, 0x0000 }, + { 0xfe2c, 0x0000 }, + { 0xfe2d, 0x0000 }, + { 0xfe2e, 0x0000 }, + { 0xfe2f, 0x0000 }, + { 0xfe30, 0x0000 }, + { 0xfe31, 0x0000 }, + { 0xfe32, 0x0000 }, + { 0xfe33, 0x0000 }, + { 0xfe34, 0x0000 }, + { 0xfe35, 0x0000 }, + { 0xfe36, 0x0000 }, + { 0xfe37, 0x0000 }, + { 0xfe38, 0x0000 }, + { 0xfe39, 0x0000 }, + { 0xfe3a, 0x0000 }, + { 0xfe3b, 0x0000 }, + { 0xfe3c, 0x0000 }, + { 0xfe3d, 0x0000 }, + { 0xfe3e, 0x0000 }, + { 0xfe3f, 0x0000 }, + { 0xfe40, 0x0000 }, + { 0xfe41, 0x0000 }, + { 0xfe42, 0x0000 }, + { 0xfe43, 0x0000 }, + { 0xfe44, 0x0000 }, + { 0xfe45, 0x0000 }, + { 0xfe46, 0x0000 }, + { 0xfe47, 0x0000 }, + { 0xfe48, 0x0000 }, + { 0xfe49, 0x0000 }, + { 0xfe4a, 0x0000 }, + { 0xfe4b, 0x0000 }, + { 0xfe4c, 0x0000 }, + { 0xfe4d, 0x0000 }, + { 0xfe4e, 0x0000 }, + { 0xfe4f, 0x0000 }, + { 0xfe50, 0x0000 }, + { 0xfe51, 0x0000 }, + { 0xfe52, 0x0000 }, + { 0xfe53, 0x0000 }, + { 0xfe54, 0x0000 }, + { 0xfe55, 0x0000 }, + { 0xfe56, 0x0000 }, + { 0xfe57, 0x0000 }, + { 0xfe58, 0x0000 }, + { 0xfe59, 0x0000 }, + { 0xfe5a, 0x0000 }, + { 0xfe5b, 0x0000 }, + { 0xfe5c, 0x0000 }, + { 0xfe5d, 0x0000 }, + { 0xfe5e, 0x0000 }, + { 0xfe5f, 0x0000 }, + { 0xfe60, 0x0000 }, + { 0xfe61, 0x0000 }, + { 0xfe62, 0x0000 }, + { 0xfe63, 0x0000 }, + { 0xfe64, 0x0000 }, + { 0xfe65, 0x0000 }, + { 0xfe66, 0x0000 }, + { 0xfe67, 0x0000 }, + { 0xfe68, 0x0000 }, + { 0xfe69, 0x0000 }, + { 0xfe6a, 0x0000 }, + { 0xfe6b, 0x0000 }, + { 0xfe6c, 0x0000 }, + { 0xfe6d, 0x0000 }, + { 0xfe6e, 0x0000 }, + { 0xfe6f, 0x0000 }, + { 0xfe70, 0x0000 }, + { 0xfe71, 0x0000 }, + { 0xfe72, 0x0000 }, + { 0xfe73, 0x0000 }, + { 0xfe74, 0x0000 }, + { 0xfe75, 0x0000 }, + { 0xfe76, 0x0000 }, + { 0xfe77, 0x0000 }, + { 0xfe78, 0x0000 }, + { 0xfe79, 0x0000 }, + { 0xfe7a, 0x0000 }, + { 0xfe7b, 0x0000 }, + { 0xfe7c, 0x0000 }, + { 0xfe7d, 0x0000 }, + { 0xfe7e, 0x0000 }, + { 0xfe7f, 0x0000 }, + { 0xfe80, 0x0000 }, + { 0xfe81, 0x0000 }, + { 0xfe82, 0x0000 }, + { 0xfe83, 0x0000 }, + { 0xfe84, 0x0000 }, + { 0xfe85, 0x0000 }, + { 0xfe86, 0x0000 }, + { 0xfe87, 0x0000 }, + { 0xfe88, 0x0000 }, + { 0xfe89, 0x0000 }, + { 0xfe8a, 0x0000 }, + { 0xfe8b, 0x0000 }, + { 0xfe8c, 0x0000 }, + { 0xfe8d, 0x0000 }, + { 0xfe8e, 0x0000 }, + { 0xfe8f, 0x0000 }, + { 0xfe90, 0x0000 }, + { 0xfe91, 0x0000 }, + { 0xfe92, 0x0000 }, + { 0xfe93, 0x0000 }, + { 0xfe94, 0x0000 }, + { 0xfe95, 0x0000 }, + { 0xfe96, 0x0000 }, + { 0xfe97, 0x0000 }, + { 0xfe98, 0x0000 }, + { 0xfe99, 0x0000 }, + { 0xfe9a, 0x0000 }, + { 0xfe9b, 0x0000 }, + { 0xfe9c, 0x0000 }, + { 0xfe9d, 0x0000 }, + { 0xfe9e, 0x0000 }, + { 0xfe9f, 0x0000 }, + { 0xfea0, 0x0000 }, + { 0xfea1, 0x0000 }, + { 0xfea2, 0x0000 }, + { 0xfea3, 0x0000 }, + { 0xfea4, 0x0000 }, + { 0xfea5, 0x0000 }, + { 0xfea6, 0x0000 }, + { 0xfea7, 0x0000 }, + { 0xfea8, 0x0000 }, + { 0xfea9, 0x0000 }, + { 0xfeaa, 0x0000 }, + { 0xfeab, 0x0000 }, + { 0xfeac, 0x0000 }, + { 0xfead, 0x0000 }, + { 0xfeae, 0x0000 }, + { 0xfeaf, 0x0000 }, + { 0xfeb0, 0x0000 }, + { 0xfeb1, 0x0000 }, + { 0xfeb2, 0x0000 }, + { 0xfeb3, 0x0000 }, + { 0xfeb4, 0x0000 }, + { 0xfeb5, 0x0000 }, + { 0xfeb6, 0x0000 }, + { 0xfeb7, 0x0000 }, + { 0xfeb8, 0x0000 }, + { 0xfeb9, 0x0000 }, + { 0xfeba, 0x0000 }, + { 0xfebb, 0x0000 }, + { 0xfebc, 0x0000 }, + { 0xfebd, 0x0000 }, + { 0xfebe, 0x0000 }, + { 0xfebf, 0x0000 }, + { 0xfec0, 0x0000 }, + { 0xfec1, 0x0000 }, + { 0xfec2, 0x0000 }, + { 0xfec3, 0x0000 }, + { 0xfec4, 0x0000 }, + { 0xfec5, 0x0000 }, + { 0xfec6, 0x0000 }, + { 0xfec7, 0x0000 }, + { 0xfec8, 0x0000 }, + { 0xfec9, 0x0000 }, + { 0xfeca, 0x0000 }, + { 0xfecb, 0x0000 }, + { 0xfecc, 0x0000 }, + { 0xfecd, 0x0000 }, + { 0xfece, 0x0000 }, + { 0xfecf, 0x0000 }, + { 0xfed0, 0x0000 }, + { 0xfed1, 0x0000 }, + { 0xfed2, 0x0000 }, + { 0xfed3, 0x0000 }, + { 0xfed4, 0x0000 }, + { 0xfed5, 0x0000 }, + { 0xfed6, 0x0000 }, + { 0xfed7, 0x0000 }, + { 0xfed8, 0x0000 }, + { 0xfed9, 0x0000 }, + { 0xfeda, 0x0000 }, + { 0xfedb, 0x0000 }, + { 0xfedc, 0x0000 }, + { 0xfedd, 0x0000 }, + { 0xfede, 0x0000 }, + { 0xfedf, 0x0000 }, + { 0xfee0, 0x0000 }, + { 0xfee1, 0x0000 }, + { 0xfee2, 0x0000 }, + { 0xfee3, 0x0000 }, + { 0xfee4, 0x0000 }, + { 0xfee5, 0x0000 }, + { 0xfee6, 0x0000 }, + { 0xfee7, 0x0000 }, + { 0xfee8, 0x0000 }, + { 0xfee9, 0x0000 }, + { 0xfeea, 0x0000 }, + { 0xfeeb, 0x0000 }, + { 0xfeec, 0x0000 }, + { 0xfeed, 0x0000 }, + { 0xfeee, 0x0000 }, + { 0xfeef, 0x0000 }, + { 0xfef0, 0x0000 }, + { 0xfef1, 0x0000 }, + { 0xfef2, 0x0000 }, + { 0xfef3, 0x0000 }, + { 0xfef4, 0x0000 }, + { 0xfef5, 0x0000 }, + { 0xfef6, 0x0000 }, + { 0xfef7, 0x0000 }, + { 0xfef8, 0x0000 }, + { 0xfef9, 0x0000 }, + { 0xfefa, 0x0000 }, + { 0xfefb, 0x0000 }, + { 0xfefc, 0x0000 }, + { 0xfefd, 0x0000 }, + { 0xfefe, 0x0000 }, + { 0xfeff, 0x0000 }, + { 0xff00, 0x0000 }, + { 0xff01, 0x0000 }, + { 0xff02, 0x0000 }, + { 0xff03, 0x0000 }, + { 0xff04, 0x0000 }, + { 0xff05, 0x0000 }, + { 0xff06, 0x0000 }, + { 0xff07, 0x0000 }, + { 0xff08, 0x0000 }, + { 0xff09, 0x0000 }, + { 0xff0a, 0x0000 }, + { 0xff0b, 0x0000 }, + { 0xff0c, 0x0000 }, + { 0xff0d, 0x0000 }, + { 0xff0e, 0x0000 }, + { 0xff0f, 0x0000 }, + { 0xff10, 0x0000 }, + { 0xff11, 0x0000 }, + { 0xff12, 0x0000 }, + { 0xff13, 0x0000 }, + { 0xff14, 0x0000 }, + { 0xff15, 0x0000 }, + { 0xff16, 0x0000 }, + { 0xff17, 0x0000 }, + { 0xff18, 0x0000 }, + { 0xff19, 0x0000 }, + { 0xff1a, 0x0000 }, + { 0xff1b, 0x0000 }, + { 0xff1c, 0x0000 }, + { 0xff1d, 0x0000 }, + { 0xff1e, 0x0000 }, + { 0xff1f, 0x0000 }, + { 0xff20, 0x0000 }, + { 0xff21, 0x0000 }, + { 0xff22, 0x0000 }, + { 0xff23, 0x0000 }, + { 0xff24, 0x0000 }, + { 0xff25, 0x0000 }, + { 0xff26, 0x0000 }, + { 0xff27, 0x0000 }, + { 0xff28, 0x0000 }, + { 0xff29, 0x0000 }, + { 0xff2a, 0x0000 }, + { 0xff2b, 0x0000 }, + { 0xff2c, 0x0000 }, + { 0xff2d, 0x0000 }, + { 0xff2e, 0x0000 }, + { 0xff2f, 0x0000 }, + { 0xff30, 0x0000 }, + { 0xff31, 0x0000 }, + { 0xff32, 0x0000 }, + { 0xff33, 0x0000 }, + { 0xff34, 0x0000 }, + { 0xff35, 0x0000 }, + { 0xff36, 0x0000 }, + { 0xff37, 0x0000 }, + { 0xff38, 0x0000 }, + { 0xff39, 0x0000 }, + { 0xff3a, 0x0000 }, + { 0xff3b, 0x0000 }, + { 0xff3c, 0x0000 }, + { 0xff3d, 0x0000 }, + { 0xff3e, 0x0000 }, + { 0xff3f, 0x0000 }, + { 0xff40, 0x0000 }, + { 0xff41, 0x0000 }, + { 0xff42, 0x0000 }, + { 0xff43, 0x0000 }, + { 0xff44, 0x0000 }, + { 0xff45, 0x0000 }, + { 0xff46, 0x0000 }, + { 0xff47, 0x0000 }, + { 0xff48, 0x0000 }, + { 0xff49, 0x0000 }, + { 0xff4a, 0x0000 }, + { 0xff4b, 0x0000 }, + { 0xff4c, 0x0000 }, + { 0xff4d, 0x0000 }, + { 0xff4e, 0x0000 }, + { 0xff4f, 0x0000 }, + { 0xff50, 0x0000 }, + { 0xff51, 0x0000 }, + { 0xff52, 0x0000 }, + { 0xff53, 0x0000 }, + { 0xff54, 0x0000 }, + { 0xff55, 0x0000 }, + { 0xff56, 0x0000 }, + { 0xff57, 0x0000 }, + { 0xff58, 0x0000 }, + { 0xff59, 0x0000 }, + { 0xff5a, 0x0000 }, + { 0xff5b, 0x0000 }, + { 0xff5c, 0x0000 }, + { 0xff5d, 0x0000 }, + { 0xff5e, 0x0000 }, + { 0xff5f, 0x0000 }, + { 0xff60, 0x0000 }, + { 0xff61, 0x0000 }, + { 0xff62, 0x0000 }, + { 0xff63, 0x0000 }, + { 0xff64, 0x0000 }, + { 0xff65, 0x0000 }, + { 0xff66, 0x0000 }, + { 0xff67, 0x0000 }, + { 0xff68, 0x0000 }, + { 0xff69, 0x0000 }, + { 0xff6a, 0x0000 }, + { 0xff6b, 0x0000 }, + { 0xff6c, 0x0000 }, + { 0xff6d, 0x0000 }, + { 0xff6e, 0x0000 }, + { 0xff6f, 0x0000 }, + { 0xff70, 0x0000 }, + { 0xff71, 0x0000 }, + { 0xff72, 0x0000 }, + { 0xff73, 0x0000 }, + { 0xff74, 0x0000 }, + { 0xff75, 0x0000 }, + { 0xff76, 0x0000 }, + { 0xff77, 0x0000 }, + { 0xff78, 0x0000 }, + { 0xff79, 0x0000 }, + { 0xff7a, 0x0000 }, + { 0xff7b, 0x0000 }, + { 0xff7c, 0x0000 }, + { 0xff7d, 0x0000 }, + { 0xff7e, 0x0000 }, + { 0xff7f, 0x0000 }, + { 0xff80, 0x0000 }, + { 0xff81, 0x0000 }, + { 0xff82, 0x0000 }, + { 0xff83, 0x0000 }, + { 0xff84, 0x0000 }, + { 0xff85, 0x0000 }, + { 0xff86, 0x0000 }, + { 0xff87, 0x0000 }, + { 0xff88, 0x0000 }, + { 0xff89, 0x0000 }, + { 0xff8a, 0x0000 }, + { 0xff8b, 0x0000 }, + { 0xff8c, 0x0000 }, + { 0xff8d, 0x0000 }, + { 0xff8e, 0x0000 }, + { 0xff8f, 0x0000 }, + { 0xff90, 0x0000 }, + { 0xff91, 0x0000 }, + { 0xff92, 0x0000 }, + { 0xff93, 0x0000 }, + { 0xff94, 0x0000 }, + { 0xff95, 0x0000 }, + { 0xff96, 0x0000 }, + { 0xff97, 0x0000 }, + { 0xff98, 0x0000 }, + { 0xff99, 0x0000 }, + { 0xff9a, 0x0000 }, + { 0xff9b, 0x0000 }, + { 0xff9c, 0x0000 }, + { 0xff9d, 0x0000 }, + { 0xff9e, 0x0000 }, + { 0xff9f, 0x0000 }, + { 0xffa0, 0x0000 }, + { 0xffa1, 0x0000 }, + { 0xffa2, 0x0000 }, + { 0xffa3, 0x0000 }, + { 0xffa4, 0x0000 }, + { 0xffa5, 0x0000 }, + { 0xffa6, 0x0000 }, + { 0xffa7, 0x0000 }, + { 0xffa8, 0x0000 }, + { 0xffa9, 0x0000 }, + { 0xffaa, 0x0000 }, + { 0xffab, 0x0000 }, + { 0xffac, 0x0000 }, + { 0xffad, 0x0000 }, + { 0xffae, 0x0000 }, + { 0xffaf, 0x0000 }, + { 0xffb0, 0x0000 }, + { 0xffb1, 0x0000 }, + { 0xffb2, 0x0000 }, + { 0xffb3, 0x0000 }, + { 0xffb4, 0x0000 }, + { 0xffb5, 0x0000 }, + { 0xffb6, 0x0000 }, + { 0xffb7, 0x0000 }, + { 0xffb8, 0x0000 }, + { 0xffb9, 0x0000 }, + { 0xffba, 0x0000 }, + { 0xffbb, 0x0000 }, + { 0xffbc, 0x0000 }, + { 0xffbd, 0x0000 }, + { 0xffbe, 0x0000 }, + { 0xffbf, 0x0000 }, + { 0xffc0, 0x0000 }, + { 0xffc1, 0x0000 }, + { 0xffc2, 0x0000 }, + { 0xffc3, 0x0000 }, + { 0xffc4, 0x0000 }, + { 0xffc5, 0x0000 }, + { 0xffc6, 0x0000 }, + { 0xffc7, 0x0000 }, + { 0xffc8, 0x0000 }, + { 0xffc9, 0x0000 }, + { 0xffca, 0x0000 }, + { 0xffcb, 0x0000 }, + { 0xffcc, 0x0000 }, + { 0xffcd, 0x0000 }, + { 0xffce, 0x0000 }, + { 0xffcf, 0x0000 }, + { 0xffd0, 0x0000 }, + { 0xffd1, 0x0000 }, + { 0xffd2, 0x0000 }, + { 0xffd3, 0x0000 }, + { 0xffd4, 0x0000 }, + { 0xffd5, 0x0000 }, + { 0xffd6, 0x0000 }, + { 0xffd7, 0x0000 }, + { 0xffd8, 0x0000 }, + { 0xffd9, 0x0000 }, + { 0xffda, 0x0000 }, + { 0xffdb, 0x0000 }, + { 0xffdc, 0x0000 }, + { 0xffdd, 0x0000 }, + { 0xffde, 0x0000 }, + { 0xffdf, 0x0000 }, + { 0xffe0, 0x0000 }, + { 0xffe1, 0x0000 }, + { 0xffe2, 0x0000 }, + { 0xffe3, 0x0000 }, + { 0xffe4, 0x0000 }, + { 0xffe5, 0x0000 }, + { 0xffe6, 0x0000 }, + { 0xffe7, 0x0000 }, + { 0xffe8, 0x0000 }, + { 0xffe9, 0x0000 }, + { 0xffea, 0x0000 }, + { 0xffeb, 0x0000 }, + { 0xffec, 0x0000 }, + { 0xffed, 0x0000 }, + { 0xffee, 0x0000 }, + { 0xffef, 0x0000 }, + { 0xfff0, 0x0000 }, + { 0xfff1, 0x0000 }, + { 0xfff2, 0x0000 }, + { 0xfff3, 0x0000 }, + { 0xfff4, 0x0000 }, + { 0xfff5, 0x0000 }, + { 0xfff6, 0x0000 }, + { 0xfff7, 0x0000 }, + { 0xfff8, 0x0000 }, + { 0xfff9, 0x0000 }, + { 0xfffa, 0x0000 }, + { 0xfffb, 0x0000 }, + { 0xfffc, 0x0000 }, + { 0xfffd, 0x0000 }, + { 0xfffe, 0x0000 }, + { 0xffff, 0x0000 }, + +}; + + +#endif /* __RT700_H__ */ diff --git a/sound/soc/codecs/rt700.c b/sound/soc/codecs/rt700.c new file mode 100644 index 000000000000..8cb67827a8db --- /dev/null +++ b/sound/soc/codecs/rt700.c @@ -0,0 +1,1600 @@ +/* + * rt700.c -- rt700 ALSA SoC audio driver + * + * Copyright 2016 Realtek, Inc. + * + * Author: Bard Liao + * ALC700 ASoC Codec Driver based Intel Dummy SdW codec driver + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +#define DEBUG +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "rt700.h" + +struct sdw_stream_data { + int stream_tag; +}; + +struct hda_cmd { + u16 vid; + u8 nid; + u16 payload; +}; + +static struct hda_cmd hda_dump_list[] = { + /* vid, nid, payload */ + {0xf00, 0x00, 0x00}, /* Vendor ID */ +#if 0 + {0xf00, 0x00, 0x02}, /* Revision ID */ + {0xf00, 0x00, 0x04}, /* Subordinate Node Count */ + {0xf00, 0x01, 0x04}, /* Subordinate Node Count */ + {0xf00, 0x01, 0x05}, /* Function Group Type */ + {0xf00, 0x01, 0x08}, /* Audio Function Capabilities */ + {0xf00, 0x00, 0x09}, /* Audio Widget Capabilities */ + {0xf00, 0x01, 0x09}, /* Audio Widget Capabilities */ + {0xf00, 0x02, 0x09}, /* Audio Widget Capabilities */ + {0xf00, 0x03, 0x09}, /* Audio Widget Capabilities */ + {0xf00, 0x04, 0x09}, /* Audio Widget Capabilities */ + {0xf00, 0x05, 0x09}, /* Audio Widget Capabilities */ + {0xf00, 0x06, 0x09}, /* Audio Widget Capabilities */ + {0xf00, 0x07, 0x09}, /* Audio Widget Capabilities */ + {0xf00, 0x08, 0x09}, /* Audio Widget Capabilities */ + {0xf00, 0x09, 0x09}, /* Audio Widget Capabilities */ + {0xf00, 0x0a, 0x09}, /* Audio Widget Capabilities */ + {0xf00, 0x27, 0x09}, /* Audio Widget Capabilities */ + {0xf00, 0x22, 0x09}, /* Audio Widget Capabilities */ + {0xf00, 0x23, 0x09}, /* Audio Widget Capabilities */ + {0xf00, 0x24, 0x09}, /* Audio Widget Capabilities */ + {0xf00, 0x25, 0x09}, /* Audio Widget Capabilities */ + {0xf00, 0x14, 0x09}, /* Audio Widget Capabilities */ + {0xf00, 0x15, 0x09}, /* Audio Widget Capabilities */ + {0xf00, 0x17, 0x09}, /* Audio Widget Capabilities */ + {0xf00, 0x18, 0x09}, /* Audio Widget Capabilities */ + {0xf00, 0x29, 0x09}, /* Audio Widget Capabilities */ + {0xf00, 0x19, 0x09}, /* Audio Widget Capabilities */ + {0xf00, 0x1a, 0x09}, /* Audio Widget Capabilities */ + {0xf00, 0x1b, 0x09}, /* Audio Widget Capabilities */ + {0xf00, 0x16, 0x09}, /* Audio Widget Capabilities */ + {0xf00, 0x1d, 0x09}, /* Audio Widget Capabilities */ + {0xf00, 0x21, 0x09}, /* Audio Widget Capabilities */ + {0xf00, 0x1e, 0x09}, /* Audio Widget Capabilities */ + {0xf00, 0x1f, 0x09}, /* Audio Widget Capabilities */ + {0xf00, 0x12, 0x09}, /* Audio Widget Capabilities */ + {0xf00, 0x13, 0x09}, /* Audio Widget Capabilities */ + {0xf00, 0x20, 0x09}, /* Audio Widget Capabilities */ + {0xf00, 0x01, 0x0a}, /* Supported PCM Size, Rates */ + {0xf00, 0x02, 0x0a}, /* Supported PCM Size, Rates */ + {0xf00, 0x03, 0x0a}, /* Supported PCM Size, Rates */ + {0xf00, 0x04, 0x0a}, /* Supported PCM Size, Rates */ + {0xf00, 0x05, 0x0a}, /* Supported PCM Size, Rates */ + {0xf00, 0x06, 0x0a}, /* Supported PCM Size, Rates */ + {0xf00, 0x07, 0x0a}, /* Supported PCM Size, Rates */ + {0xf00, 0x08, 0x0a}, /* Supported PCM Size, Rates */ + {0xf00, 0x09, 0x0a}, /* Supported PCM Size, Rates */ + {0xf00, 0x0a, 0x0a}, /* Supported PCM Size, Rates */ + {0xf00, 0x27, 0x0a}, /* Supported PCM Size, Rates */ + {0xf00, 0x01, 0x0b}, /* Supported Stream Formats */ + {0xf00, 0x02, 0x0b}, /* Supported Stream Formats */ + {0xf00, 0x03, 0x0b}, /* Supported Stream Formats */ + {0xf00, 0x04, 0x0b}, /* Supported Stream Formats */ + {0xf00, 0x05, 0x0b}, /* Supported Stream Formats */ + {0xf00, 0x06, 0x0b}, /* Supported Stream Formats */ + {0xf00, 0x07, 0x0b}, /* Supported Stream Formats */ + {0xf00, 0x08, 0x0b}, /* Supported Stream Formats */ + {0xf00, 0x09, 0x0b}, /* Supported Stream Formats */ + {0xf00, 0x0a, 0x0b}, /* Supported Stream Formats */ + {0xf00, 0x27, 0x0b}, /* Supported Stream Formats */ + {0xf00, 0x12, 0x0c}, /* Pin Capabilities */ + {0xf00, 0x13, 0x0c}, /* Pin Capabilities */ + {0xf00, 0x14, 0x0c}, /* Pin Capabilities */ + {0xf00, 0x15, 0x0c}, /* Pin Capabilities */ + {0xf00, 0x16, 0x0c}, /* Pin Capabilities */ + {0xf00, 0x17, 0x0c}, /* Pin Capabilities */ + {0xf00, 0x18, 0x0c}, /* Pin Capabilities */ + {0xf00, 0x19, 0x0c}, /* Pin Capabilities */ + {0xf00, 0x1a, 0x0c}, /* Pin Capabilities */ + {0xf00, 0x1b, 0x0c}, /* Pin Capabilities */ + {0xf00, 0x1d, 0x0c}, /* Pin Capabilities */ + {0xf00, 0x1e, 0x0c}, /* Pin Capabilities */ + {0xf00, 0x1f, 0x0c}, /* Pin Capabilities */ + {0xf00, 0x21, 0x0c}, /* Pin Capabilities */ + {0xf00, 0x29, 0x0c}, /* Pin Capabilities */ + {0xf00, 0x07, 0x0d}, /* Amplifier Capabilities */ + {0xf00, 0x08, 0x0d}, /* Amplifier Capabilities */ + {0xf00, 0x09, 0x0d}, /* Amplifier Capabilities */ + {0xf00, 0x12, 0x0d}, /* Amplifier Capabilities */ + {0xf00, 0x13, 0x0d}, /* Amplifier Capabilities */ + {0xf00, 0x18, 0x0d}, /* Amplifier Capabilities */ + {0xf00, 0x19, 0x0d}, /* Amplifier Capabilities */ + {0xf00, 0x1a, 0x0d}, /* Amplifier Capabilities */ + {0xf00, 0x1b, 0x0d}, /* Amplifier Capabilities */ + {0xf00, 0x22, 0x0d}, /* Amplifier Capabilities */ + {0xf00, 0x23, 0x0d}, /* Amplifier Capabilities */ + {0xf00, 0x24, 0x0d}, /* Amplifier Capabilities */ + {0xf00, 0x25, 0x0d}, /* Amplifier Capabilities */ + {0xf00, 0x29, 0x0d}, /* Amplifier Capabilities */ + {0xf00, 0x02, 0x12}, /* Output Amplifiers */ + {0xf00, 0x03, 0x12}, /* Output Amplifiers */ + {0xf00, 0x14, 0x12}, /* Output Amplifiers */ + {0xf00, 0x15, 0x12}, /* Output Amplifiers */ + {0xf00, 0x16, 0x12}, /* Output Amplifiers */ + {0xf00, 0x17, 0x12}, /* Output Amplifiers */ + {0xf00, 0x1a, 0x12}, /* Output Amplifiers */ + {0xf00, 0x1b, 0x12}, /* Output Amplifiers */ + {0xf00, 0x21, 0x12}, /* Output Amplifiers */ + {0xf00, 0x07, 0x0e}, /* Connect List Length */ + {0xf00, 0x08, 0x0e}, /* Connect List Length */ + {0xf00, 0x09, 0x0e}, /* Connect List Length */ + {0xf00, 0x0a, 0x0e}, /* Connect List Length */ + {0xf00, 0x27, 0x0e}, /* Connect List Length */ + {0xf00, 0x22, 0x0e}, /* Connect List Length */ + {0xf00, 0x23, 0x0e}, /* Connect List Length */ + {0xf00, 0x24, 0x0e}, /* Connect List Length */ + {0xf00, 0x25, 0x0e}, /* Connect List Length */ + {0xf00, 0x14, 0x0e}, /* Connect List Length */ + {0xf00, 0x15, 0x0e}, /* Connect List Length */ + {0xf00, 0x16, 0x0e}, /* Connect List Length */ + {0xf00, 0x17, 0x0e}, /* Connect List Length */ + {0xf00, 0x18, 0x0e}, /* Connect List Length */ + {0xf00, 0x19, 0x0e}, /* Connect List Length */ + {0xf00, 0x1a, 0x0e}, /* Connect List Length */ + {0xf00, 0x1b, 0x0e}, /* Connect List Length */ + {0xf00, 0x1d, 0x0e}, /* Connect List Length */ + {0xf00, 0x21, 0x0e}, /* Connect List Length */ + {0xf00, 0x1e, 0x0e}, /* Connect List Length */ + {0xf00, 0x1f, 0x0e}, /* Connect List Length */ + {0xf00, 0x12, 0x0e}, /* Connect List Length */ + {0xf00, 0x13, 0x0e}, /* Connect List Length */ + {0xf00, 0x29, 0x0e}, /* Connect List Length */ + {0xf00, 0x01, 0x0f}, /* Supported Power States */ + {0xf00, 0x02, 0x0f}, /* Supported Power States */ + {0xf00, 0x03, 0x0f}, /* Supported Power States */ + {0xf00, 0x04, 0x0f}, /* Supported Power States */ + {0xf00, 0x05, 0x0f}, /* Supported Power States */ + {0xf00, 0x06, 0x0f}, /* Supported Power States */ + {0xf00, 0x07, 0x0f}, /* Supported Power States */ + {0xf00, 0x08, 0x0f}, /* Supported Power States */ + {0xf00, 0x09, 0x0f}, /* Supported Power States */ + {0xf00, 0x0a, 0x0f}, /* Supported Power States */ + {0xf00, 0x27, 0x0f}, /* Supported Power States */ + {0xf00, 0x22, 0x0f}, /* Supported Power States */ + {0xf00, 0x23, 0x0f}, /* Supported Power States */ + {0xf00, 0x24, 0x0f}, /* Supported Power States */ + {0xf00, 0x21, 0x0f}, /* Supported Power States */ + {0xf00, 0x12, 0x0f}, /* Supported Power States */ + {0xf00, 0x13, 0x0f}, /* Supported Power States */ + {0xf00, 0x14, 0x0f}, /* Supported Power States */ + {0xf00, 0x15, 0x0f}, /* Supported Power States */ + {0xf00, 0x16, 0x0f}, /* Supported Power States */ + {0xf00, 0x17, 0x0f}, /* Supported Power States */ + {0xf00, 0x18, 0x0f}, /* Supported Power States */ + {0xf00, 0x19, 0x0f}, /* Supported Power States */ + {0xf00, 0x1a, 0x0f}, /* Supported Power States */ + {0xf00, 0x1b, 0x0f}, /* Supported Power States */ + {0xf00, 0x1d, 0x0f}, /* Supported Power States */ + {0xf00, 0x1e, 0x0f}, /* Supported Power States */ + {0xf00, 0x1f, 0x0f}, /* Supported Power States */ + {0xf00, 0x29, 0x0f}, /* Supported Power States */ + {0xf00, 0x0b, 0x0f}, /* Supported Power States */ + {0xf00, 0x0c, 0x0f}, /* Supported Power States */ + {0xf00, 0x0d, 0x0f}, /* Supported Power States */ + {0xf00, 0x0e, 0x0f}, /* Supported Power States */ + {0xf00, 0x0f, 0x0f}, /* Supported Power States */ + {0xf00, 0x10, 0x0f}, /* Supported Power States */ + {0xf00, 0x11, 0x0f}, /* Supported Power States */ + {0xf00, 0x1c, 0x0f}, /* Supported Power States */ + {0xf00, 0x20, 0x0f}, /* Supported Power States */ + {0xf00, 0x26, 0x0f}, /* Supported Power States */ + {0xf00, 0x28, 0x0f}, /* Supported Power States */ + {0xf00, 0x20, 0x10}, /* Processing Capabilities */ + {0xf00, 0x53, 0x10}, /* Processing Capabilities */ + {0xf00, 0x54, 0x10}, /* Processing Capabilities */ + {0xf00, 0x55, 0x10}, /* Processing Capabilities */ + {0xf00, 0x56, 0x10}, /* Processing Capabilities */ + {0xf00, 0x57, 0x10}, /* Processing Capabilities */ + {0xf00, 0x58, 0x10}, /* Processing Capabilities */ + {0xf00, 0x5b, 0x10}, /* Processing Capabilities */ + {0xf00, 0x60, 0x10}, /* Processing Capabilities */ + {0xf00, 0x01, 0x11}, /* GPIO Capabilities */ + {0xf00, 0x00, 0x16}, /* Statically Switchable BCLK Clock Frequency */ + {0xf00, 0x00, 0x17}, /* Interface Type Capability */ +#endif + {0xf01, 0x14, 0x00}, /* Connection Select Control */ + {0xf01, 0x15, 0x00}, /* Connection Select Control */ + {0xf01, 0x16, 0x00}, /* Connection Select Control */ + {0xf01, 0x1b, 0x00}, /* Connection Select Control */ + {0xf01, 0x21, 0x00}, /* Connection Select Control */ + {0xf01, 0x22, 0x00}, /* Connection Select Control */ + {0xf01, 0x23, 0x00}, /* Connection Select Control */ + {0xf01, 0x24, 0x00}, /* Connection Select Control */ + {0xf01, 0x25, 0x00}, /* Connection Select Control */ + {0xf02, 0x07, 0x00}, /* Connection List Entry */ + {0xf02, 0x08, 0x00}, /* Connection List Entry */ + {0xf02, 0x09, 0x00}, /* Connection List Entry */ + {0xf02, 0x0a, 0x00}, /* Connection List Entry */ + {0xf02, 0x14, 0x00}, /* Connection List Entry */ + {0xf02, 0x15, 0x00}, /* Connection List Entry */ + {0xf02, 0x16, 0x00}, /* Connection List Entry */ + {0xf02, 0x1b, 0x00}, /* Connection List Entry */ + {0xf02, 0x21, 0x00}, /* Connection List Entry */ + {0xf02, 0x1e, 0x00}, /* Connection List Entry */ + {0xf02, 0x21, 0x00}, /* Connection List Entry */ + {0xf02, 0x23, 0x00}, /* Connection List Entry */ + {0xf02, 0x24, 0x00}, /* Connection List Entry */ + {0xf02, 0x25, 0x00}, /* Connection List Entry */ + {0xd00, 0x20, 0x00}, /* Coefficient Index */ + {0xd00, 0x53, 0x00}, /* Coefficient Index */ + {0xd00, 0x54, 0x00}, /* Coefficient Index */ + {0xd00, 0x56, 0x00}, /* Coefficient Index */ + {0xd00, 0x57, 0x00}, /* Coefficient Index */ + {0xd00, 0x58, 0x00}, /* Coefficient Index */ + {0xc00, 0x20, 0x00}, /* Processing Coefficient */ + {0xc00, 0x53, 0x00}, /* Processing Coefficient */ + {0xc00, 0x54, 0x00}, /* Processing Coefficient */ + {0xc00, 0x56, 0x00}, /* Processing Coefficient */ + {0xc00, 0x57, 0x00}, /* Processing Coefficient */ + {0xc00, 0x58, 0x00}, /* Processing Coefficient */ + {0xb00, 0x02, 0x8000}, /* Amplifier Gain */ + {0xb00, 0x02, 0xa000}, /* Amplifier Gain */ + {0xb00, 0x03, 0x8000}, /* Amplifier Gain */ + {0xb00, 0x03, 0xa000}, /* Amplifier Gain */ + {0xb00, 0x07, 0x0000}, /* Amplifier Gain */ + {0xb00, 0x07, 0x2000}, /* Amplifier Gain */ + {0xb00, 0x08, 0x0000}, /* Amplifier Gain */ + {0xb00, 0x08, 0x2000}, /* Amplifier Gain */ + {0xb00, 0x09, 0x0000}, /* Amplifier Gain */ + {0xb00, 0x09, 0x2000}, /* Amplifier Gain */ + {0xb00, 0x1b, 0x8000}, /* Amplifier Gain */ + {0xb00, 0x1b, 0xa000}, /* Amplifier Gain */ + {0xb00, 0x1b, 0x0000}, /* Amplifier Gain */ + {0xb00, 0x1b, 0x2000}, /* Amplifier Gain */ + {0xb00, 0x12, 0x0000}, /* Amplifier Gain */ + {0xb00, 0x12, 0x2000}, /* Amplifier Gain */ + {0xb00, 0x13, 0x0000}, /* Amplifier Gain */ + {0xb00, 0x13, 0x2000}, /* Amplifier Gain */ + {0xb00, 0x19, 0x0000}, /* Amplifier Gain */ + {0xb00, 0x19, 0x2000}, /* Amplifier Gain */ + {0xb00, 0x1a, 0x0000}, /* Amplifier Gain */ + {0xb00, 0x1a, 0x2000}, /* Amplifier Gain */ + {0xb00, 0x14, 0x8000}, /* Amplifier Gain */ + {0xb00, 0x14, 0xa000}, /* Amplifier Gain */ + {0xb00, 0x15, 0x8000}, /* Amplifier Gain */ + {0xb00, 0x15, 0xa000}, /* Amplifier Gain */ + {0xb00, 0x16, 0x8000}, /* Amplifier Gain */ + {0xb00, 0x16, 0xa000}, /* Amplifier Gain */ + {0xb00, 0x17, 0x8000}, /* Amplifier Gain */ + {0xb00, 0x17, 0xa000}, /* Amplifier Gain */ + {0xb00, 0x21, 0x8000}, /* Amplifier Gain */ + {0xb00, 0x21, 0xa000}, /* Amplifier Gain */ + {0xa00, 0x02, 0x0000}, /* Converter Format */ + {0xa00, 0x03, 0x0000}, /* Converter Format */ + {0xa00, 0x04, 0x0000}, /* Converter Format */ + {0xa00, 0x05, 0x0000}, /* Converter Format */ + {0xa00, 0x06, 0x0000}, /* Converter Format */ + {0xa00, 0x07, 0x0000}, /* Converter Format */ + {0xa00, 0x08, 0x0000}, /* Converter Format */ + {0xa00, 0x09, 0x0000}, /* Converter Format */ + {0xa00, 0x0a, 0x0000}, /* Converter Format */ + {0xa00, 0x27, 0x0000}, /* Converter Format */ + {0xf05, 0x01, 0x00}, /* Power State */ + {0xf05, 0x02, 0x00}, /* Power State */ + {0xf05, 0x03, 0x00}, /* Power State */ + {0xf05, 0x04, 0x00}, /* Power State */ + {0xf05, 0x05, 0x00}, /* Power State */ + {0xf05, 0x06, 0x00}, /* Power State */ + {0xf05, 0x07, 0x00}, /* Power State */ + {0xf05, 0x08, 0x00}, /* Power State */ + {0xf05, 0x09, 0x00}, /* Power State */ + {0xf05, 0x0a, 0x00}, /* Power State */ + {0xf05, 0x12, 0x00}, /* Power State */ + {0xf05, 0x13, 0x00}, /* Power State */ + {0xf05, 0x14, 0x00}, /* Power State */ + {0xf05, 0x15, 0x00}, /* Power State */ + {0xf05, 0x16, 0x00}, /* Power State */ + {0xf05, 0x17, 0x00}, /* Power State */ + {0xf05, 0x18, 0x00}, /* Power State */ + {0xf05, 0x19, 0x00}, /* Power State */ + {0xf05, 0x1a, 0x00}, /* Power State */ + {0xf05, 0x1b, 0x00}, /* Power State */ + {0xf05, 0x1d, 0x00}, /* Power State */ + {0xf05, 0x1e, 0x00}, /* Power State */ + {0xf05, 0x1f, 0x00}, /* Power State */ + {0xf05, 0x21, 0x00}, /* Power State */ + {0xf05, 0x27, 0x00}, /* Power State */ + {0xf05, 0x29, 0x00}, /* Power State */ + {0xf06, 0x02, 0x00}, /* Converter Stream, Channel */ + {0xf06, 0x03, 0x00}, /* Converter Stream, Channel */ + {0xf06, 0x04, 0x00}, /* Converter Stream, Channel */ + {0xf06, 0x05, 0x00}, /* Converter Stream, Channel */ + {0xf06, 0x06, 0x00}, /* Converter Stream, Channel */ + {0xf06, 0x07, 0x00}, /* Converter Stream, Channel */ + {0xf06, 0x08, 0x00}, /* Converter Stream, Channel */ + {0xf06, 0x09, 0x00}, /* Converter Stream, Channel */ + {0xf06, 0x0a, 0x00}, /* Converter Stream, Channel */ + {0xf06, 0x27, 0x00}, /* Converter Stream, Channel */ + {0xf07, 0x12, 0x00}, /* Pin Widget Control */ + {0xf07, 0x13, 0x00}, /* Pin Widget Control */ + {0xf07, 0x14, 0x00}, /* Pin Widget Control */ + {0xf07, 0x15, 0x00}, /* Pin Widget Control */ + {0xf07, 0x16, 0x00}, /* Pin Widget Control */ + {0xf07, 0x17, 0x00}, /* Pin Widget Control */ + {0xf07, 0x18, 0x00}, /* Pin Widget Control */ + {0xf07, 0x19, 0x00}, /* Pin Widget Control */ + {0xf07, 0x1a, 0x00}, /* Pin Widget Control */ + {0xf07, 0x1b, 0x00}, /* Pin Widget Control */ + {0xf07, 0x1d, 0x00}, /* Pin Widget Control */ + {0xf07, 0x1e, 0x00}, /* Pin Widget Control */ + {0xf07, 0x1f, 0x00}, /* Pin Widget Control */ + {0xf07, 0x21, 0x00}, /* Pin Widget Control */ + {0xf07, 0x29, 0x00}, /* Pin Widget Control */ + {0xf0c, 0x14, 0x00}, /* EAPD Enable */ + {0xf0c, 0x15, 0x00}, /* EAPD Enable */ + {0xf0c, 0x16, 0x00}, /* EAPD Enable */ + {0xf0c, 0x17, 0x00}, /* EAPD Enable */ + {0xf0c, 0x1b, 0x00}, /* EAPD Enable */ + {0xf0c, 0x21, 0x00}, /* EAPD Enable */ + {0xf08, 0x01, 0x00}, /* Unsolicited Response */ + {0xf08, 0x15, 0x00}, /* Unsolicited Response */ + {0xf08, 0x16, 0x00}, /* Unsolicited Response */ + {0xf08, 0x17, 0x00}, /* Unsolicited Response */ + {0xf08, 0x18, 0x00}, /* Unsolicited Response */ + {0xf08, 0x19, 0x00}, /* Unsolicited Response */ + {0xf08, 0x1a, 0x00}, /* Unsolicited Response */ + {0xf08, 0x1b, 0x00}, /* Unsolicited Response */ + {0xf08, 0x1e, 0x00}, /* Unsolicited Response */ + {0xf08, 0x21, 0x00}, /* Unsolicited Response */ + {0xf08, 0x55, 0x00}, /* Unsolicited Response */ + {0xf08, 0x60, 0x00}, /* Unsolicited Response */ + {0xf09, 0x60, 0x00}, /* Pin Sense */ + {0xf09, 0x15, 0x00}, /* Pin Sense */ + {0xf09, 0x16, 0x00}, /* Pin Sense */ + {0xf09, 0x17, 0x00}, /* Pin Sense */ + {0xf09, 0x18, 0x00}, /* Pin Sense */ + {0xf09, 0x19, 0x00}, /* Pin Sense */ + {0xf09, 0x1a, 0x00}, /* Pin Sense */ + {0xf09, 0x1b, 0x00}, /* Pin Sense */ + {0xf09, 0x1e, 0x00}, /* Pin Sense */ + {0xf09, 0x1f, 0x00}, /* Pin Sense */ + {0xf09, 0x29, 0x00}, /* Pin Sense */ + {0xf0a, 0x01, 0x00}, /* BEEP Generator */ + {0xf20, 0x01, 0x00}, /* Subsystem ID */ + {0xf21, 0x01, 0x00}, /* Subsystem ID */ + {0xf22, 0x01, 0x00}, /* Subsystem ID */ + {0xf23, 0x01, 0x00}, /* Subsystem ID */ + {0xf1c, 0x12, 0x00}, /* Configuration Default */ + {0xf1c, 0x13, 0x00}, /* Configuration Default */ + {0xf1c, 0x14, 0x00}, /* Configuration Default */ + {0xf1c, 0x15, 0x00}, /* Configuration Default */ + {0xf1c, 0x16, 0x00}, /* Configuration Default */ + {0xf1c, 0x17, 0x00}, /* Configuration Default */ + {0xf1c, 0x18, 0x00}, /* Configuration Default */ + {0xf1c, 0x19, 0x00}, /* Configuration Default */ + {0xf1c, 0x1a, 0x00}, /* Configuration Default */ + {0xf1c, 0x1b, 0x00}, /* Configuration Default */ + {0xf1c, 0x1d, 0x00}, /* Configuration Default */ + {0xf1c, 0x1e, 0x00}, /* Configuration Default */ + {0xf1c, 0x1f, 0x00}, /* Configuration Default */ + {0xf1c, 0x21, 0x00}, /* Configuration Default */ + {0xf1c, 0x29, 0x00}, /* Configuration Default */ + {0xf1d, 0x12, 0x00}, /* Configuration Default */ + {0xf1d, 0x13, 0x00}, /* Configuration Default */ + {0xf1d, 0x14, 0x00}, /* Configuration Default */ + {0xf1d, 0x15, 0x00}, /* Configuration Default */ + {0xf1d, 0x16, 0x00}, /* Configuration Default */ + {0xf1d, 0x17, 0x00}, /* Configuration Default */ + {0xf1d, 0x18, 0x00}, /* Configuration Default */ + {0xf1d, 0x19, 0x00}, /* Configuration Default */ + {0xf1d, 0x1a, 0x00}, /* Configuration Default */ + {0xf1d, 0x1b, 0x00}, /* Configuration Default */ + {0xf1d, 0x1d, 0x00}, /* Configuration Default */ + {0xf1d, 0x1e, 0x00}, /* Configuration Default */ + {0xf1d, 0x1f, 0x00}, /* Configuration Default */ + {0xf1d, 0x21, 0x00}, /* Configuration Default */ + {0xf1d, 0x29, 0x00}, /* Configuration Default */ + {0xf1e, 0x12, 0x00}, /* Configuration Default */ + {0xf1e, 0x13, 0x00}, /* Configuration Default */ + {0xf1e, 0x14, 0x00}, /* Configuration Default */ + {0xf1e, 0x15, 0x00}, /* Configuration Default */ + {0xf1e, 0x16, 0x00}, /* Configuration Default */ + {0xf1e, 0x17, 0x00}, /* Configuration Default */ + {0xf1e, 0x18, 0x00}, /* Configuration Default */ + {0xf1e, 0x19, 0x00}, /* Configuration Default */ + {0xf1e, 0x1a, 0x00}, /* Configuration Default */ + {0xf1e, 0x1b, 0x00}, /* Configuration Default */ + {0xf1e, 0x1d, 0x00}, /* Configuration Default */ + {0xf1e, 0x1e, 0x00}, /* Configuration Default */ + {0xf1e, 0x1f, 0x00}, /* Configuration Default */ + {0xf1e, 0x21, 0x00}, /* Configuration Default */ + {0xf1e, 0x29, 0x00}, /* Configuration Default */ + {0xf1f, 0x12, 0x00}, /* Configuration Default */ + {0xf1f, 0x13, 0x00}, /* Configuration Default */ + {0xf1f, 0x14, 0x00}, /* Configuration Default */ + {0xf1f, 0x15, 0x00}, /* Configuration Default */ + {0xf1f, 0x16, 0x00}, /* Configuration Default */ + {0xf1f, 0x17, 0x00}, /* Configuration Default */ + {0xf1f, 0x18, 0x00}, /* Configuration Default */ + {0xf1f, 0x19, 0x00}, /* Configuration Default */ + {0xf1f, 0x1a, 0x00}, /* Configuration Default */ + {0xf1f, 0x1b, 0x00}, /* Configuration Default */ + {0xf1f, 0x1d, 0x00}, /* Configuration Default */ + {0xf1f, 0x1e, 0x00}, /* Configuration Default */ + {0xf1f, 0x1f, 0x00}, /* Configuration Default */ + {0xf1f, 0x21, 0x00}, /* Configuration Default */ + {0xf1f, 0x29, 0x00}, /* Configuration Default */ + {0xf0d, 0x06, 0x00}, /* Digital Converter */ + {0xf0d, 0x0a, 0x00}, /* Digital Converter */ + {0xf15, 0x01, 0x00}, /* GPIO Data */ + {0xf16, 0x01, 0x00}, /* GPIO Enable Mask */ + {0xf16, 0x20, 0x00}, /* GPIO Enable Mask */ + {0xf17, 0x01, 0x00}, /* GPIO Direction */ + {0xf17, 0x20, 0x00}, /* GPIO Direction */ + {0xf19, 0x01, 0x00}, /* GPIO Unsolicited Response Enable Mask */ + {0xf19, 0x20, 0x00}, /* GPIO Unsolicited Response Enable Mask */ + {0xf37, 0x01, 0x00}, /* Current BCLK Frequency */ +}; +#define RT700_HDA_DUMP_LEN ARRAY_SIZE(hda_dump_list) + +static int rt700_index_write(struct regmap *regmap, + unsigned int reg, unsigned int value) +{ + int ret; + unsigned int val_h, val_l; + + val_h = (reg >> 8) & 0xff; + val_l = reg & 0xff; + ret = regmap_write(regmap, RT700_PRIV_INDEX_W_H, val_h); + if (ret < 0) { + pr_err("Failed to set private addr: %d\n", ret); + goto err; + } + ret = regmap_write(regmap, RT700_PRIV_INDEX_W_L, val_l); + if (ret < 0) { + pr_err("Failed to set private addr: %d\n", ret); + goto err; + } + val_h = (value >> 8) & 0xff; + val_l = value & 0xff; + ret = regmap_write(regmap, RT700_PRIV_DATA_W_H, val_h); + if (ret < 0) { + pr_err("Failed to set private value: %d\n", ret); + goto err; + } + ret = regmap_write(regmap, RT700_PRIV_DATA_W_L, val_l); + if (ret < 0) { + pr_err("Failed to set private value: %d\n", ret); + goto err; + } + return 0; + +err: + return ret; +} + +static int rt700_index_read(struct regmap *regmap, + unsigned int reg, unsigned int *value) +{ + int ret; + unsigned int val_h, val_l; + unsigned int sdw_data_3, sdw_data_2, sdw_data_1, sdw_data_0; + + val_h = (reg >> 8) & 0xff; + val_l = reg & 0xff; + ret = regmap_write(regmap, RT700_PRIV_INDEX_W_H, val_h); + if (ret < 0) { + pr_err("Failed to set private addr: %d\n", ret); + goto err; + } + ret = regmap_write(regmap, RT700_PRIV_INDEX_W_L, val_l); + if (ret < 0) { + pr_err("Failed to set private addr: %d\n", ret); + goto err; + } + val_h = 0; + val_l = 0; + ret = regmap_write(regmap, RT700_PRIV_DATA_R_H, val_h); + if (ret < 0) { + pr_err("Failed to set private value: %d\n", ret); + goto err; + } + ret = regmap_write(regmap, RT700_PRIV_DATA_R_L, val_l); + if (ret < 0) { + pr_err("Failed to set private value: %d\n", ret); + goto err; + } + + sdw_data_3 = 0; + sdw_data_2 = 0; + sdw_data_1 = 0; + sdw_data_0 = 0; + regmap_read(regmap, + RT700_READ_HDA_3, &sdw_data_3); + regmap_read(regmap, + RT700_READ_HDA_2, &sdw_data_2); + regmap_read(regmap, + RT700_READ_HDA_1, &sdw_data_1); + regmap_read(regmap, + RT700_READ_HDA_0, &sdw_data_0); + *value = ((sdw_data_3 & 0xff) << 24) | ((sdw_data_2 & 0xff) << 16) | + ((sdw_data_1 & 0xff) << 8) | (sdw_data_0 & 0xff); + return 0; + +err: + return ret; +} + +static int rt700_hda_read(struct regmap *regmap, unsigned int vid, + unsigned int nid, unsigned int pid, unsigned int *value) +{ + int ret; + unsigned int val_h, val_l; + unsigned int sdw_data_3, sdw_data_2, sdw_data_1, sdw_data_0; + unsigned int sdw_addr_h, sdw_addr_l; + + sdw_data_3 = 0; + sdw_data_2 = 0; + sdw_data_1 = 0; + sdw_data_0 = 0; + + if (vid & 0x800) { /* get command */ + hda_to_sdw(nid, vid, pid, + &sdw_addr_h, &sdw_data_1, &sdw_addr_l, &sdw_data_0); + + pr_debug("write %04x %02x\n", sdw_addr_h, sdw_data_1); + regmap_write(regmap, sdw_addr_h, sdw_data_1); + if (sdw_addr_l) { + pr_debug("write %04x %02x", sdw_addr_l, sdw_data_0); + regmap_write(regmap, sdw_addr_l, sdw_data_0); + } + regmap_read(regmap, + RT700_READ_HDA_3, &sdw_data_3); + regmap_read(regmap, + RT700_READ_HDA_2, &sdw_data_2); + regmap_read(regmap, + RT700_READ_HDA_1, &sdw_data_1); + regmap_read(regmap, + RT700_READ_HDA_0, &sdw_data_0); + pr_debug("(%03x %02x %04x) = %02x%02x%02x%02x\n", + vid, nid, pid, sdw_data_3, + sdw_data_2, sdw_data_1, sdw_data_0); + } else { + pr_err("%s: it is not a get verb\n", __func__); + } + *value = ((sdw_data_3 & 0xff) << 24) | ((sdw_data_2 & 0xff) << 16) | + ((sdw_data_1 & 0xff) << 8) | (sdw_data_0 & 0xff); + + return 0; +} + +int rt700_jack_detect(struct rt700_priv *rt700, bool *hp, bool *mic) +{ + unsigned int buf; + + rt700_index_read(rt700->regmap, 0x82, &buf); + *hp = buf & 0x10; + if (*hp) { + *mic = buf & 0x40; + } else { + *mic = false; + } + + /* Clear IRQ */ + rt700_index_read(rt700->regmap, 0x10, &buf); + buf = buf | 0x1000; + rt700_index_write(rt700->regmap, 0x10, buf); + + rt700_index_read(rt700->regmap, 0x19, &buf); + buf = buf | 0x0100; + rt700_index_write(rt700->regmap, 0x19, buf); + + + return 0; +} +EXPORT_SYMBOL(rt700_jack_detect); + +/* For Verb-Set Amplifier Gain (Verb ID = 3h) */ +static int rt700_set_amp_gain_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + struct rt700_priv *rt700 = snd_soc_component_get_drvdata(component); + unsigned int addr_h, addr_l, val_h, val_l; + unsigned int read_ll, read_rl; + + + /* Can't use update bit function, so read the original value first */ + addr_h = (mc->reg + 0x2000) | 0x800; + addr_l = (mc->rreg + 0x2000) | 0x800; + if (mc->shift == RT700_DIR_OUT_SFT) /* output */ + val_h = 0x80; + else /* input */ + val_h = 0x0; + /* R Channel */ + regmap_write(rt700->regmap, addr_h, val_h); + pr_debug("%s write %04x %02x\n", __func__, addr_h, val_h); + regmap_write(rt700->regmap, addr_l, 0); + pr_debug("%s write %04x %02x\n", __func__, addr_l, 0); + regmap_read(rt700->regmap, RT700_READ_HDA_0, &read_rl); + pr_debug("%s read %04x %02x\n", __func__, RT700_READ_HDA_0, read_rl); + + /* L Channel */ + val_h |= 0x20; + regmap_write(rt700->regmap, addr_h, val_h); + pr_debug("%s write %04x %02x\n", __func__, addr_h, val_h); + regmap_write(rt700->regmap, addr_l, 0); + pr_debug("%s write %04x %02x\n", __func__, addr_l, 0); + regmap_read(rt700->regmap, RT700_READ_HDA_0, &read_ll); + pr_debug("%s read %04x %02x\n", __func__, RT700_READ_HDA_0, read_ll); + + + /* Now set value */ + addr_h = mc->reg; + addr_l = mc->rreg; + + /*pr_debug("%s val = %d, %d\n", ucontrol->value.integer.value[0], + ucontrol->value.integer.value[1]);*/ + pr_debug("%s val = %d, %d\n", __func__, ucontrol->value.integer.value[0], + ucontrol->value.integer.value[1]); + /* L Channel */ + val_h = (1 << mc->shift) | (1 << 5); + + if (mc->invert) { + /* for mute */ + val_l = (mc->max - ucontrol->value.integer.value[0]) << 7; + /* keep gain */ + read_ll = read_ll & 0x7f; + val_l |= read_ll; + } else { + /* for gain */ + val_l = ((ucontrol->value.integer.value[0]) & mc->max); + /* keep mute status */ + read_ll = read_ll & 0x80; + val_l |= read_ll; + } + + regmap_write(rt700->regmap, addr_h, val_h); + pr_debug("%s write %04x %02x\n", __func__, addr_h, val_h); + regmap_write(rt700->regmap, addr_l, val_l); + pr_debug("%s write %04x %02x\n", __func__, addr_l, val_l); + + /* R Channel */ + val_h = (1 << mc->shift) | (1 << 4); + + if (mc->invert) { + /* for mute */ + val_l = (mc->max - ucontrol->value.integer.value[1]) << 7; + /* keep gain */ + read_rl = read_rl & 0x7f; + val_l |= read_rl; + } else { + /* for gain */ + val_l = ((ucontrol->value.integer.value[1]) & mc->max); + /* keep mute status */ + read_rl = read_rl & 0x80; + val_l |= read_rl; + } + val_h = (1 << mc->shift) | (1 << 4); + regmap_write(rt700->regmap, addr_h, val_h); + pr_debug("%s write %04x %02x\n", __func__, addr_h, val_h); + regmap_write(rt700->regmap, addr_l, val_l); + pr_debug("%s write %04x %02x\n", __func__, addr_l, val_l); + + return 0; +} + +static int rt700_set_amp_gain_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct rt700_priv *rt700 = snd_soc_component_get_drvdata(component); + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + unsigned int addr_h, addr_l, val_h; + unsigned int read_ll, read_rl; + + addr_h = (mc->reg + 0x2000) | 0x800; + addr_l = (mc->rreg + 0x2000) | 0x800; + if (mc->shift == RT700_DIR_OUT_SFT) /* output */ + val_h = 0x80; + else /* input */ + val_h = 0x0; + /* R Channel */ + regmap_write(rt700->regmap, addr_h, val_h); + pr_debug("%s write %04x %02x\n", __func__, addr_h, val_h); + regmap_write(rt700->regmap, addr_l, 0); + pr_debug("%s write %04x %02x\n", __func__, addr_l, 0); + regmap_read(rt700->regmap, RT700_READ_HDA_0, &read_rl); + pr_debug("%s read %04x %02x\n", __func__, RT700_READ_HDA_0, read_rl); + + /* L Channel */ + val_h |= 0x20; + regmap_write(rt700->regmap, addr_h, val_h); + pr_debug("%s write %04x %02x\n", __func__, addr_h, val_h); + regmap_write(rt700->regmap, addr_l, 0); + pr_debug("%s write %04x %02x\n", __func__, addr_l, 0); + regmap_read(rt700->regmap, RT700_READ_HDA_0, &read_ll); + pr_debug("%s read %04x %02x\n", __func__, RT700_READ_HDA_0, read_ll); + + if (mc->invert) { + /* for mute status */ + read_ll = !((read_ll & 0x80) >> RT700_MUTE_SFT); + read_rl = !((read_rl & 0x80) >> RT700_MUTE_SFT); + } else { + /* for gain */ + read_ll = read_ll & 0x7f; + read_rl = read_rl & 0x7f; + } + ucontrol->value.integer.value[0] = read_ll; + ucontrol->value.integer.value[1] = read_rl; + + + return 0; +} + +static const DECLARE_TLV_DB_SCALE(out_vol_tlv, -6525, 75, 0); +static const DECLARE_TLV_DB_SCALE(in_vol_tlv, -1725, 75, 0); +static const DECLARE_TLV_DB_SCALE(mic_vol_tlv, 0, 1000, 0); + +#define SOC_DOUBLE_R_EXT(xname, reg_left, reg_right, xshift, xmax, xinvert,\ + xhandler_get, xhandler_put) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), \ + .info = snd_soc_info_volsw, \ + .get = xhandler_get, .put = xhandler_put, \ + .private_value = SOC_DOUBLE_R_VALUE(reg_left, reg_right, xshift, \ + xmax, xinvert) } + +static const struct snd_kcontrol_new rt700_snd_controls[] = { + SOC_DOUBLE_R_EXT_TLV("DAC Front Playback Volume", RT700_SET_GAIN_DAC1_H, + RT700_SET_GAIN_DAC1_L, RT700_DIR_OUT_SFT, 0x57, 0, + rt700_set_amp_gain_get, rt700_set_amp_gain_put, + out_vol_tlv), + SOC_DOUBLE_R_EXT("ADC 08 Capture Switch", RT700_SET_GAIN_ADC2_H, + RT700_SET_GAIN_ADC2_L, RT700_DIR_IN_SFT, 1, 1, + rt700_set_amp_gain_get, rt700_set_amp_gain_put), + SOC_DOUBLE_R_EXT("ADC 09 Capture Switch", RT700_SET_GAIN_ADC1_H, + RT700_SET_GAIN_ADC1_L, RT700_DIR_IN_SFT, 1, 1, + rt700_set_amp_gain_get, rt700_set_amp_gain_put), + SOC_DOUBLE_R_EXT_TLV("ADC 08 Capture Volume", RT700_SET_GAIN_ADC2_H, + RT700_SET_GAIN_ADC2_L, RT700_DIR_IN_SFT, 0x3f, 0, + rt700_set_amp_gain_get, rt700_set_amp_gain_put, + in_vol_tlv), + SOC_DOUBLE_R_EXT_TLV("ADC 09 Capture Volume", RT700_SET_GAIN_ADC1_H, + RT700_SET_GAIN_ADC1_L, RT700_DIR_IN_SFT, 0x3f, 0, + rt700_set_amp_gain_get, rt700_set_amp_gain_put, + in_vol_tlv), + SOC_DOUBLE_R_EXT_TLV("AMIC Volume", RT700_SET_GAIN_AMIC_H, + RT700_SET_GAIN_AMIC_L, RT700_DIR_IN_SFT, 3, 0, + rt700_set_amp_gain_get, rt700_set_amp_gain_put, + mic_vol_tlv), + SOC_DOUBLE_R_EXT("Speaker Playback Switch", RT700_SET_GAIN_SPK_H, + RT700_SET_GAIN_SPK_L, RT700_DIR_OUT_SFT, 1, 1, + rt700_set_amp_gain_get, rt700_set_amp_gain_put), + SOC_DOUBLE_R_EXT("Headphone Playback Switch", RT700_SET_GAIN_HP_H, + RT700_SET_GAIN_HP_L, RT700_DIR_OUT_SFT, 1, 1, + rt700_set_amp_gain_get, rt700_set_amp_gain_put), +}; + +static int rt700_mux_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = + snd_soc_dapm_kcontrol_component(kcontrol); + struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; + unsigned int reg, val; + + /* nid = e->reg, vid = 0xf01 */ + reg = RT700_VERB_GET_CONNECT_SEL | e->reg; + snd_soc_component_write(component, reg, 0x0); + pr_debug("%s write %04x %02x\n", __func__, reg, 0x0); + val = snd_soc_component_read32(component, RT700_READ_HDA_0); + pr_debug("%s read %04x %02x\n", __func__, RT700_READ_HDA_0, val); + ucontrol->value.enumerated.item[0] = val; + + return 0; +} + +static int rt700_mux_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = + snd_soc_dapm_kcontrol_component(kcontrol); + struct snd_soc_dapm_context *dapm = + snd_soc_dapm_kcontrol_dapm(kcontrol); + struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; + unsigned int *item = ucontrol->value.enumerated.item; + unsigned int val, val2, change, reg; + struct snd_soc_dapm_update update; + + if (item[0] >= e->items) + return -EINVAL; + + /* Verb ID = 0x701h, nid = e->reg */ + val = snd_soc_enum_item_to_val(e, item[0]) << e->shift_l; + pr_debug("%s val=%x e->reg=%x item[0]=%d\n", + __func__, val, e->reg, item[0]); + + reg = RT700_VERB_GET_CONNECT_SEL | e->reg; + snd_soc_component_write(component, reg, 0x0); + pr_debug("%s write %04x %02x\n", __func__, reg, 0x0); + val2 = snd_soc_component_read32(component, RT700_READ_HDA_0); + pr_debug("%s read %04x %02x\n", __func__, RT700_READ_HDA_0, val2); + if (val == val2) + change = 0; + else + change = 1; + + pr_debug("change=%d\n", change); + + if (change) { + reg = RT700_VERB_SET_CONNECT_SEL | e->reg; + snd_soc_component_write(component, reg, val); + pr_debug("%s write %04x %02x\n", __func__, reg, val); + update.kcontrol = kcontrol; + update.reg = e->reg; + update.mask = 0xff; + update.val = val; + snd_soc_dapm_mux_update_power(dapm, kcontrol, + item[0], e, &update); + } + + return change; +} + +static const char * const adc_mux_text[] = { + "MIC2", + "LINE1", + "LINE2", + "DMIC", +}; + +static const SOC_ENUM_SINGLE_DECL( + rt700_adc22_enum, RT700_MIXER_IN1, 0, adc_mux_text); + +static const SOC_ENUM_SINGLE_DECL( + rt700_adc23_enum, RT700_MIXER_IN2, 0, adc_mux_text); + +static const struct snd_kcontrol_new rt700_adc22_mux = + SOC_DAPM_ENUM_EXT("ADC 22 Mux", rt700_adc22_enum, + rt700_mux_get, rt700_mux_put); + +static const struct snd_kcontrol_new rt700_adc23_mux = + SOC_DAPM_ENUM_EXT("ADC 23 Mux", rt700_adc23_enum, + rt700_mux_get, rt700_mux_put); + +static const char * const out_mux_text[] = { + "Front", + "Surround", +}; + +static const SOC_ENUM_SINGLE_DECL( + rt700_hp_enum, RT700_HP_OUT, 0, out_mux_text); + +static const struct snd_kcontrol_new rt700_hp_mux = + SOC_DAPM_ENUM_EXT("HP Mux", rt700_hp_enum, + rt700_mux_get, rt700_mux_put); + +static const struct snd_soc_dapm_widget rt700_dapm_widgets[] = { + SND_SOC_DAPM_OUTPUT("HP"), + SND_SOC_DAPM_OUTPUT("SPK"), + SND_SOC_DAPM_INPUT("DMIC1"), + SND_SOC_DAPM_INPUT("DMIC2"), + SND_SOC_DAPM_INPUT("MIC2"), + SND_SOC_DAPM_INPUT("LINE1"), + SND_SOC_DAPM_INPUT("LINE2"), + SND_SOC_DAPM_DAC("DAC Front", NULL, RT700_SET_STREAMID_DAC1, 4, 0), + SND_SOC_DAPM_DAC("DAC Surround", NULL, RT700_SET_STREAMID_DAC2, 4, 0), + SND_SOC_DAPM_MUX("HPO Mux", SND_SOC_NOPM, 0, 0, &rt700_hp_mux), + SND_SOC_DAPM_PGA("SPK PGA", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_ADC("ADC 09", NULL, RT700_SET_STREAMID_ADC1, 4, 0), + SND_SOC_DAPM_ADC("ADC 08", NULL, RT700_SET_STREAMID_ADC2, 4, 0), + SND_SOC_DAPM_MUX("ADC 22 Mux", SND_SOC_NOPM, 0, 0, + &rt700_adc22_mux), + SND_SOC_DAPM_MUX("ADC 23 Mux", SND_SOC_NOPM, 0, 0, + &rt700_adc23_mux), + SND_SOC_DAPM_AIF_IN("DP1RX", "DP1 Playback", 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_IN("DP3RX", "DP3 Playback", 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("DP2TX", "DP2 Capture", 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("DP4TX", "DP4 Capture", 0, SND_SOC_NOPM, 0, 0), +}; + +static const struct snd_soc_dapm_route rt700_audio_map[] = { + {"DAC Front", NULL, "DP1RX"}, + {"DAC Surround", NULL, "DP3RX"}, + {"DP2TX", NULL, "ADC 09"}, + {"DP4TX", NULL, "ADC 08"}, + {"ADC 09", NULL, "ADC 22 Mux"}, + {"ADC 08", NULL, "ADC 23 Mux"}, + {"ADC 22 Mux", "DMIC", "DMIC1"}, + {"ADC 22 Mux", "LINE1", "LINE1"}, + {"ADC 22 Mux", "LINE2", "LINE2"}, + {"ADC 22 Mux", "MIC2", "MIC2"}, + {"ADC 23 Mux", "DMIC", "DMIC2"}, + {"ADC 23 Mux", "LINE1", "LINE1"}, + {"ADC 23 Mux", "LINE2", "LINE2"}, + {"ADC 23 Mux", "MIC2", "MIC2"}, + {"HPO Mux", "Front", "DAC Front"}, + {"HPO Mux", "Surround", "DAC Surround"}, + {"HP", NULL, "HPO Mux"}, + {"SPK PGA", NULL, "DAC Front"}, + {"SPK", NULL, "SPK PGA"}, +}; + +static int rt700_set_bias_level(struct snd_soc_component *component, + enum snd_soc_bias_level level) +{ + struct snd_soc_dapm_context *dapm = + snd_soc_component_get_dapm(component); + + switch (level) { + case SND_SOC_BIAS_PREPARE: + if (SND_SOC_BIAS_STANDBY == dapm->bias_level) { + snd_soc_component_write(component, + RT700_SET_AUDIO_POWER_STATE, AC_PWRST_D0); + } + break; + + case SND_SOC_BIAS_STANDBY: + snd_soc_component_write(component, + RT700_SET_AUDIO_POWER_STATE, AC_PWRST_D3); + break; + + default: + break; + } + dapm->bias_level = level; + return 0; +} + +static const struct snd_soc_component_driver soc_component_dev_rt700 = { + .set_bias_level = rt700_set_bias_level, + .controls = rt700_snd_controls, + .num_controls = ARRAY_SIZE(rt700_snd_controls), + .dapm_widgets = rt700_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(rt700_dapm_widgets), + .dapm_routes = rt700_audio_map, + .num_dapm_routes = ARRAY_SIZE(rt700_audio_map), +}; + +static int rt700_program_stream_tag(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai, int stream_tag) +{ + struct sdw_stream_data *stream_data; + + stream_data = kzalloc(sizeof(*stream_data), GFP_KERNEL); + if (!stream_data) + return -ENOMEM; + stream_data->stream_tag = stream_tag; + snd_soc_dai_set_dma_data(dai, substream, stream_data); + return 0; +} + +static int rt700_remove_stream_tag(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) + +{ + struct sdw_stream_data *stream_data; + + stream_data = snd_soc_dai_get_dma_data(dai, substream); + kfree(stream_data); + return 0; +} + + + +static int rt700_pcm_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct rt700_priv *rt700 = snd_soc_component_get_drvdata(component); + int retval; + enum sdw_data_direction direction; + struct sdw_stream_config stream_config; + struct sdw_port_config port_config; + struct sdw_port_cfg port_cfg; + struct sdw_stream_data *stream; + int port; + int num_channels; + int upscale_factor = 1; + unsigned int val = 0; + + stream = snd_soc_dai_get_dma_data(dai, substream); + + if (!rt700->sdw) + return 0; + + /* SoundWire specific configuration */ + /* This code assumes port 1 for playback and port 2 for capture */ + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + direction = SDW_DATA_DIR_IN; + port = 1; + } else { + direction = SDW_DATA_DIR_OUT; + port = 2; + } + switch (dai->id) { + case RT700_AIF1: + break; + case RT700_AIF2: + port += 2; + break; + default: + dev_err(component->dev, "Invalid DAI id %d\n", dai->id); + return -EINVAL; + } + stream_config.frame_rate = params_rate(params); + stream_config.frame_rate *= upscale_factor; + stream_config.channel_count = params_channels(params); + stream_config.bps = + snd_pcm_format_width(params_format(params)); + stream_config.direction = direction; + retval = sdw_config_stream(rt700->sdw->mstr, + rt700->sdw, &stream_config, stream->stream_tag); + if (retval) { + dev_err(dai->dev, "Unable to configure the stream\n"); + return retval; + } + port_config.num_ports = 1; + port_config.port_cfg = &port_cfg; + port_cfg.port_num = port; + num_channels = params_channels(params); + port_cfg.ch_mask = (1 << (num_channels)) - 1; + retval = sdw_config_port(rt700->sdw->mstr, rt700->sdw, + &port_config, stream->stream_tag); + if (retval) { + dev_err(dai->dev, "Unable to configure port\n"); + return retval; + } + + switch (params_rate(params)) { + /* bit 14 0:48K 1:44.1K */ + /* bit 15 Stream Type 0:PCM 1:Non-PCM, should always be PCM */ + case 44100: + snd_soc_component_write(component, RT700_DAC_FORMAT_H, 0x40); + snd_soc_component_write(component, RT700_ADC_FORMAT_H, 0x40); + break; + case 48000: + snd_soc_component_write(component, RT700_DAC_FORMAT_H, 0x0); + snd_soc_component_write(component, RT700_ADC_FORMAT_H, 0x0); + break; + default: + dev_err(component->dev, "Unsupported sample rate %d\n", + params_rate(params)); + return -EINVAL; + } + + if (params_channels(params) <= 16) { + /* bit 3:0 Number of Channel */ + val |= (params_channels(params) - 1); + } else { + dev_err(component->dev, "Unsupported channels %d\n", + params_channels(params)); + return -EINVAL; + } + + switch (params_width(params)) { + /* bit 6:4 Bits per Sample */ + case 8: + break; + case 16: + val |= (0x1 << 4); + break; + case 20: + val |= (0x2 << 4); + break; + case 24: + val |= (0x3 << 4); + break; + case 32: + val |= (0x4 << 4); + break; + default: + return -EINVAL; + } + + dev_dbg(component->dev, "format val = 0x%x\n", val); + + snd_soc_component_write(component, RT700_DAC_FORMAT_L, val); + snd_soc_component_write(component, RT700_ADC_FORMAT_L, val); + + return retval; +} + +int rt700_pcm_hw_free(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct rt700_priv *rt700 = snd_soc_component_get_drvdata(component); + struct sdw_stream_data *stream = snd_soc_dai_get_dma_data(dai, + substream); + if (!rt700->sdw) + return 0; + sdw_release_stream(rt700->sdw->mstr, rt700->sdw, stream->stream_tag); + return 0; +} + +#define RT700_STEREO_RATES (SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000) +#define RT700_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \ + SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S8) + +static struct snd_soc_dai_ops rt700_ops = { + .hw_params = rt700_pcm_hw_params, + .hw_free = rt700_pcm_hw_free, + .program_stream_tag = rt700_program_stream_tag, + .remove_stream_tag = rt700_remove_stream_tag, +}; + +static struct snd_soc_dai_driver rt700_dai[] = { + { + .name = "rt700-aif1", + .id = RT700_AIF1, + .playback = { + .stream_name = "DP1 Playback", + .channels_min = 2, + .channels_max = 2, + .rates = RT700_STEREO_RATES, + .formats = RT700_FORMATS, + }, + .capture = { + .stream_name = "DP2 Capture", + .channels_min = 2, + .channels_max = 2, + .rates = RT700_STEREO_RATES, + .formats = RT700_FORMATS, + }, + .ops = &rt700_ops, + }, + { + .name = "rt700-aif2", + .id = RT700_AIF2, + .playback = { + .stream_name = "DP3 Playback", + .channels_min = 2, + .channels_max = 2, + .rates = RT700_STEREO_RATES, + .formats = RT700_FORMATS, + }, + .capture = { + .stream_name = "DP4 Capture", + .channels_min = 2, + .channels_max = 2, + .rates = RT700_STEREO_RATES, + .formats = RT700_FORMATS, + }, + .ops = &rt700_ops, + }, +}; + +static ssize_t rt700_index_cmd_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct rt700_priv *rt700 = dev_get_drvdata(dev); + unsigned int sdw_addr_h, sdw_addr_l; + unsigned int sdw_data_3, sdw_data_2, sdw_data_1, sdw_data_0; + int i, cnt = 0; + + /* index */ + for (i = 0; i <= 0xa0; i++) { + rt700_index_read(rt700->regmap, i, &sdw_data_0); + cnt += snprintf(buf + cnt, 12, + "%02x = %04x\n", i, sdw_data_0); + } + + if (cnt >= PAGE_SIZE) + cnt = PAGE_SIZE - 1; + + return cnt; +} + +static ssize_t rt700_index_cmd_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct rt700_priv *rt700 = dev_get_drvdata(dev); + unsigned int sdw_addr_h, sdw_addr_l, sdw_data_h, sdw_data_l; + unsigned int index_reg, index_val; + int i; + + pr_debug("register \"%s\" count=%zu\n", buf, count); + for (i = 0; i < count; i++) { /*rt700->dbg_nidess */ + if (*(buf + i) <= '9' && *(buf + i) >= '0') + index_reg = (index_reg << 4) | + (*(buf + i) - '0'); + else if (*(buf + i) <= 'f' && *(buf + i) >= 'a') + index_reg = (index_reg << 4) | + ((*(buf + i) - 'a') + 0xa); + else if (*(buf + i) <= 'F' && *(buf + i) >= 'A') + index_reg = (index_reg << 4) | + ((*(buf + i) - 'A') + 0xa); + else + break; + } + + for (i = i + 1; i < count; i++) { + if (*(buf + i) <= '9' && *(buf + i) >= '0') + index_val = (index_val << 4) | + (*(buf + i) - '0'); + else if (*(buf + i) <= 'f' && *(buf + i) >= 'a') + index_val = (index_val << 4) | + ((*(buf + i) - 'a') + 0xa); + else if (*(buf + i) <= 'F' && *(buf + i) >= 'A') + index_val = (index_val << 4) | + ((*(buf + i) - 'A') + 0xa); + else + break; + } + + pr_debug("index_reg=0x%x index_val=0x%x\n", + index_reg, index_val); + + rt700_index_write(rt700->regmap, index_reg, index_val); + + return count; +} + +static DEVICE_ATTR(index_reg, 0664, rt700_index_cmd_show, rt700_index_cmd_store); + +static ssize_t rt700_hda_cmd_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct rt700_priv *rt700 = dev_get_drvdata(dev); + unsigned int sdw_addr_h, sdw_addr_l; + unsigned int sdw_data_3, sdw_data_2, sdw_data_1, sdw_data_0; + int i, cnt = 0; + unsigned int value; + + pr_debug("%s cnt=%d RT700_HDA_DUMP_LEN=%d PAGE_SIZE=%d\n", + __func__, cnt, RT700_HDA_DUMP_LEN, PAGE_SIZE); + for (i = 0; i < RT700_HDA_DUMP_LEN; i++) { + pr_debug("%s i=%d", __func__, i); + if (cnt + 25 >= PAGE_SIZE) + break; + rt700->dbg_nid = hda_dump_list[i].nid; + rt700->dbg_vid = hda_dump_list[i].vid; + rt700->dbg_payload = hda_dump_list[i].payload; + rt700_hda_read(rt700->regmap, rt700->dbg_vid, + rt700->dbg_nid, rt700->dbg_payload, &value); + + cnt += snprintf(buf + cnt, 25, + "%03x %02x %04x=%x\n", + rt700->dbg_vid, rt700->dbg_nid, + rt700->dbg_payload, value); + } + + if (cnt >= PAGE_SIZE) + cnt = PAGE_SIZE - 1; + + return cnt; +} + +static ssize_t rt700_hda_cmd_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct rt700_priv *rt700 = dev_get_drvdata(dev); + unsigned int sdw_addr_h, sdw_addr_l, sdw_data_h, sdw_data_l; + unsigned int sdw_data_3, sdw_data_2, sdw_data_1, sdw_data_0; + int i; + + pr_debug("register \"%s\" count=%zu\n", buf, count); + for (i = 0; i < count; i++) { /*rt700->dbg_nidess */ + if (*(buf + i) <= '9' && *(buf + i) >= '0') + rt700->dbg_nid = (rt700->dbg_nid << 4) | + (*(buf + i) - '0'); + else if (*(buf + i) <= 'f' && *(buf + i) >= 'a') + rt700->dbg_nid = (rt700->dbg_nid << 4) | + ((*(buf + i) - 'a') + 0xa); + else if (*(buf + i) <= 'F' && *(buf + i) >= 'A') + rt700->dbg_nid = (rt700->dbg_nid << 4) | + ((*(buf + i) - 'A') + 0xa); + else + break; + } + + for (i = i + 1; i < count; i++) { + if (*(buf + i) <= '9' && *(buf + i) >= '0') + rt700->dbg_vid = (rt700->dbg_vid << 4) | + (*(buf + i) - '0'); + else if (*(buf + i) <= 'f' && *(buf + i) >= 'a') + rt700->dbg_vid = (rt700->dbg_vid << 4) | + ((*(buf + i) - 'a') + 0xa); + else if (*(buf + i) <= 'F' && *(buf + i) >= 'A') + rt700->dbg_vid = (rt700->dbg_vid << 4) | + ((*(buf + i) - 'A') + 0xa); + else + break; + } + + if (rt700->dbg_vid < 0xf) + rt700->dbg_vid = rt700->dbg_vid << 8; + + for (i = i + 1; i < count; i++) { + if (*(buf + i) <= '9' && *(buf + i) >= '0') + rt700->dbg_payload = (rt700->dbg_payload << 4) | + (*(buf + i) - '0'); + else if (*(buf + i) <= 'f' && *(buf + i) >= 'a') + rt700->dbg_payload = (rt700->dbg_payload << 4) | + ((*(buf + i) - 'a') + 0xa); + else if (*(buf + i) <= 'F' && *(buf + i) >= 'A') + rt700->dbg_payload = (rt700->dbg_payload << 4) | + ((*(buf + i) - 'A') + 0xa); + else + break; + } + pr_debug("dbg_nid=0x%x dbg_vid=0x%x dbg_payload=0x%x\n", + rt700->dbg_nid, rt700->dbg_vid, rt700->dbg_payload); + + hda_to_sdw(rt700->dbg_nid, rt700->dbg_vid, rt700->dbg_payload, + &sdw_addr_h, &sdw_data_h, &sdw_addr_l, &sdw_data_l); + + regmap_write(rt700->regmap, sdw_addr_h, sdw_data_h); + if (!sdw_addr_l) + regmap_write(rt700->regmap, sdw_addr_l, sdw_data_l); + + + sdw_data_3 = 0; + sdw_data_2 = 0; + sdw_data_1 = 0; + sdw_data_0 = 0; + if (rt700->dbg_vid & 0x800) { /* get command */ + regmap_read(rt700->regmap, RT700_READ_HDA_3, &sdw_data_3); + regmap_read(rt700->regmap, RT700_READ_HDA_2, &sdw_data_2); + regmap_read(rt700->regmap, RT700_READ_HDA_1, &sdw_data_1); + regmap_read(rt700->regmap, RT700_READ_HDA_0, &sdw_data_0); + pr_info("read (%02x %03x %04x) = %02x%02x%02x%02x\n", + rt700->dbg_nid, rt700->dbg_vid, rt700->dbg_payload, + sdw_data_3, sdw_data_2, sdw_data_1, sdw_data_0); + } + + /* Enable Jack Detection */ + regmap_write(rt700->regmap, RT700_SET_MIC2_UNSOLICITED_ENABLE, 0x82); + regmap_write(rt700->regmap, RT700_SET_HP_UNSOLICITED_ENABLE, 0x81); + rt700_index_write(rt700->regmap, 0x10, 0x2420); + rt700_index_write(rt700->regmap, 0x19, 0x2e11); + + return count; +} + +static DEVICE_ATTR(hda_reg, 0664, rt700_hda_cmd_show, rt700_hda_cmd_store); + +/* Bus clock frequency */ +#define RT700_CLK_FREQ_9600000HZ 9600000 +#define RT700_CLK_FREQ_12000000HZ 12000000 +#define RT700_CLK_FREQ_6000000HZ 6000000 +#define RT700_CLK_FREQ_4800000HZ 4800000 +#define RT700_CLK_FREQ_2400000HZ 2400000 +#define RT700_CLK_FREQ_12288000HZ 12288000 + + +static int rt700_clock_config(struct device *dev, struct alc700 *alc700) +{ + struct rt700_priv *rt700 = dev_get_drvdata(dev); + int value, read_value1, read_value2; + + switch(alc700->params->bus_clk_freq) { + case RT700_CLK_FREQ_12000000HZ: + value = 0x0; + break; + case RT700_CLK_FREQ_6000000HZ: + value = 0x1; + break; + case RT700_CLK_FREQ_9600000HZ: + value = 0x2; + break; + case RT700_CLK_FREQ_4800000HZ: + value = 0x3; + break; + case RT700_CLK_FREQ_2400000HZ: + value = 0x4; + break; + case RT700_CLK_FREQ_12288000HZ: + value = 0x5; + break; + default: + return -EINVAL; + } + regmap_write(rt700->regmap, 0xe0, value); + regmap_write(rt700->regmap, 0xf0, value); + + return 0; +} + +int rt700_probe(struct device *dev, struct regmap *regmap, + struct sdw_slave *slave) +{ + struct rt700_priv *rt700; + struct alc700 *alc700 = dev_get_drvdata(dev); + int ret; + unsigned int value; + + rt700 = devm_kzalloc(dev, sizeof(struct rt700_priv), + GFP_KERNEL); + if (!rt700) + return -ENOMEM; + + dev_set_drvdata(dev, rt700); + + rt700->regmap = regmap; + rt700->sdw = slave; + + ret = devm_snd_soc_register_component(dev, + &soc_component_dev_rt700, rt700_dai, ARRAY_SIZE(rt700_dai)); + dev_info(&slave->dev, "%s\n", __func__); + + /* Set Tx route */ + /* Filter 02: index 91[13:12] 07[3] */ + /* Filter 03: index 5f[15:14] 07[4] */ + /* DAC (02) -> Front -> SPK (14)*/ + /* DAC (03) -> Surr -> HP (14)*/ + + /* Set Rx route */ + /* Rx_09: index 91[8] */ + /* Rx_08: index 91[6] */ + /* Mic2 (19) -> Mux (22) -> ADC (09) */ + //regmap_write(rt700->regmap, 0x3122, 0x0); /* Mic2 (19) -> Mux (22)*/ + + /* Assign stream ID */ + /* do it in dapm widget + regmap_write(rt700->regmap, RT700_SET_STREAMID_DAC1, 0x10); + regmap_write(rt700->regmap, RT700_SET_STREAMID_DAC2, 0x10); + regmap_write(rt700->regmap, RT700_SET_STREAMID_ADC2, 0x10); + regmap_write(rt700->regmap, RT700_SET_STREAMID_ADC1, 0x10); + */ + + /* Set Pin Widget */ + regmap_write(rt700->regmap, RT700_SET_PIN_HP, 0x40); + regmap_write(rt700->regmap, RT700_SET_PIN_SPK, 0x40); + //regmap_write(rt700->regmap, 0x3c14, 0x02); /* 14 70c 02 */ + regmap_write(rt700->regmap, RT700_SET_EAPD_SPK, RT700_EAPD_HIGH); + regmap_write(rt700->regmap, RT700_SET_PIN_DMIC1, 0x20); + regmap_write(rt700->regmap, RT700_SET_PIN_DMIC2, 0x20); + regmap_write(rt700->regmap, RT700_SET_PIN_MIC2, 0x20); + /* + regmap_write(rt700->regmap, RT700_SET_PIN_LINE1, 0x20); + regmap_write(rt700->regmap, RT700_SET_PIN_LINE2, 0x20); + */ + + /* Set Configuration Default */ + //regmap_write(rt700->regmap, 0x4f12, 0x00); + regmap_write(rt700->regmap, 0x4f12, 0x91); + regmap_write(rt700->regmap, 0x4e12, 0xd6); + regmap_write(rt700->regmap, 0x4d12, 0x11); + regmap_write(rt700->regmap, 0x4c12, 0x20); + regmap_write(rt700->regmap, 0x4f13, 0x91); + regmap_write(rt700->regmap, 0x4e13, 0xd6); + regmap_write(rt700->regmap, 0x4d13, 0x11); + regmap_write(rt700->regmap, 0x4c13, 0x21); + + regmap_write(rt700->regmap, 0x4f19, 0x02); + regmap_write(rt700->regmap, 0x4e19, 0xa1); + regmap_write(rt700->regmap, 0x4d19, 0x90); + regmap_write(rt700->regmap, 0x4c19, 0x80); + + /* Enable Line2 */ + regmap_write(rt700->regmap, 0x371b, 0x40); + regmap_write(rt700->regmap, 0x731b, 0xb0); + regmap_write(rt700->regmap, 0x839b, 0x00); + + /* Set index */ + rt700_index_write(rt700->regmap, 0x4a, 0x201b); + //rt700_index_write(rt700->regmap, 0x38, 0x4921); + + /* get the setting registers for debug + pr_debug("%s get the setting registers\n", __func__); + rt700_hda_read(rt700->regmap, 0xf07, 0x21, 0, &value); + rt700_hda_read(rt700->regmap, 0xf07, 0x14, 0, &value); + rt700_hda_read(rt700->regmap, 0xf07, 0x12, 0, &value); + rt700_hda_read(rt700->regmap, 0xf07, 0x13, 0, &value); + rt700_hda_read(rt700->regmap, 0xf07, 0x19, 0, &value); + rt700_hda_read(rt700->regmap, 0xf07, 0x1a, 0, &value); + rt700_hda_read(rt700->regmap, 0xf07, 0x1b, 0, &value); + rt700_hda_read(rt700->regmap, 0xf0c, 0x14, 0, &value); + */ + ret = rt700_clock_config(dev, alc700); + + /* Enable Jack Detection */ + regmap_write(rt700->regmap, RT700_SET_MIC2_UNSOLICITED_ENABLE, 0x82); + regmap_write(rt700->regmap, RT700_SET_HP_UNSOLICITED_ENABLE, 0x81); + rt700_index_write(rt700->regmap, 0x10, 0x2420); + rt700_index_write(rt700->regmap, 0x19, 0x2e11); + + /* Finish Initial Settings, set power to D3 */ + regmap_write(rt700->regmap, RT700_SET_AUDIO_POWER_STATE, AC_PWRST_D3); + + ret = device_create_file(&slave->dev, &dev_attr_index_reg); + if (ret != 0) { + dev_err(&slave->dev, + "Failed to create index_reg sysfs files: %d\n", ret); + return ret; + } + + ret = device_create_file(&slave->dev, &dev_attr_hda_reg); + if (ret != 0) { + dev_err(&slave->dev, + "Failed to create hda_reg sysfs files: %d\n", ret); + return ret; + } + + pm_runtime_set_autosuspend_delay(&slave->dev, 3000); + pm_runtime_use_autosuspend(&slave->dev); + pm_runtime_enable(&slave->dev); + pm_runtime_put_sync_autosuspend(&slave->dev); + return ret; +} +EXPORT_SYMBOL(rt700_probe); + +int rt700_remove(struct device *dev) +{ + + dev_info(dev, "Removing\n"); + + return 0; +} +EXPORT_SYMBOL(rt700_remove); + +#ifdef CONFIG_PM +static int rt700_runtime_suspend(struct device *dev) +{ + return 0; +} + +static int rt700_runtime_resume(struct device *dev) +{ + struct rt700_priv *rt700 = dev_get_drvdata(dev); + int ret; + int timeout = 0; + + if(rt700->sdw) { + ret = sdw_wait_for_slave_enumeration(rt700->sdw->mstr, + rt700->sdw); + if (ret < 0) + return ret; + } + + return 0; +} +#endif + +const struct dev_pm_ops rt700_runtime_pm = { + SET_RUNTIME_PM_OPS(rt700_runtime_suspend, rt700_runtime_resume, + NULL) +}; +EXPORT_SYMBOL(rt700_runtime_pm); + +MODULE_DESCRIPTION("ASoC rt700 driver"); +MODULE_DESCRIPTION("ASoC rt700 driver SDW"); +MODULE_AUTHOR("Bard Liao "); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/rt700.h b/sound/soc/codecs/rt700.h new file mode 100644 index 000000000000..3ad8b84f60f7 --- /dev/null +++ b/sound/soc/codecs/rt700.h @@ -0,0 +1,161 @@ +/* + * rt700.h -- RT700 ALSA SoC audio driver header + * + * Copyright 2016 Realtek, Inc. + * + * Author: Bard Liao + * Dummy ASoC Codec Driver based Cirrus Logic CS42L42 Codec + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +#ifndef __RT700_H__ +#define __RT700_H__ + +#include + +extern const struct dev_pm_ops rt700_runtime_pm; + +struct rt700_priv { + struct regmap *regmap; + struct snd_soc_codec *codec; + struct gpio_desc *reset_gpio; + u32 sclk; + u8 hpout_load; + u8 hpout_clamp; + struct sdw_slv *sdw; + int dbg_nid; + int dbg_vid; + int dbg_payload; +}; + +struct alc700 { + struct sdw_slv *sdw; + struct sdw_bus_params *params; +}; + +/* NID */ +#define RT700_AUDIO_FUNCTION_GROUP 0x01 +#define RT700_DAC_OUT1 0x02 +#define RT700_DAC_OUT2 0x03 +#define RT700_ADC_IN1 0x09 +#define RT700_ADC_IN2 0x08 +#define RT700_DMIC1 0x12 +#define RT700_DMIC2 0x13 +#define RT700_SPK_OUT 0x14 +#define RT700_MIC2 0x19 +#define RT700_LINE1 0x1a +#define RT700_LINE2 0x1b +#define RT700_BEEP 0x1d +#define RT700_SPDIF 0x1e +#define RT700_VENDOR_REGISTERS 0x20 +#define RT700_HP_OUT 0x21 +#define RT700_MIXER_IN1 0x22 +#define RT700_MIXER_IN2 0x23 +#define RT700_INLINE_CMD 0x55 + +/* Verb */ +#define RT700_VERB_SET_CONNECT_SEL 0x3100 +#define RT700_VERB_SET_EAPD_BTLENABLE 0x3c00 +#define RT700_VERB_GET_CONNECT_SEL 0xb100 +#define RT700_VERB_SET_POWER_STATE 0x3500 +#define RT700_VERB_SET_CHANNEL_STREAMID 0x3600 +#define RT700_VERB_SET_PIN_WIDGET_CONTROL 0x3700 +#define RT700_VERB_SET_UNSOLICITED_ENABLE 0x3800 +#define RT700_SET_AMP_GAIN_MUTE_H 0x7300 +#define RT700_SET_AMP_GAIN_MUTE_L 0x8380 + +#define RT700_READ_HDA_3 0x2012 +#define RT700_READ_HDA_2 0x2013 +#define RT700_READ_HDA_1 0x2014 +#define RT700_READ_HDA_0 0x2015 +#define RT700_PRIV_INDEX_W_H 0x7520 +#define RT700_PRIV_INDEX_W_L 0x85a0 +#define RT700_PRIV_DATA_W_H 0x7420 +#define RT700_PRIV_DATA_W_L 0x84a0 +#define RT700_PRIV_INDEX_R_H 0x9d20 +#define RT700_PRIV_INDEX_R_L 0xada0 +#define RT700_PRIV_DATA_R_H 0x9c20 +#define RT700_PRIV_DATA_R_L 0xaca0 +#define RT700_DAC_FORMAT_H 0x7203 +#define RT700_DAC_FORMAT_L 0x8283 +#define RT700_ADC_FORMAT_H 0x7209 +#define RT700_ADC_FORMAT_L 0x8289 +#define RT700_SET_AUDIO_POWER_STATE\ + (RT700_VERB_SET_POWER_STATE | RT700_AUDIO_FUNCTION_GROUP) +#define RT700_SET_PIN_DMIC1\ + (RT700_VERB_SET_PIN_WIDGET_CONTROL | RT700_DMIC1) +#define RT700_SET_PIN_DMIC2\ + (RT700_VERB_SET_PIN_WIDGET_CONTROL | RT700_DMIC2) +#define RT700_SET_PIN_SPK\ + (RT700_VERB_SET_PIN_WIDGET_CONTROL | RT700_SPK_OUT) +#define RT700_SET_PIN_HP\ + (RT700_VERB_SET_PIN_WIDGET_CONTROL | RT700_HP_OUT) +#define RT700_SET_PIN_MIC2\ + (RT700_VERB_SET_PIN_WIDGET_CONTROL | RT700_MIC2) +#define RT700_SET_PIN_LINE1\ + (RT700_VERB_SET_PIN_WIDGET_CONTROL | RT700_LINE1) +#define RT700_SET_PIN_LINE2\ + (RT700_VERB_SET_PIN_WIDGET_CONTROL | RT700_LINE2) +#define RT700_SET_MIC2_UNSOLICITED_ENABLE\ + (RT700_VERB_SET_UNSOLICITED_ENABLE | RT700_MIC2) +#define RT700_SET_HP_UNSOLICITED_ENABLE\ + (RT700_VERB_SET_UNSOLICITED_ENABLE | RT700_HP_OUT) +#define RT700_SET_STREAMID_DAC1\ + (RT700_VERB_SET_CHANNEL_STREAMID | RT700_DAC_OUT1) +#define RT700_SET_STREAMID_DAC2\ + (RT700_VERB_SET_CHANNEL_STREAMID | RT700_DAC_OUT2) +#define RT700_SET_STREAMID_ADC1\ + (RT700_VERB_SET_CHANNEL_STREAMID | RT700_ADC_IN1) +#define RT700_SET_STREAMID_ADC2\ + (RT700_VERB_SET_CHANNEL_STREAMID | RT700_ADC_IN2) +#define RT700_SET_GAIN_DAC1_L\ + (RT700_SET_AMP_GAIN_MUTE_L | RT700_DAC_OUT1) +#define RT700_SET_GAIN_DAC1_H\ + (RT700_SET_AMP_GAIN_MUTE_H | RT700_DAC_OUT1) +#define RT700_SET_GAIN_ADC1_L\ + (RT700_SET_AMP_GAIN_MUTE_L | RT700_ADC_IN1) +#define RT700_SET_GAIN_ADC1_H\ + (RT700_SET_AMP_GAIN_MUTE_H | RT700_ADC_IN1) +#define RT700_SET_GAIN_ADC2_L\ + (RT700_SET_AMP_GAIN_MUTE_L | RT700_ADC_IN2) +#define RT700_SET_GAIN_ADC2_H\ + (RT700_SET_AMP_GAIN_MUTE_H | RT700_ADC_IN2) +#define RT700_SET_GAIN_AMIC_L\ + (RT700_SET_AMP_GAIN_MUTE_L | RT700_MIC2) +#define RT700_SET_GAIN_AMIC_H\ + (RT700_SET_AMP_GAIN_MUTE_H | RT700_MIC2) +#define RT700_SET_GAIN_HP_L\ + (RT700_SET_AMP_GAIN_MUTE_L | RT700_HP_OUT) +#define RT700_SET_GAIN_HP_H\ + (RT700_SET_AMP_GAIN_MUTE_H | RT700_HP_OUT) +#define RT700_SET_GAIN_SPK_L\ + (RT700_SET_AMP_GAIN_MUTE_L | RT700_SPK_OUT) +#define RT700_SET_GAIN_SPK_H\ + (RT700_SET_AMP_GAIN_MUTE_H | RT700_SPK_OUT) +#define RT700_SET_EAPD_SPK\ + (RT700_VERB_SET_EAPD_BTLENABLE | RT700_SPK_OUT) + +#define RT700_EAPD_HIGH 0x2 +#define RT700_EAPD_LOW 0x0 +#define RT700_MUTE_SFT 7 +#define RT700_DIR_IN_SFT 6 +#define RT700_DIR_OUT_SFT 7 + +enum { + RT700_AIF1, + RT700_AIF2, + RT700_AIFS, +}; + +int rt700_probe(struct device *dev, struct regmap *regmap, + struct sdw_slave *slave); +int rt700_remove(struct device *dev); +int hda_to_sdw(unsigned int nid, unsigned int verb, unsigned int payload, + unsigned int *sdw_addr_h, unsigned int *sdw_data_h, + unsigned int *sdw_addr_l, unsigned int *sdw_data_l); +int rt700_jack_detect(struct rt700_priv *rt700, bool *hp, bool *mic); +#endif /* __RT700_H__ */ From 4b37c358d2ff6075aa93ee1d6d133d77c0d8202e Mon Sep 17 00:00:00 2001 From: Hardik Shah Date: Tue, 28 Jun 2016 15:48:56 +0530 Subject: [PATCH 0689/1103] ASoC:Intel: Add support for ALC700 machine driver Change-Id: Idcec9e08faaedbb92f714548d9fea15af4de6b91 Signed-off-by: Hardik Shah Signed-off-by: Guneshwor Singh --- sound/soc/intel/boards/Kconfig | 11 + sound/soc/intel/boards/Makefile | 2 + sound/soc/intel/boards/cnl_rt700.c | 319 +++++++++++++++++++++++++++++ sound/soc/intel/skylake/skl.c | 162 +++++++++++++++ 4 files changed, 494 insertions(+) create mode 100644 sound/soc/intel/boards/cnl_rt700.c diff --git a/sound/soc/intel/boards/Kconfig b/sound/soc/intel/boards/Kconfig index ca140a77d992..e6b065fa1dff 100644 --- a/sound/soc/intel/boards/Kconfig +++ b/sound/soc/intel/boards/Kconfig @@ -306,6 +306,17 @@ config SND_SOC_INTEL_CNL_CS42L42_MACH Say Y or m if you have such a device. This is a recommended option. If unsure select "N". +config SND_SOC_INTEL_CNL_RT700_MACH + tristate "Cannonlake with RT700 SDW mode" + depends on MFD_INTEL_LPSS && I2C && ACPI + select SND_SOC_RT700 + select SND_SOC_DMIC + help + This adds support for ASoC RT700 codec SDW machine driver. This will + create an alsa sound card for RT700. + Say Y or m if you have such a device. This is a recommended option. + If unsure select "N". + config SND_SOC_INTEL_CNL_SVFPGA_MACH tristate "Cannonlake with SVFPGA PDM SDW mode" depends on MFD_INTEL_LPSS && I2C && ACPI diff --git a/sound/soc/intel/boards/Makefile b/sound/soc/intel/boards/Makefile index 4f2b9b38edd0..c5e0ff065610 100644 --- a/sound/soc/intel/boards/Makefile +++ b/sound/soc/intel/boards/Makefile @@ -23,6 +23,7 @@ snd-soc-skl_rt286-objs := skl_rt286.o snd-skl_nau88l25_max98357a-objs := skl_nau88l25_max98357a.o snd-soc-skl_nau88l25_ssm4567-objs := skl_nau88l25_ssm4567.o snd-soc-cnl_cs42l42-objs := cnl_cs42l42.o +snd-soc-cnl_rt700-objs := cnl_rt700.o snd-soc-cnl_svfpga-objs := cnl_svfpga.o obj-$(CONFIG_SND_SOC_INTEL_HASWELL_MACH) += snd-soc-sst-haswell.o @@ -49,4 +50,5 @@ obj-$(CONFIG_SND_SOC_INTEL_SKL_RT286_MACH) += snd-soc-skl_rt286.o obj-$(CONFIG_SND_SOC_INTEL_SKL_NAU88L25_MAX98357A_MACH) += snd-skl_nau88l25_max98357a.o obj-$(CONFIG_SND_SOC_INTEL_SKL_NAU88L25_SSM4567_MACH) += snd-soc-skl_nau88l25_ssm4567.o obj-$(CONFIG_SND_SOC_INTEL_CNL_CS42L42_MACH) += snd-soc-cnl_cs42l42.o +obj-$(CONFIG_SND_SOC_INTEL_CNL_RT700_MACH) += snd-soc-cnl_rt700.o obj-$(CONFIG_SND_SOC_INTEL_CNL_SVFPGA_MACH) += snd-soc-cnl_svfpga.o diff --git a/sound/soc/intel/boards/cnl_rt700.c b/sound/soc/intel/boards/cnl_rt700.c new file mode 100644 index 000000000000..e0b93e571fb9 --- /dev/null +++ b/sound/soc/intel/boards/cnl_rt700.c @@ -0,0 +1,319 @@ +/* + * cnl_rt700.c - ASOC Machine driver for Intel cnl_rt700 platform + * with ALC700 SoundWire codec. + * + * Copyright (C) 2016 Intel Corp + * Author: Hardik Shah + * + * Based on + * moor_dpcm_florida.c - ASOC Machine driver for Intel Moorefield platform + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * 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; version 2 of the License. + * + * 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. + * + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct cnl_rt700_mc_private { + u8 pmic_id; + void __iomem *osc_clk0_reg; + int bt_mode; +}; + +static const struct snd_soc_dapm_widget cnl_rt700_widgets[] = { + SND_SOC_DAPM_HP("Headphones", NULL), + SND_SOC_DAPM_MIC("AMIC", NULL), + SND_SOC_DAPM_MIC("SoC DMIC", NULL), + SND_SOC_DAPM_SPK("Speaker", NULL), +}; + +static const struct snd_soc_dapm_route cnl_rt700_map[] = { + /*Headphones*/ + { "Headphones", NULL, "HP" }, + { "Speaker", NULL, "SPK" }, + { "I2NP", NULL, "AMIC" }, + + /* SWM map link the SWM outs to codec AIF */ + { "DP1 Playback", NULL, "SDW Tx"}, + { "SDW Tx", NULL, "sdw_codec0_out"}, + { "SDW Tx10", NULL, "sdw_codec1_out"}, + + { "sdw_codec0_in", NULL, "SDW Rx" }, + { "SDW Rx", NULL, "DP2 Capture" }, + {"sdw_codec2_in", NULL, "SDW Rx10"}, + {"SDW Rx10", NULL, "DP4 Capture"}, + + {"DMic", NULL, "SoC DMIC"}, + {"DMIC01 Rx", NULL, "Capture"}, + {"dmic01_hifi", NULL, "DMIC01 Rx"}, + +}; + +static const struct snd_kcontrol_new cnl_rt700_controls[] = { + SOC_DAPM_PIN_SWITCH("Headphones"), + SOC_DAPM_PIN_SWITCH("AMIC"), + SOC_DAPM_PIN_SWITCH("Speaker"), +}; + + +static int cnl_rt700_init(struct snd_soc_pcm_runtime *runtime) +{ + int ret; + struct snd_soc_card *card = runtime->card; + + pr_info("Entry %s\n", __func__); + card->dapm.idle_bias_off = true; + + ret = snd_soc_add_card_controls(card, cnl_rt700_controls, + ARRAY_SIZE(cnl_rt700_controls)); + if (ret) { + pr_err("unable to add card controls\n"); + return ret; + } + return 0; +} + +static unsigned int rates_48000[] = { + 48000, + 16000, + 8000, +}; + +static struct snd_pcm_hw_constraint_list constraints_48000 = { + .count = ARRAY_SIZE(rates_48000), + .list = rates_48000, +}; + +static int cnl_rt700_startup(struct snd_pcm_substream *substream) +{ + return snd_pcm_hw_constraint_list(substream->runtime, 0, + SNDRV_PCM_HW_PARAM_RATE, + &constraints_48000); +} + +static struct snd_soc_ops cnl_rt700_ops = { + .startup = cnl_rt700_startup, +}; + +static int cnl_rt700_codec_fixup(struct snd_soc_pcm_runtime *rtd, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_dai *be_cpu_dai; + int slot_width = 24; + struct snd_interval *rate = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_RATE); + struct snd_interval *channels = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_CHANNELS); + + pr_debug("Invoked %s for dailink %s\n", __func__, rtd->dai_link->name); + slot_width = 24; + rate->min = rate->max = 48000; + channels->min = channels->max = 2; + snd_mask_none(hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT)); + snd_mask_set(hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT), + (unsigned int __force)SNDRV_PCM_FORMAT_S24_LE); + + pr_info("param width set to:0x%x\n", + snd_pcm_format_width(params_format(params))); + pr_info("Slot width = %d\n", slot_width); + + be_cpu_dai = rtd->cpu_dai; + return 0; +} + +static int cnl_dmic_fixup(struct snd_soc_pcm_runtime *rtd, + struct snd_pcm_hw_params *params) +{ + struct snd_interval *channels = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_CHANNELS); + channels->min = channels->max = 2; + + return 0; +} + +static struct snd_soc_dai_link cnl_rt700_msic_dailink[] = { + { + .name = "Bxtn Audio Port", + .stream_name = "Audio", + .cpu_dai_name = "System Pin", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .platform_name = "0000:02:18.0", + .init = cnl_rt700_init, + .ignore_suspend = 1, + .nonatomic = 1, + .dynamic = 1, + .dpcm_playback = 1, + .dpcm_capture = 1, + .ops = &cnl_rt700_ops, + }, + { + .name = "CNL Reference Port", + .stream_name = "Reference Capture", + .cpu_dai_name = "Reference Pin", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .platform_name = "0000:02:18.0", + .ignore_suspend = 1, + .nonatomic = 1, + .dynamic = 1, + .dpcm_capture = 1, + .ops = &cnl_rt700_ops, + }, + { + .name = "CNL Deepbuffer Port", + .stream_name = "Deep Buffer Audio", + .cpu_dai_name = "Deepbuffer Pin", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .platform_name = "0000:02:18.0", + .dpcm_playback = 1, + .ignore_suspend = 1, + .nonatomic = 1, + .dynamic = 1, + .ops = &cnl_rt700_ops, + }, + + { + .name = "SDW0-Codec", + .cpu_dai_name = "SDW Pin", + .platform_name = "0000:02:18.0", + .codec_name = "sdw-slave0-10:02:5d:07:01:00", + .codec_dai_name = "rt700-aif1", + .be_hw_params_fixup = cnl_rt700_codec_fixup, + .ignore_suspend = 1, + .no_pcm = 1, + .dpcm_playback = 1, + .dpcm_capture = 1, + }, + { + .name = "SDW1-Codec", + .cpu_dai_name = "SDW10 Pin", + .platform_name = "0000:02:18.0", + .codec_name = "sdw-slave0-10:02:5d:07:01:00", + .codec_dai_name = "rt700-aif2", + .be_hw_params_fixup = cnl_rt700_codec_fixup, + .ignore_suspend = 1, + .no_pcm = 1, + .dpcm_playback = 1, + .dpcm_capture = 1, + }, + { + .name = "dmic01", + .cpu_dai_name = "DMIC01 Pin", + .codec_name = "dmic-codec", + .codec_dai_name = "dmic-hifi", + .platform_name = "0000:02:18.0", + .ignore_suspend = 1, + .no_pcm = 1, + .dpcm_capture = 1, + .be_hw_params_fixup = cnl_dmic_fixup, + }, +}; + +/* SoC card */ +static struct snd_soc_card snd_soc_card_cnl_rt700 = { + .name = "cnl_rt700-audio", + .dai_link = cnl_rt700_msic_dailink, + .num_links = ARRAY_SIZE(cnl_rt700_msic_dailink), + .dapm_widgets = cnl_rt700_widgets, + .num_dapm_widgets = ARRAY_SIZE(cnl_rt700_widgets), + .dapm_routes = cnl_rt700_map, + .num_dapm_routes = ARRAY_SIZE(cnl_rt700_map), +}; + + +static int snd_cnl_rt700_mc_probe(struct platform_device *pdev) +{ + int ret_val = 0; + struct cnl_rt700_mc_private *drv; + + pr_debug("Entry %s\n", __func__); + + drv = devm_kzalloc(&pdev->dev, sizeof(*drv), GFP_KERNEL); + if (!drv) + return -ENOMEM; + + snd_soc_card_cnl_rt700.dev = &pdev->dev; + snd_soc_card_set_drvdata(&snd_soc_card_cnl_rt700, drv); + /* Register the card */ + ret_val = snd_soc_register_card(&snd_soc_card_cnl_rt700); + if (ret_val && (ret_val != -EPROBE_DEFER)) { + pr_err("snd_soc_register_card failed %d\n", ret_val); + goto unalloc; + } + platform_set_drvdata(pdev, &snd_soc_card_cnl_rt700); + return ret_val; + +unalloc: + return ret_val; +} + +static int snd_cnl_rt700_mc_remove(struct platform_device *pdev) +{ + struct snd_soc_card *soc_card = platform_get_drvdata(pdev); + struct cnl_rt700_mc_private *drv = snd_soc_card_get_drvdata(soc_card); + + devm_kfree(&pdev->dev, drv); + snd_soc_card_set_drvdata(soc_card, NULL); + snd_soc_unregister_card(soc_card); + platform_set_drvdata(pdev, NULL); + return 0; +} + +static const struct platform_device_id cnl_board_ids[] = { + { .name = "cnl_rt700" }, + { .name = "icl_rt700" }, + { } +}; + +static struct platform_driver snd_cnl_rt700_mc_driver = { + .driver = { + .name = "cnl_rt700", + }, + .probe = snd_cnl_rt700_mc_probe, + .remove = snd_cnl_rt700_mc_remove, + .id_table = cnl_board_ids +}; + +static int snd_cnl_rt700_driver_init(void) +{ + return platform_driver_register(&snd_cnl_rt700_mc_driver); +} +module_init(snd_cnl_rt700_driver_init); + +static void snd_cnl_rt700_driver_exit(void) +{ + platform_driver_unregister(&snd_cnl_rt700_mc_driver); +} +module_exit(snd_cnl_rt700_driver_exit) + +MODULE_DESCRIPTION("ASoC CNL Machine driver"); +MODULE_AUTHOR("Hardik Shah "); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:cnl_rt700"); +MODULE_ALIAS("platform:icl_rt700"); diff --git a/sound/soc/intel/skylake/skl.c b/sound/soc/intel/skylake/skl.c index feb4ab5fc9b4..19ee283f9586 100644 --- a/sound/soc/intel/skylake/skl.c +++ b/sound/soc/intel/skylake/skl.c @@ -499,12 +499,17 @@ static int skl_find_machine(struct skl *skl, void *driver_data) struct snd_soc_acpi_mach *mach = driver_data; struct skl_machine_pdata *pdata; + if (IS_ENABLED(CONFIG_SND_SOC_RT700) || + IS_ENABLED(CONFIG_SND_SOC_INTEL_CNL_FPGA)) + goto out; + mach = snd_soc_acpi_find_machine(mach); if (mach == NULL) { dev_err(bus->dev, "No matching machine driver found\n"); return -ENODEV; } +out: skl->mach = mach; skl->fw_name = mach->fw_filename; pdata = mach->pdata; @@ -1053,6 +1058,163 @@ static void skl_remove(struct pci_dev *pci) dev_set_drvdata(&pci->dev, NULL); } +static struct snd_soc_acpi_codecs skl_codecs = { + .num_codecs = 1, + .codecs = {"10508825"} +}; + +static struct snd_soc_acpi_codecs kbl_codecs = { + .num_codecs = 1, + .codecs = {"10508825"} +}; + +static struct snd_soc_acpi_codecs bxt_codecs = { + .num_codecs = 1, + .codecs = {"MX98357A"} +}; + +static struct snd_soc_acpi_codecs kbl_poppy_codecs = { + .num_codecs = 1, + .codecs = {"10EC5663"} +}; + +static struct snd_soc_acpi_codecs kbl_5663_5514_codecs = { + .num_codecs = 2, + .codecs = {"10EC5663", "10EC5514"} +}; + +static struct snd_soc_acpi_codecs kbl_7219_98357_codecs = { + .num_codecs = 1, + .codecs = {"MX98357A"} +}; + +static struct skl_machine_pdata cnl_pdata = { + .use_tplg_pcm = true, +}; + +static struct snd_soc_acpi_mach sst_skl_devdata[] = { + { + .id = "INT343A", + .drv_name = "skl_alc286s_i2s", + .fw_filename = "intel/dsp_fw_release.bin", + }, + { + .id = "INT343B", + .drv_name = "skl_n88l25_s4567", + .fw_filename = "intel/dsp_fw_release.bin", + .machine_quirk = snd_soc_acpi_codec_list, + .quirk_data = &skl_codecs, + .pdata = &skl_dmic_data + }, + { + .id = "MX98357A", + .drv_name = "skl_n88l25_m98357a", + .fw_filename = "intel/dsp_fw_release.bin", + .machine_quirk = snd_soc_acpi_codec_list, + .quirk_data = &skl_codecs, + .pdata = &skl_dmic_data + }, + {} +}; + +static struct snd_soc_acpi_mach sst_bxtp_devdata[] = { + { + .id = "INT343A", + .drv_name = "bxt_alc298s_i2s", + .fw_filename = "intel/dsp_fw_bxtn.bin", + }, + { + .id = "DLGS7219", + .drv_name = "bxt_da7219_max98357a_i2s", + .fw_filename = "intel/dsp_fw_bxtn.bin", + .machine_quirk = snd_soc_acpi_codec_list, + .quirk_data = &bxt_codecs, + }, + {} +}; + +static struct snd_soc_acpi_mach sst_kbl_devdata[] = { + { + .id = "INT343A", + .drv_name = "kbl_alc286s_i2s", + .fw_filename = "intel/dsp_fw_kbl.bin", + }, + { + .id = "INT343B", + .drv_name = "kbl_n88l25_s4567", + .fw_filename = "intel/dsp_fw_kbl.bin", + .machine_quirk = snd_soc_acpi_codec_list, + .quirk_data = &kbl_codecs, + .pdata = &skl_dmic_data + }, + { + .id = "MX98357A", + .drv_name = "kbl_n88l25_m98357a", + .fw_filename = "intel/dsp_fw_kbl.bin", + .machine_quirk = snd_soc_acpi_codec_list, + .quirk_data = &kbl_codecs, + .pdata = &skl_dmic_data + }, + { + .id = "MX98927", + .drv_name = "kbl_r5514_5663_max", + .fw_filename = "intel/dsp_fw_kbl.bin", + .machine_quirk = snd_soc_acpi_codec_list, + .quirk_data = &kbl_5663_5514_codecs, + .pdata = &skl_dmic_data + }, + { + .id = "MX98927", + .drv_name = "kbl_rt5663_m98927", + .fw_filename = "intel/dsp_fw_kbl.bin", + .machine_quirk = snd_soc_acpi_codec_list, + .quirk_data = &kbl_poppy_codecs, + .pdata = &skl_dmic_data + }, + { + .id = "10EC5663", + .drv_name = "kbl_rt5663", + .fw_filename = "intel/dsp_fw_kbl.bin", + }, + { + .id = "DLGS7219", + .drv_name = "kbl_da7219_max98357a", + .fw_filename = "intel/dsp_fw_kbl.bin", + .machine_quirk = snd_soc_acpi_codec_list, + .quirk_data = &kbl_7219_98357_codecs, + .pdata = &skl_dmic_data + }, + + {} +}; + +static struct snd_soc_acpi_mach sst_glk_devdata[] = { + { + .id = "INT343A", + .drv_name = "glk_alc298s_i2s", + .fw_filename = "intel/dsp_fw_glk.bin", + }, + {} +}; + +static const struct snd_soc_acpi_mach sst_cnl_devdata[] = { +#if !IS_ENABLED(CONFIG_SND_SOC_RT700) + { + .id = "INT34C2", + .drv_name = "cnl_rt274", + .fw_filename = "intel/dsp_fw_cnl.bin", + .pdata = &cnl_pdata, + }, +#else + { + .drv_name = "cnl_rt700", + .fw_filename = "intel/dsp_fw_cnl.bin", + .pdata = &cnl_pdata, + }, +#endif + {} +}; + /* PCI IDs */ static const struct pci_device_id skl_ids[] = { /* Sunrise Point-LP */ From 15e1c8932160469665649dab50bca5ea7ec518fc Mon Sep 17 00:00:00 2001 From: Guneshwor Singh Date: Mon, 26 Jun 2017 12:12:48 +0530 Subject: [PATCH 0690/1103] ASoC: Intel: CNL: Add new BE dai for ALC701 HS plyaback Change-Id: I64ef09b6147472ce8a78e5a689413b8f4384132c Signed-off-by: Guneshwor Singh --- sound/soc/intel/skylake/skl-pcm.c | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/sound/soc/intel/skylake/skl-pcm.c b/sound/soc/intel/skylake/skl-pcm.c index 3ac58520240a..09899a05975f 100644 --- a/sound/soc/intel/skylake/skl-pcm.c +++ b/sound/soc/intel/skylake/skl-pcm.c @@ -1342,6 +1342,28 @@ static struct snd_soc_dai_driver skl_platform_dai[] = { .formats = SNDRV_PCM_FMTBIT_S16_LE, }, }, +{ + /* Currently adding 1 playback and 1 capture pin, ideally it + * should be coming from CLT based on endpoints to be supported + */ + .name = "SDW10 Pin", + .id = SDW_BE_DAI_ID_MSTR0, + .ops = &skl_sdw_dai_ops, + .playback = { + .stream_name = "SDW Tx10", + .channels_min = HDA_STEREO, + .channels_max = HDA_STEREO, + .rates = SNDRV_PCM_RATE_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + }, + .capture = { + .stream_name = "SDW Rx10", + .channels_min = HDA_STEREO, + .channels_max = HDA_STEREO, + .rates = SNDRV_PCM_RATE_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + }, +}, { /* Currently adding 1 capture pin, for PDM ideally it * should be coming from CLT based on endpoints to be supported From dee42a4ed95d86703480744e226f3b69d6259dd8 Mon Sep 17 00:00:00 2001 From: Guneshwor Singh Date: Mon, 19 Sep 2016 13:59:36 +0530 Subject: [PATCH 0691/1103] ASoC: Intel: CNL: Enable sdw interrupt during D0 Change-Id: I3f34bbbf273da5a0a9a15df319b95178e8b41fa0 Signed-off-by: Guneshwor Singh --- sound/soc/intel/skylake/cnl-sst.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sound/soc/intel/skylake/cnl-sst.c b/sound/soc/intel/skylake/cnl-sst.c index 18be70b87728..a124dabc353a 100644 --- a/sound/soc/intel/skylake/cnl-sst.c +++ b/sound/soc/intel/skylake/cnl-sst.c @@ -288,6 +288,7 @@ static int cnl_set_dsp_D0(struct sst_dsp *ctx, unsigned int core_id) if (core_id == SKL_DSP_CORE0_ID) { /* enable interrupt */ cnl_ipc_int_enable(ctx); + cnl_sdw_int_enable(ctx, true); cnl_ipc_op_int_enable(ctx); cnl->boot_complete = false; From 1703ad80db6406b3d1d8c58bc5b16c141e790024 Mon Sep 17 00:00:00 2001 From: Mousumi Jana Date: Sun, 8 Nov 2015 19:12:28 +0530 Subject: [PATCH 0692/1103] ASoC: Intel: Skylake:fix for large get config api This patch adds support for get large config API when need to send the tx parameter to retrieve one or more configuration parameters from specified module instance. Change-Id: I3db4398e52e63176cb25ec37ff06db2b8f73f72b Signed-off-by: Mousumi Jana Reviewed-on: Reviewed-by: Babu, Ramesh Tested-by: Babu, Ramesh --- sound/soc/intel/skylake/skl-messages.c | 2 +- sound/soc/intel/skylake/skl-sst-ipc.c | 26 ++++++++++++++++++-------- sound/soc/intel/skylake/skl-sst-ipc.h | 3 ++- 3 files changed, 21 insertions(+), 10 deletions(-) diff --git a/sound/soc/intel/skylake/skl-messages.c b/sound/soc/intel/skylake/skl-messages.c index fa8ef710900c..13d1898da7fb 100644 --- a/sound/soc/intel/skylake/skl-messages.c +++ b/sound/soc/intel/skylake/skl-messages.c @@ -1538,5 +1538,5 @@ int skl_get_module_params(struct skl_sst *ctx, u32 *params, int size, msg.param_data_size = size; msg.large_param_id = param_id; - return skl_ipc_get_large_config(&ctx->ipc, &msg, params); + return skl_ipc_get_large_config(&ctx->ipc, &msg, params, NULL, 0); } diff --git a/sound/soc/intel/skylake/skl-sst-ipc.c b/sound/soc/intel/skylake/skl-sst-ipc.c index b83a3076a1e3..7b6ce92c9d0e 100644 --- a/sound/soc/intel/skylake/skl-sst-ipc.c +++ b/sound/soc/intel/skylake/skl-sst-ipc.c @@ -1002,12 +1002,13 @@ int skl_ipc_set_large_config(struct sst_generic_ipc *ipc, EXPORT_SYMBOL_GPL(skl_ipc_set_large_config); int skl_ipc_get_large_config(struct sst_generic_ipc *ipc, - struct skl_ipc_large_config_msg *msg, u32 *param) + struct skl_ipc_large_config_msg *msg, u32 *param, + u32 *txparam, u32 size) { struct skl_ipc_header header = {0}; u64 *ipc_header = (u64 *)(&header); int ret = 0; - size_t sz_remaining, rx_size, data_offset; + size_t sz_remaining, rx_size, data_offset, inbox_sz; header.primary = IPC_MSG_TARGET(IPC_MOD_MSG); header.primary |= IPC_MSG_DIR(IPC_MSG_REQUEST); @@ -1022,16 +1023,25 @@ int skl_ipc_get_large_config(struct sst_generic_ipc *ipc, sz_remaining = msg->param_data_size; data_offset = 0; + inbox_sz = ipc->dsp->mailbox.in_size; + + if (msg->param_data_size >= inbox_sz) + header.extension |= IPC_FINAL_BLOCK(0); while (sz_remaining != 0) { - rx_size = sz_remaining > SKL_ADSP_W1_SZ - ? SKL_ADSP_W1_SZ : sz_remaining; + rx_size = sz_remaining > inbox_sz + ? inbox_sz : sz_remaining; if (rx_size == sz_remaining) header.extension |= IPC_FINAL_BLOCK(1); - ret = sst_ipc_tx_message_wait(ipc, *ipc_header, NULL, 0, - ((char *)param) + data_offset, - msg->param_data_size); + dev_dbg(ipc->dev, "In %s primary=%#x ext=%#x\n", __func__, + header.primary, header.extension); + dev_dbg(ipc->dev, "receiving offset: %#x, size: %#x\n", + (unsigned)data_offset, (unsigned)rx_size); + + ret = sst_ipc_tx_message_wait(ipc, *ipc_header, + ((char *)txparam), size, ((char *)param) + data_offset, + rx_size); if (ret < 0) { dev_err(ipc->dev, "ipc: get large config fail, err: %d\n", ret); @@ -1044,7 +1054,7 @@ int skl_ipc_get_large_config(struct sst_generic_ipc *ipc, header.extension &= IPC_INITIAL_BLOCK_CLEAR; header.extension &= IPC_DATA_OFFSET_SZ_CLEAR; /* fill the fields */ - header.extension |= IPC_INITIAL_BLOCK(1); + header.extension |= IPC_INITIAL_BLOCK(0); header.extension |= IPC_DATA_OFFSET_SZ(data_offset); } diff --git a/sound/soc/intel/skylake/skl-sst-ipc.h b/sound/soc/intel/skylake/skl-sst-ipc.h index 0437e4cf1261..e969ac964daa 100644 --- a/sound/soc/intel/skylake/skl-sst-ipc.h +++ b/sound/soc/intel/skylake/skl-sst-ipc.h @@ -244,7 +244,8 @@ int skl_ipc_set_large_config(struct sst_generic_ipc *ipc, struct skl_ipc_large_config_msg *msg, u32 *param); int skl_ipc_get_large_config(struct sst_generic_ipc *ipc, - struct skl_ipc_large_config_msg *msg, u32 *param); + struct skl_ipc_large_config_msg *msg, u32 *param, + u32 *txparam, u32 size); int skl_sst_ipc_load_library(struct sst_generic_ipc *ipc, u8 dma_id, u8 table_id, bool wait); From c483aa593777406d251a582da9af0dc03af86ed3 Mon Sep 17 00:00:00 2001 From: Mousumi Jana Date: Wed, 25 Nov 2015 22:52:40 +0530 Subject: [PATCH 0693/1103] ASoC: Intel: Skylake: generic IPC message support Debugfs provides an interface to send generic ipc message. This feature can be used to send any IPC command by passing it as a binary blob to the interface. Change-Id: Ic712c303a8e4559e2628e7507f16828913860a26 Signed-off-by: Mousumi Jana Reviewed-on: Reviewed-by: Babu, Ramesh Tested-by: Babu, Ramesh --- sound/soc/intel/skylake/skl-debug.c | 157 ++++++++++++++++++++++++- sound/soc/intel/skylake/skl-topology.h | 8 ++ sound/soc/intel/skylake/skl.h | 1 + 3 files changed, 165 insertions(+), 1 deletion(-) diff --git a/sound/soc/intel/skylake/skl-debug.c b/sound/soc/intel/skylake/skl-debug.c index f9db7ea363ef..23a5251e383b 100644 --- a/sound/soc/intel/skylake/skl-debug.c +++ b/sound/soc/intel/skylake/skl-debug.c @@ -24,10 +24,14 @@ #include "../common/sst-dsp-priv.h" #include "skl-nhlt.h" -#define MOD_BUF PAGE_SIZE +#define MOD_BUF (2 * PAGE_SIZE) #define FW_REG_BUF PAGE_SIZE #define FW_REG_SIZE 0x60 #define MAX_SSP 4 +#define MAX_SZ 1025 +#define IPC_MOD_LARGE_CONFIG_GET 3 +#define IPC_MOD_LARGE_CONFIG_SET 4 +#define MOD_BUF1 (3 * PAGE_SIZE) struct nhlt_blob { size_t size; @@ -44,6 +48,7 @@ struct skl_debug { u8 fw_read_buff[FW_REG_BUF]; struct nhlt_blob ssp_blob[2*MAX_SSP]; struct nhlt_blob dmic_blob; + u32 ipc_data[MAX_SZ]; }; struct nhlt_specific_cfg @@ -126,6 +131,155 @@ static const struct file_operations nhlt_fops = { .llseek = default_llseek, }; +static ssize_t mod_control_read(struct file *file, + char __user *user_buf, size_t count, loff_t *ppos) +{ + struct skl_debug *d = file->private_data; + char *state; + char *buf1; + int ret; + unsigned int ofs = 0; + + if (d->ipc_data[0] == 0) { + state = d->skl->mod_set_get_status ? "Fail\n" : "success\n"; + return simple_read_from_buffer(user_buf, count, ppos, + state, strlen(state)); + } + + state = d->skl->mod_set_get_status ? "Fail\n" : "success\n"; + buf1 = kzalloc(MOD_BUF1, GFP_KERNEL); + if (!buf1) + return -ENOMEM; + + ret = snprintf(buf1, MOD_BUF1, + "%s\nLARGE PARAM DATA\n", state); + + for (ofs = 0 ; ofs < d->ipc_data[0] ; ofs += 16) { + ret += snprintf(buf1 + ret, MOD_BUF1 - ret, "0x%.4x : ", ofs); + hex_dump_to_buffer(&(d->ipc_data[1]) + ofs, 16, 16, 4, + buf1 + ret, MOD_BUF1 - ret, 0); + ret += strlen(buf1 + ret); + if (MOD_BUF1 - ret > 0) + buf1[ret++] = '\n'; + } + + ret = simple_read_from_buffer(user_buf, count, ppos, buf1, ret); + kfree(buf1); + return ret; + +} + +static ssize_t mod_control_write(struct file *file, + const char __user *user_buf, size_t count, loff_t *ppos) +{ + struct skl_debug *d = file->private_data; + struct mod_set_get *mod_set_get; + char *buf; + int retval, type; + ssize_t written; + u32 size, mbsz; + u32 *large_data; + int large_param_size; + + struct skl_sst *ctx = d->skl->skl_sst; + struct skl_ipc_large_config_msg msg; + struct skl_ipc_header header = {0}; + u64 *ipc_header = (u64 *)(&header); + + buf = kzalloc(MOD_BUF, GFP_KERNEL); + written = simple_write_to_buffer(buf, MOD_BUF, ppos, + user_buf, count); + size = written; + print_hex_dump(KERN_DEBUG, "buf :", DUMP_PREFIX_OFFSET, 8, 4, + buf, size, false); + + mod_set_get = (struct mod_set_get *)buf; + header.primary = mod_set_get->primary; + header.extension = mod_set_get->extension; + + mbsz = mod_set_get->size - (sizeof(u32)*2); + print_hex_dump(KERN_DEBUG, "header mailbox:", DUMP_PREFIX_OFFSET, 8, 4, + mod_set_get->mailbx, size-12, false); + type = ((0x1f000000) & (mod_set_get->primary))>>24; + + switch (type) { + + case IPC_MOD_LARGE_CONFIG_GET: + msg.module_id = (header.primary) & 0x0000ffff; + msg.instance_id = ((header.primary) & 0x00ff0000)>>16; + msg.large_param_id = ((header.extension) & 0x0ff00000)>>20; + msg.param_data_size = (header.extension) & 0x000fffff; + large_param_size = msg.param_data_size; + + large_data = kzalloc(large_param_size, GFP_KERNEL); + if (!large_data) + return -ENOMEM; + + if (mbsz) + retval = skl_ipc_get_large_config(&ctx->ipc, &msg, + large_data, &(mod_set_get->mailbx[0]), mbsz); + else + retval = skl_ipc_get_large_config(&ctx->ipc, + &msg, large_data, NULL, 0); + + d->ipc_data[0] = msg.param_data_size; + memcpy(&d->ipc_data[1], large_data, msg.param_data_size); + kfree(large_data); + break; + + case IPC_MOD_LARGE_CONFIG_SET: + d->ipc_data[0] = 0; + msg.module_id = (header.primary) & 0x0000ffff; + msg.instance_id = ((header.primary) & 0x00ff0000)>>16; + msg.large_param_id = ((header.extension) & 0x0ff00000)>>20; + msg.param_data_size = (header.extension) & 0x000fffff; + + retval = skl_ipc_set_large_config(&ctx->ipc, &msg, + (u32 *)(&mod_set_get->mailbx)); + d->ipc_data[0] = 0; + break; + + default: + if (mbsz) + retval = sst_ipc_tx_message_wait(&ctx->ipc, *ipc_header, + mod_set_get->mailbx, mbsz, NULL, 0); + + else + retval = sst_ipc_tx_message_wait(&ctx->ipc, *ipc_header, + NULL, 0, NULL, 0); + + d->ipc_data[0] = 0; + break; + + } + if (retval) + d->skl->mod_set_get_status = 1; + else + d->skl->mod_set_get_status = 0; + + /* Userspace has been fiddling around behind the kernel's back */ + add_taint(TAINT_USER, LOCKDEP_NOW_UNRELIABLE); + kfree(buf); + return written; +} + +static const struct file_operations set_get_ctrl_fops = { + .open = simple_open, + .read = mod_control_read, + .write = mod_control_write, + .llseek = default_llseek, +}; + +static int skl_init_mod_set_get(struct skl_debug *d) +{ + if (!debugfs_create_file("set_get_ctrl", 0644, d->modules, d, + &set_get_ctrl_fops)) { + dev_err(d->dev, "module set get ctrl debugfs init failed\n"); + return -EIO; + } + return 0; +} + static ssize_t skl_print_pins(struct skl_module_pin *m_pin, char *buf, int max_pin, ssize_t size, bool direction) { @@ -437,6 +591,7 @@ struct skl_debug *skl_debugfs_init(struct skl *skl) } skl_init_nhlt(d); + skl_init_mod_set_get(d); return d; diff --git a/sound/soc/intel/skylake/skl-topology.h b/sound/soc/intel/skylake/skl-topology.h index 94152704e22e..c6158d37f557 100644 --- a/sound/soc/intel/skylake/skl-topology.h +++ b/sound/soc/intel/skylake/skl-topology.h @@ -506,6 +506,14 @@ static inline struct skl *get_skl_ctx(struct device *dev) } struct skl_probe_config; + +struct mod_set_get { + u32 size; + u32 primary; + u32 extension; + u32 mailbx[1024]; +}; + int skl_tplg_be_update_params(struct snd_soc_dai *dai, struct skl_pipe_params *params); int skl_dsp_set_dma_control(struct skl_sst *ctx, u32 *caps, diff --git a/sound/soc/intel/skylake/skl.h b/sound/soc/intel/skylake/skl.h index 4056ea3de714..4c7cb272a029 100644 --- a/sound/soc/intel/skylake/skl.h +++ b/sound/soc/intel/skylake/skl.h @@ -104,6 +104,7 @@ struct skl { struct skl_fw_config cfg; struct snd_soc_acpi_mach *mach; bool nhlt_override; + bool mod_set_get_status; }; #define skl_to_bus(s) (&(s)->hbus) From c61d21fa09abaa5f0bb1085c3907a180b1bcdc15 Mon Sep 17 00:00:00 2001 From: Mousumi Jana Date: Thu, 26 Nov 2015 01:28:34 +0530 Subject: [PATCH 0694/1103] ASoC: Intel: Skylake:Add support to get fw configuration Debugfs gives the support to get the fw configuration depending on base fw property. Change-Id: Ib9bd67928939fdfc9443d9641a71a506e1ac22cb Signed-off-by: Mousumi Jana Reviewed-on: Reviewed-by: Babu, Ramesh Tested-by: Babu, Ramesh --- sound/soc/intel/skylake/skl-debug.c | 206 +++++++++++++++++++++++++ sound/soc/intel/skylake/skl-sst-dsp.h | 2 + sound/soc/intel/skylake/skl-topology.h | 26 ++++ 3 files changed, 234 insertions(+) diff --git a/sound/soc/intel/skylake/skl-debug.c b/sound/soc/intel/skylake/skl-debug.c index 23a5251e383b..ff6d8356cb4f 100644 --- a/sound/soc/intel/skylake/skl-debug.c +++ b/sound/soc/intel/skylake/skl-debug.c @@ -33,6 +33,19 @@ #define IPC_MOD_LARGE_CONFIG_SET 4 #define MOD_BUF1 (3 * PAGE_SIZE) +#define DEFAULT_SZ 100 +#define DEFAULT_ID 0XFF +#define ADSP_PROPERTIES_SZ 0x64 +#define ADSP_RESOURCE_STATE_SZ 0x18 +#define FIRMWARE_CONFIG_SZ 0x14c +#define HARDWARE_CONFIG_SZ 0x84 +#define MODULES_INFO_SZ 0xa70 +#define PIPELINE_LIST_INFO_SZ 0xc +#define SCHEDULERS_INFO_SZ 0x34 +#define GATEWAYS_INFO_SZ 0x4e4 +#define MEMORY_STATE_INFO_SZ 0x1000 +#define POWER_STATE_INFO_SZ 0x1000 + struct nhlt_blob { size_t size; struct nhlt_specific_cfg *cfg; @@ -49,6 +62,7 @@ struct skl_debug { struct nhlt_blob ssp_blob[2*MAX_SSP]; struct nhlt_blob dmic_blob; u32 ipc_data[MAX_SZ]; + struct fw_ipc_data fw_ipc_data; }; struct nhlt_specific_cfg @@ -551,6 +565,196 @@ static int skl_init_nhlt(struct skl_debug *d) return 0; } +static ssize_t adsp_control_read(struct file *file, + char __user *user_buf, size_t count, loff_t *ppos) +{ + + struct skl_debug *d = file->private_data; + char *buf1; + ssize_t ret; + unsigned int data, ofs = 0; + int replysz = 0; + + mutex_lock(&d->fw_ipc_data.mutex); + replysz = d->fw_ipc_data.replysz; + data = d->fw_ipc_data.adsp_id; + + buf1 = kzalloc(MOD_BUF1, GFP_ATOMIC); + if (!buf1) { + mutex_unlock(&d->fw_ipc_data.mutex); + return -ENOMEM; + } + + ret = snprintf(buf1, MOD_BUF1, + "\nADSP_PROP ID %x\n", data); + for (ofs = 0 ; ofs < replysz ; ofs += 16) { + ret += snprintf(buf1 + ret, MOD_BUF1 - ret, + "0x%.4x : ", ofs); + hex_dump_to_buffer((u8 *)(&(d->fw_ipc_data.mailbx[0])) + ofs, + 16, 16, 4, + buf1 + ret, MOD_BUF1 - ret, 0); + ret += strlen(buf1 + ret); + if (MOD_BUF1 - ret > 0) + buf1[ret++] = '\n'; + } + + ret = simple_read_from_buffer(user_buf, count, ppos, buf1, ret); + mutex_unlock(&d->fw_ipc_data.mutex); + kfree(buf1); + + return ret; +} + +static ssize_t adsp_control_write(struct file *file, + const char __user *user_buf, size_t count, loff_t *ppos) +{ + struct skl_debug *d = file->private_data; + char buf[8]; + int err, replysz; + unsigned int dsp_property; + u32 *ipc_data; + struct skl_sst *ctx = d->skl->skl_sst; + struct skl_ipc_large_config_msg msg; + char id[8]; + u32 tx_data; + int j = 0, bufsize, tx_param = 0, tx_param_id; + int len = min(count, (sizeof(buf)-1)); + + mutex_lock(&d->fw_ipc_data.mutex); + if (copy_from_user(buf, user_buf, len)) { + mutex_unlock(&d->fw_ipc_data.mutex); + return -EFAULT; + } + + buf[len] = '\0'; + bufsize = strlen(buf); + + while (buf[j] != '\0') { + if (buf[j] == ',') { + strncpy(id, &buf[j+1], (bufsize-j)); + buf[j] = '\0'; + tx_param = 1; + } else + j++; + } + + err = kstrtouint(buf, 10, &dsp_property); + + if ((dsp_property == DMA_CONTROL) || (dsp_property == ENABLE_LOGS)) { + dev_err(d->dev, "invalid input !! not readable\n"); + mutex_unlock(&d->fw_ipc_data.mutex); + return -EINVAL; + } + + if (tx_param == 1) { + err = kstrtouint(id, 10, &tx_param_id); + tx_data = (tx_param_id << 8) | dsp_property; + } + + ipc_data = kzalloc(DSP_BUF, GFP_ATOMIC); + if (!ipc_data) { + mutex_unlock(&d->fw_ipc_data.mutex); + return -ENOMEM; + } + + switch (dsp_property) { + + case ADSP_PROPERTIES: + replysz = ADSP_PROPERTIES_SZ; + break; + + case ADSP_RESOURCE_STATE: + replysz = ADSP_RESOURCE_STATE_SZ; + break; + + case FIRMWARE_CONFIG: + replysz = FIRMWARE_CONFIG_SZ; + break; + + case HARDWARE_CONFIG: + replysz = HARDWARE_CONFIG_SZ; + break; + + case MODULES_INFO: + replysz = MODULES_INFO_SZ; + break; + + case PIPELINE_LIST_INFO: + replysz = PIPELINE_LIST_INFO_SZ; + break; + + case SCHEDULERS_INFO: + replysz = SCHEDULERS_INFO_SZ; + break; + + case GATEWAYS_INFO: + replysz = GATEWAYS_INFO_SZ; + break; + + case MEMORY_STATE_INFO: + replysz = MEMORY_STATE_INFO_SZ; + break; + + case POWER_STATE_INFO: + replysz = POWER_STATE_INFO_SZ; + break; + + default: + mutex_unlock(&d->fw_ipc_data.mutex); + kfree(ipc_data); + return -EINVAL; + } + + msg.module_id = 0x0; + msg.instance_id = 0x0; + msg.large_param_id = dsp_property; + msg.param_data_size = replysz; + + if (tx_param == 1) + skl_ipc_get_large_config(&ctx->ipc, &msg, + ipc_data, &tx_data, sizeof(u32)); + else + skl_ipc_get_large_config(&ctx->ipc, &msg, + ipc_data, NULL, 0); + + memset(&d->fw_ipc_data.mailbx[0], 0, DSP_BUF); + + memcpy(&d->fw_ipc_data.mailbx[0], ipc_data, replysz); + + d->fw_ipc_data.adsp_id = dsp_property; + + d->fw_ipc_data.replysz = replysz; + + /* Userspace has been fiddling around behindthe kernel's back*/ + add_taint(TAINT_USER, LOCKDEP_NOW_UNRELIABLE); + mutex_unlock(&d->fw_ipc_data.mutex); + kfree(ipc_data); + + return len; +} + +static const struct file_operations ssp_cntrl_adsp_fops = { + .open = simple_open, + .read = adsp_control_read, + .write = adsp_control_write, + .llseek = default_llseek, +}; + +static int skl_init_adsp(struct skl_debug *d) +{ + if (!debugfs_create_file("adsp_prop_ctrl", 0644, d->fs, d, + &ssp_cntrl_adsp_fops)) { + dev_err(d->dev, "adsp control debugfs init failed\n"); + return -EIO; + } + + memset(&d->fw_ipc_data.mailbx[0], 0, DSP_BUF); + d->fw_ipc_data.replysz = DEFAULT_SZ; + d->fw_ipc_data.adsp_id = DEFAULT_ID; + + return 0; +} + struct skl_debug *skl_debugfs_init(struct skl *skl) { struct skl_debug *d; @@ -559,6 +763,7 @@ struct skl_debug *skl_debugfs_init(struct skl *skl) if (!d) return NULL; + mutex_init(&d->fw_ipc_data.mutex); /* create the debugfs dir with platform component's debugfs as parent */ d->fs = debugfs_create_dir("dsp", skl->component->debugfs_root); @@ -591,6 +796,7 @@ struct skl_debug *skl_debugfs_init(struct skl *skl) } skl_init_nhlt(d); + skl_init_adsp(d); skl_init_mod_set_get(d); return d; diff --git a/sound/soc/intel/skylake/skl-sst-dsp.h b/sound/soc/intel/skylake/skl-sst-dsp.h index 33de8c939bc4..767c6edddc3a 100644 --- a/sound/soc/intel/skylake/skl-sst-dsp.h +++ b/sound/soc/intel/skylake/skl-sst-dsp.h @@ -127,6 +127,8 @@ struct skl_lib_info; #define SKL_ADSPCS_CPA_SHIFT 24 #define SKL_ADSPCS_CPA_MASK(cm) ((cm) << SKL_ADSPCS_CPA_SHIFT) +#define DSP_BUF PAGE_SIZE + /* DSP Core state */ enum skl_dsp_states { SKL_DSP_RUNNING = 1, diff --git a/sound/soc/intel/skylake/skl-topology.h b/sound/soc/intel/skylake/skl-topology.h index c6158d37f557..b8a1d8b90fe1 100644 --- a/sound/soc/intel/skylake/skl-topology.h +++ b/sound/soc/intel/skylake/skl-topology.h @@ -32,6 +32,7 @@ #define MAX_TS_GROUPS 8 #define MAX_DMIC_TS_GROUPS 4 #define MAX_FIXED_DMIC_PARAMS_SIZE 727 +#define MAX_ADSP_SZ 1024 /* Maximum number of coefficients up down mixer module */ #define UP_DOWN_MIXER_MAX_COEFF 8 @@ -514,6 +515,31 @@ struct mod_set_get { u32 mailbx[1024]; }; +enum base_fw_run_time_param { + ADSP_PROPERTIES = 0, + ADSP_RESOURCE_STATE = 1, + NOTIFICATION_MASK = 3, + ASTATE_TABLE = 4, + DMA_CONTROL = 5, + ENABLE_LOGS = 6, + FIRMWARE_CONFIG = 7, + HARDWARE_CONFIG = 8, + MODULES_INFO = 9, + PIPELINE_LIST_INFO = 10, + PIPELINE_PROPS = 11, + SCHEDULERS_INFO = 12, + GATEWAYS_INFO = 13, + MEMORY_STATE_INFO = 14, + POWER_STATE_INFO = 15 +}; + +struct fw_ipc_data { + u32 replysz; + u32 adsp_id; + u32 mailbx[MAX_ADSP_SZ]; + struct mutex mutex; +}; + int skl_tplg_be_update_params(struct snd_soc_dai *dai, struct skl_pipe_params *params); int skl_dsp_set_dma_control(struct skl_sst *ctx, u32 *caps, From 57fc78d5205d437e2fb0ddd3fd3b3a198380a83d Mon Sep 17 00:00:00 2001 From: Guneshwor Singh Date: Mon, 24 Oct 2016 16:23:20 +0530 Subject: [PATCH 0695/1103] [WORKAROUND] Register masters only if RT700 is selected in config This info should come from BIOS settings. Right now it is not available, so using WA to avoid registering sdw masters if used in I2S mode. Change-Id: Id5cad0a8e53507481b65a56e942b4eedc8f0a108 Signed-off-by: Guneshwor Singh --- sound/soc/intel/skylake/cnl-sst.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/sound/soc/intel/skylake/cnl-sst.c b/sound/soc/intel/skylake/cnl-sst.c index a124dabc353a..a81e1b032a96 100644 --- a/sound/soc/intel/skylake/cnl-sst.c +++ b/sound/soc/intel/skylake/cnl-sst.c @@ -509,6 +509,7 @@ static int cnl_ipc_init(struct device *dev, struct skl_sst *cnl) return 0; } +#if IS_ENABLED(CONFIG_SND_SOC_RT700) static int skl_register_sdw_masters(struct device *dev, struct skl_sst *dsp, void __iomem *mmio_base, int irq) { @@ -655,6 +656,7 @@ static int skl_register_sdw_masters(struct device *dev, struct skl_sst *dsp, cnl_sdw_int_enable(dsp->dsp, 1); return 0; } +#endif static void skl_unregister_sdw_masters(struct skl_sst *ctx) { @@ -723,11 +725,13 @@ int cnl_sst_dsp_init(struct device *dev, void __iomem *mmio_base, int irq, return ret; } +#if IS_ENABLED(CONFIG_SND_SOC_RT700) ret = skl_register_sdw_masters(dev, cnl, mmio_base, irq); if (ret) { dev_err(cnl->dev, "%s SoundWire masters registration failed\n", __func__); return ret; } +#endif return 0; } From bee91127943e84f4479aa9689bdb323ea273ae7f Mon Sep 17 00:00:00 2001 From: Shreyas NC Date: Tue, 25 Oct 2016 13:03:36 +0530 Subject: [PATCH 0696/1103] [WORKAROUND] Add #if for platform/codec name in cnl_rt700 machine This is done to ensure same machine driver works for both FPGA as well as RVP. Change-Id: I969ea974cdc02a802a576e23746cfdc4f4d9a7d5 --- sound/soc/intel/boards/cnl_rt700.c | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/sound/soc/intel/boards/cnl_rt700.c b/sound/soc/intel/boards/cnl_rt700.c index e0b93e571fb9..d49c3f9de10e 100644 --- a/sound/soc/intel/boards/cnl_rt700.c +++ b/sound/soc/intel/boards/cnl_rt700.c @@ -155,6 +155,14 @@ static int cnl_dmic_fixup(struct snd_soc_pcm_runtime *rtd, return 0; } +#if IS_ENABLED(CONFIG_SND_SOC_INTEL_CNL_FPGA) +static const char pname[] = "0000:02:18.0"; +static const char cname[] = "sdw-slave0-10:02:5d:07:01:00"; +#else +static const char pname[] = "0000:00:1f.3"; +static const char cname[] = "sdw-slave1-10:02:5d:07:00:01"; +#endif + static struct snd_soc_dai_link cnl_rt700_msic_dailink[] = { { .name = "Bxtn Audio Port", @@ -162,7 +170,7 @@ static struct snd_soc_dai_link cnl_rt700_msic_dailink[] = { .cpu_dai_name = "System Pin", .codec_name = "snd-soc-dummy", .codec_dai_name = "snd-soc-dummy-dai", - .platform_name = "0000:02:18.0", + .platform_name = pname, .init = cnl_rt700_init, .ignore_suspend = 1, .nonatomic = 1, @@ -177,7 +185,7 @@ static struct snd_soc_dai_link cnl_rt700_msic_dailink[] = { .cpu_dai_name = "Reference Pin", .codec_name = "snd-soc-dummy", .codec_dai_name = "snd-soc-dummy-dai", - .platform_name = "0000:02:18.0", + .platform_name = pname, .ignore_suspend = 1, .nonatomic = 1, .dynamic = 1, @@ -190,7 +198,7 @@ static struct snd_soc_dai_link cnl_rt700_msic_dailink[] = { .cpu_dai_name = "Deepbuffer Pin", .codec_name = "snd-soc-dummy", .codec_dai_name = "snd-soc-dummy-dai", - .platform_name = "0000:02:18.0", + .platform_name = pname, .dpcm_playback = 1, .ignore_suspend = 1, .nonatomic = 1, @@ -201,8 +209,8 @@ static struct snd_soc_dai_link cnl_rt700_msic_dailink[] = { { .name = "SDW0-Codec", .cpu_dai_name = "SDW Pin", - .platform_name = "0000:02:18.0", - .codec_name = "sdw-slave0-10:02:5d:07:01:00", + .platform_name = pname, + .codec_name = cname, .codec_dai_name = "rt700-aif1", .be_hw_params_fixup = cnl_rt700_codec_fixup, .ignore_suspend = 1, @@ -213,8 +221,8 @@ static struct snd_soc_dai_link cnl_rt700_msic_dailink[] = { { .name = "SDW1-Codec", .cpu_dai_name = "SDW10 Pin", - .platform_name = "0000:02:18.0", - .codec_name = "sdw-slave0-10:02:5d:07:01:00", + .platform_name = pname, + .codec_name = cname, .codec_dai_name = "rt700-aif2", .be_hw_params_fixup = cnl_rt700_codec_fixup, .ignore_suspend = 1, @@ -227,7 +235,7 @@ static struct snd_soc_dai_link cnl_rt700_msic_dailink[] = { .cpu_dai_name = "DMIC01 Pin", .codec_name = "dmic-codec", .codec_dai_name = "dmic-hifi", - .platform_name = "0000:02:18.0", + .platform_name = pname, .ignore_suspend = 1, .no_pcm = 1, .dpcm_capture = 1, From 9ad40e024a41895a16bc51f02344788c9fb9fc17 Mon Sep 17 00:00:00 2001 From: Shreyas NC Date: Tue, 25 Oct 2016 13:04:39 +0530 Subject: [PATCH 0697/1103] [REVERTME] ASoC: Intel: CNL: Change BE id to SDW MSTR1 Change-Id: I88e650630da9f65b222838190d14899ac28627dc Signed-off-by: Shreyas NC --- sound/soc/intel/skylake/skl-pcm.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/sound/soc/intel/skylake/skl-pcm.c b/sound/soc/intel/skylake/skl-pcm.c index 09899a05975f..ac1eef7d2b41 100644 --- a/sound/soc/intel/skylake/skl-pcm.c +++ b/sound/soc/intel/skylake/skl-pcm.c @@ -1325,7 +1325,11 @@ static struct snd_soc_dai_driver skl_platform_dai[] = { * should be coming from CLT based on endpoints to be supported */ .name = "SDW Pin", +#if IS_ENABLED(CONFIG_SND_SOC_INTEL_CNL_FPGA) .id = SDW_BE_DAI_ID_MSTR0, +#else + .id = SDW_BE_DAI_ID_MSTR1, +#endif .ops = &skl_sdw_dai_ops, .playback = { .stream_name = "SDW Tx", @@ -1347,7 +1351,11 @@ static struct snd_soc_dai_driver skl_platform_dai[] = { * should be coming from CLT based on endpoints to be supported */ .name = "SDW10 Pin", +#if IS_ENABLED(CONFIG_SND_SOC_INTEL_CNL_FPGA) .id = SDW_BE_DAI_ID_MSTR0, +#else + .id = SDW_BE_DAI_ID_MSTR1, +#endif .ops = &skl_sdw_dai_ops, .playback = { .stream_name = "SDW Tx10", From 7e98c6b150d2a9ea1c4d8d8bb82bdca7d91e5971 Mon Sep 17 00:00:00 2001 From: Sanyog Kale Date: Sun, 2 Jul 2017 11:30:20 +0530 Subject: [PATCH 0698/1103] ASoC: Intel: CNL: Update dsp ops API to take direction as input parameter This patch removes hardcoding of audio stream direction inside dsp ops. Instead it is passed as input parameter of dsp ops. Signed-off-by: Sanyog Kale --- sound/soc/intel/skylake/bxt-sst.c | 28 +++++++++++++++++--------- sound/soc/intel/skylake/cnl-sst.c | 15 +++++++++----- sound/soc/intel/skylake/skl-messages.c | 20 +++++++++--------- sound/soc/intel/skylake/skl-sst-dsp.h | 9 ++++----- 4 files changed, 44 insertions(+), 28 deletions(-) diff --git a/sound/soc/intel/skylake/bxt-sst.c b/sound/soc/intel/skylake/bxt-sst.c index 924c0dfa6ad7..2b169b40e6c4 100644 --- a/sound/soc/intel/skylake/bxt-sst.c +++ b/sound/soc/intel/skylake/bxt-sst.c @@ -69,7 +69,8 @@ bxt_load_library(struct sst_dsp *ctx, struct skl_lib_info *linfo, int lib_count) goto load_library_failed; stream_tag = ctx->dsp_ops.prepare(ctx->dev, 0x40, - stripped_fw.size, &dmab); + stripped_fw.size, &dmab, + SNDRV_PCM_STREAM_PLAYBACK); if (stream_tag <= 0) { dev_err(ctx->dev, "Lib prepare DMA err: %x\n", stream_tag); @@ -80,14 +81,17 @@ bxt_load_library(struct sst_dsp *ctx, struct skl_lib_info *linfo, int lib_count) dma_id = stream_tag - 1; memcpy(dmab.area, stripped_fw.data, stripped_fw.size); - ctx->dsp_ops.trigger(ctx->dev, true, stream_tag); + ctx->dsp_ops.trigger(ctx->dev, true, stream_tag, + SNDRV_PCM_STREAM_PLAYBACK); ret = skl_sst_ipc_load_library(&skl->ipc, dma_id, i, true); if (ret < 0) dev_err(ctx->dev, "IPC Load Lib for %s fail: %d\n", linfo[i].name, ret); - ctx->dsp_ops.trigger(ctx->dev, false, stream_tag); - ctx->dsp_ops.cleanup(ctx->dev, &dmab, stream_tag); + ctx->dsp_ops.trigger(ctx->dev, false, stream_tag, + SNDRV_PCM_STREAM_PLAYBACK); + ctx->dsp_ops.cleanup(ctx->dev, &dmab, stream_tag, + SNDRV_PCM_STREAM_PLAYBACK); } return ret; @@ -107,7 +111,8 @@ static int sst_bxt_prepare_fw(struct sst_dsp *ctx, { int stream_tag, ret; - stream_tag = ctx->dsp_ops.prepare(ctx->dev, 0x40, fwsize, &ctx->dmab); + stream_tag = ctx->dsp_ops.prepare(ctx->dev, 0x40, fwsize, &ctx->dmab, + SNDRV_PCM_STREAM_PLAYBACK); if (stream_tag <= 0) { dev_err(ctx->dev, "Failed to prepare DMA FW loading err: %x\n", stream_tag); @@ -169,7 +174,9 @@ static int sst_bxt_prepare_fw(struct sst_dsp *ctx, return ret; base_fw_load_failed: - ctx->dsp_ops.cleanup(ctx->dev, &ctx->dmab, stream_tag); + ctx->dsp_ops.cleanup(ctx->dev, &ctx->dmab, stream_tag, + SNDRV_PCM_STREAM_PLAYBACK); + skl_dsp_core_power_down(ctx, SKL_DSP_CORE_MASK(1)); skl_dsp_disable_core(ctx, SKL_DSP_CORE0_MASK); return ret; @@ -179,12 +186,15 @@ static int sst_transfer_fw_host_dma(struct sst_dsp *ctx) { int ret; - ctx->dsp_ops.trigger(ctx->dev, true, ctx->dsp_ops.stream_tag); + ctx->dsp_ops.trigger(ctx->dev, true, ctx->dsp_ops.stream_tag, + SNDRV_PCM_STREAM_PLAYBACK); ret = sst_dsp_register_poll(ctx, BXT_ADSP_FW_STATUS, SKL_FW_STS_MASK, BXT_ROM_INIT, BXT_BASEFW_TIMEOUT, "Firmware boot"); - ctx->dsp_ops.trigger(ctx->dev, false, ctx->dsp_ops.stream_tag); - ctx->dsp_ops.cleanup(ctx->dev, &ctx->dmab, ctx->dsp_ops.stream_tag); + ctx->dsp_ops.trigger(ctx->dev, false, ctx->dsp_ops.stream_tag, + SNDRV_PCM_STREAM_PLAYBACK); + ctx->dsp_ops.cleanup(ctx->dev, &ctx->dmab, ctx->dsp_ops.stream_tag, + SNDRV_PCM_STREAM_PLAYBACK); return ret; } diff --git a/sound/soc/intel/skylake/cnl-sst.c b/sound/soc/intel/skylake/cnl-sst.c index a81e1b032a96..784d92df7ac3 100644 --- a/sound/soc/intel/skylake/cnl-sst.c +++ b/sound/soc/intel/skylake/cnl-sst.c @@ -127,7 +127,8 @@ static int cnl_prepare_fw(struct sst_dsp *ctx, const void *fwdata, u32 fwsize) if (ret < 0) return ret; - stream_tag = ctx->dsp_ops.prepare(ctx->dev, 0x40, fwsize, &ctx->dmab); + stream_tag = ctx->dsp_ops.prepare(ctx->dev, 0x40, fwsize, &ctx->dmab, + SNDRV_PCM_STREAM_PLAYBACK); if (stream_tag <= 0) { dev_err(ctx->dev, "dma prepare failed: 0%#x\n", stream_tag); return stream_tag; @@ -178,7 +179,8 @@ static int cnl_prepare_fw(struct sst_dsp *ctx, const void *fwdata, u32 fwsize) return 0; base_fw_load_failed: - ctx->dsp_ops.cleanup(ctx->dev, &ctx->dmab, stream_tag); + ctx->dsp_ops.cleanup(ctx->dev, &ctx->dmab, stream_tag, + SNDRV_PCM_STREAM_PLAYBACK); cnl_dsp_disable_core(ctx, SKL_DSP_CORE0_MASK); cnl_fpga_free_imr(ctx); @@ -189,13 +191,16 @@ static int sst_transfer_fw_host_dma(struct sst_dsp *ctx) { int ret; - ctx->dsp_ops.trigger(ctx->dev, true, ctx->dsp_ops.stream_tag); + ctx->dsp_ops.trigger(ctx->dev, true, ctx->dsp_ops.stream_tag, + SNDRV_PCM_STREAM_PLAYBACK); ret = sst_dsp_register_poll(ctx, CNL_ADSP_FW_STATUS, CNL_FW_STS_MASK, CNL_FW_INIT, CNL_BASEFW_TIMEOUT, "firmware boot"); - ctx->dsp_ops.trigger(ctx->dev, false, ctx->dsp_ops.stream_tag); - ctx->dsp_ops.cleanup(ctx->dev, &ctx->dmab, ctx->dsp_ops.stream_tag); + ctx->dsp_ops.trigger(ctx->dev, false, ctx->dsp_ops.stream_tag, + SNDRV_PCM_STREAM_PLAYBACK); + ctx->dsp_ops.cleanup(ctx->dev, &ctx->dmab, ctx->dsp_ops.stream_tag, + SNDRV_PCM_STREAM_PLAYBACK); return ret; } diff --git a/sound/soc/intel/skylake/skl-messages.c b/sound/soc/intel/skylake/skl-messages.c index 13d1898da7fb..6fcd68c7bd1e 100644 --- a/sound/soc/intel/skylake/skl-messages.c +++ b/sound/soc/intel/skylake/skl-messages.c @@ -127,7 +127,9 @@ static int skl_dsp_setup_spib(struct device *dev, unsigned int size, } static int skl_dsp_prepare(struct device *dev, unsigned int format, - unsigned int size, struct snd_dma_buffer *dmab) + unsigned int size, + struct snd_dma_buffer *dmab, + int direction) { struct hdac_bus *bus = dev_get_drvdata(dev); struct hdac_ext_stream *estream; @@ -139,7 +141,8 @@ static int skl_dsp_prepare(struct device *dev, unsigned int format, return -ENODEV; memset(&substream, 0, sizeof(substream)); - substream.stream = SNDRV_PCM_STREAM_PLAYBACK; + + substream.stream = direction; estream = snd_hdac_ext_stream_assign(bus, &substream, HDAC_EXT_STREAM_TYPE_HOST); @@ -158,7 +161,8 @@ static int skl_dsp_prepare(struct device *dev, unsigned int format, return stream->stream_tag; } -static int skl_dsp_trigger(struct device *dev, bool start, int stream_tag) +static int skl_dsp_trigger(struct device *dev, bool start, int stream_tag, + int direction) { struct hdac_bus *bus = dev_get_drvdata(dev); struct hdac_stream *stream; @@ -166,8 +170,7 @@ static int skl_dsp_trigger(struct device *dev, bool start, int stream_tag) if (!bus) return -ENODEV; - stream = snd_hdac_get_stream(bus, - SNDRV_PCM_STREAM_PLAYBACK, stream_tag); + stream = snd_hdac_get_stream(bus, direction, stream_tag); if (!stream) return -EINVAL; @@ -176,8 +179,8 @@ static int skl_dsp_trigger(struct device *dev, bool start, int stream_tag) return 0; } -static int skl_dsp_cleanup(struct device *dev, - struct snd_dma_buffer *dmab, int stream_tag) +static int skl_dsp_cleanup(struct device *dev, struct snd_dma_buffer *dmab, + int stream_tag, int direction) { struct hdac_bus *bus = dev_get_drvdata(dev); struct hdac_stream *stream; @@ -186,8 +189,7 @@ static int skl_dsp_cleanup(struct device *dev, if (!bus) return -ENODEV; - stream = snd_hdac_get_stream(bus, - SNDRV_PCM_STREAM_PLAYBACK, stream_tag); + stream = snd_hdac_get_stream(bus, direction, stream_tag); if (!stream) return -EINVAL; diff --git a/sound/soc/intel/skylake/skl-sst-dsp.h b/sound/soc/intel/skylake/skl-sst-dsp.h index 767c6edddc3a..2a3ad2d43448 100644 --- a/sound/soc/intel/skylake/skl-sst-dsp.h +++ b/sound/soc/intel/skylake/skl-sst-dsp.h @@ -157,7 +157,6 @@ struct skl_dsp_fw_ops { unsigned int (*get_fw_errcode)(struct sst_dsp *ctx); int (*load_mod)(struct sst_dsp *ctx, u16 mod_id, u8 *mod_name); int (*unload_mod)(struct sst_dsp *ctx, u16 mod_id); - }; struct skl_dsp_loader_ops { @@ -169,11 +168,11 @@ struct skl_dsp_loader_ops { struct snd_dma_buffer *dmab); int (*prepare)(struct device *dev, unsigned int format, unsigned int byte_size, - struct snd_dma_buffer *bufp); - int (*trigger)(struct device *dev, bool start, int stream_tag); - + struct snd_dma_buffer *bufp, int direction); + int (*trigger)(struct device *dev, bool start, int stream_tag, + int direction); int (*cleanup)(struct device *dev, struct snd_dma_buffer *dmab, - int stream_tag); + int stream_tag, int direction); }; #define MAX_INSTANCE_BUFF 2 From db35cfee86207144277829a3169b80c3b4fdf524 Mon Sep 17 00:00:00 2001 From: Sanyog Kale Date: Sun, 2 Jul 2017 11:48:07 +0530 Subject: [PATCH 0699/1103] ASoC: Intel: CNL: Platform driver implementation for SoundWire BRA feature This patch adds APIs requied for Soundwire BRA feature. Signed-off-by: Sanyog Kale --- sound/soc/intel/skylake/skl-messages.c | 742 ++++++++++++++++++++++++- sound/soc/intel/skylake/skl-sst-ipc.h | 14 + 2 files changed, 754 insertions(+), 2 deletions(-) diff --git a/sound/soc/intel/skylake/skl-messages.c b/sound/soc/intel/skylake/skl-messages.c index 6fcd68c7bd1e..28d990450463 100644 --- a/sound/soc/intel/skylake/skl-messages.c +++ b/sound/soc/intel/skylake/skl-messages.c @@ -22,6 +22,7 @@ #include #include #include +#include #include "skl-sst-dsp.h" #include "cnl-sst-dsp.h" #include "skl-sst-ipc.h" @@ -29,6 +30,9 @@ #include "../common/sst-dsp.h" #include "../common/sst-dsp-priv.h" #include "skl-topology.h" +#include +#include +#include static int skl_alloc_dma_buf(struct device *dev, struct snd_dma_buffer *dmab, size_t size) @@ -272,6 +276,739 @@ static const struct skl_dsp_ops dsp_ops[] = { }, }; +static int cnl_sdw_bra_pipe_trigger(struct skl_sst *ctx, bool enable, + unsigned int mstr_num) +{ + struct bra_conf *bra_data = &ctx->bra_pipe_data[mstr_num]; + int ret; + + if (enable) { + + /* Run CP Pipeline */ + ret = skl_run_pipe(ctx, bra_data->cp_pipe); + if (ret < 0) { + dev_err(ctx->dev, "BRA: RX run pipeline failed: 0x%x\n", ret); + goto error; + } + + /* Run PB Pipeline */ + ret = skl_run_pipe(ctx, bra_data->pb_pipe); + if (ret < 0) { + dev_err(ctx->dev, "BRA: TX run pipeline failed: 0x%x\n", ret); + goto error; + } + + } else { + + /* Stop playback pipeline */ + ret = skl_stop_pipe(ctx, bra_data->pb_pipe); + if (ret < 0) { + dev_err(ctx->dev, "BRA: TX stop pipeline failed: 0x%x\n", ret); + goto error; + } + + /* Stop capture pipeline */ + ret = skl_stop_pipe(ctx, bra_data->cp_pipe); + if (ret < 0) { + dev_err(ctx->dev, "BRA: RX stop pipeline failed: 0x%x\n", ret); + goto error; + } + } + +error: + return ret; +} + +static int cnl_sdw_bra_pipe_cfg_pb(struct skl_sst *ctx, + unsigned int mstr_num) +{ + struct bra_conf *bra_data = &ctx->bra_pipe_data[mstr_num]; + struct skl_pipe *host_cpr_pipe = NULL; + struct skl_pipe_params host_cpr_params; + struct skl_module_cfg host_cpr_cfg, link_cpr_cfg; + int ret; + + /* Playback pipeline */ + host_cpr_pipe = kzalloc(sizeof(struct skl_pipe), GFP_KERNEL); + if (!host_cpr_pipe) { + ret = -ENOMEM; + goto error; + } + + bra_data->pb_pipe = host_cpr_pipe; + + host_cpr_pipe->p_params = &host_cpr_params; + host_cpr_cfg.pipe = host_cpr_pipe; + + host_cpr_pipe->ppl_id = 1; + host_cpr_pipe->pipe_priority = 0; + host_cpr_pipe->conn_type = 0; + host_cpr_pipe->memory_pages = 2; + + ret = skl_create_pipeline(ctx, host_cpr_cfg.pipe); + if (ret < 0) + goto error; + + host_cpr_params.host_dma_id = (bra_data->pb_stream_tag - 1); + host_cpr_params.link_dma_id = 0; + host_cpr_params.ch = 1; + host_cpr_params.s_freq = 96000; + host_cpr_params.s_fmt = 32; + host_cpr_params.linktype = 0; + host_cpr_params.stream = 0; + +#if IS_ENABLED(CONFIG_SND_SOC_INTEL_CNL_FPGA) + host_cpr_cfg.id.module_id = 3; +#else + host_cpr_cfg.id.module_id = 4; +#endif + host_cpr_cfg.id.instance_id = 1; + host_cpr_cfg.mcps = 100000; + host_cpr_cfg.mem_pages = 0; + host_cpr_cfg.ibs = 384; + host_cpr_cfg.obs = 384; + host_cpr_cfg.core_id = 0; + host_cpr_cfg.max_in_queue = 1; + host_cpr_cfg.max_out_queue = 1; + host_cpr_cfg.is_loadable = 0; + host_cpr_cfg.domain = 0; + host_cpr_cfg.m_type = SKL_MODULE_TYPE_COPIER; + host_cpr_cfg.dev_type = SKL_DEVICE_HDAHOST; + host_cpr_cfg.hw_conn_type = SKL_CONN_SOURCE; + host_cpr_cfg.formats_config.caps_size = 0; + host_cpr_cfg.dma_buffer_size = 2; + host_cpr_cfg.pdi_type = 0; + host_cpr_cfg.converter = 0; + host_cpr_cfg.vbus_id = 0; + host_cpr_cfg.sdw_agg_enable = 0; + host_cpr_cfg.formats_config.caps_size = 0; + + host_cpr_cfg.in_fmt[0].channels = 1; + host_cpr_cfg.in_fmt[0].s_freq = 96000; + host_cpr_cfg.in_fmt[0].bit_depth = 32; + host_cpr_cfg.in_fmt[0].valid_bit_depth = 24; + host_cpr_cfg.in_fmt[0].ch_cfg = 0; + host_cpr_cfg.in_fmt[0].interleaving_style = 0; + host_cpr_cfg.in_fmt[0].sample_type = 0; + host_cpr_cfg.in_fmt[0].ch_map = 0xFFFFFFF1; + + host_cpr_cfg.out_fmt[0].channels = 1; + host_cpr_cfg.out_fmt[0].s_freq = 96000; + host_cpr_cfg.out_fmt[0].bit_depth = 32; + host_cpr_cfg.out_fmt[0].valid_bit_depth = 24; + host_cpr_cfg.out_fmt[0].ch_cfg = 0; + host_cpr_cfg.out_fmt[0].interleaving_style = 0; + host_cpr_cfg.out_fmt[0].sample_type = 0; + host_cpr_cfg.out_fmt[0].ch_map = 0xFFFFFFF1; + + host_cpr_cfg.m_in_pin = kcalloc(host_cpr_cfg.max_in_queue, + sizeof(host_cpr_cfg.m_in_pin), + GFP_KERNEL); + if (!host_cpr_cfg.m_in_pin) { + ret = -ENOMEM; + goto error; + } + + host_cpr_cfg.m_out_pin = kcalloc(host_cpr_cfg.max_out_queue, + sizeof(host_cpr_cfg.m_out_pin), + GFP_KERNEL); + if (!host_cpr_cfg.m_out_pin) { + ret = -ENOMEM; + goto error; + } + + host_cpr_cfg.m_in_pin[0].id.module_id = + host_cpr_cfg.id.module_id; + host_cpr_cfg.m_in_pin[0].id.instance_id = + host_cpr_cfg.id.instance_id; + host_cpr_cfg.m_in_pin[0].in_use = false; + host_cpr_cfg.m_in_pin[0].is_dynamic = true; + host_cpr_cfg.m_in_pin[0].pin_state = SKL_PIN_UNBIND; + + host_cpr_cfg.m_out_pin[0].id.module_id = + host_cpr_cfg.id.module_id; + host_cpr_cfg.m_out_pin[0].id.instance_id = + host_cpr_cfg.id.instance_id; + host_cpr_cfg.m_out_pin[0].in_use = false; + host_cpr_cfg.m_out_pin[0].is_dynamic = true; + host_cpr_cfg.m_out_pin[0].pin_state = SKL_PIN_UNBIND; + + memcpy(&link_cpr_cfg, &host_cpr_cfg, + sizeof(struct skl_module_cfg)); + + link_cpr_cfg.id.instance_id = 2; + link_cpr_cfg.dev_type = SKL_DEVICE_SDW; +#if IS_ENABLED(CONFIG_SND_SOC_INTEL_CNL_FPGA) + link_cpr_cfg.sdw_stream_num = 0x3; +#else + link_cpr_cfg.sdw_stream_num = 0x13; +#endif + link_cpr_cfg.hw_conn_type = SKL_CONN_SOURCE; + + link_cpr_cfg.m_in_pin = kcalloc(link_cpr_cfg.max_in_queue, + sizeof(link_cpr_cfg.m_in_pin), + GFP_KERNEL); + if (!link_cpr_cfg.m_in_pin) { + ret = -ENOMEM; + goto error; + } + + link_cpr_cfg.m_out_pin = kcalloc(link_cpr_cfg.max_out_queue, + sizeof(link_cpr_cfg.m_out_pin), + GFP_KERNEL); + if (!link_cpr_cfg.m_out_pin) { + ret = -ENOMEM; + goto error; + } + + link_cpr_cfg.m_in_pin[0].id.module_id = + link_cpr_cfg.id.module_id; + link_cpr_cfg.m_in_pin[0].id.instance_id = + link_cpr_cfg.id.instance_id; + link_cpr_cfg.m_in_pin[0].in_use = false; + link_cpr_cfg.m_in_pin[0].is_dynamic = true; + link_cpr_cfg.m_in_pin[0].pin_state = SKL_PIN_UNBIND; + + link_cpr_cfg.m_out_pin[0].id.module_id = + link_cpr_cfg.id.module_id; + link_cpr_cfg.m_out_pin[0].id.instance_id = + link_cpr_cfg.id.instance_id; + link_cpr_cfg.m_out_pin[0].in_use = false; + link_cpr_cfg.m_out_pin[0].is_dynamic = true; + link_cpr_cfg.m_out_pin[0].pin_state = SKL_PIN_UNBIND; + + link_cpr_cfg.formats_config.caps_size = (sizeof(u32) * 4); + link_cpr_cfg.formats_config.caps = kzalloc((sizeof(u32) * 4), + GFP_KERNEL); + if (!link_cpr_cfg.formats_config.caps) { + ret = -ENOMEM; + goto error; + } + + link_cpr_cfg.formats_config.caps[0] = 0x0; + link_cpr_cfg.formats_config.caps[1] = 0x1; +#if IS_ENABLED(CONFIG_SND_SOC_INTEL_CNL_FPGA) + link_cpr_cfg.formats_config.caps[2] = 0x1003; +#else + link_cpr_cfg.formats_config.caps[2] = 0x1013; +#endif + link_cpr_cfg.formats_config.caps[3] = 0x0; + + /* Init PB CPR1 module */ + ret = skl_init_module(ctx, &host_cpr_cfg); + if (ret < 0) + goto error; + + /* Init PB CPR2 module */ + ret = skl_init_module(ctx, &link_cpr_cfg); + if (ret < 0) + goto error; + + /* Bind PB CPR1 and CPR2 module */ + ret = skl_bind_modules(ctx, &host_cpr_cfg, &link_cpr_cfg); + if (ret < 0) + goto error; + +error: + /* Free up all memory allocated */ + kfree(host_cpr_cfg.m_in_pin); + kfree(host_cpr_cfg.m_out_pin); + kfree(link_cpr_cfg.m_in_pin); + kfree(link_cpr_cfg.m_out_pin); + kfree(link_cpr_cfg.formats_config.caps); + + return ret; +} + +static int cnl_sdw_bra_pipe_cfg_cp(struct skl_sst *ctx, + unsigned int mstr_num) +{ + struct bra_conf *bra_data = &ctx->bra_pipe_data[mstr_num]; + struct skl_pipe *link_cpr_pipe = NULL; + struct skl_pipe_params link_cpr_params; + struct skl_module_cfg link_cpr_cfg, host_cpr_cfg; + int ret; + + /* Capture Pipeline */ + link_cpr_pipe = kzalloc(sizeof(struct skl_pipe), GFP_KERNEL); + if (!link_cpr_pipe) { + ret = -ENOMEM; + goto error; + } + + bra_data->cp_pipe = link_cpr_pipe; + link_cpr_pipe->p_params = &link_cpr_params; + link_cpr_cfg.pipe = link_cpr_pipe; + + link_cpr_pipe->ppl_id = 2; + link_cpr_pipe->pipe_priority = 0; + link_cpr_pipe->conn_type = 0; + link_cpr_pipe->memory_pages = 2; + + /* Create Capture Pipeline */ + ret = skl_create_pipeline(ctx, link_cpr_cfg.pipe); + if (ret < 0) + goto error; + + link_cpr_params.host_dma_id = 0; + link_cpr_params.link_dma_id = 0; + link_cpr_params.ch = 6; + link_cpr_params.s_freq = 48000; + link_cpr_params.s_fmt = 32; + link_cpr_params.linktype = 0; + link_cpr_params.stream = 0; + +#if IS_ENABLED(CONFIG_SND_SOC_INTEL_CNL_FPGA) + link_cpr_cfg.id.module_id = 3; +#else + link_cpr_cfg.id.module_id = 4; +#endif + link_cpr_cfg.id.instance_id = 3; + link_cpr_cfg.mcps = 100000; + link_cpr_cfg.mem_pages = 0; + link_cpr_cfg.ibs = 1152; + link_cpr_cfg.obs = 1152; + link_cpr_cfg.core_id = 0; + link_cpr_cfg.max_in_queue = 1; + link_cpr_cfg.max_out_queue = 1; + link_cpr_cfg.is_loadable = 0; + link_cpr_cfg.domain = 0; + link_cpr_cfg.m_type = SKL_MODULE_TYPE_COPIER; + link_cpr_cfg.dev_type = SKL_DEVICE_SDW; +#if IS_ENABLED(CONFIG_SND_SOC_INTEL_CNL_FPGA) + link_cpr_cfg.sdw_stream_num = 0x4; +#else + link_cpr_cfg.sdw_stream_num = 0x14; +#endif + link_cpr_cfg.hw_conn_type = SKL_CONN_SINK; + + link_cpr_cfg.formats_config.caps_size = 0; + link_cpr_cfg.dma_buffer_size = 2; + link_cpr_cfg.pdi_type = 0; + link_cpr_cfg.converter = 0; + link_cpr_cfg.vbus_id = 0; + link_cpr_cfg.sdw_agg_enable = 0; + link_cpr_cfg.formats_config.caps_size = (sizeof(u32) * 4); + link_cpr_cfg.formats_config.caps = kzalloc((sizeof(u32) * 4), + GFP_KERNEL); + if (!link_cpr_cfg.formats_config.caps) { + ret = -ENOMEM; + goto error; + } + + link_cpr_cfg.formats_config.caps[0] = 0x0; + link_cpr_cfg.formats_config.caps[1] = 0x1; +#if IS_ENABLED(CONFIG_SND_SOC_INTEL_CNL_FPGA) + link_cpr_cfg.formats_config.caps[2] = 0x1104; +#else + link_cpr_cfg.formats_config.caps[2] = 0x1114; +#endif + link_cpr_cfg.formats_config.caps[3] = 0x1; + + link_cpr_cfg.in_fmt[0].channels = 6; + link_cpr_cfg.in_fmt[0].s_freq = 48000; + link_cpr_cfg.in_fmt[0].bit_depth = 32; + link_cpr_cfg.in_fmt[0].valid_bit_depth = 24; + link_cpr_cfg.in_fmt[0].ch_cfg = 8; + link_cpr_cfg.in_fmt[0].interleaving_style = 0; + link_cpr_cfg.in_fmt[0].sample_type = 0; + link_cpr_cfg.in_fmt[0].ch_map = 0xFF657120; + + link_cpr_cfg.out_fmt[0].channels = 6; + link_cpr_cfg.out_fmt[0].s_freq = 48000; + link_cpr_cfg.out_fmt[0].bit_depth = 32; + link_cpr_cfg.out_fmt[0].valid_bit_depth = 24; + link_cpr_cfg.out_fmt[0].ch_cfg = 8; + link_cpr_cfg.out_fmt[0].interleaving_style = 0; + link_cpr_cfg.out_fmt[0].sample_type = 0; + link_cpr_cfg.out_fmt[0].ch_map = 0xFF657120; + + link_cpr_cfg.m_in_pin = kcalloc(link_cpr_cfg.max_in_queue, + sizeof(link_cpr_cfg.m_in_pin), + GFP_KERNEL); + if (!link_cpr_cfg.m_in_pin) { + ret = -ENOMEM; + goto error; + } + + link_cpr_cfg.m_out_pin = kcalloc(link_cpr_cfg.max_out_queue, + sizeof(link_cpr_cfg.m_out_pin), + GFP_KERNEL); + if (!link_cpr_cfg.m_out_pin) { + ret = -ENOMEM; + goto error; + } + + link_cpr_cfg.m_in_pin[0].id.module_id = + link_cpr_cfg.id.module_id; + link_cpr_cfg.m_in_pin[0].id.instance_id = + link_cpr_cfg.id.instance_id; + link_cpr_cfg.m_in_pin[0].in_use = false; + link_cpr_cfg.m_in_pin[0].is_dynamic = true; + link_cpr_cfg.m_in_pin[0].pin_state = SKL_PIN_UNBIND; + + link_cpr_cfg.m_out_pin[0].id.module_id = + link_cpr_cfg.id.module_id; + link_cpr_cfg.m_out_pin[0].id.instance_id = + link_cpr_cfg.id.instance_id; + link_cpr_cfg.m_out_pin[0].in_use = false; + link_cpr_cfg.m_out_pin[0].is_dynamic = true; + link_cpr_cfg.m_out_pin[0].pin_state = SKL_PIN_UNBIND; + + memcpy(&host_cpr_cfg, &link_cpr_cfg, + sizeof(struct skl_module_cfg)); + + host_cpr_cfg.id.instance_id = 4; + host_cpr_cfg.dev_type = SKL_DEVICE_HDAHOST; + host_cpr_cfg.hw_conn_type = SKL_CONN_SINK; + link_cpr_params.host_dma_id = (bra_data->cp_stream_tag - 1); + host_cpr_cfg.formats_config.caps_size = 0; + host_cpr_cfg.m_in_pin = kcalloc(host_cpr_cfg.max_in_queue, + sizeof(host_cpr_cfg.m_in_pin), + GFP_KERNEL); + if (!host_cpr_cfg.m_in_pin) { + ret = -ENOMEM; + goto error; + } + + host_cpr_cfg.m_out_pin = kcalloc(host_cpr_cfg.max_out_queue, + sizeof(host_cpr_cfg.m_out_pin), + GFP_KERNEL); + if (!host_cpr_cfg.m_out_pin) { + ret = -ENOMEM; + goto error; + } + + host_cpr_cfg.m_in_pin[0].id.module_id = + host_cpr_cfg.id.module_id; + host_cpr_cfg.m_in_pin[0].id.instance_id = + host_cpr_cfg.id.instance_id; + host_cpr_cfg.m_in_pin[0].in_use = false; + host_cpr_cfg.m_in_pin[0].is_dynamic = true; + host_cpr_cfg.m_in_pin[0].pin_state = SKL_PIN_UNBIND; + + host_cpr_cfg.m_out_pin[0].id.module_id = + host_cpr_cfg.id.module_id; + host_cpr_cfg.m_out_pin[0].id.instance_id = + host_cpr_cfg.id.instance_id; + host_cpr_cfg.m_out_pin[0].in_use = false; + host_cpr_cfg.m_out_pin[0].is_dynamic = true; + host_cpr_cfg.m_out_pin[0].pin_state = SKL_PIN_UNBIND; + + /* Init CP CPR1 module */ + ret = skl_init_module(ctx, &link_cpr_cfg); + if (ret < 0) + goto error; + + /* Init CP CPR2 module */ + ret = skl_init_module(ctx, &host_cpr_cfg); + if (ret < 0) + goto error; + + /* Bind CP CPR1 and CPR2 module */ + ret = skl_bind_modules(ctx, &link_cpr_cfg, &host_cpr_cfg); + if (ret < 0) + goto error; + + +error: + /* Free up all memory allocated */ + kfree(link_cpr_cfg.formats_config.caps); + kfree(link_cpr_cfg.m_in_pin); + kfree(link_cpr_cfg.m_out_pin); + kfree(host_cpr_cfg.m_in_pin); + kfree(host_cpr_cfg.m_out_pin); + + return ret; +} + +static int cnl_sdw_bra_pipe_setup(struct skl_sst *ctx, bool enable, + unsigned int mstr_num) +{ + struct bra_conf *bra_data = &ctx->bra_pipe_data[mstr_num]; + int ret; + + /* + * This function creates TX and TX pipelines for BRA transfers. + * TODO: Currently the pipelines are created manually. All the + * values needs to be received from XML based on the configuration + * used. + */ + + if (enable) { + + /* Create playback pipeline */ + ret = cnl_sdw_bra_pipe_cfg_pb(ctx, mstr_num); + if (ret < 0) + goto error; + + /* Create capture pipeline */ + ret = cnl_sdw_bra_pipe_cfg_cp(ctx, mstr_num); + if (ret < 0) + goto error; + } else { + + /* Delete playback pipeline */ + ret = skl_delete_pipe(ctx, bra_data->pb_pipe); + if (ret < 0) + goto error; + + /* Delete capture pipeline */ + ret = skl_delete_pipe(ctx, bra_data->cp_pipe); + if (ret < 0) + goto error; + } + + if (enable) + return 0; +error: + /* Free up all memory allocated */ + kfree(bra_data->pb_pipe); + kfree(bra_data->cp_pipe); + + return ret; +} + +static int cnl_sdw_bra_dma_trigger(struct skl_sst *ctx, bool enable, + unsigned int mstr_num) +{ + struct sst_dsp *dsp_ctx = ctx->dsp; + struct bra_conf *bra_data = &ctx->bra_pipe_data[mstr_num]; + int ret; + + if (enable) { + + ret = dsp_ctx->dsp_ops.trigger(dsp_ctx->dev, true, + bra_data->cp_stream_tag, + SNDRV_PCM_STREAM_CAPTURE); + if (ret < 0) { + dev_err(ctx->dev, "BRA: RX DMA trigger failed: 0x%x\n", ret); + goto bra_dma_failed; + } + + ret = dsp_ctx->dsp_ops.trigger(dsp_ctx->dev, true, + bra_data->pb_stream_tag, + SNDRV_PCM_STREAM_PLAYBACK); + if (ret < 0) { + dev_err(ctx->dev, "BRA: TX DMA trigger failed: 0x%x\n", ret); + goto bra_dma_failed; + } + + } else { + + ret = dsp_ctx->dsp_ops.trigger(dsp_ctx->dev, false, + bra_data->cp_stream_tag, + SNDRV_PCM_STREAM_CAPTURE); + if (ret < 0) { + dev_err(ctx->dev, "BRA: RX DMA trigger stop failed: 0x%x\n", ret); + goto bra_dma_failed; + } + ret = dsp_ctx->dsp_ops.trigger(dsp_ctx->dev, false, + bra_data->pb_stream_tag, + SNDRV_PCM_STREAM_PLAYBACK); + if (ret < 0) { + dev_err(ctx->dev, "BRA: TX DMA trigger stop failed: 0x%x\n", ret); + goto bra_dma_failed; + } + } + + if (enable) + return 0; + +bra_dma_failed: + + /* Free up resources */ + dsp_ctx->dsp_ops.cleanup(dsp_ctx->dev, &bra_data->pb_dmab, + bra_data->pb_stream_tag, + SNDRV_PCM_STREAM_PLAYBACK); + dsp_ctx->dsp_ops.cleanup(dsp_ctx->dev, &bra_data->cp_dmab, + bra_data->cp_stream_tag, + SNDRV_PCM_STREAM_CAPTURE); + + return ret; +} + + +static int cnl_sdw_bra_dma_setup(struct skl_sst *ctx, bool enable, + struct bra_info *info) +{ + struct sst_dsp *dsp_ctx = ctx->dsp; + struct bra_conf *bra_data = &ctx->bra_pipe_data[info->mstr_num]; + struct snd_dma_buffer *pb_dmab = &bra_data->pb_dmab; + struct snd_dma_buffer *cp_dmab = &bra_data->cp_dmab; + u32 pb_pages = 0, cp_pages = 0; + int pb_block_size = info->tx_block_size; + int cp_block_size = info->rx_block_size; + int ret = 0; + + /* + * TODO: In future below approach can be replaced by component + * framework + */ + if (enable) { + + /* + * Take below number for BRA DMA format + * Format = (32 * 2 = 64) = 0x40 Size = 0x80 + */ + + /* Prepare TX Host DMA */ + bra_data->pb_stream_tag = dsp_ctx->dsp_ops.prepare(dsp_ctx->dev, + 0x40, pb_block_size, + pb_dmab, + SNDRV_PCM_STREAM_PLAYBACK); + if (bra_data->pb_stream_tag <= 0) { + dev_err(dsp_ctx->dev, "BRA: PB DMA prepare failed: 0x%x\n", + bra_data->pb_stream_tag); + ret = -EINVAL; + goto bra_dma_failed; + } + + pb_pages = (pb_block_size + PAGE_SIZE - 1) >> PAGE_SHIFT; + set_memory_uc((unsigned long) pb_dmab->area, pb_pages); + memcpy(pb_dmab->area, info->tx_ptr, pb_block_size); + + /* Prepare RX Host DMA */ + bra_data->cp_stream_tag = dsp_ctx->dsp_ops.prepare(dsp_ctx->dev, + 0x40, cp_block_size, + cp_dmab, + SNDRV_PCM_STREAM_CAPTURE); + if (bra_data->cp_stream_tag <= 0) { + dev_err(dsp_ctx->dev, "BRA: CP DMA prepare failed: 0x%x\n", + bra_data->cp_stream_tag); + ret = -EINVAL; + goto bra_dma_failed; + } + + cp_pages = (cp_block_size + PAGE_SIZE - 1) >> PAGE_SHIFT; + set_memory_uc((unsigned long) cp_dmab->area, cp_pages); + + } else { + + ret = dsp_ctx->dsp_ops.cleanup(dsp_ctx->dev, &bra_data->pb_dmab, + bra_data->pb_stream_tag, + SNDRV_PCM_STREAM_PLAYBACK); + if (ret < 0) + goto bra_dma_failed; + + ret = dsp_ctx->dsp_ops.cleanup(dsp_ctx->dev, &bra_data->cp_dmab, + bra_data->cp_stream_tag, + SNDRV_PCM_STREAM_CAPTURE); + if (ret < 0) + goto bra_dma_failed; + + } + +bra_dma_failed: + + return ret; +} + +static int cnl_sdw_bra_setup(void *context, bool enable, + struct bra_info *info) +{ + struct skl_sst *ctx = context; + int ret; + + if (enable) { + + /* Setup Host DMA */ + ret = cnl_sdw_bra_dma_setup(ctx, true, info); + if (ret < 0) + goto error; + + /* Create Pipeline */ + ret = cnl_sdw_bra_pipe_setup(ctx, true, info->mstr_num); + if (ret < 0) + goto error; + + } else { + + /* De-setup Host DMA */ + ret = cnl_sdw_bra_dma_setup(ctx, false, info); + if (ret < 0) + goto error; + + /* Delete Pipeline */ + ret = cnl_sdw_bra_pipe_setup(ctx, false, info->mstr_num); + if (ret < 0) + goto error; + + } + +error: + return ret; +} + + +static int cnl_sdw_bra_xfer(void *context, bool enable, + struct bra_info *info) +{ + + struct skl_sst *ctx = context; + struct bra_conf *bra_data = &ctx->bra_pipe_data[info->mstr_num]; + struct snd_dma_buffer *cp_dmab = &bra_data->cp_dmab; + int ret; + + if (enable) { + + /* + * TODO: Need to check on how to check on RX buffer + * completion. Approaches can be used: + * 1. Check any of LPIB, SPIB or DPIB register for + * xfer completion. + * 2. Add Interrupt of completion (IOC) for RX DMA buffer. + * This needs to adds changes in common infrastructure code + * only for BRA feature. + * Currenly we are just sleeping for 100 ms and copying + * data to appropriate RX buffer. + */ + + /* Trigger Host DMA */ + ret = cnl_sdw_bra_dma_trigger(ctx, true, info->mstr_num); + if (ret < 0) + goto error; + + /* Trigger Pipeline */ + ret = cnl_sdw_bra_pipe_trigger(ctx, true, info->mstr_num); + if (ret < 0) + goto error; + + + /* Sleep for 100 ms */ + msleep(100); + + /* TODO: Remove below hex dump print */ + print_hex_dump(KERN_DEBUG, "BRA CP DMA BUFFER DUMP RCVD:", DUMP_PREFIX_OFFSET, 8, 4, + cp_dmab->area, cp_dmab->bytes, false); + + /* Copy data in RX buffer */ + memcpy(info->rx_ptr, cp_dmab->area, info->rx_block_size); + + } else { + + /* Stop Host DMA */ + ret = cnl_sdw_bra_dma_trigger(ctx, false, info->mstr_num); + if (ret < 0) + goto error; + + /* Stop Pipeline */ + ret = cnl_sdw_bra_pipe_trigger(ctx, false, info->mstr_num); + if (ret < 0) + goto error; + } + +error: + return ret; +} + + +struct cnl_bra_operation cnl_sdw_bra_ops = { + .bra_platform_setup = cnl_sdw_bra_setup, + .bra_platform_xfer = cnl_sdw_bra_xfer, +}; + + const struct skl_dsp_ops *skl_get_dsp_ops(int pci_id) { int i; @@ -498,10 +1235,11 @@ static void skl_set_base_module_format(struct skl_sst *ctx, base_cfg->audio_fmt.bit_depth = format->bit_depth; base_cfg->audio_fmt.valid_bit_depth = format->valid_bit_depth; base_cfg->audio_fmt.ch_cfg = format->ch_cfg; + base_cfg->audio_fmt.sample_type = format->sample_type; - dev_dbg(ctx->dev, "bit_depth=%x valid_bd=%x ch_config=%x\n", + dev_dbg(ctx->dev, "bit_depth=%x valid_bd=%x ch_config=%x sample_type:%x\n", format->bit_depth, format->valid_bit_depth, - format->ch_cfg); + format->ch_cfg, format->sample_type); base_cfg->audio_fmt.channel_map = format->ch_map; diff --git a/sound/soc/intel/skylake/skl-sst-ipc.h b/sound/soc/intel/skylake/skl-sst-ipc.h index e969ac964daa..0cde541f900c 100644 --- a/sound/soc/intel/skylake/skl-sst-ipc.h +++ b/sound/soc/intel/skylake/skl-sst-ipc.h @@ -17,6 +17,7 @@ #define __SKL_IPC_H #include +#include #include "../common/sst-ipc.h" #include "skl-sst-dsp.h" @@ -105,6 +106,15 @@ struct skl_probe_config { struct extractor_data eprobe[NO_OF_EXTRACTOR]; }; +struct bra_conf { + struct snd_dma_buffer pb_dmab; + struct snd_dma_buffer cp_dmab; + int pb_stream_tag; + int cp_stream_tag; + struct skl_pipe *pb_pipe; + struct skl_pipe *cp_pipe; +}; + struct skl_sst { struct device *dev; struct sst_dsp *dsp; @@ -156,7 +166,11 @@ struct skl_sst { int num_sdw_controllers; /* Array of sdw masters */ struct sdw_master *mstr; + struct skl_probe_config probe_config; + + /* BRA configuration data */ + struct bra_conf *bra_pipe_data; }; struct skl_ipc_init_instance_msg { From 77ecd0ffe1cb88092e9de8249037a712df5d2a97 Mon Sep 17 00:00:00 2001 From: Sanyog Kale Date: Sun, 2 Jul 2017 11:51:00 +0530 Subject: [PATCH 0700/1103] ASoC: Intel: CNL: Register BRA ops in init. Signed-off-by: Sanyog Kale --- sound/soc/intel/skylake/bxt-sst.c | 2 +- sound/soc/intel/skylake/cnl-sst-dsp.h | 2 +- sound/soc/intel/skylake/cnl-sst.c | 19 ++++++++++++++++--- sound/soc/intel/skylake/skl-messages.c | 5 ++--- sound/soc/intel/skylake/skl-sst-dsp.h | 8 ++++---- sound/soc/intel/skylake/skl-sst.c | 3 ++- sound/soc/intel/skylake/skl.h | 6 +++--- 7 files changed, 29 insertions(+), 16 deletions(-) diff --git a/sound/soc/intel/skylake/bxt-sst.c b/sound/soc/intel/skylake/bxt-sst.c index 2b169b40e6c4..6f9b24c8c211 100644 --- a/sound/soc/intel/skylake/bxt-sst.c +++ b/sound/soc/intel/skylake/bxt-sst.c @@ -566,7 +566,7 @@ static struct sst_dsp_device skl_dev = { int bxt_sst_dsp_init(struct device *dev, void __iomem *mmio_base, int irq, const char *fw_name, struct skl_dsp_loader_ops dsp_ops, - struct skl_sst **dsp) + struct skl_sst **dsp, void *ptr) { struct skl_sst *skl; struct sst_dsp *sst; diff --git a/sound/soc/intel/skylake/cnl-sst-dsp.h b/sound/soc/intel/skylake/cnl-sst-dsp.h index 323cebc4e389..5f0653b36308 100644 --- a/sound/soc/intel/skylake/cnl-sst-dsp.h +++ b/sound/soc/intel/skylake/cnl-sst-dsp.h @@ -118,7 +118,7 @@ void cnl_ipc_free(struct sst_generic_ipc *ipc); int cnl_sst_dsp_init(struct device *dev, void __iomem *mmio_base, int irq, const char *fw_name, struct skl_dsp_loader_ops dsp_ops, - struct skl_sst **dsp); + struct skl_sst **dsp, void *ptr); int cnl_sst_init_fw(struct device *dev, struct skl_sst *ctx); void cnl_sst_dsp_cleanup(struct device *dev, struct skl_sst *ctx); diff --git a/sound/soc/intel/skylake/cnl-sst.c b/sound/soc/intel/skylake/cnl-sst.c index 784d92df7ac3..ae3c6aa5557e 100644 --- a/sound/soc/intel/skylake/cnl-sst.c +++ b/sound/soc/intel/skylake/cnl-sst.c @@ -516,13 +516,14 @@ static int cnl_ipc_init(struct device *dev, struct skl_sst *cnl) #if IS_ENABLED(CONFIG_SND_SOC_RT700) static int skl_register_sdw_masters(struct device *dev, struct skl_sst *dsp, - void __iomem *mmio_base, int irq) + void __iomem *mmio_base, int irq, void *ptr) { struct sdw_master_capabilities *m_cap, *map_data; struct sdw_mstr_dp0_capabilities *dp0_cap; struct sdw_mstr_dpn_capabilities *dpn_cap; struct sdw_master *master; struct cnl_sdw_data *p_data; + struct cnl_bra_operation *p_ptr = ptr; int ret = 0, i, j, k, wl = 0; /* TODO: This number 4 should come from ACPI */ #if defined(CONFIG_SDW_MAXIM_SLAVE) || defined(CONFIG_SND_SOC_MXFPGA) @@ -538,11 +539,18 @@ static int skl_register_sdw_masters(struct device *dev, struct skl_sst *dsp, dsp->num_sdw_controllers = 0; } dsp->mstr = master; + + dsp->bra_pipe_data = devm_kzalloc(dev, + (sizeof(*dsp->bra_pipe_data) * + dsp->num_sdw_controllers), + GFP_KERNEL); + /* TODO This should come from ACPI */ for (i = 0; i < dsp->num_sdw_controllers; i++) { p_data = devm_kzalloc(dev, sizeof(*p_data), GFP_KERNEL); if (!p_data) return -ENOMEM; + /* PCI Device is parent of the SoundWire master device */ /* TODO: All these hardcoding should come from ACPI */ master[i].dev.parent = dev; @@ -651,6 +659,11 @@ static int skl_register_sdw_masters(struct device *dev, struct skl_sst *dsp, p_data->alh_base = mmio_base + CNL_ALH_BASE; p_data->inst_id = i; p_data->irq = irq; + + p_data->bra_data = kzalloc((sizeof(struct cnl_sdw_bra_cfg)), + GFP_KERNEL); + p_data->bra_data->drv_data = dsp; + p_data->bra_data->bra_ops = p_ptr; ret = sdw_add_master_controller(&master[i]); if (ret) { dev_err(dev, "Failed to register soundwire master\n"); @@ -676,7 +689,7 @@ static void skl_unregister_sdw_masters(struct skl_sst *ctx) int cnl_sst_dsp_init(struct device *dev, void __iomem *mmio_base, int irq, const char *fw_name, struct skl_dsp_loader_ops dsp_ops, - struct skl_sst **dsp) + struct skl_sst **dsp, void *ptr) { struct skl_sst *cnl; struct sst_dsp *sst; @@ -731,7 +744,7 @@ int cnl_sst_dsp_init(struct device *dev, void __iomem *mmio_base, int irq, } #if IS_ENABLED(CONFIG_SND_SOC_RT700) - ret = skl_register_sdw_masters(dev, cnl, mmio_base, irq); + ret = skl_register_sdw_masters(dev, cnl, mmio_base, irq, ptr); if (ret) { dev_err(cnl->dev, "%s SoundWire masters registration failed\n", __func__); return ret; diff --git a/sound/soc/intel/skylake/skl-messages.c b/sound/soc/intel/skylake/skl-messages.c index 28d990450463..bb7f24286d30 100644 --- a/sound/soc/intel/skylake/skl-messages.c +++ b/sound/soc/intel/skylake/skl-messages.c @@ -1049,9 +1049,8 @@ int skl_init_dsp(struct skl *skl) } loader_ops = ops->loader_ops(); - ret = ops->init(bus->dev, mmio_base, irq, - skl->fw_name, loader_ops, - &skl->skl_sst); + ret = ops->init(bus->dev, mmio_base, irq, skl->fw_name, loader_ops, + &skl->skl_sst, &cnl_sdw_bra_ops); if (ret < 0) goto unmap_mmio; diff --git a/sound/soc/intel/skylake/skl-sst-dsp.h b/sound/soc/intel/skylake/skl-sst-dsp.h index 2a3ad2d43448..e874e75a15fc 100644 --- a/sound/soc/intel/skylake/skl-sst-dsp.h +++ b/sound/soc/intel/skylake/skl-sst-dsp.h @@ -230,11 +230,11 @@ int skl_dsp_put_core(struct sst_dsp *ctx, unsigned int core_id); int skl_dsp_boot(struct sst_dsp *ctx); int skl_sst_dsp_init(struct device *dev, void __iomem *mmio_base, int irq, - const char *fw_name, struct skl_dsp_loader_ops dsp_ops, - struct skl_sst **dsp); + const char *fw_name, struct skl_dsp_loader_ops dsp_ops, + struct skl_sst **dsp, void *ptr); int bxt_sst_dsp_init(struct device *dev, void __iomem *mmio_base, int irq, - const char *fw_name, struct skl_dsp_loader_ops dsp_ops, - struct skl_sst **dsp); + const char *fw_name, struct skl_dsp_loader_ops dsp_ops, + struct skl_sst **dsp, void *ptr); int skl_sst_init_fw(struct device *dev, struct skl_sst *ctx); int bxt_sst_init_fw(struct device *dev, struct skl_sst *ctx); void skl_sst_dsp_cleanup(struct device *dev, struct skl_sst *ctx); diff --git a/sound/soc/intel/skylake/skl-sst.c b/sound/soc/intel/skylake/skl-sst.c index 5951bbdf1f1a..277e972eb858 100644 --- a/sound/soc/intel/skylake/skl-sst.c +++ b/sound/soc/intel/skylake/skl-sst.c @@ -528,7 +528,8 @@ static struct sst_dsp_device skl_dev = { }; int skl_sst_dsp_init(struct device *dev, void __iomem *mmio_base, int irq, - const char *fw_name, struct skl_dsp_loader_ops dsp_ops, struct skl_sst **dsp) + const char *fw_name, struct skl_dsp_loader_ops dsp_ops, + struct skl_sst **dsp, void *ptr) { struct skl_sst *skl; struct sst_dsp *sst; diff --git a/sound/soc/intel/skylake/skl.h b/sound/soc/intel/skylake/skl.h index 4c7cb272a029..2bafc7d40cec 100644 --- a/sound/soc/intel/skylake/skl.h +++ b/sound/soc/intel/skylake/skl.h @@ -125,10 +125,10 @@ struct skl_dsp_ops { int id; unsigned int num_cores; struct skl_dsp_loader_ops (*loader_ops)(void); - int (*init)(struct device *dev, void __iomem *mmio_base, - int irq, const char *fw_name, + int (*init)(struct device *dev, void __iomem *mmio_base, int irq, + const char *fw_name, struct skl_dsp_loader_ops loader_ops, - struct skl_sst **skl_sst); + struct skl_sst **skl_sst, void *ptr); int (*init_fw)(struct device *dev, struct skl_sst *ctx); void (*cleanup)(struct device *dev, struct skl_sst *ctx); }; From fec5559432843db63a797104ae62f564233214ed Mon Sep 17 00:00:00 2001 From: Sanyog Kale Date: Thu, 27 Oct 2016 12:04:26 +0530 Subject: [PATCH 0701/1103] ASoC: rt700: codec changes for BRA feature This patch create sysfs entry to test BRA feature. sysfs entry created: /sys/bus/soundwire/devices//bra_trigger On reading this sysfs entry, codec driver will initiate BRA read request of 36 bytes to bus driver. On successful packet transfer, bus driver will return SUCCESS with 36 bytes copied in buffer provided by codec driver. Signed-off-by: Sanyog Kale --- sound/soc/codecs/rt700.c | 73 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 73 insertions(+) diff --git a/sound/soc/codecs/rt700.c b/sound/soc/codecs/rt700.c index 8cb67827a8db..773e77bc7ed6 100644 --- a/sound/soc/codecs/rt700.c +++ b/sound/soc/codecs/rt700.c @@ -1432,6 +1432,70 @@ static int rt700_clock_config(struct device *dev, struct alc700 *alc700) return 0; } +static int rt700_create_bra_block(struct sdw_slv *slave) +{ + struct sdw_bra_block bra_block; + u8 *value; + int ret = 0; + + /* Fill bra data structure */ + bra_block.slave_addr = slave->slv_addr->slv_number; + bra_block.cmd = 0; /* Read command */ + bra_block.num_bytes = 36; /* 36 bytes */ + bra_block.reg_offset = 0x0000; + + value = kzalloc(bra_block.num_bytes, GFP_KERNEL); + if (!value) + return -ENOMEM; + + /* Memset with some fixed pattern */ + memset(value, 0xAB, bra_block.num_bytes); + bra_block.values = value; + + pr_info("SDW: BRA: slv_addr:%d, cmd:%d, num_bytes:%d, reg_offset:0x%x\n", + bra_block.slave_addr, + bra_block.cmd, + bra_block.num_bytes, + bra_block.reg_offset); + + print_hex_dump(KERN_DEBUG, "SDW: BRA: CODEC BUFFER:", DUMP_PREFIX_OFFSET, 8, 4, + bra_block.values, bra_block.num_bytes, false); + + + ret = sdw_slave_xfer_bra_block(slave->mstr, &bra_block); + if (ret) { + dev_err(&slave->dev, "SDW: BRA transfer failed\n"); + kfree(bra_block.values); + return ret; + } + + print_hex_dump(KERN_DEBUG, "SDW: BRA: CODEC BUFFER RCVD:", DUMP_PREFIX_OFFSET, 8, 4, + bra_block.values, bra_block.num_bytes, false); + + pr_info("SDW: BRA: Transfer successful\n"); + + /* Free up memory */ + kfree(bra_block.values); + + return ret; +} + +static ssize_t rt700_bra_trigger(struct device *dev, + struct device_attribute *attr, char *buf) +{ + + struct rt700_priv *rt700 = dev_get_drvdata(dev); + + pm_runtime_get_sync(dev); + + rt700_create_bra_block(rt700->sdw); + + pm_runtime_put_sync(dev); + + return 0; +} +static DEVICE_ATTR(bra_trigger, 0444, rt700_bra_trigger, NULL); + int rt700_probe(struct device *dev, struct regmap *regmap, struct sdw_slave *slave) { @@ -1548,6 +1612,15 @@ int rt700_probe(struct device *dev, struct regmap *regmap, return ret; } + /* create sysfs entry */ + ret = device_create_file(dev, &dev_attr_bra_trigger); + if (ret < 0) + return ret; + + pm_runtime_get_sync(&rt700->sdw->mstr->dev); + pm_runtime_mark_last_busy(&rt700->sdw->mstr->dev); + pm_runtime_put_sync_autosuspend(&rt700->sdw->mstr->dev); + pm_runtime_set_autosuspend_delay(&slave->dev, 3000); pm_runtime_use_autosuspend(&slave->dev); pm_runtime_enable(&slave->dev); From 1c4a572c290e90b573f50264114e9d9462497d8d Mon Sep 17 00:00:00 2001 From: Jeeja KP Date: Wed, 6 Dec 2006 11:28:06 +0530 Subject: [PATCH 0702/1103] ASoC: Intel: Add support for Icelake IDs Icelake is next gen SoC, so add the IDs for Icelake Change-Id: I1210d2ea5c1d19137cd0829bd0b86a13a8fcd4f Signed-off-by: Dharageswari R Reviewed-on: Reviewed-by: Diwakar, Praveen Reviewed-by: Singh, Guneshwor O Reviewed-by: Kale, Sanyog R Tested-by: Avati, Santosh Kumar Reviewed-by: Prusty, Subhransu S Reviewed-by: Kp, Jeeja Signed-off-by: Guneshwor Singh --- sound/soc/intel/skylake/skl-messages.c | 8 ++++++++ sound/soc/intel/skylake/skl.c | 8 ++++++++ 2 files changed, 16 insertions(+) diff --git a/sound/soc/intel/skylake/skl-messages.c b/sound/soc/intel/skylake/skl-messages.c index bb7f24286d30..dde1aeb11153 100644 --- a/sound/soc/intel/skylake/skl-messages.c +++ b/sound/soc/intel/skylake/skl-messages.c @@ -274,6 +274,14 @@ static const struct skl_dsp_ops dsp_ops[] = { .init_fw = cnl_sst_init_fw, .cleanup = cnl_sst_dsp_cleanup }, + { + .id = 0x34c8, + .num_cores = 4, + .loader_ops = bxt_get_loader_ops, + .init = cnl_sst_dsp_init, + .init_fw = cnl_sst_init_fw, + .cleanup = cnl_sst_dsp_cleanup + }, }; static int cnl_sdw_bra_pipe_trigger(struct skl_sst *ctx, bool enable, diff --git a/sound/soc/intel/skylake/skl.c b/sound/soc/intel/skylake/skl.c index 19ee283f9586..e4eb58c7a747 100644 --- a/sound/soc/intel/skylake/skl.c +++ b/sound/soc/intel/skylake/skl.c @@ -1215,6 +1215,11 @@ static const struct snd_soc_acpi_mach sst_cnl_devdata[] = { {} }; +static struct sst_acpi_mach sst_icl_devdata[] = { + { "dummy", "icl_wm8281", "intel/dsp_fw_icl.bin", NULL, NULL, NULL }, + {} +}; + /* PCI IDs */ static const struct pci_device_id skl_ids[] = { /* Sunrise Point-LP */ @@ -1232,6 +1237,9 @@ static const struct pci_device_id skl_ids[] = { /* CNL */ { PCI_DEVICE(0x8086, 0x9dc8), .driver_data = (unsigned long)&snd_soc_acpi_intel_cnl_machines}, + /* ICL */ + { PCI_DEVICE(0x8086, 0x34c8), + .driver_data = (unsigned long)&sst_icl_devdata}, { 0, } }; MODULE_DEVICE_TABLE(pci, skl_ids); From d815e23e948bed1c09b9387fad8cee440150538f Mon Sep 17 00:00:00 2001 From: Jaikrishna Nemallapudi Date: Wed, 14 Dec 2016 17:12:45 +0530 Subject: [PATCH 0703/1103] ASoC: Intel: Fix build warning for unused variables This patch initialize uninitialized variables reported as build warnings. Change-Id: Idaa8c3d5992a9d29d21646d653a2db57e8bbbd2a Signed-off-by: Jaikrishna Nemallapudi Reviewed-on: Reviewed-by: Diwakar, Praveen Reviewed-by: Kale, Sanyog R Reviewed-by: Koul, Vinod Reviewed-by: Singh, Guneshwor O Reviewed-by: Kp, Jeeja Tested-by: Avati, Santosh Kumar Reviewed-by: Prusty, Subhransu S Signed-off-by: Guneshwor Singh --- sound/soc/intel/skylake/skl-compress.c | 4 +--- sound/soc/intel/skylake/skl-compress.h | 5 +++-- sound/soc/intel/skylake/skl-debug.c | 5 +++++ sound/soc/intel/skylake/skl-probe.c | 5 ++++- sound/soc/intel/skylake/skl.c | 2 +- 5 files changed, 14 insertions(+), 7 deletions(-) diff --git a/sound/soc/intel/skylake/skl-compress.c b/sound/soc/intel/skylake/skl-compress.c index c8b26e80b974..a0b33e198722 100644 --- a/sound/soc/intel/skylake/skl-compress.c +++ b/sound/soc/intel/skylake/skl-compress.c @@ -25,6 +25,7 @@ #include #include #include "skl.h" +#include "skl-compress.h" inline struct hdac_ext_stream *get_hdac_ext_compr_stream(struct snd_compr_stream *stream) { @@ -61,12 +62,9 @@ void skl_set_compr_runtime_buffer(struct snd_compr_stream *substream, int skl_compr_malloc_pages(struct snd_compr_stream *substream, struct hdac_ext_bus *ebus, size_t size) { - struct snd_compr_runtime *runtime; struct snd_dma_buffer *dmab = NULL; struct skl *skl = ebus_to_skl(ebus); - runtime = substream->runtime; - dmab = kzalloc(sizeof(*dmab), GFP_KERNEL); if (!dmab) return -ENOMEM; diff --git a/sound/soc/intel/skylake/skl-compress.h b/sound/soc/intel/skylake/skl-compress.h index 9fcf6c38f5b8..2db347369c0c 100644 --- a/sound/soc/intel/skylake/skl-compress.h +++ b/sound/soc/intel/skylake/skl-compress.h @@ -18,7 +18,8 @@ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * */ - +#ifndef __SKL_COMPRESS_H__ +#define __SKL_COMPRESS_H__ inline struct hdac_ext_stream *get_hdac_ext_compr_stream(struct snd_compr_stream *stream); struct hdac_ext_bus *get_bus_compr_ctx(struct snd_compr_stream *substream); @@ -32,4 +33,4 @@ int skl_substream_alloc_compr_pages(struct hdac_ext_bus *ebus, int skl_compr_free_pages(struct snd_compr_stream *substream); int skl_substream_free_compr_pages(struct hdac_bus *bus, struct snd_compr_stream *substream); - +#endif /* __SKL_COMPRESS_H__*/ diff --git a/sound/soc/intel/skylake/skl-debug.c b/sound/soc/intel/skylake/skl-debug.c index ff6d8356cb4f..8a20f86544d2 100644 --- a/sound/soc/intel/skylake/skl-debug.c +++ b/sound/soc/intel/skylake/skl-debug.c @@ -639,6 +639,8 @@ static ssize_t adsp_control_write(struct file *file, } err = kstrtouint(buf, 10, &dsp_property); + if (err) + return -EINVAL; if ((dsp_property == DMA_CONTROL) || (dsp_property == ENABLE_LOGS)) { dev_err(d->dev, "invalid input !! not readable\n"); @@ -648,6 +650,9 @@ static ssize_t adsp_control_write(struct file *file, if (tx_param == 1) { err = kstrtouint(id, 10, &tx_param_id); + if (err) + return -EINVAL; + tx_data = (tx_param_id << 8) | dsp_property; } diff --git a/sound/soc/intel/skylake/skl-probe.c b/sound/soc/intel/skylake/skl-probe.c index 9c4d96ecc762..b4f5fe4220cf 100644 --- a/sound/soc/intel/skylake/skl-probe.c +++ b/sound/soc/intel/skylake/skl-probe.c @@ -23,10 +23,13 @@ #include #include #include +#include "../common/sst-dsp.h" +#include "../common/sst-dsp-priv.h" #include "skl.h" #include "skl-topology.h" #include "skl-sst-ipc.h" #include "skl-compress.h" +#include "skl-probe.h" #define USE_SPIB 0 @@ -199,7 +202,7 @@ int skl_probe_compr_ack(struct snd_compr_stream *substream, size_t bytes, { struct hdac_ext_bus *ebus = dev_get_drvdata(dai->dev); struct hdac_bus *bus = ebus_to_hbus(ebus); - u64 new_spib_pos; + u64 __maybe_unused new_spib_pos; struct snd_compr_runtime *runtime = substream->runtime; u64 spib_pos = div64_u64(runtime->total_bytes_available, runtime->buffer_size); diff --git a/sound/soc/intel/skylake/skl.c b/sound/soc/intel/skylake/skl.c index e4eb58c7a747..41f0d77f470b 100644 --- a/sound/soc/intel/skylake/skl.c +++ b/sound/soc/intel/skylake/skl.c @@ -366,7 +366,7 @@ static int skl_resume(struct device *dev) struct hdac_bus *bus = pci_get_drvdata(pci); struct skl *skl = bus_to_skl(bus); struct hdac_ext_link *hlink = NULL; - int ret; + int ret = 0; /* Turned OFF in HDMI codec driver after codec reconfiguration */ if (IS_ENABLED(CONFIG_SND_SOC_HDAC_HDMI)) { From fea5a92e87f99a17bb071aef5322088071d64519 Mon Sep 17 00:00:00 2001 From: Jaikrishna Nemallapudi Date: Wed, 22 Mar 2017 19:16:23 +0530 Subject: [PATCH 0704/1103] ASoC: Intel: Fix Compilation issues for probe compress APIs Move the inline to header file. Fixes: 202dcc0("ASoC: Intel: Add Probe compress APIs") Signed-off-by: Jaikrishna Nemallapudi Cc: Divya Prakash Cc: Babu, Ramesh Signed-off-by: Guneshwor Singh --- sound/soc/intel/skylake/skl-compress.c | 5 ----- sound/soc/intel/skylake/skl-compress.h | 7 +++++-- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/sound/soc/intel/skylake/skl-compress.c b/sound/soc/intel/skylake/skl-compress.c index a0b33e198722..67f2b78812b2 100644 --- a/sound/soc/intel/skylake/skl-compress.c +++ b/sound/soc/intel/skylake/skl-compress.c @@ -26,11 +26,6 @@ #include #include "skl.h" #include "skl-compress.h" -inline -struct hdac_ext_stream *get_hdac_ext_compr_stream(struct snd_compr_stream *stream) -{ - return stream->runtime->private_data; -} struct hdac_ext_bus *get_bus_compr_ctx(struct snd_compr_stream *substream) { diff --git a/sound/soc/intel/skylake/skl-compress.h b/sound/soc/intel/skylake/skl-compress.h index 2db347369c0c..abfff2d27f14 100644 --- a/sound/soc/intel/skylake/skl-compress.h +++ b/sound/soc/intel/skylake/skl-compress.h @@ -20,8 +20,6 @@ */ #ifndef __SKL_COMPRESS_H__ #define __SKL_COMPRESS_H__ -inline -struct hdac_ext_stream *get_hdac_ext_compr_stream(struct snd_compr_stream *stream); struct hdac_ext_bus *get_bus_compr_ctx(struct snd_compr_stream *substream); void skl_set_compr_runtime_buffer(struct snd_compr_stream *substream, struct snd_dma_buffer *bufp, size_t size); @@ -33,4 +31,9 @@ int skl_substream_alloc_compr_pages(struct hdac_ext_bus *ebus, int skl_compr_free_pages(struct snd_compr_stream *substream); int skl_substream_free_compr_pages(struct hdac_bus *bus, struct snd_compr_stream *substream); +static inline struct hdac_ext_stream +*get_hdac_ext_compr_stream(struct snd_compr_stream *stream) +{ + return stream->runtime->private_data; +} #endif /* __SKL_COMPRESS_H__*/ From b8898b10aff243a63e637efc3981513a797f5735 Mon Sep 17 00:00:00 2001 From: Sanyog Kale Date: Tue, 13 Dec 2016 11:17:20 +0530 Subject: [PATCH 0705/1103] ASoC: Intel: Kconfig and Makefile changes for SoundWire This patch selects SoundWire Bus and SoundWire Master driver when SND_SOC_INTEL_SKYLAKE config is selected. Change-Id: I8f59f930a2c0089663e7976e354a72c78be8e03b Signed-off-by: Sanyog Kale Reviewed-on: Reviewed-by: Diwakar, Praveen Reviewed-by: Koul, Vinod Reviewed-by: Nemallapudi, JaikrishnaX Reviewed-by: Singh, Guneshwor O Reviewed-by: Kp, Jeeja Tested-by: Avati, Santosh Kumar --- sound/soc/intel/Kconfig | 2 ++ sound/soc/intel/skylake/Makefile | 4 +--- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/sound/soc/intel/Kconfig b/sound/soc/intel/Kconfig index 2f56dbfea444..487e4445f6a0 100644 --- a/sound/soc/intel/Kconfig +++ b/sound/soc/intel/Kconfig @@ -113,6 +113,8 @@ config SND_SOC_INTEL_SKYLAKE select SND_SOC_INTEL_SST select SND_SOC_COMPRESS select SND_SOC_ACPI_INTEL_MATCH + select SDW + select SDW_CNL help If you have a Intel Skylake/Broxton/ApolloLake/KabyLake/ GeminiLake or CannonLake platform with the DSP enabled in the BIOS diff --git a/sound/soc/intel/skylake/Makefile b/sound/soc/intel/skylake/Makefile index 7b8cf119aeae..f36c0bb6e6c2 100644 --- a/sound/soc/intel/skylake/Makefile +++ b/sound/soc/intel/skylake/Makefile @@ -13,9 +13,7 @@ snd-soc-skl-ipc-objs := skl-sst-ipc.o skl-sst-dsp.o cnl-sst-dsp.o \ skl-sst-cldma.o skl-sst.o bxt-sst.o cnl-sst.o \ skl-sst-utils.o skl-fwlog.o -ifdef CONFIG_SDW - snd-soc-skl-ipc-objs += cnl-acpi.o -endif + snd-soc-skl-ipc-objs += cnl-acpi.o obj-$(CONFIG_SND_SOC_INTEL_SKYLAKE) += snd-soc-skl-ipc.o From 359c5e273070f86aa7a93358cc31fda5900aba50 Mon Sep 17 00:00:00 2001 From: Guneshwor Singh Date: Wed, 23 Nov 2016 19:05:46 +0530 Subject: [PATCH 0706/1103] ASoC: Intel: Boards: Add CNL RT274 I2S machine driver Add the CNL I2S machine driver using Realtek ALC274 codec in I2S mode. Change-Id: Ife808f52d69e73a8156130c446a3ab0602fff63d Signed-off-by: Guneshwor Singh Reviewed-on: Reviewed-by: Kale, Sanyog R Reviewed-by: Kp, Jeeja Tested-by: Avati, Santosh Kumar --- sound/soc/intel/boards/Kconfig | 11 + sound/soc/intel/boards/Makefile | 2 + sound/soc/intel/boards/cnl_rt274.c | 385 +++++++++++++++++++++++++++++ sound/soc/intel/skylake/skl.c | 2 +- 4 files changed, 399 insertions(+), 1 deletion(-) create mode 100644 sound/soc/intel/boards/cnl_rt274.c diff --git a/sound/soc/intel/boards/Kconfig b/sound/soc/intel/boards/Kconfig index e6b065fa1dff..69e1081966a8 100644 --- a/sound/soc/intel/boards/Kconfig +++ b/sound/soc/intel/boards/Kconfig @@ -328,6 +328,17 @@ config SND_SOC_INTEL_CNL_SVFPGA_MACH Say Y or m if you have such a device. This is a recommended option. If unsure select "N". +config SND_SOC_INTEL_CNL_RT274_MACH + tristate "Cannonlake with RT274 I2S mode" + depends on MFD_INTEL_LPSS && I2C && ACPI + select SND_SOC_RT274 + select SND_SOC_DMIC + select SND_SOC_HDAC_HDMI + help + This adds support for ASoC machine driver for Cannonlake platform + with RT274 I2S audio codec. + Say Y or m if you have such a device. This is a recommended option. + If unsure select "N". endif ## SND_SOC_INTEL_SKYLAKE endif ## SND_SOC_INTEL_MACH diff --git a/sound/soc/intel/boards/Makefile b/sound/soc/intel/boards/Makefile index c5e0ff065610..72dfb2ccdb61 100644 --- a/sound/soc/intel/boards/Makefile +++ b/sound/soc/intel/boards/Makefile @@ -22,6 +22,7 @@ snd-soc-kbl_rt5663_rt5514_max98927-objs := kbl_rt5663_rt5514_max98927.o snd-soc-skl_rt286-objs := skl_rt286.o snd-skl_nau88l25_max98357a-objs := skl_nau88l25_max98357a.o snd-soc-skl_nau88l25_ssm4567-objs := skl_nau88l25_ssm4567.o +snd-soc-cnl-rt274-objs := cnl_rt274.o snd-soc-cnl_cs42l42-objs := cnl_cs42l42.o snd-soc-cnl_rt700-objs := cnl_rt700.o snd-soc-cnl_svfpga-objs := cnl_svfpga.o @@ -49,6 +50,7 @@ obj-$(CONFIG_SND_SOC_INTEL_KBL_RT5663_RT5514_MAX98927_MACH) += snd-soc-kbl_rt566 obj-$(CONFIG_SND_SOC_INTEL_SKL_RT286_MACH) += snd-soc-skl_rt286.o obj-$(CONFIG_SND_SOC_INTEL_SKL_NAU88L25_MAX98357A_MACH) += snd-skl_nau88l25_max98357a.o obj-$(CONFIG_SND_SOC_INTEL_SKL_NAU88L25_SSM4567_MACH) += snd-soc-skl_nau88l25_ssm4567.o +obj-$(CONFIG_SND_SOC_INTEL_CNL_RT274_MACH) += snd-soc-cnl-rt274.o obj-$(CONFIG_SND_SOC_INTEL_CNL_CS42L42_MACH) += snd-soc-cnl_cs42l42.o obj-$(CONFIG_SND_SOC_INTEL_CNL_RT700_MACH) += snd-soc-cnl_rt700.o obj-$(CONFIG_SND_SOC_INTEL_CNL_SVFPGA_MACH) += snd-soc-cnl_svfpga.o diff --git a/sound/soc/intel/boards/cnl_rt274.c b/sound/soc/intel/boards/cnl_rt274.c new file mode 100644 index 000000000000..1b788f8e59a2 --- /dev/null +++ b/sound/soc/intel/boards/cnl_rt274.c @@ -0,0 +1,385 @@ +/* + * cnl_rt274.c - ASOC Machine driver for CNL + * + * Copyright (C) 2016 Intel Corp + * Author: Guneshwor Singh + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * 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; version 2 of the License. + * + * 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. + * + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../../codecs/rt274.h" + +static struct snd_soc_jack cnl_headset; + +/* Headset jack detection DAPM pins */ +static struct snd_soc_jack_pin cnl_headset_pins[] = { + { + .pin = "Mic Jack", + .mask = SND_JACK_MICROPHONE, + }, + { + .pin = "Headphone Jack", + .mask = SND_JACK_HEADPHONE, + }, +}; + +static const struct snd_kcontrol_new cnl_controls[] = { + SOC_DAPM_PIN_SWITCH("Headphone Jack"), + SOC_DAPM_PIN_SWITCH("Mic Jack"), +}; + +static const struct snd_soc_dapm_widget cnl_rt274_widgets[] = { + SND_SOC_DAPM_HP("Headphone Jack", NULL), + SND_SOC_DAPM_MIC("Mic Jack", NULL), + SND_SOC_DAPM_MIC("SoC DMIC", NULL), +}; + +static int cnl_dmic_fixup(struct snd_soc_pcm_runtime *rtd, + struct snd_pcm_hw_params *params) +{ + struct snd_interval *channels = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_CHANNELS); + channels->min = channels->max = 2; + + return 0; +} + +static const struct snd_soc_dapm_route cnl_map[] = { + {"Headphone Jack", NULL, "HPO Pin"}, + {"MIC", NULL, "Mic Jack"}, + {"DMic", NULL, "SoC DMIC"}, + {"DMIC01 Rx", NULL, "Capture"}, + {"dmic01_hifi", NULL, "DMIC01 Rx"}, + + /* ssp2 path */ + {"Dummy Playback", NULL, "ssp2 Tx"}, + {"ssp2 Tx", NULL, "ssp2_out"}, + + {"ssp2 Rx", NULL, "Dummy Capture"}, + {"ssp2_in", NULL, "ssp2 Rx"}, + + /* ssp1 path */ + {"Dummy Playback", NULL, "ssp1 Tx"}, + {"ssp1 Tx", NULL, "ssp1_out"}, + + {"AIF1 Playback", NULL, "ssp0 Tx"}, + {"ssp0 Tx", NULL, "codec1_out"}, + {"ssp0 Tx", NULL, "codec0_out"}, + + {"ssp0 Rx", NULL, "AIF1 Capture"}, + {"codec0_in", NULL, "ssp0 Rx"}, +}; + +static int cnl_rt274_init(struct snd_soc_pcm_runtime *runtime) +{ + int ret; + struct snd_soc_codec *codec = runtime->codec; + struct snd_soc_card *card = runtime->card; + struct snd_soc_dai *codec_dai = runtime->codec_dai; + + ret = snd_soc_card_jack_new(runtime->card, "Headset", + SND_JACK_HEADSET, &cnl_headset, + cnl_headset_pins, ARRAY_SIZE(cnl_headset_pins)); + + if (ret) + return ret; + + snd_soc_codec_set_jack(codec, &cnl_headset, NULL); + + /* TDM 4 slots 24 bit, set Rx & Tx bitmask to 4 active slots */ + ret = snd_soc_dai_set_tdm_slot(codec_dai, 0xF, 0xF, 4, 24); + if (ret < 0) { + dev_err(runtime->dev, "can't set codec pcm format %d\n", ret); + return ret; + } + + card->dapm.idle_bias_off = true; + + return 0; +} + +static unsigned int rates_supported[] = { + 48000, + 32000, + 24000, + 16000, + 8000, +}; + +static struct snd_pcm_hw_constraint_list rate_constraints = { + .count = ARRAY_SIZE(rates_supported), + .list = rates_supported, +}; + +static int cnl_fe_startup(struct snd_pcm_substream *substream) +{ + return snd_pcm_hw_constraint_list(substream->runtime, 0, + SNDRV_PCM_HW_PARAM_RATE, + &rate_constraints); +} + +static struct snd_soc_ops cnl_fe_ops = { + .startup = cnl_fe_startup, +}; + +static int cnl_be_fixup(struct snd_soc_pcm_runtime *rtd, + struct snd_pcm_hw_params *params) +{ + struct snd_interval *rate = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_RATE); + struct snd_interval *channels = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_CHANNELS); + + rate->min = rate->max = 48000; + channels->min = channels->max = 2; + snd_mask_none(hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT)); + snd_mask_set(hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT), + (unsigned int __force)SNDRV_PCM_FORMAT_S24_LE); + + return 0; +} + +#define CNL_FREQ_OUT 19200000 + +static int rt274_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *codec_dai = rtd->codec_dai; + int ret, ratio = 100; + + snd_soc_dai_set_bclk_ratio(codec_dai, ratio); + + ret = snd_soc_dai_set_pll(codec_dai, 0, RT274_PLL2_S_BCLK, + ratio * params_rate(params), CNL_FREQ_OUT); + if (ret != 0) { + dev_err(rtd->dev, "Failed to enable PLL2 with Ref Clock Loop: %d\n", ret); + return ret; + } + + ret = snd_soc_dai_set_sysclk(codec_dai, RT274_SCLK_S_PLL2, CNL_FREQ_OUT, + SND_SOC_CLOCK_IN); + if (ret < 0) + dev_err(rtd->dev, "set codec sysclk failed: %d\n", ret); + + return ret; +} + +static struct snd_soc_ops rt274_ops = { + .hw_params = rt274_hw_params, +}; + +#if IS_ENABLED(CONFIG_SND_SOC_INTEL_CNL_FPGA) +static const char pname[] = "0000:02:18.0"; +static const char cname[] = "rt274.0-001c"; +#else +static const char pname[] = "0000:00:1f.3"; +static const char cname[] = "i2c-INT34C2:00"; +#endif + +static struct snd_soc_dai_link cnl_rt274_msic_dailink[] = { + { + .name = "CNL Audio Port", + .stream_name = "Audio", + .cpu_dai_name = "System Pin", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .platform_name = pname, + .ignore_suspend = 1, + .nonatomic = 1, + .dynamic = 1, + .dpcm_playback = 1, + .dpcm_capture = 1, + .ops = &cnl_fe_ops, + }, + { + .name = "CNL Deepbuffer Port", + .stream_name = "Deep Buffer Audio", + .cpu_dai_name = "Deepbuffer Pin", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .platform_name = pname, + .dpcm_playback = 1, + .ignore_suspend = 1, + .nonatomic = 1, + .dynamic = 1, + .ops = &cnl_fe_ops, + }, + { + .name = "CNL Reference Port", + .stream_name = "Reference Capture", + .cpu_dai_name = "Reference Pin", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .platform_name = pname, + .dpcm_capture = 1, + .ignore_suspend = 1, + .nonatomic = 1, + .dynamic = 1, + }, + /* Trace Buffer DAI links */ + { + .name = "CNL Trace Buffer0", + .stream_name = "Core 0 Trace Buffer", + .cpu_dai_name = "TraceBuffer0 Pin", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .platform_name = pname, + .capture_only = true, + .ignore_suspend = 1, + }, + { + .name = "CNL Trace Buffer1", + .stream_name = "Core 1 Trace Buffer", + .cpu_dai_name = "TraceBuffer1 Pin", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .platform_name = pname, + .capture_only = true, + .ignore_suspend = 1, + }, + { + .name = "CNL Trace Buffer2", + .stream_name = "Core 2 Trace Buffer", + .cpu_dai_name = "TraceBuffer2 Pin", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .platform_name = pname, + .capture_only = true, + .ignore_suspend = 1, + }, + { + .name = "CNL Trace Buffer3", + .stream_name = "Core 3 Trace Buffer", + .cpu_dai_name = "TraceBuffer3 Pin", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .platform_name = pname, + .capture_only = true, + .ignore_suspend = 1, + }, + /* Probe DAI-links */ + { + .name = "CNL Compress Probe playback", + .stream_name = "Probe Playback", + .cpu_dai_name = "Compress Probe0 Pin", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .platform_name = pname, + .init = NULL, + .ignore_suspend = 1, + .nonatomic = 1, + }, + { + .name = "CNL Compress Probe capture", + .stream_name = "Probe Capture", + .cpu_dai_name = "Compress Probe1 Pin", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .platform_name = pname, + .init = NULL, + .ignore_suspend = 1, + .nonatomic = 1, + }, + /* back ends */ + { + .name = "SSP0-Codec", + .id = 1, + .cpu_dai_name = "SSP0 Pin", + .codec_name = cname, + .codec_dai_name = "rt274-aif1", + .platform_name = pname, + .be_hw_params_fixup = cnl_be_fixup, + .ignore_suspend = 1, + .no_pcm = 1, + .dai_fmt = SND_SOC_DAIFMT_DSP_A | + SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS, + .dpcm_playback = 1, + .dpcm_capture = 1, + .init = cnl_rt274_init, + .ops = &rt274_ops, + }, + { + .name = "SSP1-Codec", + .id = 2, + .cpu_dai_name = "SSP1 Pin", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .platform_name = pname, + .be_hw_params_fixup = cnl_be_fixup, + .ignore_suspend = 1, + .no_pcm = 1, + .dpcm_playback = 1, + }, + { + .name = "dmic01", + .id = 3, + .cpu_dai_name = "DMIC01 Pin", + .codec_name = "dmic-codec", + .codec_dai_name = "dmic-hifi", + .platform_name = pname, + .ignore_suspend = 1, + .no_pcm = 1, + .dpcm_capture = 1, + .be_hw_params_fixup = cnl_dmic_fixup, + }, +}; + +/* SoC card */ +static struct snd_soc_card snd_soc_card_cnl = { + .name = "cnl-audio", + .dai_link = cnl_rt274_msic_dailink, + .num_links = ARRAY_SIZE(cnl_rt274_msic_dailink), + .dapm_widgets = cnl_rt274_widgets, + .num_dapm_widgets = ARRAY_SIZE(cnl_rt274_widgets), + .dapm_routes = cnl_map, + .num_dapm_routes = ARRAY_SIZE(cnl_map), + .controls = cnl_controls, + .num_controls = ARRAY_SIZE(cnl_controls), +}; + +static int snd_cnl_rt274_mc_probe(struct platform_device *pdev) +{ + snd_soc_card_cnl.dev = &pdev->dev; + return devm_snd_soc_register_card(&pdev->dev, &snd_soc_card_cnl); +} + +static struct platform_driver snd_cnl_rt274_driver = { + .driver = { + .name = "cnl_rt274", + .pm = &snd_soc_pm_ops, + }, + .probe = snd_cnl_rt274_mc_probe, +}; + +module_platform_driver(snd_cnl_rt274_driver); + +MODULE_AUTHOR("Guneshwor Singh "); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:cnl_rt274"); diff --git a/sound/soc/intel/skylake/skl.c b/sound/soc/intel/skylake/skl.c index 41f0d77f470b..e3965a15e7d2 100644 --- a/sound/soc/intel/skylake/skl.c +++ b/sound/soc/intel/skylake/skl.c @@ -1215,7 +1215,7 @@ static const struct snd_soc_acpi_mach sst_cnl_devdata[] = { {} }; -static struct sst_acpi_mach sst_icl_devdata[] = { +static struct snd_soc_acpi_mach sst_icl_devdata[] = { { "dummy", "icl_wm8281", "intel/dsp_fw_icl.bin", NULL, NULL, NULL }, {} }; From 9c9b2ccea0bc5eddb364120a97fb58afa7097109 Mon Sep 17 00:00:00 2001 From: Dharageswari R Date: Fri, 15 Dec 2006 12:19:15 +0530 Subject: [PATCH 0707/1103] ASoC: Intel: Modify Icelake machine id to use RT274 Icelake features rt274 codec. Hence updating to use the right machine driver Change-Id: Ia60530a67b17b682e9a265150b53e1f2fa7095e2 Signed-off-by: Dharageswari R Reviewed-on: Reviewed-by: Koul, Vinod Reviewed-by: Singh, Guneshwor O Reviewed-by: Kale, Sanyog R Reviewed-by: Diwakar, Praveen Tested-by: Avati, Santosh Kumar --- sound/soc/intel/skylake/skl.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/sound/soc/intel/skylake/skl.c b/sound/soc/intel/skylake/skl.c index e3965a15e7d2..879be20b361a 100644 --- a/sound/soc/intel/skylake/skl.c +++ b/sound/soc/intel/skylake/skl.c @@ -1216,7 +1216,12 @@ static const struct snd_soc_acpi_mach sst_cnl_devdata[] = { }; static struct snd_soc_acpi_mach sst_icl_devdata[] = { - { "dummy", "icl_wm8281", "intel/dsp_fw_icl.bin", NULL, NULL, NULL }, + { + .id = "dummy", + .drv_name = "icl_rt274", + .fw_filename = "intel/dsp_fw_icl.bin", + .pdata = &cnl_pdata, + }, {} }; From ca15784b89244ca526ea9ebd24007374bd17bab9 Mon Sep 17 00:00:00 2001 From: Dharageswari R Date: Wed, 3 Jan 2007 09:21:03 +0530 Subject: [PATCH 0708/1103] ASoC: Intel: board: Add id_table in cnl_rt274 ICL and CNL use the same machine driver with rt274 codec. Hence added id_table to facilitate this. Change-Id: I7484f774c1fc2b1c3c779f0f084585fbf02966a2 Signed-off-by: Dharageswari R Reviewed-on: Reviewed-by: Koul, Vinod Reviewed-by: Singh, Guneshwor O Reviewed-by: Kale, Sanyog R Reviewed-by: Diwakar, Praveen Tested-by: Avati, Santosh Kumar --- sound/soc/intel/boards/cnl_rt274.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/sound/soc/intel/boards/cnl_rt274.c b/sound/soc/intel/boards/cnl_rt274.c index 1b788f8e59a2..20058f9c7fd1 100644 --- a/sound/soc/intel/boards/cnl_rt274.c +++ b/sound/soc/intel/boards/cnl_rt274.c @@ -370,12 +370,19 @@ static int snd_cnl_rt274_mc_probe(struct platform_device *pdev) return devm_snd_soc_register_card(&pdev->dev, &snd_soc_card_cnl); } +static const struct platform_device_id cnl_board_ids[] = { + { .name = "cnl_rt274" }, + { .name = "icl_rt274" }, + { } +}; + static struct platform_driver snd_cnl_rt274_driver = { .driver = { .name = "cnl_rt274", .pm = &snd_soc_pm_ops, }, .probe = snd_cnl_rt274_mc_probe, + .id_table = cnl_board_ids, }; module_platform_driver(snd_cnl_rt274_driver); @@ -383,3 +390,4 @@ module_platform_driver(snd_cnl_rt274_driver); MODULE_AUTHOR("Guneshwor Singh "); MODULE_LICENSE("GPL v2"); MODULE_ALIAS("platform:cnl_rt274"); +MODULE_ALIAS("platform:icl_rt274"); From 92ff7a12c84d66be64885f2025252fbc234cad9b Mon Sep 17 00:00:00 2001 From: Mousami Jana Date: Mon, 19 Dec 2016 12:00:13 +0530 Subject: [PATCH 0709/1103] ASoC: Intel: Skylake: Support for all rates from 8K to 192K Add support for all sample rates in FE pipelines which are required for IVI usecases. Platform capabilities have been changed to support all rates via SNDRV_PCM_RATE_KNOT. Supported rates are validated against a constraint list. The list contains the rates supported by the DSP FW's rate conversion algorithm. Change-Id: I28f275e0f0a9f2a497d8d0a9ab9b2a5b2b067e46 Signed-off-by: Mousumi Jana Signed-off-by: Yadav, PramodX K Reviewed-on: Reviewed-by: Babu, Ramesh Tested-by: Babu, Ramesh Reviewed-on: Reviewed-by: R, Dharageswari Reviewed-by: Kp, Jeeja Tested-by: Avati, Santosh Kumar --- sound/soc/intel/skylake/skl-pcm.c | 37 +++++++++++++++++++++++++++---- 1 file changed, 33 insertions(+), 4 deletions(-) diff --git a/sound/soc/intel/skylake/skl-pcm.c b/sound/soc/intel/skylake/skl-pcm.c index ac1eef7d2b41..a6901c223652 100644 --- a/sound/soc/intel/skylake/skl-pcm.c +++ b/sound/soc/intel/skylake/skl-pcm.c @@ -50,9 +50,8 @@ static const struct snd_pcm_hardware azx_pcm_hw = { SNDRV_PCM_INFO_NO_STATUS_MMAP), .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE | - SNDRV_PCM_FMTBIT_S24_LE, - .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_16000 | - SNDRV_PCM_RATE_8000, + SNDRV_PCM_FMTBIT_S24_LE, /* TODO Add constraints to other machine drivers */ + .rates = SNDRV_PCM_RATE_8000_192000 | SNDRV_PCM_RATE_KNOT, .rate_min = 8000, .rate_max = 48000, .channels_min = 1, @@ -124,6 +123,31 @@ static enum hdac_ext_stream_type skl_get_host_stream_type(struct hdac_bus *bus) return HDAC_EXT_STREAM_TYPE_COUPLED; } +static unsigned int rates[] = { + 8000, + 11025, + 12000, + 16000, + 22050, + 24000, + 32000, + 44100, + 48000, + 64000, + 88200, + 96000, + 128000, + 176400, + 192000, +}; + +static struct snd_pcm_hw_constraint_list hw_rates = { + .count = ARRAY_SIZE(rates), + .list = rates, + .mask = 0, +}; + + /* * check if the stream opened is marked as ignore_suspend by machine, if so * then enable suspend_active refcount @@ -246,6 +270,11 @@ static int skl_pcm_open(struct snd_pcm_substream *substream, skl_set_pcm_constrains(bus, runtime); + snd_pcm_hw_constraint_list(runtime, 0, + SNDRV_PCM_HW_PARAM_RATE, + &hw_rates); + + /* * disable WALLCLOCK timestamps for capture streams * until we figure out how to handle digital inputs @@ -974,7 +1003,7 @@ static struct snd_soc_dai_driver skl_fe_dai[] = { .stream_name = "System Playback", .channels_min = HDA_MONO, .channels_max = HDA_STEREO, - .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_8000, + .rates = SNDRV_PCM_RATE_8000_192000 | SNDRV_PCM_RATE_KNOT, .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE, .sig_bits = 32, From e3ceb5c312de26d567ae0f90ff6ddbcd408bd53b Mon Sep 17 00:00:00 2001 From: bardliao Date: Tue, 10 Jan 2017 10:54:11 +0530 Subject: [PATCH 0710/1103] ASoc: rt700: Fix for first playback and capture no audio issue This patch fixes no audio issue observed with first playback and capture after booting the platform with power disconnect/connect. In case for playback the issue was randomly observed and in case of capture, the issue was always reproducible. Change-Id: I0d91afa26a83a25295ab0b8faa1221caa32c80dc Signed-off-by: bardliao Signed-off-by: Sanyog Kale Reviewed-on: Reviewed-by: Diwakar, Praveen Tested-by: Avati, Santosh Kumar --- sound/soc/codecs/rt700.c | 171 ++++++++++++++++++++++++--------------- 1 file changed, 107 insertions(+), 64 deletions(-) diff --git a/sound/soc/codecs/rt700.c b/sound/soc/codecs/rt700.c index 773e77bc7ed6..400f7bbc6245 100644 --- a/sound/soc/codecs/rt700.c +++ b/sound/soc/codecs/rt700.c @@ -623,16 +623,41 @@ int rt700_jack_detect(struct rt700_priv *rt700, bool *hp, bool *mic) } EXPORT_SYMBOL(rt700_jack_detect); +static void rt700_get_gain(struct rt700_priv *rt700, unsigned int addr_h, + unsigned int addr_l, unsigned int val_h, + unsigned int *r_val, unsigned int *l_val) +{ + /* R Channel */ + regmap_write(rt700->regmap, addr_h, val_h); + pr_debug("%s write %04x %02x\n", __func__, addr_h, val_h); + regmap_write(rt700->regmap, addr_l, 0); + pr_debug("%s write %04x %02x\n", __func__, addr_l, 0); + regmap_read(rt700->regmap, RT700_READ_HDA_0, r_val); + pr_debug("%s read %04x %02x\n", __func__, RT700_READ_HDA_0, *r_val); + + /* L Channel */ + val_h |= 0x20; + regmap_write(rt700->regmap, addr_h, val_h); + pr_debug("%s write %04x %02x\n", __func__, addr_h, val_h); + regmap_write(rt700->regmap, addr_l, 0); + pr_debug("%s write %04x %02x\n", __func__, addr_l, 0); + regmap_read(rt700->regmap, RT700_READ_HDA_0, l_val); + pr_debug("%s read %04x %02x\n", __func__, RT700_READ_HDA_0, *l_val); +} + /* For Verb-Set Amplifier Gain (Verb ID = 3h) */ static int rt700_set_amp_gain_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct snd_soc_dapm_context *dapm = + snd_soc_component_get_dapm(component); struct soc_mixer_control *mc = (struct soc_mixer_control *)kcontrol->private_value; struct rt700_priv *rt700 = snd_soc_component_get_drvdata(component); - unsigned int addr_h, addr_l, val_h, val_l; + unsigned int addr_h, addr_l, val_h, val_ll, val_lr; unsigned int read_ll, read_rl; + int i; /* Can't use update bit function, so read the original value first */ @@ -642,76 +667,107 @@ static int rt700_set_amp_gain_put(struct snd_kcontrol *kcontrol, val_h = 0x80; else /* input */ val_h = 0x0; - /* R Channel */ - regmap_write(rt700->regmap, addr_h, val_h); - pr_debug("%s write %04x %02x\n", __func__, addr_h, val_h); - regmap_write(rt700->regmap, addr_l, 0); - pr_debug("%s write %04x %02x\n", __func__, addr_l, 0); - regmap_read(rt700->regmap, RT700_READ_HDA_0, &read_rl); - pr_debug("%s read %04x %02x\n", __func__, RT700_READ_HDA_0, read_rl); - - /* L Channel */ - val_h |= 0x20; - regmap_write(rt700->regmap, addr_h, val_h); - pr_debug("%s write %04x %02x\n", __func__, addr_h, val_h); - regmap_write(rt700->regmap, addr_l, 0); - pr_debug("%s write %04x %02x\n", __func__, addr_l, 0); - regmap_read(rt700->regmap, RT700_READ_HDA_0, &read_ll); - pr_debug("%s read %04x %02x\n", __func__, RT700_READ_HDA_0, read_ll); + rt700_get_gain(rt700, addr_h, addr_l, val_h, &read_rl, &read_ll); /* Now set value */ addr_h = mc->reg; addr_l = mc->rreg; - /*pr_debug("%s val = %d, %d\n", ucontrol->value.integer.value[0], - ucontrol->value.integer.value[1]);*/ pr_debug("%s val = %d, %d\n", __func__, ucontrol->value.integer.value[0], - ucontrol->value.integer.value[1]); - /* L Channel */ - val_h = (1 << mc->shift) | (1 << 5); + ucontrol->value.integer.value[1]); + /* L Channel */ if (mc->invert) { /* for mute */ - val_l = (mc->max - ucontrol->value.integer.value[0]) << 7; + val_ll = (mc->max - ucontrol->value.integer.value[0]) << 7; /* keep gain */ read_ll = read_ll & 0x7f; - val_l |= read_ll; + val_ll |= read_ll; } else { /* for gain */ - val_l = ((ucontrol->value.integer.value[0]) & mc->max); + val_ll = ((ucontrol->value.integer.value[0]) & 0x7f); + if (val_ll > mc->max) + val_ll = mc->max; /* keep mute status */ read_ll = read_ll & 0x80; - val_l |= read_ll; + val_ll |= read_ll; } - regmap_write(rt700->regmap, addr_h, val_h); - pr_debug("%s write %04x %02x\n", __func__, addr_h, val_h); - regmap_write(rt700->regmap, addr_l, val_l); - pr_debug("%s write %04x %02x\n", __func__, addr_l, val_l); - /* R Channel */ - val_h = (1 << mc->shift) | (1 << 4); - if (mc->invert) { + regmap_write(rt700->regmap, + RT700_SET_AUDIO_POWER_STATE, AC_PWRST_D0); /* for mute */ - val_l = (mc->max - ucontrol->value.integer.value[1]) << 7; + val_lr = (mc->max - ucontrol->value.integer.value[1]) << 7; /* keep gain */ read_rl = read_rl & 0x7f; - val_l |= read_rl; + val_lr |= read_rl; } else { /* for gain */ - val_l = ((ucontrol->value.integer.value[1]) & mc->max); + val_lr = ((ucontrol->value.integer.value[1]) & 0x7f); + if (val_lr > mc->max) + val_lr = mc->max; /* keep mute status */ read_rl = read_rl & 0x80; - val_l |= read_rl; + val_lr |= read_rl; } - val_h = (1 << mc->shift) | (1 << 4); - regmap_write(rt700->regmap, addr_h, val_h); - pr_debug("%s write %04x %02x\n", __func__, addr_h, val_h); - regmap_write(rt700->regmap, addr_l, val_l); - pr_debug("%s write %04x %02x\n", __func__, addr_l, val_l); + for (i = 0; i < 3; i++) { /* retry 3 times at most */ + pr_debug("%s i=%d\n", __func__, i); + addr_h = mc->reg; + addr_l = mc->rreg; + if (val_ll == val_lr) { + /* Set both L/R channels at the same time */ + val_h = (1 << mc->shift) | (3 << 4); + regmap_write(rt700->regmap, addr_h, val_h); + pr_debug("%s write %04x %02x\n", + __func__, addr_h, val_h); + regmap_write(rt700->regmap, addr_l, val_ll); + pr_debug("%s write %04x %02x\n", + __func__, addr_l, val_ll); + + } else { + /* Lch*/ + val_h = (1 << mc->shift) | (1 << 5); + regmap_write(rt700->regmap, addr_h, val_h); + pr_debug("%s write %04x %02x\n", + __func__, addr_h, val_h); + regmap_write(rt700->regmap, addr_l, val_ll); + pr_debug("%s write %04x %02x\n", + __func__, addr_l, val_ll); + + /* Rch */ + val_h = (1 << mc->shift) | (1 << 4); + regmap_write(rt700->regmap, addr_h, val_h); + pr_debug("%s write %04x %02x\n", + __func__, addr_h, val_h); + regmap_write(rt700->regmap, addr_l, val_lr); + pr_debug("%s write %04x %02x\n", + __func__, addr_l, val_lr); + + } + /* check result */ + addr_h = (mc->reg + 0x2000) | 0x800; + addr_l = (mc->rreg + 0x2000) | 0x800; + if (mc->shift == RT700_DIR_OUT_SFT) /* output */ + val_h = 0x80; + else /* input */ + val_h = 0x0; + + rt700_get_gain(rt700, addr_h, addr_l, val_h, + &read_rl, &read_ll); + if (read_rl == val_lr && read_ll == val_ll) { + pr_debug("write command successful\n"); + break; + } + + pr_warn("write command unsuccessful, retry\n"); + } + + if (dapm->bias_level <= SND_SOC_BIAS_STANDBY) + regmap_write(rt700->regmap, + RT700_SET_AUDIO_POWER_STATE, AC_PWRST_D3); return 0; } @@ -731,22 +787,8 @@ static int rt700_set_amp_gain_get(struct snd_kcontrol *kcontrol, val_h = 0x80; else /* input */ val_h = 0x0; - /* R Channel */ - regmap_write(rt700->regmap, addr_h, val_h); - pr_debug("%s write %04x %02x\n", __func__, addr_h, val_h); - regmap_write(rt700->regmap, addr_l, 0); - pr_debug("%s write %04x %02x\n", __func__, addr_l, 0); - regmap_read(rt700->regmap, RT700_READ_HDA_0, &read_rl); - pr_debug("%s read %04x %02x\n", __func__, RT700_READ_HDA_0, read_rl); - /* L Channel */ - val_h |= 0x20; - regmap_write(rt700->regmap, addr_h, val_h); - pr_debug("%s write %04x %02x\n", __func__, addr_h, val_h); - regmap_write(rt700->regmap, addr_l, 0); - pr_debug("%s write %04x %02x\n", __func__, addr_l, 0); - regmap_read(rt700->regmap, RT700_READ_HDA_0, &read_ll); - pr_debug("%s read %04x %02x\n", __func__, RT700_READ_HDA_0, read_ll); + rt700_get_gain(rt700, addr_h, addr_l, val_h, &read_rl, &read_ll); if (mc->invert) { /* for mute status */ @@ -957,10 +999,14 @@ static int rt700_set_bias_level(struct snd_soc_component *component, { struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component); + struct rt700_priv *rt700 = snd_soc_codec_get_drvdata(codec); + unsigned int sdw_data_0; + pr_debug("%s level=%d\n", __func__, level); switch (level) { case SND_SOC_BIAS_PREPARE: if (SND_SOC_BIAS_STANDBY == dapm->bias_level) { + pm_runtime_get_sync(&rt700->sdw->mstr->dev); snd_soc_component_write(component, RT700_SET_AUDIO_POWER_STATE, AC_PWRST_D0); } @@ -969,6 +1015,7 @@ static int rt700_set_bias_level(struct snd_soc_component *component, case SND_SOC_BIAS_STANDBY: snd_soc_component_write(component, RT700_SET_AUDIO_POWER_STATE, AC_PWRST_D3); + pm_runtime_put_sync_autosuspend(&rt700->sdw->mstr->dev); break; default: @@ -1379,12 +1426,6 @@ static ssize_t rt700_hda_cmd_store(struct device *dev, sdw_data_3, sdw_data_2, sdw_data_1, sdw_data_0); } - /* Enable Jack Detection */ - regmap_write(rt700->regmap, RT700_SET_MIC2_UNSOLICITED_ENABLE, 0x82); - regmap_write(rt700->regmap, RT700_SET_HP_UNSOLICITED_ENABLE, 0x81); - rt700_index_write(rt700->regmap, 0x10, 0x2420); - rt700_index_write(rt700->regmap, 0x19, 0x2e11); - return count; } @@ -1518,6 +1559,9 @@ int rt700_probe(struct device *dev, struct regmap *regmap, &soc_component_dev_rt700, rt700_dai, ARRAY_SIZE(rt700_dai)); dev_info(&slave->dev, "%s\n", __func__); + /* Enable clock before setting */ + pm_runtime_get_sync(&rt700->sdw->mstr->dev); + /* Set Tx route */ /* Filter 02: index 91[13:12] 07[3] */ /* Filter 03: index 5f[15:14] 07[4] */ @@ -1617,7 +1661,6 @@ int rt700_probe(struct device *dev, struct regmap *regmap, if (ret < 0) return ret; - pm_runtime_get_sync(&rt700->sdw->mstr->dev); pm_runtime_mark_last_busy(&rt700->sdw->mstr->dev); pm_runtime_put_sync_autosuspend(&rt700->sdw->mstr->dev); From 606361a9e88e1d941a1ad97c0875de9f8cdb8822 Mon Sep 17 00:00:00 2001 From: Sanyog Kale Date: Fri, 6 Jan 2017 16:06:23 +0530 Subject: [PATCH 0711/1103] ASoC: Intel: Add SoundWire aggregation support This patch adds aggregation support on Master 1 and Master 2 under CONFIG_SND_SOC_SDW_AGGM1M2 config. It also removes Maxim FPGA codec aggregation related changes. Change-Id: I3ae6755543c4992a2dcddb4ab86ca3c503a9bd36 Signed-off-by: Sanyog Kale Reviewed-on: Reviewed-by: Singh, Guneshwor O Reviewed-by: Diwakar, Praveen Tested-by: Avati, Santosh Kumar Reviewed-by: Nc, Shreyas Reviewed-by: Kp, Jeeja --- sound/soc/intel/skylake/cnl-sst.c | 12 ++++-------- sound/soc/intel/skylake/skl-sdw-pcm.c | 4 ++-- 2 files changed, 6 insertions(+), 10 deletions(-) diff --git a/sound/soc/intel/skylake/cnl-sst.c b/sound/soc/intel/skylake/cnl-sst.c index ae3c6aa5557e..790af2489d0e 100644 --- a/sound/soc/intel/skylake/cnl-sst.c +++ b/sound/soc/intel/skylake/cnl-sst.c @@ -526,11 +526,7 @@ static int skl_register_sdw_masters(struct device *dev, struct skl_sst *dsp, struct cnl_bra_operation *p_ptr = ptr; int ret = 0, i, j, k, wl = 0; /* TODO: This number 4 should come from ACPI */ -#if defined(CONFIG_SDW_MAXIM_SLAVE) || defined(CONFIG_SND_SOC_MXFPGA) - dsp->num_sdw_controllers = 3; -#else dsp->num_sdw_controllers = 4; -#endif master = devm_kzalloc(dev, (sizeof(*master) * dsp->num_sdw_controllers), GFP_KERNEL); @@ -636,18 +632,18 @@ static int skl_register_sdw_masters(struct device *dev, struct skl_sst *dsp, switch (i) { case 0: p_data->sdw_regs = mmio_base + CNL_SDW_LINK_0_BASE; -#ifdef CONFIG_SND_SOC_MXFPGA - master[i].link_sync_mask = 0x1; -#endif break; case 1: p_data->sdw_regs = mmio_base + CNL_SDW_LINK_1_BASE; -#ifdef CONFIG_SND_SOC_MXFPGA +#ifdef CONFIG_SND_SOC_SDW_AGGM1M2 master[i].link_sync_mask = 0x2; #endif break; case 2: p_data->sdw_regs = mmio_base + CNL_SDW_LINK_2_BASE; +#ifdef CONFIG_SND_SOC_SDW_AGGM1M2 + master[i].link_sync_mask = 0x4; +#endif break; case 3: p_data->sdw_regs = mmio_base + CNL_SDW_LINK_3_BASE; diff --git a/sound/soc/intel/skylake/skl-sdw-pcm.c b/sound/soc/intel/skylake/skl-sdw-pcm.c index 564602c0ee12..2c5ba970e34c 100644 --- a/sound/soc/intel/skylake/skl-sdw-pcm.c +++ b/sound/soc/intel/skylake/skl-sdw-pcm.c @@ -47,7 +47,7 @@ struct sdw_dma_data { int mstr_nr; }; -#ifdef CONFIG_SND_SOC_MXFPGA +#ifdef CONFIG_SND_SOC_SDW_AGGM1M2 static char uuid_playback[] = "Agg_p"; static char uuid_capture[] = "Agg_c"; #endif @@ -102,7 +102,7 @@ int cnl_sdw_startup(struct snd_pcm_substream *substream, dma->mstr_nr = sdw_ctrl_nr; snd_soc_dai_set_dma_data(dai, substream, dma); -#ifdef CONFIG_SND_SOC_MXFPGA +#ifdef CONFIG_SND_SOC_SDW_AGGM1M2 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) uuid = uuid_playback; else From b82342d9aea5757a76262efece11e78489eb9f39 Mon Sep 17 00:00:00 2001 From: "R, Dharageswari" Date: Fri, 26 Feb 2016 20:00:57 +0530 Subject: [PATCH 0712/1103] ASoC: Intel: Skylake: Avoid resume capablity for capture streams DMA resume capability is not supported by the HW. Hence this patch avoids resume capablity for capture streams. Change-Id: If3f44facdd746677d8b1021759df996a09b0c024 Signed-off-by: R, Dharageswari Signed-off-by: Pawse, GuruprasadX Reviewed-on: Reviewed-by: Prodduvaka, Leoni Reviewed-by: Nc, Shreyas Reviewed-by: Kp, Jeeja Reviewed-by: Diwakar, Praveen Tested-by: Sm, Bhadur A --- sound/soc/intel/skylake/skl-pcm.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/sound/soc/intel/skylake/skl-pcm.c b/sound/soc/intel/skylake/skl-pcm.c index a6901c223652..a7b8df50aa9b 100644 --- a/sound/soc/intel/skylake/skl-pcm.c +++ b/sound/soc/intel/skylake/skl-pcm.c @@ -536,7 +536,11 @@ static int skl_pcm_trigger(struct snd_pcm_substream *substream, int cmd, switch (cmd) { case SNDRV_PCM_TRIGGER_RESUME: - if (!w->ignore_suspend) { + /* + * DMA resume capablity is not attempted for capture stream + * as it is not supported by HW + */ + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { /* * enable DMA Resume enable bit for the stream, set the * dpib & lpib position to resume before starting the From 35979d2a0271b9056bf8034c6a4e95f9f9728c82 Mon Sep 17 00:00:00 2001 From: "Pawse, GuruprasadX" Date: Wed, 18 Jan 2017 15:53:24 +0530 Subject: [PATCH 0713/1103] ASoC: Intel: Skylake: Support all I2S ports with all possible capabilities This patch adds all possible capabilities that the platform supports. This is the superset of the capabilities that all machines support. Below are the platform capabilities supported: Rates = Support all rates via SNDRV_PCM_RATE_KNOT BE Sample size = S16_LE, S24_LE, S32_LE FE Sample size = S16_LE, S24_LE, S32_LE, FLOAT_LE BE Num Channels = 1 to 8 FE Num Channels = 1 to 8 Change-Id: I975d3d1599505a1a4592160377b0d0ede1548c54 Signed-off-by: Pawse, GuruprasadX Reviewed-on: Reviewed-by: R, Dharageswari Reviewed-by: Diwakar, Praveen Reviewed-by: Nc, Shreyas Reviewed-by: Kp, Jeeja Tested-by: Sm, Bhadur A --- sound/soc/intel/skylake/skl-pcm.c | 129 ++++++++++++++++-------------- 1 file changed, 71 insertions(+), 58 deletions(-) diff --git a/sound/soc/intel/skylake/skl-pcm.c b/sound/soc/intel/skylake/skl-pcm.c index a7b8df50aa9b..4701d9e09647 100644 --- a/sound/soc/intel/skylake/skl-pcm.c +++ b/sound/soc/intel/skylake/skl-pcm.c @@ -35,6 +35,7 @@ #define HDA_MONO 1 #define HDA_STEREO 2 #define HDA_QUAD 4 +#define HDA_8_CH 8 static const struct snd_pcm_hardware azx_pcm_hw = { .info = (SNDRV_PCM_INFO_MMAP | @@ -50,10 +51,11 @@ static const struct snd_pcm_hardware azx_pcm_hw = { SNDRV_PCM_INFO_NO_STATUS_MMAP), .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE | - SNDRV_PCM_FMTBIT_S24_LE, /* TODO Add constraints to other machine drivers */ + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_FLOAT_LE, .rates = SNDRV_PCM_RATE_8000_192000 | SNDRV_PCM_RATE_KNOT, .rate_min = 8000, - .rate_max = 48000, + .rate_max = 192000, .channels_min = 1, .channels_max = 8, .buffer_bytes_max = AZX_MAX_BUF_SIZE, @@ -1006,20 +1008,19 @@ static struct snd_soc_dai_driver skl_fe_dai[] = { .playback = { .stream_name = "System Playback", .channels_min = HDA_MONO, - .channels_max = HDA_STEREO, + .channels_max = HDA_8_CH, .rates = SNDRV_PCM_RATE_8000_192000 | SNDRV_PCM_RATE_KNOT, - .formats = SNDRV_PCM_FMTBIT_S16_LE | - SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE, + .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_FLOAT_LE, .sig_bits = 32, }, .capture = { .stream_name = "System Capture", .channels_min = HDA_MONO, - .channels_max = HDA_STEREO, - .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_32000 | - SNDRV_PCM_RATE_KNOT | - SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_8000, - .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE, + .channels_max = HDA_8_CH, + .rates = SNDRV_PCM_RATE_8000_192000 | SNDRV_PCM_RATE_KNOT, + .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_FLOAT_LE, .sig_bits = 32, }, }, @@ -1172,17 +1173,19 @@ static struct snd_soc_dai_driver skl_platform_dai[] = { .ops = &skl_be_ssp_dai_ops, .playback = { .stream_name = "ssp0 Tx", - .channels_min = HDA_STEREO, - .channels_max = HDA_STEREO, - .rates = SNDRV_PCM_RATE_48000, - .formats = SNDRV_PCM_FMTBIT_S16_LE, + .channels_min = HDA_MONO, + .channels_max = HDA_8_CH, + .rates = SNDRV_PCM_RATE_8000_192000 | SNDRV_PCM_RATE_KNOT, + .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S32_LE, }, .capture = { .stream_name = "ssp0 Rx", - .channels_min = HDA_STEREO, - .channels_max = HDA_STEREO, - .rates = SNDRV_PCM_RATE_48000, - .formats = SNDRV_PCM_FMTBIT_S16_LE, + .channels_min = HDA_MONO, + .channels_max = HDA_8_CH, + .rates = SNDRV_PCM_RATE_8000_192000 | SNDRV_PCM_RATE_KNOT, + .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S32_LE, }, }, { @@ -1190,17 +1193,19 @@ static struct snd_soc_dai_driver skl_platform_dai[] = { .ops = &skl_be_ssp_dai_ops, .playback = { .stream_name = "ssp1 Tx", - .channels_min = HDA_STEREO, - .channels_max = HDA_STEREO, - .rates = SNDRV_PCM_RATE_48000, - .formats = SNDRV_PCM_FMTBIT_S16_LE, + .channels_min = HDA_MONO, + .channels_max = HDA_8_CH, + .rates = SNDRV_PCM_RATE_8000_192000 | SNDRV_PCM_RATE_KNOT, + .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S32_LE, }, .capture = { .stream_name = "ssp1 Rx", - .channels_min = HDA_STEREO, - .channels_max = HDA_STEREO, - .rates = SNDRV_PCM_RATE_48000, - .formats = SNDRV_PCM_FMTBIT_S16_LE, + .channels_min = HDA_MONO, + .channels_max = HDA_8_CH, + .rates = SNDRV_PCM_RATE_8000_192000 | SNDRV_PCM_RATE_KNOT, + .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S32_LE, }, }, { @@ -1208,17 +1213,19 @@ static struct snd_soc_dai_driver skl_platform_dai[] = { .ops = &skl_be_ssp_dai_ops, .playback = { .stream_name = "ssp2 Tx", - .channels_min = HDA_STEREO, - .channels_max = HDA_STEREO, - .rates = SNDRV_PCM_RATE_48000, - .formats = SNDRV_PCM_FMTBIT_S16_LE, + .channels_min = HDA_MONO, + .channels_max = HDA_8_CH, + .rates = SNDRV_PCM_RATE_8000_192000 | SNDRV_PCM_RATE_KNOT, + .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S32_LE, }, .capture = { .stream_name = "ssp2 Rx", - .channels_min = HDA_STEREO, - .channels_max = HDA_STEREO, - .rates = SNDRV_PCM_RATE_48000, - .formats = SNDRV_PCM_FMTBIT_S16_LE, + .channels_min = HDA_MONO, + .channels_max = HDA_8_CH, + .rates = SNDRV_PCM_RATE_8000_192000 | SNDRV_PCM_RATE_KNOT, + .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S32_LE, }, }, { @@ -1226,17 +1233,19 @@ static struct snd_soc_dai_driver skl_platform_dai[] = { .ops = &skl_be_ssp_dai_ops, .playback = { .stream_name = "ssp3 Tx", - .channels_min = HDA_STEREO, - .channels_max = HDA_STEREO, - .rates = SNDRV_PCM_RATE_48000, - .formats = SNDRV_PCM_FMTBIT_S16_LE, + .channels_min = HDA_MONO, + .channels_max = HDA_8_CH, + .rates = SNDRV_PCM_RATE_8000_192000 | SNDRV_PCM_RATE_KNOT, + .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S32_LE, }, .capture = { .stream_name = "ssp3 Rx", - .channels_min = HDA_STEREO, - .channels_max = HDA_STEREO, - .rates = SNDRV_PCM_RATE_48000, - .formats = SNDRV_PCM_FMTBIT_S16_LE, + .channels_min = HDA_MONO, + .channels_max = HDA_8_CH, + .rates = SNDRV_PCM_RATE_8000_192000 | SNDRV_PCM_RATE_KNOT, + .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S32_LE, }, }, { @@ -1244,17 +1253,19 @@ static struct snd_soc_dai_driver skl_platform_dai[] = { .ops = &skl_be_ssp_dai_ops, .playback = { .stream_name = "ssp4 Tx", - .channels_min = HDA_STEREO, - .channels_max = HDA_STEREO, - .rates = SNDRV_PCM_RATE_48000, - .formats = SNDRV_PCM_FMTBIT_S16_LE, + .channels_min = HDA_MONO, + .channels_max = HDA_8_CH, + .rates = SNDRV_PCM_RATE_8000_192000 | SNDRV_PCM_RATE_KNOT, + .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S32_LE, }, .capture = { .stream_name = "ssp4 Rx", - .channels_min = HDA_STEREO, - .channels_max = HDA_STEREO, - .rates = SNDRV_PCM_RATE_48000, - .formats = SNDRV_PCM_FMTBIT_S16_LE, + .channels_min = HDA_MONO, + .channels_max = HDA_8_CH, + .rates = SNDRV_PCM_RATE_8000_192000 | SNDRV_PCM_RATE_KNOT, + .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S32_LE, }, }, { @@ -1262,17 +1273,19 @@ static struct snd_soc_dai_driver skl_platform_dai[] = { .ops = &skl_be_ssp_dai_ops, .playback = { .stream_name = "ssp5 Tx", - .channels_min = HDA_STEREO, - .channels_max = HDA_STEREO, - .rates = SNDRV_PCM_RATE_48000, - .formats = SNDRV_PCM_FMTBIT_S16_LE, + .channels_min = HDA_MONO, + .channels_max = HDA_8_CH, + .rates = SNDRV_PCM_RATE_8000_192000 | SNDRV_PCM_RATE_KNOT, + .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S32_LE, }, .capture = { .stream_name = "ssp5 Rx", - .channels_min = HDA_STEREO, - .channels_max = HDA_STEREO, - .rates = SNDRV_PCM_RATE_48000, - .formats = SNDRV_PCM_FMTBIT_S16_LE, + .channels_min = HDA_MONO, + .channels_max = HDA_8_CH, + .rates = SNDRV_PCM_RATE_8000_192000 | SNDRV_PCM_RATE_KNOT, + .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S32_LE, }, }, { From 70443fe6ea3af700c975a52f66dbf3670af92ccd Mon Sep 17 00:00:00 2001 From: "Pawse, GuruprasadX" Date: Fri, 10 Feb 2017 20:22:42 +0530 Subject: [PATCH 0714/1103] ASoC: Intel: Skylake: Add platform DAI for deepbuffer capture This patch adds platform DAI for deepbuffer capture Change-Id: I2278766ac028e4801ec96a26c28b40c6e5c0e477 Signed-off-by: Pawse, GuruprasadX Reviewed-on: Reviewed-by: R, Dharageswari Reviewed-by: Singh, Guneshwor O Reviewed-by: Nc, Shreyas Reviewed-by: Kp, Jeeja Reviewed-by: Koul, Vinod Tested-by: Sm, Bhadur A --- sound/soc/intel/skylake/skl-pcm.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/sound/soc/intel/skylake/skl-pcm.c b/sound/soc/intel/skylake/skl-pcm.c index 4701d9e09647..1da485053967 100644 --- a/sound/soc/intel/skylake/skl-pcm.c +++ b/sound/soc/intel/skylake/skl-pcm.c @@ -1073,6 +1073,13 @@ static struct snd_soc_dai_driver skl_fe_dai[] = { .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE, .sig_bits = 32, }, + .capture = { + .stream_name = "Deepbuffer Capture", + .channels_min = HDA_STEREO, + .channels_max = HDA_STEREO, + .rates = SNDRV_PCM_RATE_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE, + }, }, { .name = "Compress Probe0 Pin", From a55cea47cc86701899b205c2bd30040db0255954 Mon Sep 17 00:00:00 2001 From: Dharageswari R Date: Sun, 13 Mar 2016 22:22:47 +0530 Subject: [PATCH 0715/1103] ASoC: Intel: board: Enable deepbuffer capture in cnl_rt274 Change-Id: I42a782d7689a90b814214420bc3a3ea8e2ad224e Signed-off-by: Dharageswari R Reviewed-on: Reviewed-by: Kale, Sanyog R Reviewed-by: Diwakar, Praveen Reviewed-by: Singh, Guneshwor O Reviewed-by: Kp, Jeeja Reviewed-by: Koul, Vinod Tested-by: Sm, Bhadur A --- sound/soc/intel/boards/cnl_rt274.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sound/soc/intel/boards/cnl_rt274.c b/sound/soc/intel/boards/cnl_rt274.c index 20058f9c7fd1..eebde84e7700 100644 --- a/sound/soc/intel/boards/cnl_rt274.c +++ b/sound/soc/intel/boards/cnl_rt274.c @@ -226,6 +226,7 @@ static struct snd_soc_dai_link cnl_rt274_msic_dailink[] = { .codec_dai_name = "snd-soc-dummy-dai", .platform_name = pname, .dpcm_playback = 1, + .dpcm_capture = 1, .ignore_suspend = 1, .nonatomic = 1, .dynamic = 1, From bc5472dfabdd6d629337545a6ba4e5a797e7fc54 Mon Sep 17 00:00:00 2001 From: "Pawse, GuruprasadX" Date: Fri, 13 Jan 2017 10:49:09 +0530 Subject: [PATCH 0716/1103] ASoC: Intel: Add Icelake machine id to use RT700 Icelake supports RT700 SoundWire codec as well, hence updating the machine id list. Change-Id: I2ff08cac04d4affcdae92a36fc4b74f352cb624c Signed-off-by: Pawse, GuruprasadX Reviewed-on: Reviewed-by: R, Dharageswari Reviewed-by: Kale, Sanyog R Reviewed-by: Singh, Guneshwor O Reviewed-by: Prodduvaka, Leoni Reviewed-by: Kp, Jeeja Tested-by: Sm, Bhadur A --- sound/soc/intel/skylake/skl.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/sound/soc/intel/skylake/skl.c b/sound/soc/intel/skylake/skl.c index 879be20b361a..9bcd376ec445 100644 --- a/sound/soc/intel/skylake/skl.c +++ b/sound/soc/intel/skylake/skl.c @@ -1216,12 +1216,22 @@ static const struct snd_soc_acpi_mach sst_cnl_devdata[] = { }; static struct snd_soc_acpi_mach sst_icl_devdata[] = { +#if IS_ENABLED(CONFIG_SND_SOC_RT700) + { + .id = "dummy", + .drv_name = "icl_rt700", + .fw_filename = "intel/dsp_fw_icl.bin", + .pdata = &cnl_pdata, + }, +#else { .id = "dummy", .drv_name = "icl_rt274", .fw_filename = "intel/dsp_fw_icl.bin", .pdata = &cnl_pdata, }, + +#endif {} }; From 8d1382ce0f7efaff6f2747619aeb8fe03f97670c Mon Sep 17 00:00:00 2001 From: "Pawse, GuruprasadX" Date: Wed, 11 Jan 2017 18:43:31 +0530 Subject: [PATCH 0717/1103] ASoC: Intel: Add Icelake machine id to use WM8281 Icelake supports WM8281 codec as well, hence updating the machine id list. Change-Id: I7f18b9cc11d06f2d3c535a611b5bd2894b9ab2ad Signed-off-by: Pawse, GuruprasadX Reviewed-on: Reviewed-by: R, Dharageswari Reviewed-by: Diwakar, Praveen Reviewed-by: Kp, Jeeja Tested-by: Sm, Bhadur A --- sound/soc/intel/skylake/skl.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/sound/soc/intel/skylake/skl.c b/sound/soc/intel/skylake/skl.c index 9bcd376ec445..dbff367fb3d0 100644 --- a/sound/soc/intel/skylake/skl.c +++ b/sound/soc/intel/skylake/skl.c @@ -1223,6 +1223,13 @@ static struct snd_soc_acpi_mach sst_icl_devdata[] = { .fw_filename = "intel/dsp_fw_icl.bin", .pdata = &cnl_pdata, }, +#elif IS_ENABLED(CONFIG_SND_SOC_WM5110) + { + .id = "dummy", + .drv_name = "icl_wm8281", + .fw_filename = "intel/dsp_fw_icl.bin", + .pdata = &cnl_pdata, + }, #else { .id = "dummy", From 59c7dadb9b7209ccea58b69b8e296aa335bcaac2 Mon Sep 17 00:00:00 2001 From: Shreyas NC Date: Wed, 15 Feb 2017 06:04:04 +0530 Subject: [PATCH 0718/1103] ASoC: Intel: Skylake: Fix library name length Topology framework allows a maximum string length of 44 bytes. So, fix the library name length to 44 bytes as well. Change-Id: I3df2bb5d6130bc96200565aa59757b9c8eb0119e Signed-off-by: Shreyas NC Reviewed-on: Reviewed-by: Kale, Sanyog R Reviewed-by: Singh, Guneshwor O Reviewed-by: Diwakar, Praveen Tested-by: Sm, Bhadur A --- sound/soc/intel/skylake/skl-sst-ipc.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/intel/skylake/skl-sst-ipc.h b/sound/soc/intel/skylake/skl-sst-ipc.h index 0cde541f900c..6c037d2c8a3c 100644 --- a/sound/soc/intel/skylake/skl-sst-ipc.h +++ b/sound/soc/intel/skylake/skl-sst-ipc.h @@ -72,7 +72,7 @@ struct skl_d0i3_data { struct delayed_work work; }; -#define SKL_LIB_NAME_LENGTH 128 +#define SKL_LIB_NAME_LENGTH 44 #define SKL_MAX_LIB 16 struct skl_lib_info { From bc2b8b0314291e72316c19eb5a9ebb8a9b37ea1a Mon Sep 17 00:00:00 2001 From: Shreyas NC Date: Mon, 23 Jan 2017 05:43:58 +0530 Subject: [PATCH 0719/1103] ASoC: Intel: Skylake: Update SDW BRA interface Change SoundWire BRA interfaces to accommodate updated driver module config structures. Change-Id: I7e2099846389fe106196568a4eb7406385a26099 Signed-off-by: Shreyas NC Reviewed-on: Reviewed-by: Kale, Sanyog R Reviewed-by: Singh, Guneshwor O Reviewed-by: Diwakar, Praveen Tested-by: Sm, Bhadur A Signed-off-by: Guneshwor Singh --- sound/soc/intel/skylake/skl-messages.c | 199 ++++++++++++++++--------- 1 file changed, 131 insertions(+), 68 deletions(-) diff --git a/sound/soc/intel/skylake/skl-messages.c b/sound/soc/intel/skylake/skl-messages.c index dde1aeb11153..495f82fc5d65 100644 --- a/sound/soc/intel/skylake/skl-messages.c +++ b/sound/soc/intel/skylake/skl-messages.c @@ -334,7 +334,24 @@ static int cnl_sdw_bra_pipe_cfg_pb(struct skl_sst *ctx, struct skl_pipe *host_cpr_pipe = NULL; struct skl_pipe_params host_cpr_params; struct skl_module_cfg host_cpr_cfg, link_cpr_cfg; + struct skl_module host_cpr_mod, link_cpr_mod; int ret; + struct skl_module_fmt *in_fmt, *out_fmt; + u8 guid[16] = { 131, 12, 160, 155, 18, 202, 131, + 74, 148, 60, 31, 162, 232, 47, 157, 218 }; + + link_cpr_cfg.module = &link_cpr_mod; + host_cpr_cfg.module = &host_cpr_mod; + + /* + * To get the pvt id, UUID of the module config is + * necessary. Hence hardocde this to the UUID fof copier + * module + */ + memcpy(&host_cpr_cfg.guid, &guid, 16); + memcpy(&link_cpr_cfg.guid, &guid, 16); + in_fmt = &host_cpr_cfg.module->formats[0].inputs[0].fmt; + out_fmt = &host_cpr_cfg.module->formats[0].outputs[0].fmt; /* Playback pipeline */ host_cpr_pipe = kzalloc(sizeof(struct skl_pipe), GFP_KERNEL); @@ -343,6 +360,10 @@ static int cnl_sdw_bra_pipe_cfg_pb(struct skl_sst *ctx, goto error; } + host_cpr_cfg.fmt_idx = 0; + host_cpr_cfg.res_idx = 0; + link_cpr_cfg.fmt_idx = 0; + link_cpr_cfg.res_idx = 0; bra_data->pb_pipe = host_cpr_pipe; host_cpr_pipe->p_params = &host_cpr_params; @@ -371,54 +392,59 @@ static int cnl_sdw_bra_pipe_cfg_pb(struct skl_sst *ctx, host_cpr_cfg.id.module_id = 4; #endif host_cpr_cfg.id.instance_id = 1; - host_cpr_cfg.mcps = 100000; - host_cpr_cfg.mem_pages = 0; - host_cpr_cfg.ibs = 384; - host_cpr_cfg.obs = 384; + host_cpr_cfg.id.pvt_id = skl_get_pvt_id(ctx, + (uuid_le *)host_cpr_cfg.guid, host_cpr_cfg.id.instance_id); + if (host_cpr_cfg.id.pvt_id < 0) + return -EINVAL; + + host_cpr_cfg.module->resources[0].cps = 100000; + host_cpr_cfg.module->resources[0].is_pages = 0; + host_cpr_cfg.module->resources[0].ibs = 384; + host_cpr_cfg.module->resources[0].obs = 384; host_cpr_cfg.core_id = 0; - host_cpr_cfg.max_in_queue = 1; - host_cpr_cfg.max_out_queue = 1; - host_cpr_cfg.is_loadable = 0; + host_cpr_cfg.module->max_input_pins = 1; + host_cpr_cfg.module->max_output_pins = 1; + host_cpr_cfg.module->loadable = 0; host_cpr_cfg.domain = 0; host_cpr_cfg.m_type = SKL_MODULE_TYPE_COPIER; host_cpr_cfg.dev_type = SKL_DEVICE_HDAHOST; host_cpr_cfg.hw_conn_type = SKL_CONN_SOURCE; host_cpr_cfg.formats_config.caps_size = 0; - host_cpr_cfg.dma_buffer_size = 2; + host_cpr_cfg.module->resources[0].dma_buffer_size = 2; host_cpr_cfg.pdi_type = 0; host_cpr_cfg.converter = 0; host_cpr_cfg.vbus_id = 0; host_cpr_cfg.sdw_agg_enable = 0; host_cpr_cfg.formats_config.caps_size = 0; - host_cpr_cfg.in_fmt[0].channels = 1; - host_cpr_cfg.in_fmt[0].s_freq = 96000; - host_cpr_cfg.in_fmt[0].bit_depth = 32; - host_cpr_cfg.in_fmt[0].valid_bit_depth = 24; - host_cpr_cfg.in_fmt[0].ch_cfg = 0; - host_cpr_cfg.in_fmt[0].interleaving_style = 0; - host_cpr_cfg.in_fmt[0].sample_type = 0; - host_cpr_cfg.in_fmt[0].ch_map = 0xFFFFFFF1; - - host_cpr_cfg.out_fmt[0].channels = 1; - host_cpr_cfg.out_fmt[0].s_freq = 96000; - host_cpr_cfg.out_fmt[0].bit_depth = 32; - host_cpr_cfg.out_fmt[0].valid_bit_depth = 24; - host_cpr_cfg.out_fmt[0].ch_cfg = 0; - host_cpr_cfg.out_fmt[0].interleaving_style = 0; - host_cpr_cfg.out_fmt[0].sample_type = 0; - host_cpr_cfg.out_fmt[0].ch_map = 0xFFFFFFF1; - - host_cpr_cfg.m_in_pin = kcalloc(host_cpr_cfg.max_in_queue, - sizeof(host_cpr_cfg.m_in_pin), + in_fmt->channels = 1; + in_fmt->s_freq = 96000; + in_fmt->bit_depth = 32; + in_fmt->valid_bit_depth = 24; + in_fmt->ch_cfg = 0; + in_fmt->interleaving_style = 0; + in_fmt->sample_type = 0; + in_fmt->ch_map = 0xFFFFFFF1; + + out_fmt->channels = 1; + out_fmt->s_freq = 96000; + out_fmt->bit_depth = 32; + out_fmt->valid_bit_depth = 24; + out_fmt->ch_cfg = 0; + out_fmt->interleaving_style = 0; + out_fmt->sample_type = 0; + out_fmt->ch_map = 0xFFFFFFF1; + + host_cpr_cfg.m_in_pin = kcalloc(host_cpr_cfg.module->max_input_pins, + sizeof(*host_cpr_cfg.m_in_pin), GFP_KERNEL); if (!host_cpr_cfg.m_in_pin) { ret = -ENOMEM; goto error; } - host_cpr_cfg.m_out_pin = kcalloc(host_cpr_cfg.max_out_queue, - sizeof(host_cpr_cfg.m_out_pin), + host_cpr_cfg.m_out_pin = kcalloc(host_cpr_cfg.module->max_output_pins, + sizeof(*host_cpr_cfg.m_out_pin), GFP_KERNEL); if (!host_cpr_cfg.m_out_pin) { ret = -ENOMEM; @@ -445,6 +471,11 @@ static int cnl_sdw_bra_pipe_cfg_pb(struct skl_sst *ctx, sizeof(struct skl_module_cfg)); link_cpr_cfg.id.instance_id = 2; + link_cpr_cfg.id.pvt_id = skl_get_pvt_id(ctx, + (uuid_le *)link_cpr_cfg.guid, link_cpr_cfg.id.instance_id); + if (link_cpr_cfg.id.pvt_id < 0) + return -EINVAL; + link_cpr_cfg.dev_type = SKL_DEVICE_SDW; #if IS_ENABLED(CONFIG_SND_SOC_INTEL_CNL_FPGA) link_cpr_cfg.sdw_stream_num = 0x3; @@ -453,16 +484,16 @@ static int cnl_sdw_bra_pipe_cfg_pb(struct skl_sst *ctx, #endif link_cpr_cfg.hw_conn_type = SKL_CONN_SOURCE; - link_cpr_cfg.m_in_pin = kcalloc(link_cpr_cfg.max_in_queue, - sizeof(link_cpr_cfg.m_in_pin), + link_cpr_cfg.m_in_pin = kcalloc(link_cpr_cfg.module->max_input_pins, + sizeof(*link_cpr_cfg.m_in_pin), GFP_KERNEL); if (!link_cpr_cfg.m_in_pin) { ret = -ENOMEM; goto error; } - link_cpr_cfg.m_out_pin = kcalloc(link_cpr_cfg.max_out_queue, - sizeof(link_cpr_cfg.m_out_pin), + link_cpr_cfg.m_out_pin = kcalloc(link_cpr_cfg.module->max_output_pins, + sizeof(*link_cpr_cfg.m_out_pin), GFP_KERNEL); if (!link_cpr_cfg.m_out_pin) { ret = -ENOMEM; @@ -534,8 +565,26 @@ static int cnl_sdw_bra_pipe_cfg_cp(struct skl_sst *ctx, struct bra_conf *bra_data = &ctx->bra_pipe_data[mstr_num]; struct skl_pipe *link_cpr_pipe = NULL; struct skl_pipe_params link_cpr_params; + struct skl_module host_cpr_mod, link_cpr_mod; struct skl_module_cfg link_cpr_cfg, host_cpr_cfg; int ret; + struct skl_module_fmt *in_fmt, *out_fmt; + u8 guid[16] = { 131, 12, 160, 155, 18, 202, 131, + 74, 148, 60, 31, 162, 232, 47, 157, 218 }; + + link_cpr_cfg.module = &link_cpr_mod; + host_cpr_cfg.module = &host_cpr_mod; + + + /* + * To get the pvt id, UUID of the module config is + * necessary. Hence hardocde this to the UUID fof copier + * module + */ + memcpy(&host_cpr_cfg.guid, &guid, 16); + memcpy(&link_cpr_cfg.guid, &guid, 16); + in_fmt = &link_cpr_cfg.module->formats[0].inputs[0].fmt; + out_fmt = &link_cpr_cfg.module->formats[0].outputs[0].fmt; /* Capture Pipeline */ link_cpr_pipe = kzalloc(sizeof(struct skl_pipe), GFP_KERNEL); @@ -544,6 +593,10 @@ static int cnl_sdw_bra_pipe_cfg_cp(struct skl_sst *ctx, goto error; } + link_cpr_cfg.fmt_idx = 0; + link_cpr_cfg.res_idx = 0; + host_cpr_cfg.fmt_idx = 0; + host_cpr_cfg.res_idx = 0; bra_data->cp_pipe = link_cpr_pipe; link_cpr_pipe->p_params = &link_cpr_params; link_cpr_cfg.pipe = link_cpr_pipe; @@ -572,14 +625,19 @@ static int cnl_sdw_bra_pipe_cfg_cp(struct skl_sst *ctx, link_cpr_cfg.id.module_id = 4; #endif link_cpr_cfg.id.instance_id = 3; - link_cpr_cfg.mcps = 100000; - link_cpr_cfg.mem_pages = 0; - link_cpr_cfg.ibs = 1152; - link_cpr_cfg.obs = 1152; + link_cpr_cfg.id.pvt_id = skl_get_pvt_id(ctx, + (uuid_le *)link_cpr_cfg.guid, link_cpr_cfg.id.instance_id); + if (link_cpr_cfg.id.pvt_id < 0) + return -EINVAL; + + link_cpr_cfg.module->resources[0].cps = 100000; + link_cpr_cfg.module->resources[0].is_pages = 0; + link_cpr_cfg.module->resources[0].ibs = 1152; + link_cpr_cfg.module->resources[0].obs = 1152; link_cpr_cfg.core_id = 0; - link_cpr_cfg.max_in_queue = 1; - link_cpr_cfg.max_out_queue = 1; - link_cpr_cfg.is_loadable = 0; + link_cpr_cfg.module->max_input_pins = 1; + link_cpr_cfg.module->max_output_pins = 1; + link_cpr_cfg.module->loadable = 0; link_cpr_cfg.domain = 0; link_cpr_cfg.m_type = SKL_MODULE_TYPE_COPIER; link_cpr_cfg.dev_type = SKL_DEVICE_SDW; @@ -591,7 +649,7 @@ static int cnl_sdw_bra_pipe_cfg_cp(struct skl_sst *ctx, link_cpr_cfg.hw_conn_type = SKL_CONN_SINK; link_cpr_cfg.formats_config.caps_size = 0; - link_cpr_cfg.dma_buffer_size = 2; + link_cpr_cfg.module->resources[0].dma_buffer_size = 2; link_cpr_cfg.pdi_type = 0; link_cpr_cfg.converter = 0; link_cpr_cfg.vbus_id = 0; @@ -613,34 +671,34 @@ static int cnl_sdw_bra_pipe_cfg_cp(struct skl_sst *ctx, #endif link_cpr_cfg.formats_config.caps[3] = 0x1; - link_cpr_cfg.in_fmt[0].channels = 6; - link_cpr_cfg.in_fmt[0].s_freq = 48000; - link_cpr_cfg.in_fmt[0].bit_depth = 32; - link_cpr_cfg.in_fmt[0].valid_bit_depth = 24; - link_cpr_cfg.in_fmt[0].ch_cfg = 8; - link_cpr_cfg.in_fmt[0].interleaving_style = 0; - link_cpr_cfg.in_fmt[0].sample_type = 0; - link_cpr_cfg.in_fmt[0].ch_map = 0xFF657120; - - link_cpr_cfg.out_fmt[0].channels = 6; - link_cpr_cfg.out_fmt[0].s_freq = 48000; - link_cpr_cfg.out_fmt[0].bit_depth = 32; - link_cpr_cfg.out_fmt[0].valid_bit_depth = 24; - link_cpr_cfg.out_fmt[0].ch_cfg = 8; - link_cpr_cfg.out_fmt[0].interleaving_style = 0; - link_cpr_cfg.out_fmt[0].sample_type = 0; - link_cpr_cfg.out_fmt[0].ch_map = 0xFF657120; - - link_cpr_cfg.m_in_pin = kcalloc(link_cpr_cfg.max_in_queue, - sizeof(link_cpr_cfg.m_in_pin), + in_fmt->channels = 6; + in_fmt->s_freq = 48000; + in_fmt->bit_depth = 32; + in_fmt->valid_bit_depth = 24; + in_fmt->ch_cfg = 8; + in_fmt->interleaving_style = 0; + in_fmt->sample_type = 0; + in_fmt->ch_map = 0xFF657120; + + out_fmt->channels = 6; + out_fmt->s_freq = 48000; + out_fmt->bit_depth = 32; + out_fmt->valid_bit_depth = 24; + out_fmt->ch_cfg = 8; + out_fmt->interleaving_style = 0; + out_fmt->sample_type = 0; + out_fmt->ch_map = 0xFF657120; + + link_cpr_cfg.m_in_pin = kcalloc(link_cpr_cfg.module->max_input_pins, + sizeof(*link_cpr_cfg.m_in_pin), GFP_KERNEL); if (!link_cpr_cfg.m_in_pin) { ret = -ENOMEM; goto error; } - link_cpr_cfg.m_out_pin = kcalloc(link_cpr_cfg.max_out_queue, - sizeof(link_cpr_cfg.m_out_pin), + link_cpr_cfg.m_out_pin = kcalloc(link_cpr_cfg.module->max_output_pins, + sizeof(*link_cpr_cfg.m_out_pin), GFP_KERNEL); if (!link_cpr_cfg.m_out_pin) { ret = -ENOMEM; @@ -667,20 +725,25 @@ static int cnl_sdw_bra_pipe_cfg_cp(struct skl_sst *ctx, sizeof(struct skl_module_cfg)); host_cpr_cfg.id.instance_id = 4; + host_cpr_cfg.id.pvt_id = skl_get_pvt_id(ctx, + (uuid_le *)host_cpr_cfg.guid, host_cpr_cfg.id.instance_id); + if (host_cpr_cfg.id.pvt_id < 0) + return -EINVAL; + host_cpr_cfg.dev_type = SKL_DEVICE_HDAHOST; host_cpr_cfg.hw_conn_type = SKL_CONN_SINK; link_cpr_params.host_dma_id = (bra_data->cp_stream_tag - 1); host_cpr_cfg.formats_config.caps_size = 0; - host_cpr_cfg.m_in_pin = kcalloc(host_cpr_cfg.max_in_queue, - sizeof(host_cpr_cfg.m_in_pin), + host_cpr_cfg.m_in_pin = kcalloc(host_cpr_cfg.module->max_input_pins, + sizeof(*host_cpr_cfg.m_in_pin), GFP_KERNEL); if (!host_cpr_cfg.m_in_pin) { ret = -ENOMEM; goto error; } - host_cpr_cfg.m_out_pin = kcalloc(host_cpr_cfg.max_out_queue, - sizeof(host_cpr_cfg.m_out_pin), + host_cpr_cfg.m_out_pin = kcalloc(host_cpr_cfg.module->max_output_pins, + sizeof(*host_cpr_cfg.m_out_pin), GFP_KERNEL); if (!host_cpr_cfg.m_out_pin) { ret = -ENOMEM; From 1b8aee5c7bd703bac650d9ebcf020a15aa8a4252 Mon Sep 17 00:00:00 2001 From: Guneshwor Singh Date: Mon, 6 Mar 2017 09:47:50 +0530 Subject: [PATCH 0720/1103] ASoC: Intel: Skylake: Split dais and add flag for dynamic dais Since FE dais can come from topology, split the FE dais from existing dai array and use module param to decide to register them during probe. Updated commit message: Split dai change is already merged in #c3ae22e39db79 ("ASoC: Intel: Skylake: Add flag to check to register FE dais from topology"). With this patch only the remaining the dais are added. Change-Id: I9f5d3d89e070b65800ada57746df21d1f6754e78 Signed-off-by: Guneshwor Singh Reviewed-on: Reviewed-by: Prusty, Subhransu S Reviewed-by: Jayanti, Satya Charitardha Reviewed-by: Diwakar, Praveen Tested-by: Sm, Bhadur A --- sound/soc/intel/skylake/skl-pcm.c | 118 +++++++++++++++--------------- 1 file changed, 59 insertions(+), 59 deletions(-) diff --git a/sound/soc/intel/skylake/skl-pcm.c b/sound/soc/intel/skylake/skl-pcm.c index 1da485053967..7ac209ddd1eb 100644 --- a/sound/soc/intel/skylake/skl-pcm.c +++ b/sound/soc/intel/skylake/skl-pcm.c @@ -962,46 +962,6 @@ static struct snd_soc_dai_ops skl_sdw_dai_ops = { }; static struct snd_soc_dai_driver skl_fe_dai[] = { -{ - .name = "TraceBuffer0 Pin", - .compress_new = snd_soc_new_compress, - .cops = &skl_trace_compr_ops, - .capture = { - .stream_name = "TraceBuffer0 Capture", - .channels_min = HDA_MONO, - .channels_max = HDA_MONO, - }, -}, -{ - .name = "TraceBuffer1 Pin", - .compress_new = snd_soc_new_compress, - .cops = &skl_trace_compr_ops, - .capture = { - .stream_name = "TraceBuffer1 Capture", - .channels_min = HDA_MONO, - .channels_max = HDA_MONO, - }, -}, -{ - .name = "TraceBuffer2 Pin", - .compress_new = snd_soc_new_compress, - .cops = &skl_trace_compr_ops, - .capture = { - .stream_name = "TraceBuffer2 Capture", - .channels_min = HDA_MONO, - .channels_max = HDA_MONO, - }, -}, -{ - .name = "TraceBuffer3 Pin", - .compress_new = snd_soc_new_compress, - .cops = &skl_trace_compr_ops, - .capture = { - .stream_name = "TraceBuffer3 Capture", - .channels_min = HDA_MONO, - .channels_max = HDA_MONO, - }, -}, { .name = "System Pin", .ops = &skl_pcm_dai_ops, @@ -1081,24 +1041,6 @@ static struct snd_soc_dai_driver skl_fe_dai[] = { .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE, }, }, -{ - .name = "Compress Probe0 Pin", - .compress_new = snd_soc_new_compress, - .cops = &skl_probe_compr_ops, - .playback = { - .stream_name = "Probe Playback", - .channels_min = HDA_MONO, - }, -}, -{ - .name = "Compress Probe1 Pin", - .compress_new = snd_soc_new_compress, - .cops = &skl_probe_compr_ops, - .capture = { - .stream_name = "Probe Capture", - .channels_min = HDA_MONO, - }, -}, { .name = "LowLatency Pin", .ops = &skl_pcm_dai_ops, @@ -1173,7 +1115,7 @@ static struct snd_soc_dai_driver skl_fe_dai[] = { }, }; -/* BE CPU Dais */ +/* BE cpu dais and compress dais*/ static struct snd_soc_dai_driver skl_platform_dai[] = { { .name = "SSP0 Pin", @@ -1485,6 +1427,64 @@ static struct snd_soc_dai_driver skl_platform_dai[] = { }, }, +{ + .name = "TraceBuffer0 Pin", + .compress_new = snd_soc_new_compress, + .cops = &skl_trace_compr_ops, + .capture = { + .stream_name = "TraceBuffer0 Capture", + .channels_min = HDA_MONO, + .channels_max = HDA_MONO, + }, +}, +{ + .name = "TraceBuffer1 Pin", + .compress_new = snd_soc_new_compress, + .cops = &skl_trace_compr_ops, + .capture = { + .stream_name = "TraceBuffer1 Capture", + .channels_min = HDA_MONO, + .channels_max = HDA_MONO, + }, +}, +{ + .name = "TraceBuffer2 Pin", + .compress_new = snd_soc_new_compress, + .cops = &skl_trace_compr_ops, + .capture = { + .stream_name = "TraceBuffer2 Capture", + .channels_min = HDA_MONO, + .channels_max = HDA_MONO, + }, +}, +{ + .name = "TraceBuffer3 Pin", + .compress_new = snd_soc_new_compress, + .cops = &skl_trace_compr_ops, + .capture = { + .stream_name = "TraceBuffer3 Capture", + .channels_min = HDA_MONO, + .channels_max = HDA_MONO, + }, +}, +{ + .name = "Compress Probe0 Pin", + .compress_new = snd_soc_new_compress, + .cops = &skl_probe_compr_ops, + .playback = { + .stream_name = "Probe Playback", + .channels_min = HDA_MONO, + }, +}, +{ + .name = "Compress Probe1 Pin", + .compress_new = snd_soc_new_compress, + .cops = &skl_probe_compr_ops, + .capture = { + .stream_name = "Probe Capture", + .channels_min = HDA_MONO, + }, +}, }; int skl_dai_load(struct snd_soc_component *cmp, int index, From f45eec0f6085b86cc1298994080f17905738c3be Mon Sep 17 00:00:00 2001 From: Guneshwor Singh Date: Mon, 6 Mar 2017 09:56:10 +0530 Subject: [PATCH 0721/1103] ASoC: Intel: Skylake: Add component ops for dai load Since FE dais can come from topology, add component ops for the same. Change-Id: I868be6943a69d0dafc6fb04b91f70be576318400 Signed-off-by: Guneshwor Singh Reviewed-on: Reviewed-by: Kp, Jeeja Reviewed-by: Prusty, Subhransu S Reviewed-by: Koul, Vinod Reviewed-by: Diwakar, Praveen Reviewed-by: Nc, Shreyas Tested-by: Sm, Bhadur A --- sound/soc/intel/skylake/skl-topology.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/sound/soc/intel/skylake/skl-topology.h b/sound/soc/intel/skylake/skl-topology.h index b8a1d8b90fe1..d17809b337ed 100644 --- a/sound/soc/intel/skylake/skl-topology.h +++ b/sound/soc/intel/skylake/skl-topology.h @@ -617,4 +617,7 @@ int skl_tplg_dsp_log_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol); int skl_tplg_dsp_log_set(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol); + +int skl_dai_load(struct snd_soc_component *cmp, + struct snd_soc_dai_driver *pcm_dai); #endif From e1a0c39e6b1ef85f38c0d62bdc9783249478ab66 Mon Sep 17 00:00:00 2001 From: Guneshwor Singh Date: Mon, 27 Feb 2017 09:09:37 +0530 Subject: [PATCH 0722/1103] ASoC: Intel: board: Add support for dynamic FE dai link in cnl_rt274 machine FE dai links now come from topology, so remove them from machine driver. Additionally register ops to initialize dai link. Rate constraint is not required as rates will come from topology. So remove the startup ops as well which sets the rate constraint. Change-Id: I0fb07c74450bf55415323539e383ef39ed3ff4c4 Signed-off-by: Guneshwor Singh Reviewed-on: Reviewed-by: Koul, Vinod Reviewed-by: Prusty, Subhransu S Reviewed-by: Kp, Jeeja Reviewed-by: Nc, Shreyas Reviewed-by: Diwakar, Praveen Tested-by: Sm, Bhadur A --- sound/soc/intel/boards/cnl_rt274.c | 74 ++++-------------------------- 1 file changed, 10 insertions(+), 64 deletions(-) diff --git a/sound/soc/intel/boards/cnl_rt274.c b/sound/soc/intel/boards/cnl_rt274.c index eebde84e7700..21b40b895629 100644 --- a/sound/soc/intel/boards/cnl_rt274.c +++ b/sound/soc/intel/boards/cnl_rt274.c @@ -124,30 +124,6 @@ static int cnl_rt274_init(struct snd_soc_pcm_runtime *runtime) return 0; } -static unsigned int rates_supported[] = { - 48000, - 32000, - 24000, - 16000, - 8000, -}; - -static struct snd_pcm_hw_constraint_list rate_constraints = { - .count = ARRAY_SIZE(rates_supported), - .list = rates_supported, -}; - -static int cnl_fe_startup(struct snd_pcm_substream *substream) -{ - return snd_pcm_hw_constraint_list(substream->runtime, 0, - SNDRV_PCM_HW_PARAM_RATE, - &rate_constraints); -} - -static struct snd_soc_ops cnl_fe_ops = { - .startup = cnl_fe_startup, -}; - static int cnl_be_fixup(struct snd_soc_pcm_runtime *rtd, struct snd_pcm_hw_params *params) { @@ -204,46 +180,6 @@ static const char cname[] = "i2c-INT34C2:00"; #endif static struct snd_soc_dai_link cnl_rt274_msic_dailink[] = { - { - .name = "CNL Audio Port", - .stream_name = "Audio", - .cpu_dai_name = "System Pin", - .codec_name = "snd-soc-dummy", - .codec_dai_name = "snd-soc-dummy-dai", - .platform_name = pname, - .ignore_suspend = 1, - .nonatomic = 1, - .dynamic = 1, - .dpcm_playback = 1, - .dpcm_capture = 1, - .ops = &cnl_fe_ops, - }, - { - .name = "CNL Deepbuffer Port", - .stream_name = "Deep Buffer Audio", - .cpu_dai_name = "Deepbuffer Pin", - .codec_name = "snd-soc-dummy", - .codec_dai_name = "snd-soc-dummy-dai", - .platform_name = pname, - .dpcm_playback = 1, - .dpcm_capture = 1, - .ignore_suspend = 1, - .nonatomic = 1, - .dynamic = 1, - .ops = &cnl_fe_ops, - }, - { - .name = "CNL Reference Port", - .stream_name = "Reference Capture", - .cpu_dai_name = "Reference Pin", - .codec_name = "snd-soc-dummy", - .codec_dai_name = "snd-soc-dummy-dai", - .platform_name = pname, - .dpcm_capture = 1, - .ignore_suspend = 1, - .nonatomic = 1, - .dynamic = 1, - }, /* Trace Buffer DAI links */ { .name = "CNL Trace Buffer0", @@ -352,6 +288,15 @@ static struct snd_soc_dai_link cnl_rt274_msic_dailink[] = { }, }; +static int +cnl_add_dai_link(struct snd_soc_card *card, struct snd_soc_dai_link *link) +{ + link->platform_name = pname; + link->nonatomic = 1; + + return 0; +} + /* SoC card */ static struct snd_soc_card snd_soc_card_cnl = { .name = "cnl-audio", @@ -363,6 +308,7 @@ static struct snd_soc_card snd_soc_card_cnl = { .num_dapm_routes = ARRAY_SIZE(cnl_map), .controls = cnl_controls, .num_controls = ARRAY_SIZE(cnl_controls), + .add_dai_link = cnl_add_dai_link, }; static int snd_cnl_rt274_mc_probe(struct platform_device *pdev) From 6f1d7d8a8a93a409564ef4a34df23df3e963aa84 Mon Sep 17 00:00:00 2001 From: Guneshwor Singh Date: Mon, 27 Feb 2017 09:20:59 +0530 Subject: [PATCH 0723/1103] ASoC: Intel: board: Add support for dynamic FE dai link in cnl_rt700 machine FE dai links now come from topology, so remove them from machine driver. Additionally register ops to initialize dai link. Rate constraint is not required as rates will come from topology. So remove the startup ops as well which sets the rate constraint. Change-Id: Ia2dcaeebd785f79c87f3032c0ad39939bb31cee6 Signed-off-by: Guneshwor Singh Reviewed-on: Reviewed-by: Koul, Vinod Reviewed-by: Prusty, Subhransu S Reviewed-by: Kp, Jeeja Reviewed-by: Diwakar, Praveen Tested-by: Sm, Bhadur A --- sound/soc/intel/boards/cnl_rt700.c | 93 ++++-------------------------- 1 file changed, 12 insertions(+), 81 deletions(-) diff --git a/sound/soc/intel/boards/cnl_rt700.c b/sound/soc/intel/boards/cnl_rt700.c index d49c3f9de10e..740062a4108a 100644 --- a/sound/soc/intel/boards/cnl_rt700.c +++ b/sound/soc/intel/boards/cnl_rt700.c @@ -80,45 +80,6 @@ static const struct snd_kcontrol_new cnl_rt700_controls[] = { }; -static int cnl_rt700_init(struct snd_soc_pcm_runtime *runtime) -{ - int ret; - struct snd_soc_card *card = runtime->card; - - pr_info("Entry %s\n", __func__); - card->dapm.idle_bias_off = true; - - ret = snd_soc_add_card_controls(card, cnl_rt700_controls, - ARRAY_SIZE(cnl_rt700_controls)); - if (ret) { - pr_err("unable to add card controls\n"); - return ret; - } - return 0; -} - -static unsigned int rates_48000[] = { - 48000, - 16000, - 8000, -}; - -static struct snd_pcm_hw_constraint_list constraints_48000 = { - .count = ARRAY_SIZE(rates_48000), - .list = rates_48000, -}; - -static int cnl_rt700_startup(struct snd_pcm_substream *substream) -{ - return snd_pcm_hw_constraint_list(substream->runtime, 0, - SNDRV_PCM_HW_PARAM_RATE, - &constraints_48000); -} - -static struct snd_soc_ops cnl_rt700_ops = { - .startup = cnl_rt700_startup, -}; - static int cnl_rt700_codec_fixup(struct snd_soc_pcm_runtime *rtd, struct snd_pcm_hw_params *params) { @@ -164,48 +125,6 @@ static const char cname[] = "sdw-slave1-10:02:5d:07:00:01"; #endif static struct snd_soc_dai_link cnl_rt700_msic_dailink[] = { - { - .name = "Bxtn Audio Port", - .stream_name = "Audio", - .cpu_dai_name = "System Pin", - .codec_name = "snd-soc-dummy", - .codec_dai_name = "snd-soc-dummy-dai", - .platform_name = pname, - .init = cnl_rt700_init, - .ignore_suspend = 1, - .nonatomic = 1, - .dynamic = 1, - .dpcm_playback = 1, - .dpcm_capture = 1, - .ops = &cnl_rt700_ops, - }, - { - .name = "CNL Reference Port", - .stream_name = "Reference Capture", - .cpu_dai_name = "Reference Pin", - .codec_name = "snd-soc-dummy", - .codec_dai_name = "snd-soc-dummy-dai", - .platform_name = pname, - .ignore_suspend = 1, - .nonatomic = 1, - .dynamic = 1, - .dpcm_capture = 1, - .ops = &cnl_rt700_ops, - }, - { - .name = "CNL Deepbuffer Port", - .stream_name = "Deep Buffer Audio", - .cpu_dai_name = "Deepbuffer Pin", - .codec_name = "snd-soc-dummy", - .codec_dai_name = "snd-soc-dummy-dai", - .platform_name = pname, - .dpcm_playback = 1, - .ignore_suspend = 1, - .nonatomic = 1, - .dynamic = 1, - .ops = &cnl_rt700_ops, - }, - { .name = "SDW0-Codec", .cpu_dai_name = "SDW Pin", @@ -243,6 +162,15 @@ static struct snd_soc_dai_link cnl_rt700_msic_dailink[] = { }, }; +static int +cnl_add_dai_link(struct snd_soc_card *card, struct snd_soc_dai_link *link) +{ + link->platform_name = pname; + link->nonatomic = 1; + + return 0; +} + /* SoC card */ static struct snd_soc_card snd_soc_card_cnl_rt700 = { .name = "cnl_rt700-audio", @@ -252,6 +180,9 @@ static struct snd_soc_card snd_soc_card_cnl_rt700 = { .num_dapm_widgets = ARRAY_SIZE(cnl_rt700_widgets), .dapm_routes = cnl_rt700_map, .num_dapm_routes = ARRAY_SIZE(cnl_rt700_map), + .add_dai_link = cnl_add_dai_link, + .controls = cnl_rt700_controls, + .num_controls = ARRAY_SIZE(cnl_rt700_controls), }; From 84a9758dd32225d07519d0e59a88ff982ba07285 Mon Sep 17 00:00:00 2001 From: Sanyog Kale Date: Fri, 17 Feb 2017 11:58:27 +0530 Subject: [PATCH 0724/1103] ASoC: Intel: Update device type entry for SoundWire device SoundWire device type is updated as SoundWire PCM and SoundWire PDM type. This information will be used to assign SoundWire stream type (PCM or PDM). Change-Id: Ide861544b6f175153431cc1e411591f9a45e44e4 Signed-off-by: Sanyog Kale Reviewed-on: Reviewed-by: Singh, Guneshwor O Reviewed-by: Nc, Shreyas Reviewed-by: Prusty, Subhransu S Reviewed-by: Diwakar, Praveen Tested-by: Avati, Santosh Kumar --- include/uapi/sound/skl-tplg-interface.h | 3 ++- sound/soc/intel/skylake/skl-messages.c | 7 ++++--- sound/soc/intel/skylake/skl-topology.c | 3 ++- 3 files changed, 8 insertions(+), 5 deletions(-) diff --git a/include/uapi/sound/skl-tplg-interface.h b/include/uapi/sound/skl-tplg-interface.h index e1a7771f4873..c0145433d05e 100644 --- a/include/uapi/sound/skl-tplg-interface.h +++ b/include/uapi/sound/skl-tplg-interface.h @@ -108,7 +108,8 @@ enum skl_dev_type { SKL_DEVICE_SLIMBUS = 0x3, SKL_DEVICE_HDALINK = 0x4, SKL_DEVICE_HDAHOST = 0x5, - SKL_DEVICE_SDW = 0x6, + SKL_DEVICE_SDW_PCM = 0x6, + SKL_DEVICE_SDW_PDM = 0x7, SKL_DEVICE_NONE }; diff --git a/sound/soc/intel/skylake/skl-messages.c b/sound/soc/intel/skylake/skl-messages.c index 495f82fc5d65..b4b2d1dd0ee2 100644 --- a/sound/soc/intel/skylake/skl-messages.c +++ b/sound/soc/intel/skylake/skl-messages.c @@ -476,7 +476,7 @@ static int cnl_sdw_bra_pipe_cfg_pb(struct skl_sst *ctx, if (link_cpr_cfg.id.pvt_id < 0) return -EINVAL; - link_cpr_cfg.dev_type = SKL_DEVICE_SDW; + link_cpr_cfg.dev_type = SKL_DEVICE_SDW_PCM; #if IS_ENABLED(CONFIG_SND_SOC_INTEL_CNL_FPGA) link_cpr_cfg.sdw_stream_num = 0x3; #else @@ -640,7 +640,7 @@ static int cnl_sdw_bra_pipe_cfg_cp(struct skl_sst *ctx, link_cpr_cfg.module->loadable = 0; link_cpr_cfg.domain = 0; link_cpr_cfg.m_type = SKL_MODULE_TYPE_COPIER; - link_cpr_cfg.dev_type = SKL_DEVICE_SDW; + link_cpr_cfg.dev_type = SKL_DEVICE_SDW_PCM; #if IS_ENABLED(CONFIG_SND_SOC_INTEL_CNL_FPGA) link_cpr_cfg.sdw_stream_num = 0x4; #else @@ -1392,7 +1392,8 @@ static u32 skl_get_node_id(struct skl_sst *ctx, SKL_DMA_HDA_HOST_INPUT_CLASS; node_id.node.vindex = params->host_dma_id; break; - case SKL_DEVICE_SDW: + case SKL_DEVICE_SDW_PCM: + case SKL_DEVICE_SDW_PDM: node_id.node.dma_type = (SKL_CONN_SOURCE == mconfig->hw_conn_type) ? SKL_DMA_SDW_LINK_OUTPUT_CLASS : diff --git a/sound/soc/intel/skylake/skl-topology.c b/sound/soc/intel/skylake/skl-topology.c index 2e4a894f58a4..629f420b58de 100644 --- a/sound/soc/intel/skylake/skl-topology.c +++ b/sound/soc/intel/skylake/skl-topology.c @@ -2120,7 +2120,8 @@ static u8 skl_tplg_be_link_type(int dev_type) case SKL_DEVICE_HDALINK: ret = NHLT_LINK_HDA; break; - case SKL_DEVICE_SDW: + case SKL_DEVICE_SDW_PCM: + case SKL_DEVICE_SDW_PDM: ret = NHLT_LINK_SDW; break; default: From 1f5c157a07406eb7ab431a6d4d72ad29cc1394ad Mon Sep 17 00:00:00 2001 From: Sanyog Kale Date: Fri, 17 Feb 2017 12:08:49 +0530 Subject: [PATCH 0725/1103] ASoC: Intel: Skylake: Use device type to determine SoundWire stream type To determine SoundWire stream type, device type information is used instead of pdi type. Change-Id: I98ba5b7141b1a6b865f38697510fb9439dd4816c Signed-off-by: Sanyog Kale Reviewed-on: Reviewed-by: Singh, Guneshwor O Reviewed-by: Nc, Shreyas Reviewed-by: Prusty, Subhransu S Reviewed-by: Diwakar, Praveen Tested-by: Avati, Santosh Kumar --- sound/soc/intel/skylake/skl-sdw-pcm.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/sound/soc/intel/skylake/skl-sdw-pcm.c b/sound/soc/intel/skylake/skl-sdw-pcm.c index 2c5ba970e34c..6b8c4dba5b01 100644 --- a/sound/soc/intel/skylake/skl-sdw-pcm.c +++ b/sound/soc/intel/skylake/skl-sdw-pcm.c @@ -90,9 +90,10 @@ int cnl_sdw_startup(struct snd_pcm_substream *substream, ret = -ENOMEM; goto alloc_failed; } - if (m_cfg->pdi_type == SKL_PDI_PCM) + + if (m_cfg->dev_type == SKL_DEVICE_SDW_PCM) dma->stream_type = CNL_SDW_PDI_TYPE_PCM; - else if (m_cfg->pdi_type == SKL_PDI_PDM) + else if (m_cfg->dev_type == SKL_DEVICE_SDW_PDM) dma->stream_type = CNL_SDW_PDI_TYPE_PDM; else { dev_err(dai->dev, "Stream type not known\n"); From fecf7cfc4e5d3864a8ed30063338a48e8f127dee Mon Sep 17 00:00:00 2001 From: Sanyog Kale Date: Tue, 21 Feb 2017 15:51:38 +0530 Subject: [PATCH 0726/1103] ASoC: Intel: Remove pdi_type support from topology The pdi_type tag used for SoundWire device type is not used, so removing support from driver. Change-Id: I77a71c02cc0b2b51edce24aa667549e24752d095 Signed-off-by: Sanyog Kale Reviewed-on: Reviewed-by: Singh, Guneshwor O Reviewed-by: Nc, Shreyas Reviewed-by: Prusty, Subhransu S Reviewed-by: Diwakar, Praveen Tested-by: Avati, Santosh Kumar --- include/uapi/sound/skl-tplg-interface.h | 6 ------ sound/soc/intel/skylake/skl-messages.c | 2 -- sound/soc/intel/skylake/skl-topology.h | 1 - 3 files changed, 9 deletions(-) diff --git a/include/uapi/sound/skl-tplg-interface.h b/include/uapi/sound/skl-tplg-interface.h index c0145433d05e..fc4e4324f94b 100644 --- a/include/uapi/sound/skl-tplg-interface.h +++ b/include/uapi/sound/skl-tplg-interface.h @@ -113,12 +113,6 @@ enum skl_dev_type { SKL_DEVICE_NONE }; -enum skl_pdi_type { - SKL_PDI_PCM = 0, - SKL_PDI_PDM = 1, - SKL_PDI_INVALID = 2 -}; - /** * enum skl_interleaving - interleaving style * diff --git a/sound/soc/intel/skylake/skl-messages.c b/sound/soc/intel/skylake/skl-messages.c index b4b2d1dd0ee2..79451737be9e 100644 --- a/sound/soc/intel/skylake/skl-messages.c +++ b/sound/soc/intel/skylake/skl-messages.c @@ -411,7 +411,6 @@ static int cnl_sdw_bra_pipe_cfg_pb(struct skl_sst *ctx, host_cpr_cfg.hw_conn_type = SKL_CONN_SOURCE; host_cpr_cfg.formats_config.caps_size = 0; host_cpr_cfg.module->resources[0].dma_buffer_size = 2; - host_cpr_cfg.pdi_type = 0; host_cpr_cfg.converter = 0; host_cpr_cfg.vbus_id = 0; host_cpr_cfg.sdw_agg_enable = 0; @@ -650,7 +649,6 @@ static int cnl_sdw_bra_pipe_cfg_cp(struct skl_sst *ctx, link_cpr_cfg.formats_config.caps_size = 0; link_cpr_cfg.module->resources[0].dma_buffer_size = 2; - link_cpr_cfg.pdi_type = 0; link_cpr_cfg.converter = 0; link_cpr_cfg.vbus_id = 0; link_cpr_cfg.sdw_agg_enable = 0; diff --git a/sound/soc/intel/skylake/skl-topology.h b/sound/soc/intel/skylake/skl-topology.h index d17809b337ed..00265245beee 100644 --- a/sound/soc/intel/skylake/skl-topology.h +++ b/sound/soc/intel/skylake/skl-topology.h @@ -442,7 +442,6 @@ struct skl_module_cfg { u32 mem_pages; enum d0i3_capability d0i3_caps; u32 dma_buffer_size; /* in milli seconds */ - u8 pdi_type; u32 sdw_stream_num; bool sdw_agg_enable; struct skl_sdw_aggregation sdw_agg; From 2947a0a0854fcc46abb17764e15fa4919e67463a Mon Sep 17 00:00:00 2001 From: Shreyas NC Date: Wed, 8 Mar 2017 03:08:18 +0530 Subject: [PATCH 0727/1103] ASoC: Intel: Skylake: Define tokens for aggregation To support aggregation, define tokens for aggregation id, masters participating in aggregation, link id and channel mask. Change-Id: Ib7e3f5a3aec4d8a6e2dec1b1f045c8078a3ea958 Signed-off-by: Shreyas NC Reviewed-on: Reviewed-by: Singh, Guneshwor O Reviewed-by: Jayanti, Satya Charitardha Reviewed-by: Prodduvaka, Leoni Reviewed-by: Prusty, Subhransu S Reviewed-by: R, Dharageswari Reviewed-by: Koul, Vinod Reviewed-by: Diwakar, Praveen Reviewed-by: Kale, Sanyog R Tested-by: Avati, Santosh Kumar --- include/uapi/sound/snd_sst_tokens.h | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/include/uapi/sound/snd_sst_tokens.h b/include/uapi/sound/snd_sst_tokens.h index 8ba0112e5336..5d3d81af0c30 100644 --- a/include/uapi/sound/snd_sst_tokens.h +++ b/include/uapi/sound/snd_sst_tokens.h @@ -232,6 +232,16 @@ * from source specified by clock source. * * %SKL_TKN_U32_ASTATE_CLK_SRC: Clock source for A-State entry + * %SKL_TKN_U32_AGG_NUM_MASTERS: + * Number of aggregated masters + * + * %SKL_TKN_U32_AGG_LINK_ID: Aggregated master's instance id + * + * %SKL_TKN_U32_AGG_CH_MASK: Represents channels driven by the master + * + * %SKL_TKN_U32_AGG_ID: Aggregation id is a non zero identifier to + * indicate if this endpoint is participating + * in aggregation. * * module_id and loadable flags dont have tokens as these values will be * read from the DSP FW manifest @@ -324,7 +334,13 @@ enum SKL_TKNS { SKL_TKN_U32_ASTATE_COUNT, SKL_TKN_U32_ASTATE_KCPS, SKL_TKN_U32_ASTATE_CLK_SRC, - SKL_TKN_MAX = SKL_TKN_U32_ASTATE_CLK_SRC, + + SKL_TKN_U32_AGG_NUM_MASTERS, + SKL_TKN_U32_AGG_LINK_ID, + SKL_TKN_U32_AGG_CH_MASK, + SKL_TKN_U32_AGG_ID, + + SKL_TKN_MAX = SKL_TKN_U32_AGG_ID, }; #endif From fdfb943364fe7f05338eae6c225abff4b1267b5f Mon Sep 17 00:00:00 2001 From: Shreyas NC Date: Wed, 8 Mar 2017 03:09:33 +0530 Subject: [PATCH 0728/1103] ASoC: Intel: Skylake: Parse tokens to support aggregation To support aggregation, we need to parse the aggregation related tokens like channel mask, number of masters participating in aggregation and link id. So, add parsing logic for the same. Change-Id: I167a5023bcf7e7bd319ecd249a7a4e7c051ff2c2 Signed-off-by: Shreyas NC Reviewed-on: Reviewed-by: Jayanti, Satya Charitardha Reviewed-by: Singh, Guneshwor O Reviewed-by: Prodduvaka, Leoni Reviewed-by: Prusty, Subhransu S Reviewed-by: R, Dharageswari Reviewed-by: Koul, Vinod Reviewed-by: Diwakar, Praveen Reviewed-by: Kale, Sanyog R Tested-by: Avati, Santosh Kumar --- sound/soc/intel/skylake/skl-topology.c | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/sound/soc/intel/skylake/skl-topology.c b/sound/soc/intel/skylake/skl-topology.c index 629f420b58de..711629143aa3 100644 --- a/sound/soc/intel/skylake/skl-topology.c +++ b/sound/soc/intel/skylake/skl-topology.c @@ -2720,7 +2720,7 @@ static int skl_tplg_get_token(struct device *dev, int tkn_count = 0; int ret; static int is_pipe_exists; - static int pin_index, dir, conf_idx; + static int pin_index, dir, conf_idx, agg_id; struct skl_module_iface *iface = NULL; struct skl_module_res *res = NULL; int res_idx = mconfig->res_idx; @@ -2939,6 +2939,23 @@ static int skl_tplg_get_token(struct device *dev, break; + case SKL_TKN_U32_AGG_LINK_ID: + agg_id = tkn_elem->value; + if (agg_id > SDW_MAX_MASTERS) + return -EINVAL; + break; + + case SKL_TKN_U32_AGG_NUM_MASTERS: + mconfig->sdw_agg.num_masters = tkn_elem->value; + mconfig->sdw_agg_enable = (tkn_elem->value > 1) + ? true : false; + break; + + case SKL_TKN_U32_AGG_CH_MASK: + mconfig->sdw_agg.agg_data[agg_id].ch_mask = + tkn_elem->value; + break; + case SKL_TKN_U32_DMA_BUF_SIZE: mconfig->dma_buffer_size = tkn_elem->value; break; From 182e624ec1800ea1ce8ae83db59380cb638754a4 Mon Sep 17 00:00:00 2001 From: "Jayanti, Satya Charitardha" Date: Wed, 4 Jan 2017 20:41:55 +0530 Subject: [PATCH 0729/1103] ASoC: Intel: CNL: Add DAIs for SDW Aggregation This patch adds DAI to enable Aggregation feature for playback and capture on SoundWire Master 1 and Master 2 with RT700 codec. It also makes changes in channel and format configurations for both playback and capture in DAIs. Change-Id: I252733c39e2e81a2aa8c2e4c44a9416a02aafab2 Signed-off-by: Jayanti, Satya Charitardha Reviewed-on: Reviewed-by: Singh, Guneshwor O Reviewed-by: Diwakar, Praveen Tested-by: Avati, Santosh Kumar --- sound/soc/intel/skylake/skl-pcm.c | 34 +++++++++++++++++++++++++++---- 1 file changed, 30 insertions(+), 4 deletions(-) diff --git a/sound/soc/intel/skylake/skl-pcm.c b/sound/soc/intel/skylake/skl-pcm.c index 7ac209ddd1eb..265992ec11ff 100644 --- a/sound/soc/intel/skylake/skl-pcm.c +++ b/sound/soc/intel/skylake/skl-pcm.c @@ -1354,17 +1354,17 @@ static struct snd_soc_dai_driver skl_platform_dai[] = { .ops = &skl_sdw_dai_ops, .playback = { .stream_name = "SDW Tx10", - .channels_min = HDA_STEREO, + .channels_min = HDA_MONO, .channels_max = HDA_STEREO, .rates = SNDRV_PCM_RATE_48000, - .formats = SNDRV_PCM_FMTBIT_S16_LE, + .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE, }, .capture = { .stream_name = "SDW Rx10", - .channels_min = HDA_STEREO, + .channels_min = HDA_MONO, .channels_max = HDA_STEREO, .rates = SNDRV_PCM_RATE_48000, - .formats = SNDRV_PCM_FMTBIT_S16_LE, + .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE, }, }, { @@ -1405,6 +1405,32 @@ static struct snd_soc_dai_driver skl_platform_dai[] = { }, }, +#ifdef CONFIG_SND_SOC_SDW_AGGM1M2 +{ + /* + * Currently adding 1 playback and 1 capture pin, ideally it + * should be coming from CLT based on endpoints to be supported + */ + .name = "SDW2 Pin", + .id = SDW_BE_DAI_ID_MSTR2, + .ops = &skl_sdw_dai_ops, + .playback = { + .stream_name = "SDW2 Tx", + .channels_min = HDA_MONO, + .channels_max = HDA_STEREO, + .rates = SNDRV_PCM_RATE_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE, + }, + .capture = { + .stream_name = "SDW2 Rx", + .channels_min = HDA_MONO, + .channels_max = HDA_STEREO, + .rates = SNDRV_PCM_RATE_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE, + }, + +}, +#endif { /* Currently adding 1 playback and 1 capture pin, ideally it * should be coming from CLT based on endpoints to be supported From e553636b599aa9262304d97dcfb09c6b89fc4176 Mon Sep 17 00:00:00 2001 From: Sanyog Kale Date: Fri, 6 Jan 2017 16:12:13 +0530 Subject: [PATCH 0730/1103] ASoC: Intel: Kconfig changes for SoundWire aggregation support This patch adds SoundWire aggregation config. Change-Id: I7e5ab3d7079454dfde16b98caec818dd21e47c99 Signed-off-by: Sanyog Kale Reviewed-on: Reviewed-by: Jayanti, Satya Charitardha Reviewed-by: Nc, Shreyas Reviewed-by: Singh, Guneshwor O Reviewed-by: Kp, Jeeja Reviewed-by: Diwakar, Praveen Tested-by: Avati, Santosh Kumar --- sound/soc/intel/Kconfig | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/sound/soc/intel/Kconfig b/sound/soc/intel/Kconfig index 487e4445f6a0..9d2be10e484c 100644 --- a/sound/soc/intel/Kconfig +++ b/sound/soc/intel/Kconfig @@ -132,6 +132,11 @@ config SND_SOC_INTEL_CNL_FPGA Select Y if you are using FPGA. If unsure select "N". +config SND_SOC_SDW_AGGM1M2 + bool "Enable SoundWire aggregation on Master 1 and Master 2" + help + Say Y to enable SoundWire aggregation on Master1 and Master2. + endif ## SND_SOC_INTEL_SST_TOPLEVEL From c767fd3e9438197a66e990c79e1f4ac4d14fd95a Mon Sep 17 00:00:00 2001 From: "Jayanti, Satya Charitardha" Date: Wed, 4 Jan 2017 20:32:35 +0530 Subject: [PATCH 0731/1103] ASoC: rt700: codec changes for SDW Aggregation Add stream aggregation changes to codec driver. Playback and capture can be performed using two SoundWire master controllers and 2 RT700 codecs each connected to one master controller. Change-Id: I07d22afaa0e7dd4fbabe59adfe9a72b3f1e91852 Signed-off-by: Jayanti, Satya Charitardha Reviewed-on: Reviewed-by: Babu, Ramesh Reviewed-by: D M, Karthik Reviewed-by: Diwakar, Praveen Tested-by: Avati, Santosh Kumar --- sound/soc/codecs/rt700-sdw.c | 14 ++- sound/soc/codecs/rt700.c | 187 ++++++++++++++++++++++++++++++++++- sound/soc/codecs/rt700.h | 3 +- 3 files changed, 197 insertions(+), 7 deletions(-) diff --git a/sound/soc/codecs/rt700-sdw.c b/sound/soc/codecs/rt700-sdw.c index a2533edfa48a..1c935e3a05e2 100644 --- a/sound/soc/codecs/rt700-sdw.c +++ b/sound/soc/codecs/rt700-sdw.c @@ -252,7 +252,11 @@ static int rt700_register_sdw_capabilties(struct sdw_slv *sdw, dpn_cap->dpn_grouping = SDW_BLOCKGROUPCOUNT_1; dpn_cap->prepare_ch = SDW_SIMPLIFIED_CP_SM; dpn_cap->imp_def_intr_mask = 0; /* bit 0: Test Fail */ +#ifdef CONFIG_SND_SOC_SDW_AGGM1M2 + dpn_cap->min_ch_num = 1; +#else dpn_cap->min_ch_num = 2; +#endif dpn_cap->max_ch_num = 2; dpn_cap->num_ch_supported = 0; dpn_cap->ch_supported = NULL; @@ -319,7 +323,7 @@ static int rt700_sdw_probe(struct sdw_slv *sdw, ret = sdw_slave_get_bus_params(sdw, alc700_priv->params); if (ret) return -EFAULT; - return rt700_probe(&sdw->dev, regmap, sdw); + return rt700_probe(&sdw->dev, regmap, sdw, sdw_id->driver_data); } static int rt700_sdw_remove(struct sdw_slv *sdw) @@ -344,7 +348,15 @@ static const struct sdw_slv_id rt700_id[] = { {"15:02:5d:07:01:00", 0}, {"16:02:5d:07:01:00", 0}, {"17:02:5d:07:01:00", 0}, +#ifndef CONFIG_SND_SOC_INTEL_CNL_FPGA +#ifndef CONFIG_SND_SOC_SDW_AGGM1M2 {"10:02:5d:07:00:01", 0}, +#else + {"10:02:5d:07:00:01", 1}, + {"10:02:5d:07:01:02", 2}, + {"10:02:5d:07:01:03", 3}, +#endif +#endif {} }; diff --git a/sound/soc/codecs/rt700.c b/sound/soc/codecs/rt700.c index 400f7bbc6245..8fc9c00e15b8 100644 --- a/sound/soc/codecs/rt700.c +++ b/sound/soc/codecs/rt700.c @@ -849,6 +849,39 @@ static const struct snd_kcontrol_new rt700_snd_controls[] = { rt700_set_amp_gain_get, rt700_set_amp_gain_put), }; +#ifdef CONFIG_SND_SOC_SDW_AGGM1M2 +static const struct snd_kcontrol_new rt700_2_snd_controls[] = { + SOC_DOUBLE_R_EXT_TLV("DAC Front_2 Playback Volume", RT700_SET_GAIN_DAC1_H, + RT700_SET_GAIN_DAC1_L, RT700_DIR_OUT_SFT, 0x7f, 0, + rt700_set_amp_gain_get, rt700_set_amp_gain_put, + out_vol_tlv), + SOC_DOUBLE_R_EXT("ADC 08_2 Capture Switch", RT700_SET_GAIN_ADC2_H, + RT700_SET_GAIN_ADC2_L, RT700_DIR_IN_SFT, 1, 1, + rt700_set_amp_gain_get, rt700_set_amp_gain_put), + SOC_DOUBLE_R_EXT("ADC 09_2 Capture Switch", RT700_SET_GAIN_ADC1_H, + RT700_SET_GAIN_ADC1_L, RT700_DIR_IN_SFT, 1, 1, + rt700_set_amp_gain_get, rt700_set_amp_gain_put), + SOC_DOUBLE_R_EXT_TLV("ADC 08_2 Capture Volume", RT700_SET_GAIN_ADC2_H, + RT700_SET_GAIN_ADC2_L, RT700_DIR_IN_SFT, 0x7f, 0, + rt700_set_amp_gain_get, rt700_set_amp_gain_put, + out_vol_tlv), + SOC_DOUBLE_R_EXT_TLV("ADC 09_2 Capture Volume", RT700_SET_GAIN_ADC1_H, + RT700_SET_GAIN_ADC1_L, RT700_DIR_IN_SFT, 0x7f, 0, + rt700_set_amp_gain_get, rt700_set_amp_gain_put, + out_vol_tlv), + SOC_DOUBLE_R_EXT_TLV("AMIC_2 Volume", RT700_SET_GAIN_AMIC_H, + RT700_SET_GAIN_AMIC_L, RT700_DIR_IN_SFT, 3, 0, + rt700_set_amp_gain_get, rt700_set_amp_gain_put, + mic_vol_tlv), + SOC_DOUBLE_R_EXT("Speaker Playback_2 Switch", RT700_SET_GAIN_SPK_H, + RT700_SET_GAIN_SPK_L, RT700_DIR_OUT_SFT, 1, 1, + rt700_set_amp_gain_get, rt700_set_amp_gain_put), + SOC_DOUBLE_R_EXT("Headphone Playback_2 Switch", RT700_SET_GAIN_HP_H, + RT700_SET_GAIN_HP_L, RT700_DIR_OUT_SFT, 1, 1, + rt700_set_amp_gain_get, rt700_set_amp_gain_put), +}; +#endif + static int rt700_mux_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { @@ -922,12 +955,29 @@ static const char * const adc_mux_text[] = { "DMIC", }; +#ifdef CONFIG_SND_SOC_SDW_AGGM1M2 +static const char * const adc_mux_2_text[] = { + "MIC2_2", + "LINE1_2", + "LINE2_2", + "DMIC", +}; +#endif + static const SOC_ENUM_SINGLE_DECL( rt700_adc22_enum, RT700_MIXER_IN1, 0, adc_mux_text); static const SOC_ENUM_SINGLE_DECL( rt700_adc23_enum, RT700_MIXER_IN2, 0, adc_mux_text); +#ifdef CONFIG_SND_SOC_SDW_AGGM1M2 +static const SOC_ENUM_SINGLE_DECL( + rt700_adc22_2_enum, RT700_MIXER_IN1, 0, adc_mux_2_text); + +static const SOC_ENUM_SINGLE_DECL( + rt700_adc23_2_enum, RT700_MIXER_IN2, 0, adc_mux_2_text); +#endif + static const struct snd_kcontrol_new rt700_adc22_mux = SOC_DAPM_ENUM_EXT("ADC 22 Mux", rt700_adc22_enum, rt700_mux_get, rt700_mux_put); @@ -935,6 +985,15 @@ static const struct snd_kcontrol_new rt700_adc22_mux = static const struct snd_kcontrol_new rt700_adc23_mux = SOC_DAPM_ENUM_EXT("ADC 23 Mux", rt700_adc23_enum, rt700_mux_get, rt700_mux_put); +#ifdef CONFIG_SND_SOC_SDW_AGGM1M2 +static const struct snd_kcontrol_new rt700_adc22_mux_2 = + SOC_DAPM_ENUM_EXT("ADC 22 Mux_2", rt700_adc22_2_enum, + rt700_mux_get, rt700_mux_put); + +static const struct snd_kcontrol_new rt700_adc23_mux_2 = + SOC_DAPM_ENUM_EXT("ADC 23 Mux_2", rt700_adc23_2_enum, + rt700_mux_get, rt700_mux_put); +#endif static const char * const out_mux_text[] = { "Front", @@ -972,6 +1031,32 @@ static const struct snd_soc_dapm_widget rt700_dapm_widgets[] = { SND_SOC_DAPM_AIF_OUT("DP4TX", "DP4 Capture", 0, SND_SOC_NOPM, 0, 0), }; +#ifdef CONFIG_SND_SOC_SDW_AGGM1M2 +static const struct snd_soc_dapm_widget rt700_2_dapm_widgets[] = { + SND_SOC_DAPM_OUTPUT("HP_2"), + SND_SOC_DAPM_OUTPUT("SPK_2"), + SND_SOC_DAPM_INPUT("DMIC1_2"), + SND_SOC_DAPM_INPUT("DMIC2_2"), + SND_SOC_DAPM_INPUT("MIC2_2"), + SND_SOC_DAPM_INPUT("LINE1_2"), + SND_SOC_DAPM_INPUT("LINE2_2"), + SND_SOC_DAPM_DAC("DAC Front_2", NULL, RT700_SET_STREAMID_DAC1, 4, 0), + SND_SOC_DAPM_DAC("DAC Surround_2", NULL, RT700_SET_STREAMID_DAC2, 4, 0), + SND_SOC_DAPM_MUX("HPO Mux_2", SND_SOC_NOPM, 0, 0, &rt700_hp_mux), + SND_SOC_DAPM_PGA("SPK PGA_2", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_ADC("ADC 09_2", NULL, RT700_SET_STREAMID_ADC1, 4, 0), + SND_SOC_DAPM_ADC("ADC 08_2", NULL, RT700_SET_STREAMID_ADC2, 4, 0), + SND_SOC_DAPM_MUX("ADC 22 Mux_2", SND_SOC_NOPM, 0, 0, + &rt700_adc22_mux_2), + SND_SOC_DAPM_MUX("ADC 23 Mux_2", SND_SOC_NOPM, 0, 0, + &rt700_adc23_mux_2), + SND_SOC_DAPM_AIF_IN("DP1RX_2", "DP1 Playback2", 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_IN("DP3RX_2", "DP3 Playback2", 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("DP2TX_2", "DP2 Capture2", 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("DP4TX_2", "DP4 Capture2", 0, SND_SOC_NOPM, 0, 0), +}; +#endif + static const struct snd_soc_dapm_route rt700_audio_map[] = { {"DAC Front", NULL, "DP1RX"}, {"DAC Surround", NULL, "DP3RX"}, @@ -994,6 +1079,30 @@ static const struct snd_soc_dapm_route rt700_audio_map[] = { {"SPK", NULL, "SPK PGA"}, }; +#ifdef CONFIG_SND_SOC_SDW_AGGM1M2 +static const struct snd_soc_dapm_route rt700_2_audio_map[] = { + {"DAC Front_2", NULL, "DP1RX_2"}, + {"DAC Surround_2", NULL, "DP3RX_2"}, + {"DP2TX_2", NULL, "ADC 09_2"}, + {"DP4TX_2", NULL, "ADC 08_2"}, + {"ADC 09_2", NULL, "ADC 22 Mux_2"}, + {"ADC 08_2", NULL, "ADC 23 Mux_2"}, + {"ADC 22 Mux_2", "DMIC", "DMIC1_2"}, + {"ADC 22 Mux_2", "LINE1_2", "LINE1_2"}, + {"ADC 22 Mux_2", "LINE2_2", "LINE2_2"}, + {"ADC 22 Mux_2", "MIC2_2", "MIC2_2"}, + {"ADC 23 Mux_2", "DMIC", "DMIC2_2"}, + {"ADC 23 Mux_2", "LINE1_2", "LINE1_2"}, + {"ADC 23 Mux_2", "LINE2_2", "LINE2_2"}, + {"ADC 23 Mux_2", "MIC2_2", "MIC2_2"}, + {"HPO Mux_2", "Front", "DAC Front_2"}, + {"HPO Mux_2", "Surround", "DAC Surround_2"}, + {"HP_2", NULL, "HPO Mux_2"}, + {"SPK PGA_2", NULL, "DAC Front_2"}, + {"SPK_2", NULL, "SPK PGA_2"}, +}; +#endif + static int rt700_set_bias_level(struct snd_soc_component *component, enum snd_soc_bias_level level) { @@ -1035,6 +1144,18 @@ static const struct snd_soc_component_driver soc_component_dev_rt700 = { .num_dapm_routes = ARRAY_SIZE(rt700_audio_map), }; +#ifdef CONFIG_SND_SOC_SDW_AGGM1M2 +static const struct snd_soc_component_driver soc_component_dev_rt700_2 = { + .set_bias_level = rt700_set_bias_level, + .controls = rt700_2_snd_controls, + .num_controls = ARRAY_SIZE(rt700_2_snd_controls), + .dapm_widgets = rt700_2_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(rt700_2_dapm_widgets), + .dapm_routes = rt700_2_audio_map, + .num_dapm_routes = ARRAY_SIZE(rt700_2_audio_map), +}; +#endif + static int rt700_program_stream_tag(struct snd_pcm_substream *substream, struct snd_soc_dai *dai, int stream_tag) { @@ -1210,14 +1331,14 @@ static struct snd_soc_dai_driver rt700_dai[] = { .id = RT700_AIF1, .playback = { .stream_name = "DP1 Playback", - .channels_min = 2, + .channels_min = 1, .channels_max = 2, .rates = RT700_STEREO_RATES, .formats = RT700_FORMATS, }, .capture = { .stream_name = "DP2 Capture", - .channels_min = 2, + .channels_min = 1, .channels_max = 2, .rates = RT700_STEREO_RATES, .formats = RT700_FORMATS, @@ -1229,14 +1350,56 @@ static struct snd_soc_dai_driver rt700_dai[] = { .id = RT700_AIF2, .playback = { .stream_name = "DP3 Playback", - .channels_min = 2, + .channels_min = 1, .channels_max = 2, .rates = RT700_STEREO_RATES, .formats = RT700_FORMATS, }, .capture = { .stream_name = "DP4 Capture", - .channels_min = 2, + .channels_min = 1, + .channels_max = 2, + .rates = RT700_STEREO_RATES, + .formats = RT700_FORMATS, + }, + .ops = &rt700_ops, + }, +}; + +#ifdef CONFIG_SND_SOC_SDW_AGGM1M2 +static struct snd_soc_dai_driver rt700_2_dai[] = { + { + .name = "rt700-aif1_2", + .id = RT700_AIF1, + .playback = { + .stream_name = "DP1 Playback2", + .channels_min = 1, + .channels_max = 2, + .rates = RT700_STEREO_RATES, + .formats = RT700_FORMATS, + }, + .capture = { + .stream_name = "DP2 Capture2", + .channels_min = 1, + .channels_max = 2, + .rates = RT700_STEREO_RATES, + .formats = RT700_FORMATS, + }, + .ops = &rt700_ops, + }, + { + .name = "rt700-aif2_2", + .id = RT700_AIF2, + .playback = { + .stream_name = "DP3 Playback2", + .channels_min = 1, + .channels_max = 2, + .rates = RT700_STEREO_RATES, + .formats = RT700_FORMATS, + }, + .capture = { + .stream_name = "DP4 Capture2", + .channels_min = 1, .channels_max = 2, .rates = RT700_STEREO_RATES, .formats = RT700_FORMATS, @@ -1244,6 +1407,7 @@ static struct snd_soc_dai_driver rt700_dai[] = { .ops = &rt700_ops, }, }; +#endif static ssize_t rt700_index_cmd_show(struct device *dev, struct device_attribute *attr, char *buf) @@ -1538,7 +1702,8 @@ static ssize_t rt700_bra_trigger(struct device *dev, static DEVICE_ATTR(bra_trigger, 0444, rt700_bra_trigger, NULL); int rt700_probe(struct device *dev, struct regmap *regmap, - struct sdw_slave *slave) + struct sdw_slv *slave, + kernel_ulong_t driver_data) { struct rt700_priv *rt700; struct alc700 *alc700 = dev_get_drvdata(dev); @@ -1555,8 +1720,20 @@ int rt700_probe(struct device *dev, struct regmap *regmap, rt700->regmap = regmap; rt700->sdw = slave; +#ifndef CONFIG_SND_SOC_SDW_AGGM1M2 ret = devm_snd_soc_register_component(dev, &soc_component_dev_rt700, rt700_dai, ARRAY_SIZE(rt700_dai)); +#else + if (driver_data == 1) { + ret = devm_snd_soc_register_component(dev, + &soc_component_dev_rt700, + rt700_dai, ARRAY_SIZE(rt700_dai)); + } else if (driver_data == 2) { + ret = devm_snd_soc_register_component(dev, + &soc_component_dev_rt700_2, + rt700_2_dai, ARRAY_SIZE(rt700_2_dai)); + } +#endif dev_info(&slave->dev, "%s\n", __func__); /* Enable clock before setting */ diff --git a/sound/soc/codecs/rt700.h b/sound/soc/codecs/rt700.h index 3ad8b84f60f7..bcfb86340112 100644 --- a/sound/soc/codecs/rt700.h +++ b/sound/soc/codecs/rt700.h @@ -152,7 +152,8 @@ enum { }; int rt700_probe(struct device *dev, struct regmap *regmap, - struct sdw_slave *slave); + struct sdw_slv *slave, + kernel_ulong_t driver_data); int rt700_remove(struct device *dev); int hda_to_sdw(unsigned int nid, unsigned int verb, unsigned int payload, unsigned int *sdw_addr_h, unsigned int *sdw_data_h, From 5f88781f98dd681c1d8096776f394c9dea61ab09 Mon Sep 17 00:00:00 2001 From: "Jayanti, Satya Charitardha" Date: Wed, 4 Jan 2017 20:38:56 +0530 Subject: [PATCH 0732/1103] ASoC: Intel: Boards: Add SDW Aggregation changes This patch makes machine driver changes to enable Aggregation feature for playback and capture on SoundWire Master 1 and Master 2 with RT700 codec Change-Id: I51b2c3de040621a2dd1989ad2e2ca1e4f70b748f Signed-off-by: Jayanti, Satya Charitardha Reviewed-on: Reviewed-by: Singh, Guneshwor O Reviewed-by: D M, Karthik Reviewed-by: Diwakar, Praveen Tested-by: Avati, Santosh Kumar --- sound/soc/intel/boards/cnl_rt700.c | 70 ++++++++++++++++++++++++++++++ 1 file changed, 70 insertions(+) diff --git a/sound/soc/intel/boards/cnl_rt700.c b/sound/soc/intel/boards/cnl_rt700.c index 740062a4108a..ee8fe3934571 100644 --- a/sound/soc/intel/boards/cnl_rt700.c +++ b/sound/soc/intel/boards/cnl_rt700.c @@ -44,13 +44,24 @@ struct cnl_rt700_mc_private { int bt_mode; }; +#ifndef CONFIG_SND_SOC_SDW_AGGM1M2 static const struct snd_soc_dapm_widget cnl_rt700_widgets[] = { SND_SOC_DAPM_HP("Headphones", NULL), SND_SOC_DAPM_MIC("AMIC", NULL), SND_SOC_DAPM_MIC("SoC DMIC", NULL), SND_SOC_DAPM_SPK("Speaker", NULL), }; +#else +static const struct snd_soc_dapm_widget cnl_rt700_widgets[] = { + SND_SOC_DAPM_HP("Headphones", NULL), + SND_SOC_DAPM_HP("Headphones_2", NULL), + SND_SOC_DAPM_MIC("AMIC", NULL), + SND_SOC_DAPM_MIC("AMIC_2", NULL), + SND_SOC_DAPM_MIC("SoC DMIC", NULL), +}; +#endif +#ifndef CONFIG_SND_SOC_SDW_AGGM1M2 static const struct snd_soc_dapm_route cnl_rt700_map[] = { /*Headphones*/ { "Headphones", NULL, "HP" }, @@ -72,12 +83,46 @@ static const struct snd_soc_dapm_route cnl_rt700_map[] = { {"dmic01_hifi", NULL, "DMIC01 Rx"}, }; +#else +static const struct snd_soc_dapm_route cnl_rt700_map[] = { + /*Headphones*/ + { "Headphones", NULL, "HP" }, + { "Headphones_2", NULL, "HP_2" }, + { "MIC2", NULL, "AMIC" }, + { "MIC2_2", NULL, "AMIC_2" }, + + /* SWM map link the SWM outs to codec AIF */ + { "DP1 Playback", NULL, "SDW Tx10"}, + { "SDW Tx10", NULL, "sdw_codec0_out"}, + { "DP1 Playback2", NULL, "SDW2 Tx"}, + { "SDW2 Tx", NULL, "sdw_codec0_out"}, + + { "sdw_codec0_in", NULL, "SDW Rx10" }, + { "SDW Rx10", NULL, "DP2 Capture" }, + {"sdw_codec0_in", NULL, "SDW2 Rx"}, + {"SDW2 Rx", NULL, "DP2 Capture2"}, + + {"DMic", NULL, "SoC DMIC"}, + {"DMIC01 Rx", NULL, "Capture"}, + {"dmic01_hifi", NULL, "DMIC01 Rx"}, + +}; +#endif +#ifndef CONFIG_SND_SOC_SDW_AGGM1M2 static const struct snd_kcontrol_new cnl_rt700_controls[] = { SOC_DAPM_PIN_SWITCH("Headphones"), SOC_DAPM_PIN_SWITCH("AMIC"), SOC_DAPM_PIN_SWITCH("Speaker"), }; +#else +static const struct snd_kcontrol_new cnl_rt700_controls[] = { + SOC_DAPM_PIN_SWITCH("Headphones"), + SOC_DAPM_PIN_SWITCH("Headphones_2"), + SOC_DAPM_PIN_SWITCH("AMIC"), + SOC_DAPM_PIN_SWITCH("AMIC_2"), +}; +#endif static int cnl_rt700_codec_fixup(struct snd_soc_pcm_runtime *rtd, @@ -93,7 +138,12 @@ static int cnl_rt700_codec_fixup(struct snd_soc_pcm_runtime *rtd, pr_debug("Invoked %s for dailink %s\n", __func__, rtd->dai_link->name); slot_width = 24; rate->min = rate->max = 48000; +#ifdef CONFIG_SND_SOC_SDW_AGGM1M2 + channels->min = 1; + channels->max = 2; +#else channels->min = channels->max = 2; +#endif snd_mask_none(hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT)); snd_mask_set(hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT), (unsigned int __force)SNDRV_PCM_FORMAT_S24_LE); @@ -125,6 +175,7 @@ static const char cname[] = "sdw-slave1-10:02:5d:07:00:01"; #endif static struct snd_soc_dai_link cnl_rt700_msic_dailink[] = { +#ifndef CONFIG_SND_SOC_SDW_AGGM1M2 { .name = "SDW0-Codec", .cpu_dai_name = "SDW Pin", @@ -137,18 +188,37 @@ static struct snd_soc_dai_link cnl_rt700_msic_dailink[] = { .dpcm_playback = 1, .dpcm_capture = 1, }, +#endif { .name = "SDW1-Codec", .cpu_dai_name = "SDW10 Pin", .platform_name = pname, .codec_name = cname, +#ifndef CONFIG_SND_SOC_SDW_AGGM1M2 .codec_dai_name = "rt700-aif2", +#else + .codec_dai_name = "rt700-aif1", +#endif + .be_hw_params_fixup = cnl_rt700_codec_fixup, + .ignore_suspend = 1, + .no_pcm = 1, + .dpcm_playback = 1, + .dpcm_capture = 1, + }, +#ifdef CONFIG_SND_SOC_SDW_AGGM1M2 + { + .name = "SDW2-Codec", + .cpu_dai_name = "SDW2 Pin", + .platform_name = pname, + .codec_name = "sdw-slave2-10:02:5d:07:01:02", + .codec_dai_name = "rt700-aif1_2", .be_hw_params_fixup = cnl_rt700_codec_fixup, .ignore_suspend = 1, .no_pcm = 1, .dpcm_playback = 1, .dpcm_capture = 1, }, +#endif { .name = "dmic01", .cpu_dai_name = "DMIC01 Pin", From c27aef749416fb5d41940156a32432174f0c41a3 Mon Sep 17 00:00:00 2001 From: Leoni Prodduvaka Date: Thu, 16 Mar 2017 21:49:33 +0530 Subject: [PATCH 0733/1103] ASoC: Intel: Change sst_ipc_tx_message_wait api to return valid data Since the firmware returns the rx_bytes as a part of ipc response the api parameter is changed to pointer to accommodate the change. Change-Id: I7d5ae8bfaa1e7514fe91b03e2a4e9113956c984a Signed-off-by: Leoni Prodduvaka Reviewed-on: Reviewed-by: R, Dharageswari Reviewed-by: Kp, Jeeja Reviewed-by: Diwakar, Praveen Tested-by: Sm, Bhadur A --- sound/soc/intel/baytrail/sst-baytrail-ipc.c | 7 +++-- sound/soc/intel/common/sst-ipc.c | 35 +++++++++++++++------ sound/soc/intel/common/sst-ipc.h | 3 +- sound/soc/intel/haswell/sst-haswell-ipc.c | 28 ++++++++++------- sound/soc/intel/skylake/skl-debug.c | 16 ++++++---- sound/soc/intel/skylake/skl-messages.c | 3 +- sound/soc/intel/skylake/skl-sst-ipc.c | 34 ++++++++++---------- sound/soc/intel/skylake/skl-sst-ipc.h | 2 +- 8 files changed, 78 insertions(+), 50 deletions(-) diff --git a/sound/soc/intel/baytrail/sst-baytrail-ipc.c b/sound/soc/intel/baytrail/sst-baytrail-ipc.c index 260447da32b8..403dac3a514b 100644 --- a/sound/soc/intel/baytrail/sst-baytrail-ipc.c +++ b/sound/soc/intel/baytrail/sst-baytrail-ipc.c @@ -420,13 +420,14 @@ int sst_byt_stream_commit(struct sst_byt *byt, struct sst_byt_stream *stream) struct sst_byt_alloc_response *reply = &stream->reply; u64 header; int ret; + size_t rx_bytes = sizeof(*reply); header = sst_byt_header(IPC_IA_ALLOC_STREAM, sizeof(*str_req) + sizeof(u32), true, stream->str_id); ret = sst_ipc_tx_message_wait(&byt->ipc, header, str_req, sizeof(*str_req), - reply, sizeof(*reply)); + reply, &rx_bytes); if (ret < 0) { dev_err(byt->dev, "ipc: error stream commit failed\n"); return ret; @@ -448,7 +449,7 @@ int sst_byt_stream_free(struct sst_byt *byt, struct sst_byt_stream *stream) goto out; header = sst_byt_header(IPC_IA_FREE_STREAM, 0, false, stream->str_id); - ret = sst_ipc_tx_message_wait(&byt->ipc, header, NULL, 0, NULL, 0); + ret = sst_ipc_tx_message_wait(&byt->ipc, header, NULL, 0, NULL, NULL); if (ret < 0) { dev_err(byt->dev, "ipc: free stream %d failed\n", stream->str_id); @@ -473,7 +474,7 @@ static int sst_byt_stream_operations(struct sst_byt *byt, int type, header = sst_byt_header(type, 0, false, stream_id); if (wait) return sst_ipc_tx_message_wait(&byt->ipc, header, NULL, - 0, NULL, 0); + 0, NULL, NULL); else return sst_ipc_tx_message_nowait(&byt->ipc, header, NULL, 0); diff --git a/sound/soc/intel/common/sst-ipc.c b/sound/soc/intel/common/sst-ipc.c index dcff13802c00..f4b09503518e 100644 --- a/sound/soc/intel/common/sst-ipc.c +++ b/sound/soc/intel/common/sst-ipc.c @@ -52,7 +52,7 @@ static struct ipc_message *msg_get_empty(struct sst_generic_ipc *ipc) } static int tx_wait_done(struct sst_generic_ipc *ipc, - struct ipc_message *msg, void *rx_data) + struct ipc_message *msg, void *rx_data, size_t *rx_bytes) { unsigned long flags; int ret; @@ -71,11 +71,21 @@ static int tx_wait_done(struct sst_generic_ipc *ipc, } else { /* copy the data returned from DSP */ - if (msg->rx_size) + if ((rx_bytes != NULL) && + (msg->rx_size > *rx_bytes)) { + dev_err(ipc->dev, "rx size is more than expected\n"); + ret = -EINVAL; + goto err; + } + + if (msg->rx_size) { + if (rx_bytes != NULL) + *rx_bytes = msg->rx_size; memcpy(rx_data, msg->rx_data, msg->rx_size); + } ret = msg->errno; } - +err: list_add_tail(&msg->list, &ipc->empty_list); spin_unlock_irqrestore(&ipc->dsp->spinlock, flags); return ret; @@ -83,7 +93,7 @@ static int tx_wait_done(struct sst_generic_ipc *ipc, static int ipc_tx_message(struct sst_generic_ipc *ipc, u64 header, void *tx_data, size_t tx_bytes, void *rx_data, - size_t rx_bytes, int wait) + size_t *rx_bytes, int wait) { struct ipc_message *msg; unsigned long flags; @@ -98,7 +108,12 @@ static int ipc_tx_message(struct sst_generic_ipc *ipc, u64 header, msg->header = header; msg->tx_size = tx_bytes; - msg->rx_size = rx_bytes; + + if (!rx_bytes) + msg->rx_size = 0; + else + msg->rx_size = *rx_bytes; + msg->wait = wait; msg->errno = 0; msg->pending = false; @@ -112,7 +127,8 @@ static int ipc_tx_message(struct sst_generic_ipc *ipc, u64 header, spin_unlock_irqrestore(&ipc->dsp->spinlock, flags); if (wait) - return tx_wait_done(ipc, msg, rx_data); + return tx_wait_done(ipc, msg, rx_data, + rx_bytes); else return 0; } @@ -183,7 +199,8 @@ static void ipc_tx_msgs(struct work_struct *work) } int sst_ipc_tx_message_wait(struct sst_generic_ipc *ipc, u64 header, - void *tx_data, size_t tx_bytes, void *rx_data, size_t rx_bytes) + void *tx_data, size_t tx_bytes, void *rx_data, + size_t *rx_bytes) { int ret; @@ -211,7 +228,7 @@ int sst_ipc_tx_message_nowait(struct sst_generic_ipc *ipc, u64 header, void *tx_data, size_t tx_bytes) { return ipc_tx_message(ipc, header, tx_data, tx_bytes, - NULL, 0, 0); + NULL, NULL, 0); } EXPORT_SYMBOL_GPL(sst_ipc_tx_message_nowait); @@ -219,7 +236,7 @@ int sst_ipc_tx_message_nopm(struct sst_generic_ipc *ipc, u64 header, void *tx_data, size_t tx_bytes, void *rx_data, size_t rx_bytes) { return ipc_tx_message(ipc, header, tx_data, tx_bytes, - rx_data, rx_bytes, 1); + rx_data, &rx_bytes, 1); } EXPORT_SYMBOL_GPL(sst_ipc_tx_message_nopm); diff --git a/sound/soc/intel/common/sst-ipc.h b/sound/soc/intel/common/sst-ipc.h index 7ed42a640ad6..4cfa9e37ac28 100644 --- a/sound/soc/intel/common/sst-ipc.h +++ b/sound/soc/intel/common/sst-ipc.h @@ -75,7 +75,8 @@ struct sst_generic_ipc { }; int sst_ipc_tx_message_wait(struct sst_generic_ipc *ipc, u64 header, - void *tx_data, size_t tx_bytes, void *rx_data, size_t rx_bytes); + void *tx_data, size_t tx_bytes, void *rx_data, + size_t *rx_bytes); int sst_ipc_tx_message_nowait(struct sst_generic_ipc *ipc, u64 header, void *tx_data, size_t tx_bytes); diff --git a/sound/soc/intel/haswell/sst-haswell-ipc.c b/sound/soc/intel/haswell/sst-haswell-ipc.c index d33bdaf92c57..557f7800f373 100644 --- a/sound/soc/intel/haswell/sst-haswell-ipc.c +++ b/sound/soc/intel/haswell/sst-haswell-ipc.c @@ -826,10 +826,11 @@ int sst_hsw_fw_get_version(struct sst_hsw *hsw, struct sst_hsw_ipc_fw_version *version) { int ret; + size_t rx_bytes = sizeof(*version); ret = sst_ipc_tx_message_wait(&hsw->ipc, IPC_GLB_TYPE(IPC_GLB_GET_FW_VERSION), - NULL, 0, version, sizeof(*version)); + NULL, 0, version, &rx_bytes); if (ret < 0) dev_err(hsw->dev, "error: get version failed\n"); @@ -893,7 +894,7 @@ int sst_hsw_stream_set_volume(struct sst_hsw *hsw, } ret = sst_ipc_tx_message_wait(&hsw->ipc, header, req, - sizeof(*req), NULL, 0); + sizeof(*req), NULL, NULL); if (ret < 0) { dev_err(hsw->dev, "error: set stream volume failed\n"); return ret; @@ -959,7 +960,7 @@ int sst_hsw_mixer_set_volume(struct sst_hsw *hsw, u32 stage_id, u32 channel, req.target_volume = volume; ret = sst_ipc_tx_message_wait(&hsw->ipc, header, &req, - sizeof(req), NULL, 0); + sizeof(req), NULL, NULL); if (ret < 0) { dev_err(hsw->dev, "error: set mixer volume failed\n"); return ret; @@ -1018,7 +1019,7 @@ int sst_hsw_stream_free(struct sst_hsw *hsw, struct sst_hsw_stream *stream) header = IPC_GLB_TYPE(IPC_GLB_FREE_STREAM); ret = sst_ipc_tx_message_wait(&hsw->ipc, header, &stream->free_req, - sizeof(stream->free_req), NULL, 0); + sizeof(stream->free_req), NULL, NULL); if (ret < 0) { dev_err(hsw->dev, "error: free stream %d failed\n", stream->free_req.stream_id); @@ -1194,6 +1195,7 @@ int sst_hsw_stream_commit(struct sst_hsw *hsw, struct sst_hsw_stream *stream) struct sst_hsw_ipc_stream_alloc_reply *reply = &stream->reply; u32 header; int ret; + size_t rx_bytes = sizeof(*reply); if (!stream) { dev_warn(hsw->dev, "warning: stream is NULL, no stream to commit, ignore it.\n"); @@ -1210,7 +1212,7 @@ int sst_hsw_stream_commit(struct sst_hsw *hsw, struct sst_hsw_stream *stream) header = IPC_GLB_TYPE(IPC_GLB_ALLOCATE_STREAM); ret = sst_ipc_tx_message_wait(&hsw->ipc, header, str_req, - sizeof(*str_req), reply, sizeof(*reply)); + sizeof(*str_req), reply, &rx_bytes); if (ret < 0) { dev_err(hsw->dev, "error: stream commit failed\n"); return ret; @@ -1253,6 +1255,7 @@ int sst_hsw_mixer_get_info(struct sst_hsw *hsw) struct sst_hsw_ipc_stream_info_reply *reply; u32 header; int ret; + size_t rx_bytes = sizeof(*reply); reply = &hsw->mixer_info; header = IPC_GLB_TYPE(IPC_GLB_GET_MIXER_STREAM_INFO); @@ -1260,7 +1263,7 @@ int sst_hsw_mixer_get_info(struct sst_hsw *hsw) trace_ipc_request("get global mixer info", 0); ret = sst_ipc_tx_message_wait(&hsw->ipc, header, NULL, 0, - reply, sizeof(*reply)); + reply, &rx_bytes); if (ret < 0) { dev_err(hsw->dev, "error: get stream info failed\n"); return ret; @@ -1282,7 +1285,7 @@ static int sst_hsw_stream_operations(struct sst_hsw *hsw, int type, if (wait) return sst_ipc_tx_message_wait(&hsw->ipc, header, - NULL, 0, NULL, 0); + NULL, 0, NULL, NULL); else return sst_ipc_tx_message_nowait(&hsw->ipc, header, NULL, 0); } @@ -1412,7 +1415,7 @@ int sst_hsw_device_set_config(struct sst_hsw *hsw, header = IPC_GLB_TYPE(IPC_GLB_SET_DEVICE_FORMATS); ret = sst_ipc_tx_message_wait(&hsw->ipc, header, &config, - sizeof(config), NULL, 0); + sizeof(config), NULL, NULL); if (ret < 0) dev_err(hsw->dev, "error: set device formats failed\n"); @@ -1426,6 +1429,7 @@ int sst_hsw_dx_set_state(struct sst_hsw *hsw, { u32 header, state_; int ret, item; + size_t rx_bytes = sizeof(*dx); header = IPC_GLB_TYPE(IPC_GLB_ENTER_DX_STATE); state_ = state; @@ -1433,7 +1437,7 @@ int sst_hsw_dx_set_state(struct sst_hsw *hsw, trace_ipc_request("PM enter Dx state", state); ret = sst_ipc_tx_message_wait(&hsw->ipc, header, &state_, - sizeof(state_), dx, sizeof(*dx)); + sizeof(state_), dx, &rx_bytes); if (ret < 0) { dev_err(hsw->dev, "ipc: error set dx state %d failed\n", state); return ret; @@ -1948,7 +1952,7 @@ int sst_hsw_module_enable(struct sst_hsw *hsw, config.map.module_entries[0].entry_point); ret = sst_ipc_tx_message_wait(&hsw->ipc, header, - &config, sizeof(config), NULL, 0); + &config, sizeof(config), NULL, NULL); if (ret < 0) dev_err(dev, "ipc: module enable failed - %d\n", ret); else @@ -1986,7 +1990,7 @@ int sst_hsw_module_disable(struct sst_hsw *hsw, IPC_MODULE_OPERATION(IPC_MODULE_DISABLE) | IPC_MODULE_ID(module_id); - ret = sst_ipc_tx_message_wait(&hsw->ipc, header, NULL, 0, NULL, 0); + ret = sst_ipc_tx_message_wait(&hsw->ipc, header, NULL, 0, NULL, NULL); if (ret < 0) dev_err(dev, "module disable failed - %d\n", ret); else @@ -2039,7 +2043,7 @@ int sst_hsw_module_set_param(struct sst_hsw *hsw, parameter->data_size = param_size; ret = sst_ipc_tx_message_wait(&hsw->ipc, header, - parameter, transfer_parameter_size , NULL, 0); + parameter, transfer_parameter_size, NULL, NULL); if (ret < 0) dev_err(dev, "ipc: module set parameter failed - %d\n", ret); diff --git a/sound/soc/intel/skylake/skl-debug.c b/sound/soc/intel/skylake/skl-debug.c index 8a20f86544d2..5cc274a8d531 100644 --- a/sound/soc/intel/skylake/skl-debug.c +++ b/sound/soc/intel/skylake/skl-debug.c @@ -231,10 +231,12 @@ static ssize_t mod_control_write(struct file *file, if (mbsz) retval = skl_ipc_get_large_config(&ctx->ipc, &msg, - large_data, &(mod_set_get->mailbx[0]), mbsz); + large_data, &(mod_set_get->mailbx[0]), + mbsz, NULL); else retval = skl_ipc_get_large_config(&ctx->ipc, - &msg, large_data, NULL, 0); + &msg, large_data, NULL, + 0, NULL); d->ipc_data[0] = msg.param_data_size; memcpy(&d->ipc_data[1], large_data, msg.param_data_size); @@ -256,11 +258,11 @@ static ssize_t mod_control_write(struct file *file, default: if (mbsz) retval = sst_ipc_tx_message_wait(&ctx->ipc, *ipc_header, - mod_set_get->mailbx, mbsz, NULL, 0); + mod_set_get->mailbx, mbsz, NULL, NULL); else retval = sst_ipc_tx_message_wait(&ctx->ipc, *ipc_header, - NULL, 0, NULL, 0); + NULL, 0, NULL, NULL); d->ipc_data[0] = 0; break; @@ -717,10 +719,12 @@ static ssize_t adsp_control_write(struct file *file, if (tx_param == 1) skl_ipc_get_large_config(&ctx->ipc, &msg, - ipc_data, &tx_data, sizeof(u32)); + ipc_data, &tx_data, + sizeof(u32), NULL); else skl_ipc_get_large_config(&ctx->ipc, &msg, - ipc_data, NULL, 0); + ipc_data, NULL, + 0, NULL); memset(&d->fw_ipc_data.mailbx[0], 0, DSP_BUF); diff --git a/sound/soc/intel/skylake/skl-messages.c b/sound/soc/intel/skylake/skl-messages.c index 79451737be9e..4a0ad2916388 100644 --- a/sound/soc/intel/skylake/skl-messages.c +++ b/sound/soc/intel/skylake/skl-messages.c @@ -2347,5 +2347,6 @@ int skl_get_module_params(struct skl_sst *ctx, u32 *params, int size, msg.param_data_size = size; msg.large_param_id = param_id; - return skl_ipc_get_large_config(&ctx->ipc, &msg, params, NULL, 0); + return skl_ipc_get_large_config(&ctx->ipc, &msg, params, NULL, + 0, NULL); } diff --git a/sound/soc/intel/skylake/skl-sst-ipc.c b/sound/soc/intel/skylake/skl-sst-ipc.c index 7b6ce92c9d0e..bf9f97bb36de 100644 --- a/sound/soc/intel/skylake/skl-sst-ipc.c +++ b/sound/soc/intel/skylake/skl-sst-ipc.c @@ -676,7 +676,7 @@ int skl_ipc_create_pipeline(struct sst_generic_ipc *ipc, header.extension = IPC_PPL_LP_MODE(lp_mode); dev_dbg(ipc->dev, "In %s header=%d\n", __func__, header.primary); - ret = sst_ipc_tx_message_wait(ipc, *ipc_header, NULL, 0, NULL, 0); + ret = sst_ipc_tx_message_wait(ipc, *ipc_header, NULL, 0, NULL, NULL); if (ret < 0) { dev_err(ipc->dev, "ipc: create pipeline fail, err: %d\n", ret); return ret; @@ -698,7 +698,7 @@ int skl_ipc_delete_pipeline(struct sst_generic_ipc *ipc, u8 instance_id) header.primary |= IPC_INSTANCE_ID(instance_id); dev_dbg(ipc->dev, "In %s header=%d\n", __func__, header.primary); - ret = sst_ipc_tx_message_wait(ipc, *ipc_header, NULL, 0, NULL, 0); + ret = sst_ipc_tx_message_wait(ipc, *ipc_header, NULL, 0, NULL, NULL); if (ret < 0) { dev_err(ipc->dev, "ipc: delete pipeline failed, err %d\n", ret); return ret; @@ -722,7 +722,7 @@ int skl_ipc_set_pipeline_state(struct sst_generic_ipc *ipc, header.primary |= IPC_PPL_STATE(state); dev_dbg(ipc->dev, "In %s header=%d\n", __func__, header.primary); - ret = sst_ipc_tx_message_wait(ipc, *ipc_header, NULL, 0, NULL, 0); + ret = sst_ipc_tx_message_wait(ipc, *ipc_header, NULL, 0, NULL, NULL); if (ret < 0) { dev_err(ipc->dev, "ipc: set pipeline state failed, err: %d\n", ret); return ret; @@ -745,7 +745,7 @@ skl_ipc_save_pipeline(struct sst_generic_ipc *ipc, u8 instance_id, int dma_id) header.extension = IPC_DMA_ID(dma_id); dev_dbg(ipc->dev, "In %s header=%d\n", __func__, header.primary); - ret = sst_ipc_tx_message_wait(ipc, *ipc_header, NULL, 0, NULL, 0); + ret = sst_ipc_tx_message_wait(ipc, *ipc_header, NULL, 0, NULL, NULL); if (ret < 0) { dev_err(ipc->dev, "ipc: save pipeline failed, err: %d\n", ret); return ret; @@ -767,7 +767,7 @@ int skl_ipc_restore_pipeline(struct sst_generic_ipc *ipc, u8 instance_id) header.primary |= IPC_INSTANCE_ID(instance_id); dev_dbg(ipc->dev, "In %s header=%d\n", __func__, header.primary); - ret = sst_ipc_tx_message_wait(ipc, *ipc_header, NULL, 0, NULL, 0); + ret = sst_ipc_tx_message_wait(ipc, *ipc_header, NULL, 0, NULL, NULL); if (ret < 0) { dev_err(ipc->dev, "ipc: restore pipeline failed, err: %d\n", ret); return ret; @@ -793,7 +793,7 @@ int skl_ipc_set_dx(struct sst_generic_ipc *ipc, u8 instance_id, dev_dbg(ipc->dev, "In %s primary =%x ext=%x\n", __func__, header.primary, header.extension); ret = sst_ipc_tx_message_wait(ipc, *ipc_header, - dx, sizeof(*dx), NULL, 0); + dx, sizeof(*dx), NULL, NULL); if (ret < 0) { dev_err(ipc->dev, "ipc: set dx failed, err %d\n", ret); return ret; @@ -819,7 +819,7 @@ int skl_ipc_delete_instance(struct sst_generic_ipc *ipc, dev_dbg(ipc->dev, "In %s primary =%x ext=%x\n", __func__, header.primary, header.extension); ret = sst_ipc_tx_message_wait(ipc, *ipc_header, NULL, - msg->param_data_size, NULL, 0); + msg->param_data_size, NULL, NULL); if (ret < 0) { dev_err(ipc->dev, "ipc: delete instance failed\n"); @@ -857,7 +857,7 @@ int skl_ipc_init_instance(struct sst_generic_ipc *ipc, dev_dbg(ipc->dev, "In %s primary =%x ext=%x\n", __func__, header.primary, header.extension); ret = sst_ipc_tx_message_wait(ipc, *ipc_header, param_data, - msg->param_data_size, NULL, 0); + msg->param_data_size, NULL, NULL); if (ret < 0) { dev_err(ipc->dev, "ipc: init instance failed\n"); @@ -889,7 +889,7 @@ int skl_ipc_bind_unbind(struct sst_generic_ipc *ipc, dev_dbg(ipc->dev, "In %s hdr=%x ext=%x\n", __func__, header.primary, header.extension); - ret = sst_ipc_tx_message_wait(ipc, *ipc_header, NULL, 0, NULL, 0); + ret = sst_ipc_tx_message_wait(ipc, *ipc_header, NULL, 0, NULL, NULL); if (ret < 0) { dev_err(ipc->dev, "ipc: bind/unbind failed\n"); return ret; @@ -917,8 +917,8 @@ int skl_ipc_load_modules(struct sst_generic_ipc *ipc, header.primary |= IPC_GLB_TYPE(IPC_GLB_LOAD_MULTIPLE_MODS); header.primary |= IPC_LOAD_MODULE_CNT(module_cnt); - ret = sst_ipc_tx_message_nowait(ipc, *ipc_header, data, - (sizeof(u16) * module_cnt)); + ret = sst_ipc_tx_message_wait(ipc, *ipc_header, data, + (sizeof(u16) * module_cnt), NULL, NULL); if (ret < 0) dev_err(ipc->dev, "ipc: load modules failed :%d\n", ret); @@ -939,7 +939,7 @@ int skl_ipc_unload_modules(struct sst_generic_ipc *ipc, u8 module_cnt, header.primary |= IPC_LOAD_MODULE_CNT(module_cnt); ret = sst_ipc_tx_message_wait(ipc, *ipc_header, data, - (sizeof(u16) * module_cnt), NULL, 0); + (sizeof(u16) * module_cnt), NULL, NULL); if (ret < 0) dev_err(ipc->dev, "ipc: unload modules failed :%d\n", ret); @@ -980,7 +980,7 @@ int skl_ipc_set_large_config(struct sst_generic_ipc *ipc, (unsigned)data_offset, (unsigned)tx_size); ret = sst_ipc_tx_message_wait(ipc, *ipc_header, ((char *)param) + data_offset, - tx_size, NULL, 0); + tx_size, NULL, NULL); if (ret < 0) { dev_err(ipc->dev, "ipc: set large config fail, err: %d\n", ret); @@ -1003,7 +1003,7 @@ EXPORT_SYMBOL_GPL(skl_ipc_set_large_config); int skl_ipc_get_large_config(struct sst_generic_ipc *ipc, struct skl_ipc_large_config_msg *msg, u32 *param, - u32 *txparam, u32 size) + u32 *txparam, u32 tx_bytes, size_t *rx_bytes) { struct skl_ipc_header header = {0}; u64 *ipc_header = (u64 *)(&header); @@ -1040,8 +1040,8 @@ int skl_ipc_get_large_config(struct sst_generic_ipc *ipc, (unsigned)data_offset, (unsigned)rx_size); ret = sst_ipc_tx_message_wait(ipc, *ipc_header, - ((char *)txparam), size, ((char *)param) + data_offset, - rx_size); + ((char *)txparam), tx_bytes, + ((char *)param) + data_offset, &rx_size); if (ret < 0) { dev_err(ipc->dev, "ipc: get large config fail, err: %d\n", ret); @@ -1077,7 +1077,7 @@ int skl_sst_ipc_load_library(struct sst_generic_ipc *ipc, if (wait) ret = sst_ipc_tx_message_wait(ipc, *ipc_header, - NULL, 0, NULL, 0); + NULL, 0, NULL, NULL); else ret = sst_ipc_tx_message_nowait(ipc, *ipc_header, NULL, 0); diff --git a/sound/soc/intel/skylake/skl-sst-ipc.h b/sound/soc/intel/skylake/skl-sst-ipc.h index 6c037d2c8a3c..bc02740e5525 100644 --- a/sound/soc/intel/skylake/skl-sst-ipc.h +++ b/sound/soc/intel/skylake/skl-sst-ipc.h @@ -259,7 +259,7 @@ int skl_ipc_set_large_config(struct sst_generic_ipc *ipc, int skl_ipc_get_large_config(struct sst_generic_ipc *ipc, struct skl_ipc_large_config_msg *msg, u32 *param, - u32 *txparam, u32 size); + u32 *txparam, u32 tx_bytes, size_t *rx_bytes); int skl_sst_ipc_load_library(struct sst_generic_ipc *ipc, u8 dma_id, u8 table_id, bool wait); From ff710e732599eeeceea4359d43a913840c8c434b Mon Sep 17 00:00:00 2001 From: Leoni Prodduvaka Date: Thu, 16 Mar 2017 21:50:51 +0530 Subject: [PATCH 0734/1103] ASoC: Intel: Skylake: Extract the receive response size returned by the FW The driver uses the rx_bytes value returned from firmware to copy the response of the ipc. Change-Id: I76b78c01ef83b28d6328de66249af84b4c99700f Signed-off-by: Dharageswari R Signed-off-by: Leoni Prodduvaka Reviewed-on: Reviewed-by: Koul, Vinod Reviewed-by: Diwakar, Praveen Reviewed-by: Kp, Jeeja Tested-by: Sm, Bhadur A --- sound/soc/intel/skylake/skl-sst-ipc.c | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/sound/soc/intel/skylake/skl-sst-ipc.c b/sound/soc/intel/skylake/skl-sst-ipc.c index bf9f97bb36de..b3591c8de471 100644 --- a/sound/soc/intel/skylake/skl-sst-ipc.c +++ b/sound/soc/intel/skylake/skl-sst-ipc.c @@ -475,6 +475,10 @@ void skl_ipc_process_reply(struct sst_generic_ipc *ipc, if (reply == IPC_GLB_REPLY_SUCCESS) { dev_dbg(ipc->dev, "ipc FW reply %x: success\n", header.primary); /* copy the rx data from the mailbox */ + if (IPC_GLB_NOTIFY_MSG_TYPE(header.primary) == + IPC_MOD_LARGE_CONFIG_GET) + msg->rx_size = header.extension & + IPC_DATA_OFFSET_SZ_MASK; sst_dsp_inbox_read(ipc->dsp, msg->rx_data, msg->rx_size); switch (IPC_GLB_NOTIFY_MSG_TYPE(header.primary)) { case IPC_GLB_LOAD_MULTIPLE_MODS: @@ -1039,15 +1043,27 @@ int skl_ipc_get_large_config(struct sst_generic_ipc *ipc, dev_dbg(ipc->dev, "receiving offset: %#x, size: %#x\n", (unsigned)data_offset, (unsigned)rx_size); + if (rx_bytes != NULL) + *rx_bytes = rx_size; + ret = sst_ipc_tx_message_wait(ipc, *ipc_header, ((char *)txparam), tx_bytes, - ((char *)param) + data_offset, &rx_size); + ((char *)param) + data_offset, rx_bytes); + if (ret < 0) { dev_err(ipc->dev, "ipc: get large config fail, err: %d\n", ret); return ret; } + /* exit as this is the final block */ + if (header.extension | (0 << IPC_FINAL_BLOCK_SHIFT)) + break; + + if (rx_bytes != NULL) + rx_size = *rx_bytes; + sz_remaining -= rx_size; + data_offset = msg->param_data_size - sz_remaining; /* clear the fields */ From d9b163a7306bd6def8f0e7b958ab5314a229d02b Mon Sep 17 00:00:00 2001 From: Leoni Prodduvaka Date: Tue, 7 Feb 2017 18:42:40 +0530 Subject: [PATCH 0735/1103] ASoC: Intel: Skylake: Querying FW CONFIG information FW Config information is queried from the firmware. This information is useful to set the behavior of the driver. [Ex: Memory reclaim] Change-Id: Idba891a1db4f61bdcce26120974409ffc484770c Signed-off-by: Leoni Prodduvaka Reviewed-on: Reviewed-by: Nc, Shreyas Reviewed-by: R, Dharageswari Reviewed-by: Koul, Vinod Reviewed-by: Diwakar, Praveen Tested-by: Sm, Bhadur A --- sound/soc/intel/skylake/skl-sst-dsp.h | 1 + sound/soc/intel/skylake/skl-sst-ipc.h | 18 +++++++++++++++++ sound/soc/intel/skylake/skl-sst-utils.c | 26 +++++++++++++++++++++++++ sound/soc/intel/skylake/skl-topology.h | 18 ----------------- 4 files changed, 45 insertions(+), 18 deletions(-) diff --git a/sound/soc/intel/skylake/skl-sst-dsp.h b/sound/soc/intel/skylake/skl-sst-dsp.h index e874e75a15fc..ea6e57e6899f 100644 --- a/sound/soc/intel/skylake/skl-sst-dsp.h +++ b/sound/soc/intel/skylake/skl-sst-dsp.h @@ -262,4 +262,5 @@ int skl_prepare_lib_load(struct skl_sst *skl, struct skl_lib_info *linfo, unsigned int hdr_offset, int index); void skl_release_library(struct skl_lib_info *linfo, int lib_count); +int skl_get_firmware_configuration(struct sst_dsp *ctx); #endif /*__SKL_SST_DSP_H__*/ diff --git a/sound/soc/intel/skylake/skl-sst-ipc.h b/sound/soc/intel/skylake/skl-sst-ipc.h index bc02740e5525..074014668f0a 100644 --- a/sound/soc/intel/skylake/skl-sst-ipc.h +++ b/sound/soc/intel/skylake/skl-sst-ipc.h @@ -39,6 +39,24 @@ enum skl_ipc_pipeline_state { PPL_RESTORED = 7 }; +enum base_fw_run_time_param { + ADSP_PROPERTIES = 0, + ADSP_RESOURCE_STATE = 1, + NOTIFICATION_MASK = 3, + ASTATE_TABLE = 4, + DMA_CONTROL = 5, + ENABLE_LOGS = 6, + FIRMWARE_CONFIG = 7, + HARDWARE_CONFIG = 8, + MODULES_INFO = 9, + PIPELINE_LIST_INFO = 10, + PIPELINE_PROPS = 11, + SCHEDULERS_INFO = 12, + GATEWAYS_INFO = 13, + MEMORY_STATE_INFO = 14, + POWER_STATE_INFO = 15 +}; + struct skl_ipc_dxstate_info { u32 core_mask; u32 dx_mask; diff --git a/sound/soc/intel/skylake/skl-sst-utils.c b/sound/soc/intel/skylake/skl-sst-utils.c index 2ae405617876..59fb24bc0adc 100644 --- a/sound/soc/intel/skylake/skl-sst-utils.c +++ b/sound/soc/intel/skylake/skl-sst-utils.c @@ -446,3 +446,29 @@ void skl_release_library(struct skl_lib_info *linfo, int lib_count) } } } + +int skl_get_firmware_configuration(struct sst_dsp *ctx) +{ + struct skl_ipc_large_config_msg msg; + struct skl_sst *skl = ctx->thread_context; + u8 *ipc_data; + int ret = 0; + size_t rx_bytes; + + ipc_data = kzalloc(DSP_BUF, GFP_KERNEL); + if (!ipc_data) + return -ENOMEM; + + msg.module_id = 0; + msg.instance_id = 0; + msg.large_param_id = FIRMWARE_CONFIG; + msg.param_data_size = DSP_BUF; + + ret = skl_ipc_get_large_config(&skl->ipc, &msg, + (u32 *)ipc_data, NULL, 0, &rx_bytes); + if (ret < 0) + dev_err(ctx->dev, "failed to get fw configuration !!!\n"); + + kfree(ipc_data); + return ret; +} diff --git a/sound/soc/intel/skylake/skl-topology.h b/sound/soc/intel/skylake/skl-topology.h index 00265245beee..676a769374ae 100644 --- a/sound/soc/intel/skylake/skl-topology.h +++ b/sound/soc/intel/skylake/skl-topology.h @@ -514,24 +514,6 @@ struct mod_set_get { u32 mailbx[1024]; }; -enum base_fw_run_time_param { - ADSP_PROPERTIES = 0, - ADSP_RESOURCE_STATE = 1, - NOTIFICATION_MASK = 3, - ASTATE_TABLE = 4, - DMA_CONTROL = 5, - ENABLE_LOGS = 6, - FIRMWARE_CONFIG = 7, - HARDWARE_CONFIG = 8, - MODULES_INFO = 9, - PIPELINE_LIST_INFO = 10, - PIPELINE_PROPS = 11, - SCHEDULERS_INFO = 12, - GATEWAYS_INFO = 13, - MEMORY_STATE_INFO = 14, - POWER_STATE_INFO = 15 -}; - struct fw_ipc_data { u32 replysz; u32 adsp_id; From a034fbe2d22889f8fe938c663fcc1a66a89c25c7 Mon Sep 17 00:00:00 2001 From: Leoni Prodduvaka Date: Mon, 6 Feb 2017 22:16:04 +0530 Subject: [PATCH 0736/1103] ASoC: Intel: Skylake: Parse the fw property The value returned by the FW CONFIG IPC is in the form of a TLV. It contains information about fw version, memory reclaim, mailbox size etc., Parse the fw property, it could be used during runtime operations. Change-Id: Ie366c59b8ba103f38e8a8e67e1aa679123e77c05 Signed-off-by: Leoni Prodduvaka Reviewed-on: Reviewed-by: R, Dharageswari Reviewed-by: Diwakar, Praveen Reviewed-by: Koul, Vinod Tested-by: Sm, Bhadur A --- sound/soc/intel/skylake/skl-messages.c | 5 + sound/soc/intel/skylake/skl-sst-dsp.h | 32 ++++ sound/soc/intel/skylake/skl-sst-ipc.h | 58 +++++++ sound/soc/intel/skylake/skl-sst-utils.c | 195 +++++++++++++++++++++++- 4 files changed, 289 insertions(+), 1 deletion(-) diff --git a/sound/soc/intel/skylake/skl-messages.c b/sound/soc/intel/skylake/skl-messages.c index 4a0ad2916388..e0d6f74a8fd5 100644 --- a/sound/soc/intel/skylake/skl-messages.c +++ b/sound/soc/intel/skylake/skl-messages.c @@ -1158,6 +1158,8 @@ int skl_free_dsp(struct skl *skl) { struct hdac_bus *bus = skl_to_bus(skl); struct skl_sst *ctx = skl->skl_sst; + struct skl_fw_property_info fw_property = skl->skl_sst->fw_property; + struct skl_scheduler_config sch_config = fw_property.scheduler_config; /* disable ppcap interrupt */ snd_hdac_ext_bus_ppcap_int_enable(bus, false); @@ -1170,6 +1172,9 @@ int skl_free_dsp(struct skl *skl) if (ctx->dsp->addr.lpe) iounmap(ctx->dsp->addr.lpe); + kfree(fw_property.dma_config); + kfree(sch_config.sys_tick_cfg); + return 0; } diff --git a/sound/soc/intel/skylake/skl-sst-dsp.h b/sound/soc/intel/skylake/skl-sst-dsp.h index ea6e57e6899f..df37765858c4 100644 --- a/sound/soc/intel/skylake/skl-sst-dsp.h +++ b/sound/soc/intel/skylake/skl-sst-dsp.h @@ -127,8 +127,40 @@ struct skl_lib_info; #define SKL_ADSPCS_CPA_SHIFT 24 #define SKL_ADSPCS_CPA_MASK(cm) ((cm) << SKL_ADSPCS_CPA_SHIFT) +/* Header size is in number of bytes */ +#define SKL_TLV_HEADER_SIZE 8 +struct skl_tlv_message { + u32 type; + u32 length; + char data[0]; +} __packed; + #define DSP_BUF PAGE_SIZE +enum skl_fw_info_type { + SKL_FW_VERSION = 0, + SKL_MEMORY_RECLAIMED, + SKL_SLOW_CLOCK_FREQ_HZ, + SKL_FAST_CLOCK_FREQ_HZ, + SKL_DMA_BUFFER_CONFIG, + SKL_ALH_SUPPORT_LEVEL, + SKL_IPC_DL_MAILBOX_BYTES, + SKL_IPC_UL_MAILBOX_BYTES, + SKL_TRACE_LOG_BYTES, + SKL_MAX_PPL_COUNT, + SKL_MAX_ASTATE_COUNT, + SKL_MAX_MODULE_PIN_COUNT, + SKL_MODULES_COUNT, + SKL_MAX_MOD_INST_COUNT, + SKL_MAX_LL_TASKS_PER_PRI_COUNT, + SKL_LL_PRI_COUNT, + SKL_MAX_DP_TASKS_COUNT, + SKL_MAX_LIBS_COUNT, + SKL_SCHEDULER_CONFIG, + SKL_XTAL_FREQ_HZ, + SKL_CLOCKS_CONFIG, +}; + /* DSP Core state */ enum skl_dsp_states { SKL_DSP_RUNNING = 1, diff --git a/sound/soc/intel/skylake/skl-sst-ipc.h b/sound/soc/intel/skylake/skl-sst-ipc.h index 074014668f0a..7700b5c54934 100644 --- a/sound/soc/intel/skylake/skl-sst-ipc.h +++ b/sound/soc/intel/skylake/skl-sst-ipc.h @@ -133,6 +133,61 @@ struct bra_conf { struct skl_pipe *cp_pipe; }; +struct skl_fw_version { + u16 major; + u16 minor; + u16 hotfix; + u16 build; +}; + +struct skl_dma_buff_config { + u32 min_size_bytes; + u32 max_size_bytes; +}; + +enum skl_alh_support_level { + ALH_NO_SUPPORT = 0x00000, + ALH_CAVS_1_8_CNL = 0x10000, +}; + +struct skl_clk_config { + u32 clock_source; + u32 clock_param_mask; +}; + +struct skl_scheduler_config { + u32 sys_tick_multiplier; + u32 sys_tick_divider; + u32 sys_tick_source; + u32 sys_tick_cfg_length; + u32 *sys_tick_cfg; +}; + +struct skl_fw_property_info { + struct skl_fw_version version; + u32 memory_reclaimed; + u32 slow_clock_freq_hz; + u32 fast_clock_freq_hz; + enum skl_alh_support_level alh_support; + u32 ipc_dl_mailbox_bytes; + u32 ipc_ul_mailbox_bytes; + u32 trace_log_bytes; + u32 max_ppl_count; + u32 max_astate_count; + u32 max_module_pin_count; + u32 modules_count; + u32 max_mod_inst_count; + u32 max_ll_tasks_per_pri_count; + u32 ll_pri_count; + u32 max_dp_tasks_count; + u32 max_libs_count; + u32 xtal_freq_hz; + struct skl_clk_config clk_config; + struct skl_scheduler_config scheduler_config; + u32 num_dma_cfg; + struct skl_dma_buff_config *dma_config; +}; + struct skl_sst { struct device *dev; struct sst_dsp *dsp; @@ -189,6 +244,9 @@ struct skl_sst { /* BRA configuration data */ struct bra_conf *bra_pipe_data; + + /* firmware configuration information */ + struct skl_fw_property_info fw_property; }; struct skl_ipc_init_instance_msg { diff --git a/sound/soc/intel/skylake/skl-sst-utils.c b/sound/soc/intel/skylake/skl-sst-utils.c index 59fb24bc0adc..2bd5565d5f80 100644 --- a/sound/soc/intel/skylake/skl-sst-utils.c +++ b/sound/soc/intel/skylake/skl-sst-utils.c @@ -446,6 +446,192 @@ void skl_release_library(struct skl_lib_info *linfo, int lib_count) } } } +static int skl_fill_sch_cfg( + struct skl_fw_property_info *fw_property, + u32 *src) +{ + struct skl_scheduler_config *sch_config = + &(fw_property->scheduler_config); + + sch_config->sys_tick_multiplier = *src; + sch_config->sys_tick_divider = *(src + 1); + sch_config->sys_tick_source = *(src + 2); + sch_config->sys_tick_cfg_length = *(src + 3); + + if (sch_config->sys_tick_cfg_length > 0) { + sch_config->sys_tick_cfg = + kcalloc(sch_config->sys_tick_cfg_length, + sizeof(*sch_config->sys_tick_cfg), + GFP_KERNEL); + + if (!sch_config->sys_tick_cfg) + return -ENOMEM; + + memcpy(sch_config->sys_tick_cfg, + src + 4, + sch_config->sys_tick_cfg_length * + sizeof(*sch_config->sys_tick_cfg)); + } + return 0; +} + +static int skl_fill_dma_cfg(struct skl_tlv_message *message, + struct skl_fw_property_info *fw_property, u32 *src) +{ + struct skl_dma_buff_config dma_buff_cfg; + + fw_property->num_dma_cfg = message->length / + sizeof(dma_buff_cfg); + + if (fw_property->num_dma_cfg > 0) { + fw_property->dma_config = + kcalloc(fw_property->num_dma_cfg, + sizeof(dma_buff_cfg), + GFP_KERNEL); + + if (!fw_property->dma_config) + return -ENOMEM; + + memcpy(fw_property->dma_config, src, + message->length); + } + return 0; +} + +static int skl_parse_fw_config_info(struct sst_dsp *ctx, + u8 *src, int limit) +{ + struct skl_tlv_message *message; + int offset = 0, shift, ret = 0; + u32 *value; + struct skl_sst *skl = ctx->thread_context; + struct skl_fw_property_info *fw_property = &skl->fw_property; + enum skl_fw_info_type type; + struct skl_scheduler_config *sch_config = + &fw_property->scheduler_config; + + while (offset < limit) { + + message = (struct skl_tlv_message *)src; + if (message == NULL) + break; + + /* Skip TLV header to read value */ + src += sizeof(*message); + + value = (u32 *)src; + type = message->type; + + switch (type) { + case SKL_FW_VERSION: + memcpy(&fw_property->version, value, + sizeof(fw_property->version)); + break; + + case SKL_MEMORY_RECLAIMED: + fw_property->memory_reclaimed = *value; + break; + + case SKL_SLOW_CLOCK_FREQ_HZ: + fw_property->slow_clock_freq_hz = *value; + break; + + case SKL_FAST_CLOCK_FREQ_HZ: + fw_property->fast_clock_freq_hz = *value; + break; + + case SKL_DMA_BUFFER_CONFIG: + ret = skl_fill_dma_cfg(message, fw_property, value); + if (ret < 0) + goto err; + break; + + case SKL_ALH_SUPPORT_LEVEL: + fw_property->alh_support = *value; + break; + + case SKL_IPC_DL_MAILBOX_BYTES: + fw_property->ipc_dl_mailbox_bytes = *value; + break; + + case SKL_IPC_UL_MAILBOX_BYTES: + fw_property->ipc_ul_mailbox_bytes = *value; + break; + + case SKL_TRACE_LOG_BYTES: + fw_property->trace_log_bytes = *value; + break; + + case SKL_MAX_PPL_COUNT: + fw_property->max_ppl_count = *value; + break; + + case SKL_MAX_ASTATE_COUNT: + fw_property->max_astate_count = *value; + break; + + case SKL_MAX_MODULE_PIN_COUNT: + fw_property->max_module_pin_count = *value; + break; + + case SKL_MODULES_COUNT: + fw_property->modules_count = *value; + break; + + case SKL_MAX_MOD_INST_COUNT: + fw_property->max_mod_inst_count = *value; + break; + + case SKL_MAX_LL_TASKS_PER_PRI_COUNT: + fw_property->max_ll_tasks_per_pri_count = *value; + break; + + case SKL_LL_PRI_COUNT: + fw_property->ll_pri_count = *value; + break; + + case SKL_MAX_DP_TASKS_COUNT: + fw_property->max_dp_tasks_count = *value; + break; + + case SKL_MAX_LIBS_COUNT: + fw_property->max_libs_count = *value; + break; + + case SKL_SCHEDULER_CONFIG: + ret = skl_fill_sch_cfg(fw_property, value); + if (ret < 0) + goto err; + break; + + case SKL_XTAL_FREQ_HZ: + fw_property->xtal_freq_hz = *value; + break; + + case SKL_CLOCKS_CONFIG: + memcpy(&(fw_property->clk_config), value, + message->length); + break; + + default: + dev_err(ctx->dev, "Invalid fw info type:%d !!\n", + type); + break; + } + + shift = message->length + sizeof(*message); + offset += shift; + /* skip over to next tlv data */ + src += message->length; + } +err: + if (ret < 0) { + kfree(fw_property->dma_config); + kfree(sch_config->sys_tick_cfg); + } + + return ret; +} int skl_get_firmware_configuration(struct sst_dsp *ctx) { @@ -466,9 +652,16 @@ int skl_get_firmware_configuration(struct sst_dsp *ctx) ret = skl_ipc_get_large_config(&skl->ipc, &msg, (u32 *)ipc_data, NULL, 0, &rx_bytes); - if (ret < 0) + if (ret < 0) { dev_err(ctx->dev, "failed to get fw configuration !!!\n"); + goto err; + } + + ret = skl_parse_fw_config_info(ctx, ipc_data, rx_bytes); + if (ret < 0) + dev_err(ctx->dev, "failed to parse configuration !!!\n"); +err: kfree(ipc_data); return ret; } From 0d64d3e6683760927a96d9e92712df22aecd1204 Mon Sep 17 00:00:00 2001 From: Leoni Prodduvaka Date: Mon, 6 Feb 2017 22:24:18 +0530 Subject: [PATCH 0737/1103] ASoC: Intel: Skylake: Check for memory reclaim bit Memory reclaim bit indicates whether legacy DMA memory is managed by FW. It would be set by firmware if DMA is to be used. Check for the memory reclaimed bit during the probe. If the bit is not set then fail the probe. Change-Id: I8a926ea2b4f86b1f7d66749d2e9809f23308c51c Signed-off-by: Leoni Prodduvaka Reviewed-on: Reviewed-by: R, Dharageswari Reviewed-by: Nc, Shreyas Reviewed-by: Koul, Vinod Reviewed-by: Diwakar, Praveen Tested-by: Sm, Bhadur A --- sound/soc/intel/skylake/cnl-sst.c | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/sound/soc/intel/skylake/cnl-sst.c b/sound/soc/intel/skylake/cnl-sst.c index 790af2489d0e..d5e2c32b3f83 100644 --- a/sound/soc/intel/skylake/cnl-sst.c +++ b/sound/soc/intel/skylake/cnl-sst.c @@ -209,8 +209,10 @@ static int cnl_load_base_firmware(struct sst_dsp *ctx) { struct firmware stripped_fw; struct skl_sst *cnl = ctx->thread_context; + struct skl_fw_property_info fw_property; int ret; + fw_property.memory_reclaimed = -1; if (!ctx->fw) { ret = request_firmware(&ctx->fw, ctx->fw_name, ctx->dev); if (ret < 0) { @@ -255,6 +257,21 @@ static int cnl_load_base_firmware(struct sst_dsp *ctx) cnl->fw_loaded = true; + ret = skl_get_firmware_configuration(ctx); + if (ret < 0) { + dev_err(ctx->dev, "fwconfig ipc failed !\n"); + ret = -EIO; + goto cnl_load_base_firmware_failed; + } + + fw_property = cnl->fw_property; + if (fw_property.memory_reclaimed <= 0) { + dev_err(ctx->dev, "Memory reclaim not enabled:%d\n", + fw_property.memory_reclaimed); + ret = -EIO; + goto cnl_load_base_firmware_failed; + } + return 0; cnl_load_base_firmware_failed: From 4ccffd2a79cd733997b00baeb8509847f0903456 Mon Sep 17 00:00:00 2001 From: Jayachandran B Date: Thu, 11 Feb 2016 20:02:14 +0530 Subject: [PATCH 0738/1103] ASoC: Intel: Skylake: Better handling of stream interrupts There are storm of interrupts while audio is running. It seems like we have level triggered irq for audio. We need to disable the source of IRQ and re-enable them after we handle them. So here we are disabling them in main irq handler and re-enable them in bottom half. Change-Id: I482dacb83f61229783bfe0c61721b73f47ee4703 Tracked-On: Signed-off-by: Jayachandran B Signed-off-by: Ramesh Babu Reviewed-on: Reviewed-on: Reviewed-by: Babu, Ramesh Tested-by: Babu, Ramesh Signed-off-by: Leoni Prodduvaka Reviewed-on: Reviewed-by: R, Dharageswari Reviewed-by: Diwakar, Praveen Tested-by: Sm, Bhadur A --- sound/soc/intel/skylake/skl.c | 29 ++++++++++++++++++++++++++--- 1 file changed, 26 insertions(+), 3 deletions(-) diff --git a/sound/soc/intel/skylake/skl.c b/sound/soc/intel/skylake/skl.c index dbff367fb3d0..d3fd3dd5f882 100644 --- a/sound/soc/intel/skylake/skl.c +++ b/sound/soc/intel/skylake/skl.c @@ -216,16 +216,18 @@ static irqreturn_t skl_interrupt(int irq, void *dev_id) { struct hdac_bus *bus = dev_id; u32 status; + u32 mask, int_enable; + int ret = IRQ_NONE; if (!pm_runtime_active(bus->dev)) - return IRQ_NONE; + return ret; spin_lock(&bus->reg_lock); status = snd_hdac_chip_readl(bus, INTSTS); if (status == 0 || status == 0xffffffff) { spin_unlock(&bus->reg_lock); - return IRQ_NONE; + return ret; } /* clear rirb int */ @@ -236,20 +238,41 @@ static irqreturn_t skl_interrupt(int irq, void *dev_id) snd_hdac_chip_writeb(bus, RIRBSTS, RIRB_INT_MASK); } + mask = (0x1 << ebus->num_streams) - 1; + + status = snd_hdac_chip_readl(bus, INTSTS); + status &= mask; + if (status) { + /* Disable stream interrupts; Re-enable in bottom half */ + int_enable = snd_hdac_chip_readl(bus, INTCTL); + snd_hdac_chip_writel(bus, INTCTL, (int_enable & (~mask))); + ret = IRQ_WAKE_THREAD; + } else + ret = IRQ_HANDLED; + spin_unlock(&bus->reg_lock); + return ret; - return snd_hdac_chip_readl(bus, INTSTS) ? IRQ_WAKE_THREAD : IRQ_HANDLED; } static irqreturn_t skl_threaded_handler(int irq, void *dev_id) { struct hdac_bus *bus = dev_id; u32 status; + u32 int_enable; + u32 mask; + unsigned long flags; status = snd_hdac_chip_readl(bus, INTSTS); snd_hdac_bus_handle_stream_irq(bus, status, skl_stream_update); + /* Re-enable stream interrupts */ + mask = (0x1 << ebus->num_streams) - 1; + spin_lock_irqsave(&bus->reg_lock, flags); + int_enable = snd_hdac_chip_readl(bus, INTCTL); + snd_hdac_chip_writel(bus, INTCTL, (int_enable | mask)); + spin_unlock_irqrestore(&bus->reg_lock, flags); return IRQ_HANDLED; } From 5589b21d463b42f2b23f1432614d040a54182e30 Mon Sep 17 00:00:00 2001 From: Hardik T Shah Date: Tue, 6 Dec 2016 15:22:43 +0530 Subject: [PATCH 0739/1103] [REVERTME] ASoC: Intel: Skylake: Set DUM bit in EM2 register HW recommends that we set DUM bit so that DPIB write request will occur every frame regardless whether DPIB has changed or not. Change-Id: I84933645bef2a22ce4758b29e6f618f2ac37a8e9 Signed-off-by: Senthilnathan Veppur Signed-off-by: Dharageswari R Signed-off-by: GuruprasadX Pawse Signed-off-by: Hardik T Shah Signed-off-by: Leoni Prodduvaka Reviewed-on: Reviewed-by: Kp, Jeeja Tested-by: Sangaraju, KarthikeyaX Reviewed-on: Reviewed-by: Diwakar, Praveen Reviewed-by: Koul, Vinod Tested-by: Sm, Bhadur A --- sound/soc/intel/skylake/skl.c | 29 +++++++++++++++++++++++++++++ sound/soc/intel/skylake/skl.h | 1 + 2 files changed, 30 insertions(+) diff --git a/sound/soc/intel/skylake/skl.c b/sound/soc/intel/skylake/skl.c index d3fd3dd5f882..97e8367eed30 100644 --- a/sound/soc/intel/skylake/skl.c +++ b/sound/soc/intel/skylake/skl.c @@ -201,6 +201,33 @@ static void skl_get_total_bytes_transferred(struct hdac_stream *hstr) hstr->curr_pos += no_of_bytes; } +/* + * skl_dum_set - Set the DUM bit in EM2 register to fix the IP bug + * of incorrect postion reporting for capture stream. + */ +static void skl_dum_set(struct hdac_ext_bus *ebus) +{ + struct hdac_bus *bus = ebus_to_hbus(ebus); + u32 reg; + u8 val; + + /* + * For the DUM bit to be set, CRST needs to be out of reset state + */ + val = snd_hdac_chip_readb(bus, GCTL) & AZX_GCTL_RESET; + if (!val) { + skl_enable_miscbdcge(bus->dev, false); + snd_hdac_bus_exit_link_reset(bus); + skl_enable_miscbdcge(bus->dev, true); + } + /* + * Set the DUM bit in EM2 register to fix the IP bug of incorrect + * postion reporting for capture stream. + */ + reg = snd_hdac_chip_readl(bus, VS_EM2); + snd_hdac_chip_writel(bus, VS_EM2, (reg | AZX_EM2_DUM_MASK)); +} + /* called from IRQ */ static void skl_stream_update(struct hdac_bus *bus, struct hdac_stream *hstr) { @@ -927,6 +954,8 @@ static int skl_first_init(struct hdac_bus *bus) /* initialize chip */ skl_init_pci(skl); + skl_dum_set(ebus); + return skl_init_chip(bus, true); } diff --git a/sound/soc/intel/skylake/skl.h b/sound/soc/intel/skylake/skl.h index 2bafc7d40cec..10a414d77cb2 100644 --- a/sound/soc/intel/skylake/skl.h +++ b/sound/soc/intel/skylake/skl.h @@ -44,6 +44,7 @@ #define DMA_CLK_CONTROLS 1 #define DMA_TRANSMITION_START 2 #define DMA_TRANSMITION_STOP 3 +#define AZX_EM2_DUM_MASK (1 << 23) #define AZX_REG_VS_EM2_L1SEN BIT(13) From 25f0d69504539900a2154e01242c9d12e65a7969 Mon Sep 17 00:00:00 2001 From: Leoni Prodduvaka Date: Wed, 15 Feb 2017 17:04:40 +0530 Subject: [PATCH 0740/1103] ASoC: Intel: Skylake: Add D0i3 support for Icelake platform The driver needs two DSP callback, one to set D0i0 (active) and D0i3 (low-power) states. Add these callbacks in icelake dsp ops. Change-Id: Ibd077dd4bef1a617a9d86f2c2e639f47ce6b5f25 Signed-off-by: Leoni Prodduvaka Reviewed-on: Reviewed-by: Koul, Vinod Reviewed-by: Babu, Ramesh Reviewed-by: R, Dharageswari Reviewed-by: Diwakar, Praveen Reviewed-by: Kp, Jeeja Tested-by: Sm, Bhadur A --- sound/soc/intel/skylake/bxt-sst.c | 6 +++--- sound/soc/intel/skylake/cnl-sst.c | 7 +++++++ sound/soc/intel/skylake/skl-sst-dsp.h | 6 ++++++ 3 files changed, 16 insertions(+), 3 deletions(-) diff --git a/sound/soc/intel/skylake/bxt-sst.c b/sound/soc/intel/skylake/bxt-sst.c index 6f9b24c8c211..c1f6d8b6ab79 100644 --- a/sound/soc/intel/skylake/bxt-sst.c +++ b/sound/soc/intel/skylake/bxt-sst.c @@ -302,7 +302,7 @@ static int bxt_d0i3_target_state(struct sst_dsp *ctx) return SKL_DSP_D0I3_NONE; } -static void bxt_set_dsp_D0i3(struct work_struct *work) +void bxt_set_dsp_D0i3(struct work_struct *work) { int ret; struct skl_ipc_d0ix_msg msg; @@ -347,7 +347,7 @@ static void bxt_set_dsp_D0i3(struct work_struct *work) skl->cores.state[SKL_DSP_CORE0_ID] = SKL_DSP_RUNNING_D0I3; } -static int bxt_schedule_dsp_D0i3(struct sst_dsp *ctx) +int bxt_schedule_dsp_D0i3(struct sst_dsp *ctx) { struct skl_sst *skl = ctx->thread_context; struct skl_d0i3_data *d0i3 = &skl->d0i3; @@ -364,7 +364,7 @@ static int bxt_schedule_dsp_D0i3(struct sst_dsp *ctx) return 0; } -static int bxt_set_dsp_D0i0(struct sst_dsp *ctx) +int bxt_set_dsp_D0i0(struct sst_dsp *ctx) { int ret; struct skl_ipc_d0ix_msg msg; diff --git a/sound/soc/intel/skylake/cnl-sst.c b/sound/soc/intel/skylake/cnl-sst.c index d5e2c32b3f83..5ee7b350eb7f 100644 --- a/sound/soc/intel/skylake/cnl-sst.c +++ b/sound/soc/intel/skylake/cnl-sst.c @@ -389,6 +389,8 @@ static unsigned int cnl_get_errno(struct sst_dsp *ctx) static const struct skl_dsp_fw_ops cnl_fw_ops = { .set_state_D0 = cnl_set_dsp_D0, .set_state_D3 = cnl_set_dsp_D3, + .set_state_D0i3 = bxt_schedule_dsp_D0i3, + .set_state_D0i0 = bxt_set_dsp_D0i0, .load_fw = cnl_load_base_firmware, .get_fw_errcode = cnl_get_errno, .load_library = bxt_load_library, @@ -743,9 +745,14 @@ int cnl_sst_dsp_init(struct device *dev, void __iomem *mmio_base, int irq, return ret; } + /* set the D0i3 check */ + cnl->ipc.ops.check_dsp_lp_on = skl_ipc_check_D0i0; cnl->boot_complete = false; init_waitqueue_head(&cnl->boot_wait); + INIT_DELAYED_WORK(&cnl->d0i3.work, bxt_set_dsp_D0i3); + cnl->d0i3.state = SKL_DSP_D0I3_NONE; + ret = skl_dsp_acquire_irq(sst); if (ret < 0) return ret; diff --git a/sound/soc/intel/skylake/skl-sst-dsp.h b/sound/soc/intel/skylake/skl-sst-dsp.h index df37765858c4..58c0c13c15aa 100644 --- a/sound/soc/intel/skylake/skl-sst-dsp.h +++ b/sound/soc/intel/skylake/skl-sst-dsp.h @@ -295,4 +295,10 @@ int skl_prepare_lib_load(struct skl_sst *skl, struct skl_lib_info *linfo, void skl_release_library(struct skl_lib_info *linfo, int lib_count); int skl_get_firmware_configuration(struct sst_dsp *ctx); + +int bxt_set_dsp_D0i0(struct sst_dsp *ctx); + +int bxt_schedule_dsp_D0i3(struct sst_dsp *ctx); + +void bxt_set_dsp_D0i3(struct work_struct *work); #endif /*__SKL_SST_DSP_H__*/ From 650ef11c0623b2a36ebb977bb51ff4021defd427 Mon Sep 17 00:00:00 2001 From: Praveen Diwakar Date: Wed, 8 Mar 2017 20:12:57 +0530 Subject: [PATCH 0741/1103] ASoC: Intel: Skylake: Audio format mismatch detection This patch detects mismatch in audio format for source and destination modules. It prints warning to inform a mismatch occured in given path. Change-Id: Ic9fe0bbde2e2487d3ec25cbd3723ebaab81b395a Signed-off-by: Praveen Diwakar Signed-off-by: Rahul Patil Reviewed-on: Reviewed-by: Nc, Shreyas Reviewed-by: Prusty, Subhransu S Reviewed-by: Singh, Guneshwor O Reviewed-by: Babu, Ramesh Reviewed-by: Kp, Jeeja Tested-by: Sm, Bhadur A --- sound/soc/intel/skylake/skl-messages.c | 67 ++++++++++++++++++++++++++ 1 file changed, 67 insertions(+) diff --git a/sound/soc/intel/skylake/skl-messages.c b/sound/soc/intel/skylake/skl-messages.c index e0d6f74a8fd5..f4ebdce7446d 100644 --- a/sound/soc/intel/skylake/skl-messages.c +++ b/sound/soc/intel/skylake/skl-messages.c @@ -2086,6 +2086,70 @@ static void fill_pin_params(struct skl_audio_data_format *pin_fmt, #define CPR_SINK_FMT_PARAM_ID 2 +static struct +skl_module_fmt *skl_get_pin_format(struct skl_module_cfg *mconfig, + u8 pin_direction, u8 pin_idx) +{ + struct skl_module *module = mconfig->module; + int fmt_idx = mconfig->fmt_idx; + struct skl_module_iface *intf; + struct skl_module_fmt *pin_fmt; + + intf = &module->formats[fmt_idx]; + + if (pin_direction == SKL_INPUT_PIN) + pin_fmt = &intf->inputs[pin_idx].fmt; + else + pin_fmt = &intf->outputs[pin_idx].fmt; + + return pin_fmt; +} + +/* + * This function checks for source module and destination module format + * mismatch + */ +static void skl_module_format_mismatch_detection(struct skl_sst *ctx, + struct skl_module_cfg *src_mcfg, + struct skl_module_cfg *dst_mcfg, + int src_index, int dst_index) +{ + struct skl_module_fmt *src_fmt, *dst_fmt; + + src_fmt = skl_get_pin_format(src_mcfg, SKL_OUTPUT_PIN, src_index); + dst_fmt = skl_get_pin_format(dst_mcfg, SKL_INPUT_PIN, dst_index); + + if(memcmp(src_fmt, dst_fmt, sizeof(*src_fmt))) { + dev_warn(ctx->dev, "#### src and dst format mismatch: ####\n"); + dev_warn(ctx->dev, "pipe=%d src module_id=%d src instance_id=%d\n", + src_mcfg->pipe->ppl_id, + src_mcfg->id.module_id, + src_mcfg->id.pvt_id); + + dev_warn(ctx->dev, "pipe=%d dst module_id=%d dst instance_id=%d\n", + dst_mcfg->pipe->ppl_id, + dst_mcfg->id.module_id, + dst_mcfg->id.pvt_id); + + dev_warn(ctx->dev, "channels: src=%d dst=%d\n", + src_fmt->channels, dst_fmt->channels); + dev_warn(ctx->dev, "s_freq: src=%d dst=%d\n", + src_fmt->s_freq, dst_fmt->s_freq); + dev_warn(ctx->dev, "bit_depth: src=%d dst=%d\n", + src_fmt->bit_depth, dst_fmt->bit_depth); + dev_warn(ctx->dev, "valid_bit_depth: src=%d dst=%d\n", + src_fmt->valid_bit_depth, dst_fmt->valid_bit_depth); + dev_warn(ctx->dev, "ch_cfg: src=%d dst=%d\n", + src_fmt->ch_cfg, dst_fmt->ch_cfg); + dev_warn(ctx->dev, "interleaving_style: src=%d dst=%d\n", + src_fmt->interleaving_style, dst_fmt->interleaving_style); + dev_warn(ctx->dev, "sample_type: src=%d dst=%d\n", + src_fmt->sample_type, dst_fmt->sample_type); + dev_warn(ctx->dev, "ch_map: src=%d dst=%d\n", + src_fmt->ch_map, dst_fmt->ch_map); + } +} + /* * Once a module is instantiated it need to be 'bind' with other modules in * the pipeline. For binding we need to find the module pins which are bind @@ -2152,6 +2216,9 @@ int skl_bind_modules(struct skl_sst *ctx, dev_dbg(ctx->dev, "src queue = %d dst queue =%d\n", msg.src_queue, msg.dst_queue); + skl_module_format_mismatch_detection(ctx, src_mcfg, dst_mcfg, + src_index, dst_index); + msg.module_id = src_mcfg->id.module_id; msg.instance_id = src_mcfg->id.pvt_id; msg.dst_module_id = dst_mcfg->id.module_id; From 9404f8cb0fb67e3b5d5c4208cbc38f153591e368 Mon Sep 17 00:00:00 2001 From: "Pawse, GuruprasadX" Date: Mon, 30 Jan 2017 20:12:52 +0530 Subject: [PATCH 0742/1103] ASoC: Intel: Skylake: add sysfs files for firmware modules This patch adds sysfs files for firmware modules. Below is the structure of sysfs files created: /sys/bus/pci/devices//dsp/modules/ |---id: module id |---hash: module hash |---loaded: module state loaded/unloaded Change-Id: Ia097a3cc1409a33b2a82b1d1cdc634fb4b0eee90 Signed-off-by: Pawse, GuruprasadX Reviewed-on: Reviewed-by: R, Dharageswari Reviewed-by: Kp, Jeeja Reviewed-by: Prodduvaka, Leoni Tested-by: Sm, Bhadur A --- sound/soc/intel/skylake/skl-messages.c | 1 + sound/soc/intel/skylake/skl-pcm.c | 3 + sound/soc/intel/skylake/skl-sst-dsp.h | 7 + sound/soc/intel/skylake/skl-sst-ipc.h | 3 + sound/soc/intel/skylake/skl-sst-utils.c | 222 +++++++++++++++++++++++- 5 files changed, 235 insertions(+), 1 deletion(-) diff --git a/sound/soc/intel/skylake/skl-messages.c b/sound/soc/intel/skylake/skl-messages.c index f4ebdce7446d..54bd83eb55b5 100644 --- a/sound/soc/intel/skylake/skl-messages.c +++ b/sound/soc/intel/skylake/skl-messages.c @@ -1164,6 +1164,7 @@ int skl_free_dsp(struct skl *skl) /* disable ppcap interrupt */ snd_hdac_ext_bus_ppcap_int_enable(bus, false); + skl_module_sysfs_exit(skl->skl_sst); ctx->dsp_ops->cleanup(bus->dev, ctx); kfree(ctx->cores.state); diff --git a/sound/soc/intel/skylake/skl-pcm.c b/sound/soc/intel/skylake/skl-pcm.c index 265992ec11ff..361b85bfa792 100644 --- a/sound/soc/intel/skylake/skl-pcm.c +++ b/sound/soc/intel/skylake/skl-pcm.c @@ -1954,6 +1954,9 @@ static int skl_platform_soc_probe(struct snd_soc_component *component) } skl_get_probe_widget(component, skl); + + /* create sysfs to list modules downloaded by driver */ + skl_module_sysfs_init(skl->skl_sst, &component->dev->kobj); } pm_runtime_mark_last_busy(component->dev); pm_runtime_put_autosuspend(component->dev); diff --git a/sound/soc/intel/skylake/skl-sst-dsp.h b/sound/soc/intel/skylake/skl-sst-dsp.h index 58c0c13c15aa..7be2cdeb85b6 100644 --- a/sound/soc/intel/skylake/skl-sst-dsp.h +++ b/sound/soc/intel/skylake/skl-sst-dsp.h @@ -137,6 +137,8 @@ struct skl_tlv_message { #define DSP_BUF PAGE_SIZE +#define DEFAULT_HASH_SHA256_LEN 32 + enum skl_fw_info_type { SKL_FW_VERSION = 0, SKL_MEMORY_RECLAIMED, @@ -218,6 +220,7 @@ struct uuid_module { int *instance_id; struct list_head list; + u8 hash[DEFAULT_HASH_SHA256_LEN]; }; struct skl_load_module_info { @@ -301,4 +304,8 @@ int bxt_set_dsp_D0i0(struct sst_dsp *ctx); int bxt_schedule_dsp_D0i3(struct sst_dsp *ctx); void bxt_set_dsp_D0i3(struct work_struct *work); + +int skl_module_sysfs_init(struct skl_sst *ctx, struct kobject *fw_modules_kobj); + +void skl_module_sysfs_exit(struct skl_sst *ctx); #endif /*__SKL_SST_DSP_H__*/ diff --git a/sound/soc/intel/skylake/skl-sst-ipc.h b/sound/soc/intel/skylake/skl-sst-ipc.h index 7700b5c54934..f3b8d7bc68ec 100644 --- a/sound/soc/intel/skylake/skl-sst-ipc.h +++ b/sound/soc/intel/skylake/skl-sst-ipc.h @@ -247,6 +247,9 @@ struct skl_sst { /* firmware configuration information */ struct skl_fw_property_info fw_property; + + /* sysfs for module info */ + struct skl_sysfs_tree *sysfs_tree; }; struct skl_ipc_init_instance_msg { diff --git a/sound/soc/intel/skylake/skl-sst-utils.c b/sound/soc/intel/skylake/skl-sst-utils.c index 2bd5565d5f80..0eaa88e6ae61 100644 --- a/sound/soc/intel/skylake/skl-sst-utils.c +++ b/sound/soc/intel/skylake/skl-sst-utils.c @@ -23,11 +23,35 @@ #define UUID_STR_SIZE 37 -#define DEFAULT_HASH_SHA256_LEN 32 /* FW Extended Manifest Header id = $AE1 */ #define SKL_EXT_MANIFEST_HEADER_MAGIC 0x31454124 +#define UUID_ATTR_RO(_name) \ + struct uuid_attribute uuid_attr_##_name = __ATTR_RO(_name) + +struct skl_sysfs_tree { + struct kobject *dsp_kobj; + struct kobject *modules_kobj; + struct skl_sysfs_module **mod_obj; +}; + +struct skl_sysfs_module { + struct kobject kobj; + struct uuid_module *uuid_mod; + struct list_head *module_list; + int fw_ops_load_mod; +}; + +struct uuid_attribute { + struct attribute attr; + ssize_t (*show)(struct skl_sysfs_module *modinfo_obj, + struct uuid_attribute *attr, char *buf); + ssize_t (*store)(struct skl_sysfs_module *modinfo_obj, + struct uuid_attribute *attr, const char *buf, + size_t count); +}; + struct UUID { u8 id[16]; }; @@ -319,6 +343,7 @@ int snd_skl_parse_uuids(struct sst_dsp *ctx, const struct firmware *fw, ret = -ENOMEM; goto free_uuid_list; } + memcpy(&module->hash, mod_entry->hash1, sizeof(module->hash)); list_add_tail(&module->list, &skl->uuid_list); @@ -665,3 +690,198 @@ int skl_get_firmware_configuration(struct sst_dsp *ctx) kfree(ipc_data); return ret; } + +static ssize_t uuid_attr_show(struct kobject *kobj, struct attribute *attr, + char *buf) +{ + struct uuid_attribute *uuid_attr = + container_of(attr, struct uuid_attribute, attr); + struct skl_sysfs_module *modinfo_obj = + container_of(kobj, struct skl_sysfs_module, kobj); + + if (uuid_attr->show) + return uuid_attr->show(modinfo_obj, uuid_attr, buf); + + return 0; +} + +static const struct sysfs_ops uuid_sysfs_ops = { + .show = uuid_attr_show, +}; + +static void uuid_release(struct kobject *kobj) +{ + struct skl_sysfs_module *modinfo_obj = + container_of(kobj, struct skl_sysfs_module, kobj); + + kfree(modinfo_obj); +} + +static struct kobj_type uuid_ktype = { + .release = uuid_release, + .sysfs_ops = &uuid_sysfs_ops, +}; + +static ssize_t loaded_show(struct skl_sysfs_module *modinfo_obj, + struct uuid_attribute *attr, char *buf) +{ + struct skl_module_table *module_list; + + if ((!modinfo_obj->fw_ops_load_mod) || + (modinfo_obj->fw_ops_load_mod && + !modinfo_obj->uuid_mod->is_loadable)) + return sprintf(buf, "%d\n", true); + + if (list_empty(modinfo_obj->module_list)) + return sprintf(buf, "%d\n", false); + + list_for_each_entry(module_list, modinfo_obj->module_list, list) { + if (module_list->mod_info->mod_id + == modinfo_obj->uuid_mod->id) + return sprintf(buf, "%d\n", module_list->usage_cnt); + } + + return sprintf(buf, "%d\n", false); +} + +static ssize_t hash_show(struct skl_sysfs_module *modinfo_obj, + struct uuid_attribute *attr, char *buf) +{ + int ret = 0; + int i; + + for (i = 0; i < DEFAULT_HASH_SHA256_LEN; i++) + ret += sprintf(buf + ret, "%d ", + modinfo_obj->uuid_mod->hash[i]); + ret += sprintf(buf + ret, "\n"); + + return ret; +} + + +static ssize_t id_show(struct skl_sysfs_module *modinfo_obj, + struct uuid_attribute *attr, char *buf) +{ + return sprintf(buf, "%d\n", modinfo_obj->uuid_mod->id); +} + +static UUID_ATTR_RO(loaded); +static UUID_ATTR_RO(hash); +static UUID_ATTR_RO(id); + +static struct attribute *modules_attrs[] = { + &uuid_attr_loaded.attr, + &uuid_attr_hash.attr, + &uuid_attr_id.attr, + NULL, +}; + +static const struct attribute_group uuid_group = { + .attrs = modules_attrs, +}; + +static void free_uuid_node(struct kobject *kobj, + const struct attribute_group *group) +{ + if (kobj) { + sysfs_remove_group(kobj, group); + kobject_put(kobj); + } +} + +void skl_module_sysfs_exit(struct skl_sst *ctx) +{ + struct skl_sysfs_tree *tree = ctx->sysfs_tree; + struct skl_sysfs_module **m; + + if (!tree) + return; + + if (tree->mod_obj) { + for (m = tree->mod_obj; *m; m++) + free_uuid_node(&(*m)->kobj, &uuid_group); + kfree(tree->mod_obj); + } + + if (tree->modules_kobj) + kobject_put(tree->modules_kobj); + + if (tree->dsp_kobj) + kobject_put(tree->dsp_kobj); + + kfree(tree); + ctx->sysfs_tree = NULL; +} +EXPORT_SYMBOL_GPL(skl_module_sysfs_exit); + +int skl_module_sysfs_init(struct skl_sst *ctx, struct kobject *kobj) +{ + struct uuid_module *module; + struct skl_sysfs_module *modinfo_obj; + char *uuid_name; + int count = 0; + int max_mod = 0; + int ret = 0; + + if (list_empty(&ctx->uuid_list)) + return 0; + + ctx->sysfs_tree = kzalloc(sizeof(*ctx->sysfs_tree), GFP_KERNEL); + if (!ctx->sysfs_tree) { + ret = -ENOMEM; + goto err_sysfs_exit; + } + + ctx->sysfs_tree->dsp_kobj = kobject_create_and_add("dsp", kobj); + if (!ctx->sysfs_tree->dsp_kobj) + goto err_sysfs_exit; + + ctx->sysfs_tree->modules_kobj = kobject_create_and_add("modules", + ctx->sysfs_tree->dsp_kobj); + if (!ctx->sysfs_tree->modules_kobj) + goto err_sysfs_exit; + + list_for_each_entry(module, &ctx->uuid_list, list) + max_mod++; + + ctx->sysfs_tree->mod_obj = kcalloc(max_mod + 1, + sizeof(*ctx->sysfs_tree->mod_obj), GFP_KERNEL); + if (!ctx->sysfs_tree->mod_obj) { + ret = -ENOMEM; + goto err_sysfs_exit; + } + + list_for_each_entry(module, &ctx->uuid_list, list) { + modinfo_obj = kzalloc(sizeof(*modinfo_obj), GFP_KERNEL); + if (!modinfo_obj) { + ret = -ENOMEM; + goto err_sysfs_exit; + } + + uuid_name = kasprintf(GFP_KERNEL, "%pUL", &module->uuid); + ret = kobject_init_and_add(&modinfo_obj->kobj, &uuid_ktype, + ctx->sysfs_tree->modules_kobj, uuid_name); + if (ret < 0) + goto err_sysfs_exit; + + ret = sysfs_create_group(&modinfo_obj->kobj, &uuid_group); + if (ret < 0) + goto err_sysfs_exit; + + modinfo_obj->uuid_mod = module; + modinfo_obj->module_list = &ctx->dsp->module_list; + modinfo_obj->fw_ops_load_mod = + (ctx->dsp->fw_ops.load_mod == NULL) ? 0 : 1; + + ctx->sysfs_tree->mod_obj[count] = modinfo_obj; + count++; + } + + return 0; + +err_sysfs_exit: + skl_module_sysfs_exit(ctx); + + return ret; +} +EXPORT_SYMBOL_GPL(skl_module_sysfs_init); From 98e35aed519d1569da736b3b49703f1748fd1e09 Mon Sep 17 00:00:00 2001 From: Guneshwor Singh Date: Thu, 27 Apr 2017 14:32:17 +0530 Subject: [PATCH 0743/1103] ASoC: Intel: Skylake: Append SDW device to device type enum Append the SDW device type enum to the device type enum instead of putting it in between. Change-Id: Idc6e7a19569bfcb672047311767804177ceb5c4d Signed-off-by: Guneshwor Singh Reviewed-on: Reviewed-by: audio_build Reviewed-by: Prusty, Subhransu S Reviewed-by: R, Dharageswari Reviewed-by: Kale, Sanyog R Reviewed-by: Nc, Shreyas Reviewed-by: Koul, Vinod Tested-by: Avati, Santosh Kumar --- include/uapi/sound/skl-tplg-interface.h | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/include/uapi/sound/skl-tplg-interface.h b/include/uapi/sound/skl-tplg-interface.h index fc4e4324f94b..e5656fc0ca02 100644 --- a/include/uapi/sound/skl-tplg-interface.h +++ b/include/uapi/sound/skl-tplg-interface.h @@ -108,9 +108,10 @@ enum skl_dev_type { SKL_DEVICE_SLIMBUS = 0x3, SKL_DEVICE_HDALINK = 0x4, SKL_DEVICE_HDAHOST = 0x5, - SKL_DEVICE_SDW_PCM = 0x6, - SKL_DEVICE_SDW_PDM = 0x7, - SKL_DEVICE_NONE + SKL_DEVICE_NONE = 0x6, + SKL_DEVICE_SDW_PCM = 0x7, + SKL_DEVICE_SDW_PDM = 0x8, + SKL_DEVICE_MAX = SKL_DEVICE_SDW_PDM, }; /** From a505620a0ae0fc7d8d15f511f68a5ea35b088e5e Mon Sep 17 00:00:00 2001 From: Jayachandran B Date: Wed, 27 Jan 2016 09:30:16 +0530 Subject: [PATCH 0744/1103] ASoC: Intel: Skylake: Debugfs for core power handling This Debugfs allows to test core power handling. To turn on a core: echo wake n > core_power To turn off a core: echo sleep n > core_power where, n is the core id (0 ... num cores on audio IP - 1) Note that when core 0 is turned on/off using this debug i/f, pm_runtime_get_sync/pm_runtime_put is called. Hence this debug i/f can be used for runtime PM unit tests without having to run usecases to invoke runtime PM. Change-Id: Id63fc95d99ed6ed78eccfba134f204fdd2f07629 Signed-off-by: Jayachandran B Reviewed-on: Reviewed-by: Babu, Ramesh Tested-by: Babu, Ramesh Reviewed-on: Reviewed-by: Kale, Sanyog R Reviewed-by: Avati, Santosh Kumar Tested-by: Avati, Santosh Kumar --- sound/soc/intel/skylake/skl-debug.c | 75 +++++++++++++++++++++++++++++ 1 file changed, 75 insertions(+) diff --git a/sound/soc/intel/skylake/skl-debug.c b/sound/soc/intel/skylake/skl-debug.c index 5cc274a8d531..2041e3387c03 100644 --- a/sound/soc/intel/skylake/skl-debug.c +++ b/sound/soc/intel/skylake/skl-debug.c @@ -16,6 +16,8 @@ #include #include #include +#include +#include #include "skl.h" #include "skl-sst-dsp.h" #include "skl-sst-ipc.h" @@ -764,6 +766,73 @@ static int skl_init_adsp(struct skl_debug *d) return 0; } +static ssize_t core_power_write(struct file *file, + const char __user *user_buf, size_t count, loff_t *ppos) +{ + struct skl_debug *d = file->private_data; + struct skl_sst *skl_ctx = d->skl->skl_sst; + struct sst_dsp *ctx = skl_ctx->dsp; + char buf[16]; + int len = min(count, (sizeof(buf) - 1)); + unsigned int core_id; + char *ptr; + int wake; + int err; + + + if (copy_from_user(buf, user_buf, len)) + return -EFAULT; + buf[len] = 0; + + /* + * The buffer content should be "wake n" or "sleep n", + * where n is the core id + */ + ptr = strnstr(buf, "wake", len); + if (ptr) { + ptr = ptr + 5; + wake = 1; + } else { + ptr = strnstr(buf, "sleep", len); + if (ptr) { + ptr = ptr + 6; + wake = 0; + } else + return -EINVAL; + } + + err = kstrtouint(ptr, 10, &core_id); + if (err) { + dev_err(d->dev, "%s: Debugfs kstrtouint returned error = %d\n", + __func__, err); + return err; + } + + dev_info(d->dev, "Debugfs: %s %d\n", wake ? "wake" : "sleep", core_id); + + if (wake) { + if (core_id == SKL_DSP_CORE0_ID) + pm_runtime_get_sync(d->dev); + else + skl_dsp_get_core(ctx, core_id); + } else { + if (core_id == SKL_DSP_CORE0_ID) + pm_runtime_put_sync(d->dev); + else + skl_dsp_put_core(ctx, core_id); + } + + /* Userspace has been fiddling around behind the kernel's back */ + add_taint(TAINT_USER, LOCKDEP_NOW_UNRELIABLE); + + return len; +} +static const struct file_operations core_power_fops = { + .open = simple_open, + .write = core_power_write, + .llseek = default_llseek, +}; + struct skl_debug *skl_debugfs_init(struct skl *skl) { struct skl_debug *d; @@ -797,6 +866,12 @@ struct skl_debug *skl_debugfs_init(struct skl *skl) goto err; } + if (!debugfs_create_file("core_power", 0644, d->fs, d, + &core_power_fops)) { + dev_err(d->dev, "core power debugfs init failed\n"); + goto err; + } + /* now create the NHLT dir */ d->nhlt = debugfs_create_dir("nhlt", d->fs); if (IS_ERR(d->nhlt) || !d->nhlt) { From 200aa1edaa25a04f1ea0cda4c04fb078b7e2f6e6 Mon Sep 17 00:00:00 2001 From: Pardha Saradhi K Date: Thu, 25 Feb 2016 12:06:18 +0530 Subject: [PATCH 0745/1103] ASoC: Intel: Skylake: DebugFs changes to suit FDK Following changes have been made in the generic ipc debugfs interface to better suit the FDK implementation 1. Return IO error on write operation if the ipc fails. 2. IPC read operations are exposed in binary format Change-Id: If0254fdb91030c917e0c0501089214d0654d39c7 Signed-off-by: Pardha Saradhi K Signed-off-by: Sanyog Kale Reviewed-on: Reviewed-by: Babu, Ramesh Tested-by: Babu, Ramesh Reviewed-on: Reviewed-by: audio_build Reviewed-by: Avati, Santosh Kumar Tested-by: Avati, Santosh Kumar --- sound/soc/intel/skylake/skl-debug.c | 67 +++++++---------------------- 1 file changed, 16 insertions(+), 51 deletions(-) diff --git a/sound/soc/intel/skylake/skl-debug.c b/sound/soc/intel/skylake/skl-debug.c index 2041e3387c03..587fcac48ec0 100644 --- a/sound/soc/intel/skylake/skl-debug.c +++ b/sound/soc/intel/skylake/skl-debug.c @@ -151,37 +151,11 @@ static ssize_t mod_control_read(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { struct skl_debug *d = file->private_data; - char *state; - char *buf1; - int ret; - unsigned int ofs = 0; - - if (d->ipc_data[0] == 0) { - state = d->skl->mod_set_get_status ? "Fail\n" : "success\n"; - return simple_read_from_buffer(user_buf, count, ppos, - state, strlen(state)); - } - - state = d->skl->mod_set_get_status ? "Fail\n" : "success\n"; - buf1 = kzalloc(MOD_BUF1, GFP_KERNEL); - if (!buf1) - return -ENOMEM; - - ret = snprintf(buf1, MOD_BUF1, - "%s\nLARGE PARAM DATA\n", state); - - for (ofs = 0 ; ofs < d->ipc_data[0] ; ofs += 16) { - ret += snprintf(buf1 + ret, MOD_BUF1 - ret, "0x%.4x : ", ofs); - hex_dump_to_buffer(&(d->ipc_data[1]) + ofs, 16, 16, 4, - buf1 + ret, MOD_BUF1 - ret, 0); - ret += strlen(buf1 + ret); - if (MOD_BUF1 - ret > 0) - buf1[ret++] = '\n'; - } + const u32 param_data_size = d->ipc_data[0]; + const u32 *param_data = &d->ipc_data[1]; - ret = simple_read_from_buffer(user_buf, count, ppos, buf1, ret); - kfree(buf1); - return ret; + return simple_read_from_buffer(user_buf, count, ppos, + param_data, param_data_size); } @@ -194,14 +168,14 @@ static ssize_t mod_control_write(struct file *file, int retval, type; ssize_t written; u32 size, mbsz; - u32 *large_data; - int large_param_size; struct skl_sst *ctx = d->skl->skl_sst; struct skl_ipc_large_config_msg msg; struct skl_ipc_header header = {0}; u64 *ipc_header = (u64 *)(&header); + d->ipc_data[0] = 0; + buf = kzalloc(MOD_BUF, GFP_KERNEL); written = simple_write_to_buffer(buf, MOD_BUF, ppos, user_buf, count); @@ -225,28 +199,22 @@ static ssize_t mod_control_write(struct file *file, msg.instance_id = ((header.primary) & 0x00ff0000)>>16; msg.large_param_id = ((header.extension) & 0x0ff00000)>>20; msg.param_data_size = (header.extension) & 0x000fffff; - large_param_size = msg.param_data_size; - - large_data = kzalloc(large_param_size, GFP_KERNEL); - if (!large_data) - return -ENOMEM; if (mbsz) retval = skl_ipc_get_large_config(&ctx->ipc, &msg, - large_data, &(mod_set_get->mailbx[0]), - mbsz, NULL); + &d->ipc_data[1], + &mod_set_get->mailbx[0], + mbsz, NULL); else retval = skl_ipc_get_large_config(&ctx->ipc, - &msg, large_data, NULL, - 0, NULL); - - d->ipc_data[0] = msg.param_data_size; - memcpy(&d->ipc_data[1], large_data, msg.param_data_size); - kfree(large_data); + &msg, + &d->ipc_data[1], + NULL, 0, NULL); + if (retval == 0) + d->ipc_data[0] = msg.param_data_size; break; case IPC_MOD_LARGE_CONFIG_SET: - d->ipc_data[0] = 0; msg.module_id = (header.primary) & 0x0000ffff; msg.instance_id = ((header.primary) & 0x00ff0000)>>16; msg.large_param_id = ((header.extension) & 0x0ff00000)>>20; @@ -254,7 +222,6 @@ static ssize_t mod_control_write(struct file *file, retval = skl_ipc_set_large_config(&ctx->ipc, &msg, (u32 *)(&mod_set_get->mailbx)); - d->ipc_data[0] = 0; break; default: @@ -266,14 +233,12 @@ static ssize_t mod_control_write(struct file *file, retval = sst_ipc_tx_message_wait(&ctx->ipc, *ipc_header, NULL, 0, NULL, NULL); - d->ipc_data[0] = 0; break; } + if (retval) - d->skl->mod_set_get_status = 1; - else - d->skl->mod_set_get_status = 0; + return -EIO; /* Userspace has been fiddling around behind the kernel's back */ add_taint(TAINT_USER, LOCKDEP_NOW_UNRELIABLE); From 3d7f4bb8e10b66f933d542d075a8f18435025b7e Mon Sep 17 00:00:00 2001 From: Pardha Saradhi K Date: Wed, 24 Feb 2016 17:04:30 +0530 Subject: [PATCH 0746/1103] ASoC: Intel: Skylake: Support Pipeline Properties IPC Add support to query te properties of any pipeline, that is running in te ADSP. This ipc involves sending the pipe id for which properties is sought for. This IPC is supported in a TLV format and so the associated changes in the framework are done. Typical syntax from a debugfs standpoint is echo 11, > adsp_prop_ctrl Change-Id: I69f0c5b0a6bbe93587ba6981b0f76e423fb97be8 Signed-off-by: Pardha Saradhi K Reviewed-on: Reviewed-by: Babu, Ramesh Tested-by: Babu, Ramesh Reviewed-on: Reviewed-by: Kale, Sanyog R Reviewed-by: Avati, Santosh Kumar Tested-by: Avati, Santosh Kumar --- sound/soc/intel/skylake/skl-debug.c | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/sound/soc/intel/skylake/skl-debug.c b/sound/soc/intel/skylake/skl-debug.c index 587fcac48ec0..8f6d11d49dd6 100644 --- a/sound/soc/intel/skylake/skl-debug.c +++ b/sound/soc/intel/skylake/skl-debug.c @@ -34,6 +34,8 @@ #define IPC_MOD_LARGE_CONFIG_GET 3 #define IPC_MOD_LARGE_CONFIG_SET 4 #define MOD_BUF1 (3 * PAGE_SIZE) +#define MAX_TLV_PAYLOAD_SIZE 4088 +#define EXTENDED_PARAMS_SZ 2 #define DEFAULT_SZ 100 #define DEFAULT_ID 0XFF @@ -43,6 +45,7 @@ #define HARDWARE_CONFIG_SZ 0x84 #define MODULES_INFO_SZ 0xa70 #define PIPELINE_LIST_INFO_SZ 0xc +#define PIPELINE_PROPS_SZ 0x60 #define SCHEDULERS_INFO_SZ 0x34 #define GATEWAYS_INFO_SZ 0x4e4 #define MEMORY_STATE_INFO_SZ 0x1000 @@ -585,7 +588,7 @@ static ssize_t adsp_control_write(struct file *file, struct skl_sst *ctx = d->skl->skl_sst; struct skl_ipc_large_config_msg msg; char id[8]; - u32 tx_data; + u32 tx_data[EXTENDED_PARAMS_SZ]; int j = 0, bufsize, tx_param = 0, tx_param_id; int len = min(count, (sizeof(buf)-1)); @@ -622,7 +625,8 @@ static ssize_t adsp_control_write(struct file *file, if (err) return -EINVAL; - tx_data = (tx_param_id << 8) | dsp_property; + tx_data[0] = (tx_param_id << 8) | dsp_property; + tx_data[1] = MAX_TLV_PAYLOAD_SIZE; } ipc_data = kzalloc(DSP_BUF, GFP_ATOMIC); @@ -657,6 +661,10 @@ static ssize_t adsp_control_write(struct file *file, replysz = PIPELINE_LIST_INFO_SZ; break; + case PIPELINE_PROPS: + replysz = PIPELINE_PROPS_SZ; + break; + case SCHEDULERS_INFO: replysz = SCHEDULERS_INFO_SZ; break; @@ -681,13 +689,18 @@ static ssize_t adsp_control_write(struct file *file, msg.module_id = 0x0; msg.instance_id = 0x0; - msg.large_param_id = dsp_property; + + if (tx_param == 1) + msg.large_param_id = 0xFF; + else + msg.large_param_id = dsp_property; + msg.param_data_size = replysz; if (tx_param == 1) skl_ipc_get_large_config(&ctx->ipc, &msg, - ipc_data, &tx_data, - sizeof(u32), NULL); + ipc_data, tx_data, + EXTENDED_PARAMS_SZ*sizeof(u32), NULL); else skl_ipc_get_large_config(&ctx->ipc, &msg, ipc_data, NULL, From 940e30853734463eef2b7d7abce57f982c60e38f Mon Sep 17 00:00:00 2001 From: Senthilnathan Veppur Date: Wed, 23 Mar 2016 16:46:08 +0530 Subject: [PATCH 0747/1103] ASoC: Intel: Skylake: Add check for buffer overflow Return error if source buffer would overflow in strncpy. Change-Id: I8637aa3fed40dd5f042cb041cc5a447506bc15d2 Signed-off-by: Senthilnathan Veppur Reviewed-on: Reviewed-by: Babu, Ramesh Tested-by: Babu, Ramesh Reviewed-on: Reviewed-by: Kale, Sanyog R Reviewed-by: Avati, Santosh Kumar Tested-by: Avati, Santosh Kumar --- sound/soc/intel/skylake/skl-debug.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/sound/soc/intel/skylake/skl-debug.c b/sound/soc/intel/skylake/skl-debug.c index 8f6d11d49dd6..6dc4ef3d7e8e 100644 --- a/sound/soc/intel/skylake/skl-debug.c +++ b/sound/soc/intel/skylake/skl-debug.c @@ -603,6 +603,10 @@ static ssize_t adsp_control_write(struct file *file, while (buf[j] != '\0') { if (buf[j] == ',') { + if ((bufsize-j) > sizeof(id)) { + dev_err(d->dev, "ID buffer overflow\n"); + return -EINVAL; + } strncpy(id, &buf[j+1], (bufsize-j)); buf[j] = '\0'; tx_param = 1; From de68214b8d34ef2ed8f362cbf00aa8552ccf86e9 Mon Sep 17 00:00:00 2001 From: Ramesh Babu Date: Fri, 9 Oct 2015 01:31:44 +0530 Subject: [PATCH 0748/1103] ASoC: Intel: Skylake: Increase the SSP count in debugFS Some of Broxton SKUs has 6 SSP ports, hence support them in debugFS. Change-Id: I0b8fdf9feed0a4484789f750134d7734e805e5bc Signed-off-by: Ramesh Babu Reviewed-on: Reviewed-on: Reviewed-by: Kale, Sanyog R Reviewed-by: Avati, Santosh Kumar Tested-by: Avati, Santosh Kumar --- sound/soc/intel/skylake/skl-debug.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/intel/skylake/skl-debug.c b/sound/soc/intel/skylake/skl-debug.c index 6dc4ef3d7e8e..07b3309bf4ee 100644 --- a/sound/soc/intel/skylake/skl-debug.c +++ b/sound/soc/intel/skylake/skl-debug.c @@ -29,7 +29,7 @@ #define MOD_BUF (2 * PAGE_SIZE) #define FW_REG_BUF PAGE_SIZE #define FW_REG_SIZE 0x60 -#define MAX_SSP 4 +#define MAX_SSP 6 #define MAX_SZ 1025 #define IPC_MOD_LARGE_CONFIG_GET 3 #define IPC_MOD_LARGE_CONFIG_SET 4 From 6b0281f045a81cf4dd12a940130e4002fb0c06f4 Mon Sep 17 00:00:00 2001 From: "Mallikarjun, chippalkatti" Date: Mon, 24 Apr 2017 14:43:23 +0530 Subject: [PATCH 0749/1103] ASoC: Intel: CNL: Retrieve module id from GUID This patch retrieves module id from GUID for copier module used for SoundWire BRA feature. Change-Id: Ib9453f929e4b0280535a2151851ac04c5098c806 Signed-off-by: Mallikarjun, chippalkatti Signed-off-by: Dharageswari R Reviewed-on: Reviewed-by: audio_build Reviewed-by: Singh, Guneshwor O Reviewed-by: Prodduvaka, Leoni Reviewed-by: Kp, Jeeja Reviewed-by: Diwakar, Praveen Tested-by: Avati, Santosh Kumar --- sound/soc/intel/skylake/skl-messages.c | 14 ++++---------- sound/soc/intel/skylake/skl-sst-dsp.h | 1 + sound/soc/intel/skylake/skl-topology.c | 3 ++- 3 files changed, 7 insertions(+), 11 deletions(-) diff --git a/sound/soc/intel/skylake/skl-messages.c b/sound/soc/intel/skylake/skl-messages.c index 54bd83eb55b5..2c0cf7a96128 100644 --- a/sound/soc/intel/skylake/skl-messages.c +++ b/sound/soc/intel/skylake/skl-messages.c @@ -385,12 +385,9 @@ static int cnl_sdw_bra_pipe_cfg_pb(struct skl_sst *ctx, host_cpr_params.s_fmt = 32; host_cpr_params.linktype = 0; host_cpr_params.stream = 0; + host_cpr_cfg.id.module_id = skl_get_module_id(ctx, + (uuid_le *)host_cpr_cfg.guid); -#if IS_ENABLED(CONFIG_SND_SOC_INTEL_CNL_FPGA) - host_cpr_cfg.id.module_id = 3; -#else - host_cpr_cfg.id.module_id = 4; -#endif host_cpr_cfg.id.instance_id = 1; host_cpr_cfg.id.pvt_id = skl_get_pvt_id(ctx, (uuid_le *)host_cpr_cfg.guid, host_cpr_cfg.id.instance_id); @@ -617,12 +614,9 @@ static int cnl_sdw_bra_pipe_cfg_cp(struct skl_sst *ctx, link_cpr_params.s_fmt = 32; link_cpr_params.linktype = 0; link_cpr_params.stream = 0; + host_cpr_cfg.id.module_id = skl_get_module_id(ctx, + (uuid_le *)host_cpr_cfg.guid); -#if IS_ENABLED(CONFIG_SND_SOC_INTEL_CNL_FPGA) - link_cpr_cfg.id.module_id = 3; -#else - link_cpr_cfg.id.module_id = 4; -#endif link_cpr_cfg.id.instance_id = 3; link_cpr_cfg.id.pvt_id = skl_get_pvt_id(ctx, (uuid_le *)link_cpr_cfg.guid, link_cpr_cfg.id.instance_id); diff --git a/sound/soc/intel/skylake/skl-sst-dsp.h b/sound/soc/intel/skylake/skl-sst-dsp.h index 7be2cdeb85b6..402fb875272c 100644 --- a/sound/soc/intel/skylake/skl-sst-dsp.h +++ b/sound/soc/intel/skylake/skl-sst-dsp.h @@ -278,6 +278,7 @@ int bxt_load_library(struct sst_dsp *ctx, struct skl_lib_info *linfo, int lib_co int snd_skl_parse_uuids(struct sst_dsp *ctx, const struct firmware *fw, unsigned int offset, int index); +int skl_get_module_id(struct skl_sst *ctx, uuid_le *uuid_mod); int skl_get_pvt_id(struct skl_sst *ctx, uuid_le *uuid_mod, int instance_id); int skl_put_pvt_id(struct skl_sst *ctx, uuid_le *uuid_mod, int *pvt_id); int skl_get_pvt_instance_id_map(struct skl_sst *ctx, diff --git a/sound/soc/intel/skylake/skl-topology.c b/sound/soc/intel/skylake/skl-topology.c index 711629143aa3..2dfb454f7007 100644 --- a/sound/soc/intel/skylake/skl-topology.c +++ b/sound/soc/intel/skylake/skl-topology.c @@ -1040,7 +1040,7 @@ static int skl_tplg_set_module_bind_params(struct snd_soc_dapm_widget *w, return 0; } -static int skl_get_module_id(struct skl_sst *ctx, uuid_le *uuid) +int skl_get_module_id(struct skl_sst *ctx, uuid_le *uuid) { struct uuid_module *module; @@ -1051,6 +1051,7 @@ static int skl_get_module_id(struct skl_sst *ctx, uuid_le *uuid) return -EINVAL; } +EXPORT_SYMBOL_GPL(skl_get_module_id); static int skl_tplg_find_moduleid_from_uuid(struct skl *skl, const struct snd_kcontrol_new *k) From 87411685ffd5c47c390dd379b99f3ebdd6ae9f4e Mon Sep 17 00:00:00 2001 From: Sanyog Kale Date: Fri, 24 Mar 2017 03:40:36 +0530 Subject: [PATCH 0750/1103] ASoc: rt700: Remove runtime get and put from set bias Earlier runtime get and put was added while setting bias level to make sure the clock to the codec is ON. Since clock will be ON while setting bias level, get and put can be removed from set bias level API. Change-Id: Ic8fe49a5dfda5a2f1d5bf49db8a669e0297d10b1 Signed-off-by: Sanyog Kale Reviewed-on: Reviewed-by: R, Dharageswari Reviewed-by: Singh, Guneshwor O Reviewed-by: Prusty, Subhransu S Reviewed-by: Kp, Jeeja Tested-by: Avati, Santosh Kumar --- sound/soc/codecs/rt700.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/sound/soc/codecs/rt700.c b/sound/soc/codecs/rt700.c index 8fc9c00e15b8..c88aa16a4280 100644 --- a/sound/soc/codecs/rt700.c +++ b/sound/soc/codecs/rt700.c @@ -1115,7 +1115,6 @@ static int rt700_set_bias_level(struct snd_soc_component *component, switch (level) { case SND_SOC_BIAS_PREPARE: if (SND_SOC_BIAS_STANDBY == dapm->bias_level) { - pm_runtime_get_sync(&rt700->sdw->mstr->dev); snd_soc_component_write(component, RT700_SET_AUDIO_POWER_STATE, AC_PWRST_D0); } @@ -1124,7 +1123,6 @@ static int rt700_set_bias_level(struct snd_soc_component *component, case SND_SOC_BIAS_STANDBY: snd_soc_component_write(component, RT700_SET_AUDIO_POWER_STATE, AC_PWRST_D3); - pm_runtime_put_sync_autosuspend(&rt700->sdw->mstr->dev); break; default: From a2191110fdc3c47a3cd5ea345b8a4bfee11df0e2 Mon Sep 17 00:00:00 2001 From: "Pawse, GuruprasadX" Date: Wed, 12 Apr 2017 16:07:43 +0530 Subject: [PATCH 0751/1103] ASoC: Intel: board: Add SSP0 codec-codec link Change-Id: I111f0405411125a8176aa6e9c8295f72059ab2e2 Signed-off-by: Pawse, GuruprasadX Reviewed-on: Reviewed-by: Singh, Guneshwor O Reviewed-by: Diwakar, Praveen Reviewed-by: R, Dharageswari Reviewed-by: Kale, Sanyog R Reviewed-by: Kp, Jeeja Reviewed-by: Babu, Ramesh Tested-by: Avati, Santosh Kumar --- sound/soc/intel/boards/cnl_rt274.c | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/sound/soc/intel/boards/cnl_rt274.c b/sound/soc/intel/boards/cnl_rt274.c index 21b40b895629..3615c79e5bc5 100644 --- a/sound/soc/intel/boards/cnl_rt274.c +++ b/sound/soc/intel/boards/cnl_rt274.c @@ -60,6 +60,14 @@ static const struct snd_soc_dapm_widget cnl_rt274_widgets[] = { SND_SOC_DAPM_MIC("SoC DMIC", NULL), }; +static const struct snd_soc_pcm_stream dai_params_codec = { + .formats = SNDRV_PCM_FMTBIT_S24_LE, + .rate_min = 48000, + .rate_max = 48000, + .channels_min = 2, + .channels_max = 2, +}; + static int cnl_dmic_fixup(struct snd_soc_pcm_runtime *rtd, struct snd_pcm_hw_params *params) { @@ -286,6 +294,20 @@ static struct snd_soc_dai_link cnl_rt274_msic_dailink[] = { .dpcm_capture = 1, .be_hw_params_fixup = cnl_dmic_fixup, }, + /* codec-codec link */ + { + .name = "CNL SSP0-Loop Port", + .stream_name = "CNL SSP0-Loop", + .cpu_dai_name = "SSP0 Pin", + .platform_name = pname, + .codec_name = cname, + .codec_dai_name = "rt274-aif1", + .params = &dai_params_codec, + .dsp_loopback = true, + .dai_fmt = SND_SOC_DAIFMT_DSP_A | + SND_SOC_DAIFMT_NB_NF | + SND_SOC_DAIFMT_CBS_CFS, + }, }; static int From bf94c97033aed76dc7106aa95495119ec25d80b6 Mon Sep 17 00:00:00 2001 From: Guneshwor Singh Date: Tue, 4 Apr 2017 08:34:51 +0530 Subject: [PATCH 0752/1103] ASoC: Intel: board: Move cnl_rt274 clock setting to supply widget During BE-BE loop, codec clocks were not set as it was a part of dai link ops hw_params and no sound was heard due to this reason when use cases involve BE-BE loop. So, move codec clock setting as a part of supply widget and define routes appropriately. Also use macro to define BE rate fixup and use it for both fixup as well as clock computation. Change-Id: Id5a08d2bd6024a61b601dbbe70ad99a52149da5e Signed-off-by: Guneshwor Singh Reviewed-on: Reviewed-by: Prusty, Subhransu S Reviewed-by: Kale, Sanyog R Reviewed-by: R, Dharageswari Reviewed-by: Diwakar, Praveen Tested-by: Avati, Santosh Kumar --- sound/soc/intel/boards/cnl_rt274.c | 89 +++++++++++++++++++----------- 1 file changed, 57 insertions(+), 32 deletions(-) diff --git a/sound/soc/intel/boards/cnl_rt274.c b/sound/soc/intel/boards/cnl_rt274.c index 3615c79e5bc5..4b434dc82b9f 100644 --- a/sound/soc/intel/boards/cnl_rt274.c +++ b/sound/soc/intel/boards/cnl_rt274.c @@ -35,6 +35,56 @@ #include "../../codecs/rt274.h" +#define CNL_FREQ_OUT 19200000 +#define CNL_BE_FIXUP_RATE 48000 +#define RT274_CODEC_DAI "rt274-aif1" + +static struct snd_soc_dai *cnl_get_codec_dai(struct snd_soc_card *card, + const char *dai_name) +{ + struct snd_soc_pcm_runtime *rtd; + + list_for_each_entry(rtd, &card->rtd_list, list) { + if (!strcmp(rtd->codec_dai->name, dai_name)) + return rtd->codec_dai; + } + + return NULL; +} + +static int cnl_rt274_clock_control(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *k, int event) +{ + struct snd_soc_dapm_context *dapm = w->dapm; + struct snd_soc_card *card = dapm->card; + int ret = 0, ratio = 100; + struct snd_soc_dai *codec_dai = cnl_get_codec_dai(card, + RT274_CODEC_DAI); + + /* Codec needs clock for Jack detection and button press */ + ret = snd_soc_dai_set_sysclk(codec_dai, RT274_SCLK_S_PLL2, + CNL_FREQ_OUT, SND_SOC_CLOCK_IN); + if (ret < 0) { + dev_err(codec_dai->dev, "set codec sysclk failed: %d\n", ret); + return ret; + } + + if (SND_SOC_DAPM_EVENT_ON(event)) { + snd_soc_dai_set_bclk_ratio(codec_dai, ratio); + + ret = snd_soc_dai_set_pll(codec_dai, 0, RT274_PLL2_S_BCLK, + CNL_BE_FIXUP_RATE * ratio, + CNL_FREQ_OUT); + if (ret) { + dev_err(codec_dai->dev, + "failed to enable PLL2: %d\n", ret); + return ret; + } + } + + return ret; +} + static struct snd_soc_jack cnl_headset; /* Headset jack detection DAPM pins */ @@ -58,6 +108,9 @@ static const struct snd_soc_dapm_widget cnl_rt274_widgets[] = { SND_SOC_DAPM_HP("Headphone Jack", NULL), SND_SOC_DAPM_MIC("Mic Jack", NULL), SND_SOC_DAPM_MIC("SoC DMIC", NULL), + SND_SOC_DAPM_SUPPLY("Platform Clock", SND_SOC_NOPM, 0, 0, + cnl_rt274_clock_control, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMD), }; static const struct snd_soc_pcm_stream dai_params_codec = { @@ -102,6 +155,9 @@ static const struct snd_soc_dapm_route cnl_map[] = { {"ssp0 Rx", NULL, "AIF1 Capture"}, {"codec0_in", NULL, "ssp0 Rx"}, + + {"Headphone Jack", NULL, "Platform Clock"}, + {"MIC", NULL, "Platform Clock"}, }; static int cnl_rt274_init(struct snd_soc_pcm_runtime *runtime) @@ -140,7 +196,7 @@ static int cnl_be_fixup(struct snd_soc_pcm_runtime *rtd, struct snd_interval *channels = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS); - rate->min = rate->max = 48000; + rate->min = rate->max = CNL_BE_FIXUP_RATE; channels->min = channels->max = 2; snd_mask_none(hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT)); snd_mask_set(hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT), @@ -149,36 +205,6 @@ static int cnl_be_fixup(struct snd_soc_pcm_runtime *rtd, return 0; } -#define CNL_FREQ_OUT 19200000 - -static int rt274_hw_params(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *params) -{ - struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_dai *codec_dai = rtd->codec_dai; - int ret, ratio = 100; - - snd_soc_dai_set_bclk_ratio(codec_dai, ratio); - - ret = snd_soc_dai_set_pll(codec_dai, 0, RT274_PLL2_S_BCLK, - ratio * params_rate(params), CNL_FREQ_OUT); - if (ret != 0) { - dev_err(rtd->dev, "Failed to enable PLL2 with Ref Clock Loop: %d\n", ret); - return ret; - } - - ret = snd_soc_dai_set_sysclk(codec_dai, RT274_SCLK_S_PLL2, CNL_FREQ_OUT, - SND_SOC_CLOCK_IN); - if (ret < 0) - dev_err(rtd->dev, "set codec sysclk failed: %d\n", ret); - - return ret; -} - -static struct snd_soc_ops rt274_ops = { - .hw_params = rt274_hw_params, -}; - #if IS_ENABLED(CONFIG_SND_SOC_INTEL_CNL_FPGA) static const char pname[] = "0000:02:18.0"; static const char cname[] = "rt274.0-001c"; @@ -268,7 +294,6 @@ static struct snd_soc_dai_link cnl_rt274_msic_dailink[] = { .dpcm_playback = 1, .dpcm_capture = 1, .init = cnl_rt274_init, - .ops = &rt274_ops, }, { .name = "SSP1-Codec", From c2f194aea5836cd2f7dc1af9f87f1b4162b698d1 Mon Sep 17 00:00:00 2001 From: Sanyog Kale Date: Fri, 21 Apr 2017 12:21:17 +0530 Subject: [PATCH 0753/1103] SoundWire: Select default frame shape based on platform This patch selects default frame shape in SoundWire controller init based on FPGA/NONFPGA platform. Change-Id: I07cb3f578367ef3afb4ea1e0db905d562d134f8f Signed-off-by: Sanyog Kale Reviewed-on: Reviewed-by: Prodduvaka, Leoni Reviewed-by: Kp, Jeeja Reviewed-by: Diwakar, Praveen Reviewed-by: R, Dharageswari Tested-by: Avati, Santosh Kumar --- drivers/sdw/sdw_cnl_priv.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/sdw/sdw_cnl_priv.h b/drivers/sdw/sdw_cnl_priv.h index 504df88d681a..b7f44e1f9d6f 100644 --- a/drivers/sdw/sdw_cnl_priv.h +++ b/drivers/sdw/sdw_cnl_priv.h @@ -28,12 +28,13 @@ #define SDW_CNL_CMD_WORD_LEN 4 #define SDW_CNL_DEFAULT_SSP_INTERVAL 0x18 #define SDW_CNL_DEFAULT_CLK_DIVIDER 0 -#define SDW_CNL_DEFAULT_FRAME_SHAPE 0x30 #if IS_ENABLED(CONFIG_SND_SOC_INTEL_CNL_FPGA) #define SDW_CNL_DEFAULT_SYNC_PERIOD 0x257F +#define SDW_CNL_DEFAULT_FRAME_SHAPE 0x48 #else #define SDW_CNL_DEFAULT_SYNC_PERIOD 0x176F +#define SDW_CNL_DEFAULT_FRAME_SHAPE 0x30 #endif #define SDW_CNL_PORT_REG_OFFSET 0x80 From aa2c4dde783527dfd6a0d6fecd63376997e47b10 Mon Sep 17 00:00:00 2001 From: Leoni Prodduvaka Date: Tue, 18 Apr 2017 23:02:20 +0530 Subject: [PATCH 0754/1103] ASoC: rt700: Added support for ICL FPGA SDW Aggregation Adding RT700 codec slave id connected to Master 1 used for aggregation and the master and slave id combination more general across different platforms Change-Id: If2552c5b56a7508179263791ccd996703ccf64c7 Signed-off-by: Leoni Prodduvaka Reviewed-on: Reviewed-by: audio_build Reviewed-by: R, Dharageswari Reviewed-by: Kp, Jeeja Reviewed-by: Diwakar, Praveen Tested-by: Avati, Santosh Kumar --- sound/soc/codecs/rt700-sdw.c | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/sound/soc/codecs/rt700-sdw.c b/sound/soc/codecs/rt700-sdw.c index 1c935e3a05e2..8a7fbc5a40c7 100644 --- a/sound/soc/codecs/rt700-sdw.c +++ b/sound/soc/codecs/rt700-sdw.c @@ -348,15 +348,10 @@ static const struct sdw_slv_id rt700_id[] = { {"15:02:5d:07:01:00", 0}, {"16:02:5d:07:01:00", 0}, {"17:02:5d:07:01:00", 0}, -#ifndef CONFIG_SND_SOC_INTEL_CNL_FPGA -#ifndef CONFIG_SND_SOC_SDW_AGGM1M2 - {"10:02:5d:07:00:01", 0}, -#else {"10:02:5d:07:00:01", 1}, + {"10:02:5d:07:01:01", 1}, {"10:02:5d:07:01:02", 2}, {"10:02:5d:07:01:03", 3}, -#endif -#endif {} }; From fab89b83c79d868226c9256c09b1263f15f171ca Mon Sep 17 00:00:00 2001 From: Leoni Prodduvaka Date: Tue, 18 Apr 2017 23:02:32 +0530 Subject: [PATCH 0755/1103] ASoC: Intel: Boards: Add support for ICL FPGA SDW Aggregation Aggregation is performed on ICL FPGA with Master 1 and Master2 hence added slave id Change-Id: Ic6506769242c099582b7435ca2bd338d3a7ff919 Signed-off-by: Leoni Prodduvaka Reviewed-on: Reviewed-by: audio_build Reviewed-by: Kp, Jeeja Reviewed-by: R, Dharageswari Reviewed-by: Diwakar, Praveen Reviewed-by: Jayanti, Satya Charitardha Tested-by: Avati, Santosh Kumar --- sound/soc/intel/boards/cnl_rt700.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/sound/soc/intel/boards/cnl_rt700.c b/sound/soc/intel/boards/cnl_rt700.c index ee8fe3934571..dcb02b08a91c 100644 --- a/sound/soc/intel/boards/cnl_rt700.c +++ b/sound/soc/intel/boards/cnl_rt700.c @@ -168,8 +168,12 @@ static int cnl_dmic_fixup(struct snd_soc_pcm_runtime *rtd, #if IS_ENABLED(CONFIG_SND_SOC_INTEL_CNL_FPGA) static const char pname[] = "0000:02:18.0"; +#ifndef CONFIG_SND_SOC_SDW_AGGM1M2 static const char cname[] = "sdw-slave0-10:02:5d:07:01:00"; #else +static const char cname[] = "sdw-slave1-10:02:5d:07:01:01"; +#endif +#else static const char pname[] = "0000:00:1f.3"; static const char cname[] = "sdw-slave1-10:02:5d:07:00:01"; #endif From 73935b50e6e7d0f13ae23a3de7f9c1453ec3eb9f Mon Sep 17 00:00:00 2001 From: Leoni Prodduvaka Date: Tue, 18 Apr 2017 22:57:51 +0530 Subject: [PATCH 0756/1103] ASoC: Intel: CNL: Add support for ICL FPGA SDW Aggregation Added Master 1 as the skl_platform_dai id for icl fpga sdw aggregation Change-Id: Ia21d008dddad0ca0928b7903d6760e0da0433b04 Signed-off-by: Leoni Prodduvaka Reviewed-on: Reviewed-by: audio_build Reviewed-by: Kale, Sanyog R Reviewed-by: Kp, Jeeja Reviewed-by: R, Dharageswari Reviewed-by: Diwakar, Praveen Reviewed-by: Jayanti, Satya Charitardha Tested-by: Avati, Santosh Kumar --- sound/soc/intel/skylake/skl-pcm.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/sound/soc/intel/skylake/skl-pcm.c b/sound/soc/intel/skylake/skl-pcm.c index 361b85bfa792..c1ddf5ce1852 100644 --- a/sound/soc/intel/skylake/skl-pcm.c +++ b/sound/soc/intel/skylake/skl-pcm.c @@ -1321,7 +1321,11 @@ static struct snd_soc_dai_driver skl_platform_dai[] = { */ .name = "SDW Pin", #if IS_ENABLED(CONFIG_SND_SOC_INTEL_CNL_FPGA) +#ifndef CONFIG_SND_SOC_SDW_AGGM1M2 .id = SDW_BE_DAI_ID_MSTR0, +#else + .id = SDW_BE_DAI_ID_MSTR1, +#endif #else .id = SDW_BE_DAI_ID_MSTR1, #endif @@ -1347,7 +1351,11 @@ static struct snd_soc_dai_driver skl_platform_dai[] = { */ .name = "SDW10 Pin", #if IS_ENABLED(CONFIG_SND_SOC_INTEL_CNL_FPGA) +#ifndef CONFIG_SND_SOC_SDW_AGGM1M2 .id = SDW_BE_DAI_ID_MSTR0, +#else + .id = SDW_BE_DAI_ID_MSTR1, +#endif #else .id = SDW_BE_DAI_ID_MSTR1, #endif From ec3e88741d441f3a585351406dd8e1a6bb32f78a Mon Sep 17 00:00:00 2001 From: "Diwakar, Praveen" Date: Wed, 28 Jun 2017 16:42:00 +0530 Subject: [PATCH 0757/1103] ASoC: Intel: Skylake: Add support for getting hw config from DSP This patch gets hw config from DSP by sending hw config ipc. Signed-off-by: Diwakar, Praveen --- sound/soc/intel/skylake/skl-sst-dsp.h | 14 +++ sound/soc/intel/skylake/skl-sst-ipc.h | 41 +++++++++ sound/soc/intel/skylake/skl-sst-utils.c | 111 ++++++++++++++++++++++++ 3 files changed, 166 insertions(+) diff --git a/sound/soc/intel/skylake/skl-sst-dsp.h b/sound/soc/intel/skylake/skl-sst-dsp.h index 402fb875272c..baec42299419 100644 --- a/sound/soc/intel/skylake/skl-sst-dsp.h +++ b/sound/soc/intel/skylake/skl-sst-dsp.h @@ -163,6 +163,19 @@ enum skl_fw_info_type { SKL_CLOCKS_CONFIG, }; +enum skl_hw_info_type { + SKL_CAVS_VERSION = 0, + SKL_DSP_CORES, + SKL_MEM_PAGE_TYPES, + SKL_TOTAL_PHYS_MEM_PAGES, + SKL_I2S_CAPS, + SKL_GPDMA_CAPS, + SKL_GATEWAY_COUNT, + SKL_HB_EBB_COUNT, + SKL_LP_EBB_COUNT, + SKL_EBB_SIZE_BYTES, +}; + /* DSP Core state */ enum skl_dsp_states { SKL_DSP_RUNNING = 1, @@ -299,6 +312,7 @@ int skl_prepare_lib_load(struct skl_sst *skl, struct skl_lib_info *linfo, void skl_release_library(struct skl_lib_info *linfo, int lib_count); int skl_get_firmware_configuration(struct sst_dsp *ctx); +int skl_get_hardware_configuration(struct sst_dsp *ctx); int bxt_set_dsp_D0i0(struct sst_dsp *ctx); diff --git a/sound/soc/intel/skylake/skl-sst-ipc.h b/sound/soc/intel/skylake/skl-sst-ipc.h index f3b8d7bc68ec..43d3eedad780 100644 --- a/sound/soc/intel/skylake/skl-sst-ipc.h +++ b/sound/soc/intel/skylake/skl-sst-ipc.h @@ -188,6 +188,44 @@ struct skl_fw_property_info { struct skl_dma_buff_config *dma_config; }; +enum skl_cavs_version { + CAVS_VER_NA = 0x0, + CAVS_VER_1_5 = 0x1005, + CAVS_VER_1_8 = 0x1008 +}; + +enum skl_i2s_version { + I2S_VER_15_SKYLAKE = 0x00000, + I2S_VER_15_BROXTON = 0x10000, + I2S_VER_15_BROXTON_P = 0x20000 +}; + +struct skl_i2s_capabilities { + enum skl_i2s_version version; + u32 controller_count; + u32 *controller_base_addr; +}; + +struct skl_gpdma_capabilities { + u32 lp_ctrl_count; + u32 *lp_ch_count; + u32 hp_ctrl_count; + u32 *hp_ch_count; +}; + +struct skl_hw_property_info { + enum skl_cavs_version cavs_version; + u32 dsp_cores; + u32 mem_page_bytes; + u32 total_phys_mem_pages; + struct skl_i2s_capabilities i2s_caps; + struct skl_gpdma_capabilities gpdma_caps; + u32 gateway_count; + u32 hb_ebb_count; + u32 lp_ebb_count; + u32 ebb_size_bytes; +}; + struct skl_sst { struct device *dev; struct sst_dsp *dsp; @@ -248,6 +286,9 @@ struct skl_sst { /* firmware configuration information */ struct skl_fw_property_info fw_property; + /* hardware configuration information */ + struct skl_hw_property_info hw_property; + /* sysfs for module info */ struct skl_sysfs_tree *sysfs_tree; }; diff --git a/sound/soc/intel/skylake/skl-sst-utils.c b/sound/soc/intel/skylake/skl-sst-utils.c index 0eaa88e6ae61..7288893ce61a 100644 --- a/sound/soc/intel/skylake/skl-sst-utils.c +++ b/sound/soc/intel/skylake/skl-sst-utils.c @@ -359,6 +359,117 @@ int snd_skl_parse_uuids(struct sst_dsp *ctx, const struct firmware *fw, return ret; } +static int skl_parse_hw_config_info(struct sst_dsp *ctx, u8 *src, int limit) +{ + struct skl_tlv_message *message; + int offset = 0, shift; + u32 *value; + struct skl_sst *skl = ctx->thread_context; + struct skl_hw_property_info *hw_property = &skl->hw_property; + enum skl_hw_info_type type; + + while (offset < limit) { + + message = (struct skl_tlv_message *)src; + if (message == NULL) + break; + + /* Skip TLV header to read value */ + src += sizeof(struct skl_tlv_message); + + value = (u32 *)src; + type = message->type; + + switch (type) { + case SKL_CAVS_VERSION: + hw_property->cavs_version = *value; + break; + + case SKL_DSP_CORES: + hw_property->dsp_cores = *value; + break; + + case SKL_MEM_PAGE_TYPES: + hw_property->mem_page_bytes = *value; + break; + + case SKL_TOTAL_PHYS_MEM_PAGES: + hw_property->total_phys_mem_pages = *value; + break; + + case SKL_I2S_CAPS: + memcpy(&hw_property->i2s_caps, value, + sizeof(hw_property->i2s_caps)); + break; + + case SKL_GPDMA_CAPS: + memcpy(&hw_property->gpdma_caps, value, + sizeof(hw_property->gpdma_caps)); + break; + + case SKL_GATEWAY_COUNT: + hw_property->gateway_count = *value; + break; + + case SKL_HB_EBB_COUNT: + hw_property->hb_ebb_count = *value; + break; + + case SKL_LP_EBB_COUNT: + hw_property->lp_ebb_count = *value; + break; + + case SKL_EBB_SIZE_BYTES: + hw_property->ebb_size_bytes = *value; + break; + + default: + dev_err(ctx->dev, "Invalid hw info type:%d \n", type); + break; + } + + shift = message->length + sizeof(*message); + offset += shift; + /* skip over to next tlv data */ + src += message->length; + } + + return 0; +} + +int skl_get_hardware_configuration(struct sst_dsp *ctx) +{ + struct skl_ipc_large_config_msg msg; + struct skl_sst *skl = ctx->thread_context; + u8 *ipc_data; + int ret = 0; + size_t rx_bytes; + + ipc_data = kzalloc(DSP_BUF, GFP_KERNEL); + if (!ipc_data) + return -ENOMEM; + + msg.module_id = 0; + msg.instance_id = 0; + msg.large_param_id = HARDWARE_CONFIG; + msg.param_data_size = DSP_BUF; + + ret = skl_ipc_get_large_config(&skl->ipc, &msg, + (u32 *)ipc_data, NULL, 0, &rx_bytes); + if (ret < 0) { + dev_err(ctx->dev, "failed to get hw configuration !!!\n"); + goto err; + } + + ret = skl_parse_hw_config_info(ctx, ipc_data, rx_bytes); + if (ret < 0) + dev_err(ctx->dev, "failed to parse configuration !!!\n"); + +err: + kfree(ipc_data); + return ret; +} + void skl_freeup_uuid_list(struct skl_sst *ctx) { struct uuid_module *uuid, *_uuid; From b9d490de3dac1ef990af2a1ca9561fa3911726aa Mon Sep 17 00:00:00 2001 From: "Diwakar, Praveen" Date: Wed, 28 Jun 2017 16:49:23 +0530 Subject: [PATCH 0758/1103] ASoC: Intel: Skylake: Get dsp core count from hw config ipc Instead of getting dsp core count from hard coded value, use hw config ipc to reterive the same. Signed-off-by: Diwakar, Praveen --- sound/soc/intel/skylake/cnl-sst.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/sound/soc/intel/skylake/cnl-sst.c b/sound/soc/intel/skylake/cnl-sst.c index 5ee7b350eb7f..9441a724ea59 100644 --- a/sound/soc/intel/skylake/cnl-sst.c +++ b/sound/soc/intel/skylake/cnl-sst.c @@ -272,6 +272,16 @@ static int cnl_load_base_firmware(struct sst_dsp *ctx) goto cnl_load_base_firmware_failed; } + ret = skl_get_hardware_configuration(ctx); + if (ret < 0) { + dev_err(ctx->dev, "hwconfig ipc failed !\n"); + ret = -EIO; + goto cnl_load_base_firmware_failed; + } + + /* Update dsp core count retrieved from hw config IPC */ + cnl->cores.count = cnl->hw_property.dsp_cores; + return 0; cnl_load_base_firmware_failed: From 891dcfdd6bb973d5e01d943f3c0857fbf4f9b5be Mon Sep 17 00:00:00 2001 From: Anamika Lal Date: Wed, 3 May 2017 01:20:41 +0530 Subject: [PATCH 0759/1103] ASoC: Intel: Skylake: Add user notification event for pipe creation/deletion This patch provides kernel event generation on pipeline creation/deletion and also update the debugfs file with event timestamp. Userspace gets the event for each pipe creation and deletion and can read the event timestamp from the debugfs. Change-Id: I6e015e5f3e5285ecad215a37dfe286a4e3dc3435 Signed-off-by: Guneshwor Singh Signed-off-by: Anamika Lal Reviewed-on: Reviewed-by: audio_build Reviewed-by: Prusty, Subhransu S Reviewed-by: Koul, Vinod Tested-by: Sm, Bhadur A --- sound/soc/intel/skylake/skl-debug.c | 80 ++++++++++++++++++++++++++ sound/soc/intel/skylake/skl-messages.c | 2 + sound/soc/intel/skylake/skl.h | 4 ++ 3 files changed, 86 insertions(+) diff --git a/sound/soc/intel/skylake/skl-debug.c b/sound/soc/intel/skylake/skl-debug.c index 07b3309bf4ee..f3c5e29afaa6 100644 --- a/sound/soc/intel/skylake/skl-debug.c +++ b/sound/soc/intel/skylake/skl-debug.c @@ -56,6 +56,11 @@ struct nhlt_blob { struct nhlt_specific_cfg *cfg; }; +struct skl_pipe_event_data { + long event_time; + int event_type; +}; + struct skl_debug { struct skl *skl; struct device *dev; @@ -68,6 +73,7 @@ struct skl_debug { struct nhlt_blob dmic_blob; u32 ipc_data[MAX_SZ]; struct fw_ipc_data fw_ipc_data; + struct skl_pipe_event_data data; }; struct nhlt_specific_cfg @@ -815,9 +821,76 @@ static const struct file_operations core_power_fops = { .llseek = default_llseek, }; +void skl_dbg_event(struct skl_sst *ctx, int type) +{ + int retval; + struct timeval pipe_event_tv; + struct skl *skl = get_skl_ctx(ctx->dev); + struct kobject *kobj; + + kobj = &skl->component->dev->kobj; + + if (type == SKL_PIPE_CREATED) + /* pipe creation event */ + retval = kobject_uevent(kobj, KOBJ_ADD); + else if (type == SKL_PIPE_INVALID) + /* pipe deletion event */ + retval = kobject_uevent(kobj, KOBJ_REMOVE); + else + return; + + if (retval < 0) { + dev_err(ctx->dev, + "pipeline uevent failed, ret = %d\n", retval); + return; + } + + do_gettimeofday(&pipe_event_tv); + + skl->debugfs->data.event_time = pipe_event_tv.tv_usec; + skl->debugfs->data.event_type = type; +} + +static ssize_t skl_dbg_event_read(struct file *file, + char __user *user_buf, size_t count, loff_t *ppos) +{ + struct skl_debug *d = file->private_data; + char buf[32]; + char pipe_state[24]; + int retval; + + if (d->data.event_type) + strcpy(pipe_state, "SKL_PIPE_CREATED"); + else + strcpy(pipe_state, "SKL_PIPE_INVALID"); + + retval = snprintf(buf, sizeof(buf), "%s - %ld\n", + pipe_state, d->data.event_time); + + return simple_read_from_buffer(user_buf, count, ppos, buf, retval); +} + +static const struct file_operations skl_dbg_event_fops = { + .open = simple_open, + .read = skl_dbg_event_read, + .llseek = default_llseek, +}; + +static int skl_init_dbg_event(struct skl_debug *d) +{ + if (!debugfs_create_file("dbg_event", 0644, d->fs, d, + &skl_dbg_event_fops)) { + dev_err(d->dev, "dbg_event debugfs file creation failed\n"); + return -EIO; + } + + return 0; +} + struct skl_debug *skl_debugfs_init(struct skl *skl) { struct skl_debug *d; + int ret; d = devm_kzalloc(&skl->pci->dev, sizeof(*d), GFP_KERNEL); if (!d) @@ -865,6 +938,13 @@ struct skl_debug *skl_debugfs_init(struct skl *skl) skl_init_adsp(d); skl_init_mod_set_get(d); + ret = skl_init_dbg_event(d); + if (ret < 0) { + dev_err(&skl->pci->dev, + "dbg_event debugfs init failed, ret = %d\n", ret); + goto err; + } + return d; err: diff --git a/sound/soc/intel/skylake/skl-messages.c b/sound/soc/intel/skylake/skl-messages.c index 2c0cf7a96128..31384afe42b3 100644 --- a/sound/soc/intel/skylake/skl-messages.c +++ b/sound/soc/intel/skylake/skl-messages.c @@ -2265,6 +2265,7 @@ int skl_create_pipeline(struct skl_sst *ctx, struct skl_pipe *pipe) } pipe->state = SKL_PIPE_CREATED; + skl_dbg_event(ctx, pipe->state); return 0; } @@ -2303,6 +2304,7 @@ int skl_delete_pipe(struct skl_sst *ctx, struct skl_pipe *pipe) } pipe->state = SKL_PIPE_INVALID; + skl_dbg_event(ctx, pipe->state); return ret; } diff --git a/sound/soc/intel/skylake/skl.h b/sound/soc/intel/skylake/skl.h index 10a414d77cb2..66f34ceae74e 100644 --- a/sound/soc/intel/skylake/skl.h +++ b/sound/soc/intel/skylake/skl.h @@ -170,6 +170,7 @@ void skl_debug_init_module(struct skl_debug *d, struct nhlt_specific_cfg *skl_nhlt_get_debugfs_blob(struct skl_debug *d, u8 link_type, u32 instance, u8 stream); +void skl_dbg_event(struct skl_sst *ctx, int type); #else static inline struct skl_debug *skl_debugfs_init(struct skl *skl) { @@ -185,6 +186,9 @@ static inline struct nhlt_specific_cfg { return NULL; } +void skl_dbg_event(struct skl_sst *ctx, int type) +{ +} #endif #endif /* __SOUND_SOC_SKL_H */ From a5c82eea4beca345f1f652843bcca155e27fb81e Mon Sep 17 00:00:00 2001 From: Leoni Prodduvaka Date: Thu, 18 May 2017 21:56:06 +0530 Subject: [PATCH 0760/1103] ASoC: Intel: Extract the "nhlt-version" from DSDT table This patch extracts the "nhlt-version" from the DSDT table present at /sys/firmware/acpi/tables/DSDT. Change-Id: Icf20d440ff8a2e9e5f1ae1aacd1f1e0991235672 Signed-off-by: Leoni Prodduvaka Reviewed-on: Reviewed-by: Singh, Guneshwor O Reviewed-by: audio_build Reviewed-by: Kale, Sanyog R Reviewed-by: Koul, Vinod Reviewed-by: Diwakar, Praveen Reviewed-by: R, Dharageswari Tested-by: Sm, Bhadur A --- sound/soc/intel/skylake/skl-nhlt.c | 15 +++++++++++++++ sound/soc/intel/skylake/skl-nhlt.h | 6 ++++++ sound/soc/intel/skylake/skl.h | 2 ++ 3 files changed, 23 insertions(+) diff --git a/sound/soc/intel/skylake/skl-nhlt.c b/sound/soc/intel/skylake/skl-nhlt.c index 01a050cf8775..b2873dd8cdf3 100644 --- a/sound/soc/intel/skylake/skl-nhlt.c +++ b/sound/soc/intel/skylake/skl-nhlt.c @@ -28,6 +28,21 @@ static guid_t osc_guid = GUID_INIT(0xA69F886E, 0x6CEB, 0x4594, 0xA4, 0x1F, 0x7B, 0x5D, 0xCE, 0x24, 0xC5, 0x53); +int skl_get_nhlt_version(struct device *dev) +{ + const char *version; + int ret; + + ret = device_property_read_string(dev, "nhlt-version", &version); + if (!ret) { + if (!strncmp(version, "1.8-0", strlen("1.8-0"))) + return VERSION_1; + else + return VERSION_INVALID; + } + /* if reading fails, assume we are on older platforms */ + return VERSION_0; +} struct nhlt_acpi_table *skl_nhlt_init(struct device *dev) { diff --git a/sound/soc/intel/skylake/skl-nhlt.h b/sound/soc/intel/skylake/skl-nhlt.h index fc17da503b4d..62550a75a9dc 100644 --- a/sound/soc/intel/skylake/skl-nhlt.h +++ b/sound/soc/intel/skylake/skl-nhlt.h @@ -59,6 +59,12 @@ enum nhlt_device_type { NHLT_DEVICE_INVALID }; +enum nhlt_version_type { + VERSION_INVALID = -1, + VERSION_0, + VERSION_1, +}; + struct nhlt_specific_cfg { u32 size; u8 caps[0]; diff --git a/sound/soc/intel/skylake/skl.h b/sound/soc/intel/skylake/skl.h index 66f34ceae74e..01d54513b029 100644 --- a/sound/soc/intel/skylake/skl.h +++ b/sound/soc/intel/skylake/skl.h @@ -82,6 +82,7 @@ struct skl { struct snd_soc_component *component; struct snd_soc_dai_driver *dais; + unsigned int nhlt_version; struct nhlt_acpi_table *nhlt; /* nhlt ptr */ struct skl_sst *skl_sst; /* sst skl ctx */ @@ -137,6 +138,7 @@ struct skl_dsp_ops { int skl_platform_unregister(struct device *dev); int skl_platform_register(struct device *dev); +int skl_get_nhlt_version(struct device *dev); struct nhlt_acpi_table *skl_nhlt_init(struct device *dev); void skl_nhlt_free(struct nhlt_acpi_table *addr); struct nhlt_specific_cfg *skl_get_ep_blob(struct skl *skl, u32 instance, From c1caaac50b7bf8ea38d75a1777b062b064baf696 Mon Sep 17 00:00:00 2001 From: Puneeth Prabhu Date: Wed, 17 May 2017 12:05:30 +0530 Subject: [PATCH 0761/1103] ASoC: Intel: Skylake: Increase the max number of entries for resources, path configs and formats To support multi-format playback/capture, 0.7 xml has more number of entries for module resources, path configs and formats. This patch increases the max limit for module resources to 32, path config entries to 32 and module formats to 64. Although this is not a permanent solution. Right solution is to dynamically calculate the entries instead of hard coding. Change-Id: If052e6b95a69b9ed47f08ebe284383c4d2e8e81f Signed-off-by: Puneeth Prabhu Reviewed-on: Reviewed-by: audio_build Reviewed-by: Babu, Ramesh Reviewed-by: Kale, Sanyog R Reviewed-by: Singh, Guneshwor O Reviewed-by: R, Dharageswari Reviewed-by: Nc, Shreyas Reviewed-by: Diwakar, Praveen Tested-by: Sm, Bhadur A --- sound/soc/intel/skylake/skl-topology.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sound/soc/intel/skylake/skl-topology.h b/sound/soc/intel/skylake/skl-topology.h index 676a769374ae..adf35fa0d75c 100644 --- a/sound/soc/intel/skylake/skl-topology.h +++ b/sound/soc/intel/skylake/skl-topology.h @@ -47,9 +47,9 @@ #define SKL_OUTPUT_PIN 0 #define SKL_INPUT_PIN 1 -#define SKL_MAX_PATH_CONFIGS 8 +#define SKL_MAX_PATH_CONFIGS 32 #define SKL_MAX_MODULES_IN_PIPE 8 -#define SKL_MAX_MODULE_FORMATS 32 +#define SKL_MAX_MODULE_FORMATS 64 #define SKL_MAX_MODULE_RESOURCES 32 enum skl_channel_index { From 7adba4dfdab73aed73ee617e030aefed06b5cd1d Mon Sep 17 00:00:00 2001 From: "Diwakar, Praveen" Date: Tue, 25 Apr 2017 06:24:38 +0530 Subject: [PATCH 0762/1103] ASoC: Intel: Skylake: Add single module support in a given pipeline Single pipeline module will be modelled as PGA leaf. Decision to model it as PGA instead of mixer is taken because there may be pipeline with single module which has TLV controls associated with it. New handler for single module PGA event has been assigned. DAPM graph connectivity of single module will come from Pathconnector. Change-Id: Ie34420fb970553df5f370a66cd7bf817d6cbc0d2 Signed-off-by: Diwakar, Praveen Reviewed-on: Reviewed-by: audio_build Reviewed-by: Prodduvaka, Leoni Reviewed-by: S, Pavan K Reviewed-by: R, Dharageswari Reviewed-by: Singh, Guneshwor O Reviewed-by: Kale, Sanyog R Reviewed-by: Kp, Jeeja Reviewed-by: Prusty, Subhransu S Tested-by: Sm, Bhadur A --- sound/soc/intel/skylake/skl-topology.c | 136 +++++++++++++++++++++++++ sound/soc/intel/skylake/skl-topology.h | 4 + 2 files changed, 140 insertions(+) diff --git a/sound/soc/intel/skylake/skl-topology.c b/sound/soc/intel/skylake/skl-topology.c index 2dfb454f7007..ed5c6d585e33 100644 --- a/sound/soc/intel/skylake/skl-topology.c +++ b/sound/soc/intel/skylake/skl-topology.c @@ -60,6 +60,9 @@ static const int mic_quatro_list[][SKL_CH_QUATRO] = { #define CHECK_HW_PARAMS(ch, freq, bps, prm_ch, prm_freq, prm_bps) \ ((ch == prm_ch) && (bps == prm_bps) && (freq == prm_freq)) +static void skl_init_single_module_pipe(struct snd_soc_dapm_widget *w, + struct skl *skl); + void skl_tplg_d0i3_get(struct skl *skl, enum d0i3_capability caps) { struct skl_d0i3_data *d0i3 = &skl->skl_sst->d0i3; @@ -885,6 +888,17 @@ static int skl_tplg_mixer_dapm_pre_pmu_event(struct snd_soc_dapm_widget *w, struct skl_sst *ctx = skl->skl_sst; struct skl_module_deferred_bind *modules; + if (mconfig->pipe->state >= SKL_PIPE_CREATED) + return 0; + + /* + * This will check for single module in source pipeline. If single + * module pipeline exists then its going to create source pipeline + * first. This will handle/satisfy source-to-sink pipeline creation + * scenario for single module in any stream + */ + skl_init_single_module_pipe(w, skl); + ret = skl_tplg_get_pipe_config(skl, mconfig); if (ret < 0) return ret; @@ -947,6 +961,54 @@ static int skl_tplg_mixer_dapm_pre_pmu_event(struct snd_soc_dapm_widget *w, return 0; } +/* + * This function returns pipe order in given stream + */ +static int skl_get_pipe_order(struct skl_module_cfg *mcfg) +{ + struct skl_pipe *pipe = mcfg->pipe; + + switch (pipe->conn_type) { + case SKL_PIPE_CONN_TYPE_FE: + if (pipe->direction == SNDRV_PCM_STREAM_CAPTURE) + return SKL_LAST_PIPE; + else if (pipe->direction == SNDRV_PCM_STREAM_PLAYBACK) + return SKL_FIRST_PIPE; + break; + case SKL_PIPE_CONN_TYPE_BE: + if (pipe->direction == SNDRV_PCM_STREAM_PLAYBACK) + return SKL_LAST_PIPE; + else if (pipe->direction == SNDRV_PCM_STREAM_CAPTURE) + return SKL_FIRST_PIPE; + break; + } + return SKL_INTERMEDIATE_PIPE; +} + +/* + * This function checks for single module source pipeline. If found any then + * it will initialize source pipeline and its module + */ +static void skl_init_single_module_pipe(struct snd_soc_dapm_widget *w, + struct skl *skl) +{ + struct snd_soc_dapm_path *p; + struct snd_soc_dapm_widget *src_w = NULL; + struct skl_module_cfg *mcfg; + + snd_soc_dapm_widget_for_each_source_path(w, p) { + src_w = p->source; + + if ((src_w->priv != NULL) && is_skl_dsp_widget_type(src_w, skl->skl_sst->dev)) { + mcfg = src_w->priv; + if ((list_is_singular(&mcfg->pipe->w_list)) && + (src_w->power_check(src_w))) + skl_tplg_mixer_dapm_pre_pmu_event(src_w, skl); + } + skl_init_single_module_pipe(src_w, skl); + } +} + static int skl_fill_sink_instance_id(struct skl_sst *ctx, u32 *params, int size, struct skl_module_cfg *mcfg) { @@ -1570,6 +1632,43 @@ static int skl_tplg_pga_event(struct snd_soc_dapm_widget *w, return 0; } + +/* + * In modelling, we assumed that all single module will be PGA leaf. Have + * added new event flag POST_PMU. PRE_PMU is going to handle dynamic connection + * i.e (dynamic FE or BE connection to already running stream). POST_PMU will + * handle the pipeline binding and running from sink to source. POST_PMD + * will handle the cleanup of single module pipe. + */ +static int skl_tplg_pga_single_module_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *k, int event) + +{ + struct snd_soc_dapm_context *dapm = w->dapm; + struct skl *skl = get_skl_ctx(dapm->dev); + struct skl_module_cfg *mcfg = w->priv; + int ret; + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + ret = skl_tplg_mixer_dapm_pre_pmu_event(w, skl); + if ((skl_get_pipe_order(mcfg) == SKL_LAST_PIPE) && (ret == 0)) + ret = skl_tplg_mixer_dapm_post_pmu_event(w, skl); + return ret; + + case SND_SOC_DAPM_POST_PMU: + return skl_tplg_pga_dapm_pre_pmu_event(w, skl); + + case SND_SOC_DAPM_POST_PMD: + ret = skl_tplg_pga_dapm_post_pmd_event(w, skl); + if (ret >= 0) + ret = skl_tplg_mixer_dapm_post_pmd_event(w, skl); + return ret; + } + + return 0; +} + int skl_tplg_dsp_log_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { @@ -3979,6 +4078,40 @@ static int skl_manifest_load(struct snd_soc_component *cmpnt, int index, return 0; } +/* + * This function updates the event flag and fucntiona handler for single module + */ +static void skl_update_single_module_event(struct skl *skl, + struct skl_pipe *pipe) +{ + struct skl_module_cfg *mcfg; + struct skl_pipe_module *w_module; + struct snd_soc_dapm_widget *w; + + list_for_each_entry(w_module, &pipe->w_list, node) { + w = w_module->w; + mcfg = w->priv; + + if (list_is_singular(&pipe->w_list)) { + + /* + * If module pipe order is last then we dont need + * POST_PMU, as POST_PMU bind/run sink to source. + * For last pipe order there is no sink pipelne. + */ + if (skl_get_pipe_order(mcfg) == SKL_LAST_PIPE) + w->event_flags = SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMD; + else + w->event_flags = SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD; + + w->event = skl_tplg_pga_single_module_event; + } + } +} + static struct snd_soc_tplg_ops skl_tplg_ops = { .widget_load = skl_tplg_widget_load, .control_load = skl_tplg_control_load, @@ -4093,5 +4226,8 @@ int skl_tplg_init(struct snd_soc_component *component, struct hdac_bus *bus) list_for_each_entry(ppl, &skl->ppl_list, node) skl_tplg_set_pipe_type(skl, ppl->pipe); + list_for_each_entry(ppl, &skl->ppl_list, node) + skl_update_single_module_event(skl, ppl->pipe); + return 0; } diff --git a/sound/soc/intel/skylake/skl-topology.h b/sound/soc/intel/skylake/skl-topology.h index adf35fa0d75c..0c6b5c6b9c99 100644 --- a/sound/soc/intel/skylake/skl-topology.h +++ b/sound/soc/intel/skylake/skl-topology.h @@ -28,6 +28,10 @@ #include #include "skl.h" +#define SKL_FIRST_PIPE 0 +#define SKL_LAST_PIPE 1 +#define SKL_INTERMEDIATE_PIPE 2 + #define BITS_PER_BYTE 8 #define MAX_TS_GROUPS 8 #define MAX_DMIC_TS_GROUPS 4 From 9c983658e241deb764ef217cfb3c23e14d12d092 Mon Sep 17 00:00:00 2001 From: Shreyas NC Date: Tue, 30 May 2017 19:37:19 +0530 Subject: [PATCH 0763/1103] ASoC: Intel: Skylake: Fix incorrect parsing of pipe tokens To avoid parsing of pipe related tokens multiple times for a case where the pipe has more than one module, a logic was added to parse these tokens only once. But, the existing logic would parse these tokens only if there are more than one module in the pipe. So, for a pipe with single module, the logic is insufficient. So, fix it by updating the flag accordingly. Change-Id: Ie183f14eaf98f21cf87691e0e681b77342706b37 Signed-off-by: Shreyas NC Signed-off-by: Diwakar, Praveen Reviewed-on: Reviewed-by: audio_build Reviewed-by: Prodduvaka, Leoni Reviewed-by: S, Pavan K Reviewed-by: R, Dharageswari Reviewed-by: Singh, Guneshwor O Reviewed-by: Kale, Sanyog R Reviewed-by: Kp, Jeeja Tested-by: Sm, Bhadur A --- sound/soc/intel/skylake/skl-topology.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/sound/soc/intel/skylake/skl-topology.c b/sound/soc/intel/skylake/skl-topology.c index ed5c6d585e33..6e7a192cc68c 100644 --- a/sound/soc/intel/skylake/skl-topology.c +++ b/sound/soc/intel/skylake/skl-topology.c @@ -2935,6 +2935,7 @@ static int skl_tplg_get_token(struct device *dev, } return is_pipe_exists; } + is_pipe_exists = 0; break; @@ -2948,7 +2949,7 @@ static int skl_tplg_get_token(struct device *dev, case SKL_TKN_U32_PMODE: case SKL_TKN_U32_PIPE_DIRECTION: case SKL_TKN_U32_NUM_CONFIGS: - if (is_pipe_exists) { + if (!is_pipe_exists) { ret = skl_tplg_fill_pipe_tkn(dev, mconfig->pipe, tkn_elem->token, tkn_elem->value); if (ret < 0) From 3c0b9ef903083d2d1922d7eea4a54c8b93a6969f Mon Sep 17 00:00:00 2001 From: "Diwakar, Praveen" Date: Tue, 6 Jun 2017 21:04:21 +0530 Subject: [PATCH 0764/1103] ASoC: Intel: Skylake: Create SSP BE dais dynamically This patch creates BE SSP dai dynamically, by getting SSP link information from NHLT. Change-Id: I2b6e45125a3fbd1e7f155efe86b5fb1a983c0f41 Signed-off-by: Diwakar, Praveen Reviewed-on: Reviewed-by: Kale, Sanyog R Tested-by: Sm, Bhadur A --- sound/soc/intel/skylake/skl-nhlt.c | 36 ++++++ sound/soc/intel/skylake/skl-pcm.c | 195 ++++++++++------------------- sound/soc/intel/skylake/skl.h | 7 ++ 3 files changed, 111 insertions(+), 127 deletions(-) diff --git a/sound/soc/intel/skylake/skl-nhlt.c b/sound/soc/intel/skylake/skl-nhlt.c index b2873dd8cdf3..cf3d38136289 100644 --- a/sound/soc/intel/skylake/skl-nhlt.c +++ b/sound/soc/intel/skylake/skl-nhlt.c @@ -464,3 +464,39 @@ void skl_get_clks(struct skl *skl, struct skl_ssp_clk *ssp_clks) epnt = (struct nhlt_endpoint *)((u8 *)epnt + epnt->length); } } + +static bool is_vbus_id_exist(struct skl *skl, int vbus_id) +{ + bool ret = false; + int i; + + for (i = 0; i < skl->nhlt->endpoint_count; i++) { + if (vbus_id == skl->grp_cnt.vbus_id[i]) + return true; + } + return ret; +} + +/* + * This function gets endpoint count and vbus_id for the specific link type + * passed as parameter. + */ +void skl_nhlt_get_ep_cnt(struct skl *skl, int link_type) +{ + struct nhlt_endpoint *epnt = (struct nhlt_endpoint *) skl->nhlt->desc; + int i; + + skl->grp_cnt.cnt = 0; + memset(skl->grp_cnt.vbus_id, 0xff, + (sizeof(int) * skl->nhlt->endpoint_count)); + + for (i = 0; i < skl->nhlt->endpoint_count; i++) { + + if (epnt->linktype == link_type) { + if (!is_vbus_id_exist(skl, epnt->virtual_bus_id)) + skl->grp_cnt.vbus_id[skl->grp_cnt.cnt++] = + epnt->virtual_bus_id; + } + epnt = (struct nhlt_endpoint *)((u8 *)epnt + epnt->length); + } +} diff --git a/sound/soc/intel/skylake/skl-pcm.c b/sound/soc/intel/skylake/skl-pcm.c index c1ddf5ce1852..96c912bafc87 100644 --- a/sound/soc/intel/skylake/skl-pcm.c +++ b/sound/soc/intel/skylake/skl-pcm.c @@ -1117,126 +1117,6 @@ static struct snd_soc_dai_driver skl_fe_dai[] = { /* BE cpu dais and compress dais*/ static struct snd_soc_dai_driver skl_platform_dai[] = { -{ - .name = "SSP0 Pin", - .ops = &skl_be_ssp_dai_ops, - .playback = { - .stream_name = "ssp0 Tx", - .channels_min = HDA_MONO, - .channels_max = HDA_8_CH, - .rates = SNDRV_PCM_RATE_8000_192000 | SNDRV_PCM_RATE_KNOT, - .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE | - SNDRV_PCM_FMTBIT_S32_LE, - }, - .capture = { - .stream_name = "ssp0 Rx", - .channels_min = HDA_MONO, - .channels_max = HDA_8_CH, - .rates = SNDRV_PCM_RATE_8000_192000 | SNDRV_PCM_RATE_KNOT, - .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE | - SNDRV_PCM_FMTBIT_S32_LE, - }, -}, -{ - .name = "SSP1 Pin", - .ops = &skl_be_ssp_dai_ops, - .playback = { - .stream_name = "ssp1 Tx", - .channels_min = HDA_MONO, - .channels_max = HDA_8_CH, - .rates = SNDRV_PCM_RATE_8000_192000 | SNDRV_PCM_RATE_KNOT, - .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE | - SNDRV_PCM_FMTBIT_S32_LE, - }, - .capture = { - .stream_name = "ssp1 Rx", - .channels_min = HDA_MONO, - .channels_max = HDA_8_CH, - .rates = SNDRV_PCM_RATE_8000_192000 | SNDRV_PCM_RATE_KNOT, - .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE | - SNDRV_PCM_FMTBIT_S32_LE, - }, -}, -{ - .name = "SSP2 Pin", - .ops = &skl_be_ssp_dai_ops, - .playback = { - .stream_name = "ssp2 Tx", - .channels_min = HDA_MONO, - .channels_max = HDA_8_CH, - .rates = SNDRV_PCM_RATE_8000_192000 | SNDRV_PCM_RATE_KNOT, - .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE | - SNDRV_PCM_FMTBIT_S32_LE, - }, - .capture = { - .stream_name = "ssp2 Rx", - .channels_min = HDA_MONO, - .channels_max = HDA_8_CH, - .rates = SNDRV_PCM_RATE_8000_192000 | SNDRV_PCM_RATE_KNOT, - .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE | - SNDRV_PCM_FMTBIT_S32_LE, - }, -}, -{ - .name = "SSP3 Pin", - .ops = &skl_be_ssp_dai_ops, - .playback = { - .stream_name = "ssp3 Tx", - .channels_min = HDA_MONO, - .channels_max = HDA_8_CH, - .rates = SNDRV_PCM_RATE_8000_192000 | SNDRV_PCM_RATE_KNOT, - .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE | - SNDRV_PCM_FMTBIT_S32_LE, - }, - .capture = { - .stream_name = "ssp3 Rx", - .channels_min = HDA_MONO, - .channels_max = HDA_8_CH, - .rates = SNDRV_PCM_RATE_8000_192000 | SNDRV_PCM_RATE_KNOT, - .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE | - SNDRV_PCM_FMTBIT_S32_LE, - }, -}, -{ - .name = "SSP4 Pin", - .ops = &skl_be_ssp_dai_ops, - .playback = { - .stream_name = "ssp4 Tx", - .channels_min = HDA_MONO, - .channels_max = HDA_8_CH, - .rates = SNDRV_PCM_RATE_8000_192000 | SNDRV_PCM_RATE_KNOT, - .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE | - SNDRV_PCM_FMTBIT_S32_LE, - }, - .capture = { - .stream_name = "ssp4 Rx", - .channels_min = HDA_MONO, - .channels_max = HDA_8_CH, - .rates = SNDRV_PCM_RATE_8000_192000 | SNDRV_PCM_RATE_KNOT, - .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE | - SNDRV_PCM_FMTBIT_S32_LE, - }, -}, -{ - .name = "SSP5 Pin", - .ops = &skl_be_ssp_dai_ops, - .playback = { - .stream_name = "ssp5 Tx", - .channels_min = HDA_MONO, - .channels_max = HDA_8_CH, - .rates = SNDRV_PCM_RATE_8000_192000 | SNDRV_PCM_RATE_KNOT, - .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE | - SNDRV_PCM_FMTBIT_S32_LE, - }, - .capture = { - .stream_name = "ssp5 Rx", - .channels_min = HDA_MONO, - .channels_max = HDA_8_CH, - .rates = SNDRV_PCM_RATE_8000_192000 | SNDRV_PCM_RATE_KNOT, - .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE | - SNDRV_PCM_FMTBIT_S32_LE, - }, -}, { .name = "iDisp1 Pin", .ops = &skl_link_dai_ops, @@ -1994,40 +1874,101 @@ static const struct snd_soc_component_driver skl_component = { .num_controls = ARRAY_SIZE(skl_controls), }; +static struct snd_soc_dai_driver ssp_dai_info = { + .ops = &skl_be_ssp_dai_ops, + .playback = { + .channels_min = HDA_MONO, + .channels_max = HDA_8_CH, + .rates = SNDRV_PCM_RATE_8000_192000 | SNDRV_PCM_RATE_KNOT, + .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S32_LE, + }, + .capture = { + .channels_min = HDA_MONO, + .channels_max = HDA_8_CH, + .rates = SNDRV_PCM_RATE_8000_192000 | SNDRV_PCM_RATE_KNOT, + .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S32_LE, + }, +}; + int skl_platform_register(struct device *dev) { int ret; - struct snd_soc_dai_driver *dais; - int num_dais = ARRAY_SIZE(skl_platform_dai); struct hdac_bus *bus = dev_get_drvdata(dev); struct skl *skl = bus_to_skl(bus); + struct snd_soc_dai_driver *dais; + int num_dais = ARRAY_SIZE(skl_platform_dai); + int total_dais; + int i, index; INIT_LIST_HEAD(&skl->ppl_list); INIT_LIST_HEAD(&skl->bind_list); skl->dais = kmemdup(skl_platform_dai, sizeof(skl_platform_dai), GFP_KERNEL); + skl->grp_cnt.vbus_id = devm_kcalloc(dev, skl->nhlt->endpoint_count, + sizeof(int), GFP_KERNEL); + if (!skl->grp_cnt.vbus_id) + return -ENOMEM; + + skl_nhlt_get_ep_cnt(skl, NHLT_LINK_SSP); + + total_dais = num_dais + skl->grp_cnt.cnt; + + skl->dais = devm_kcalloc(dev, total_dais, sizeof(*dais), GFP_KERNEL); + if (!skl->dais) { ret = -ENOMEM; goto err; } + memcpy(skl->dais, skl_platform_dai, sizeof(skl_platform_dai)); + + for (i = 0; i < skl->grp_cnt.cnt; i++) { + index = num_dais + i; + + memcpy(&skl->dais[index], &ssp_dai_info, sizeof(ssp_dai_info)); + + skl->dais[index].name = kasprintf(GFP_KERNEL, "SSP%d Pin", + skl->grp_cnt.vbus_id[i]); + if (!skl->dais[index].name) + return -ENOMEM; + + skl->dais[index].playback.stream_name = kasprintf(GFP_KERNEL, + "ssp%d Tx", skl->grp_cnt.vbus_id[i]); + if (!skl->dais[index].playback.stream_name) { + kfree(skl->dais[index].name); + return -ENOMEM; + } + + skl->dais[index].capture.stream_name = kasprintf(GFP_KERNEL, + "ssp%d Rx", skl->grp_cnt.vbus_id[i]); + if (!skl->dais[index].capture.stream_name) { + kfree(skl->dais[index].name); + kfree(skl->dais[index].playback.stream_name); + return -ENOMEM; + } + } + if (!skl->use_tplg_pcm) { - dais = krealloc(skl->dais, sizeof(skl_fe_dai) + - sizeof(skl_platform_dai), GFP_KERNEL); + total_dais += ARRAY_SIZE(skl_fe_dai); + dais = krealloc(skl->dais, (total_dais * sizeof(*dais)), + GFP_KERNEL); if (!dais) { ret = -ENOMEM; goto err; } skl->dais = dais; - memcpy(&skl->dais[ARRAY_SIZE(skl_platform_dai)], skl_fe_dai, + memcpy(&skl->dais[num_dais + skl->grp_cnt.cnt], skl_fe_dai, sizeof(skl_fe_dai)); - num_dais += ARRAY_SIZE(skl_fe_dai); + + num_dais = total_dais; } ret = devm_snd_soc_register_component(dev, &skl_component, - skl->dais, num_dais); + skl->dais, total_dais); if (ret) dev_err(dev, "soc component registration failed %d\n", ret); err: diff --git a/sound/soc/intel/skylake/skl.h b/sound/soc/intel/skylake/skl.h index 01d54513b029..2883d86d56fe 100644 --- a/sound/soc/intel/skylake/skl.h +++ b/sound/soc/intel/skylake/skl.h @@ -71,6 +71,11 @@ struct skl_fw_config { struct skl_astate_config *astate_cfg; }; +struct ep_group_cnt { + int cnt; + int *vbus_id; +}; + struct skl { struct hdac_bus hbus; struct pci_dev *pci; @@ -107,6 +112,7 @@ struct skl { struct snd_soc_acpi_mach *mach; bool nhlt_override; bool mod_set_get_status; + struct ep_group_cnt grp_cnt; }; #define skl_to_bus(s) (&(s)->hbus) @@ -139,6 +145,7 @@ int skl_platform_unregister(struct device *dev); int skl_platform_register(struct device *dev); int skl_get_nhlt_version(struct device *dev); +void skl_nhlt_get_ep_cnt(struct skl *skl, int link_type); struct nhlt_acpi_table *skl_nhlt_init(struct device *dev); void skl_nhlt_free(struct nhlt_acpi_table *addr); struct nhlt_specific_cfg *skl_get_ep_blob(struct skl *skl, u32 instance, From 4ccf659be7f9a4c056d5a0c1550fbb51e94625d3 Mon Sep 17 00:00:00 2001 From: "Paul, Subhankar" Date: Wed, 31 Aug 2016 03:24:12 +0530 Subject: [PATCH 0765/1103] ASoC: Intel: Skylake: Adding support for set system time to aDSP In order to calculate the logging timestamps, firmware need the IA timestamps reference. A new IPC "SYSTEM TIME" has been added to pass UTC time to firmware. Also enable log skl_log_state_msg structure for enable log IPC has been modified according to latest firmware interface specification document. Change-Id: Ibcfb185c01c70b9b8e5a716849b9c935327594d3 Signed-off-by: Paul, Subhankar Signed-off-by: Sanyog Kale Reviewed-on: Reviewed-by: Jayanti, Satya Charitardha Tested-by: Jayanti, Satya Charitardha Reviewed-on: Reviewed-by: audio_build Reviewed-by: R, Dharageswari Reviewed-by: Diwakar, Praveen Tested-by: Sm, Bhadur A --- sound/soc/intel/skylake/skl-messages.c | 32 +++++++++++++++++ sound/soc/intel/skylake/skl-pcm.c | 6 ++++ sound/soc/intel/skylake/skl-sst-ipc.h | 49 +++++++++++++++++++++++++- 3 files changed, 86 insertions(+), 1 deletion(-) diff --git a/sound/soc/intel/skylake/skl-messages.c b/sound/soc/intel/skylake/skl-messages.c index 31384afe42b3..dc648b7db934 100644 --- a/sound/soc/intel/skylake/skl-messages.c +++ b/sound/soc/intel/skylake/skl-messages.c @@ -71,6 +71,8 @@ void skl_dsp_set_astate_cfg(struct skl_sst *ctx, u32 cnt, void *data) } #define ENABLE_LOGS 6 +#define FW_LOGGING_AGING_TIMER_PERIOD 100 +#define FW_LOG_FIFO_FULL_TIMER_PERIOD 100 /* set firmware logging state via IPC */ int skl_dsp_enable_logging(struct sst_generic_ipc *ipc, int core, int enable) @@ -79,6 +81,9 @@ int skl_dsp_enable_logging(struct sst_generic_ipc *ipc, int core, int enable) struct skl_ipc_large_config_msg msg = {0}; int ret = 0; + log_msg.aging_timer_period = FW_LOGGING_AGING_TIMER_PERIOD; + log_msg.fifo_full_timer_period = FW_LOG_FIFO_FULL_TIMER_PERIOD; + log_msg.core_mask = (1 << core); log_msg.logs_core[core].enable = enable; log_msg.logs_core[core].priority = ipc->dsp->trace_wind.log_priority; @@ -91,6 +96,33 @@ int skl_dsp_enable_logging(struct sst_generic_ipc *ipc, int core, int enable) return ret; } +#define SYSTEM_TIME 20 + +/* set system time to DSP via IPC */ +int skl_dsp_set_system_time(struct skl_sst *skl_sst) +{ + struct sst_generic_ipc *ipc = &skl_sst->ipc; + struct SystemTime sys_time_msg; + struct skl_ipc_large_config_msg msg = {0}; + struct timeval tv; + u64 sys_time; + u64 mask = 0x00000000FFFFFFFF; + int ret; + + do_gettimeofday(&tv); + + /* DSP firmware expects UTC time in micro seconds */ + sys_time = tv.tv_sec*1000*1000 + tv.tv_usec; + sys_time_msg.val_l = sys_time & mask; + sys_time_msg.val_u = (sys_time & (~mask)) >> 32; + + msg.large_param_id = SYSTEM_TIME; + msg.param_data_size = sizeof(sys_time_msg); + + ret = skl_ipc_set_large_config(ipc, &msg, (u32 *)&sys_time_msg); + return ret; +} + #define NOTIFICATION_PARAM_ID 3 #define NOTIFICATION_MASK 0xf diff --git a/sound/soc/intel/skylake/skl-pcm.c b/sound/soc/intel/skylake/skl-pcm.c index 96c912bafc87..ac9f67c8b7e4 100644 --- a/sound/soc/intel/skylake/skl-pcm.c +++ b/sound/soc/intel/skylake/skl-pcm.c @@ -847,6 +847,12 @@ static int skl_trace_compr_set_params(struct snd_compr_stream *stream, return ret; } + ret = skl_dsp_set_system_time(skl_sst); + if (ret < 0) { + dev_err(sst->dev, "Set system time to dsp firmware failed: %d\n", ret); + return ret; + } + skl_dsp_get_log_buff(sst, core); sst->trace_wind.flags |= BIT(core); ret = skl_dsp_enable_logging(ipc, core, 1); diff --git a/sound/soc/intel/skylake/skl-sst-ipc.h b/sound/soc/intel/skylake/skl-sst-ipc.h index 43d3eedad780..bc9b27884120 100644 --- a/sound/soc/intel/skylake/skl-sst-ipc.h +++ b/sound/soc/intel/skylake/skl-sst-ipc.h @@ -27,6 +27,7 @@ struct sst_generic_ipc; #define NO_OF_INJECTOR 6 #define NO_OF_EXTRACTOR 8 +#define FW_REG_SZ 1024 enum skl_ipc_pipeline_state { PPL_INVALID_STATE = 0, @@ -332,11 +333,56 @@ struct skl_log_state { }; struct skl_log_state_msg { + uint32_t aging_timer_period; + uint32_t fifo_full_timer_period; + u32 core_mask; struct skl_log_state logs_core[2]; }; -#define SKL_IPC_BOOT_MSECS 3000 +struct SystemTime { + uint32_t val_l; + uint32_t val_u; +}; + +struct fw_version { + u16 major; + u16 minor; + u16 hotfix; + u16 build; +} __packed; + +struct sw_version { + u16 major; + u16 minor; + u16 hotfix; + u16 build; +} __packed; + +struct skl_dsp_core_dump { + u16 type0; + u16 length0; + u32 crash_dump_ver; + u16 bus_dev_id; + u16 cavs_hw_version; + struct fw_version fw_ver; + struct sw_version sw_ver; + u16 type2; + u16 length2; + u32 fwreg[FW_REG_SZ]; +} __packed; + +struct skl_module_notify { + u32 unique_id; + u32 event_id; + u32 event_data_size; + u32 event_data[0]; +} __packed; + +/* Timeout values in milliseconds for response from FW */ +#define SKL_IPC_BOOT_MSECS 3000 +#define SKL_IPC_LOAD_LIB_TIMEOUT 3000 +#define SKL_IPC_DEFAULT_TIMEOUT 300 #define SKL_IPC_D3_MASK 0 #define SKL_IPC_D0_MASK 3 @@ -390,6 +436,7 @@ int skl_ipc_set_d0ix(struct sst_generic_ipc *ipc, int skl_ipc_check_D0i0(struct sst_dsp *dsp, bool state); int skl_dsp_enable_logging(struct sst_generic_ipc *ipc, int core, int enable); +int skl_dsp_set_system_time(struct skl_sst *skl_sst); void skl_ipc_int_enable(struct sst_dsp *dsp); void skl_ipc_op_int_enable(struct sst_dsp *ctx); From c4f3c2406c25462515e6ff0b4d637db8083dac34 Mon Sep 17 00:00:00 2001 From: Mousumi Jana Date: Sat, 3 Sep 2016 02:56:03 +0530 Subject: [PATCH 0766/1103] ASoC: Intel: Skylake: Update FW Trace logs feature to new interface FW trace logs feature has been changed according to a new implementation from the firmware. 1. Core Mask has been updated to support 4 cores 2. Read and Write pointers of the trace buffer are moved to the base of the Trace window of the FW This patch contains the related driver changes Change-Id: Ie2336b5df91bfe291bb871c00726779ac77c0472 Signed-off-by: Mousumi Jana Signed-off-by: Sanyog Kale Reviewed-on: Reviewed-by: Jayanti, Satya Charitardha Tested-by: Jayanti, Satya Charitardha Reviewed-on: Reviewed-by: audio_build Reviewed-by: R, Dharageswari Reviewed-by: Diwakar, Praveen Tested-by: Sm, Bhadur A --- sound/soc/intel/skylake/skl-sst-ipc.c | 27 +++++++++++++++++++-------- 1 file changed, 19 insertions(+), 8 deletions(-) diff --git a/sound/soc/intel/skylake/skl-sst-ipc.c b/sound/soc/intel/skylake/skl-sst-ipc.c index b3591c8de471..1c53ba3c6172 100644 --- a/sound/soc/intel/skylake/skl-sst-ipc.c +++ b/sound/soc/intel/skylake/skl-sst-ipc.c @@ -53,8 +53,8 @@ #define IPC_MSG_DIR(x) (((x) & IPC_MSG_DIR_MASK) \ << IPC_MSG_DIR_SHIFT) /* Global Notification Message */ -#define IPC_GLB_NOTIFY_CORE_SHIFT 15 -#define IPC_GLB_NOTIFY_CORE_MASK 0x1 +#define IPC_GLB_NOTIFY_CORE_SHIFT 12 +#define IPC_GLB_NOTIFY_CORE_MASK 0xF #define IPC_GLB_NOTIFY_CORE_ID(x) (((x) >> IPC_GLB_NOTIFY_CORE_SHIFT) \ & IPC_GLB_NOTIFY_CORE_MASK) #define IPC_GLB_NOTIFY_TYPE_SHIFT 16 @@ -356,8 +356,9 @@ static void skl_process_log_buffer(struct sst_dsp *sst, struct skl_ipc_header header) { int core, size; - u32 *ptr, avail; + u32 *ptr; u8 *base; + u32 write, read; #if defined(CONFIG_SND_SOC_INTEL_CNL_FPGA) core = 0; @@ -383,11 +384,21 @@ skl_process_log_buffer(struct sst_dsp *sst, struct skl_ipc_header header) base = (u8 *)sst->trace_wind.addr; /* move to the source dsp tracing window */ base += (core * size); - ptr = (u32 *)sst->trace_wind.dsp_wps[core]; - avail = *ptr; - if (avail < size/2) - base += size/2; - skl_dsp_write_log(sst, (void __iomem *)base, core, size/2); + ptr = (u32 *) base; + read = ptr[0]; + write = ptr[1]; + if (write > read) { + skl_dsp_write_log(sst, (void __iomem *)(base + 8 + read), + core, (write - read)); + /* read pointer */ + ptr[0] += write - read; + } else { + skl_dsp_write_log(sst, (void __iomem *) (base + 8 + read), + core, size - read); + skl_dsp_write_log(sst, (void __iomem *) (base + 8), + core, write); + ptr[0] = write; + } skl_dsp_put_log_buff(sst, core); } From 1c2f2ffba20455a424c65dc045677ac644f651a4 Mon Sep 17 00:00:00 2001 From: Pardha Saradhi K Date: Mon, 7 Nov 2016 13:38:52 +0530 Subject: [PATCH 0767/1103] ASoC: Intel: Skylake: Fix FW logging data corruption As per the new logging scheme introduced in latest firmware, the log buffer read/write pointers are located at the base of the log buffer for each core. Hence, while reading log data during buffer wrap around, the data count needs to be decremented to skip these pointers. This patch fixes the issue. Change-Id: I2fc52125823cc0e317eb56d217b95ad56589df28 Signed-off-by: Pardha Saradhi K Signed-off-by: Sanyog Kale Reviewed-on: Reviewed-by: Babu, Ramesh Tested-by: Sangaraju, KarthikeyaX Reviewed-on: Reviewed-by: audio_build Reviewed-by: R, Dharageswari Reviewed-by: Diwakar, Praveen Tested-by: Sm, Bhadur A --- sound/soc/intel/skylake/skl-sst-ipc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/intel/skylake/skl-sst-ipc.c b/sound/soc/intel/skylake/skl-sst-ipc.c index 1c53ba3c6172..508382d52e04 100644 --- a/sound/soc/intel/skylake/skl-sst-ipc.c +++ b/sound/soc/intel/skylake/skl-sst-ipc.c @@ -394,7 +394,7 @@ skl_process_log_buffer(struct sst_dsp *sst, struct skl_ipc_header header) ptr[0] += write - read; } else { skl_dsp_write_log(sst, (void __iomem *) (base + 8 + read), - core, size - read); + core, size - 8 - read); skl_dsp_write_log(sst, (void __iomem *) (base + 8), core, write); ptr[0] = write; From 0744c58306fe5f5a827f51db148b91b4687d9427 Mon Sep 17 00:00:00 2001 From: "Schweikhardt, Markus" Date: Tue, 9 May 2017 23:00:20 +0530 Subject: [PATCH 0768/1103] ASoC: Intel: Board: Add BXTP MRB machine driver for NXP TDF8532 This is the machine driver for NXP TDF8532 Change-Id: Ieee7ba1fc2dab6fbe43836b65def88c81360d48f Signed-off-by: Mohit Sinha Signed-off-by: Markus Schweikhardt Reviewed-on: Reviewed-by: Shaik, Kareem M Reviewed-by: B, Jayachandran Reviewed-by: Koul, Vinod Tested-by: Sm, Bhadur A --- sound/soc/intel/boards/Kconfig | 10 ++ sound/soc/intel/boards/Makefile | 2 + sound/soc/intel/boards/bxt_tdf8532.c | 209 +++++++++++++++++++++++++++ 3 files changed, 221 insertions(+) create mode 100644 sound/soc/intel/boards/bxt_tdf8532.c diff --git a/sound/soc/intel/boards/Kconfig b/sound/soc/intel/boards/Kconfig index 69e1081966a8..d8b639c426fa 100644 --- a/sound/soc/intel/boards/Kconfig +++ b/sound/soc/intel/boards/Kconfig @@ -339,6 +339,16 @@ config SND_SOC_INTEL_CNL_RT274_MACH with RT274 I2S audio codec. Say Y or m if you have such a device. This is a recommended option. If unsure select "N". + +config SND_SOC_INTEL_BXT_TDF8532_MACH + tristate "Broxton with TDF8532 I2S mode" + depends on MFD_INTEL_LPSS && I2C && ACPI + select SND_SOC_TDF8532 + help + This adds support for ASoC machine driver for Broxton IVI GP MRB + platforms with TDF8532 I2S audio codec. + Say Y or m if you have such a device. This is a recommended option. + If unsure select "N". endif ## SND_SOC_INTEL_SKYLAKE endif ## SND_SOC_INTEL_MACH diff --git a/sound/soc/intel/boards/Makefile b/sound/soc/intel/boards/Makefile index 72dfb2ccdb61..dcd898c0b4a2 100644 --- a/sound/soc/intel/boards/Makefile +++ b/sound/soc/intel/boards/Makefile @@ -7,6 +7,7 @@ snd-soc-sst-broadwell-objs := broadwell.o snd-soc-sst-bxt-da7219_max98357a-objs := bxt_da7219_max98357a.o snd-soc-sst-bxt-rt298-objs := bxt_rt298.o snd-soc-sst-glk-rt5682_max98357a-objs := glk_rt5682_max98357a.o +snd-soc-sst_bxt_tdf8532-objs := bxt_tdf8532.o snd-soc-sst-bytcr-rt5640-objs := bytcr_rt5640.o snd-soc-sst-bytcr-rt5651-objs := bytcr_rt5651.o snd-soc-sst-cht-bsw-rt5672-objs := cht_bsw_rt5672.o @@ -33,6 +34,7 @@ obj-$(CONFIG_SND_SOC_INTEL_BYT_MAX98090_MACH) += snd-soc-sst-byt-max98090-mach.o obj-$(CONFIG_SND_SOC_INTEL_BXT_DA7219_MAX98357A_MACH) += snd-soc-sst-bxt-da7219_max98357a.o obj-$(CONFIG_SND_SOC_INTEL_BXT_RT298_MACH) += snd-soc-sst-bxt-rt298.o obj-$(CONFIG_SND_SOC_INTEL_GLK_RT5682_MAX98357A_MACH) += snd-soc-sst-glk-rt5682_max98357a.o +obj-$(CONFIG_SND_SOC_INTEL_BXT_TDF8532_MACH) += snd-soc-sst_bxt_tdf8532.o obj-$(CONFIG_SND_SOC_INTEL_BROADWELL_MACH) += snd-soc-sst-broadwell.o obj-$(CONFIG_SND_SOC_INTEL_BDW_RT5677_MACH) += snd-soc-sst-bdw-rt5677-mach.o obj-$(CONFIG_SND_SOC_INTEL_BYTCR_RT5640_MACH) += snd-soc-sst-bytcr-rt5640.o diff --git a/sound/soc/intel/boards/bxt_tdf8532.c b/sound/soc/intel/boards/bxt_tdf8532.c new file mode 100644 index 000000000000..027060b17322 --- /dev/null +++ b/sound/soc/intel/boards/bxt_tdf8532.c @@ -0,0 +1,209 @@ +/* + * Intel Broxton-P I2S Machine Driver for IVI reference platform + * Copyright (c) 2017, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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. + */ + +#include +#include +#include +#include +#include +#include + +static const struct snd_kcontrol_new broxton_tdf8532_controls[] = { + SOC_DAPM_PIN_SWITCH("Speaker"), +}; + +static const struct snd_soc_dapm_widget broxton_tdf8532_widgets[] = { + SND_SOC_DAPM_SPK("Speaker", NULL), + SND_SOC_DAPM_MIC("DiranaCp", NULL), + SND_SOC_DAPM_HP("DiranaPb", NULL), + SND_SOC_DAPM_MIC("HdmiIn", NULL), + SND_SOC_DAPM_MIC("TestPinCp", NULL), + SND_SOC_DAPM_HP("TestPinPb", NULL), + SND_SOC_DAPM_MIC("BtHfpDl", NULL), + SND_SOC_DAPM_HP("BtHfpUl", NULL), + SND_SOC_DAPM_MIC("ModemDl", NULL), + SND_SOC_DAPM_HP("ModemUl", NULL), +}; + +static const struct snd_soc_dapm_route broxton_tdf8532_map[] = { + + /* Speaker BE connections */ + { "Speaker", NULL, "ssp4 Tx"}, + { "ssp4 Tx", NULL, "codec0_out"}, + + { "dirana_in", NULL, "ssp2 Rx"}, + { "ssp2 Rx", NULL, "DiranaCp"}, + + { "dirana_aux_in", NULL, "ssp2 Rx"}, + { "ssp2 Rx", NULL, "DiranaCp"}, + + { "dirana_tuner_in", NULL, "ssp2 Rx"}, + { "ssp2 Rx", NULL, "DiranaCp"}, + + { "DiranaPb", NULL, "ssp2 Tx"}, + { "ssp2 Tx", NULL, "dirana_out"}, + + { "hdmi_ssp1_in", NULL, "ssp1 Rx"}, + { "ssp1 Rx", NULL, "HdmiIn"}, + + { "TestPin_ssp5_in", NULL, "ssp5 Rx"}, + { "ssp5 Rx", NULL, "TestPinCp"}, + + { "TestPinPb", NULL, "ssp5 Tx"}, + { "ssp5 Tx", NULL, "TestPin_ssp5_out"}, + + { "BtHfp_ssp0_in", NULL, "ssp0 Rx"}, + { "ssp0 Rx", NULL, "BtHfpDl"}, + + { "BtHfpUl", NULL, "ssp0 Tx"}, + { "ssp0 Tx", NULL, "BtHfp_ssp0_out"}, + + { "Modem_ssp3_in", NULL, "ssp3 Rx"}, + { "ssp3 Rx", NULL, "ModemDl"}, + + { "ModemUl", NULL, "ssp3 Tx"}, + { "ssp3 Tx", NULL, "Modem_ssp3_out"}, +}; + +/* broxton digital audio interface glue - connects codec <--> CPU */ +static struct snd_soc_dai_link broxton_tdf8532_dais[] = { + /* Back End DAI links */ + { + /* SSP0 - BT */ + .name = "SSP0-Codec", + .id = 0, + .cpu_dai_name = "SSP0 Pin", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .platform_name = "0000:00:0e.0", + .ignore_suspend = 1, + .dpcm_capture = 1, + .dpcm_playback = 1, + .no_pcm = 1, + }, + { + /* SSP1 - HDMI-In */ + .name = "SSP1-Codec", + .id = 1, + .cpu_dai_name = "SSP1 Pin", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .platform_name = "0000:00:0e.0", + .ignore_suspend = 1, + .dpcm_capture = 1, + .no_pcm = 1, + }, + { + /* SSP2 - Dirana */ + .name = "SSP2-Codec", + .id = 2, + .cpu_dai_name = "SSP2 Pin", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .platform_name = "0000:00:0e.0", + .ignore_suspend = 1, + .dpcm_capture = 1, + .dpcm_playback = 1, + .no_pcm = 1, + }, + { + /* SSP3 - Modem */ + .name = "SSP3-Codec", + .id = 3, + .cpu_dai_name = "SSP3 Pin", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .platform_name = "0000:00:0e.0", + .ignore_suspend = 1, + .dpcm_capture = 1, + .dpcm_playback = 1, + .no_pcm = 1, + }, + { + /* SSP4 - Amplifier */ + .name = "SSP4-Codec", + .id = 4, + .cpu_dai_name = "SSP4 Pin", + .codec_name = "i2c-INT34C3:00", + .codec_dai_name = "tdf8532-hifi", + .platform_name = "0000:00:0e.0", + .ignore_suspend = 1, + .dpcm_playback = 1, + .no_pcm = 1, + }, + { + /* SSP5 - TestPin */ + .name = "SSP5-Codec", + .id = 5, + .cpu_dai_name = "SSP5 Pin", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .platform_name = "0000:00:0e.0", + .ignore_suspend = 1, + .dpcm_capture = 1, + .dpcm_playback = 1, + .no_pcm = 1, + }, +}; + +static int bxt_add_dai_link(struct snd_soc_card *card, + struct snd_soc_dai_link *link) +{ + link->platform_name = "0000:00:0e.0"; + link->nonatomic = 1; + return 0; +} + +/* broxton audio machine driver for TDF8532 */ +static struct snd_soc_card broxton_tdf8532 = { + .name = "broxton_tdf8532", + .dai_link = broxton_tdf8532_dais, + .num_links = ARRAY_SIZE(broxton_tdf8532_dais), + .controls = broxton_tdf8532_controls, + .num_controls = ARRAY_SIZE(broxton_tdf8532_controls), + .dapm_widgets = broxton_tdf8532_widgets, + .num_dapm_widgets = ARRAY_SIZE(broxton_tdf8532_widgets), + .dapm_routes = broxton_tdf8532_map, + .num_dapm_routes = ARRAY_SIZE(broxton_tdf8532_map), + .fully_routed = true, + .add_dai_link = bxt_add_dai_link, +}; + +static int broxton_tdf8532_audio_probe(struct platform_device *pdev) +{ + dev_info(&pdev->dev, "%s registering %s\n", __func__, pdev->name); + broxton_tdf8532.dev = &pdev->dev; + return snd_soc_register_card(&broxton_tdf8532); +} + +static int broxton_tdf8532_audio_remove(struct platform_device *pdev) +{ + snd_soc_unregister_card(&broxton_tdf8532); + return 0; +} + +static struct platform_driver broxton_tdf8532_audio = { + .probe = broxton_tdf8532_audio_probe, + .remove = broxton_tdf8532_audio_remove, + .driver = { + .name = "bxt_tdf8532", + }, +}; + +module_platform_driver(broxton_tdf8532_audio) + +/* Module information */ +MODULE_DESCRIPTION("Intel SST Audio for Broxton GP MRB"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:gpmrb_machine"); From f9e9f9e631943fdca2bd8ddf43e14bf16e59effd Mon Sep 17 00:00:00 2001 From: "Wagner, Steffen" Date: Mon, 8 May 2017 21:19:09 +0530 Subject: [PATCH 0769/1103] ASoC: tdf8532: NXP TDF8532 audio class-D amplifier driver This is a basic driver to register the codec, expose the codec DAI and control the power mode of the amplifier. Change-Id: Ie6ab037cd4d6c87e8e139b6d8af6cd4295445bf2 Signed-off-by: Mohit Sinha Signed-off-by: Steffen Wagner Reviewed-on: Reviewed-by: B, Jayachandran Reviewed-by: Shaik, Kareem M Reviewed-by: Koul, Vinod Tested-by: Sm, Bhadur A --- sound/soc/codecs/Kconfig | 5 + sound/soc/codecs/Makefile | 2 + sound/soc/codecs/tdf8532.c | 377 +++++++++++++++++++++++++++++++++++++ sound/soc/codecs/tdf8532.h | 101 ++++++++++ 4 files changed, 485 insertions(+) create mode 100644 sound/soc/codecs/tdf8532.c create mode 100644 sound/soc/codecs/tdf8532.h diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index b002d79d910e..006be9a90286 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -163,6 +163,7 @@ config SND_SOC_ALL_CODECS select SND_SOC_TAS5720 if I2C select SND_SOC_TAS6424 if I2C select SND_SOC_TDA7419 if I2C + select SND_SOC_TDF8532 if I2C select SND_SOC_TFA9879 if I2C select SND_SOC_TLV320AIC23_I2C if I2C select SND_SOC_TLV320AIC23_SPI if SPI_MASTER @@ -1015,6 +1016,10 @@ config SND_SOC_TDA7419 depends on I2C select REGMAP_I2C +config SND_SOC_TDF8532 + tristate + depends on I2C + config SND_SOC_TFA9879 tristate "NXP Semiconductors TFA9879 amplifier" depends on I2C diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile index 5aed74a73acf..b9ec39dfc960 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile @@ -179,6 +179,7 @@ snd-soc-tas571x-objs := tas571x.o snd-soc-tas5720-objs := tas5720.o snd-soc-tas6424-objs := tas6424.o snd-soc-tda7419-objs := tda7419.o +snd-soc-tdf8532-objs := tdf8532.o snd-soc-tfa9879-objs := tfa9879.o snd-soc-tlv320aic23-objs := tlv320aic23.o snd-soc-tlv320aic23-i2c-objs := tlv320aic23-i2c.o @@ -443,6 +444,7 @@ obj-$(CONFIG_SND_SOC_TAS571X) += snd-soc-tas571x.o obj-$(CONFIG_SND_SOC_TAS5720) += snd-soc-tas5720.o obj-$(CONFIG_SND_SOC_TAS6424) += snd-soc-tas6424.o obj-$(CONFIG_SND_SOC_TDA7419) += snd-soc-tda7419.o +obj-$(CONFIG_SND_SOC_TDF8532) += snd-soc-tdf8532.o obj-$(CONFIG_SND_SOC_TFA9879) += snd-soc-tfa9879.o obj-$(CONFIG_SND_SOC_TLV320AIC23) += snd-soc-tlv320aic23.o obj-$(CONFIG_SND_SOC_TLV320AIC23_I2C) += snd-soc-tlv320aic23-i2c.o diff --git a/sound/soc/codecs/tdf8532.c b/sound/soc/codecs/tdf8532.c new file mode 100644 index 000000000000..7a3cca073845 --- /dev/null +++ b/sound/soc/codecs/tdf8532.c @@ -0,0 +1,377 @@ +/* + * Codec driver for NXP Semiconductors - TDF8532 + * Copyright (c) 2017, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "tdf8532.h" + +static int __tdf8532_build_pkt(struct tdf8532_priv *dev_data, + va_list valist, u8 *payload) +{ + int param; + u8 len; + u8 *cmd_payload; + const u8 cmd_offset = 3; + + payload[HEADER_TYPE] = MSG_TYPE_STX; + payload[HEADER_PKTID] = dev_data->pkt_id; + + cmd_payload = &(payload[cmd_offset]); + + param = va_arg(valist, int); + len = 0; + + while (param != END) { + cmd_payload[len] = param; + + len++; + + param = va_arg(valist, int); + } + + payload[HEADER_LEN] = len; + + return len + cmd_offset; +} + +static int __tdf8532_single_write(struct tdf8532_priv *dev_data, + int dummy, ...) +{ + va_list valist; + int ret; + u8 len; + u8 payload[255]; + + va_start(valist, dummy); + + len = __tdf8532_build_pkt(dev_data, valist, payload); + + va_end(valist); + + print_hex_dump_debug("tdf8532-codec: Tx:", DUMP_PREFIX_NONE, 32, 1, + payload, len, false); + ret = i2c_master_send(dev_data->i2c, payload, len); + + dev_data->pkt_id++; + + if (ret < 0) { + dev_err(&(dev_data->i2c->dev), + "i2c send packet returned: %d\n", ret); + + return ret; + } + + return 0; +} + + +static uint8_t tdf8532_read_wait_ack(struct tdf8532_priv *dev_data, + unsigned long timeout) +{ + uint8_t ack_repl[HEADER_SIZE] = {0, 0, 0}; + unsigned long timeout_point = jiffies + timeout; + int ret; + + do { + ret = i2c_master_recv(dev_data->i2c, ack_repl, HEADER_SIZE); + if (ret < 0) + goto out; + } while (time_before(jiffies, timeout_point) && + ack_repl[0] != MSG_TYPE_ACK); + + if (ack_repl[0] != MSG_TYPE_ACK) + return -ETIME; + else + return ack_repl[2]; + +out: + return ret; +} + +static uint8_t tdf8532_single_read(struct tdf8532_priv *dev_data, + char **repl_buff) +{ + int ret; + uint8_t len; + + struct device *dev = &(dev_data->i2c->dev); + + ret = tdf8532_read_wait_ack(dev_data, msecs_to_jiffies(ACK_TIMEOUT)); + + if (ret < 0) { + dev_err(dev, + "Error waiting for ACK reply: %d\n", ret); + goto out; + } + + len = ret + HEADER_SIZE; + + *repl_buff = kzalloc(len, GFP_KERNEL); + + ret = i2c_master_recv(dev_data->i2c, *repl_buff, len); + + print_hex_dump_debug("tdf8532-codec: Rx:", DUMP_PREFIX_NONE, 32, 1, + *repl_buff, len, false); + + if (ret < 0 || ret != len) { + dev_err(dev, + "i2c recv packet returned: %d (expected: %d)\n", + ret, len); + goto out_free; + } + + return len; + +out_free: + kfree(*repl_buff); + repl_buff = NULL; +out: + return ret; +} + +static int tdf8532_get_state(struct tdf8532_priv *dev_data, + struct get_dev_status_repl **status_repl) +{ + int ret = 0; + char *repl_buff = NULL; + + ret = tdf8532_amp_write(dev_data, GET_DEV_STATUS); + if (ret < 0) + goto out; + + ret = tdf8532_single_read(dev_data, &repl_buff); + if (ret < 0) + goto out; + + *status_repl = (struct get_dev_status_repl *) repl_buff; + +out: + return ret; +} + +static int tdf8532_wait_state(struct tdf8532_priv *dev_data, u8 req_state, + unsigned long timeout) +{ + unsigned long timeout_point = jiffies + msecs_to_jiffies(timeout); + int ret; + struct get_dev_status_repl *status_repl; + struct device *dev = &(dev_data->i2c->dev); + + do { + ret = tdf8532_get_state(dev_data, &status_repl); + if (ret < 0) + goto out; + + print_hex_dump_debug("tdf8532-codec: wait_state: ", + DUMP_PREFIX_NONE, 32, 1, status_repl, + 6, false); + } while (time_before(jiffies, timeout_point) + && status_repl->state != req_state); + + if (status_repl->state == req_state) + return 0; + + ret = -ETIME; + + dev_err(dev, "tdf8532-codec: state: %u, req_state: %u, ret: %d\n", + status_repl->state, req_state, ret); + +out: + kfree(status_repl); + return ret; +} + +static int tdf8532_start_play(struct tdf8532_priv *tdf8532) +{ + int ret; + + ret = tdf8532_amp_write(tdf8532, SET_CLK_STATE, CLK_CONNECT); + if (ret < 0) + return ret; + + ret = tdf8532_amp_write(tdf8532, SET_CHNL_ENABLE, + CHNL_MASK(tdf8532->channels)); + + if (ret >= 0) + ret = tdf8532_wait_state(tdf8532, STATE_PLAY, ACK_TIMEOUT); + + return ret; +} + + +static int tdf8532_stop_play(struct tdf8532_priv *tdf8532) +{ + int ret; + + ret = tdf8532_amp_write(tdf8532, SET_CHNL_DISABLE, + CHNL_MASK(tdf8532->channels)); + if (ret < 0) + goto out; + + ret = tdf8532_wait_state(tdf8532, STATE_STBY, ACK_TIMEOUT); + if (ret < 0) + goto out; + + ret = tdf8532_amp_write(tdf8532, SET_CLK_STATE, CLK_DISCONNECT); + if (ret < 0) + goto out; + + ret = tdf8532_wait_state(tdf8532, STATE_IDLE, ACK_TIMEOUT); + +out: + return ret; +} + + +static int tdf8532_dai_trigger(struct snd_pcm_substream *substream, int cmd, + struct snd_soc_dai *dai) +{ + int ret = 0; + struct snd_soc_component *component = dai->component; + struct tdf8532_priv *tdf8532 = snd_soc_component_get_drvdata(component); + + dev_dbg(component->dev, "%s: cmd = %d\n", __func__, cmd); + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + case SNDRV_PCM_TRIGGER_RESUME: + ret = tdf8532_start_play(tdf8532); + break; + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + case SNDRV_PCM_TRIGGER_SUSPEND: + case SNDRV_PCM_TRIGGER_STOP: + ret = tdf8532_stop_play(tdf8532); + break; + } + + return ret; +} + +static int tdf8532_mute(struct snd_soc_dai *dai, int mute) +{ + struct snd_soc_component *component = dai->component; + struct tdf8532_priv *tdf8532 = snd_soc_component_get_drvdata(component); + + dev_dbg(component->dev, "%s\n", __func__); + + if (mute) + return tdf8532_amp_write(tdf8532, SET_CHNL_MUTE, + CHNL_MASK(CHNL_MAX)); + else + return tdf8532_amp_write(tdf8532, SET_CHNL_UNMUTE, + CHNL_MASK(CHNL_MAX)); +} + +static const struct snd_soc_dai_ops tdf8532_dai_ops = { + .trigger = tdf8532_dai_trigger, + .digital_mute = tdf8532_mute, +}; + +static struct snd_soc_component_driver soc_component_tdf8532; + +static struct snd_soc_dai_driver tdf8532_dai[] = { + { + .name = "tdf8532-hifi", + .playback = { + .stream_name = "Playback", + .channels_min = 4, + .channels_max = 4, + .rates = SNDRV_PCM_RATE_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + }, + .ops = &tdf8532_dai_ops, + } +}; + +static int tdf8532_i2c_probe(struct i2c_client *i2c, + const struct i2c_device_id *id) +{ + int ret; + struct tdf8532_priv *dev_data; + struct device *dev = &(i2c->dev); + + dev_dbg(&i2c->dev, "%s\n", __func__); + + dev_data = devm_kzalloc(dev, sizeof(struct tdf8532_priv), GFP_KERNEL); + + if (!dev_data) { + ret = -ENOMEM; + goto out; + } + + if (ret < 0) + dev_err(&i2c->dev, "Failed to set fast mute option: %d\n", ret); + + dev_data->i2c = i2c; + dev_data->pkt_id = 0; + dev_data->channels = 4; + + i2c_set_clientdata(i2c, dev_data); + + ret = devm_snd_soc_register_component(&i2c->dev, &soc_component_tdf8532, + tdf8532_dai, ARRAY_SIZE(tdf8532_dai)); + if (ret != 0) { + dev_err(&i2c->dev, "Failed to register codec: %d\n", ret); + goto out; + } + +out: + return ret; +} + +static int tdf8532_i2c_remove(struct i2c_client *i2c) +{ + return 0; +} + +static const struct i2c_device_id tdf8532_i2c_id[] = { + { "tdf8532", 0 }, + { } +}; + +MODULE_DEVICE_TABLE(i2c, tdf8532_i2c_id); + +#if CONFIG_ACPI +static const struct acpi_device_id tdf8532_acpi_match[] = { + {"INT34C3", 0}, + {}, +}; + +MODULE_DEVICE_TABLE(acpi, tdf8532_acpi_match); +#endif + +static struct i2c_driver tdf8532_i2c_driver = { + .driver = { + .name = "tdf8532-codec", + .owner = THIS_MODULE, + .acpi_match_table = ACPI_PTR(tdf8532_acpi_match), + }, + .probe = tdf8532_i2c_probe, + .remove = tdf8532_i2c_remove, + .id_table = tdf8532_i2c_id, +}; + +module_i2c_driver(tdf8532_i2c_driver); + +MODULE_DESCRIPTION("ASoC NXP Semiconductors TDF8532 driver"); +MODULE_AUTHOR("Steffen Wagner "); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/codecs/tdf8532.h b/sound/soc/codecs/tdf8532.h new file mode 100644 index 000000000000..6e3f2c147eac --- /dev/null +++ b/sound/soc/codecs/tdf8532.h @@ -0,0 +1,101 @@ +/* + * tdf8532.h - Codec driver for NXP Semiconductors + * Copyright (c) 2017, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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. + */ + + +#ifndef __TDF8532_H_ +#define __TDF8532_H_ + +#define ACK_TIMEOUT 300 + +#define CHNL_MAX 5 + +#define AMP_MOD 0x80 +#define END -1 + +#define MSG_TYPE_STX 0x02 +#define MSG_TYPE_NAK 0x15 +#define MSG_TYPE_ACK 0x6 + +#define HEADER_SIZE 3 +#define HEADER_TYPE 0 +#define HEADER_PKTID 1 +#define HEADER_LEN 2 + +/* Set commands */ +#define SET_CLK_STATE 0x1A +#define CLK_DISCONNECT 0x00 +#define CLK_CONNECT 0x01 + +#define SET_CHNL_ENABLE 0x26 +#define SET_CHNL_DISABLE 0x27 + +#define SET_CHNL_MUTE 0x42 +#define SET_CHNL_UNMUTE 0x43 + +struct header_repl { + u8 msg_type; + u8 pkt_id; + u8 len; +} __packed; + +#define GET_IDENT 0xE0 + +struct get_ident_repl { + struct header_repl header; + u8 module_id; + u8 cmd_id; + u8 type_name; + u8 hw_major; + u8 hw_minor; + u8 sw_major; + u8 sw_minor; + u8 sw_sub; +} __packed; + +#define GET_ERROR 0xE2 + +struct get_error_repl { + struct header_repl header; + u8 module_id; + u8 cmd_id; + u8 last_cmd_id; + u8 error; + u8 status; +} __packed; + +#define GET_DEV_STATUS 0x80 + +enum dev_state {STATE_BOOT, STATE_IDLE, STATE_STBY, STATE_LDAG, STATE_PLAY, + STATE_PROT, STATE_SDWN, STATE_CLFA, STATE_NONE }; + +struct get_dev_status_repl { + struct header_repl header; + u8 module_id; + u8 cmd_id; + u8 state; +} __packed; + +/* Helpers */ +#define CHNL_MASK(channels) (u8)((0x00FF << channels) >> 8) + +#define tdf8532_amp_write(dev_data, ...)\ + __tdf8532_single_write(dev_data, 0, AMP_MOD, __VA_ARGS__, END) + +struct tdf8532_priv { + struct i2c_client *i2c; + u8 channels; + u8 pkt_id; +}; + +#endif From 2118b5dc2228cbaef8457c40000f5c905b2fdd59 Mon Sep 17 00:00:00 2001 From: "Sinha, Mohit" Date: Thu, 8 Jun 2017 20:37:58 +0530 Subject: [PATCH 0770/1103] ASoC: Intel: Skylake: Added support for creating BXTP GPMRB machine with TDF8532 codec GPMRB board has TDF8532 codec on board, so added the machine name. Change-Id: Icdcb03e1068b11de12740ba9b9c4e7e83050aab0 Signed-off-by: Mohit Sinha Reviewed-on: Reviewed-by: Babu, Ramesh Reviewed-by: Shaik, Kareem M Reviewed-by: Kale, Sanyog R Reviewed-by: Nc, Shreyas Reviewed-by: Koul, Vinod Reviewed-by: B, Jayachandran Tested-by: Sm, Bhadur A --- sound/soc/intel/skylake/skl.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/sound/soc/intel/skylake/skl.c b/sound/soc/intel/skylake/skl.c index 97e8367eed30..c1fa7f6aa821 100644 --- a/sound/soc/intel/skylake/skl.c +++ b/sound/soc/intel/skylake/skl.c @@ -1182,6 +1182,11 @@ static struct snd_soc_acpi_mach sst_bxtp_devdata[] = { .machine_quirk = snd_soc_acpi_codec_list, .quirk_data = &bxt_codecs, }, + { + .id = "INT34C3", + .drv_name = "bxt_tdf8532", + .fw_filename = "intel/dsp_fw_bxtn.bin", + }, {} }; From 0f1e7a9f7cfc4d99e6d05b98c7ba7748ce721fc8 Mon Sep 17 00:00:00 2001 From: Guneshwor Singh Date: Thu, 15 Jun 2017 15:08:06 +0530 Subject: [PATCH 0771/1103] ASoC: Intel: boards: Remove SSP1-codec dai link from cnl_rt274 machine Since NHLT does not have SSP1 endpoint, remove it from the dai link definitions Change-Id: I7b08f43d21eeff9decb5722e3af4f142f800b3f7 Signed-off-by: Guneshwor Singh Reviewed-on: Reviewed-by: Prusty, Subhransu S Reviewed-by: Koul, Vinod Reviewed-by: Babu, Ramesh Reviewed-by: audio_build Reviewed-by: Diwakar, Praveen Tested-by: Sm, Bhadur A --- sound/soc/intel/boards/cnl_rt274.c | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) diff --git a/sound/soc/intel/boards/cnl_rt274.c b/sound/soc/intel/boards/cnl_rt274.c index 4b434dc82b9f..ebfe74132da5 100644 --- a/sound/soc/intel/boards/cnl_rt274.c +++ b/sound/soc/intel/boards/cnl_rt274.c @@ -295,21 +295,9 @@ static struct snd_soc_dai_link cnl_rt274_msic_dailink[] = { .dpcm_capture = 1, .init = cnl_rt274_init, }, - { - .name = "SSP1-Codec", - .id = 2, - .cpu_dai_name = "SSP1 Pin", - .codec_name = "snd-soc-dummy", - .codec_dai_name = "snd-soc-dummy-dai", - .platform_name = pname, - .be_hw_params_fixup = cnl_be_fixup, - .ignore_suspend = 1, - .no_pcm = 1, - .dpcm_playback = 1, - }, { .name = "dmic01", - .id = 3, + .id = 2, .cpu_dai_name = "DMIC01 Pin", .codec_name = "dmic-codec", .codec_dai_name = "dmic-hifi", From f9eeb40c049937e8e8dffb1ee8b568ed705e48bd Mon Sep 17 00:00:00 2001 From: Anamika Lal Date: Thu, 29 Jun 2017 11:57:13 +0530 Subject: [PATCH 0772/1103] [WORKAROUND] FIX Kconfigs to build compile with all platforms Change-Id: I4e29037b8c4d2068c244b30668133ae243cb957b Signed-off-by: Anamika Lal --- drivers/sdw/Kconfig | 5 +++-- sound/soc/codecs/Kconfig | 7 +++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/sdw/Kconfig b/drivers/sdw/Kconfig index 1b7e2cc2ebc3..660188bd2c02 100644 --- a/drivers/sdw/Kconfig +++ b/drivers/sdw/Kconfig @@ -1,17 +1,18 @@ menuconfig SDW tristate "SoundWire bus support" depends on CRC8 + depends on X86 help SoundWire interface is typically used for transporting data related to audio functions. menuconfig SDW_CNL tristate "Intel SoundWire master controller support" - depends on SDW + depends on SDW && X86 help Intel SoundWire master controller driver menuconfig SDW_MAXIM_SLAVE bool "SoundWire Slave for the Intel CNL FPGA" - depends on SDW + depends on SDW && X86 help SoundWire Slave on FPGA platform for Intel CNL IP Mostly N for all the cases other than CNL Slave FPGA diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index 006be9a90286..934bd6e4c53f 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -81,7 +81,6 @@ config SND_SOC_ALL_CODECS select SND_SOC_ES7134 select SND_SOC_ES7241 select SND_SOC_GTM601 - select SND_SOC_HDAC_HDMI select SND_SOC_ICS43432 select SND_SOC_INNO_RK3036 select SND_SOC_ISABELLE if I2C @@ -235,7 +234,6 @@ config SND_SOC_ALL_CODECS select SND_SOC_WM9705 if (SND_SOC_AC97_BUS || SND_SOC_AC97_BUS_NEW) select SND_SOC_WM9712 if (SND_SOC_AC97_BUS || SND_SOC_AC97_BUS_NEW) select SND_SOC_WM9713 if (SND_SOC_AC97_BUS || SND_SOC_AC97_BUS_NEW) - select SND_SOC_SVFPGA_I2C if I2C help Normally ASoC codec drivers are only built if a machine driver which uses them is also built since they are only usable with a machine @@ -474,13 +472,13 @@ config SND_SOC_SVFPGA config SND_SOC_SVFPGA_SDW tristate "Intel SVFPGA Codec - SDW" - depends on SDW + depends on SDW && X86 select SND_SOC_SVFPGA select REGMAP_SDW config SND_SOC_SVFPGA_I2C tristate "Intel SVFPGA Codec - I2C" - depends on I2C + depends on I2C && X86 select SND_SOC_SVFPGA config SND_SOC_CS42L51 @@ -628,6 +626,7 @@ config SND_SOC_GTM601 tristate 'GTM601 UMTS modem audio codec' config SND_SOC_HDAC_HDMI + depends on X86 && ACPI tristate select SND_HDA_EXT_CORE select SND_PCM_ELD From 0112e797a3ee434fe3d168a0db32f9c40211da3e Mon Sep 17 00:00:00 2001 From: "Panwar, Ashish" Date: Mon, 22 Feb 2016 17:16:57 +0530 Subject: [PATCH 0773/1103] ASoC: Intel: Boards: FW logging DAI-links for BXT-P Added two DAI links, one for each DSP core, for FW logging Change-Id: I94f3d7be8c5263082deb7e1a995f599dcc174921 Signed-off-by: Panwar, Ashish Reviewed-on: Reviewed-by: Babu, Ramesh Tested-by: Babu, Ramesh Signed-off-by: Mohit Sinha Reviewed-on: Reviewed-by: Koul, Vinod Reviewed-by: Singh, Guneshwor O Reviewed-by: Shaik, Kareem M Tested-by: Avati, Santosh Kumar --- sound/soc/intel/boards/bxt_rt298.c | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/sound/soc/intel/boards/bxt_rt298.c b/sound/soc/intel/boards/bxt_rt298.c index 27308337ab12..a06f50464f54 100644 --- a/sound/soc/intel/boards/bxt_rt298.c +++ b/sound/soc/intel/boards/bxt_rt298.c @@ -428,6 +428,28 @@ static struct snd_soc_dai_link broxton_rt298_dais[] = { .nonatomic = 1, .dynamic = 1, }, + /* Trace Buffer DAI links */ + { + .name = "Bxt Trace Buffer0", + .stream_name = "Core 0 Trace Buffer", + .cpu_dai_name = "TraceBuffer0 Pin", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .platform_name = "0000:00:0e.0", + .capture_only = true, + .ignore_suspend = 1, + }, + { + .name = "Bxt Trace Buffer1", + .stream_name = "Core 1 Trace Buffer", + .cpu_dai_name = "TraceBuffer1 Pin", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .platform_name = "0000:00:0e.0", + .capture_only = true, + .ignore_suspend = 1, + }, + /* Back End DAI links */ { /* SSP5 - Codec */ From efc23a6abbcaf7c99cb1c86328fea7c99b78e41b Mon Sep 17 00:00:00 2001 From: Pramod Kumar Yadav Date: Tue, 26 Apr 2016 15:49:45 +0530 Subject: [PATCH 0774/1103] ASoC: Intel: Board: DAI links for probe in APL machine driver Added two DAI link, one for each playback & capture, for probe Change-Id: I4acd2e9421a96a1bd6938b8e5c8644a739c856a0 Signed-off-by: Pramod Yadav Signed-off-by: Mohit Sinha Reviewed-on: Reviewed-by: Babu, Ramesh Tested-by: Avati, Santosh Kumar --- sound/soc/intel/boards/bxt_rt298.c | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/sound/soc/intel/boards/bxt_rt298.c b/sound/soc/intel/boards/bxt_rt298.c index a06f50464f54..9d40c403ac5c 100644 --- a/sound/soc/intel/boards/bxt_rt298.c +++ b/sound/soc/intel/boards/bxt_rt298.c @@ -428,6 +428,27 @@ static struct snd_soc_dai_link broxton_rt298_dais[] = { .nonatomic = 1, .dynamic = 1, }, + /* Probe DAI links */ + { + .name = "Bxt Compress Probe playback", + .stream_name = "Probe Playback", + .cpu_dai_name = "Compress Probe0 Pin", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .platform_name = "0000:00:0e.0", + .init = NULL, + .nonatomic = 1, + }, + { + .name = "Bxt Compress Probe capture", + .stream_name = "Probe Capture", + .cpu_dai_name = "Compress Probe1 Pin", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .platform_name = "0000:00:0e.0", + .init = NULL, + .nonatomic = 1, + }, /* Trace Buffer DAI links */ { .name = "Bxt Trace Buffer0", From 94b0a684bc080fadc5ebd5aa33e4c888b177d24a Mon Sep 17 00:00:00 2001 From: "Pawse, GuruprasadX" Date: Mon, 11 Jul 2016 18:29:27 +0530 Subject: [PATCH 0775/1103] ASoC: Intel: Skylake: Probe sequence changes based on state transition PROBE STATE TRANSITIONS: This patch implements below state sequence in setting-up and tearing down the extractor/injector probe point. EXTRACTOR: Default state: SKL_PROBE_STATE_EXT_NONE 1. Probe module instantiation and probe point connections State: SKL_PROBE_STATE_EXT_CONNECTED 2. Probe point disconnection State: SKL_PROBE_STATE_EXT_NONE Note: Extractor does not have separate attach/detach DMA step INJECTOR: Default state: SKL_PROBE_STATE_INJ_NONE 1. Probe module instantiation & Injection DMA attachment State: SKL_PROBE_STATE_INJ_DMA_ATTACHED 2. Probe point connection State: SKL_PROBE_STATE_INJ_CONNECTED 3. Probe point disconnection State: SKL_PROBE_STATE_INJ_DISCONNECTED 4. Injection DMA detachment State: SKL_PROBE_STATE_INJ_NONE Change-Id: I4ceb720d9dfae82c8877db1c971715956382852d Signed-off-by: Pawse, GuruprasadX Signed-off-by: Mohit Sinha Reviewed-on: Reviewed-by: Shaik, Kareem M Reviewed-by: Babu, Ramesh Tested-by: Avati, Santosh Kumar --- include/uapi/sound/skl-tplg-interface.h | 22 +- sound/soc/intel/skylake/skl-messages.c | 66 +++- sound/soc/intel/skylake/skl-pcm.c | 14 +- sound/soc/intel/skylake/skl-probe.c | 104 +++++- sound/soc/intel/skylake/skl-sst-ipc.h | 26 +- sound/soc/intel/skylake/skl-topology.c | 408 +++++++++++++++++------- sound/soc/intel/skylake/skl-topology.h | 24 +- 7 files changed, 495 insertions(+), 169 deletions(-) diff --git a/include/uapi/sound/skl-tplg-interface.h b/include/uapi/sound/skl-tplg-interface.h index e5656fc0ca02..2dceadfbc1f5 100644 --- a/include/uapi/sound/skl-tplg-interface.h +++ b/include/uapi/sound/skl-tplg-interface.h @@ -151,17 +151,33 @@ enum skl_module_param_type { SKL_PARAM_BIND }; -enum skl_probe_connect_type { - SKL_PROBE_CONNECT = 3, +enum skl_probe_param_id_type { + SKL_PROBE_INJECT_DMA_ATTACH = 1, + SKL_PROBE_INJECT_DMA_DETACH, + SKL_PROBE_CONNECT, SKL_PROBE_DISCONNECT }; -enum skl_probe_direction { +enum skl_probe_purpose { SKL_PROBE_EXTRACT = 0, SKL_PROBE_INJECT, SKL_PROBE_INJECT_REEXTRACT }; +/* Injector probe states */ +enum skl_probe_state_inj { + SKL_PROBE_STATE_INJ_NONE = 1, + SKL_PROBE_STATE_INJ_DMA_ATTACHED, + SKL_PROBE_STATE_INJ_CONNECTED, + SKL_PROBE_STATE_INJ_DISCONNECTED +}; + +/* Extractor probe states */ +enum skl_probe_state_ext { + SKL_PROBE_STATE_EXT_NONE = 1, + SKL_PROBE_STATE_EXT_CONNECTED +}; + struct skl_dfw_sdw_aggdata { u32 alh_stream_num; u32 channel_mask; diff --git a/sound/soc/intel/skylake/skl-messages.c b/sound/soc/intel/skylake/skl-messages.c index dc648b7db934..b52f1c08d0a5 100644 --- a/sound/soc/intel/skylake/skl-messages.c +++ b/sound/soc/intel/skylake/skl-messages.c @@ -2008,33 +2008,83 @@ static void skl_dump_bind_info(struct skl_sst *ctx, struct skl_module_cfg src_module->m_state, dst_module->m_state); } -int skl_disconnect_probe_point(struct skl_sst *ctx, +int skl_probe_point_disconnect_ext(struct skl_sst *ctx, struct snd_soc_dapm_widget *w) { struct skl_ipc_large_config_msg msg; struct skl_probe_config *pconfig = &ctx->probe_config; struct skl_module_cfg *mcfg; - int probe_point[8] = {0}; + u32 probe_point[NO_OF_EXTRACTOR] = {0}; + int store_prb_pt_index[NO_OF_EXTRACTOR] = {0}; int n = 0, i; + int ret = 0; int no_of_extractor = pconfig->no_extractor; - dev_dbg(ctx->dev, "Disconnecting probe\n"); + dev_dbg(ctx->dev, "Disconnecting extractor probe points\n"); mcfg = w->priv; msg.module_id = mcfg->id.module_id; msg.instance_id = mcfg->id.instance_id; msg.large_param_id = SKL_PROBE_DISCONNECT; for (i = 0; i < no_of_extractor; i++) { - if (pconfig->eprobe[i].set) { - probe_point[n] = pconfig->eprobe[i].id; - pconfig->eprobe[i].set = -1; + if (pconfig->eprobe[i].state == SKL_PROBE_STATE_EXT_CONNECTED) { + probe_point[n] = pconfig->eprobe[i].probe_point_id; + store_prb_pt_index[i] = 1; n++; } } + if (n == 0) + return ret; msg.param_data_size = n * sizeof(u32); - return skl_ipc_set_large_config(&ctx->ipc, &msg, - probe_point); + dev_dbg(ctx->dev, "setting module params size=%d\n", + msg.param_data_size); + ret = skl_ipc_set_large_config(&ctx->ipc, &msg, probe_point); + if (ret < 0) + return -EINVAL; + + for (i = 0; i < pconfig->no_extractor; i++) { + if (store_prb_pt_index[i]) { + pconfig->eprobe[i].state = SKL_PROBE_STATE_EXT_NONE; + dev_dbg(ctx->dev, "eprobe[%d].state %d\n", + i, pconfig->eprobe[i].state); + } + } + + return ret; +} + +int skl_probe_point_disconnect_inj(struct skl_sst *ctx, + struct snd_soc_dapm_widget *w, int index) +{ + struct skl_ipc_large_config_msg msg; + struct skl_probe_config *pconfig = &ctx->probe_config; + struct skl_module_cfg *mcfg; + u32 probe_point = 0; + int ret = 0; + + if (pconfig->iprobe[index].state == SKL_PROBE_STATE_INJ_CONNECTED) { + dev_dbg(ctx->dev, "Disconnecting injector probe point\n"); + mcfg = w->priv; + msg.module_id = mcfg->id.module_id; + msg.instance_id = mcfg->id.instance_id; + msg.large_param_id = SKL_PROBE_DISCONNECT; + probe_point = pconfig->iprobe[index].probe_point_id; + msg.param_data_size = sizeof(u32); + + dev_dbg(ctx->dev, "setting module params size=%d\n", + msg.param_data_size); + ret = skl_ipc_set_large_config(&ctx->ipc, &msg, &probe_point); + if (ret < 0) + return -EINVAL; + + pconfig->iprobe[index].state = SKL_PROBE_STATE_INJ_DISCONNECTED; + dev_dbg(ctx->dev, "iprobe[%d].state %d\n", + index, pconfig->iprobe[index].state); + } + + return ret; + } /* * On module freeup, we need to unbind the module with modules diff --git a/sound/soc/intel/skylake/skl-pcm.c b/sound/soc/intel/skylake/skl-pcm.c index ac9f67c8b7e4..1a722d2fa87e 100644 --- a/sound/soc/intel/skylake/skl-pcm.c +++ b/sound/soc/intel/skylake/skl-pcm.c @@ -1774,6 +1774,7 @@ static int skl_get_probe_widget(struct snd_soc_component *component, { struct skl_probe_config *pconfig = &skl->skl_sst->probe_config; struct snd_soc_dapm_widget *w; + int i; list_for_each_entry(w, &component->card->widgets, list) { if (is_skl_dsp_widget_type(w, skl->skl_sst->dev) && @@ -1786,9 +1787,16 @@ static int skl_get_probe_widget(struct snd_soc_component *component, } } - pconfig->probe_count = 0; - pconfig->no_injector = 6; - pconfig->no_extractor = 8; + pconfig->i_refc = 0; + pconfig->e_refc = 0; + pconfig->no_injector = NO_OF_INJECTOR; + pconfig->no_extractor = NO_OF_EXTRACTOR; + + for (i = 0; i < pconfig->no_injector; i++) + pconfig->iprobe[i].state = SKL_PROBE_STATE_INJ_NONE; + + for (i = 0; i < pconfig->no_extractor; i++) + pconfig->eprobe[i].state = SKL_PROBE_STATE_EXT_NONE; return 0; } diff --git a/sound/soc/intel/skylake/skl-probe.c b/sound/soc/intel/skylake/skl-probe.c index b4f5fe4220cf..9ccd19d32ef3 100644 --- a/sound/soc/intel/skylake/skl-probe.c +++ b/sound/soc/intel/skylake/skl-probe.c @@ -40,6 +40,37 @@ */ #define SKL_EXTRACT_PROBE_DMA_BUFF_SIZE 6208 +/* + * ======================== + * PROBE STATE TRANSITIONS: + * ======================== + * Below gives the steps involved in setting-up and tearing down the + * extractor/injector probe point and the corresponding state to which + * it transition after each step. + * + * EXTRACTOR: + * Default state: SKL_PROBE_STATE_EXT_NONE + * 1. Probe module instantiation and probe point connections + * (can connect multiple probe points) + * State: SKL_PROBE_STATE_EXT_CONNECTED + * --> State where the stream is running. + * 2. Probe point disconnection + * State: SKL_PROBE_STATE_EXT_NONE + * Note: Extractor does not have separate attach/detach DMA step + * + * INJECTOR: + * Default state: SKL_PROBE_STATE_INJ_NONE + * 1. Probe module instantiation & Injection DMA attachment + * State: SKL_PROBE_STATE_INJ_DMA_ATTACHED + * 2. Probe point connection + * State: SKL_PROBE_STATE_INJ_CONNECTED + * --> State where the stream is running. + * 3. Probe point disconnection + * State: SKL_PROBE_STATE_INJ_DISCONNECTED + * 4. Injection DMA detachment + * State: SKL_PROBE_STATE_INJ_NONE + */ + static int set_injector_stream(struct hdac_ext_stream *stream, struct snd_soc_dai *dai) { @@ -50,9 +81,9 @@ static int set_injector_stream(struct hdac_ext_stream *stream, */ struct skl *skl = get_skl_ctx(dai->dev); struct skl_probe_config *pconfig = &skl->skl_sst->probe_config; - int i; + int i = skl_probe_get_index(dai, pconfig); - if ((i = skl_get_probe_index(dai, pconfig)) != -1) { + if (i != -1) { pconfig->iprobe[i].stream = stream; pconfig->iprobe[i].dma_id = hdac_stream(stream)->stream_tag - 1; @@ -71,7 +102,7 @@ int skl_probe_compr_open(struct snd_compr_stream *substream, dev_dbg(dai->dev, "%s dev is %s\n", __func__, dev_name(dai->dev)); - if (!pconfig->probe_count) { + if ((pconfig->i_refc + pconfig->e_refc) == 0) { pconfig->edma_buffsize = SKL_EXTRACT_PROBE_DMA_BUFF_SIZE; pconfig->edma_type = SKL_DMA_HDA_HOST_INPUT_CLASS; pconfig->estream = hdac_ext_host_stream_compr_assign(ebus, @@ -118,8 +149,15 @@ int skl_probe_compr_set_params(struct snd_compr_stream *substream, int ret, dma_id; unsigned int format_val = 0; int err; + int index; dev_dbg(dai->dev, "%s: %s\n", __func__, dai->name); + + if (hdac_stream(stream)->prepared) { + dev_dbg(dai->dev, "already stream is prepared - returning\n"); + return 0; + } + ret = skl_substream_alloc_compr_pages(ebus, substream, runtime->fragments*runtime->fragment_size); if (ret < 0) @@ -128,11 +166,6 @@ int skl_probe_compr_set_params(struct snd_compr_stream *substream, dma_id = hdac_stream(stream)->stream_tag - 1; dev_dbg(dai->dev, "dma_id=%d\n", dma_id); - if (hdac_stream(stream)->prepared) { - dev_dbg(dai->dev, "already stream is prepared - returning\n"); - return 0; - } - snd_hdac_stream_reset(hdac_stream(stream)); err = snd_hdac_stream_set_params(hdac_stream(stream), format_val); @@ -148,17 +181,25 @@ int skl_probe_compr_set_params(struct snd_compr_stream *substream, hdac_stream(stream)->prepared = 1; /* Initialize probe module only the first time */ - if (!pconfig->probe_count) { - + if ((pconfig->i_refc + pconfig->e_refc) == 0) { ret = skl_init_probe_module(skl->skl_sst, mconfig); if (ret < 0) return ret; } - if (substream->direction == SND_COMPRESS_PLAYBACK) - skl_tplg_attach_probe_dma(pconfig->w, skl->skl_sst, dai); + if (substream->direction == SND_COMPRESS_PLAYBACK) { + index = skl_probe_get_index(dai, pconfig); + if (index < 0) + return -EINVAL; + + ret = skl_probe_attach_inj_dma(pconfig->w, skl->skl_sst, index); + if (ret < 0) + return -EINVAL; - pconfig->probe_count++; + pconfig->i_refc++; + } else { + pconfig->e_refc++; + } #if USE_SPIB snd_hdac_ext_stream_spbcap_enable(ebus, 1, hdac_stream(stream)->index); @@ -173,15 +214,43 @@ int skl_probe_compr_close(struct snd_compr_stream *substream, struct hdac_ext_bus *ebus = dev_get_drvdata(dai->dev); struct skl *skl = get_skl_ctx(dai->dev); struct skl_probe_config *pconfig = &skl->skl_sst->probe_config; - int ret; + struct skl_module_cfg *mconfig = pconfig->w->priv; + int ret = 0; + int index; dev_dbg(dai->dev, "%s: %s\n", __func__, dai->name); #if USE_SPIB snd_hdac_ext_stream_spbcap_enable(ebus, 0, hdac_stream(stream)->index); #endif + if ((pconfig->i_refc + pconfig->e_refc) == 0) + goto probe_uninit; + + if (substream->direction == SND_COMPRESS_PLAYBACK) { + index = skl_probe_get_index(dai, pconfig); + if (index < 0) + return -EINVAL; + + ret = skl_probe_point_disconnect_inj(skl->skl_sst, + pconfig->w, index); + if (ret < 0) + return -EINVAL; + + ret = skl_probe_detach_inj_dma(skl->skl_sst, pconfig->w, index); + if (ret < 0) + return -EINVAL; + + pconfig->i_refc--; + } else if (substream->direction == SND_COMPRESS_CAPTURE) { + ret = skl_probe_point_disconnect_ext(skl->skl_sst, pconfig->w); + if (ret < 0) + return -EINVAL; + + pconfig->e_refc--; + } - if (!--pconfig->probe_count) { - skl_disconnect_probe_point(skl->skl_sst, pconfig->w); +probe_uninit: + if (((pconfig->i_refc + pconfig->e_refc) == 0) + && mconfig->m_state == SKL_MODULE_INIT_DONE) { ret = skl_uninit_probe_module(skl->skl_sst, pconfig->w->priv); if (ret < 0) return ret; @@ -344,7 +413,8 @@ int skl_probe_compr_trigger(struct snd_compr_stream *substream, int cmd, /* FW starts probe module soon after its params are set. * So to avoid xruns, start DMA first and then set probe params. */ - ret = skl_tplg_set_probe_params(pconfig->w, skl->skl_sst, substream->direction, dai); + ret = skl_probe_point_set_config(pconfig->w, skl->skl_sst, + substream->direction, dai); if (ret < 0) return -EINVAL; } diff --git a/sound/soc/intel/skylake/skl-sst-ipc.h b/sound/soc/intel/skylake/skl-sst-ipc.h index bc9b27884120..d6866bc15469 100644 --- a/sound/soc/intel/skylake/skl-sst-ipc.h +++ b/sound/soc/intel/skylake/skl-sst-ipc.h @@ -20,6 +20,7 @@ #include #include "../common/sst-ipc.h" #include "skl-sst-dsp.h" +#include "skl-tplg-interface.h" struct sst_dsp; struct skl_sst; @@ -100,21 +101,36 @@ struct skl_lib_info { }; struct injector_data { - int set; - int id; + /* connect or disconnect */ + u8 operation; + /* Specifies EXTRACTOR or INJECTOR or INJECT_REEXTRACT */ + u32 purpose; + /* Injector probe param */ + u32 probe_point_id; struct hdac_ext_stream *stream; int dma_id; int dma_buf_size; + enum skl_probe_state_inj state; }; struct extractor_data { - int set; - int id; + /* Probe connect or disconnect */ + u8 operation; + /* Specifies EXTRACTOR or INJECTOR or INJECT_REEXTRACT */ + u32 purpose; + /* Extractor probe param */ + u32 probe_point_id; + enum skl_probe_state_ext state; }; struct skl_probe_config { struct snd_soc_dapm_widget *w; - int probe_count; + /* Number of extractor DMA's used */ + int e_refc; + + /* Number of injector DMA's used */ + int i_refc; + int edma_id; int edma_type; int edma_buffsize; diff --git a/sound/soc/intel/skylake/skl-topology.c b/sound/soc/intel/skylake/skl-topology.c index 6e7a192cc68c..4bcd184033e8 100644 --- a/sound/soc/intel/skylake/skl-topology.c +++ b/sound/soc/intel/skylake/skl-topology.c @@ -495,7 +495,7 @@ static void skl_tplg_update_module_params(struct snd_soc_dapm_widget *w, skl_dump_mconfig(ctx, m_cfg); } -int skl_get_probe_index(struct snd_soc_dai *dai, +int skl_probe_get_index(struct snd_soc_dai *dai, struct skl_probe_config *pconfig) { int i, ret = -1; @@ -509,62 +509,124 @@ int skl_get_probe_index(struct snd_soc_dai *dai, return ret; } -int skl_tplg_attach_probe_dma(struct snd_soc_dapm_widget *w, - struct skl_sst *ctx, struct snd_soc_dai *dai) +int skl_probe_attach_inj_dma(struct snd_soc_dapm_widget *w, + struct skl_sst *ctx, int index) { - int i, ret; + int ret = -EINVAL; + struct skl_module_cfg *mconfig = w->priv; - struct skl_attach_probe_dma ad; + struct skl_probe_attach_inj_dma ad; struct skl_probe_config *pconfig = &ctx->probe_config; - if ((i = skl_get_probe_index(dai, pconfig)) != -1) { - ad.node_id.node.vindex = pconfig->iprobe[i].dma_id; + if (pconfig->iprobe[index].state == SKL_PROBE_STATE_INJ_NONE) { + dev_dbg(ctx->dev, "Attaching injector DMA\n"); + ad.node_id.node.vindex = pconfig->iprobe[index].dma_id; ad.node_id.node.dma_type = SKL_DMA_HDA_HOST_OUTPUT_CLASS; ad.node_id.node.rsvd = 0; ad.dma_buff_size = SKL_INJECT_PROBE_DMA_BUFF_SIZE; + + ret = skl_set_module_params(ctx, (void *)&ad, + sizeof(struct skl_probe_attach_inj_dma), + SKL_PROBE_INJECT_DMA_ATTACH, mconfig); + if (ret < 0) + return -EINVAL; + + pconfig->iprobe[index].state = SKL_PROBE_STATE_INJ_DMA_ATTACHED; + dev_dbg(ctx->dev, "iprobe[%d].state %d\n", index, + pconfig->iprobe[index].state); } ret = skl_set_module_params(ctx, (u32 *)&ad, - sizeof(struct skl_attach_probe_dma), 1, mconfig); + sizeof(struct skl_probe_attach_inj_dma), + 1, mconfig); return ret; } -int skl_tplg_set_probe_params(struct snd_soc_dapm_widget *w, +int skl_probe_detach_inj_dma(struct skl_sst *ctx, struct snd_soc_dapm_widget *w, + int index) +{ + struct skl_module_cfg *mconfig = w->priv; + struct skl_probe_config *pconfig = &ctx->probe_config; + struct skl_ipc_large_config_msg msg; + union skl_connector_node_id node_id; + int ret = -EINVAL; + + if (pconfig->iprobe[index].state == SKL_PROBE_STATE_INJ_DISCONNECTED) { + dev_dbg(ctx->dev, "Detaching injector DMA\n"); + node_id.node.vindex = pconfig->iprobe[index].dma_id; + node_id.node.dma_type = SKL_DMA_HDA_HOST_OUTPUT_CLASS; + node_id.node.rsvd = 0; + + msg.module_id = mconfig->id.module_id; + msg.instance_id = mconfig->id.instance_id; + msg.large_param_id = SKL_PROBE_INJECT_DMA_DETACH; + msg.param_data_size = sizeof(union skl_connector_node_id); + + dev_dbg(ctx->dev, "setting module params size=%d\n", + msg.param_data_size); + ret = skl_ipc_set_large_config(&ctx->ipc, &msg, + (u32 *)&node_id); + if (ret < 0) + return -EINVAL; + + pconfig->iprobe[index].state = SKL_PROBE_STATE_INJ_NONE; + dev_dbg(ctx->dev, "iprobe[%d].state %d\n", index, + pconfig->iprobe[index].state); + } + return ret; +} + + +int skl_probe_point_set_config(struct snd_soc_dapm_widget *w, struct skl_sst *ctx, int direction, struct snd_soc_dai *dai) { - int i, ret = 0, n = 0; + int i, ret = -EIO, n = 0; struct skl_module_cfg *mconfig = w->priv; const struct snd_kcontrol_new *k; - struct soc_bytes_ext *sb; - struct skl_probe_data *bc; struct skl_probe_config *pconfig = &ctx->probe_config; struct probe_pt_param prb_pt_param[8] = {{0}}; + int store_prb_pt_index[8] = {0}; if (direction == SND_COMPRESS_PLAYBACK) { /* only one injector point can be set at a time*/ - n = skl_get_probe_index(dai, pconfig); + n = skl_probe_get_index(dai, pconfig); if (n < 0) return -EINVAL; k = &w->kcontrol_news[pconfig->no_extractor + n]; - - if (k->access & SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK) { - sb = (void *) k->private_value; - bc = (struct skl_probe_data *)sb->dobj.private; - pr_debug("bc->is_ext_inj = %d, bc->params = %d, bc->is_connect = %d \n", - bc->is_ext_inj, bc->params, bc->is_connect); - if (!(bc->is_ext_inj == SKL_PROBE_INJECT || - bc->is_ext_inj == SKL_PROBE_INJECT_REEXTRACT)) + dev_dbg(dai->dev, "operation = %d, purpose = %d, probe_point_id = %d\n", + pconfig->iprobe[n].operation, pconfig->iprobe[n].purpose, + pconfig->iprobe[n].probe_point_id); + + if ((k->access & SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK) + && (pconfig->iprobe[n].state == + SKL_PROBE_STATE_INJ_DMA_ATTACHED) + && (pconfig->iprobe[n].operation == + SKL_PROBE_CONNECT) + && (pconfig->iprobe[n].purpose == + SKL_PROBE_INJECT || + pconfig->iprobe[n].purpose == + SKL_PROBE_INJECT_REEXTRACT)) { + + prb_pt_param[0].params = + pconfig->iprobe[n].probe_point_id; + prb_pt_param[0].connection = pconfig->iprobe[n].purpose; + prb_pt_param[0].node_id = pconfig->iprobe[n].dma_id; + ret = skl_set_module_params(ctx, (void *)prb_pt_param, + sizeof(struct probe_pt_param), + SKL_PROBE_CONNECT, mconfig); + if (ret < 0) { + dev_dbg(dai->dev, "failed to set injector probe point\n"); return -EINVAL; + } - prb_pt_param[0].params = (int)bc->params; - prb_pt_param[0].connection = bc->is_ext_inj; - prb_pt_param[0].node_id = pconfig->iprobe[n].dma_id; - ret = skl_set_module_params(ctx, (void *)prb_pt_param, sizeof(struct probe_pt_param), - bc->is_connect, mconfig); + pconfig->iprobe[n].state = + SKL_PROBE_STATE_INJ_CONNECTED; + dev_dbg(dai->dev, "iprobe[%d].state %d\n", n, + pconfig->iprobe[n].state); } } else if (direction == SND_COMPRESS_CAPTURE) { @@ -572,27 +634,50 @@ int skl_tplg_set_probe_params(struct snd_soc_dapm_widget *w, /*multiple extractor points can be set simultaneously*/ for (i = 0; i < pconfig->no_extractor; i++) { k = &w->kcontrol_news[i]; - if (k->access & SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK) { - sb = (void *) k->private_value; - bc = (struct skl_probe_data *)sb->dobj.private; - - pr_debug("bc->is_ext_inj = %d, bc->params = %d, bc->is_connect = %d \n", - bc->is_ext_inj, bc->params, bc->is_connect); - if (bc->is_ext_inj == SKL_PROBE_EXTRACT && - pconfig->eprobe[i].set == 1) { - pr_debug("Retrieving the exractor params \n"); - prb_pt_param[n].params = (int)bc->params; - prb_pt_param[n].connection = bc->is_ext_inj; - prb_pt_param[n].node_id = -1; - n++; - } + dev_dbg(dai->dev, "operation = %d, purpose = %d, probe_point_id = %d\n", + pconfig->eprobe[i].operation, + pconfig->eprobe[i].purpose, + pconfig->eprobe[i].probe_point_id); + if ((k->access & SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK) + && (pconfig->eprobe[i].state == + SKL_PROBE_STATE_EXT_NONE) + && (pconfig->eprobe[i].operation == + SKL_PROBE_CONNECT) + && (pconfig->eprobe[i].purpose == + SKL_PROBE_EXTRACT || + pconfig->eprobe[i].purpose == + SKL_PROBE_INJECT_REEXTRACT)) { + + dev_dbg(dai->dev, "Retrieving the exractor params\n"); + prb_pt_param[n].params = + pconfig->eprobe[i].probe_point_id; + prb_pt_param[n].connection = + pconfig->eprobe[i].purpose; + prb_pt_param[n].node_id = -1; + store_prb_pt_index[i] = 1; + n++; } } - if (n > 0) + if (n > 0) { ret = skl_set_module_params(ctx, (void *)prb_pt_param, n * sizeof(struct probe_pt_param), SKL_PROBE_CONNECT, mconfig); + if (ret < 0) { + dev_dbg(dai->dev, "failed to set extractor probe point\n"); + return -EINVAL; + } + } + + for (i = 0; i < pconfig->no_extractor; i++) { + if (store_prb_pt_index[i]) { + pconfig->eprobe[i].state = + SKL_PROBE_STATE_EXT_CONNECTED; + dev_dbg(dai->dev, "eprobe[%d].state %d\n", + n, pconfig->eprobe[i].state); + } + } + } return ret; } @@ -1906,64 +1991,85 @@ static void skl_tplg_fill_dma_id(struct skl_module_cfg *mcfg, memcpy(pipe->p_params, params, sizeof(*params)); } } -static int skl_cache_probe_param(struct snd_kcontrol *kctl, - struct skl_probe_data *ap, struct skl_sst *ctx) + +static int skl_probe_set_tlv_ext(struct snd_kcontrol *kcontrol) { - struct skl_probe_config *pconfig = &ctx->probe_config; - union skl_connector_node_id node_id = {-1}; + struct snd_soc_dapm_context *dapm = + snd_soc_dapm_kcontrol_dapm(kcontrol); + struct snd_soc_dapm_widget *w = snd_soc_dapm_kcontrol_widget(kcontrol); + struct skl_module_cfg *mconfig = w->priv; + struct soc_bytes_ext *sb = (void *) kcontrol->private_value; + struct skl_probe_data *ap = (struct skl_probe_data *)sb->dobj.private; + struct skl *skl = get_skl_ctx(dapm->dev); + struct skl_probe_config *pconfig = &skl->skl_sst->probe_config; + struct probe_pt_param connect_point; + int disconnect_point; + int ret = 0; int index = -1, i; char buf[20], pos[10]; - if (ap->is_ext_inj == SKL_PROBE_EXTRACT) { - /* From the control ID get the extractor index */ - for (i = 0; i < pconfig->no_extractor; i++) { - strcpy(buf, "Extractor"); - snprintf(pos, 4, "%d", i); - if (strstr(kctl->id.name, strcat(buf, pos))) { - index = i; - break; - } - } - if (index < 0) - return -EINVAL; + for (i = 0; i < pconfig->no_extractor; i++) { + strcpy(buf, "Extractor"); + snprintf(pos, 4, "%d", i); + if (strstr(kcontrol->id.name, strcat(buf, pos))) { + index = i; + break; + } + } + if (index < 0) + return -EINVAL; - pr_debug("Setting extractor probe index %d\n", index); - memcpy(&ap->node_id, &node_id, sizeof(u32)); - pconfig->eprobe[index].id = ap->params; - if (ap->is_connect == SKL_PROBE_CONNECT) - pconfig->eprobe[index].set = 1; - else if (ap->is_connect == SKL_PROBE_DISCONNECT) - pconfig->eprobe[index].set = -1; + if ((ap->operation == SKL_PROBE_CONNECT) && + (pconfig->eprobe[index].state == SKL_PROBE_STATE_EXT_NONE)) { + /* cache extractor params */ + pconfig->eprobe[index].operation = ap->operation; + pconfig->eprobe[index].purpose = ap->purpose; + pconfig->eprobe[index].probe_point_id = ap->probe_point_id; - } else { - /* From the control ID get the injector index */ - for (i = 0; i < pconfig->no_injector; i++) { - strcpy(buf, "Injector"); - snprintf(pos, 4, "%d", i); - if (strstr(kctl->id.name, strcat(buf, pos))) { - index = i; - break; + /* Below check ensures that atleast one extractor stream is in + * progress in which case the driver can send the CONNECT IPC + */ + if (pconfig->e_refc > 0) { + memcpy(&connect_point.params, &ap->probe_point_id, + sizeof(u32)); + connect_point.connection = ap->purpose; + connect_point.node_id = -1; + ret = skl_set_module_params(skl->skl_sst, + (void *)&connect_point, + sizeof(struct probe_pt_param), + SKL_PROBE_CONNECT, mconfig); + if (ret < 0) { + dev_err(dapm->dev, "failed to connect extractor probe point\n"); + return -EINVAL; } + pconfig->eprobe[index].state = + SKL_PROBE_STATE_EXT_CONNECTED; + dev_dbg(dapm->dev, "eprobe[%d].state %d\n", index, + pconfig->eprobe[index].state); } - - if (index < 0) + } else if ((ap->operation == SKL_PROBE_DISCONNECT) && + (pconfig->eprobe[index].state == + SKL_PROBE_STATE_EXT_CONNECTED) && + (pconfig->e_refc > 0)) { + disconnect_point = (int)ap->probe_point_id; + ret = skl_set_module_params(skl->skl_sst, + (void *)&disconnect_point, sizeof(disconnect_point), + SKL_PROBE_DISCONNECT, mconfig); + if (ret < 0) { + dev_err(dapm->dev, "failed to disconnect extractor probe point\n"); return -EINVAL; + } + pconfig->eprobe[index].state = SKL_PROBE_STATE_EXT_NONE; + dev_dbg(dapm->dev, "eprobe[%d].state %d\n", index, + pconfig->eprobe[index].state); + } else + ret = -EINVAL; - pconfig->iprobe[index].id = ap->params; - node_id.node.dma_type = SKL_DMA_HDA_HOST_OUTPUT_CLASS; - node_id.node.vindex = pconfig->iprobe[index].dma_id; - memcpy(&ap->node_id, &node_id, sizeof(u32)); - if (ap->is_connect == SKL_PROBE_CONNECT) - pconfig->iprobe[index].set = 1; - else if (ap->is_connect == SKL_PROBE_DISCONNECT) - pconfig->iprobe[index].set = -1; - } - return 0; + return ret; } -static int skl_tplg_tlv_probe_set(struct snd_kcontrol *kcontrol, - const unsigned int __user *data, unsigned int size) +static int skl_probe_set_tlv_inj(struct snd_kcontrol *kcontrol) { struct snd_soc_dapm_context *dapm = snd_soc_dapm_kcontrol_dapm(kcontrol); @@ -1973,64 +2079,118 @@ static int skl_tplg_tlv_probe_set(struct snd_kcontrol *kcontrol, struct skl_probe_data *ap = (struct skl_probe_data *)sb->dobj.private; struct skl *skl = get_skl_ctx(dapm->dev); struct skl_probe_config *pconfig = &skl->skl_sst->probe_config; - struct probe_pt_param connect_point; int disconnect_point; + int ret = 0; + int index = -1, i; + char buf[20], pos[10]; + + for (i = 0; i < pconfig->no_injector; i++) { + strcpy(buf, "Injector"); + snprintf(pos, 4, "%d", i); + if (strstr(kcontrol->id.name, strcat(buf, pos))) { + index = i; + break; + } + } + if (index < 0) + return -EINVAL; + + if ((ap->operation == SKL_PROBE_CONNECT) && + (pconfig->iprobe[index].state == SKL_PROBE_STATE_INJ_NONE)) { + /* cache injector params */ + pconfig->iprobe[index].operation = ap->operation; + pconfig->iprobe[index].purpose = ap->purpose; + pconfig->iprobe[index].probe_point_id = ap->probe_point_id; + } else if ((ap->operation == SKL_PROBE_DISCONNECT) && + + (pconfig->iprobe[index].state == + SKL_PROBE_STATE_INJ_CONNECTED) && + (pconfig->i_refc > 0)) { + disconnect_point = (int)ap->probe_point_id; + ret = skl_set_module_params(skl->skl_sst, + (void *)&disconnect_point, + sizeof(disconnect_point), + SKL_PROBE_DISCONNECT, mconfig); + if (ret < 0) { + dev_err(dapm->dev, "failed to disconnect injector probe point\n"); + return -EINVAL; + } + pconfig->iprobe[index].state = SKL_PROBE_STATE_INJ_DISCONNECTED; + dev_dbg(dapm->dev, "iprobe[%d].state %d\n", index, + pconfig->iprobe[index].state); + } else + ret = -EINVAL; + + return ret; +} + +static int skl_tplg_tlv_probe_set(struct snd_kcontrol *kcontrol, + const unsigned int __user *data, unsigned int size) +{ + struct snd_soc_dapm_context *dapm = + snd_soc_dapm_kcontrol_dapm(kcontrol); + struct soc_bytes_ext *sb = (void *) kcontrol->private_value; + struct skl_probe_data *ap = (struct skl_probe_data *)sb->dobj.private; void *offset; - int ret; + int ret = -EIO, ret1; - dev_dbg(dapm->dev, "in %s control=%s\n", __func__, kcontrol->id.name); + dev_dbg(dapm->dev, "In %s control=%s\n", __func__, kcontrol->id.name); dev_dbg(dapm->dev, "size = %u, %#x\n", size, size); if (data) { offset = (unsigned char *)data; offset += 2 * sizeof(u32); /* To skip TLV heeader */ - if (copy_from_user(&ap->is_connect, - offset, sizeof(ap->is_connect))) + if (copy_from_user(&ap->operation, + offset, sizeof(ap->operation))) return -EIO; - offset += sizeof(ap->is_connect); - if (copy_from_user(&ap->is_ext_inj, - offset, sizeof(ap->is_ext_inj))) + offset += sizeof(ap->operation); + if (copy_from_user(&ap->purpose, + offset, sizeof(ap->purpose))) return -EIO; - offset += sizeof(ap->is_ext_inj); - if (copy_from_user(&ap->params, - offset, sizeof(ap->params))) + offset += sizeof(ap->purpose); + if (copy_from_user(&ap->probe_point_id, + offset, sizeof(ap->probe_point_id))) return -EIO; - dev_dbg(dapm->dev, "connect state = %d, extract_inject = %d, params = %d \n", - ap->is_connect, ap->is_ext_inj, ap->params); + dev_dbg(dapm->dev, "operation = %d, purpose = %d, probe_point_id = %d\n", + ap->operation, ap->purpose, ap->probe_point_id); - ret = skl_cache_probe_param(kcontrol, ap, skl->skl_sst); - if (ret < 0) - return -EINVAL; - - if (pconfig->probe_count) { - /* In the case of extraction, additional probe points can be set when - * the stream is in progress and the driver can immediately send the - * connect IPC. But in the case of injector, for each probe point - * connection a new stream with the DAI number corresponding to that - * control has to be opened. Hence below check ensures that the - * connect IPC is sent only in case of extractor. - */ - if ((ap->is_connect == SKL_PROBE_CONNECT) - && (ap->is_ext_inj == SKL_PROBE_EXTRACT)) { + /* In the case of extraction, additional probe points can + * be set when the stream is in progress and the driver can + * immediately send the connect IPC. But in the case of + * injector, for each probe point connection a new stream with + * the DAI number corresponding to that control has to be + * opened. Hence below implementation ensures that the connect + * IPC is sent only in case of extractor. + */ + switch (ap->purpose) { + case SKL_PROBE_EXTRACT: + ret = skl_probe_set_tlv_ext(kcontrol); + break; - memcpy(&connect_point.params, &ap->params, sizeof(u32)); - connect_point.connection = ap->is_ext_inj; - memcpy(&connect_point.node_id, (&ap->node_id), sizeof(u32)); - return skl_set_module_params(skl->skl_sst, (void *)&connect_point, - sizeof(struct probe_pt_param), ap->is_connect, mconfig); + case SKL_PROBE_INJECT: + ret = skl_probe_set_tlv_inj(kcontrol); + break; - } else if (ap->is_connect == SKL_PROBE_DISCONNECT) { + case SKL_PROBE_INJECT_REEXTRACT: + /* Injector and extractor control will be set one by one + * for Inject_Reextract + */ + ret = skl_probe_set_tlv_ext(kcontrol); + ret1 = skl_probe_set_tlv_inj(kcontrol); + if (ret == 0 || ret1 == 0) + ret = 0; + else + ret = -EINVAL; + break; - disconnect_point = (int)ap->params; - return skl_set_module_params(skl->skl_sst, (void *)&disconnect_point, - sizeof(disconnect_point), ap->is_connect, mconfig); - } + default: + ret = -EINVAL; } } - return 0; + return ret; } /* diff --git a/sound/soc/intel/skylake/skl-topology.h b/sound/soc/intel/skylake/skl-topology.h index 0c6b5c6b9c99..02256d70f60f 100644 --- a/sound/soc/intel/skylake/skl-topology.h +++ b/sound/soc/intel/skylake/skl-topology.h @@ -468,13 +468,15 @@ struct skl_algo_data { }; struct skl_probe_data { - u8 is_connect; - u32 is_ext_inj; - u32 params; + /* connect or disconnect */ + u8 operation; + /* extractor or injector or inject-reextract */ + u32 purpose; + u32 probe_point_id; u32 node_id; } __packed; -struct skl_attach_probe_dma { +struct skl_probe_attach_inj_dma { union skl_connector_node_id node_id; u32 dma_buff_size; } __packed; @@ -559,12 +561,14 @@ int skl_init_probe_module(struct skl_sst *ctx, struct skl_module_cfg *module_con int skl_uninit_probe_module(struct skl_sst *ctx, struct skl_module_cfg *module_config); -int skl_get_probe_index(struct snd_soc_dai *dai, +int skl_probe_get_index(struct snd_soc_dai *dai, struct skl_probe_config *pconfig); -int skl_tplg_attach_probe_dma(struct snd_soc_dapm_widget *w, - struct skl_sst *ctx, struct snd_soc_dai *dai); -int skl_tplg_set_probe_params(struct snd_soc_dapm_widget *w, +int skl_probe_attach_inj_dma(struct snd_soc_dapm_widget *w, + struct skl_sst *ctx, int index); +int skl_probe_detach_inj_dma(struct skl_sst *ctx, + struct snd_soc_dapm_widget *w, int index); +int skl_probe_point_set_config(struct snd_soc_dapm_widget *w, struct skl_sst *ctx, int direction, struct snd_soc_dai *dai); int skl_tplg_set_module_params(struct snd_soc_dapm_widget *w, @@ -575,8 +579,10 @@ int skl_bind_modules(struct skl_sst *ctx, struct skl_module_cfg int skl_unbind_modules(struct skl_sst *ctx, struct skl_module_cfg *src_module, struct skl_module_cfg *dst_module); -int skl_disconnect_probe_point(struct skl_sst *ctx, +int skl_probe_point_disconnect_ext(struct skl_sst *ctx, struct snd_soc_dapm_widget *w); +int skl_probe_point_disconnect_inj(struct skl_sst *ctx, + struct snd_soc_dapm_widget *w, int index); int skl_set_module_params(struct skl_sst *ctx, u32 *params, int size, u32 param_id, struct skl_module_cfg *mcfg); int skl_get_module_params(struct skl_sst *ctx, u32 *params, int size, From c072c46fd9d526843664308ce7a22fdb61cf8beb Mon Sep 17 00:00:00 2001 From: "Pawse, GuruprasadX" Date: Tue, 2 Aug 2016 17:00:12 +0530 Subject: [PATCH 0776/1103] ASoC: Intel: Skylake: Probe DMA release for extractor 1. Extractor DMA is to be assigned when the first probe stream (irrespective of whether it is injector or extractor) is opened. But if the first probe stream is injector, we get injector's substream pointer and we do not have the right substream pointer for extractor. The existing code passed injector's substream while assigning extractor DMA.This patch sets NULL for substream while assigning the DMA for extractor and sets the correct substream pointer later when open is indeed for extractor. 2. DMA reset for extractor should not be done after probe module is initialized. The existing code reset DMA in hw_params. This can result in DMA reset after probe module init when injector and extractor are started one after the other. 3. Extractor DMA is assigned in compr_open for the first probe stream irrespective of whether it is injector or extractor. So DMA release for extractor should be done in the case where a injector probe alone was started and stopped without starting any extractor. This patch moves the DMA reset from hw params to immediately after DMA assignment in open call back for both injector and extractor. Change-Id: I2604796d81e2e6da5acd3977774887a0b2e14559 Signed-off-by: Pawse, GuruprasadX Signed-off-by: Mohit Sinha Reviewed-on: Reviewed-by: Shaik, Kareem M Reviewed-by: audio_build Reviewed-by: Babu, Ramesh Tested-by: Avati, Santosh Kumar --- sound/soc/intel/skylake/skl-probe.c | 48 ++++++++++++++++++++++++----- 1 file changed, 40 insertions(+), 8 deletions(-) diff --git a/sound/soc/intel/skylake/skl-probe.c b/sound/soc/intel/skylake/skl-probe.c index 9ccd19d32ef3..b563dc38dce8 100644 --- a/sound/soc/intel/skylake/skl-probe.c +++ b/sound/soc/intel/skylake/skl-probe.c @@ -105,29 +105,51 @@ int skl_probe_compr_open(struct snd_compr_stream *substream, if ((pconfig->i_refc + pconfig->e_refc) == 0) { pconfig->edma_buffsize = SKL_EXTRACT_PROBE_DMA_BUFF_SIZE; pconfig->edma_type = SKL_DMA_HDA_HOST_INPUT_CLASS; + /* + * Extractor DMA is to be assigned when the first probe + * stream(irrespective of whether it is injector or extractor) + * is opened. But if the first probe stream is injector, we + * get injector's substream pointer and we do not have the + * right substream pointer for extractor. So, pass NULL for + * substream while assigning the DMA for extractor and set the + * correct substream pointer later when open is indeed for + * extractor. + */ pconfig->estream = hdac_ext_host_stream_compr_assign(ebus, - substream, + NULL, SND_COMPRESS_CAPTURE); - if (!pconfig->estream) + if (!pconfig->estream) { + dev_err(dai->dev, "Failed to assign extractor stream\n"); return -EINVAL; + } pconfig->edma_id = hdac_stream(pconfig->estream)->stream_tag - 1; + snd_hdac_stream_reset(hdac_stream(pconfig->estream)); } if (substream->direction == SND_COMPRESS_PLAYBACK) { stream = hdac_ext_host_stream_compr_assign(ebus, substream, SND_COMPRESS_PLAYBACK); + if (stream == NULL) { + if ((pconfig->i_refc + pconfig->e_refc) == 0) + snd_hdac_ext_stream_release(pconfig->estream, + HDAC_EXT_STREAM_TYPE_HOST); + + dev_err(dai->dev, "Failed to assign injector stream\n"); + return -EBUSY; + } set_injector_stream(stream, dai); runtime->private_data = stream; + snd_hdac_stream_reset(hdac_stream(stream)); } else if (substream->direction == SND_COMPRESS_CAPTURE) { stream = pconfig->estream; runtime->private_data = pconfig->estream; - } - - if (stream == NULL) { - dev_err(dai->dev, "stream = NULL\n"); - return -EBUSY; + /* + * Open is indeed for extractor. So, set the correct substream + * pointer now. + */ + stream->hstream.stream = substream; } hdac_stream(stream)->curr_pos = 0; @@ -166,7 +188,6 @@ int skl_probe_compr_set_params(struct snd_compr_stream *substream, dma_id = hdac_stream(stream)->stream_tag - 1; dev_dbg(dai->dev, "dma_id=%d\n", dma_id); - snd_hdac_stream_reset(hdac_stream(stream)); err = snd_hdac_stream_set_params(hdac_stream(stream), format_val); if (err < 0) @@ -254,6 +275,16 @@ int skl_probe_compr_close(struct snd_compr_stream *substream, ret = skl_uninit_probe_module(skl->skl_sst, pconfig->w->priv); if (ret < 0) return ret; + + /* + * Extractor DMA is assigned in compr_open for the first probe stream + * irrespective of whether it is injector or extractor. + * So DMA release for extractor should be done in the case where + * a injector probe alone was started and stopped without + * starting any extractor. + */ + if (substream->direction == SND_COMPRESS_PLAYBACK) + snd_hdac_ext_stream_release(pconfig->estream, HDAC_EXT_STREAM_TYPE_HOST); } snd_hdac_stream_cleanup(hdac_stream(stream)); @@ -261,6 +292,7 @@ int skl_probe_compr_close(struct snd_compr_stream *substream, skl_substream_free_compr_pages(ebus_to_hbus(ebus), substream); + /* Release the particular injector/extractor stream getting closed */ snd_hdac_ext_stream_release(stream, HDAC_EXT_STREAM_TYPE_HOST); return 0; From a629c9c455111be8083a1c734a517e59dee9907e Mon Sep 17 00:00:00 2001 From: "S, Pavan K" Date: Fri, 30 Jun 2017 20:52:37 +0530 Subject: [PATCH 0777/1103] ASoC: Intel: Multiple I/O PCM format support for pipe If a pipe supports multiple input/output formats, kcontrol is created and selection of pipe input and output configuration is done based on control set. If more than one configuration is supported, then this patch allows user to select configuration of choice using amixer settings. Change-Id: Ie977d9857507a13aade10a1175994ecabcceed0c Signed-off-by: S, Pavan K Reviewed-on: Reviewed-by: R, Dharageswari Reviewed-by: Prodduvaka, Leoni Reviewed-by: Prusty, Subhransu S Reviewed-by: Singh, Guneshwor O Reviewed-by: Diwakar, Praveen Tested-by: Sm, Bhadur A --- include/uapi/sound/skl-tplg-interface.h | 1 + sound/soc/intel/skylake/skl-topology.c | 103 ++++++++++++++++++++++++ sound/soc/intel/skylake/skl-topology.h | 1 + 3 files changed, 105 insertions(+) diff --git a/include/uapi/sound/skl-tplg-interface.h b/include/uapi/sound/skl-tplg-interface.h index 2dceadfbc1f5..89844fb10aa5 100644 --- a/include/uapi/sound/skl-tplg-interface.h +++ b/include/uapi/sound/skl-tplg-interface.h @@ -19,6 +19,7 @@ #define SKL_CONTROL_TYPE_BYTE_TLV 0x100 #define SKL_CONTROL_TYPE_MIC_SELECT 0x102 #define SKL_CONTROL_TYPE_BYTE_PROBE 0x101 +#define SKL_CONTROL_TYPE_MULTI_IO_SELECT 0x103 #define HDA_SST_CFG_MAX 900 /* size of copier cfg*/ #define MAX_IN_QUEUE 8 diff --git a/sound/soc/intel/skylake/skl-topology.c b/sound/soc/intel/skylake/skl-topology.c index 4bcd184033e8..a1f88c3e2aa4 100644 --- a/sound/soc/intel/skylake/skl-topology.c +++ b/sound/soc/intel/skylake/skl-topology.c @@ -60,6 +60,14 @@ static const int mic_quatro_list[][SKL_CH_QUATRO] = { #define CHECK_HW_PARAMS(ch, freq, bps, prm_ch, prm_freq, prm_bps) \ ((ch == prm_ch) && (bps == prm_bps) && (freq == prm_freq)) +#define GET_PIPE(ppl, skl, node, pipe_id, pipe) \ + do { list_for_each_entry(ppl, &skl->ppl_list, node) { \ + if (ppl->pipe->ppl_id == pipe_id) { \ + pipe = ppl->pipe; \ + break; } \ + } \ + } while (0) + static void skl_init_single_module_pipe(struct snd_soc_dapm_widget *w, struct skl *skl); @@ -895,6 +903,35 @@ static int skl_tplg_unload_pipe_modules(struct skl_sst *ctx, return ret; } +static bool is_skl_tplg_multi_fmt(struct skl *skl, struct skl_pipe *pipe) +{ + int i; + struct skl_pipe_fmt *cur_fmt; + struct skl_pipe_fmt *next_fmt; + + if (pipe->conn_type == SKL_PIPE_CONN_TYPE_FE && + pipe->nr_cfgs > 1) { + for (i = 0; i < pipe->nr_cfgs-1; i++) { + if (pipe->direction == SNDRV_PCM_STREAM_PLAYBACK) { + cur_fmt = &pipe->configs[i].out_fmt; + next_fmt = &pipe->configs[i+1].out_fmt; + } else { + cur_fmt = &pipe->configs[i].in_fmt; + next_fmt = &pipe->configs[i+1].in_fmt; + } + if (!CHECK_HW_PARAMS(cur_fmt->channels, cur_fmt->freq, + cur_fmt->bps, + next_fmt->channels, + next_fmt->freq, next_fmt->bps)) + return true; + } + } else if (pipe->nr_cfgs > 1) { + return true; + } + + return false; +} + /* * Here, we select pipe format based on the pipe type and pipe * direction to determine the current config index for the pipeline. @@ -912,12 +949,21 @@ skl_tplg_get_pipe_config(struct skl *skl, struct skl_module_cfg *mconfig) struct skl_pipe_fmt *fmt = NULL; bool in_fmt = false; int i; + bool ret; if (pipe->nr_cfgs == 0) { pipe->cur_config_idx = 0; return 0; } + ret = is_skl_tplg_multi_fmt(skl, pipe); + if (ret) { + pipe->cur_config_idx = pipe->pipe_config_idx; + pipe->memory_pages = pconfig->mem_pages; + dev_dbg(ctx->dev, "found pipe config idx:%d\n", + pipe->cur_config_idx); + return 0; + } if (pipe->conn_type == SKL_PIPE_CONN_TYPE_NONE) { dev_dbg(ctx->dev, "No conn_type detected, take 0th config\n"); pipe->cur_config_idx = 0; @@ -1778,6 +1824,58 @@ int skl_tplg_dsp_log_set(struct snd_kcontrol *kcontrol, return 0; } +static int skl_tplg_multi_config_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct hdac_ext_bus *ebus = snd_soc_component_get_drvdata(component); + struct skl *skl = ebus_to_skl(ebus); + struct skl_pipeline *ppl; + struct skl_pipe *pipe = NULL; + u32 *pipe_id; + struct soc_enum *ec = (struct soc_enum *)kcontrol->private_value; + + if (!ec) + return -EINVAL; + + pipe_id = ec->dobj.private; + GET_PIPE(ppl, skl, node, *pipe_id, pipe); + if (!pipe) + return -EIO; + + ucontrol->value.enumerated.item[0] = pipe->pipe_config_idx; + + return 0; +} + +static int skl_tplg_multi_config_set(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct hdac_ext_bus *ebus = snd_soc_component_get_drvdata(component); + struct skl *skl = ebus_to_skl(ebus); + struct skl_pipeline *ppl; + struct skl_pipe *pipe = NULL; + struct soc_enum *ec = (struct soc_enum *)kcontrol->private_value; + u32 *pipe_id; + + if (!ec) + return -EINVAL; + + if (ucontrol->value.enumerated.item[0] > ec->items) + return -EINVAL; + + pipe_id = ec->dobj.private; + GET_PIPE(ppl, skl, node, *pipe_id, pipe); + if (!pipe) + return -EIO; + + pipe->pipe_config_idx = ucontrol->value.enumerated.item[0]; + + return 0; +} + + static int skl_tplg_tlv_control_get(struct snd_kcontrol *kcontrol, unsigned int __user *data, unsigned int size) { @@ -2571,6 +2669,11 @@ static const struct snd_soc_tplg_kcontrol_ops skl_tplg_kcontrol_ops[] = { .get = skl_tplg_mic_control_get, .put = skl_tplg_mic_control_set, }, + { + .id = SKL_CONTROL_TYPE_MULTI_IO_SELECT, + .get = skl_tplg_multi_config_get, + .put = skl_tplg_multi_config_set, + }, }; static int skl_tplg_fill_pipe_cfg(struct device *dev, diff --git a/sound/soc/intel/skylake/skl-topology.h b/sound/soc/intel/skylake/skl-topology.h index 02256d70f60f..07d041fc8b72 100644 --- a/sound/soc/intel/skylake/skl-topology.h +++ b/sound/soc/intel/skylake/skl-topology.h @@ -342,6 +342,7 @@ struct skl_pipe { struct skl_path_config configs[SKL_MAX_PATH_CONFIGS]; struct list_head w_list; bool passthru; + u32 pipe_config_idx; }; enum skl_module_state { From 160f5aa95b3a677f0c64218394b516f209f5dce1 Mon Sep 17 00:00:00 2001 From: "Kareem,Shaik" Date: Thu, 15 Jun 2017 13:25:09 +0530 Subject: [PATCH 0778/1103] ASoC: Intel: Skylake: Parse manifest data to fill DMA control parameters DMA control parameters are required in order to initialize or modify DMA gateway configuration in ADSP Firmware. These parameters are kept in the manifest data blocks and driver should read these values from this manifest. This patch parses manifest private data blocks and fill DMA control configuration structure in driver accordingly. Change-Id: Icb01a78c1869181681c7d82f49069dc666be4444 Signed-off-by: Kareem,Shaik --- include/uapi/sound/snd_sst_tokens.h | 11 +++- sound/soc/intel/skylake/skl-topology.c | 85 ++++++++++++++++++++++++-- sound/soc/intel/skylake/skl.h | 21 +++++++ 3 files changed, 111 insertions(+), 6 deletions(-) diff --git a/include/uapi/sound/snd_sst_tokens.h b/include/uapi/sound/snd_sst_tokens.h index 5d3d81af0c30..7c0149476820 100644 --- a/include/uapi/sound/snd_sst_tokens.h +++ b/include/uapi/sound/snd_sst_tokens.h @@ -243,6 +243,12 @@ * indicate if this endpoint is participating * in aggregation. * + * %SKL_TKN_U32_DMACTRL_CFG_IDX: + * Config index to fill up DMA control params + * + * %SKL_TKN_U32_DMACTRL_CFG_SIZE: + * Size information of DMA control params + * * module_id and loadable flags dont have tokens as these values will be * read from the DSP FW manifest * @@ -339,8 +345,9 @@ enum SKL_TKNS { SKL_TKN_U32_AGG_LINK_ID, SKL_TKN_U32_AGG_CH_MASK, SKL_TKN_U32_AGG_ID, - - SKL_TKN_MAX = SKL_TKN_U32_AGG_ID, + SKL_TKN_U32_DMACTRL_CFG_IDX, + SKL_TKN_U32_DMACTRL_CFG_SIZE, + SKL_TKN_MAX = SKL_TKN_U32_DMACTRL_CFG_SIZE, }; #endif diff --git a/sound/soc/intel/skylake/skl-topology.c b/sound/soc/intel/skylake/skl-topology.c index a1f88c3e2aa4..cd5640eac05a 100644 --- a/sound/soc/intel/skylake/skl-topology.c +++ b/sound/soc/intel/skylake/skl-topology.c @@ -3925,6 +3925,63 @@ static int skl_tplg_get_str_tkn(struct device *dev, return tkn_count; } +static int skl_tplg_mfest_fill_dmactrl(struct device *dev, + struct skl_dmactrl_config *dmactrl_cfg, + struct snd_soc_tplg_vendor_value_elem *tkn_elem) +{ + + u32 cfg_idx = dmactrl_cfg->idx; + struct skl_dmctrl_hdr *hdr = &dmactrl_cfg->hdr[cfg_idx]; + + switch (tkn_elem->token) { + case SKL_TKN_U32_FMT_CH: + hdr->ch = tkn_elem->value; + break; + + case SKL_TKN_U32_FMT_FREQ: + hdr->freq = tkn_elem->value; + break; + + case SKL_TKN_U32_FMT_BIT_DEPTH: + hdr->fmt = tkn_elem->value; + break; + + case SKL_TKN_U32_PIPE_DIRECTION: + hdr->direction = tkn_elem->value; + break; + + case SKL_TKN_U8_TIME_SLOT: + hdr->tdm_slot = tkn_elem->value; + break; + + case SKL_TKN_U32_VBUS_ID: + hdr->vbus_id = tkn_elem->value; + break; + + case SKL_TKN_U32_DMACTRL_CFG_IDX: + dmactrl_cfg->idx = tkn_elem->value; + break; + + case SKL_TKN_U32_DMACTRL_CFG_SIZE: + if (tkn_elem->value && !hdr->data) { + hdr->data = devm_kzalloc(dev, + tkn_elem->value, GFP_KERNEL); + if (!hdr->data) + return -ENOMEM; + hdr->data_size = tkn_elem->value; + } else { + hdr->data_size = 0; + dev_err(dev, "Invalid dmactrl info \n"); + } + break; + default: + dev_err(dev, "Invalid token %d\n", tkn_elem->token); + return -EINVAL; + } + + return 0; +} + static int skl_tplg_manifest_fill_fmt(struct device *dev, struct skl_module_iface *fmt, struct snd_soc_tplg_vendor_value_elem *tkn_elem, @@ -4163,8 +4220,17 @@ static int skl_tplg_get_int_tkn(struct device *dev, case SKL_TKN_U32_FMT_SAMPLE_TYPE: case SKL_TKN_U32_FMT_CH_MAP: case SKL_TKN_MM_U32_INTF_PIN_ID: - ret = skl_tplg_manifest_fill_fmt(dev, fmt, tkn_elem, - dir, pin_idx); + case SKL_TKN_U32_PIPE_DIRECTION: + case SKL_TKN_U8_TIME_SLOT: + case SKL_TKN_U32_VBUS_ID: + case SKL_TKN_U32_DMACTRL_CFG_IDX: + case SKL_TKN_U32_DMACTRL_CFG_SIZE: + if (skl->modules) + ret = skl_tplg_manifest_fill_fmt(dev, fmt, tkn_elem, + dir, pin_idx); + else + ret = skl_tplg_mfest_fill_dmactrl(dev, &skl->cfg.dmactrl_cfg, + tkn_elem); if (ret < 0) return ret; break; @@ -4267,8 +4333,9 @@ static int skl_tplg_get_manifest_data(struct snd_soc_tplg_manifest *manifest, { struct snd_soc_tplg_vendor_array *array; int num_blocks, block_size = 0, block_type, off = 0; + struct skl_dmctrl_hdr *dmactrl_hdr; + int cfg_idx, ret; char *data; - int ret; /* Read the NUM_DATA_BLOCKS descriptor */ array = (struct snd_soc_tplg_vendor_array *)manifest->priv.data; @@ -4313,7 +4380,17 @@ static int skl_tplg_get_manifest_data(struct snd_soc_tplg_manifest *manifest, --num_blocks; } else { - return -EINVAL; + cfg_idx = skl->cfg.dmactrl_cfg.idx; + if (cfg_idx < SKL_MAX_DMACTRL) { + dmactrl_hdr = &skl->cfg.dmactrl_cfg.hdr[cfg_idx]; + if (dmactrl_hdr->data && (dmactrl_hdr->data_size == block_size)) + memcpy(dmactrl_hdr->data, data, block_size); + } else { + dev_err(dev, "error block_idx value exceeding %d\n", cfg_idx); + return -EINVAL; + } + ret = block_size; + --num_blocks; } off += ret; } diff --git a/sound/soc/intel/skylake/skl.h b/sound/soc/intel/skylake/skl.h index 2883d86d56fe..51cc193c3353 100644 --- a/sound/soc/intel/skylake/skl.h +++ b/sound/soc/intel/skylake/skl.h @@ -47,6 +47,8 @@ #define AZX_EM2_DUM_MASK (1 << 23) #define AZX_REG_VS_EM2_L1SEN BIT(13) +#define SKL_MAX_DMA_CFG 24 +#define SKL_MAX_DMACTRL 7 struct skl_dsp_resource { u32 max_mcps; @@ -67,7 +69,26 @@ struct skl_astate_config { struct skl_astate_param astate_table[0]; }; +struct skl_dmctrl_hdr { + u32 vbus_id; + u32 freq; + u32 tdm_slot; + u32 fmt; + u32 direction; + u32 ch; + u32 data_size; + u32 *data; +} __packed; + +struct skl_dmactrl_config { + u32 type; + u32 size; + u32 idx; + struct skl_dmctrl_hdr hdr[SKL_MAX_DMACTRL]; +} __packed; + struct skl_fw_config { + struct skl_dmactrl_config dmactrl_cfg; struct skl_astate_config *astate_cfg; }; From 6b4c29fc5845c2a54895f1e1f2c3d66fe4d4dc01 Mon Sep 17 00:00:00 2001 From: "Kareem,Shaik" Date: Thu, 15 Jun 2017 13:40:04 +0530 Subject: [PATCH 0779/1103] ASoC: Intel: Skylake: Add support for always on CLK configuration For some platforms it is required that ADSP generate BCLK, Frame_sync and MCLK regardless of whether audio stream is active or not. Clock generation is controlled by ADSP Firmware, so driver can configure that by sending DMA control IPC. The configuration for clock is prepared using DMA control manifest data. This patch prepares DMA control IPC by extracting specific ACPI NHLT blob using DMA control manifest data and appending Firmware gateway configuration to NHLT blob. Firmware Gateway configuration is available in DMA control manifest data. Finally DMA control IPC is sent to ADSP after firmware download is completed and ADSP enters D0 state. Change-Id: I65b090931c5ccaf1189c700975a1da6a772a44d8 Signed-off-by: Kareem,Shaik --- sound/soc/intel/skylake/skl-messages.c | 92 ++++++++++++++++++++++++++ sound/soc/intel/skylake/skl-nhlt.c | 20 ++++++ sound/soc/intel/skylake/skl-pcm.c | 4 ++ sound/soc/intel/skylake/skl-topology.c | 11 +-- sound/soc/intel/skylake/skl-topology.h | 1 + sound/soc/intel/skylake/skl.h | 3 + 6 files changed, 123 insertions(+), 8 deletions(-) diff --git a/sound/soc/intel/skylake/skl-messages.c b/sound/soc/intel/skylake/skl-messages.c index b52f1c08d0a5..1fd9a3afe9a9 100644 --- a/sound/soc/intel/skylake/skl-messages.c +++ b/sound/soc/intel/skylake/skl-messages.c @@ -1169,6 +1169,10 @@ int skl_init_dsp(struct skl *skl) dev_dbg(bus->dev, "dsp registration status=%d\n", ret); + /* Set DMA clock controls */ + ret = skl_dsp_set_dma_clk_controls(skl->skl_sst); + if (ret < 0) + return ret; return 0; free_core_state: @@ -1291,6 +1295,9 @@ int skl_resume_dsp(struct skl *skl) skl->cfg.astate_cfg); } return ret; + + /* Set DMA clock controls */ + return skl_dsp_set_dma_clk_controls(skl->skl_sst); } enum skl_bitdepth skl_get_bit_depth(int params) @@ -1544,10 +1551,95 @@ int skl_dsp_set_dma_control(struct skl_sst *ctx, u32 *caps, err = skl_ipc_set_large_config(&ctx->ipc, &msg, (u32 *)dma_ctrl); kfree(dma_ctrl); + return err; } EXPORT_SYMBOL_GPL(skl_dsp_set_dma_control); +static u32 skl_prepare_i2s_node_id(u32 instance, u8 dev_type, + u32 dir, u32 time_slot) +{ + union skl_connector_node_id node_id = {0}; + union skl_ssp_dma_node ssp_node = {0}; + + node_id.node.dma_type = (dir == SNDRV_PCM_STREAM_PLAYBACK) ? + SKL_DMA_I2S_LINK_OUTPUT_CLASS : + SKL_DMA_I2S_LINK_INPUT_CLASS; + ssp_node.dma_node.time_slot_index = time_slot; + ssp_node.dma_node.i2s_instance = instance; + node_id.node.vindex = ssp_node.val; + + return node_id.val; +} + +int skl_dsp_set_dma_clk_controls(struct skl_sst *ctx) +{ + struct nhlt_specific_cfg *cfg = NULL; + struct skl *skl = get_skl_ctx(ctx->dev); + struct skl_dmactrl_config *dmactrl_cfg = &skl->cfg.dmactrl_cfg; + struct skl_dmctrl_hdr *hdr; + u8 *dma_ctrl_config; + void *i2s_config = NULL; + u32 i2s_config_size, node_id; + int i, ret = 0; + + if (!skl->cfg.dmactrl_cfg.size) + return 0; + + for (i = 0; i < SKL_MAX_DMACTRL; i++) { + hdr = &dmactrl_cfg->hdr[i]; + + /* get nhlt specific config info */ + cfg = skl_get_nhlt_specific_cfg(skl, hdr->vbus_id, + NHLT_LINK_SSP, hdr->fmt, + hdr->ch, hdr->freq, + hdr->direction, NHLT_DEVICE_I2S); + + if (cfg && hdr->data_size) { + print_hex_dump(KERN_DEBUG, "NHLT blob Info:", + DUMP_PREFIX_OFFSET, 8, 4, + cfg->caps, cfg->size, false); + + i2s_config_size = cfg->size + hdr->data_size; + i2s_config = kzalloc(i2s_config_size, GFP_KERNEL); + if (!i2s_config) + return -ENOMEM; + + /* copy blob */ + memcpy(i2s_config, cfg->caps, cfg->size); + + /* copy additional dma controls informatioin */ + dma_ctrl_config = (u8 *)i2s_config + cfg->size; + memcpy(dma_ctrl_config, hdr->data, hdr->data_size); + + print_hex_dump(KERN_DEBUG, "Blob + DMA Control Info:", + DUMP_PREFIX_OFFSET, 8, 4, + i2s_config, i2s_config_size, false); + + /* get node id */ + node_id = skl_prepare_i2s_node_id(hdr->vbus_id, + SKL_DEVICE_I2S, + hdr->direction, + hdr->tdm_slot); + + ret = skl_dsp_set_dma_control(ctx, (u32 *)i2s_config, + i2s_config_size, node_id); + + kfree(i2s_config); + + if (ret < 0) + return ret; + + } else { + dev_err(ctx->dev, "Failed to get NHLT config: vbusi_id=%d ch=%d fmt=%d s_rate=%d\n", + hdr->vbus_id, hdr->ch, hdr->fmt, hdr->freq); + return -EIO; + } + } + + return 0; +} + static void skl_setup_out_format(struct skl_sst *ctx, struct skl_module_cfg *mconfig, struct skl_audio_data_format *out_fmt) diff --git a/sound/soc/intel/skylake/skl-nhlt.c b/sound/soc/intel/skylake/skl-nhlt.c index cf3d38136289..742b0cb0dd15 100644 --- a/sound/soc/intel/skylake/skl-nhlt.c +++ b/sound/soc/intel/skylake/skl-nhlt.c @@ -149,6 +149,26 @@ static bool skl_check_ep_match(struct device *dev, struct nhlt_endpoint *epnt, return false; } +struct nhlt_specific_cfg * +skl_get_nhlt_specific_cfg(struct skl *skl, u32 instance, u8 link_type, + u8 s_fmt, u8 num_ch, u32 s_rate, u8 dir, u8 dev_type) +{ + struct nhlt_specific_cfg *cfg = NULL; + struct hdac_ext_bus *ebus = &skl->ebus; + + /* update the blob based on virtual bus_id*/ + if (!skl->nhlt_override) { + dev_warn(ebus_to_hbus(ebus)->dev, "Querying NHLT blob from ACPI NHLT table !!\n"); + cfg = skl_get_ep_blob(skl, instance, link_type, s_fmt, + num_ch, s_rate, dir, dev_type); + } else { + dev_warn(ebus_to_hbus(ebus)->dev, "Querying NHLT blob from Debugfs!!\n"); + cfg = skl_nhlt_get_debugfs_blob(skl->debugfs, link_type, instance, dir); + } + + return cfg; +} + struct nhlt_specific_cfg *skl_get_ep_blob(struct skl *skl, u32 instance, u8 link_type, u8 s_fmt, u8 num_ch, u32 s_rate, diff --git a/sound/soc/intel/skylake/skl-pcm.c b/sound/soc/intel/skylake/skl-pcm.c index 1a722d2fa87e..7fda668d9fc9 100644 --- a/sound/soc/intel/skylake/skl-pcm.c +++ b/sound/soc/intel/skylake/skl-pcm.c @@ -1845,6 +1845,10 @@ static int skl_platform_soc_probe(struct snd_soc_component *component) dev_err(component->dev, "Failed to boot first fw: %d\n", ret); return ret; } + + /* Set DMA clock controls */ + skl_dsp_set_dma_clk_controls(skl->skl_sst); + skl_populate_modules(skl); skl->skl_sst->update_d0i3c = skl_update_d0i3c; skl_dsp_enable_notification(skl->skl_sst, false); diff --git a/sound/soc/intel/skylake/skl-topology.c b/sound/soc/intel/skylake/skl-topology.c index cd5640eac05a..24c08dca8bd5 100644 --- a/sound/soc/intel/skylake/skl-topology.c +++ b/sound/soc/intel/skylake/skl-topology.c @@ -2548,18 +2548,13 @@ static int skl_tplg_be_fill_pipe_params(struct snd_soc_dai *dai, mconfig->formats_config.caps = (u32 *) sdw_cfg; return 0; } + /* update the blob based on virtual bus_id*/ - if (!skl->nhlt_override) { - cfg = skl_get_ep_blob(skl, mconfig->vbus_id, link_type, + cfg = skl_get_nhlt_specific_cfg(skl, mconfig->vbus_id, link_type, params->s_fmt, params->ch, params->s_freq, params->stream, dev_type); - } else { - dev_warn(dai->dev, "Querying NHLT blob from Debugfs!!!!\n"); - cfg = skl_nhlt_get_debugfs_blob(skl->debugfs, - link_type, mconfig->vbus_id, - params->stream); - } + if (cfg) { mconfig->formats_config.caps_size = cfg->size; mconfig->formats_config.caps = (u32 *) &cfg->caps; diff --git a/sound/soc/intel/skylake/skl-topology.h b/sound/soc/intel/skylake/skl-topology.h index 07d041fc8b72..8dbe73ee3d59 100644 --- a/sound/soc/intel/skylake/skl-topology.h +++ b/sound/soc/intel/skylake/skl-topology.h @@ -530,6 +530,7 @@ struct fw_ipc_data { int skl_tplg_be_update_params(struct snd_soc_dai *dai, struct skl_pipe_params *params); +int skl_dsp_set_dma_clk_controls(struct skl_sst *ctx); int skl_dsp_set_dma_control(struct skl_sst *ctx, u32 *caps, u32 caps_size, u32 node_id); void skl_tplg_set_be_dmic_config(struct snd_soc_dai *dai, diff --git a/sound/soc/intel/skylake/skl.h b/sound/soc/intel/skylake/skl.h index 51cc193c3353..318b3c54c44e 100644 --- a/sound/soc/intel/skylake/skl.h +++ b/sound/soc/intel/skylake/skl.h @@ -172,6 +172,9 @@ void skl_nhlt_free(struct nhlt_acpi_table *addr); struct nhlt_specific_cfg *skl_get_ep_blob(struct skl *skl, u32 instance, u8 link_type, u8 s_fmt, u8 no_ch, u32 s_rate, u8 dirn, u8 dev_type); +struct nhlt_specific_cfg * +skl_get_nhlt_specific_cfg(struct skl *skl, u32 instance, u8 link_type, + u8 s_fmt, u8 num_ch, u32 s_rate, u8 dir, u8 dev_type); int skl_get_dmic_geo(struct skl *skl); int skl_nhlt_update_topology_bin(struct skl *skl); From e19eefbe4da57e0f78174a5f823b4044ebb2708d Mon Sep 17 00:00:00 2001 From: "Panwar, Ashish" Date: Wed, 20 Jan 2016 19:13:49 +0530 Subject: [PATCH 0780/1103] ASoC: Intel: bxtn: Initialize fw tracing window for bxt Initializing the tracing window for the platform along with the firmware write pointers Change-Id: Ibb735215c6bd0af8abc0e1146b28b4961277665b Signed-off-by: Panwar, Ashish Reviewed-by: Babu, Ramesh Signed-off-by: Mohit Sinha --- sound/soc/intel/skylake/bxt-sst.c | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/sound/soc/intel/skylake/bxt-sst.c b/sound/soc/intel/skylake/bxt-sst.c index c1f6d8b6ab79..9c8d923fe358 100644 --- a/sound/soc/intel/skylake/bxt-sst.c +++ b/sound/soc/intel/skylake/bxt-sst.c @@ -21,7 +21,7 @@ #include #include "../common/sst-dsp.h" -#include "../common/sst-dsp-priv.h" +#include "skl-fwlog.h" #include "skl-sst-ipc.h" #define BXT_BASEFW_TIMEOUT 3000 @@ -32,6 +32,13 @@ #define BXT_ROM_INIT 0x5 #define BXT_ADSP_SRAM0_BASE 0x80000 +/* Trace Buffer Window */ +#define BXT_ADSP_SRAM2_BASE 0x0C0000 +#define BXT_ADSP_W2_SIZE 0x2000 +#define BXT_ADSP_WP_DSP0 (BXT_ADSP_SRAM0_BASE+0x30) +#define BXT_ADSP_WP_DSP1 (BXT_ADSP_SRAM0_BASE+0x34) +#define BXT_ADSP_NR_DSP 2 + /* Firmware status window */ #define BXT_ADSP_FW_STATUS BXT_ADSP_SRAM0_BASE #define BXT_ADSP_ERROR_CODE (BXT_ADSP_FW_STATUS + 0x4) @@ -570,6 +577,7 @@ int bxt_sst_dsp_init(struct device *dev, void __iomem *mmio_base, int irq, { struct skl_sst *skl; struct sst_dsp *sst; + u32 dsp_wp[] = {BXT_ADSP_WP_DSP0, BXT_ADSP_WP_DSP1}; int ret; ret = skl_sst_ctx_init(dev, irq, fw_name, dsp_ops, dsp, &skl_dev); @@ -590,6 +598,12 @@ int bxt_sst_dsp_init(struct device *dev, void __iomem *mmio_base, int irq, sst_dsp_mailbox_init(sst, (BXT_ADSP_SRAM0_BASE + SKL_ADSP_W0_STAT_SZ), SKL_ADSP_W0_UP_SZ, BXT_ADSP_SRAM1_BASE, SKL_ADSP_W1_SZ); + ret = skl_dsp_init_trace_window(sst, dsp_wp, BXT_ADSP_SRAM2_BASE, + BXT_ADSP_W2_SIZE, BXT_ADSP_NR_DSP); + if (ret) { + dev_err(dev, "FW tracing init failed : %x", ret); + return ret; + } ret = skl_ipc_init(dev, skl); if (ret) { From b97fde514f9a9fd6dbdb3f95348702171fb48f4f Mon Sep 17 00:00:00 2001 From: "Sinha, Mohit" Date: Thu, 6 Jul 2017 16:10:32 +0530 Subject: [PATCH 0781/1103] ASoC: Intel: Board: DAI links for probe in GPMRB machine driver Added two DAI links for probe playback and capture Change-Id: I0bf364eba3b6a2b779625a6fd1b664c2530a1ab2 Signed-off-by: Sinha, Mohit --- sound/soc/intel/boards/bxt_tdf8532.c | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/sound/soc/intel/boards/bxt_tdf8532.c b/sound/soc/intel/boards/bxt_tdf8532.c index 027060b17322..1e2b8be00127 100644 --- a/sound/soc/intel/boards/bxt_tdf8532.c +++ b/sound/soc/intel/boards/bxt_tdf8532.c @@ -78,6 +78,27 @@ static const struct snd_soc_dapm_route broxton_tdf8532_map[] = { /* broxton digital audio interface glue - connects codec <--> CPU */ static struct snd_soc_dai_link broxton_tdf8532_dais[] = { + /* Probe DAI links*/ + { + .name = "Bxt Compress Probe playback", + .stream_name = "Probe Playback", + .cpu_dai_name = "Compress Probe0 Pin", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .platform_name = "0000:00:0e.0", + .init = NULL, + .nonatomic = 1, + }, + { + .name = "Bxt Compress Probe capture", + .stream_name = "Probe Capture", + .cpu_dai_name = "Compress Probe1 Pin", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .platform_name = "0000:00:0e.0", + .init = NULL, + .nonatomic = 1, + }, /* Back End DAI links */ { /* SSP0 - BT */ From 68d36ca49a6a5d24d8d99adb536d51220de4c387 Mon Sep 17 00:00:00 2001 From: "Sinha, Mohit" Date: Thu, 6 Jul 2017 16:21:19 +0530 Subject: [PATCH 0782/1103] ASoC: Intel: Boards: Add FW logging DAI-links for GPMRB Add two FW logging DAI for each DSP core Change-Id: Ic825ecb4afbbcacabda6b74e2e5f2969fc722a1f Signed-off-by: Sinha, Mohit --- sound/soc/intel/boards/bxt_tdf8532.c | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/sound/soc/intel/boards/bxt_tdf8532.c b/sound/soc/intel/boards/bxt_tdf8532.c index 1e2b8be00127..325b59adaf1c 100644 --- a/sound/soc/intel/boards/bxt_tdf8532.c +++ b/sound/soc/intel/boards/bxt_tdf8532.c @@ -99,6 +99,27 @@ static struct snd_soc_dai_link broxton_tdf8532_dais[] = { .init = NULL, .nonatomic = 1, }, + /* Trace Buffer DAI links */ + { + .name = "Bxt Trace Buffer0", + .stream_name = "Core 0 Trace Buffer", + .cpu_dai_name = "TraceBuffer0 Pin", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .platform_name = "0000:00:0e.0", + .capture_only = true, + .ignore_suspend = 1, + }, + { + .name = "Bxt Trace Buffer1", + .stream_name = "Core 1 Trace Buffer", + .cpu_dai_name = "TraceBuffer1 Pin", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .platform_name = "0000:00:0e.0", + .capture_only = true, + .ignore_suspend = 1, + }, /* Back End DAI links */ { /* SSP0 - BT */ From c5fc5c4a80cbb3aaa61f330d360184f3c6184284 Mon Sep 17 00:00:00 2001 From: Pardha Saradhi K Date: Thu, 25 Feb 2016 11:13:35 +0530 Subject: [PATCH 0783/1103] ASoC: Intel: Skylake: Send correct size in ipc header for large config To query information from FW we use "Large Config Get" message which accept arguments in the form of extended params. So we need to send a TX message of fixed size (8 bytes) to retrieve a large reply. Hence, IPC header should reflect correct size of TX message for these messages. Change-Id: Ib055c879d6e9dc00e8c861ab25ea9d9080e98732 Signed-off-by: Pardha Saradhi K Reviewed-by: Babu, Ramesh Signed-off-by: Mohit Sinha --- sound/soc/intel/skylake/skl-sst-ipc.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/sound/soc/intel/skylake/skl-sst-ipc.c b/sound/soc/intel/skylake/skl-sst-ipc.c index 508382d52e04..14802ab7ea20 100644 --- a/sound/soc/intel/skylake/skl-sst-ipc.c +++ b/sound/soc/intel/skylake/skl-sst-ipc.c @@ -1031,7 +1031,11 @@ int skl_ipc_get_large_config(struct sst_generic_ipc *ipc, header.primary |= IPC_MOD_INSTANCE_ID(msg->instance_id); header.primary |= IPC_MOD_ID(msg->module_id); - header.extension = IPC_DATA_OFFSET_SZ(msg->param_data_size); + if (!tx_bytes) + header.extension = IPC_DATA_OFFSET_SZ(msg->param_data_size); + else + header.extension = IPC_DATA_OFFSET_SZ(tx_bytes); + header.extension |= IPC_LARGE_PARAM_ID(msg->large_param_id); header.extension |= IPC_FINAL_BLOCK(1); header.extension |= IPC_INITIAL_BLOCK(1); From 2ae1d523f9688cd62f7741c2593c1c526d92e256 Mon Sep 17 00:00:00 2001 From: Guneshwor Singh Date: Sun, 18 Jun 2017 09:52:40 +0530 Subject: [PATCH 0784/1103] ASoC: Intel: board: Add support for HDMI in cnl_rt274 To enable HDMI/iDisp, corresponding BE DAI links are defined in the machine driver. FE links will come topology with an assumption that the dai link name will consist the string "HDMI". This assumption is made to distinguish other dai links from hdmi dai links. Special handling is needed for hdmi dai links because hdmi jack is mapped for each hdmi BE dai link with the corresponding pcm device. And since FE links come from topology, they can come in any order. Hence the need to keep a track of pcm_count. Change-Id: I6fbdfbdb61d5fa58691cfe84abbd859209ccfce5 Signed-off-by: Guneshwor Singh --- sound/soc/intel/boards/cnl_rt274.c | 129 ++++++++++++++++++++++++++++- 1 file changed, 126 insertions(+), 3 deletions(-) diff --git a/sound/soc/intel/boards/cnl_rt274.c b/sound/soc/intel/boards/cnl_rt274.c index ebfe74132da5..e9e5959e256a 100644 --- a/sound/soc/intel/boards/cnl_rt274.c +++ b/sound/soc/intel/boards/cnl_rt274.c @@ -33,11 +33,27 @@ #include #include +#include "../../codecs/hdac_hdmi.h" #include "../../codecs/rt274.h" #define CNL_FREQ_OUT 19200000 #define CNL_BE_FIXUP_RATE 48000 #define RT274_CODEC_DAI "rt274-aif1" +#define CNL_NAME_SIZE 32 +#define CNL_MAX_HDMI 3 + +static struct snd_soc_jack cnl_hdmi[CNL_MAX_HDMI]; + +struct cnl_hdmi_pcm { + struct list_head head; + struct snd_soc_dai *codec_dai; + int device; +}; + +struct cnl_rt274_private { + struct list_head hdmi_pcm_list; + int pcm_count; +}; static struct snd_soc_dai *cnl_get_codec_dai(struct snd_soc_card *card, const char *dai_name) @@ -158,6 +174,13 @@ static const struct snd_soc_dapm_route cnl_map[] = { {"Headphone Jack", NULL, "Platform Clock"}, {"MIC", NULL, "Platform Clock"}, + + {"hifi1", NULL, "iDisp1 Tx"}, + {"iDisp1 Tx", NULL, "iDisp1_out"}, + {"hifi2", NULL, "iDisp2 Tx"}, + {"iDisp2 Tx", NULL, "iDisp2_out"}, + {"hifi3", NULL, "iDisp3 Tx"}, + {"iDisp3 Tx", NULL, "iDisp3_out"}, }; static int cnl_rt274_init(struct snd_soc_pcm_runtime *runtime) @@ -307,6 +330,36 @@ static struct snd_soc_dai_link cnl_rt274_msic_dailink[] = { .dpcm_capture = 1, .be_hw_params_fixup = cnl_dmic_fixup, }, + { + .name = "iDisp1", + .id = 3, + .cpu_dai_name = "iDisp1 Pin", + .codec_name = "ehdaudio0D2", + .codec_dai_name = "intel-hdmi-hifi1", + .platform_name = pname, + .dpcm_playback = 1, + .no_pcm = 1, + }, + { + .name = "iDisp2", + .id = 4, + .cpu_dai_name = "iDisp2 Pin", + .codec_name = "ehdaudio0D2", + .codec_dai_name = "intel-hdmi-hifi2", + .platform_name = pname, + .dpcm_playback = 1, + .no_pcm = 1, + }, + { + .name = "iDisp3", + .id = 5, + .cpu_dai_name = "iDisp3 Pin", + .codec_name = "ehdaudio0D2", + .codec_dai_name = "intel-hdmi-hifi3", + .platform_name = pname, + .dpcm_playback = 1, + .no_pcm = 1, + }, /* codec-codec link */ { .name = "CNL SSP0-Loop Port", @@ -326,10 +379,67 @@ static struct snd_soc_dai_link cnl_rt274_msic_dailink[] = { static int cnl_add_dai_link(struct snd_soc_card *card, struct snd_soc_dai_link *link) { - link->platform_name = pname; - link->nonatomic = 1; + struct cnl_rt274_private *ctx = snd_soc_card_get_drvdata(card); + char hdmi_dai_name[CNL_NAME_SIZE]; + struct cnl_hdmi_pcm *pcm; + + link->platform_name = pname; + link->nonatomic = 1; + + /* Assuming HDMI dai link will consist the string "HDMI" */ + if (strstr(link->name, "HDMI")) { + static int i = 1; /* hdmi codec dai name starts from index 1 */ + + pcm = devm_kzalloc(card->dev, sizeof(*pcm), GFP_KERNEL); + if (!pcm) + return -ENOMEM; - return 0; + snprintf(hdmi_dai_name, sizeof(hdmi_dai_name), "intel-hdmi-hifi%d", i++); + pcm->codec_dai = cnl_get_codec_dai(card, hdmi_dai_name); + if (!pcm->codec_dai) + return -EINVAL; + + pcm->device = ctx->pcm_count; + list_add_tail(&pcm->head, &ctx->hdmi_pcm_list); + } + ctx->pcm_count++; + + return 0; +} + +static int cnl_card_late_probe(struct snd_soc_card *card) +{ + struct cnl_rt274_private *ctx = snd_soc_card_get_drvdata(card); + struct snd_soc_component *component = NULL; + char jack_name[CNL_NAME_SIZE]; + struct cnl_hdmi_pcm *pcm; + int err, i = 0; + + if (list_empty(&ctx->hdmi_pcm_list)) + return 0; + + list_for_each_entry(pcm, &ctx->hdmi_pcm_list, head) { + component = pcm->codec_dai->component; + snprintf(jack_name, sizeof(jack_name), + "HDMI/DP, pcm=%d Jack", pcm->device); + err = snd_soc_card_jack_new(card, jack_name, + SND_JACK_AVOUT, &cnl_hdmi[i], + NULL, 0); + if (err) + return err; + + err = hdac_hdmi_jack_init(pcm->codec_dai, + pcm->device, &cnl_hdmi[i]); + if (err < 0) + return err; + + i++; + } + + if (!component) + return -EINVAL; + + return hdac_hdmi_jack_port_init(component, &card->dapm); } /* SoC card */ @@ -344,11 +454,24 @@ static struct snd_soc_card snd_soc_card_cnl = { .controls = cnl_controls, .num_controls = ARRAY_SIZE(cnl_controls), .add_dai_link = cnl_add_dai_link, + .fully_routed = true, + .late_probe = cnl_card_late_probe, }; static int snd_cnl_rt274_mc_probe(struct platform_device *pdev) { + struct cnl_rt274_private *ctx; + + ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_KERNEL); + if (!ctx) + return -ENOMEM; + + ctx->pcm_count = ARRAY_SIZE(cnl_rt274_msic_dailink); + INIT_LIST_HEAD(&ctx->hdmi_pcm_list); + snd_soc_card_cnl.dev = &pdev->dev; + snd_soc_card_set_drvdata(&snd_soc_card_cnl, ctx); + return devm_snd_soc_register_card(&pdev->dev, &snd_soc_card_cnl); } From fa7979b5bec8737fb0bcf289093299f4a935c4f6 Mon Sep 17 00:00:00 2001 From: Mousumi Jana Date: Tue, 4 Jul 2017 18:39:18 +0530 Subject: [PATCH 0785/1103] ASoC: Intel: Skylake: Support for DSP exception record dump In the cases where the DSP encounters an exception during its execution, the record is stored in the FW registers window, aligned towards the end. This data is read by the driver and is passed to the user space using the linux coredump framework The record contains data on a per core basis is dumped to the userspace in scenario - when DSP sends an EXCEPTION_CAUGHT IPC Change-Id: I1d2ac3bca545db7fe5d13c1a0d6ab850da4d7984 Signed-off-by: Mousumi Jana Signed-off-by: Giribabu Gogineni --- sound/soc/intel/Kconfig | 1 + sound/soc/intel/skylake/skl-sst-ipc.c | 17 +++- sound/soc/intel/skylake/skl-sst-ipc.h | 13 --- sound/soc/intel/skylake/skl-sst-utils.c | 114 +++++++++++++++++++++++- sound/soc/intel/skylake/skl-topology.h | 1 + 5 files changed, 131 insertions(+), 15 deletions(-) diff --git a/sound/soc/intel/Kconfig b/sound/soc/intel/Kconfig index 9d2be10e484c..c08d87821c9f 100644 --- a/sound/soc/intel/Kconfig +++ b/sound/soc/intel/Kconfig @@ -115,6 +115,7 @@ config SND_SOC_INTEL_SKYLAKE select SND_SOC_ACPI_INTEL_MATCH select SDW select SDW_CNL + select WANT_DEV_COREDUMP help If you have a Intel Skylake/Broxton/ApolloLake/KabyLake/ GeminiLake or CannonLake platform with the DSP enabled in the BIOS diff --git a/sound/soc/intel/skylake/skl-sst-ipc.c b/sound/soc/intel/skylake/skl-sst-ipc.c index 14802ab7ea20..cfdf2ce3733b 100644 --- a/sound/soc/intel/skylake/skl-sst-ipc.c +++ b/sound/soc/intel/skylake/skl-sst-ipc.c @@ -21,6 +21,7 @@ #include "skl-sst-ipc.h" #include "skl-fwlog.h" #include "sound/hdaudio_ext.h" +#include "skl-topology.h" #define IPC_IXC_STATUS_BITS 24 @@ -271,7 +272,9 @@ enum skl_ipc_notification_type { IPC_GLB_NOTIFY_RESOURCE_EVENT = 5, IPC_GLB_NOTIFY_LOG_BUFFER_STATUS = 6, IPC_GLB_NOTIFY_TIMESTAMP_CAPTURED = 7, - IPC_GLB_NOTIFY_FW_READY = 8 + IPC_GLB_NOTIFY_FW_READY = 8, + IPC_GLB_NOTIFY_FW_AUD_CLASS_RESULT = 9, + IPC_GLB_NOTIFY_EXCEPTION_CAUGHT = 10 }; /* Module Message Types */ @@ -406,6 +409,7 @@ int skl_ipc_process_notification(struct sst_generic_ipc *ipc, struct skl_ipc_header header) { struct skl_sst *skl = container_of(ipc, struct skl_sst, ipc); + int ret; if (IPC_GLB_NOTIFY_MSG_TYPE(header.primary)) { switch (IPC_GLB_NOTIFY_TYPE(header.primary)) { @@ -440,6 +444,17 @@ int skl_ipc_process_notification(struct sst_generic_ipc *ipc, skl->enable_miscbdcge(ipc->dev, false); skl->miscbdcg_disabled = true; break; + case IPC_GLB_NOTIFY_EXCEPTION_CAUGHT: + dev_err(ipc->dev, "*****Exception Detected **********\n"); + /* hexdump of the fw core exception record reg */ + ret = skl_dsp_crash_dump_read(skl); + if (ret < 0) { + dev_err(ipc->dev, + "dsp crash dump read fail:%d\n", ret); + return ret; + } + break; + default: dev_err(ipc->dev, "ipc: Unhandled error msg=%x\n", diff --git a/sound/soc/intel/skylake/skl-sst-ipc.h b/sound/soc/intel/skylake/skl-sst-ipc.h index d6866bc15469..ad437be40334 100644 --- a/sound/soc/intel/skylake/skl-sst-ipc.h +++ b/sound/soc/intel/skylake/skl-sst-ipc.h @@ -375,19 +375,6 @@ struct sw_version { u16 build; } __packed; -struct skl_dsp_core_dump { - u16 type0; - u16 length0; - u32 crash_dump_ver; - u16 bus_dev_id; - u16 cavs_hw_version; - struct fw_version fw_ver; - struct sw_version sw_ver; - u16 type2; - u16 length2; - u32 fwreg[FW_REG_SZ]; -} __packed; - struct skl_module_notify { u32 unique_id; u32 event_id; diff --git a/sound/soc/intel/skylake/skl-sst-utils.c b/sound/soc/intel/skylake/skl-sst-utils.c index 7288893ce61a..8f4e3074de17 100644 --- a/sound/soc/intel/skylake/skl-sst-utils.c +++ b/sound/soc/intel/skylake/skl-sst-utils.c @@ -16,6 +16,8 @@ #include #include #include +#include +#include #include "skl-sst-dsp.h" #include "../common/sst-dsp.h" #include "../common/sst-dsp-priv.h" @@ -23,7 +25,11 @@ #define UUID_STR_SIZE 37 - +#define TYPE0_EXCEPTION 0 +#define TYPE1_EXCEPTION 1 +#define TYPE2_EXCEPTION 2 +#define MAX_CRASH_DATA_TYPES 3 +#define CRASH_DUMP_VERSION 0x1 /* FW Extended Manifest Header id = $AE1 */ #define SKL_EXT_MANIFEST_HEADER_MAGIC 0x31454124 @@ -126,6 +132,31 @@ struct skl_ext_manifest_hdr { u32 entries; }; +struct adsp_crash_hdr { + u16 type; + u16 length; + char data[0]; +} __packed; + +struct adsp_type0_crash_data { + u32 crash_dump_ver; + u16 bus_dev_id; + u16 cavs_hw_version; + struct fw_version fw_ver; + struct sw_version sw_ver; +} __packed; + +struct adsp_type1_crash_data { + u32 mod_uuid[4]; + u32 hash[2]; + u16 mod_id; + u16 rsvd; +} __packed; + +struct adsp_type2_crash_data { + u32 fwreg[FW_REG_SZ]; +} __packed; + static int skl_get_pvtid_map(struct uuid_module *module, int instance_id) { int pvt_id; @@ -261,6 +292,87 @@ int skl_put_pvt_id(struct skl_sst *ctx, uuid_le *uuid_mod, int *pvt_id) } EXPORT_SYMBOL_GPL(skl_put_pvt_id); + + +int skl_dsp_crash_dump_read(struct skl_sst *ctx, int stack_size) +{ + int num_mod = 0, size_core_dump, sz_ext_dump = 0, idx = 0; + struct uuid_module *module, *module1; + void *coredump, *ext_core_dump; + void *fw_reg_addr, *offset; + struct pci_dev *pci = to_pci_dev(ctx->dsp->dev); + u16 length0, length1, length2; + struct adsp_crash_hdr *crash_data_hdr; + struct adsp_type0_crash_data *type0_data; + struct adsp_type1_crash_data *type1_data; + struct adsp_type2_crash_data *type2_data; + + if (list_empty(&ctx->uuid_list)) + dev_info(ctx->dev, "Module list is empty\n"); + + list_for_each_entry(module1, &ctx->uuid_list, list) { + num_mod++; + } + + /* Length representing in DWORD */ + length0 = sizeof(*type0_data) / sizeof(u32); + length1 = (num_mod * sizeof(*type1_data)) / sizeof(u32); + length2 = sizeof(*type2_data) / sizeof(u32); + + /* type1 data size is calculated based on number of modules */ + size_core_dump = (MAX_CRASH_DATA_TYPES * sizeof(*crash_data_hdr)) + + sizeof(*type0_data) + (num_mod * sizeof(*type1_data)) + + sizeof(*type2_data); + + coredump = vzalloc(size_core_dump); + if (!coredump) + return -ENOMEM; + + offset = coredump; + + /* Fill type0 header and data */ + crash_data_hdr = (struct adsp_crash_hdr *) offset; + crash_data_hdr->type = TYPE0_EXCEPTION; + crash_data_hdr->length = length0; + offset += sizeof(*crash_data_hdr); + type0_data = (struct adsp_type0_crash_data *) offset; + type0_data->crash_dump_ver = CRASH_DUMP_VERSION; + type0_data->bus_dev_id = pci->device; + offset += sizeof(*type0_data); + + /* Fill type1 header and data */ + crash_data_hdr = (struct adsp_crash_hdr *) offset; + crash_data_hdr->type = TYPE1_EXCEPTION; + crash_data_hdr->length = length1; + offset += sizeof(*crash_data_hdr); + type1_data = (struct adsp_type1_crash_data *) offset; + list_for_each_entry(module, &ctx->uuid_list, list) { + memcpy(type1_data->mod_uuid, &(module->uuid), + (sizeof(type1_data->mod_uuid))); + memcpy(type1_data->hash, &(module->hash), + (sizeof(type1_data->hash))); + memcpy(&type1_data->mod_id, &(module->id), + (sizeof(type1_data->mod_id))); + type1_data++; + } + offset += (num_mod * sizeof(*type1_data)); + + /* Fill type2 header and data */ + crash_data_hdr = (struct adsp_crash_hdr *) offset; + crash_data_hdr->type = TYPE2_EXCEPTION; + crash_data_hdr->length = length2; + offset += sizeof(*crash_data_hdr); + type2_data = (struct adsp_type2_crash_data *) offset; + fw_reg_addr = (void __force*)(ctx->dsp->mailbox.in_base - + ctx->dsp->addr.w0_stat_sz); + memcpy_fromio(type2_data->fwreg, (const void __iomem *)fw_reg_addr, + sizeof(*type2_data)); + + dev_coredumpv(ctx->dsp->dev, coredump, + size_core_dump, GFP_KERNEL); + return 0; +} + /* * Parse the firmware binary to get the UUID, module id * and loadable flags diff --git a/sound/soc/intel/skylake/skl-topology.h b/sound/soc/intel/skylake/skl-topology.h index 8dbe73ee3d59..b3a15ed76122 100644 --- a/sound/soc/intel/skylake/skl-topology.h +++ b/sound/soc/intel/skylake/skl-topology.h @@ -613,4 +613,5 @@ int skl_tplg_dsp_log_set(struct snd_kcontrol *kcontrol, int skl_dai_load(struct snd_soc_component *cmp, struct snd_soc_dai_driver *pcm_dai); +int skl_dsp_crash_dump_read(struct skl_sst *ctx); #endif From 1a72638773af15e097979ab7b07bc3a540d2a046 Mon Sep 17 00:00:00 2001 From: Dronamraju Santosh P K Date: Tue, 25 Jul 2017 08:51:27 +0530 Subject: [PATCH 0786/1103] ASoC: Intel: board: Separate out icl_rt274 from cnl_rt274 Since HDMI is enabled on cnl_rt274 machine driver and HDMI codec is not available with ICL FPGA, another machine driver for ICL is created without HDMI. Change-Id: Ia975415cafad536832d3383ed3e8c4314bf0d308 Signed-off-by: Dronamraju Santosh P K Signed-off-by: Guneshwor Singh --- sound/soc/intel/boards/Kconfig | 11 + sound/soc/intel/boards/Makefile | 2 + sound/soc/intel/boards/cnl_rt274.c | 2 - sound/soc/intel/boards/icl_rt274.c | 373 +++++++++++++++++++++++++++++ 4 files changed, 386 insertions(+), 2 deletions(-) create mode 100644 sound/soc/intel/boards/icl_rt274.c diff --git a/sound/soc/intel/boards/Kconfig b/sound/soc/intel/boards/Kconfig index d8b639c426fa..e655978d3418 100644 --- a/sound/soc/intel/boards/Kconfig +++ b/sound/soc/intel/boards/Kconfig @@ -340,6 +340,17 @@ config SND_SOC_INTEL_CNL_RT274_MACH Say Y or m if you have such a device. This is a recommended option. If unsure select "N". +config SND_SOC_INTEL_ICL_RT274_MACH + tristate "Icelake with RT274 I2S mode" + depends on MFD_INTEL_LPSS && I2C && ACPI + select SND_SOC_RT274 + select SND_SOC_DMIC + help + This adds support for ASoC machine driver for Icelake platform with + RT274 I2S audio codec. + Say Y or m if you have such a device. This is a recommended option. + If unsure select "N". + config SND_SOC_INTEL_BXT_TDF8532_MACH tristate "Broxton with TDF8532 I2S mode" depends on MFD_INTEL_LPSS && I2C && ACPI diff --git a/sound/soc/intel/boards/Makefile b/sound/soc/intel/boards/Makefile index dcd898c0b4a2..239051cc6f22 100644 --- a/sound/soc/intel/boards/Makefile +++ b/sound/soc/intel/boards/Makefile @@ -24,6 +24,7 @@ snd-soc-skl_rt286-objs := skl_rt286.o snd-skl_nau88l25_max98357a-objs := skl_nau88l25_max98357a.o snd-soc-skl_nau88l25_ssm4567-objs := skl_nau88l25_ssm4567.o snd-soc-cnl-rt274-objs := cnl_rt274.o +snd-soc-icl-rt274-objs := icl_rt274.o snd-soc-cnl_cs42l42-objs := cnl_cs42l42.o snd-soc-cnl_rt700-objs := cnl_rt700.o snd-soc-cnl_svfpga-objs := cnl_svfpga.o @@ -53,6 +54,7 @@ obj-$(CONFIG_SND_SOC_INTEL_SKL_RT286_MACH) += snd-soc-skl_rt286.o obj-$(CONFIG_SND_SOC_INTEL_SKL_NAU88L25_MAX98357A_MACH) += snd-skl_nau88l25_max98357a.o obj-$(CONFIG_SND_SOC_INTEL_SKL_NAU88L25_SSM4567_MACH) += snd-soc-skl_nau88l25_ssm4567.o obj-$(CONFIG_SND_SOC_INTEL_CNL_RT274_MACH) += snd-soc-cnl-rt274.o +obj-$(CONFIG_SND_SOC_INTEL_ICL_RT274_MACH) += snd-soc-icl-rt274.o obj-$(CONFIG_SND_SOC_INTEL_CNL_CS42L42_MACH) += snd-soc-cnl_cs42l42.o obj-$(CONFIG_SND_SOC_INTEL_CNL_RT700_MACH) += snd-soc-cnl_rt700.o obj-$(CONFIG_SND_SOC_INTEL_CNL_SVFPGA_MACH) += snd-soc-cnl_svfpga.o diff --git a/sound/soc/intel/boards/cnl_rt274.c b/sound/soc/intel/boards/cnl_rt274.c index e9e5959e256a..07a876f4f3b2 100644 --- a/sound/soc/intel/boards/cnl_rt274.c +++ b/sound/soc/intel/boards/cnl_rt274.c @@ -477,7 +477,6 @@ static int snd_cnl_rt274_mc_probe(struct platform_device *pdev) static const struct platform_device_id cnl_board_ids[] = { { .name = "cnl_rt274" }, - { .name = "icl_rt274" }, { } }; @@ -495,4 +494,3 @@ module_platform_driver(snd_cnl_rt274_driver); MODULE_AUTHOR("Guneshwor Singh "); MODULE_LICENSE("GPL v2"); MODULE_ALIAS("platform:cnl_rt274"); -MODULE_ALIAS("platform:icl_rt274"); diff --git a/sound/soc/intel/boards/icl_rt274.c b/sound/soc/intel/boards/icl_rt274.c new file mode 100644 index 000000000000..f4d855766b81 --- /dev/null +++ b/sound/soc/intel/boards/icl_rt274.c @@ -0,0 +1,373 @@ +/* + * icl_rt274.c - ASOC Machine driver for ICL + * + * Copyright (C) 2016 Intel Corp + * Author: Dronamraju Santosh Pavan Kumar + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * 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; version 2 of the License. + * + * 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. + * + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../../codecs/rt274.h" + +#define ICL_FREQ_OUT 19200000 +#define ICL_BE_FIXUP_RATE 48000 +#define RT274_CODEC_DAI "rt274-aif1" + +static struct snd_soc_dai *icl_get_codec_dai(struct snd_soc_card *card, + const char *dai_name) +{ + struct snd_soc_pcm_runtime *rtd; + + list_for_each_entry(rtd, &card->rtd_list, list) { + if (!strcmp(rtd->codec_dai->name, dai_name)) + return rtd->codec_dai; + } + + return NULL; +} + +static int icl_rt274_clock_control(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *k, int event) +{ + struct snd_soc_dapm_context *dapm = w->dapm; + struct snd_soc_card *card = dapm->card; + int ret = 0, ratio = 100; + struct snd_soc_dai *codec_dai = icl_get_codec_dai(card, + RT274_CODEC_DAI); + + /* Codec needs clock for Jack detection and button press */ + ret = snd_soc_dai_set_sysclk(codec_dai, RT274_SCLK_S_PLL2, + ICL_FREQ_OUT, SND_SOC_CLOCK_IN); + if (ret < 0) { + dev_err(codec_dai->dev, "set codec sysclk failed: %d\n", ret); + return ret; + } + + if (SND_SOC_DAPM_EVENT_ON(event)) { + snd_soc_dai_set_bclk_ratio(codec_dai, ratio); + + ret = snd_soc_dai_set_pll(codec_dai, 0, RT274_PLL2_S_BCLK, + ICL_BE_FIXUP_RATE * ratio, + ICL_FREQ_OUT); + if (ret) { + dev_err(codec_dai->dev, + "failed to enable PLL2: %d\n", ret); + return ret; + } + } + + return ret; +} + +static struct snd_soc_jack icl_headset; + +/* Headset jack detection DAPM pins */ +static struct snd_soc_jack_pin icl_headset_pins[] = { + { + .pin = "Mic Jack", + .mask = SND_JACK_MICROPHONE, + }, + { + .pin = "Headphone Jack", + .mask = SND_JACK_HEADPHONE, + }, +}; + +static const struct snd_kcontrol_new icl_controls[] = { + SOC_DAPM_PIN_SWITCH("Headphone Jack"), + SOC_DAPM_PIN_SWITCH("Mic Jack"), +}; + +static const struct snd_soc_dapm_widget icl_rt274_widgets[] = { + SND_SOC_DAPM_HP("Headphone Jack", NULL), + SND_SOC_DAPM_MIC("Mic Jack", NULL), + SND_SOC_DAPM_MIC("SoC DMIC", NULL), + SND_SOC_DAPM_SUPPLY("Platform Clock", SND_SOC_NOPM, 0, 0, + icl_rt274_clock_control, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMD), +}; + +static const struct snd_soc_pcm_stream dai_params_codec = { + .formats = SNDRV_PCM_FMTBIT_S24_LE, + .rate_min = 48000, + .rate_max = 48000, + .channels_min = 2, + .channels_max = 2, +}; + +static int icl_dmic_fixup(struct snd_soc_pcm_runtime *rtd, + struct snd_pcm_hw_params *params) +{ + struct snd_interval *channels = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_CHANNELS); + channels->min = channels->max = 2; + + return 0; +} + +static const struct snd_soc_dapm_route icl_map[] = { + {"Headphone Jack", NULL, "HPO Pin"}, + {"MIC", NULL, "Mic Jack"}, + {"DMic", NULL, "SoC DMIC"}, + {"DMIC01 Rx", NULL, "Capture"}, + {"dmic01_hifi", NULL, "DMIC01 Rx"}, + + /* ssp2 path */ + {"Dummy Playback", NULL, "ssp2 Tx"}, + {"ssp2 Tx", NULL, "ssp2_out"}, + + {"ssp2 Rx", NULL, "Dummy Capture"}, + {"ssp2_in", NULL, "ssp2 Rx"}, + + /* ssp1 path */ + {"Dummy Playback", NULL, "ssp1 Tx"}, + {"ssp1 Tx", NULL, "ssp1_out"}, + + {"AIF1 Playback", NULL, "ssp0 Tx"}, + {"ssp0 Tx", NULL, "codec1_out"}, + {"ssp0 Tx", NULL, "codec0_out"}, + + {"ssp0 Rx", NULL, "AIF1 Capture"}, + {"codec0_in", NULL, "ssp0 Rx"}, + + {"Headphone Jack", NULL, "Platform Clock"}, + {"MIC", NULL, "Platform Clock"}, +}; + +static int icl_rt274_init(struct snd_soc_pcm_runtime *runtime) +{ + int ret; + struct snd_soc_component *component = runtime->codec_dai->component; + struct snd_soc_card *card = runtime->card; + struct snd_soc_dai *codec_dai = runtime->codec_dai; + + ret = snd_soc_card_jack_new(runtime->card, "Headset", + SND_JACK_HEADSET, &icl_headset, + icl_headset_pins, ARRAY_SIZE(icl_headset_pins)); + + if (ret) + return ret; + + snd_soc_component_set_jack(component, &icl_headset, NULL); + + /* TDM 4 slots 24 bit, set Rx & Tx bitmask to 4 active slots */ + ret = snd_soc_dai_set_tdm_slot(codec_dai, 0xF, 0xF, 4, 24); + if (ret < 0) { + dev_err(runtime->dev, "can't set codec pcm format %d\n", ret); + return ret; + } + + card->dapm.idle_bias_off = true; + + return 0; +} + +static int icl_be_fixup(struct snd_soc_pcm_runtime *rtd, + struct snd_pcm_hw_params *params) +{ + struct snd_interval *rate = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_RATE); + struct snd_interval *channels = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_CHANNELS); + + rate->min = rate->max = ICL_BE_FIXUP_RATE; + channels->min = channels->max = 2; + snd_mask_none(hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT)); + snd_mask_set(hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT), + (unsigned int __force)SNDRV_PCM_FORMAT_S24_LE); + + return 0; +} + +#if IS_ENABLED(CONFIG_SND_SOC_INTEL_CNL_FPGA) +static const char pname[] = "0000:02:18.0"; +static const char cname[] = "rt274.0-001c"; +#else +static const char pname[] = "0000:00:1f.3"; +static const char cname[] = "i2c-INT34C2:00"; +#endif + +static struct snd_soc_dai_link icl_rt274_msic_dailink[] = { + /* Trace Buffer DAI links */ + { + .name = "ICL Trace Buffer0", + .stream_name = "Core 0 Trace Buffer", + .cpu_dai_name = "TraceBuffer0 Pin", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .platform_name = pname, + .capture_only = true, + .ignore_suspend = 1, + }, + { + .name = "ICL Trace Buffer1", + .stream_name = "Core 1 Trace Buffer", + .cpu_dai_name = "TraceBuffer1 Pin", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .platform_name = pname, + .capture_only = true, + .ignore_suspend = 1, + }, + { + .name = "ICL Trace Buffer2", + .stream_name = "Core 2 Trace Buffer", + .cpu_dai_name = "TraceBuffer2 Pin", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .platform_name = pname, + .capture_only = true, + .ignore_suspend = 1, + }, + { + .name = "ICL Trace Buffer3", + .stream_name = "Core 3 Trace Buffer", + .cpu_dai_name = "TraceBuffer3 Pin", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .platform_name = pname, + .capture_only = true, + .ignore_suspend = 1, + }, + /* Probe DAI-links */ + { + .name = "ICL Compress Probe playback", + .stream_name = "Probe Playback", + .cpu_dai_name = "Compress Probe0 Pin", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .platform_name = pname, + .init = NULL, + .ignore_suspend = 1, + .nonatomic = 1, + }, + { + .name = "ICL Compress Probe capture", + .stream_name = "Probe Capture", + .cpu_dai_name = "Compress Probe1 Pin", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .platform_name = pname, + .init = NULL, + .ignore_suspend = 1, + .nonatomic = 1, + }, + /* back ends */ + { + .name = "SSP0-Codec", + .id = 1, + .cpu_dai_name = "SSP0 Pin", + .codec_name = cname, + .codec_dai_name = "rt274-aif1", + .platform_name = pname, + .be_hw_params_fixup = icl_be_fixup, + .ignore_suspend = 1, + .no_pcm = 1, + .dai_fmt = SND_SOC_DAIFMT_DSP_A | + SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS, + .dpcm_playback = 1, + .dpcm_capture = 1, + .init = icl_rt274_init, + }, + { + .name = "dmic01", + .id = 2, + .cpu_dai_name = "DMIC01 Pin", + .codec_name = "dmic-codec", + .codec_dai_name = "dmic-hifi", + .platform_name = pname, + .ignore_suspend = 1, + .no_pcm = 1, + .dpcm_capture = 1, + .be_hw_params_fixup = icl_dmic_fixup, + }, + /* codec-codec link */ + { + .name = "ICL SSP0-Loop Port", + .stream_name = "ICL SSP0-Loop", + .cpu_dai_name = "SSP0 Pin", + .platform_name = pname, + .codec_name = cname, + .codec_dai_name = "rt274-aif1", + .params = &dai_params_codec, + .dsp_loopback = true, + .dai_fmt = SND_SOC_DAIFMT_DSP_A | + SND_SOC_DAIFMT_NB_NF | + SND_SOC_DAIFMT_CBS_CFS, + }, +}; + +static int +icl_add_dai_link(struct snd_soc_card *card, struct snd_soc_dai_link *link) +{ + link->platform_name = pname; + link->nonatomic = 1; + + return 0; +} + +/* SoC card */ +static struct snd_soc_card snd_soc_card_icl = { + .name = "icl-audio", + .dai_link = icl_rt274_msic_dailink, + .num_links = ARRAY_SIZE(icl_rt274_msic_dailink), + .dapm_widgets = icl_rt274_widgets, + .num_dapm_widgets = ARRAY_SIZE(icl_rt274_widgets), + .dapm_routes = icl_map, + .num_dapm_routes = ARRAY_SIZE(icl_map), + .controls = icl_controls, + .num_controls = ARRAY_SIZE(icl_controls), + .add_dai_link = icl_add_dai_link, +}; + +static int snd_icl_rt274_mc_probe(struct platform_device *pdev) +{ + snd_soc_card_icl.dev = &pdev->dev; + return devm_snd_soc_register_card(&pdev->dev, &snd_soc_card_icl); +} + +static const struct platform_device_id icl_board_ids[] = { + { .name = "icl_rt274" }, + { } +}; + +static struct platform_driver snd_icl_rt274_driver = { + .driver = { + .name = "icl_rt274", + .pm = &snd_soc_pm_ops, + }, + .probe = snd_icl_rt274_mc_probe, + .id_table = icl_board_ids, +}; + +module_platform_driver(snd_icl_rt274_driver); + +MODULE_AUTHOR("Dronamraju Santosh pavan Kumar "); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:icl_rt274"); From 718da5abf4169fee65b5f8a6c22614255fc9f48c Mon Sep 17 00:00:00 2001 From: "Sinha, Mohit" Date: Fri, 4 Aug 2017 16:06:57 +0530 Subject: [PATCH 0787/1103] ASoC: Intel: Skylake: Removed duplicate IPC call for Probe Injector DMA Removed duplicate IPC call for attaching DMA for Probe Injector. Change-Id: I12d8bd73ba5203a697cdbe1caee0747eb16344b1 Signed-off-by: Mohit Sinha Reviewed-on: Reviewed-by: Koul, Vinod Reviewed-by: Shaik, Kareem M Reviewed-by: Gogineni, GiribabuX Reviewed-by: Babu, Ramesh Tested-by: Sm, Bhadur A --- sound/soc/intel/skylake/skl-topology.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/sound/soc/intel/skylake/skl-topology.c b/sound/soc/intel/skylake/skl-topology.c index 24c08dca8bd5..29be5b7da3ba 100644 --- a/sound/soc/intel/skylake/skl-topology.c +++ b/sound/soc/intel/skylake/skl-topology.c @@ -544,9 +544,6 @@ int skl_probe_attach_inj_dma(struct snd_soc_dapm_widget *w, pconfig->iprobe[index].state); } - ret = skl_set_module_params(ctx, (u32 *)&ad, - sizeof(struct skl_probe_attach_inj_dma), - 1, mconfig); return ret; } From 6793f1adcc4249017c6c20203959396e117f4800 Mon Sep 17 00:00:00 2001 From: Mousumi Jana Date: Thu, 20 Jul 2017 16:39:00 +0530 Subject: [PATCH 0788/1103] ASoC: Intel: Skylake: Notify topology changes Some events like pipeline start, pipeline delete, DSP D0/D3 need to be notified to the user in order to convey a change in the topology. Support for notifying such events has been add using kcontrol. This kcontrol reports time at which the last change occurred in the topology. Change-Id: I3745a5a6d7034cb95bea13ba47f8d6eaf76f5a43 Signed-off-by: Giribabu Gogineni Signed-off-by: Mousumi Jana Reviewed-on: Reviewed-by: Shaik, Kareem M Reviewed-by: Singh, Guneshwor O Reviewed-by: Nc, Shreyas Reviewed-by: Kale, Sanyog R Reviewed-by: Koul, Vinod Tested-by: Sm, Bhadur A --- include/uapi/sound/snd_sst_tokens.h | 16 ++++++ sound/soc/intel/skylake/bxt-sst.c | 10 ++++ sound/soc/intel/skylake/skl-messages.c | 8 +++ sound/soc/intel/skylake/skl-pcm.c | 10 ++++ sound/soc/intel/skylake/skl-sst-dsp.h | 16 ++++++ sound/soc/intel/skylake/skl-sst-ipc.h | 6 +++ sound/soc/intel/skylake/skl-sst-utils.c | 19 +++++++ sound/soc/intel/skylake/skl-topology.c | 72 +++++++++++++++++++++++++ sound/soc/intel/skylake/skl-topology.h | 3 ++ 9 files changed, 160 insertions(+) diff --git a/include/uapi/sound/snd_sst_tokens.h b/include/uapi/sound/snd_sst_tokens.h index 7c0149476820..0f74eeb85995 100644 --- a/include/uapi/sound/snd_sst_tokens.h +++ b/include/uapi/sound/snd_sst_tokens.h @@ -350,4 +350,20 @@ enum SKL_TKNS { SKL_TKN_MAX = SKL_TKN_U32_DMACTRL_CFG_SIZE, }; +/* + * Topology change notification events along with time at which + * the change occurred in topology. + */ +enum skl_event_type { + SKL_TPLG_CHG_NOTIFY_PIPELINE_START = 1, + SKL_TPLG_CHG_NOTIFY_PIPELINE_DELETE, + SKL_TPLG_CHG_NOTIFY_DSP_D0, + SKL_TPLG_CHG_NOTIFY_DSP_D3, +}; + +struct skl_tcn_events { + enum skl_event_type type; + struct timeval tv; +}; + #endif diff --git a/sound/soc/intel/skylake/bxt-sst.c b/sound/soc/intel/skylake/bxt-sst.c index 9c8d923fe358..c7f7c1529354 100644 --- a/sound/soc/intel/skylake/bxt-sst.c +++ b/sound/soc/intel/skylake/bxt-sst.c @@ -498,6 +498,11 @@ static int bxt_set_dsp_D0(struct sst_dsp *ctx, unsigned int core_id) } skl->cores.state[core_id] = SKL_DSP_RUNNING; + ret = skl_notify_tplg_change(skl, SKL_TPLG_CHG_NOTIFY_DSP_D0); + if (ret < 0) + dev_warn(ctx->dev, + "update of topology event D0 failed\n"); + return 0; err: if (core_id == SKL_DSP_CORE0_ID) @@ -544,6 +549,11 @@ static int bxt_set_dsp_D3(struct sst_dsp *ctx, unsigned int core_id) return ret; } skl->cores.state[core_id] = SKL_DSP_RESET; + ret = skl_notify_tplg_change(skl, SKL_TPLG_CHG_NOTIFY_DSP_D3); + if (ret < 0) + dev_warn(ctx->dev, + "update of topology event D3 failed\n"); + return 0; } diff --git a/sound/soc/intel/skylake/skl-messages.c b/sound/soc/intel/skylake/skl-messages.c index 1fd9a3afe9a9..8560cd65e77c 100644 --- a/sound/soc/intel/skylake/skl-messages.c +++ b/sound/soc/intel/skylake/skl-messages.c @@ -2479,6 +2479,10 @@ int skl_delete_pipe(struct skl_sst *ctx, struct skl_pipe *pipe) pipe->state = SKL_PIPE_INVALID; skl_dbg_event(ctx, pipe->state); + ret = skl_notify_tplg_change(ctx, SKL_TPLG_CHG_NOTIFY_PIPELINE_DELETE); + if (ret < 0) + dev_warn(ctx->dev, + "update of topology event delete pipe failed\n"); return ret; } @@ -2514,6 +2518,10 @@ int skl_run_pipe(struct skl_sst *ctx, struct skl_pipe *pipe) } pipe->state = SKL_PIPE_STARTED; + ret = skl_notify_tplg_change(ctx, SKL_TPLG_CHG_NOTIFY_PIPELINE_START); + if (ret < 0) + dev_warn(ctx->dev, + "update of topology event run pipe failed\n"); return 0; } diff --git a/sound/soc/intel/skylake/skl-pcm.c b/sound/soc/intel/skylake/skl-pcm.c index 7fda668d9fc9..8209c954b7ad 100644 --- a/sound/soc/intel/skylake/skl-pcm.c +++ b/sound/soc/intel/skylake/skl-pcm.c @@ -967,6 +967,10 @@ static struct snd_soc_dai_ops skl_sdw_dai_ops = { .shutdown = skl_sdw_shutdown, }; +struct skl_dsp_notify_ops cb_ops = { + .notify_cb = skl_dsp_cb_event, +}; + static struct snd_soc_dai_driver skl_fe_dai[] = { { .name = "System Pin", @@ -1821,6 +1825,8 @@ static int skl_platform_soc_probe(struct snd_soc_component *component) return ret; } + skl->component = component; + /* load the firmwares, since all is set */ ops = skl_get_dsp_ops(skl->pci->device); if (!ops) @@ -1851,6 +1857,7 @@ static int skl_platform_soc_probe(struct snd_soc_component *component) skl_populate_modules(skl); skl->skl_sst->update_d0i3c = skl_update_d0i3c; + skl->skl_sst->notify_ops = cb_ops; skl_dsp_enable_notification(skl->skl_sst, false); if (skl->cfg.astate_cfg != NULL) { @@ -1879,6 +1886,9 @@ static const struct soc_enum dsp_log_enum = static struct snd_kcontrol_new skl_controls[] = { SOC_ENUM_EXT("DSP Log Level", dsp_log_enum, skl_tplg_dsp_log_get, skl_tplg_dsp_log_set), + SND_SOC_BYTES_TLV("Topology Change Notification", + sizeof(struct skl_tcn_events), skl_tplg_change_notification_get, + NULL), }; static const struct snd_soc_component_driver skl_component = { diff --git a/sound/soc/intel/skylake/skl-sst-dsp.h b/sound/soc/intel/skylake/skl-sst-dsp.h index baec42299419..6736c89c6520 100644 --- a/sound/soc/intel/skylake/skl-sst-dsp.h +++ b/sound/soc/intel/skylake/skl-sst-dsp.h @@ -20,6 +20,7 @@ #include #include #include +#include #include "skl-sst-cldma.h" struct sst_dsp; @@ -236,6 +237,17 @@ struct uuid_module { u8 hash[DEFAULT_HASH_SHA256_LEN]; }; +struct skl_notify_data { + u32 type; + u32 length; + struct skl_tcn_events tcn_data; +}; + +struct skl_dsp_notify_ops { + int (*notify_cb)(struct skl_sst *skl, unsigned int event, + struct skl_notify_data *notify_data); +}; + struct skl_load_module_info { u16 mod_id; const struct firmware *fw; @@ -323,4 +335,8 @@ void bxt_set_dsp_D0i3(struct work_struct *work); int skl_module_sysfs_init(struct skl_sst *ctx, struct kobject *fw_modules_kobj); void skl_module_sysfs_exit(struct skl_sst *ctx); + +int skl_dsp_cb_event(struct skl_sst *ctx, unsigned int event, + struct skl_notify_data *notify_data); + #endif /*__SKL_SST_DSP_H__*/ diff --git a/sound/soc/intel/skylake/skl-sst-ipc.h b/sound/soc/intel/skylake/skl-sst-ipc.h index ad437be40334..bc083b0a0d4f 100644 --- a/sound/soc/intel/skylake/skl-sst-ipc.h +++ b/sound/soc/intel/skylake/skl-sst-ipc.h @@ -29,6 +29,7 @@ struct sst_generic_ipc; #define NO_OF_INJECTOR 6 #define NO_OF_EXTRACTOR 8 #define FW_REG_SZ 1024 +#define SKL_TPLG_CHG_NOTIFY 3 enum skl_ipc_pipeline_state { PPL_INVALID_STATE = 0, @@ -283,6 +284,8 @@ struct skl_sst { /* Callback to update D0i3C register */ void (*update_d0i3c)(struct device *dev, bool enable); + struct skl_dsp_notify_ops notify_ops; + struct skl_d0i3_data d0i3; const struct skl_dsp_ops *dsp_ops; @@ -308,6 +311,8 @@ struct skl_sst { /* sysfs for module info */ struct skl_sysfs_tree *sysfs_tree; + + struct snd_kcontrol *kcontrol; }; struct skl_ipc_init_instance_msg { @@ -457,4 +462,5 @@ int skl_ipc_process_notification(struct sst_generic_ipc *ipc, struct skl_ipc_header header); void skl_ipc_tx_data_copy(struct ipc_message *msg, char *tx_data, size_t tx_size); +int skl_notify_tplg_change(struct skl_sst *ctx, int type); #endif /* __SKL_IPC_H */ diff --git a/sound/soc/intel/skylake/skl-sst-utils.c b/sound/soc/intel/skylake/skl-sst-utils.c index 8f4e3074de17..f20b842af4e4 100644 --- a/sound/soc/intel/skylake/skl-sst-utils.c +++ b/sound/soc/intel/skylake/skl-sst-utils.c @@ -881,6 +881,25 @@ static int skl_parse_fw_config_info(struct sst_dsp *ctx, return ret; } +int skl_notify_tplg_change(struct skl_sst *ctx, int type) +{ + struct skl_notify_data *notify_data; + + notify_data = kzalloc(sizeof(*notify_data), GFP_KERNEL); + if (!notify_data) + return -ENOMEM; + + notify_data->type = 0xFF; + notify_data->length = sizeof(struct skl_tcn_events); + notify_data->tcn_data.type = type; + do_gettimeofday(&(notify_data->tcn_data.tv)); + ctx->notify_ops.notify_cb(ctx, SKL_TPLG_CHG_NOTIFY, notify_data); + kfree(notify_data); + + return 0; +} +EXPORT_SYMBOL_GPL(skl_notify_tplg_change); + int skl_get_firmware_configuration(struct sst_dsp *ctx) { struct skl_ipc_large_config_msg msg; diff --git a/sound/soc/intel/skylake/skl-topology.c b/sound/soc/intel/skylake/skl-topology.c index 29be5b7da3ba..373dbced8efa 100644 --- a/sound/soc/intel/skylake/skl-topology.c +++ b/sound/soc/intel/skylake/skl-topology.c @@ -2642,6 +2642,78 @@ int skl_tplg_be_update_params(struct snd_soc_dai *dai, return 0; } +/* + * Get the events along with data stored in notify_data and pass + * to kcontrol private data. + */ +int skl_dsp_cb_event(struct skl_sst *ctx, unsigned int event, + struct skl_notify_data *notify_data) +{ + struct snd_soc_card *card; + struct soc_bytes_ext *sb; + struct skl *skl = get_skl_ctx(ctx->dev); + struct snd_soc_component *component = skl->component; + struct skl_module_notify *m_notification = NULL; + struct skl_algo_data *bc; + u8 param_length; + + switch (event) { + case SKL_TPLG_CHG_NOTIFY: + card = component->card; + + if (!ctx->kcontrol) { + ctx->kcontrol = snd_soc_card_get_kcontrol(card, + "Topology Change Notification"); + if (!ctx->kcontrol) { + dev_dbg(ctx->dev, + "NOTIFICATION Controls not found\n"); + return -EINVAL; + } + } + + sb = (struct soc_bytes_ext *)ctx->kcontrol->private_value; + if (!sb->dobj.private) { + sb->dobj.private = devm_kzalloc(ctx->dev, + sizeof(*notify_data), GFP_KERNEL); + if (!sb->dobj.private) + return -ENOMEM; + } + + memcpy(sb->dobj.private, notify_data, sizeof(*notify_data)); + snd_ctl_notify(card->snd_card, SNDRV_CTL_EVENT_MASK_VALUE, + &ctx->kcontrol->id); + break; + + default: + return -EINVAL; + } + + return 0; +} + +/* + * Get last topology change events like pipeline start, pipeline delete, + * DSP D0/D3 and notify to user along with time at which last change occurred + * in topology. + */ +int skl_tplg_change_notification_get(struct snd_kcontrol *kcontrol, + unsigned int __user *data, unsigned int size) +{ + struct skl_notify_data *notify_data; + struct soc_bytes_ext *sb = + (struct soc_bytes_ext *)kcontrol->private_value; + + if (sb->dobj.private) { + notify_data = (struct skl_notify_data *)sb->dobj.private; + if (copy_to_user(data, notify_data, sizeof(*notify_data))) + return -EFAULT; + /* Clear the data after copy to user as per requirement */ + memset(notify_data, 0, sizeof(*notify_data)); + } + + return 0; +} + static const struct snd_soc_tplg_widget_events skl_tplg_widget_ops[] = { {SKL_MIXER_EVENT, skl_tplg_mixer_event}, {SKL_VMIXER_EVENT, skl_tplg_mixer_event}, diff --git a/sound/soc/intel/skylake/skl-topology.h b/sound/soc/intel/skylake/skl-topology.h index b3a15ed76122..ce4069607dda 100644 --- a/sound/soc/intel/skylake/skl-topology.h +++ b/sound/soc/intel/skylake/skl-topology.h @@ -614,4 +614,7 @@ int skl_tplg_dsp_log_set(struct snd_kcontrol *kcontrol, int skl_dai_load(struct snd_soc_component *cmp, struct snd_soc_dai_driver *pcm_dai); int skl_dsp_crash_dump_read(struct skl_sst *ctx); + +int skl_tplg_change_notification_get(struct snd_kcontrol *kcontrol, + unsigned int __user *data, unsigned int size); #endif From ff8d7967fb5c187802a9ff7297696414d530b74e Mon Sep 17 00:00:00 2001 From: Anamika Lal Date: Thu, 10 Aug 2017 12:28:20 +0530 Subject: [PATCH 0789/1103] ASoC: rt700: Remove unused variables. Change-Id: Ia975415cafad536832d3383ed3e8c4314bf0d313 Signed-off-by: Anamika Lal Reviewed-on: Reviewed-by: Diwakar, Praveen Reviewed-by: Singh, Guneshwor O Reviewed-by: Koul, Vinod Tested-by: Sm, Bhadur A --- sound/soc/codecs/rt700.c | 19 +++++-------------- 1 file changed, 5 insertions(+), 14 deletions(-) diff --git a/sound/soc/codecs/rt700.c b/sound/soc/codecs/rt700.c index c88aa16a4280..03bab55537b6 100644 --- a/sound/soc/codecs/rt700.c +++ b/sound/soc/codecs/rt700.c @@ -557,8 +557,6 @@ static int rt700_index_read(struct regmap *regmap, static int rt700_hda_read(struct regmap *regmap, unsigned int vid, unsigned int nid, unsigned int pid, unsigned int *value) { - int ret; - unsigned int val_h, val_l; unsigned int sdw_data_3, sdw_data_2, sdw_data_1, sdw_data_0; unsigned int sdw_addr_h, sdw_addr_l; @@ -1108,8 +1106,6 @@ static int rt700_set_bias_level(struct snd_soc_component *component, { struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component); - struct rt700_priv *rt700 = snd_soc_codec_get_drvdata(codec); - unsigned int sdw_data_0; pr_debug("%s level=%d\n", __func__, level); switch (level) { @@ -1411,8 +1407,7 @@ static ssize_t rt700_index_cmd_show(struct device *dev, struct device_attribute *attr, char *buf) { struct rt700_priv *rt700 = dev_get_drvdata(dev); - unsigned int sdw_addr_h, sdw_addr_l; - unsigned int sdw_data_3, sdw_data_2, sdw_data_1, sdw_data_0; + unsigned int sdw_data_0; int i, cnt = 0; /* index */ @@ -1433,8 +1428,7 @@ static ssize_t rt700_index_cmd_store(struct device *dev, const char *buf, size_t count) { struct rt700_priv *rt700 = dev_get_drvdata(dev); - unsigned int sdw_addr_h, sdw_addr_l, sdw_data_h, sdw_data_l; - unsigned int index_reg, index_val; + unsigned int index_reg = 0, index_val = 0; int i; pr_debug("register \"%s\" count=%zu\n", buf, count); @@ -1480,8 +1474,6 @@ static ssize_t rt700_hda_cmd_show(struct device *dev, struct device_attribute *attr, char *buf) { struct rt700_priv *rt700 = dev_get_drvdata(dev); - unsigned int sdw_addr_h, sdw_addr_l; - unsigned int sdw_data_3, sdw_data_2, sdw_data_1, sdw_data_0; int i, cnt = 0; unsigned int value; @@ -1605,9 +1597,9 @@ static DEVICE_ATTR(hda_reg, 0664, rt700_hda_cmd_show, rt700_hda_cmd_store); static int rt700_clock_config(struct device *dev, struct alc700 *alc700) { struct rt700_priv *rt700 = dev_get_drvdata(dev); - int value, read_value1, read_value2; + int value; - switch(alc700->params->bus_clk_freq) { + switch (alc700->params->bus_clk_freq) { case RT700_CLK_FREQ_12000000HZ: value = 0x0; break; @@ -1706,7 +1698,6 @@ int rt700_probe(struct device *dev, struct regmap *regmap, struct rt700_priv *rt700; struct alc700 *alc700 = dev_get_drvdata(dev); int ret; - unsigned int value; rt700 = devm_kzalloc(dev, sizeof(struct rt700_priv), GFP_KERNEL); @@ -1868,7 +1859,7 @@ static int rt700_runtime_resume(struct device *dev) int ret; int timeout = 0; - if(rt700->sdw) { + if (rt700->sdw) { ret = sdw_wait_for_slave_enumeration(rt700->sdw->mstr, rt700->sdw); if (ret < 0) From 3375c0992fd2265f190f0e9ec2a46974ba58f12b Mon Sep 17 00:00:00 2001 From: Anamika Lal Date: Thu, 10 Aug 2017 12:24:43 +0530 Subject: [PATCH 0790/1103] ASoC: rt700: Remove prints used for debugging. While integrating rt700 codec driver prints were added for debugging purpose. Hence removing them. Change-Id: Ia975415cafad536832d3383ed3e8c4314bf0d312 Signed-off-by: Anamika Lal Reviewed-on: Reviewed-by: Diwakar, Praveen Reviewed-by: Singh, Guneshwor O Reviewed-by: Koul, Vinod Tested-by: Sm, Bhadur A --- sound/soc/codecs/rt700-sdw.c | 3 -- sound/soc/codecs/rt700.c | 74 ++---------------------------------- 2 files changed, 3 insertions(+), 74 deletions(-) diff --git a/sound/soc/codecs/rt700-sdw.c b/sound/soc/codecs/rt700-sdw.c index 8a7fbc5a40c7..62f7e2989ca8 100644 --- a/sound/soc/codecs/rt700-sdw.c +++ b/sound/soc/codecs/rt700-sdw.c @@ -364,11 +364,8 @@ static int rt700_sdw_handle_impl_def_interrupts(struct sdw_slv *swdev, struct rt700_priv *rt700 = dev_get_drvdata(&swdev->dev); bool hp, mic; - pr_debug("%s control_port_stat=%x port0_stat=%x\n", __func__, - intr_status->control_port_stat, intr_status->port0_stat); if (intr_status->control_port_stat & 0x4) { rt700_jack_detect(rt700, &hp, &mic); - pr_info("%s hp=%d mic=%d\n", __func__, hp, mic); } return 0; diff --git a/sound/soc/codecs/rt700.c b/sound/soc/codecs/rt700.c index 03bab55537b6..a654c092b8c5 100644 --- a/sound/soc/codecs/rt700.c +++ b/sound/soc/codecs/rt700.c @@ -569,12 +569,10 @@ static int rt700_hda_read(struct regmap *regmap, unsigned int vid, hda_to_sdw(nid, vid, pid, &sdw_addr_h, &sdw_data_1, &sdw_addr_l, &sdw_data_0); - pr_debug("write %04x %02x\n", sdw_addr_h, sdw_data_1); regmap_write(regmap, sdw_addr_h, sdw_data_1); - if (sdw_addr_l) { - pr_debug("write %04x %02x", sdw_addr_l, sdw_data_0); + if (sdw_addr_l) regmap_write(regmap, sdw_addr_l, sdw_data_0); - } + regmap_read(regmap, RT700_READ_HDA_3, &sdw_data_3); regmap_read(regmap, @@ -583,11 +581,6 @@ static int rt700_hda_read(struct regmap *regmap, unsigned int vid, RT700_READ_HDA_1, &sdw_data_1); regmap_read(regmap, RT700_READ_HDA_0, &sdw_data_0); - pr_debug("(%03x %02x %04x) = %02x%02x%02x%02x\n", - vid, nid, pid, sdw_data_3, - sdw_data_2, sdw_data_1, sdw_data_0); - } else { - pr_err("%s: it is not a get verb\n", __func__); } *value = ((sdw_data_3 & 0xff) << 24) | ((sdw_data_2 & 0xff) << 16) | ((sdw_data_1 & 0xff) << 8) | (sdw_data_0 & 0xff); @@ -627,20 +620,14 @@ static void rt700_get_gain(struct rt700_priv *rt700, unsigned int addr_h, { /* R Channel */ regmap_write(rt700->regmap, addr_h, val_h); - pr_debug("%s write %04x %02x\n", __func__, addr_h, val_h); regmap_write(rt700->regmap, addr_l, 0); - pr_debug("%s write %04x %02x\n", __func__, addr_l, 0); regmap_read(rt700->regmap, RT700_READ_HDA_0, r_val); - pr_debug("%s read %04x %02x\n", __func__, RT700_READ_HDA_0, *r_val); /* L Channel */ val_h |= 0x20; regmap_write(rt700->regmap, addr_h, val_h); - pr_debug("%s write %04x %02x\n", __func__, addr_h, val_h); regmap_write(rt700->regmap, addr_l, 0); - pr_debug("%s write %04x %02x\n", __func__, addr_l, 0); regmap_read(rt700->regmap, RT700_READ_HDA_0, l_val); - pr_debug("%s read %04x %02x\n", __func__, RT700_READ_HDA_0, *l_val); } /* For Verb-Set Amplifier Gain (Verb ID = 3h) */ @@ -672,9 +659,6 @@ static int rt700_set_amp_gain_put(struct snd_kcontrol *kcontrol, addr_h = mc->reg; addr_l = mc->rreg; - pr_debug("%s val = %d, %d\n", __func__, ucontrol->value.integer.value[0], - ucontrol->value.integer.value[1]); - /* L Channel */ if (mc->invert) { /* for mute */ @@ -712,38 +696,23 @@ static int rt700_set_amp_gain_put(struct snd_kcontrol *kcontrol, } for (i = 0; i < 3; i++) { /* retry 3 times at most */ - pr_debug("%s i=%d\n", __func__, i); addr_h = mc->reg; addr_l = mc->rreg; if (val_ll == val_lr) { /* Set both L/R channels at the same time */ val_h = (1 << mc->shift) | (3 << 4); regmap_write(rt700->regmap, addr_h, val_h); - pr_debug("%s write %04x %02x\n", - __func__, addr_h, val_h); regmap_write(rt700->regmap, addr_l, val_ll); - pr_debug("%s write %04x %02x\n", - __func__, addr_l, val_ll); - } else { /* Lch*/ val_h = (1 << mc->shift) | (1 << 5); regmap_write(rt700->regmap, addr_h, val_h); - pr_debug("%s write %04x %02x\n", - __func__, addr_h, val_h); regmap_write(rt700->regmap, addr_l, val_ll); - pr_debug("%s write %04x %02x\n", - __func__, addr_l, val_ll); /* Rch */ val_h = (1 << mc->shift) | (1 << 4); regmap_write(rt700->regmap, addr_h, val_h); - pr_debug("%s write %04x %02x\n", - __func__, addr_h, val_h); regmap_write(rt700->regmap, addr_l, val_lr); - pr_debug("%s write %04x %02x\n", - __func__, addr_l, val_lr); - } /* check result */ addr_h = (mc->reg + 0x2000) | 0x800; @@ -755,12 +724,8 @@ static int rt700_set_amp_gain_put(struct snd_kcontrol *kcontrol, rt700_get_gain(rt700, addr_h, addr_l, val_h, &read_rl, &read_ll); - if (read_rl == val_lr && read_ll == val_ll) { - pr_debug("write command successful\n"); + if (read_rl == val_lr && read_ll == val_ll) break; - } - - pr_warn("write command unsuccessful, retry\n"); } if (dapm->bias_level <= SND_SOC_BIAS_STANDBY) @@ -891,9 +856,7 @@ static int rt700_mux_get(struct snd_kcontrol *kcontrol, /* nid = e->reg, vid = 0xf01 */ reg = RT700_VERB_GET_CONNECT_SEL | e->reg; snd_soc_component_write(component, reg, 0x0); - pr_debug("%s write %04x %02x\n", __func__, reg, 0x0); val = snd_soc_component_read32(component, RT700_READ_HDA_0); - pr_debug("%s read %04x %02x\n", __func__, RT700_READ_HDA_0, val); ucontrol->value.enumerated.item[0] = val; return 0; @@ -916,25 +879,18 @@ static int rt700_mux_put(struct snd_kcontrol *kcontrol, /* Verb ID = 0x701h, nid = e->reg */ val = snd_soc_enum_item_to_val(e, item[0]) << e->shift_l; - pr_debug("%s val=%x e->reg=%x item[0]=%d\n", - __func__, val, e->reg, item[0]); reg = RT700_VERB_GET_CONNECT_SEL | e->reg; snd_soc_component_write(component, reg, 0x0); - pr_debug("%s write %04x %02x\n", __func__, reg, 0x0); val2 = snd_soc_component_read32(component, RT700_READ_HDA_0); - pr_debug("%s read %04x %02x\n", __func__, RT700_READ_HDA_0, val2); if (val == val2) change = 0; else change = 1; - pr_debug("change=%d\n", change); - if (change) { reg = RT700_VERB_SET_CONNECT_SEL | e->reg; snd_soc_component_write(component, reg, val); - pr_debug("%s write %04x %02x\n", __func__, reg, val); update.kcontrol = kcontrol; update.reg = e->reg; update.mask = 0xff; @@ -1107,7 +1063,6 @@ static int rt700_set_bias_level(struct snd_soc_component *component, struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component); - pr_debug("%s level=%d\n", __func__, level); switch (level) { case SND_SOC_BIAS_PREPARE: if (SND_SOC_BIAS_STANDBY == dapm->bias_level) { @@ -1431,7 +1386,6 @@ static ssize_t rt700_index_cmd_store(struct device *dev, unsigned int index_reg = 0, index_val = 0; int i; - pr_debug("register \"%s\" count=%zu\n", buf, count); for (i = 0; i < count; i++) { /*rt700->dbg_nidess */ if (*(buf + i) <= '9' && *(buf + i) >= '0') index_reg = (index_reg << 4) | @@ -1460,9 +1414,6 @@ static ssize_t rt700_index_cmd_store(struct device *dev, break; } - pr_debug("index_reg=0x%x index_val=0x%x\n", - index_reg, index_val); - rt700_index_write(rt700->regmap, index_reg, index_val); return count; @@ -1477,10 +1428,7 @@ static ssize_t rt700_hda_cmd_show(struct device *dev, int i, cnt = 0; unsigned int value; - pr_debug("%s cnt=%d RT700_HDA_DUMP_LEN=%d PAGE_SIZE=%d\n", - __func__, cnt, RT700_HDA_DUMP_LEN, PAGE_SIZE); for (i = 0; i < RT700_HDA_DUMP_LEN; i++) { - pr_debug("%s i=%d", __func__, i); if (cnt + 25 >= PAGE_SIZE) break; rt700->dbg_nid = hda_dump_list[i].nid; @@ -1510,7 +1458,6 @@ static ssize_t rt700_hda_cmd_store(struct device *dev, unsigned int sdw_data_3, sdw_data_2, sdw_data_1, sdw_data_0; int i; - pr_debug("register \"%s\" count=%zu\n", buf, count); for (i = 0; i < count; i++) { /*rt700->dbg_nidess */ if (*(buf + i) <= '9' && *(buf + i) >= '0') rt700->dbg_nid = (rt700->dbg_nid << 4) | @@ -1555,8 +1502,6 @@ static ssize_t rt700_hda_cmd_store(struct device *dev, else break; } - pr_debug("dbg_nid=0x%x dbg_vid=0x%x dbg_payload=0x%x\n", - rt700->dbg_nid, rt700->dbg_vid, rt700->dbg_payload); hda_to_sdw(rt700->dbg_nid, rt700->dbg_vid, rt700->dbg_payload, &sdw_addr_h, &sdw_data_h, &sdw_addr_l, &sdw_data_l); @@ -1784,19 +1729,6 @@ int rt700_probe(struct device *dev, struct regmap *regmap, /* Set index */ rt700_index_write(rt700->regmap, 0x4a, 0x201b); - //rt700_index_write(rt700->regmap, 0x38, 0x4921); - - /* get the setting registers for debug - pr_debug("%s get the setting registers\n", __func__); - rt700_hda_read(rt700->regmap, 0xf07, 0x21, 0, &value); - rt700_hda_read(rt700->regmap, 0xf07, 0x14, 0, &value); - rt700_hda_read(rt700->regmap, 0xf07, 0x12, 0, &value); - rt700_hda_read(rt700->regmap, 0xf07, 0x13, 0, &value); - rt700_hda_read(rt700->regmap, 0xf07, 0x19, 0, &value); - rt700_hda_read(rt700->regmap, 0xf07, 0x1a, 0, &value); - rt700_hda_read(rt700->regmap, 0xf07, 0x1b, 0, &value); - rt700_hda_read(rt700->regmap, 0xf0c, 0x14, 0, &value); - */ ret = rt700_clock_config(dev, alc700); /* Enable Jack Detection */ From d08710c5c5b288f1f434b707faf4ddfb7a1cb741 Mon Sep 17 00:00:00 2001 From: "Gogineni, GiribabuX" Date: Tue, 15 Aug 2017 16:36:28 +0530 Subject: [PATCH 0791/1103] ASoC: tdf8532: Fix compilation warnings Initialized the reported variables, listed below warning: 'ret' may be used uninitialized in this function warning: 'status_repl' may be used uninitialized in this function Change-Id: I6ca5a6e017402a582239d75959c122ffaa9f7298 Signed-off-by: Gogineni, GiribabuX Reviewed-on: Reviewed-by: Singh, Guneshwor O Reviewed-by: Sinha, Mohit Reviewed-by: Shaik, Kareem M Reviewed-by: Koul, Vinod Tested-by: Sm, Bhadur A --- sound/soc/codecs/tdf8532.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/sound/soc/codecs/tdf8532.c b/sound/soc/codecs/tdf8532.c index 7a3cca073845..e723ffebed0f 100644 --- a/sound/soc/codecs/tdf8532.c +++ b/sound/soc/codecs/tdf8532.c @@ -172,7 +172,7 @@ static int tdf8532_wait_state(struct tdf8532_priv *dev_data, u8 req_state, { unsigned long timeout_point = jiffies + msecs_to_jiffies(timeout); int ret; - struct get_dev_status_repl *status_repl; + struct get_dev_status_repl *status_repl = NULL; struct device *dev = &(dev_data->i2c->dev); do { @@ -318,9 +318,6 @@ static int tdf8532_i2c_probe(struct i2c_client *i2c, goto out; } - if (ret < 0) - dev_err(&i2c->dev, "Failed to set fast mute option: %d\n", ret); - dev_data->i2c = i2c; dev_data->pkt_id = 0; dev_data->channels = 4; From 13d69c91516a82674291ca7857f6a41f4935738b Mon Sep 17 00:00:00 2001 From: Dronamraju Santosh Pavan Kumar Date: Wed, 16 Aug 2017 17:04:49 +0530 Subject: [PATCH 0792/1103] ASoC: Intel: CNL: Remove larger frame size warnings from cnl_sdw_bra_pipe_cfg_pb and cnl_sdw_bra_pipe_cfg_cp functions. Below warning message observed due to static allocation of struct skl_module_cfg and struct skl_module in above mentioned functions: warning: the frame size of 85664 bytes is larger than 2048 bytes To avoid this warning memory is allocated dynamically. Change-Id: I62beb19219b70640a4e7391604b2f3884897e7d4 Signed-off-by: Dronamraju Santosh Pavan Kumar Reviewed-on: Reviewed-by: Singh, Guneshwor O Reviewed-by: Tewani, Pradeep D Reviewed-by: Shaik, Kareem M Reviewed-by: Prusty, Subhransu S Reviewed-by: Kp, Jeeja Reviewed-by: Koul, Vinod Tested-by: Sm, Bhadur A --- sound/soc/intel/skylake/skl-messages.c | 485 ++++++++++++++----------- 1 file changed, 270 insertions(+), 215 deletions(-) diff --git a/sound/soc/intel/skylake/skl-messages.c b/sound/soc/intel/skylake/skl-messages.c index 8560cd65e77c..9d413b816437 100644 --- a/sound/soc/intel/skylake/skl-messages.c +++ b/sound/soc/intel/skylake/skl-messages.c @@ -365,25 +365,49 @@ static int cnl_sdw_bra_pipe_cfg_pb(struct skl_sst *ctx, struct bra_conf *bra_data = &ctx->bra_pipe_data[mstr_num]; struct skl_pipe *host_cpr_pipe = NULL; struct skl_pipe_params host_cpr_params; - struct skl_module_cfg host_cpr_cfg, link_cpr_cfg; - struct skl_module host_cpr_mod, link_cpr_mod; + struct skl_module_cfg *host_cpr_cfg = NULL, *link_cpr_cfg = NULL; + struct skl_module *host_cpr_mod = NULL, *link_cpr_mod = NULL; int ret; struct skl_module_fmt *in_fmt, *out_fmt; u8 guid[16] = { 131, 12, 160, 155, 18, 202, 131, 74, 148, 60, 31, 162, 232, 47, 157, 218 }; - link_cpr_cfg.module = &link_cpr_mod; - host_cpr_cfg.module = &host_cpr_mod; + host_cpr_cfg = kzalloc(sizeof(*host_cpr_cfg), GFP_KERNEL); + if (!host_cpr_cfg) { + ret = -ENOMEM; + goto error; + } + + link_cpr_cfg = kzalloc(sizeof(*link_cpr_cfg), GFP_KERNEL); + if (!link_cpr_cfg) { + ret = -ENOMEM; + goto error; + } + + host_cpr_mod = kzalloc(sizeof(*host_cpr_mod), GFP_KERNEL); + if (!host_cpr_mod) { + ret = -ENOMEM; + goto error; + } + + link_cpr_mod = kzalloc(sizeof(*link_cpr_mod), GFP_KERNEL); + if (!link_cpr_mod) { + ret = -ENOMEM; + goto error; + } + + link_cpr_cfg->module = link_cpr_mod; + host_cpr_cfg->module = host_cpr_mod; /* * To get the pvt id, UUID of the module config is * necessary. Hence hardocde this to the UUID fof copier * module */ - memcpy(&host_cpr_cfg.guid, &guid, 16); - memcpy(&link_cpr_cfg.guid, &guid, 16); - in_fmt = &host_cpr_cfg.module->formats[0].inputs[0].fmt; - out_fmt = &host_cpr_cfg.module->formats[0].outputs[0].fmt; + memcpy(host_cpr_cfg->guid, &guid, 16); + memcpy(link_cpr_cfg->guid, &guid, 16); + in_fmt = &link_cpr_cfg->module->formats[0].inputs[0].fmt; + out_fmt = &link_cpr_cfg->module->formats[0].outputs[0].fmt; /* Playback pipeline */ host_cpr_pipe = kzalloc(sizeof(struct skl_pipe), GFP_KERNEL); @@ -392,21 +416,21 @@ static int cnl_sdw_bra_pipe_cfg_pb(struct skl_sst *ctx, goto error; } - host_cpr_cfg.fmt_idx = 0; - host_cpr_cfg.res_idx = 0; - link_cpr_cfg.fmt_idx = 0; - link_cpr_cfg.res_idx = 0; + host_cpr_cfg->fmt_idx = 0; + host_cpr_cfg->res_idx = 0; + link_cpr_cfg->fmt_idx = 0; + link_cpr_cfg->res_idx = 0; bra_data->pb_pipe = host_cpr_pipe; host_cpr_pipe->p_params = &host_cpr_params; - host_cpr_cfg.pipe = host_cpr_pipe; + host_cpr_cfg->pipe = host_cpr_pipe; host_cpr_pipe->ppl_id = 1; host_cpr_pipe->pipe_priority = 0; host_cpr_pipe->conn_type = 0; host_cpr_pipe->memory_pages = 2; - ret = skl_create_pipeline(ctx, host_cpr_cfg.pipe); + ret = skl_create_pipeline(ctx, host_cpr_cfg->pipe); if (ret < 0) goto error; @@ -417,33 +441,33 @@ static int cnl_sdw_bra_pipe_cfg_pb(struct skl_sst *ctx, host_cpr_params.s_fmt = 32; host_cpr_params.linktype = 0; host_cpr_params.stream = 0; - host_cpr_cfg.id.module_id = skl_get_module_id(ctx, - (uuid_le *)host_cpr_cfg.guid); + host_cpr_cfg->id.module_id = skl_get_module_id(ctx, + (uuid_le *)host_cpr_cfg->guid); - host_cpr_cfg.id.instance_id = 1; - host_cpr_cfg.id.pvt_id = skl_get_pvt_id(ctx, - (uuid_le *)host_cpr_cfg.guid, host_cpr_cfg.id.instance_id); - if (host_cpr_cfg.id.pvt_id < 0) + host_cpr_cfg->id.instance_id = 1; + host_cpr_cfg->id.pvt_id = skl_get_pvt_id(ctx, + (uuid_le *)host_cpr_cfg->guid, host_cpr_cfg->id.instance_id); + if (host_cpr_cfg->id.pvt_id < 0) return -EINVAL; - host_cpr_cfg.module->resources[0].cps = 100000; - host_cpr_cfg.module->resources[0].is_pages = 0; - host_cpr_cfg.module->resources[0].ibs = 384; - host_cpr_cfg.module->resources[0].obs = 384; - host_cpr_cfg.core_id = 0; - host_cpr_cfg.module->max_input_pins = 1; - host_cpr_cfg.module->max_output_pins = 1; - host_cpr_cfg.module->loadable = 0; - host_cpr_cfg.domain = 0; - host_cpr_cfg.m_type = SKL_MODULE_TYPE_COPIER; - host_cpr_cfg.dev_type = SKL_DEVICE_HDAHOST; - host_cpr_cfg.hw_conn_type = SKL_CONN_SOURCE; - host_cpr_cfg.formats_config.caps_size = 0; - host_cpr_cfg.module->resources[0].dma_buffer_size = 2; - host_cpr_cfg.converter = 0; - host_cpr_cfg.vbus_id = 0; - host_cpr_cfg.sdw_agg_enable = 0; - host_cpr_cfg.formats_config.caps_size = 0; + host_cpr_cfg->module->resources[0].cps = 100000; + host_cpr_cfg->module->resources[0].is_pages = 0; + host_cpr_cfg->module->resources[0].ibs = 384; + host_cpr_cfg->module->resources[0].obs = 384; + host_cpr_cfg->core_id = 0; + host_cpr_cfg->module->max_input_pins = 1; + host_cpr_cfg->module->max_output_pins = 1; + host_cpr_cfg->module->loadable = 0; + host_cpr_cfg->domain = 0; + host_cpr_cfg->m_type = SKL_MODULE_TYPE_COPIER; + host_cpr_cfg->dev_type = SKL_DEVICE_HDAHOST; + host_cpr_cfg->hw_conn_type = SKL_CONN_SOURCE; + host_cpr_cfg->formats_config.caps_size = 0; + host_cpr_cfg->module->resources[0].dma_buffer_size = 2; + host_cpr_cfg->converter = 0; + host_cpr_cfg->vbus_id = 0; + host_cpr_cfg->sdw_agg_enable = 0; + host_cpr_cfg->formats_config.caps_size = 0; in_fmt->channels = 1; in_fmt->s_freq = 96000; @@ -463,126 +487,130 @@ static int cnl_sdw_bra_pipe_cfg_pb(struct skl_sst *ctx, out_fmt->sample_type = 0; out_fmt->ch_map = 0xFFFFFFF1; - host_cpr_cfg.m_in_pin = kcalloc(host_cpr_cfg.module->max_input_pins, - sizeof(*host_cpr_cfg.m_in_pin), + host_cpr_cfg->m_in_pin = kcalloc(host_cpr_cfg->module->max_input_pins, + sizeof(*host_cpr_cfg->m_in_pin), GFP_KERNEL); - if (!host_cpr_cfg.m_in_pin) { + if (!host_cpr_cfg->m_in_pin) { ret = -ENOMEM; goto error; } - host_cpr_cfg.m_out_pin = kcalloc(host_cpr_cfg.module->max_output_pins, - sizeof(*host_cpr_cfg.m_out_pin), + host_cpr_cfg->m_out_pin = kcalloc(host_cpr_cfg->module->max_output_pins, + sizeof(*host_cpr_cfg->m_out_pin), GFP_KERNEL); - if (!host_cpr_cfg.m_out_pin) { + if (!host_cpr_cfg->m_out_pin) { ret = -ENOMEM; goto error; } - host_cpr_cfg.m_in_pin[0].id.module_id = - host_cpr_cfg.id.module_id; - host_cpr_cfg.m_in_pin[0].id.instance_id = - host_cpr_cfg.id.instance_id; - host_cpr_cfg.m_in_pin[0].in_use = false; - host_cpr_cfg.m_in_pin[0].is_dynamic = true; - host_cpr_cfg.m_in_pin[0].pin_state = SKL_PIN_UNBIND; - - host_cpr_cfg.m_out_pin[0].id.module_id = - host_cpr_cfg.id.module_id; - host_cpr_cfg.m_out_pin[0].id.instance_id = - host_cpr_cfg.id.instance_id; - host_cpr_cfg.m_out_pin[0].in_use = false; - host_cpr_cfg.m_out_pin[0].is_dynamic = true; - host_cpr_cfg.m_out_pin[0].pin_state = SKL_PIN_UNBIND; - - memcpy(&link_cpr_cfg, &host_cpr_cfg, + host_cpr_cfg->m_in_pin[0].id.module_id = + host_cpr_cfg->id.module_id; + host_cpr_cfg->m_in_pin[0].id.instance_id = + host_cpr_cfg->id.instance_id; + host_cpr_cfg->m_in_pin[0].in_use = false; + host_cpr_cfg->m_in_pin[0].is_dynamic = true; + host_cpr_cfg->m_in_pin[0].pin_state = SKL_PIN_UNBIND; + + host_cpr_cfg->m_out_pin[0].id.module_id = + host_cpr_cfg->id.module_id; + host_cpr_cfg->m_out_pin[0].id.instance_id = + host_cpr_cfg->id.instance_id; + host_cpr_cfg->m_out_pin[0].in_use = false; + host_cpr_cfg->m_out_pin[0].is_dynamic = true; + host_cpr_cfg->m_out_pin[0].pin_state = SKL_PIN_UNBIND; + + memcpy(link_cpr_cfg, host_cpr_cfg, sizeof(struct skl_module_cfg)); - link_cpr_cfg.id.instance_id = 2; - link_cpr_cfg.id.pvt_id = skl_get_pvt_id(ctx, - (uuid_le *)link_cpr_cfg.guid, link_cpr_cfg.id.instance_id); - if (link_cpr_cfg.id.pvt_id < 0) + link_cpr_cfg->id.instance_id = 2; + link_cpr_cfg->id.pvt_id = skl_get_pvt_id(ctx, + (uuid_le *)link_cpr_cfg->guid, link_cpr_cfg->id.instance_id); + if (link_cpr_cfg->id.pvt_id < 0) return -EINVAL; - link_cpr_cfg.dev_type = SKL_DEVICE_SDW_PCM; + link_cpr_cfg->dev_type = SKL_DEVICE_SDW_PCM; #if IS_ENABLED(CONFIG_SND_SOC_INTEL_CNL_FPGA) - link_cpr_cfg.sdw_stream_num = 0x3; + link_cpr_cfg->sdw_stream_num = 0x3; #else - link_cpr_cfg.sdw_stream_num = 0x13; + link_cpr_cfg->sdw_stream_num = 0x13; #endif - link_cpr_cfg.hw_conn_type = SKL_CONN_SOURCE; + link_cpr_cfg->hw_conn_type = SKL_CONN_SOURCE; - link_cpr_cfg.m_in_pin = kcalloc(link_cpr_cfg.module->max_input_pins, - sizeof(*link_cpr_cfg.m_in_pin), + link_cpr_cfg->m_in_pin = kcalloc(link_cpr_cfg->module->max_input_pins, + sizeof(*link_cpr_cfg->m_in_pin), GFP_KERNEL); - if (!link_cpr_cfg.m_in_pin) { + if (!link_cpr_cfg->m_in_pin) { ret = -ENOMEM; goto error; } - link_cpr_cfg.m_out_pin = kcalloc(link_cpr_cfg.module->max_output_pins, - sizeof(*link_cpr_cfg.m_out_pin), + link_cpr_cfg->m_out_pin = kcalloc(link_cpr_cfg->module->max_output_pins, + sizeof(*link_cpr_cfg->m_out_pin), GFP_KERNEL); - if (!link_cpr_cfg.m_out_pin) { + if (!link_cpr_cfg->m_out_pin) { ret = -ENOMEM; goto error; } - link_cpr_cfg.m_in_pin[0].id.module_id = - link_cpr_cfg.id.module_id; - link_cpr_cfg.m_in_pin[0].id.instance_id = - link_cpr_cfg.id.instance_id; - link_cpr_cfg.m_in_pin[0].in_use = false; - link_cpr_cfg.m_in_pin[0].is_dynamic = true; - link_cpr_cfg.m_in_pin[0].pin_state = SKL_PIN_UNBIND; - - link_cpr_cfg.m_out_pin[0].id.module_id = - link_cpr_cfg.id.module_id; - link_cpr_cfg.m_out_pin[0].id.instance_id = - link_cpr_cfg.id.instance_id; - link_cpr_cfg.m_out_pin[0].in_use = false; - link_cpr_cfg.m_out_pin[0].is_dynamic = true; - link_cpr_cfg.m_out_pin[0].pin_state = SKL_PIN_UNBIND; - - link_cpr_cfg.formats_config.caps_size = (sizeof(u32) * 4); - link_cpr_cfg.formats_config.caps = kzalloc((sizeof(u32) * 4), + link_cpr_cfg->m_in_pin[0].id.module_id = + link_cpr_cfg->id.module_id; + link_cpr_cfg->m_in_pin[0].id.instance_id = + link_cpr_cfg->id.instance_id; + link_cpr_cfg->m_in_pin[0].in_use = false; + link_cpr_cfg->m_in_pin[0].is_dynamic = true; + link_cpr_cfg->m_in_pin[0].pin_state = SKL_PIN_UNBIND; + + link_cpr_cfg->m_out_pin[0].id.module_id = + link_cpr_cfg->id.module_id; + link_cpr_cfg->m_out_pin[0].id.instance_id = + link_cpr_cfg->id.instance_id; + link_cpr_cfg->m_out_pin[0].in_use = false; + link_cpr_cfg->m_out_pin[0].is_dynamic = true; + link_cpr_cfg->m_out_pin[0].pin_state = SKL_PIN_UNBIND; + + link_cpr_cfg->formats_config.caps_size = (sizeof(u32) * 4); + link_cpr_cfg->formats_config.caps = kzalloc((sizeof(u32) * 4), GFP_KERNEL); - if (!link_cpr_cfg.formats_config.caps) { + if (!link_cpr_cfg->formats_config.caps) { ret = -ENOMEM; goto error; } - link_cpr_cfg.formats_config.caps[0] = 0x0; - link_cpr_cfg.formats_config.caps[1] = 0x1; + link_cpr_cfg->formats_config.caps[0] = 0x0; + link_cpr_cfg->formats_config.caps[1] = 0x1; #if IS_ENABLED(CONFIG_SND_SOC_INTEL_CNL_FPGA) - link_cpr_cfg.formats_config.caps[2] = 0x1003; + link_cpr_cfg->formats_config.caps[2] = 0x1003; #else - link_cpr_cfg.formats_config.caps[2] = 0x1013; + link_cpr_cfg->formats_config.caps[2] = 0x1013; #endif - link_cpr_cfg.formats_config.caps[3] = 0x0; + link_cpr_cfg->formats_config.caps[3] = 0x0; /* Init PB CPR1 module */ - ret = skl_init_module(ctx, &host_cpr_cfg); + ret = skl_init_module(ctx, host_cpr_cfg); if (ret < 0) goto error; /* Init PB CPR2 module */ - ret = skl_init_module(ctx, &link_cpr_cfg); + ret = skl_init_module(ctx, link_cpr_cfg); if (ret < 0) goto error; /* Bind PB CPR1 and CPR2 module */ - ret = skl_bind_modules(ctx, &host_cpr_cfg, &link_cpr_cfg); + ret = skl_bind_modules(ctx, host_cpr_cfg, link_cpr_cfg); if (ret < 0) goto error; error: /* Free up all memory allocated */ - kfree(host_cpr_cfg.m_in_pin); - kfree(host_cpr_cfg.m_out_pin); - kfree(link_cpr_cfg.m_in_pin); - kfree(link_cpr_cfg.m_out_pin); - kfree(link_cpr_cfg.formats_config.caps); + kfree(host_cpr_cfg->m_in_pin); + kfree(host_cpr_cfg->m_out_pin); + kfree(link_cpr_cfg->m_in_pin); + kfree(link_cpr_cfg->m_out_pin); + kfree(link_cpr_cfg->formats_config.caps); + kfree(host_cpr_cfg); + kfree(link_cpr_cfg); + kfree(host_cpr_mod); + kfree(link_cpr_mod); return ret; } @@ -593,15 +621,39 @@ static int cnl_sdw_bra_pipe_cfg_cp(struct skl_sst *ctx, struct bra_conf *bra_data = &ctx->bra_pipe_data[mstr_num]; struct skl_pipe *link_cpr_pipe = NULL; struct skl_pipe_params link_cpr_params; - struct skl_module host_cpr_mod, link_cpr_mod; - struct skl_module_cfg link_cpr_cfg, host_cpr_cfg; + struct skl_module *host_cpr_mod = NULL, *link_cpr_mod = NULL; + struct skl_module_cfg *link_cpr_cfg = NULL, *host_cpr_cfg = NULL; int ret; struct skl_module_fmt *in_fmt, *out_fmt; u8 guid[16] = { 131, 12, 160, 155, 18, 202, 131, 74, 148, 60, 31, 162, 232, 47, 157, 218 }; - link_cpr_cfg.module = &link_cpr_mod; - host_cpr_cfg.module = &host_cpr_mod; + link_cpr_cfg = kzalloc(sizeof(*link_cpr_cfg), GFP_KERNEL); + if (!link_cpr_cfg) { + ret = -ENOMEM; + goto error; + } + + host_cpr_cfg = kzalloc(sizeof(*host_cpr_cfg), GFP_KERNEL); + if (!host_cpr_cfg) { + ret = -ENOMEM; + goto error; + } + + host_cpr_mod = kzalloc(sizeof(*host_cpr_mod), GFP_KERNEL); + if (!host_cpr_mod) { + ret = -ENOMEM; + goto error; + } + + link_cpr_mod = kzalloc(sizeof(*link_cpr_mod), GFP_KERNEL); + if (!link_cpr_mod) { + ret = -ENOMEM; + goto error; + } + + link_cpr_cfg->module = link_cpr_mod; + host_cpr_cfg->module = host_cpr_mod; /* @@ -609,10 +661,10 @@ static int cnl_sdw_bra_pipe_cfg_cp(struct skl_sst *ctx, * necessary. Hence hardocde this to the UUID fof copier * module */ - memcpy(&host_cpr_cfg.guid, &guid, 16); - memcpy(&link_cpr_cfg.guid, &guid, 16); - in_fmt = &link_cpr_cfg.module->formats[0].inputs[0].fmt; - out_fmt = &link_cpr_cfg.module->formats[0].outputs[0].fmt; + memcpy(host_cpr_cfg->guid, &guid, 16); + memcpy(link_cpr_cfg->guid, &guid, 16); + in_fmt = &link_cpr_cfg->module->formats[0].inputs[0].fmt; + out_fmt = &link_cpr_cfg->module->formats[0].outputs[0].fmt; /* Capture Pipeline */ link_cpr_pipe = kzalloc(sizeof(struct skl_pipe), GFP_KERNEL); @@ -621,13 +673,13 @@ static int cnl_sdw_bra_pipe_cfg_cp(struct skl_sst *ctx, goto error; } - link_cpr_cfg.fmt_idx = 0; - link_cpr_cfg.res_idx = 0; - host_cpr_cfg.fmt_idx = 0; - host_cpr_cfg.res_idx = 0; + link_cpr_cfg->fmt_idx = 0; + link_cpr_cfg->res_idx = 0; + host_cpr_cfg->fmt_idx = 0; + host_cpr_cfg->res_idx = 0; bra_data->cp_pipe = link_cpr_pipe; link_cpr_pipe->p_params = &link_cpr_params; - link_cpr_cfg.pipe = link_cpr_pipe; + link_cpr_cfg->pipe = link_cpr_pipe; link_cpr_pipe->ppl_id = 2; link_cpr_pipe->pipe_priority = 0; @@ -635,7 +687,7 @@ static int cnl_sdw_bra_pipe_cfg_cp(struct skl_sst *ctx, link_cpr_pipe->memory_pages = 2; /* Create Capture Pipeline */ - ret = skl_create_pipeline(ctx, link_cpr_cfg.pipe); + ret = skl_create_pipeline(ctx, link_cpr_cfg->pipe); if (ret < 0) goto error; @@ -646,54 +698,54 @@ static int cnl_sdw_bra_pipe_cfg_cp(struct skl_sst *ctx, link_cpr_params.s_fmt = 32; link_cpr_params.linktype = 0; link_cpr_params.stream = 0; - host_cpr_cfg.id.module_id = skl_get_module_id(ctx, - (uuid_le *)host_cpr_cfg.guid); + host_cpr_cfg->id.module_id = skl_get_module_id(ctx, + (uuid_le *)host_cpr_cfg->guid); - link_cpr_cfg.id.instance_id = 3; - link_cpr_cfg.id.pvt_id = skl_get_pvt_id(ctx, - (uuid_le *)link_cpr_cfg.guid, link_cpr_cfg.id.instance_id); - if (link_cpr_cfg.id.pvt_id < 0) + link_cpr_cfg->id.instance_id = 3; + link_cpr_cfg->id.pvt_id = skl_get_pvt_id(ctx, + (uuid_le *)link_cpr_cfg->guid, link_cpr_cfg->id.instance_id); + if (link_cpr_cfg->id.pvt_id < 0) return -EINVAL; - link_cpr_cfg.module->resources[0].cps = 100000; - link_cpr_cfg.module->resources[0].is_pages = 0; - link_cpr_cfg.module->resources[0].ibs = 1152; - link_cpr_cfg.module->resources[0].obs = 1152; - link_cpr_cfg.core_id = 0; - link_cpr_cfg.module->max_input_pins = 1; - link_cpr_cfg.module->max_output_pins = 1; - link_cpr_cfg.module->loadable = 0; - link_cpr_cfg.domain = 0; - link_cpr_cfg.m_type = SKL_MODULE_TYPE_COPIER; - link_cpr_cfg.dev_type = SKL_DEVICE_SDW_PCM; + link_cpr_cfg->module->resources[0].cps = 100000; + link_cpr_cfg->module->resources[0].is_pages = 0; + link_cpr_cfg->module->resources[0].ibs = 1152; + link_cpr_cfg->module->resources[0].obs = 1152; + link_cpr_cfg->core_id = 0; + link_cpr_cfg->module->max_input_pins = 1; + link_cpr_cfg->module->max_output_pins = 1; + link_cpr_cfg->module->loadable = 0; + link_cpr_cfg->domain = 0; + link_cpr_cfg->m_type = SKL_MODULE_TYPE_COPIER; + link_cpr_cfg->dev_type = SKL_DEVICE_SDW_PCM; #if IS_ENABLED(CONFIG_SND_SOC_INTEL_CNL_FPGA) - link_cpr_cfg.sdw_stream_num = 0x4; + link_cpr_cfg->sdw_stream_num = 0x4; #else - link_cpr_cfg.sdw_stream_num = 0x14; + link_cpr_cfg->sdw_stream_num = 0x14; #endif - link_cpr_cfg.hw_conn_type = SKL_CONN_SINK; - - link_cpr_cfg.formats_config.caps_size = 0; - link_cpr_cfg.module->resources[0].dma_buffer_size = 2; - link_cpr_cfg.converter = 0; - link_cpr_cfg.vbus_id = 0; - link_cpr_cfg.sdw_agg_enable = 0; - link_cpr_cfg.formats_config.caps_size = (sizeof(u32) * 4); - link_cpr_cfg.formats_config.caps = kzalloc((sizeof(u32) * 4), + link_cpr_cfg->hw_conn_type = SKL_CONN_SINK; + + link_cpr_cfg->formats_config.caps_size = 0; + link_cpr_cfg->module->resources[0].dma_buffer_size = 2; + link_cpr_cfg->converter = 0; + link_cpr_cfg->vbus_id = 0; + link_cpr_cfg->sdw_agg_enable = 0; + link_cpr_cfg->formats_config.caps_size = (sizeof(u32) * 4); + link_cpr_cfg->formats_config.caps = kzalloc((sizeof(u32) * 4), GFP_KERNEL); - if (!link_cpr_cfg.formats_config.caps) { + if (!link_cpr_cfg->formats_config.caps) { ret = -ENOMEM; goto error; } - link_cpr_cfg.formats_config.caps[0] = 0x0; - link_cpr_cfg.formats_config.caps[1] = 0x1; + link_cpr_cfg->formats_config.caps[0] = 0x0; + link_cpr_cfg->formats_config.caps[1] = 0x1; #if IS_ENABLED(CONFIG_SND_SOC_INTEL_CNL_FPGA) - link_cpr_cfg.formats_config.caps[2] = 0x1104; + link_cpr_cfg->formats_config.caps[2] = 0x1104; #else - link_cpr_cfg.formats_config.caps[2] = 0x1114; + link_cpr_cfg->formats_config.caps[2] = 0x1114; #endif - link_cpr_cfg.formats_config.caps[3] = 0x1; + link_cpr_cfg->formats_config.caps[3] = 0x1; in_fmt->channels = 6; in_fmt->s_freq = 48000; @@ -713,107 +765,110 @@ static int cnl_sdw_bra_pipe_cfg_cp(struct skl_sst *ctx, out_fmt->sample_type = 0; out_fmt->ch_map = 0xFF657120; - link_cpr_cfg.m_in_pin = kcalloc(link_cpr_cfg.module->max_input_pins, - sizeof(*link_cpr_cfg.m_in_pin), + link_cpr_cfg->m_in_pin = kcalloc(link_cpr_cfg->module->max_input_pins, + sizeof(*link_cpr_cfg->m_in_pin), GFP_KERNEL); - if (!link_cpr_cfg.m_in_pin) { + if (!link_cpr_cfg->m_in_pin) { ret = -ENOMEM; goto error; } - link_cpr_cfg.m_out_pin = kcalloc(link_cpr_cfg.module->max_output_pins, - sizeof(*link_cpr_cfg.m_out_pin), + link_cpr_cfg->m_out_pin = kcalloc(link_cpr_cfg->module->max_output_pins, + sizeof(*link_cpr_cfg->m_out_pin), GFP_KERNEL); - if (!link_cpr_cfg.m_out_pin) { + if (!link_cpr_cfg->m_out_pin) { ret = -ENOMEM; goto error; } - link_cpr_cfg.m_in_pin[0].id.module_id = - link_cpr_cfg.id.module_id; - link_cpr_cfg.m_in_pin[0].id.instance_id = - link_cpr_cfg.id.instance_id; - link_cpr_cfg.m_in_pin[0].in_use = false; - link_cpr_cfg.m_in_pin[0].is_dynamic = true; - link_cpr_cfg.m_in_pin[0].pin_state = SKL_PIN_UNBIND; - - link_cpr_cfg.m_out_pin[0].id.module_id = - link_cpr_cfg.id.module_id; - link_cpr_cfg.m_out_pin[0].id.instance_id = - link_cpr_cfg.id.instance_id; - link_cpr_cfg.m_out_pin[0].in_use = false; - link_cpr_cfg.m_out_pin[0].is_dynamic = true; - link_cpr_cfg.m_out_pin[0].pin_state = SKL_PIN_UNBIND; - - memcpy(&host_cpr_cfg, &link_cpr_cfg, + link_cpr_cfg->m_in_pin[0].id.module_id = + link_cpr_cfg->id.module_id; + link_cpr_cfg->m_in_pin[0].id.instance_id = + link_cpr_cfg->id.instance_id; + link_cpr_cfg->m_in_pin[0].in_use = false; + link_cpr_cfg->m_in_pin[0].is_dynamic = true; + link_cpr_cfg->m_in_pin[0].pin_state = SKL_PIN_UNBIND; + + link_cpr_cfg->m_out_pin[0].id.module_id = + link_cpr_cfg->id.module_id; + link_cpr_cfg->m_out_pin[0].id.instance_id = + link_cpr_cfg->id.instance_id; + link_cpr_cfg->m_out_pin[0].in_use = false; + link_cpr_cfg->m_out_pin[0].is_dynamic = true; + link_cpr_cfg->m_out_pin[0].pin_state = SKL_PIN_UNBIND; + + memcpy(host_cpr_cfg, link_cpr_cfg, sizeof(struct skl_module_cfg)); - host_cpr_cfg.id.instance_id = 4; - host_cpr_cfg.id.pvt_id = skl_get_pvt_id(ctx, - (uuid_le *)host_cpr_cfg.guid, host_cpr_cfg.id.instance_id); - if (host_cpr_cfg.id.pvt_id < 0) + host_cpr_cfg->id.instance_id = 4; + host_cpr_cfg->id.pvt_id = skl_get_pvt_id(ctx, + (uuid_le *)host_cpr_cfg->guid, host_cpr_cfg->id.instance_id); + if (host_cpr_cfg->id.pvt_id < 0) return -EINVAL; - host_cpr_cfg.dev_type = SKL_DEVICE_HDAHOST; - host_cpr_cfg.hw_conn_type = SKL_CONN_SINK; + host_cpr_cfg->dev_type = SKL_DEVICE_HDAHOST; + host_cpr_cfg->hw_conn_type = SKL_CONN_SINK; link_cpr_params.host_dma_id = (bra_data->cp_stream_tag - 1); - host_cpr_cfg.formats_config.caps_size = 0; - host_cpr_cfg.m_in_pin = kcalloc(host_cpr_cfg.module->max_input_pins, - sizeof(*host_cpr_cfg.m_in_pin), + host_cpr_cfg->formats_config.caps_size = 0; + host_cpr_cfg->m_in_pin = kcalloc(host_cpr_cfg->module->max_input_pins, + sizeof(*host_cpr_cfg->m_in_pin), GFP_KERNEL); - if (!host_cpr_cfg.m_in_pin) { + if (!host_cpr_cfg->m_in_pin) { ret = -ENOMEM; goto error; } - host_cpr_cfg.m_out_pin = kcalloc(host_cpr_cfg.module->max_output_pins, - sizeof(*host_cpr_cfg.m_out_pin), + host_cpr_cfg->m_out_pin = kcalloc(host_cpr_cfg->module->max_output_pins, + sizeof(*host_cpr_cfg->m_out_pin), GFP_KERNEL); - if (!host_cpr_cfg.m_out_pin) { + if (!host_cpr_cfg->m_out_pin) { ret = -ENOMEM; goto error; } - host_cpr_cfg.m_in_pin[0].id.module_id = - host_cpr_cfg.id.module_id; - host_cpr_cfg.m_in_pin[0].id.instance_id = - host_cpr_cfg.id.instance_id; - host_cpr_cfg.m_in_pin[0].in_use = false; - host_cpr_cfg.m_in_pin[0].is_dynamic = true; - host_cpr_cfg.m_in_pin[0].pin_state = SKL_PIN_UNBIND; - - host_cpr_cfg.m_out_pin[0].id.module_id = - host_cpr_cfg.id.module_id; - host_cpr_cfg.m_out_pin[0].id.instance_id = - host_cpr_cfg.id.instance_id; - host_cpr_cfg.m_out_pin[0].in_use = false; - host_cpr_cfg.m_out_pin[0].is_dynamic = true; - host_cpr_cfg.m_out_pin[0].pin_state = SKL_PIN_UNBIND; + host_cpr_cfg->m_in_pin[0].id.module_id = + host_cpr_cfg->id.module_id; + host_cpr_cfg->m_in_pin[0].id.instance_id = + host_cpr_cfg->id.instance_id; + host_cpr_cfg->m_in_pin[0].in_use = false; + host_cpr_cfg->m_in_pin[0].is_dynamic = true; + host_cpr_cfg->m_in_pin[0].pin_state = SKL_PIN_UNBIND; + + host_cpr_cfg->m_out_pin[0].id.module_id = + host_cpr_cfg->id.module_id; + host_cpr_cfg->m_out_pin[0].id.instance_id = + host_cpr_cfg->id.instance_id; + host_cpr_cfg->m_out_pin[0].in_use = false; + host_cpr_cfg->m_out_pin[0].is_dynamic = true; + host_cpr_cfg->m_out_pin[0].pin_state = SKL_PIN_UNBIND; /* Init CP CPR1 module */ - ret = skl_init_module(ctx, &link_cpr_cfg); + ret = skl_init_module(ctx, link_cpr_cfg); if (ret < 0) goto error; /* Init CP CPR2 module */ - ret = skl_init_module(ctx, &host_cpr_cfg); + ret = skl_init_module(ctx, host_cpr_cfg); if (ret < 0) goto error; /* Bind CP CPR1 and CPR2 module */ - ret = skl_bind_modules(ctx, &link_cpr_cfg, &host_cpr_cfg); + ret = skl_bind_modules(ctx, link_cpr_cfg, host_cpr_cfg); if (ret < 0) goto error; error: /* Free up all memory allocated */ - kfree(link_cpr_cfg.formats_config.caps); - kfree(link_cpr_cfg.m_in_pin); - kfree(link_cpr_cfg.m_out_pin); - kfree(host_cpr_cfg.m_in_pin); - kfree(host_cpr_cfg.m_out_pin); - + kfree(link_cpr_cfg->formats_config.caps); + kfree(link_cpr_cfg->m_in_pin); + kfree(link_cpr_cfg->m_out_pin); + kfree(host_cpr_cfg->m_in_pin); + kfree(host_cpr_cfg->m_out_pin); + kfree(link_cpr_cfg); + kfree(host_cpr_cfg); + kfree(host_cpr_mod); + kfree(link_cpr_mod); return ret; } From 74c5008806a8f6474eb90343ce1a317181a96cd6 Mon Sep 17 00:00:00 2001 From: "Paul, Subhankar" Date: Tue, 8 Aug 2017 22:46:45 +0530 Subject: [PATCH 0793/1103] ASoC: Intel: Skylake: Add support for module notifications Firmware modules can send asynchronous notification to driver with event data as payload. Add support for notifying user of such asynchronous notifications from firmware modules by adding kcontrols. These kcontrols have the module event data that needs to be sent to user. Change-Id: If204e275a9613c769cf00fe632e45b174bd2fa2f Signed-off-by: Mohit Sinha Reviewed-on: Reviewed-by: Shaik, Kareem M Reviewed-by: Kesapragada, Pardha Saradhi Reviewed-by: Paul, Subhankar Reviewed-by: Koul, Vinod Reviewed-by: audio_build Tested-by: Avati, Santosh Kumar --- sound/soc/intel/skylake/skl-messages.c | 2 + sound/soc/intel/skylake/skl-sst-dsp.h | 1 + sound/soc/intel/skylake/skl-sst-ipc.c | 58 +++++++++++++- sound/soc/intel/skylake/skl-sst-ipc.h | 9 +++ sound/soc/intel/skylake/skl-topology.c | 107 +++++++++++++++++++++++++ sound/soc/intel/skylake/skl-topology.h | 8 ++ sound/soc/intel/skylake/skl.c | 2 + 7 files changed, 186 insertions(+), 1 deletion(-) diff --git a/sound/soc/intel/skylake/skl-messages.c b/sound/soc/intel/skylake/skl-messages.c index 9d413b816437..ea500be81329 100644 --- a/sound/soc/intel/skylake/skl-messages.c +++ b/sound/soc/intel/skylake/skl-messages.c @@ -1224,6 +1224,8 @@ int skl_init_dsp(struct skl *skl) dev_dbg(bus->dev, "dsp registration status=%d\n", ret); + INIT_LIST_HEAD(&skl->skl_sst->notify_kctls); + /* Set DMA clock controls */ ret = skl_dsp_set_dma_clk_controls(skl->skl_sst); if (ret < 0) diff --git a/sound/soc/intel/skylake/skl-sst-dsp.h b/sound/soc/intel/skylake/skl-sst-dsp.h index 6736c89c6520..207bf3fe8ffd 100644 --- a/sound/soc/intel/skylake/skl-sst-dsp.h +++ b/sound/soc/intel/skylake/skl-sst-dsp.h @@ -241,6 +241,7 @@ struct skl_notify_data { u32 type; u32 length; struct skl_tcn_events tcn_data; + char data[0]; }; struct skl_dsp_notify_ops { diff --git a/sound/soc/intel/skylake/skl-sst-ipc.c b/sound/soc/intel/skylake/skl-sst-ipc.c index cfdf2ce3733b..5a8766acc1e2 100644 --- a/sound/soc/intel/skylake/skl-sst-ipc.c +++ b/sound/soc/intel/skylake/skl-sst-ipc.c @@ -198,6 +198,8 @@ #define IPC_D0IX_STREAMING(x) (((x) & IPC_D0IX_STREAMING_MASK) \ << IPC_D0IX_STREAMING_SHIFT) +/* Offset to get the event data for module notification */ +#define MOD_DATA_OFFSET 12 enum skl_ipc_msg_target { IPC_FW_GEN_MSG = 0, @@ -274,7 +276,8 @@ enum skl_ipc_notification_type { IPC_GLB_NOTIFY_TIMESTAMP_CAPTURED = 7, IPC_GLB_NOTIFY_FW_READY = 8, IPC_GLB_NOTIFY_FW_AUD_CLASS_RESULT = 9, - IPC_GLB_NOTIFY_EXCEPTION_CAUGHT = 10 + IPC_GLB_NOTIFY_EXCEPTION_CAUGHT = 10, + IPC_GLB_MODULE_NOTIFICATION = 12 }; /* Module Message Types */ @@ -355,6 +358,51 @@ static struct ipc_message *skl_ipc_reply_get_msg(struct sst_generic_ipc *ipc, } +static int skl_process_module_notification(struct skl_sst *skl) +{ + struct skl_notify_data *notify_data; + struct skl_module_notify mod_notif; + u32 notify_data_sz; + char *module_data; + + dev_dbg(skl->dev, "***** Module Notification ******\n"); + /* read module notification structure from mailbox */ + sst_dsp_inbox_read(skl->dsp, &mod_notif, + sizeof(struct skl_module_notify)); + + notify_data_sz = sizeof(mod_notif) + mod_notif.event_data_size; + notify_data = kzalloc((sizeof(*notify_data) + notify_data_sz), + GFP_KERNEL); + + if (!notify_data) + return -ENOMEM; + + /* read the complete notification message */ + sst_dsp_inbox_read(skl->dsp, notify_data->data, notify_data_sz); + + notify_data->length = notify_data_sz; + notify_data->type = 0xFF; + + /* Module notification data to console */ + dev_dbg(skl->dev, "Module Id = %#x\n", + (mod_notif.unique_id >> 16)); + dev_dbg(skl->dev, "Instanse Id = %#x\n", + (mod_notif.unique_id & 0x0000FFFF)); + dev_dbg(skl->dev, "Data Size = %d bytes\n", + mod_notif.event_data_size); + + module_data = notify_data->data; + + print_hex_dump(KERN_DEBUG, "DATA: ", MOD_DATA_OFFSET, 8, 4, + module_data, notify_data->length, false); + + skl->notify_ops.notify_cb(skl, IPC_GLB_MODULE_NOTIFICATION, + notify_data); + kfree(notify_data); + + return 0; +} + static void skl_process_log_buffer(struct sst_dsp *sst, struct skl_ipc_header header) { @@ -455,6 +503,14 @@ int skl_ipc_process_notification(struct sst_generic_ipc *ipc, } break; + case IPC_GLB_MODULE_NOTIFICATION: + ret = skl_process_module_notification(skl); + if (ret < 0) { + dev_err(ipc->dev, + "Module Notification read fail:%d\n", ret); + return ret; + } + break; default: dev_err(ipc->dev, "ipc: Unhandled error msg=%x\n", diff --git a/sound/soc/intel/skylake/skl-sst-ipc.h b/sound/soc/intel/skylake/skl-sst-ipc.h index bc083b0a0d4f..5db66e257887 100644 --- a/sound/soc/intel/skylake/skl-sst-ipc.h +++ b/sound/soc/intel/skylake/skl-sst-ipc.h @@ -29,6 +29,7 @@ struct sst_generic_ipc; #define NO_OF_INJECTOR 6 #define NO_OF_EXTRACTOR 8 #define FW_REG_SZ 1024 +#define SKL_EVENT_GLB_MODULE_NOTIFICATION 12 #define SKL_TPLG_CHG_NOTIFY 3 enum skl_ipc_pipeline_state { @@ -244,6 +245,12 @@ struct skl_hw_property_info { u32 ebb_size_bytes; }; +struct skl_notify_kctrl_info { + struct list_head list; + u32 notify_id; + struct snd_kcontrol *notify_kctl; +}; + struct skl_sst { struct device *dev; struct sst_dsp *dsp; @@ -313,6 +320,8 @@ struct skl_sst { struct skl_sysfs_tree *sysfs_tree; struct snd_kcontrol *kcontrol; + + struct list_head notify_kctls; }; struct skl_ipc_init_instance_msg { diff --git a/sound/soc/intel/skylake/skl-topology.c b/sound/soc/intel/skylake/skl-topology.c index 373dbced8efa..df06e446d25a 100644 --- a/sound/soc/intel/skylake/skl-topology.c +++ b/sound/soc/intel/skylake/skl-topology.c @@ -2642,6 +2642,96 @@ int skl_tplg_be_update_params(struct snd_soc_dai *dai, return 0; } +/* + * This function searches notification kcontrol list present in skl_sst + * context against unique notify_id and returns kcontrol pointer if match + * found. + */ +struct snd_kcontrol *skl_search_notify_kctl(struct skl_sst *skl, + u32 notify_id) +{ + struct skl_notify_kctrl_info *kctl_info; + + list_for_each_entry(kctl_info, &skl->notify_kctls, list) { + if (notify_id == kctl_info->notify_id) + return kctl_info->notify_kctl; + } + return NULL; +} + +/* + * This function creates notification kcontrol list by searching control + * list present in snd_card context. It compares kcontrol name with specific + * string "notify params" to get notification kcontrols and add it up to the + * notification list present in skl_sst context. + * NOTE: To use module notification feature, new kcontrol named "notify" should + * be added in topology XML for that particular module. + */ +int skl_create_notify_kctl_list(struct skl_sst *skl_sst, + struct snd_card *card) +{ + struct snd_kcontrol *kctl; + struct snd_soc_dapm_widget *w; + struct skl_module_cfg *mconfig; + struct skl_notify_kctrl_info *info; + u32 size = sizeof(*info); + + list_for_each_entry(kctl, &card->controls, list) { + if (strnstr(kctl->id.name, "notify params", + strlen(kctl->id.name))) { + info = kzalloc(size, GFP_KERNEL); + if (!info) + return -ENOMEM; + + w = snd_soc_dapm_kcontrol_widget(kctl); + mconfig = w->priv; + + /* Module ID (MS word) + Module Instance ID (LS word) */ + info->notify_id = ((mconfig->id.module_id << 16) | + (mconfig->id.instance_id)); + info->notify_kctl = kctl; + + list_add_tail(&info->list, &skl_sst->notify_kctls); + } + } + return 0; +} + +/* + * This function deletes notification kcontrol list from skl_sst + * context. + */ +void skl_delete_notify_kctl_list(struct skl_sst *skl_sst) +{ + struct skl_notify_kctrl_info *info, *tmp; + + list_for_each_entry_safe(info, tmp, &skl_sst->notify_kctls, list) { + list_del(&info->list); + kfree(info); + } +} + +/* + * This function creates notification kcontrol list on first module + * notification from firmware. It also search notification kcontrol + * list against unique notify_id sent from firmware and returns the + * corresponding kcontrol pointer. + */ +struct snd_kcontrol *skl_get_notify_kcontrol(struct skl_sst *skl, + struct snd_card *card, u32 notify_id) +{ + struct snd_kcontrol *kctl = NULL; + + if (list_empty(&skl->notify_kctls)) + skl_create_notify_kctl_list(skl, card); + + kctl = skl_search_notify_kctl(skl, notify_id); + + return kctl; +} + + + /* * Get the events along with data stored in notify_data and pass * to kcontrol private data. @@ -2683,7 +2773,24 @@ int skl_dsp_cb_event(struct skl_sst *ctx, unsigned int event, snd_ctl_notify(card->snd_card, SNDRV_CTL_EVENT_MASK_VALUE, &ctx->kcontrol->id); break; + case SKL_EVENT_GLB_MODULE_NOTIFICATION: + m_notification = (struct skl_module_notify *)notify_data->data; + card = component->card; + ctx->kcontrol = skl_get_notify_kcontrol(ctx, card->snd_card, + m_notification->unique_id); + if (!ctx->kcontrol) { + dev_dbg(ctx->dev, "Module notify control not found\n"); + return -EINVAL; + } + sb = (struct soc_bytes_ext *)ctx->kcontrol->private_value; + bc = (struct skl_algo_data *)sb->dobj.private; + param_length = sizeof(struct skl_notify_data) + + notify_data->length; + memcpy(bc->params, (char *)notify_data, param_length); + snd_ctl_notify(card->snd_card, + SNDRV_CTL_EVENT_MASK_VALUE, &ctx->kcontrol->id); + break; default: return -EINVAL; } diff --git a/sound/soc/intel/skylake/skl-topology.h b/sound/soc/intel/skylake/skl-topology.h index ce4069607dda..4c09a0896e42 100644 --- a/sound/soc/intel/skylake/skl-topology.h +++ b/sound/soc/intel/skylake/skl-topology.h @@ -617,4 +617,12 @@ int skl_dsp_crash_dump_read(struct skl_sst *ctx); int skl_tplg_change_notification_get(struct snd_kcontrol *kcontrol, unsigned int __user *data, unsigned int size); +struct snd_kcontrol *skl_search_notify_kctl(struct skl_sst *skl, + u32 notify_id); +int skl_create_notify_kctl_list(struct skl_sst *skl_sst, + struct snd_card *card); +void skl_delete_notify_kctl_list(struct skl_sst *skl_sst); +struct snd_kcontrol *skl_get_notify_kcontrol(struct skl_sst *skl, + struct snd_card *card, u32 notify_id); + #endif diff --git a/sound/soc/intel/skylake/skl.c b/sound/soc/intel/skylake/skl.c index c1fa7f6aa821..a848eae708c8 100644 --- a/sound/soc/intel/skylake/skl.c +++ b/sound/soc/intel/skylake/skl.c @@ -37,6 +37,7 @@ #include "skl.h" #include "skl-sst-dsp.h" #include "skl-sst-ipc.h" +#include "skl-topology.h" /* * initialize the PCI registers @@ -1091,6 +1092,7 @@ static void skl_remove(struct pci_dev *pci) struct hdac_bus *bus = pci_get_drvdata(pci); struct skl *skl = bus_to_skl(bus); + skl_delete_notify_kctl_list(skl->skl_sst); release_firmware(skl->tplg); pm_runtime_get_noresume(&pci->dev); From 06c92ac802b6b250e2085e41ebd4f8b7446b6187 Mon Sep 17 00:00:00 2001 From: "Kareem,Shaik" Date: Wed, 30 Aug 2017 16:46:40 +0530 Subject: [PATCH 0794/1103] ASoC: Intel: Board: Add pm_ops to fix suspend/resume issue Audio playback not resumed after it is suspended. Add snd_soc_pm_ops to execute power management operation. Change-Id: I84ccf6a0ac7e35c1f79971ee59555f24024d4309 Signed-off-by: Mohit Sinha Reviewed-on: Reviewed-by: Shaik, Kareem M Reviewed-by: Prusty, Subhransu S Reviewed-by: H S, Vijay Reviewed-by: Kp, Jeeja Reviewed-by: audio_build Reviewed-by: Koul, Vinod Tested-by: Avati, Santosh Kumar --- sound/soc/intel/boards/bxt_tdf8532.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sound/soc/intel/boards/bxt_tdf8532.c b/sound/soc/intel/boards/bxt_tdf8532.c index 325b59adaf1c..c7b7fe3f9ed7 100644 --- a/sound/soc/intel/boards/bxt_tdf8532.c +++ b/sound/soc/intel/boards/bxt_tdf8532.c @@ -240,6 +240,7 @@ static struct platform_driver broxton_tdf8532_audio = { .remove = broxton_tdf8532_audio_remove, .driver = { .name = "bxt_tdf8532", + .pm = &snd_soc_pm_ops, }, }; From 4f6892e2850c5a535e14058e6ff91d3307c782af Mon Sep 17 00:00:00 2001 From: Guneshwor Singh Date: Thu, 7 Sep 2017 09:56:44 +0530 Subject: [PATCH 0795/1103] ASoC: rt700: Remove unused variable Compiler throws warning for unused variable, so remove it. Signed-off-by: Guneshwor Singh --- sound/soc/codecs/rt700.c | 1 - 1 file changed, 1 deletion(-) diff --git a/sound/soc/codecs/rt700.c b/sound/soc/codecs/rt700.c index a654c092b8c5..952140d5fbbe 100644 --- a/sound/soc/codecs/rt700.c +++ b/sound/soc/codecs/rt700.c @@ -1789,7 +1789,6 @@ static int rt700_runtime_resume(struct device *dev) { struct rt700_priv *rt700 = dev_get_drvdata(dev); int ret; - int timeout = 0; if (rt700->sdw) { ret = sdw_wait_for_slave_enumeration(rt700->sdw->mstr, From 8bdf59a664ce44f436807d3e93e9a7aef7d73e5e Mon Sep 17 00:00:00 2001 From: Guneshwor Singh Date: Thu, 7 Sep 2017 09:58:03 +0530 Subject: [PATCH 0796/1103] ASoC: Intel: board: Remove unused function cnl_dmic_fixup cnl_dmic_fixup is never used, remove it to shun compiler warning. Signed-off-by: Guneshwor Singh --- sound/soc/intel/boards/cnl_cs42l42.c | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/sound/soc/intel/boards/cnl_cs42l42.c b/sound/soc/intel/boards/cnl_cs42l42.c index 879aa4e552f3..bf5885b59a74 100644 --- a/sound/soc/intel/boards/cnl_cs42l42.c +++ b/sound/soc/intel/boards/cnl_cs42l42.c @@ -162,16 +162,6 @@ static int cnl_cs42l42_codec_fixup(struct snd_soc_pcm_runtime *rtd, return 0; } -static int cnl_dmic_fixup(struct snd_soc_pcm_runtime *rtd, - struct snd_pcm_hw_params *params) -{ - struct snd_interval *channels = hw_param_interval(params, - SNDRV_PCM_HW_PARAM_CHANNELS); - channels->min = channels->max = 2; - - return 0; -} - struct snd_soc_dai_link cnl_cs42l42_msic_dailink[] = { { .name = "Bxtn Audio Port", From f908bba3ff90b520b3fb5b15f90e675eba89616a Mon Sep 17 00:00:00 2001 From: Guneshwor Singh Date: Thu, 7 Sep 2017 09:59:36 +0530 Subject: [PATCH 0797/1103] ASoC: Intel: Skylake: Move prototype to appropriate header MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit skl_dsp_crash_dump_read is defined in skl-sst-ipc.c and but skl-topology.h in which its prototype lives, is not included in skl-sst-ipc.c. So move the prototype to skl-sst-ipc.h so as to avoid compiler warning below: warning: no previous prototype for ‘skl_dsp_crash_dump_read’ [-Wmissing-prototypes] Signed-off-by: Guneshwor Singh --- sound/soc/intel/skylake/skl-sst-ipc.h | 2 ++ sound/soc/intel/skylake/skl-topology.h | 2 -- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/sound/soc/intel/skylake/skl-sst-ipc.h b/sound/soc/intel/skylake/skl-sst-ipc.h index 5db66e257887..30ba9a9e708d 100644 --- a/sound/soc/intel/skylake/skl-sst-ipc.h +++ b/sound/soc/intel/skylake/skl-sst-ipc.h @@ -472,4 +472,6 @@ int skl_ipc_process_notification(struct sst_generic_ipc *ipc, void skl_ipc_tx_data_copy(struct ipc_message *msg, char *tx_data, size_t tx_size); int skl_notify_tplg_change(struct skl_sst *ctx, int type); +int skl_dsp_crash_dump_read(struct skl_sst *ctx); + #endif /* __SKL_IPC_H */ diff --git a/sound/soc/intel/skylake/skl-topology.h b/sound/soc/intel/skylake/skl-topology.h index 4c09a0896e42..12639e4bba76 100644 --- a/sound/soc/intel/skylake/skl-topology.h +++ b/sound/soc/intel/skylake/skl-topology.h @@ -613,8 +613,6 @@ int skl_tplg_dsp_log_set(struct snd_kcontrol *kcontrol, int skl_dai_load(struct snd_soc_component *cmp, struct snd_soc_dai_driver *pcm_dai); -int skl_dsp_crash_dump_read(struct skl_sst *ctx); - int skl_tplg_change_notification_get(struct snd_kcontrol *kcontrol, unsigned int __user *data, unsigned int size); struct snd_kcontrol *skl_search_notify_kctl(struct skl_sst *skl, From 88aed42ffd66cb568c6c262c44d3c2a842d02f7b Mon Sep 17 00:00:00 2001 From: Guneshwor Singh Date: Mon, 18 Sep 2017 11:03:25 +0530 Subject: [PATCH 0798/1103] ASoC: Intel: cnl: Move d0i[03] ops after cnl_load_base_firmware Since ipc's are sent during cnl_load_base_firmware and there's no need to check d0i0 state at the time of firmware load, move the ops assignments after loading firmware. Signed-off-by: Guneshwor Singh --- sound/soc/intel/skylake/cnl-sst.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/sound/soc/intel/skylake/cnl-sst.c b/sound/soc/intel/skylake/cnl-sst.c index 9441a724ea59..c99b2c931202 100644 --- a/sound/soc/intel/skylake/cnl-sst.c +++ b/sound/soc/intel/skylake/cnl-sst.c @@ -755,14 +755,9 @@ int cnl_sst_dsp_init(struct device *dev, void __iomem *mmio_base, int irq, return ret; } - /* set the D0i3 check */ - cnl->ipc.ops.check_dsp_lp_on = skl_ipc_check_D0i0; cnl->boot_complete = false; init_waitqueue_head(&cnl->boot_wait); - INIT_DELAYED_WORK(&cnl->d0i3.work, bxt_set_dsp_D0i3); - cnl->d0i3.state = SKL_DSP_D0I3_NONE; - ret = skl_dsp_acquire_irq(sst); if (ret < 0) return ret; @@ -773,6 +768,12 @@ int cnl_sst_dsp_init(struct device *dev, void __iomem *mmio_base, int irq, return ret; } + /* set the D0i3 check */ + cnl->ipc.ops.check_dsp_lp_on = skl_ipc_check_D0i0; + + INIT_DELAYED_WORK(&cnl->d0i3.work, bxt_set_dsp_D0i3); + cnl->d0i3.state = SKL_DSP_D0I3_NONE; + #if IS_ENABLED(CONFIG_SND_SOC_RT700) ret = skl_register_sdw_masters(dev, cnl, mmio_base, irq, ptr); if (ret) { From 1072e9c45bb10f9f6056a2fbe4fedc12daa29040 Mon Sep 17 00:00:00 2001 From: Mousumi Jana Date: Wed, 30 Aug 2017 20:52:51 +0530 Subject: [PATCH 0799/1103] ASoC: Intel: Skylake: Support for Probe DMA Buffer size This patch removes hard coding of buffer size for Probe Module. For probe, DMA Buffer size is calculated as- 2 * [Probe Hdr + ((SRate/1000)*(ch)*(sch_time_ms)*(bytes per sample)) + Probe Footer] where, Probe Header and footer are 24 and 8 bytes respectively. Hence this value is topology specific and is to be used from XML as dma_buffer_size value for Probe Module. Change-Id: I2f1d388d5e4f77731f7fa3753eac4550bd5ae57b Signed-off-by: Mohit Sinha Reviewed-on: Reviewed-by: Shaik, Kareem M Reviewed-by: Singh, Guneshwor O Reviewed-by: Kesapragada, Pardha Saradhi Reviewed-by: Koul, Vinod Tested-by: Sm, Bhadur A --- sound/soc/intel/skylake/skl-messages.c | 5 +++++ sound/soc/intel/skylake/skl-probe.c | 8 -------- sound/soc/intel/skylake/skl-topology.c | 15 +-------------- 3 files changed, 6 insertions(+), 22 deletions(-) diff --git a/sound/soc/intel/skylake/skl-messages.c b/sound/soc/intel/skylake/skl-messages.c index ea500be81329..6cf06fa8293a 100644 --- a/sound/soc/intel/skylake/skl-messages.c +++ b/sound/soc/intel/skylake/skl-messages.c @@ -1782,8 +1782,13 @@ static void skl_setup_probe_gateway_cfg(struct skl_sst *ctx, struct skl_probe_cfg *probe_cfg) { union skl_connector_node_id node_id = {0}; + struct skl_module_res *res; struct skl_probe_config *pconfig = &ctx->probe_config; + res = &mconfig->module->resources[mconfig->res_idx]; + + pconfig->edma_buffsize = res->dma_buffer_size; + node_id.node.dma_type = pconfig->edma_type; node_id.node.vindex = pconfig->edma_id; probe_cfg->prb_cfg.dma_buffer_size = pconfig->edma_buffsize; diff --git a/sound/soc/intel/skylake/skl-probe.c b/sound/soc/intel/skylake/skl-probe.c index b563dc38dce8..0716bf62f92e 100644 --- a/sound/soc/intel/skylake/skl-probe.c +++ b/sound/soc/intel/skylake/skl-probe.c @@ -33,13 +33,6 @@ #define USE_SPIB 0 -/* - * DMA buffer size needed for 48KHz, 4 channel, 32 bit data - * scheduled at 4ms for 2 probe packets is - * 2* [ 24 + (48*4*4*32/8) + 8] = 6208. - */ -#define SKL_EXTRACT_PROBE_DMA_BUFF_SIZE 6208 - /* * ======================== * PROBE STATE TRANSITIONS: @@ -103,7 +96,6 @@ int skl_probe_compr_open(struct snd_compr_stream *substream, dev_dbg(dai->dev, "%s dev is %s\n", __func__, dev_name(dai->dev)); if ((pconfig->i_refc + pconfig->e_refc) == 0) { - pconfig->edma_buffsize = SKL_EXTRACT_PROBE_DMA_BUFF_SIZE; pconfig->edma_type = SKL_DMA_HDA_HOST_INPUT_CLASS; /* * Extractor DMA is to be assigned when the first probe diff --git a/sound/soc/intel/skylake/skl-topology.c b/sound/soc/intel/skylake/skl-topology.c index df06e446d25a..4a7b7d3c67ec 100644 --- a/sound/soc/intel/skylake/skl-topology.c +++ b/sound/soc/intel/skylake/skl-topology.c @@ -37,12 +37,6 @@ #define SKL_FMT_FIXUP_MASK (1 << 2) #define SKL_IN_DIR_BIT_MASK BIT(0) #define SKL_PIN_COUNT_MASK GENMASK(7, 4) -/* - * DMA buffer size needed for 48KHz, 4 channel, 32 bit data - * scheduled at 4ms for 2 probe packets is - * 2* [ 24 + (48*4*4*32/8) + 8] = 6208. - */ -#define SKL_INJECT_PROBE_DMA_BUFF_SIZE 6208 static const int mic_mono_list[] = { 0, 1, 2, 3, @@ -109,13 +103,6 @@ void skl_tplg_d0i3_put(struct skl *skl, enum d0i3_capability caps) } } -/* - * DMA buffer size needed for 48KHz, 4 channel, 32 bit data - * scheduled at 4ms for 2 probe packets is - * 2* [ 24 + (48*4*4*32/8) + 8] = 6208. - */ -#define SKL_INJECT_PROBE_DMA_BUFF_SIZE 6208 - /* * SKL DSP driver modelling uses only few DAPM widgets so for rest we will * ignore. This helpers checks if the SKL driver handles this widget type @@ -531,7 +518,7 @@ int skl_probe_attach_inj_dma(struct snd_soc_dapm_widget *w, ad.node_id.node.vindex = pconfig->iprobe[index].dma_id; ad.node_id.node.dma_type = SKL_DMA_HDA_HOST_OUTPUT_CLASS; ad.node_id.node.rsvd = 0; - ad.dma_buff_size = SKL_INJECT_PROBE_DMA_BUFF_SIZE; + ad.dma_buff_size = pconfig->edma_buffsize; ret = skl_set_module_params(ctx, (void *)&ad, sizeof(struct skl_probe_attach_inj_dma), From 8065fd080f2d6ba654833ee2deab76db4cef4728 Mon Sep 17 00:00:00 2001 From: Pardha Saradhi K Date: Mon, 21 Aug 2017 22:03:10 +0530 Subject: [PATCH 0800/1103] ASoC: Intel: Skylake: Add a separate module type for ASRC algo ASRC algorithm has the same interfaces like the SRC algorithm. This patch distinguishes both of them with similar names so as to enable using them in the same topology One parameter called mode has been added to ASRC init structure whose value is dependent on stream direction. Recommendation from the FW team is to set asrc mode = 2 for capture and asrc mode = 1 for playback for better signal quality Change-Id: I26961f8601bbaba71ebd3944438ebfa294774037 Signed-off-by: Sameer Sharma Signed-off-by: Pardha Saradhi K Reviewed-on: Reviewed-by: Sinha, Mohit Reviewed-by: Singh, Guneshwor O Reviewed-by: Koul, Vinod Tested-by: Sm, Bhadur A --- include/uapi/sound/skl-tplg-interface.h | 3 ++- sound/soc/intel/skylake/skl-messages.c | 13 +++++++++++++ sound/soc/intel/skylake/skl-topology.h | 1 + 3 files changed, 16 insertions(+), 1 deletion(-) diff --git a/include/uapi/sound/skl-tplg-interface.h b/include/uapi/sound/skl-tplg-interface.h index 89844fb10aa5..a3f0890c75bd 100644 --- a/include/uapi/sound/skl-tplg-interface.h +++ b/include/uapi/sound/skl-tplg-interface.h @@ -81,7 +81,8 @@ enum skl_module_type { SKL_MODULE_TYPE_BASE_OUTFMT, SKL_MODULE_TYPE_KPB, SKL_MODULE_TYPE_MIC_SELECT, - SKL_MODULE_TYPE_PROBE + SKL_MODULE_TYPE_PROBE, + SKL_MODULE_TYPE_ASRC }; enum skl_core_affinity { diff --git a/sound/soc/intel/skylake/skl-messages.c b/sound/soc/intel/skylake/skl-messages.c index 6cf06fa8293a..d0d60d740441 100644 --- a/sound/soc/intel/skylake/skl-messages.c +++ b/sound/soc/intel/skylake/skl-messages.c @@ -34,6 +34,9 @@ #include #include +#define ASRC_MODE_UPLINK 2 +#define ASRC_MODE_DOWNLINK 1 + static int skl_alloc_dma_buf(struct device *dev, struct snd_dma_buffer *dmab, size_t size) { @@ -1736,6 +1739,14 @@ static void skl_set_src_format(struct skl_sst *ctx, (struct skl_base_cfg *)src_mconfig); src_mconfig->src_cfg = fmt->s_freq; + + if (mconfig->m_type == SKL_MODULE_TYPE_ASRC) { + if (mconfig->pipe->p_params->stream == + SNDRV_PCM_STREAM_PLAYBACK) + src_mconfig->mode = ASRC_MODE_DOWNLINK; + else + src_mconfig->mode = ASRC_MODE_UPLINK; + } } /* @@ -1862,6 +1873,7 @@ static u16 skl_get_module_param_size(struct skl_sst *ctx, return sizeof(struct skl_probe_cfg); case SKL_MODULE_TYPE_SRCINT: + case SKL_MODULE_TYPE_ASRC: return sizeof(struct skl_src_module_cfg); case SKL_MODULE_TYPE_UPDWMIX: @@ -1920,6 +1932,7 @@ static int skl_set_module_format(struct skl_sst *ctx, break; case SKL_MODULE_TYPE_SRCINT: + case SKL_MODULE_TYPE_ASRC: skl_set_src_format(ctx, module_config, *param_data); break; diff --git a/sound/soc/intel/skylake/skl-topology.h b/sound/soc/intel/skylake/skl-topology.h index 12639e4bba76..f9ac42cdc64d 100644 --- a/sound/soc/intel/skylake/skl-topology.h +++ b/sound/soc/intel/skylake/skl-topology.h @@ -167,6 +167,7 @@ struct skl_cpr_pin_fmt { struct skl_src_module_cfg { struct skl_base_cfg base_cfg; enum skl_s_freq src_cfg; + u32 mode; } __packed; struct notification_mask { From 1e6857766a214f60469f1716ab291c61b40f4d8c Mon Sep 17 00:00:00 2001 From: Pardha Saradhi K Date: Tue, 5 Sep 2017 19:57:00 +0530 Subject: [PATCH 0801/1103] ASoC: Intel: Skylake: Add support for DMA Buffer configuration DMA buffer configuration is to manage the HW Buffers at gateway copiers. This information configured from topology and sent as an IPC to the ADSP after firmware download and subsequent D0/D3 cycles. If the topology doesn't have DMA buffer configuration, no IPC would be sent and the HW buffer organization lies with the FW. Change-Id: I7d031b7a5c76c5b4943007b216f2ac515435c0fc Signed-off-by: Gogineni, GiribabuX Signed-off-by: Pardha Saradhi K Reviewed-on: Reviewed-by: Shaik, Kareem M Reviewed-by: Sinha, Mohit Reviewed-by: Koul, Vinod Reviewed-by: audio_build Tested-by: Sm, Bhadur A --- include/uapi/sound/snd_sst_tokens.h | 20 +++++++++++++++++++- sound/soc/intel/skylake/bxt-sst.c | 3 --- sound/soc/intel/skylake/skl-messages.c | 6 +++++- sound/soc/intel/skylake/skl-pcm.c | 6 ++++++ sound/soc/intel/skylake/skl-sst-ipc.c | 20 ++++++++++++++++++++ sound/soc/intel/skylake/skl-sst-ipc.h | 2 ++ sound/soc/intel/skylake/skl-topology.c | 22 ++++++++++++++++++++++ sound/soc/intel/skylake/skl.h | 16 +++++++++++++++- 8 files changed, 89 insertions(+), 6 deletions(-) diff --git a/include/uapi/sound/snd_sst_tokens.h b/include/uapi/sound/snd_sst_tokens.h index 0f74eeb85995..b29d07b018ea 100644 --- a/include/uapi/sound/snd_sst_tokens.h +++ b/include/uapi/sound/snd_sst_tokens.h @@ -249,6 +249,19 @@ * %SKL_TKN_U32_DMACTRL_CFG_SIZE: * Size information of DMA control params * + * %SKL_TKN_U32_DMA_IDX Config index to fill up DMA config info + * from the manifest. + * + * %SKL_TKN_U32_DMA_TYPE: Types of FW configs + * DMA_CONFIG, SCHEDULER_CONFIG. + * + * %SKL_TKN_U32_DMA_SIZE: DMA buffer Size + * + * %SKL_TKN_U32_DMA_MAX_SIZE: Maximum DMA buffer size + * + * %SKL_TKN_U32_DMA_MIN_SIZE: Minimum DMA buffer size + * + * * module_id and loadable flags dont have tokens as these values will be * read from the DSP FW manifest * @@ -347,7 +360,12 @@ enum SKL_TKNS { SKL_TKN_U32_AGG_ID, SKL_TKN_U32_DMACTRL_CFG_IDX, SKL_TKN_U32_DMACTRL_CFG_SIZE, - SKL_TKN_MAX = SKL_TKN_U32_DMACTRL_CFG_SIZE, + SKL_TKN_U32_DMA_IDX, + SKL_TKN_U32_DMA_TYPE, + SKL_TKN_U32_DMA_SIZE, + SKL_TKN_U32_DMA_MAX_SIZE, + SKL_TKN_U32_DMA_MIN_SIZE, + SKL_TKN_MAX = SKL_TKN_U32_DMA_MIN_SIZE, }; /* diff --git a/sound/soc/intel/skylake/bxt-sst.c b/sound/soc/intel/skylake/bxt-sst.c index c7f7c1529354..406d278555f0 100644 --- a/sound/soc/intel/skylake/bxt-sst.c +++ b/sound/soc/intel/skylake/bxt-sst.c @@ -45,9 +45,6 @@ #define BXT_ADSP_SRAM1_BASE 0xA0000 -#define BXT_INSTANCE_ID 0 -#define BXT_BASE_FW_MODULE_ID 0 - #define BXT_ADSP_FW_BIN_HDR_OFFSET 0x2000 /* Delay before scheduling D0i3 entry */ diff --git a/sound/soc/intel/skylake/skl-messages.c b/sound/soc/intel/skylake/skl-messages.c index d0d60d740441..02b3aad155f1 100644 --- a/sound/soc/intel/skylake/skl-messages.c +++ b/sound/soc/intel/skylake/skl-messages.c @@ -1354,7 +1354,11 @@ int skl_resume_dsp(struct skl *skl) skl_dsp_set_astate_cfg(skl->skl_sst, skl->cfg.astate_cfg->count, skl->cfg.astate_cfg); } - return ret; + + /* Set DMA buffer configuration */ + if (skl->cfg.dmacfg.size) + skl_ipc_set_dma_cfg(&skl->skl_sst->ipc, BXT_INSTANCE_ID, + BXT_BASE_FW_MODULE_ID, (u32 *)(&skl->cfg.dmacfg)); /* Set DMA clock controls */ return skl_dsp_set_dma_clk_controls(skl->skl_sst); diff --git a/sound/soc/intel/skylake/skl-pcm.c b/sound/soc/intel/skylake/skl-pcm.c index 8209c954b7ad..889fd8680629 100644 --- a/sound/soc/intel/skylake/skl-pcm.c +++ b/sound/soc/intel/skylake/skl-pcm.c @@ -1852,6 +1852,12 @@ static int skl_platform_soc_probe(struct snd_soc_component *component) return ret; } + /* Set DMA buffer configuration */ + if (skl->cfg.dmacfg.size) + skl_ipc_set_dma_cfg(&skl->skl_sst->ipc, + BXT_INSTANCE_ID, BXT_BASE_FW_MODULE_ID, + (u32 *)(&skl->cfg.dmacfg)); + /* Set DMA clock controls */ skl_dsp_set_dma_clk_controls(skl->skl_sst); diff --git a/sound/soc/intel/skylake/skl-sst-ipc.c b/sound/soc/intel/skylake/skl-sst-ipc.c index 5a8766acc1e2..d7c75da0625f 100644 --- a/sound/soc/intel/skylake/skl-sst-ipc.c +++ b/sound/soc/intel/skylake/skl-sst-ipc.c @@ -200,6 +200,7 @@ /* Offset to get the event data for module notification */ #define MOD_DATA_OFFSET 12 +#define SET_LARGE_CFG_FW_CONFIG 7 enum skl_ipc_msg_target { IPC_FW_GEN_MSG = 0, @@ -1164,6 +1165,25 @@ int skl_ipc_get_large_config(struct sst_generic_ipc *ipc, } EXPORT_SYMBOL_GPL(skl_ipc_get_large_config); +void skl_ipc_set_dma_cfg(struct sst_generic_ipc *ipc, u8 instance_id, + u16 module_id, u32 *data) +{ + struct skl_ipc_large_config_msg msg = {0}; + u32 size_offset = 1; + int ret; + + msg.module_id = module_id; + msg.instance_id = instance_id; + msg.large_param_id = SET_LARGE_CFG_FW_CONFIG; + /* size of total message = size of payload + size of headers*/ + msg.param_data_size = data[size_offset] + (2 * sizeof(u32)); + + ret = skl_ipc_set_large_config(ipc, &msg, data); + if (ret < 0) + dev_err(ipc->dev, "ipc: set dma config failed, err %d\n", ret); +} +EXPORT_SYMBOL_GPL(skl_ipc_set_dma_cfg); + int skl_sst_ipc_load_library(struct sst_generic_ipc *ipc, u8 dma_id, u8 table_id, bool wait) { diff --git a/sound/soc/intel/skylake/skl-sst-ipc.h b/sound/soc/intel/skylake/skl-sst-ipc.h index 30ba9a9e708d..76e83d216e49 100644 --- a/sound/soc/intel/skylake/skl-sst-ipc.h +++ b/sound/soc/intel/skylake/skl-sst-ipc.h @@ -474,4 +474,6 @@ void skl_ipc_tx_data_copy(struct ipc_message *msg, char *tx_data, int skl_notify_tplg_change(struct skl_sst *ctx, int type); int skl_dsp_crash_dump_read(struct skl_sst *ctx); +void skl_ipc_set_dma_cfg(struct sst_generic_ipc *ipc, u8 instance_id, + u16 module_id, u32 *data); #endif /* __SKL_IPC_H */ diff --git a/sound/soc/intel/skylake/skl-topology.c b/sound/soc/intel/skylake/skl-topology.c index 4a7b7d3c67ec..1a40c66e1c58 100644 --- a/sound/soc/intel/skylake/skl-topology.c +++ b/sound/soc/intel/skylake/skl-topology.c @@ -4232,6 +4232,7 @@ static int skl_tplg_get_int_tkn(struct device *dev, { int tkn_count = 0, ret, size; static int mod_idx, res_val_idx, intf_val_idx, dir, pin_idx; + static int dma_cfg_idx; struct skl_module_res *res = NULL; struct skl_module_iface *fmt = NULL; struct skl_module *mod = NULL; @@ -4307,6 +4308,27 @@ static int skl_tplg_get_int_tkn(struct device *dev, case SKL_TKN_U32_ASTATE_CLK_SRC: astate_table[astate_cfg_idx].clk_src = tkn_elem->value; + + case SKL_TKN_U32_DMA_TYPE: + skl->cfg.dmacfg.type = tkn_elem->value; + break; + + case SKL_TKN_U32_DMA_SIZE: + skl->cfg.dmacfg.size = tkn_elem->value; + break; + + case SKL_TKN_U32_DMA_IDX: + dma_cfg_idx = tkn_elem->value; + break; + + case SKL_TKN_U32_DMA_MIN_SIZE: + skl->cfg.dmacfg.dma_cfg[dma_cfg_idx].min_size = + tkn_elem->value; + break; + + case SKL_TKN_U32_DMA_MAX_SIZE: + skl->cfg.dmacfg.dma_cfg[dma_cfg_idx].max_size = + tkn_elem->value; break; case SKL_TKN_U8_IN_PIN_TYPE: diff --git a/sound/soc/intel/skylake/skl.h b/sound/soc/intel/skylake/skl.h index 318b3c54c44e..c8a2c27d971d 100644 --- a/sound/soc/intel/skylake/skl.h +++ b/sound/soc/intel/skylake/skl.h @@ -47,8 +47,10 @@ #define AZX_EM2_DUM_MASK (1 << 23) #define AZX_REG_VS_EM2_L1SEN BIT(13) -#define SKL_MAX_DMA_CFG 24 #define SKL_MAX_DMACTRL 7 +#define SKL_MAX_DMA_CFG 24 +#define BXT_INSTANCE_ID 0 +#define BXT_BASE_FW_MODULE_ID 0 struct skl_dsp_resource { u32 max_mcps; @@ -69,6 +71,17 @@ struct skl_astate_config { struct skl_astate_param astate_table[0]; }; +struct skl_dma_config { + u32 min_size; + u32 max_size; +} __packed; + +struct skl_dma_buff_cfg { + u32 type; + u32 size; + struct skl_dma_config dma_cfg[SKL_MAX_DMA_CFG]; +} __packed; + struct skl_dmctrl_hdr { u32 vbus_id; u32 freq; @@ -88,6 +101,7 @@ struct skl_dmactrl_config { } __packed; struct skl_fw_config { + struct skl_dma_buff_cfg dmacfg; struct skl_dmactrl_config dmactrl_cfg; struct skl_astate_config *astate_cfg; }; From 3830b22436149308887f17398189d3e2673a0b12 Mon Sep 17 00:00:00 2001 From: Mousumi Jana Date: Tue, 12 Sep 2017 17:18:44 +0530 Subject: [PATCH 0802/1103] ASoC: Intel: Set all I2S ports to slave mode after DSP power up in BXTP During DSP power up sequences, the I2S ports default to Master mode. This drives frame sync and bit clock high and may cause distortion issues on peripherals in some boards. To prevent this, the ports should be set slave mode before the DSP boot. Change-Id: Id8f96989d35674acad89f7080f58e7682bcd81dc Signed-off-by: Sameer Sharma Signed-off-by: Mousumi Jana Signed-off-by: Mohit Sinha Reviewed-on: Reviewed-by: Singh, Guneshwor O Reviewed-by: Shaik, Kareem M Reviewed-by: Gogineni, GiribabuX Reviewed-by: Koul, Vinod Reviewed-by: audio_build Tested-by: Sm, Bhadur A --- sound/soc/intel/skylake/bxt-sst.c | 45 +++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/sound/soc/intel/skylake/bxt-sst.c b/sound/soc/intel/skylake/bxt-sst.c index 406d278555f0..dd5453daa562 100644 --- a/sound/soc/intel/skylake/bxt-sst.c +++ b/sound/soc/intel/skylake/bxt-sst.c @@ -32,6 +32,15 @@ #define BXT_ROM_INIT 0x5 #define BXT_ADSP_SRAM0_BASE 0x80000 +/* BXT SSP/I2S Registers */ +#define I2S_SSC1_REG_OFF BIT(2) +#define SET_SLAVE_MASK GENMASK(25, 24) + +/*BXT I2S Clock Gating*/ +#define BXT_DSP_CLK_CTL 0x378 +#define BXT_DISABLE_4_SSP_CLK_GT GENMASK(21, 18) +#define BXT_DISABLE_ALL_SSP_CLK_GT GENMASK(23, 18) + /* Trace Buffer Window */ #define BXT_ADSP_SRAM2_BASE 0x0C0000 #define BXT_ADSP_W2_SIZE 0x2000 @@ -52,6 +61,36 @@ #define BXT_FW_ROM_INIT_RETRY 3 +#define GET_SSP_BASE(N) (N > 4 ? 0x2000 : 0x4000) + +#define BXTP_NUM_I2S_PORTS 6 + +static void bxt_set_ssp_slave(struct sst_dsp *ctx) +{ + u32 mask, i2s_base_addr; + int i; + + if (BXTP_NUM_I2S_PORTS == 4) + mask = BXT_DISABLE_4_SSP_CLK_GT; + else + mask = BXT_DISABLE_ALL_SSP_CLK_GT; + + /* disable clock gating on all SSPs */ + sst_dsp_shim_update_bits_unlocked(ctx, + BXT_DSP_CLK_CTL, mask, mask); + + /* set all SSPs to slave */ + i2s_base_addr = GET_SSP_BASE(BXTP_NUM_I2S_PORTS); + for (i = 0; i < BXTP_NUM_I2S_PORTS; i++) { + sst_dsp_shim_update_bits_unlocked(ctx, + (i2s_base_addr + (i * 0x1000) + I2S_SSC1_REG_OFF), + SET_SLAVE_MASK, SET_SLAVE_MASK); + } + + /* re-enable clock gating */ + sst_dsp_shim_update_bits_unlocked(ctx, BXT_DSP_CLK_CTL, mask, 0); +} + static unsigned int bxt_get_errorcode(struct sst_dsp *ctx) { return sst_dsp_shim_read(ctx, BXT_ADSP_ERROR_CODE); @@ -134,6 +173,9 @@ static int sst_bxt_prepare_fw(struct sst_dsp *ctx, goto base_fw_load_failed; } + /* DSP is powered up, set all SSPs to slave mode */ + bxt_set_ssp_slave(ctx); + /* Step 2: Purge FW request */ sst_dsp_shim_write(ctx, SKL_ADSP_REG_HIPCI, SKL_ADSP_REG_HIPCI_BUSY | (BXT_IPC_PURGE_FW | ((stream_tag - 1) << 9))); @@ -448,6 +490,9 @@ static int bxt_set_dsp_D0(struct sst_dsp *ctx, unsigned int core_id) if (core_id == SKL_DSP_CORE0_ID) { + /* set all SSPs to slave mode */ + bxt_set_ssp_slave(ctx); + /* * Enable interrupt after SPA is set and before * DSP is unstalled From 29c7b1a7c5130e938bae5ecfa3738445ba6f8956 Mon Sep 17 00:00:00 2001 From: Imed BEN ROMDHANE Date: Tue, 12 Sep 2017 20:44:07 +0530 Subject: [PATCH 0803/1103] ASoC: Intel: Skylake: Return default sampling rate for Trace compress devices. Defining sampling rate for getting FW logs from FwLogging/Trace compress devices. As user space expects a valid sample rate but existing API compress_get_hpointer returns an error if sampling rate is 0. Change-Id: Ie4eea4850c6cfca9e24bdf3af881b9afd96d2a50 Signed-off-by: Sameer Sharma Reviewed-on: Reviewed-by: Kale, Sanyog R Reviewed-by: Shaik, Kareem M Reviewed-by: Sinha, Mohit Reviewed-by: Gogineni, GiribabuX Reviewed-by: Koul, Vinod Tested-by: Sm, Bhadur A --- sound/soc/intel/skylake/skl-pcm.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/sound/soc/intel/skylake/skl-pcm.c b/sound/soc/intel/skylake/skl-pcm.c index 889fd8680629..4679bf9f4543 100644 --- a/sound/soc/intel/skylake/skl-pcm.c +++ b/sound/soc/intel/skylake/skl-pcm.c @@ -877,6 +877,8 @@ static int skl_trace_compr_tstamp(struct snd_compr_stream *stream, return -EINVAL; tstamp->copied_total = skl_dsp_log_avail(sst, core); + tstamp->sampling_rate = snd_pcm_rate_bit_to_rate(cpu_dai->driver->capture.rates); + return 0; } @@ -1359,6 +1361,9 @@ static struct snd_soc_dai_driver skl_platform_dai[] = { .stream_name = "TraceBuffer0 Capture", .channels_min = HDA_MONO, .channels_max = HDA_MONO, + .rates = SNDRV_PCM_RATE_48000, + .rate_min = 48000, + .rate_max = 48000, }, }, { @@ -1369,6 +1374,9 @@ static struct snd_soc_dai_driver skl_platform_dai[] = { .stream_name = "TraceBuffer1 Capture", .channels_min = HDA_MONO, .channels_max = HDA_MONO, + .rates = SNDRV_PCM_RATE_48000, + .rate_min = 48000, + .rate_max = 48000, }, }, { @@ -1379,6 +1387,9 @@ static struct snd_soc_dai_driver skl_platform_dai[] = { .stream_name = "TraceBuffer2 Capture", .channels_min = HDA_MONO, .channels_max = HDA_MONO, + .rates = SNDRV_PCM_RATE_48000, + .rate_min = 48000, + .rate_max = 48000, }, }, { @@ -1389,6 +1400,9 @@ static struct snd_soc_dai_driver skl_platform_dai[] = { .stream_name = "TraceBuffer3 Capture", .channels_min = HDA_MONO, .channels_max = HDA_MONO, + .rates = SNDRV_PCM_RATE_48000, + .rate_min = 48000, + .rate_max = 48000, }, }, { From 33b96078872308877d76debacf26aae14194e20a Mon Sep 17 00:00:00 2001 From: Mohit Sinha Date: Mon, 4 Sep 2017 23:31:17 +0530 Subject: [PATCH 0804/1103] ASoC: Intel: Board: Add fixup for 32 bit masking Fixup function does the masking of the format to set the SSP2 to 32 bit Change-Id: I1c5f20ce1244f9c3a47a47342d46184fdd718290 Signed-off-by: Mohit Sinha Reviewed-on: Reviewed-by: Gogineni, GiribabuX Reviewed-by: Shaik, Kareem M Reviewed-by: Koul, Vinod Tested-by: Sm, Bhadur A --- sound/soc/intel/boards/bxt_tdf8532.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/sound/soc/intel/boards/bxt_tdf8532.c b/sound/soc/intel/boards/bxt_tdf8532.c index c7b7fe3f9ed7..27361e8f72d3 100644 --- a/sound/soc/intel/boards/bxt_tdf8532.c +++ b/sound/soc/intel/boards/bxt_tdf8532.c @@ -76,6 +76,18 @@ static const struct snd_soc_dapm_route broxton_tdf8532_map[] = { { "ssp3 Tx", NULL, "Modem_ssp3_out"}, }; +static int bxt_tdf8532_ssp2_fixup(struct snd_soc_pcm_runtime *rtd, + struct snd_pcm_hw_params *params) +{ + struct snd_mask *fmt = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT); + + /* set SSP to 32 bit */ + snd_mask_none(fmt); + snd_mask_set(fmt, SNDRV_PCM_FORMAT_S32_LE); + + return 0; +} + /* broxton digital audio interface glue - connects codec <--> CPU */ static struct snd_soc_dai_link broxton_tdf8532_dais[] = { /* Probe DAI links*/ @@ -158,6 +170,7 @@ static struct snd_soc_dai_link broxton_tdf8532_dais[] = { .dpcm_capture = 1, .dpcm_playback = 1, .no_pcm = 1, + .be_hw_params_fixup = bxt_tdf8532_ssp2_fixup, }, { /* SSP3 - Modem */ From abcd2fbe98e1defc2fc92d54b0f49121518afbf5 Mon Sep 17 00:00:00 2001 From: "Dharageswari.R" Date: Tue, 25 Jul 2017 17:49:58 +0530 Subject: [PATCH 0805/1103] ASoC: Intel: Skylake: Add support for GAIN module Gain module is represented by three entities such as Ramp Duration, Ramp Type and Volume controlled by three kcontrols. These kcontrols are added with the corresponding get and put handlers. A Large IPC message is sent to the DSP when any of these controls are set and if widget is in powered on state else these values are cached in driver and sent during init of gain module. Multichannel support for volume control is implemented by user defined kcontrol info method to set number of channels to what we are getting from mconfig out channels. Change-Id: I2473d636fb673f7a73118d7b5018f07127def69f Signed-off-by: Giribabu Gogineni Signed-off-by: Dharageswari.R Reviewed-on: Reviewed-by: Sinha, Mohit Reviewed-by: Shaik, Kareem M Reviewed-by: Koul, Vinod Tested-by: Sm, Bhadur A --- include/uapi/sound/skl-tplg-interface.h | 13 +- sound/soc/intel/skylake/skl-messages.c | 29 ++ sound/soc/intel/skylake/skl-topology.c | 467 ++++++++++++++++++++++++ sound/soc/intel/skylake/skl-topology.h | 13 + 4 files changed, 521 insertions(+), 1 deletion(-) diff --git a/include/uapi/sound/skl-tplg-interface.h b/include/uapi/sound/skl-tplg-interface.h index a3f0890c75bd..5e284a4b6ce0 100644 --- a/include/uapi/sound/skl-tplg-interface.h +++ b/include/uapi/sound/skl-tplg-interface.h @@ -20,6 +20,9 @@ #define SKL_CONTROL_TYPE_MIC_SELECT 0x102 #define SKL_CONTROL_TYPE_BYTE_PROBE 0x101 #define SKL_CONTROL_TYPE_MULTI_IO_SELECT 0x103 +#define SKL_CONTROL_TYPE_VOLUME 0x104 +#define SKL_CONTROL_TYPE_RAMP_DURATION 0x105 +#define SKL_CONTROL_TYPE_RAMP_TYPE 0x106 #define HDA_SST_CFG_MAX 900 /* size of copier cfg*/ #define MAX_IN_QUEUE 8 @@ -82,7 +85,8 @@ enum skl_module_type { SKL_MODULE_TYPE_KPB, SKL_MODULE_TYPE_MIC_SELECT, SKL_MODULE_TYPE_PROBE, - SKL_MODULE_TYPE_ASRC + SKL_MODULE_TYPE_ASRC, + SKL_MODULE_TYPE_GAIN }; enum skl_core_affinity { @@ -193,6 +197,13 @@ struct skl_dfw_algo_data { char params[0]; } __packed; +struct skl_gain_config { + u32 channel_id; + u32 target_volume; + u32 ramp_type; + u64 ramp_duration; +} __packed; + enum skl_tkn_dir { SKL_DIR_IN, SKL_DIR_OUT diff --git a/sound/soc/intel/skylake/skl-messages.c b/sound/soc/intel/skylake/skl-messages.c index 02b3aad155f1..ba60269255f6 100644 --- a/sound/soc/intel/skylake/skl-messages.c +++ b/sound/soc/intel/skylake/skl-messages.c @@ -36,6 +36,7 @@ #define ASRC_MODE_UPLINK 2 #define ASRC_MODE_DOWNLINK 1 +#define SKL_ENABLE_ALL_CHANNELS 0xffffffff static int skl_alloc_dma_buf(struct device *dev, struct snd_dma_buffer *dmab, size_t size) @@ -1726,6 +1727,22 @@ static void skl_setup_out_format(struct skl_sst *ctx, out_fmt->number_of_channels, format->s_freq, format->bit_depth); } +static int skl_set_gain_format(struct skl_sst *ctx, + struct skl_module_cfg *mconfig, + struct skl_gain_module_config *gain_mconfig) +{ + struct skl_gain_data *gain_fmt = mconfig->gain_data; + + skl_set_base_module_format(ctx, mconfig, + (struct skl_base_cfg *)gain_mconfig); + gain_mconfig->gain_cfg.channel_id = SKL_ENABLE_ALL_CHANNELS; + gain_mconfig->gain_cfg.target_volume = gain_fmt->volume[0]; + gain_mconfig->gain_cfg.ramp_type = gain_fmt->ramp_type; + gain_mconfig->gain_cfg.ramp_duration = gain_fmt->ramp_duration; + + return 0; +} + /* * DSP needs SRC module for frequency conversion, SRC takes base module * configuration and the target frequency as extra parameter passed as src @@ -1866,6 +1883,7 @@ static u16 skl_get_module_param_size(struct skl_sst *ctx, struct skl_module_cfg *mconfig) { u16 param_size; + struct skl_module_iface *m_intf; switch (mconfig->m_type) { case SKL_MODULE_TYPE_COPIER: @@ -1893,6 +1911,13 @@ static u16 skl_get_module_param_size(struct skl_sst *ctx, case SKL_MODULE_TYPE_KPB: return sizeof(struct skl_base_outfmt_cfg); + case SKL_MODULE_TYPE_GAIN: + m_intf = &mconfig->module->formats[mconfig->fmt_idx]; + param_size = sizeof(struct skl_base_cfg); + param_size += sizeof(struct skl_gain_config) + * m_intf->outputs[0].fmt.channels; + return param_size; + default: /* * return only base cfg when no specific module type is @@ -1954,6 +1979,10 @@ static int skl_set_module_format(struct skl_sst *ctx, skl_set_base_outfmt_format(ctx, module_config, *param_data); break; + case SKL_MODULE_TYPE_GAIN: + skl_set_gain_format(ctx, module_config, *param_data); + break; + default: skl_set_base_module_format(ctx, module_config, *param_data); break; diff --git a/sound/soc/intel/skylake/skl-topology.c b/sound/soc/intel/skylake/skl-topology.c index 1a40c66e1c58..451b28a21585 100644 --- a/sound/soc/intel/skylake/skl-topology.c +++ b/sound/soc/intel/skylake/skl-topology.c @@ -32,6 +32,8 @@ #include "../common/sst-dsp-priv.h" #include "skl-fwlog.h" +#define SKL_CURVE_NONE 0 +#define SKL_MAX_GAIN 0x7FFFFFFF #define SKL_CH_FIXUP_MASK (1 << 0) #define SKL_RATE_FIXUP_MASK (1 << 1) #define SKL_FMT_FIXUP_MASK (1 << 2) @@ -62,6 +64,257 @@ static const int mic_quatro_list[][SKL_CH_QUATRO] = { } \ } while (0) +/* + * The following table provides the gain in linear scale corresponding to + * gain in dB scale in the range of -144 dB to 0 dB with 0.1 dB resolution. + * The real number linear gain is scaled by 0x7FFFFFFFF to convert it to a + * 32 bit integer as required by FW. + * linear_gain[i] = 0 for i = 0 ; (Mapped as mute) + * = 0x7FFFFFFF*Round(10^(-144+0.1*i)/20) for i = 1 ... 1440 + */ +static u32 linear_gain[] = { +0x00000000, 0x00000089, 0x0000008B, 0x0000008C, 0x0000008E, 0x00000090, +0x00000091, 0x00000093, 0x00000095, 0x00000096, 0x00000098, 0x0000009A, +0x0000009C, 0x0000009D, 0x0000009F, 0x000000A1, 0x000000A3, 0x000000A5, +0x000000A7, 0x000000A9, 0x000000AB, 0x000000AD, 0x000000AF, 0x000000B1, +0x000000B3, 0x000000B5, 0x000000B7, 0x000000B9, 0x000000BB, 0x000000BD, +0x000000BF, 0x000000C2, 0x000000C4, 0x000000C6, 0x000000C8, 0x000000CB, +0x000000CD, 0x000000CF, 0x000000D2, 0x000000D4, 0x000000D7, 0x000000D9, +0x000000DC, 0x000000DE, 0x000000E1, 0x000000E3, 0x000000E6, 0x000000E9, +0x000000EB, 0x000000EE, 0x000000F1, 0x000000F4, 0x000000F7, 0x000000F9, +0x000000FC, 0x000000FF, 0x00000102, 0x00000105, 0x00000108, 0x0000010B, +0x0000010E, 0x00000111, 0x00000115, 0x00000118, 0x0000011B, 0x0000011E, +0x00000122, 0x00000125, 0x00000128, 0x0000012C, 0x0000012F, 0x00000133, +0x00000136, 0x0000013A, 0x0000013E, 0x00000141, 0x00000145, 0x00000149, +0x0000014D, 0x00000150, 0x00000154, 0x00000158, 0x0000015C, 0x00000160, +0x00000164, 0x00000169, 0x0000016D, 0x00000171, 0x00000175, 0x0000017A, +0x0000017E, 0x00000182, 0x00000187, 0x0000018B, 0x00000190, 0x00000195, +0x00000199, 0x0000019E, 0x000001A3, 0x000001A8, 0x000001AC, 0x000001B1, +0x000001B6, 0x000001BC, 0x000001C1, 0x000001C6, 0x000001CB, 0x000001D0, +0x000001D6, 0x000001DB, 0x000001E1, 0x000001E6, 0x000001EC, 0x000001F2, +0x000001F7, 0x000001FD, 0x00000203, 0x00000209, 0x0000020F, 0x00000215, +0x0000021B, 0x00000222, 0x00000228, 0x0000022E, 0x00000235, 0x0000023B, +0x00000242, 0x00000249, 0x0000024F, 0x00000256, 0x0000025D, 0x00000264, +0x0000026B, 0x00000273, 0x0000027A, 0x00000281, 0x00000289, 0x00000290, +0x00000298, 0x0000029F, 0x000002A7, 0x000002AF, 0x000002B7, 0x000002BF, +0x000002C7, 0x000002CF, 0x000002D8, 0x000002E0, 0x000002E9, 0x000002F1, +0x000002FA, 0x00000303, 0x0000030C, 0x00000315, 0x0000031E, 0x00000327, +0x00000330, 0x0000033A, 0x00000343, 0x0000034D, 0x00000357, 0x00000361, +0x0000036B, 0x00000375, 0x0000037F, 0x0000038A, 0x00000394, 0x0000039F, +0x000003A9, 0x000003B4, 0x000003BF, 0x000003CA, 0x000003D6, 0x000003E1, +0x000003EC, 0x000003F8, 0x00000404, 0x00000410, 0x0000041C, 0x00000428, +0x00000434, 0x00000441, 0x0000044D, 0x0000045A, 0x00000467, 0x00000474, +0x00000481, 0x0000048F, 0x0000049C, 0x000004AA, 0x000004B8, 0x000004C6, +0x000004D4, 0x000004E2, 0x000004F1, 0x000004FF, 0x0000050E, 0x0000051D, +0x0000052C, 0x0000053B, 0x0000054B, 0x0000055B, 0x0000056B, 0x0000057B, +0x0000058B, 0x0000059B, 0x000005AC, 0x000005BD, 0x000005CE, 0x000005DF, +0x000005F0, 0x00000602, 0x00000614, 0x00000626, 0x00000638, 0x0000064A, +0x0000065D, 0x00000670, 0x00000683, 0x00000696, 0x000006AA, 0x000006BE, +0x000006D2, 0x000006E6, 0x000006FA, 0x0000070F, 0x00000724, 0x00000739, +0x0000074E, 0x00000764, 0x0000077A, 0x00000790, 0x000007A7, 0x000007BD, +0x000007D4, 0x000007EB, 0x00000803, 0x0000081B, 0x00000833, 0x0000084B, +0x00000863, 0x0000087C, 0x00000896, 0x000008AF, 0x000008C9, 0x000008E3, +0x000008FD, 0x00000918, 0x00000933, 0x0000094E, 0x0000096A, 0x00000985, +0x000009A2, 0x000009BE, 0x000009DB, 0x000009F8, 0x00000A16, 0x00000A34, +0x00000A52, 0x00000A71, 0x00000A90, 0x00000AAF, 0x00000ACE, 0x00000AEF, +0x00000B0F, 0x00000B30, 0x00000B51, 0x00000B72, 0x00000B94, 0x00000BB7, +0x00000BD9, 0x00000BFD, 0x00000C20, 0x00000C44, 0x00000C68, 0x00000C8D, +0x00000CB2, 0x00000CD8, 0x00000CFE, 0x00000D25, 0x00000D4C, 0x00000D73, +0x00000D9B, 0x00000DC3, 0x00000DEC, 0x00000E15, 0x00000E3F, 0x00000E69, +0x00000E94, 0x00000EBF, 0x00000EEB, 0x00000F17, 0x00000F44, 0x00000F71, +0x00000F9F, 0x00000FCD, 0x00000FFC, 0x0000102B, 0x0000105B, 0x0000108C, +0x000010BD, 0x000010EE, 0x00001121, 0x00001153, 0x00001187, 0x000011BB, +0x000011EF, 0x00001224, 0x0000125A, 0x00001291, 0x000012C8, 0x000012FF, +0x00001338, 0x00001371, 0x000013AA, 0x000013E4, 0x0000141F, 0x0000145B, +0x00001497, 0x000014D4, 0x00001512, 0x00001551, 0x00001590, 0x000015D0, +0x00001610, 0x00001652, 0x00001694, 0x000016D7, 0x0000171B, 0x0000175F, +0x000017A4, 0x000017EB, 0x00001831, 0x00001879, 0x000018C2, 0x0000190B, +0x00001955, 0x000019A0, 0x000019EC, 0x00001A39, 0x00001A87, 0x00001AD6, +0x00001B25, 0x00001B76, 0x00001BC7, 0x00001C19, 0x00001C6D, 0x00001CC1, +0x00001D16, 0x00001D6C, 0x00001DC4, 0x00001E1C, 0x00001E75, 0x00001ECF, +0x00001F2B, 0x00001F87, 0x00001FE5, 0x00002043, 0x000020A3, 0x00002103, +0x00002165, 0x000021C8, 0x0000222C, 0x00002292, 0x000022F8, 0x00002360, +0x000023C9, 0x00002433, 0x0000249E, 0x0000250B, 0x00002578, 0x000025E8, +0x00002658, 0x000026CA, 0x0000273D, 0x000027B1, 0x00002827, 0x0000289E, +0x00002916, 0x00002990, 0x00002A0B, 0x00002A88, 0x00002B06, 0x00002B85, +0x00002C06, 0x00002C89, 0x00002D0D, 0x00002D92, 0x00002E19, 0x00002EA2, +0x00002F2C, 0x00002FB8, 0x00003045, 0x000030D5, 0x00003165, 0x000031F8, +0x0000328C, 0x00003322, 0x000033B9, 0x00003453, 0x000034EE, 0x0000358B, +0x00003629, 0x000036CA, 0x0000376C, 0x00003811, 0x000038B7, 0x0000395F, +0x00003A09, 0x00003AB5, 0x00003B63, 0x00003C13, 0x00003CC5, 0x00003D79, +0x00003E30, 0x00003EE8, 0x00003FA2, 0x0000405F, 0x0000411E, 0x000041DF, +0x000042A2, 0x00004368, 0x0000442F, 0x000044FA, 0x000045C6, 0x00004695, +0x00004766, 0x0000483A, 0x00004910, 0x000049E8, 0x00004AC3, 0x00004BA1, +0x00004C81, 0x00004D64, 0x00004E49, 0x00004F32, 0x0000501C, 0x0000510A, +0x000051FA, 0x000052ED, 0x000053E3, 0x000054DC, 0x000055D7, 0x000056D6, +0x000057D7, 0x000058DB, 0x000059E3, 0x00005AED, 0x00005BFB, 0x00005D0B, +0x00005E1F, 0x00005F36, 0x00006050, 0x0000616E, 0x0000628F, 0x000063B3, +0x000064DA, 0x00006605, 0x00006734, 0x00006866, 0x0000699B, 0x00006AD4, +0x00006C11, 0x00006D51, 0x00006E95, 0x00006FDD, 0x00007129, 0x00007278, +0x000073CC, 0x00007523, 0x0000767E, 0x000077DD, 0x00007941, 0x00007AA8, +0x00007C14, 0x00007D83, 0x00007EF7, 0x00008070, 0x000081ED, 0x0000836E, +0x000084F3, 0x0000867D, 0x0000880C, 0x0000899F, 0x00008B37, 0x00008CD4, +0x00008E76, 0x0000901C, 0x000091C7, 0x00009377, 0x0000952C, 0x000096E6, +0x000098A6, 0x00009A6A, 0x00009C34, 0x00009E03, 0x00009FD7, 0x0000A1B1, +0x0000A391, 0x0000A575, 0x0000A760, 0x0000A950, 0x0000AB46, 0x0000AD42, +0x0000AF43, 0x0000B14B, 0x0000B358, 0x0000B56C, 0x0000B786, 0x0000B9A6, +0x0000BBCC, 0x0000BDF9, 0x0000C02C, 0x0000C266, 0x0000C4A6, 0x0000C6ED, +0x0000C93B, 0x0000CB8F, 0x0000CDEA, 0x0000D04D, 0x0000D2B6, 0x0000D527, +0x0000D79F, 0x0000DA1E, 0x0000DCA5, 0x0000DF33, 0x0000E1C8, 0x0000E466, +0x0000E70B, 0x0000E9B7, 0x0000EC6C, 0x0000EF29, 0x0000F1EE, 0x0000F4BB, +0x0000F791, 0x0000FA6F, 0x0000FD55, 0x00010044, 0x0001033C, 0x0001063C, +0x00010945, 0x00010C58, 0x00010F73, 0x00011298, 0x000115C6, 0x000118FD, +0x00011C3E, 0x00011F89, 0x000122DD, 0x0001263B, 0x000129A4, 0x00012D16, +0x00013092, 0x00013419, 0x000137AB, 0x00013B46, 0x00013EED, 0x0001429E, +0x0001465B, 0x00014A22, 0x00014DF5, 0x000151D3, 0x000155BC, 0x000159B1, +0x00015DB2, 0x000161BF, 0x000165D7, 0x000169FC, 0x00016E2D, 0x0001726B, +0x000176B5, 0x00017B0B, 0x00017F6F, 0x000183E0, 0x0001885D, 0x00018CE8, +0x00019181, 0x00019627, 0x00019ADB, 0x00019F9D, 0x0001A46D, 0x0001A94B, +0x0001AE38, 0x0001B333, 0x0001B83E, 0x0001BD57, 0x0001C27F, 0x0001C7B6, +0x0001CCFD, 0x0001D254, 0x0001D7BA, 0x0001DD30, 0x0001E2B7, 0x0001E84E, +0x0001EDF5, 0x0001F3AD, 0x0001F977, 0x0001FF51, 0x0002053D, 0x00020B3A, +0x00021149, 0x0002176A, 0x00021D9D, 0x000223E3, 0x00022A3B, 0x000230A6, +0x00023724, 0x00023DB5, 0x0002445A, 0x00024B12, 0x000251DE, 0x000258BF, +0x00025FB3, 0x000266BD, 0x00026DDB, 0x0002750F, 0x00027C57, 0x000283B6, +0x00028B2A, 0x000292B4, 0x00029A55, 0x0002A20C, 0x0002A9DA, 0x0002B1BF, +0x0002B9BC, 0x0002C1D0, 0x0002C9FD, 0x0002D241, 0x0002DA9E, 0x0002E314, +0x0002EBA3, 0x0002F44B, 0x0002FD0D, 0x000305E9, 0x00030EDF, 0x000317F0, +0x0003211B, 0x00032A62, 0x000333C4, 0x00033D42, 0x000346DC, 0x00035093, +0x00035A66, 0x00036457, 0x00036E65, 0x00037891, 0x000382DB, 0x00038D44, +0x000397CB, 0x0003A271, 0x0003AD38, 0x0003B81E, 0x0003C324, 0x0003CE4B, +0x0003D993, 0x0003E4FD, 0x0003F088, 0x0003FC36, 0x00040806, 0x000413F9, +0x00042010, 0x00042C4B, 0x000438A9, 0x0004452D, 0x000451D5, 0x00045EA4, +0x00046B98, 0x000478B2, 0x000485F3, 0x0004935C, 0x0004A0EC, 0x0004AEA5, +0x0004BC86, 0x0004CA90, 0x0004D8C4, 0x0004E722, 0x0004F5AB, 0x0005045F, +0x0005133E, 0x00052249, 0x00053181, 0x000540E6, 0x00055079, 0x0005603A, +0x0005702A, 0x00058048, 0x00059097, 0x0005A116, 0x0005B1C6, 0x0005C2A7, +0x0005D3BB, 0x0005E501, 0x0005F67A, 0x00060827, 0x00061A08, 0x00062C1F, +0x00063E6B, 0x000650ED, 0x000663A6, 0x00067697, 0x000689BF, 0x00069D21, +0x0006B0BC, 0x0006C491, 0x0006D8A1, 0x0006ECEC, 0x00070174, 0x00071638, +0x00072B3A, 0x0007407A, 0x000755FA, 0x00076BB9, 0x000781B8, 0x000797F9, +0x0007AE7B, 0x0007C541, 0x0007DC49, 0x0007F397, 0x00080B29, 0x00082301, +0x00083B20, 0x00085386, 0x00086C34, 0x0008852C, 0x00089E6E, 0x0008B7FA, +0x0008D1D3, 0x0008EBF8, 0x0009066A, 0x0009212B, 0x00093C3B, 0x0009579C, +0x0009734D, 0x00098F51, 0x0009ABA7, 0x0009C852, 0x0009E552, 0x000A02A7, +0x000A2054, 0x000A3E58, 0x000A5CB6, 0x000A7B6D, 0x000A9A80, 0x000AB9EF, +0x000AD9BB, 0x000AF9E5, 0x000B1A6E, 0x000B3B58, 0x000B5CA4, 0x000B7E52, +0x000BA064, 0x000BC2DB, 0x000BE5B8, 0x000C08FD, 0x000C2CAA, 0x000C50C1, +0x000C7543, 0x000C9A31, 0x000CBF8C, 0x000CE556, 0x000D0B91, 0x000D323C, +0x000D595A, 0x000D80ED, 0x000DA8F4, 0x000DD172, 0x000DFA69, 0x000E23D8, +0x000E4DC3, 0x000E7829, 0x000EA30E, 0x000ECE71, 0x000EFA55, 0x000F26BC, +0x000F53A6, 0x000F8115, 0x000FAF0A, 0x000FDD88, 0x00100C90, 0x00103C23, +0x00106C43, 0x00109CF2, 0x0010CE31, 0x00110003, 0x00113267, 0x00116562, +0x001198F3, 0x0011CD1D, 0x001201E2, 0x00123743, 0x00126D43, 0x0012A3E2, +0x0012DB24, 0x00131309, 0x00134B94, 0x001384C7, 0x0013BEA3, 0x0013F92B, +0x00143460, 0x00147044, 0x0014ACDB, 0x0014EA24, 0x00152824, 0x001566DB, +0x0015A64C, 0x0015E67A, 0x00162765, 0x00166911, 0x0016AB80, 0x0016EEB3, +0x001732AE, 0x00177772, 0x0017BD02, 0x00180361, 0x00184A90, 0x00189292, +0x0018DB69, 0x00192518, 0x00196FA2, 0x0019BB09, 0x001A074F, 0x001A5477, +0x001AA284, 0x001AF179, 0x001B4157, 0x001B9222, 0x001BE3DD, 0x001C368A, +0x001C8A2C, 0x001CDEC6, 0x001D345B, 0x001D8AED, 0x001DE280, 0x001E3B17, +0x001E94B4, 0x001EEF5B, 0x001F4B0F, 0x001FA7D2, 0x002005A9, 0x00206496, +0x0020C49C, 0x002125BE, 0x00218801, 0x0021EB67, 0x00224FF3, 0x0022B5AA, +0x00231C8E, 0x002384A3, 0x0023EDED, 0x0024586F, 0x0024C42C, 0x00253129, +0x00259F69, 0x00260EF0, 0x00267FC1, 0x0026F1E1, 0x00276553, 0x0027DA1C, +0x0028503E, 0x0028C7BF, 0x002940A2, 0x0029BAEB, 0x002A369F, 0x002AB3C1, +0x002B3257, 0x002BB263, 0x002C33EC, 0x002CB6F4, 0x002D3B81, 0x002DC196, +0x002E4939, 0x002ED26E, 0x002F5D3A, 0x002FE9A2, 0x003077A9, 0x00310756, +0x003198AC, 0x00322BB1, 0x0032C06A, 0x003356DC, 0x0033EF0C, 0x003488FF, +0x003524BB, 0x0035C244, 0x003661A0, 0x003702D4, 0x0037A5E6, 0x00384ADC, +0x0038F1BB, 0x00399A88, 0x003A454A, 0x003AF206, 0x003BA0C2, 0x003C5184, +0x003D0452, 0x003DB932, 0x003E702A, 0x003F2940, 0x003FE47B, 0x0040A1E2, +0x00416179, 0x00422349, 0x0042E757, 0x0043ADAA, 0x00447649, 0x0045413B, +0x00460E87, 0x0046DE33, 0x0047B046, 0x004884C9, 0x00495BC1, 0x004A3537, +0x004B1131, 0x004BEFB7, 0x004CD0D1, 0x004DB486, 0x004E9ADE, 0x004F83E1, +0x00506F97, 0x00515E08, 0x00524F3B, 0x00534339, 0x00543A0B, 0x005533B8, +0x00563049, 0x00572FC8, 0x0058323B, 0x005937AD, 0x005A4025, 0x005B4BAE, +0x005C5A4F, 0x005D6C13, 0x005E8102, 0x005F9927, 0x0060B48A, 0x0061D334, +0x0062F531, 0x00641A89, 0x00654347, 0x00666F74, 0x00679F1C, 0x0068D247, +0x006A0901, 0x006B4354, 0x006C814B, 0x006DC2F0, 0x006F084F, 0x00705172, +0x00719E65, 0x0072EF33, 0x007443E8, 0x00759C8E, 0x0076F932, 0x007859DF, +0x0079BEA2, 0x007B2787, 0x007C9499, 0x007E05E6, 0x007F7B79, 0x0080F560, +0x008273A6, 0x0083F65A, 0x00857D89, 0x0087093F, 0x0088998A, 0x008A2E77, +0x008BC815, 0x008D6672, 0x008F099A, 0x0090B19D, 0x00925E89, 0x0094106D, +0x0095C756, 0x00978355, 0x00994478, 0x009B0ACE, 0x009CD667, 0x009EA752, +0x00A07DA0, 0x00A25960, 0x00A43AA2, 0x00A62177, 0x00A80DEE, 0x00AA001A, +0x00ABF80A, 0x00ADF5D1, 0x00AFF97E, 0x00B20324, 0x00B412D4, 0x00B628A1, +0x00B8449C, 0x00BA66D8, 0x00BC8F67, 0x00BEBE5B, 0x00C0F3C9, 0x00C32FC3, +0x00C5725D, 0x00C7BBA9, 0x00CA0BBD, 0x00CC62AC, 0x00CEC08A, 0x00D1256C, +0x00D39167, 0x00D60490, 0x00D87EFC, 0x00DB00C0, 0x00DD89F3, 0x00E01AAB, +0x00E2B2FD, 0x00E55300, 0x00E7FACC, 0x00EAAA77, 0x00ED6218, 0x00F021C7, +0x00F2E99C, 0x00F5B9B0, 0x00F89219, 0x00FB72F2, 0x00FE5C54, 0x01014E57, +0x01044915, 0x01074CA8, 0x010A592A, 0x010D6EB6, 0x01108D67, 0x0113B557, +0x0116E6A2, 0x011A2164, 0x011D65B9, 0x0120B3BC, 0x01240B8C, 0x01276D45, +0x012AD904, 0x012E4EE7, 0x0131CF0B, 0x01355991, 0x0138EE96, 0x013C8E39, +0x0140389A, 0x0143EDD8, 0x0147AE14, 0x014B796F, 0x014F500A, 0x01533205, +0x01571F82, 0x015B18A5, 0x015F1D8E, 0x01632E61, 0x01674B42, 0x016B7454, +0x016FA9BB, 0x0173EB9C, 0x01783A1B, 0x017C955F, 0x0180FD8D, 0x018572CB, +0x0189F540, 0x018E8513, 0x0193226D, 0x0197CD74, 0x019C8651, 0x01A14D2E, +0x01A62234, 0x01AB058D, 0x01AFF764, 0x01B4F7E3, 0x01BA0735, 0x01BF2588, +0x01C45306, 0x01C98FDE, 0x01CEDC3D, 0x01D43850, 0x01D9A447, 0x01DF2050, +0x01E4AC9B, 0x01EA4958, 0x01EFF6B8, 0x01F5B4ED, 0x01FB8428, 0x0201649B, +0x0207567A, 0x020D59F9, 0x02136F4B, 0x021996A5, 0x021FD03D, 0x02261C4A, +0x022C7B01, 0x0232EC9A, 0x0239714D, 0x02400952, 0x0246B4E4, 0x024D743B, +0x02544792, 0x025B2F26, 0x02622B31, 0x02693BF0, 0x027061A1, 0x02779C82, +0x027EECD2, 0x028652D0, 0x028DCEBC, 0x029560D8, 0x029D0964, 0x02A4C8A5, +0x02AC9EDD, 0x02B48C50, 0x02BC9142, 0x02C4ADFB, 0x02CCE2BF, 0x02D52FD7, +0x02DD958A, 0x02E61422, 0x02EEABE8, 0x02F75D27, 0x0300282A, 0x03090D3F, +0x03120CB1, 0x031B26CF, 0x03245BE9, 0x032DAC4D, 0x0337184E, 0x0340A03D, +0x034A446D, 0x03540531, 0x035DE2DF, 0x0367DDCC, 0x0371F64E, 0x037C2CBD, +0x03868173, 0x0390F4C8, 0x039B8719, 0x03A638BF, 0x03B10A19, 0x03BBFB84, +0x03C70D60, 0x03D2400C, 0x03DD93E9, 0x03E9095B, 0x03F4A0C5, 0x04005A8B, +0x040C3714, 0x041836C5, 0x04245A09, 0x0430A147, 0x043D0CEB, 0x04499D60, +0x04565314, 0x04632E76, 0x04702FF4, 0x047D57FF, 0x048AA70B, 0x04981D8B, +0x04A5BBF3, 0x04B382B9, 0x04C17257, 0x04CF8B44, 0x04DDCDFB, 0x04EC3AF8, +0x04FAD2B9, 0x050995BB, 0x05188480, 0x05279F89, 0x0536E758, 0x05465C74, +0x0555FF62, 0x0565D0AB, 0x0575D0D6, 0x05860070, 0x05966005, 0x05A6F023, +0x05B7B15B, 0x05C8A43D, 0x05D9C95D, 0x05EB2150, 0x05FCACAD, 0x060E6C0B, +0x06206006, 0x06328938, 0x0644E841, 0x06577DBE, 0x066A4A53, 0x067D4EA2, +0x06908B50, 0x06A40104, 0x06B7B068, 0x06CB9A26, 0x06DFBEEC, 0x06F41F68, +0x0708BC4C, 0x071D964A, 0x0732AE18, 0x0748046D, 0x075D9A02, 0x07736F92, +0x078985DC, 0x079FDD9F, 0x07B6779E, 0x07CD549C, 0x07E47560, 0x07FBDAB4, +0x08138562, 0x082B7638, 0x0843AE06, 0x085C2D9E, 0x0874F5D6, 0x088E0783, +0x08A76381, 0x08C10AAC, 0x08DAFDE2, 0x08F53E04, 0x090FCBF7, 0x092AA8A2, +0x0945D4EE, 0x096151C6, 0x097D201A, 0x099940DB, 0x09B5B4FE, 0x09D27D79, +0x09EF9B47, 0x0A0D0F64, 0x0A2ADAD1, 0x0A48FE91, 0x0A677BA8, 0x0A865320, +0x0AA58606, 0x0AC51567, 0x0AE50256, 0x0B054DE8, 0x0B25F937, 0x0B47055D, +0x0B68737A, 0x0B8A44AF, 0x0BAC7A24, 0x0BCF1501, 0x0BF21673, 0x0C157FA9, +0x0C3951D8, 0x0C5D8E36, 0x0C8235FF, 0x0CA74A70, 0x0CCCCCCD, 0x0CF2BE5A, +0x0D192061, 0x0D3FF430, 0x0D673B17, 0x0D8EF66D, 0x0DB7278B, 0x0DDFCFCC, +0x0E08F094, 0x0E328B46, 0x0E5CA14C, 0x0E873415, 0x0EB24511, 0x0EDDD5B7, +0x0F09E781, 0x0F367BEE, 0x0F639481, 0x0F9132C3, 0x0FBF583F, 0x0FEE0686, +0x101D3F2D, 0x104D03D0, 0x107D560D, 0x10AE3787, 0x10DFA9E7, 0x1111AEDB, +0x11444815, 0x1177774D, 0x11AB3E3F, 0x11DF9EAE, 0x12149A60, 0x124A3321, +0x12806AC3, 0x12B7431D, 0x12EEBE0C, 0x1326DD70, 0x135FA333, 0x13991141, +0x13D3298C, 0x140DEE0E, 0x144960C5, 0x148583B6, 0x14C258EA, 0x14FFE273, +0x153E2266, 0x157D1AE2, 0x15BCCE07, 0x15FD3E01, 0x163E6CFE, 0x16805D35, +0x16C310E3, 0x17068A4B, 0x174ACBB8, 0x178FD779, 0x17D5AFE8, 0x181C5762, +0x1863D04D, 0x18AC1D17, 0x18F54033, 0x193F3C1D, 0x198A1357, 0x19D5C86C, +0x1A225DED, 0x1A6FD673, 0x1ABE349F, 0x1B0D7B1B, 0x1B5DAC97, 0x1BAECBCA, +0x1C00DB77, 0x1C53DE66, 0x1CA7D768, 0x1CFCC956, 0x1D52B712, 0x1DA9A387, +0x1E0191A9, 0x1E5A8471, 0x1EB47EE7, 0x1F0F8416, 0x1F6B9715, 0x1FC8BB06, +0x2026F30F, 0x20864265, 0x20E6AC43, 0x214833EE, 0x21AADCB6, 0x220EA9F4, +0x22739F0A, 0x22D9BF65, 0x23410E7E, 0x23A98FD5, 0x241346F6, 0x247E3777, +0x24EA64F9, 0x2557D328, 0x25C685BB, 0x26368073, 0x26A7C71D, 0x271A5D91, +0x278E47B3, 0x28038970, 0x287A26C4, 0x28F223B6, 0x296B8457, 0x29E64CC5, +0x2A62812C, 0x2AE025C3, 0x2B5F3ECC, 0x2BDFD098, 0x2C61DF84, 0x2CE56FF9, +0x2D6A866F, 0x2DF12769, 0x2E795779, 0x2F031B3E, 0x2F8E7765, 0x301B70A8, +0x30AA0BCF, 0x313A4DB3, 0x31CC3B37, 0x325FD94F, 0x32F52CFF, 0x338C3B56, +0x34250975, 0x34BF9C8B, 0x355BF9D8, 0x35FA26A9, 0x369A285D, 0x373C0461, +0x37DFC033, 0x38856163, 0x392CED8E, 0x39D66A63, 0x3A81DDA4, 0x3B2F4D22, +0x3BDEBEBF, 0x3C90386F, 0x3D43C038, 0x3DF95C32, 0x3EB11285, 0x3F6AE96F, +0x4026E73C, 0x40E5124F, 0x41A5711B, 0x42680A28, 0x432CE40F, 0x43F4057E, +0x44BD7539, 0x45893A13, 0x46575AF8, 0x4727DEE6, 0x47FACCF0, 0x48D02C3F, +0x49A8040F, 0x4A825BB5, 0x4B5F3A99, 0x4C3EA838, 0x4D20AC29, 0x4E054E17, +0x4EEC95C3, 0x4FD68B07, 0x50C335D3, 0x51B29E2F, 0x52A4CC3A, 0x5399C82D, +0x54919A57, 0x558C4B22, 0x5689E30E, 0x578A6AB7, 0x588DEAD1, 0x59946C2A, +0x5A9DF7AB, 0x5BAA9656, 0x5CBA514A, 0x5DCD31BD, 0x5EE34105, 0x5FFC8890, +0x611911E9, 0x6238E6BA, 0x635C10C5, 0x648299EC, 0x65AC8C2E, 0x66D9F1A7, +0x680AD491, 0x693F3F45, 0x6A773C39, 0x6BB2D603, 0x6CF2175A, 0x6E350B13, +0x6F7BBC23, 0x70C6359F, 0x721482BF, 0x7366AEDB, 0x74BCC56B, 0x7616D20D, +0x7774E07D, 0x78D6FC9E, 0x7A3D3271, 0x7BA78E21, 0x7D161BF7, 0x7E88E865, +0x7FFFFFFF}; + static void skl_init_single_module_pipe(struct snd_soc_dapm_widget *w, struct skl *skl); @@ -1808,6 +2061,191 @@ int skl_tplg_dsp_log_set(struct snd_kcontrol *kcontrol, return 0; } +static int skl_tplg_send_gain_ipc(struct snd_soc_dapm_context *dapm, + struct skl_module_cfg *mconfig) +{ + struct skl_gain_config *gain_cfg; + struct skl *skl = get_skl_ctx(dapm->dev); + struct skl_module_iface *m_intf; + int num_channel, i, ret = 0; + + m_intf = &mconfig->module->formats[mconfig->fmt_idx]; + num_channel = (m_intf->outputs[0].fmt.channels > + MAX_NUM_CHANNELS) ? MAX_NUM_CHANNELS : + m_intf->outputs[0].fmt.channels; + + gain_cfg = kzalloc(sizeof(*gain_cfg), GFP_KERNEL); + if (!gain_cfg) + return -ENOMEM; + + gain_cfg->ramp_type = mconfig->gain_data->ramp_type; + gain_cfg->ramp_duration = mconfig->gain_data->ramp_duration; + for (i = 0; i < num_channel; i++) { + gain_cfg->channel_id = i; + gain_cfg->target_volume = mconfig->gain_data->volume[i]; + ret = skl_set_module_params(skl->skl_sst, (u32 *)gain_cfg, + sizeof(*gain_cfg), 0, mconfig); + if (ret < 0) { + dev_err(dapm->dev, + "set gain for channel:%d failed\n", i); + break; + } + } + kfree(gain_cfg); + + return ret; +} + +static int skl_tplg_ramp_duration_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dapm_widget *w = snd_soc_dapm_kcontrol_widget(kcontrol); + struct skl_module_cfg *mconfig = w->priv; + + ucontrol->value.integer.value[0] = mconfig->gain_data->ramp_duration; + + return 0; +} + +static int skl_tplg_ramp_type_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dapm_widget *w = snd_soc_dapm_kcontrol_widget(kcontrol); + struct skl_module_cfg *mconfig = w->priv; + + ucontrol->value.integer.value[0] = mconfig->gain_data->ramp_type; + + return 0; +} + +static int skl_tplg_get_linear_toindex(int val) +{ + int i, index = -EINVAL; + + for (i = 0; i < ARRAY_SIZE(linear_gain); i++) { + if (val == linear_gain[i]) { + index = i; + break; + } + } + + return index; +} + +static int skl_tplg_volume_ctl_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + struct soc_mixer_control *mc; + struct snd_soc_dapm_widget *w; + struct skl_module_iface *m_intf; + struct skl_module_cfg *mconfig; + + mc = (struct soc_mixer_control *)kcontrol->private_value; + w = snd_soc_dapm_kcontrol_widget(kcontrol); + mconfig = w->priv; + + m_intf = &mconfig->module->formats[mconfig->fmt_idx]; + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = m_intf->outputs[0].fmt.channels; + uinfo->value.integer.min = mc->min; + uinfo->value.integer.max = mc->max; + + return 0; +} + +static int skl_tplg_volume_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dapm_widget *w = snd_soc_dapm_kcontrol_widget(kcontrol); + struct skl_module_cfg *mconfig = w->priv; + struct skl_module_iface *m_intf; + int i, max_channels; + + m_intf = &mconfig->module->formats[mconfig->fmt_idx]; + max_channels = (m_intf->outputs[0].fmt.channels > + MAX_NUM_CHANNELS) ? MAX_NUM_CHANNELS : + m_intf->outputs[0].fmt.channels; + for (i = 0; i < max_channels; i++) + ucontrol->value.integer.value[i] = + skl_tplg_get_linear_toindex( + mconfig->gain_data->volume[i]); + + return 0; +} + +static int skl_tplg_ramp_duration_set(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dapm_context *dapm; + struct snd_soc_dapm_widget *w; + struct skl_module_cfg *mconfig; + int ret = 0; + + dapm = snd_soc_dapm_kcontrol_dapm(kcontrol); + w = snd_soc_dapm_kcontrol_widget(kcontrol); + mconfig = w->priv; + mconfig->gain_data->ramp_duration = ucontrol->value.integer.value[0]; + + if (w->power) + ret = skl_tplg_send_gain_ipc(dapm, mconfig); + return ret; +} + +static int skl_tplg_ramp_type_set(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dapm_context *dapm; + struct snd_soc_dapm_widget *w; + struct skl_module_cfg *mconfig; + int ret = 0; + + dapm = snd_soc_dapm_kcontrol_dapm(kcontrol); + w = snd_soc_dapm_kcontrol_widget(kcontrol); + mconfig = w->priv; + mconfig->gain_data->ramp_type = ucontrol->value.integer.value[0]; + + if (w->power) + ret = skl_tplg_send_gain_ipc(dapm, mconfig); + + return ret; +} + +static int skl_tplg_volume_set(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dapm_context *dapm; + struct snd_soc_dapm_widget *w; + struct skl_module_cfg *mconfig; + struct skl_module_iface *m_intf; + int ret = 0, i, max_channels; + + dapm = snd_soc_dapm_kcontrol_dapm(kcontrol); + w = snd_soc_dapm_kcontrol_widget(kcontrol); + mconfig = w->priv; + + m_intf = &mconfig->module->formats[mconfig->fmt_idx]; + max_channels = (m_intf->outputs[0].fmt.channels > + MAX_NUM_CHANNELS) ? MAX_NUM_CHANNELS : + m_intf->outputs[0].fmt.channels; + + for (i = 0; i < max_channels; i++) + if (ucontrol->value.integer.value[i] >= + ARRAY_SIZE(linear_gain)) { + dev_err(dapm->dev, + "Volume requested is out of range!!!\n"); + return -EINVAL; + } + + for (i = 0; i < max_channels; i++) + mconfig->gain_data->volume[i] = + linear_gain[ucontrol->value.integer.value[i]]; + + if (w->power) + ret = skl_tplg_send_gain_ipc(dapm, mconfig); + + return ret; +} + static int skl_tplg_multi_config_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { @@ -2832,6 +3270,22 @@ static const struct snd_soc_tplg_kcontrol_ops skl_tplg_kcontrol_ops[] = { .get = skl_tplg_multi_config_get, .put = skl_tplg_multi_config_set, }, + { + .id = SKL_CONTROL_TYPE_VOLUME, + .info = skl_tplg_volume_ctl_info, + .get = skl_tplg_volume_get, + .put = skl_tplg_volume_set, + }, + { + .id = SKL_CONTROL_TYPE_RAMP_DURATION, + .get = skl_tplg_ramp_duration_get, + .put = skl_tplg_ramp_duration_set, + }, + { + .id = SKL_CONTROL_TYPE_RAMP_TYPE, + .get = skl_tplg_ramp_type_get, + .put = skl_tplg_ramp_type_set, + }, }; static int skl_tplg_fill_pipe_cfg(struct device *dev, @@ -3922,6 +4376,19 @@ static int skl_tplg_widget_load(struct snd_soc_component *cmpnt, int index, if (ret < 0) return ret; + if (mconfig->m_type == SKL_MODULE_TYPE_GAIN) { + mconfig->gain_data = devm_kzalloc(bus->dev, + sizeof(*mconfig->gain_data), GFP_KERNEL); + + if (!mconfig->gain_data) + return -ENOMEM; + + mconfig->gain_data->ramp_duration = 0; + mconfig->gain_data->ramp_type = SKL_CURVE_NONE; + for (i = 0; i < MAX_NUM_CHANNELS; i++) + mconfig->gain_data->volume[i] = SKL_MAX_GAIN; + } + skl_debug_init_module(skl->debugfs, w, mconfig); bind_event: diff --git a/sound/soc/intel/skylake/skl-topology.h b/sound/soc/intel/skylake/skl-topology.h index f9ac42cdc64d..cc23a18fb2b4 100644 --- a/sound/soc/intel/skylake/skl-topology.h +++ b/sound/soc/intel/skylake/skl-topology.h @@ -55,6 +55,7 @@ #define SKL_MAX_MODULES_IN_PIPE 8 #define SKL_MAX_MODULE_FORMATS 64 #define SKL_MAX_MODULE_RESOURCES 32 +#define MAX_NUM_CHANNELS 8 enum skl_channel_index { SKL_CHANNEL_LEFT = 0, @@ -258,6 +259,11 @@ struct skl_kpb_params { } u; }; +struct skl_gain_module_config { + struct skl_base_cfg mconf; + struct skl_gain_config gain_cfg; +}; + struct skl_module_inst_id { uuid_le mod_uuid; int module_id; @@ -415,6 +421,12 @@ struct skl_sdw_aggregation { struct skl_sdw_agg_data agg_data[4]; }; +struct skl_gain_data { + u64 ramp_duration; + u32 ramp_type; + u32 volume[MAX_NUM_CHANNELS]; +}; + struct skl_module_cfg { u8 guid[16]; struct skl_module_inst_id id; @@ -459,6 +471,7 @@ struct skl_module_cfg { struct skl_pipe *pipe; struct skl_specific_cfg formats_config; struct skl_pipe_mcfg mod_cfg[SKL_MAX_MODULES_IN_PIPE]; + struct skl_gain_data *gain_data; }; struct skl_algo_data { From 326bc22c0f8a7efa7dc292b57e2c8ee3368e7121 Mon Sep 17 00:00:00 2001 From: Pankaj Bharadiya Date: Wed, 6 Sep 2017 14:04:55 +0530 Subject: [PATCH 0806/1103] ASoC: Intel: Skylake: Fix codec_dai NULL pointer dereferening Pointer 'codec_dai' returned from call to cnl_get_codec_dai() can be NULL. Check for the valid pointer before dereferencing. Change-Id: I783b6220e32a9b8bf7655b92df7a4b034175a509 Signed-off-by: Pankaj Bharadiya Reviewed-on: Reviewed-by: Prusty, Subhransu S Reviewed-by: audio_build Reviewed-by: Koul, Vinod Tested-by: Sm, Bhadur A --- sound/soc/intel/boards/cnl_rt274.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sound/soc/intel/boards/cnl_rt274.c b/sound/soc/intel/boards/cnl_rt274.c index 07a876f4f3b2..5570474806ab 100644 --- a/sound/soc/intel/boards/cnl_rt274.c +++ b/sound/soc/intel/boards/cnl_rt274.c @@ -76,6 +76,8 @@ static int cnl_rt274_clock_control(struct snd_soc_dapm_widget *w, int ret = 0, ratio = 100; struct snd_soc_dai *codec_dai = cnl_get_codec_dai(card, RT274_CODEC_DAI); + if (!codec_dai) + return -EINVAL; /* Codec needs clock for Jack detection and button press */ ret = snd_soc_dai_set_sysclk(codec_dai, RT274_SCLK_S_PLL2, From bda40d8df8da547dfd4caa652177460fa920691a Mon Sep 17 00:00:00 2001 From: Pankaj Bharadiya Date: Wed, 6 Sep 2017 14:04:56 +0530 Subject: [PATCH 0807/1103] ASoC: Intel: Skylake: Check for word_length_buffer allcation failure word_length_buffer buffer allocation can fail. Make sure to return -ENOMEM on word_length_buffer allocation failure Change-Id: Idf31300cadd6f014f729cae09e981b4459694dd9 Signed-off-by: Pankaj Bharadiya Reviewed-on: Reviewed-by: Prusty, Subhransu S Reviewed-by: audio_build Reviewed-by: Koul, Vinod Tested-by: Sm, Bhadur A --- sound/soc/intel/skylake/cnl-sst.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sound/soc/intel/skylake/cnl-sst.c b/sound/soc/intel/skylake/cnl-sst.c index c99b2c931202..813cd73545e4 100644 --- a/sound/soc/intel/skylake/cnl-sst.c +++ b/sound/soc/intel/skylake/cnl-sst.c @@ -638,6 +638,8 @@ static int skl_register_sdw_masters(struct device *dev, struct skl_sst *dsp, dpn_cap->word_length_buffer = kzalloc(((sizeof(unsigned int)) * dpn_cap->num_word_length), GFP_KERNEL); + if (!dpn_cap->word_length_buffer) + return -ENOMEM; for (k = 0; k < dpn_cap->num_word_length; k++) dpn_cap->word_length_buffer[k] = wl = wl + 8; wl = 0; From a85df907fc9d2731b05ec87448ea6382e7dce2d8 Mon Sep 17 00:00:00 2001 From: Pankaj Bharadiya Date: Wed, 6 Sep 2017 14:04:57 +0530 Subject: [PATCH 0808/1103] ASoC: Intel: Skylake: Fix cnl_sdw_startup() error path In cnl_sdw_startup function, error -EINVAL is returned after invalid stream type is detected without freeing the previously allocated sdw_dma_data memory. Add the missing error handling path to avoid memory leak. Change-Id: Iacdc9608ad65075d68ff46e0410c49c8358491cf Signed-off-by: Pankaj Bharadiya Reviewed-on: Reviewed-by: Prusty, Subhransu S Reviewed-by: Koul, Vinod Reviewed-by: audio_build Tested-by: Sm, Bhadur A --- sound/soc/intel/skylake/skl-sdw-pcm.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/sound/soc/intel/skylake/skl-sdw-pcm.c b/sound/soc/intel/skylake/skl-sdw-pcm.c index 6b8c4dba5b01..955c952cc4bb 100644 --- a/sound/soc/intel/skylake/skl-sdw-pcm.c +++ b/sound/soc/intel/skylake/skl-sdw-pcm.c @@ -97,7 +97,8 @@ int cnl_sdw_startup(struct snd_pcm_substream *substream, dma->stream_type = CNL_SDW_PDI_TYPE_PDM; else { dev_err(dai->dev, "Stream type not known\n"); - return -EINVAL; + ret = -EINVAL; + goto free_dma; } dma->mstr = mstr; dma->mstr_nr = sdw_ctrl_nr; @@ -113,13 +114,13 @@ int cnl_sdw_startup(struct snd_pcm_substream *substream, if (ret) { dev_err(dai->dev, "Unable to allocate stream tag"); ret = -EINVAL; - goto alloc_stream_tag_failed; + goto free_dma; } ret = snd_soc_dai_program_stream_tag(substream, dai, dma->stream_tag); dma->stream_state = STREAM_STATE_ALLOC_STREAM_TAG; return 0; -alloc_stream_tag_failed: +free_dma: kfree(dma); alloc_failed: sdw_put_master(mstr); From b5c124673b3921325a1e83e5886fdb2ade52e15e Mon Sep 17 00:00:00 2001 From: Pankaj Bharadiya Date: Wed, 6 Sep 2017 14:04:58 +0530 Subject: [PATCH 0809/1103] ASoC: Intel: Skylake: Fix error handling in cnl_sdw_hw_params() There are bunch of error paths where allocated resources are not freed before returning. Add the missing error handling to free up the allocated resources. Change-Id: I2b7b3e901d6878f951823661cfc32f03167c32fa Signed-off-by: Pankaj Bharadiya Reviewed-on: Reviewed-by: Prusty, Subhransu S Reviewed-by: Koul, Vinod Reviewed-by: audio_build Tested-by: Sm, Bhadur A --- sound/soc/intel/skylake/skl-sdw-pcm.c | 34 ++++++++++++++++++--------- 1 file changed, 23 insertions(+), 11 deletions(-) diff --git a/sound/soc/intel/skylake/skl-sdw-pcm.c b/sound/soc/intel/skylake/skl-sdw-pcm.c index 955c952cc4bb..fb3186a5a35f 100644 --- a/sound/soc/intel/skylake/skl-sdw-pcm.c +++ b/sound/soc/intel/skylake/skl-sdw-pcm.c @@ -141,6 +141,7 @@ int cnl_sdw_hw_params(struct snd_pcm_substream *substream, struct skl_pipe_params p_params = {0}; struct skl_module_cfg *m_cfg; int i, upscale_factor = 16; + int nr_port; p_params.s_fmt = snd_pcm_format_width(params_format(params)); p_params.ch = params_channels(params); @@ -168,13 +169,14 @@ int cnl_sdw_hw_params(struct snd_pcm_substream *substream, if (!dma->port) return -ENOMEM; - for (i = 0; i < dma->nr_ports; i++) { + for (nr_port = 0; nr_port < dma->nr_ports; nr_port++) { /* Dynamically alloc port and PDI streams for this DAI */ - dma->port[i] = cnl_sdw_alloc_port(dma->mstr, channels, + dma->port[nr_port] = cnl_sdw_alloc_port(dma->mstr, channels, direction, dma->stream_type); - if (!dma->port[i]) { + if (!dma->port[nr_port]) { dev_err(dai->dev, "Unable to allocate port\n"); - return -EINVAL; + ret = -EINVAL; + goto free_dma_port; } } @@ -182,7 +184,8 @@ int cnl_sdw_hw_params(struct snd_pcm_substream *substream, m_cfg = skl_tplg_be_get_cpr_module(dai, substream->stream); if (!m_cfg) { dev_err(dai->dev, "BE Copier not found\n"); - return -EINVAL; + ret = -EINVAL; + goto free_dma_port; } if (!m_cfg->sdw_agg_enable) @@ -192,7 +195,7 @@ int cnl_sdw_hw_params(struct snd_pcm_substream *substream, dma->port[0]->pdi_stream->sdw_pdi_num; ret = skl_tplg_be_update_params(dai, &p_params); if (ret) - return ret; + goto free_dma_port; stream_config.frame_rate = params_rate(params); @@ -216,13 +219,14 @@ int cnl_sdw_hw_params(struct snd_pcm_substream *substream, dma->stream_tag); if (ret) { dev_err(dai->dev, "Unable to configure the stream\n"); - return ret; + goto free_dma_port; } port_cfg = kcalloc(dma->nr_ports, sizeof(struct sdw_port_cfg), GFP_KERNEL); - if (!port_cfg) - return -ENOMEM; - + if (!port_cfg) { + ret = -ENOMEM; + goto free_dma_port; + } port_config.num_ports = dma->nr_ports; port_config.port_cfg = port_cfg; @@ -238,10 +242,18 @@ int cnl_sdw_hw_params(struct snd_pcm_substream *substream, ret = sdw_config_port(dma->mstr, NULL, &port_config, dma->stream_tag); if (ret) { dev_err(dai->dev, "Unable to configure port\n"); - return ret; + goto free_port_cfg; } dma->stream_state = STREAM_STATE_CONFIG_STREAM; return 0; + +free_port_cfg: + kfree(port_cfg); +free_dma_port: + while (nr_port--) + cnl_sdw_free_port(dma->mstr, dma->port[nr_port]->port_num); + kfree(dma->port); + return ret; } int cnl_sdw_hw_free(struct snd_pcm_substream *substream, From 09f31227939a84ecfcdacf47756dece65f3cf7b1 Mon Sep 17 00:00:00 2001 From: Pankaj Bharadiya Date: Mon, 18 Sep 2017 16:03:41 +0530 Subject: [PATCH 0810/1103] ASoC: Intel: Skylake: Check for NHLT ACPI header signature Due to buggy BIOS acpi_evaluate_dsm() may not return the correct NHLT table, so check the NHLT table header signature before accessing it. Change-Id: I525cc504f1cf71d969bdcc2120b8a280dedeb4f5 Signed-off-by: Pankaj Bharadiya Reviewed-on: Reviewed-by: audio_build Reviewed-by: Prusty, Subhransu S Reviewed-by: Koul, Vinod Tested-by: Sm, Bhadur A --- sound/soc/intel/skylake/skl-nhlt.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sound/soc/intel/skylake/skl-nhlt.c b/sound/soc/intel/skylake/skl-nhlt.c index 742b0cb0dd15..284e765eebad 100644 --- a/sound/soc/intel/skylake/skl-nhlt.c +++ b/sound/soc/intel/skylake/skl-nhlt.c @@ -28,6 +28,8 @@ static guid_t osc_guid = GUID_INIT(0xA69F886E, 0x6CEB, 0x4594, 0xA4, 0x1F, 0x7B, 0x5D, 0xCE, 0x24, 0xC5, 0x53); +#define NHLT_ACPI_HEADER_SIG "NHLT" + int skl_get_nhlt_version(struct device *dev) { const char *version; From 71bb648683826d73531e2db7354c01e18937afd1 Mon Sep 17 00:00:00 2001 From: "Shaik, Kareem M" Date: Thu, 6 Oct 2016 16:23:09 +0530 Subject: [PATCH 0811/1103] ASoC: Intel: Skylake: Fix Max DSP MCPS value Currently Max MCPS is specified as 30M, but it should be 350M. However actual MCPS is 400M, since module CPS are simulated on HW and it's peak MCPS is taken into count instead of average cycle count. So it is limited to 350M to avoid MCPS overshooting. Change-Id: I40f1a5b8ec66611da52cbfc9365b045015133270 Signed-off-by: Kareem Shaik Signed-off-by: Ramesh Babu Reviewed-on: Reviewed-by: audio_build Reviewed-by: Sinha, Mohit Reviewed-by: Kesapragada, Pardha Saradhi Reviewed-by: Nc, Shreyas Reviewed-by: Koul, Vinod Reviewed-by: Kp, Jeeja Tested-by: Sm, Bhadur A --- sound/soc/intel/skylake/skl-topology.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/intel/skylake/skl-topology.c b/sound/soc/intel/skylake/skl-topology.c index 451b28a21585..1931d4ed73f5 100644 --- a/sound/soc/intel/skylake/skl-topology.c +++ b/sound/soc/intel/skylake/skl-topology.c @@ -5166,7 +5166,7 @@ static void skl_tplg_set_pipe_type(struct skl *skl, struct skl_pipe *pipe) } /* This will be read from topology manifest, currently defined here */ -#define SKL_MAX_MCPS 30000000 +#define SKL_MAX_MCPS 350000000 #define SKL_FW_MAX_MEM 1000000 /* From daabd558e77c73c4788125c33639eb026c6aacfa Mon Sep 17 00:00:00 2001 From: "Mallikarjun, chippalkatti" Date: Mon, 19 Jun 2017 09:33:05 +0530 Subject: [PATCH 0812/1103] ASoC: Intel: Skylake: Fix bug in module id retrieval for link copier This was a copy paste mistake introduced in commit e3bd94ea9644 ("ASoC: Intel: CNL: Retrieve module id from GUID").So, fix it. Change-Id: Iab9bcf9433f0e2441bf88a886a762b93f5bbdf31 Signed-off-by: Mallikarjun, chippalkatti Reviewed-on: Reviewed-by: Pawse, GuruprasadX Reviewed-by: R, Dharageswari Reviewed-by: audio_build Reviewed-by: Sinha, Mohit Reviewed-by: Kesapragada, Pardha Saradhi Reviewed-by: Kale, Sanyog R Tested-by: Sm, Bhadur A --- sound/soc/intel/skylake/skl-messages.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sound/soc/intel/skylake/skl-messages.c b/sound/soc/intel/skylake/skl-messages.c index ba60269255f6..891e24842be3 100644 --- a/sound/soc/intel/skylake/skl-messages.c +++ b/sound/soc/intel/skylake/skl-messages.c @@ -702,8 +702,8 @@ static int cnl_sdw_bra_pipe_cfg_cp(struct skl_sst *ctx, link_cpr_params.s_fmt = 32; link_cpr_params.linktype = 0; link_cpr_params.stream = 0; - host_cpr_cfg->id.module_id = skl_get_module_id(ctx, - (uuid_le *)host_cpr_cfg->guid); + link_cpr_cfg->id.module_id = skl_get_module_id(ctx, + (uuid_le *)link_cpr_cfg->guid); link_cpr_cfg->id.instance_id = 3; link_cpr_cfg->id.pvt_id = skl_get_pvt_id(ctx, From 9fc64fb7b6b63b7d07328e54ebf47ad0b8b3b06d Mon Sep 17 00:00:00 2001 From: "Pawse, GuruprasadX" Date: Wed, 11 Oct 2017 10:41:43 +0530 Subject: [PATCH 0813/1103] ASoC: Intel: Skylake: Fix incorrect in_fmt and out_fmt pointers in BRA playback pipeline Fix the in_fmt/out_fmt pointers for host copier of playback pipeline. Change-Id: I8a6c66ec5ebc291ae968ebca779d702cc4de105e Signed-off-by: Pawse, GuruprasadX Reviewed-on: Reviewed-by: audio_build Reviewed-by: R, Dharageswari Reviewed-by: Sinha, Mohit Reviewed-by: Kale, Sanyog R Reviewed-by: H S, Vijay Reviewed-by: Koul, Vinod Tested-by: Sm, Bhadur A --- sound/soc/intel/skylake/skl-messages.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sound/soc/intel/skylake/skl-messages.c b/sound/soc/intel/skylake/skl-messages.c index 891e24842be3..84acb0630373 100644 --- a/sound/soc/intel/skylake/skl-messages.c +++ b/sound/soc/intel/skylake/skl-messages.c @@ -410,8 +410,8 @@ static int cnl_sdw_bra_pipe_cfg_pb(struct skl_sst *ctx, */ memcpy(host_cpr_cfg->guid, &guid, 16); memcpy(link_cpr_cfg->guid, &guid, 16); - in_fmt = &link_cpr_cfg->module->formats[0].inputs[0].fmt; - out_fmt = &link_cpr_cfg->module->formats[0].outputs[0].fmt; + in_fmt = &host_cpr_cfg->module->formats[0].inputs[0].fmt; + out_fmt = &host_cpr_cfg->module->formats[0].outputs[0].fmt; /* Playback pipeline */ host_cpr_pipe = kzalloc(sizeof(struct skl_pipe), GFP_KERNEL); From 449bd390f32452916d002284f3a351dd71e7e384 Mon Sep 17 00:00:00 2001 From: "Sinha, Mohit" Date: Thu, 26 Oct 2017 14:52:16 +0530 Subject: [PATCH 0814/1103] ASoC: Intel: Skylake: Avoid global kcontrol pointer for event handling The audio driver registers multiple kcontrols to represent multiple events like topology change or async notifications. However, using a single global variable to cache and to point to these several kcontrols in skl_dsp_cb_event() resulted in incorrect pointer being accessed. Hence use a local variable to point to the kcontrol for the current event handled by skl_dsp_cb_event() Change-Id: I6230650153abbe59fe54e70d81718702f05d6d77 Signed-off-by: Mohit Sinha Reviewed-on: Reviewed-by: Shaik, Kareem M Reviewed-by: B, Jayachandran Reviewed-by: Prusty, Subhransu S Reviewed-by: Koul, Vinod Reviewed-by: audio_build Tested-by: Sm, Bhadur A --- sound/soc/intel/skylake/skl-sst-ipc.h | 2 -- sound/soc/intel/skylake/skl-topology.c | 29 +++++++++++++------------- 2 files changed, 14 insertions(+), 17 deletions(-) diff --git a/sound/soc/intel/skylake/skl-sst-ipc.h b/sound/soc/intel/skylake/skl-sst-ipc.h index 76e83d216e49..4fa372c1bf38 100644 --- a/sound/soc/intel/skylake/skl-sst-ipc.h +++ b/sound/soc/intel/skylake/skl-sst-ipc.h @@ -319,8 +319,6 @@ struct skl_sst { /* sysfs for module info */ struct skl_sysfs_tree *sysfs_tree; - struct snd_kcontrol *kcontrol; - struct list_head notify_kctls; }; diff --git a/sound/soc/intel/skylake/skl-topology.c b/sound/soc/intel/skylake/skl-topology.c index 1931d4ed73f5..514b316ddd47 100644 --- a/sound/soc/intel/skylake/skl-topology.c +++ b/sound/soc/intel/skylake/skl-topology.c @@ -3169,6 +3169,7 @@ int skl_dsp_cb_event(struct skl_sst *ctx, unsigned int event, struct skl *skl = get_skl_ctx(ctx->dev); struct snd_soc_component *component = skl->component; struct skl_module_notify *m_notification = NULL; + struct snd_kcontrol *kcontrol; struct skl_algo_data *bc; u8 param_length; @@ -3176,17 +3177,15 @@ int skl_dsp_cb_event(struct skl_sst *ctx, unsigned int event, case SKL_TPLG_CHG_NOTIFY: card = component->card; - if (!ctx->kcontrol) { - ctx->kcontrol = snd_soc_card_get_kcontrol(card, - "Topology Change Notification"); - if (!ctx->kcontrol) { - dev_dbg(ctx->dev, - "NOTIFICATION Controls not found\n"); - return -EINVAL; - } + kcontrol = snd_soc_card_get_kcontrol(card, + "Topology Change Notification"); + if (!kcontrol) { + dev_warn(ctx->dev, + "NOTIFICATION Controls not found\n"); + return -EINVAL; } - sb = (struct soc_bytes_ext *)ctx->kcontrol->private_value; + sb = (struct soc_bytes_ext *)kcontrol->private_value; if (!sb->dobj.private) { sb->dobj.private = devm_kzalloc(ctx->dev, sizeof(*notify_data), GFP_KERNEL); @@ -3196,25 +3195,25 @@ int skl_dsp_cb_event(struct skl_sst *ctx, unsigned int event, memcpy(sb->dobj.private, notify_data, sizeof(*notify_data)); snd_ctl_notify(card->snd_card, SNDRV_CTL_EVENT_MASK_VALUE, - &ctx->kcontrol->id); + &kcontrol->id); break; case SKL_EVENT_GLB_MODULE_NOTIFICATION: m_notification = (struct skl_module_notify *)notify_data->data; card = component->card; - ctx->kcontrol = skl_get_notify_kcontrol(ctx, card->snd_card, + kcontrol = skl_get_notify_kcontrol(ctx, card->snd_card, m_notification->unique_id); - if (!ctx->kcontrol) { - dev_dbg(ctx->dev, "Module notify control not found\n"); + if (!kcontrol) { + dev_warn(ctx->dev, "Module notify control not found\n"); return -EINVAL; } - sb = (struct soc_bytes_ext *)ctx->kcontrol->private_value; + sb = (struct soc_bytes_ext *)kcontrol->private_value; bc = (struct skl_algo_data *)sb->dobj.private; param_length = sizeof(struct skl_notify_data) + notify_data->length; memcpy(bc->params, (char *)notify_data, param_length); snd_ctl_notify(card->snd_card, - SNDRV_CTL_EVENT_MASK_VALUE, &ctx->kcontrol->id); + SNDRV_CTL_EVENT_MASK_VALUE, &kcontrol->id); break; default: return -EINVAL; From 6b831a8e7203e47e02da2e4ae08d967428449464 Mon Sep 17 00:00:00 2001 From: Kareem Shaik Date: Tue, 31 Oct 2017 15:44:52 +0530 Subject: [PATCH 0815/1103] ASoC: Intel: Skylake: Return default sampling rate for Probe compress devices. Defining sampling rate for getting Probe data from probe compress devices. As user space expects a valid sample rate but existing API compress_get_hpointer returns an error if sampling rate is 0. Change-Id: I0482c96fea4583a93b952d7484a4234388d6a6f0 Signed-off-by: Kareem Shaik Reviewed-on: Reviewed-by: B, Jayachandran Reviewed-by: Kesapragada, Pardha Saradhi Reviewed-by: Tewani, Pradeep D Reviewed-by: Koul, Vinod Tested-by: Sm, Bhadur A --- sound/soc/intel/skylake/skl-pcm.c | 6 ++++++ sound/soc/intel/skylake/skl-probe.c | 5 +++++ 2 files changed, 11 insertions(+) diff --git a/sound/soc/intel/skylake/skl-pcm.c b/sound/soc/intel/skylake/skl-pcm.c index 4679bf9f4543..8702f0c3e17e 100644 --- a/sound/soc/intel/skylake/skl-pcm.c +++ b/sound/soc/intel/skylake/skl-pcm.c @@ -1412,6 +1412,9 @@ static struct snd_soc_dai_driver skl_platform_dai[] = { .playback = { .stream_name = "Probe Playback", .channels_min = HDA_MONO, + .rates = SNDRV_PCM_RATE_48000, + .rate_min = 48000, + .rate_max = 48000, }, }, { @@ -1421,6 +1424,9 @@ static struct snd_soc_dai_driver skl_platform_dai[] = { .capture = { .stream_name = "Probe Capture", .channels_min = HDA_MONO, + .rates = SNDRV_PCM_RATE_48000, + .rate_min = 48000, + .rate_max = 48000, }, }, }; diff --git a/sound/soc/intel/skylake/skl-probe.c b/sound/soc/intel/skylake/skl-probe.c index 0716bf62f92e..4a22c75c552b 100644 --- a/sound/soc/intel/skylake/skl-probe.c +++ b/sound/soc/intel/skylake/skl-probe.c @@ -323,6 +323,11 @@ int skl_probe_compr_tstamp(struct snd_compr_stream *stream, tstamp->copied_total = hstream->hstream.curr_pos; + if (stream->direction == SND_COMPRESS_PLAYBACK) + tstamp->sampling_rate = snd_pcm_rate_bit_to_rate(dai->driver->playback.rates); + else + tstamp->sampling_rate = snd_pcm_rate_bit_to_rate(dai->driver->capture.rates); + return 0; } From ee184a29184566a7af31d497151a8358d5b7d800 Mon Sep 17 00:00:00 2001 From: "Gogineni, GiribabuX" Date: Fri, 27 Oct 2017 19:30:32 +0530 Subject: [PATCH 0816/1103] ASoC: Intel: Skylake: Add ULL machine driver entry This patch adds ULL machine driver name to driver data. Change-Id: Ib6e47b14f7e7c8a24a3bdad4034a39008de7f3be Signed-off-by: Gogineni, GiribabuX Reviewed-on: Reviewed-by: Tewani, Pradeep D Reviewed-by: Shaik, Kareem M Reviewed-by: Koul, Vinod Reviewed-by: Kale, Sanyog R Reviewed-by: audio_build Tested-by: Sm, Bhadur A --- sound/soc/intel/skylake/skl.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/sound/soc/intel/skylake/skl.c b/sound/soc/intel/skylake/skl.c index a848eae708c8..ba614c96254b 100644 --- a/sound/soc/intel/skylake/skl.c +++ b/sound/soc/intel/skylake/skl.c @@ -1189,6 +1189,11 @@ static struct snd_soc_acpi_mach sst_bxtp_devdata[] = { .drv_name = "bxt_tdf8532", .fw_filename = "intel/dsp_fw_bxtn.bin", }, + { + .id = "INT34C3", + .drv_name = "bxt_ivi_ull", + .fw_filename = "intel/dsp_fw_ull_bxtn.bin", + }, {} }; From 298b3f0c22f1273b2711a8cc50e5641c37ee1b47 Mon Sep 17 00:00:00 2001 From: "Gogineni, GiribabuX" Date: Fri, 27 Oct 2017 18:50:54 +0530 Subject: [PATCH 0817/1103] ASoC: Intel: Board: Add BXTP MRB ULL machine driver This is the machine driver for Ultra Low latency(ULL) topology on MRB using dummy codec in I2S mode. Change-Id: Ica5c6515ccf99660efcea79d6ff67f3946e9b0d8 Signed-off-by: Kareem Shaik Reviewed-on: Reviewed-by: audio_build Reviewed-by: Tewani, Pradeep D Reviewed-by: Kale, Sanyog R Reviewed-by: Koul, Vinod Tested-by: Sm, Bhadur A --- sound/soc/intel/boards/Kconfig | 9 + sound/soc/intel/boards/Makefile | 2 + sound/soc/intel/boards/bxt_ivi_ull.c | 330 +++++++++++++++++++++++++++ 3 files changed, 341 insertions(+) create mode 100644 sound/soc/intel/boards/bxt_ivi_ull.c diff --git a/sound/soc/intel/boards/Kconfig b/sound/soc/intel/boards/Kconfig index e655978d3418..45b1d5a03ed0 100644 --- a/sound/soc/intel/boards/Kconfig +++ b/sound/soc/intel/boards/Kconfig @@ -360,6 +360,15 @@ config SND_SOC_INTEL_BXT_TDF8532_MACH platforms with TDF8532 I2S audio codec. Say Y or m if you have such a device. This is a recommended option. If unsure select "N". + +config SND_SOC_INTEL_BXT_ULL_MACH + tristate "Broxton ULL reference for GP MRB platform" + depends on MFD_INTEL_LPSS && I2C && ACPI + help + This adds support for ASoC machine driver for Broxton ULL GP MRB + platform. + Say Y or m if you have such a device. This is a recommended option. + If unsure select "N". endif ## SND_SOC_INTEL_SKYLAKE endif ## SND_SOC_INTEL_MACH diff --git a/sound/soc/intel/boards/Makefile b/sound/soc/intel/boards/Makefile index 239051cc6f22..9a77052f0469 100644 --- a/sound/soc/intel/boards/Makefile +++ b/sound/soc/intel/boards/Makefile @@ -28,6 +28,7 @@ snd-soc-icl-rt274-objs := icl_rt274.o snd-soc-cnl_cs42l42-objs := cnl_cs42l42.o snd-soc-cnl_rt700-objs := cnl_rt700.o snd-soc-cnl_svfpga-objs := cnl_svfpga.o +snd-soc-bxt_ivi_ull-objs := bxt_ivi_ull.o obj-$(CONFIG_SND_SOC_INTEL_HASWELL_MACH) += snd-soc-sst-haswell.o obj-$(CONFIG_SND_SOC_INTEL_BYT_RT5640_MACH) += snd-soc-sst-byt-rt5640-mach.o @@ -58,3 +59,4 @@ obj-$(CONFIG_SND_SOC_INTEL_ICL_RT274_MACH) += snd-soc-icl-rt274.o obj-$(CONFIG_SND_SOC_INTEL_CNL_CS42L42_MACH) += snd-soc-cnl_cs42l42.o obj-$(CONFIG_SND_SOC_INTEL_CNL_RT700_MACH) += snd-soc-cnl_rt700.o obj-$(CONFIG_SND_SOC_INTEL_CNL_SVFPGA_MACH) += snd-soc-cnl_svfpga.o +obj-$(CONFIG_SND_SOC_INTEL_BXT_ULL_MACH) += snd-soc-bxt_ivi_ull.o diff --git a/sound/soc/intel/boards/bxt_ivi_ull.c b/sound/soc/intel/boards/bxt_ivi_ull.c new file mode 100644 index 000000000000..22ae8ab30a1d --- /dev/null +++ b/sound/soc/intel/boards/bxt_ivi_ull.c @@ -0,0 +1,330 @@ +/* + * Intel Broxton-P I2S ULL Machine Driver + * + * Copyright (C) 2017, Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License version + * 2 as published by the Free Software Foundation. + * + * 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. + */ + +#include +#include +#include +#include +#include +#include + +static const struct snd_soc_pcm_stream media1_out_params = { + .formats = SNDRV_PCM_FMTBIT_S32_LE, + .rate_min = 48000, + .rate_max = 48000, + .channels_min = 3, + .channels_max = 3, +}; + +static const struct snd_soc_pcm_stream codec1_in_params = { + .formats = SNDRV_PCM_FMTBIT_S32_LE, + .rate_min = 48000, + .rate_max = 48000, + .channels_min = 6, + .channels_max = 6, +}; + +static const struct snd_soc_pcm_stream codec0_in_params = { + .formats = SNDRV_PCM_FMTBIT_S32_LE, + .rate_min = 48000, + .rate_max = 48000, + .channels_min = 1, + .channels_max = 1, +}; + +static const struct snd_soc_dapm_widget broxton_widgets[] = { + SND_SOC_DAPM_SPK("DummySpeaker1", NULL), + SND_SOC_DAPM_SPK("DummySpeaker2", NULL), + SND_SOC_DAPM_SPK("DummySpeaker3", NULL), + SND_SOC_DAPM_SPK("DummySpeaker4", NULL), + SND_SOC_DAPM_MIC("DummyMIC0", NULL), + SND_SOC_DAPM_MIC("DummyMIC2", NULL), + SND_SOC_DAPM_MIC("DummyMIC4", NULL), +}; + +static const struct snd_soc_dapm_route bxtp_ull_map[] = { + {"8ch_pt_in3", NULL, "ssp0 Rx" }, + {"ssp0 Rx", NULL, "Dummy Capture" }, + {"Dummy Capture", NULL, "DummyMIC0"}, + + {"DummySpeaker2", NULL, "Dummy Playback2"}, + {"Dummy Playback2", NULL, "ssp2 Tx"}, + {"ssp2 Tx", NULL, "8ch_pt_out2"}, + + {"DummySpeaker1", NULL, "Dummy Playback1"}, + {"Dummy Playback1", NULL, "ssp1 Tx"}, + {"ssp1 Tx", NULL, "8ch_pt_out3"}, + + {"8ch_pt_in2", NULL, "ssp2 Rx" }, + {"ssp2 Rx", NULL, "Dummy Capture2" }, + {"Dummy Capture2", NULL, "DummyMIC2"}, + + {"DummySpeaker4", NULL, "Dummy Playback4"}, + {"Dummy Playback4", NULL, "ssp4 Tx"}, + {"ssp4 Tx", NULL, "8ch_pt_out"}, + + {"8ch_pt_in", NULL, "ssp4 Rx" }, + {"ssp4 Rx", NULL, "Dummy Capture4" }, + {"Dummy Capture4", NULL, "DummyMIC4"}, + + /* (ANC) Codec1_in - Loop pipe */ + { "codec1_in", NULL, "ssp0-b Rx" }, + { "ssp0-b Rx", NULL, "Dummy Capture" }, + + /* Codec0_in - Loop pipe */ + { "codec0_in", NULL, "ssp2-b Rx" }, + { "ssp2-b Rx", NULL, "Dummy Capture2" }, + + /* Media1_out Loop Path */ + {"DummySpeaker3", NULL, "Dummy Playback3"}, + { "Dummy Playback3", NULL, "ssp1-b Tx"}, + { "ssp1-b Tx", NULL, "media1_out"}, +}; + +/* broxton digital audio interface glue - connects codec <--> CPU */ +static struct snd_soc_dai_link bxtp_ull_dais[] = { + { + .name = "Bxt Audio Port 3", + .stream_name = "Stereo-16K SSP4", + .cpu_dai_name = "System Pin 3", + .platform_name = "0000:00:0e.0", + .nonatomic = 1, + .dynamic = 1, + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .dpcm_playback = 1, + .dpcm_capture = 1, + }, + { + .name = "Bxt Audio Port 4", + .stream_name = "5-ch SSP1", + .cpu_dai_name = "System Pin 4", + .platform_name = "0000:00:0e.0", + .nonatomic = 1, + .dynamic = 1, + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .dpcm_playback = 1, + }, + { + .name = "Bxt Audio Port 5", + .stream_name = "SSP2 Stream", + .cpu_dai_name = "System Pin 5", + .platform_name = "0000:00:0e.0", + .nonatomic = 1, + .dynamic = 1, + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .dpcm_playback = 1, + .dpcm_capture = 1, + }, + { + .name = "Bxt Audio Port 6", + .stream_name = "8-Ch SSP0", + .cpu_dai_name = "System Pin 6", + .platform_name = "0000:00:0e.0", + .nonatomic = 1, + .dynamic = 1, + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .dpcm_capture = 1, + }, + /* Probe DAI Links */ + { + .name = "Bxt Compress Probe playback", + .stream_name = "Probe Playback", + .cpu_dai_name = "Compress Probe0 Pin", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .platform_name = "0000:00:0e.0", + .init = NULL, + .nonatomic = 1, + }, + { + .name = "Bxt Compress Probe capture", + .stream_name = "Probe Capture", + .cpu_dai_name = "Compress Probe1 Pin", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .platform_name = "0000:00:0e.0", + .init = NULL, + .nonatomic = 1, + }, + /* Trace Buffer DAI links */ + { + .name = "Bxt Trace Buffer0", + .stream_name = "Core 0 Trace Buffer", + .cpu_dai_name = "TraceBuffer0 Pin", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .platform_name = "0000:00:0e.0", + .capture_only = true, + .ignore_suspend = 1, + }, + { + .name = "Bxt Trace Buffer1", + .stream_name = "Core 1 Trace Buffer", + .cpu_dai_name = "TraceBuffer1 Pin", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .platform_name = "0000:00:0e.0", + .capture_only = true, + .ignore_suspend = 1, + }, + /* CODEC<->CODEC link */ + { + .name = "Bxtn SSP0 Port", + .stream_name = "Bxtn SSP0", + .cpu_dai_name = "SSP0-B Pin", + .platform_name = "0000:00:0e.0", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .params = &codec1_in_params, + .dsp_loopback = true, + }, + + { + .name = "Bxtn SSP2 port", + .stream_name = "Bxtn SSP2", + .cpu_dai_name = "SSP2-B Pin", + .platform_name = "0000:00:0e.0", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .params = &codec0_in_params, + .dsp_loopback = true, + }, + + { + .name = "Bxtn SSP1 port", + .stream_name = "Bxtn SSP2", + .cpu_dai_name = "SSP1-B Pin", + .platform_name = "0000:00:0e.0", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .params = &media1_out_params, + .dsp_loopback = true, + }, + + /* Back End DAI links */ + { + /* SSP4 - Codec */ + .name = "SSP4-Codec", + .cpu_dai_name = "SSP4 Pin", + .platform_name = "0000:00:0e.0", + .no_pcm = 1, + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + .dpcm_playback = 1, + .dpcm_capture = 1, + }, + { + /* SSP1 - Codec */ + .name = "SSP1-Codec", + .cpu_dai_name = "SSP1 Pin", + .platform_name = "0000:00:0e.0", + .no_pcm = 1, + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + .dpcm_playback = 1, + }, + { + /* SSP2 - Codec */ + .name = "SSP2-Codec", + .cpu_dai_name = "SSP2 Pin", + .platform_name = "0000:00:0e.0", + .no_pcm = 1, + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + .dpcm_playback = 1, + .dpcm_capture = 1, + }, + { + /* SSP0 - Codec */ + .name = "SSP0-Codec", + .cpu_dai_name = "SSP0 Pin", + .platform_name = "0000:00:0e.0", + .no_pcm = 1, + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + .dpcm_capture = 1, + }, +}; + +static int bxt_add_dai_link(struct snd_soc_card *card, + struct snd_soc_dai_link *link) +{ + link->platform_name = "0000:00:0e.0"; + link->nonatomic = 1; + return 0; +} + +/* broxton audio machine driver for ULL Dummy Codec*/ +static struct snd_soc_card bxtp_ull = { + .name = "bxtp-ull", + .owner = THIS_MODULE, + .dai_link = bxtp_ull_dais, + .num_links = ARRAY_SIZE(bxtp_ull_dais), + .dapm_widgets = broxton_widgets, + .num_dapm_widgets = ARRAY_SIZE(broxton_widgets), + .dapm_routes = bxtp_ull_map, + .num_dapm_routes = ARRAY_SIZE(bxtp_ull_map), + .fully_routed = false, + .add_dai_link = bxt_add_dai_link, +}; + +static int broxton_audio_probe(struct platform_device *pdev) +{ + dev_info(&pdev->dev, "%s registering %s\n", __func__, pdev->name); + bxtp_ull.dev = &pdev->dev; + return snd_soc_register_card(&bxtp_ull); +} + +static int broxton_audio_remove(struct platform_device *pdev) +{ + snd_soc_unregister_card(&bxtp_ull); + return 0; +} + +static struct platform_driver broxton_audio = { + .probe = broxton_audio_probe, + .remove = broxton_audio_remove, + .driver = { + .name = "bxt_ivi_ull", + .pm = &snd_soc_pm_ops, + }, +}; + +module_platform_driver(broxton_audio) + +/* Module information */ +MODULE_DESCRIPTION("Intel SST Audio for Broxton ULL Machine"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:bxtp_i2s_ull"); From 58af0e9e152a890b61894d8ca725e287965837f6 Mon Sep 17 00:00:00 2001 From: "Shaik, Kareem M" Date: Thu, 26 Oct 2017 02:56:25 +0530 Subject: [PATCH 0818/1103] ASoC: Intel: Skylake: Add support to configure ADSP Scheduler ADSP scheduler can be configured to run either from 1. Internal Timer 2. DMA completion interrupts from SSP/DMIC This patch adds support to send this information to the ADSP during every D0/D3 cycle as a Large Config Set IPC. In addition to providing the source, there is a provision to alter the LL task period to values less than 1ms. Change-Id: Id856ef66a0930c6dceab804f8081c7449527476b Signed-off-by: Kareem Shaik Reviewed-on: Reviewed-by: audio_build Reviewed-by: Kesapragada, Pardha Saradhi Reviewed-by: Prusty, Subhransu S Tested-by: Sm, Bhadur A --- include/uapi/sound/snd_sst_tokens.h | 29 ++++++++++++++++- sound/soc/intel/skylake/skl-messages.c | 9 ++---- sound/soc/intel/skylake/skl-pcm.c | 16 +++++----- sound/soc/intel/skylake/skl-sst-ipc.c | 6 ++-- sound/soc/intel/skylake/skl-sst-ipc.h | 2 +- sound/soc/intel/skylake/skl-topology.c | 44 ++++++++++++++++++++++++++ sound/soc/intel/skylake/skl-topology.h | 1 + sound/soc/intel/skylake/skl.h | 11 +++++++ 8 files changed, 99 insertions(+), 19 deletions(-) diff --git a/include/uapi/sound/snd_sst_tokens.h b/include/uapi/sound/snd_sst_tokens.h index b29d07b018ea..ba4806d5ef17 100644 --- a/include/uapi/sound/snd_sst_tokens.h +++ b/include/uapi/sound/snd_sst_tokens.h @@ -261,6 +261,25 @@ * * %SKL_TKN_U32_DMA_MIN_SIZE: Minimum DMA buffer size * + * %SKL_TKN_U32_SCH_TYPE: Types of FW configs: SCHEDULER_CONFIG + * + * %SKL_TKN_U32_SCH_SIZE: Scheduler config size + * + * %SKL_TKN_U32_SCH_SYS_TICK_MUL: + * System tick multiplier + * + * %SKL_TKN_U32_SCH_SYS_TICK_DIV: + * System tick divider + * + * %SKL_TKN_U32_SCH_SYS_TICK_LL_SRC: + * Low Latency interrupt source + * + * %SKL_TKN_U32_SCH_SYS_TICK_CFG_LEN: + * Config length + * + * %SKL_TKN_U32_SCH_SYS_TICK_CFG: + * Config contain capture on which SSP to + * active the FW * * module_id and loadable flags dont have tokens as these values will be * read from the DSP FW manifest @@ -365,7 +384,15 @@ enum SKL_TKNS { SKL_TKN_U32_DMA_SIZE, SKL_TKN_U32_DMA_MAX_SIZE, SKL_TKN_U32_DMA_MIN_SIZE, - SKL_TKN_MAX = SKL_TKN_U32_DMA_MIN_SIZE, + + SKL_TKN_U32_SCH_TYPE, + SKL_TKN_U32_SCH_SIZE, + SKL_TKN_U32_SCH_SYS_TICK_MUL, + SKL_TKN_U32_SCH_SYS_TICK_DIV, + SKL_TKN_U32_SCH_SYS_TICK_LL_SRC, + SKL_TKN_U32_SCH_SYS_TICK_CFG_LEN, + SKL_TKN_U32_SCH_SYS_TICK_CFG, + SKL_TKN_MAX = SKL_TKN_U32_SCH_SYS_TICK_CFG, }; /* diff --git a/sound/soc/intel/skylake/skl-messages.c b/sound/soc/intel/skylake/skl-messages.c index 84acb0630373..b97255b5aae0 100644 --- a/sound/soc/intel/skylake/skl-messages.c +++ b/sound/soc/intel/skylake/skl-messages.c @@ -1356,13 +1356,10 @@ int skl_resume_dsp(struct skl *skl) skl->cfg.astate_cfg); } - /* Set DMA buffer configuration */ - if (skl->cfg.dmacfg.size) - skl_ipc_set_dma_cfg(&skl->skl_sst->ipc, BXT_INSTANCE_ID, - BXT_BASE_FW_MODULE_ID, (u32 *)(&skl->cfg.dmacfg)); + /* Set the FW config info from topology */ + skl_tplg_fw_cfg_set(skl); - /* Set DMA clock controls */ - return skl_dsp_set_dma_clk_controls(skl->skl_sst); + return ret; } enum skl_bitdepth skl_get_bit_depth(int params) diff --git a/sound/soc/intel/skylake/skl-pcm.c b/sound/soc/intel/skylake/skl-pcm.c index 8702f0c3e17e..cd036f5fe515 100644 --- a/sound/soc/intel/skylake/skl-pcm.c +++ b/sound/soc/intel/skylake/skl-pcm.c @@ -1872,14 +1872,14 @@ static int skl_platform_soc_probe(struct snd_soc_component *component) return ret; } - /* Set DMA buffer configuration */ - if (skl->cfg.dmacfg.size) - skl_ipc_set_dma_cfg(&skl->skl_sst->ipc, - BXT_INSTANCE_ID, BXT_BASE_FW_MODULE_ID, - (u32 *)(&skl->cfg.dmacfg)); - - /* Set DMA clock controls */ - skl_dsp_set_dma_clk_controls(skl->skl_sst); + if (skl->cfg.astate_cfg != NULL) { + skl_dsp_set_astate_cfg(skl->skl_sst, + skl->cfg.astate_cfg->count, + skl->cfg.astate_cfg); + } + + /* Set the FW config info from topology */ + skl_tplg_fw_cfg_set(skl); skl_populate_modules(skl); skl->skl_sst->update_d0i3c = skl_update_d0i3c; diff --git a/sound/soc/intel/skylake/skl-sst-ipc.c b/sound/soc/intel/skylake/skl-sst-ipc.c index d7c75da0625f..13917d54dc32 100644 --- a/sound/soc/intel/skylake/skl-sst-ipc.c +++ b/sound/soc/intel/skylake/skl-sst-ipc.c @@ -1165,7 +1165,7 @@ int skl_ipc_get_large_config(struct sst_generic_ipc *ipc, } EXPORT_SYMBOL_GPL(skl_ipc_get_large_config); -void skl_ipc_set_dma_cfg(struct sst_generic_ipc *ipc, u8 instance_id, +void skl_ipc_set_fw_cfg(struct sst_generic_ipc *ipc, u8 instance_id, u16 module_id, u32 *data) { struct skl_ipc_large_config_msg msg = {0}; @@ -1180,9 +1180,9 @@ void skl_ipc_set_dma_cfg(struct sst_generic_ipc *ipc, u8 instance_id, ret = skl_ipc_set_large_config(ipc, &msg, data); if (ret < 0) - dev_err(ipc->dev, "ipc: set dma config failed, err %d\n", ret); + dev_err(ipc->dev, "ipc: set fw config failed, err %d\n", ret); } -EXPORT_SYMBOL_GPL(skl_ipc_set_dma_cfg); +EXPORT_SYMBOL_GPL(skl_ipc_set_fw_cfg); int skl_sst_ipc_load_library(struct sst_generic_ipc *ipc, u8 dma_id, u8 table_id, bool wait) diff --git a/sound/soc/intel/skylake/skl-sst-ipc.h b/sound/soc/intel/skylake/skl-sst-ipc.h index 4fa372c1bf38..4b3c7e283030 100644 --- a/sound/soc/intel/skylake/skl-sst-ipc.h +++ b/sound/soc/intel/skylake/skl-sst-ipc.h @@ -472,6 +472,6 @@ void skl_ipc_tx_data_copy(struct ipc_message *msg, char *tx_data, int skl_notify_tplg_change(struct skl_sst *ctx, int type); int skl_dsp_crash_dump_read(struct skl_sst *ctx); -void skl_ipc_set_dma_cfg(struct sst_generic_ipc *ipc, u8 instance_id, +void skl_ipc_set_fw_cfg(struct sst_generic_ipc *ipc, u8 instance_id, u16 module_id, u32 *data); #endif /* __SKL_IPC_H */ diff --git a/sound/soc/intel/skylake/skl-topology.c b/sound/soc/intel/skylake/skl-topology.c index 514b316ddd47..22c1795d4624 100644 --- a/sound/soc/intel/skylake/skl-topology.c +++ b/sound/soc/intel/skylake/skl-topology.c @@ -4797,6 +4797,34 @@ static int skl_tplg_get_int_tkn(struct device *dev, tkn_elem->value; break; + case SKL_TKN_U32_SCH_TYPE: + skl->cfg.sch_cfg.type = tkn_elem->value; + break; + + case SKL_TKN_U32_SCH_SIZE: + skl->cfg.sch_cfg.length = tkn_elem->value; + break; + + case SKL_TKN_U32_SCH_SYS_TICK_MUL: + skl->cfg.sch_cfg.sys_tick_mul = tkn_elem->value; + break; + + case SKL_TKN_U32_SCH_SYS_TICK_DIV: + skl->cfg.sch_cfg.sys_tick_div = tkn_elem->value; + break; + + case SKL_TKN_U32_SCH_SYS_TICK_LL_SRC: + skl->cfg.sch_cfg.sys_tick_ll_src = tkn_elem->value; + break; + + case SKL_TKN_U32_SCH_SYS_TICK_CFG_LEN: + skl->cfg.sch_cfg.sys_tick_cfg_len = tkn_elem->value; + break; + + case SKL_TKN_U32_SCH_SYS_TICK_CFG: + skl->cfg.sch_cfg.sys_tick_cfg = tkn_elem->value; + break; + case SKL_TKN_U8_IN_PIN_TYPE: case SKL_TKN_U8_OUT_PIN_TYPE: case SKL_TKN_U8_IN_QUEUE_COUNT: @@ -5141,6 +5169,22 @@ static int skl_tplg_create_pipe_widget_list(struct snd_soc_component *component) return 0; } +void skl_tplg_fw_cfg_set(struct skl *skl) +{ + /* Set DMA buffer configuration */ + if (skl->cfg.dmacfg.size) + skl_ipc_set_fw_cfg(&skl->skl_sst->ipc, SKL_INSTANCE_ID, + SKL_BASE_FW_MODULE_ID, (u32 *)(&skl->cfg.dmacfg)); + + /* set scheduler config if available */ + if (skl->cfg.sch_cfg.length) + skl_ipc_set_fw_cfg(&skl->skl_sst->ipc, SKL_INSTANCE_ID, + SKL_BASE_FW_MODULE_ID, (u32 *)(&skl->cfg.sch_cfg)); + + /* Set DMA clock controls */ + skl_dsp_set_dma_clk_controls(skl->skl_sst); +} + static void skl_tplg_set_pipe_type(struct skl *skl, struct skl_pipe *pipe) { struct skl_pipe_module *w_module; diff --git a/sound/soc/intel/skylake/skl-topology.h b/sound/soc/intel/skylake/skl-topology.h index cc23a18fb2b4..cd1827e20832 100644 --- a/sound/soc/intel/skylake/skl-topology.h +++ b/sound/soc/intel/skylake/skl-topology.h @@ -636,5 +636,6 @@ int skl_create_notify_kctl_list(struct skl_sst *skl_sst, void skl_delete_notify_kctl_list(struct skl_sst *skl_sst); struct snd_kcontrol *skl_get_notify_kcontrol(struct skl_sst *skl, struct snd_card *card, u32 notify_id); +void skl_tplg_fw_cfg_set(struct skl *skl); #endif diff --git a/sound/soc/intel/skylake/skl.h b/sound/soc/intel/skylake/skl.h index c8a2c27d971d..b75dc47331f6 100644 --- a/sound/soc/intel/skylake/skl.h +++ b/sound/soc/intel/skylake/skl.h @@ -82,6 +82,16 @@ struct skl_dma_buff_cfg { struct skl_dma_config dma_cfg[SKL_MAX_DMA_CFG]; } __packed; +struct skl_sch_config { + u32 type; + u32 length; + u32 sys_tick_mul; + u32 sys_tick_div; + u32 sys_tick_ll_src; + u32 sys_tick_cfg_len; + u32 sys_tick_cfg; +}; + struct skl_dmctrl_hdr { u32 vbus_id; u32 freq; @@ -102,6 +112,7 @@ struct skl_dmactrl_config { struct skl_fw_config { struct skl_dma_buff_cfg dmacfg; + struct skl_sch_config sch_cfg; struct skl_dmactrl_config dmactrl_cfg; struct skl_astate_config *astate_cfg; }; From cfd6ec115e9d270ca71a25966e6debad2fa0656b Mon Sep 17 00:00:00 2001 From: "Subhransu S. Prusty" Date: Thu, 30 Nov 2017 20:11:42 +0530 Subject: [PATCH 0819/1103] WA: Disable irq in rt274 Signed-off-by: Subhransu S. Prusty --- sound/soc/codecs/rt274.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sound/soc/codecs/rt274.c b/sound/soc/codecs/rt274.c index c5c3ee4d3182..6b03996c410e 100644 --- a/sound/soc/codecs/rt274.c +++ b/sound/soc/codecs/rt274.c @@ -1179,6 +1179,7 @@ static int rt274_i2c_probe(struct i2c_client *i2c, regmap_write(rt274->regmap, RT274_UNSOLICITED_HP_OUT, 0x81); regmap_write(rt274->regmap, RT274_UNSOLICITED_MIC, 0x82); +#if 0 if (rt274->i2c->irq) { ret = request_threaded_irq(rt274->i2c->irq, NULL, rt274_irq, IRQF_TRIGGER_HIGH | IRQF_ONESHOT, "rt274", rt274); @@ -1188,6 +1189,7 @@ static int rt274_i2c_probe(struct i2c_client *i2c, return ret; } } +#endif ret = devm_snd_soc_register_component(&i2c->dev, &soc_component_dev_rt274, From 6d369541cbd0e9f4c96bf6ad2dec3a0c3df42e9f Mon Sep 17 00:00:00 2001 From: Guneshwor Singh Date: Wed, 20 Sep 2017 14:29:38 +0530 Subject: [PATCH 0820/1103] ASoC: Intel: Skylake: Replace modulus operator with div_u64_rem. Linking errors are observed when compiled for 32 bit architecture as shown below: sound/soc/intel/skylake/skl.o: In function `skl_stream_update': skl.c:(.text+0x213): undefined reference to `__umoddi3' sound/soc/intel/skylake/skl-probe.o: In function `skl_probe_compr_copy': skl-probe.c:(.text+0x3f7): undefined reference to `__umoddi3' skl-probe.c:(.text+0x459): undefined reference to `__umoddi3' The error for __umoddi3 is observed due to usage of modulus operator on u64 variable which is unsupported for i386. To fix this, use div_u64_rem instead. Change-Id: I3ebba0c28ac50215dc4efff7356a31ba4db930f6 Signed-off-by: Guneshwor Singh Signed-off-by: Dronamraju, Santosh Pavan KumarX Reviewed-on: Reviewed-by: audio_build Reviewed-by: Prusty, Subhransu S Reviewed-by: Shaik, Kareem M Reviewed-by: Tewani, Pradeep D Reviewed-by: Kale, Sanyog R Tested-by: Sm, Bhadur A --- sound/soc/intel/skylake/skl-probe.c | 11 ++++++----- sound/soc/intel/skylake/skl.c | 6 ++++-- 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/sound/soc/intel/skylake/skl-probe.c b/sound/soc/intel/skylake/skl-probe.c index 4a22c75c552b..2d9d0ea6c907 100644 --- a/sound/soc/intel/skylake/skl-probe.c +++ b/sound/soc/intel/skylake/skl-probe.c @@ -335,7 +335,8 @@ int skl_probe_compr_tstamp(struct snd_compr_stream *stream, int skl_probe_compr_copy(struct snd_compr_stream *stream, char __user *buf, size_t count, struct snd_soc_dai *dai) { - int offset = 0, availcount = 0, retval = 0, copy; + int availcount = 0, retval = 0, copy; + unsigned int offset = 0; void *dstn; /* * If userspace happens to issue a copy with count > ring buffer size, @@ -345,8 +346,8 @@ int skl_probe_compr_copy(struct snd_compr_stream *stream, char __user *buf, count = stream->runtime->buffer_size; if (stream->direction == SND_COMPRESS_CAPTURE) { - offset = stream->runtime->total_bytes_transferred % - stream->runtime->buffer_size; + div_u64_rem(stream->runtime->total_bytes_transferred, + stream->runtime->buffer_size, &offset); dstn = stream->runtime->dma_area + offset; availcount = (stream->runtime->buffer_size - offset); if (count > availcount) { @@ -366,8 +367,8 @@ int skl_probe_compr_copy(struct snd_compr_stream *stream, char __user *buf, } else if (stream->direction == SND_COMPRESS_PLAYBACK) { - offset = stream->runtime->total_bytes_available % - stream->runtime->buffer_size; + div_u64_rem(stream->runtime->total_bytes_available, + stream->runtime->buffer_size, &offset); dstn = stream->runtime->dma_area + offset; if (count < stream->runtime->buffer_size - offset) diff --git a/sound/soc/intel/skylake/skl.c b/sound/soc/intel/skylake/skl.c index ba614c96254b..691496cecaa0 100644 --- a/sound/soc/intel/skylake/skl.c +++ b/sound/soc/intel/skylake/skl.c @@ -189,9 +189,11 @@ void skl_update_d0i3c(struct device *dev, bool enable) static void skl_get_total_bytes_transferred(struct hdac_stream *hstr) { - int pos, prev_pos, no_of_bytes; + int pos, no_of_bytes; + unsigned int prev_pos; - prev_pos = hstr->curr_pos % hstr->stream->runtime->buffer_size; + div_u64_rem(hstr->curr_pos, + hstr->stream->runtime->buffer_size, &prev_pos); pos = snd_hdac_stream_get_pos_posbuf(hstr); if (pos < prev_pos) From 91c76939fecbcd689fd8e4eb17edb9beedaecbb7 Mon Sep 17 00:00:00 2001 From: Mohit Sinha Date: Thu, 16 Nov 2017 21:35:07 +0530 Subject: [PATCH 0821/1103] ASoC: Intel: Skylake: Poll on ADSPCS.CSTALL bit to confirm stall state change. ADSPCS.CSTALL bit should be set/cleared in order to stall/ un-stall the DSP core. Since the stall bit(state) transition need not happen instantaneously, the driver has to poll on the bit until the correct state transition occurs or the polling times out indicating a failure. Such a polling was not done in the current code. As a result an operation on the core failed because it was attempted without waiting for the core to be un-stalled. Change-Id: I268c89e031acf8a9bb5220ba621e4bf2d99ae745 Signed-off-by: Mohit Sinha Reviewed-on: Reviewed-by: B, Jayachandran Reviewed-by: audio_build Reviewed-by: Prusty, Subhransu S Reviewed-by: Kesapragada, Pardha Saradhi Reviewed-by: Pawse, GuruprasadX Reviewed-by: Kp, Jeeja Reviewed-by: Kale, Sanyog R Tested-by: Sm, Bhadur A --- sound/soc/intel/skylake/skl-sst-dsp.c | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/sound/soc/intel/skylake/skl-sst-dsp.c b/sound/soc/intel/skylake/skl-sst-dsp.c index 71e31ad0bb3f..4b16e0002cd4 100644 --- a/sound/soc/intel/skylake/skl-sst-dsp.c +++ b/sound/soc/intel/skylake/skl-sst-dsp.c @@ -160,11 +160,24 @@ is_skl_dsp_core_enable(struct sst_dsp *ctx, unsigned int core_mask) static int skl_dsp_reset_core(struct sst_dsp *ctx, unsigned int core_mask) { + int ret; + /* stall core */ sst_dsp_shim_update_bits_unlocked(ctx, SKL_ADSP_REG_ADSPCS, SKL_ADSPCS_CSTALL_MASK(core_mask), SKL_ADSPCS_CSTALL_MASK(core_mask)); + /* poll with timeout to check if operation successful */ + ret = sst_dsp_register_poll(ctx, + SKL_ADSP_REG_ADSPCS, + SKL_ADSPCS_CSTALL_MASK(core_mask), + SKL_ADSPCS_CSTALL_MASK(core_mask), + SKL_DSP_PU_TO, + "Stall Core"); + + if (ret < 0) + return ret; + /* set reset state */ return skl_dsp_core_set_reset_state(ctx, core_mask); } @@ -183,6 +196,16 @@ int skl_dsp_start_core(struct sst_dsp *ctx, unsigned int core_mask) sst_dsp_shim_update_bits_unlocked(ctx, SKL_ADSP_REG_ADSPCS, SKL_ADSPCS_CSTALL_MASK(core_mask), 0); + /* poll with timeout to check if operation successful */ + ret = sst_dsp_register_poll(ctx, + SKL_ADSP_REG_ADSPCS, + SKL_ADSPCS_CSTALL_MASK(core_mask), + 0, + SKL_DSP_PU_TO, + "Unstall Core"); + if (ret < 0) + return ret; + if (!is_skl_dsp_core_enable(ctx, core_mask)) { skl_dsp_reset_core(ctx, core_mask); dev_err(ctx->dev, "DSP start core failed: core_mask %x\n", From b3be98ab52e75504a86c8cd4c0f998fa7fb38f65 Mon Sep 17 00:00:00 2001 From: Mohit Sinha Date: Thu, 16 Nov 2017 23:01:21 +0530 Subject: [PATCH 0822/1103] ASoC: Intel: Skylake: Add delay during DSP core start 1msec delay is required in power-up sequence of DSP core. Power-up sequence for DSP core is as follows: 1) power up core 2) unreset core 3) unstall core 4) send IPC ~1msec wait is required between unstall core and before sending IPC to ensure proper propagation of signals. Change-Id: Ie7c4b47d4b4ce6f869178a5fd9940f6af84b13d2 Signed-off-by: Mohit Sinha Reviewed-on: Reviewed-by: audio_build Reviewed-by: B, Jayachandran Reviewed-by: Prusty, Subhransu S Reviewed-by: Pawse, GuruprasadX Reviewed-by: Tewani, Pradeep D Reviewed-by: Kp, Jeeja Reviewed-by: Kale, Sanyog R Tested-by: Sm, Bhadur A --- sound/soc/intel/skylake/skl-sst-dsp.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/sound/soc/intel/skylake/skl-sst-dsp.c b/sound/soc/intel/skylake/skl-sst-dsp.c index 4b16e0002cd4..b34f7b73e24e 100644 --- a/sound/soc/intel/skylake/skl-sst-dsp.c +++ b/sound/soc/intel/skylake/skl-sst-dsp.c @@ -17,6 +17,7 @@ */ #include +#include #include "../common/sst-dsp.h" #include "../common/sst-ipc.h" #include "../common/sst-dsp-priv.h" @@ -206,6 +207,9 @@ int skl_dsp_start_core(struct sst_dsp *ctx, unsigned int core_mask) if (ret < 0) return ret; + /* delay to ensure proper signal propagation after unreset/unstall */ + usleep_range(1000, 1500); + if (!is_skl_dsp_core_enable(ctx, core_mask)) { skl_dsp_reset_core(ctx, core_mask); dev_err(ctx->dev, "DSP start core failed: core_mask %x\n", From 44fd1f9b49ce63771e6d867937b1837632e871c4 Mon Sep 17 00:00:00 2001 From: Pardha Saradhi K Date: Tue, 18 Jul 2017 22:56:08 +0530 Subject: [PATCH 0823/1103] ALSA: hda: Make sure DMA is stopped by reading back the RUN bit As per HW recommendation, after clearing the RUN bit, software must read a 0 from the RUN bit, before modifying related control registers or re-starting the DMA engine. Change-Id: Ied7c3534dde57fab7dc3eea809811933ced555b8 Signed-off-by: Pardha Saradhi K Reviewed-on: Reviewed-by: Prakash, Divya1 Reviewed-by: Koul, Vinod Tested-by: Sm, Bhadur A --- sound/hda/hdac_stream.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/sound/hda/hdac_stream.c b/sound/hda/hdac_stream.c index 3c0c7c353566..2ddda76ba91d 100644 --- a/sound/hda/hdac_stream.c +++ b/sound/hda/hdac_stream.c @@ -71,8 +71,24 @@ EXPORT_SYMBOL_GPL(snd_hdac_stream_start); */ void snd_hdac_stream_clear(struct hdac_stream *azx_dev) { + int timeout; + unsigned char val; + snd_hdac_stream_updateb(azx_dev, SD_CTL, SD_CTL_DMA_START | SD_INT_MASK, 0); + + timeout = 300; + do { + udelay(3); + val = snd_hdac_stream_readb(azx_dev, SD_CTL) & + SD_CTL_DMA_START; + if (!val) + break; + } while (--timeout); + + if (!timeout) + dev_err(azx_dev->bus->dev, "unable to stop the stream\n"); + snd_hdac_stream_writeb(azx_dev, SD_STS, SD_INT_MASK); /* to be sure */ azx_dev->running = false; } From 9887db36be2d88c0eb11835427adc0d9a494fd26 Mon Sep 17 00:00:00 2001 From: Pardha Saradhi K Date: Fri, 21 Jul 2017 02:08:12 +0530 Subject: [PATCH 0824/1103] ALSA: hda: Make sure DMA is started by reading back the RUN bit As per HW recommendation, after setting the RUN bit, software must read a 1 from the RUN bit, before modifying related control registers/re-starting the DMA engine. Change-Id: I5e81791a3d210a579f61d2345c4d3a29cd4c7b08 Signed-off-by: Pardha Saradhi K Reviewed-on: Reviewed-by: Prakash, Divya1 Reviewed-by: Koul, Vinod Tested-by: Sm, Bhadur A --- sound/hda/hdac_stream.c | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/sound/hda/hdac_stream.c b/sound/hda/hdac_stream.c index 2ddda76ba91d..00954efaa483 100644 --- a/sound/hda/hdac_stream.c +++ b/sound/hda/hdac_stream.c @@ -49,6 +49,8 @@ EXPORT_SYMBOL_GPL(snd_hdac_stream_init); void snd_hdac_stream_start(struct hdac_stream *azx_dev, bool fresh_start) { struct hdac_bus *bus = azx_dev->bus; + int timeout; + unsigned char val; trace_snd_hdac_stream_start(bus, azx_dev); @@ -61,6 +63,21 @@ void snd_hdac_stream_start(struct hdac_stream *azx_dev, bool fresh_start) /* set DMA start and interrupt mask */ snd_hdac_stream_updateb(azx_dev, SD_CTL, 0, SD_CTL_DMA_START | SD_INT_MASK); + + timeout = 300; + do { + udelay(3); + val = snd_hdac_stream_readb(azx_dev, SD_CTL) & SD_CTL_DMA_START; + if (val) + break; + } while(--timeout); + + if (!timeout) { + dev_err(azx_dev->bus->dev, "unable to start the stream\n"); + azx_dev->running = false; + return; + } + azx_dev->running = true; } EXPORT_SYMBOL_GPL(snd_hdac_stream_start); From 34653a95b2c78331fd7048b274b3967e009e9a16 Mon Sep 17 00:00:00 2001 From: Pardha Saradhi K Date: Wed, 19 Jul 2017 00:41:43 +0530 Subject: [PATCH 0825/1103] ALSA: hda: Log HDA Hardware related errors Detect the timeout while modifying HDA DMA related Registers for stream reset and print them to console for user information Change-Id: Id2441bee1fd6083cd11c4725af2d36cb90e9bf92 Signed-off-by: Pardha Saradhi K Reviewed-on: Reviewed-by: Prakash, Divya1 Reviewed-by: Koul, Vinod Tested-by: Sm, Bhadur A --- sound/hda/hdac_stream.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/sound/hda/hdac_stream.c b/sound/hda/hdac_stream.c index 00954efaa483..0fdb6dd7f316 100644 --- a/sound/hda/hdac_stream.c +++ b/sound/hda/hdac_stream.c @@ -147,6 +147,10 @@ void snd_hdac_stream_reset(struct hdac_stream *azx_dev) if (val) break; } while (--timeout); + + if (!timeout) + dev_err(azx_dev->bus->dev, "timeout on stream reset entry\n"); + val &= ~SD_CTL_STREAM_RESET; snd_hdac_stream_writeb(azx_dev, SD_CTL, val); udelay(3); @@ -160,6 +164,9 @@ void snd_hdac_stream_reset(struct hdac_stream *azx_dev) break; } while (--timeout); + if (!timeout) + dev_err(azx_dev->bus->dev, "timeout on stream reset exit\n"); + /* reset first position - may not be synced with hw at this time */ if (azx_dev->posbuf) *azx_dev->posbuf = 0; From 292e529ee47c130fb3628b83c6276e6577cc1a3a Mon Sep 17 00:00:00 2001 From: Pardha Saradhi K Date: Tue, 17 Oct 2017 23:01:00 +0530 Subject: [PATCH 0826/1103] ALSA: hda: check if stream is stopped in snd_hdac_stream_clear Check if the DMA Channel is already stopped. There is no need to stop it again if stopped. Change-Id: Ia4632675638726b65dc3b9e2aca4f6c36773f757 Signed-off-by: Pardha Saradhi K Reviewed-on: Reviewed-by: Koul, Vinod Tested-by: Sm, Bhadur A --- sound/hda/hdac_stream.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/sound/hda/hdac_stream.c b/sound/hda/hdac_stream.c index 0fdb6dd7f316..67d672192606 100644 --- a/sound/hda/hdac_stream.c +++ b/sound/hda/hdac_stream.c @@ -91,6 +91,11 @@ void snd_hdac_stream_clear(struct hdac_stream *azx_dev) int timeout; unsigned char val; + /* check if the DMA is already stopped */ + val = snd_hdac_stream_readb(azx_dev, SD_CTL) & SD_CTL_DMA_START; + if (!val) + return; + snd_hdac_stream_updateb(azx_dev, SD_CTL, SD_CTL_DMA_START | SD_INT_MASK, 0); From 499da24ea07f2f1fad1882bc4cdefcf078927034 Mon Sep 17 00:00:00 2001 From: "Shaik, Kareem M" Date: Sat, 18 Nov 2017 03:34:50 +0530 Subject: [PATCH 0827/1103] ASoC: Intel: Skylake: Support multiple format configs A module can have two kinds of set params, as per topology requirements. For example, one pre-init and one post-init. But currently, there is support for just one type, as the format_config. This patch extends the format_configs to 4, so as to be able to support pre-init, post-init and post-bind type of set params, for the same module, simultaneously. Change-Id: I5cdf6a921db6e9ffcabda2ec601795564c8f53d2 Signed-off-by: Kareem Shaik Reviewed-on: Reviewed-by: Gogineni, GiribabuX Reviewed-by: Kale, Sanyog R Reviewed-by: Singh, Guneshwor O Reviewed-by: Nc, Shreyas Reviewed-by: Kesapragada, Pardha Saradhi Reviewed-by: Koul, Vinod Tested-by: Sm, Bhadur A --- include/uapi/sound/snd_sst_tokens.h | 6 +- sound/soc/intel/skylake/skl-messages.c | 68 ++++++++-------- sound/soc/intel/skylake/skl-topology.c | 108 ++++++++++++++++--------- sound/soc/intel/skylake/skl-topology.h | 4 +- 4 files changed, 113 insertions(+), 73 deletions(-) diff --git a/include/uapi/sound/snd_sst_tokens.h b/include/uapi/sound/snd_sst_tokens.h index ba4806d5ef17..c04dd0418173 100644 --- a/include/uapi/sound/snd_sst_tokens.h +++ b/include/uapi/sound/snd_sst_tokens.h @@ -281,6 +281,8 @@ * Config contain capture on which SSP to * active the FW * + * %SKL_TKN_U32_FMT_CFG_IDX: Format config index + * * module_id and loadable flags dont have tokens as these values will be * read from the DSP FW manifest * @@ -392,7 +394,9 @@ enum SKL_TKNS { SKL_TKN_U32_SCH_SYS_TICK_LL_SRC, SKL_TKN_U32_SCH_SYS_TICK_CFG_LEN, SKL_TKN_U32_SCH_SYS_TICK_CFG, - SKL_TKN_MAX = SKL_TKN_U32_SCH_SYS_TICK_CFG, + + SKL_TKN_U32_FMT_CFG_IDX, + SKL_TKN_MAX = SKL_TKN_U32_FMT_CFG_IDX, }; /* diff --git a/sound/soc/intel/skylake/skl-messages.c b/sound/soc/intel/skylake/skl-messages.c index b97255b5aae0..c6d9cb386de3 100644 --- a/sound/soc/intel/skylake/skl-messages.c +++ b/sound/soc/intel/skylake/skl-messages.c @@ -466,12 +466,12 @@ static int cnl_sdw_bra_pipe_cfg_pb(struct skl_sst *ctx, host_cpr_cfg->m_type = SKL_MODULE_TYPE_COPIER; host_cpr_cfg->dev_type = SKL_DEVICE_HDAHOST; host_cpr_cfg->hw_conn_type = SKL_CONN_SOURCE; - host_cpr_cfg->formats_config.caps_size = 0; + host_cpr_cfg->formats_config[SKL_PARAM_INIT].caps_size = 0; host_cpr_cfg->module->resources[0].dma_buffer_size = 2; host_cpr_cfg->converter = 0; host_cpr_cfg->vbus_id = 0; host_cpr_cfg->sdw_agg_enable = 0; - host_cpr_cfg->formats_config.caps_size = 0; + host_cpr_cfg->formats_config[SKL_PARAM_INIT].caps_size = 0; in_fmt->channels = 1; in_fmt->s_freq = 96000; @@ -572,22 +572,23 @@ static int cnl_sdw_bra_pipe_cfg_pb(struct skl_sst *ctx, link_cpr_cfg->m_out_pin[0].is_dynamic = true; link_cpr_cfg->m_out_pin[0].pin_state = SKL_PIN_UNBIND; - link_cpr_cfg->formats_config.caps_size = (sizeof(u32) * 4); - link_cpr_cfg->formats_config.caps = kzalloc((sizeof(u32) * 4), - GFP_KERNEL); - if (!link_cpr_cfg->formats_config.caps) { + link_cpr_cfg->formats_config[SKL_PARAM_INIT].caps_size = + (sizeof(u32) * 4); + link_cpr_cfg->formats_config[SKL_PARAM_INIT].caps = + kzalloc((sizeof(u32) * 4), GFP_KERNEL); + if (!link_cpr_cfg->formats_config[SKL_PARAM_INIT].caps) { ret = -ENOMEM; goto error; } - link_cpr_cfg->formats_config.caps[0] = 0x0; - link_cpr_cfg->formats_config.caps[1] = 0x1; + link_cpr_cfg->formats_config[SKL_PARAM_INIT].caps[0] = 0x0; + link_cpr_cfg->formats_config[SKL_PARAM_INIT].caps[1] = 0x1; #if IS_ENABLED(CONFIG_SND_SOC_INTEL_CNL_FPGA) - link_cpr_cfg->formats_config.caps[2] = 0x1003; + link_cpr_cfg->formats_config[SKL_PARAM_INIT].caps[2] = 0x1003; #else - link_cpr_cfg->formats_config.caps[2] = 0x1013; + link_cpr_cfg->formats_config[SKL_PARAM_INIT].caps[2] = 0x1013; #endif - link_cpr_cfg->formats_config.caps[3] = 0x0; + link_cpr_cfg->formats_config[SKL_PARAM_INIT].caps[3] = 0x0; /* Init PB CPR1 module */ ret = skl_init_module(ctx, host_cpr_cfg); @@ -610,7 +611,7 @@ static int cnl_sdw_bra_pipe_cfg_pb(struct skl_sst *ctx, kfree(host_cpr_cfg->m_out_pin); kfree(link_cpr_cfg->m_in_pin); kfree(link_cpr_cfg->m_out_pin); - kfree(link_cpr_cfg->formats_config.caps); + kfree(link_cpr_cfg->formats_config[SKL_PARAM_INIT].caps); kfree(host_cpr_cfg); kfree(link_cpr_cfg); kfree(host_cpr_mod); @@ -729,27 +730,28 @@ static int cnl_sdw_bra_pipe_cfg_cp(struct skl_sst *ctx, #endif link_cpr_cfg->hw_conn_type = SKL_CONN_SINK; - link_cpr_cfg->formats_config.caps_size = 0; + link_cpr_cfg->formats_config[SKL_PARAM_INIT].caps_size = 0; link_cpr_cfg->module->resources[0].dma_buffer_size = 2; link_cpr_cfg->converter = 0; link_cpr_cfg->vbus_id = 0; link_cpr_cfg->sdw_agg_enable = 0; - link_cpr_cfg->formats_config.caps_size = (sizeof(u32) * 4); - link_cpr_cfg->formats_config.caps = kzalloc((sizeof(u32) * 4), - GFP_KERNEL); - if (!link_cpr_cfg->formats_config.caps) { + link_cpr_cfg->formats_config[SKL_PARAM_INIT].caps_size = + (sizeof(u32) * 4); + link_cpr_cfg->formats_config[SKL_PARAM_INIT].caps = + kzalloc((sizeof(u32) * 4), GFP_KERNEL); + if (!link_cpr_cfg->formats_config[SKL_PARAM_INIT].caps) { ret = -ENOMEM; goto error; } - link_cpr_cfg->formats_config.caps[0] = 0x0; - link_cpr_cfg->formats_config.caps[1] = 0x1; + link_cpr_cfg->formats_config[SKL_PARAM_INIT].caps[0] = 0x0; + link_cpr_cfg->formats_config[SKL_PARAM_INIT].caps[1] = 0x1; #if IS_ENABLED(CONFIG_SND_SOC_INTEL_CNL_FPGA) - link_cpr_cfg->formats_config.caps[2] = 0x1104; + link_cpr_cfg->formats_config[SKL_PARAM_INIT].caps[2] = 0x1104; #else - link_cpr_cfg->formats_config.caps[2] = 0x1114; + link_cpr_cfg->formats_config[SKL_PARAM_INIT].caps[2] = 0x1114; #endif - link_cpr_cfg->formats_config.caps[3] = 0x1; + link_cpr_cfg->formats_config[SKL_PARAM_INIT].caps[3] = 0x1; in_fmt->channels = 6; in_fmt->s_freq = 48000; @@ -813,7 +815,7 @@ static int cnl_sdw_bra_pipe_cfg_cp(struct skl_sst *ctx, host_cpr_cfg->dev_type = SKL_DEVICE_HDAHOST; host_cpr_cfg->hw_conn_type = SKL_CONN_SINK; link_cpr_params.host_dma_id = (bra_data->cp_stream_tag - 1); - host_cpr_cfg->formats_config.caps_size = 0; + host_cpr_cfg->formats_config[SKL_PARAM_INIT].caps_size = 0; host_cpr_cfg->m_in_pin = kcalloc(host_cpr_cfg->module->max_input_pins, sizeof(*host_cpr_cfg->m_in_pin), GFP_KERNEL); @@ -864,7 +866,7 @@ static int cnl_sdw_bra_pipe_cfg_cp(struct skl_sst *ctx, error: /* Free up all memory allocated */ - kfree(link_cpr_cfg->formats_config.caps); + kfree(link_cpr_cfg->formats_config[SKL_PARAM_INIT].caps); kfree(link_cpr_cfg->m_in_pin); kfree(link_cpr_cfg->m_out_pin); kfree(host_cpr_cfg->m_in_pin); @@ -1427,15 +1429,15 @@ static void skl_set_base_module_format(struct skl_sst *ctx, static void skl_copy_copier_caps(struct skl_module_cfg *mconfig, struct skl_cpr_cfg *cpr_mconfig) { - if (mconfig->formats_config.caps_size == 0) + if (mconfig->formats_config[SKL_PARAM_INIT].caps_size == 0) return; memcpy(cpr_mconfig->gtw_cfg.config_data, - mconfig->formats_config.caps, - mconfig->formats_config.caps_size); + mconfig->formats_config[SKL_PARAM_INIT].caps, + mconfig->formats_config[SKL_PARAM_INIT].caps_size); cpr_mconfig->gtw_cfg.config_length = - (mconfig->formats_config.caps_size) / 4; + (mconfig->formats_config[SKL_PARAM_INIT].caps_size) / 4; } #define SKL_NON_GATEWAY_CPR_NODE_ID 0xFFFFFFFF @@ -1848,12 +1850,12 @@ static void skl_set_algo_format(struct skl_sst *ctx, skl_set_base_module_format(ctx, mconfig, base_cfg); - if (mconfig->formats_config.caps_size == 0) + if (mconfig->formats_config[SKL_PARAM_INIT].caps_size == 0) return; memcpy(algo_mcfg->params, - mconfig->formats_config.caps, - mconfig->formats_config.caps_size); + mconfig->formats_config[SKL_PARAM_INIT].caps, + mconfig->formats_config[SKL_PARAM_INIT].caps_size); } @@ -1885,7 +1887,7 @@ static u16 skl_get_module_param_size(struct skl_sst *ctx, switch (mconfig->m_type) { case SKL_MODULE_TYPE_COPIER: param_size = sizeof(struct skl_cpr_cfg); - param_size += mconfig->formats_config.caps_size; + param_size += mconfig->formats_config[SKL_PARAM_INIT].caps_size; return param_size; case SKL_MODULE_TYPE_PROBE: @@ -1900,7 +1902,7 @@ static u16 skl_get_module_param_size(struct skl_sst *ctx, case SKL_MODULE_TYPE_ALGO: param_size = sizeof(struct skl_base_cfg); - param_size += mconfig->formats_config.caps_size; + param_size += mconfig->formats_config[SKL_PARAM_INIT].caps_size; return param_size; case SKL_MODULE_TYPE_BASE_OUTFMT: diff --git a/sound/soc/intel/skylake/skl-topology.c b/sound/soc/intel/skylake/skl-topology.c index 22c1795d4624..8026abf99ae8 100644 --- a/sound/soc/intel/skylake/skl-topology.c +++ b/sound/soc/intel/skylake/skl-topology.c @@ -664,7 +664,7 @@ static int skl_tplg_update_be_blob(struct snd_soc_dapm_widget *w, struct skl_module_iface *m_iface = &m_cfg->module->formats[fmt_idx]; /* check if we already have blob */ - if (m_cfg->formats_config.caps_size > 0) + if (m_cfg->formats_config[SKL_PARAM_INIT].caps_size > 0) return 0; dev_dbg(ctx->dev, "Applying default cfg blob\n"); @@ -700,8 +700,8 @@ static int skl_tplg_update_be_blob(struct snd_soc_dapm_widget *w, cfg = skl_get_ep_blob(skl, m_cfg->vbus_id, link_type, s_fmt, ch, s_freq, dir, dev_type); if (cfg) { - m_cfg->formats_config.caps_size = cfg->size; - m_cfg->formats_config.caps = (u32 *) &cfg->caps; + m_cfg->formats_config[SKL_PARAM_INIT].caps_size = cfg->size; + m_cfg->formats_config[SKL_PARAM_INIT].caps = (u32 *) &cfg->caps; } else { dev_err(ctx->dev, "Blob NULL for id %x type %d dirn %d\n", m_cfg->vbus_id, link_type, dir); @@ -942,9 +942,10 @@ int skl_tplg_set_module_params(struct snd_soc_dapm_widget *w, struct skl_algo_data *bc; struct skl_specific_cfg *sp_cfg; - if (mconfig->formats_config.caps_size > 0 && - mconfig->formats_config.set_params == SKL_PARAM_SET) { - sp_cfg = &mconfig->formats_config; + if (mconfig->formats_config[SKL_PARAM_SET].caps_size > 0 && + mconfig->formats_config[SKL_PARAM_SET].set_params == + SKL_PARAM_SET) { + sp_cfg = &mconfig->formats_config[SKL_PARAM_SET]; ret = skl_set_module_params(ctx, sp_cfg->caps, sp_cfg->caps_size, sp_cfg->param_id, mconfig); @@ -994,8 +995,10 @@ static int skl_tplg_set_module_init_data(struct snd_soc_dapm_widget *w) if (bc->set_params != SKL_PARAM_INIT) continue; - mconfig->formats_config.caps = (u32 *)bc->params; - mconfig->formats_config.caps_size = bc->size; + mconfig->formats_config[SKL_PARAM_INIT].caps = + (u32 *)bc->params; + mconfig->formats_config[SKL_PARAM_INIT].caps_size = + bc->size; break; } @@ -1432,9 +1435,10 @@ static int skl_tplg_set_module_bind_params(struct snd_soc_dapm_widget *w, return 0; } - if (mconfig->formats_config.caps_size > 0 && - mconfig->formats_config.set_params == SKL_PARAM_BIND) { - sp_cfg = &mconfig->formats_config; + if (mconfig->formats_config[SKL_PARAM_BIND].caps_size > 0 && + mconfig->formats_config[SKL_PARAM_BIND].set_params == + SKL_PARAM_BIND) { + sp_cfg = &mconfig->formats_config[SKL_PARAM_BIND]; ret = skl_set_module_params(ctx, sp_cfg->caps, sp_cfg->caps_size, sp_cfg->param_id, mconfig); @@ -2390,7 +2394,8 @@ static int skl_tplg_mic_control_get(struct snd_kcontrol *kcontrol, static int skl_fill_mic_sel_params(struct skl_module_cfg *mconfig, struct skl_mic_sel_config *mic_cfg, struct device *dev) { - struct skl_specific_cfg *sp_cfg = &mconfig->formats_config; + struct skl_specific_cfg *sp_cfg = + &mconfig->formats_config[SKL_PARAM_INIT]; sp_cfg->caps_size = sizeof(struct skl_mic_sel_config); sp_cfg->set_params = SKL_PARAM_SET; @@ -2951,8 +2956,8 @@ static int skl_tplg_be_fill_pipe_params(struct snd_soc_dai *dai, GFP_KERNEL); if (!sdw_cfg) return -ENOMEM; - mconfig->formats_config.caps_size = (((sizeof(u32)) * - (mconfig->sdw_agg.num_masters) * 2) + mconfig->formats_config[SKL_PARAM_INIT].caps_size = + (((sizeof(u32)) * (mconfig->sdw_agg.num_masters) * 2) + (2 * (sizeof(u32)))); sdw_cfg->count = mconfig->sdw_agg.num_masters; @@ -2967,7 +2972,7 @@ static int skl_tplg_be_fill_pipe_params(struct snd_soc_dai *dai, } } sdw_cfg->count = mconfig->sdw_agg.num_masters; - mconfig->formats_config.caps = (u32 *) sdw_cfg; + mconfig->formats_config[SKL_PARAM_INIT].caps = (u32 *) sdw_cfg; return 0; } @@ -2978,8 +2983,9 @@ static int skl_tplg_be_fill_pipe_params(struct snd_soc_dai *dai, dev_type); if (cfg) { - mconfig->formats_config.caps_size = cfg->size; - mconfig->formats_config.caps = (u32 *) &cfg->caps; + mconfig->formats_config[SKL_PARAM_INIT].caps_size = cfg->size; + mconfig->formats_config[SKL_PARAM_INIT].caps = + (u32 *) &cfg->caps; } else { dev_err(dai->dev, "Blob NULL for id %x type %d dirn %d\n", mconfig->vbus_id, link_type, @@ -3892,19 +3898,26 @@ static int skl_tplg_get_token(struct device *dev, break; + case SKL_TKN_U32_FMT_CFG_IDX: + if (tkn_elem->value > SKL_MAX_PARAMS_TYPES) + return -EINVAL; + + mconfig->fmt_cfg_idx = tkn_elem->value; + break; + case SKL_TKN_U32_CAPS_SIZE: - mconfig->formats_config.caps_size = + mconfig->formats_config[mconfig->fmt_cfg_idx].caps_size = tkn_elem->value; break; case SKL_TKN_U32_CAPS_SET_PARAMS: - mconfig->formats_config.set_params = + mconfig->formats_config[mconfig->fmt_cfg_idx].set_params = tkn_elem->value; break; case SKL_TKN_U32_CAPS_PARAMS_ID: - mconfig->formats_config.param_id = + mconfig->formats_config[mconfig->fmt_cfg_idx].param_id = tkn_elem->value; break; @@ -4135,6 +4148,7 @@ static int skl_tplg_get_pvt_data_v4(struct snd_soc_tplg_dapm_widget *tplg_w, struct skl_dfw_v4_module *dfw = (struct skl_dfw_v4_module *)tplg_w->priv.data; int ret; + int idx = mconfig->fmt_cfg_idx; dev_dbg(dev, "Parsing Skylake v4 widget topology data\n"); @@ -4168,7 +4182,7 @@ static int skl_tplg_get_pvt_data_v4(struct snd_soc_tplg_dapm_widget *tplg_w, mconfig->dev_type = dfw->dev_type; mconfig->hw_conn_type = dfw->hw_conn_type; mconfig->time_slot = dfw->time_slot; - mconfig->formats_config.caps_size = dfw->caps.caps_size; + mconfig->formats_config[idx].caps_size = dfw->caps.caps_size; mconfig->m_in_pin = devm_kcalloc(dev, MAX_IN_QUEUE, sizeof(*mconfig->m_in_pin), @@ -4189,21 +4203,40 @@ static int skl_tplg_get_pvt_data_v4(struct snd_soc_tplg_dapm_widget *tplg_w, dfw->is_dynamic_out_pin, mconfig->module->max_output_pins); - if (mconfig->formats_config.caps_size) { - mconfig->formats_config.set_params = dfw->caps.set_params; - mconfig->formats_config.param_id = dfw->caps.param_id; - mconfig->formats_config.caps = - devm_kzalloc(dev, mconfig->formats_config.caps_size, + if (mconfig->formats_config[idx].caps_size) { + mconfig->formats_config[idx].set_params = dfw->caps.set_params; + mconfig->formats_config[idx].param_id = dfw->caps.param_id; + mconfig->formats_config[idx].caps = + devm_kzalloc(dev, mconfig->formats_config[idx].caps_size, GFP_KERNEL); - if (!mconfig->formats_config.caps) + if (!mconfig->formats_config[idx].caps) return -ENOMEM; - memcpy(mconfig->formats_config.caps, dfw->caps.caps, + memcpy(mconfig->formats_config[idx].caps, dfw->caps.caps, dfw->caps.caps_size); } return 0; } +static int skl_tplg_get_caps_data(struct device *dev, char *data, + struct skl_module_cfg *mconfig) +{ + int idx; + + idx = mconfig->fmt_cfg_idx; + if (mconfig->formats_config[idx].caps_size > 0) { + mconfig->formats_config[idx].caps = (u32 *)devm_kzalloc(dev, + mconfig->formats_config[idx].caps_size, + GFP_KERNEL); + if (mconfig->formats_config[idx].caps == NULL) + return -ENOMEM; + memcpy(mconfig->formats_config[idx].caps, data, + mconfig->formats_config[idx].caps_size); + } + + return mconfig->formats_config[idx].caps_size; +} + /* * Parse the private data for the token and corresponding value. * The private data can have multiple data blocks. So, a data block @@ -4264,18 +4297,14 @@ static int skl_tplg_get_pvt_data(struct snd_soc_tplg_dapm_widget *tplg_w, if (block_type == SKL_TYPE_TUPLE) { ret = skl_tplg_get_tokens(dev, data, skl, mconfig, block_size); - - if (ret < 0) - return ret; - - --num_blocks; } else { - if (mconfig->formats_config.caps_size > 0) - memcpy(mconfig->formats_config.caps, data, - mconfig->formats_config.caps_size); - --num_blocks; - ret = mconfig->formats_config.caps_size; + ret = skl_tplg_get_caps_data(dev, data, mconfig); } + + if (ret < 0) + return ret; + + --num_blocks; off += ret; } @@ -4370,6 +4399,9 @@ static int skl_tplg_widget_load(struct snd_soc_component *cmpnt, int index, */ mconfig->id.module_id = -1; + /* To provide backward compatibility, set default as SKL_PARAM_INIT */ + mconfig->fmt_cfg_idx = SKL_PARAM_INIT; + /* Parse private data for tuples */ ret = skl_tplg_get_pvt_data(tplg_w, skl, bus->dev, mconfig); if (ret < 0) diff --git a/sound/soc/intel/skylake/skl-topology.h b/sound/soc/intel/skylake/skl-topology.h index cd1827e20832..97b9614f57af 100644 --- a/sound/soc/intel/skylake/skl-topology.h +++ b/sound/soc/intel/skylake/skl-topology.h @@ -56,6 +56,7 @@ #define SKL_MAX_MODULE_FORMATS 64 #define SKL_MAX_MODULE_RESOURCES 32 #define MAX_NUM_CHANNELS 8 +#define SKL_MAX_PARAMS_TYPES 4 enum skl_channel_index { SKL_CHANNEL_LEFT = 0, @@ -433,6 +434,7 @@ struct skl_module_cfg { struct skl_module *module; int res_idx; int fmt_idx; + int fmt_cfg_idx; u8 domain; bool homogenous_inputs; bool homogenous_outputs; @@ -469,7 +471,7 @@ struct skl_module_cfg { enum skl_hw_conn_type hw_conn_type; enum skl_module_state m_state; struct skl_pipe *pipe; - struct skl_specific_cfg formats_config; + struct skl_specific_cfg formats_config[SKL_MAX_PARAMS_TYPES]; struct skl_pipe_mcfg mod_cfg[SKL_MAX_MODULES_IN_PIPE]; struct skl_gain_data *gain_data; }; From 1f744cdf451a8471bf6850dcdabe2aec50a02f5e Mon Sep 17 00:00:00 2001 From: Guneshwor Singh Date: Wed, 20 Sep 2017 14:26:09 +0530 Subject: [PATCH 0828/1103] SoundWire: Fix CRC8 dependency CRC8 should be selected when SDW is selected. So use 'select' instead of 'depends' in Kconfig. Change-Id: Ia5f7c07750574cee1f6308bf884bd3b5441aedb3 Signed-off-by: Guneshwor Singh Signed-off-by: Dronamraju, Santosh Pavan KumarX Reviewed-on: Reviewed-by: Prabhu, PuneethX Reviewed-by: Kale, Sanyog R Reviewed-by: Pawse, GuruprasadX Reviewed-by: Koul, Vinod Tested-by: Sm, Bhadur A --- drivers/sdw/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/sdw/Kconfig b/drivers/sdw/Kconfig index 660188bd2c02..7e5a57f1f6d2 100644 --- a/drivers/sdw/Kconfig +++ b/drivers/sdw/Kconfig @@ -1,6 +1,6 @@ menuconfig SDW tristate "SoundWire bus support" - depends on CRC8 + select CRC8 depends on X86 help SoundWire interface is typically used for transporting data From 697706ac03fa3d62bbb3f63383434dd5e8e1aec9 Mon Sep 17 00:00:00 2001 From: Sanyog Kale Date: Mon, 4 Dec 2017 15:05:59 +0530 Subject: [PATCH 0829/1103] soundwire: Change programming sequence for BRA Currently, the register programming (PDI, ALH, DP_CTRL) is performed before FW pipeline creation. The ALH ownership is set to host as part of first SoundWire gateway configuration. Due to this changes in FW, the first instance of BRA fails. This patch changes the sequence and performs register programming after FW pipeline creation. Change-Id: Ifbff05f9033e88541406b42d7d5a5370e1bccdcb Signed-off-by: Sanyog Kale Reviewed-on: Reviewed-by: Prabhu, PuneethX Reviewed-by: Pawse, GuruprasadX Reviewed-by: Singh, Guneshwor O Reviewed-by: Koul, Vinod Tested-by: Sm, Bhadur A --- drivers/sdw/sdw_cnl.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/sdw/sdw_cnl.c b/drivers/sdw/sdw_cnl.c index 9f8e77c20699..95eb7a8fab2b 100644 --- a/drivers/sdw/sdw_cnl.c +++ b/drivers/sdw/sdw_cnl.c @@ -1900,9 +1900,6 @@ static int cnl_sdw_xfer_bulk(struct sdw_master *mstr, /* Fill master number in bra info data structure */ info.mstr_num = mstr->nr; - /* PDI Configuration (ON) */ - cnl_sdw_bra_pdi_config(mstr, true); - /* Prepare TX buffer */ ret = cnl_sdw_bra_data_ops(mstr, block, &info); if (ret < 0) { @@ -1917,6 +1914,9 @@ static int cnl_sdw_xfer_bulk(struct sdw_master *mstr, goto out; } + /* PDI Configuration (ON) */ + cnl_sdw_bra_pdi_config(mstr, true); + /* Trigger START host DMA and pipeline */ ret = ops->bra_platform_xfer(data->bra_data->drv_data, true, &info); if (ret < 0) { From 0c02280b73f67899085a8c8299f405a70933efaa Mon Sep 17 00:00:00 2001 From: "Gogineni, GiribabuX" Date: Tue, 2 Jan 2018 23:53:39 +0530 Subject: [PATCH 0830/1103] [WORKAROUND] ASoC: tdf8532: Add delay while reading a packet from I2C While doing the continuous play and stop, the codec may not be ready for I2C reading after successive writes. This triggers BE failure, because I2C reading value is incorrect. Fix this by adding 10ms delay to ensure the smooth I2C read and write. Change-Id: If918e263bc799fecc2c807229f5b4b165e011fa6 Signed-off-by: Gogineni, GiribabuX Reviewed-on: Reviewed-by: Shaik, Kareem M Reviewed-by: Sinha, Mohit Reviewed-by: Nc, Shreyas Reviewed-by: Periyasamy, SriramX Reviewed-by: Kale, Sanyog R Tested-by: Sm, Bhadur A --- sound/soc/codecs/tdf8532.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sound/soc/codecs/tdf8532.c b/sound/soc/codecs/tdf8532.c index e723ffebed0f..a5e2a028338c 100644 --- a/sound/soc/codecs/tdf8532.c +++ b/sound/soc/codecs/tdf8532.c @@ -90,6 +90,7 @@ static uint8_t tdf8532_read_wait_ack(struct tdf8532_priv *dev_data, unsigned long timeout_point = jiffies + timeout; int ret; + usleep_range(10000,20000); do { ret = i2c_master_recv(dev_data->i2c, ack_repl, HEADER_SIZE); if (ret < 0) From 47dd1174ca266810484707c442837c43ae67f434 Mon Sep 17 00:00:00 2001 From: Sanyog Kale Date: Wed, 16 Aug 2017 12:26:31 +0530 Subject: [PATCH 0831/1103] SoundWire: Perform clock exit by setting clock stop clear As per HW sequence, clock stop clear bit in mcp_control register should be set and wait for bit to be cleared in order to exit Master from clock stop. Hence adding the support. Change-Id: I3491c74a9969e4ce112ed6afc5eb366e1cc6737a Signed-off-by: Sanyog Kale Signed-off-by: Paul, Subhankar --- drivers/sdw/sdw_cnl.c | 55 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 54 insertions(+), 1 deletion(-) diff --git a/drivers/sdw/sdw_cnl.c b/drivers/sdw/sdw_cnl.c index 95eb7a8fab2b..c754edbe6564 100644 --- a/drivers/sdw/sdw_cnl.c +++ b/drivers/sdw/sdw_cnl.c @@ -485,7 +485,7 @@ static int sdw_init(struct cnl_sdw *sdw, bool is_first_init) int mcp_config, mcp_control, sync_reg, mcp_clockctrl; volatile int sync_update = 0; int timeout = 10; /* Try 10 times before timing out */ - int ret = 0; + int ret = 0, mask; /* Power up the link controller */ ret = sdw_power_up_link(sdw); @@ -498,6 +498,56 @@ static int sdw_init(struct cnl_sdw *sdw, bool is_first_init) /* Switch the ownership to Master IP from glue logic */ sdw_switch_to_mip(sdw); + /* write to MCP Control register to enable block wakeup */ + mcp_control = cnl_sdw_reg_readl(data->sdw_regs, SDW_CNL_MCP_CONTROL); + mask = (MCP_CONTROL_BLOCKWAKEUP_MASK << + MCP_CONTROL_BLOCKWAKEUP_SHIFT); + mcp_control &= ~mask; + cnl_sdw_reg_writel(data->sdw_regs, SDW_CNL_MCP_CONTROL, mcp_control); + do { + mcp_control = cnl_sdw_reg_readl(data->sdw_regs, + SDW_CNL_MCP_CONTROL); + if (!(mcp_control & mask)) + break; + + timeout--; + /* Wait 20ms before each time */ + msleep(20); + } while (timeout != 0); + + /* Write the MCP Control register to exit from clock stop */ + mcp_control = cnl_sdw_reg_readl(data->sdw_regs, SDW_CNL_MCP_CONTROL); + mask = (MCP_CONTROL_CLOCKSTOPCLEAR_MASK << + MCP_CONTROL_CLOCKSTOPCLEAR_SHIFT); + mcp_control |= mask; + cnl_sdw_reg_writel(data->sdw_regs, SDW_CNL_MCP_CONTROL, mcp_control); + + /* Reset timeout */ + timeout = 10; + + /* Wait for clock stop exit bit to be self cleared */ + do { + mcp_control = cnl_sdw_reg_readl(data->sdw_regs, + SDW_CNL_MCP_CONTROL); + if (!(mcp_control & mask)) + break; + timeout--; + /* Wait 20ms before each time */ + msleep(20); + } while (timeout != 0); + + /* Read once again to confirm */ + mcp_control = cnl_sdw_reg_readl(data->sdw_regs, SDW_CNL_MCP_CONTROL); + if (!(mcp_control & mask)) { + dev_dbg(&sdw->mstr->dev, "SDW ctrl %d exit clock stop success\n", + data->inst_id); + } else { + dev_err(&sdw->mstr->dev, + "Failed exit from clock stop SDW ctrl %d\n", + data->inst_id); + return -EIO; + } + /* Set SyncPRD period */ sync_reg = cnl_sdw_reg_readl(data->sdw_shim, SDW_CNL_SYNC); sync_reg |= (SDW_CNL_DEFAULT_SYNC_PERIOD << CNL_SYNC_SYNCPRD_SHIFT); @@ -506,6 +556,9 @@ static int sdw_init(struct cnl_sdw *sdw, bool is_first_init) sync_reg |= (0x1 << CNL_SYNC_SYNCCPU_SHIFT); cnl_sdw_reg_writel(data->sdw_shim, SDW_CNL_SYNC, sync_reg); + /* Reset timeout */ + timeout = 10; + do { sync_update = cnl_sdw_reg_readl(data->sdw_shim, SDW_CNL_SYNC); if ((sync_update & CNL_SYNC_SYNCCPU_MASK) == 0) From 6f97a574a3016f51b2b39125f6112ea3170c3481 Mon Sep 17 00:00:00 2001 From: "R, Dharageswari" Date: Mon, 25 Dec 2017 07:03:56 +0530 Subject: [PATCH 0832/1103] ASoC: Intel: Skylake: Add API to reset private instance id of modules When the Audio DSP becomes unresponsive, DSP will be reset and firmware will be downloaded again. As a consequence of this driver resources need to be reinitialized with its default values. With this patch an API is added to reset private/dynamic instance id of the modules. This will be used during recovery of the Audio DSP Change-Id: Id4e7c4fca80d3dd97b823853cfd476d3d82dd116 Signed-off-by: R, Dharageswari Signed-off-by: Pradeep Tewani Reviewed-on: Reviewed-by: Periyasamy, SriramX Reviewed-by: Prakash, Divya1 Reviewed-by: Kale, Sanyog R Reviewed-by: Prusty, Subhransu S Tested-by: Madiwalar, MadiwalappaX --- sound/soc/intel/skylake/skl-sst-dsp.h | 1 + sound/soc/intel/skylake/skl-sst-utils.c | 14 ++++++++++++++ 2 files changed, 15 insertions(+) diff --git a/sound/soc/intel/skylake/skl-sst-dsp.h b/sound/soc/intel/skylake/skl-sst-dsp.h index 207bf3fe8ffd..807f5b7db0ae 100644 --- a/sound/soc/intel/skylake/skl-sst-dsp.h +++ b/sound/soc/intel/skylake/skl-sst-dsp.h @@ -307,6 +307,7 @@ int snd_skl_parse_uuids(struct sst_dsp *ctx, const struct firmware *fw, int skl_get_module_id(struct skl_sst *ctx, uuid_le *uuid_mod); int skl_get_pvt_id(struct skl_sst *ctx, uuid_le *uuid_mod, int instance_id); int skl_put_pvt_id(struct skl_sst *ctx, uuid_le *uuid_mod, int *pvt_id); +void skl_reset_instance_id(struct skl_sst *ctx); int skl_get_pvt_instance_id_map(struct skl_sst *ctx, int module_id, int instance_id); void skl_freeup_uuid_list(struct skl_sst *ctx); diff --git a/sound/soc/intel/skylake/skl-sst-utils.c b/sound/soc/intel/skylake/skl-sst-utils.c index f20b842af4e4..8f4056ebf670 100644 --- a/sound/soc/intel/skylake/skl-sst-utils.c +++ b/sound/soc/intel/skylake/skl-sst-utils.c @@ -292,7 +292,21 @@ int skl_put_pvt_id(struct skl_sst *ctx, uuid_le *uuid_mod, int *pvt_id) } EXPORT_SYMBOL_GPL(skl_put_pvt_id); +void skl_reset_instance_id(struct skl_sst *ctx) +{ + struct uuid_module *module; + int size, i; + + list_for_each_entry(module, &ctx->uuid_list, list) { + for (i = 0; i < MAX_INSTANCE_BUFF; i++) + module->pvt_id[i] = 0; + + size = sizeof(int) * module->max_instance; + memset(module->instance_id, -1, size); + } +} +EXPORT_SYMBOL_GPL(skl_reset_instance_id); int skl_dsp_crash_dump_read(struct skl_sst *ctx, int stack_size) { From 608d99857839d870a30143d0e61f34842ce7862d Mon Sep 17 00:00:00 2001 From: "R, Dharageswari" Date: Mon, 25 Dec 2017 07:06:05 +0530 Subject: [PATCH 0833/1103] ASoC: Intel: Skylake: Add an API to reset the usage count of the DSP cores Add an interface to reset the usage count of ADSP cores. This is needed in situations where the DSP needs to restart afresh, after a crash Change-Id: I56fcd89a9055b99671f0d4229f19aa0fd3340d48 Signed-off-by: R, Dharageswari Signed-off-by: Pradeep Tewani Reviewed-on: Reviewed-by: Periyasamy, SriramX Reviewed-by: Kale, Sanyog R Reviewed-by: Prakash, Divya1 Reviewed-by: Kp, Jeeja Reviewed-by: Prusty, Subhransu S Tested-by: Madiwalar, MadiwalappaX --- sound/soc/intel/skylake/skl-sst-dsp.c | 12 ++++++++++++ sound/soc/intel/skylake/skl-sst-dsp.h | 1 + 2 files changed, 13 insertions(+) diff --git a/sound/soc/intel/skylake/skl-sst-dsp.c b/sound/soc/intel/skylake/skl-sst-dsp.c index b34f7b73e24e..0b1cc44c901d 100644 --- a/sound/soc/intel/skylake/skl-sst-dsp.c +++ b/sound/soc/intel/skylake/skl-sst-dsp.c @@ -54,6 +54,18 @@ void skl_dsp_init_core_state(struct sst_dsp *ctx) } } +void skl_dsp_reset_core_state(struct sst_dsp *ctx) +{ + struct skl_sst *skl = ctx->thread_context; + int i; + + for (i = 0; i < skl->cores.count; i++) { + skl->cores.state[i] = SKL_DSP_RESET; + skl->cores.usage_count[i] = 0; + } +} +EXPORT_SYMBOL_GPL(skl_dsp_reset_core_state); + /* Get the mask for all enabled cores */ unsigned int skl_dsp_get_enabled_cores(struct sst_dsp *ctx) { diff --git a/sound/soc/intel/skylake/skl-sst-dsp.h b/sound/soc/intel/skylake/skl-sst-dsp.h index 807f5b7db0ae..ef9bf4a4a1b7 100644 --- a/sound/soc/intel/skylake/skl-sst-dsp.h +++ b/sound/soc/intel/skylake/skl-sst-dsp.h @@ -273,6 +273,7 @@ bool is_skl_dsp_running(struct sst_dsp *ctx); unsigned int skl_dsp_get_enabled_cores(struct sst_dsp *ctx); void skl_dsp_init_core_state(struct sst_dsp *ctx); +void skl_dsp_reset_core_state(struct sst_dsp *ctx); int skl_dsp_enable_core(struct sst_dsp *ctx, unsigned int core_mask); int skl_dsp_disable_core(struct sst_dsp *ctx, unsigned int core_mask); int skl_dsp_core_power_up(struct sst_dsp *ctx, unsigned int core_mask); From 990a5b1c7b63aa6bb7c2a151d7f04aa7daf35b34 Mon Sep 17 00:00:00 2001 From: "R, Dharageswari" Date: Mon, 25 Dec 2017 07:07:11 +0530 Subject: [PATCH 0834/1103] ASoC: Intel: Skylake: Fix the is_dsp_running() to return core0 state DSP state is mainly dependent on the core0 state. This patches fixes the is_dsp_running callback to return dsp state based on core0 state. Change-Id: I60297162b6512b2092886f4ae8cbcd286bafdf09 Signed-off-by: R, Dharageswari Signed-off-by: Pradeep Tewani Reviewed-on: Reviewed-by: Kale, Sanyog R Reviewed-by: Periyasamy, SriramX Reviewed-by: Prakash, Divya1 Reviewed-by: Kp, Jeeja Reviewed-by: Prusty, Subhransu S Tested-by: Madiwalar, MadiwalappaX --- sound/soc/intel/common/sst-dsp-priv.h | 1 - sound/soc/intel/skylake/skl-sst-dsp.c | 11 +++-------- 2 files changed, 3 insertions(+), 9 deletions(-) diff --git a/sound/soc/intel/common/sst-dsp-priv.h b/sound/soc/intel/common/sst-dsp-priv.h index 8e80c6f177f8..196bb7d7ebf0 100644 --- a/sound/soc/intel/common/sst-dsp-priv.h +++ b/sound/soc/intel/common/sst-dsp-priv.h @@ -355,7 +355,6 @@ struct sst_dsp { /* To allocate CL dma buffers */ struct skl_dsp_loader_ops dsp_ops; struct skl_dsp_fw_ops fw_ops; - int sst_state; struct skl_cl_dev cl_dev; u32 intr_status; const struct firmware *fw; diff --git a/sound/soc/intel/skylake/skl-sst-dsp.c b/sound/soc/intel/skylake/skl-sst-dsp.c index 0b1cc44c901d..485c8b8c38a1 100644 --- a/sound/soc/intel/skylake/skl-sst-dsp.c +++ b/sound/soc/intel/skylake/skl-sst-dsp.c @@ -28,13 +28,6 @@ #define SKL_DSP_PD_TO 50 #define SKL_DSP_RESET_TO 50 -void skl_dsp_set_state_locked(struct sst_dsp *ctx, int state) -{ - mutex_lock(&ctx->mutex); - ctx->sst_state = state; - mutex_unlock(&ctx->mutex); -} - /* * Initialize core power state and usage count. To be called after * successful first boot. Hence core 0 will be running and other cores @@ -504,6 +497,8 @@ EXPORT_SYMBOL_GPL(skl_dsp_free); bool is_skl_dsp_running(struct sst_dsp *ctx) { - return (ctx->sst_state == SKL_DSP_RUNNING); + struct skl_sst *skl_sst = ctx->thread_context; + + return (skl_sst->cores.state[SKL_DSP_CORE0_ID] != SKL_DSP_RESET); } EXPORT_SYMBOL_GPL(is_skl_dsp_running); From 1c7e60a750df7b5d87c21cf38a16e5e6ceadd5db Mon Sep 17 00:00:00 2001 From: "R, Dharageswari" Date: Mon, 25 Dec 2017 07:17:03 +0530 Subject: [PATCH 0835/1103] ASoC: Intel: Skylake: Avoid sending IPCs during the crash recovery Stream is put to suspend as a part of firmware crash recovery. This in turn invokes the PCM trigger suspend as well as DAPM pre/post PMD events, where driver sends IPCs to DSP. However, DSP can't respond to IPCs during recovery. This patch ensures no IPCs are sent during the recovery process. Change-Id: If06d57563d3e24898ce86e33b3427ca24aa12804 Signed-off-by: R, Dharageswari Signed-off-by: Pradeep Tewani Reviewed-on: Reviewed-by: Prusty, Subhransu S Tested-by: Madiwalar, MadiwalappaX --- sound/soc/intel/common/sst-ipc.c | 4 ++++ sound/soc/intel/skylake/skl-topology.c | 6 ++++++ 2 files changed, 10 insertions(+) diff --git a/sound/soc/intel/common/sst-ipc.c b/sound/soc/intel/common/sst-ipc.c index f4b09503518e..03896bde44d3 100644 --- a/sound/soc/intel/common/sst-ipc.c +++ b/sound/soc/intel/common/sst-ipc.c @@ -98,6 +98,10 @@ static int ipc_tx_message(struct sst_generic_ipc *ipc, u64 header, struct ipc_message *msg; unsigned long flags; + if (ipc->dsp->is_recovery) { + dev_dbg(ipc->dev, "Recovery in progress..\n"); + return 0; + } spin_lock_irqsave(&ipc->dsp->spinlock, flags); msg = msg_get_empty(ipc); diff --git a/sound/soc/intel/skylake/skl-topology.c b/sound/soc/intel/skylake/skl-topology.c index 8026abf99ae8..c09f03c9da4d 100644 --- a/sound/soc/intel/skylake/skl-topology.c +++ b/sound/soc/intel/skylake/skl-topology.c @@ -1972,9 +1972,13 @@ static int skl_tplg_mixer_event(struct snd_soc_dapm_widget *w, return skl_tplg_mixer_dapm_post_pmu_event(w, skl); case SND_SOC_DAPM_PRE_PMD: + if (!(is_skl_dsp_running(skl->skl_sst->dsp))) + return 0; return skl_tplg_mixer_dapm_pre_pmd_event(w, skl); case SND_SOC_DAPM_POST_PMD: + if (!(is_skl_dsp_running(skl->skl_sst->dsp))) + return 0; return skl_tplg_mixer_dapm_post_pmd_event(w, skl); } @@ -1999,6 +2003,8 @@ static int skl_tplg_pga_event(struct snd_soc_dapm_widget *w, return skl_tplg_pga_dapm_pre_pmu_event(w, skl); case SND_SOC_DAPM_POST_PMD: + if (!(is_skl_dsp_running(skl->skl_sst->dsp))) + return 0; return skl_tplg_pga_dapm_post_pmd_event(w, skl); } From 1ac851b88aad19dc200b4394db319c777b4e5d61 Mon Sep 17 00:00:00 2001 From: "R, Dharageswari" Date: Mon, 25 Dec 2017 04:48:41 +0530 Subject: [PATCH 0836/1103] [REVERTME] ASoC: Intel: CNL: Fix for the firmware redownload failure on ICL There is a bug in hda uncaching mechanism due to which seecond time firmware download was failing in the ICL platform. In order to get around this, flush_cache_range() is used as WA to maintain the integrity of firmware. This patch will be reverted, once the proper fix is available Change-Id: I2efdfe82dc302e8a1b623c905da2ea08084d8074 Signed-off-by: R, Dharageswari Signed-off-by: Pradeep Tewani Reviewed-on: Reviewed-by: Kale, Sanyog R Reviewed-by: Prakash, Divya1 Reviewed-by: Prusty, Subhransu S Tested-by: Madiwalar, MadiwalappaX --- sound/soc/intel/skylake/cnl-sst.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sound/soc/intel/skylake/cnl-sst.c b/sound/soc/intel/skylake/cnl-sst.c index 813cd73545e4..ea7bec1ad073 100644 --- a/sound/soc/intel/skylake/cnl-sst.c +++ b/sound/soc/intel/skylake/cnl-sst.c @@ -30,6 +30,7 @@ #include #include #include +#include #include "../common/sst-dsp.h" #include "../common/sst-dsp-priv.h" @@ -137,6 +138,7 @@ static int cnl_prepare_fw(struct sst_dsp *ctx, const void *fwdata, u32 fwsize) ctx->dsp_ops.stream_tag = stream_tag; memcpy(ctx->dmab.area, fwdata, fwsize); + clflush_cache_range(ctx->dmab.area, fwsize); /* purge FW request */ sst_dsp_shim_write(ctx, CNL_ADSP_REG_HIPCIDR, CNL_ADSP_REG_HIPCIDR_BUSY | (CNL_IPC_PURGE | From 61d9c0f16ce8eec6a2a95b882723a3e1469a768b Mon Sep 17 00:00:00 2001 From: "R, Dharageswari" Date: Mon, 25 Dec 2017 03:03:35 +0530 Subject: [PATCH 0837/1103] ASoC: Intel: SKL: Implement the timer to trigger firmware crash recovery This patch implements timer to trigger firmware crash recovery when there is no period elapsed for the period boundary of a stream. Change-Id: I500d0307f5367e30bf28b37f356e2f63d648c5ff Signed-off-by: R, Dharageswari Signed-off-by: Pradeep Tewani Reviewed-on: Reviewed-by: Kale, Sanyog R Reviewed-by: Prakash, Divya1 Reviewed-by: Prusty, Subhransu S Tested-by: Madiwalar, MadiwalappaX --- sound/soc/intel/skylake/skl-messages.c | 33 +++++++++++++++++ sound/soc/intel/skylake/skl-pcm.c | 51 +++++++++++++++++++++++++- sound/soc/intel/skylake/skl.c | 28 +++++++++++++- sound/soc/intel/skylake/skl.h | 16 ++++++++ 4 files changed, 125 insertions(+), 3 deletions(-) diff --git a/sound/soc/intel/skylake/skl-messages.c b/sound/soc/intel/skylake/skl-messages.c index c6d9cb386de3..5fb1093cdfa2 100644 --- a/sound/soc/intel/skylake/skl-messages.c +++ b/sound/soc/intel/skylake/skl-messages.c @@ -22,6 +22,7 @@ #include #include #include +#include #include #include "skl-sst-dsp.h" #include "cnl-sst-dsp.h" @@ -363,6 +364,38 @@ static int cnl_sdw_bra_pipe_trigger(struct skl_sst *ctx, bool enable, return ret; } +void skl_trigger_recovery(struct work_struct *work) +{ + struct skl_monitor *monitor_dsp = container_of(work, + struct skl_monitor, mwork); + struct skl *skl = container_of(monitor_dsp, + struct skl, monitor_dsp); + const struct skl_dsp_ops *ops; + + ops = skl_get_dsp_ops(skl->pci->device); + + if (ops->do_recovery) + ops->do_recovery(skl); + return; + +} + +void skl_timer_cb(struct timer_list *t) +{ + struct skl *skl = from_timer(skl, t, monitor_dsp.timer); + struct skl_sst *ctx = skl->skl_sst; + const struct skl_dsp_ops *ops; + + ops = skl_get_dsp_ops(skl->pci->device); + ctx->cores.state[SKL_DSP_CORE0_ID] = SKL_DSP_RESET; + + if (ops->do_recovery) { + schedule_work(&skl->monitor_dsp.mwork); + del_timer(&skl->monitor_dsp.timer); + } + +} + static int cnl_sdw_bra_pipe_cfg_pb(struct skl_sst *ctx, unsigned int mstr_num) { diff --git a/sound/soc/intel/skylake/skl-pcm.c b/sound/soc/intel/skylake/skl-pcm.c index cd036f5fe515..bee7c5ea7fd1 100644 --- a/sound/soc/intel/skylake/skl-pcm.c +++ b/sound/soc/intel/skylake/skl-pcm.c @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include "skl.h" @@ -519,12 +520,19 @@ static int skl_pcm_trigger(struct snd_pcm_substream *substream, int cmd, struct snd_soc_dai *dai) { struct skl *skl = get_skl_ctx(dai->dev); + struct skl_monitor *monitor = &skl->monitor_dsp; struct skl_sst *ctx = skl->skl_sst; struct skl_module_cfg *mconfig; struct hdac_bus *bus = get_bus_ctx(substream); struct hdac_ext_stream *stream = get_hdac_ext_stream(substream); - struct snd_pcm_runtime *runtime = substream->runtime; struct snd_soc_dapm_widget *w; + struct snd_pcm_runtime *runtime = substream->runtime; + struct hdac_stream *azx_dev; +#if !IS_ENABLED(CONFIG_SND_SOC_INTEL_CNL_FPGA) + u32 interval; + int i; +#endif + bool is_running = false; int ret; mconfig = skl_tplg_fe_get_cpr_module(dai, substream->stream); @@ -554,7 +562,7 @@ static int skl_pcm_trigger(struct snd_pcm_substream *substream, int cmd, stream->lpib); snd_hdac_ext_stream_set_lpib(stream, stream->lpib); if (runtime->no_rewinds) - snd_hdac_ext_stream_set_spib(ebus, + snd_hdac_ext_stream_set_spib(bus, stream, stream->spib); } case SNDRV_PCM_TRIGGER_START: @@ -568,6 +576,25 @@ static int skl_pcm_trigger(struct snd_pcm_substream *substream, int cmd, ret = skl_decoupled_trigger(substream, cmd); if (ret < 0) return ret; + /* + * Period elapsed interrupts with multiple streams are not + * consistent on FPGA. However, it works without any issues on + * RVP. So, using the default max value for FPGA + */ +#if !IS_ENABLED(CONFIG_SND_SOC_INTEL_CNL_FPGA) + /* + * To be on the safer side, restricting the minimal interval to + * 10ms + */ + interval = SKL_MIN_TIME_INTERVAL + + ((2 * runtime->period_size * 1000) / + runtime->rate); + monitor->intervals[hdac_stream(stream)->index] = interval; + if (interval > monitor->interval) + monitor->interval = interval; +#else + monitor->interval = SKL_MAX_TIME_INTERVAL; +#endif return skl_run_pipe(ctx, mconfig->pipe); break; @@ -595,6 +622,26 @@ static int skl_pcm_trigger(struct snd_pcm_substream *substream, int cmd, hdac_stream(stream)); snd_hdac_ext_stream_decouple(bus, stream, false); } + + list_for_each_entry(azx_dev, &bus->stream_list, list) { + if (azx_dev->running) { + is_running = true; + break; + } + } + monitor->intervals[hdac_stream(stream)->index] = 0; + if (!is_running) + del_timer(&skl->monitor_dsp.timer); +#if !IS_ENABLED(CONFIG_SND_SOC_INTEL_CNL_FPGA) + else { + interval = SKL_MIN_TIME_INTERVAL; + for (i = 0; i < bus->num_streams; i++) { + if (monitor->intervals[i] > interval) + interval = monitor->intervals[i]; + } + monitor->interval = interval; + } +#endif break; default: diff --git a/sound/soc/intel/skylake/skl.c b/sound/soc/intel/skylake/skl.c index 691496cecaa0..7532c11ee1f0 100644 --- a/sound/soc/intel/skylake/skl.c +++ b/sound/soc/intel/skylake/skl.c @@ -21,6 +21,7 @@ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ +#include #include #include #include @@ -35,6 +36,8 @@ #include #include #include "skl.h" +#include "../common/sst-dsp.h" +#include "../common/sst-dsp-priv.h" #include "skl-sst-dsp.h" #include "skl-sst-ipc.h" #include "skl-topology.h" @@ -245,6 +248,7 @@ static void skl_stream_update(struct hdac_bus *bus, struct hdac_stream *hstr) static irqreturn_t skl_interrupt(int irq, void *dev_id) { struct hdac_bus *bus = dev_id; + struct skl *skl = bus_to_skl(ebus); u32 status; u32 mask, int_enable; int ret = IRQ_NONE; @@ -268,7 +272,7 @@ static irqreturn_t skl_interrupt(int irq, void *dev_id) snd_hdac_chip_writeb(bus, RIRBSTS, RIRB_INT_MASK); } - mask = (0x1 << ebus->num_streams) - 1; + mask = (0x1 << bus->num_streams) - 1; status = snd_hdac_chip_readl(bus, INTSTS); status &= mask; @@ -276,6 +280,8 @@ static irqreturn_t skl_interrupt(int irq, void *dev_id) /* Disable stream interrupts; Re-enable in bottom half */ int_enable = snd_hdac_chip_readl(bus, INTCTL); snd_hdac_chip_writel(bus, INTCTL, (int_enable & (~mask))); + mod_timer(&skl->monitor_dsp.timer, jiffies + + msecs_to_jiffies(skl->monitor_dsp.interval)); ret = IRQ_WAKE_THREAD; } else ret = IRQ_HANDLED; @@ -858,6 +864,22 @@ static void skl_probe_work(struct work_struct *work) err = snd_hdac_display_power(bus, false); } +static int skl_init_recovery(struct skl *skl) +{ + struct skl_monitor *monitor = &skl->monitor_dsp; + + INIT_WORK(&monitor->mwork, skl_trigger_recovery); + monitor->interval = SKL_MIN_TIME_INTERVAL; + + monitor->intervals = devm_kzalloc(&skl->pci->dev, + skl->ebus.num_streams * sizeof(u32), + GFP_KERNEL); + if (!monitor->intervals) + return -ENOMEM; + timer_setup(&monitor->timer, skl_timer_cb, 0); + return 0; +} + /* * constructor */ @@ -981,6 +1003,10 @@ static int skl_probe(struct pci_dev *pci, if (err < 0) goto out_free; + err = skl_init_recovery(skl); + if (err < 0) + return err; + skl->pci_id = pci->device; device_disable_async_suspend(bus->dev); diff --git a/sound/soc/intel/skylake/skl.h b/sound/soc/intel/skylake/skl.h index b75dc47331f6..01053c4cdf6e 100644 --- a/sound/soc/intel/skylake/skl.h +++ b/sound/soc/intel/skylake/skl.h @@ -23,6 +23,7 @@ #include #include +#include #include #include "skl-nhlt.h" #include "skl-ssp-clk.h" @@ -52,6 +53,9 @@ #define BXT_INSTANCE_ID 0 #define BXT_BASE_FW_MODULE_ID 0 +#define SKL_MAX_TIME_INTERVAL 1000 +#define SKL_MIN_TIME_INTERVAL 10 + struct skl_dsp_resource { u32 max_mcps; u32 max_mem; @@ -122,6 +126,14 @@ struct ep_group_cnt { int *vbus_id; }; +/* For crash recovery */ +struct skl_monitor { + struct work_struct mwork; + struct timer_list timer; + u32 interval; + u32 *intervals; +}; + struct skl { struct hdac_bus hbus; struct pci_dev *pci; @@ -137,6 +149,7 @@ struct skl { struct nhlt_acpi_table *nhlt; /* nhlt ptr */ struct skl_sst *skl_sst; /* sst skl ctx */ + struct skl_monitor monitor_dsp; struct skl_dsp_resource resource; struct list_head ppl_list; struct list_head bind_list; @@ -185,6 +198,7 @@ struct skl_dsp_ops { struct skl_sst **skl_sst, void *ptr); int (*init_fw)(struct device *dev, struct skl_sst *ctx); void (*cleanup)(struct device *dev, struct skl_sst *ctx); + void (*do_recovery)(struct skl *skl); }; int skl_platform_unregister(struct device *dev); @@ -218,6 +232,8 @@ struct skl_clk_parent_src *skl_get_parent_clk(u8 clk_id); int skl_dsp_set_dma_control(struct skl_sst *ctx, u32 *caps, u32 caps_size, u32 node_id); +void skl_timer_cb(struct timer_list *t); +void skl_trigger_recovery(struct work_struct *work); struct skl_module_cfg; #ifdef CONFIG_DEBUG_FS From c5885a937bcd525a40800d06b53daa8973ae6f05 Mon Sep 17 00:00:00 2001 From: "R, Dharageswari" Date: Thu, 28 Dec 2017 08:31:45 +0530 Subject: [PATCH 0838/1103] ASoC: Intel: Skylake: Implement recovery for cAVS platforms This patch implements the Audio dsp crash recovery for cAVS platforms for single and multiple streams. As a part of recovery, the firmware needs to be re-downloaded which requires the DMA channel 0. The patch does the necessary changes to reuse the DMA channel 0 for firmware download Change-Id: Icb09bca1525759d45a7739b42aa4404556087922 Signed-off-by: R, Dharageswari Signed-off-by: Pradeep Tewani Reviewed-on: Reviewed-by: Prusty, Subhransu S Tested-by: Madiwalar, MadiwalappaX --- sound/soc/intel/common/sst-dsp-priv.h | 1 + sound/soc/intel/skylake/bxt-sst.c | 3 ++ sound/soc/intel/skylake/cnl-sst.c | 7 ++++ sound/soc/intel/skylake/skl-messages.c | 54 ++++++++++++++++++++++++-- sound/soc/intel/skylake/skl-pcm.c | 2 + sound/soc/intel/skylake/skl-sst-dsp.h | 2 + sound/soc/intel/skylake/skl-topology.c | 1 + sound/soc/intel/skylake/skl-topology.h | 2 + sound/soc/intel/skylake/skl.c | 3 ++ 9 files changed, 71 insertions(+), 4 deletions(-) diff --git a/sound/soc/intel/common/sst-dsp-priv.h b/sound/soc/intel/common/sst-dsp-priv.h index 196bb7d7ebf0..b9935fdd0910 100644 --- a/sound/soc/intel/common/sst-dsp-priv.h +++ b/sound/soc/intel/common/sst-dsp-priv.h @@ -355,6 +355,7 @@ struct sst_dsp { /* To allocate CL dma buffers */ struct skl_dsp_loader_ops dsp_ops; struct skl_dsp_fw_ops fw_ops; + bool is_recovery; struct skl_cl_dev cl_dev; u32 intr_status; const struct firmware *fw; diff --git a/sound/soc/intel/skylake/bxt-sst.c b/sound/soc/intel/skylake/bxt-sst.c index dd5453daa562..2eb57d75f1b1 100644 --- a/sound/soc/intel/skylake/bxt-sst.c +++ b/sound/soc/intel/skylake/bxt-sst.c @@ -680,6 +680,9 @@ int bxt_sst_init_fw(struct device *dev, struct skl_sst *ctx) int ret; struct sst_dsp *sst = ctx->dsp; + if (sst->is_recovery) + skl_dsp_disable_core(sst, SKL_DSP_CORE0_MASK); + ret = sst->fw_ops.load_fw(sst); if (ret < 0) { dev_err(dev, "Load base fw failed: %x\n", ret); diff --git a/sound/soc/intel/skylake/cnl-sst.c b/sound/soc/intel/skylake/cnl-sst.c index ea7bec1ad073..df42b2157b21 100644 --- a/sound/soc/intel/skylake/cnl-sst.c +++ b/sound/soc/intel/skylake/cnl-sst.c @@ -795,6 +795,13 @@ int cnl_sst_init_fw(struct device *dev, struct skl_sst *ctx) struct sst_dsp *sst = ctx->dsp; int ret; + if (sst->is_recovery) { + cnl_dsp_disable_core(sst, SKL_DSP_CORE0_MASK); + ret = cnl_load_base_firmware(sst); + if (ret < 0) + return ret; + } + skl_dsp_init_core_state(sst); if (ctx->lib_count > 1) { diff --git a/sound/soc/intel/skylake/skl-messages.c b/sound/soc/intel/skylake/skl-messages.c index 5fb1093cdfa2..bc8d3afcf7d1 100644 --- a/sound/soc/intel/skylake/skl-messages.c +++ b/sound/soc/intel/skylake/skl-messages.c @@ -293,7 +293,8 @@ static const struct skl_dsp_ops dsp_ops[] = { .loader_ops = bxt_get_loader_ops, .init = bxt_sst_dsp_init, .init_fw = bxt_sst_init_fw, - .cleanup = bxt_sst_dsp_cleanup + .cleanup = bxt_sst_dsp_cleanup, + .do_recovery = skl_do_recovery }, { .id = 0x3198, @@ -301,7 +302,8 @@ static const struct skl_dsp_ops dsp_ops[] = { .loader_ops = bxt_get_loader_ops, .init = bxt_sst_dsp_init, .init_fw = bxt_sst_init_fw, - .cleanup = bxt_sst_dsp_cleanup + .cleanup = bxt_sst_dsp_cleanup, + .do_recovery = skl_do_recovery }, { .id = 0x9dc8, @@ -309,7 +311,8 @@ static const struct skl_dsp_ops dsp_ops[] = { .loader_ops = bxt_get_loader_ops, .init = cnl_sst_dsp_init, .init_fw = cnl_sst_init_fw, - .cleanup = cnl_sst_dsp_cleanup + .cleanup = cnl_sst_dsp_cleanup, + .do_recovery = skl_do_recovery }, { .id = 0x34c8, @@ -317,7 +320,8 @@ static const struct skl_dsp_ops dsp_ops[] = { .loader_ops = bxt_get_loader_ops, .init = cnl_sst_dsp_init, .init_fw = cnl_sst_init_fw, - .cleanup = cnl_sst_dsp_cleanup + .cleanup = cnl_sst_dsp_cleanup, + .do_recovery = skl_do_recovery }, }; @@ -364,6 +368,48 @@ static int cnl_sdw_bra_pipe_trigger(struct skl_sst *ctx, bool enable, return ret; } +void skl_do_recovery(struct skl *skl) +{ + struct snd_soc_component *soc_component = skl->component; + const struct skl_dsp_ops *ops; + struct snd_soc_card *card; + struct hdac_stream *azx_dev; + struct hdac_ext_bus *ebus = &skl->ebus; + struct hdac_bus *bus = ebus_to_hbus(ebus); + struct snd_pcm_substream *substream = NULL; + struct hdac_ext_stream *stream; + + skl->skl_sst->dsp->is_recovery = true; + skl_dsp_reset_core_state(skl->skl_sst->dsp); + card = soc_component->card; + snd_soc_suspend(card->dev); + skl_cleanup_resources(skl); + skl_reset_instance_id(skl->skl_sst); + + /* Free up DMA channel 0 for firmware re-download */ + list_for_each_entry(azx_dev, &bus->stream_list, list) { + if (azx_dev->stream_tag == 1 && + azx_dev->direction == SNDRV_PCM_STREAM_PLAYBACK) { + if (azx_dev->opened) { + substream = azx_dev->substream; + stream = stream_to_hdac_ext_stream(azx_dev); + snd_hdac_ext_stream_release(stream, + skl_get_host_stream_type(ebus)); + } + break; + } + } + ops = skl_get_dsp_ops(skl->pci->device); + if (ops->init_fw(soc_component->dev, skl->skl_sst) < 0) + dev_err(skl->skl_sst->dev, "Recovery failed\n"); + if (substream != NULL) { + stream = snd_hdac_ext_stream_assign(ebus, substream, + skl_get_host_stream_type(ebus)); + } + snd_soc_resume(card->dev); + skl->skl_sst->dsp->is_recovery = false; +} + void skl_trigger_recovery(struct work_struct *work) { struct skl_monitor *monitor_dsp = container_of(work, diff --git a/sound/soc/intel/skylake/skl-pcm.c b/sound/soc/intel/skylake/skl-pcm.c index bee7c5ea7fd1..6da6b492b70b 100644 --- a/sound/soc/intel/skylake/skl-pcm.c +++ b/sound/soc/intel/skylake/skl-pcm.c @@ -189,6 +189,7 @@ int skl_pcm_host_dma_prepare(struct device *dev, struct skl_pipe_params *params) params->host_dma_id + 1); if (!hstream) return -EINVAL; + hstream->substream = params->substream; stream = stream_to_hdac_ext_stream(hstream); snd_hdac_ext_stream_decouple(bus, stream, true); @@ -373,6 +374,7 @@ static int skl_pcm_hw_params(struct snd_pcm_substream *substream, p_params.host_dma_id = dma_id; p_params.stream = substream->stream; p_params.format = params_format(params); + p_params.substream = substream; if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) p_params.host_bps = dai->driver->playback.sig_bits; else diff --git a/sound/soc/intel/skylake/skl-sst-dsp.h b/sound/soc/intel/skylake/skl-sst-dsp.h index ef9bf4a4a1b7..dc793d503115 100644 --- a/sound/soc/intel/skylake/skl-sst-dsp.h +++ b/sound/soc/intel/skylake/skl-sst-dsp.h @@ -22,6 +22,7 @@ #include #include #include "skl-sst-cldma.h" +#include "skl.h" struct sst_dsp; struct skl_sst; @@ -270,6 +271,7 @@ struct sst_dsp *skl_dsp_ctx_init(struct device *dev, struct sst_dsp_device *sst_dev, int irq); int skl_dsp_acquire_irq(struct sst_dsp *sst); bool is_skl_dsp_running(struct sst_dsp *ctx); +void skl_do_recovery(struct skl *skl); unsigned int skl_dsp_get_enabled_cores(struct sst_dsp *ctx); void skl_dsp_init_core_state(struct sst_dsp *ctx); diff --git a/sound/soc/intel/skylake/skl-topology.c b/sound/soc/intel/skylake/skl-topology.c index c09f03c9da4d..b91c4ea88002 100644 --- a/sound/soc/intel/skylake/skl-topology.c +++ b/sound/soc/intel/skylake/skl-topology.c @@ -2517,6 +2517,7 @@ static void skl_tplg_fill_dma_id(struct skl_module_cfg *mcfg, pipe->p_params->s_freq = params->s_freq; pipe->p_params->stream = params->stream; pipe->p_params->format = params->format; + pipe->p_params->substream = params->substream; } else { memcpy(pipe->p_params, params, sizeof(*params)); diff --git a/sound/soc/intel/skylake/skl-topology.h b/sound/soc/intel/skylake/skl-topology.h index 97b9614f57af..e86b84e9868d 100644 --- a/sound/soc/intel/skylake/skl-topology.h +++ b/sound/soc/intel/skylake/skl-topology.h @@ -317,6 +317,7 @@ struct skl_pipe_params { int stream; unsigned int host_bps; unsigned int link_bps; + struct snd_pcm_substream *substream; }; struct skl_pipe_fmt { @@ -616,6 +617,7 @@ int skl_pcm_host_dma_prepare(struct device *dev, struct skl_pipe_params *params); int skl_pcm_link_dma_prepare(struct device *dev, struct skl_pipe_params *params); +enum hdac_ext_stream_type skl_get_host_stream_type(struct hdac_ext_bus *ebus); int skl_dai_load(struct snd_soc_component *cmp, int index, struct snd_soc_dai_driver *dai_drv, diff --git a/sound/soc/intel/skylake/skl.c b/sound/soc/intel/skylake/skl.c index 7532c11ee1f0..29e6c5a22f51 100644 --- a/sound/soc/intel/skylake/skl.c +++ b/sound/soc/intel/skylake/skl.c @@ -389,6 +389,9 @@ static int skl_suspend(struct device *dev) struct skl *skl = bus_to_skl(bus); int ret = 0; + if (skl->skl_sst->dsp->is_recovery) + return -EBUSY; + /* * Do not suspend if streams which are marked ignore suspend are * running, we need to save the state for these and continue From 5a5fd488775ab5adaaf94f0797752dca0de8aaaf Mon Sep 17 00:00:00 2001 From: "Shaik, ShahinaX" Date: Mon, 5 Feb 2018 20:12:17 +0530 Subject: [PATCH 0839/1103] ASoC: Intel: Skylake: Update gain interface structure This patch updates gain structure alignment as per firmware interface requirement. Change-Id: I67d509ec8aaff2f9f02d1ad830f03dca7fa50465 Signed-off-by: Shaik, ShahinaX Reviewed-on: Reviewed-by: Shaik, Kareem M Reviewed-by: Kesapragada, Pardha Saradhi Reviewed-by: Kp, Jeeja Reviewed-by: Kale, Sanyog R Reviewed-by: Singh, Guneshwor O Reviewed-by: Prusty, Subhransu S Tested-by: Madiwalar, MadiwalappaX --- include/uapi/sound/skl-tplg-interface.h | 1 + 1 file changed, 1 insertion(+) diff --git a/include/uapi/sound/skl-tplg-interface.h b/include/uapi/sound/skl-tplg-interface.h index 5e284a4b6ce0..7fbd306fca68 100644 --- a/include/uapi/sound/skl-tplg-interface.h +++ b/include/uapi/sound/skl-tplg-interface.h @@ -201,6 +201,7 @@ struct skl_gain_config { u32 channel_id; u32 target_volume; u32 ramp_type; + u32 reserved; u64 ramp_duration; } __packed; From 0e75c9e43ede605e712e95e3205a00f6d0647b0d Mon Sep 17 00:00:00 2001 From: Pardha Saradhi K Date: Thu, 8 Mar 2018 00:35:50 +0530 Subject: [PATCH 0840/1103] ASoC: Intel: Skylake: Read extended crash dump info from DSP When DSP encounters an exception, besides providing basic info about the crash in the FW REGS section, an extended info is written in the log buffer, on a per core basis. This information is usually related to the module's stack that helps in identifying the reason for the Exception to occur. Audio driver needs to read this info from the log buffers and append it to the crash dump file. Change-Id: I0ae67e510f7627317b10445cdf3c2c927beaca4f Signed-off-by: Pardha Saradhi K Reviewed-on: Reviewed-by: Shaik, ShahinaX Reviewed-by: Shaik, Kareem M Reviewed-by: Sinha, Mohit Reviewed-by: Kp, Jeeja Tested-by: Madiwalar, MadiwalappaX --- sound/soc/intel/skylake/skl-sst-ipc.c | 9 ++- sound/soc/intel/skylake/skl-sst-ipc.h | 2 +- sound/soc/intel/skylake/skl-sst-utils.c | 102 +++++++++++++++++++++++- 3 files changed, 106 insertions(+), 7 deletions(-) diff --git a/sound/soc/intel/skylake/skl-sst-ipc.c b/sound/soc/intel/skylake/skl-sst-ipc.c index 13917d54dc32..e36160b1173f 100644 --- a/sound/soc/intel/skylake/skl-sst-ipc.c +++ b/sound/soc/intel/skylake/skl-sst-ipc.c @@ -202,6 +202,9 @@ #define MOD_DATA_OFFSET 12 #define SET_LARGE_CFG_FW_CONFIG 7 +#define DSP_EXCEP_CORE_MASK 0x3 +#define DSP_EXCEP_STACK_SIZE_SHIFT 2 + enum skl_ipc_msg_target { IPC_FW_GEN_MSG = 0, IPC_MOD_MSG = 1 @@ -494,9 +497,11 @@ int skl_ipc_process_notification(struct sst_generic_ipc *ipc, skl->miscbdcg_disabled = true; break; case IPC_GLB_NOTIFY_EXCEPTION_CAUGHT: - dev_err(ipc->dev, "*****Exception Detected **********\n"); + dev_err(ipc->dev, "*****Exception Detected on core id: %d \n",(header.extension & DSP_EXCEP_CORE_MASK)); + dev_err(ipc->dev, "Exception Stack size is %d\n", (header.extension >> DSP_EXCEP_STACK_SIZE_SHIFT)); /* hexdump of the fw core exception record reg */ - ret = skl_dsp_crash_dump_read(skl); + ret = skl_dsp_crash_dump_read(skl, + (header.extension >> DSP_EXCEP_STACK_SIZE_SHIFT)); if (ret < 0) { dev_err(ipc->dev, "dsp crash dump read fail:%d\n", ret); diff --git a/sound/soc/intel/skylake/skl-sst-ipc.h b/sound/soc/intel/skylake/skl-sst-ipc.h index 4b3c7e283030..36e699a1765b 100644 --- a/sound/soc/intel/skylake/skl-sst-ipc.h +++ b/sound/soc/intel/skylake/skl-sst-ipc.h @@ -470,7 +470,7 @@ int skl_ipc_process_notification(struct sst_generic_ipc *ipc, void skl_ipc_tx_data_copy(struct ipc_message *msg, char *tx_data, size_t tx_size); int skl_notify_tplg_change(struct skl_sst *ctx, int type); -int skl_dsp_crash_dump_read(struct skl_sst *ctx); +int skl_dsp_crash_dump_read(struct skl_sst *ctx, int stack_size); void skl_ipc_set_fw_cfg(struct sst_generic_ipc *ipc, u8 instance_id, u16 module_id, u32 *data); diff --git a/sound/soc/intel/skylake/skl-sst-utils.c b/sound/soc/intel/skylake/skl-sst-utils.c index 8f4056ebf670..a420d702f689 100644 --- a/sound/soc/intel/skylake/skl-sst-utils.c +++ b/sound/soc/intel/skylake/skl-sst-utils.c @@ -32,6 +32,7 @@ #define CRASH_DUMP_VERSION 0x1 /* FW Extended Manifest Header id = $AE1 */ #define SKL_EXT_MANIFEST_HEADER_MAGIC 0x31454124 +#define MAX_DSP_EXCEPTION_STACK_SIZE (64*1024) #define UUID_ATTR_RO(_name) \ struct uuid_attribute uuid_attr_##_name = __ATTR_RO(_name) @@ -308,6 +309,73 @@ void skl_reset_instance_id(struct skl_sst *ctx) } EXPORT_SYMBOL_GPL(skl_reset_instance_id); +/* This function checks tha available data on the core id + * passed as an argument and returns the bytes available + */ +static int skl_check_ext_excep_data_avail(struct skl_sst *ctx, int idx) +{ + u32 size = ctx->dsp->trace_wind.size/ctx->dsp->trace_wind.nr_dsp; + u8 *base = (u8 __force*)ctx->dsp->trace_wind.addr; + u32 read, write; + u32 *ptr; + + /* move to the source dsp tracing window */ + base += (idx * size); + ptr = (u32 *) base; + read = ptr[0]; + write = ptr[1]; + + if (write == read) + return 0; + else if (write > read) + return (write - read); + else + return (size - 8 - read + write); +} + +/* Function to read the extended DSP crash information from the + * log buffer memory window, on per core basis. + * Data is read into the buffer passed as *ext_core_dump. + * number of bytes read is updated in the sz_ext_dump + */ +static void skl_read_ext_exception_data(struct skl_sst *ctx, int idx, + void *ext_core_dump, int *sz_ext_dump) +{ + u32 size = ctx->dsp->trace_wind.size/ctx->dsp->trace_wind.nr_dsp; + u8 *base = (u8 __force*)ctx->dsp->trace_wind.addr; + u32 read, write; + int offset = *sz_ext_dump; + u32 *ptr; + + /* move to the current core's tracing window */ + base += (idx * size); + ptr = (u32 *) base; + read = ptr[0]; + write = ptr[1]; + if (write > read) { + memcpy_fromio((ext_core_dump + offset), + (const void __iomem *)(base + 8 + read), + (write - read)); + *sz_ext_dump = offset + write - read; + /* advance read pointer */ + ptr[0] += write - read; + } else { + /* wrap around condition - copy till the end */ + memcpy_fromio((ext_core_dump + offset), + (const void __iomem *)(base + 8 + read), + (size - 8 - read)); + *sz_ext_dump = offset + size - 8 - read; + offset = *sz_ext_dump; + + /* copy from the beginnning */ + memcpy_fromio((ext_core_dump + offset), + (const void __iomem *) (base + 8), write); + *sz_ext_dump = offset + write; + /* update the read pointer */ + ptr[0] = write; + } +} + int skl_dsp_crash_dump_read(struct skl_sst *ctx, int stack_size) { int num_mod = 0, size_core_dump, sz_ext_dump = 0, idx = 0; @@ -320,6 +388,7 @@ int skl_dsp_crash_dump_read(struct skl_sst *ctx, int stack_size) struct adsp_type0_crash_data *type0_data; struct adsp_type1_crash_data *type1_data; struct adsp_type2_crash_data *type2_data; + struct sst_dsp *sst = ctx->dsp; if (list_empty(&ctx->uuid_list)) dev_info(ctx->dev, "Module list is empty\n"); @@ -328,6 +397,21 @@ int skl_dsp_crash_dump_read(struct skl_sst *ctx, int stack_size) num_mod++; } + if(stack_size) + ext_core_dump = vzalloc(stack_size); + else + ext_core_dump = vzalloc(MAX_DSP_EXCEPTION_STACK_SIZE); + if (!ext_core_dump) { + dev_err(ctx->dsp->dev, "failed to allocate memory for FW Stack\n"); + return -ENOMEM; + } + for (idx = 0; idx < sst->trace_wind.nr_dsp; idx++) { + while(skl_check_ext_excep_data_avail(ctx, idx)) { + skl_read_ext_exception_data(ctx, idx, + ext_core_dump, &sz_ext_dump); + } + } + /* Length representing in DWORD */ length0 = sizeof(*type0_data) / sizeof(u32); length1 = (num_mod * sizeof(*type1_data)) / sizeof(u32); @@ -336,11 +420,14 @@ int skl_dsp_crash_dump_read(struct skl_sst *ctx, int stack_size) /* type1 data size is calculated based on number of modules */ size_core_dump = (MAX_CRASH_DATA_TYPES * sizeof(*crash_data_hdr)) + sizeof(*type0_data) + (num_mod * sizeof(*type1_data)) + - sizeof(*type2_data); + sizeof(*type2_data) + sz_ext_dump; - coredump = vzalloc(size_core_dump); - if (!coredump) + coredump = vzalloc(size_core_dump + sz_ext_dump); + if (!coredump){ + dev_err(ctx->dsp->dev, "failed to allocate memory \n"); + vfree(ext_core_dump); return -ENOMEM; + } offset = coredump; @@ -382,8 +469,15 @@ int skl_dsp_crash_dump_read(struct skl_sst *ctx, int stack_size) memcpy_fromio(type2_data->fwreg, (const void __iomem *)fw_reg_addr, sizeof(*type2_data)); + if (sz_ext_dump) { + offset = coredump + size_core_dump; + memcpy(offset, ext_core_dump, sz_ext_dump); + } + + vfree(ext_core_dump); + dev_coredumpv(ctx->dsp->dev, coredump, - size_core_dump, GFP_KERNEL); + size_core_dump + sz_ext_dump, GFP_KERNEL); return 0; } From 948dd487702864190fa248cdecac3d6baf0b3c8e Mon Sep 17 00:00:00 2001 From: Pardha Saradhi K Date: Thu, 8 Mar 2018 00:45:37 +0530 Subject: [PATCH 0841/1103] ASoC: Intel: Skylake: Fix issues in ADSP Extended Crash Dump feature Extended crash data along with Header and footer tags is written to the log buffers by the ADSP - FW owns the write pointer and driver manages the read pointer. It has been observed that complete info is not getting flushed out to the dump file. Hence the dump logic has been altered to check for completeness with respect to stack size as returned by the ADSP. Change-Id: Ieebef84a454af2f8510272a9156269d7ccdb567c Signed-off-by: Pardha Saradhi K Reviewed-on: Reviewed-by: Shaik, ShahinaX Reviewed-by: Shaik, Kareem M Reviewed-by: Kp, Jeeja Reviewed-by: Tewani, Pradeep D Tested-by: Madiwalar, MadiwalappaX --- sound/soc/intel/skylake/skl-sst-utils.c | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/sound/soc/intel/skylake/skl-sst-utils.c b/sound/soc/intel/skylake/skl-sst-utils.c index a420d702f689..668e7d020a9c 100644 --- a/sound/soc/intel/skylake/skl-sst-utils.c +++ b/sound/soc/intel/skylake/skl-sst-utils.c @@ -34,6 +34,9 @@ #define SKL_EXT_MANIFEST_HEADER_MAGIC 0x31454124 #define MAX_DSP_EXCEPTION_STACK_SIZE (64*1024) +/* FW adds headers and trailing patters to extended crash data */ +#define EXTRA_BYTES 256 + #define UUID_ATTR_RO(_name) \ struct uuid_attribute uuid_attr_##_name = __ATTR_RO(_name) @@ -352,6 +355,11 @@ static void skl_read_ext_exception_data(struct skl_sst *ctx, int idx, ptr = (u32 *) base; read = ptr[0]; write = ptr[1]; + + /* in case of read = write, just return */ + if (read == write) + return; + if (write > read) { memcpy_fromio((ext_core_dump + offset), (const void __iomem *)(base + 8 + read), @@ -398,17 +406,19 @@ int skl_dsp_crash_dump_read(struct skl_sst *ctx, int stack_size) } if(stack_size) - ext_core_dump = vzalloc(stack_size); + ext_core_dump = vzalloc(stack_size + EXTRA_BYTES); else - ext_core_dump = vzalloc(MAX_DSP_EXCEPTION_STACK_SIZE); + ext_core_dump = vzalloc(MAX_DSP_EXCEPTION_STACK_SIZE + EXTRA_BYTES); if (!ext_core_dump) { dev_err(ctx->dsp->dev, "failed to allocate memory for FW Stack\n"); return -ENOMEM; } for (idx = 0; idx < sst->trace_wind.nr_dsp; idx++) { - while(skl_check_ext_excep_data_avail(ctx, idx)) { - skl_read_ext_exception_data(ctx, idx, + if(skl_check_ext_excep_data_avail(ctx, idx) != 0) { + while(sz_ext_dump < stack_size) { + skl_read_ext_exception_data(ctx, idx, ext_core_dump, &sz_ext_dump); + } } } From 4aae4862e17cd240837e4ae050f42e8f979071db Mon Sep 17 00:00:00 2001 From: Vinod Koul Date: Wed, 17 Jan 2018 09:56:04 +0530 Subject: [PATCH 0842/1103] ASoC: Intel: kconfig: Make drivers build on x86 only The drivers are designed to build and run for X86 arch only. The current depends on ACPI was okay earlier but now we have ACPI on non X86 systems too so add explicit X86 dependency. Change-Id: I3ef91b9799b7593c2c75d07e7f63bf0bf7d9113d Signed-off-by: Vinod Koul Reviewed-on: Reviewed-by: Pawse, GuruprasadX Reviewed-by: Singh, Guneshwor O Reviewed-by: Kale, Sanyog R Tested-by: Madiwalar, MadiwalappaX --- sound/soc/intel/Kconfig | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/sound/soc/intel/Kconfig b/sound/soc/intel/Kconfig index c08d87821c9f..e6334dda6237 100644 --- a/sound/soc/intel/Kconfig +++ b/sound/soc/intel/Kconfig @@ -49,7 +49,7 @@ config SND_SOC_INTEL_SST_FIRMWARE config SND_SOC_INTEL_HASWELL tristate "Haswell/Broadwell Platforms" depends on SND_DMA_SGBUF - depends on DMADEVICES && ACPI + depends on DMADEVICES && ACPI && X86 select SND_SOC_INTEL_SST select SND_SOC_INTEL_SST_ACPI select SND_SOC_INTEL_SST_FIRMWARE @@ -61,7 +61,7 @@ config SND_SOC_INTEL_HASWELL config SND_SOC_INTEL_BAYTRAIL tristate "Baytrail (legacy) Platforms" - depends on DMADEVICES && ACPI && SND_SST_ATOM_HIFI2_PLATFORM=n + depends on DMADEVICES && ACPI && X86 && SND_SST_ATOM_HIFI2_PLATFORM=n select SND_SOC_INTEL_SST select SND_SOC_INTEL_SST_ACPI select SND_SOC_INTEL_SST_FIRMWARE @@ -106,7 +106,7 @@ config SND_SOC_INTEL_SKYLAKE_SSP_CLK config SND_SOC_INTEL_SKYLAKE tristate "SKL/BXT/KBL/GLK/CNL... Platforms" - depends on PCI && ACPI + depends on PCI && ACPI && X86 select SND_HDA_EXT_CORE select SND_HDA_DSP_LOADER select SND_SOC_TOPOLOGY From 58579a684620a44a3bd8db85248dcd2303649808 Mon Sep 17 00:00:00 2001 From: "Shaik, ShahinaX" Date: Mon, 2 Apr 2018 23:37:54 +0530 Subject: [PATCH 0843/1103] ASoC: tdf8532: Fix Audio memory leakage at boot time Fix kmemleak issue in tdf8532_get_state function by relasing the local allocated memory before exiting the function. kmemleak backtrace: unreferenced object 0xffff880270cabd40 (size 32): comm "alsa_aplay", pid 2409, jiffies 4294673205 (age 91.856s) hex dump (first 32 bytes): 02 00 03 80 80 01 00 00 00 00 00 00 00 00 00 00 ................ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ backtrace: [] kmemleak_alloc+0x4a/0xa0 [] __kmalloc+0x128/0x210 [] tdf8532_wait_state.constprop.5+0x116/0x250 [snd_soc_tdf8532] [] tdf8532_dai_trigger+0x148/0x14d [snd_soc_tdf8532] [] soc_pcm_trigger+0x75/0x130 [] dpcm_do_trigger.isra.6+0x29/0x90 [] dpcm_be_dai_trigger+0x100/0x350 [] dpcm_fe_dai_do_trigger+0x13a/0x200 [] dpcm_fe_dai_trigger+0x38/0x40 [] snd_pcm_do_start+0x2a/0x30 [] snd_pcm_action_single+0x3b/0x90 [] snd_pcm_action+0xe1/0x110 [] snd_pcm_common_ioctl1+0x318/0xc90 [] snd_pcm_playback_ioctl1+0x19f/0x250 [] snd_pcm_playback_ioctl+0x27/0x40 [] do_vfs_ioctl+0xa8/0x620 Change-Id: I8621e17997022274509554139097d849b211f384 Signed-off-by: Shaik, ShahinaX Reviewed-on: Reviewed-by: Kale, Sanyog R Reviewed-by: Periyasamy, SriramX Reviewed-by: Singh, Guneshwor O Reviewed-by: Babu, Ramesh Reviewed-by: Shaik, Kareem M Reviewed-by: Koul, Vinod Tested-by: Madiwalar, MadiwalappaX --- sound/soc/codecs/tdf8532.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sound/soc/codecs/tdf8532.c b/sound/soc/codecs/tdf8532.c index a5e2a028338c..68decd023a9d 100644 --- a/sound/soc/codecs/tdf8532.c +++ b/sound/soc/codecs/tdf8532.c @@ -165,6 +165,8 @@ static int tdf8532_get_state(struct tdf8532_priv *dev_data, *status_repl = (struct get_dev_status_repl *) repl_buff; out: + if (repl_buff) + kfree(repl_buff); return ret; } From 600eac65bda3b981709dfcb0d9f3c63fc1e75782 Mon Sep 17 00:00:00 2001 From: "Shaik, ShahinaX" Date: Thu, 29 Mar 2018 21:40:15 +0530 Subject: [PATCH 0844/1103] ASoC: Intel: Skylake: Fix Audio memory leakage at boot time Fix kmemleak issues in skl_module_sysfs_init functions by, instead of allocating memory, we use local variable of type "char array" and snprintf to write uuid. kmemleak backtrace: unreferenced object 0xffff880267946e00 (size 64): comm "insmod", pid 2340, jiffies 4294672904 (age 92.133s) hex dump (first 32 bytes): 36 36 42 34 34 30 32 44 2d 42 34 36 38 2d 34 32 66B4402D-B468-42 46 32 2d 38 31 41 37 2d 42 33 37 31 32 31 38 36 F2-81A7-B3712186 backtrace: [] kmemleak_alloc+0x4a/0xa0 [] __kmalloc_track_caller+0x124/0x200 [] kvasprintf+0x66/0xd0 [] kasprintf+0x4e/0x70 [] skl_module_sysfs_init+0x13a/0x260 [snd_soc_skl_ipc] [] skl_platform_soc_probe+0x246/0x480 [snd_soc_skl] [] snd_soc_platform_drv_probe+0x1c/0x20 [] soc_probe_component+0x227/0x3c0 [] snd_soc_register_card+0x687/0xf00 [] broxton_tdf8532_audio_probe+0x3a/0x3d [snd_soc_sst_bxt_tdf8532] [] platform_drv_probe+0x3e/0xa0 [] driver_probe_device+0xef/0x410 [] __driver_attach+0xa7/0xf0 [] bus_for_each_dev+0x70/0xc0 [] driver_attach+0x1e/0x20 [] bus_add_driver+0x1c7/0x270 Change-Id: Ib326d4400c0d7ac6cd4ad36a2dd006663837cee7 Signed-off-by: Shaik, ShahinaX Reviewed-on: Reviewed-by: Shaik, Kareem M Reviewed-by: Singh, Guneshwor O Reviewed-by: Babu, Ramesh Reviewed-by: Kale, Sanyog R Reviewed-by: Koul, Vinod Tested-by: Madiwalar, MadiwalappaX --- sound/soc/intel/skylake/skl-sst-utils.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/sound/soc/intel/skylake/skl-sst-utils.c b/sound/soc/intel/skylake/skl-sst-utils.c index 668e7d020a9c..7c867426b39b 100644 --- a/sound/soc/intel/skylake/skl-sst-utils.c +++ b/sound/soc/intel/skylake/skl-sst-utils.c @@ -1178,10 +1178,11 @@ int skl_module_sysfs_init(struct skl_sst *ctx, struct kobject *kobj) { struct uuid_module *module; struct skl_sysfs_module *modinfo_obj; - char *uuid_name; int count = 0; int max_mod = 0; int ret = 0; + unsigned int uuid_size = sizeof(module->uuid); + char uuid_name[uuid_size]; if (list_empty(&ctx->uuid_list)) return 0; @@ -1218,7 +1219,7 @@ int skl_module_sysfs_init(struct skl_sst *ctx, struct kobject *kobj) goto err_sysfs_exit; } - uuid_name = kasprintf(GFP_KERNEL, "%pUL", &module->uuid); + snprintf(uuid_name, sizeof(uuid_name), "%pUL", &module->uuid); ret = kobject_init_and_add(&modinfo_obj->kobj, &uuid_ktype, ctx->sysfs_tree->modules_kobj, uuid_name); if (ret < 0) @@ -1240,7 +1241,7 @@ int skl_module_sysfs_init(struct skl_sst *ctx, struct kobject *kobj) return 0; err_sysfs_exit: - skl_module_sysfs_exit(ctx); + skl_module_sysfs_exit(ctx); return ret; } From 31c7d3bee5d6d0950ba5dd4b432a24875eeb31b9 Mon Sep 17 00:00:00 2001 From: Pardha Saradhi K Date: Tue, 10 Apr 2018 23:46:48 +0530 Subject: [PATCH 0845/1103] ASoC: Intel: Skylake: Add support to notify resource event ADSP notifies to driver in case of any resource events that occur while executing a usecase. These are notification IPCs that belong to a class called RESOURCE_EVENT. This patch displays such notifications to the console via debug messages. Change-Id: I7faaf31fab692ac77eefd91a5ed6e4d36c92b228 Signed-off-by: Giribabu Gogineni Reviewed-on: Reviewed-by: Shaik, Kareem M Reviewed-by: Prabhu, PuneethX Reviewed-by: Singh, Guneshwor O Reviewed-by: Sinha, Mohit Reviewed-by: Kp, Jeeja Tested-by: Madiwalar, MadiwalappaX --- sound/soc/intel/skylake/skl-sst-ipc.c | 125 +++++++++++++++++++++++++- 1 file changed, 123 insertions(+), 2 deletions(-) diff --git a/sound/soc/intel/skylake/skl-sst-ipc.c b/sound/soc/intel/skylake/skl-sst-ipc.c index e36160b1173f..6efc9502d06e 100644 --- a/sound/soc/intel/skylake/skl-sst-ipc.c +++ b/sound/soc/intel/skylake/skl-sst-ipc.c @@ -204,6 +204,7 @@ #define DSP_EXCEP_CORE_MASK 0x3 #define DSP_EXCEP_STACK_SIZE_SHIFT 2 +#define SKL_FW_RSRCE_EVNT_DATA_SZ 6 enum skl_ipc_msg_target { IPC_FW_GEN_MSG = 0, @@ -232,6 +233,28 @@ enum skl_ipc_glb_type { IPC_GLB_MAX_IPC_MSG_NUMBER = 31 /* Maximum message number */ }; +/* Resource Event Types */ +enum skl_ipc_resource_event_type { + SKL_BUDGET_VIOLATION = 0, + SKL_MIXER_UNDERRUN = 1, + SKL_STREAM_DATA_SEGMENT = 2, + SKL_PROCESS_DATA_ERR = 3, + SKL_STACK_OVERFLOW = 4, + SKL_BUFFERING_MODE_CHANGED = 5, + SKL_GATEWAY_UNDERRUN = 6, + SKL_GATEWAY_OVERRUN = 7, + SKL_EDF_DOMAIN_UNSTABLE = 8, + SKL_WCLK_SAMPLE_COUNT = 9, + SKL_GATEWAY_HIGH_THRESHOLD = 10, + SKL_GATEWAY_LOW_THRESHOLD = 11, + SKL_I2S_BCE_DETECTED = 12, + SKL_I2S_CLK_STATE_CHANGED = 13, + SKL_I2S_SINK_MODE_CHANGED = 14, + SKL_I2S_SOURCE_MODE_CHANGED = 15, + SKL_SRE_DRIFT_TOO_HIGH = 16, + SKL_INVALID_RESORUCE_EVENT_TYPE = 17 +}; + enum skl_ipc_glb_reply { IPC_GLB_REPLY_SUCCESS = 0, @@ -298,6 +321,13 @@ enum skl_ipc_module_msg { IPC_MOD_DELETE_INSTANCE = 11 }; +struct skl_event_notify { + u32 resource_type; + u32 resource_id; + u32 event_type; + u32 event_data[SKL_FW_RSRCE_EVNT_DATA_SZ]; +} __packed; + void skl_ipc_tx_data_copy(struct ipc_message *msg, char *tx_data, size_t tx_size) { @@ -457,6 +487,98 @@ skl_process_log_buffer(struct sst_dsp *sst, struct skl_ipc_header header) skl_dsp_put_log_buff(sst, core); } +static void +skl_parse_resource_event(struct skl_sst *skl, struct skl_ipc_header header) +{ + struct skl_event_notify notify; + struct sst_dsp *sst = skl->dsp; + + /* read the message contents from mailbox */ + sst_dsp_inbox_read(sst, ¬ify, sizeof(struct skl_event_notify)); + + /* notify user about the event type */ + switch (notify.event_type) { + + case SKL_BUDGET_VIOLATION: + dev_err(sst->dev, "MCPS Budget Violation: %x\n", + header.primary); + break; + case SKL_MIXER_UNDERRUN: + dev_err(sst->dev, "Mixer Underrun Detected: %x\n", + header.primary); + break; + case SKL_STREAM_DATA_SEGMENT: + dev_err(sst->dev, "Stream Data Segment: %x\n", + header.primary); + break; + case SKL_PROCESS_DATA_ERR: + dev_err(sst->dev, "Process Data Error: %x\n", + header.primary); + break; + case SKL_STACK_OVERFLOW: + dev_err(sst->dev, "Stack Overflow: %x\n", + header.primary); + break; + case SKL_BUFFERING_MODE_CHANGED: + dev_err(sst->dev, "Buffering Mode Changed: %x\n", + header.primary); + break; + case SKL_GATEWAY_UNDERRUN: + dev_err(sst->dev, "Gateway Underrun Detected: %x\n", + header.primary); + break; + case SKL_GATEWAY_OVERRUN: + dev_err(sst->dev, "Gateway Overrun Detected: %x\n", + header.primary); + break; + case SKL_WCLK_SAMPLE_COUNT: + dev_err(sst->dev, + "FW Wclk and Sample count Notif Detected: %x\n", + header.primary); + break; + case SKL_GATEWAY_HIGH_THRESHOLD: + dev_err(sst->dev, "IPC gateway reached high threshold: %x\n", + header.primary); + break; + case SKL_GATEWAY_LOW_THRESHOLD: + dev_err(sst->dev, "IPC gateway reached low threshold: %x\n", + header.primary); + break; + case SKL_I2S_BCE_DETECTED: + dev_err(sst->dev, "Bit Count Error detected on I2S port: %x\n", + header.primary); + break; + case SKL_I2S_CLK_STATE_CHANGED: + dev_err(sst->dev, "Clock detected/loss on I2S port: %x\n", + header.primary); + break; + case SKL_I2S_SINK_MODE_CHANGED: + dev_err(sst->dev, "I2S Sink started/stopped dropping \ + data in non-blk mode: %x\n", header.primary); + break; + case SKL_I2S_SOURCE_MODE_CHANGED: + dev_err(sst->dev, "I2S Source started/stopped generating 0's \ + in non-blk mode: %x\n", header.primary); + break; + case SKL_SRE_DRIFT_TOO_HIGH: + dev_err(sst->dev, + "Frequency drift exceeded limit in SRE: %x\n", + header.primary); + break; + case SKL_INVALID_RESORUCE_EVENT_TYPE: + dev_err(sst->dev, "Invalid type: %x\n", header.primary); + break; + default: + dev_err(sst->dev, "ipc: Unhandled resource event=%x", + header.primary); + break; + } + + print_hex_dump(KERN_DEBUG, "Params:", + DUMP_PREFIX_OFFSET, 8, 4, + ¬ify, sizeof(struct skl_event_notify), false); +} + int skl_ipc_process_notification(struct sst_generic_ipc *ipc, struct skl_ipc_header header) { @@ -471,8 +593,7 @@ int skl_ipc_process_notification(struct sst_generic_ipc *ipc, break; case IPC_GLB_NOTIFY_RESOURCE_EVENT: - dev_err(ipc->dev, "MCPS Budget Violation: %x\n", - header.primary); + skl_parse_resource_event(skl, header); break; case IPC_GLB_NOTIFY_FW_READY: From cb9871536dace0817dd0d1fb834f7c5991158944 Mon Sep 17 00:00:00 2001 From: "Shaik, Kareem M" Date: Fri, 1 Dec 2017 00:46:21 +0530 Subject: [PATCH 0846/1103] ASoC: Intel: BXT: Retry FW download sequence In FW download failure case, the recommended solution is to repeat complete download sequence in multiple iterations.FW download sequence is: 1. ROM initialization 2. BaseFW download During FW download failure, repeat the entire download sequence in three iteration and give-up. Change-Id: Ib29d3b0208c91d959ca1df0fc963e3e9f1d2b994 Tracked-On: Signed-off-by: Kareem,Shaik Reviewed-on: Reviewed-on: Reviewed-by: Shaik, ShahinaX Reviewed-by: Gogineni, GiribabuX Reviewed-by: Sinha, Mohit Reviewed-by: Singh, Guneshwor O Reviewed-by: Kp, Jeeja Tested-by: Madiwalar, MadiwalappaX --- sound/soc/intel/skylake/bxt-sst.c | 46 ++++++++++++++++++------------- 1 file changed, 27 insertions(+), 19 deletions(-) diff --git a/sound/soc/intel/skylake/bxt-sst.c b/sound/soc/intel/skylake/bxt-sst.c index 2eb57d75f1b1..47b2a24f84dc 100644 --- a/sound/soc/intel/skylake/bxt-sst.c +++ b/sound/soc/intel/skylake/bxt-sst.c @@ -59,7 +59,7 @@ /* Delay before scheduling D0i3 entry */ #define BXT_D0I3_DELAY 5000 -#define BXT_FW_ROM_INIT_RETRY 3 +#define BXT_FW_INIT_RETRY 3 #define GET_SSP_BASE(N) (N > 4 ? 0x2000 : 0x4000) @@ -270,30 +270,38 @@ static int bxt_load_base_firmware(struct sst_dsp *ctx) stripped_fw.size = ctx->fw->size; skl_dsp_strip_extended_manifest(&stripped_fw); - - for (i = 0; i < BXT_FW_ROM_INIT_RETRY; i++) { + for (i = 0; i < BXT_FW_INIT_RETRY; i++) { ret = sst_bxt_prepare_fw(ctx, stripped_fw.data, stripped_fw.size); + if (ret < 0) { + dev_err(ctx->dev, "Error code=0x%x: FW status=0x%x\n", + sst_dsp_shim_read(ctx, BXT_ADSP_ERROR_CODE), + sst_dsp_shim_read(ctx, BXT_ADSP_FW_STATUS)); + + dev_err(ctx->dev, "Itertion %d Core En/ROM load fail:%d\n", i,ret); + continue; + } + dev_dbg(ctx->dev, "Itertion %d ROM load Success:%d,%d\n", i,ret); + + ret = sst_transfer_fw_host_dma(ctx); + if (ret < 0) { + dev_err(ctx->dev, "Itertion %d Transfer firmware failed %d\n", i,ret); + dev_info(ctx->dev, "Error code=0x%x: FW status=0x%x\n", + sst_dsp_shim_read(ctx, BXT_ADSP_ERROR_CODE), + sst_dsp_shim_read(ctx, BXT_ADSP_FW_STATUS)); + + skl_dsp_core_power_down(ctx, SKL_DSP_CORE_MASK(1)); + skl_dsp_disable_core(ctx, SKL_DSP_CORE0_MASK); + continue; + } + dev_dbg(ctx->dev, "Itertion %d FW transfer Success:%d,%d\n", i,ret); + if (ret == 0) break; } - if (ret < 0) { - dev_err(ctx->dev, "Error code=0x%x: FW status=0x%x\n", - sst_dsp_shim_read(ctx, BXT_ADSP_ERROR_CODE), - sst_dsp_shim_read(ctx, BXT_ADSP_FW_STATUS)); - - dev_err(ctx->dev, "Core En/ROM load fail:%d\n", ret); + if (ret < 0) { + dev_err(ctx->dev, "Firmware download failed\n"); goto sst_load_base_firmware_failed; - } - - ret = sst_transfer_fw_host_dma(ctx); - if (ret < 0) { - dev_err(ctx->dev, "Transfer firmware failed %d\n", ret); - dev_info(ctx->dev, "Error code=0x%x: FW status=0x%x\n", - sst_dsp_shim_read(ctx, BXT_ADSP_ERROR_CODE), - sst_dsp_shim_read(ctx, BXT_ADSP_FW_STATUS)); - - skl_dsp_disable_core(ctx, SKL_DSP_CORE0_MASK); } else { dev_dbg(ctx->dev, "Firmware download successful\n"); ret = wait_event_timeout(skl->boot_wait, skl->boot_complete, From afc4abca2d23064b9f812255a027a1e7e7a30d97 Mon Sep 17 00:00:00 2001 From: Pankaj Bharadiya Date: Wed, 13 Dec 2017 10:40:30 +0530 Subject: [PATCH 0847/1103] ASoC: Intel: Skylake: Check for pointer validity. kzalloc allocation can fail. Return -ENOMEM if allocation fails. Change-Id: Idc3c1c05e2797332b94372f401794c4d9c5ad7c0 Signed-off-by: Pankaj Bharadiya --- sound/soc/intel/skylake/skl-debug.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/sound/soc/intel/skylake/skl-debug.c b/sound/soc/intel/skylake/skl-debug.c index f3c5e29afaa6..30507fa0208c 100644 --- a/sound/soc/intel/skylake/skl-debug.c +++ b/sound/soc/intel/skylake/skl-debug.c @@ -186,6 +186,9 @@ static ssize_t mod_control_write(struct file *file, d->ipc_data[0] = 0; buf = kzalloc(MOD_BUF, GFP_KERNEL); + if (!buf) + return -ENOMEM; + written = simple_write_to_buffer(buf, MOD_BUF, ppos, user_buf, count); size = written; From 4ea56d997ce748416060a3f39aa5684ca28acc53 Mon Sep 17 00:00:00 2001 From: Pankaj Bharadiya Date: Fri, 23 Feb 2018 15:47:19 +0530 Subject: [PATCH 0848/1103] ASoC: Intel: Skylake: Fix logs_core array size 2 extra trace buffer dais were added for CNL but logs_core array size had not been updated to accommodate this. Fix the logs_core array size to correct value. Fixes: ccc6166da92f ("ASoC: Intel: Skylake: Add trace buffer dais for CNL") Change-Id: I2ec363e22605d60ec886fdea20a600c9583a4b4e Signed-off-by: Pankaj Bharadiya --- sound/soc/intel/skylake/skl-sst-ipc.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/intel/skylake/skl-sst-ipc.h b/sound/soc/intel/skylake/skl-sst-ipc.h index 36e699a1765b..436e0365aa76 100644 --- a/sound/soc/intel/skylake/skl-sst-ipc.h +++ b/sound/soc/intel/skylake/skl-sst-ipc.h @@ -365,7 +365,7 @@ struct skl_log_state_msg { uint32_t fifo_full_timer_period; u32 core_mask; - struct skl_log_state logs_core[2]; + struct skl_log_state logs_core[4]; }; struct SystemTime { From 00016a34175e444b31f0d6025d85211df9eea24b Mon Sep 17 00:00:00 2001 From: Pankaj Bharadiya Date: Wed, 28 Feb 2018 15:38:13 +0530 Subject: [PATCH 0849/1103] ASoC: Intel: Skylake: Replace strcpy with strlcpy Replace strcpy with strlcpy, as it avoids a possible buffer overflow. Change-Id: I3f612640979d919af513ff2c79d4d0207bb62f5a Signed-off-by: Pankaj Bharadiya --- sound/soc/intel/skylake/cnl-acpi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/intel/skylake/cnl-acpi.c b/sound/soc/intel/skylake/cnl-acpi.c index 1bee574f2ab8..bda94640d966 100644 --- a/sound/soc/intel/skylake/cnl-acpi.c +++ b/sound/soc/intel/skylake/cnl-acpi.c @@ -129,7 +129,7 @@ int cnl_sdw_get_master_caps(struct device *dev, acpi_string path_sdw_ctrl = {"SCD"}; char path[SDW_PATH_CTRL_MAX]; - strcpy(path, path_sdw_ctrl); + strlcpy(path, path_sdw_ctrl, sizeof(path)); sdw_acpi_mstr_map_data(m_cap, dev, path_sdw_ctrl, path); if (!m_cap) { dev_err(dev, "SoundWire controller mapping failed...\n"); From bc67688812cb62c286829348d6cd41e4e5cce02f Mon Sep 17 00:00:00 2001 From: Shahina Shaik Date: Mon, 30 Apr 2018 18:47:02 +0530 Subject: [PATCH 0850/1103] ASoC: Intel: Boards: Replace codec to component in RT274 machine driver As the framework is changed in kernel 4.17 version, replace codec variable with component and use component specific function to set jack. Change-Id: Id6d1cda7968a5d524a3210f1b38221214c2bb67d Signed-off-by: Shahina Shaik --- sound/soc/intel/boards/cnl_rt274.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sound/soc/intel/boards/cnl_rt274.c b/sound/soc/intel/boards/cnl_rt274.c index 5570474806ab..890e474ce597 100644 --- a/sound/soc/intel/boards/cnl_rt274.c +++ b/sound/soc/intel/boards/cnl_rt274.c @@ -188,7 +188,7 @@ static const struct snd_soc_dapm_route cnl_map[] = { static int cnl_rt274_init(struct snd_soc_pcm_runtime *runtime) { int ret; - struct snd_soc_codec *codec = runtime->codec; + struct snd_soc_component *component = runtime->codec_dai->component; struct snd_soc_card *card = runtime->card; struct snd_soc_dai *codec_dai = runtime->codec_dai; @@ -199,7 +199,7 @@ static int cnl_rt274_init(struct snd_soc_pcm_runtime *runtime) if (ret) return ret; - snd_soc_codec_set_jack(codec, &cnl_headset, NULL); + snd_soc_component_set_jack(component, &cnl_headset, NULL); /* TDM 4 slots 24 bit, set Rx & Tx bitmask to 4 active slots */ ret = snd_soc_dai_set_tdm_slot(codec_dai, 0xF, 0xF, 4, 24); From 84b5b998d40db79c6d5bdea60aec74e9a7a00c4f Mon Sep 17 00:00:00 2001 From: Zhang Yanmin Date: Thu, 12 Apr 2018 16:42:28 +0530 Subject: [PATCH 0851/1103] ASoC: Intel: Skylake: Set dsp cores off during shutdown When the cores.usage_count is equal to 0, driver puts dsp cores to sleep. The issue happens when cores.usage_count is not equal to 0 and dsp core remains ON even when the system goes to shutdown. Removing the dependency of usage_count by forcing to disable the dsp core. Change-Id: I4d1c925dd9521c9eda2188e20eb262abf81e7b49 Signed-off-by: Mohit Sinha Signed-off-by: Zhang Yanmin Reviewed-on: Reviewed-by: Gogineni, GiribabuX Reviewed-by: Shaik, Kareem M Reviewed-by: Periyasamy, SriramX Reviewed-by: Kesapragada, Pardha Saradhi Reviewed-by: Kale, Sanyog R Tested-by: Madiwalar, MadiwalappaX --- sound/soc/intel/skylake/skl-sst-dsp.c | 1 + sound/soc/intel/skylake/skl.c | 9 +++++++++ 2 files changed, 10 insertions(+) diff --git a/sound/soc/intel/skylake/skl-sst-dsp.c b/sound/soc/intel/skylake/skl-sst-dsp.c index 485c8b8c38a1..0f5e497e6f93 100644 --- a/sound/soc/intel/skylake/skl-sst-dsp.c +++ b/sound/soc/intel/skylake/skl-sst-dsp.c @@ -310,6 +310,7 @@ int skl_dsp_disable_core(struct sst_dsp *ctx, unsigned int core_mask) return ret; } +EXPORT_SYMBOL(skl_dsp_disable_core); int skl_dsp_boot(struct sst_dsp *ctx) { diff --git a/sound/soc/intel/skylake/skl.c b/sound/soc/intel/skylake/skl.c index 29e6c5a22f51..a0d41441a7a4 100644 --- a/sound/soc/intel/skylake/skl.c +++ b/sound/soc/intel/skylake/skl.c @@ -1110,6 +1110,15 @@ static void skl_shutdown(struct pci_dev *pci) return; snd_hdac_ext_stop_streams(bus); + /* While doing the warm reboot testing, some times dsp core is on + * when system goes to shutdown. When cores.usage_count is + * equal to zero then driver puts the dsp core to zero. On few + * warm reboots cores.usage_count is not equal to zero and dsp + * core is ON even system goes to shutdown. Force the dsp cores + * off without checking the usage_count. + */ + skl_dsp_disable_core(skl->skl_sst->dsp, SKL_DSP_CORE0_ID); + list_for_each_entry(s, &bus->stream_list, list) { stream = stream_to_hdac_ext_stream(s); snd_hdac_ext_stream_decouple(bus, stream, false); From 4a7088cfd3d64eab51236daec168a30a7451edc3 Mon Sep 17 00:00:00 2001 From: Zhang Yanmin Date: Thu, 12 Apr 2018 17:05:23 +0530 Subject: [PATCH 0852/1103] ASoC: Intel: Disable dsp core in skl_shutdown skl_shutdown requires to put dsp cores quiescent else leads to the issue when PMC timeout while waiting for IP SIDE_POK_STS and PRIM_POK_STS deassertions Change-Id: I6c654e5afeb9267b0887a70722fce9f4afa8a1d9 Signed-off-by: Mohit Sinha Signed-off-by: Zhang Yanmin Reviewed-on: Reviewed-by: Gogineni, GiribabuX Reviewed-by: Shaik, Kareem M Reviewed-by: Periyasamy, SriramX Reviewed-by: Kesapragada, Pardha Saradhi Reviewed-by: Kale, Sanyog R Tested-by: Madiwalar, MadiwalappaX --- sound/hda/ext/hdac_ext_stream.c | 1 - sound/soc/intel/skylake/skl.c | 2 ++ 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/sound/hda/ext/hdac_ext_stream.c b/sound/hda/ext/hdac_ext_stream.c index 0ede36f51e2e..492f85841ac0 100644 --- a/sound/hda/ext/hdac_ext_stream.c +++ b/sound/hda/ext/hdac_ext_stream.c @@ -489,7 +489,6 @@ void snd_hdac_ext_stop_streams(struct hdac_bus *bus) if (bus->chip_init) { list_for_each_entry(stream, &bus->stream_list, list) snd_hdac_stream_stop(stream); - snd_hdac_bus_stop_chip(bus); } } EXPORT_SYMBOL_GPL(snd_hdac_ext_stop_streams); diff --git a/sound/soc/intel/skylake/skl.c b/sound/soc/intel/skylake/skl.c index a0d41441a7a4..99f8cdf0cc07 100644 --- a/sound/soc/intel/skylake/skl.c +++ b/sound/soc/intel/skylake/skl.c @@ -1110,6 +1110,8 @@ static void skl_shutdown(struct pci_dev *pci) return; snd_hdac_ext_stop_streams(bus); + snd_hdac_ext_bus_link_power_down_all(bus); + skl_dsp_sleep(skl->skl_sst->dsp); /* While doing the warm reboot testing, some times dsp core is on * when system goes to shutdown. When cores.usage_count is * equal to zero then driver puts the dsp core to zero. On few From 03d6d8e5258fbb8e13ca639eede61ab12fd79c83 Mon Sep 17 00:00:00 2001 From: xiao jin Date: Thu, 12 Apr 2018 17:32:04 +0530 Subject: [PATCH 0853/1103] ASoC: soc-pcm: Fix FE and BE race when accessing substream->runtime After start of FE and BE, FE might close without triggering STOP, and substream->runtime gets freed. However, BE remains at START state and BE's substream->runtime still points to the freed runtime. Later if FE gets opened/started again, and triggers STOP, then skl_platform_pcm_trigger accesses the freed old runtime data. Fix is by assigning be_substream->runtime in dpcm_be_dai_startup when BE's state is START. Change-Id: If1fd0464a3c6c2a3e22c8b2af7ccc68c801e0e80 Signed-off-by: Mohit Sinha Signed-off-by: xiao jin Reviewed-on: Reviewed-by: Shaik, Kareem M Reviewed-by: Gogineni, GiribabuX Reviewed-by: Shaik, ShahinaX Reviewed-by: Kesapragada, Pardha Saradhi Reviewed-by: Kale, Sanyog R Tested-by: Madiwalar, MadiwalappaX --- sound/soc/soc-pcm.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/sound/soc/soc-pcm.c b/sound/soc/soc-pcm.c index e8b98bfd4cf1..afcb2cfaf551 100644 --- a/sound/soc/soc-pcm.c +++ b/sound/soc/soc-pcm.c @@ -1620,6 +1620,8 @@ int dpcm_be_dai_startup(struct snd_soc_pcm_runtime *fe, int stream) if (be->dpcm[stream].users++ != 0) continue; + be_substream->runtime = be->dpcm[stream].runtime; + if ((be->dpcm[stream].state != SND_SOC_DPCM_STATE_NEW) && (be->dpcm[stream].state != SND_SOC_DPCM_STATE_CLOSE)) continue; @@ -1627,7 +1629,6 @@ int dpcm_be_dai_startup(struct snd_soc_pcm_runtime *fe, int stream) dev_dbg(be->dev, "ASoC: open %s BE %s\n", stream ? "capture" : "playback", be->dai_link->name); - be_substream->runtime = be->dpcm[stream].runtime; err = soc_pcm_open(be_substream); if (err < 0) { dev_err(be->dev, "ASoC: BE open failed %d\n", err); From a8a2970147af0ea13f16583c90cfbe981ab2911a Mon Sep 17 00:00:00 2001 From: "Shaik, ShahinaX" Date: Tue, 15 May 2018 23:41:08 +0530 Subject: [PATCH 0854/1103] Revert "ASoC: tdf8532: Fix Audio memory leakage at boot time" This reverts commit c7083ded28e697dadc4cf0ef838220260e4a10f4. Change-Id: I77b671edc5c02c2ddab7cc834ee8626845df7489 Signed-off-by: Shaik, ShahinaX Reviewed-on: Reviewed-by: Tewani, Pradeep D Reviewed-by: Singh, Guneshwor O Reviewed-by: Kesapragada, Pardha Saradhi Reviewed-by: Gogineni, GiribabuX Reviewed-by: Kp, Jeeja Tested-by: Madiwalar, MadiwalappaX --- sound/soc/codecs/tdf8532.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/sound/soc/codecs/tdf8532.c b/sound/soc/codecs/tdf8532.c index 68decd023a9d..a5e2a028338c 100644 --- a/sound/soc/codecs/tdf8532.c +++ b/sound/soc/codecs/tdf8532.c @@ -165,8 +165,6 @@ static int tdf8532_get_state(struct tdf8532_priv *dev_data, *status_repl = (struct get_dev_status_repl *) repl_buff; out: - if (repl_buff) - kfree(repl_buff); return ret; } From 9dd59dcc40e12c2013639f1edbb516705da69b76 Mon Sep 17 00:00:00 2001 From: Liu Changcheng Date: Fri, 11 May 2018 17:24:01 +0800 Subject: [PATCH 0855/1103] ASoC: tdf8532: fix memleak in tdf8532_wait_state Fix kmemleak issue in tdf8532_wait_state function by releasing the memory getting allocated continuosly in instance of get_dev_status_repl i.e. status_repl before exiting the function. kernel memory leakage in audio stack/kmemleak backtrace: unreferenced object 0xffff88006227cc20 (size 32): comm "irq/25-snd_soc_", pid 2302, jiffies 4294679082 (age 5506.010s) hex dump (first 32 bytes): 02 00 03 80 80 02 00 00 00 00 00 00 00 00 00 00 ................ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ backtrace: [] kmemleak_alloc+0x4a/0xa0 [] __kmalloc+0x128/0x210 [] tdf8532_wait_state.constprop.5+0x116/0x260 [snd_soc_tdf8532] [] tdf8532_dai_trigger+0xab/0x15a [snd_soc_tdf8532] [] soc_pcm_trigger+0x75/0x130 [] dpcm_do_trigger.isra.6+0x29/0x90 [] dpcm_be_dai_trigger+0x18d/0x350 Change-Id: I550897d6b1efbd5ebbe15ab47038adf99581a82f Tracked-On: Signed-off-by: Liu Changcheng Reviewed-on: Reviewed-by: Shaik, ShahinaX Reviewed-by: Singh, Guneshwor O Reviewed-by: Gogineni, GiribabuX Reviewed-by: Tewani, Pradeep D Reviewed-by: Kesapragada, Pardha Saradhi Reviewed-by: Kp, Jeeja Tested-by: Madiwalar, MadiwalappaX --- sound/soc/codecs/tdf8532.c | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/sound/soc/codecs/tdf8532.c b/sound/soc/codecs/tdf8532.c index a5e2a028338c..dd4cebec5cf9 100644 --- a/sound/soc/codecs/tdf8532.c +++ b/sound/soc/codecs/tdf8532.c @@ -174,29 +174,31 @@ static int tdf8532_wait_state(struct tdf8532_priv *dev_data, u8 req_state, unsigned long timeout_point = jiffies + msecs_to_jiffies(timeout); int ret; struct get_dev_status_repl *status_repl = NULL; + u8 cur_state = STATE_NONE; struct device *dev = &(dev_data->i2c->dev); do { ret = tdf8532_get_state(dev_data, &status_repl); if (ret < 0) goto out; - + cur_state = status_repl->state; print_hex_dump_debug("tdf8532-codec: wait_state: ", DUMP_PREFIX_NONE, 32, 1, status_repl, 6, false); + + kfree(status_repl); + status_repl = NULL; } while (time_before(jiffies, timeout_point) - && status_repl->state != req_state); + && cur_state != req_state); - if (status_repl->state == req_state) + if (cur_state == req_state) return 0; +out: ret = -ETIME; dev_err(dev, "tdf8532-codec: state: %u, req_state: %u, ret: %d\n", - status_repl->state, req_state, ret); - -out: - kfree(status_repl); + cur_state, req_state, ret); return ret; } From e711cc83cfc8199ca3ed38e9b0617e725e361922 Mon Sep 17 00:00:00 2001 From: Liu Changcheng Date: Fri, 11 May 2018 17:11:42 +0800 Subject: [PATCH 0856/1103] ASoC: tdf8532: right free allocated space in case of error 1. Check allocated space before using it. 2. The repl_buff parameter in tdf8523_single_read is used to store the read data from i2c interface. When the data isn't right read, the pre-allocate space should be freed and the content of repl_buff should be set as NULL in case of being wrong used by the caller. 3. In the wrong case i.e. ret != len, return -EINVAL Change-Id: I3d0e12a9fcb6516716efc92eb734a0248ab3fb28 Tracked-On: Signed-off-by: Liu Changcheng Reviewed-on: Reviewed-by: Shaik, ShahinaX Reviewed-by: Kesapragada, Pardha Saradhi Reviewed-by: Gogineni, GiribabuX Reviewed-by: Singh, Guneshwor O Reviewed-by: Tewani, Pradeep D Reviewed-by: Kp, Jeeja Tested-by: Madiwalar, MadiwalappaX --- sound/soc/codecs/tdf8532.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/sound/soc/codecs/tdf8532.c b/sound/soc/codecs/tdf8532.c index dd4cebec5cf9..86b7430f4c88 100644 --- a/sound/soc/codecs/tdf8532.c +++ b/sound/soc/codecs/tdf8532.c @@ -107,11 +107,11 @@ static uint8_t tdf8532_read_wait_ack(struct tdf8532_priv *dev_data, return ret; } -static uint8_t tdf8532_single_read(struct tdf8532_priv *dev_data, +static int tdf8532_single_read(struct tdf8532_priv *dev_data, char **repl_buff) { int ret; - uint8_t len; + int len; struct device *dev = &(dev_data->i2c->dev); @@ -126,6 +126,10 @@ static uint8_t tdf8532_single_read(struct tdf8532_priv *dev_data, len = ret + HEADER_SIZE; *repl_buff = kzalloc(len, GFP_KERNEL); + if (*repl_buff == NULL) { + ret = -ENOMEM; + goto out; + } ret = i2c_master_recv(dev_data->i2c, *repl_buff, len); @@ -136,6 +140,8 @@ static uint8_t tdf8532_single_read(struct tdf8532_priv *dev_data, dev_err(dev, "i2c recv packet returned: %d (expected: %d)\n", ret, len); + + ret = -EINVAL; goto out_free; } @@ -143,7 +149,7 @@ static uint8_t tdf8532_single_read(struct tdf8532_priv *dev_data, out_free: kfree(*repl_buff); - repl_buff = NULL; + *repl_buff = NULL; out: return ret; } From 1d7b169345fedb819996cff682b29a2b17a318fd Mon Sep 17 00:00:00 2001 From: Puneeth Prabhu Date: Thu, 23 Nov 2017 20:52:05 +0530 Subject: [PATCH 0857/1103] ASoC: Intel: Skylake: Add kabylake R machine driver entry This patch adds acpi entry for kabylake R I2S machine driver, which makes use of ALC298 codec. Change-Id: Ie61f3c3e2759cd3a1b1380870307654e0d773ce7 Signed-off-by: Puneeth Prabhu Reviewed-on: Reviewed-by: Pawse, GuruprasadX Reviewed-by: Periyasamy, SriramX Reviewed-by: Kale, Sanyog R Tested-by: Madiwalar, MadiwalappaX --- sound/soc/intel/skylake/skl.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/sound/soc/intel/skylake/skl.c b/sound/soc/intel/skylake/skl.c index 99f8cdf0cc07..5a442ee5af64 100644 --- a/sound/soc/intel/skylake/skl.c +++ b/sound/soc/intel/skylake/skl.c @@ -1242,7 +1242,11 @@ static struct snd_soc_acpi_mach sst_bxtp_devdata[] = { static struct snd_soc_acpi_mach sst_kbl_devdata[] = { { .id = "INT343A", +#if IS_ENABLED(CONFIG_SND_SOC_INTEL_KBLR_RT298_MACH) + .drv_name = "kblr_alc298s_i2s", +#else .drv_name = "kbl_alc286s_i2s", +#endif .fw_filename = "intel/dsp_fw_kbl.bin", }, { From bca8f9ce99bf56556aecb84a9b46bbaa8f17a172 Mon Sep 17 00:00:00 2001 From: Puneeth Prabhu Date: Wed, 31 Jan 2018 12:31:27 +0530 Subject: [PATCH 0858/1103] ASoC: Intel: Boards: Add machine driver for Kabylake R This patch adds I2S machine driver for Kabylake R platform which makes use of ALC298 codec. Change-Id: I46b931b4f6f1c144c82bce8d03c3dafa635ac3d1 Signed-off-by: Puneeth Prabhu Reviewed-on: Reviewed-by: Kale, Sanyog R Tested-by: Madiwalar, MadiwalappaX --- sound/soc/intel/boards/Kconfig | 13 + sound/soc/intel/boards/Makefile | 2 + sound/soc/intel/boards/kblr_rt298.c | 549 ++++++++++++++++++++++++++++ 3 files changed, 564 insertions(+) create mode 100644 sound/soc/intel/boards/kblr_rt298.c diff --git a/sound/soc/intel/boards/Kconfig b/sound/soc/intel/boards/Kconfig index 45b1d5a03ed0..44eac7c32e9b 100644 --- a/sound/soc/intel/boards/Kconfig +++ b/sound/soc/intel/boards/Kconfig @@ -369,6 +369,19 @@ config SND_SOC_INTEL_BXT_ULL_MACH platform. Say Y or m if you have such a device. This is a recommended option. If unsure select "N". + +config SND_SOC_INTEL_KBLR_RT298_MACH + tristate "ASoC Audio driver for KBL-R with RT298 I2S mode" + depends on X86 && ACPI && I2C + depends on SND_SOC_INTEL_SKYLAKE + select SND_SOC_RT298 + select SND_SOC_DMIC + select SND_SOC_HDAC_HDMI + help + This adds support for ASoC machine driver for Kabylake-R platform + with RT298 I2S audio codec. + Say Y if you have such a device. + If unsure select "N". endif ## SND_SOC_INTEL_SKYLAKE endif ## SND_SOC_INTEL_MACH diff --git a/sound/soc/intel/boards/Makefile b/sound/soc/intel/boards/Makefile index 9a77052f0469..f255f7ab0960 100644 --- a/sound/soc/intel/boards/Makefile +++ b/sound/soc/intel/boards/Makefile @@ -29,6 +29,7 @@ snd-soc-cnl_cs42l42-objs := cnl_cs42l42.o snd-soc-cnl_rt700-objs := cnl_rt700.o snd-soc-cnl_svfpga-objs := cnl_svfpga.o snd-soc-bxt_ivi_ull-objs := bxt_ivi_ull.o +snd-soc-kblr_rt298-objs := kblr_rt298.o obj-$(CONFIG_SND_SOC_INTEL_HASWELL_MACH) += snd-soc-sst-haswell.o obj-$(CONFIG_SND_SOC_INTEL_BYT_RT5640_MACH) += snd-soc-sst-byt-rt5640-mach.o @@ -60,3 +61,4 @@ obj-$(CONFIG_SND_SOC_INTEL_CNL_CS42L42_MACH) += snd-soc-cnl_cs42l42.o obj-$(CONFIG_SND_SOC_INTEL_CNL_RT700_MACH) += snd-soc-cnl_rt700.o obj-$(CONFIG_SND_SOC_INTEL_CNL_SVFPGA_MACH) += snd-soc-cnl_svfpga.o obj-$(CONFIG_SND_SOC_INTEL_BXT_ULL_MACH) += snd-soc-bxt_ivi_ull.o +obj-$(CONFIG_SND_SOC_INTEL_KBLR_RT298_MACH) += snd-soc-kblr_rt298.o diff --git a/sound/soc/intel/boards/kblr_rt298.c b/sound/soc/intel/boards/kblr_rt298.c new file mode 100644 index 000000000000..1007e0239bb9 --- /dev/null +++ b/sound/soc/intel/boards/kblr_rt298.c @@ -0,0 +1,549 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright(c) 2017-18 Intel Corporation + +/* + * kblr_rt298.c -Intel Kabylake-R I2S Machine Driver + */ + +#include +#include +#include +#include +#include +#include +#include +#include "../../codecs/rt298.h" +#include "../../codecs/hdac_hdmi.h" + +static struct snd_soc_jack kabylake_headset; +static struct snd_soc_jack kabylake_hdmi[3]; + +struct kbl_hdmi_pcm { + struct list_head head; + struct snd_soc_dai *codec_dai; + int device; +}; + +struct kbl_rt298_private { + struct list_head hdmi_pcm_list; +}; + +enum { + SKL_DPCM_AUDIO_PB = 0, + SKL_DPCM_AUDIO_DB_PB, + SKL_DPCM_AUDIO_CP, + SKL_DPCM_AUDIO_REF_CP, + SKL_DPCM_AUDIO_DMIC_CP, + SKL_DPCM_AUDIO_HDMI1_PB, + SKL_DPCM_AUDIO_HDMI2_PB, + SKL_DPCM_AUDIO_HDMI3_PB, +}; + +/* Headset jack detection DAPM pins */ +static struct snd_soc_jack_pin kabylake_headset_pins[] = { + { + .pin = "Mic Jack", + .mask = SND_JACK_MICROPHONE, + }, + { + .pin = "Headphone Jack", + .mask = SND_JACK_HEADPHONE, + }, +}; + +static const struct snd_kcontrol_new kabylake_controls[] = { + SOC_DAPM_PIN_SWITCH("Speaker"), + SOC_DAPM_PIN_SWITCH("Headphone Jack"), + SOC_DAPM_PIN_SWITCH("Mic Jack"), +}; + +static const struct snd_soc_dapm_widget kabylake_widgets[] = { + SND_SOC_DAPM_HP("Headphone Jack", NULL), + SND_SOC_DAPM_SPK("Speaker", NULL), + SND_SOC_DAPM_MIC("Mic Jack", NULL), + SND_SOC_DAPM_MIC("DMIC2", NULL), + SND_SOC_DAPM_MIC("SoC DMIC", NULL), + SND_SOC_DAPM_SPK("HDMI1", NULL), + SND_SOC_DAPM_SPK("HDMI2", NULL), + SND_SOC_DAPM_SPK("HDMI3", NULL), +}; + +static const struct snd_soc_dapm_route kabylake_rt298_map[] = { + /* speaker */ + {"Speaker", NULL, "SPOR"}, + {"Speaker", NULL, "SPOL"}, + + /* HP jack connectors - unknown if we have jack detect */ + {"Headphone Jack", NULL, "HPO Pin"}, + + /* other jacks */ + {"MIC1", NULL, "Mic Jack"}, + + /* digital mics */ + {"DMIC1 Pin", NULL, "DMIC2"}, + {"DMic", NULL, "SoC DMIC"}, + + /* CODEC BE connections */ + { "AIF1 Playback", NULL, "ssp0 Tx"}, + { "ssp0 Tx", NULL, "codec0_out"}, + { "ssp0 Tx", NULL, "codec1_out"}, + + { "codec0_in", NULL, "ssp0 Rx" }, + { "codec1_in", NULL, "ssp0 Rx" }, + { "ssp0 Rx", NULL, "AIF1 Capture" }, + + { "dmic01_hifi", NULL, "DMIC01 Rx" }, + { "DMIC01 Rx", NULL, "DMIC AIF" }, + + { "hifi3", NULL, "iDisp3 Tx"}, + { "iDisp3 Tx", NULL, "iDisp3_out"}, + { "hifi2", NULL, "iDisp2 Tx"}, + { "iDisp2 Tx", NULL, "iDisp2_out"}, + { "hifi1", NULL, "iDisp1 Tx"}, + { "iDisp1 Tx", NULL, "iDisp1_out"}, + +}; + +static int kabylake_rt298_fe_init(struct snd_soc_pcm_runtime *rtd) +{ + struct snd_soc_dapm_context *dapm; + struct snd_soc_component *component = rtd->cpu_dai->component; + + dapm = snd_soc_component_get_dapm(component); + snd_soc_dapm_ignore_suspend(dapm, "Reference Capture"); + + return 0; +} + +static int kabylake_rt298_codec_init(struct snd_soc_pcm_runtime *rtd) +{ + struct snd_soc_component *codec = rtd->codec_dai->component; + int ret; + + ret = snd_soc_card_jack_new(rtd->card, "Headset", + SND_JACK_HEADSET | SND_JACK_BTN_0, + &kabylake_headset, + kabylake_headset_pins, ARRAY_SIZE(kabylake_headset_pins)); + + if (ret) + return ret; + + rt298_mic_detect(codec, &kabylake_headset); + snd_soc_dapm_ignore_suspend(&rtd->card->dapm, "SoC DMIC"); + + return 0; +} + +static int kabylake_hdmi_init(struct snd_soc_pcm_runtime *rtd) +{ + struct kbl_rt298_private *ctx = snd_soc_card_get_drvdata(rtd->card); + struct snd_soc_dai *dai = rtd->codec_dai; + struct kbl_hdmi_pcm *pcm; + + pcm = devm_kzalloc(rtd->card->dev, sizeof(*pcm), GFP_KERNEL); + if (!pcm) + return -ENOMEM; + + pcm->device = SKL_DPCM_AUDIO_HDMI1_PB + dai->id; + pcm->codec_dai = dai; + + list_add_tail(&pcm->head, &ctx->hdmi_pcm_list); + + return 0; +} + +static const unsigned int rates[] = { + 48000, +}; + +static const struct snd_pcm_hw_constraint_list constraints_rates = { + .count = ARRAY_SIZE(rates), + .list = rates, + .mask = 0, +}; + +static const unsigned int channels[] = { + 2, +}; + +static const struct snd_pcm_hw_constraint_list constraints_channels = { + .count = ARRAY_SIZE(channels), + .list = channels, + .mask = 0, +}; + +static int kbl_fe_startup(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + + /* + * on this platform for PCM device we support, + * 48Khz + * stereo + * 16 bit audio + */ + + runtime->hw.channels_max = 2; + snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, + &constraints_channels); + + runtime->hw.formats = SNDRV_PCM_FMTBIT_S16_LE; + snd_pcm_hw_constraint_msbits(runtime, 0, 16, 16); + + snd_pcm_hw_constraint_list(runtime, 0, + SNDRV_PCM_HW_PARAM_RATE, &constraints_rates); + + return 0; +} + +static const struct snd_soc_ops kabylake_rt298_fe_ops = { + .startup = kbl_fe_startup, +}; + +static int kabylake_ssp0_fixup(struct snd_soc_pcm_runtime *rtd, + struct snd_pcm_hw_params *params) +{ + struct snd_interval *rate = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_RATE); + struct snd_interval *channels = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_CHANNELS); + struct snd_mask *fmt = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT); + + /* The output is 48KHz, stereo, 16bits */ + rate->min = rate->max = 48000; + channels->min = channels->max = 2; + + /* set SSP0 to 24 bit */ + snd_mask_none(fmt); + snd_mask_set(fmt, SNDRV_PCM_FORMAT_S24_LE); + return 0; +} + +static int kabylake_rt298_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *codec_dai = rtd->codec_dai; + int ret; + + ret = snd_soc_dai_set_sysclk(codec_dai, RT298_SCLK_S_PLL, 24000000, + SND_SOC_CLOCK_IN); + if (ret < 0) + dev_err(rtd->dev, "set codec sysclk failed: %d\n", ret); + + return ret; +} + +static const struct snd_soc_ops kabylake_rt298_ops = { + .hw_params = kabylake_rt298_hw_params, +}; + +static int kabylake_dmic_fixup(struct snd_soc_pcm_runtime *rtd, + struct snd_pcm_hw_params *params) +{ + struct snd_interval *channels = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_CHANNELS); + if (params_channels(params) == 2) + channels->min = channels->max = 2; + else + channels->min = channels->max = 4; + + return 0; +} + +static const unsigned int channels_dmic[] = { + 2, 4, +}; + +static const struct snd_pcm_hw_constraint_list constraints_dmic_channels = { + .count = ARRAY_SIZE(channels_dmic), + .list = channels_dmic, + .mask = 0, +}; + +static int kabylake_dmic_startup(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + + runtime->hw.channels_max = 4; + snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, + &constraints_dmic_channels); + + return snd_pcm_hw_constraint_list(substream->runtime, 0, + SNDRV_PCM_HW_PARAM_RATE, &constraints_rates); +} + +static const struct snd_soc_ops kabylake_dmic_ops = { + .startup = kabylake_dmic_startup, +}; + +/* kabylake digital audio interface glue - connects codec <--> CPU */ +static struct snd_soc_dai_link kabylake_rt298_dais[] = { + /* Front End DAI links */ + [SKL_DPCM_AUDIO_PB] = { + .name = "Skl Audio Port", + .stream_name = "Audio", + .cpu_dai_name = "System Pin", + .platform_name = "0000:00:1f.3", + .nonatomic = 1, + .dynamic = 1, + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .init = kabylake_rt298_fe_init, + .trigger = { + SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST + }, + .dpcm_playback = 1, + .ops = &kabylake_rt298_fe_ops, + }, + [SKL_DPCM_AUDIO_DB_PB] = { + .name = "Skl Deepbuffer Port", + .stream_name = "Deep Buffer Audio", + .cpu_dai_name = "Deepbuffer Pin", + .platform_name = "0000:00:1f.3", + .nonatomic = 1, + .dynamic = 1, + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .trigger = { + SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST + }, + .dpcm_playback = 1, + .ops = &kabylake_rt298_fe_ops, + + }, + [SKL_DPCM_AUDIO_CP] = { + .name = "Skl Audio Capture Port", + .stream_name = "Audio Record", + .cpu_dai_name = "System Pin", + .platform_name = "0000:00:1f.3", + .nonatomic = 1, + .dynamic = 1, + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .trigger = { + SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST + }, + .dpcm_capture = 1, + .ops = &kabylake_rt298_fe_ops, + }, + [SKL_DPCM_AUDIO_REF_CP] = { + .name = "Skl Audio Reference cap", + .stream_name = "refcap", + .cpu_dai_name = "Reference Pin", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .platform_name = "0000:00:1f.3", + .init = NULL, + .dpcm_capture = 1, + .nonatomic = 1, + .dynamic = 1, + }, + [SKL_DPCM_AUDIO_DMIC_CP] = { + .name = "Skl Audio DMIC cap", + .stream_name = "dmiccap", + .cpu_dai_name = "DMIC Pin", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .platform_name = "0000:00:1f.3", + .init = NULL, + .dpcm_capture = 1, + .nonatomic = 1, + .dynamic = 1, + .ops = &kabylake_dmic_ops, + }, + [SKL_DPCM_AUDIO_HDMI1_PB] = { + .name = "Skl HDMI Port1", + .stream_name = "Hdmi1", + .cpu_dai_name = "HDMI1 Pin", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .platform_name = "0000:00:1f.3", + .dpcm_playback = 1, + .init = NULL, + .nonatomic = 1, + .dynamic = 1, + }, + [SKL_DPCM_AUDIO_HDMI2_PB] = { + .name = "Skl HDMI Port2", + .stream_name = "Hdmi2", + .cpu_dai_name = "HDMI2 Pin", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .platform_name = "0000:00:1f.3", + .dpcm_playback = 1, + .init = NULL, + .nonatomic = 1, + .dynamic = 1, + }, + [SKL_DPCM_AUDIO_HDMI3_PB] = { + .name = "Skl HDMI Port3", + .stream_name = "Hdmi3", + .cpu_dai_name = "HDMI3 Pin", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .platform_name = "0000:00:1f.3", + .dpcm_playback = 1, + .init = NULL, + .nonatomic = 1, + .dynamic = 1, + }, + + /* Back End DAI links */ + { + /* SSP0 - Codec */ + .name = "SSP0-Codec", + .id = 0, + .cpu_dai_name = "SSP0 Pin", + .platform_name = "0000:00:1f.3", + .no_pcm = 1, + .codec_name = "i2c-INT343A:00", + .codec_dai_name = "rt298-aif1", + .init = kabylake_rt298_codec_init, + .dai_fmt = SND_SOC_DAIFMT_DSP_A | + SND_SOC_DAIFMT_NB_NF | + SND_SOC_DAIFMT_CBS_CFS, + .ignore_pmdown_time = 1, + .be_hw_params_fixup = kabylake_ssp0_fixup, + .ops = &kabylake_rt298_ops, + .dpcm_playback = 1, + .dpcm_capture = 1, + }, + { + .name = "dmic01", + .id = 1, + .cpu_dai_name = "DMIC01 Pin", + .codec_name = "dmic-codec", + .codec_dai_name = "dmic-hifi", + .platform_name = "0000:00:1f.3", + .be_hw_params_fixup = kabylake_dmic_fixup, + .ignore_suspend = 1, + .dpcm_capture = 1, + .no_pcm = 1, + }, + { + .name = "iDisp1", + .id = 2, + .cpu_dai_name = "iDisp1 Pin", + .codec_name = "ehdaudio0D2", + .codec_dai_name = "intel-hdmi-hifi1", + .platform_name = "0000:00:1f.3", + .init = kabylake_hdmi_init, + .dpcm_playback = 1, + .no_pcm = 1, + }, + { + .name = "iDisp2", + .id = 3, + .cpu_dai_name = "iDisp2 Pin", + .codec_name = "ehdaudio0D2", + .codec_dai_name = "intel-hdmi-hifi2", + .platform_name = "0000:00:1f.3", + .init = kabylake_hdmi_init, + .dpcm_playback = 1, + .no_pcm = 1, + }, + { + .name = "iDisp3", + .id = 4, + .cpu_dai_name = "iDisp3 Pin", + .codec_name = "ehdaudio0D2", + .codec_dai_name = "intel-hdmi-hifi3", + .platform_name = "0000:00:1f.3", + .init = kabylake_hdmi_init, + .dpcm_playback = 1, + .no_pcm = 1, + }, +}; + +#define NAME_SIZE 32 +static int kabylake_card_late_probe(struct snd_soc_card *card) +{ + struct kbl_rt298_private *ctx = snd_soc_card_get_drvdata(card); + struct kbl_hdmi_pcm *pcm; + struct snd_soc_component *codec = NULL; + int err, i = 0; + char jack_name[NAME_SIZE]; + + list_for_each_entry(pcm, &ctx->hdmi_pcm_list, head) { + codec = pcm->codec_dai->component; + snprintf(jack_name, sizeof(jack_name), + "HDMI/DP, pcm=%d Jack", pcm->device); + err = snd_soc_card_jack_new(card, jack_name, + SND_JACK_AVOUT, &kabylake_hdmi[i], + NULL, 0); + + if (err) + return err; + + err = hdac_hdmi_jack_init(pcm->codec_dai, pcm->device, + &kabylake_hdmi[i]); + if (err < 0) + return err; + + i++; + } + + if (!codec) + return -EINVAL; + + return hdac_hdmi_jack_port_init(codec, &card->dapm); +} + +/* kabylake audio machine driver for SPT + RT298S */ +static struct snd_soc_card kabylake_rt298 = { + .name = "kabylake-rt298", + .owner = THIS_MODULE, + .dai_link = kabylake_rt298_dais, + .num_links = ARRAY_SIZE(kabylake_rt298_dais), + .controls = kabylake_controls, + .num_controls = ARRAY_SIZE(kabylake_controls), + .dapm_widgets = kabylake_widgets, + .num_dapm_widgets = ARRAY_SIZE(kabylake_widgets), + .dapm_routes = kabylake_rt298_map, + .num_dapm_routes = ARRAY_SIZE(kabylake_rt298_map), + .fully_routed = true, + .late_probe = kabylake_card_late_probe, +}; + +static int kabylake_audio_probe(struct platform_device *pdev) +{ + struct kbl_rt298_private *ctx; + ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_ATOMIC); + if (!ctx) + return -ENOMEM; + + INIT_LIST_HEAD(&ctx->hdmi_pcm_list); + + kabylake_rt298.dev = &pdev->dev; + snd_soc_card_set_drvdata(&kabylake_rt298, ctx); + + return devm_snd_soc_register_card(&pdev->dev, &kabylake_rt298); +} + +static const struct platform_device_id kbl_board_ids[] = { + { .name = "kblr_alc298s_i2s" }, + { } +}; + +static struct platform_driver kabylake_audio = { + .probe = kabylake_audio_probe, + .driver = { + .name = "kblr_alc298s_i2s", + .pm = &snd_soc_pm_ops, + }, + .id_table = kbl_board_ids, + +}; + +module_platform_driver(kabylake_audio) + +/* Module information */ +MODULE_AUTHOR("Omair Mohammed Abdullah "); +MODULE_AUTHOR("Puneeth Prabhu "); +MODULE_DESCRIPTION("Intel SST Audio for Kabylake"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:kblr_alc298s_i2s"); From 4b2839a813e7f023c8179e67d8cdfe539812a2b1 Mon Sep 17 00:00:00 2001 From: Puneeth Prabhu Date: Fri, 12 Jan 2018 15:50:50 +0530 Subject: [PATCH 0859/1103] ASoC: rt298: Set jack combo for kabylake R This patch adds DMI information of Kabylake R to force_combo_jack_table[]. Change-Id: Ibf29d8eb5d3ded179aa76a31da5fbf8ad5d99d36 Signed-off-by: Puneeth Prabhu Reviewed-on: Reviewed-by: Singh, Guneshwor O Reviewed-by: Pawse, GuruprasadX Reviewed-by: Periyasamy, SriramX Reviewed-by: Kale, Sanyog R Tested-by: Madiwalar, MadiwalappaX --- sound/soc/codecs/rt298.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/sound/soc/codecs/rt298.c b/sound/soc/codecs/rt298.c index 06cdba4edfe2..47f7d14f1413 100644 --- a/sound/soc/codecs/rt298.c +++ b/sound/soc/codecs/rt298.c @@ -1169,6 +1169,13 @@ static const struct dmi_system_id force_combo_jack_table[] = { DMI_MATCH(DMI_PRODUCT_NAME, "Geminilake") } }, + { + .ident = "Intel Kabylake R RVP", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Intel Corp"), + DMI_MATCH(DMI_PRODUCT_NAME, "Kabylake Client platform") + } + }, { } }; From da44bfb28faf22270de4d37cacc72dfb89850be3 Mon Sep 17 00:00:00 2001 From: Puneeth Prabhu Date: Wed, 2 May 2018 12:55:39 +0530 Subject: [PATCH 0860/1103] ASoC: Intel: Boards: Add machine driver for RSE topology This patch adds I2S machine driver for IVI RSE topology which uses dummy codec. Change-Id: I8152c3ec7d6057f341412a0a8179283dab247fa2 Signed-off-by: Puneeth Prabhu Reviewed-on: Reviewed-by: Kale, Sanyog R Tested-by: Madiwalar, MadiwalappaX --- sound/soc/intel/boards/Kconfig | 10 + sound/soc/intel/boards/Makefile | 2 + sound/soc/intel/boards/bxtp_ivi_rse_rt298.c | 359 ++++++++++++++++++++ 3 files changed, 371 insertions(+) create mode 100644 sound/soc/intel/boards/bxtp_ivi_rse_rt298.c diff --git a/sound/soc/intel/boards/Kconfig b/sound/soc/intel/boards/Kconfig index 44eac7c32e9b..c807cc465c47 100644 --- a/sound/soc/intel/boards/Kconfig +++ b/sound/soc/intel/boards/Kconfig @@ -382,6 +382,16 @@ config SND_SOC_INTEL_KBLR_RT298_MACH with RT298 I2S audio codec. Say Y if you have such a device. If unsure select "N". + +config SND_SOC_INTEL_BXTP_IVI_RSE_MACH + tristate "ASoC Audio driver for BXTP IVI RSE with Dummy Codecs" + depends on MFD_INTEL_LPSS && I2C && ACPI + help + This adds support for ASoC machine driver for Broxton-P platforms + with Dummy I2S audio codec for IVI Rear Seat Unit. + Say Y or m if you have such a device. This is a recommended option. + If unsure select "N". + endif ## SND_SOC_INTEL_SKYLAKE endif ## SND_SOC_INTEL_MACH diff --git a/sound/soc/intel/boards/Makefile b/sound/soc/intel/boards/Makefile index f255f7ab0960..683737d503dc 100644 --- a/sound/soc/intel/boards/Makefile +++ b/sound/soc/intel/boards/Makefile @@ -30,6 +30,7 @@ snd-soc-cnl_rt700-objs := cnl_rt700.o snd-soc-cnl_svfpga-objs := cnl_svfpga.o snd-soc-bxt_ivi_ull-objs := bxt_ivi_ull.o snd-soc-kblr_rt298-objs := kblr_rt298.o +snd-soc-bxtp_ivi_rse_rt298-objs := bxtp_ivi_rse_rt298.o obj-$(CONFIG_SND_SOC_INTEL_HASWELL_MACH) += snd-soc-sst-haswell.o obj-$(CONFIG_SND_SOC_INTEL_BYT_RT5640_MACH) += snd-soc-sst-byt-rt5640-mach.o @@ -62,3 +63,4 @@ obj-$(CONFIG_SND_SOC_INTEL_CNL_RT700_MACH) += snd-soc-cnl_rt700.o obj-$(CONFIG_SND_SOC_INTEL_CNL_SVFPGA_MACH) += snd-soc-cnl_svfpga.o obj-$(CONFIG_SND_SOC_INTEL_BXT_ULL_MACH) += snd-soc-bxt_ivi_ull.o obj-$(CONFIG_SND_SOC_INTEL_KBLR_RT298_MACH) += snd-soc-kblr_rt298.o +obj-$(CONFIG_SND_SOC_INTEL_BXTP_IVI_RSE_MACH) += snd-soc-bxtp_ivi_rse_rt298.o diff --git a/sound/soc/intel/boards/bxtp_ivi_rse_rt298.c b/sound/soc/intel/boards/bxtp_ivi_rse_rt298.c new file mode 100644 index 000000000000..25917c4a39d2 --- /dev/null +++ b/sound/soc/intel/boards/bxtp_ivi_rse_rt298.c @@ -0,0 +1,359 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright(c) 2018 Intel Corporation + +/* + * bxtp_ivi_rse_rt298.c -Intel RSE I2S Machine Driver + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static const struct snd_soc_dapm_widget broxton_widgets[] = { + SND_SOC_DAPM_SPK("Speaker", NULL), + SND_SOC_DAPM_MIC("DMIC2", NULL), +}; + +static const struct snd_soc_dapm_route broxton_rt298_map[] = { + {"Speaker", NULL, "Dummy Playback"}, + {"Dummy Capture", NULL, "DMIC2"}, + /* BE connections */ + { "Dummy Playback", NULL, "ssp4 Tx"}, + { "ssp4 Tx", NULL, "codec0_out"}, + { "Dummy Playback", NULL, "ssp2 Tx"}, + { "ssp2 Tx", NULL, "codec1_out"}, + { "Dummy Playback", NULL, "ssp1 Tx"}, + { "ssp1 Tx", NULL, "codec2_out"}, + { "Dummy Playback", NULL, "ssp1 Tx"}, + { "ssp1 Tx", NULL, "codec3_out"}, + { "hdmi_ssp0_in", NULL, "ssp0 Rx" }, + { "ssp0 Rx", NULL, "Dummy Capture" }, + /* Test connections */ + { "Dummy Playback", NULL, "ssp3 Tx"}, + { "ssp3 Tx", NULL, "TestSSP3_out"}, + { "TestSSP3_in", NULL, "ssp3 Rx" }, + { "ssp3 Rx", NULL, "Dummy Capture" }, +}; + +static int bxtp_ssp0_gpio_init(struct snd_soc_pcm_runtime *rtd) +{ + char *gpio_addr; + u32 gpio_value1 = 0x40900500; + u32 gpio_value2 = 0x44000600; + + gpio_addr = (void *)ioremap_nocache(0xd0c40610, 0x30); + if (gpio_addr == NULL) + return(-EIO); + + memcpy_toio(gpio_addr + 0x8, &gpio_value1, sizeof(gpio_value1)); + memcpy_toio(gpio_addr + 0x10, &gpio_value2, sizeof(gpio_value2)); + memcpy_toio(gpio_addr + 0x18, &gpio_value2, sizeof(gpio_value2)); + memcpy_toio(gpio_addr + 0x20, &gpio_value2, sizeof(gpio_value2)); + + iounmap(gpio_addr); + return 0; +} + +static int bxtp_ssp1_gpio_init(struct snd_soc_pcm_runtime *rtd) +{ + + char *gpio_addr; + u32 gpio_value1 = 0x44000400; + + gpio_addr = (void *)ioremap_nocache(0xd0c40660, 0x30); + if (gpio_addr == NULL) + return(-EIO); + + memcpy_toio(gpio_addr + 0x8, &gpio_value1, sizeof(gpio_value1)); + memcpy_toio(gpio_addr + 0x10, &gpio_value1, sizeof(gpio_value1)); + memcpy_toio(gpio_addr + 0x18, &gpio_value1, sizeof(gpio_value1)); + memcpy_toio(gpio_addr + 0x20, &gpio_value1, sizeof(gpio_value1)); + + iounmap(gpio_addr); + return 0; +} + +static int bxtp_ssp4_gpio_init(struct snd_soc_pcm_runtime *rtd) +{ + + char *gpio_addr; + u32 gpio_value1 = 0x44000A00; + u32 gpio_value2 = 0x44000800; + + gpio_addr = (void *)ioremap_nocache(0xd0c705A0, 0x30); + if (gpio_addr == NULL) + return(-EIO); + + memcpy_toio(gpio_addr, &gpio_value1, sizeof(gpio_value1)); + memcpy_toio(gpio_addr + 0x8, &gpio_value1, sizeof(gpio_value1)); + memcpy_toio(gpio_addr + 0x10, &gpio_value1, sizeof(gpio_value1)); + memcpy_toio(gpio_addr + 0x18, &gpio_value2, sizeof(gpio_value2)); + + iounmap(gpio_addr); + return 0; + +} + +static int bxtp_ssp3_gpio_init(struct snd_soc_pcm_runtime *rtd) +{ + + char *gpio_addr; + u32 gpio_value1 = 0x44000800; + u32 gpio_value2 = 0x44000802; + + gpio_addr = (void *)ioremap_nocache(0xd0c40638, 0x30); + if (gpio_addr == NULL) + return(-EIO); + + memcpy_toio(gpio_addr, &gpio_value1, sizeof(gpio_value1)); + memcpy_toio(gpio_addr + 0x8, &gpio_value2, sizeof(gpio_value2)); + memcpy_toio(gpio_addr + 0x10, &gpio_value1, sizeof(gpio_value1)); + memcpy_toio(gpio_addr + 0x18, &gpio_value1, sizeof(gpio_value1)); + + iounmap(gpio_addr); + return 0; +} + +static int broxton_ssp1_fixup(struct snd_soc_pcm_runtime *rtd, + struct snd_pcm_hw_params *params) +{ + struct snd_interval *rate = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_RATE); + struct snd_interval *channels = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_CHANNELS); + + /* The ADSP will covert the FE rate to 48k, 2 Channel */ + rate->min = rate->max = 48000; + channels->min = channels->max = 2; + + /* set SSP1 to 16 bit */ + snd_mask_set(¶ms->masks[SNDRV_PCM_HW_PARAM_FORMAT - + SNDRV_PCM_HW_PARAM_FIRST_MASK], + SNDRV_PCM_FORMAT_S16_LE); + return 0; +} + +static int broxton_ssp2_fixup(struct snd_soc_pcm_runtime *rtd, + struct snd_pcm_hw_params *params) +{ + struct snd_interval *rate = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_RATE); + struct snd_interval *channels = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_CHANNELS); + + /* The ADSP will covert the FE rate to 44k, stereo */ + rate->min = rate->max = 44100; + channels->min = channels->max = 2; + + /* set SSP2 to 16 bit */ + snd_mask_set(¶ms->masks[SNDRV_PCM_HW_PARAM_FORMAT - + SNDRV_PCM_HW_PARAM_FIRST_MASK], + SNDRV_PCM_FORMAT_S16_LE); + return 0; +} + +static int broxton_ssp4_fixup(struct snd_soc_pcm_runtime *rtd, + struct snd_pcm_hw_params *params) +{ + struct snd_interval *rate = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_RATE); + struct snd_interval *channels = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_CHANNELS); + + /* The ADSP will covert the FE rate to 44k, stereo */ + rate->min = rate->max = 44100; + channels->min = channels->max = 2; + + /* set SSP4 to 16 bit */ + snd_mask_set(¶ms->masks[SNDRV_PCM_HW_PARAM_FORMAT - + SNDRV_PCM_HW_PARAM_FIRST_MASK], + SNDRV_PCM_FORMAT_S16_LE); + return 0; +} + +static const char pname[] = "0000:00:0e.0"; + +struct snd_soc_dai_link broxton_rt298_dais[] = { + /* Trace Buffer DAI links */ + { + .name = "Bxt Trace Buffer0", + .stream_name = "Core 0 Trace Buffer", + .cpu_dai_name = "TraceBuffer0 Pin", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .platform_name = pname, + .capture_only = true, + .ignore_suspend = 1, + }, + { + .name = "Bxt Trace Buffer1", + .stream_name = "Core 1 Trace Buffer", + .cpu_dai_name = "TraceBuffer1 Pin", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .platform_name = pname, + .capture_only = true, + .ignore_suspend = 1, + }, + { + .name = "Bxt Compress Probe playback", + .stream_name = "Probe Playback", + .cpu_dai_name = "Compress Probe0 Pin", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .platform_name = pname, + .init = NULL, + .nonatomic = 1, + }, + { + .name = "Bxt Compress Probe capture", + .stream_name = "Probe Capture", + .cpu_dai_name = "Compress Probe1 Pin", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .platform_name = pname, + .init = NULL, + .nonatomic = 1, + }, + /* Back End DAI links */ + { .name = "SSP0-Codec", + .id = 1, + .cpu_dai_name = "SSP0 Pin", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .platform_name = pname, + .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | + SND_SOC_DAIFMT_CBS_CFS, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + .no_pcm = 1, + .init = bxtp_ssp0_gpio_init, + .dpcm_capture = 1, + }, + { + .name = "SSP1-Codec", + .id = 2, + .cpu_dai_name = "SSP1 Pin", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .platform_name = pname, + .be_hw_params_fixup = broxton_ssp1_fixup, + .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | + SND_SOC_DAIFMT_CBS_CFS, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + .no_pcm = 1, + .init = bxtp_ssp1_gpio_init, + .dpcm_playback = 1, + }, + { + .name = "SSP2-Codec", + .id = 3, + .cpu_dai_name = "SSP2 Pin", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .platform_name = pname, + .be_hw_params_fixup = broxton_ssp2_fixup, + .ignore_suspend = 1, + .no_pcm = 1, + .init = NULL, + .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | + SND_SOC_DAIFMT_CBS_CFS, + .dpcm_playback = 1, + }, + { + .name = "SSP3-Codec", + .id = 4, + .cpu_dai_name = "SSP3 Pin", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .platform_name = pname, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + .no_pcm = 1, + .init = bxtp_ssp3_gpio_init, + .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | + SND_SOC_DAIFMT_CBS_CFS, + .dpcm_playback = 1, + .dpcm_capture = 1, + }, + { + .name = "SSP4-Codec", + .id = 5, + .cpu_dai_name = "SSP4 Pin", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .platform_name = pname, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + .no_pcm = 1, + .init = bxtp_ssp4_gpio_init, + .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | + SND_SOC_DAIFMT_CBS_CFS, + .be_hw_params_fixup = broxton_ssp4_fixup, + .dpcm_playback = 1, + }, +}; + +static int +bxt_add_dai_link(struct snd_soc_card *card, struct snd_soc_dai_link *link) +{ + link->platform_name = pname; + link->nonatomic = 1; + + return 0; +} + +/* SoC card */ +static struct snd_soc_card broxton_rt298 = { + .name = "broxton-ivi-rse", + .dai_link = broxton_rt298_dais, + .num_links = ARRAY_SIZE(broxton_rt298_dais), + .dapm_widgets = broxton_widgets, + .num_dapm_widgets = ARRAY_SIZE(broxton_widgets), + .dapm_routes = broxton_rt298_map, + .num_dapm_routes = ARRAY_SIZE(broxton_rt298_map), + .controls = NULL, + .num_controls = 0, + .fully_routed = true, + .add_dai_link = bxt_add_dai_link, +}; + +static int broxton_audio_probe(struct platform_device *pdev) +{ + broxton_rt298.dev = &pdev->dev; + return snd_soc_register_card(&broxton_rt298); +} + +static int broxton_audio_remove(struct platform_device *pdev) +{ + snd_soc_unregister_card(&broxton_rt298); + return 0; +} + +static struct platform_driver broxton_audio = { + .probe = broxton_audio_probe, + .remove = broxton_audio_remove, + .driver = { + .name = "bxt_ivi_rse_i2s", + }, +}; + +module_platform_driver(broxton_audio); + +/* Module information */ +MODULE_AUTHOR("Pardha Saradhi K "); +MODULE_AUTHOR("Ramesh Babu "); +MODULE_AUTHOR("Senthilnathan Veppur "); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:bxt_ivi_rse_i2s"); From 25e32ad99abca74d80b03f3d01e728960cb704b5 Mon Sep 17 00:00:00 2001 From: Puneeth Prabhu Date: Mon, 7 May 2018 17:22:32 +0530 Subject: [PATCH 0861/1103] ASoC: Intel: Boards: Add machine driver for HU topology This patch adds I2S machine driver for IVI HU topology which uses dummy codec. Change-Id: Iff3afa704d23bc7a479265bf1139db5af579b258 Signed-off-by: Puneeth Prabhu Reviewed-on: Reviewed-by: Singh, Guneshwor O Reviewed-by: Kesapragada, Pardha Saradhi Reviewed-by: Kale, Sanyog R Tested-by: Madiwalar, MadiwalappaX --- sound/soc/intel/boards/Kconfig | 9 + sound/soc/intel/boards/Makefile | 2 + sound/soc/intel/boards/bxtp_ivi_hu_rt298.c | 250 +++++++++++++++++++++ 3 files changed, 261 insertions(+) create mode 100644 sound/soc/intel/boards/bxtp_ivi_hu_rt298.c diff --git a/sound/soc/intel/boards/Kconfig b/sound/soc/intel/boards/Kconfig index c807cc465c47..d98f82cf4a3c 100644 --- a/sound/soc/intel/boards/Kconfig +++ b/sound/soc/intel/boards/Kconfig @@ -392,6 +392,15 @@ config SND_SOC_INTEL_BXTP_IVI_RSE_MACH Say Y or m if you have such a device. This is a recommended option. If unsure select "N". +config SND_SOC_INTEL_BXTP_IVI_HU_MACH + tristate "ASoC Audio driver for BXTP IVI HU with Dummy Codecs" + depends on MFD_INTEL_LPSS && I2C && ACPI + help + This adds support for ASoC machine driver for Broxton-P platforms + with Dummy I2S audio codec for IVI Head Unit. + Say Y or m if you have such a device. This is a recommended option. + If unsure select "N". + endif ## SND_SOC_INTEL_SKYLAKE endif ## SND_SOC_INTEL_MACH diff --git a/sound/soc/intel/boards/Makefile b/sound/soc/intel/boards/Makefile index 683737d503dc..949db4b0438d 100644 --- a/sound/soc/intel/boards/Makefile +++ b/sound/soc/intel/boards/Makefile @@ -31,6 +31,7 @@ snd-soc-cnl_svfpga-objs := cnl_svfpga.o snd-soc-bxt_ivi_ull-objs := bxt_ivi_ull.o snd-soc-kblr_rt298-objs := kblr_rt298.o snd-soc-bxtp_ivi_rse_rt298-objs := bxtp_ivi_rse_rt298.o +snd-soc-bxtp_ivi_hu_rt298-objs := bxtp_ivi_hu_rt298.o obj-$(CONFIG_SND_SOC_INTEL_HASWELL_MACH) += snd-soc-sst-haswell.o obj-$(CONFIG_SND_SOC_INTEL_BYT_RT5640_MACH) += snd-soc-sst-byt-rt5640-mach.o @@ -64,3 +65,4 @@ obj-$(CONFIG_SND_SOC_INTEL_CNL_SVFPGA_MACH) += snd-soc-cnl_svfpga.o obj-$(CONFIG_SND_SOC_INTEL_BXT_ULL_MACH) += snd-soc-bxt_ivi_ull.o obj-$(CONFIG_SND_SOC_INTEL_KBLR_RT298_MACH) += snd-soc-kblr_rt298.o obj-$(CONFIG_SND_SOC_INTEL_BXTP_IVI_RSE_MACH) += snd-soc-bxtp_ivi_rse_rt298.o +obj-$(CONFIG_SND_SOC_INTEL_BXTP_IVI_HU_MACH) += snd-soc-bxtp_ivi_hu_rt298.o diff --git a/sound/soc/intel/boards/bxtp_ivi_hu_rt298.c b/sound/soc/intel/boards/bxtp_ivi_hu_rt298.c new file mode 100644 index 000000000000..ce467721305a --- /dev/null +++ b/sound/soc/intel/boards/bxtp_ivi_hu_rt298.c @@ -0,0 +1,250 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright(c) 2018 Intel Corporation + +/* + * bxtp_ivi_hu_rt298.c -Intel HU I2S Machine Driver + */ + +#include +#include +#include +#include +#include +#include + +#define DEF_BT_RATE_INBDEX 0x0 + +struct bxtp_ivi_hu_prv { + int srate; +}; + +static unsigned int ivi_hu_bt_rates[] = { + 8000, + 16000, +}; + +/* sound card controls */ +static const char * const bt_rate[] = {"8K", "16K"}; + +static const struct soc_enum btrate_enum = + SOC_ENUM_SINGLE_EXT(2, bt_rate); + +static int bt_sample_rate_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_card *card = snd_kcontrol_chip(kcontrol); + struct bxtp_ivi_hu_prv *drv = snd_soc_card_get_drvdata(card); + + ucontrol->value.integer.value[0] = drv->srate; + return 0; +} + +static int bt_sample_rate_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_card *card = snd_kcontrol_chip(kcontrol); + struct bxtp_ivi_hu_prv *drv = snd_soc_card_get_drvdata(card); + + if (ucontrol->value.integer.value[0] == drv->srate) + return 0; + + drv->srate = ucontrol->value.integer.value[0]; + return 0; + +} + +static const struct snd_kcontrol_new hu_snd_controls[] = { + + SOC_ENUM_EXT("BT Rate", btrate_enum, + bt_sample_rate_get, bt_sample_rate_put), +}; + +static const struct snd_soc_dapm_widget broxton_widgets[] = { + SND_SOC_DAPM_SPK("Speaker", NULL), + SND_SOC_DAPM_MIC("DMIC2", NULL), +}; + +static const struct snd_soc_dapm_route broxton_rt298_map[] = { + {"Speaker", NULL, "Dummy Playback"}, + {"Dummy Capture", NULL, "DMIC2"}, + + { "Dummy Playback", NULL, "ssp0 Tx"}, + { "ssp0 Tx", NULL, "codec0_out"}, + + { "bt_ssp0_in", NULL, "ssp0 Rx" }, + { "ssp0 Rx", NULL, "Dummy Capture" }, +}; + +static int bxtp_ssp0_gpio_init(struct snd_soc_pcm_runtime *rtd) +{ + char *gpio_addr; + u32 gpio_value1 = 0x40900500; + u32 gpio_value2 = 0x44000600; + + gpio_addr = (void *)ioremap_nocache(0xd0c40610, 0x30); + if (gpio_addr == NULL) + return(-EIO); + + memcpy_toio(gpio_addr + 0x8, &gpio_value1, sizeof(gpio_value1)); + memcpy_toio(gpio_addr + 0x10, &gpio_value2, sizeof(gpio_value2)); + memcpy_toio(gpio_addr + 0x18, &gpio_value2, sizeof(gpio_value2)); + memcpy_toio(gpio_addr + 0x20, &gpio_value2, sizeof(gpio_value2)); + + iounmap(gpio_addr); + return 0; +} + +static int broxton_ssp0_fixup(struct snd_soc_pcm_runtime *rtd, + struct snd_pcm_hw_params *params) +{ + struct snd_interval *rate = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_RATE); + + struct snd_soc_card *card = rtd->card; + struct bxtp_ivi_hu_prv *drv = snd_soc_card_get_drvdata(card); + + /* SSP0 operates with a BT Transceiver */ + rate->min = rate->max = ivi_hu_bt_rates[drv->srate]; + return 0; +} + +static const char pname[] = "0000:00:0e.0"; + +/* broxton digital audio interface glue - connects codec <--> CPU */ +static struct snd_soc_dai_link broxton_rt298_dais[] = { + /* Trace Buffer DAI links */ + { + .name = "Bxt Trace Buffer0", + .stream_name = "Core 0 Trace Buffer", + .cpu_dai_name = "TraceBuffer0 Pin", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .platform_name = pname, + .capture_only = true, + .ignore_suspend = 1, + }, + { + .name = "Bxt Trace Buffer1", + .stream_name = "Core 1 Trace Buffer", + .cpu_dai_name = "TraceBuffer1 Pin", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .platform_name = pname, + .capture_only = true, + .ignore_suspend = 1, + }, + { + .name = "Bxt Compress Probe playback", + .stream_name = "Probe Playback", + .cpu_dai_name = "Compress Probe0 Pin", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .platform_name = pname, + .init = NULL, + .nonatomic = 1, + }, + { + .name = "Bxt Compress Probe capture", + .stream_name = "Probe Capture", + .cpu_dai_name = "Compress Probe1 Pin", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .platform_name = pname, + .init = NULL, + .nonatomic = 1, + }, + + /* Back End DAI links */ + { + /* SSP0 - Codec - for HDMI MCH */ + .name = "SSP0-Codec", + .id = 0, + .cpu_dai_name = "SSP0 Pin", + .platform_name = pname, + .no_pcm = 1, + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .init = bxtp_ssp0_gpio_init, + .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | + SND_SOC_DAIFMT_CBS_CFS, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + .be_hw_params_fixup = broxton_ssp0_fixup, + .dpcm_capture = 1, + .dpcm_playback = 1, + }, +}; + +static int +bxt_add_dai_link(struct snd_soc_card *card, struct snd_soc_dai_link *link) +{ + link->platform_name = pname; + link->nonatomic = 1; + + return 0; +} + +/* broxton audio machine driver for SPT + RT298S */ +static struct snd_soc_card broxton_rt298 = { + .name = "broxton-ivi-hu", + .dai_link = broxton_rt298_dais, + .num_links = ARRAY_SIZE(broxton_rt298_dais), + .controls = hu_snd_controls, + .num_controls = ARRAY_SIZE(hu_snd_controls), + .dapm_widgets = broxton_widgets, + .num_dapm_widgets = ARRAY_SIZE(broxton_widgets), + .dapm_routes = broxton_rt298_map, + .num_dapm_routes = ARRAY_SIZE(broxton_rt298_map), + .fully_routed = true, + .add_dai_link = bxt_add_dai_link, +}; + +static int broxton_audio_probe(struct platform_device *pdev) +{ + int ret_val; + struct bxtp_ivi_hu_prv *drv; + + broxton_rt298.dev = &pdev->dev; + + drv = devm_kzalloc(&pdev->dev, sizeof(*drv), GFP_KERNEL); + if (!drv) + return -ENOMEM; + + drv->srate = DEF_BT_RATE_INBDEX; + snd_soc_card_set_drvdata(&broxton_rt298, drv); + + ret_val = snd_soc_register_card(&broxton_rt298); + if (ret_val) { + dev_dbg(&pdev->dev, "snd_soc_register_card failed %d\n", + ret_val); + return ret_val; + } + + platform_set_drvdata(pdev, &broxton_rt298); + + return ret_val; +} + +static int broxton_audio_remove(struct platform_device *pdev) +{ + snd_soc_unregister_card(&broxton_rt298); + return 0; +} + +static struct platform_driver broxton_audio = { + .probe = broxton_audio_probe, + .remove = broxton_audio_remove, + .driver = { + .name = "bxt_ivi_hu_i2s", + }, +}; + +module_platform_driver(broxton_audio); + +/* Module information */ +MODULE_AUTHOR("Pardha Saradhi K "); +MODULE_AUTHOR("Ramesh Babu "); +MODULE_AUTHOR("Senthilnathan Veppur "); +MODULE_DESCRIPTION("Intel SST Audio for Broxton-P IVI HU"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:bxt_ivi_hu_i2s"); From af9488973ae6fa9002c204e0f8d1c3682b3e1152 Mon Sep 17 00:00:00 2001 From: Pardha Saradhi K Date: Wed, 3 Jan 2018 19:52:29 +0530 Subject: [PATCH 0862/1103] ASoC: Intel: Boards: Add a machine driver for BXT-P IVI M3 This patch adds support for a machine driver for Validation purposes only for M3 topology in IVI, that supports - 1. 2 bluetooth streams for playback and capture 2. 2 streams out of a Master SSP with 8 channels (PB & CP) 3. 2 more streams out of a Slave SSP with 8 channels (PB & CP) whose clock and frame are tied to above Master SSP for synchronous playback of 16 channel data. Dummy Codec driver is used for reference. Related Changes for Makefile and Kconfig have been added. Change-Id: Ibd8562678890accd21adfd89f521502fc7e88bf9 Signed-off-by: Pardha Saradhi K Reviewed-on: Reviewed-by: Singh, Guneshwor O Reviewed-by: Prabhu, PuneethX Reviewed-by: Kale, Sanyog R Tested-by: Madiwalar, MadiwalappaX --- sound/soc/intel/boards/Kconfig | 9 + sound/soc/intel/boards/Makefile | 2 + sound/soc/intel/boards/bxtp_ivi_m3.c | 327 +++++++++++++++++++++++++++ 3 files changed, 338 insertions(+) create mode 100644 sound/soc/intel/boards/bxtp_ivi_m3.c diff --git a/sound/soc/intel/boards/Kconfig b/sound/soc/intel/boards/Kconfig index d98f82cf4a3c..b38c4550fa2d 100644 --- a/sound/soc/intel/boards/Kconfig +++ b/sound/soc/intel/boards/Kconfig @@ -401,6 +401,15 @@ config SND_SOC_INTEL_BXTP_IVI_HU_MACH Say Y or m if you have such a device. This is a recommended option. If unsure select "N". +config SND_SOC_INTEL_BXTP_IVI_M3_MACH + tristate "ASoC Audio driver for BXTP IVI M3 with Dummy Codecs" + depends on MFD_INTEL_LPSS && I2C && ACPI + help + This adds support for ASoC machine driver for Broxton-P platforms + with Dummy I2S audio codec for IVI M3 topology. + Say Y or m if you have such a device. This is a recommended option. + If unsure select "N". + endif ## SND_SOC_INTEL_SKYLAKE endif ## SND_SOC_INTEL_MACH diff --git a/sound/soc/intel/boards/Makefile b/sound/soc/intel/boards/Makefile index 949db4b0438d..7bba4ac5ab44 100644 --- a/sound/soc/intel/boards/Makefile +++ b/sound/soc/intel/boards/Makefile @@ -32,6 +32,7 @@ snd-soc-bxt_ivi_ull-objs := bxt_ivi_ull.o snd-soc-kblr_rt298-objs := kblr_rt298.o snd-soc-bxtp_ivi_rse_rt298-objs := bxtp_ivi_rse_rt298.o snd-soc-bxtp_ivi_hu_rt298-objs := bxtp_ivi_hu_rt298.o +snd-soc-bxtp_ivi_m3-objs := bxtp_ivi_m3.o obj-$(CONFIG_SND_SOC_INTEL_HASWELL_MACH) += snd-soc-sst-haswell.o obj-$(CONFIG_SND_SOC_INTEL_BYT_RT5640_MACH) += snd-soc-sst-byt-rt5640-mach.o @@ -66,3 +67,4 @@ obj-$(CONFIG_SND_SOC_INTEL_BXT_ULL_MACH) += snd-soc-bxt_ivi_ull.o obj-$(CONFIG_SND_SOC_INTEL_KBLR_RT298_MACH) += snd-soc-kblr_rt298.o obj-$(CONFIG_SND_SOC_INTEL_BXTP_IVI_RSE_MACH) += snd-soc-bxtp_ivi_rse_rt298.o obj-$(CONFIG_SND_SOC_INTEL_BXTP_IVI_HU_MACH) += snd-soc-bxtp_ivi_hu_rt298.o +obj-$(CONFIG_SND_SOC_INTEL_BXTP_IVI_M3_MACH) += snd-soc-bxtp_ivi_m3.o diff --git a/sound/soc/intel/boards/bxtp_ivi_m3.c b/sound/soc/intel/boards/bxtp_ivi_m3.c new file mode 100644 index 000000000000..5dcafb979a49 --- /dev/null +++ b/sound/soc/intel/boards/bxtp_ivi_m3.c @@ -0,0 +1,327 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright(c) 2018 Intel Corporation + +/* + * bxtp_ivi_m3.c -Intel BXTP-IVI M3 I2S Machine Driver + */ + +#include +#include +#include +#include +#include +#include + +#define SSP0_GPIO_BASE 0xd0c40610 +#define SSP0_GPIO_VALUE1 0x40900500 +#define SSP0_GPIO_VALUE2 0x44000600 +#define SSP1_GPIO_BASE 0xd0c40660 +#define SSP1_GPIO_VALUE1 0x44000400 +#define SSP4_GPIO_BASE 0xd0c705A0 +#define SSP4_GPIO_VALUE1 0x44000A00 +#define SSP4_GPIO_VALUE2 0x44000800 +#define SSP5_GPIO_BASE 0xd0c70580 +#define SSP5_GPIO_VALUE 0x44000800 + +#define DEF_BT_RATE_INBDEX 0x0 + +struct bxtp_ivi_gen_prv { + int srate; +}; + +static unsigned int ivi_gen_bt_rates[] = { + 8000, + 16000, +}; + +/* sound card controls */ +static const char * const bt_rate[] = {"8K", "16K"}; + +static const struct soc_enum btrate_enum = + SOC_ENUM_SINGLE_EXT(2, bt_rate); + +static int bt_sample_rate_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_card *card = snd_kcontrol_chip(kcontrol); + struct bxtp_ivi_gen_prv *drv = snd_soc_card_get_drvdata(card); + + ucontrol->value.integer.value[0] = drv->srate; + return 0; +} + +static int bt_sample_rate_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_card *card = snd_kcontrol_chip(kcontrol); + struct bxtp_ivi_gen_prv *drv = snd_soc_card_get_drvdata(card); + + if (ucontrol->value.integer.value[0] == drv->srate) + return 0; + + drv->srate = ucontrol->value.integer.value[0]; + return 0; + +} +static const struct snd_kcontrol_new gen_snd_controls[] = { + + SOC_ENUM_EXT("BT Rate", btrate_enum, + bt_sample_rate_get, bt_sample_rate_put), +}; + +static const struct snd_soc_dapm_widget broxton_widgets[] = { + SND_SOC_DAPM_SPK("Speaker", NULL), + SND_SOC_DAPM_MIC("DMIC2", NULL), +}; + +static const struct snd_soc_dapm_route broxton_rt298_map[] = { + {"Speaker", NULL, "Dummy Playback"}, + {"Dummy Capture", NULL, "DMIC2"}, + /* BE connections */ + { "Dummy Playback", NULL, "ssp4 Tx"}, + { "ssp4 Tx", NULL, "codec0_out"}, + { "codec0_in", NULL, "ssp4 Rx" }, + { "ssp4 Rx", NULL, "Dummy Capture"}, + + { "Dummy Playback", NULL, "ssp2 Tx"}, + { "ssp2 Tx", NULL, "codec1_out"}, + { "codec1_in", NULL, "ssp2 Rx"}, + { "ssp2 Rx", NULL, "Dummy Capture"}, + + { "hdmi_ssp0_in", NULL, "ssp0 Rx"}, + { "ssp0 Rx", NULL, "Dummy Capture"}, + { "Dummy Playback", NULL, "ssp0 Tx"}, + { "ssp0 Tx", NULL, "codec4_out"}, +}; + +static int bxtp_ssp0_gpio_init(struct snd_soc_pcm_runtime *rtd) +{ + char *gpio_addr; + u32 gpio_value1 = SSP0_GPIO_VALUE1; + u32 gpio_value2 = SSP0_GPIO_VALUE2; + + gpio_addr = (void *)ioremap_nocache(SSP0_GPIO_BASE, 0x30); + if (gpio_addr == NULL) + return(-EIO); + + memcpy_toio(gpio_addr + 0x8, &gpio_value1, sizeof(gpio_value1)); + memcpy_toio(gpio_addr + 0x10, &gpio_value2, sizeof(gpio_value2)); + memcpy_toio(gpio_addr + 0x18, &gpio_value2, sizeof(gpio_value2)); + memcpy_toio(gpio_addr + 0x20, &gpio_value2, sizeof(gpio_value2)); + + iounmap(gpio_addr); + return 0; +} + +static int bxtp_ssp4_gpio_init(struct snd_soc_pcm_runtime *rtd) +{ + + char *gpio_addr; + u32 gpio_value1 = SSP4_GPIO_VALUE1; + u32 gpio_value2 = SSP4_GPIO_VALUE2; + + gpio_addr = (void *)ioremap_nocache(SSP4_GPIO_BASE, 0x30); + if (gpio_addr == NULL) + return(-EIO); + + memcpy_toio(gpio_addr, &gpio_value1, sizeof(gpio_value1)); + memcpy_toio(gpio_addr + 0x8, &gpio_value1, sizeof(gpio_value1)); + memcpy_toio(gpio_addr + 0x10, &gpio_value1, sizeof(gpio_value1)); + memcpy_toio(gpio_addr + 0x18, &gpio_value2, sizeof(gpio_value2)); + + iounmap(gpio_addr); + return 0; + +} + +static int broxton_ssp2_fixup(struct snd_soc_pcm_runtime *rtd, + struct snd_pcm_hw_params *params) +{ + struct snd_interval *rate = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_RATE); + struct snd_interval *channels = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_CHANNELS); + struct snd_soc_card *card = rtd->card; + struct bxtp_ivi_gen_prv *drv = snd_soc_card_get_drvdata(card); + + + /* The ADSP will covert the FE rate to 8k,16k mono */ + rate->min = rate->max = ivi_gen_bt_rates[drv->srate]; + channels->min = channels->max = 2; + return 0; + +} + +static const char pname[] = "0000:00:0e.0"; + +/* broxton digital audio interface glue - connects codec <--> CPU */ +static struct snd_soc_dai_link broxton_rt298_dais[] = { + /* Trace Buffer DAI links */ + { + .name = "Bxt Trace Buffer0", + .stream_name = "Core 0 Trace Buffer", + .cpu_dai_name = "TraceBuffer0 Pin", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .platform_name = pname, + .capture_only = true, + .ignore_suspend = 1, + }, + { + .name = "Bxt Trace Buffer1", + .stream_name = "Core 1 Trace Buffer", + .cpu_dai_name = "TraceBuffer1 Pin", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .platform_name = pname, + .capture_only = true, + .ignore_suspend = 1, + }, + { + .name = "Bxt Compress Probe playback", + .stream_name = "Probe Playback", + .cpu_dai_name = "Compress Probe0 Pin", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .platform_name = pname, + .init = NULL, + .nonatomic = 1, + }, + { + .name = "Bxt Compress Probe capture", + .stream_name = "Probe Capture", + .cpu_dai_name = "Compress Probe1 Pin", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .platform_name = pname, + .init = NULL, + .nonatomic = 1, + }, + + /* Back End DAI links */ + { + /* SSP0 - Codec */ + .name = "SSP0-Codec", + .id = 0, + .cpu_dai_name = "SSP0 Pin", + .platform_name = pname, + .no_pcm = 1, + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .init = bxtp_ssp0_gpio_init, + .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | + SND_SOC_DAIFMT_CBS_CFS, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + .dpcm_capture = 1, + .dpcm_playback = 1, + }, + { + /* SSP2 - Codec */ + .name = "SSP2-Codec", + .id = 2, + .cpu_dai_name = "SSP2 Pin", + .platform_name = pname, + .no_pcm = 1, + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .init = NULL, + .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | + SND_SOC_DAIFMT_CBS_CFS, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + .be_hw_params_fixup = broxton_ssp2_fixup, + .dpcm_playback = 1, + .dpcm_capture = 1, + }, + { + /* SSP4 - Codec */ + .name = "SSP4-Codec", + .id = 4, + .cpu_dai_name = "SSP4 Pin", + .platform_name = pname, + .no_pcm = 1, + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .init = bxtp_ssp4_gpio_init, + .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | + SND_SOC_DAIFMT_CBS_CFS, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + .dpcm_playback = 1, + .dpcm_capture = 1, + }, +}; + +static int +bxt_add_dai_link(struct snd_soc_card *card, struct snd_soc_dai_link *link) +{ + link->platform_name = pname; + link->nonatomic = 1; + + return 0; +} + +/* broxton audio machine driver for SPT + RT298S */ +static struct snd_soc_card broxton_rt298 = { + .name = "broxton-ivi-m3", + .dai_link = broxton_rt298_dais, + .num_links = ARRAY_SIZE(broxton_rt298_dais), + .controls = gen_snd_controls, + .num_controls = ARRAY_SIZE(gen_snd_controls), + .dapm_widgets = broxton_widgets, + .num_dapm_widgets = ARRAY_SIZE(broxton_widgets), + .dapm_routes = broxton_rt298_map, + .num_dapm_routes = ARRAY_SIZE(broxton_rt298_map), + .fully_routed = true, + .add_dai_link = bxt_add_dai_link, +}; + +static int broxton_audio_probe(struct platform_device *pdev) +{ + int ret_val; + struct bxtp_ivi_gen_prv *drv; + + broxton_rt298.dev = &pdev->dev; + + drv = devm_kzalloc(&pdev->dev, sizeof(*drv), GFP_KERNEL); + if (!drv) + return -ENOMEM; + + drv->srate = DEF_BT_RATE_INBDEX; + snd_soc_card_set_drvdata(&broxton_rt298, drv); + ret_val=snd_soc_register_card(&broxton_rt298); + + if (ret_val) { + dev_dbg(&pdev->dev, "snd_soc_register_card failed %d\n", + ret_val); + return ret_val; + } + + platform_set_drvdata(pdev, &broxton_rt298); + + return ret_val; +} + +static int broxton_audio_remove(struct platform_device *pdev) +{ + snd_soc_unregister_card(&broxton_rt298); + return 0; +} + +static struct platform_driver broxton_audio = { + .probe = broxton_audio_probe, + .remove = broxton_audio_remove, + .driver = { + .name = "bxt_ivi_m3_i2s", + }, +}; + +module_platform_driver(broxton_audio); + +/* Module information */ +MODULE_AUTHOR("Pardha Saradhi K "); +MODULE_AUTHOR("Mousumi Jana "); +MODULE_DESCRIPTION("Intel SST Audio for Broxton"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:bxt_ivi_m3_i2s"); From d249645390765aaf9d0e1fec9fe668685c1b1053 Mon Sep 17 00:00:00 2001 From: Puneeth Prabhu Date: Fri, 11 May 2018 11:32:53 +0530 Subject: [PATCH 0863/1103] ASoC: Intel: Boards: Add machine driver for generic topology This patch adds I2S machine driver for IVI generic topology which uses dummy codec. Generic topology supports 6 streams of playback and 6 streams of capture each stream is of 48K sampling rate, 8 channels and 32 bit depth. Change-Id: I6bc7b38553f90a3281a8c46b5cdbd7504713302e Signed-off-by: Puneeth Prabhu Reviewed-on: Reviewed-by: Singh, Guneshwor O Reviewed-by: Kesapragada, Pardha Saradhi Reviewed-by: Kale, Sanyog R Tested-by: Madiwalar, MadiwalappaX --- sound/soc/intel/boards/Kconfig | 9 + sound/soc/intel/boards/Makefile | 2 + sound/soc/intel/boards/bxtp_ivi_generic.c | 366 ++++++++++++++++++++++ 3 files changed, 377 insertions(+) create mode 100644 sound/soc/intel/boards/bxtp_ivi_generic.c diff --git a/sound/soc/intel/boards/Kconfig b/sound/soc/intel/boards/Kconfig index b38c4550fa2d..069ae85c555b 100644 --- a/sound/soc/intel/boards/Kconfig +++ b/sound/soc/intel/boards/Kconfig @@ -410,6 +410,15 @@ config SND_SOC_INTEL_BXTP_IVI_M3_MACH Say Y or m if you have such a device. This is a recommended option. If unsure select "N". +config SND_SOC_INTEL_BXTP_IVI_GENERIC_MACH + tristate "ASoC Audio driver for BXTP IVI GENERIC with Dummy Codecs" + depends on MFD_INTEL_LPSS && I2C && ACPI + help + This adds support for ASoC machine driver for Broxton-P platforms + with Dummy I2S audio codec for IVI generic topology. + Say Y or m if you have such a device. This is a recommended option. + If unsure select "N". + endif ## SND_SOC_INTEL_SKYLAKE endif ## SND_SOC_INTEL_MACH diff --git a/sound/soc/intel/boards/Makefile b/sound/soc/intel/boards/Makefile index 7bba4ac5ab44..f69831624273 100644 --- a/sound/soc/intel/boards/Makefile +++ b/sound/soc/intel/boards/Makefile @@ -33,6 +33,7 @@ snd-soc-kblr_rt298-objs := kblr_rt298.o snd-soc-bxtp_ivi_rse_rt298-objs := bxtp_ivi_rse_rt298.o snd-soc-bxtp_ivi_hu_rt298-objs := bxtp_ivi_hu_rt298.o snd-soc-bxtp_ivi_m3-objs := bxtp_ivi_m3.o +snd-soc-bxtp_ivi_generic-objs := bxtp_ivi_generic.o obj-$(CONFIG_SND_SOC_INTEL_HASWELL_MACH) += snd-soc-sst-haswell.o obj-$(CONFIG_SND_SOC_INTEL_BYT_RT5640_MACH) += snd-soc-sst-byt-rt5640-mach.o @@ -68,3 +69,4 @@ obj-$(CONFIG_SND_SOC_INTEL_KBLR_RT298_MACH) += snd-soc-kblr_rt298.o obj-$(CONFIG_SND_SOC_INTEL_BXTP_IVI_RSE_MACH) += snd-soc-bxtp_ivi_rse_rt298.o obj-$(CONFIG_SND_SOC_INTEL_BXTP_IVI_HU_MACH) += snd-soc-bxtp_ivi_hu_rt298.o obj-$(CONFIG_SND_SOC_INTEL_BXTP_IVI_M3_MACH) += snd-soc-bxtp_ivi_m3.o +obj-$(CONFIG_SND_SOC_INTEL_BXTP_IVI_GENERIC_MACH) += snd-soc-bxtp_ivi_generic.o diff --git a/sound/soc/intel/boards/bxtp_ivi_generic.c b/sound/soc/intel/boards/bxtp_ivi_generic.c new file mode 100644 index 000000000000..e256a4bf173b --- /dev/null +++ b/sound/soc/intel/boards/bxtp_ivi_generic.c @@ -0,0 +1,366 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright(c) 2018 Intel Corporation + +/* + * bxtp_ivi_generic.c -Intel generic I2S Machine Driver + */ + +#include +#include +#include +#include +#include +#include + +#define SSP0_GPIO_BASE 0xd0c40610 +#define SSP0_GPIO_VALUE1 0x40900500 +#define SSP0_GPIO_VALUE2 0x44000600 +#define SSP1_GPIO_BASE 0xd0c40660 +#define SSP1_GPIO_VALUE1 0x44000400 +#define SSP4_GPIO_BASE 0xd0c705A0 +#define SSP4_GPIO_VALUE1 0x44000A00 +#define SSP4_GPIO_VALUE2 0x44000800 +#define SSP5_GPIO_BASE 0xd0c70580 +#define SSP5_GPIO_VALUE 0x44000800 + +static const struct snd_soc_dapm_widget broxton_widgets[] = { + SND_SOC_DAPM_SPK("Speaker", NULL), + SND_SOC_DAPM_MIC("DMIC2", NULL), +}; + +static const struct snd_soc_dapm_route broxton_rt298_map[] = { + /* DAPM route for IVI generic topology */ + {"Speaker", NULL, "Dummy Playback"}, + {"Dummy Capture", NULL, "DMIC2"}, + + /* BE connections */ + { "Dummy Playback", NULL, "ssp5 Tx"}, + { "ssp5 Tx", NULL, "codec3_out"}, + { "codec3_in", NULL, "ssp5 Rx" }, + { "ssp5 Rx", NULL, "Dummy Capture"}, + { "Dummy Playback", NULL, "ssp4 Tx"}, + { "ssp4 Tx", NULL, "codec0_out"}, + { "codec0_in", NULL, "ssp4 Rx" }, + { "ssp4 Rx", NULL, "Dummy Capture"}, + + { "Dummy Playback", NULL, "ssp3 Tx"}, + { "ssp3 Tx", NULL, "codec5_out"}, + { "codec5_in", NULL, "ssp3 Rx"}, + { "ssp3 Rx", NULL, "Dummy Capture"}, + { "Dummy Playback", NULL, "ssp2 Tx"}, + { "ssp2 Tx", NULL, "codec1_out"}, + { "codec1_in", NULL, "ssp2 Rx"}, + { "ssp2 Rx", NULL, "Dummy Capture"}, + + { "Dummy Playback", NULL, "ssp1 Tx"}, + { "ssp1 Tx", NULL, "codec2_out"}, + { "codec2_in", NULL, "ssp1 Rx"}, + { "ssp1 Rx", NULL, "Dummy Capture"}, + + { "hdmi_ssp0_in", NULL, "ssp0 Rx"}, + { "ssp0 Rx", NULL, "Dummy Capture"}, + { "Dummy Playback", NULL, "ssp0 Tx"}, + { "ssp0 Tx", NULL, "codec4_out"}, +}; + +static int bxtp_ssp0_gpio_init(struct snd_soc_pcm_runtime *rtd) +{ + char *gpio_addr; + u32 gpio_value1 = SSP0_GPIO_VALUE1; + u32 gpio_value2 = SSP0_GPIO_VALUE2; + + gpio_addr = (void *)ioremap_nocache(SSP0_GPIO_BASE, 0x30); + if (gpio_addr == NULL) + return(-EIO); + + memcpy_toio(gpio_addr + 0x8, &gpio_value1, sizeof(gpio_value1)); + memcpy_toio(gpio_addr + 0x10, &gpio_value2, sizeof(gpio_value2)); + memcpy_toio(gpio_addr + 0x18, &gpio_value2, sizeof(gpio_value2)); + memcpy_toio(gpio_addr + 0x20, &gpio_value2, sizeof(gpio_value2)); + + iounmap(gpio_addr); + return 0; +} + +static int bxtp_ssp1_gpio_init(struct snd_soc_pcm_runtime *rtd) +{ + + char *gpio_addr; + u32 gpio_value1 = SSP1_GPIO_VALUE1; + + gpio_addr = (void *)ioremap_nocache(SSP1_GPIO_BASE, 0x30); + if (gpio_addr == NULL) + return(-EIO); + + memcpy_toio(gpio_addr + 0x8, &gpio_value1, sizeof(gpio_value1)); + memcpy_toio(gpio_addr + 0x10, &gpio_value1, sizeof(gpio_value1)); + memcpy_toio(gpio_addr + 0x18, &gpio_value1, sizeof(gpio_value1)); + memcpy_toio(gpio_addr + 0x20, &gpio_value1, sizeof(gpio_value1)); + + iounmap(gpio_addr); + return 0; +} + +static int bxtp_ssp4_gpio_init(struct snd_soc_pcm_runtime *rtd) +{ + + char *gpio_addr; + u32 gpio_value1 = SSP4_GPIO_VALUE1; + u32 gpio_value2 = SSP4_GPIO_VALUE2; + + gpio_addr = (void *)ioremap_nocache(SSP4_GPIO_BASE, 0x30); + if (gpio_addr == NULL) + return(-EIO); + + memcpy_toio(gpio_addr, &gpio_value1, sizeof(gpio_value1)); + memcpy_toio(gpio_addr + 0x8, &gpio_value1, sizeof(gpio_value1)); + memcpy_toio(gpio_addr + 0x10, &gpio_value1, sizeof(gpio_value1)); + memcpy_toio(gpio_addr + 0x18, &gpio_value2, sizeof(gpio_value2)); + + iounmap(gpio_addr); + return 0; + +} + +static int bxtp_ssp5_gpio_init(struct snd_soc_pcm_runtime *rtd) +{ + char *gpio_addr; + u32 gpio_value1 = SSP5_GPIO_VALUE; + + gpio_addr = (void *)ioremap_nocache(SSP5_GPIO_BASE, 0x30); + if (gpio_addr == NULL) + return(-EIO); + + memcpy_toio(gpio_addr, &gpio_value1, sizeof(gpio_value1)); + memcpy_toio(gpio_addr + 0x8, &gpio_value1, sizeof(gpio_value1)); + memcpy_toio(gpio_addr + 0x10, &gpio_value1, sizeof(gpio_value1)); + memcpy_toio(gpio_addr + 0x18, &gpio_value1, sizeof(gpio_value1)); + + iounmap(gpio_addr); + return 0; +} + +static int bxtp_ssp3_gpio_init(struct snd_soc_pcm_runtime *rtd) +{ + + char *gpio_addr; + u32 gpio_value1 = 0x44000800; + u32 gpio_value2 = 0x44000802; + + gpio_addr = (void *)ioremap_nocache(0xd0c40638, 0x30); + if (gpio_addr == NULL) + return(-EIO); + + memcpy_toio(gpio_addr, &gpio_value1, sizeof(gpio_value1)); + memcpy_toio(gpio_addr + 0x8, &gpio_value2, sizeof(gpio_value2)); + memcpy_toio(gpio_addr + 0x10, &gpio_value1, sizeof(gpio_value1)); + memcpy_toio(gpio_addr + 0x18, &gpio_value1, sizeof(gpio_value1)); + + iounmap(gpio_addr); + return 0; +} + +static const char pname[] = "0000:00:0e.0"; + +/* broxton digital audio interface glue - connects codec <--> CPU */ +static struct snd_soc_dai_link broxton_rt298_dais[] = { + /* Trace Buffer DAI links */ + { + .name = "Bxt Trace Buffer0", + .stream_name = "Core 0 Trace Buffer", + .cpu_dai_name = "TraceBuffer0 Pin", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .platform_name = pname, + .capture_only = true, + .ignore_suspend = 1, + }, + { + .name = "Bxt Trace Buffer1", + .stream_name = "Core 1 Trace Buffer", + .cpu_dai_name = "TraceBuffer1 Pin", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .platform_name = pname, + .capture_only = true, + .ignore_suspend = 1, + }, + { + .name = "Bxt Compress Probe playback", + .stream_name = "Probe Playback", + .cpu_dai_name = "Compress Probe0 Pin", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .platform_name = pname, + .init = NULL, + .nonatomic = 1, + }, + { + .name = "Bxt Compress Probe capture", + .stream_name = "Probe Capture", + .cpu_dai_name = "Compress Probe1 Pin", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .platform_name = pname, + .init = NULL, + .nonatomic = 1, + }, + + /* Back End DAI links */ + { + /* SSP0 - Codec */ + .name = "SSP0-Codec", + .id = 0, + .cpu_dai_name = "SSP0 Pin", + .platform_name = pname, + .no_pcm = 1, + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .init = bxtp_ssp0_gpio_init, + .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | + SND_SOC_DAIFMT_CBS_CFS, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + .dpcm_capture = 1, + .dpcm_playback = 1, + }, + { + /* SSP1 - Codec */ + .name = "SSP1-Codec", + .id = 1, + .cpu_dai_name = "SSP1 Pin", + .platform_name = pname, + .no_pcm = 1, + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .init = bxtp_ssp1_gpio_init, + .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | + SND_SOC_DAIFMT_CBS_CFS, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + .dpcm_playback = 1, + .dpcm_capture = 1, + }, + { + /* SSP2 - Codec */ + .name = "SSP2-Codec", + .id = 2, + .cpu_dai_name = "SSP2 Pin", + .platform_name = pname, + .no_pcm = 1, + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .init = NULL, + .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | + SND_SOC_DAIFMT_CBS_CFS, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + .dpcm_playback = 1, + .dpcm_capture = 1, + }, + { + /* SSP3 - Codec */ + .name = "SSP3-Codec", + .id = 3, + .cpu_dai_name = "SSP3 Pin", + .platform_name = pname, + .no_pcm = 1, + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .init = bxtp_ssp3_gpio_init, + .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | + SND_SOC_DAIFMT_CBS_CFS, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + .dpcm_playback = 1, + .dpcm_capture = 1, + }, + { + /* SSP4 - Codec */ + .name = "SSP4-Codec", + .id = 4, + .cpu_dai_name = "SSP4 Pin", + .platform_name = pname, + .no_pcm = 1, + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .init = bxtp_ssp4_gpio_init, + .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | + SND_SOC_DAIFMT_CBS_CFS, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + .dpcm_playback = 1, + .dpcm_capture = 1, + }, + { + /* SSP5 - Codec */ + .name = "SSP5-Codec", + .id = 5, + .cpu_dai_name = "SSP5 Pin", + .platform_name = pname, + .no_pcm = 1, + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .init = bxtp_ssp5_gpio_init, + .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | + SND_SOC_DAIFMT_CBS_CFS, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + .dpcm_playback = 1, + .dpcm_capture = 1, + }, +}; + +static int +bxt_add_dai_link(struct snd_soc_card *card, struct snd_soc_dai_link *link) +{ + link->platform_name = pname; + link->nonatomic = 1; + + return 0; +} + +/* broxton audio machine driver for SPT + RT298S */ +static struct snd_soc_card broxton_rt298 = { + .name = "broxton-ivi-generic", + .dai_link = broxton_rt298_dais, + .num_links = ARRAY_SIZE(broxton_rt298_dais), + .controls = NULL, + .num_controls = 0, + .dapm_widgets = broxton_widgets, + .num_dapm_widgets = ARRAY_SIZE(broxton_widgets), + .dapm_routes = broxton_rt298_map, + .num_dapm_routes = ARRAY_SIZE(broxton_rt298_map), + .fully_routed = true, + .add_dai_link = bxt_add_dai_link, +}; + +static int broxton_audio_probe(struct platform_device *pdev) +{ + broxton_rt298.dev = &pdev->dev; + return snd_soc_register_card(&broxton_rt298); +} + +static int broxton_audio_remove(struct platform_device *pdev) +{ + snd_soc_unregister_card(&broxton_rt298); + return 0; +} + +static struct platform_driver broxton_audio = { + .probe = broxton_audio_probe, + .remove = broxton_audio_remove, + .driver = { + .name = "bxt_ivi_generic_i2s", + }, +}; + +module_platform_driver(broxton_audio); + +/* Module information */ +MODULE_AUTHOR("Puneeth Prabhu "); +MODULE_AUTHOR("Pardha Saradhi K "); +MODULE_AUTHOR("Mousumi Jana "); +MODULE_DESCRIPTION("Intel SST Audio for Broxton"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:bxt_ivi_generic_i2s"); From 0be127995690b3d2e2466083ca4c89d4688245b9 Mon Sep 17 00:00:00 2001 From: Puneeth Prabhu Date: Mon, 7 May 2018 16:38:11 +0530 Subject: [PATCH 0864/1103] ASoC: Intel: Skylake: Add RSE, HU, M3 and generic machine driver entry This patch adds driver names of RSE, HU, M3 and generic machine drivers to driver data. Also adds the name of the FW file that has to be loaded when IVI machine drivers are enabled. Change-Id: I39eafec466e83a56ee7ada22b6b47927e7533ece Signed-off-by: Puneeth Prabhu Reviewed-on: Reviewed-by: Kesapragada, Pardha Saradhi Reviewed-by: Singh, Guneshwor O Reviewed-by: Kale, Sanyog R Tested-by: Madiwalar, MadiwalappaX --- sound/soc/intel/skylake/skl.c | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/sound/soc/intel/skylake/skl.c b/sound/soc/intel/skylake/skl.c index 5a442ee5af64..293a1b98b8db 100644 --- a/sound/soc/intel/skylake/skl.c +++ b/sound/soc/intel/skylake/skl.c @@ -1216,8 +1216,25 @@ static struct snd_soc_acpi_mach sst_skl_devdata[] = { static struct snd_soc_acpi_mach sst_bxtp_devdata[] = { { .id = "INT343A", +#if IS_ENABLED(CONFIG_SND_SOC_INTEL_BXTP_IVI_RSE_MACH) + .drv_name = "bxt_ivi_rse_i2s", +#elif IS_ENABLED(CONFIG_SND_SOC_INTEL_BXTP_IVI_HU_MACH) + .drv_name = "bxt_ivi_hu_i2s", +#elif IS_ENABLED(CONFIG_SND_SOC_INTEL_BXTP_IVI_M3_MACH) + .drv_name = "bxt_ivi_m3_i2s", +#elif IS_ENABLED(CONFIG_SND_SOC_INTEL_BXTP_IVI_GENERIC_MACH) + .drv_name = "bxt_ivi_generic_i2s", +#else .drv_name = "bxt_alc298s_i2s", +#endif +#if IS_ENABLED(CONFIG_SND_SOC_INTEL_BXTP_IVI_RSE_MACH) || \ +IS_ENABLED(CONFIG_SND_SOC_INTEL_BXTP_IVI_HU_MACH) || \ +IS_ENABLED(CONFIG_SND_SOC_INTEL_BXTP_IVI_M3_MACH) || \ +IS_ENABLED(CONFIG_SND_SOC_INTEL_BXTP_IVI_GENERIC_MACH) + .fw_filename = "intel/ADSPFW.bin" +#else .fw_filename = "intel/dsp_fw_bxtn.bin", +#endif }, { .id = "DLGS7219", From 14e37329321b829534a8aadc6064e7fd06ffa350 Mon Sep 17 00:00:00 2001 From: "Shaik, ShahinaX" Date: Wed, 23 May 2018 21:17:13 +0530 Subject: [PATCH 0865/1103] ASoC: Intel: Skylake: Resolve load DMA control config issue Audio driver doesn't load dmactrl_cfg after adding it in topology xml. There is no value assigned to dmactrl_cfg.size when dmactrl_cfg is parsed from topology xml by codec. But it checks for dmactrl_cfg.size when dmactrl_cfg is send to DSP. This Patch resolve this issue. Change-Id: I7caac5281cd1a6151ca7874f3b74865d91347065 Signed-off-by: Shaik, ShahinaX Reviewed-on: Reviewed-by: Gogineni, GiribabuX Reviewed-by: Kesapragada, Pardha Saradhi Reviewed-by: Shaik, Kareem M Reviewed-by: Kale, Sanyog R Tested-by: Madiwalar, MadiwalappaX --- sound/soc/intel/skylake/skl-topology.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sound/soc/intel/skylake/skl-topology.c b/sound/soc/intel/skylake/skl-topology.c index b91c4ea88002..c80ad4558ba6 100644 --- a/sound/soc/intel/skylake/skl-topology.c +++ b/sound/soc/intel/skylake/skl-topology.c @@ -4632,6 +4632,7 @@ static int skl_tplg_mfest_fill_dmactrl(struct device *dev, if (!hdr->data) return -ENOMEM; hdr->data_size = tkn_elem->value; + dmactrl_cfg->size = hdr->data_size; } else { hdr->data_size = 0; dev_err(dev, "Invalid dmactrl info \n"); From 488e42bcf8653808866f0c4b2355babf5e60703b Mon Sep 17 00:00:00 2001 From: Kareem Shaik Date: Wed, 29 Nov 2017 15:38:20 +0530 Subject: [PATCH 0866/1103] ASoC: Intel: Skylake: Improve BXT-P machine driver selection based on configuration Use configuration option to select the correct machine driver defined under same machine id. Otherwise second machine driver is not loading without a configuration check. Change-Id: I419125f133f6b684916e81f26d0e5e685414a5af Signed-off-by: Kareem Shaik Reviewed-on: Reviewed-by: Gogineni, GiribabuX Reviewed-by: Kesapragada, Pardha Saradhi Reviewed-by: Kale, Sanyog R Reviewed-by: Kp, Jeeja Tested-by: Madiwalar, MadiwalappaX --- sound/soc/intel/skylake/skl.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/sound/soc/intel/skylake/skl.c b/sound/soc/intel/skylake/skl.c index 293a1b98b8db..3a11cef06e6b 100644 --- a/sound/soc/intel/skylake/skl.c +++ b/sound/soc/intel/skylake/skl.c @@ -1243,16 +1243,19 @@ IS_ENABLED(CONFIG_SND_SOC_INTEL_BXTP_IVI_GENERIC_MACH) .machine_quirk = snd_soc_acpi_codec_list, .quirk_data = &bxt_codecs, }, +#if IS_ENABLED(CONFIG_SND_SOC_INTEL_BXT_TDF8532_MACH) { .id = "INT34C3", .drv_name = "bxt_tdf8532", .fw_filename = "intel/dsp_fw_bxtn.bin", }, +#elif IS_ENABLED(CONFIG_SND_SOC_INTEL_BXT_ULL_MACH) { .id = "INT34C3", .drv_name = "bxt_ivi_ull", .fw_filename = "intel/dsp_fw_ull_bxtn.bin", }, +#endif {} }; From 8797014caad793353ab7fd415bd4681062ae88f0 Mon Sep 17 00:00:00 2001 From: Pardha Saradhi K Date: Thu, 30 Mar 2017 22:57:48 +0530 Subject: [PATCH 0867/1103] ASoC: Intel: common: Provide an interface to send IPCs directly This patch adds support for an IPC Tx function that can be called directly rather than adding new Tx messages to the Queue. Hence the new logic to send IPCs will be - If the DSP is not busy, send IPCs directly. Else add to the queue Change-Id: I9d58872a051c9d704c60cf25a2e3b69c3b580818 Signed-off-by: Pardha Saradhi K Reviewed-on: Reviewed-by: Koul, Vinod Tested-by: Tr, HarishkumarX Reviewed-on: Reviewed-by: Shaik, ShahinaX Reviewed-by: Kale, Sanyog R Tested-by: Madiwalar, MadiwalappaX --- sound/soc/intel/common/sst-ipc.c | 11 +++++++-- sound/soc/intel/common/sst-ipc.h | 1 + sound/soc/intel/skylake/skl-sst-ipc.c | 34 +++++++++++++++++++++++++++ 3 files changed, 44 insertions(+), 2 deletions(-) diff --git a/sound/soc/intel/common/sst-ipc.c b/sound/soc/intel/common/sst-ipc.c index 03896bde44d3..0037e21f0b69 100644 --- a/sound/soc/intel/common/sst-ipc.c +++ b/sound/soc/intel/common/sst-ipc.c @@ -127,8 +127,15 @@ static int ipc_tx_message(struct sst_generic_ipc *ipc, u64 header, ipc->ops.tx_data_copy(msg, tx_data, tx_bytes); list_add_tail(&msg->list, &ipc->tx_list); - schedule_work(&ipc->kwork); - spin_unlock_irqrestore(&ipc->dsp->spinlock, flags); + + if ((ipc->ops.is_dsp_busy && ipc->ops.is_dsp_busy(ipc->dsp)) || + (ipc->ops.direct_tx_msg == NULL)) { + schedule_work(&ipc->kwork); + spin_unlock_irqrestore(&ipc->dsp->spinlock, flags); + } else { + spin_unlock_irqrestore(&ipc->dsp->spinlock, flags); + ipc->ops.direct_tx_msg(ipc); + } if (wait) return tx_wait_done(ipc, msg, rx_data, diff --git a/sound/soc/intel/common/sst-ipc.h b/sound/soc/intel/common/sst-ipc.h index 4cfa9e37ac28..eae3fa7d1aef 100644 --- a/sound/soc/intel/common/sst-ipc.h +++ b/sound/soc/intel/common/sst-ipc.h @@ -47,6 +47,7 @@ struct sst_generic_ipc; struct sst_plat_ipc_ops { void (*tx_msg)(struct sst_generic_ipc *, struct ipc_message *); + void (*direct_tx_msg)(struct sst_generic_ipc *); void (*shim_dbg)(struct sst_generic_ipc *, const char *); void (*tx_data_copy)(struct ipc_message *, char *, size_t); u64 (*reply_msg_match)(u64 header, u64 *mask); diff --git a/sound/soc/intel/skylake/skl-sst-ipc.c b/sound/soc/intel/skylake/skl-sst-ipc.c index 6efc9502d06e..9370c474c618 100644 --- a/sound/soc/intel/skylake/skl-sst-ipc.c +++ b/sound/soc/intel/skylake/skl-sst-ipc.c @@ -343,6 +343,39 @@ static bool skl_ipc_is_dsp_busy(struct sst_dsp *dsp) return (hipci & SKL_ADSP_REG_HIPCI_BUSY); } +static void skl_ipc_tx_msgs_direct(struct sst_generic_ipc *ipc) +{ + struct ipc_message *msg; + unsigned long flags; + + spin_lock_irqsave(&ipc->dsp->spinlock, flags); + + if (list_empty(&ipc->tx_list) || ipc->pending) { + spin_unlock_irqrestore(&ipc->dsp->spinlock, flags); + return; + } + + /* if the DSP is busy, we will TX messages after IRQ. + * also postpone if we are in the middle of procesing completion irq*/ + if (ipc->ops.is_dsp_busy && ipc->ops.is_dsp_busy(ipc->dsp)) { + dev_dbg(ipc->dev, "skl_ipc_tx_msgs_direct dsp busy\n"); + spin_unlock_irqrestore(&ipc->dsp->spinlock, flags); + return; + } + + msg = list_first_entry(&ipc->tx_list, struct ipc_message, list); + list_move(&msg->list, &ipc->rx_list); + + dev_dbg(ipc->dev, "skl_ipc_tx_msgs_direct sending message, header - %#.16lx\n", + (unsigned long)msg->header); + print_hex_dump_debug("Params:", DUMP_PREFIX_OFFSET, 8, 4, + msg->tx_data, msg->tx_size, false); + if (ipc->ops.tx_msg != NULL) + ipc->ops.tx_msg(ipc, msg); + + spin_unlock_irqrestore(&ipc->dsp->spinlock, flags); +} + /* Lock to be held by caller */ static void skl_ipc_tx_msg(struct sst_generic_ipc *ipc, struct ipc_message *msg) { @@ -854,6 +887,7 @@ int skl_ipc_init(struct device *dev, struct skl_sst *skl) ipc->ops.tx_msg = skl_ipc_tx_msg; ipc->ops.tx_data_copy = skl_ipc_tx_data_copy; + ipc->ops.direct_tx_msg = skl_ipc_tx_msgs_direct; ipc->ops.is_dsp_busy = skl_ipc_is_dsp_busy; return 0; From bbec5325a7651b42f7a144985bf01eb239f5821d Mon Sep 17 00:00:00 2001 From: Pardha Saradhi K Date: Wed, 14 Feb 2018 06:39:45 +0530 Subject: [PATCH 0868/1103] ASoC: Intel: BXT: Remove compile warnings Remove compile warnings from the function bxt_load_base_firmware Change-Id: I88b13e9dc045ecc57b989a96e83c7da1683b0e3b Signed-off-by: Pardha Saradhi K Reviewed-on: Reviewed-by: Kp, Jeeja Tested-by: Tr, HarishkumarX Reviewed-on: Reviewed-by: Shaik, ShahinaX Reviewed-by: Kale, Sanyog R Tested-by: Madiwalar, MadiwalappaX --- sound/soc/intel/skylake/bxt-sst.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/sound/soc/intel/skylake/bxt-sst.c b/sound/soc/intel/skylake/bxt-sst.c index 47b2a24f84dc..bac77f8fc0d4 100644 --- a/sound/soc/intel/skylake/bxt-sst.c +++ b/sound/soc/intel/skylake/bxt-sst.c @@ -277,14 +277,14 @@ static int bxt_load_base_firmware(struct sst_dsp *ctx) sst_dsp_shim_read(ctx, BXT_ADSP_ERROR_CODE), sst_dsp_shim_read(ctx, BXT_ADSP_FW_STATUS)); - dev_err(ctx->dev, "Itertion %d Core En/ROM load fail:%d\n", i,ret); + dev_err(ctx->dev, "Iteration %d Core En/ROM load fail:%d\n", i, ret); continue; } - dev_dbg(ctx->dev, "Itertion %d ROM load Success:%d,%d\n", i,ret); + dev_dbg(ctx->dev, "Iteration %d ROM load Success:%d\n", i, ret); ret = sst_transfer_fw_host_dma(ctx); if (ret < 0) { - dev_err(ctx->dev, "Itertion %d Transfer firmware failed %d\n", i,ret); + dev_err(ctx->dev, "Iteration %d Transfer firmware failed %d\n", i, ret); dev_info(ctx->dev, "Error code=0x%x: FW status=0x%x\n", sst_dsp_shim_read(ctx, BXT_ADSP_ERROR_CODE), sst_dsp_shim_read(ctx, BXT_ADSP_FW_STATUS)); @@ -293,7 +293,7 @@ static int bxt_load_base_firmware(struct sst_dsp *ctx) skl_dsp_disable_core(ctx, SKL_DSP_CORE0_MASK); continue; } - dev_dbg(ctx->dev, "Itertion %d FW transfer Success:%d,%d\n", i,ret); + dev_dbg(ctx->dev, "Iteration %d FW transfer Success:%d\n", i, ret); if (ret == 0) break; From ca4c062e80bcd42464696110086a0da543ba37dc Mon Sep 17 00:00:00 2001 From: Puneeth Prabhu Date: Tue, 15 May 2018 12:01:07 +0530 Subject: [PATCH 0869/1103] ASoC: topology: Increase SND_SOC_TPLG_NUM_TEXTS to 32 ITT supports maximum of 32 path configs for a path. But, kernel supports upto 16 path configs. So, increase SND_SOC_TPLG_NUM_TEXTS to 32 to support 32 path configs for a path. Change-Id: Idc3bb893829e5a7a58a001fe28773257f96c11e1 Signed-off-by: Puneeth Prabhu Reviewed-on: Reviewed-by: Kesapragada, Pardha Saradhi Reviewed-by: Singh, Guneshwor O Reviewed-by: Kale, Sanyog R Tested-by: Madiwalar, MadiwalappaX --- include/uapi/sound/asoc.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/uapi/sound/asoc.h b/include/uapi/sound/asoc.h index a74ca232f1fc..5b1411bf79c5 100644 --- a/include/uapi/sound/asoc.h +++ b/include/uapi/sound/asoc.h @@ -88,7 +88,7 @@ #define SND_SOC_TPLG_MAGIC 0x41536F43 /* ASoC */ /* string sizes */ -#define SND_SOC_TPLG_NUM_TEXTS 16 +#define SND_SOC_TPLG_NUM_TEXTS 32 /* ABI version */ #define SND_SOC_TPLG_ABI_VERSION 0x5 /* current version */ From 7101a386244b67b2ae560523062d4575344d395b Mon Sep 17 00:00:00 2001 From: Sameer Sharma Date: Thu, 7 Jun 2018 17:28:42 +0530 Subject: [PATCH 0870/1103] ASoC: Intel: board: Add support for FE dynamic dai for ULL FE dai links now come from the topology, so removing them from machine driver. Change-Id: I2ac610902496beecb225fc44e57be28d05fb98a7 Signed-off-by: Sameer Sharma Reviewed-on: Reviewed-by: Shaik, ShahinaX Reviewed-by: Prabhu, PuneethX Reviewed-by: Periyasamy, SriramX Reviewed-by: Gogineni, GiribabuX Reviewed-by: Bozek, DominikX Reviewed-by: Lewandowski, Gustaw Tested-by: Madiwalar, MadiwalappaX --- sound/soc/intel/boards/bxt_ivi_ull.c | 54 ---------------------------- 1 file changed, 54 deletions(-) diff --git a/sound/soc/intel/boards/bxt_ivi_ull.c b/sound/soc/intel/boards/bxt_ivi_ull.c index 22ae8ab30a1d..fe9e28598bbe 100644 --- a/sound/soc/intel/boards/bxt_ivi_ull.c +++ b/sound/soc/intel/boards/bxt_ivi_ull.c @@ -95,60 +95,6 @@ static const struct snd_soc_dapm_route bxtp_ull_map[] = { /* broxton digital audio interface glue - connects codec <--> CPU */ static struct snd_soc_dai_link bxtp_ull_dais[] = { - { - .name = "Bxt Audio Port 3", - .stream_name = "Stereo-16K SSP4", - .cpu_dai_name = "System Pin 3", - .platform_name = "0000:00:0e.0", - .nonatomic = 1, - .dynamic = 1, - .codec_name = "snd-soc-dummy", - .codec_dai_name = "snd-soc-dummy-dai", - .trigger = {SND_SOC_DPCM_TRIGGER_POST, - SND_SOC_DPCM_TRIGGER_POST}, - .dpcm_playback = 1, - .dpcm_capture = 1, - }, - { - .name = "Bxt Audio Port 4", - .stream_name = "5-ch SSP1", - .cpu_dai_name = "System Pin 4", - .platform_name = "0000:00:0e.0", - .nonatomic = 1, - .dynamic = 1, - .codec_name = "snd-soc-dummy", - .codec_dai_name = "snd-soc-dummy-dai", - .trigger = {SND_SOC_DPCM_TRIGGER_POST, - SND_SOC_DPCM_TRIGGER_POST}, - .dpcm_playback = 1, - }, - { - .name = "Bxt Audio Port 5", - .stream_name = "SSP2 Stream", - .cpu_dai_name = "System Pin 5", - .platform_name = "0000:00:0e.0", - .nonatomic = 1, - .dynamic = 1, - .codec_name = "snd-soc-dummy", - .codec_dai_name = "snd-soc-dummy-dai", - .trigger = {SND_SOC_DPCM_TRIGGER_POST, - SND_SOC_DPCM_TRIGGER_POST}, - .dpcm_playback = 1, - .dpcm_capture = 1, - }, - { - .name = "Bxt Audio Port 6", - .stream_name = "8-Ch SSP0", - .cpu_dai_name = "System Pin 6", - .platform_name = "0000:00:0e.0", - .nonatomic = 1, - .dynamic = 1, - .codec_name = "snd-soc-dummy", - .codec_dai_name = "snd-soc-dummy-dai", - .trigger = {SND_SOC_DPCM_TRIGGER_POST, - SND_SOC_DPCM_TRIGGER_POST}, - .dpcm_capture = 1, - }, /* Probe DAI Links */ { .name = "Bxt Compress Probe playback", From 28163706d56218a2f8eb94d69db8d471a8ac8733 Mon Sep 17 00:00:00 2001 From: Sameer Sharma Date: Mon, 18 Jun 2018 19:54:54 +0530 Subject: [PATCH 0871/1103] ASoC: Intel: Skylake: Enable use_tplg_pcm flag for BXTP platform Enabling use_tplg_pcm flag to check whether FE dais will be registered from topology during dai driver component registration for BXTP platform Change-Id: I60766088a56922e61c8010b0a587452db3db42d2 Signed-off-by: Sameer Sharma Reviewed-on: Reviewed-by: Periyasamy, SriramX Reviewed-by: Gogineni, GiribabuX Reviewed-by: Prabhu, PuneethX Reviewed-by: Bozek, DominikX Reviewed-by: Lewandowski, Gustaw Tested-by: Madiwalar, MadiwalappaX --- sound/soc/intel/skylake/skl.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/sound/soc/intel/skylake/skl.c b/sound/soc/intel/skylake/skl.c index 3a11cef06e6b..199cbfe1d00d 100644 --- a/sound/soc/intel/skylake/skl.c +++ b/sound/soc/intel/skylake/skl.c @@ -1188,6 +1188,10 @@ static struct skl_machine_pdata cnl_pdata = { .use_tplg_pcm = true, }; +static struct skl_machine_pdata bxt_pdata = { + .use_tplg_pcm = true, +}; + static struct snd_soc_acpi_mach sst_skl_devdata[] = { { .id = "INT343A", @@ -1248,12 +1252,14 @@ IS_ENABLED(CONFIG_SND_SOC_INTEL_BXTP_IVI_GENERIC_MACH) .id = "INT34C3", .drv_name = "bxt_tdf8532", .fw_filename = "intel/dsp_fw_bxtn.bin", + .pdata = &bxt_pdata, }, #elif IS_ENABLED(CONFIG_SND_SOC_INTEL_BXT_ULL_MACH) { .id = "INT34C3", .drv_name = "bxt_ivi_ull", .fw_filename = "intel/dsp_fw_ull_bxtn.bin", + .pdata = &bxt_pdata, }, #endif {} From d661ec20f54f595b69b606d476df87e5b8208d65 Mon Sep 17 00:00:00 2001 From: "Lewandowski, Gustaw" Date: Wed, 11 Jul 2018 13:52:39 +0200 Subject: [PATCH 0872/1103] ASoC: Intel: Fix race condition in IPC rx list Since there are multiple IPCs being sent in a short span of time, there is a possibility of more than one message being on the Rx list after receiving response from firmware. In such cases, when the first notification of interrupt from firmware is received, driver retrieves the message from the Rx list but does not delete it from the list till the next lock. In the meantime, when another interrupt is received from the firmware, driver is reading the previous message again since the previous message has not been removed from the list. Change-Id: I3d85cce7b0e9632a73e286d7d42a2627c1431d6e Reviewed-on: Tested-by: Lewandowski, Gustaw Reviewed-by: Wasko, Michal --- sound/soc/intel/skylake/skl-sst-ipc.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/sound/soc/intel/skylake/skl-sst-ipc.c b/sound/soc/intel/skylake/skl-sst-ipc.c index 9370c474c618..0e7b5ce64b9e 100644 --- a/sound/soc/intel/skylake/skl-sst-ipc.c +++ b/sound/soc/intel/skylake/skl-sst-ipc.c @@ -420,6 +420,7 @@ static struct ipc_message *skl_ipc_reply_get_msg(struct sst_generic_ipc *ipc, msg = list_first_entry(&ipc->rx_list, struct ipc_message, list); + list_del(&msg->list); out: return msg; @@ -707,8 +708,8 @@ void skl_ipc_process_reply(struct sst_generic_ipc *ipc, spin_lock_irqsave(&ipc->dsp->spinlock, flags); msg = skl_ipc_reply_get_msg(ipc, *ipc_header); - spin_unlock_irqrestore(&ipc->dsp->spinlock, flags); if (msg == NULL) { + spin_unlock_irqrestore(&ipc->dsp->spinlock, flags); dev_dbg(ipc->dev, "ipc: rx list is empty\n"); return; } @@ -753,8 +754,6 @@ void skl_ipc_process_reply(struct sst_generic_ipc *ipc, } } - spin_lock_irqsave(&ipc->dsp->spinlock, flags); - list_del(&msg->list); sst_ipc_tx_msg_reply_complete(ipc, msg); spin_unlock_irqrestore(&ipc->dsp->spinlock, flags); } From 27a4ce8038dada552a3c8d75033841793e9cae7b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Amadeusz=20S=C5=82awi=C5=84ski?= Date: Thu, 12 Jul 2018 12:55:26 +0200 Subject: [PATCH 0873/1103] ASoC: Intel: Skylake: pipeline needs to be reset before disconnect MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change-Id: I5666ca1fca1ca80e9b431138470f4d9693ff011b Signed-off-by: Amadeusz Sławiński Reviewed-on: Reviewed-by: Bozek, DominikX Reviewed-by: Lewandowski, Gustaw Reviewed-by: Rojewski, Cezary Tested-by: Rojewski, Cezary --- sound/soc/intel/skylake/skl-messages.c | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/sound/soc/intel/skylake/skl-messages.c b/sound/soc/intel/skylake/skl-messages.c index bc8d3afcf7d1..c01875a2a61d 100644 --- a/sound/soc/intel/skylake/skl-messages.c +++ b/sound/soc/intel/skylake/skl-messages.c @@ -2632,7 +2632,9 @@ int skl_create_pipeline(struct skl_sst *ctx, struct skl_pipe *pipe) /* * A pipeline needs to be deleted on cleanup. If a pipeline is running, then - * pause the pipeline first and then delete it + * pause the pipeline first and then delete it. There is also case in which + * pipeline needs to be reset before deletion, so always reset as it doesn't + * change anything in other cases. * The pipe delete is done by sending delete pipeline IPC. DSP will stop the * DMA engines and releases resources */ @@ -2642,6 +2644,10 @@ int skl_delete_pipe(struct skl_sst *ctx, struct skl_pipe *pipe) dev_dbg(ctx->dev, "%s: pipe = %d\n", __func__, pipe->ppl_id); + /* If pipe was not created in FW, do not try to delete it */ + if (pipe->state < SKL_PIPE_CREATED) + return 0; + /* If pipe is started, do stop the pipe in FW. */ if (pipe->state >= SKL_PIPE_STARTED) { ret = skl_set_pipe_state(ctx, pipe, PPL_PAUSED); @@ -2653,9 +2659,14 @@ int skl_delete_pipe(struct skl_sst *ctx, struct skl_pipe *pipe) pipe->state = SKL_PIPE_PAUSED; } - /* If pipe was not created in FW, do not try to delete it */ - if (pipe->state < SKL_PIPE_CREATED) - return 0; + /* reset pipe state before deletion */ + ret = skl_set_pipe_state(ctx, pipe, PPL_RESET); + if (ret < 0) { + dev_err(ctx->dev, "Failed to reset pipe ret=%d\n", ret); + return ret; + } + + pipe->state = SKL_PIPE_RESET; ret = skl_ipc_delete_pipeline(&ctx->ipc, pipe->ppl_id); if (ret < 0) { From d745cfab1cf41ea38306b4071302afb98c82848e Mon Sep 17 00:00:00 2001 From: Gustaw Lewandowski Date: Tue, 24 Jul 2018 16:30:03 +0200 Subject: [PATCH 0874/1103] Revert "ASoC: topology: Increase SND_SOC_TPLG_NUM_TEXTS to 32" This reverts commit 640a66de27627b1f18270497d8e16fec7c9afb58. Since correspondig changes to alsa-lib is not properly upstreamed we should not introduce such change without ensure backward compatibility. Change-Id: I512b63b0e4dc7bbd889503a560c373c9e396326e Reviewed-on: Reviewed-by: Slawinski, AmadeuszX Reviewed-by: Bozek, DominikX Reviewed-by: Rojewski, Cezary Tested-by: Rojewski, Cezary --- include/uapi/sound/asoc.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/uapi/sound/asoc.h b/include/uapi/sound/asoc.h index 5b1411bf79c5..a74ca232f1fc 100644 --- a/include/uapi/sound/asoc.h +++ b/include/uapi/sound/asoc.h @@ -88,7 +88,7 @@ #define SND_SOC_TPLG_MAGIC 0x41536F43 /* ASoC */ /* string sizes */ -#define SND_SOC_TPLG_NUM_TEXTS 32 +#define SND_SOC_TPLG_NUM_TEXTS 16 /* ABI version */ #define SND_SOC_TPLG_ABI_VERSION 0x5 /* current version */ From 3216686e10fde93b78c897de2a140556f91a9f1b Mon Sep 17 00:00:00 2001 From: Cezary Rojewski Date: Tue, 17 Jul 2018 17:06:34 +0200 Subject: [PATCH 0875/1103] ASoC: Intel: Allow for firmware load retry. Due to unconditional initial timeouts, firmware may fail to load during its initialization. This issue cannot be resolved on driver side but has to be accounted for nonetheless. Default firmware load retry count is set to 3. Change-Id: Idd12483e07633d1f809cdc4363fb51f5217233ad Signed-off-by: Cezary Rojewski Reviewed-on: Reviewed-by: Slawinski, AmadeuszX Reviewed-by: Lewandowski, Gustaw Tested-by: Lewandowski, Gustaw --- sound/soc/intel/common/sst-dsp.h | 7 +++++ sound/soc/intel/skylake/cnl-sst.c | 48 ++++++++++++++++++------------- 2 files changed, 35 insertions(+), 20 deletions(-) diff --git a/sound/soc/intel/common/sst-dsp.h b/sound/soc/intel/common/sst-dsp.h index 859f0de00339..92150322496c 100644 --- a/sound/soc/intel/common/sst-dsp.h +++ b/sound/soc/intel/common/sst-dsp.h @@ -174,6 +174,13 @@ #define SST_PMCS 0x84 #define SST_PMCS_PS_MASK 0x3 +/* + * Number of times to retry firmware load before driver commits failure. + * This is to account for initial timeouts, e.g., from ROM init during + * FW load procedure when the former fails to receive imr from CSE. + */ +#define SST_FW_INIT_RETRY 3 + struct sst_dsp; /* diff --git a/sound/soc/intel/skylake/cnl-sst.c b/sound/soc/intel/skylake/cnl-sst.c index df42b2157b21..19398e200cb0 100644 --- a/sound/soc/intel/skylake/cnl-sst.c +++ b/sound/soc/intel/skylake/cnl-sst.c @@ -212,49 +212,56 @@ static int cnl_load_base_firmware(struct sst_dsp *ctx) struct firmware stripped_fw; struct skl_sst *cnl = ctx->thread_context; struct skl_fw_property_info fw_property; - int ret; + int ret, i; fw_property.memory_reclaimed = -1; if (!ctx->fw) { ret = request_firmware(&ctx->fw, ctx->fw_name, ctx->dev); if (ret < 0) { dev_err(ctx->dev, "request firmware failed: %d\n", ret); - goto cnl_load_base_firmware_failed; + return ret; } } /* parse uuids if first boot */ if (cnl->is_first_boot) { - ret = snd_skl_parse_uuids(ctx, ctx->fw, - CNL_ADSP_FW_HDR_OFFSET, 0); + ret = snd_skl_parse_uuids(ctx, ctx->fw, CNL_ADSP_FW_HDR_OFFSET, 0); if (ret < 0) - goto cnl_load_base_firmware_failed; + goto load_base_firmware_failed; } stripped_fw.data = ctx->fw->data; stripped_fw.size = ctx->fw->size; skl_dsp_strip_extended_manifest(&stripped_fw); - ret = cnl_prepare_fw(ctx, stripped_fw.data, stripped_fw.size); - if (ret < 0) { - dev_err(ctx->dev, "prepare firmware failed: %d\n", ret); - goto cnl_load_base_firmware_failed; - } + ret = -ENOEXEC; + for (i = 0; i < SST_FW_INIT_RETRY && ret < 0; i++) { + ret = cnl_prepare_fw(ctx, stripped_fw.data, stripped_fw.size); + if (ret < 0) { + dev_dbg(ctx->dev, "prepare firmware failed: %d\n", ret); + continue; + } - ret = sst_transfer_fw_host_dma(ctx); - if (ret < 0) { - dev_err(ctx->dev, "transfer firmware failed: %d\n", ret); - cnl_dsp_disable_core(ctx, SKL_DSP_CORE0_MASK); - goto cnl_load_base_firmware_failed; + dev_dbg(ctx->dev, "ROM loaded successfully on iteration %d.\n", i); + + ret = sst_transfer_fw_host_dma(ctx); + if (ret < 0) { + dev_dbg(ctx->dev, "transfer firmware failed: %d\n", ret); + cnl_dsp_disable_core(ctx, SKL_DSP_CORE0_MASK); + } } + if (ret < 0) + goto load_base_firmware_failed; + dev_dbg(ctx->dev, "Firmware download successful.\n"); + ret = wait_event_timeout(cnl->boot_wait, cnl->boot_complete, msecs_to_jiffies(SKL_IPC_BOOT_MSECS)); if (ret == 0) { dev_err(ctx->dev, "FW ready timed-out\n"); cnl_dsp_disable_core(ctx, SKL_DSP_CORE0_MASK); ret = -EIO; - goto cnl_load_base_firmware_failed; + goto load_base_firmware_failed; } cnl->fw_loaded = true; @@ -263,7 +270,7 @@ static int cnl_load_base_firmware(struct sst_dsp *ctx) if (ret < 0) { dev_err(ctx->dev, "fwconfig ipc failed !\n"); ret = -EIO; - goto cnl_load_base_firmware_failed; + goto load_base_firmware_failed; } fw_property = cnl->fw_property; @@ -271,14 +278,14 @@ static int cnl_load_base_firmware(struct sst_dsp *ctx) dev_err(ctx->dev, "Memory reclaim not enabled:%d\n", fw_property.memory_reclaimed); ret = -EIO; - goto cnl_load_base_firmware_failed; + goto load_base_firmware_failed; } ret = skl_get_hardware_configuration(ctx); if (ret < 0) { dev_err(ctx->dev, "hwconfig ipc failed !\n"); ret = -EIO; - goto cnl_load_base_firmware_failed; + goto load_base_firmware_failed; } /* Update dsp core count retrieved from hw config IPC */ @@ -286,7 +293,8 @@ static int cnl_load_base_firmware(struct sst_dsp *ctx) return 0; -cnl_load_base_firmware_failed: +load_base_firmware_failed: + dev_err(ctx->dev, "Firmware load failed: %d.\n", ret); release_firmware(ctx->fw); ctx->fw = NULL; From 91a7014fac0846514fe3fed179f1a3cf4725d65b Mon Sep 17 00:00:00 2001 From: Puneeth Prabhu Date: Thu, 19 Jul 2018 11:59:01 +0530 Subject: [PATCH 0876/1103] Revert "ASoC: core: Do not return for dummy codec in soc_probe_component" This reverts commit 88088a3e6a6c379b069241176f5c4f1c8573f85c. As an impact of above commit, dummy widget gets added to the widget list which leads to creation of DAI link between dummy widget and the FE DAI widget. As a result, skl_tplg_fe_get_cpr_module() returns an invalid module config pointer which leads to crash. Reverting the above commit will avoid adding a dummy widget to the widget list. Change-Id: I3d41fc776806185d9040dae695dbc258989be863 Signed-off-by: Puneeth Prabhu Reviewed-on: Reviewed-by: Pawse, GuruprasadX Reviewed-by: Kale, Sanyog R Tested-by: Madiwalar, MadiwalappaX --- sound/soc/soc-core.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index f41ff22e1317..2ecf3442c965 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -1242,10 +1242,10 @@ static int soc_probe_component(struct snd_soc_card *card, struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component); struct snd_soc_dai *dai; int ret; -#if 0 + if (!strcmp(component->name, "snd-soc-dummy")) return 0; -#endif + if (component->card) { if (component->card != card) { dev_err(component->dev, From 721507a19412b6cb620f15d0e3c53463913cdb4d Mon Sep 17 00:00:00 2001 From: Cezary Rojewski Date: Mon, 13 Aug 2018 15:29:19 +0200 Subject: [PATCH 0877/1103] ASoC: Intel: Skylake: Align with v4.18-rc1 linux kernel base. File skl-tplg-interface.h no longer present, relocated to uapi. Removal of duplicate declaration for skl_dai_load within skl-topology.h. Signed-off-by: Cezary Rojewski --- sound/soc/intel/skylake/skl-sst-ipc.h | 2 +- sound/soc/intel/skylake/skl-topology.h | 2 -- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/sound/soc/intel/skylake/skl-sst-ipc.h b/sound/soc/intel/skylake/skl-sst-ipc.h index 436e0365aa76..ee32aaa68490 100644 --- a/sound/soc/intel/skylake/skl-sst-ipc.h +++ b/sound/soc/intel/skylake/skl-sst-ipc.h @@ -20,7 +20,7 @@ #include #include "../common/sst-ipc.h" #include "skl-sst-dsp.h" -#include "skl-tplg-interface.h" +#include struct sst_dsp; struct skl_sst; diff --git a/sound/soc/intel/skylake/skl-topology.h b/sound/soc/intel/skylake/skl-topology.h index e86b84e9868d..c0ef5e78dbfa 100644 --- a/sound/soc/intel/skylake/skl-topology.h +++ b/sound/soc/intel/skylake/skl-topology.h @@ -629,8 +629,6 @@ int skl_tplg_dsp_log_get(struct snd_kcontrol *kcontrol, int skl_tplg_dsp_log_set(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol); -int skl_dai_load(struct snd_soc_component *cmp, - struct snd_soc_dai_driver *pcm_dai); int skl_tplg_change_notification_get(struct snd_kcontrol *kcontrol, unsigned int __user *data, unsigned int size); struct snd_kcontrol *skl_search_notify_kctl(struct skl_sst *skl, From 55dbde1aa998f1cb3b86f559b648700fa2c1d789 Mon Sep 17 00:00:00 2001 From: Cezary Rojewski Date: Tue, 14 Aug 2018 11:38:49 +0200 Subject: [PATCH 0878/1103] ASoC: Intel: Move bxt machine drv tables to common directory. Align with machine-drv-tables declaration model present on upstream. Tables are moved from skl.c into separate file within common directory dedicated to bxt-specific data only. Change-Id: I6780bcd1506709757e673130ffc0ce6d0b26709f Signed-off-by: Cezary Rojewski Reviewed-on: Reviewed-by: Uzieblo, Olgierd --- .../intel/common/soc-acpi-intel-bxt-match.c | 33 +++++++++++ sound/soc/intel/skylake/skl.c | 57 ------------------- 2 files changed, 33 insertions(+), 57 deletions(-) diff --git a/sound/soc/intel/common/soc-acpi-intel-bxt-match.c b/sound/soc/intel/common/soc-acpi-intel-bxt-match.c index f39386e540d3..77cbd98d3e22 100644 --- a/sound/soc/intel/common/soc-acpi-intel-bxt-match.c +++ b/sound/soc/intel/common/soc-acpi-intel-bxt-match.c @@ -8,17 +8,39 @@ #include #include +#include "../skylake/skl.h" static struct snd_soc_acpi_codecs bxt_codecs = { .num_codecs = 1, .codecs = {"MX98357A"} }; +static struct skl_machine_pdata bxt_pdata = { + .use_tplg_pcm = true, +}; + struct snd_soc_acpi_mach snd_soc_acpi_intel_bxt_machines[] = { { .id = "INT343A", +#if IS_ENABLED(CONFIG_SND_SOC_INTEL_BXTP_IVI_RSE_MACH) + .drv_name = "bxt_ivi_rse_i2s", +#elif IS_ENABLED(CONFIG_SND_SOC_INTEL_BXTP_IVI_HU_MACH) + .drv_name = "bxt_ivi_hu_i2s", +#elif IS_ENABLED(CONFIG_SND_SOC_INTEL_BXTP_IVI_M3_MACH) + .drv_name = "bxt_ivi_m3_i2s", +#elif IS_ENABLED(CONFIG_SND_SOC_INTEL_BXTP_IVI_GENERIC_MACH) + .drv_name = "bxt_ivi_generic_i2s", +#else .drv_name = "bxt_alc298s_i2s", +#endif +#if IS_ENABLED(CONFIG_SND_SOC_INTEL_BXTP_IVI_RSE_MACH) || \ + IS_ENABLED(CONFIG_SND_SOC_INTEL_BXTP_IVI_HU_MACH) || \ + IS_ENABLED(CONFIG_SND_SOC_INTEL_BXTP_IVI_M3_MACH) || \ + IS_ENABLED(CONFIG_SND_SOC_INTEL_BXTP_IVI_GENERIC_MACH) + .fw_filename = "intel/ADSPFW.bin" +#else .fw_filename = "intel/dsp_fw_bxtn.bin", +#endif }, { .id = "DLGS7219", @@ -44,13 +66,24 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_bxt_machines[] = { .sof_tplg_filename = "intel/sof-apl-wm8804.tplg", .asoc_plat_name = "0000:00:0e.0", }, +#if IS_ENABLED(CONFIG_SND_SOC_INTEL_BXT_ULL_MACH) + { + .id = "INT34C3", + .drv_name = "bxt_ivi_ull", + .fw_filename = "intel/dsp_fw_ull_bxtn.bin", + .pdata = &bxt_pdata, + }, +#else { .id = "INT34C3", .drv_name = "bxt_tdf8532", + .fw_filename = "intel/dsp_fw_bxtn.bin", .sof_fw_filename = "intel/sof-apl.ri", .sof_tplg_filename = "intel/sof-apl-tdf8532.tplg", .asoc_plat_name = "0000:00:0e.0", + .pdata = &bxt_pdata, }, +#endif {}, }; EXPORT_SYMBOL_GPL(snd_soc_acpi_intel_bxt_machines); diff --git a/sound/soc/intel/skylake/skl.c b/sound/soc/intel/skylake/skl.c index 199cbfe1d00d..6dc99d4961ae 100644 --- a/sound/soc/intel/skylake/skl.c +++ b/sound/soc/intel/skylake/skl.c @@ -1164,11 +1164,6 @@ static struct snd_soc_acpi_codecs kbl_codecs = { .codecs = {"10508825"} }; -static struct snd_soc_acpi_codecs bxt_codecs = { - .num_codecs = 1, - .codecs = {"MX98357A"} -}; - static struct snd_soc_acpi_codecs kbl_poppy_codecs = { .num_codecs = 1, .codecs = {"10EC5663"} @@ -1188,10 +1183,6 @@ static struct skl_machine_pdata cnl_pdata = { .use_tplg_pcm = true, }; -static struct skl_machine_pdata bxt_pdata = { - .use_tplg_pcm = true, -}; - static struct snd_soc_acpi_mach sst_skl_devdata[] = { { .id = "INT343A", @@ -1217,54 +1208,6 @@ static struct snd_soc_acpi_mach sst_skl_devdata[] = { {} }; -static struct snd_soc_acpi_mach sst_bxtp_devdata[] = { - { - .id = "INT343A", -#if IS_ENABLED(CONFIG_SND_SOC_INTEL_BXTP_IVI_RSE_MACH) - .drv_name = "bxt_ivi_rse_i2s", -#elif IS_ENABLED(CONFIG_SND_SOC_INTEL_BXTP_IVI_HU_MACH) - .drv_name = "bxt_ivi_hu_i2s", -#elif IS_ENABLED(CONFIG_SND_SOC_INTEL_BXTP_IVI_M3_MACH) - .drv_name = "bxt_ivi_m3_i2s", -#elif IS_ENABLED(CONFIG_SND_SOC_INTEL_BXTP_IVI_GENERIC_MACH) - .drv_name = "bxt_ivi_generic_i2s", -#else - .drv_name = "bxt_alc298s_i2s", -#endif -#if IS_ENABLED(CONFIG_SND_SOC_INTEL_BXTP_IVI_RSE_MACH) || \ -IS_ENABLED(CONFIG_SND_SOC_INTEL_BXTP_IVI_HU_MACH) || \ -IS_ENABLED(CONFIG_SND_SOC_INTEL_BXTP_IVI_M3_MACH) || \ -IS_ENABLED(CONFIG_SND_SOC_INTEL_BXTP_IVI_GENERIC_MACH) - .fw_filename = "intel/ADSPFW.bin" -#else - .fw_filename = "intel/dsp_fw_bxtn.bin", -#endif - }, - { - .id = "DLGS7219", - .drv_name = "bxt_da7219_max98357a_i2s", - .fw_filename = "intel/dsp_fw_bxtn.bin", - .machine_quirk = snd_soc_acpi_codec_list, - .quirk_data = &bxt_codecs, - }, -#if IS_ENABLED(CONFIG_SND_SOC_INTEL_BXT_TDF8532_MACH) - { - .id = "INT34C3", - .drv_name = "bxt_tdf8532", - .fw_filename = "intel/dsp_fw_bxtn.bin", - .pdata = &bxt_pdata, - }, -#elif IS_ENABLED(CONFIG_SND_SOC_INTEL_BXT_ULL_MACH) - { - .id = "INT34C3", - .drv_name = "bxt_ivi_ull", - .fw_filename = "intel/dsp_fw_ull_bxtn.bin", - .pdata = &bxt_pdata, - }, -#endif - {} -}; - static struct snd_soc_acpi_mach sst_kbl_devdata[] = { { .id = "INT343A", From 65de7b114a8ac4c65f5f15a7428e58aa8dfba5f8 Mon Sep 17 00:00:00 2001 From: Cezary Rojewski Date: Tue, 14 Aug 2018 12:02:26 +0200 Subject: [PATCH 0879/1103] ASoC: Intel: Move kbl machine drv tables to common directory. Align with machine-drv-tables declaration model present on upstream. Tables are moved from skl.c into separate file within common directory dedicated to kbl-specific data only. Change-Id: I25846b83d6c791c7c7dae25766a06beddd686277 Depends-On: I6780bcd1506709757e673130ffc0ce6d0b26709f Signed-off-by: Cezary Rojewski Reviewed-on: Reviewed-by: Uzieblo, Olgierd --- .../intel/common/soc-acpi-intel-kbl-match.c | 4 + sound/soc/intel/skylake/skl.c | 79 ------------------- 2 files changed, 4 insertions(+), 79 deletions(-) diff --git a/sound/soc/intel/common/soc-acpi-intel-kbl-match.c b/sound/soc/intel/common/soc-acpi-intel-kbl-match.c index 0ee173ca437d..750a7f851a2c 100644 --- a/sound/soc/intel/common/soc-acpi-intel-kbl-match.c +++ b/sound/soc/intel/common/soc-acpi-intel-kbl-match.c @@ -35,7 +35,11 @@ static struct snd_soc_acpi_codecs kbl_7219_98357_codecs = { struct snd_soc_acpi_mach snd_soc_acpi_intel_kbl_machines[] = { { .id = "INT343A", +#if IS_ENABLED(CONFIG_SND_SOC_INTEL_KBLR_RT298_MACH) + .drv_name = "kblr_alc298s_i2s", +#else .drv_name = "kbl_alc286s_i2s", +#endif .fw_filename = "intel/dsp_fw_kbl.bin", }, { diff --git a/sound/soc/intel/skylake/skl.c b/sound/soc/intel/skylake/skl.c index 6dc99d4961ae..f661ffc4a33a 100644 --- a/sound/soc/intel/skylake/skl.c +++ b/sound/soc/intel/skylake/skl.c @@ -1159,26 +1159,6 @@ static struct snd_soc_acpi_codecs skl_codecs = { .codecs = {"10508825"} }; -static struct snd_soc_acpi_codecs kbl_codecs = { - .num_codecs = 1, - .codecs = {"10508825"} -}; - -static struct snd_soc_acpi_codecs kbl_poppy_codecs = { - .num_codecs = 1, - .codecs = {"10EC5663"} -}; - -static struct snd_soc_acpi_codecs kbl_5663_5514_codecs = { - .num_codecs = 2, - .codecs = {"10EC5663", "10EC5514"} -}; - -static struct snd_soc_acpi_codecs kbl_7219_98357_codecs = { - .num_codecs = 1, - .codecs = {"MX98357A"} -}; - static struct skl_machine_pdata cnl_pdata = { .use_tplg_pcm = true, }; @@ -1208,65 +1188,6 @@ static struct snd_soc_acpi_mach sst_skl_devdata[] = { {} }; -static struct snd_soc_acpi_mach sst_kbl_devdata[] = { - { - .id = "INT343A", -#if IS_ENABLED(CONFIG_SND_SOC_INTEL_KBLR_RT298_MACH) - .drv_name = "kblr_alc298s_i2s", -#else - .drv_name = "kbl_alc286s_i2s", -#endif - .fw_filename = "intel/dsp_fw_kbl.bin", - }, - { - .id = "INT343B", - .drv_name = "kbl_n88l25_s4567", - .fw_filename = "intel/dsp_fw_kbl.bin", - .machine_quirk = snd_soc_acpi_codec_list, - .quirk_data = &kbl_codecs, - .pdata = &skl_dmic_data - }, - { - .id = "MX98357A", - .drv_name = "kbl_n88l25_m98357a", - .fw_filename = "intel/dsp_fw_kbl.bin", - .machine_quirk = snd_soc_acpi_codec_list, - .quirk_data = &kbl_codecs, - .pdata = &skl_dmic_data - }, - { - .id = "MX98927", - .drv_name = "kbl_r5514_5663_max", - .fw_filename = "intel/dsp_fw_kbl.bin", - .machine_quirk = snd_soc_acpi_codec_list, - .quirk_data = &kbl_5663_5514_codecs, - .pdata = &skl_dmic_data - }, - { - .id = "MX98927", - .drv_name = "kbl_rt5663_m98927", - .fw_filename = "intel/dsp_fw_kbl.bin", - .machine_quirk = snd_soc_acpi_codec_list, - .quirk_data = &kbl_poppy_codecs, - .pdata = &skl_dmic_data - }, - { - .id = "10EC5663", - .drv_name = "kbl_rt5663", - .fw_filename = "intel/dsp_fw_kbl.bin", - }, - { - .id = "DLGS7219", - .drv_name = "kbl_da7219_max98357a", - .fw_filename = "intel/dsp_fw_kbl.bin", - .machine_quirk = snd_soc_acpi_codec_list, - .quirk_data = &kbl_7219_98357_codecs, - .pdata = &skl_dmic_data - }, - - {} -}; - static struct snd_soc_acpi_mach sst_glk_devdata[] = { { .id = "INT343A", From 10a7ab9393f8bbf2b05723306d12bac510758a59 Mon Sep 17 00:00:00 2001 From: Cezary Rojewski Date: Tue, 14 Aug 2018 12:25:29 +0200 Subject: [PATCH 0880/1103] ASoC: Intel: Move skl machine drv tables to common directory. Align with machine drv tables declaration model present on upstream. Tables are moved from skl.c into separate file within common directory dedicated to skl-specific data only. Change-Id: Ia7c823611f6e0f14a94447399cdcafe9aeb5e51e Depends-On: I25846b83d6c791c7c7dae25766a06beddd686277 Signed-off-by: Cezary Rojewski Reviewed-on: Reviewed-by: Uzieblo, Olgierd --- sound/soc/intel/skylake/skl.c | 30 ------------------------------ 1 file changed, 30 deletions(-) diff --git a/sound/soc/intel/skylake/skl.c b/sound/soc/intel/skylake/skl.c index f661ffc4a33a..815fa5541444 100644 --- a/sound/soc/intel/skylake/skl.c +++ b/sound/soc/intel/skylake/skl.c @@ -1154,40 +1154,10 @@ static void skl_remove(struct pci_dev *pci) dev_set_drvdata(&pci->dev, NULL); } -static struct snd_soc_acpi_codecs skl_codecs = { - .num_codecs = 1, - .codecs = {"10508825"} -}; - static struct skl_machine_pdata cnl_pdata = { .use_tplg_pcm = true, }; -static struct snd_soc_acpi_mach sst_skl_devdata[] = { - { - .id = "INT343A", - .drv_name = "skl_alc286s_i2s", - .fw_filename = "intel/dsp_fw_release.bin", - }, - { - .id = "INT343B", - .drv_name = "skl_n88l25_s4567", - .fw_filename = "intel/dsp_fw_release.bin", - .machine_quirk = snd_soc_acpi_codec_list, - .quirk_data = &skl_codecs, - .pdata = &skl_dmic_data - }, - { - .id = "MX98357A", - .drv_name = "skl_n88l25_m98357a", - .fw_filename = "intel/dsp_fw_release.bin", - .machine_quirk = snd_soc_acpi_codec_list, - .quirk_data = &skl_codecs, - .pdata = &skl_dmic_data - }, - {} -}; - static struct snd_soc_acpi_mach sst_glk_devdata[] = { { .id = "INT343A", From 27bc78a767079edd2bd7f5833adae27bdcbd9dd6 Mon Sep 17 00:00:00 2001 From: Cezary Rojewski Date: Tue, 14 Aug 2018 12:15:45 +0200 Subject: [PATCH 0881/1103] ASoC: Intel: Move glk machine drv tables to common directory. Align with machine-drv-tables declaration model present on upstream. Tables are moved from skl.c into separate file within common directory dedicated to glk-specific data only. Change-Id: I3c2c0ee4eeffddb495dfd765788b9cc861e7741e Depends-On: Ia7c823611f6e0f14a94447399cdcafe9aeb5e51e Signed-off-by: Cezary Rojewski Reviewed-on: Reviewed-by: Uzieblo, Olgierd --- sound/soc/intel/skylake/skl.c | 9 --------- 1 file changed, 9 deletions(-) diff --git a/sound/soc/intel/skylake/skl.c b/sound/soc/intel/skylake/skl.c index 815fa5541444..eb26714a128a 100644 --- a/sound/soc/intel/skylake/skl.c +++ b/sound/soc/intel/skylake/skl.c @@ -1158,15 +1158,6 @@ static struct skl_machine_pdata cnl_pdata = { .use_tplg_pcm = true, }; -static struct snd_soc_acpi_mach sst_glk_devdata[] = { - { - .id = "INT343A", - .drv_name = "glk_alc298s_i2s", - .fw_filename = "intel/dsp_fw_glk.bin", - }, - {} -}; - static const struct snd_soc_acpi_mach sst_cnl_devdata[] = { #if !IS_ENABLED(CONFIG_SND_SOC_RT700) { From 8a83352b18684edf782fb177f7cbb9f68c2948a3 Mon Sep 17 00:00:00 2001 From: Cezary Rojewski Date: Tue, 14 Aug 2018 12:41:13 +0200 Subject: [PATCH 0882/1103] ASoC: Intel: Move icl machine drv tables to common directory. Align with machine-drv-tables declaration model present on upstream. Tables are moved from skl.c into separate file within common directory dedicated to icl-specific data only. Change-Id: I67ead17edb1bc469894a053e7d85b67409eeb2e6 Depends-On: I3c2c0ee4eeffddb495dfd765788b9cc861e7741e Signed-off-by: Cezary Rojewski Reviewed-on: Reviewed-by: Uzieblo, Olgierd --- include/sound/soc-acpi-intel-match.h | 1 + sound/soc/intel/common/Makefile | 2 +- .../intel/common/soc-acpi-intel-icl-match.c | 46 +++++++++++++++++++ sound/soc/intel/skylake/skl.c | 29 +----------- 4 files changed, 49 insertions(+), 29 deletions(-) create mode 100644 sound/soc/intel/common/soc-acpi-intel-icl-match.c diff --git a/include/sound/soc-acpi-intel-match.h b/include/sound/soc-acpi-intel-match.h index bb1d24b703fb..54096616fbec 100644 --- a/include/sound/soc-acpi-intel-match.h +++ b/include/sound/soc-acpi-intel-match.h @@ -24,5 +24,6 @@ extern struct snd_soc_acpi_mach snd_soc_acpi_intel_kbl_machines[]; extern struct snd_soc_acpi_mach snd_soc_acpi_intel_bxt_machines[]; extern struct snd_soc_acpi_mach snd_soc_acpi_intel_glk_machines[]; extern struct snd_soc_acpi_mach snd_soc_acpi_intel_cnl_machines[]; +extern struct snd_soc_acpi_mach snd_soc_acpi_intel_icl_machines[]; #endif diff --git a/sound/soc/intel/common/Makefile b/sound/soc/intel/common/Makefile index 915a34cdc8ac..fd52419f1c81 100644 --- a/sound/soc/intel/common/Makefile +++ b/sound/soc/intel/common/Makefile @@ -7,7 +7,7 @@ snd-soc-acpi-intel-match-objs := soc-acpi-intel-byt-match.o soc-acpi-intel-cht-m soc-acpi-intel-hsw-bdw-match.o \ soc-acpi-intel-skl-match.o soc-acpi-intel-kbl-match.o \ soc-acpi-intel-bxt-match.o soc-acpi-intel-glk-match.o \ - soc-acpi-intel-cnl-match.o + soc-acpi-intel-cnl-match.o soc-acpi-intel-icl-match.o obj-$(CONFIG_SND_SOC_INTEL_SST) += snd-soc-sst-dsp.o snd-soc-sst-ipc.o obj-$(CONFIG_SND_SOC_INTEL_SST_ACPI) += snd-soc-sst-acpi.o diff --git a/sound/soc/intel/common/soc-acpi-intel-icl-match.c b/sound/soc/intel/common/soc-acpi-intel-icl-match.c new file mode 100644 index 000000000000..94a7e5603102 --- /dev/null +++ b/sound/soc/intel/common/soc-acpi-intel-icl-match.c @@ -0,0 +1,46 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * soc-apci-intel-icl-match.c - tables and support for ICL ACPI enumeration. + * + * Copyright (c) 2018, Intel Corporation. + * + */ + +#include +#include +#include "../skylake/skl.h" + +static struct skl_machine_pdata icl_pdata = { + .use_tplg_pcm = true, +}; + +struct snd_soc_acpi_mach snd_soc_acpi_intel_icl_machines[] = { +#if IS_ENABLED(CONFIG_SND_SOC_RT700) + { + .id = "dummy", + .drv_name = "icl_rt700", + .fw_filename = "intel/dsp_fw_icl.bin", + .pdata = &icl_pdata, + }, +#elif IS_ENABLED(CONFIG_SND_SOC_WM5110) + { + .id = "dummy", + .drv_name = "icl_wm8281", + .fw_filename = "intel/dsp_fw_icl.bin", + .pdata = &icl_pdata, + }, +#else + { + .id = "dummy", + .drv_name = "icl_rt274", + .fw_filename = "intel/dsp_fw_icl.bin", + .pdata = &icl_pdata, + }, + +#endif + {} +}; +EXPORT_SYMBOL_GPL(snd_soc_acpi_intel_icl_machines); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("Intel Common ACPI Match module"); diff --git a/sound/soc/intel/skylake/skl.c b/sound/soc/intel/skylake/skl.c index eb26714a128a..efa24a9e58fe 100644 --- a/sound/soc/intel/skylake/skl.c +++ b/sound/soc/intel/skylake/skl.c @@ -1176,33 +1176,6 @@ static const struct snd_soc_acpi_mach sst_cnl_devdata[] = { {} }; -static struct snd_soc_acpi_mach sst_icl_devdata[] = { -#if IS_ENABLED(CONFIG_SND_SOC_RT700) - { - .id = "dummy", - .drv_name = "icl_rt700", - .fw_filename = "intel/dsp_fw_icl.bin", - .pdata = &cnl_pdata, - }, -#elif IS_ENABLED(CONFIG_SND_SOC_WM5110) - { - .id = "dummy", - .drv_name = "icl_wm8281", - .fw_filename = "intel/dsp_fw_icl.bin", - .pdata = &cnl_pdata, - }, -#else - { - .id = "dummy", - .drv_name = "icl_rt274", - .fw_filename = "intel/dsp_fw_icl.bin", - .pdata = &cnl_pdata, - }, - -#endif - {} -}; - /* PCI IDs */ static const struct pci_device_id skl_ids[] = { /* Sunrise Point-LP */ @@ -1222,7 +1195,7 @@ static const struct pci_device_id skl_ids[] = { .driver_data = (unsigned long)&snd_soc_acpi_intel_cnl_machines}, /* ICL */ { PCI_DEVICE(0x8086, 0x34c8), - .driver_data = (unsigned long)&sst_icl_devdata}, + .driver_data = (unsigned long)&snd_soc_acpi_intel_icl_machines}, { 0, } }; MODULE_DEVICE_TABLE(pci, skl_ids); From 97315ec12632d18a5894eecd15f1faa121cfb503 Mon Sep 17 00:00:00 2001 From: Cezary Rojewski Date: Tue, 14 Aug 2018 12:34:29 +0200 Subject: [PATCH 0883/1103] ASoC: Intel: Move cnl machine drv tables to common directory. Align with machine-drv-tables declaration model present on upstream. Tables are moved from skl.c into separate file within common directory dedicated to cnl-specific data only. Change-Id: I9bccf434c9a1d7949e88d0ab363e28c1bd193e75 Depends-On: I67ead17edb1bc469894a053e7d85b67409eeb2e6 Signed-off-by: Cezary Rojewski Reviewed-on: Reviewed-by: Uzieblo, Olgierd --- .../intel/common/soc-acpi-intel-cnl-match.c | 8 +++++++ sound/soc/intel/skylake/skl.c | 22 ------------------- 2 files changed, 8 insertions(+), 22 deletions(-) diff --git a/sound/soc/intel/common/soc-acpi-intel-cnl-match.c b/sound/soc/intel/common/soc-acpi-intel-cnl-match.c index ec8e28e7b937..9abc557b7b54 100644 --- a/sound/soc/intel/common/soc-acpi-intel-cnl-match.c +++ b/sound/soc/intel/common/soc-acpi-intel-cnl-match.c @@ -15,6 +15,7 @@ static struct skl_machine_pdata cnl_pdata = { }; struct snd_soc_acpi_mach snd_soc_acpi_intel_cnl_machines[] = { +#if !IS_ENABLED(CONFIG_SND_SOC_RT700) { .id = "INT34C2", .drv_name = "cnl_rt274", @@ -24,6 +25,13 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_cnl_machines[] = { .sof_tplg_filename = "intel/sof-cnl-rt274.tplg", .asoc_plat_name = "0000:00:1f.3", }, +#else + { + .drv_name = "cnl_rt700", + .fw_filename = "intel/dsp_fw_cnl.bin", + .pdata = &cnl_pdata, + }, +#endif {}, }; EXPORT_SYMBOL_GPL(snd_soc_acpi_intel_cnl_machines); diff --git a/sound/soc/intel/skylake/skl.c b/sound/soc/intel/skylake/skl.c index efa24a9e58fe..31d214a0db85 100644 --- a/sound/soc/intel/skylake/skl.c +++ b/sound/soc/intel/skylake/skl.c @@ -1154,28 +1154,6 @@ static void skl_remove(struct pci_dev *pci) dev_set_drvdata(&pci->dev, NULL); } -static struct skl_machine_pdata cnl_pdata = { - .use_tplg_pcm = true, -}; - -static const struct snd_soc_acpi_mach sst_cnl_devdata[] = { -#if !IS_ENABLED(CONFIG_SND_SOC_RT700) - { - .id = "INT34C2", - .drv_name = "cnl_rt274", - .fw_filename = "intel/dsp_fw_cnl.bin", - .pdata = &cnl_pdata, - }, -#else - { - .drv_name = "cnl_rt700", - .fw_filename = "intel/dsp_fw_cnl.bin", - .pdata = &cnl_pdata, - }, -#endif - {} -}; - /* PCI IDs */ static const struct pci_device_id skl_ids[] = { /* Sunrise Point-LP */ From d036c19a2cb17e831e4cd6bd43bbbaae8383d2f0 Mon Sep 17 00:00:00 2001 From: Pardha Saradhi K Date: Thu, 4 Jan 2018 19:40:48 +0530 Subject: [PATCH 0884/1103] ASoC: Intel: Skylake: validate the downloaded firmware MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Print the firmware version downloaded and verify if it is equal or greater than the minimum supported version for the existing driver. Report error and exit driver initialization if downloaded firmware is not compatible with current driver. Change-Id: I6e0f046d88123c9c216ac9a1c9054f37661e1bfe Signed-off-by: Prakash, Divya1 Signed-off-by: Mohit Sinha Signed-off-by: Pardha Saradhi K Signed-off-by: Amadeusz Sławiński Reviewed-on: Reviewed-by: Rojewski, Cezary Tested-by: Rojewski, Cezary --- sound/soc/intel/skylake/bxt-sst.c | 17 ++++++++++++++-- sound/soc/intel/skylake/cnl-sst-dsp.h | 2 +- sound/soc/intel/skylake/cnl-sst.c | 11 ++++++++-- sound/soc/intel/skylake/skl-messages.c | 22 +++++++++++--------- sound/soc/intel/skylake/skl-sst-dsp.h | 27 +++++++++++++++++++++++-- sound/soc/intel/skylake/skl-sst-ipc.h | 7 ------- sound/soc/intel/skylake/skl-sst-utils.c | 25 +++++++++++++++++++++++ sound/soc/intel/skylake/skl-sst.c | 18 +++++++++++++++-- sound/soc/intel/skylake/skl.h | 14 ------------- 9 files changed, 104 insertions(+), 39 deletions(-) diff --git a/sound/soc/intel/skylake/bxt-sst.c b/sound/soc/intel/skylake/bxt-sst.c index bac77f8fc0d4..76266d3945e7 100644 --- a/sound/soc/intel/skylake/bxt-sst.c +++ b/sound/soc/intel/skylake/bxt-sst.c @@ -311,6 +311,16 @@ static int bxt_load_base_firmware(struct sst_dsp *ctx) skl_dsp_disable_core(ctx, SKL_DSP_CORE0_MASK); ret = -EIO; } else { + ret = skl_get_firmware_configuration(ctx); + if (ret < 0) { + dev_err(ctx->dev, "FW version query failed\n"); + goto sst_load_base_firmware_failed; + } + + ret = skl_validate_fw_version(skl); + if (ret < 0) + goto sst_load_base_firmware_failed; + ret = 0; skl->fw_loaded = true; } @@ -632,21 +642,24 @@ static struct sst_dsp_device skl_dev = { }; int bxt_sst_dsp_init(struct device *dev, void __iomem *mmio_base, int irq, - const char *fw_name, struct skl_dsp_loader_ops dsp_ops, + const char *fw_name, const struct skl_dsp_ops *dsp_ops, struct skl_sst **dsp, void *ptr) { struct skl_sst *skl; struct sst_dsp *sst; + struct skl_dsp_loader_ops loader_ops; u32 dsp_wp[] = {BXT_ADSP_WP_DSP0, BXT_ADSP_WP_DSP1}; int ret; - ret = skl_sst_ctx_init(dev, irq, fw_name, dsp_ops, dsp, &skl_dev); + loader_ops = dsp_ops->loader_ops(); + ret = skl_sst_ctx_init(dev, irq, fw_name, loader_ops, dsp, &skl_dev); if (ret < 0) { dev_err(dev, "%s: no device\n", __func__); return ret; } skl = *dsp; + skl->dsp_ops = dsp_ops; sst = skl->dsp; sst->fw_ops = bxt_fw_ops; sst->addr.lpe = mmio_base; diff --git a/sound/soc/intel/skylake/cnl-sst-dsp.h b/sound/soc/intel/skylake/cnl-sst-dsp.h index 5f0653b36308..7bc24ee5eecc 100644 --- a/sound/soc/intel/skylake/cnl-sst-dsp.h +++ b/sound/soc/intel/skylake/cnl-sst-dsp.h @@ -117,7 +117,7 @@ bool cnl_ipc_int_status(struct sst_dsp *ctx); void cnl_ipc_free(struct sst_generic_ipc *ipc); int cnl_sst_dsp_init(struct device *dev, void __iomem *mmio_base, int irq, - const char *fw_name, struct skl_dsp_loader_ops dsp_ops, + const char *fw_name, const struct skl_dsp_ops *dsp_ops, struct skl_sst **dsp, void *ptr); int cnl_sst_init_fw(struct device *dev, struct skl_sst *ctx); void cnl_sst_dsp_cleanup(struct device *dev, struct skl_sst *ctx); diff --git a/sound/soc/intel/skylake/cnl-sst.c b/sound/soc/intel/skylake/cnl-sst.c index 19398e200cb0..52565423c83e 100644 --- a/sound/soc/intel/skylake/cnl-sst.c +++ b/sound/soc/intel/skylake/cnl-sst.c @@ -273,6 +273,10 @@ static int cnl_load_base_firmware(struct sst_dsp *ctx) goto load_base_firmware_failed; } + ret = skl_validate_fw_version(cnl); + if (ret < 0) + goto load_base_firmware_failed; + fw_property = cnl->fw_property; if (fw_property.memory_reclaimed <= 0) { dev_err(ctx->dev, "Memory reclaim not enabled:%d\n", @@ -725,22 +729,25 @@ static void skl_unregister_sdw_masters(struct skl_sst *ctx) } int cnl_sst_dsp_init(struct device *dev, void __iomem *mmio_base, int irq, - const char *fw_name, struct skl_dsp_loader_ops dsp_ops, + const char *fw_name, const struct skl_dsp_ops *dsp_ops, struct skl_sst **dsp, void *ptr) { struct skl_sst *cnl; struct sst_dsp *sst; + struct skl_dsp_loader_ops loader_ops; u32 dsp_wp[] = {CNL_ADSP_WP_DSP0, CNL_ADSP_WP_DSP1, CNL_ADSP_WP_DSP2, CNL_ADSP_WP_DSP3}; int ret; - ret = skl_sst_ctx_init(dev, irq, fw_name, dsp_ops, dsp, &cnl_dev); + loader_ops = dsp_ops->loader_ops(); + ret = skl_sst_ctx_init(dev, irq, fw_name, loader_ops, dsp, &cnl_dev); if (ret < 0) { dev_err(dev, "%s: no device\n", __func__); return ret; } cnl = *dsp; + cnl->dsp_ops = dsp_ops; sst = cnl->dsp; sst->fw_ops = cnl_fw_ops; sst->addr.lpe = mmio_base; diff --git a/sound/soc/intel/skylake/skl-messages.c b/sound/soc/intel/skylake/skl-messages.c index c01875a2a61d..f66ae9d702bc 100644 --- a/sound/soc/intel/skylake/skl-messages.c +++ b/sound/soc/intel/skylake/skl-messages.c @@ -277,7 +277,8 @@ static const struct skl_dsp_ops dsp_ops[] = { .loader_ops = skl_get_loader_ops, .init = skl_sst_dsp_init, .init_fw = skl_sst_init_fw, - .cleanup = skl_sst_dsp_cleanup + .cleanup = skl_sst_dsp_cleanup, + .min_fw_ver = {9, 21, 0, 3173} }, { .id = 0x9d71, @@ -285,7 +286,8 @@ static const struct skl_dsp_ops dsp_ops[] = { .loader_ops = skl_get_loader_ops, .init = skl_sst_dsp_init, .init_fw = skl_sst_init_fw, - .cleanup = skl_sst_dsp_cleanup + .cleanup = skl_sst_dsp_cleanup, + .min_fw_ver = {9, 21, 0, 3173} }, { .id = 0x5a98, @@ -294,7 +296,8 @@ static const struct skl_dsp_ops dsp_ops[] = { .init = bxt_sst_dsp_init, .init_fw = bxt_sst_init_fw, .cleanup = bxt_sst_dsp_cleanup, - .do_recovery = skl_do_recovery + .do_recovery = skl_do_recovery, + .min_fw_ver = {9, 22, 1, 3132} }, { .id = 0x3198, @@ -303,7 +306,8 @@ static const struct skl_dsp_ops dsp_ops[] = { .init = bxt_sst_dsp_init, .init_fw = bxt_sst_init_fw, .cleanup = bxt_sst_dsp_cleanup, - .do_recovery = skl_do_recovery + .do_recovery = skl_do_recovery, + .min_fw_ver = {9, 22, 1, 3366} }, { .id = 0x9dc8, @@ -312,7 +316,8 @@ static const struct skl_dsp_ops dsp_ops[] = { .init = cnl_sst_dsp_init, .init_fw = cnl_sst_init_fw, .cleanup = cnl_sst_dsp_cleanup, - .do_recovery = skl_do_recovery + .do_recovery = skl_do_recovery, + .min_fw_ver = {10, 23, 0, 1233} }, { .id = 0x34c8, @@ -321,7 +326,8 @@ static const struct skl_dsp_ops dsp_ops[] = { .init = cnl_sst_dsp_init, .init_fw = cnl_sst_init_fw, .cleanup = cnl_sst_dsp_cleanup, - .do_recovery = skl_do_recovery + .do_recovery = skl_do_recovery, + .min_fw_ver = {10, 23, 0, 1233} }, }; @@ -1283,14 +1289,12 @@ int skl_init_dsp(struct skl *skl) goto unmap_mmio; } - loader_ops = ops->loader_ops(); - ret = ops->init(bus->dev, mmio_base, irq, skl->fw_name, loader_ops, + ret = ops->init(bus->dev, mmio_base, irq, skl->fw_name, ops, &skl->skl_sst, &cnl_sdw_bra_ops); if (ret < 0) goto unmap_mmio; - skl->skl_sst->dsp_ops = ops; cores = &skl->skl_sst->cores; cores->count = ops->num_cores; diff --git a/sound/soc/intel/skylake/skl-sst-dsp.h b/sound/soc/intel/skylake/skl-sst-dsp.h index dc793d503115..fa3a47183916 100644 --- a/sound/soc/intel/skylake/skl-sst-dsp.h +++ b/sound/soc/intel/skylake/skl-sst-dsp.h @@ -178,6 +178,13 @@ enum skl_hw_info_type { SKL_EBB_SIZE_BYTES, }; +struct skl_fw_version { + u16 major; + u16 minor; + u16 hotfix; + u16 build; +}; + /* DSP Core state */ enum skl_dsp_states { SKL_DSP_RUNNING = 1, @@ -193,6 +200,19 @@ enum skl_dsp_d0i3_states { SKL_DSP_D0I3_STREAMING = 1, }; +struct skl_dsp_ops { + int id; + unsigned int num_cores; + struct skl_dsp_loader_ops (*loader_ops)(void); + struct skl_fw_version min_fw_ver; + int (*init)(struct device *dev, void __iomem *mmio_base, int irq, + const char *fw_name, const struct skl_dsp_ops *dsp_ops, + struct skl_sst **skl_sst, void *ptr); + int (*init_fw)(struct device *dev, struct skl_sst *ctx); + void (*cleanup)(struct device *dev, struct skl_sst *ctx); + void (*do_recovery)(struct skl *skl); +}; + struct skl_dsp_fw_ops { int (*load_fw)(struct sst_dsp *ctx); /* FW module parser/loader */ @@ -294,10 +314,10 @@ int skl_dsp_put_core(struct sst_dsp *ctx, unsigned int core_id); int skl_dsp_boot(struct sst_dsp *ctx); int skl_sst_dsp_init(struct device *dev, void __iomem *mmio_base, int irq, - const char *fw_name, struct skl_dsp_loader_ops dsp_ops, + const char *fw_name, const struct skl_dsp_ops *dsp_ops, struct skl_sst **dsp, void *ptr); int bxt_sst_dsp_init(struct device *dev, void __iomem *mmio_base, int irq, - const char *fw_name, struct skl_dsp_loader_ops dsp_ops, + const char *fw_name, const struct skl_dsp_ops *dsp_ops, struct skl_sst **dsp, void *ptr); int skl_sst_init_fw(struct device *dev, struct skl_sst *ctx); int bxt_sst_init_fw(struct device *dev, struct skl_sst *ctx); @@ -331,6 +351,8 @@ void skl_release_library(struct skl_lib_info *linfo, int lib_count); int skl_get_firmware_configuration(struct sst_dsp *ctx); int skl_get_hardware_configuration(struct sst_dsp *ctx); +int skl_validate_fw_version(struct skl_sst *skl); + int bxt_set_dsp_D0i0(struct sst_dsp *ctx); int bxt_schedule_dsp_D0i3(struct sst_dsp *ctx); @@ -344,4 +366,5 @@ void skl_module_sysfs_exit(struct skl_sst *ctx); int skl_dsp_cb_event(struct skl_sst *ctx, unsigned int event, struct skl_notify_data *notify_data); +const struct skl_dsp_ops *skl_get_dsp_ops(int pci_id); #endif /*__SKL_SST_DSP_H__*/ diff --git a/sound/soc/intel/skylake/skl-sst-ipc.h b/sound/soc/intel/skylake/skl-sst-ipc.h index ee32aaa68490..4eef4a386996 100644 --- a/sound/soc/intel/skylake/skl-sst-ipc.h +++ b/sound/soc/intel/skylake/skl-sst-ipc.h @@ -152,13 +152,6 @@ struct bra_conf { struct skl_pipe *cp_pipe; }; -struct skl_fw_version { - u16 major; - u16 minor; - u16 hotfix; - u16 build; -}; - struct skl_dma_buff_config { u32 min_size_bytes; u32 max_size_bytes; diff --git a/sound/soc/intel/skylake/skl-sst-utils.c b/sound/soc/intel/skylake/skl-sst-utils.c index 7c867426b39b..a495cea49b05 100644 --- a/sound/soc/intel/skylake/skl-sst-utils.c +++ b/sound/soc/intel/skylake/skl-sst-utils.c @@ -1246,3 +1246,28 @@ int skl_module_sysfs_init(struct skl_sst *ctx, struct kobject *kobj) return ret; } EXPORT_SYMBOL_GPL(skl_module_sysfs_init); + +int skl_validate_fw_version(struct skl_sst *skl) +{ + struct skl_fw_version *fw_version = &skl->fw_property.version; + const struct skl_dsp_ops *ops = skl->dsp_ops; + + dev_info(skl->dev, "ADSP FW Version: %d.%d.%d.%d\n", + fw_version->major, fw_version->minor, + fw_version->hotfix, fw_version->build); + + + if (ops->min_fw_ver.major == fw_version->major && + ops->min_fw_ver.minor == fw_version->minor && + ops->min_fw_ver.hotfix == fw_version->hotfix && + ops->min_fw_ver.build <= fw_version->build) + return 0; + + dev_err(skl->dev, "Incorrect ADSP FW version = %d.%d.%d.%d, minimum supported FW version = %d.%d.%d.%d\n", + fw_version->major, fw_version->minor, + fw_version->hotfix, fw_version->build, + ops->min_fw_ver.major, ops->min_fw_ver.minor, + ops->min_fw_ver.hotfix, ops->min_fw_ver.build); + + return -EINVAL; +} diff --git a/sound/soc/intel/skylake/skl-sst.c b/sound/soc/intel/skylake/skl-sst.c index 277e972eb858..77cf3f2ba95f 100644 --- a/sound/soc/intel/skylake/skl-sst.c +++ b/sound/soc/intel/skylake/skl-sst.c @@ -152,6 +152,17 @@ static int skl_load_base_firmware(struct sst_dsp *ctx) goto transfer_firmware_failed; } + ret = skl_get_firmware_configuration(ctx); + if (ret < 0) { + dev_err(ctx->dev, "FW version query failed\n"); + goto skl_load_base_firmware_failed; + } + + ret = skl_validate_fw_version(skl); + if (ret < 0) { + ret = -EIO; + goto skl_load_base_firmware_failed; + } dev_dbg(ctx->dev, "Download firmware successful%d\n", ret); skl->fw_loaded = true; } @@ -528,20 +539,23 @@ static struct sst_dsp_device skl_dev = { }; int skl_sst_dsp_init(struct device *dev, void __iomem *mmio_base, int irq, - const char *fw_name, struct skl_dsp_loader_ops dsp_ops, + const char *fw_name, const struct skl_dsp_ops *dsp_ops, struct skl_sst **dsp, void *ptr) { struct skl_sst *skl; struct sst_dsp *sst; + struct skl_dsp_loader_ops loader_ops; int ret; - ret = skl_sst_ctx_init(dev, irq, fw_name, dsp_ops, dsp, &skl_dev); + loader_ops = dsp_ops->loader_ops(); + ret = skl_sst_ctx_init(dev, irq, fw_name, loader_ops, dsp, &skl_dev); if (ret < 0) { dev_err(dev, "%s: no device\n", __func__); return ret; } skl = *dsp; + skl->dsp_ops = dsp_ops; sst = skl->dsp; sst->addr.lpe = mmio_base; sst->addr.shim = mmio_base; diff --git a/sound/soc/intel/skylake/skl.h b/sound/soc/intel/skylake/skl.h index 01053c4cdf6e..d00a8b19d054 100644 --- a/sound/soc/intel/skylake/skl.h +++ b/sound/soc/intel/skylake/skl.h @@ -188,19 +188,6 @@ struct skl_machine_pdata { bool use_tplg_pcm; /* use dais and dai links from topology */ }; -struct skl_dsp_ops { - int id; - unsigned int num_cores; - struct skl_dsp_loader_ops (*loader_ops)(void); - int (*init)(struct device *dev, void __iomem *mmio_base, int irq, - const char *fw_name, - struct skl_dsp_loader_ops loader_ops, - struct skl_sst **skl_sst, void *ptr); - int (*init_fw)(struct device *dev, struct skl_sst *ctx); - void (*cleanup)(struct device *dev, struct skl_sst *ctx); - void (*do_recovery)(struct skl *skl); -}; - int skl_platform_unregister(struct device *dev); int skl_platform_register(struct device *dev); @@ -223,7 +210,6 @@ int skl_suspend_late_dsp(struct skl *skl); int skl_suspend_dsp(struct skl *skl); int skl_resume_dsp(struct skl *skl); void skl_cleanup_resources(struct skl *skl); -const struct skl_dsp_ops *skl_get_dsp_ops(int pci_id); void skl_update_d0i3c(struct device *dev, bool enable); int skl_nhlt_create_sysfs(struct skl *skl); void skl_nhlt_remove_sysfs(struct skl *skl); From 3e4def831e78dfab4b3cb2abf2748d0f623f8ed6 Mon Sep 17 00:00:00 2001 From: Cezary Rojewski Date: Mon, 3 Sep 2018 14:42:21 +0200 Subject: [PATCH 0885/1103] ASoC: Intel: Replace hdac_ext_bus usage with hdac_bus. This patch updates CoE against following change: ALSA: hdac: Remove usage of struct hdac_ext_bus and use hda_bus instead. This patch also fixes NULL dereference panic within soc_dai_hw_params introduced by lastedsoc-framework changes for CoE. Change-Id: I06600e84714863ec37f551ed5373830dec10c29c Signed-off-by: Cezary Rojewski --- include/sound/hdaudio_ext.h | 2 +- sound/hda/ext/hdac_ext_stream.c | 18 +++++++++--------- sound/soc/intel/skylake/skl-compress.c | 17 ++++++++--------- sound/soc/intel/skylake/skl-compress.h | 6 +++--- sound/soc/intel/skylake/skl-messages.c | 10 ++++------ sound/soc/intel/skylake/skl-nhlt.c | 6 +++--- sound/soc/intel/skylake/skl-pcm.c | 18 +++++++++--------- sound/soc/intel/skylake/skl-probe.c | 26 ++++++++++++-------------- sound/soc/intel/skylake/skl-topology.c | 18 +++++++++--------- sound/soc/intel/skylake/skl-topology.h | 4 ++-- sound/soc/intel/skylake/skl.c | 17 ++++++++++------- sound/soc/soc-pcm.c | 2 +- 12 files changed, 71 insertions(+), 73 deletions(-) diff --git a/include/sound/hdaudio_ext.h b/include/sound/hdaudio_ext.h index 62181677f009..5ebf57fa778d 100644 --- a/include/sound/hdaudio_ext.h +++ b/include/sound/hdaudio_ext.h @@ -90,7 +90,7 @@ struct hdac_ext_stream *snd_hdac_ext_stream_assign(struct hdac_bus *bus, struct snd_pcm_substream *substream, int type); struct hdac_ext_stream * -hdac_ext_host_stream_compr_assign(struct hdac_ext_bus *ebus, +hdac_ext_host_stream_compr_assign(struct hdac_bus *bus, struct snd_compr_stream *substream, int direction); void snd_hdac_ext_stream_release(struct hdac_ext_stream *azx_dev, int type); diff --git a/sound/hda/ext/hdac_ext_stream.c b/sound/hda/ext/hdac_ext_stream.c index 492f85841ac0..fa7b80fcba9d 100644 --- a/sound/hda/ext/hdac_ext_stream.c +++ b/sound/hda/ext/hdac_ext_stream.c @@ -552,21 +552,21 @@ int snd_hdac_ext_stream_set_lpib(struct hdac_ext_stream *stream, u32 value) return 0; } EXPORT_SYMBOL_GPL(snd_hdac_ext_stream_set_lpib); + struct hdac_ext_stream * -hdac_ext_host_stream_compr_assign(struct hdac_ext_bus *ebus, +hdac_ext_host_stream_compr_assign(struct hdac_bus *bus, struct snd_compr_stream *substream, int direction) { struct hdac_ext_stream *res = NULL; struct hdac_stream *stream = NULL; - struct hdac_bus *hbus = &ebus->bus; - if (!hbus->ppcap) { - dev_err(hbus->dev, "stream type not supported\n"); + if (!bus->ppcap) { + dev_err(bus->dev, "stream type not supported\n"); return NULL; } - list_for_each_entry(stream, &hbus->stream_list, list) { + list_for_each_entry(stream, &bus->stream_list, list) { struct hdac_ext_stream *hstream = container_of(stream, struct hdac_ext_stream, hstream); @@ -575,19 +575,19 @@ hdac_ext_host_stream_compr_assign(struct hdac_ext_bus *ebus, if (!stream->opened) { if (!hstream->decoupled) - snd_hdac_ext_stream_decouple(ebus, + snd_hdac_ext_stream_decouple(bus, hstream, true); res = hstream; break; } } if (res) { - spin_lock_irq(&hbus->reg_lock); + spin_lock_irq(&bus->reg_lock); res->hstream.opened = 1; res->hstream.running = 0; res->hstream.stream = substream; - spin_unlock_irq(&hbus->reg_lock); - dev_dbg(hbus->dev, "Stream tag = %d, index = %d\n", + spin_unlock_irq(&bus->reg_lock); + dev_dbg(bus->dev, "Stream tag = %d, index = %d\n", res->hstream.stream_tag, res->hstream.index); } return res; diff --git a/sound/soc/intel/skylake/skl-compress.c b/sound/soc/intel/skylake/skl-compress.c index 67f2b78812b2..d346b08ad044 100644 --- a/sound/soc/intel/skylake/skl-compress.c +++ b/sound/soc/intel/skylake/skl-compress.c @@ -27,13 +27,12 @@ #include "skl.h" #include "skl-compress.h" -struct hdac_ext_bus *get_bus_compr_ctx(struct snd_compr_stream *substream) +struct hdac_bus *get_bus_compr_ctx(struct snd_compr_stream *substream) { struct hdac_ext_stream *stream = get_hdac_ext_compr_stream(substream); struct hdac_stream *hstream = hdac_stream(stream); - struct hdac_bus *bus = hstream->bus; - return hbus_to_ebus(bus); + return hstream->bus; } void skl_set_compr_runtime_buffer(struct snd_compr_stream *substream, @@ -55,10 +54,10 @@ void skl_set_compr_runtime_buffer(struct snd_compr_stream *substream, } int skl_compr_malloc_pages(struct snd_compr_stream *substream, - struct hdac_ext_bus *ebus, size_t size) + struct hdac_bus *bus, size_t size) { struct snd_dma_buffer *dmab = NULL; - struct skl *skl = ebus_to_skl(ebus); + struct skl *skl = bus_to_skl(bus); dmab = kzalloc(sizeof(*dmab), GFP_KERNEL); if (!dmab) @@ -69,7 +68,7 @@ int skl_compr_malloc_pages(struct snd_compr_stream *substream, if (snd_dma_alloc_pages(substream->dma_buffer.dev.type, substream->dma_buffer.dev.dev, size, dmab) < 0) { - dev_err(ebus_to_hbus(ebus)->dev, + dev_err(bus->dev, "Error in snd_dma_alloc_pages\n"); kfree(dmab); return -ENOMEM; @@ -79,7 +78,7 @@ int skl_compr_malloc_pages(struct snd_compr_stream *substream, return 1; } -int skl_substream_alloc_compr_pages(struct hdac_ext_bus *ebus, +int skl_substream_alloc_compr_pages(struct hdac_bus *bus, struct snd_compr_stream *substream, size_t size) { @@ -90,10 +89,10 @@ int skl_substream_alloc_compr_pages(struct hdac_ext_bus *ebus, hdac_stream(stream)->period_bytes = 0; hdac_stream(stream)->format_val = 0; - ret = skl_compr_malloc_pages(substream, ebus, size); + ret = skl_compr_malloc_pages(substream, bus, size); if (ret < 0) return ret; - ebus->bus.io_ops->mark_pages_uc(snd_pcm_get_dma_buf(substream), true); + bus->io_ops->mark_pages_uc(snd_pcm_get_dma_buf(substream), true); return ret; } diff --git a/sound/soc/intel/skylake/skl-compress.h b/sound/soc/intel/skylake/skl-compress.h index abfff2d27f14..4b7f9d58e513 100644 --- a/sound/soc/intel/skylake/skl-compress.h +++ b/sound/soc/intel/skylake/skl-compress.h @@ -20,12 +20,12 @@ */ #ifndef __SKL_COMPRESS_H__ #define __SKL_COMPRESS_H__ -struct hdac_ext_bus *get_bus_compr_ctx(struct snd_compr_stream *substream); +struct hdac_bus *get_bus_compr_ctx(struct snd_compr_stream *substream); void skl_set_compr_runtime_buffer(struct snd_compr_stream *substream, struct snd_dma_buffer *bufp, size_t size); int skl_compr_malloc_pages(struct snd_compr_stream *substream, - struct hdac_ext_bus *ebus, size_t size); -int skl_substream_alloc_compr_pages(struct hdac_ext_bus *ebus, + struct hdac_bus *bus, size_t size); +int skl_substream_alloc_compr_pages(struct hdac_bus *bus, struct snd_compr_stream *substream, size_t size); int skl_compr_free_pages(struct snd_compr_stream *substream); diff --git a/sound/soc/intel/skylake/skl-messages.c b/sound/soc/intel/skylake/skl-messages.c index f66ae9d702bc..74833f0cddfb 100644 --- a/sound/soc/intel/skylake/skl-messages.c +++ b/sound/soc/intel/skylake/skl-messages.c @@ -380,8 +380,7 @@ void skl_do_recovery(struct skl *skl) const struct skl_dsp_ops *ops; struct snd_soc_card *card; struct hdac_stream *azx_dev; - struct hdac_ext_bus *ebus = &skl->ebus; - struct hdac_bus *bus = ebus_to_hbus(ebus); + struct hdac_bus *bus = skl_to_bus(skl); struct snd_pcm_substream *substream = NULL; struct hdac_ext_stream *stream; @@ -400,7 +399,7 @@ void skl_do_recovery(struct skl *skl) substream = azx_dev->substream; stream = stream_to_hdac_ext_stream(azx_dev); snd_hdac_ext_stream_release(stream, - skl_get_host_stream_type(ebus)); + skl_get_host_stream_type(bus)); } break; } @@ -409,8 +408,8 @@ void skl_do_recovery(struct skl *skl) if (ops->init_fw(soc_component->dev, skl->skl_sst) < 0) dev_err(skl->skl_sst->dev, "Recovery failed\n"); if (substream != NULL) { - stream = snd_hdac_ext_stream_assign(ebus, substream, - skl_get_host_stream_type(ebus)); + stream = snd_hdac_ext_stream_assign(bus, substream, + skl_get_host_stream_type(bus)); } snd_soc_resume(card->dev); skl->skl_sst->dsp->is_recovery = false; @@ -1266,7 +1265,6 @@ int skl_init_dsp(struct skl *skl) { void __iomem *mmio_base; struct hdac_bus *bus = skl_to_bus(skl); - struct skl_dsp_loader_ops loader_ops; int irq = bus->irq; const struct skl_dsp_ops *ops; struct skl_dsp_cores *cores; diff --git a/sound/soc/intel/skylake/skl-nhlt.c b/sound/soc/intel/skylake/skl-nhlt.c index 284e765eebad..73d6c9841d91 100644 --- a/sound/soc/intel/skylake/skl-nhlt.c +++ b/sound/soc/intel/skylake/skl-nhlt.c @@ -156,15 +156,15 @@ skl_get_nhlt_specific_cfg(struct skl *skl, u32 instance, u8 link_type, u8 s_fmt, u8 num_ch, u32 s_rate, u8 dir, u8 dev_type) { struct nhlt_specific_cfg *cfg = NULL; - struct hdac_ext_bus *ebus = &skl->ebus; + struct hdac_bus *bus = &skl->hbus; /* update the blob based on virtual bus_id*/ if (!skl->nhlt_override) { - dev_warn(ebus_to_hbus(ebus)->dev, "Querying NHLT blob from ACPI NHLT table !!\n"); + dev_warn(bus->dev, "Querying NHLT blob from ACPI NHLT table !!\n"); cfg = skl_get_ep_blob(skl, instance, link_type, s_fmt, num_ch, s_rate, dir, dev_type); } else { - dev_warn(ebus_to_hbus(ebus)->dev, "Querying NHLT blob from Debugfs!!\n"); + dev_warn(bus->dev, "Querying NHLT blob from Debugfs!!\n"); cfg = skl_nhlt_get_debugfs_blob(skl->debugfs, link_type, instance, dir); } diff --git a/sound/soc/intel/skylake/skl-pcm.c b/sound/soc/intel/skylake/skl-pcm.c index 6da6b492b70b..fe4cd70700f7 100644 --- a/sound/soc/intel/skylake/skl-pcm.c +++ b/sound/soc/intel/skylake/skl-pcm.c @@ -95,7 +95,7 @@ static int skl_substream_alloc_pages(struct hdac_bus *bus, ret = snd_pcm_lib_malloc_pages(substream, size); if (ret < 0) return ret; - ebus->bus.io_ops->mark_pages_uc(snd_pcm_get_dma_buf(substream), true); + bus->io_ops->mark_pages_uc(snd_pcm_get_dma_buf(substream), true); return ret; } @@ -118,7 +118,7 @@ static void skl_set_pcm_constrains(struct hdac_bus *bus, 20, 178000000); } -static enum hdac_ext_stream_type skl_get_host_stream_type(struct hdac_bus *bus) +enum hdac_ext_stream_type skl_get_host_stream_type(struct hdac_bus *bus) { if (bus->ppcap) return HDAC_EXT_STREAM_TYPE_HOST; @@ -212,7 +212,7 @@ int skl_pcm_host_dma_prepare(struct device *dev, struct skl_pipe_params *params) runtime = hdac_stream(stream)->substream->runtime; /* enable SPIB if no_rewinds flag is set */ if (runtime->no_rewinds) - snd_hdac_ext_stream_spbcap_enable(ebus, 1, hstream->index); + snd_hdac_ext_stream_spbcap_enable(bus, 1, hstream->index); hdac_stream(stream)->prepared = 1; @@ -442,8 +442,8 @@ static int skl_pcm_hw_free(struct snd_pcm_substream *substream, mconfig = skl_tplg_fe_get_cpr_module(dai, substream->stream); if (runtime->no_rewinds) { - snd_hdac_ext_stream_set_spib(ebus, stream, 0); - snd_hdac_ext_stream_spbcap_enable(ebus, 0, hstream->index); + snd_hdac_ext_stream_set_spib(bus, stream, 0); + snd_hdac_ext_stream_spbcap_enable(bus, 0, hstream->index); } if (mconfig) { ret = skl_reset_pipe(skl->skl_sst, mconfig->pipe); @@ -863,8 +863,8 @@ static struct skl_sst *skl_get_sst_compr(struct snd_compr_stream *stream) { struct snd_soc_pcm_runtime *rtd = stream->private_data; struct snd_soc_dai *dai = rtd->cpu_dai; - struct hdac_ext_bus *ebus = dev_get_drvdata(dai->dev); - struct skl *skl = ebus_to_skl(ebus); + struct hdac_bus *bus = dev_get_drvdata(dai->dev); + struct skl *skl = bus_to_skl(bus); struct skl_sst *sst = skl->skl_sst; return sst; @@ -1589,7 +1589,7 @@ static int skl_platform_pcm_trigger(struct snd_pcm_substream *substream, /* update SPIB register with appl position */ static int skl_platform_ack(struct snd_pcm_substream *substream) { - struct hdac_ext_bus *ebus = get_bus_ctx(substream); + struct hdac_bus *bus = get_bus_ctx(substream); struct hdac_ext_stream *estream = get_hdac_ext_stream(substream); struct snd_pcm_runtime *runtime = substream->runtime; ssize_t appl_pos, buf_size; @@ -1606,7 +1606,7 @@ static int skl_platform_ack(struct snd_pcm_substream *substream) /* Allowable value for SPIB is 1 byte to max buffer size */ spib = (spib == 0) ? buf_size : spib; - snd_hdac_ext_stream_set_spib(ebus, estream, spib); + snd_hdac_ext_stream_set_spib(bus, estream, spib); return 0; } diff --git a/sound/soc/intel/skylake/skl-probe.c b/sound/soc/intel/skylake/skl-probe.c index 2d9d0ea6c907..166ffa1e0d3e 100644 --- a/sound/soc/intel/skylake/skl-probe.c +++ b/sound/soc/intel/skylake/skl-probe.c @@ -87,7 +87,7 @@ static int set_injector_stream(struct hdac_ext_stream *stream, int skl_probe_compr_open(struct snd_compr_stream *substream, struct snd_soc_dai *dai) { - struct hdac_ext_bus *ebus = dev_get_drvdata(dai->dev); + struct hdac_bus *bus = dev_get_drvdata(dai->dev); struct hdac_ext_stream *stream = NULL; struct snd_compr_runtime *runtime = substream->runtime; struct skl *skl = get_skl_ctx(dai->dev); @@ -107,7 +107,7 @@ int skl_probe_compr_open(struct snd_compr_stream *substream, * correct substream pointer later when open is indeed for * extractor. */ - pconfig->estream = hdac_ext_host_stream_compr_assign(ebus, + pconfig->estream = hdac_ext_host_stream_compr_assign(bus, NULL, SND_COMPRESS_CAPTURE); if (!pconfig->estream) { @@ -120,7 +120,7 @@ int skl_probe_compr_open(struct snd_compr_stream *substream, } if (substream->direction == SND_COMPRESS_PLAYBACK) { - stream = hdac_ext_host_stream_compr_assign(ebus, substream, + stream = hdac_ext_host_stream_compr_assign(bus, substream, SND_COMPRESS_PLAYBACK); if (stream == NULL) { if ((pconfig->i_refc + pconfig->e_refc) == 0) @@ -154,7 +154,7 @@ int skl_probe_compr_set_params(struct snd_compr_stream *substream, struct snd_soc_dai *dai) { - struct hdac_ext_bus *ebus = dev_get_drvdata(dai->dev); + struct hdac_bus *bus = dev_get_drvdata(dai->dev); struct hdac_ext_stream *stream = get_hdac_ext_compr_stream(substream); struct snd_compr_runtime *runtime = substream->runtime; struct skl *skl = get_skl_ctx(dai->dev); @@ -172,7 +172,7 @@ int skl_probe_compr_set_params(struct snd_compr_stream *substream, return 0; } - ret = skl_substream_alloc_compr_pages(ebus, substream, + ret = skl_substream_alloc_compr_pages(bus, substream, runtime->fragments*runtime->fragment_size); if (ret < 0) return ret; @@ -215,7 +215,7 @@ int skl_probe_compr_set_params(struct snd_compr_stream *substream, } #if USE_SPIB - snd_hdac_ext_stream_spbcap_enable(ebus, 1, hdac_stream(stream)->index); + snd_hdac_ext_stream_spbcap_enable(bus, 1, hdac_stream(stream)->index); #endif return 0; } @@ -224,7 +224,7 @@ int skl_probe_compr_close(struct snd_compr_stream *substream, struct snd_soc_dai *dai) { struct hdac_ext_stream *stream = get_hdac_ext_compr_stream(substream); - struct hdac_ext_bus *ebus = dev_get_drvdata(dai->dev); + struct hdac_bus *bus = dev_get_drvdata(dai->dev); struct skl *skl = get_skl_ctx(dai->dev); struct skl_probe_config *pconfig = &skl->skl_sst->probe_config; struct skl_module_cfg *mconfig = pconfig->w->priv; @@ -233,7 +233,7 @@ int skl_probe_compr_close(struct snd_compr_stream *substream, dev_dbg(dai->dev, "%s: %s\n", __func__, dai->name); #if USE_SPIB - snd_hdac_ext_stream_spbcap_enable(ebus, 0, hdac_stream(stream)->index); + snd_hdac_ext_stream_spbcap_enable(bus, 0, hdac_stream(stream)->index); #endif if ((pconfig->i_refc + pconfig->e_refc) == 0) goto probe_uninit; @@ -282,7 +282,7 @@ int skl_probe_compr_close(struct snd_compr_stream *substream, snd_hdac_stream_cleanup(hdac_stream(stream)); hdac_stream(stream)->prepared = 0; - skl_substream_free_compr_pages(ebus_to_hbus(ebus), substream); + skl_substream_free_compr_pages(bus, substream); /* Release the particular injector/extractor stream getting closed */ snd_hdac_ext_stream_release(stream, HDAC_EXT_STREAM_TYPE_HOST); @@ -293,8 +293,7 @@ int skl_probe_compr_close(struct snd_compr_stream *substream, int skl_probe_compr_ack(struct snd_compr_stream *substream, size_t bytes, struct snd_soc_dai *dai) { - struct hdac_ext_bus *ebus = dev_get_drvdata(dai->dev); - struct hdac_bus *bus = ebus_to_hbus(ebus); + struct hdac_bus *bus = dev_get_drvdata(dai->dev); u64 __maybe_unused new_spib_pos; struct snd_compr_runtime *runtime = substream->runtime; u64 spib_pos = div64_u64(runtime->total_bytes_available, @@ -387,7 +386,7 @@ int skl_probe_compr_copy(struct snd_compr_stream *stream, char __user *buf, #if USE_SPIB spib_pos = (offset + retval)%stream->runtime->dma_bytes; - snd_hdac_ext_stream_set_spib(ebus, estream, spib_pos); + snd_hdac_ext_stream_set_spib(bus, estream, spib_pos); #endif return retval; @@ -397,8 +396,7 @@ int skl_probe_compr_copy(struct snd_compr_stream *stream, char __user *buf, int skl_probe_compr_trigger(struct snd_compr_stream *substream, int cmd, struct snd_soc_dai *dai) { - struct hdac_ext_bus *ebus = get_bus_compr_ctx(substream); - struct hdac_bus *bus = ebus_to_hbus(ebus); + struct hdac_bus *bus = get_bus_compr_ctx(substream); struct hdac_ext_stream *stream; struct hdac_stream *hstr; int start; diff --git a/sound/soc/intel/skylake/skl-topology.c b/sound/soc/intel/skylake/skl-topology.c index c80ad4558ba6..9b6e3bfc464d 100644 --- a/sound/soc/intel/skylake/skl-topology.c +++ b/sound/soc/intel/skylake/skl-topology.c @@ -2051,8 +2051,8 @@ int skl_tplg_dsp_log_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); - struct hdac_ext_bus *ebus = snd_soc_component_get_drvdata(component); - struct skl *skl = ebus_to_skl(ebus); + struct hdac_bus *bus = snd_soc_component_get_drvdata(component); + struct skl *skl = bus_to_skl(bus); ucontrol->value.integer.value[0] = get_dsp_log_priority(skl); @@ -2063,8 +2063,8 @@ int skl_tplg_dsp_log_set(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); - struct hdac_ext_bus *ebus = snd_soc_component_get_drvdata(component); - struct skl *skl = ebus_to_skl(ebus); + struct hdac_bus *bus = snd_soc_component_get_drvdata(component); + struct skl *skl = bus_to_skl(bus); update_dsp_log_priority(ucontrol->value.integer.value[0], skl); @@ -2260,8 +2260,8 @@ static int skl_tplg_multi_config_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); - struct hdac_ext_bus *ebus = snd_soc_component_get_drvdata(component); - struct skl *skl = ebus_to_skl(ebus); + struct hdac_bus *bus = snd_soc_component_get_drvdata(component); + struct skl *skl = bus_to_skl(bus); struct skl_pipeline *ppl; struct skl_pipe *pipe = NULL; u32 *pipe_id; @@ -2284,8 +2284,8 @@ static int skl_tplg_multi_config_set(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); - struct hdac_ext_bus *ebus = snd_soc_component_get_drvdata(component); - struct skl *skl = ebus_to_skl(ebus); + struct hdac_bus *bus = snd_soc_component_get_drvdata(component); + struct skl *skl = bus_to_skl(bus); struct skl_pipeline *ppl; struct skl_pipe *pipe = NULL; struct soc_enum *ec = (struct soc_enum *)kcontrol->private_value; @@ -4378,7 +4378,7 @@ static int skl_tplg_widget_load(struct snd_soc_component *cmpnt, int index, struct snd_soc_dapm_widget *w, struct snd_soc_tplg_dapm_widget *tplg_w) { - int ret; + int i, ret; struct hdac_bus *bus = snd_soc_component_get_drvdata(cmpnt); struct skl *skl = bus_to_skl(bus); struct skl_module_cfg *mconfig; diff --git a/sound/soc/intel/skylake/skl-topology.h b/sound/soc/intel/skylake/skl-topology.h index c0ef5e78dbfa..da3c8d19033b 100644 --- a/sound/soc/intel/skylake/skl-topology.h +++ b/sound/soc/intel/skylake/skl-topology.h @@ -553,7 +553,7 @@ int skl_dsp_set_dma_control(struct skl_sst *ctx, u32 *caps, void skl_tplg_set_be_dmic_config(struct snd_soc_dai *dai, struct skl_pipe_params *params, int stream); int skl_tplg_init(struct snd_soc_component *component, - struct hdac_bus *ebus); + struct hdac_bus *bus); struct skl_module_cfg *skl_tplg_fe_get_cpr_module( struct snd_soc_dai *dai, int stream); int skl_tplg_update_pipe_params(struct device *dev, @@ -617,7 +617,7 @@ int skl_pcm_host_dma_prepare(struct device *dev, struct skl_pipe_params *params); int skl_pcm_link_dma_prepare(struct device *dev, struct skl_pipe_params *params); -enum hdac_ext_stream_type skl_get_host_stream_type(struct hdac_ext_bus *ebus); +enum hdac_ext_stream_type skl_get_host_stream_type(struct hdac_bus *bus); int skl_dai_load(struct snd_soc_component *cmp, int index, struct snd_soc_dai_driver *dai_drv, diff --git a/sound/soc/intel/skylake/skl.c b/sound/soc/intel/skylake/skl.c index 31d214a0db85..8d7305133424 100644 --- a/sound/soc/intel/skylake/skl.c +++ b/sound/soc/intel/skylake/skl.c @@ -42,6 +42,10 @@ #include "skl-sst-ipc.h" #include "skl-topology.h" +#if !IS_ENABLED(CONFIG_SND_SOC_INTEL_CNL_FPGA) +static struct skl_machine_pdata skl_dmic_data; +#endif + /* * initialize the PCI registers */ @@ -211,9 +215,8 @@ static void skl_get_total_bytes_transferred(struct hdac_stream *hstr) * skl_dum_set - Set the DUM bit in EM2 register to fix the IP bug * of incorrect postion reporting for capture stream. */ -static void skl_dum_set(struct hdac_ext_bus *ebus) +static void skl_dum_set(struct hdac_bus *bus) { - struct hdac_bus *bus = ebus_to_hbus(ebus); u32 reg; u8 val; @@ -248,7 +251,7 @@ static void skl_stream_update(struct hdac_bus *bus, struct hdac_stream *hstr) static irqreturn_t skl_interrupt(int irq, void *dev_id) { struct hdac_bus *bus = dev_id; - struct skl *skl = bus_to_skl(ebus); + struct skl *skl = bus_to_skl(bus); u32 status; u32 mask, int_enable; int ret = IRQ_NONE; @@ -304,7 +307,7 @@ static irqreturn_t skl_threaded_handler(int irq, void *dev_id) snd_hdac_bus_handle_stream_irq(bus, status, skl_stream_update); /* Re-enable stream interrupts */ - mask = (0x1 << ebus->num_streams) - 1; + mask = (0x1 << bus->num_streams) - 1; spin_lock_irqsave(&bus->reg_lock, flags); int_enable = snd_hdac_chip_readl(bus, INTCTL); snd_hdac_chip_writel(bus, INTCTL, (int_enable | mask)); @@ -875,7 +878,7 @@ static int skl_init_recovery(struct skl *skl) monitor->interval = SKL_MIN_TIME_INTERVAL; monitor->intervals = devm_kzalloc(&skl->pci->dev, - skl->ebus.num_streams * sizeof(u32), + skl->hbus.num_streams * sizeof(u32), GFP_KERNEL); if (!monitor->intervals) return -ENOMEM; @@ -982,7 +985,7 @@ static int skl_first_init(struct hdac_bus *bus) /* initialize chip */ skl_init_pci(skl); - skl_dum_set(ebus); + skl_dum_set(bus); return skl_init_chip(bus, true); } @@ -1043,7 +1046,7 @@ static int skl_probe(struct pci_dev *pci, nhlt_continue: #endif - pci_set_drvdata(skl->pci, ebus); + pci_set_drvdata(skl->pci, bus); #if !IS_ENABLED(CONFIG_SND_SOC_INTEL_CNL_FPGA) skl_dmic_data.dmic_num = skl_get_dmic_geo(skl); diff --git a/sound/soc/soc-pcm.c b/sound/soc/soc-pcm.c index afcb2cfaf551..2a71d8e04f5b 100644 --- a/sound/soc/soc-pcm.c +++ b/sound/soc/soc-pcm.c @@ -863,7 +863,7 @@ int soc_dai_hw_params(struct snd_pcm_substream *substream, int ret; /* perform any topology hw_params fixups before DAI */ - if (rtd->dai_link->be_hw_params_fixup) { + if (rtd && rtd->dai_link->be_hw_params_fixup) { ret = rtd->dai_link->be_hw_params_fixup(rtd, params); if (ret < 0) { dev_err(rtd->dev, From b46379d081f5dfa5c540133ea2b69d5c50955157 Mon Sep 17 00:00:00 2001 From: Zhao Yakui Date: Fri, 14 Sep 2018 16:10:16 +0800 Subject: [PATCH 0886/1103] Kernel/VHM: Rename acpi_generic_address in acrn_common.h to avoid redefinition Currently the acpi_generic_address is defined in acrn_common.h. And it is also defined in include/linux/acpi.h. If the two files are included by one driver, it will complain the redefinition of acpi_generic_address. So it is renamed to avoid the redefinition conflict. Signed-off-by: Zhao Yakui --- include/linux/vhm/acrn_common.h | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/include/linux/vhm/acrn_common.h b/include/linux/vhm/acrn_common.h index 69499245a994..179957d273e9 100644 --- a/include/linux/vhm/acrn_common.h +++ b/include/linux/vhm/acrn_common.h @@ -481,7 +481,7 @@ struct acrn_vm_pci_msix_remap { #define SPACE_PLATFORM_COMM 10 #define SPACE_FFixedHW 0x7F -struct acpi_generic_address { +struct acrn_generic_address { uint8_t space_id; uint8_t bit_width; uint8_t bit_offset; @@ -490,7 +490,7 @@ struct acpi_generic_address { } __attribute__((aligned(8))); struct cpu_cx_data { - struct acpi_generic_address cx_reg; + struct acrn_generic_address cx_reg; uint8_t type; uint32_t latency; uint64_t power; @@ -512,10 +512,10 @@ struct acpi_sstate_pkg { } __attribute__((aligned(8))); struct acpi_sstate_data { - struct acpi_generic_address pm1a_evt; - struct acpi_generic_address pm1b_evt; - struct acpi_generic_address pm1a_cnt; - struct acpi_generic_address pm1b_cnt; + struct acrn_generic_address pm1a_evt; + struct acrn_generic_address pm1b_evt; + struct acrn_generic_address pm1a_cnt; + struct acrn_generic_address pm1b_cnt; struct acpi_sstate_pkg s3_pkg; struct acpi_sstate_pkg s5_pkg; uint32_t *wake_vector_32; From 6e5b87daa27e21ef57ebf9e151e605f4cc90d9c1 Mon Sep 17 00:00:00 2001 From: Min He Date: Fri, 14 Sep 2018 16:10:16 +0800 Subject: [PATCH 0887/1103] drm/i915/gvt: some changes to support xengt/acrngt Set guest ppgtt entry in ppgtt_handle_guest_write_page_table_bytes() Change-Id: Ic6e46561f58eec1a17fb8d5bd9c5dd76d32b5350 Signed-off-by: Min He Reviewed-on: Reviewed-by: Jiang, Fei Reviewed-by: Dong, Eddie Tested-by: Dong, Eddie --- drivers/gpu/drm/i915/gvt/gtt.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/gpu/drm/i915/gvt/gtt.c b/drivers/gpu/drm/i915/gvt/gtt.c index 00aad8164dec..6262308c4a78 100644 --- a/drivers/gpu/drm/i915/gvt/gtt.c +++ b/drivers/gpu/drm/i915/gvt/gtt.c @@ -1688,6 +1688,8 @@ static int ppgtt_handle_guest_write_page_table_bytes( index = (pa & (PAGE_SIZE - 1)) >> info->gtt_entry_size_shift; + /* Set guest ppgtt entry. Optional for KVMGT, but MUST for XENGT. */ + intel_gvt_hypervisor_write_gpa(vgpu, pa, p_data, bytes); ppgtt_get_guest_entry(spt, &we, index); /* From 194ce811134e7403699af2d5eee879ab9430014c Mon Sep 17 00:00:00 2001 From: Min He Date: Fri, 14 Sep 2018 16:10:16 +0800 Subject: [PATCH 0888/1103] drm/i915/gvt: Refactored BXT plane registers Refactored BXT plane registers and implemented their plane register handlers. Signed-off-by: Min He Change-Id: Id72f6aa11db332008b73ce4ad1069386b9b81f35 Reviewed-on: Reviewed-by: He, Min Reviewed-by: Jiang, Fei Reviewed-by: Dong, Eddie Tested-by: Dong, Eddie --- drivers/gpu/drm/i915/gvt/handlers.c | 283 ++++++++++++--------------- drivers/gpu/drm/i915/gvt/interrupt.c | 4 + drivers/gpu/drm/i915/gvt/interrupt.h | 3 + drivers/gpu/drm/i915/gvt/reg.h | 7 +- 4 files changed, 133 insertions(+), 164 deletions(-) diff --git a/drivers/gpu/drm/i915/gvt/handlers.c b/drivers/gpu/drm/i915/gvt/handlers.c index 94c1089ecf59..e89d228ddc58 100644 --- a/drivers/gpu/drm/i915/gvt/handlers.c +++ b/drivers/gpu/drm/i915/gvt/handlers.c @@ -1959,71 +1959,71 @@ static int init_generic_mmio_info(struct intel_gvt *gvt) MMIO_D(_MMIO(0x70098), D_ALL); MMIO_D(_MMIO(0x7009c), D_ALL); - MMIO_D(DSPCNTR(PIPE_A), D_ALL); - MMIO_D(DSPADDR(PIPE_A), D_ALL); - MMIO_D(DSPSTRIDE(PIPE_A), D_ALL); - MMIO_D(DSPPOS(PIPE_A), D_ALL); - MMIO_D(DSPSIZE(PIPE_A), D_ALL); - MMIO_DH(DSPSURF(PIPE_A), D_ALL, NULL, pri_surf_mmio_write); - MMIO_D(DSPOFFSET(PIPE_A), D_ALL); - MMIO_D(DSPSURFLIVE(PIPE_A), D_ALL); - - MMIO_D(DSPCNTR(PIPE_B), D_ALL); - MMIO_D(DSPADDR(PIPE_B), D_ALL); - MMIO_D(DSPSTRIDE(PIPE_B), D_ALL); - MMIO_D(DSPPOS(PIPE_B), D_ALL); - MMIO_D(DSPSIZE(PIPE_B), D_ALL); - MMIO_DH(DSPSURF(PIPE_B), D_ALL, NULL, pri_surf_mmio_write); - MMIO_D(DSPOFFSET(PIPE_B), D_ALL); - MMIO_D(DSPSURFLIVE(PIPE_B), D_ALL); - - MMIO_D(DSPCNTR(PIPE_C), D_ALL); - MMIO_D(DSPADDR(PIPE_C), D_ALL); - MMIO_D(DSPSTRIDE(PIPE_C), D_ALL); - MMIO_D(DSPPOS(PIPE_C), D_ALL); - MMIO_D(DSPSIZE(PIPE_C), D_ALL); - MMIO_DH(DSPSURF(PIPE_C), D_ALL, NULL, pri_surf_mmio_write); - MMIO_D(DSPOFFSET(PIPE_C), D_ALL); - MMIO_D(DSPSURFLIVE(PIPE_C), D_ALL); - - MMIO_D(SPRCTL(PIPE_A), D_ALL); - MMIO_D(SPRLINOFF(PIPE_A), D_ALL); - MMIO_D(SPRSTRIDE(PIPE_A), D_ALL); - MMIO_D(SPRPOS(PIPE_A), D_ALL); - MMIO_D(SPRSIZE(PIPE_A), D_ALL); - MMIO_D(SPRKEYVAL(PIPE_A), D_ALL); - MMIO_D(SPRKEYMSK(PIPE_A), D_ALL); - MMIO_DH(SPRSURF(PIPE_A), D_ALL, NULL, spr_surf_mmio_write); - MMIO_D(SPRKEYMAX(PIPE_A), D_ALL); - MMIO_D(SPROFFSET(PIPE_A), D_ALL); - MMIO_D(SPRSCALE(PIPE_A), D_ALL); - MMIO_D(SPRSURFLIVE(PIPE_A), D_ALL); - - MMIO_D(SPRCTL(PIPE_B), D_ALL); - MMIO_D(SPRLINOFF(PIPE_B), D_ALL); - MMIO_D(SPRSTRIDE(PIPE_B), D_ALL); - MMIO_D(SPRPOS(PIPE_B), D_ALL); - MMIO_D(SPRSIZE(PIPE_B), D_ALL); - MMIO_D(SPRKEYVAL(PIPE_B), D_ALL); - MMIO_D(SPRKEYMSK(PIPE_B), D_ALL); - MMIO_DH(SPRSURF(PIPE_B), D_ALL, NULL, spr_surf_mmio_write); - MMIO_D(SPRKEYMAX(PIPE_B), D_ALL); - MMIO_D(SPROFFSET(PIPE_B), D_ALL); - MMIO_D(SPRSCALE(PIPE_B), D_ALL); - MMIO_D(SPRSURFLIVE(PIPE_B), D_ALL); - - MMIO_D(SPRCTL(PIPE_C), D_ALL); - MMIO_D(SPRLINOFF(PIPE_C), D_ALL); - MMIO_D(SPRSTRIDE(PIPE_C), D_ALL); - MMIO_D(SPRPOS(PIPE_C), D_ALL); - MMIO_D(SPRSIZE(PIPE_C), D_ALL); - MMIO_D(SPRKEYVAL(PIPE_C), D_ALL); - MMIO_D(SPRKEYMSK(PIPE_C), D_ALL); - MMIO_DH(SPRSURF(PIPE_C), D_ALL, NULL, spr_surf_mmio_write); - MMIO_D(SPRKEYMAX(PIPE_C), D_ALL); - MMIO_D(SPROFFSET(PIPE_C), D_ALL); - MMIO_D(SPRSCALE(PIPE_C), D_ALL); - MMIO_D(SPRSURFLIVE(PIPE_C), D_ALL); + MMIO_D(DSPCNTR(PIPE_A), D_BDW); + MMIO_D(DSPADDR(PIPE_A), D_BDW); + MMIO_D(DSPSTRIDE(PIPE_A), D_BDW); + MMIO_D(DSPPOS(PIPE_A), D_BDW); + MMIO_D(DSPSIZE(PIPE_A), D_BDW); + MMIO_DH(DSPSURF(PIPE_A), D_BDW, NULL, pri_surf_mmio_write); + MMIO_D(DSPOFFSET(PIPE_A), D_BDW); + MMIO_D(DSPSURFLIVE(PIPE_A), D_BDW); + + MMIO_D(DSPCNTR(PIPE_B), D_BDW); + MMIO_D(DSPADDR(PIPE_B), D_BDW); + MMIO_D(DSPSTRIDE(PIPE_B), D_BDW); + MMIO_D(DSPPOS(PIPE_B), D_BDW); + MMIO_D(DSPSIZE(PIPE_B), D_BDW); + MMIO_DH(DSPSURF(PIPE_B), D_BDW, NULL, pri_surf_mmio_write); + MMIO_D(DSPOFFSET(PIPE_B), D_BDW); + MMIO_D(DSPSURFLIVE(PIPE_B), D_BDW); + + MMIO_D(DSPCNTR(PIPE_C), D_BDW); + MMIO_D(DSPADDR(PIPE_C), D_BDW); + MMIO_D(DSPSTRIDE(PIPE_C), D_BDW); + MMIO_D(DSPPOS(PIPE_C), D_BDW); + MMIO_D(DSPSIZE(PIPE_C), D_BDW); + MMIO_DH(DSPSURF(PIPE_C), D_BDW, NULL, pri_surf_mmio_write); + MMIO_D(DSPOFFSET(PIPE_C), D_BDW); + MMIO_D(DSPSURFLIVE(PIPE_C), D_BDW); + + MMIO_D(SPRCTL(PIPE_A), D_BDW); + MMIO_D(SPRLINOFF(PIPE_A), D_BDW); + MMIO_D(SPRSTRIDE(PIPE_A), D_BDW); + MMIO_D(SPRPOS(PIPE_A), D_BDW); + MMIO_D(SPRSIZE(PIPE_A), D_BDW); + MMIO_D(SPRKEYVAL(PIPE_A), D_BDW); + MMIO_D(SPRKEYMSK(PIPE_A), D_BDW); + MMIO_DH(SPRSURF(PIPE_A), D_BDW, NULL, spr_surf_mmio_write); + MMIO_D(SPRKEYMAX(PIPE_A), D_BDW); + MMIO_D(SPROFFSET(PIPE_A), D_BDW); + MMIO_D(SPRSCALE(PIPE_A), D_BDW); + MMIO_D(SPRSURFLIVE(PIPE_A), D_BDW); + + MMIO_D(SPRCTL(PIPE_B), D_BDW); + MMIO_D(SPRLINOFF(PIPE_B), D_BDW); + MMIO_D(SPRSTRIDE(PIPE_B), D_BDW); + MMIO_D(SPRPOS(PIPE_B), D_BDW); + MMIO_D(SPRSIZE(PIPE_B), D_BDW); + MMIO_D(SPRKEYVAL(PIPE_B), D_BDW); + MMIO_D(SPRKEYMSK(PIPE_B), D_BDW); + MMIO_DH(SPRSURF(PIPE_B), D_BDW, NULL, spr_surf_mmio_write); + MMIO_D(SPRKEYMAX(PIPE_B), D_BDW); + MMIO_D(SPROFFSET(PIPE_B), D_BDW); + MMIO_D(SPRSCALE(PIPE_B), D_BDW); + MMIO_D(SPRSURFLIVE(PIPE_B), D_BDW); + + MMIO_D(SPRCTL(PIPE_C), D_BDW); + MMIO_D(SPRLINOFF(PIPE_C), D_BDW); + MMIO_D(SPRSTRIDE(PIPE_C), D_BDW); + MMIO_D(SPRPOS(PIPE_C), D_BDW); + MMIO_D(SPRSIZE(PIPE_C), D_BDW); + MMIO_D(SPRKEYVAL(PIPE_C), D_BDW); + MMIO_D(SPRKEYMSK(PIPE_C), D_BDW); + MMIO_DH(SPRSURF(PIPE_C), D_BDW, NULL, spr_surf_mmio_write); + MMIO_D(SPRKEYMAX(PIPE_C), D_BDW); + MMIO_D(SPROFFSET(PIPE_C), D_BDW); + MMIO_D(SPRSCALE(PIPE_C), D_BDW); + MMIO_D(SPRSURFLIVE(PIPE_C), D_BDW); MMIO_D(HTOTAL(TRANSCODER_A), D_ALL); MMIO_D(HBLANK(TRANSCODER_A), D_ALL); @@ -2804,6 +2804,46 @@ static int init_broadwell_mmio_info(struct intel_gvt *gvt) return 0; } +static int skl_plane_surf_write(struct intel_vgpu *vgpu, unsigned int offset, + void *p_data, unsigned int bytes) +{ + unsigned int pipe = SKL_PLANE_REG_TO_PIPE(offset); + unsigned int plane = SKL_PLANE_REG_TO_PLANE(offset); + i915_reg_t reg_1ac = _MMIO(_REG_701AC(pipe, plane)); + int flip_event = SKL_FLIP_EVENT(pipe, plane); + + write_vreg(vgpu, offset, p_data, bytes); + vgpu_vreg_t(vgpu, reg_1ac) = vgpu_vreg(vgpu, offset); + + set_bit(flip_event, vgpu->irq.flip_done_event[pipe]); + return 0; +} + +static int skl_plane_mmio_write(struct intel_vgpu *vgpu, unsigned int offset, + void *p_data, unsigned int bytes) +{ + write_vreg(vgpu, offset, p_data, bytes); + return 0; +} + +#define MMIO_PIPES_SDH(prefix, plane, s, d, r, w) do { \ + int pipe; \ + for_each_pipe(dev_priv, pipe) \ + MMIO_F(prefix(pipe, plane), s, 0, 0, 0, d, r, w); \ +} while (0) + +#define MMIO_PLANES_SDH(prefix, s, d, r, w) do { \ + int pipe, plane; \ + for_each_pipe(dev_priv, pipe) \ + for_each_universal_plane(dev_priv, pipe, plane) \ + MMIO_F(prefix(pipe, plane), s, 0, 0, 0, d, r, w); \ +} while (0) + +#define MMIO_PLANES_DH(prefix, d, r, w) \ + MMIO_PLANES_SDH(prefix, 4, d, r, w) + +#define PLANE_WM_BASE(pipe, plane) _MMIO(_PLANE_WM_BASE(pipe, plane)) + static int init_skl_mmio_info(struct intel_gvt *gvt) { struct drm_i915_private *dev_priv = gvt->dev_priv; @@ -2875,108 +2915,37 @@ static int init_skl_mmio_info(struct intel_gvt *gvt) MMIO_DH(SKL_PS_CTRL(PIPE_C, 0), D_SKL_PLUS, NULL, pf_write); MMIO_DH(SKL_PS_CTRL(PIPE_C, 1), D_SKL_PLUS, NULL, pf_write); - MMIO_DH(PLANE_BUF_CFG(PIPE_A, 0), D_SKL_PLUS, NULL, NULL); - MMIO_DH(PLANE_BUF_CFG(PIPE_A, 1), D_SKL_PLUS, NULL, NULL); - MMIO_DH(PLANE_BUF_CFG(PIPE_A, 2), D_SKL_PLUS, NULL, NULL); - MMIO_DH(PLANE_BUF_CFG(PIPE_A, 3), D_SKL_PLUS, NULL, NULL); + MMIO_PLANES_DH(PLANE_CTL, D_SKL_PLUS, NULL, skl_plane_mmio_write); + MMIO_PLANES_DH(PLANE_STRIDE, D_SKL_PLUS, NULL, skl_plane_mmio_write); + MMIO_PLANES_DH(PLANE_POS, D_SKL_PLUS, NULL, skl_plane_mmio_write); + MMIO_PLANES_DH(PLANE_SIZE, D_SKL_PLUS, NULL, skl_plane_mmio_write); + MMIO_PLANES_DH(PLANE_KEYVAL, D_SKL_PLUS, NULL, skl_plane_mmio_write); + MMIO_PLANES_DH(PLANE_KEYMSK, D_SKL_PLUS, NULL, skl_plane_mmio_write); + + MMIO_PLANES_DH(PLANE_SURF, D_SKL_PLUS, NULL, skl_plane_surf_write); - MMIO_DH(PLANE_BUF_CFG(PIPE_B, 0), D_SKL_PLUS, NULL, NULL); - MMIO_DH(PLANE_BUF_CFG(PIPE_B, 1), D_SKL_PLUS, NULL, NULL); - MMIO_DH(PLANE_BUF_CFG(PIPE_B, 2), D_SKL_PLUS, NULL, NULL); - MMIO_DH(PLANE_BUF_CFG(PIPE_B, 3), D_SKL_PLUS, NULL, NULL); + MMIO_PLANES_DH(PLANE_KEYMAX, D_SKL_PLUS, NULL, skl_plane_mmio_write); + MMIO_PLANES_DH(PLANE_OFFSET, D_SKL_PLUS, NULL, skl_plane_mmio_write); + MMIO_PLANES_DH(PLANE_AUX_DIST, D_SKL_PLUS, NULL, skl_plane_mmio_write); + MMIO_PLANES_DH(PLANE_AUX_OFFSET, D_SKL_PLUS, NULL, skl_plane_mmio_write); - MMIO_DH(PLANE_BUF_CFG(PIPE_C, 0), D_SKL_PLUS, NULL, NULL); - MMIO_DH(PLANE_BUF_CFG(PIPE_C, 1), D_SKL_PLUS, NULL, NULL); - MMIO_DH(PLANE_BUF_CFG(PIPE_C, 2), D_SKL_PLUS, NULL, NULL); - MMIO_DH(PLANE_BUF_CFG(PIPE_C, 3), D_SKL_PLUS, NULL, NULL); + MMIO_PLANES_SDH(PLANE_WM_BASE, 4 * 8, D_SKL_PLUS, NULL, NULL); + MMIO_PLANES_DH(PLANE_WM_TRANS, D_SKL_PLUS, NULL, NULL); + MMIO_PLANES_DH(PLANE_NV12_BUF_CFG, D_SKL_PLUS, NULL, NULL); + MMIO_PLANES_DH(PLANE_BUF_CFG, D_SKL_PLUS, NULL, NULL); MMIO_DH(CUR_BUF_CFG(PIPE_A), D_SKL_PLUS, NULL, NULL); MMIO_DH(CUR_BUF_CFG(PIPE_B), D_SKL_PLUS, NULL, NULL); MMIO_DH(CUR_BUF_CFG(PIPE_C), D_SKL_PLUS, NULL, NULL); - MMIO_F(PLANE_WM(PIPE_A, 0, 0), 4 * 8, 0, 0, 0, D_SKL_PLUS, NULL, NULL); - MMIO_F(PLANE_WM(PIPE_A, 1, 0), 4 * 8, 0, 0, 0, D_SKL_PLUS, NULL, NULL); - MMIO_F(PLANE_WM(PIPE_A, 2, 0), 4 * 8, 0, 0, 0, D_SKL_PLUS, NULL, NULL); - - MMIO_F(PLANE_WM(PIPE_B, 0, 0), 4 * 8, 0, 0, 0, D_SKL_PLUS, NULL, NULL); - MMIO_F(PLANE_WM(PIPE_B, 1, 0), 4 * 8, 0, 0, 0, D_SKL_PLUS, NULL, NULL); - MMIO_F(PLANE_WM(PIPE_B, 2, 0), 4 * 8, 0, 0, 0, D_SKL_PLUS, NULL, NULL); - - MMIO_F(PLANE_WM(PIPE_C, 0, 0), 4 * 8, 0, 0, 0, D_SKL_PLUS, NULL, NULL); - MMIO_F(PLANE_WM(PIPE_C, 1, 0), 4 * 8, 0, 0, 0, D_SKL_PLUS, NULL, NULL); - MMIO_F(PLANE_WM(PIPE_C, 2, 0), 4 * 8, 0, 0, 0, D_SKL_PLUS, NULL, NULL); - MMIO_F(CUR_WM(PIPE_A, 0), 4 * 8, 0, 0, 0, D_SKL_PLUS, NULL, NULL); MMIO_F(CUR_WM(PIPE_B, 0), 4 * 8, 0, 0, 0, D_SKL_PLUS, NULL, NULL); MMIO_F(CUR_WM(PIPE_C, 0), 4 * 8, 0, 0, 0, D_SKL_PLUS, NULL, NULL); - MMIO_DH(PLANE_WM_TRANS(PIPE_A, 0), D_SKL_PLUS, NULL, NULL); - MMIO_DH(PLANE_WM_TRANS(PIPE_A, 1), D_SKL_PLUS, NULL, NULL); - MMIO_DH(PLANE_WM_TRANS(PIPE_A, 2), D_SKL_PLUS, NULL, NULL); - - MMIO_DH(PLANE_WM_TRANS(PIPE_B, 0), D_SKL_PLUS, NULL, NULL); - MMIO_DH(PLANE_WM_TRANS(PIPE_B, 1), D_SKL_PLUS, NULL, NULL); - MMIO_DH(PLANE_WM_TRANS(PIPE_B, 2), D_SKL_PLUS, NULL, NULL); - - MMIO_DH(PLANE_WM_TRANS(PIPE_C, 0), D_SKL_PLUS, NULL, NULL); - MMIO_DH(PLANE_WM_TRANS(PIPE_C, 1), D_SKL_PLUS, NULL, NULL); - MMIO_DH(PLANE_WM_TRANS(PIPE_C, 2), D_SKL_PLUS, NULL, NULL); - MMIO_DH(CUR_WM_TRANS(PIPE_A), D_SKL_PLUS, NULL, NULL); MMIO_DH(CUR_WM_TRANS(PIPE_B), D_SKL_PLUS, NULL, NULL); MMIO_DH(CUR_WM_TRANS(PIPE_C), D_SKL_PLUS, NULL, NULL); - MMIO_DH(PLANE_NV12_BUF_CFG(PIPE_A, 0), D_SKL_PLUS, NULL, NULL); - MMIO_DH(PLANE_NV12_BUF_CFG(PIPE_A, 1), D_SKL_PLUS, NULL, NULL); - MMIO_DH(PLANE_NV12_BUF_CFG(PIPE_A, 2), D_SKL_PLUS, NULL, NULL); - MMIO_DH(PLANE_NV12_BUF_CFG(PIPE_A, 3), D_SKL_PLUS, NULL, NULL); - - MMIO_DH(PLANE_NV12_BUF_CFG(PIPE_B, 0), D_SKL_PLUS, NULL, NULL); - MMIO_DH(PLANE_NV12_BUF_CFG(PIPE_B, 1), D_SKL_PLUS, NULL, NULL); - MMIO_DH(PLANE_NV12_BUF_CFG(PIPE_B, 2), D_SKL_PLUS, NULL, NULL); - MMIO_DH(PLANE_NV12_BUF_CFG(PIPE_B, 3), D_SKL_PLUS, NULL, NULL); - - MMIO_DH(PLANE_NV12_BUF_CFG(PIPE_C, 0), D_SKL_PLUS, NULL, NULL); - MMIO_DH(PLANE_NV12_BUF_CFG(PIPE_C, 1), D_SKL_PLUS, NULL, NULL); - MMIO_DH(PLANE_NV12_BUF_CFG(PIPE_C, 2), D_SKL_PLUS, NULL, NULL); - MMIO_DH(PLANE_NV12_BUF_CFG(PIPE_C, 3), D_SKL_PLUS, NULL, NULL); - - MMIO_DH(_MMIO(_REG_701C0(PIPE_A, 1)), D_SKL_PLUS, NULL, NULL); - MMIO_DH(_MMIO(_REG_701C0(PIPE_A, 2)), D_SKL_PLUS, NULL, NULL); - MMIO_DH(_MMIO(_REG_701C0(PIPE_A, 3)), D_SKL_PLUS, NULL, NULL); - MMIO_DH(_MMIO(_REG_701C0(PIPE_A, 4)), D_SKL_PLUS, NULL, NULL); - - MMIO_DH(_MMIO(_REG_701C0(PIPE_B, 1)), D_SKL_PLUS, NULL, NULL); - MMIO_DH(_MMIO(_REG_701C0(PIPE_B, 2)), D_SKL_PLUS, NULL, NULL); - MMIO_DH(_MMIO(_REG_701C0(PIPE_B, 3)), D_SKL_PLUS, NULL, NULL); - MMIO_DH(_MMIO(_REG_701C0(PIPE_B, 4)), D_SKL_PLUS, NULL, NULL); - - MMIO_DH(_MMIO(_REG_701C0(PIPE_C, 1)), D_SKL_PLUS, NULL, NULL); - MMIO_DH(_MMIO(_REG_701C0(PIPE_C, 2)), D_SKL_PLUS, NULL, NULL); - MMIO_DH(_MMIO(_REG_701C0(PIPE_C, 3)), D_SKL_PLUS, NULL, NULL); - MMIO_DH(_MMIO(_REG_701C0(PIPE_C, 4)), D_SKL_PLUS, NULL, NULL); - - MMIO_DH(_MMIO(_REG_701C4(PIPE_A, 1)), D_SKL_PLUS, NULL, NULL); - MMIO_DH(_MMIO(_REG_701C4(PIPE_A, 2)), D_SKL_PLUS, NULL, NULL); - MMIO_DH(_MMIO(_REG_701C4(PIPE_A, 3)), D_SKL_PLUS, NULL, NULL); - MMIO_DH(_MMIO(_REG_701C4(PIPE_A, 4)), D_SKL_PLUS, NULL, NULL); - - MMIO_DH(_MMIO(_REG_701C4(PIPE_B, 1)), D_SKL_PLUS, NULL, NULL); - MMIO_DH(_MMIO(_REG_701C4(PIPE_B, 2)), D_SKL_PLUS, NULL, NULL); - MMIO_DH(_MMIO(_REG_701C4(PIPE_B, 3)), D_SKL_PLUS, NULL, NULL); - MMIO_DH(_MMIO(_REG_701C4(PIPE_B, 4)), D_SKL_PLUS, NULL, NULL); - - MMIO_DH(_MMIO(_REG_701C4(PIPE_C, 1)), D_SKL_PLUS, NULL, NULL); - MMIO_DH(_MMIO(_REG_701C4(PIPE_C, 2)), D_SKL_PLUS, NULL, NULL); - MMIO_DH(_MMIO(_REG_701C4(PIPE_C, 3)), D_SKL_PLUS, NULL, NULL); - MMIO_DH(_MMIO(_REG_701C4(PIPE_C, 4)), D_SKL_PLUS, NULL, NULL); - - MMIO_D(_MMIO(0x70380), D_SKL_PLUS); - MMIO_D(_MMIO(0x71380), D_SKL_PLUS); - MMIO_D(_MMIO(0x72380), D_SKL_PLUS); - MMIO_D(_MMIO(0x7239c), D_SKL_PLUS); - MMIO_D(_MMIO(0x7039c), D_SKL_PLUS); - MMIO_D(_MMIO(0x8f074), D_SKL_PLUS); MMIO_D(_MMIO(0x8f004), D_SKL_PLUS); MMIO_D(_MMIO(0x8f034), D_SKL_PLUS); @@ -3031,16 +3000,6 @@ static int init_skl_mmio_info(struct intel_gvt *gvt) MMIO_D(_MMIO(0x71034), D_SKL_PLUS); MMIO_D(_MMIO(0x72034), D_SKL_PLUS); - MMIO_D(_MMIO(_PLANE_KEYVAL_1(PIPE_A)), D_SKL_PLUS); - MMIO_D(_MMIO(_PLANE_KEYVAL_1(PIPE_B)), D_SKL_PLUS); - MMIO_D(_MMIO(_PLANE_KEYVAL_1(PIPE_C)), D_SKL_PLUS); - MMIO_D(_MMIO(_PLANE_KEYMAX_1(PIPE_A)), D_SKL_PLUS); - MMIO_D(_MMIO(_PLANE_KEYMAX_1(PIPE_B)), D_SKL_PLUS); - MMIO_D(_MMIO(_PLANE_KEYMAX_1(PIPE_C)), D_SKL_PLUS); - MMIO_D(_MMIO(_PLANE_KEYMSK_1(PIPE_A)), D_SKL_PLUS); - MMIO_D(_MMIO(_PLANE_KEYMSK_1(PIPE_B)), D_SKL_PLUS); - MMIO_D(_MMIO(_PLANE_KEYMSK_1(PIPE_C)), D_SKL_PLUS); - MMIO_D(_MMIO(0x44500), D_SKL_PLUS); MMIO_DFH(GEN9_CSFE_CHICKEN1_RCS, D_SKL_PLUS, F_CMD_ACCESS, NULL, NULL); MMIO_DFH(GEN8_HDC_CHICKEN1, D_SKL_PLUS, F_MODE_MASK | F_CMD_ACCESS, diff --git a/drivers/gpu/drm/i915/gvt/interrupt.c b/drivers/gpu/drm/i915/gvt/interrupt.c index 5daa23ae566b..d749d46bc05b 100644 --- a/drivers/gpu/drm/i915/gvt/interrupt.c +++ b/drivers/gpu/drm/i915/gvt/interrupt.c @@ -595,6 +595,10 @@ static void gen8_init_irq( SET_BIT_INFO(irq, 4, SPRITE_A_FLIP_DONE, INTEL_GVT_IRQ_INFO_DE_PIPE_A); SET_BIT_INFO(irq, 4, SPRITE_B_FLIP_DONE, INTEL_GVT_IRQ_INFO_DE_PIPE_B); SET_BIT_INFO(irq, 4, SPRITE_C_FLIP_DONE, INTEL_GVT_IRQ_INFO_DE_PIPE_C); + + SET_BIT_INFO(irq, 5, PLANE_3_A_FLIP_DONE, INTEL_GVT_IRQ_INFO_DE_PIPE_A); + SET_BIT_INFO(irq, 5, PLANE_3_B_FLIP_DONE, INTEL_GVT_IRQ_INFO_DE_PIPE_B); + SET_BIT_INFO(irq, 5, PLANE_3_C_FLIP_DONE, INTEL_GVT_IRQ_INFO_DE_PIPE_C); } /* GEN8 interrupt PCU events */ diff --git a/drivers/gpu/drm/i915/gvt/interrupt.h b/drivers/gpu/drm/i915/gvt/interrupt.h index 5313fb1b33e1..f7d7ade4f13c 100644 --- a/drivers/gpu/drm/i915/gvt/interrupt.h +++ b/drivers/gpu/drm/i915/gvt/interrupt.h @@ -92,6 +92,9 @@ enum intel_gvt_event_type { SPRITE_A_FLIP_DONE, SPRITE_B_FLIP_DONE, SPRITE_C_FLIP_DONE, + PLANE_3_A_FLIP_DONE, + PLANE_3_B_FLIP_DONE, + PLANE_3_C_FLIP_DONE, PCU_THERMAL, PCU_PCODE2DRIVER_MAILBOX, diff --git a/drivers/gpu/drm/i915/gvt/reg.h b/drivers/gpu/drm/i915/gvt/reg.h index d4f7ce6dc1d7..d05c5516a472 100644 --- a/drivers/gpu/drm/i915/gvt/reg.h +++ b/drivers/gpu/drm/i915/gvt/reg.h @@ -57,8 +57,11 @@ #define VGT_SPRSTRIDE(pipe) _PIPE(pipe, _SPRA_STRIDE, _PLANE_STRIDE_2_B) -#define _REG_701C0(pipe, plane) (0x701c0 + pipe * 0x1000 + (plane - 1) * 0x100) -#define _REG_701C4(pipe, plane) (0x701c4 + pipe * 0x1000 + (plane - 1) * 0x100) +#define _REG_701AC(pipe, plane) (0x701ac + pipe * 0x1000 + plane * 0x100) + +#define SKL_PLANE_REG_TO_PIPE(reg) (((reg) >> 12) & 0x3) +#define SKL_PLANE_REG_TO_PLANE(reg) ((((reg) & 0xFFF) - 0x180) >> 8) +#define SKL_FLIP_EVENT(pipe, plane) (PRIMARY_A_FLIP_DONE + (plane)*3 + pipe) #define GFX_MODE_BIT_SET_IN_MASK(val, bit) \ ((((bit) & 0xffff0000) == 0) && !!((val) & (((bit) << 16)))) From 997c9be98299e83de96c6ae21b460c8171b8b5c8 Mon Sep 17 00:00:00 2001 From: Min He Date: Fri, 14 Sep 2018 16:10:16 +0800 Subject: [PATCH 0889/1103] drm/i915/gvt: passthru PIPE_DSL regiser to guest Change-Id: I4d903a982052b8b241f090a91e2251d66bba2778 Signed-off-by: Min He Reviewed-on: Reviewed-by: Jiang, Fei Reviewed-by: Dong, Eddie Tested-by: Dong, Eddie --- drivers/gpu/drm/i915/gvt/handlers.c | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/i915/gvt/handlers.c b/drivers/gpu/drm/i915/gvt/handlers.c index e89d228ddc58..7f7f2fb09a37 100644 --- a/drivers/gpu/drm/i915/gvt/handlers.c +++ b/drivers/gpu/drm/i915/gvt/handlers.c @@ -530,6 +530,14 @@ static int force_nonpriv_write(struct intel_vgpu *vgpu, return 0; } +static int pipe_dsl_mmio_read(struct intel_vgpu *vgpu, + unsigned int offset, void *p_data, unsigned int bytes) +{ + struct drm_i915_private *dev_priv = vgpu->gvt->dev_priv; + vgpu_vreg(vgpu, offset) = I915_READ(_MMIO(offset)); + return intel_vgpu_default_mmio_read(vgpu, offset, p_data, bytes); +} + static int ddi_buf_ctl_mmio_write(struct intel_vgpu *vgpu, unsigned int offset, void *p_data, unsigned int bytes) { @@ -1909,9 +1917,9 @@ static int init_generic_mmio_info(struct intel_gvt *gvt) MMIO_D(_MMIO(0xc4040), D_ALL); MMIO_D(DERRMR, D_ALL); - MMIO_D(PIPEDSL(PIPE_A), D_ALL); - MMIO_D(PIPEDSL(PIPE_B), D_ALL); - MMIO_D(PIPEDSL(PIPE_C), D_ALL); + MMIO_DH(PIPEDSL(PIPE_A), D_ALL, pipe_dsl_mmio_read, NULL); + MMIO_DH(PIPEDSL(PIPE_B), D_ALL, pipe_dsl_mmio_read, NULL); + MMIO_DH(PIPEDSL(PIPE_C), D_ALL, pipe_dsl_mmio_read, NULL); MMIO_D(PIPEDSL(_PIPE_EDP), D_ALL); MMIO_DH(PIPECONF(PIPE_A), D_ALL, NULL, pipeconf_mmio_write); From 4987a98152c38c8a107059c50639a9ebca14f59c Mon Sep 17 00:00:00 2001 From: Min He Date: Tue, 19 Dec 2017 10:22:34 +0800 Subject: [PATCH 0890/1103] drm/i915/gvt: local display support Added local display dispaly support for GVT-g. Signed-off-by: Min He Change-Id: I430fbf98318cd35f7104657c4d1c128e50f56f2d Reviewed-on: Reviewed-by: He, Min Reviewed-by: Jiang, Fei Reviewed-by: Dong, Eddie Tested-by: Dong, Eddie --- drivers/gpu/drm/i915/gvt/cfg_space.c | 9 +++ drivers/gpu/drm/i915/gvt/display.c | 100 ++++++++++++++++++++++++--- drivers/gpu/drm/i915/gvt/edid.c | 20 +++--- drivers/gpu/drm/i915/gvt/edid.h | 2 +- drivers/gpu/drm/i915/gvt/gvt.c | 43 ++++++++++++ drivers/gpu/drm/i915/gvt/gvt.h | 13 ++++ drivers/gpu/drm/i915/gvt/handlers.c | 33 ++++++--- drivers/gpu/drm/i915/i915_irq.c | 25 ++++++- drivers/gpu/drm/i915/intel_dp.c | 2 +- drivers/gpu/drm/i915/intel_drv.h | 1 + drivers/gpu/drm/i915/intel_pm.c | 17 +++++ drivers/gpu/drm/i915/intel_sprite.c | 15 ++++ 12 files changed, 251 insertions(+), 29 deletions(-) diff --git a/drivers/gpu/drm/i915/gvt/cfg_space.c b/drivers/gpu/drm/i915/gvt/cfg_space.c index c62346fdc05d..707b0a50da3c 100644 --- a/drivers/gpu/drm/i915/gvt/cfg_space.c +++ b/drivers/gpu/drm/i915/gvt/cfg_space.c @@ -322,6 +322,15 @@ int intel_vgpu_emulate_cfg_write(struct intel_vgpu *vgpu, unsigned int offset, case INTEL_GVT_PCI_OPREGION: if (WARN_ON(!IS_ALIGNED(offset, 4))) return -EINVAL; + + /* + * To support virtual display, we need to override the real VBT in the + * OpRegion. So here we don't report OpRegion to guest. + */ + if (IS_BROXTON(vgpu->gvt->dev_priv) || + IS_KABYLAKE(vgpu->gvt->dev_priv)) + return 0; + ret = intel_vgpu_opregion_base_write_handler(vgpu, *(u32 *)p_data); if (ret) diff --git a/drivers/gpu/drm/i915/gvt/display.c b/drivers/gpu/drm/i915/gvt/display.c index 3019dbc39aef..2e01c38887bd 100644 --- a/drivers/gpu/drm/i915/gvt/display.c +++ b/drivers/gpu/drm/i915/gvt/display.c @@ -315,14 +315,19 @@ static void clean_virtual_dp_monitor(struct intel_vgpu *vgpu, int port_num) } static int setup_virtual_dp_monitor(struct intel_vgpu *vgpu, int port_num, - int type, unsigned int resolution) + int type, unsigned int resolution, void *edid) { struct intel_vgpu_port *port = intel_vgpu_port(vgpu, port_num); + int valid_extensions = 1; + struct edid *tmp_edid = NULL; if (WARN_ON(resolution >= GVT_EDID_NUM)) return -EINVAL; - port->edid = kzalloc(sizeof(*(port->edid)), GFP_KERNEL); + if (edid) + valid_extensions += ((struct edid *)edid)->extensions; + port->edid = kzalloc(sizeof(*(port->edid)) + + valid_extensions * EDID_SIZE, GFP_KERNEL); if (!port->edid) return -ENOMEM; @@ -332,8 +337,23 @@ static int setup_virtual_dp_monitor(struct intel_vgpu *vgpu, int port_num, return -ENOMEM; } - memcpy(port->edid->edid_block, virtual_dp_monitor_edid[resolution], - EDID_SIZE); + if (edid) + memcpy(port->edid->edid_block, edid, EDID_SIZE * valid_extensions); + else + memcpy(port->edid->edid_block, virtual_dp_monitor_edid[resolution], + EDID_SIZE); + + /* Sometimes the physical display will report the EDID with no + * digital bit set, which will cause the guest fail to enumerate + * the virtual HDMI monitor. So here we will set the digital + * bit and re-calculate the checksum. + */ + tmp_edid = ((struct edid *)port->edid->edid_block); + if (!(tmp_edid->input & DRM_EDID_INPUT_DIGITAL)) { + tmp_edid->input += DRM_EDID_INPUT_DIGITAL; + tmp_edid->checksum -= DRM_EDID_INPUT_DIGITAL; + } + port->edid->data_valid = true; memcpy(port->dpcd->data, dpcd_fix_data, DPCD_HEADER_SIZE); @@ -442,6 +462,66 @@ void intel_gvt_emulate_vblank(struct intel_gvt *gvt) mutex_unlock(&gvt->lock); } +static void intel_gvt_vblank_work(struct work_struct *w) +{ + struct intel_gvt_pipe_info *pipe_info = container_of(w, + struct intel_gvt_pipe_info, vblank_work); + struct intel_gvt *gvt = pipe_info->gvt; + struct intel_vgpu *vgpu; + int id; + + mutex_lock(&gvt->lock); + for_each_active_vgpu(gvt, vgpu, id) + emulate_vblank_on_pipe(vgpu, pipe_info->pipe_num); + mutex_unlock(&gvt->lock); +} + +void intel_gvt_init_pipe_info(struct intel_gvt *gvt) +{ + int pipe; + + for (pipe = PIPE_A; pipe <= PIPE_C; pipe++) { + gvt->pipe_info[pipe].pipe_num = pipe; + gvt->pipe_info[pipe].gvt = gvt; + INIT_WORK(&gvt->pipe_info[pipe].vblank_work, + intel_gvt_vblank_work); + } +} + +int setup_virtual_monitors(struct intel_vgpu *vgpu) +{ + struct intel_connector *connector = NULL; + struct drm_connector_list_iter conn_iter; + int pipe = 0; + int ret = 0; + + drm_connector_list_iter_begin(&vgpu->gvt->dev_priv->drm, &conn_iter); + for_each_intel_connector_iter(connector, &conn_iter) { + if (connector->encoder->get_hw_state(connector->encoder, &pipe) + && connector->detect_edid) { + ret = setup_virtual_dp_monitor(vgpu, pipe, + GVT_DP_A + pipe, 0, + connector->detect_edid); + if (ret) + return ret; + } + } + drm_connector_list_iter_end(&conn_iter); + return 0; +} + +void clean_virtual_monitors(struct intel_vgpu *vgpu) +{ + int port = 0; + + for (port = PORT_A; port < I915_MAX_PORTS; port++) { + struct intel_vgpu_port *p = intel_vgpu_port(vgpu, port); + + if (p->edid) + clean_virtual_dp_monitor(vgpu, port); + } +} + /** * intel_vgpu_clean_display - clean vGPU virtual display emulation * @vgpu: a vGPU @@ -453,7 +533,9 @@ void intel_vgpu_clean_display(struct intel_vgpu *vgpu) { struct drm_i915_private *dev_priv = vgpu->gvt->dev_priv; - if (IS_SKYLAKE(dev_priv) || IS_KABYLAKE(dev_priv)) + if (IS_BROXTON(dev_priv) || IS_KABYLAKE(dev_priv)) + clean_virtual_monitors(vgpu); + else if (IS_SKYLAKE(dev_priv)) clean_virtual_dp_monitor(vgpu, PORT_D); else clean_virtual_dp_monitor(vgpu, PORT_B); @@ -475,12 +557,14 @@ int intel_vgpu_init_display(struct intel_vgpu *vgpu, u64 resolution) intel_vgpu_init_i2c_edid(vgpu); - if (IS_SKYLAKE(dev_priv) || IS_KABYLAKE(dev_priv)) + if (IS_BROXTON(dev_priv) || IS_KABYLAKE(dev_priv)) + return setup_virtual_monitors(vgpu); + else if (IS_SKYLAKE(dev_priv)) return setup_virtual_dp_monitor(vgpu, PORT_D, GVT_DP_D, - resolution); + resolution, NULL); else return setup_virtual_dp_monitor(vgpu, PORT_B, GVT_DP_B, - resolution); + resolution, NULL); } /** diff --git a/drivers/gpu/drm/i915/gvt/edid.c b/drivers/gpu/drm/i915/gvt/edid.c index 4b98539025c5..4785b8a10619 100644 --- a/drivers/gpu/drm/i915/gvt/edid.c +++ b/drivers/gpu/drm/i915/gvt/edid.c @@ -55,10 +55,6 @@ static unsigned char edid_get_byte(struct intel_vgpu *vgpu) gvt_vgpu_err("Driver tries to read EDID without proper sequence!\n"); return 0; } - if (edid->current_edid_read >= EDID_SIZE) { - gvt_vgpu_err("edid_get_byte() exceeds the size of EDID!\n"); - return 0; - } if (!edid->edid_available) { gvt_vgpu_err("Reading EDID but EDID is not available!\n"); @@ -452,6 +448,8 @@ void intel_gvt_i2c_handle_aux_ch_write(struct intel_vgpu *vgpu, u32 value = *(u32 *)p_data; int aux_data_for_write = 0; int reg = get_aux_ch_reg(offset); + uint8_t rxbuf[20]; + size_t rxsize; if (reg != AUX_CH_CTL) { vgpu_vreg(vgpu, offset) = value; @@ -459,6 +457,9 @@ void intel_gvt_i2c_handle_aux_ch_write(struct intel_vgpu *vgpu, } msg_length = AUX_CTL_MSG_LENGTH(value); + for (rxsize = 0; rxsize < msg_length; rxsize += 4) + intel_dp_unpack_aux(vgpu_vreg(vgpu, offset + 4 + rxsize), + rxbuf + rxsize, msg_length - rxsize); // check the msg in DATA register. msg = vgpu_vreg(vgpu, offset + 4); addr = (msg >> 8) & 0xffff; @@ -498,12 +499,13 @@ void intel_gvt_i2c_handle_aux_ch_write(struct intel_vgpu *vgpu, } } } else if ((op & 0x1) == GVT_AUX_I2C_WRITE) { - /* TODO - * We only support EDID reading from I2C_over_AUX. And - * we do not expect the index mode to be used. Right now - * the WRITE operation is ignored. It is good enough to - * support the gfx driver to do EDID access. + /* We only support EDID reading from I2C_over_AUX. + * But if EDID has extension blocks, we use this write + * operation to set block starting address */ + if (addr == EDID_ADDR) { + i2c_edid->current_edid_read = rxbuf[4]; + } } else { if (WARN_ON((op & 0x1) != GVT_AUX_I2C_READ)) return; diff --git a/drivers/gpu/drm/i915/gvt/edid.h b/drivers/gpu/drm/i915/gvt/edid.h index f6dfc8b795ec..11a75d69062d 100644 --- a/drivers/gpu/drm/i915/gvt/edid.h +++ b/drivers/gpu/drm/i915/gvt/edid.h @@ -48,7 +48,7 @@ struct intel_vgpu_edid_data { bool data_valid; - unsigned char edid_block[EDID_SIZE]; + unsigned char edid_block[0]; }; enum gmbus_cycle_type { diff --git a/drivers/gpu/drm/i915/gvt/gvt.c b/drivers/gpu/drm/i915/gvt/gvt.c index 46c8b720e336..8523fbc1e494 100644 --- a/drivers/gpu/drm/i915/gvt/gvt.c +++ b/drivers/gpu/drm/i915/gvt/gvt.c @@ -301,6 +301,46 @@ static int init_service_thread(struct intel_gvt *gvt) return 0; } +void intel_gvt_init_pipe_info(struct intel_gvt *gvt); + +/* + * When enabling multi-plane in DomU, an issue is that the PLANE_BUF_CFG + * register cannot be updated dynamically, since Dom0 has no idea of the + * plane information of DomU's planes, so here we statically allocate the + * ddb entries for all the possible enabled planes. + */ +static void intel_gvt_init_ddb(struct intel_gvt *gvt) +{ + struct drm_i915_private *dev_priv = gvt->dev_priv; + struct skl_ddb_allocation *ddb = &gvt->ddb; + unsigned int pipe_size, ddb_size, plane_size, plane_cnt; + u16 start, end; + enum pipe pipe; + enum plane_id plane; + + ddb_size = INTEL_INFO(dev_priv)->ddb_size; + ddb_size -= 4; /* 4 blocks for bypass path allocation */ + pipe_size = ddb_size / INTEL_INFO(dev_priv)->num_pipes; + + memset(ddb, 0, sizeof(*ddb)); + for_each_pipe(dev_priv, pipe) { + start = pipe * ddb_size / INTEL_INFO(dev_priv)->num_pipes; + end = start + pipe_size; + ddb->plane[pipe][PLANE_CURSOR].start = end - 8; + ddb->plane[pipe][PLANE_CURSOR].end = end; + + plane_cnt = (INTEL_INFO(dev_priv)->num_sprites[pipe] + 1); + plane_size = (pipe_size - 8) / plane_cnt; + + for_each_universal_plane(dev_priv, pipe, plane) { + ddb->plane[pipe][plane].start = start + + (plane * (pipe_size - 8) / plane_cnt); + ddb->plane[pipe][plane].end = + ddb->plane[pipe][plane].start + plane_size; + } + } +} + /** * intel_gvt_clean_device - clean a GVT device * @gvt: intel gvt device @@ -421,6 +461,9 @@ int intel_gvt_init_device(struct drm_i915_private *dev_priv) goto out_clean_types; } + intel_gvt_init_pipe_info(gvt); + intel_gvt_init_ddb(gvt); + ret = intel_gvt_hypervisor_host_init(&dev_priv->drm.pdev->dev, gvt, &intel_gvt_ops); if (ret) { diff --git a/drivers/gpu/drm/i915/gvt/gvt.h b/drivers/gpu/drm/i915/gvt/gvt.h index 31f6cdbe5c42..47ed1789ea28 100644 --- a/drivers/gpu/drm/i915/gvt/gvt.h +++ b/drivers/gpu/drm/i915/gvt/gvt.h @@ -291,6 +291,7 @@ struct intel_gvt_firmware { }; #define NR_MAX_INTEL_VGPU_TYPES 20 + struct intel_vgpu_type { char name[16]; unsigned int avail_instance; @@ -301,6 +302,14 @@ struct intel_vgpu_type { enum intel_vgpu_edid resolution; }; +struct intel_gvt_pipe_info { + enum pipe pipe_num; + int owner; + struct intel_gvt *gvt; + struct work_struct vblank_work; + int plane_owner[I915_MAX_PLANES]; +}; + struct intel_gvt { /* GVT scope lock, protect GVT itself, and all resource currently * not yet protected by special locks(vgpu and scheduler lock). @@ -334,6 +343,10 @@ struct intel_gvt { */ unsigned long service_request; + struct intel_gvt_pipe_info pipe_info[I915_MAX_PIPES]; + + struct skl_ddb_allocation ddb; + struct { struct engine_mmio *mmio; int ctx_mmio_count[I915_NUM_ENGINES]; diff --git a/drivers/gpu/drm/i915/gvt/handlers.c b/drivers/gpu/drm/i915/gvt/handlers.c index 7f7f2fb09a37..e1855328eba6 100644 --- a/drivers/gpu/drm/i915/gvt/handlers.c +++ b/drivers/gpu/drm/i915/gvt/handlers.c @@ -441,18 +441,21 @@ static int pipeconf_mmio_write(struct intel_vgpu *vgpu, unsigned int offset, void *p_data, unsigned int bytes) { u32 data; + unsigned int pipe = SKL_PLANE_REG_TO_PIPE(offset); + struct intel_crtc *crtc = intel_get_crtc_for_pipe( + vgpu->gvt->dev_priv, pipe); write_vreg(vgpu, offset, p_data, bytes); data = vgpu_vreg(vgpu, offset); - if (data & PIPECONF_ENABLE) + if (data & PIPECONF_ENABLE) { vgpu_vreg(vgpu, offset) |= I965_PIPECONF_ACTIVE; - else + if (crtc) + drm_crtc_vblank_get(&crtc->base); + } else { vgpu_vreg(vgpu, offset) &= ~I965_PIPECONF_ACTIVE; - /* vgpu_lock already hold by emulate mmio r/w */ - mutex_unlock(&vgpu->vgpu_lock); - intel_gvt_check_vblank_emulation(vgpu->gvt); - mutex_lock(&vgpu->vgpu_lock); + } + return 0; } @@ -2815,6 +2818,7 @@ static int init_broadwell_mmio_info(struct intel_gvt *gvt) static int skl_plane_surf_write(struct intel_vgpu *vgpu, unsigned int offset, void *p_data, unsigned int bytes) { + struct drm_i915_private *dev_priv = vgpu->gvt->dev_priv; unsigned int pipe = SKL_PLANE_REG_TO_PIPE(offset); unsigned int plane = SKL_PLANE_REG_TO_PLANE(offset); i915_reg_t reg_1ac = _MMIO(_REG_701AC(pipe, plane)); @@ -2823,6 +2827,11 @@ static int skl_plane_surf_write(struct intel_vgpu *vgpu, unsigned int offset, write_vreg(vgpu, offset, p_data, bytes); vgpu_vreg_t(vgpu, reg_1ac) = vgpu_vreg(vgpu, offset); + if ((vgpu_vreg_t(vgpu, PIPECONF(pipe)) & I965_PIPECONF_ACTIVE) && + (vgpu->gvt->pipe_info[pipe].plane_owner[plane] == vgpu->id)) { + I915_WRITE(_MMIO(offset), vgpu_vreg(vgpu, offset)); + } + set_bit(flip_event, vgpu->irq.flip_done_event[pipe]); return 0; } @@ -2830,7 +2839,15 @@ static int skl_plane_surf_write(struct intel_vgpu *vgpu, unsigned int offset, static int skl_plane_mmio_write(struct intel_vgpu *vgpu, unsigned int offset, void *p_data, unsigned int bytes) { + struct drm_i915_private *dev_priv = vgpu->gvt->dev_priv; + unsigned int pipe = SKL_PLANE_REG_TO_PIPE(offset); + unsigned int plane = SKL_PLANE_REG_TO_PLANE(offset); + write_vreg(vgpu, offset, p_data, bytes); + if ((vgpu_vreg_t(vgpu, PIPECONF(pipe)) & I965_PIPECONF_ACTIVE) && + (vgpu->gvt->pipe_info[pipe].plane_owner[plane] == vgpu->id)) { + I915_WRITE(_MMIO(offset), vgpu_vreg(vgpu, offset)); + } return 0; } @@ -2937,8 +2954,8 @@ static int init_skl_mmio_info(struct intel_gvt *gvt) MMIO_PLANES_DH(PLANE_AUX_DIST, D_SKL_PLUS, NULL, skl_plane_mmio_write); MMIO_PLANES_DH(PLANE_AUX_OFFSET, D_SKL_PLUS, NULL, skl_plane_mmio_write); - MMIO_PLANES_SDH(PLANE_WM_BASE, 4 * 8, D_SKL_PLUS, NULL, NULL); - MMIO_PLANES_DH(PLANE_WM_TRANS, D_SKL_PLUS, NULL, NULL); + MMIO_PLANES_SDH(PLANE_WM_BASE, 4 * 8, D_SKL_PLUS, NULL, skl_plane_mmio_write); + MMIO_PLANES_DH(PLANE_WM_TRANS, D_SKL_PLUS, NULL, skl_plane_mmio_write); MMIO_PLANES_DH(PLANE_NV12_BUF_CFG, D_SKL_PLUS, NULL, NULL); MMIO_PLANES_DH(PLANE_BUF_CFG, D_SKL_PLUS, NULL, NULL); diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c index 29877969310d..0b16775e292c 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c @@ -37,6 +37,10 @@ #include "i915_trace.h" #include "intel_drv.h" +#if IS_ENABLED(CONFIG_DRM_I915_GVT) +#include "gvt.h" +#endif + /** * DOC: interrupt handling * @@ -221,6 +225,17 @@ static void gen2_assert_iir_is_zero(struct drm_i915_private *dev_priv, static void gen6_rps_irq_handler(struct drm_i915_private *dev_priv, u32 pm_iir); static void gen9_guc_irq_handler(struct drm_i915_private *dev_priv, u32 pm_iir); + +#if IS_ENABLED(CONFIG_DRM_I915_GVT) +static inline void gvt_notify_vblank(struct drm_i915_private *dev_priv, + enum pipe pipe) +{ + if (dev_priv->gvt) + queue_work(system_highpri_wq, + &dev_priv->gvt->pipe_info[pipe].vblank_work); +} +#endif + /* For display hotplug interrupt */ static inline void i915_hotplug_interrupt_update_locked(struct drm_i915_private *dev_priv, @@ -2837,8 +2852,12 @@ gen8_de_irq_handler(struct drm_i915_private *dev_priv, u32 master_ctl) ret = IRQ_HANDLED; I915_WRITE(GEN8_DE_PIPE_IIR(pipe), iir); - if (iir & GEN8_PIPE_VBLANK) + if (iir & GEN8_PIPE_VBLANK) { drm_handle_vblank(&dev_priv->drm, pipe); +#if IS_ENABLED(CONFIG_DRM_I915_GVT) + gvt_notify_vblank(dev_priv, pipe); +#endif + } if (iir & GEN8_PIPE_CDCLK_CRC_DONE) hsw_pipe_crc_irq_handler(dev_priv, pipe); @@ -3446,7 +3465,9 @@ static void gen8_disable_vblank(struct drm_device *dev, unsigned int pipe) unsigned long irqflags; spin_lock_irqsave(&dev_priv->irq_lock, irqflags); - bdw_disable_pipe_irq(dev_priv, pipe, GEN8_PIPE_VBLANK); + /*since guest will see all the pipes, we don't want it disable vblank*/ + if (!dev_priv->gvt) + bdw_disable_pipe_irq(dev_priv, pipe, GEN8_PIPE_VBLANK); spin_unlock_irqrestore(&dev_priv->irq_lock, irqflags); } diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c index 1193202766a2..37c08c71fdf6 100644 --- a/drivers/gpu/drm/i915/intel_dp.c +++ b/drivers/gpu/drm/i915/intel_dp.c @@ -479,7 +479,7 @@ uint32_t intel_dp_pack_aux(const uint8_t *src, int src_bytes) return v; } -static void intel_dp_unpack_aux(uint32_t src, uint8_t *dst, int dst_bytes) +void intel_dp_unpack_aux(uint32_t src, uint8_t *dst, int dst_bytes) { int i; if (dst_bytes > 4) diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h index 8fc61e96754f..7c11b8d10b66 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h @@ -1708,6 +1708,7 @@ int intel_dp_rate_select(struct intel_dp *intel_dp, int rate); void intel_dp_hot_plug(struct intel_encoder *intel_encoder); void intel_power_sequencer_reset(struct drm_i915_private *dev_priv); uint32_t intel_dp_pack_aux(const uint8_t *src, int src_bytes); +void intel_dp_unpack_aux(uint32_t src, uint8_t *dst, int dst_bytes); void intel_plane_destroy(struct drm_plane *plane); void intel_edp_drrs_enable(struct intel_dp *intel_dp, const struct intel_crtc_state *crtc_state); diff --git a/drivers/gpu/drm/i915/intel_pm.c b/drivers/gpu/drm/i915/intel_pm.c index 43ae9de12ba3..01d0f4d3653a 100644 --- a/drivers/gpu/drm/i915/intel_pm.c +++ b/drivers/gpu/drm/i915/intel_pm.c @@ -33,6 +33,10 @@ #include #include +#if IS_ENABLED(CONFIG_DRM_I915_GVT) +#include "gvt.h" +#endif + /** * DOC: RC6 * @@ -5128,6 +5132,14 @@ skl_compute_ddb(struct drm_atomic_state *state) memcpy(ddb, &dev_priv->wm.skl_hw.ddb, sizeof(*ddb)); +#if IS_ENABLED(CONFIG_DRM_I915_GVT) + /* In GVT environemnt, we only use the statically allocated ddb */ + if (dev_priv->gvt) { + memcpy(ddb, &dev_priv->gvt->ddb, sizeof(*ddb)); + return 0; + } +#endif + for_each_new_intel_crtc_in_state(intel_state, crtc, cstate, i) { ret = skl_allocate_pipe_ddb(cstate, ddb); if (ret) @@ -5354,6 +5366,11 @@ static void skl_atomic_update_crtc_wm(struct intel_atomic_state *state, I915_WRITE(PIPE_WM_LINETIME(pipe), pipe_wm->linetime); for_each_plane_id_on_crtc(crtc, plane_id) { +#if IS_ENABLED(CONFIG_DRM_I915_GVT) + if (dev_priv->gvt && + dev_priv->gvt->pipe_info[pipe].plane_owner[plane_id]) + return; +#endif if (plane_id != PLANE_CURSOR) skl_write_plane_wm(crtc, &pipe_wm->planes[plane_id], ddb, plane_id); diff --git a/drivers/gpu/drm/i915/intel_sprite.c b/drivers/gpu/drm/i915/intel_sprite.c index f7026e887fa9..11b7afc0a0d3 100644 --- a/drivers/gpu/drm/i915/intel_sprite.c +++ b/drivers/gpu/drm/i915/intel_sprite.c @@ -41,6 +41,10 @@ #include #include "i915_drv.h" +#if IS_ENABLED(CONFIG_DRM_I915_GVT) +#include "gvt.h" +#endif + int intel_usecs_to_scanlines(const struct drm_display_mode *adjusted_mode, int usecs) { @@ -253,6 +257,11 @@ skl_update_plane(struct intel_plane *plane, uint32_t src_h = drm_rect_height(&plane_state->base.src) >> 16; unsigned long irqflags; +#if IS_ENABLED(CONFIG_DRM_I915_GVT) + if (dev_priv->gvt && + dev_priv->gvt->pipe_info[pipe].plane_owner[plane_id]) + return; +#endif /* Sizes are 0 based */ src_w--; src_h--; @@ -336,6 +345,12 @@ skl_disable_plane(struct intel_plane *plane, struct intel_crtc *crtc) enum pipe pipe = plane->pipe; unsigned long irqflags; +#if IS_ENABLED(CONFIG_DRM_I915_GVT) + if (dev_priv->gvt && + dev_priv->gvt->pipe_info[pipe].plane_owner[plane_id]) + return; +#endif + spin_lock_irqsave(&dev_priv->uncore.lock, irqflags); I915_WRITE_FW(PLANE_CTL(pipe, plane_id), 0); From d9831ab9e7c3fe69b64a240b1eff033291debd45 Mon Sep 17 00:00:00 2001 From: Min He Date: Thu, 28 Dec 2017 12:21:16 +0800 Subject: [PATCH 0891/1103] drm/i915/gvt: local display support in GVT-g guest This patch includes below features in GVT-g guest 1. DP on port A will be treated as external DP 2. Avoid some unnecessary checks in GVT-g guest 3. initial default vbt values by using PCH_NONE Change-Id: I6f6dfa4e5c801e7a63105310d5991adb0a0acad1 Signed-off-by: Min He Reviewed-on: Reviewed-by: Jiang, Fei Reviewed-by: Dong, Eddie Tested-by: Dong, Eddie --- drivers/gpu/drm/i915/intel_bios.c | 8 ++++++++ drivers/gpu/drm/i915/intel_ddi.c | 4 ++-- drivers/gpu/drm/i915/intel_display.c | 11 ++++++++++- drivers/gpu/drm/i915/intel_dp.c | 7 ++++++- 4 files changed, 26 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_bios.c b/drivers/gpu/drm/i915/intel_bios.c index 1faa494e2bc9..1f99373dcd77 100644 --- a/drivers/gpu/drm/i915/intel_bios.c +++ b/drivers/gpu/drm/i915/intel_bios.c @@ -1726,6 +1726,14 @@ void intel_bios_init(struct drm_i915_private *dev_priv) return; } + if (HAS_PCH_NOP(dev_priv) && !intel_vgpu_active(dev_priv)) { + DRM_DEBUG_KMS("Skipping VBT init due to disabled display.\n"); + return; + } + else if (HAS_PCH_NOP(dev_priv)) { + dev_priv->pch_type = PCH_NONE; + } + init_vbt_defaults(dev_priv); /* If the OpRegion does not have VBT, look in PCI ROM. */ diff --git a/drivers/gpu/drm/i915/intel_ddi.c b/drivers/gpu/drm/i915/intel_ddi.c index c9af34861d9e..588b03353fa7 100644 --- a/drivers/gpu/drm/i915/intel_ddi.c +++ b/drivers/gpu/drm/i915/intel_ddi.c @@ -1882,7 +1882,7 @@ bool intel_ddi_connector_get_hw_state(struct intel_connector *intel_connector) goto out; } - if (port == PORT_A) + if (port == PORT_A && !intel_vgpu_active(dev_priv)) cpu_transcoder = TRANSCODER_EDP; else cpu_transcoder = (enum transcoder) pipe; @@ -3278,7 +3278,7 @@ static bool intel_ddi_compute_config(struct intel_encoder *encoder, enum port port = encoder->port; int ret; - if (port == PORT_A) + if (port == PORT_A && !intel_vgpu_active(dev_priv)) pipe_config->cpu_transcoder = TRANSCODER_EDP; if (intel_crtc_has_type(pipe_config, INTEL_OUTPUT_HDMI)) diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index d2951096bca0..8547b379c110 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -11874,7 +11874,16 @@ verify_crtc_state(struct drm_crtc *crtc, intel_pipe_config_sanity_check(dev_priv, pipe_config); sw_config = to_intel_crtc_state(new_crtc_state); - if (!intel_pipe_config_compare(dev_priv, sw_config, + + /* + * Only check for pipe config if we are not in a GVT guest environment, + * because such a check in a GVT guest environment doesn't make any sense + * as we don't allow the guest to do a mode set, so there can very well + * be a difference between what it has programmed vs. what the host + * truly configured the HW pipe to be in. + */ + if (!intel_vgpu_active(dev_priv) && + !intel_pipe_config_compare(dev_priv, sw_config, pipe_config, false)) { I915_STATE_WARN(1, "pipe state doesn't match!\n"); intel_dump_pipe_config(intel_crtc, pipe_config, diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c index 37c08c71fdf6..23a94eedb896 100644 --- a/drivers/gpu/drm/i915/intel_dp.c +++ b/drivers/gpu/drm/i915/intel_dp.c @@ -2045,7 +2045,12 @@ static void wait_panel_status(struct intel_dp *intel_dp, I915_READ(pp_stat_reg), I915_READ(pp_ctrl_reg)); - if (intel_wait_for_register(dev_priv, + /* + * Only wait for panel status if we are not in a GVT guest environment, + * because such a wait in a GVT guest environment doesn't make any sense + * as we are exposing virtual DP monitors to the guest. + */ + if (!intel_vgpu_active(dev_priv) && intel_wait_for_register(dev_priv, pp_stat_reg, mask, value, 5000)) DRM_ERROR("Panel status timeout: status %08x control %08x\n", From 4290bbbde63fcdeaccef879b63f9bd88e9f7143a Mon Sep 17 00:00:00 2001 From: Michael Byrne Date: Fri, 14 Sep 2018 16:10:16 +0800 Subject: [PATCH 0892/1103] drm/i915/gvt: Change DomU to support 3 HDMI displays. Request DomU supports 3 virtual HDMI displays. This requires eDP-1, DP-1 and DP-2 to be disabled on DomU command line. DomU's ias.conf must also be updated to support HDMI and not DP displays. If a panel is not present on port A, DDI B and DDI C will be made active (as in Dom0). Reviewed by: Min He Signed-off-by: Michael Byrne Change-Id: Ib944de7bf9be5c19db28d35edd77a9f89148f27a Reviewed-on: Reviewed-by: He, Min Reviewed-by: Jiang, Fei Reviewed-by: Dong, Eddie Tested-by: Dong, Eddie --- drivers/gpu/drm/i915/gvt/display.c | 40 +++++++++++++++++++++--------- drivers/gpu/drm/i915/gvt/display.h | 1 + drivers/gpu/drm/i915/gvt/edid.c | 2 +- 3 files changed, 30 insertions(+), 13 deletions(-) diff --git a/drivers/gpu/drm/i915/gvt/display.c b/drivers/gpu/drm/i915/gvt/display.c index 2e01c38887bd..40ecca217733 100644 --- a/drivers/gpu/drm/i915/gvt/display.c +++ b/drivers/gpu/drm/i915/gvt/display.c @@ -314,8 +314,8 @@ static void clean_virtual_dp_monitor(struct intel_vgpu *vgpu, int port_num) port->dpcd = NULL; } -static int setup_virtual_dp_monitor(struct intel_vgpu *vgpu, int port_num, - int type, unsigned int resolution, void *edid) +static int setup_virtual_monitor(struct intel_vgpu *vgpu, int port_num, + int type, unsigned int resolution, void *edid, bool is_dp) { struct intel_vgpu_port *port = intel_vgpu_port(vgpu, port_num); int valid_extensions = 1; @@ -356,9 +356,11 @@ static int setup_virtual_dp_monitor(struct intel_vgpu *vgpu, int port_num, port->edid->data_valid = true; - memcpy(port->dpcd->data, dpcd_fix_data, DPCD_HEADER_SIZE); - port->dpcd->data_valid = true; - port->dpcd->data[DPCD_SINK_COUNT] = 0x1; + if (is_dp) { + memcpy(port->dpcd->data, dpcd_fix_data, DPCD_HEADER_SIZE); + port->dpcd->data_valid = true; + port->dpcd->data[DPCD_SINK_COUNT] = 0x1; + } port->type = type; emulate_monitor_status_change(vgpu); @@ -488,22 +490,36 @@ void intel_gvt_init_pipe_info(struct intel_gvt *gvt) } } +bool gvt_emulate_hdmi = true; + int setup_virtual_monitors(struct intel_vgpu *vgpu) { struct intel_connector *connector = NULL; struct drm_connector_list_iter conn_iter; + struct drm_i915_private *dev_priv = vgpu->gvt->dev_priv; int pipe = 0; int ret = 0; + int type = gvt_emulate_hdmi ? GVT_HDMI_A : GVT_DP_A; + int port = PORT_B; + + /* BXT have to use port A for HDMI to support 3 HDMI monitors */ + if (IS_BROXTON(dev_priv)) + port = PORT_A; drm_connector_list_iter_begin(&vgpu->gvt->dev_priv->drm, &conn_iter); for_each_intel_connector_iter(connector, &conn_iter) { if (connector->encoder->get_hw_state(connector->encoder, &pipe) && connector->detect_edid) { - ret = setup_virtual_dp_monitor(vgpu, pipe, - GVT_DP_A + pipe, 0, - connector->detect_edid); + /* Get (Dom0) port associated with current pipe. */ + port = enc_to_dig_port( + &(connector->encoder->base))->base.port; + ret = setup_virtual_monitor(vgpu, port, + type, 0, connector->detect_edid, + !gvt_emulate_hdmi); if (ret) return ret; + type++; + port++; } } drm_connector_list_iter_end(&conn_iter); @@ -560,11 +576,11 @@ int intel_vgpu_init_display(struct intel_vgpu *vgpu, u64 resolution) if (IS_BROXTON(dev_priv) || IS_KABYLAKE(dev_priv)) return setup_virtual_monitors(vgpu); else if (IS_SKYLAKE(dev_priv)) - return setup_virtual_dp_monitor(vgpu, PORT_D, GVT_DP_D, - resolution, NULL); + return setup_virtual_monitor(vgpu, PORT_D, GVT_DP_D, + resolution, NULL, true); else - return setup_virtual_dp_monitor(vgpu, PORT_B, GVT_DP_B, - resolution, NULL); + return setup_virtual_monitor(vgpu, PORT_B, GVT_DP_B, + resolution, NULL, true); } /** diff --git a/drivers/gpu/drm/i915/gvt/display.h b/drivers/gpu/drm/i915/gvt/display.h index ea7c1c525b8c..e6d3912bc730 100644 --- a/drivers/gpu/drm/i915/gvt/display.h +++ b/drivers/gpu/drm/i915/gvt/display.h @@ -140,6 +140,7 @@ enum intel_vgpu_port_type { GVT_DP_B, GVT_DP_C, GVT_DP_D, + GVT_HDMI_A, GVT_HDMI_B, GVT_HDMI_C, GVT_HDMI_D, diff --git a/drivers/gpu/drm/i915/gvt/edid.c b/drivers/gpu/drm/i915/gvt/edid.c index 4785b8a10619..fb690a4f55a0 100644 --- a/drivers/gpu/drm/i915/gvt/edid.c +++ b/drivers/gpu/drm/i915/gvt/edid.c @@ -83,7 +83,7 @@ static inline int bxt_get_port_from_gmbus0(u32 gmbus0) else if (port_select == 2) port = PORT_C; else if (port_select == 3) - port = PORT_D; + port = PORT_A; return port; } From f12ad9982d6a85c40d0284a1c7dc498c64ff5829 Mon Sep 17 00:00:00 2001 From: Michael Byrne Date: Wed, 25 Oct 2017 13:12:34 +0200 Subject: [PATCH 0893/1103] drm/i915: i915 changes to allow DomU to support 3 HDMI displays. If vgpu is active; a) and we are requesting port A, and a monitor is present on port A, force HDMI initialization (Port A is normally reserved for DP/eDP). b) ignore check for lspcon as this disables HDMI initialiisation if lspcon is supported. c) ignore check for intel_encoder->type != INTEL_OUTPUT_EDP so we can initialise HDMI (HDMI initialization will change the encode type anyway). d) allow HDMI to specify pins for port A to allow initialisation. Change-Id: I99f9636dc6403d67a0de0422eff9b504190fc5f1 Reviewed-by: Min He Signed-off-by: Michael Byrne Reviewed-on: Reviewed-by: He, Min Reviewed-by: Jiang, Fei Reviewed-by: Dong, Eddie Tested-by: Dong, Eddie --- drivers/gpu/drm/i915/intel_ddi.c | 14 +++++++++++--- drivers/gpu/drm/i915/intel_hdmi.c | 11 ++++++++++- 2 files changed, 21 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_ddi.c b/drivers/gpu/drm/i915/intel_ddi.c index 588b03353fa7..1ca518775b68 100644 --- a/drivers/gpu/drm/i915/intel_ddi.c +++ b/drivers/gpu/drm/i915/intel_ddi.c @@ -3548,11 +3548,18 @@ void intel_ddi_init(struct drm_i915_private *dev_priv, enum port port) bool init_hdmi, init_dp, init_lspcon = false; - init_hdmi = (dev_priv->vbt.ddi_port_info[port].supports_dvi || + /* + * For port A check whether vgpu is active and we have a monitor + * attached to port A. + * */ + init_hdmi = (intel_vgpu_active(dev_priv) && port == PORT_A && + (I915_READ(GEN8_DE_PORT_ISR) & BXT_DE_PORT_HP_DDIA)) || + (dev_priv->vbt.ddi_port_info[port].supports_dvi || dev_priv->vbt.ddi_port_info[port].supports_hdmi); init_dp = dev_priv->vbt.ddi_port_info[port].supports_dp; - if (intel_bios_is_lspcon_present(dev_priv, port)) { + if (!intel_vgpu_active(dev_priv) && + intel_bios_is_lspcon_present(dev_priv, port)) { /* * Lspcon device needs to be driven with DP connector * with special detection sequence. So make sure DP @@ -3648,7 +3655,8 @@ void intel_ddi_init(struct drm_i915_private *dev_priv, enum port port) /* In theory we don't need the encoder->type check, but leave it just in * case we have some really bad VBTs... */ - if (intel_encoder->type != INTEL_OUTPUT_EDP && init_hdmi) { + if ((intel_vgpu_active(dev_priv) && IS_BROXTON(dev_priv)) || + (intel_encoder->type != INTEL_OUTPUT_EDP && init_hdmi)) { if (!intel_ddi_init_hdmi_connector(intel_dig_port)) goto err; } diff --git a/drivers/gpu/drm/i915/intel_hdmi.c b/drivers/gpu/drm/i915/intel_hdmi.c index 192972a7d287..a3b6d078ca24 100644 --- a/drivers/gpu/drm/i915/intel_hdmi.c +++ b/drivers/gpu/drm/i915/intel_hdmi.c @@ -2182,6 +2182,14 @@ static u8 bxt_port_to_ddc_pin(struct drm_i915_private *dev_priv, enum port port) u8 ddc_pin; switch (port) { + case PORT_A: + if ((IS_GEN9_LP(dev_priv)) && (intel_vgpu_active(dev_priv))) + ddc_pin = GMBUS_PIN_3_BXT; + else { + MISSING_CASE(port); + ddc_pin = GMBUS_PIN_DPB; + } + break; case PORT_B: ddc_pin = GMBUS_PIN_1_BXT; break; @@ -2365,7 +2373,8 @@ void intel_hdmi_init_connector(struct intel_digital_port *intel_dig_port, intel_hdmi->ddc_bus = intel_hdmi_ddc_pin(dev_priv, port); - if (WARN_ON(port == PORT_A)) + if (!intel_vgpu_active(dev_priv) && + WARN_ON(port == PORT_A)) return; intel_encoder->hpd_pin = intel_hpd_pin_default(dev_priv, port); From 04b61a8beb2632349a3c2e2cc388a85551577d26 Mon Sep 17 00:00:00 2001 From: Min He Date: Sat, 15 Apr 2017 04:20:20 +0800 Subject: [PATCH 0894/1103] drm/i915/gvt: removed save/store registers Simple the logic of workload conext status change. Remove the unnecessary status switch when one workload is scheduled in. Signed-off-by: Zhao Yan Signed-off-by: Zhao Yakui Signed-off-by: Min He Change-Id: I509310bf1febb4cd9918440521c29eacad8c06eb Acknowledged-by: Vivek Kasireddy Reviewed-on: Reviewed-by: He, Min Reviewed-by: Jiang, Fei Reviewed-by: Dong, Eddie Tested-by: Dong, Eddie --- drivers/gpu/drm/i915/gvt/sched_policy.c | 12 -------- drivers/gpu/drm/i915/gvt/scheduler.c | 39 ++++++++----------------- 2 files changed, 12 insertions(+), 39 deletions(-) diff --git a/drivers/gpu/drm/i915/gvt/sched_policy.c b/drivers/gpu/drm/i915/gvt/sched_policy.c index c32e7d5e8629..4fac40d26549 100644 --- a/drivers/gpu/drm/i915/gvt/sched_policy.c +++ b/drivers/gpu/drm/i915/gvt/sched_policy.c @@ -444,9 +444,7 @@ void intel_vgpu_stop_schedule(struct intel_vgpu *vgpu) { struct intel_gvt_workload_scheduler *scheduler = &vgpu->gvt->scheduler; - int ring_id; struct vgpu_sched_data *vgpu_data = vgpu->sched_data; - struct drm_i915_private *dev_priv = vgpu->gvt->dev_priv; if (!vgpu_data->active) return; @@ -465,15 +463,5 @@ void intel_vgpu_stop_schedule(struct intel_vgpu *vgpu) scheduler->current_vgpu = NULL; } - intel_runtime_pm_get(dev_priv); - spin_lock_bh(&scheduler->mmio_context_lock); - for (ring_id = 0; ring_id < I915_NUM_ENGINES; ring_id++) { - if (scheduler->engine_owner[ring_id] == vgpu) { - intel_gvt_switch_mmio(vgpu, NULL, ring_id); - scheduler->engine_owner[ring_id] = NULL; - } - } - spin_unlock_bh(&scheduler->mmio_context_lock); - intel_runtime_pm_put(dev_priv); mutex_unlock(&vgpu->gvt->sched_lock); } diff --git a/drivers/gpu/drm/i915/gvt/scheduler.c b/drivers/gpu/drm/i915/gvt/scheduler.c index 43aa058e29fc..e7514bdf2fe9 100644 --- a/drivers/gpu/drm/i915/gvt/scheduler.c +++ b/drivers/gpu/drm/i915/gvt/scheduler.c @@ -203,6 +203,7 @@ static inline bool is_gvt_request(struct i915_request *req) return i915_gem_context_force_single_submission(req->gem_context); } +/* static void save_ring_hw_state(struct intel_vgpu *vgpu, int ring_id) { struct drm_i915_private *dev_priv = vgpu->gvt->dev_priv; @@ -216,6 +217,7 @@ static void save_ring_hw_state(struct intel_vgpu *vgpu, int ring_id) reg = RING_ACTHD_UDW(ring_base); vgpu_vreg(vgpu, i915_mmio_reg_offset(reg)) = I915_READ_FW(reg); } +*/ static int shadow_context_status_change(struct notifier_block *nb, unsigned long action, void *data) @@ -226,21 +228,9 @@ static int shadow_context_status_change(struct notifier_block *nb, struct intel_gvt_workload_scheduler *scheduler = &gvt->scheduler; enum intel_engine_id ring_id = req->engine->id; struct intel_vgpu_workload *workload; - unsigned long flags; - - if (!is_gvt_request(req)) { - spin_lock_irqsave(&scheduler->mmio_context_lock, flags); - if (action == INTEL_CONTEXT_SCHEDULE_IN && - scheduler->engine_owner[ring_id]) { - /* Switch ring from vGPU to host. */ - intel_gvt_switch_mmio(scheduler->engine_owner[ring_id], - NULL, ring_id); - scheduler->engine_owner[ring_id] = NULL; - } - spin_unlock_irqrestore(&scheduler->mmio_context_lock, flags); + if (!is_gvt_request(req)) return NOTIFY_OK; - } workload = scheduler->current_workload[ring_id]; if (unlikely(!workload)) @@ -248,25 +238,13 @@ static int shadow_context_status_change(struct notifier_block *nb, switch (action) { case INTEL_CONTEXT_SCHEDULE_IN: - spin_lock_irqsave(&scheduler->mmio_context_lock, flags); - if (workload->vgpu != scheduler->engine_owner[ring_id]) { - /* Switch ring from host to vGPU or vGPU to vGPU. */ - intel_gvt_switch_mmio(scheduler->engine_owner[ring_id], - workload->vgpu, ring_id); - scheduler->engine_owner[ring_id] = workload->vgpu; - } else - gvt_dbg_sched("skip ring %d mmio switch for vgpu%d\n", - ring_id, workload->vgpu->id); - spin_unlock_irqrestore(&scheduler->mmio_context_lock, flags); atomic_set(&workload->shadow_ctx_active, 1); break; case INTEL_CONTEXT_SCHEDULE_OUT: - save_ring_hw_state(workload->vgpu, ring_id); atomic_set(&workload->shadow_ctx_active, 0); break; case INTEL_CONTEXT_SCHEDULE_PREEMPTED: - save_ring_hw_state(workload->vgpu, ring_id); - break; + return NOTIFY_OK; default: WARN_ON(1); return NOTIFY_OK; @@ -909,6 +887,7 @@ static int workload_thread(void *priv) struct intel_vgpu_workload *workload = NULL; struct intel_vgpu *vgpu = NULL; int ret; + long lret; bool need_force_wake = IS_SKYLAKE(gvt->dev_priv) || IS_KABYLAKE(gvt->dev_priv) || IS_BROXTON(gvt->dev_priv); @@ -955,7 +934,13 @@ static int workload_thread(void *priv) gvt_dbg_sched("ring id %d wait workload %p\n", workload->ring_id, workload); - i915_request_wait(workload->req, 0, MAX_SCHEDULE_TIMEOUT); + lret = i915_request_wait(workload->req, 0, + MAX_SCHEDULE_TIMEOUT); + + gvt_dbg_sched("i915_wait_request %p returns %ld\n", + workload, lret); + if (lret >= 0 && workload->status == -EINPROGRESS) + workload->status = 0; complete: gvt_dbg_sched("will complete workload %p, status: %d\n", From d0ce8b02a4a46d276e544cf1342f97ed1edc8f9d Mon Sep 17 00:00:00 2001 From: Ping Gao Date: Fri, 2 Jun 2017 09:03:19 +0800 Subject: [PATCH 0895/1103] drm/i915/gvt: ivi: lazy shadow context It's a significant overhead to shadow guest context by copying all the pages, for performance consideration this patch introduce context lazy shadow, it copy the first page of the context only and let the GGTT entries of other shadow context pages point to the corresponding pages of the guest context. Change-Id: I6b806da29fa75eff73122d0328b1a277780eabe1 Signed-off-by: Ping Gao Acknowledged-by: Singh, Satyeshwar Reviewed-on: Reviewed-by: He, Min Reviewed-by: Jiang, Fei Reviewed-by: Dong, Eddie Tested-by: Dong, Eddie --- drivers/gpu/drm/i915/gvt/scheduler.c | 92 +++++++++++++++++++++------- 1 file changed, 69 insertions(+), 23 deletions(-) diff --git a/drivers/gpu/drm/i915/gvt/scheduler.c b/drivers/gpu/drm/i915/gvt/scheduler.c index e7514bdf2fe9..f0f0ada705cd 100644 --- a/drivers/gpu/drm/i915/gvt/scheduler.c +++ b/drivers/gpu/drm/i915/gvt/scheduler.c @@ -119,6 +119,7 @@ static void sr_oa_regs(struct intel_vgpu_workload *workload, } } +static bool enable_lazy_shadow_ctx = true; static int populate_shadow_context(struct intel_vgpu_workload *workload) { struct intel_vgpu *vgpu = workload->vgpu; @@ -130,6 +131,10 @@ static int populate_shadow_context(struct intel_vgpu_workload *workload) struct page *page; void *dst; unsigned long context_gpa, context_page_num; + struct drm_i915_private *dev_priv = gvt->dev_priv; + struct i915_ggtt *ggtt = &gvt->dev_priv->ggtt; + dma_addr_t addr; + gen8_pte_t __iomem *pte; int i; gvt_dbg_sched("ring id %d workload lrca %x", ring_id, @@ -143,6 +148,18 @@ static int populate_shadow_context(struct intel_vgpu_workload *workload) context_page_num = 19; i = 2; +#ifdef CONFIG_INTEL_IOMMU + /* + * In case IOMMU for graphics is turned on, we don't want to + * turn on lazy shadow context feature because it will touch + * GGTT entries which require a BKL and since this is a + * performance enhancement feature, we will end up negating + * the performance. + */ + if(intel_iommu_gfx_mapped) { + enable_lazy_shadow_ctx = false; + } +#endif while (i < context_page_num) { context_gpa = intel_vgpu_gma_to_gpa(vgpu->gtt.ggtt_mm, @@ -153,14 +170,41 @@ static int populate_shadow_context(struct intel_vgpu_workload *workload) return -EFAULT; } - page = i915_gem_object_get_page(ctx_obj, LRC_HEADER_PAGES + i); - dst = kmap(page); - intel_gvt_hypervisor_read_gpa(vgpu, context_gpa, dst, + if (!enable_lazy_shadow_ctx) { + page = i915_gem_object_get_page(ctx_obj, + LRC_PPHWSP_PN + i); + dst = kmap(page); + intel_gvt_hypervisor_read_gpa(vgpu, context_gpa, dst, I915_GTT_PAGE_SIZE); - kunmap(page); + kunmap(page); + } else { + unsigned long mfn; + struct i915_gem_context *shadow_ctx = + workload->vgpu->submission.shadow_ctx; + + addr = i915_ggtt_offset( + shadow_ctx->__engine[ring_id].state) + + (LRC_PPHWSP_PN + i) * PAGE_SIZE; + pte = (gen8_pte_t __iomem *)ggtt->gsm + + (addr >> PAGE_SHIFT); + + mfn = intel_gvt_hypervisor_gfn_to_mfn(vgpu, + context_gpa >> 12); + if (mfn == INTEL_GVT_INVALID_ADDR) { + gvt_vgpu_err("fail to translate gfn during context shadow\n"); + return -ENXIO; + } + + mfn <<= 12; + mfn |= _PAGE_PRESENT | _PAGE_RW | PPAT_CACHED; + writeq(mfn, pte); + } i++; } + I915_WRITE(GFX_FLSH_CNTL_GEN6, GFX_FLSH_CNTL_EN); + POSTING_READ(GFX_FLSH_CNTL_GEN6); + page = i915_gem_object_get_page(ctx_obj, LRC_STATE_PN); shadow_ring_context = kmap(page); @@ -712,29 +756,31 @@ static void update_guest_context(struct intel_vgpu_workload *workload) gvt_dbg_sched("ring id %d workload lrca %x\n", rq->engine->id, workload->ctx_desc.lrca); - context_page_num = rq->engine->context_size; - context_page_num = context_page_num >> PAGE_SHIFT; + if (!enable_lazy_shadow_ctx) { + context_page_num = rq->engine->context_size; + context_page_num = context_page_num >> PAGE_SHIFT; - if (IS_BROADWELL(gvt->dev_priv) && rq->engine->id == RCS) - context_page_num = 19; + if (IS_BROADWELL(gvt->dev_priv) && rq->engine->id == RCS) + context_page_num = 19; - i = 2; + i = 2; - while (i < context_page_num) { - context_gpa = intel_vgpu_gma_to_gpa(vgpu->gtt.ggtt_mm, - (u32)((workload->ctx_desc.lrca + i) << - I915_GTT_PAGE_SHIFT)); - if (context_gpa == INTEL_GVT_INVALID_ADDR) { - gvt_vgpu_err("invalid guest context descriptor\n"); - return; - } + while (i < context_page_num) { + context_gpa = intel_vgpu_gma_to_gpa(vgpu->gtt.ggtt_mm, + (u32)((workload->ctx_desc.lrca + i) << + I915_GTT_PAGE_SHIFT)); + if (context_gpa == INTEL_GVT_INVALID_ADDR) { + gvt_vgpu_err("invalid guest context descriptor\n"); + return; + } - page = i915_gem_object_get_page(ctx_obj, LRC_HEADER_PAGES + i); - src = kmap(page); - intel_gvt_hypervisor_write_gpa(vgpu, context_gpa, src, - I915_GTT_PAGE_SIZE); - kunmap(page); - i++; + page = i915_gem_object_get_page(ctx_obj, LRC_HEADER_PAGES + i); + src = kmap(page); + intel_gvt_hypervisor_write_gpa(vgpu, context_gpa, src, + I915_GTT_PAGE_SIZE); + kunmap(page); + i++; + } } intel_gvt_hypervisor_write_gpa(vgpu, workload->ring_context_gpa + From 75aadf61781c61ee6bd9b0b561416b986c35d7d3 Mon Sep 17 00:00:00 2001 From: Pei Zhang Date: Fri, 14 Sep 2018 16:10:17 +0800 Subject: [PATCH 0896/1103] drm/i915/gvt: add some MMIO value initialization There are some virtual MMIO registers contains static value which won't be changed by guest writing. Those registers could be optimized with a static value initialization, and do nothing in write handler. Change-Id: I1a85f717ef23c171651f1a1cad5c73880d538e3d Signed-off-by: Pei Zhang Acknowledged-by: Singh, Satyeshwar Reviewed-on: Reviewed-by: He, Min Reviewed-by: Jiang, Fei Reviewed-by: Dong, Eddie Tested-by: Dong, Eddie --- drivers/gpu/drm/i915/gvt/handlers.c | 32 +++++++---------------------- drivers/gpu/drm/i915/gvt/mmio.c | 9 ++++++++ 2 files changed, 16 insertions(+), 25 deletions(-) diff --git a/drivers/gpu/drm/i915/gvt/handlers.c b/drivers/gpu/drm/i915/gvt/handlers.c index e1855328eba6..495d6a298924 100644 --- a/drivers/gpu/drm/i915/gvt/handlers.c +++ b/drivers/gpu/drm/i915/gvt/handlers.c @@ -413,27 +413,9 @@ static int lcpll_ctl_mmio_write(struct intel_vgpu *vgpu, unsigned int offset, return 0; } -static int dpy_reg_mmio_read(struct intel_vgpu *vgpu, unsigned int offset, +static int mmio_write_empty(struct intel_vgpu *vgpu, unsigned int offset, void *p_data, unsigned int bytes) { - switch (offset) { - case 0xe651c: - case 0xe661c: - case 0xe671c: - case 0xe681c: - vgpu_vreg(vgpu, offset) = 1 << 17; - break; - case 0xe6c04: - vgpu_vreg(vgpu, offset) = 0x3; - break; - case 0xe6e1c: - vgpu_vreg(vgpu, offset) = 0x2f << 16; - break; - default: - return -EINVAL; - } - - read_vreg(vgpu, offset, p_data, bytes); return 0; } @@ -2240,12 +2222,12 @@ static int init_generic_mmio_info(struct intel_gvt *gvt) MMIO_D(PCH_PP_ON_DELAYS, D_ALL); MMIO_D(PCH_PP_OFF_DELAYS, D_ALL); - MMIO_DH(_MMIO(0xe651c), D_ALL, dpy_reg_mmio_read, NULL); - MMIO_DH(_MMIO(0xe661c), D_ALL, dpy_reg_mmio_read, NULL); - MMIO_DH(_MMIO(0xe671c), D_ALL, dpy_reg_mmio_read, NULL); - MMIO_DH(_MMIO(0xe681c), D_ALL, dpy_reg_mmio_read, NULL); - MMIO_DH(_MMIO(0xe6c04), D_ALL, dpy_reg_mmio_read, NULL); - MMIO_DH(_MMIO(0xe6e1c), D_ALL, dpy_reg_mmio_read, NULL); + MMIO_DH(_MMIO(0xe651c), D_ALL, NULL, mmio_write_empty); + MMIO_DH(_MMIO(0xe661c), D_ALL, NULL, mmio_write_empty); + MMIO_DH(_MMIO(0xe671c), D_ALL, NULL, mmio_write_empty); + MMIO_DH(_MMIO(0xe681c), D_ALL, NULL, mmio_write_empty); + MMIO_DH(_MMIO(0xe6c04), D_ALL, NULL, mmio_write_empty); + MMIO_DH(_MMIO(0xe6e1c), D_ALL, NULL, mmio_write_empty); MMIO_RO(PCH_PORT_HOTPLUG, D_ALL, 0, PORTA_HOTPLUG_STATUS_MASK diff --git a/drivers/gpu/drm/i915/gvt/mmio.c b/drivers/gpu/drm/i915/gvt/mmio.c index 9bb9a85c992c..4149eae235b5 100644 --- a/drivers/gpu/drm/i915/gvt/mmio.c +++ b/drivers/gpu/drm/i915/gvt/mmio.c @@ -282,6 +282,15 @@ void intel_vgpu_reset_mmio(struct intel_vgpu *vgpu, bool dmlr) memcpy(vgpu->mmio.sreg, mmio, GVT_GEN8_MMIO_RESET_OFFSET); } + /* below vreg init value are got from handler.c, + * which won't change during vgpu life cycle + */ + vgpu_vreg(vgpu, 0xe651c) = 1 << 17; + vgpu_vreg(vgpu, 0xe661c) = 1 << 17; + vgpu_vreg(vgpu, 0xe671c) = 1 << 17; + vgpu_vreg(vgpu, 0xe681c) = 1 << 17; + vgpu_vreg(vgpu, 0xe6c04) = 3; + vgpu_vreg(vgpu, 0xe6e1c) = 0x2f << 16; } /** From ea48596ab7b47ee6e428b18940cfd1a61c16ce61 Mon Sep 17 00:00:00 2001 From: Min He Date: Fri, 14 Sep 2018 16:10:17 +0800 Subject: [PATCH 0897/1103] drm/i915/gvt: added option to disable wa_ctx shadowing This patch add an option in GVTg to disable shadowing wa_ctx. In current IVI environment, since Dom0 and DomU has the same i915, the wa_ctx of them is same. So no need to do shadow copy of wa_ctx. By default, shadowing wa_ctx is disabled. If for testing purpose, people can enable it manually by modifying the code. Change-Id: I535af0a720e7ccfbca80686e45c79c5c2a3dd9c6 Signed-off-by: Min He Reviewed-by: Singh, Satyeshwar Reviewed-on: Reviewed-by: Jiang, Fei Reviewed-by: Dong, Eddie Tested-by: Dong, Eddie --- drivers/gpu/drm/i915/gvt/scheduler.c | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/drivers/gpu/drm/i915/gvt/scheduler.c b/drivers/gpu/drm/i915/gvt/scheduler.c index f0f0ada705cd..9d8f0bfc434c 100644 --- a/drivers/gpu/drm/i915/gvt/scheduler.c +++ b/drivers/gpu/drm/i915/gvt/scheduler.c @@ -41,6 +41,8 @@ #define RING_CTX_OFF(x) \ offsetof(struct execlist_ring_context, x) +bool gvt_shadow_wa_ctx = false; + static void set_context_pdp_root_pointer( struct execlist_ring_context *ring_context, u32 pdp[8]) @@ -403,7 +405,8 @@ int intel_gvt_scan_and_shadow_workload(struct intel_vgpu_workload *workload) goto err_unpin; if ((workload->ring_id == RCS) && - (workload->wa_ctx.indirect_ctx.size != 0)) { + (workload->wa_ctx.indirect_ctx.size != 0) + && gvt_shadow_wa_ctx) { ret = intel_gvt_scan_and_shadow_wa_ctx(&workload->wa_ctx); if (ret) goto err_shadow; @@ -628,10 +631,12 @@ static int prepare_workload(struct intel_vgpu_workload *workload) goto err_unpin_mm; } - ret = prepare_shadow_wa_ctx(&workload->wa_ctx); - if (ret) { - gvt_vgpu_err("fail to prepare_shadow_wa_ctx\n"); - goto err_shadow_batch; + if (gvt_shadow_wa_ctx) { + ret = prepare_shadow_wa_ctx(&workload->wa_ctx); + if (ret) { + gvt_vgpu_err("fail to prepare_shadow_wa_ctx\n"); + goto err_shadow_batch; + } } if (workload->prepare) { @@ -887,7 +892,8 @@ static void complete_current_workload(struct intel_gvt *gvt, int ring_id) if (!workload->status) { release_shadow_batch_buffer(workload); - release_shadow_wa_ctx(&workload->wa_ctx); + if(gvt_shadow_wa_ctx) + release_shadow_wa_ctx(&workload->wa_ctx); } if (workload->status || (vgpu->resetting_eng & ENGINE_MASK(ring_id))) { From 7e2b0b8f79b84ab0ea277a8a165abd15c3862ad6 Mon Sep 17 00:00:00 2001 From: Pei Zhang Date: Thu, 22 Jun 2017 18:45:21 +0800 Subject: [PATCH 0898/1103] drm/i915/gvt: enable ppgtt oos sync by default After enabling oos page sync, the loading time of GVT-g guest workload will be reduced. Signed-off-by: Pei Zhang Reviewed-by: Vivek Kasireddy Reviewed-by: He, Min Reviewed-by: Jiang, Fei Reviewed-by: Dong, Eddie Tested-by: Dong, Eddie --- drivers/gpu/drm/i915/gvt/gtt.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/i915/gvt/gtt.c b/drivers/gpu/drm/i915/gvt/gtt.c index 6262308c4a78..af3b1dea4510 100644 --- a/drivers/gpu/drm/i915/gvt/gtt.c +++ b/drivers/gpu/drm/i915/gvt/gtt.c @@ -44,7 +44,7 @@ #define gvt_vdbg_mm(fmt, args...) #endif -static bool enable_out_of_sync = false; +static bool enable_out_of_sync = true; static int preallocated_oos_pages = 8192; /* From d25d0f0ebff6f5609afb21a36eb6d1c7758ed809 Mon Sep 17 00:00:00 2001 From: Min He Date: Fri, 14 Sep 2018 16:10:17 +0800 Subject: [PATCH 0899/1103] drm/i915/gvt: emit shadow ppgtt root in LRI Usually the PDP/PML4 root poiners should be updated through the context, however, on BXT, we found that sometimes the PDP/PML4 root pointers will be reverted back to an old value, which causes GPU hang. The reason is still unclear, but by adding LRI command in the ring buffer, we are able to update the PDP/PML4 root pointers successfully. So far we will treat this patch as a workaround, unless we figure out why the PDP/PML4 root pointers was reverted back. Change-Id: Id65f7621ed9d45073f220fd2d91112558e7820d9 Signed-off-by: Min He Reviewed-by: Singh, Satyeshwar Reviewed-by: Jiang, Fei Reviewed-by: Dong, Eddie Tested-by: Dong, Eddie --- drivers/gpu/drm/i915/gvt/cmd_parser.c | 27 +++++++++++++++++++++++++++ drivers/gpu/drm/i915/gvt/cmd_parser.h | 1 + drivers/gpu/drm/i915/gvt/scheduler.c | 8 ++++++++ 3 files changed, 36 insertions(+) diff --git a/drivers/gpu/drm/i915/gvt/cmd_parser.c b/drivers/gpu/drm/i915/gvt/cmd_parser.c index a614db310ea2..95244b2cb679 100644 --- a/drivers/gpu/drm/i915/gvt/cmd_parser.c +++ b/drivers/gpu/drm/i915/gvt/cmd_parser.c @@ -2710,6 +2710,33 @@ static int scan_wa_ctx(struct intel_shadow_wa_ctx *wa_ctx) return ret; } +#define GEN8_PDPES 4 +int gvt_emit_pdps(struct intel_vgpu_workload *workload) +{ + const int num_cmds = GEN8_PDPES * 2; + struct i915_request *req = workload->req; + struct intel_engine_cs *engine = req->engine; + u32 *cs; + u32 *pdps = (u32 *)(workload->shadow_mm->ppgtt_mm.shadow_pdps); + int i; + + cs = intel_ring_begin(req, num_cmds * 2 + 2); + if (IS_ERR(cs)) + return PTR_ERR(cs); + + *cs++ = MI_LOAD_REGISTER_IMM(num_cmds); + for (i = 0; i < GEN8_PDPES; i++) { + *cs++ = i915_mmio_reg_offset(GEN8_RING_PDP_LDW(engine, i)); + *cs++ = pdps[i * 2]; + *cs++ = i915_mmio_reg_offset(GEN8_RING_PDP_UDW(engine, i)); + *cs++ = pdps[i * 2 + 1]; + } + *cs++ = MI_NOOP; + intel_ring_advance(req, cs); + + return 0; +} + static int shadow_workload_ring_buffer(struct intel_vgpu_workload *workload) { struct intel_vgpu *vgpu = workload->vgpu; diff --git a/drivers/gpu/drm/i915/gvt/cmd_parser.h b/drivers/gpu/drm/i915/gvt/cmd_parser.h index 286703643002..1356803a0586 100644 --- a/drivers/gpu/drm/i915/gvt/cmd_parser.h +++ b/drivers/gpu/drm/i915/gvt/cmd_parser.h @@ -46,4 +46,5 @@ int intel_gvt_scan_and_shadow_ringbuffer(struct intel_vgpu_workload *workload); int intel_gvt_scan_and_shadow_wa_ctx(struct intel_shadow_wa_ctx *wa_ctx); +int gvt_emit_pdps(struct intel_vgpu_workload *workload); #endif diff --git a/drivers/gpu/drm/i915/gvt/scheduler.c b/drivers/gpu/drm/i915/gvt/scheduler.c index 9d8f0bfc434c..7243324cb93c 100644 --- a/drivers/gpu/drm/i915/gvt/scheduler.c +++ b/drivers/gpu/drm/i915/gvt/scheduler.c @@ -619,6 +619,14 @@ static int prepare_workload(struct intel_vgpu_workload *workload) goto err_unpin_mm; } + /* we consider this as an workaround to avoid the situation that + * PDP's not updated, and right now we only limit it to BXT platform + * since it's not reported on the other platforms + */ + if (IS_BROXTON(vgpu->gvt->dev_priv)) { + gvt_emit_pdps(workload); + } + ret = copy_workload_to_ring_buffer(workload); if (ret) { gvt_vgpu_err("fail to generate request\n"); From d15d811229b84d9d55a7e607a82e091b4ef7a83e Mon Sep 17 00:00:00 2001 From: Satyeshwar Singh Date: Wed, 13 Sep 2017 17:29:57 -0700 Subject: [PATCH 0900/1103] drm/i915/gvt: Raise a uevent when Dom 0 is ready for Dom U HV vendors want to know when Dom 0 is ready to start a Dom U because they want to start Dom U as early as possible. This feature informs XenGT module as soon as Dom 0 is ready. In our example, we raise a uevent from XenGT module but the HV vendors are free to change the module to do any custom action that they want. Change-Id: Ibfdaca65002825e14e15527c386db00f59b372e5 Signed-off-by: Satyeshwar Singh Reviewed-by: He, Min Reviewed-on: Reviewed-by: Jiang, Fei Reviewed-by: Dong, Eddie Tested-by: Dong, Eddie --- drivers/gpu/drm/i915/gvt/gvt.c | 8 ++++++++ drivers/gpu/drm/i915/gvt/gvt.h | 1 + drivers/gpu/drm/i915/gvt/hypercall.h | 1 + drivers/gpu/drm/i915/gvt/mpt.h | 8 ++++++++ drivers/gpu/drm/i915/intel_display.c | 13 +++++++++++++ 5 files changed, 31 insertions(+) diff --git a/drivers/gpu/drm/i915/gvt/gvt.c b/drivers/gpu/drm/i915/gvt/gvt.c index 8523fbc1e494..25e0a58a24a5 100644 --- a/drivers/gpu/drm/i915/gvt/gvt.c +++ b/drivers/gpu/drm/i915/gvt/gvt.c @@ -511,6 +511,14 @@ int intel_gvt_init_device(struct drm_i915_private *dev_priv) return ret; } +int gvt_dom0_ready(struct drm_i915_private *dev_priv) +{ + if (!intel_gvt_active(dev_priv)) + return 0; + + return intel_gvt_hypervisor_dom0_ready(); +} + #if IS_ENABLED(CONFIG_DRM_I915_GVT_KVMGT) MODULE_SOFTDEP("pre: kvmgt"); #endif diff --git a/drivers/gpu/drm/i915/gvt/gvt.h b/drivers/gpu/drm/i915/gvt/gvt.h index 47ed1789ea28..9c291924c1d0 100644 --- a/drivers/gpu/drm/i915/gvt/gvt.h +++ b/drivers/gpu/drm/i915/gvt/gvt.h @@ -592,6 +592,7 @@ struct intel_gvt_ops { unsigned int); }; +int gvt_dom0_ready(struct drm_i915_private *dev_priv); enum { GVT_FAILSAFE_UNSUPPORTED_GUEST, diff --git a/drivers/gpu/drm/i915/gvt/hypercall.h b/drivers/gpu/drm/i915/gvt/hypercall.h index 5af11cf1b482..f14cff32ae2f 100644 --- a/drivers/gpu/drm/i915/gvt/hypercall.h +++ b/drivers/gpu/drm/i915/gvt/hypercall.h @@ -64,6 +64,7 @@ struct intel_gvt_mpt { int (*get_vfio_device)(void *vgpu); void (*put_vfio_device)(void *vgpu); bool (*is_valid_gfn)(unsigned long handle, unsigned long gfn); + int (*dom0_ready)(void); }; extern struct intel_gvt_mpt xengt_mpt; diff --git a/drivers/gpu/drm/i915/gvt/mpt.h b/drivers/gpu/drm/i915/gvt/mpt.h index 67f19992b226..feed7adb6fde 100644 --- a/drivers/gpu/drm/i915/gvt/mpt.h +++ b/drivers/gpu/drm/i915/gvt/mpt.h @@ -362,4 +362,12 @@ static inline bool intel_gvt_hypervisor_is_valid_gfn( return intel_gvt_host.mpt->is_valid_gfn(vgpu->handle, gfn); } +static inline int intel_gvt_hypervisor_dom0_ready(void) +{ + if (!intel_gvt_host.mpt->dom0_ready) + return 0; + + return intel_gvt_host.mpt->dom0_ready(); +} + #endif /* _GVT_MPT_H_ */ diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 8547b379c110..d095f1f46de8 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -49,6 +49,10 @@ #include #include +#if IS_ENABLED(CONFIG_DRM_I915_GVT) +#include "gvt.h" +#endif + /* Primary plane formats for gen <= 3 */ static const uint32_t i8xx_primary_formats[] = { DRM_FORMAT_C8, @@ -14324,6 +14328,15 @@ static void intel_setup_outputs(struct drm_i915_private *dev_priv) intel_encoder_clones(encoder); } +#if IS_ENABLED(CONFIG_DRM_I915_GVT) + /* + * Encoders have been initialized. If we are in VGT mode, + * let's inform the HV that it can start Dom U as Dom 0 + * is ready to accept new Dom Us. + */ + gvt_dom0_ready(dev_priv); +#endif + intel_init_pch_refclk(dev_priv); drm_helper_move_panel_connectors_to_head(&dev_priv->drm); From 879db2209b69037aaddaadf4fb2ddcfc66f57d77 Mon Sep 17 00:00:00 2001 From: Satyeshwar Singh Date: Fri, 14 Sep 2018 16:10:17 +0800 Subject: [PATCH 0901/1103] drm/i915/gvt: Don't load CSR for Dom U This change prevents the CSR firmware to be loaded for Dom U as we don't allow Dom U to control display power management settings and it can save time for Dom U bootup by skipping this firmware loading. Tests have shown this can be anywhere between 80 to 500 ms. Change-Id: Ic8119d69eaf01fda3082f055e306a2d6263411ed Signed-off-by: Satyeshwar Singh Reviewed-by: Vivek Kasireddy Reviewed-on: Reviewed-by: He, Min Reviewed-by: Jiang, Fei Reviewed-by: Dong, Eddie Tested-by: Dong, Eddie --- drivers/gpu/drm/i915/intel_csr.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/i915/intel_csr.c b/drivers/gpu/drm/i915/intel_csr.c index cf9b600cca79..addb223604fa 100644 --- a/drivers/gpu/drm/i915/intel_csr.c +++ b/drivers/gpu/drm/i915/intel_csr.c @@ -453,7 +453,13 @@ void intel_csr_ucode_init(struct drm_i915_private *dev_priv) INIT_WORK(&dev_priv->csr.work, csr_load_work_fn); - if (!HAS_CSR(dev_priv)) + /* + * In a GVTg enabled environment, loading the CSR firmware for DomU doesn't + * make much sense since we don't allow it to control display power + * management settings. Furthermore, we can save some time for DomU bootup + * by skipping CSR loading. + */ + if (!HAS_CSR(dev_priv) || intel_vgpu_active(dev_priv)) return; if (i915_modparams.dmc_firmware_path) From aced4adcef4c11b058d669679dc89fb11d3c4fd9 Mon Sep 17 00:00:00 2001 From: Min He Date: Fri, 14 Sep 2018 16:10:17 +0800 Subject: [PATCH 0902/1103] drm/i915/gvt: add acrngt support Refine the structure based on the latest gvt-g structure Change-Id: Ifd599d5a7375d73c557a878027bca7ba8851c5f1 Signed-off-by: Fei Jiang Signed-off-by: Min He Signed-off-by: Zhao Yakui Reviewed-on: Reviewed-by: Dong, Eddie Tested-by: Dong, Eddie --- drivers/gpu/drm/i915/Kconfig | 9 + drivers/gpu/drm/i915/gvt/Makefile | 3 +- drivers/gpu/drm/i915/gvt/acrngt.c | 893 +++++++++++++++++++++++++++ drivers/gpu/drm/i915/gvt/acrngt.h | 81 +++ drivers/gpu/drm/i915/gvt/gvt.c | 6 + drivers/gpu/drm/i915/gvt/gvt.h | 1 + drivers/gpu/drm/i915/gvt/hypercall.h | 1 + 7 files changed, 993 insertions(+), 1 deletion(-) create mode 100644 drivers/gpu/drm/i915/gvt/acrngt.c create mode 100644 drivers/gpu/drm/i915/gvt/acrngt.h diff --git a/drivers/gpu/drm/i915/Kconfig b/drivers/gpu/drm/i915/Kconfig index 33a458b7f1fc..19dfbf39f6ca 100644 --- a/drivers/gpu/drm/i915/Kconfig +++ b/drivers/gpu/drm/i915/Kconfig @@ -127,6 +127,15 @@ config DRM_I915_GVT_KVMGT help Choose this option if you want to enable KVMGT support for Intel GVT-g. +config DRM_I915_GVT_ACRN_GVT + tristate "Enable ACRN support for Intel GVT-g" + depends on DRM_I915_GVT + depends on ACRN + depends on ACRN_VHM + default n + help + Choose this option if you want to enable ACRN_GVT support for + Intel GVT-g under ACRN hypervisor environment. menu "drm/i915 Debugging" depends on DRM_I915 diff --git a/drivers/gpu/drm/i915/gvt/Makefile b/drivers/gpu/drm/i915/gvt/Makefile index b016dc753db9..0acb4dabc00c 100644 --- a/drivers/gpu/drm/i915/gvt/Makefile +++ b/drivers/gpu/drm/i915/gvt/Makefile @@ -5,6 +5,7 @@ GVT_SOURCE := gvt.o aperture_gm.o handlers.o vgpu.o trace_points.o firmware.o \ execlist.o scheduler.o sched_policy.o mmio_context.o cmd_parser.o debugfs.o \ fb_decoder.o dmabuf.o page_track.o -ccflags-y += -I$(src) -I$(src)/$(GVT_DIR) +ccflags-y += -I$(src) -I$(src)/$(GVT_DIR) -Wall i915-y += $(addprefix $(GVT_DIR)/, $(GVT_SOURCE)) obj-$(CONFIG_DRM_I915_GVT_KVMGT) += $(GVT_DIR)/kvmgt.o +obj-$(CONFIG_DRM_I915_GVT_ACRN_GVT) += $(GVT_DIR)/acrngt.o diff --git a/drivers/gpu/drm/i915/gvt/acrngt.c b/drivers/gpu/drm/i915/gvt/acrngt.c new file mode 100644 index 000000000000..fec5751a4bed --- /dev/null +++ b/drivers/gpu/drm/i915/gvt/acrngt.c @@ -0,0 +1,893 @@ +/* + * Interfaces coupled to ACRN + * + * Copyright(c) 2018 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of Version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program. + * + */ + +/* + * NOTE: + * This file contains hypervisor specific interactions to + * implement the concept of mediated pass-through framework. + * What this file provides is actually a general abstraction + * of in-kernel device model, which is not gvt specific. + * + */ +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include "acrngt.h" + +MODULE_AUTHOR("Intel Corporation"); +MODULE_DESCRIPTION("ACRNGT mediated passthrough driver"); +MODULE_LICENSE("GPL"); +MODULE_VERSION("0.1"); + +#define ASSERT(x) \ +do { if (x) break; \ + printk(KERN_EMERG "### ASSERTION FAILED %s: %s: %d: %s\n", \ + __FILE__, __func__, __LINE__, #x); dump_stack(); BUG(); \ +} while (0) + + +struct kobject *acrn_gvt_ctrl_kobj; +static struct kset *acrn_gvt_kset; +static DEFINE_MUTEX(acrn_gvt_sysfs_lock); + +struct gvt_acrngt acrngt_priv; +const struct intel_gvt_ops *intel_gvt_ops; + +static void disable_domu_plane(int pipe, int plane) +{ + struct drm_i915_private *dev_priv = acrngt_priv.gvt->dev_priv; + + I915_WRITE(PLANE_CTL(pipe, plane), 0); + + I915_WRITE(PLANE_SURF(pipe, plane), 0); + POSTING_READ(PLANE_SURF(pipe, plane)); +} + +void acrngt_instance_destroy(struct intel_vgpu *vgpu) +{ + int pipe, plane; + struct acrngt_hvm_dev *info = NULL; + struct intel_gvt *gvt = acrngt_priv.gvt; + + if (vgpu) { + info = (struct acrngt_hvm_dev *)vgpu->handle; + + if (info && info->emulation_thread != NULL) + kthread_stop(info->emulation_thread); + + for_each_pipe(gvt->dev_priv, pipe) { + for_each_universal_plane(gvt->dev_priv, pipe, plane) { + if (gvt->pipe_info[pipe].plane_owner[plane] == + vgpu->id) { + disable_domu_plane(pipe, plane); + } + } + } + + intel_gvt_ops->vgpu_deactivate(vgpu); + intel_gvt_ops->vgpu_destroy(vgpu); + } + + if (info) { + gvt_dbg_core("destroy vgpu instance, vm id: %d, client %d", + info->vm_id, info->client); + + if (info->client != 0) + acrn_ioreq_destroy_client(info->client); + + if (info->vm) + put_vm(info->vm); + + kfree(info); + } +} + +static bool acrngt_write_cfg_space(struct intel_vgpu *vgpu, + unsigned int port, unsigned int bytes, unsigned long val) +{ + if (intel_gvt_ops->emulate_cfg_write(vgpu, port, &val, bytes)) { + gvt_err("failed to write config space port 0x%x\n", port); + return false; + } + return true; +} + +static bool acrngt_read_cfg_space(struct intel_vgpu *vgpu, + unsigned int port, unsigned int bytes, unsigned long *val) +{ + unsigned long data; + + if (intel_gvt_ops->emulate_cfg_read(vgpu, port, &data, bytes)) { + gvt_err("failed to read config space port 0x%x\n", port); + return false; + } + memcpy(val, &data, bytes); + return true; +} + +static int acrngt_hvm_pio_emulation(struct intel_vgpu *vgpu, + struct vhm_request *req) +{ + if (req->reqs.pci_request.direction == REQUEST_READ) { + /* PIO READ */ + gvt_dbg_core("handle pio read emulation at port 0x%x\n", + req->reqs.pci_request.reg); + if (!acrngt_read_cfg_space(vgpu, + req->reqs.pci_request.reg, + req->reqs.pci_request.size, + (unsigned long *)&req->reqs.pci_request.value)) { + gvt_err("failed to read pio at addr 0x%x\n", + req->reqs.pci_request.reg); + return -EINVAL; + } + } else if (req->reqs.pci_request.direction == REQUEST_WRITE) { + /* PIO WRITE */ + gvt_dbg_core("handle pio write emulation at address 0x%x, " + "value 0x%x\n", + req->reqs.pci_request.reg, req->reqs.pci_request.value); + if (!acrngt_write_cfg_space(vgpu, + req->reqs.pci_request.reg, + req->reqs.pci_request.size, + (unsigned long)req->reqs.pci_request.value)) { + gvt_err("failed to write pio at addr 0x%x\n", + req->reqs.pci_request.reg); + return -EINVAL; + } + } + return 0; +} + +static int acrngt_hvm_write_handler(struct intel_vgpu *vgpu, uint64_t pa, + void *p_data, unsigned int bytes) +{ + + /* Check whether pa is ppgtt */ + if (intel_gvt_ops->write_protect_handler(vgpu, pa, p_data, bytes) == 0) + return 0; + + /* pa is mmio reg or gtt */ + return intel_gvt_ops->emulate_mmio_write(vgpu, pa, p_data, bytes); +} + +static int acrngt_hvm_mmio_emulation(struct intel_vgpu *vgpu, + struct vhm_request *req) +{ + if (req->reqs.mmio_request.direction == REQUEST_READ) { + /* MMIO READ */ + gvt_dbg_core("handle mmio read emulation at address 0x%llx\n", + req->reqs.mmio_request.address); + if (intel_gvt_ops->emulate_mmio_read(vgpu, + req->reqs.mmio_request.address, + &req->reqs.mmio_request.value, + req->reqs.mmio_request.size)) { + gvt_err("failed to read mmio at addr 0x%llx\n", + req->reqs.mmio_request.address); + return -EINVAL; + } + } else if (req->reqs.mmio_request.direction == REQUEST_WRITE) { + /* MMIO Write */ + if (acrngt_hvm_write_handler(vgpu, + req->reqs.mmio_request.address, + &req->reqs.mmio_request.value, + req->reqs.mmio_request.size)) { + gvt_err("failed to write mmio at addr 0x%llx\n", + req->reqs.mmio_request.address); + return -EINVAL; + } + gvt_dbg_core("handle mmio write emulation at address 0x%llx, " + "value 0x%llx\n", + req->reqs.mmio_request.address, req->reqs.mmio_request.value); + } + + return 0; +} + +static void handle_request_error(struct intel_vgpu *vgpu) +{ + mutex_lock(&vgpu->gvt->lock); + if (vgpu->failsafe == false) { + vgpu->failsafe= true; + gvt_err("Now vgpu %d will enter failsafe mode.\n", vgpu->id); + } + mutex_unlock(&vgpu->gvt->lock); +} + +static int acrngt_emulation_thread(void *priv) +{ + struct intel_vgpu *vgpu = (struct intel_vgpu *)priv; + struct acrngt_hvm_dev *info = (struct acrngt_hvm_dev *)vgpu->handle; + struct vhm_request *req; + + int vcpu, ret; + int nr_vcpus = info->nr_vcpu; + + gvt_dbg_core("start kthread for VM%d\n", info->vm_id); + ASSERT(info->nr_vcpu <= MAX_HVM_VCPUS_SUPPORTED); + + set_freezable(); + while (1) { + acrn_ioreq_attach_client(info->client, 1); + + if (kthread_should_stop()) + return 0; + + for (vcpu = 0; vcpu < nr_vcpus; vcpu++) { + req = &info->req_buf[vcpu]; + if (atomic_read(&req->processed) == + REQ_STATE_PROCESSING && + req->client == info->client) { + gvt_dbg_core("handle ioreq type %d\n", + req->type); + switch (req->type) { + case REQ_PCICFG: + ret = acrngt_hvm_pio_emulation(vgpu, req); + break; + case REQ_MMIO: + case REQ_WP: + ret = acrngt_hvm_mmio_emulation(vgpu, req); + break; + default: + gvt_err("Unknown ioreq type %x\n", + req->type); + ret = -EINVAL; + break; + } + /* error handling */ + if (ret) + handle_request_error(vgpu); + + smp_mb(); + atomic_set(&req->processed, REQ_STATE_COMPLETE); + /* complete request */ + if (acrn_ioreq_complete_request(info->client, + vcpu)) + gvt_err("failed complete request\n"); + } + } + } + + BUG(); /* It's actually impossible to reach here */ + return 0; +} + +struct intel_vgpu *acrngt_instance_create(domid_t vm_id, + struct intel_vgpu_type *vgpu_type) +{ + struct acrngt_hvm_dev *info; + struct intel_vgpu *vgpu; + int ret = 0; + struct task_struct *thread; + struct vm_info vm_info; + + gvt_dbg_core("acrngt_instance_create enter\n"); + if (!intel_gvt_ops || !acrngt_priv.gvt) + return NULL; + + vgpu = intel_gvt_ops->vgpu_create(acrngt_priv.gvt, vgpu_type); + if (IS_ERR(vgpu)) { + gvt_err("failed to create vgpu\n"); + return NULL; + } + + info = kzalloc(sizeof(struct acrngt_hvm_dev), GFP_KERNEL); + if (info == NULL) { + gvt_err("failed to alloc acrngt_hvm_dev\n"); + goto err; + } + + info->vm_id = vm_id; + info->vgpu = vgpu; + vgpu->handle = (unsigned long)info; + + if ((info->vm = find_get_vm(vm_id)) == NULL) { + gvt_err("failed to get vm %d\n", vm_id); + acrngt_instance_destroy(vgpu); + return NULL; + } + if (info->vm->req_buf == NULL) { + gvt_err("failed to get req buf for vm %d\n", vm_id); + goto err; + } + gvt_dbg_core("get vm req_buf from vm_id %d\n", vm_id); + + /* create client: no handler -> handle request by itself */ + info->client = acrn_ioreq_create_client(vm_id, NULL, "ioreq gvt-g"); + if (info->client < 0) { + gvt_err("failed to create ioreq client for vm id %d\n", vm_id); + goto err; + } + + /* get vm info */ + ret = vhm_get_vm_info(vm_id, &vm_info); + if (ret < 0) { + gvt_err("failed to get vm info for vm id %d\n", vm_id); + goto err; + } + + info->nr_vcpu = vm_info.max_vcpu; + + /* get req buf */ + info->req_buf = acrn_ioreq_get_reqbuf(info->client); + if (info->req_buf == NULL) { + gvt_err("failed to get req_buf for client %d\n", info->client); + goto err; + } + + /* trap config space access */ + acrn_ioreq_intercept_bdf(info->client, 0, 2, 0); + + thread = kthread_run(acrngt_emulation_thread, vgpu, + "acrngt_emulation:%d", vm_id); + if (IS_ERR(thread)) { + gvt_err("failed to run emulation thread for vm %d\n", vm_id); + goto err; + } + info->emulation_thread = thread; + gvt_dbg_core("create vgpu instance success, vm_id %d, client %d," + " nr_vcpu %d\n", info->vm_id,info->client, info->nr_vcpu); + + intel_gvt_ops->vgpu_activate(vgpu); + + return vgpu; + +err: + acrngt_instance_destroy(vgpu); + return NULL; +} + +static ssize_t kobj_attr_show(struct kobject *kobj, + struct attribute *attr, char *buf) +{ + struct kobj_attribute *kattr; + ssize_t ret = -EIO; + + kattr = container_of(attr, struct kobj_attribute, attr); + if (kattr->show) + ret = kattr->show(kobj, kattr, buf); + return ret; +} + +static ssize_t kobj_attr_store(struct kobject *kobj, + struct attribute *attr, const char *buf, size_t count) +{ + struct kobj_attribute *kattr; + ssize_t ret = -EIO; + + kattr = container_of(attr, struct kobj_attribute, attr); + if (kattr->store) + ret = kattr->store(kobj, kattr, buf, count); + return ret; +} + +const struct sysfs_ops acrngt_kobj_sysfs_ops = { + .show = kobj_attr_show, + .store = kobj_attr_store, +}; + +static ssize_t acrngt_sysfs_vgpu_id(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + int i; + + for (i = 0; i < GVT_MAX_VGPU_INSTANCE; i++) { + if (acrngt_priv.vgpus[i] && + (kobj == &((struct acrngt_hvm_dev *) + (acrngt_priv.vgpus[i]->handle))->kobj)) { + return sprintf(buf, "%d\n", acrngt_priv.vgpus[i]->id); + } + } + return 0; +} + +static struct kobj_attribute acrngt_vm_attr = +__ATTR(vgpu_id, 0440, acrngt_sysfs_vgpu_id, NULL); + + +static struct attribute *acrngt_vm_attrs[] = { + &acrngt_vm_attr.attr, + NULL, /* need to NULL terminate the list of attributes */ +}; + +static struct kobj_type acrngt_instance_ktype = { + .sysfs_ops = &acrngt_kobj_sysfs_ops, + .default_attrs = acrngt_vm_attrs, +}; + +static int acrngt_sysfs_add_instance(struct acrngt_hvm_params *vp) +{ + int ret = 0; + struct intel_vgpu *vgpu; + struct acrngt_hvm_dev *info; + + struct intel_vgpu_type type = acrngt_priv.gvt->types[0]; + type.low_gm_size = vp->aperture_sz * VMEM_1MB; + type.high_gm_size = (vp->gm_sz - vp->aperture_sz) * VMEM_1MB; + type.fence = vp->fence_sz; + mutex_lock(&acrn_gvt_sysfs_lock); + vgpu = acrngt_instance_create(vp->vm_id, &type); + mutex_unlock(&acrn_gvt_sysfs_lock); + if (vgpu == NULL) { + gvt_err("acrngt_sysfs_add_instance failed.\n"); + ret = -EINVAL; + } else { + info = (struct acrngt_hvm_dev *) vgpu->handle; + info->vm_id = vp->vm_id; + acrngt_priv.vgpus[vgpu->id - 1] = vgpu; + gvt_dbg_core("add acrngt instance for vm-%d with vgpu-%d.\n", + vp->vm_id, vgpu->id); + + kobject_init(&info->kobj, &acrngt_instance_ktype); + info->kobj.kset = acrn_gvt_kset; + /* add kobject, NULL parent indicates using kset as parent */ + ret = kobject_add(&info->kobj, NULL, "vm%u", info->vm_id); + if (ret) { + gvt_err("%s: kobject add error: %d\n", __func__, ret); + kobject_put(&info->kobj); + } + } + + return ret; +} + +static struct intel_vgpu *vgpu_from_id(int vm_id) +{ + int i; + struct acrngt_hvm_dev *hvm_dev = NULL; + + /* vm_id is negtive in del_instance call */ + if (vm_id < 0) + vm_id = -vm_id; + for (i = 0; i < GVT_MAX_VGPU_INSTANCE; i++) + if (acrngt_priv.vgpus[i]) { + hvm_dev = (struct acrngt_hvm_dev *) + acrngt_priv.vgpus[i]->handle; + if (hvm_dev && (vm_id == hvm_dev->vm_id)) + return acrngt_priv.vgpus[i]; + } + return NULL; +} + +static int acrngt_sysfs_del_instance(struct acrngt_hvm_params *vp) +{ + int ret = 0; + struct intel_vgpu *vgpu = vgpu_from_id(vp->vm_id); + struct acrngt_hvm_dev *info = NULL; + + if (vgpu) { + info = (struct acrngt_hvm_dev *) vgpu->handle; + gvt_dbg_core("remove vm-%d sysfs node.\n", vp->vm_id); + kobject_put(&info->kobj); + + mutex_lock(&acrn_gvt_sysfs_lock); + acrngt_priv.vgpus[vgpu->id - 1] = NULL; + acrngt_instance_destroy(vgpu); + mutex_unlock(&acrn_gvt_sysfs_lock); + } + + return ret; +} + +static ssize_t acrngt_sysfs_instance_manage(struct kobject *kobj, + struct kobj_attribute *attr, const char *buf, size_t count) +{ + struct acrngt_hvm_params vp; + int param_cnt; + char param_str[64]; + int rc; + int high_gm_sz; + int low_gm_sz; + + /* We expect the param_str should be vmid,a,b,c (where the guest + * wants a MB aperture and b MB gm, and c fence registers) or -vmid + * (where we want to release the gvt instance). + */ + (void)sscanf(buf, "%63s", param_str); + param_cnt = sscanf(param_str, "%d,%d,%d,%d", &vp.vm_id, + &low_gm_sz, &high_gm_sz, &vp.fence_sz); + gvt_dbg_core("create vm-%d sysfs node, low gm size %d," + " high gm size %d, fence size %d\n", + vp.vm_id, low_gm_sz, high_gm_sz, vp.fence_sz); + vp.aperture_sz = low_gm_sz; + vp.gm_sz = high_gm_sz + low_gm_sz; + if (param_cnt == 1) { + if (vp.vm_id >= 0) + return -EINVAL; + } else if (param_cnt == 4) { + if (!(vp.vm_id > 0 && vp.aperture_sz > 0 && + vp.aperture_sz <= vp.gm_sz && vp.fence_sz > 0)) + return -EINVAL; + } else { + gvt_err("%s: parameter counter incorrect\n", __func__); + return -EINVAL; + } + + rc = (vp.vm_id > 0) ? acrngt_sysfs_add_instance(&vp) : + acrngt_sysfs_del_instance(&vp); + + return rc < 0 ? rc : count; +} + +static ssize_t show_plane_owner(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + return sprintf(buf, "Planes:\nPipe A: %d %d %d %d\n" + "Pipe B: %d %d %d %d\nPipe C: %d %d %d\n", + acrngt_priv.gvt->pipe_info[PIPE_A].plane_owner[PLANE_PRIMARY], + acrngt_priv.gvt->pipe_info[PIPE_A].plane_owner[PLANE_SPRITE0], + acrngt_priv.gvt->pipe_info[PIPE_A].plane_owner[PLANE_SPRITE1], + acrngt_priv.gvt->pipe_info[PIPE_A].plane_owner[PLANE_SPRITE2], + acrngt_priv.gvt->pipe_info[PIPE_B].plane_owner[PLANE_PRIMARY], + acrngt_priv.gvt->pipe_info[PIPE_B].plane_owner[PLANE_SPRITE0], + acrngt_priv.gvt->pipe_info[PIPE_B].plane_owner[PLANE_SPRITE1], + acrngt_priv.gvt->pipe_info[PIPE_B].plane_owner[PLANE_SPRITE2], + acrngt_priv.gvt->pipe_info[PIPE_C].plane_owner[PLANE_PRIMARY], + acrngt_priv.gvt->pipe_info[PIPE_C].plane_owner[PLANE_SPRITE0], + acrngt_priv.gvt->pipe_info[PIPE_C].plane_owner[PLANE_SPRITE1]); +} + +static struct kobj_attribute acrngt_instance_attr = +__ATTR(create_gvt_instance, 0220, NULL, acrngt_sysfs_instance_manage); + +static struct kobj_attribute plane_owner_attr = +__ATTR(plane_owner_show, 0440, show_plane_owner, NULL); + +static struct attribute *acrngt_ctrl_attrs[] = { + &acrngt_instance_attr.attr, + &plane_owner_attr.attr, + NULL, /* need to NULL terminate the list of attributes */ +}; + +static struct kobj_type acrngt_ctrl_ktype = { + .sysfs_ops = &acrngt_kobj_sysfs_ops, + .default_attrs = acrngt_ctrl_attrs, +}; + +int acrngt_sysfs_init(struct intel_gvt *gvt) +{ + int ret; + + acrn_gvt_kset = kset_create_and_add("gvt", NULL, kernel_kobj); + if (!acrn_gvt_kset) { + ret = -ENOMEM; + goto kset_fail; + } + + acrn_gvt_ctrl_kobj = kzalloc(sizeof(struct kobject), GFP_KERNEL); + if (!acrn_gvt_ctrl_kobj) { + ret = -ENOMEM; + goto ctrl_fail; + } + + acrn_gvt_ctrl_kobj->kset = acrn_gvt_kset; + ret = kobject_init_and_add(acrn_gvt_ctrl_kobj, &acrngt_ctrl_ktype, + NULL, "control"); + if (ret) { + ret = -EINVAL; + goto kobj_fail; + } + + return 0; + +kobj_fail: + kobject_put(acrn_gvt_ctrl_kobj); +ctrl_fail: + kset_unregister(acrn_gvt_kset); +kset_fail: + return ret; +} + +void acrngt_sysfs_del(void) +{ + kobject_put(acrn_gvt_ctrl_kobj); + kset_unregister(acrn_gvt_kset); +} + +static int acrngt_host_init(struct device *dev, void *gvt, const void *ops) +{ + int ret = -EFAULT; + + if (!gvt || !ops) + return -EINVAL; + + acrngt_priv.gvt = (struct intel_gvt *)gvt; + intel_gvt_ops = (const struct intel_gvt_ops *)ops; + + ret = acrngt_sysfs_init(acrngt_priv.gvt); + if (ret) { + gvt_err("failed call acrngt_sysfs_init, error: %d\n", ret); + acrngt_priv.gvt = NULL; + intel_gvt_ops = NULL; + } + + return ret; +} + +static void acrngt_host_exit(struct device *dev, void *gvt) +{ + acrngt_sysfs_del(); + acrngt_priv.gvt = NULL; + intel_gvt_ops = NULL; +} + +static int acrngt_attach_vgpu(void *vgpu, unsigned long *handle) +{ + return 0; +} + +static void acrngt_detach_vgpu(unsigned long handle) +{ + return; +} + +static int acrngt_inject_msi(unsigned long handle, u32 addr_lo, u16 data) +{ + int ret; + struct acrngt_hvm_dev *info = (struct acrngt_hvm_dev *)handle; + gvt_dbg_core("inject msi irq, addr 0x%x, data 0x%hx\n", addr_lo, data); + + ret = vhm_inject_msi(info->vm_id, addr_lo, data); + if (ret) + gvt_err("failed to inject msi for vm %d\n", info->vm_id); + return ret; +} + +static unsigned long acrngt_virt_to_mfn(void *addr) +{ + uint64_t gpa; + uint64_t hpa; + gvt_dbg_core("virt 0x%lx to mfn\n", (unsigned long)addr); + + gpa = virt_to_phys(addr); + hpa = vhm_vm_gpa2hpa(0, gpa); + + return (unsigned long) (hpa >> PAGE_SHIFT); +} + +static int acrngt_page_track_add(unsigned long handle, u64 gfn) +{ + int ret; + unsigned long hpa; + struct acrngt_hvm_dev *info = (struct acrngt_hvm_dev *)handle; + gvt_dbg_core("set wp page for gfn 0x%llx\n", gfn); + + hpa = vhm_vm_gpa2hpa(info->vm_id, gfn << PAGE_SHIFT); + ret = acrn_ioreq_add_iorange(info->client, REQ_WP, gfn << PAGE_SHIFT, + ((gfn + 1) << PAGE_SHIFT) - 1); + if (ret) { + gvt_err("failed acrn_ioreq_add_iorange for gfn 0x%llx\n", gfn); + return ret; + } + ret = write_protect_page(info->vm_id, gfn << PAGE_SHIFT, true); + if (ret) + gvt_err("failed set write protect for gfn 0x%llx\n", gfn); + return ret; +} + +static int acrngt_page_track_remove(unsigned long handle, u64 gfn) +{ + int ret; + unsigned long hpa; + struct acrngt_hvm_dev *info = (struct acrngt_hvm_dev *)handle; + gvt_dbg_core("unset wp page for gfx 0x%llx\n", gfn); + + hpa = vhm_vm_gpa2hpa(info->vm_id, gfn << PAGE_SHIFT); + ret = write_protect_page(info->vm_id, gfn << PAGE_SHIFT, false); + if (ret) { + gvt_err("failed update_memmap_attr unset for gfn 0x%llx\n", gfn); + return ret; + } + ret = acrn_ioreq_del_iorange(info->client, REQ_WP, gfn << PAGE_SHIFT, + ((gfn + 1) << PAGE_SHIFT) - 1); + if (ret) + gvt_err("failed acrn_ioreq_del_iorange for gfn 0x%llx\n", gfn); + return ret; +} + +static int acrngt_read_gpa(unsigned long handle, unsigned long gpa, + void *buf, unsigned long len) +{ + void *va = NULL; + struct acrngt_hvm_dev *info = (struct acrngt_hvm_dev *)handle; + gvt_dbg_core("read gpa 0x%lx with len 0x%lx\n", gpa, len); + + va = map_guest_phys(info->vm_id, gpa, len); + if (!va) { + gvt_err("GVT: can not read gpa = 0x%lx!!!\n", gpa); + return -EFAULT; + } + + switch (len) + { + case 1: + *((uint8_t *) buf) = *((uint8_t *) va); + break; + case 2: + *((uint16_t *) buf) = *((uint16_t *) va); + break; + case 4: + *((uint32_t *) buf) = *((uint32_t *) va); + break; + case 8: + *((uint64_t *) buf) = *((uint64_t *) va); + break; + default: + memcpy(buf, va, len); + } + return 0; +} + +static int acrngt_write_gpa(unsigned long handle, unsigned long gpa, + void *buf, unsigned long len) +{ + void *va = NULL; + struct acrngt_hvm_dev *info = (struct acrngt_hvm_dev *)handle; + gvt_dbg_core("write gpa 0x%lx with len 0x%lx\n", gpa, len); + + va = map_guest_phys(info->vm_id, gpa, len); + if (!va) { + gvt_err("GVT: can not write gpa = 0x%lx!!!\n", gpa); + return -EFAULT; + } + + switch (len) + { + case 1: + *((uint8_t *) va) = *((uint8_t *) buf); + break; + case 2: + *((uint16_t *) va) = *((uint16_t *) buf); + break; + case 4: + *((uint32_t *) va) = *((uint32_t *) buf); + break; + case 8: + *((uint64_t *) va) = *((uint64_t *) buf); + break; + default: + memcpy(va, buf, len); + } + return 0; +} + +static unsigned long acrngt_gfn_to_pfn(unsigned long handle, unsigned long gfn) +{ + unsigned long hpa; + struct acrngt_hvm_dev *info = (struct acrngt_hvm_dev *)handle; + gvt_dbg_core("convert gfn 0x%lx to pfn\n", gfn); + + hpa = vhm_vm_gpa2hpa(info->vm_id, gfn << PAGE_SHIFT); + return hpa >> PAGE_SHIFT; +} + +static int acrngt_map_gfn_to_mfn(unsigned long handle, unsigned long gfn, + unsigned long mfn, unsigned int nr, bool map) +{ + int ret; + struct acrngt_hvm_dev *info = (struct acrngt_hvm_dev *)handle; + gvt_dbg_core("map/unmap gfn 0x%lx to mfn 0x%lx with %u pages, map %d\n", + gfn, mfn, nr, map); + + if (map) + ret = add_memory_region(info->vm_id, gfn << PAGE_SHIFT, + mfn << PAGE_SHIFT, nr << PAGE_SHIFT, + MEM_TYPE_UC, MEM_ACCESS_RWX); + else + ret = del_memory_region(info->vm_id, gfn << PAGE_SHIFT, + nr << PAGE_SHIFT); + if (ret) + gvt_err("failed map/unmap gfn 0x%lx to mfn 0x%lx with %u pages," + " map %d\n", gfn, mfn, nr, map); + return ret; +} + +static int acrngt_set_trap_area(unsigned long handle, u64 start, + u64 end, bool map) +{ + int ret; + struct acrngt_hvm_dev *info = (struct acrngt_hvm_dev *)handle; + gvt_dbg_core("set trap area, start 0x%llx, end 0x%llx, map %d\n", + start, end, map); + + if (map) + ret = acrn_ioreq_add_iorange(info->client, REQ_MMIO, + start, end); + else + ret = acrn_ioreq_del_iorange(info->client, REQ_MMIO, + start, end); + if (ret) + gvt_err("failed set trap, start 0x%llx, end 0x%llx, map %d\n", + start, end, map); + return ret; +} + +static int acrngt_dom0_ready(void) +{ + char *env[] = {"GVT_DOM0_READY=1", NULL}; + if(!acrn_gvt_ctrl_kobj) + return 0; + gvt_dbg_core("acrngt: Dom 0 ready to accept Dom U guests\n"); + return kobject_uevent_env(acrn_gvt_ctrl_kobj, KOBJ_ADD, env); +} + +static int acrngt_dma_map_guest_page(unsigned long handle, unsigned long gfn, + unsigned long size, dma_addr_t *dma_addr) +{ + unsigned long pfn; + + pfn = acrngt_gfn_to_pfn(handle, gfn); + *dma_addr = pfn << PAGE_SHIFT; + + return 0; +} + +static void acrngt_dma_unmap_guest_page(unsigned long handle, + dma_addr_t dma_addr) +{ +} + +struct intel_gvt_mpt acrn_gvt_mpt = { + //.detect_host = acrngt_detect_host, + .host_init = acrngt_host_init, + .host_exit = acrngt_host_exit, + .attach_vgpu = acrngt_attach_vgpu, + .detach_vgpu = acrngt_detach_vgpu, + .inject_msi = acrngt_inject_msi, + .from_virt_to_mfn = acrngt_virt_to_mfn, + .enable_page_track = acrngt_page_track_add, + .disable_page_track = acrngt_page_track_remove, + .read_gpa = acrngt_read_gpa, + .write_gpa = acrngt_write_gpa, + .gfn_to_mfn = acrngt_gfn_to_pfn, + .map_gfn_to_mfn = acrngt_map_gfn_to_mfn, + .dma_map_guest_page = acrngt_dma_map_guest_page, + .dma_unmap_guest_page = acrngt_dma_unmap_guest_page, + .set_trap_area = acrngt_set_trap_area, + .dom0_ready = acrngt_dom0_ready, +}; +EXPORT_SYMBOL_GPL(acrn_gvt_mpt); + +static int __init acrngt_init(void) +{ + /* todo: to support need implment check_gfx_iommu_enabled func */ + gvt_dbg_core("acrngt loaded\n"); + return 0; +} + +static void __exit acrngt_exit(void) +{ + gvt_dbg_core("acrngt: unloaded\n"); +} + +module_init(acrngt_init); +module_exit(acrngt_exit); diff --git a/drivers/gpu/drm/i915/gvt/acrngt.h b/drivers/gpu/drm/i915/gvt/acrngt.h new file mode 100644 index 000000000000..0799df2ec557 --- /dev/null +++ b/drivers/gpu/drm/i915/gvt/acrngt.h @@ -0,0 +1,81 @@ +/* + * Copyright(c) 2011-2016 Intel Corporation. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + */ + +#ifndef INTEL_GVT_ACRNGT_H +#define INTEL_GVT_ACRNGT_H + +extern struct intel_gvt *gvt_instance; +extern const struct intel_gvt_ops *acrn_intel_gvt_ops; + +#define MAX_HVM_VCPUS_SUPPORTED 127 + +#define VMEM_1MB (1ULL << 20) /* the size of the first 1MB */ + +typedef uint16_t domid_t; + +/* + * acrngt_hvm_dev is a wrapper of a vGPU instance which is reprensented by the + * intel_vgpu structure. Under acrn hypervisor, the acrngt_instance stands for a + * HVM device, which the related resource. + */ +struct acrngt_hvm_dev { + domid_t vm_id; + struct kobject kobj; + struct intel_vgpu *vgpu; + + int nr_vcpu; + struct task_struct *emulation_thread; + + int client; + struct vhm_request *req_buf; + struct vhm_vm *vm; +}; + +struct acrngt_hvm_params { + int vm_id; + int aperture_sz; /* in MB */ + int gm_sz; /* in MB */ + int fence_sz; +}; + +/* + * struct gvt_acrngt should be a single instance to share global + * information for ACRNGT module. + */ +#define GVT_MAX_VGPU_INSTANCE 15 +struct gvt_acrngt { + struct intel_gvt *gvt; + struct intel_vgpu *vgpus[GVT_MAX_VGPU_INSTANCE]; +}; + +static ssize_t acrngt_sysfs_instance_manage(struct kobject *kobj, + struct kobj_attribute *attr, const char *buf, size_t count); +static ssize_t acrngt_sysfs_vgpu_id(struct kobject *kobj, + struct kobj_attribute *attr, char *buf); + +struct intel_vgpu *acrngt_instance_create(domid_t vm_id, + struct intel_vgpu_type *type); +void acrngt_instance_destroy(struct intel_vgpu *vgpu); + +#endif diff --git a/drivers/gpu/drm/i915/gvt/gvt.c b/drivers/gpu/drm/i915/gvt/gvt.c index 25e0a58a24a5..6261af450ee4 100644 --- a/drivers/gpu/drm/i915/gvt/gvt.c +++ b/drivers/gpu/drm/i915/gvt/gvt.c @@ -44,6 +44,7 @@ struct intel_gvt_host intel_gvt_host; static const char * const supported_hypervisors[] = { [INTEL_GVT_HYPERVISOR_XEN] = "XEN", [INTEL_GVT_HYPERVISOR_KVM] = "KVM", + [INTEL_GVT_HYPERVISOR_ACRN] = "ACRN", }; static struct intel_vgpu_type *intel_gvt_find_vgpu_type(struct intel_gvt *gvt, @@ -221,6 +222,11 @@ int intel_gvt_init_host(void) symbol_get(kvmgt_mpt), "kvmgt"); intel_gvt_host.hypervisor_type = INTEL_GVT_HYPERVISOR_KVM; #endif + /* not in Xen. Try ACRN */ + intel_gvt_host.mpt = try_then_request_module( + symbol_get(acrn_gvt_mpt), "acrn_gvt"); + intel_gvt_host.hypervisor_type = INTEL_GVT_HYPERVISOR_ACRN; + printk("acrngt %s\n", intel_gvt_host.mpt?"found":"not found"); } /* Fail to load MPT modules - bail out */ diff --git a/drivers/gpu/drm/i915/gvt/gvt.h b/drivers/gpu/drm/i915/gvt/gvt.h index 9c291924c1d0..d673c46e3179 100644 --- a/drivers/gpu/drm/i915/gvt/gvt.h +++ b/drivers/gpu/drm/i915/gvt/gvt.h @@ -55,6 +55,7 @@ enum { INTEL_GVT_HYPERVISOR_XEN = 0, INTEL_GVT_HYPERVISOR_KVM, + INTEL_GVT_HYPERVISOR_ACRN, }; struct intel_gvt_host { diff --git a/drivers/gpu/drm/i915/gvt/hypercall.h b/drivers/gpu/drm/i915/gvt/hypercall.h index f14cff32ae2f..d4b7929c8bee 100644 --- a/drivers/gpu/drm/i915/gvt/hypercall.h +++ b/drivers/gpu/drm/i915/gvt/hypercall.h @@ -69,5 +69,6 @@ struct intel_gvt_mpt { extern struct intel_gvt_mpt xengt_mpt; extern struct intel_gvt_mpt kvmgt_mpt; +extern struct intel_gvt_mpt acrn_gvt_mpt; #endif /* _GVT_HYPERCALL_H_ */ From 4edbf7e97f1f4ac07bb1437e10e1e13372df0b4a Mon Sep 17 00:00:00 2001 From: Fei Jiang Date: Fri, 29 Dec 2017 19:14:16 +0800 Subject: [PATCH 0903/1103] drm/i915/gvt: hard code Pipe B plane owner to UOS It is a work around patch due to plane restriction patches are not porting Change-Id: If09ff8c40254ec275dc2d9b9674d7267d306a7e7 Signed-off-by: Fei Jiang Reviewed-on: Reviewed-by: Dong, Eddie Tested-by: Dong, Eddie --- drivers/gpu/drm/i915/gvt/acrngt.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/gpu/drm/i915/gvt/acrngt.c b/drivers/gpu/drm/i915/gvt/acrngt.c index fec5751a4bed..346a676d77bc 100644 --- a/drivers/gpu/drm/i915/gvt/acrngt.c +++ b/drivers/gpu/drm/i915/gvt/acrngt.c @@ -431,6 +431,13 @@ static int acrngt_sysfs_add_instance(struct acrngt_hvm_params *vp) struct acrngt_hvm_dev *info; struct intel_vgpu_type type = acrngt_priv.gvt->types[0]; + + /* todo: wa patch due to plane restriction patches are not porting */ + acrngt_priv.gvt->pipe_info[1].plane_owner[0] = 1; + acrngt_priv.gvt->pipe_info[1].plane_owner[1] = 1; + acrngt_priv.gvt->pipe_info[1].plane_owner[2] = 1; + acrngt_priv.gvt->pipe_info[1].plane_owner[3] = 1; + type.low_gm_size = vp->aperture_sz * VMEM_1MB; type.high_gm_size = (vp->gm_sz - vp->aperture_sz) * VMEM_1MB; type.fence = vp->fence_sz; From 3b377475baf9943527cb01931229f2c5e3f38457 Mon Sep 17 00:00:00 2001 From: Min He Date: Fri, 14 Sep 2018 16:10:18 +0800 Subject: [PATCH 0904/1103] drm/i915/gvt: remove some initialization of ggtt in GVTg guest This patch removed the initialization for the ggtt holes in GVT-g guest. So that: 1. can improve the guest boot up time; 2. avoid boot failure issue when i915.enable_guc_loading != 0. Signed-off-by: Min He Reviewed-by: Vivek Kasireddy Reviewed-by: Singh, Satyeshwar (cherry picked from commit d1ca0614ef13513202362994d5506ae9a33b3483) Change-Id: I2d854d5fea65dfbd92c12b9426fa882075d766e1 Reviewed-on: Reviewed-by: He, Min Reviewed-by: Jiang, Fei Reviewed-by: Dong, Eddie Tested-by: Dong, Eddie --- drivers/gpu/drm/i915/i915_gem_gtt.c | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_gem_gtt.c b/drivers/gpu/drm/i915/i915_gem_gtt.c index f00c7fbef79e..6e792e3167a4 100644 --- a/drivers/gpu/drm/i915/i915_gem_gtt.c +++ b/drivers/gpu/drm/i915/i915_gem_gtt.c @@ -2949,16 +2949,19 @@ int i915_gem_init_ggtt(struct drm_i915_private *dev_priv) if (ret) return ret; - /* Clear any non-preallocated blocks */ - drm_mm_for_each_hole(entry, &ggtt->vm.mm, hole_start, hole_end) { - DRM_DEBUG_KMS("clearing unused GTT space: [%lx, %lx]\n", + if (!intel_vgpu_active(dev_priv)) { + /* Clear any non-preallocated blocks */ + drm_mm_for_each_hole(entry, &ggtt->vm.mm, hole_start, hole_end) { + DRM_DEBUG_KMS("clearing unused GTT space: [%lx, %lx]\n", hole_start, hole_end); - ggtt->vm.clear_range(&ggtt->vm, hole_start, + ggtt->vm.clear_range(&ggtt->vm, hole_start, hole_end - hole_start); - } + } - /* And finally clear the reserved guard page */ - ggtt->vm.clear_range(&ggtt->vm, ggtt->vm.total - PAGE_SIZE, PAGE_SIZE); + /* And finally clear the reserved guard page */ + ggtt->vm.clear_range(&ggtt->vm, ggtt->vm.total - PAGE_SIZE, PAGE_SIZE); + + } if (USES_PPGTT(dev_priv) && !USES_FULL_PPGTT(dev_priv)) { ret = i915_gem_init_aliasing_ppgtt(dev_priv); From c581f93b5bb7a2ce1834a97b0bd6310538713f57 Mon Sep 17 00:00:00 2001 From: Min He Date: Fri, 14 Sep 2018 16:10:18 +0800 Subject: [PATCH 0905/1103] drm/i915/gvt: avoid unncessary reset in GVT-g guest When i915 boots up, it will trigger a reset, but it's unnecessary in GVT-g environment, so remove this reset which can reduce guest boot time. Change-Id: Id8c120c3229118af3c41fb1ef4ddbfbf71cb69fe Signed-off-by: Min He Reviewed-on: Reviewed-by: Jiang, Fei Reviewed-by: Dong, Eddie Tested-by: Dong, Eddie --- drivers/gpu/drm/i915/i915_gem.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index fcc73a6ab503..c3e87d10f14e 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -5027,7 +5027,8 @@ void i915_gem_sanitize(struct drm_i915_private *i915) * of the reset, so this could be applied to even earlier gen. */ err = -ENODEV; - if (INTEL_GEN(i915) >= 5 && intel_has_gpu_reset(i915)) + if (INTEL_GEN(i915) >= 5 && intel_has_gpu_reset(i915) && + !intel_vgpu_active(i915)) err = WARN_ON(intel_gpu_reset(i915, ALL_ENGINES)); if (!err) intel_engines_sanitize(i915); From 7b4cf2970090f8893201e963bde6296f13767264 Mon Sep 17 00:00:00 2001 From: Fei Jiang Date: Fri, 29 Dec 2017 18:48:22 +0800 Subject: [PATCH 0906/1103] drm/i915/gvt: add param disable_gvt_fw_loading to disable gvt fw loading when gvt fw doesn't exist, try to load fw from initrd will cost about 1 minute, add disable_gvt_fw_loading to speed up sos boot up time. Change-Id: I07aca795057fec09ff9ee729a65a5f96006c5ef8 Signed-off-by: Fei Jiang Reviewed-on: Reviewed-by: Dong, Eddie Tested-by: Dong, Eddie --- drivers/gpu/drm/i915/gvt/firmware.c | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/drivers/gpu/drm/i915/gvt/firmware.c b/drivers/gpu/drm/i915/gvt/firmware.c index 4ac18b447247..f0d30237c988 100644 --- a/drivers/gpu/drm/i915/gvt/firmware.c +++ b/drivers/gpu/drm/i915/gvt/firmware.c @@ -199,6 +199,7 @@ static int verify_firmware(struct intel_gvt *gvt, #define GVT_FIRMWARE_PATH "i915/gvt" +bool disable_gvt_fw_loading=true; /** * intel_gvt_load_firmware - load GVT firmware * @gvt: intel gvt device @@ -216,27 +217,27 @@ int intel_gvt_load_firmware(struct intel_gvt *gvt) void *mem; int ret; - path = kmalloc(PATH_MAX, GFP_KERNEL); - if (!path) - return -ENOMEM; - mem = kmalloc(info->cfg_space_size, GFP_KERNEL); - if (!mem) { - kfree(path); + if (!mem) return -ENOMEM; - } firmware->cfg_space = mem; mem = kmalloc(info->mmio_size, GFP_KERNEL); if (!mem) { - kfree(path); kfree(firmware->cfg_space); return -ENOMEM; } firmware->mmio = mem; + if (disable_gvt_fw_loading) + goto expose_firmware; + + path = kmalloc(PATH_MAX, GFP_KERNEL); + if (!path) + return -ENOMEM; + sprintf(path, "%s/vid_0x%04x_did_0x%04x_rid_0x%02x.golden_hw_state", GVT_FIRMWARE_PATH, pdev->vendor, pdev->device, pdev->revision); From 9e2c7269141d50559b570590696860ae512e1873 Mon Sep 17 00:00:00 2001 From: Min He Date: Fri, 14 Sep 2018 16:10:18 +0800 Subject: [PATCH 0907/1103] drm/i915/gvt: inject error interrupt to DomU when GPU hang When GVT finds a request from DomU causes GPU hang, it will trigger an error interrupt to guest, so that DomU can trigger a virtual GPU reset. Change-Id: I49d9339e99ebfdbe9b158ba311655ab356562bae Signed-off-by: Min He Signed-off-by: Satyeshwar Singh Reviewed-on: Reviewed-by: Jiang, Fei Reviewed-by: Dong, Eddie Tested-by: Dong, Eddie --- drivers/gpu/drm/i915/gvt/interrupt.c | 7 +++++++ drivers/gpu/drm/i915/gvt/interrupt.h | 2 ++ drivers/gpu/drm/i915/gvt/scheduler.c | 27 +++++++++++++++++++++++++++ drivers/gpu/drm/i915/gvt/scheduler.h | 1 + 4 files changed, 37 insertions(+) diff --git a/drivers/gpu/drm/i915/gvt/interrupt.c b/drivers/gpu/drm/i915/gvt/interrupt.c index d749d46bc05b..06ce906b6673 100644 --- a/drivers/gpu/drm/i915/gvt/interrupt.c +++ b/drivers/gpu/drm/i915/gvt/interrupt.c @@ -69,6 +69,7 @@ static const char * const irq_name[INTEL_GVT_EVENT_MAX] = { [VCS_PAGE_DIRECTORY_FAULT] = "Video page directory faults", [VCS_AS_CONTEXT_SWITCH] = "Video AS Context Switch Interrupt", [VCS2_MI_USER_INTERRUPT] = "VCS2 Video CS MI USER INTERRUPT", + [VCS2_CMD_STREAMER_ERR] = "VCS2 Video CS error interrupt", [VCS2_MI_FLUSH_DW] = "VCS2 Video MI FLUSH DW notify", [VCS2_AS_CONTEXT_SWITCH] = "VCS2 Context Switch Interrupt", @@ -524,21 +525,26 @@ static void gen8_init_irq( /* GEN8 interrupt GT0 events */ SET_BIT_INFO(irq, 0, RCS_MI_USER_INTERRUPT, INTEL_GVT_IRQ_INFO_GT0); + SET_BIT_INFO(irq, 3, RCS_CMD_STREAMER_ERR, INTEL_GVT_IRQ_INFO_GT0); SET_BIT_INFO(irq, 4, RCS_PIPE_CONTROL, INTEL_GVT_IRQ_INFO_GT0); SET_BIT_INFO(irq, 8, RCS_AS_CONTEXT_SWITCH, INTEL_GVT_IRQ_INFO_GT0); SET_BIT_INFO(irq, 16, BCS_MI_USER_INTERRUPT, INTEL_GVT_IRQ_INFO_GT0); + SET_BIT_INFO(irq, 19, BCS_CMD_STREAMER_ERR, INTEL_GVT_IRQ_INFO_GT0); SET_BIT_INFO(irq, 20, BCS_MI_FLUSH_DW, INTEL_GVT_IRQ_INFO_GT0); SET_BIT_INFO(irq, 24, BCS_AS_CONTEXT_SWITCH, INTEL_GVT_IRQ_INFO_GT0); /* GEN8 interrupt GT1 events */ SET_BIT_INFO(irq, 0, VCS_MI_USER_INTERRUPT, INTEL_GVT_IRQ_INFO_GT1); + SET_BIT_INFO(irq, 3, VCS_CMD_STREAMER_ERR, INTEL_GVT_IRQ_INFO_GT1); SET_BIT_INFO(irq, 4, VCS_MI_FLUSH_DW, INTEL_GVT_IRQ_INFO_GT1); SET_BIT_INFO(irq, 8, VCS_AS_CONTEXT_SWITCH, INTEL_GVT_IRQ_INFO_GT1); if (HAS_BSD2(gvt->dev_priv)) { SET_BIT_INFO(irq, 16, VCS2_MI_USER_INTERRUPT, INTEL_GVT_IRQ_INFO_GT1); + SET_BIT_INFO(irq, 19, VCS2_CMD_STREAMER_ERR, + INTEL_GVT_IRQ_INFO_GT1); SET_BIT_INFO(irq, 20, VCS2_MI_FLUSH_DW, INTEL_GVT_IRQ_INFO_GT1); SET_BIT_INFO(irq, 24, VCS2_AS_CONTEXT_SWITCH, @@ -547,6 +553,7 @@ static void gen8_init_irq( /* GEN8 interrupt GT3 events */ SET_BIT_INFO(irq, 0, VECS_MI_USER_INTERRUPT, INTEL_GVT_IRQ_INFO_GT3); + SET_BIT_INFO(irq, 3, VECS_CMD_STREAMER_ERR, INTEL_GVT_IRQ_INFO_GT3); SET_BIT_INFO(irq, 4, VECS_MI_FLUSH_DW, INTEL_GVT_IRQ_INFO_GT3); SET_BIT_INFO(irq, 8, VECS_AS_CONTEXT_SWITCH, INTEL_GVT_IRQ_INFO_GT3); diff --git a/drivers/gpu/drm/i915/gvt/interrupt.h b/drivers/gpu/drm/i915/gvt/interrupt.h index f7d7ade4f13c..6ec761a84557 100644 --- a/drivers/gpu/drm/i915/gvt/interrupt.h +++ b/drivers/gpu/drm/i915/gvt/interrupt.h @@ -53,6 +53,7 @@ enum intel_gvt_event_type { VCS_AS_CONTEXT_SWITCH, VCS2_MI_USER_INTERRUPT, + VCS2_CMD_STREAMER_ERR, VCS2_MI_FLUSH_DW, VCS2_AS_CONTEXT_SWITCH, @@ -64,6 +65,7 @@ enum intel_gvt_event_type { BCS_AS_CONTEXT_SWITCH, VECS_MI_USER_INTERRUPT, + VECS_CMD_STREAMER_ERR, VECS_MI_FLUSH_DW, VECS_AS_CONTEXT_SWITCH, diff --git a/drivers/gpu/drm/i915/gvt/scheduler.c b/drivers/gpu/drm/i915/gvt/scheduler.c index 7243324cb93c..13f83135f5b1 100644 --- a/drivers/gpu/drm/i915/gvt/scheduler.c +++ b/drivers/gpu/drm/i915/gvt/scheduler.c @@ -682,6 +682,7 @@ static int dispatch_workload(struct intel_vgpu_workload *workload) ret = prepare_workload(workload); + workload->guilty_count = atomic_read(&workload->req->gem_context->guilty_count); out: if (ret) workload->status = ret; @@ -898,6 +899,9 @@ static void complete_current_workload(struct intel_gvt *gvt, int ring_id) list_del_init(&workload->list); + if (workload->status == -EIO) + intel_vgpu_reset_submission(vgpu, 1 << ring_id); + if (!workload->status) { release_shadow_batch_buffer(workload); if(gvt_shadow_wa_ctx) @@ -933,6 +937,18 @@ static void complete_current_workload(struct intel_gvt *gvt, int ring_id) mutex_unlock(&vgpu->vgpu_lock); } +static void inject_error_cs_irq(struct intel_vgpu *vgpu, int ring_id) +{ + enum intel_gvt_event_type events[] = { + RCS_CMD_STREAMER_ERR, + BCS_CMD_STREAMER_ERR, + VCS_CMD_STREAMER_ERR, + VCS2_CMD_STREAMER_ERR, + VECS_CMD_STREAMER_ERR, + }; + intel_vgpu_trigger_virtual_event(vgpu, events[ring_id]); +} + struct workload_thread_param { struct intel_gvt *gvt; int ring_id; @@ -1002,6 +1018,17 @@ static int workload_thread(void *priv) if (lret >= 0 && workload->status == -EINPROGRESS) workload->status = 0; + /* + * increased guilty_count means that this request triggerred + * a GPU reset, so we need to notify the guest about the + * hang. + */ + if (workload->guilty_count < + atomic_read(&workload->req->gem_context->guilty_count)) { + workload->status = -EIO; + inject_error_cs_irq(workload->vgpu, ring_id); + } + complete: gvt_dbg_sched("will complete workload %p, status: %d\n", workload, workload->status); diff --git a/drivers/gpu/drm/i915/gvt/scheduler.h b/drivers/gpu/drm/i915/gvt/scheduler.h index ca5529d0e48e..043c2ff07a7c 100644 --- a/drivers/gpu/drm/i915/gvt/scheduler.h +++ b/drivers/gpu/drm/i915/gvt/scheduler.h @@ -84,6 +84,7 @@ struct intel_vgpu_workload { /* if this workload has been dispatched to i915? */ bool dispatched; int status; + unsigned int guilty_count; struct intel_vgpu_mm *shadow_mm; From 7f91be6779ff4e62e4e97278e2c21e989a55cfb4 Mon Sep 17 00:00:00 2001 From: Min He Date: Fri, 14 Sep 2018 16:10:18 +0800 Subject: [PATCH 0908/1103] drm/i915/gvt: Added error interrupt handler for GVT-g guest An draft version of adding error interrupt handler in GVT-g guest, to trigger a GPU reset when receiving the CS error interrupt. v2: Updated by JH - made the worker object local to the engine rather than a global. Also, don't re-initialise it in the interrupt handler otherwise null pointer dereferences ensue when the interrupts occur too quickly. In the Xen virtualised environment, the TDR is a co-operative effort between the host and guest domains. The actual detection is done entirely by the host as only it really knows what the hardware is doing. The guest still needs to do all the recovery processing when informed about a TDR event occuring by the host. However, the guest should not attempt to do the detection itself. Change-Id: I3edac5211f9878725b14abaab7cacf9048ccd620 Signed-off-by: Min He Signed-off-by: John Harrison Signed-off-by: Satyeshwar Singh Reviewed-on: Reviewed-by: Jiang, Fei Reviewed-by: Dong, Eddie Tested-by: Dong, Eddie --- drivers/gpu/drm/i915/i915_debugfs.c | 3 +++ drivers/gpu/drm/i915/i915_irq.c | 19 +++++++++++++++++++ drivers/gpu/drm/i915/intel_hangcheck.c | 3 +++ drivers/gpu/drm/i915/intel_lrc.c | 12 ++++++++++++ drivers/gpu/drm/i915/intel_ringbuffer.h | 1 + 5 files changed, 38 insertions(+) diff --git a/drivers/gpu/drm/i915/i915_debugfs.c b/drivers/gpu/drm/i915/i915_debugfs.c index f9ce35da4123..892a5e7f0648 100644 --- a/drivers/gpu/drm/i915/i915_debugfs.c +++ b/drivers/gpu/drm/i915/i915_debugfs.c @@ -4000,6 +4000,9 @@ i915_wedged_set(void *data, u64 val) struct intel_engine_cs *engine; unsigned int tmp; + if (intel_vgpu_active(i915)) + return -EINVAL; + /* * There is no safeguard against this debugfs entry colliding * with the hangcheck calling same i915_handle_error() in diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c index 0b16775e292c..71977fb8725c 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c @@ -1518,6 +1518,12 @@ gen8_cs_irq_handler(struct intel_engine_cs *engine, u32 iir) tasklet |= USES_GUC_SUBMISSION(engine->i915); } + if ((iir & (GT_RENDER_CS_MASTER_ERROR_INTERRUPT)) && + intel_vgpu_active(engine->i915)) { + queue_work(system_highpri_wq, &engine->reset_work); + return; + } + if (tasklet) tasklet_hi_schedule(&engine->execlists.tasklet); } @@ -4159,6 +4165,19 @@ static void gen8_gt_irq_postinstall(struct drm_i915_private *dev_priv) if (HAS_L3_DPF(dev_priv)) gt_interrupts[0] |= GT_RENDER_L3_PARITY_ERROR_INTERRUPT; + if (intel_vgpu_active(dev_priv)) { + gt_interrupts[0] |= GT_RENDER_CS_MASTER_ERROR_INTERRUPT << + GEN8_RCS_IRQ_SHIFT | + GT_RENDER_CS_MASTER_ERROR_INTERRUPT << + GEN8_BCS_IRQ_SHIFT; + gt_interrupts[1] |= GT_RENDER_CS_MASTER_ERROR_INTERRUPT << + GEN8_VCS1_IRQ_SHIFT | + GT_RENDER_CS_MASTER_ERROR_INTERRUPT << + GEN8_VCS2_IRQ_SHIFT; + gt_interrupts[3] |= GT_RENDER_CS_MASTER_ERROR_INTERRUPT << + GEN8_VECS_IRQ_SHIFT; + } + dev_priv->pm_ier = 0x0; dev_priv->pm_imr = ~dev_priv->pm_ier; GEN8_IRQ_INIT_NDX(GT, 0, ~gt_interrupts[0], gt_interrupts[0]); diff --git a/drivers/gpu/drm/i915/intel_hangcheck.c b/drivers/gpu/drm/i915/intel_hangcheck.c index 2fc7a0dd0df9..1f7da1cfd4b2 100644 --- a/drivers/gpu/drm/i915/intel_hangcheck.c +++ b/drivers/gpu/drm/i915/intel_hangcheck.c @@ -418,6 +418,9 @@ static void i915_hangcheck_elapsed(struct work_struct *work) if (!i915_modparams.enable_hangcheck) return; + if (intel_vgpu_active(dev_priv)) + return; + if (!READ_ONCE(dev_priv->gt.awake)) return; diff --git a/drivers/gpu/drm/i915/intel_lrc.c b/drivers/gpu/drm/i915/intel_lrc.c index 174479232e94..db2cb423e88f 100644 --- a/drivers/gpu/drm/i915/intel_lrc.c +++ b/drivers/gpu/drm/i915/intel_lrc.c @@ -2369,6 +2369,16 @@ logical_ring_default_irqs(struct intel_engine_cs *engine) engine->irq_keep_mask = GT_CONTEXT_SWITCH_INTERRUPT << shift; } +static void i915_error_reset(struct work_struct *work) { + struct intel_engine_cs *engine = + container_of(work, struct intel_engine_cs, + reset_work); + i915_handle_error(engine->i915, 1 << engine->id, + I915_ERROR_CAPTURE, + "Received error interrupt from engine %d", + engine->id); +} + static void logical_ring_setup(struct intel_engine_cs *engine) { @@ -2382,6 +2392,8 @@ logical_ring_setup(struct intel_engine_cs *engine) logical_ring_default_vfuncs(engine); logical_ring_default_irqs(engine); + + INIT_WORK(&engine->reset_work, i915_error_reset); } static bool csb_force_mmio(struct drm_i915_private *i915) diff --git a/drivers/gpu/drm/i915/intel_ringbuffer.h b/drivers/gpu/drm/i915/intel_ringbuffer.h index f5ffa6d31e82..93daa92232e0 100644 --- a/drivers/gpu/drm/i915/intel_ringbuffer.h +++ b/drivers/gpu/drm/i915/intel_ringbuffer.h @@ -567,6 +567,7 @@ struct intel_engine_cs { } semaphore; struct intel_engine_execlists execlists; + struct work_struct reset_work; /* Contexts are pinned whilst they are active on the GPU. The last * context executed remains active whilst the GPU is idle - the From 18e2d5af23bf1a3d4c7df1b6da2c5eb744208119 Mon Sep 17 00:00:00 2001 From: Zhao Yakui Date: Thu, 22 Mar 2018 17:01:07 +0800 Subject: [PATCH 0909/1103] drm/i915/gvt: Add the support of HUC_STATUS2 reg emulation for Guest VGPU The HUC_STATUS2 reg is used to indicate whether the Huc FW is loaded. Only when it is loaded successfully, the user-space driver on guest can use the Huc to do the expected operation. This provides the support of HUC_STATUS2 trap for guest VGPU. Signed-off-by: Zhao Yakui --- drivers/gpu/drm/i915/gvt/handlers.c | 2 ++ drivers/gpu/drm/i915/gvt/mmio.c | 7 +++++++ 2 files changed, 9 insertions(+) diff --git a/drivers/gpu/drm/i915/gvt/handlers.c b/drivers/gpu/drm/i915/gvt/handlers.c index 495d6a298924..91fe0a44367d 100644 --- a/drivers/gpu/drm/i915/gvt/handlers.c +++ b/drivers/gpu/drm/i915/gvt/handlers.c @@ -3017,6 +3017,8 @@ static int init_skl_mmio_info(struct intel_gvt *gvt) MMIO_D(_MMIO(0x4ab8), D_KBL); MMIO_D(_MMIO(0x2248), D_KBL | D_SKL); + MMIO_D(HUC_STATUS2, D_SKL_PLUS); + return 0; } diff --git a/drivers/gpu/drm/i915/gvt/mmio.c b/drivers/gpu/drm/i915/gvt/mmio.c index 4149eae235b5..878a8a1f5ff5 100644 --- a/drivers/gpu/drm/i915/gvt/mmio.c +++ b/drivers/gpu/drm/i915/gvt/mmio.c @@ -235,6 +235,7 @@ void intel_vgpu_reset_mmio(struct intel_vgpu *vgpu, bool dmlr) struct intel_gvt *gvt = vgpu->gvt; const struct intel_gvt_device_info *info = &gvt->device_info; void *mmio = gvt->firmware.mmio; + struct drm_i915_private *dev_priv = gvt->dev_priv; if (dmlr) { memcpy(vgpu->mmio.vreg, mmio, info->mmio_size); @@ -291,6 +292,12 @@ void intel_vgpu_reset_mmio(struct intel_vgpu *vgpu, bool dmlr) vgpu_vreg(vgpu, 0xe681c) = 1 << 17; vgpu_vreg(vgpu, 0xe6c04) = 3; vgpu_vreg(vgpu, 0xe6e1c) = 0x2f << 16; + + if (HAS_HUC_UCODE(dev_priv)) { + mmio_hw_access_pre(dev_priv); + vgpu_vreg_t(vgpu, HUC_STATUS2) = I915_READ(HUC_STATUS2); + mmio_hw_access_post(dev_priv); + } } /** From 1b53cc641393c08c23c1a4d0d9d5cc324edf6a25 Mon Sep 17 00:00:00 2001 From: Mitul Chokshi Date: Fri, 14 Sep 2018 16:10:18 +0800 Subject: [PATCH 0910/1103] drm/i915/gvt: Add vgt-id in context id Context id is reported to OABUFFER along with performance statistics counter. So when performance monitoring code gets the Context id, it can extract vm-id to identify which VM submitted given context. v2: define SIZE_CONTEXT_HW_ID even when CONFIG_DRM_I915_GVT is not set, which fixes RTC defect 192523. Change-Id: I7e8ed7f741e2f41e2e9da4b7bdd463dfd3e2fe12 Signed-off-by: Mitul Chokshi Signed-off-by: Daniel van der Wath Reviewed-by: Singh, Satyeshwar Reviewed-by: Adebisi, YetundeX Verified-by: Van Der Wath, Daniel J (cherry picked from commit 3e52d8f679eb93c2c1eb7e0f69de34c7d78260d2) (cherry picked from commit 74c64f2595b70a2079c28bbda960fff7d3e99289) Reviewed-on: Reviewed-by: Jiang, Fei Reviewed-by: He, Min Reviewed-by: Dong, Eddie Tested-by: Dong, Eddie --- drivers/gpu/drm/i915/i915_drv.h | 4 ++++ drivers/gpu/drm/i915/i915_gem_context.c | 16 +++++++++++++++- 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index 4aca5344863d..bfb655ec6996 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -1837,6 +1837,10 @@ struct drm_i915_private { * This is limited in execlists to 21 bits. */ struct ida hw_ida; + + /* In case of virtualization, 3-bits of vgt-id will be added to hw_id */ +#define SIZE_CONTEXT_HW_ID_GVT (18) +#define MAX_CONTEXT_HW_ID_GVT (1<link); - ida_simple_remove(&ctx->i915->contexts.hw_ida, ctx->hw_id); + if (intel_vgpu_active(ctx->i915)) + ida_simple_remove(&ctx->i915->contexts.hw_ida, ctx->hw_id & + ~(0x7 << SIZE_CONTEXT_HW_ID_GVT)); + else + ida_simple_remove(&ctx->i915->contexts.hw_ida, ctx->hw_id); + kfree_rcu(ctx, rcu); } @@ -217,6 +223,8 @@ static int assign_hw_id(struct drm_i915_private *dev_priv, unsigned *out) */ if (USES_GUC_SUBMISSION(dev_priv)) max = MAX_GUC_CONTEXT_HW_ID; + else if (intel_vgpu_active(dev_priv) || intel_gvt_active(dev_priv)) + max = MAX_CONTEXT_HW_ID_GVT; else max = MAX_CONTEXT_HW_ID; } @@ -236,6 +244,12 @@ static int assign_hw_id(struct drm_i915_private *dev_priv, unsigned *out) return ret; } + if (intel_vgpu_active(dev_priv)) { + /* add vgpu_id to context hw_id */ + ret = ret | (I915_READ(vgtif_reg(vgt_id)) + << SIZE_CONTEXT_HW_ID_GVT); + } + *out = ret; return 0; } From beefc434283fad1e75315444c40d03b4dde53557 Mon Sep 17 00:00:00 2001 From: Min He Date: Thu, 4 Jan 2018 21:48:47 +0800 Subject: [PATCH 0911/1103] drm/i915/gvt: show pid/hw_id of current DomU process in debugfs v1: show pid and hw id of current DomU process when showing shadow context status This allows us to identify which process a domu workload has come from. v2: expose HW context id to debugfs This patch expose the HW context id to the debugfs node, so that vtune can utilize this context id to match with the one exposed by MD API. v3: When storing DomU pid and hw id in the HWS page, offset them by the vgpu id. When there were multiple DomUs running, they would all write their pid to the same address in the HWS page. When we checked i915_context_status each shadow context would show the same current pid and hw id. By offsetting the writes by the DomU's ID, we can see the details for each shadow context correctly. This fixes defect 201282. Change-Id: I106fae75af5963f043286acd604d3bab02b87c17 Signed-off-by: Min He Signed-off-by: Daniel van der Wath Signed-off-by: Fei Jiang Reviewed-by: Singh, Satyeshwar Reviewed-by: Abes, Brahim Reviewed-by: He, Min Reviewed-on: Reviewed-by: Dong, Eddie Tested-by: Dong, Eddie --- drivers/gpu/drm/i915/gvt/scheduler.c | 38 +++++++++++++++++++++++++ drivers/gpu/drm/i915/i915_debugfs.c | 26 ++++++++++++++++- drivers/gpu/drm/i915/intel_lrc.c | 8 ++++++ drivers/gpu/drm/i915/intel_ringbuffer.h | 5 ++++ 4 files changed, 76 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/i915/gvt/scheduler.c b/drivers/gpu/drm/i915/gvt/scheduler.c index 13f83135f5b1..7c3636e3a725 100644 --- a/drivers/gpu/drm/i915/gvt/scheduler.c +++ b/drivers/gpu/drm/i915/gvt/scheduler.c @@ -435,6 +435,38 @@ int intel_gvt_scan_and_shadow_workload(struct intel_vgpu_workload *workload) return ret; } +static void gen8_shadow_pid_cid(struct intel_vgpu_workload *workload) +{ + int ring_id = workload->ring_id; + struct drm_i915_private *dev_priv = workload->vgpu->gvt->dev_priv; + struct intel_engine_cs *engine = dev_priv->engine[ring_id]; + u32 *cs; + + /* Copy the PID and CID from the guest's HWS page to the host's one */ + cs = intel_ring_begin(workload->req, 16); + *cs++ = MI_LOAD_REGISTER_MEM_GEN8 | MI_SRM_LRM_GLOBAL_GTT; + *cs++ = i915_mmio_reg_offset(NOPID); + *cs++ = (workload->ctx_desc.lrca << I915_GTT_PAGE_SHIFT) + + I915_GEM_HWS_PID_ADDR; + *cs++ = 0; + *cs++ = MI_STORE_REGISTER_MEM_GEN8 | MI_SRM_LRM_GLOBAL_GTT; + *cs++ = i915_mmio_reg_offset(NOPID); + *cs++ = engine->status_page.ggtt_offset + I915_GEM_HWS_PID_ADDR + + (workload->vgpu->id << MI_STORE_DWORD_INDEX_SHIFT); + *cs++ = 0; + *cs++ = MI_LOAD_REGISTER_MEM_GEN8 | MI_SRM_LRM_GLOBAL_GTT; + *cs++ = i915_mmio_reg_offset(NOPID); + *cs++ = (workload->ctx_desc.lrca << I915_GTT_PAGE_SHIFT) + + I915_GEM_HWS_CID_ADDR; + *cs++ = 0; + *cs++ = MI_STORE_REGISTER_MEM_GEN8 | MI_SRM_LRM_GLOBAL_GTT; + *cs++ = i915_mmio_reg_offset(NOPID); + *cs++ = engine->status_page.ggtt_offset + I915_GEM_HWS_CID_ADDR + + (workload->vgpu->id << MI_STORE_DWORD_INDEX_SHIFT); + *cs++ = 0; + intel_ring_advance(workload->req, cs); +} + static void release_shadow_batch_buffer(struct intel_vgpu_workload *workload); static int prepare_shadow_batch_buffer(struct intel_vgpu_workload *workload) @@ -633,6 +665,8 @@ static int prepare_workload(struct intel_vgpu_workload *workload) goto err_unpin_mm; } + gen8_shadow_pid_cid(workload); + ret = prepare_shadow_batch_buffer(workload); if (ret) { gvt_vgpu_err("fail to prepare_shadow_batch_buffer\n"); @@ -1180,6 +1214,10 @@ int intel_vgpu_setup_submission(struct intel_vgpu *vgpu) if (IS_ERR(s->shadow_ctx)) return PTR_ERR(s->shadow_ctx); + if (!s->shadow_ctx->name) { + s->shadow_ctx->name = kasprintf(GFP_KERNEL, "Shadow Context %d", vgpu->id); + } + bitmap_zero(s->shadow_ctx_desc_updated, I915_NUM_ENGINES); s->workloads = kmem_cache_create_usercopy("gvt-g_vgpu_workload", diff --git a/drivers/gpu/drm/i915/i915_debugfs.c b/drivers/gpu/drm/i915/i915_debugfs.c index 892a5e7f0648..f575aff83c9b 100644 --- a/drivers/gpu/drm/i915/i915_debugfs.c +++ b/drivers/gpu/drm/i915/i915_debugfs.c @@ -1939,6 +1939,19 @@ static void describe_ctx_ring(struct seq_file *m, struct intel_ring *ring) ring->space, ring->head, ring->tail, ring->emit); } +static void describe_ctx_ring_shadowed(struct seq_file *m, + struct i915_gem_context *ctx, struct intel_ring *ring, + struct intel_engine_cs *engine) +{ + int pid, cid, vgt_id; + + sscanf(ctx->name, "Shadow Context %d", &vgt_id); + pid = intel_read_status_page(engine, I915_GEM_HWS_PID_INDEX + vgt_id); + cid = intel_read_status_page(engine, I915_GEM_HWS_CID_INDEX + vgt_id); + seq_printf(m, " (Current DomU Process PID: %d, CID: %d)", + pid, cid); +} + static int i915_context_status(struct seq_file *m, void *unused) { struct drm_i915_private *dev_priv = node_to_i915(m->private); @@ -1953,6 +1966,7 @@ static int i915_context_status(struct seq_file *m, void *unused) return ret; list_for_each_entry(ctx, &dev_priv->contexts.list, link) { + bool is_shadow_context = false; seq_printf(m, "HW context %u ", ctx->hw_id); if (ctx->pid) { struct task_struct *task; @@ -1963,6 +1977,9 @@ static int i915_context_status(struct seq_file *m, void *unused) task->comm, task->pid); put_task_struct(task); } + } else if (ctx->name && !strncmp(ctx->name, "Shadow Context", 14)) { + seq_puts(m, "DomU Shadow Context "); + is_shadow_context = true; } else if (IS_ERR(ctx->file_priv)) { seq_puts(m, "(deleted) "); } else { @@ -1975,12 +1992,19 @@ static int i915_context_status(struct seq_file *m, void *unused) for_each_engine(engine, dev_priv, id) { struct intel_context *ce = to_intel_context(ctx, engine); + u64 lrc_desc = ce->lrc_desc; + seq_printf(m, "ctx id 0x%x ", (uint32_t)((lrc_desc >> 12) & + 0xFFFFF)); seq_printf(m, "%s: ", engine->name); if (ce->state) describe_obj(m, ce->state->obj); - if (ce->ring) + if (ce->ring) { describe_ctx_ring(m, ce->ring); + if(is_shadow_context) + describe_ctx_ring_shadowed(m, ctx, + ce->ring, engine); + } seq_putc(m, '\n'); } diff --git a/drivers/gpu/drm/i915/intel_lrc.c b/drivers/gpu/drm/i915/intel_lrc.c index db2cb423e88f..5dde7253c963 100644 --- a/drivers/gpu/drm/i915/intel_lrc.c +++ b/drivers/gpu/drm/i915/intel_lrc.c @@ -2724,6 +2724,14 @@ populate_lr_context(struct i915_gem_context *ctx, _MASKED_BIT_ENABLE(CTX_CTRL_ENGINE_CTX_RESTORE_INHIBIT | CTX_CTRL_ENGINE_CTX_SAVE_INHIBIT); + /* write the context's pid and hw_id/cid to the per-context HWS page */ + if(intel_vgpu_active(engine->i915) && pid_nr(ctx->pid)) { + *(u32*)(vaddr + LRC_PPHWSP_PN * PAGE_SIZE + I915_GEM_HWS_PID_ADDR) + = pid_nr(ctx->pid) & 0x3fffff; + *(u32*)(vaddr + LRC_PPHWSP_PN * PAGE_SIZE + I915_GEM_HWS_CID_ADDR) + = ctx->hw_id & 0x3fffff; + } + err_unpin_ctx: i915_gem_object_unpin_map(ctx_obj); return ret; diff --git a/drivers/gpu/drm/i915/intel_ringbuffer.h b/drivers/gpu/drm/i915/intel_ringbuffer.h index 93daa92232e0..ff42345d735b 100644 --- a/drivers/gpu/drm/i915/intel_ringbuffer.h +++ b/drivers/gpu/drm/i915/intel_ringbuffer.h @@ -790,6 +790,11 @@ intel_write_status_page(struct intel_engine_cs *engine, int reg, u32 value) #define I915_GEM_HWS_SCRATCH_INDEX 0x40 #define I915_GEM_HWS_SCRATCH_ADDR (I915_GEM_HWS_SCRATCH_INDEX << MI_STORE_DWORD_INDEX_SHIFT) +#define I915_GEM_HWS_PID_INDEX 0x50 +#define I915_GEM_HWS_PID_ADDR (I915_GEM_HWS_PID_INDEX << MI_STORE_DWORD_INDEX_SHIFT) +#define I915_GEM_HWS_CID_INDEX 0x58 +#define I915_GEM_HWS_CID_ADDR (I915_GEM_HWS_CID_INDEX << MI_STORE_DWORD_INDEX_SHIFT) + #define I915_HWS_CSB_BUF0_INDEX 0x10 #define I915_HWS_CSB_WRITE_INDEX 0x1f #define CNL_HWS_CSB_WRITE_INDEX 0x2f From 1696086e9d30ebdec1db2da360497d0ffab655a2 Mon Sep 17 00:00:00 2001 From: Brahim Abes Date: Fri, 14 Sep 2018 16:10:18 +0800 Subject: [PATCH 0912/1103] drm/i915/gvt: Add new trace point to output per domain info Added trace point "i915_gem_request_add_domain" that prints the following extra fields per each packet: -is_shadow_ctx: Check for Dom0 or guest domains -hw_id: To check against i915_context_status's HW context id -vgt_id: The host or guests domain ID -pid: Process ID submitting the request Change-Id: I3a71e1d5909260df5a07c98291ee9e908f698ea2 Signed-off-by: Brahim Abes Reviewed-by: Singh, Satyeshwar Reviewed-on: Reviewed-by: Jiang, Fei Reviewed-by: He, Min Reviewed-by: Dong, Eddie Tested-by: Dong, Eddie --- drivers/gpu/drm/i915/i915_debugfs.c | 30 ++++++++++++++++++ drivers/gpu/drm/i915/i915_request.c | 1 + drivers/gpu/drm/i915/i915_trace.h | 47 +++++++++++++++++++++++++++++ drivers/gpu/drm/i915/intel_drv.h | 5 +++ 4 files changed, 83 insertions(+) diff --git a/drivers/gpu/drm/i915/i915_debugfs.c b/drivers/gpu/drm/i915/i915_debugfs.c index f575aff83c9b..ff6d2c8b41bb 100644 --- a/drivers/gpu/drm/i915/i915_debugfs.c +++ b/drivers/gpu/drm/i915/i915_debugfs.c @@ -1939,6 +1939,36 @@ static void describe_ctx_ring(struct seq_file *m, struct intel_ring *ring) ring->space, ring->head, ring->tail, ring->emit); } +bool is_shadow_context(struct i915_gem_context *ctx) +{ + if (ctx->name && !strncmp(ctx->name, "Shadow Context", 14)) + return true; + + return false; +} + +int get_vgt_id(struct i915_gem_context *ctx) +{ + int vgt_id; + + vgt_id = 0; + + if (is_shadow_context(ctx)) + sscanf(ctx->name, "Shadow Context %d", &vgt_id); + + return vgt_id; +} + +int get_pid_shadowed(struct i915_gem_context *ctx, + struct intel_engine_cs *engine) +{ + int pid, vgt_id; + + sscanf(ctx->name, "Shadow Context %d", &vgt_id); + pid = intel_read_status_page(engine, I915_GEM_HWS_PID_INDEX + vgt_id); + return pid; +} + static void describe_ctx_ring_shadowed(struct seq_file *m, struct i915_gem_context *ctx, struct intel_ring *ring, struct intel_engine_cs *engine) diff --git a/drivers/gpu/drm/i915/i915_request.c b/drivers/gpu/drm/i915/i915_request.c index 5c2c93cbab12..1bd2a7ef1885 100644 --- a/drivers/gpu/drm/i915/i915_request.c +++ b/drivers/gpu/drm/i915/i915_request.c @@ -1054,6 +1054,7 @@ void i915_request_add(struct i915_request *request) lockdep_assert_held(&request->i915->drm.struct_mutex); trace_i915_request_add(request); + trace_i915_request_add_domain(request); /* * Make sure that no request gazumped us - if it was allocated after diff --git a/drivers/gpu/drm/i915/i915_trace.h b/drivers/gpu/drm/i915/i915_trace.h index b50c6b829715..af592e3d09a9 100644 --- a/drivers/gpu/drm/i915/i915_trace.h +++ b/drivers/gpu/drm/i915/i915_trace.h @@ -679,6 +679,53 @@ DEFINE_EVENT(i915_request, i915_request_add, TP_ARGS(rq) ); +TRACE_EVENT(i915_multi_domains, + TP_PROTO(struct i915_request *req), + TP_ARGS(req), + + TP_STRUCT__entry( + __field(u32, dev) + __field(u32, ctx) + __field(u32, ring) + __field(u32, seqno) + __field(u32, global) + __field(int, prio_req) + __field(int, prio_ctx) + __field(bool, shadow_ctx) + __field(u32, hw_id) + __field(int, vgt_id) + __field(u32, pid) + ), + + TP_fast_assign( + __entry->dev = req->i915->drm.primary->index; + __entry->ring = req->engine->id; + __entry->ctx = req->fence.context; + __entry->seqno = req->fence.seqno; + __entry->global = req->global_seqno; + __entry->prio_req = req->sched.attr.priority; + __entry->prio_ctx = req->sched.attr.priority; + __entry->shadow_ctx = is_shadow_context(req->gem_context); + __entry->hw_id = req->gem_context->hw_id; + __entry->vgt_id = get_vgt_id(req->gem_context); + __entry->pid = is_shadow_context(req->gem_context) ? + get_pid_shadowed(req->gem_context, req->engine) : + pid_nr(req->gem_context->pid); + ), + + TP_printk("dev=%u, ring=%u, ctx=%u, seqno=%u, global=%u, " + "priority=%d (%d), is_shadow_ctx=%u, hw_id=%u, " + "vgt_id=%u, pid=%u", __entry->dev, __entry->ring, + __entry->ctx, __entry->seqno, __entry->global, + __entry->prio_req, __entry->prio_ctx, __entry->shadow_ctx, + __entry->hw_id, __entry->vgt_id, __entry->pid) +); + +DEFINE_EVENT(i915_multi_domains, i915_request_add_domain, + TP_PROTO(struct i915_request *req), + TP_ARGS(req) +); + #if defined(CONFIG_DRM_I915_LOW_LEVEL_TRACEPOINTS) DEFINE_EVENT(i915_request, i915_request_submit, TP_PROTO(struct i915_request *rq), diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h index 7c11b8d10b66..b64da5510f67 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h @@ -1384,6 +1384,11 @@ static inline bool intel_irqs_enabled(struct drm_i915_private *dev_priv) return dev_priv->runtime_pm.irqs_enabled; } +bool is_shadow_context(struct i915_gem_context *ctx); +int get_vgt_id(struct i915_gem_context *ctx); +int get_pid_shadowed(struct i915_gem_context *ctx, + struct intel_engine_cs *engine); + int intel_get_crtc_scanline(struct intel_crtc *crtc); void gen8_irq_power_well_post_enable(struct drm_i915_private *dev_priv, u8 pipe_mask); From 52849fdda4700820e198b5bb7605edf9e13b8c1e Mon Sep 17 00:00:00 2001 From: Fei Jiang Date: Fri, 14 Sep 2018 16:10:19 +0800 Subject: [PATCH 0913/1103] drm/i915/gvt: preliminary per ring scheduler The basic idea is to make the scheduler of fine granularity at per-ring level, and to let vGPUs run simultaneously on different rings when they have different type of workload. Signed-off-by: Ping Gao Signed-off-by: Fei Jiang Acknowledged-by: Singh, Satyeshwar --- drivers/gpu/drm/i915/gvt/gvt.h | 2 +- drivers/gpu/drm/i915/gvt/sched_policy.c | 212 +++++++++++++++--------- drivers/gpu/drm/i915/gvt/scheduler.c | 12 +- drivers/gpu/drm/i915/gvt/scheduler.h | 6 +- drivers/gpu/drm/i915/gvt/vgpu.c | 7 +- 5 files changed, 147 insertions(+), 92 deletions(-) diff --git a/drivers/gpu/drm/i915/gvt/gvt.h b/drivers/gpu/drm/i915/gvt/gvt.h index d673c46e3179..718cf020e963 100644 --- a/drivers/gpu/drm/i915/gvt/gvt.h +++ b/drivers/gpu/drm/i915/gvt/gvt.h @@ -183,7 +183,7 @@ struct intel_vgpu { * scheduler structure. So below 2 vgpu data are protected * by sched_lock, not vgpu_lock. */ - void *sched_data; + void *sched_data[I915_NUM_ENGINES]; struct vgpu_sched_ctl sched_ctl; struct intel_vgpu_fence fence; diff --git a/drivers/gpu/drm/i915/gvt/sched_policy.c b/drivers/gpu/drm/i915/gvt/sched_policy.c index 4fac40d26549..f5127e07570b 100644 --- a/drivers/gpu/drm/i915/gvt/sched_policy.c +++ b/drivers/gpu/drm/i915/gvt/sched_policy.c @@ -34,15 +34,11 @@ #include "i915_drv.h" #include "gvt.h" -static bool vgpu_has_pending_workload(struct intel_vgpu *vgpu) +static bool vgpu_has_pending_workload(struct intel_vgpu *vgpu, + enum intel_engine_id ring_id) { - enum intel_engine_id i; - struct intel_engine_cs *engine; - - for_each_engine(engine, vgpu->gvt->dev_priv, i) { - if (!list_empty(workload_q_head(vgpu, i))) - return true; - } + if (!list_empty(workload_q_head(vgpu, ring_id))) + return true; return false; } @@ -68,11 +64,12 @@ struct gvt_sched_data { struct intel_gvt *gvt; struct hrtimer timer; unsigned long period; - struct list_head lru_runq_head; + struct list_head lru_runq_head[I915_NUM_ENGINES]; ktime_t expire_time; }; -static void vgpu_update_timeslice(struct intel_vgpu *vgpu, ktime_t cur_time) +static void vgpu_update_timeslice(struct intel_vgpu *vgpu, ktime_t cur_time, + enum intel_engine_id ring_id) { ktime_t delta_ts; struct vgpu_sched_data *vgpu_data; @@ -80,7 +77,7 @@ static void vgpu_update_timeslice(struct intel_vgpu *vgpu, ktime_t cur_time) if (!vgpu || vgpu == vgpu->gvt->idle_vgpu) return; - vgpu_data = vgpu->sched_data; + vgpu_data = vgpu->sched_data[ring_id]; delta_ts = ktime_sub(cur_time, vgpu_data->sched_in_time); vgpu_data->sched_time = ktime_add(vgpu_data->sched_time, delta_ts); vgpu_data->left_ts = ktime_sub(vgpu_data->left_ts, delta_ts); @@ -90,12 +87,13 @@ static void vgpu_update_timeslice(struct intel_vgpu *vgpu, ktime_t cur_time) #define GVT_TS_BALANCE_PERIOD_MS 100 #define GVT_TS_BALANCE_STAGE_NUM 10 -static void gvt_balance_timeslice(struct gvt_sched_data *sched_data) +static void gvt_balance_timeslice(struct gvt_sched_data *sched_data, + enum intel_engine_id ring_id) { struct vgpu_sched_data *vgpu_data; struct list_head *pos; - static uint64_t stage_check; - int stage = stage_check++ % GVT_TS_BALANCE_STAGE_NUM; + static uint64_t stage_check[I915_NUM_ENGINES]; + int stage = stage_check[ring_id]++ % GVT_TS_BALANCE_STAGE_NUM; /* The timeslice accumulation reset at stage 0, which is * allocated again without adding previous debt. @@ -104,12 +102,12 @@ static void gvt_balance_timeslice(struct gvt_sched_data *sched_data) int total_weight = 0; ktime_t fair_timeslice; - list_for_each(pos, &sched_data->lru_runq_head) { + list_for_each(pos, &sched_data->lru_runq_head[ring_id]) { vgpu_data = container_of(pos, struct vgpu_sched_data, lru_list); total_weight += vgpu_data->sched_ctl.weight; } - list_for_each(pos, &sched_data->lru_runq_head) { + list_for_each(pos, &sched_data->lru_runq_head[ring_id]) { vgpu_data = container_of(pos, struct vgpu_sched_data, lru_list); fair_timeslice = ktime_divns(ms_to_ktime(GVT_TS_BALANCE_PERIOD_MS), total_weight) * vgpu_data->sched_ctl.weight; @@ -118,7 +116,7 @@ static void gvt_balance_timeslice(struct gvt_sched_data *sched_data) vgpu_data->left_ts = vgpu_data->allocated_ts; } } else { - list_for_each(pos, &sched_data->lru_runq_head) { + list_for_each(pos, &sched_data->lru_runq_head[ring_id]) { vgpu_data = container_of(pos, struct vgpu_sched_data, lru_list); /* timeslice for next 100ms should add the left/debt @@ -129,62 +127,63 @@ static void gvt_balance_timeslice(struct gvt_sched_data *sched_data) } } -static void try_to_schedule_next_vgpu(struct intel_gvt *gvt) +static void try_to_schedule_next_vgpu(struct intel_gvt *gvt, + enum intel_engine_id ring_id) { struct intel_gvt_workload_scheduler *scheduler = &gvt->scheduler; - enum intel_engine_id i; - struct intel_engine_cs *engine; struct vgpu_sched_data *vgpu_data; ktime_t cur_time; /* no need to schedule if next_vgpu is the same with current_vgpu, * let scheduler chose next_vgpu again by setting it to NULL. */ - if (scheduler->next_vgpu == scheduler->current_vgpu) { - scheduler->next_vgpu = NULL; + if (scheduler->next_vgpu[ring_id] == + scheduler->current_vgpu[ring_id]) { + scheduler->next_vgpu[ring_id] = NULL; return; } + /* no target to schedule */ + if (!scheduler->next_vgpu[ring_id]) + return; /* * after the flag is set, workload dispatch thread will * stop dispatching workload for current vgpu */ - scheduler->need_reschedule = true; + scheduler->need_reschedule[ring_id] = true; /* still have uncompleted workload? */ - for_each_engine(engine, gvt->dev_priv, i) { - if (scheduler->current_workload[i]) - return; - } + if (scheduler->current_workload[ring_id]) + return; cur_time = ktime_get(); - vgpu_update_timeslice(scheduler->current_vgpu, cur_time); - vgpu_data = scheduler->next_vgpu->sched_data; + vgpu_update_timeslice(scheduler->current_vgpu[ring_id], cur_time, ring_id); + vgpu_data = scheduler->next_vgpu[ring_id]->sched_data[ring_id]; vgpu_data->sched_in_time = cur_time; /* switch current vgpu */ - scheduler->current_vgpu = scheduler->next_vgpu; - scheduler->next_vgpu = NULL; + scheduler->current_vgpu[ring_id] = scheduler->next_vgpu[ring_id]; + scheduler->next_vgpu[ring_id] = NULL; - scheduler->need_reschedule = false; + scheduler->need_reschedule[ring_id] = false; /* wake up workload dispatch thread */ - for_each_engine(engine, gvt->dev_priv, i) - wake_up(&scheduler->waitq[i]); + wake_up(&scheduler->waitq[ring_id]); } -static struct intel_vgpu *find_busy_vgpu(struct gvt_sched_data *sched_data) +static struct intel_vgpu *find_busy_vgpu(struct gvt_sched_data *sched_data, + enum intel_engine_id ring_id) { struct vgpu_sched_data *vgpu_data; struct intel_vgpu *vgpu = NULL; - struct list_head *head = &sched_data->lru_runq_head; + struct list_head *head = &sched_data->lru_runq_head[ring_id]; struct list_head *pos; /* search a vgpu with pending workload */ list_for_each(pos, head) { vgpu_data = container_of(pos, struct vgpu_sched_data, lru_list); - if (!vgpu_has_pending_workload(vgpu_data->vgpu)) + if (!vgpu_has_pending_workload(vgpu_data->vgpu, ring_id)) continue; if (vgpu_data->pri_sched) { @@ -208,7 +207,8 @@ static struct intel_vgpu *find_busy_vgpu(struct gvt_sched_data *sched_data) /* in nanosecond */ #define GVT_DEFAULT_TIME_SLICE 1000000 -static void tbs_sched_func(struct gvt_sched_data *sched_data) +static void tbs_sched_func(struct gvt_sched_data *sched_data, + enum intel_engine_id ring_id) { struct intel_gvt *gvt = sched_data->gvt; struct intel_gvt_workload_scheduler *scheduler = &gvt->scheduler; @@ -216,31 +216,34 @@ static void tbs_sched_func(struct gvt_sched_data *sched_data) struct intel_vgpu *vgpu = NULL; /* no active vgpu or has already had a target */ - if (list_empty(&sched_data->lru_runq_head) || scheduler->next_vgpu) + if (list_empty(&sched_data->lru_runq_head[ring_id]) + || scheduler->next_vgpu[ring_id]) goto out; - vgpu = find_busy_vgpu(sched_data); + vgpu = find_busy_vgpu(sched_data, ring_id); if (vgpu) { - scheduler->next_vgpu = vgpu; - vgpu_data = vgpu->sched_data; + scheduler->next_vgpu[ring_id] = vgpu; + vgpu_data = vgpu->sched_data[ring_id]; if (!vgpu_data->pri_sched) { /* Move the last used vGPU to the tail of lru_list */ list_del_init(&vgpu_data->lru_list); list_add_tail(&vgpu_data->lru_list, - &sched_data->lru_runq_head); + &sched_data->lru_runq_head[ring_id]); } } else { - scheduler->next_vgpu = gvt->idle_vgpu; + scheduler->next_vgpu[ring_id] = gvt->idle_vgpu; } out: - if (scheduler->next_vgpu) - try_to_schedule_next_vgpu(gvt); + if (scheduler->next_vgpu[ring_id]) + try_to_schedule_next_vgpu(gvt, ring_id); } void intel_gvt_schedule(struct intel_gvt *gvt) { struct gvt_sched_data *sched_data = gvt->scheduler.sched_data; ktime_t cur_time; + enum intel_engine_id i; + struct intel_engine_cs *engine; mutex_lock(&gvt->sched_lock); cur_time = ktime_get(); @@ -248,15 +251,19 @@ void intel_gvt_schedule(struct intel_gvt *gvt) if (test_and_clear_bit(INTEL_GVT_REQUEST_SCHED, (void *)&gvt->service_request)) { if (cur_time >= sched_data->expire_time) { - gvt_balance_timeslice(sched_data); + for_each_engine(engine, gvt->dev_priv, i) + gvt_balance_timeslice(sched_data, i); sched_data->expire_time = ktime_add_ms( cur_time, GVT_TS_BALANCE_PERIOD_MS); } } clear_bit(INTEL_GVT_REQUEST_EVENT_SCHED, (void *)&gvt->service_request); - vgpu_update_timeslice(gvt->scheduler.current_vgpu, cur_time); - tbs_sched_func(sched_data); + for_each_engine(engine, gvt->dev_priv, i) { + vgpu_update_timeslice(gvt->scheduler.current_vgpu[i], + cur_time, i); + tbs_sched_func(sched_data, i); + } mutex_unlock(&gvt->sched_lock); } @@ -276,6 +283,9 @@ static enum hrtimer_restart tbs_timer_fn(struct hrtimer *timer_data) static int tbs_sched_init(struct intel_gvt *gvt) { + enum intel_engine_id i; + struct intel_engine_cs *engine; + struct intel_gvt_workload_scheduler *scheduler = &gvt->scheduler; @@ -285,7 +295,9 @@ static int tbs_sched_init(struct intel_gvt *gvt) if (!data) return -ENOMEM; - INIT_LIST_HEAD(&data->lru_runq_head); + for_each_engine(engine, gvt->dev_priv, i) + INIT_LIST_HEAD(&data->lru_runq_head[i]); + hrtimer_init(&data->timer, CLOCK_MONOTONIC, HRTIMER_MODE_ABS); data->timer.function = tbs_timer_fn; data->period = GVT_DEFAULT_TIME_SLICE; @@ -311,18 +323,29 @@ static void tbs_sched_clean(struct intel_gvt *gvt) static int tbs_sched_init_vgpu(struct intel_vgpu *vgpu) { struct vgpu_sched_data *data; + enum intel_engine_id i; + struct intel_engine_cs *engine; - data = kzalloc(sizeof(*data), GFP_KERNEL); - if (!data) - return -ENOMEM; + for_each_engine(engine, vgpu->gvt->dev_priv, i) { + data = kzalloc(sizeof(*data), GFP_KERNEL); + if (!data) + goto err; - data->sched_ctl.weight = vgpu->sched_ctl.weight; - data->vgpu = vgpu; - INIT_LIST_HEAD(&data->lru_list); + data->sched_ctl.weight = vgpu->sched_ctl.weight; + data->vgpu = vgpu; + INIT_LIST_HEAD(&data->lru_list); - vgpu->sched_data = data; + vgpu->sched_data[i] = data; + } return 0; + +err: + for (; i >= 0; i--) { + kfree(vgpu->sched_data[i]); + vgpu->sched_data[i] = NULL; + } + return -ENOMEM; } static void tbs_sched_clean_vgpu(struct intel_vgpu *vgpu) @@ -330,8 +353,13 @@ static void tbs_sched_clean_vgpu(struct intel_vgpu *vgpu) struct intel_gvt *gvt = vgpu->gvt; struct gvt_sched_data *sched_data = gvt->scheduler.sched_data; - kfree(vgpu->sched_data); - vgpu->sched_data = NULL; + enum intel_engine_id i; + struct intel_engine_cs *engine; + + for_each_engine(engine, vgpu->gvt->dev_priv, i) { + kfree(vgpu->sched_data[i]); + vgpu->sched_data[i] = NULL; + } /* this vgpu id has been removed */ if (idr_is_empty(&gvt->vgpu_idr)) @@ -341,31 +369,42 @@ static void tbs_sched_clean_vgpu(struct intel_vgpu *vgpu) static void tbs_sched_start_schedule(struct intel_vgpu *vgpu) { struct gvt_sched_data *sched_data = vgpu->gvt->scheduler.sched_data; - struct vgpu_sched_data *vgpu_data = vgpu->sched_data; ktime_t now; + struct vgpu_sched_data *vgpu_data; + enum intel_engine_id i; + struct intel_engine_cs *engine; - if (!list_empty(&vgpu_data->lru_list)) - return; + for_each_engine(engine, vgpu->gvt->dev_priv, i) { + vgpu_data = vgpu->sched_data[i]; + if (!list_empty(&vgpu_data->lru_list)) + continue; - now = ktime_get(); - vgpu_data->pri_time = ktime_add(now, + now = ktime_get(); + vgpu_data->pri_time = ktime_add(now, ktime_set(GVT_SCHED_VGPU_PRI_TIME, 0)); - vgpu_data->pri_sched = true; + vgpu_data->pri_sched = true; - list_add(&vgpu_data->lru_list, &sched_data->lru_runq_head); + list_add(&vgpu_data->lru_list, &sched_data->lru_runq_head[i]); + vgpu_data->active = true; + } if (!hrtimer_active(&sched_data->timer)) hrtimer_start(&sched_data->timer, ktime_add_ns(ktime_get(), sched_data->period), HRTIMER_MODE_ABS); - vgpu_data->active = true; } static void tbs_sched_stop_schedule(struct intel_vgpu *vgpu) { - struct vgpu_sched_data *vgpu_data = vgpu->sched_data; + struct vgpu_sched_data *vgpu_data; + enum intel_engine_id i; + struct intel_engine_cs *engine; + + for_each_engine(engine, vgpu->gvt->dev_priv, i) { + vgpu_data = vgpu->sched_data[i]; - list_del_init(&vgpu_data->lru_list); - vgpu_data->active = false; + list_del_init(&vgpu_data->lru_list); + vgpu_data->active = false; + } } static struct intel_gvt_sched_policy_ops tbs_schedule_ops = { @@ -423,10 +462,16 @@ void intel_vgpu_clean_sched_policy(struct intel_vgpu *vgpu) void intel_vgpu_start_schedule(struct intel_vgpu *vgpu) { - struct vgpu_sched_data *vgpu_data = vgpu->sched_data; + struct vgpu_sched_data *vgpu_data; + struct intel_engine_cs *engine; + enum intel_engine_id i; mutex_lock(&vgpu->gvt->sched_lock); - if (!vgpu_data->active) { + for_each_engine(engine, vgpu->gvt->dev_priv, i) { + vgpu_data = vgpu->sched_data[i]; + if (vgpu_data->active) + continue; + gvt_dbg_core("vgpu%d: start schedule\n", vgpu->id); vgpu->gvt->scheduler.sched_ops->start_schedule(vgpu); } @@ -444,23 +489,26 @@ void intel_vgpu_stop_schedule(struct intel_vgpu *vgpu) { struct intel_gvt_workload_scheduler *scheduler = &vgpu->gvt->scheduler; - struct vgpu_sched_data *vgpu_data = vgpu->sched_data; - - if (!vgpu_data->active) - return; + struct vgpu_sched_data *vgpu_data; + enum intel_engine_id i; + struct intel_engine_cs *engine; gvt_dbg_core("vgpu%d: stop schedule\n", vgpu->id); mutex_lock(&vgpu->gvt->sched_lock); scheduler->sched_ops->stop_schedule(vgpu); - if (scheduler->next_vgpu == vgpu) - scheduler->next_vgpu = NULL; + for_each_engine(engine, vgpu->gvt->dev_priv, i) { + vgpu_data = vgpu->sched_data[i]; + + if (scheduler->next_vgpu[i] == vgpu) + scheduler->next_vgpu[i] = NULL; - if (scheduler->current_vgpu == vgpu) { - /* stop workload dispatching */ - scheduler->need_reschedule = true; - scheduler->current_vgpu = NULL; + if (scheduler->current_vgpu[i] == vgpu) { + /* stop workload dispatching */ + scheduler->need_reschedule[i] = true; + scheduler->current_vgpu[i] = NULL; + } } mutex_unlock(&vgpu->gvt->sched_lock); diff --git a/drivers/gpu/drm/i915/gvt/scheduler.c b/drivers/gpu/drm/i915/gvt/scheduler.c index 7c3636e3a725..ebba07a402d7 100644 --- a/drivers/gpu/drm/i915/gvt/scheduler.c +++ b/drivers/gpu/drm/i915/gvt/scheduler.c @@ -745,17 +745,18 @@ static struct intel_vgpu_workload *pick_next_workload( * no current vgpu / will be scheduled out / no workload * bail out */ - if (!scheduler->current_vgpu) { + if (!scheduler->current_vgpu[ring_id]) { gvt_dbg_sched("ring id %d stop - no current vgpu\n", ring_id); goto out; } - if (scheduler->need_reschedule) { + if (scheduler->need_reschedule[ring_id]) { gvt_dbg_sched("ring id %d stop - will reschedule\n", ring_id); goto out; } - if (list_empty(workload_q_head(scheduler->current_vgpu, ring_id))) + if (list_empty(workload_q_head(scheduler->current_vgpu[ring_id], + ring_id))) goto out; /* @@ -776,7 +777,8 @@ static struct intel_vgpu_workload *pick_next_workload( * schedule out a vgpu. */ scheduler->current_workload[ring_id] = container_of( - workload_q_head(scheduler->current_vgpu, ring_id)->next, + workload_q_head(scheduler->current_vgpu[ring_id], + ring_id)->next, struct intel_vgpu_workload, list); workload = scheduler->current_workload[ring_id]; @@ -964,7 +966,7 @@ static void complete_current_workload(struct intel_gvt *gvt, int ring_id) atomic_dec(&s->running_workload_num); wake_up(&scheduler->workload_complete_wq); - if (gvt->scheduler.need_reschedule) + if (gvt->scheduler.need_reschedule[ring_id]) intel_gvt_request_service(gvt, INTEL_GVT_REQUEST_EVENT_SCHED); mutex_unlock(&gvt->sched_lock); diff --git a/drivers/gpu/drm/i915/gvt/scheduler.h b/drivers/gpu/drm/i915/gvt/scheduler.h index 043c2ff07a7c..3cec02d2ac1a 100644 --- a/drivers/gpu/drm/i915/gvt/scheduler.h +++ b/drivers/gpu/drm/i915/gvt/scheduler.h @@ -37,10 +37,10 @@ #define _GVT_SCHEDULER_H_ struct intel_gvt_workload_scheduler { - struct intel_vgpu *current_vgpu; - struct intel_vgpu *next_vgpu; + struct intel_vgpu *current_vgpu[I915_NUM_ENGINES]; + struct intel_vgpu *next_vgpu[I915_NUM_ENGINES]; struct intel_vgpu_workload *current_workload[I915_NUM_ENGINES]; - bool need_reschedule; + bool need_reschedule[I915_NUM_ENGINES]; spinlock_t mmio_context_lock; /* can be null when owner is host */ diff --git a/drivers/gpu/drm/i915/gvt/vgpu.c b/drivers/gpu/drm/i915/gvt/vgpu.c index c628be05fbfe..d83e48b53d62 100644 --- a/drivers/gpu/drm/i915/gvt/vgpu.c +++ b/drivers/gpu/drm/i915/gvt/vgpu.c @@ -525,6 +525,8 @@ void intel_gvt_reset_vgpu_locked(struct intel_vgpu *vgpu, bool dmlr, struct intel_gvt *gvt = vgpu->gvt; struct intel_gvt_workload_scheduler *scheduler = &gvt->scheduler; unsigned int resetting_eng = dmlr ? ALL_ENGINES : engine_mask; + enum intel_engine_id i; + struct intel_engine_cs *engine; gvt_dbg_core("------------------------------------------\n"); gvt_dbg_core("resseting vgpu%d, dmlr %d, engine_mask %08x\n", @@ -537,7 +539,10 @@ void intel_gvt_reset_vgpu_locked(struct intel_vgpu *vgpu, bool dmlr, * The current_vgpu will set to NULL after stopping the * scheduler when the reset is triggered by current vgpu. */ - if (scheduler->current_vgpu == NULL) { + for_each_engine_masked(engine, gvt->dev_priv, resetting_eng, i) { + if (scheduler->current_vgpu[i] != NULL) + continue; + mutex_unlock(&vgpu->vgpu_lock); intel_gvt_wait_vgpu_idle(vgpu); mutex_lock(&vgpu->vgpu_lock); From 678e5e1a7e4f626915ff8b17b34c42fd5b0e2636 Mon Sep 17 00:00:00 2001 From: Min He Date: Fri, 14 Sep 2018 16:10:19 +0800 Subject: [PATCH 0914/1103] drm/i915/gvt: Support vGPU guest framebuffer GEM object In the compositor mode of display, dom0/host needs to get the guest framebuffer to do more rendering, so that the guest VM's screen can show up in more fancy way, e.g., in an X window of dom0/host. In order to do that, a new gem object type "gvtbuffer" is introduced to i915. Different from normal gem object in i915, gvtbuffer does not have its own backing storage. Instead, it borrows the page frames of guest VM's framebuffer as its own backing storage. From high level, it works this way: a) gvt notifies kernel/userspace the guest OS page flip by monitoring the related guest MMIO changes and commands. b) user space issue IOCTL to create gvtbuffer gem object. c) kernel creates the gem object, and record the guest FB base address (gfx address) from MMIO. d) When needed, the gvtbuffer will be bound to graphics memory, and be used as normal gem object for rendering. Guest framebuffer must be inside GGTT, whereas the gvtbuffer can be in either GGTT or PPGTT, depending on the requirement of the rendering. Since the gvtbuffer corresponds to the guest framebuffer, which is from guest physical memory, we may not be able to get "page struct" for them. But i915 gem framework has had similar cases. A gem object can have stolen memory as its backing storage. In such case, the backing storage does not have "page struct" as well, and i915 has handled the case in the framework well. This patch was originally from daivid.j.cowperthwaite@intel.com, and pretty some changes were made since then. Change-Id: Ic0821f58dd568217a44b1b478c9659c709889c43 Signed-off-by: Zhiyuan Lv Reviewed-on: Reviewed-by: He, Min Reviewed-by: Jiang, Fei Reviewed-by: Dong, Eddie Tested-by: Dong, Eddie --- drivers/gpu/drm/i915/Makefile | 1 + drivers/gpu/drm/i915/gvt/fb_decoder.c | 68 +++++ drivers/gpu/drm/i915/gvt/fb_decoder.h | 4 + drivers/gpu/drm/i915/i915_drv.c | 1 + drivers/gpu/drm/i915/i915_drv.h | 2 + drivers/gpu/drm/i915/i915_gem_gvtbuffer.c | 293 ++++++++++++++++++++++ include/uapi/drm/drm_fourcc.h | 9 + include/uapi/drm/i915_drm.h | 40 +++ 8 files changed, 418 insertions(+) create mode 100644 drivers/gpu/drm/i915/i915_gem_gvtbuffer.c diff --git a/drivers/gpu/drm/i915/Makefile b/drivers/gpu/drm/i915/Makefile index 5794f102f9b8..a5198df1b1ca 100644 --- a/drivers/gpu/drm/i915/Makefile +++ b/drivers/gpu/drm/i915/Makefile @@ -79,6 +79,7 @@ i915-y += i915_cmd_parser.o \ i915_trace_points.o \ i915_vma.o \ intel_breadcrumbs.o \ + i915_gem_gvtbuffer.o \ intel_engine_cs.o \ intel_hangcheck.o \ intel_lrc.o \ diff --git a/drivers/gpu/drm/i915/gvt/fb_decoder.c b/drivers/gpu/drm/i915/gvt/fb_decoder.c index 481896fb712a..6bc40be48649 100644 --- a/drivers/gpu/drm/i915/gvt/fb_decoder.c +++ b/drivers/gpu/drm/i915/gvt/fb_decoder.c @@ -37,6 +37,7 @@ #include "i915_drv.h" #include "gvt.h" #include "i915_pvinfo.h" +#include "fb_decoder.h" #define PRIMARY_FORMAT_NUM 16 struct pixel_format { @@ -511,3 +512,70 @@ int intel_vgpu_decode_sprite_plane(struct intel_vgpu *vgpu, return 0; } + +/** + * intel_vgpu_decode_fb_format - Decode framebuffer information from raw vMMIO + * @gvt: GVT device + * @vmid: guest domain ID + * @fb: frame buffer infomation of guest. + * This function is called for query frame buffer format, so that gl can + * display guest fb in Dom0 + * + * Returns: + * Zero on success, negative error code if failed. + */ +int intel_vgpu_decode_fb_format(struct intel_gvt *gvt, int id, + struct intel_vgpu_fb_format *fb) + +{ + int i; + struct intel_vgpu *vgpu = NULL; + int ret = 0; + struct drm_i915_private *dev_priv = gvt->dev_priv; + + if (!fb) + return -EINVAL; + + /* TODO: use fine-grained refcnt later */ + mutex_lock(&gvt->lock); + + for_each_active_vgpu(gvt, vgpu, i) + if (vgpu->id == id) + break; + + if (!vgpu) { + gvt_err("Invalid vgpu ID (%d)\n", id); + mutex_unlock(&gvt->lock); + return -ENODEV; + } + + for (i = 0; i < I915_MAX_PIPES; i++) { + struct intel_vgpu_pipe_format *pipe = &fb->pipes[i]; + u32 ddi_func_ctl = vgpu_vreg_t(vgpu, TRANS_DDI_FUNC_CTL(i)); + + if (!(ddi_func_ctl & TRANS_DDI_FUNC_ENABLE)) { + pipe->ddi_port = DDI_PORT_NONE; + } else { + u32 port = (ddi_func_ctl & TRANS_DDI_PORT_MASK) >> + TRANS_DDI_PORT_SHIFT; + if (port <= DDI_PORT_E) + pipe->ddi_port = port; + else + pipe->ddi_port = DDI_PORT_NONE; + } + + ret |= intel_vgpu_decode_primary_plane(vgpu, &pipe->primary); + ret |= intel_vgpu_decode_sprite_plane(vgpu, &pipe->sprite); + ret |= intel_vgpu_decode_cursor_plane(vgpu, &pipe->cursor); + + if (ret) { + gvt_err("Decode format error for pipe(%d)\n", i); + ret = -EINVAL; + break; + } + } + + mutex_unlock(&gvt->lock); + + return ret; +} diff --git a/drivers/gpu/drm/i915/gvt/fb_decoder.h b/drivers/gpu/drm/i915/gvt/fb_decoder.h index 60c155085029..a202f9f6e81a 100644 --- a/drivers/gpu/drm/i915/gvt/fb_decoder.h +++ b/drivers/gpu/drm/i915/gvt/fb_decoder.h @@ -166,4 +166,8 @@ int intel_vgpu_decode_cursor_plane(struct intel_vgpu *vgpu, int intel_vgpu_decode_sprite_plane(struct intel_vgpu *vgpu, struct intel_vgpu_sprite_plane_format *plane); +extern +int intel_vgpu_decode_fb_format(struct intel_gvt *pdev, int vmid, + struct intel_vgpu_fb_format *fb); + #endif diff --git a/drivers/gpu/drm/i915/i915_drv.c b/drivers/gpu/drm/i915/i915_drv.c index f8cfd16be534..91fd59fe6345 100644 --- a/drivers/gpu/drm/i915/i915_drv.c +++ b/drivers/gpu/drm/i915/i915_drv.c @@ -2860,6 +2860,7 @@ static const struct drm_ioctl_desc i915_ioctls[] = { DRM_IOCTL_DEF_DRV(I915_PERF_ADD_CONFIG, i915_perf_add_config_ioctl, DRM_UNLOCKED|DRM_RENDER_ALLOW), DRM_IOCTL_DEF_DRV(I915_PERF_REMOVE_CONFIG, i915_perf_remove_config_ioctl, DRM_UNLOCKED|DRM_RENDER_ALLOW), DRM_IOCTL_DEF_DRV(I915_QUERY, i915_query_ioctl, DRM_UNLOCKED|DRM_RENDER_ALLOW), + DRM_IOCTL_DEF_DRV(I915_GEM_GVTBUFFER, i915_gem_gvtbuffer_ioctl, DRM_RENDER_ALLOW), }; static struct drm_driver driver = { diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index bfb655ec6996..127f69ad8368 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -3246,6 +3246,8 @@ int i915_perf_remove_config_ioctl(struct drm_device *dev, void *data, void i915_oa_init_reg_state(struct intel_engine_cs *engine, struct i915_gem_context *ctx, uint32_t *reg_state); +int i915_gem_gvtbuffer_ioctl(struct drm_device *dev, void *data, + struct drm_file *file); /* i915_gem_evict.c */ int __must_check i915_gem_evict_something(struct i915_address_space *vm, diff --git a/drivers/gpu/drm/i915/i915_gem_gvtbuffer.c b/drivers/gpu/drm/i915/i915_gem_gvtbuffer.c new file mode 100644 index 000000000000..fe723099788d --- /dev/null +++ b/drivers/gpu/drm/i915/i915_gem_gvtbuffer.c @@ -0,0 +1,293 @@ +/* + * Copyright © 2012 - 2015 Intel Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + * + */ + +#include "i915_drv.h" +#include "i915_trace.h" +#include "intel_drv.h" +#include + +#include "gvt/gvt.h" +#include "gvt/fb_decoder.h" + +static int +i915_gem_gvtbuffer_get_pages(struct drm_i915_gem_object *obj) +{ + BUG(); + return -EINVAL; +} + +static void i915_gem_gvtbuffer_put_pages(struct drm_i915_gem_object *obj, + struct sg_table *pages) +{ + /* like stolen memory, this should only be called during free + * after clearing pin count. + */ + sg_free_table(pages); + kfree(pages); +} + +static void +i915_gem_gvtbuffer_release(struct drm_i915_gem_object *obj) +{ + i915_gem_object_unpin_pages(obj); +} + +static const struct drm_i915_gem_object_ops i915_gem_gvtbuffer_ops = { + .get_pages = i915_gem_gvtbuffer_get_pages, + .put_pages = i915_gem_gvtbuffer_put_pages, + .release = i915_gem_gvtbuffer_release, +}; + +#define GEN8_DECODE_PTE(pte) \ + ((dma_addr_t)(((((u64)pte) >> 12) & 0x7ffffffULL) << 12)) + +#define GEN7_DECODE_PTE(pte) \ + ((dma_addr_t)(((((u64)pte) & 0x7f0) << 28) | (u64)(pte & 0xfffff000))) + +static struct sg_table * +i915_create_sg_pages_for_gvtbuffer(struct drm_device *dev, + u32 start, u32 num_pages) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + struct sg_table *st; + struct scatterlist *sg; + int i; + + st = kmalloc(sizeof(*st), GFP_KERNEL); + if (st == NULL) + return NULL; + + if (sg_alloc_table(st, num_pages, GFP_KERNEL)) { + kfree(st); + return NULL; + } + + if (INTEL_INFO(dev_priv)->gen >= 8) { + gen8_pte_t __iomem *gtt_entries = + (gen8_pte_t __iomem *)dev_priv->ggtt.gsm + + (start >> PAGE_SHIFT); + for_each_sg(st->sgl, sg, num_pages, i) { + sg->offset = 0; + sg->length = PAGE_SIZE; + sg_dma_address(sg) = + GEN8_DECODE_PTE(readq(>t_entries[i])); + sg_dma_len(sg) = PAGE_SIZE; + } + } else { + gen6_pte_t __iomem *gtt_entries = + (gen6_pte_t __iomem *)dev_priv->ggtt.gsm + + (start >> PAGE_SHIFT); + for_each_sg(st->sgl, sg, num_pages, i) { + sg->offset = 0; + sg->length = PAGE_SIZE; + sg_dma_address(sg) = + GEN7_DECODE_PTE(readq(>t_entries[i])); + sg_dma_len(sg) = PAGE_SIZE; + } + } + + return st; +} + +struct drm_i915_gem_object * +i915_gem_object_create_gvtbuffer(struct drm_device *dev, + u32 start, u32 num_pages) +{ + struct drm_i915_gem_object *obj; + obj = i915_gem_object_alloc(to_i915(dev)); + if (obj == NULL) + return NULL; + + drm_gem_private_object_init(dev, &obj->base, num_pages << PAGE_SHIFT); + i915_gem_object_init(obj, &i915_gem_gvtbuffer_ops); + + obj->mm.pages = i915_create_sg_pages_for_gvtbuffer(dev, start, num_pages); + if (obj->mm.pages == NULL) { + i915_gem_object_free(obj); + return NULL; + } + + if (i915_gem_object_pin_pages(obj)) + printk(KERN_ERR "%s:%d> Pin pages failed!\n", __func__, __LINE__); + obj->cache_level = I915_CACHE_L3_LLC; + + DRM_DEBUG_DRIVER("GVT_GEM: backing store base = 0x%x pages = 0x%x\n", + start, num_pages); + return obj; +} + +static int gvt_decode_information(struct drm_device *dev, + struct drm_i915_gem_gvtbuffer *args) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + struct intel_vgpu_fb_format fb; + struct intel_vgpu_primary_plane_format *p; + struct intel_vgpu_cursor_plane_format *c; + struct intel_vgpu_pipe_format *pipe; +#if IS_ENABLED(CONFIG_DRM_I915_GVT) + u32 id = args->id; + + if (intel_vgpu_decode_fb_format(dev_priv->gvt, id, &fb)) + return -EINVAL; +#else + return -EINVAL; +#endif + + pipe = ((args->pipe_id >= I915_MAX_PIPES) ? + NULL : &fb.pipes[args->pipe_id]); + + if (!pipe || !pipe->primary.enabled) { + DRM_DEBUG_DRIVER("GVT_GEM: Invalid pipe_id: %d\n", + args->pipe_id); + return -EINVAL; + } + + if ((args->plane_id) == I915_GVT_PLANE_PRIMARY) { + p = &pipe->primary; + args->enabled = p->enabled; + args->x_offset = p->x_offset; + args->y_offset = p->y_offset; + args->start = p->base; + args->width = p->width; + args->height = p->height; + args->stride = p->stride; + args->bpp = p->bpp; + args->hw_format = p->hw_format; + args->drm_format = p->drm_format; + args->tiled = p->tiled; + } else if ((args->plane_id) == I915_GVT_PLANE_CURSOR) { + c = &pipe->cursor; + args->enabled = c->enabled; + args->x_offset = c->x_hot; + args->y_offset = c->y_hot; + args->x_pos = c->x_pos; + args->y_pos = c->y_pos; + args->start = c->base; + args->width = c->width; + args->height = c->height; + args->stride = c->width * (c->bpp / 8); + args->bpp = c->bpp; + args->tiled = 0; + } else { + DRM_DEBUG_DRIVER("GVT_GEM: Invalid plaine_id: %d\n", + args->plane_id); + return -EINVAL; + } + + args->size = (((args->width * args->height * args->bpp) / 8) + + (PAGE_SIZE - 1)) >> PAGE_SHIFT; + + if (args->start & (PAGE_SIZE - 1)) { + DRM_DEBUG_DRIVER("GVT_GEM: Not aligned fb start address: " + "0x%x\n", args->start); + return -EINVAL; + } + + if (((args->start >> PAGE_SHIFT) + args->size) > + ggtt_total_entries(&dev_priv->ggtt)) { + DRM_DEBUG_DRIVER("GVT: Invalid GTT offset or size\n"); + return -EINVAL; + } + return 0; +} + +/** + * Creates a new mm object that wraps some user memory. + */ +int +i915_gem_gvtbuffer_ioctl(struct drm_device *dev, void *data, + struct drm_file *file) +{ + struct drm_i915_private *dev_priv = to_i915(dev); + struct drm_i915_gem_gvtbuffer *args = data; + struct drm_i915_gem_object *obj; + u32 handle; + int ret = 0; + + if (INTEL_INFO(dev_priv)->gen < 7) + return -EPERM; + + if (args->flags & I915_GVTBUFFER_CHECK_CAPABILITY) + return 0; +#if 0 + if (!gvt_check_host()) + return -EPERM; +#endif + /* if args->start != 0 do not decode, but use it as ggtt offset*/ + if (args->start == 0) { + ret = gvt_decode_information(dev, args); + if (ret) + return ret; + } + + if (ret) + return ret; + + if (args->flags & I915_GVTBUFFER_QUERY_ONLY) + return 0; + + obj = i915_gem_object_create_gvtbuffer(dev, args->start, args->size); + if (!obj) { + DRM_DEBUG_DRIVER("GVT_GEM: Failed to create gem object" + " for VM FB!\n"); + return -EINVAL; + } + + if (IS_SKYLAKE(dev_priv) || IS_BROXTON(dev_priv) || + IS_KABYLAKE(dev_priv)) { + unsigned int tiling_mode = I915_TILING_NONE; + unsigned int stride = 0; + + switch (args->tiled << 10) { + case PLANE_CTL_TILED_LINEAR: + /* Default valid value */ + break; + case PLANE_CTL_TILED_X: + tiling_mode = I915_TILING_X; + stride = args->stride; + break; + case PLANE_CTL_TILED_Y: + tiling_mode = I915_TILING_Y; + stride = args->stride; + break; + default: + DRM_ERROR("gvt: tiling mode %d not supported\n", args->tiled); + } + obj->tiling_and_stride = tiling_mode | stride; + } else { + obj->tiling_and_stride = (args->tiled ? I915_TILING_X : I915_TILING_NONE) | + (args->tiled ? args->stride : 0); + } + + ret = drm_gem_handle_create(file, &obj->base, &handle); + + /* drop reference from allocate - handle holds it now */ + i915_gem_object_put(obj); + + if (ret) + return ret; + + args->handle = handle; + return 0; +} diff --git a/include/uapi/drm/drm_fourcc.h b/include/uapi/drm/drm_fourcc.h index 721ab7e54d96..39b9a73a3c77 100644 --- a/include/uapi/drm/drm_fourcc.h +++ b/include/uapi/drm/drm_fourcc.h @@ -100,6 +100,15 @@ extern "C" { #define DRM_FORMAT_RGBX1010102 fourcc_code('R', 'X', '3', '0') /* [31:0] R:G:B:x 10:10:10:2 little endian */ #define DRM_FORMAT_BGRX1010102 fourcc_code('B', 'X', '3', '0') /* [31:0] B:G:R:x 10:10:10:2 little endian */ +/* 64 bpp RGB, below two items is add by VGT project, the reason as below: + * 1. Current version DRM code is not contains 64 bpp RGB definations. + * 2. VGT should support 64 bpp RGB for Windows 10 guest. + * 3. VGT add the 64 bpp RGB definations temperarily, before the DRM code add these definations. + */ +#define DRM_FORMAT_XRGB161616_VGT fourcc_code('X', 'R', '4', '8') /* [63:0] x:R:G:B 16:16:16:16 little endian */ +#define DRM_FORMAT_XBGR161616_VGT fourcc_code('X', 'B', '4', '8') /* [63:0] x:B:G:R 16:16:16:16 little endian */ + + #define DRM_FORMAT_ARGB2101010 fourcc_code('A', 'R', '3', '0') /* [31:0] A:R:G:B 2:10:10:10 little endian */ #define DRM_FORMAT_ABGR2101010 fourcc_code('A', 'B', '3', '0') /* [31:0] A:B:G:R 2:10:10:10 little endian */ #define DRM_FORMAT_RGBA1010102 fourcc_code('R', 'A', '3', '0') /* [31:0] R:G:B:A 10:10:10:2 little endian */ diff --git a/include/uapi/drm/i915_drm.h b/include/uapi/drm/i915_drm.h index 7f5634ce8e88..fc70f54e6ee9 100644 --- a/include/uapi/drm/i915_drm.h +++ b/include/uapi/drm/i915_drm.h @@ -319,6 +319,7 @@ typedef struct _drm_i915_sarea { #define DRM_I915_PERF_ADD_CONFIG 0x37 #define DRM_I915_PERF_REMOVE_CONFIG 0x38 #define DRM_I915_QUERY 0x39 +#define DRM_I915_GEM_GVTBUFFER 0x40 #define DRM_IOCTL_I915_INIT DRM_IOW( DRM_COMMAND_BASE + DRM_I915_INIT, drm_i915_init_t) #define DRM_IOCTL_I915_FLUSH DRM_IO ( DRM_COMMAND_BASE + DRM_I915_FLUSH) @@ -378,6 +379,8 @@ typedef struct _drm_i915_sarea { #define DRM_IOCTL_I915_PERF_REMOVE_CONFIG DRM_IOW(DRM_COMMAND_BASE + DRM_I915_PERF_REMOVE_CONFIG, __u64) #define DRM_IOCTL_I915_QUERY DRM_IOWR(DRM_COMMAND_BASE + DRM_I915_QUERY, struct drm_i915_query) +#define DRM_IOCTL_I915_GEM_GVTBUFFER DRM_IOWR(DRM_COMMAND_BASE + DRM_I915_GEM_GVTBUFFER, struct drm_i915_gem_gvtbuffer) + /* Allow drivers to submit batchbuffers directly to hardware, relying * on the security mechanisms provided by hardware. */ @@ -1717,6 +1720,43 @@ struct drm_i915_query_topology_info { __u8 data[]; }; +struct drm_i915_gem_gvtbuffer { + __u32 id; + __u32 plane_id; +#define I915_GVT_PLANE_PRIMARY 1 +#define I915_GVT_PLANE_SPRITE 2 +#define I915_GVT_PLANE_CURSOR 3 + __u32 pipe_id; + __u32 phys_pipe_id; + __u8 enabled; + __u8 tiled; + __u32 bpp; + __u32 hw_format; + __u32 drm_format; + __u32 start; + __u32 x_pos; + __u32 y_pos; + __u32 x_offset; + __u32 y_offset; + __u32 size; + __u32 width; + __u32 height; + __u32 stride; + __u64 user_ptr; + __u32 user_size; + __u32 flags; +#define I915_GVTBUFFER_READ_ONLY (1<<0) +#define I915_GVTBUFFER_QUERY_ONLY (1<<1) +#define I915_GVTBUFFER_CHECK_CAPABILITY (1<<2) +#define I915_GVTBUFFER_UNSYNCHRONIZED 0x80000000 + /** + * Returned handle for the object. + * + * Object handles are nonzero. + */ + __u32 handle; +}; + #if defined(__cplusplus) } #endif From 5d1fc7b7a0697be3cff0186497c6ca758b8f999b Mon Sep 17 00:00:00 2001 From: Fei Jiang Date: Tue, 27 Mar 2018 22:59:22 +0800 Subject: [PATCH 0915/1103] drm/i915/gvt: unset DDI_BUF_CTL_ENABLE during port emulation reset HDMI port enabling will assert port status, if it's already set during reset stage, i915 will pop up warning message. Unset those bits to avoid such warning message. Signed-off-by: Fei Jiang Change-Id: Ic8c738baa472d7f1086081cb1b634670327aae97 --- drivers/gpu/drm/i915/gvt/display.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/i915/gvt/display.c b/drivers/gpu/drm/i915/gvt/display.c index 40ecca217733..58b32dbd50e3 100644 --- a/drivers/gpu/drm/i915/gvt/display.c +++ b/drivers/gpu/drm/i915/gvt/display.c @@ -228,7 +228,7 @@ static void emulate_monitor_status_change(struct intel_vgpu *vgpu) vgpu_vreg_t(vgpu, PORT_CLK_SEL(PORT_B)) |= PORT_CLK_SEL_LCPLL_810; } - vgpu_vreg_t(vgpu, DDI_BUF_CTL(PORT_B)) |= DDI_BUF_CTL_ENABLE; + vgpu_vreg_t(vgpu, DDI_BUF_CTL(PORT_B)) &= ~DDI_BUF_CTL_ENABLE; vgpu_vreg_t(vgpu, DDI_BUF_CTL(PORT_B)) &= ~DDI_BUF_IS_IDLE; vgpu_vreg_t(vgpu, SDEISR) |= SDE_PORTB_HOTPLUG_CPT; } @@ -248,7 +248,7 @@ static void emulate_monitor_status_change(struct intel_vgpu *vgpu) vgpu_vreg_t(vgpu, PORT_CLK_SEL(PORT_C)) |= PORT_CLK_SEL_LCPLL_810; } - vgpu_vreg_t(vgpu, DDI_BUF_CTL(PORT_C)) |= DDI_BUF_CTL_ENABLE; + vgpu_vreg_t(vgpu, DDI_BUF_CTL(PORT_C)) &= ~DDI_BUF_CTL_ENABLE; vgpu_vreg_t(vgpu, DDI_BUF_CTL(PORT_C)) &= ~DDI_BUF_IS_IDLE; vgpu_vreg_t(vgpu, SFUSE_STRAP) |= SFUSE_STRAP_DDIC_DETECTED; } @@ -268,7 +268,7 @@ static void emulate_monitor_status_change(struct intel_vgpu *vgpu) vgpu_vreg_t(vgpu, PORT_CLK_SEL(PORT_D)) |= PORT_CLK_SEL_LCPLL_810; } - vgpu_vreg_t(vgpu, DDI_BUF_CTL(PORT_D)) |= DDI_BUF_CTL_ENABLE; + vgpu_vreg_t(vgpu, DDI_BUF_CTL(PORT_D)) &= ~DDI_BUF_CTL_ENABLE; vgpu_vreg_t(vgpu, DDI_BUF_CTL(PORT_D)) &= ~DDI_BUF_IS_IDLE; vgpu_vreg_t(vgpu, SFUSE_STRAP) |= SFUSE_STRAP_DDID_DETECTED; } From bc8ac8d80d91129d1e3c510d89dbb87988c80a22 Mon Sep 17 00:00:00 2001 From: Fei Jiang Date: Fri, 14 Sep 2018 16:10:19 +0800 Subject: [PATCH 0916/1103] drm/i915/gvt: add scaler owner to support guest plane scaling It is to support plane scaling feature, add scaler owner to avoid con-current scaler access. Such ownership is passed from SOS side through pvmmio scaler_owned member. Guest OS patch. Signed-off-by: Fei Jiang --- drivers/gpu/drm/i915/i915_drv.h | 1 + drivers/gpu/drm/i915/i915_pvinfo.h | 3 ++- drivers/gpu/drm/i915/i915_vgpu.c | 2 ++ drivers/gpu/drm/i915/intel_atomic.c | 14 +++++++++----- drivers/gpu/drm/i915/intel_display.c | 8 +++++++- drivers/gpu/drm/i915/intel_drv.h | 1 + 6 files changed, 22 insertions(+), 7 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index 127f69ad8368..671c1e66f9b7 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -1306,6 +1306,7 @@ struct i915_workarounds { struct i915_virtual_gpu { bool active; u32 caps; + u32 scaler_owned; }; /* used in computing the new watermarks state */ diff --git a/drivers/gpu/drm/i915/i915_pvinfo.h b/drivers/gpu/drm/i915/i915_pvinfo.h index eeaa3d506d95..dc9bdeaa3147 100644 --- a/drivers/gpu/drm/i915/i915_pvinfo.h +++ b/drivers/gpu/drm/i915/i915_pvinfo.h @@ -106,8 +106,9 @@ struct vgt_if { u32 execlist_context_descriptor_lo; u32 execlist_context_descriptor_hi; + u32 scaler_owned; - u32 rsv7[0x200 - 24]; /* pad to one page */ + u32 rsv7[0x200 - 25]; /* pad to one page */ } __packed; #define vgtif_reg(x) \ diff --git a/drivers/gpu/drm/i915/i915_vgpu.c b/drivers/gpu/drm/i915/i915_vgpu.c index 869cf4a3b6de..0f6182f32ded 100644 --- a/drivers/gpu/drm/i915/i915_vgpu.c +++ b/drivers/gpu/drm/i915/i915_vgpu.c @@ -76,6 +76,8 @@ void i915_check_vgpu(struct drm_i915_private *dev_priv) } dev_priv->vgpu.caps = __raw_i915_read32(dev_priv, vgtif_reg(vgt_caps)); + dev_priv->vgpu.scaler_owned = + __raw_i915_read32(dev_priv, vgtif_reg(scaler_owned)); dev_priv->vgpu.active = true; DRM_INFO("Virtual GPU for Intel GVT-g detected.\n"); diff --git a/drivers/gpu/drm/i915/intel_atomic.c b/drivers/gpu/drm/i915/intel_atomic.c index b04952bacf77..ec4a73e79709 100644 --- a/drivers/gpu/drm/i915/intel_atomic.c +++ b/drivers/gpu/drm/i915/intel_atomic.c @@ -316,7 +316,8 @@ int intel_atomic_setup_scalers(struct drm_i915_private *dev_priv, if (*scaler_id < 0) { /* find a free scaler */ for (j = 0; j < intel_crtc->num_scalers; j++) { - if (!scaler_state->scalers[j].in_use) { + if (!scaler_state->scalers[j].in_use && + scaler_state->scalers[j].owned == 1) { scaler_state->scalers[j].in_use = 1; *scaler_id = j; DRM_DEBUG_KMS("Attached scaler id %u.%u to %s:%d\n", @@ -350,10 +351,13 @@ int intel_atomic_setup_scalers(struct drm_i915_private *dev_priv, * scaler 0 operates in high quality (HQ) mode. * In this case use scaler 0 to take advantage of HQ mode */ - *scaler_id = 0; - scaler_state->scalers[0].in_use = 1; - scaler_state->scalers[0].mode = PS_SCALER_MODE_HQ; - scaler_state->scalers[1].in_use = 0; + if (scaler_state->scalers[0].owned == 1) { + *scaler_id = 0; + scaler_state->scalers[0].in_use = 1; + scaler_state->scalers[0].mode = + PS_SCALER_MODE_HQ; + scaler_state->scalers[1].in_use = 0; + } } else { scaler_state->scalers[*scaler_id].mode = PS_SCALER_MODE_DYN; } diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index d095f1f46de8..91b53bc444dd 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -8732,7 +8732,8 @@ static void skylake_get_pfit_config(struct intel_crtc *crtc, /* find scaler attached to this pipe */ for (i = 0; i < crtc->num_scalers; i++) { ps_ctrl = I915_READ(SKL_PS_CTRL(crtc->pipe, i)); - if (ps_ctrl & PS_SCALER_EN && !(ps_ctrl & PS_PLANE_SEL_MASK)) { + if (ps_ctrl & PS_SCALER_EN && !(ps_ctrl & PS_PLANE_SEL_MASK) && + scaler_state->scalers[i].owned) { id = i; pipe_config->pch_pfit.enabled = true; pipe_config->pch_pfit.pos = I915_READ(SKL_PS_WIN_POS(crtc->pipe, i)); @@ -13924,6 +13925,11 @@ static void intel_crtc_init_scalers(struct intel_crtc *crtc, scaler->in_use = 0; scaler->mode = PS_SCALER_MODE_DYN; + scaler->owned = 1; + if (intel_vgpu_active(dev_priv) && + !(1 << (crtc->pipe * SKL_NUM_SCALERS + i) & + dev_priv->vgpu.scaler_owned)) + scaler->owned = 0; } scaler_state->scaler_id = -1; diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h index b64da5510f67..098c2886cf7e 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h @@ -564,6 +564,7 @@ struct intel_initial_plane_config { struct intel_scaler { int in_use; uint32_t mode; + int owned; }; struct intel_crtc_scaler_state { From 0263a7efbdc67b89e82ab9b760b138d769d49cf2 Mon Sep 17 00:00:00 2001 From: Fei Jiang Date: Fri, 14 Sep 2018 16:10:19 +0800 Subject: [PATCH 0917/1103] drm/i915/gvt: support guest plane scaling It is to support plane scaling feature, need let guest access PS_CTRL, PS_WIN, PS_POS registers. Use parameter domain_scaler_owner to control domain's scaler ownership. Validate with IGT test: kms_plane_scaling. SOS only patch. V2: Fix the issue when it writes 0 to PS_CTRL register to disable scaling. Signed-off-by: Min He Signed-off-by: Fei Jiang Reviewed-by: Zhao Yakui --- drivers/gpu/drm/i915/gvt/display.c | 20 +++++- drivers/gpu/drm/i915/gvt/gvt.h | 6 ++ drivers/gpu/drm/i915/gvt/handlers.c | 96 +++++++++++++++++----------- drivers/gpu/drm/i915/gvt/reg.h | 4 ++ drivers/gpu/drm/i915/gvt/vgpu.c | 13 ++++ drivers/gpu/drm/i915/i915_params.c | 22 +++++++ drivers/gpu/drm/i915/i915_params.h | 1 + drivers/gpu/drm/i915/intel_display.c | 5 ++ 8 files changed, 130 insertions(+), 37 deletions(-) diff --git a/drivers/gpu/drm/i915/gvt/display.c b/drivers/gpu/drm/i915/gvt/display.c index 58b32dbd50e3..73a2dbbaf79d 100644 --- a/drivers/gpu/drm/i915/gvt/display.c +++ b/drivers/gpu/drm/i915/gvt/display.c @@ -478,15 +478,33 @@ static void intel_gvt_vblank_work(struct work_struct *w) mutex_unlock(&gvt->lock); } +#define BITS_PER_DOMAIN 4 +#define MAX_SCALERS_PER_DOMAIN 2 + +#define DOMAIN_SCALER_OWNER(owner, pipe, scaler) \ + ((((owner) >> (pipe) * BITS_PER_DOMAIN * MAX_SCALERS_PER_DOMAIN) >> \ + BITS_PER_DOMAIN * (scaler)) & 0xf) + void intel_gvt_init_pipe_info(struct intel_gvt *gvt) { - int pipe; + enum pipe pipe; + unsigned int scaler; + unsigned int domain_scaler_owner = i915_modparams.domain_scaler_owner; + struct drm_i915_private *dev_priv = gvt->dev_priv; for (pipe = PIPE_A; pipe <= PIPE_C; pipe++) { gvt->pipe_info[pipe].pipe_num = pipe; gvt->pipe_info[pipe].gvt = gvt; INIT_WORK(&gvt->pipe_info[pipe].vblank_work, intel_gvt_vblank_work); + /* Each nibble represents domain id + * ids can be from 0-F. 0 for Dom0, 1,2,3...0xF for DomUs + * scaler_owner[i] holds the id of the domain that owns it, + * eg:0,1,2 etc + */ + for_each_universal_scaler(dev_priv, pipe, scaler) + gvt->pipe_info[pipe].scaler_owner[scaler] = + DOMAIN_SCALER_OWNER(domain_scaler_owner, pipe, scaler); } } diff --git a/drivers/gpu/drm/i915/gvt/gvt.h b/drivers/gpu/drm/i915/gvt/gvt.h index 718cf020e963..64139240207f 100644 --- a/drivers/gpu/drm/i915/gvt/gvt.h +++ b/drivers/gpu/drm/i915/gvt/gvt.h @@ -309,6 +309,7 @@ struct intel_gvt_pipe_info { struct intel_gvt *gvt; struct work_struct vblank_work; int plane_owner[I915_MAX_PLANES]; + int scaler_owner[SKL_NUM_SCALERS]; }; struct intel_gvt { @@ -472,6 +473,11 @@ void intel_vgpu_write_fence(struct intel_vgpu *vgpu, idr_for_each_entry((&(gvt)->vgpu_idr), (vgpu), (id)) \ for_each_if(vgpu->active) +#define for_each_universal_scaler(__dev_priv, __pipe, __s) \ + for ((__s) = 0; \ + (__s) < INTEL_INFO(__dev_priv)->num_scalers[(__pipe)] + 1; \ + (__s)++) + static inline void intel_vgpu_write_pci_bar(struct intel_vgpu *vgpu, u32 offset, u32 val, bool low) { diff --git a/drivers/gpu/drm/i915/gvt/handlers.c b/drivers/gpu/drm/i915/gvt/handlers.c index 91fe0a44367d..88d34ef2c057 100644 --- a/drivers/gpu/drm/i915/gvt/handlers.c +++ b/drivers/gpu/drm/i915/gvt/handlers.c @@ -1161,6 +1161,7 @@ static int pvinfo_mmio_read(struct intel_vgpu *vgpu, unsigned int offset, break; case 0x78010: /* vgt_caps */ case 0x7881c: + case _vgtif_reg(scaler_owned): break; default: invalid_read = true; @@ -1259,22 +1260,6 @@ static int pvinfo_mmio_write(struct intel_vgpu *vgpu, unsigned int offset, return 0; } -static int pf_write(struct intel_vgpu *vgpu, - unsigned int offset, void *p_data, unsigned int bytes) -{ - u32 val = *(u32 *)p_data; - - if ((offset == _PS_1A_CTRL || offset == _PS_2A_CTRL || - offset == _PS_1B_CTRL || offset == _PS_2B_CTRL || - offset == _PS_1C_CTRL) && (val & PS_PLANE_SEL_MASK) != 0) { - WARN_ONCE(true, "VM(%d): guest is trying to scaling a plane\n", - vgpu->id); - return 0; - } - - return intel_vgpu_default_mmio_write(vgpu, offset, p_data, bytes); -} - static int power_well_ctl_mmio_write(struct intel_vgpu *vgpu, unsigned int offset, void *p_data, unsigned int bytes) { @@ -2851,6 +2836,45 @@ static int skl_plane_mmio_write(struct intel_vgpu *vgpu, unsigned int offset, #define PLANE_WM_BASE(pipe, plane) _MMIO(_PLANE_WM_BASE(pipe, plane)) +static int skl_ps_mmio_write(struct intel_vgpu *vgpu, unsigned int offset, + void *p_data, unsigned int bytes) +{ + struct drm_i915_private *dev_priv = vgpu->gvt->dev_priv; + unsigned int pipe = SKL_PS_REG_TO_PIPE(offset); + unsigned int scaler = SKL_PS_REG_TO_SCALER(offset) - 1; + + if (pipe >= I915_MAX_PIPES || scaler >= SKL_NUM_SCALERS || + vgpu->gvt->pipe_info[pipe].scaler_owner[scaler] != vgpu->id) { + gvt_vgpu_err("Unsupport pipe %d, scaler %d scaling\n", + pipe, scaler); + return 0; + } + + if (!(vgpu_vreg_t(vgpu, PIPECONF(pipe)) & I965_PIPECONF_ACTIVE)) + return 0; + + if ((offset == _PS_1A_CTRL || offset == _PS_2A_CTRL || + offset == _PS_1B_CTRL || offset == _PS_2B_CTRL || + offset == _PS_1C_CTRL) && ((*(u32 *)p_data) & PS_SCALER_EN)) { + unsigned int plane; + + if (SKL_PS_REG_VALUE_TO_PLANE(*(u32 *)p_data) == 0) { + gvt_vgpu_err("Unsupport crtc scaling for UOS\n"); + return 0; + } + plane = SKL_PS_REG_VALUE_TO_PLANE(*(u32 *)p_data) - 1; + if (plane >= I915_MAX_PLANES || + vgpu->gvt->pipe_info[pipe].plane_owner[plane] != vgpu->id) { + gvt_vgpu_err("Unsupport plane %d scaling\n", plane); + return 0; + } + } + + write_vreg(vgpu, offset, p_data, bytes); + I915_WRITE(_MMIO(offset), vgpu_vreg(vgpu, offset)); + return 0; +} + static int init_skl_mmio_info(struct intel_gvt *gvt) { struct drm_i915_private *dev_priv = gvt->dev_priv; @@ -2901,26 +2925,26 @@ static int init_skl_mmio_info(struct intel_gvt *gvt) MMIO_D(_MMIO(0x6c05c), D_SKL_PLUS); MMIO_DH(_MMIO(0x6c060), D_SKL_PLUS, dpll_status_read, NULL); - MMIO_DH(SKL_PS_WIN_POS(PIPE_A, 0), D_SKL_PLUS, NULL, pf_write); - MMIO_DH(SKL_PS_WIN_POS(PIPE_A, 1), D_SKL_PLUS, NULL, pf_write); - MMIO_DH(SKL_PS_WIN_POS(PIPE_B, 0), D_SKL_PLUS, NULL, pf_write); - MMIO_DH(SKL_PS_WIN_POS(PIPE_B, 1), D_SKL_PLUS, NULL, pf_write); - MMIO_DH(SKL_PS_WIN_POS(PIPE_C, 0), D_SKL_PLUS, NULL, pf_write); - MMIO_DH(SKL_PS_WIN_POS(PIPE_C, 1), D_SKL_PLUS, NULL, pf_write); - - MMIO_DH(SKL_PS_WIN_SZ(PIPE_A, 0), D_SKL_PLUS, NULL, pf_write); - MMIO_DH(SKL_PS_WIN_SZ(PIPE_A, 1), D_SKL_PLUS, NULL, pf_write); - MMIO_DH(SKL_PS_WIN_SZ(PIPE_B, 0), D_SKL_PLUS, NULL, pf_write); - MMIO_DH(SKL_PS_WIN_SZ(PIPE_B, 1), D_SKL_PLUS, NULL, pf_write); - MMIO_DH(SKL_PS_WIN_SZ(PIPE_C, 0), D_SKL_PLUS, NULL, pf_write); - MMIO_DH(SKL_PS_WIN_SZ(PIPE_C, 1), D_SKL_PLUS, NULL, pf_write); - - MMIO_DH(SKL_PS_CTRL(PIPE_A, 0), D_SKL_PLUS, NULL, pf_write); - MMIO_DH(SKL_PS_CTRL(PIPE_A, 1), D_SKL_PLUS, NULL, pf_write); - MMIO_DH(SKL_PS_CTRL(PIPE_B, 0), D_SKL_PLUS, NULL, pf_write); - MMIO_DH(SKL_PS_CTRL(PIPE_B, 1), D_SKL_PLUS, NULL, pf_write); - MMIO_DH(SKL_PS_CTRL(PIPE_C, 0), D_SKL_PLUS, NULL, pf_write); - MMIO_DH(SKL_PS_CTRL(PIPE_C, 1), D_SKL_PLUS, NULL, pf_write); + MMIO_DH(SKL_PS_WIN_POS(PIPE_A, 0), D_SKL_PLUS, NULL, skl_ps_mmio_write); + MMIO_DH(SKL_PS_WIN_POS(PIPE_A, 1), D_SKL_PLUS, NULL, skl_ps_mmio_write); + MMIO_DH(SKL_PS_WIN_POS(PIPE_B, 0), D_SKL_PLUS, NULL, skl_ps_mmio_write); + MMIO_DH(SKL_PS_WIN_POS(PIPE_B, 1), D_SKL_PLUS, NULL, skl_ps_mmio_write); + MMIO_DH(SKL_PS_WIN_POS(PIPE_C, 0), D_SKL_PLUS, NULL, skl_ps_mmio_write); + MMIO_DH(SKL_PS_WIN_POS(PIPE_C, 1), D_SKL_PLUS, NULL, skl_ps_mmio_write); + + MMIO_DH(SKL_PS_WIN_SZ(PIPE_A, 0), D_SKL_PLUS, NULL, skl_ps_mmio_write); + MMIO_DH(SKL_PS_WIN_SZ(PIPE_A, 1), D_SKL_PLUS, NULL, skl_ps_mmio_write); + MMIO_DH(SKL_PS_WIN_SZ(PIPE_B, 0), D_SKL_PLUS, NULL, skl_ps_mmio_write); + MMIO_DH(SKL_PS_WIN_SZ(PIPE_B, 1), D_SKL_PLUS, NULL, skl_ps_mmio_write); + MMIO_DH(SKL_PS_WIN_SZ(PIPE_C, 0), D_SKL_PLUS, NULL, skl_ps_mmio_write); + MMIO_DH(SKL_PS_WIN_SZ(PIPE_C, 1), D_SKL_PLUS, NULL, skl_ps_mmio_write); + + MMIO_DH(SKL_PS_CTRL(PIPE_A, 0), D_SKL_PLUS, NULL, skl_ps_mmio_write); + MMIO_DH(SKL_PS_CTRL(PIPE_A, 1), D_SKL_PLUS, NULL, skl_ps_mmio_write); + MMIO_DH(SKL_PS_CTRL(PIPE_B, 0), D_SKL_PLUS, NULL, skl_ps_mmio_write); + MMIO_DH(SKL_PS_CTRL(PIPE_B, 1), D_SKL_PLUS, NULL, skl_ps_mmio_write); + MMIO_DH(SKL_PS_CTRL(PIPE_C, 0), D_SKL_PLUS, NULL, skl_ps_mmio_write); + MMIO_DH(SKL_PS_CTRL(PIPE_C, 1), D_SKL_PLUS, NULL, skl_ps_mmio_write); MMIO_PLANES_DH(PLANE_CTL, D_SKL_PLUS, NULL, skl_plane_mmio_write); MMIO_PLANES_DH(PLANE_STRIDE, D_SKL_PLUS, NULL, skl_plane_mmio_write); diff --git a/drivers/gpu/drm/i915/gvt/reg.h b/drivers/gpu/drm/i915/gvt/reg.h index d05c5516a472..b55fc82027e0 100644 --- a/drivers/gpu/drm/i915/gvt/reg.h +++ b/drivers/gpu/drm/i915/gvt/reg.h @@ -59,6 +59,10 @@ #define _REG_701AC(pipe, plane) (0x701ac + pipe * 0x1000 + plane * 0x100) +#define SKL_PS_REG_TO_PIPE(reg) (((reg) >> 11) & 0x3) +#define SKL_PS_REG_TO_SCALER(reg) (((reg) >> 8) & 0x3) +#define SKL_PS_REG_VALUE_TO_PLANE(val) (((val) >> 25) & 0x7) + #define SKL_PLANE_REG_TO_PIPE(reg) (((reg) >> 12) & 0x3) #define SKL_PLANE_REG_TO_PLANE(reg) ((((reg) & 0xFFF) - 0x180) >> 8) #define SKL_FLIP_EVENT(pipe, plane) (PRIMARY_A_FLIP_DONE + (plane)*3 + pipe) diff --git a/drivers/gpu/drm/i915/gvt/vgpu.c b/drivers/gpu/drm/i915/gvt/vgpu.c index d83e48b53d62..5cb46999b238 100644 --- a/drivers/gpu/drm/i915/gvt/vgpu.c +++ b/drivers/gpu/drm/i915/gvt/vgpu.c @@ -37,6 +37,11 @@ void populate_pvinfo_page(struct intel_vgpu *vgpu) { + enum pipe pipe; + int scaler; + struct intel_gvt *gvt = vgpu->gvt; + struct drm_i915_private *dev_priv = gvt->dev_priv; + /* setup the ballooning information */ vgpu_vreg64_t(vgpu, vgtif_reg(magic)) = VGT_MAGIC; vgpu_vreg_t(vgpu, vgtif_reg(version_major)) = 1; @@ -62,6 +67,14 @@ void populate_pvinfo_page(struct intel_vgpu *vgpu) vgpu_vreg_t(vgpu, vgtif_reg(cursor_x_hot)) = UINT_MAX; vgpu_vreg_t(vgpu, vgtif_reg(cursor_y_hot)) = UINT_MAX; + vgpu_vreg_t(vgpu, vgtif_reg(scaler_owned)) = 0; + for_each_pipe(dev_priv, pipe) + for_each_universal_scaler(dev_priv, pipe, scaler) + if (gvt->pipe_info[pipe].scaler_owner[scaler] == + vgpu->id) + vgpu_vreg_t(vgpu, vgtif_reg(scaler_owned)) |= + 1 << (pipe * SKL_NUM_SCALERS + scaler); + gvt_dbg_core("Populate PVINFO PAGE for vGPU %d\n", vgpu->id); gvt_dbg_core("aperture base [GMADR] 0x%llx size 0x%llx\n", vgpu_aperture_gmadr_base(vgpu), vgpu_aperture_sz(vgpu)); diff --git a/drivers/gpu/drm/i915/i915_params.c b/drivers/gpu/drm/i915/i915_params.c index 295e981e4a39..8bdd4043b563 100644 --- a/drivers/gpu/drm/i915/i915_params.c +++ b/drivers/gpu/drm/i915/i915_params.c @@ -174,6 +174,28 @@ i915_param_named(enable_dpcd_backlight, bool, 0600, i915_param_named(enable_gvt, bool, 0400, "Enable support for Intel GVT-g graphics virtualization host support(default:false)"); +i915_param_named(domain_scaler_owner, int, 0400, + "scaler owners for each domain and for each pipe ids can be from 0-F"); + +/* pipeA Scaler = BITS 0-7 pipeB scaler = 8-15, pipeC = 16-19 + * + * +----------+------------+-------------+------------+ + * |unused | Pipe C | Pipe B | Pipe A | + * +----------+------------+-------------+------------+ + * 31 20 19 16 15 8 7 0 + * + * Each nibble represents domain id. 0 for Dom0, 1,2,3...0xF for DomUs + * eg: domains_scaler_owners = 0x00030210 // 0x000|3|02|10 + * scaler domain + * scaler_owner1A -0 + * scaler_owner2A -1 + * scaler_owner1B -2 + * scaler_owner2B -0 + * scaler_owner1C -3 + * scaler_owner2C -0 + * + */ + static __always_inline void _print_param(struct drm_printer *p, const char *name, const char *type, diff --git a/drivers/gpu/drm/i915/i915_params.h b/drivers/gpu/drm/i915/i915_params.h index 6c4d4a21474b..74865c23f809 100644 --- a/drivers/gpu/drm/i915/i915_params.h +++ b/drivers/gpu/drm/i915/i915_params.h @@ -68,6 +68,7 @@ struct drm_printer; param(bool, nuclear_pageflip, false) \ param(bool, enable_dp_mst, true) \ param(bool, enable_dpcd_backlight, false) \ + param(int, domain_scaler_owner, 0x11100) \ param(bool, enable_gvt, false) #define MEMBER(T, member, ...) T member; diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 91b53bc444dd..bc4d36c1a672 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -13926,6 +13926,11 @@ static void intel_crtc_init_scalers(struct intel_crtc *crtc, scaler->in_use = 0; scaler->mode = PS_SCALER_MODE_DYN; scaler->owned = 1; +#if IS_ENABLED(CONFIG_DRM_I915_GVT) + if (intel_gvt_active(dev_priv) && + dev_priv->gvt->pipe_info[crtc->pipe].scaler_owner[i] != 0) + scaler->owned = 0; +#endif if (intel_vgpu_active(dev_priv) && !(1 << (crtc->pipe * SKL_NUM_SCALERS + i) & dev_priv->vgpu.scaler_owned)) From 385a1836b7e04fbfd6dba0804351cd3f535c00ae Mon Sep 17 00:00:00 2001 From: Pei Zhang Date: Fri, 14 Sep 2018 16:10:19 +0800 Subject: [PATCH 0918/1103] drm/i915/gvt: add module parameter enable_pvmmio This uint type module parameter is used to control the pvmmio features for MMIO emulation in GVT. This parameter is default 0. Its permission type is 0400 which means user could only change its value through the cmdline, this is to prevent the dynamic modification during runtime which would break the pvmmio internal logic. Notice: this patch is required to be applied to guest kernel. Change-Id: I570f1fe02101e518595c02fce67601b692871aa9 Signed-off-by: Pei Zhang Signed-off-by: Jiang, Fei Acknowledged-by: Singh, Satyeshwar Reviewed-on: Reviewed-by: He, Min Reviewed-by: Jiang, Fei Reviewed-by: Dong, Eddie Tested-by: Dong, Eddie --- drivers/gpu/drm/i915/i915_drv.c | 21 +++++++++++++++++++++ drivers/gpu/drm/i915/i915_drv.h | 1 + drivers/gpu/drm/i915/i915_params.c | 5 +++++ drivers/gpu/drm/i915/i915_params.h | 1 + drivers/gpu/drm/i915/i915_pvinfo.h | 13 ++++++++++++- drivers/gpu/drm/i915/i915_vgpu.c | 9 +++++++++ 6 files changed, 49 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/i915/i915_drv.c b/drivers/gpu/drm/i915/i915_drv.c index 91fd59fe6345..acc3be54b9f4 100644 --- a/drivers/gpu/drm/i915/i915_drv.c +++ b/drivers/gpu/drm/i915/i915_drv.c @@ -51,6 +51,7 @@ #include "i915_pmu.h" #include "i915_query.h" #include "i915_vgpu.h" +#include "intel_uc.h" #include "intel_drv.h" #include "intel_uc.h" @@ -991,6 +992,9 @@ static void i915_mmio_cleanup(struct drm_i915_private *dev_priv) intel_teardown_mchbar(dev_priv); pci_iounmap(pdev, dev_priv->regs); + if (intel_vgpu_active(dev_priv) && dev_priv->shared_page) + pci_iounmap(pdev, dev_priv->shared_page); + } /** @@ -1024,6 +1028,21 @@ static int i915_driver_init_mmio(struct drm_i915_private *dev_priv) intel_uc_init_mmio(dev_priv); + if (intel_vgpu_active(dev_priv) && i915_modparams.enable_pvmmio) { + u32 bar = 0; + u32 mmio_size = 2 * 1024 * 1024; + + /* Map a share page from the end of 2M mmio region in bar0. */ + dev_priv->shared_page = (struct gvt_shared_page *) + pci_iomap_range(dev_priv->drm.pdev, bar, + mmio_size, PAGE_SIZE); + if (dev_priv->shared_page == NULL) { + ret = -EIO; + DRM_ERROR("ivi: failed to map share page.\n"); + goto err_uncore; + } + } + ret = intel_engines_init_mmio(dev_priv); if (ret) goto err_uncore; @@ -1033,6 +1052,8 @@ static int i915_driver_init_mmio(struct drm_i915_private *dev_priv) return 0; err_uncore: + if (intel_vgpu_active(dev_priv) && dev_priv->shared_page) + pci_iounmap(dev_priv->drm.pdev, dev_priv->shared_page); intel_uncore_fini(dev_priv); err_bridge: pci_dev_put(dev_priv->bridge_dev); diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index 671c1e66f9b7..a5f8d05793ba 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -1590,6 +1590,7 @@ struct drm_i915_private { resource_size_t stolen_usable_size; /* Total size minus reserved ranges */ void __iomem *regs; + struct gvt_shared_page *shared_page; struct intel_uncore uncore; diff --git a/drivers/gpu/drm/i915/i915_params.c b/drivers/gpu/drm/i915/i915_params.c index 8bdd4043b563..062190f99a28 100644 --- a/drivers/gpu/drm/i915/i915_params.c +++ b/drivers/gpu/drm/i915/i915_params.c @@ -196,6 +196,11 @@ i915_param_named(domain_scaler_owner, int, 0400, * */ + +i915_param_named(enable_pvmmio, uint, 0400, + "Enable pv mmio feature and set pvmmio level, default 1." + "This parameter could only set from host, guest value is set through vgt_if"); + static __always_inline void _print_param(struct drm_printer *p, const char *name, const char *type, diff --git a/drivers/gpu/drm/i915/i915_params.h b/drivers/gpu/drm/i915/i915_params.h index 74865c23f809..358094837650 100644 --- a/drivers/gpu/drm/i915/i915_params.h +++ b/drivers/gpu/drm/i915/i915_params.h @@ -69,6 +69,7 @@ struct drm_printer; param(bool, enable_dp_mst, true) \ param(bool, enable_dpcd_backlight, false) \ param(int, domain_scaler_owner, 0x11100) \ + param(unsigned int, enable_pvmmio, 0) \ param(bool, enable_gvt, false) #define MEMBER(T, member, ...) T member; diff --git a/drivers/gpu/drm/i915/i915_pvinfo.h b/drivers/gpu/drm/i915/i915_pvinfo.h index dc9bdeaa3147..d1a3e3e68512 100644 --- a/drivers/gpu/drm/i915/i915_pvinfo.h +++ b/drivers/gpu/drm/i915/i915_pvinfo.h @@ -49,6 +49,15 @@ enum vgt_g2v_type { VGT_G2V_MAX, }; +#define VGPU_PVMMIO(vgpu) vgpu_vreg_t(vgpu, vgtif_reg(enable_pvmmio)) + +/* + * define different levels of PVMMIO optimization + */ +enum pvmmio_levels { + PVMMIO_ELSP_SUBMIT = 0x1, +}; + /* * VGT capabilities type */ @@ -106,9 +115,11 @@ struct vgt_if { u32 execlist_context_descriptor_lo; u32 execlist_context_descriptor_hi; + u32 enable_pvmmio; + u32 pv_mmio; u32 scaler_owned; - u32 rsv7[0x200 - 25]; /* pad to one page */ + u32 rsv7[0x200 - 27]; /* pad to one page */ } __packed; #define vgtif_reg(x) \ diff --git a/drivers/gpu/drm/i915/i915_vgpu.c b/drivers/gpu/drm/i915/i915_vgpu.c index 0f6182f32ded..d7a328f52978 100644 --- a/drivers/gpu/drm/i915/i915_vgpu.c +++ b/drivers/gpu/drm/i915/i915_vgpu.c @@ -79,6 +79,15 @@ void i915_check_vgpu(struct drm_i915_private *dev_priv) dev_priv->vgpu.scaler_owned = __raw_i915_read32(dev_priv, vgtif_reg(scaler_owned)); + /* If guest wants to enable pvmmio, it needs to enable it explicitly + * through vgt_if interface, and then read back the enable state from + * gvt layer. + */ + __raw_i915_write32(dev_priv, vgtif_reg(enable_pvmmio), + i915_modparams.enable_pvmmio); + i915_modparams.enable_pvmmio = __raw_i915_read16(dev_priv, + vgtif_reg(enable_pvmmio)); + dev_priv->vgpu.active = true; DRM_INFO("Virtual GPU for Intel GVT-g detected.\n"); } From dad824370efdd2713c99c97b9171bdc9e91a6a47 Mon Sep 17 00:00:00 2001 From: Pei Zhang Date: Fri, 14 Sep 2018 16:10:19 +0800 Subject: [PATCH 0919/1103] drm/i915/gvt: get ready of memory for pvmmio To enable pvmmio feature, we need to prepare to regions memory: the mmio memory whose size is 2M for Gen8/9, and the 4K shared page. GVT creates them for every vGPU instance, guest i915 driver will map them to virtual address. Change-Id: Ifcbd0e55783e19125e98036622cd5d08624d34fa Signed-off-by: Pei Zhang Acknowledged-by: Singh, Satyeshwar Reviewed-on: Reviewed-by: He, Min Reviewed-by: Jiang, Fei Reviewed-by: Dong, Eddie Tested-by: Dong, Eddie --- drivers/gpu/drm/i915/gvt/gvt.c | 2 ++ drivers/gpu/drm/i915/gvt/gvt.h | 2 ++ drivers/gpu/drm/i915/gvt/mmio.c | 22 ++++++++++++++++++---- 3 files changed, 22 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/i915/gvt/gvt.c b/drivers/gpu/drm/i915/gvt/gvt.c index 6261af450ee4..e4a3823e1226 100644 --- a/drivers/gpu/drm/i915/gvt/gvt.c +++ b/drivers/gpu/drm/i915/gvt/gvt.c @@ -248,6 +248,8 @@ static void init_device_info(struct intel_gvt *gvt) info->max_support_vgpus = 8; info->cfg_space_size = PCI_CFG_SPACE_EXP_SIZE; info->mmio_size = 2 * 1024 * 1024; + /* order of mmio size. assert(2^order == mmio_size) */ + info->mmio_size_order = 9; info->mmio_bar = 0; info->gtt_start_offset = 8 * 1024 * 1024; info->gtt_entry_size = 8; diff --git a/drivers/gpu/drm/i915/gvt/gvt.h b/drivers/gpu/drm/i915/gvt/gvt.h index 64139240207f..ba88f722d602 100644 --- a/drivers/gpu/drm/i915/gvt/gvt.h +++ b/drivers/gpu/drm/i915/gvt/gvt.h @@ -71,6 +71,7 @@ struct intel_gvt_device_info { u32 max_support_vgpus; u32 cfg_space_size; u32 mmio_size; + u32 mmio_size_order; u32 mmio_bar; unsigned long msi_cap_offset; u32 gtt_start_offset; @@ -100,6 +101,7 @@ struct intel_vgpu_fence { struct intel_vgpu_mmio { void *vreg; void *sreg; + struct gvt_shared_page *shared_page; }; #define INTEL_GVT_MAX_BAR_NUM 4 diff --git a/drivers/gpu/drm/i915/gvt/mmio.c b/drivers/gpu/drm/i915/gvt/mmio.c index 878a8a1f5ff5..4cb3f72ab56a 100644 --- a/drivers/gpu/drm/i915/gvt/mmio.c +++ b/drivers/gpu/drm/i915/gvt/mmio.c @@ -311,11 +311,21 @@ int intel_vgpu_init_mmio(struct intel_vgpu *vgpu) { const struct intel_gvt_device_info *info = &vgpu->gvt->device_info; - vgpu->mmio.vreg = vzalloc(array_size(info->mmio_size, 2)); + BUILD_BUG_ON(sizeof(struct gvt_shared_page) != PAGE_SIZE); + + vgpu->mmio.sreg = vzalloc(info->mmio_size); + vgpu->mmio.vreg = (void *)__get_free_pages(GFP_KERNEL, + info->mmio_size_order); if (!vgpu->mmio.vreg) return -ENOMEM; - vgpu->mmio.sreg = vgpu->mmio.vreg + info->mmio_size; + vgpu->mmio.shared_page = (struct gvt_shared_page *) __get_free_pages( + GFP_KERNEL, 0); + if (!vgpu->mmio.shared_page) { + vfree(vgpu->mmio.vreg); + vgpu->mmio.vreg = NULL; + return -ENOMEM; + } intel_vgpu_reset_mmio(vgpu, true); @@ -329,6 +339,10 @@ int intel_vgpu_init_mmio(struct intel_vgpu *vgpu) */ void intel_vgpu_clean_mmio(struct intel_vgpu *vgpu) { - vfree(vgpu->mmio.vreg); - vgpu->mmio.vreg = vgpu->mmio.sreg = NULL; + const struct intel_gvt_device_info *info = &vgpu->gvt->device_info; + + vfree(vgpu->mmio.sreg); + free_pages((unsigned long) vgpu->mmio.vreg, info->mmio_size_order); + free_pages((unsigned long) vgpu->mmio.shared_page, 0); + vgpu->mmio.vreg = vgpu->mmio.sreg = vgpu->mmio.shared_page = NULL; } From f8769fdd0aa0249410384dc79cb30c90fedaa60c Mon Sep 17 00:00:00 2001 From: Pei Zhang Date: Fri, 14 Sep 2018 16:10:19 +0800 Subject: [PATCH 0920/1103] drm/i915: implement pvmmio in guest i915 If pvmmio is enabled in i915 host driver, guest i915 will read most MMIO register directly, which won't be trapped to host GVT. A small range MMIOs still need trap. They are filtered in a static function. This patch is to implement the pvmmio in guest i915 driver. Also, for the elsp port writting, we will optimize it to leverage pvmmio. Notice: this patch is required in GVTg guest. Change-Id: I8d51430015822976450c14085979460b9cc021d7 Signed-off-by: Pei Zhang Acknowledged-by: Singh, Satyeshwar Reviewed-on: Reviewed-by: He, Min Reviewed-by: Jiang, Fei Reviewed-by: Dong, Eddie Tested-by: Dong, Eddie --- drivers/gpu/drm/i915/i915_drv.c | 1 + drivers/gpu/drm/i915/i915_drv.h | 10 ++++++++-- drivers/gpu/drm/i915/i915_pvinfo.h | 12 ++++++++++++ drivers/gpu/drm/i915/i915_reg.h | 27 +++++++++++++++++++++++++++ drivers/gpu/drm/i915/intel_lrc.c | 21 ++++++++++++++++++++- 5 files changed, 68 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_drv.c b/drivers/gpu/drm/i915/i915_drv.c index acc3be54b9f4..06b786199215 100644 --- a/drivers/gpu/drm/i915/i915_drv.c +++ b/drivers/gpu/drm/i915/i915_drv.c @@ -896,6 +896,7 @@ static int i915_driver_init_early(struct drm_i915_private *dev_priv, sizeof(device_info->platform_mask) * BITS_PER_BYTE); BUG_ON(device_info->gen > sizeof(device_info->gen_mask) * BITS_PER_BYTE); spin_lock_init(&dev_priv->irq_lock); + spin_lock_init(&dev_priv->shared_page_lock); spin_lock_init(&dev_priv->gpu_error.lock); mutex_init(&dev_priv->backlight_lock); spin_lock_init(&dev_priv->uncore.lock); diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index a5f8d05793ba..18650bc9c8e4 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -55,6 +55,7 @@ #include "i915_params.h" #include "i915_reg.h" +#include "i915_pvinfo.h" #include "i915_utils.h" #include "intel_bios.h" @@ -1591,6 +1592,7 @@ struct drm_i915_private { void __iomem *regs; struct gvt_shared_page *shared_page; + spinlock_t shared_page_lock; struct intel_uncore uncore; @@ -2786,7 +2788,7 @@ static inline bool intel_gvt_active(struct drm_i915_private *dev_priv) return dev_priv->gvt; } -static inline bool intel_vgpu_active(struct drm_i915_private *dev_priv) +static inline bool intel_vgpu_active(const struct drm_i915_private *dev_priv) { return dev_priv->vgpu.active; } @@ -3586,7 +3588,11 @@ static inline u64 intel_rc6_residency_us(struct drm_i915_private *dev_priv, static inline uint##x##_t __raw_i915_read##x(const struct drm_i915_private *dev_priv, \ i915_reg_t reg) \ { \ - return read##s(dev_priv->regs + i915_mmio_reg_offset(reg)); \ + if (!intel_vgpu_active(dev_priv) || !i915_modparams.enable_pvmmio || \ + likely(!in_mmio_read_trap_list((reg).reg))) \ + return read##s(dev_priv->regs + i915_mmio_reg_offset(reg)); \ + dev_priv->shared_page->reg_addr = i915_mmio_reg_offset(reg); \ + return read##s(dev_priv->regs + i915_mmio_reg_offset(vgtif_reg(pv_mmio))); \ } #define __raw_write(x, s) \ diff --git a/drivers/gpu/drm/i915/i915_pvinfo.h b/drivers/gpu/drm/i915/i915_pvinfo.h index d1a3e3e68512..c1089bdedd2e 100644 --- a/drivers/gpu/drm/i915/i915_pvinfo.h +++ b/drivers/gpu/drm/i915/i915_pvinfo.h @@ -49,6 +49,15 @@ enum vgt_g2v_type { VGT_G2V_MAX, }; +/* shared page(4KB) between gvt and VM, located at the first page next + * to MMIO region(2MB size normally). + */ +struct gvt_shared_page { + u32 elsp_data[4]; + u32 reg_addr; + u32 rsvd2[0x400 - 5]; +}; + #define VGPU_PVMMIO(vgpu) vgpu_vreg_t(vgpu, vgtif_reg(enable_pvmmio)) /* @@ -65,6 +74,9 @@ enum pvmmio_levels { #define VGT_CAPS_HWSP_EMULATION BIT(3) #define VGT_CAPS_HUGE_GTT BIT(4) +#define PVMMIO_LEVEL(dev_priv, level) \ + (intel_vgpu_active(dev_priv) && (i915_modparams.enable_pvmmio & level)) + struct vgt_if { u64 magic; /* VGT_MAGIC */ u16 version_major; diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h index 9e63cd47b60f..3c2cc2e19826 100644 --- a/drivers/gpu/drm/i915/i915_reg.h +++ b/drivers/gpu/drm/i915/i915_reg.h @@ -10652,4 +10652,31 @@ enum skl_power_gate { _ICL_DSC1_RC_BUF_THRESH_1_UDW_PB, \ _ICL_DSC1_RC_BUF_THRESH_1_UDW_PC) +/* GVT has special read process from some MMIO register, + * which so that should be trapped to GVT to make a + * complete emulation. Such MMIO is not too much, now using + * a static list to cover them. + */ +static inline bool in_mmio_read_trap_list(u32 reg) +{ + if (unlikely(reg >= PCH_GMBUS0.reg && reg <= PCH_GMBUS5.reg)) + return true; + + if (unlikely(reg == RING_TIMESTAMP(RENDER_RING_BASE).reg || + reg == RING_TIMESTAMP(BLT_RING_BASE).reg || + reg == RING_TIMESTAMP(GEN6_BSD_RING_BASE).reg || + reg == RING_TIMESTAMP(VEBOX_RING_BASE).reg || + reg == RING_TIMESTAMP(GEN8_BSD2_RING_BASE).reg || + reg == RING_TIMESTAMP_UDW(RENDER_RING_BASE).reg || + reg == RING_TIMESTAMP_UDW(BLT_RING_BASE).reg || + reg == RING_TIMESTAMP_UDW(GEN6_BSD_RING_BASE).reg || + reg == RING_TIMESTAMP_UDW(VEBOX_RING_BASE).reg)) + return true; + + if (unlikely(reg == SBI_DATA.reg || reg == 0x6c060 || reg == 0x206c)) + return true; + + return false; +} + #endif /* _I915_REG_H_ */ diff --git a/drivers/gpu/drm/i915/intel_lrc.c b/drivers/gpu/drm/i915/intel_lrc.c index 5dde7253c963..b937f9f05e6a 100644 --- a/drivers/gpu/drm/i915/intel_lrc.c +++ b/drivers/gpu/drm/i915/intel_lrc.c @@ -451,6 +451,8 @@ static void execlists_submit_ports(struct intel_engine_cs *engine) struct intel_engine_execlists *execlists = &engine->execlists; struct execlist_port *port = execlists->port; unsigned int n; + u32 descs[4]; + int i = 0; /* * We can skip acquiring intel_runtime_pm_get() here as it was taken @@ -493,10 +495,27 @@ static void execlists_submit_ports(struct intel_engine_cs *engine) GEM_BUG_ON(!n); desc = 0; } + if (intel_vgpu_active(engine->i915) && + PVMMIO_LEVEL(engine->i915, PVMMIO_ELSP_SUBMIT)) { + BUG_ON(i >= 4); + descs[i] = upper_32_bits(desc); + descs[i + 1] = lower_32_bits(desc); + i += 2; + continue; + } write_desc(execlists, desc, n); } - + if (intel_vgpu_active(engine->i915) && + PVMMIO_LEVEL(engine->i915, PVMMIO_ELSP_SUBMIT)) { + u32 __iomem *elsp_data = engine->i915->shared_page->elsp_data; + spin_lock(&engine->i915->shared_page_lock); + writel(descs[0], elsp_data); + writel(descs[1], elsp_data + 1); + writel(descs[2], elsp_data + 2); + writel(descs[3], execlists->submit_reg); + spin_unlock(&engine->i915->shared_page_lock); + } /* we need to manually load the submit queue */ if (execlists->ctrl_reg) writel(EL_CTRL_LOAD, execlists->ctrl_reg); From 8ec995d8fea29862e18030a4a0ad5984b08ea50d Mon Sep 17 00:00:00 2001 From: Pei Zhang Date: Fri, 14 Sep 2018 16:10:19 +0800 Subject: [PATCH 0921/1103] drm/i915/gvt: implement pvmmio in GVTg If pvmmio is enabled in i915 host driver, guest i915 will read most MMIO register directly, which won't be trapped to host GVT. A small range MMIOs still need trap. They are filtered in a static function, and this patch is to implement the handler of these registers in GVTg. Also, when pvmmio is enabled, we will optimize ELSP port writing, to reduce the mmio trap numbers from 4 to 1, which can improve the guest GPU performance. Change-Id: Ic72a87499baabe9b3b2fbb5ad827e6ae062ff959 Signed-off-by: Pei Zhang Acknowledged-by: Singh, Satyeshwar Reviewed-on: Reviewed-by: He, Min Reviewed-by: Jiang, Fei Reviewed-by: Dong, Eddie Tested-by: Dong, Eddie --- drivers/gpu/drm/i915/gvt/acrngt.c | 92 ++++++++++++++++++++++++++++ drivers/gpu/drm/i915/gvt/handlers.c | 75 ++++++++++++++++++++--- drivers/gpu/drm/i915/gvt/hypercall.h | 2 + drivers/gpu/drm/i915/gvt/mpt.h | 21 +++++++ drivers/gpu/drm/i915/gvt/vgpu.c | 4 ++ 5 files changed, 186 insertions(+), 8 deletions(-) diff --git a/drivers/gpu/drm/i915/gvt/acrngt.c b/drivers/gpu/drm/i915/gvt/acrngt.c index 346a676d77bc..c6fff10a1679 100644 --- a/drivers/gpu/drm/i915/gvt/acrngt.c +++ b/drivers/gpu/drm/i915/gvt/acrngt.c @@ -838,6 +838,97 @@ static int acrngt_set_trap_area(unsigned long handle, u64 start, return ret; } +static int acrngt_set_pvmmio(unsigned long handle, u64 start, u64 end, bool map) +{ + int rc, i; + unsigned long mfn, shared_mfn; + unsigned long pfn = start >> PAGE_SHIFT; + u32 mmio_size_fn = acrngt_priv.gvt->device_info.mmio_size >> PAGE_SHIFT; + struct acrngt_hvm_dev *info = (struct acrngt_hvm_dev *)handle; + + if (map) { + mfn = acrngt_virt_to_mfn(info->vgpu->mmio.vreg); + rc = acrngt_map_gfn_to_mfn(handle, pfn, mfn, mmio_size_fn, map); + if (rc) { + gvt_err("acrn-gvt: map pfn %lx to mfn %lx fail with ret %d\n", + pfn, mfn, rc); + return rc; + } + + /* map the shared page to guest */ + shared_mfn = acrngt_virt_to_mfn(info->vgpu->mmio.shared_page); + rc = acrngt_map_gfn_to_mfn(handle, pfn + mmio_size_fn, shared_mfn, 1, map); + if (rc) { + gvt_err("acrn-gvt: map shared page fail with ret %d\n", rc); + return rc; + } + + /* mmio access is trapped like memory write protection */ + rc = acrn_ioreq_add_iorange(info->client, REQ_WP, pfn << PAGE_SHIFT, + ((pfn + mmio_size_fn) << PAGE_SHIFT) - 1); + if (rc) { + gvt_err("failed acrn_ioreq_add_iorange for pfn 0x%lx\n", pfn); + return rc; + } + + for (i = 0; i < mmio_size_fn; i++) { + rc = write_protect_page(info->vm_id, + (pfn + i) << PAGE_SHIFT, true); + if (rc) { + gvt_err("failed set wp for pfn 0x%lx\n", pfn + i); + return rc; + } + } + + /* scratch reg access is trapped like mmio access, 1 page */ + rc = acrngt_map_gfn_to_mfn(handle, pfn + (VGT_PVINFO_PAGE >> PAGE_SHIFT), + mfn + (VGT_PVINFO_PAGE >> PAGE_SHIFT), 1, 0); + if (rc) { + gvt_err("acrn-gvt: map pfn %lx to mfn %lx fail with ret %d\n", + pfn, mfn, rc); + return rc; + } + rc = acrn_ioreq_add_iorange(info->client, REQ_MMIO, + (pfn << PAGE_SHIFT) + VGT_PVINFO_PAGE, + ((pfn + 1) << PAGE_SHIFT) + VGT_PVINFO_PAGE - 1); + if (rc) { + gvt_err("failed acrn_ioreq_add_iorange for pfn 0x%lx\n", + (pfn << PAGE_SHIFT) + VGT_PVINFO_PAGE); + return rc; + } + + } else { + mfn = acrngt_virt_to_mfn(info->vgpu->mmio.vreg); + rc = acrngt_map_gfn_to_mfn(handle, pfn, mfn, mmio_size_fn, map); + if (rc) { + gvt_err("acrn-gvt: map pfn %lx to mfn %lx fail with ret %d\n", + pfn, mfn, rc); + return rc; + } + rc = acrn_ioreq_del_iorange(info->client, REQ_WP, pfn << PAGE_SHIFT, + ((pfn + mmio_size_fn) << PAGE_SHIFT) - 1); + if (rc) { + gvt_err("failed acrn_ioreq_add_iorange for pfn 0x%lx\n", pfn); + return rc; + } + rc = acrn_ioreq_add_iorange(info->client, REQ_MMIO, pfn << PAGE_SHIFT, + ((pfn + mmio_size_fn) << PAGE_SHIFT) - 1); + if (rc) { + gvt_err("failed acrn_ioreq_del_iorange for pfn 0x%lx\n", pfn); + return rc; + } + + /* unmap the shared page to guest */ + shared_mfn = acrngt_virt_to_mfn(info->vgpu->mmio.shared_page); + rc = acrngt_map_gfn_to_mfn(handle, pfn + mmio_size_fn, shared_mfn, 1, map); + if (rc) { + gvt_err("acrn-gvt: map shared page fail with ret %d\n", rc); + return rc; + } + } + return rc; +} + static int acrngt_dom0_ready(void) { char *env[] = {"GVT_DOM0_READY=1", NULL}; @@ -880,6 +971,7 @@ struct intel_gvt_mpt acrn_gvt_mpt = { .dma_map_guest_page = acrngt_dma_map_guest_page, .dma_unmap_guest_page = acrngt_dma_unmap_guest_page, .set_trap_area = acrngt_set_trap_area, + .set_pvmmio = acrngt_set_pvmmio, .dom0_ready = acrngt_dom0_ready, }; EXPORT_SYMBOL_GPL(acrn_gvt_mpt); diff --git a/drivers/gpu/drm/i915/gvt/handlers.c b/drivers/gpu/drm/i915/gvt/handlers.c index 88d34ef2c057..14a75c6dff4d 100644 --- a/drivers/gpu/drm/i915/gvt/handlers.c +++ b/drivers/gpu/drm/i915/gvt/handlers.c @@ -1145,6 +1145,7 @@ static int pvinfo_mmio_read(struct intel_vgpu *vgpu, unsigned int offset, void *p_data, unsigned int bytes) { bool invalid_read = false; + int ret = 0; read_vreg(vgpu, offset, p_data, bytes); @@ -1159,9 +1160,27 @@ static int pvinfo_mmio_read(struct intel_vgpu *vgpu, unsigned int offset, _vgtif_reg(avail_rs.fence_num) + 4) invalid_read = true; break; + case _vgtif_reg(pv_mmio): + /* a remap happens from guest mmio read operation, the target reg offset + * is in the first DWORD of shared_page. + */ + { + u32 reg = vgpu->mmio.shared_page->reg_addr; + struct intel_gvt_mmio_info *mmio; + + mmio = find_mmio_info(vgpu->gvt, rounddown(reg, 4)); + if (mmio) + ret = mmio->read(vgpu, reg, p_data, bytes); + else + ret = intel_vgpu_default_mmio_read(vgpu, reg, p_data, + bytes); + break; + } + case 0x78010: /* vgt_caps */ case 0x7881c: case _vgtif_reg(scaler_owned): + case _vgtif_reg(enable_pvmmio): break; default: invalid_read = true; @@ -1171,7 +1190,7 @@ static int pvinfo_mmio_read(struct intel_vgpu *vgpu, unsigned int offset, gvt_vgpu_err("invalid pvinfo read: [%x:%x] = %x\n", offset, bytes, *(u32 *)p_data); vgpu->pv_notified = true; - return 0; + return ret; } static int handle_g2v_notification(struct intel_vgpu *vgpu, int notification) @@ -1219,6 +1238,26 @@ static int send_display_ready_uevent(struct intel_vgpu *vgpu, int ready) return kobject_uevent_env(kobj, KOBJ_ADD, env); } +#define INTEL_GVT_PCI_BAR_GTTMMIO 0 +static int set_pvmmio(struct intel_vgpu *vgpu, bool map) +{ + u64 start, end; + u64 val; + int ret; + + val = vgpu_cfg_space(vgpu)[PCI_BASE_ADDRESS_0]; + if (val & PCI_BASE_ADDRESS_MEM_TYPE_64) + start = *(u64 *)(vgpu_cfg_space(vgpu) + PCI_BASE_ADDRESS_0); + else + start = *(u32 *)(vgpu_cfg_space(vgpu) + PCI_BASE_ADDRESS_0); + + start &= ~GENMASK(3, 0); + end = start + vgpu->cfg_space.bar[INTEL_GVT_PCI_BAR_GTTMMIO].size - 1; + + ret = intel_gvt_hypervisor_set_pvmmio(vgpu, start, end, map); + return ret; +} + static int pvinfo_mmio_write(struct intel_vgpu *vgpu, unsigned int offset, void *p_data, unsigned int bytes) { @@ -1235,6 +1274,18 @@ static int pvinfo_mmio_write(struct intel_vgpu *vgpu, unsigned int offset, case _vgtif_reg(g2v_notify): ret = handle_g2v_notification(vgpu, data); break; + case _vgtif_reg(enable_pvmmio): + if (i915_modparams.enable_pvmmio) { + vgpu_vreg(vgpu, offset) = data & + i915_modparams.enable_pvmmio; + if (set_pvmmio(vgpu, !!vgpu_vreg(vgpu, offset))) { + vgpu_vreg(vgpu, offset) = 0; + break; + } + } else { + vgpu_vreg(vgpu, offset) = 0; + } + break; /* add xhot and yhot to handled list to avoid error log */ case _vgtif_reg(cursor_x_hot): case _vgtif_reg(cursor_y_hot): @@ -1628,6 +1679,7 @@ static int elsp_mmio_write(struct intel_vgpu *vgpu, unsigned int offset, int ring_id = intel_gvt_render_mmio_to_ring_id(vgpu->gvt, offset); struct intel_vgpu_execlist *execlist; u32 data = *(u32 *)p_data; + u32 *elsp_data = vgpu->mmio.shared_page->elsp_data; int ret = 0; if (WARN_ON(ring_id < 0 || ring_id >= I915_NUM_ENGINES)) @@ -1635,16 +1687,23 @@ static int elsp_mmio_write(struct intel_vgpu *vgpu, unsigned int offset, execlist = &vgpu->submission.execlist[ring_id]; - execlist->elsp_dwords.data[3 - execlist->elsp_dwords.index] = data; - if (execlist->elsp_dwords.index == 3) { + if (VGPU_PVMMIO(vgpu) & PVMMIO_ELSP_SUBMIT) { + execlist->elsp_dwords.data[3] = elsp_data[0]; + execlist->elsp_dwords.data[2] = elsp_data[1]; + execlist->elsp_dwords.data[1] = elsp_data[2]; + execlist->elsp_dwords.data[0] = data; ret = intel_vgpu_submit_execlist(vgpu, ring_id); - if(ret) - gvt_vgpu_err("fail submit workload on ring %d\n", - ring_id); + } else { + execlist->elsp_dwords.data[3 - execlist->elsp_dwords.index] = data; + if (execlist->elsp_dwords.index == 3) + ret = intel_vgpu_submit_execlist(vgpu, ring_id); + ++execlist->elsp_dwords.index; + execlist->elsp_dwords.index &= 0x3; } - ++execlist->elsp_dwords.index; - execlist->elsp_dwords.index &= 0x3; + if (ret) + gvt_vgpu_err("fail submit workload on ring %d\n", ring_id); + return ret; } diff --git a/drivers/gpu/drm/i915/gvt/hypercall.h b/drivers/gpu/drm/i915/gvt/hypercall.h index d4b7929c8bee..4c550627e78e 100644 --- a/drivers/gpu/drm/i915/gvt/hypercall.h +++ b/drivers/gpu/drm/i915/gvt/hypercall.h @@ -60,6 +60,8 @@ struct intel_gvt_mpt { unsigned long mfn, unsigned int nr, bool map); int (*set_trap_area)(unsigned long handle, u64 start, u64 end, bool map); + int (*set_pvmmio)(unsigned long handle, u64 start, u64 end, + bool map); int (*set_opregion)(void *vgpu); int (*get_vfio_device)(void *vgpu); void (*put_vfio_device)(void *vgpu); diff --git a/drivers/gpu/drm/i915/gvt/mpt.h b/drivers/gpu/drm/i915/gvt/mpt.h index feed7adb6fde..6eef2e01e46a 100644 --- a/drivers/gpu/drm/i915/gvt/mpt.h +++ b/drivers/gpu/drm/i915/gvt/mpt.h @@ -300,6 +300,27 @@ static inline int intel_gvt_hypervisor_set_trap_area( return intel_gvt_host.mpt->set_trap_area(vgpu->handle, start, end, map); } +/** + * intel_gvt_hypervisor_set_pvmmio - Set the pvmmio area + * @vgpu: a vGPU + * @start: the beginning of the guest physical address region + * @end: the end of the guest physical address region + * @map: map or unmap + * + * Returns: + * Zero on success, negative error code if failed. + */ +static inline int intel_gvt_hypervisor_set_pvmmio( + struct intel_vgpu *vgpu, u64 start, u64 end, bool map) +{ + /* a MPT implementation could have MMIO trapped elsewhere */ + if (!intel_gvt_host.mpt->set_pvmmio) + return -ENOENT; + + return intel_gvt_host.mpt->set_pvmmio(vgpu->handle, start, end, map); +} + + /** * intel_gvt_hypervisor_set_opregion - Set opregion for guest * @vgpu: a vGPU diff --git a/drivers/gpu/drm/i915/gvt/vgpu.c b/drivers/gpu/drm/i915/gvt/vgpu.c index 5cb46999b238..9e4a0b2b5f22 100644 --- a/drivers/gpu/drm/i915/gvt/vgpu.c +++ b/drivers/gpu/drm/i915/gvt/vgpu.c @@ -75,6 +75,8 @@ void populate_pvinfo_page(struct intel_vgpu *vgpu) vgpu_vreg_t(vgpu, vgtif_reg(scaler_owned)) |= 1 << (pipe * SKL_NUM_SCALERS + scaler); + vgpu_vreg_t(vgpu, vgtif_reg(enable_pvmmio)) = 0; + gvt_dbg_core("Populate PVINFO PAGE for vGPU %d\n", vgpu->id); gvt_dbg_core("aperture base [GMADR] 0x%llx size 0x%llx\n", vgpu_aperture_gmadr_base(vgpu), vgpu_aperture_sz(vgpu)); @@ -540,6 +542,7 @@ void intel_gvt_reset_vgpu_locked(struct intel_vgpu *vgpu, bool dmlr, unsigned int resetting_eng = dmlr ? ALL_ENGINES : engine_mask; enum intel_engine_id i; struct intel_engine_cs *engine; + bool enable_pvmmio = vgpu_vreg_t(vgpu, vgtif_reg(enable_pvmmio)); gvt_dbg_core("------------------------------------------\n"); gvt_dbg_core("resseting vgpu%d, dmlr %d, engine_mask %08x\n", @@ -574,6 +577,7 @@ void intel_gvt_reset_vgpu_locked(struct intel_vgpu *vgpu, bool dmlr, intel_vgpu_reset_mmio(vgpu, dmlr); populate_pvinfo_page(vgpu); + vgpu_vreg_t(vgpu, vgtif_reg(enable_pvmmio)) = enable_pvmmio; intel_vgpu_reset_display(vgpu); if (dmlr) { From 78cfa84c993bfb7149ccacbd0eb888f0725e2165 Mon Sep 17 00:00:00 2001 From: Fei Jiang Date: Mon, 26 Feb 2018 20:07:34 +0800 Subject: [PATCH 0922/1103] drm/i915/gvt: add pvmmio support in preempt context submission This patch added the pvmmio support in preemption context submission for gvt-g guest. And because GVT-g doesn't support preemption in guests, this patch also disabled preempttion in guest. Signed-off-by: Fei Jiang --- drivers/gpu/drm/i915/intel_lrc.c | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_lrc.c b/drivers/gpu/drm/i915/intel_lrc.c index b937f9f05e6a..534223874dd2 100644 --- a/drivers/gpu/drm/i915/intel_lrc.c +++ b/drivers/gpu/drm/i915/intel_lrc.c @@ -186,7 +186,8 @@ static inline bool need_preempt(const struct intel_engine_cs *engine, const struct i915_request *last, int prio) { - return (intel_engine_has_preemption(engine) && + return (!intel_vgpu_active(engine->i915) && + intel_engine_has_preemption(engine) && __execlists_need_preempt(prio, rq_prio(last)) && !i915_request_completed(last)); } @@ -571,10 +572,24 @@ static void inject_preempt_context(struct intel_engine_cs *engine) * the state of the GPU is known (idle). */ GEM_TRACE("%s\n", engine->name); - for (n = execlists_num_ports(execlists); --n; ) - write_desc(execlists, 0, n); - write_desc(execlists, ce->lrc_desc, n); + if (intel_vgpu_active(engine->i915) && + PVMMIO_LEVEL(engine->i915, PVMMIO_ELSP_SUBMIT)) { + u32 __iomem *elsp_data = engine->i915->shared_page->elsp_data; + + spin_lock(&engine->i915->shared_page_lock); + writel(0, elsp_data); + writel(0, elsp_data + 1); + writel(upper_32_bits(ce->lrc_desc), elsp_data + 2); + writel(lower_32_bits(ce->lrc_desc), execlists->submit_reg); + spin_unlock(&engine->i915->shared_page_lock); + + } else { + for (n = execlists_num_ports(execlists); --n; ) + write_desc(execlists, 0, n); + + write_desc(execlists, ce->lrc_desc, n); + } /* we need to manually load the submit queue */ if (execlists->ctrl_reg) From aa5604c95cfb5c2f7089c0678ff1496f69c3c192 Mon Sep 17 00:00:00 2001 From: Zhao Yakui Date: Fri, 14 Sep 2018 16:10:20 +0800 Subject: [PATCH 0923/1103] drm/i915: Use 64-bit write to optimize writing fence_reg On VGPU scenario the read/write operation of fence_reg will be trapped by the GVT-g. Then gvt-g follows the HW spec to program the fence_reg. And the gvt-g takes care of updating the fence reg correctly for any trapped value of fence reg. So it is unnecessary to read/write fence reg several times. It is enough that the fence reg is written only value in 64-bit mode. This will help to reduce the redundant trap of fence_reg mmio operation. V1->V2: Add back the condition judgement of !pipelined Signed-off-by: Zhao Yakui Reviewed-by: He Min --- drivers/gpu/drm/i915/i915_gem_fence_reg.c | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_gem_fence_reg.c b/drivers/gpu/drm/i915/i915_gem_fence_reg.c index d548ac05ccd7..317e376cc2da 100644 --- a/drivers/gpu/drm/i915/i915_gem_fence_reg.c +++ b/drivers/gpu/drm/i915/i915_gem_fence_reg.c @@ -63,6 +63,7 @@ static void i965_write_fence_reg(struct drm_i915_fence_reg *fence, i915_reg_t fence_reg_lo, fence_reg_hi; int fence_pitch_shift; u64 val; + struct drm_i915_private *dev_priv = fence->i915; if (INTEL_GEN(fence->i915) >= 6) { fence_reg_lo = FENCE_REG_GEN6_LO(fence->id); @@ -92,9 +93,17 @@ static void i965_write_fence_reg(struct drm_i915_fence_reg *fence, val |= I965_FENCE_REG_VALID; } - if (!pipelined) { - struct drm_i915_private *dev_priv = fence->i915; - + if (intel_vgpu_active(dev_priv)) { + /* Use the 64-bit RW to write fence reg on VGPU mode. + * The GVT-g can trap the written val of VGPU to program the + * fence reg. And the fence write in gvt-g follows the + * sequence of off/read/double-write/read. This assures that + * the fence reg is configured as expected. + * At the same time the 64-bit op can help to reduce the num + * of VGPU trap for the fence reg. + */ + I915_WRITE64_FW(fence_reg_lo, val); + } else if (!pipelined) { /* To w/a incoherency with non-atomic 64-bit register updates, * we split the 64-bit update into two 32-bit writes. In order * for a partial fence not to be evaluated between writes, we From 4fd44c3b38d7aaa3e2373f7f9263c22a92c85dea Mon Sep 17 00:00:00 2001 From: Fei Jiang Date: Tue, 10 Jul 2018 09:55:30 +0800 Subject: [PATCH 0924/1103] drm/i915/gvt: don't treat EINVAL if trap pci_command and pci_status together Previously we only support single pci_command writing trap. While when system suspends, pci_command and pci_status are written together in 32 bits, GVT-g also need trap them in such scenario. Signed-off-by: Fei Jiang --- drivers/gpu/drm/i915/gvt/cfg_space.c | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/i915/gvt/cfg_space.c b/drivers/gpu/drm/i915/gvt/cfg_space.c index 707b0a50da3c..f6bcfcb571b7 100644 --- a/drivers/gpu/drm/i915/gvt/cfg_space.c +++ b/drivers/gpu/drm/i915/gvt/cfg_space.c @@ -295,9 +295,22 @@ int intel_vgpu_emulate_cfg_write(struct intel_vgpu *vgpu, unsigned int offset, /* First check if it's PCI_COMMAND */ if (IS_ALIGNED(offset, 2) && offset == PCI_COMMAND) { - if (WARN_ON(bytes > 2)) + if (WARN_ON(bytes != 2 && bytes != 4)) return -EINVAL; - return emulate_pci_command_write(vgpu, offset, p_data, bytes); + + ret = -EINVAL; + if (bytes == 2) + ret = emulate_pci_command_write(vgpu, offset, + p_data, bytes); + if (bytes == 4) { + ret = emulate_pci_command_write(vgpu, offset, + p_data, 2); + if (ret) + return ret; + vgpu_pci_cfg_mem_write(vgpu, offset + 2, + (u8 *)p_data + 2, 2); + } + return ret; } switch (rounddown(offset, 4)) { From eb45a86a7b452176ba4230a8ffe5a23adfe3fcce Mon Sep 17 00:00:00 2001 From: Fei Jiang Date: Fri, 14 Sep 2018 16:10:20 +0800 Subject: [PATCH 0925/1103] drm/i915/gvt: pvmmio optimization for plane update It is performance optimization to reduce plane related register trap counter. When update plane, multiple plane related registers are updated together, optimize it to firstly cache all register values in share page, then only PLANE_SURF register writing is trapped. Plane pvmmio level is PVMMIO_PLANE_UPDATE. Patch for both SOS and UOS. V2: add memset tmp_plane to be more safer and add more commit description Signed-off-by: Fei Jiang Reviewed-by: Min He Reviewed-by: Zhao Yakui --- drivers/gpu/drm/i915/i915_pvinfo.h | 27 +++++++++++- drivers/gpu/drm/i915/intel_sprite.c | 68 +++++++++++++++++++++++++++++ 2 files changed, 94 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/i915/i915_pvinfo.h b/drivers/gpu/drm/i915/i915_pvinfo.h index c1089bdedd2e..740b2da14186 100644 --- a/drivers/gpu/drm/i915/i915_pvinfo.h +++ b/drivers/gpu/drm/i915/i915_pvinfo.h @@ -49,13 +49,37 @@ enum vgt_g2v_type { VGT_G2V_MAX, }; +#define PLANE_COLOR_CTL_BIT (1 << 0) +#define PLANE_KEY_BIT (1 << 1) +#define PLANE_SCALER_BIT (1 << 2) + +struct pv_plane_update { + u32 flags; + u32 plane_color_ctl; + u32 plane_key_val; + u32 plane_key_max; + u32 plane_key_msk; + u32 plane_offset; + u32 plane_stride; + u32 plane_size; + u32 plane_aux_dist; + u32 plane_aux_offset; + u32 ps_ctrl; + u32 ps_pwr_gate; + u32 ps_win_ps; + u32 ps_win_sz; + u32 plane_pos; + u32 plane_ctl; +}; + /* shared page(4KB) between gvt and VM, located at the first page next * to MMIO region(2MB size normally). */ struct gvt_shared_page { u32 elsp_data[4]; u32 reg_addr; - u32 rsvd2[0x400 - 5]; + struct pv_plane_update pv_plane; + u32 rsvd2[0x400 - 21]; }; #define VGPU_PVMMIO(vgpu) vgpu_vreg_t(vgpu, vgtif_reg(enable_pvmmio)) @@ -65,6 +89,7 @@ struct gvt_shared_page { */ enum pvmmio_levels { PVMMIO_ELSP_SUBMIT = 0x1, + PVMMIO_PLANE_UPDATE = 0x2, }; /* diff --git a/drivers/gpu/drm/i915/intel_sprite.c b/drivers/gpu/drm/i915/intel_sprite.c index 11b7afc0a0d3..4b89ba3e021c 100644 --- a/drivers/gpu/drm/i915/intel_sprite.c +++ b/drivers/gpu/drm/i915/intel_sprite.c @@ -232,6 +232,68 @@ void intel_pipe_update_end(struct intel_crtc_state *new_crtc_state) #endif } +static void pv_update_plane_reg(struct intel_plane *plane, + u32 stride, uint32_t src_w, uint32_t src_h, + uint32_t crtc_w, uint32_t crtc_h, u32 aux_stride, + const struct intel_crtc_state *crtc_state, + const struct intel_plane_state *plane_state) +{ + int i; + struct pv_plane_update tmp_plane; + uint32_t x = plane_state->main.x; + uint32_t y = plane_state->main.y; + struct drm_i915_private *dev_priv = to_i915(plane->base.dev); + u32 __iomem *pv_plane = (u32 *)&(dev_priv->shared_page->pv_plane); + + memset(&tmp_plane, 0, sizeof(struct pv_plane_update)); + if (IS_GEMINILAKE(dev_priv) || IS_CANNONLAKE(dev_priv)) { + tmp_plane.flags |= PLANE_COLOR_CTL_BIT; + tmp_plane.plane_color_ctl = PLANE_COLOR_PIPE_GAMMA_ENABLE | + PLANE_COLOR_PIPE_CSC_ENABLE | + PLANE_COLOR_PLANE_GAMMA_DISABLE; + } + + if (plane_state->ckey.flags) { + tmp_plane.flags |= PLANE_KEY_BIT; + tmp_plane.plane_key_val = plane_state->ckey.min_value; + tmp_plane.plane_key_max = plane_state->ckey.max_value; + tmp_plane.plane_key_msk = plane_state->ckey.channel_mask; + } + + tmp_plane.plane_offset = (y << 16) | x; + tmp_plane.plane_stride = stride; + tmp_plane.plane_size = (src_h << 16) | src_w; + tmp_plane.plane_aux_dist = + (plane_state->aux.offset - plane_state->main.offset) | + aux_stride; + tmp_plane.plane_aux_offset = + (plane_state->aux.y << 16) | plane_state->aux.x; + + /* program plane scaler */ + if (plane_state->scaler_id >= 0) { + tmp_plane.flags |= PLANE_SCALER_BIT; + tmp_plane.ps_ctrl = PS_SCALER_EN | PS_PLANE_SEL(plane->id) | + crtc_state->scaler_state.scalers[plane_state->scaler_id].mode; + tmp_plane.ps_pwr_gate = 0; + tmp_plane.ps_win_ps = + (plane_state->base.dst.x1 << 16) | plane_state->base.dst.y1; + tmp_plane.ps_win_sz = ((crtc_w + 1) << 16) | (crtc_h + 1); + tmp_plane.plane_pos = 0; + } else { + tmp_plane.plane_pos = + (plane_state->base.dst.y1 << 16) | plane_state->base.dst.x1; + } + + tmp_plane.plane_ctl = plane_state->ctl; + + spin_lock(&dev_priv->shared_page_lock); + for (i = 0; i < sizeof(struct pv_plane_update) / 4; i++) + writel(*((u32 *)(&tmp_plane) + i), pv_plane + i); + I915_WRITE_FW(PLANE_SURF(plane->pipe, plane->id), + intel_plane_ggtt_offset(plane_state) + plane_state->main.offset); + spin_unlock(&dev_priv->shared_page_lock); +} + void skl_update_plane(struct intel_plane *plane, const struct intel_crtc_state *crtc_state, @@ -268,6 +330,12 @@ skl_update_plane(struct intel_plane *plane, crtc_w--; crtc_h--; + if (PVMMIO_LEVEL(dev_priv, PVMMIO_PLANE_UPDATE)) { + pv_update_plane_reg(plane, stride, src_w, src_h, + crtc_w, crtc_h, aux_stride, crtc_state, plane_state); + return; + } + spin_lock_irqsave(&dev_priv->uncore.lock, irqflags); if (INTEL_GEN(dev_priv) >= 10 || IS_GEMINILAKE(dev_priv)) From baed22e0e0971305d77b5f41c51d4798ac0b85bc Mon Sep 17 00:00:00 2001 From: Fei Jiang Date: Fri, 14 Sep 2018 16:10:20 +0800 Subject: [PATCH 0926/1103] drm/i915/gvt: handling pvmmio update of plane registers in GVT-g When pvmmio level PVMMIO_PLANE_UPDATE is enabled, need handle multiple plane related registers updating together when PLANE_SURF is traped. sos only patch. V2: restore sequence of skl_plane_mmio_write/skl_plane_mmio_write and skl_plane_surf_write, while need add extra declarations. Signed-off-by: Fei Jiang Reviewed-by: Min He Reviewed-by: Zhao Yakui --- drivers/gpu/drm/i915/gvt/handlers.c | 64 +++++++++++++++++++++++++++++ 1 file changed, 64 insertions(+) diff --git a/drivers/gpu/drm/i915/gvt/handlers.c b/drivers/gpu/drm/i915/gvt/handlers.c index 14a75c6dff4d..afa15c0bb347 100644 --- a/drivers/gpu/drm/i915/gvt/handlers.c +++ b/drivers/gpu/drm/i915/gvt/handlers.c @@ -778,6 +778,66 @@ static int spr_surf_mmio_write(struct intel_vgpu *vgpu, unsigned int offset, return 0; } +static int skl_plane_mmio_write(struct intel_vgpu *vgpu, unsigned int offset, + void *p_data, unsigned int bytes); +static int skl_ps_mmio_write(struct intel_vgpu *vgpu, unsigned int offset, + void *p_data, unsigned int bytes); + +static void pvmmio_update_plane_register(struct intel_vgpu *vgpu, + unsigned int pipe, unsigned int plane) +{ + struct pv_plane_update *pv_plane = &vgpu->mmio.shared_page->pv_plane; + + /* null function for PLANE_COLOR_CTL, PLANE_AUX_DIST, PLANE_AUX_OFFSET, + * and SKL_PS_PWR_GATE register trap + */ + + if (pv_plane->flags & PLANE_KEY_BIT) { + skl_plane_mmio_write(vgpu, + i915_mmio_reg_offset(PLANE_KEYVAL(pipe, plane)), + &pv_plane->plane_key_val, 4); + skl_plane_mmio_write(vgpu, + i915_mmio_reg_offset(PLANE_KEYMAX(pipe, plane)), + &pv_plane->plane_key_max, 4); + skl_plane_mmio_write(vgpu, + i915_mmio_reg_offset(PLANE_KEYMSK(pipe, plane)), + &pv_plane->plane_key_msk, 4); + } + skl_plane_mmio_write(vgpu, + i915_mmio_reg_offset(PLANE_OFFSET(pipe, plane)), + &pv_plane->plane_offset, 4); + skl_plane_mmio_write(vgpu, + i915_mmio_reg_offset(PLANE_STRIDE(pipe, plane)), + &pv_plane->plane_stride, 4); + skl_plane_mmio_write(vgpu, + i915_mmio_reg_offset(PLANE_SIZE(pipe, plane)), + &pv_plane->plane_size, 4); + skl_plane_mmio_write(vgpu, + i915_mmio_reg_offset(PLANE_AUX_DIST(pipe, plane)), + &pv_plane->plane_aux_dist, 4); + skl_plane_mmio_write(vgpu, + i915_mmio_reg_offset(PLANE_AUX_OFFSET(pipe, plane)), + &pv_plane->plane_aux_offset, 4); + + if (pv_plane->flags & PLANE_SCALER_BIT) { + skl_ps_mmio_write(vgpu, + i915_mmio_reg_offset(SKL_PS_CTRL(pipe, plane)), + &pv_plane->ps_ctrl, 4); + skl_ps_mmio_write(vgpu, + i915_mmio_reg_offset(SKL_PS_WIN_POS(pipe, plane)), + &pv_plane->ps_win_ps, 4); + skl_ps_mmio_write(vgpu, + i915_mmio_reg_offset(SKL_PS_WIN_SZ(pipe, plane)), + &pv_plane->ps_win_sz, 4); + } + skl_plane_mmio_write(vgpu, + i915_mmio_reg_offset(PLANE_POS(pipe, plane)), + &pv_plane->plane_pos, 4); + skl_plane_mmio_write(vgpu, + i915_mmio_reg_offset(PLANE_CTL(pipe, plane)), + &pv_plane->plane_ctl, 4); +} + static int trigger_aux_channel_interrupt(struct intel_vgpu *vgpu, unsigned int reg) { @@ -2850,6 +2910,10 @@ static int skl_plane_surf_write(struct intel_vgpu *vgpu, unsigned int offset, i915_reg_t reg_1ac = _MMIO(_REG_701AC(pipe, plane)); int flip_event = SKL_FLIP_EVENT(pipe, plane); + /* plane disable is not pv and it is indicated by value 0 */ + if (*(u32 *)p_data != 0 && VGPU_PVMMIO(vgpu) & PVMMIO_PLANE_UPDATE) + pvmmio_update_plane_register(vgpu, pipe, plane); + write_vreg(vgpu, offset, p_data, bytes); vgpu_vreg_t(vgpu, reg_1ac) = vgpu_vreg(vgpu, offset); From cf4ab4953c8c6f5123262fefa299e35c9ceeabc4 Mon Sep 17 00:00:00 2001 From: Fei Jiang Date: Mon, 23 Jul 2018 10:51:33 +0800 Subject: [PATCH 0927/1103] drm/i915/gvt: enable plane update pvmmio through enable_pvmmio param plane update pvmmio level is 0x2, need set it in enable_pvmmio for both SOS and UOS kernel driver. Patch for both SOS and UOS. Signed-off-by: Fei Jiang Reviewed-by: Min He Reviewed-by: Zhao Yakui --- drivers/gpu/drm/i915/i915_params.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/i915/i915_params.h b/drivers/gpu/drm/i915/i915_params.h index 358094837650..d9b901f622eb 100644 --- a/drivers/gpu/drm/i915/i915_params.h +++ b/drivers/gpu/drm/i915/i915_params.h @@ -69,7 +69,8 @@ struct drm_printer; param(bool, enable_dp_mst, true) \ param(bool, enable_dpcd_backlight, false) \ param(int, domain_scaler_owner, 0x11100) \ - param(unsigned int, enable_pvmmio, 0) \ + param(unsigned int, enable_pvmmio, \ + PVMMIO_ELSP_SUBMIT | PVMMIO_PLANE_UPDATE) \ param(bool, enable_gvt, false) #define MEMBER(T, member, ...) T member; From 8f263dc55ae18d25cf5e50cdb53cb348f18fb508 Mon Sep 17 00:00:00 2001 From: Fei Jiang Date: Tue, 31 Jul 2018 12:06:29 +0800 Subject: [PATCH 0928/1103] drm/i915/gvt: implement gfn_to_mfn with identical 1:1 mapping check If hypervisor is implementing identical 1:1 memory mapping for sos kernel, sos gpa equals to hpa, so we don't need call hypercall for gfn_to_mfn. Currently this is a hack solution in function is_identical_mmap, later hypervisor will provide one hypercall to let SOS query such kind of info. V2: add error handling, if map_guest_phys fail, still call vhm_gpa2hpa Signed-off-by: Fei Jiang --- drivers/gpu/drm/i915/gvt/acrngt.c | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/i915/gvt/acrngt.c b/drivers/gpu/drm/i915/gvt/acrngt.c index c6fff10a1679..abde541b76f8 100644 --- a/drivers/gpu/drm/i915/gvt/acrngt.c +++ b/drivers/gpu/drm/i915/gvt/acrngt.c @@ -787,13 +787,33 @@ static int acrngt_write_gpa(unsigned long handle, unsigned long gpa, return 0; } +static bool is_identical_mmap(void) +{ + /* todo: need add hypercall to get such info from hypervisor */ + return true; +} + static unsigned long acrngt_gfn_to_pfn(unsigned long handle, unsigned long gfn) { unsigned long hpa; struct acrngt_hvm_dev *info = (struct acrngt_hvm_dev *)handle; + gvt_dbg_core("convert gfn 0x%lx to pfn\n", gfn); + if (is_identical_mmap()) { + void *va = NULL; + + va = map_guest_phys(info->vm_id, gfn << PAGE_SHIFT, + 1 << PAGE_SHIFT); + if (!va) { + gvt_err("GVT: can not map gfn = 0x%lx!!!\n", gfn); + hpa = vhm_vm_gpa2hpa(info->vm_id, gfn << PAGE_SHIFT); + } else { + hpa = virt_to_phys(va); + } + } else { + hpa = vhm_vm_gpa2hpa(info->vm_id, gfn << PAGE_SHIFT); + } - hpa = vhm_vm_gpa2hpa(info->vm_id, gfn << PAGE_SHIFT); return hpa >> PAGE_SHIFT; } From 26d9eee4106ce698de048a7dd07726d2388adb66 Mon Sep 17 00:00:00 2001 From: Fei Jiang Date: Fri, 14 Sep 2018 16:10:20 +0800 Subject: [PATCH 0929/1103] drm/i915/gvt: cached read_gpa optimization in shadow ppgtt update During shadow ppgtt update, we need call intel_gvt_hypervisor_read_gpa every entry for whole ppgtt page, for performance consideration, optmize it by firstly reading whole page ppgtt gpa content into one scratch page, then following read_gpa directly read from scratch page. Have vgpu->ge_cache_enable to control, currently we only cache in ppgtt_populate_shadow_page for pte update case. Signed-off-by: Fei Jiang Reviewed-by: Min He Reviewed-by: Zhao Yakui --- drivers/gpu/drm/i915/gvt/gtt.c | 23 +++++++++++++++++++++++ drivers/gpu/drm/i915/gvt/gvt.h | 3 +++ 2 files changed, 26 insertions(+) diff --git a/drivers/gpu/drm/i915/gvt/gtt.c b/drivers/gpu/drm/i915/gvt/gtt.c index af3b1dea4510..3fbce20268c6 100644 --- a/drivers/gpu/drm/i915/gvt/gtt.c +++ b/drivers/gpu/drm/i915/gvt/gtt.c @@ -303,6 +303,18 @@ static inline int gtt_get_entry64(void *pt, return -EINVAL; if (hypervisor_access) { + if (vgpu->ge_cache_enable && vgpu->cached_guest_entry) { + if (index == 0) { + ret = intel_gvt_hypervisor_read_gpa(vgpu, gpa, + vgpu->cached_guest_entry, + I915_GTT_PAGE_SIZE); + if (WARN_ON(ret)) + return ret; + } + e->val64 = *(vgpu->cached_guest_entry + index); + return 0; + } + ret = intel_gvt_hypervisor_read_gpa(vgpu, gpa + (index << info->gtt_entry_size_shift), &e->val64, 8); @@ -1277,8 +1289,10 @@ static int ppgtt_populate_spt(struct intel_vgpu_ppgtt_spt *spt) trace_spt_change(spt->vgpu->id, "born", spt, spt->guest_page.gfn, spt->shadow_page.type); + vgpu->ge_cache_enable = true; for_each_present_guest_entry(spt, &ge, i) { if (gtt_type_is_pt(get_next_pt_type(ge.type))) { + vgpu->ge_cache_enable = false; s = ppgtt_populate_spt_by_guest_entry(vgpu, &ge); if (IS_ERR(s)) { ret = PTR_ERR(s); @@ -1300,6 +1314,7 @@ static int ppgtt_populate_spt(struct intel_vgpu_ppgtt_spt *spt) goto fail; } } + vgpu->ge_cache_enable = false; return 0; fail: gvt_vgpu_err("fail: shadow page %p guest entry 0x%llx type %d\n", @@ -2428,6 +2443,13 @@ int intel_vgpu_init_gtt(struct intel_vgpu *vgpu) intel_vgpu_reset_ggtt(vgpu, false); + vgpu->cached_guest_entry = kzalloc(I915_GTT_PAGE_SIZE, GFP_KERNEL); + if (!vgpu->cached_guest_entry) { + gvt_vgpu_err("fail to allocate cached_guest_entry page\n"); + return -ENOMEM; + } + vgpu->ge_cache_enable = false; + return create_scratch_page_tree(vgpu); } @@ -2470,6 +2492,7 @@ void intel_vgpu_clean_gtt(struct intel_vgpu *vgpu) { intel_vgpu_destroy_all_ppgtt_mm(vgpu); intel_vgpu_destroy_ggtt_mm(vgpu); + kfree(vgpu->cached_guest_entry); release_scratch_page_tree(vgpu); } diff --git a/drivers/gpu/drm/i915/gvt/gvt.h b/drivers/gpu/drm/i915/gvt/gvt.h index ba88f722d602..1a287ba76923 100644 --- a/drivers/gpu/drm/i915/gvt/gvt.h +++ b/drivers/gpu/drm/i915/gvt/gvt.h @@ -235,6 +235,9 @@ struct intel_vgpu { struct completion vblank_done; u32 scan_nonprivbb; + + unsigned long long *cached_guest_entry; + bool ge_cache_enable; }; /* validating GM healthy status*/ From 6a4cabdc2a488cd57deff07b9eb886b8a530f862 Mon Sep 17 00:00:00 2001 From: Zhao Yan Date: Fri, 14 Sep 2018 16:10:20 +0800 Subject: [PATCH 0930/1103] drm/i915/gvt: add a fastpath for cmd parsing on MI_NOOP MI_NOOP is a common command appearing in almost all command buffers, put it into a fastpath can improve perfomance, especially in command buffers contains lots of MI_NOOPs (0s). Take glmark2 as an example, 3% performance increase is observed after introduced this patch. Meanwhile, in case where abundant in MI_NOOPs, up to 12% performance increase is measured. v2: use lowercase for index of MI_NOOP in cmd_info (zhenyu wang) Signed-off-by: Li Weinan Signed-off-by: Zhao Yan Signed-off-by: Zhenyu Wang Reviewed-by: Zhao Yakui Reviewed-by: He, Min --- drivers/gpu/drm/i915/gvt/cmd_parser.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/i915/gvt/cmd_parser.c b/drivers/gpu/drm/i915/gvt/cmd_parser.c index 95244b2cb679..8d130d4d58b7 100644 --- a/drivers/gpu/drm/i915/gvt/cmd_parser.c +++ b/drivers/gpu/drm/i915/gvt/cmd_parser.c @@ -1840,6 +1840,8 @@ static int cmd_handler_mi_batch_buffer_start(struct parser_exec_state *s) return ret; } +static int mi_noop_index; + static struct cmd_info cmd_info[] = { {"MI_NOOP", OP_MI_NOOP, F_LEN_CONST, R_ALL, D_ALL, 0, 1, NULL}, @@ -2525,7 +2527,12 @@ static int cmd_parser_exec(struct parser_exec_state *s) cmd = cmd_val(s, 0); - info = get_cmd_info(s->vgpu->gvt, cmd, s->ring_id); + /* fastpath for MI_NOOP */ + if (cmd == MI_NOOP) + info = &cmd_info[mi_noop_index]; + else + info = get_cmd_info(s->vgpu->gvt, cmd, s->ring_id); + if (info == NULL) { gvt_vgpu_err("unknown cmd 0x%x, opcode=0x%x, addr_type=%s, ring %d, workload=%p\n", cmd, get_opcode(cmd, s->ring_id), @@ -2955,6 +2962,8 @@ static int init_cmd_table(struct intel_gvt *gvt) kfree(e); return -EEXIST; } + if (cmd_info[i].opcode == OP_MI_NOOP) + mi_noop_index = i; INIT_HLIST_NODE(&e->hlist); add_cmd_entry(gvt, e); From 2db581dae8fbe0bde3ae03be0237ea0d74ee42e3 Mon Sep 17 00:00:00 2001 From: Zhipeng Gong Date: Fri, 14 Sep 2018 16:10:20 +0800 Subject: [PATCH 0931/1103] drm/i915/gvt: notify ppgtt update through g2v This patch extends g2v notification to notify host GVT-g of ppgtt update from guest, including alloc_4lvl, clear_4lv4 and insert_4lvl. It uses shared page to pass the additional params. This patch also add one new pvmmio level to control ppgtt update. This patch is needed for both uos and sos v2: - create a struct for ppggt update in shared page. - use multiple notifications in case insert size is too big. v3: - not pass pd pages, let GVT-g read from guest memory instead. v4: - not change rsvd2 type. v5: - pass cache_level to GVT-g Tracked-On: #874 Signed-off-by: Zhipeng Gong Reviewed-by: He, Min Reviewed-by: Zhao Yakui --- drivers/gpu/drm/i915/i915_gem_gtt.c | 38 +++++++++++++++++++++++++++++ drivers/gpu/drm/i915/i915_pvinfo.h | 14 ++++++++++- 2 files changed, 51 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/i915/i915_gem_gtt.c b/drivers/gpu/drm/i915/i915_gem_gtt.c index 6e792e3167a4..c8c1df7812d7 100644 --- a/drivers/gpu/drm/i915/i915_gem_gtt.c +++ b/drivers/gpu/drm/i915/i915_gem_gtt.c @@ -998,6 +998,8 @@ static void gen8_ppgtt_clear_4lvl(struct i915_address_space *vm, struct i915_pml4 *pml4 = &ppgtt->pml4; struct i915_page_directory_pointer *pdp; unsigned int pml4e; + u64 orig_start = start; + u64 orig_length = length; GEM_BUG_ON(!use_4lvl(vm)); @@ -1011,6 +1013,17 @@ static void gen8_ppgtt_clear_4lvl(struct i915_address_space *vm, free_pdp(vm, pdp); } + + if (PVMMIO_LEVEL(vm->i915, PVMMIO_PPGTT_UPDATE)) { + struct drm_i915_private *dev_priv = vm->i915; + struct pv_ppgtt_update *pv_ppgtt = + &dev_priv->shared_page->pv_ppgtt; + + writeq(px_dma(pml4), &pv_ppgtt->pdp); + writeq(orig_start, &pv_ppgtt->start); + writeq(orig_length, &pv_ppgtt->length); + I915_WRITE(vgtif_reg(g2v_notify), VGT_G2V_PPGTT_L4_CLEAR); + } } static inline struct sgt_dma { @@ -1250,6 +1263,18 @@ static void gen8_ppgtt_insert_4lvl(struct i915_address_space *vm, flags)) GEM_BUG_ON(idx.pml4e >= GEN8_PML4ES_PER_PML4); + if (PVMMIO_LEVEL(vm->i915, PVMMIO_PPGTT_UPDATE)) { + struct drm_i915_private *dev_priv = vm->i915; + struct pv_ppgtt_update *pv_ppgtt = + &dev_priv->shared_page->pv_ppgtt; + + writeq(px_dma(&ppgtt->pml4), &pv_ppgtt->pdp); + writeq(vma->node.start, &pv_ppgtt->start); + writeq(vma->node.size, &pv_ppgtt->length); + writel(cache_level, &pv_ppgtt->cache_level); + I915_WRITE(vgtif_reg(g2v_notify), VGT_G2V_PPGTT_L4_INSERT); + } + vma->page_sizes.gtt = I915_GTT_PAGE_SIZE; } } @@ -1498,6 +1523,8 @@ static int gen8_ppgtt_alloc_4lvl(struct i915_address_space *vm, u64 from = start; u32 pml4e; int ret; + u64 orig_start = start; + u64 orig_length = length; gen8_for_each_pml4e(pdp, pml4, start, length, pml4e) { if (pml4->pdps[pml4e] == vm->scratch_pdp) { @@ -1514,6 +1541,17 @@ static int gen8_ppgtt_alloc_4lvl(struct i915_address_space *vm, goto unwind_pdp; } + if (PVMMIO_LEVEL(vm->i915, PVMMIO_PPGTT_UPDATE)) { + struct drm_i915_private *dev_priv = vm->i915; + struct pv_ppgtt_update *pv_ppgtt = + &dev_priv->shared_page->pv_ppgtt; + + writeq(px_dma(pml4), &pv_ppgtt->pdp); + writeq(orig_start, &pv_ppgtt->start); + writeq(orig_length, &pv_ppgtt->length); + I915_WRITE(vgtif_reg(g2v_notify), VGT_G2V_PPGTT_L4_ALLOC); + } + return 0; unwind_pdp: diff --git a/drivers/gpu/drm/i915/i915_pvinfo.h b/drivers/gpu/drm/i915/i915_pvinfo.h index 740b2da14186..9c76cab07010 100644 --- a/drivers/gpu/drm/i915/i915_pvinfo.h +++ b/drivers/gpu/drm/i915/i915_pvinfo.h @@ -46,6 +46,9 @@ enum vgt_g2v_type { VGT_G2V_PPGTT_L4_PAGE_TABLE_DESTROY, VGT_G2V_EXECLIST_CONTEXT_CREATE, VGT_G2V_EXECLIST_CONTEXT_DESTROY, + VGT_G2V_PPGTT_L4_ALLOC, + VGT_G2V_PPGTT_L4_CLEAR, + VGT_G2V_PPGTT_L4_INSERT, VGT_G2V_MAX, }; @@ -72,6 +75,13 @@ struct pv_plane_update { u32 plane_ctl; }; +struct pv_ppgtt_update { + u64 pdp; + u64 start; + u64 length; + u32 cache_level; +}; + /* shared page(4KB) between gvt and VM, located at the first page next * to MMIO region(2MB size normally). */ @@ -79,7 +89,8 @@ struct gvt_shared_page { u32 elsp_data[4]; u32 reg_addr; struct pv_plane_update pv_plane; - u32 rsvd2[0x400 - 21]; + struct pv_ppgtt_update pv_ppgtt; + u32 rsvd2[0x400 - 30]; }; #define VGPU_PVMMIO(vgpu) vgpu_vreg_t(vgpu, vgtif_reg(enable_pvmmio)) @@ -90,6 +101,7 @@ struct gvt_shared_page { enum pvmmio_levels { PVMMIO_ELSP_SUBMIT = 0x1, PVMMIO_PLANE_UPDATE = 0x2, + PVMMIO_PPGTT_UPDATE = 0x10, }; /* From 323bc935e02a0984a1016ff28f2a41440a2489fd Mon Sep 17 00:00:00 2001 From: Zhipeng Gong Date: Fri, 14 Sep 2018 16:10:21 +0800 Subject: [PATCH 0932/1103] drm/i915/gvt: handle ppgtt update from g2v This patch handles ppgtt update from g2v notification. It read out ppgtt pte entries from guest pte tables page and convert them to host pfns. It creates local ppgtt tables and insert the content pages into the local ppgtt tables directly, which does not track the usage of guest page table and removes the cost of write protection from the original shadow page mechansim. This patch is only for sos. v3: - Not pass pd pages, let GVT-g read from guest memory instead. v4: - fix page walk error. - remove insert_4lvl_sg. v5: - use cache_level from guest. Tracked-On: #874 Signed-off-by: Zhipeng Gong Reviewed-by: He, Min Reviewed-by: Zhao Yakui --- drivers/gpu/drm/i915/gvt/gtt.c | 354 ++++++++++++++++++++++++++++ drivers/gpu/drm/i915/gvt/gtt.h | 11 +- drivers/gpu/drm/i915/gvt/handlers.c | 6 + 3 files changed, 370 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/i915/gvt/gtt.c b/drivers/gpu/drm/i915/gvt/gtt.c index 3fbce20268c6..0c7160a3b925 100644 --- a/drivers/gpu/drm/i915/gvt/gtt.c +++ b/drivers/gpu/drm/i915/gvt/gtt.c @@ -1757,6 +1757,32 @@ static int ppgtt_handle_guest_write_page_table_bytes( return 0; } +static void invalidate_mm_pv(struct intel_vgpu_mm *mm) +{ + struct intel_vgpu *vgpu = mm->vgpu; + struct intel_gvt *gvt = vgpu->gvt; + struct intel_gvt_gtt *gtt = &gvt->gtt; + struct intel_gvt_gtt_pte_ops *ops = gtt->pte_ops; + struct intel_gvt_gtt_entry se; + + if (WARN_ON(mm->ppgtt_mm.root_entry_type != + GTT_TYPE_PPGTT_ROOT_L4_ENTRY)) + return; + + i915_ppgtt_close(&mm->ppgtt_mm.ppgtt->vm); + i915_ppgtt_put(mm->ppgtt_mm.ppgtt); + + ppgtt_get_shadow_root_entry(mm, &se, 0); + if (!ops->test_present(&se)) + return; + trace_spt_guest_change(vgpu->id, "destroy root pointer", + NULL, se.type, se.val64, 0); + se.val64 = 0; + ppgtt_set_shadow_root_entry(mm, &se, 0); + + mm->ppgtt_mm.shadowed = false; +} + static void invalidate_ppgtt_mm(struct intel_vgpu_mm *mm) { struct intel_vgpu *vgpu = mm->vgpu; @@ -1769,6 +1795,11 @@ static void invalidate_ppgtt_mm(struct intel_vgpu_mm *mm) if (!mm->ppgtt_mm.shadowed) return; + if (VGPU_PVMMIO(mm->vgpu) & PVMMIO_PPGTT_UPDATE) { + invalidate_mm_pv(mm); + return; + } + for (index = 0; index < ARRAY_SIZE(mm->ppgtt_mm.shadow_pdps); index++) { ppgtt_get_shadow_root_entry(mm, &se, index); @@ -1786,6 +1817,33 @@ static void invalidate_ppgtt_mm(struct intel_vgpu_mm *mm) mm->ppgtt_mm.shadowed = false; } +static int shadow_mm_pv(struct intel_vgpu_mm *mm) +{ + struct intel_vgpu *vgpu = mm->vgpu; + struct intel_gvt *gvt = vgpu->gvt; + struct intel_gvt_gtt_entry se; + + if (WARN_ON(mm->ppgtt_mm.root_entry_type != + GTT_TYPE_PPGTT_ROOT_L4_ENTRY)) + return -EINVAL; + + mm->ppgtt_mm.ppgtt = i915_ppgtt_create(gvt->dev_priv, NULL); + if (IS_ERR(mm->ppgtt_mm.ppgtt)) { + gvt_vgpu_err("fail to create ppgtt for pdp 0x%llx\n", + px_dma(&mm->ppgtt_mm.ppgtt->pml4)); + return PTR_ERR(mm->ppgtt_mm.ppgtt); + } + + se.type = GTT_TYPE_PPGTT_ROOT_L4_ENTRY; + se.val64 = px_dma(&mm->ppgtt_mm.ppgtt->pml4); + ppgtt_set_shadow_root_entry(mm, &se, 0); + + trace_spt_guest_change(vgpu->id, "populate root pointer", + NULL, se.type, se.val64, 0); + mm->ppgtt_mm.shadowed = true; + + return 0; +} static int shadow_ppgtt_mm(struct intel_vgpu_mm *mm) { @@ -1800,6 +1858,9 @@ static int shadow_ppgtt_mm(struct intel_vgpu_mm *mm) if (mm->ppgtt_mm.shadowed) return 0; + if (VGPU_PVMMIO(mm->vgpu) & PVMMIO_PPGTT_UPDATE) + return shadow_mm_pv(mm); + mm->ppgtt_mm.shadowed = true; for (index = 0; index < ARRAY_SIZE(mm->ppgtt_mm.guest_pdps); index++) { @@ -2788,3 +2849,296 @@ void intel_vgpu_reset_gtt(struct intel_vgpu *vgpu) intel_vgpu_destroy_all_ppgtt_mm(vgpu); intel_vgpu_reset_ggtt(vgpu, true); } + +int intel_vgpu_g2v_pv_ppgtt_alloc_4lvl(struct intel_vgpu *vgpu, + int page_table_level) +{ + struct pv_ppgtt_update *pv_ppgtt = &vgpu->mmio.shared_page->pv_ppgtt; + struct intel_vgpu_mm *mm; + u64 pdps[4] = {pv_ppgtt->pdp, 0, 0, 0}; + int ret = 0; + + if (WARN_ON(page_table_level != 4)) + return -EINVAL; + + gvt_dbg_mm("alloc_4lvl pdp=%llx start=%llx length=%llx\n", + pv_ppgtt->pdp, pv_ppgtt->start, + pv_ppgtt->length); + + mm = intel_vgpu_find_ppgtt_mm(vgpu, pdps); + if (!mm) { + gvt_vgpu_err("failed to find mm for pdp 0x%llx\n", pdps[0]); + ret = -EINVAL; + } else { + ret = mm->ppgtt_mm.ppgtt->vm.allocate_va_range( + &mm->ppgtt_mm.ppgtt->vm, + pv_ppgtt->start, pv_ppgtt->length); + if (ret) + gvt_vgpu_err("failed to alloc for pdp %llx\n", pdps[0]); + } + + return ret; +} + +int intel_vgpu_g2v_pv_ppgtt_clear_4lvl(struct intel_vgpu *vgpu, + int page_table_level) +{ + struct pv_ppgtt_update *pv_ppgtt = &vgpu->mmio.shared_page->pv_ppgtt; + struct intel_vgpu_mm *mm; + u64 pdps[4] = {pv_ppgtt->pdp, 0, 0, 0}; + int ret = 0; + + if (WARN_ON(page_table_level != 4)) + return -EINVAL; + + gvt_dbg_mm("clear_4lvl pdp=%llx start=%llx length=%llx\n", + pv_ppgtt->pdp, pv_ppgtt->start, + pv_ppgtt->length); + + mm = intel_vgpu_find_ppgtt_mm(vgpu, pdps); + if (!mm) { + gvt_vgpu_err("failed to find mm for pdp 0x%llx\n", pdps[0]); + ret = -EINVAL; + } else { + mm->ppgtt_mm.ppgtt->vm.clear_range( + &mm->ppgtt_mm.ppgtt->vm, + pv_ppgtt->start, pv_ppgtt->length); + } + + return ret; +} + +#define GEN8_PML4E_SIZE (1UL << GEN8_PML4E_SHIFT) +#define GEN8_PML4E_SIZE_MASK (~(GEN8_PML4E_SIZE - 1)) +#define GEN8_PDPE_SIZE (1UL << GEN8_PDPE_SHIFT) +#define GEN8_PDPE_SIZE_MASK (~(GEN8_PDPE_SIZE - 1)) +#define GEN8_PDE_SIZE (1UL << GEN8_PDE_SHIFT) +#define GEN8_PDE_SIZE_MASK (~(GEN8_PDE_SIZE - 1)) + +#define pml4_addr_end(addr, end) \ +({ unsigned long __boundary = \ + ((addr) + GEN8_PML4E_SIZE) & GEN8_PML4E_SIZE_MASK; \ + (__boundary < (end)) ? __boundary : (end); \ +}) + +#define pdp_addr_end(addr, end) \ +({ unsigned long __boundary = \ + ((addr) + GEN8_PDPE_SIZE) & GEN8_PDPE_SIZE_MASK; \ + (__boundary < (end)) ? __boundary : (end); \ +}) + +#define pd_addr_end(addr, end) \ +({ unsigned long __boundary = \ + ((addr) + GEN8_PDE_SIZE) & GEN8_PDE_SIZE_MASK; \ + (__boundary < (end)) ? __boundary : (end); \ +}) + +struct ppgtt_walk { + unsigned long *mfns; + int mfn_index; + unsigned long *pt; +}; + +static int walk_pt_range(struct intel_vgpu *vgpu, u64 pt, + u64 start, u64 end, struct ppgtt_walk *walk) +{ + const struct intel_gvt_device_info *info = &vgpu->gvt->device_info; + struct intel_gvt_gtt_gma_ops *gma_ops = vgpu->gvt->gtt.gma_ops; + unsigned long start_index, end_index; + int ret; + int i; + unsigned long mfn, gfn; + + start_index = gma_ops->gma_to_pte_index(start); + end_index = ((end - start) >> PAGE_SHIFT) + start_index; + + gvt_dbg_mm("%s: %llx start=%llx end=%llx start_index=%lx end_index=%lx mfn_index=%x\n", + __func__, pt, start, end, + start_index, end_index, walk->mfn_index); + ret = intel_gvt_hypervisor_read_gpa(vgpu, + (pt & PAGE_MASK) + (start_index << info->gtt_entry_size_shift), + walk->pt + start_index, + (end_index - start_index) << info->gtt_entry_size_shift); + if (ret) { + gvt_vgpu_err("fail to read gpa %llx\n", pt); + return ret; + } + + for (i = start_index; i < end_index; i++) { + gfn = walk->pt[i] >> PAGE_SHIFT; + mfn = intel_gvt_hypervisor_gfn_to_mfn(vgpu, gfn); + if (mfn == INTEL_GVT_INVALID_ADDR) { + gvt_vgpu_err("fail to translate gfn: 0x%lx\n", gfn); + return -ENXIO; + } + walk->mfns[walk->mfn_index++] = mfn << PAGE_SHIFT; + } + + return 0; +} + + +static int walk_pd_range(struct intel_vgpu *vgpu, u64 pd, + u64 start, u64 end, struct ppgtt_walk *walk) +{ + const struct intel_gvt_device_info *info = &vgpu->gvt->device_info; + struct intel_gvt_gtt_gma_ops *gma_ops = vgpu->gvt->gtt.gma_ops; + unsigned long index; + u64 pt, next; + int ret = 0; + + do { + index = gma_ops->gma_to_pde_index(start); + + ret = intel_gvt_hypervisor_read_gpa(vgpu, + (pd & PAGE_MASK) + (index << + info->gtt_entry_size_shift), &pt, 8); + if (ret) + return ret; + next = pd_addr_end(start, end); + gvt_dbg_mm("%s: %llx start=%llx end=%llx next=%llx\n", + __func__, pd, start, end, next); + walk_pt_range(vgpu, pt, start, next, walk); + + start = next; + } while (start != end); + + return ret; +} + + +static int walk_pdp_range(struct intel_vgpu *vgpu, u64 pdp, + u64 start, u64 end, struct ppgtt_walk *walk) +{ + const struct intel_gvt_device_info *info = &vgpu->gvt->device_info; + struct intel_gvt_gtt_gma_ops *gma_ops = vgpu->gvt->gtt.gma_ops; + unsigned long index; + u64 pd, next; + int ret = 0; + + do { + index = gma_ops->gma_to_l4_pdp_index(start); + + ret = intel_gvt_hypervisor_read_gpa(vgpu, + (pdp & PAGE_MASK) + (index << + info->gtt_entry_size_shift), &pd, 8); + if (ret) + return ret; + next = pdp_addr_end(start, end); + gvt_dbg_mm("%s: %llx start=%llx end=%llx next=%llx\n", + __func__, pdp, start, end, next); + + walk_pd_range(vgpu, pd, start, next, walk); + start = next; + } while (start != end); + + return ret; +} + + +static int walk_pml4_range(struct intel_vgpu *vgpu, u64 pml4, + u64 start, u64 end, struct ppgtt_walk *walk) +{ + const struct intel_gvt_device_info *info = &vgpu->gvt->device_info; + struct intel_gvt_gtt_gma_ops *gma_ops = vgpu->gvt->gtt.gma_ops; + unsigned long index; + u64 pdp, next; + int ret = 0; + + do { + index = gma_ops->gma_to_pml4_index(start); + ret = intel_gvt_hypervisor_read_gpa(vgpu, + (pml4 & PAGE_MASK) + (index << + info->gtt_entry_size_shift), &pdp, 8); + if (ret) + return ret; + next = pml4_addr_end(start, end); + gvt_dbg_mm("%s: %llx start=%llx end=%llx next=%llx\n", + __func__, pml4, start, end, next); + + walk_pdp_range(vgpu, pdp, start, next, walk); + start = next; + } while (start != end); + + return ret; +} + +int intel_vgpu_g2v_pv_ppgtt_insert_4lvl(struct intel_vgpu *vgpu, + int page_table_level) +{ + struct pv_ppgtt_update *pv_ppgtt = &vgpu->mmio.shared_page->pv_ppgtt; + struct intel_vgpu_mm *mm; + u64 pdps[4] = {pv_ppgtt->pdp, 0, 0, 0}; + int ret = 0; + u64 start = pv_ppgtt->start; + u64 length = pv_ppgtt->length; + struct sg_table st; + struct scatterlist *sg = NULL; + int num_pages = length >> PAGE_SHIFT; + struct i915_vma vma; + struct ppgtt_walk walk; + int i; + + if (WARN_ON(page_table_level != 4)) + return -EINVAL; + + gvt_dbg_mm("insert_4lvl pml4=%llx start=%llx length=%llx cache=%x\n", + pv_ppgtt->pdp, start, length, pv_ppgtt->cache_level); + + mm = intel_vgpu_find_ppgtt_mm(vgpu, pdps); + if (!mm) { + gvt_vgpu_err("fail to find mm for pml4 0x%llx\n", pdps[0]); + return -EINVAL; + } + + walk.mfn_index = 0; + walk.mfns = NULL; + walk.pt = NULL; + + walk.mfns = kmalloc_array(num_pages, + sizeof(unsigned long), GFP_KERNEL); + if (!walk.mfns) { + ret = -ENOMEM; + goto fail; + } + + walk.pt = (unsigned long *)__get_free_pages(GFP_KERNEL, 0); + if (!walk.pt) { + ret = -ENOMEM; + goto fail; + } + + if (sg_alloc_table(&st, num_pages, GFP_KERNEL)) { + ret = -ENOMEM; + goto fail; + } + + ret = walk_pml4_range(vgpu, pdps[0], start, start + length, &walk); + if (ret) + goto fail_free_sg; + + WARN_ON(num_pages != walk.mfn_index); + + for_each_sg(st.sgl, sg, num_pages, i) { + sg->offset = 0; + sg->length = PAGE_SIZE; + sg_dma_address(sg) = walk.mfns[i]; + sg_dma_len(sg) = PAGE_SIZE; + } + + /* fake vma for insert call*/ + memset(&vma, 0, sizeof(vma)); + vma.node.start = start; + vma.pages = &st; + mm->ppgtt_mm.ppgtt->vm.insert_entries( + &mm->ppgtt_mm.ppgtt->vm, &vma, + pv_ppgtt->cache_level, 0); + +fail_free_sg: + sg_free_table(&st); +fail: + kfree(walk.mfns); + free_page((unsigned long)walk.pt); + + return ret; +} diff --git a/drivers/gpu/drm/i915/gvt/gtt.h b/drivers/gpu/drm/i915/gvt/gtt.h index 7a9b36176efb..6d46a123cb96 100644 --- a/drivers/gpu/drm/i915/gvt/gtt.h +++ b/drivers/gpu/drm/i915/gvt/gtt.h @@ -131,7 +131,7 @@ enum intel_gvt_mm_type { INTEL_GVT_MM_PPGTT, }; -#define GVT_RING_CTX_NR_PDPS GEN8_3LVL_PDPES +#define GVT_RING_CTX_NR_PDPS GEN8_3LVL_PDPES struct intel_vgpu_mm { enum intel_gvt_mm_type type; @@ -154,6 +154,7 @@ struct intel_vgpu_mm { struct list_head list; struct list_head lru_list; + struct i915_hw_ppgtt *ppgtt; } ppgtt_mm; struct { void *virtual_ggtt; @@ -272,4 +273,12 @@ int intel_vgpu_emulate_ggtt_mmio_read(struct intel_vgpu *vgpu, int intel_vgpu_emulate_ggtt_mmio_write(struct intel_vgpu *vgpu, unsigned int off, void *p_data, unsigned int bytes); +int intel_vgpu_g2v_pv_ppgtt_alloc_4lvl(struct intel_vgpu *vgpu, + int page_table_level); + +int intel_vgpu_g2v_pv_ppgtt_clear_4lvl(struct intel_vgpu *vgpu, + int page_table_level); + +int intel_vgpu_g2v_pv_ppgtt_insert_4lvl(struct intel_vgpu *vgpu, + int page_table_level); #endif /* _GVT_GTT_H_ */ diff --git a/drivers/gpu/drm/i915/gvt/handlers.c b/drivers/gpu/drm/i915/gvt/handlers.c index afa15c0bb347..d1870e0a97f4 100644 --- a/drivers/gpu/drm/i915/gvt/handlers.c +++ b/drivers/gpu/drm/i915/gvt/handlers.c @@ -1271,6 +1271,12 @@ static int handle_g2v_notification(struct intel_vgpu *vgpu, int notification) case VGT_G2V_PPGTT_L3_PAGE_TABLE_DESTROY: case VGT_G2V_PPGTT_L4_PAGE_TABLE_DESTROY: return intel_vgpu_put_ppgtt_mm(vgpu, pdps); + case VGT_G2V_PPGTT_L4_ALLOC: + return intel_vgpu_g2v_pv_ppgtt_alloc_4lvl(vgpu, 4); + case VGT_G2V_PPGTT_L4_INSERT: + return intel_vgpu_g2v_pv_ppgtt_insert_4lvl(vgpu, 4); + case VGT_G2V_PPGTT_L4_CLEAR: + return intel_vgpu_g2v_pv_ppgtt_clear_4lvl(vgpu, 4); case VGT_G2V_EXECLIST_CONTEXT_CREATE: case VGT_G2V_EXECLIST_CONTEXT_DESTROY: case 1: /* Remove this in guest driver. */ From a9753ece248b792edd9fc687a9cb4df84bbcfb44 Mon Sep 17 00:00:00 2001 From: Zhipeng Gong Date: Tue, 10 Jul 2018 15:29:22 +0800 Subject: [PATCH 0933/1103] drm/i915/gvt: enable pv ppgtt update by default This patch enables pv ppgtt update by default. It is needed for both uos and sos. Tracked-On: #874 Signed-off-by: Zhipeng Gong Reviewed-by: He, Min Reviewed-by: Zhao Yakui --- drivers/gpu/drm/i915/i915_params.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/i915/i915_params.h b/drivers/gpu/drm/i915/i915_params.h index d9b901f622eb..2e56ea5d7411 100644 --- a/drivers/gpu/drm/i915/i915_params.h +++ b/drivers/gpu/drm/i915/i915_params.h @@ -70,7 +70,8 @@ struct drm_printer; param(bool, enable_dpcd_backlight, false) \ param(int, domain_scaler_owner, 0x11100) \ param(unsigned int, enable_pvmmio, \ - PVMMIO_ELSP_SUBMIT | PVMMIO_PLANE_UPDATE) \ + PVMMIO_ELSP_SUBMIT | PVMMIO_PLANE_UPDATE \ + | PVMMIO_PPGTT_UPDATE) \ param(bool, enable_gvt, false) #define MEMBER(T, member, ...) T member; From 29d2794a708241c30db4ea600e3fc65985aada8a Mon Sep 17 00:00:00 2001 From: Fei Jiang Date: Wed, 29 Aug 2018 11:49:44 +0800 Subject: [PATCH 0934/1103] drm/i915/gvt: pvmmio optimization for plane wm register update It is performance optimization to reduce plane wm related register trap counter. When update plane wm, multiple plane wm related registers are updated together, optimize it to firstly cache all register values in share page, then only PLANE_NV12_BUF_CFG register writing is trapped. Plane pvmmio level is PVMMIO_PLANE_WM_UPDATE. If plane restriction feature is enabled, trap handlers for plane wm related register are null, then directly return. Patch for both SOS and UOS. V2: when plane restriction feature is enabled, SOS trap handlers for plane wm related registers are null, then don't trap Signed-off-by: Fei Jiang Reviewed-by: Min He Reviewed-by: Zhao Yakui --- drivers/gpu/drm/i915/i915_pvinfo.h | 11 ++++- drivers/gpu/drm/i915/intel_pm.c | 79 ++++++++++++++++++++++++++++++ 2 files changed, 89 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/i915/i915_pvinfo.h b/drivers/gpu/drm/i915/i915_pvinfo.h index 9c76cab07010..82ab32e87805 100644 --- a/drivers/gpu/drm/i915/i915_pvinfo.h +++ b/drivers/gpu/drm/i915/i915_pvinfo.h @@ -75,6 +75,13 @@ struct pv_plane_update { u32 plane_ctl; }; +struct pv_plane_wm_update { + u32 max_wm_level; + u32 plane_wm_level[8]; + u32 plane_trans_wm_level; + u32 plane_buf_cfg; +}; + struct pv_ppgtt_update { u64 pdp; u64 start; @@ -89,8 +96,9 @@ struct gvt_shared_page { u32 elsp_data[4]; u32 reg_addr; struct pv_plane_update pv_plane; + struct pv_plane_wm_update pv_plane_wm; struct pv_ppgtt_update pv_ppgtt; - u32 rsvd2[0x400 - 30]; + u32 rsvd2[0x400 - 40]; }; #define VGPU_PVMMIO(vgpu) vgpu_vreg_t(vgpu, vgtif_reg(enable_pvmmio)) @@ -101,6 +109,7 @@ struct gvt_shared_page { enum pvmmio_levels { PVMMIO_ELSP_SUBMIT = 0x1, PVMMIO_PLANE_UPDATE = 0x2, + PVMMIO_PLANE_WM_UPDATE = 0x4, PVMMIO_PPGTT_UPDATE = 0x10, }; diff --git a/drivers/gpu/drm/i915/intel_pm.c b/drivers/gpu/drm/i915/intel_pm.c index 01d0f4d3653a..affd3e821687 100644 --- a/drivers/gpu/drm/i915/intel_pm.c +++ b/drivers/gpu/drm/i915/intel_pm.c @@ -4961,6 +4961,70 @@ static void skl_write_wm_level(struct drm_i915_private *dev_priv, I915_WRITE(reg, val); } +static void skl_pv_write_wm_level(u32 *plane_wm_level, + const struct skl_wm_level *level) +{ + uint32_t val = 0; + + if (level->plane_en) { + val |= PLANE_WM_EN; + val |= level->plane_res_b; + val |= level->plane_res_l << PLANE_WM_LINES_SHIFT; + } + + *plane_wm_level = val; +} + +static void skl_pv_ddb_entry_write(u32 *plane_cfg, + const struct skl_ddb_entry *entry) +{ + if (entry->end) + *plane_cfg = (entry->end - 1) << 16 | entry->start; + else + *plane_cfg = 0; +} + +static void skl_pv_write_plane_wm(struct intel_crtc *intel_crtc, + const struct skl_plane_wm *wm, + const struct skl_ddb_allocation *ddb, + enum plane_id plane_id) +{ + int i, level; + struct pv_plane_wm_update tmp_plane_wm; + struct drm_i915_private *dev_priv = to_i915(intel_crtc->base.dev); + int max_level = ilk_wm_max_level(dev_priv); + u32 __iomem *pv_plane_wm = (u32 *)&(dev_priv->shared_page->pv_plane_wm); + enum pipe pipe = intel_crtc->pipe; + + memset(&tmp_plane_wm, 0, sizeof(struct pv_plane_wm_update)); + tmp_plane_wm.max_wm_level = max_level; + for (level = 0; level <= max_level; level++) { + skl_pv_write_wm_level(&tmp_plane_wm.plane_wm_level[level], + &wm->wm[level]); + } + skl_pv_write_wm_level(&tmp_plane_wm.plane_trans_wm_level, + &wm->trans_wm); + + if (wm->is_planar) { + skl_pv_ddb_entry_write(&tmp_plane_wm.plane_buf_cfg, + &ddb->uv_plane[pipe][plane_id]); + } else { + skl_pv_ddb_entry_write(&tmp_plane_wm.plane_buf_cfg, + &ddb->plane[pipe][plane_id]); + } + + spin_lock(&dev_priv->shared_page_lock); + for (i = 0; i < sizeof(struct pv_plane_wm_update) / 4; i++) + writel(*((u32 *)(&tmp_plane_wm) + i), pv_plane_wm + i); + if (wm->is_planar) + skl_ddb_entry_write(dev_priv, + PLANE_NV12_BUF_CFG(pipe, plane_id), + &ddb->plane[pipe][plane_id]); + else + I915_WRITE(PLANE_NV12_BUF_CFG(pipe, plane_id), 0x0); + spin_unlock(&dev_priv->shared_page_lock); +} + static void skl_write_plane_wm(struct intel_crtc *intel_crtc, const struct skl_plane_wm *wm, const struct skl_ddb_allocation *ddb, @@ -4972,6 +5036,21 @@ static void skl_write_plane_wm(struct intel_crtc *intel_crtc, int level, max_level = ilk_wm_max_level(dev_priv); enum pipe pipe = intel_crtc->pipe; + if (INTEL_GEN(dev_priv) < 11) { + /* + * when plane restriction feature is enabled, + * sos trap handlers for plane wm related registers are null + */ + /* TODO: uncomment when plane restriction feature is enabled */ +#if 0 + if (i915_modparams.avail_planes_per_pipe) + return; +#endif + if (PVMMIO_LEVEL(dev_priv, PVMMIO_PLANE_WM_UPDATE)) + return skl_pv_write_plane_wm(intel_crtc, wm, + ddb, plane_id); + } + for (level = 0; level <= max_level; level++) { skl_write_wm_level(dev_priv, PLANE_WM(pipe, plane_id, level), &wm->wm[level]); From be90ba594bf1e52409769b0c89e2ab69fa375d36 Mon Sep 17 00:00:00 2001 From: Fei Jiang Date: Fri, 14 Sep 2018 16:10:21 +0800 Subject: [PATCH 0935/1103] drm/i915/gvt: handling pvmmio update of plane wm registers in GVT-g When pvmmio level PVMMIO_PLANE_WM_UPDATE is enabled, need handle multiple plane wm related registers updating when PLANE_NV12_BUF_CFG writing is traped. sos only patch. Signed-off-by: Fei Jiang Reviewed-by: Min He Reviewed-by: Zhao Yakui --- drivers/gpu/drm/i915/gvt/handlers.c | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/i915/gvt/handlers.c b/drivers/gpu/drm/i915/gvt/handlers.c index d1870e0a97f4..16e2d41174bd 100644 --- a/drivers/gpu/drm/i915/gvt/handlers.c +++ b/drivers/gpu/drm/i915/gvt/handlers.c @@ -2947,6 +2947,29 @@ static int skl_plane_mmio_write(struct intel_vgpu *vgpu, unsigned int offset, return 0; } +static int pv_plane_wm_mmio_write(struct intel_vgpu *vgpu, unsigned int offset, + void *p_data, unsigned int bytes) +{ + unsigned int pipe = SKL_PLANE_REG_TO_PIPE(offset); + unsigned int plane = SKL_PLANE_REG_TO_PLANE(offset); + struct pv_plane_wm_update *pv_plane_wm = + &vgpu->mmio.shared_page->pv_plane_wm; + int level; + + if (VGPU_PVMMIO(vgpu) & PVMMIO_PLANE_WM_UPDATE) { + for (level = 0; level <= pv_plane_wm->max_wm_level; level++) + skl_plane_mmio_write(vgpu, + i915_mmio_reg_offset( + PLANE_WM(pipe, plane, level)), + &pv_plane_wm->plane_wm_level[level], 4); + skl_plane_mmio_write(vgpu, + i915_mmio_reg_offset(PLANE_WM_TRANS(pipe, plane)), + &pv_plane_wm->plane_trans_wm_level, 4); + /* null function for PLANE_BUF_CFG and PLANE_NV12_BUF_CFG */ + } + return 0; +} + #define MMIO_PIPES_SDH(prefix, plane, s, d, r, w) do { \ int pipe; \ for_each_pipe(dev_priv, pipe) \ @@ -3091,7 +3114,8 @@ static int init_skl_mmio_info(struct intel_gvt *gvt) MMIO_PLANES_SDH(PLANE_WM_BASE, 4 * 8, D_SKL_PLUS, NULL, skl_plane_mmio_write); MMIO_PLANES_DH(PLANE_WM_TRANS, D_SKL_PLUS, NULL, skl_plane_mmio_write); - MMIO_PLANES_DH(PLANE_NV12_BUF_CFG, D_SKL_PLUS, NULL, NULL); + MMIO_PLANES_DH(PLANE_NV12_BUF_CFG, D_SKL_PLUS, NULL, + pv_plane_wm_mmio_write); MMIO_PLANES_DH(PLANE_BUF_CFG, D_SKL_PLUS, NULL, NULL); MMIO_DH(CUR_BUF_CFG(PIPE_A), D_SKL_PLUS, NULL, NULL); From dd3a3908415389757720119ef255179bfd8d4ee9 Mon Sep 17 00:00:00 2001 From: Fei Jiang Date: Wed, 29 Aug 2018 12:08:45 +0800 Subject: [PATCH 0936/1103] drm/i915/gvt: enable plane wm pvmmio level through enable_pvmmio param plane wm update pvmmio level is 0x4, need set it in enable_pvmmio for both SOS and UOS kernel driver. Patch for both SOS and UOS. V2: use PVMMIO_PLANE_WM_UPDATE bit definition to improve readability Signed-off-by: Fei Jiang Reviewed-by: Min He Reviewed-by: Zhao Yakui --- drivers/gpu/drm/i915/i915_params.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/i915/i915_params.h b/drivers/gpu/drm/i915/i915_params.h index 2e56ea5d7411..28769e6b6837 100644 --- a/drivers/gpu/drm/i915/i915_params.h +++ b/drivers/gpu/drm/i915/i915_params.h @@ -71,7 +71,7 @@ struct drm_printer; param(int, domain_scaler_owner, 0x11100) \ param(unsigned int, enable_pvmmio, \ PVMMIO_ELSP_SUBMIT | PVMMIO_PLANE_UPDATE \ - | PVMMIO_PPGTT_UPDATE) \ + | PVMMIO_PLANE_WM_UPDATE | PVMMIO_PPGTT_UPDATE) \ param(bool, enable_gvt, false) #define MEMBER(T, member, ...) T member; From 066a90f76bed437e15386a291d7a04b994034a91 Mon Sep 17 00:00:00 2001 From: Zhipeng Gong Date: Fri, 14 Sep 2018 16:10:21 +0800 Subject: [PATCH 0937/1103] drm/i915/gvt: notify global gtt update through g2v This patch extends g2v notification to notify host GVT-g of ggtt update from guest, including ggtt_insert_entries and ggtt_clear_range. This patch also add one new pvmmio level to control ggtt update. This patch is needed for both uos and sos. v2: - calculate num_entries from gtt_entries. v3: - pass cache_level to GVT-g - consolidate the same code into a function. Tracked-On: projectacrn/acrn-hypervisor#994 Signed-off-by: Zhipeng Gong Reviewed-by: He, Min --- drivers/gpu/drm/i915/i915_gem_gtt.c | 40 +++++++++++++++++++++++++++++ drivers/gpu/drm/i915/i915_pvinfo.h | 12 ++++++++- 2 files changed, 51 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/i915/i915_gem_gtt.c b/drivers/gpu/drm/i915/i915_gem_gtt.c index c8c1df7812d7..280095010286 100644 --- a/drivers/gpu/drm/i915/i915_gem_gtt.c +++ b/drivers/gpu/drm/i915/i915_gem_gtt.c @@ -2503,6 +2503,17 @@ static void gen8_set_pte(void __iomem *addr, gen8_pte_t pte) writeq(pte, addr); } +static void vgpu_ggtt_insert(struct drm_i915_private *dev_priv, + u64 start, int num_entries, enum i915_cache_level level) +{ + struct gvt_shared_page *shared_page = dev_priv->shared_page; + + writeq(start, &shared_page->pv_ggtt.start); + writeq(num_entries, &shared_page->pv_ggtt.length); + writel(level, &shared_page->pv_ggtt.cache_level); + I915_WRITE(vgtif_reg(g2v_notify), VGT_G2V_GGTT_INSERT); +} + static void gen8_ggtt_insert_page(struct i915_address_space *vm, dma_addr_t addr, u64 offset, @@ -2515,6 +2526,11 @@ static void gen8_ggtt_insert_page(struct i915_address_space *vm, gen8_set_pte(pte, gen8_pte_encode(addr, level, 0)); + if (PVMMIO_LEVEL(vm->i915, PVMMIO_GGTT_UPDATE)) { + vgpu_ggtt_insert(vm->i915, offset, 1, level); + return; + } + ggtt->invalidate(vm->i915); } @@ -2539,6 +2555,20 @@ static void gen8_ggtt_insert_entries(struct i915_address_space *vm, for_each_sgt_dma(addr, sgt_iter, vma->pages) gen8_set_pte(gtt_entries++, pte_encode | addr); + if (PVMMIO_LEVEL(vm->i915, PVMMIO_GGTT_UPDATE)) { + int num_entries = gtt_entries - + ((gen8_pte_t __iomem *)ggtt->gsm + + (vma->node.start >> PAGE_SHIFT)); + /* + * Sometimes number of entries does not match vma node size. + * Pass number of pte entries instead. + */ + vgpu_ggtt_insert(vm->i915, vma->node.start, + num_entries, level); + return; + } + + /* * We want to flush the TLBs only after we're certain all the PTE * updates have finished. @@ -2612,6 +2642,16 @@ static void gen8_ggtt_clear_range(struct i915_address_space *vm, for (i = 0; i < num_entries; i++) gen8_set_pte(>t_base[i], scratch_pte); + + if (PVMMIO_LEVEL(vm->i915, PVMMIO_GGTT_UPDATE)) { + struct drm_i915_private *dev_priv = vm->i915; + struct gvt_shared_page *shared_page = dev_priv->shared_page; + + writeq(start, &shared_page->pv_ggtt.start); + writeq(length, &shared_page->pv_ggtt.length); + I915_WRITE(vgtif_reg(g2v_notify), VGT_G2V_GGTT_CLEAR); + } + } static void bxt_vtd_ggtt_wa(struct i915_address_space *vm) diff --git a/drivers/gpu/drm/i915/i915_pvinfo.h b/drivers/gpu/drm/i915/i915_pvinfo.h index 82ab32e87805..c15d4578bb5f 100644 --- a/drivers/gpu/drm/i915/i915_pvinfo.h +++ b/drivers/gpu/drm/i915/i915_pvinfo.h @@ -49,6 +49,8 @@ enum vgt_g2v_type { VGT_G2V_PPGTT_L4_ALLOC, VGT_G2V_PPGTT_L4_CLEAR, VGT_G2V_PPGTT_L4_INSERT, + VGT_G2V_GGTT_INSERT, + VGT_G2V_GGTT_CLEAR, VGT_G2V_MAX, }; @@ -89,6 +91,12 @@ struct pv_ppgtt_update { u32 cache_level; }; +struct pv_ggtt_update { + u64 start; + u64 length; + u32 cache_level; +}; + /* shared page(4KB) between gvt and VM, located at the first page next * to MMIO region(2MB size normally). */ @@ -98,7 +106,8 @@ struct gvt_shared_page { struct pv_plane_update pv_plane; struct pv_plane_wm_update pv_plane_wm; struct pv_ppgtt_update pv_ppgtt; - u32 rsvd2[0x400 - 40]; + struct pv_ggtt_update pv_ggtt; + u32 rsvd2[0x400 - 46]; }; #define VGPU_PVMMIO(vgpu) vgpu_vreg_t(vgpu, vgtif_reg(enable_pvmmio)) @@ -111,6 +120,7 @@ enum pvmmio_levels { PVMMIO_PLANE_UPDATE = 0x2, PVMMIO_PLANE_WM_UPDATE = 0x4, PVMMIO_PPGTT_UPDATE = 0x10, + PVMMIO_GGTT_UPDATE = 0x20, }; /* From be359370b30f4eae4f5c7e7055e60d154e456f1e Mon Sep 17 00:00:00 2001 From: Zhipeng Gong Date: Fri, 14 Sep 2018 16:10:21 +0800 Subject: [PATCH 0938/1103] drm/i915/gvt: handle global gtt update from g2v This patch handles ggtt update from g2v notification. It maps the physical pages behind virtual page table to guest, so guest can update its pte entries directly to avoid mmio trap. Then guest ggtt pte entries are converted to host pte entries and inserted into host gtt table. The tricky part is that pvmmio parameter detection is later than virtual page trable creation and pci bar address update, So the map ggtt mmio is done during pvmmio param detection. This patch is only for sos. v2: - cut invalid range. - release gfn to mfn mapping when free gtt. v3: - call ggtt insert_entries function - add size check in validate_ggtt_range - rename trap to map V4: Fall back to disable ggtt PV when it fails to alloc 2M pages for GGTT pV. Tracked-On: projectacrn/acrn-hypervisor#994 Signed-off-by: Zhipeng Gong Reviewed-by: He, Min --- drivers/gpu/drm/i915/gvt/cfg_space.c | 41 +++++ drivers/gpu/drm/i915/gvt/gtt.c | 261 ++++++++++++++++++++++++++- drivers/gpu/drm/i915/gvt/gtt.h | 5 + drivers/gpu/drm/i915/gvt/gvt.h | 3 + drivers/gpu/drm/i915/gvt/handlers.c | 15 ++ 5 files changed, 320 insertions(+), 5 deletions(-) diff --git a/drivers/gpu/drm/i915/gvt/cfg_space.c b/drivers/gpu/drm/i915/gvt/cfg_space.c index f6bcfcb571b7..fd461db48a8d 100644 --- a/drivers/gpu/drm/i915/gvt/cfg_space.c +++ b/drivers/gpu/drm/i915/gvt/cfg_space.c @@ -138,6 +138,47 @@ static int map_aperture(struct intel_vgpu *vgpu, bool map) return 0; } +int map_gttmmio(struct intel_vgpu *vgpu, bool map) +{ + struct intel_vgpu_gm *gm = &vgpu->gm; + unsigned long mfn; + struct scatterlist *sg; + struct sg_table *st = gm->st; + u64 start, end; + int ret = 0; + + if (!st) { + DRM_INFO("no scatter list, fallback to disable ggtt pv\n"); + return -EINVAL; + } + + start = *(u64 *)(vgpu_cfg_space(vgpu) + PCI_BASE_ADDRESS_0); + start &= ~GENMASK(3, 0); + start += vgpu->cfg_space.bar[INTEL_GVT_PCI_BAR_GTTMMIO].size >> 1; + + end = start + + (vgpu->cfg_space.bar[INTEL_GVT_PCI_BAR_GTTMMIO].size >> 1); + + WARN_ON((end - start) != gvt_ggtt_sz(vgpu->gvt)); + + gvt_dbg_mmio("%s start=%llx end=%llx map=%d\n", + __func__, start, end, map); + + start >>= PAGE_SHIFT; + for (sg = st->sgl; sg; sg = __sg_next(sg)) { + mfn = page_to_pfn(sg_page(sg)); + gvt_dbg_mmio("page=%p mfn=%lx size=%x start=%llx\n", + sg_page(sg), mfn, sg->length, start); + ret = intel_gvt_hypervisor_map_gfn_to_mfn(vgpu, start, + mfn, sg->length >> PAGE_SHIFT, map); + if (ret) + return ret; + start += sg->length >> PAGE_SHIFT; + } + + return ret; +} + static int trap_gttmmio(struct intel_vgpu *vgpu, bool trap) { u64 start, end; diff --git a/drivers/gpu/drm/i915/gvt/gtt.c b/drivers/gpu/drm/i915/gvt/gtt.c index 0c7160a3b925..22c79df59d65 100644 --- a/drivers/gpu/drm/i915/gvt/gtt.c +++ b/drivers/gpu/drm/i915/gvt/gtt.c @@ -1569,6 +1569,106 @@ int intel_vgpu_sync_oos_pages(struct intel_vgpu *vgpu) return 0; } +static void free_ggtt_virtual_page_table(struct intel_vgpu_mm *mm) +{ + struct intel_vgpu_gm *gm = &mm->vgpu->gm; + struct sg_table *st = gm->st; + struct scatterlist *sg; + + for (sg = st->sgl; sg; sg = __sg_next(sg)) { + if (sg_page(sg)) + __free_pages(sg_page(sg), get_order(sg->length)); + } + + sg_free_table(st); + kfree(st); + vunmap(mm->ggtt_mm.virtual_ggtt); + gm->st = NULL; +} + +/* + * Alloc virtual page table for guest ggtt. If ggtt pv enabled, the + * physical pages behind virtual page table is also mapped to guest, + * guest can update its pte entries directly to avoid trap. + */ +static void *alloc_ggtt_virtual_page_table(struct intel_vgpu_mm *mm) +{ + struct intel_vgpu *vgpu = mm->vgpu; + unsigned int page_count = gvt_ggtt_sz(vgpu->gvt) >> PAGE_SHIFT; + struct intel_vgpu_gm *gm = &vgpu->gm; + struct page **pages = NULL; + struct page *p; + unsigned int i; + void *vaddr = NULL; + int order; + struct sg_table *st; + struct scatterlist *sg; + struct sgt_iter sgt_iter; + unsigned int npages = page_count; + + /* + * page_table_entry_size is bigger than the size alloc_pages can + * allocate, We have to split it according to the PMD size (2M). + * Head page is kept in scatter list so that we can free them later. + */ + order = get_order(1 << PMD_SHIFT); + + st = kmalloc(sizeof(*st), GFP_KERNEL); + if (!st) + return ERR_PTR(-ENOMEM); + + if (sg_alloc_table(st, page_count, GFP_KERNEL)) { + kfree(st); + return ERR_PTR(-ENOMEM); + } + + sg = st->sgl; + st->nents = 0; + gm->st = st; + do { + p = alloc_pages(GFP_KERNEL, order); + if (!p) + goto fail; + gvt_dbg_mm("page=%p size=%ld\n", p, PAGE_SIZE << order); + sg_set_page(sg, p, PAGE_SIZE << order, 0); + st->nents++; + npages -= 1 << order; + if (!npages) { + sg_mark_end(sg); + break; + } + sg = __sg_next(sg); + } while (1); + + + /* keep all the pages for vmap */ + pages = kmalloc_array(page_count, sizeof(struct page *), GFP_KERNEL); + if (!pages) + goto fail; + + i = 0; + for_each_sgt_page(p, sgt_iter, st) + pages[i++] = p; + + WARN_ON(i != page_count); + + vaddr = vmap(pages, page_count, VM_MAP, PAGE_KERNEL); + if (!vaddr) { + gvt_vgpu_err("fail to vmap pages"); + goto fail; + } + kfree(pages); + return vaddr; + +fail: + sg_set_page(sg, NULL, 0, 0); + sg_mark_end(sg); + free_ggtt_virtual_page_table(mm); + kfree(pages); + gm->st = NULL; + return NULL; +} + /* * The heart of PPGTT shadow page table. */ @@ -1963,7 +2063,6 @@ struct intel_vgpu_mm *intel_vgpu_create_ppgtt_mm(struct intel_vgpu *vgpu, static struct intel_vgpu_mm *intel_vgpu_create_ggtt_mm(struct intel_vgpu *vgpu) { struct intel_vgpu_mm *mm; - unsigned long nr_entries; mm = vgpu_alloc_mm(vgpu); if (!mm) @@ -1971,10 +2070,17 @@ static struct intel_vgpu_mm *intel_vgpu_create_ggtt_mm(struct intel_vgpu *vgpu) mm->type = INTEL_GVT_MM_GGTT; - nr_entries = gvt_ggtt_gm_sz(vgpu->gvt) >> I915_GTT_PAGE_SHIFT; - mm->ggtt_mm.virtual_ggtt = - vzalloc(array_size(nr_entries, + mm->ggtt_mm.virtual_ggtt = alloc_ggtt_virtual_page_table(mm); + if (!mm->ggtt_mm.virtual_ggtt) { + unsigned long nr_entries; + + DRM_INFO("fail to alloc contiguous pages, fallback\n"); + nr_entries = gvt_ggtt_gm_sz(vgpu->gvt) >> I915_GTT_PAGE_SHIFT; + mm->ggtt_mm.virtual_ggtt = + vzalloc(array_size(nr_entries, vgpu->gvt->device_info.gtt_entry_size)); + } + if (!mm->ggtt_mm.virtual_ggtt) { vgpu_free_mm(mm); return ERR_PTR(-ENOMEM); @@ -2003,7 +2109,17 @@ void _intel_vgpu_mm_release(struct kref *mm_ref) list_del(&mm->ppgtt_mm.lru_list); invalidate_ppgtt_mm(mm); } else { - vfree(mm->ggtt_mm.virtual_ggtt); + if (mm->ggtt_mm.virtual_ggtt) { + struct intel_vgpu *vgpu = mm->vgpu; + struct intel_vgpu_gm *gm = &vgpu->gm; + + if (gm->st) { + map_gttmmio(mm->vgpu, false); + free_ggtt_virtual_page_table(mm); + } else + vfree(mm->ggtt_mm.virtual_ggtt); + mm->ggtt_mm.virtual_ggtt = NULL; + } mm->ggtt_mm.last_partial_off = -1UL; } @@ -3142,3 +3258,138 @@ int intel_vgpu_g2v_pv_ppgtt_insert_4lvl(struct intel_vgpu *vgpu, return ret; } + +static void validate_ggtt_range(struct intel_vgpu *vgpu, + u64 *start, u64 *length) +{ + u64 end; + + if (WARN_ON(*start > vgpu->gvt->dev_priv->ggtt.vm.total || + *length > vgpu->gvt->dev_priv->ggtt.vm.total)) { + *length = 0; + return; + } + + end = *start + *length - 1; + + if (*start >= vgpu_aperture_gmadr_base(vgpu) && + end <= vgpu_aperture_gmadr_end(vgpu)) + return; + + if (*start >= vgpu_hidden_gmadr_base(vgpu) && + end <= vgpu_hidden_gmadr_end(vgpu)) + return; + + /* handle the cases with invalid ranges */ + WARN_ON(1); + + /* start is in aperture range, end is after apeture range */ + if (*start >= vgpu_aperture_gmadr_base(vgpu) && + *start <= vgpu_aperture_gmadr_end(vgpu)) { + *length = vgpu_aperture_gmadr_end(vgpu) - *start + 1; + return; + } + + /* start is before aperture range, end is in apeture range */ + if (end >= vgpu_aperture_gmadr_base(vgpu) && + end <= vgpu_aperture_gmadr_end(vgpu)) { + *start = vgpu_aperture_gmadr_base(vgpu); + return; + } + + /* start is in hidden range, end is after hidden range */ + if (*start >= vgpu_hidden_gmadr_base(vgpu) && + *start <= vgpu_hidden_gmadr_end(vgpu)) { + *length = vgpu_hidden_gmadr_end(vgpu) - *start + 1; + return; + } + + /* start is before hidden range, end is in hidden range */ + if (end >= vgpu_hidden_gmadr_base(vgpu) && + end <= vgpu_hidden_gmadr_end(vgpu)) { + *start = vgpu_hidden_gmadr_base(vgpu); + return; + } + + /* both start and end are not in valid range*/ + *length = 0; + + return; +} + +int intel_vgpu_g2v_pv_ggtt_insert(struct intel_vgpu *vgpu) +{ + struct intel_vgpu_gtt *gtt = &vgpu->gtt; + struct gvt_shared_page *shared_page = vgpu->mmio.shared_page; + struct drm_i915_private *dev_priv = vgpu->gvt->dev_priv; + struct i915_ggtt *ggtt = &dev_priv->ggtt; + u64 start = shared_page->pv_ggtt.start; + u64 num_entries = shared_page->pv_ggtt.length; + u32 cache_level = shared_page->pv_ggtt.cache_level; + u64 length = num_entries << PAGE_SHIFT; + u64 *vaddr = gtt->ggtt_mm->ggtt_mm.virtual_ggtt; + u64 gtt_entry_index; + u64 gtt_entry; + unsigned long mfn; + struct i915_vma vma; + struct sg_table st; + struct scatterlist *sg = NULL; + int ret = 0; + int i; + + gvt_dbg_mm("ggtt_insert: start=%llx length=%llx cache=%x\n", + start, length, cache_level); + validate_ggtt_range(vgpu, &start, &length); + if (length == 0) + return 0; + + num_entries = length >> PAGE_SHIFT; + + if (sg_alloc_table(&st, num_entries, GFP_KERNEL)) + return -ENOMEM; + + for_each_sg(st.sgl, sg, num_entries, i) { + gtt_entry_index = (start >> PAGE_SHIFT) + i; + gtt_entry = vaddr[gtt_entry_index]; + mfn = intel_gvt_hypervisor_gfn_to_mfn(vgpu, + gtt_entry >> PAGE_SHIFT); + if (mfn == INTEL_GVT_INVALID_ADDR) { + gvt_vgpu_err("fail to translate gfn: 0x%llx\n", + gtt_entry >> PAGE_SHIFT); + ret = -ENXIO; + goto fail; + } + sg->offset = 0; + sg->length = PAGE_SIZE; + sg_dma_address(sg) = mfn << PAGE_SHIFT; + sg_dma_len(sg) = PAGE_SIZE; + } + + /* fake vma for insert call*/ + memset(&vma, 0, sizeof(vma)); + vma.node.start = start; + vma.pages = &st; + ggtt->vm.insert_entries(&ggtt->vm, &vma, cache_level, 0); + +fail: + sg_free_table(&st); + return ret; +} + +int intel_vgpu_g2v_pv_ggtt_clear(struct intel_vgpu *vgpu) +{ + struct gvt_shared_page *shared_page = vgpu->mmio.shared_page; + u64 start = shared_page->pv_ggtt.start; + u64 length = shared_page->pv_ggtt.length; + struct i915_ggtt *ggtt = &vgpu->gvt->dev_priv->ggtt; + + gvt_dbg_mm("ggtt_clear: start=%llx length=%llx\n", + start, length); + validate_ggtt_range(vgpu, &start, &length); + if (length == 0) + return 0; + + ggtt->vm.clear_range(&ggtt->vm, start, length); + + return 0; +} diff --git a/drivers/gpu/drm/i915/gvt/gtt.h b/drivers/gpu/drm/i915/gvt/gtt.h index 6d46a123cb96..7ba7400772b0 100644 --- a/drivers/gpu/drm/i915/gvt/gtt.h +++ b/drivers/gpu/drm/i915/gvt/gtt.h @@ -281,4 +281,9 @@ int intel_vgpu_g2v_pv_ppgtt_clear_4lvl(struct intel_vgpu *vgpu, int intel_vgpu_g2v_pv_ppgtt_insert_4lvl(struct intel_vgpu *vgpu, int page_table_level); + +int intel_vgpu_g2v_pv_ggtt_insert(struct intel_vgpu *vgpu); + +int intel_vgpu_g2v_pv_ggtt_clear(struct intel_vgpu *vgpu); + #endif /* _GVT_GTT_H_ */ diff --git a/drivers/gpu/drm/i915/gvt/gvt.h b/drivers/gpu/drm/i915/gvt/gvt.h index 1a287ba76923..9344293ed692 100644 --- a/drivers/gpu/drm/i915/gvt/gvt.h +++ b/drivers/gpu/drm/i915/gvt/gvt.h @@ -85,6 +85,7 @@ struct intel_gvt_device_info { struct intel_vgpu_gm { u64 aperture_sz; u64 hidden_sz; + struct sg_table *st; struct drm_mm_node low_gm_node; struct drm_mm_node high_gm_node; }; @@ -568,6 +569,8 @@ static inline u64 intel_vgpu_get_bar_gpa(struct intel_vgpu *vgpu, int bar) PCI_BASE_ADDRESS_MEM_MASK; } +int map_gttmmio(struct intel_vgpu *vgpu, bool map); + void intel_vgpu_clean_opregion(struct intel_vgpu *vgpu); int intel_vgpu_init_opregion(struct intel_vgpu *vgpu); int intel_vgpu_opregion_base_write_handler(struct intel_vgpu *vgpu, u32 gpa); diff --git a/drivers/gpu/drm/i915/gvt/handlers.c b/drivers/gpu/drm/i915/gvt/handlers.c index 16e2d41174bd..8fa3bed75d61 100644 --- a/drivers/gpu/drm/i915/gvt/handlers.c +++ b/drivers/gpu/drm/i915/gvt/handlers.c @@ -1277,6 +1277,12 @@ static int handle_g2v_notification(struct intel_vgpu *vgpu, int notification) return intel_vgpu_g2v_pv_ppgtt_insert_4lvl(vgpu, 4); case VGT_G2V_PPGTT_L4_CLEAR: return intel_vgpu_g2v_pv_ppgtt_clear_4lvl(vgpu, 4); + case VGT_G2V_GGTT_INSERT: + return intel_vgpu_g2v_pv_ggtt_insert(vgpu); + break; + case VGT_G2V_GGTT_CLEAR: + return intel_vgpu_g2v_pv_ggtt_clear(vgpu); + break; case VGT_G2V_EXECLIST_CONTEXT_CREATE: case VGT_G2V_EXECLIST_CONTEXT_DESTROY: case 1: /* Remove this in guest driver. */ @@ -1348,6 +1354,15 @@ static int pvinfo_mmio_write(struct intel_vgpu *vgpu, unsigned int offset, vgpu_vreg(vgpu, offset) = 0; break; } + if (vgpu_vreg(vgpu, offset) & PVMMIO_GGTT_UPDATE) { + ret = map_gttmmio(vgpu, true); + if (ret) { + DRM_INFO("ggtt pv mode is off\n"); + vgpu_vreg(vgpu, offset) &= + ~PVMMIO_GGTT_UPDATE; + } + } + } else { vgpu_vreg(vgpu, offset) = 0; } From e81ea17d9c8cf5ec7e59e1535953cb335a007b47 Mon Sep 17 00:00:00 2001 From: Zhipeng Gong Date: Thu, 26 Jul 2018 10:03:02 +0800 Subject: [PATCH 0939/1103] drm/i915/gvt: enable pv global gtt update by default This patch enables pv ggtt update by default. It is needed for both uos and sos. Tracked-On: projectacrn/acrn-hypervisor#994 Signed-off-by: Zhipeng Gong Reviewed-by: He, Min --- drivers/gpu/drm/i915/i915_params.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/i915/i915_params.h b/drivers/gpu/drm/i915/i915_params.h index 28769e6b6837..40aee5b37645 100644 --- a/drivers/gpu/drm/i915/i915_params.h +++ b/drivers/gpu/drm/i915/i915_params.h @@ -71,7 +71,8 @@ struct drm_printer; param(int, domain_scaler_owner, 0x11100) \ param(unsigned int, enable_pvmmio, \ PVMMIO_ELSP_SUBMIT | PVMMIO_PLANE_UPDATE \ - | PVMMIO_PLANE_WM_UPDATE | PVMMIO_PPGTT_UPDATE) \ + | PVMMIO_PLANE_WM_UPDATE | PVMMIO_PPGTT_UPDATE \ + | PVMMIO_GGTT_UPDATE ) \ param(bool, enable_gvt, false) #define MEMBER(T, member, ...) T member; From c2fd70ad8782b7c32fb9a0b3b224f4a1233f1876 Mon Sep 17 00:00:00 2001 From: Zhao Yakui Date: Wed, 5 Sep 2018 15:52:06 +0800 Subject: [PATCH 0940/1103] drm/i915/gvt: Check the state of PVMMIO gtt table to avoid incorrect calling for hypervisor Now hypervisor adds strict checks when the sos tries to add/delete the mapping between gfn and mfn. In such case it should check the state of PVMMIO gtt table to avoid the incorrect hyper call. If the mapping between gfn and mfn is not setup for PVMMIO GTT, it should not remove the mapping. Otherwise the hypervisor will report that the mapping doesn't exist. Tracked-On: https://github.com/projectacrn/acrn-hypervisor/issues/1158 Signed-off-by: Zhao Yakui Reviewed-by: He, Min --- drivers/gpu/drm/i915/gvt/cfg_space.c | 7 +++++++ drivers/gpu/drm/i915/gvt/gtt.h | 3 +++ 2 files changed, 10 insertions(+) diff --git a/drivers/gpu/drm/i915/gvt/cfg_space.c b/drivers/gpu/drm/i915/gvt/cfg_space.c index fd461db48a8d..505e255c4d8a 100644 --- a/drivers/gpu/drm/i915/gvt/cfg_space.c +++ b/drivers/gpu/drm/i915/gvt/cfg_space.c @@ -152,6 +152,11 @@ int map_gttmmio(struct intel_vgpu *vgpu, bool map) return -EINVAL; } + if (vgpu->gtt.ggtt_pv_mapped == map) { + /* If it is already set as the target state, skip it */ + return ret; + } + start = *(u64 *)(vgpu_cfg_space(vgpu) + PCI_BASE_ADDRESS_0); start &= ~GENMASK(3, 0); start += vgpu->cfg_space.bar[INTEL_GVT_PCI_BAR_GTTMMIO].size >> 1; @@ -176,6 +181,8 @@ int map_gttmmio(struct intel_vgpu *vgpu, bool map) start += sg->length >> PAGE_SHIFT; } + vgpu->gtt.ggtt_pv_mapped = map; + return ret; } diff --git a/drivers/gpu/drm/i915/gvt/gtt.h b/drivers/gpu/drm/i915/gvt/gtt.h index 7ba7400772b0..6c0d3bdcaee4 100644 --- a/drivers/gpu/drm/i915/gvt/gtt.h +++ b/drivers/gpu/drm/i915/gvt/gtt.h @@ -199,6 +199,9 @@ struct intel_vgpu_gtt { struct list_head oos_page_list_head; struct list_head post_shadow_list_head; struct intel_vgpu_scratch_pt scratch_pt[GTT_TYPE_MAX]; + + /* indicate whether the PV mapped is enabled for ggtt */ + bool ggtt_pv_mapped; }; extern int intel_vgpu_init_gtt(struct intel_vgpu *vgpu); From 8f8fa88e03a892dd9026f266379b1ee3858854c1 Mon Sep 17 00:00:00 2001 From: Zhipeng Gong Date: Fri, 14 Sep 2018 16:10:22 +0800 Subject: [PATCH 0941/1103] drm/i915/gvt: allocate ddb according to active pipes This patch add back the ddb allocation for the PLANE_CURSOR and allocate ddb according to current active pipes. In this way, this patch can support 4K monitors and fix the issue that cursor not work when enable_initial_modeset is 0. v2: - correct ddb start calculation Tracked-On: projectacrn/acrn-hypervisor#1171 Signed-off-by: Zhipeng Gong Reviewed-by: Zhao Yakui Reviewed-by: Fei Jiang --- drivers/gpu/drm/i915/gvt/gvt.c | 16 ++++++++++------ drivers/gpu/drm/i915/gvt/gvt.h | 2 ++ drivers/gpu/drm/i915/intel_pm.c | 13 +++++++++++-- 3 files changed, 23 insertions(+), 8 deletions(-) diff --git a/drivers/gpu/drm/i915/gvt/gvt.c b/drivers/gpu/drm/i915/gvt/gvt.c index e4a3823e1226..308966edc04a 100644 --- a/drivers/gpu/drm/i915/gvt/gvt.c +++ b/drivers/gpu/drm/i915/gvt/gvt.c @@ -317,22 +317,27 @@ void intel_gvt_init_pipe_info(struct intel_gvt *gvt); * plane information of DomU's planes, so here we statically allocate the * ddb entries for all the possible enabled planes. */ -static void intel_gvt_init_ddb(struct intel_gvt *gvt) +void intel_gvt_allocate_ddb(struct intel_gvt *gvt, + struct skl_ddb_allocation *ddb, unsigned int active_crtcs) { struct drm_i915_private *dev_priv = gvt->dev_priv; - struct skl_ddb_allocation *ddb = &gvt->ddb; unsigned int pipe_size, ddb_size, plane_size, plane_cnt; u16 start, end; enum pipe pipe; enum plane_id plane; + int i = 0; + int num_active = hweight32(active_crtcs); + + if (WARN_ON(!num_active)) + return; ddb_size = INTEL_INFO(dev_priv)->ddb_size; ddb_size -= 4; /* 4 blocks for bypass path allocation */ - pipe_size = ddb_size / INTEL_INFO(dev_priv)->num_pipes; + pipe_size = ddb_size / num_active; memset(ddb, 0, sizeof(*ddb)); - for_each_pipe(dev_priv, pipe) { - start = pipe * ddb_size / INTEL_INFO(dev_priv)->num_pipes; + for_each_pipe_masked(dev_priv, pipe, active_crtcs) { + start = pipe_size * (i++); end = start + pipe_size; ddb->plane[pipe][PLANE_CURSOR].start = end - 8; ddb->plane[pipe][PLANE_CURSOR].end = end; @@ -470,7 +475,6 @@ int intel_gvt_init_device(struct drm_i915_private *dev_priv) } intel_gvt_init_pipe_info(gvt); - intel_gvt_init_ddb(gvt); ret = intel_gvt_hypervisor_host_init(&dev_priv->drm.pdev->dev, gvt, &intel_gvt_ops); diff --git a/drivers/gpu/drm/i915/gvt/gvt.h b/drivers/gpu/drm/i915/gvt/gvt.h index 9344293ed692..f4d9056175ae 100644 --- a/drivers/gpu/drm/i915/gvt/gvt.h +++ b/drivers/gpu/drm/i915/gvt/gvt.h @@ -608,6 +608,8 @@ struct intel_gvt_ops { }; int gvt_dom0_ready(struct drm_i915_private *dev_priv); +void intel_gvt_allocate_ddb(struct intel_gvt *gvt, + struct skl_ddb_allocation *ddb, unsigned int active_crtcs); enum { GVT_FAILSAFE_UNSUPPORTED_GUEST, diff --git a/drivers/gpu/drm/i915/intel_pm.c b/drivers/gpu/drm/i915/intel_pm.c index affd3e821687..c7c7b5e78330 100644 --- a/drivers/gpu/drm/i915/intel_pm.c +++ b/drivers/gpu/drm/i915/intel_pm.c @@ -5212,9 +5212,18 @@ skl_compute_ddb(struct drm_atomic_state *state) memcpy(ddb, &dev_priv->wm.skl_hw.ddb, sizeof(*ddb)); #if IS_ENABLED(CONFIG_DRM_I915_GVT) - /* In GVT environemnt, we only use the statically allocated ddb */ + /* + * In GVT environemnt, allocate ddb for all planes in active crtc. + * When there is active pipe change, intel_state active_crtcs is + * not zero and updated before dev_priv, so use intel_state + * active_crtc when it is not zero. + */ if (dev_priv->gvt) { - memcpy(ddb, &dev_priv->gvt->ddb, sizeof(*ddb)); + unsigned int active_crtcs; + + active_crtcs = intel_state->active_crtcs ? + intel_state->active_crtcs : dev_priv->active_crtcs; + intel_gvt_allocate_ddb(dev_priv->gvt, ddb, active_crtcs); return 0; } #endif From b87ddc196e26192e47ea3be69a593cbf879cf1d3 Mon Sep 17 00:00:00 2001 From: Min He Date: Fri, 14 Sep 2018 16:10:22 +0800 Subject: [PATCH 0942/1103] drm/i915: to limit the supported modifiers for plane restriction In GVT-g environment, to ensure all the OS's have enough DDB to display, GVT-g will statically allocate all the DDBs for all the planes on all the pipes. However, when SOS or UOS wants to use Y/Yf tiled modifier, the watermark required will exceed the DDBs allocated by GVT-g, thus causes some display issues. So in this patch, we removed the supports of the Y/Yf tiled modifiers for both SOS and UOS when plane restriction is enabled. And a consequence is that the RBC will be disabled since _CCS modifiers will no longer be supported. Tracked-on: https://github.com/projectacrn/acrn-hypervisor/issues/1193 Signed-off-by: Min He Reviewed-by: Zhao Yakui Reviewed-by: Fei Jiang --- drivers/gpu/drm/i915/intel_display.c | 5 +---- drivers/gpu/drm/i915/intel_sprite.c | 5 +---- 2 files changed, 2 insertions(+), 8 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index bc4d36c1a672..5b32748f8c07 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -13729,10 +13729,7 @@ intel_primary_plane_create(struct drm_i915_private *dev_priv, enum pipe pipe) num_formats = ARRAY_SIZE(skl_primary_formats); } - if (primary->has_ccs) - modifiers = skl_format_modifiers_ccs; - else - modifiers = skl_format_modifiers_noccs; + modifiers = i9xx_format_modifiers; primary->update_plane = skl_update_plane; primary->disable_plane = skl_disable_plane; diff --git a/drivers/gpu/drm/i915/intel_sprite.c b/drivers/gpu/drm/i915/intel_sprite.c index 4b89ba3e021c..2d9cff34018c 100644 --- a/drivers/gpu/drm/i915/intel_sprite.c +++ b/drivers/gpu/drm/i915/intel_sprite.c @@ -1624,10 +1624,7 @@ intel_sprite_plane_create(struct drm_i915_private *dev_priv, num_plane_formats = ARRAY_SIZE(skl_plane_formats); } - if (intel_plane->has_ccs) - modifiers = skl_plane_format_modifiers_ccs; - else - modifiers = skl_plane_format_modifiers_noccs; + modifiers = i9xx_plane_format_modifiers; plane_funcs = &skl_plane_funcs; } else if (IS_VALLEYVIEW(dev_priv) || IS_CHERRYVIEW(dev_priv)) { From 25e45c71cbf5ab229254f27bb0f21f4e89d4f2dd Mon Sep 17 00:00:00 2001 From: "Kim, Dongwon" Date: Thu, 2 Nov 2017 14:58:23 -0700 Subject: [PATCH 0943/1103] REVERTME [IOTG]: hyper_dmabuf: Introducing the hyper_dmabuf driver This commit squishes all the different commits related to the hyper_dmabuf driver into one. This driver is currently under reiview upstream: https://lists.freedesktop.org/archives/dri-devel/2018-February/165720.html Once the review is done upstream, the relevant changes between this version and the upstream one need to be synced. Upload of intial version of hyper_DMABUF driver enabling DMA_BUF exchange between two different VMs in virtualized platform based on hypervisor such as KVM or XEN. Hyper_DMABUF drv's primary role is to import a DMA_BUF from originator then re-export it to another Linux VM so that it can be mapped and accessed by it. The functionality of this driver highly depends on Hypervisor's native page sharing mechanism and inter-VM communication support. This driver has two layers, one is main hyper_DMABUF framework for scatter-gather list management that handles actual import and export of DMA_BUF. Lower layer is about actual memory sharing and communication between two VMs, which is hypervisor-specific interface. This driver is initially designed to enable DMA_BUF sharing across VMs in Xen environment, so currently working with Xen only. This also adds Kernel configuration for hyper_DMABUF drv under Device Drivers->Xen driver support->hyper_dmabuf options. To give some brief information about each source file, hyper_dmabuf/hyper_dmabuf_conf.h : configuration info hyper_dmabuf/hyper_dmabuf_drv.c : driver interface and initialization hyper_dmabuf/hyper_dmabuf_imp.c : scatter-gather list generation and management. DMA_BUF ops for DMA_BUF reconstructed from hyper_DMABUF hyper_dmabuf/hyper_dmabuf_ioctl.c : IOCTLs calls for export/import and comm channel creation unexport. hyper_dmabuf/hyper_dmabuf_list.c : Database (linked-list) for exported and imported hyper_DMABUF hyper_dmabuf/hyper_dmabuf_msg.c : creation and management of messages between exporter and importer hyper_dmabuf/xen/hyper_dmabuf_xen_comm.c : comm ch management and ISRs for incoming messages. hyper_dmabuf/xen/hyper_dmabuf_xen_comm_list.c : Database (linked-list) for keeping information about existing comm channels among VMs Signed-off-by: Kim, Dongwon Signed-off-by: Matuesz, Polrola --- drivers/dma-buf/Kconfig | 2 + drivers/dma-buf/Makefile | 1 + drivers/dma-buf/hyper_dmabuf/Kconfig | 71 ++ drivers/dma-buf/hyper_dmabuf/Makefile | 57 ++ .../dma-buf/hyper_dmabuf/hyper_dmabuf_drv.c | 411 ++++++++ .../dma-buf/hyper_dmabuf/hyper_dmabuf_drv.h | 118 +++ .../dma-buf/hyper_dmabuf/hyper_dmabuf_event.c | 122 +++ .../dma-buf/hyper_dmabuf/hyper_dmabuf_event.h | 38 + .../dma-buf/hyper_dmabuf/hyper_dmabuf_id.c | 133 +++ .../dma-buf/hyper_dmabuf/hyper_dmabuf_id.h | 51 + .../dma-buf/hyper_dmabuf/hyper_dmabuf_ioctl.c | 789 +++++++++++++++ .../dma-buf/hyper_dmabuf/hyper_dmabuf_ioctl.h | 50 + .../dma-buf/hyper_dmabuf/hyper_dmabuf_list.c | 293 ++++++ .../dma-buf/hyper_dmabuf/hyper_dmabuf_list.h | 71 ++ .../dma-buf/hyper_dmabuf/hyper_dmabuf_msg.c | 413 ++++++++ .../dma-buf/hyper_dmabuf/hyper_dmabuf_msg.h | 87 ++ .../dma-buf/hyper_dmabuf/hyper_dmabuf_ops.c | 414 ++++++++ .../dma-buf/hyper_dmabuf/hyper_dmabuf_ops.h | 32 + .../dma-buf/hyper_dmabuf/hyper_dmabuf_query.c | 172 ++++ .../dma-buf/hyper_dmabuf/hyper_dmabuf_query.h | 10 + .../hyper_dmabuf/hyper_dmabuf_remote_sync.c | 322 ++++++ .../hyper_dmabuf/hyper_dmabuf_remote_sync.h | 30 + .../hyper_dmabuf/hyper_dmabuf_sgl_proc.c | 261 +++++ .../hyper_dmabuf/hyper_dmabuf_sgl_proc.h | 41 + .../hyper_dmabuf/hyper_dmabuf_struct.h | 141 +++ .../virtio/hyper_dmabuf_virtio_be_drv.c | 505 ++++++++++ .../virtio/hyper_dmabuf_virtio_comm_ring.c | 89 ++ .../virtio/hyper_dmabuf_virtio_comm_ring.h | 68 ++ .../virtio/hyper_dmabuf_virtio_common.c | 35 + .../virtio/hyper_dmabuf_virtio_common.h | 55 + .../virtio/hyper_dmabuf_virtio_fe_drv.c | 385 +++++++ .../virtio/hyper_dmabuf_virtio_fe_list.c | 99 ++ .../virtio/hyper_dmabuf_virtio_fe_list.h | 48 + .../virtio/hyper_dmabuf_virtio_shm.c | 343 +++++++ .../virtio/hyper_dmabuf_virtio_shm.h | 40 + .../hyper_dmabuf/xen/hyper_dmabuf_xen_comm.c | 951 ++++++++++++++++++ .../hyper_dmabuf/xen/hyper_dmabuf_xen_comm.h | 78 ++ .../xen/hyper_dmabuf_xen_comm_list.c | 158 +++ .../xen/hyper_dmabuf_xen_comm_list.h | 67 ++ .../hyper_dmabuf/xen/hyper_dmabuf_xen_drv.c | 46 + .../hyper_dmabuf/xen/hyper_dmabuf_xen_drv.h | 53 + .../hyper_dmabuf/xen/hyper_dmabuf_xen_shm.c | 525 ++++++++++ .../hyper_dmabuf/xen/hyper_dmabuf_xen_shm.h | 46 + include/uapi/linux/hyper_dmabuf.h | 134 +++ include/uapi/xen/Kbuild | 5 + 45 files changed, 7860 insertions(+) create mode 100644 drivers/dma-buf/hyper_dmabuf/Kconfig create mode 100644 drivers/dma-buf/hyper_dmabuf/Makefile create mode 100644 drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_drv.c create mode 100644 drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_drv.h create mode 100644 drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_event.c create mode 100644 drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_event.h create mode 100644 drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_id.c create mode 100644 drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_id.h create mode 100644 drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_ioctl.c create mode 100644 drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_ioctl.h create mode 100644 drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_list.c create mode 100644 drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_list.h create mode 100644 drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_msg.c create mode 100644 drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_msg.h create mode 100644 drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_ops.c create mode 100644 drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_ops.h create mode 100644 drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_query.c create mode 100644 drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_query.h create mode 100644 drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_remote_sync.c create mode 100644 drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_remote_sync.h create mode 100644 drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_sgl_proc.c create mode 100644 drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_sgl_proc.h create mode 100644 drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_struct.h create mode 100644 drivers/dma-buf/hyper_dmabuf/virtio/hyper_dmabuf_virtio_be_drv.c create mode 100644 drivers/dma-buf/hyper_dmabuf/virtio/hyper_dmabuf_virtio_comm_ring.c create mode 100644 drivers/dma-buf/hyper_dmabuf/virtio/hyper_dmabuf_virtio_comm_ring.h create mode 100644 drivers/dma-buf/hyper_dmabuf/virtio/hyper_dmabuf_virtio_common.c create mode 100644 drivers/dma-buf/hyper_dmabuf/virtio/hyper_dmabuf_virtio_common.h create mode 100644 drivers/dma-buf/hyper_dmabuf/virtio/hyper_dmabuf_virtio_fe_drv.c create mode 100644 drivers/dma-buf/hyper_dmabuf/virtio/hyper_dmabuf_virtio_fe_list.c create mode 100644 drivers/dma-buf/hyper_dmabuf/virtio/hyper_dmabuf_virtio_fe_list.h create mode 100644 drivers/dma-buf/hyper_dmabuf/virtio/hyper_dmabuf_virtio_shm.c create mode 100644 drivers/dma-buf/hyper_dmabuf/virtio/hyper_dmabuf_virtio_shm.h create mode 100644 drivers/dma-buf/hyper_dmabuf/xen/hyper_dmabuf_xen_comm.c create mode 100644 drivers/dma-buf/hyper_dmabuf/xen/hyper_dmabuf_xen_comm.h create mode 100644 drivers/dma-buf/hyper_dmabuf/xen/hyper_dmabuf_xen_comm_list.c create mode 100644 drivers/dma-buf/hyper_dmabuf/xen/hyper_dmabuf_xen_comm_list.h create mode 100644 drivers/dma-buf/hyper_dmabuf/xen/hyper_dmabuf_xen_drv.c create mode 100644 drivers/dma-buf/hyper_dmabuf/xen/hyper_dmabuf_xen_drv.h create mode 100644 drivers/dma-buf/hyper_dmabuf/xen/hyper_dmabuf_xen_shm.c create mode 100644 drivers/dma-buf/hyper_dmabuf/xen/hyper_dmabuf_xen_shm.h create mode 100644 include/uapi/linux/hyper_dmabuf.h create mode 100644 include/uapi/xen/Kbuild diff --git a/drivers/dma-buf/Kconfig b/drivers/dma-buf/Kconfig index ed3b785bae37..09ccac1768e3 100644 --- a/drivers/dma-buf/Kconfig +++ b/drivers/dma-buf/Kconfig @@ -30,4 +30,6 @@ config SW_SYNC WARNING: improper use of this can result in deadlocking kernel drivers from userspace. Intended for test and debug only. +source "drivers/dma-buf/hyper_dmabuf/Kconfig" + endmenu diff --git a/drivers/dma-buf/Makefile b/drivers/dma-buf/Makefile index c33bf8863147..3f15a841502e 100644 --- a/drivers/dma-buf/Makefile +++ b/drivers/dma-buf/Makefile @@ -1,3 +1,4 @@ obj-y := dma-buf.o dma-fence.o dma-fence-array.o reservation.o seqno-fence.o obj-$(CONFIG_SYNC_FILE) += sync_file.o obj-$(CONFIG_SW_SYNC) += sw_sync.o sync_debug.o +obj-$(CONFIG_HYPER_DMABUF) += hyper_dmabuf/ diff --git a/drivers/dma-buf/hyper_dmabuf/Kconfig b/drivers/dma-buf/hyper_dmabuf/Kconfig new file mode 100644 index 000000000000..88992167c645 --- /dev/null +++ b/drivers/dma-buf/hyper_dmabuf/Kconfig @@ -0,0 +1,71 @@ +menu "hyper_dmabuf options" + +config HYPER_DMABUF + bool "Enables hyper dmabuf driver" + default y + +choice + prompt "Hypervisor" + depends on HYPER_DMABUF + default HYPER_DMABUF_XEN + +config HYPER_DMABUF_XEN + bool "Configure hyper_dmabuf for XEN hypervisor" + depends on HYPER_DMABUF && XEN + help + Configuring hyper_dmabuf driver for XEN hypervisor + +config HYPER_DMABUF_ACRN + bool "Configure hyper_dmabuf for ACRN hypervisor" + depends on HYPER_DMABUF && ACRN_VIRTIO_DEVICES + help + Configuring hyper_dmabuf driver for ACRN hypervisor +endchoice + +choice + prompt "Virtio driver type" + depends on HYPER_DMABUF && HYPER_DMABUF_ACRN + default HYPER_DMABUF_VIRTIO_BE + +config HYPER_DMABUF_VIRTIO_BE + depends on VBS && DRM_I915_GVT + bool "Configure hyper_dmabuf as virtio backend" + help + Configuring hyper_dmabuf driver as virtio backend + +config HYPER_DMABUF_VIRTIO_FE + depends on ACRN_VIRTIO_DEVICES + bool "Configure hyper_dmabuf as virtio frontend" + help + Configuring hyper_dmabuf driver as virtio frontend +endchoice + +config HYPER_DMABUF_SYSFS + bool "Enable sysfs information about hyper DMA buffers" + default y + depends on HYPER_DMABUF + help + Expose information about imported and exported buffers using + hyper_dmabuf driver + +config HYPER_DMABUF_EVENT_GEN + bool "Enable event-generation and polling operation" + default n + depends on HYPER_DMABUF + help + With this config enabled, hyper_dmabuf driver on the importer side + generates events and queue those up in the event list whenever a new + shared DMA-BUF is available. Events in the list can be retrieved by + read operation. + +config HYPER_DMABUF_XEN_AUTO_RX_CH_ADD + bool "Enable automatic rx-ch add with 10 secs interval" + default y + depends on HYPER_DMABUF && HYPER_DMABUF_XEN + help + If enabled, driver reads a node in xenstore every 10 seconds + to check whether there is any tx comm ch configured by another + domain then initialize matched rx comm ch automatically for any + existing tx comm chs. + +endmenu diff --git a/drivers/dma-buf/hyper_dmabuf/Makefile b/drivers/dma-buf/hyper_dmabuf/Makefile new file mode 100644 index 000000000000..f63967cc99f6 --- /dev/null +++ b/drivers/dma-buf/hyper_dmabuf/Makefile @@ -0,0 +1,57 @@ +TARGET_MODULE:=hyper_dmabuf + +# If we running by kernel building system +ifneq ($(KERNELRELEASE),) + $(TARGET_MODULE)-objs := hyper_dmabuf_drv.o \ + hyper_dmabuf_ioctl.o \ + hyper_dmabuf_list.o \ + hyper_dmabuf_sgl_proc.o \ + hyper_dmabuf_ops.o \ + hyper_dmabuf_msg.o \ + hyper_dmabuf_id.o \ + hyper_dmabuf_remote_sync.o \ + hyper_dmabuf_query.o \ + +ifeq ($(CONFIG_HYPER_DMABUF_EVENT_GEN), y) + $(TARGET_MODULE)-objs += hyper_dmabuf_event.o +endif + +ifeq ($(CONFIG_HYPER_DMABUF_XEN), y) + $(TARGET_MODULE)-objs += xen/hyper_dmabuf_xen_comm.o \ + xen/hyper_dmabuf_xen_comm_list.o \ + xen/hyper_dmabuf_xen_shm.o \ + xen/hyper_dmabuf_xen_drv.o +else ifeq ($(CONFIG_HYPER_DMABUF_ACRN), y) + ifeq ($(CONFIG_HYPER_DMABUF_VIRTIO_BE), y) + $(TARGET_MODULE)-objs += virtio/hyper_dmabuf_virtio_be_drv.o \ + virtio/hyper_dmabuf_virtio_fe_list.o + else + $(TARGET_MODULE)-objs += virtio/hyper_dmabuf_virtio_fe_drv.o + endif + $(TARGET_MODULE)-objs += virtio/hyper_dmabuf_virtio_common.o \ + virtio/hyper_dmabuf_virtio_shm.o \ + virtio/hyper_dmabuf_virtio_comm_ring.o +endif + +obj-$(CONFIG_HYPER_DMABUF) := $(TARGET_MODULE).o + +# If we are running without kernel build system +else +BUILDSYSTEM_DIR?=../../../ +PWD:=$(shell pwd) + +all : +# run kernel build system to make module + $(MAKE) -C $(BUILDSYSTEM_DIR) M=$(PWD) modules + +clean: +# run kernel build system to cleanup in current directory + $(MAKE) -C $(BUILDSYSTEM_DIR) M=$(PWD) clean + +load: + insmod ./$(TARGET_MODULE).ko + +unload: + rmmod ./$(TARGET_MODULE).ko + +endif diff --git a/drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_drv.c b/drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_drv.c new file mode 100644 index 000000000000..f1afce29d6af --- /dev/null +++ b/drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_drv.c @@ -0,0 +1,411 @@ +/* + * Copyright © 2017 Intel Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + * + * Authors: + * Dongwon Kim + * Mateusz Polrola + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "hyper_dmabuf_drv.h" +#include "hyper_dmabuf_ioctl.h" +#include "hyper_dmabuf_list.h" +#include "hyper_dmabuf_id.h" +#include "hyper_dmabuf_event.h" + +#ifdef CONFIG_HYPER_DMABUF_XEN +#include "xen/hyper_dmabuf_xen_drv.h" +#elif defined (CONFIG_HYPER_DMABUF_ACRN) +#include "virtio/hyper_dmabuf_virtio_common.h" +#endif + +MODULE_LICENSE("GPL and additional rights"); +MODULE_AUTHOR("Intel Corporation"); + +struct hyper_dmabuf_private *hy_drv_priv; + +static void force_free(struct exported_sgt_info *exported, + void *attr) +{ + struct ioctl_hyper_dmabuf_unexport unexport_attr; + struct file *filp = (struct file *)attr; + + if (!filp || !exported) + return; + + if (exported->filp == filp) { + dev_dbg(hy_drv_priv->dev, + "Forcefully releasing buffer {id:%d key:%d %d %d}\n", + exported->hid.id, exported->hid.rng_key[0], + exported->hid.rng_key[1], exported->hid.rng_key[2]); + + unexport_attr.hid = exported->hid; + unexport_attr.delay_ms = 0; + + hyper_dmabuf_unexport_ioctl(filp, &unexport_attr); + } +} + +static int hyper_dmabuf_open(struct inode *inode, struct file *filp) +{ + int ret = 0; + + /* Do not allow exclusive open */ + if (filp->f_flags & O_EXCL) + return -EBUSY; + + return ret; +} + +static int hyper_dmabuf_release(struct inode *inode, struct file *filp) +{ + hyper_dmabuf_foreach_exported(force_free, filp); + + return 0; +} + +#ifdef CONFIG_HYPER_DMABUF_EVENT_GEN + +static unsigned int hyper_dmabuf_event_poll(struct file *filp, + struct poll_table_struct *wait) +{ + poll_wait(filp, &hy_drv_priv->event_wait, wait); + + if (!list_empty(&hy_drv_priv->event_list)) + return POLLIN | POLLRDNORM; + + return 0; +} + +static ssize_t hyper_dmabuf_event_read(struct file *filp, char __user *buffer, + size_t count, loff_t *offset) +{ + int ret; + + /* only root can read events */ + if (!capable(CAP_DAC_OVERRIDE)) { + dev_err(hy_drv_priv->dev, + "Only root can read events\n"); + return -EPERM; + } + + /* make sure user buffer can be written */ + if (!access_ok(VERIFY_WRITE, buffer, count)) { + dev_err(hy_drv_priv->dev, + "User buffer can't be written.\n"); + return -EINVAL; + } + + ret = mutex_lock_interruptible(&hy_drv_priv->event_read_lock); + if (ret) + return ret; + + while (1) { + struct hyper_dmabuf_event *e = NULL; + + spin_lock_irq(&hy_drv_priv->event_lock); + if (!list_empty(&hy_drv_priv->event_list)) { + e = list_first_entry(&hy_drv_priv->event_list, + struct hyper_dmabuf_event, link); + list_del(&e->link); + } + spin_unlock_irq(&hy_drv_priv->event_lock); + + if (!e) { + if (ret) + break; + + if (filp->f_flags & O_NONBLOCK) { + ret = -EAGAIN; + break; + } + + mutex_unlock(&hy_drv_priv->event_read_lock); + ret = wait_event_interruptible(hy_drv_priv->event_wait, + !list_empty(&hy_drv_priv->event_list)); + + if (ret == 0) + ret = mutex_lock_interruptible( + &hy_drv_priv->event_read_lock); + + if (ret) + return ret; + } else { + unsigned int length = (sizeof(e->event_data.hdr) + + e->event_data.hdr.size); + + if (length > count - ret) { +put_back_event: + spin_lock_irq(&hy_drv_priv->event_lock); + list_add(&e->link, &hy_drv_priv->event_list); + spin_unlock_irq(&hy_drv_priv->event_lock); + break; + } + + if (copy_to_user(buffer + ret, &e->event_data.hdr, + sizeof(e->event_data.hdr))) { + if (ret == 0) + ret = -EFAULT; + + goto put_back_event; + } + + ret += sizeof(e->event_data.hdr); + + if (copy_to_user(buffer + ret, e->event_data.data, + e->event_data.hdr.size)) { + /* error while copying void *data */ + + struct hyper_dmabuf_event_hdr dummy_hdr = {0}; + + ret -= sizeof(e->event_data.hdr); + + /* nullifying hdr of the event in user buffer */ + if (copy_to_user(buffer + ret, &dummy_hdr, + sizeof(dummy_hdr))) { + dev_err(hy_drv_priv->dev, + "failed to nullify invalid hdr already in userspace\n"); + } + + ret = -EFAULT; + + goto put_back_event; + } + + ret += e->event_data.hdr.size; + hy_drv_priv->pending--; + kfree(e); + } + } + + mutex_unlock(&hy_drv_priv->event_read_lock); + + return ret; +} + +#endif + +static const struct file_operations hyper_dmabuf_driver_fops = { + .owner = THIS_MODULE, + .open = hyper_dmabuf_open, + .release = hyper_dmabuf_release, + +/* poll and read interfaces are needed only for event-polling */ +#ifdef CONFIG_HYPER_DMABUF_EVENT_GEN + .read = hyper_dmabuf_event_read, + .poll = hyper_dmabuf_event_poll, +#endif + + .unlocked_ioctl = hyper_dmabuf_ioctl, +}; + +static struct miscdevice hyper_dmabuf_miscdev = { + .minor = MISC_DYNAMIC_MINOR, + .name = "hyper_dmabuf", + .fops = &hyper_dmabuf_driver_fops, +}; + +static int register_device(void) +{ + int ret = 0; + + ret = misc_register(&hyper_dmabuf_miscdev); + + if (ret) { + printk(KERN_ERR "hyper_dmabuf: driver can't be registered\n"); + return ret; + } + + hy_drv_priv->dev = hyper_dmabuf_miscdev.this_device; + + /* TODO: Check if there is a different way to initialize dma mask */ + dma_coerce_mask_and_coherent(hy_drv_priv->dev, DMA_BIT_MASK(64)); + + return ret; +} + +static void unregister_device(void) +{ + dev_info(hy_drv_priv->dev, + "hyper_dmabuf: unregister_device() is called\n"); + + misc_deregister(&hyper_dmabuf_miscdev); +} + +static int __init hyper_dmabuf_drv_init(void) +{ + int ret = 0; + + printk(KERN_NOTICE "hyper_dmabuf_starting: Initialization started\n"); + + hy_drv_priv = kcalloc(1, sizeof(struct hyper_dmabuf_private), + GFP_KERNEL); + + if (!hy_drv_priv) + return -ENOMEM; + + ret = register_device(); + if (ret < 0) + return ret; + +/* currently only supports XEN hypervisor */ +#ifdef CONFIG_HYPER_DMABUF_XEN + hy_drv_priv->bknd_ops = &xen_bknd_ops; +#elif defined (CONFIG_HYPER_DMABUF_ACRN) + hy_drv_priv->bknd_ops = &virtio_bknd_ops; +#else + hy_drv_priv->bknd_ops = NULL; + printk(KERN_ERR "No backend configured for hyper_dmabuf in kernel config\n"); +#endif + + if (hy_drv_priv->bknd_ops == NULL) { + printk(KERN_ERR "Hyper_dmabuf: no backend found\n"); + return -1; + } + + mutex_init(&hy_drv_priv->lock); + + mutex_lock(&hy_drv_priv->lock); + + hy_drv_priv->initialized = false; + + dev_info(hy_drv_priv->dev, + "initializing database for imported/exported dmabufs\n"); + + hy_drv_priv->work_queue = create_workqueue("hyper_dmabuf_wqueue"); + + ret = hyper_dmabuf_table_init(); + if (ret < 0) { + dev_err(hy_drv_priv->dev, + "fail to init table for exported/imported entries\n"); + mutex_unlock(&hy_drv_priv->lock); + kfree(hy_drv_priv); + return ret; + } + +#ifdef CONFIG_HYPER_DMABUF_SYSFS + ret = hyper_dmabuf_register_sysfs(hy_drv_priv->dev); + if (ret < 0) { + dev_err(hy_drv_priv->dev, + "failed to initialize sysfs\n"); + mutex_unlock(&hy_drv_priv->lock); + kfree(hy_drv_priv); + return ret; + } +#endif + +#ifdef CONFIG_HYPER_DMABUF_EVENT_GEN + mutex_init(&hy_drv_priv->event_read_lock); + spin_lock_init(&hy_drv_priv->event_lock); + + /* Initialize event queue */ + INIT_LIST_HEAD(&hy_drv_priv->event_list); + init_waitqueue_head(&hy_drv_priv->event_wait); + + /* resetting number of pending events */ + hy_drv_priv->pending = 0; +#endif + + if (hy_drv_priv->bknd_ops->init) { + ret = hy_drv_priv->bknd_ops->init(); + + if (ret < 0) { + dev_dbg(hy_drv_priv->dev, + "failed to initialize backend.\n"); + return ret; + } + } + + hy_drv_priv->domid = hy_drv_priv->bknd_ops->get_vm_id(); + + hy_drv_priv->initialized = true; + if (hy_drv_priv->bknd_ops->init_comm_env) { + ret = hy_drv_priv->bknd_ops->init_comm_env(); + if (ret < 0) { + hy_drv_priv->initialized = false; + dev_dbg(hy_drv_priv->dev, + "failed to initialize comm-env.\n"); + } + } + + mutex_unlock(&hy_drv_priv->lock); + + dev_info(hy_drv_priv->dev, + "Finishing up initialization of hyper_dmabuf drv\n"); + + /* interrupt for comm should be registered here: */ + return ret; +} + +static void hyper_dmabuf_drv_exit(void) +{ +#ifdef CONFIG_HYPER_DMABUF_SYSFS + hyper_dmabuf_unregister_sysfs(hy_drv_priv->dev); +#endif + + mutex_lock(&hy_drv_priv->lock); + + /* hash tables for export/import entries and ring_infos */ + hyper_dmabuf_table_destroy(); + + if (hy_drv_priv->bknd_ops->destroy_comm) { + hy_drv_priv->bknd_ops->destroy_comm(); + } + + if (hy_drv_priv->bknd_ops->cleanup) { + hy_drv_priv->bknd_ops->cleanup(); + }; + + /* destroy workqueue */ + if (hy_drv_priv->work_queue) + destroy_workqueue(hy_drv_priv->work_queue); + + /* destroy id_queue */ + if (hy_drv_priv->id_queue) + hyper_dmabuf_free_hid_list(); + +#ifdef CONFIG_HYPER_DMABUF_EVENT_GEN + /* clean up event queue */ + hyper_dmabuf_events_release(); +#endif + + mutex_unlock(&hy_drv_priv->lock); + + dev_info(hy_drv_priv->dev, + "hyper_dmabuf driver: Exiting\n"); + + kfree(hy_drv_priv); + + unregister_device(); +} + +module_init(hyper_dmabuf_drv_init); +module_exit(hyper_dmabuf_drv_exit); diff --git a/drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_drv.h b/drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_drv.h new file mode 100644 index 000000000000..45c24fd8d25d --- /dev/null +++ b/drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_drv.h @@ -0,0 +1,118 @@ +/* + * Copyright © 2017 Intel Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + * + */ + +#ifndef __LINUX_PUBLIC_HYPER_DMABUF_DRV_H__ +#define __LINUX_PUBLIC_HYPER_DMABUF_DRV_H__ + +#include +#include + +struct hyper_dmabuf_req; + +struct hyper_dmabuf_event { + struct hyper_dmabuf_event_data event_data; + struct list_head link; +}; + +struct hyper_dmabuf_private { + struct device *dev; + + /* VM(domain) id of current VM instance */ + int domid; + + /* workqueue dedicated to hyper_dmabuf driver */ + struct workqueue_struct *work_queue; + + /* list of reusable hyper_dmabuf_ids */ + struct list_reusable_id *id_queue; + + /* backend ops - hypervisor specific */ + struct hyper_dmabuf_bknd_ops *bknd_ops; + + /* device global lock */ + /* TODO: might need a lock per resource (e.g. EXPORT LIST) */ + struct mutex lock; + + /* flag that shows whether backend is initialized */ + bool initialized; + + wait_queue_head_t event_wait; + struct list_head event_list; + + spinlock_t event_lock; + struct mutex event_read_lock; + + /* # of pending events */ + int pending; +}; + +struct list_reusable_id { + hyper_dmabuf_id_t hid; + struct list_head list; +}; + +struct hyper_dmabuf_bknd_ops { + /* backend initialization routine (optional) */ + int (*init)(void); + + /* backend cleanup routine (optional) */ + void (*cleanup)(void); + + /* retreiving id of current virtual machine */ + int (*get_vm_id)(void); + + /* get pages shared via hypervisor-specific method */ + int (*share_pages)(struct page **, int, int, void **); + + /* make shared pages unshared via hypervisor specific method */ + int (*unshare_pages)(void **, int); + + /* map remotely shared pages on importer's side via + * hypervisor-specific method + */ + struct page ** (*map_shared_pages)(unsigned long, int, int, void **); + + /* unmap and free shared pages on importer's side via + * hypervisor-specific method + */ + int (*unmap_shared_pages)(void **, int); + + /* initialize communication environment */ + int (*init_comm_env)(void); + + void (*destroy_comm)(void); + + /* upstream ch setup (receiving and responding) */ + int (*init_rx_ch)(int); + + /* downstream ch setup (transmitting and parsing responses) */ + int (*init_tx_ch)(int); + + int (*send_req)(int, struct hyper_dmabuf_req *, int); +}; + +/* exporting global drv private info */ +extern struct hyper_dmabuf_private *hy_drv_priv; + +#endif /* __LINUX_PUBLIC_HYPER_DMABUF_DRV_H__ */ diff --git a/drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_event.c b/drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_event.c new file mode 100644 index 000000000000..392ea99e0784 --- /dev/null +++ b/drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_event.c @@ -0,0 +1,122 @@ +/* + * Copyright © 2017 Intel Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + * + * Authors: + * Dongwon Kim + * Mateusz Polrola + * + */ + +#include +#include +#include +#include +#include "hyper_dmabuf_drv.h" +#include "hyper_dmabuf_struct.h" +#include "hyper_dmabuf_list.h" +#include "hyper_dmabuf_event.h" + +static void send_event(struct hyper_dmabuf_event *e) +{ + struct hyper_dmabuf_event *oldest; + unsigned long irqflags; + + spin_lock_irqsave(&hy_drv_priv->event_lock, irqflags); + + /* check current number of event then if it hits the max num allowed + * then remove the oldest event in the list + */ + if (hy_drv_priv->pending > MAX_DEPTH_EVENT_QUEUE - 1) { + oldest = list_first_entry(&hy_drv_priv->event_list, + struct hyper_dmabuf_event, link); + list_del(&oldest->link); + hy_drv_priv->pending--; + kfree(oldest); + } + + list_add_tail(&e->link, + &hy_drv_priv->event_list); + + hy_drv_priv->pending++; + + wake_up_interruptible(&hy_drv_priv->event_wait); + + spin_unlock_irqrestore(&hy_drv_priv->event_lock, irqflags); +} + +void hyper_dmabuf_events_release(void) +{ + struct hyper_dmabuf_event *e, *et; + unsigned long irqflags; + + spin_lock_irqsave(&hy_drv_priv->event_lock, irqflags); + + list_for_each_entry_safe(e, et, &hy_drv_priv->event_list, + link) { + list_del(&e->link); + kfree(e); + hy_drv_priv->pending--; + } + + if (hy_drv_priv->pending) { + dev_err(hy_drv_priv->dev, + "possible leak on event_list\n"); + } + + spin_unlock_irqrestore(&hy_drv_priv->event_lock, irqflags); +} + +int hyper_dmabuf_import_event(hyper_dmabuf_id_t hid) +{ + struct hyper_dmabuf_event *e; + struct imported_sgt_info *imported; + + imported = hyper_dmabuf_find_imported(hid); + + if (!imported) { + dev_err(hy_drv_priv->dev, + "can't find imported_sgt_info in the list\n"); + return -EINVAL; + } + + e = kzalloc(sizeof(*e), GFP_KERNEL); + + if (!e) + return -ENOMEM; + + e->event_data.hdr.event_type = HYPER_DMABUF_NEW_IMPORT; + e->event_data.hdr.hid = hid; + e->event_data.data = (void *)imported->priv; + e->event_data.hdr.size = imported->sz_priv; + + send_event(e); + + dev_dbg(hy_drv_priv->dev, + "event number = %d :", hy_drv_priv->pending); + + dev_dbg(hy_drv_priv->dev, + "generating events for {%d, %d, %d, %d}\n", + imported->hid.id, imported->hid.rng_key[0], + imported->hid.rng_key[1], imported->hid.rng_key[2]); + + return 0; +} diff --git a/drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_event.h b/drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_event.h new file mode 100644 index 000000000000..50db04faf222 --- /dev/null +++ b/drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_event.h @@ -0,0 +1,38 @@ +/* + * Copyright © 2017 Intel Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + * + */ + +#ifndef __HYPER_DMABUF_EVENT_H__ +#define __HYPER_DMABUF_EVENT_H__ + +#define MAX_DEPTH_EVENT_QUEUE 32 + +enum hyper_dmabuf_event_type { + HYPER_DMABUF_NEW_IMPORT = 0x10000, +}; + +void hyper_dmabuf_events_release(void); + +int hyper_dmabuf_import_event(hyper_dmabuf_id_t hid); + +#endif /* __HYPER_DMABUF_EVENT_H__ */ diff --git a/drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_id.c b/drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_id.c new file mode 100644 index 000000000000..e67b84a7e64c --- /dev/null +++ b/drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_id.c @@ -0,0 +1,133 @@ +/* + * Copyright © 2017 Intel Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + * + * Authors: + * Dongwon Kim + * Mateusz Polrola + * + */ + +#include +#include +#include +#include "hyper_dmabuf_drv.h" +#include "hyper_dmabuf_id.h" + +void hyper_dmabuf_store_hid(hyper_dmabuf_id_t hid) +{ + struct list_reusable_id *reusable_head = hy_drv_priv->id_queue; + struct list_reusable_id *new_reusable; + + new_reusable = kmalloc(sizeof(*new_reusable), GFP_KERNEL); + + if (!new_reusable) + return; + + new_reusable->hid = hid; + + list_add(&new_reusable->list, &reusable_head->list); +} + +static hyper_dmabuf_id_t get_reusable_hid(void) +{ + struct list_reusable_id *reusable_head = hy_drv_priv->id_queue; + hyper_dmabuf_id_t hid = {-1, {0, 0, 0} }; + + /* check there is reusable id */ + if (!list_empty(&reusable_head->list)) { + reusable_head = list_first_entry(&reusable_head->list, + struct list_reusable_id, + list); + + list_del(&reusable_head->list); + hid = reusable_head->hid; + kfree(reusable_head); + } + + return hid; +} + +void hyper_dmabuf_free_hid_list(void) +{ + struct list_reusable_id *reusable_head = hy_drv_priv->id_queue; + struct list_reusable_id *temp_head; + + if (reusable_head) { + /* freeing mem space all reusable ids in the stack */ + while (!list_empty(&reusable_head->list)) { + temp_head = list_first_entry(&reusable_head->list, + struct list_reusable_id, + list); + list_del(&temp_head->list); + kfree(temp_head); + } + + /* freeing head */ + kfree(reusable_head); + } +} + +hyper_dmabuf_id_t hyper_dmabuf_get_hid(void) +{ + static int count; + hyper_dmabuf_id_t hid; + struct list_reusable_id *reusable_head; + + /* first call to hyper_dmabuf_get_id */ + if (count == 0) { + reusable_head = kmalloc(sizeof(*reusable_head), GFP_KERNEL); + + if (!reusable_head) + return (hyper_dmabuf_id_t){-1, {0, 0, 0} }; + + /* list head has an invalid count */ + reusable_head->hid.id = -1; + INIT_LIST_HEAD(&reusable_head->list); + hy_drv_priv->id_queue = reusable_head; + } + + hid = get_reusable_hid(); + + /*creating a new H-ID only if nothing in the reusable id queue + * and count is less than maximum allowed + */ + if (hid.id == -1 && count < HYPER_DMABUF_ID_MAX) + hid.id = HYPER_DMABUF_ID_CREATE(hy_drv_priv->domid, count++); + + /* random data embedded in the id for security */ + get_random_bytes(&hid.rng_key[0], 12); + + return hid; +} + +bool hyper_dmabuf_hid_keycomp(hyper_dmabuf_id_t hid1, hyper_dmabuf_id_t hid2) +{ + int i; + + /* compare keys */ + for (i = 0; i < 3; i++) { + if (hid1.rng_key[i] != hid2.rng_key[i]) + return false; + } + + return true; +} diff --git a/drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_id.h b/drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_id.h new file mode 100644 index 000000000000..ed690f3a478c --- /dev/null +++ b/drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_id.h @@ -0,0 +1,51 @@ +/* + * Copyright © 2017 Intel Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + * + */ + +#ifndef __HYPER_DMABUF_ID_H__ +#define __HYPER_DMABUF_ID_H__ + +#define HYPER_DMABUF_ID_CREATE(domid, cnt) \ + ((((domid) & 0xFF) << 24) | ((cnt) & 0xFFFFFF)) + +#define HYPER_DMABUF_DOM_ID(hid) \ + (((hid.id) >> 24) & 0xFF) + +/* currently maximum number of buffers shared + * at any given moment is limited to 1000 + */ +#define HYPER_DMABUF_ID_MAX 1000 + +/* adding freed hid to the reusable list */ +void hyper_dmabuf_store_hid(hyper_dmabuf_id_t hid); + +/* freeing the reusasble list */ +void hyper_dmabuf_free_hid_list(void); + +/* getting a hid available to use. */ +hyper_dmabuf_id_t hyper_dmabuf_get_hid(void); + +/* comparing two different hid */ +bool hyper_dmabuf_hid_keycomp(hyper_dmabuf_id_t hid1, hyper_dmabuf_id_t hid2); + +#endif /*__HYPER_DMABUF_ID_H*/ diff --git a/drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_ioctl.c b/drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_ioctl.c new file mode 100644 index 000000000000..20274e1b9e20 --- /dev/null +++ b/drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_ioctl.c @@ -0,0 +1,789 @@ +/* + * Copyright © 2017 Intel Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + * + * Authors: + * Dongwon Kim + * Mateusz Polrola + * + */ + +#include +#include +#include +#include +#include +#include "hyper_dmabuf_drv.h" +#include "hyper_dmabuf_id.h" +#include "hyper_dmabuf_struct.h" +#include "hyper_dmabuf_ioctl.h" +#include "hyper_dmabuf_list.h" +#include "hyper_dmabuf_msg.h" +#include "hyper_dmabuf_sgl_proc.h" +#include "hyper_dmabuf_ops.h" +#include "hyper_dmabuf_query.h" + +static int hyper_dmabuf_tx_ch_setup_ioctl(struct file *filp, void *data) +{ + struct ioctl_hyper_dmabuf_tx_ch_setup *tx_ch_attr; + struct hyper_dmabuf_bknd_ops *bknd_ops = hy_drv_priv->bknd_ops; + int ret = 0; + + if (!data) { + dev_err(hy_drv_priv->dev, "user data is NULL\n"); + return -EINVAL; + } + tx_ch_attr = (struct ioctl_hyper_dmabuf_tx_ch_setup *)data; + + if (bknd_ops->init_tx_ch) { + ret = bknd_ops->init_tx_ch(tx_ch_attr->remote_domain); + } + + return ret; +} + +static int hyper_dmabuf_rx_ch_setup_ioctl(struct file *filp, void *data) +{ + struct ioctl_hyper_dmabuf_rx_ch_setup *rx_ch_attr; + struct hyper_dmabuf_bknd_ops *bknd_ops = hy_drv_priv->bknd_ops; + int ret = 0; + + if (!data) { + dev_err(hy_drv_priv->dev, "user data is NULL\n"); + return -EINVAL; + } + + rx_ch_attr = (struct ioctl_hyper_dmabuf_rx_ch_setup *)data; + + if (bknd_ops->init_rx_ch) + ret = bknd_ops->init_rx_ch(rx_ch_attr->source_domain); + + return ret; +} + +static int send_export_msg(struct exported_sgt_info *exported, + struct pages_info *pg_info) +{ + struct hyper_dmabuf_bknd_ops *bknd_ops = hy_drv_priv->bknd_ops; + struct hyper_dmabuf_req *req; + int op[MAX_NUMBER_OF_OPERANDS] = {0}; + int ret, i; + + /* now create request for importer via ring */ + op[0] = exported->hid.id; + + for (i = 0; i < 3; i++) + op[i+1] = exported->hid.rng_key[i]; + + if (pg_info) { + op[4] = pg_info->nents; + op[5] = pg_info->frst_ofst; + op[6] = pg_info->last_len; + op[7] = bknd_ops->share_pages(pg_info->pgs, exported->rdomid, + pg_info->nents, &exported->refs_info); + if (op[7] < 0) { + dev_err(hy_drv_priv->dev, "pages sharing failed\n"); + return op[7]; + } + } + + op[8] = exported->sz_priv; + + /* driver/application specific private info */ + memcpy(&op[9], exported->priv, op[8]); + + req = kcalloc(1, sizeof(*req), GFP_KERNEL); + + if (!req) + return -ENOMEM; + + /* composing a message to the importer */ + hyper_dmabuf_create_req(req, HYPER_DMABUF_EXPORT, &op[0]); + + ret = bknd_ops->send_req(exported->rdomid, req, true); + + kfree(req); + + return ret; +} + +/* Fast path exporting routine in case same buffer is already exported. + * In this function, we skip normal exporting process and just update + * private data on both VMs (importer and exporter) + * + * return '1' if reexport is needed, return '0' if succeeds, return + * Kernel error code if something goes wrong + */ +static int fastpath_export(hyper_dmabuf_id_t hid, int sz_priv, char *priv) +{ + int reexport = 1; + int ret = 0; + struct exported_sgt_info *exported; + + exported = hyper_dmabuf_find_exported(hid); + + if (!exported) + return reexport; + + if (exported->valid == false) + return reexport; + + /* + * Check if unexport is already scheduled for that buffer, + * if so try to cancel it. If that will fail, buffer needs + * to be reexport once again. + */ + if (exported->unexport_sched) { + if (!cancel_delayed_work_sync(&exported->unexport)) + return reexport; + + exported->unexport_sched = false; + } + + /* if there's any change in size of private data. + * we reallocate space for private data with new size + */ + if (sz_priv != exported->sz_priv) { + kfree(exported->priv); + + /* truncating size */ + if (sz_priv > MAX_SIZE_PRIV_DATA) + exported->sz_priv = MAX_SIZE_PRIV_DATA; + else + exported->sz_priv = sz_priv; + + exported->priv = kcalloc(1, exported->sz_priv, + GFP_KERNEL); + + if (!exported->priv) { + hyper_dmabuf_remove_exported(exported->hid); + hyper_dmabuf_cleanup_sgt_info(exported, true); + kfree(exported); + return -ENOMEM; + } + } + + /* update private data in sgt_info with new ones */ + ret = copy_from_user(exported->priv, priv, exported->sz_priv); + if (ret) { + dev_err(hy_drv_priv->dev, + "Failed to load a new private data\n"); + ret = -EINVAL; + } else { + /* send an export msg for updating priv in importer */ + ret = send_export_msg(exported, NULL); + + if (ret < 0) { + dev_err(hy_drv_priv->dev, + "Failed to send a new private data\n"); + ret = -EBUSY; + } + } + + return ret; +} + +static int hyper_dmabuf_export_remote_ioctl(struct file *filp, void *data) +{ + struct ioctl_hyper_dmabuf_export_remote *export_remote_attr = + (struct ioctl_hyper_dmabuf_export_remote *)data; + struct dma_buf *dma_buf; + struct dma_buf_attachment *attachment; + struct sg_table *sgt; + struct pages_info *pg_info; + struct exported_sgt_info *exported; + hyper_dmabuf_id_t hid; + int ret = 0; + + if (hy_drv_priv->domid == export_remote_attr->remote_domain) { + dev_err(hy_drv_priv->dev, + "exporting to the same VM is not permitted\n"); + return -EINVAL; + } + + dma_buf = dma_buf_get(export_remote_attr->dmabuf_fd); + + if (IS_ERR(dma_buf)) { + dev_err(hy_drv_priv->dev, "Cannot get dma buf\n"); + return PTR_ERR(dma_buf); + } + + /* we check if this specific attachment was already exported + * to the same domain and if yes and it's valid sgt_info, + * it returns hyper_dmabuf_id of pre-exported sgt_info + */ + hid = hyper_dmabuf_find_hid_exported(dma_buf, + export_remote_attr->remote_domain); + + if (hid.id != -1) { + ret = fastpath_export(hid, export_remote_attr->sz_priv, + export_remote_attr->priv); + + /* return if fastpath_export succeeds or + * gets some fatal error + */ + if (ret <= 0) { + dma_buf_put(dma_buf); + export_remote_attr->hid = hid; + return ret; + } + } + + attachment = dma_buf_attach(dma_buf, hy_drv_priv->dev); + if (IS_ERR(attachment)) { + dev_err(hy_drv_priv->dev, "cannot get attachment\n"); + ret = PTR_ERR(attachment); + goto fail_attach; + } + + sgt = dma_buf_map_attachment(attachment, DMA_BIDIRECTIONAL); + + if (IS_ERR(sgt)) { + dev_err(hy_drv_priv->dev, "cannot map attachment\n"); + ret = PTR_ERR(sgt); + goto fail_map_attachment; + } + + exported = kcalloc(1, sizeof(*exported), GFP_KERNEL); + + if (!exported) { + ret = -ENOMEM; + goto fail_sgt_info_creation; + } + + /* possible truncation */ + if (export_remote_attr->sz_priv > MAX_SIZE_PRIV_DATA) + exported->sz_priv = MAX_SIZE_PRIV_DATA; + else + exported->sz_priv = export_remote_attr->sz_priv; + + /* creating buffer for private data of buffer */ + if (exported->sz_priv != 0) { + exported->priv = kcalloc(1, exported->sz_priv, GFP_KERNEL); + + if (!exported->priv) { + ret = -ENOMEM; + goto fail_priv_creation; + } + } else { + dev_err(hy_drv_priv->dev, "size is 0\n"); + } + + exported->hid = hyper_dmabuf_get_hid(); + + /* no more exported dmabuf allowed */ + if (exported->hid.id == -1) { + dev_err(hy_drv_priv->dev, + "exceeds allowed number of dmabuf to be exported\n"); + ret = -ENOMEM; + goto fail_sgt_info_creation; + } + + exported->rdomid = export_remote_attr->remote_domain; + exported->dma_buf = dma_buf; + exported->valid = true; + + exported->active_sgts = kmalloc(sizeof(struct sgt_list), GFP_KERNEL); + if (!exported->active_sgts) { + ret = -ENOMEM; + goto fail_map_active_sgts; + } + + exported->active_attached = kmalloc(sizeof(struct attachment_list), + GFP_KERNEL); + if (!exported->active_attached) { + ret = -ENOMEM; + goto fail_map_active_attached; + } + + exported->va_kmapped = kmalloc(sizeof(struct kmap_vaddr_list), + GFP_KERNEL); + if (!exported->va_kmapped) { + ret = -ENOMEM; + goto fail_map_va_kmapped; + } + + exported->va_vmapped = kmalloc(sizeof(struct vmap_vaddr_list), + GFP_KERNEL); + if (!exported->va_vmapped) { + ret = -ENOMEM; + goto fail_map_va_vmapped; + } + + exported->active_sgts->sgt = sgt; + exported->active_attached->attach = attachment; + exported->va_kmapped->vaddr = NULL; + exported->va_vmapped->vaddr = NULL; + + /* initialize list of sgt, attachment and vaddr for dmabuf sync + * via shadow dma-buf + */ + INIT_LIST_HEAD(&exported->active_sgts->list); + INIT_LIST_HEAD(&exported->active_attached->list); + INIT_LIST_HEAD(&exported->va_kmapped->list); + INIT_LIST_HEAD(&exported->va_vmapped->list); + + /* copy private data to sgt_info */ + ret = copy_from_user(exported->priv, export_remote_attr->priv, + exported->sz_priv); + + if (ret) { + dev_err(hy_drv_priv->dev, + "failed to load private data\n"); + ret = -EINVAL; + goto fail_export; + } + + pg_info = hyper_dmabuf_ext_pgs(sgt); + if (!pg_info) { + dev_err(hy_drv_priv->dev, + "failed to construct pg_info\n"); + ret = -ENOMEM; + goto fail_export; + } + + exported->nents = pg_info->nents; + + /* now register it to export list */ + hyper_dmabuf_register_exported(exported); + + export_remote_attr->hid = exported->hid; + + ret = send_export_msg(exported, pg_info); + + if (ret < 0) { + dev_err(hy_drv_priv->dev, + "failed to send out the export request\n"); + goto fail_send_request; + } + + /* free pg_info */ + kfree(pg_info->pgs); + kfree(pg_info); + + exported->filp = filp; + + return ret; + +/* Clean-up if error occurs */ + +fail_send_request: + hyper_dmabuf_remove_exported(exported->hid); + + /* free pg_info */ + kfree(pg_info->pgs); + kfree(pg_info); + +fail_export: + kfree(exported->va_vmapped); + +fail_map_va_vmapped: + kfree(exported->va_kmapped); + +fail_map_va_kmapped: + kfree(exported->active_attached); + +fail_map_active_attached: + kfree(exported->active_sgts); + kfree(exported->priv); + +fail_priv_creation: + kfree(exported); + +fail_map_active_sgts: +fail_sgt_info_creation: + dma_buf_unmap_attachment(attachment, sgt, + DMA_BIDIRECTIONAL); + +fail_map_attachment: + dma_buf_detach(dma_buf, attachment); + +fail_attach: + dma_buf_put(dma_buf); + + return ret; +} + +static int hyper_dmabuf_export_fd_ioctl(struct file *filp, void *data) +{ + struct ioctl_hyper_dmabuf_export_fd *export_fd_attr = + (struct ioctl_hyper_dmabuf_export_fd *)data; + struct hyper_dmabuf_bknd_ops *bknd_ops = hy_drv_priv->bknd_ops; + struct imported_sgt_info *imported; + struct hyper_dmabuf_req *req; + struct page **data_pgs; + int op[4]; + int i; + int ret = 0; + + dev_dbg(hy_drv_priv->dev, "%s entry\n", __func__); + + /* look for dmabuf for the id */ + imported = hyper_dmabuf_find_imported(export_fd_attr->hid); + + /* can't find sgt from the table */ + if (!imported) { + dev_err(hy_drv_priv->dev, "can't find the entry\n"); + return -ENOENT; + } + + mutex_lock(&hy_drv_priv->lock); + + imported->importers++; + + /* send notification for export_fd to exporter */ + op[0] = imported->hid.id; + + for (i = 0; i < 3; i++) + op[i+1] = imported->hid.rng_key[i]; + + dev_dbg(hy_drv_priv->dev, "Export FD of buffer {id:%d key:%d %d %d}\n", + imported->hid.id, imported->hid.rng_key[0], + imported->hid.rng_key[1], imported->hid.rng_key[2]); + + req = kcalloc(1, sizeof(*req), GFP_KERNEL); + + if (!req) { + mutex_unlock(&hy_drv_priv->lock); + return -ENOMEM; + } + + hyper_dmabuf_create_req(req, HYPER_DMABUF_EXPORT_FD, &op[0]); + + ret = bknd_ops->send_req(HYPER_DMABUF_DOM_ID(imported->hid), req, true); + + if (ret < 0) { + /* in case of timeout other end eventually will receive request, + * so we need to undo it + */ + hyper_dmabuf_create_req(req, HYPER_DMABUF_EXPORT_FD_FAILED, + &op[0]); + bknd_ops->send_req(HYPER_DMABUF_DOM_ID(imported->hid), req, false); + kfree(req); + dev_err(hy_drv_priv->dev, + "Failed to create sgt or notify exporter\n"); + imported->importers--; + mutex_unlock(&hy_drv_priv->lock); + return ret; + } + + kfree(req); + + if (ret == HYPER_DMABUF_REQ_ERROR) { + dev_err(hy_drv_priv->dev, + "Buffer invalid {id:%d key:%d %d %d}, cannot import\n", + imported->hid.id, imported->hid.rng_key[0], + imported->hid.rng_key[1], imported->hid.rng_key[2]); + + imported->importers--; + mutex_unlock(&hy_drv_priv->lock); + return -EINVAL; + } + + ret = 0; + + dev_dbg(hy_drv_priv->dev, + "Found buffer gref %d off %d\n", + imported->ref_handle, imported->frst_ofst); + + dev_dbg(hy_drv_priv->dev, + "last len %d nents %d domain %d\n", + imported->last_len, imported->nents, + HYPER_DMABUF_DOM_ID(imported->hid)); + + if (!imported->sgt) { + dev_dbg(hy_drv_priv->dev, + "buffer {id:%d key:%d %d %d} pages not mapped yet\n", + imported->hid.id, imported->hid.rng_key[0], + imported->hid.rng_key[1], imported->hid.rng_key[2]); + + data_pgs = bknd_ops->map_shared_pages(imported->ref_handle, + HYPER_DMABUF_DOM_ID(imported->hid), + imported->nents, + &imported->refs_info); + + if (!data_pgs) { + dev_err(hy_drv_priv->dev, + "can't map pages hid {id:%d key:%d %d %d}\n", + imported->hid.id, imported->hid.rng_key[0], + imported->hid.rng_key[1], + imported->hid.rng_key[2]); + + imported->importers--; + + req = kcalloc(1, sizeof(*req), GFP_KERNEL); + + if (!req) { + mutex_unlock(&hy_drv_priv->lock); + return -ENOMEM; + } + + hyper_dmabuf_create_req(req, + HYPER_DMABUF_EXPORT_FD_FAILED, + &op[0]); + bknd_ops->send_req(HYPER_DMABUF_DOM_ID(imported->hid), req, + false); + kfree(req); + mutex_unlock(&hy_drv_priv->lock); + return -EINVAL; + } + + imported->sgt = hyper_dmabuf_create_sgt(data_pgs, + imported->frst_ofst, + imported->last_len, + imported->nents); + + } + + export_fd_attr->fd = hyper_dmabuf_export_fd(imported, + export_fd_attr->flags); + + if (export_fd_attr->fd < 0) { + /* fail to get fd */ + ret = export_fd_attr->fd; + } + + mutex_unlock(&hy_drv_priv->lock); + + dev_dbg(hy_drv_priv->dev, "%s exit\n", __func__); + return ret; +} + +/* unexport dmabuf from the database and send int req to the source domain + * to unmap it. + */ +static void delayed_unexport(struct work_struct *work) +{ + struct hyper_dmabuf_req *req; + struct hyper_dmabuf_bknd_ops *bknd_ops = hy_drv_priv->bknd_ops; + struct exported_sgt_info *exported = + container_of(work, struct exported_sgt_info, unexport.work); + int op[4]; + int i, ret; + + if (!exported) + return; + + dev_dbg(hy_drv_priv->dev, + "Marking buffer {id:%d key:%d %d %d} as invalid\n", + exported->hid.id, exported->hid.rng_key[0], + exported->hid.rng_key[1], exported->hid.rng_key[2]); + + /* no longer valid */ + exported->valid = false; + + req = kcalloc(1, sizeof(*req), GFP_KERNEL); + + if (!req) + return; + + op[0] = exported->hid.id; + + for (i = 0; i < 3; i++) + op[i+1] = exported->hid.rng_key[i]; + + hyper_dmabuf_create_req(req, HYPER_DMABUF_NOTIFY_UNEXPORT, &op[0]); + + /* Now send unexport request to remote domain, marking + * that buffer should not be used anymore + */ + ret = bknd_ops->send_req(exported->rdomid, req, true); + if (ret < 0) { + dev_err(hy_drv_priv->dev, + "unexport message for buffer {id:%d key:%d %d %d} failed\n", + exported->hid.id, exported->hid.rng_key[0], + exported->hid.rng_key[1], exported->hid.rng_key[2]); + } + + kfree(req); + exported->unexport_sched = false; + + /* Immediately clean-up if it has never been exported by importer + * (so no SGT is constructed on importer). + * clean it up later in remote sync when final release ops + * is called (importer does this only when there's no + * no consumer of locally exported FDs) + */ + if (exported->active == 0) { + dev_dbg(hy_drv_priv->dev, + "claning up buffer {id:%d key:%d %d %d} completly\n", + exported->hid.id, exported->hid.rng_key[0], + exported->hid.rng_key[1], exported->hid.rng_key[2]); + + hyper_dmabuf_cleanup_sgt_info(exported, false); + hyper_dmabuf_remove_exported(exported->hid); + + /* register hyper_dmabuf_id to the list for reuse */ + hyper_dmabuf_store_hid(exported->hid); + + if (exported->sz_priv > 0 && !exported->priv) + kfree(exported->priv); + + kfree(exported); + } +} + +/* Schedule unexport of dmabuf. + */ +int hyper_dmabuf_unexport_ioctl(struct file *filp, void *data) +{ + struct ioctl_hyper_dmabuf_unexport *unexport_attr = + (struct ioctl_hyper_dmabuf_unexport *)data; + struct exported_sgt_info *exported; + + dev_dbg(hy_drv_priv->dev, "%s entry\n", __func__); + + /* find dmabuf in export list */ + exported = hyper_dmabuf_find_exported(unexport_attr->hid); + + dev_dbg(hy_drv_priv->dev, + "scheduling unexport of buffer {id:%d key:%d %d %d}\n", + unexport_attr->hid.id, unexport_attr->hid.rng_key[0], + unexport_attr->hid.rng_key[1], unexport_attr->hid.rng_key[2]); + + /* failed to find corresponding entry in export list */ + if (exported == NULL) { + unexport_attr->status = -ENOENT; + return -ENOENT; + } + + if (exported->unexport_sched) + return 0; + + exported->unexport_sched = true; + INIT_DELAYED_WORK(&exported->unexport, delayed_unexport); + schedule_delayed_work(&exported->unexport, + msecs_to_jiffies(unexport_attr->delay_ms)); + + dev_dbg(hy_drv_priv->dev, "%s exit\n", __func__); + return 0; +} + +static int hyper_dmabuf_query_ioctl(struct file *filp, void *data) +{ + struct ioctl_hyper_dmabuf_query *query_attr = + (struct ioctl_hyper_dmabuf_query *)data; + struct exported_sgt_info *exported = NULL; + struct imported_sgt_info *imported = NULL; + int ret = 0; + + if (HYPER_DMABUF_DOM_ID(query_attr->hid) == hy_drv_priv->domid) { + /* query for exported dmabuf */ + exported = hyper_dmabuf_find_exported(query_attr->hid); + if (exported) { + ret = hyper_dmabuf_query_exported(exported, + query_attr->item, + &query_attr->info); + } else { + dev_err(hy_drv_priv->dev, + "hid {id:%d key:%d %d %d} not in exp list\n", + query_attr->hid.id, + query_attr->hid.rng_key[0], + query_attr->hid.rng_key[1], + query_attr->hid.rng_key[2]); + return -ENOENT; + } + } else { + /* query for imported dmabuf */ + imported = hyper_dmabuf_find_imported(query_attr->hid); + if (imported) { + ret = hyper_dmabuf_query_imported(imported, + query_attr->item, + &query_attr->info); + } else { + dev_err(hy_drv_priv->dev, + "hid {id:%d key:%d %d %d} not in imp list\n", + query_attr->hid.id, + query_attr->hid.rng_key[0], + query_attr->hid.rng_key[1], + query_attr->hid.rng_key[2]); + return -ENOENT; + } + } + + return ret; +} + +const struct hyper_dmabuf_ioctl_desc hyper_dmabuf_ioctls[] = { + HYPER_DMABUF_IOCTL_DEF(IOCTL_HYPER_DMABUF_TX_CH_SETUP, + hyper_dmabuf_tx_ch_setup_ioctl, 0), + HYPER_DMABUF_IOCTL_DEF(IOCTL_HYPER_DMABUF_RX_CH_SETUP, + hyper_dmabuf_rx_ch_setup_ioctl, 0), + HYPER_DMABUF_IOCTL_DEF(IOCTL_HYPER_DMABUF_EXPORT_REMOTE, + hyper_dmabuf_export_remote_ioctl, 0), + HYPER_DMABUF_IOCTL_DEF(IOCTL_HYPER_DMABUF_EXPORT_FD, + hyper_dmabuf_export_fd_ioctl, 0), + HYPER_DMABUF_IOCTL_DEF(IOCTL_HYPER_DMABUF_UNEXPORT, + hyper_dmabuf_unexport_ioctl, 0), + HYPER_DMABUF_IOCTL_DEF(IOCTL_HYPER_DMABUF_QUERY, + hyper_dmabuf_query_ioctl, 0), +}; + +long hyper_dmabuf_ioctl(struct file *filp, + unsigned int cmd, unsigned long param) +{ + const struct hyper_dmabuf_ioctl_desc *ioctl = NULL; + unsigned int nr = _IOC_NR(cmd); + int ret; + hyper_dmabuf_ioctl_t func; + char *kdata; + + if (nr > ARRAY_SIZE(hyper_dmabuf_ioctls)) { + dev_err(hy_drv_priv->dev, "invalid ioctl\n"); + return -EINVAL; + } + + ioctl = &hyper_dmabuf_ioctls[nr]; + + func = ioctl->func; + + if (unlikely(!func)) { + dev_err(hy_drv_priv->dev, "no function\n"); + return -EINVAL; + } + + kdata = kmalloc(_IOC_SIZE(cmd), GFP_KERNEL); + if (!kdata) + return -ENOMEM; + + if (copy_from_user(kdata, (void __user *)param, + _IOC_SIZE(cmd)) != 0) { + dev_err(hy_drv_priv->dev, + "failed to copy from user arguments\n"); + ret = -EFAULT; + goto ioctl_error; + } + + ret = func(filp, kdata); + + if (copy_to_user((void __user *)param, kdata, + _IOC_SIZE(cmd)) != 0) { + dev_err(hy_drv_priv->dev, + "failed to copy to user arguments\n"); + ret = -EFAULT; + goto ioctl_error; + } + +ioctl_error: + kfree(kdata); + + return ret; +} diff --git a/drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_ioctl.h b/drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_ioctl.h new file mode 100644 index 000000000000..5991a87b194f --- /dev/null +++ b/drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_ioctl.h @@ -0,0 +1,50 @@ +/* + * Copyright © 2017 Intel Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + * + */ + +#ifndef __HYPER_DMABUF_IOCTL_H__ +#define __HYPER_DMABUF_IOCTL_H__ + +typedef int (*hyper_dmabuf_ioctl_t)(struct file *filp, void *data); + +struct hyper_dmabuf_ioctl_desc { + unsigned int cmd; + int flags; + hyper_dmabuf_ioctl_t func; + const char *name; +}; + +#define HYPER_DMABUF_IOCTL_DEF(ioctl, _func, _flags) \ + [_IOC_NR(ioctl)] = { \ + .cmd = ioctl, \ + .func = _func, \ + .flags = _flags, \ + .name = #ioctl \ + } + +long hyper_dmabuf_ioctl(struct file *filp, + unsigned int cmd, unsigned long param); + +int hyper_dmabuf_unexport_ioctl(struct file *filp, void *data); + +#endif //__HYPER_DMABUF_IOCTL_H__ diff --git a/drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_list.c b/drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_list.c new file mode 100644 index 000000000000..bba6d1d607a8 --- /dev/null +++ b/drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_list.c @@ -0,0 +1,293 @@ +/* + * Copyright © 2017 Intel Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + * + * Authors: + * Dongwon Kim + * Mateusz Polrola + * + */ + +#include +#include +#include +#include +#include +#include "hyper_dmabuf_drv.h" +#include "hyper_dmabuf_list.h" +#include "hyper_dmabuf_id.h" +#include "hyper_dmabuf_event.h" + +DECLARE_HASHTABLE(hyper_dmabuf_hash_imported, MAX_ENTRY_IMPORTED); +DECLARE_HASHTABLE(hyper_dmabuf_hash_exported, MAX_ENTRY_EXPORTED); + +#ifdef CONFIG_HYPER_DMABUF_SYSFS +static ssize_t hyper_dmabuf_imported_show(struct device *drv, + struct device_attribute *attr, + char *buf) +{ + struct list_entry_imported *info_entry; + int bkt; + ssize_t count = 0; + size_t total = 0; + + hash_for_each(hyper_dmabuf_hash_imported, bkt, info_entry, node) { + hyper_dmabuf_id_t hid = info_entry->imported->hid; + int nents = info_entry->imported->nents; + bool valid = info_entry->imported->valid; + int num_importers = info_entry->imported->importers; + + total += nents; + count += scnprintf(buf + count, PAGE_SIZE - count, + "hid:{%d %d %d %d}, nent:%d, v:%c, numi:%d\n", + hid.id, hid.rng_key[0], hid.rng_key[1], + hid.rng_key[2], nents, (valid ? 't' : 'f'), + num_importers); + } + count += scnprintf(buf + count, PAGE_SIZE - count, + "total nents: %lu\n", total); + + return count; +} + +static ssize_t hyper_dmabuf_exported_show(struct device *drv, + struct device_attribute *attr, + char *buf) +{ + struct list_entry_exported *info_entry; + int bkt; + ssize_t count = 0; + size_t total = 0; + + hash_for_each(hyper_dmabuf_hash_exported, bkt, info_entry, node) { + hyper_dmabuf_id_t hid = info_entry->exported->hid; + int nents = info_entry->exported->nents; + bool valid = info_entry->exported->valid; + int importer_exported = info_entry->exported->active; + + total += nents; + count += scnprintf(buf + count, PAGE_SIZE - count, + "hid:{%d %d %d %d}, nent:%d, v:%c, ie:%d\n", + hid.id, hid.rng_key[0], hid.rng_key[1], + hid.rng_key[2], nents, (valid ? 't' : 'f'), + importer_exported); + } + count += scnprintf(buf + count, PAGE_SIZE - count, + "total nents: %lu\n", total); + + return count; +} + +static DEVICE_ATTR(imported, 0400, hyper_dmabuf_imported_show, NULL); +static DEVICE_ATTR(exported, 0400, hyper_dmabuf_exported_show, NULL); + +int hyper_dmabuf_register_sysfs(struct device *dev) +{ + int err; + + err = device_create_file(dev, &dev_attr_imported); + if (err < 0) + goto err1; + err = device_create_file(dev, &dev_attr_exported); + if (err < 0) + goto err2; + + return 0; +err2: + device_remove_file(dev, &dev_attr_imported); +err1: + return -1; +} + +int hyper_dmabuf_unregister_sysfs(struct device *dev) +{ + device_remove_file(dev, &dev_attr_imported); + device_remove_file(dev, &dev_attr_exported); + return 0; +} + +#endif + +int hyper_dmabuf_table_init(void) +{ + hash_init(hyper_dmabuf_hash_imported); + hash_init(hyper_dmabuf_hash_exported); + return 0; +} + +int hyper_dmabuf_table_destroy(void) +{ + /* TODO: cleanup hyper_dmabuf_hash_imported + * and hyper_dmabuf_hash_exported + */ + return 0; +} + +int hyper_dmabuf_register_exported(struct exported_sgt_info *exported) +{ + struct list_entry_exported *info_entry; + + info_entry = kmalloc(sizeof(*info_entry), GFP_KERNEL); + + if (!info_entry) + return -ENOMEM; + + info_entry->exported = exported; + + hash_add(hyper_dmabuf_hash_exported, &info_entry->node, + info_entry->exported->hid.id); + + return 0; +} + +int hyper_dmabuf_register_imported(struct imported_sgt_info *imported) +{ + struct list_entry_imported *info_entry; + + info_entry = kmalloc(sizeof(*info_entry), GFP_KERNEL); + + if (!info_entry) + return -ENOMEM; + + info_entry->imported = imported; + + hash_add(hyper_dmabuf_hash_imported, &info_entry->node, + info_entry->imported->hid.id); + + return 0; +} + +struct exported_sgt_info *hyper_dmabuf_find_exported(hyper_dmabuf_id_t hid) +{ + struct list_entry_exported *info_entry; + int bkt; + + hash_for_each(hyper_dmabuf_hash_exported, bkt, info_entry, node) + /* checking hid.id first */ + if (info_entry->exported->hid.id == hid.id) { + /* then key is compared */ + if (hyper_dmabuf_hid_keycomp(info_entry->exported->hid, + hid)) + return info_entry->exported; + + /* if key is unmatched, given HID is invalid, + * so returning NULL + */ + break; + } + + return NULL; +} + +/* search for pre-exported sgt and return id of it if it exist */ +hyper_dmabuf_id_t hyper_dmabuf_find_hid_exported(struct dma_buf *dmabuf, + int domid) +{ + struct list_entry_exported *info_entry; + hyper_dmabuf_id_t hid = {-1, {0, 0, 0} }; + int bkt; + + hash_for_each(hyper_dmabuf_hash_exported, bkt, info_entry, node) + if (info_entry->exported->dma_buf == dmabuf && + info_entry->exported->rdomid == domid) + return info_entry->exported->hid; + + return hid; +} + +struct imported_sgt_info *hyper_dmabuf_find_imported(hyper_dmabuf_id_t hid) +{ + struct list_entry_imported *info_entry; + int bkt; + + hash_for_each(hyper_dmabuf_hash_imported, bkt, info_entry, node) + /* checking hid.id first */ + if (info_entry->imported->hid.id == hid.id) { + /* then key is compared */ + if (hyper_dmabuf_hid_keycomp(info_entry->imported->hid, + hid)) + return info_entry->imported; + /* if key is unmatched, given HID is invalid, + * so returning NULL + */ + break; + } + + return NULL; +} + +int hyper_dmabuf_remove_exported(hyper_dmabuf_id_t hid) +{ + struct list_entry_exported *info_entry; + int bkt; + + hash_for_each(hyper_dmabuf_hash_exported, bkt, info_entry, node) + /* checking hid.id first */ + if (info_entry->exported->hid.id == hid.id) { + /* then key is compared */ + if (hyper_dmabuf_hid_keycomp(info_entry->exported->hid, + hid)) { + hash_del(&info_entry->node); + kfree(info_entry); + return 0; + } + + break; + } + + return -ENOENT; +} + +int hyper_dmabuf_remove_imported(hyper_dmabuf_id_t hid) +{ + struct list_entry_imported *info_entry; + int bkt; + + hash_for_each(hyper_dmabuf_hash_imported, bkt, info_entry, node) + /* checking hid.id first */ + if (info_entry->imported->hid.id == hid.id) { + /* then key is compared */ + if (hyper_dmabuf_hid_keycomp(info_entry->imported->hid, + hid)) { + hash_del(&info_entry->node); + kfree(info_entry); + return 0; + } + + break; + } + + return -ENOENT; +} + +void hyper_dmabuf_foreach_exported( + void (*func)(struct exported_sgt_info *, void *attr), + void *attr) +{ + struct list_entry_exported *info_entry; + struct hlist_node *tmp; + int bkt; + + hash_for_each_safe(hyper_dmabuf_hash_exported, bkt, tmp, + info_entry, node) { + func(info_entry->exported, attr); + } +} diff --git a/drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_list.h b/drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_list.h new file mode 100644 index 000000000000..f7102f5db75d --- /dev/null +++ b/drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_list.h @@ -0,0 +1,71 @@ +/* + * Copyright © 2017 Intel Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + * + */ + +#ifndef __HYPER_DMABUF_LIST_H__ +#define __HYPER_DMABUF_LIST_H__ + +#include "hyper_dmabuf_struct.h" + +/* number of bits to be used for exported dmabufs hash table */ +#define MAX_ENTRY_EXPORTED 7 +/* number of bits to be used for imported dmabufs hash table */ +#define MAX_ENTRY_IMPORTED 7 + +struct list_entry_exported { + struct exported_sgt_info *exported; + struct hlist_node node; +}; + +struct list_entry_imported { + struct imported_sgt_info *imported; + struct hlist_node node; +}; + +int hyper_dmabuf_table_init(void); + +int hyper_dmabuf_table_destroy(void); + +int hyper_dmabuf_register_exported(struct exported_sgt_info *info); + +/* search for pre-exported sgt and return id of it if it exist */ +hyper_dmabuf_id_t hyper_dmabuf_find_hid_exported(struct dma_buf *dmabuf, + int domid); + +int hyper_dmabuf_register_imported(struct imported_sgt_info *info); + +struct exported_sgt_info *hyper_dmabuf_find_exported(hyper_dmabuf_id_t hid); + +struct imported_sgt_info *hyper_dmabuf_find_imported(hyper_dmabuf_id_t hid); + +int hyper_dmabuf_remove_exported(hyper_dmabuf_id_t hid); + +int hyper_dmabuf_remove_imported(hyper_dmabuf_id_t hid); + +void hyper_dmabuf_foreach_exported(void (*func)(struct exported_sgt_info *, + void *attr), void *attr); + +int hyper_dmabuf_register_sysfs(struct device *dev); +int hyper_dmabuf_unregister_sysfs(struct device *dev); + +#endif /* __HYPER_DMABUF_LIST_H__ */ diff --git a/drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_msg.c b/drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_msg.c new file mode 100644 index 000000000000..37ee894ec418 --- /dev/null +++ b/drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_msg.c @@ -0,0 +1,413 @@ +/* + * Copyright © 2017 Intel Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + * + * Authors: + * Dongwon Kim + * Mateusz Polrola + * + */ + +#include +#include +#include +#include +#include "hyper_dmabuf_drv.h" +#include "hyper_dmabuf_msg.h" +#include "hyper_dmabuf_remote_sync.h" +#include "hyper_dmabuf_event.h" +#include "hyper_dmabuf_list.h" + +struct cmd_process { + struct work_struct work; + struct hyper_dmabuf_req *rq; + int domid; +}; + +void hyper_dmabuf_create_req(struct hyper_dmabuf_req *req, + enum hyper_dmabuf_command cmd, int *op) +{ + int i; + + req->stat = HYPER_DMABUF_REQ_NOT_RESPONDED; + req->cmd = cmd; + + switch (cmd) { + /* as exporter, commands to importer */ + case HYPER_DMABUF_EXPORT: + /* exporting pages for dmabuf */ + /* command : HYPER_DMABUF_EXPORT, + * op0~op3 : hyper_dmabuf_id + * op4 : number of pages to be shared + * op5 : offset of data in the first page + * op6 : length of data in the last page + * op7 : top-level reference number for shared pages + * op8 : size of private data (from op9) + * op9 ~ : Driver-specific private data + * (e.g. graphic buffer's meta info) + */ + + memcpy(&req->op[0], &op[0], 9 * sizeof(int) + op[8]); + break; + + case HYPER_DMABUF_NOTIFY_UNEXPORT: + /* destroy sg_list for hyper_dmabuf_id on remote side */ + /* command : DMABUF_DESTROY, + * op0~op3 : hyper_dmabuf_id_t hid + */ + + for (i = 0; i < 4; i++) + req->op[i] = op[i]; + break; + + case HYPER_DMABUF_EXPORT_FD: + case HYPER_DMABUF_EXPORT_FD_FAILED: + /* dmabuf fd is being created on imported side or importing + * failed + * + * command : HYPER_DMABUF_EXPORT_FD or + * HYPER_DMABUF_EXPORT_FD_FAILED, + * op0~op3 : hyper_dmabuf_id + */ + + for (i = 0; i < 4; i++) + req->op[i] = op[i]; + break; + + case HYPER_DMABUF_OPS_TO_REMOTE: + /* notifying dmabuf map/unmap to importer (probably not needed) + * for dmabuf synchronization + */ + break; + + case HYPER_DMABUF_OPS_TO_SOURCE: + /* notifying dmabuf map/unmap to exporter, map will make + * the driver to do shadow mapping or unmapping for + * synchronization with original exporter (e.g. i915) + * + * command : DMABUF_OPS_TO_SOURCE. + * op0~3 : hyper_dmabuf_id + * op4 : map(=1)/unmap(=2)/attach(=3)/detach(=4) + */ + for (i = 0; i < 5; i++) + req->op[i] = op[i]; + break; + + default: + /* no command found */ + return; + } +} + +static void cmd_process_work(struct work_struct *work) +{ + struct imported_sgt_info *imported; + struct cmd_process *proc = container_of(work, + struct cmd_process, work); + struct hyper_dmabuf_req *req; + hyper_dmabuf_id_t hid; + int domid; + int i; + + req = proc->rq; + domid = proc->domid; + + switch (req->cmd) { + case HYPER_DMABUF_EXPORT: + /* exporting pages for dmabuf */ + /* command : HYPER_DMABUF_EXPORT, + * op0~op3 : hyper_dmabuf_id + * op4 : number of pages to be shared + * op5 : offset of data in the first page + * op6 : length of data in the last page + * op7 : top-level reference number for shared pages + * op8 : size of private data (from op9) + * op9 ~ : Driver-specific private data + * (e.g. graphic buffer's meta info) + */ + + /* if nents == 0, it means it is a message only for + * priv synchronization. for existing imported_sgt_info + * so not creating a new one + */ + if (req->op[4] == 0) { + hyper_dmabuf_id_t exist = {req->op[0], + {req->op[1], req->op[2], + req->op[3] } }; + + imported = hyper_dmabuf_find_imported(exist); + + if (!imported) { + dev_err(hy_drv_priv->dev, + "Can't find imported sgt_info\n"); + break; + } + + /* if size of new private data is different, + * we reallocate it. + */ + if (imported->sz_priv != req->op[8]) { + kfree(imported->priv); + imported->sz_priv = req->op[8]; + imported->priv = kcalloc(1, req->op[8], + GFP_KERNEL); + if (!imported->priv) { + /* set it invalid */ + imported->valid = 0; + break; + } + } + + /* updating priv data */ + memcpy(imported->priv, &req->op[9], req->op[8]); + +#ifdef CONFIG_HYPER_DMABUF_EVENT_GEN + /* generating import event */ + hyper_dmabuf_import_event(imported->hid); +#endif + + break; + } + + imported = kcalloc(1, sizeof(*imported), GFP_KERNEL); + + if (!imported) + break; + + imported->sz_priv = req->op[8]; + imported->priv = kcalloc(1, req->op[8], GFP_KERNEL); + + if (!imported->priv) { + kfree(imported); + break; + } + + imported->hid.id = req->op[0]; + + for (i = 0; i < 3; i++) + imported->hid.rng_key[i] = req->op[i+1]; + + imported->nents = req->op[4]; + imported->frst_ofst = req->op[5]; + imported->last_len = req->op[6]; + imported->ref_handle = req->op[7]; + + dev_dbg(hy_drv_priv->dev, "DMABUF was exported\n"); + dev_dbg(hy_drv_priv->dev, "\thid{id:%d key:%d %d %d}\n", + req->op[0], req->op[1], req->op[2], + req->op[3]); + dev_dbg(hy_drv_priv->dev, "\tnents %d\n", req->op[4]); + dev_dbg(hy_drv_priv->dev, "\tfirst offset %d\n", req->op[5]); + dev_dbg(hy_drv_priv->dev, "\tlast len %d\n", req->op[6]); + dev_dbg(hy_drv_priv->dev, "\tgrefid %d\n", req->op[7]); + + memcpy(imported->priv, &req->op[9], req->op[8]); + + imported->valid = true; + hyper_dmabuf_register_imported(imported); + +#ifdef CONFIG_HYPER_DMABUF_EVENT_GEN + /* generating import event */ + hyper_dmabuf_import_event(imported->hid); +#endif + + break; + + case HYPER_DMABUF_OPS_TO_SOURCE: + /* notifying dmabuf map/unmap to exporter, map will + * make the driver to do shadow mapping + * or unmapping for synchronization with original + * exporter (e.g. i915) + * + * command : DMABUF_OPS_TO_SOURCE. + * op0~3 : hyper_dmabuf_id + * op1 : enum hyper_dmabuf_ops {....} + */ + dev_dbg(hy_drv_priv->dev, + "%s: HYPER_DMABUF_OPS_TO_SOURCE\n", __func__); + + hid.id = req->op[0]; + hid.rng_key[0] = req->op[1]; + hid.rng_key[1] = req->op[2]; + hid.rng_key[2] = req->op[3]; + hyper_dmabuf_remote_sync(hid, req->op[4]); + + break; + + + case HYPER_DMABUF_OPS_TO_REMOTE: + /* notifying dmabuf map/unmap to importer + * (probably not needed) for dmabuf synchronization + */ + break; + + default: + /* shouldn't get here */ + break; + } + + kfree(req); + kfree(proc); +} + +int hyper_dmabuf_msg_parse(int domid, struct hyper_dmabuf_req *req) +{ + struct cmd_process *proc; + struct hyper_dmabuf_req *temp_req; + struct imported_sgt_info *imported; + struct exported_sgt_info *exported; + hyper_dmabuf_id_t hid; + + if (!req) { + dev_err(hy_drv_priv->dev, "request is NULL\n"); + return -EINVAL; + } + + hid.id = req->op[0]; + hid.rng_key[0] = req->op[1]; + hid.rng_key[1] = req->op[2]; + hid.rng_key[2] = req->op[3]; + + if ((req->cmd < HYPER_DMABUF_EXPORT) || + (req->cmd > HYPER_DMABUF_OPS_TO_SOURCE)) { + dev_err(hy_drv_priv->dev, "invalid command\n"); + return -EINVAL; + } + + req->stat = HYPER_DMABUF_REQ_PROCESSED; + + /* HYPER_DMABUF_DESTROY requires immediate + * follow up so can't be processed in workqueue + */ + if (req->cmd == HYPER_DMABUF_NOTIFY_UNEXPORT) { + /* destroy sg_list for hyper_dmabuf_id on remote side */ + /* command : HYPER_DMABUF_NOTIFY_UNEXPORT, + * op0~3 : hyper_dmabuf_id + */ + dev_dbg(hy_drv_priv->dev, + "processing HYPER_DMABUF_NOTIFY_UNEXPORT\n"); + + imported = hyper_dmabuf_find_imported(hid); + + if (imported) { + /* if anything is still using dma_buf */ + if (imported->importers) { + /* Buffer is still in use, just mark that + * it should not be allowed to export its fd + * anymore. + */ + imported->valid = false; + } else { + /* No one is using buffer, remove it from + * imported list + */ + hyper_dmabuf_remove_imported(hid); + kfree(imported->priv); + kfree(imported); + } + } else { + req->stat = HYPER_DMABUF_REQ_ERROR; + } + + return req->cmd; + } + + /* synchronous dma_buf_fd export */ + if (req->cmd == HYPER_DMABUF_EXPORT_FD) { + /* find a corresponding SGT for the id */ + dev_dbg(hy_drv_priv->dev, + "HYPER_DMABUF_EXPORT_FD for {id:%d key:%d %d %d}\n", + hid.id, hid.rng_key[0], hid.rng_key[1], hid.rng_key[2]); + + exported = hyper_dmabuf_find_exported(hid); + + if (!exported) { + dev_err(hy_drv_priv->dev, + "buffer {id:%d key:%d %d %d} not found\n", + hid.id, hid.rng_key[0], hid.rng_key[1], + hid.rng_key[2]); + + req->stat = HYPER_DMABUF_REQ_ERROR; + } else if (!exported->valid) { + dev_dbg(hy_drv_priv->dev, + "Buffer no longer valid {id:%d key:%d %d %d}\n", + hid.id, hid.rng_key[0], hid.rng_key[1], + hid.rng_key[2]); + + req->stat = HYPER_DMABUF_REQ_ERROR; + } else { + dev_dbg(hy_drv_priv->dev, + "Buffer still valid {id:%d key:%d %d %d}\n", + hid.id, hid.rng_key[0], hid.rng_key[1], + hid.rng_key[2]); + + exported->active++; + req->stat = HYPER_DMABUF_REQ_PROCESSED; + } + return req->cmd; + } + + if (req->cmd == HYPER_DMABUF_EXPORT_FD_FAILED) { + dev_dbg(hy_drv_priv->dev, + "HYPER_DMABUF_EXPORT_FD_FAILED for {id:%d key:%d %d %d}\n", + hid.id, hid.rng_key[0], hid.rng_key[1], hid.rng_key[2]); + + exported = hyper_dmabuf_find_exported(hid); + + if (!exported) { + dev_err(hy_drv_priv->dev, + "buffer {id:%d key:%d %d %d} not found\n", + hid.id, hid.rng_key[0], hid.rng_key[1], + hid.rng_key[2]); + + req->stat = HYPER_DMABUF_REQ_ERROR; + } else { + exported->active--; + req->stat = HYPER_DMABUF_REQ_PROCESSED; + } + return req->cmd; + } + + dev_dbg(hy_drv_priv->dev, + "%s: putting request to workqueue\n", __func__); + temp_req = kmalloc(sizeof(*temp_req), GFP_ATOMIC); + + if (!temp_req) + return -ENOMEM; + + memcpy(temp_req, req, sizeof(*temp_req)); + + proc = kcalloc(1, sizeof(struct cmd_process), GFP_ATOMIC); + + if (!proc) { + kfree(temp_req); + return -ENOMEM; + } + + proc->rq = temp_req; + proc->domid = domid; + + INIT_WORK(&(proc->work), cmd_process_work); + + queue_work(hy_drv_priv->work_queue, &(proc->work)); + + return req->cmd; +} diff --git a/drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_msg.h b/drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_msg.h new file mode 100644 index 000000000000..9c8a76bf261e --- /dev/null +++ b/drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_msg.h @@ -0,0 +1,87 @@ +/* + * Copyright © 2017 Intel Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + * + */ + +#ifndef __HYPER_DMABUF_MSG_H__ +#define __HYPER_DMABUF_MSG_H__ + +#define MAX_NUMBER_OF_OPERANDS 64 + +struct hyper_dmabuf_req { + unsigned int req_id; + unsigned int stat; + unsigned int cmd; + unsigned int op[MAX_NUMBER_OF_OPERANDS]; +}; + +struct hyper_dmabuf_resp { + unsigned int resp_id; + unsigned int stat; + unsigned int cmd; + unsigned int op[MAX_NUMBER_OF_OPERANDS]; +}; + +enum hyper_dmabuf_command { + HYPER_DMABUF_EXPORT = 0x10, + HYPER_DMABUF_EXPORT_FD, + HYPER_DMABUF_EXPORT_FD_FAILED, + HYPER_DMABUF_NOTIFY_UNEXPORT, + HYPER_DMABUF_OPS_TO_REMOTE, + HYPER_DMABUF_OPS_TO_SOURCE, +}; + +enum hyper_dmabuf_ops { + HYPER_DMABUF_OPS_ATTACH = 0x1000, + HYPER_DMABUF_OPS_DETACH, + HYPER_DMABUF_OPS_MAP, + HYPER_DMABUF_OPS_UNMAP, + HYPER_DMABUF_OPS_RELEASE, + HYPER_DMABUF_OPS_BEGIN_CPU_ACCESS, + HYPER_DMABUF_OPS_END_CPU_ACCESS, + HYPER_DMABUF_OPS_KMAP_ATOMIC, + HYPER_DMABUF_OPS_KUNMAP_ATOMIC, + HYPER_DMABUF_OPS_KMAP, + HYPER_DMABUF_OPS_KUNMAP, + HYPER_DMABUF_OPS_MMAP, + HYPER_DMABUF_OPS_VMAP, + HYPER_DMABUF_OPS_VUNMAP, +}; + +enum hyper_dmabuf_req_feedback { + HYPER_DMABUF_REQ_PROCESSED = 0x100, + HYPER_DMABUF_REQ_NEEDS_FOLLOW_UP, + HYPER_DMABUF_REQ_ERROR, + HYPER_DMABUF_REQ_NOT_RESPONDED +}; + +/* create a request packet with given command and operands */ +void hyper_dmabuf_create_req(struct hyper_dmabuf_req *req, + enum hyper_dmabuf_command command, + int *operands); + +/* parse incoming request packet (or response) and take + * appropriate actions for those + */ +int hyper_dmabuf_msg_parse(int domid, struct hyper_dmabuf_req *req); + +#endif // __HYPER_DMABUF_MSG_H__ diff --git a/drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_ops.c b/drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_ops.c new file mode 100644 index 000000000000..915743741897 --- /dev/null +++ b/drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_ops.c @@ -0,0 +1,414 @@ +/* + * Copyright © 2017 Intel Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + * + * Authors: + * Dongwon Kim + * Mateusz Polrola + * + */ + +#include +#include +#include +#include +#include "hyper_dmabuf_drv.h" +#include "hyper_dmabuf_struct.h" +#include "hyper_dmabuf_ops.h" +#include "hyper_dmabuf_sgl_proc.h" +#include "hyper_dmabuf_id.h" +#include "hyper_dmabuf_msg.h" +#include "hyper_dmabuf_list.h" + +#define WAIT_AFTER_SYNC_REQ 0 +#define REFS_PER_PAGE (PAGE_SIZE/sizeof(grant_ref_t)) + +static int dmabuf_refcount(struct dma_buf *dma_buf) +{ + if ((dma_buf != NULL) && (dma_buf->file != NULL)) + return file_count(dma_buf->file); + + return -EINVAL; +} + +static int sync_request(hyper_dmabuf_id_t hid, int dmabuf_ops) +{ + struct hyper_dmabuf_req *req; + struct hyper_dmabuf_bknd_ops *bknd_ops = hy_drv_priv->bknd_ops; + int op[5]; + int i; + int ret; + + op[0] = hid.id; + + for (i = 0; i < 3; i++) + op[i+1] = hid.rng_key[i]; + + op[4] = dmabuf_ops; + + req = kcalloc(1, sizeof(*req), GFP_KERNEL); + + if (!req) + return -ENOMEM; + + hyper_dmabuf_create_req(req, HYPER_DMABUF_OPS_TO_SOURCE, &op[0]); + + /* send request and wait for a response */ + ret = bknd_ops->send_req(HYPER_DMABUF_DOM_ID(hid), req, + WAIT_AFTER_SYNC_REQ); + + if (ret < 0) { + dev_dbg(hy_drv_priv->dev, + "dmabuf sync request failed:%d\n", req->op[4]); + } + + kfree(req); + + return ret; +} + +static int hyper_dmabuf_ops_attach(struct dma_buf *dmabuf, + struct device *dev, + struct dma_buf_attachment *attach) +{ + struct imported_sgt_info *imported; + int ret; + + if (!attach->dmabuf->priv) + return -EINVAL; + + imported = (struct imported_sgt_info *)attach->dmabuf->priv; + + ret = sync_request(imported->hid, HYPER_DMABUF_OPS_ATTACH); + + return ret; +} + +static void hyper_dmabuf_ops_detach(struct dma_buf *dmabuf, + struct dma_buf_attachment *attach) +{ + struct imported_sgt_info *imported; + int ret; + + if (!attach->dmabuf->priv) + return; + + imported = (struct imported_sgt_info *)attach->dmabuf->priv; + + ret = sync_request(imported->hid, HYPER_DMABUF_OPS_DETACH); +} + +static struct sg_table *hyper_dmabuf_ops_map( + struct dma_buf_attachment *attachment, + enum dma_data_direction dir) +{ + struct sg_table *st; + struct imported_sgt_info *imported; + struct pages_info *pg_info; + int ret; + + if (!attachment->dmabuf->priv) + return NULL; + + imported = (struct imported_sgt_info *)attachment->dmabuf->priv; + + /* extract pages from sgt */ + pg_info = hyper_dmabuf_ext_pgs(imported->sgt); + + if (!pg_info) + return NULL; + + /* create a new sg_table with extracted pages */ + st = hyper_dmabuf_create_sgt(pg_info->pgs, pg_info->frst_ofst, + pg_info->last_len, pg_info->nents); + if (!st) + goto err_free_sg; + + if (!dma_map_sg(attachment->dev, st->sgl, st->nents, dir)) + goto err_free_sg; + + ret = sync_request(imported->hid, HYPER_DMABUF_OPS_MAP); + + kfree(pg_info->pgs); + kfree(pg_info); + + return st; + +err_free_sg: + if (st) { + sg_free_table(st); + kfree(st); + } + + kfree(pg_info->pgs); + kfree(pg_info); + + return NULL; +} + +static void hyper_dmabuf_ops_unmap(struct dma_buf_attachment *attachment, + struct sg_table *sg, + enum dma_data_direction dir) +{ + struct imported_sgt_info *imported; + int ret; + + if (!attachment->dmabuf->priv) + return; + + imported = (struct imported_sgt_info *)attachment->dmabuf->priv; + + dma_unmap_sg(attachment->dev, sg->sgl, sg->nents, dir); + + sg_free_table(sg); + kfree(sg); + + ret = sync_request(imported->hid, HYPER_DMABUF_OPS_UNMAP); +} + +static void hyper_dmabuf_ops_release(struct dma_buf *dma_buf) +{ + struct imported_sgt_info *imported; + struct hyper_dmabuf_bknd_ops *bknd_ops = hy_drv_priv->bknd_ops; + int ret; + int finish; + + if (!dma_buf->priv) + return; + + imported = (struct imported_sgt_info *)dma_buf->priv; + + if (!dmabuf_refcount(imported->dma_buf)) + imported->dma_buf = NULL; + + imported->importers--; + + if (imported->importers == 0) { + bknd_ops->unmap_shared_pages(&imported->refs_info, + imported->nents); + + if (imported->sgt) { + sg_free_table(imported->sgt); + kfree(imported->sgt); + imported->sgt = NULL; + } + } + + finish = imported && !imported->valid && + !imported->importers; + + ret = sync_request(imported->hid, HYPER_DMABUF_OPS_RELEASE); + + /* + * Check if buffer is still valid and if not remove it + * from imported list. That has to be done after sending + * sync request + */ + if (finish) { + hyper_dmabuf_remove_imported(imported->hid); + kfree(imported->priv); + kfree(imported); + } +} + +static int hyper_dmabuf_ops_begin_cpu_access(struct dma_buf *dmabuf, + enum dma_data_direction dir) +{ + struct imported_sgt_info *imported; + int ret; + + if (!dmabuf->priv) + return -EINVAL; + + imported = (struct imported_sgt_info *)dmabuf->priv; + + ret = sync_request(imported->hid, HYPER_DMABUF_OPS_BEGIN_CPU_ACCESS); + + return ret; +} + +static int hyper_dmabuf_ops_end_cpu_access(struct dma_buf *dmabuf, + enum dma_data_direction dir) +{ + struct imported_sgt_info *imported; + int ret; + + if (!dmabuf->priv) + return -EINVAL; + + imported = (struct imported_sgt_info *)dmabuf->priv; + + ret = sync_request(imported->hid, HYPER_DMABUF_OPS_END_CPU_ACCESS); + + return 0; +} + +static void *hyper_dmabuf_ops_kmap_atomic(struct dma_buf *dmabuf, + unsigned long pgnum) +{ + struct imported_sgt_info *imported; + int ret; + + if (!dmabuf->priv) + return NULL; + + imported = (struct imported_sgt_info *)dmabuf->priv; + + ret = sync_request(imported->hid, HYPER_DMABUF_OPS_KMAP_ATOMIC); + + /* TODO: NULL for now. Need to return the addr of mapped region */ + return NULL; +} + +static void hyper_dmabuf_ops_kunmap_atomic(struct dma_buf *dmabuf, + unsigned long pgnum, void *vaddr) +{ + struct imported_sgt_info *imported; + int ret; + + if (!dmabuf->priv) + return; + + imported = (struct imported_sgt_info *)dmabuf->priv; + + ret = sync_request(imported->hid, HYPER_DMABUF_OPS_KUNMAP_ATOMIC); +} + +static void *hyper_dmabuf_ops_kmap(struct dma_buf *dmabuf, unsigned long pgnum) +{ + struct imported_sgt_info *imported; + int ret; + + if (!dmabuf->priv) + return NULL; + + imported = (struct imported_sgt_info *)dmabuf->priv; + + ret = sync_request(imported->hid, HYPER_DMABUF_OPS_KMAP); + + /* for now NULL.. need to return the address of mapped region */ + return NULL; +} + +static void hyper_dmabuf_ops_kunmap(struct dma_buf *dmabuf, unsigned long pgnum, + void *vaddr) +{ + struct imported_sgt_info *imported; + int ret; + + if (!dmabuf->priv) + return; + + imported = (struct imported_sgt_info *)dmabuf->priv; + + ret = sync_request(imported->hid, HYPER_DMABUF_OPS_KUNMAP); +} + +static int hyper_dmabuf_ops_mmap(struct dma_buf *dmabuf, + struct vm_area_struct *vma) +{ + struct imported_sgt_info *imported; + int ret; + + if (!dmabuf->priv) + return -EINVAL; + + imported = (struct imported_sgt_info *)dmabuf->priv; + + ret = sync_request(imported->hid, HYPER_DMABUF_OPS_MMAP); + + return ret; +} + +static void *hyper_dmabuf_ops_vmap(struct dma_buf *dmabuf) +{ + struct imported_sgt_info *imported; + int ret; + + if (!dmabuf->priv) + return NULL; + + imported = (struct imported_sgt_info *)dmabuf->priv; + + ret = sync_request(imported->hid, HYPER_DMABUF_OPS_VMAP); + + return NULL; +} + +static void hyper_dmabuf_ops_vunmap(struct dma_buf *dmabuf, void *vaddr) +{ + struct imported_sgt_info *imported; + int ret; + + if (!dmabuf->priv) + return; + + imported = (struct imported_sgt_info *)dmabuf->priv; + + ret = sync_request(imported->hid, HYPER_DMABUF_OPS_VUNMAP); +} + +static const struct dma_buf_ops hyper_dmabuf_ops = { + .attach = hyper_dmabuf_ops_attach, + .detach = hyper_dmabuf_ops_detach, + .map_dma_buf = hyper_dmabuf_ops_map, + .unmap_dma_buf = hyper_dmabuf_ops_unmap, + .release = hyper_dmabuf_ops_release, + .begin_cpu_access = (void *)hyper_dmabuf_ops_begin_cpu_access, + .end_cpu_access = (void *)hyper_dmabuf_ops_end_cpu_access, + .map_atomic = hyper_dmabuf_ops_kmap_atomic, + .unmap_atomic = hyper_dmabuf_ops_kunmap_atomic, + .map = hyper_dmabuf_ops_kmap, + .unmap = hyper_dmabuf_ops_kunmap, + .mmap = hyper_dmabuf_ops_mmap, + .vmap = hyper_dmabuf_ops_vmap, + .vunmap = hyper_dmabuf_ops_vunmap, +}; + +/* exporting dmabuf as fd */ +int hyper_dmabuf_export_fd(struct imported_sgt_info *imported, int flags) +{ + int fd = -1; + + /* call hyper_dmabuf_export_dmabuf and create + * and bind a handle for it then release + */ + hyper_dmabuf_export_dma_buf(imported); + + if (imported->dma_buf) + fd = dma_buf_fd(imported->dma_buf, flags); + + return fd; +} + +void hyper_dmabuf_export_dma_buf(struct imported_sgt_info *imported) +{ + DEFINE_DMA_BUF_EXPORT_INFO(exp_info); + + exp_info.ops = &hyper_dmabuf_ops; + + /* multiple of PAGE_SIZE, not considering offset */ + exp_info.size = imported->sgt->nents * PAGE_SIZE; + exp_info.flags = /* not sure about flag */ 0; + exp_info.priv = imported; + + imported->dma_buf = dma_buf_export(&exp_info); +} diff --git a/drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_ops.h b/drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_ops.h new file mode 100644 index 000000000000..c5505a41f0fe --- /dev/null +++ b/drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_ops.h @@ -0,0 +1,32 @@ +/* + * Copyright © 2017 Intel Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + * + */ + +#ifndef __HYPER_DMABUF_OPS_H__ +#define __HYPER_DMABUF_OPS_H__ + +int hyper_dmabuf_export_fd(struct imported_sgt_info *imported, int flags); + +void hyper_dmabuf_export_dma_buf(struct imported_sgt_info *imported); + +#endif /* __HYPER_DMABUF_IMP_H__ */ diff --git a/drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_query.c b/drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_query.c new file mode 100644 index 000000000000..1f2f56b1162d --- /dev/null +++ b/drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_query.c @@ -0,0 +1,172 @@ +/* + * Copyright © 2017 Intel Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + * + * Authors: + * Dongwon Kim + * Mateusz Polrola + * + */ + +#include +#include +#include "hyper_dmabuf_drv.h" +#include "hyper_dmabuf_struct.h" +#include "hyper_dmabuf_id.h" + +#define HYPER_DMABUF_SIZE(nents, first_offset, last_len) \ + ((nents)*PAGE_SIZE - (first_offset) - PAGE_SIZE + (last_len)) + +int hyper_dmabuf_query_exported(struct exported_sgt_info *exported, + int query, unsigned long *info) +{ + switch (query) { + case HYPER_DMABUF_QUERY_TYPE: + *info = EXPORTED; + break; + + /* exporting domain of this specific dmabuf*/ + case HYPER_DMABUF_QUERY_EXPORTER: + *info = HYPER_DMABUF_DOM_ID(exported->hid); + break; + + /* importing domain of this specific dmabuf */ + case HYPER_DMABUF_QUERY_IMPORTER: + *info = exported->rdomid; + break; + + /* size of dmabuf in byte */ + case HYPER_DMABUF_QUERY_SIZE: + *info = exported->dma_buf->size; + break; + + /* whether the buffer is used by importer */ + case HYPER_DMABUF_QUERY_BUSY: + *info = (exported->active > 0); + break; + + /* whether the buffer is unexported */ + case HYPER_DMABUF_QUERY_UNEXPORTED: + *info = !exported->valid; + break; + + /* whether the buffer is scheduled to be unexported */ + case HYPER_DMABUF_QUERY_DELAYED_UNEXPORTED: + *info = !exported->unexport_sched; + break; + + /* size of private info attached to buffer */ + case HYPER_DMABUF_QUERY_PRIV_INFO_SIZE: + *info = exported->sz_priv; + break; + + /* copy private info attached to buffer */ + case HYPER_DMABUF_QUERY_PRIV_INFO: + if (exported->sz_priv > 0) { + int n; + + n = copy_to_user((void __user *) *info, + exported->priv, + exported->sz_priv); + if (n != 0) + return -EINVAL; + } + break; + + default: + return -EINVAL; + } + + return 0; +} + + +int hyper_dmabuf_query_imported(struct imported_sgt_info *imported, + int query, unsigned long *info) +{ + switch (query) { + case HYPER_DMABUF_QUERY_TYPE: + *info = IMPORTED; + break; + + /* exporting domain of this specific dmabuf*/ + case HYPER_DMABUF_QUERY_EXPORTER: + *info = HYPER_DMABUF_DOM_ID(imported->hid); + break; + + /* importing domain of this specific dmabuf */ + case HYPER_DMABUF_QUERY_IMPORTER: + *info = hy_drv_priv->domid; + break; + + /* size of dmabuf in byte */ + case HYPER_DMABUF_QUERY_SIZE: + if (imported->dma_buf) { + /* if local dma_buf is created (if it's + * ever mapped), retrieve it directly + * from struct dma_buf * + */ + *info = imported->dma_buf->size; + } else { + /* calcuate it from given nents, frst_ofst + * and last_len + */ + *info = HYPER_DMABUF_SIZE(imported->nents, + imported->frst_ofst, + imported->last_len); + } + break; + + /* whether the buffer is used or not */ + case HYPER_DMABUF_QUERY_BUSY: + /* checks if it's used by importer */ + *info = (imported->importers > 0); + break; + + /* whether the buffer is unexported */ + case HYPER_DMABUF_QUERY_UNEXPORTED: + *info = !imported->valid; + break; + + /* size of private info attached to buffer */ + case HYPER_DMABUF_QUERY_PRIV_INFO_SIZE: + *info = imported->sz_priv; + break; + + /* copy private info attached to buffer */ + case HYPER_DMABUF_QUERY_PRIV_INFO: + if (imported->sz_priv > 0) { + int n; + + n = copy_to_user((void __user *)*info, + imported->priv, + imported->sz_priv); + if (n != 0) + return -EINVAL; + } + break; + + default: + return -EINVAL; + } + + return 0; +} diff --git a/drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_query.h b/drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_query.h new file mode 100644 index 000000000000..65ae738f8f53 --- /dev/null +++ b/drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_query.h @@ -0,0 +1,10 @@ +#ifndef __HYPER_DMABUF_QUERY_H__ +#define __HYPER_DMABUF_QUERY_H__ + +int hyper_dmabuf_query_imported(struct imported_sgt_info *imported, + int query, unsigned long *info); + +int hyper_dmabuf_query_exported(struct exported_sgt_info *exported, + int query, unsigned long *info); + +#endif // __HYPER_DMABUF_QUERY_H__ diff --git a/drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_remote_sync.c b/drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_remote_sync.c new file mode 100644 index 000000000000..a82fd7b087b8 --- /dev/null +++ b/drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_remote_sync.c @@ -0,0 +1,322 @@ +/* + * Copyright © 2017 Intel Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + * + * Authors: + * Dongwon Kim + * Mateusz Polrola + * + */ + +#include +#include +#include +#include +#include "hyper_dmabuf_drv.h" +#include "hyper_dmabuf_struct.h" +#include "hyper_dmabuf_list.h" +#include "hyper_dmabuf_msg.h" +#include "hyper_dmabuf_id.h" +#include "hyper_dmabuf_sgl_proc.h" + +/* Whenever importer does dma operations from remote domain, + * a notification is sent to the exporter so that exporter + * issues equivalent dma operation on the original dma buf + * for indirect synchronization via shadow operations. + * + * All ptrs and references (e.g struct sg_table*, + * struct dma_buf_attachment) created via these operations on + * exporter's side are kept in stack (implemented as circular + * linked-lists) separately so that those can be re-referenced + * later when unmapping operations are invoked to free those. + * + * The very first element on the bottom of each stack holds + * is what is created when initial exporting is issued so it + * should not be modified or released by this fuction. + */ +int hyper_dmabuf_remote_sync(hyper_dmabuf_id_t hid, int ops) +{ + struct exported_sgt_info *exported; + struct sgt_list *sgtl; + struct attachment_list *attachl; + struct kmap_vaddr_list *va_kmapl; + struct vmap_vaddr_list *va_vmapl; + int ret; + + /* find a coresponding SGT for the id */ + exported = hyper_dmabuf_find_exported(hid); + + if (!exported) { + dev_err(hy_drv_priv->dev, + "dmabuf remote sync::can't find exported list\n"); + return -ENOENT; + } + + switch (ops) { + case HYPER_DMABUF_OPS_ATTACH: + attachl = kcalloc(1, sizeof(*attachl), GFP_KERNEL); + + if (!attachl) + return -ENOMEM; + + attachl->attach = dma_buf_attach(exported->dma_buf, + hy_drv_priv->dev); + + if (!attachl->attach) { + kfree(attachl); + dev_err(hy_drv_priv->dev, + "remote sync::HYPER_DMABUF_OPS_ATTACH\n"); + return -ENOMEM; + } + + list_add(&attachl->list, &exported->active_attached->list); + break; + + case HYPER_DMABUF_OPS_DETACH: + if (list_empty(&exported->active_attached->list)) { + dev_err(hy_drv_priv->dev, + "remote sync::HYPER_DMABUF_OPS_DETACH\n"); + dev_err(hy_drv_priv->dev, + "no more dmabuf attachment left to be detached\n"); + return -EFAULT; + } + + attachl = list_first_entry(&exported->active_attached->list, + struct attachment_list, list); + + dma_buf_detach(exported->dma_buf, attachl->attach); + list_del(&attachl->list); + kfree(attachl); + break; + + case HYPER_DMABUF_OPS_MAP: + if (list_empty(&exported->active_attached->list)) { + dev_err(hy_drv_priv->dev, + "remote sync::HYPER_DMABUF_OPS_MAP\n"); + dev_err(hy_drv_priv->dev, + "no more dmabuf attachment left to be mapped\n"); + return -EFAULT; + } + + attachl = list_first_entry(&exported->active_attached->list, + struct attachment_list, list); + + sgtl = kcalloc(1, sizeof(*sgtl), GFP_KERNEL); + + if (!sgtl) + return -ENOMEM; + + sgtl->sgt = dma_buf_map_attachment(attachl->attach, + DMA_BIDIRECTIONAL); + if (!sgtl->sgt) { + kfree(sgtl); + dev_err(hy_drv_priv->dev, + "remote sync::HYPER_DMABUF_OPS_MAP\n"); + return -ENOMEM; + } + list_add(&sgtl->list, &exported->active_sgts->list); + break; + + case HYPER_DMABUF_OPS_UNMAP: + if (list_empty(&exported->active_sgts->list) || + list_empty(&exported->active_attached->list)) { + dev_err(hy_drv_priv->dev, + "remote sync::HYPER_DMABUF_OPS_UNMAP\n"); + dev_err(hy_drv_priv->dev, + "no SGT or attach left to be unmapped\n"); + return -EFAULT; + } + + attachl = list_first_entry(&exported->active_attached->list, + struct attachment_list, list); + sgtl = list_first_entry(&exported->active_sgts->list, + struct sgt_list, list); + + dma_buf_unmap_attachment(attachl->attach, sgtl->sgt, + DMA_BIDIRECTIONAL); + list_del(&sgtl->list); + kfree(sgtl); + break; + + case HYPER_DMABUF_OPS_RELEASE: + dev_dbg(hy_drv_priv->dev, + "id:%d key:%d %d %d} released, ref left: %d\n", + exported->hid.id, exported->hid.rng_key[0], + exported->hid.rng_key[1], exported->hid.rng_key[2], + exported->active - 1); + + exported->active--; + + /* If there are still importers just break, if no then + * continue with final cleanup + */ + if (exported->active) + break; + + /* Importer just released buffer fd, check if there is + * any other importer still using it. + * If not and buffer was unexported, clean up shared + * data and remove that buffer. + */ + dev_dbg(hy_drv_priv->dev, + "Buffer {id:%d key:%d %d %d} final released\n", + exported->hid.id, exported->hid.rng_key[0], + exported->hid.rng_key[1], exported->hid.rng_key[2]); + + if (!exported->valid && !exported->active && + !exported->unexport_sched) { + hyper_dmabuf_cleanup_sgt_info(exported, false); + hyper_dmabuf_remove_exported(hid); + kfree(exported); + /* store hyper_dmabuf_id in the list for reuse */ + hyper_dmabuf_store_hid(hid); + } + + break; + + case HYPER_DMABUF_OPS_BEGIN_CPU_ACCESS: + ret = dma_buf_begin_cpu_access(exported->dma_buf, + DMA_BIDIRECTIONAL); + if (ret) { + dev_err(hy_drv_priv->dev, + "HYPER_DMABUF_OPS_BEGIN_CPU_ACCESS\n"); + return ret; + } + break; + + case HYPER_DMABUF_OPS_END_CPU_ACCESS: + ret = dma_buf_end_cpu_access(exported->dma_buf, + DMA_BIDIRECTIONAL); + if (ret) { + dev_err(hy_drv_priv->dev, + "HYPER_DMABUF_OPS_END_CPU_ACCESS\n"); + return ret; + } + break; + + case HYPER_DMABUF_OPS_KMAP_ATOMIC: + case HYPER_DMABUF_OPS_KMAP: + va_kmapl = kcalloc(1, sizeof(*va_kmapl), GFP_KERNEL); + if (!va_kmapl) + return -ENOMEM; + + /* dummy kmapping of 1 page */ + if (ops == HYPER_DMABUF_OPS_KMAP_ATOMIC) + va_kmapl->vaddr = dma_buf_kmap_atomic( + exported->dma_buf, 1); + else + va_kmapl->vaddr = dma_buf_kmap( + exported->dma_buf, 1); + + if (!va_kmapl->vaddr) { + kfree(va_kmapl); + dev_err(hy_drv_priv->dev, + "HYPER_DMABUF_OPS_KMAP(_ATOMIC)\n"); + return -ENOMEM; + } + list_add(&va_kmapl->list, &exported->va_kmapped->list); + break; + + case HYPER_DMABUF_OPS_KUNMAP_ATOMIC: + case HYPER_DMABUF_OPS_KUNMAP: + if (list_empty(&exported->va_kmapped->list)) { + dev_err(hy_drv_priv->dev, + "HYPER_DMABUF_OPS_KUNMAP(_ATOMIC)\n"); + dev_err(hy_drv_priv->dev, + "no more dmabuf VA to be freed\n"); + return -EFAULT; + } + + va_kmapl = list_first_entry(&exported->va_kmapped->list, + struct kmap_vaddr_list, list); + if (!va_kmapl->vaddr) { + dev_err(hy_drv_priv->dev, + "HYPER_DMABUF_OPS_KUNMAP(_ATOMIC)\n"); + return PTR_ERR(va_kmapl->vaddr); + } + + /* unmapping 1 page */ + if (ops == HYPER_DMABUF_OPS_KUNMAP_ATOMIC) + dma_buf_kunmap_atomic(exported->dma_buf, + 1, va_kmapl->vaddr); + else + dma_buf_kunmap(exported->dma_buf, + 1, va_kmapl->vaddr); + + list_del(&va_kmapl->list); + kfree(va_kmapl); + break; + + case HYPER_DMABUF_OPS_MMAP: + /* currently not supported: looking for a way to create + * a dummy vma + */ + dev_warn(hy_drv_priv->dev, + "remote sync::sychronized mmap is not supported\n"); + break; + + case HYPER_DMABUF_OPS_VMAP: + va_vmapl = kcalloc(1, sizeof(*va_vmapl), GFP_KERNEL); + + if (!va_vmapl) + return -ENOMEM; + + /* dummy vmapping */ + va_vmapl->vaddr = dma_buf_vmap(exported->dma_buf); + + if (!va_vmapl->vaddr) { + kfree(va_vmapl); + dev_err(hy_drv_priv->dev, + "remote sync::HYPER_DMABUF_OPS_VMAP\n"); + return -ENOMEM; + } + list_add(&va_vmapl->list, &exported->va_vmapped->list); + break; + + case HYPER_DMABUF_OPS_VUNMAP: + if (list_empty(&exported->va_vmapped->list)) { + dev_err(hy_drv_priv->dev, + "remote sync::HYPER_DMABUF_OPS_VUNMAP\n"); + dev_err(hy_drv_priv->dev, + "no more dmabuf VA to be freed\n"); + return -EFAULT; + } + va_vmapl = list_first_entry(&exported->va_vmapped->list, + struct vmap_vaddr_list, list); + if (!va_vmapl || va_vmapl->vaddr == NULL) { + dev_err(hy_drv_priv->dev, + "remote sync::HYPER_DMABUF_OPS_VUNMAP\n"); + return -EFAULT; + } + + dma_buf_vunmap(exported->dma_buf, va_vmapl->vaddr); + + list_del(&va_vmapl->list); + kfree(va_vmapl); + break; + + default: + /* program should not get here */ + break; + } + + return 0; +} diff --git a/drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_remote_sync.h b/drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_remote_sync.h new file mode 100644 index 000000000000..366389287f4e --- /dev/null +++ b/drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_remote_sync.h @@ -0,0 +1,30 @@ +/* + * Copyright © 2017 Intel Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + * + */ + +#ifndef __HYPER_DMABUF_REMOTE_SYNC_H__ +#define __HYPER_DMABUF_REMOTE_SYNC_H__ + +int hyper_dmabuf_remote_sync(hyper_dmabuf_id_t hid, int ops); + +#endif // __HYPER_DMABUF_REMOTE_SYNC_H__ diff --git a/drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_sgl_proc.c b/drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_sgl_proc.c new file mode 100644 index 000000000000..c1887d1ad709 --- /dev/null +++ b/drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_sgl_proc.c @@ -0,0 +1,261 @@ +/* + * Copyright © 2017 Intel Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + * + * Authors: + * Dongwon Kim + * Mateusz Polrola + * + */ + +#include +#include +#include +#include +#include "hyper_dmabuf_drv.h" +#include "hyper_dmabuf_struct.h" +#include "hyper_dmabuf_sgl_proc.h" + +#define REFS_PER_PAGE (PAGE_SIZE/sizeof(grant_ref_t)) + +/* return total number of pages referenced by a sgt + * for pre-calculation of # of pages behind a given sgt + */ +static int get_num_pgs(struct sg_table *sgt) +{ + struct scatterlist *sgl; + int length, i; + /* at least one page */ + int num_pages = 1; + + sgl = sgt->sgl; + + length = sgl->length - PAGE_SIZE + sgl->offset; + + /* round-up */ + num_pages += ((length + PAGE_SIZE - 1)/PAGE_SIZE); + + for (i = 1; i < sgt->nents; i++) { + sgl = sg_next(sgl); + + /* round-up */ + num_pages += ((sgl->length + PAGE_SIZE - 1) / + PAGE_SIZE); /* round-up */ + } + + return num_pages; +} + +/* extract pages directly from struct sg_table */ +struct pages_info *hyper_dmabuf_ext_pgs(struct sg_table *sgt) +{ + struct pages_info *pg_info; + int i, j, k; + int length; + struct scatterlist *sgl; + + pg_info = kmalloc(sizeof(*pg_info), GFP_KERNEL); + if (!pg_info) + return NULL; + + pg_info->pgs = kmalloc_array(get_num_pgs(sgt), + sizeof(struct page *), + GFP_KERNEL); + + if (!pg_info->pgs) { + kfree(pg_info); + return NULL; + } + + sgl = sgt->sgl; + + pg_info->nents = 1; + pg_info->frst_ofst = sgl->offset; + pg_info->pgs[0] = sg_page(sgl); + length = sgl->length - PAGE_SIZE + sgl->offset; + i = 1; + + while (length > 0) { + pg_info->pgs[i] = nth_page(sg_page(sgl), i); + length -= PAGE_SIZE; + pg_info->nents++; + i++; + } + + for (j = 1; j < sgt->nents; j++) { + sgl = sg_next(sgl); + pg_info->pgs[i++] = sg_page(sgl); + length = sgl->length - PAGE_SIZE; + pg_info->nents++; + k = 1; + + while (length > 0) { + pg_info->pgs[i++] = nth_page(sg_page(sgl), k++); + length -= PAGE_SIZE; + pg_info->nents++; + } + } + + /* + * lenght at that point will be 0 or negative, + * so to calculate last page size just add it to PAGE_SIZE + */ + pg_info->last_len = PAGE_SIZE + length; + + return pg_info; +} + +/* create sg_table with given pages and other parameters */ +struct sg_table *hyper_dmabuf_create_sgt(struct page **pgs, + int frst_ofst, int last_len, + int nents) +{ + struct sg_table *sgt; + struct scatterlist *sgl; + int i, ret; + + sgt = kmalloc(sizeof(struct sg_table), GFP_KERNEL); + if (!sgt) + return NULL; + + ret = sg_alloc_table(sgt, nents, GFP_KERNEL); + if (ret) { + if (sgt) { + sg_free_table(sgt); + kfree(sgt); + } + + return NULL; + } + + sgl = sgt->sgl; + + sg_set_page(sgl, pgs[0], PAGE_SIZE-frst_ofst, frst_ofst); + + for (i = 1; i < nents-1; i++) { + sgl = sg_next(sgl); + sg_set_page(sgl, pgs[i], PAGE_SIZE, 0); + } + + if (nents > 1) /* more than one page */ { + sgl = sg_next(sgl); + sg_set_page(sgl, pgs[i], last_len, 0); + } + + return sgt; +} + +int hyper_dmabuf_cleanup_sgt_info(struct exported_sgt_info *exported, + int force) +{ + struct sgt_list *sgtl; + struct attachment_list *attachl; + struct kmap_vaddr_list *va_kmapl; + struct vmap_vaddr_list *va_vmapl; + struct hyper_dmabuf_bknd_ops *bknd_ops = hy_drv_priv->bknd_ops; + + if (!exported) { + dev_err(hy_drv_priv->dev, "invalid hyper_dmabuf_id\n"); + return -EINVAL; + } + + /* if force != 1, sgt_info can be released only if + * there's no activity on exported dma-buf on importer + * side. + */ + if (!force && + exported->active) { + dev_warn(hy_drv_priv->dev, + "dma-buf is used by importer\n"); + + return -EPERM; + } + + /* force == 1 is not recommended */ + while (!list_empty(&exported->va_kmapped->list)) { + va_kmapl = list_first_entry(&exported->va_kmapped->list, + struct kmap_vaddr_list, list); + + dma_buf_kunmap(exported->dma_buf, 1, va_kmapl->vaddr); + list_del(&va_kmapl->list); + kfree(va_kmapl); + } + + while (!list_empty(&exported->va_vmapped->list)) { + va_vmapl = list_first_entry(&exported->va_vmapped->list, + struct vmap_vaddr_list, list); + + dma_buf_vunmap(exported->dma_buf, va_vmapl->vaddr); + list_del(&va_vmapl->list); + kfree(va_vmapl); + } + + while (!list_empty(&exported->active_sgts->list)) { + attachl = list_first_entry(&exported->active_attached->list, + struct attachment_list, list); + + sgtl = list_first_entry(&exported->active_sgts->list, + struct sgt_list, list); + + dma_buf_unmap_attachment(attachl->attach, sgtl->sgt, + DMA_BIDIRECTIONAL); + list_del(&sgtl->list); + kfree(sgtl); + } + + while (!list_empty(&exported->active_sgts->list)) { + attachl = list_first_entry(&exported->active_attached->list, + struct attachment_list, list); + + dma_buf_detach(exported->dma_buf, attachl->attach); + list_del(&attachl->list); + kfree(attachl); + } + + /* Start cleanup of buffer in reverse order to exporting */ + bknd_ops->unshare_pages(&exported->refs_info, exported->nents); + + /* unmap dma-buf */ + dma_buf_unmap_attachment(exported->active_attached->attach, + exported->active_sgts->sgt, + DMA_BIDIRECTIONAL); + + /* detatch dma-buf */ + dma_buf_detach(exported->dma_buf, exported->active_attached->attach); + + /* close connection to dma-buf completely */ + dma_buf_put(exported->dma_buf); + exported->dma_buf = NULL; + + kfree(exported->active_sgts); + kfree(exported->active_attached); + kfree(exported->va_kmapped); + kfree(exported->va_vmapped); + kfree(exported->priv); + + exported->active_sgts = NULL; + exported->active_attached = NULL; + exported->va_kmapped = NULL; + exported->va_vmapped = NULL; + exported->priv = NULL; + + return 0; +} diff --git a/drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_sgl_proc.h b/drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_sgl_proc.h new file mode 100644 index 000000000000..869d98204e03 --- /dev/null +++ b/drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_sgl_proc.h @@ -0,0 +1,41 @@ +/* + * Copyright © 2017 Intel Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + * + */ + +#ifndef __HYPER_DMABUF_IMP_H__ +#define __HYPER_DMABUF_IMP_H__ + +/* extract pages directly from struct sg_table */ +struct pages_info *hyper_dmabuf_ext_pgs(struct sg_table *sgt); + +/* create sg_table with given pages and other parameters */ +struct sg_table *hyper_dmabuf_create_sgt(struct page **pgs, + int frst_ofst, int last_len, + int nents); + +int hyper_dmabuf_cleanup_sgt_info(struct exported_sgt_info *exported, + int force); + +void hyper_dmabuf_free_sgt(struct sg_table *sgt); + +#endif /* __HYPER_DMABUF_IMP_H__ */ diff --git a/drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_struct.h b/drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_struct.h new file mode 100644 index 000000000000..a11f804edfb3 --- /dev/null +++ b/drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_struct.h @@ -0,0 +1,141 @@ +/* + * Copyright © 2017 Intel Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + * + */ + +#ifndef __HYPER_DMABUF_STRUCT_H__ +#define __HYPER_DMABUF_STRUCT_H__ + +/* stack of mapped sgts */ +struct sgt_list { + struct sg_table *sgt; + struct list_head list; +}; + +/* stack of attachments */ +struct attachment_list { + struct dma_buf_attachment *attach; + struct list_head list; +}; + +/* stack of vaddr mapped via kmap */ +struct kmap_vaddr_list { + void *vaddr; + struct list_head list; +}; + +/* stack of vaddr mapped via vmap */ +struct vmap_vaddr_list { + void *vaddr; + struct list_head list; +}; + +/* Exporter builds pages_info before sharing pages */ +struct pages_info { + int frst_ofst; + int last_len; + int nents; + struct page **pgs; +}; + + +/* Exporter stores references to sgt in a hash table + * Exporter keeps these references for synchronization + * and tracking purposes + */ +struct exported_sgt_info { + hyper_dmabuf_id_t hid; + + /* VM ID of importer */ + int rdomid; + + struct dma_buf *dma_buf; + int nents; + + /* list for tracking activities on dma_buf */ + struct sgt_list *active_sgts; + struct attachment_list *active_attached; + struct kmap_vaddr_list *va_kmapped; + struct vmap_vaddr_list *va_vmapped; + + /* set to 0 when unexported. Importer doesn't + * do a new mapping of buffer if valid == false + */ + bool valid; + + /* active == true if the buffer is actively used + * (mapped) by importer + */ + int active; + + /* hypervisor specific reference data for shared pages */ + void *refs_info; + + struct delayed_work unexport; + bool unexport_sched; + + /* list for file pointers associated with all user space + * application that have exported this same buffer to + * another VM. This needs to be tracked to know whether + * the buffer can be completely freed. + */ + struct file *filp; + + /* size of private */ + size_t sz_priv; + + /* private data associated with the exported buffer */ + char *priv; +}; + +/* imported_sgt_info contains information about imported DMA_BUF + * this info is kept in IMPORT list and asynchorously retrieved and + * used to map DMA_BUF on importer VM's side upon export fd ioctl + * request from user-space + */ + +struct imported_sgt_info { + hyper_dmabuf_id_t hid; /* unique id for shared dmabuf imported */ + + /* hypervisor-specific handle to pages */ + int ref_handle; + + /* offset and size info of DMA_BUF */ + int frst_ofst; + int last_len; + int nents; + + struct dma_buf *dma_buf; + struct sg_table *sgt; + + void *refs_info; + bool valid; + int importers; + + /* size of private */ + size_t sz_priv; + + /* private data associated with the exported buffer */ + char *priv; +}; + +#endif /* __HYPER_DMABUF_STRUCT_H__ */ diff --git a/drivers/dma-buf/hyper_dmabuf/virtio/hyper_dmabuf_virtio_be_drv.c b/drivers/dma-buf/hyper_dmabuf/virtio/hyper_dmabuf_virtio_be_drv.c new file mode 100644 index 000000000000..bb16360d06d5 --- /dev/null +++ b/drivers/dma-buf/hyper_dmabuf/virtio/hyper_dmabuf_virtio_be_drv.c @@ -0,0 +1,505 @@ +/* + * Copyright © 2017 Intel Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + * + * Authors: + * Mateusz Polrola + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "../hyper_dmabuf_msg.h" +#include "../hyper_dmabuf_drv.h" +#include "hyper_dmabuf_virtio_common.h" +#include "hyper_dmabuf_virtio_fe_list.h" +#include "hyper_dmabuf_virtio_shm.h" +#include "hyper_dmabuf_virtio_comm_ring.h" + +/* + * Identifies which queue is used for TX and RX + * Note: it is opposite regarding to frontent definition + */ +enum virio_queue_type { + HDMA_VIRTIO_RX_QUEUE = 0, + HDMA_VIRTIO_TX_QUEUE, + HDMA_VIRTIO_QUEUE_MAX +}; + +/* Data required for sending TX messages using virtqueues*/ +struct virtio_be_tx_data { + struct iovec tx_iov; + uint16_t tx_idx; +}; + +struct virtio_be_priv { + struct virtio_dev_info dev; + struct virtio_vq_info vqs[HDMA_VIRTIO_QUEUE_MAX]; + bool busy; + struct hyper_dmabuf_req *pending_tx_req; + struct virtio_comm_ring tx_ring; + struct mutex lock; +}; + + +/* + * Received response to TX request, + * or empty buffer to be used for TX requests in future + */ +static void virtio_be_handle_tx_kick(struct virtio_vq_info *vq, + struct virtio_fe_info *fe_info) +{ + struct virtio_be_priv *priv = fe_info->priv; + /* Fill last used buffer with received buffer details */ + struct virtio_be_tx_data *tx_data = + (struct virtio_be_tx_data *) + virtio_comm_ring_pop(&priv->tx_ring); + + virtio_vq_getchain(vq, &tx_data->tx_idx, &tx_data->tx_iov, 1, NULL); + + /* Copy response if request was synchronous */ + if (priv->busy) { + memcpy(priv->pending_tx_req, + tx_data->tx_iov.iov_base, + tx_data->tx_iov.iov_len); + priv->busy = false; + } +} + +/* + * Received request from frontend + */ +static void virtio_be_handle_rx_kick(struct virtio_vq_info *vq, + struct virtio_fe_info *fe_info) +{ + struct iovec iov; + uint16_t idx; + struct hyper_dmabuf_req *req = NULL; + int len; + int ret; + + /* Make sure we will process all pending requests */ + while (virtio_vq_has_descs(vq)) { + virtio_vq_getchain(vq, &idx, &iov, 1, NULL); + + if (iov.iov_len != sizeof(struct hyper_dmabuf_req)) { + /* HACK: if int size buffer was provided, + * treat that as request to get frontend vmid */ + if (iov.iov_len == sizeof(int)) { + *((int *)iov.iov_base) = fe_info->vmid; + len = iov.iov_len; + } else { + len = 0; + dev_warn(hy_drv_priv->dev, + "received request with wrong size"); + dev_warn(hy_drv_priv->dev, + "%zu != %zu\n", + iov.iov_len, + sizeof(struct hyper_dmabuf_req)); + } + + virtio_vq_relchain(vq, idx, len); + continue; + } + + req = (struct hyper_dmabuf_req *)iov.iov_base; + + ret = hyper_dmabuf_msg_parse(1, req); + + len = iov.iov_len; + + virtio_vq_relchain(vq, idx, len); + } + virtio_vq_endchains(vq, 1); +} + +/* + * Check in what virtqueue we received buffer and process it accordingly. + */ +static void virtio_be_handle_vq_kick( + int vq_idx, struct virtio_fe_info *fe_info) +{ + struct virtio_vq_info *vq; + + vq = &fe_info->priv->vqs[vq_idx]; + + if (vq_idx == HDMA_VIRTIO_RX_QUEUE) + virtio_be_handle_rx_kick(vq, fe_info); + else + virtio_be_handle_tx_kick(vq, fe_info); +} + +/* + * Received new buffer in virtqueue + */ +static int virtio_be_handle_kick(int client_id, int req_cnt) +{ + int val = -1; + struct vhm_request *req; + struct virtio_fe_info *fe_info; + int i; + + if (unlikely(req_cnt <= 0)) + return -EINVAL; + + fe_info = virtio_fe_find(client_id); + if (fe_info == NULL) { + dev_warn(hy_drv_priv->dev, "Client %d not found\n", client_id); + return -EINVAL; + } + + for (i = 0; i < fe_info->max_vcpu; ++i) { + req = &fe_info->req_buf[i]; + if (req->valid && + req->processed == REQ_STATE_PROCESSING && + req->client == fe_info->client_id) { + if (req->reqs.pio_request.direction == REQUEST_READ) + req->reqs.pio_request.value = 0; + else + val = req->reqs.pio_request.value; + + req->processed = REQ_STATE_SUCCESS; + acrn_ioreq_complete_request(fe_info->client_id, i); + } + } + + if (val >= 0) + virtio_be_handle_vq_kick(val, fe_info); + + return 0; +} + +/* + * New frontend is connecting to backend. + * Creates virtqueues for it and registers internally. + */ +static int virtio_be_register_vhm_client(struct virtio_dev_info *d) +{ + unsigned int vmid; + struct vm_info info; + struct virtio_fe_info *fe_info; + int ret; + + fe_info = kcalloc(1, sizeof(*fe_info), GFP_KERNEL); + if (fe_info == NULL) + return -ENOMEM; + + fe_info->priv = + container_of(d, struct virtio_be_priv, dev); + vmid = d->_ctx.vmid; + fe_info->vmid = vmid; + + dev_dbg(hy_drv_priv->dev, + "Virtio frontend from vm %d connected\n", vmid); + + fe_info->client_id = + acrn_ioreq_create_client(vmid, + virtio_be_handle_kick, + "hyper dmabuf kick"); + if (fe_info->client_id < 0) { + dev_err(hy_drv_priv->dev, + "Failed to create client of ACRN ioreq\n"); + goto err; + } + + ret = acrn_ioreq_add_iorange(fe_info->client_id, + d->io_range_type ? REQ_MMIO : REQ_PORTIO, + d->io_range_start, + d->io_range_start + d->io_range_len); + + if (ret < 0) { + dev_err(hy_drv_priv->dev, + "Failed to add iorange to acrn ioreq\n"); + goto err; + } + + ret = vhm_get_vm_info(vmid, &info); + if (ret < 0) { + acrn_ioreq_del_iorange(fe_info->client_id, + d->io_range_type ? REQ_MMIO : REQ_PORTIO, + d->io_range_start, + d->io_range_start + d->io_range_len); + + dev_err(hy_drv_priv->dev, "Failed in vhm_get_vm_info\n"); + goto err; + } + + fe_info->max_vcpu = info.max_vcpu; + + fe_info->req_buf = acrn_ioreq_get_reqbuf(fe_info->client_id); + if (fe_info->req_buf == NULL) { + acrn_ioreq_del_iorange(fe_info->client_id, + d->io_range_type ? REQ_MMIO : REQ_PORTIO, + d->io_range_start, + d->io_range_start + d->io_range_len); + + dev_err(hy_drv_priv->dev, "Failed in acrn_ioreq_get_reqbuf\n"); + goto err; + } + + acrn_ioreq_attach_client(fe_info->client_id, 0); + + virtio_fe_add(fe_info); + + return 0; + +err: + acrn_ioreq_destroy_client(fe_info->client_id); + kfree(fe_info); + + return -EINVAL; +} + +/* + * DM is opening our VBS interface to create new frontend instance. + */ +static int vbs_k_open(struct inode *inode, struct file *f) +{ + struct virtio_be_priv *priv; + struct virtio_dev_info *dev; + struct virtio_vq_info *vqs; + int i; + + priv = kcalloc(1, sizeof(*priv), GFP_KERNEL); + if (priv == NULL) + return -ENOMEM; + + vqs = &priv->vqs[0]; + + dev = &priv->dev; + + for (i = 0; i < HDMA_VIRTIO_QUEUE_MAX; i++) { + vqs[i].dev = dev; + vqs[i].vq_notify = NULL; + } + dev->vqs = vqs; + + virtio_dev_init(dev, vqs, HDMA_VIRTIO_QUEUE_MAX); + + priv->pending_tx_req = + kcalloc(1, sizeof(struct hyper_dmabuf_req), GFP_KERNEL); + + virtio_comm_ring_init(&priv->tx_ring, + sizeof(struct virtio_be_tx_data), + REQ_RING_SIZE); + + mutex_init(&priv->lock); + + f->private_data = priv; + + return 0; +} + +static int vbs_k_release(struct inode *inode, struct file *f) +{ + struct virtio_be_priv *priv = + (struct virtio_be_priv *) f->private_data; + int i; + +// virtio_dev_stop(&priv->dev); +// virtio_dev_cleanup(&priv->dev, false); + + for (i = 0; i < HDMA_VIRTIO_QUEUE_MAX; i++) + virtio_vq_reset(&priv->vqs[i]); + + kfree(priv->pending_tx_req); + virtio_comm_ring_free(&priv->tx_ring); + kfree(priv); + return 0; +} + +static long vbs_k_ioctl(struct file *f, unsigned int ioctl, + unsigned long arg) +{ + struct virtio_be_priv *priv = + (struct virtio_be_priv *) f->private_data; + void __user *argp = (void __user *)arg; + int r; + + if (priv == NULL) { + dev_err(hy_drv_priv->dev, + "No backend private data\n"); + + return -EINVAL; + } + + if (ioctl == VBS_SET_VQ) { + /* Overridden to call additionally + * virtio_be_register_vhm_client */ + r = virtio_vqs_ioctl(&priv->dev, ioctl, argp); + if (r == -ENOIOCTLCMD) + return -EFAULT; + + if (virtio_be_register_vhm_client(&priv->dev) < 0) + return -EFAULT; + } else { + r = virtio_dev_ioctl(&priv->dev, ioctl, argp); + if (r == -ENOIOCTLCMD) + r = virtio_vqs_ioctl(&priv->dev, ioctl, argp); + } + + return r; +} + +static const struct file_operations vbs_hyper_dmabuf_fops = { + .owner = THIS_MODULE, + .open = vbs_k_open, + .release = vbs_k_release, + .unlocked_ioctl = vbs_k_ioctl, + .llseek = noop_llseek, +}; + +static struct miscdevice vbs_hyper_dmabuf_misc = { + .minor = MISC_DYNAMIC_MINOR, + .name = "vbs_hyper_dmabuf", + .fops = &vbs_hyper_dmabuf_fops, +}; + +static int virtio_be_register(void) +{ + return misc_register(&vbs_hyper_dmabuf_misc); +} + +static void virtio_be_unregister(void) +{ + misc_deregister(&vbs_hyper_dmabuf_misc); +} + +/* + * ACRN SOS will always has vmid 0 + * TODO: check if that always will be true + */ +static int virtio_be_get_vmid(void) +{ + return 0; +} + +static int virtio_be_send_req(int vmid, struct hyper_dmabuf_req *req, + int wait) +{ + int timeout = 1000; + struct virtio_fe_info *fe_info; + struct virtio_be_priv *priv; + struct virtio_be_tx_data *tx_data; + struct virtio_vq_info *vq; + int len; + + fe_info = virtio_fe_find_by_vmid(vmid); + + if (fe_info == NULL) { + dev_err(hy_drv_priv->dev, + "No frontend registered for vmid %d\n", vmid); + return -ENOENT; + } + + priv = fe_info->priv; + + mutex_lock(&priv->lock); + + /* Check if we have any free buffers for sending new request */ + while (virtio_comm_ring_full(&priv->tx_ring) && + timeout--) { + usleep_range(100, 120); + } + + if (timeout <= 0) { + dev_warn(hy_drv_priv->dev, "Requests ring full\n"); + return -EBUSY; + } + + /* Get free buffer for sending request from ring */ + tx_data = (struct virtio_be_tx_data *) + virtio_comm_ring_push(&priv->tx_ring); + + vq = &priv->vqs[HDMA_VIRTIO_TX_QUEUE]; + + if (tx_data->tx_iov.iov_len != sizeof(struct hyper_dmabuf_req)) { + dev_warn(hy_drv_priv->dev, + "received request with wrong size\n"); + virtio_vq_relchain(vq, tx_data->tx_idx, 0); + mutex_unlock(&priv->lock); + return -EINVAL; + } + + req->req_id = hyper_dmabuf_virtio_get_next_req_id(); + + /* Copy request data to virtqueue buffer */ + memcpy(tx_data->tx_iov.iov_base, req, sizeof(*req)); + len = tx_data->tx_iov.iov_len; + + /* update req_pending with current request */ + if (wait) { + priv->busy = true; + memcpy(priv->pending_tx_req, req, sizeof(*req)); + } + + virtio_vq_relchain(vq, tx_data->tx_idx, len); + + virtio_vq_endchains(vq, 1); + + if (wait) { + while (timeout--) { + if (priv->pending_tx_req->stat != + HYPER_DMABUF_REQ_NOT_RESPONDED) + break; + usleep_range(100, 120); + } + + if (timeout < 0) { + mutex_unlock(&priv->lock); + dev_err(hy_drv_priv->dev, "request timed-out\n"); + return -EBUSY; + } + } + + mutex_unlock(&priv->lock); + return 0; +}; + +struct hyper_dmabuf_bknd_ops virtio_bknd_ops = { + .init = virtio_be_register, + .cleanup = virtio_be_unregister, + .get_vm_id = virtio_be_get_vmid, + .share_pages = virtio_share_pages, + .unshare_pages = virtio_unshare_pages, + .map_shared_pages = virtio_map_shared_pages, + .unmap_shared_pages = virtio_unmap_shared_pages, + .init_comm_env = NULL, + .destroy_comm = NULL, + .init_rx_ch = NULL, + .init_tx_ch = NULL, + .send_req = virtio_be_send_req, +}; + + +MODULE_DESCRIPTION("Hyper dmabuf virtio driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/dma-buf/hyper_dmabuf/virtio/hyper_dmabuf_virtio_comm_ring.c b/drivers/dma-buf/hyper_dmabuf/virtio/hyper_dmabuf_virtio_comm_ring.c new file mode 100644 index 000000000000..d73bcbcc8e87 --- /dev/null +++ b/drivers/dma-buf/hyper_dmabuf/virtio/hyper_dmabuf_virtio_comm_ring.c @@ -0,0 +1,89 @@ +/* + * Copyright © 2017 Intel Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + * + * Authors: + * Mateusz Polrola + * + */ + +#include +#include +#include "hyper_dmabuf_virtio_comm_ring.h" + +int virtio_comm_ring_init(struct virtio_comm_ring *ring, + int entry_size, + int num_entries) +{ + ring->data = kcalloc(num_entries, entry_size, GFP_KERNEL); + + if (!ring->data) + return -ENOMEM; + + ring->head = 0; + ring->tail = 0; + ring->used = 0; + ring->num_entries = num_entries; + ring->entry_size = entry_size; + + return 0; +} + +void virtio_comm_ring_free(struct virtio_comm_ring *ring) +{ + kfree(ring->data); + ring->data = NULL; +} + +bool virtio_comm_ring_full(struct virtio_comm_ring *ring) +{ + if (ring->used == ring->num_entries) + return true; + + return false; +} + +void *virtio_comm_ring_push(struct virtio_comm_ring *ring) +{ + int old_head; + + if (virtio_comm_ring_full(ring)) + return NULL; + + old_head = ring->head; + + ring->head++; + ring->head %= ring->num_entries; + ring->used++; + + return ring->data + (ring->entry_size * old_head); +} + +void *virtio_comm_ring_pop(struct virtio_comm_ring *ring) +{ + int old_tail = ring->tail; + + ring->tail++; + ring->tail %= ring->num_entries; + ring->used--; + + return ring->data + (ring->entry_size * old_tail); +} diff --git a/drivers/dma-buf/hyper_dmabuf/virtio/hyper_dmabuf_virtio_comm_ring.h b/drivers/dma-buf/hyper_dmabuf/virtio/hyper_dmabuf_virtio_comm_ring.h new file mode 100644 index 000000000000..a95a63af2ba0 --- /dev/null +++ b/drivers/dma-buf/hyper_dmabuf/virtio/hyper_dmabuf_virtio_comm_ring.h @@ -0,0 +1,68 @@ +/* + * Copyright © 2017 Intel Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + * + */ + +#ifndef __HYPER_DMABUF_VIRTIO_COMM_RING_H__ +#define __HYPER_DMABUF_VIRTIO_COMM_RING_H__ + +/* Generic ring buffer */ +struct virtio_comm_ring { + /* Buffer allocated for keeping ring entries */ + void *data; + + /* Index pointing to next free element in ring */ + int head; + + /* Index pointing to last released element in ring */ + int tail; + + /* Total number of elements that ring can contain */ + int num_entries; + + /* Size of single ring element in bytes */ + int entry_size; + + /* Number of currently used elements */ + int used; +}; + +/* Initializes given ring for keeping given a + * number of entries of specific size */ +int virtio_comm_ring_init(struct virtio_comm_ring *ring, + int entry_size, + int num_entries); + +/* Frees buffer used for storing ring entries */ +void virtio_comm_ring_free(struct virtio_comm_ring *ring); + +/* Checks if ring is full */ +bool virtio_comm_ring_full(struct virtio_comm_ring *ring); + +/* Gets next free element from ring and marks it as used + * or NULL if ring is full */ +void *virtio_comm_ring_push(struct virtio_comm_ring *ring); + +/* Pops oldest element from ring and marks it as free */ +void *virtio_comm_ring_pop(struct virtio_comm_ring *ring); + +#endif /* __HYPER_DMABUF_VIRTIO_COMM_RING_H__*/ diff --git a/drivers/dma-buf/hyper_dmabuf/virtio/hyper_dmabuf_virtio_common.c b/drivers/dma-buf/hyper_dmabuf/virtio/hyper_dmabuf_virtio_common.c new file mode 100644 index 000000000000..05be74358a74 --- /dev/null +++ b/drivers/dma-buf/hyper_dmabuf/virtio/hyper_dmabuf_virtio_common.c @@ -0,0 +1,35 @@ +/* + * Copyright © 2017 Intel Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + * + * Authors: + * Mateusz Polrola + * + */ + +#include "hyper_dmabuf_virtio_common.h" + +int hyper_dmabuf_virtio_get_next_req_id(void) +{ + static int req_id; + + return req_id++; +} diff --git a/drivers/dma-buf/hyper_dmabuf/virtio/hyper_dmabuf_virtio_common.h b/drivers/dma-buf/hyper_dmabuf/virtio/hyper_dmabuf_virtio_common.h new file mode 100644 index 000000000000..24a652ef54c0 --- /dev/null +++ b/drivers/dma-buf/hyper_dmabuf/virtio/hyper_dmabuf_virtio_common.h @@ -0,0 +1,55 @@ +/* + * Copyright © 2017 Intel Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + * + */ + +#ifndef __HYPER_DMABUF_VIRTIO_COMMON_H__ +#define __HYPER_DMABUF_VIRTIO_COMMON_H__ + +/* + * ACRN uses physicall addresses for memory sharing, + * so size of one page ref will be 64-bits + */ + +#define REFS_PER_PAGE (PAGE_SIZE/sizeof(u64)) + +/* Defines size of requests circular buffer */ +#define REQ_RING_SIZE 128 + +extern struct hyper_dmabuf_bknd_ops virtio_bknd_ops; +struct virtio_be_priv; +struct vhm_request; + +/* Entry describing each connected frontend */ +struct virtio_fe_info { + struct virtio_be_priv *priv; + int client_id; + int vmid; + int max_vcpu; + struct vhm_request *req_buf; +}; + +extern struct hyper_dmabuf_private hyper_dmabuf_private; + +int hyper_dmabuf_virtio_get_next_req_id(void); + +#endif /* __HYPER_DMABUF_VIRTIO_COMMON_H__*/ diff --git a/drivers/dma-buf/hyper_dmabuf/virtio/hyper_dmabuf_virtio_fe_drv.c b/drivers/dma-buf/hyper_dmabuf/virtio/hyper_dmabuf_virtio_fe_drv.c new file mode 100644 index 000000000000..9ae290435d70 --- /dev/null +++ b/drivers/dma-buf/hyper_dmabuf/virtio/hyper_dmabuf_virtio_fe_drv.c @@ -0,0 +1,385 @@ +/* + * Copyright © 2017 Intel Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + * + * Authors: + * Mateusz Polrola + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include "../hyper_dmabuf_msg.h" +#include "../hyper_dmabuf_drv.h" +#include "hyper_dmabuf_virtio_common.h" +#include "hyper_dmabuf_virtio_shm.h" +#include "hyper_dmabuf_virtio_comm_ring.h" + +/* + * Identifies which queue is used for TX and RX + * Note: it is opposite regarding to backend definition + */ +enum virio_queue_type { + HDMA_VIRTIO_TX_QUEUE = 0, + HDMA_VIRTIO_RX_QUEUE, + HDMA_VIRTIO_QUEUE_MAX +}; + +struct virtio_hdma_fe_priv { + struct virtqueue *vqs[HDMA_VIRTIO_QUEUE_MAX]; + struct virtio_comm_ring tx_ring; + struct virtio_comm_ring rx_ring; + int vmid; +}; + +/* Assuming there will be one FE instance per VM */ +static struct virtio_hdma_fe_priv *hyper_dmabuf_virtio_fe; + +/* + * Received response for request. + * No need for copying request with updated result, + * as backend is processing original request data directly. + */ +static void virtio_hdma_fe_tx_done(struct virtqueue *vq) +{ + struct virtio_hdma_fe_priv *priv = + (struct virtio_hdma_fe_priv *) vq->vdev->priv; + int len; + + if (priv == NULL) { + dev_dbg(hy_drv_priv->dev, + "No frontend private data\n"); + return; + } + + /* Make sure that all pending responses are processed */ + while (virtqueue_get_buf(vq, &len)) { + if (len == sizeof(struct hyper_dmabuf_req)) { + /* Mark that response was received + * and buffer can be reused */ + virtio_comm_ring_pop(&priv->tx_ring); + } + } +} + +/* + * Sends given data buffer via given virtqueue. + */ +static void virtio_hdma_fe_queue_buffer(struct virtio_hdma_fe_priv *priv, + unsigned int queue_nr, + void *buf, size_t size) +{ + struct scatterlist sg; + + if (queue_nr >= HDMA_VIRTIO_QUEUE_MAX) { + dev_dbg(hy_drv_priv->dev, + "queue_nr exceeding max queue number\n"); + return; + } + + sg_init_one(&sg, buf, size); + + virtqueue_add_inbuf(priv->vqs[queue_nr], &sg, 1, buf, GFP_KERNEL); + + virtqueue_kick(priv->vqs[queue_nr]); +} + +/* + * Handle requests coming from other VMs + */ +static void virtio_hdma_fe_handle_rx(struct virtqueue *vq) +{ + struct virtio_hdma_fe_priv *priv = + (struct virtio_hdma_fe_priv *) vq->vdev->priv; + struct hyper_dmabuf_req *rx_req; + int size, ret; + + if (priv == NULL) { + dev_dbg(hy_drv_priv->dev, + "No frontend private data\n"); + return; + } + + /* Make sure all pending requests will be processed */ + while (virtqueue_get_buf(vq, &size)) { + + /* Get next request from ring */ + rx_req = (struct hyper_dmabuf_req *) + virtio_comm_ring_pop(&priv->rx_ring); + + if (size != sizeof(struct hyper_dmabuf_req)) { + dev_dbg(hy_drv_priv->dev, + "Received malformed request\n"); + } else { + ret = hyper_dmabuf_msg_parse(1, rx_req); + } + + /* Send updated request back to virtqueue as a response.*/ + virtio_hdma_fe_queue_buffer(priv, HDMA_VIRTIO_RX_QUEUE, + rx_req, sizeof(*rx_req)); + } +} + +static int virtio_hdma_fe_probe_common(struct virtio_device *vdev) +{ + struct virtio_hdma_fe_priv *priv; + vq_callback_t *callbacks[] = {virtio_hdma_fe_tx_done, + virtio_hdma_fe_handle_rx}; + static const char *names[] = {"txqueue", "rxqueue"}; + int ret; + + priv = kzalloc(sizeof(struct virtio_hdma_fe_priv), GFP_KERNEL); + if (priv == NULL) + return -ENOMEM; + + virtio_comm_ring_init(&priv->tx_ring, + sizeof(struct hyper_dmabuf_req), + REQ_RING_SIZE); + virtio_comm_ring_init(&priv->rx_ring, + sizeof(struct hyper_dmabuf_req), + REQ_RING_SIZE); + + /* Set vmid to -1 to mark that it is not initialized yet */ + priv->vmid = -1; + + vdev->priv = priv; + + ret = virtio_find_vqs(vdev, HDMA_VIRTIO_QUEUE_MAX, + priv->vqs, callbacks, names, NULL); + if (ret) + goto err; + + hyper_dmabuf_virtio_fe = priv; + + return 0; +err: + virtio_comm_ring_free(&priv->tx_ring); + virtio_comm_ring_free(&priv->rx_ring); + kfree(priv); + return ret; +} + +static void virtio_hdma_fe_remove_common(struct virtio_device *vdev) +{ + struct virtio_hdma_fe_priv *priv = + (struct virtio_hdma_fe_priv *) vdev->priv; + + if (priv == NULL) { + dev_err(hy_drv_priv->dev, + "No frontend private data\n"); + + return; + } + + vdev->config->reset(vdev); + vdev->config->del_vqs(vdev); + virtio_comm_ring_free(&priv->tx_ring); + virtio_comm_ring_free(&priv->rx_ring); + kfree(priv); + hyper_dmabuf_virtio_fe = NULL; +} + +static int virtio_hdma_fe_probe(struct virtio_device *vdev) +{ + return virtio_hdma_fe_probe_common(vdev); +} + +static void virtio_hdma_fe_remove(struct virtio_device *vdev) +{ + virtio_hdma_fe_remove_common(vdev); +} + +/* + * Queues empty requests buffers to backend, + * which will be used by it to send requests back to frontend. + */ +static void virtio_hdma_fe_scan(struct virtio_device *vdev) +{ + struct virtio_hdma_fe_priv *priv = + (struct virtio_hdma_fe_priv *) vdev->priv; + struct hyper_dmabuf_req *rx_req; + int timeout = 1000; + + if (priv == NULL) { + dev_dbg(hy_drv_priv->dev, + "No frontend private data\n"); + + return; + } + + /* Send request to query vmid, in ACRN guest instances don't + * know their ids, but host does. Here a small hack is used, + * and buffer of int size is sent to backend, in that case + * backend will fill it with vmid of instance that sent that request + */ + virtio_hdma_fe_queue_buffer(priv, HDMA_VIRTIO_TX_QUEUE, + &priv->vmid, sizeof(priv->vmid)); + + while (timeout--) { + if (priv->vmid > 0) + break; + usleep_range(100, 120); + } + + if (timeout < 0) + dev_err(hy_drv_priv->dev, + "Cannot query vmid\n"); + + while (!virtio_comm_ring_full(&priv->rx_ring)) { + rx_req = virtio_comm_ring_push(&priv->rx_ring); + + virtio_hdma_fe_queue_buffer(priv, HDMA_VIRTIO_RX_QUEUE, + rx_req, sizeof(*rx_req)); + } +} + +#ifdef CONFIG_PM_SLEEP +static int virtio_hdma_fe_freeze(struct virtio_device *vdev) +{ + virtio_hdma_fe_remove_common(vdev); + return 0; +} + +static int virtio_hdma_fe_restore(struct virtio_device *vdev) +{ + return virtio_hdma_fe_probe_common(vdev); +} +#endif + + +static struct virtio_device_id id_table[] = { + { VIRTIO_ID_HYPERDMABUF, VIRTIO_DEV_ANY_ID }, + { 0 }, +}; + +static struct virtio_driver virtio_hdma_fe_driver = { + .driver.name = KBUILD_MODNAME, + .driver.owner = THIS_MODULE, + .id_table = id_table, + .probe = virtio_hdma_fe_probe, + .remove = virtio_hdma_fe_remove, + .scan = virtio_hdma_fe_scan, +#ifdef CONFIG_PM_SLEEP + .freeze = virtio_hdma_fe_freeze, + .restore = virtio_hdma_fe_restore, +#endif +}; + +int virtio_hdma_fe_register(void) +{ + return register_virtio_driver(&virtio_hdma_fe_driver); +} + +void virtio_hdma_fe_unregister(void) +{ + unregister_virtio_driver(&virtio_hdma_fe_driver); +} + +static int virtio_hdma_fe_get_vmid(void) +{ + struct virtio_hdma_fe_priv *priv = hyper_dmabuf_virtio_fe; + + if (hyper_dmabuf_virtio_fe == NULL) { + dev_err(hy_drv_priv->dev, + "Backend not connected\n"); + return -1; + } + + return priv->vmid; +} + +static int virtio_hdma_fe_send_req(int vmid, struct hyper_dmabuf_req *req, + int wait) +{ + struct virtio_hdma_fe_priv *priv = hyper_dmabuf_virtio_fe; + struct hyper_dmabuf_req *tx_req; + int timeout = 1000; + + if (priv == NULL) { + dev_err(hy_drv_priv->dev, + "Backend not connected\n"); + return -ENOENT; + } + + /* Check if there are any free buffers in ring */ + while (timeout--) { + if (!virtio_comm_ring_full(&priv->tx_ring)) + break; + usleep_range(100, 120); + } + + if (timeout < 0) { + dev_err(hy_drv_priv->dev, + "Timedout while waiting for free request buffers\n"); + return -EBUSY; + } + + /* Get free buffer for sending request from ring */ + tx_req = (struct hyper_dmabuf_req *) + virtio_comm_ring_push(&priv->tx_ring); + req->req_id = hyper_dmabuf_virtio_get_next_req_id(); + + /* copy request to buffer that will be used in virtqueue */ + memcpy(tx_req, req, sizeof(*req)); + + virtio_hdma_fe_queue_buffer(hyper_dmabuf_virtio_fe, + HDMA_VIRTIO_TX_QUEUE, + tx_req, sizeof(*tx_req)); + + if (wait) { + while (timeout--) { + if (tx_req->stat != + HYPER_DMABUF_REQ_NOT_RESPONDED) + break; + usleep_range(100, 120); + } + + if (timeout < 0) + return -EBUSY; + } + + return 0; +} + +struct hyper_dmabuf_bknd_ops virtio_bknd_ops = { + .init = virtio_hdma_fe_register, + .cleanup = virtio_hdma_fe_unregister, + .get_vm_id = virtio_hdma_fe_get_vmid, + .share_pages = virtio_share_pages, + .unshare_pages = virtio_unshare_pages, + .map_shared_pages = virtio_map_shared_pages, + .unmap_shared_pages = virtio_unmap_shared_pages, + .send_req = virtio_hdma_fe_send_req, + .init_comm_env = NULL, + .destroy_comm = NULL, + .init_rx_ch = NULL, + .init_tx_ch = NULL, +}; + + +MODULE_DEVICE_TABLE(virtio, id_table); +MODULE_DESCRIPTION("Hyper dmabuf virtio driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/dma-buf/hyper_dmabuf/virtio/hyper_dmabuf_virtio_fe_list.c b/drivers/dma-buf/hyper_dmabuf/virtio/hyper_dmabuf_virtio_fe_list.c new file mode 100644 index 000000000000..79b30e286b5e --- /dev/null +++ b/drivers/dma-buf/hyper_dmabuf/virtio/hyper_dmabuf_virtio_fe_list.c @@ -0,0 +1,99 @@ +/* + * Copyright © 2017 Intel Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + * + * Authors: + * Mateusz Polrola + * + */ + +#include +#include +#include +#include +#include +#include +#include "../hyper_dmabuf_drv.h" +#include "hyper_dmabuf_virtio_common.h" +#include "hyper_dmabuf_virtio_fe_list.h" + +DECLARE_HASHTABLE(virtio_fe_hash, MAX_ENTRY_FE); + +void virtio_fe_table_init(void) +{ + hash_init(virtio_fe_hash); +} + +int virtio_fe_add(struct virtio_fe_info *fe_info) +{ + struct virtio_fe_info_entry *info_entry; + + info_entry = kmalloc(sizeof(*info_entry), GFP_KERNEL); + + if (!info_entry) + return -ENOMEM; + + info_entry->info = fe_info; + + hash_add(virtio_fe_hash, &info_entry->node, + info_entry->info->client_id); + + return 0; +} + +struct virtio_fe_info *virtio_fe_find(int client_id) +{ + struct virtio_fe_info_entry *info_entry; + int bkt; + + hash_for_each(virtio_fe_hash, bkt, info_entry, node) + if (info_entry->info->client_id == client_id) + return info_entry->info; + + return NULL; +} + +struct virtio_fe_info *virtio_fe_find_by_vmid(int vmid) +{ + struct virtio_fe_info_entry *info_entry; + int bkt; + + hash_for_each(virtio_fe_hash, bkt, info_entry, node) + if (info_entry->info->vmid == vmid) + return info_entry->info; + + return NULL; +} + +int virtio_fe_remove(int client_id) +{ + struct virtio_fe_info_entry *info_entry; + int bkt; + + hash_for_each(virtio_fe_hash, bkt, info_entry, node) + if (info_entry->info->client_id == client_id) { + hash_del(&info_entry->node); + kfree(info_entry); + return 0; + } + + return -ENOENT; +} diff --git a/drivers/dma-buf/hyper_dmabuf/virtio/hyper_dmabuf_virtio_fe_list.h b/drivers/dma-buf/hyper_dmabuf/virtio/hyper_dmabuf_virtio_fe_list.h new file mode 100644 index 000000000000..bc7ef843161c --- /dev/null +++ b/drivers/dma-buf/hyper_dmabuf/virtio/hyper_dmabuf_virtio_fe_list.h @@ -0,0 +1,48 @@ +/* + * Copyright © 2017 Intel Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + * + */ + +#ifndef __HYPER_DMABUF_VIRTIO_FE_LIST_H__ +#define __HYPER_DMABUF_VIRTIO_FE_LIST_H__ + +/* number of bits to be used for exported dmabufs hash table */ +#define MAX_ENTRY_FE 7 + +struct virtio_fe_info; + +struct virtio_fe_info_entry { + struct virtio_fe_info *info; + struct hlist_node node; +}; + +void virtio_fe_table_init(void); + +int virtio_fe_add(struct virtio_fe_info *fe_info); + +int virtio_remove_fe(int client_id); + +struct virtio_fe_info *virtio_fe_find(int client_id); + +struct virtio_fe_info *virtio_fe_find_by_vmid(int vmid); + +#endif /* __HYPER_DMABUF_VIRTIO_FE_LIST_H__*/ diff --git a/drivers/dma-buf/hyper_dmabuf/virtio/hyper_dmabuf_virtio_shm.c b/drivers/dma-buf/hyper_dmabuf/virtio/hyper_dmabuf_virtio_shm.c new file mode 100644 index 000000000000..be5141c25191 --- /dev/null +++ b/drivers/dma-buf/hyper_dmabuf/virtio/hyper_dmabuf_virtio_shm.c @@ -0,0 +1,343 @@ +/* + * Copyright © 2017 Intel Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + * + * Authors: + * Mateusz Polrola + * + */ + +#include +#include +#include +#include +#include +#ifdef CONFIG_HYPER_DMABUF_VIRTIO_BE +#include +#endif +#include "../hyper_dmabuf_drv.h" +#include "hyper_dmabuf_virtio_shm.h" +#include "hyper_dmabuf_virtio_common.h" + +#ifdef CONFIG_HYPER_DMABUF_VIRTIO_BE +struct virtio_shared_pages_info { + u64 *lvl3_table; + u64 **lvl2_table; + u64 lvl3_gref; + struct page **data_pages; + int n_lvl2_refs; + int nents_last; + int vmid; +}; +#else +struct virtio_shared_pages_info { + u64 *lvl3_table; + u64 *lvl2_table; + u64 lvl3_gref; +}; +#endif + +#ifdef CONFIG_HYPER_DMABUF_VIRTIO_BE +static int virtio_be_share_pages(struct page **pages, + int vmid, + int nents, + void **refs_info) +{ + dev_err(hy_drv_priv->dev, + "Pages sharing not available with ACRN backend in SOS\n"); + + return -EINVAL; +} + +static int virtio_be_unshare_pages(void **refs_info, + int nents) +{ + dev_err(hy_drv_priv->dev, + "Pages sharing not available with ACRN backend in SOS\n"); + + return -EINVAL; +} + +static struct page **virtio_be_map_shared_pages(unsigned long lvl3_gref, + int vmid, int nents, + void **refs_info) +{ + u64 *lvl3_table = NULL; + u64 **lvl2_table = NULL; + struct page **data_pages = NULL; + struct virtio_shared_pages_info *sh_pages_info = NULL; + void *pageaddr; + + int nents_last = (nents - 1) % REFS_PER_PAGE + 1; + int n_lvl2_refs = (nents / REFS_PER_PAGE) + ((nents_last > 0) ? 1 : 0) - + (nents_last == REFS_PER_PAGE); + int i, j, k; + + sh_pages_info = kmalloc(sizeof(*sh_pages_info), GFP_KERNEL); + if (sh_pages_info == NULL) + goto map_failed; + + *refs_info = (void *) sh_pages_info; + + data_pages = kcalloc(nents, sizeof(struct page *), GFP_KERNEL); + if (data_pages == NULL) + goto map_failed; + + lvl2_table = kcalloc(n_lvl2_refs, sizeof(u64 *), GFP_KERNEL); + if (lvl2_table == NULL) + goto map_failed; + + lvl3_table = (u64 *)map_guest_phys(vmid, lvl3_gref, PAGE_SIZE); + if (lvl3_table == NULL) + goto map_failed; + + for (i = 0; i < n_lvl2_refs; i++) { + lvl2_table[i] = (u64 *)map_guest_phys(vmid, + lvl3_table[i], + PAGE_SIZE); + if (lvl2_table[i] == NULL) + goto map_failed; + } + + k = 0; + for (i = 0; i < n_lvl2_refs - 1; i++) { + for (j = 0; j < REFS_PER_PAGE; j++) { + pageaddr = map_guest_phys(vmid, + lvl2_table[i][j], + PAGE_SIZE); + if (pageaddr == NULL) + goto map_failed; + + data_pages[k] = virt_to_page(pageaddr); + k++; + } + } + + for (j = 0; j < nents_last; j++) { + pageaddr = map_guest_phys(vmid, + lvl2_table[i][j], + PAGE_SIZE); + if (pageaddr == NULL) + goto map_failed; + + data_pages[k] = virt_to_page(pageaddr); + k++; + } + + sh_pages_info->lvl2_table = lvl2_table; + sh_pages_info->lvl3_table = lvl3_table; + sh_pages_info->lvl3_gref = lvl3_gref; + sh_pages_info->n_lvl2_refs = n_lvl2_refs; + sh_pages_info->nents_last = nents_last; + sh_pages_info->data_pages = data_pages; + sh_pages_info->vmid = vmid; + + return data_pages; + +map_failed: + dev_err(hy_drv_priv->dev, + "Cannot map guest memory\n"); + + kfree(lvl2_table); + kfree(data_pages); + kfree(sh_pages_info); + + return NULL; +} + +/* + * TODO: In theory pages don't need to be unmaped, + * as ACRN is just translating memory addresses, + * but not sure if that will work the same way in future + */ +static int virtio_be_unmap_shared_pages(void **refs_info, int nents) +{ + struct virtio_shared_pages_info *sh_pages_info; + int vmid; + int i, j; + + sh_pages_info = (struct virtio_shared_pages_info *)(*refs_info); + + if (sh_pages_info->data_pages == NULL) { + dev_warn(hy_drv_priv->dev, + "Imported pages already cleaned up"); + dev_warn(hy_drv_priv->dev, + "or buffer was not imported yet\n"); + return 0; + } + vmid = sh_pages_info->vmid; + + for (i = 0; i < sh_pages_info->n_lvl2_refs - 1; i++) { + for (j = 0; j < REFS_PER_PAGE; j++) + unmap_guest_phys(vmid, + sh_pages_info->lvl2_table[i][j]); + } + + for (j = 0; j < sh_pages_info->nents_last; j++) + unmap_guest_phys(vmid, sh_pages_info->lvl2_table[i][j]); + + for (i = 0; i < sh_pages_info->n_lvl2_refs; i++) + unmap_guest_phys(vmid, sh_pages_info->lvl3_table[i]); + + unmap_guest_phys(vmid, sh_pages_info->lvl3_gref); + + kfree(sh_pages_info->lvl2_table); + kfree(sh_pages_info->data_pages); + sh_pages_info->data_pages = NULL; + kfree(sh_pages_info); + sh_pages_info = NULL; + + return 0; +} +#else +static int virtio_fe_share_pages(struct page **pages, + int domid, int nents, + void **refs_info) +{ + struct virtio_shared_pages_info *sh_pages_info; + u64 lvl3_gref; + u64 *lvl2_table; + u64 *lvl3_table; + int i; + + /* + * Calculate number of pages needed for 2nd level addresing: + */ + int n_lvl2_grefs = (nents/REFS_PER_PAGE + + ((nents % REFS_PER_PAGE) ? 1 : 0)); + + lvl3_table = (u64 *)__get_free_pages(GFP_KERNEL, 1); + lvl2_table = (u64 *)__get_free_pages(GFP_KERNEL, n_lvl2_grefs); + + sh_pages_info = kmalloc(sizeof(*sh_pages_info), GFP_KERNEL); + + if (sh_pages_info == NULL) + return -ENOMEM; + + *refs_info = (void *)sh_pages_info; + + /* Share physical address of pages */ + for (i = 0; i < nents; i++) + lvl2_table[i] = page_to_phys(pages[i]); + + for (i = 0; i < n_lvl2_grefs; i++) + lvl3_table[i] = + virt_to_phys((void *)lvl2_table + i * PAGE_SIZE); + + lvl3_gref = virt_to_phys(lvl3_table); + + sh_pages_info->lvl3_table = lvl3_table; + sh_pages_info->lvl2_table = lvl2_table; + sh_pages_info->lvl3_gref = lvl3_gref; + + return lvl3_gref; +} + +static int virtio_fe_unshare_pages(void **refs_info, + int nents) +{ + struct virtio_shared_pages_info *sh_pages_info; + int n_lvl2_grefs = (nents/REFS_PER_PAGE + + ((nents % REFS_PER_PAGE) ? 1 : 0)); + + sh_pages_info = (struct virtio_shared_pages_info *)(*refs_info); + + if (sh_pages_info == NULL) { + dev_err(hy_drv_priv->dev, + "No pages info\n"); + return -EINVAL; + } + + free_pages((unsigned long)sh_pages_info->lvl2_table, n_lvl2_grefs); + free_pages((unsigned long)sh_pages_info->lvl3_table, 1); + + kfree(sh_pages_info); + + return 0; +} + +static struct page **virtio_fe_map_shared_pages(unsigned long lvl3_gref, + int vmid, int nents, + void **refs_info) +{ + dev_dbg(hy_drv_priv->dev, + "Virtio frontend not supporting currently page mapping\n"); + return NULL; +} + +static int virtio_fe_unmap_shared_pages(void **refs_info, int nents) +{ + dev_dbg(hy_drv_priv->dev, + "Virtio frontend not supporting currently page mapping\n"); + return -EINVAL; +} + +#endif + +int virtio_share_pages(struct page **pages, + int domid, int nents, + void **refs_info) +{ + int ret; +#ifdef CONFIG_HYPER_DMABUF_VIRTIO_BE + ret = virtio_be_share_pages(pages, domid, nents, refs_info); +#else + ret = virtio_fe_share_pages(pages, domid, nents, refs_info); +#endif + return ret; +} + +int virtio_unshare_pages(void **refs_info, int nents) +{ + int ret; +#ifdef CONFIG_HYPER_DMABUF_VIRTIO_BE + ret = virtio_be_unshare_pages(refs_info, nents); +#else + ret = virtio_fe_unshare_pages(refs_info, nents); +#endif + return ret; +} + +struct page **virtio_map_shared_pages(unsigned long lvl3_gref, + int vmid, int nents, + void **refs_info) +{ + struct page **ret; +#ifdef CONFIG_HYPER_DMABUF_VIRTIO_BE + ret = virtio_be_map_shared_pages(lvl3_gref, vmid, + nents, refs_info); +#else + ret = virtio_fe_map_shared_pages(lvl3_gref, vmid, + nents, refs_info); +#endif + return ret; +} + +int virtio_unmap_shared_pages(void **refs_info, int nents) +{ + int ret; +#ifdef CONFIG_HYPER_DMABUF_VIRTIO_BE + ret = virtio_be_unmap_shared_pages(refs_info, nents); +#else + ret = virtio_fe_unmap_shared_pages(refs_info, nents); +#endif + return ret; +} diff --git a/drivers/dma-buf/hyper_dmabuf/virtio/hyper_dmabuf_virtio_shm.h b/drivers/dma-buf/hyper_dmabuf/virtio/hyper_dmabuf_virtio_shm.h new file mode 100644 index 000000000000..05cbf5779f86 --- /dev/null +++ b/drivers/dma-buf/hyper_dmabuf/virtio/hyper_dmabuf_virtio_shm.h @@ -0,0 +1,40 @@ +/* + * Copyright © 2017 Intel Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + * + */ + +#ifndef __HYPER_DMABUF_VIRTIO_SHM_H__ +#define __HYPER_DMABUF_VIRTIO_SHM_H__ + +int virtio_share_pages(struct page **pages, + int domid, int nents, + void **refs_info); + +int virtio_unshare_pages(void **refs_info, int nents); + +struct page **virtio_map_shared_pages(unsigned long lvl3_gref, + int vmid, int nents, + void **refs_info); + +int virtio_unmap_shared_pages(void **refs_info, int nents); + +#endif /* __HYPER_DMABUF_VIRTIO_SHM_H__*/ diff --git a/drivers/dma-buf/hyper_dmabuf/xen/hyper_dmabuf_xen_comm.c b/drivers/dma-buf/hyper_dmabuf/xen/hyper_dmabuf_xen_comm.c new file mode 100644 index 000000000000..3dd49db66e31 --- /dev/null +++ b/drivers/dma-buf/hyper_dmabuf/xen/hyper_dmabuf_xen_comm.c @@ -0,0 +1,951 @@ +/* + * Copyright © 2017 Intel Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + * + * Authors: + * Dongwon Kim + * Mateusz Polrola + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "hyper_dmabuf_xen_comm.h" +#include "hyper_dmabuf_xen_comm_list.h" +#include "../hyper_dmabuf_drv.h" + +static int export_req_id; + +struct hyper_dmabuf_req req_pending = {0}; + +static void xen_get_domid_delayed(struct work_struct *unused); +static void xen_init_comm_env_delayed(struct work_struct *unused); + +static DECLARE_DELAYED_WORK(get_vm_id_work, xen_get_domid_delayed); +static DECLARE_DELAYED_WORK(xen_init_comm_env_work, xen_init_comm_env_delayed); + +/* Creates entry in xen store that will keep details of all + * exporter rings created by this domain + */ +static int xen_comm_setup_data_dir(void) +{ + char buf[255]; + + sprintf(buf, "/local/domain/%d/data/hyper_dmabuf", + hy_drv_priv->domid); + + return xenbus_mkdir(XBT_NIL, buf, ""); +} + +/* Removes entry from xenstore with exporter ring details. + * Other domains that has connected to any of exporter rings + * created by this domain, will be notified about removal of + * this entry and will treat that as signal to cleanup importer + * rings created for this domain + */ +static int xen_comm_destroy_data_dir(void) +{ + char buf[255]; + + sprintf(buf, "/local/domain/%d/data/hyper_dmabuf", + hy_drv_priv->domid); + + return xenbus_rm(XBT_NIL, buf, ""); +} + +/* Adds xenstore entries with details of exporter ring created + * for given remote domain. It requires special daemon running + * in dom0 to make sure that given remote domain will have right + * permissions to access that data. + */ +static int xen_comm_expose_ring_details(int domid, int rdomid, + int gref, int port) +{ + char buf[255]; + int ret; + + sprintf(buf, "/local/domain/%d/data/hyper_dmabuf/%d", + domid, rdomid); + + ret = xenbus_printf(XBT_NIL, buf, "grefid", "%d", gref); + + if (ret) { + dev_err(hy_drv_priv->dev, + "Failed to write xenbus entry %s: %d\n", + buf, ret); + + return ret; + } + + ret = xenbus_printf(XBT_NIL, buf, "port", "%d", port); + + if (ret) { + dev_err(hy_drv_priv->dev, + "Failed to write xenbus entry %s: %d\n", + buf, ret); + + return ret; + } + + return 0; +} + +/* + * Queries details of ring exposed by remote domain. + */ +static int xen_comm_get_ring_details(int domid, int rdomid, + int *grefid, int *port) +{ + char buf[255]; + int ret; + + sprintf(buf, "/local/domain/%d/data/hyper_dmabuf/%d", + rdomid, domid); + + ret = xenbus_scanf(XBT_NIL, buf, "grefid", "%d", grefid); + + if (ret <= 0) { + dev_err(hy_drv_priv->dev, + "Failed to read xenbus entry %s: %d\n", + buf, ret); + + return 1; + } + + ret = xenbus_scanf(XBT_NIL, buf, "port", "%d", port); + + if (ret <= 0) { + dev_err(hy_drv_priv->dev, + "Failed to read xenbus entry %s: %d\n", + buf, ret); + + return 1; + } + + return 0; +} + +static void xen_get_domid_delayed(struct work_struct *unused) +{ + struct xenbus_transaction xbt; + int domid, ret; + + /* scheduling another if driver is still running + * and xenstore has not been initialized + */ + if (likely(xenstored_ready == 0)) { + dev_dbg(hy_drv_priv->dev, + "Xenstore is not ready yet. Will retry in 500ms\n"); + schedule_delayed_work(&get_vm_id_work, msecs_to_jiffies(500)); + } else { + xenbus_transaction_start(&xbt); + + ret = xenbus_scanf(xbt, "domid", "", "%d", &domid); + + if (ret <= 0) + domid = -1; + + xenbus_transaction_end(xbt, 0); + + /* try again since -1 is an invalid id for domain + * (but only if driver is still running) + */ + if (unlikely(domid == -1)) { + dev_dbg(hy_drv_priv->dev, + "domid==-1 is invalid. Will retry it in 500ms\n"); + schedule_delayed_work(&get_vm_id_work, + msecs_to_jiffies(500)); + } else { + dev_info(hy_drv_priv->dev, + "Successfully retrieved domid from Xenstore:%d\n", + domid); + hy_drv_priv->domid = domid; + } + } +} + +int xen_be_get_domid(void) +{ + struct xenbus_transaction xbt; + int domid; + + if (unlikely(xenstored_ready == 0)) { + xen_get_domid_delayed(NULL); + return -1; + } + + xenbus_transaction_start(&xbt); + + if (!xenbus_scanf(xbt, "domid", "", "%d", &domid)) + domid = -1; + + xenbus_transaction_end(xbt, 0); + + return domid; +} + +static int xen_comm_next_req_id(void) +{ + export_req_id++; + return export_req_id; +} + +/* For now cache latast rings as global variables TODO: keep them in list*/ +static irqreturn_t front_ring_isr(int irq, void *info); +static irqreturn_t back_ring_isr(int irq, void *info); + +/* Callback function that will be called on any change of xenbus path + * being watched. Used for detecting creation/destruction of remote + * domain exporter ring. + * + * When remote domain's exporter ring will be detected, importer ring + * on this domain will be created. + * + * When remote domain's exporter ring destruction will be detected it + * will celanup this domain importer ring. + * + * Destruction can be caused by unloading module by remote domain or + * it's crash/force shutdown. + */ +static void remote_dom_exporter_watch_cb(struct xenbus_watch *watch, + const char *path, const char *token) +{ + int rdom, ret; + uint32_t grefid, port; + struct xen_comm_rx_ring_info *ring_info; + + /* Check which domain has changed its exporter rings */ + ret = sscanf(watch->node, "/local/domain/%d/", &rdom); + if (ret <= 0) + return; + + /* Check if we have importer ring for given remote domain already + * created + */ + ring_info = xen_comm_find_rx_ring(rdom); + + /* Try to query remote domain exporter ring details - if + * that will fail and we have importer ring that means remote + * domains has cleanup its exporter ring, so our importer ring + * is no longer useful. + * + * If querying details will succeed and we don't have importer ring, + * it means that remote domain has setup it for us and we should + * connect to it. + */ + + ret = xen_comm_get_ring_details(xen_be_get_domid(), + rdom, &grefid, &port); + + if (ring_info && ret != 0) { + dev_info(hy_drv_priv->dev, + "Remote exporter closed, cleaninup importer\n"); + xen_be_cleanup_rx_rbuf(rdom); + } else if (!ring_info && ret == 0) { + dev_info(hy_drv_priv->dev, + "Registering importer\n"); + xen_be_init_rx_rbuf(rdom); + } +} + +/* exporter needs to generated info for page sharing */ +int xen_be_init_tx_rbuf(int domid) +{ + struct xen_comm_tx_ring_info *ring_info; + struct xen_comm_sring *sring; + struct evtchn_alloc_unbound alloc_unbound; + struct evtchn_close close; + + void *shared_ring; + int ret; + + /* check if there's any existing tx channel in the table */ + ring_info = xen_comm_find_tx_ring(domid); + + if (ring_info) { + dev_info(hy_drv_priv->dev, + "tx ring ch to domid = %d already exist\ngref = %d, port = %d\n", + ring_info->rdomain, ring_info->gref_ring, ring_info->port); + return 0; + } + + ring_info = kmalloc(sizeof(*ring_info), GFP_KERNEL); + + if (!ring_info) + return -ENOMEM; + + /* from exporter to importer */ + shared_ring = (void *)__get_free_pages(GFP_KERNEL, 1); + if (shared_ring == 0) { + kfree(ring_info); + return -ENOMEM; + } + + sring = (struct xen_comm_sring *) shared_ring; + + SHARED_RING_INIT(sring); + + FRONT_RING_INIT(&(ring_info->ring_front), sring, PAGE_SIZE); + + ring_info->gref_ring = gnttab_grant_foreign_access(domid, + virt_to_mfn(shared_ring), + 0); + if (ring_info->gref_ring < 0) { + /* fail to get gref */ + kfree(ring_info); + return -EFAULT; + } + + alloc_unbound.dom = DOMID_SELF; + alloc_unbound.remote_dom = domid; + ret = HYPERVISOR_event_channel_op(EVTCHNOP_alloc_unbound, + &alloc_unbound); + if (ret) { + dev_err(hy_drv_priv->dev, + "Cannot allocate event channel\n"); + kfree(ring_info); + return -EIO; + } + + /* setting up interrupt */ + ret = bind_evtchn_to_irqhandler(alloc_unbound.port, + front_ring_isr, 0, + NULL, (void *) ring_info); + + if (ret < 0) { + dev_err(hy_drv_priv->dev, + "Failed to setup event channel\n"); + close.port = alloc_unbound.port; + HYPERVISOR_event_channel_op(EVTCHNOP_close, &close); + gnttab_end_foreign_access(ring_info->gref_ring, 0, + virt_to_mfn(shared_ring)); + kfree(ring_info); + return -EIO; + } + + ring_info->rdomain = domid; + ring_info->irq = ret; + ring_info->port = alloc_unbound.port; + + mutex_init(&ring_info->lock); + + dev_dbg(hy_drv_priv->dev, + "%s: allocated eventchannel gref %d port: %d irq: %d\n", + __func__, + ring_info->gref_ring, + ring_info->port, + ring_info->irq); + + ret = xen_comm_add_tx_ring(ring_info); + + if (ret < 0) { + kfree(ring_info); + return -ENOMEM; + } + + ret = xen_comm_expose_ring_details(xen_be_get_domid(), + domid, + ring_info->gref_ring, + ring_info->port); + + /* Register watch for remote domain exporter ring. + * When remote domain will setup its exporter ring, + * we will automatically connect our importer ring to it. + */ + ring_info->watch.callback = remote_dom_exporter_watch_cb; + ring_info->watch.node = kmalloc(255, GFP_KERNEL); + + if (!ring_info->watch.node) { + kfree(ring_info); + return -ENOMEM; + } + + sprintf((char *)ring_info->watch.node, + "/local/domain/%d/data/hyper_dmabuf/%d/port", + domid, xen_be_get_domid()); + + register_xenbus_watch(&ring_info->watch); + + return ret; +} + +/* cleans up exporter ring created for given remote domain */ +void xen_be_cleanup_tx_rbuf(int domid) +{ + struct xen_comm_tx_ring_info *ring_info; + struct xen_comm_rx_ring_info *rx_ring_info; + + /* check if we at all have exporter ring for given rdomain */ + ring_info = xen_comm_find_tx_ring(domid); + + if (!ring_info) + return; + + xen_comm_remove_tx_ring(domid); + + unregister_xenbus_watch(&ring_info->watch); + kfree(ring_info->watch.node); + + /* No need to close communication channel, will be done by + * this function + */ + unbind_from_irqhandler(ring_info->irq, (void *) ring_info); + + /* No need to free sring page, will be freed by this function + * when other side will end its access + */ + gnttab_end_foreign_access(ring_info->gref_ring, 0, + (unsigned long) ring_info->ring_front.sring); + + kfree(ring_info); + + rx_ring_info = xen_comm_find_rx_ring(domid); + if (!rx_ring_info) + return; + + BACK_RING_INIT(&(rx_ring_info->ring_back), + rx_ring_info->ring_back.sring, + PAGE_SIZE); +} + +/* importer needs to know about shared page and port numbers for + * ring buffer and event channel + */ +int xen_be_init_rx_rbuf(int domid) +{ + struct xen_comm_rx_ring_info *ring_info; + struct xen_comm_sring *sring; + + struct page *shared_ring; + + struct gnttab_map_grant_ref *map_ops; + + int ret; + int rx_gref, rx_port; + + /* check if there's existing rx ring channel */ + ring_info = xen_comm_find_rx_ring(domid); + + if (ring_info) { + dev_info(hy_drv_priv->dev, + "rx ring ch from domid = %d already exist\n", + ring_info->sdomain); + + return 0; + } + + ret = xen_comm_get_ring_details(xen_be_get_domid(), domid, + &rx_gref, &rx_port); + + if (ret) { + dev_err(hy_drv_priv->dev, + "Domain %d has not created exporter ring for current domain\n", + domid); + + return ret; + } + + ring_info = kmalloc(sizeof(*ring_info), GFP_KERNEL); + + if (!ring_info) + return -ENOMEM; + + ring_info->sdomain = domid; + ring_info->evtchn = rx_port; + + map_ops = kmalloc(sizeof(*map_ops), GFP_KERNEL); + + if (!map_ops) { + ret = -ENOMEM; + goto fail_no_map_ops; + } + + if (gnttab_alloc_pages(1, &shared_ring)) { + ret = -ENOMEM; + goto fail_others; + } + + gnttab_set_map_op(&map_ops[0], + (unsigned long)pfn_to_kaddr( + page_to_pfn(shared_ring)), + GNTMAP_host_map, rx_gref, domid); + + gnttab_set_unmap_op(&ring_info->unmap_op, + (unsigned long)pfn_to_kaddr( + page_to_pfn(shared_ring)), + GNTMAP_host_map, -1); + + ret = gnttab_map_refs(map_ops, NULL, &shared_ring, 1); + if (ret < 0) { + dev_err(hy_drv_priv->dev, "Cannot map ring\n"); + ret = -EFAULT; + goto fail_others; + } + + if (map_ops[0].status) { + dev_err(hy_drv_priv->dev, "Ring mapping failed\n"); + ret = -EFAULT; + goto fail_others; + } else { + ring_info->unmap_op.handle = map_ops[0].handle; + } + + kfree(map_ops); + + sring = (struct xen_comm_sring *)pfn_to_kaddr(page_to_pfn(shared_ring)); + + BACK_RING_INIT(&ring_info->ring_back, sring, PAGE_SIZE); + + ret = bind_interdomain_evtchn_to_irq(domid, rx_port); + + if (ret < 0) { + ret = -EIO; + goto fail_others; + } + + ring_info->irq = ret; + + dev_dbg(hy_drv_priv->dev, + "%s: bound to eventchannel port: %d irq: %d\n", __func__, + rx_port, + ring_info->irq); + + ret = xen_comm_add_rx_ring(ring_info); + + if (ret < 0) { + ret = -ENOMEM; + goto fail_others; + } + + /* Setup communcation channel in opposite direction */ + if (!xen_comm_find_tx_ring(domid)) + ret = xen_be_init_tx_rbuf(domid); + + ret = request_irq(ring_info->irq, + back_ring_isr, 0, + NULL, (void *)ring_info); + + return ret; + +fail_others: + kfree(map_ops); + +fail_no_map_ops: + kfree(ring_info); + + return ret; +} + +/* clenas up importer ring create for given source domain */ +void xen_be_cleanup_rx_rbuf(int domid) +{ + struct xen_comm_rx_ring_info *ring_info; + struct xen_comm_tx_ring_info *tx_ring_info; + struct page *shared_ring; + + /* check if we have importer ring created for given sdomain */ + ring_info = xen_comm_find_rx_ring(domid); + + if (!ring_info) + return; + + xen_comm_remove_rx_ring(domid); + + /* no need to close event channel, will be done by that function */ + unbind_from_irqhandler(ring_info->irq, (void *)ring_info); + + /* unmapping shared ring page */ + shared_ring = virt_to_page(ring_info->ring_back.sring); + gnttab_unmap_refs(&ring_info->unmap_op, NULL, &shared_ring, 1); + gnttab_free_pages(1, &shared_ring); + + kfree(ring_info); + + tx_ring_info = xen_comm_find_tx_ring(domid); + if (!tx_ring_info) + return; + + SHARED_RING_INIT(tx_ring_info->ring_front.sring); + FRONT_RING_INIT(&(tx_ring_info->ring_front), + tx_ring_info->ring_front.sring, + PAGE_SIZE); +} + +#ifdef CONFIG_HYPER_DMABUF_XEN_AUTO_RX_CH_ADD + +static void xen_rx_ch_add_delayed(struct work_struct *unused); + +static DECLARE_DELAYED_WORK(xen_rx_ch_auto_add_work, xen_rx_ch_add_delayed); + +#define DOMID_SCAN_START 1 /* domid = 1 */ +#define DOMID_SCAN_END 10 /* domid = 10 */ + +static void xen_rx_ch_add_delayed(struct work_struct *unused) +{ + int ret; + char buf[128]; + int i, dummy; + + dev_dbg(hy_drv_priv->dev, + "Scanning new tx channel comming from another domain\n"); + + /* check other domains and schedule another work if driver + * is still running and backend is valid + */ + if (hy_drv_priv && + hy_drv_priv->initialized) { + for (i = DOMID_SCAN_START; i < DOMID_SCAN_END + 1; i++) { + if (i == hy_drv_priv->domid) + continue; + + sprintf(buf, "/local/domain/%d/data/hyper_dmabuf/%d", + i, hy_drv_priv->domid); + + ret = xenbus_scanf(XBT_NIL, buf, "port", "%d", &dummy); + + if (ret > 0) { + if (xen_comm_find_rx_ring(i) != NULL) + continue; + + ret = xen_be_init_rx_rbuf(i); + + if (!ret) + dev_info(hy_drv_priv->dev, + "Done rx ch init for VM %d\n", + i); + } + } + + /* check every 10 seconds */ + schedule_delayed_work(&xen_rx_ch_auto_add_work, + msecs_to_jiffies(10000)); + } +} + +#endif /* CONFIG_HYPER_DMABUF_XEN_AUTO_RX_CH_ADD */ + +void xen_init_comm_env_delayed(struct work_struct *unused) +{ + int ret; + + /* scheduling another work if driver is still running + * and xenstore hasn't been initialized or dom_id hasn't + * been correctly retrieved. + */ + if (likely(xenstored_ready == 0 || + hy_drv_priv->domid == -1)) { + dev_dbg(hy_drv_priv->dev, + "Xenstore not ready Will re-try in 500ms\n"); + schedule_delayed_work(&xen_init_comm_env_work, + msecs_to_jiffies(500)); + } else { + ret = xen_comm_setup_data_dir(); + if (ret < 0) { + dev_err(hy_drv_priv->dev, + "Failed to create data dir in Xenstore\n"); + } else { + dev_info(hy_drv_priv->dev, + "Successfully finished comm env init\n"); + hy_drv_priv->initialized = true; + +#ifdef CONFIG_HYPER_DMABUF_XEN_AUTO_RX_CH_ADD + xen_rx_ch_add_delayed(NULL); +#endif /* CONFIG_HYPER_DMABUF_XEN_AUTO_RX_CH_ADD */ + } + } +} + +int xen_be_init_comm_env(void) +{ + int ret; + + xen_comm_ring_table_init(); + + if (unlikely(xenstored_ready == 0 || + hy_drv_priv->domid == -1)) { + xen_init_comm_env_delayed(NULL); + return -1; + } + + ret = xen_comm_setup_data_dir(); + if (ret < 0) { + dev_err(hy_drv_priv->dev, + "Failed to create data dir in Xenstore\n"); + } else { + dev_info(hy_drv_priv->dev, + "Successfully finished comm env initialization\n"); + + hy_drv_priv->initialized = true; + } + + return ret; +} + +/* cleans up all tx/rx rings */ +static void xen_be_cleanup_all_rbufs(void) +{ + xen_comm_foreach_tx_ring(xen_be_cleanup_tx_rbuf); + xen_comm_foreach_rx_ring(xen_be_cleanup_rx_rbuf); +} + +void xen_be_destroy_comm(void) +{ + xen_be_cleanup_all_rbufs(); + xen_comm_destroy_data_dir(); +} + +int xen_be_send_req(int domid, struct hyper_dmabuf_req *req, + int wait) +{ + struct xen_comm_front_ring *ring; + struct hyper_dmabuf_req *new_req; + struct xen_comm_tx_ring_info *ring_info; + int notify; + + struct timeval tv_start, tv_end; + struct timeval tv_diff; + + int timeout = 1000; + + /* find a ring info for the channel */ + ring_info = xen_comm_find_tx_ring(domid); + if (!ring_info) { + dev_err(hy_drv_priv->dev, + "Can't find ring info for the channel\n"); + return -ENOENT; + } + + + ring = &ring_info->ring_front; + + do_gettimeofday(&tv_start); + + while (RING_FULL(ring)) { + dev_dbg(hy_drv_priv->dev, "RING_FULL\n"); + + if (timeout == 0) { + dev_err(hy_drv_priv->dev, + "Timeout while waiting for an entry in the ring\n"); + return -EIO; + } + usleep_range(100, 120); + timeout--; + } + + timeout = 1000; + + mutex_lock(&ring_info->lock); + + new_req = RING_GET_REQUEST(ring, ring->req_prod_pvt); + if (!new_req) { + mutex_unlock(&ring_info->lock); + dev_err(hy_drv_priv->dev, + "NULL REQUEST\n"); + return -EIO; + } + + req->req_id = xen_comm_next_req_id(); + + /* update req_pending with current request */ + memcpy(&req_pending, req, sizeof(req_pending)); + + /* pass current request to the ring */ + memcpy(new_req, req, sizeof(*new_req)); + + ring->req_prod_pvt++; + + RING_PUSH_REQUESTS_AND_CHECK_NOTIFY(ring, notify); + if (notify) + notify_remote_via_irq(ring_info->irq); + + if (wait) { + while (timeout--) { + if (req_pending.stat != + HYPER_DMABUF_REQ_NOT_RESPONDED) + break; + usleep_range(100, 120); + } + + if (timeout < 0) { + mutex_unlock(&ring_info->lock); + dev_err(hy_drv_priv->dev, + "request timed-out\n"); + return -EBUSY; + } + + mutex_unlock(&ring_info->lock); + do_gettimeofday(&tv_end); + + /* checking time duration for round-trip of a request + * for debugging + */ + if (tv_end.tv_usec >= tv_start.tv_usec) { + tv_diff.tv_sec = tv_end.tv_sec-tv_start.tv_sec; + tv_diff.tv_usec = tv_end.tv_usec-tv_start.tv_usec; + } else { + tv_diff.tv_sec = tv_end.tv_sec-tv_start.tv_sec-1; + tv_diff.tv_usec = tv_end.tv_usec+1000000- + tv_start.tv_usec; + } + + if (tv_diff.tv_sec != 0 && tv_diff.tv_usec > 16000) + dev_dbg(hy_drv_priv->dev, + "send_req:time diff: %ld sec, %ld usec\n", + tv_diff.tv_sec, tv_diff.tv_usec); + } + + mutex_unlock(&ring_info->lock); + + return 0; +} + +/* ISR for handling request */ +static irqreturn_t back_ring_isr(int irq, void *info) +{ + RING_IDX rc, rp; + struct hyper_dmabuf_req req; + struct hyper_dmabuf_resp resp; + + int notify, more_to_do; + int ret; + + struct xen_comm_rx_ring_info *ring_info; + struct xen_comm_back_ring *ring; + + ring_info = (struct xen_comm_rx_ring_info *)info; + ring = &ring_info->ring_back; + + dev_dbg(hy_drv_priv->dev, "%s\n", __func__); + + do { + rc = ring->req_cons; + rp = ring->sring->req_prod; + more_to_do = 0; + while (rc != rp) { + if (RING_REQUEST_CONS_OVERFLOW(ring, rc)) + break; + + memcpy(&req, RING_GET_REQUEST(ring, rc), sizeof(req)); + ring->req_cons = ++rc; + + ret = hyper_dmabuf_msg_parse(ring_info->sdomain, &req); + + if (ret > 0) { + /* preparing a response for the request and + * send it to the requester + */ + memcpy(&resp, &req, sizeof(resp)); + memcpy(RING_GET_RESPONSE(ring, + ring->rsp_prod_pvt), + &resp, sizeof(resp)); + ring->rsp_prod_pvt++; + + dev_dbg(hy_drv_priv->dev, + "responding to exporter for req:%d\n", + resp.resp_id); + + RING_PUSH_RESPONSES_AND_CHECK_NOTIFY(ring, + notify); + + if (notify) + notify_remote_via_irq(ring_info->irq); + } + + RING_FINAL_CHECK_FOR_REQUESTS(ring, more_to_do); + } + } while (more_to_do); + + return IRQ_HANDLED; +} + +/* ISR for handling responses */ +static irqreturn_t front_ring_isr(int irq, void *info) +{ + /* front ring only care about response from back */ + struct hyper_dmabuf_resp *resp; + RING_IDX i, rp; + int more_to_do, ret; + + struct xen_comm_tx_ring_info *ring_info; + struct xen_comm_front_ring *ring; + + ring_info = (struct xen_comm_tx_ring_info *)info; + ring = &ring_info->ring_front; + + dev_dbg(hy_drv_priv->dev, "%s\n", __func__); + + do { + more_to_do = 0; + rp = ring->sring->rsp_prod; + for (i = ring->rsp_cons; i != rp; i++) { + resp = RING_GET_RESPONSE(ring, i); + + /* update pending request's status with what is + * in the response + */ + + dev_dbg(hy_drv_priv->dev, + "getting response from importer\n"); + + if (req_pending.req_id == resp->resp_id) + req_pending.stat = resp->stat; + + if (resp->stat == HYPER_DMABUF_REQ_NEEDS_FOLLOW_UP) { + /* parsing response */ + ret = hyper_dmabuf_msg_parse(ring_info->rdomain, + (struct hyper_dmabuf_req *)resp); + + if (ret < 0) { + dev_err(hy_drv_priv->dev, + "err while parsing resp\n"); + } + } else if (resp->stat == HYPER_DMABUF_REQ_PROCESSED) { + /* for debugging dma_buf remote synch */ + dev_dbg(hy_drv_priv->dev, + "original request = 0x%x\n", resp->cmd); + dev_dbg(hy_drv_priv->dev, + "got HYPER_DMABUF_REQ_PROCESSED\n"); + } else if (resp->stat == HYPER_DMABUF_REQ_ERROR) { + /* for debugging dma_buf remote synch */ + dev_dbg(hy_drv_priv->dev, + "original request = 0x%x\n", resp->cmd); + dev_dbg(hy_drv_priv->dev, + "got HYPER_DMABUF_REQ_ERROR\n"); + } + } + + ring->rsp_cons = i; + + if (i != ring->req_prod_pvt) + RING_FINAL_CHECK_FOR_RESPONSES(ring, more_to_do); + else + ring->sring->rsp_event = i+1; + + } while (more_to_do); + + return IRQ_HANDLED; +} diff --git a/drivers/dma-buf/hyper_dmabuf/xen/hyper_dmabuf_xen_comm.h b/drivers/dma-buf/hyper_dmabuf/xen/hyper_dmabuf_xen_comm.h new file mode 100644 index 000000000000..70a2b704badd --- /dev/null +++ b/drivers/dma-buf/hyper_dmabuf/xen/hyper_dmabuf_xen_comm.h @@ -0,0 +1,78 @@ +/* + * Copyright © 2017 Intel Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + * + */ + +#ifndef __HYPER_DMABUF_XEN_COMM_H__ +#define __HYPER_DMABUF_XEN_COMM_H__ + +#include "xen/interface/io/ring.h" +#include "xen/xenbus.h" +#include "../hyper_dmabuf_msg.h" + +extern int xenstored_ready; + +DEFINE_RING_TYPES(xen_comm, struct hyper_dmabuf_req, struct hyper_dmabuf_resp); + +struct xen_comm_tx_ring_info { + struct xen_comm_front_ring ring_front; + int rdomain; + int gref_ring; + int irq; + int port; + struct mutex lock; + struct xenbus_watch watch; +}; + +struct xen_comm_rx_ring_info { + int sdomain; + int irq; + int evtchn; + struct xen_comm_back_ring ring_back; + struct gnttab_unmap_grant_ref unmap_op; +}; + +int xen_be_get_domid(void); + +int xen_be_init_comm_env(void); + +/* exporter needs to generated info for page sharing */ +int xen_be_init_tx_rbuf(int domid); + +/* importer needs to know about shared page and port numbers + * for ring buffer and event channel + */ +int xen_be_init_rx_rbuf(int domid); + +/* cleans up exporter ring created for given domain */ +void xen_be_cleanup_tx_rbuf(int domid); + +/* cleans up importer ring created for given domain */ +void xen_be_cleanup_rx_rbuf(int domid); + +void xen_be_destroy_comm(void); + +/* send request to the remote domain */ +int xen_be_send_req(int domid, struct hyper_dmabuf_req *req, + int wait); + +#endif /* __HYPER_DMABUF_XEN_COMM_H__ */ diff --git a/drivers/dma-buf/hyper_dmabuf/xen/hyper_dmabuf_xen_comm_list.c b/drivers/dma-buf/hyper_dmabuf/xen/hyper_dmabuf_xen_comm_list.c new file mode 100644 index 000000000000..15023dbc8ced --- /dev/null +++ b/drivers/dma-buf/hyper_dmabuf/xen/hyper_dmabuf_xen_comm_list.c @@ -0,0 +1,158 @@ +/* + * Copyright © 2017 Intel Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + * + * Authors: + * Dongwon Kim + * Mateusz Polrola + * + */ + +#include +#include +#include +#include +#include +#include +#include "../hyper_dmabuf_drv.h" +#include "hyper_dmabuf_xen_comm.h" +#include "hyper_dmabuf_xen_comm_list.h" + +DECLARE_HASHTABLE(xen_comm_tx_ring_hash, MAX_ENTRY_TX_RING); +DECLARE_HASHTABLE(xen_comm_rx_ring_hash, MAX_ENTRY_RX_RING); + +void xen_comm_ring_table_init(void) +{ + hash_init(xen_comm_rx_ring_hash); + hash_init(xen_comm_tx_ring_hash); +} + +int xen_comm_add_tx_ring(struct xen_comm_tx_ring_info *ring_info) +{ + struct xen_comm_tx_ring_info_entry *info_entry; + + info_entry = kmalloc(sizeof(*info_entry), GFP_KERNEL); + + if (!info_entry) + return -ENOMEM; + + info_entry->info = ring_info; + + hash_add(xen_comm_tx_ring_hash, &info_entry->node, + info_entry->info->rdomain); + + return 0; +} + +int xen_comm_add_rx_ring(struct xen_comm_rx_ring_info *ring_info) +{ + struct xen_comm_rx_ring_info_entry *info_entry; + + info_entry = kmalloc(sizeof(*info_entry), GFP_KERNEL); + + if (!info_entry) + return -ENOMEM; + + info_entry->info = ring_info; + + hash_add(xen_comm_rx_ring_hash, &info_entry->node, + info_entry->info->sdomain); + + return 0; +} + +struct xen_comm_tx_ring_info *xen_comm_find_tx_ring(int domid) +{ + struct xen_comm_tx_ring_info_entry *info_entry; + int bkt; + + hash_for_each(xen_comm_tx_ring_hash, bkt, info_entry, node) + if (info_entry->info->rdomain == domid) + return info_entry->info; + + return NULL; +} + +struct xen_comm_rx_ring_info *xen_comm_find_rx_ring(int domid) +{ + struct xen_comm_rx_ring_info_entry *info_entry; + int bkt; + + hash_for_each(xen_comm_rx_ring_hash, bkt, info_entry, node) + if (info_entry->info->sdomain == domid) + return info_entry->info; + + return NULL; +} + +int xen_comm_remove_tx_ring(int domid) +{ + struct xen_comm_tx_ring_info_entry *info_entry; + int bkt; + + hash_for_each(xen_comm_tx_ring_hash, bkt, info_entry, node) + if (info_entry->info->rdomain == domid) { + hash_del(&info_entry->node); + kfree(info_entry); + return 0; + } + + return -ENOENT; +} + +int xen_comm_remove_rx_ring(int domid) +{ + struct xen_comm_rx_ring_info_entry *info_entry; + int bkt; + + hash_for_each(xen_comm_rx_ring_hash, bkt, info_entry, node) + if (info_entry->info->sdomain == domid) { + hash_del(&info_entry->node); + kfree(info_entry); + return 0; + } + + return -ENOENT; +} + +void xen_comm_foreach_tx_ring(void (*func)(int domid)) +{ + struct xen_comm_tx_ring_info_entry *info_entry; + struct hlist_node *tmp; + int bkt; + + hash_for_each_safe(xen_comm_tx_ring_hash, bkt, tmp, + info_entry, node) { + func(info_entry->info->rdomain); + } +} + +void xen_comm_foreach_rx_ring(void (*func)(int domid)) +{ + struct xen_comm_rx_ring_info_entry *info_entry; + struct hlist_node *tmp; + int bkt; + + hash_for_each_safe(xen_comm_rx_ring_hash, bkt, tmp, + info_entry, node) { + func(info_entry->info->sdomain); + } +} diff --git a/drivers/dma-buf/hyper_dmabuf/xen/hyper_dmabuf_xen_comm_list.h b/drivers/dma-buf/hyper_dmabuf/xen/hyper_dmabuf_xen_comm_list.h new file mode 100644 index 000000000000..8502fe7df578 --- /dev/null +++ b/drivers/dma-buf/hyper_dmabuf/xen/hyper_dmabuf_xen_comm_list.h @@ -0,0 +1,67 @@ +/* + * Copyright © 2017 Intel Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + * + */ + +#ifndef __HYPER_DMABUF_XEN_COMM_LIST_H__ +#define __HYPER_DMABUF_XEN_COMM_LIST_H__ + +/* number of bits to be used for exported dmabufs hash table */ +#define MAX_ENTRY_TX_RING 7 +/* number of bits to be used for imported dmabufs hash table */ +#define MAX_ENTRY_RX_RING 7 + +struct xen_comm_tx_ring_info_entry { + struct xen_comm_tx_ring_info *info; + struct hlist_node node; +}; + +struct xen_comm_rx_ring_info_entry { + struct xen_comm_rx_ring_info *info; + struct hlist_node node; +}; + +void xen_comm_ring_table_init(void); + +int xen_comm_add_tx_ring(struct xen_comm_tx_ring_info *ring_info); + +int xen_comm_add_rx_ring(struct xen_comm_rx_ring_info *ring_info); + +int xen_comm_remove_tx_ring(int domid); + +int xen_comm_remove_rx_ring(int domid); + +struct xen_comm_tx_ring_info *xen_comm_find_tx_ring(int domid); + +struct xen_comm_rx_ring_info *xen_comm_find_rx_ring(int domid); + +/* iterates over all exporter rings and calls provided + * function for each of them + */ +void xen_comm_foreach_tx_ring(void (*func)(int domid)); + +/* iterates over all importer rings and calls provided + * function for each of them + */ +void xen_comm_foreach_rx_ring(void (*func)(int domid)); + +#endif // __HYPER_DMABUF_XEN_COMM_LIST_H__ diff --git a/drivers/dma-buf/hyper_dmabuf/xen/hyper_dmabuf_xen_drv.c b/drivers/dma-buf/hyper_dmabuf/xen/hyper_dmabuf_xen_drv.c new file mode 100644 index 000000000000..14ed3bc51e6a --- /dev/null +++ b/drivers/dma-buf/hyper_dmabuf/xen/hyper_dmabuf_xen_drv.c @@ -0,0 +1,46 @@ +/* + * Copyright © 2017 Intel Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + * + * Authors: + * Dongwon Kim + * Mateusz Polrola + * + */ + +#include "../hyper_dmabuf_drv.h" +#include "hyper_dmabuf_xen_comm.h" +#include "hyper_dmabuf_xen_shm.h" + +struct hyper_dmabuf_bknd_ops xen_bknd_ops = { + .init = NULL, /* not needed for xen */ + .cleanup = NULL, /* not needed for xen */ + .get_vm_id = xen_be_get_domid, + .share_pages = xen_be_share_pages, + .unshare_pages = xen_be_unshare_pages, + .map_shared_pages = (void *)xen_be_map_shared_pages, + .unmap_shared_pages = xen_be_unmap_shared_pages, + .init_comm_env = xen_be_init_comm_env, + .destroy_comm = xen_be_destroy_comm, + .init_rx_ch = xen_be_init_rx_rbuf, + .init_tx_ch = xen_be_init_tx_rbuf, + .send_req = xen_be_send_req, +}; diff --git a/drivers/dma-buf/hyper_dmabuf/xen/hyper_dmabuf_xen_drv.h b/drivers/dma-buf/hyper_dmabuf/xen/hyper_dmabuf_xen_drv.h new file mode 100644 index 000000000000..a4902b747a87 --- /dev/null +++ b/drivers/dma-buf/hyper_dmabuf/xen/hyper_dmabuf_xen_drv.h @@ -0,0 +1,53 @@ +/* + * Copyright © 2017 Intel Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + * + */ + +#ifndef __HYPER_DMABUF_XEN_DRV_H__ +#define __HYPER_DMABUF_XEN_DRV_H__ +#include + +extern struct hyper_dmabuf_bknd_ops xen_bknd_ops; + +/* Main purpose of this structure is to keep + * all references created or acquired for sharing + * pages with another domain for freeing those later + * when unsharing. + */ +struct xen_shared_pages_info { + /* top level refid */ + grant_ref_t lvl3_gref; + + /* page of top level addressing, it contains refids of 2nd lvl pages */ + grant_ref_t *lvl3_table; + + /* table of 2nd level pages, that contains refids to data pages */ + grant_ref_t *lvl2_table; + + /* unmap ops for mapped pages */ + struct gnttab_unmap_grant_ref *unmap_ops; + + /* data pages to be unmapped */ + struct page **data_pages; +}; + +#endif // __HYPER_DMABUF_XEN_COMM_H__ diff --git a/drivers/dma-buf/hyper_dmabuf/xen/hyper_dmabuf_xen_shm.c b/drivers/dma-buf/hyper_dmabuf/xen/hyper_dmabuf_xen_shm.c new file mode 100644 index 000000000000..c6a15f187fe3 --- /dev/null +++ b/drivers/dma-buf/hyper_dmabuf/xen/hyper_dmabuf_xen_shm.c @@ -0,0 +1,525 @@ +/* + * Copyright © 2017 Intel Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + * + * Authors: + * Dongwon Kim + * Mateusz Polrola + * + */ + +#include +#include +#include +#include "hyper_dmabuf_xen_drv.h" +#include "../hyper_dmabuf_drv.h" + +#define REFS_PER_PAGE (PAGE_SIZE/sizeof(grant_ref_t)) + +/* + * Creates 2 level page directory structure for referencing shared pages. + * Top level page is a single page that contains up to 1024 refids that + * point to 2nd level pages. + * + * Each 2nd level page contains up to 1024 refids that point to shared + * data pages. + * + * There will always be one top level page and number of 2nd level pages + * depends on number of shared data pages. + * + * 3rd level page 2nd level pages Data pages + * +-------------------------+ ┌>+--------------------+ ┌>+------------+ + * |2nd level page 0 refid |---┘ |Data page 0 refid |-┘ |Data page 0 | + * |2nd level page 1 refid |---┐ |Data page 1 refid |-┐ +------------+ + * | ... | | | .... | | + * |2nd level page 1023 refid|-┐ | |Data page 1023 refid| └>+------------+ + * +-------------------------+ | | +--------------------+ |Data page 1 | + * | | +------------+ + * | └>+--------------------+ + * | |Data page 1024 refid| + * | |Data page 1025 refid| + * | | ... | + * | |Data page 2047 refid| + * | +--------------------+ + * | + * | ..... + * └-->+-----------------------+ + * |Data page 1047552 refid| + * |Data page 1047553 refid| + * | ... | + * |Data page 1048575 refid| + * +-----------------------+ + * + * Using such 2 level structure it is possible to reference up to 4GB of + * shared data using single refid pointing to top level page. + * + * Returns refid of top level page. + */ +int xen_be_share_pages(struct page **pages, int domid, int nents, + void **refs_info) +{ + grant_ref_t lvl3_gref; + grant_ref_t *lvl2_table; + grant_ref_t *lvl3_table; + + /* + * Calculate number of pages needed for 2nd level addresing: + */ + int n_lvl2_grefs = (nents/REFS_PER_PAGE + + ((nents % REFS_PER_PAGE) ? 1 : 0)); + + struct xen_shared_pages_info *sh_pages_info; + int i; + + lvl3_table = (grant_ref_t *)__get_free_pages(GFP_KERNEL, 1); + lvl2_table = (grant_ref_t *)__get_free_pages(GFP_KERNEL, n_lvl2_grefs); + + sh_pages_info = kmalloc(sizeof(*sh_pages_info), GFP_KERNEL); + + if (!sh_pages_info) + return -ENOMEM; + + *refs_info = (void *)sh_pages_info; + + /* share data pages in readonly mode for security */ + for (i = 0; i < nents; i++) { + lvl2_table[i] = gnttab_grant_foreign_access(domid, + pfn_to_mfn(page_to_pfn(pages[i])), + true /* read only */); + if (lvl2_table[i] == -ENOSPC) { + dev_err(hy_drv_priv->dev, + "No more space left in grant table\n"); + + /* Unshare all already shared pages for lvl2 */ + while (i--) { + gnttab_end_foreign_access_ref(lvl2_table[i], 0); + gnttab_free_grant_reference(lvl2_table[i]); + } + goto err_cleanup; + } + } + + /* Share 2nd level addressing pages in readonly mode*/ + for (i = 0; i < n_lvl2_grefs; i++) { + lvl3_table[i] = gnttab_grant_foreign_access(domid, + virt_to_mfn( + (unsigned long)lvl2_table+i*PAGE_SIZE), + true); + + if (lvl3_table[i] == -ENOSPC) { + dev_err(hy_drv_priv->dev, + "No more space left in grant table\n"); + + /* Unshare all already shared pages for lvl3 */ + while (i--) { + gnttab_end_foreign_access_ref(lvl3_table[i], 1); + gnttab_free_grant_reference(lvl3_table[i]); + } + + /* Unshare all pages for lvl2 */ + while (nents--) { + gnttab_end_foreign_access_ref( + lvl2_table[nents], 0); + gnttab_free_grant_reference(lvl2_table[nents]); + } + + goto err_cleanup; + } + } + + /* Share lvl3_table in readonly mode*/ + lvl3_gref = gnttab_grant_foreign_access(domid, + virt_to_mfn((unsigned long)lvl3_table), + true); + + if (lvl3_gref == -ENOSPC) { + dev_err(hy_drv_priv->dev, + "No more space left in grant table\n"); + + /* Unshare all pages for lvl3 */ + while (i--) { + gnttab_end_foreign_access_ref(lvl3_table[i], 1); + gnttab_free_grant_reference(lvl3_table[i]); + } + + /* Unshare all pages for lvl2 */ + while (nents--) { + gnttab_end_foreign_access_ref(lvl2_table[nents], 0); + gnttab_free_grant_reference(lvl2_table[nents]); + } + + goto err_cleanup; + } + + /* Store lvl3_table page to be freed later */ + sh_pages_info->lvl3_table = lvl3_table; + + /* Store lvl2_table pages to be freed later */ + sh_pages_info->lvl2_table = lvl2_table; + + + /* Store exported pages refid to be unshared later */ + sh_pages_info->lvl3_gref = lvl3_gref; + + dev_dbg(hy_drv_priv->dev, "%s exit\n", __func__); + return lvl3_gref; + +err_cleanup: + free_pages((unsigned long)lvl2_table, n_lvl2_grefs); + free_pages((unsigned long)lvl3_table, 1); + + return -ENOSPC; +} + +int xen_be_unshare_pages(void **refs_info, int nents) +{ + struct xen_shared_pages_info *sh_pages_info; + int n_lvl2_grefs = (nents/REFS_PER_PAGE + + ((nents % REFS_PER_PAGE) ? 1 : 0)); + int i; + + dev_dbg(hy_drv_priv->dev, "%s entry\n", __func__); + sh_pages_info = (struct xen_shared_pages_info *)(*refs_info); + + if (sh_pages_info->lvl3_table == NULL || + sh_pages_info->lvl2_table == NULL || + sh_pages_info->lvl3_gref == -1) { + dev_warn(hy_drv_priv->dev, + "gref table for hyper_dmabuf already cleaned up\n"); + return 0; + } + + /* End foreign access for data pages, but do not free them */ + for (i = 0; i < nents; i++) { + if (gnttab_query_foreign_access(sh_pages_info->lvl2_table[i])) + dev_warn(hy_drv_priv->dev, "refid not shared !!\n"); + + gnttab_end_foreign_access_ref(sh_pages_info->lvl2_table[i], 0); + gnttab_free_grant_reference(sh_pages_info->lvl2_table[i]); + } + + /* End foreign access for 2nd level addressing pages */ + for (i = 0; i < n_lvl2_grefs; i++) { + if (gnttab_query_foreign_access(sh_pages_info->lvl3_table[i])) + dev_warn(hy_drv_priv->dev, "refid not shared !!\n"); + + if (!gnttab_end_foreign_access_ref( + sh_pages_info->lvl3_table[i], 1)) + dev_warn(hy_drv_priv->dev, "refid still in use!!!\n"); + + gnttab_free_grant_reference(sh_pages_info->lvl3_table[i]); + } + + /* End foreign access for top level addressing page */ + if (gnttab_query_foreign_access(sh_pages_info->lvl3_gref)) + dev_warn(hy_drv_priv->dev, "gref not shared !!\n"); + + gnttab_end_foreign_access_ref(sh_pages_info->lvl3_gref, 1); + gnttab_free_grant_reference(sh_pages_info->lvl3_gref); + + /* freeing all pages used for 2 level addressing */ + free_pages((unsigned long)sh_pages_info->lvl2_table, n_lvl2_grefs); + free_pages((unsigned long)sh_pages_info->lvl3_table, 1); + + sh_pages_info->lvl3_gref = -1; + sh_pages_info->lvl2_table = NULL; + sh_pages_info->lvl3_table = NULL; + kfree(sh_pages_info); + sh_pages_info = NULL; + + dev_dbg(hy_drv_priv->dev, "%s exit\n", __func__); + return 0; +} + +/* Maps provided top level ref id and then return array of pages + * containing data refs. + */ +struct page **xen_be_map_shared_pages(unsigned long lvl3_gref, int domid, + int nents, void **refs_info) +{ + struct page *lvl3_table_page; + struct page **lvl2_table_pages; + struct page **data_pages; + struct xen_shared_pages_info *sh_pages_info; + + grant_ref_t *lvl3_table; + grant_ref_t *lvl2_table; + + struct gnttab_map_grant_ref lvl3_map_ops; + struct gnttab_unmap_grant_ref lvl3_unmap_ops; + + struct gnttab_map_grant_ref *lvl2_map_ops; + struct gnttab_unmap_grant_ref *lvl2_unmap_ops; + + struct gnttab_map_grant_ref *data_map_ops; + struct gnttab_unmap_grant_ref *data_unmap_ops; + + /* # of grefs in the last page of lvl2 table */ + int nents_last = (nents - 1) % REFS_PER_PAGE + 1; + int n_lvl2_grefs = (nents / REFS_PER_PAGE) + + ((nents_last > 0) ? 1 : 0) - + (nents_last == REFS_PER_PAGE); + int i, j, k; + + dev_dbg(hy_drv_priv->dev, "%s entry\n", __func__); + + sh_pages_info = kmalloc(sizeof(*sh_pages_info), GFP_KERNEL); + *refs_info = (void *) sh_pages_info; + + lvl2_table_pages = kcalloc(n_lvl2_grefs, sizeof(struct page *), + GFP_KERNEL); + + data_pages = kcalloc(nents, sizeof(struct page *), GFP_KERNEL); + + lvl2_map_ops = kcalloc(n_lvl2_grefs, sizeof(*lvl2_map_ops), + GFP_KERNEL); + + lvl2_unmap_ops = kcalloc(n_lvl2_grefs, sizeof(*lvl2_unmap_ops), + GFP_KERNEL); + + data_map_ops = kcalloc(nents, sizeof(*data_map_ops), GFP_KERNEL); + data_unmap_ops = kcalloc(nents, sizeof(*data_unmap_ops), GFP_KERNEL); + + /* Map top level addressing page */ + if (gnttab_alloc_pages(1, &lvl3_table_page)) { + dev_err(hy_drv_priv->dev, "Cannot allocate pages\n"); + return NULL; + } + + lvl3_table = (grant_ref_t *)pfn_to_kaddr(page_to_pfn(lvl3_table_page)); + + gnttab_set_map_op(&lvl3_map_ops, (unsigned long)lvl3_table, + GNTMAP_host_map | GNTMAP_readonly, + (grant_ref_t)lvl3_gref, domid); + + gnttab_set_unmap_op(&lvl3_unmap_ops, (unsigned long)lvl3_table, + GNTMAP_host_map | GNTMAP_readonly, -1); + + if (gnttab_map_refs(&lvl3_map_ops, NULL, &lvl3_table_page, 1)) { + dev_err(hy_drv_priv->dev, + "HYPERVISOR map grant ref failed"); + return NULL; + } + + if (lvl3_map_ops.status) { + dev_err(hy_drv_priv->dev, + "HYPERVISOR map grant ref failed status = %d", + lvl3_map_ops.status); + + goto error_cleanup_lvl3; + } else { + lvl3_unmap_ops.handle = lvl3_map_ops.handle; + } + + /* Map all second level pages */ + if (gnttab_alloc_pages(n_lvl2_grefs, lvl2_table_pages)) { + dev_err(hy_drv_priv->dev, "Cannot allocate pages\n"); + goto error_cleanup_lvl3; + } + + for (i = 0; i < n_lvl2_grefs; i++) { + lvl2_table = (grant_ref_t *)pfn_to_kaddr( + page_to_pfn(lvl2_table_pages[i])); + gnttab_set_map_op(&lvl2_map_ops[i], + (unsigned long)lvl2_table, GNTMAP_host_map | + GNTMAP_readonly, + lvl3_table[i], domid); + gnttab_set_unmap_op(&lvl2_unmap_ops[i], + (unsigned long)lvl2_table, GNTMAP_host_map | + GNTMAP_readonly, -1); + } + + /* Unmap top level page, as it won't be needed any longer */ + if (gnttab_unmap_refs(&lvl3_unmap_ops, NULL, + &lvl3_table_page, 1)) { + dev_err(hy_drv_priv->dev, + "xen: cannot unmap top level page\n"); + return NULL; + } + + /* Mark that page was unmapped */ + lvl3_unmap_ops.handle = -1; + + if (gnttab_map_refs(lvl2_map_ops, NULL, + lvl2_table_pages, n_lvl2_grefs)) { + dev_err(hy_drv_priv->dev, + "HYPERVISOR map grant ref failed"); + return NULL; + } + + /* Checks if pages were mapped correctly */ + for (i = 0; i < n_lvl2_grefs; i++) { + if (lvl2_map_ops[i].status) { + dev_err(hy_drv_priv->dev, + "HYPERVISOR map grant ref failed status = %d", + lvl2_map_ops[i].status); + goto error_cleanup_lvl2; + } else { + lvl2_unmap_ops[i].handle = lvl2_map_ops[i].handle; + } + } + + if (gnttab_alloc_pages(nents, data_pages)) { + dev_err(hy_drv_priv->dev, + "Cannot allocate pages\n"); + goto error_cleanup_lvl2; + } + + k = 0; + + for (i = 0; i < n_lvl2_grefs - 1; i++) { + lvl2_table = pfn_to_kaddr(page_to_pfn(lvl2_table_pages[i])); + for (j = 0; j < REFS_PER_PAGE; j++) { + gnttab_set_map_op(&data_map_ops[k], + (unsigned long)pfn_to_kaddr( + page_to_pfn(data_pages[k])), + GNTMAP_host_map | GNTMAP_readonly, + lvl2_table[j], domid); + + gnttab_set_unmap_op(&data_unmap_ops[k], + (unsigned long)pfn_to_kaddr( + page_to_pfn(data_pages[k])), + GNTMAP_host_map | GNTMAP_readonly, -1); + k++; + } + } + + /* for grefs in the last lvl2 table page */ + lvl2_table = pfn_to_kaddr(page_to_pfn( + lvl2_table_pages[n_lvl2_grefs - 1])); + + for (j = 0; j < nents_last; j++) { + gnttab_set_map_op(&data_map_ops[k], + (unsigned long)pfn_to_kaddr(page_to_pfn(data_pages[k])), + GNTMAP_host_map | GNTMAP_readonly, + lvl2_table[j], domid); + + gnttab_set_unmap_op(&data_unmap_ops[k], + (unsigned long)pfn_to_kaddr(page_to_pfn(data_pages[k])), + GNTMAP_host_map | GNTMAP_readonly, -1); + k++; + } + + if (gnttab_map_refs(data_map_ops, NULL, + data_pages, nents)) { + dev_err(hy_drv_priv->dev, + "HYPERVISOR map grant ref failed\n"); + return NULL; + } + + /* unmapping lvl2 table pages */ + if (gnttab_unmap_refs(lvl2_unmap_ops, + NULL, lvl2_table_pages, + n_lvl2_grefs)) { + dev_err(hy_drv_priv->dev, + "Cannot unmap 2nd level refs\n"); + return NULL; + } + + /* Mark that pages were unmapped */ + for (i = 0; i < n_lvl2_grefs; i++) + lvl2_unmap_ops[i].handle = -1; + + for (i = 0; i < nents; i++) { + if (data_map_ops[i].status) { + dev_err(hy_drv_priv->dev, + "HYPERVISOR map grant ref failed status = %d\n", + data_map_ops[i].status); + goto error_cleanup_data; + } else { + data_unmap_ops[i].handle = data_map_ops[i].handle; + } + } + + /* store these references for unmapping in the future */ + sh_pages_info->unmap_ops = data_unmap_ops; + sh_pages_info->data_pages = data_pages; + + gnttab_free_pages(1, &lvl3_table_page); + gnttab_free_pages(n_lvl2_grefs, lvl2_table_pages); + kfree(lvl2_table_pages); + kfree(lvl2_map_ops); + kfree(lvl2_unmap_ops); + kfree(data_map_ops); + + dev_dbg(hy_drv_priv->dev, "%s exit\n", __func__); + return data_pages; + +error_cleanup_data: + gnttab_unmap_refs(data_unmap_ops, NULL, data_pages, + nents); + + gnttab_free_pages(nents, data_pages); + +error_cleanup_lvl2: + if (lvl2_unmap_ops[0].handle != -1) + gnttab_unmap_refs(lvl2_unmap_ops, NULL, + lvl2_table_pages, n_lvl2_grefs); + gnttab_free_pages(n_lvl2_grefs, lvl2_table_pages); + +error_cleanup_lvl3: + if (lvl3_unmap_ops.handle != -1) + gnttab_unmap_refs(&lvl3_unmap_ops, NULL, + &lvl3_table_page, 1); + gnttab_free_pages(1, &lvl3_table_page); + + kfree(lvl2_table_pages); + kfree(lvl2_map_ops); + kfree(lvl2_unmap_ops); + kfree(data_map_ops); + + + return NULL; +} + +int xen_be_unmap_shared_pages(void **refs_info, int nents) +{ + struct xen_shared_pages_info *sh_pages_info; + + dev_dbg(hy_drv_priv->dev, "%s entry\n", __func__); + + sh_pages_info = (struct xen_shared_pages_info *)(*refs_info); + + if (sh_pages_info->unmap_ops == NULL || + sh_pages_info->data_pages == NULL) { + dev_warn(hy_drv_priv->dev, + "pages already cleaned up or buffer not imported yet\n"); + return 0; + } + + if (gnttab_unmap_refs(sh_pages_info->unmap_ops, NULL, + sh_pages_info->data_pages, nents)) { + dev_err(hy_drv_priv->dev, "Cannot unmap data pages\n"); + return -EFAULT; + } + + gnttab_free_pages(nents, sh_pages_info->data_pages); + + kfree(sh_pages_info->data_pages); + kfree(sh_pages_info->unmap_ops); + sh_pages_info->unmap_ops = NULL; + sh_pages_info->data_pages = NULL; + kfree(sh_pages_info); + sh_pages_info = NULL; + + dev_dbg(hy_drv_priv->dev, "%s exit\n", __func__); + return 0; +} diff --git a/drivers/dma-buf/hyper_dmabuf/xen/hyper_dmabuf_xen_shm.h b/drivers/dma-buf/hyper_dmabuf/xen/hyper_dmabuf_xen_shm.h new file mode 100644 index 000000000000..d5236b500075 --- /dev/null +++ b/drivers/dma-buf/hyper_dmabuf/xen/hyper_dmabuf_xen_shm.h @@ -0,0 +1,46 @@ +/* + * Copyright © 2017 Intel Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + * + */ + +#ifndef __HYPER_DMABUF_XEN_SHM_H__ +#define __HYPER_DMABUF_XEN_SHM_H__ + +/* This collects all reference numbers for 2nd level shared pages and + * create a table with those in 1st level shared pages then return reference + * numbers for this top level table. + */ +int xen_be_share_pages(struct page **pages, int domid, int nents, + void **refs_info); + +int xen_be_unshare_pages(void **refs_info, int nents); + +/* Maps provided top level ref id and then return array of pages containing + * data refs. + */ +struct page **xen_be_map_shared_pages(unsigned long lvl3_gref, int domid, + int nents, + void **refs_info); + +int xen_be_unmap_shared_pages(void **refs_info, int nents); + +#endif /* __HYPER_DMABUF_XEN_SHM_H__ */ diff --git a/include/uapi/linux/hyper_dmabuf.h b/include/uapi/linux/hyper_dmabuf.h new file mode 100644 index 000000000000..cb25299e2666 --- /dev/null +++ b/include/uapi/linux/hyper_dmabuf.h @@ -0,0 +1,134 @@ +/* + * Copyright © 2017 Intel Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + * + */ + +#ifndef __LINUX_PUBLIC_HYPER_DMABUF_H__ +#define __LINUX_PUBLIC_HYPER_DMABUF_H__ + +#define MAX_SIZE_PRIV_DATA 192 + +typedef struct { + int id; + int rng_key[3]; /* 12bytes long random number */ +} hyper_dmabuf_id_t; + +struct hyper_dmabuf_event_hdr { + int event_type; /* one type only for now - new import */ + hyper_dmabuf_id_t hid; /* hyper_dmabuf_id of specific hyper_dmabuf */ + int size; /* size of data */ +}; + +struct hyper_dmabuf_event_data { + struct hyper_dmabuf_event_hdr hdr; + void *data; /* private data */ +}; + +#define IOCTL_HYPER_DMABUF_TX_CH_SETUP \ +_IOC(_IOC_NONE, 'G', 0, sizeof(struct ioctl_hyper_dmabuf_tx_ch_setup)) +struct ioctl_hyper_dmabuf_tx_ch_setup { + /* IN parameters */ + /* Remote domain id */ + int remote_domain; +}; + +#define IOCTL_HYPER_DMABUF_RX_CH_SETUP \ +_IOC(_IOC_NONE, 'G', 1, sizeof(struct ioctl_hyper_dmabuf_rx_ch_setup)) +struct ioctl_hyper_dmabuf_rx_ch_setup { + /* IN parameters */ + /* Source domain id */ + int source_domain; +}; + +#define IOCTL_HYPER_DMABUF_EXPORT_REMOTE \ +_IOC(_IOC_NONE, 'G', 2, sizeof(struct ioctl_hyper_dmabuf_export_remote)) +struct ioctl_hyper_dmabuf_export_remote { + /* IN parameters */ + /* DMA buf fd to be exported */ + int dmabuf_fd; + /* Domain id to which buffer should be exported */ + int remote_domain; + /* exported dma buf id */ + hyper_dmabuf_id_t hid; + int sz_priv; + char *priv; +}; + +#define IOCTL_HYPER_DMABUF_EXPORT_FD \ +_IOC(_IOC_NONE, 'G', 3, sizeof(struct ioctl_hyper_dmabuf_export_fd)) +struct ioctl_hyper_dmabuf_export_fd { + /* IN parameters */ + /* hyper dmabuf id to be imported */ + hyper_dmabuf_id_t hid; + /* flags */ + int flags; + /* OUT parameters */ + /* exported dma buf fd */ + int fd; +}; + +#define IOCTL_HYPER_DMABUF_UNEXPORT \ +_IOC(_IOC_NONE, 'G', 4, sizeof(struct ioctl_hyper_dmabuf_unexport)) +struct ioctl_hyper_dmabuf_unexport { + /* IN parameters */ + /* hyper dmabuf id to be unexported */ + hyper_dmabuf_id_t hid; + /* delay in ms by which unexport processing will be postponed */ + int delay_ms; + /* OUT parameters */ + /* Status of request */ + int status; +}; + +#define IOCTL_HYPER_DMABUF_QUERY \ +_IOC(_IOC_NONE, 'G', 5, sizeof(struct ioctl_hyper_dmabuf_query)) +struct ioctl_hyper_dmabuf_query { + /* in parameters */ + /* hyper dmabuf id to be queried */ + hyper_dmabuf_id_t hid; + /* item to be queried */ + int item; + /* OUT parameters */ + /* Value of queried item */ + unsigned long info; +}; + +/* DMABUF query */ + +enum hyper_dmabuf_query { + HYPER_DMABUF_QUERY_TYPE = 0x10, + HYPER_DMABUF_QUERY_EXPORTER, + HYPER_DMABUF_QUERY_IMPORTER, + HYPER_DMABUF_QUERY_SIZE, + HYPER_DMABUF_QUERY_BUSY, + HYPER_DMABUF_QUERY_UNEXPORTED, + HYPER_DMABUF_QUERY_DELAYED_UNEXPORTED, + HYPER_DMABUF_QUERY_PRIV_INFO_SIZE, + HYPER_DMABUF_QUERY_PRIV_INFO, +}; + +enum hyper_dmabuf_status { + EXPORTED = 0x01, + IMPORTED, +}; + +#endif //__LINUX_PUBLIC_HYPER_DMABUF_H__ diff --git a/include/uapi/xen/Kbuild b/include/uapi/xen/Kbuild new file mode 100644 index 000000000000..5c459628e8c7 --- /dev/null +++ b/include/uapi/xen/Kbuild @@ -0,0 +1,5 @@ +# UAPI Header export list +header-y += evtchn.h +header-y += gntalloc.h +header-y += gntdev.h +header-y += privcmd.h From a1042d79b58f4ba9a299d70b1ae94bd6a4623add Mon Sep 17 00:00:00 2001 From: Vivek Kasireddy Date: Thu, 22 Mar 2018 15:11:05 -0700 Subject: [PATCH 0944/1103] hyper_dmabuf: Enable hyper_dmabuf only on x86 or x86_64 The hyper_dmabuf driver is designed and tested only on x86/x86_64 architecture based systems. Therefore, disable it when trying to build for other architectures. Signed-off-by: Vivek Kasireddy Reviewed-by: Mateusz Polrola --- drivers/dma-buf/hyper_dmabuf/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/dma-buf/hyper_dmabuf/Kconfig b/drivers/dma-buf/hyper_dmabuf/Kconfig index 88992167c645..999900b97625 100644 --- a/drivers/dma-buf/hyper_dmabuf/Kconfig +++ b/drivers/dma-buf/hyper_dmabuf/Kconfig @@ -3,6 +3,7 @@ menu "hyper_dmabuf options" config HYPER_DMABUF bool "Enables hyper dmabuf driver" default y + depends on (X86=y || X86_64=y || 64BIT=y) choice prompt "Hypervisor" From 4a73147983cf63428c721faf0b25f54e9e40ca9e Mon Sep 17 00:00:00 2001 From: Wei Liu Date: Thu, 26 Jul 2018 11:24:55 +0800 Subject: [PATCH 0945/1103] hyper_dmabuf: Fix array length check issue in hyper_dmabuf_ioctl() Current boundry check for hyper_dmabuf_ioctls array only verifies whether index value is not greater than total number of elements. But the correct check should be to verify whether index is always less than number of array elements. Change-Id: I711979c270545e02fb878da0eec39b71b451574a Signed-off-by: Kuppuswamy Sathyanarayanan Reviewed-by: Zhao Yakui Reviewed-by: Wei Liu --- drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_ioctl.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_ioctl.c b/drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_ioctl.c index 20274e1b9e20..e9f1d64ee60b 100644 --- a/drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_ioctl.c +++ b/drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_ioctl.c @@ -746,7 +746,7 @@ long hyper_dmabuf_ioctl(struct file *filp, hyper_dmabuf_ioctl_t func; char *kdata; - if (nr > ARRAY_SIZE(hyper_dmabuf_ioctls)) { + if (nr >= ARRAY_SIZE(hyper_dmabuf_ioctls)) { dev_err(hy_drv_priv->dev, "invalid ioctl\n"); return -EINVAL; } From f707cb038f0aba564ed23a62fc36111581caf120 Mon Sep 17 00:00:00 2001 From: Wei Liu Date: Thu, 19 Jul 2018 15:04:40 +0800 Subject: [PATCH 0946/1103] kernel/hyper_dmabuf: disable hyper_dmabuf on arch arm64 hyper_dmabuf should not be enabled while make allyesconfig on arch arm64, this patch will disable the option and fix the compile error Signed-off-by: Wei Liu --- drivers/dma-buf/hyper_dmabuf/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/dma-buf/hyper_dmabuf/Kconfig b/drivers/dma-buf/hyper_dmabuf/Kconfig index 999900b97625..1d91a114ba61 100644 --- a/drivers/dma-buf/hyper_dmabuf/Kconfig +++ b/drivers/dma-buf/hyper_dmabuf/Kconfig @@ -3,7 +3,7 @@ menu "hyper_dmabuf options" config HYPER_DMABUF bool "Enables hyper dmabuf driver" default y - depends on (X86=y || X86_64=y || 64BIT=y) + depends on (X86=y || X86_64=y) choice prompt "Hypervisor" From 6ed782f1325caefbe8e39d4f1ab5494eeefb9685 Mon Sep 17 00:00:00 2001 From: Kuppuswamy Sathyanarayanan Date: Tue, 31 Jul 2018 14:39:37 -0700 Subject: [PATCH 0947/1103] hyper_dmabuf: Remove void* cast in cpu_access function pointers In dma_buf_ops structure init, hyper_dmabuf_ops_begin_cpu_access() and hyper_dmabuf_ops_end_cpu_access() functions are of same type as begin_cpu_access() and end_cpu_access() function declartions. So there is no need for casting them with void*. Signed-off-by: Kuppuswamy Sathyanarayanan Reviewed-by: Zhao Yakui --- drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_ops.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_ops.c b/drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_ops.c index 915743741897..e2bdab75a7cf 100644 --- a/drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_ops.c +++ b/drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_ops.c @@ -372,8 +372,8 @@ static const struct dma_buf_ops hyper_dmabuf_ops = { .map_dma_buf = hyper_dmabuf_ops_map, .unmap_dma_buf = hyper_dmabuf_ops_unmap, .release = hyper_dmabuf_ops_release, - .begin_cpu_access = (void *)hyper_dmabuf_ops_begin_cpu_access, - .end_cpu_access = (void *)hyper_dmabuf_ops_end_cpu_access, + .begin_cpu_access = hyper_dmabuf_ops_begin_cpu_access, + .end_cpu_access = hyper_dmabuf_ops_end_cpu_access, .map_atomic = hyper_dmabuf_ops_kmap_atomic, .unmap_atomic = hyper_dmabuf_ops_kunmap_atomic, .map = hyper_dmabuf_ops_kmap, From cda77a031983ab2cbe0028985826bf944beebc15 Mon Sep 17 00:00:00 2001 From: Kuppuswamy Sathyanarayanan Date: Tue, 31 Jul 2018 14:39:38 -0700 Subject: [PATCH 0948/1103] hyper_dmabuf: Fix incorrect return in hyper_dmabuf_ops_end_cpu_access() In hyper_dmabuf_ops_end_cpu_access(), currently we don't check for return value of sync_request() function and always return zero. This is incorrect. This patch fixes this issue. Signed-off-by: Kuppuswamy Sathyanarayanan Reviewed-by: Zhao Yakui --- drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_ops.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_ops.c b/drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_ops.c index e2bdab75a7cf..a8207b4be20f 100644 --- a/drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_ops.c +++ b/drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_ops.c @@ -249,16 +249,13 @@ static int hyper_dmabuf_ops_end_cpu_access(struct dma_buf *dmabuf, enum dma_data_direction dir) { struct imported_sgt_info *imported; - int ret; if (!dmabuf->priv) return -EINVAL; imported = (struct imported_sgt_info *)dmabuf->priv; - ret = sync_request(imported->hid, HYPER_DMABUF_OPS_END_CPU_ACCESS); - - return 0; + return sync_request(imported->hid, HYPER_DMABUF_OPS_END_CPU_ACCESS); } static void *hyper_dmabuf_ops_kmap_atomic(struct dma_buf *dmabuf, From 6757b37fdc3db6df2e27597493c9edffdfa29c8a Mon Sep 17 00:00:00 2001 From: Kuppuswamy Sathyanarayanan Date: Tue, 31 Jul 2018 14:39:39 -0700 Subject: [PATCH 0949/1103] hyper_dmabuf: Check for NULL value before access work pointer. In delayed_unexport() check for work pointer NULL value before accessing it. Signed-off-by: Kuppuswamy Sathyanarayanan Reviewed-by: Zhao Yakui --- drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_ioctl.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_ioctl.c b/drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_ioctl.c index e9f1d64ee60b..66cdcf6eff78 100644 --- a/drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_ioctl.c +++ b/drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_ioctl.c @@ -574,14 +574,15 @@ static void delayed_unexport(struct work_struct *work) { struct hyper_dmabuf_req *req; struct hyper_dmabuf_bknd_ops *bknd_ops = hy_drv_priv->bknd_ops; - struct exported_sgt_info *exported = - container_of(work, struct exported_sgt_info, unexport.work); + struct exported_sgt_info *exported; int op[4]; int i, ret; - if (!exported) + if (!work) return; + exported = container_of(work, struct exported_sgt_info, unexport.work); + dev_dbg(hy_drv_priv->dev, "Marking buffer {id:%d key:%d %d %d} as invalid\n", exported->hid.id, exported->hid.rng_key[0], From 94109f7e575fd56228b3400f1c64fef483516d93 Mon Sep 17 00:00:00 2001 From: Kuppuswamy Sathyanarayanan Date: Tue, 31 Jul 2018 14:39:40 -0700 Subject: [PATCH 0950/1103] hyper_dmabuf: Remove unused variable warnings Remove unused variable warnings in hyper_dmabuf_list.c, hyper_dmabuf_msg.c and hyper_dmabuf_ops.c Signed-off-by: Kuppuswamy Sathyanarayanan Reviewed-by: Zhao Yakui --- .../dma-buf/hyper_dmabuf/hyper_dmabuf_list.c | 1 - .../dma-buf/hyper_dmabuf/hyper_dmabuf_msg.c | 2 -- .../dma-buf/hyper_dmabuf/hyper_dmabuf_ops.c | 27 +++++++------------ 3 files changed, 9 insertions(+), 21 deletions(-) diff --git a/drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_list.c b/drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_list.c index bba6d1d607a8..84cfb065bddd 100644 --- a/drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_list.c +++ b/drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_list.c @@ -34,7 +34,6 @@ #include "hyper_dmabuf_drv.h" #include "hyper_dmabuf_list.h" #include "hyper_dmabuf_id.h" -#include "hyper_dmabuf_event.h" DECLARE_HASHTABLE(hyper_dmabuf_hash_imported, MAX_ENTRY_IMPORTED); DECLARE_HASHTABLE(hyper_dmabuf_hash_exported, MAX_ENTRY_EXPORTED); diff --git a/drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_msg.c b/drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_msg.c index 37ee894ec418..c5d99d2f12c9 100644 --- a/drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_msg.c +++ b/drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_msg.c @@ -124,11 +124,9 @@ static void cmd_process_work(struct work_struct *work) struct cmd_process, work); struct hyper_dmabuf_req *req; hyper_dmabuf_id_t hid; - int domid; int i; req = proc->rq; - domid = proc->domid; switch (req->cmd) { case HYPER_DMABUF_EXPORT: diff --git a/drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_ops.c b/drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_ops.c index a8207b4be20f..10b5510b3816 100644 --- a/drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_ops.c +++ b/drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_ops.c @@ -106,14 +106,13 @@ static void hyper_dmabuf_ops_detach(struct dma_buf *dmabuf, struct dma_buf_attachment *attach) { struct imported_sgt_info *imported; - int ret; if (!attach->dmabuf->priv) return; imported = (struct imported_sgt_info *)attach->dmabuf->priv; - ret = sync_request(imported->hid, HYPER_DMABUF_OPS_DETACH); + sync_request(imported->hid, HYPER_DMABUF_OPS_DETACH); } static struct sg_table *hyper_dmabuf_ops_map( @@ -169,7 +168,6 @@ static void hyper_dmabuf_ops_unmap(struct dma_buf_attachment *attachment, enum dma_data_direction dir) { struct imported_sgt_info *imported; - int ret; if (!attachment->dmabuf->priv) return; @@ -181,14 +179,13 @@ static void hyper_dmabuf_ops_unmap(struct dma_buf_attachment *attachment, sg_free_table(sg); kfree(sg); - ret = sync_request(imported->hid, HYPER_DMABUF_OPS_UNMAP); + sync_request(imported->hid, HYPER_DMABUF_OPS_UNMAP); } static void hyper_dmabuf_ops_release(struct dma_buf *dma_buf) { struct imported_sgt_info *imported; struct hyper_dmabuf_bknd_ops *bknd_ops = hy_drv_priv->bknd_ops; - int ret; int finish; if (!dma_buf->priv) @@ -215,7 +212,7 @@ static void hyper_dmabuf_ops_release(struct dma_buf *dma_buf) finish = imported && !imported->valid && !imported->importers; - ret = sync_request(imported->hid, HYPER_DMABUF_OPS_RELEASE); + sync_request(imported->hid, HYPER_DMABUF_OPS_RELEASE); /* * Check if buffer is still valid and if not remove it @@ -262,14 +259,13 @@ static void *hyper_dmabuf_ops_kmap_atomic(struct dma_buf *dmabuf, unsigned long pgnum) { struct imported_sgt_info *imported; - int ret; if (!dmabuf->priv) return NULL; imported = (struct imported_sgt_info *)dmabuf->priv; - ret = sync_request(imported->hid, HYPER_DMABUF_OPS_KMAP_ATOMIC); + sync_request(imported->hid, HYPER_DMABUF_OPS_KMAP_ATOMIC); /* TODO: NULL for now. Need to return the addr of mapped region */ return NULL; @@ -279,27 +275,25 @@ static void hyper_dmabuf_ops_kunmap_atomic(struct dma_buf *dmabuf, unsigned long pgnum, void *vaddr) { struct imported_sgt_info *imported; - int ret; if (!dmabuf->priv) return; imported = (struct imported_sgt_info *)dmabuf->priv; - ret = sync_request(imported->hid, HYPER_DMABUF_OPS_KUNMAP_ATOMIC); + sync_request(imported->hid, HYPER_DMABUF_OPS_KUNMAP_ATOMIC); } static void *hyper_dmabuf_ops_kmap(struct dma_buf *dmabuf, unsigned long pgnum) { struct imported_sgt_info *imported; - int ret; if (!dmabuf->priv) return NULL; imported = (struct imported_sgt_info *)dmabuf->priv; - ret = sync_request(imported->hid, HYPER_DMABUF_OPS_KMAP); + sync_request(imported->hid, HYPER_DMABUF_OPS_KMAP); /* for now NULL.. need to return the address of mapped region */ return NULL; @@ -309,14 +303,13 @@ static void hyper_dmabuf_ops_kunmap(struct dma_buf *dmabuf, unsigned long pgnum, void *vaddr) { struct imported_sgt_info *imported; - int ret; if (!dmabuf->priv) return; imported = (struct imported_sgt_info *)dmabuf->priv; - ret = sync_request(imported->hid, HYPER_DMABUF_OPS_KUNMAP); + sync_request(imported->hid, HYPER_DMABUF_OPS_KUNMAP); } static int hyper_dmabuf_ops_mmap(struct dma_buf *dmabuf, @@ -338,14 +331,13 @@ static int hyper_dmabuf_ops_mmap(struct dma_buf *dmabuf, static void *hyper_dmabuf_ops_vmap(struct dma_buf *dmabuf) { struct imported_sgt_info *imported; - int ret; if (!dmabuf->priv) return NULL; imported = (struct imported_sgt_info *)dmabuf->priv; - ret = sync_request(imported->hid, HYPER_DMABUF_OPS_VMAP); + sync_request(imported->hid, HYPER_DMABUF_OPS_VMAP); return NULL; } @@ -353,14 +345,13 @@ static void *hyper_dmabuf_ops_vmap(struct dma_buf *dmabuf) static void hyper_dmabuf_ops_vunmap(struct dma_buf *dmabuf, void *vaddr) { struct imported_sgt_info *imported; - int ret; if (!dmabuf->priv) return; imported = (struct imported_sgt_info *)dmabuf->priv; - ret = sync_request(imported->hid, HYPER_DMABUF_OPS_VUNMAP); + sync_request(imported->hid, HYPER_DMABUF_OPS_VUNMAP); } static const struct dma_buf_ops hyper_dmabuf_ops = { From 7fb64e726ee0275fbc7502ff0819f67fee5ed73f Mon Sep 17 00:00:00 2001 From: Mateusz Polrola Date: Wed, 22 Aug 2018 14:27:16 +0200 Subject: [PATCH 0951/1103] hyper_dmabuf/virtio: Protect virtqueue operations with spinlock. virtqueue_add_*/virtqueue_get_buf are not safe to run in parallel as they operate on the same structs, so code that uses them must ensure that they won't run in parallel, as that can lead to corruption of virtqueue data. In case of hyper dmabuf TX queue operations can be run from different threads and need to be protected with spinlock, RX queue operations are always done from one thread and do not require lock. This fixes: RTC252213 Signed-off-by: Mateusz Polrola --- .../virtio/hyper_dmabuf_virtio_fe_drv.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/drivers/dma-buf/hyper_dmabuf/virtio/hyper_dmabuf_virtio_fe_drv.c b/drivers/dma-buf/hyper_dmabuf/virtio/hyper_dmabuf_virtio_fe_drv.c index 9ae290435d70..5ef8801eb085 100644 --- a/drivers/dma-buf/hyper_dmabuf/virtio/hyper_dmabuf_virtio_fe_drv.c +++ b/drivers/dma-buf/hyper_dmabuf/virtio/hyper_dmabuf_virtio_fe_drv.c @@ -53,6 +53,11 @@ struct virtio_hdma_fe_priv { struct virtio_comm_ring tx_ring; struct virtio_comm_ring rx_ring; int vmid; + /* + * Lock to protect operations on virtqueue + * which are not safe to run concurrently + */ + spinlock_t lock; }; /* Assuming there will be one FE instance per VM */ @@ -68,6 +73,7 @@ static void virtio_hdma_fe_tx_done(struct virtqueue *vq) struct virtio_hdma_fe_priv *priv = (struct virtio_hdma_fe_priv *) vq->vdev->priv; int len; + unsigned long flags; if (priv == NULL) { dev_dbg(hy_drv_priv->dev, @@ -75,6 +81,7 @@ static void virtio_hdma_fe_tx_done(struct virtqueue *vq) return; } + spin_lock_irqsave(&priv->lock, flags); /* Make sure that all pending responses are processed */ while (virtqueue_get_buf(vq, &len)) { if (len == sizeof(struct hyper_dmabuf_req)) { @@ -83,6 +90,7 @@ static void virtio_hdma_fe_tx_done(struct virtqueue *vq) virtio_comm_ring_pop(&priv->tx_ring); } } + spin_unlock_irqrestore(&priv->lock, flags); } /* @@ -165,6 +173,8 @@ static int virtio_hdma_fe_probe_common(struct virtio_device *vdev) /* Set vmid to -1 to mark that it is not initialized yet */ priv->vmid = -1; + spin_lock_init(&priv->lock); + vdev->priv = priv; ret = virtio_find_vqs(vdev, HDMA_VIRTIO_QUEUE_MAX, @@ -317,6 +327,7 @@ static int virtio_hdma_fe_send_req(int vmid, struct hyper_dmabuf_req *req, struct virtio_hdma_fe_priv *priv = hyper_dmabuf_virtio_fe; struct hyper_dmabuf_req *tx_req; int timeout = 1000; + unsigned long flags; if (priv == NULL) { dev_err(hy_drv_priv->dev, @@ -337,6 +348,7 @@ static int virtio_hdma_fe_send_req(int vmid, struct hyper_dmabuf_req *req, return -EBUSY; } + spin_lock_irqsave(&priv->lock, flags); /* Get free buffer for sending request from ring */ tx_req = (struct hyper_dmabuf_req *) virtio_comm_ring_push(&priv->tx_ring); @@ -348,6 +360,7 @@ static int virtio_hdma_fe_send_req(int vmid, struct hyper_dmabuf_req *req, virtio_hdma_fe_queue_buffer(hyper_dmabuf_virtio_fe, HDMA_VIRTIO_TX_QUEUE, tx_req, sizeof(*tx_req)); + spin_unlock_irqrestore(&priv->lock, flags); if (wait) { while (timeout--) { From 7284a25a2b858771d1b29333b310056ec6bd1ef0 Mon Sep 17 00:00:00 2001 From: Mateusz Polrola Date: Tue, 21 Aug 2018 11:19:52 +0200 Subject: [PATCH 0952/1103] hyper_dmabuf/virtio: Correctly cleanup front end connections Virtio frontends were not fully cleanup on release of VBS-K handle, this change adds helper function that allows to find virtio frontend that was using particular VBS-K handle and clean it up. Signed-off-by: Mateusz Polrola --- .../virtio/hyper_dmabuf_virtio_be_drv.c | 22 +++++++++++++++++++ .../virtio/hyper_dmabuf_virtio_fe_list.c | 14 ++++++++++++ .../virtio/hyper_dmabuf_virtio_fe_list.h | 5 ++++- 3 files changed, 40 insertions(+), 1 deletion(-) diff --git a/drivers/dma-buf/hyper_dmabuf/virtio/hyper_dmabuf_virtio_be_drv.c b/drivers/dma-buf/hyper_dmabuf/virtio/hyper_dmabuf_virtio_be_drv.c index bb16360d06d5..a89d557c7c4c 100644 --- a/drivers/dma-buf/hyper_dmabuf/virtio/hyper_dmabuf_virtio_be_drv.c +++ b/drivers/dma-buf/hyper_dmabuf/virtio/hyper_dmabuf_virtio_be_drv.c @@ -319,6 +319,21 @@ static int vbs_k_open(struct inode *inode, struct file *f) return 0; } +static void cleanup_fe(struct virtio_fe_info *fe_info, void *attr) +{ + struct virtio_be_priv *priv = attr; + if (fe_info->priv == priv) { + acrn_ioreq_del_iorange(fe_info->client_id, + priv->dev.io_range_type ? REQ_MMIO : REQ_PORTIO, + priv->dev.io_range_start, + priv->dev.io_range_start + priv->dev.io_range_len); + + acrn_ioreq_destroy_client(fe_info->client_id); + virtio_fe_remove(fe_info->client_id); + kfree(fe_info); + } +} + static int vbs_k_release(struct inode *inode, struct file *f) { struct virtio_be_priv *priv = @@ -333,6 +348,13 @@ static int vbs_k_release(struct inode *inode, struct file *f) kfree(priv->pending_tx_req); virtio_comm_ring_free(&priv->tx_ring); + + /* + * Find and cleanup virtio frontend that + * has been using released vbs k file + */ + virtio_fe_foreach(cleanup_fe, priv); + kfree(priv); return 0; } diff --git a/drivers/dma-buf/hyper_dmabuf/virtio/hyper_dmabuf_virtio_fe_list.c b/drivers/dma-buf/hyper_dmabuf/virtio/hyper_dmabuf_virtio_fe_list.c index 79b30e286b5e..84b6ed5e96c1 100644 --- a/drivers/dma-buf/hyper_dmabuf/virtio/hyper_dmabuf_virtio_fe_list.c +++ b/drivers/dma-buf/hyper_dmabuf/virtio/hyper_dmabuf_virtio_fe_list.c @@ -97,3 +97,17 @@ int virtio_fe_remove(int client_id) return -ENOENT; } + +void virtio_fe_foreach( + void (*func)(struct virtio_fe_info *, void *attr), + void *attr) +{ + struct virtio_fe_info_entry *info_entry; + struct hlist_node *tmp; + int bkt; + + hash_for_each_safe(virtio_fe_hash, bkt, tmp, + info_entry, node) { + func(info_entry->info, attr); + } +} diff --git a/drivers/dma-buf/hyper_dmabuf/virtio/hyper_dmabuf_virtio_fe_list.h b/drivers/dma-buf/hyper_dmabuf/virtio/hyper_dmabuf_virtio_fe_list.h index bc7ef843161c..c353c1e5baa1 100644 --- a/drivers/dma-buf/hyper_dmabuf/virtio/hyper_dmabuf_virtio_fe_list.h +++ b/drivers/dma-buf/hyper_dmabuf/virtio/hyper_dmabuf_virtio_fe_list.h @@ -39,10 +39,13 @@ void virtio_fe_table_init(void); int virtio_fe_add(struct virtio_fe_info *fe_info); -int virtio_remove_fe(int client_id); +int virtio_fe_remove(int client_id); struct virtio_fe_info *virtio_fe_find(int client_id); struct virtio_fe_info *virtio_fe_find_by_vmid(int vmid); +void virtio_fe_foreach(void (*func)(struct virtio_fe_info *, + void *attr), void *attr); + #endif /* __HYPER_DMABUF_VIRTIO_FE_LIST_H__*/ From 97832512041f3ff0526cf61b883ba00769fcdf0c Mon Sep 17 00:00:00 2001 From: Mateusz Polrola Date: Tue, 21 Aug 2018 11:19:53 +0200 Subject: [PATCH 0953/1103] hyper_dmabuf/virtio: bugfix on acrn_ioreq_add_iorange() usage Align usage of acrn_ioreq_add_iorange according to description of change: "VBS-K: bugfix on cwp_ioreq_add_iorange() usage": "However, previous VBS-K rng reference driver mistakenly uses "start" and "start + len". This leads to the fact that VBS-K not only hooked "kick" register, VIRTIO_PCI_QUEUE_NOTIFY, but also "status" register, VIRTIO_PCI_STATUS, mistakenly." Signed-off-by: Mateusz Polrola --- .../hyper_dmabuf/virtio/hyper_dmabuf_virtio_be_drv.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/dma-buf/hyper_dmabuf/virtio/hyper_dmabuf_virtio_be_drv.c b/drivers/dma-buf/hyper_dmabuf/virtio/hyper_dmabuf_virtio_be_drv.c index a89d557c7c4c..c84b2dd746a3 100644 --- a/drivers/dma-buf/hyper_dmabuf/virtio/hyper_dmabuf_virtio_be_drv.c +++ b/drivers/dma-buf/hyper_dmabuf/virtio/hyper_dmabuf_virtio_be_drv.c @@ -234,7 +234,7 @@ static int virtio_be_register_vhm_client(struct virtio_dev_info *d) ret = acrn_ioreq_add_iorange(fe_info->client_id, d->io_range_type ? REQ_MMIO : REQ_PORTIO, d->io_range_start, - d->io_range_start + d->io_range_len); + d->io_range_start + d->io_range_len - 1); if (ret < 0) { dev_err(hy_drv_priv->dev, @@ -247,7 +247,7 @@ static int virtio_be_register_vhm_client(struct virtio_dev_info *d) acrn_ioreq_del_iorange(fe_info->client_id, d->io_range_type ? REQ_MMIO : REQ_PORTIO, d->io_range_start, - d->io_range_start + d->io_range_len); + d->io_range_start + d->io_range_len - 1); dev_err(hy_drv_priv->dev, "Failed in vhm_get_vm_info\n"); goto err; @@ -260,7 +260,7 @@ static int virtio_be_register_vhm_client(struct virtio_dev_info *d) acrn_ioreq_del_iorange(fe_info->client_id, d->io_range_type ? REQ_MMIO : REQ_PORTIO, d->io_range_start, - d->io_range_start + d->io_range_len); + d->io_range_start + d->io_range_len - 1); dev_err(hy_drv_priv->dev, "Failed in acrn_ioreq_get_reqbuf\n"); goto err; @@ -326,7 +326,7 @@ static void cleanup_fe(struct virtio_fe_info *fe_info, void *attr) acrn_ioreq_del_iorange(fe_info->client_id, priv->dev.io_range_type ? REQ_MMIO : REQ_PORTIO, priv->dev.io_range_start, - priv->dev.io_range_start + priv->dev.io_range_len); + priv->dev.io_range_start + priv->dev.io_range_len - 1); acrn_ioreq_destroy_client(fe_info->client_id); virtio_fe_remove(fe_info->client_id); From 61f73ceaaf91b8ffc95f6f42f669690381656525 Mon Sep 17 00:00:00 2001 From: Mateusz Polrola Date: Fri, 24 Aug 2018 09:25:02 +0200 Subject: [PATCH 0954/1103] hyper_dmabuf/virtio: Add support for VBS_RESET_DEV ioctl (v2) During VBS_RESET_DEV virtio frontend client will be cleaned up and VBS-K device will be restarted. v2 changes: - call virtio_dev_reset from vbs_k_release Signed-off-by: Mateusz Polrola --- .../virtio/hyper_dmabuf_virtio_be_drv.c | 52 ++++++++++++------- 1 file changed, 33 insertions(+), 19 deletions(-) diff --git a/drivers/dma-buf/hyper_dmabuf/virtio/hyper_dmabuf_virtio_be_drv.c b/drivers/dma-buf/hyper_dmabuf/virtio/hyper_dmabuf_virtio_be_drv.c index c84b2dd746a3..51dd8ed8271a 100644 --- a/drivers/dma-buf/hyper_dmabuf/virtio/hyper_dmabuf_virtio_be_drv.c +++ b/drivers/dma-buf/hyper_dmabuf/virtio/hyper_dmabuf_virtio_be_drv.c @@ -338,13 +338,6 @@ static int vbs_k_release(struct inode *inode, struct file *f) { struct virtio_be_priv *priv = (struct virtio_be_priv *) f->private_data; - int i; - -// virtio_dev_stop(&priv->dev); -// virtio_dev_cleanup(&priv->dev, false); - - for (i = 0; i < HDMA_VIRTIO_QUEUE_MAX; i++) - virtio_vq_reset(&priv->vqs[i]); kfree(priv->pending_tx_req); virtio_comm_ring_free(&priv->tx_ring); @@ -355,10 +348,25 @@ static int vbs_k_release(struct inode *inode, struct file *f) */ virtio_fe_foreach(cleanup_fe, priv); + virtio_dev_reset(&priv->dev); + kfree(priv); return 0; } +static int vbs_k_reset(struct virtio_be_priv *priv) +{ + virtio_comm_ring_free(&priv->tx_ring); + + virtio_fe_foreach(cleanup_fe, priv); + + virtio_dev_reset(&priv->dev); + + virtio_comm_ring_init(&priv->tx_ring, + sizeof(struct virtio_be_tx_data), + REQ_RING_SIZE); +} + static long vbs_k_ioctl(struct file *f, unsigned int ioctl, unsigned long arg) { @@ -374,19 +382,25 @@ static long vbs_k_ioctl(struct file *f, unsigned int ioctl, return -EINVAL; } - if (ioctl == VBS_SET_VQ) { - /* Overridden to call additionally - * virtio_be_register_vhm_client */ - r = virtio_vqs_ioctl(&priv->dev, ioctl, argp); - if (r == -ENOIOCTLCMD) - return -EFAULT; - - if (virtio_be_register_vhm_client(&priv->dev) < 0) - return -EFAULT; - } else { - r = virtio_dev_ioctl(&priv->dev, ioctl, argp); - if (r == -ENOIOCTLCMD) + switch(ioctl) { + case VBS_SET_VQ: + /* Overridden to call additionally + * virtio_be_register_vhm_client */ r = virtio_vqs_ioctl(&priv->dev, ioctl, argp); + if (r == -ENOIOCTLCMD) + return -EFAULT; + + if (virtio_be_register_vhm_client(&priv->dev) < 0) + return -EFAULT; + break; + case VBS_RESET_DEV: + vbs_k_reset(priv); + break; + default: + r = virtio_dev_ioctl(&priv->dev, ioctl, argp); + if (r == -ENOIOCTLCMD) + r = virtio_vqs_ioctl(&priv->dev, ioctl, argp); + break; } return r; From 4c7ea0762ea0feb29f4f59243964dceb780eb977 Mon Sep 17 00:00:00 2001 From: Mateusz Polrola Date: Fri, 24 Aug 2018 09:25:03 +0200 Subject: [PATCH 0955/1103] hyper_dmabuf/virtio: Handle S3 resume correctly (v2) After resume from S3 virtqueues are reset, so buffers for communication from SOS to UOS needs to be reinitialized. That cannot be done directly in resume routine, as that point virtio PCI device is not yet fully restored, because of that it has to be scheduled to run after some short delay. v2: changes: - schedule reinit of communication from SOS to UOS immediately at the end of restore routine - Wait for virtio device to become ready before actually doing reinit of communication Signed-off-by: Mateusz Polrola --- .../virtio/hyper_dmabuf_virtio_fe_drv.c | 49 +++++++++++++++++-- 1 file changed, 45 insertions(+), 4 deletions(-) diff --git a/drivers/dma-buf/hyper_dmabuf/virtio/hyper_dmabuf_virtio_fe_drv.c b/drivers/dma-buf/hyper_dmabuf/virtio/hyper_dmabuf_virtio_fe_drv.c index 5ef8801eb085..e0c811135699 100644 --- a/drivers/dma-buf/hyper_dmabuf/virtio/hyper_dmabuf_virtio_fe_drv.c +++ b/drivers/dma-buf/hyper_dmabuf/virtio/hyper_dmabuf_virtio_fe_drv.c @@ -32,6 +32,7 @@ #include #include #include +#include #include "../hyper_dmabuf_msg.h" #include "../hyper_dmabuf_drv.h" #include "hyper_dmabuf_virtio_common.h" @@ -222,14 +223,20 @@ static void virtio_hdma_fe_remove(struct virtio_device *vdev) virtio_hdma_fe_remove_common(vdev); } +struct virtio_hdma_restore_work +{ + struct work_struct work; + struct virtio_device *dev; +}; + /* * Queues empty requests buffers to backend, * which will be used by it to send requests back to frontend. */ -static void virtio_hdma_fe_scan(struct virtio_device *vdev) +static void virtio_hdma_query_vmid(struct virtio_device *vdev) { - struct virtio_hdma_fe_priv *priv = - (struct virtio_hdma_fe_priv *) vdev->priv; + struct virtio_hdma_fe_priv *priv = + (struct virtio_hdma_fe_priv *) vdev->priv; struct hyper_dmabuf_req *rx_req; int timeout = 1000; @@ -266,6 +273,29 @@ static void virtio_hdma_fe_scan(struct virtio_device *vdev) } } +/* + * Queues empty requests buffers to backend, + * which will be used by it to send requests back to frontend. + */ +static void virtio_hdma_fe_scan(struct virtio_device *vdev) +{ + virtio_hdma_query_vmid(vdev); +} + +static void virtio_hdma_restore_bh(struct work_struct *w) +{ + struct virtio_hdma_restore_work *work = + (struct virtio_hdma_restore_work *) w; + + while (!(VIRTIO_CONFIG_S_DRIVER_OK & + work->dev->config->get_status(work->dev))) { + usleep_range(100, 120); + } + + virtio_hdma_query_vmid(work->dev); + kfree(w); +} + #ifdef CONFIG_PM_SLEEP static int virtio_hdma_fe_freeze(struct virtio_device *vdev) { @@ -275,7 +305,18 @@ static int virtio_hdma_fe_freeze(struct virtio_device *vdev) static int virtio_hdma_fe_restore(struct virtio_device *vdev) { - return virtio_hdma_fe_probe_common(vdev); + struct virtio_hdma_restore_work *work; + int ret; + + ret = virtio_hdma_fe_probe_common(vdev); + if (!ret) { + work = kmalloc(sizeof(*work), GFP_KERNEL); + INIT_WORK(&work->work, virtio_hdma_restore_bh); + work->dev = vdev; + schedule_work(&work->work); + } + + return ret; } #endif From 77445b4fcddeff500a3edff565df31cb281786fb Mon Sep 17 00:00:00 2001 From: Fei Jiang Date: Mon, 27 Aug 2018 09:34:15 +0200 Subject: [PATCH 0956/1103] hyper_dmabuf: fix map failure issue when assign 4G memory to UOS (v2) When assign 4G memory to UOS, 32bit GPA ref_handle will overflow, instead we need set ref_handle as unsigned long to fix this issue. op is int type, then we need use two int op[7] and op[8] to pass ref_handle. v2 changes: - Aligned Xen backend with backend interface changes Change-Id: Ibc827e54897b0b48a4056d8df400d0bf4b3e923f Signed-off-by: Fei Jiang Signed-off-by: Mateusz Polrola --- .../dma-buf/hyper_dmabuf/hyper_dmabuf_drv.h | 2 +- .../dma-buf/hyper_dmabuf/hyper_dmabuf_ioctl.c | 15 ++++---- .../dma-buf/hyper_dmabuf/hyper_dmabuf_msg.c | 35 ++++++++++--------- .../hyper_dmabuf/hyper_dmabuf_struct.h | 2 +- .../virtio/hyper_dmabuf_virtio_shm.c | 8 ++--- .../virtio/hyper_dmabuf_virtio_shm.h | 2 +- .../hyper_dmabuf/xen/hyper_dmabuf_xen_shm.c | 4 +-- .../hyper_dmabuf/xen/hyper_dmabuf_xen_shm.h | 4 +-- 8 files changed, 39 insertions(+), 33 deletions(-) diff --git a/drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_drv.h b/drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_drv.h index 45c24fd8d25d..ad4839b9c0f2 100644 --- a/drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_drv.h +++ b/drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_drv.h @@ -83,7 +83,7 @@ struct hyper_dmabuf_bknd_ops { int (*get_vm_id)(void); /* get pages shared via hypervisor-specific method */ - int (*share_pages)(struct page **, int, int, void **); + long (*share_pages)(struct page **, int, int, void **); /* make shared pages unshared via hypervisor specific method */ int (*unshare_pages)(void **, int); diff --git a/drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_ioctl.c b/drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_ioctl.c index 66cdcf6eff78..62f83cc45f36 100644 --- a/drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_ioctl.c +++ b/drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_ioctl.c @@ -86,6 +86,7 @@ static int send_export_msg(struct exported_sgt_info *exported, struct hyper_dmabuf_req *req; int op[MAX_NUMBER_OF_OPERANDS] = {0}; int ret, i; + long tmp; /* now create request for importer via ring */ op[0] = exported->hid.id; @@ -97,18 +98,20 @@ static int send_export_msg(struct exported_sgt_info *exported, op[4] = pg_info->nents; op[5] = pg_info->frst_ofst; op[6] = pg_info->last_len; - op[7] = bknd_ops->share_pages(pg_info->pgs, exported->rdomid, + tmp = bknd_ops->share_pages(pg_info->pgs, exported->rdomid, pg_info->nents, &exported->refs_info); - if (op[7] < 0) { + if (tmp < 0) { dev_err(hy_drv_priv->dev, "pages sharing failed\n"); - return op[7]; + return tmp; } + op[7] = tmp & 0xffffffff; + op[8] = (tmp >> 32) & 0xffffffff; } - op[8] = exported->sz_priv; + op[9] = exported->sz_priv; /* driver/application specific private info */ - memcpy(&op[9], exported->priv, op[8]); + memcpy(&op[10], exported->priv, op[9]); req = kcalloc(1, sizeof(*req), GFP_KERNEL); @@ -501,7 +504,7 @@ static int hyper_dmabuf_export_fd_ioctl(struct file *filp, void *data) ret = 0; dev_dbg(hy_drv_priv->dev, - "Found buffer gref %d off %d\n", + "Found buffer gref 0x%lx off %d\n", imported->ref_handle, imported->frst_ofst); dev_dbg(hy_drv_priv->dev, diff --git a/drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_msg.c b/drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_msg.c index c5d99d2f12c9..d91a9eb3bed8 100644 --- a/drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_msg.c +++ b/drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_msg.c @@ -59,13 +59,14 @@ void hyper_dmabuf_create_req(struct hyper_dmabuf_req *req, * op4 : number of pages to be shared * op5 : offset of data in the first page * op6 : length of data in the last page - * op7 : top-level reference number for shared pages - * op8 : size of private data (from op9) - * op9 ~ : Driver-specific private data + * op7 : 32 LSB of top-level reference number for shared pages + * op8 : 32 MSB of top-level reference number for shared pages + * op9 : size of private data (from op9) + * op10 ~ : Driver-specific private data * (e.g. graphic buffer's meta info) */ - memcpy(&req->op[0], &op[0], 9 * sizeof(int) + op[8]); + memcpy(&req->op[0], &op[0], 10 * sizeof(int) + op[9]); break; case HYPER_DMABUF_NOTIFY_UNEXPORT: @@ -136,9 +137,10 @@ static void cmd_process_work(struct work_struct *work) * op4 : number of pages to be shared * op5 : offset of data in the first page * op6 : length of data in the last page - * op7 : top-level reference number for shared pages - * op8 : size of private data (from op9) - * op9 ~ : Driver-specific private data + * op7 : 32 LSB of top-level reference number for shared pages + * op8 : 32 MSB of top-level reference number for shared pages + * op9 : size of private data (from op9) + * op10 ~ : Driver-specific private data * (e.g. graphic buffer's meta info) */ @@ -162,10 +164,10 @@ static void cmd_process_work(struct work_struct *work) /* if size of new private data is different, * we reallocate it. */ - if (imported->sz_priv != req->op[8]) { + if (imported->sz_priv != req->op[9]) { kfree(imported->priv); - imported->sz_priv = req->op[8]; - imported->priv = kcalloc(1, req->op[8], + imported->sz_priv = req->op[9]; + imported->priv = kcalloc(1, req->op[9], GFP_KERNEL); if (!imported->priv) { /* set it invalid */ @@ -175,7 +177,7 @@ static void cmd_process_work(struct work_struct *work) } /* updating priv data */ - memcpy(imported->priv, &req->op[9], req->op[8]); + memcpy(imported->priv, &req->op[10], req->op[9]); #ifdef CONFIG_HYPER_DMABUF_EVENT_GEN /* generating import event */ @@ -190,8 +192,8 @@ static void cmd_process_work(struct work_struct *work) if (!imported) break; - imported->sz_priv = req->op[8]; - imported->priv = kcalloc(1, req->op[8], GFP_KERNEL); + imported->sz_priv = req->op[9]; + imported->priv = kcalloc(1, req->op[9], GFP_KERNEL); if (!imported->priv) { kfree(imported); @@ -206,7 +208,7 @@ static void cmd_process_work(struct work_struct *work) imported->nents = req->op[4]; imported->frst_ofst = req->op[5]; imported->last_len = req->op[6]; - imported->ref_handle = req->op[7]; + imported->ref_handle = (u64)req->op[8] << 32 | req->op[7]; dev_dbg(hy_drv_priv->dev, "DMABUF was exported\n"); dev_dbg(hy_drv_priv->dev, "\thid{id:%d key:%d %d %d}\n", @@ -215,9 +217,10 @@ static void cmd_process_work(struct work_struct *work) dev_dbg(hy_drv_priv->dev, "\tnents %d\n", req->op[4]); dev_dbg(hy_drv_priv->dev, "\tfirst offset %d\n", req->op[5]); dev_dbg(hy_drv_priv->dev, "\tlast len %d\n", req->op[6]); - dev_dbg(hy_drv_priv->dev, "\tgrefid %d\n", req->op[7]); + dev_dbg(hy_drv_priv->dev, "\tgrefid 0x%lx\n", + (u64)req->op[8] << 32 | req->op[7]); - memcpy(imported->priv, &req->op[9], req->op[8]); + memcpy(imported->priv, &req->op[10], req->op[9]); imported->valid = true; hyper_dmabuf_register_imported(imported); diff --git a/drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_struct.h b/drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_struct.h index a11f804edfb3..f7b7de0e1432 100644 --- a/drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_struct.h +++ b/drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_struct.h @@ -117,7 +117,7 @@ struct imported_sgt_info { hyper_dmabuf_id_t hid; /* unique id for shared dmabuf imported */ /* hypervisor-specific handle to pages */ - int ref_handle; + unsigned long ref_handle; /* offset and size info of DMA_BUF */ int frst_ofst; diff --git a/drivers/dma-buf/hyper_dmabuf/virtio/hyper_dmabuf_virtio_shm.c b/drivers/dma-buf/hyper_dmabuf/virtio/hyper_dmabuf_virtio_shm.c index be5141c25191..b18f7cae0115 100644 --- a/drivers/dma-buf/hyper_dmabuf/virtio/hyper_dmabuf_virtio_shm.c +++ b/drivers/dma-buf/hyper_dmabuf/virtio/hyper_dmabuf_virtio_shm.c @@ -56,7 +56,7 @@ struct virtio_shared_pages_info { #endif #ifdef CONFIG_HYPER_DMABUF_VIRTIO_BE -static int virtio_be_share_pages(struct page **pages, +static long virtio_be_share_pages(struct page **pages, int vmid, int nents, void **refs_info) @@ -208,7 +208,7 @@ static int virtio_be_unmap_shared_pages(void **refs_info, int nents) return 0; } #else -static int virtio_fe_share_pages(struct page **pages, +static long virtio_fe_share_pages(struct page **pages, int domid, int nents, void **refs_info) { @@ -292,11 +292,11 @@ static int virtio_fe_unmap_shared_pages(void **refs_info, int nents) #endif -int virtio_share_pages(struct page **pages, +long virtio_share_pages(struct page **pages, int domid, int nents, void **refs_info) { - int ret; + long ret; #ifdef CONFIG_HYPER_DMABUF_VIRTIO_BE ret = virtio_be_share_pages(pages, domid, nents, refs_info); #else diff --git a/drivers/dma-buf/hyper_dmabuf/virtio/hyper_dmabuf_virtio_shm.h b/drivers/dma-buf/hyper_dmabuf/virtio/hyper_dmabuf_virtio_shm.h index 05cbf5779f86..55f3e13ef2df 100644 --- a/drivers/dma-buf/hyper_dmabuf/virtio/hyper_dmabuf_virtio_shm.h +++ b/drivers/dma-buf/hyper_dmabuf/virtio/hyper_dmabuf_virtio_shm.h @@ -25,7 +25,7 @@ #ifndef __HYPER_DMABUF_VIRTIO_SHM_H__ #define __HYPER_DMABUF_VIRTIO_SHM_H__ -int virtio_share_pages(struct page **pages, +long virtio_share_pages(struct page **pages, int domid, int nents, void **refs_info); diff --git a/drivers/dma-buf/hyper_dmabuf/xen/hyper_dmabuf_xen_shm.c b/drivers/dma-buf/hyper_dmabuf/xen/hyper_dmabuf_xen_shm.c index c6a15f187fe3..5889485125e0 100644 --- a/drivers/dma-buf/hyper_dmabuf/xen/hyper_dmabuf_xen_shm.c +++ b/drivers/dma-buf/hyper_dmabuf/xen/hyper_dmabuf_xen_shm.c @@ -73,8 +73,8 @@ * * Returns refid of top level page. */ -int xen_be_share_pages(struct page **pages, int domid, int nents, - void **refs_info) +long xen_be_share_pages(struct page **pages, int domid, int nents, + void **refs_info) { grant_ref_t lvl3_gref; grant_ref_t *lvl2_table; diff --git a/drivers/dma-buf/hyper_dmabuf/xen/hyper_dmabuf_xen_shm.h b/drivers/dma-buf/hyper_dmabuf/xen/hyper_dmabuf_xen_shm.h index d5236b500075..f23deb394a00 100644 --- a/drivers/dma-buf/hyper_dmabuf/xen/hyper_dmabuf_xen_shm.h +++ b/drivers/dma-buf/hyper_dmabuf/xen/hyper_dmabuf_xen_shm.h @@ -29,8 +29,8 @@ * create a table with those in 1st level shared pages then return reference * numbers for this top level table. */ -int xen_be_share_pages(struct page **pages, int domid, int nents, - void **refs_info); +long xen_be_share_pages(struct page **pages, int domid, int nents, + void **refs_info); int xen_be_unshare_pages(void **refs_info, int nents); From 3a5e99d24dd337c3fc277d42b7e6a387bd055276 Mon Sep 17 00:00:00 2001 From: Wei Liu Date: Wed, 19 Sep 2018 16:21:22 +0800 Subject: [PATCH 0957/1103] hyper_dmabuf: fix compile warnings in hyper_dmabuf This patch fix the compile warnings in hyper_dmabuf Tracked-On: https://github.com/projectacrn/acrn-hypervisor/issues/1286 Signed-off-by: Wei Liu Reviewed-by: Jason Chen CJ --- drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_msg.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_msg.c b/drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_msg.c index d91a9eb3bed8..fe9e4e2339a1 100644 --- a/drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_msg.c +++ b/drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_msg.c @@ -217,7 +217,7 @@ static void cmd_process_work(struct work_struct *work) dev_dbg(hy_drv_priv->dev, "\tnents %d\n", req->op[4]); dev_dbg(hy_drv_priv->dev, "\tfirst offset %d\n", req->op[5]); dev_dbg(hy_drv_priv->dev, "\tlast len %d\n", req->op[6]); - dev_dbg(hy_drv_priv->dev, "\tgrefid 0x%lx\n", + dev_dbg(hy_drv_priv->dev, "\tgrefid 0x%llx\n", (u64)req->op[8] << 32 | req->op[7]); memcpy(imported->priv, &req->op[10], req->op[9]); From 42f632716f79cb20e645c6d00e19e8736f03ba21 Mon Sep 17 00:00:00 2001 From: Mateusz Polrola Date: Thu, 20 Sep 2018 12:12:52 +0200 Subject: [PATCH 0958/1103] hyper_dmabuf/virtio: Adapt to the new state transition of VHM requests Instead of using two members (namely ''valid'' and ''processed''), the new state transition uses a single member (i.e. ''processed) following the transition pattern below. FREE -> PENDING -> PROCESSING -> COMPLETE -> FREE -> ... Additionally atomic operations should be used to access the state. Signed-off-by: Mateusz Polrola --- .../hyper_dmabuf/virtio/hyper_dmabuf_virtio_be_drv.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/dma-buf/hyper_dmabuf/virtio/hyper_dmabuf_virtio_be_drv.c b/drivers/dma-buf/hyper_dmabuf/virtio/hyper_dmabuf_virtio_be_drv.c index 51dd8ed8271a..400c6e702005 100644 --- a/drivers/dma-buf/hyper_dmabuf/virtio/hyper_dmabuf_virtio_be_drv.c +++ b/drivers/dma-buf/hyper_dmabuf/virtio/hyper_dmabuf_virtio_be_drv.c @@ -179,15 +179,15 @@ static int virtio_be_handle_kick(int client_id, int req_cnt) for (i = 0; i < fe_info->max_vcpu; ++i) { req = &fe_info->req_buf[i]; - if (req->valid && - req->processed == REQ_STATE_PROCESSING && + if (atomic_read(&req->processed) == REQ_STATE_PROCESSING && req->client == fe_info->client_id) { if (req->reqs.pio_request.direction == REQUEST_READ) req->reqs.pio_request.value = 0; else val = req->reqs.pio_request.value; - req->processed = REQ_STATE_SUCCESS; + smp_mb(); + atomic_set(&req->processed, REQ_STATE_COMPLETE); acrn_ioreq_complete_request(fe_info->client_id, i); } } From 58fc70f33c973adb93fd15925d9fcaa0422c1442 Mon Sep 17 00:00:00 2001 From: Mateusz Polrola Date: Thu, 20 Sep 2018 14:09:19 +0200 Subject: [PATCH 0959/1103] hyper_dmabuf/virtio: Process ioreq according to bitmap Vhm will record pending ioreqs of the vhm client into a bitmap, then vhm client (like hyper_dmabuf) can process the ioreq directly according to the bitmap. Signed-off-by: Mateusz Polrola --- .../virtio/hyper_dmabuf_virtio_be_drv.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/drivers/dma-buf/hyper_dmabuf/virtio/hyper_dmabuf_virtio_be_drv.c b/drivers/dma-buf/hyper_dmabuf/virtio/hyper_dmabuf_virtio_be_drv.c index 400c6e702005..67c79683407b 100644 --- a/drivers/dma-buf/hyper_dmabuf/virtio/hyper_dmabuf_virtio_be_drv.c +++ b/drivers/dma-buf/hyper_dmabuf/virtio/hyper_dmabuf_virtio_be_drv.c @@ -161,15 +161,12 @@ static void virtio_be_handle_vq_kick( /* * Received new buffer in virtqueue */ -static int virtio_be_handle_kick(int client_id, int req_cnt) +static int virtio_be_handle_kick(int client_id, unsigned long *ioreqs_map) { int val = -1; struct vhm_request *req; struct virtio_fe_info *fe_info; - int i; - - if (unlikely(req_cnt <= 0)) - return -EINVAL; + int vcpu; fe_info = virtio_fe_find(client_id); if (fe_info == NULL) { @@ -177,8 +174,11 @@ static int virtio_be_handle_kick(int client_id, int req_cnt) return -EINVAL; } - for (i = 0; i < fe_info->max_vcpu; ++i) { - req = &fe_info->req_buf[i]; + while (1) { + vcpu = find_first_bit(ioreqs_map, fe_info->max_vcpu); + if (vcpu == fe_info->max_vcpu) + break; + req = &fe_info->req_buf[vcpu]; if (atomic_read(&req->processed) == REQ_STATE_PROCESSING && req->client == fe_info->client_id) { if (req->reqs.pio_request.direction == REQUEST_READ) @@ -188,7 +188,7 @@ static int virtio_be_handle_kick(int client_id, int req_cnt) smp_mb(); atomic_set(&req->processed, REQ_STATE_COMPLETE); - acrn_ioreq_complete_request(fe_info->client_id, i); + acrn_ioreq_complete_request(fe_info->client_id, vcpu); } } From 6d2d902f2d70f2bcdcd5c03458c77e77ee6afbef Mon Sep 17 00:00:00 2001 From: Mateusz Polrola Date: Thu, 20 Sep 2018 14:09:37 +0200 Subject: [PATCH 0960/1103] hyper_dmabuf/virtio: Fixed compilation warnings Added missing return in vbs_k_reset. Initialized return value in vbs_k_ioctl. Signed-off-by: Mateusz Polrola --- .../dma-buf/hyper_dmabuf/virtio/hyper_dmabuf_virtio_be_drv.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/dma-buf/hyper_dmabuf/virtio/hyper_dmabuf_virtio_be_drv.c b/drivers/dma-buf/hyper_dmabuf/virtio/hyper_dmabuf_virtio_be_drv.c index 67c79683407b..b308d7e00d18 100644 --- a/drivers/dma-buf/hyper_dmabuf/virtio/hyper_dmabuf_virtio_be_drv.c +++ b/drivers/dma-buf/hyper_dmabuf/virtio/hyper_dmabuf_virtio_be_drv.c @@ -365,6 +365,8 @@ static int vbs_k_reset(struct virtio_be_priv *priv) virtio_comm_ring_init(&priv->tx_ring, sizeof(struct virtio_be_tx_data), REQ_RING_SIZE); + + return 0; } static long vbs_k_ioctl(struct file *f, unsigned int ioctl, @@ -373,7 +375,7 @@ static long vbs_k_ioctl(struct file *f, unsigned int ioctl, struct virtio_be_priv *priv = (struct virtio_be_priv *) f->private_data; void __user *argp = (void __user *)arg; - int r; + int r = 0; if (priv == NULL) { dev_err(hy_drv_priv->dev, From 4c85e3807e7f55ede261492a6e477a13ced65149 Mon Sep 17 00:00:00 2001 From: Mateusz Polrola Date: Thu, 20 Sep 2018 14:01:22 +0200 Subject: [PATCH 0961/1103] hyper_dmabuf: Align with dma_buf_ops changes In 4.17.0 kernel, map_atomic and unmap_atomic callbacks were removed from dma_buf_ops, additionally device param for attach callback was removed. Signed-off-by: Mateusz Polrola --- drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_ops.c | 7 +++++++ drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_remote_sync.c | 5 +++++ 2 files changed, 12 insertions(+) diff --git a/drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_ops.c b/drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_ops.c index 10b5510b3816..3bd13c584ffc 100644 --- a/drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_ops.c +++ b/drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_ops.c @@ -30,6 +30,7 @@ #include #include #include +#include #include "hyper_dmabuf_drv.h" #include "hyper_dmabuf_struct.h" #include "hyper_dmabuf_ops.h" @@ -86,7 +87,9 @@ static int sync_request(hyper_dmabuf_id_t hid, int dmabuf_ops) } static int hyper_dmabuf_ops_attach(struct dma_buf *dmabuf, +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 17, 0) struct device *dev, +#endif struct dma_buf_attachment *attach) { struct imported_sgt_info *imported; @@ -255,6 +258,7 @@ static int hyper_dmabuf_ops_end_cpu_access(struct dma_buf *dmabuf, return sync_request(imported->hid, HYPER_DMABUF_OPS_END_CPU_ACCESS); } +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 17, 0) static void *hyper_dmabuf_ops_kmap_atomic(struct dma_buf *dmabuf, unsigned long pgnum) { @@ -283,6 +287,7 @@ static void hyper_dmabuf_ops_kunmap_atomic(struct dma_buf *dmabuf, sync_request(imported->hid, HYPER_DMABUF_OPS_KUNMAP_ATOMIC); } +#endif static void *hyper_dmabuf_ops_kmap(struct dma_buf *dmabuf, unsigned long pgnum) { @@ -362,8 +367,10 @@ static const struct dma_buf_ops hyper_dmabuf_ops = { .release = hyper_dmabuf_ops_release, .begin_cpu_access = hyper_dmabuf_ops_begin_cpu_access, .end_cpu_access = hyper_dmabuf_ops_end_cpu_access, +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 17, 0) .map_atomic = hyper_dmabuf_ops_kmap_atomic, .unmap_atomic = hyper_dmabuf_ops_kunmap_atomic, +#endif .map = hyper_dmabuf_ops_kmap, .unmap = hyper_dmabuf_ops_kunmap, .mmap = hyper_dmabuf_ops_mmap, diff --git a/drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_remote_sync.c b/drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_remote_sync.c index a82fd7b087b8..3cd3d6c98c33 100644 --- a/drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_remote_sync.c +++ b/drivers/dma-buf/hyper_dmabuf/hyper_dmabuf_remote_sync.c @@ -30,6 +30,7 @@ #include #include #include +#include #include "hyper_dmabuf_drv.h" #include "hyper_dmabuf_struct.h" #include "hyper_dmabuf_list.h" @@ -219,10 +220,12 @@ int hyper_dmabuf_remote_sync(hyper_dmabuf_id_t hid, int ops) return -ENOMEM; /* dummy kmapping of 1 page */ +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 17, 0) if (ops == HYPER_DMABUF_OPS_KMAP_ATOMIC) va_kmapl->vaddr = dma_buf_kmap_atomic( exported->dma_buf, 1); else +#endif va_kmapl->vaddr = dma_buf_kmap( exported->dma_buf, 1); @@ -253,11 +256,13 @@ int hyper_dmabuf_remote_sync(hyper_dmabuf_id_t hid, int ops) return PTR_ERR(va_kmapl->vaddr); } +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 17, 0) /* unmapping 1 page */ if (ops == HYPER_DMABUF_OPS_KUNMAP_ATOMIC) dma_buf_kunmap_atomic(exported->dma_buf, 1, va_kmapl->vaddr); else +#endif dma_buf_kunmap(exported->dma_buf, 1, va_kmapl->vaddr); From 7c88ab46ca1712877e1b581b14f8a82bc19b48d8 Mon Sep 17 00:00:00 2001 From: Min He Date: Tue, 9 Oct 2018 16:41:21 +0800 Subject: [PATCH 0962/1103] drm/i915: diable huge page ppgtt when using PVMMIO ppgtt update When using PVMMIO ppgtt update, it's too complex and has performance impact to support huge page PPGTT, so we will disable this feature in this patch. Tracked-On: projectacrn/acrn-hypervisor#1413 Signed-off-by: Min He Reviewed-by: Xinyun Liu --- drivers/gpu/drm/i915/i915_gem.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index c3e87d10f14e..23478d7edc94 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -5462,7 +5462,8 @@ int i915_gem_init(struct drm_i915_private *dev_priv) int ret; /* We need to fallback to 4K pages if host doesn't support huge gtt. */ - if (intel_vgpu_active(dev_priv) && !intel_vgpu_has_huge_gtt(dev_priv)) + if ((intel_vgpu_active(dev_priv) && !intel_vgpu_has_huge_gtt(dev_priv)) + || PVMMIO_LEVEL(dev_priv, PVMMIO_PPGTT_UPDATE)) mkwrite_device_info(dev_priv)->page_sizes = I915_GTT_PAGE_SIZE_4K; From 78ae6a867191c959ad9bb4c65eada94163e49f11 Mon Sep 17 00:00:00 2001 From: Vivek Kasireddy Date: Thu, 8 Mar 2018 15:10:34 -0800 Subject: [PATCH 0963/1103] INTERNAL [IOTG] drm/i915: Decouple pipe and crtc index dependencies i915 driver had assumptions that pipe and the crtc index always matched, but with plane restrictions feature, this assuumption is no longer true. In virualized environment guest Oses may have pipes with no planes attached and no crtc should be created for that pipe (but for service OS, CRTC still needs to be created to do the initial modeset). In cases were no CRTC was created for some pipes, the index and pipe won't match and was causing issues. These changes are to decouple the pipe and index dependencies in the driver. v2(ssingh) : Ported this patch to 4.19 kernel. Signed-off-by: Satyeshwar Singh Signed-off-by: Anitha Chrisanthus Signed-off-by: Vivek Kasireddy Change-Id: Iccf6b54c254e2b3d7053620c1b64ad8dda7632b8 --- drivers/gpu/drm/i915/i915_drv.h | 10 +++--- drivers/gpu/drm/i915/i915_irq.c | 32 ++++++++++++----- drivers/gpu/drm/i915/i915_trace.h | 2 +- drivers/gpu/drm/i915/intel_display.c | 40 ++++++++++++++++++---- drivers/gpu/drm/i915/intel_dpll_mgr.c | 5 +-- drivers/gpu/drm/i915/intel_drv.h | 10 +++++- drivers/gpu/drm/i915/intel_fifo_underrun.c | 11 ++++-- drivers/gpu/drm/i915/intel_pm.c | 7 ++-- 8 files changed, 87 insertions(+), 30 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index 18650bc9c8e4..e477f8a0764f 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -2822,18 +2822,18 @@ ilk_disable_display_irq(struct drm_i915_private *dev_priv, uint32_t bits) ilk_update_display_irq(dev_priv, bits, 0); } void bdw_update_pipe_irq(struct drm_i915_private *dev_priv, - enum pipe pipe, + unsigned int crtc_index, uint32_t interrupt_mask, uint32_t enabled_irq_mask); static inline void bdw_enable_pipe_irq(struct drm_i915_private *dev_priv, - enum pipe pipe, uint32_t bits) + unsigned int crtc_index, uint32_t bits) { - bdw_update_pipe_irq(dev_priv, pipe, bits, bits); + bdw_update_pipe_irq(dev_priv, crtc_index, bits, bits); } static inline void bdw_disable_pipe_irq(struct drm_i915_private *dev_priv, - enum pipe pipe, uint32_t bits) + unsigned int crtc_index, uint32_t bits) { - bdw_update_pipe_irq(dev_priv, pipe, bits, 0); + bdw_update_pipe_irq(dev_priv, crtc_index, bits, 0); } void ibx_display_interrupt_update(struct drm_i915_private *dev_priv, uint32_t interrupt_mask, diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c index 71977fb8725c..5e0e9f189418 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c @@ -626,11 +626,12 @@ static void bdw_update_port_irq(struct drm_i915_private *dev_priv, * @enabled_irq_mask: mask of interrupt bits to enable */ void bdw_update_pipe_irq(struct drm_i915_private *dev_priv, - enum pipe pipe, + unsigned int crtc_index, uint32_t interrupt_mask, uint32_t enabled_irq_mask) { uint32_t new_val; + enum pipe pipe; lockdep_assert_held(&dev_priv->irq_lock); @@ -639,6 +640,9 @@ void bdw_update_pipe_irq(struct drm_i915_private *dev_priv, if (WARN_ON(!intel_irqs_enabled(dev_priv))) return; + if(get_pipe_from_crtc_index(&dev_priv->drm, crtc_index, &pipe)) + return; + new_val = dev_priv->de_irq_mask[pipe]; new_val &= ~interrupt_mask; new_val |= (~enabled_irq_mask & interrupt_mask); @@ -884,9 +888,14 @@ static u32 i915_get_vblank_counter(struct drm_device *dev, unsigned int pipe) return (((high1 << 8) | low) + (pixel >= vbl_start)) & 0xffffff; } -static u32 g4x_get_vblank_counter(struct drm_device *dev, unsigned int pipe) +static u32 g4x_get_vblank_counter(struct drm_device *dev, + unsigned int crtc_index) { struct drm_i915_private *dev_priv = to_i915(dev); + enum pipe pipe; + + if(get_pipe_from_crtc_index(dev, crtc_index, &pipe)) + return 0; return I915_READ(PIPE_FRMCOUNT_G4X(pipe)); } @@ -1002,18 +1011,21 @@ static int __intel_get_crtc_scanline(struct intel_crtc *crtc) return (position + crtc->scanline_offset) % vtotal; } -static bool i915_get_crtc_scanoutpos(struct drm_device *dev, unsigned int pipe, - bool in_vblank_irq, int *vpos, int *hpos, - ktime_t *stime, ktime_t *etime, - const struct drm_display_mode *mode) +static bool i915_get_crtc_scanoutpos(struct drm_device *dev, unsigned int crtc_index, + bool in_vblank_irq, int *vpos, int *hpos, + ktime_t *stime, ktime_t *etime, + const struct drm_display_mode *mode) { struct drm_i915_private *dev_priv = to_i915(dev); - struct intel_crtc *intel_crtc = intel_get_crtc_for_pipe(dev_priv, - pipe); + struct intel_crtc *intel_crtc; + enum pipe pipe; int position; int vbl_start, vbl_end, hsync_start, htotal, vtotal; unsigned long irqflags; + intel_crtc = get_intel_crtc_from_index(dev, crtc_index); + pipe = intel_crtc->pipe; + if (WARN_ON(!mode->crtc_clock)) { DRM_DEBUG_DRIVER("trying to get scanoutpos for disabled " "pipe %c\n", pipe_name(pipe)); @@ -2748,6 +2760,7 @@ gen8_de_irq_handler(struct drm_i915_private *dev_priv, u32 master_ctl) irqreturn_t ret = IRQ_NONE; u32 iir; enum pipe pipe; + struct intel_crtc *crtc; if (master_ctl & GEN8_DE_MISC_IRQ) { iir = I915_READ(GEN8_DE_MISC_IIR); @@ -2858,8 +2871,9 @@ gen8_de_irq_handler(struct drm_i915_private *dev_priv, u32 master_ctl) ret = IRQ_HANDLED; I915_WRITE(GEN8_DE_PIPE_IIR(pipe), iir); + crtc = intel_get_crtc_for_pipe(dev_priv, pipe); if (iir & GEN8_PIPE_VBLANK) { - drm_handle_vblank(&dev_priv->drm, pipe); + drm_handle_vblank(&dev_priv->drm, drm_crtc_index(&crtc->base)); #if IS_ENABLED(CONFIG_DRM_I915_GVT) gvt_notify_vblank(dev_priv, pipe); #endif diff --git a/drivers/gpu/drm/i915/i915_trace.h b/drivers/gpu/drm/i915/i915_trace.h index af592e3d09a9..6f25961ad9ad 100644 --- a/drivers/gpu/drm/i915/i915_trace.h +++ b/drivers/gpu/drm/i915/i915_trace.h @@ -281,7 +281,7 @@ TRACE_EVENT(i915_pipe_update_start, TP_fast_assign( __entry->pipe = crtc->pipe; __entry->frame = crtc->base.dev->driver->get_vblank_counter(crtc->base.dev, - crtc->pipe); + drm_crtc_index(&crtc->base)); __entry->scanline = intel_get_crtc_scanline(crtc); __entry->min = crtc->debug.min_vbl; __entry->max = crtc->debug.max_vbl; diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 5b32748f8c07..6a7678908da9 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -11949,11 +11949,13 @@ verify_single_dpll_state(struct drm_i915_private *dev_priv, if (new_state->active) I915_STATE_WARN(!(pll->active_mask & crtc_mask), "pll active mismatch (expected pipe %c in active mask 0x%02x)\n", - pipe_name(drm_crtc_index(crtc)), pll->active_mask); + pipe_name(to_intel_crtc(crtc)->pipe), + pll->active_mask); else I915_STATE_WARN(pll->active_mask & crtc_mask, "pll active mismatch (didn't expect pipe %c in active mask 0x%02x)\n", - pipe_name(drm_crtc_index(crtc)), pll->active_mask); + pipe_name(to_intel_crtc(crtc)->pipe), + pll->active_mask); I915_STATE_WARN(!(pll->state.crtc_mask & crtc_mask), "pll enabled crtcs mismatch (expected 0x%x in 0x%02x)\n", @@ -11984,10 +11986,10 @@ verify_shared_dpll_state(struct drm_device *dev, struct drm_crtc *crtc, I915_STATE_WARN(pll->active_mask & crtc_mask, "pll active mismatch (didn't expect pipe %c in active mask)\n", - pipe_name(drm_crtc_index(crtc))); + pipe_name(to_intel_crtc(crtc)->pipe)); I915_STATE_WARN(pll->state.crtc_mask & crtc_mask, "pll enabled crtcs mismatch (found %x in enabled mask)\n", - pipe_name(drm_crtc_index(crtc))); + pipe_name(to_intel_crtc(crtc)->pipe)); } } @@ -12402,7 +12404,8 @@ u32 intel_crtc_get_vblank_counter(struct intel_crtc *crtc) if (!dev->max_vblank_count) return (u32)drm_crtc_accurate_vblank_count(&crtc->base); - return dev->driver->get_vblank_counter(dev, crtc->pipe); + return dev->driver->get_vblank_counter(dev, + drm_crtc_index(&crtc->base)); } static void intel_update_crtc(struct drm_crtc *crtc, @@ -14055,6 +14058,27 @@ int intel_get_pipe_from_crtc_id_ioctl(struct drm_device *dev, void *data, return 0; } +int get_pipe_from_crtc_index(struct drm_device *dev, unsigned int index, enum pipe *pipe) +{ + struct drm_crtc *c = drm_crtc_from_index(dev, index); + + if (WARN_ON(!c)) + return -ENOENT; + + *pipe = (to_intel_crtc(c)->pipe); + return 0; +} + +struct intel_crtc *get_intel_crtc_from_index(struct drm_device *dev, + unsigned int index) +{ + struct drm_crtc *c = drm_crtc_from_index(dev, index); + + WARN_ON(!c); + return to_intel_crtc(c); +} + + static int intel_encoder_clones(struct intel_encoder *encoder) { struct drm_device *dev = encoder->base.dev; @@ -15641,7 +15665,8 @@ static void intel_modeset_readout_hw_state(struct drm_device *dev) crtc->active = crtc_state->base.active; if (crtc_state->base.active) - dev_priv->active_crtcs |= 1 << crtc->pipe; + dev_priv->active_crtcs |= + 1 << drm_crtc_index(&crtc->base); readout_plane_state(crtc); @@ -15662,7 +15687,8 @@ static void intel_modeset_readout_hw_state(struct drm_device *dev) if (crtc_state->base.active && crtc_state->shared_dpll == pll) - pll->state.crtc_mask |= 1 << crtc->pipe; + pll->state.crtc_mask |= + 1 << drm_crtc_index(&crtc->base); } pll->active_mask = pll->state.crtc_mask; diff --git a/drivers/gpu/drm/i915/intel_dpll_mgr.c b/drivers/gpu/drm/i915/intel_dpll_mgr.c index b51ad2917dbe..47b1dfe06152 100644 --- a/drivers/gpu/drm/i915/intel_dpll_mgr.c +++ b/drivers/gpu/drm/i915/intel_dpll_mgr.c @@ -303,7 +303,7 @@ intel_reference_shared_dpll(struct intel_shared_dpll *pll, DRM_DEBUG_DRIVER("using %s for pipe %c\n", pll->info->name, pipe_name(crtc->pipe)); - shared_dpll[id].crtc_mask |= 1 << crtc->pipe; + shared_dpll[id].crtc_mask |= 1 << (drm_crtc_index(&crtc->base)); } /** @@ -3285,7 +3285,8 @@ void intel_release_shared_dpll(struct intel_shared_dpll *dpll, struct intel_shared_dpll_state *shared_dpll_state; shared_dpll_state = intel_atomic_get_shared_dpll_state(state); - shared_dpll_state[dpll->info->id].crtc_mask &= ~(1 << crtc->pipe); + shared_dpll_state[dpll->info->id].crtc_mask &= + ~(1 << drm_crtc_index(&crtc->base)); } /** diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h index 098c2886cf7e..49d4282b234b 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h @@ -1520,6 +1520,9 @@ enum tc_port intel_port_to_tc(struct drm_i915_private *dev_priv, enum pipe intel_get_pipe_from_connector(struct intel_connector *connector); int intel_get_pipe_from_crtc_id_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv); +int get_pipe_from_crtc_index(struct drm_device *dev, unsigned int index, enum pipe *pipe); +struct intel_crtc *get_intel_crtc_from_index(struct drm_device *dev, + unsigned int index); enum transcoder intel_pipe_to_cpu_transcoder(struct drm_i915_private *dev_priv, enum pipe pipe); static inline bool @@ -1539,7 +1542,12 @@ intel_crtc_has_dp_encoder(const struct intel_crtc_state *crtc_state) static inline void intel_wait_for_vblank(struct drm_i915_private *dev_priv, enum pipe pipe) { - drm_wait_one_vblank(&dev_priv->drm, pipe); + struct intel_crtc *crtc; + + crtc = intel_get_crtc_for_pipe(dev_priv, pipe); + if (crtc) + drm_wait_one_vblank(&dev_priv->drm, + drm_crtc_index(&crtc->base)); } static inline void intel_wait_for_vblank_if_active(struct drm_i915_private *dev_priv, int pipe) diff --git a/drivers/gpu/drm/i915/intel_fifo_underrun.c b/drivers/gpu/drm/i915/intel_fifo_underrun.c index 77c123cc8817..83ed6f56ed56 100644 --- a/drivers/gpu/drm/i915/intel_fifo_underrun.c +++ b/drivers/gpu/drm/i915/intel_fifo_underrun.c @@ -181,11 +181,18 @@ static void broadwell_set_fifo_underrun_reporting(struct drm_device *dev, enum pipe pipe, bool enable) { struct drm_i915_private *dev_priv = to_i915(dev); + struct intel_crtc *crtc = intel_get_crtc_for_pipe(dev_priv, pipe); + if (!crtc) { + DRM_DEBUG("No crtc for pipe=%d\n", pipe); + return; + } if (enable) - bdw_enable_pipe_irq(dev_priv, pipe, GEN8_PIPE_FIFO_UNDERRUN); + bdw_enable_pipe_irq(dev_priv, drm_crtc_index(&crtc->base), + GEN8_PIPE_FIFO_UNDERRUN); else - bdw_disable_pipe_irq(dev_priv, pipe, GEN8_PIPE_FIFO_UNDERRUN); + bdw_disable_pipe_irq(dev_priv, drm_crtc_index(&crtc->base), + GEN8_PIPE_FIFO_UNDERRUN); } static void ibx_set_fifo_underrun_reporting(struct drm_device *dev, diff --git a/drivers/gpu/drm/i915/intel_pm.c b/drivers/gpu/drm/i915/intel_pm.c index c7c7b5e78330..1884b8140131 100644 --- a/drivers/gpu/drm/i915/intel_pm.c +++ b/drivers/gpu/drm/i915/intel_pm.c @@ -3710,7 +3710,6 @@ bool intel_can_enable_sagv(struct drm_atomic_state *state) struct intel_crtc *crtc; struct intel_plane *plane; struct intel_crtc_state *cstate; - enum pipe pipe; int level, latency; int sagv_block_time_us; @@ -3736,8 +3735,10 @@ bool intel_can_enable_sagv(struct drm_atomic_state *state) return false; /* Since we're now guaranteed to only have one active CRTC... */ - pipe = ffs(intel_state->active_crtcs) - 1; - crtc = intel_get_crtc_for_pipe(dev_priv, pipe); + crtc = get_intel_crtc_from_index(dev, + ffs(intel_state->active_crtcs) - 1); + if (!crtc) + return false; cstate = to_intel_crtc_state(crtc->base.state); if (crtc->base.state->adjusted_mode.flags & DRM_MODE_FLAG_INTERLACE) From 7bd8b2491b4ab179de9fd9b842a7d56a245c213c Mon Sep 17 00:00:00 2001 From: Vivek Kasireddy Date: Thu, 8 Mar 2018 11:14:57 -0800 Subject: [PATCH 0964/1103] INTERNAL [IOTG] drm: Don't assume that the primary plane always exists In some virtualization use-cases, the operating system and hence the graphics drivers may have to deal with a crtc(s) that does not have a primary plane associated with it. For such cases, the legacy non-atomic APIs should check for the existence of the primary plane and return -EINVAL if it's not present. v2(ssingh) : Ported this patch to 4.19 kernel. Signed-off-by: Satyeshwar Singh Signed-off-by: Vivek Kasireddy Change-Id: Ie16c4bdd19a3df25ca5c4241ded12b87ca5a106a --- drivers/gpu/drm/drm_atomic_helper.c | 5 +++- drivers/gpu/drm/drm_crtc.c | 13 ++++++++++ drivers/gpu/drm/drm_crtc_helper.c | 10 +++++++ drivers/gpu/drm/drm_fb_helper.c | 2 ++ drivers/gpu/drm/drm_framebuffer.c | 2 +- drivers/gpu/drm/i915/i915_debugfs.c | 39 ++++++++++++++++++---------- drivers/gpu/drm/i915/intel_display.c | 14 +++++----- drivers/gpu/drm/i915/intel_fbdev.c | 11 ++++++-- 8 files changed, 73 insertions(+), 23 deletions(-) diff --git a/drivers/gpu/drm/drm_atomic_helper.c b/drivers/gpu/drm/drm_atomic_helper.c index 80be74df7ba6..bb7582f7613b 100644 --- a/drivers/gpu/drm/drm_atomic_helper.c +++ b/drivers/gpu/drm/drm_atomic_helper.c @@ -1074,7 +1074,7 @@ drm_atomic_helper_update_legacy_modeset_state(struct drm_device *dev, crtc->enabled = new_crtc_state->enable; new_plane_state = - drm_atomic_get_new_plane_state(old_state, primary); + primary ? drm_atomic_get_new_plane_state(old_state, primary) : NULL; if (new_plane_state && new_plane_state->crtc == crtc) { crtc->x = new_plane_state->src_x >> 16; @@ -2912,6 +2912,9 @@ int __drm_atomic_helper_set_config(struct drm_mode_set *set, int hdisplay, vdisplay; int ret; + if (!crtc->primary) + return -EINVAL; + crtc_state = drm_atomic_get_crtc_state(state, crtc); if (IS_ERR(crtc_state)) return PTR_ERR(crtc_state); diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c index bae43938c8f6..8dd3c1123eba 100644 --- a/drivers/gpu/drm/drm_crtc.c +++ b/drivers/gpu/drm/drm_crtc.c @@ -410,6 +410,9 @@ int drm_mode_getcrtc(struct drm_device *dev, plane = crtc->primary; + if (!crtc->primary) + return -EINVAL; + crtc_resp->gamma_size = crtc->gamma_size; drm_modeset_lock(&plane->mutex, NULL); @@ -461,6 +464,9 @@ static int __drm_mode_set_config_internal(struct drm_mode_set *set, struct drm_crtc *tmp; int ret; + if (!crtc->primary) + return -EINVAL; + WARN_ON(drm_drv_uses_atomic_modeset(crtc->dev)); /* @@ -470,6 +476,8 @@ static int __drm_mode_set_config_internal(struct drm_mode_set *set, */ drm_for_each_crtc(tmp, crtc->dev) { struct drm_plane *plane = tmp->primary; + if (!tmp->primary) + continue; plane->old_fb = plane->fb; } @@ -486,6 +494,8 @@ static int __drm_mode_set_config_internal(struct drm_mode_set *set, drm_for_each_crtc(tmp, crtc->dev) { struct drm_plane *plane = tmp->primary; + if (!tmp->primary) + continue; if (plane->fb) drm_framebuffer_get(plane->fb); @@ -602,6 +612,9 @@ int drm_mode_setcrtc(struct drm_device *dev, void *data, if (ret) goto out; + if (!crtc->primary) + return -EINVAL; + if (crtc_req->mode_valid) { /* If we have a mode we need a framebuffer. */ /* If we pass -1, set the mode with the currently bound fb */ diff --git a/drivers/gpu/drm/drm_crtc_helper.c b/drivers/gpu/drm/drm_crtc_helper.c index 5a84c3bc915d..aaa80d640203 100644 --- a/drivers/gpu/drm/drm_crtc_helper.c +++ b/drivers/gpu/drm/drm_crtc_helper.c @@ -185,6 +185,8 @@ static void __drm_helper_disable_unused_functions(struct drm_device *dev) (*crtc_funcs->disable)(crtc); else (*crtc_funcs->dpms)(crtc, DRM_MODE_DPMS_OFF); + if (!crtc->primary) + continue; crtc->primary->fb = NULL; } } @@ -539,6 +541,9 @@ int drm_crtc_helper_set_config(struct drm_mode_set *set, crtc_funcs = set->crtc->helper_private; + if (!set->crtc->primary) + return -EINVAL; + if (!set->mode) set->fb = NULL; @@ -950,6 +955,8 @@ void drm_helper_resume_force_mode(struct drm_device *dev) if (!crtc->enabled) continue; + if (!crtc->primary) + continue; ret = drm_crtc_helper_set_mode(crtc, &crtc->mode, crtc->x, crtc->y, crtc->primary->fb); @@ -1072,6 +1079,9 @@ int drm_helper_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y, struct drm_plane_state *plane_state; struct drm_plane *plane = crtc->primary; + if (!plane) + return -EINVAL; + if (plane->funcs->atomic_duplicate_state) plane_state = plane->funcs->atomic_duplicate_state(plane); else { diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c index 515a7aec57ac..03f780be85a0 100644 --- a/drivers/gpu/drm/drm_fb_helper.c +++ b/drivers/gpu/drm/drm_fb_helper.c @@ -549,6 +549,8 @@ static bool drm_fb_helper_is_bound(struct drm_fb_helper *fb_helper) drm_for_each_crtc(crtc, dev) { drm_modeset_lock(&crtc->mutex, NULL); + if (!crtc->primary) + continue; if (crtc->primary->fb) crtcs_bound++; if (crtc->primary->fb == fb_helper->fb) diff --git a/drivers/gpu/drm/drm_framebuffer.c b/drivers/gpu/drm/drm_framebuffer.c index 781af1d42d76..d2d2c30bea97 100644 --- a/drivers/gpu/drm/drm_framebuffer.c +++ b/drivers/gpu/drm/drm_framebuffer.c @@ -891,7 +891,7 @@ static void legacy_remove_fb(struct drm_framebuffer *fb) drm_modeset_lock_all(dev); /* remove from any CRTC */ drm_for_each_crtc(crtc, dev) { - if (crtc->primary->fb == fb) { + if (crtc->primary && crtc->primary->fb == fb) { /* should turn off the crtc */ if (drm_crtc_force_disable(crtc)) DRM_ERROR("failed to reset crtc %p when fb was deleted\n", crtc); diff --git a/drivers/gpu/drm/i915/i915_debugfs.c b/drivers/gpu/drm/i915/i915_debugfs.c index ff6d2c8b41bb..89b2863845d5 100644 --- a/drivers/gpu/drm/i915/i915_debugfs.c +++ b/drivers/gpu/drm/i915/i915_debugfs.c @@ -3008,15 +3008,23 @@ static void intel_crtc_info(struct seq_file *m, struct intel_crtc *intel_crtc) struct drm_device *dev = &dev_priv->drm; struct drm_crtc *crtc = &intel_crtc->base; struct intel_encoder *intel_encoder; - struct drm_plane_state *plane_state = crtc->primary->state; - struct drm_framebuffer *fb = plane_state->fb; + struct drm_plane_state *plane_state; + struct drm_framebuffer *fb; - if (fb) - seq_printf(m, "\tfb: %d, pos: %dx%d, size: %dx%d\n", + if (!crtc->primary) { + seq_puts(m, "\tno primary plane\n"); + } else { + plane_state = crtc->primary->state; + fb = plane_state->fb; + + if (fb) + seq_printf(m, "\tfb: %d, pos: %dx%d, size: %dx%d\n", fb->base.id, plane_state->src_x >> 16, plane_state->src_y >> 16, fb->width, fb->height); - else - seq_puts(m, "\tprimary plane disabled\n"); + else + seq_puts(m, "\tprimary plane disabled\n"); + } + for_each_encoder_on_crtc(dev, crtc, intel_encoder) intel_encoder_info(m, intel_crtc, intel_encoder); } @@ -3261,13 +3269,18 @@ static int i915_display_info(struct seq_file *m, void *unused) intel_crtc_info(m, crtc); - seq_printf(m, "\tcursor visible? %s, position (%d, %d), size %dx%d, addr 0x%08x\n", - yesno(cursor->base.state->visible), - cursor->base.state->crtc_x, - cursor->base.state->crtc_y, - cursor->base.state->crtc_w, - cursor->base.state->crtc_h, - cursor->cursor.base); + if (cursor) { + seq_printf(m, "\tcursor visible? %s, position (%d, %d), size %dx%d, addr 0x%08x\n", + yesno(cursor->base.state->visible), + cursor->base.state->crtc_x, + cursor->base.state->crtc_y, + cursor->base.state->crtc_w, + cursor->base.state->crtc_h, + cursor->cursor.base); + } else { + seq_puts(m, "\tNo cursor plane available on this platform\n"); + } + intel_scaler_info(m, crtc); intel_plane_info(m, crtc); } diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 6a7678908da9..a0d164244401 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -5241,8 +5241,8 @@ static void intel_post_plane_update(struct intel_crtc_state *old_crtc_state) intel_atomic_get_new_crtc_state(to_intel_atomic_state(old_state), crtc); struct drm_plane *primary = crtc->base.primary; - struct drm_plane_state *old_primary_state = - drm_atomic_get_old_plane_state(old_state, primary); + struct drm_plane_state *old_primary_state = primary ? + drm_atomic_get_old_plane_state(old_state, primary) : NULL; intel_frontbuffer_flip(to_i915(crtc->base.dev), pipe_config->fb_bits); @@ -5280,8 +5280,8 @@ static void intel_pre_plane_update(struct intel_crtc_state *old_crtc_state, struct drm_i915_private *dev_priv = to_i915(dev); struct drm_atomic_state *old_state = old_crtc_state->base.state; struct drm_plane *primary = crtc->base.primary; - struct drm_plane_state *old_primary_state = - drm_atomic_get_old_plane_state(old_state, primary); + struct drm_plane_state *old_primary_state = primary ? + drm_atomic_get_old_plane_state(old_state, primary) : NULL; bool modeset = needs_modeset(&pipe_config->base); struct intel_atomic_state *old_intel_state = to_intel_atomic_state(old_state); @@ -12419,8 +12419,9 @@ static void intel_update_crtc(struct drm_crtc *crtc, struct intel_crtc_state *pipe_config = to_intel_crtc_state(new_crtc_state); bool modeset = needs_modeset(new_crtc_state); struct intel_plane_state *new_plane_state = + crtc->primary ? intel_atomic_get_new_plane_state(to_intel_atomic_state(state), - to_intel_plane(crtc->primary)); + to_intel_plane(crtc->primary)) : NULL; if (modeset) { update_scanline_offset(intel_crtc); @@ -15668,7 +15669,8 @@ static void intel_modeset_readout_hw_state(struct drm_device *dev) dev_priv->active_crtcs |= 1 << drm_crtc_index(&crtc->base); - readout_plane_state(crtc); + if (crtc->base.primary) + readout_plane_state(crtc); DRM_DEBUG_KMS("[CRTC:%d:%s] hw state readout: %s\n", crtc->base.base.id, crtc->base.name, diff --git a/drivers/gpu/drm/i915/intel_fbdev.c b/drivers/gpu/drm/i915/intel_fbdev.c index fb2f9fce34cd..f028a6fb33fe 100644 --- a/drivers/gpu/drm/i915/intel_fbdev.c +++ b/drivers/gpu/drm/i915/intel_fbdev.c @@ -548,10 +548,17 @@ static bool intel_fbdev_init_bios(struct drm_device *dev, /* Find the largest fb */ for_each_crtc(dev, crtc) { - struct drm_i915_gem_object *obj = - intel_fb_obj(crtc->primary->state->fb); + struct drm_i915_gem_object *obj; intel_crtc = to_intel_crtc(crtc); + if (!crtc->primary) { + DRM_DEBUG_KMS("pipe %c has no primary plane\n", + pipe_name(intel_crtc->pipe)); + continue; + } + + obj = intel_fb_obj(crtc->primary->state->fb); + if (!crtc->state->active || !obj) { DRM_DEBUG_KMS("pipe %c not active or no fb, skipping\n", pipe_name(intel_crtc->pipe)); From 6d93ca851eddd4008ca386bfda6de43809dac239 Mon Sep 17 00:00:00 2001 From: Vivek Kasireddy Date: Mon, 12 Mar 2018 17:52:42 -0700 Subject: [PATCH 0965/1103] INTERNAL [IOTG] drm/i915: Introduce the Plane Restriction feature This feature allows restrictions to the way planes are created in the driver. Currently driver creates a primary plane and all the sprite planes that are possible depending on the underlying hardware.This feature allows restrictions on the number and type of planes that are created. To accomodate the legacy APIs the first plane that is created is designated as DRM_PLANE_TYPE_PRIMARY. Usecase is to restrict the planes that are available for each domain. Each domain passes in the plane mask through command line paramenter- available_planes_per_pipe. Only these planes are created for each domain. Another parameter domain_plane_owners is passed into dom0 so that gvt is aware of the plane ownership for all the domains and can restrict access. DDBs are written once for all planes in Dom0 when in a virtualized environment. v2(ssingh) : Ported this patch to 4.19 kernel. Signed-off-by: Satyeshwar Singh Signed-off-by: Anitha Chrisanthus Signed-off-by: Vivek Kasireddy Change-Id: I293c490be8994bb525e511c8d1b8324b3c70932c --- drivers/gpu/drm/i915/gvt/display.c | 20 +++ drivers/gpu/drm/i915/gvt/gvt.c | 28 ++- drivers/gpu/drm/i915/gvt/handlers.c | 10 +- drivers/gpu/drm/i915/i915_drv.c | 25 ++- drivers/gpu/drm/i915/i915_drv.h | 5 + drivers/gpu/drm/i915/i915_params.c | 52 ++++++ drivers/gpu/drm/i915/i915_params.h | 2 + drivers/gpu/drm/i915/intel_display.c | 255 ++++++++++++++++++++++++++- drivers/gpu/drm/i915/intel_drv.h | 3 + drivers/gpu/drm/i915/intel_pm.c | 142 ++++++++++++--- drivers/gpu/drm/i915/intel_sprite.c | 2 +- 11 files changed, 509 insertions(+), 35 deletions(-) diff --git a/drivers/gpu/drm/i915/gvt/display.c b/drivers/gpu/drm/i915/gvt/display.c index 73a2dbbaf79d..b013d1be62db 100644 --- a/drivers/gpu/drm/i915/gvt/display.c +++ b/drivers/gpu/drm/i915/gvt/display.c @@ -485,6 +485,22 @@ static void intel_gvt_vblank_work(struct work_struct *w) ((((owner) >> (pipe) * BITS_PER_DOMAIN * MAX_SCALERS_PER_DOMAIN) >> \ BITS_PER_DOMAIN * (scaler)) & 0xf) +int bxt_check_planes(struct intel_vgpu *vgpu, int pipe) +{ + int plane = 0; + bool ret = false; + + for (plane = 0; + plane < ((INTEL_INFO(vgpu->gvt->dev_priv)->num_sprites[pipe]) + 1); + plane++) { + if (vgpu->gvt->pipe_info[pipe].plane_owner[plane] == vgpu->id) { + ret = true; + break; + } + } + return ret; +} + void intel_gvt_init_pipe_info(struct intel_gvt *gvt) { enum pipe pipe; @@ -528,6 +544,10 @@ int setup_virtual_monitors(struct intel_vgpu *vgpu) for_each_intel_connector_iter(connector, &conn_iter) { if (connector->encoder->get_hw_state(connector->encoder, &pipe) && connector->detect_edid) { + /* if no planes are allocated for this pipe, skip it */ + if (i915_modparams.avail_planes_per_pipe && + !bxt_check_planes(vgpu, pipe)) + continue; /* Get (Dom0) port associated with current pipe. */ port = enc_to_dig_port( &(connector->encoder->base))->base.port; diff --git a/drivers/gpu/drm/i915/gvt/gvt.c b/drivers/gpu/drm/i915/gvt/gvt.c index 308966edc04a..976bb6e223d2 100644 --- a/drivers/gpu/drm/i915/gvt/gvt.c +++ b/drivers/gpu/drm/i915/gvt/gvt.c @@ -389,6 +389,12 @@ void intel_gvt_clean_device(struct drm_i915_private *dev_priv) dev_priv->gvt = NULL; } +#define BITS_PER_DOMAIN 4 +#define MAX_PLANES_PER_DOMAIN 4 +#define DOMAIN_PLANE_OWNER(owner, pipe, plane) \ + ((((owner) >> (pipe) * BITS_PER_DOMAIN * MAX_PLANES_PER_DOMAIN) >> \ + BITS_PER_DOMAIN * (plane)) & 0xf) + /** * intel_gvt_init_device - initialize a GVT device * @dev_priv: drm i915 private data @@ -495,8 +501,28 @@ int intel_gvt_init_device(struct drm_i915_private *dev_priv) if (ret) gvt_err("debugfs registeration failed, go on.\n"); - gvt_dbg_core("gvt device initialization is done\n"); dev_priv->gvt = gvt; + + if (i915_modparams.avail_planes_per_pipe) { + unsigned long long domain_plane_owners; + int plane; + enum pipe pipe; + + /* + * Each nibble represents domain id + * ids can be from 0-F. 0 for Dom0, 1,2,3...0xF for DomUs + * plane_owner[i] holds the id of the domain that owns it,eg:0,1,2 etc + */ + domain_plane_owners = i915_modparams.domain_plane_owners; + for_each_pipe(dev_priv, pipe) { + for_each_universal_plane(dev_priv, pipe, plane) { + gvt->pipe_info[pipe].plane_owner[plane] = + DOMAIN_PLANE_OWNER(domain_plane_owners, pipe, plane); + } + } + } + + gvt_dbg_core("gvt device initialization is done\n"); return 0; out_clean_types: diff --git a/drivers/gpu/drm/i915/gvt/handlers.c b/drivers/gpu/drm/i915/gvt/handlers.c index 8fa3bed75d61..ec0886ac8d2b 100644 --- a/drivers/gpu/drm/i915/gvt/handlers.c +++ b/drivers/gpu/drm/i915/gvt/handlers.c @@ -3127,8 +3127,14 @@ static int init_skl_mmio_info(struct intel_gvt *gvt) MMIO_PLANES_DH(PLANE_AUX_DIST, D_SKL_PLUS, NULL, skl_plane_mmio_write); MMIO_PLANES_DH(PLANE_AUX_OFFSET, D_SKL_PLUS, NULL, skl_plane_mmio_write); - MMIO_PLANES_SDH(PLANE_WM_BASE, 4 * 8, D_SKL_PLUS, NULL, skl_plane_mmio_write); - MMIO_PLANES_DH(PLANE_WM_TRANS, D_SKL_PLUS, NULL, skl_plane_mmio_write); + if (i915_modparams.avail_planes_per_pipe) { + MMIO_PLANES_SDH(PLANE_WM_BASE, 4 * 8, D_SKL_PLUS, NULL, NULL); + MMIO_PLANES_DH(PLANE_WM_TRANS, D_SKL_PLUS, NULL, NULL); + } else { + MMIO_PLANES_SDH(PLANE_WM_BASE, 4 * 8, D_SKL_PLUS, NULL, skl_plane_mmio_write); + MMIO_PLANES_DH(PLANE_WM_TRANS, D_SKL_PLUS, NULL, skl_plane_mmio_write); + } + MMIO_PLANES_DH(PLANE_NV12_BUF_CFG, D_SKL_PLUS, NULL, pv_plane_wm_mmio_write); MMIO_PLANES_DH(PLANE_BUF_CFG, D_SKL_PLUS, NULL, NULL); diff --git a/drivers/gpu/drm/i915/i915_drv.c b/drivers/gpu/drm/i915/i915_drv.c index 06b786199215..8f7ed15458dc 100644 --- a/drivers/gpu/drm/i915/i915_drv.c +++ b/drivers/gpu/drm/i915/i915_drv.c @@ -1340,6 +1340,24 @@ static void i915_welcome_messages(struct drm_i915_private *dev_priv) DRM_INFO("DRM_I915_DEBUG_GEM enabled\n"); } +static inline int get_max_avail_pipes(struct drm_i915_private *dev_priv) +{ + enum pipe pipe; + int index = 0; + + if (!intel_vgpu_active(dev_priv) || + !i915_modparams.avail_planes_per_pipe) + return INTEL_INFO(dev_priv)->num_pipes; + + for_each_pipe(dev_priv, pipe) { + if (AVAIL_PLANE_PER_PIPE(dev_priv, i915_modparams.avail_planes_per_pipe, + pipe)) + index++; + } + + return index; +} + /** * i915_driver_load - setup chip and create an initial config * @pdev: PCI device @@ -1357,6 +1375,7 @@ int i915_driver_load(struct pci_dev *pdev, const struct pci_device_id *ent) (struct intel_device_info *)ent->driver_data; struct drm_i915_private *dev_priv; int ret; + int num_crtcs = 0; /* Enable nuclear pageflip on ILK+ */ if (!i915_modparams.nuclear_pageflip && match_info->gen < 5) @@ -1408,9 +1427,9 @@ int i915_driver_load(struct pci_dev *pdev, const struct pci_device_id *ent) * of the i915_driver_init_/i915_driver_register functions according * to the role/effect of the given init step. */ - if (INTEL_INFO(dev_priv)->num_pipes) { - ret = drm_vblank_init(&dev_priv->drm, - INTEL_INFO(dev_priv)->num_pipes); + num_crtcs = get_max_avail_pipes(dev_priv); + if (num_crtcs) { + ret = drm_vblank_init(&dev_priv->drm, num_crtcs); if (ret) goto out_cleanup_hw; } diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index e477f8a0764f..5d97f3b4e0a6 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -2672,6 +2672,11 @@ intel_info(const struct drm_i915_private *dev_priv) #define GT_FREQUENCY_MULTIPLIER 50 #define GEN9_FREQ_SCALER 3 +#define BITS_PER_PIPE 8 +#define AVAIL_PLANE_PER_PIPE(dev_priv, mask, pipe) \ + (((mask) >> (pipe) * BITS_PER_PIPE) & \ + ((1 << ((INTEL_INFO(dev_priv)->num_sprites[pipe]) + 1)) - 1)) + #include "i915_trace.h" static inline bool intel_vtd_active(void) diff --git a/drivers/gpu/drm/i915/i915_params.c b/drivers/gpu/drm/i915/i915_params.c index 062190f99a28..4ffdd533bddc 100644 --- a/drivers/gpu/drm/i915/i915_params.c +++ b/drivers/gpu/drm/i915/i915_params.c @@ -201,6 +201,56 @@ i915_param_named(enable_pvmmio, uint, 0400, "Enable pv mmio feature and set pvmmio level, default 1." "This parameter could only set from host, guest value is set through vgt_if"); +/* pipeA = BITS 0-3, pipeB = BITS 8-11, pipeC = BITS 16-18 + * +----------+-------+---------+--------+--------+--------+--------+ + * |unused |unused | Pipe C | unused | Pipe B | unused | Pipe A | + * +----------+-------+---------+--------+--------+--------+--------+ + * 31 23 18 15 11 7 3 0 + * + * + * BITS 0,1,2,3 - needs to be set planes assigned for pipes A and B + * and BITs 0,1,2 - for pipe C + * eg: avail_planes_per_pipe = 0x3 - pipe A=2(planes 1 and 2) , pipeB=0 and pipeC=0 planes + * eg: avail_planes_per_pipe = 0x5 - pipe A=2(planes 1 and 3) , pipeB=0 and pipeC=0 planes + * avail_planes_per_pipe = 0x030701 - pipe A =1(plane 1, pipeB=3(planes 1,2 and 3), pipeC=2( planes 1 and 2) + * + */ +i915_param_named_unsafe(avail_planes_per_pipe, uint, 0400, + "plane mask for each pipe: \ + set BITS 0-3:pipeA 8-11:pipeB 16-18:pipeC to specify the planes that \ + are available eg: 0x030701 : planes 1:pipeA 1,2,3:pipeB \ + 1,2:pipeC (0x0 - default value)"); + +/* pipeA = BITS 0-15 pipeB = 16-31, pipeC = 32-47 + * + * +----------+------------+-------------+------------+ + * |unused | Pipe C | Pipe B | Pipe A | + * +----------+------------+-------------+------------+ + * 63 47 31 15 0 + * + * Each nibble represents domain id. 0 for Dom0, 1,2,3...0xF for DomUs + * eg: domain_plane_owners = 0x022111000010 // 0x0221|1100|0010 + * plane domain + * plane_owner1A -0 + * plane_owner2A -1 + * plane_owner3A -0 + * plane_owner4A -0 + * plane_owner1B -0 + * plane_owner2B -0 + * plane_owner3B -1 + * plane_owner4B -1 + * plane_owner1C -1 + * plane_owner2C -2 + * plane_owner3C -2 + * + * + */ +i915_param_named_unsafe(domain_plane_owners, ullong, 0400, + "plane owners for each domain and for each pipe \ + ids can be from 0-F, eg: domain_plane_owners = 0x022111000010 \ + planes owner: 3C:2 2C:2 1C:1 4B:1 3B:1 2B:1 1B:0 4A:0 3A:0 2A:1 1A:0 \ + (0x0 - default value)"); + static __always_inline void _print_param(struct drm_printer *p, const char *name, const char *type, @@ -212,6 +262,8 @@ static __always_inline void _print_param(struct drm_printer *p, drm_printf(p, "i915.%s=%d\n", name, *(const int *)x); else if (!__builtin_strcmp(type, "unsigned int")) drm_printf(p, "i915.%s=%u\n", name, *(const unsigned int *)x); + else if (!__builtin_strcmp(type, "unsigned long long")) + drm_printf(p, "i915.%s=%llu\n", name, *(const unsigned long long *)x); else if (!__builtin_strcmp(type, "char *")) drm_printf(p, "i915.%s=%s\n", name, *(const char **)x); else diff --git a/drivers/gpu/drm/i915/i915_params.h b/drivers/gpu/drm/i915/i915_params.h index 40aee5b37645..6b0f98c37761 100644 --- a/drivers/gpu/drm/i915/i915_params.h +++ b/drivers/gpu/drm/i915/i915_params.h @@ -55,6 +55,8 @@ struct drm_printer; param(int, edp_vswing, 0) \ param(int, reset, 2) \ param(unsigned int, inject_load_failure, 0) \ + param(unsigned int, avail_planes_per_pipe, 0) \ + param(unsigned long long, domain_plane_owners, 0) \ /* leave bools at the end to not create holes */ \ param(bool, alpha_support, IS_ENABLED(CONFIG_DRM_I915_ALPHA_SUPPORT)) \ param(bool, enable_hangcheck, true) \ diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index a0d164244401..66ee465e4861 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -5362,15 +5362,37 @@ static void intel_pre_plane_update(struct intel_crtc_state *old_crtc_state, intel_update_watermarks(crtc); } +static void disable_primary_plane(struct drm_i915_private *dev_priv, int pipe) +{ + u32 val; + + val = I915_READ(PLANE_CTL(pipe, PLANE_PRIMARY)); + if (val & PLANE_CTL_ENABLE) { + I915_WRITE(PLANE_CTL(pipe, PLANE_PRIMARY), 0); + I915_WRITE(PLANE_SURF(pipe, PLANE_PRIMARY), 0); + POSTING_READ(PLANE_SURF(pipe, PLANE_PRIMARY)); + } +} + static void intel_crtc_disable_planes(struct drm_crtc *crtc, unsigned plane_mask) { struct drm_device *dev = crtc->dev; + struct drm_i915_private *dev_priv = to_i915(dev); struct intel_crtc *intel_crtc = to_intel_crtc(crtc); struct drm_plane *p; int pipe = intel_crtc->pipe; intel_crtc_dpms_overlay_disable(intel_crtc); + /* + * On BIOS based systems, if Dom0 doesn't own Plane 0 (Primary Plane), + * then during modeset, it wouldn't be able to disable this plane and + * this can lead to unexpected behavior after the modeset. Therefore, + * disable the primary plane if it was enabled by the BIOS/GOP. + */ + if (dev_priv->gvt && i915_modparams.avail_planes_per_pipe) + disable_primary_plane(dev_priv, pipe); + drm_for_each_plane_mask(p, dev, plane_mask) to_intel_plane(p)->disable_plane(to_intel_plane(p), intel_crtc); @@ -11634,7 +11656,8 @@ static void verify_wm_state(struct drm_crtc *crtc, const enum pipe pipe = intel_crtc->pipe; int plane, level, max_level = ilk_wm_max_level(dev_priv); - if (INTEL_GEN(dev_priv) < 9 || !new_state->active) + if (INTEL_GEN(dev_priv) < 9 || !new_state->active || + i915_modparams.avail_planes_per_pipe) return; skl_pipe_wm_get_hw_state(crtc, &hw_wm); @@ -13832,6 +13855,109 @@ intel_primary_plane_create(struct drm_i915_private *dev_priv, enum pipe pipe) return ERR_PTR(ret); } +static struct intel_plane * +intel_skl_plane_create(struct drm_i915_private *dev_priv, enum pipe pipe, + int plane, bool is_primary) +{ + struct intel_plane *intel_plane = NULL; + struct intel_plane_state *state = NULL; + unsigned long possible_crtcs; + const uint32_t *plane_formats; + unsigned int supported_rotations, plane_type; + unsigned int num_formats; + const uint64_t *modifiers; + int ret; + + intel_plane = kzalloc(sizeof(*intel_plane), GFP_KERNEL); + if (!intel_plane) { + ret = -ENOMEM; + goto fail; + } + + state = intel_create_plane_state(&intel_plane->base); + if (!state) { + ret = -ENOMEM; + goto fail; + } + + intel_plane->base.state = &state->base; + intel_plane->can_scale = false; + state->scaler_id = -1; + intel_plane->pipe = pipe; + + /* + * On gen2/3 only plane A can do FBC, but the panel fitter and LVDS + * port is hooked to pipe B. Hence we want plane A feeding pipe B. + */ + if (is_primary) { + intel_plane->i9xx_plane = (enum i9xx_plane_id) pipe; + intel_plane->check_plane = intel_check_primary_plane; + plane_type = DRM_PLANE_TYPE_PRIMARY; + } else { + intel_plane->i9xx_plane = (enum i9xx_plane_id) plane; + intel_plane->check_plane = intel_check_sprite_plane; + plane_type = DRM_PLANE_TYPE_OVERLAY; + } + + if (plane == PLANE_PRIMARY) { + intel_plane->frontbuffer_bit = INTEL_FRONTBUFFER(pipe, plane); + intel_plane->update_plane = skl_update_plane; + intel_plane->disable_plane = skl_disable_plane; + intel_plane->get_hw_state = skl_plane_get_hw_state; + } else { + intel_plane->frontbuffer_bit = INTEL_FRONTBUFFER(pipe, plane); + intel_plane->update_plane = skl_update_plane; + intel_plane->disable_plane = skl_disable_plane; + intel_plane->get_hw_state = skl_plane_get_hw_state; + } + + intel_plane->id = plane; + plane_formats = skl_primary_formats; + + if (pipe < PIPE_C) + modifiers = skl_format_modifiers_ccs; + else + modifiers = skl_format_modifiers_noccs; + + num_formats = ARRAY_SIZE(skl_primary_formats); + + /* + * Drop final format (NV12) for pipes or hardware steppings + * that don't support it. + */ + if (IS_BXT_REVID(dev_priv, 0, BXT_REVID_C0) || pipe >= PIPE_C + || plane >= 2) + num_formats--; + + + possible_crtcs = (1 << dev_priv->drm.mode_config.num_crtc); + ret = drm_universal_plane_init(&dev_priv->drm, &intel_plane->base, + possible_crtcs, &skl_plane_funcs, + plane_formats, num_formats, + modifiers, + plane_type, + "plane %d%c", plane+1, pipe_name(pipe)); + + if (ret) + goto fail; + + supported_rotations = DRM_MODE_ROTATE_0; + if (INTEL_GEN(dev_priv) >= 4) + drm_plane_create_rotation_property(&intel_plane->base, + DRM_MODE_ROTATE_0, + supported_rotations); + + drm_plane_helper_add(&intel_plane->base, &intel_plane_helper_funcs); + + return intel_plane; + +fail: + kfree(state); + kfree(intel_plane); + + return ERR_PTR(ret); +} + static struct intel_plane * intel_cursor_plane_create(struct drm_i915_private *dev_priv, enum pipe pipe) @@ -13941,6 +14067,79 @@ static void intel_crtc_init_scalers(struct intel_crtc *crtc, scaler_state->scaler_id = -1; } +static int intel_crtc_init_restrict_planes(struct drm_i915_private *dev_priv, + enum pipe pipe, int planes_mask) +{ + struct intel_crtc *intel_crtc; + struct intel_crtc_state *crtc_state; + struct intel_plane *primary = NULL, *intel_plane = NULL; + bool is_primary = true; + int plane, ret, crtc_plane; + + intel_crtc = kzalloc(sizeof(*intel_crtc), GFP_KERNEL); + if (!intel_crtc) + return -ENOMEM; + + crtc_state = kzalloc(sizeof(*crtc_state), GFP_KERNEL); + if (!crtc_state) { + ret = -ENOMEM; + goto fail; + } + intel_crtc->config = crtc_state; + intel_crtc->base.state = &crtc_state->base; + crtc_state->base.crtc = &intel_crtc->base; + + for_each_universal_plane(dev_priv, pipe, plane) { + if (planes_mask & BIT(plane)) { + intel_plane = intel_skl_plane_create(dev_priv, + pipe, plane, is_primary); + if (IS_ERR(intel_plane)) { + DRM_DEBUG_KMS(" plane %d failed for pipe %d\n", plane, pipe); + ret = PTR_ERR(intel_plane); + goto fail; + } + if (is_primary) { + primary = intel_plane; + is_primary = false; + } + DRM_DEBUG_KMS(" plane %d created for pipe %d\n", plane, pipe); + intel_crtc->plane_ids_mask |= BIT(intel_plane->id); + } + } + + ret = drm_crtc_init_with_planes(&dev_priv->drm, + &intel_crtc->base, + primary ? &primary->base : NULL, NULL, + &intel_crtc_funcs, + "pipe %c", pipe_name(pipe)); + if (ret) + goto fail; + + intel_crtc->pipe = pipe; + crtc_plane = primary ? primary->i9xx_plane : 0; + + dev_priv->plane_to_crtc_mapping[crtc_plane] = intel_crtc; + dev_priv->pipe_to_crtc_mapping[intel_crtc->pipe] = intel_crtc; + + drm_crtc_helper_add(&intel_crtc->base, &intel_helper_funcs); + + intel_color_init(&intel_crtc->base); + + WARN_ON(drm_crtc_index(&intel_crtc->base) != intel_crtc->pipe); + + return 0; + +fail: + /* + * drm_mode_config_cleanup() will free up any + * crtcs/planes already initialized. + */ + kfree(crtc_state); + kfree(intel_crtc); + + return ret; +} + static int intel_crtc_init(struct drm_i915_private *dev_priv, enum pipe pipe) { struct intel_crtc *intel_crtc; @@ -15188,12 +15387,26 @@ static void intel_update_fdi_pll_freq(struct drm_i915_private *dev_priv) DRM_DEBUG_DRIVER("FDI PLL freq=%d\n", dev_priv->fdi_pll_freq); } + +static int intel_sanitize_plane_restriction(struct drm_i915_private *dev_priv) +{ + /*plane restriction feature is only for APL for now*/ + if (!IS_BROXTON(dev_priv)) { + i915_modparams.avail_planes_per_pipe = 0; + DRM_INFO("Turning off Plane Restrictions feature\n"); + } + + return i915_modparams.avail_planes_per_pipe; +} + int intel_modeset_init(struct drm_device *dev) { struct drm_i915_private *dev_priv = to_i915(dev); struct i915_ggtt *ggtt = &dev_priv->ggtt; enum pipe pipe; struct intel_crtc *crtc; + unsigned int planes_mask[I915_MAX_PIPES]; + unsigned int avail_plane_per_pipe_mask = 0; dev_priv->modeset_wq = alloc_ordered_workqueue("i915_modeset", 0); @@ -15267,10 +15480,29 @@ int intel_modeset_init(struct drm_device *dev) INTEL_INFO(dev_priv)->num_pipes, INTEL_INFO(dev_priv)->num_pipes > 1 ? "s" : ""); + avail_plane_per_pipe_mask = intel_sanitize_plane_restriction(dev_priv); + DRM_DEBUG_KMS("avail_planes_per_pipe = 0x%x \n", i915_modparams.avail_planes_per_pipe); + DRM_DEBUG_KMS("domain_plane_owners = 0x%llx \n", i915_modparams.domain_plane_owners); + + for_each_pipe(dev_priv, pipe) { + planes_mask[pipe] = AVAIL_PLANE_PER_PIPE(dev_priv, + avail_plane_per_pipe_mask, pipe); + DRM_DEBUG_KMS("for pipe %d plane_mask = %d \n", pipe, planes_mask[pipe]); + } + for_each_pipe(dev_priv, pipe) { int ret; - ret = intel_crtc_init(dev_priv, pipe); + if (!i915_modparams.avail_planes_per_pipe) { + ret = intel_crtc_init(dev_priv, pipe); + } else { + if (!intel_vgpu_active(dev_priv) || (intel_vgpu_active(dev_priv) + && planes_mask[pipe])) { + ret = intel_crtc_init_restrict_planes(dev_priv, + pipe, + planes_mask[pipe]); + } + } if (ret) { drm_mode_config_cleanup(dev); return ret; @@ -15705,10 +15937,15 @@ static void intel_modeset_readout_hw_state(struct drm_device *dev) struct intel_crtc_state *crtc_state; crtc = intel_get_crtc_for_pipe(dev_priv, pipe); - crtc_state = to_intel_crtc_state(crtc->base.state); + if (!crtc) { + encoder->base.crtc = NULL; + } else { + crtc_state = to_intel_crtc_state(crtc->base.state); - encoder->base.crtc = &crtc->base; - encoder->get_config(encoder, crtc_state); + encoder->base.crtc = &crtc->base; + encoder->get_config(encoder, crtc_state); + + } } else { encoder->base.crtc = NULL; } @@ -15867,9 +16104,11 @@ intel_modeset_setup_hw_state(struct drm_device *dev, for_each_pipe(dev_priv, pipe) { crtc = intel_get_crtc_for_pipe(dev_priv, pipe); - intel_sanitize_crtc(crtc, ctx); - intel_dump_pipe_config(crtc, crtc->config, - "[setup_hw_state]"); + if (crtc) { + intel_sanitize_crtc(crtc, ctx); + intel_dump_pipe_config(crtc, crtc->config, + "[setup_hw_state]"); + } } intel_modeset_update_connector_atomic_state(dev); diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h index 49d4282b234b..c8290474f0ad 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h @@ -2114,6 +2114,9 @@ int intel_sprite_set_colorkey_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv); void intel_pipe_update_start(const struct intel_crtc_state *new_crtc_state); void intel_pipe_update_end(struct intel_crtc_state *new_crtc_state); +int intel_check_sprite_plane(struct intel_plane *plane, + struct intel_crtc_state *crtc_state, + struct intel_plane_state *state); void skl_update_plane(struct intel_plane *plane, const struct intel_crtc_state *crtc_state, const struct intel_plane_state *plane_state); diff --git a/drivers/gpu/drm/i915/intel_pm.c b/drivers/gpu/drm/i915/intel_pm.c index 1884b8140131..276a46f26a4b 100644 --- a/drivers/gpu/drm/i915/intel_pm.c +++ b/drivers/gpu/drm/i915/intel_pm.c @@ -807,11 +807,14 @@ static int intel_wm_num_levels(struct drm_i915_private *dev_priv) static bool intel_wm_plane_visible(const struct intel_crtc_state *crtc_state, const struct intel_plane_state *plane_state) { - struct intel_plane *plane = to_intel_plane(plane_state->base.plane); + struct intel_plane *plane = plane_state ? to_intel_plane(plane_state->base.plane) : NULL; /* FIXME check the 'enable' instead */ if (!crtc_state->base.active) return false; + if (!plane_state && i915_modparams.avail_planes_per_pipe) { + return true; + } /* * Treat cursor with fb as always visible since cursor updates @@ -3973,11 +3976,15 @@ static uint_fixed_16_16_t skl_plane_downscale_amount(const struct intel_crtc_state *cstate, const struct intel_plane_state *pstate) { - struct intel_plane *plane = to_intel_plane(pstate->base.plane); + struct intel_plane *plane = pstate ? to_intel_plane(pstate->base.plane) : NULL; uint32_t src_w, src_h, dst_w, dst_h; uint_fixed_16_16_t fp_w_ratio, fp_h_ratio; uint_fixed_16_16_t downscale_h, downscale_w; + if (!pstate && i915_modparams.avail_planes_per_pipe) { + return mul_fixed16(u32_to_fixed16(1), u32_to_fixed16(1)); + } + if (WARN_ON(!intel_wm_plane_visible(cstate, pstate))) return u32_to_fixed16(0); @@ -4488,9 +4495,9 @@ skl_compute_plane_wm_params(const struct drm_i915_private *dev_priv, const struct intel_plane_state *intel_pstate, struct skl_wm_params *wp, int plane_id) { - struct intel_plane *plane = to_intel_plane(intel_pstate->base.plane); - const struct drm_plane_state *pstate = &intel_pstate->base; - const struct drm_framebuffer *fb = pstate->fb; + struct intel_plane *plane = intel_pstate ? to_intel_plane(intel_pstate->base.plane) : NULL; + const struct drm_plane_state *pstate = intel_pstate ? &intel_pstate->base : NULL; + const struct drm_framebuffer *fb = pstate ? pstate->fb : NULL; uint32_t interm_pbpl; struct intel_atomic_state *state = to_intel_atomic_state(cstate->base.state); @@ -4505,6 +4512,19 @@ skl_compute_plane_wm_params(const struct drm_i915_private *dev_priv, return -EINVAL; } + if (!intel_pstate && i915_modparams.avail_planes_per_pipe) { + wp->y_tiled = false; + wp->x_tiled = true; + wp->cpp = 4; + wp->y_min_scanlines = 8; + wp->rc_surface = fb ? fb->modifier == I915_FORMAT_MOD_Y_TILED_CCS || + fb->modifier == I915_FORMAT_MOD_Yf_TILED_CCS : 0; + wp->is_planar = fb ? fb->format->format == DRM_FORMAT_NV12 : 0; + wp->width = cstate->pipe_src_w; + wp->dbuf_block_size = 512; + goto calculate_wm; + } + wp->y_tiled = fb->modifier == I915_FORMAT_MOD_Y_TILED || fb->modifier == I915_FORMAT_MOD_Yf_TILED || fb->modifier == I915_FORMAT_MOD_Y_TILED_CCS || @@ -4529,8 +4549,6 @@ skl_compute_plane_wm_params(const struct drm_i915_private *dev_priv, wp->width /= 2; wp->cpp = fb->format->cpp[plane_id]; - wp->plane_pixel_rate = skl_adjusted_plane_pixel_rate(cstate, - intel_pstate); if (INTEL_GEN(dev_priv) >= 11 && fb->modifier == I915_FORMAT_MOD_Yf_TILED && wp->cpp == 8) @@ -4561,6 +4579,9 @@ skl_compute_plane_wm_params(const struct drm_i915_private *dev_priv, if (apply_memory_bw_wa) wp->y_min_scanlines *= 2; +calculate_wm: + wp->plane_pixel_rate = skl_adjusted_plane_pixel_rate(cstate, + intel_pstate); wp->plane_bytes_per_line = wp->width * wp->cpp; if (wp->y_tiled) { interm_pbpl = DIV_ROUND_UP(wp->plane_bytes_per_line * @@ -4599,7 +4620,8 @@ static int skl_compute_plane_wm(const struct drm_i915_private *dev_priv, const struct skl_wm_level *result_prev, struct skl_wm_level *result /* out */) { - const struct drm_plane_state *pstate = &intel_pstate->base; + const struct drm_plane_state *pstate = intel_pstate ? &intel_pstate->base : NULL; + const struct drm_framebuffer *fb = pstate ? pstate->fb : NULL; uint32_t latency = dev_priv->wm.skl_latency[level]; uint_fixed_16_16_t method1, method2; uint_fixed_16_16_t selected_result; @@ -4652,7 +4674,7 @@ static int skl_compute_plane_wm(const struct drm_i915_private *dev_priv, wp->plane_blocks_per_line); /* Display WA #1125: skl,bxt,kbl,glk */ - if (level == 0 && wp->rc_surface) + if (fb && level == 0 && wp->rc_surface) res_blocks += fixed16_to_u32_round_up(wp->y_tile_minimum); /* Display WA #1126: skl,bxt,kbl,glk */ @@ -4710,12 +4732,16 @@ static int skl_compute_plane_wm(const struct drm_i915_private *dev_priv, if (level) { return 0; } else { - struct drm_plane *plane = pstate->plane; + struct drm_plane *plane = pstate ? pstate->plane : NULL; DRM_DEBUG_KMS("Requested display configuration exceeds system watermark limitations\n"); - DRM_DEBUG_KMS("[PLANE:%d:%s] blocks required = %u/%u, lines required = %u/31\n", + + if (plane) { + DRM_DEBUG_KMS("[PLANE:%d:%s] blocks required = %u/%u, lines required = %u/31\n", plane->base.id, plane->name, res_blocks, ddb_allocation, res_lines); + } + return -EINVAL; } } @@ -4746,18 +4772,16 @@ skl_compute_wm_levels(const struct drm_i915_private *dev_priv, const struct intel_plane_state *intel_pstate, const struct skl_wm_params *wm_params, struct skl_plane_wm *wm, - int plane_id) + int plane_id, + enum plane_id intel_plane_id) { struct intel_crtc *intel_crtc = to_intel_crtc(cstate->base.crtc); - struct drm_plane *plane = intel_pstate->base.plane; - struct intel_plane *intel_plane = to_intel_plane(plane); uint16_t ddb_blocks; enum pipe pipe = intel_crtc->pipe; int level, max_level = ilk_wm_max_level(dev_priv); - enum plane_id intel_plane_id = intel_plane->id; int ret; - if (WARN_ON(!intel_pstate->base.fb)) + if (WARN_ON(intel_pstate && !intel_pstate->base.fb)) return -EINVAL; ddb_blocks = plane_id ? @@ -4787,7 +4811,8 @@ skl_compute_wm_levels(const struct drm_i915_private *dev_priv, return ret; } - if (intel_pstate->base.fb->format->format == DRM_FORMAT_NV12) + if (intel_pstate && + intel_pstate->base.fb->format->format == DRM_FORMAT_NV12) wm->is_planar = true; return 0; @@ -4871,6 +4896,64 @@ static void skl_compute_transition_wm(struct intel_crtc_state *cstate, trans_wm->plane_en = false; } +static int skl_build_pipe_all_plane_wm(struct intel_crtc_state *cstate, + struct skl_ddb_allocation *ddb, + struct skl_pipe_wm *pipe_wm) +{ + struct drm_device *dev = cstate->base.crtc->dev; + const struct drm_i915_private *dev_priv = to_i915(dev); + struct skl_plane_wm *wm; + struct intel_crtc *crtc = to_intel_crtc(cstate->base.crtc); + struct drm_crtc_state *crtc_state = &cstate->base; + struct drm_plane *plane; + const struct drm_plane_state *pstate; + struct intel_plane_state *intel_pstate; + int pipe = crtc->pipe; + int plane_id; + int ret; + + memset(pipe_wm->planes, 0, sizeof(pipe_wm->planes)); + + /* + * Since Dom0 may not own all planes on this pipe, there will + * not be a valid intel_plane for the planes it doesn't own. + * Therefore, we have to pass NULL to skl_compute_wm_level() + * which will then know that this plane is not owned by Dom0 + * and hence will use width and height from the crtc and will + * also assume cpp = 4 and tiling = x_tiled. + */ + for_each_universal_plane(dev_priv, pipe, plane_id) { + struct skl_wm_params wm_params; + uint16_t ddb_blocks; + intel_pstate = NULL; + + drm_atomic_crtc_state_for_each_plane_state(plane, pstate, crtc_state) { + if (plane_id == to_intel_plane(plane)->id) { + intel_pstate = to_intel_plane_state(pstate); + break; + } + } + + wm = &pipe_wm->planes[plane_id]; + ddb_blocks = skl_ddb_entry_size(&ddb->plane[pipe][plane_id]); + ret = skl_compute_plane_wm_params(dev_priv, cstate, + intel_pstate, &wm_params, 0); + if (ret) + return ret; + + ret = skl_compute_wm_levels(dev_priv, ddb, cstate, + intel_pstate, &wm_params, wm, 0, plane_id); + if (ret) + return ret; + + skl_compute_transition_wm(cstate, &wm_params, &wm->wm[0], + ddb_blocks, &wm->trans_wm); + } + pipe_wm->linetime = skl_compute_linetime_wm(cstate); + + return 0; +} + static int skl_build_pipe_wm(struct intel_crtc_state *cstate, struct skl_ddb_allocation *ddb, struct skl_pipe_wm *pipe_wm) @@ -4906,7 +4989,7 @@ static int skl_build_pipe_wm(struct intel_crtc_state *cstate, return ret; ret = skl_compute_wm_levels(dev_priv, ddb, cstate, - intel_pstate, &wm_params, wm, 0); + intel_pstate, &wm_params, wm, 0, plane_id); if (ret) return ret; @@ -4926,7 +5009,7 @@ static int skl_build_pipe_wm(struct intel_crtc_state *cstate, ret = skl_compute_wm_levels(dev_priv, ddb, cstate, intel_pstate, &wm_params, - wm, 1); + wm, 1, plane_id); if (ret) return ret; } @@ -5143,7 +5226,10 @@ static int skl_update_pipe_wm(struct drm_crtc_state *cstate, struct intel_crtc_state *intel_cstate = to_intel_crtc_state(cstate); int ret; - ret = skl_build_pipe_wm(intel_cstate, ddb, pipe_wm); + if (i915_modparams.avail_planes_per_pipe) + ret = skl_build_pipe_all_plane_wm(intel_cstate, ddb, pipe_wm); + else + ret = skl_build_pipe_wm(intel_cstate, ddb, pipe_wm); if (ret) return ret; @@ -5387,10 +5473,14 @@ skl_compute_wm(struct drm_atomic_state *state) struct drm_crtc_state *cstate; struct intel_atomic_state *intel_state = to_intel_atomic_state(state); struct skl_ddb_values *results = &intel_state->wm_results; + struct drm_i915_private *dev_priv = to_i915(intel_state->base.dev); struct skl_pipe_wm *pipe_wm; bool changed = false; int ret, i; + if (intel_vgpu_active(dev_priv) && i915_modparams.avail_planes_per_pipe) + return 0; + /* Clear all dirty flags */ results->dirty_pipes = 0; @@ -5449,11 +5539,23 @@ static void skl_atomic_update_crtc_wm(struct intel_atomic_state *state, enum pipe pipe = crtc->pipe; enum plane_id plane_id; + if (intel_vgpu_active(dev_priv) && i915_modparams.avail_planes_per_pipe) + return; + if (!(state->wm_results.dirty_pipes & drm_crtc_mask(&crtc->base))) return; I915_WRITE(PIPE_WM_LINETIME(pipe), pipe_wm->linetime); + if (i915_modparams.avail_planes_per_pipe) { + for_each_universal_plane(dev_priv, pipe, plane_id) { + skl_write_plane_wm(crtc, &pipe_wm->planes[plane_id], + ddb, plane_id); + } + + return; + } + for_each_plane_id_on_crtc(crtc, plane_id) { #if IS_ENABLED(CONFIG_DRM_I915_GVT) if (dev_priv->gvt && diff --git a/drivers/gpu/drm/i915/intel_sprite.c b/drivers/gpu/drm/i915/intel_sprite.c index 2d9cff34018c..e71dd44c64ab 100644 --- a/drivers/gpu/drm/i915/intel_sprite.c +++ b/drivers/gpu/drm/i915/intel_sprite.c @@ -1039,7 +1039,7 @@ g4x_plane_get_hw_state(struct intel_plane *plane, return ret; } -static int +int intel_check_sprite_plane(struct intel_plane *plane, struct intel_crtc_state *crtc_state, struct intel_plane_state *state) From 57e09f4a67f6da4fcb812001ae9fab9e290772ef Mon Sep 17 00:00:00 2001 From: Fei Jiang Date: Tue, 26 Jun 2018 08:49:50 +0800 Subject: [PATCH 0966/1103] drm/i915/gvt: make KBL also support plane restriction feature previously plane restriction feature was for APL only, we need extend this feature to KBL also. Change-Id: I192ad3419b96bc34702189fc9c6a3b463ae50061 Signed-off-by: Fei Jiang Reviewed-by: Min He --- drivers/gpu/drm/i915/intel_display.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 66ee465e4861..25751dd7b5d8 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -15390,8 +15390,8 @@ static void intel_update_fdi_pll_freq(struct drm_i915_private *dev_priv) static int intel_sanitize_plane_restriction(struct drm_i915_private *dev_priv) { - /*plane restriction feature is only for APL for now*/ - if (!IS_BROXTON(dev_priv)) { + /*plane restriction feature is only for APL and KBL for now*/ + if (!(IS_BROXTON(dev_priv) || IS_KABYLAKE(dev_priv))) { i915_modparams.avail_planes_per_pipe = 0; DRM_INFO("Turning off Plane Restrictions feature\n"); } From 9b003336ec7d35771219a2dba7ced59bd5c284e4 Mon Sep 17 00:00:00 2001 From: Zhao Yakui Date: Thu, 26 Jul 2018 15:17:00 +0800 Subject: [PATCH 0967/1103] kernel/drm/i915: Check the plane_state->fb to avoid Null pointer After the boot option of "enable_initial_modeset=1" is added for gvt-g, the function of disable_planes will assign the Null fb for each plane. In such case it is possible that the plane_state->fb is NULL when calling intel_can_enable_sagv. Then it will trigger the kernel panic. This is to fix the kernel panic on KBL-NUC. BUG: unable to handle kernel NULL pointer dereference at 0000000000000068 IP: intel_can_enable_sagv+0x1a7/0x1c0 PGD 0 P4D 0 Oops: 0000 [#1] PREEMPT SMP Modules linked in: CPU: 0 PID: 111 Comm: kworker/0:2 Tainted: G U 4.14.55-65.pk414-sos #1 Hardware name: Dell Inc. OptiPlex 7050/062KRH, BIOS 1.5.2 06/19/2017 Workqueue: events modeset_config_fn task: ffff9c020a36a100 task.stack: ffff9c0205ac4000 RIP: 0010:intel_can_enable_sagv+0x1a7/0x1c0 RSP: 0000:ffff9c0205ac7bd8 EFLAGS: 00010246 RAX: 0000000000000000 RBX: 000000000000001e RCX: ffff9c020a388400 RDX: 0000000000000063 RSI: 0000000000000000 RDI: ffff9c02056f5000 RBP: ffff9c0205ac7bf0 R08: ffff9c020a398498 R09: 0000000000000000 R10: 0100000000000001 R11: 000000000000002a R12: ffff9c020576ec00 R13: ffff9c020a398000 R14: ffff9c0205770000 R15: 0000000000000003 FS: 0000000000000000(0000) GS:ffff9c021dc00000(0000) knlGS:0000000000000000 CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 CR2: 0000000000000068 CR3: 0000000153813000 CR4: 00000000003406f0 DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000 DR3: 0000000000000000 DR6: 00000000fffe0ff0 DR7: 0000000000000400 Call Trace: intel_atomic_commit_tail+0x9fe/0xfc0 ? __i915_sw_fence_complete+0x127/0x1c0 intel_atomic_commit+0x191/0x240 drm_atomic_commit+0x51/0x60 modeset_config_fn+0x4fb/0xc20 ? __switch_to_asm+0x30/0x60 process_one_work+0x192/0x3a0 worker_thread+0x41/0x3b0 kthread+0x132/0x150 ? wq_sysfs_prep_attrs+0x50/0x50 ? kthread_create_on_node+0x40/0x40 ret_from_fork+0x3a/0x50 Code: e8 07 00 00 17 74 19 39 da 0f 8d 3b ff ff ff e9 b6 fe ff ff 5b b8 01 00 00 00 41 5c 41 5d 5d c3 48 8b 81 80 02 00 00 48 8b 40 10 <4c> 39 50 68 75 d6 83 c2 0f eb d1 66 66 2e 0f 1f 84 RIP: intel_can_enable_sagv+0x1a7/0x1c0 RSP: ffff9c0205ac7bd8 Signed-off-by: Zhao Yakui Reviewed-by: Jiang, Fei --- drivers/gpu/drm/i915/intel_pm.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/gpu/drm/i915/intel_pm.c b/drivers/gpu/drm/i915/intel_pm.c index 276a46f26a4b..27554caeca6b 100644 --- a/drivers/gpu/drm/i915/intel_pm.c +++ b/drivers/gpu/drm/i915/intel_pm.c @@ -3763,6 +3763,7 @@ bool intel_can_enable_sagv(struct drm_atomic_state *state) latency = dev_priv->wm.skl_latency[level]; if (skl_needs_memory_bw_wa(intel_state) && + plane->base.state->fb && plane->base.state->fb->modifier == I915_FORMAT_MOD_X_TILED) latency += 15; From 9cba3f632844203f7923bb30a2149802de8f7c52 Mon Sep 17 00:00:00 2001 From: Min He Date: Tue, 14 Aug 2018 02:24:22 +0000 Subject: [PATCH 0968/1103] drm/i915: fix a kernel panic issue of plane restriction When plane restriction feature enabled in sevice os, there could be the case that there're some CRTC's without a primary plane. If we don't assign any plane of pipe A to sos, like i915.avail_planes_per_pipe =0x000F00, it will cause kernel panic when booting because it assumes primary plane existing in intel_find_initial_plane_obj(). Added a check to the primary plane in CRTC to avoid such kind of issue. Signed-off-by: Min He Reviewed-by: Xinyun Liu Reviewed-by: Zhao Yakui Tracked-on: https://github.com/projectacrn/acrn-hypervisor/issues/1155 --- drivers/gpu/drm/i915/intel_display.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 25751dd7b5d8..a5494fe26fe1 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -15529,7 +15529,7 @@ int intel_modeset_init(struct drm_device *dev) for_each_intel_crtc(dev, crtc) { struct intel_initial_plane_config plane_config = {}; - if (!crtc->active) + if (!crtc->active || !crtc->base.primary) continue; /* From 656255942c3e4e7414a862956e96db11126ef48a Mon Sep 17 00:00:00 2001 From: Xinyun Liu Date: Fri, 17 Aug 2018 17:54:24 +0800 Subject: [PATCH 0969/1103] drm/i915/gvt: ensure each pipe has a plane in Host OS This is a workaround patch to fix black screen issue and pass plane restriction tests. Weston 4.0 won't enable the CRTCs which doesn't have a primary plane. So explicitly check `avail_planes_per_pipe` to make sure each pipe has a primary plane to make weston happy. Note: When GVT-g enabled with plane restriction feature, User App changed mode in host OS will make chaos for guest OS. v3: remove unnecessary check and fix style per Min's advice v2: improve the check logic per Fei's advice v1: force enable a plane for each CRTC Tracked-On: projectacrn/acrn-hypervisor#1131 Signed-off-by: Xinyun Liu Reviewed-by: Fei Jiang Reviewed-by: Min He --- drivers/gpu/drm/i915/intel_display.c | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index a5494fe26fe1..0eee2a5926fb 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -15390,13 +15390,29 @@ static void intel_update_fdi_pll_freq(struct drm_i915_private *dev_priv) static int intel_sanitize_plane_restriction(struct drm_i915_private *dev_priv) { + unsigned int mask; + /*plane restriction feature is only for APL and KBL for now*/ if (!(IS_BROXTON(dev_priv) || IS_KABYLAKE(dev_priv))) { i915_modparams.avail_planes_per_pipe = 0; DRM_INFO("Turning off Plane Restrictions feature\n"); } - return i915_modparams.avail_planes_per_pipe; + mask = i915_modparams.avail_planes_per_pipe; + + /* make sure SOS has a (dummy) plane per pipe. */ + if ((IS_BROXTON(dev_priv) || IS_KABYLAKE(dev_priv)) && + intel_gvt_active(dev_priv)) { + enum pipe pipe; + + for_each_pipe(dev_priv, pipe) { + if (!AVAIL_PLANE_PER_PIPE(dev_priv, mask, pipe)) + mask |= (1 << pipe * BITS_PER_PIPE); + } + DRM_INFO("Fix internal plane mask: 0x%06x --> 0x%06x", + i915_modparams.avail_planes_per_pipe, mask); + } + return mask; } int intel_modeset_init(struct drm_device *dev) From 8c4c2f02522932cd0ea8f6bd59e4d25a708c565c Mon Sep 17 00:00:00 2001 From: Min He Date: Fri, 7 Sep 2018 15:15:38 +0800 Subject: [PATCH 0970/1103] drm/i915: to limit the supported modifiers for plane restriction In GVT-g environment, to ensure all the OS's have enough DDB to display, GVT-g will statically allocate all the DDBs for all the planes on all the pipes. However, when SOS or UOS wants to use Y/Yf tiled modifier, the watermark required will exceed the DDBs allocated by GVT-g, thus causes some display issues. So in this patch, we removed the supports of the Y/Yf tiled modifiers for both SOS and UOS when plane restriction is enabled. And a consequence is that the RBC will be disabled since _CCS modifiers will no longer be supported. Tracked-on: https://github.com/projectacrn/acrn-hypervisor/issues/1193 Signed-off-by: Min He Reviewed-by: Zhao Yakui Reviewed-by: Fei Jiang --- drivers/gpu/drm/i915/intel_display.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 0eee2a5926fb..8560df16fd95 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -13914,10 +13914,7 @@ intel_skl_plane_create(struct drm_i915_private *dev_priv, enum pipe pipe, intel_plane->id = plane; plane_formats = skl_primary_formats; - if (pipe < PIPE_C) - modifiers = skl_format_modifiers_ccs; - else - modifiers = skl_format_modifiers_noccs; + modifiers = i9xx_format_modifiers; num_formats = ARRAY_SIZE(skl_primary_formats); From 44710b5206a0fcc1595b5b3c3a88cbd1f5e9f711 Mon Sep 17 00:00:00 2001 From: Satyeshwar Singh Date: Fri, 5 Oct 2018 13:52:11 -0700 Subject: [PATCH 0971/1103] drm/i915: Optimize watermark calculation for plane restrictions The same code was being used in skl_build_plane_wm and skl_build_pipe_all_plane_wm which was redundant. Stripped out this code into a common function. Signed-off-by: Satyeshwar Singh --- drivers/gpu/drm/i915/intel_pm.c | 106 +++++++++++++++++--------------- 1 file changed, 55 insertions(+), 51 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_pm.c b/drivers/gpu/drm/i915/intel_pm.c index 27554caeca6b..07a8f8dc4935 100644 --- a/drivers/gpu/drm/i915/intel_pm.c +++ b/drivers/gpu/drm/i915/intel_pm.c @@ -4897,13 +4897,63 @@ static void skl_compute_transition_wm(struct intel_crtc_state *cstate, trans_wm->plane_en = false; } +static int skl_build_plane_wm(struct intel_crtc_state *cstate, + struct skl_ddb_allocation *ddb, + struct skl_pipe_wm *pipe_wm, + int pipe, + enum plane_id plane_id, + struct intel_plane_state *intel_pstate) +{ + struct drm_device *dev = cstate->base.crtc->dev; + const struct drm_i915_private *dev_priv = to_i915(dev); + struct skl_plane_wm *wm; + struct skl_wm_params wm_params; + uint16_t ddb_blocks; + int ret; + + wm = &pipe_wm->planes[plane_id]; + ddb_blocks = skl_ddb_entry_size(&ddb->plane[pipe][plane_id]); + + ret = skl_compute_plane_wm_params(dev_priv, cstate, + intel_pstate, &wm_params, 0); + if (ret) + return ret; + + ret = skl_compute_wm_levels(dev_priv, ddb, cstate, + intel_pstate, &wm_params, wm, 0, plane_id); + if (ret) + return ret; + + skl_compute_transition_wm(cstate, &wm_params, &wm->wm[0], + ddb_blocks, &wm->trans_wm); + + /* uv plane watermarks must also be validated for NV12/Planar */ + if (wm_params.is_planar) { + memset(&wm_params, 0, sizeof(struct skl_wm_params)); + wm->is_planar = true; + + ret = skl_compute_plane_wm_params(dev_priv, cstate, + intel_pstate, + &wm_params, 1); + if (ret) + return ret; + + ret = skl_compute_wm_levels(dev_priv, ddb, cstate, + intel_pstate, &wm_params, + wm, 1, plane_id); + if (ret) + return ret; + } + + return 0; +} + static int skl_build_pipe_all_plane_wm(struct intel_crtc_state *cstate, struct skl_ddb_allocation *ddb, struct skl_pipe_wm *pipe_wm) { struct drm_device *dev = cstate->base.crtc->dev; const struct drm_i915_private *dev_priv = to_i915(dev); - struct skl_plane_wm *wm; struct intel_crtc *crtc = to_intel_crtc(cstate->base.crtc); struct drm_crtc_state *crtc_state = &cstate->base; struct drm_plane *plane; @@ -4924,8 +4974,6 @@ static int skl_build_pipe_all_plane_wm(struct intel_crtc_state *cstate, * also assume cpp = 4 and tiling = x_tiled. */ for_each_universal_plane(dev_priv, pipe, plane_id) { - struct skl_wm_params wm_params; - uint16_t ddb_blocks; intel_pstate = NULL; drm_atomic_crtc_state_for_each_plane_state(plane, pstate, crtc_state) { @@ -4935,20 +4983,10 @@ static int skl_build_pipe_all_plane_wm(struct intel_crtc_state *cstate, } } - wm = &pipe_wm->planes[plane_id]; - ddb_blocks = skl_ddb_entry_size(&ddb->plane[pipe][plane_id]); - ret = skl_compute_plane_wm_params(dev_priv, cstate, - intel_pstate, &wm_params, 0); - if (ret) - return ret; - - ret = skl_compute_wm_levels(dev_priv, ddb, cstate, - intel_pstate, &wm_params, wm, 0, plane_id); + ret = skl_build_plane_wm(cstate, ddb, pipe_wm, + pipe, plane_id, (struct intel_plane_state *) intel_pstate); if (ret) return ret; - - skl_compute_transition_wm(cstate, &wm_params, &wm->wm[0], - ddb_blocks, &wm->trans_wm); } pipe_wm->linetime = skl_compute_linetime_wm(cstate); @@ -4959,12 +4997,9 @@ static int skl_build_pipe_wm(struct intel_crtc_state *cstate, struct skl_ddb_allocation *ddb, struct skl_pipe_wm *pipe_wm) { - struct drm_device *dev = cstate->base.crtc->dev; struct drm_crtc_state *crtc_state = &cstate->base; - const struct drm_i915_private *dev_priv = to_i915(dev); struct drm_plane *plane; const struct drm_plane_state *pstate; - struct skl_plane_wm *wm; int ret; /* @@ -4977,43 +5012,12 @@ static int skl_build_pipe_wm(struct intel_crtc_state *cstate, const struct intel_plane_state *intel_pstate = to_intel_plane_state(pstate); enum plane_id plane_id = to_intel_plane(plane)->id; - struct skl_wm_params wm_params; enum pipe pipe = to_intel_crtc(cstate->base.crtc)->pipe; - uint16_t ddb_blocks; - wm = &pipe_wm->planes[plane_id]; - ddb_blocks = skl_ddb_entry_size(&ddb->plane[pipe][plane_id]); - - ret = skl_compute_plane_wm_params(dev_priv, cstate, - intel_pstate, &wm_params, 0); + ret = skl_build_plane_wm(cstate, ddb, pipe_wm, + pipe, plane_id, (struct intel_plane_state *) intel_pstate); if (ret) return ret; - - ret = skl_compute_wm_levels(dev_priv, ddb, cstate, - intel_pstate, &wm_params, wm, 0, plane_id); - if (ret) - return ret; - - skl_compute_transition_wm(cstate, &wm_params, &wm->wm[0], - ddb_blocks, &wm->trans_wm); - - /* uv plane watermarks must also be validated for NV12/Planar */ - if (wm_params.is_planar) { - memset(&wm_params, 0, sizeof(struct skl_wm_params)); - wm->is_planar = true; - - ret = skl_compute_plane_wm_params(dev_priv, cstate, - intel_pstate, - &wm_params, 1); - if (ret) - return ret; - - ret = skl_compute_wm_levels(dev_priv, ddb, cstate, - intel_pstate, &wm_params, - wm, 1, plane_id); - if (ret) - return ret; - } } pipe_wm->linetime = skl_compute_linetime_wm(cstate); From 80eed71a6087433636467d79f300ab1aaf7656eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arve=20Hj=C3=B8nnev=C3=A5g?= Date: Mon, 18 Nov 2013 20:46:48 -0800 Subject: [PATCH 0972/1103] trusty: Add trusty driver MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit includes: Add arm64 support includes: Add trusty_fast_call64 api on 64 bit systems. includes: move probe to subsys_initcall Child devices of trusty like FIQ-based debuggers and watchdogs may want to probe early, move trusty from module init to subsys init to allow it and its children to probe earlier. includes: Retry std_calls on SM_ERR_BUSY If the trusty spinlock is held, or if the strex fails for another reason, trusty returns SM_ERR_BUSY. Add retry code to handle this. Without this retry code, std_calls can fail. If the previous smc call had returned SM_ERR_INTERRUPTED, this failure would cause the driver to get out of sync with trusty. All later calls would then fail with SM_ERR_INTERLEAVED_SMC. Change-Id: Idc0bbe78b557bc5d95dbec448e4085e3ab9111b4 Signed-off-by: Arve Hjønnevåg --- .../devicetree/bindings/trusty/trusty-smc.txt | 6 + drivers/Kconfig | 2 + drivers/Makefile | 1 + drivers/trusty/Kconfig | 11 + drivers/trusty/Makefile | 5 + drivers/trusty/trusty.c | 258 ++++++++++++++++++ include/linux/trusty/sm_err.h | 39 +++ include/linux/trusty/smcall.h | 75 +++++ include/linux/trusty/trusty.h | 46 ++++ 9 files changed, 443 insertions(+) create mode 100644 Documentation/devicetree/bindings/trusty/trusty-smc.txt create mode 100644 drivers/trusty/Kconfig create mode 100644 drivers/trusty/Makefile create mode 100644 drivers/trusty/trusty.c create mode 100644 include/linux/trusty/sm_err.h create mode 100644 include/linux/trusty/smcall.h create mode 100644 include/linux/trusty/trusty.h diff --git a/Documentation/devicetree/bindings/trusty/trusty-smc.txt b/Documentation/devicetree/bindings/trusty/trusty-smc.txt new file mode 100644 index 000000000000..1b39ad317c67 --- /dev/null +++ b/Documentation/devicetree/bindings/trusty/trusty-smc.txt @@ -0,0 +1,6 @@ +Trusty smc interface + +Trusty is running in secure mode on the same (arm) cpu(s) as the current os. + +Required properties: +- compatible: "android,trusty-smc-v1" diff --git a/drivers/Kconfig b/drivers/Kconfig index ab4d43923c4d..ca0515d53928 100644 --- a/drivers/Kconfig +++ b/drivers/Kconfig @@ -79,6 +79,8 @@ source "drivers/hwmon/Kconfig" source "drivers/thermal/Kconfig" +source "drivers/trusty/Kconfig" + source "drivers/watchdog/Kconfig" source "drivers/ssb/Kconfig" diff --git a/drivers/Makefile b/drivers/Makefile index 578f469f72fb..372a5c27dcb1 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -118,6 +118,7 @@ obj-$(CONFIG_W1) += w1/ obj-y += power/ obj-$(CONFIG_HWMON) += hwmon/ obj-$(CONFIG_THERMAL) += thermal/ +obj-$(CONFIG_TRUSTY) += trusty/ obj-$(CONFIG_WATCHDOG) += watchdog/ obj-$(CONFIG_MD) += md/ obj-$(CONFIG_BT) += bluetooth/ diff --git a/drivers/trusty/Kconfig b/drivers/trusty/Kconfig new file mode 100644 index 000000000000..f577ae8acad3 --- /dev/null +++ b/drivers/trusty/Kconfig @@ -0,0 +1,11 @@ +# +# Trusty +# + +menu "Trusty" + +config TRUSTY + tristate "Trusty" + default n + +endmenu diff --git a/drivers/trusty/Makefile b/drivers/trusty/Makefile new file mode 100644 index 000000000000..1d77805d7dd6 --- /dev/null +++ b/drivers/trusty/Makefile @@ -0,0 +1,5 @@ +# +# Makefile for trusty components +# + +obj-$(CONFIG_TRUSTY) += trusty.o diff --git a/drivers/trusty/trusty.c b/drivers/trusty/trusty.c new file mode 100644 index 000000000000..59ecf60fc050 --- /dev/null +++ b/drivers/trusty/trusty.c @@ -0,0 +1,258 @@ +/* + * Copyright (C) 2013 Google, Inc. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct trusty_state { + struct mutex smc_lock; +}; + +#ifdef CONFIG_ARM64 +#define SMC_ARG0 "x0" +#define SMC_ARG1 "x1" +#define SMC_ARG2 "x2" +#define SMC_ARG3 "x3" +#define SMC_ARCH_EXTENSION "" +#define SMC_REGISTERS_TRASHED "x4","x5","x6","x7","x8","x9","x10","x11", \ + "x12","x13","x14","x15","x16","x17" +#else +#define SMC_ARG0 "r0" +#define SMC_ARG1 "r1" +#define SMC_ARG2 "r2" +#define SMC_ARG3 "r3" +#define SMC_ARCH_EXTENSION ".arch_extension sec\n" +#define SMC_REGISTERS_TRASHED "ip" +#endif + +static inline ulong smc(ulong r0, ulong r1, ulong r2, ulong r3) +{ + register ulong _r0 asm(SMC_ARG0) = r0; + register ulong _r1 asm(SMC_ARG1) = r1; + register ulong _r2 asm(SMC_ARG2) = r2; + register ulong _r3 asm(SMC_ARG3) = r3; + + asm volatile( + __asmeq("%0", SMC_ARG0) + __asmeq("%1", SMC_ARG1) + __asmeq("%2", SMC_ARG2) + __asmeq("%3", SMC_ARG3) + __asmeq("%4", SMC_ARG0) + __asmeq("%5", SMC_ARG1) + __asmeq("%6", SMC_ARG2) + __asmeq("%7", SMC_ARG3) + SMC_ARCH_EXTENSION + "smc #0" /* switch to secure world */ + : "=r" (_r0), "=r" (_r1), "=r" (_r2), "=r" (_r3) + : "r" (_r0), "r" (_r1), "r" (_r2), "r" (_r3) + : SMC_REGISTERS_TRASHED); + return _r0; +} + +s32 trusty_fast_call32(struct device *dev, u32 smcnr, u32 a0, u32 a1, u32 a2) +{ + struct trusty_state *s = platform_get_drvdata(to_platform_device(dev)); + + BUG_ON(!s); + BUG_ON(!SMC_IS_FASTCALL(smcnr)); + BUG_ON(SMC_IS_SMC64(smcnr)); + + return smc(smcnr, a0, a1, a2); +} +EXPORT_SYMBOL(trusty_fast_call32); + +#ifdef CONFIG_64BIT +s64 trusty_fast_call64(struct device *dev, u64 smcnr, u64 a0, u64 a1, u64 a2) +{ + struct trusty_state *s = platform_get_drvdata(to_platform_device(dev)); + + BUG_ON(!s); + BUG_ON(!SMC_IS_FASTCALL(smcnr)); + BUG_ON(!SMC_IS_SMC64(smcnr)); + + return smc(smcnr, a0, a1, a2); +} +#endif + +static ulong trusty_std_call_inner(struct device *dev, ulong smcnr, + ulong a0, ulong a1, ulong a2) +{ + ulong ret; + int retry = 5; + + dev_dbg(dev, "%s(0x%lx 0x%lx 0x%lx 0x%lx)\n", + __func__, smcnr, a0, a1, a2); + while (true) { + ret = smc(smcnr, a0, a1, a2); + if ((int)ret != SM_ERR_BUSY || !retry) + break; + + dev_dbg(dev, "%s(0x%lx 0x%lx 0x%lx 0x%lx) returned busy, retry\n", + __func__, smcnr, a0, a1, a2); + retry--; + } + + return ret; +} + +static ulong trusty_std_call_helper(struct device *dev, ulong smcnr, + ulong a0, ulong a1, ulong a2) +{ + ulong ret; + int sleep_time = 1; + struct trusty_state *s = platform_get_drvdata(to_platform_device(dev)); + + while (true) { + ret = trusty_std_call_inner(dev, smcnr, a0, a1, a2); + if ((int)ret != SM_ERR_BUSY) + break; + + if (sleep_time == 256) + dev_warn(dev, "%s(0x%lx 0x%lx 0x%lx 0x%lx) returned busy\n", + __func__, smcnr, a0, a1, a2); + dev_dbg(dev, "%s(0x%lx 0x%lx 0x%lx 0x%lx) returned busy, wait %d ms\n", + __func__, smcnr, a0, a1, a2, sleep_time); + + msleep(sleep_time); + if (sleep_time < 1000) + sleep_time <<= 1; + + dev_dbg(dev, "%s(0x%lx 0x%lx 0x%lx 0x%lx) retry\n", + __func__, smcnr, a0, a1, a2); + } + + if (sleep_time > 256) + dev_warn(dev, "%s(0x%lx 0x%lx 0x%lx 0x%lx) busy cleared\n", + __func__, smcnr, a0, a1, a2); + + return ret; +} + +s32 trusty_std_call32(struct device *dev, u32 smcnr, u32 a0, u32 a1, u32 a2) +{ + int ret; + struct trusty_state *s = platform_get_drvdata(to_platform_device(dev)); + + BUG_ON(SMC_IS_FASTCALL(smcnr)); + BUG_ON(SMC_IS_SMC64(smcnr)); + + mutex_lock(&s->smc_lock); + + dev_dbg(dev, "%s(0x%x 0x%x 0x%x 0x%x) started\n", + __func__, smcnr, a0, a1, a2); + + ret = trusty_std_call_helper(dev, smcnr, a0, a1, a2); + while (ret == SM_ERR_INTERRUPTED) { + dev_dbg(dev, "%s(0x%x 0x%x 0x%x 0x%x) interrupted\n", + __func__, smcnr, a0, a1, a2); + ret = trusty_std_call_helper(dev, SMC_SC_RESTART_LAST, 0, 0, 0); + } + dev_dbg(dev, "%s(0x%x 0x%x 0x%x 0x%x) returned 0x%x\n", + __func__, smcnr, a0, a1, a2, ret); + + WARN_ONCE(ret == SM_ERR_PANIC, "trusty crashed"); + + mutex_unlock(&s->smc_lock); + + return ret; +} +EXPORT_SYMBOL(trusty_std_call32); + +static int trusty_remove_child(struct device *dev, void *data) +{ + platform_device_unregister(to_platform_device(dev)); + return 0; +} + +static int trusty_probe(struct platform_device *pdev) +{ + int ret; + struct trusty_state *s; + struct device_node *node = pdev->dev.of_node; + + if (!node) { + dev_err(&pdev->dev, "of_node required\n"); + return -EINVAL; + } + + s = kzalloc(sizeof(*s), GFP_KERNEL); + if (!s) { + ret = -ENOMEM; + goto err_allocate_state; + } + mutex_init(&s->smc_lock); + platform_set_drvdata(pdev, s); + + ret = of_platform_populate(pdev->dev.of_node, NULL, NULL, &pdev->dev); + if (ret < 0) { + dev_err(&pdev->dev, "Failed to add children: %d\n", ret); + goto err_add_children; + } + + return 0; + +err_add_children: + device_for_each_child(&pdev->dev, NULL, trusty_remove_child); + mutex_destroy(&s->smc_lock); + kfree(s); +err_allocate_state: + return ret; +} + +static int trusty_remove(struct platform_device *pdev) +{ + struct trusty_state *s = platform_get_drvdata(pdev); + + device_for_each_child(&pdev->dev, NULL, trusty_remove_child); + mutex_destroy(&s->smc_lock); + kfree(s); + return 0; +} + +static const struct of_device_id trusty_of_match[] = { + { .compatible = "android,trusty-smc-v1", }, + {}, +}; + +static struct platform_driver trusty_driver = { + .probe = trusty_probe, + .remove = trusty_remove, + .driver = { + .name = "trusty", + .owner = THIS_MODULE, + .of_match_table = trusty_of_match, + }, +}; + +static int __init trusty_driver_init(void) +{ + return platform_driver_register(&trusty_driver); +} + +static void __exit trusty_driver_exit(void) +{ + platform_driver_unregister(&trusty_driver); +} + +subsys_initcall(trusty_driver_init); +module_exit(trusty_driver_exit); diff --git a/include/linux/trusty/sm_err.h b/include/linux/trusty/sm_err.h new file mode 100644 index 000000000000..4ee67589ce63 --- /dev/null +++ b/include/linux/trusty/sm_err.h @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2013 Google Inc. All rights reserved + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files + * (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, + * publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef __LINUX_TRUSTY_SM_ERR_H +#define __LINUX_TRUSTY_SM_ERR_H + +/* Errors from the secure monitor */ +#define SM_ERR_UNDEFINED_SMC 0xFFFFFFFF /* Unknown SMC (defined by ARM DEN 0028A(0.9.0) */ +#define SM_ERR_INVALID_PARAMETERS -2 +#define SM_ERR_INTERRUPTED -3 /* Got interrupted. Call back with restart SMC */ +#define SM_ERR_UNEXPECTED_RESTART -4 /* Got an restart SMC when we didn't expect it */ +#define SM_ERR_BUSY -5 /* Temporarily busy. Call back with original args */ +#define SM_ERR_INTERLEAVED_SMC -6 /* Got a trusted_service SMC when a restart SMC is required */ +#define SM_ERR_INTERNAL_FAILURE -7 /* Unknown error */ +#define SM_ERR_NOT_SUPPORTED -8 +#define SM_ERR_NOT_ALLOWED -9 /* SMC call not allowed */ +#define SM_ERR_END_OF_INPUT -10 +#define SM_ERR_PANIC -11 /* Secure OS crashed */ + +#endif diff --git a/include/linux/trusty/smcall.h b/include/linux/trusty/smcall.h new file mode 100644 index 000000000000..278a4b256fbc --- /dev/null +++ b/include/linux/trusty/smcall.h @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2013-2014 Google Inc. All rights reserved + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files + * (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, + * publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef __LINUX_TRUSTY_SMCALL_H +#define __LINUX_TRUSTY_SMCALL_H + +#define SMC_NUM_ENTITIES 64 +#define SMC_NUM_ARGS 4 +#define SMC_NUM_PARAMS (SMC_NUM_ARGS - 1) + +#define SMC_IS_FASTCALL(smc_nr) ((smc_nr) & 0x80000000) +#define SMC_IS_SMC64(smc_nr) ((smc_nr) & 0x40000000) +#define SMC_ENTITY(smc_nr) (((smc_nr) & 0x3F000000) >> 24) +#define SMC_FUNCTION(smc_nr) ((smc_nr) & 0x0000FFFF) + +#define SMC_NR(entity, fn, fastcall, smc64) ((((fastcall) & 0x1) << 31) | \ + (((smc64) & 0x1) << 30) | \ + (((entity) & 0x3F) << 24) | \ + ((fn) & 0xFFFF) \ + ) + +#define SMC_FASTCALL_NR(entity, fn) SMC_NR((entity), (fn), 1, 0) +#define SMC_STDCALL_NR(entity, fn) SMC_NR((entity), (fn), 0, 0) +#define SMC_FASTCALL64_NR(entity, fn) SMC_NR((entity), (fn), 1, 1) +#define SMC_STDCALL64_NR(entity, fn) SMC_NR((entity), (fn), 0, 1) + +#define SMC_ENTITY_ARCH 0 /* ARM Architecture calls */ +#define SMC_ENTITY_CPU 1 /* CPU Service calls */ +#define SMC_ENTITY_SIP 2 /* SIP Service calls */ +#define SMC_ENTITY_OEM 3 /* OEM Service calls */ +#define SMC_ENTITY_STD 4 /* Standard Service calls */ +#define SMC_ENTITY_RESERVED 5 /* Reserved for future use */ +#define SMC_ENTITY_TRUSTED_APP 48 /* Trusted Application calls */ +#define SMC_ENTITY_TRUSTED_OS 50 /* Trusted OS calls */ +#define SMC_ENTITY_SECURE_MONITOR 60 /* Trusted OS calls internal to secure monitor */ + +/* FC = Fast call, SC = Standard call */ +#define SMC_SC_RESTART_LAST SMC_STDCALL_NR (SMC_ENTITY_SECURE_MONITOR, 0) +#define SMC_SC_NOP SMC_STDCALL_NR (SMC_ENTITY_SECURE_MONITOR, 1) + +/* + * Return from secure os to non-secure os with return value in r1 + */ +#define SMC_SC_NS_RETURN SMC_STDCALL_NR (SMC_ENTITY_SECURE_MONITOR, 0) + +#define SMC_FC_RESERVED SMC_FASTCALL_NR (SMC_ENTITY_SECURE_MONITOR, 0) +#define SMC_FC_FIQ_EXIT SMC_FASTCALL_NR (SMC_ENTITY_SECURE_MONITOR, 1) +#define SMC_FC_REQUEST_FIQ SMC_FASTCALL_NR (SMC_ENTITY_SECURE_MONITOR, 2) +#define SMC_FC_GET_NEXT_IRQ SMC_FASTCALL_NR (SMC_ENTITY_SECURE_MONITOR, 3) + +#define SMC_FC_CPU_SUSPEND SMC_FASTCALL_NR (SMC_ENTITY_SECURE_MONITOR, 7) +#define SMC_FC_CPU_RESUME SMC_FASTCALL_NR (SMC_ENTITY_SECURE_MONITOR, 8) + +#define SMC_FC_AARCH_SWITCH SMC_FASTCALL_NR (SMC_ENTITY_SECURE_MONITOR, 9) + +#endif /* __LINUX_TRUSTY_SMCALL_H */ diff --git a/include/linux/trusty/trusty.h b/include/linux/trusty/trusty.h new file mode 100644 index 000000000000..30d4300ba301 --- /dev/null +++ b/include/linux/trusty/trusty.h @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2013 Google, Inc. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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. + * + */ +#ifndef __LINUX_TRUSTY_TRUSTY_H +#define __LINUX_TRUSTY_TRUSTY_H + +#include +#include + +#ifdef CONFIG_TRUSTY +s32 trusty_std_call32(struct device *dev, u32 smcnr, u32 a0, u32 a1, u32 a2); +s32 trusty_fast_call32(struct device *dev, u32 smcnr, u32 a0, u32 a1, u32 a2); +#ifdef CONFIG_64BIT +s64 trusty_fast_call64(struct device *dev, u64 smcnr, u64 a0, u64 a1, u64 a2); +#endif +#else +static inline s32 trusty_std_call32(struct device *dev, u32 smcnr, + u32 a0, u32 a1, u32 a2) +{ + return SM_ERR_UNDEFINED_SMC; +} +static inline s32 trusty_fast_call32(struct device *dev, u32 smcnr, + u32 a0, u32 a1, u32 a2) +{ + return SM_ERR_UNDEFINED_SMC; +} +#ifdef CONFIG_64BIT +static inline s64 trusty_fast_call64(struct device *dev, + u64 smcnr, u64 a0, u64 a1, u64 a2) +{ + return SM_ERR_UNDEFINED_SMC; +} +#endif +#endif + +#endif From 4e6069ced66ab75ae8d0cd184ab33bb4663c174e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arve=20Hj=C3=B8nnev=C3=A5g?= Date: Thu, 5 May 2016 15:43:44 -0700 Subject: [PATCH 0973/1103] trusty: Add notifier before and after every smc call. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Called with local interrupts disabled. Change-Id: I5d2b15ce0fee29f067d8403a6f7127046fc185e9 Signed-off-by: Arve Hjønnevåg --- drivers/trusty/trusty.c | 26 ++++++++++++++++++++++++++ include/linux/trusty/trusty.h | 10 ++++++++++ 2 files changed, 36 insertions(+) diff --git a/drivers/trusty/trusty.c b/drivers/trusty/trusty.c index 59ecf60fc050..7efcff89610c 100644 --- a/drivers/trusty/trusty.c +++ b/drivers/trusty/trusty.c @@ -26,6 +26,7 @@ struct trusty_state { struct mutex smc_lock; + struct atomic_notifier_head notifier; }; #ifdef CONFIG_ARM64 @@ -123,7 +124,14 @@ static ulong trusty_std_call_helper(struct device *dev, ulong smcnr, struct trusty_state *s = platform_get_drvdata(to_platform_device(dev)); while (true) { + local_irq_disable(); + atomic_notifier_call_chain(&s->notifier, TRUSTY_CALL_PREPARE, + NULL); ret = trusty_std_call_inner(dev, smcnr, a0, a1, a2); + atomic_notifier_call_chain(&s->notifier, TRUSTY_CALL_RETURNED, + NULL); + local_irq_enable(); + if ((int)ret != SM_ERR_BUSY) break; @@ -178,6 +186,23 @@ s32 trusty_std_call32(struct device *dev, u32 smcnr, u32 a0, u32 a1, u32 a2) } EXPORT_SYMBOL(trusty_std_call32); +int trusty_call_notifier_register(struct device *dev, struct notifier_block *n) +{ + struct trusty_state *s = platform_get_drvdata(to_platform_device(dev)); + + return atomic_notifier_chain_register(&s->notifier, n); +} +EXPORT_SYMBOL(trusty_call_notifier_register); + +int trusty_call_notifier_unregister(struct device *dev, + struct notifier_block *n) +{ + struct trusty_state *s = platform_get_drvdata(to_platform_device(dev)); + + return atomic_notifier_chain_unregister(&s->notifier, n); +} +EXPORT_SYMBOL(trusty_call_notifier_unregister); + static int trusty_remove_child(struct device *dev, void *data) { platform_device_unregister(to_platform_device(dev)); @@ -201,6 +226,7 @@ static int trusty_probe(struct platform_device *pdev) goto err_allocate_state; } mutex_init(&s->smc_lock); + ATOMIC_INIT_NOTIFIER_HEAD(&s->notifier); platform_set_drvdata(pdev, s); ret = of_platform_populate(pdev->dev.of_node, NULL, NULL, &pdev->dev); diff --git a/include/linux/trusty/trusty.h b/include/linux/trusty/trusty.h index 30d4300ba301..ce00c1d46a5e 100644 --- a/include/linux/trusty/trusty.h +++ b/include/linux/trusty/trusty.h @@ -43,4 +43,14 @@ static inline s64 trusty_fast_call64(struct device *dev, #endif #endif +struct notifier_block; +enum { + TRUSTY_CALL_PREPARE, + TRUSTY_CALL_RETURNED, +}; +int trusty_call_notifier_register(struct device *dev, + struct notifier_block *n); +int trusty_call_notifier_unregister(struct device *dev, + struct notifier_block *n); + #endif From 481e2a795aa85fbfb388585ebdd5598ce1a52d8c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arve=20Hj=C3=B8nnev=C3=A5g?= Date: Thu, 9 Oct 2014 21:24:17 -0700 Subject: [PATCH 0974/1103] trusty: Get version string from trusty MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Print trusty version to kernel log on startup, and export it in sysfs. Change-Id: Ib8e3d856ed9cf86a71d334f5ab753af1ec8a8bd3 Signed-off-by: Arve Hjønnevåg --- drivers/trusty/trusty.c | 66 +++++++++++++++++++++++++++++++++++ include/linux/trusty/smcall.h | 1 + include/linux/trusty/trusty.h | 2 +- 3 files changed, 68 insertions(+), 1 deletion(-) diff --git a/drivers/trusty/trusty.c b/drivers/trusty/trusty.c index 7efcff89610c..16c595bf5e29 100644 --- a/drivers/trusty/trusty.c +++ b/drivers/trusty/trusty.c @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include @@ -27,6 +28,7 @@ struct trusty_state { struct mutex smc_lock; struct atomic_notifier_head notifier; + char *version_str; }; #ifdef CONFIG_ARM64 @@ -209,6 +211,60 @@ static int trusty_remove_child(struct device *dev, void *data) return 0; } +ssize_t trusty_version_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct trusty_state *s = platform_get_drvdata(to_platform_device(dev)); + + return scnprintf(buf, PAGE_SIZE, "%s\n", s->version_str); +} + +DEVICE_ATTR(trusty_version, S_IRUSR, trusty_version_show, NULL); + +const char *trusty_version_str_get(struct device *dev) +{ + struct trusty_state *s = platform_get_drvdata(to_platform_device(dev)); + + return s->version_str; +} +EXPORT_SYMBOL(trusty_version_str_get); + +static void trusty_init_version(struct trusty_state *s, struct device *dev) +{ + int ret; + int i; + int version_str_len; + + ret = trusty_fast_call32(dev, SMC_FC_GET_VERSION_STR, -1, 0, 0); + if (ret <= 0) + goto err_get_size; + + version_str_len = ret; + + s->version_str = kmalloc(version_str_len + 1, GFP_KERNEL); + for (i = 0; i < version_str_len; i++) { + ret = trusty_fast_call32(dev, SMC_FC_GET_VERSION_STR, i, 0, 0); + if (ret < 0) + goto err_get_char; + s->version_str[i] = ret; + } + s->version_str[i] = '\0'; + + dev_info(dev, "trusty version: %s\n", s->version_str); + + ret = device_create_file(dev, &dev_attr_trusty_version); + if (ret) + goto err_create_file; + return; + +err_create_file: +err_get_char: + kfree(s->version_str); + s->version_str = NULL; +err_get_size: + dev_err(dev, "failed to get version: %d\n", ret); +} + static int trusty_probe(struct platform_device *pdev) { int ret; @@ -229,6 +285,8 @@ static int trusty_probe(struct platform_device *pdev) ATOMIC_INIT_NOTIFIER_HEAD(&s->notifier); platform_set_drvdata(pdev, s); + trusty_init_version(s, &pdev->dev); + ret = of_platform_populate(pdev->dev.of_node, NULL, NULL, &pdev->dev); if (ret < 0) { dev_err(&pdev->dev, "Failed to add children: %d\n", ret); @@ -238,6 +296,10 @@ static int trusty_probe(struct platform_device *pdev) return 0; err_add_children: + if (s->version_str) { + device_remove_file(&pdev->dev, &dev_attr_trusty_version); + kfree(s->version_str); + } device_for_each_child(&pdev->dev, NULL, trusty_remove_child); mutex_destroy(&s->smc_lock); kfree(s); @@ -251,6 +313,10 @@ static int trusty_remove(struct platform_device *pdev) device_for_each_child(&pdev->dev, NULL, trusty_remove_child); mutex_destroy(&s->smc_lock); + if (s->version_str) { + device_remove_file(&pdev->dev, &dev_attr_trusty_version); + kfree(s->version_str); + } kfree(s); return 0; } diff --git a/include/linux/trusty/smcall.h b/include/linux/trusty/smcall.h index 278a4b256fbc..4344683f6c61 100644 --- a/include/linux/trusty/smcall.h +++ b/include/linux/trusty/smcall.h @@ -71,5 +71,6 @@ #define SMC_FC_CPU_RESUME SMC_FASTCALL_NR (SMC_ENTITY_SECURE_MONITOR, 8) #define SMC_FC_AARCH_SWITCH SMC_FASTCALL_NR (SMC_ENTITY_SECURE_MONITOR, 9) +#define SMC_FC_GET_VERSION_STR SMC_FASTCALL_NR (SMC_ENTITY_SECURE_MONITOR, 10) #endif /* __LINUX_TRUSTY_SMCALL_H */ diff --git a/include/linux/trusty/trusty.h b/include/linux/trusty/trusty.h index ce00c1d46a5e..abb77f1db74d 100644 --- a/include/linux/trusty/trusty.h +++ b/include/linux/trusty/trusty.h @@ -52,5 +52,5 @@ int trusty_call_notifier_register(struct device *dev, struct notifier_block *n); int trusty_call_notifier_unregister(struct device *dev, struct notifier_block *n); - +const char *trusty_version_str_get(struct device *dev); #endif From 5744acaa576ee14b2547085748f9977b4ac1b499 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arve=20Hj=C3=B8nnev=C3=A5g?= Date: Mon, 18 Nov 2013 20:52:55 -0800 Subject: [PATCH 0975/1103] trusty: Add interrupt support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Get list of interrupts from secure mode and register handlers for them. When an interrupt triggers, disable the interrupt and schedule a work function. The work functions then masks interrupts at the cpu, reenables the interrupt and calls into secure mode. Edge triggered interrupts are not supported. Change-Id: I6df62e791140f0f2a8b5718b30edd86cca3dde5b Signed-off-by: Arve Hjønnevåg --- .../devicetree/bindings/trusty/trusty-irq.txt | 8 + drivers/trusty/Makefile | 1 + drivers/trusty/trusty-irq.c | 536 ++++++++++++++++++ 3 files changed, 545 insertions(+) create mode 100644 Documentation/devicetree/bindings/trusty/trusty-irq.txt create mode 100644 drivers/trusty/trusty-irq.c diff --git a/Documentation/devicetree/bindings/trusty/trusty-irq.txt b/Documentation/devicetree/bindings/trusty/trusty-irq.txt new file mode 100644 index 000000000000..85fe1f1c7458 --- /dev/null +++ b/Documentation/devicetree/bindings/trusty/trusty-irq.txt @@ -0,0 +1,8 @@ +Trusty irq interface + +Trusty requires non-secure irqs to be forwarded to the secure OS. + +Required properties: +- compatible: "android,trusty-irq-v1" + +Must be a child of the node that provides the trusty std/fast call interface. diff --git a/drivers/trusty/Makefile b/drivers/trusty/Makefile index 1d77805d7dd6..89acb6f7868a 100644 --- a/drivers/trusty/Makefile +++ b/drivers/trusty/Makefile @@ -3,3 +3,4 @@ # obj-$(CONFIG_TRUSTY) += trusty.o +obj-$(CONFIG_TRUSTY) += trusty-irq.o diff --git a/drivers/trusty/trusty-irq.c b/drivers/trusty/trusty-irq.c new file mode 100644 index 000000000000..ae9535af77dd --- /dev/null +++ b/drivers/trusty/trusty-irq.c @@ -0,0 +1,536 @@ +/* + * Copyright (C) 2013 Google, Inc. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct trusty_irq { + struct trusty_irq_state *is; + struct hlist_node node; + unsigned int irq; + bool percpu; + bool enable; + struct trusty_irq __percpu *percpu_ptr; +}; + +struct trusty_irq_work { + struct trusty_irq_state *is; + struct work_struct work; +}; + +struct trusty_irq_irqset { + struct hlist_head pending; + struct hlist_head inactive; +}; + +struct trusty_irq_state { + struct device *dev; + struct device *trusty_dev; + struct trusty_irq_work __percpu *irq_work; + struct trusty_irq_irqset normal_irqs; + spinlock_t normal_irqs_lock; + struct trusty_irq_irqset __percpu *percpu_irqs; + struct notifier_block trusty_call_notifier; + struct notifier_block cpu_notifier; +}; + +static void trusty_irq_enable_pending_irqs(struct trusty_irq_state *is, + struct trusty_irq_irqset *irqset, + bool percpu) +{ + struct hlist_node *n; + struct trusty_irq *trusty_irq; + + hlist_for_each_entry_safe(trusty_irq, n, &irqset->pending, node) { + dev_dbg(is->dev, + "%s: enable pending irq %d, percpu %d, cpu %d\n", + __func__, trusty_irq->irq, percpu, smp_processor_id()); + if (percpu) + enable_percpu_irq(trusty_irq->irq, 0); + else + enable_irq(trusty_irq->irq); + hlist_del(&trusty_irq->node); + hlist_add_head(&trusty_irq->node, &irqset->inactive); + } +} + +static void trusty_irq_enable_irqset(struct trusty_irq_state *is, + struct trusty_irq_irqset *irqset) +{ + struct trusty_irq *trusty_irq; + + hlist_for_each_entry(trusty_irq, &irqset->inactive, node) { + if (trusty_irq->enable) { + dev_warn(is->dev, + "%s: percpu irq %d already enabled, cpu %d\n", + __func__, trusty_irq->irq, smp_processor_id()); + continue; + } + dev_dbg(is->dev, "%s: enable percpu irq %d, cpu %d\n", + __func__, trusty_irq->irq, smp_processor_id()); + enable_percpu_irq(trusty_irq->irq, 0); + trusty_irq->enable = true; + } +} + +static void trusty_irq_disable_irqset(struct trusty_irq_state *is, + struct trusty_irq_irqset *irqset) +{ + struct hlist_node *n; + struct trusty_irq *trusty_irq; + + hlist_for_each_entry(trusty_irq, &irqset->inactive, node) { + if (!trusty_irq->enable) { + dev_warn(is->dev, + "irq %d already disabled, percpu %d, cpu %d\n", + trusty_irq->irq, trusty_irq->percpu, + smp_processor_id()); + continue; + } + dev_dbg(is->dev, "%s: disable irq %d, percpu %d, cpu %d\n", + __func__, trusty_irq->irq, trusty_irq->percpu, + smp_processor_id()); + trusty_irq->enable = false; + if (trusty_irq->percpu) + disable_percpu_irq(trusty_irq->irq); + else + disable_irq_nosync(trusty_irq->irq); + } + hlist_for_each_entry_safe(trusty_irq, n, &irqset->pending, node) { + if (!trusty_irq->enable) { + dev_warn(is->dev, + "pending irq %d already disabled, percpu %d, cpu %d\n", + trusty_irq->irq, trusty_irq->percpu, + smp_processor_id()); + } + dev_dbg(is->dev, + "%s: disable pending irq %d, percpu %d, cpu %d\n", + __func__, trusty_irq->irq, trusty_irq->percpu, + smp_processor_id()); + trusty_irq->enable = false; + hlist_del(&trusty_irq->node); + hlist_add_head(&trusty_irq->node, &irqset->inactive); + } +} + +static int trusty_irq_call_notify(struct notifier_block *nb, + unsigned long action, void *data) +{ + struct trusty_irq_state *is; + + BUG_ON(!irqs_disabled()); + + if (action != TRUSTY_CALL_PREPARE) + return NOTIFY_DONE; + + is = container_of(nb, struct trusty_irq_state, trusty_call_notifier); + + spin_lock(&is->normal_irqs_lock); + trusty_irq_enable_pending_irqs(is, &is->normal_irqs, false); + spin_unlock(&is->normal_irqs_lock); + trusty_irq_enable_pending_irqs(is, this_cpu_ptr(is->percpu_irqs), true); + + return NOTIFY_OK; +} + + +static void trusty_irq_work_func(struct work_struct *work) +{ + int ret; + struct trusty_irq_state *is = + container_of(work, struct trusty_irq_work, work)->is; + + dev_dbg(is->dev, "%s\n", __func__); + + ret = trusty_std_call32(is->trusty_dev, SMC_SC_NOP, 0, 0, 0); + if (ret != 0) + dev_err(is->dev, "%s: SMC_SC_NOP failed %d", __func__, ret); + + dev_dbg(is->dev, "%s: done\n", __func__); +} + +irqreturn_t trusty_irq_handler(int irq, void *data) +{ + struct trusty_irq *trusty_irq = data; + struct trusty_irq_state *is = trusty_irq->is; + struct trusty_irq_work *trusty_irq_work = this_cpu_ptr(is->irq_work); + struct trusty_irq_irqset *irqset; + + dev_dbg(is->dev, "%s: irq %d, percpu %d, cpu %d, enable %d\n", + __func__, irq, trusty_irq->irq, smp_processor_id(), + trusty_irq->enable); + + if (trusty_irq->percpu) { + disable_percpu_irq(irq); + irqset = this_cpu_ptr(is->percpu_irqs); + } else { + disable_irq_nosync(irq); + irqset = &is->normal_irqs; + } + + spin_lock(&is->normal_irqs_lock); + if (trusty_irq->enable) { + hlist_del(&trusty_irq->node); + hlist_add_head(&trusty_irq->node, &irqset->pending); + } + spin_unlock(&is->normal_irqs_lock); + + schedule_work_on(raw_smp_processor_id(), &trusty_irq_work->work); + + dev_dbg(is->dev, "%s: irq %d done\n", __func__, irq); + + return IRQ_HANDLED; +} + +static void trusty_irq_cpu_up(void *info) +{ + unsigned long irq_flags; + struct trusty_irq_state *is = info; + + dev_dbg(is->dev, "%s: cpu %d\n", __func__, smp_processor_id()); + + local_irq_save(irq_flags); + trusty_irq_enable_irqset(is, this_cpu_ptr(is->percpu_irqs)); + local_irq_restore(irq_flags); +} + +static void trusty_irq_cpu_down(void *info) +{ + unsigned long irq_flags; + struct trusty_irq_state *is = info; + + dev_dbg(is->dev, "%s: cpu %d\n", __func__, smp_processor_id()); + + local_irq_save(irq_flags); + trusty_irq_disable_irqset(is, this_cpu_ptr(is->percpu_irqs)); + local_irq_restore(irq_flags); +} + +static int trusty_irq_cpu_notify(struct notifier_block *nb, + unsigned long action, void *hcpu) +{ + struct trusty_irq_state *is; + + is = container_of(nb, struct trusty_irq_state, cpu_notifier); + + dev_dbg(is->dev, "%s: 0x%lx\n", __func__, action); + + switch (action & ~CPU_TASKS_FROZEN) { + case CPU_STARTING: + trusty_irq_cpu_up(is); + break; + case CPU_DYING: + trusty_irq_cpu_down(is); + break; + } + + return NOTIFY_OK; +} + +static int trusty_irq_init_normal_irq(struct trusty_irq_state *is, int irq) +{ + int ret; + unsigned long irq_flags; + struct trusty_irq *trusty_irq; + + dev_dbg(is->dev, "%s: irq %d\n", __func__, irq); + + trusty_irq = kzalloc(sizeof(*trusty_irq), GFP_KERNEL); + if (!trusty_irq) + return -ENOMEM; + + trusty_irq->is = is; + trusty_irq->irq = irq; + trusty_irq->enable = true; + + spin_lock_irqsave(&is->normal_irqs_lock, irq_flags); + hlist_add_head(&trusty_irq->node, &is->normal_irqs.inactive); + spin_unlock_irqrestore(&is->normal_irqs_lock, irq_flags); + + ret = request_irq(irq, trusty_irq_handler, IRQF_NO_THREAD, + "trusty", trusty_irq); + if (ret) { + dev_err(is->dev, "request_irq failed %d\n", ret); + goto err_request_irq; + } + return 0; + +err_request_irq: + spin_lock_irqsave(&is->normal_irqs_lock, irq_flags); + hlist_del(&trusty_irq->node); + spin_unlock_irqrestore(&is->normal_irqs_lock, irq_flags); + kfree(trusty_irq); + return ret; +} + +static int trusty_irq_init_per_cpu_irq(struct trusty_irq_state *is, int irq) +{ + int ret; + unsigned int cpu; + struct trusty_irq __percpu *trusty_irq_handler_data; + + dev_dbg(is->dev, "%s: irq %d\n", __func__, irq); + + trusty_irq_handler_data = alloc_percpu(struct trusty_irq); + if (!trusty_irq_handler_data) + return -ENOMEM; + + for_each_possible_cpu(cpu) { + struct trusty_irq *trusty_irq; + struct trusty_irq_irqset *irqset; + + trusty_irq = per_cpu_ptr(trusty_irq_handler_data, cpu); + irqset = per_cpu_ptr(is->percpu_irqs, cpu); + + trusty_irq->is = is; + hlist_add_head(&trusty_irq->node, &irqset->inactive); + trusty_irq->irq = irq; + trusty_irq->percpu = true; + trusty_irq->percpu_ptr = trusty_irq_handler_data; + } + + ret = request_percpu_irq(irq, trusty_irq_handler, "trusty", + trusty_irq_handler_data); + if (ret) { + dev_err(is->dev, "request_percpu_irq failed %d\n", ret); + goto err_request_percpu_irq; + } + + return 0; + +err_request_percpu_irq: + for_each_possible_cpu(cpu) { + struct trusty_irq *trusty_irq; + + trusty_irq = per_cpu_ptr(trusty_irq_handler_data, cpu); + hlist_del(&trusty_irq->node); + } + + free_percpu(trusty_irq_handler_data); + return ret; +} + +static int trusty_smc_get_next_irq(struct trusty_irq_state *is, + unsigned long min_irq, bool per_cpu) +{ + return trusty_fast_call32(is->trusty_dev, SMC_FC_GET_NEXT_IRQ, + min_irq, per_cpu, 0); +} + +static int trusty_irq_init_one(struct trusty_irq_state *is, + int irq, bool per_cpu) +{ + int ret; + + irq = trusty_smc_get_next_irq(is, irq, per_cpu); + if (irq < 0) + return irq; + + if (per_cpu) + ret = trusty_irq_init_per_cpu_irq(is, irq); + else + ret = trusty_irq_init_normal_irq(is, irq); + + if (ret) { + dev_warn(is->dev, + "failed to initialize irq %d, irq will be ignored\n", + irq); + } + + return irq + 1; +} + +static void trusty_irq_free_irqs(struct trusty_irq_state *is) +{ + struct trusty_irq *irq; + struct hlist_node *n; + unsigned int cpu; + + hlist_for_each_entry_safe(irq, n, &is->normal_irqs.inactive, node) { + dev_dbg(is->dev, "%s: irq %d\n", __func__, irq->irq); + free_irq(irq->irq, irq); + hlist_del(&irq->node); + kfree(irq); + } + hlist_for_each_entry_safe(irq, n, + &this_cpu_ptr(is->percpu_irqs)->inactive, + node) { + struct trusty_irq __percpu *trusty_irq_handler_data; + + dev_dbg(is->dev, "%s: percpu irq %d\n", __func__, irq->irq); + trusty_irq_handler_data = irq->percpu_ptr; + free_percpu_irq(irq->irq, trusty_irq_handler_data); + for_each_possible_cpu(cpu) { + struct trusty_irq *irq_tmp; + + irq_tmp = per_cpu_ptr(trusty_irq_handler_data, cpu); + hlist_del(&irq_tmp->node); + } + free_percpu(trusty_irq_handler_data); + } +} + +static int trusty_irq_probe(struct platform_device *pdev) +{ + int ret; + int irq; + unsigned int cpu; + unsigned long irq_flags; + struct trusty_irq_state *is; + + dev_dbg(&pdev->dev, "%s\n", __func__); + + is = kzalloc(sizeof(*is), GFP_KERNEL); + if (!is) { + ret = -ENOMEM; + goto err_alloc_is; + } + + is->dev = &pdev->dev; + is->trusty_dev = is->dev->parent; + is->irq_work = alloc_percpu(struct trusty_irq_work); + if (!is->irq_work) { + ret = -ENOMEM; + goto err_alloc_irq_work; + } + spin_lock_init(&is->normal_irqs_lock); + is->percpu_irqs = alloc_percpu(struct trusty_irq_irqset); + if (!is->percpu_irqs) { + ret = -ENOMEM; + goto err_alloc_pending_percpu_irqs; + } + + platform_set_drvdata(pdev, is); + + is->trusty_call_notifier.notifier_call = trusty_irq_call_notify; + ret = trusty_call_notifier_register(is->trusty_dev, + &is->trusty_call_notifier); + if (ret) { + dev_err(&pdev->dev, + "failed to register trusty call notifier\n"); + goto err_trusty_call_notifier_register; + } + + for_each_possible_cpu(cpu) { + struct trusty_irq_work *trusty_irq_work; + + trusty_irq_work = per_cpu_ptr(is->irq_work, cpu); + trusty_irq_work->is = is; + INIT_WORK(&trusty_irq_work->work, trusty_irq_work_func); + } + + for (irq = 0; irq >= 0;) + irq = trusty_irq_init_one(is, irq, true); + for (irq = 0; irq >= 0;) + irq = trusty_irq_init_one(is, irq, false); + + is->cpu_notifier.notifier_call = trusty_irq_cpu_notify; + ret = register_hotcpu_notifier(&is->cpu_notifier); + if (ret) { + dev_err(&pdev->dev, "register_cpu_notifier failed %d\n", ret); + goto err_register_hotcpu_notifier; + } + ret = on_each_cpu(trusty_irq_cpu_up, is, 0); + if (ret) { + dev_err(&pdev->dev, "register_cpu_notifier failed %d\n", ret); + goto err_on_each_cpu; + } + + return 0; + +err_on_each_cpu: + unregister_hotcpu_notifier(&is->cpu_notifier); + on_each_cpu(trusty_irq_cpu_down, is, 1); +err_register_hotcpu_notifier: + spin_lock_irqsave(&is->normal_irqs_lock, irq_flags); + trusty_irq_disable_irqset(is, &is->normal_irqs); + spin_unlock_irqrestore(&is->normal_irqs_lock, irq_flags); + trusty_irq_free_irqs(is); + trusty_call_notifier_unregister(is->trusty_dev, + &is->trusty_call_notifier); +err_trusty_call_notifier_register: + free_percpu(is->percpu_irqs); +err_alloc_pending_percpu_irqs: + for_each_possible_cpu(cpu) { + struct trusty_irq_work *trusty_irq_work; + + trusty_irq_work = per_cpu_ptr(is->irq_work, cpu); + flush_work(&trusty_irq_work->work); + } + free_percpu(is->irq_work); +err_alloc_irq_work: + kfree(is); +err_alloc_is: + return ret; +} + +static int trusty_irq_remove(struct platform_device *pdev) +{ + int ret; + unsigned int cpu; + unsigned long irq_flags; + struct trusty_irq_state *is = platform_get_drvdata(pdev); + + dev_dbg(&pdev->dev, "%s\n", __func__); + + unregister_hotcpu_notifier(&is->cpu_notifier); + ret = on_each_cpu(trusty_irq_cpu_down, is, 1); + if (ret) + dev_err(&pdev->dev, "on_each_cpu failed %d\n", ret); + spin_lock_irqsave(&is->normal_irqs_lock, irq_flags); + trusty_irq_disable_irqset(is, &is->normal_irqs); + spin_unlock_irqrestore(&is->normal_irqs_lock, irq_flags); + + trusty_irq_free_irqs(is); + + trusty_call_notifier_unregister(is->trusty_dev, + &is->trusty_call_notifier); + free_percpu(is->percpu_irqs); + for_each_possible_cpu(cpu) { + struct trusty_irq_work *trusty_irq_work; + + trusty_irq_work = per_cpu_ptr(is->irq_work, cpu); + flush_work(&trusty_irq_work->work); + } + free_percpu(is->irq_work); + kfree(is); + + return 0; +} + +static const struct of_device_id trusty_test_of_match[] = { + { .compatible = "android,trusty-irq-v1", }, + {}, +}; + +static struct platform_driver trusty_irq_driver = { + .probe = trusty_irq_probe, + .remove = trusty_irq_remove, + .driver = { + .name = "trusty-irq", + .owner = THIS_MODULE, + .of_match_table = trusty_test_of_match, + }, +}; + +module_platform_driver(trusty_irq_driver); From fa49f69dd5bf4d5210c61f5e8e1dbb943871411a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arve=20Hj=C3=B8nnev=C3=A5g?= Date: Tue, 26 Nov 2013 20:18:35 -0800 Subject: [PATCH 0976/1103] trusty: Add fiq support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Register a custom fiq-return handler with the fiq-glue so the monitor mode can atomically re-enable the fiq and return to the last mode. Change-Id: I0016d67edccea096d7f189e223ac73cc20f79ac9 Signed-off-by: Arve Hjønnevåg --- .../bindings/trusty/trusty-fiq-debugger.txt | 8 ++ .../devicetree/bindings/trusty/trusty-fiq.txt | 8 ++ drivers/trusty/Kconfig | 12 +++ drivers/trusty/Makefile | 2 + drivers/trusty/trusty-fiq-arm.c | 42 +++++++++ drivers/trusty/trusty-fiq.c | 85 +++++++++++++++++++ drivers/trusty/trusty-fiq.h | 16 ++++ 7 files changed, 173 insertions(+) create mode 100644 Documentation/devicetree/bindings/trusty/trusty-fiq-debugger.txt create mode 100644 Documentation/devicetree/bindings/trusty/trusty-fiq.txt create mode 100644 drivers/trusty/trusty-fiq-arm.c create mode 100644 drivers/trusty/trusty-fiq.c create mode 100644 drivers/trusty/trusty-fiq.h diff --git a/Documentation/devicetree/bindings/trusty/trusty-fiq-debugger.txt b/Documentation/devicetree/bindings/trusty/trusty-fiq-debugger.txt new file mode 100644 index 000000000000..18329d39487e --- /dev/null +++ b/Documentation/devicetree/bindings/trusty/trusty-fiq-debugger.txt @@ -0,0 +1,8 @@ +Trusty fiq debugger interface + +Provides a single fiq for the fiq debugger. + +Required properties: +- compatible: compatible = "android,trusty-fiq-v1-*"; where * is a serial port. + +Must be a child of the node that provides fiq support ("android,trusty-fiq-v1"). diff --git a/Documentation/devicetree/bindings/trusty/trusty-fiq.txt b/Documentation/devicetree/bindings/trusty/trusty-fiq.txt new file mode 100644 index 000000000000..de810b955bc9 --- /dev/null +++ b/Documentation/devicetree/bindings/trusty/trusty-fiq.txt @@ -0,0 +1,8 @@ +Trusty fiq interface + +Trusty provides fiq emulation. + +Required properties: +- compatible: "android,trusty-fiq-v1" + +Must be a child of the node that provides the trusty std/fast call interface. diff --git a/drivers/trusty/Kconfig b/drivers/trusty/Kconfig index f577ae8acad3..3c725e29b399 100644 --- a/drivers/trusty/Kconfig +++ b/drivers/trusty/Kconfig @@ -8,4 +8,16 @@ config TRUSTY tristate "Trusty" default n +config TRUSTY_FIQ + tristate + depends on TRUSTY + +config TRUSTY_FIQ_ARM + tristate + depends on TRUSTY + depends on ARM + select FIQ_GLUE + select TRUSTY_FIQ + default y + endmenu diff --git a/drivers/trusty/Makefile b/drivers/trusty/Makefile index 89acb6f7868a..a01c82485eb6 100644 --- a/drivers/trusty/Makefile +++ b/drivers/trusty/Makefile @@ -4,3 +4,5 @@ obj-$(CONFIG_TRUSTY) += trusty.o obj-$(CONFIG_TRUSTY) += trusty-irq.o +obj-$(CONFIG_TRUSTY_FIQ) += trusty-fiq.o +obj-$(CONFIG_TRUSTY_FIQ_ARM) += trusty-fiq-arm.o diff --git a/drivers/trusty/trusty-fiq-arm.c b/drivers/trusty/trusty-fiq-arm.c new file mode 100644 index 000000000000..8c62a00bbc44 --- /dev/null +++ b/drivers/trusty/trusty-fiq-arm.c @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2013 Google, Inc. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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. + * + */ + +#include +#include +#include +#include + +#include "trusty-fiq.h" + +#define _STRINGIFY(x) #x +#define STRINGIFY(x) _STRINGIFY(x) + +static void __naked trusty_fiq_return(void) +{ + asm volatile( + ".arch_extension sec\n" + "mov r12, r0\n" + "ldr r0, =" STRINGIFY(SMC_FC_FIQ_EXIT) "\n" + "smc #0"); +} + +int trusty_fiq_arch_probe(struct platform_device *pdev) +{ + return fiq_glue_set_return_handler(trusty_fiq_return); +} + +void trusty_fiq_arch_remove(struct platform_device *pdev) +{ + fiq_glue_clear_return_handler(trusty_fiq_return); +} diff --git a/drivers/trusty/trusty-fiq.c b/drivers/trusty/trusty-fiq.c new file mode 100644 index 000000000000..1a031c67ea72 --- /dev/null +++ b/drivers/trusty/trusty-fiq.c @@ -0,0 +1,85 @@ +/* + * Copyright (C) 2013 Google, Inc. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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. + * + */ + +#include +#include +#include +#include +#include + +#include "trusty-fiq.h" + +static int trusty_fiq_remove_child(struct device *dev, void *data) +{ + platform_device_unregister(to_platform_device(dev)); + return 0; +} + +static int trusty_fiq_probe(struct platform_device *pdev) +{ + int ret; + + ret = trusty_fiq_arch_probe(pdev); + if (ret) + goto err_set_fiq_return; + + ret = of_platform_populate(pdev->dev.of_node, NULL, NULL, &pdev->dev); + if (ret < 0) { + dev_err(&pdev->dev, "Failed to add children: %d\n", ret); + goto err_add_children; + } + + return 0; + +err_add_children: + device_for_each_child(&pdev->dev, NULL, trusty_fiq_remove_child); + trusty_fiq_arch_remove(pdev); +err_set_fiq_return: + return ret; +} + +static int trusty_fiq_remove(struct platform_device *pdev) +{ + device_for_each_child(&pdev->dev, NULL, trusty_fiq_remove_child); + trusty_fiq_arch_remove(pdev); + return 0; +} + +static const struct of_device_id trusty_fiq_of_match[] = { + { .compatible = "android,trusty-fiq-v1", }, + {}, +}; + +static struct platform_driver trusty_fiq_driver = { + .probe = trusty_fiq_probe, + .remove = trusty_fiq_remove, + .driver = { + .name = "trusty-fiq", + .owner = THIS_MODULE, + .of_match_table = trusty_fiq_of_match, + }, +}; + +static int __init trusty_fiq_driver_init(void) +{ + return platform_driver_register(&trusty_fiq_driver); +} + +static void __exit trusty_fiq_driver_exit(void) +{ + platform_driver_unregister(&trusty_fiq_driver); +} + +subsys_initcall(trusty_fiq_driver_init); +module_exit(trusty_fiq_driver_exit); diff --git a/drivers/trusty/trusty-fiq.h b/drivers/trusty/trusty-fiq.h new file mode 100644 index 000000000000..d4ae9a9635f3 --- /dev/null +++ b/drivers/trusty/trusty-fiq.h @@ -0,0 +1,16 @@ +/* + * Copyright (C) 2014 Google, Inc. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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. + * + */ + +int trusty_fiq_arch_probe(struct platform_device *pdev); +void trusty_fiq_arch_remove(struct platform_device *pdev); From a4e5d8f0ebf9631f1066de0213ea7b34b35918b4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arve=20Hj=C3=B8nnev=C3=A5g?= Date: Mon, 14 Apr 2014 17:18:40 -0700 Subject: [PATCH 0977/1103] trusty: arm64 fiq support Change-Id: I907fbaa2b9d1697b204dad6c16d9027ef3bb0a58 --- drivers/trusty/Kconfig | 8 ++ drivers/trusty/Makefile | 1 + drivers/trusty/trusty-fiq-arm64-glue.S | 54 ++++++++++ drivers/trusty/trusty-fiq-arm64.c | 140 +++++++++++++++++++++++++ include/linux/trusty/smcall.h | 4 + 5 files changed, 207 insertions(+) create mode 100644 drivers/trusty/trusty-fiq-arm64-glue.S create mode 100644 drivers/trusty/trusty-fiq-arm64.c diff --git a/drivers/trusty/Kconfig b/drivers/trusty/Kconfig index 3c725e29b399..fc1061deb876 100644 --- a/drivers/trusty/Kconfig +++ b/drivers/trusty/Kconfig @@ -20,4 +20,12 @@ config TRUSTY_FIQ_ARM select TRUSTY_FIQ default y +config TRUSTY_FIQ_ARM64 + tristate + depends on TRUSTY + depends on ARM64 + select FIQ_GLUE + select TRUSTY_FIQ + default y + endmenu diff --git a/drivers/trusty/Makefile b/drivers/trusty/Makefile index a01c82485eb6..e162a4061e14 100644 --- a/drivers/trusty/Makefile +++ b/drivers/trusty/Makefile @@ -6,3 +6,4 @@ obj-$(CONFIG_TRUSTY) += trusty.o obj-$(CONFIG_TRUSTY) += trusty-irq.o obj-$(CONFIG_TRUSTY_FIQ) += trusty-fiq.o obj-$(CONFIG_TRUSTY_FIQ_ARM) += trusty-fiq-arm.o +obj-$(CONFIG_TRUSTY_FIQ_ARM64) += trusty-fiq-arm64.o trusty-fiq-arm64-glue.o diff --git a/drivers/trusty/trusty-fiq-arm64-glue.S b/drivers/trusty/trusty-fiq-arm64-glue.S new file mode 100644 index 000000000000..6994b3a94fc3 --- /dev/null +++ b/drivers/trusty/trusty-fiq-arm64-glue.S @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2013 Google, Inc. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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. + * + */ + +#include +#include +#include +#include + +.macro push reg1,reg2,remregs:vararg + .ifnb \remregs + push \remregs + .endif + stp \reg1, \reg2, [sp, #-16]! +.endm + +.macro pop reg1,reg2,remregs:vararg + ldp \reg1, \reg2, [sp], #16 + .ifnb \remregs + pop \remregs + .endif +.endm + +ENTRY(trusty_fiq_glue_arm64) + sub sp, sp, #S_FRAME_SIZE - S_LR + push x0, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, \ + x14, x15, x16, x17, x18, x19, x20, x21, x22, x23, x24, x25, \ + x26, x27, x28, x29 + ldr x0, =SMC_FC64_GET_FIQ_REGS + smc #0 + stp x0, x1, [sp, #S_PC] /* original pc, cpsr */ + tst x1, PSR_MODE_MASK + csel x2, x2, x3, eq /* sp el0, sp el1 */ + stp x30, x2, [sp, #S_LR] /* lr, original sp */ + mov x0, sp + mov x1, x3 + bl trusty_fiq_handler + pop x0, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, \ + x14, x15, x16, x17, x18, x19, x20, x21, x22, x23, x24, x25, \ + x26, x27, x28, x29 + ldr x30, [sp], #S_FRAME_SIZE - S_LR /* load LR and restore SP */ + ldr x0, =SMC_FC_FIQ_EXIT + smc #0 + b . /* should not get here */ diff --git a/drivers/trusty/trusty-fiq-arm64.c b/drivers/trusty/trusty-fiq-arm64.c new file mode 100644 index 000000000000..df05a98f235d --- /dev/null +++ b/drivers/trusty/trusty-fiq-arm64.c @@ -0,0 +1,140 @@ +/* + * Copyright (C) 2013 Google, Inc. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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. + * + */ + +#include +#include +#include +#include +#include + +#include + +#include "trusty-fiq.h" + +extern void trusty_fiq_glue_arm64(void); + +static struct device *trusty_dev; +static DEFINE_PER_CPU(void *, fiq_stack); +static struct fiq_glue_handler *current_handler; +static DEFINE_MUTEX(fiq_glue_lock); + +void trusty_fiq_handler(struct pt_regs *regs, void *svc_sp) +{ + current_handler->fiq(current_handler, regs, svc_sp); +} + +static void smp_nop_call(void *info) +{ + /* If this call is reached, the fiq handler is not currently running */ +} + +static void fiq_glue_clear_handler(void) +{ + int cpu; + int ret; + void *stack; + + for_each_possible_cpu(cpu) { + stack = per_cpu(fiq_stack, cpu); + if (!stack) + continue; + + ret = trusty_fast_call64(trusty_dev, SMC_FC64_SET_FIQ_HANDLER, + cpu, 0, 0); + if (ret) { + pr_err("%s: SMC_FC_SET_FIQ_HANDLER(%d, 0, 0) failed 0x%x, skip free stack\n", + __func__, cpu, ret); + continue; + } + + per_cpu(fiq_stack, cpu) = NULL; + smp_call_function_single(cpu, smp_nop_call, NULL, true); + free_pages((unsigned long)stack, THREAD_SIZE_ORDER); + } +} + +int fiq_glue_register_handler(struct fiq_glue_handler *handler) +{ + int ret; + int cpu; + void *stack; + unsigned long irqflags; + + if (!handler || !handler->fiq) + return -EINVAL; + + mutex_lock(&fiq_glue_lock); + + if (!trusty_dev) { + ret = -ENODEV; + goto err_no_trusty; + } + if (current_handler) { + ret = -EBUSY; + goto err_busy; + } + + current_handler = handler; + + for_each_possible_cpu(cpu) { + stack = (void *)__get_free_pages(GFP_KERNEL, THREAD_SIZE_ORDER); + if (WARN_ON(!stack)) { + ret = -ENOMEM; + goto err_alloc_fiq_stack; + } + per_cpu(fiq_stack, cpu) = stack; + stack += THREAD_START_SP; + + local_irq_save(irqflags); + ret = trusty_fast_call64(trusty_dev, SMC_FC64_SET_FIQ_HANDLER, + cpu, (uintptr_t)trusty_fiq_glue_arm64, + (uintptr_t)stack); + local_irq_restore(irqflags); + if (ret) { + pr_err("%s: SMC_FC_SET_FIQ_HANDLER(%d, %p, %p) failed 0x%x\n", + __func__, cpu, trusty_fiq_glue_arm64, + stack, ret); + ret = -EINVAL; + goto err_set_fiq_handler; + } + } + + mutex_unlock(&fiq_glue_lock); + return 0; + +err_set_fiq_handler: +err_alloc_fiq_stack: + fiq_glue_clear_handler(); +err_busy: +err_no_trusty: + mutex_unlock(&fiq_glue_lock); + return ret; +} + +int trusty_fiq_arch_probe(struct platform_device *pdev) +{ + mutex_lock(&fiq_glue_lock); + trusty_dev = pdev->dev.parent; + mutex_unlock(&fiq_glue_lock); + + return 0; +} + +void trusty_fiq_arch_remove(struct platform_device *pdev) +{ + mutex_lock(&fiq_glue_lock); + fiq_glue_clear_handler(); + trusty_dev = NULL; + mutex_unlock(&fiq_glue_lock); +} diff --git a/include/linux/trusty/smcall.h b/include/linux/trusty/smcall.h index 4344683f6c61..e8704974d3e3 100644 --- a/include/linux/trusty/smcall.h +++ b/include/linux/trusty/smcall.h @@ -66,6 +66,10 @@ #define SMC_FC_FIQ_EXIT SMC_FASTCALL_NR (SMC_ENTITY_SECURE_MONITOR, 1) #define SMC_FC_REQUEST_FIQ SMC_FASTCALL_NR (SMC_ENTITY_SECURE_MONITOR, 2) #define SMC_FC_GET_NEXT_IRQ SMC_FASTCALL_NR (SMC_ENTITY_SECURE_MONITOR, 3) +#define SMC_FC_FIQ_ENTER SMC_FASTCALL_NR (SMC_ENTITY_SECURE_MONITOR, 4) + +#define SMC_FC64_SET_FIQ_HANDLER SMC_FASTCALL64_NR(SMC_ENTITY_SECURE_MONITOR, 5) +#define SMC_FC64_GET_FIQ_REGS SMC_FASTCALL64_NR (SMC_ENTITY_SECURE_MONITOR, 6) #define SMC_FC_CPU_SUSPEND SMC_FASTCALL_NR (SMC_ENTITY_SECURE_MONITOR, 7) #define SMC_FC_CPU_RESUME SMC_FASTCALL_NR (SMC_ENTITY_SECURE_MONITOR, 8) From 81a59960ba7254c0de0ec73d7b0170a7850aa15a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arve=20Hj=C3=B8nnev=C3=A5g?= Date: Fri, 2 May 2014 19:15:44 -0700 Subject: [PATCH 0978/1103] trusty: fiq-arm64: Allow multiple fiq handlers. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit If multiple fiq handlers are reqistered call them all. There is currently no api to remove handlers. Change-Id: I1d4bd936081d690ea6f1ec0c041f43a5f7717733 Signed-off-by: Arve Hjønnevåg --- drivers/trusty/trusty-fiq-arm64.c | 76 ++++++++++++++++++++++--------- 1 file changed, 54 insertions(+), 22 deletions(-) diff --git a/drivers/trusty/trusty-fiq-arm64.c b/drivers/trusty/trusty-fiq-arm64.c index df05a98f235d..8b9a40887587 100644 --- a/drivers/trusty/trusty-fiq-arm64.c +++ b/drivers/trusty/trusty-fiq-arm64.c @@ -26,12 +26,19 @@ extern void trusty_fiq_glue_arm64(void); static struct device *trusty_dev; static DEFINE_PER_CPU(void *, fiq_stack); -static struct fiq_glue_handler *current_handler; +static struct fiq_glue_handler *fiq_handlers; static DEFINE_MUTEX(fiq_glue_lock); void trusty_fiq_handler(struct pt_regs *regs, void *svc_sp) { - current_handler->fiq(current_handler, regs, svc_sp); + struct fiq_glue_handler *handler; + + for (handler = ACCESS_ONCE(fiq_handlers); handler; + handler = ACCESS_ONCE(handler->next)) { + /* Barrier paired with smp_wmb in fiq_glue_register_handler */ + smp_read_barrier_depends(); + handler->fiq(handler, regs, svc_sp); + } } static void smp_nop_call(void *info) @@ -64,29 +71,13 @@ static void fiq_glue_clear_handler(void) } } -int fiq_glue_register_handler(struct fiq_glue_handler *handler) +static int fiq_glue_set_handler(void) { int ret; int cpu; void *stack; unsigned long irqflags; - if (!handler || !handler->fiq) - return -EINVAL; - - mutex_lock(&fiq_glue_lock); - - if (!trusty_dev) { - ret = -ENODEV; - goto err_no_trusty; - } - if (current_handler) { - ret = -EBUSY; - goto err_busy; - } - - current_handler = handler; - for_each_possible_cpu(cpu) { stack = (void *)__get_free_pages(GFP_KERNEL, THREAD_SIZE_ORDER); if (WARN_ON(!stack)) { @@ -109,16 +100,57 @@ int fiq_glue_register_handler(struct fiq_glue_handler *handler) goto err_set_fiq_handler; } } + return 0; + +err_alloc_fiq_stack: +err_set_fiq_handler: + fiq_glue_clear_handler(); + return ret; +} + +int fiq_glue_register_handler(struct fiq_glue_handler *handler) +{ + int ret; + + if (!handler || !handler->fiq) { + ret = -EINVAL; + goto err_bad_arg; + } + + mutex_lock(&fiq_glue_lock); + + if (!trusty_dev) { + ret = -ENODEV; + goto err_no_trusty; + } + + handler->next = fiq_handlers; + /* + * Write barrier paired with smp_read_barrier_depends in + * trusty_fiq_handler. Make sure next pointer is updated before + * fiq_handlers so trusty_fiq_handler does not see an uninitialized + * value and terminate early or crash. + */ + smp_wmb(); + fiq_handlers = handler; + + smp_call_function(smp_nop_call, NULL, true); + + if (!handler->next) { + ret = fiq_glue_set_handler(); + if (ret) + goto err_set_fiq_handler; + } mutex_unlock(&fiq_glue_lock); return 0; err_set_fiq_handler: -err_alloc_fiq_stack: - fiq_glue_clear_handler(); -err_busy: + fiq_handlers = handler->next; err_no_trusty: mutex_unlock(&fiq_glue_lock); +err_bad_arg: + pr_err("%s: failed, %d\n", __func__, ret); return ret; } From 90aaab854b2570f69cdc62a8a6bb2b7ef51a0aca Mon Sep 17 00:00:00 2001 From: Riley Andrews Date: Thu, 5 May 2016 14:42:41 -0700 Subject: [PATCH 0979/1103] trusty: Add trusty logging driver. This driver is the consumer side of a ringbuffer of log data that the secure operating system dumps prints into. Trusty printfs will be dumped into the kernel log after smc calls and during panics. Change-Id: Iadc939b60940330e8fe02a52f3e397da7833c2fa --- drivers/trusty/Kconfig | 5 + drivers/trusty/Makefile | 1 + drivers/trusty/trusty-log.c | 274 ++++++++++++++++++++++++++++++++++ drivers/trusty/trusty-log.h | 22 +++ include/linux/trusty/smcall.h | 1 + 5 files changed, 303 insertions(+) create mode 100644 drivers/trusty/trusty-log.c create mode 100644 drivers/trusty/trusty-log.h diff --git a/drivers/trusty/Kconfig b/drivers/trusty/Kconfig index fc1061deb876..ea75813254c0 100644 --- a/drivers/trusty/Kconfig +++ b/drivers/trusty/Kconfig @@ -28,4 +28,9 @@ config TRUSTY_FIQ_ARM64 select TRUSTY_FIQ default y +config TRUSTY_LOG + tristate + depends on TRUSTY + default y + endmenu diff --git a/drivers/trusty/Makefile b/drivers/trusty/Makefile index e162a4061e14..641ee2a6e830 100644 --- a/drivers/trusty/Makefile +++ b/drivers/trusty/Makefile @@ -7,3 +7,4 @@ obj-$(CONFIG_TRUSTY) += trusty-irq.o obj-$(CONFIG_TRUSTY_FIQ) += trusty-fiq.o obj-$(CONFIG_TRUSTY_FIQ_ARM) += trusty-fiq-arm.o obj-$(CONFIG_TRUSTY_FIQ_ARM64) += trusty-fiq-arm64.o trusty-fiq-arm64-glue.o +obj-$(CONFIG_TRUSTY_LOG) += trusty-log.o diff --git a/drivers/trusty/trusty-log.c b/drivers/trusty/trusty-log.c new file mode 100644 index 000000000000..e8dcced2ff1d --- /dev/null +++ b/drivers/trusty/trusty-log.c @@ -0,0 +1,274 @@ +/* + * Copyright (C) 2015 Google, Inc. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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. + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "trusty-log.h" + +#define TRUSTY_LOG_SIZE (PAGE_SIZE * 2) +#define TRUSTY_LINE_BUFFER_SIZE 256 + +struct trusty_log_state { + struct device *dev; + struct device *trusty_dev; + + /* + * This lock is here to ensure only one consumer will read + * from the log ring buffer at a time. + */ + spinlock_t lock; + struct log_rb *log; + uint32_t get; + + struct page *log_pages; + + struct notifier_block call_notifier; + struct notifier_block panic_notifier; + char line_buffer[TRUSTY_LINE_BUFFER_SIZE]; +}; + +static int log_read_line(struct trusty_log_state *s, int put, int get) +{ + struct log_rb *log = s->log; + int i; + char c = '\0'; + size_t max_to_read = min((size_t)(put - get), + sizeof(s->line_buffer) - 1); + size_t mask = log->sz - 1; + + for (i = 0; i < max_to_read && c != '\n';) + s->line_buffer[i++] = c = log->data[get++ & mask]; + s->line_buffer[i] = '\0'; + + return i; +} + +static void trusty_dump_logs(struct trusty_log_state *s) +{ + struct log_rb *log = s->log; + uint32_t get, put, alloc; + int read_chars; + + BUG_ON(!is_power_of_2(log->sz)); + + /* + * For this ring buffer, at any given point, alloc >= put >= get. + * The producer side of the buffer is not locked, so the put and alloc + * pointers must be read in a defined order (put before alloc) so + * that the above condition is maintained. A read barrier is needed + * to make sure the hardware and compiler keep the reads ordered. + */ + get = s->get; + while ((put = log->put) != get) { + /* Make sure that the read of put occurs before the read of log data */ + rmb(); + + /* Read a line from the log */ + read_chars = log_read_line(s, put, get); + + /* Force the loads from log_read_line to complete. */ + rmb(); + alloc = log->alloc; + + /* + * Discard the line that was just read if the data could + * have been corrupted by the producer. + */ + if (alloc - get > log->sz) { + pr_err("trusty: log overflow."); + get = alloc - log->sz; + continue; + } + pr_info("trusty: %s", s->line_buffer); + get += read_chars; + } + s->get = get; +} + +static int trusty_log_call_notify(struct notifier_block *nb, + unsigned long action, void *data) +{ + struct trusty_log_state *s; + unsigned long flags; + + if (action != TRUSTY_CALL_RETURNED) + return NOTIFY_DONE; + + s = container_of(nb, struct trusty_log_state, call_notifier); + spin_lock_irqsave(&s->lock, flags); + trusty_dump_logs(s); + spin_unlock_irqrestore(&s->lock, flags); + return NOTIFY_OK; +} + +static int trusty_log_panic_notify(struct notifier_block *nb, + unsigned long action, void *data) +{ + struct trusty_log_state *s; + + /* + * Don't grab the spin lock to hold up the panic notifier, even + * though this is racy. + */ + s = container_of(nb, struct trusty_log_state, panic_notifier); + pr_info("trusty-log panic notifier - trusty version %s", + trusty_version_str_get(s->trusty_dev)); + trusty_dump_logs(s); + return NOTIFY_OK; +} + +static bool trusty_supports_logging(struct device *device) +{ + int result; + + result = trusty_std_call32(device, SMC_SC_SHARED_LOG_VERSION, + TRUSTY_LOG_API_VERSION, 0, 0); + if (result == SM_ERR_UNDEFINED_SMC) { + pr_info("trusty-log not supported on secure side.\n"); + return false; + } else if (result < 0) { + pr_err("trusty std call (SMC_SC_SHARED_LOG_VERSION) failed: %d\n", + result); + return false; + } + + if (result == TRUSTY_LOG_API_VERSION) { + return true; + } else { + pr_info("trusty-log unsupported api version: %d, supported: %d\n", + result, TRUSTY_LOG_API_VERSION); + return false; + } +} + +static int trusty_log_probe(struct platform_device *pdev) +{ + struct trusty_log_state *s; + int result; + phys_addr_t pa; + + dev_dbg(&pdev->dev, "%s\n", __func__); + if (!trusty_supports_logging(pdev->dev.parent)) { + return -ENXIO; + } + + s = kzalloc(sizeof(*s), GFP_KERNEL); + if (!s) { + result = -ENOMEM; + goto error_alloc_state; + } + + spin_lock_init(&s->lock); + s->dev = &pdev->dev; + s->trusty_dev = s->dev->parent; + s->get = 0; + s->log_pages = alloc_pages(GFP_KERNEL | __GFP_ZERO, + get_order(TRUSTY_LOG_SIZE)); + if (!s->log_pages) { + result = -ENOMEM; + goto error_alloc_log; + } + s->log = page_address(s->log_pages); + + pa = page_to_phys(s->log_pages); + result = trusty_std_call32(s->trusty_dev, + SMC_SC_SHARED_LOG_ADD, + (u32)(pa), (u32)(pa >> 32), + TRUSTY_LOG_SIZE); + if (result < 0) { + pr_err("trusty std call (SMC_SC_SHARED_LOG_ADD) failed: %d %pa\n", + result, &pa); + goto error_std_call; + } + + s->call_notifier.notifier_call = trusty_log_call_notify; + result = trusty_call_notifier_register(s->trusty_dev, + &s->call_notifier); + if (result < 0) { + dev_err(&pdev->dev, + "failed to register trusty call notifier\n"); + goto error_call_notifier; + } + + s->panic_notifier.notifier_call = trusty_log_panic_notify; + result = atomic_notifier_chain_register(&panic_notifier_list, + &s->panic_notifier); + if (result < 0) { + dev_err(&pdev->dev, + "failed to register panic notifier\n"); + goto error_panic_notifier; + } + platform_set_drvdata(pdev, s); + + return 0; + +error_panic_notifier: + trusty_call_notifier_unregister(s->trusty_dev, &s->call_notifier); +error_call_notifier: + trusty_std_call32(s->trusty_dev, SMC_SC_SHARED_LOG_RM, + (u32)pa, (u32)(pa >> 32), 0); +error_std_call: + __free_pages(s->log_pages, get_order(TRUSTY_LOG_SIZE)); +error_alloc_log: + kfree(s); +error_alloc_state: + return result; +} + +static int trusty_log_remove(struct platform_device *pdev) +{ + int result; + struct trusty_log_state *s = platform_get_drvdata(pdev); + phys_addr_t pa = page_to_phys(s->log_pages); + + dev_dbg(&pdev->dev, "%s\n", __func__); + + atomic_notifier_chain_unregister(&panic_notifier_list, + &s->panic_notifier); + trusty_call_notifier_unregister(s->trusty_dev, &s->call_notifier); + + result = trusty_std_call32(s->trusty_dev, SMC_SC_SHARED_LOG_RM, + (u32)pa, (u32)(pa >> 32), 0); + if (result) { + pr_err("trusty std call (SMC_SC_SHARED_LOG_RM) failed: %d\n", + result); + } + __free_pages(s->log_pages, get_order(TRUSTY_LOG_SIZE)); + kfree(s); + + return 0; +} + +static const struct of_device_id trusty_test_of_match[] = { + { .compatible = "android,trusty-log-v1", }, + {}, +}; + +static struct platform_driver trusty_log_driver = { + .probe = trusty_log_probe, + .remove = trusty_log_remove, + .driver = { + .name = "trusty-log", + .owner = THIS_MODULE, + .of_match_table = trusty_test_of_match, + }, +}; + +module_platform_driver(trusty_log_driver); diff --git a/drivers/trusty/trusty-log.h b/drivers/trusty/trusty-log.h new file mode 100644 index 000000000000..09f60213e1f6 --- /dev/null +++ b/drivers/trusty/trusty-log.h @@ -0,0 +1,22 @@ +#ifndef _TRUSTY_LOG_H_ +#define _TRUSTY_LOG_H_ + +/* + * Ring buffer that supports one secure producer thread and one + * linux side consumer thread. + */ +struct log_rb { + volatile uint32_t alloc; + volatile uint32_t put; + uint32_t sz; + volatile char data[0]; +} __packed; + +#define SMC_SC_SHARED_LOG_VERSION SMC_STDCALL_NR(SMC_ENTITY_LOGGING, 0) +#define SMC_SC_SHARED_LOG_ADD SMC_STDCALL_NR(SMC_ENTITY_LOGGING, 1) +#define SMC_SC_SHARED_LOG_RM SMC_STDCALL_NR(SMC_ENTITY_LOGGING, 2) + +#define TRUSTY_LOG_API_VERSION 1 + +#endif + diff --git a/include/linux/trusty/smcall.h b/include/linux/trusty/smcall.h index e8704974d3e3..aaad5cee6143 100644 --- a/include/linux/trusty/smcall.h +++ b/include/linux/trusty/smcall.h @@ -51,6 +51,7 @@ #define SMC_ENTITY_RESERVED 5 /* Reserved for future use */ #define SMC_ENTITY_TRUSTED_APP 48 /* Trusted Application calls */ #define SMC_ENTITY_TRUSTED_OS 50 /* Trusted OS calls */ +#define SMC_ENTITY_LOGGING 51 /* Used for secure -> nonsecure logging */ #define SMC_ENTITY_SECURE_MONITOR 60 /* Trusted OS calls internal to secure monitor */ /* FC = Fast call, SC = Standard call */ From b65d347fc60734afcfcab96bcce031345a8ddc66 Mon Sep 17 00:00:00 2001 From: Michael Ryleev Date: Thu, 26 Mar 2015 19:31:25 -0700 Subject: [PATCH 0980/1103] trusty: add couple non-secure memory related helper routines trusty_encode_page_info - encodes page physical address, memory type and other attributes into architecture specific structure that can be parsed by secure side. trusty_call32_mem_buf - can be used by drivers to make specified smc call with physicaly contigues memory buffer as an argument. Memory buffer info in retrieved by trusty_encode_page_info and along with buffer size is encoded into series of 32-bit smc call parameters. Change-Id: I79aadca85e2329bb89469b4c8f183cf0752f7641 Signed-off-by: Michael Ryleev --- drivers/trusty/Makefile | 1 + drivers/trusty/trusty-mem.c | 134 ++++++++++++++++++++++++++++++++++ include/linux/trusty/trusty.h | 15 ++++ 3 files changed, 150 insertions(+) create mode 100644 drivers/trusty/trusty-mem.c diff --git a/drivers/trusty/Makefile b/drivers/trusty/Makefile index 641ee2a6e830..e527a237cb5d 100644 --- a/drivers/trusty/Makefile +++ b/drivers/trusty/Makefile @@ -8,3 +8,4 @@ obj-$(CONFIG_TRUSTY_FIQ) += trusty-fiq.o obj-$(CONFIG_TRUSTY_FIQ_ARM) += trusty-fiq-arm.o obj-$(CONFIG_TRUSTY_FIQ_ARM64) += trusty-fiq-arm64.o trusty-fiq-arm64-glue.o obj-$(CONFIG_TRUSTY_LOG) += trusty-log.o +obj-$(CONFIG_TRUSTY) += trusty-mem.o diff --git a/drivers/trusty/trusty-mem.c b/drivers/trusty/trusty-mem.c new file mode 100644 index 000000000000..c55ace25beed --- /dev/null +++ b/drivers/trusty/trusty-mem.c @@ -0,0 +1,134 @@ +/* + * Copyright (C) 2015 Google, Inc. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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. + * + */ + +#include +#include +#include +#include + +static int get_mem_attr(struct page *page, pgprot_t pgprot) +{ +#if defined(CONFIG_ARM64) + uint64_t mair; + uint attr_index = (pgprot_val(pgprot) & PTE_ATTRINDX_MASK) >> 2; + + asm ("mrs %0, mair_el1\n" : "=&r" (mair)); + return (mair >> (attr_index * 8)) & 0xff; + +#elif defined(CONFIG_ARM_LPAE) + uint32_t mair; + uint attr_index = ((pgprot_val(pgprot) & L_PTE_MT_MASK) >> 2); + + if (attr_index >= 4) { + attr_index -= 4; + asm volatile("mrc p15, 0, %0, c10, c2, 1\n" : "=&r" (mair)); + } else { + asm volatile("mrc p15, 0, %0, c10, c2, 0\n" : "=&r" (mair)); + } + return (mair >> (attr_index * 8)) & 0xff; + +#elif defined(CONFIG_ARM) + /* check memory type */ + switch (pgprot_val(pgprot) & L_PTE_MT_MASK) { + case L_PTE_MT_WRITEALLOC: + /* Normal: write back write allocate */ + return 0xFF; + + case L_PTE_MT_BUFFERABLE: + /* Normal: non-cacheble */ + return 0x44; + + case L_PTE_MT_WRITEBACK: + /* Normal: writeback, read allocate */ + return 0xEE; + + case L_PTE_MT_WRITETHROUGH: + /* Normal: write through */ + return 0xAA; + + case L_PTE_MT_UNCACHED: + /* strongly ordered */ + return 0x00; + + case L_PTE_MT_DEV_SHARED: + case L_PTE_MT_DEV_NONSHARED: + /* device */ + return 0x04; + + default: + return -EINVAL; + } +#else + return 0; +#endif +} + +int trusty_encode_page_info(struct ns_mem_page_info *inf, + struct page *page, pgprot_t pgprot) +{ + int mem_attr; + uint64_t pte; + + if (!inf || !page) + return -EINVAL; + + /* get physical address */ + pte = (uint64_t) page_to_phys(page); + + /* get memory attributes */ + mem_attr = get_mem_attr(page, pgprot); + if (mem_attr < 0) + return mem_attr; + + /* add other attributes */ +#if defined(CONFIG_ARM64) || defined(CONFIG_ARM_LPAE) + pte |= pgprot_val(pgprot); +#elif defined(CONFIG_ARM) + if (pgprot_val(pgprot) & L_PTE_USER) + pte |= (1 << 6); + if (pgprot_val(pgprot) & L_PTE_RDONLY) + pte |= (1 << 7); + if (pgprot_val(pgprot) & L_PTE_SHARED) + pte |= (3 << 8); /* inner sharable */ +#endif + + inf->attr = (pte & 0x0000FFFFFFFFFFFFull) | ((uint64_t)mem_attr << 48); + return 0; +} + +int trusty_call32_mem_buf(struct device *dev, u32 smcnr, + struct page *page, u32 size, + pgprot_t pgprot) +{ + int ret; + struct ns_mem_page_info pg_inf; + + if (!dev || !page) + return -EINVAL; + + ret = trusty_encode_page_info(&pg_inf, page, pgprot); + if (ret) + return ret; + + if (SMC_IS_FASTCALL(smcnr)) { + return trusty_fast_call32(dev, smcnr, + (u32)pg_inf.attr, + (u32)(pg_inf.attr >> 32), size); + } else { + return trusty_std_call32(dev, smcnr, + (u32)pg_inf.attr, + (u32)(pg_inf.attr >> 32), size); + } +} + diff --git a/include/linux/trusty/trusty.h b/include/linux/trusty/trusty.h index abb77f1db74d..d084d9d68a7b 100644 --- a/include/linux/trusty/trusty.h +++ b/include/linux/trusty/trusty.h @@ -16,6 +16,9 @@ #include #include +#include +#include + #ifdef CONFIG_TRUSTY s32 trusty_std_call32(struct device *dev, u32 smcnr, u32 a0, u32 a1, u32 a2); @@ -53,4 +56,16 @@ int trusty_call_notifier_register(struct device *dev, int trusty_call_notifier_unregister(struct device *dev, struct notifier_block *n); const char *trusty_version_str_get(struct device *dev); + +struct ns_mem_page_info { + uint64_t attr; +}; + +int trusty_encode_page_info(struct ns_mem_page_info *inf, + struct page *page, pgprot_t pgprot); + +int trusty_call32_mem_buf(struct device *dev, u32 smcnr, + struct page *page, u32 size, + pgprot_t pgprot); + #endif From 5ca739c911e5939c4f2bc9b2c025dad1d0899812 Mon Sep 17 00:00:00 2001 From: Michael Ryleev Date: Mon, 30 Mar 2015 12:43:59 -0700 Subject: [PATCH 0981/1103] trusty: add trusty virtio driver Trusty virtio driver is responsible for management an interaction with virtio devices exposed by Trusty. During initialization, this driver makes an smc call to retrieve Trusty virtio device descriptor from secure side, parses it then instantiates and configures the specified set of virtio devices. Change-Id: I3bac25d861db55a0f1408a4344ff5f8e53a75d44 Signed-off-by: Michael Ryleev --- drivers/trusty/Kconfig | 6 + drivers/trusty/Makefile | 1 + drivers/trusty/trusty-virtio.c | 697 +++++++++++++++++++++++++++++++++ include/linux/trusty/smcall.h | 8 + 4 files changed, 712 insertions(+) create mode 100644 drivers/trusty/trusty-virtio.c diff --git a/drivers/trusty/Kconfig b/drivers/trusty/Kconfig index ea75813254c0..2255c0a9a815 100644 --- a/drivers/trusty/Kconfig +++ b/drivers/trusty/Kconfig @@ -33,4 +33,10 @@ config TRUSTY_LOG depends on TRUSTY default y +config TRUSTY_VIRTIO + tristate "Trusty virtio support" + depends on TRUSTY + select VIRTIO + default y + endmenu diff --git a/drivers/trusty/Makefile b/drivers/trusty/Makefile index e527a237cb5d..beb89a87f115 100644 --- a/drivers/trusty/Makefile +++ b/drivers/trusty/Makefile @@ -9,3 +9,4 @@ obj-$(CONFIG_TRUSTY_FIQ_ARM) += trusty-fiq-arm.o obj-$(CONFIG_TRUSTY_FIQ_ARM64) += trusty-fiq-arm64.o trusty-fiq-arm64-glue.o obj-$(CONFIG_TRUSTY_LOG) += trusty-log.o obj-$(CONFIG_TRUSTY) += trusty-mem.o +obj-$(CONFIG_TRUSTY_VIRTIO) += trusty-virtio.o diff --git a/drivers/trusty/trusty-virtio.c b/drivers/trusty/trusty-virtio.c new file mode 100644 index 000000000000..fabbf29bffcc --- /dev/null +++ b/drivers/trusty/trusty-virtio.c @@ -0,0 +1,697 @@ +/* + * Trusty Virtio driver + * + * Copyright (C) 2015 Google, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * 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. + */ +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include + +#include + +#define RSC_DESCR_VER 1 + +struct trusty_vdev; + +struct trusty_ctx { + struct device *dev; + void *shared_va; + size_t shared_sz; + struct work_struct check_vqs; + struct work_struct kick_vqs; + struct notifier_block call_notifier; + struct list_head vdev_list; + struct mutex mlock; /* protects vdev_list */ +}; + +struct trusty_vring { + void *vaddr; + phys_addr_t paddr; + size_t size; + uint align; + uint elem_num; + u32 notifyid; + atomic_t needs_kick; + struct fw_rsc_vdev_vring *vr_descr; + struct virtqueue *vq; + struct trusty_vdev *tvdev; +}; + +struct trusty_vdev { + struct list_head node; + struct virtio_device vdev; + struct trusty_ctx *tctx; + u32 notifyid; + uint config_len; + void *config; + struct fw_rsc_vdev *vdev_descr; + uint vring_num; + struct trusty_vring vrings[0]; +}; + +#define vdev_to_tvdev(vd) container_of((vd), struct trusty_vdev, vdev) + +static void check_all_vqs(struct work_struct *work) +{ + uint i; + struct trusty_ctx *tctx = container_of(work, struct trusty_ctx, + check_vqs); + struct trusty_vdev *tvdev; + + list_for_each_entry(tvdev, &tctx->vdev_list, node) { + for (i = 0; i < tvdev->vring_num; i++) + vring_interrupt(0, tvdev->vrings[i].vq); + } +} + +static int trusty_call_notify(struct notifier_block *nb, + unsigned long action, void *data) +{ + struct trusty_ctx *tctx; + + if (action != TRUSTY_CALL_RETURNED) + return NOTIFY_DONE; + + tctx = container_of(nb, struct trusty_ctx, call_notifier); + schedule_work(&tctx->check_vqs); + + return NOTIFY_OK; +} + +static void kick_vq(struct trusty_ctx *tctx, + struct trusty_vdev *tvdev, + struct trusty_vring *tvr) +{ + int ret; + + dev_dbg(tctx->dev, "%s: vdev_id=%d: vq_id=%d\n", + __func__, tvdev->notifyid, tvr->notifyid); + + ret = trusty_std_call32(tctx->dev->parent, SMC_SC_VDEV_KICK_VQ, + tvdev->notifyid, tvr->notifyid, 0); + if (ret) { + dev_err(tctx->dev, "vq notify (%d, %d) returned %d\n", + tvdev->notifyid, tvr->notifyid, ret); + } +} + +static void kick_vqs(struct work_struct *work) +{ + uint i; + struct trusty_vdev *tvdev; + struct trusty_ctx *tctx = container_of(work, struct trusty_ctx, + kick_vqs); + mutex_lock(&tctx->mlock); + list_for_each_entry(tvdev, &tctx->vdev_list, node) { + for (i = 0; i < tvdev->vring_num; i++) { + struct trusty_vring *tvr = &tvdev->vrings[i]; + if (atomic_xchg(&tvr->needs_kick, 0)) + kick_vq(tctx, tvdev, tvr); + } + } + mutex_unlock(&tctx->mlock); +} + +static bool trusty_virtio_notify(struct virtqueue *vq) +{ + struct trusty_vring *tvr = vq->priv; + struct trusty_vdev *tvdev = tvr->tvdev; + struct trusty_ctx *tctx = tvdev->tctx; + + atomic_set(&tvr->needs_kick, 1); + schedule_work(&tctx->kick_vqs); + + return true; +} + +static int trusty_load_device_descr(struct trusty_ctx *tctx, + void *va, size_t sz) +{ + int ret; + + dev_dbg(tctx->dev, "%s: %zu bytes @ %p\n", __func__, sz, va); + + ret = trusty_call32_mem_buf(tctx->dev->parent, + SMC_SC_VIRTIO_GET_DESCR, + virt_to_page(va), sz, PAGE_KERNEL); + if (ret < 0) { + dev_err(tctx->dev, "%s: virtio get descr returned (%d)\n", + __func__, ret); + return -ENODEV; + } + return ret; +} + +static void trusty_virtio_stop(struct trusty_ctx *tctx, void *va, size_t sz) +{ + int ret; + + dev_dbg(tctx->dev, "%s: %zu bytes @ %p\n", __func__, sz, va); + + ret = trusty_call32_mem_buf(tctx->dev->parent, SMC_SC_VIRTIO_STOP, + virt_to_page(va), sz, PAGE_KERNEL); + if (ret) { + dev_err(tctx->dev, "%s: virtio done returned (%d)\n", + __func__, ret); + return; + } +} + +static int trusty_virtio_start(struct trusty_ctx *tctx, + void *va, size_t sz) +{ + int ret; + + dev_dbg(tctx->dev, "%s: %zu bytes @ %p\n", __func__, sz, va); + + ret = trusty_call32_mem_buf(tctx->dev->parent, SMC_SC_VIRTIO_START, + virt_to_page(va), sz, PAGE_KERNEL); + if (ret) { + dev_err(tctx->dev, "%s: virtio start returned (%d)\n", + __func__, ret); + return -ENODEV; + } + return 0; +} + +static void trusty_virtio_reset(struct virtio_device *vdev) +{ + struct trusty_vdev *tvdev = vdev_to_tvdev(vdev); + struct trusty_ctx *tctx = tvdev->tctx; + + dev_dbg(&vdev->dev, "reset vdev_id=%d\n", tvdev->notifyid); + trusty_std_call32(tctx->dev->parent, SMC_SC_VDEV_RESET, + tvdev->notifyid, 0, 0); +} + +static u64 trusty_virtio_get_features(struct virtio_device *vdev) +{ + struct trusty_vdev *tvdev = vdev_to_tvdev(vdev); + return tvdev->vdev_descr->dfeatures; +} + +static int trusty_virtio_finalize_features(struct virtio_device *vdev) +{ + struct trusty_vdev *tvdev = vdev_to_tvdev(vdev); + + /* Make sure we don't have any features > 32 bits! */ + BUG_ON((u32)vdev->features != vdev->features); + + tvdev->vdev_descr->gfeatures = vdev->features; + return 0; +} + +static void trusty_virtio_get_config(struct virtio_device *vdev, + unsigned offset, void *buf, + unsigned len) +{ + struct trusty_vdev *tvdev = vdev_to_tvdev(vdev); + + dev_dbg(&vdev->dev, "%s: %d bytes @ offset %d\n", + __func__, len, offset); + + if (tvdev->config) { + if (offset + len <= tvdev->config_len) + memcpy(buf, tvdev->config + offset, len); + } +} + +static void trusty_virtio_set_config(struct virtio_device *vdev, + unsigned offset, const void *buf, + unsigned len) +{ + dev_dbg(&vdev->dev, "%s\n", __func__); +} + +static u8 trusty_virtio_get_status(struct virtio_device *vdev) +{ + struct trusty_vdev *tvdev = vdev_to_tvdev(vdev); + return tvdev->vdev_descr->status; +} + +static void trusty_virtio_set_status(struct virtio_device *vdev, u8 status) +{ + struct trusty_vdev *tvdev = vdev_to_tvdev(vdev); + tvdev->vdev_descr->status = status; +} + +static void _del_vqs(struct virtio_device *vdev) +{ + uint i; + struct trusty_vdev *tvdev = vdev_to_tvdev(vdev); + struct trusty_vring *tvr = &tvdev->vrings[0]; + + for (i = 0; i < tvdev->vring_num; i++, tvr++) { + /* delete vq */ + if (tvr->vq) { + vring_del_virtqueue(tvr->vq); + tvr->vq = NULL; + } + /* delete vring */ + if (tvr->vaddr) { + free_pages_exact(tvr->vaddr, tvr->size); + tvr->vaddr = NULL; + } + } +} + +static void trusty_virtio_del_vqs(struct virtio_device *vdev) +{ + dev_dbg(&vdev->dev, "%s\n", __func__); + _del_vqs(vdev); +} + + +static struct virtqueue *_find_vq(struct virtio_device *vdev, + unsigned id, + void (*callback)(struct virtqueue *vq), + const char *name) +{ + struct trusty_vring *tvr; + struct trusty_vdev *tvdev = vdev_to_tvdev(vdev); + phys_addr_t pa; + + if (!name) + return ERR_PTR(-EINVAL); + + if (id >= tvdev->vring_num) + return ERR_PTR(-EINVAL); + + tvr = &tvdev->vrings[id]; + + /* actual size of vring (in bytes) */ + tvr->size = PAGE_ALIGN(vring_size(tvr->elem_num, tvr->align)); + + /* allocate memory for the vring. */ + tvr->vaddr = alloc_pages_exact(tvr->size, GFP_KERNEL | __GFP_ZERO); + if (!tvr->vaddr) { + dev_err(&vdev->dev, "vring alloc failed\n"); + return ERR_PTR(-ENOMEM); + } + + pa = virt_to_phys(tvr->vaddr); + /* save vring address to shared structure */ + tvr->vr_descr->da = (u32)pa; + /* da field is only 32 bit wide. Use previously unused 'reserved' field + * to store top 32 bits of 64-bit address + */ + tvr->vr_descr->reserved = (u32)(pa >> 32); + + dev_info(&vdev->dev, "vring%d: va(pa) %p(%llx) qsz %d notifyid %d\n", + id, tvr->vaddr, (u64)tvr->paddr, tvr->elem_num, tvr->notifyid); + + tvr->vq = vring_new_virtqueue(id, tvr->elem_num, tvr->align, + vdev, true, tvr->vaddr, + trusty_virtio_notify, callback, name); + if (!tvr->vq) { + dev_err(&vdev->dev, "vring_new_virtqueue %s failed\n", + name); + goto err_new_virtqueue; + } + + tvr->vq->priv = tvr; + + return tvr->vq; + +err_new_virtqueue: + free_pages_exact(tvr->vaddr, tvr->size); + tvr->vaddr = NULL; + return ERR_PTR(-ENOMEM); +} + +static int trusty_virtio_find_vqs(struct virtio_device *vdev, unsigned nvqs, + struct virtqueue *vqs[], + vq_callback_t *callbacks[], + const char *names[]) +{ + uint i; + int ret; + + for (i = 0; i < nvqs; i++) { + vqs[i] = _find_vq(vdev, i, callbacks[i], names[i]); + if (IS_ERR(vqs[i])) { + ret = PTR_ERR(vqs[i]); + _del_vqs(vdev); + return ret; + } + } + return 0; +} + +static const char *trusty_virtio_bus_name(struct virtio_device *vdev) +{ + return "trusty-virtio"; +} + +/* The ops structure which hooks everything together. */ +static const struct virtio_config_ops trusty_virtio_config_ops = { + .get_features = trusty_virtio_get_features, + .finalize_features = trusty_virtio_finalize_features, + .get = trusty_virtio_get_config, + .set = trusty_virtio_set_config, + .get_status = trusty_virtio_get_status, + .set_status = trusty_virtio_set_status, + .reset = trusty_virtio_reset, + .find_vqs = trusty_virtio_find_vqs, + .del_vqs = trusty_virtio_del_vqs, + .bus_name = trusty_virtio_bus_name, +}; + +static int trusty_virtio_add_device(struct trusty_ctx *tctx, + struct fw_rsc_vdev *vdev_descr, + struct fw_rsc_vdev_vring *vr_descr, + void *config) +{ + int i, ret; + struct trusty_vdev *tvdev; + + tvdev = kzalloc(sizeof(struct trusty_vdev) + + vdev_descr->num_of_vrings * sizeof(struct trusty_vring), + GFP_KERNEL); + if (!tvdev) { + dev_err(tctx->dev, "Failed to allocate VDEV\n"); + return -ENOMEM; + } + + /* setup vdev */ + tvdev->tctx = tctx; + tvdev->vdev.dev.parent = tctx->dev; + tvdev->vdev.id.device = vdev_descr->id; + tvdev->vdev.config = &trusty_virtio_config_ops; + tvdev->vdev_descr = vdev_descr; + tvdev->notifyid = vdev_descr->notifyid; + + /* setup config */ + tvdev->config = config; + tvdev->config_len = vdev_descr->config_len; + + /* setup vrings and vdev resource */ + tvdev->vring_num = vdev_descr->num_of_vrings; + + for (i = 0; i < tvdev->vring_num; i++, vr_descr++) { + struct trusty_vring *tvr = &tvdev->vrings[i]; + tvr->tvdev = tvdev; + tvr->vr_descr = vr_descr; + tvr->align = vr_descr->align; + tvr->elem_num = vr_descr->num; + tvr->notifyid = vr_descr->notifyid; + } + + /* register device */ + ret = register_virtio_device(&tvdev->vdev); + if (ret) { + dev_err(tctx->dev, + "Failed (%d) to register device dev type %u\n", + ret, vdev_descr->id); + goto err_register; + } + + /* add it to tracking list */ + list_add_tail(&tvdev->node, &tctx->vdev_list); + + return 0; + +err_register: + kfree(tvdev); + return ret; +} + +static int trusty_parse_device_descr(struct trusty_ctx *tctx, + void *descr_va, size_t descr_sz) +{ + u32 i; + struct resource_table *descr = descr_va; + + if (descr_sz < sizeof(*descr)) { + dev_err(tctx->dev, "descr table is too small (0x%x)\n", + (int)descr_sz); + return -ENODEV; + } + + if (descr->ver != RSC_DESCR_VER) { + dev_err(tctx->dev, "unexpected descr ver (0x%x)\n", + (int)descr->ver); + return -ENODEV; + } + + if (descr_sz < (sizeof(*descr) + descr->num * sizeof(u32))) { + dev_err(tctx->dev, "descr table is too small (0x%x)\n", + (int)descr->ver); + return -ENODEV; + } + + for (i = 0; i < descr->num; i++) { + struct fw_rsc_hdr *hdr; + struct fw_rsc_vdev *vd; + struct fw_rsc_vdev_vring *vr; + void *cfg; + size_t vd_sz; + + u32 offset = descr->offset[i]; + + if (offset >= descr_sz) { + dev_err(tctx->dev, "offset is out of bounds (%u)\n", + (uint)offset); + return -ENODEV; + } + + /* check space for rsc header */ + if ((descr_sz - offset) < sizeof(struct fw_rsc_hdr)) { + dev_err(tctx->dev, "no space for rsc header (%u)\n", + (uint)offset); + return -ENODEV; + } + hdr = (struct fw_rsc_hdr *)((u8 *)descr + offset); + offset += sizeof(struct fw_rsc_hdr); + + /* check type */ + if (hdr->type != RSC_VDEV) { + dev_err(tctx->dev, "unsupported rsc type (%u)\n", + (uint)hdr->type); + continue; + } + + /* got vdev: check space for vdev */ + if ((descr_sz - offset) < sizeof(struct fw_rsc_vdev)) { + dev_err(tctx->dev, "no space for vdev descr (%u)\n", + (uint)offset); + return -ENODEV; + } + vd = (struct fw_rsc_vdev *)((u8 *)descr + offset); + + /* check space for vrings and config area */ + vd_sz = sizeof(struct fw_rsc_vdev) + + vd->num_of_vrings * sizeof(struct fw_rsc_vdev_vring) + + vd->config_len; + + if ((descr_sz - offset) < vd_sz) { + dev_err(tctx->dev, "no space for vdev (%u)\n", + (uint)offset); + return -ENODEV; + } + vr = (struct fw_rsc_vdev_vring *)vd->vring; + cfg = (void *)(vr + vd->num_of_vrings); + + trusty_virtio_add_device(tctx, vd, vr, cfg); + } + + return 0; +} + +static void _remove_devices_locked(struct trusty_ctx *tctx) +{ + struct trusty_vdev *tvdev, *next; + + list_for_each_entry_safe(tvdev, next, &tctx->vdev_list, node) { + list_del(&tvdev->node); + unregister_virtio_device(&tvdev->vdev); + kfree(tvdev); + } +} + +static void trusty_virtio_remove_devices(struct trusty_ctx *tctx) +{ + mutex_lock(&tctx->mlock); + _remove_devices_locked(tctx); + mutex_unlock(&tctx->mlock); +} + +static int trusty_virtio_add_devices(struct trusty_ctx *tctx) +{ + int ret; + void *descr_va; + size_t descr_sz; + size_t descr_buf_sz; + + /* allocate buffer to load device descriptor into */ + descr_buf_sz = PAGE_SIZE; + descr_va = alloc_pages_exact(descr_buf_sz, GFP_KERNEL | __GFP_ZERO); + if (!descr_va) { + dev_err(tctx->dev, "Failed to allocate shared area\n"); + return -ENOMEM; + } + + /* load device descriptors */ + ret = trusty_load_device_descr(tctx, descr_va, descr_buf_sz); + if (ret < 0) { + dev_err(tctx->dev, "failed (%d) to load device descr\n", ret); + goto err_load_descr; + } + + descr_sz = (size_t)ret; + + mutex_lock(&tctx->mlock); + + /* parse device descriptor and add virtio devices */ + ret = trusty_parse_device_descr(tctx, descr_va, descr_sz); + if (ret) { + dev_err(tctx->dev, "failed (%d) to parse device descr\n", ret); + goto err_parse_descr; + } + + /* register call notifier */ + ret = trusty_call_notifier_register(tctx->dev->parent, + &tctx->call_notifier); + if (ret) { + dev_err(tctx->dev, "%s: failed (%d) to register notifier\n", + __func__, ret); + goto err_register_notifier; + } + + /* start virtio */ + ret = trusty_virtio_start(tctx, descr_va, descr_sz); + if (ret) { + dev_err(tctx->dev, "failed (%d) to start virtio\n", ret); + goto err_start_virtio; + } + + /* attach shared area */ + tctx->shared_va = descr_va; + tctx->shared_sz = descr_buf_sz; + + mutex_unlock(&tctx->mlock); + + return 0; + +err_start_virtio: + trusty_call_notifier_unregister(tctx->dev->parent, + &tctx->call_notifier); + cancel_work_sync(&tctx->check_vqs); +err_register_notifier: +err_parse_descr: + _remove_devices_locked(tctx); + mutex_unlock(&tctx->mlock); + cancel_work_sync(&tctx->kick_vqs); + trusty_virtio_stop(tctx, descr_va, descr_sz); +err_load_descr: + free_pages_exact(descr_va, descr_buf_sz); + return ret; +} + +static int trusty_virtio_probe(struct platform_device *pdev) +{ + int ret; + struct trusty_ctx *tctx; + + dev_info(&pdev->dev, "initializing\n"); + + tctx = kzalloc(sizeof(*tctx), GFP_KERNEL); + if (!tctx) { + dev_err(&pdev->dev, "Failed to allocate context\n"); + return -ENOMEM; + } + + tctx->dev = &pdev->dev; + tctx->call_notifier.notifier_call = trusty_call_notify; + mutex_init(&tctx->mlock); + INIT_LIST_HEAD(&tctx->vdev_list); + INIT_WORK(&tctx->check_vqs, check_all_vqs); + INIT_WORK(&tctx->kick_vqs, kick_vqs); + platform_set_drvdata(pdev, tctx); + + ret = trusty_virtio_add_devices(tctx); + if (ret) { + dev_err(&pdev->dev, "Failed to add virtio devices\n"); + goto err_add_devices; + } + + dev_info(&pdev->dev, "initializing done\n"); + return 0; + +err_add_devices: + kfree(tctx); + return ret; +} + +static int trusty_virtio_remove(struct platform_device *pdev) +{ + struct trusty_ctx *tctx = platform_get_drvdata(pdev); + + dev_err(&pdev->dev, "removing\n"); + + /* unregister call notifier and wait until workqueue is done */ + trusty_call_notifier_unregister(tctx->dev->parent, + &tctx->call_notifier); + cancel_work_sync(&tctx->check_vqs); + + /* remove virtio devices */ + trusty_virtio_remove_devices(tctx); + cancel_work_sync(&tctx->kick_vqs); + + /* notify remote that shared area goes away */ + trusty_virtio_stop(tctx, tctx->shared_va, tctx->shared_sz); + + /* free shared area */ + free_pages_exact(tctx->shared_va, tctx->shared_sz); + + /* free context */ + kfree(tctx); + return 0; +} + +static const struct of_device_id trusty_of_match[] = { + { + .compatible = "android,trusty-virtio-v1", + }, +}; + +MODULE_DEVICE_TABLE(of, trusty_of_match); + +static struct platform_driver trusty_virtio_driver = { + .probe = trusty_virtio_probe, + .remove = trusty_virtio_remove, + .driver = { + .name = "trusty-virtio", + .owner = THIS_MODULE, + .of_match_table = trusty_of_match, + }, +}; + +module_platform_driver(trusty_virtio_driver); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("Trusty virtio driver"); diff --git a/include/linux/trusty/smcall.h b/include/linux/trusty/smcall.h index aaad5cee6143..a2be2e3579f3 100644 --- a/include/linux/trusty/smcall.h +++ b/include/linux/trusty/smcall.h @@ -78,4 +78,12 @@ #define SMC_FC_AARCH_SWITCH SMC_FASTCALL_NR (SMC_ENTITY_SECURE_MONITOR, 9) #define SMC_FC_GET_VERSION_STR SMC_FASTCALL_NR (SMC_ENTITY_SECURE_MONITOR, 10) +/* TRUSTED_OS entity calls */ +#define SMC_SC_VIRTIO_GET_DESCR SMC_STDCALL_NR(SMC_ENTITY_TRUSTED_OS, 20) +#define SMC_SC_VIRTIO_START SMC_STDCALL_NR(SMC_ENTITY_TRUSTED_OS, 21) +#define SMC_SC_VIRTIO_STOP SMC_STDCALL_NR(SMC_ENTITY_TRUSTED_OS, 22) + +#define SMC_SC_VDEV_RESET SMC_STDCALL_NR(SMC_ENTITY_TRUSTED_OS, 23) +#define SMC_SC_VDEV_KICK_VQ SMC_STDCALL_NR(SMC_ENTITY_TRUSTED_OS, 24) + #endif /* __LINUX_TRUSTY_SMCALL_H */ From 21684d2a78967497ca2fa5d883e9d12dd1dd47e9 Mon Sep 17 00:00:00 2001 From: Michael Ryleev Date: Wed, 7 Jan 2015 15:47:37 -0800 Subject: [PATCH 0982/1103] trusty: add trusty-ipc driver Trusty IPC driver provides message passing interface between non-secure side (Linux) and secure side running Trusty. It is handling a set of trusty IPC virtio devices instantiated and configured by trusty-virtio driver based on device description retrieved from secure side. Change-Id: I7249384380850dfb8795c0e0d5e39dfb907400c6 Signed-off-by: Michael Ryleev --- drivers/trusty/Kconfig | 10 + drivers/trusty/Makefile | 1 + drivers/trusty/trusty-ipc.c | 1672 +++++++++++++++++++++++++++++ include/linux/trusty/trusty_ipc.h | 88 ++ 4 files changed, 1771 insertions(+) create mode 100644 drivers/trusty/trusty-ipc.c create mode 100644 include/linux/trusty/trusty_ipc.h diff --git a/drivers/trusty/Kconfig b/drivers/trusty/Kconfig index 2255c0a9a815..052cd8e91ab0 100644 --- a/drivers/trusty/Kconfig +++ b/drivers/trusty/Kconfig @@ -39,4 +39,14 @@ config TRUSTY_VIRTIO select VIRTIO default y +config TRUSTY_VIRTIO_IPC + tristate "Trusty Virtio IPC driver" + depends on TRUSTY_VIRTIO + default y + help + This module adds support for communications with Trusty Services + + If you choose to build a module, it'll be called trusty-ipc. + Say N if unsure. + endmenu diff --git a/drivers/trusty/Makefile b/drivers/trusty/Makefile index beb89a87f115..9ca451e50dee 100644 --- a/drivers/trusty/Makefile +++ b/drivers/trusty/Makefile @@ -10,3 +10,4 @@ obj-$(CONFIG_TRUSTY_FIQ_ARM64) += trusty-fiq-arm64.o trusty-fiq-arm64-glue.o obj-$(CONFIG_TRUSTY_LOG) += trusty-log.o obj-$(CONFIG_TRUSTY) += trusty-mem.o obj-$(CONFIG_TRUSTY_VIRTIO) += trusty-virtio.o +obj-$(CONFIG_TRUSTY_VIRTIO_IPC) += trusty-ipc.o diff --git a/drivers/trusty/trusty-ipc.c b/drivers/trusty/trusty-ipc.c new file mode 100644 index 000000000000..06e026344e67 --- /dev/null +++ b/drivers/trusty/trusty-ipc.c @@ -0,0 +1,1672 @@ +/* + * Copyright (C) 2015 Google, Inc. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +#define MAX_DEVICES 4 + +#define REPLY_TIMEOUT 5000 +#define TXBUF_TIMEOUT 15000 + +#define MAX_SRV_NAME_LEN 256 +#define MAX_DEV_NAME_LEN 32 + +#define DEFAULT_MSG_BUF_SIZE PAGE_SIZE +#define DEFAULT_MSG_BUF_ALIGN PAGE_SIZE + +#define TIPC_CTRL_ADDR 53 +#define TIPC_ANY_ADDR 0xFFFFFFFF + +#define TIPC_MIN_LOCAL_ADDR 1024 + +#define TIPC_IOC_MAGIC 'r' +#define TIPC_IOC_CONNECT _IOW(TIPC_IOC_MAGIC, 0x80, char *) +#if defined(CONFIG_COMPAT) +#define TIPC_IOC_CONNECT_COMPAT _IOW(TIPC_IOC_MAGIC, 0x80, \ + compat_uptr_t) +#endif + +struct tipc_virtio_dev; + +struct tipc_dev_config { + u32 msg_buf_max_size; + u32 msg_buf_alignment; + char dev_name[MAX_DEV_NAME_LEN]; +} __packed; + +struct tipc_msg_hdr { + u32 src; + u32 dst; + u32 reserved; + u16 len; + u16 flags; + u8 data[0]; +} __packed; + +enum tipc_ctrl_msg_types { + TIPC_CTRL_MSGTYPE_GO_ONLINE = 1, + TIPC_CTRL_MSGTYPE_GO_OFFLINE, + TIPC_CTRL_MSGTYPE_CONN_REQ, + TIPC_CTRL_MSGTYPE_CONN_RSP, + TIPC_CTRL_MSGTYPE_DISC_REQ, +}; + +struct tipc_ctrl_msg { + u32 type; + u32 body_len; + u8 body[0]; +} __packed; + +struct tipc_conn_req_body { + char name[MAX_SRV_NAME_LEN]; +} __packed; + +struct tipc_conn_rsp_body { + u32 target; + u32 status; + u32 remote; + u32 max_msg_size; + u32 max_msg_cnt; +} __packed; + +struct tipc_disc_req_body { + u32 target; +} __packed; + +struct tipc_cdev_node { + struct cdev cdev; + struct device *dev; + unsigned int minor; +}; + +enum tipc_device_state { + VDS_OFFLINE = 0, + VDS_ONLINE, + VDS_DEAD, +}; + +struct tipc_virtio_dev { + struct kref refcount; + struct mutex lock; /* protects access to this device */ + struct virtio_device *vdev; + struct virtqueue *rxvq; + struct virtqueue *txvq; + uint msg_buf_cnt; + uint msg_buf_max_cnt; + size_t msg_buf_max_sz; + uint free_msg_buf_cnt; + struct list_head free_buf_list; + wait_queue_head_t sendq; + struct idr addr_idr; + enum tipc_device_state state; + struct tipc_cdev_node cdev_node; + char cdev_name[MAX_DEV_NAME_LEN]; +}; + +enum tipc_chan_state { + TIPC_DISCONNECTED = 0, + TIPC_CONNECTING, + TIPC_CONNECTED, + TIPC_STALE, +}; + +struct tipc_chan { + struct mutex lock; /* protects channel state */ + struct kref refcount; + enum tipc_chan_state state; + struct tipc_virtio_dev *vds; + const struct tipc_chan_ops *ops; + void *ops_arg; + u32 remote; + u32 local; + u32 max_msg_size; + u32 max_msg_cnt; + char srv_name[MAX_SRV_NAME_LEN]; +}; + +static struct class *tipc_class; +static unsigned int tipc_major; + +struct virtio_device *default_vdev; + +static DEFINE_IDR(tipc_devices); +static DEFINE_MUTEX(tipc_devices_lock); + +static int _match_any(int id, void *p, void *data) +{ + return id; +} + +static int _match_data(int id, void *p, void *data) +{ + return (p == data); +} + +static void *_alloc_shareable_mem(size_t sz, phys_addr_t *ppa, gfp_t gfp) +{ + return alloc_pages_exact(sz, gfp); +} + +static void _free_shareable_mem(size_t sz, void *va, phys_addr_t pa) +{ + free_pages_exact(va, sz); +} + +static struct tipc_msg_buf *_alloc_msg_buf(size_t sz) +{ + struct tipc_msg_buf *mb; + + /* allocate tracking structure */ + mb = kzalloc(sizeof(struct tipc_msg_buf), GFP_KERNEL); + if (!mb) + return NULL; + + /* allocate buffer that can be shared with secure world */ + mb->buf_va = _alloc_shareable_mem(sz, &mb->buf_pa, GFP_KERNEL); + if (!mb->buf_va) + goto err_alloc; + + mb->buf_sz = sz; + + return mb; + +err_alloc: + kfree(mb); + return NULL; +} + +static void _free_msg_buf(struct tipc_msg_buf *mb) +{ + _free_shareable_mem(mb->buf_sz, mb->buf_va, mb->buf_pa); + kfree(mb); +} + +static void _free_msg_buf_list(struct list_head *list) +{ + struct tipc_msg_buf *mb = NULL; + + mb = list_first_entry_or_null(list, struct tipc_msg_buf, node); + while (mb) { + list_del(&mb->node); + _free_msg_buf(mb); + mb = list_first_entry_or_null(list, struct tipc_msg_buf, node); + } +} + +static inline void mb_reset(struct tipc_msg_buf *mb) +{ + mb->wpos = 0; + mb->rpos = 0; +} + +static void _free_chan(struct kref *kref) +{ + struct tipc_chan *ch = container_of(kref, struct tipc_chan, refcount); + kfree(ch); +} + +static void _free_vds(struct kref *kref) +{ + struct tipc_virtio_dev *vds = + container_of(kref, struct tipc_virtio_dev, refcount); + kfree(vds); +} + +static struct tipc_msg_buf *vds_alloc_msg_buf(struct tipc_virtio_dev *vds) +{ + return _alloc_msg_buf(vds->msg_buf_max_sz); +} + +static void vds_free_msg_buf(struct tipc_virtio_dev *vds, + struct tipc_msg_buf *mb) +{ + _free_msg_buf(mb); +} + +static bool _put_txbuf_locked(struct tipc_virtio_dev *vds, + struct tipc_msg_buf *mb) +{ + list_add_tail(&mb->node, &vds->free_buf_list); + return vds->free_msg_buf_cnt++ == 0; +} + +static struct tipc_msg_buf *_get_txbuf_locked(struct tipc_virtio_dev *vds) +{ + struct tipc_msg_buf *mb; + + if (vds->state != VDS_ONLINE) + return ERR_PTR(-ENODEV); + + if (vds->free_msg_buf_cnt) { + /* take it out of free list */ + mb = list_first_entry(&vds->free_buf_list, + struct tipc_msg_buf, node); + list_del(&mb->node); + vds->free_msg_buf_cnt--; + } else { + if (vds->msg_buf_cnt >= vds->msg_buf_max_cnt) + return ERR_PTR(-EAGAIN); + + /* try to allocate it */ + mb = _alloc_msg_buf(vds->msg_buf_max_sz); + if (!mb) + return ERR_PTR(-ENOMEM); + + vds->msg_buf_cnt++; + } + return mb; +} + +static struct tipc_msg_buf *_vds_get_txbuf(struct tipc_virtio_dev *vds) +{ + struct tipc_msg_buf *mb; + + mutex_lock(&vds->lock); + mb = _get_txbuf_locked(vds); + mutex_unlock(&vds->lock); + + return mb; +} + +static void vds_put_txbuf(struct tipc_virtio_dev *vds, struct tipc_msg_buf *mb) +{ + if (!vds) + return; + + mutex_lock(&vds->lock); + _put_txbuf_locked(vds, mb); + wake_up_interruptible(&vds->sendq); + mutex_unlock(&vds->lock); +} + +static struct tipc_msg_buf *vds_get_txbuf(struct tipc_virtio_dev *vds, + long timeout) +{ + struct tipc_msg_buf *mb; + + if (!vds) + return ERR_PTR(-EINVAL); + + mb = _vds_get_txbuf(vds); + + if ((PTR_ERR(mb) == -EAGAIN) && timeout) { + DEFINE_WAIT_FUNC(wait, woken_wake_function); + + timeout = msecs_to_jiffies(timeout); + add_wait_queue(&vds->sendq, &wait); + for (;;) { + timeout = wait_woken(&wait, TASK_INTERRUPTIBLE, + timeout); + if (!timeout) { + mb = ERR_PTR(-ETIMEDOUT); + break; + } + + if (signal_pending(current)) { + mb = ERR_PTR(-ERESTARTSYS); + break; + } + + mb = _vds_get_txbuf(vds); + if (PTR_ERR(mb) != -EAGAIN) + break; + } + remove_wait_queue(&vds->sendq, &wait); + } + + if (IS_ERR(mb)) + return mb; + + BUG_ON(!mb); + + /* reset and reserve space for message header */ + mb_reset(mb); + mb_put_data(mb, sizeof(struct tipc_msg_hdr)); + + return mb; +} + +static int vds_queue_txbuf(struct tipc_virtio_dev *vds, + struct tipc_msg_buf *mb) +{ + int err; + struct scatterlist sg; + bool need_notify = false; + + if (!vds) + return -EINVAL; + + mutex_lock(&vds->lock); + if (vds->state == VDS_ONLINE) { + sg_init_one(&sg, mb->buf_va, mb->wpos); + err = virtqueue_add_outbuf(vds->txvq, &sg, 1, mb, GFP_KERNEL); + need_notify = virtqueue_kick_prepare(vds->txvq); + } else { + err = -ENODEV; + } + mutex_unlock(&vds->lock); + + if (need_notify) + virtqueue_notify(vds->txvq); + + return err; +} + +static int vds_add_channel(struct tipc_virtio_dev *vds, + struct tipc_chan *chan) +{ + int ret; + + mutex_lock(&vds->lock); + if (vds->state == VDS_ONLINE) { + ret = idr_alloc(&vds->addr_idr, chan, + TIPC_MIN_LOCAL_ADDR, TIPC_ANY_ADDR - 1, + GFP_KERNEL); + if (ret > 0) { + chan->local = ret; + kref_get(&chan->refcount); + ret = 0; + } + } else { + ret = -EINVAL; + } + mutex_unlock(&vds->lock); + + return ret; +} + +static void vds_del_channel(struct tipc_virtio_dev *vds, + struct tipc_chan *chan) +{ + mutex_lock(&vds->lock); + if (chan->local) { + idr_remove(&vds->addr_idr, chan->local); + chan->local = 0; + chan->remote = 0; + kref_put(&chan->refcount, _free_chan); + } + mutex_unlock(&vds->lock); +} + +static struct tipc_chan *vds_lookup_channel(struct tipc_virtio_dev *vds, + u32 addr) +{ + int id; + struct tipc_chan *chan = NULL; + + mutex_lock(&vds->lock); + if (addr == TIPC_ANY_ADDR) { + id = idr_for_each(&vds->addr_idr, _match_any, NULL); + if (id > 0) + chan = idr_find(&vds->addr_idr, id); + } else { + chan = idr_find(&vds->addr_idr, addr); + } + if (chan) + kref_get(&chan->refcount); + mutex_unlock(&vds->lock); + + return chan; +} + +static struct tipc_chan *vds_create_channel(struct tipc_virtio_dev *vds, + const struct tipc_chan_ops *ops, + void *ops_arg) +{ + int ret; + struct tipc_chan *chan = NULL; + + if (!vds) + return ERR_PTR(-ENOENT); + + if (!ops) + return ERR_PTR(-EINVAL); + + chan = kzalloc(sizeof(*chan), GFP_KERNEL); + if (!chan) + return ERR_PTR(-ENOMEM); + + kref_get(&vds->refcount); + chan->vds = vds; + chan->ops = ops; + chan->ops_arg = ops_arg; + mutex_init(&chan->lock); + kref_init(&chan->refcount); + chan->state = TIPC_DISCONNECTED; + + ret = vds_add_channel(vds, chan); + if (ret) { + kfree(chan); + kref_put(&vds->refcount, _free_vds); + return ERR_PTR(ret); + } + + return chan; +} + +static void fill_msg_hdr(struct tipc_msg_buf *mb, u32 src, u32 dst) +{ + struct tipc_msg_hdr *hdr = mb_get_data(mb, sizeof(*hdr)); + + hdr->src = src; + hdr->dst = dst; + hdr->len = mb_avail_data(mb); + hdr->flags = 0; + hdr->reserved = 0; +} + +/*****************************************************************************/ + +struct tipc_chan *tipc_create_channel(struct device *dev, + const struct tipc_chan_ops *ops, + void *ops_arg) +{ + struct virtio_device *vd; + struct tipc_chan *chan; + struct tipc_virtio_dev *vds; + + mutex_lock(&tipc_devices_lock); + if (dev) { + vd = container_of(dev, struct virtio_device, dev); + } else { + vd = default_vdev; + if (!vd) { + mutex_unlock(&tipc_devices_lock); + return ERR_PTR(-ENOENT); + } + } + vds = vd->priv; + kref_get(&vds->refcount); + mutex_unlock(&tipc_devices_lock); + + chan = vds_create_channel(vds, ops, ops_arg); + kref_put(&vds->refcount, _free_vds); + return chan; +} +EXPORT_SYMBOL(tipc_create_channel); + +struct tipc_msg_buf *tipc_chan_get_rxbuf(struct tipc_chan *chan) +{ + return vds_alloc_msg_buf(chan->vds); +} +EXPORT_SYMBOL(tipc_chan_get_rxbuf); + +void tipc_chan_put_rxbuf(struct tipc_chan *chan, struct tipc_msg_buf *mb) +{ + vds_free_msg_buf(chan->vds, mb); +} +EXPORT_SYMBOL(tipc_chan_put_rxbuf); + +struct tipc_msg_buf *tipc_chan_get_txbuf_timeout(struct tipc_chan *chan, + long timeout) +{ + return vds_get_txbuf(chan->vds, timeout); +} +EXPORT_SYMBOL(tipc_chan_get_txbuf_timeout); + +void tipc_chan_put_txbuf(struct tipc_chan *chan, struct tipc_msg_buf *mb) +{ + vds_put_txbuf(chan->vds, mb); +} +EXPORT_SYMBOL(tipc_chan_put_txbuf); + +int tipc_chan_queue_msg(struct tipc_chan *chan, struct tipc_msg_buf *mb) +{ + int err; + + mutex_lock(&chan->lock); + switch (chan->state) { + case TIPC_CONNECTED: + fill_msg_hdr(mb, chan->local, chan->remote); + err = vds_queue_txbuf(chan->vds, mb); + if (err) { + /* this should never happen */ + pr_err("%s: failed to queue tx buffer (%d)\n", + __func__, err); + } + break; + case TIPC_DISCONNECTED: + case TIPC_CONNECTING: + err = -ENOTCONN; + break; + case TIPC_STALE: + err = -ESHUTDOWN; + break; + default: + err = -EBADFD; + pr_err("%s: unexpected channel state %d\n", + __func__, chan->state); + } + mutex_unlock(&chan->lock); + return err; +} +EXPORT_SYMBOL(tipc_chan_queue_msg); + + +int tipc_chan_connect(struct tipc_chan *chan, const char *name) +{ + int err; + struct tipc_ctrl_msg *msg; + struct tipc_conn_req_body *body; + struct tipc_msg_buf *txbuf; + + txbuf = vds_get_txbuf(chan->vds, TXBUF_TIMEOUT); + if (IS_ERR(txbuf)) + return PTR_ERR(txbuf); + + /* reserve space for connection request control message */ + msg = mb_put_data(txbuf, sizeof(*msg) + sizeof(*body)); + body = (struct tipc_conn_req_body *)msg->body; + + /* fill message */ + msg->type = TIPC_CTRL_MSGTYPE_CONN_REQ; + msg->body_len = sizeof(*body); + + strncpy(body->name, name, sizeof(body->name)); + body->name[sizeof(body->name)-1] = '\0'; + + mutex_lock(&chan->lock); + switch (chan->state) { + case TIPC_DISCONNECTED: + /* save service name we are connecting to */ + strcpy(chan->srv_name, body->name); + + fill_msg_hdr(txbuf, chan->local, TIPC_CTRL_ADDR); + err = vds_queue_txbuf(chan->vds, txbuf); + if (err) { + /* this should never happen */ + pr_err("%s: failed to queue tx buffer (%d)\n", + __func__, err); + } else { + chan->state = TIPC_CONNECTING; + txbuf = NULL; /* prevents discarding buffer */ + } + break; + case TIPC_CONNECTED: + case TIPC_CONNECTING: + /* check if we are trying to connect to the same service */ + if (strcmp(chan->srv_name, body->name) == 0) + err = 0; + else + if (chan->state == TIPC_CONNECTING) + err = -EALREADY; /* in progress */ + else + err = -EISCONN; /* already connected */ + break; + + case TIPC_STALE: + err = -ESHUTDOWN; + break; + default: + err = -EBADFD; + pr_err("%s: unexpected channel state %d\n", + __func__, chan->state); + break; + } + mutex_unlock(&chan->lock); + + if (txbuf) + tipc_chan_put_txbuf(chan, txbuf); /* discard it */ + + return err; +} +EXPORT_SYMBOL(tipc_chan_connect); + +int tipc_chan_shutdown(struct tipc_chan *chan) +{ + int err; + struct tipc_ctrl_msg *msg; + struct tipc_disc_req_body *body; + struct tipc_msg_buf *txbuf = NULL; + + /* get tx buffer */ + txbuf = vds_get_txbuf(chan->vds, TXBUF_TIMEOUT); + if (IS_ERR(txbuf)) + return PTR_ERR(txbuf); + + mutex_lock(&chan->lock); + if (chan->state == TIPC_CONNECTED || chan->state == TIPC_CONNECTING) { + /* reserve space for disconnect request control message */ + msg = mb_put_data(txbuf, sizeof(*msg) + sizeof(*body)); + body = (struct tipc_disc_req_body *)msg->body; + + msg->type = TIPC_CTRL_MSGTYPE_DISC_REQ; + msg->body_len = sizeof(*body); + body->target = chan->remote; + + fill_msg_hdr(txbuf, chan->local, TIPC_CTRL_ADDR); + err = vds_queue_txbuf(chan->vds, txbuf); + if (err) { + /* this should never happen */ + pr_err("%s: failed to queue tx buffer (%d)\n", + __func__, err); + } + } else { + err = -ENOTCONN; + } + chan->state = TIPC_STALE; + mutex_unlock(&chan->lock); + + if (err) { + /* release buffer */ + tipc_chan_put_txbuf(chan, txbuf); + } + + return err; +} +EXPORT_SYMBOL(tipc_chan_shutdown); + +void tipc_chan_destroy(struct tipc_chan *chan) +{ + mutex_lock(&chan->lock); + if (chan->vds) { + vds_del_channel(chan->vds, chan); + kref_put(&chan->vds->refcount, _free_vds); + chan->vds = NULL; + } + mutex_unlock(&chan->lock); + kref_put(&chan->refcount, _free_chan); +} +EXPORT_SYMBOL(tipc_chan_destroy); + +/***************************************************************************/ + +struct tipc_dn_chan { + int state; + struct mutex lock; /* protects rx_msg_queue list and channel state */ + struct tipc_chan *chan; + wait_queue_head_t readq; + struct completion reply_comp; + struct list_head rx_msg_queue; +}; + +static int dn_wait_for_reply(struct tipc_dn_chan *dn, int timeout) +{ + int ret; + + ret = wait_for_completion_interruptible_timeout(&dn->reply_comp, + msecs_to_jiffies(timeout)); + if (ret < 0) + return ret; + + mutex_lock(&dn->lock); + if (!ret) { + /* no reply from remote */ + dn->state = TIPC_STALE; + ret = -ETIMEDOUT; + } else { + /* got reply */ + if (dn->state == TIPC_CONNECTED) + ret = 0; + else if (dn->state == TIPC_DISCONNECTED) + if (!list_empty(&dn->rx_msg_queue)) + ret = 0; + else + ret = -ENOTCONN; + else + ret = -EIO; + } + mutex_unlock(&dn->lock); + + return ret; +} + +struct tipc_msg_buf *dn_handle_msg(void *data, struct tipc_msg_buf *rxbuf) +{ + struct tipc_dn_chan *dn = data; + struct tipc_msg_buf *newbuf = rxbuf; + + mutex_lock(&dn->lock); + if (dn->state == TIPC_CONNECTED) { + /* get new buffer */ + newbuf = tipc_chan_get_rxbuf(dn->chan); + if (newbuf) { + /* queue an old buffer and return a new one */ + list_add_tail(&rxbuf->node, &dn->rx_msg_queue); + wake_up_interruptible(&dn->readq); + } else { + /* + * return an old buffer effectively discarding + * incoming message + */ + pr_err("%s: discard incoming message\n", __func__); + newbuf = rxbuf; + } + } + mutex_unlock(&dn->lock); + + return newbuf; +} + +static void dn_connected(struct tipc_dn_chan *dn) +{ + mutex_lock(&dn->lock); + dn->state = TIPC_CONNECTED; + + /* complete all pending */ + complete(&dn->reply_comp); + + mutex_unlock(&dn->lock); +} + +static void dn_disconnected(struct tipc_dn_chan *dn) +{ + mutex_lock(&dn->lock); + dn->state = TIPC_DISCONNECTED; + + /* complete all pending */ + complete(&dn->reply_comp); + + /* wakeup all readers */ + wake_up_interruptible_all(&dn->readq); + + mutex_unlock(&dn->lock); +} + +static void dn_shutdown(struct tipc_dn_chan *dn) +{ + mutex_lock(&dn->lock); + + /* set state to STALE */ + dn->state = TIPC_STALE; + + /* complete all pending */ + complete(&dn->reply_comp); + + /* wakeup all readers */ + wake_up_interruptible_all(&dn->readq); + + mutex_unlock(&dn->lock); +} + +static void dn_handle_event(void *data, int event) +{ + struct tipc_dn_chan *dn = data; + + switch (event) { + case TIPC_CHANNEL_SHUTDOWN: + dn_shutdown(dn); + break; + + case TIPC_CHANNEL_DISCONNECTED: + dn_disconnected(dn); + break; + + case TIPC_CHANNEL_CONNECTED: + dn_connected(dn); + break; + + default: + pr_err("%s: unhandled event %d\n", __func__, event); + break; + } +} + +static struct tipc_chan_ops _dn_ops = { + .handle_msg = dn_handle_msg, + .handle_event = dn_handle_event, +}; + +#define cdev_to_cdn(c) container_of((c), struct tipc_cdev_node, cdev) +#define cdn_to_vds(cdn) container_of((cdn), struct tipc_virtio_dev, cdev_node) + +static struct tipc_virtio_dev *_dn_lookup_vds(struct tipc_cdev_node *cdn) +{ + int ret; + struct tipc_virtio_dev *vds = NULL; + + mutex_lock(&tipc_devices_lock); + ret = idr_for_each(&tipc_devices, _match_data, cdn); + if (ret) { + vds = cdn_to_vds(cdn); + kref_get(&vds->refcount); + } + mutex_unlock(&tipc_devices_lock); + return vds; +} + +static int tipc_open(struct inode *inode, struct file *filp) +{ + int ret; + struct tipc_virtio_dev *vds; + struct tipc_dn_chan *dn; + struct tipc_cdev_node *cdn = cdev_to_cdn(inode->i_cdev); + + vds = _dn_lookup_vds(cdn); + if (!vds) { + ret = -ENOENT; + goto err_vds_lookup; + } + + dn = kzalloc(sizeof(*dn), GFP_KERNEL); + if (!dn) { + ret = -ENOMEM; + goto err_alloc_chan; + } + + mutex_init(&dn->lock); + init_waitqueue_head(&dn->readq); + init_completion(&dn->reply_comp); + INIT_LIST_HEAD(&dn->rx_msg_queue); + + dn->state = TIPC_DISCONNECTED; + + dn->chan = vds_create_channel(vds, &_dn_ops, dn); + if (IS_ERR(dn->chan)) { + ret = PTR_ERR(dn->chan); + goto err_create_chan; + } + + filp->private_data = dn; + kref_put(&vds->refcount, _free_vds); + return 0; + +err_create_chan: + kfree(dn); +err_alloc_chan: + kref_put(&vds->refcount, _free_vds); +err_vds_lookup: + return ret; +} + + +static int dn_connect_ioctl(struct tipc_dn_chan *dn, char __user *usr_name) +{ + int err; + char name[MAX_SRV_NAME_LEN]; + + /* copy in service name from user space */ + err = strncpy_from_user(name, usr_name, sizeof(name)); + if (err < 0) { + pr_err("%s: copy_from_user (%p) failed (%d)\n", + __func__, usr_name, err); + return err; + } + name[sizeof(name)-1] = '\0'; + + /* send connect request */ + err = tipc_chan_connect(dn->chan, name); + if (err) + return err; + + /* and wait for reply */ + return dn_wait_for_reply(dn, REPLY_TIMEOUT); +} + +static long tipc_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) +{ + int ret; + struct tipc_dn_chan *dn = filp->private_data; + + if (_IOC_TYPE(cmd) != TIPC_IOC_MAGIC) + return -EINVAL; + + switch (cmd) { + case TIPC_IOC_CONNECT: + ret = dn_connect_ioctl(dn, (char __user *)arg); + break; + default: + pr_warn("%s: Unhandled ioctl cmd: 0x%x\n", + __func__, cmd); + ret = -EINVAL; + } + return ret; +} + +#if defined(CONFIG_COMPAT) +static long tipc_compat_ioctl(struct file *filp, + unsigned int cmd, unsigned long arg) +{ + int ret; + struct tipc_dn_chan *dn = filp->private_data; + void __user *user_req = compat_ptr(arg); + + if (_IOC_TYPE(cmd) != TIPC_IOC_MAGIC) + return -EINVAL; + + switch (cmd) { + case TIPC_IOC_CONNECT_COMPAT: + ret = dn_connect_ioctl(dn, user_req); + break; + default: + pr_warn("%s: Unhandled ioctl cmd: 0x%x\n", + __func__, cmd); + ret = -EINVAL; + } + return ret; +} +#endif + +static inline bool _got_rx(struct tipc_dn_chan *dn) +{ + if (dn->state != TIPC_CONNECTED) + return true; + + if (!list_empty(&dn->rx_msg_queue)) + return true; + + return false; +} + +static ssize_t tipc_read_iter(struct kiocb *iocb, struct iov_iter *iter) +{ + ssize_t ret; + size_t len; + struct tipc_msg_buf *mb; + struct file *filp = iocb->ki_filp; + struct tipc_dn_chan *dn = filp->private_data; + + mutex_lock(&dn->lock); + + while (list_empty(&dn->rx_msg_queue)) { + if (dn->state != TIPC_CONNECTED) { + if (dn->state == TIPC_CONNECTING) + ret = -ENOTCONN; + else if (dn->state == TIPC_DISCONNECTED) + ret = -ENOTCONN; + else if (dn->state == TIPC_STALE) + ret = -ESHUTDOWN; + else + ret = -EBADFD; + goto out; + } + + mutex_unlock(&dn->lock); + + if (filp->f_flags & O_NONBLOCK) + return -EAGAIN; + + if (wait_event_interruptible(dn->readq, _got_rx(dn))) + return -ERESTARTSYS; + + mutex_lock(&dn->lock); + } + + mb = list_first_entry(&dn->rx_msg_queue, struct tipc_msg_buf, node); + + len = mb_avail_data(mb); + if (len > iov_iter_count(iter)) { + ret = -EMSGSIZE; + goto out; + } + + if (copy_to_iter(mb_get_data(mb, len), len, iter) != len) { + ret = -EFAULT; + goto out; + } + + ret = len; + list_del(&mb->node); + tipc_chan_put_rxbuf(dn->chan, mb); + +out: + mutex_unlock(&dn->lock); + return ret; +} + +static ssize_t tipc_write_iter(struct kiocb *iocb, struct iov_iter *iter) +{ + ssize_t ret; + size_t len; + long timeout = TXBUF_TIMEOUT; + struct tipc_msg_buf *txbuf = NULL; + struct file *filp = iocb->ki_filp; + struct tipc_dn_chan *dn = filp->private_data; + + if (filp->f_flags & O_NONBLOCK) + timeout = 0; + + txbuf = tipc_chan_get_txbuf_timeout(dn->chan, timeout); + if (IS_ERR(txbuf)) + return PTR_ERR(txbuf); + + /* message length */ + len = iov_iter_count(iter); + + /* check available space */ + if (len > mb_avail_space(txbuf)) { + ret = -EMSGSIZE; + goto err_out; + } + + /* copy in message data */ + if (copy_from_iter(mb_put_data(txbuf, len), len, iter) != len) { + ret = -EFAULT; + goto err_out; + } + + /* queue message */ + ret = tipc_chan_queue_msg(dn->chan, txbuf); + if (ret) + goto err_out; + + return len; + +err_out: + tipc_chan_put_txbuf(dn->chan, txbuf); + return ret; +} + +static unsigned int tipc_poll(struct file *filp, poll_table *wait) +{ + unsigned int mask = 0; + struct tipc_dn_chan *dn = filp->private_data; + + mutex_lock(&dn->lock); + + poll_wait(filp, &dn->readq, wait); + + /* Writes always succeed for now */ + mask |= POLLOUT | POLLWRNORM; + + if (!list_empty(&dn->rx_msg_queue)) + mask |= POLLIN | POLLRDNORM; + + if (dn->state != TIPC_CONNECTED) + mask |= POLLERR; + + mutex_unlock(&dn->lock); + return mask; +} + + +static int tipc_release(struct inode *inode, struct file *filp) +{ + struct tipc_dn_chan *dn = filp->private_data; + + dn_shutdown(dn); + + /* free all pending buffers */ + _free_msg_buf_list(&dn->rx_msg_queue); + + /* shutdown channel */ + tipc_chan_shutdown(dn->chan); + + /* and destroy it */ + tipc_chan_destroy(dn->chan); + + kfree(dn); + + return 0; +} + +static const struct file_operations tipc_fops = { + .open = tipc_open, + .release = tipc_release, + .unlocked_ioctl = tipc_ioctl, +#if defined(CONFIG_COMPAT) + .compat_ioctl = tipc_compat_ioctl, +#endif + .read_iter = tipc_read_iter, + .write_iter = tipc_write_iter, + .poll = tipc_poll, + .owner = THIS_MODULE, +}; + +/*****************************************************************************/ + +static void chan_trigger_event(struct tipc_chan *chan, int event) +{ + if (!event) + return; + + chan->ops->handle_event(chan->ops_arg, event); +} + +static void _cleanup_vq(struct virtqueue *vq) +{ + struct tipc_msg_buf *mb; + + while ((mb = virtqueue_detach_unused_buf(vq)) != NULL) + _free_msg_buf(mb); +} + +static int _create_cdev_node(struct device *parent, + struct tipc_cdev_node *cdn, + const char *name) +{ + int ret; + dev_t devt; + + if (!name) { + dev_dbg(parent, "%s: cdev name has to be provided\n", + __func__); + return -EINVAL; + } + + /* allocate minor */ + ret = idr_alloc(&tipc_devices, cdn, 0, MAX_DEVICES-1, GFP_KERNEL); + if (ret < 0) { + dev_dbg(parent, "%s: failed (%d) to get id\n", + __func__, ret); + return ret; + } + + cdn->minor = ret; + cdev_init(&cdn->cdev, &tipc_fops); + cdn->cdev.owner = THIS_MODULE; + + /* Add character device */ + devt = MKDEV(tipc_major, cdn->minor); + ret = cdev_add(&cdn->cdev, devt, 1); + if (ret) { + dev_dbg(parent, "%s: cdev_add failed (%d)\n", + __func__, ret); + goto err_add_cdev; + } + + /* Create a device node */ + cdn->dev = device_create(tipc_class, parent, + devt, NULL, "trusty-ipc-%s", name); + if (IS_ERR(cdn->dev)) { + ret = PTR_ERR(cdn->dev); + dev_dbg(parent, "%s: device_create failed: %d\n", + __func__, ret); + goto err_device_create; + } + + return 0; + +err_device_create: + cdn->dev = NULL; + cdev_del(&cdn->cdev); +err_add_cdev: + idr_remove(&tipc_devices, cdn->minor); + return ret; +} + +static void create_cdev_node(struct tipc_virtio_dev *vds, + struct tipc_cdev_node *cdn) +{ + int err; + + mutex_lock(&tipc_devices_lock); + + if (!default_vdev) { + kref_get(&vds->refcount); + default_vdev = vds->vdev; + } + + if (vds->cdev_name[0] && !cdn->dev) { + kref_get(&vds->refcount); + err = _create_cdev_node(&vds->vdev->dev, cdn, vds->cdev_name); + if (err) { + dev_err(&vds->vdev->dev, + "failed (%d) to create cdev node\n", err); + kref_put(&vds->refcount, _free_vds); + } + } + mutex_unlock(&tipc_devices_lock); +} + +static void destroy_cdev_node(struct tipc_virtio_dev *vds, + struct tipc_cdev_node *cdn) +{ + mutex_lock(&tipc_devices_lock); + if (cdn->dev) { + device_destroy(tipc_class, MKDEV(tipc_major, cdn->minor)); + cdev_del(&cdn->cdev); + idr_remove(&tipc_devices, cdn->minor); + cdn->dev = NULL; + kref_put(&vds->refcount, _free_vds); + } + + if (default_vdev == vds->vdev) { + default_vdev = NULL; + kref_put(&vds->refcount, _free_vds); + } + + mutex_unlock(&tipc_devices_lock); +} + +static void _go_online(struct tipc_virtio_dev *vds) +{ + mutex_lock(&vds->lock); + if (vds->state == VDS_OFFLINE) + vds->state = VDS_ONLINE; + mutex_unlock(&vds->lock); + + create_cdev_node(vds, &vds->cdev_node); + + dev_info(&vds->vdev->dev, "is online\n"); +} + +static void _go_offline(struct tipc_virtio_dev *vds) +{ + struct tipc_chan *chan; + + /* change state to OFFLINE */ + mutex_lock(&vds->lock); + if (vds->state != VDS_ONLINE) { + mutex_unlock(&vds->lock); + return; + } + vds->state = VDS_OFFLINE; + mutex_unlock(&vds->lock); + + /* wakeup all waiters */ + wake_up_interruptible_all(&vds->sendq); + + /* shutdown all channels */ + while ((chan = vds_lookup_channel(vds, TIPC_ANY_ADDR))) { + mutex_lock(&chan->lock); + chan->state = TIPC_STALE; + chan->remote = 0; + chan_trigger_event(chan, TIPC_CHANNEL_SHUTDOWN); + mutex_unlock(&chan->lock); + kref_put(&chan->refcount, _free_chan); + } + + /* shutdown device node */ + destroy_cdev_node(vds, &vds->cdev_node); + + dev_info(&vds->vdev->dev, "is offline\n"); +} + +static void _handle_conn_rsp(struct tipc_virtio_dev *vds, + struct tipc_conn_rsp_body *rsp, size_t len) +{ + struct tipc_chan *chan; + + if (sizeof(*rsp) != len) { + dev_err(&vds->vdev->dev, "%s: Invalid response length %zd\n", + __func__, len); + return; + } + + dev_dbg(&vds->vdev->dev, + "%s: connection response: for addr 0x%x: " + "status %d remote addr 0x%x\n", + __func__, rsp->target, rsp->status, rsp->remote); + + /* Lookup channel */ + chan = vds_lookup_channel(vds, rsp->target); + if (chan) { + mutex_lock(&chan->lock); + if (chan->state == TIPC_CONNECTING) { + if (!rsp->status) { + chan->state = TIPC_CONNECTED; + chan->remote = rsp->remote; + chan->max_msg_cnt = rsp->max_msg_cnt; + chan->max_msg_size = rsp->max_msg_size; + chan_trigger_event(chan, + TIPC_CHANNEL_CONNECTED); + } else { + chan->state = TIPC_DISCONNECTED; + chan->remote = 0; + chan_trigger_event(chan, + TIPC_CHANNEL_DISCONNECTED); + } + } + mutex_unlock(&chan->lock); + kref_put(&chan->refcount, _free_chan); + } +} + +static void _handle_disc_req(struct tipc_virtio_dev *vds, + struct tipc_disc_req_body *req, size_t len) +{ + struct tipc_chan *chan; + + if (sizeof(*req) != len) { + dev_err(&vds->vdev->dev, "%s: Invalid request length %zd\n", + __func__, len); + return; + } + + dev_dbg(&vds->vdev->dev, "%s: disconnect request: for addr 0x%x\n", + __func__, req->target); + + chan = vds_lookup_channel(vds, req->target); + if (chan) { + mutex_lock(&chan->lock); + if (chan->state == TIPC_CONNECTED || + chan->state == TIPC_CONNECTING) { + chan->state = TIPC_DISCONNECTED; + chan->remote = 0; + chan_trigger_event(chan, TIPC_CHANNEL_DISCONNECTED); + } + mutex_unlock(&chan->lock); + kref_put(&chan->refcount, _free_chan); + } +} + +static void _handle_ctrl_msg(struct tipc_virtio_dev *vds, + void *data, int len, u32 src) +{ + struct tipc_ctrl_msg *msg = data; + + if ((len < sizeof(*msg)) || (sizeof(*msg) + msg->body_len != len)) { + dev_err(&vds->vdev->dev, + "%s: Invalid message length ( %d vs. %d)\n", + __func__, (int)(sizeof(*msg) + msg->body_len), len); + return; + } + + dev_dbg(&vds->vdev->dev, + "%s: Incoming ctrl message: src 0x%x type %d len %d\n", + __func__, src, msg->type, msg->body_len); + + switch (msg->type) { + case TIPC_CTRL_MSGTYPE_GO_ONLINE: + _go_online(vds); + break; + + case TIPC_CTRL_MSGTYPE_GO_OFFLINE: + _go_offline(vds); + break; + + case TIPC_CTRL_MSGTYPE_CONN_RSP: + _handle_conn_rsp(vds, (struct tipc_conn_rsp_body *)msg->body, + msg->body_len); + break; + + case TIPC_CTRL_MSGTYPE_DISC_REQ: + _handle_disc_req(vds, (struct tipc_disc_req_body *)msg->body, + msg->body_len); + break; + + default: + dev_warn(&vds->vdev->dev, + "%s: Unexpected message type: %d\n", + __func__, msg->type); + } +} + +static int _handle_rxbuf(struct tipc_virtio_dev *vds, + struct tipc_msg_buf *rxbuf, size_t rxlen) +{ + int err; + struct scatterlist sg; + struct tipc_msg_hdr *msg; + struct device *dev = &vds->vdev->dev; + + /* message sanity check */ + if (rxlen > rxbuf->buf_sz) { + dev_warn(dev, "inbound msg is too big: %zd\n", rxlen); + goto drop_it; + } + + if (rxlen < sizeof(*msg)) { + dev_warn(dev, "inbound msg is too short: %zd\n", rxlen); + goto drop_it; + } + + /* reset buffer and put data */ + mb_reset(rxbuf); + mb_put_data(rxbuf, rxlen); + + /* get message header */ + msg = mb_get_data(rxbuf, sizeof(*msg)); + if (mb_avail_data(rxbuf) != msg->len) { + dev_warn(dev, "inbound msg length mismatch: (%d vs. %d)\n", + (uint) mb_avail_data(rxbuf), (uint)msg->len); + goto drop_it; + } + + dev_dbg(dev, "From: %d, To: %d, Len: %d, Flags: 0x%x, Reserved: %d\n", + msg->src, msg->dst, msg->len, msg->flags, msg->reserved); + + /* message directed to control endpoint is a special case */ + if (msg->dst == TIPC_CTRL_ADDR) { + _handle_ctrl_msg(vds, msg->data, msg->len, msg->src); + } else { + struct tipc_chan *chan = NULL; + /* Lookup channel */ + chan = vds_lookup_channel(vds, msg->dst); + if (chan) { + /* handle it */ + rxbuf = chan->ops->handle_msg(chan->ops_arg, rxbuf); + BUG_ON(!rxbuf); + kref_put(&chan->refcount, _free_chan); + } + } + +drop_it: + /* add the buffer back to the virtqueue */ + sg_init_one(&sg, rxbuf->buf_va, rxbuf->buf_sz); + err = virtqueue_add_inbuf(vds->rxvq, &sg, 1, rxbuf, GFP_KERNEL); + if (err < 0) { + dev_err(dev, "failed to add a virtqueue buffer: %d\n", err); + return err; + } + + return 0; +} + +static void _rxvq_cb(struct virtqueue *rxvq) +{ + unsigned int len; + struct tipc_msg_buf *mb; + unsigned int msg_cnt = 0; + struct tipc_virtio_dev *vds = rxvq->vdev->priv; + + while ((mb = virtqueue_get_buf(rxvq, &len)) != NULL) { + if (_handle_rxbuf(vds, mb, len)) + break; + msg_cnt++; + } + + /* tell the other size that we added rx buffers */ + if (msg_cnt) + virtqueue_kick(rxvq); +} + +static void _txvq_cb(struct virtqueue *txvq) +{ + unsigned int len; + struct tipc_msg_buf *mb; + bool need_wakeup = false; + struct tipc_virtio_dev *vds = txvq->vdev->priv; + + dev_dbg(&txvq->vdev->dev, "%s\n", __func__); + + /* detach all buffers */ + mutex_lock(&vds->lock); + while ((mb = virtqueue_get_buf(txvq, &len)) != NULL) + need_wakeup |= _put_txbuf_locked(vds, mb); + mutex_unlock(&vds->lock); + + if (need_wakeup) { + /* wake up potential senders waiting for a tx buffer */ + wake_up_interruptible_all(&vds->sendq); + } +} + +static int tipc_virtio_probe(struct virtio_device *vdev) +{ + int err, i; + struct tipc_virtio_dev *vds; + struct tipc_dev_config config; + struct virtqueue *vqs[2]; + vq_callback_t *vq_cbs[] = {_rxvq_cb, _txvq_cb}; + const char *vq_names[] = { "rx", "tx" }; + + dev_dbg(&vdev->dev, "%s:\n", __func__); + + vds = kzalloc(sizeof(*vds), GFP_KERNEL); + if (!vds) + return -ENOMEM; + + vds->vdev = vdev; + + mutex_init(&vds->lock); + kref_init(&vds->refcount); + init_waitqueue_head(&vds->sendq); + INIT_LIST_HEAD(&vds->free_buf_list); + idr_init(&vds->addr_idr); + + /* set default max message size and alignment */ + memset(&config, 0, sizeof(config)); + config.msg_buf_max_size = DEFAULT_MSG_BUF_SIZE; + config.msg_buf_alignment = DEFAULT_MSG_BUF_ALIGN; + + /* get configuration if present */ + vdev->config->get(vdev, 0, &config, sizeof(config)); + + /* copy dev name */ + strncpy(vds->cdev_name, config.dev_name, sizeof(vds->cdev_name)); + vds->cdev_name[sizeof(vds->cdev_name)-1] = '\0'; + + /* find tx virtqueues (rx and tx and in this order) */ + err = vdev->config->find_vqs(vdev, 2, vqs, vq_cbs, vq_names); + if (err) + goto err_find_vqs; + + vds->rxvq = vqs[0]; + vds->txvq = vqs[1]; + + /* save max buffer size and count */ + vds->msg_buf_max_sz = config.msg_buf_max_size; + vds->msg_buf_max_cnt = virtqueue_get_vring_size(vds->txvq); + + /* set up the receive buffers */ + for (i = 0; i < virtqueue_get_vring_size(vds->rxvq); i++) { + struct scatterlist sg; + struct tipc_msg_buf *rxbuf; + + rxbuf = _alloc_msg_buf(vds->msg_buf_max_sz); + if (!rxbuf) { + dev_err(&vdev->dev, "failed to allocate rx buffer\n"); + err = -ENOMEM; + goto err_free_rx_buffers; + } + + sg_init_one(&sg, rxbuf->buf_va, rxbuf->buf_sz); + err = virtqueue_add_inbuf(vds->rxvq, &sg, 1, rxbuf, GFP_KERNEL); + WARN_ON(err); /* sanity check; this can't really happen */ + } + + vdev->priv = vds; + vds->state = VDS_OFFLINE; + + dev_dbg(&vdev->dev, "%s: done\n", __func__); + return 0; + +err_free_rx_buffers: + _cleanup_vq(vds->rxvq); +err_find_vqs: + kref_put(&vds->refcount, _free_vds); + return err; +} + +static void tipc_virtio_remove(struct virtio_device *vdev) +{ + struct tipc_virtio_dev *vds = vdev->priv; + + _go_offline(vds); + + mutex_lock(&vds->lock); + vds->state = VDS_DEAD; + vds->vdev = NULL; + mutex_unlock(&vds->lock); + + vdev->config->reset(vdev); + + idr_destroy(&vds->addr_idr); + + _cleanup_vq(vds->rxvq); + _cleanup_vq(vds->txvq); + _free_msg_buf_list(&vds->free_buf_list); + + vdev->config->del_vqs(vds->vdev); + + kref_put(&vds->refcount, _free_vds); +} + +static struct virtio_device_id tipc_virtio_id_table[] = { + { VIRTIO_ID_TRUSTY_IPC, VIRTIO_DEV_ANY_ID }, + { 0 }, +}; + +static unsigned int features[] = { + 0, +}; + +static struct virtio_driver virtio_tipc_driver = { + .feature_table = features, + .feature_table_size = ARRAY_SIZE(features), + .driver.name = KBUILD_MODNAME, + .driver.owner = THIS_MODULE, + .id_table = tipc_virtio_id_table, + .probe = tipc_virtio_probe, + .remove = tipc_virtio_remove, +}; + +static int __init tipc_init(void) +{ + int ret; + dev_t dev; + + ret = alloc_chrdev_region(&dev, 0, MAX_DEVICES, KBUILD_MODNAME); + if (ret) { + pr_err("%s: alloc_chrdev_region failed: %d\n", __func__, ret); + return ret; + } + + tipc_major = MAJOR(dev); + tipc_class = class_create(THIS_MODULE, KBUILD_MODNAME); + if (IS_ERR(tipc_class)) { + ret = PTR_ERR(tipc_class); + pr_err("%s: class_create failed: %d\n", __func__, ret); + goto err_class_create; + } + + ret = register_virtio_driver(&virtio_tipc_driver); + if (ret) { + pr_err("failed to register virtio driver: %d\n", ret); + goto err_register_virtio_drv; + } + + return 0; + +err_register_virtio_drv: + class_destroy(tipc_class); + +err_class_create: + unregister_chrdev_region(dev, MAX_DEVICES); + return ret; +} + +static void __exit tipc_exit(void) +{ + unregister_virtio_driver(&virtio_tipc_driver); + class_destroy(tipc_class); + unregister_chrdev_region(MKDEV(tipc_major, 0), MAX_DEVICES); +} + +/* We need to init this early */ +subsys_initcall(tipc_init); +module_exit(tipc_exit); + +MODULE_DEVICE_TABLE(tipc, tipc_virtio_id_table); +MODULE_DESCRIPTION("Trusty IPC driver"); +MODULE_LICENSE("GPL v2"); diff --git a/include/linux/trusty/trusty_ipc.h b/include/linux/trusty/trusty_ipc.h new file mode 100644 index 000000000000..4ca15938a854 --- /dev/null +++ b/include/linux/trusty/trusty_ipc.h @@ -0,0 +1,88 @@ +/* + * Copyright (C) 2015 Google, Inc. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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. + * + */ +#ifndef __LINUX_TRUSTY_TRUSTY_IPC_H +#define __LINUX_TRUSTY_TRUSTY_IPC_H + +struct tipc_chan; + +struct tipc_msg_buf { + void *buf_va; + phys_addr_t buf_pa; + size_t buf_sz; + size_t wpos; + size_t rpos; + struct list_head node; +}; + +enum tipc_chan_event { + TIPC_CHANNEL_CONNECTED = 1, + TIPC_CHANNEL_DISCONNECTED, + TIPC_CHANNEL_SHUTDOWN, +}; + +struct tipc_chan_ops { + void (*handle_event)(void *cb_arg, int event); + struct tipc_msg_buf *(*handle_msg)(void *cb_arg, + struct tipc_msg_buf *mb); +}; + +struct tipc_chan *tipc_create_channel(struct device *dev, + const struct tipc_chan_ops *ops, + void *cb_arg); + +int tipc_chan_connect(struct tipc_chan *chan, const char *port); + +int tipc_chan_queue_msg(struct tipc_chan *chan, struct tipc_msg_buf *mb); + +int tipc_chan_shutdown(struct tipc_chan *chan); + +void tipc_chan_destroy(struct tipc_chan *chan); + +struct tipc_msg_buf *tipc_chan_get_rxbuf(struct tipc_chan *chan); + +void tipc_chan_put_rxbuf(struct tipc_chan *chan, struct tipc_msg_buf *mb); + +struct tipc_msg_buf * +tipc_chan_get_txbuf_timeout(struct tipc_chan *chan, long timeout); + +void tipc_chan_put_txbuf(struct tipc_chan *chan, struct tipc_msg_buf *mb); + +static inline size_t mb_avail_space(struct tipc_msg_buf *mb) +{ + return mb->buf_sz - mb->wpos; +} + +static inline size_t mb_avail_data(struct tipc_msg_buf *mb) +{ + return mb->wpos - mb->rpos; +} + +static inline void *mb_put_data(struct tipc_msg_buf *mb, size_t len) +{ + void *pos = (u8 *)mb->buf_va + mb->wpos; + BUG_ON(mb->wpos + len > mb->buf_sz); + mb->wpos += len; + return pos; +} + +static inline void *mb_get_data(struct tipc_msg_buf *mb, size_t len) +{ + void *pos = (u8 *)mb->buf_va + mb->rpos; + BUG_ON(mb->rpos + len > mb->wpos); + mb->rpos += len; + return pos; +} + +#endif /* __LINUX_TRUSTY_TRUSTY_IPC_H */ + From 5ab4027b6dbd7cdd9c14e0980008999ee60c952c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arve=20Hj=C3=B8nnev=C3=A5g?= Date: Thu, 11 Jun 2015 19:34:28 -0700 Subject: [PATCH 0983/1103] trusty: Select api version Select api version in probe, and store it in trusty_state. This enables new return codes from trusty, and will later be used to enable a nop stdcall that does not take smc_lock. Change-Id: I8011325265da818725ef65f094bf820402878eb5 --- drivers/trusty/trusty.c | 35 +++++++++++++++++++++++++++++++++++ include/linux/trusty/smcall.h | 19 +++++++++++++++++++ include/linux/trusty/trusty.h | 1 + 3 files changed, 55 insertions(+) diff --git a/drivers/trusty/trusty.c b/drivers/trusty/trusty.c index 16c595bf5e29..fcdbba518797 100644 --- a/drivers/trusty/trusty.c +++ b/drivers/trusty/trusty.c @@ -29,6 +29,7 @@ struct trusty_state { struct mutex smc_lock; struct atomic_notifier_head notifier; char *version_str; + u32 api_version; }; #ifdef CONFIG_ARM64 @@ -265,6 +266,35 @@ static void trusty_init_version(struct trusty_state *s, struct device *dev) dev_err(dev, "failed to get version: %d\n", ret); } +u32 trusty_get_api_version(struct device *dev) +{ + struct trusty_state *s = platform_get_drvdata(to_platform_device(dev)); + + return s->api_version; +} +EXPORT_SYMBOL(trusty_get_api_version); + +static int trusty_init_api_version(struct trusty_state *s, struct device *dev) +{ + u32 api_version; + api_version = trusty_fast_call32(dev, SMC_FC_API_VERSION, + TRUSTY_API_VERSION_CURRENT, 0, 0); + if (api_version == SM_ERR_UNDEFINED_SMC) + api_version = 0; + + if (api_version > TRUSTY_API_VERSION_CURRENT) { + dev_err(dev, "unsupported api version %u > %u\n", + api_version, TRUSTY_API_VERSION_CURRENT); + return -EINVAL; + } + + dev_info(dev, "selected api version: %u (requested %u)\n", + api_version, TRUSTY_API_VERSION_CURRENT); + s->api_version = api_version; + + return 0; +} + static int trusty_probe(struct platform_device *pdev) { int ret; @@ -287,6 +317,10 @@ static int trusty_probe(struct platform_device *pdev) trusty_init_version(s, &pdev->dev); + ret = trusty_init_api_version(s, &pdev->dev); + if (ret < 0) + goto err_api_version; + ret = of_platform_populate(pdev->dev.of_node, NULL, NULL, &pdev->dev); if (ret < 0) { dev_err(&pdev->dev, "Failed to add children: %d\n", ret); @@ -296,6 +330,7 @@ static int trusty_probe(struct platform_device *pdev) return 0; err_add_children: +err_api_version: if (s->version_str) { device_remove_file(&pdev->dev, &dev_attr_trusty_version); kfree(s->version_str); diff --git a/include/linux/trusty/smcall.h b/include/linux/trusty/smcall.h index a2be2e3579f3..cdb4eccd7bc3 100644 --- a/include/linux/trusty/smcall.h +++ b/include/linux/trusty/smcall.h @@ -78,6 +78,25 @@ #define SMC_FC_AARCH_SWITCH SMC_FASTCALL_NR (SMC_ENTITY_SECURE_MONITOR, 9) #define SMC_FC_GET_VERSION_STR SMC_FASTCALL_NR (SMC_ENTITY_SECURE_MONITOR, 10) +/** + * SMC_FC_API_VERSION - Find and select supported API version. + * + * @r1: Version supported by client. + * + * Returns version supported by trusty. + * + * If multiple versions are supported, the client should start by calling + * SMC_FC_API_VERSION with the largest version it supports. Trusty will then + * return a version it supports. If the client does not support the version + * returned by trusty and the version returned is less than the version + * requested, repeat the call with the largest supported version less than the + * last returned version. + * + * This call must be made before any calls that are affected by the api version. + */ +#define TRUSTY_API_VERSION_CURRENT (0) +#define SMC_FC_API_VERSION SMC_FASTCALL_NR (SMC_ENTITY_SECURE_MONITOR, 11) + /* TRUSTED_OS entity calls */ #define SMC_SC_VIRTIO_GET_DESCR SMC_STDCALL_NR(SMC_ENTITY_TRUSTED_OS, 20) #define SMC_SC_VIRTIO_START SMC_STDCALL_NR(SMC_ENTITY_TRUSTED_OS, 21) diff --git a/include/linux/trusty/trusty.h b/include/linux/trusty/trusty.h index d084d9d68a7b..24fe2101a528 100644 --- a/include/linux/trusty/trusty.h +++ b/include/linux/trusty/trusty.h @@ -56,6 +56,7 @@ int trusty_call_notifier_register(struct device *dev, int trusty_call_notifier_unregister(struct device *dev, struct notifier_block *n); const char *trusty_version_str_get(struct device *dev); +u32 trusty_get_api_version(struct device *dev); struct ns_mem_page_info { uint64_t attr; From c0f2292d310f71621e2e1040826c8536aaabc65f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arve=20Hj=C3=B8nnev=C3=A5g?= Date: Thu, 11 Jun 2015 19:51:54 -0700 Subject: [PATCH 0984/1103] trusty: Handle fiqs without calling notifier and enabling interrupts Change-Id: I9c147376bd1596f4ecd1e932b30140c87410c860 --- drivers/trusty/trusty.c | 2 ++ include/linux/trusty/sm_err.h | 1 + include/linux/trusty/smcall.h | 15 ++++++++++++++- 3 files changed, 17 insertions(+), 1 deletion(-) diff --git a/drivers/trusty/trusty.c b/drivers/trusty/trusty.c index fcdbba518797..4b5d3552720b 100644 --- a/drivers/trusty/trusty.c +++ b/drivers/trusty/trusty.c @@ -108,6 +108,8 @@ static ulong trusty_std_call_inner(struct device *dev, ulong smcnr, __func__, smcnr, a0, a1, a2); while (true) { ret = smc(smcnr, a0, a1, a2); + while ((s32)ret == SM_ERR_FIQ_INTERRUPTED) + ret = smc(SMC_SC_RESTART_FIQ, 0, 0, 0); if ((int)ret != SM_ERR_BUSY || !retry) break; diff --git a/include/linux/trusty/sm_err.h b/include/linux/trusty/sm_err.h index 4ee67589ce63..7de09b46fddb 100644 --- a/include/linux/trusty/sm_err.h +++ b/include/linux/trusty/sm_err.h @@ -35,5 +35,6 @@ #define SM_ERR_NOT_ALLOWED -9 /* SMC call not allowed */ #define SM_ERR_END_OF_INPUT -10 #define SM_ERR_PANIC -11 /* Secure OS crashed */ +#define SM_ERR_FIQ_INTERRUPTED -12 /* Got interrupted by FIQ. Call back with SMC_SC_RESTART_FIQ on same CPU */ #endif diff --git a/include/linux/trusty/smcall.h b/include/linux/trusty/smcall.h index cdb4eccd7bc3..7d8950a8890e 100644 --- a/include/linux/trusty/smcall.h +++ b/include/linux/trusty/smcall.h @@ -58,6 +58,18 @@ #define SMC_SC_RESTART_LAST SMC_STDCALL_NR (SMC_ENTITY_SECURE_MONITOR, 0) #define SMC_SC_NOP SMC_STDCALL_NR (SMC_ENTITY_SECURE_MONITOR, 1) +/** + * SMC_SC_RESTART_FIQ - Re-enter trusty after it was interrupted by an fiq + * + * No arguments, no return value. + * + * Re-enter trusty after returning to ns to process an fiq. Must be called iff + * trusty returns SM_ERR_FIQ_INTERRUPTED. + * + * Enable by selecting api version TRUSTY_API_VERSION_RESTART_FIQ (1) or later. + */ +#define SMC_SC_RESTART_FIQ SMC_STDCALL_NR (SMC_ENTITY_SECURE_MONITOR, 2) + /* * Return from secure os to non-secure os with return value in r1 */ @@ -94,7 +106,8 @@ * * This call must be made before any calls that are affected by the api version. */ -#define TRUSTY_API_VERSION_CURRENT (0) +#define TRUSTY_API_VERSION_RESTART_FIQ (1) +#define TRUSTY_API_VERSION_CURRENT (1) #define SMC_FC_API_VERSION SMC_FASTCALL_NR (SMC_ENTITY_SECURE_MONITOR, 11) /* TRUSTED_OS entity calls */ From ed620ae6752e9f308487741d13711226cd95e851 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arve=20Hj=C3=B8nnev=C3=A5g?= Date: Fri, 23 Jan 2015 17:55:48 -0800 Subject: [PATCH 0985/1103] trusty: Add smp support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add an unlocked nop call to allow multiple cpus to enter trusty. Other standard calls are still serialized to avoid return codes getting mixed up. A new return code is used to indicate that the standard call is running on another cpu. Change-Id: I0eecb88fb28989e3f4942659d109eee8863f3227 Signed-off-by: Arve Hjønnevåg --- drivers/trusty/trusty-irq.c | 31 ++++++++++++++++++++++++++++--- drivers/trusty/trusty.c | 27 ++++++++++++++++++++++++--- include/linux/trusty/sm_err.h | 3 +++ include/linux/trusty/smcall.h | 17 +++++++++++++++-- 4 files changed, 70 insertions(+), 8 deletions(-) diff --git a/drivers/trusty/trusty-irq.c b/drivers/trusty/trusty-irq.c index ae9535af77dd..1f14f7f48bed 100644 --- a/drivers/trusty/trusty-irq.c +++ b/drivers/trusty/trusty-irq.c @@ -154,7 +154,7 @@ static int trusty_irq_call_notify(struct notifier_block *nb, } -static void trusty_irq_work_func(struct work_struct *work) +static void trusty_irq_work_func_locked_nop(struct work_struct *work) { int ret; struct trusty_irq_state *is = @@ -162,8 +162,27 @@ static void trusty_irq_work_func(struct work_struct *work) dev_dbg(is->dev, "%s\n", __func__); - ret = trusty_std_call32(is->trusty_dev, SMC_SC_NOP, 0, 0, 0); + ret = trusty_std_call32(is->trusty_dev, SMC_SC_LOCKED_NOP, 0, 0, 0); if (ret != 0) + dev_err(is->dev, "%s: SMC_SC_LOCKED_NOP failed %d", + __func__, ret); + + dev_dbg(is->dev, "%s: done\n", __func__); +} + +static void trusty_irq_work_func(struct work_struct *work) +{ + int ret; + struct trusty_irq_state *is = + container_of(work, struct trusty_irq_work, work)->is; + + dev_dbg(is->dev, "%s\n", __func__); + + do { + ret = trusty_std_call32(is->trusty_dev, SMC_SC_NOP, 0, 0, 0); + } while (ret == SM_ERR_NOP_INTERRUPTED); + + if (ret != SM_ERR_NOP_DONE) dev_err(is->dev, "%s: SMC_SC_NOP failed %d", __func__, ret); dev_dbg(is->dev, "%s: done\n", __func__); @@ -397,6 +416,7 @@ static int trusty_irq_probe(struct platform_device *pdev) unsigned int cpu; unsigned long irq_flags; struct trusty_irq_state *is; + work_func_t work_func; dev_dbg(&pdev->dev, "%s\n", __func__); @@ -431,12 +451,17 @@ static int trusty_irq_probe(struct platform_device *pdev) goto err_trusty_call_notifier_register; } + if (trusty_get_api_version(is->trusty_dev) < TRUSTY_API_VERSION_SMP) + work_func = trusty_irq_work_func_locked_nop; + else + work_func = trusty_irq_work_func; + for_each_possible_cpu(cpu) { struct trusty_irq_work *trusty_irq_work; trusty_irq_work = per_cpu_ptr(is->irq_work, cpu); trusty_irq_work->is = is; - INIT_WORK(&trusty_irq_work->work, trusty_irq_work_func); + INIT_WORK(&trusty_irq_work->work, work_func); } for (irq = 0; irq >= 0;) diff --git a/drivers/trusty/trusty.c b/drivers/trusty/trusty.c index 4b5d3552720b..2a7aeb4725c5 100644 --- a/drivers/trusty/trusty.c +++ b/drivers/trusty/trusty.c @@ -28,6 +28,7 @@ struct trusty_state { struct mutex smc_lock; struct atomic_notifier_head notifier; + struct completion cpu_idle_completion; char *version_str; u32 api_version; }; @@ -161,6 +162,17 @@ static ulong trusty_std_call_helper(struct device *dev, ulong smcnr, return ret; } +static void trusty_std_call_cpu_idle(struct trusty_state *s) +{ + int ret; + + ret = wait_for_completion_timeout(&s->cpu_idle_completion, HZ * 10); + if (!ret) { + pr_warn("%s: timed out waiting for cpu idle to clear, retry anyway\n", + __func__); + } +} + s32 trusty_std_call32(struct device *dev, u32 smcnr, u32 a0, u32 a1, u32 a2) { int ret; @@ -169,15 +181,20 @@ s32 trusty_std_call32(struct device *dev, u32 smcnr, u32 a0, u32 a1, u32 a2) BUG_ON(SMC_IS_FASTCALL(smcnr)); BUG_ON(SMC_IS_SMC64(smcnr)); - mutex_lock(&s->smc_lock); + if (smcnr != SMC_SC_NOP) { + mutex_lock(&s->smc_lock); + reinit_completion(&s->cpu_idle_completion); + } dev_dbg(dev, "%s(0x%x 0x%x 0x%x 0x%x) started\n", __func__, smcnr, a0, a1, a2); ret = trusty_std_call_helper(dev, smcnr, a0, a1, a2); - while (ret == SM_ERR_INTERRUPTED) { + while (ret == SM_ERR_INTERRUPTED || ret == SM_ERR_CPU_IDLE) { dev_dbg(dev, "%s(0x%x 0x%x 0x%x 0x%x) interrupted\n", __func__, smcnr, a0, a1, a2); + if (ret == SM_ERR_CPU_IDLE) + trusty_std_call_cpu_idle(s); ret = trusty_std_call_helper(dev, SMC_SC_RESTART_LAST, 0, 0, 0); } dev_dbg(dev, "%s(0x%x 0x%x 0x%x 0x%x) returned 0x%x\n", @@ -185,7 +202,10 @@ s32 trusty_std_call32(struct device *dev, u32 smcnr, u32 a0, u32 a1, u32 a2) WARN_ONCE(ret == SM_ERR_PANIC, "trusty crashed"); - mutex_unlock(&s->smc_lock); + if (smcnr == SMC_SC_NOP) + complete(&s->cpu_idle_completion); + else + mutex_unlock(&s->smc_lock); return ret; } @@ -315,6 +335,7 @@ static int trusty_probe(struct platform_device *pdev) } mutex_init(&s->smc_lock); ATOMIC_INIT_NOTIFIER_HEAD(&s->notifier); + init_completion(&s->cpu_idle_completion); platform_set_drvdata(pdev, s); trusty_init_version(s, &pdev->dev); diff --git a/include/linux/trusty/sm_err.h b/include/linux/trusty/sm_err.h index 7de09b46fddb..32ee08e499c3 100644 --- a/include/linux/trusty/sm_err.h +++ b/include/linux/trusty/sm_err.h @@ -36,5 +36,8 @@ #define SM_ERR_END_OF_INPUT -10 #define SM_ERR_PANIC -11 /* Secure OS crashed */ #define SM_ERR_FIQ_INTERRUPTED -12 /* Got interrupted by FIQ. Call back with SMC_SC_RESTART_FIQ on same CPU */ +#define SM_ERR_CPU_IDLE -13 /* SMC call waiting for another CPU */ +#define SM_ERR_NOP_INTERRUPTED -14 /* Got interrupted. Call back with new SMC_SC_NOP */ +#define SM_ERR_NOP_DONE -15 /* Cpu idle after SMC_SC_NOP (not an error) */ #endif diff --git a/include/linux/trusty/smcall.h b/include/linux/trusty/smcall.h index 7d8950a8890e..2e43803d9333 100644 --- a/include/linux/trusty/smcall.h +++ b/include/linux/trusty/smcall.h @@ -56,7 +56,7 @@ /* FC = Fast call, SC = Standard call */ #define SMC_SC_RESTART_LAST SMC_STDCALL_NR (SMC_ENTITY_SECURE_MONITOR, 0) -#define SMC_SC_NOP SMC_STDCALL_NR (SMC_ENTITY_SECURE_MONITOR, 1) +#define SMC_SC_LOCKED_NOP SMC_STDCALL_NR (SMC_ENTITY_SECURE_MONITOR, 1) /** * SMC_SC_RESTART_FIQ - Re-enter trusty after it was interrupted by an fiq @@ -70,6 +70,18 @@ */ #define SMC_SC_RESTART_FIQ SMC_STDCALL_NR (SMC_ENTITY_SECURE_MONITOR, 2) +/** + * SMC_SC_NOP - Enter trusty to run pending work. + * + * No arguments. + * + * Returns SM_ERR_NOP_INTERRUPTED or SM_ERR_NOP_DONE. + * If SM_ERR_NOP_INTERRUPTED is returned, the call must be repeated. + * + * Enable by selecting api version TRUSTY_API_VERSION_SMP (2) or later. + */ +#define SMC_SC_NOP SMC_STDCALL_NR (SMC_ENTITY_SECURE_MONITOR, 3) + /* * Return from secure os to non-secure os with return value in r1 */ @@ -107,7 +119,8 @@ * This call must be made before any calls that are affected by the api version. */ #define TRUSTY_API_VERSION_RESTART_FIQ (1) -#define TRUSTY_API_VERSION_CURRENT (1) +#define TRUSTY_API_VERSION_SMP (2) +#define TRUSTY_API_VERSION_CURRENT (2) #define SMC_FC_API_VERSION SMC_FASTCALL_NR (SMC_ENTITY_SECURE_MONITOR, 11) /* TRUSTED_OS entity calls */ From 72fc84dbfe1c0ae0bc0c3e97f0c7032bb673286e Mon Sep 17 00:00:00 2001 From: Michael Ryleev Date: Fri, 26 Jun 2015 13:47:02 -0700 Subject: [PATCH 0986/1103] trusty-irq: Add support for secure interrupt mapping Trusty TEE is using flat IRQ space to identify its interrupts which does not match to IRQ domain model introduced on the Linux side. This CL adds support for optional "interrupt-templates" and "interrupt-ranges" properties that can be used to define correspondence between secure and non-secure IRQ IDs. Change-Id: Idb298760f2f21f0b8507eafa72600cca7ab8ac64 Signed-off-by: Michael Ryleev --- .../devicetree/bindings/trusty/trusty-irq.txt | 59 ++++++++++ drivers/trusty/trusty-irq.c | 106 +++++++++++++++++- 2 files changed, 161 insertions(+), 4 deletions(-) diff --git a/Documentation/devicetree/bindings/trusty/trusty-irq.txt b/Documentation/devicetree/bindings/trusty/trusty-irq.txt index 85fe1f1c7458..5aefeb8e536f 100644 --- a/Documentation/devicetree/bindings/trusty/trusty-irq.txt +++ b/Documentation/devicetree/bindings/trusty/trusty-irq.txt @@ -5,4 +5,63 @@ Trusty requires non-secure irqs to be forwarded to the secure OS. Required properties: - compatible: "android,trusty-irq-v1" +Optional properties: + +- interrupt-templates: is an optional property that works together + with "interrupt-ranges" to specify secure side to kernel IRQs mapping. + + It is a list of entries, each one of which defines a group of interrupts + having common properties, and has the following format: + < phandle irq_id_pos [templ_data]> + phandle - phandle of interrupt controller this template is for + irq_id_pos - the position of irq id in interrupt specifier array + for interrupt controller referenced by phandle. + templ_data - is an array of u32 values (could be empty) in the same + format as interrupt specifier for interrupt controller + referenced by phandle but with omitted irq id field. + +- interrupt-ranges: list of entries that specifies secure side to kernel + IRQs mapping. + + Each entry in the "interrupt-ranges" list has the following format: + + beg - first entry in this range + end - last entry in this range + templ_idx - index of entry in "interrupt-templates" property + that must be used as a template for all interrupts + in this range + +Example: +{ + gic: interrupt-controller@50041000 { + compatible = "arm,gic-400"; + #interrupt-cells = <3>; + interrupt-controller; + ... + }; + ... + IPI: interrupt-controller { + compatible = "android,CustomIPI"; + #interrupt-cells = <1>; + interrupt-controller; + }; + ... + trusty { + compatible = "android,trusty-smc-v1"; + ranges; + #address-cells = <2>; + #size-cells = <2>; + + irq { + compatible = "android,trusty-irq-v1"; + interrupt-templates = <&IPI 0>, + <&gic 1 GIC_PPI 0>, + <&gic 1 GIC_SPI 0>; + interrupt-ranges = < 0 15 0>, + <16 31 1>, + <32 223 2>; + }; + } +} + Must be a child of the node that provides the trusty std/fast call interface. diff --git a/drivers/trusty/trusty-irq.c b/drivers/trusty/trusty-irq.c index 1f14f7f48bed..8d6e8afb2a2f 100644 --- a/drivers/trusty/trusty-irq.c +++ b/drivers/trusty/trusty-irq.c @@ -15,8 +15,10 @@ #include #include #include +#include #include #include +#include #include #include #include @@ -266,13 +268,101 @@ static int trusty_irq_cpu_notify(struct notifier_block *nb, return NOTIFY_OK; } -static int trusty_irq_init_normal_irq(struct trusty_irq_state *is, int irq) +static int trusty_irq_create_irq_mapping(struct trusty_irq_state *is, int irq) { int ret; + int index; + u32 irq_pos; + u32 templ_idx; + u32 range_base; + u32 range_end; + struct of_phandle_args oirq; + + /* check if "interrupt-ranges" property is present */ + if (!of_find_property(is->dev->of_node, "interrupt-ranges", NULL)) { + /* fallback to old behavior to be backward compatible with + * systems that do not need IRQ domains. + */ + return irq; + } + + /* find irq range */ + for (index = 0;; index += 3) { + ret = of_property_read_u32_index(is->dev->of_node, + "interrupt-ranges", + index, &range_base); + if (ret) + return ret; + + ret = of_property_read_u32_index(is->dev->of_node, + "interrupt-ranges", + index + 1, &range_end); + if (ret) + return ret; + + if (irq >= range_base && irq <= range_end) + break; + } + + /* read the rest of range entry: template index and irq_pos */ + ret = of_property_read_u32_index(is->dev->of_node, + "interrupt-ranges", + index + 2, &templ_idx); + if (ret) + return ret; + + /* read irq template */ + ret = of_parse_phandle_with_args(is->dev->of_node, + "interrupt-templates", + "#interrupt-cells", + templ_idx, &oirq); + if (ret) + return ret; + + WARN_ON(!oirq.np); + WARN_ON(!oirq.args_count); + + /* + * An IRQ template is a non empty array of u32 values describing group + * of interrupts having common properties. The u32 entry with index + * zero contains the position of irq_id in interrupt specifier array + * followed by data representing interrupt specifier array with irq id + * field omitted, so to convert irq template to interrupt specifier + * array we have to move down one slot the first irq_pos entries and + * replace the resulting gap with real irq id. + */ + irq_pos = oirq.args[0]; + + if (irq_pos >= oirq.args_count) { + dev_err(is->dev, "irq pos is out of range: %d\n", irq_pos); + return -EINVAL; + } + + for (index = 1; index <= irq_pos; index++) + oirq.args[index - 1] = oirq.args[index]; + + oirq.args[irq_pos] = irq - range_base; + + ret = irq_create_of_mapping(&oirq); + + return (!ret) ? -EINVAL : ret; +} + +static int trusty_irq_init_normal_irq(struct trusty_irq_state *is, int tirq) +{ + int ret; + int irq; unsigned long irq_flags; struct trusty_irq *trusty_irq; - dev_dbg(is->dev, "%s: irq %d\n", __func__, irq); + dev_dbg(is->dev, "%s: irq %d\n", __func__, tirq); + + irq = trusty_irq_create_irq_mapping(is, tirq); + if (irq < 0) { + dev_err(is->dev, + "trusty_irq_create_irq_mapping failed (%d)\n", irq); + return irq; + } trusty_irq = kzalloc(sizeof(*trusty_irq), GFP_KERNEL); if (!trusty_irq) @@ -302,13 +392,21 @@ static int trusty_irq_init_normal_irq(struct trusty_irq_state *is, int irq) return ret; } -static int trusty_irq_init_per_cpu_irq(struct trusty_irq_state *is, int irq) +static int trusty_irq_init_per_cpu_irq(struct trusty_irq_state *is, int tirq) { int ret; + int irq; unsigned int cpu; struct trusty_irq __percpu *trusty_irq_handler_data; - dev_dbg(is->dev, "%s: irq %d\n", __func__, irq); + dev_dbg(is->dev, "%s: irq %d\n", __func__, tirq); + + irq = trusty_irq_create_irq_mapping(is, tirq); + if (irq <= 0) { + dev_err(is->dev, + "trusty_irq_create_irq_mapping failed (%d)\n", irq); + return irq; + } trusty_irq_handler_data = alloc_percpu(struct trusty_irq); if (!trusty_irq_handler_data) From 90d5274955a2997c603e957b312b2e98c5f0a730 Mon Sep 17 00:00:00 2001 From: weideng Date: Fri, 13 May 2016 10:36:16 +0800 Subject: [PATCH 0987/1103] Modify the static analysis errors for google's trusty driver patches. Totally 15 patches for Google's trusty driver are ported into kernel/glv. This patch will fix all of the static analysis errors for the 15 patches from google. Change-Id: I38c604cc010f1e93fda6a06d1f9410ab578656df Signed-off-by: weideng --- drivers/trusty/trusty.c | 4 ++-- include/linux/trusty/smcall.h | 32 ++++++++++++++++---------------- 2 files changed, 18 insertions(+), 18 deletions(-) diff --git a/drivers/trusty/trusty.c b/drivers/trusty/trusty.c index 2a7aeb4725c5..6fcd5481ac88 100644 --- a/drivers/trusty/trusty.c +++ b/drivers/trusty/trusty.c @@ -39,8 +39,8 @@ struct trusty_state { #define SMC_ARG2 "x2" #define SMC_ARG3 "x3" #define SMC_ARCH_EXTENSION "" -#define SMC_REGISTERS_TRASHED "x4","x5","x6","x7","x8","x9","x10","x11", \ - "x12","x13","x14","x15","x16","x17" +#define SMC_REGISTERS_TRASHED "x4", "x5", "x6", "x7", "x8", "x9", "x10", "x11", \ + "x12", "x13", "x14", "x15", "x16", "x17" #else #define SMC_ARG0 "r0" #define SMC_ARG1 "r1" diff --git a/include/linux/trusty/smcall.h b/include/linux/trusty/smcall.h index 2e43803d9333..1160890a3d90 100644 --- a/include/linux/trusty/smcall.h +++ b/include/linux/trusty/smcall.h @@ -55,8 +55,8 @@ #define SMC_ENTITY_SECURE_MONITOR 60 /* Trusted OS calls internal to secure monitor */ /* FC = Fast call, SC = Standard call */ -#define SMC_SC_RESTART_LAST SMC_STDCALL_NR (SMC_ENTITY_SECURE_MONITOR, 0) -#define SMC_SC_LOCKED_NOP SMC_STDCALL_NR (SMC_ENTITY_SECURE_MONITOR, 1) +#define SMC_SC_RESTART_LAST SMC_STDCALL_NR(SMC_ENTITY_SECURE_MONITOR, 0) +#define SMC_SC_LOCKED_NOP SMC_STDCALL_NR(SMC_ENTITY_SECURE_MONITOR, 1) /** * SMC_SC_RESTART_FIQ - Re-enter trusty after it was interrupted by an fiq @@ -68,7 +68,7 @@ * * Enable by selecting api version TRUSTY_API_VERSION_RESTART_FIQ (1) or later. */ -#define SMC_SC_RESTART_FIQ SMC_STDCALL_NR (SMC_ENTITY_SECURE_MONITOR, 2) +#define SMC_SC_RESTART_FIQ SMC_STDCALL_NR(SMC_ENTITY_SECURE_MONITOR, 2) /** * SMC_SC_NOP - Enter trusty to run pending work. @@ -80,27 +80,27 @@ * * Enable by selecting api version TRUSTY_API_VERSION_SMP (2) or later. */ -#define SMC_SC_NOP SMC_STDCALL_NR (SMC_ENTITY_SECURE_MONITOR, 3) +#define SMC_SC_NOP SMC_STDCALL_NR(SMC_ENTITY_SECURE_MONITOR, 3) /* * Return from secure os to non-secure os with return value in r1 */ -#define SMC_SC_NS_RETURN SMC_STDCALL_NR (SMC_ENTITY_SECURE_MONITOR, 0) +#define SMC_SC_NS_RETURN SMC_STDCALL_NR(SMC_ENTITY_SECURE_MONITOR, 0) -#define SMC_FC_RESERVED SMC_FASTCALL_NR (SMC_ENTITY_SECURE_MONITOR, 0) -#define SMC_FC_FIQ_EXIT SMC_FASTCALL_NR (SMC_ENTITY_SECURE_MONITOR, 1) -#define SMC_FC_REQUEST_FIQ SMC_FASTCALL_NR (SMC_ENTITY_SECURE_MONITOR, 2) -#define SMC_FC_GET_NEXT_IRQ SMC_FASTCALL_NR (SMC_ENTITY_SECURE_MONITOR, 3) -#define SMC_FC_FIQ_ENTER SMC_FASTCALL_NR (SMC_ENTITY_SECURE_MONITOR, 4) +#define SMC_FC_RESERVED SMC_FASTCALL_NR(SMC_ENTITY_SECURE_MONITOR, 0) +#define SMC_FC_FIQ_EXIT SMC_FASTCALL_NR(SMC_ENTITY_SECURE_MONITOR, 1) +#define SMC_FC_REQUEST_FIQ SMC_FASTCALL_NR(SMC_ENTITY_SECURE_MONITOR, 2) +#define SMC_FC_GET_NEXT_IRQ SMC_FASTCALL_NR(SMC_ENTITY_SECURE_MONITOR, 3) +#define SMC_FC_FIQ_ENTER SMC_FASTCALL_NR(SMC_ENTITY_SECURE_MONITOR, 4) #define SMC_FC64_SET_FIQ_HANDLER SMC_FASTCALL64_NR(SMC_ENTITY_SECURE_MONITOR, 5) -#define SMC_FC64_GET_FIQ_REGS SMC_FASTCALL64_NR (SMC_ENTITY_SECURE_MONITOR, 6) +#define SMC_FC64_GET_FIQ_REGS SMC_FASTCALL64_NR(SMC_ENTITY_SECURE_MONITOR, 6) -#define SMC_FC_CPU_SUSPEND SMC_FASTCALL_NR (SMC_ENTITY_SECURE_MONITOR, 7) -#define SMC_FC_CPU_RESUME SMC_FASTCALL_NR (SMC_ENTITY_SECURE_MONITOR, 8) +#define SMC_FC_CPU_SUSPEND SMC_FASTCALL_NR(SMC_ENTITY_SECURE_MONITOR, 7) +#define SMC_FC_CPU_RESUME SMC_FASTCALL_NR(SMC_ENTITY_SECURE_MONITOR, 8) -#define SMC_FC_AARCH_SWITCH SMC_FASTCALL_NR (SMC_ENTITY_SECURE_MONITOR, 9) -#define SMC_FC_GET_VERSION_STR SMC_FASTCALL_NR (SMC_ENTITY_SECURE_MONITOR, 10) +#define SMC_FC_AARCH_SWITCH SMC_FASTCALL_NR(SMC_ENTITY_SECURE_MONITOR, 9) +#define SMC_FC_GET_VERSION_STR SMC_FASTCALL_NR(SMC_ENTITY_SECURE_MONITOR, 10) /** * SMC_FC_API_VERSION - Find and select supported API version. @@ -121,7 +121,7 @@ #define TRUSTY_API_VERSION_RESTART_FIQ (1) #define TRUSTY_API_VERSION_SMP (2) #define TRUSTY_API_VERSION_CURRENT (2) -#define SMC_FC_API_VERSION SMC_FASTCALL_NR (SMC_ENTITY_SECURE_MONITOR, 11) +#define SMC_FC_API_VERSION SMC_FASTCALL_NR(SMC_ENTITY_SECURE_MONITOR, 11) /* TRUSTED_OS entity calls */ #define SMC_SC_VIRTIO_GET_DESCR SMC_STDCALL_NR(SMC_ENTITY_TRUSTED_OS, 20) From 54453286518319139fb0b47865c3b5448f1e8cc3 Mon Sep 17 00:00:00 2001 From: weideng Date: Fri, 28 Oct 2016 13:46:37 +0800 Subject: [PATCH 0988/1103] Modify Google's trusty drivers so as to support Intel platform Previously, Google's trusty drivers can just work on ARM platform. With this patch, the trusty drivers can then support Intel platform so as to implement IPC functionality between android and lk. This patch is implemented by Intel, and it has been verified by tipc-test32 test cases which are also provided by Google. Change-Id: I7076ee23eb1eb8f1102feca4b299b34873f7f861 Author: chunmei Signed-off-by: kwang13 Signed-off-by: chunmei Signed-off-by: weideng --- drivers/trusty/Kconfig | 2 +- drivers/trusty/trusty-ipc.c | 9 +- drivers/trusty/trusty-irq.c | 102 ++++++++++++-- drivers/trusty/trusty-log.c | 2 + drivers/trusty/trusty-mem.c | 115 ++++++++-------- drivers/trusty/trusty-virtio.c | 15 ++- drivers/trusty/trusty.c | 236 +++++++++++++++++++++++++-------- include/linux/trusty/trusty.h | 2 +- 8 files changed, 348 insertions(+), 135 deletions(-) diff --git a/drivers/trusty/Kconfig b/drivers/trusty/Kconfig index 052cd8e91ab0..0b6b88e3a718 100644 --- a/drivers/trusty/Kconfig +++ b/drivers/trusty/Kconfig @@ -29,7 +29,7 @@ config TRUSTY_FIQ_ARM64 default y config TRUSTY_LOG - tristate + tristate "Trusty Log support" depends on TRUSTY default y diff --git a/drivers/trusty/trusty-ipc.c b/drivers/trusty/trusty-ipc.c index 06e026344e67..7d66e9f74220 100644 --- a/drivers/trusty/trusty-ipc.c +++ b/drivers/trusty/trusty-ipc.c @@ -33,6 +33,8 @@ #define MAX_DEVICES 4 +#define VIRTIO_ID_TRUSTY_IPC 13 /* virtio trusty ipc */ + #define REPLY_TIMEOUT 5000 #define TXBUF_TIMEOUT 15000 @@ -172,7 +174,10 @@ static int _match_data(int id, void *p, void *data) static void *_alloc_shareable_mem(size_t sz, phys_addr_t *ppa, gfp_t gfp) { - return alloc_pages_exact(sz, gfp); + void *buf_va; + buf_va = alloc_pages_exact(sz, gfp); + *ppa = virt_to_phys(buf_va); + return buf_va; } static void _free_shareable_mem(size_t sz, void *va, phys_addr_t pa) @@ -1597,7 +1602,7 @@ static void tipc_virtio_remove(struct virtio_device *vdev) _cleanup_vq(vds->txvq); _free_msg_buf_list(&vds->free_buf_list); - vdev->config->del_vqs(vds->vdev); + vdev->config->del_vqs(vdev); kref_put(&vds->refcount, _free_vds); } diff --git a/drivers/trusty/trusty-irq.c b/drivers/trusty/trusty-irq.c index 8d6e8afb2a2f..b325bff33774 100644 --- a/drivers/trusty/trusty-irq.c +++ b/drivers/trusty/trusty-irq.c @@ -12,6 +12,7 @@ * */ +#include #include #include #include @@ -22,6 +23,8 @@ #include #include #include +#include +#include #include #include #include @@ -56,6 +59,24 @@ struct trusty_irq_state { struct notifier_block cpu_notifier; }; +#define TRUSTY_VMCALL_PENDING_INTR 0x74727505 +static inline void set_pending_intr_to_lk(uint8_t vector) +{ + __asm__ __volatile__( + "vmcall" + ::"a"(TRUSTY_VMCALL_PENDING_INTR), "b"(vector) + ); +} + +#define TRUSTY_VMCALL_IRQ_DONE 0x74727506 +static inline void irq_register_done(void) +{ + __asm__ __volatile__( + "vmcall" + ::"a"(TRUSTY_VMCALL_IRQ_DONE) + ); +} + static void trusty_irq_enable_pending_irqs(struct trusty_irq_state *is, struct trusty_irq_irqset *irqset, bool percpu) @@ -201,6 +222,8 @@ irqreturn_t trusty_irq_handler(int irq, void *data) __func__, irq, trusty_irq->irq, smp_processor_id(), trusty_irq->enable); + set_pending_intr_to_lk(irq+0x30); + if (trusty_irq->percpu) { disable_percpu_irq(irq); irqset = this_cpu_ptr(is->percpu_irqs); @@ -348,6 +371,39 @@ static int trusty_irq_create_irq_mapping(struct trusty_irq_state *is, int irq) return (!ret) ? -EINVAL : ret; } +static inline void trusty_irq_unmask(struct irq_data *data) +{ + return; +} + +static inline void trusty_irq_mask(struct irq_data *data) +{ + return; +} + +static void trusty_irq_enable(struct irq_data *data) +{ + return; +} + +static void trusty_irq_disable(struct irq_data *data) +{ + return; +} + +void trusty_irq_eoi(struct irq_data *data) +{ + return; +} +static struct irq_chip trusty_irq_chip = { + .name = "TRUSY-IRQ", + .irq_mask = trusty_irq_mask, + .irq_unmask = trusty_irq_unmask, + .irq_enable = trusty_irq_enable, + .irq_disable = trusty_irq_disable, + .irq_eoi = trusty_irq_eoi, +}; + static int trusty_irq_init_normal_irq(struct trusty_irq_state *is, int tirq) { int ret; @@ -357,12 +413,7 @@ static int trusty_irq_init_normal_irq(struct trusty_irq_state *is, int tirq) dev_dbg(is->dev, "%s: irq %d\n", __func__, tirq); - irq = trusty_irq_create_irq_mapping(is, tirq); - if (irq < 0) { - dev_err(is->dev, - "trusty_irq_create_irq_mapping failed (%d)\n", irq); - return irq; - } + irq = tirq; trusty_irq = kzalloc(sizeof(*trusty_irq), GFP_KERNEL); if (!trusty_irq) @@ -376,8 +427,17 @@ static int trusty_irq_init_normal_irq(struct trusty_irq_state *is, int tirq) hlist_add_head(&trusty_irq->node, &is->normal_irqs.inactive); spin_unlock_irqrestore(&is->normal_irqs_lock, irq_flags); + ret = irq_alloc_desc_at(irq, 0); + if (ret >= 0) + irq_set_chip_and_handler_name(irq, &trusty_irq_chip, handle_edge_irq, "trusty-irq"); + else if (ret != -EEXIST) { + dev_err(is->dev, "can't allocate irq desc %d\n", ret); + goto err_request_irq; + } + ret = request_irq(irq, trusty_irq_handler, IRQF_NO_THREAD, - "trusty", trusty_irq); + "trusty-irq", trusty_irq); + if (ret) { dev_err(is->dev, "request_irq failed %d\n", ret); goto err_request_irq; @@ -416,6 +476,8 @@ static int trusty_irq_init_per_cpu_irq(struct trusty_irq_state *is, int tirq) struct trusty_irq *trusty_irq; struct trusty_irq_irqset *irqset; + if (cpu >= 32) + return -EINVAL; trusty_irq = per_cpu_ptr(trusty_irq_handler_data, cpu); irqset = per_cpu_ptr(is->percpu_irqs, cpu); @@ -439,6 +501,8 @@ static int trusty_irq_init_per_cpu_irq(struct trusty_irq_state *is, int tirq) for_each_possible_cpu(cpu) { struct trusty_irq *trusty_irq; + if (cpu >= 32) + return -EINVAL; trusty_irq = per_cpu_ptr(trusty_irq_handler_data, cpu); hlist_del(&trusty_irq->node); } @@ -462,11 +526,11 @@ static int trusty_irq_init_one(struct trusty_irq_state *is, irq = trusty_smc_get_next_irq(is, irq, per_cpu); if (irq < 0) return irq; - + dev_info(is->dev, "irq from lk = %d\n", irq); if (per_cpu) - ret = trusty_irq_init_per_cpu_irq(is, irq); + ret = trusty_irq_init_per_cpu_irq(is, irq-0x30); else - ret = trusty_irq_init_normal_irq(is, irq); + ret = trusty_irq_init_normal_irq(is, irq-0x30); if (ret) { dev_warn(is->dev, @@ -481,7 +545,6 @@ static void trusty_irq_free_irqs(struct trusty_irq_state *is) { struct trusty_irq *irq; struct hlist_node *n; - unsigned int cpu; hlist_for_each_entry_safe(irq, n, &is->normal_irqs.inactive, node) { dev_dbg(is->dev, "%s: irq %d\n", __func__, irq->irq); @@ -489,6 +552,7 @@ static void trusty_irq_free_irqs(struct trusty_irq_state *is) hlist_del(&irq->node); kfree(irq); } +/* hlist_for_each_entry_safe(irq, n, &this_cpu_ptr(is->percpu_irqs)->inactive, node) { @@ -504,7 +568,7 @@ static void trusty_irq_free_irqs(struct trusty_irq_state *is) hlist_del(&irq_tmp->node); } free_percpu(trusty_irq_handler_data); - } + } */ } static int trusty_irq_probe(struct platform_device *pdev) @@ -557,16 +621,18 @@ static int trusty_irq_probe(struct platform_device *pdev) for_each_possible_cpu(cpu) { struct trusty_irq_work *trusty_irq_work; + if (cpu >= 32) + return -EINVAL; trusty_irq_work = per_cpu_ptr(is->irq_work, cpu); trusty_irq_work->is = is; INIT_WORK(&trusty_irq_work->work, work_func); } - for (irq = 0; irq >= 0;) - irq = trusty_irq_init_one(is, irq, true); for (irq = 0; irq >= 0;) irq = trusty_irq_init_one(is, irq, false); + irq_register_done(); + is->cpu_notifier.notifier_call = trusty_irq_cpu_notify; ret = register_hotcpu_notifier(&is->cpu_notifier); if (ret) { @@ -597,6 +663,8 @@ static int trusty_irq_probe(struct platform_device *pdev) for_each_possible_cpu(cpu) { struct trusty_irq_work *trusty_irq_work; + if (cpu >= 32) + return -EINVAL; trusty_irq_work = per_cpu_ptr(is->irq_work, cpu); flush_work(&trusty_irq_work->work); } @@ -632,6 +700,8 @@ static int trusty_irq_remove(struct platform_device *pdev) for_each_possible_cpu(cpu) { struct trusty_irq_work *trusty_irq_work; + if (cpu >= 32) + return -EINVAL; trusty_irq_work = per_cpu_ptr(is->irq_work, cpu); flush_work(&trusty_irq_work->work); } @@ -657,3 +727,7 @@ static struct platform_driver trusty_irq_driver = { }; module_platform_driver(trusty_irq_driver); + + +MODULE_LICENSE("GPL v2"); + diff --git a/drivers/trusty/trusty-log.c b/drivers/trusty/trusty-log.c index e8dcced2ff1d..112287cd4739 100644 --- a/drivers/trusty/trusty-log.c +++ b/drivers/trusty/trusty-log.c @@ -11,6 +11,7 @@ * GNU General Public License for more details. * */ +#include #include #include #include @@ -272,3 +273,4 @@ static struct platform_driver trusty_log_driver = { }; module_platform_driver(trusty_log_driver); +MODULE_LICENSE("GPL"); diff --git a/drivers/trusty/trusty-mem.c b/drivers/trusty/trusty-mem.c index c55ace25beed..1317ec734315 100644 --- a/drivers/trusty/trusty-mem.c +++ b/drivers/trusty/trusty-mem.c @@ -11,66 +11,68 @@ * GNU General Public License for more details. * */ - +#include #include #include #include #include +#include + +/* Normal memory */ +#define NS_MAIR_NORMAL_CACHED_WB_RWA 0xFF /* inner and outer write back read/write allocate */ +#define NS_MAIR_NORMAL_CACHED_WT_RA 0xAA /* inner and outer write through read allocate */ +#define NS_MAIR_NORMAL_CACHED_WB_RA 0xEE /* inner and outer wriet back, read allocate */ +#define NS_MAIR_NORMAL_UNCACHED 0x44 /* uncached */ static int get_mem_attr(struct page *page, pgprot_t pgprot) { -#if defined(CONFIG_ARM64) - uint64_t mair; - uint attr_index = (pgprot_val(pgprot) & PTE_ATTRINDX_MASK) >> 2; - - asm ("mrs %0, mair_el1\n" : "=&r" (mair)); - return (mair >> (attr_index * 8)) & 0xff; - -#elif defined(CONFIG_ARM_LPAE) - uint32_t mair; - uint attr_index = ((pgprot_val(pgprot) & L_PTE_MT_MASK) >> 2); - - if (attr_index >= 4) { - attr_index -= 4; - asm volatile("mrc p15, 0, %0, c10, c2, 1\n" : "=&r" (mair)); - } else { - asm volatile("mrc p15, 0, %0, c10, c2, 0\n" : "=&r" (mair)); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 1, 0) + /* The porting to CHT kernel (3.14.55) is in the #else clause. + ** For BXT kernel (4.1.0), the function get_page_memtype() is static. + ** + ** The orignal google code (for arm) getst the cache states and page + ** flags from input parameter "pgprot", which is not prefered in x86. + ** In x86, both cache states and page flags should be got from input + ** parameter "page". But, since current caller of trusty_call32_mem_buf() + ** always allocate memory in kernel heap, it is also ok to use hardcode + ** here. + ** + ** The memory allocated in kernel heap should be CACHED. The reason to + ** return UNCACHED here is to pass the check in LK sm_decode_ns_memory_attr() + ** with SMP, which only allow UNCACHED. + */ + return NS_MAIR_NORMAL_UNCACHED; +#else + unsigned long type; + int ret_mem_attr = 0; + + type = get_page_memtype(page); + /* + * -1 from get_page_memtype() implies RAM page is in its + * default state and not reserved, and hence of type WB + */ + if (type == -1) { + type = _PAGE_CACHE_MODE_WB; } - return (mair >> (attr_index * 8)) & 0xff; - -#elif defined(CONFIG_ARM) - /* check memory type */ - switch (pgprot_val(pgprot) & L_PTE_MT_MASK) { - case L_PTE_MT_WRITEALLOC: - /* Normal: write back write allocate */ - return 0xFF; - - case L_PTE_MT_BUFFERABLE: - /* Normal: non-cacheble */ - return 0x44; - - case L_PTE_MT_WRITEBACK: - /* Normal: writeback, read allocate */ - return 0xEE; - - case L_PTE_MT_WRITETHROUGH: - /* Normal: write through */ - return 0xAA; - - case L_PTE_MT_UNCACHED: - /* strongly ordered */ - return 0x00; - - case L_PTE_MT_DEV_SHARED: - case L_PTE_MT_DEV_NONSHARED: - /* device */ - return 0x04; + switch (type) { + case _PAGE_CACHE_MODE_UC_MINUS: + /* uncacheable */ + ret_mem_attr = NS_MAIR_NORMAL_UNCACHED; + break; + case _PAGE_CACHE_MODE_WB: + /* writeback */ + ret_mem_attr = NS_MAIR_NORMAL_CACHED_WB_RWA; + break; + case _PAGE_CACHE_MODE_WC: + /* write combined */ + ret_mem_attr = NS_MAIR_NORMAL_UNCACHED; + break; default: - return -EINVAL; + printk(KERN_ERR "%s(): invalid type: 0x%x\n", __func__, type); + ret_mem_attr = -EINVAL; } -#else - return 0; + return ret_mem_attr; #endif } @@ -90,18 +92,10 @@ int trusty_encode_page_info(struct ns_mem_page_info *inf, mem_attr = get_mem_attr(page, pgprot); if (mem_attr < 0) return mem_attr; - - /* add other attributes */ -#if defined(CONFIG_ARM64) || defined(CONFIG_ARM_LPAE) - pte |= pgprot_val(pgprot); -#elif defined(CONFIG_ARM) - if (pgprot_val(pgprot) & L_PTE_USER) + if (pgprot_val(pgprot) & _PAGE_USER) pte |= (1 << 6); - if (pgprot_val(pgprot) & L_PTE_RDONLY) + if (!(pgprot_val(pgprot) & _PAGE_RW)) pte |= (1 << 7); - if (pgprot_val(pgprot) & L_PTE_SHARED) - pte |= (3 << 8); /* inner sharable */ -#endif inf->attr = (pte & 0x0000FFFFFFFFFFFFull) | ((uint64_t)mem_attr << 48); return 0; @@ -131,4 +125,5 @@ int trusty_call32_mem_buf(struct device *dev, u32 smcnr, (u32)(pg_inf.attr >> 32), size); } } - +EXPORT_SYMBOL(trusty_call32_mem_buf); +MODULE_LICENSE("GPL"); diff --git a/drivers/trusty/trusty-virtio.c b/drivers/trusty/trusty-virtio.c index fabbf29bffcc..f00c4ece03bf 100644 --- a/drivers/trusty/trusty-virtio.c +++ b/drivers/trusty/trusty-virtio.c @@ -206,22 +206,23 @@ static void trusty_virtio_reset(struct virtio_device *vdev) dev_dbg(&vdev->dev, "reset vdev_id=%d\n", tvdev->notifyid); trusty_std_call32(tctx->dev->parent, SMC_SC_VDEV_RESET, tvdev->notifyid, 0, 0); + vdev->config->set_status(vdev, 0); } static u64 trusty_virtio_get_features(struct virtio_device *vdev) { struct trusty_vdev *tvdev = vdev_to_tvdev(vdev); - return tvdev->vdev_descr->dfeatures; + return ((u64)tvdev->vdev_descr->dfeatures) & 0x00000000FFFFFFFFULL; } static int trusty_virtio_finalize_features(struct virtio_device *vdev) { struct trusty_vdev *tvdev = vdev_to_tvdev(vdev); - + /* Make sure we don't have any features > 32 bits! */ BUG_ON((u32)vdev->features != vdev->features); - tvdev->vdev_descr->gfeatures = vdev->features; + tvdev->vdev_descr->gfeatures = (u32)(vdev->features); return 0; } @@ -381,6 +382,12 @@ static const struct virtio_config_ops trusty_virtio_config_ops = { .bus_name = trusty_virtio_bus_name, }; +void virtio_vdev_release(struct device *dev) +{ + dev_dbg(dev, "%s() is called\n", __func__); + return; +} + static int trusty_virtio_add_device(struct trusty_ctx *tctx, struct fw_rsc_vdev *vdev_descr, struct fw_rsc_vdev_vring *vr_descr, @@ -400,6 +407,7 @@ static int trusty_virtio_add_device(struct trusty_ctx *tctx, /* setup vdev */ tvdev->tctx = tctx; tvdev->vdev.dev.parent = tctx->dev; + tvdev->vdev.dev.release = virtio_vdev_release; tvdev->vdev.id.device = vdev_descr->id; tvdev->vdev.config = &trusty_virtio_config_ops; tvdev->vdev_descr = vdev_descr; @@ -677,6 +685,7 @@ static const struct of_device_id trusty_of_match[] = { { .compatible = "android,trusty-virtio-v1", }, + {}, }; MODULE_DEVICE_TABLE(of, trusty_of_match); diff --git a/drivers/trusty/trusty.c b/drivers/trusty/trusty.c index 6fcd5481ac88..12a90224eb27 100644 --- a/drivers/trusty/trusty.c +++ b/drivers/trusty/trusty.c @@ -12,7 +12,6 @@ * */ -#include #include #include #include @@ -20,11 +19,14 @@ #include #include #include +#include #include #include #include #include +#define TRUSTY_VMCALL_SMC 0x74727500 + struct trusty_state { struct mutex smc_lock; struct atomic_notifier_head notifier; @@ -33,56 +35,57 @@ struct trusty_state { u32 api_version; }; -#ifdef CONFIG_ARM64 -#define SMC_ARG0 "x0" -#define SMC_ARG1 "x1" -#define SMC_ARG2 "x2" -#define SMC_ARG3 "x3" -#define SMC_ARCH_EXTENSION "" -#define SMC_REGISTERS_TRASHED "x4", "x5", "x6", "x7", "x8", "x9", "x10", "x11", \ - "x12", "x13", "x14", "x15", "x16", "x17" -#else -#define SMC_ARG0 "r0" -#define SMC_ARG1 "r1" -#define SMC_ARG2 "r2" -#define SMC_ARG3 "r3" -#define SMC_ARCH_EXTENSION ".arch_extension sec\n" -#define SMC_REGISTERS_TRASHED "ip" -#endif +struct trusty_smc_interface { + struct device *dev; + ulong args[5]; +}; static inline ulong smc(ulong r0, ulong r1, ulong r2, ulong r3) { - register ulong _r0 asm(SMC_ARG0) = r0; - register ulong _r1 asm(SMC_ARG1) = r1; - register ulong _r2 asm(SMC_ARG2) = r2; - register ulong _r3 asm(SMC_ARG3) = r3; - - asm volatile( - __asmeq("%0", SMC_ARG0) - __asmeq("%1", SMC_ARG1) - __asmeq("%2", SMC_ARG2) - __asmeq("%3", SMC_ARG3) - __asmeq("%4", SMC_ARG0) - __asmeq("%5", SMC_ARG1) - __asmeq("%6", SMC_ARG2) - __asmeq("%7", SMC_ARG3) - SMC_ARCH_EXTENSION - "smc #0" /* switch to secure world */ - : "=r" (_r0), "=r" (_r1), "=r" (_r2), "=r" (_r3) - : "r" (_r0), "r" (_r1), "r" (_r2), "r" (_r3) - : SMC_REGISTERS_TRASHED); - return _r0; + __asm__ __volatile__( + "vmcall; \n" + :"=D"(r0) + :"a"(TRUSTY_VMCALL_SMC), "D"(r0), "S"(r1), "d"(r2), "b"(r3) + ); + return r0; } -s32 trusty_fast_call32(struct device *dev, u32 smcnr, u32 a0, u32 a1, u32 a2) +static void trusty_fast_call32_remote(void *args) { + struct trusty_smc_interface *p_args = args; + struct device *dev = p_args->dev; + ulong smcnr = p_args->args[0]; + ulong a0 = p_args->args[1]; + ulong a1 = p_args->args[2]; + ulong a2 = p_args->args[3]; struct trusty_state *s = platform_get_drvdata(to_platform_device(dev)); BUG_ON(!s); BUG_ON(!SMC_IS_FASTCALL(smcnr)); BUG_ON(SMC_IS_SMC64(smcnr)); - return smc(smcnr, a0, a1, a2); + p_args->args[4] = smc(smcnr, a0, a1, a2); +} + +s32 trusty_fast_call32(struct device *dev, u32 smcnr, u32 a0, u32 a1, u32 a2) +{ + int cpu = 0; + int ret = 0; + struct trusty_smc_interface s; + s.dev = dev; + s.args[0] = smcnr; + s.args[1] = a0; + s.args[2] = a1; + s.args[3] = a2; + s.args[4] = 0; + + ret = smp_call_function_single(cpu, trusty_fast_call32_remote, (void *)&s, 1); + + if (ret) { + pr_err("%s: smp_call_function_single failed: %d\n", __func__, ret); + } + + return s.args[4]; } EXPORT_SYMBOL(trusty_fast_call32); @@ -122,21 +125,59 @@ static ulong trusty_std_call_inner(struct device *dev, ulong smcnr, return ret; } +static void trusty_std_call_inner_wrapper_remote(void *args) +{ + struct trusty_smc_interface *p_args = args; + struct device *dev = p_args->dev; + ulong smcnr = p_args->args[0]; + ulong a0 = p_args->args[1]; + ulong a1 = p_args->args[2]; + ulong a2 = p_args->args[3]; + struct trusty_state *s = platform_get_drvdata(to_platform_device(dev)); + ulong ret; + unsigned long flags; + + local_irq_save(flags); + atomic_notifier_call_chain(&s->notifier, TRUSTY_CALL_PREPARE, + NULL); + ret = trusty_std_call_inner(dev, smcnr, a0, a1, a2); + atomic_notifier_call_chain(&s->notifier, TRUSTY_CALL_RETURNED, + NULL); + local_irq_restore(flags); + + p_args->args[4] = ret; +} + +static ulong trusty_std_call_inner_wrapper(struct device *dev, ulong smcnr, + ulong a0, ulong a1, ulong a2) +{ + int cpu = 0; + int ret = 0; + struct trusty_smc_interface s; + s.dev = dev; + s.args[0] = smcnr; + s.args[1] = a0; + s.args[2] = a1; + s.args[3] = a2; + s.args[4] = 0; + + ret = smp_call_function_single(cpu, trusty_std_call_inner_wrapper_remote, (void *)&s, 1); + + if (ret) { + pr_err("%s: smp_call_function_single failed: %d\n", __func__, ret); + } + + return s.args[4]; +} + static ulong trusty_std_call_helper(struct device *dev, ulong smcnr, ulong a0, ulong a1, ulong a2) { ulong ret; int sleep_time = 1; - struct trusty_state *s = platform_get_drvdata(to_platform_device(dev)); while (true) { - local_irq_disable(); - atomic_notifier_call_chain(&s->notifier, TRUSTY_CALL_PREPARE, - NULL); - ret = trusty_std_call_inner(dev, smcnr, a0, a1, a2); - atomic_notifier_call_chain(&s->notifier, TRUSTY_CALL_RETURNED, - NULL); - local_irq_enable(); + ret = trusty_std_call_inner_wrapper(dev, smcnr, a0, a1, a2); if ((int)ret != SM_ERR_BUSY) break; @@ -173,6 +214,9 @@ static void trusty_std_call_cpu_idle(struct trusty_state *s) } } +/* must set CONFIG_DEBUG_ATOMIC_SLEEP=n +** otherwise mutex_lock() will fail and crash +*/ s32 trusty_std_call32(struct device *dev, u32 smcnr, u32 a0, u32 a1, u32 a2) { int ret; @@ -230,6 +274,7 @@ EXPORT_SYMBOL(trusty_call_notifier_unregister); static int trusty_remove_child(struct device *dev, void *data) { + dev_dbg(dev, "%s() is called()\n", __func__); platform_device_unregister(to_platform_device(dev)); return 0; } @@ -265,6 +310,8 @@ static void trusty_init_version(struct trusty_state *s, struct device *dev) version_str_len = ret; s->version_str = kmalloc(version_str_len + 1, GFP_KERNEL); + if (!s->version_str) + goto err_get_size; for (i = 0; i < version_str_len; i++) { ret = trusty_fast_call32(dev, SMC_FC_GET_VERSION_STR, i, 0, 0); if (ret < 0) @@ -344,15 +391,8 @@ static int trusty_probe(struct platform_device *pdev) if (ret < 0) goto err_api_version; - ret = of_platform_populate(pdev->dev.of_node, NULL, NULL, &pdev->dev); - if (ret < 0) { - dev_err(&pdev->dev, "Failed to add children: %d\n", ret); - goto err_add_children; - } - return 0; -err_add_children: err_api_version: if (s->version_str) { device_remove_file(&pdev->dev, &dev_attr_trusty_version); @@ -369,6 +409,8 @@ static int trusty_remove(struct platform_device *pdev) { struct trusty_state *s = platform_get_drvdata(pdev); + dev_dbg(&(pdev->dev), "%s() is called\n", __func__); + device_for_each_child(&pdev->dev, NULL, trusty_remove_child); mutex_destroy(&s->smc_lock); if (s->version_str) { @@ -394,15 +436,101 @@ static struct platform_driver trusty_driver = { }, }; +void trusty_dev_release(struct device *dev) +{ + dev_dbg(dev, "%s() is called()\n", __func__); + return; +} + +static struct device_node trusty_irq_node = { + .name = "trusty-irq", + .sibling = NULL, +}; + +static struct device_node trusty_virtio_node = { + .name = "trusty-virtio", + .sibling = &trusty_irq_node, +}; + +static struct device_node trusty_log_node = { + .name = "trusty-log", + .sibling = &trusty_virtio_node, +}; + + +static struct device_node trusty_node = { + .name = "trusty", + .child = &trusty_log_node, +}; + +static struct platform_device trusty_platform_dev = { + .name = "trusty", + .id = -1, + .num_resources = 0, + .dev = { + .release = trusty_dev_release, + .of_node = &trusty_node, + }, +}; +static struct platform_device trusty_platform_dev_log = { + .name = "trusty-log", + .id = -1, + .num_resources = 0, + .dev = { + .release = trusty_dev_release, + .parent = &trusty_platform_dev.dev, + .of_node = &trusty_log_node, + }, +}; + +static struct platform_device trusty_platform_dev_virtio = { + .name = "trusty-virtio", + .id = -1, + .num_resources = 0, + .dev = { + .release = trusty_dev_release, + .parent = &trusty_platform_dev.dev, + .of_node = &trusty_virtio_node, + }, +}; + +static struct platform_device trusty_platform_dev_irq = { + .name = "trusty-irq", + .id = -1, + .num_resources = 0, + .dev = { + .release = trusty_dev_release, + .parent = &trusty_platform_dev.dev, + .of_node = &trusty_irq_node, + }, +}; + +static struct platform_device *trusty_devices[] __initdata = { + &trusty_platform_dev, + &trusty_platform_dev_log, + &trusty_platform_dev_virtio, + &trusty_platform_dev_irq +}; static int __init trusty_driver_init(void) { + int ret = 0; + + ret = platform_add_devices(trusty_devices, ARRAY_SIZE(trusty_devices)); + if (ret) { + printk(KERN_ERR "%s(): platform_add_devices() failed, ret %d\n", __func__, ret); + return ret; + } return platform_driver_register(&trusty_driver); } static void __exit trusty_driver_exit(void) { platform_driver_unregister(&trusty_driver); + platform_device_unregister(&trusty_platform_dev); } subsys_initcall(trusty_driver_init); module_exit(trusty_driver_exit); + +MODULE_LICENSE("GPL"); + diff --git a/include/linux/trusty/trusty.h b/include/linux/trusty/trusty.h index 24fe2101a528..74598389c308 100644 --- a/include/linux/trusty/trusty.h +++ b/include/linux/trusty/trusty.h @@ -20,7 +20,7 @@ #include -#ifdef CONFIG_TRUSTY +#if IS_ENABLED(CONFIG_TRUSTY) s32 trusty_std_call32(struct device *dev, u32 smcnr, u32 a0, u32 a1, u32 a2); s32 trusty_fast_call32(struct device *dev, u32 smcnr, u32 a0, u32 a1, u32 a2); #ifdef CONFIG_64BIT From a84580e314b535c59ca73910c45684714dfe5709 Mon Sep 17 00:00:00 2001 From: weideng Date: Mon, 20 Jun 2016 14:19:52 +0800 Subject: [PATCH 0989/1103] Fix the issue for tipc test case closer1 If the server channel accept the connection and immediately close the server channel, the client channel will receive one CONN_RSP message and then immediately one DISCONN_REQ message. At this time, channel status will maintain in CONNECTED status for one short time and dn_wait_for_reply() cannot capture the channel status. This is the reason why closer1 will fail. This patch will add one pulse variable to capture channel CONNECTED status. And it can work well for both UP and SMP mode. Change-Id: I4aac5af714daa67d3095093907c0b9f26af4d76c Signed-off-by: weideng --- drivers/trusty/trusty-ipc.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/drivers/trusty/trusty-ipc.c b/drivers/trusty/trusty-ipc.c index 7d66e9f74220..d6765f1d4510 100644 --- a/drivers/trusty/trusty-ipc.c +++ b/drivers/trusty/trusty-ipc.c @@ -38,6 +38,9 @@ #define REPLY_TIMEOUT 5000 #define TXBUF_TIMEOUT 15000 +#define PULSE_ACTIVE 1 +#define PULSE_DEACTIVE 0 + #define MAX_SRV_NAME_LEN 256 #define MAX_DEV_NAME_LEN 32 @@ -705,6 +708,7 @@ EXPORT_SYMBOL(tipc_chan_destroy); /***************************************************************************/ struct tipc_dn_chan { + int pulse; int state; struct mutex lock; /* protects rx_msg_queue list and channel state */ struct tipc_chan *chan; @@ -729,9 +733,10 @@ static int dn_wait_for_reply(struct tipc_dn_chan *dn, int timeout) ret = -ETIMEDOUT; } else { /* got reply */ - if (dn->state == TIPC_CONNECTED) + if (dn->pulse == PULSE_ACTIVE) { + dn->pulse = PULSE_DEACTIVE; ret = 0; - else if (dn->state == TIPC_DISCONNECTED) + } else if (dn->state == TIPC_DISCONNECTED) if (!list_empty(&dn->rx_msg_queue)) ret = 0; else @@ -775,6 +780,7 @@ static void dn_connected(struct tipc_dn_chan *dn) { mutex_lock(&dn->lock); dn->state = TIPC_CONNECTED; + dn->pulse = PULSE_ACTIVE; /* complete all pending */ complete(&dn->reply_comp); @@ -883,6 +889,7 @@ static int tipc_open(struct inode *inode, struct file *filp) INIT_LIST_HEAD(&dn->rx_msg_queue); dn->state = TIPC_DISCONNECTED; + dn->pulse = PULSE_DEACTIVE; dn->chan = vds_create_channel(vds, &_dn_ops, dn); if (IS_ERR(dn->chan)) { From 493a2f5a16368723926dc5bebc092008340cbd31 Mon Sep 17 00:00:00 2001 From: "Zhu, Bing" Date: Fri, 15 Jul 2016 13:24:42 +0800 Subject: [PATCH 0990/1103] trusty: implement trusty OS timer proxy for performance enhancement Previously VMX timer causes 14 times of vmexit/vmresume switches every 10ms and VMX timer stops when processor enters C3+ sleep state. With linux proxiedtimer implementation, we can reduces vmexit/vmresume switches down to 4. But a drawback is that Trusty OS has no timer during the boot time (before Linux kernel bringup), because Trusty OS also intends to be used to provide services for bootloader, like GVB and FRP(factor reset protection). We plan to solve it in other ways, e.g. taking control of lapic timer before Linux kernel boot. Change-Id: I4baa827ecca51fcca5315a1e973a7533553073a0 Signed-off-by: Zhu, Bing Signed-off-by: Feng, Wang Signed-off-by: weideng Tracked-On: --- drivers/trusty/trusty-irq.c | 2 - drivers/trusty/trusty.c | 87 +++++++++++++++++++++++++++++++++++++ 2 files changed, 87 insertions(+), 2 deletions(-) diff --git a/drivers/trusty/trusty-irq.c b/drivers/trusty/trusty-irq.c index b325bff33774..2c2a792a3636 100644 --- a/drivers/trusty/trusty-irq.c +++ b/drivers/trusty/trusty-irq.c @@ -631,8 +631,6 @@ static int trusty_irq_probe(struct platform_device *pdev) for (irq = 0; irq >= 0;) irq = trusty_irq_init_one(is, irq, false); - irq_register_done(); - is->cpu_notifier.notifier_call = trusty_irq_cpu_notify; ret = register_hotcpu_notifier(&is->cpu_notifier); if (ret) { diff --git a/drivers/trusty/trusty.c b/drivers/trusty/trusty.c index 12a90224eb27..8daf817634d8 100644 --- a/drivers/trusty/trusty.c +++ b/drivers/trusty/trusty.c @@ -26,11 +26,23 @@ #include #define TRUSTY_VMCALL_SMC 0x74727500 +#define TRUSTY_LKTIMER_INTERVAL 10 /* 10 ms */ +#define TRUSTY_LKTIMER_VECTOR 0x31 /* INT_PIT */ + +enum lktimer_mode { + ONESHOT_TIMER, + PERIODICAL_TIMER, +}; struct trusty_state { + struct device *dev; struct mutex smc_lock; struct atomic_notifier_head notifier; struct completion cpu_idle_completion; + struct timer_list timer; + struct work_struct timer_work; + enum lktimer_mode timer_mode; + unsigned long timer_interval; char *version_str; u32 api_version; }; @@ -40,6 +52,72 @@ struct trusty_smc_interface { ulong args[5]; }; +static void trusty_lktimer_work_func(struct work_struct *work) +{ + int ret; + unsigned int vector; + struct trusty_state *s = + container_of(work, struct trusty_state, timer_work); + + dev_dbg(s->dev, "%s\n", __func__); + + /* need vector number only for the first time */ + vector = TRUSTY_LKTIMER_VECTOR; + + do { + ret = trusty_std_call32(s->dev, SMC_SC_NOP, vector, 0, 0); + vector = 0; + } while (ret == SM_ERR_NOP_INTERRUPTED); + + if (ret != SM_ERR_NOP_DONE) + dev_err(s->dev, "%s: SMC_SC_NOP failed %d", __func__, ret); + + dev_notice_once(s->dev, "LK OS proxy timer works\n"); +} + +static void trusty_lktimer_func(unsigned long data) +{ + struct trusty_state *s = (struct trusty_state *)data; + + /* binding it physical CPU0 only because trusty OS runs on it */ + schedule_work_on(0, &s->timer_work); + + /* reactivate the timer again in periodic mode */ + if (s->timer_mode == PERIODICAL_TIMER) + mod_timer(&s->timer, + jiffies + msecs_to_jiffies(s->timer_interval)); +} + +static void trusty_init_lktimer(struct trusty_state *s) +{ + INIT_WORK(&s->timer_work, trusty_lktimer_work_func); + setup_timer(&s->timer, trusty_lktimer_func, (unsigned long)s); +} + +/* note that this function is not thread-safe */ +static void trusty_configure_lktimer(struct trusty_state *s, + enum lktimer_mode mode, unsigned long interval) +{ + if (mode != ONESHOT_TIMER && mode != PERIODICAL_TIMER) { + pr_err("%s: invalid timer mode: %d\n", __func__, mode); + return; + } + + s->timer_mode = mode; + s->timer_interval = interval; + mod_timer(&s->timer, jiffies + msecs_to_jiffies(s->timer_interval)); +} + +/* + * this should be called when removing trusty dev and + * when LK/Trusty crashes, to disable proxy timer. + */ +static void trusty_del_lktimer(struct trusty_state *s) +{ + del_timer_sync(&s->timer); + flush_work(&s->timer_work); +} + static inline ulong smc(ulong r0, ulong r1, ulong r2, ulong r3) { __asm__ __volatile__( @@ -246,6 +324,9 @@ s32 trusty_std_call32(struct device *dev, u32 smcnr, u32 a0, u32 a1, u32 a2) WARN_ONCE(ret == SM_ERR_PANIC, "trusty crashed"); + if (ret == SM_ERR_PANIC) + trusty_del_lktimer(s); + if (smcnr == SMC_SC_NOP) complete(&s->cpu_idle_completion); else @@ -384,6 +465,7 @@ static int trusty_probe(struct platform_device *pdev) ATOMIC_INIT_NOTIFIER_HEAD(&s->notifier); init_completion(&s->cpu_idle_completion); platform_set_drvdata(pdev, s); + s->dev = &pdev->dev; trusty_init_version(s, &pdev->dev); @@ -391,6 +473,10 @@ static int trusty_probe(struct platform_device *pdev) if (ret < 0) goto err_api_version; + trusty_init_lktimer(s); + trusty_configure_lktimer(s, + PERIODICAL_TIMER, TRUSTY_LKTIMER_INTERVAL); + return 0; err_api_version: @@ -417,6 +503,7 @@ static int trusty_remove(struct platform_device *pdev) device_remove_file(&pdev->dev, &dev_attr_trusty_version); kfree(s->version_str); } + trusty_del_lktimer(s); kfree(s); return 0; } From 2f67ac62fcadee977e7a1e911f5383abb467762a Mon Sep 17 00:00:00 2001 From: "Deng, Wei A" Date: Wed, 16 Nov 2016 16:31:43 +0800 Subject: [PATCH 0991/1103] Replace CPU_STARTING/CPU_DYING with CPU_UP_PREPARE/CPU_DEAD CPU_STARTING and CPU_DYING notifier are removed from kernel 4.9. Add this patch to replace them with CPU_UP_PREPARE/CPU_DEAD. Change-Id: I1f48e7a8598dc684e70c8e4bc678723cbb1a0353 Signed-off-by: Deng, Wei A --- drivers/trusty/trusty-irq.c | 4 ++-- drivers/trusty/trusty-virtio.c | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/trusty/trusty-irq.c b/drivers/trusty/trusty-irq.c index 2c2a792a3636..aeb0918dc572 100644 --- a/drivers/trusty/trusty-irq.c +++ b/drivers/trusty/trusty-irq.c @@ -280,10 +280,10 @@ static int trusty_irq_cpu_notify(struct notifier_block *nb, dev_dbg(is->dev, "%s: 0x%lx\n", __func__, action); switch (action & ~CPU_TASKS_FROZEN) { - case CPU_STARTING: + case CPU_UP_PREPARE: trusty_irq_cpu_up(is); break; - case CPU_DYING: + case CPU_DEAD: trusty_irq_cpu_down(is); break; } diff --git a/drivers/trusty/trusty-virtio.c b/drivers/trusty/trusty-virtio.c index f00c4ece03bf..a48f4f9884a8 100644 --- a/drivers/trusty/trusty-virtio.c +++ b/drivers/trusty/trusty-virtio.c @@ -320,7 +320,7 @@ static struct virtqueue *_find_vq(struct virtio_device *vdev, /* da field is only 32 bit wide. Use previously unused 'reserved' field * to store top 32 bits of 64-bit address */ - tvr->vr_descr->reserved = (u32)(pa >> 32); + tvr->vr_descr->pa = (u32)(pa >> 32); dev_info(&vdev->dev, "vring%d: va(pa) %p(%llx) qsz %d notifyid %d\n", id, tvr->vaddr, (u64)tvr->paddr, tvr->elem_num, tvr->notifyid); From d43d5717e3c2d5935cd9d1727601afdad4a73db0 Mon Sep 17 00:00:00 2001 From: Dwane Pottratz Date: Thu, 17 Nov 2016 12:53:31 -0800 Subject: [PATCH 0992/1103] trusty: fix incompatible-pointer-types incompatible-pointer-types found in function trusty_virtio_find_vps drivers/trusty/trusty-virtio.c:380:14: error: initialization from incompatible pointer type [-Werror=incompatible-pointer-types] .find_vqs = trusty_virtio_find_vqs, Change-Id: Idfd949f9ca20b46537db135621bfe17ad1178d36 Signed-off-by: Dwane Pottratz --- drivers/trusty/trusty-virtio.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/trusty/trusty-virtio.c b/drivers/trusty/trusty-virtio.c index a48f4f9884a8..eb4c0d31e249 100644 --- a/drivers/trusty/trusty-virtio.c +++ b/drivers/trusty/trusty-virtio.c @@ -347,7 +347,7 @@ static struct virtqueue *_find_vq(struct virtio_device *vdev, static int trusty_virtio_find_vqs(struct virtio_device *vdev, unsigned nvqs, struct virtqueue *vqs[], vq_callback_t *callbacks[], - const char *names[]) + const char * const names[]) { uint i; int ret; From b216780aec0a5d24adc6fb45eef059ca921d848a Mon Sep 17 00:00:00 2001 From: Michael Ryleev Date: Mon, 12 Dec 2016 14:18:25 +0000 Subject: [PATCH 0993/1103] trusty: move async works off system workqueue Trusty async works might be very CPU intensive, move all Trusty works to separate workqueues. Change-Id: I78a906bc0963beea9b20ad8d8599a31b34546376 Signed-off-by: Michael Ryleev Signed-off-by: weideng Reviewed-by: mark gross --- drivers/trusty/trusty-irq.c | 12 +++++++++++- drivers/trusty/trusty-virtio.c | 29 +++++++++++++++++++++++++++-- 2 files changed, 38 insertions(+), 3 deletions(-) diff --git a/drivers/trusty/trusty-irq.c b/drivers/trusty/trusty-irq.c index aeb0918dc572..5a74d75ce820 100644 --- a/drivers/trusty/trusty-irq.c +++ b/drivers/trusty/trusty-irq.c @@ -57,6 +57,7 @@ struct trusty_irq_state { struct trusty_irq_irqset __percpu *percpu_irqs; struct notifier_block trusty_call_notifier; struct notifier_block cpu_notifier; + struct workqueue_struct *wq; }; #define TRUSTY_VMCALL_PENDING_INTR 0x74727505 @@ -239,7 +240,7 @@ irqreturn_t trusty_irq_handler(int irq, void *data) } spin_unlock(&is->normal_irqs_lock); - schedule_work_on(raw_smp_processor_id(), &trusty_irq_work->work); + queue_work_on(raw_smp_processor_id(), is->wq, &trusty_irq_work->work); dev_dbg(is->dev, "%s: irq %d done\n", __func__, irq); @@ -588,6 +589,12 @@ static int trusty_irq_probe(struct platform_device *pdev) goto err_alloc_is; } + is->wq = alloc_workqueue("trusty-irq-wq", WQ_CPU_INTENSIVE, 0); + if (!is->wq) { + ret = -ENOMEM; + goto err_alloc_wq; + } + is->dev = &pdev->dev; is->trusty_dev = is->dev->parent; is->irq_work = alloc_percpu(struct trusty_irq_work); @@ -668,6 +675,8 @@ static int trusty_irq_probe(struct platform_device *pdev) } free_percpu(is->irq_work); err_alloc_irq_work: + destroy_workqueue(is->wq); +err_alloc_wq: kfree(is); err_alloc_is: return ret; @@ -704,6 +713,7 @@ static int trusty_irq_remove(struct platform_device *pdev) flush_work(&trusty_irq_work->work); } free_percpu(is->irq_work); + destroy_workqueue(is->wq); kfree(is); return 0; diff --git a/drivers/trusty/trusty-virtio.c b/drivers/trusty/trusty-virtio.c index eb4c0d31e249..eaeb020e98f4 100644 --- a/drivers/trusty/trusty-virtio.c +++ b/drivers/trusty/trusty-virtio.c @@ -46,6 +46,8 @@ struct trusty_ctx { struct notifier_block call_notifier; struct list_head vdev_list; struct mutex mlock; /* protects vdev_list */ + struct workqueue_struct *kick_wq; + struct workqueue_struct *check_wq; }; struct trusty_vring { @@ -97,7 +99,7 @@ static int trusty_call_notify(struct notifier_block *nb, return NOTIFY_DONE; tctx = container_of(nb, struct trusty_ctx, call_notifier); - schedule_work(&tctx->check_vqs); + queue_work(tctx->check_wq, &tctx->check_vqs); return NOTIFY_OK; } @@ -143,7 +145,7 @@ static bool trusty_virtio_notify(struct virtqueue *vq) struct trusty_ctx *tctx = tvdev->tctx; atomic_set(&tvr->needs_kick, 1); - schedule_work(&tctx->kick_vqs); + queue_work(tctx->kick_wq, &tctx->kick_vqs); return true; } @@ -641,6 +643,21 @@ static int trusty_virtio_probe(struct platform_device *pdev) INIT_WORK(&tctx->kick_vqs, kick_vqs); platform_set_drvdata(pdev, tctx); + tctx->check_wq = alloc_workqueue("trusty-check-wq", WQ_UNBOUND, 0); + if (!tctx->check_wq) { + ret = -ENODEV; + dev_err(&pdev->dev, "Failed create trusty-check-wq\n"); + goto err_create_check_wq; + } + + tctx->kick_wq = alloc_workqueue("trusty-kick-wq", + WQ_UNBOUND | WQ_CPU_INTENSIVE, 0); + if (!tctx->kick_wq) { + ret = -ENODEV; + dev_err(&pdev->dev, "Failed create trusty-kick-wq\n"); + goto err_create_kick_wq; + } + ret = trusty_virtio_add_devices(tctx); if (ret) { dev_err(&pdev->dev, "Failed to add virtio devices\n"); @@ -651,6 +668,10 @@ static int trusty_virtio_probe(struct platform_device *pdev) return 0; err_add_devices: + destroy_workqueue(tctx->kick_wq); +err_create_kick_wq: + destroy_workqueue(tctx->check_wq); +err_create_check_wq: kfree(tctx); return ret; } @@ -670,6 +691,10 @@ static int trusty_virtio_remove(struct platform_device *pdev) trusty_virtio_remove_devices(tctx); cancel_work_sync(&tctx->kick_vqs); + /* destroy workqueues */ + destroy_workqueue(tctx->kick_wq); + destroy_workqueue(tctx->check_wq); + /* notify remote that shared area goes away */ trusty_virtio_stop(tctx, tctx->shared_va, tctx->shared_sz); From bfa1e1b40c607d30b588ad0bda608d125a15827e Mon Sep 17 00:00:00 2001 From: "Yan, Shaoou" Date: Thu, 8 Dec 2016 04:58:55 +0000 Subject: [PATCH 0994/1103] trusty: print out "Built: " in kernel directly. do this instead of get them from trusty which can save 28 times vmexit/vmresume switch, so we can reduce some boot time Change-Id: I196d506f606a77c1abe9a87d4d48dc18e40ca6bc Tracked-On: Signed-off-by: Feng, Wang Reviewed-by: Ilkka Koskinen --- drivers/trusty/trusty.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/trusty/trusty.c b/drivers/trusty/trusty.c index 8daf817634d8..cfef965402c4 100644 --- a/drivers/trusty/trusty.c +++ b/drivers/trusty/trusty.c @@ -401,7 +401,7 @@ static void trusty_init_version(struct trusty_state *s, struct device *dev) } s->version_str[i] = '\0'; - dev_info(dev, "trusty version: %s\n", s->version_str); + dev_info(dev, "trusty version: Built: %s\n", s->version_str); ret = device_create_file(dev, &dev_attr_trusty_version); if (ret) From 6056d46c7e43758efa247fd5df6680df91d505b0 Mon Sep 17 00:00:00 2001 From: "Yan, Shaoou" Date: Thu, 8 Dec 2016 05:14:48 +0000 Subject: [PATCH 0995/1103] trusty: Popup warning when LK timer interrupt is not as expected LK timer interrupt vector 0x31 should map to irq 1, if not LK timer interrupt is not work as expected Change-Id: I4936bf3dd1d9a21e6913d8d3c4353568eb67c2b2 Tracked-On: Signed-off-by: Feng, Wang Reviewed-by: Ilkka Koskinen --- drivers/trusty/trusty-irq.c | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/drivers/trusty/trusty-irq.c b/drivers/trusty/trusty-irq.c index 5a74d75ce820..6c510a65e784 100644 --- a/drivers/trusty/trusty-irq.c +++ b/drivers/trusty/trusty-irq.c @@ -29,6 +29,9 @@ #include #include +#define IRQ_VECTOR_OFFSET 0x30 +#define IRQ_FOR_LK_TIMER 1 + struct trusty_irq { struct trusty_irq_state *is; struct hlist_node node; @@ -223,7 +226,9 @@ irqreturn_t trusty_irq_handler(int irq, void *data) __func__, irq, trusty_irq->irq, smp_processor_id(), trusty_irq->enable); - set_pending_intr_to_lk(irq+0x30); + WARN_ON(irq != IRQ_FOR_LK_TIMER); + + set_pending_intr_to_lk(irq+IRQ_VECTOR_OFFSET); if (trusty_irq->percpu) { disable_percpu_irq(irq); @@ -528,10 +533,13 @@ static int trusty_irq_init_one(struct trusty_irq_state *is, if (irq < 0) return irq; dev_info(is->dev, "irq from lk = %d\n", irq); + + WARN_ON(irq-IRQ_VECTOR_OFFSET != IRQ_FOR_LK_TIMER); + if (per_cpu) - ret = trusty_irq_init_per_cpu_irq(is, irq-0x30); + ret = trusty_irq_init_per_cpu_irq(is, irq-IRQ_VECTOR_OFFSET); else - ret = trusty_irq_init_normal_irq(is, irq-0x30); + ret = trusty_irq_init_normal_irq(is, irq-IRQ_VECTOR_OFFSET); if (ret) { dev_warn(is->dev, From 4045e91fb0e085d84b5c47a46a5e73a37b1ba95d Mon Sep 17 00:00:00 2001 From: "Yan, Shaoou" Date: Fri, 9 Dec 2016 05:32:20 +0000 Subject: [PATCH 0996/1103] trusty-log: Add vmm panic notifier for vmm deadloop dumping register a new vmcall TRUSTY_VMCALL_DUMP_INIT. Change-Id: Icee169358f30c64da44894dc5816ce5f3020fc70 Tracked-On: Signed-off-by: syan10 Reviewed-by: Ilkka Koskinen --- drivers/trusty/trusty-log.c | 107 ++++++++++++++++++++++++++++++++++++ drivers/trusty/trusty-log.h | 22 ++++++++ 2 files changed, 129 insertions(+) diff --git a/drivers/trusty/trusty-log.c b/drivers/trusty/trusty-log.c index 112287cd4739..a066481c4f1d 100644 --- a/drivers/trusty/trusty-log.c +++ b/drivers/trusty/trusty-log.c @@ -26,6 +26,8 @@ #define TRUSTY_LOG_SIZE (PAGE_SIZE * 2) #define TRUSTY_LINE_BUFFER_SIZE 256 +static uint64_t g_vmm_debug_buf; + struct trusty_log_state { struct device *dev; struct device *trusty_dev; @@ -135,6 +137,72 @@ static int trusty_log_panic_notify(struct notifier_block *nb, return NOTIFY_OK; } +static void trusty_vmm_dump_header(struct deadloop_dump *dump) +{ + struct dump_header *header; + + if (!dump) + return; + + header = &(dump->header); + pr_info("VMM version = %s\n", header->vmm_version); + pr_info("Signature = %s\n", header->signature); + pr_info("Error_info = %s\n", header->error_info); + pr_info("Cpuid = %d\n", header->cpuid); +} + +static void trusty_vmm_dump_data(struct deadloop_dump *dump) +{ + struct dump_data *dump_data; + int i; + + if (!dump) + return; + + dump_data = &(dump->data); + + for (i = 0; i < dump_data->length; i++) + pr_info("%c", dump_data->data[i]); +} + +static int trusty_vmm_panic_notify(struct notifier_block *nb, + unsigned long action, void *data) +{ + struct deadloop_dump *dump_info; + + if (g_vmm_debug_buf) { + dump_info = (struct deadloop_dump *)g_vmm_debug_buf; + + if (dump_info->is_valid) { + pr_info("trusty-vmm panic start!\n"); + trusty_vmm_dump_header(dump_info); + trusty_vmm_dump_data(dump_info); + pr_info("trusty-vmm panic dump end!\n"); + } + } + + return NOTIFY_OK; +} + +static struct notifier_block trusty_vmm_panic_nb = { + .notifier_call = trusty_vmm_panic_notify, + .priority = 0, +}; + +#define TRUSTY_VMCALL_DUMP_INIT 0x74727507 +static int trusty_vmm_dump_init(void *gva) +{ + int ret = -1; + + __asm__ __volatile__( + "vmcall" + : "=a"(ret) + : "a"(TRUSTY_VMCALL_DUMP_INIT), "D"(gva) + ); + + return ret; +} + static bool trusty_supports_logging(struct device *device) { int result; @@ -164,6 +232,7 @@ static int trusty_log_probe(struct platform_device *pdev) struct trusty_log_state *s; int result; phys_addr_t pa; + struct deadloop_dump *dump; dev_dbg(&pdev->dev, "%s\n", __func__); if (!trusty_supports_logging(pdev->dev.parent)) { @@ -216,10 +285,45 @@ static int trusty_log_probe(struct platform_device *pdev) "failed to register panic notifier\n"); goto error_panic_notifier; } + + /* allocate debug buffer for vmm panic dump */ + g_vmm_debug_buf = get_zeroed_page(GFP_KERNEL); + if (!g_vmm_debug_buf) { + result = -ENOMEM; + goto error_alloc_vmm; + } + + dump = (struct deadloop_dump *)g_vmm_debug_buf; + dump->version_of_this_struct = VMM_DUMP_VERSION; + dump->size_of_this_struct = sizeof(struct deadloop_dump); + dump->is_valid = false; + + /* shared the buffer to vmm by VMCALL */ + result = trusty_vmm_dump_init(dump); + if (result < 0) { + dev_err(&pdev->dev, + "failed to share the dump buffer to VMM\n"); + goto error_vmm_panic_notifier; + } + + /* register the panic notifier for vmm */ + result = atomic_notifier_chain_register(&panic_notifier_list, + &trusty_vmm_panic_nb); + if (result < 0) { + dev_err(&pdev->dev, + "failed to register vmm panic notifier\n"); + goto error_vmm_panic_notifier; + } + platform_set_drvdata(pdev, s); return 0; +error_vmm_panic_notifier: + free_page(g_vmm_debug_buf); +error_alloc_vmm: + atomic_notifier_chain_unregister(&panic_notifier_list, + &s->panic_notifier); error_panic_notifier: trusty_call_notifier_unregister(s->trusty_dev, &s->call_notifier); error_call_notifier: @@ -241,6 +345,8 @@ static int trusty_log_remove(struct platform_device *pdev) dev_dbg(&pdev->dev, "%s\n", __func__); + atomic_notifier_chain_unregister(&panic_notifier_list, + &trusty_vmm_panic_nb); atomic_notifier_chain_unregister(&panic_notifier_list, &s->panic_notifier); trusty_call_notifier_unregister(s->trusty_dev, &s->call_notifier); @@ -253,6 +359,7 @@ static int trusty_log_remove(struct platform_device *pdev) } __free_pages(s->log_pages, get_order(TRUSTY_LOG_SIZE)); kfree(s); + free_page(g_vmm_debug_buf); return 0; } diff --git a/drivers/trusty/trusty-log.h b/drivers/trusty/trusty-log.h index 09f60213e1f6..587bc7aaa145 100644 --- a/drivers/trusty/trusty-log.h +++ b/drivers/trusty/trusty-log.h @@ -18,5 +18,27 @@ struct log_rb { #define TRUSTY_LOG_API_VERSION 1 +#define VMM_DUMP_VERSION 1 + +struct dump_data { + uint32_t length; + uint8_t data[0]; +} __packed; + +struct dump_header { + uint8_t vmm_version[64]; /* version of the vmm */ + uint8_t signature[16]; /* signature for the dump structure */ + uint8_t error_info[32]; /* filename:linenum */ + uint16_t cpuid; +} __packed; + +struct deadloop_dump { + uint16_t size_of_this_struct; + uint16_t version_of_this_struct; + uint32_t is_valid; + struct dump_header header; + struct dump_data data; +} __packed; + #endif From 37e1f353ab701f945f9720cfc1ab52880a0a7dfe Mon Sep 17 00:00:00 2001 From: "Yan, Shaoou" Date: Fri, 9 Dec 2016 05:33:22 +0000 Subject: [PATCH 0997/1103] trusty: fix rcu_preempt soft lockup crash issue since we'll run a long TEE/Trusty task, e.g generate 3K RSA key pair, the previous API not meet the requirement of "must be fast and non-blocking for smp_call_function_single()", we replace smp_call_function_single() with work_on_cpu() to bind cpu #0, which can fix the rcu_preempt softup crash issue. Change-Id: I63225c16be50b1ff21accb2ae51114d377c45059 Signed-off-by: Zhu, Bing Signed-off-by: Yan, shaopu Reviewed-by: Ilkka Koskinen --- drivers/trusty/trusty.c | 101 ++++++++++++++++++++-------------------- 1 file changed, 50 insertions(+), 51 deletions(-) diff --git a/drivers/trusty/trusty.c b/drivers/trusty/trusty.c index cfef965402c4..679c5a9a7acf 100644 --- a/drivers/trusty/trusty.c +++ b/drivers/trusty/trusty.c @@ -203,59 +203,22 @@ static ulong trusty_std_call_inner(struct device *dev, ulong smcnr, return ret; } -static void trusty_std_call_inner_wrapper_remote(void *args) -{ - struct trusty_smc_interface *p_args = args; - struct device *dev = p_args->dev; - ulong smcnr = p_args->args[0]; - ulong a0 = p_args->args[1]; - ulong a1 = p_args->args[2]; - ulong a2 = p_args->args[3]; - struct trusty_state *s = platform_get_drvdata(to_platform_device(dev)); - ulong ret; - unsigned long flags; - - local_irq_save(flags); - atomic_notifier_call_chain(&s->notifier, TRUSTY_CALL_PREPARE, - NULL); - ret = trusty_std_call_inner(dev, smcnr, a0, a1, a2); - atomic_notifier_call_chain(&s->notifier, TRUSTY_CALL_RETURNED, - NULL); - local_irq_restore(flags); - - p_args->args[4] = ret; -} - -static ulong trusty_std_call_inner_wrapper(struct device *dev, ulong smcnr, - ulong a0, ulong a1, ulong a2) -{ - int cpu = 0; - int ret = 0; - struct trusty_smc_interface s; - s.dev = dev; - s.args[0] = smcnr; - s.args[1] = a0; - s.args[2] = a1; - s.args[3] = a2; - s.args[4] = 0; - - ret = smp_call_function_single(cpu, trusty_std_call_inner_wrapper_remote, (void *)&s, 1); - - if (ret) { - pr_err("%s: smp_call_function_single failed: %d\n", __func__, ret); - } - - return s.args[4]; -} - static ulong trusty_std_call_helper(struct device *dev, ulong smcnr, ulong a0, ulong a1, ulong a2) { ulong ret; int sleep_time = 1; + unsigned long flags; + struct trusty_state *s = platform_get_drvdata(to_platform_device(dev)); while (true) { - ret = trusty_std_call_inner_wrapper(dev, smcnr, a0, a1, a2); + local_irq_save(flags); + atomic_notifier_call_chain(&s->notifier, TRUSTY_CALL_PREPARE, + NULL); + ret = trusty_std_call_inner(dev, smcnr, a0, a1, a2); + atomic_notifier_call_chain(&s->notifier, TRUSTY_CALL_RETURNED, + NULL); + local_irq_restore(flags); if ((int)ret != SM_ERR_BUSY) break; @@ -292,13 +255,33 @@ static void trusty_std_call_cpu_idle(struct trusty_state *s) } } -/* must set CONFIG_DEBUG_ATOMIC_SLEEP=n -** otherwise mutex_lock() will fail and crash -*/ -s32 trusty_std_call32(struct device *dev, u32 smcnr, u32 a0, u32 a1, u32 a2) + +struct trusty_std_call32_args { + struct device *dev; + u32 smcnr; + u32 a0; + u32 a1; + u32 a2; +}; + +static long trusty_std_call32_work(void *args) { int ret; - struct trusty_state *s = platform_get_drvdata(to_platform_device(dev)); + struct device *dev; + u32 smcnr, a0, a1, a2; + struct trusty_state *s; + struct trusty_std_call32_args *work_args; + + BUG_ON(!args); + + work_args = args; + dev = work_args->dev; + s = platform_get_drvdata(to_platform_device(dev)); + + smcnr = work_args->smcnr; + a0 = work_args->a0; + a1 = work_args->a1; + a2 = work_args->a2; BUG_ON(SMC_IS_FASTCALL(smcnr)); BUG_ON(SMC_IS_SMC64(smcnr)); @@ -334,6 +317,22 @@ s32 trusty_std_call32(struct device *dev, u32 smcnr, u32 a0, u32 a1, u32 a2) return ret; } + +s32 trusty_std_call32(struct device *dev, u32 smcnr, u32 a0, u32 a1, u32 a2) +{ + const int cpu = 0; + struct trusty_std_call32_args args = { + .dev = dev, + .smcnr = smcnr, + .a0 = a0, + .a1 = a1, + .a2 = a2, + }; + + /* bind cpu 0 for now since trusty OS is running on physical cpu #0*/ + return work_on_cpu(cpu, trusty_std_call32_work, (void *) &args); +} + EXPORT_SYMBOL(trusty_std_call32); int trusty_call_notifier_register(struct device *dev, struct notifier_block *n) From c937ee9305eb4ad1d6248c0b284088c7987a1458 Mon Sep 17 00:00:00 2001 From: "Yan, Xiangyang" Date: Wed, 11 Jan 2017 01:26:27 +0000 Subject: [PATCH 0998/1103] trusty: Add VMM PANIC dump data. 1. Increase the alloced size of dump data field to 4 page; 2. Kick off '\r' character in dump data which is outputted from mon_vsprintf_s() in evmm code; Change-Id: I255d97c2a7e898c8d4e1f15777ddd7f7c11af2b0 Tracked-On: Signed-off-by: Yan, Xiangyang Reviewed-by: Gross, Mark --- drivers/trusty/trusty-log.c | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/drivers/trusty/trusty-log.c b/drivers/trusty/trusty-log.c index a066481c4f1d..8091a596a5e3 100644 --- a/drivers/trusty/trusty-log.c +++ b/drivers/trusty/trusty-log.c @@ -154,15 +154,28 @@ static void trusty_vmm_dump_header(struct deadloop_dump *dump) static void trusty_vmm_dump_data(struct deadloop_dump *dump) { struct dump_data *dump_data; - int i; + char *p, *pstr; if (!dump) return; dump_data = &(dump->data); - for (i = 0; i < dump_data->length; i++) - pr_info("%c", dump_data->data[i]); + pstr = (char *)dump_data->data; + for (p = pstr; p < ((char *)dump_data->data + dump_data->length); p++) { + if (*p == '\r') { + *p = 0x00; + } else if (*p == '\n') { + *p = 0x00; + pr_info("%s\n", pstr); + pstr = (char *)(p + 1); + } + } + /* dump the characters in the last line */ + if ((pstr - (char *)(dump_data->data)) < dump_data->length) { + *p = 0x00; + pr_info("%s\n", pstr); + } } static int trusty_vmm_panic_notify(struct notifier_block *nb, @@ -287,7 +300,7 @@ static int trusty_log_probe(struct platform_device *pdev) } /* allocate debug buffer for vmm panic dump */ - g_vmm_debug_buf = get_zeroed_page(GFP_KERNEL); + g_vmm_debug_buf = __get_free_pages(GFP_KERNEL | __GFP_ZERO, 2); if (!g_vmm_debug_buf) { result = -ENOMEM; goto error_alloc_vmm; From a8320fae632fca8dc68f1518e487806a808407e7 Mon Sep 17 00:00:00 2001 From: weideng Date: Tue, 28 Mar 2017 01:40:53 +0000 Subject: [PATCH 0999/1103] Modify Trusty drivers so as to compatible with Kernel 4.11 Cpu_hotplug_register/unregister APIs are removed from Kernel 4.11. Add this patch to fix these issues for kernel change. Change-Id: I0ecafaff20128dd53f80fbdc357918ef69a36da7 Signed-off-by: weideng --- drivers/trusty/trusty-ipc.c | 3 +- drivers/trusty/trusty-irq.c | 96 +++++++++++++++++++--------------- drivers/trusty/trusty-virtio.c | 3 +- 3 files changed, 58 insertions(+), 44 deletions(-) diff --git a/drivers/trusty/trusty-ipc.c b/drivers/trusty/trusty-ipc.c index d6765f1d4510..363b0239310a 100644 --- a/drivers/trusty/trusty-ipc.c +++ b/drivers/trusty/trusty-ipc.c @@ -21,6 +21,7 @@ #include #include #include +#include #include #include #include @@ -1549,7 +1550,7 @@ static int tipc_virtio_probe(struct virtio_device *vdev) vds->cdev_name[sizeof(vds->cdev_name)-1] = '\0'; /* find tx virtqueues (rx and tx and in this order) */ - err = vdev->config->find_vqs(vdev, 2, vqs, vq_cbs, vq_names); + err = vdev->config->find_vqs(vdev, 2, vqs, vq_cbs, vq_names, NULL); if (err) goto err_find_vqs; diff --git a/drivers/trusty/trusty-irq.c b/drivers/trusty/trusty-irq.c index 6c510a65e784..363b302dec0a 100644 --- a/drivers/trusty/trusty-irq.c +++ b/drivers/trusty/trusty-irq.c @@ -59,10 +59,13 @@ struct trusty_irq_state { spinlock_t normal_irqs_lock; struct trusty_irq_irqset __percpu *percpu_irqs; struct notifier_block trusty_call_notifier; - struct notifier_block cpu_notifier; + /* CPU hotplug instances for online */ + struct hlist_node node; struct workqueue_struct *wq; }; +static enum cpuhp_state trusty_irq_online; + #define TRUSTY_VMCALL_PENDING_INTR 0x74727505 static inline void set_pending_intr_to_lk(uint8_t vector) { @@ -252,49 +255,30 @@ irqreturn_t trusty_irq_handler(int irq, void *data) return IRQ_HANDLED; } -static void trusty_irq_cpu_up(void *info) +static int trusty_irq_cpu_up(unsigned int cpu, struct hlist_node *node) { unsigned long irq_flags; - struct trusty_irq_state *is = info; + struct trusty_irq_state *is = hlist_entry_safe(node, struct trusty_irq_state, node); dev_dbg(is->dev, "%s: cpu %d\n", __func__, smp_processor_id()); local_irq_save(irq_flags); trusty_irq_enable_irqset(is, this_cpu_ptr(is->percpu_irqs)); local_irq_restore(irq_flags); + return 0; } -static void trusty_irq_cpu_down(void *info) +static int trusty_irq_cpu_down(unsigned int cpu, struct hlist_node *node) { unsigned long irq_flags; - struct trusty_irq_state *is = info; + struct trusty_irq_state *is = hlist_entry_safe(node, struct trusty_irq_state, node); dev_dbg(is->dev, "%s: cpu %d\n", __func__, smp_processor_id()); local_irq_save(irq_flags); trusty_irq_disable_irqset(is, this_cpu_ptr(is->percpu_irqs)); local_irq_restore(irq_flags); -} - -static int trusty_irq_cpu_notify(struct notifier_block *nb, - unsigned long action, void *hcpu) -{ - struct trusty_irq_state *is; - - is = container_of(nb, struct trusty_irq_state, cpu_notifier); - - dev_dbg(is->dev, "%s: 0x%lx\n", __func__, action); - - switch (action & ~CPU_TASKS_FROZEN) { - case CPU_UP_PREPARE: - trusty_irq_cpu_up(is); - break; - case CPU_DEAD: - trusty_irq_cpu_down(is); - break; - } - - return NOTIFY_OK; + return 0; } static int trusty_irq_create_irq_mapping(struct trusty_irq_state *is, int irq) @@ -580,6 +564,20 @@ static void trusty_irq_free_irqs(struct trusty_irq_state *is) } */ } +static int trusty_irq_cpu_notif_add(struct trusty_irq_state *is) +{ + int ret; + + ret = cpuhp_state_add_instance(trusty_irq_online, &is->node); + + return ret; +} + +static void trusty_irq_cpu_notif_remove(struct trusty_irq_state *is) +{ + cpuhp_state_remove_instance(trusty_irq_online, &is->node); +} + static int trusty_irq_probe(struct platform_device *pdev) { int ret; @@ -646,23 +644,14 @@ static int trusty_irq_probe(struct platform_device *pdev) for (irq = 0; irq >= 0;) irq = trusty_irq_init_one(is, irq, false); - is->cpu_notifier.notifier_call = trusty_irq_cpu_notify; - ret = register_hotcpu_notifier(&is->cpu_notifier); + ret = trusty_irq_cpu_notif_add(is); if (ret) { dev_err(&pdev->dev, "register_cpu_notifier failed %d\n", ret); goto err_register_hotcpu_notifier; } - ret = on_each_cpu(trusty_irq_cpu_up, is, 0); - if (ret) { - dev_err(&pdev->dev, "register_cpu_notifier failed %d\n", ret); - goto err_on_each_cpu; - } return 0; -err_on_each_cpu: - unregister_hotcpu_notifier(&is->cpu_notifier); - on_each_cpu(trusty_irq_cpu_down, is, 1); err_register_hotcpu_notifier: spin_lock_irqsave(&is->normal_irqs_lock, irq_flags); trusty_irq_disable_irqset(is, &is->normal_irqs); @@ -692,17 +681,13 @@ static int trusty_irq_probe(struct platform_device *pdev) static int trusty_irq_remove(struct platform_device *pdev) { - int ret; unsigned int cpu; unsigned long irq_flags; struct trusty_irq_state *is = platform_get_drvdata(pdev); dev_dbg(&pdev->dev, "%s\n", __func__); - unregister_hotcpu_notifier(&is->cpu_notifier); - ret = on_each_cpu(trusty_irq_cpu_down, is, 1); - if (ret) - dev_err(&pdev->dev, "on_each_cpu failed %d\n", ret); + trusty_irq_cpu_notif_remove(is); spin_lock_irqsave(&is->normal_irqs_lock, irq_flags); trusty_irq_disable_irqset(is, &is->normal_irqs); spin_unlock_irqrestore(&is->normal_irqs_lock, irq_flags); @@ -742,8 +727,35 @@ static struct platform_driver trusty_irq_driver = { }, }; -module_platform_driver(trusty_irq_driver); +static int __init trusty_irq_driver_init(void) +{ + int ret; + + ret = cpuhp_setup_state_multi(CPUHP_AP_ONLINE_DYN, "x86/trustyirq:online", + trusty_irq_cpu_up, trusty_irq_cpu_down); + if (ret < 0) + goto out; + trusty_irq_online = ret; + + ret = platform_driver_register(&trusty_irq_driver); + if (ret) + goto err_dead; + + return 0; +err_dead: + cpuhp_remove_multi_state(trusty_irq_online); +out: + return ret; +} + +static void __exit trusty_irq_driver_exit(void) +{ + cpuhp_remove_multi_state(trusty_irq_online); + platform_driver_unregister(&trusty_irq_driver); +} +module_init(trusty_irq_driver_init); +module_exit(trusty_irq_driver_exit); MODULE_LICENSE("GPL v2"); diff --git a/drivers/trusty/trusty-virtio.c b/drivers/trusty/trusty-virtio.c index eaeb020e98f4..3d1a9aabef83 100644 --- a/drivers/trusty/trusty-virtio.c +++ b/drivers/trusty/trusty-virtio.c @@ -349,7 +349,8 @@ static struct virtqueue *_find_vq(struct virtio_device *vdev, static int trusty_virtio_find_vqs(struct virtio_device *vdev, unsigned nvqs, struct virtqueue *vqs[], vq_callback_t *callbacks[], - const char * const names[]) + const char * const names[], + struct irq_affinity *desc) { uint i; int ret; From 5bbf552cce74a8438d6ad8182ec5c3f9114c768c Mon Sep 17 00:00:00 2001 From: yingbinx Date: Wed, 22 Feb 2017 14:28:03 +0800 Subject: [PATCH 1000/1103] Limit to output trusty/lk log on debug version Modified trusty_dump_log() to only output lk side's log on debug version. This is to avoid the condition that tipc drivers will print lots of info/log from lk side at one time to serial console on release version. Details may reference Change-Id: I28681a97a037d08a97d13b8314ab05f4f13b2309 Tracked-On: Tracked-On: Signed-off-by: yingbinx Signed-off-by: weideng Reviewed-on: --- drivers/trusty/trusty-log.c | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/drivers/trusty/trusty-log.c b/drivers/trusty/trusty-log.c index 8091a596a5e3..4200e901d925 100644 --- a/drivers/trusty/trusty-log.c +++ b/drivers/trusty/trusty-log.c @@ -63,7 +63,7 @@ static int log_read_line(struct trusty_log_state *s, int put, int get) return i; } -static void trusty_dump_logs(struct trusty_log_state *s) +static void trusty_dump_logs(struct trusty_log_state *s, bool dump_panic_log) { struct log_rb *log = s->log; uint32_t get, put, alloc; @@ -99,7 +99,10 @@ static void trusty_dump_logs(struct trusty_log_state *s) get = alloc - log->sz; continue; } - pr_info("trusty: %s", s->line_buffer); + + if (dump_panic_log) + pr_info("trusty: %s", s->line_buffer); + get += read_chars; } s->get = get; @@ -116,7 +119,11 @@ static int trusty_log_call_notify(struct notifier_block *nb, s = container_of(nb, struct trusty_log_state, call_notifier); spin_lock_irqsave(&s->lock, flags); - trusty_dump_logs(s); +#ifdef CONFIG_DEBUG_INFO + trusty_dump_logs(s, true); +#else + trusty_dump_logs(s, false); +#endif spin_unlock_irqrestore(&s->lock, flags); return NOTIFY_OK; } @@ -133,7 +140,7 @@ static int trusty_log_panic_notify(struct notifier_block *nb, s = container_of(nb, struct trusty_log_state, panic_notifier); pr_info("trusty-log panic notifier - trusty version %s", trusty_version_str_get(s->trusty_dev)); - trusty_dump_logs(s); + trusty_dump_logs(s, true); return NOTIFY_OK; } From 1faa94b51545ab2955ce0babb448ba5465c146e7 Mon Sep 17 00:00:00 2001 From: "Yan, Xiangyang" Date: Tue, 21 Mar 2017 13:31:33 +0800 Subject: [PATCH 1001/1103] trusty-ipc:tipc_msg_hdr structure: support large message transfer len field type of tipc_msg_hdr structure is u16 which will only handle message length of less than 64K. Change it to u32 to support larger message. Change-Id: I9f08d699842723224a10242d19165fa748a8c8b4 Tracked-On: Signed-off-by: Yan, Xiangyang Reviewed-on: --- drivers/trusty/trusty-ipc.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/trusty/trusty-ipc.c b/drivers/trusty/trusty-ipc.c index 363b0239310a..44843eb811bd 100644 --- a/drivers/trusty/trusty-ipc.c +++ b/drivers/trusty/trusty-ipc.c @@ -71,9 +71,9 @@ struct tipc_dev_config { struct tipc_msg_hdr { u32 src; u32 dst; - u32 reserved; - u16 len; + u32 len; u16 flags; + u16 reserved; u8 data[0]; } __packed; From a4e88258f5029b5f62218928600304d36bff9294 Mon Sep 17 00:00:00 2001 From: "Yan, Shaopu" Date: Mon, 13 Mar 2017 13:22:21 +0800 Subject: [PATCH 1002/1103] trusty-ipc: change DEFAULT_MSG_BUF_SIZE to 68K after hw-backed keymaster enabled, the cts cases of testLargeMsgKat and testLongMsgKat both will failed due to the default CHUNK size(64K) is exceed the channel buffer size in trusty keymaster which use the 4K as default. In order to fix the failed cases, we will enlarge the default channel buffer size to 68K. Change-Id: I2bfb0174430962c6e66c08033be958aaffeca515 Tracked-On: Signed-off-by: Yan, Shaopu Reviewed-on: --- drivers/trusty/trusty-ipc.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/trusty/trusty-ipc.c b/drivers/trusty/trusty-ipc.c index 44843eb811bd..9d6f6bf94f97 100644 --- a/drivers/trusty/trusty-ipc.c +++ b/drivers/trusty/trusty-ipc.c @@ -45,7 +45,8 @@ #define MAX_SRV_NAME_LEN 256 #define MAX_DEV_NAME_LEN 32 -#define DEFAULT_MSG_BUF_SIZE PAGE_SIZE +#define DEFAULT_MSG_BUF_SIZE (68*1024) + #define DEFAULT_MSG_BUF_ALIGN PAGE_SIZE #define TIPC_CTRL_ADDR 53 From 3439759dc2677dec501dc916b6b8853fc6757a39 Mon Sep 17 00:00:00 2001 From: yingbinx Date: Mon, 27 Mar 2017 12:24:04 +0800 Subject: [PATCH 1003/1103] check CPUID while probe trusty drivers. Trusty ipc drivers only work when eVmm is alive. So when probe the trusty drivers, we need to call CPUID to check if eVmm is already existed. Change-Id: I295785b0510729aa2e9d212b243d7c242370389f Tracked-On: Signed-off-by: yingbinx Signed-off-by: weideng Reviewed-on: --- drivers/trusty/trusty-ipc.c | 7 +++++++ drivers/trusty/trusty-irq.c | 6 ++++++ drivers/trusty/trusty-log.c | 6 ++++++ drivers/trusty/trusty-virtio.c | 6 ++++++ drivers/trusty/trusty.c | 6 ++++++ include/linux/trusty/trusty.h | 16 ++++++++++++++++ 6 files changed, 47 insertions(+) diff --git a/drivers/trusty/trusty-ipc.c b/drivers/trusty/trusty-ipc.c index 9d6f6bf94f97..a05c5f957146 100644 --- a/drivers/trusty/trusty-ipc.c +++ b/drivers/trusty/trusty-ipc.c @@ -31,6 +31,7 @@ #include #include +#include #define MAX_DEVICES 4 @@ -1524,6 +1525,12 @@ static int tipc_virtio_probe(struct virtio_device *vdev) vq_callback_t *vq_cbs[] = {_rxvq_cb, _txvq_cb}; const char *vq_names[] = { "rx", "tx" }; + err = trusty_check_cpuid(); + if (err < 0) { + dev_err(&vdev->dev, "CPUID Error: Cannot find eVmm in trusty driver initialization!"); + return -EINVAL; + } + dev_dbg(&vdev->dev, "%s:\n", __func__); vds = kzalloc(sizeof(*vds), GFP_KERNEL); diff --git a/drivers/trusty/trusty-irq.c b/drivers/trusty/trusty-irq.c index 363b302dec0a..afdea66c23c2 100644 --- a/drivers/trusty/trusty-irq.c +++ b/drivers/trusty/trusty-irq.c @@ -587,6 +587,12 @@ static int trusty_irq_probe(struct platform_device *pdev) struct trusty_irq_state *is; work_func_t work_func; + ret = trusty_check_cpuid(); + if (ret < 0) { + dev_err(&pdev->dev, "CPUID Error: Cannot find eVmm in trusty driver initialization!"); + return -EINVAL; + } + dev_dbg(&pdev->dev, "%s\n", __func__); is = kzalloc(sizeof(*is), GFP_KERNEL); diff --git a/drivers/trusty/trusty-log.c b/drivers/trusty/trusty-log.c index 4200e901d925..c977d33ccde5 100644 --- a/drivers/trusty/trusty-log.c +++ b/drivers/trusty/trusty-log.c @@ -254,6 +254,12 @@ static int trusty_log_probe(struct platform_device *pdev) phys_addr_t pa; struct deadloop_dump *dump; + result = trusty_check_cpuid(); + if (result < 0) { + dev_err(&pdev->dev, "CPUID Error: Cannot find eVmm in trusty driver initialization!"); + return -EINVAL; + } + dev_dbg(&pdev->dev, "%s\n", __func__); if (!trusty_supports_logging(pdev->dev.parent)) { return -ENXIO; diff --git a/drivers/trusty/trusty-virtio.c b/drivers/trusty/trusty-virtio.c index 3d1a9aabef83..2ce818cef175 100644 --- a/drivers/trusty/trusty-virtio.c +++ b/drivers/trusty/trusty-virtio.c @@ -628,6 +628,12 @@ static int trusty_virtio_probe(struct platform_device *pdev) int ret; struct trusty_ctx *tctx; + ret = trusty_check_cpuid(); + if (ret < 0) { + dev_err(&pdev->dev, "CPUID Error: Cannot find eVmm in trusty driver initialization!"); + return -EINVAL; + } + dev_info(&pdev->dev, "initializing\n"); tctx = kzalloc(sizeof(*tctx), GFP_KERNEL); diff --git a/drivers/trusty/trusty.c b/drivers/trusty/trusty.c index 679c5a9a7acf..93c73882b00c 100644 --- a/drivers/trusty/trusty.c +++ b/drivers/trusty/trusty.c @@ -450,6 +450,12 @@ static int trusty_probe(struct platform_device *pdev) struct trusty_state *s; struct device_node *node = pdev->dev.of_node; + ret = trusty_check_cpuid(); + if (ret < 0) { + dev_err(&pdev->dev, "CPUID Error: Cannot find eVmm in trusty driver initialization!"); + return -EINVAL; + } + if (!node) { dev_err(&pdev->dev, "of_node required\n"); return -EINVAL; diff --git a/include/linux/trusty/trusty.h b/include/linux/trusty/trusty.h index 74598389c308..7dc2dad40daa 100644 --- a/include/linux/trusty/trusty.h +++ b/include/linux/trusty/trusty.h @@ -69,4 +69,20 @@ int trusty_call32_mem_buf(struct device *dev, u32 smcnr, struct page *page, u32 size, pgprot_t pgprot); +/* CPUID leaf 0x3 is used because eVMM will trap this leaf.*/ +#define EVMM_RUNNING_SIGNATURE_CORP 0x43544E49 /* "INTC", edx */ +#define EVMM_RUNNING_SIGNATURE_MON 0x4D4D5645 /* "XMON", ecx */ + +static inline int trusty_check_cpuid(void) +{ + u32 eax, ebx, ecx, edx; + + cpuid(3, &eax, &ebx, &ecx, &edx); + if ((ecx != EVMM_RUNNING_SIGNATURE_MON) || + (edx != EVMM_RUNNING_SIGNATURE_CORP)) { + return -EINVAL; + } + + return 0; +} #endif From f93eb62e25b0bc1aa3f6ec4c2109e2fc8df9cc88 Mon Sep 17 00:00:00 2001 From: Zhou Furong Date: Thu, 18 May 2017 16:31:32 +0800 Subject: [PATCH 1004/1103] Fix the compile error when update 4.12 virtio API updated on 4.12, trusty need update accordingly. Change-Id: I6ef8a63a23d19cbce1471f9f3bc6e8a38002ad25 Tracked-On: --- drivers/trusty/trusty-ipc.c | 2 +- drivers/trusty/trusty-virtio.c | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/trusty/trusty-ipc.c b/drivers/trusty/trusty-ipc.c index a05c5f957146..68f677f91c21 100644 --- a/drivers/trusty/trusty-ipc.c +++ b/drivers/trusty/trusty-ipc.c @@ -1558,7 +1558,7 @@ static int tipc_virtio_probe(struct virtio_device *vdev) vds->cdev_name[sizeof(vds->cdev_name)-1] = '\0'; /* find tx virtqueues (rx and tx and in this order) */ - err = vdev->config->find_vqs(vdev, 2, vqs, vq_cbs, vq_names, NULL); + err = vdev->config->find_vqs(vdev, 2, vqs, vq_cbs, vq_names, NULL, NULL); if (err) goto err_find_vqs; diff --git a/drivers/trusty/trusty-virtio.c b/drivers/trusty/trusty-virtio.c index 2ce818cef175..2368c10f1b7b 100644 --- a/drivers/trusty/trusty-virtio.c +++ b/drivers/trusty/trusty-virtio.c @@ -328,7 +328,7 @@ static struct virtqueue *_find_vq(struct virtio_device *vdev, id, tvr->vaddr, (u64)tvr->paddr, tvr->elem_num, tvr->notifyid); tvr->vq = vring_new_virtqueue(id, tvr->elem_num, tvr->align, - vdev, true, tvr->vaddr, + vdev, true, true, tvr->vaddr, trusty_virtio_notify, callback, name); if (!tvr->vq) { dev_err(&vdev->dev, "vring_new_virtqueue %s failed\n", @@ -350,6 +350,7 @@ static int trusty_virtio_find_vqs(struct virtio_device *vdev, unsigned nvqs, struct virtqueue *vqs[], vq_callback_t *callbacks[], const char * const names[], + const bool *ctx, struct irq_affinity *desc) { uint i; From 9450b4c4f08764870b4631498ad6f5118e21cdcb Mon Sep 17 00:00:00 2001 From: yingbinx Date: Tue, 9 May 2017 13:45:06 +0800 Subject: [PATCH 1005/1103] trusty: Fix the warnings for eywa building Several warnings are generated while we build for eywa with ARCH i386. The patch is to fix the warnings. Tested by tipc test cases and CTS, all are pass. Change-Id: I2710dd94dfb635f12f5b482a894891bcf725f6be Tracked-On: Signed-off-by: yingbinx Signed-off-by: weideng Reviewed-on: --- drivers/trusty/trusty-log.c | 10 +++++++--- drivers/trusty/trusty-virtio.c | 2 +- include/linux/trusty/trusty.h | 7 +++++++ 3 files changed, 15 insertions(+), 4 deletions(-) diff --git a/drivers/trusty/trusty-log.c b/drivers/trusty/trusty-log.c index c977d33ccde5..0f00d0074fc9 100644 --- a/drivers/trusty/trusty-log.c +++ b/drivers/trusty/trusty-log.c @@ -26,7 +26,11 @@ #define TRUSTY_LOG_SIZE (PAGE_SIZE * 2) #define TRUSTY_LINE_BUFFER_SIZE 256 +#ifdef CONFIG_64BIT static uint64_t g_vmm_debug_buf; +#else +static uint32_t g_vmm_debug_buf; +#endif struct trusty_log_state { struct device *dev; @@ -286,7 +290,7 @@ static int trusty_log_probe(struct platform_device *pdev) pa = page_to_phys(s->log_pages); result = trusty_std_call32(s->trusty_dev, SMC_SC_SHARED_LOG_ADD, - (u32)(pa), (u32)(pa >> 32), + (u32)(pa), (u32)HIULINT(pa), TRUSTY_LOG_SIZE); if (result < 0) { pr_err("trusty std call (SMC_SC_SHARED_LOG_ADD) failed: %d %pa\n", @@ -354,7 +358,7 @@ static int trusty_log_probe(struct platform_device *pdev) trusty_call_notifier_unregister(s->trusty_dev, &s->call_notifier); error_call_notifier: trusty_std_call32(s->trusty_dev, SMC_SC_SHARED_LOG_RM, - (u32)pa, (u32)(pa >> 32), 0); + (u32)pa, (u32)HIULINT(pa), 0); error_std_call: __free_pages(s->log_pages, get_order(TRUSTY_LOG_SIZE)); error_alloc_log: @@ -378,7 +382,7 @@ static int trusty_log_remove(struct platform_device *pdev) trusty_call_notifier_unregister(s->trusty_dev, &s->call_notifier); result = trusty_std_call32(s->trusty_dev, SMC_SC_SHARED_LOG_RM, - (u32)pa, (u32)(pa >> 32), 0); + (u32)pa, (u32)HIULINT(pa), 0); if (result) { pr_err("trusty std call (SMC_SC_SHARED_LOG_RM) failed: %d\n", result); diff --git a/drivers/trusty/trusty-virtio.c b/drivers/trusty/trusty-virtio.c index 2368c10f1b7b..6cb1ec762efe 100644 --- a/drivers/trusty/trusty-virtio.c +++ b/drivers/trusty/trusty-virtio.c @@ -322,7 +322,7 @@ static struct virtqueue *_find_vq(struct virtio_device *vdev, /* da field is only 32 bit wide. Use previously unused 'reserved' field * to store top 32 bits of 64-bit address */ - tvr->vr_descr->pa = (u32)(pa >> 32); + tvr->vr_descr->pa = (u32)HIULINT(pa); dev_info(&vdev->dev, "vring%d: va(pa) %p(%llx) qsz %d notifyid %d\n", id, tvr->vaddr, (u64)tvr->paddr, tvr->elem_num, tvr->notifyid); diff --git a/include/linux/trusty/trusty.h b/include/linux/trusty/trusty.h index 7dc2dad40daa..f7b0a14c9a1d 100644 --- a/include/linux/trusty/trusty.h +++ b/include/linux/trusty/trusty.h @@ -85,4 +85,11 @@ static inline int trusty_check_cpuid(void) return 0; } + +/* High 32 bits of unsigned 64-bit integer*/ +#ifdef CONFIG_64BIT +#define HIULINT(x) ((x) >> 32) +#else +#define HIULINT(x) 0 +#endif #endif From 718ab8290eef15cd0d3ae58ff7b9b23e1a72d534 Mon Sep 17 00:00:00 2001 From: "Zhong,Fangjian" Date: Tue, 6 Jun 2017 00:15:01 +0000 Subject: [PATCH 1006/1103] trusty: Enable dynamic timer Enable the dynamic timer support for Trusty scheduling. Besides periodic timer, Trusty now supports both dynamic timer and periodic timer. Proxy timer drives the Trusty OS scheduling in fixed periodical intervals. Dynamic timer is similar to tickless mode which will not schedule if Trusty OS is idle. This patch will consult Trusty OS for the timer mode to use and enable the specified timer to drive Trusty scheduling. Change-Id: Ic972c40d768cb59a8326842c698fafbe45af906c Signed-off-by: Zhong,Fangjian --- drivers/trusty/trusty.c | 91 ++++++++++++++++++++++++++++++++--- include/linux/trusty/smcall.h | 17 +++++++ 2 files changed, 100 insertions(+), 8 deletions(-) diff --git a/drivers/trusty/trusty.c b/drivers/trusty/trusty.c index 93c73882b00c..647031dacb4e 100644 --- a/drivers/trusty/trusty.c +++ b/drivers/trusty/trusty.c @@ -25,9 +25,10 @@ #include #include -#define TRUSTY_VMCALL_SMC 0x74727500 +#define TRUSTY_VMCALL_SMC 0x74727500 #define TRUSTY_LKTIMER_INTERVAL 10 /* 10 ms */ #define TRUSTY_LKTIMER_VECTOR 0x31 /* INT_PIT */ +#define TRUSTY_STOP_TIMER 0xFFFFFFFF enum lktimer_mode { ONESHOT_TIMER, @@ -52,6 +53,12 @@ struct trusty_smc_interface { ulong args[5]; }; +static struct timer_list *lk_timer; + +static ulong (*smc_func)(ulong r0, ulong r1, ulong r2, ulong r3); +static ulong smc_dynamic_timer(ulong r0, ulong r1, ulong r2, ulong r3); +static ulong smc_periodic_timer(ulong r0, ulong r1, ulong r2, ulong r3); + static void trusty_lktimer_work_func(struct work_struct *work) { int ret; @@ -72,7 +79,7 @@ static void trusty_lktimer_work_func(struct work_struct *work) if (ret != SM_ERR_NOP_DONE) dev_err(s->dev, "%s: SMC_SC_NOP failed %d", __func__, ret); - dev_notice_once(s->dev, "LK OS proxy timer works\n"); + dev_notice_once(s->dev, "LK OS timer works\n"); } static void trusty_lktimer_func(unsigned long data) @@ -92,6 +99,7 @@ static void trusty_init_lktimer(struct trusty_state *s) { INIT_WORK(&s->timer_work, trusty_lktimer_work_func); setup_timer(&s->timer, trusty_lktimer_func, (unsigned long)s); + lk_timer = &s->timer; } /* note that this function is not thread-safe */ @@ -108,6 +116,39 @@ static void trusty_configure_lktimer(struct trusty_state *s, mod_timer(&s->timer, jiffies + msecs_to_jiffies(s->timer_interval)); } +static void trusty_init_smc_function(void) +{ + smc_func = smc_periodic_timer; +} + +static void trusty_set_timer_mode(struct trusty_state *s, struct device *dev) +{ + int ret; + + ret = trusty_fast_call32(dev, SMC_FC_TIMER_MODE, 0, 0, 0); + + if (ret == 0) { + smc_func = smc_dynamic_timer; + } else { + smc_func = smc_periodic_timer; + /* + * If bit 31 set indicates periodic timer is used + * bit 15:0 indicates interval + */ + if ((ret & 0x80000000) && (ret & 0x0FFFF)) { + trusty_configure_lktimer(s, + PERIODICAL_TIMER, + ret & 0x0FFFF); + } else { + /* set periodical timer with default interval */ + trusty_configure_lktimer(s, + PERIODICAL_TIMER, + TRUSTY_LKTIMER_INTERVAL); + } + } + +} + /* * this should be called when removing trusty dev and * when LK/Trusty crashes, to disable proxy timer. @@ -119,12 +160,45 @@ static void trusty_del_lktimer(struct trusty_state *s) } static inline ulong smc(ulong r0, ulong r1, ulong r2, ulong r3) +{ + return smc_func(r0, r1, r2, r3); +} + +static ulong smc_dynamic_timer(ulong r0, ulong r1, ulong r2, ulong r3) { __asm__ __volatile__( "vmcall; \n" - :"=D"(r0) - :"a"(TRUSTY_VMCALL_SMC), "D"(r0), "S"(r1), "d"(r2), "b"(r3) + : "=D"(r0), "=S"(r1), "=d"(r2), "=b"(r3) + : "a"(TRUSTY_VMCALL_SMC), "D"(r0), "S"(r1), "d"(r2), "b"(r3) ); + + if (((r0 == SM_ERR_NOP_INTERRUPTED) || + (r0 == SM_ERR_INTERRUPTED)) && + (r1 != 0)) { + struct trusty_state *s; + + if (lk_timer != NULL) { + s = container_of(lk_timer, struct trusty_state, timer); + if (r1 != TRUSTY_STOP_TIMER) + trusty_configure_lktimer(s, ONESHOT_TIMER, r1); + else + trusty_configure_lktimer(s, ONESHOT_TIMER, 0); + } else { + pr_err("Trusty timer has not been initialized yet!\n"); + } + } + + return r0; +} + +static inline ulong smc_periodic_timer(ulong r0, ulong r1, ulong r2, ulong r3) +{ + __asm__ __volatile__( + "vmcall; \n" + : "=D"(r0), "=S"(r1), "=d"(r2), "=b"(r3) + : "a"(TRUSTY_VMCALL_SMC), "D"(r0), "S"(r1), "d"(r2), "b"(r3) + ); + return r0; } @@ -472,19 +546,20 @@ static int trusty_probe(struct platform_device *pdev) platform_set_drvdata(pdev, s); s->dev = &pdev->dev; + trusty_init_smc_function(); + trusty_init_lktimer(s); + trusty_set_timer_mode(s, &pdev->dev); + trusty_init_version(s, &pdev->dev); ret = trusty_init_api_version(s, &pdev->dev); if (ret < 0) goto err_api_version; - trusty_init_lktimer(s); - trusty_configure_lktimer(s, - PERIODICAL_TIMER, TRUSTY_LKTIMER_INTERVAL); - return 0; err_api_version: + trusty_del_lktimer(s); if (s->version_str) { device_remove_file(&pdev->dev, &dev_attr_trusty_version); kfree(s->version_str); diff --git a/include/linux/trusty/smcall.h b/include/linux/trusty/smcall.h index 1160890a3d90..974b7b3e753d 100644 --- a/include/linux/trusty/smcall.h +++ b/include/linux/trusty/smcall.h @@ -123,6 +123,23 @@ #define TRUSTY_API_VERSION_CURRENT (2) #define SMC_FC_API_VERSION SMC_FASTCALL_NR(SMC_ENTITY_SECURE_MONITOR, 11) +/** + * SMC_FC_TIMER_MODE - Find and set timer mode + * Returns timer mode from trusty. + * + * Return value stands for: + * Bit 31 : + * If this bit is set, trusty uses periodic timer, Android trusty driver + * injects timer interrupt to trusty with specified interval. + * If this bit is clear, trusty uses dynamic timer, Android trusty + * driver injects timer interrupt to trusty on demand. + * Bit 15:0 : + * If bit 31 is set, Android trusty driver injects timer interrupt to + * trusty with interval specified by this field in milliseconds. + * If bit 31 is clear, this field is ignored. + */ +#define SMC_FC_TIMER_MODE SMC_FASTCALL_NR(SMC_ENTITY_SECURE_MONITOR, 12) + /* TRUSTED_OS entity calls */ #define SMC_SC_VIRTIO_GET_DESCR SMC_STDCALL_NR(SMC_ENTITY_TRUSTED_OS, 20) #define SMC_SC_VIRTIO_START SMC_STDCALL_NR(SMC_ENTITY_TRUSTED_OS, 21) From 4597c25da8ac46c91f9afbeacc270584042e220e Mon Sep 17 00:00:00 2001 From: "Zhang, Qi" Date: Wed, 28 Jun 2017 06:26:15 +0000 Subject: [PATCH 1007/1103] check vmm signature for vmm dump Change-Id: Ibc0e1ebf561b0b4278bb5f2d92d173685810aa22 Signed-off-by: Zhang, Qi --- drivers/trusty/trusty-ipc.c | 2 +- drivers/trusty/trusty-irq.c | 2 +- drivers/trusty/trusty-log.c | 55 ++++++++++++++++++---------------- drivers/trusty/trusty-virtio.c | 2 +- drivers/trusty/trusty.c | 2 +- include/linux/trusty/trusty.h | 14 +++++---- 6 files changed, 42 insertions(+), 35 deletions(-) diff --git a/drivers/trusty/trusty-ipc.c b/drivers/trusty/trusty-ipc.c index 68f677f91c21..93003b45eb32 100644 --- a/drivers/trusty/trusty-ipc.c +++ b/drivers/trusty/trusty-ipc.c @@ -1525,7 +1525,7 @@ static int tipc_virtio_probe(struct virtio_device *vdev) vq_callback_t *vq_cbs[] = {_rxvq_cb, _txvq_cb}; const char *vq_names[] = { "rx", "tx" }; - err = trusty_check_cpuid(); + err = trusty_check_cpuid(NULL); if (err < 0) { dev_err(&vdev->dev, "CPUID Error: Cannot find eVmm in trusty driver initialization!"); return -EINVAL; diff --git a/drivers/trusty/trusty-irq.c b/drivers/trusty/trusty-irq.c index afdea66c23c2..d17162c6a85e 100644 --- a/drivers/trusty/trusty-irq.c +++ b/drivers/trusty/trusty-irq.c @@ -587,7 +587,7 @@ static int trusty_irq_probe(struct platform_device *pdev) struct trusty_irq_state *is; work_func_t work_func; - ret = trusty_check_cpuid(); + ret = trusty_check_cpuid(NULL); if (ret < 0) { dev_err(&pdev->dev, "CPUID Error: Cannot find eVmm in trusty driver initialization!"); return -EINVAL; diff --git a/drivers/trusty/trusty-log.c b/drivers/trusty/trusty-log.c index 0f00d0074fc9..c5a85ccaf222 100644 --- a/drivers/trusty/trusty-log.c +++ b/drivers/trusty/trusty-log.c @@ -255,10 +255,11 @@ static int trusty_log_probe(struct platform_device *pdev) { struct trusty_log_state *s; int result; + u32 vmm_signature; phys_addr_t pa; struct deadloop_dump *dump; - result = trusty_check_cpuid(); + result = trusty_check_cpuid(&vmm_signature); if (result < 0) { dev_err(&pdev->dev, "CPUID Error: Cannot find eVmm in trusty driver initialization!"); return -EINVAL; @@ -316,33 +317,35 @@ static int trusty_log_probe(struct platform_device *pdev) goto error_panic_notifier; } - /* allocate debug buffer for vmm panic dump */ - g_vmm_debug_buf = __get_free_pages(GFP_KERNEL | __GFP_ZERO, 2); - if (!g_vmm_debug_buf) { - result = -ENOMEM; - goto error_alloc_vmm; - } - - dump = (struct deadloop_dump *)g_vmm_debug_buf; - dump->version_of_this_struct = VMM_DUMP_VERSION; - dump->size_of_this_struct = sizeof(struct deadloop_dump); - dump->is_valid = false; + if(vmm_signature == EVMM_SIGNATURE_VMM) { + /* allocate debug buffer for vmm panic dump */ + g_vmm_debug_buf = __get_free_pages(GFP_KERNEL | __GFP_ZERO, 2); + if (!g_vmm_debug_buf) { + result = -ENOMEM; + goto error_alloc_vmm; + } - /* shared the buffer to vmm by VMCALL */ - result = trusty_vmm_dump_init(dump); - if (result < 0) { - dev_err(&pdev->dev, - "failed to share the dump buffer to VMM\n"); - goto error_vmm_panic_notifier; - } + dump = (struct deadloop_dump *)g_vmm_debug_buf; + dump->version_of_this_struct = VMM_DUMP_VERSION; + dump->size_of_this_struct = sizeof(struct deadloop_dump); + dump->is_valid = false; + + /* shared the buffer to vmm by VMCALL */ + result = trusty_vmm_dump_init(dump); + if (result < 0) { + dev_err(&pdev->dev, + "failed to share the dump buffer to VMM\n"); + goto error_vmm_panic_notifier; + } - /* register the panic notifier for vmm */ - result = atomic_notifier_chain_register(&panic_notifier_list, - &trusty_vmm_panic_nb); - if (result < 0) { - dev_err(&pdev->dev, - "failed to register vmm panic notifier\n"); - goto error_vmm_panic_notifier; + /* register the panic notifier for vmm */ + result = atomic_notifier_chain_register(&panic_notifier_list, + &trusty_vmm_panic_nb); + if (result < 0) { + dev_err(&pdev->dev, + "failed to register vmm panic notifier\n"); + goto error_vmm_panic_notifier; + } } platform_set_drvdata(pdev, s); diff --git a/drivers/trusty/trusty-virtio.c b/drivers/trusty/trusty-virtio.c index 6cb1ec762efe..6bbf80ce7d7f 100644 --- a/drivers/trusty/trusty-virtio.c +++ b/drivers/trusty/trusty-virtio.c @@ -629,7 +629,7 @@ static int trusty_virtio_probe(struct platform_device *pdev) int ret; struct trusty_ctx *tctx; - ret = trusty_check_cpuid(); + ret = trusty_check_cpuid(NULL); if (ret < 0) { dev_err(&pdev->dev, "CPUID Error: Cannot find eVmm in trusty driver initialization!"); return -EINVAL; diff --git a/drivers/trusty/trusty.c b/drivers/trusty/trusty.c index 647031dacb4e..8e7e715d7018 100644 --- a/drivers/trusty/trusty.c +++ b/drivers/trusty/trusty.c @@ -524,7 +524,7 @@ static int trusty_probe(struct platform_device *pdev) struct trusty_state *s; struct device_node *node = pdev->dev.of_node; - ret = trusty_check_cpuid(); + ret = trusty_check_cpuid(NULL); if (ret < 0) { dev_err(&pdev->dev, "CPUID Error: Cannot find eVmm in trusty driver initialization!"); return -EINVAL; diff --git a/include/linux/trusty/trusty.h b/include/linux/trusty/trusty.h index f7b0a14c9a1d..aba204b9ff3a 100644 --- a/include/linux/trusty/trusty.h +++ b/include/linux/trusty/trusty.h @@ -70,19 +70,23 @@ int trusty_call32_mem_buf(struct device *dev, u32 smcnr, pgprot_t pgprot); /* CPUID leaf 0x3 is used because eVMM will trap this leaf.*/ -#define EVMM_RUNNING_SIGNATURE_CORP 0x43544E49 /* "INTC", edx */ -#define EVMM_RUNNING_SIGNATURE_MON 0x4D4D5645 /* "XMON", ecx */ +#define EVMM_SIGNATURE_CORP 0x43544E49 /* "INTC", edx */ +#define EVMM_SIGNATURE_VMM 0x4D4D5645 /* "EVMM", ecx */ -static inline int trusty_check_cpuid(void) +static inline int trusty_check_cpuid(u32 *vmm_signature) { u32 eax, ebx, ecx, edx; cpuid(3, &eax, &ebx, &ecx, &edx); - if ((ecx != EVMM_RUNNING_SIGNATURE_MON) || - (edx != EVMM_RUNNING_SIGNATURE_CORP)) { + if ((ecx != EVMM_SIGNATURE_VMM) || + (edx != EVMM_SIGNATURE_CORP)) { return -EINVAL; } + if(vmm_signature) { + *vmm_signature = ecx; + } + return 0; } From 840ddac4e433536c69c3017c1566115b8a36de62 Mon Sep 17 00:00:00 2001 From: "Zhong,Fangjian" Date: Tue, 11 Jul 2017 04:15:01 +0000 Subject: [PATCH 1008/1103] Revert "[BXT][DYNAMIC TIMER] Enable dynamic timer" This reverts commit 56dae7b0c686eeaf2ff604497ab940328124f611. Change-Id: I3603787890e6de43acc5f895034237e7b0c5f954 Signed-off-by: Zhong,Fangjian --- drivers/trusty/trusty.c | 88 +++-------------------------------- include/linux/trusty/smcall.h | 17 ------- 2 files changed, 7 insertions(+), 98 deletions(-) diff --git a/drivers/trusty/trusty.c b/drivers/trusty/trusty.c index 8e7e715d7018..2fc1b232fee3 100644 --- a/drivers/trusty/trusty.c +++ b/drivers/trusty/trusty.c @@ -25,10 +25,9 @@ #include #include -#define TRUSTY_VMCALL_SMC 0x74727500 +#define TRUSTY_VMCALL_SMC 0x74727500 #define TRUSTY_LKTIMER_INTERVAL 10 /* 10 ms */ #define TRUSTY_LKTIMER_VECTOR 0x31 /* INT_PIT */ -#define TRUSTY_STOP_TIMER 0xFFFFFFFF enum lktimer_mode { ONESHOT_TIMER, @@ -53,12 +52,6 @@ struct trusty_smc_interface { ulong args[5]; }; -static struct timer_list *lk_timer; - -static ulong (*smc_func)(ulong r0, ulong r1, ulong r2, ulong r3); -static ulong smc_dynamic_timer(ulong r0, ulong r1, ulong r2, ulong r3); -static ulong smc_periodic_timer(ulong r0, ulong r1, ulong r2, ulong r3); - static void trusty_lktimer_work_func(struct work_struct *work) { int ret; @@ -79,7 +72,7 @@ static void trusty_lktimer_work_func(struct work_struct *work) if (ret != SM_ERR_NOP_DONE) dev_err(s->dev, "%s: SMC_SC_NOP failed %d", __func__, ret); - dev_notice_once(s->dev, "LK OS timer works\n"); + dev_notice_once(s->dev, "LK OS proxy timer works\n"); } static void trusty_lktimer_func(unsigned long data) @@ -99,7 +92,6 @@ static void trusty_init_lktimer(struct trusty_state *s) { INIT_WORK(&s->timer_work, trusty_lktimer_work_func); setup_timer(&s->timer, trusty_lktimer_func, (unsigned long)s); - lk_timer = &s->timer; } /* note that this function is not thread-safe */ @@ -116,39 +108,6 @@ static void trusty_configure_lktimer(struct trusty_state *s, mod_timer(&s->timer, jiffies + msecs_to_jiffies(s->timer_interval)); } -static void trusty_init_smc_function(void) -{ - smc_func = smc_periodic_timer; -} - -static void trusty_set_timer_mode(struct trusty_state *s, struct device *dev) -{ - int ret; - - ret = trusty_fast_call32(dev, SMC_FC_TIMER_MODE, 0, 0, 0); - - if (ret == 0) { - smc_func = smc_dynamic_timer; - } else { - smc_func = smc_periodic_timer; - /* - * If bit 31 set indicates periodic timer is used - * bit 15:0 indicates interval - */ - if ((ret & 0x80000000) && (ret & 0x0FFFF)) { - trusty_configure_lktimer(s, - PERIODICAL_TIMER, - ret & 0x0FFFF); - } else { - /* set periodical timer with default interval */ - trusty_configure_lktimer(s, - PERIODICAL_TIMER, - TRUSTY_LKTIMER_INTERVAL); - } - } - -} - /* * this should be called when removing trusty dev and * when LK/Trusty crashes, to disable proxy timer. @@ -160,45 +119,12 @@ static void trusty_del_lktimer(struct trusty_state *s) } static inline ulong smc(ulong r0, ulong r1, ulong r2, ulong r3) -{ - return smc_func(r0, r1, r2, r3); -} - -static ulong smc_dynamic_timer(ulong r0, ulong r1, ulong r2, ulong r3) { __asm__ __volatile__( "vmcall; \n" - : "=D"(r0), "=S"(r1), "=d"(r2), "=b"(r3) + : "=D"(r0) : "a"(TRUSTY_VMCALL_SMC), "D"(r0), "S"(r1), "d"(r2), "b"(r3) ); - - if (((r0 == SM_ERR_NOP_INTERRUPTED) || - (r0 == SM_ERR_INTERRUPTED)) && - (r1 != 0)) { - struct trusty_state *s; - - if (lk_timer != NULL) { - s = container_of(lk_timer, struct trusty_state, timer); - if (r1 != TRUSTY_STOP_TIMER) - trusty_configure_lktimer(s, ONESHOT_TIMER, r1); - else - trusty_configure_lktimer(s, ONESHOT_TIMER, 0); - } else { - pr_err("Trusty timer has not been initialized yet!\n"); - } - } - - return r0; -} - -static inline ulong smc_periodic_timer(ulong r0, ulong r1, ulong r2, ulong r3) -{ - __asm__ __volatile__( - "vmcall; \n" - : "=D"(r0), "=S"(r1), "=d"(r2), "=b"(r3) - : "a"(TRUSTY_VMCALL_SMC), "D"(r0), "S"(r1), "d"(r2), "b"(r3) - ); - return r0; } @@ -546,16 +472,16 @@ static int trusty_probe(struct platform_device *pdev) platform_set_drvdata(pdev, s); s->dev = &pdev->dev; - trusty_init_smc_function(); - trusty_init_lktimer(s); - trusty_set_timer_mode(s, &pdev->dev); - trusty_init_version(s, &pdev->dev); ret = trusty_init_api_version(s, &pdev->dev); if (ret < 0) goto err_api_version; + trusty_init_lktimer(s); + trusty_configure_lktimer(s, + PERIODICAL_TIMER, TRUSTY_LKTIMER_INTERVAL); + return 0; err_api_version: diff --git a/include/linux/trusty/smcall.h b/include/linux/trusty/smcall.h index 974b7b3e753d..1160890a3d90 100644 --- a/include/linux/trusty/smcall.h +++ b/include/linux/trusty/smcall.h @@ -123,23 +123,6 @@ #define TRUSTY_API_VERSION_CURRENT (2) #define SMC_FC_API_VERSION SMC_FASTCALL_NR(SMC_ENTITY_SECURE_MONITOR, 11) -/** - * SMC_FC_TIMER_MODE - Find and set timer mode - * Returns timer mode from trusty. - * - * Return value stands for: - * Bit 31 : - * If this bit is set, trusty uses periodic timer, Android trusty driver - * injects timer interrupt to trusty with specified interval. - * If this bit is clear, trusty uses dynamic timer, Android trusty - * driver injects timer interrupt to trusty on demand. - * Bit 15:0 : - * If bit 31 is set, Android trusty driver injects timer interrupt to - * trusty with interval specified by this field in milliseconds. - * If bit 31 is clear, this field is ignored. - */ -#define SMC_FC_TIMER_MODE SMC_FASTCALL_NR(SMC_ENTITY_SECURE_MONITOR, 12) - /* TRUSTED_OS entity calls */ #define SMC_SC_VIRTIO_GET_DESCR SMC_STDCALL_NR(SMC_ENTITY_TRUSTED_OS, 20) #define SMC_SC_VIRTIO_START SMC_STDCALL_NR(SMC_ENTITY_TRUSTED_OS, 21) From bf2adc34102604375c3bbd4104a6760439a6f3d1 Mon Sep 17 00:00:00 2001 From: "Zhong,Fangjian" Date: Tue, 11 Jul 2017 04:19:21 +0000 Subject: [PATCH 1009/1103] Revert "trusty: implement trusty OS timer proxy for performance enhancement" This reverts commit 3e30c8c0a0b5928bc11fa44571563635a9b1e0a8. Change-Id: I16e64b07a9ddfd50f44ab85ed0aa27925c8ac8a2 Signed-off-by: Zhong,Fangjian --- drivers/trusty/trusty-irq.c | 2 + drivers/trusty/trusty.c | 88 ------------------------------------- 2 files changed, 2 insertions(+), 88 deletions(-) diff --git a/drivers/trusty/trusty-irq.c b/drivers/trusty/trusty-irq.c index d17162c6a85e..e60068b50e04 100644 --- a/drivers/trusty/trusty-irq.c +++ b/drivers/trusty/trusty-irq.c @@ -651,6 +651,8 @@ static int trusty_irq_probe(struct platform_device *pdev) irq = trusty_irq_init_one(is, irq, false); ret = trusty_irq_cpu_notif_add(is); + irq_register_done(); + if (ret) { dev_err(&pdev->dev, "register_cpu_notifier failed %d\n", ret); goto err_register_hotcpu_notifier; diff --git a/drivers/trusty/trusty.c b/drivers/trusty/trusty.c index 2fc1b232fee3..7e55453ae5f5 100644 --- a/drivers/trusty/trusty.c +++ b/drivers/trusty/trusty.c @@ -26,23 +26,11 @@ #include #define TRUSTY_VMCALL_SMC 0x74727500 -#define TRUSTY_LKTIMER_INTERVAL 10 /* 10 ms */ -#define TRUSTY_LKTIMER_VECTOR 0x31 /* INT_PIT */ - -enum lktimer_mode { - ONESHOT_TIMER, - PERIODICAL_TIMER, -}; struct trusty_state { - struct device *dev; struct mutex smc_lock; struct atomic_notifier_head notifier; struct completion cpu_idle_completion; - struct timer_list timer; - struct work_struct timer_work; - enum lktimer_mode timer_mode; - unsigned long timer_interval; char *version_str; u32 api_version; }; @@ -52,72 +40,6 @@ struct trusty_smc_interface { ulong args[5]; }; -static void trusty_lktimer_work_func(struct work_struct *work) -{ - int ret; - unsigned int vector; - struct trusty_state *s = - container_of(work, struct trusty_state, timer_work); - - dev_dbg(s->dev, "%s\n", __func__); - - /* need vector number only for the first time */ - vector = TRUSTY_LKTIMER_VECTOR; - - do { - ret = trusty_std_call32(s->dev, SMC_SC_NOP, vector, 0, 0); - vector = 0; - } while (ret == SM_ERR_NOP_INTERRUPTED); - - if (ret != SM_ERR_NOP_DONE) - dev_err(s->dev, "%s: SMC_SC_NOP failed %d", __func__, ret); - - dev_notice_once(s->dev, "LK OS proxy timer works\n"); -} - -static void trusty_lktimer_func(unsigned long data) -{ - struct trusty_state *s = (struct trusty_state *)data; - - /* binding it physical CPU0 only because trusty OS runs on it */ - schedule_work_on(0, &s->timer_work); - - /* reactivate the timer again in periodic mode */ - if (s->timer_mode == PERIODICAL_TIMER) - mod_timer(&s->timer, - jiffies + msecs_to_jiffies(s->timer_interval)); -} - -static void trusty_init_lktimer(struct trusty_state *s) -{ - INIT_WORK(&s->timer_work, trusty_lktimer_work_func); - setup_timer(&s->timer, trusty_lktimer_func, (unsigned long)s); -} - -/* note that this function is not thread-safe */ -static void trusty_configure_lktimer(struct trusty_state *s, - enum lktimer_mode mode, unsigned long interval) -{ - if (mode != ONESHOT_TIMER && mode != PERIODICAL_TIMER) { - pr_err("%s: invalid timer mode: %d\n", __func__, mode); - return; - } - - s->timer_mode = mode; - s->timer_interval = interval; - mod_timer(&s->timer, jiffies + msecs_to_jiffies(s->timer_interval)); -} - -/* - * this should be called when removing trusty dev and - * when LK/Trusty crashes, to disable proxy timer. - */ -static void trusty_del_lktimer(struct trusty_state *s) -{ - del_timer_sync(&s->timer); - flush_work(&s->timer_work); -} - static inline ulong smc(ulong r0, ulong r1, ulong r2, ulong r3) { __asm__ __volatile__( @@ -307,9 +229,6 @@ static long trusty_std_call32_work(void *args) WARN_ONCE(ret == SM_ERR_PANIC, "trusty crashed"); - if (ret == SM_ERR_PANIC) - trusty_del_lktimer(s); - if (smcnr == SMC_SC_NOP) complete(&s->cpu_idle_completion); else @@ -470,7 +389,6 @@ static int trusty_probe(struct platform_device *pdev) ATOMIC_INIT_NOTIFIER_HEAD(&s->notifier); init_completion(&s->cpu_idle_completion); platform_set_drvdata(pdev, s); - s->dev = &pdev->dev; trusty_init_version(s, &pdev->dev); @@ -478,14 +396,9 @@ static int trusty_probe(struct platform_device *pdev) if (ret < 0) goto err_api_version; - trusty_init_lktimer(s); - trusty_configure_lktimer(s, - PERIODICAL_TIMER, TRUSTY_LKTIMER_INTERVAL); - return 0; err_api_version: - trusty_del_lktimer(s); if (s->version_str) { device_remove_file(&pdev->dev, &dev_attr_trusty_version); kfree(s->version_str); @@ -509,7 +422,6 @@ static int trusty_remove(struct platform_device *pdev) device_remove_file(&pdev->dev, &dev_attr_trusty_version); kfree(s->version_str); } - trusty_del_lktimer(s); kfree(s); return 0; } From b606463e3804bf4d287f047a2eb9f020e494d4e3 Mon Sep 17 00:00:00 2001 From: "Zhang, Qi" Date: Tue, 11 Jul 2017 04:42:49 +0000 Subject: [PATCH 1010/1103] trusty: add support for parameterized NOP ops Parameterized NOPs are introduced by Trusty secure side to facilitate better SMP concurrency. They are effectively NOP calls with parameters that will be routed to appropriate handlers on secure side which can be executed concurrently on multiple CPUs. Parameterized NOPs are represented by trusty_nop structure that has to be initialized by calling trusty_nop_init call. This patch creates queue for such items, adds per CPU work queue to invoke them and adds API to enqueue and dequeue them. Change-Id: I7cf32bfdf07727e7d9b0d955ddfb3bf1b52e3a46 Signed-off-by: Zhong,Fangjian Author: Michael Ryleev --- drivers/trusty/trusty-irq.c | 96 +------------------ drivers/trusty/trusty.c | 169 ++++++++++++++++++++++++++++++++++ include/linux/trusty/smcall.h | 3 +- include/linux/trusty/trusty.h | 17 ++++ 4 files changed, 189 insertions(+), 96 deletions(-) diff --git a/drivers/trusty/trusty-irq.c b/drivers/trusty/trusty-irq.c index e60068b50e04..5b4686f4f85f 100644 --- a/drivers/trusty/trusty-irq.c +++ b/drivers/trusty/trusty-irq.c @@ -41,11 +41,6 @@ struct trusty_irq { struct trusty_irq __percpu *percpu_ptr; }; -struct trusty_irq_work { - struct trusty_irq_state *is; - struct work_struct work; -}; - struct trusty_irq_irqset { struct hlist_head pending; struct hlist_head inactive; @@ -54,14 +49,12 @@ struct trusty_irq_irqset { struct trusty_irq_state { struct device *dev; struct device *trusty_dev; - struct trusty_irq_work __percpu *irq_work; struct trusty_irq_irqset normal_irqs; spinlock_t normal_irqs_lock; struct trusty_irq_irqset __percpu *percpu_irqs; struct notifier_block trusty_call_notifier; /* CPU hotplug instances for online */ struct hlist_node node; - struct workqueue_struct *wq; }; static enum cpuhp_state trusty_irq_online; @@ -183,46 +176,10 @@ static int trusty_irq_call_notify(struct notifier_block *nb, return NOTIFY_OK; } - -static void trusty_irq_work_func_locked_nop(struct work_struct *work) -{ - int ret; - struct trusty_irq_state *is = - container_of(work, struct trusty_irq_work, work)->is; - - dev_dbg(is->dev, "%s\n", __func__); - - ret = trusty_std_call32(is->trusty_dev, SMC_SC_LOCKED_NOP, 0, 0, 0); - if (ret != 0) - dev_err(is->dev, "%s: SMC_SC_LOCKED_NOP failed %d", - __func__, ret); - - dev_dbg(is->dev, "%s: done\n", __func__); -} - -static void trusty_irq_work_func(struct work_struct *work) -{ - int ret; - struct trusty_irq_state *is = - container_of(work, struct trusty_irq_work, work)->is; - - dev_dbg(is->dev, "%s\n", __func__); - - do { - ret = trusty_std_call32(is->trusty_dev, SMC_SC_NOP, 0, 0, 0); - } while (ret == SM_ERR_NOP_INTERRUPTED); - - if (ret != SM_ERR_NOP_DONE) - dev_err(is->dev, "%s: SMC_SC_NOP failed %d", __func__, ret); - - dev_dbg(is->dev, "%s: done\n", __func__); -} - irqreturn_t trusty_irq_handler(int irq, void *data) { struct trusty_irq *trusty_irq = data; struct trusty_irq_state *is = trusty_irq->is; - struct trusty_irq_work *trusty_irq_work = this_cpu_ptr(is->irq_work); struct trusty_irq_irqset *irqset; dev_dbg(is->dev, "%s: irq %d, percpu %d, cpu %d, enable %d\n", @@ -248,7 +205,7 @@ irqreturn_t trusty_irq_handler(int irq, void *data) } spin_unlock(&is->normal_irqs_lock); - queue_work_on(raw_smp_processor_id(), is->wq, &trusty_irq_work->work); + trusty_enqueue_nop(is->trusty_dev, NULL); dev_dbg(is->dev, "%s: irq %d done\n", __func__, irq); @@ -582,10 +539,8 @@ static int trusty_irq_probe(struct platform_device *pdev) { int ret; int irq; - unsigned int cpu; unsigned long irq_flags; struct trusty_irq_state *is; - work_func_t work_func; ret = trusty_check_cpuid(NULL); if (ret < 0) { @@ -601,19 +556,8 @@ static int trusty_irq_probe(struct platform_device *pdev) goto err_alloc_is; } - is->wq = alloc_workqueue("trusty-irq-wq", WQ_CPU_INTENSIVE, 0); - if (!is->wq) { - ret = -ENOMEM; - goto err_alloc_wq; - } - is->dev = &pdev->dev; is->trusty_dev = is->dev->parent; - is->irq_work = alloc_percpu(struct trusty_irq_work); - if (!is->irq_work) { - ret = -ENOMEM; - goto err_alloc_irq_work; - } spin_lock_init(&is->normal_irqs_lock); is->percpu_irqs = alloc_percpu(struct trusty_irq_irqset); if (!is->percpu_irqs) { @@ -632,21 +576,6 @@ static int trusty_irq_probe(struct platform_device *pdev) goto err_trusty_call_notifier_register; } - if (trusty_get_api_version(is->trusty_dev) < TRUSTY_API_VERSION_SMP) - work_func = trusty_irq_work_func_locked_nop; - else - work_func = trusty_irq_work_func; - - for_each_possible_cpu(cpu) { - struct trusty_irq_work *trusty_irq_work; - - if (cpu >= 32) - return -EINVAL; - trusty_irq_work = per_cpu_ptr(is->irq_work, cpu); - trusty_irq_work->is = is; - INIT_WORK(&trusty_irq_work->work, work_func); - } - for (irq = 0; irq >= 0;) irq = trusty_irq_init_one(is, irq, false); @@ -670,18 +599,6 @@ static int trusty_irq_probe(struct platform_device *pdev) err_trusty_call_notifier_register: free_percpu(is->percpu_irqs); err_alloc_pending_percpu_irqs: - for_each_possible_cpu(cpu) { - struct trusty_irq_work *trusty_irq_work; - - if (cpu >= 32) - return -EINVAL; - trusty_irq_work = per_cpu_ptr(is->irq_work, cpu); - flush_work(&trusty_irq_work->work); - } - free_percpu(is->irq_work); -err_alloc_irq_work: - destroy_workqueue(is->wq); -err_alloc_wq: kfree(is); err_alloc_is: return ret; @@ -689,7 +606,6 @@ static int trusty_irq_probe(struct platform_device *pdev) static int trusty_irq_remove(struct platform_device *pdev) { - unsigned int cpu; unsigned long irq_flags; struct trusty_irq_state *is = platform_get_drvdata(pdev); @@ -705,16 +621,6 @@ static int trusty_irq_remove(struct platform_device *pdev) trusty_call_notifier_unregister(is->trusty_dev, &is->trusty_call_notifier); free_percpu(is->percpu_irqs); - for_each_possible_cpu(cpu) { - struct trusty_irq_work *trusty_irq_work; - - if (cpu >= 32) - return -EINVAL; - trusty_irq_work = per_cpu_ptr(is->irq_work, cpu); - flush_work(&trusty_irq_work->work); - } - free_percpu(is->irq_work); - destroy_workqueue(is->wq); kfree(is); return 0; diff --git a/drivers/trusty/trusty.c b/drivers/trusty/trusty.c index 7e55453ae5f5..4aa4a89799dc 100644 --- a/drivers/trusty/trusty.c +++ b/drivers/trusty/trusty.c @@ -27,12 +27,24 @@ #define TRUSTY_VMCALL_SMC 0x74727500 +struct trusty_state; + +struct trusty_work { + struct trusty_state *ts; + struct work_struct work; +}; + struct trusty_state { struct mutex smc_lock; struct atomic_notifier_head notifier; struct completion cpu_idle_completion; char *version_str; u32 api_version; + struct device *dev; + struct workqueue_struct *nop_wq; + struct trusty_work __percpu *nop_works; + struct list_head nop_queue; + spinlock_t nop_lock; /* protects nop_queue */ }; struct trusty_smc_interface { @@ -363,9 +375,116 @@ static int trusty_init_api_version(struct trusty_state *s, struct device *dev) return 0; } +static bool dequeue_nop(struct trusty_state *s, u32 *args) +{ + unsigned long flags; + struct trusty_nop *nop = NULL; + + spin_lock_irqsave(&s->nop_lock, flags); + if (!list_empty(&s->nop_queue)) { + nop = list_first_entry(&s->nop_queue, + struct trusty_nop, node); + list_del_init(&nop->node); + args[0] = nop->args[0]; + args[1] = nop->args[1]; + args[2] = nop->args[2]; + } else { + args[0] = 0; + args[1] = 0; + args[2] = 0; + } + spin_unlock_irqrestore(&s->nop_lock, flags); + return nop; +} + +static void locked_nop_work_func(struct work_struct *work) +{ + int ret; + struct trusty_work *tw = container_of(work, struct trusty_work, work); + struct trusty_state *s = tw->ts; + + dev_dbg(s->dev, "%s\n", __func__); + + ret = trusty_std_call32(s->dev, SMC_SC_LOCKED_NOP, 0, 0, 0); + if (ret != 0) + dev_err(s->dev, "%s: SMC_SC_LOCKED_NOP failed %d", + __func__, ret); + dev_dbg(s->dev, "%s: done\n", __func__); +} + +static void nop_work_func(struct work_struct *work) +{ + int ret; + bool next; + u32 args[3]; + struct trusty_work *tw = container_of(work, struct trusty_work, work); + struct trusty_state *s = tw->ts; + + dev_dbg(s->dev, "%s:\n", __func__); + + dequeue_nop(s, args); + do { + dev_dbg(s->dev, "%s: %x %x %x\n", + __func__, args[0], args[1], args[2]); + + ret = trusty_std_call32(s->dev, SMC_SC_NOP, + args[0], args[1], args[2]); + + next = dequeue_nop(s, args); + + if (ret == SM_ERR_NOP_INTERRUPTED) + next = true; + else if (ret != SM_ERR_NOP_DONE) + dev_err(s->dev, "%s: SMC_SC_NOP failed %d", + __func__, ret); + } while (next); + + dev_dbg(s->dev, "%s: done\n", __func__); +} + +void trusty_enqueue_nop(struct device *dev, struct trusty_nop *nop) +{ + unsigned long flags; + struct trusty_work *tw; + struct trusty_state *s = platform_get_drvdata(to_platform_device(dev)); + + preempt_disable(); + tw = this_cpu_ptr(s->nop_works); + if (nop) { + WARN_ON(s->api_version < TRUSTY_API_VERSION_SMP_NOP); + + spin_lock_irqsave(&s->nop_lock, flags); + if (list_empty(&nop->node)) + list_add_tail(&nop->node, &s->nop_queue); + spin_unlock_irqrestore(&s->nop_lock, flags); + } + queue_work(s->nop_wq, &tw->work); + preempt_enable(); +} +EXPORT_SYMBOL(trusty_enqueue_nop); + +void trusty_dequeue_nop(struct device *dev, struct trusty_nop *nop) +{ + unsigned long flags; + struct trusty_state *s = platform_get_drvdata(to_platform_device(dev)); + + if (WARN_ON(!nop)) + return; + + spin_lock_irqsave(&s->nop_lock, flags); + if (!list_empty(&nop->node)) + list_del_init(&nop->node); + spin_unlock_irqrestore(&s->nop_lock, flags); +} +EXPORT_SYMBOL(trusty_dequeue_nop); + + + static int trusty_probe(struct platform_device *pdev) { int ret; + unsigned int cpu; + work_func_t work_func; struct trusty_state *s; struct device_node *node = pdev->dev.of_node; @@ -385,6 +504,11 @@ static int trusty_probe(struct platform_device *pdev) ret = -ENOMEM; goto err_allocate_state; } + + s->dev = &pdev->dev; + spin_lock_init(&s->nop_lock); + INIT_LIST_HEAD(&s->nop_queue); + mutex_init(&s->smc_lock); ATOMIC_INIT_NOTIFIER_HEAD(&s->notifier); init_completion(&s->cpu_idle_completion); @@ -396,8 +520,43 @@ static int trusty_probe(struct platform_device *pdev) if (ret < 0) goto err_api_version; + s->nop_wq = alloc_workqueue("trusty-nop-wq", WQ_CPU_INTENSIVE, 0); + if (!s->nop_wq) { + ret = -ENODEV; + dev_err(&pdev->dev, "Failed create trusty-nop-wq\n"); + goto err_create_nop_wq; + } + + s->nop_works = alloc_percpu(struct trusty_work); + if (!s->nop_works) { + ret = -ENOMEM; + dev_err(&pdev->dev, "Failed to allocate works\n"); + goto err_alloc_works; + } + + if (s->api_version < TRUSTY_API_VERSION_SMP) + work_func = locked_nop_work_func; + else + work_func = nop_work_func; + + for_each_possible_cpu(cpu) { + struct trusty_work *tw = per_cpu_ptr(s->nop_works, cpu); + + tw->ts = s; + INIT_WORK(&tw->work, work_func); + } + return 0; +err_alloc_works: + for_each_possible_cpu(cpu) { + struct trusty_work *tw = per_cpu_ptr(s->nop_works, cpu); + + flush_work(&tw->work); + } + free_percpu(s->nop_works); + destroy_workqueue(s->nop_wq); +err_create_nop_wq: err_api_version: if (s->version_str) { device_remove_file(&pdev->dev, &dev_attr_trusty_version); @@ -412,11 +571,21 @@ static int trusty_probe(struct platform_device *pdev) static int trusty_remove(struct platform_device *pdev) { + unsigned int cpu; struct trusty_state *s = platform_get_drvdata(pdev); dev_dbg(&(pdev->dev), "%s() is called\n", __func__); device_for_each_child(&pdev->dev, NULL, trusty_remove_child); + + for_each_possible_cpu(cpu) { + struct trusty_work *tw = per_cpu_ptr(s->nop_works, cpu); + + flush_work(&tw->work); + } + free_percpu(s->nop_works); + destroy_workqueue(s->nop_wq); + mutex_destroy(&s->smc_lock); if (s->version_str) { device_remove_file(&pdev->dev, &dev_attr_trusty_version); diff --git a/include/linux/trusty/smcall.h b/include/linux/trusty/smcall.h index 1160890a3d90..fc98b3e5b2e7 100644 --- a/include/linux/trusty/smcall.h +++ b/include/linux/trusty/smcall.h @@ -120,7 +120,8 @@ */ #define TRUSTY_API_VERSION_RESTART_FIQ (1) #define TRUSTY_API_VERSION_SMP (2) -#define TRUSTY_API_VERSION_CURRENT (2) +#define TRUSTY_API_VERSION_SMP_NOP (3) +#define TRUSTY_API_VERSION_CURRENT (3) #define SMC_FC_API_VERSION SMC_FASTCALL_NR(SMC_ENTITY_SECURE_MONITOR, 11) /* TRUSTED_OS entity calls */ diff --git a/include/linux/trusty/trusty.h b/include/linux/trusty/trusty.h index aba204b9ff3a..eaa833bdea73 100644 --- a/include/linux/trusty/trusty.h +++ b/include/linux/trusty/trusty.h @@ -69,6 +69,23 @@ int trusty_call32_mem_buf(struct device *dev, u32 smcnr, struct page *page, u32 size, pgprot_t pgprot); +struct trusty_nop { + struct list_head node; + u32 args[3]; +}; + +static inline void trusty_nop_init(struct trusty_nop *nop, + u32 arg0, u32 arg1, u32 arg2) { + INIT_LIST_HEAD(&nop->node); + nop->args[0] = arg0; + nop->args[1] = arg1; + nop->args[2] = arg2; +} + +void trusty_enqueue_nop(struct device *dev, struct trusty_nop *nop); +void trusty_dequeue_nop(struct device *dev, struct trusty_nop *nop); + + /* CPUID leaf 0x3 is used because eVMM will trap this leaf.*/ #define EVMM_SIGNATURE_CORP 0x43544E49 /* "INTC", edx */ #define EVMM_SIGNATURE_VMM 0x4D4D5645 /* "EVMM", ecx */ From d0e99484858e6fe46eacce21e72761ac56d40706 Mon Sep 17 00:00:00 2001 From: "Zhong,Fangjian" Date: Tue, 11 Jul 2017 04:44:59 +0000 Subject: [PATCH 1011/1103] trusty: switch to use version 3 of TRUSTY_API Version 3 of Trusty API adds support for new command (SMC_NC_VDEV_KICK_VQ) that can be used to notify virtqueue that new item is available. This command is a parameterized NOP, it has to be queued using trusty_enqueue_nop API and as such can be executed concurrently on multiple CPUs. Change-Id: I9ba615e70b59e0689a47fa6eae0a6d9ba6033841 Signed-off-by: Zhong,Fangjian Author: Michael Ryleev --- drivers/trusty/trusty-virtio.c | 18 +++++++++++++++--- include/linux/trusty/smcall.h | 1 + 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/drivers/trusty/trusty-virtio.c b/drivers/trusty/trusty-virtio.c index 6bbf80ce7d7f..b2418d7da5e1 100644 --- a/drivers/trusty/trusty-virtio.c +++ b/drivers/trusty/trusty-virtio.c @@ -60,7 +60,8 @@ struct trusty_vring { atomic_t needs_kick; struct fw_rsc_vdev_vring *vr_descr; struct virtqueue *vq; - struct trusty_vdev *tvdev; + struct trusty_vdev *tvdev; + struct trusty_nop kick_nop; }; struct trusty_vdev { @@ -144,8 +145,14 @@ static bool trusty_virtio_notify(struct virtqueue *vq) struct trusty_vdev *tvdev = tvr->tvdev; struct trusty_ctx *tctx = tvdev->tctx; - atomic_set(&tvr->needs_kick, 1); - queue_work(tctx->kick_wq, &tctx->kick_vqs); + u32 api_ver = trusty_get_api_version(tctx->dev->parent); + + if (api_ver < TRUSTY_API_VERSION_SMP_NOP) { + atomic_set(&tvr->needs_kick, 1); + queue_work(tctx->kick_wq, &tctx->kick_vqs); + } else { + trusty_enqueue_nop(tctx->dev->parent, &tvr->kick_nop); + } return true; } @@ -269,6 +276,9 @@ static void _del_vqs(struct virtio_device *vdev) struct trusty_vring *tvr = &tvdev->vrings[0]; for (i = 0; i < tvdev->vring_num; i++, tvr++) { + /* dequeue kick_nop */ + trusty_dequeue_nop(tvdev->tctx->dev->parent, &tvr->kick_nop); + /* delete vq */ if (tvr->vq) { vring_del_virtqueue(tvr->vq); @@ -431,6 +441,8 @@ static int trusty_virtio_add_device(struct trusty_ctx *tctx, tvr->align = vr_descr->align; tvr->elem_num = vr_descr->num; tvr->notifyid = vr_descr->notifyid; + trusty_nop_init(&tvr->kick_nop, SMC_NC_VDEV_KICK_VQ, + tvdev->notifyid, tvr->notifyid); } /* register device */ diff --git a/include/linux/trusty/smcall.h b/include/linux/trusty/smcall.h index fc98b3e5b2e7..037b3fa4429e 100644 --- a/include/linux/trusty/smcall.h +++ b/include/linux/trusty/smcall.h @@ -131,5 +131,6 @@ #define SMC_SC_VDEV_RESET SMC_STDCALL_NR(SMC_ENTITY_TRUSTED_OS, 23) #define SMC_SC_VDEV_KICK_VQ SMC_STDCALL_NR(SMC_ENTITY_TRUSTED_OS, 24) +#define SMC_NC_VDEV_KICK_VQ SMC_STDCALL_NR(SMC_ENTITY_TRUSTED_OS, 25) #endif /* __LINUX_TRUSTY_SMCALL_H */ From 15ca9385eff0cfc4686af50883818b9c8b5afac1 Mon Sep 17 00:00:00 2001 From: "Zhang, Qi" Date: Tue, 11 Jul 2017 05:03:20 +0000 Subject: [PATCH 1012/1103] trusty: add support for SM Wall object SM Wall is a shared memory buffer established between secure and non-secure side that allows for secure side to publish in efficient manner certain state that non-secure side might acts. This patch adds support for such buffer in a generic way, an API to setup such buffer with secure side and an API to locate it's content based on well object known id's. Change-Id: Ibc4d43bdb7f47e803939461ece2ed848fda5738d Signed-off-by: Zhong,Fangjian Author: Michael Ryleev Author: Zhong,Fangjian --- drivers/trusty/Makefile | 1 + drivers/trusty/trusty-irq.c | 20 ---- drivers/trusty/trusty-wall.c | 199 ++++++++++++++++++++++++++++++++++ drivers/trusty/trusty.c | 22 +++- include/linux/trusty/smcall.h | 21 +++- include/linux/trusty/smwall.h | 90 +++++++++++++++ include/linux/trusty/trusty.h | 13 +++ 7 files changed, 343 insertions(+), 23 deletions(-) create mode 100644 drivers/trusty/trusty-wall.c create mode 100644 include/linux/trusty/smwall.h diff --git a/drivers/trusty/Makefile b/drivers/trusty/Makefile index 9ca451e50dee..c1afb140ee00 100644 --- a/drivers/trusty/Makefile +++ b/drivers/trusty/Makefile @@ -11,3 +11,4 @@ obj-$(CONFIG_TRUSTY_LOG) += trusty-log.o obj-$(CONFIG_TRUSTY) += trusty-mem.o obj-$(CONFIG_TRUSTY_VIRTIO) += trusty-virtio.o obj-$(CONFIG_TRUSTY_VIRTIO_IPC) += trusty-ipc.o +obj-$(CONFIG_TRUSTY) += trusty-wall.o diff --git a/drivers/trusty/trusty-irq.c b/drivers/trusty/trusty-irq.c index 5b4686f4f85f..eda0bff48c40 100644 --- a/drivers/trusty/trusty-irq.c +++ b/drivers/trusty/trusty-irq.c @@ -59,24 +59,6 @@ struct trusty_irq_state { static enum cpuhp_state trusty_irq_online; -#define TRUSTY_VMCALL_PENDING_INTR 0x74727505 -static inline void set_pending_intr_to_lk(uint8_t vector) -{ - __asm__ __volatile__( - "vmcall" - ::"a"(TRUSTY_VMCALL_PENDING_INTR), "b"(vector) - ); -} - -#define TRUSTY_VMCALL_IRQ_DONE 0x74727506 -static inline void irq_register_done(void) -{ - __asm__ __volatile__( - "vmcall" - ::"a"(TRUSTY_VMCALL_IRQ_DONE) - ); -} - static void trusty_irq_enable_pending_irqs(struct trusty_irq_state *is, struct trusty_irq_irqset *irqset, bool percpu) @@ -580,8 +562,6 @@ static int trusty_irq_probe(struct platform_device *pdev) irq = trusty_irq_init_one(is, irq, false); ret = trusty_irq_cpu_notif_add(is); - irq_register_done(); - if (ret) { dev_err(&pdev->dev, "register_cpu_notifier failed %d\n", ret); goto err_register_hotcpu_notifier; diff --git a/drivers/trusty/trusty-wall.c b/drivers/trusty/trusty-wall.c new file mode 100644 index 000000000000..3c33d724b3fa --- /dev/null +++ b/drivers/trusty/trusty-wall.c @@ -0,0 +1,199 @@ +/* + * Copyright (C) 2017 Intel, Inc. + * Copyright (C) 2016 Google, Inc. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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. + * + */ +#include +#include +#include +#include +#include +#include + + +void *trusty_wall_base(struct device *dev) +{ + struct trusty_wall_dev_state *s; + + s = platform_get_drvdata(to_platform_device(dev)); + + if (NULL == s) + return NULL; + + return s->va; +} +EXPORT_SYMBOL(trusty_wall_base); + +void *trusty_wall_per_cpu_item_ptr(struct device *dev, unsigned int cpu, + u32 item_id, size_t exp_sz) +{ + uint i; + struct sm_wall_toc *toc; + struct sm_wall_toc_item *item; + struct trusty_wall_dev_state *s; + + s = platform_get_drvdata(to_platform_device(dev)); + + if (!s->va) { + dev_dbg(s->dev, "No smwall buffer is set\n"); + return NULL; + } + + toc = (struct sm_wall_toc *)s->va; + if (toc->version != SM_WALL_TOC_VER) { + dev_err(s->dev, "Unexpected toc version: %d\n", toc->version); + return NULL; + } + + if (cpu >= toc->cpu_num) { + dev_err(s->dev, "Unsupported cpu (%d) requested\n", cpu); + return NULL; + } + + item = (struct sm_wall_toc_item *)((uintptr_t)toc + + toc->per_cpu_toc_offset); + for (i = 0; i < toc->per_cpu_num_items; i++, item++) { + if (item->id != item_id) + continue; + + if (item->size != exp_sz) { + dev_err(s->dev, + "Size mismatch (%zd vs. %zd) for item_id %d\n", + (size_t)item->size, exp_sz, item_id); + return NULL; + } + + return s->va + toc->per_cpu_base_offset + + cpu * toc->per_cpu_region_size + item->offset; + } + return NULL; +} +EXPORT_SYMBOL(trusty_wall_per_cpu_item_ptr); + +static int trusty_wall_setup(struct trusty_wall_dev_state *s) +{ + int ret; + void *va; + size_t sz; + + /* check if wall feature is supported by Trusted OS */ + ret = trusty_fast_call32(s->trusty_dev, SMC_FC_GET_WALL_SIZE, 0, 0, 0); + if (ret == SM_ERR_UNDEFINED_SMC || ret == SM_ERR_NOT_SUPPORTED) { + /* wall is not supported */ + dev_notice(s->dev, "smwall: is not supported by Trusted OS\n"); + return 0; + } else if (ret < 0) { + dev_err(s->dev, "smwall: failed (%d) to query buffer size\n", + ret); + return ret; + } else if (ret == 0) { + dev_notice(s->dev, "smwall: zero-sized buffer requested\n"); + return 0; + } + sz = (size_t)ret; + + /* allocate memory for shared buffer */ + va = alloc_pages_exact(sz, GFP_KERNEL | __GFP_ZERO); + if (!va) { + dev_err(s->dev, "smwall: failed to allocate buffer\n"); + return -ENOMEM; + } + + /* call into Trusted OS to setup wall */ + ret = trusty_call32_mem_buf(s->trusty_dev, SMC_SC_SETUP_WALL, + virt_to_page(va), sz, PAGE_KERNEL); + if (ret < 0) { + dev_err(s->dev, "smwall: TEE returned (%d)\n", ret); + free_pages_exact(va, sz); + return -ENODEV; + } + + dev_info(s->dev, "smwall: initialized %zu bytes\n", sz); + + s->va = va; + s->sz = sz; + + return 0; +} + +static void trusty_wall_destroy(struct trusty_wall_dev_state *s) +{ + int ret; + + ret = trusty_std_call32(s->trusty_dev, SMC_SC_DESTROY_WALL, 0, 0, 0); + if (ret) { + /** + * It should never happen, but if it happens, it is + * unsafe to free buffer so we have to leak memory + */ + dev_err(s->dev, "Failed (%d) to destroy the wall buffer\n", + ret); + } else { + free_pages_exact(s->va, s->sz); + } +} + +static int trusty_wall_probe(struct platform_device *pdev) +{ + int ret; + struct trusty_wall_dev_state *s; + + dev_dbg(&pdev->dev, "%s\n", __func__); + + s = kzalloc(sizeof(*s), GFP_KERNEL); + if (!s) + return -ENOMEM; + + s->dev = &pdev->dev; + s->trusty_dev = s->dev->parent; + platform_set_drvdata(pdev, s); + + ret = trusty_wall_setup(s); + if (ret < 0) { + dev_warn(s->dev, "Failed (%d) to setup the wall\n", ret); + kfree(s); + return ret; + } + + return 0; +} + +static int trusty_wall_remove(struct platform_device *pdev) +{ + struct trusty_wall_dev_state *s = platform_get_drvdata(pdev); + + trusty_wall_destroy(s); + + return 0; +} + +static const struct of_device_id trusty_wall_of_match[] = { + { .compatible = "android, trusty-wall-v1", }, + {}, +}; + +MODULE_DEVICE_TABLE(of, trusty_wall_of_match); + +static struct platform_driver trusty_wall_driver = { + .probe = trusty_wall_probe, + .remove = trusty_wall_remove, + .driver = { + .name = "trusty-wall", + .owner = THIS_MODULE, + .of_match_table = trusty_wall_of_match, + }, +}; + +module_platform_driver(trusty_wall_driver); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("Trusty smwall driver"); diff --git a/drivers/trusty/trusty.c b/drivers/trusty/trusty.c index 4aa4a89799dc..0b3e75823be1 100644 --- a/drivers/trusty/trusty.c +++ b/drivers/trusty/trusty.c @@ -22,6 +22,7 @@ #include #include #include +#include #include #include @@ -610,12 +611,17 @@ static struct platform_driver trusty_driver = { }, }; -void trusty_dev_release(struct device *dev) +void trusty_dev_release(struct device *dev) { dev_dbg(dev, "%s() is called()\n", __func__); return; } +static struct device_node trusty_wall_node = { + .name = "trusty-wall", + .sibling = NULL, +}; + static struct device_node trusty_irq_node = { .name = "trusty-irq", .sibling = NULL, @@ -679,11 +685,23 @@ static struct platform_device trusty_platform_dev_irq = { }, }; +static struct platform_device trusty_platform_dev_wall = { + .name = "trusty-wall", + .id = -1, + .num_resources = 0, + .dev = { + .release = trusty_dev_release, + .parent = &trusty_platform_dev.dev, + .of_node = &trusty_wall_node, + }, +}; + static struct platform_device *trusty_devices[] __initdata = { &trusty_platform_dev, &trusty_platform_dev_log, &trusty_platform_dev_virtio, - &trusty_platform_dev_irq + &trusty_platform_dev_irq, + &trusty_platform_dev_wall }; static int __init trusty_driver_init(void) { diff --git a/include/linux/trusty/smcall.h b/include/linux/trusty/smcall.h index 037b3fa4429e..ee5dda2560b6 100644 --- a/include/linux/trusty/smcall.h +++ b/include/linux/trusty/smcall.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013-2014 Google Inc. All rights reserved + * Copyright (c) 2013-2016 Google Inc. All rights reserved * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files @@ -124,6 +124,25 @@ #define TRUSTY_API_VERSION_CURRENT (3) #define SMC_FC_API_VERSION SMC_FASTCALL_NR(SMC_ENTITY_SECURE_MONITOR, 11) +/* + * SM Wall is a shared memory buffer established between secure and non-secure + * side that allows for secure side to publish certain state that non-secure + * side might acts on. One known example is a state of per CPU timer on + * platforms that require migration to broadcast timer in deep idle states. + * + * SMC_FC_GET_WALL_SIZE - retrieves the size of memory buffer that will be + * required to setup the SM Wall object. + * + * SMC_SC_SETUP_WALL - specifies location, size and attributes of memory buffer + * allocated by non-secure side to setup the SM Wall object. + * + * SMC_SC_DESTROY_WALL - notifies secure side that previously specifies SM Wall + * object should be released usually as part of normal shutdown sequence. + */ +#define SMC_FC_GET_WALL_SIZE SMC_FASTCALL_NR(SMC_ENTITY_SECURE_MONITOR, 12) +#define SMC_SC_SETUP_WALL SMC_STDCALL_NR(SMC_ENTITY_SECURE_MONITOR, 12) +#define SMC_SC_DESTROY_WALL SMC_STDCALL_NR(SMC_ENTITY_SECURE_MONITOR, 13) + /* TRUSTED_OS entity calls */ #define SMC_SC_VIRTIO_GET_DESCR SMC_STDCALL_NR(SMC_ENTITY_TRUSTED_OS, 20) #define SMC_SC_VIRTIO_START SMC_STDCALL_NR(SMC_ENTITY_TRUSTED_OS, 21) diff --git a/include/linux/trusty/smwall.h b/include/linux/trusty/smwall.h new file mode 100644 index 000000000000..370d8b32f26a --- /dev/null +++ b/include/linux/trusty/smwall.h @@ -0,0 +1,90 @@ +/* + * Copyright (c) 2016 Google Inc. All rights reserved + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files + * (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, + * publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef __LINUX_TRUSTY_SMWALL_H +#define __LINUX_TRUSTY_SMWALL_H + +/** + * DOC: Introduction + * + * SM Wall buffer is formatted by secure side to contain the location of + * objects it exports: + * + * In general it starts with sm_wall_toc header struct followed + * by array of sm_wall_toc_item objects describing location of + * individual objects within SM Wall buffer. + */ + +/* current version of TOC structure */ +#define SM_WALL_TOC_VER 1 + +/** + * struct sm_wall_toc_item - describes individual table of content item + * @id: item id + * @offset: item offset relative to appropriate base. For global items + * it is relative to SM wall buffer base address. For per cpu item, this is an + * offset within each individual per cpu region. + * @size: item size + * @reserved: reserved: must be set to zero + */ +struct sm_wall_toc_item { + u32 id; + u32 offset; + u32 size; + u32 reserved; +}; + +/** + * struct sm_wall_toc - describes sm_wall table of content structure + * @version: current toc structure version + * @cpu_num: number of cpus supported + * @per_cpu_toc_offset: offset of the start of per_cpu item table relative to + * SM wall buffer base address. + * @per_cpu_num_items: number of per cpu toc items located at position + * specified by @per_cpu_toc_offset. + * @per_cpu_base_offset: offset of the start of a sequence of per cpu data + * regions (@cpu_num total) relative to SM wall buffer + * base address. + * @per_cpu_region_size: size of each per cpu data region. + * @global_toc_offset: offset of the start of global item table relative to + * SM wall buffer base address. + * @global_num_items: number of items in global item table + */ +struct sm_wall_toc { + u32 version; + u32 cpu_num; + u32 per_cpu_toc_offset; + u32 per_cpu_num_items; + u32 per_cpu_base_offset; + u32 per_cpu_region_size; + u32 global_toc_offset; + u32 global_num_items; +}; + +struct trusty_wall_dev_state { + struct device *dev; + struct device *trusty_dev; + void *va; + size_t sz; +}; + +#endif /* __LINUX_TRUSTY_SMWALL_H */ diff --git a/include/linux/trusty/trusty.h b/include/linux/trusty/trusty.h index eaa833bdea73..029b0986566f 100644 --- a/include/linux/trusty/trusty.h +++ b/include/linux/trusty/trusty.h @@ -85,6 +85,19 @@ static inline void trusty_nop_init(struct trusty_nop *nop, void trusty_enqueue_nop(struct device *dev, struct trusty_nop *nop); void trusty_dequeue_nop(struct device *dev, struct trusty_nop *nop); +#define TRUSTY_VMCALL_PENDING_INTR 0x74727505 +static inline void set_pending_intr_to_lk(uint8_t vector) +{ + __asm__ __volatile__( + "vmcall" + ::"a"(TRUSTY_VMCALL_PENDING_INTR), "b"(vector) + ); +} + +void trusty_update_wall_info(struct device *dev, void *va, size_t sz); +void *trusty_wall_base(struct device *dev); +void *trusty_wall_per_cpu_item_ptr(struct device *dev, unsigned int cpu, + u32 item_id, size_t exp_sz); /* CPUID leaf 0x3 is used because eVMM will trap this leaf.*/ #define EVMM_SIGNATURE_CORP 0x43544E49 /* "INTC", edx */ From ccfc824c743bfd8df534d0ca593aa049f515d838 Mon Sep 17 00:00:00 2001 From: "Zhong,Fangjian" Date: Tue, 11 Jul 2017 05:09:10 +0000 Subject: [PATCH 1013/1103] trusty: add support for trusty backup timer On some platforms, in certain cpu idle modes, Trusty might lose the state of secure timer that it is using for work scheduling. In such cases, non-secure side would typically migrate such timers to alternative implementations that does not lose their state.Ideally, secure side should have similar mechanizm, but it might not be always feasible due to hardware limitations. This patch introduces a generic workaround for this issue but adding backup non-secure timers that is used to kick cpus out of deep idle modes when appropriate. Change-Id: I7ce18d45db67cc650f7875395451da7a2ed1ab2d Signed-off-by: Zhong,Fangjian Author: Michael Ryleev Author: Zhong,Fangjian --- drivers/trusty/Kconfig | 13 +++ drivers/trusty/Makefile | 1 + drivers/trusty/trusty-timer.c | 166 ++++++++++++++++++++++++++++++++++ drivers/trusty/trusty.c | 19 +++- include/linux/trusty/smwall.h | 14 +++ 5 files changed, 212 insertions(+), 1 deletion(-) create mode 100644 drivers/trusty/trusty-timer.c diff --git a/drivers/trusty/Kconfig b/drivers/trusty/Kconfig index 0b6b88e3a718..7b58db5e9a21 100644 --- a/drivers/trusty/Kconfig +++ b/drivers/trusty/Kconfig @@ -49,4 +49,17 @@ config TRUSTY_VIRTIO_IPC If you choose to build a module, it'll be called trusty-ipc. Say N if unsure. +config TRUSTY_BACKUP_TIMER + tristate "Trusty backup timer" + depends on TRUSTY + default y + help + This module adds support for Trusty backup timer. Trusty backup + timer might be required on platforms that might loose state of + secure timer in deep idle state. + + If you choose to build a module, it'll be called trusty-timer. + Say N if unsure. + + endmenu diff --git a/drivers/trusty/Makefile b/drivers/trusty/Makefile index c1afb140ee00..69a78688f1b0 100644 --- a/drivers/trusty/Makefile +++ b/drivers/trusty/Makefile @@ -12,3 +12,4 @@ obj-$(CONFIG_TRUSTY) += trusty-mem.o obj-$(CONFIG_TRUSTY_VIRTIO) += trusty-virtio.o obj-$(CONFIG_TRUSTY_VIRTIO_IPC) += trusty-ipc.o obj-$(CONFIG_TRUSTY) += trusty-wall.o +obj-$(CONFIG_TRUSTY_BACKUP_TIMER) += trusty-timer.o diff --git a/drivers/trusty/trusty-timer.c b/drivers/trusty/trusty-timer.c new file mode 100644 index 000000000000..0998e027984b --- /dev/null +++ b/drivers/trusty/trusty-timer.c @@ -0,0 +1,166 @@ +/* + * Copyright (C) 2017 Intel, Inc. + * Copyright (C) 2016 Google, Inc. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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. + * + */ +#include +#include +#include +#include +#include +#include +#include +#include + +struct trusty_timer { + struct sec_timer_state *sts; + struct hrtimer tm; +}; + +struct trusty_timer_dev_state { + struct device *dev; + struct device *smwall_dev; + struct device *trusty_dev; + struct notifier_block call_notifier; + struct trusty_timer timer; +}; + +static enum hrtimer_restart trusty_timer_cb(struct hrtimer *tm) +{ + struct trusty_timer_dev_state *s; + + s = container_of(tm, struct trusty_timer_dev_state, timer.tm); + + set_pending_intr_to_lk(0x31); + trusty_enqueue_nop(s->trusty_dev, NULL); + + return HRTIMER_NORESTART; +} + +static int trusty_timer_call_notify(struct notifier_block *nb, + unsigned long action, void *data) +{ + struct trusty_timer *tt; + struct sec_timer_state *sts; + struct trusty_timer_dev_state *s; + + if (action != TRUSTY_CALL_RETURNED) + return NOTIFY_DONE; + + s = container_of(nb, struct trusty_timer_dev_state, call_notifier); + + /* this notifier is executed in non-preemptible context */ + tt = &s->timer; + sts = tt->sts; + + if (sts->tv_ns > sts->cv_ns) { + hrtimer_cancel(&tt->tm); + } else if (sts->cv_ns > sts->tv_ns) { + /* need to set/reset timer */ + hrtimer_start(&tt->tm, ns_to_ktime(sts->cv_ns - sts->tv_ns), + HRTIMER_MODE_REL_PINNED); + } + + sts->cv_ns = 0ULL; + sts->tv_ns = 0ULL; + + return NOTIFY_OK; +} + +static int trusty_timer_probe(struct platform_device *pdev) +{ + int ret; + unsigned int cpu; + struct trusty_timer_dev_state *s; + struct trusty_timer *tt; + + dev_dbg(&pdev->dev, "%s\n", __func__); + + if (!trusty_wall_base(pdev->dev.parent)) { + dev_notice(&pdev->dev, "smwall: is not setup by parent\n"); + return -ENODEV; + } + + s = kzalloc(sizeof(*s), GFP_KERNEL); + if (!s) + return -ENOMEM; + + s->dev = &pdev->dev; + s->smwall_dev = s->dev->parent; + s->trusty_dev = s->smwall_dev->parent; + platform_set_drvdata(pdev, s); + + tt = &s->timer; + + hrtimer_init(&tt->tm, CLOCK_BOOTTIME, HRTIMER_MODE_REL_PINNED); + tt->tm.function = trusty_timer_cb; + tt->sts = + trusty_wall_per_cpu_item_ptr(s->smwall_dev, 0, + SM_WALL_PER_CPU_SEC_TIMER_ID, + sizeof(*tt->sts)); + WARN_ON(!tt->sts); + + + /* register notifier */ + s->call_notifier.notifier_call = trusty_timer_call_notify; + ret = trusty_call_notifier_register(s->trusty_dev, &s->call_notifier); + if (ret < 0) { + dev_err(&pdev->dev, "Failed to register call notifier\n"); + kfree(s); + return ret; + } + + dev_info(s->dev, "initialized\n"); + + return 0; + +} + +static int trusty_timer_remove(struct platform_device *pdev) +{ + unsigned int cpu; + struct trusty_timer_dev_state *s = platform_get_drvdata(pdev); + struct trusty_timer *tt; + + + dev_dbg(&pdev->dev, "%s\n", __func__); + + /* unregister notifier */ + trusty_call_notifier_unregister(s->trusty_dev, &s->call_notifier); + + tt = &s->timer; + hrtimer_cancel(&tt->tm); + + /* free state */ + kfree(s); + return 0; +} + +static const struct of_device_id trusty_test_of_match[] = { + { .compatible = "android,trusty-timer-v1", }, + {}, +}; + +static struct platform_driver trusty_timer_driver = { + .probe = trusty_timer_probe, + .remove = trusty_timer_remove, + .driver = { + .name = "trusty-timer", + .owner = THIS_MODULE, + .of_match_table = trusty_test_of_match, + }, +}; + +module_platform_driver(trusty_timer_driver); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("Trusty timer driver"); diff --git a/drivers/trusty/trusty.c b/drivers/trusty/trusty.c index 0b3e75823be1..1568849e4501 100644 --- a/drivers/trusty/trusty.c +++ b/drivers/trusty/trusty.c @@ -617,6 +617,11 @@ void trusty_dev_release(struct device *dev) return; } +static struct device_node trusty_timer_node = { + .name = "trusty-timer", + .sibling = NULL, +}; + static struct device_node trusty_wall_node = { .name = "trusty-wall", .sibling = NULL, @@ -696,12 +701,24 @@ static struct platform_device trusty_platform_dev_wall = { }, }; +static struct platform_device trusty_platform_dev_timer = { + .name = "trusty-timer", + .id = -1, + .num_resources = 0, + .dev = { + .release = trusty_dev_release, + .parent = &trusty_platform_dev_wall.dev, + .of_node = &trusty_timer_node, + }, +}; + static struct platform_device *trusty_devices[] __initdata = { &trusty_platform_dev, &trusty_platform_dev_log, &trusty_platform_dev_virtio, &trusty_platform_dev_irq, - &trusty_platform_dev_wall + &trusty_platform_dev_wall, + &trusty_platform_dev_timer }; static int __init trusty_driver_init(void) { diff --git a/include/linux/trusty/smwall.h b/include/linux/trusty/smwall.h index 370d8b32f26a..66368de8c137 100644 --- a/include/linux/trusty/smwall.h +++ b/include/linux/trusty/smwall.h @@ -87,4 +87,18 @@ struct trusty_wall_dev_state { size_t sz; }; +/* ID's of well known wall objects */ +#define SM_WALL_PER_CPU_SEC_TIMER_ID 1 + +/** + * struct sec_timer_state - structure to hold secute timer state + * @tv_ns: If non-zero this field contains snapshot of timers + * current time (ns). + * @cv_ns: next timer event configured (ns) + */ +struct sec_timer_state { + u64 tv_ns; + u64 cv_ns; +}; + #endif /* __LINUX_TRUSTY_SMWALL_H */ From 2e9061ee73823394835096c3f6f9d055cf30a063 Mon Sep 17 00:00:00 2001 From: yingbinx Date: Thu, 10 Aug 2017 06:57:08 +0000 Subject: [PATCH 1014/1103] trusty kernel driver code refine Merge the code refine change form kerenl 4.4 code. Use define CONFIG_X86 to sperate IA code and ARM code. Use NR_CPUS to replace the hardcode value. Inprove the debug log. Change-Id: I11075d594e6b119913cc38d79fe5fa3032ca254e Tracked-On: Signed-off-by: yingbinx Signed-off-by: Sheng, W --- drivers/trusty/trusty-irq.c | 4 +- drivers/trusty/trusty-log.c | 6 ++- drivers/trusty/trusty-mem.c | 71 ++++++++++++++++++++++++++++++++++- drivers/trusty/trusty.c | 4 +- include/linux/trusty/trusty.h | 2 + 5 files changed, 80 insertions(+), 7 deletions(-) mode change 100644 => 100755 drivers/trusty/trusty-log.c mode change 100644 => 100755 drivers/trusty/trusty-mem.c mode change 100644 => 100755 drivers/trusty/trusty.c diff --git a/drivers/trusty/trusty-irq.c b/drivers/trusty/trusty-irq.c index eda0bff48c40..b576729ec868 100644 --- a/drivers/trusty/trusty-irq.c +++ b/drivers/trusty/trusty-irq.c @@ -405,7 +405,7 @@ static int trusty_irq_init_per_cpu_irq(struct trusty_irq_state *is, int tirq) struct trusty_irq *trusty_irq; struct trusty_irq_irqset *irqset; - if (cpu >= 32) + if (cpu >= NR_CPUS) return -EINVAL; trusty_irq = per_cpu_ptr(trusty_irq_handler_data, cpu); irqset = per_cpu_ptr(is->percpu_irqs, cpu); @@ -430,7 +430,7 @@ static int trusty_irq_init_per_cpu_irq(struct trusty_irq_state *is, int tirq) for_each_possible_cpu(cpu) { struct trusty_irq *trusty_irq; - if (cpu >= 32) + if (cpu >= NR_CPUS) return -EINVAL; trusty_irq = per_cpu_ptr(trusty_irq_handler_data, cpu); hlist_del(&trusty_irq->node); diff --git a/drivers/trusty/trusty-log.c b/drivers/trusty/trusty-log.c old mode 100644 new mode 100755 index c5a85ccaf222..b58715cc2ef3 --- a/drivers/trusty/trusty-log.c +++ b/drivers/trusty/trusty-log.c @@ -156,10 +156,12 @@ static void trusty_vmm_dump_header(struct deadloop_dump *dump) return; header = &(dump->header); + pr_info("-----------VMM PANIC HEADER-----------\n"); pr_info("VMM version = %s\n", header->vmm_version); pr_info("Signature = %s\n", header->signature); pr_info("Error_info = %s\n", header->error_info); pr_info("Cpuid = %d\n", header->cpuid); + pr_info("-----------END OF VMM PANIC HEADER-----------\n"); } static void trusty_vmm_dump_data(struct deadloop_dump *dump) @@ -172,6 +174,7 @@ static void trusty_vmm_dump_data(struct deadloop_dump *dump) dump_data = &(dump->data); + pr_info("-----------VMM PANIC DATA INFO-----------\n"); pstr = (char *)dump_data->data; for (p = pstr; p < ((char *)dump_data->data + dump_data->length); p++) { if (*p == '\r') { @@ -187,12 +190,13 @@ static void trusty_vmm_dump_data(struct deadloop_dump *dump) *p = 0x00; pr_info("%s\n", pstr); } + pr_info("-----------END OF VMM PANIC DATA INFO-----------\n"); } static int trusty_vmm_panic_notify(struct notifier_block *nb, unsigned long action, void *data) { - struct deadloop_dump *dump_info; + struct deadloop_dump *dump_info = NULL; if (g_vmm_debug_buf) { dump_info = (struct deadloop_dump *)g_vmm_debug_buf; diff --git a/drivers/trusty/trusty-mem.c b/drivers/trusty/trusty-mem.c old mode 100644 new mode 100755 index 1317ec734315..fc299e348581 --- a/drivers/trusty/trusty-mem.c +++ b/drivers/trusty/trusty-mem.c @@ -26,7 +26,58 @@ static int get_mem_attr(struct page *page, pgprot_t pgprot) { -#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 1, 0) +#if defined(CONFIG_ARM64) + uint64_t mair; + uint attr_index = (pgprot_val(pgprot) & PTE_ATTRINDX_MASK) >> 2; + + asm ("mrs %0, mair_el1\n" : "=&r" (mair)); + return (mair >> (attr_index * 8)) & 0xff; + +#elif defined(CONFIG_ARM_LPAE) + uint32_t mair; + uint attr_index = ((pgprot_val(pgprot) & L_PTE_MT_MASK) >> 2); + + if (attr_index >= 4) { + attr_index -= 4; + asm volatile("mrc p15, 0, %0, c10, c2, 1\n" : "=&r" (mair)); + } else { + asm volatile("mrc p15, 0, %0, c10, c2, 0\n" : "=&r" (mair)); + } + return (mair >> (attr_index * 8)) & 0xff; + +#elif defined(CONFIG_ARM) + /* check memory type */ + switch (pgprot_val(pgprot) & L_PTE_MT_MASK) { + case L_PTE_MT_WRITEALLOC: + /* Normal: write back write allocate */ + return 0xFF; + + case L_PTE_MT_BUFFERABLE: + /* Normal: non-cacheble */ + return 0x44; + + case L_PTE_MT_WRITEBACK: + /* Normal: writeback, read allocate */ + return 0xEE; + + case L_PTE_MT_WRITETHROUGH: + /* Normal: write through */ + return 0xAA; + + case L_PTE_MT_UNCACHED: + /* strongly ordered */ + return 0x00; + + case L_PTE_MT_DEV_SHARED: + case L_PTE_MT_DEV_NONSHARED: + /* device */ + return 0x04; + + default: + return -EINVAL; + } +#elif defined(CONFIG_X86) + #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 1, 0) /* The porting to CHT kernel (3.14.55) is in the #else clause. ** For BXT kernel (4.1.0), the function get_page_memtype() is static. ** @@ -42,7 +93,7 @@ static int get_mem_attr(struct page *page, pgprot_t pgprot) ** with SMP, which only allow UNCACHED. */ return NS_MAIR_NORMAL_UNCACHED; -#else + #else unsigned long type; int ret_mem_attr = 0; @@ -73,6 +124,9 @@ static int get_mem_attr(struct page *page, pgprot_t pgprot) ret_mem_attr = -EINVAL; } return ret_mem_attr; + #endif +#else + return 0; #endif } @@ -92,10 +146,23 @@ int trusty_encode_page_info(struct ns_mem_page_info *inf, mem_attr = get_mem_attr(page, pgprot); if (mem_attr < 0) return mem_attr; + + /* add other attributes */ +#if defined(CONFIG_ARM64) || defined(CONFIG_ARM_LPAE) + pte |= pgprot_val(pgprot); +#elif defined(CONFIG_ARM) + if (pgprot_val(pgprot) & L_PTE_USER) + pte |= (1 << 6); + if (pgprot_val(pgprot) & L_PTE_RDONLY) + pte |= (1 << 7); + if (pgprot_val(pgprot) & L_PTE_SHARED) + pte |= (3 << 8); /* inner sharable */ +#elif defined(CONFIG_X86) if (pgprot_val(pgprot) & _PAGE_USER) pte |= (1 << 6); if (!(pgprot_val(pgprot) & _PAGE_RW)) pte |= (1 << 7); +#endif inf->attr = (pte & 0x0000FFFFFFFFFFFFull) | ((uint64_t)mem_attr << 48); return 0; diff --git a/drivers/trusty/trusty.c b/drivers/trusty/trusty.c old mode 100644 new mode 100755 index 1568849e4501..d4eeb40e2b60 --- a/drivers/trusty/trusty.c +++ b/drivers/trusty/trusty.c @@ -209,7 +209,7 @@ static long trusty_std_call32_work(void *args) BUG_ON(!args); - work_args = args; + work_args = (struct trusty_std_call32_args *)args; dev = work_args->dev; s = platform_get_drvdata(to_platform_device(dev)); @@ -332,7 +332,7 @@ static void trusty_init_version(struct trusty_state *s, struct device *dev) } s->version_str[i] = '\0'; - dev_info(dev, "trusty version: Built: %s\n", s->version_str); + dev_info(dev, "trusty version: %s\n", s->version_str); ret = device_create_file(dev, &dev_attr_trusty_version); if (ret) diff --git a/include/linux/trusty/trusty.h b/include/linux/trusty/trusty.h index 029b0986566f..1e9b4559d1b6 100644 --- a/include/linux/trusty/trusty.h +++ b/include/linux/trusty/trusty.h @@ -88,10 +88,12 @@ void trusty_dequeue_nop(struct device *dev, struct trusty_nop *nop); #define TRUSTY_VMCALL_PENDING_INTR 0x74727505 static inline void set_pending_intr_to_lk(uint8_t vector) { +#ifdef CONFIG_X86 __asm__ __volatile__( "vmcall" ::"a"(TRUSTY_VMCALL_PENDING_INTR), "b"(vector) ); +#endif } void trusty_update_wall_info(struct device *dev, void *va, size_t sz); From 9dc18b60992cdc0df7d630bf1585bed6fd293626 Mon Sep 17 00:00:00 2001 From: weideng Date: Fri, 21 Apr 2017 00:52:03 +0000 Subject: [PATCH 1015/1103] Change Trusty Kconfig to build for X86 Arch only Currently Trusty only works on x86, so the module should never build for other archs except x86. Add this patch to add 'depends' part on drivers/trusty/Kconfig to disable them. Change-Id: Ic18f351696a9c1c31d57621a4af3e8993cc73de5 Signed-off-by: weideng --- drivers/trusty/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/trusty/Kconfig b/drivers/trusty/Kconfig index 7b58db5e9a21..7d26922ed84c 100644 --- a/drivers/trusty/Kconfig +++ b/drivers/trusty/Kconfig @@ -6,6 +6,7 @@ menu "Trusty" config TRUSTY tristate "Trusty" + depends on X86 default n config TRUSTY_FIQ From fa25a6430a9ffd9fa73aecd5000b056a7efb4cb3 Mon Sep 17 00:00:00 2001 From: "Zhang, Qi" Date: Mon, 6 Nov 2017 12:35:31 +0800 Subject: [PATCH 1016/1103] trusty: Add null check pointer before deference Add null check before deference pointer. Change-Id: Icc9d61e17e3ecadfa1bd7fc252cf5e3d7aabb636 Signed-off-by: Zhang, Qi --- drivers/trusty/trusty-irq.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/trusty/trusty-irq.c b/drivers/trusty/trusty-irq.c index b576729ec868..868a31c01f19 100644 --- a/drivers/trusty/trusty-irq.c +++ b/drivers/trusty/trusty-irq.c @@ -199,6 +199,9 @@ static int trusty_irq_cpu_up(unsigned int cpu, struct hlist_node *node) unsigned long irq_flags; struct trusty_irq_state *is = hlist_entry_safe(node, struct trusty_irq_state, node); + if(is == NULL) + return 0; + dev_dbg(is->dev, "%s: cpu %d\n", __func__, smp_processor_id()); local_irq_save(irq_flags); @@ -212,6 +215,9 @@ static int trusty_irq_cpu_down(unsigned int cpu, struct hlist_node *node) unsigned long irq_flags; struct trusty_irq_state *is = hlist_entry_safe(node, struct trusty_irq_state, node); + if(is == NULL) + return 0; + dev_dbg(is->dev, "%s: cpu %d\n", __func__, smp_processor_id()); local_irq_save(irq_flags); From 8d2a19e72db1cc2e225691acce79ba788f459728 Mon Sep 17 00:00:00 2001 From: Zhou Furong Date: Tue, 21 Nov 2017 14:13:27 +0800 Subject: [PATCH 1017/1103] trusty: Check if eVmm is available before init driver eVmm not available in recovery mode, there is a kernel panic if trusty driver initialize without check it. Change-Id: I21f788ab7186cddbc0b0d1a10f7896b5523d257a Tracked-On: --- drivers/trusty/trusty-timer.c | 8 ++++++-- drivers/trusty/trusty-wall.c | 6 ++++++ 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/drivers/trusty/trusty-timer.c b/drivers/trusty/trusty-timer.c index 0998e027984b..e88dc5f4cdf8 100644 --- a/drivers/trusty/trusty-timer.c +++ b/drivers/trusty/trusty-timer.c @@ -79,10 +79,15 @@ static int trusty_timer_call_notify(struct notifier_block *nb, static int trusty_timer_probe(struct platform_device *pdev) { int ret; - unsigned int cpu; struct trusty_timer_dev_state *s; struct trusty_timer *tt; + ret = trusty_check_cpuid(NULL); + if (ret < 0) { + dev_err(&pdev->dev, "CPUID Error: Cannot find eVmm in trusty driver initialization!"); + return -EINVAL; + } + dev_dbg(&pdev->dev, "%s\n", __func__); if (!trusty_wall_base(pdev->dev.parent)) { @@ -127,7 +132,6 @@ static int trusty_timer_probe(struct platform_device *pdev) static int trusty_timer_remove(struct platform_device *pdev) { - unsigned int cpu; struct trusty_timer_dev_state *s = platform_get_drvdata(pdev); struct trusty_timer *tt; diff --git a/drivers/trusty/trusty-wall.c b/drivers/trusty/trusty-wall.c index 3c33d724b3fa..64368480c309 100644 --- a/drivers/trusty/trusty-wall.c +++ b/drivers/trusty/trusty-wall.c @@ -147,6 +147,12 @@ static int trusty_wall_probe(struct platform_device *pdev) int ret; struct trusty_wall_dev_state *s; + ret = trusty_check_cpuid(NULL); + if (ret < 0) { + dev_err(&pdev->dev, "CPUID Error: Cannot find eVmm in trusty driver initialization!"); + return -EINVAL; + } + dev_dbg(&pdev->dev, "%s\n", __func__); s = kzalloc(sizeof(*s), GFP_KERNEL); From e816ef570998f3f166cd85dd6fd3604558f9313a Mon Sep 17 00:00:00 2001 From: "Zhang, Qi" Date: Tue, 12 Dec 2017 15:16:21 +0800 Subject: [PATCH 1018/1103] trusty: Update Trusty timer solution 1. Add new customized SMC calls 2. Move send pending interrupt implementation to trusty-irq.c 3. Invokes new added standard SMC call to inject timer interrupt to secure side Change-Id: I6c9a94c8ff50f42b58abd2e2b2dd5efd26c126e2 Signed-off-by: Zhong,Fangjian --- drivers/trusty/trusty-irq.c | 9 +++++++++ drivers/trusty/trusty-timer.c | 38 +++++++++++++++++++++++++++++++++-- include/linux/trusty/trusty.h | 11 ---------- 3 files changed, 45 insertions(+), 13 deletions(-) diff --git a/drivers/trusty/trusty-irq.c b/drivers/trusty/trusty-irq.c index 868a31c01f19..04df531bf9d0 100644 --- a/drivers/trusty/trusty-irq.c +++ b/drivers/trusty/trusty-irq.c @@ -59,6 +59,15 @@ struct trusty_irq_state { static enum cpuhp_state trusty_irq_online; +#define TRUSTY_VMCALL_PENDING_INTR 0x74727505 +static inline void set_pending_intr_to_lk(uint8_t vector) +{ + __asm__ __volatile__( + "vmcall" + ::"a"(TRUSTY_VMCALL_PENDING_INTR), "b"(vector) + ); +} + static void trusty_irq_enable_pending_irqs(struct trusty_irq_state *is, struct trusty_irq_irqset *irqset, bool percpu) diff --git a/drivers/trusty/trusty-timer.c b/drivers/trusty/trusty-timer.c index e88dc5f4cdf8..43e43265c3c6 100644 --- a/drivers/trusty/trusty-timer.c +++ b/drivers/trusty/trusty-timer.c @@ -24,6 +24,7 @@ struct trusty_timer { struct sec_timer_state *sts; struct hrtimer tm; + struct work_struct work; }; struct trusty_timer_dev_state { @@ -32,16 +33,33 @@ struct trusty_timer_dev_state { struct device *trusty_dev; struct notifier_block call_notifier; struct trusty_timer timer; + struct workqueue_struct *workqueue; }; +/* Max entity defined as SMC_NUM_ENTITIES(64) */ +#define SMC_ENTITY_SMC_X86 63 /* Used for customized SMC calls */ + +#define SMC_SC_LK_TIMER SMC_STDCALL_NR(SMC_ENTITY_SMC_X86, 0) + +static void timer_work_func(struct work_struct *work) +{ + int ret; + struct trusty_timer_dev_state *s; + + s = container_of(work, struct trusty_timer_dev_state, timer.work); + + ret = trusty_std_call32(s->trusty_dev, SMC_SC_LK_TIMER, 0, 0, 0); + if (ret != 0) + dev_err(s->dev, "%s failed %d\n", __func__, ret); +} + static enum hrtimer_restart trusty_timer_cb(struct hrtimer *tm) { struct trusty_timer_dev_state *s; s = container_of(tm, struct trusty_timer_dev_state, timer.tm); - set_pending_intr_to_lk(0x31); - trusty_enqueue_nop(s->trusty_dev, NULL); + queue_work(s->workqueue, &s->timer.work); return HRTIMER_NORESTART; } @@ -114,6 +132,12 @@ static int trusty_timer_probe(struct platform_device *pdev) sizeof(*tt->sts)); WARN_ON(!tt->sts); + s->workqueue = alloc_workqueue("trusty-timer-wq", WQ_CPU_INTENSIVE, 0); + if (!s->workqueue) { + ret = -ENODEV; + dev_err(&pdev->dev, "Failed to allocate work queue\n"); + goto err_allocate_work_queue; + } /* register notifier */ s->call_notifier.notifier_call = trusty_timer_call_notify; @@ -124,10 +148,18 @@ static int trusty_timer_probe(struct platform_device *pdev) return ret; } + INIT_WORK(&s->timer.work, timer_work_func); + dev_info(s->dev, "initialized\n"); return 0; +err_register_call_notifier: + destroy_workqueue(s->workqueue); +err_allocate_work_queue: + kfree(s); + return ret; + } static int trusty_timer_remove(struct platform_device *pdev) @@ -144,6 +176,8 @@ static int trusty_timer_remove(struct platform_device *pdev) tt = &s->timer; hrtimer_cancel(&tt->tm); + flush_work(&tt->work); + destroy_workqueue(s->workqueue); /* free state */ kfree(s); return 0; diff --git a/include/linux/trusty/trusty.h b/include/linux/trusty/trusty.h index 1e9b4559d1b6..3189c7ec967c 100644 --- a/include/linux/trusty/trusty.h +++ b/include/linux/trusty/trusty.h @@ -85,17 +85,6 @@ static inline void trusty_nop_init(struct trusty_nop *nop, void trusty_enqueue_nop(struct device *dev, struct trusty_nop *nop); void trusty_dequeue_nop(struct device *dev, struct trusty_nop *nop); -#define TRUSTY_VMCALL_PENDING_INTR 0x74727505 -static inline void set_pending_intr_to_lk(uint8_t vector) -{ -#ifdef CONFIG_X86 - __asm__ __volatile__( - "vmcall" - ::"a"(TRUSTY_VMCALL_PENDING_INTR), "b"(vector) - ); -#endif -} - void trusty_update_wall_info(struct device *dev, void *va, size_t sz); void *trusty_wall_base(struct device *dev); void *trusty_wall_per_cpu_item_ptr(struct device *dev, unsigned int cpu, From e3f4cfa623fbde7e3cceec89240fc02a69b1337b Mon Sep 17 00:00:00 2001 From: "Qi, Yadong" Date: Fri, 2 Feb 2018 13:12:40 +0800 Subject: [PATCH 1019/1103] trusty: detect vmm when load trusty driver Use hypervisor_cpuid_base() to detect VMM which support Trusty. Currently, there are 2 hypervisors support trusty: CWP and EVMM. Use different hypercall to implement SMC for EVMM and CWP. Change-Id: I45a9c69862c785aba3d2911ca439b5e3d8cf0cf6 Signed-off-by: Qi, Yadong Tracked-On: --- drivers/trusty/trusty-ipc.c | 4 +-- drivers/trusty/trusty-irq.c | 4 +-- drivers/trusty/trusty-log.c | 10 +++---- drivers/trusty/trusty-timer.c | 4 +-- drivers/trusty/trusty-virtio.c | 4 +-- drivers/trusty/trusty-wall.c | 4 +-- drivers/trusty/trusty.c | 52 +++++++++++++++++++++++++++------- include/linux/trusty/trusty.h | 33 +++++++++++---------- 8 files changed, 74 insertions(+), 41 deletions(-) diff --git a/drivers/trusty/trusty-ipc.c b/drivers/trusty/trusty-ipc.c index 93003b45eb32..a2bc3fcba29a 100644 --- a/drivers/trusty/trusty-ipc.c +++ b/drivers/trusty/trusty-ipc.c @@ -1525,9 +1525,9 @@ static int tipc_virtio_probe(struct virtio_device *vdev) vq_callback_t *vq_cbs[] = {_rxvq_cb, _txvq_cb}; const char *vq_names[] = { "rx", "tx" }; - err = trusty_check_cpuid(NULL); + err = trusty_detect_vmm(); if (err < 0) { - dev_err(&vdev->dev, "CPUID Error: Cannot find eVmm in trusty driver initialization!"); + dev_err(&vdev->dev, "Cannot detect VMM which supports trusty!"); return -EINVAL; } diff --git a/drivers/trusty/trusty-irq.c b/drivers/trusty/trusty-irq.c index 04df531bf9d0..af2af6ee37ba 100644 --- a/drivers/trusty/trusty-irq.c +++ b/drivers/trusty/trusty-irq.c @@ -539,9 +539,9 @@ static int trusty_irq_probe(struct platform_device *pdev) unsigned long irq_flags; struct trusty_irq_state *is; - ret = trusty_check_cpuid(NULL); + ret = trusty_detect_vmm(); if (ret < 0) { - dev_err(&pdev->dev, "CPUID Error: Cannot find eVmm in trusty driver initialization!"); + dev_err(&pdev->dev, "Cannot detect VMM which supports trusty!"); return -EINVAL; } diff --git a/drivers/trusty/trusty-log.c b/drivers/trusty/trusty-log.c index b58715cc2ef3..d2446a1f34c9 100755 --- a/drivers/trusty/trusty-log.c +++ b/drivers/trusty/trusty-log.c @@ -259,13 +259,13 @@ static int trusty_log_probe(struct platform_device *pdev) { struct trusty_log_state *s; int result; - u32 vmm_signature; + int vmm_id; phys_addr_t pa; struct deadloop_dump *dump; - result = trusty_check_cpuid(&vmm_signature); - if (result < 0) { - dev_err(&pdev->dev, "CPUID Error: Cannot find eVmm in trusty driver initialization!"); + vmm_id = trusty_detect_vmm(); + if (vmm_id < 0) { + dev_err(&pdev->dev, "Cannot detect VMM which supports trusty!"); return -EINVAL; } @@ -321,7 +321,7 @@ static int trusty_log_probe(struct platform_device *pdev) goto error_panic_notifier; } - if(vmm_signature == EVMM_SIGNATURE_VMM) { + if(vmm_id == VMM_ID_EVMM) { /* allocate debug buffer for vmm panic dump */ g_vmm_debug_buf = __get_free_pages(GFP_KERNEL | __GFP_ZERO, 2); if (!g_vmm_debug_buf) { diff --git a/drivers/trusty/trusty-timer.c b/drivers/trusty/trusty-timer.c index 43e43265c3c6..5d4466d4e157 100644 --- a/drivers/trusty/trusty-timer.c +++ b/drivers/trusty/trusty-timer.c @@ -100,9 +100,9 @@ static int trusty_timer_probe(struct platform_device *pdev) struct trusty_timer_dev_state *s; struct trusty_timer *tt; - ret = trusty_check_cpuid(NULL); + ret = trusty_detect_vmm(); if (ret < 0) { - dev_err(&pdev->dev, "CPUID Error: Cannot find eVmm in trusty driver initialization!"); + dev_err(&pdev->dev, "Cannot detect VMM which supports trusty!"); return -EINVAL; } diff --git a/drivers/trusty/trusty-virtio.c b/drivers/trusty/trusty-virtio.c index b2418d7da5e1..743a4789772f 100644 --- a/drivers/trusty/trusty-virtio.c +++ b/drivers/trusty/trusty-virtio.c @@ -641,9 +641,9 @@ static int trusty_virtio_probe(struct platform_device *pdev) int ret; struct trusty_ctx *tctx; - ret = trusty_check_cpuid(NULL); + ret = trusty_detect_vmm(); if (ret < 0) { - dev_err(&pdev->dev, "CPUID Error: Cannot find eVmm in trusty driver initialization!"); + dev_err(&pdev->dev, "Cannot detect VMM which supports trusty!"); return -EINVAL; } diff --git a/drivers/trusty/trusty-wall.c b/drivers/trusty/trusty-wall.c index 64368480c309..2345f56a6405 100644 --- a/drivers/trusty/trusty-wall.c +++ b/drivers/trusty/trusty-wall.c @@ -147,9 +147,9 @@ static int trusty_wall_probe(struct platform_device *pdev) int ret; struct trusty_wall_dev_state *s; - ret = trusty_check_cpuid(NULL); + ret = trusty_detect_vmm(); if (ret < 0) { - dev_err(&pdev->dev, "CPUID Error: Cannot find eVmm in trusty driver initialization!"); + dev_err(&pdev->dev, "Cannot detect VMM which supports trusty!"); return -EINVAL; } diff --git a/drivers/trusty/trusty.c b/drivers/trusty/trusty.c index d4eeb40e2b60..98c866487a3e 100755 --- a/drivers/trusty/trusty.c +++ b/drivers/trusty/trusty.c @@ -26,7 +26,8 @@ #include #include -#define TRUSTY_VMCALL_SMC 0x74727500 +#define EVMM_SMC_HC_ID 0x74727500 +#define CWP_SMC_HC_ID 0x80000071 struct trusty_state; @@ -53,13 +54,28 @@ struct trusty_smc_interface { ulong args[5]; }; -static inline ulong smc(ulong r0, ulong r1, ulong r2, ulong r3) +static ulong (*smc)(ulong, ulong, ulong, ulong); + +#define asm_smc_vmcall(smc_id, rdi, rsi, rdx, rbx) \ +do { \ + __asm__ __volatile__( \ + "vmcall; \n" \ + : "=D"(rdi) \ + : "r"(smc_id), "D"(rdi), "S"(rsi), "d"(rdx), "b"(rbx) \ + ); \ +} while (0) + +static inline ulong smc_evmm(ulong r0, ulong r1, ulong r2, ulong r3) { - __asm__ __volatile__( - "vmcall; \n" - : "=D"(r0) - : "a"(TRUSTY_VMCALL_SMC), "D"(r0), "S"(r1), "d"(r2), "b"(r3) - ); + register unsigned long smc_id asm("rax") = EVMM_SMC_HC_ID; + asm_smc_vmcall(smc_id, r0, r1, r2, r3); + return r0; +} + +static inline ulong smc_cwp(ulong r0, ulong r1, ulong r2, ulong r3) +{ + register unsigned long smc_id asm("r8") = CWP_SMC_HC_ID; + asm_smc_vmcall(smc_id, r0, r1, r2, r3); return r0; } @@ -443,6 +459,19 @@ static void nop_work_func(struct work_struct *work) dev_dbg(s->dev, "%s: done\n", __func__); } +static void trusty_init_smc(int vmm_id) +{ + if (vmm_id == VMM_ID_EVMM) { + smc = smc_evmm; + } else if (vmm_id == VMM_ID_CWP) { + smc = smc_cwp; + } else { + pr_err("%s: No smc supports VMM[%d](sig:%s)!", + __func__, vmm_id, vmm_signature[vmm_id]); + BUG(); + } +} + void trusty_enqueue_nop(struct device *dev, struct trusty_nop *nop) { unsigned long flags; @@ -479,8 +508,6 @@ void trusty_dequeue_nop(struct device *dev, struct trusty_nop *nop) } EXPORT_SYMBOL(trusty_dequeue_nop); - - static int trusty_probe(struct platform_device *pdev) { int ret; @@ -489,11 +516,14 @@ static int trusty_probe(struct platform_device *pdev) struct trusty_state *s; struct device_node *node = pdev->dev.of_node; - ret = trusty_check_cpuid(NULL); + ret = trusty_detect_vmm(); if (ret < 0) { - dev_err(&pdev->dev, "CPUID Error: Cannot find eVmm in trusty driver initialization!"); + dev_err(&pdev->dev, "Cannot detect VMM which supports trusty!"); return -EINVAL; } + dev_dbg(&pdev->dev, "Detected VMM: sig=%s\n", vmm_signature[ret]); + + trusty_init_smc(ret); if (!node) { dev_err(&pdev->dev, "of_node required\n"); diff --git a/include/linux/trusty/trusty.h b/include/linux/trusty/trusty.h index 3189c7ec967c..48e1ea716889 100644 --- a/include/linux/trusty/trusty.h +++ b/include/linux/trusty/trusty.h @@ -18,6 +18,7 @@ #include #include #include +#include #if IS_ENABLED(CONFIG_TRUSTY) @@ -90,25 +91,27 @@ void *trusty_wall_base(struct device *dev); void *trusty_wall_per_cpu_item_ptr(struct device *dev, unsigned int cpu, u32 item_id, size_t exp_sz); -/* CPUID leaf 0x3 is used because eVMM will trap this leaf.*/ -#define EVMM_SIGNATURE_CORP 0x43544E49 /* "INTC", edx */ -#define EVMM_SIGNATURE_VMM 0x4D4D5645 /* "EVMM", ecx */ - -static inline int trusty_check_cpuid(u32 *vmm_signature) -{ - u32 eax, ebx, ecx, edx; +enum { + VMM_ID_EVMM = 0, + VMM_ID_CWP, + VMM_SUPPORTED_NUM +}; - cpuid(3, &eax, &ebx, &ecx, &edx); - if ((ecx != EVMM_SIGNATURE_VMM) || - (edx != EVMM_SIGNATURE_CORP)) { - return -EINVAL; - } +static const char *vmm_signature[] = { + [VMM_ID_EVMM] = "EVMMEVMMEVMM", + [VMM_ID_CWP] = "CWPCWPCWP\0\0" +}; - if(vmm_signature) { - *vmm_signature = ecx; +/* Detect VMM and return vmm_id */ +static inline int trusty_detect_vmm(void) +{ + int i; + for (i = 0; i < VMM_SUPPORTED_NUM; i++) { + if (hypervisor_cpuid_base(vmm_signature[i], 0)) + return i; } - return 0; + return -EINVAL; } /* High 32 bits of unsigned 64-bit integer*/ From eaa51429bf1ca172abac019bc34fda747ced1757 Mon Sep 17 00:00:00 2001 From: Zhou Furong Date: Wed, 14 Feb 2018 15:14:20 +0800 Subject: [PATCH 1020/1103] Remove unused label to depress compile warning As title, remove a unused label to depress compile wrning Change-Id: I8a6daa1d85b9a95ec9a475ef39990e74c84e89e9 --- drivers/trusty/trusty-timer.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/trusty/trusty-timer.c b/drivers/trusty/trusty-timer.c index 5d4466d4e157..18e315c25067 100644 --- a/drivers/trusty/trusty-timer.c +++ b/drivers/trusty/trusty-timer.c @@ -154,7 +154,6 @@ static int trusty_timer_probe(struct platform_device *pdev) return 0; -err_register_call_notifier: destroy_workqueue(s->workqueue); err_allocate_work_queue: kfree(s); From d81b19b6691253b9df580650b8b4617848b732ba Mon Sep 17 00:00:00 2001 From: "Qi, Yadong" Date: Fri, 23 Feb 2018 14:12:07 +0800 Subject: [PATCH 1021/1103] trusty: Update dependency of trusty module Trusty is supported only for x86_64 arch. Modify Kconfig to make it depends on x86_64. Change-Id: Ia52a8ba05f2de3d423e070a53e7368901b20ada7 Signed-off-by: Qi, Yadong --- drivers/trusty/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/trusty/Kconfig b/drivers/trusty/Kconfig index 7d26922ed84c..a230dad0434d 100644 --- a/drivers/trusty/Kconfig +++ b/drivers/trusty/Kconfig @@ -6,7 +6,7 @@ menu "Trusty" config TRUSTY tristate "Trusty" - depends on X86 + depends on X86_64 default n config TRUSTY_FIQ From 6aaac6f4e789576eb84dca6c59b3f96a31b1c4dd Mon Sep 17 00:00:00 2001 From: "Qi, Yadong" Date: Mon, 26 Feb 2018 09:48:06 +0800 Subject: [PATCH 1022/1103] trusty: Rename CWP with ACRN The CWP hypervisor has been renamed to ACRN. Change-Id: I23bcff44954110fbc20148fd3266ac48864a3a1f Signed-off-by: Qi, Yadong --- drivers/trusty/trusty.c | 10 +++++----- include/linux/trusty/trusty.h | 4 ++-- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/drivers/trusty/trusty.c b/drivers/trusty/trusty.c index 98c866487a3e..e253ee498ab5 100755 --- a/drivers/trusty/trusty.c +++ b/drivers/trusty/trusty.c @@ -27,7 +27,7 @@ #include #define EVMM_SMC_HC_ID 0x74727500 -#define CWP_SMC_HC_ID 0x80000071 +#define ACRN_SMC_HC_ID 0x80000071 struct trusty_state; @@ -72,9 +72,9 @@ static inline ulong smc_evmm(ulong r0, ulong r1, ulong r2, ulong r3) return r0; } -static inline ulong smc_cwp(ulong r0, ulong r1, ulong r2, ulong r3) +static inline ulong smc_acrn(ulong r0, ulong r1, ulong r2, ulong r3) { - register unsigned long smc_id asm("r8") = CWP_SMC_HC_ID; + register unsigned long smc_id asm("r8") = ACRN_SMC_HC_ID; asm_smc_vmcall(smc_id, r0, r1, r2, r3); return r0; } @@ -463,8 +463,8 @@ static void trusty_init_smc(int vmm_id) { if (vmm_id == VMM_ID_EVMM) { smc = smc_evmm; - } else if (vmm_id == VMM_ID_CWP) { - smc = smc_cwp; + } else if (vmm_id == VMM_ID_ACRN) { + smc = smc_acrn; } else { pr_err("%s: No smc supports VMM[%d](sig:%s)!", __func__, vmm_id, vmm_signature[vmm_id]); diff --git a/include/linux/trusty/trusty.h b/include/linux/trusty/trusty.h index 48e1ea716889..546e6db03498 100644 --- a/include/linux/trusty/trusty.h +++ b/include/linux/trusty/trusty.h @@ -93,13 +93,13 @@ void *trusty_wall_per_cpu_item_ptr(struct device *dev, unsigned int cpu, enum { VMM_ID_EVMM = 0, - VMM_ID_CWP, + VMM_ID_ACRN, VMM_SUPPORTED_NUM }; static const char *vmm_signature[] = { [VMM_ID_EVMM] = "EVMMEVMMEVMM", - [VMM_ID_CWP] = "CWPCWPCWP\0\0" + [VMM_ID_ACRN] = "ACRNACRNACRN" }; /* Detect VMM and return vmm_id */ From 1168adb0d00433e934de84b4370b8126d96d98ae Mon Sep 17 00:00:00 2001 From: "Qi, Yadong" Date: Fri, 16 Mar 2018 15:42:07 +0800 Subject: [PATCH 1023/1103] trusty: add RAX into clobber list of inline asm for ACRN The RAX regiser will be modified when do "vmcall" for ACRN hypervisor. So the RAX register should to be listed in asm clobber list to inform compiler aware of such changes. Change-Id: I298c056c109e974d2a391ba7b3e8dfbb7f25ed4f Signed-off-by: Qi, Yadong --- drivers/trusty/trusty.c | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/drivers/trusty/trusty.c b/drivers/trusty/trusty.c index e253ee498ab5..4d33f269851d 100755 --- a/drivers/trusty/trusty.c +++ b/drivers/trusty/trusty.c @@ -56,26 +56,28 @@ struct trusty_smc_interface { static ulong (*smc)(ulong, ulong, ulong, ulong); -#define asm_smc_vmcall(smc_id, rdi, rsi, rdx, rbx) \ -do { \ - __asm__ __volatile__( \ - "vmcall; \n" \ - : "=D"(rdi) \ - : "r"(smc_id), "D"(rdi), "S"(rsi), "d"(rdx), "b"(rbx) \ - ); \ -} while (0) - static inline ulong smc_evmm(ulong r0, ulong r1, ulong r2, ulong r3) { register unsigned long smc_id asm("rax") = EVMM_SMC_HC_ID; - asm_smc_vmcall(smc_id, r0, r1, r2, r3); + __asm__ __volatile__( + "vmcall; \n" + : "=D"(r0) + : "r"(smc_id), "D"(r0), "S"(r1), "d"(r2), "b"(r3) + ); + return r0; } static inline ulong smc_acrn(ulong r0, ulong r1, ulong r2, ulong r3) { register unsigned long smc_id asm("r8") = ACRN_SMC_HC_ID; - asm_smc_vmcall(smc_id, r0, r1, r2, r3); + __asm__ __volatile__( + "vmcall; \n" + : "=D"(r0) + : "r"(smc_id), "D"(r0), "S"(r1), "d"(r2), "b"(r3) + : "rax" + ); + return r0; } From 796b7b2668ca6e9a2ce7151aebed91f2490cff69 Mon Sep 17 00:00:00 2001 From: "Ding,XinX" Date: Wed, 21 Mar 2018 11:09:27 +0800 Subject: [PATCH 1024/1103] trusty: Update macro SMC_FC_GET_WALL_SIZE from 12 to 20 Keep this macro synced with that of Trusty OS because we rebased trusty OS with Google's and this smc id was increased. Change-Id: I09d68971de6d8f3d099525c21f99fe7ed2fdcb9d Signed-off-by: Ding,XinX --- include/linux/trusty/smcall.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/linux/trusty/smcall.h b/include/linux/trusty/smcall.h index ee5dda2560b6..3ab2f688cb33 100644 --- a/include/linux/trusty/smcall.h +++ b/include/linux/trusty/smcall.h @@ -139,7 +139,7 @@ * SMC_SC_DESTROY_WALL - notifies secure side that previously specifies SM Wall * object should be released usually as part of normal shutdown sequence. */ -#define SMC_FC_GET_WALL_SIZE SMC_FASTCALL_NR(SMC_ENTITY_SECURE_MONITOR, 12) +#define SMC_FC_GET_WALL_SIZE SMC_FASTCALL_NR(SMC_ENTITY_SECURE_MONITOR, 20) #define SMC_SC_SETUP_WALL SMC_STDCALL_NR(SMC_ENTITY_SECURE_MONITOR, 12) #define SMC_SC_DESTROY_WALL SMC_STDCALL_NR(SMC_ENTITY_SECURE_MONITOR, 13) From 78efa123d2284df3037005b93c1cb3f6ba870098 Mon Sep 17 00:00:00 2001 From: "Zhang, Qi" Date: Wed, 7 Mar 2018 15:45:48 +0800 Subject: [PATCH 1025/1103] unify trusty driver Keep One Trusty driver accross different version kernel as we have One Trusty OS Change-Id: Ie81201bb543ffdf6050bfab7560bd275f3a92eb0 Signed-off-by: Zhang, Qi --- drivers/trusty/trusty-ipc.c | 7 +++++++ drivers/trusty/trusty-mem.c | 6 +++--- drivers/trusty/trusty-virtio.c | 14 ++++++++++++++ 3 files changed, 24 insertions(+), 3 deletions(-) mode change 100755 => 100644 drivers/trusty/trusty-mem.c diff --git a/drivers/trusty/trusty-ipc.c b/drivers/trusty/trusty-ipc.c index a2bc3fcba29a..7df0972ddd05 100644 --- a/drivers/trusty/trusty-ipc.c +++ b/drivers/trusty/trusty-ipc.c @@ -21,7 +21,10 @@ #include #include #include +#include +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 14, 0) #include +#endif #include #include #include @@ -1558,7 +1561,11 @@ static int tipc_virtio_probe(struct virtio_device *vdev) vds->cdev_name[sizeof(vds->cdev_name)-1] = '\0'; /* find tx virtqueues (rx and tx and in this order) */ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 14, 0) err = vdev->config->find_vqs(vdev, 2, vqs, vq_cbs, vq_names, NULL, NULL); +#else + err = vdev->config->find_vqs(vdev, 2, vqs, vq_cbs, vq_names); +#endif if (err) goto err_find_vqs; diff --git a/drivers/trusty/trusty-mem.c b/drivers/trusty/trusty-mem.c old mode 100755 new mode 100644 index fc299e348581..470df8823d3a --- a/drivers/trusty/trusty-mem.c +++ b/drivers/trusty/trusty-mem.c @@ -77,7 +77,7 @@ static int get_mem_attr(struct page *page, pgprot_t pgprot) return -EINVAL; } #elif defined(CONFIG_X86) - #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 1, 0) +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 1, 0) /* The porting to CHT kernel (3.14.55) is in the #else clause. ** For BXT kernel (4.1.0), the function get_page_memtype() is static. ** @@ -93,7 +93,7 @@ static int get_mem_attr(struct page *page, pgprot_t pgprot) ** with SMP, which only allow UNCACHED. */ return NS_MAIR_NORMAL_UNCACHED; - #else +#else unsigned long type; int ret_mem_attr = 0; @@ -124,7 +124,7 @@ static int get_mem_attr(struct page *page, pgprot_t pgprot) ret_mem_attr = -EINVAL; } return ret_mem_attr; - #endif +#endif #else return 0; #endif diff --git a/drivers/trusty/trusty-virtio.c b/drivers/trusty/trusty-virtio.c index 743a4789772f..66b4ee7caf0d 100644 --- a/drivers/trusty/trusty-virtio.c +++ b/drivers/trusty/trusty-virtio.c @@ -15,6 +15,7 @@ #include #include #include +#include #include #include @@ -337,9 +338,15 @@ static struct virtqueue *_find_vq(struct virtio_device *vdev, dev_info(&vdev->dev, "vring%d: va(pa) %p(%llx) qsz %d notifyid %d\n", id, tvr->vaddr, (u64)tvr->paddr, tvr->elem_num, tvr->notifyid); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 14, 0) tvr->vq = vring_new_virtqueue(id, tvr->elem_num, tvr->align, vdev, true, true, tvr->vaddr, trusty_virtio_notify, callback, name); +#else + tvr->vq = vring_new_virtqueue(id, tvr->elem_num, tvr->align, + vdev, true, tvr->vaddr, + trusty_virtio_notify, callback, name); +#endif if (!tvr->vq) { dev_err(&vdev->dev, "vring_new_virtqueue %s failed\n", name); @@ -356,12 +363,19 @@ static struct virtqueue *_find_vq(struct virtio_device *vdev, return ERR_PTR(-ENOMEM); } +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 14, 0) static int trusty_virtio_find_vqs(struct virtio_device *vdev, unsigned nvqs, struct virtqueue *vqs[], vq_callback_t *callbacks[], const char * const names[], const bool *ctx, struct irq_affinity *desc) +#else +static int trusty_virtio_find_vqs(struct virtio_device *vdev, unsigned nvqs, + struct virtqueue *vqs[], + vq_callback_t *callbacks[], + const char * const names[]) +#endif { uint i; int ret; From f131070d9bdb152045284395dc2245848cee6cdf Mon Sep 17 00:00:00 2001 From: "Yan, Shaopu" Date: Thu, 12 Apr 2018 09:06:04 +0800 Subject: [PATCH 1026/1103] Revert "trusty-ipc: change DEFAULT_MSG_BUF_SIZE to 68K" This reverts commit f3e776a486937859e6cd67ab558544544fae7004. Signed-off-by: Yan, Shaopu --- drivers/trusty/trusty-ipc.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/trusty/trusty-ipc.c b/drivers/trusty/trusty-ipc.c index 7df0972ddd05..f0b6b1bb444a 100644 --- a/drivers/trusty/trusty-ipc.c +++ b/drivers/trusty/trusty-ipc.c @@ -49,8 +49,7 @@ #define MAX_SRV_NAME_LEN 256 #define MAX_DEV_NAME_LEN 32 -#define DEFAULT_MSG_BUF_SIZE (68*1024) - +#define DEFAULT_MSG_BUF_SIZE PAGE_SIZE #define DEFAULT_MSG_BUF_ALIGN PAGE_SIZE #define TIPC_CTRL_ADDR 53 From 7d3e4d186ffd0cca85106820a9390de87fbce4e5 Mon Sep 17 00:00:00 2001 From: "Zhang, Qi" Date: Tue, 17 Jul 2018 21:16:53 +0800 Subject: [PATCH 1027/1103] refine work queue in trusty driver Change-Id: I049497485f87d2c90e23be11893696513602800b Tracked-On: Signed-off-by: Zhang, Qi --- drivers/trusty/trusty-timer.c | 7 +------ drivers/trusty/trusty-virtio.c | 4 ++-- drivers/trusty/trusty.c | 9 ++++++--- include/linux/trusty/smcall.h | 3 +++ 4 files changed, 12 insertions(+), 11 deletions(-) diff --git a/drivers/trusty/trusty-timer.c b/drivers/trusty/trusty-timer.c index 18e315c25067..6783a30b4a11 100644 --- a/drivers/trusty/trusty-timer.c +++ b/drivers/trusty/trusty-timer.c @@ -36,11 +36,6 @@ struct trusty_timer_dev_state { struct workqueue_struct *workqueue; }; -/* Max entity defined as SMC_NUM_ENTITIES(64) */ -#define SMC_ENTITY_SMC_X86 63 /* Used for customized SMC calls */ - -#define SMC_SC_LK_TIMER SMC_STDCALL_NR(SMC_ENTITY_SMC_X86, 0) - static void timer_work_func(struct work_struct *work) { int ret; @@ -59,7 +54,7 @@ static enum hrtimer_restart trusty_timer_cb(struct hrtimer *tm) s = container_of(tm, struct trusty_timer_dev_state, timer.tm); - queue_work(s->workqueue, &s->timer.work); + queue_work_on(0, s->workqueue, &s->timer.work); return HRTIMER_NORESTART; } diff --git a/drivers/trusty/trusty-virtio.c b/drivers/trusty/trusty-virtio.c index 66b4ee7caf0d..df066dda80d3 100644 --- a/drivers/trusty/trusty-virtio.c +++ b/drivers/trusty/trusty-virtio.c @@ -150,7 +150,7 @@ static bool trusty_virtio_notify(struct virtqueue *vq) if (api_ver < TRUSTY_API_VERSION_SMP_NOP) { atomic_set(&tvr->needs_kick, 1); - queue_work(tctx->kick_wq, &tctx->kick_vqs); + queue_work_on(0, tctx->kick_wq, &tctx->kick_vqs); } else { trusty_enqueue_nop(tctx->dev->parent, &tvr->kick_nop); } @@ -685,7 +685,7 @@ static int trusty_virtio_probe(struct platform_device *pdev) } tctx->kick_wq = alloc_workqueue("trusty-kick-wq", - WQ_UNBOUND | WQ_CPU_INTENSIVE, 0); + WQ_CPU_INTENSIVE, 0); if (!tctx->kick_wq) { ret = -ENODEV; dev_err(&pdev->dev, "Failed create trusty-kick-wq\n"); diff --git a/drivers/trusty/trusty.c b/drivers/trusty/trusty.c index 4d33f269851d..8f80f9b84772 100755 --- a/drivers/trusty/trusty.c +++ b/drivers/trusty/trusty.c @@ -270,7 +270,6 @@ static long trusty_std_call32_work(void *args) s32 trusty_std_call32(struct device *dev, u32 smcnr, u32 a0, u32 a1, u32 a2) { - const int cpu = 0; struct trusty_std_call32_args args = { .dev = dev, .smcnr = smcnr, @@ -280,7 +279,11 @@ s32 trusty_std_call32(struct device *dev, u32 smcnr, u32 a0, u32 a1, u32 a2) }; /* bind cpu 0 for now since trusty OS is running on physical cpu #0*/ - return work_on_cpu(cpu, trusty_std_call32_work, (void *) &args); + if((smcnr == SMC_SC_VDEV_KICK_VQ) || (smcnr == SMC_SC_LK_TIMER) + || (smcnr == SMC_SC_LOCKED_NOP) || (smcnr == SMC_SC_NOP)) + return trusty_std_call32_work((void *) &args); + else + return work_on_cpu(0, trusty_std_call32_work, (void *) &args); } EXPORT_SYMBOL(trusty_std_call32); @@ -490,7 +493,7 @@ void trusty_enqueue_nop(struct device *dev, struct trusty_nop *nop) list_add_tail(&nop->node, &s->nop_queue); spin_unlock_irqrestore(&s->nop_lock, flags); } - queue_work(s->nop_wq, &tw->work); + queue_work_on(0, s->nop_wq, &tw->work); preempt_enable(); } EXPORT_SYMBOL(trusty_enqueue_nop); diff --git a/include/linux/trusty/smcall.h b/include/linux/trusty/smcall.h index 3ab2f688cb33..55d25dddc4a8 100644 --- a/include/linux/trusty/smcall.h +++ b/include/linux/trusty/smcall.h @@ -152,4 +152,7 @@ #define SMC_SC_VDEV_KICK_VQ SMC_STDCALL_NR(SMC_ENTITY_TRUSTED_OS, 24) #define SMC_NC_VDEV_KICK_VQ SMC_STDCALL_NR(SMC_ENTITY_TRUSTED_OS, 25) +/* Max entity defined as SMC_NUM_ENTITIES(64) */ +#define SMC_ENTITY_SMC_X86 63 /* Used for customized SMC calls */ +#define SMC_SC_LK_TIMER SMC_STDCALL_NR(SMC_ENTITY_SMC_X86, 0) #endif /* __LINUX_TRUSTY_SMCALL_H */ From 75cc04176d9a55686ecb158708afe03fa62873bf Mon Sep 17 00:00:00 2001 From: "Zhang, Qi" Date: Tue, 17 Jul 2018 15:01:38 +0800 Subject: [PATCH 1028/1103] register suspend callback Save secure world context by the hyercall Change-Id: I21ad1569c12f9b8dda66ab47beab273d4b3791cb Tracked-On: Signed-off-by: Zhang, Qi --- drivers/trusty/trusty.c | 42 +++++++++++++++++++++++++++++++++++++++-- 1 file changed, 40 insertions(+), 2 deletions(-) diff --git a/drivers/trusty/trusty.c b/drivers/trusty/trusty.c index 8f80f9b84772..7bff133a4610 100755 --- a/drivers/trusty/trusty.c +++ b/drivers/trusty/trusty.c @@ -27,7 +27,8 @@ #include #define EVMM_SMC_HC_ID 0x74727500 -#define ACRN_SMC_HC_ID 0x80000071 +#define ACRN_HC_SWITCH_WORLD 0x80000071 +#define ACRN_HC_SAVE_SWORLD_CONTEXT 0x80000072 struct trusty_state; @@ -70,7 +71,7 @@ static inline ulong smc_evmm(ulong r0, ulong r1, ulong r2, ulong r3) static inline ulong smc_acrn(ulong r0, ulong r1, ulong r2, ulong r3) { - register unsigned long smc_id asm("r8") = ACRN_SMC_HC_ID; + register unsigned long smc_id asm("r8") = ACRN_HC_SWITCH_WORLD; __asm__ __volatile__( "vmcall; \n" : "=D"(r0) @@ -81,6 +82,20 @@ static inline ulong smc_acrn(ulong r0, ulong r1, ulong r2, ulong r3) return r0; } +static void acrn_save_sworld_context(void *arg) +{ + long *save_ret = arg; + register signed long result asm("rax"); + register unsigned long hc_id asm("r8") = ACRN_HC_SAVE_SWORLD_CONTEXT; + __asm__ __volatile__( + "vmcall; \n" + : "=r"(result) + : "r"(hc_id) + ); + + *save_ret = result; +} + static void trusty_fast_call32_remote(void *args) { struct trusty_smc_interface *p_args = args; @@ -631,6 +646,24 @@ static int trusty_remove(struct platform_device *pdev) return 0; } +static int trusty_suspend(struct platform_device *pdev, pm_message_t state) +{ + dev_info(&pdev->dev, "%s() is called\n", __func__); + long ret = 0, save_ret = 0; + int cpu = 0; + + ret = smp_call_function_single(cpu, acrn_save_sworld_context, (void *)&save_ret, 1); + if (ret) { + pr_err("%s: smp_call_function_single failed: %d\n", __func__, ret); + } + if(save_ret < 0) { + dev_err(&pdev->dev, "%s(): failed to save world context!\n", __func__); + return -EPERM; + } + + return 0; +} + static const struct of_device_id trusty_of_match[] = { { .compatible = "android,trusty-smc-v1", }, {}, @@ -764,6 +797,11 @@ static int __init trusty_driver_init(void) printk(KERN_ERR "%s(): platform_add_devices() failed, ret %d\n", __func__, ret); return ret; } + + if(trusty_detect_vmm() == VMM_ID_ACRN) { + trusty_driver.suspend = trusty_suspend; + } + return platform_driver_register(&trusty_driver); } From a215976a999683f09db873d6e050f9ebd10c5176 Mon Sep 17 00:00:00 2001 From: Zhou Furong Date: Fri, 10 Aug 2018 15:00:04 +0800 Subject: [PATCH 1029/1103] Fix compile warning from ISO90 and output format Fix the warning of mixed declarations and code which are forbidded in ISO90, and update 'long' output format. Change-Id: I96e6e4152151f1b26d5d2243974cc85bd7fc5bdd --- drivers/trusty/trusty.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/trusty/trusty.c b/drivers/trusty/trusty.c index 7bff133a4610..f37a1a58dce8 100755 --- a/drivers/trusty/trusty.c +++ b/drivers/trusty/trusty.c @@ -648,13 +648,14 @@ static int trusty_remove(struct platform_device *pdev) static int trusty_suspend(struct platform_device *pdev, pm_message_t state) { - dev_info(&pdev->dev, "%s() is called\n", __func__); long ret = 0, save_ret = 0; int cpu = 0; + dev_info(&pdev->dev, "%s() is called\n", __func__); + ret = smp_call_function_single(cpu, acrn_save_sworld_context, (void *)&save_ret, 1); if (ret) { - pr_err("%s: smp_call_function_single failed: %d\n", __func__, ret); + pr_err("%s: smp_call_function_single failed: %ld\n", __func__, ret); } if(save_ret < 0) { dev_err(&pdev->dev, "%s(): failed to save world context!\n", __func__); From a57532398c46a50daac60cf346ead9c8dbd3448c Mon Sep 17 00:00:00 2001 From: "Zhang, Qi" Date: Thu, 16 Aug 2018 18:04:44 +0800 Subject: [PATCH 1030/1103] check return value of hypercall exit from probe if acrn does not enable trusty Change-Id: I99271cd96c6df46e141b4e57a2af378119a1c25c Tracked-On: Signed-off-by: Zhang, Qi --- drivers/trusty/trusty.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/drivers/trusty/trusty.c b/drivers/trusty/trusty.c index f37a1a58dce8..881924f88e4f 100755 --- a/drivers/trusty/trusty.c +++ b/drivers/trusty/trusty.c @@ -72,13 +72,18 @@ static inline ulong smc_evmm(ulong r0, ulong r1, ulong r2, ulong r3) static inline ulong smc_acrn(ulong r0, ulong r1, ulong r2, ulong r3) { register unsigned long smc_id asm("r8") = ACRN_HC_SWITCH_WORLD; + register signed long ret asm("rax"); __asm__ __volatile__( "vmcall; \n" - : "=D"(r0) + : "=D"(r0), "=r"(ret) : "r"(smc_id), "D"(r0), "S"(r1), "d"(r2), "b"(r3) - : "rax" ); + if(ret < 0) { + pr_err("trusty: %s: hypercall failed: %ld\n", __func__, ret); + r0 = (ulong)SM_ERR_NOT_SUPPORTED; + } + return r0; } From effc6c07f9861cd834d919f618f35f293a994a50 Mon Sep 17 00:00:00 2001 From: Zhou Furong Date: Mon, 27 Aug 2018 15:15:51 +0800 Subject: [PATCH 1031/1103] Fix compilation errors when rebase to v4.19-rc1. Include header file of_platform.h when update kernel to v4.19-rc1. Change-Id: I732913061fed8ab14edddb40544df370e19edc54 --- drivers/trusty/trusty-log.c | 4 ++++ drivers/trusty/trusty-timer.c | 4 ++++ drivers/trusty/trusty-wall.c | 4 ++++ 3 files changed, 12 insertions(+) diff --git a/drivers/trusty/trusty-log.c b/drivers/trusty/trusty-log.c index d2446a1f34c9..48883439dce2 100755 --- a/drivers/trusty/trusty-log.c +++ b/drivers/trusty/trusty-log.c @@ -12,6 +12,10 @@ * */ #include +#include +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 19, 0) +#include +#endif #include #include #include diff --git a/drivers/trusty/trusty-timer.c b/drivers/trusty/trusty-timer.c index 6783a30b4a11..ca6ea5799eeb 100644 --- a/drivers/trusty/trusty-timer.c +++ b/drivers/trusty/trusty-timer.c @@ -15,6 +15,10 @@ #include #include #include +#include +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 19, 0) +#include +#endif #include #include #include diff --git a/drivers/trusty/trusty-wall.c b/drivers/trusty/trusty-wall.c index 2345f56a6405..812ac2a3ea98 100644 --- a/drivers/trusty/trusty-wall.c +++ b/drivers/trusty/trusty-wall.c @@ -13,6 +13,10 @@ * */ #include +#include +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 19, 0) +#include +#endif #include #include #include From c1f2cfe71ce9d4aee0f2883c9b081975a5e689cb Mon Sep 17 00:00:00 2001 From: "Fan, Yugang" Date: Mon, 17 Sep 2018 09:50:43 +0800 Subject: [PATCH 1032/1103] drm/i915: Sysfs interface to get GFX shmem usage stats per process There is a requirement of a new interface for achieving below functionalities: 1) Need to provide Client based detailed information about the distribution of Graphics memory 2) Need to provide an interface which can provide info about the sharing of Graphics buffers between the clients. The client based interface would also aid in debugging of memory usage/consumption by each client & debug memleak related issues. With this new interface, 1) In case of memleak scenarios, we can easily zero in on the culprit client which is unexpectedly holding on the Graphics buffers for an inordinate amount of time. 2) We can get an estimate of the instantaneous memory footprint of every Graphics client. 3) We can now trace all the processes sharing a particular Graphics buffer. By means of this patch we try to provide a sysfs interface to achieve the mentioned functionalities. There are two files created in sysfs: 'i915_gem_meminfo' will provide summary of the graphics resources used by each graphics client. 'i915_gem_objinfo' will provide detailed view of each object created by individual clients. Rebased for kernel v4.19 1A (Fan Yugang) 1) Removes unuseful shmem_inode_info->info_lock per Jeremy's optimization. 2) Removes unuseful object info for each pid for performance optimization. 3) Adds CONFIG_DRM_I915_MEMTRACK which depends on CONFIG_DRM_I915_CAPTURE_ERROR to pass kernel config test. Change-Id: If9705d001b922de3570693b2ad39125babd8e860 Signed-off-by: Sourab Gupta Sgined-off-by: Deepak S Signed-off-by: Hu Beiyuan Signed-off-by: Mingwei Wang Signed-off-by: Harish Krupo Signed-off-by: Jeremy Compostella Signed-off-by: Yugang Fan --- drivers/gpu/drm/drm_file.c | 4 + drivers/gpu/drm/drm_internal.h | 5 + drivers/gpu/drm/i915/Kconfig | 10 + drivers/gpu/drm/i915/i915_drv.c | 7 + drivers/gpu/drm/i915/i915_drv.h | 45 ++ drivers/gpu/drm/i915/i915_gem.c | 954 +++++++++++++++++++++++++ drivers/gpu/drm/i915/i915_gem_object.h | 8 + drivers/gpu/drm/i915/i915_gpu_error.c | 25 +- drivers/gpu/drm/i915/i915_gpu_error.h | 7 + drivers/gpu/drm/i915/i915_sysfs.c | 311 ++++++++ 10 files changed, 1375 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/drm_file.c b/drivers/gpu/drm/drm_file.c index ffa8dc35515f..d47119e045e8 100644 --- a/drivers/gpu/drm/drm_file.c +++ b/drivers/gpu/drm/drm_file.c @@ -46,6 +46,10 @@ /* from BKL pushdown */ DEFINE_MUTEX(drm_global_mutex); +#if IS_ENABLED(CONFIG_DRM_I915_MEMTRACK) +EXPORT_SYMBOL(drm_global_mutex); +#endif + /** * DOC: file operations * diff --git a/drivers/gpu/drm/drm_internal.h b/drivers/gpu/drm/drm_internal.h index 40179c5fc6b8..d9848ae2219e 100644 --- a/drivers/gpu/drm/drm_internal.h +++ b/drivers/gpu/drm/drm_internal.h @@ -48,6 +48,11 @@ void drm_prime_remove_buf_handle_locked(struct drm_prime_file_private *prime_fpr struct dma_buf *dma_buf); /* drm_drv.c */ + +#if IS_ENABLED(CONFIG_DRM_I915_MEMTRACK) +#define DRM_MAGIC_HASH_ORDER 4 /**< Size of key hash table. Must be power of 2. */ +#endif + struct drm_minor *drm_minor_acquire(unsigned int minor_id); void drm_minor_release(struct drm_minor *minor); diff --git a/drivers/gpu/drm/i915/Kconfig b/drivers/gpu/drm/i915/Kconfig index 33a458b7f1fc..858c578179d9 100644 --- a/drivers/gpu/drm/i915/Kconfig +++ b/drivers/gpu/drm/i915/Kconfig @@ -84,6 +84,16 @@ config DRM_I915_COMPRESS_ERROR If in doubt, say "Y". +config DRM_I915_MEMTRACK + bool "Enable shmem usage status track" + depends on DRM_I915_CAPTURE_ERROR + default y + help + This option enables shmem usage status track of system summary and + each process. + + If in doubt, say "N". + config DRM_I915_USERPTR bool "Always enable userptr support" depends on DRM_I915 diff --git a/drivers/gpu/drm/i915/i915_drv.c b/drivers/gpu/drm/i915/i915_drv.c index f8cfd16be534..d8ec9962c94e 100644 --- a/drivers/gpu/drm/i915/i915_drv.c +++ b/drivers/gpu/drm/i915/i915_drv.c @@ -1516,6 +1516,10 @@ static void i915_driver_postclose(struct drm_device *dev, struct drm_file *file) i915_gem_release(dev, file); mutex_unlock(&dev->struct_mutex); +#if IS_ENABLED(CONFIG_DRM_I915_MEMTRACK) + kfree(file_priv->process_name); +#endif + kfree(file_priv); } @@ -2874,6 +2878,9 @@ static struct drm_driver driver = { .lastclose = i915_driver_lastclose, .postclose = i915_driver_postclose, +#if IS_ENABLED(CONFIG_DRM_I915_MEMTRACK) + .gem_open_object = i915_gem_open_object, +#endif .gem_close_object = i915_gem_close_object, .gem_free_object_unlocked = i915_gem_free_object, .gem_vm_ops = &i915_gem_vm_ops, diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index 4aca5344863d..c2791ebd3fca 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -78,6 +78,9 @@ #include "i915_scheduler.h" #include "i915_timeline.h" #include "i915_vma.h" +#if IS_ENABLED(CONFIG_DRM_I915_MEMTRACK) +#include "i915_gpu_error.h" +#endif #include "intel_gvt.h" @@ -333,6 +336,11 @@ struct drm_i915_file_private { struct drm_i915_private *dev_priv; struct drm_file *file; +#if IS_ENABLED(CONFIG_DRM_I915_MEMTRACK) + char *process_name; + struct pid *tgid; +#endif + struct { spinlock_t lock; struct list_head request_list; @@ -351,6 +359,10 @@ struct drm_i915_file_private { unsigned int bsd_engine; +#if IS_ENABLED(CONFIG_DRM_I915_MEMTRACK) + struct bin_attribute *obj_attr; +#endif + /* * Every context ban increments per client ban score. Also * hangs in short succession increments ban score. If ban threshold @@ -996,6 +1008,10 @@ struct i915_gem_mm { spinlock_t object_stat_lock; u64 object_memory; u32 object_count; + +#if IS_ENABLED(CONFIG_DRM_I915_MEMTRACK) + size_t phys_mem_total; +#endif }; #define I915_IDLE_ENGINES_TIMEOUT (200) /* in ms */ @@ -1666,6 +1682,10 @@ struct drm_i915_private { bool preserve_bios_swizzle; +#if IS_ENABLED(CONFIG_DRM_I915_MEMTRACK) + struct kobject memtrack_kobj; +#endif + /* overlay */ struct intel_overlay *overlay; @@ -2898,6 +2918,11 @@ i915_gem_object_create(struct drm_i915_private *dev_priv, u64 size); struct drm_i915_gem_object * i915_gem_object_create_from_data(struct drm_i915_private *dev_priv, const void *data, size_t size); + +#if IS_ENABLED(CONFIG_DRM_I915_MEMTRACK) +int i915_gem_open_object(struct drm_gem_object *gem, struct drm_file *file); +#endif + void i915_gem_close_object(struct drm_gem_object *gem, struct drm_file *file); void i915_gem_free_object(struct drm_gem_object *obj); @@ -3319,6 +3344,19 @@ u32 i915_gem_fence_size(struct drm_i915_private *dev_priv, u32 size, u32 i915_gem_fence_alignment(struct drm_i915_private *dev_priv, u32 size, unsigned int tiling, unsigned int stride); +#if IS_ENABLED(CONFIG_DRM_I915_MEMTRACK) +int i915_get_pid_cmdline(struct task_struct *task, char *buffer); +int i915_gem_obj_insert_pid(struct drm_i915_gem_object *obj); +void i915_gem_obj_remove_all_pids(struct drm_i915_gem_object *obj); +int i915_obj_insert_virt_addr(struct drm_i915_gem_object *obj, + unsigned long addr, bool is_map_gtt, + bool is_mutex_locked); +int i915_get_drm_clients_info(struct drm_i915_error_state_buf *m, + struct drm_device *dev); +int i915_gem_get_obj_info(struct drm_i915_error_state_buf *m, + struct drm_device *dev, struct pid *tgid); +#endif + /* i915_debugfs.c */ #ifdef CONFIG_DEBUG_FS int i915_debugfs_register(struct drm_i915_private *dev_priv); @@ -3358,6 +3396,13 @@ extern int i915_restore_state(struct drm_i915_private *dev_priv); void i915_setup_sysfs(struct drm_i915_private *dev_priv); void i915_teardown_sysfs(struct drm_i915_private *dev_priv); +#if IS_ENABLED(CONFIG_DRM_I915_MEMTRACK) +int i915_gem_create_sysfs_file_entry(struct drm_device *dev, + struct drm_file *file); +void i915_gem_remove_sysfs_file_entry(struct drm_device *dev, + struct drm_file *file); +#endif + /* intel_lpe_audio.c */ int intel_lpe_audio_init(struct drm_i915_private *dev_priv); void intel_lpe_audio_teardown(struct drm_i915_private *dev_priv); diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index fcc73a6ab503..4b31093239b2 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -47,8 +47,852 @@ #include #include +#if IS_ENABLED(CONFIG_DRM_I915_MEMTRACK) +#include +#include +#include +#include "../drm_internal.h" +#endif + static void i915_gem_flush_free_objects(struct drm_i915_private *i915); +#if IS_ENABLED(CONFIG_DRM_I915_MEMTRACK) +struct per_file_obj_mem_info { + int num_obj; + int num_obj_shared; + int num_obj_private; + int num_obj_gtt_bound; + int num_obj_purged; + int num_obj_purgeable; + int num_obj_allocated; + int num_obj_fault_mappable; + int num_obj_stolen; + size_t gtt_space_allocated_shared; + size_t gtt_space_allocated_priv; + size_t phys_space_allocated_shared; + size_t phys_space_allocated_priv; + size_t phys_space_purgeable; + size_t phys_space_shared_proportion; + size_t fault_mappable_size; + size_t stolen_space_allocated; + char *process_name; +}; + +struct name_entry { + struct list_head head; + struct drm_hash_item hash_item; +}; + +struct pid_stat_entry { + struct list_head head; + struct list_head namefree; + struct drm_open_hash namelist; + struct per_file_obj_mem_info stats; + struct pid *tgid; + int pid_num; +}; + +struct drm_i915_obj_virt_addr { + struct list_head head; + unsigned long user_virt_addr; +}; + +struct drm_i915_obj_pid_info { + struct list_head head; + pid_t tgid; + int open_handle_count; + struct list_head virt_addr_head; +}; + +struct get_obj_stats_buf { + struct pid_stat_entry *entry; + struct drm_i915_error_state_buf *m; +}; + +#define err_printf(e, ...) i915_error_printf(e, __VA_ARGS__) +#define err_puts(e, s) i915_error_puts(e, s) + +static const char *get_tiling_flag(struct drm_i915_gem_object *obj) +{ + switch (i915_gem_object_get_tiling(obj)) { + default: + case I915_TILING_NONE: return " "; + case I915_TILING_X: return "X"; + case I915_TILING_Y: return "Y"; + } +} + +/* + * If this mmput call is the last one, it will tear down the mmaps of the + * process and calls drm_gem_vm_close(), which leads deadlock on i915 mutex. + * Instead, asynchronously schedule mmput function here, to avoid recursive + * calls to acquire i915_mutex. + */ +static void async_mmput_func(void *data, async_cookie_t cookie) +{ + struct mm_struct *mm = data; + mmput(mm); +} + +static void async_mmput(struct mm_struct *mm) +{ + async_schedule(async_mmput_func, mm); +} + +int i915_get_pid_cmdline(struct task_struct *task, char *buffer) +{ + int res = 0; + unsigned int len; + struct mm_struct *mm = get_task_mm(task); + + if (!mm) + goto out; + if (!mm->arg_end) + goto out_mm; + + len = mm->arg_end - mm->arg_start; + + if (len > PAGE_SIZE) + len = PAGE_SIZE; + + res = access_process_vm(task, mm->arg_start, buffer, len, 0); + if (res < 0) { + async_mmput(mm); + return res; + } + + if (res > 0 && buffer[res-1] != '\0' && len < PAGE_SIZE) + buffer[res-1] = '\0'; +out_mm: + async_mmput(mm); +out: + return 0; +} + +static int i915_obj_get_shmem_pages_alloced(struct drm_i915_gem_object *obj) +{ + if (obj->base.filp) { + struct inode *inode = file_inode(obj->base.filp); + + if (!inode) + return 0; + return inode->i_mapping->nrpages; + } + return 0; +} + +int i915_gem_obj_insert_pid(struct drm_i915_gem_object *obj) +{ + int found = 0; + struct drm_i915_obj_pid_info *entry; + pid_t current_tgid = task_tgid_nr(current); + + mutex_lock(&obj->base.dev->struct_mutex); + + list_for_each_entry(entry, &obj->pid_info, head) { + if (entry->tgid == current_tgid) { + entry->open_handle_count++; + found = 1; + break; + } + } + if (found == 0) { + entry = kzalloc(sizeof(*entry), GFP_KERNEL); + if (entry == NULL) { + DRM_ERROR("alloc failed\n"); + mutex_unlock(&obj->base.dev->struct_mutex); + return -ENOMEM; + } + entry->tgid = current_tgid; + entry->open_handle_count = 1; + INIT_LIST_HEAD(&entry->virt_addr_head); + list_add_tail(&entry->head, &obj->pid_info); + } + + mutex_unlock(&obj->base.dev->struct_mutex); + return 0; +} + +void i915_gem_obj_remove_all_pids(struct drm_i915_gem_object *obj) +{ + struct drm_i915_obj_pid_info *pid_entry, *pid_next; + struct drm_i915_obj_virt_addr *virt_entry, *virt_next; + + list_for_each_entry_safe(pid_entry, pid_next, &obj->pid_info, head) { + list_for_each_entry_safe(virt_entry, + virt_next, + &pid_entry->virt_addr_head, + head) { + list_del(&virt_entry->head); + kfree(virt_entry); + } + list_del(&pid_entry->head); + kfree(pid_entry); + } +} + + int +i915_obj_insert_virt_addr(struct drm_i915_gem_object *obj, + unsigned long addr, + bool is_map_gtt, + bool is_mutex_locked) +{ + struct drm_i915_obj_pid_info *pid_entry; + pid_t current_tgid = task_tgid_nr(current); + int ret = 0, found = 0; + + if (is_map_gtt) + addr |= 1; + + if (!is_mutex_locked) { + ret = i915_mutex_lock_interruptible(obj->base.dev); + if (ret) + return ret; + } + + list_for_each_entry(pid_entry, &obj->pid_info, head) { + if (pid_entry->tgid == current_tgid) { + struct drm_i915_obj_virt_addr *virt_entry, *new_entry; + + list_for_each_entry(virt_entry, + &pid_entry->virt_addr_head, + head) { + if (virt_entry->user_virt_addr == addr) { + found = 1; + break; + } + } + if (found) + break; + new_entry = kzalloc(sizeof(*new_entry), GFP_KERNEL); + if (new_entry == NULL) { + DRM_ERROR("alloc failed\n"); + ret = -ENOMEM; + goto out; + } + new_entry->user_virt_addr = addr; + list_add_tail(&new_entry->head, + &pid_entry->virt_addr_head); + break; + } + } + +out: + if (!is_mutex_locked) + mutex_unlock(&obj->base.dev->struct_mutex); + + return ret; +} + +static int i915_obj_virt_addr_is_invalid(struct drm_gem_object *obj, + struct pid *tgid, unsigned long addr) +{ + struct task_struct *task; + struct mm_struct *mm; + struct vm_area_struct *vma; + int locked, ret = 0; + + task = get_pid_task(tgid, PIDTYPE_PID); + if (task == NULL) { + DRM_DEBUG("null task for tgid=%d\n", pid_nr(tgid)); + return -EINVAL; + } + + mm = get_task_mm(task); + if (mm == NULL) { + DRM_DEBUG("null mm for tgid=%d\n", pid_nr(tgid)); + ret = -EINVAL; + goto out_task; + } + + locked = down_read_trylock(&mm->mmap_sem); + if (!locked) + goto out_mm; + + vma = find_vma(mm, addr); + if (vma) { + if (addr & 1) { /* mmap_gtt case */ + if (vma->vm_pgoff*PAGE_SIZE == (unsigned long) + drm_vma_node_offset_addr(&obj->vma_node)) + ret = 0; + else + ret = -EINVAL; + } else { /* mmap case */ + if (vma->vm_file == obj->filp) + ret = 0; + else + ret = -EINVAL; + } + } else + ret = -EINVAL; + + up_read(&mm->mmap_sem); + +out_mm: + async_mmput(mm); +out_task: + put_task_struct(task); + return ret; +} + +static void i915_obj_pidarray_validate(struct drm_gem_object *gem_obj) +{ + struct drm_i915_gem_object *obj = to_intel_bo(gem_obj); + struct drm_device *dev = gem_obj->dev; + struct drm_i915_obj_virt_addr *virt_entry, *virt_next; + struct drm_i915_obj_pid_info *pid_entry, *pid_next; + struct drm_file *file; + struct drm_i915_file_private *file_priv; + struct pid *tgid; + int pid_num, present; + + /* + * Run a sanity check on pid_array. All entries in pid_array should + * be subset of the the drm filelist pid entries. + */ + list_for_each_entry_safe(pid_entry, pid_next, &obj->pid_info, head) { + if (pid_next == NULL) { + DRM_ERROR( + "Invalid pid info. obj:%p, size:%zdK, tiling:%s, userptr=%s, stolen:%s, name:%d, handle_count=%d\n", + &obj->base, obj->base.size/1024, + get_tiling_flag(obj), + (obj->userptr.mm != 0) ? "Y" : "N", + obj->stolen ? "Y" : "N", obj->base.name, + obj->base.handle_count); + break; + } + + present = 0; + list_for_each_entry(file, &dev->filelist, lhead) { + file_priv = file->driver_priv; + tgid = file_priv->tgid; + pid_num = pid_nr(tgid); + + if (pid_num == pid_entry->tgid) { + present = 1; + break; + } + } + if (present == 0) { + DRM_DEBUG("stale_tgid=%d\n", pid_entry->tgid); + list_for_each_entry_safe(virt_entry, virt_next, + &pid_entry->virt_addr_head, + head) { + list_del(&virt_entry->head); + kfree(virt_entry); + } + list_del(&pid_entry->head); + kfree(pid_entry); + } else { + /* Validate the virtual address list */ + struct task_struct *task = + get_pid_task(tgid, PIDTYPE_PID); + if (task == NULL) + continue; + + list_for_each_entry_safe(virt_entry, virt_next, + &pid_entry->virt_addr_head, + head) { + if (i915_obj_virt_addr_is_invalid(gem_obj, tgid, + virt_entry->user_virt_addr)) { + DRM_DEBUG("stale_addr=%ld\n", + virt_entry->user_virt_addr); + list_del(&virt_entry->head); + kfree(virt_entry); + } + } + put_task_struct(task); + } + } +} + +static int i915_obj_find_insert_in_hash(struct drm_i915_gem_object *obj, + struct pid_stat_entry *pid_entry, + bool *found) +{ + struct drm_hash_item *hash_item; + int ret; + + ret = drm_ht_find_item(&pid_entry->namelist, + (unsigned long)&obj->base, &hash_item); + /* Not found, insert in hash */ + if (ret) { + struct name_entry *entry = + kzalloc(sizeof(*entry), GFP_NOWAIT); + if (entry == NULL) { + DRM_ERROR("alloc failed\n"); + return -ENOMEM; + } + entry->hash_item.key = (unsigned long)&obj->base; + drm_ht_insert_item(&pid_entry->namelist, + &entry->hash_item); + list_add_tail(&entry->head, &pid_entry->namefree); + *found = false; + } else + *found = true; + + return 0; +} + +static int i915_obj_shared_count(struct drm_i915_gem_object *obj, + struct pid_stat_entry *pid_entry, + bool *discard) +{ + struct drm_i915_obj_pid_info *pid_info_entry; + int ret, obj_shared_count = 0; + + /* + * The object can be shared among different processes by either flink + * or dma-buf mechanism, leading to shared count more than 1. For the + * objects not shared , return the shared count as 1. + * In case of shared dma-buf objects, there's a possibility that these + * may be external to i915. Detect this condition through + * 'import_attach' field. + */ + if (!obj->base.name && !obj->base.dma_buf) + return 1; + else if(obj->base.import_attach) { + /* not our GEM obj */ + *discard = true; + return 0; + } + + ret = i915_obj_find_insert_in_hash(obj, pid_entry, discard); + if (ret) + return ret; + + list_for_each_entry(pid_info_entry, &obj->pid_info, head) + obj_shared_count++; + + if (WARN_ON(obj_shared_count == 0)) + return -EINVAL; + + return obj_shared_count; +} + + static int +i915_describe_obj(struct get_obj_stats_buf *obj_stat_buf, + struct drm_i915_gem_object *obj) +{ + struct pid_stat_entry *pid_entry = obj_stat_buf->entry; + struct per_file_obj_mem_info *stats = &pid_entry->stats; + int obj_shared_count = 0; + + bool discard = false; + + obj_shared_count = i915_obj_shared_count(obj, pid_entry, &discard); + if (obj_shared_count < 0) + return obj_shared_count; + + if (!discard && !obj->stolen && + (obj->mm.madv != __I915_MADV_PURGED) && + (i915_obj_get_shmem_pages_alloced(obj) != 0)) { + if (obj_shared_count > 1) + stats->phys_space_shared_proportion += + obj->base.size/obj_shared_count; + else + stats->phys_space_allocated_priv += + obj->base.size; + } + + return 0; +} + + static int +i915_drm_gem_obj_info(int id, void *ptr, void *data) +{ + struct drm_i915_gem_object *obj = ptr; + struct get_obj_stats_buf *obj_stat_buf = data; + + if (obj->pid_info.next == NULL) { + DRM_ERROR( + "Invalid pid info. obj:%p, size:%zdK, tiling:%s, userptr=%s, stolen:%s, name:%d, handle_count=%d\n", + &obj->base, obj->base.size/1024, + get_tiling_flag(obj), + (obj->userptr.mm != 0) ? "Y" : "N", + obj->stolen ? "Y" : "N", obj->base.name, + obj->base.handle_count); + return 0; + } + + return i915_describe_obj(obj_stat_buf, obj); +} + +bool i915_gem_obj_bound_any(struct drm_i915_gem_object *o) +{ + struct i915_vma *vma; + + list_for_each_entry(vma, &o->vma_list, obj_link) + if (drm_mm_node_allocated(&vma->node)) + return true; + + return false; +} + + static int +i915_drm_gem_object_per_file_summary(int id, void *ptr, void *data) +{ + struct pid_stat_entry *pid_entry = data; + struct drm_i915_gem_object *obj = ptr; + struct per_file_obj_mem_info *stats = &pid_entry->stats; + int obj_shared_count = 0; + bool discard = false; + + if (obj->pid_info.next == NULL) { + DRM_ERROR( + "Invalid pid info. obj:%p, size:%zdK, tiling:%s, userptr=%s, stolen:%s, name:%d, handle_count=%d\n", + &obj->base, obj->base.size/1024, + get_tiling_flag(obj), + (obj->userptr.mm != 0) ? "Y" : "N", + obj->stolen ? "Y" : "N", obj->base.name, + obj->base.handle_count); + return 0; + } + + i915_obj_pidarray_validate(&obj->base); + + stats->num_obj++; + + obj_shared_count = i915_obj_shared_count(obj, pid_entry, &discard); + if (obj_shared_count < 0) + return obj_shared_count; + + if (discard) + return 0; + + if (obj_shared_count > 1) + stats->num_obj_shared++; + else + stats->num_obj_private++; + + if (i915_gem_obj_bound_any(obj)) { + stats->num_obj_gtt_bound++; + if (obj_shared_count > 1) + stats->gtt_space_allocated_shared += obj->base.size; + else + stats->gtt_space_allocated_priv += obj->base.size; + } + + if (obj->stolen) { + stats->num_obj_stolen++; + stats->stolen_space_allocated += obj->base.size; + } else if (obj->mm.madv == __I915_MADV_PURGED) { + stats->num_obj_purged++; + } else if (obj->mm.madv == I915_MADV_DONTNEED) { + stats->num_obj_purgeable++; + stats->num_obj_allocated++; + if (i915_obj_get_shmem_pages_alloced(obj) != 0) { + stats->phys_space_purgeable += obj->base.size; + if (obj_shared_count > 1) { + stats->phys_space_allocated_shared += + obj->base.size; + stats->phys_space_shared_proportion += + obj->base.size/obj_shared_count; + } else + stats->phys_space_allocated_priv += + obj->base.size; + } else + WARN_ON(1); + } else if (i915_obj_get_shmem_pages_alloced(obj) != 0) { + stats->num_obj_allocated++; + if (obj_shared_count > 1) { + stats->phys_space_allocated_shared += + obj->base.size; + stats->phys_space_shared_proportion += + obj->base.size/obj_shared_count; + } + else + stats->phys_space_allocated_priv += obj->base.size; + } + return 0; +} + + static int +__i915_get_drm_clients_info(struct drm_i915_error_state_buf *m, + struct drm_device *dev) +{ + struct drm_file *file; + struct drm_i915_private *dev_priv = dev->dev_private; + + struct name_entry *entry, *next; + struct pid_stat_entry *pid_entry, *temp_entry; + struct pid_stat_entry *new_pid_entry, *new_temp_entry; + struct list_head per_pid_stats, sorted_pid_stats; + int ret = 0; + size_t total_shared_prop_space = 0, total_priv_space = 0; + + INIT_LIST_HEAD(&per_pid_stats); + INIT_LIST_HEAD(&sorted_pid_stats); + + err_puts(m, + "\n\n pid Total Shared Priv Purgeable Alloced SharedPHYsize SharedPHYprop PrivPHYsize PurgeablePHYsize process\n"); + + list_for_each_entry(file, &dev->filelist, lhead) { + struct pid *tgid; + struct drm_i915_file_private *file_priv = file->driver_priv; + int pid_num, found = 0; + + tgid = file_priv->tgid; + pid_num = pid_nr(tgid); + + list_for_each_entry(pid_entry, &per_pid_stats, head) { + if (pid_entry->pid_num == pid_num) { + found = 1; + break; + } + } + + if (!found) { + struct pid_stat_entry *new_entry = + kzalloc(sizeof(*new_entry), GFP_KERNEL); + if (new_entry == NULL) { + DRM_ERROR("alloc failed\n"); + ret = -ENOMEM; + break; + } + new_entry->tgid = tgid; + new_entry->pid_num = pid_num; + ret = drm_ht_create(&new_entry->namelist, + DRM_MAGIC_HASH_ORDER); + if (ret) { + kfree(new_entry); + break; + } + + list_add_tail(&new_entry->head, &per_pid_stats); + INIT_LIST_HEAD(&new_entry->namefree); + new_entry->stats.process_name = file_priv->process_name; + pid_entry = new_entry; + } + + spin_lock(&file->table_lock); + ret = idr_for_each(&file->object_idr, + &i915_drm_gem_object_per_file_summary, pid_entry); + spin_unlock(&file->table_lock); + if (ret) + break; + } + + list_for_each_entry_safe(pid_entry, temp_entry, &per_pid_stats, head) { + if (list_empty(&sorted_pid_stats)) { + list_del(&pid_entry->head); + list_add_tail(&pid_entry->head, &sorted_pid_stats); + continue; + } + + list_for_each_entry_safe(new_pid_entry, new_temp_entry, + &sorted_pid_stats, head) { + int prev_space = + pid_entry->stats.phys_space_shared_proportion + + pid_entry->stats.phys_space_allocated_priv; + int new_space = + new_pid_entry-> + stats.phys_space_shared_proportion + + new_pid_entry->stats.phys_space_allocated_priv; + if (prev_space > new_space) { + list_del(&pid_entry->head); + list_add_tail(&pid_entry->head, + &new_pid_entry->head); + break; + } + if (list_is_last(&new_pid_entry->head, + &sorted_pid_stats)) { + list_del(&pid_entry->head); + list_add_tail(&pid_entry->head, + &sorted_pid_stats); + } + } + } + + list_for_each_entry_safe(pid_entry, temp_entry, + &sorted_pid_stats, head) { + struct task_struct *task = get_pid_task(pid_entry->tgid, + PIDTYPE_PID); + err_printf(m, + "%5d %6d %6d %6d %9d %8d %14zdK %14zdK %14zdK %14zdK %s", + pid_entry->pid_num, + pid_entry->stats.num_obj, + pid_entry->stats.num_obj_shared, + pid_entry->stats.num_obj_private, + pid_entry->stats.num_obj_purgeable, + pid_entry->stats.num_obj_allocated, + pid_entry->stats.phys_space_allocated_shared/1024, + pid_entry->stats.phys_space_shared_proportion/1024, + pid_entry->stats.phys_space_allocated_priv/1024, + pid_entry->stats.phys_space_purgeable/1024, + pid_entry->stats.process_name); + + if (task == NULL) + err_puts(m, "*\n"); + else + err_puts(m, "\n"); + + total_shared_prop_space += + pid_entry->stats.phys_space_shared_proportion/1024; + total_priv_space += + pid_entry->stats.phys_space_allocated_priv/1024; + list_del(&pid_entry->head); + + list_for_each_entry_safe(entry, next, + &pid_entry->namefree, head) { + list_del(&entry->head); + drm_ht_remove_item(&pid_entry->namelist, + &entry->hash_item); + kfree(entry); + } + drm_ht_remove(&pid_entry->namelist); + kfree(pid_entry); + if (task) + put_task_struct(task); + } + + err_puts(m, + "\t\t\t\t\t\t\t\t--------------\t-------------\t--------\n"); + err_printf(m, + "\t\t\t\t\t\t\t\t%13zdK\t%12zdK\tTotal\n", + total_shared_prop_space, total_priv_space); + + err_printf(m, "\nTotal used GFX Shmem Physical space %8zdK\n", + dev_priv->mm.phys_mem_total/1024); + + if (ret) + return ret; + if (m->bytes == 0 && m->err) + return m->err; + + return 0; +} + +#define NUM_SPACES 100 +#define INITIAL_SPACES_STR(x) #x +#define SPACES_STR(x) INITIAL_SPACES_STR(x) + + static int +__i915_gem_get_obj_info(struct drm_i915_error_state_buf *m, + struct drm_device *dev, struct pid *tgid) +{ + struct drm_file *file; + struct drm_i915_file_private *file_priv_reqd = NULL; + int bytes_copy, ret = 0; + struct pid_stat_entry pid_entry; + struct name_entry *entry, *next; + + pid_entry.stats.phys_space_shared_proportion = 0; + pid_entry.stats.phys_space_allocated_priv = 0; + pid_entry.tgid = tgid; + pid_entry.pid_num = pid_nr(tgid); + ret = drm_ht_create(&pid_entry.namelist, DRM_MAGIC_HASH_ORDER); + if (ret) + return ret; + + INIT_LIST_HEAD(&pid_entry.namefree); + + /* + * Fill up initial few bytes with spaces, to insert summary data later + * on + */ + err_printf(m, "%"SPACES_STR(NUM_SPACES)"s\n", " "); + + list_for_each_entry(file, &dev->filelist, lhead) { + struct drm_i915_file_private *file_priv = file->driver_priv; + struct get_obj_stats_buf obj_stat_buf; + + obj_stat_buf.entry = &pid_entry; + obj_stat_buf.m = m; + + if (file_priv->tgid != tgid) + continue; + + file_priv_reqd = file_priv; + spin_lock(&file->table_lock); + ret = idr_for_each(&file->object_idr, + &i915_drm_gem_obj_info, &obj_stat_buf); + spin_unlock(&file->table_lock); + if (ret) + break; + } + + if (file_priv_reqd) { + int space_remaining; + + /* Reset the bytes counter to buffer beginning */ + bytes_copy = m->bytes; + m->bytes = 0; + + err_printf(m, "\n PID GfxMem Process\n"); + err_printf(m, "%5d %8zdK ", pid_nr(file_priv_reqd->tgid), + (pid_entry.stats.phys_space_shared_proportion + + pid_entry.stats.phys_space_allocated_priv)/1024); + + space_remaining = NUM_SPACES - m->bytes - 1; + if (strlen(file_priv_reqd->process_name) > space_remaining) + file_priv_reqd->process_name[space_remaining] = '\0'; + + err_printf(m, "%s\n", file_priv_reqd->process_name); + + /* Reinstate the previous saved value of bytes counter */ + m->bytes = bytes_copy; + } else + WARN(1, "drm file corresponding to tgid:%d not found\n", + pid_nr(tgid)); + + list_for_each_entry_safe(entry, next, + &pid_entry.namefree, head) { + list_del(&entry->head); + drm_ht_remove_item(&pid_entry.namelist, + &entry->hash_item); + kfree(entry); + } + drm_ht_remove(&pid_entry.namelist); + + if (ret) + return ret; + if (m->bytes == 0 && m->err) + return m->err; + return 0; +} + +int i915_get_drm_clients_info(struct drm_i915_error_state_buf *m, + struct drm_device *dev) +{ + int ret = 0; + + /* + * Protect the access to global drm resources such as filelist. Protect + * against their removal under our noses, while in use. + */ + mutex_lock(&drm_global_mutex); + ret = i915_mutex_lock_interruptible(dev); + if (ret) { + mutex_unlock(&drm_global_mutex); + return ret; + } + + ret = __i915_get_drm_clients_info(m, dev); + + mutex_unlock(&dev->struct_mutex); + mutex_unlock(&drm_global_mutex); + + return ret; +} + +int i915_gem_get_obj_info(struct drm_i915_error_state_buf *m, + struct drm_device *dev, struct pid *tgid) +{ + int ret = 0; + + ret = i915_mutex_lock_interruptible(dev); + if (ret) + return ret; + + ret = __i915_gem_get_obj_info(m, dev, tgid); + + mutex_unlock(&dev->struct_mutex); + + return ret; +} +#endif + static bool cpu_write_needs_clflush(struct drm_i915_gem_object *obj) { if (obj->cache_dirty) @@ -1856,6 +2700,9 @@ i915_gem_mmap_ioctl(struct drm_device *dev, void *data, struct drm_i915_gem_mmap *args = data; struct drm_i915_gem_object *obj; unsigned long addr; +#if IS_ENABLED(CONFIG_DRM_I915_MEMTRACK) + int ret; +#endif if (args->flags & ~(I915_MMAP_WC)) return -EINVAL; @@ -1901,6 +2748,12 @@ i915_gem_mmap_ioctl(struct drm_device *dev, void *data, if (IS_ERR((void *)addr)) return addr; +#if IS_ENABLED(CONFIG_DRM_I915_MEMTRACK) + ret = i915_obj_insert_virt_addr(obj, addr, false, false); + if (ret) + return ret; +#endif + args->addr_ptr = (uint64_t) addr; return 0; @@ -2103,6 +2956,9 @@ vm_fault_t i915_gem_fault(struct vm_fault *vmf) (ggtt->gmadr.start + vma->node.start) >> PAGE_SHIFT, min_t(u64, vma->size, area->vm_end - area->vm_start), &ggtt->iomap); +#if IS_ENABLED(CONFIG_DRM_I915_MEMTRACK) + ret = i915_obj_insert_virt_addr(obj, (unsigned long)area->vm_start, true, true); +#endif if (ret) goto err_fence; @@ -2360,6 +3216,19 @@ i915_gem_object_truncate(struct drm_i915_gem_object *obj) shmem_truncate_range(file_inode(obj->base.filp), 0, (loff_t)-1); obj->mm.madv = __I915_MADV_PURGED; obj->mm.pages = ERR_PTR(-EFAULT); + +#if IS_ENABLED(CONFIG_DRM_I915_MEMTRACK) + /* + * Mark the object as not having backing pages, as physical space + * returned back to kernel + */ + if (obj->has_backing_pages == 1) { + struct drm_i915_private *dev_priv = obj->base.dev->dev_private; + + dev_priv->mm.phys_mem_total -= obj->base.size; + obj->has_backing_pages = 0; + } +#endif } /* Try to discard unwanted pages */ @@ -2655,6 +3524,14 @@ static int i915_gem_object_get_pages_gtt(struct drm_i915_gem_object *obj) if (i915_gem_object_needs_bit17_swizzle(obj)) i915_gem_object_do_bit_17_swizzle(obj, st); +#if IS_ENABLED(CONFIG_DRM_I915_MEMTRACK) + if (obj->has_backing_pages == 0) { + struct drm_i915_private *dev_priv = obj->base.dev->dev_private; + + dev_priv->mm.phys_mem_total += obj->base.size; + obj->has_backing_pages = 1; + } +#endif __i915_gem_object_set_pages(obj, st, sg_page_sizes); return 0; @@ -4699,6 +5576,15 @@ void i915_gem_object_init(struct drm_i915_gem_object *obj, INIT_RADIX_TREE(&obj->mm.get_page.radix, GFP_KERNEL | __GFP_NOWARN); mutex_init(&obj->mm.get_page.lock); +#if IS_ENABLED(CONFIG_DRM_I915_MEMTRACK) + /* + * Mark the object as not having backing pages, as no allocation + * for it yet + */ + obj->has_backing_pages = 0; + INIT_LIST_HEAD(&obj->pid_info); +#endif + i915_gem_info_add_obj(to_i915(obj->base.dev), obj->base.size); } @@ -4832,6 +5718,17 @@ static bool discard_backing_storage(struct drm_i915_gem_object *obj) return atomic_long_read(&obj->base.filp->f_count) == 1; } +#if IS_ENABLED(CONFIG_DRM_I915_MEMTRACK) +int +i915_gem_open_object(struct drm_gem_object *gem_obj, + struct drm_file *file_priv) +{ + struct drm_i915_gem_object *obj = to_intel_bo(gem_obj); + + return i915_gem_obj_insert_pid(obj); +} +#endif + static void __i915_gem_free_objects(struct drm_i915_private *i915, struct llist_node *freed) { @@ -4885,6 +5782,16 @@ static void __i915_gem_free_objects(struct drm_i915_private *i915, if (obj->base.import_attach) drm_prime_gem_destroy(&obj->base, NULL); +#if IS_ENABLED(CONFIG_DRM_I915_MEMTRACK) + if (!obj->stolen && (obj->has_backing_pages == 1)) { + struct drm_i915_private *dev_priv = obj->base.dev->dev_private; + + dev_priv->mm.phys_mem_total -= obj->base.size; + obj->has_backing_pages = 0; + } + i915_gem_obj_remove_all_pids(obj); +#endif + reservation_object_fini(&obj->__builtin_resv); drm_gem_object_release(&obj->base); i915_gem_info_remove_obj(i915, obj->base.size); @@ -5837,6 +6744,11 @@ void i915_gem_release(struct drm_device *dev, struct drm_file *file) struct drm_i915_file_private *file_priv = file->driver_priv; struct i915_request *request; +#if IS_ENABLED(CONFIG_DRM_I915_MEMTRACK) + i915_gem_remove_sysfs_file_entry(dev, file); + put_pid(file_priv->tgid); +#endif + /* Clean up our request list when the client is going away, so that * later retire_requests won't dereference our soon-to-be-gone * file_priv. @@ -5862,15 +6774,57 @@ int i915_gem_open(struct drm_i915_private *i915, struct drm_file *file) file_priv->dev_priv = i915; file_priv->file = file; +#if IS_ENABLED(CONFIG_DRM_I915_MEMTRACK) + rcu_read_lock(); + file_priv->tgid = get_pid(find_vpid(task_tgid_nr(current))); + rcu_read_unlock(); + + file_priv->process_name = kzalloc(PAGE_SIZE, GFP_ATOMIC); + if (!file_priv->process_name) { + ret = -ENOMEM; + goto out_free_file; + } + + ret = i915_get_pid_cmdline(current, file_priv->process_name); + if (ret) + goto out_free_name; +#endif + spin_lock_init(&file_priv->mm.lock); INIT_LIST_HEAD(&file_priv->mm.request_list); file_priv->bsd_engine = -1; file_priv->hang_timestamp = jiffies; +#if IS_ENABLED(CONFIG_DRM_I915_MEMTRACK) + intel_runtime_pm_get(i915); +#endif + ret = i915_gem_context_open(i915, file); +#if IS_ENABLED(CONFIG_DRM_I915_MEMTRACK) + if (ret) { + intel_runtime_pm_put(i915); + goto out_free_name; + } + intel_runtime_pm_put(i915); + + ret = i915_gem_create_sysfs_file_entry(&i915->drm, file); + if (ret) { + i915_gem_context_close(file); + goto out_free_name; + } + + return 0; + +out_free_name: + kfree(file_priv->process_name); +out_free_file: + put_pid(file_priv->tgid); + kfree(file_priv); +#else if (ret) kfree(file_priv); +#endif return ret; } diff --git a/drivers/gpu/drm/i915/i915_gem_object.h b/drivers/gpu/drm/i915/i915_gem_object.h index 83e5e01fa9ea..338709b6640e 100644 --- a/drivers/gpu/drm/i915/i915_gem_object.h +++ b/drivers/gpu/drm/i915/i915_gem_object.h @@ -147,6 +147,10 @@ struct drm_i915_gem_object { #define I915_BO_CACHE_COHERENT_FOR_WRITE BIT(1) unsigned int cache_dirty:1; +#if IS_ENABLED(CONFIG_DRM_I915_MEMTRACK) + unsigned int has_backing_pages:1; +#endif + /** * @read_domains: Read memory domains. * @@ -278,6 +282,10 @@ struct drm_i915_gem_object { void *gvt_info; }; +#if IS_ENABLED(CONFIG_DRM_I915_MEMTRACK) + struct list_head pid_info; +#endif + /** for phys allocated objects */ struct drm_dma_handle *phys_handle; diff --git a/drivers/gpu/drm/i915/i915_gpu_error.c b/drivers/gpu/drm/i915/i915_gpu_error.c index a262a64f5625..a803449498f8 100644 --- a/drivers/gpu/drm/i915/i915_gpu_error.c +++ b/drivers/gpu/drm/i915/i915_gpu_error.c @@ -91,6 +91,13 @@ static bool __i915_error_ok(struct drm_i915_error_state_buf *e) return true; } +#if IS_ENABLED(CONFIG_DRM_I915_MEMTRACK) +bool i915_error_ok(struct drm_i915_error_state_buf *e) +{ + return __i915_error_ok(e); +} +#endif + static bool __i915_error_seek(struct drm_i915_error_state_buf *e, unsigned len) { @@ -162,7 +169,7 @@ static void i915_error_vprintf(struct drm_i915_error_state_buf *e, __i915_error_advance(e, len); } -static void i915_error_puts(struct drm_i915_error_state_buf *e, +void i915_error_puts(struct drm_i915_error_state_buf *e, const char *str) { unsigned len; @@ -871,6 +878,22 @@ int i915_error_state_buf_init(struct drm_i915_error_state_buf *ebuf, return 0; } +#if IS_ENABLED(CONFIG_DRM_I915_MEMTRACK) +int i915_obj_state_buf_init(struct drm_i915_error_state_buf *ebuf, + size_t count) +{ + memset(ebuf, 0, sizeof(*ebuf)); + + ebuf->buf = kmalloc(count, GFP_KERNEL); + + if (ebuf->buf == NULL) + return -ENOMEM; + + ebuf->size = count; + return 0; +} +#endif + static void i915_error_object_free(struct drm_i915_error_object *obj) { int page; diff --git a/drivers/gpu/drm/i915/i915_gpu_error.h b/drivers/gpu/drm/i915/i915_gpu_error.h index 8710fb18ed74..821bed7bd375 100644 --- a/drivers/gpu/drm/i915/i915_gpu_error.h +++ b/drivers/gpu/drm/i915/i915_gpu_error.h @@ -307,6 +307,13 @@ struct drm_i915_error_state_buf { }; #if IS_ENABLED(CONFIG_DRM_I915_CAPTURE_ERROR) +#if IS_ENABLED(CONFIG_DRM_I915_MEMTRACK) +void i915_error_puts(struct drm_i915_error_state_buf *e, + const char *str); +bool i915_error_ok(struct drm_i915_error_state_buf *e); +int i915_obj_state_buf_init(struct drm_i915_error_state_buf *eb, + size_t count); +#endif __printf(2, 3) void i915_error_printf(struct drm_i915_error_state_buf *e, const char *f, ...); diff --git a/drivers/gpu/drm/i915/i915_sysfs.c b/drivers/gpu/drm/i915/i915_sysfs.c index e5e6f6bb2b05..4ff644202743 100644 --- a/drivers/gpu/drm/i915/i915_sysfs.c +++ b/drivers/gpu/drm/i915/i915_sysfs.c @@ -32,6 +32,10 @@ #include "intel_drv.h" #include "i915_drv.h" +#if IS_ENABLED(CONFIG_DRM_I915_MEMTRACK) +#include "../drm_internal.h" +#endif + static inline struct drm_i915_private *kdev_minor_to_i915(struct device *kdev) { struct drm_minor *minor = dev_get_drvdata(kdev); @@ -571,6 +575,284 @@ static void i915_teardown_error_capture(struct device *kdev) { sysfs_remove_bin_file(&kdev->kobj, &error_state_attr); } + +#if IS_ENABLED(CONFIG_DRM_I915_MEMTRACK) +#define dev_to_drm_minor(d) dev_get_drvdata((d)) + +static ssize_t i915_gem_clients_state_read(struct file *filp, + struct kobject *memtrack_kobj, + struct bin_attribute *attr, + char *buf, loff_t off, size_t count) +{ + struct kobject *kobj = memtrack_kobj->parent; + struct device *kdev = container_of(kobj, struct device, kobj); + struct drm_minor *minor = dev_to_drm_minor(kdev); + struct drm_device *dev = minor->dev; + struct drm_i915_private *dev_priv = dev->dev_private; + struct drm_i915_error_state_buf error_str; + ssize_t ret_count = 0; + int ret; + + ret = i915_error_state_buf_init(&error_str, dev_priv, count, off); + if (ret) + return ret; + + ret = i915_get_drm_clients_info(&error_str, dev); + if (ret) + goto out; + + ret_count = count < error_str.bytes ? count : error_str.bytes; + + memcpy(buf, error_str.buf, ret_count); +out: + i915_error_state_buf_release(&error_str); + + return ret ?: ret_count; +} + +#define GEM_OBJ_STAT_BUF_SIZE (4*1024) /* 4KB */ +#define GEM_OBJ_STAT_BUF_SIZE_MAX (1024*1024) /* 1MB */ + +struct i915_gem_file_attr_priv { + char tgid_str[16]; + struct pid *tgid; + struct drm_i915_error_state_buf buf; +}; + +static ssize_t i915_gem_read_objects(struct file *filp, + struct kobject *memtrack_kobj, + struct bin_attribute *attr, + char *buf, loff_t off, size_t count) +{ + struct kobject *kobj = memtrack_kobj->parent; + struct device *kdev = container_of(kobj, struct device, kobj); + struct drm_minor *minor = dev_to_drm_minor(kdev); + struct drm_device *dev = minor->dev; + struct i915_gem_file_attr_priv *attr_priv; + struct pid *tgid; + ssize_t ret_count = 0; + long bytes_available; + int ret = 0, buf_size = GEM_OBJ_STAT_BUF_SIZE; + unsigned long timeout = msecs_to_jiffies(500) + 1; + + /* + * There may arise a scenario where syfs file entry is being removed, + * and may race against sysfs read. Sysfs file remove function would + * have taken the drm_global_mutex and would wait for read to finish, + * which is again waiting to acquire drm_global_mutex, leading to + * deadlock. To avoid this, use mutex_trylock here with a timeout. + */ + while (!mutex_trylock(&drm_global_mutex) && --timeout) + schedule_timeout_killable(1); + if (timeout == 0) { + DRM_DEBUG_DRIVER("Unable to acquire drm global mutex.\n"); + return -EBUSY; + } + + if (!attr || !attr->private) { + ret = -EINVAL; + DRM_ERROR("attr | attr->private pointer is NULL\n"); + goto out; + } + attr_priv = attr->private; + tgid = attr_priv->tgid; + + if (off && !attr_priv->buf.buf) { + ret = -EINVAL; + DRM_ERROR( + "Buf not allocated during read with non-zero offset\n"); + goto out; + } + + if (off == 0) { +retry: + if (!attr_priv->buf.buf) { + ret = i915_obj_state_buf_init(&attr_priv->buf, + buf_size); + if (ret) { + DRM_ERROR( + "obj state buf init failed. buf_size=%d\n", + buf_size); + goto out; + } + } else { + /* Reset the buf parameters before filling data */ + attr_priv->buf.pos = 0; + attr_priv->buf.bytes = 0; + } + + /* Read the gfx device stats */ + ret = i915_gem_get_obj_info(&attr_priv->buf, dev, tgid); + if (ret) + goto out; + + ret = i915_error_ok(&attr_priv->buf); + if (ret) { + ret = 0; + goto copy_data; + } + if (buf_size >= GEM_OBJ_STAT_BUF_SIZE_MAX) { + DRM_DEBUG_DRIVER("obj stat buf size limit reached\n"); + ret = -ENOMEM; + goto out; + } else { + /* Try to reallocate buf of larger size */ + i915_error_state_buf_release(&attr_priv->buf); + buf_size *= 2; + + ret = i915_obj_state_buf_init(&attr_priv->buf, + buf_size); + if (ret) { + DRM_ERROR( + "obj stat buf init failed. buf_size=%d\n", + buf_size); + goto out; + } + goto retry; + } + } +copy_data: + + bytes_available = (long)attr_priv->buf.bytes - (long)off; + + if (bytes_available > 0) { + ret_count = count < bytes_available ? count : bytes_available; + memcpy(buf, attr_priv->buf.buf + off, ret_count); + } else + ret_count = 0; + +out: + mutex_unlock(&drm_global_mutex); + + return ret ?: ret_count; +} + +int i915_gem_create_sysfs_file_entry(struct drm_device *dev, + struct drm_file *file) +{ + struct drm_i915_file_private *file_priv = file->driver_priv; + struct drm_i915_private *dev_priv = dev->dev_private; + struct i915_gem_file_attr_priv *attr_priv; + struct bin_attribute *obj_attr; + struct drm_file *file_local; + int ret; + + /* + * Check for multiple drm files having same tgid. If found, copy the + * bin attribute into the new file priv. Otherwise allocate a new + * copy of bin attribute, and create its corresponding sysfs file. + */ + mutex_lock(&dev->struct_mutex); + list_for_each_entry(file_local, &dev->filelist, lhead) { + struct drm_i915_file_private *file_priv_local = + file_local->driver_priv; + + if (file_priv->tgid == file_priv_local->tgid) { + file_priv->obj_attr = file_priv_local->obj_attr; + mutex_unlock(&dev->struct_mutex); + return 0; + } + } + mutex_unlock(&dev->struct_mutex); + + obj_attr = kzalloc(sizeof(*obj_attr), GFP_KERNEL); + if (!obj_attr) { + DRM_ERROR("Alloc failed. Out of memory\n"); + ret = -ENOMEM; + goto out; + } + + attr_priv = kzalloc(sizeof(*attr_priv), GFP_KERNEL); + if (!attr_priv) { + DRM_ERROR("Alloc failed. Out of memory\n"); + ret = -ENOMEM; + goto out_obj_attr; + } + + snprintf(attr_priv->tgid_str, 16, "%d", task_tgid_nr(current)); + obj_attr->attr.name = attr_priv->tgid_str; + obj_attr->attr.mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH; + obj_attr->size = 0; + obj_attr->read = i915_gem_read_objects; + + attr_priv->tgid = file_priv->tgid; + obj_attr->private = attr_priv; + + ret = sysfs_create_bin_file(&dev_priv->memtrack_kobj, + obj_attr); + if (ret) { + DRM_ERROR( + "sysfs tgid file setup failed. tgid=%d, process:%s, ret:%d\n", + pid_nr(file_priv->tgid), file_priv->process_name, ret); + + goto out_attr_priv; + } + + file_priv->obj_attr = obj_attr; + return 0; + +out_attr_priv: + kfree(attr_priv); +out_obj_attr: + kfree(obj_attr); +out: + return ret; +} + +void i915_gem_remove_sysfs_file_entry(struct drm_device *dev, + struct drm_file *file) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + struct drm_i915_file_private *file_priv = file->driver_priv; + struct drm_file *file_local; + int open_count = 1; + + /* + * The current drm file instance is already removed from filelist at + * this point. + * Check if this particular drm file being removed is the last one for + * that particular tgid, and no other instances for this tgid exist in + * the filelist. If so, remove the corresponding sysfs file entry also. + */ + list_for_each_entry(file_local, &dev->filelist, lhead) { + struct drm_i915_file_private *file_priv_local = + file_local->driver_priv; + + if (pid_nr(file_priv->tgid) == pid_nr(file_priv_local->tgid)) + open_count++; + } + + if (open_count == 1) { + struct i915_gem_file_attr_priv *attr_priv; + + if (WARN_ON(file_priv->obj_attr == NULL)) + return; + attr_priv = file_priv->obj_attr->private; + + sysfs_remove_bin_file(&dev_priv->memtrack_kobj, + file_priv->obj_attr); + + i915_error_state_buf_release(&attr_priv->buf); + kfree(file_priv->obj_attr->private); + kfree(file_priv->obj_attr); + } +} + +static struct bin_attribute i915_gem_client_state_attr = { + .attr.name = "i915_gem_meminfo", + .attr.mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH, + .size = 0, + .read = i915_gem_clients_state_read, +}; + +static struct attribute *memtrack_kobj_attrs[] = {NULL}; + +static struct kobj_type memtrack_kobj_type = { + .release = NULL, + .sysfs_ops = NULL, + .default_attrs = memtrack_kobj_attrs, +}; +#endif #else static void i915_setup_error_capture(struct device *kdev) {} static void i915_teardown_error_capture(struct device *kdev) {} @@ -623,6 +905,28 @@ void i915_setup_sysfs(struct drm_i915_private *dev_priv) DRM_ERROR("RPS sysfs setup failed\n"); i915_setup_error_capture(kdev); + +#if IS_ENABLED(CONFIG_DRM_I915_MEMTRACK) + /* + * Create the gfx_memtrack directory for memtrack sysfs files + */ + ret = kobject_init_and_add( + &dev_priv->memtrack_kobj, &memtrack_kobj_type, + &kdev->kobj, "gfx_memtrack"); + if (unlikely(ret != 0)) { + DRM_ERROR( + "i915 sysfs setup memtrack directory failed\n" + ); + kobject_put(&dev_priv->memtrack_kobj); + } else { + ret = sysfs_create_bin_file(&dev_priv->memtrack_kobj, + &i915_gem_client_state_attr); + if (ret) + DRM_ERROR( + "i915_gem_client_state sysfs setup failed\n" + ); + } +#endif } void i915_teardown_sysfs(struct drm_i915_private *dev_priv) @@ -641,4 +945,11 @@ void i915_teardown_sysfs(struct drm_i915_private *dev_priv) sysfs_unmerge_group(&kdev->kobj, &rc6_attr_group); sysfs_unmerge_group(&kdev->kobj, &rc6p_attr_group); #endif + +#if IS_ENABLED(CONFIG_DRM_I915_MEMTRACK) + sysfs_remove_bin_file(&dev_priv->memtrack_kobj, + &i915_gem_client_state_attr); + kobject_del(&dev_priv->memtrack_kobj); + kobject_put(&dev_priv->memtrack_kobj); +#endif } From 8ead3514436124c7208640cfbc6813b968f657b2 Mon Sep 17 00:00:00 2001 From: "Romli, Khairul Anuar" Date: Wed, 19 Sep 2018 14:17:45 +0800 Subject: [PATCH 1033/1103] drm/i915: Async work for hdcp authentication Each HDCP authentication, could take upto 5.1Sec, based on the downstream HDCP topology. Hence to avoid this much delay in the atomic_commit path, this patch schedules the HDCP authentication into a asynchronous work. Change-Id: I951f20d9db082c80c4323495c4cf290a6eed1238 Signed-off-by: Romli, Khairul Anuar --- drivers/gpu/drm/i915/intel_display.c | 1 + drivers/gpu/drm/i915/intel_drv.h | 1 + drivers/gpu/drm/i915/intel_hdcp.c | 36 +++++++++++++++++----------- 3 files changed, 24 insertions(+), 14 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index d2951096bca0..9507d9086691 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -15925,6 +15925,7 @@ static void intel_hpd_poll_fini(struct drm_device *dev) if (connector->hdcp_shim) { cancel_delayed_work_sync(&connector->hdcp_check_work); cancel_work_sync(&connector->hdcp_prop_work); + cancel_work_sync(&connector->hdcp_enable_work); } } drm_connector_list_iter_end(&conn_iter); diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h index 8fc61e96754f..25807569ec34 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h @@ -418,6 +418,7 @@ struct intel_connector { uint64_t hdcp_value; /* protected by hdcp_mutex */ struct delayed_work hdcp_check_work; struct work_struct hdcp_prop_work; + struct work_struct hdcp_enable_work; }; struct intel_digital_connector_state { diff --git a/drivers/gpu/drm/i915/intel_hdcp.c b/drivers/gpu/drm/i915/intel_hdcp.c index 0cc6a861bcf8..ea938b9146b0 100644 --- a/drivers/gpu/drm/i915/intel_hdcp.c +++ b/drivers/gpu/drm/i915/intel_hdcp.c @@ -679,8 +679,14 @@ static int _intel_hdcp_enable(struct intel_connector *connector) for (i = 0; i < tries; i++) { ret = intel_hdcp_auth(conn_to_dig_port(connector), connector->hdcp_shim); - if (!ret) + if (!ret) { + connector->hdcp_value = + DRM_MODE_CONTENT_PROTECTION_ENABLED; + schedule_work(&connector->hdcp_prop_work); + schedule_delayed_work(&connector->hdcp_check_work, + DRM_HDCP_CHECK_PERIOD_MS); return 0; + } DRM_DEBUG_KMS("HDCP Auth failure (%d)\n", ret); @@ -692,6 +698,17 @@ static int _intel_hdcp_enable(struct intel_connector *connector) return ret; } +static void intel_hdcp_enable_work(struct work_struct *work) +{ + struct intel_connector *connector = container_of(work, + struct intel_connector, + hdcp_enable_work); + + mutex_lock(&connector->hdcp_mutex); + _intel_hdcp_enable(connector); + mutex_unlock(&connector->hdcp_mutex); +} + static void intel_hdcp_check_work(struct work_struct *work) { struct intel_connector *connector = container_of(to_delayed_work(work), @@ -748,29 +765,20 @@ int intel_hdcp_init(struct intel_connector *connector, mutex_init(&connector->hdcp_mutex); INIT_DELAYED_WORK(&connector->hdcp_check_work, intel_hdcp_check_work); INIT_WORK(&connector->hdcp_prop_work, intel_hdcp_prop_work); + INIT_WORK(&connector->hdcp_enable_work, intel_hdcp_enable_work); return 0; } int intel_hdcp_enable(struct intel_connector *connector) { - int ret; - if (!connector->hdcp_shim) return -ENOENT; mutex_lock(&connector->hdcp_mutex); - - ret = _intel_hdcp_enable(connector); - if (ret) - goto out; - - connector->hdcp_value = DRM_MODE_CONTENT_PROTECTION_ENABLED; - schedule_work(&connector->hdcp_prop_work); - schedule_delayed_work(&connector->hdcp_check_work, - DRM_HDCP_CHECK_PERIOD_MS); -out: + schedule_work(&connector->hdcp_enable_work); mutex_unlock(&connector->hdcp_mutex); - return ret; + + return 0; } int intel_hdcp_disable(struct intel_connector *connector) From 60b649a423a0bfdba4b70f88c1b459b4c7bbad42 Mon Sep 17 00:00:00 2001 From: "Romli, Khairul Anuar" Date: Wed, 19 Sep 2018 14:18:50 +0800 Subject: [PATCH 1034/1103] drm/i915: Commit CP without modeset This patch commits the content protection change of a connector, without crtc modeset. This will give better user experience. Daniel vetter and Ville has mentioned that ville is developing some alternate approach for this kind of connector related commits. Till then it is preferred to have this solution in place. Change-Id: I95fd78f0c45851fda1453ba2d94ee634ee29bf2f Signed-off-by: Romli, Khairul Anuar --- drivers/gpu/drm/i915/intel_ddi.c | 7 ------ drivers/gpu/drm/i915/intel_display.c | 10 +++++++++ drivers/gpu/drm/i915/intel_drv.h | 7 ++++-- drivers/gpu/drm/i915/intel_hdcp.c | 32 ++++++++++++++++++++++++---- 4 files changed, 43 insertions(+), 13 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_ddi.c b/drivers/gpu/drm/i915/intel_ddi.c index c9af34861d9e..4c81548df7fa 100644 --- a/drivers/gpu/drm/i915/intel_ddi.c +++ b/drivers/gpu/drm/i915/intel_ddi.c @@ -3009,11 +3009,6 @@ static void intel_enable_ddi(struct intel_encoder *encoder, intel_enable_ddi_hdmi(encoder, crtc_state, conn_state); else intel_enable_ddi_dp(encoder, crtc_state, conn_state); - - /* Enable hdcp if it's desired */ - if (conn_state->content_protection == - DRM_MODE_CONTENT_PROTECTION_DESIRED) - intel_hdcp_enable(to_intel_connector(conn_state->connector)); } static void intel_disable_ddi_dp(struct intel_encoder *encoder, @@ -3053,8 +3048,6 @@ static void intel_disable_ddi(struct intel_encoder *encoder, const struct intel_crtc_state *old_crtc_state, const struct drm_connector_state *old_conn_state) { - intel_hdcp_disable(to_intel_connector(old_conn_state->connector)); - if (intel_crtc_has_type(old_crtc_state, INTEL_OUTPUT_HDMI)) intel_disable_ddi_hdmi(encoder, old_crtc_state, old_conn_state); else diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 9507d9086691..b3c0e61bf612 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -12580,6 +12580,8 @@ static void intel_atomic_commit_tail(struct drm_atomic_state *state) struct intel_atomic_state *intel_state = to_intel_atomic_state(state); struct drm_i915_private *dev_priv = to_i915(dev); struct drm_crtc_state *old_crtc_state, *new_crtc_state; + struct drm_connector_state *old_conn_state, *new_conn_state; + struct drm_connector *connector; struct drm_crtc *crtc; struct intel_crtc_state *intel_cstate; u64 put_domains[I915_MAX_PIPES] = {}; @@ -12677,9 +12679,17 @@ static void intel_atomic_commit_tail(struct drm_atomic_state *state) } } + for_each_oldnew_connector_in_state(state, connector, old_conn_state, + new_conn_state, i) + intel_hdcp_atomic_pre_commit(connector, old_conn_state, + new_conn_state); + /* Now enable the clocks, plane, pipe, and connectors that we set up. */ dev_priv->display.update_crtcs(state); + for_each_new_connector_in_state(state, connector, new_conn_state, i) + intel_hdcp_atomic_commit(connector, new_conn_state); + /* FIXME: We should call drm_atomic_helper_commit_hw_done() here * already, but still need the state for the delayed optimization. To * fix this: diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h index 25807569ec34..f20ce3b3545a 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h @@ -1917,10 +1917,13 @@ static inline void intel_backlight_device_unregister(struct intel_connector *con void intel_hdcp_atomic_check(struct drm_connector *connector, struct drm_connector_state *old_state, struct drm_connector_state *new_state); +void intel_hdcp_atomic_pre_commit(struct drm_connector *connector, + struct drm_connector_state *old_state, + struct drm_connector_state *new_state); +void intel_hdcp_atomic_commit(struct drm_connector *connector, + struct drm_connector_state *new_state); int intel_hdcp_init(struct intel_connector *connector, const struct intel_hdcp_shim *hdcp_shim); -int intel_hdcp_enable(struct intel_connector *connector); -int intel_hdcp_disable(struct intel_connector *connector); int intel_hdcp_check_link(struct intel_connector *connector); bool is_hdcp_supported(struct drm_i915_private *dev_priv, enum port port); diff --git a/drivers/gpu/drm/i915/intel_hdcp.c b/drivers/gpu/drm/i915/intel_hdcp.c index ea938b9146b0..7f87667753b3 100644 --- a/drivers/gpu/drm/i915/intel_hdcp.c +++ b/drivers/gpu/drm/i915/intel_hdcp.c @@ -806,7 +806,6 @@ void intel_hdcp_atomic_check(struct drm_connector *connector, { uint64_t old_cp = old_state->content_protection; uint64_t new_cp = new_state->content_protection; - struct drm_crtc_state *crtc_state; if (!new_state->crtc) { /* @@ -827,10 +826,35 @@ void intel_hdcp_atomic_check(struct drm_connector *connector, (old_cp == DRM_MODE_CONTENT_PROTECTION_DESIRED && new_cp == DRM_MODE_CONTENT_PROTECTION_ENABLED)) return; +} + +void intel_hdcp_atomic_pre_commit(struct drm_connector *connector, + struct drm_connector_state *old_state, + struct drm_connector_state *new_state) +{ + uint64_t old_cp = old_state->content_protection; + uint64_t new_cp = new_state->content_protection; + + /* + * Disable HDCP if the connector is becoming disabled, or if requested + * via the property. + */ + if ((!new_state->crtc && + old_cp != DRM_MODE_CONTENT_PROTECTION_UNDESIRED) || + (new_state->crtc && + old_cp != DRM_MODE_CONTENT_PROTECTION_UNDESIRED && + new_cp == DRM_MODE_CONTENT_PROTECTION_UNDESIRED)) + intel_hdcp_disable(to_intel_connector(connector)); +} + +void intel_hdcp_atomic_commit(struct drm_connector *connector, + struct drm_connector_state *new_state) +{ + uint64_t new_cp = new_state->content_protection; - crtc_state = drm_atomic_get_new_crtc_state(new_state->state, - new_state->crtc); - crtc_state->mode_changed = true; + /* Enable hdcp if it's desired */ + if (new_state->crtc && new_cp == DRM_MODE_CONTENT_PROTECTION_DESIRED) + intel_hdcp_enable(to_intel_connector(connector)); } /* Implements Part 3 of the HDCP authorization procedure */ From ac19fbd106ad41a25b133f180a5e575bf8598a48 Mon Sep 17 00:00:00 2001 From: Badiuzzaman Iskhandar Date: Tue, 9 Oct 2018 15:51:18 +0800 Subject: [PATCH 1035/1103] MUST_REBASE [IOTG]: drm/i915: Allow late GuC/HuC loading This patch allows for the GuC and HuC to be loaded until the first device is open (instead of during the driver initialisation). Delaying the fw load prevents a load failure when the partition that the firmware is supposed to be on has not been mounted yet, as is the case in Android. During i915 init, if huc/guc enabled, the huc/guc fw sizes need to be known. As we defer the fw fetch later, the fw sizes can't be determined. ggtt_pin_bias relies on the fw size as well. MUST_REBASE: The hw_late code is part of a solution for loading firmware of a file system that has not been mounted yet. Only a problem on Android. This patch is a rewrote from similar patch in 4.11 Change-Id: Ic4f414deed9961d73c25434b3d8698ae9fbb425c Tracked-On: Signed-off-by: Badiuzzaman Iskhandar --- drivers/gpu/drm/i915/i915_drv.h | 3 ++ drivers/gpu/drm/i915/i915_gem.c | 48 ++++++++++++++++++++----- drivers/gpu/drm/i915/i915_gem_context.c | 44 +++++++++++++++++++---- drivers/gpu/drm/i915/intel_wopcm.c | 7 ++++ 4 files changed, 88 insertions(+), 14 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index c2791ebd3fca..bf711b1e13eb 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -2137,6 +2137,8 @@ struct drm_i915_private { struct i915_pmu pmu; + bool contexts_ready; /* for deferred initialization */ + /* * NOTE: This is the dri1/ums dungeon, don't add stuff here. Your patch * will be rejected. Instead look for a better place. @@ -3174,6 +3176,7 @@ void i915_gem_reset_engine(struct intel_engine_cs *engine, void i915_gem_init_mmio(struct drm_i915_private *i915); int __must_check i915_gem_init(struct drm_i915_private *dev_priv); int __must_check i915_gem_init_hw(struct drm_i915_private *dev_priv); +int __must_check i915_gem_init_hw_late(struct drm_i915_private *dev_priv); void i915_gem_init_swizzling(struct drm_i915_private *dev_priv); void i915_gem_fini(struct drm_i915_private *dev_priv); void i915_gem_cleanup_engines(struct drm_i915_private *dev_priv); diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index 4b31093239b2..b3e4923129a3 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -6161,6 +6161,28 @@ static int __i915_gem_restart_engines(void *data) return 0; } +int i915_gem_init_hw_late(struct drm_i915_private *dev_priv) +{ + int ret; + + /* + * Place for things that can be delayed until the first context + * is open. For example, fw loading in android. + */ + + /* fetch firmware */ + intel_uc_init_misc(dev_priv); + + /* Load fw. We can't enable contexts until all firmware is loaded */ + ret = intel_uc_init_hw(dev_priv); + if (ret) { + DRM_ERROR("Late init: enabling uc failed (%d)\n", ret); + return ret; + } + + return 0; +} + int i915_gem_init_hw(struct drm_i915_private *dev_priv) { int ret; @@ -6219,11 +6241,17 @@ int i915_gem_init_hw(struct drm_i915_private *dev_priv) goto out; } - /* We can't enable contexts until all firmware is loaded */ - ret = intel_uc_init_hw(dev_priv); - if (ret) { - DRM_ERROR("Enabling uc failed (%d)\n", ret); - goto out; + /* + * Don't call i915_gem_init_hw_late() the very first time (during + * driver load); it will get called during first open instead. + * It should only be called on subsequent (re-initialization) passes. + */ + if (dev_priv->contexts_ready) { + ret = i915_gem_init_hw_late(dev_priv); + if (ret) + goto out; + } else { + DRM_DEBUG_DRIVER("Deferring late initialization\n"); } intel_mocs_init_l3cc_table(dev_priv); @@ -6386,9 +6414,13 @@ int i915_gem_init(struct drm_i915_private *dev_priv) if (ret) return ret; - ret = intel_uc_init_misc(dev_priv); - if (ret) - return ret; + /* + * ANDROID: fetch fw during drm_open instead + * due to filesystem is not up yet during driver init + * ret = intel_uc_init_misc(dev_priv); + * if (ret) + * return ret; + */ ret = intel_wopcm_init(&dev_priv->wopcm); if (ret) diff --git a/drivers/gpu/drm/i915/i915_gem_context.c b/drivers/gpu/drm/i915/i915_gem_context.c index b10770cfccd2..e703779a503e 100644 --- a/drivers/gpu/drm/i915/i915_gem_context.c +++ b/drivers/gpu/drm/i915/i915_gem_context.c @@ -548,23 +548,55 @@ static int context_idr_cleanup(int id, void *p, void *data) return 0; } +int i915_gem_context_first_open(struct drm_i915_private *dev_priv) +{ + int ret; + + lockdep_assert_held(&dev_priv->drm.struct_mutex); + + DRM_DEBUG_DRIVER("Late initialization starting\n"); + + intel_runtime_pm_get(dev_priv); + intel_uncore_forcewake_get(dev_priv, FORCEWAKE_ALL); + + ret = i915_gem_init_hw_late(dev_priv); + if (ret == 0) + dev_priv->contexts_ready = true; + else + DRM_ERROR("Late initialization failed: %d\n", ret); + + intel_uncore_forcewake_put(dev_priv, FORCEWAKE_ALL); + intel_runtime_pm_put(dev_priv); + + return ret; +} + int i915_gem_context_open(struct drm_i915_private *i915, struct drm_file *file) { struct drm_i915_file_private *file_priv = file->driver_priv; struct i915_gem_context *ctx; + int ret = 0; idr_init(&file_priv->context_idr); mutex_lock(&i915->drm.struct_mutex); - ctx = i915_gem_create_context(i915, file_priv); - mutex_unlock(&i915->drm.struct_mutex); - if (IS_ERR(ctx)) { - idr_destroy(&file_priv->context_idr); - return PTR_ERR(ctx); + + if (!(i915->contexts_ready)) + ret = i915_gem_context_first_open(i915); + + if (ret == 0) { + ctx = i915_gem_create_context(i915, file_priv); + if (IS_ERR(ctx)) + ret = PTR_ERR(ctx); + + GEM_BUG_ON(i915_gem_context_is_kernel(ctx)); } - GEM_BUG_ON(i915_gem_context_is_kernel(ctx)); + mutex_unlock(&i915->drm.struct_mutex); + + if (ret) + idr_destroy(&file_priv->context_idr); return 0; } diff --git a/drivers/gpu/drm/i915/intel_wopcm.c b/drivers/gpu/drm/i915/intel_wopcm.c index 74bf76f3fddc..5ff877ed7b12 100644 --- a/drivers/gpu/drm/i915/intel_wopcm.c +++ b/drivers/gpu/drm/i915/intel_wopcm.c @@ -207,6 +207,13 @@ int intel_wopcm_init(struct intel_wopcm *wopcm) wopcm->guc.base = guc_wopcm_base; wopcm->guc.size = guc_wopcm_size; + /* + * In deferred fw loading, we defer the intel_guc_init which will + * initialize the guc.ggtt_pin_bias. As it relies on wopcm size, + * set the ggtt_pin_bias after wopcm initialization + */ + i915->guc.ggtt_pin_bias = i915->wopcm.size - i915->wopcm.guc.base; + return 0; } From 52df8299481c45a18c7e589f59a6efe4e581f8e7 Mon Sep 17 00:00:00 2001 From: "Romli, Khairul Anuar" Date: Tue, 9 Oct 2018 19:02:37 +0800 Subject: [PATCH 1036/1103] drm/i915: Passing the intel_connector to HDCP auth Reference of Intel_connector is needed at generic authentication flow for upcoming revocation check and downstream topology info gathering etc. As a preparation for storing the downstream topology information at intel_connector and accessig SRM revocated ID list from intel_connector, we are passing the intel_connector reference to hdcp authentication. Change-Id: I2cb3608ed1d3b408e3531fe15687a01f78d716b0 Signed-off-by: Ramalingam C Signed-off-by: Romli, Khairul Anuar --- drivers/gpu/drm/i915/intel_hdcp.c | 143 ++++++++++++------------------ 1 file changed, 57 insertions(+), 86 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_hdcp.c b/drivers/gpu/drm/i915/intel_hdcp.c index 7f87667753b3..a03a745544e2 100644 --- a/drivers/gpu/drm/i915/intel_hdcp.c +++ b/drivers/gpu/drm/i915/intel_hdcp.c @@ -179,16 +179,60 @@ bool intel_hdcp_is_ksv_valid(u8 *ksv) return true; } +struct intel_digital_port *conn_to_dig_port(struct intel_connector *connector) +{ + return enc_to_dig_port(&intel_attached_encoder(&connector->base)->base); +} + +/* Implements Part 2 of the HDCP authorization procedure */ static -int intel_hdcp_validate_v_prime(struct intel_digital_port *intel_dig_port, - const struct intel_hdcp_shim *shim, - u8 *ksv_fifo, u8 num_downstream, u8 *bstatus) +int intel_hdcp_auth_downstream(struct intel_connector *connector) { - struct drm_i915_private *dev_priv; + struct intel_digital_port *intel_dig_port = + conn_to_dig_port(connector); + const struct intel_hdcp_shim *shim = connector->hdcp_shim; + struct drm_i915_private *dev_priv = to_i915(connector->base.dev); u32 vprime, sha_text, sha_leftovers, rep_ctl; + u8 bstatus[2], num_downstream, *ksv_fifo; int ret, i, j, sha_idx; - dev_priv = intel_dig_port->base.base.dev->dev_private; + if(intel_dig_port == NULL) + return EINVAL; + + ret = intel_hdcp_poll_ksv_fifo(intel_dig_port, shim); + if (ret) { + DRM_ERROR("KSV list failed to become ready (%d)\n", ret); + return ret; + } + + ret = shim->read_bstatus(intel_dig_port, bstatus); + if (ret) + return ret; + + if (DRM_HDCP_MAX_DEVICE_EXCEEDED(bstatus[0]) || + DRM_HDCP_MAX_CASCADE_EXCEEDED(bstatus[1])) { + DRM_ERROR("Max Topology Limit Exceeded\n"); + return -EPERM; + } + + /* + * When repeater reports 0 device count, HDCP1.4 spec allows disabling + * the HDCP encryption. That implies that repeater can't have its own + * display. As there is no consumption of encrypted content in the + * repeater with 0 downstream devices, we are failing the + * authentication. + */ + num_downstream = DRM_HDCP_NUM_DOWNSTREAM(bstatus[0]); + if (num_downstream == 0) + return -EINVAL; + + ksv_fifo = kzalloc(num_downstream * DRM_HDCP_KSV_LEN, GFP_KERNEL); + if (!ksv_fifo) + return -ENOMEM; + + ret = shim->read_ksv_fifo(intel_dig_port, num_downstream, ksv_fifo); + if (ret) + return ret; /* Process V' values from the receiver */ for (i = 0; i < DRM_HDCP_V_PRIME_NUM_PARTS; i++) { @@ -394,79 +438,12 @@ int intel_hdcp_validate_v_prime(struct intel_digital_port *intel_dig_port, return 0; } -/* Implements Part 2 of the HDCP authorization procedure */ -static -int intel_hdcp_auth_downstream(struct intel_digital_port *intel_dig_port, - const struct intel_hdcp_shim *shim) -{ - u8 bstatus[2], num_downstream, *ksv_fifo; - int ret, i, tries = 3; - - ret = intel_hdcp_poll_ksv_fifo(intel_dig_port, shim); - if (ret) { - DRM_ERROR("KSV list failed to become ready (%d)\n", ret); - return ret; - } - - ret = shim->read_bstatus(intel_dig_port, bstatus); - if (ret) - return ret; - - if (DRM_HDCP_MAX_DEVICE_EXCEEDED(bstatus[0]) || - DRM_HDCP_MAX_CASCADE_EXCEEDED(bstatus[1])) { - DRM_ERROR("Max Topology Limit Exceeded\n"); - return -EPERM; - } - - /* - * When repeater reports 0 device count, HDCP1.4 spec allows disabling - * the HDCP encryption. That implies that repeater can't have its own - * display. As there is no consumption of encrypted content in the - * repeater with 0 downstream devices, we are failing the - * authentication. - */ - num_downstream = DRM_HDCP_NUM_DOWNSTREAM(bstatus[0]); - if (num_downstream == 0) - return -EINVAL; - - ksv_fifo = kcalloc(DRM_HDCP_KSV_LEN, num_downstream, GFP_KERNEL); - if (!ksv_fifo) - return -ENOMEM; - - ret = shim->read_ksv_fifo(intel_dig_port, num_downstream, ksv_fifo); - if (ret) - goto err; - - /* - * When V prime mismatches, DP Spec mandates re-read of - * V prime atleast twice. - */ - for (i = 0; i < tries; i++) { - ret = intel_hdcp_validate_v_prime(intel_dig_port, shim, - ksv_fifo, num_downstream, - bstatus); - if (!ret) - break; - } - - if (i == tries) { - DRM_ERROR("V Prime validation failed.(%d)\n", ret); - goto err; - } - - DRM_DEBUG_KMS("HDCP is enabled (%d downstream devices)\n", - num_downstream); - ret = 0; -err: - kfree(ksv_fifo); - return ret; -} - /* Implements Part 1 of the HDCP authorization procedure */ -static int intel_hdcp_auth(struct intel_digital_port *intel_dig_port, - const struct intel_hdcp_shim *shim) +static int intel_hdcp_auth(struct intel_connector *connector) { - struct drm_i915_private *dev_priv; + struct intel_digital_port *intel_dig_port = conn_to_dig_port(connector); + const struct intel_hdcp_shim *shim = connector->hdcp_shim; + struct drm_i915_private *dev_priv = to_i915(connector->base.dev); enum port port; unsigned long r0_prime_gen_start; int ret, i, tries = 2; @@ -484,7 +461,8 @@ static int intel_hdcp_auth(struct intel_digital_port *intel_dig_port, } ri; bool repeater_present, hdcp_capable; - dev_priv = intel_dig_port->base.base.dev->dev_private; + if(intel_dig_port == NULL) + return EINVAL; port = intel_dig_port->base.port; @@ -612,18 +590,12 @@ static int intel_hdcp_auth(struct intel_digital_port *intel_dig_port, */ if (repeater_present) - return intel_hdcp_auth_downstream(intel_dig_port, shim); + return intel_hdcp_auth_downstream(connector); DRM_DEBUG_KMS("HDCP is enabled (no repeater present)\n"); return 0; } -static -struct intel_digital_port *conn_to_dig_port(struct intel_connector *connector) -{ - return enc_to_dig_port(&intel_attached_encoder(&connector->base)->base); -} - static int _intel_hdcp_disable(struct intel_connector *connector) { struct drm_i915_private *dev_priv = connector->base.dev->dev_private; @@ -677,8 +649,7 @@ static int _intel_hdcp_enable(struct intel_connector *connector) /* Incase of authentication failures, HDCP spec expects reauth. */ for (i = 0; i < tries; i++) { - ret = intel_hdcp_auth(conn_to_dig_port(connector), - connector->hdcp_shim); + ret = intel_hdcp_auth(connector); if (!ret) { connector->hdcp_value = DRM_MODE_CONTENT_PROTECTION_ENABLED; From cf14b1e5123d22ff66e2f638d7485b590c395f24 Mon Sep 17 00:00:00 2001 From: "Romli, Khairul Anuar" Date: Wed, 13 Dec 2017 23:23:08 +0530 Subject: [PATCH 1037/1103] drm: Add CP downstream_info property This patch adds a optional CP downstream info blob property to the connectors. This enables the Userspace to read the information of HDCP authenticated downstream topology. Driver will updated this blob with all downstream information at the end of the authentication. In case userspace configures this platform as repeater, then this information is needed for the authentication with upstream HDCP transmitter. Change-Id: Ic04109af286c0ab30f8a2ae3585700a7218e43fe Signed-off-by: Ramalingam C Signed-off-by: Romli, Khairul Anuar --- drivers/gpu/drm/drm_atomic.c | 8 ++-- drivers/gpu/drm/drm_connector.c | 69 +++++++++++++++++++++++++++++++++ include/drm/drm_connector.h | 19 +++++++-- include/uapi/drm/drm_mode.h | 28 +++++++++++++ 4 files changed, 115 insertions(+), 9 deletions(-) diff --git a/drivers/gpu/drm/drm_atomic.c b/drivers/gpu/drm/drm_atomic.c index 018fcdb353d2..cd2db45d074c 100644 --- a/drivers/gpu/drm/drm_atomic.c +++ b/drivers/gpu/drm/drm_atomic.c @@ -1506,11 +1506,9 @@ drm_atomic_connector_get_property(struct drm_connector *connector, *val = state->scaling_mode; } else if (property == connector->content_protection_property) { *val = state->content_protection; - } else if (property == config->writeback_fb_id_property) { - /* Writeback framebuffer is one-shot, write and forget */ - *val = 0; - } else if (property == config->writeback_out_fence_ptr_property) { - *val = 0; + } else if (property == connector->cp_downstream_property) { + *val = connector->cp_downstream_blob_ptr ? + connector->cp_downstream_blob_ptr->base.id : 0; } else if (connector->funcs->atomic_get_property) { return connector->funcs->atomic_get_property(connector, state, property, val); diff --git a/drivers/gpu/drm/drm_connector.c b/drivers/gpu/drm/drm_connector.c index 6011d769d50b..8b21a017edaa 100644 --- a/drivers/gpu/drm/drm_connector.c +++ b/drivers/gpu/drm/drm_connector.c @@ -241,6 +241,7 @@ int drm_connector_init(struct drm_device *dev, INIT_LIST_HEAD(&connector->modes); mutex_init(&connector->mutex); connector->edid_blob_ptr = NULL; + connector->cp_downstream_blob_ptr = NULL; connector->status = connector_status_unknown; connector->display_info.panel_orientation = DRM_MODE_PANEL_ORIENTATION_UNKNOWN; @@ -1346,6 +1347,42 @@ int drm_connector_attach_content_protection_property( } EXPORT_SYMBOL(drm_connector_attach_content_protection_property); +/** + * drm_connector_attach_cp_downstream_property - attach cp downstream + * property + * + * @connector: connector to attach cp downstream property on. + * + * This is used to add support for content protection downstream info on + * select connectors. when Intel platform is configured as repeater, + * this downstream info is used by userspace, to complete the repeater + * authentication of HDCP specification with upstream HDCP transmitter. + * + * The cp downstream will be set to &drm_connector_state.cp_downstream + * + * Returns: + * Zero on success, negative errno on failure. + */ +int drm_connector_attach_cp_downstream_property( + struct drm_connector *connector) +{ + struct drm_device *dev = connector->dev; + struct drm_property *prop; + + prop = drm_property_create(dev, DRM_MODE_PROP_BLOB | + DRM_MODE_PROP_IMMUTABLE, + "CP_Downstream_Info", 0); + if (!prop) + return -ENOMEM; + + drm_object_attach_property(&connector->base, prop, 0); + + connector->cp_downstream_property = prop; + + return 0; +} +EXPORT_SYMBOL(drm_connector_attach_cp_downstream_property); + /** * drm_mode_create_aspect_ratio_property - create aspect ratio property * @dev: DRM device @@ -1578,6 +1615,38 @@ void drm_connector_set_link_status_property(struct drm_connector *connector, } EXPORT_SYMBOL(drm_connector_set_link_status_property); +/** + * drm_mode_connector_update_cp_downstream_property - update the cp_downstream + * property of a connector + * @connector: drm connector + * @cp_downstream_info: new value of the cp_downstream property + * + * This function creates a new blob modeset object and assigns its id to the + * connector's cp_downstream property. + * + * Returns: + * Zero on success, negative errno on failure. + */ +int drm_mode_connector_update_cp_downstream_property( + struct drm_connector *connector, + const struct cp_downstream_info *info) +{ + struct drm_device *dev = connector->dev; + int ret; + + if (!info) + return -EINVAL; + + ret = drm_property_replace_global_blob(dev, + &connector->cp_downstream_blob_ptr, + sizeof(struct cp_downstream_info), + info, + &connector->base, + connector->cp_downstream_property); + return ret; +} +EXPORT_SYMBOL(drm_mode_connector_update_cp_downstream_property); + /** * drm_connector_init_panel_orientation_property - * initialize the connecters panel_orientation property diff --git a/include/drm/drm_connector.h b/include/drm/drm_connector.h index 97ea41dc678f..9fd96c96d598 100644 --- a/include/drm/drm_connector.h +++ b/include/drm/drm_connector.h @@ -915,6 +915,13 @@ struct drm_connector { */ struct drm_property *content_protection_property; + /** + * @cp_downstream_property: DRM BLOB property for content + * protection downstream information. + */ + struct drm_property *cp_downstream_property; + struct drm_property_blob *cp_downstream_blob_ptr; + /** * @path_blob_ptr: * @@ -1184,20 +1191,24 @@ int drm_connector_attach_scaling_mode_property(struct drm_connector *connector, u32 scaling_mode_mask); int drm_connector_attach_content_protection_property( struct drm_connector *connector); +int drm_connector_attach_cp_downstream_property( + struct drm_connector *connector); int drm_mode_create_aspect_ratio_property(struct drm_device *dev); int drm_mode_create_content_type_property(struct drm_device *dev); void drm_hdmi_avi_infoframe_content_type(struct hdmi_avi_infoframe *frame, const struct drm_connector_state *conn_state); - int drm_mode_create_suggested_offset_properties(struct drm_device *dev); int drm_connector_set_path_property(struct drm_connector *connector, - const char *path); + const char *path); int drm_connector_set_tile_property(struct drm_connector *connector); int drm_connector_update_edid_property(struct drm_connector *connector, - const struct edid *edid); + const struct edid *edid); void drm_connector_set_link_status_property(struct drm_connector *connector, - uint64_t link_status); + uint64_t link_status); +int drm_mode_connector_update_cp_downstream_property( + struct drm_connector *connector, + const struct cp_downstream_info *info); int drm_connector_init_panel_orientation_property( struct drm_connector *connector, int width, int height); diff --git a/include/uapi/drm/drm_mode.h b/include/uapi/drm/drm_mode.h index 8d67243952f4..926d80323b55 100644 --- a/include/uapi/drm/drm_mode.h +++ b/include/uapi/drm/drm_mode.h @@ -209,6 +209,34 @@ extern "C" { #define DRM_MODE_CONTENT_PROTECTION_DESIRED 1 #define DRM_MODE_CONTENT_PROTECTION_ENABLED 2 +#define DRM_MODE_HDCP_KSV_LEN 5 +#define DRM_MODE_HDCP_MAX_DEVICE_CNT 127 + +struct cp_downstream_info { + + /* KSV of immediate HDCP Sink. In Little-Endian Format. */ + char bksv[DRM_MODE_HDCP_KSV_LEN]; + + /* Whether Immediate HDCP sink is a repeater? */ + bool is_repeater; + + /* Depth received from immediate downstream repeater */ + __u8 depth; + + /* Device count received from immediate downstream repeater */ + __u32 device_count; + + /* + * Max buffer required to hold ksv list received from immediate + * repeater. In this array first device_count * DRM_MODE_HDCP_KSV_LEN + * will hold the valid ksv bytes. + * If authentication specification is + * HDCP1.4 - each KSV's Bytes will be in Little-Endian format. + * HDCP2.2 - each KSV's Bytes will be in Big-Endian format. + */ + char ksv_list[DRM_MODE_HDCP_KSV_LEN * DRM_MODE_HDCP_MAX_DEVICE_CNT]; +}; + struct drm_mode_modeinfo { __u32 clock; __u16 hdisplay; From 8fe455b308bc22902de37657306330709ab02704 Mon Sep 17 00:00:00 2001 From: "Romli, Khairul Anuar" Date: Thu, 14 Dec 2017 20:42:55 +0530 Subject: [PATCH 1038/1103] drm: Add CP System Renewability Msg Property This patch adds a drm blob property to the selected connector. This property will be used to pass the SRM Blob ID from userspace to kernel. Revocated ksv list from SRM Table will be used by the kernel in the HDCP authentication. Change-Id: Ie2a09d3834386d8ba401762e1c717ea336a1193a Signed-off-by: Ramalingam C Signed-off-by: Romli, Khairul Anuar --- drivers/gpu/drm/drm_atomic.c | 15 ++++---------- drivers/gpu/drm/drm_connector.c | 35 +++++++++++++++++++++++++++++++++ include/drm/drm_connector.h | 13 ++++++++++++ 3 files changed, 52 insertions(+), 11 deletions(-) diff --git a/drivers/gpu/drm/drm_atomic.c b/drivers/gpu/drm/drm_atomic.c index cd2db45d074c..3fdb8ced9264 100644 --- a/drivers/gpu/drm/drm_atomic.c +++ b/drivers/gpu/drm/drm_atomic.c @@ -1405,17 +1405,8 @@ static int drm_atomic_connector_set_property(struct drm_connector *connector, return -EINVAL; } state->content_protection = val; - } else if (property == config->writeback_fb_id_property) { - struct drm_framebuffer *fb = drm_framebuffer_lookup(dev, NULL, val); - int ret = drm_atomic_set_writeback_fb_for_connector(state, fb); - if (fb) - drm_framebuffer_put(fb); - return ret; - } else if (property == config->writeback_out_fence_ptr_property) { - s32 __user *fence_ptr = u64_to_user_ptr(val); - - return set_out_fence_for_connector(state->state, connector, - fence_ptr); + } else if (property == connector->cp_srm_property) { + state->cp_srm_blob_id = val; } else if (connector->funcs->atomic_set_property) { return connector->funcs->atomic_set_property(connector, state, property, val); @@ -1506,6 +1497,8 @@ drm_atomic_connector_get_property(struct drm_connector *connector, *val = state->scaling_mode; } else if (property == connector->content_protection_property) { *val = state->content_protection; + } else if (property == connector->cp_srm_property) { + *val = state->cp_srm_blob_id; } else if (property == connector->cp_downstream_property) { *val = connector->cp_downstream_blob_ptr ? connector->cp_downstream_blob_ptr->base.id : 0; diff --git a/drivers/gpu/drm/drm_connector.c b/drivers/gpu/drm/drm_connector.c index 8b21a017edaa..60858b76374c 100644 --- a/drivers/gpu/drm/drm_connector.c +++ b/drivers/gpu/drm/drm_connector.c @@ -1383,6 +1383,41 @@ int drm_connector_attach_cp_downstream_property( } EXPORT_SYMBOL(drm_connector_attach_cp_downstream_property); +/** + * drm_connector_attach_cp_srm_property - attach cp srm + * property + * + * @connector: connector to attach cp srm property on. + * + * This is used to add support for sending the SRM table from userspace to + * kernel on selected connectors. Protected content provider will provide + * the system renewability Message(SRM) to userspace before requesting for + * HDCP on a port. Hence if a Port supports content protection (mostly HDCP) + * then this property will be attached to receive the SRM for revocation check + * of the ksvs. + * + * The srm blob id will be set to &drm_connector_state.cp_srm_blob_id + * + * Returns: + * Zero on success, negative errno on failure. + */ +int drm_connector_attach_cp_srm_property(struct drm_connector *connector) +{ + struct drm_device *dev = connector->dev; + struct drm_property *prop; + + prop = drm_property_create(dev, DRM_MODE_PROP_BLOB, "CP_SRM", 0); + if (!prop) + return -ENOMEM; + + drm_object_attach_property(&connector->base, prop, 0); + connector->cp_srm_property = prop; + + return 0; +} +EXPORT_SYMBOL(drm_connector_attach_cp_srm_property); + + /** * drm_mode_create_aspect_ratio_property - create aspect ratio property * @dev: DRM device diff --git a/include/drm/drm_connector.h b/include/drm/drm_connector.h index 9fd96c96d598..dc5f4d217bc0 100644 --- a/include/drm/drm_connector.h +++ b/include/drm/drm_connector.h @@ -460,6 +460,12 @@ struct drm_connector_state { * drm_writeback_signal_completion() */ struct drm_writeback_job *writeback_job; + + /** + * @cp_srm_blob_id: Connector property to pass the SRM table for content + * protection. This is most commonly used for HDCP. + */ + unsigned int cp_srm_blob_id; }; /** @@ -922,6 +928,12 @@ struct drm_connector { struct drm_property *cp_downstream_property; struct drm_property_blob *cp_downstream_blob_ptr; + /** + * @cp_srm_property: DRM BLOB property for content + * protection SRM information. + */ + struct drm_property *cp_srm_property; + /** * @path_blob_ptr: * @@ -1193,6 +1205,7 @@ int drm_connector_attach_content_protection_property( struct drm_connector *connector); int drm_connector_attach_cp_downstream_property( struct drm_connector *connector); +int drm_connector_attach_cp_srm_property(struct drm_connector *connector); int drm_mode_create_aspect_ratio_property(struct drm_device *dev); int drm_mode_create_content_type_property(struct drm_device *dev); void drm_hdmi_avi_infoframe_content_type(struct hdmi_avi_infoframe *frame, From 08b17493fd4dcfd954c1da0c170e9f2e978ee054 Mon Sep 17 00:00:00 2001 From: "Romli, Khairul Anuar" Date: Wed, 20 Dec 2017 21:47:01 +0530 Subject: [PATCH 1039/1103] drm/i915: Add HDCP SRM Blob parsing This patch adds a drm blob property to selected connectors. And also adds capability to parse the new srm blob passed through cp_srm_property. The revocated KSV list and their counts are stored in the intel_connector. This list should be used for revocation check of BKSVs in first stage HDCP authentication and for revocation check of ksv_fifo in second stage authentication. Signed-off-by: Ramalingam C --- drivers/gpu/drm/i915/intel_drv.h | 5 + drivers/gpu/drm/i915/intel_hdcp.c | 146 ++++++++++++++++++++++++++++++ include/drm/drm_hdcp.h | 14 +++ 3 files changed, 165 insertions(+) diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h index f20ce3b3545a..d4b307b531ec 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h @@ -419,6 +419,11 @@ struct intel_connector { struct delayed_work hdcp_check_work; struct work_struct hdcp_prop_work; struct work_struct hdcp_enable_work; + + /* list of Revocated KSVs and their count from SRM blob Parsing */ + unsigned int revocated_ksv_cnt; + u8 *revocated_ksv_list; + u32 srm_blob_id; }; struct intel_digital_connector_state { diff --git a/drivers/gpu/drm/i915/intel_hdcp.c b/drivers/gpu/drm/i915/intel_hdcp.c index a03a745544e2..ca2c73469cb2 100644 --- a/drivers/gpu/drm/i915/intel_hdcp.c +++ b/drivers/gpu/drm/i915/intel_hdcp.c @@ -10,6 +10,7 @@ #include #include #include +#include #include "intel_drv.h" #include "i915_reg.h" @@ -732,6 +733,10 @@ int intel_hdcp_init(struct intel_connector *connector, if (ret) return ret; + ret = drm_connector_attach_cp_srm_property(&connector->base); + if (ret) + return ret; + connector->hdcp_shim = hdcp_shim; mutex_init(&connector->hdcp_mutex); INIT_DELAYED_WORK(&connector->hdcp_check_work, intel_hdcp_check_work); @@ -818,11 +823,152 @@ void intel_hdcp_atomic_pre_commit(struct drm_connector *connector, intel_hdcp_disable(to_intel_connector(connector)); } +static u32 intel_hdcp_get_revocated_ksv_count(u8 *buf, u32 vrls_length) +{ + u32 parsed_bytes = 0, ksv_count = 0, vrl_ksv_cnt, vrl_sz; + + do { + vrl_ksv_cnt = *buf; + ksv_count += vrl_ksv_cnt; + + vrl_sz = (vrl_ksv_cnt * DRM_HDCP_KSV_LEN) + 1; + buf += vrl_sz; + parsed_bytes += vrl_sz; + } while (parsed_bytes < vrls_length); + + return ksv_count; +} + +static u32 intel_hdcp_get_revocated_ksvs(u8 *ksv_list, const u8 *buf, + u32 vrls_length) +{ + u32 parsed_bytes = 0, ksv_count = 0; + u32 vrl_ksv_cnt, vrl_ksv_sz, vrl_idx = 0; + + do { + vrl_ksv_cnt = *buf; + vrl_ksv_sz = vrl_ksv_cnt * DRM_HDCP_KSV_LEN; + + buf++; + + DRM_INFO("vrl: %d, Revoked KSVs: %d\n", vrl_idx++, + vrl_ksv_cnt); + memcpy(ksv_list, buf, vrl_ksv_sz); + + ksv_count += vrl_ksv_cnt; + ksv_list += vrl_ksv_sz; + buf += vrl_ksv_sz; + + parsed_bytes += (vrl_ksv_sz + 1); + } while (parsed_bytes < vrls_length); + + return ksv_count; +} + +static int intel_hdcp_parse_srm(struct drm_connector *connector, + struct drm_property_blob *blob) +{ + struct intel_connector *intel_connector = to_intel_connector(connector); + struct cp_srm_header *header; + u32 vrl_length, ksv_count; + u8 *buf; + + if (blob->length < (sizeof(struct cp_srm_header) + + DRM_HDCP_1_4_VRL_LENGTH_SIZE + + DRM_HDCP_1_4_DCP_SIG_SIZE)) { + DRM_ERROR("Invalid blob length\n"); + return -EINVAL; + } + + header = (struct cp_srm_header *)blob->data; + + DRM_INFO("SRM ID: 0x%x, SRM Ver: 0x%x, SRM Gen No: 0x%x\n", + header->spec_indicator.srm_id, + __swab16(header->srm_version), + header->srm_gen_no); + + WARN_ON(header->spec_indicator.reserved_hi || + header->spec_indicator.reserved_lo); + + if (header->spec_indicator.srm_id != DRM_HDCP_1_4_SRM_ID) { + DRM_ERROR("Invalid srm_id\n"); + return -EINVAL; + } + + buf = blob->data + sizeof(*header); + + vrl_length = (*buf << 16 | *(buf + 1) << 8 | *(buf + 2)); + + if (blob->length < (sizeof(struct cp_srm_header) + vrl_length) || + vrl_length < (DRM_HDCP_1_4_VRL_LENGTH_SIZE + + DRM_HDCP_1_4_DCP_SIG_SIZE)) { + DRM_ERROR("Invalid blob length or vrl length\n"); + return -EINVAL; + } + + /* Length of the all vrls combined */ + vrl_length -= (DRM_HDCP_1_4_VRL_LENGTH_SIZE + + DRM_HDCP_1_4_DCP_SIG_SIZE); + + if (!vrl_length) { + DRM_DEBUG("No vrl found\n"); + return -EINVAL; + } + + buf += DRM_HDCP_1_4_VRL_LENGTH_SIZE; + + + ksv_count = intel_hdcp_get_revocated_ksv_count(buf, vrl_length); + if (!ksv_count) { + DRM_INFO("Revocated KSV count is 0\n"); + return 0; + } + + kfree(intel_connector->revocated_ksv_list); + intel_connector->revocated_ksv_list = kzalloc(ksv_count * + DRM_HDCP_KSV_LEN, GFP_KERNEL); + if (!intel_connector->revocated_ksv_list) { + DRM_ERROR("Out of Memory\n"); + return -ENOMEM; + } + + if (intel_hdcp_get_revocated_ksvs(intel_connector->revocated_ksv_list, + buf, vrl_length) != ksv_count) { + intel_connector->revocated_ksv_cnt = 0; + kfree(intel_connector->revocated_ksv_list); + return -EINVAL; + } + + intel_connector->revocated_ksv_cnt = ksv_count; + return 0; +} + +static void intel_hdcp_update_srm(struct drm_connector *connector, + u32 srm_blob_id) +{ + struct intel_connector *intel_connector = to_intel_connector(connector); + struct drm_property_blob *blob; + + blob = drm_property_lookup_blob(connector->dev, srm_blob_id); + if (!blob || !blob->data) + return; + + if (!intel_hdcp_parse_srm(connector, blob)) + intel_connector->srm_blob_id = srm_blob_id; + + drm_property_blob_put(blob); +} + void intel_hdcp_atomic_commit(struct drm_connector *connector, struct drm_connector_state *new_state) { + struct intel_connector *intel_connector = to_intel_connector(connector); uint64_t new_cp = new_state->content_protection; + if (new_state->cp_srm_blob_id && + new_state->cp_srm_blob_id != intel_connector->srm_blob_id) + intel_hdcp_update_srm(connector, new_state->cp_srm_blob_id); + /* Enable hdcp if it's desired */ if (new_state->crtc && new_cp == DRM_MODE_CONTENT_PROTECTION_DESIRED) intel_hdcp_enable(to_intel_connector(connector)); diff --git a/include/drm/drm_hdcp.h b/include/drm/drm_hdcp.h index 98e63d870139..f17eb2910bdb 100644 --- a/include/drm/drm_hdcp.h +++ b/include/drm/drm_hdcp.h @@ -38,4 +38,18 @@ #define DRM_HDCP_DDC_BSTATUS 0x41 #define DRM_HDCP_DDC_KSV_FIFO 0x43 +#define DRM_HDCP_1_4_SRM_ID 0x8 +#define DRM_HDCP_1_4_VRL_LENGTH_SIZE 3 +#define DRM_HDCP_1_4_DCP_SIG_SIZE 40 + +struct cp_srm_header { + struct { + uint8_t reserved_hi:4; + uint8_t srm_id:4; + uint8_t reserved_lo; + } spec_indicator; + uint16_t srm_version; + uint8_t srm_gen_no; +} __packed; + #endif From 1380cd7bf12881ddd8228d5aedadab0754411934 Mon Sep 17 00:00:00 2001 From: "Romli, Khairul Anuar" Date: Thu, 21 Dec 2017 13:55:41 +0530 Subject: [PATCH 1040/1103] drm/i915: Add revocation check on Ksvs KSV list revocated by DCP LLC is provided as SRM Blob to kernel. Which is parsed and stored in intel_connector->revocated_ksv_list. This patch adds the revocation check for BKSV and KSV_FIFO in case of repeater. Signed-off-by: Ramalingam C --- drivers/gpu/drm/i915/intel_hdcp.c | 49 +++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/drivers/gpu/drm/i915/intel_hdcp.c b/drivers/gpu/drm/i915/intel_hdcp.c index ca2c73469cb2..20f78b572d1b 100644 --- a/drivers/gpu/drm/i915/intel_hdcp.c +++ b/drivers/gpu/drm/i915/intel_hdcp.c @@ -185,6 +185,45 @@ struct intel_digital_port *conn_to_dig_port(struct intel_connector *connector) return enc_to_dig_port(&intel_attached_encoder(&connector->base)->base); } +static inline void intel_hdcp_print_ksv(u8 *ksv) +{ + DRM_DEBUG_KMS("\t%#04x, %#04x, %#04x, %#04x, %#04x\n", *ksv, + *(ksv + 1), *(ksv + 2), *(ksv + 3), *(ksv + 4)); +} + +/* Check if any of the KSV is revocated by DCP LLC through SRM table */ +static inline bool intel_hdcp_ksvs_revocated(struct intel_connector *connector, + u8 *ksvs, u32 ksv_count) +{ + u32 rev_ksv_cnt = connector->revocated_ksv_cnt; + u8 *rev_ksv_list = connector->revocated_ksv_list; + u32 cnt, i, j; + + /* If the Revocated ksv list is empty */ + if (!rev_ksv_cnt || !rev_ksv_list) + return false; + + for (cnt = 0; cnt < ksv_count; cnt++) { + rev_ksv_list = connector->revocated_ksv_list; + for (i = 0; i < rev_ksv_cnt; i++) { + for (j = 0; j < DRM_HDCP_KSV_LEN; j++) + if (*(ksvs + j) != *(rev_ksv_list + j)) { + break; + } else if (j == (DRM_HDCP_KSV_LEN - 1)) { + DRM_DEBUG_KMS("Revocated KSV is "); + intel_hdcp_print_ksv(ksvs); + return true; + } + /* Move the offset to next KSV in the revocated list */ + rev_ksv_list += DRM_HDCP_KSV_LEN; + } + + /* Iterate to next ksv_offset */ + ksvs += DRM_HDCP_KSV_LEN; + } + return false; +} + /* Implements Part 2 of the HDCP authorization procedure */ static int intel_hdcp_auth_downstream(struct intel_connector *connector) @@ -235,6 +274,11 @@ int intel_hdcp_auth_downstream(struct intel_connector *connector) if (ret) return ret; + if (intel_hdcp_ksvs_revocated(connector, ksv_fifo, num_downstream)) { + DRM_ERROR("Revocated Ksv(s) in ksv_fifo\n"); + return -EPERM; + } + /* Process V' values from the receiver */ for (i = 0; i < DRM_HDCP_V_PRIME_NUM_PARTS; i++) { ret = shim->read_v_prime_part(intel_dig_port, i, &vprime); @@ -519,6 +563,11 @@ static int intel_hdcp_auth(struct intel_connector *connector) return -ENODEV; } + if (intel_hdcp_ksvs_revocated(connector, bksv.shim, 1)) { + DRM_ERROR("BKSV is revocated\n"); + return -EPERM; + } + I915_WRITE(PORT_HDCP_BKSVLO(port), bksv.reg[0]); I915_WRITE(PORT_HDCP_BKSVHI(port), bksv.reg[1]); From 1c1ae6c93368cf8b574acbfc01cce541339a856f Mon Sep 17 00:00:00 2001 From: "Romli, Khairul Anuar" Date: Thu, 21 Dec 2017 23:51:03 +0530 Subject: [PATCH 1041/1103] drm/i915: Add cp_downstream property Implements drm blob property cp_downstream_info property on HDCP capable connectors. Downstream topology info is gathered across authentication stages and stored in intel_conenctor. When HDCP authentication is successful, new blob with latest downstream topology information is updated to cp_downstream_info property. Change-Id: I4646ce3c1e971573bab815e655c2bb66da170a40 Signed-off-by: Ramalingam C Signed-off-by: Romli, Khairul Anuar --- drivers/gpu/drm/i915/intel_drv.h | 3 +++ drivers/gpu/drm/i915/intel_hdcp.c | 39 +++++++++++++++++++++++++++++-- include/drm/drm_hdcp.h | 3 ++- 3 files changed, 42 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h index d4b307b531ec..d734030c2ac1 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h @@ -424,6 +424,9 @@ struct intel_connector { unsigned int revocated_ksv_cnt; u8 *revocated_ksv_list; u32 srm_blob_id; + + /* Downstream info like, depth, device_count, bksv and ksv_list etc */ + struct cp_downstream_info *downstream_info; }; struct intel_digital_connector_state { diff --git a/drivers/gpu/drm/i915/intel_hdcp.c b/drivers/gpu/drm/i915/intel_hdcp.c index 20f78b572d1b..0c481f59b24d 100644 --- a/drivers/gpu/drm/i915/intel_hdcp.c +++ b/drivers/gpu/drm/i915/intel_hdcp.c @@ -266,6 +266,9 @@ int intel_hdcp_auth_downstream(struct intel_connector *connector) if (num_downstream == 0) return -EINVAL; + connector->downstream_info->device_count = num_downstream; + connector->downstream_info->depth = DRM_HDCP_DEPTH(bstatus[1]); + ksv_fifo = kzalloc(num_downstream * DRM_HDCP_KSV_LEN, GFP_KERNEL); if (!ksv_fifo) return -ENOMEM; @@ -279,6 +282,9 @@ int intel_hdcp_auth_downstream(struct intel_connector *connector) return -EPERM; } + memcpy(connector->downstream_info->ksv_list, ksv_fifo, + num_downstream * DRM_HDCP_KSV_LEN); + /* Process V' values from the receiver */ for (i = 0; i < DRM_HDCP_V_PRIME_NUM_PARTS; i++) { ret = shim->read_v_prime_part(intel_dig_port, i, &vprime); @@ -568,15 +574,20 @@ static int intel_hdcp_auth(struct intel_connector *connector) return -EPERM; } + memcpy(connector->downstream_info->bksv, bksv.shim, + DRM_MODE_HDCP_KSV_LEN); + I915_WRITE(PORT_HDCP_BKSVLO(port), bksv.reg[0]); I915_WRITE(PORT_HDCP_BKSVHI(port), bksv.reg[1]); ret = shim->repeater_present(intel_dig_port, &repeater_present); if (ret) return ret; - if (repeater_present) + if (repeater_present) { I915_WRITE(HDCP_REP_CTL, intel_hdcp_get_repeater_ctl(intel_dig_port)); + connector->downstream_info->is_repeater = true; + } ret = shim->toggle_signalling(intel_dig_port, true); if (ret) @@ -669,6 +680,9 @@ static int _intel_hdcp_disable(struct intel_connector *connector) return ret; } + memset(connector->downstream_info, 0, + sizeof(struct cp_downstream_info)); + DRM_DEBUG_KMS("HDCP is disabled\n"); return 0; } @@ -715,6 +729,9 @@ static int _intel_hdcp_enable(struct intel_connector *connector) _intel_hdcp_disable(connector); } + memset(connector->downstream_info, 0, + sizeof(struct cp_downstream_info)); + DRM_ERROR("HDCP authentication failed (%d tries/%d)\n", tries, ret); return ret; } @@ -724,9 +741,18 @@ static void intel_hdcp_enable_work(struct work_struct *work) struct intel_connector *connector = container_of(work, struct intel_connector, hdcp_enable_work); + int ret; mutex_lock(&connector->hdcp_mutex); - _intel_hdcp_enable(connector); + ret = _intel_hdcp_enable(connector); + if (!ret) { + ret = drm_mode_connector_update_cp_downstream_property( + &connector->base, + connector->downstream_info); + if (ret) + DRM_ERROR("Downstream_property update failed.%d\n", + ret); + } mutex_unlock(&connector->hdcp_mutex); } @@ -786,6 +812,15 @@ int intel_hdcp_init(struct intel_connector *connector, if (ret) return ret; + ret = drm_connector_attach_cp_downstream_property(&connector->base); + if (ret) + return ret; + + connector->downstream_info = kzalloc(sizeof(struct cp_downstream_info), + GFP_KERNEL); + if (!connector->downstream_info) + return -ENOMEM; + connector->hdcp_shim = hdcp_shim; mutex_init(&connector->hdcp_mutex); INIT_DELAYED_WORK(&connector->hdcp_check_work, intel_hdcp_check_work); diff --git a/include/drm/drm_hdcp.h b/include/drm/drm_hdcp.h index f17eb2910bdb..9f5284e4e427 100644 --- a/include/drm/drm_hdcp.h +++ b/include/drm/drm_hdcp.h @@ -19,7 +19,8 @@ #define DRM_HDCP_RI_LEN 2 #define DRM_HDCP_V_PRIME_PART_LEN 4 #define DRM_HDCP_V_PRIME_NUM_PARTS 5 -#define DRM_HDCP_NUM_DOWNSTREAM(x) (x & 0x7f) +#define DRM_HDCP_NUM_DOWNSTREAM(x) (x & 0x3f) +#define DRM_HDCP_DEPTH(x) (x & 0x7) #define DRM_HDCP_MAX_CASCADE_EXCEEDED(x) (x & BIT(3)) #define DRM_HDCP_MAX_DEVICE_EXCEEDED(x) (x & BIT(7)) From e63236503d533003845d0cc664b042dbda6be31c Mon Sep 17 00:00:00 2001 From: Badiuzzaman Iskhandar Date: Tue, 9 Oct 2018 15:31:22 +0800 Subject: [PATCH 1042/1103] REVERTME [IOTG]: drm/i915/: Add GuC v9.29 and HuC v1.07 firmware size In deferred firmware loading, the value of the firmware sizes were not known during i915 init. This will cause the i915 driver init to fail. Add the firmware size as workaround to enable huc/guc loading Change-Id: Id20318a9553e473e768ced036e24c14efd925ae2 Tracked-On: Signed-off-by: Badiuzzaman Iskhandar --- drivers/gpu/drm/i915/intel_wopcm.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_wopcm.c b/drivers/gpu/drm/i915/intel_wopcm.c index 5ff877ed7b12..10d1ea189775 100644 --- a/drivers/gpu/drm/i915/intel_wopcm.c +++ b/drivers/gpu/drm/i915/intel_wopcm.c @@ -63,6 +63,9 @@ #define GEN9_GUC_FW_RESERVED (128 * 1024) #define GEN9_GUC_WOPCM_OFFSET (GUC_WOPCM_RESERVED + GEN9_GUC_FW_RESERVED) +#define GEN9_GUC_9_29_SIZE ((142 * 1024) + 768) +#define GEN9_HUC_1_07_SIZE ((150 * 1024) + 576) + /** * intel_wopcm_init_early() - Early initialization of the WOPCM. * @wopcm: pointer to intel_wopcm. @@ -155,8 +158,8 @@ static inline int check_hw_restriction(struct drm_i915_private *i915, int intel_wopcm_init(struct intel_wopcm *wopcm) { struct drm_i915_private *i915 = wopcm_to_i915(wopcm); - u32 guc_fw_size = intel_uc_fw_get_upload_size(&i915->guc.fw); - u32 huc_fw_size = intel_uc_fw_get_upload_size(&i915->huc.fw); + u32 guc_fw_size = GEN9_GUC_9_29_SIZE; + u32 huc_fw_size = GEN9_HUC_1_07_SIZE; u32 ctx_rsvd = context_reserved_size(i915); u32 guc_wopcm_base; u32 guc_wopcm_size; From 6e285b5eb17c7c2178c1d296d05d483995dd080c Mon Sep 17 00:00:00 2001 From: Min He Date: Tue, 4 Sep 2018 01:00:01 +0000 Subject: [PATCH 1043/1103] drm/i915/gvt: clean up the cfg space and MMIO spaces When vgpu instance is destroyed, we need to clean up the MMIO and aperture regions, which is missing in current code. Signed-off-by: Min He Signed-off-by: Yin Fengwei Reviewed-by: Zhao Yakui Tracked-on: https://github.com/projectacrn/acrn-hypervisor/issues/1146 --- drivers/gpu/drm/i915/gvt/cfg_space.c | 2 ++ drivers/gpu/drm/i915/gvt/gvt.h | 2 ++ drivers/gpu/drm/i915/gvt/handlers.c | 2 +- drivers/gpu/drm/i915/gvt/vgpu.c | 1 + 4 files changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/i915/gvt/cfg_space.c b/drivers/gpu/drm/i915/gvt/cfg_space.c index 505e255c4d8a..8606925d339d 100644 --- a/drivers/gpu/drm/i915/gvt/cfg_space.c +++ b/drivers/gpu/drm/i915/gvt/cfg_space.c @@ -469,6 +469,8 @@ void intel_vgpu_reset_cfg_space(struct intel_vgpu *vgpu) INTEL_GVT_PCI_CLASS_VGA_OTHER; if (cmd & PCI_COMMAND_MEMORY) { + if (VGPU_PVMMIO(vgpu)) + set_pvmmio(vgpu, false); trap_gttmmio(vgpu, false); map_aperture(vgpu, false); } diff --git a/drivers/gpu/drm/i915/gvt/gvt.h b/drivers/gpu/drm/i915/gvt/gvt.h index f4d9056175ae..bbf2489f251d 100644 --- a/drivers/gpu/drm/i915/gvt/gvt.h +++ b/drivers/gpu/drm/i915/gvt/gvt.h @@ -556,6 +556,8 @@ void intel_vgpu_init_cfg_space(struct intel_vgpu *vgpu, bool primary); void intel_vgpu_reset_cfg_space(struct intel_vgpu *vgpu); +int set_pvmmio(struct intel_vgpu *vgpu, bool map); + int intel_vgpu_emulate_cfg_read(struct intel_vgpu *vgpu, unsigned int offset, void *p_data, unsigned int bytes); diff --git a/drivers/gpu/drm/i915/gvt/handlers.c b/drivers/gpu/drm/i915/gvt/handlers.c index ec0886ac8d2b..0fc1fb37e1ef 100644 --- a/drivers/gpu/drm/i915/gvt/handlers.c +++ b/drivers/gpu/drm/i915/gvt/handlers.c @@ -1311,7 +1311,7 @@ static int send_display_ready_uevent(struct intel_vgpu *vgpu, int ready) } #define INTEL_GVT_PCI_BAR_GTTMMIO 0 -static int set_pvmmio(struct intel_vgpu *vgpu, bool map) +int set_pvmmio(struct intel_vgpu *vgpu, bool map) { u64 start, end; u64 val; diff --git a/drivers/gpu/drm/i915/gvt/vgpu.c b/drivers/gpu/drm/i915/gvt/vgpu.c index 9e4a0b2b5f22..511681b24c59 100644 --- a/drivers/gpu/drm/i915/gvt/vgpu.c +++ b/drivers/gpu/drm/i915/gvt/vgpu.c @@ -300,6 +300,7 @@ void intel_gvt_destroy_vgpu(struct intel_vgpu *vgpu) intel_vgpu_clean_gtt(vgpu); intel_gvt_hypervisor_detach_vgpu(vgpu); intel_vgpu_free_resource(vgpu); + intel_vgpu_reset_cfg_space(vgpu); intel_vgpu_clean_mmio(vgpu); intel_vgpu_dmabuf_cleanup(vgpu); mutex_unlock(&vgpu->vgpu_lock); From e0e6e4be6f735515acf153a97004c729d9f26ed3 Mon Sep 17 00:00:00 2001 From: Zhipeng Gong Date: Thu, 20 Sep 2018 13:10:45 +0800 Subject: [PATCH 1044/1103] drm/i915/gvt: use plane size for fb decoder Current fb decoder returns pipe src size to user space, while ggtt surface sharing only show primary plane framebuffer. When plane fb size is smaller than pipe size, some garbages are showed. This patch change fb decoder to use plane size to avoid those garbages. v2: - align size to PAGE_SIZE Tracked-On: projectacrn/acrn-hypervisor#1301 Signed-off-by: Zhipeng Gong Reviewed-by: He, Min --- drivers/gpu/drm/i915/gvt/fb_decoder.c | 9 +++++---- drivers/gpu/drm/i915/gvt/fb_decoder.h | 4 ++++ drivers/gpu/drm/i915/i915_gem_gvtbuffer.c | 3 +-- 3 files changed, 10 insertions(+), 6 deletions(-) diff --git a/drivers/gpu/drm/i915/gvt/fb_decoder.c b/drivers/gpu/drm/i915/gvt/fb_decoder.c index 6bc40be48649..7c43c916b3d8 100644 --- a/drivers/gpu/drm/i915/gvt/fb_decoder.c +++ b/drivers/gpu/drm/i915/gvt/fb_decoder.c @@ -267,11 +267,12 @@ int intel_vgpu_decode_primary_plane(struct intel_vgpu *vgpu, (_PRI_PLANE_STRIDE_MASK >> 6) : _PRI_PLANE_STRIDE_MASK, plane->bpp); - plane->width = (vgpu_vreg_t(vgpu, PIPESRC(pipe)) & _PIPE_H_SRCSZ_MASK) >> - _PIPE_H_SRCSZ_SHIFT; + plane->width = vgpu_vreg_t(vgpu, PLANE_SIZE(pipe, PLANE_PRIMARY))& + _PLANE_SIZE_WIDTH_MASK; + plane->width += 1; - plane->height = (vgpu_vreg_t(vgpu, PIPESRC(pipe)) & - _PIPE_V_SRCSZ_MASK) >> _PIPE_V_SRCSZ_SHIFT; + plane->height = (vgpu_vreg_t(vgpu, PLANE_SIZE(pipe, PLANE_PRIMARY)) & + _PLANE_SIZE_HEIGHT_MASK) >> _PLANE_SIZE_HEIGHT_SHIFT; plane->height += 1; /* raw height is one minus the real value */ val = vgpu_vreg_t(vgpu, DSPTILEOFF(pipe)); diff --git a/drivers/gpu/drm/i915/gvt/fb_decoder.h b/drivers/gpu/drm/i915/gvt/fb_decoder.h index a202f9f6e81a..51626759534b 100644 --- a/drivers/gpu/drm/i915/gvt/fb_decoder.h +++ b/drivers/gpu/drm/i915/gvt/fb_decoder.h @@ -50,6 +50,10 @@ #define _PRI_PLANE_Y_OFF_SHIFT 16 #define _PRI_PLANE_Y_OFF_MASK (0xfff << _PRI_PLANE_Y_OFF_SHIFT) +#define _PLANE_SIZE_HEIGHT_SHIFT 16 +#define _PLANE_SIZE_HEIGHT_MASK (0xfff << _PLANE_SIZE_HEIGHT_SHIFT) +#define _PLANE_SIZE_WIDTH_MASK 0x1fff + #define _CURSOR_MODE 0x3f #define _CURSOR_ALPHA_FORCE_SHIFT 8 #define _CURSOR_ALPHA_FORCE_MASK (0x3 << _CURSOR_ALPHA_FORCE_SHIFT) diff --git a/drivers/gpu/drm/i915/i915_gem_gvtbuffer.c b/drivers/gpu/drm/i915/i915_gem_gvtbuffer.c index fe723099788d..f482eceb5c7f 100644 --- a/drivers/gpu/drm/i915/i915_gem_gvtbuffer.c +++ b/drivers/gpu/drm/i915/i915_gem_gvtbuffer.c @@ -195,8 +195,7 @@ static int gvt_decode_information(struct drm_device *dev, return -EINVAL; } - args->size = (((args->width * args->height * args->bpp) / 8) + - (PAGE_SIZE - 1)) >> PAGE_SHIFT; + args->size = ALIGN(args->stride * args->height, PAGE_SIZE) >> PAGE_SHIFT; if (args->start & (PAGE_SIZE - 1)) { DRM_DEBUG_DRIVER("GVT_GEM: Not aligned fb start address: " From e818a6797bc5c3ee3d090d9390cb6bc38601b0f5 Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Tue, 28 Apr 2015 15:30:49 +0300 Subject: [PATCH 1045/1103] mmc: core: Add functions for SDIO to hold re-tuning Add sdio_retune_hold_now() and sdio_retune_release() in order to allow SDIO function drivers to prevent re-tuning in cases where it conflicts with the device state. Signed-off-by: Adrian Hunter --- drivers/mmc/core/sdio_io.c | 13 +++++++++++++ include/linux/mmc/sdio_func.h | 3 +++ 2 files changed, 16 insertions(+) diff --git a/drivers/mmc/core/sdio_io.c b/drivers/mmc/core/sdio_io.c index d40744bbafa9..d8c119e50644 100644 --- a/drivers/mmc/core/sdio_io.c +++ b/drivers/mmc/core/sdio_io.c @@ -15,6 +15,7 @@ #include #include +#include "host.h" #include "sdio_ops.h" #include "core.h" #include "card.h" @@ -725,3 +726,15 @@ int sdio_set_host_pm_flags(struct sdio_func *func, mmc_pm_flag_t flags) return 0; } EXPORT_SYMBOL_GPL(sdio_set_host_pm_flags); + +void sdio_retune_hold_now(struct sdio_func *func) +{ + mmc_retune_hold_now(func->card->host); +} +EXPORT_SYMBOL_GPL(sdio_retune_hold_now); + +void sdio_retune_release(struct sdio_func *func) +{ + mmc_retune_release(func->card->host); +} +EXPORT_SYMBOL_GPL(sdio_retune_release); diff --git a/include/linux/mmc/sdio_func.h b/include/linux/mmc/sdio_func.h index 97ca105347a6..62de54434719 100644 --- a/include/linux/mmc/sdio_func.h +++ b/include/linux/mmc/sdio_func.h @@ -159,4 +159,7 @@ extern void sdio_f0_writeb(struct sdio_func *func, unsigned char b, extern mmc_pm_flag_t sdio_get_host_pm_caps(struct sdio_func *func); extern int sdio_set_host_pm_flags(struct sdio_func *func, mmc_pm_flag_t flags); +extern void sdio_retune_hold_now(struct sdio_func *func); +extern void sdio_retune_release(struct sdio_func *func); + #endif /* LINUX_MMC_SDIO_FUNC_H */ From 68d6720b4ec34e44906439de1444d6c13d7ea4de Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Tue, 28 Apr 2015 15:35:50 +0300 Subject: [PATCH 1046/1103] brcmfmac: Prevent re-tuning conflicting with 'wake-up' If the device is in a custom sleep state, then re-tuning will fail. Add calls to sdio_retune_hold_now() and sdio_retune_release() to prevent re-tuning before the wake-up command. In the case re-tuning was needed, the wake-up command might return an error, but the wake-up is expected still to have happened, and the error is anyway ignored. Signed-off-by: Adrian Hunter --- drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c index a907d7b065fa..1ac89386aabe 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c @@ -668,10 +668,16 @@ brcmf_sdio_kso_control(struct brcmf_sdio *bus, bool on) brcmf_dbg(TRACE, "Enter: on=%d\n", on); wr_val = (on << SBSDIO_FUNC1_SLEEPCSR_KSO_SHIFT); + + /* Cannot re-tune if device is asleep */ + if (on) + sdio_retune_hold_now(bus->sdiodev->func1); + /* 1st KSO write goes to AOS wake up core if device is asleep */ brcmf_sdiod_writeb(bus->sdiodev, SBSDIO_FUNC1_SLEEPCSR, wr_val, &err); if (on) { + sdio_retune_release(bus->sdiodev->func1); /* device WAKEUP through KSO: * write bit 0 & read back until * both bits 0 (kso bit) & 1 (dev on status) are set From bb941bcb95cad2014b2ebb198a1964128774b46b Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Mon, 13 Mar 2017 15:30:25 +0200 Subject: [PATCH 1047/1103] mmc: sdhci-pci: Add support for Intel ICP Add PCI Ids for Intel ICP. Signed-off-by: Adrian Hunter --- drivers/mmc/host/sdhci-pci-core.c | 2 ++ drivers/mmc/host/sdhci-pci.h | 2 ++ 2 files changed, 4 insertions(+) diff --git a/drivers/mmc/host/sdhci-pci-core.c b/drivers/mmc/host/sdhci-pci-core.c index 7bfd366d970d..a67a1d520823 100644 --- a/drivers/mmc/host/sdhci-pci-core.c +++ b/drivers/mmc/host/sdhci-pci-core.c @@ -1502,6 +1502,8 @@ static const struct pci_device_id pci_ids[] = { SDHCI_PCI_DEVICE(INTEL, CNPH_SD, intel_byt_sd), SDHCI_PCI_DEVICE(INTEL, ICP_EMMC, intel_glk_emmc), SDHCI_PCI_DEVICE(INTEL, ICP_SD, intel_byt_sd), + SDHCI_PCI_DEVICE(INTEL, ICPN_SD, intel_byt_sd), + SDHCI_PCI_DEVICE(INTEL, ICPH_SD, intel_byt_sd), SDHCI_PCI_DEVICE(O2, 8120, o2), SDHCI_PCI_DEVICE(O2, 8220, o2), SDHCI_PCI_DEVICE(O2, 8221, o2), diff --git a/drivers/mmc/host/sdhci-pci.h b/drivers/mmc/host/sdhci-pci.h index 2ef0bdca9197..18822bb82bcf 100644 --- a/drivers/mmc/host/sdhci-pci.h +++ b/drivers/mmc/host/sdhci-pci.h @@ -50,6 +50,8 @@ #define PCI_DEVICE_ID_INTEL_CNPH_SD 0xa375 #define PCI_DEVICE_ID_INTEL_ICP_EMMC 0x34c4 #define PCI_DEVICE_ID_INTEL_ICP_SD 0x34f8 +#define PCI_DEVICE_ID_INTEL_ICPN_SD 0x38f8 +#define PCI_DEVICE_ID_INTEL_ICPH_SD 0x3df8 #define PCI_DEVICE_ID_SYSKONNECT_8000 0x8000 #define PCI_DEVICE_ID_VIA_95D0 0x95d0 From b588a7be43264f0ae6b71c5001572b4a2042f2b9 Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Tue, 6 Jun 2017 14:47:42 +0300 Subject: [PATCH 1048/1103] scsi: ufshdc-pci: Add more Intel PCI Ids Add more Intel PCI Ids. Signed-off-by: Adrian Hunter --- drivers/scsi/ufs/ufshcd-pci.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/scsi/ufs/ufshcd-pci.c b/drivers/scsi/ufs/ufshcd-pci.c index ffe6f82182ba..04727a3de6e3 100644 --- a/drivers/scsi/ufs/ufshcd-pci.c +++ b/drivers/scsi/ufs/ufshcd-pci.c @@ -200,6 +200,9 @@ static const struct dev_pm_ops ufshcd_pci_pm_ops = { static const struct pci_device_id ufshcd_pci_tbl[] = { { PCI_VENDOR_ID_SAMSUNG, 0xC00C, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, { PCI_VDEVICE(INTEL, 0x9DFA), (kernel_ulong_t)&ufs_intel_cnl_hba_vops }, + { PCI_VDEVICE(INTEL, 0x34FA), (kernel_ulong_t)&ufs_intel_cnl_hba_vops }, + { PCI_VDEVICE(INTEL, 0x34FD), (kernel_ulong_t)&ufs_intel_cnl_hba_vops }, + { PCI_VDEVICE(INTEL, 0x38FA), (kernel_ulong_t)&ufs_intel_cnl_hba_vops }, { } /* terminate list */ }; From 17e2aa05197dec144a62e26991ac132b17c786b7 Mon Sep 17 00:00:00 2001 From: Szymon Mielczarek Date: Thu, 25 May 2017 11:29:04 +0200 Subject: [PATCH 1049/1103] HACK: scsi: ufshc-intel-pci: Force Data Rate A for HS mode for CNP The M-PHY layer protocol defines two Data Rate series (A and B) of pre-defined frequencies that are used for High-Speed mode. Because of an issue with M-PHY clock settings managed by BIOS, the priority to RateA has been given. HSDES sighting: 1504335234 Signed-off-by: Szymon Mielczarek Signed-off-by: Adrian Hunter --- drivers/scsi/ufs/ufshcd-pci.c | 40 +++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/drivers/scsi/ufs/ufshcd-pci.c b/drivers/scsi/ufs/ufshcd-pci.c index 04727a3de6e3..b86dcca2baf6 100644 --- a/drivers/scsi/ufs/ufshcd-pci.c +++ b/drivers/scsi/ufs/ufshcd-pci.c @@ -67,9 +67,49 @@ static int ufs_intel_link_startup_notify(struct ufs_hba *hba, return err; } +static int ufs_intel_pwr_change_notify(struct ufs_hba *hba, + enum ufs_notify_change_status notify, + struct ufs_pa_layer_attr *desired_pwr_info, + struct ufs_pa_layer_attr *final_pwr_info) +{ + struct pci_dev *pdev = to_pci_dev(hba->dev); + int ret = 0; + + if (!desired_pwr_info || !final_pwr_info) { + ret = -EINVAL; + goto out; + } + + switch (notify) { + case PRE_CHANGE: + dev_dbg(hba->dev, "PWR change PRE_CHANGE start\n"); + memcpy(final_pwr_info, desired_pwr_info, + sizeof(struct ufs_pa_layer_attr)); + + if (pdev->device == 0x9DFA && + (final_pwr_info->pwr_tx == FASTAUTO_MODE || + final_pwr_info->pwr_tx == FAST_MODE || + final_pwr_info->pwr_rx == FASTAUTO_MODE || + final_pwr_info->pwr_rx == FAST_MODE)) { + /* Currently, only RATE A is supported for HS mode */ + final_pwr_info->hs_rate = PA_HS_MODE_A; + } + break; + case POST_CHANGE: + break; + default: + ret = -EINVAL; + break; + } + +out: + return ret; +} + static struct ufs_hba_variant_ops ufs_intel_cnl_hba_vops = { .name = "intel-pci", .link_startup_notify = ufs_intel_link_startup_notify, + .pwr_change_notify = ufs_intel_pwr_change_notify, }; #ifdef CONFIG_PM_SLEEP From 290d71916f82d29009925b917330bcd0f241731b Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Tue, 14 Nov 2017 11:50:53 +0200 Subject: [PATCH 1050/1103] HACK: mmc: sdhci-pci: Disable DCMD for CNP DCMD will be fixed in PCH C0 refer HSD 1604306740 Signed-off-by: Adrian Hunter --- drivers/mmc/host/sdhci-pci-core.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/mmc/host/sdhci-pci-core.c b/drivers/mmc/host/sdhci-pci-core.c index a67a1d520823..6350db11df69 100644 --- a/drivers/mmc/host/sdhci-pci-core.c +++ b/drivers/mmc/host/sdhci-pci-core.c @@ -738,7 +738,8 @@ static int glk_emmc_probe_slot(struct sdhci_pci_slot *slot) slot->host->mmc->caps2 |= MMC_CAP2_HS400_ES, slot->host->mmc_host_ops.hs400_enhanced_strobe = intel_hs400_enhanced_strobe; - slot->host->mmc->caps2 |= MMC_CAP2_CQE_DCMD; + if (slot->chip->pdev->device != PCI_DEVICE_ID_INTEL_CNP_EMMC) + slot->host->mmc->caps2 |= MMC_CAP2_CQE_DCMD; } return ret; From 8caf56faa07efa6b7d1f875cd02c6c3db1ba4416 Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Mon, 6 Nov 2017 11:01:30 +0200 Subject: [PATCH 1051/1103] scsi: ufshdc-pci: Add yet more Intel PCI Ids Add more Intel PCI Ids. Signed-off-by: Adrian Hunter --- drivers/scsi/ufs/ufshcd-pci.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/scsi/ufs/ufshcd-pci.c b/drivers/scsi/ufs/ufshcd-pci.c index b86dcca2baf6..0f8af1433e46 100644 --- a/drivers/scsi/ufs/ufshcd-pci.c +++ b/drivers/scsi/ufs/ufshcd-pci.c @@ -243,6 +243,8 @@ static const struct pci_device_id ufshcd_pci_tbl[] = { { PCI_VDEVICE(INTEL, 0x34FA), (kernel_ulong_t)&ufs_intel_cnl_hba_vops }, { PCI_VDEVICE(INTEL, 0x34FD), (kernel_ulong_t)&ufs_intel_cnl_hba_vops }, { PCI_VDEVICE(INTEL, 0x38FA), (kernel_ulong_t)&ufs_intel_cnl_hba_vops }, + { PCI_VDEVICE(INTEL, 0xA0FA), (kernel_ulong_t)&ufs_intel_cnl_hba_vops }, + { PCI_VDEVICE(INTEL, 0xA0FF), (kernel_ulong_t)&ufs_intel_cnl_hba_vops }, { } /* terminate list */ }; From 3620041115ea12b38b2d0bc8d5069067a712b002 Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Tue, 23 May 2017 10:34:14 +0300 Subject: [PATCH 1052/1103] DEBUG: ufs: query bRefClkFreq Signed-off-by: Adrian Hunter --- drivers/scsi/ufs/ufshcd.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c index c55f38ec391c..7cb3cb71a1fb 100644 --- a/drivers/scsi/ufs/ufshcd.c +++ b/drivers/scsi/ufs/ufshcd.c @@ -6659,6 +6659,17 @@ static int ufshcd_probe_hba(struct ufs_hba *hba) if (!hba->is_init_prefetch) hba->is_init_prefetch = true; + { + u32 refclkfreq; + + /* index = 0, selector = 0 */ + ret = ufshcd_query_attr_retry(hba, UPIU_QUERY_OPCODE_READ_ATTR, QUERY_ATTR_IDN_REF_CLK_FREQ, 0, 0, &refclkfreq); + if (ret) + dev_err(hba->dev, "%s: UFS attribute bRefClkFreq %u, error %d\n", __func__, refclkfreq, ret); + else + dev_info(hba->dev, "%s: UFS attribute bRefClkFreq %u\n", __func__, refclkfreq); + } + out: /* * If we failed to initialize the device or the device is not From 1a2aa3d9ed8ee2ff009d40ce99dff5626274310a Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Tue, 23 May 2017 10:40:09 +0300 Subject: [PATCH 1053/1103] DEBUG: ufs: set bRefClkFreq to zero Signed-off-by: Adrian Hunter --- drivers/scsi/ufs/ufshcd.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c index 7cb3cb71a1fb..8cb6e538db3e 100644 --- a/drivers/scsi/ufs/ufshcd.c +++ b/drivers/scsi/ufs/ufshcd.c @@ -6668,6 +6668,14 @@ static int ufshcd_probe_hba(struct ufs_hba *hba) dev_err(hba->dev, "%s: UFS attribute bRefClkFreq %u, error %d\n", __func__, refclkfreq, ret); else dev_info(hba->dev, "%s: UFS attribute bRefClkFreq %u\n", __func__, refclkfreq); + if (!ret && refclkfreq != 0) { + refclkfreq = 0; + ret = ufshcd_query_attr_retry(hba, UPIU_QUERY_OPCODE_WRITE_ATTR, QUERY_ATTR_IDN_REF_CLK_FREQ, 0, 0, &refclkfreq); + if (ret) + dev_err(hba->dev, "%s: UFS failed to write attribute bRefClkFreq %u, error %d\n", __func__, refclkfreq, ret); + else + dev_info(hba->dev, "%s: UFS wrote attribute bRefClkFreq %u\n", __func__, refclkfreq); + } } out: From 62d659cc8586ffdc27d04995789ae36efc51a122 Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Fri, 13 Apr 2018 15:01:49 +0300 Subject: [PATCH 1054/1103] scsi: ufshdc-pci: Add Intel PCI Ids for EHL Add more Intel PCI Ids. Signed-off-by: Adrian Hunter --- drivers/scsi/ufs/ufshcd-pci.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/scsi/ufs/ufshcd-pci.c b/drivers/scsi/ufs/ufshcd-pci.c index 0f8af1433e46..00ac35c961e5 100644 --- a/drivers/scsi/ufs/ufshcd-pci.c +++ b/drivers/scsi/ufs/ufshcd-pci.c @@ -245,6 +245,8 @@ static const struct pci_device_id ufshcd_pci_tbl[] = { { PCI_VDEVICE(INTEL, 0x38FA), (kernel_ulong_t)&ufs_intel_cnl_hba_vops }, { PCI_VDEVICE(INTEL, 0xA0FA), (kernel_ulong_t)&ufs_intel_cnl_hba_vops }, { PCI_VDEVICE(INTEL, 0xA0FF), (kernel_ulong_t)&ufs_intel_cnl_hba_vops }, + { PCI_VDEVICE(INTEL, 0x4B90), (kernel_ulong_t)&ufs_intel_cnl_hba_vops }, + { PCI_VDEVICE(INTEL, 0x4B95), (kernel_ulong_t)&ufs_intel_cnl_hba_vops }, { } /* terminate list */ }; From d14ee15361eefbb235278f0dc72835b0ba092599 Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Fri, 13 Apr 2018 15:01:49 +0300 Subject: [PATCH 1055/1103] scsi: ufshdc-pci: Add some more Intel PCI Ids Add more Intel PCI Ids. Signed-off-by: Adrian Hunter --- drivers/scsi/ufs/ufshcd-pci.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/scsi/ufs/ufshcd-pci.c b/drivers/scsi/ufs/ufshcd-pci.c index 00ac35c961e5..4e79ea065556 100644 --- a/drivers/scsi/ufs/ufshcd-pci.c +++ b/drivers/scsi/ufs/ufshcd-pci.c @@ -247,6 +247,8 @@ static const struct pci_device_id ufshcd_pci_tbl[] = { { PCI_VDEVICE(INTEL, 0xA0FF), (kernel_ulong_t)&ufs_intel_cnl_hba_vops }, { PCI_VDEVICE(INTEL, 0x4B90), (kernel_ulong_t)&ufs_intel_cnl_hba_vops }, { PCI_VDEVICE(INTEL, 0x4B95), (kernel_ulong_t)&ufs_intel_cnl_hba_vops }, + { PCI_VDEVICE(INTEL, 0x43FA), (kernel_ulong_t)&ufs_intel_cnl_hba_vops }, + { PCI_VDEVICE(INTEL, 0x43FF), (kernel_ulong_t)&ufs_intel_cnl_hba_vops }, { } /* terminate list */ }; From f63feb9aea35b6ab7f5ef5a1e24cca5e93574939 Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Mon, 13 Mar 2017 15:30:25 +0200 Subject: [PATCH 1056/1103] mmc: sdhci-pci: Add support for Intel EHL Add PCI Ids for Intel EHL. Signed-off-by: Adrian Hunter --- drivers/mmc/host/sdhci-pci-core.c | 2 ++ drivers/mmc/host/sdhci-pci.h | 2 ++ 2 files changed, 4 insertions(+) diff --git a/drivers/mmc/host/sdhci-pci-core.c b/drivers/mmc/host/sdhci-pci-core.c index 6350db11df69..84644a102344 100644 --- a/drivers/mmc/host/sdhci-pci-core.c +++ b/drivers/mmc/host/sdhci-pci-core.c @@ -1505,6 +1505,8 @@ static const struct pci_device_id pci_ids[] = { SDHCI_PCI_DEVICE(INTEL, ICP_SD, intel_byt_sd), SDHCI_PCI_DEVICE(INTEL, ICPN_SD, intel_byt_sd), SDHCI_PCI_DEVICE(INTEL, ICPH_SD, intel_byt_sd), + SDHCI_PCI_DEVICE(INTEL, EHL_EMMC, intel_glk_emmc), + SDHCI_PCI_DEVICE(INTEL, EHL_SD, intel_byt_sd), SDHCI_PCI_DEVICE(O2, 8120, o2), SDHCI_PCI_DEVICE(O2, 8220, o2), SDHCI_PCI_DEVICE(O2, 8221, o2), diff --git a/drivers/mmc/host/sdhci-pci.h b/drivers/mmc/host/sdhci-pci.h index 18822bb82bcf..66322cefe390 100644 --- a/drivers/mmc/host/sdhci-pci.h +++ b/drivers/mmc/host/sdhci-pci.h @@ -52,6 +52,8 @@ #define PCI_DEVICE_ID_INTEL_ICP_SD 0x34f8 #define PCI_DEVICE_ID_INTEL_ICPN_SD 0x38f8 #define PCI_DEVICE_ID_INTEL_ICPH_SD 0x3df8 +#define PCI_DEVICE_ID_INTEL_EHL_EMMC 0x4b47 +#define PCI_DEVICE_ID_INTEL_EHL_SD 0x4b48 #define PCI_DEVICE_ID_SYSKONNECT_8000 0x8000 #define PCI_DEVICE_ID_VIA_95D0 0x95d0 From 6e538b32659a9de278b0aad76cbf63af5d70670d Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Tue, 22 May 2018 10:44:51 +0300 Subject: [PATCH 1057/1103] HACK: scsi: ufs: Add module parameters max_gear, dflt_hs_rate and dflt_hs_mode Signed-off-by: Adrian Hunter --- drivers/scsi/ufs/ufshcd.c | 30 +++++++++++++++++++++++++++--- 1 file changed, 27 insertions(+), 3 deletions(-) diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c index 8cb6e538db3e..a4d36497a047 100644 --- a/drivers/scsi/ufs/ufshcd.c +++ b/drivers/scsi/ufs/ufshcd.c @@ -234,6 +234,10 @@ static struct ufs_dev_fix ufs_fixups[] = { END_FIX }; +static int max_gear; +static int dflt_hs_rate; +static int dflt_hs_mode; + static void ufshcd_tmc_handler(struct ufs_hba *hba); static void ufshcd_async_scan(void *data, async_cookie_t cookie); static int ufshcd_reset_and_restore(struct ufs_hba *hba); @@ -3932,9 +3936,15 @@ static int ufshcd_get_max_pwr_mode(struct ufs_hba *hba) if (hba->max_pwr_info.is_valid) return 0; - pwr_info->pwr_tx = FAST_MODE; - pwr_info->pwr_rx = FAST_MODE; - pwr_info->hs_rate = PA_HS_MODE_B; + if (dflt_hs_mode != FAST_MODE && dflt_hs_mode != FASTAUTO_MODE) + dflt_hs_mode = FAST_MODE; + + if (dflt_hs_rate != PA_HS_MODE_A && dflt_hs_rate != PA_HS_MODE_B) + dflt_hs_rate = PA_HS_MODE_B; + + pwr_info->pwr_tx = dflt_hs_mode; + pwr_info->pwr_rx = dflt_hs_mode; + pwr_info->hs_rate = dflt_hs_rate; /* Get the connected lane count */ ufshcd_dme_get(hba, UIC_ARG_MIB(PA_CONNECTEDRXDATALANES), @@ -3980,6 +3990,12 @@ static int ufshcd_get_max_pwr_mode(struct ufs_hba *hba) pwr_info->pwr_tx = SLOW_MODE; } + if (max_gear > 0 && + (pwr_info->gear_rx > max_gear || pwr_info->gear_tx > max_gear)) { + pwr_info->gear_rx = max_gear; + pwr_info->gear_tx = max_gear; + } + hba->max_pwr_info.is_valid = true; return 0; } @@ -8170,6 +8186,14 @@ int ufshcd_init(struct ufs_hba *hba, void __iomem *mmio_base, unsigned int irq) } EXPORT_SYMBOL_GPL(ufshcd_init); +module_param(max_gear, int, 0444); +module_param(dflt_hs_rate, int, 0444); +module_param(dflt_hs_mode, int, 0444); + +MODULE_PARM_DESC(, "Maximum gear: 1, 2 , 3 ..."); +MODULE_PARM_DESC(, "Default high speed rate series : 1 (= rate A), 2 (= rate B)"); +MODULE_PARM_DESC(, "Default high speed power mode: 1 (= FAST), 4 (= FASTAUTO)"); + MODULE_AUTHOR("Santosh Yaragnavi "); MODULE_AUTHOR("Vinayak Holikatti "); MODULE_DESCRIPTION("Generic UFS host controller driver Core"); From 8033e05e35fad7dbf82a93b249ad905dc8a1ae81 Mon Sep 17 00:00:00 2001 From: Tomas Winkler Date: Sun, 1 Feb 2015 10:17:24 +0200 Subject: [PATCH 1058/1103] rpmb: add Replay Protected Memory Block (RPMB) subsystem Few storage technologies such is EMMC, UFS, and NVMe support RPMB a hardware partition with common protocol and frame layout. The RPMB partition cannot be accessed via standard block layer, but by a set of specific commands: WRITE, READ, GET_WRITE_COUNTER, and PROGRAM_KEY. Such a partition provides authenticated and replay protected access, hence suitable as a secure storage. The RPMB layer aims to provide in-kernel API for Trusted Execution Environment (TEE) devices that are capable to securely compute block frame signature. In case a TEE device wishes to store a replay protected data, it creates an RPMB frame with requested data and computes HMAC of the frame, then it requests the storage device via RPMB layer to store the data. A TEE device driver can claim the RPMB interface, for example, via class_interface_register(). The RPMB layer provides an API for issuing a sequence of RPMB protocol frames via rpmb_cmd_seq() call. A storage device registers its RPMB (eMMC) partition, RPMB W-LUN (UFS), or RPMB target NVMe with the RPMB layer providing an implementation for rpmb_cmd_seq() handler, that enables sending sequence of RPMB standard frames and set of attributes. V2: added short workflow description in the commit message V3: commit message fix V4: resend V5: add rpmb sequence interface. V6: 1. More info in the commit message 2. Define simulation device type V7: resend V8: 1. Add rpmb_cmd_req_write/read helper functions. 2. Fix minor checkpatch warning. 3. Change the license to Dual BSD/GPL V9: 1. Drop rpmb_cmd_req interface. 2. Add NVME type 3. Support for multiple RPMB partition on same device. 4. Add additional information about partition. 5. Add driver data access functions. 6. Add SPDX identifiers. 7. Unexport rpmb_dev_find_device() Change-Id: I830751859c2aed519c41a8123bd96c7a7243262a Signed-off-by: Tomas Winkler Signed-off-by: Alexander Usyskin Tested-by: Avri Altman --- MAINTAINERS | 7 + drivers/char/Kconfig | 2 + drivers/char/Makefile | 1 + drivers/char/rpmb/Kconfig | 9 + drivers/char/rpmb/Makefile | 5 + drivers/char/rpmb/core.c | 333 +++++++++++++++++++++++++++++++++++++ include/linux/rpmb.h | 250 ++++++++++++++++++++++++++++ 7 files changed, 607 insertions(+) create mode 100644 drivers/char/rpmb/Kconfig create mode 100644 drivers/char/rpmb/Makefile create mode 100644 drivers/char/rpmb/core.c create mode 100644 include/linux/rpmb.h diff --git a/MAINTAINERS b/MAINTAINERS index 48a65c3a4189..392cc9ba380d 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -12513,6 +12513,13 @@ F: include/net/rose.h F: include/uapi/linux/rose.h F: net/rose/ +RPMB SUBSYSTEM +M: Tomas Winkler +L: linux-kernel@vger.kernel.org +S: Supported +F: drivers/char/rpmb/* +F: include/linux/rpmb.h + RTL2830 MEDIA DRIVER M: Antti Palosaari L: linux-media@vger.kernel.org diff --git a/drivers/char/Kconfig b/drivers/char/Kconfig index 40728491f37b..26a2da8dde63 100644 --- a/drivers/char/Kconfig +++ b/drivers/char/Kconfig @@ -552,6 +552,8 @@ config ADI and SSM (Silicon Secured Memory). Intended consumers of this driver include crash and makedumpfile. +source "drivers/char/rpmb/Kconfig" + endmenu config RANDOM_TRUST_CPU diff --git a/drivers/char/Makefile b/drivers/char/Makefile index b8d42b4e979b..88764b76d975 100644 --- a/drivers/char/Makefile +++ b/drivers/char/Makefile @@ -58,3 +58,4 @@ js-rtc-y = rtc.o obj-$(CONFIG_XILLYBUS) += xillybus/ obj-$(CONFIG_POWERNV_OP_PANEL) += powernv-op-panel.o obj-$(CONFIG_ADI) += adi.o +obj-$(CONFIG_RPMB) += rpmb/ diff --git a/drivers/char/rpmb/Kconfig b/drivers/char/rpmb/Kconfig new file mode 100644 index 000000000000..b5cd02de91bb --- /dev/null +++ b/drivers/char/rpmb/Kconfig @@ -0,0 +1,9 @@ +# SPDX-License-Identifier: GPL-2.0 +config RPMB + tristate "RPMB partition interface" + help + Unified RPMB partition interface for eMMC and UFS. + Provides interface for in kernel security controllers to + access RPMB partition. + + If unsure, select N. diff --git a/drivers/char/rpmb/Makefile b/drivers/char/rpmb/Makefile new file mode 100644 index 000000000000..badc1cd9428b --- /dev/null +++ b/drivers/char/rpmb/Makefile @@ -0,0 +1,5 @@ +# SPDX-License-Identifier: GPL-2.0 +obj-$(CONFIG_RPMB) += rpmb.o +rpmb-objs += core.o + +ccflags-y += -D__CHECK_ENDIAN__ diff --git a/drivers/char/rpmb/core.c b/drivers/char/rpmb/core.c new file mode 100644 index 000000000000..69a590106ae1 --- /dev/null +++ b/drivers/char/rpmb/core.c @@ -0,0 +1,333 @@ +// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0 +/* + * Copyright(c) 2015 - 2018 Intel Corporation. All rights reserved. + */ +#include +#include +#include +#include +#include +#include +#include + +#include + +static DEFINE_IDA(rpmb_ida); + +/** + * rpmb_dev_get - increase rpmb device ref counter + * + * @rdev: rpmb device + */ +struct rpmb_dev *rpmb_dev_get(struct rpmb_dev *rdev) +{ + return get_device(&rdev->dev) ? rdev : NULL; +} +EXPORT_SYMBOL_GPL(rpmb_dev_get); + +/** + * rpmb_dev_put - decrease rpmb device ref counter + * + * @rdev: rpmb device + */ +void rpmb_dev_put(struct rpmb_dev *rdev) +{ + put_device(&rdev->dev); +} +EXPORT_SYMBOL_GPL(rpmb_dev_put); + +/** + * rpmb_cmd_seq - send RPMB command sequence + * + * @rdev: rpmb device + * @cmds: rpmb command list + * @ncmds: number of commands + * + * Return: 0 on success + * -EINVAL on wrong parameters + * -EOPNOTSUPP if device doesn't support the requested operation + * < 0 if the operation fails + */ +int rpmb_cmd_seq(struct rpmb_dev *rdev, struct rpmb_cmd *cmds, u32 ncmds) +{ + int err; + + if (!rdev || !cmds || !ncmds) + return -EINVAL; + + mutex_lock(&rdev->lock); + err = -EOPNOTSUPP; + if (rdev->ops && rdev->ops->cmd_seq) { + err = rdev->ops->cmd_seq(rdev->dev.parent, rdev->target, + cmds, ncmds); + } + mutex_unlock(&rdev->lock); + + return err; +} +EXPORT_SYMBOL_GPL(rpmb_cmd_seq); + +int rpmb_get_capacity(struct rpmb_dev *rdev) +{ + int err; + + if (!rdev) + return -EINVAL; + + mutex_lock(&rdev->lock); + err = -EOPNOTSUPP; + if (rdev->ops && rdev->ops->get_capacity) + err = rdev->ops->get_capacity(rdev->dev.parent, rdev->target); + mutex_unlock(&rdev->lock); + + return err; +} +EXPORT_SYMBOL_GPL(rpmb_get_capacity); + +static void rpmb_dev_release(struct device *dev) +{ + struct rpmb_dev *rdev = to_rpmb_dev(dev); + + ida_simple_remove(&rpmb_ida, rdev->id); + kfree(rdev); +} + +struct class rpmb_class = { + .name = "rpmb", + .owner = THIS_MODULE, + .dev_release = rpmb_dev_release, +}; +EXPORT_SYMBOL(rpmb_class); + +/** + * rpmb_dev_find_device - return first matching rpmb device + * + * @data: data for the match function + * @match: the matching function + * + * Return: matching rpmb device or NULL on failure + */ +static +struct rpmb_dev *rpmb_dev_find_device(const void *data, + int (*match)(struct device *dev, + const void *data)) +{ + struct device *dev; + + dev = class_find_device(&rpmb_class, NULL, data, match); + + return dev ? to_rpmb_dev(dev) : NULL; +} + +static int match_by_type(struct device *dev, const void *data) +{ + struct rpmb_dev *rdev = to_rpmb_dev(dev); + const u32 *type = data; + + return (*type == RPMB_TYPE_ANY || rdev->ops->type == *type); +} + +/** + * rpmb_dev_get_by_type - return first registered rpmb device + * with matching type. + * If run with RPMB_TYPE_ANY the first an probably only + * device is returned + * + * @type: rpbm underlying device type + * + * Return: matching rpmb device or NULL/ERR_PTR on failure + */ +struct rpmb_dev *rpmb_dev_get_by_type(u32 type) +{ + if (type > RPMB_TYPE_MAX) + return ERR_PTR(-EINVAL); + + return rpmb_dev_find_device(&type, match_by_type); +} +EXPORT_SYMBOL_GPL(rpmb_dev_get_by_type); + +struct device_with_target { + const struct device *dev; + u8 target; +}; + +static int match_by_parent(struct device *dev, const void *data) +{ + const struct device_with_target *d = data; + struct rpmb_dev *rdev = to_rpmb_dev(dev); + + return (d->dev && dev->parent == d->dev && rdev->target == d->target); +} + +/** + * rpmb_dev_find_by_device - retrieve rpmb device from the parent device + * + * @parent: parent device of the rpmb device + * @target: RPMB target/region within the physical device + * + * Return: NULL if there is no rpmb device associated with the parent device + */ +struct rpmb_dev *rpmb_dev_find_by_device(struct device *parent, u8 target) +{ + struct device_with_target t; + + if (!parent) + return NULL; + + t.dev = parent; + t.target = target; + + return rpmb_dev_find_device(&t, match_by_parent); +} +EXPORT_SYMBOL_GPL(rpmb_dev_find_by_device); + +/** + * rpmb_dev_unregister - unregister RPMB partition from the RPMB subsystem + * + * @rdev: the rpmb device to unregister + */ +int rpmb_dev_unregister(struct rpmb_dev *rdev) +{ + if (!rdev) + return -EINVAL; + + mutex_lock(&rdev->lock); + device_del(&rdev->dev); + mutex_unlock(&rdev->lock); + + rpmb_dev_put(rdev); + + return 0; +} +EXPORT_SYMBOL_GPL(rpmb_dev_unregister); + +/** + * rpmb_dev_unregister_by_device - unregister RPMB partition + * from the RPMB subsystem + * + * @dev: the parent device of the rpmb device + * @target: RPMB target/region within the physical device + */ +int rpmb_dev_unregister_by_device(struct device *dev, u8 target) +{ + struct rpmb_dev *rdev; + + if (!dev) + return -EINVAL; + + rdev = rpmb_dev_find_by_device(dev, target); + if (!rdev) { + dev_warn(dev, "no disk found %s\n", dev_name(dev->parent)); + return -ENODEV; + } + + rpmb_dev_put(rdev); + + return rpmb_dev_unregister(rdev); +} +EXPORT_SYMBOL_GPL(rpmb_dev_unregister_by_device); + +/** + * rpmb_dev_get_drvdata - driver data getter + * + * @rdev: rpmb device + * + * Return: driver private data + */ +void *rpmb_dev_get_drvdata(const struct rpmb_dev *rdev) +{ + return dev_get_drvdata(&rdev->dev); +} +EXPORT_SYMBOL_GPL(rpmb_dev_get_drvdata); + +/** + * rpmb_dev_set_drvdata - driver data setter + * + * @rdev: rpmb device + * @data: data to store + */ +void rpmb_dev_set_drvdata(struct rpmb_dev *rdev, void *data) +{ + dev_set_drvdata(&rdev->dev, data); +} +EXPORT_SYMBOL_GPL(rpmb_dev_set_drvdata); + +/** + * rpmb_dev_register - register RPMB partition with the RPMB subsystem + * + * @dev: storage device of the rpmb device + * @target: RPMB target/region within the physical device + * @ops: device specific operations + */ +struct rpmb_dev *rpmb_dev_register(struct device *dev, u8 target, + const struct rpmb_ops *ops) +{ + struct rpmb_dev *rdev; + int id; + int ret; + + if (!dev || !ops) + return ERR_PTR(-EINVAL); + + if (!ops->cmd_seq) + return ERR_PTR(-EINVAL); + + if (!ops->get_capacity) + return ERR_PTR(-EINVAL); + + if (ops->type == RPMB_TYPE_ANY || ops->type > RPMB_TYPE_MAX) + return ERR_PTR(-EINVAL); + + rdev = kzalloc(sizeof(*rdev), GFP_KERNEL); + if (!rdev) + return ERR_PTR(-ENOMEM); + + id = ida_simple_get(&rpmb_ida, 0, 0, GFP_KERNEL); + if (id < 0) { + ret = id; + goto exit; + } + + mutex_init(&rdev->lock); + rdev->ops = ops; + rdev->id = id; + rdev->target = target; + + dev_set_name(&rdev->dev, "rpmb%d", id); + rdev->dev.class = &rpmb_class; + rdev->dev.parent = dev; + ret = device_register(&rdev->dev); + if (ret) + goto exit; + + dev_dbg(&rdev->dev, "registered device\n"); + + return rdev; + +exit: + if (id >= 0) + ida_simple_remove(&rpmb_ida, id); + kfree(rdev); + return ERR_PTR(ret); +} +EXPORT_SYMBOL_GPL(rpmb_dev_register); + +static int __init rpmb_init(void) +{ + ida_init(&rpmb_ida); + class_register(&rpmb_class); + return 0; +} + +static void __exit rpmb_exit(void) +{ + class_unregister(&rpmb_class); + ida_destroy(&rpmb_ida); +} + +subsys_initcall(rpmb_init); +module_exit(rpmb_exit); + +MODULE_AUTHOR("Intel Corporation"); +MODULE_DESCRIPTION("RPMB class"); +MODULE_LICENSE("Dual BSD/GPL"); diff --git a/include/linux/rpmb.h b/include/linux/rpmb.h new file mode 100644 index 000000000000..6acd9b1e70f6 --- /dev/null +++ b/include/linux/rpmb.h @@ -0,0 +1,250 @@ +/* SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0 */ +/* + * Copyright (C) 2015-2018 Intel Corp. All rights reserved + */ +#ifndef __RPMB_H__ +#define __RPMB_H__ + +#include +#include +#include + +/** + * struct rpmb_frame_jdec - rpmb frame as defined by JDEC specs + * + * @stuff : stuff bytes + * @key_mac : The authentication key or the message authentication + * code (MAC) depending on the request/response type. + * The MAC will be delivered in the last (or the only) + * block of data. + * @data : Data to be written or read by signed access. + * @nonce : Random number generated by the host for the requests + * and copied to the response by the RPMB engine. + * @write_counter: Counter value for the total amount of the successful + * authenticated data write requests made by the host. + * @addr : Address of the data to be programmed to or read + * from the RPMB. Address is the serial number of + * the accessed block (half sector 256B). + * @block_count : Number of blocks (half sectors, 256B) requested to be + * read/programmed. + * @result : Includes information about the status of the write counter + * (valid, expired) and result of the access made to the RPMB. + * @req_resp : Defines the type of request and response to/from the memory. + */ +struct rpmb_frame_jdec { + u8 stuff[196]; + u8 key_mac[32]; + u8 data[256]; + u8 nonce[16]; + __be32 write_counter; + __be16 addr; + __be16 block_count; + __be16 result; + __be16 req_resp; +} __packed; + +#define RPMB_PROGRAM_KEY 0x0001 /* Program RPMB Authentication Key */ +#define RPMB_GET_WRITE_COUNTER 0x0002 /* Read RPMB write counter */ +#define RPMB_WRITE_DATA 0x0003 /* Write data to RPMB partition */ +#define RPMB_READ_DATA 0x0004 /* Read data from RPMB partition */ +#define RPMB_RESULT_READ 0x0005 /* Read result request (Internal) */ + +#define RPMB_REQ2RESP(_OP) ((_OP) << 8) +#define RPMB_RESP2REQ(_OP) ((_OP) >> 8) + +/** + * enum rpmb_op_result - rpmb operation results + * + * @RPMB_ERR_OK : operation successful + * @RPMB_ERR_GENERAL : general failure + * @RPMB_ERR_AUTH : mac doesn't match or ac calculation failure + * @RPMB_ERR_COUNTER : counter doesn't match or counter increment failure + * @RPMB_ERR_ADDRESS : address out of range or wrong address alignment + * @RPMB_ERR_WRITE : data, counter, or result write failure + * @RPMB_ERR_READ : data, counter, or result read failure + * @RPMB_ERR_NO_KEY : authentication key not yet programmed + * + * @RPMB_ERR_COUNTER_EXPIRED: counter expired + */ +enum rpmb_op_result { + RPMB_ERR_OK = 0x0000, + RPMB_ERR_GENERAL = 0x0001, + RPMB_ERR_AUTH = 0x0002, + RPMB_ERR_COUNTER = 0x0003, + RPMB_ERR_ADDRESS = 0x0004, + RPMB_ERR_WRITE = 0x0005, + RPMB_ERR_READ = 0x0006, + RPMB_ERR_NO_KEY = 0x0007, + + RPMB_ERR_COUNTER_EXPIRED = 0x0080 +}; + +/** + * enum rpmb_type - type of underlying storage technology + * + * @RPMB_TYPE_ANY : any type used for search only + * @RPMB_TYPE_EMMC : eMMC (JESD84-B50.1) + * @RPMB_TYPE_UFS : UFS (JESD220) + * @RPMB_TYPE_NVME : NVM Express Revision 1.3a + * @RPMB_TYPE_SIM : Simulation device. + * @RPMB_TYPE_MAX : upper sentinel + */ +enum rpmb_type { + RPMB_TYPE_ANY = 0, + RPMB_TYPE_EMMC, + RPMB_TYPE_UFS, + RPMB_TYPE_NVME, + + RPMB_TYPE_SIM = 0x0100, + RPMB_TYPE_MAX = RPMB_TYPE_SIM | RPMB_TYPE_NVME, +}; + +#define RPMB_TYPE_HW(_type) ((_type) & 0xFF) + +extern struct class rpmb_class; + +#define RPMB_F_WRITE BIT(0) +#define RPMB_F_REL_WRITE BIT(1) + +/** + * struct rpmb_cmd: rpmb access command + * + * @flags: command flags + * 0 - read command + * 1 - write command RPMB_F_WRITE + * 2 - reliable write RPMB_F_REL_WRITE + * @nframes: number of rpmb frames in the command + * @frames: list of rpmb frames + */ +struct rpmb_cmd { + u32 flags; + u32 nframes; + void *frames; +}; + +enum rpmb_auth_method { + RPMB_HMAC_ALGO_SHA_256 = 0, +}; + +/** + * struct rpmb_ops - RPMB ops to be implemented by underlying block device + * + * @cmd_seq : send RPMB command sequence to the RPBM partition + * backed by the storage device to specific + * region(UFS)/target(NVMe) + * @get_capacity : rpmb size in 128K units in for region/target. + * @type : block device type eMMC, UFS, NVMe. + * @block_size : block size in half sectors (1 == 256B) + * @wr_cnt_max : maximal number of blocks that can be + * written in one access. + * @rd_cnt_max : maximal number of blocks that can be + * read in one access. + * @auth_method : rpmb_auth_method + * @dev_id : unique device identifier + * @dev_id_len : unique device identifier length + */ +struct rpmb_ops { + int (*cmd_seq)(struct device *dev, u8 target, + struct rpmb_cmd *cmds, u32 ncmds); + int (*get_capacity)(struct device *dev, u8 target); + u32 type; + u16 block_size; + u16 wr_cnt_max; + u16 rd_cnt_max; + u16 auth_method; + const u8 *dev_id; + size_t dev_id_len; +}; + +/** + * struct rpmb_dev - device which can support RPMB partition + * + * @lock : the device lock + * @dev : device + * @id : device id + * @target : RPMB target/region within the physical device + * @ops : operation exported by block layer + */ +struct rpmb_dev { + struct mutex lock; /* device serialization lock */ + struct device dev; + int id; + u8 target; + const struct rpmb_ops *ops; +}; + +#define to_rpmb_dev(x) container_of((x), struct rpmb_dev, dev) + +#if IS_ENABLED(CONFIG_RPMB) +struct rpmb_dev *rpmb_dev_get(struct rpmb_dev *rdev); +void rpmb_dev_put(struct rpmb_dev *rdev); +struct rpmb_dev *rpmb_dev_find_by_device(struct device *parent, u8 target); +struct rpmb_dev *rpmb_dev_get_by_type(u32 type); +struct rpmb_dev *rpmb_dev_register(struct device *dev, u8 target, + const struct rpmb_ops *ops); +void *rpmb_dev_get_drvdata(const struct rpmb_dev *rdev); +void rpmb_dev_set_drvdata(struct rpmb_dev *rdev, void *data); +int rpmb_dev_unregister(struct rpmb_dev *rdev); +int rpmb_dev_unregister_by_device(struct device *dev, u8 target); +int rpmb_cmd_seq(struct rpmb_dev *rdev, struct rpmb_cmd *cmds, u32 ncmds); +int rpmb_get_capacity(struct rpmb_dev *rdev); + +#else +static inline struct rpmb_dev *rpmb_dev_get(struct rpmb_dev *rdev) +{ + return NULL; +} + +static inline void rpmb_dev_put(struct rpmb_dev *rdev) { } + +static inline struct rpmb_dev *rpmb_dev_find_by_device(struct device *parent, + u8 target) +{ + return NULL; +} + +static inline +struct rpmb_dev *rpmb_dev_get_by_type(enum rpmb_type type) +{ + return NULL; +} + +static inline void *rpmb_dev_get_drvdata(const struct rpmb_dev *rdev) +{ + return NULL; +} + +static inline void rpmb_dev_set_drvdata(struct rpmb_dev *rdev, void *data) +{ +} + +static inline struct rpmb_dev * +rpmb_dev_register(struct device *dev, u8 target, const struct rpmb_ops *ops) +{ + return NULL; +} + +static inline int rpmb_dev_unregister(struct rpmb_dev *dev) +{ + return 0; +} + +static inline int rpmb_dev_unregister_by_device(struct device *dev, u8 target) +{ + return 0; +} + +static inline int rpmb_cmd_seq(struct rpmb_dev *rdev, + struct rpmb_cmd *cmds, u32 ncmds) +{ + return 0; +} + +static inline int rpmb_get_capacity(struct rpmb_dev *rdev) +{ + return 0; +} + +#endif /* CONFIG_RPMB */ + +#endif /* __RPMB_H__ */ From 60095f06e94737b266c248a54838419a896ec20b Mon Sep 17 00:00:00 2001 From: Tomas Winkler Date: Sun, 7 Aug 2016 11:27:08 +0300 Subject: [PATCH 1059/1103] rpmb: enable emmc specific read data fixup For eMMC the block count of the RPMB read operation is not indicated in the original RPMB Data Read Request packet. This might be different then the implementation of other protocol standards. This patch implements a fixup for this behavior. V6: New in the series. V7: Resend V8: Resend. V9: Scan all the frames in the sequence. Change-Id: I34a4aeccbd0294b2c7c83837faa4ba5a54b9be48 Signed-off-by: Tomas Winkler Signed-off-by: Alexander Usyskin Tested-by: Avri Altman --- drivers/char/rpmb/core.c | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/drivers/char/rpmb/core.c b/drivers/char/rpmb/core.c index 69a590106ae1..3bf3f0db54fb 100644 --- a/drivers/char/rpmb/core.c +++ b/drivers/char/rpmb/core.c @@ -36,6 +36,38 @@ void rpmb_dev_put(struct rpmb_dev *rdev) } EXPORT_SYMBOL_GPL(rpmb_dev_put); +/** + * rpmb_cmd_fixup - fixup rpmb command + * + * @rdev: rpmb device + * @cmds: rpmb command list + * @ncmds: number of commands + * + */ +static void rpmb_cmd_fixup(struct rpmb_dev *rdev, + struct rpmb_cmd *cmds, u32 ncmds) +{ + int i; + + if (RPMB_TYPE_HW(rdev->ops->type) != RPMB_TYPE_EMMC) + return; + + /* Fixup RPMB_READ_DATA specific to eMMC + * The block count of the RPMB read operation is not indicated + * in the original RPMB Data Read Request packet. + * This is different then implementation for other protocol + * standards. + */ + for (i = 0; i < ncmds; i++) { + struct rpmb_frame_jdec *frame = cmds[i].frames; + + if (frame->req_resp == cpu_to_be16(RPMB_READ_DATA)) { + dev_dbg(&rdev->dev, "Fixing up READ_DATA frame to block_count=0\n"); + frame->block_count = 0; + } + } +} + /** * rpmb_cmd_seq - send RPMB command sequence * @@ -58,6 +90,7 @@ int rpmb_cmd_seq(struct rpmb_dev *rdev, struct rpmb_cmd *cmds, u32 ncmds) mutex_lock(&rdev->lock); err = -EOPNOTSUPP; if (rdev->ops && rdev->ops->cmd_seq) { + rpmb_cmd_fixup(rdev, cmds, ncmds); err = rdev->ops->cmd_seq(rdev->dev.parent, rdev->target, cmds, ncmds); } From 5aae1bb9e96a61ceeef4816558e893d91a57d5d9 Mon Sep 17 00:00:00 2001 From: Tomas Winkler Date: Sun, 13 Mar 2016 13:36:52 +0200 Subject: [PATCH 1060/1103] rpmb: add sysfs-class ABI documentation V2: resend V3: add more verbose description V4: resend V5: adjust date and kernel version V6: adjust date and kernel version V7: adjust date and kernel version V8: adjust date and kernel version V9: adjust date and kernel version Change-Id: I2d71ca467e5960ca93c904e92cfcf69591a3de59 Signed-off-by: Tomas Winkler Signed-off-by: Alexander Usyskin --- Documentation/ABI/testing/sysfs-class-rpmb | 20 ++++++++++++++++++++ MAINTAINERS | 1 + 2 files changed, 21 insertions(+) create mode 100644 Documentation/ABI/testing/sysfs-class-rpmb diff --git a/Documentation/ABI/testing/sysfs-class-rpmb b/Documentation/ABI/testing/sysfs-class-rpmb new file mode 100644 index 000000000000..a9d45aa77f77 --- /dev/null +++ b/Documentation/ABI/testing/sysfs-class-rpmb @@ -0,0 +1,20 @@ +What: /sys/class/rpmb/ +Date: Jul 2018 +KernelVersion: 4.18 +Contact: Tomas Winkler +Description: + The rpmb/ class sub-directory belongs to RPMB device class. + + Few storage technologies such is EMMC, UFS, and NVMe support + Replay Protected Memory Block (RPMB) hardware partition with + common protocol and similar frame layout. + Such a partition provides authenticated and replay protected access, + hence suitable as a secure storage. + +What: /sys/class/rpmb/rpmbN/ +Date: Jul 2018 +KernelVersion: 4.18 +Contact: Tomas Winkler +Description: + The /sys/class/rpmb/rpmbN directory is created for + each RPMB registered device. diff --git a/MAINTAINERS b/MAINTAINERS index 392cc9ba380d..c7ae6123a827 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -12519,6 +12519,7 @@ L: linux-kernel@vger.kernel.org S: Supported F: drivers/char/rpmb/* F: include/linux/rpmb.h +F: Documentation/ABI/testing/sysfs-class-rpmb RTL2830 MEDIA DRIVER M: Antti Palosaari From de27190fb045357707b49608c0783c700d4c74ab Mon Sep 17 00:00:00 2001 From: Tomas Winkler Date: Sun, 28 Feb 2016 23:59:39 +0200 Subject: [PATCH 1061/1103] char: rpmb: add device attributes Add attribute type that displays underlay storage type technology EMMC, UFS, and attribute id, that displays underlay storage device id. For EMMC this would be content of CID and for UFS serial number from the device descriptor. V2: resend V3: set kernel version to 4.7 V4: update target date to Maj V5: update date and kernel version V6: 1. Add simulation device type 2. Update date and kernel version 3. Use binary attribute for id 4. use simple sprintf instead of scnprintf 5. Add more verbose documenation V7: resend V8: update date and kernel version V9: 1. update date and kernel version 2. add new rd_cnt_max and wr_cnt_max attributes. 3. Use SIM as a suffix of the device type. Change-Id: If25a96f1371d8fea5820f6e06366bc0945d32faa Signed-off-by: Tomas Winkler Signed-off-by: Alexander Usyskin --- Documentation/ABI/testing/sysfs-class-rpmb | 37 ++++++++++ drivers/char/rpmb/core.c | 84 ++++++++++++++++++++++ 2 files changed, 121 insertions(+) diff --git a/Documentation/ABI/testing/sysfs-class-rpmb b/Documentation/ABI/testing/sysfs-class-rpmb index a9d45aa77f77..d0540be7db19 100644 --- a/Documentation/ABI/testing/sysfs-class-rpmb +++ b/Documentation/ABI/testing/sysfs-class-rpmb @@ -18,3 +18,40 @@ Contact: Tomas Winkler Description: The /sys/class/rpmb/rpmbN directory is created for each RPMB registered device. + +What: /sys/class/rpmb/rpmbN/type +Date: Jul 2018 +KernelVersion: 4.18 +Contact: Tomas Winkler +Description: + The /sys/class/rpmb/rpmbN/type file contains device + underlying storage type technology: EMMC, UFS, NVMe. + In case of simulated device it will have :SIM suffix + i.e EMMC:SIM. + +What: /sys/class/rpmb/rpmbN/id +Date: Jul 2018 +KernelVersion: 4.18 +Contact: Tomas Winkler +Description: + The /sys/class/rpmb/rpmbN/id file contains unique device id + in a binary form as defined by underlying storage device. + In case of multiple RPMB devices a user can determine correct + device. + The content can be parsed according the storage device type. + +What: /sys/class/rpmb/rpmbN/wr_cnt_max +Date: Jul 2018 +KernelVersion: 4.18 +Contact: Tomas Winkler +Description: + The /sys/class/rpmb/rpmbN/wr_cnt_max file contains + number of blocks that can be reliable written in a single request. + +What: /sys/class/rpmb/rpmbN/rd_cnt_max +Date: Jul 2018 +KernelVersion: 4.18 +Contact: Tomas Winkler +Description: + The /sys/class/rpmb/rpmbN/rd_cnt_max file contains + number of blocks that can be read in a single request. diff --git a/drivers/char/rpmb/core.c b/drivers/char/rpmb/core.c index 3bf3f0db54fb..41a249c0c58f 100644 --- a/drivers/char/rpmb/core.c +++ b/drivers/char/rpmb/core.c @@ -214,6 +214,88 @@ struct rpmb_dev *rpmb_dev_find_by_device(struct device *parent, u8 target) } EXPORT_SYMBOL_GPL(rpmb_dev_find_by_device); +static ssize_t type_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct rpmb_dev *rdev = to_rpmb_dev(dev); + const char *sim; + ssize_t ret; + + sim = (rdev->ops->type & RPMB_TYPE_SIM) ? ":SIM" : ""; + switch (RPMB_TYPE_HW(rdev->ops->type)) { + case RPMB_TYPE_EMMC: + ret = sprintf(buf, "EMMC%s\n", sim); + break; + case RPMB_TYPE_UFS: + ret = sprintf(buf, "UFS%s\n", sim); + break; + case RPMB_TYPE_NVME: + ret = sprintf(buf, "NVMe%s\n", sim); + break; + default: + ret = sprintf(buf, "UNKNOWN\n"); + break; + } + + return ret; +} +static DEVICE_ATTR_RO(type); + +static ssize_t id_read(struct file *file, struct kobject *kobj, + struct bin_attribute *attr, char *buf, + loff_t off, size_t count) +{ + struct device *dev = kobj_to_dev(kobj); + struct rpmb_dev *rdev = to_rpmb_dev(dev); + size_t sz = min_t(size_t, rdev->ops->dev_id_len, PAGE_SIZE); + + if (!rdev->ops->dev_id) + return 0; + + return memory_read_from_buffer(buf, count, &off, rdev->ops->dev_id, sz); +} +static BIN_ATTR_RO(id, 0); + +static ssize_t wr_cnt_max_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct rpmb_dev *rdev = to_rpmb_dev(dev); + + return sprintf(buf, "%u\n", rdev->ops->wr_cnt_max); +} +static DEVICE_ATTR_RO(wr_cnt_max); + +static ssize_t rd_cnt_max_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct rpmb_dev *rdev = to_rpmb_dev(dev); + + return sprintf(buf, "%u\n", rdev->ops->rd_cnt_max); +} +static DEVICE_ATTR_RO(rd_cnt_max); + +static struct attribute *rpmb_attrs[] = { + &dev_attr_type.attr, + &dev_attr_wr_cnt_max.attr, + &dev_attr_rd_cnt_max.attr, + NULL, +}; + +static struct bin_attribute *rpmb_bin_attributes[] = { + &bin_attr_id, + NULL, +}; + +static struct attribute_group rpmb_attr_group = { + .attrs = rpmb_attrs, + .bin_attrs = rpmb_bin_attributes, +}; + +static const struct attribute_group *rpmb_attr_groups[] = { + &rpmb_attr_group, + NULL +}; + /** * rpmb_dev_unregister - unregister RPMB partition from the RPMB subsystem * @@ -329,6 +411,8 @@ struct rpmb_dev *rpmb_dev_register(struct device *dev, u8 target, dev_set_name(&rdev->dev, "rpmb%d", id); rdev->dev.class = &rpmb_class; rdev->dev.parent = dev; + rdev->dev.groups = rpmb_attr_groups; + ret = device_register(&rdev->dev); if (ret) goto exit; From 43e26fae1b31a74222e80bb813932a7cd6a17fdc Mon Sep 17 00:00:00 2001 From: Tomas Winkler Date: Thu, 16 Jul 2015 12:29:50 +0300 Subject: [PATCH 1062/1103] char: rpmb: provide a user space interface The user space API is achieved via two synchronous IOCTLs. Simplified one, RPMB_IOC_REQ_CMD, were read result cycles is performed by the framework on behalf the user and second, RPMB_IOC_SEQ_CMD where the whole RPMB sequence including RESULT_READ is supplied by the caller. The latter is intended for easier adjusting of the applications that use MMC_IOC_MULTI_CMD ioctl. V2: use memdup_user V3: commit message fix V4: resend V5: 1. Add RPMB_IOC_SEQ_CMD API. 2. Export uapi rpmb.h header V6: 1. Remove #include . 2. Add ioctl documentation. V7: 1. copy_from_user the value of the frame pointer. 2. Fix possible macro side-effect due to macro argument reuse. V8: 1. Fix kdoc errors 2. Move IOCTL to a different range due to conflict 3. Change license to dual BSD/GPL V9: 1. Add version and capability ioctls and drop the request ioctl 2. Use zero based frame count: 0 means only meted are in a frame. 2. Add SPDX identifiers. 3. Fix comment typo in uapi/linux/rpmb.h V10: 1. Rebase on 4.18 V11: 1. Rebase on 4.19 Change-Id: I00f2b5d5c92982fa2a3814a8bc56a3fecd19456f Signed-off-by: Tomas Winkler Signed-off-by: Alexander Usyskin Tested-by: Avri Altman --- Documentation/ioctl/ioctl-number.txt | 1 + MAINTAINERS | 1 + drivers/char/rpmb/Kconfig | 7 + drivers/char/rpmb/Makefile | 1 + drivers/char/rpmb/cdev.c | 296 +++++++++++++++++++++++++++ drivers/char/rpmb/core.c | 9 +- drivers/char/rpmb/rpmb-cdev.h | 17 ++ include/linux/rpmb.h | 107 +--------- include/uapi/linux/rpmb.h | 192 +++++++++++++++++ 9 files changed, 532 insertions(+), 99 deletions(-) create mode 100644 drivers/char/rpmb/cdev.c create mode 100644 drivers/char/rpmb/rpmb-cdev.h create mode 100644 include/uapi/linux/rpmb.h diff --git a/Documentation/ioctl/ioctl-number.txt b/Documentation/ioctl/ioctl-number.txt index 13a7c999c04a..ae2f081324f4 100644 --- a/Documentation/ioctl/ioctl-number.txt +++ b/Documentation/ioctl/ioctl-number.txt @@ -324,6 +324,7 @@ Code Seq#(hex) Include File Comments 0xB3 00 linux/mmc/ioctl.h 0xB4 00-0F linux/gpio.h 0xB5 00-0F uapi/linux/rpmsg.h +0xB5 80-8F linux/uapi/linux/rpmb.h 0xB6 all linux/fpga-dfl.h 0xC0 00-0F linux/usb/iowarrior.h 0xCA 00-0F uapi/misc/cxl.h diff --git a/MAINTAINERS b/MAINTAINERS index c7ae6123a827..046a93f0dc3f 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -12518,6 +12518,7 @@ M: Tomas Winkler L: linux-kernel@vger.kernel.org S: Supported F: drivers/char/rpmb/* +F: include/uapi/linux/rpmb.h F: include/linux/rpmb.h F: Documentation/ABI/testing/sysfs-class-rpmb diff --git a/drivers/char/rpmb/Kconfig b/drivers/char/rpmb/Kconfig index b5cd02de91bb..cfecb1fccfc0 100644 --- a/drivers/char/rpmb/Kconfig +++ b/drivers/char/rpmb/Kconfig @@ -7,3 +7,10 @@ config RPMB access RPMB partition. If unsure, select N. + +config RPMB_INTF_DEV + bool "RPMB character device interface /dev/rpmbN" + depends on RPMB + help + Say yes here if you want to access RPMB from user space + via character device interface /dev/rpmb%d diff --git a/drivers/char/rpmb/Makefile b/drivers/char/rpmb/Makefile index badc1cd9428b..c171a5cfaed3 100644 --- a/drivers/char/rpmb/Makefile +++ b/drivers/char/rpmb/Makefile @@ -1,5 +1,6 @@ # SPDX-License-Identifier: GPL-2.0 obj-$(CONFIG_RPMB) += rpmb.o rpmb-objs += core.o +rpmb-$(CONFIG_RPMB_INTF_DEV) += cdev.o ccflags-y += -D__CHECK_ENDIAN__ diff --git a/drivers/char/rpmb/cdev.c b/drivers/char/rpmb/cdev.c new file mode 100644 index 000000000000..bdac3894b111 --- /dev/null +++ b/drivers/char/rpmb/cdev.c @@ -0,0 +1,296 @@ +// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0 +/* + * Copyright(c) 2015 - 2018 Intel Corporation. All rights reserved. + */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include + +#include + +#include "rpmb-cdev.h" + +static dev_t rpmb_devt; +#define RPMB_MAX_DEVS MINORMASK + +#define RPMB_DEV_OPEN 0 /** single open bit (position) */ +/* from MMC_IOC_MAX_CMDS */ +#define RPMB_MAX_FRAMES 255 + +/** + * rpmb_open - the open function + * + * @inode: pointer to inode structure + * @fp: pointer to file structure + * + * Return: 0 on success, <0 on error + */ +static int rpmb_open(struct inode *inode, struct file *fp) +{ + struct rpmb_dev *rdev; + + rdev = container_of(inode->i_cdev, struct rpmb_dev, cdev); + if (!rdev) + return -ENODEV; + + /* the rpmb is single open! */ + if (test_and_set_bit(RPMB_DEV_OPEN, &rdev->status)) + return -EBUSY; + + mutex_lock(&rdev->lock); + + fp->private_data = rdev; + + mutex_unlock(&rdev->lock); + + return nonseekable_open(inode, fp); +} + +/** + * rpmb_release - the cdev release function + * + * @inode: pointer to inode structure + * @fp: pointer to file structure + * + * Return: 0 always. + */ +static int rpmb_release(struct inode *inode, struct file *fp) +{ + struct rpmb_dev *rdev = fp->private_data; + + clear_bit(RPMB_DEV_OPEN, &rdev->status); + + return 0; +} + +/** + * rpmb_cmd_copy_from_user - copy rpmb command from the user space + * + * @cmd: internal cmd structure + * @ucmd: user space cmd structure + * + * Return: 0 on success, <0 on error + */ +static int rpmb_cmd_copy_from_user(struct rpmb_cmd *cmd, + struct rpmb_ioc_cmd __user *ucmd) +{ + struct rpmb_frame *frames; + u64 frames_ptr; + + if (get_user(cmd->flags, &ucmd->flags)) + return -EFAULT; + + if (get_user(cmd->nframes, &ucmd->nframes)) + return -EFAULT; + + if (cmd->nframes > RPMB_MAX_FRAMES) + return -EOVERFLOW; + + /* some archs have issues with 64bit get_user */ + if (copy_from_user(&frames_ptr, &ucmd->frames_ptr, sizeof(frames_ptr))) + return -EFAULT; + + frames = memdup_user(u64_to_user_ptr(frames_ptr), + rpmb_ioc_frames_len_jdec(cmd->nframes)); + if (IS_ERR(frames)) + return PTR_ERR(frames); + + cmd->frames = frames; + return 0; +} + +/** + * rpmb_cmd_copy_to_user - copy rpmb command to the user space + * + * @ucmd: user space cmd structure + * @cmd: internal cmd structure + * + * Return: 0 on success, <0 on error + */ +static int rpmb_cmd_copy_to_user(struct rpmb_ioc_cmd __user *ucmd, + struct rpmb_cmd *cmd) +{ + u64 frames_ptr; + + if (copy_from_user(&frames_ptr, &ucmd->frames_ptr, sizeof(frames_ptr))) + return -EFAULT; + + /* some archs have issues with 64bit get_user */ + if (copy_to_user(u64_to_user_ptr(frames_ptr), cmd->frames, + rpmb_ioc_frames_len_jdec(cmd->nframes))) + return -EFAULT; + + return 0; +} + +/** + * rpmb_ioctl_seq_cmd - issue an rpmb command sequence + * + * @rdev: rpmb device + * @ptr: rpmb cmd sequence + * + * RPMB_IOC_SEQ_CMD handler + * + * Return: 0 on success, <0 on error + */ +static long rpmb_ioctl_seq_cmd(struct rpmb_dev *rdev, + struct rpmb_ioc_seq_cmd __user *ptr) +{ + __u64 ncmds; + struct rpmb_cmd *cmds; + struct rpmb_ioc_cmd __user *ucmds; + + int i; + int ret; + + /* The caller must have CAP_SYS_RAWIO, like mmc ioctl */ + if (!capable(CAP_SYS_RAWIO)) + return -EPERM; + + /* some archs have issues with 64bit get_user */ + if (copy_from_user(&ncmds, &ptr->num_of_cmds, sizeof(ncmds))) + return -EFAULT; + + if (ncmds > 3) { + dev_err(&rdev->dev, "supporting up to 3 packets (%llu)\n", + ncmds); + return -EINVAL; + } + + cmds = kcalloc(ncmds, sizeof(*cmds), GFP_KERNEL); + if (!cmds) + return -ENOMEM; + + ucmds = (struct rpmb_ioc_cmd __user *)ptr->cmds; + for (i = 0; i < ncmds; i++) { + ret = rpmb_cmd_copy_from_user(&cmds[i], &ucmds[i]); + if (ret) + goto out; + } + + ret = rpmb_cmd_seq(rdev, cmds, ncmds); + if (ret) + goto out; + + for (i = 0; i < ncmds; i++) { + ret = rpmb_cmd_copy_to_user(&ucmds[i], &cmds[i]); + if (ret) + goto out; + } +out: + for (i = 0; i < ncmds; i++) + kfree(cmds[i].frames); + kfree(cmds); + return ret; +} + +static long rpmb_ioctl_ver_cmd(struct rpmb_dev *rdev, + struct rpmb_ioc_ver_cmd __user *ptr) +{ + struct rpmb_ioc_ver_cmd ver = { + .api_version = RPMB_API_VERSION, + }; + + return copy_to_user(ptr, &ver, sizeof(ver)) ? -EFAULT : 0; +} + +static long rpmb_ioctl_cap_cmd(struct rpmb_dev *rdev, + struct rpmb_ioc_cap_cmd __user *ptr) +{ + struct rpmb_ioc_cap_cmd cap; + + cap.device_type = rdev->ops->type; + cap.target = rdev->target; + cap.block_size = rdev->ops->block_size; + cap.wr_cnt_max = rdev->ops->wr_cnt_max; + cap.rd_cnt_max = rdev->ops->rd_cnt_max; + cap.auth_method = rdev->ops->auth_method; + cap.capacity = rpmb_get_capacity(rdev); + cap.reserved = 0; + + return copy_to_user(ptr, &cap, sizeof(cap)) ? -EFAULT : 0; +} + +/** + * rpmb_ioctl - rpmb ioctl dispatcher + * + * @fp: a file pointer + * @cmd: ioctl command RPMB_IOC_SEQ_CMD RPMB_IOC_VER_CMD RPMB_IOC_CAP_CMD + * @arg: ioctl data: rpmb_ioc_ver_cmd rpmb_ioc_cap_cmd pmb_ioc_seq_cmd + * + * Return: 0 on success; < 0 on error + */ +static long rpmb_ioctl(struct file *fp, unsigned int cmd, unsigned long arg) +{ + struct rpmb_dev *rdev = fp->private_data; + void __user *ptr = (void __user *)arg; + + switch (cmd) { + case RPMB_IOC_VER_CMD: + return rpmb_ioctl_ver_cmd(rdev, ptr); + case RPMB_IOC_CAP_CMD: + return rpmb_ioctl_cap_cmd(rdev, ptr); + case RPMB_IOC_SEQ_CMD: + return rpmb_ioctl_seq_cmd(rdev, ptr); + default: + dev_err(&rdev->dev, "unsupported ioctl 0x%x.\n", cmd); + return -ENOIOCTLCMD; + } +} + +#ifdef CONFIG_COMPAT +static long rpmb_compat_ioctl(struct file *fp, unsigned int cmd, + unsigned long arg) +{ + return rpmb_ioctl(fp, cmd, (unsigned long)compat_ptr(arg)); +} +#endif /* CONFIG_COMPAT */ + +static const struct file_operations rpmb_fops = { + .open = rpmb_open, + .release = rpmb_release, + .unlocked_ioctl = rpmb_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl = rpmb_compat_ioctl, +#endif + .owner = THIS_MODULE, + .llseek = noop_llseek, +}; + +void rpmb_cdev_prepare(struct rpmb_dev *rdev) +{ + rdev->dev.devt = MKDEV(MAJOR(rpmb_devt), rdev->id); + rdev->cdev.owner = THIS_MODULE; + cdev_init(&rdev->cdev, &rpmb_fops); +} + +void rpmb_cdev_add(struct rpmb_dev *rdev) +{ + cdev_add(&rdev->cdev, rdev->dev.devt, 1); +} + +void rpmb_cdev_del(struct rpmb_dev *rdev) +{ + if (rdev->dev.devt) + cdev_del(&rdev->cdev); +} + +int __init rpmb_cdev_init(void) +{ + int ret; + + ret = alloc_chrdev_region(&rpmb_devt, 0, RPMB_MAX_DEVS, "rpmb"); + if (ret < 0) + pr_err("unable to allocate char dev region\n"); + + return ret; +} + +void __exit rpmb_cdev_exit(void) +{ + unregister_chrdev_region(rpmb_devt, RPMB_MAX_DEVS); +} diff --git a/drivers/char/rpmb/core.c b/drivers/char/rpmb/core.c index 41a249c0c58f..e02c12b8046c 100644 --- a/drivers/char/rpmb/core.c +++ b/drivers/char/rpmb/core.c @@ -11,6 +11,7 @@ #include #include +#include "rpmb-cdev.h" static DEFINE_IDA(rpmb_ida); @@ -307,6 +308,7 @@ int rpmb_dev_unregister(struct rpmb_dev *rdev) return -EINVAL; mutex_lock(&rdev->lock); + rpmb_cdev_del(rdev); device_del(&rdev->dev); mutex_unlock(&rdev->lock); @@ -413,10 +415,14 @@ struct rpmb_dev *rpmb_dev_register(struct device *dev, u8 target, rdev->dev.parent = dev; rdev->dev.groups = rpmb_attr_groups; + rpmb_cdev_prepare(rdev); + ret = device_register(&rdev->dev); if (ret) goto exit; + rpmb_cdev_add(rdev); + dev_dbg(&rdev->dev, "registered device\n"); return rdev; @@ -433,11 +439,12 @@ static int __init rpmb_init(void) { ida_init(&rpmb_ida); class_register(&rpmb_class); - return 0; + return rpmb_cdev_init(); } static void __exit rpmb_exit(void) { + rpmb_cdev_exit(); class_unregister(&rpmb_class); ida_destroy(&rpmb_ida); } diff --git a/drivers/char/rpmb/rpmb-cdev.h b/drivers/char/rpmb/rpmb-cdev.h new file mode 100644 index 000000000000..e59ff0c05e9d --- /dev/null +++ b/drivers/char/rpmb/rpmb-cdev.h @@ -0,0 +1,17 @@ +/* SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0 */ +/* + * Copyright (C) 2015-2018 Intel Corp. All rights reserved + */ +#ifdef CONFIG_RPMB_INTF_DEV +int __init rpmb_cdev_init(void); +void __exit rpmb_cdev_exit(void); +void rpmb_cdev_prepare(struct rpmb_dev *rdev); +void rpmb_cdev_add(struct rpmb_dev *rdev); +void rpmb_cdev_del(struct rpmb_dev *rdev); +#else +static inline int __init rpmb_cdev_init(void) { return 0; } +static inline void __exit rpmb_cdev_exit(void) {} +static inline void rpmb_cdev_prepare(struct rpmb_dev *rdev) {} +static inline void rpmb_cdev_add(struct rpmb_dev *rdev) {} +static inline void rpmb_cdev_del(struct rpmb_dev *rdev) {} +#endif /* CONFIG_RPMB_INTF_DEV */ diff --git a/include/linux/rpmb.h b/include/linux/rpmb.h index 6acd9b1e70f6..fb535336d50f 100644 --- a/include/linux/rpmb.h +++ b/include/linux/rpmb.h @@ -7,105 +7,14 @@ #include #include +#include +#include #include -/** - * struct rpmb_frame_jdec - rpmb frame as defined by JDEC specs - * - * @stuff : stuff bytes - * @key_mac : The authentication key or the message authentication - * code (MAC) depending on the request/response type. - * The MAC will be delivered in the last (or the only) - * block of data. - * @data : Data to be written or read by signed access. - * @nonce : Random number generated by the host for the requests - * and copied to the response by the RPMB engine. - * @write_counter: Counter value for the total amount of the successful - * authenticated data write requests made by the host. - * @addr : Address of the data to be programmed to or read - * from the RPMB. Address is the serial number of - * the accessed block (half sector 256B). - * @block_count : Number of blocks (half sectors, 256B) requested to be - * read/programmed. - * @result : Includes information about the status of the write counter - * (valid, expired) and result of the access made to the RPMB. - * @req_resp : Defines the type of request and response to/from the memory. - */ -struct rpmb_frame_jdec { - u8 stuff[196]; - u8 key_mac[32]; - u8 data[256]; - u8 nonce[16]; - __be32 write_counter; - __be16 addr; - __be16 block_count; - __be16 result; - __be16 req_resp; -} __packed; - -#define RPMB_PROGRAM_KEY 0x0001 /* Program RPMB Authentication Key */ -#define RPMB_GET_WRITE_COUNTER 0x0002 /* Read RPMB write counter */ -#define RPMB_WRITE_DATA 0x0003 /* Write data to RPMB partition */ -#define RPMB_READ_DATA 0x0004 /* Read data from RPMB partition */ -#define RPMB_RESULT_READ 0x0005 /* Read result request (Internal) */ - -#define RPMB_REQ2RESP(_OP) ((_OP) << 8) -#define RPMB_RESP2REQ(_OP) ((_OP) >> 8) - -/** - * enum rpmb_op_result - rpmb operation results - * - * @RPMB_ERR_OK : operation successful - * @RPMB_ERR_GENERAL : general failure - * @RPMB_ERR_AUTH : mac doesn't match or ac calculation failure - * @RPMB_ERR_COUNTER : counter doesn't match or counter increment failure - * @RPMB_ERR_ADDRESS : address out of range or wrong address alignment - * @RPMB_ERR_WRITE : data, counter, or result write failure - * @RPMB_ERR_READ : data, counter, or result read failure - * @RPMB_ERR_NO_KEY : authentication key not yet programmed - * - * @RPMB_ERR_COUNTER_EXPIRED: counter expired - */ -enum rpmb_op_result { - RPMB_ERR_OK = 0x0000, - RPMB_ERR_GENERAL = 0x0001, - RPMB_ERR_AUTH = 0x0002, - RPMB_ERR_COUNTER = 0x0003, - RPMB_ERR_ADDRESS = 0x0004, - RPMB_ERR_WRITE = 0x0005, - RPMB_ERR_READ = 0x0006, - RPMB_ERR_NO_KEY = 0x0007, - - RPMB_ERR_COUNTER_EXPIRED = 0x0080 -}; - -/** - * enum rpmb_type - type of underlying storage technology - * - * @RPMB_TYPE_ANY : any type used for search only - * @RPMB_TYPE_EMMC : eMMC (JESD84-B50.1) - * @RPMB_TYPE_UFS : UFS (JESD220) - * @RPMB_TYPE_NVME : NVM Express Revision 1.3a - * @RPMB_TYPE_SIM : Simulation device. - * @RPMB_TYPE_MAX : upper sentinel - */ -enum rpmb_type { - RPMB_TYPE_ANY = 0, - RPMB_TYPE_EMMC, - RPMB_TYPE_UFS, - RPMB_TYPE_NVME, - - RPMB_TYPE_SIM = 0x0100, - RPMB_TYPE_MAX = RPMB_TYPE_SIM | RPMB_TYPE_NVME, -}; - -#define RPMB_TYPE_HW(_type) ((_type) & 0xFF) +#define RPMB_API_VERSION 0x80000001 extern struct class rpmb_class; -#define RPMB_F_WRITE BIT(0) -#define RPMB_F_REL_WRITE BIT(1) - /** * struct rpmb_cmd: rpmb access command * @@ -122,10 +31,6 @@ struct rpmb_cmd { void *frames; }; -enum rpmb_auth_method { - RPMB_HMAC_ALGO_SHA_256 = 0, -}; - /** * struct rpmb_ops - RPMB ops to be implemented by underlying block device * @@ -163,6 +68,8 @@ struct rpmb_ops { * @dev : device * @id : device id * @target : RPMB target/region within the physical device + * @cdev : character dev + * @status : device status * @ops : operation exported by block layer */ struct rpmb_dev { @@ -170,6 +77,10 @@ struct rpmb_dev { struct device dev; int id; u8 target; +#ifdef CONFIG_RPMB_INTF_DEV + struct cdev cdev; + unsigned long status; +#endif /* CONFIG_RPMB_INTF_DEV */ const struct rpmb_ops *ops; }; diff --git a/include/uapi/linux/rpmb.h b/include/uapi/linux/rpmb.h new file mode 100644 index 000000000000..d304701cd258 --- /dev/null +++ b/include/uapi/linux/rpmb.h @@ -0,0 +1,192 @@ +/* SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note) OR BSD-3-Clause) */ +/* + * Copyright (C) 2015-2018 Intel Corp. All rights reserved + */ +#ifndef _UAPI_LINUX_RPMB_H_ +#define _UAPI_LINUX_RPMB_H_ + +#include + +/** + * enum rpmb_type - type of underlying storage technology + * + * @RPMB_TYPE_ANY : any type used for search only + * @RPMB_TYPE_EMMC : eMMC (JESD84-B50.1) + * @RPMB_TYPE_UFS : UFS (JESD220) + * @RPMB_TYPE_NVME : NVM Express Revision 1.3a + * @RPMB_TYPE_SIM : Simulation device. + * @RPMB_TYPE_MAX : upper sentinel + */ +enum rpmb_type { + RPMB_TYPE_ANY = 0, + RPMB_TYPE_EMMC, + RPMB_TYPE_UFS, + RPMB_TYPE_NVME, + + RPMB_TYPE_SIM = 0x0100, + RPMB_TYPE_MAX = RPMB_TYPE_SIM | RPMB_TYPE_NVME, +}; + +#define RPMB_TYPE_HW(_type) ((_type) & 0xFF) + +/** + * struct rpmb_frame_jdec - rpmb frame as defined by JDEC specs + * + * @stuff : stuff bytes + * @key_mac : The authentication key or the message authentication + * code (MAC) depending on the request/response type. + * The MAC will be delivered in the last (or the only) + * block of data. + * @data : Data to be written or read by signed access. + * @nonce : Random number generated by the host for the requests + * and copied to the response by the RPMB engine. + * @write_counter: Counter value for the total amount of the successful + * authenticated data write requests made by the host. + * @addr : Address of the data to be programmed to or read + * from the RPMB. Address is the serial number of + * the accessed block (half sector 256B). + * @block_count : Number of blocks (half sectors, 256B) requested to be + * read/programmed. + * @result : Includes information about the status of the write counter + * (valid, expired) and result of the access made to the RPMB. + * @req_resp : Defines the type of request and response to/from the memory. + */ +struct rpmb_frame_jdec { + __u8 stuff[196]; + __u8 key_mac[32]; + __u8 data[256]; + __u8 nonce[16]; + __be32 write_counter; + __be16 addr; + __be16 block_count; + __be16 result; + __be16 req_resp; +} __attribute__((packed)); + +/* length of the part of the frame used for HMAC computation */ +#define rpmb_jdec_hmac_data_len \ + (sizeof(struct rpmb_frame_jdec) - \ + offsetof(struct rpmb_frame_jdec, data)) + +#define RPMB_PROGRAM_KEY 0x0001 /* Program RPMB Authentication Key */ +#define RPMB_GET_WRITE_COUNTER 0x0002 /* Read RPMB write counter */ +#define RPMB_WRITE_DATA 0x0003 /* Write data to RPMB partition */ +#define RPMB_READ_DATA 0x0004 /* Read data from RPMB partition */ +#define RPMB_RESULT_READ 0x0005 /* Read result request (Internal) */ + +#define RPMB_REQ2RESP(_OP) ((_OP) << 8) +#define RPMB_RESP2REQ(_OP) ((_OP) >> 8) + +/** + * enum rpmb_op_result - rpmb operation results + * + * @RPMB_ERR_OK: operation successful + * @RPMB_ERR_GENERAL: general failure + * @RPMB_ERR_AUTH: mac doesn't match or ac calculation failure + * @RPMB_ERR_COUNTER: counter doesn't match or counter increment failure + * @RPMB_ERR_ADDRESS: address out of range or wrong address alignment + * @RPMB_ERR_WRITE: data, counter, or result write failure + * @RPMB_ERR_READ: data, counter, or result read failure + * @RPMB_ERR_NO_KEY: authentication key not yet programmed + * + * @RPMB_ERR_COUNTER_EXPIRED: counter expired + */ +enum rpmb_op_result { + RPMB_ERR_OK = 0x0000, + RPMB_ERR_GENERAL = 0x0001, + RPMB_ERR_AUTH = 0x0002, + RPMB_ERR_COUNTER = 0x0003, + RPMB_ERR_ADDRESS = 0x0004, + RPMB_ERR_WRITE = 0x0005, + RPMB_ERR_READ = 0x0006, + RPMB_ERR_NO_KEY = 0x0007, + + RPMB_ERR_COUNTER_EXPIRED = 0x0080 +}; + +#define RPMB_F_READ 0UL +#define RPMB_F_WRITE (1UL << 0) +#define RPMB_F_REL_WRITE (1UL << 1) + +enum rpmb_auth_method { + RPMB_HMAC_ALGO_SHA_256 = 0, +}; + +/** + * struct rpmb_cmd - rpmb access command + * + * @flags: command flags + * 0 - read command + * 1 - write command RPMB_F_WRITE + * 2 - reliable write RPMB_F_REL_WRITE + * @nframes: number of rpmb data frames in the command. + * 0 means 1 frame with meta data only. + * @frames_ptr: a pointer to the list of rpmb frames + */ +struct rpmb_ioc_cmd { + __u32 flags; + __u32 nframes; + __aligned_u64 frames_ptr; +}; + +#define rpmb_ioc_cmd_set_frames(_cmd, _ptr) \ + (_cmd).frames_ptr = (__aligned_u64)(intptr_t)(_ptr) + +#define rpmb_ioc_cmd_set(_cmd, _flags, _ptr, _n) do { \ + struct rpmb_ioc_cmd *icmd = &(_cmd); \ + icmd->flags = (_flags); \ + icmd->nframes = (_n); \ + icmd->frames_ptr = (__aligned_u64)(intptr_t)(_ptr); \ +} while (0) + +#define rpmb_ioc_frames_len_jdec(_n) \ + (((_n) ?: 1) * sizeof(struct rpmb_frame_jdec)) + +/** + * struct rpmb_ioc_seq_cmd - rpmb command sequence + * + * @num_of_cmds: number of commands + * @cmds: list of rpmb commands + */ +struct rpmb_ioc_seq_cmd { + __u64 num_of_cmds; + struct rpmb_ioc_cmd cmds[0]; +} __attribute__((packed)); + +/** + * struct rpmb_ioc_ver_cmd - rpmb api version + * + * @api_version: rpmb API version. + */ +struct rpmb_ioc_ver_cmd { + __u32 api_version; +} __attribute__((packed)); + +/** + * struct rpmb_ioc_ver_cmd - rpmb api version + * + * @device_type: underlying storage device type + * @target: rpmb target/region within RPMB partition. + * @capacity: storage capacity + * @block_size: storage data block size + * @wr_cnt_max: maximal number of block that can be written in a single request. + * @rd_cnt_max: maximal number of block that can be read in a single request. + * @auth_method: authentication method: currently always HMAC_SHA_256 + * @reserved: reserved to align to 4 bytes. + */ +struct rpmb_ioc_cap_cmd { + __u16 device_type; + __u16 target; + __u16 capacity; + __u16 block_size; + __u16 wr_cnt_max; + __u16 rd_cnt_max; + __u16 auth_method; + __u16 reserved; +} __attribute__((packed)); + +#define RPMB_IOC_VER_CMD _IOR(0xB5, 80, struct rpmb_ioc_ver_cmd) +#define RPMB_IOC_CAP_CMD _IOR(0xB5, 81, struct rpmb_ioc_cap_cmd) +#define RPMB_IOC_SEQ_CMD _IOWR(0xB5, 82, struct rpmb_ioc_seq_cmd) + +#endif /* _UAPI_LINUX_RPMB_H_ */ From d6dc7ae5b75e35466b990a9b8b452f5cd547b176 Mon Sep 17 00:00:00 2001 From: Tomas Winkler Date: Sun, 28 Feb 2016 10:36:13 +0200 Subject: [PATCH 1063/1103] char: rpmb: add RPMB simulation device The RPMB partition simulation device is a virtual device that provides simulation of the RPMB protocol and use kernel memory as storage. Be aware it doesn't promise any real security. This driver is suitable only for testing of the RPMB subsystem or RPMB applications prior to RPMB key provisioning, as RPMB key programming can be performed only once in the life time of the storage device. The module currently supports two configuration options via module parameters 1. max_wr_blks: for specifying max blocks that can be written in a single command 2. daunits: used to set storage capacity in 128K units. V2: remove .owner setting, it is set automatically V3: 1. Add shutdown handler (similar to ufshcd) 2. Commit message fix V4: Use select RPMB in Kconfg to ensure valid configuration. V5: Revamp the code using the sequence command. V6: 1. Be more verbose about some errors, after all this is a testing module. 2. Fix RPMB_READ_DATA: a. The number of blocks for eMMC request frame should be 0 b. Fix missing return before bailing on error c. Copy all the frames back 3. Fix RPMB_WRITE_DATA: a. Compute MAC on result packet b. Also address should be set in the result frame. 4. Remove platform device 5. Update the commit message V7: Resend. V8: 1. drop use SHASH_DESC_ON_STACK, variable length arrays are problematic in C. 2. Fix typos. 3. Set out_frames in case of not programmed keys otherwise read cycle won't return correct answer. V9: 1. Add SPDX identifiers. 2. Adjust to new unregister API. 3. Adjust to the new zero based RPMB frame count. Change-Id: Idd0a414c4ce157631f69586f1ed3a6e88cd8a4ee Signed-off-by: Tomas Winkler Signed-off-by: Alexander Usyskin --- drivers/char/rpmb/Kconfig | 16 + drivers/char/rpmb/Makefile | 1 + drivers/char/rpmb/rpmb_sim.c | 715 +++++++++++++++++++++++++++++++++++ 3 files changed, 732 insertions(+) create mode 100644 drivers/char/rpmb/rpmb_sim.c diff --git a/drivers/char/rpmb/Kconfig b/drivers/char/rpmb/Kconfig index cfecb1fccfc0..c069664eec92 100644 --- a/drivers/char/rpmb/Kconfig +++ b/drivers/char/rpmb/Kconfig @@ -14,3 +14,19 @@ config RPMB_INTF_DEV help Say yes here if you want to access RPMB from user space via character device interface /dev/rpmb%d + +config RPMB_SIM + tristate "RPMB partition device simulator" + default n + select RPMB + select CRYPTO_SHA256 + select CRYPTO_HMAC + help + RPMB partition simulation device is a virtual device that + provides simulation of the RPMB protocol and use kernel memory + as storage. + + Be aware it doesn't promise any real security. This driver is + suitable only for testing of the RPMB subsystem or RPMB applications + prior to RPMB key provisioning. + Most people should say N here. diff --git a/drivers/char/rpmb/Makefile b/drivers/char/rpmb/Makefile index c171a5cfaed3..8bd1186948b0 100644 --- a/drivers/char/rpmb/Makefile +++ b/drivers/char/rpmb/Makefile @@ -2,5 +2,6 @@ obj-$(CONFIG_RPMB) += rpmb.o rpmb-objs += core.o rpmb-$(CONFIG_RPMB_INTF_DEV) += cdev.o +obj-$(CONFIG_RPMB_SIM) += rpmb_sim.o ccflags-y += -D__CHECK_ENDIAN__ diff --git a/drivers/char/rpmb/rpmb_sim.c b/drivers/char/rpmb/rpmb_sim.c new file mode 100644 index 000000000000..728e25511377 --- /dev/null +++ b/drivers/char/rpmb/rpmb_sim.c @@ -0,0 +1,715 @@ +// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0 +/* + * Copyright(c) 2015 - 2018 Intel Corporation. All rights reserved. + */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include +#include + +#include + +static const char id[] = "RPMB:SIM"; +#define CAPACITY_UNIT SZ_128K +#define CAPACITY_MIN SZ_128K +#define CAPACITY_MAX SZ_16M +#define BLK_UNIT SZ_256 + +static unsigned int max_wr_blks = 2; +module_param(max_wr_blks, uint, 0644); +MODULE_PARM_DESC(max_wr_blks, "max blocks that can be written in a single command (default: 2)"); + +static unsigned int daunits = 1; +module_param(daunits, uint, 0644); +MODULE_PARM_DESC(daunits, "number of data area units of 128K (default: 1)"); + +struct blk { + u8 data[BLK_UNIT]; +}; + +/** + * struct rpmb_sim_dev + * + * @dev: back pointer device + * @rdev: rpmb device + * @auth_key: Authentication key register which is used to authenticate + * accesses when MAC is calculated; + * @auth_key_set: true if authentication key was set + * @write_counter: Counter value for the total amount of successful + * authenticated data write requests made by the host. + * The initial value of this register after production is 00000000h. + * The value will be incremented by one along with each successful + * programming access. The value cannot be reset. After the counter + * has reached the maximum value of FFFFFFFFh, + * it will not be incremented anymore (overflow prevention) + * @hash_desc: hmac(sha256) shash descriptor + * + * @res_frames: frame that holds the result of the last write operation + * @out_frames: next read operation result frames + * @out_frames_cnt: number of the output frames + * + * @capacity: size of the partition in bytes multiple of 128K + * @blkcnt: block count + * @da: data area in blocks + */ +struct rpmb_sim_dev { + struct device *dev; + struct rpmb_dev *rdev; + u8 auth_key[32]; + bool auth_key_set; + u32 write_counter; + struct shash_desc *hash_desc; + + struct rpmb_frame_jdec res_frames[1]; + struct rpmb_frame_jdec *out_frames; + unsigned int out_frames_cnt; + + size_t capacity; + size_t blkcnt; + struct blk *da; +}; + +static __be16 op_result(struct rpmb_sim_dev *rsdev, u16 result) +{ + if (!rsdev->auth_key_set) + return cpu_to_be16(RPMB_ERR_NO_KEY); + + if (rsdev->write_counter == 0xFFFFFFFF) + result |= RPMB_ERR_COUNTER_EXPIRED; + + return cpu_to_be16(result); +} + +static __be16 req_to_resp(u16 req) +{ + return cpu_to_be16(RPMB_REQ2RESP(req)); +} + +static int rpmb_sim_calc_hmac(struct rpmb_sim_dev *rsdev, + struct rpmb_frame_jdec *frames, + unsigned int blks, u8 *mac) +{ + struct shash_desc *desc = rsdev->hash_desc; + int i; + int ret; + + ret = crypto_shash_init(desc); + if (ret) + goto out; + + for (i = 0; i < blks; i++) { + ret = crypto_shash_update(desc, frames[i].data, + rpmb_jdec_hmac_data_len); + if (ret) + goto out; + } + ret = crypto_shash_final(desc, mac); +out: + if (ret) + dev_err(rsdev->dev, "digest error = %d", ret); + + return ret; +} + +static int rpmb_op_not_programmed(struct rpmb_sim_dev *rsdev, u16 req) +{ + struct rpmb_frame_jdec *res_frame = rsdev->res_frames; + + res_frame->req_resp = req_to_resp(req); + res_frame->result = op_result(rsdev, RPMB_ERR_NO_KEY); + + rsdev->out_frames = res_frame; + rsdev->out_frames_cnt = 1; + + dev_err(rsdev->dev, "not programmed\n"); + + return 0; +} + +static int rpmb_op_program_key(struct rpmb_sim_dev *rsdev, + struct rpmb_frame_jdec *in_frame, u32 cnt) +{ + struct rpmb_frame_jdec *res_frame = rsdev->res_frames; + struct crypto_shash *tfm = rsdev->hash_desc->tfm; + u16 req; + int ret; + u16 err = RPMB_ERR_OK; + + req = be16_to_cpu(in_frame[0].req_resp); + + if (req != RPMB_PROGRAM_KEY) + return -EINVAL; + + if (cnt != 1) { + dev_err(rsdev->dev, "wrong number of frames %d != 1\n", cnt); + return -EINVAL; + } + + if (rsdev->auth_key_set) { + dev_err(rsdev->dev, "key already set\n"); + err = RPMB_ERR_WRITE; + goto out; + } + + ret = crypto_shash_setkey(tfm, in_frame[0].key_mac, 32); + if (ret) { + dev_err(rsdev->dev, "set key failed = %d\n", ret); + err = RPMB_ERR_GENERAL; + goto out; + } + + dev_dbg(rsdev->dev, "digest size %u\n", crypto_shash_digestsize(tfm)); + + memcpy(rsdev->auth_key, in_frame[0].key_mac, 32); + rsdev->auth_key_set = true; +out: + + memset(res_frame, 0, sizeof(*res_frame)); + res_frame->req_resp = req_to_resp(req); + res_frame->result = op_result(rsdev, err); + + return 0; +} + +static int rpmb_op_get_wr_counter(struct rpmb_sim_dev *rsdev, + struct rpmb_frame_jdec *in_frame, u32 cnt) +{ + struct rpmb_frame_jdec *frame; + int ret = 0; + u16 req; + u16 err; + + req = be16_to_cpu(in_frame[0].req_resp); + if (req != RPMB_GET_WRITE_COUNTER) + return -EINVAL; + + if (cnt != 1) { + dev_err(rsdev->dev, "wrong number of frames %d != 1\n", cnt); + return -EINVAL; + } + + frame = kcalloc(1, sizeof(*frame), GFP_KERNEL); + if (!frame) { + err = RPMB_ERR_READ; + ret = -ENOMEM; + rsdev->out_frames = rsdev->res_frames; + rsdev->out_frames_cnt = cnt; + goto out; + } + + rsdev->out_frames = frame; + rsdev->out_frames_cnt = cnt; + + frame->req_resp = req_to_resp(req); + frame->write_counter = cpu_to_be32(rsdev->write_counter); + memcpy(frame->nonce, in_frame[0].nonce, 16); + + err = RPMB_ERR_OK; + if (rpmb_sim_calc_hmac(rsdev, frame, cnt, frame->key_mac)) + err = RPMB_ERR_READ; + +out: + rsdev->out_frames[0].req_resp = req_to_resp(req); + rsdev->out_frames[0].result = op_result(rsdev, err); + + return ret; +} + +static int rpmb_op_write_data(struct rpmb_sim_dev *rsdev, + struct rpmb_frame_jdec *in_frame, u32 cnt) +{ + struct rpmb_frame_jdec *res_frame = rsdev->res_frames; + u8 mac[32]; + u16 req, err, addr, blks; + unsigned int i; + int ret = 0; + + req = be16_to_cpu(in_frame[0].req_resp); + if (req != RPMB_WRITE_DATA) + return -EINVAL; + + if (rsdev->write_counter == 0xFFFFFFFF) { + err = RPMB_ERR_WRITE; + goto out; + } + + blks = be16_to_cpu(in_frame[0].block_count); + if (blks == 0 || blks > cnt) { + dev_err(rsdev->dev, "wrong number of blocks: blks=%u cnt=%u\n", + blks, cnt); + ret = -EINVAL; + err = RPMB_ERR_GENERAL; + goto out; + } + + if (blks > max_wr_blks) { + err = RPMB_ERR_WRITE; + goto out; + } + + addr = be16_to_cpu(in_frame[0].addr); + if (addr >= rsdev->blkcnt) { + err = RPMB_ERR_ADDRESS; + goto out; + } + + if (rpmb_sim_calc_hmac(rsdev, in_frame, blks, mac)) { + err = RPMB_ERR_AUTH; + goto out; + } + + /* mac is in the last frame */ + if (memcmp(mac, in_frame[blks - 1].key_mac, sizeof(mac)) != 0) { + err = RPMB_ERR_AUTH; + goto out; + } + + if (be32_to_cpu(in_frame[0].write_counter) != rsdev->write_counter) { + err = RPMB_ERR_COUNTER; + goto out; + } + + if (addr + blks > rsdev->blkcnt) { + err = RPMB_ERR_WRITE; + goto out; + } + + dev_dbg(rsdev->dev, "Writing = %u blocks at addr = 0x%X\n", blks, addr); + err = RPMB_ERR_OK; + for (i = 0; i < blks; i++) + memcpy(rsdev->da[addr + i].data, in_frame[i].data, BLK_UNIT); + + rsdev->write_counter++; + + memset(res_frame, 0, sizeof(*res_frame)); + res_frame->req_resp = req_to_resp(req); + res_frame->write_counter = cpu_to_be32(rsdev->write_counter); + res_frame->addr = cpu_to_be16(addr); + if (rpmb_sim_calc_hmac(rsdev, res_frame, 1, res_frame->key_mac)) + err = RPMB_ERR_READ; + +out: + if (err != RPMB_ERR_OK) { + memset(res_frame, 0, sizeof(*res_frame)); + res_frame->req_resp = req_to_resp(req); + } + res_frame->result = op_result(rsdev, err); + + return ret; +} + +static int rpmb_do_read_data(struct rpmb_sim_dev *rsdev, + struct rpmb_frame_jdec *in_frame, u32 cnt) +{ + struct rpmb_frame_jdec *res_frame = rsdev->res_frames; + struct rpmb_frame_jdec *out_frames = NULL; + u8 mac[32]; + u16 req, err, addr, blks; + unsigned int i; + int ret; + + req = be16_to_cpu(in_frame->req_resp); + if (req != RPMB_READ_DATA) + return -EINVAL; + + /* eMMC intentionally set 0 here */ + blks = be16_to_cpu(in_frame->block_count); + blks = blks ?: cnt; + if (blks > cnt) { + dev_err(rsdev->dev, "wrong number of frames cnt %u\n", blks); + ret = -EINVAL; + err = RPMB_ERR_GENERAL; + goto out; + } + + out_frames = kcalloc(blks, sizeof(*out_frames), GFP_KERNEL); + if (!out_frames) { + ret = -ENOMEM; + err = RPMB_ERR_READ; + goto out; + } + + ret = 0; + addr = be16_to_cpu(in_frame[0].addr); + if (addr >= rsdev->blkcnt) { + err = RPMB_ERR_ADDRESS; + goto out; + } + + if (addr + blks > rsdev->blkcnt) { + err = RPMB_ERR_READ; + goto out; + } + + dev_dbg(rsdev->dev, "reading = %u blocks at addr = 0x%X\n", blks, addr); + for (i = 0; i < blks; i++) { + memcpy(out_frames[i].data, rsdev->da[addr + i].data, BLK_UNIT); + memcpy(out_frames[i].nonce, in_frame[0].nonce, 16); + out_frames[i].req_resp = req_to_resp(req); + out_frames[i].addr = in_frame[0].addr; + out_frames[i].block_count = cpu_to_be16(blks); + } + + if (rpmb_sim_calc_hmac(rsdev, out_frames, blks, mac)) { + err = RPMB_ERR_AUTH; + goto out; + } + + memcpy(out_frames[blks - 1].key_mac, mac, sizeof(mac)); + + err = RPMB_ERR_OK; + for (i = 0; i < blks; i++) + out_frames[i].result = op_result(rsdev, err); + + rsdev->out_frames = out_frames; + rsdev->out_frames_cnt = cnt; + + return 0; + +out: + memset(res_frame, 0, sizeof(*res_frame)); + res_frame->req_resp = req_to_resp(req); + res_frame->result = op_result(rsdev, err); + kfree(out_frames); + rsdev->out_frames = res_frame; + rsdev->out_frames_cnt = 1; + + return ret; +} + +static int rpmb_op_read_data(struct rpmb_sim_dev *rsdev, + struct rpmb_frame_jdec *in_frame, u32 cnt) +{ + struct rpmb_frame_jdec *res_frame = rsdev->res_frames; + u16 req; + + req = be16_to_cpu(in_frame->req_resp); + if (req != RPMB_READ_DATA) + return -EINVAL; + + memcpy(res_frame, in_frame, sizeof(*res_frame)); + + rsdev->out_frames = res_frame; + rsdev->out_frames_cnt = 1; + + return 0; +} + +static int rpmb_op_result_read(struct rpmb_sim_dev *rsdev, + struct rpmb_frame_jdec *frames, u32 cnt) +{ + u16 req = be16_to_cpu(frames[0].req_resp); + u16 blks = be16_to_cpu(frames[0].block_count); + + if (req != RPMB_RESULT_READ) + return -EINVAL; + + if (blks != 0) { + dev_err(rsdev->dev, "wrong number of frames %u != 0\n", blks); + return -EINVAL; + } + + rsdev->out_frames = rsdev->res_frames; + rsdev->out_frames_cnt = 1; + return 0; +} + +static int rpmb_sim_write(struct rpmb_sim_dev *rsdev, + struct rpmb_frame_jdec *frames, u32 cnt) +{ + u16 req; + int ret; + + if (!frames) + return -EINVAL; + + if (cnt == 0) + cnt = 1; + + req = be16_to_cpu(frames[0].req_resp); + if (!rsdev->auth_key_set && req != RPMB_PROGRAM_KEY) + return rpmb_op_not_programmed(rsdev, req); + + switch (req) { + case RPMB_PROGRAM_KEY: + dev_dbg(rsdev->dev, "rpmb: program key\n"); + ret = rpmb_op_program_key(rsdev, frames, cnt); + break; + case RPMB_WRITE_DATA: + dev_dbg(rsdev->dev, "rpmb: write data\n"); + ret = rpmb_op_write_data(rsdev, frames, cnt); + break; + case RPMB_GET_WRITE_COUNTER: + dev_dbg(rsdev->dev, "rpmb: get write counter\n"); + ret = rpmb_op_get_wr_counter(rsdev, frames, cnt); + break; + case RPMB_READ_DATA: + dev_dbg(rsdev->dev, "rpmb: read data\n"); + ret = rpmb_op_read_data(rsdev, frames, cnt); + break; + case RPMB_RESULT_READ: + dev_dbg(rsdev->dev, "rpmb: result read\n"); + ret = rpmb_op_result_read(rsdev, frames, cnt); + break; + default: + dev_err(rsdev->dev, "unsupported command %u\n", req); + ret = -EINVAL; + break; + } + + dev_dbg(rsdev->dev, "rpmb: ret=%d\n", ret); + + return ret; +} + +static int rpmb_sim_read(struct rpmb_sim_dev *rsdev, + struct rpmb_frame_jdec *frames, u32 cnt) +{ + int i; + + if (!frames) + return -EINVAL; + + if (cnt == 0) + cnt = 1; + + if (!rsdev->out_frames || rsdev->out_frames_cnt == 0) { + dev_err(rsdev->dev, "out_frames are not set\n"); + return -EINVAL; + } + + if (rsdev->out_frames->req_resp == cpu_to_be16(RPMB_READ_DATA)) + rpmb_do_read_data(rsdev, rsdev->out_frames, cnt); + + for (i = 0; i < min_t(u32, rsdev->out_frames_cnt, cnt); i++) + memcpy(&frames[i], &rsdev->out_frames[i], sizeof(frames[i])); + + if (rsdev->out_frames != rsdev->res_frames) + kfree(rsdev->out_frames); + + rsdev->out_frames = NULL; + rsdev->out_frames_cnt = 0; + dev_dbg(rsdev->dev, "rpmb: cnt=%d\n", cnt); + + return 0; +} + +static int rpmb_sim_cmd_seq(struct device *dev, u8 target, + struct rpmb_cmd *cmds, u32 ncmds) +{ + struct rpmb_sim_dev *rsdev; + int i; + int ret; + struct rpmb_cmd *cmd; + + if (!dev) + return -EINVAL; + + rsdev = dev_get_drvdata(dev); + + if (!rsdev) + return -EINVAL; + + for (ret = 0, i = 0; i < ncmds && !ret; i++) { + cmd = &cmds[i]; + if (cmd->flags & RPMB_F_WRITE) + ret = rpmb_sim_write(rsdev, cmd->frames, cmd->nframes); + else + ret = rpmb_sim_read(rsdev, cmd->frames, cmd->nframes); + } + return ret; +} + +static int rpmb_sim_get_capacity(struct device *dev, u8 target) +{ + return daunits; +} + +static struct rpmb_ops rpmb_sim_ops = { + .cmd_seq = rpmb_sim_cmd_seq, + .get_capacity = rpmb_sim_get_capacity, + .type = RPMB_TYPE_EMMC | RPMB_TYPE_SIM, +}; + +static int rpmb_sim_hmac_256_alloc(struct rpmb_sim_dev *rsdev) +{ + struct shash_desc *desc; + struct crypto_shash *tfm; + + tfm = crypto_alloc_shash("hmac(sha256)", 0, 0); + if (IS_ERR(tfm)) + return PTR_ERR(tfm); + + desc = kzalloc(sizeof(*desc) + crypto_shash_descsize(tfm), GFP_KERNEL); + if (!desc) { + crypto_free_shash(tfm); + return -ENOMEM; + } + + desc->tfm = tfm; + rsdev->hash_desc = desc; + + dev_dbg(rsdev->dev, "hamac(sha256) registered\n"); + return 0; +} + +static void rpmb_sim_hmac_256_free(struct rpmb_sim_dev *rsdev) +{ + struct shash_desc *desc = rsdev->hash_desc; + + if (desc->tfm) + crypto_free_shash(desc->tfm); + kfree(desc); + + rsdev->hash_desc = NULL; +} + +static int rpmb_sim_probe(struct device *dev) +{ + struct rpmb_sim_dev *rsdev; + int ret; + + rsdev = kzalloc(sizeof(*rsdev), GFP_KERNEL); + if (!rsdev) + return -ENOMEM; + + rsdev->dev = dev; + + ret = rpmb_sim_hmac_256_alloc(rsdev); + if (ret) + goto err; + + rsdev->capacity = CAPACITY_UNIT * daunits; + rsdev->blkcnt = rsdev->capacity / BLK_UNIT; + rsdev->da = kzalloc(rsdev->capacity, GFP_KERNEL); + if (!rsdev->da) { + ret = -ENOMEM; + goto err; + } + + rpmb_sim_ops.dev_id_len = strlen(id); + rpmb_sim_ops.dev_id = id; + rpmb_sim_ops.wr_cnt_max = max_wr_blks; + rpmb_sim_ops.rd_cnt_max = max_wr_blks; + rpmb_sim_ops.block_size = 1; + + rsdev->rdev = rpmb_dev_register(rsdev->dev, 0, &rpmb_sim_ops); + if (IS_ERR(rsdev->rdev)) { + ret = PTR_ERR(rsdev->rdev); + goto err; + } + + dev_info(dev, "registered RPMB capacity = %zu of %zu blocks\n", + rsdev->capacity, rsdev->blkcnt); + + dev_set_drvdata(dev, rsdev); + + return 0; +err: + rpmb_sim_hmac_256_free(rsdev); + if (rsdev) + kfree(rsdev->da); + kfree(rsdev); + return ret; +} + +static int rpmb_sim_remove(struct device *dev) +{ + struct rpmb_sim_dev *rsdev; + + rsdev = dev_get_drvdata(dev); + + rpmb_dev_unregister(rsdev->rdev); + + dev_set_drvdata(dev, NULL); + + rpmb_sim_hmac_256_free(rsdev); + + kfree(rsdev->da); + kfree(rsdev); + return 0; +} + +static void rpmb_sim_shutdown(struct device *dev) +{ + rpmb_sim_remove(dev); +} + +static int rpmb_sim_match(struct device *dev, struct device_driver *drv) +{ + return 1; +} + +static struct bus_type rpmb_sim_bus = { + .name = "rpmb_sim", + .match = rpmb_sim_match, +}; + +static struct device_driver rpmb_sim_drv = { + .name = "rpmb_sim", + .probe = rpmb_sim_probe, + .remove = rpmb_sim_remove, + .shutdown = rpmb_sim_shutdown, +}; + +static void rpmb_sim_dev_release(struct device *dev) +{ +} + +static struct device rpmb_sim_dev; + +static int __init rpmb_sim_init(void) +{ + int ret; + struct device *dev = &rpmb_sim_dev; + struct device_driver *drv = &rpmb_sim_drv; + + ret = bus_register(&rpmb_sim_bus); + if (ret) + return ret; + + dev->bus = &rpmb_sim_bus; + dev->release = rpmb_sim_dev_release; + dev_set_name(dev, "%s", "rpmb_sim"); + ret = device_register(dev); + if (ret) { + pr_err("device register failed %d\n", ret); + goto err_device; + } + + drv->bus = &rpmb_sim_bus; + ret = driver_register(drv); + if (ret) { + pr_err("driver register failed %d\n", ret); + goto err_driver; + } + + return 0; + +err_driver: + device_unregister(dev); +err_device: + bus_unregister(&rpmb_sim_bus); + return ret; +} + +static void __exit rpmb_sim_exit(void) +{ + struct device *dev = &rpmb_sim_dev; + struct device_driver *drv = &rpmb_sim_drv; + + device_unregister(dev); + driver_unregister(drv); + bus_unregister(&rpmb_sim_bus); +} + +module_init(rpmb_sim_init); +module_exit(rpmb_sim_exit); + +MODULE_AUTHOR("Tomas Winkler Date: Sun, 20 Mar 2016 11:32:42 +0200 Subject: [PATCH 1064/1103] tools rpmb: add RPBM access tool Add simple RPMB host testing tool. It can be used to program key, write and read data block, and retrieve write counter. V2: Resend. V3: Fix missing objtool. V4: Add verbose option. V5: 1. Adjust to the new API. 2. Exercise both request and sequence ioctls. V6: 1. Add includes to openssl/rand.h and endian.h. 2. Fix some signed, unsigned comparisons. 3. Check results more thoroughly. 4. use HOSTCFLAGS in compilation. 5. Allocate frames dynamically. V7: 1. Fix rpmb_alloc_frames, it has always allocated one frame instead of requested number. 2. Use an inline function instead of macro for rw blocking wrapper. V8: 1. Free frames in write_counter functions. 2. Enhance error and debug messages. 3. Tool can be compiled statically if RPMB_STATIC is set 4. Support for openssl 1.1.0 V9: 1. Remove request_cmd() interface. 2. Fix c&p error in rpmb_result_str 3. Use new version and capability ioctls. 4. Add get-info argument 5. Use SPDX identifiers. Change-Id: I914bb9918e7a555932b38b11950b22fd5187799b Signed-off-by: Tomas Winkler Signed-off-by: Alexander Usyskin --- MAINTAINERS | 1 + tools/Makefile | 15 +- tools/rpmb/.gitignore | 2 + tools/rpmb/Makefile | 38 ++ tools/rpmb/rpmb.c | 1020 +++++++++++++++++++++++++++++++++++++++++ 5 files changed, 1070 insertions(+), 6 deletions(-) create mode 100644 tools/rpmb/.gitignore create mode 100644 tools/rpmb/Makefile create mode 100644 tools/rpmb/rpmb.c diff --git a/MAINTAINERS b/MAINTAINERS index 046a93f0dc3f..d21898548164 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -12521,6 +12521,7 @@ F: drivers/char/rpmb/* F: include/uapi/linux/rpmb.h F: include/linux/rpmb.h F: Documentation/ABI/testing/sysfs-class-rpmb +F: tools/rpmb/ RTL2830 MEDIA DRIVER M: Antti Palosaari diff --git a/tools/Makefile b/tools/Makefile index be02c8b904db..72f644639fcc 100644 --- a/tools/Makefile +++ b/tools/Makefile @@ -22,6 +22,7 @@ help: @echo ' liblockdep - user-space wrapper for kernel locking-validator' @echo ' bpf - misc BPF tools' @echo ' perf - Linux performance measurement and analysis tool' + @echo ' rpmb - Replay protected memory block access tool' @echo ' selftests - various kernel selftests' @echo ' spi - spi tools' @echo ' objtool - an ELF object analysis tool' @@ -59,7 +60,7 @@ acpi: FORCE cpupower: FORCE $(call descend,power/$@) -cgroup firewire hv guest spi usb virtio vm bpf iio gpio objtool leds wmi: FORCE +cgroup firewire hv guest rpmb spi usb virtio vm bpf iio gpio objtool leds wmi: FORCE $(call descend,$@) liblockdep: FORCE @@ -92,7 +93,7 @@ kvm_stat: FORCE $(call descend,kvm/$@) all: acpi cgroup cpupower gpio hv firewire liblockdep \ - perf selftests spi turbostat usb \ + perf rpmb selftests spi turbostat usb \ virtio vm bpf x86_energy_perf_policy \ tmon freefall iio objtool kvm_stat wmi @@ -102,7 +103,7 @@ acpi_install: cpupower_install: $(call descend,power/$(@:_install=),install) -cgroup_install firewire_install gpio_install hv_install iio_install perf_install spi_install usb_install virtio_install vm_install bpf_install objtool_install wmi_install: +cgroup_install firewire_install gpio_install hv_install iio_install rpmb_install perf_install spi_install usb_install virtio_install vm_install bpf_install objtool_install wmi_install: $(call descend,$(@:_install=),install) liblockdep_install: @@ -125,11 +126,10 @@ kvm_stat_install: install: acpi_install cgroup_install cpupower_install gpio_install \ hv_install firewire_install iio_install liblockdep_install \ - perf_install selftests_install turbostat_install usb_install \ + perf_install rpmb_install selftests_install turbostat_install usb_install \ virtio_install vm_install bpf_install x86_energy_perf_policy_install \ tmon_install freefall_install objtool_install kvm_stat_install \ wmi_install - acpi_clean: $(call descend,power/acpi,clean) @@ -155,6 +155,9 @@ perf_clean: $(Q)mkdir -p $(PERF_O) . $(Q)$(MAKE) --no-print-directory -C perf O=$(PERF_O) subdir= clean +rpmb_clean: + $(call descend,$(@:_clean=),clean) + selftests_clean: $(call descend,testing/$(@:_clean=),clean) @@ -171,7 +174,7 @@ build_clean: $(call descend,build,clean) clean: acpi_clean cgroup_clean cpupower_clean hv_clean firewire_clean \ - perf_clean selftests_clean turbostat_clean spi_clean usb_clean virtio_clean \ + perf_clean rpmb_clean selftests_clean turbostat_clean spi_clean usb_clean virtio_clean \ vm_clean bpf_clean iio_clean x86_energy_perf_policy_clean tmon_clean \ freefall_clean build_clean libbpf_clean libsubcmd_clean liblockdep_clean \ gpio_clean objtool_clean leds_clean wmi_clean diff --git a/tools/rpmb/.gitignore b/tools/rpmb/.gitignore new file mode 100644 index 000000000000..218f680548e6 --- /dev/null +++ b/tools/rpmb/.gitignore @@ -0,0 +1,2 @@ +*.o +rpmb diff --git a/tools/rpmb/Makefile b/tools/rpmb/Makefile new file mode 100644 index 000000000000..c99d5f9e9439 --- /dev/null +++ b/tools/rpmb/Makefile @@ -0,0 +1,38 @@ +# SPDX-License-Identifier: GPL-2.0 +CC = $(CROSS_COMPILE)gcc +LD = $(CROSS_COMPILE)ld +PKG_CONFIG = $(CROSS_COMPILE)pkg-config + +ifeq ($(srctree),) +srctree := $(patsubst %/,%,$(dir $(shell pwd))) +srctree := $(patsubst %/,%,$(dir $(srctree))) +#$(info Determined 'srctree' to be $(srctree)) +endif + +INSTALL = install +prefix ?= /usr/local +bindir = $(prefix)/bin + + +CFLAGS += $(HOSTCFLAGS) +CFLAGS += -D__EXPORTED_HEADERS__ -g +ifdef RPMB_STATIC +LDFLAGS += -pthread -static +CFLAGS += -pthread -static +PKG_STATIC = --static +endif +CFLAGS += -I$(srctree)/include/uapi -I$(srctree)/include +LDLIBS += $(shell $(PKG_CONFIG) --libs $(PKG_STATIC) libcrypto) + +prog := rpmb + +all : $(prog) + +$(prog): rpmb.o + +clean : + $(RM) $(prog) *.o + +install: $(prog) + $(INSTALL) -m755 -d $(DESTDIR)$(bindir) + $(INSTALL) $(prog) $(DESTDIR)$(bindir) diff --git a/tools/rpmb/rpmb.c b/tools/rpmb/rpmb.c new file mode 100644 index 000000000000..1a507db2739e --- /dev/null +++ b/tools/rpmb/rpmb.c @@ -0,0 +1,1020 @@ +// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0 +/* + * Copyright (C) 2016-2018 Intel Corp. All rights reserved + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "linux/rpmb.h" + +#define RPMB_KEY_SIZE 32 +#define RPMB_MAC_SIZE 32 +#define RPMB_NONCE_SIZE 16 + +bool verbose; +#define rpmb_dbg(fmt, ARGS...) do { \ + if (verbose) \ + fprintf(stderr, "rpmb: " fmt, ##ARGS); \ +} while (0) + +#define rpmb_msg(fmt, ARGS...) \ + fprintf(stderr, "rpmb: " fmt, ##ARGS) + +#define rpmb_err(fmt, ARGS...) \ + fprintf(stderr, "rpmb: error: " fmt, ##ARGS) + +static const char *rpmb_op_str(uint16_t op) +{ +#define RPMB_OP(_op) case RPMB_##_op: return #_op + + switch (op) { + RPMB_OP(PROGRAM_KEY); + RPMB_OP(GET_WRITE_COUNTER); + RPMB_OP(WRITE_DATA); + RPMB_OP(READ_DATA); + RPMB_OP(RESULT_READ); + break; + default: + return "unknown"; + } +#undef RPMB_OP +} + +static const char *rpmb_result_str(enum rpmb_op_result result) +{ +#define str(x) #x +#define RPMB_ERR(_res) case RPMB_ERR_##_res: \ + { if (result & RPMB_ERR_COUNTER_EXPIRED) \ + return "COUNTER_EXPIRE:" str(_res); \ + else \ + return str(_res); \ + } + + switch (result & 0x000F) { + RPMB_ERR(OK); + RPMB_ERR(GENERAL); + RPMB_ERR(AUTH); + RPMB_ERR(COUNTER); + RPMB_ERR(ADDRESS); + RPMB_ERR(WRITE); + RPMB_ERR(READ); + RPMB_ERR(NO_KEY); + break; + default: + return "unknown"; + } +#undef RPMB_ERR +#undef str +}; + +static inline void __dump_buffer(const char *buf) +{ + fprintf(stderr, "%s\n", buf); +} + +static void +dump_hex_buffer(const char *title, const void *buf, size_t len) +{ + const unsigned char *_buf = (const unsigned char *)buf; + const size_t pbufsz = 16 * 3; + char pbuf[pbufsz]; + int j = 0; + + if (title) + fprintf(stderr, "%s\n", title); + while (len-- > 0) { + snprintf(&pbuf[j], pbufsz - j, "%02X ", *_buf++); + j += 3; + if (j == 16 * 3) { + __dump_buffer(pbuf); + j = 0; + } + } + if (j) + __dump_buffer(pbuf); +} + +static int open_dev_file(const char *devfile, struct rpmb_ioc_cap_cmd *cap) +{ + struct rpmb_ioc_ver_cmd ver; + int fd; + int ret; + + fd = open(devfile, O_RDWR); + if (fd < 0) + rpmb_err("Cannot open: %s: %s.\n", devfile, strerror(errno)); + + ret = ioctl(fd, RPMB_IOC_VER_CMD, &ver); + if (ret < 0) { + rpmb_err("ioctl failure %d: %s.\n", ret, strerror(errno)); + goto err; + } + + printf("RPMB API Version %X\n", ver.api_version); + + ret = ioctl(fd, RPMB_IOC_CAP_CMD, cap); + if (ret < 0) { + rpmb_err("ioctl failure %d: %s.\n", ret, strerror(errno)); + goto err; + } + + rpmb_dbg("RPMB device_type = %hd\n", cap->device_type); + rpmb_dbg("RPMB rpmb_target = %hd\n", cap->target); + rpmb_dbg("RPMB capacity = %hd\n", cap->capacity); + rpmb_dbg("RPMB block_size = %hd\n", cap->block_size); + rpmb_dbg("RPMB wr_cnt_max = %hd\n", cap->wr_cnt_max); + rpmb_dbg("RPMB rd_cnt_max = %hd\n", cap->rd_cnt_max); + rpmb_dbg("RPMB auth_method = %hd\n", cap->auth_method); + + return fd; +err: + close(fd); + return -1; +} + +static int open_rd_file(const char *datafile, const char *type) +{ + int fd; + + if (!strcmp(datafile, "-")) + fd = STDIN_FILENO; + else + fd = open(datafile, O_RDONLY); + + if (fd < 0) + rpmb_err("Cannot open %s: %s: %s.\n", + type, datafile, strerror(errno)); + + return fd; +} + +static int open_wr_file(const char *datafile, const char *type) +{ + int fd; + + if (!strcmp(datafile, "-")) + fd = STDOUT_FILENO; + else + fd = open(datafile, O_WRONLY | O_CREAT | O_APPEND, 0600); + if (fd < 0) + rpmb_err("Cannot open %s: %s: %s.\n", + type, datafile, strerror(errno)); + return fd; +} + +static void close_fd(int fd) +{ + if (fd > 0 && fd != STDIN_FILENO && fd != STDOUT_FILENO) + close(fd); +} + +/* need to just cast out 'const' in write(2) */ +typedef ssize_t (*rwfunc_t)(int fd, void *buf, size_t count); +/* blocking rw wrapper */ +static inline ssize_t rw(rwfunc_t func, int fd, unsigned char *buf, size_t size) +{ + ssize_t ntotal = 0, n; + char *_buf = (char *)buf; + + do { + n = func(fd, _buf + ntotal, size - ntotal); + if (n == -1 && errno != EINTR) { + ntotal = -1; + break; + } else if (n > 0) { + ntotal += n; + } + } while (n != 0 && (size_t)ntotal != size); + + return ntotal; +} + +static ssize_t read_file(int fd, unsigned char *data, size_t size) +{ + ssize_t ret; + + ret = rw(read, fd, data, size); + if (ret < 0) { + rpmb_err("cannot read file: %s\n.", strerror(errno)); + } else if ((size_t)ret != size) { + rpmb_err("read %zd but must be %zu bytes length.\n", ret, size); + ret = -EINVAL; + } + + return ret; +} + +static ssize_t write_file(int fd, unsigned char *data, size_t size) +{ + ssize_t ret; + + ret = rw((rwfunc_t)write, fd, data, size); + if (ret < 0) { + rpmb_err("cannot read file: %s.\n", strerror(errno)); + } else if ((size_t)ret != size) { + rpmb_err("data is %zd but must be %zu bytes length.\n", + ret, size); + ret = -EINVAL; + } + return ret; +} + +static void dbg_dump_frame(const char *title, const struct rpmb_frame_jdec *f) +{ + uint16_t result, req_resp; + + if (!verbose) + return; + + if (!f) + return; + + result = be16toh(f->result); + req_resp = be16toh(f->req_resp); + if (req_resp & 0xf00) + req_resp = RPMB_RESP2REQ(req_resp); + + fprintf(stderr, "--------------- %s ---------------\n", + title ? title : "start"); + fprintf(stderr, "ptr: %p\n", f); + dump_hex_buffer("key_mac: ", f->key_mac, 32); + dump_hex_buffer("data: ", f->data, 256); + dump_hex_buffer("nonce: ", f->nonce, 16); + fprintf(stderr, "write_counter: %u\n", be32toh(f->write_counter)); + fprintf(stderr, "address: %0X\n", be16toh(f->addr)); + fprintf(stderr, "block_count: %u\n", be16toh(f->block_count)); + fprintf(stderr, "result %s:%d\n", rpmb_result_str(result), result); + fprintf(stderr, "req_resp %s\n", rpmb_op_str(req_resp)); + fprintf(stderr, "--------------- End ---------------\n"); +} + +static struct rpmb_frame_jdec *rpmb_alloc_frames(unsigned int cnt) +{ + return calloc(1, rpmb_ioc_frames_len_jdec(cnt)); +} + +#if OPENSSL_VERSION_NUMBER < 0x10100000L +static int rpmb_calc_hmac_sha256(struct rpmb_frame_jdec *frames, + size_t blocks_cnt, + const unsigned char key[], + unsigned int key_size, + unsigned char mac[], + unsigned int mac_size) +{ + HMAC_CTX ctx; + int ret; + unsigned int i; + + /* SSL returns 1 on success 0 on failure */ + + HMAC_CTX_init(&ctx); + ret = HMAC_Init_ex(&ctx, key, key_size, EVP_sha256(), NULL); + if (ret == 0) + goto out; + for (i = 0; i < blocks_cnt; i++) + HMAC_Update(&ctx, frames[i].data, hmac_data_len); + + ret = HMAC_Final(&ctx, mac, &mac_size); + if (ret == 0) + goto out; + if (mac_size != RPMB_MAC_SIZE) + ret = 0; + + ret = 1; +out: + HMAC_CTX_cleanup(&ctx); + return ret == 1 ? 0 : -1; +} +#else +static int rpmb_calc_hmac_sha256(struct rpmb_frame_jdec *frames, + size_t blocks_cnt, + const unsigned char key[], + unsigned int key_size, + unsigned char mac[], + unsigned int mac_size) +{ + HMAC_CTX *ctx; + int ret; + unsigned int i; + + /* SSL returns 1 on success 0 on failure */ + + ctx = HMAC_CTX_new(); + + ret = HMAC_Init_ex(ctx, key, key_size, EVP_sha256(), NULL); + if (ret == 0) + goto out; + for (i = 0; i < blocks_cnt; i++) + HMAC_Update(ctx, frames[i].data, rpmb_jdec_hmac_data_len); + + ret = HMAC_Final(ctx, mac, &mac_size); + if (ret == 0) + goto out; + if (mac_size != RPMB_MAC_SIZE) + ret = 0; + + ret = 1; +out: + HMAC_CTX_free(ctx); + return ret == 1 ? 0 : -1; +} +#endif + +static int rpmb_check_req_resp(uint16_t req, struct rpmb_frame_jdec *frame_out) +{ + if (RPMB_REQ2RESP(req) != be16toh(frame_out->req_resp)) { + rpmb_err("RPMB response mismatch %04X != %04X\n.", + RPMB_REQ2RESP(req), be16toh(frame_out->req_resp)); + return -1; + } + return 0; +} + +static int rpmb_check_mac(const unsigned char *key, + struct rpmb_frame_jdec *frames_out, + unsigned int cnt_out) +{ + unsigned char mac[RPMB_MAC_SIZE]; + + if (cnt_out == 0) { + rpmb_err("RPMB 0 output frames.\n"); + return -1; + } + + rpmb_calc_hmac_sha256(frames_out, cnt_out, + key, RPMB_KEY_SIZE, + mac, RPMB_MAC_SIZE); + + if (memcmp(mac, frames_out[cnt_out - 1].key_mac, RPMB_MAC_SIZE)) { + rpmb_err("RPMB hmac mismatch:\n"); + dump_hex_buffer("Result MAC: ", + frames_out[cnt_out - 1].key_mac, RPMB_MAC_SIZE); + dump_hex_buffer("Expected MAC: ", mac, RPMB_MAC_SIZE); + return -1; + } + + return 0; +} + +static int rpmb_ioctl(int fd, uint16_t req, + const struct rpmb_frame_jdec *frames_in, + unsigned int cnt_in, + struct rpmb_frame_jdec *frames_out, + unsigned int cnt_out) +{ + int ret; + struct __attribute__((packed)) { + struct rpmb_ioc_seq_cmd h; + struct rpmb_ioc_cmd cmd[3]; + } iseq = {}; + struct rpmb_frame_jdec *frame_res = NULL; + int i; + uint32_t flags; + + rpmb_dbg("RPMB OP: %s\n", rpmb_op_str(req)); + dbg_dump_frame("In Frame: ", frames_in); + + i = 0; + flags = RPMB_F_WRITE; + if (req == RPMB_WRITE_DATA || req == RPMB_PROGRAM_KEY) + flags |= RPMB_F_REL_WRITE; + rpmb_ioc_cmd_set(iseq.cmd[i], flags, frames_in, cnt_in); + i++; + + if (req == RPMB_WRITE_DATA || req == RPMB_PROGRAM_KEY) { + frame_res = rpmb_alloc_frames(0); + if (!frame_res) + return -ENOMEM; + frame_res->req_resp = htobe16(RPMB_RESULT_READ); + rpmb_ioc_cmd_set(iseq.cmd[i], RPMB_F_WRITE, frame_res, 0); + i++; + } + + rpmb_ioc_cmd_set(iseq.cmd[i], 0, frames_out, cnt_out); + i++; + + iseq.h.num_of_cmds = i; + ret = ioctl(fd, RPMB_IOC_SEQ_CMD, &iseq); + if (ret < 0) + rpmb_err("ioctl failure %d: %s.\n", ret, strerror(errno)); + + ret = rpmb_check_req_resp(req, frames_out); + + dbg_dump_frame("Res Frame: ", frame_res); + dbg_dump_frame("Out Frame: ", frames_out); + free(frame_res); + return ret; +} + +static int op_get_info(int nargs, char *argv[]) +{ + int dev_fd; + struct rpmb_ioc_cap_cmd cap; + + if (nargs != 1) + return -EINVAL; + + memset(&cap, 0, sizeof(cap)); + dev_fd = open_dev_file(argv[0], &cap); + if (dev_fd < 0) + return -errno; + argv++; + + printf("RPMB device_type = %hd\n", cap.device_type); + printf("RPMB rpmb_target = %hd\n", cap.target); + printf("RPMB capacity = %hd\n", cap.capacity); + printf("RPMB block_size = %hd\n", cap.block_size); + printf("RPMB wr_cnt_max = %hd\n", cap.wr_cnt_max); + printf("RPMB rd_cnt_max = %hd\n", cap.rd_cnt_max); + printf("RPMB auth_method = %hd\n", cap.auth_method); + + close(dev_fd); + + return 0; +} + +static int op_rpmb_program_key(int nargs, char *argv[]) +{ + int ret; + int dev_fd = -1, key_fd = -1; + uint16_t req = RPMB_PROGRAM_KEY; + struct rpmb_ioc_cap_cmd cap; + struct rpmb_frame_jdec *frame_in = NULL, *frame_out = NULL; + + ret = -EINVAL; + if (nargs != 2) + return ret; + + dev_fd = open_dev_file(argv[0], &cap); + if (dev_fd < 0) + goto out; + argv++; + + key_fd = open_rd_file(argv[0], "key file"); + if (key_fd < 0) + goto out; + argv++; + + frame_in = rpmb_alloc_frames(0); + frame_out = rpmb_alloc_frames(0); + if (!frame_in || !frame_out) { + ret = -ENOMEM; + goto out; + } + + frame_in->req_resp = htobe16(req); + + read_file(key_fd, frame_in->key_mac, RPMB_KEY_SIZE); + + ret = rpmb_ioctl(dev_fd, req, frame_in, 0, frame_out, 0); + if (ret) + goto out; + + if (RPMB_REQ2RESP(req) != be16toh(frame_out->req_resp)) { + rpmb_err("RPMB response mismatch.\n"); + ret = -1; + goto out; + } + + ret = be16toh(frame_out->result); + if (ret) + rpmb_err("RPMB operation %s failed, %s[0x%04x].\n", + rpmb_op_str(req), rpmb_result_str(ret), ret); + +out: + free(frame_in); + free(frame_out); + close_fd(dev_fd); + close_fd(key_fd); + + return ret; +} + +static int rpmb_get_write_counter(int dev_fd, unsigned int *cnt, + const unsigned char *key) +{ + int ret; + uint16_t res = 0x000F; + uint16_t req = RPMB_GET_WRITE_COUNTER; + struct rpmb_frame_jdec *frame_in = NULL; + struct rpmb_frame_jdec *frame_out = NULL; + + frame_in = rpmb_alloc_frames(0); + frame_out = rpmb_alloc_frames(0); + if (!frame_in || !frame_out) { + ret = -ENOMEM; + goto out; + } + + frame_in->req_resp = htobe16(req); + RAND_bytes(frame_in->nonce, RPMB_NONCE_SIZE); + + ret = rpmb_ioctl(dev_fd, req, frame_in, 0, frame_out, 0); + if (ret) + goto out; + + res = be16toh(frame_out->result); + if (res != RPMB_ERR_OK) { + ret = -1; + goto out; + } + + if (memcmp(&frame_in->nonce, &frame_out->nonce, RPMB_NONCE_SIZE)) { + rpmb_err("RPMB NONCE mismatch\n"); + dump_hex_buffer("Result NONCE:", + &frame_out->nonce, RPMB_NONCE_SIZE); + dump_hex_buffer("Expected NONCE: ", + &frame_in->nonce, RPMB_NONCE_SIZE); + ret = -1; + goto out; + } + + if (key) { + ret = rpmb_check_mac(key, frame_out, 1); + if (ret) + goto out; + } + + *cnt = be32toh(frame_out->write_counter); + +out: + if (ret) + rpmb_err("RPMB operation %s failed=%d %s[0x%04x]\n", + rpmb_op_str(req), ret, rpmb_result_str(res), res); + free(frame_in); + free(frame_out); + return ret; +} + +static int op_rpmb_get_write_counter(int nargs, char **argv) +{ + int ret; + int dev_fd = -1, key_fd = -1; + bool has_key; + struct rpmb_ioc_cap_cmd cap; + unsigned char key[RPMB_KEY_SIZE]; + unsigned int cnt; + + if (nargs == 2) + has_key = true; + else if (nargs == 1) + has_key = false; + else + return -EINVAL; + + ret = -EINVAL; + dev_fd = open_dev_file(argv[0], &cap); + if (dev_fd < 0) + return ret; + argv++; + + if (has_key) { + key_fd = open_rd_file(argv[0], "key file"); + if (key_fd < 0) + goto out; + argv++; + + ret = read_file(key_fd, key, RPMB_KEY_SIZE); + if (ret < 0) + goto out; + + ret = rpmb_get_write_counter(dev_fd, &cnt, key); + } else { + ret = rpmb_get_write_counter(dev_fd, &cnt, NULL); + } + + if (!ret) + printf("Counter value: 0x%08x\n", cnt); + +out: + close_fd(dev_fd); + close_fd(key_fd); + return ret; +} + +static int op_rpmb_read_blocks(int nargs, char **argv) +{ + int i, ret; + int dev_fd = -1, data_fd = -1, key_fd = -1; + uint16_t req = RPMB_READ_DATA; + uint16_t addr, blocks_cnt; + unsigned char key[RPMB_KEY_SIZE]; + unsigned long numarg; + bool has_key; + struct rpmb_ioc_cap_cmd cap; + struct rpmb_frame_jdec *frame_in = NULL; + struct rpmb_frame_jdec *frames_out = NULL; + struct rpmb_frame_jdec *frame_out; + + ret = -EINVAL; + if (nargs == 4) + has_key = false; + else if (nargs == 5) + has_key = true; + else + return ret; + + dev_fd = open_dev_file(argv[0], &cap); + if (dev_fd < 0) + goto out; + argv++; + + errno = 0; + numarg = strtoul(argv[0], NULL, 0); + if (errno || numarg > USHRT_MAX) { + rpmb_err("wrong block address\n"); + goto out; + } + addr = (uint16_t)numarg; + argv++; + + errno = 0; + numarg = strtoul(argv[0], NULL, 0); + if (errno || numarg > USHRT_MAX) { + rpmb_err("wrong blocks count\n"); + goto out; + } + blocks_cnt = (uint16_t)numarg; + argv++; + + if (blocks_cnt == 0) { + rpmb_err("wrong blocks count\n"); + goto out; + } + + data_fd = open_wr_file(argv[0], "output data"); + if (data_fd < 0) + goto out; + argv++; + + if (has_key) { + key_fd = open_rd_file(argv[0], "key file"); + if (key_fd < 0) + goto out; + argv++; + + ret = read_file(key_fd, key, RPMB_KEY_SIZE); + if (ret < 0) + goto out; + } + + ret = 0; + frames_out = rpmb_alloc_frames(blocks_cnt); + frame_in = rpmb_alloc_frames(0); + if (!frames_out || !frame_in) { + rpmb_err("Cannot allocate %d RPMB frames\n", blocks_cnt); + ret = -ENOMEM; + goto out; + } + + frame_in->req_resp = htobe16(req); + frame_in->addr = htobe16(addr); + /* eMMc spec ask for 0 here this will be translated by the rpmb layer */ + frame_in->block_count = htobe16(blocks_cnt); + RAND_bytes(frame_in->nonce, RPMB_NONCE_SIZE); + + ret = rpmb_ioctl(dev_fd, req, frame_in, 0, frames_out, blocks_cnt); + if (ret) + goto out; + + frame_out = &frames_out[blocks_cnt - 1]; + ret = be16toh(frame_out->result); + if (ret) { + rpmb_err("RPMB operation %s failed, %s[0x%04x]\n", + rpmb_op_str(req), rpmb_result_str(ret), ret); + goto out; + } + + if (has_key) { + ret = rpmb_check_mac(key, frames_out, blocks_cnt); + if (ret) + goto out; + } + + for (i = 0; i < blocks_cnt; i++) { + ret = write_file(data_fd, frames_out[i].data, + sizeof(frames_out[i].data)); + if (ret < 0) + goto out; + } + +out: + free(frame_in); + free(frames_out); + close_fd(dev_fd); + close_fd(data_fd); + close_fd(key_fd); + + return ret; +} + +static int op_rpmb_write_blocks(int nargs, char **argv) +{ + int ret; + int dev_fd = -1, key_fd = -1, data_fd = -1; + int i; + uint16_t req = RPMB_WRITE_DATA; + unsigned char key[RPMB_KEY_SIZE]; + unsigned char mac[RPMB_MAC_SIZE]; + unsigned long numarg; + uint16_t addr, blocks_cnt; + uint32_t write_counter; + struct rpmb_ioc_cap_cmd cap; + struct rpmb_frame_jdec *frames_in = NULL; + struct rpmb_frame_jdec *frame_out = NULL; + + ret = -EINVAL; + if (nargs != 5) + goto out; + + dev_fd = open_dev_file(argv[0], &cap); + if (dev_fd < 0) + goto out; + argv++; + + errno = 0; + numarg = strtoul(argv[0], NULL, 0); + if (errno || numarg > USHRT_MAX) { + rpmb_err("wrong block address %s\n", argv[0]); + goto out; + } + addr = (uint16_t)numarg; + argv++; + + errno = 0; + numarg = strtoul(argv[0], NULL, 0); + if (errno || numarg > USHRT_MAX) { + rpmb_err("wrong blocks count\n"); + goto out; + } + blocks_cnt = (uint16_t)numarg; + argv++; + + if (blocks_cnt == 0) { + rpmb_err("wrong blocks count\n"); + goto out; + } + + data_fd = open_rd_file(argv[0], "data file"); + if (data_fd < 0) + goto out; + argv++; + + key_fd = open_rd_file(argv[0], "key file"); + if (key_fd < 0) + goto out; + argv++; + + ret = read_file(key_fd, key, RPMB_KEY_SIZE); + if (ret < 0) + goto out; + + frames_in = rpmb_alloc_frames(blocks_cnt); + frame_out = rpmb_alloc_frames(0); + if (!frames_in || !frame_out) { + rpmb_err("can't allocate memory for RPMB outer frames\n"); + ret = -ENOMEM; + goto out; + } + + ret = rpmb_get_write_counter(dev_fd, &write_counter, key); + if (ret) + goto out; + + for (i = 0; i < blocks_cnt; i++) { + frames_in[i].req_resp = htobe16(req); + frames_in[i].block_count = htobe16(blocks_cnt); + frames_in[i].addr = htobe16(addr); + frames_in[i].write_counter = htobe32(write_counter); + } + + for (i = 0; i < blocks_cnt; i++) { + ret = read_file(data_fd, frames_in[i].data, + sizeof(frames_in[0].data)); + if (ret < 0) + goto out; + } + + rpmb_calc_hmac_sha256(frames_in, blocks_cnt, + key, RPMB_KEY_SIZE, + mac, RPMB_MAC_SIZE); + memcpy(frames_in[blocks_cnt - 1].key_mac, mac, RPMB_MAC_SIZE); + ret = rpmb_ioctl(dev_fd, req, frames_in, blocks_cnt, frame_out, 0); + if (ret != 0) + goto out; + + ret = be16toh(frame_out->result); + if (ret) { + rpmb_err("RPMB operation %s failed, %s[0x%04x]\n", + rpmb_op_str(req), rpmb_result_str(ret), ret); + ret = -1; + } + + if (be16toh(frame_out->addr) != addr) { + rpmb_err("RPMB addr mismatchs res=%04x req=%04x\n", + be16toh(frame_out->addr), addr); + ret = -1; + } + + if (be32toh(frame_out->write_counter) <= write_counter) { + rpmb_err("RPMB write counter not incremented res=%x req=%x\n", + be32toh(frame_out->write_counter), write_counter); + ret = -1; + } + + ret = rpmb_check_mac(key, frame_out, 1); +out: + free(frames_in); + free(frame_out); + close_fd(dev_fd); + close_fd(data_fd); + close_fd(key_fd); + return ret; +} + +typedef int (*rpmb_op)(int argc, char *argv[]); + +struct rpmb_cmd { + const char *op_name; + rpmb_op op; + const char *usage; /* usage title */ + const char *help; /* help */ +}; + +static const struct rpmb_cmd cmds[] = { + { + "get-info", + op_get_info, + "", + " Get RPMB device info\n", + }, + { + "program-key", + op_rpmb_program_key, + " ", + " Program authentication key of 32 bytes length from the KEY_FILE\n" + " when KEY_FILE is -, read standard input.\n" + " NOTE: This is a one-time programmable irreversible change.\n", + }, + { + "write-counter", + op_rpmb_get_write_counter, + " [KEY_FILE]", + " Rertrive write counter value from the to stdout.\n" + " When KEY_FILE is present data is verified via HMAC\n" + " when KEY_FILE is -, read standard input.\n" + }, + { + "write-blocks", + op_rpmb_write_blocks, + "
", + " of 256 bytes will be written from the DATA_FILE\n" + " to the at block offset
.\n" + " When DATA_FILE is -, read from standard input.\n", + }, + { + "read-blocks", + op_rpmb_read_blocks, + "
[KEY_FILE]", + " of 256 bytes will be read from \n" + " to the OUTPUT_FILE\n" + " When KEY_FILE is present data is verified via HMAC\n" + " When OUTPUT/KEY_FILE is -, read from standard input.\n" + " When OUTPUT_FILE is -, write to standard output\n", + }, + + { NULL, NULL, NULL, NULL } +}; + +static void help(const char *prog, const struct rpmb_cmd *cmd) +{ + printf("%s %s %s\n", prog, cmd->op_name, cmd->usage); + printf("%s\n", cmd->help); +} + +static void usage(const char *prog) +{ + int i; + + printf("\n"); + printf("Usage: %s [-v] \n\n", prog); + for (i = 0; cmds[i].op_name; i++) + printf(" %s %s %s\n", + prog, cmds[i].op_name, cmds[i].usage); + + printf("\n"); + printf(" %s -v/--verbose: runs in verbose mode\n", prog); + printf(" %s help : shows this help\n", prog); + printf(" %s help : shows detailed help\n", prog); +} + +static bool call_for_help(const char *arg) +{ + return !strcmp(arg, "help") || + !strcmp(arg, "-h") || + !strcmp(arg, "--help"); +} + +static bool parse_verbose(const char *arg) +{ + return !strcmp(arg, "-v") || + !strcmp(arg, "--verbose"); +} + +static const +struct rpmb_cmd *parse_args(const char *prog, int *_argc, char **_argv[]) +{ + int i; + int argc = *_argc; + char **argv = *_argv; + const struct rpmb_cmd *cmd = NULL; + bool need_help = false; + + argc--; argv++; + + if (argc == 0) + goto out; + + if (call_for_help(argv[0])) { + argc--; argv++; + if (argc == 0) + goto out; + + need_help = true; + } + + if (parse_verbose(argv[0])) { + argc--; argv++; + if (argc == 0) + goto out; + + verbose = true; + } + + for (i = 0; cmds[i].op_name; i++) { + if (!strncmp(argv[0], cmds[i].op_name, + strlen(cmds[i].op_name))) { + cmd = &cmds[i]; + argc--; argv++; + break; + } + } + + if (!cmd) + goto out; + + if (need_help || (argc > 0 && call_for_help(argv[0]))) { + help(prog, cmd); + argc--; argv++; + return NULL; + } + +out: + *_argc = argc; + *_argv = argv; + + if (!cmd) + usage(prog); + + return cmd; +} + +int main(int argc, char *argv[]) +{ + const char *prog = basename(argv[0]); + const struct rpmb_cmd *cmd; + int ret; + + cmd = parse_args(prog, &argc, &argv); + if (!cmd) + exit(EXIT_SUCCESS); + + ret = cmd->op(argc, argv); + if (ret == -EINVAL) + help(prog, cmd); + + if (ret) + exit(EXIT_FAILURE); + + exit(EXIT_SUCCESS); +} From c833ac3b8d7609b0f1d593e365cde64a00eb4bdf Mon Sep 17 00:00:00 2001 From: Tomas Winkler Date: Wed, 28 Jan 2015 17:01:34 +0200 Subject: [PATCH 1065/1103] mmc: block: register RPMB partition with the RPMB subsystem Register eMMC RPMB partition with the RPMB subsystem and provide implementation for the RPMB access operations abstracting actual multi step process. V2: resend V3: commit message fix V4: Kconfig: use select RPMB to ensure valid configuration Switch back to main area after RPMB access V5: Revamp code using new sequence command Support for 8K packets in e.MMC v5.1 V6: Resend. V7: Resend. V8: Rebase after block.c was moved under core/ Rebase for 4.14 V9: Rebase for 4.16 and 4.17 Build RPMB connection above ioctl layer Supply RPMB capabilities. Change-Id: I6de67f475ef738e30dc3b8c78185a1bee24595b2 Signed-off-by: Tomas Winkler Signed-off-by: Alexander Usyskin Tested-by: Avri Altman --- drivers/mmc/core/Kconfig | 1 + drivers/mmc/core/block.c | 222 ++++++++++++++++++++++++++++++++++++++- 2 files changed, 220 insertions(+), 3 deletions(-) diff --git a/drivers/mmc/core/Kconfig b/drivers/mmc/core/Kconfig index 42e89060cd41..96c7ff63178c 100644 --- a/drivers/mmc/core/Kconfig +++ b/drivers/mmc/core/Kconfig @@ -36,6 +36,7 @@ config PWRSEQ_SIMPLE config MMC_BLOCK tristate "MMC block device driver" depends on BLOCK + select RPMB default y help Say Y here to enable the MMC block device driver support. diff --git a/drivers/mmc/core/block.c b/drivers/mmc/core/block.c index a0b9102c4c6e..354fdb883d3c 100644 --- a/drivers/mmc/core/block.c +++ b/drivers/mmc/core/block.c @@ -44,6 +44,7 @@ #include #include #include +#include #include @@ -409,8 +410,8 @@ static int mmc_blk_ioctl_copy_to_user(struct mmc_ioc_cmd __user *ic_ptr, return 0; } -static int ioctl_rpmb_card_status_poll(struct mmc_card *card, u32 *status, - u32 retries_max) +static int ioctl_mmc_blk_rpmb_status_poll(struct mmc_card *card, u32 *status, + u32 retries_max) { int err; u32 retry_count = 0; @@ -612,7 +613,7 @@ static int __mmc_blk_ioctl_cmd(struct mmc_card *card, struct mmc_blk_data *md, * Ensure RPMB command has completed by polling CMD13 * "Send Status". */ - err = ioctl_rpmb_card_status_poll(card, &status, 5); + err = ioctl_mmc_blk_rpmb_status_poll(card, &status, 5); if (err) dev_err(mmc_dev(card->host), "%s: Card Status=0x%08X, error %d\n", @@ -1116,6 +1117,217 @@ static void mmc_blk_issue_drv_op(struct mmc_queue *mq, struct request *req) blk_mq_end_request(req, ret ? BLK_STS_IOERR : BLK_STS_OK); } +static int mmc_blk_rpmb_process(struct mmc_blk_data *md, + struct mmc_blk_ioc_data *idata[], + u64 num_of_cmds) +{ + struct mmc_card *card; + struct mmc_queue *mq; + int err = 0; + struct request *req; + int op_mode; + + card = md->queue.card; + if (IS_ERR(card)) { + err = PTR_ERR(card); + goto cmd_err; + } + + /* + * Dispatch the ioctl()s into the block request queue. + */ + mq = &md->queue; + op_mode = idata[0]->ic.write_flag ? REQ_OP_DRV_OUT : REQ_OP_DRV_IN, + req = blk_get_request(mq->queue, op_mode, 0); + if (IS_ERR(req)) { + err = PTR_ERR(req); + goto cmd_err; + } + + req_to_mmc_queue_req(req)->drv_op = MMC_DRV_OP_IOCTL_RPMB; + req_to_mmc_queue_req(req)->drv_op_data = idata; + req_to_mmc_queue_req(req)->ioc_count = num_of_cmds; + + blk_execute_rq(mq->queue, NULL, req, 0); + + err = req_to_mmc_queue_req(req)->drv_op_result; + + blk_put_request(req); + +cmd_err: + return err; +} + +static +struct mmc_blk_ioc_data *mmc_blk_rpmb_cmd_to_ioc_data(struct rpmb_cmd *cmd) +{ + struct mmc_blk_ioc_data *idata; + int err; + + idata = kzalloc(sizeof(*idata), GFP_KERNEL); + if (!idata) { + err = -ENOMEM; + goto out; + } + + if (cmd->flags & RPMB_F_WRITE) { + idata->ic.opcode = MMC_WRITE_MULTIPLE_BLOCK; + idata->ic.write_flag = 1; + if (cmd->flags & RPMB_F_REL_WRITE) + idata->ic.write_flag |= 1 << 31; + } else { + idata->ic.opcode = MMC_READ_MULTIPLE_BLOCK; + } + + /* nframes == 0 in case there is only meta data in the frame */ + idata->ic.blocks = cmd->nframes ?: 1; + idata->ic.blksz = 512; + + idata->buf_bytes = (u64)idata->ic.blksz * idata->ic.blocks; + if (idata->buf_bytes > MMC_IOC_MAX_BYTES) { + err = -EOVERFLOW; + goto out; + } + + idata->buf = (unsigned char *)cmd->frames; + + return idata; +out: + kfree(idata); + return ERR_PTR(err); +} + +static int mmc_blk_rpmb_cmd_seq(struct device *dev, u8 target, + struct rpmb_cmd *cmds, + u32 num_of_cmds) +{ + struct mmc_rpmb_data *rpmb = dev_get_drvdata(dev); + struct mmc_blk_ioc_data **idata; + int err = 0; + u32 i; + + if (!rpmb) + return -ENODEV; + + idata = kcalloc(num_of_cmds, sizeof(*idata), GFP_KERNEL); + if (!idata) + return -ENOMEM; + + for (i = 0; i < num_of_cmds; i++) { + idata[i] = mmc_blk_rpmb_cmd_to_ioc_data(&cmds[i]); + if (IS_ERR(idata[i])) { + err = PTR_ERR(idata[i]); + num_of_cmds = i; + goto cmd_err; + } + idata[i]->rpmb = rpmb; + } + + get_device(&rpmb->dev); + mmc_blk_get(rpmb->md->disk); + + err = mmc_blk_rpmb_process(rpmb->md, idata, num_of_cmds); + +cmd_err: + for (i = 0; i < num_of_cmds; i++) + kfree(idata[i]); + + kfree(idata); + + put_device(&rpmb->dev); + mmc_blk_put(rpmb->md); + + return err; +} + +static int mmc_blk_rpmb_get_capacity(struct device *dev, u8 target) +{ + struct mmc_rpmb_data *rpmb = dev_get_drvdata(dev); + struct mmc_card *card; + + card = rpmb->md->queue.card; + return card->ext_csd.raw_rpmb_size_mult; +} + +static struct rpmb_ops mmc_rpmb_dev_ops = { + .cmd_seq = mmc_blk_rpmb_cmd_seq, + .get_capacity = mmc_blk_rpmb_get_capacity, + .type = RPMB_TYPE_EMMC, + .auth_method = RPMB_HMAC_ALGO_SHA_256, +}; + +static void mmc_blk_rpmb_unset_dev_id(struct rpmb_ops *ops) +{ + kfree(ops->dev_id); + ops->dev_id = NULL; +} + +static int mmc_blk_rpmb_set_dev_id(struct rpmb_ops *ops, struct mmc_card *card) +{ + char *id; + + id = kmalloc(sizeof(card->raw_cid), GFP_KERNEL); + if (!id) + return -ENOMEM; + + memcpy(id, card->raw_cid, sizeof(card->raw_cid)); + ops->dev_id = id; + ops->dev_id_len = sizeof(card->raw_cid); + + return 0; +} + +static void mmc_blk_rpmb_set_cap(struct rpmb_ops *ops, + struct mmc_card *card) +{ + u16 rel_wr_cnt; + + /* RPMB blocks are written in half sectors hence '* 2' */ + rel_wr_cnt = card->ext_csd.rel_sectors * 2; + /* eMMC 5.1 may support RPMB 8K (32) frames */ + if (card->ext_csd.rev >= 8) { + if (card->ext_csd.rel_param & EXT_CSD_WR_REL_PARAM_EN) + rel_wr_cnt = 32; + else + rel_wr_cnt = 2; + } + ops->wr_cnt_max = rel_wr_cnt; + ops->rd_cnt_max = card->host->max_blk_count; + ops->block_size = 1; /* 256B */ +} + +static void mmc_blk_rpmb_add(struct mmc_card *card) +{ + struct mmc_blk_data *md = dev_get_drvdata(&card->dev); + struct rpmb_dev *rdev; + struct mmc_rpmb_data *rpmb; + u8 i = 0; + + mmc_blk_rpmb_set_dev_id(&mmc_rpmb_dev_ops, card); + mmc_blk_rpmb_set_cap(&mmc_rpmb_dev_ops, card); + + /* Add RPMB partitions */ + list_for_each_entry(rpmb, &md->rpmbs, node) { + rdev = rpmb_dev_register(&rpmb->dev, i++, &mmc_rpmb_dev_ops); + if (IS_ERR(rdev)) { + pr_warn("%s: cannot register to rpmb %ld\n", + dev_name(&rpmb->dev), PTR_ERR(rdev)); + } + } +} + +static void mmc_blk_rpmb_remove(struct mmc_card *card) +{ + struct mmc_blk_data *md = dev_get_drvdata(&card->dev); + struct mmc_rpmb_data *rpmb; + u8 i = 0; + + list_for_each_entry(rpmb, &md->rpmbs, node) + rpmb_dev_unregister_by_device(&rpmb->dev, i++); + + mmc_blk_rpmb_unset_dev_id(&mmc_rpmb_dev_ops); +} + static void mmc_blk_issue_discard_rq(struct mmc_queue *mq, struct request *req) { struct mmc_blk_data *md = mq->blkdata; @@ -2936,6 +3148,9 @@ static int mmc_blk_probe(struct mmc_card *card) goto out; } + /* Add rpmb layer */ + mmc_blk_rpmb_add(card); + /* Add two debugfs entries */ mmc_blk_add_debugfs(card, md); @@ -2964,6 +3179,7 @@ static void mmc_blk_remove(struct mmc_card *card) struct mmc_blk_data *md = dev_get_drvdata(&card->dev); mmc_blk_remove_debugfs(card, md); + mmc_blk_rpmb_remove(card); mmc_blk_remove_parts(card, md); pm_runtime_get_sync(&card->dev); if (md->part_curr != md->part_type) { From daea51aa302800476c4c8d2b5a8c3285da1be642 Mon Sep 17 00:00:00 2001 From: Tomas Winkler Date: Wed, 4 Jan 2017 14:14:21 +0200 Subject: [PATCH 1066/1103] scsi: ufs: revamp string descriptor reading Define new a type: uc_string_id for easier string handling and less casting. Reduce number or string copies in price of a dynamic allocation. In addition drop usage of variable length array (VLA) as it's not considered to be safe. V9: Fix memory corruption. Change-Id: Ieda6a4b68e60b8a2d8a2d93a371ff5396dec989b Signed-off-by: Tomas Winkler --- drivers/scsi/ufs/ufs-sysfs.c | 20 ++--- drivers/scsi/ufs/ufs.h | 2 +- drivers/scsi/ufs/ufshcd.c | 163 +++++++++++++++++++++-------------- drivers/scsi/ufs/ufshcd.h | 9 +- 4 files changed, 114 insertions(+), 80 deletions(-) diff --git a/drivers/scsi/ufs/ufs-sysfs.c b/drivers/scsi/ufs/ufs-sysfs.c index 8d9332bb7d0c..0b221c5a244c 100644 --- a/drivers/scsi/ufs/ufs-sysfs.c +++ b/drivers/scsi/ufs/ufs-sysfs.c @@ -570,10 +570,11 @@ static ssize_t _name##_show(struct device *dev, \ struct ufs_hba *hba = dev_get_drvdata(dev); \ int ret; \ int desc_len = QUERY_DESC_MAX_SIZE; \ - u8 *desc_buf; \ + char *desc_buf; \ + \ desc_buf = kzalloc(QUERY_DESC_MAX_SIZE, GFP_ATOMIC); \ - if (!desc_buf) \ - return -ENOMEM; \ + if (!desc_buf) \ + return -ENOMEM; \ ret = ufshcd_query_descriptor_retry(hba, \ UPIU_QUERY_OPCODE_READ_DESC, QUERY_DESC_IDN_DEVICE, \ 0, 0, desc_buf, &desc_len); \ @@ -582,14 +583,13 @@ static ssize_t _name##_show(struct device *dev, \ goto out; \ } \ index = desc_buf[DEVICE_DESC_PARAM##_pname]; \ - memset(desc_buf, 0, QUERY_DESC_MAX_SIZE); \ - if (ufshcd_read_string_desc(hba, index, desc_buf, \ - QUERY_DESC_MAX_SIZE, true)) { \ - ret = -EINVAL; \ + kfree(desc_buf); \ + desc_buf = NULL; \ + ret = ufshcd_read_string_desc(hba, index, &desc_buf, \ + SD_ASCII_STD); \ + if (ret < 0) \ goto out; \ - } \ - ret = snprintf(buf, PAGE_SIZE, "%s\n", \ - desc_buf + QUERY_DESC_HDR_SIZE); \ + ret = snprintf(buf, PAGE_SIZE, "%s\n", desc_buf); \ out: \ kfree(desc_buf); \ return ret; \ diff --git a/drivers/scsi/ufs/ufs.h b/drivers/scsi/ufs/ufs.h index 14e5bf7af0bb..c9774b59b6bf 100644 --- a/drivers/scsi/ufs/ufs.h +++ b/drivers/scsi/ufs/ufs.h @@ -617,7 +617,7 @@ struct ufs_dev_info { */ struct ufs_dev_desc { u16 wmanufacturerid; - char model[MAX_MODEL_LEN + 1]; + char *model; }; /** diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c index a4d36497a047..361e264bb667 100644 --- a/drivers/scsi/ufs/ufshcd.c +++ b/drivers/scsi/ufs/ufshcd.c @@ -301,16 +301,6 @@ static void ufshcd_scsi_block_requests(struct ufs_hba *hba) scsi_block_requests(hba->host); } -/* replace non-printable or non-ASCII characters with spaces */ -static inline void ufshcd_remove_non_printable(char *val) -{ - if (!val) - return; - - if (*val < 0x20 || *val > 0x7e) - *val = ' '; -} - static void ufshcd_add_cmd_upiu_trace(struct ufs_hba *hba, unsigned int tag, const char *str) { @@ -3124,7 +3114,7 @@ int ufshcd_read_desc_param(struct ufs_hba *hba, enum desc_idn desc_id, int desc_index, u8 param_offset, - u8 *param_read_buf, + void *param_read_buf, u8 param_size) { int ret; @@ -3192,7 +3182,7 @@ int ufshcd_read_desc_param(struct ufs_hba *hba, static inline int ufshcd_read_desc(struct ufs_hba *hba, enum desc_idn desc_id, int desc_index, - u8 *buf, + void *buf, u32 size) { return ufshcd_read_desc_param(hba, desc_id, desc_index, 0, buf, size); @@ -3210,49 +3200,77 @@ static int ufshcd_read_device_desc(struct ufs_hba *hba, u8 *buf, u32 size) return ufshcd_read_desc(hba, QUERY_DESC_IDN_DEVICE, 0, buf, size); } +/** + * struct uc_string_id - unicode string + * + * @len: size of this descriptor inclusive + * @type: descriptor type + * @uc: unicode string character + */ +struct uc_string_id { + u8 len; + u8 type; + wchar_t uc[0]; +} __packed; + +/* replace non-printable or non-ASCII characters with spaces */ +static inline char blank_non_printable(char ch) +{ + return (ch >= 0x20 && ch <= 0x7e) ? ch : ' '; +} + /** * ufshcd_read_string_desc - read string descriptor * @hba: pointer to adapter instance * @desc_index: descriptor index - * @buf: pointer to buffer where descriptor would be read - * @size: size of buf + * @buf: pointer to buffer where descriptor would be read, + * the caller should free the memory. * @ascii: if true convert from unicode to ascii characters + * null terminated string. * - * Return 0 in case of success, non-zero otherwise + * Return: string size on success. + * -ENOMEM: on allocation failure + * -EINVAL: on a wrong parameter */ -int ufshcd_read_string_desc(struct ufs_hba *hba, int desc_index, - u8 *buf, u32 size, bool ascii) +int ufshcd_read_string_desc(struct ufs_hba *hba, u8 desc_index, + char **buf, bool ascii) { - int err = 0; + struct uc_string_id *uc_str; + char *str; + int ret; - err = ufshcd_read_desc(hba, - QUERY_DESC_IDN_STRING, desc_index, buf, size); + if (!buf) + return -EINVAL; - if (err) { - dev_err(hba->dev, "%s: reading String Desc failed after %d retries. err = %d\n", - __func__, QUERY_REQ_RETRIES, err); + uc_str = kzalloc(QUERY_DESC_MAX_SIZE, GFP_KERNEL); + if (!uc_str) + return -ENOMEM; + + ret = ufshcd_read_desc(hba, QUERY_DESC_IDN_STRING, + desc_index, uc_str, + QUERY_DESC_MAX_SIZE); + if (ret < 0) { + dev_err(hba->dev, "Reading String Desc failed after %d retries. err = %d\n", + QUERY_REQ_RETRIES, ret); + str = NULL; + goto out; + } + + if (uc_str->len <= QUERY_DESC_HDR_SIZE) { + dev_dbg(hba->dev, "String Desc is of zero length\n"); + str = NULL; + ret = 0; goto out; } if (ascii) { - int desc_len; - int ascii_len; + ssize_t ascii_len; int i; - char *buff_ascii; - - desc_len = buf[0]; /* remove header and divide by 2 to move from UTF16 to UTF8 */ - ascii_len = (desc_len - QUERY_DESC_HDR_SIZE) / 2 + 1; - if (size < ascii_len + QUERY_DESC_HDR_SIZE) { - dev_err(hba->dev, "%s: buffer allocated size is too small\n", - __func__); - err = -ENOMEM; - goto out; - } - - buff_ascii = kmalloc(ascii_len, GFP_KERNEL); - if (!buff_ascii) { - err = -ENOMEM; + ascii_len = (uc_str->len - QUERY_DESC_HDR_SIZE) / 2 + 1; + str = kzalloc(ascii_len, GFP_KERNEL); + if (!str) { + ret = -ENOMEM; goto out; } @@ -3260,22 +3278,29 @@ int ufshcd_read_string_desc(struct ufs_hba *hba, int desc_index, * the descriptor contains string in UTF16 format * we need to convert to utf-8 so it can be displayed */ - utf16s_to_utf8s((wchar_t *)&buf[QUERY_DESC_HDR_SIZE], - desc_len - QUERY_DESC_HDR_SIZE, - UTF16_BIG_ENDIAN, buff_ascii, ascii_len); + ret = utf16s_to_utf8s(uc_str->uc, + uc_str->len - QUERY_DESC_HDR_SIZE, + UTF16_BIG_ENDIAN, str, ascii_len); /* replace non-printable or non-ASCII characters with spaces */ - for (i = 0; i < ascii_len; i++) - ufshcd_remove_non_printable(&buff_ascii[i]); + for (i = 0; i < ret; i++) + str[i] = blank_non_printable(str[i]); - memset(buf + QUERY_DESC_HDR_SIZE, 0, - size - QUERY_DESC_HDR_SIZE); - memcpy(buf + QUERY_DESC_HDR_SIZE, buff_ascii, ascii_len); - buf[QUERY_DESC_LENGTH_OFFSET] = ascii_len + QUERY_DESC_HDR_SIZE; - kfree(buff_ascii); + str[ret++] = '\0'; + + } else { + str = kzalloc(uc_str->len, GFP_KERNEL); + if (!str) { + ret = -ENOMEM; + goto out; + } + memcpy(str, uc_str, uc_str->len); + ret = uc_str->len; } out: - return err; + *buf = str; + kfree(uc_str); + return ret; } /** @@ -6266,6 +6291,9 @@ static int ufs_get_device_desc(struct ufs_hba *hba, u8 model_index; u8 *desc_buf; + if (!dev_desc) + return -EINVAL; + buff_len = max_t(size_t, hba->desc_size.dev_desc, QUERY_DESC_MAX_SIZE + 1); desc_buf = kmalloc(buff_len, GFP_KERNEL); @@ -6289,31 +6317,31 @@ static int ufs_get_device_desc(struct ufs_hba *hba, desc_buf[DEVICE_DESC_PARAM_MANF_ID + 1]; model_index = desc_buf[DEVICE_DESC_PARAM_PRDCT_NAME]; - - /* Zero-pad entire buffer for string termination. */ - memset(desc_buf, 0, buff_len); - - err = ufshcd_read_string_desc(hba, model_index, desc_buf, - QUERY_DESC_MAX_SIZE, true/*ASCII*/); - if (err) { + err = ufshcd_read_string_desc(hba, model_index, + &dev_desc->model, SD_ASCII_STD); + if (err < 0) { dev_err(hba->dev, "%s: Failed reading Product Name. err = %d\n", __func__, err); goto out; } - desc_buf[QUERY_DESC_MAX_SIZE] = '\0'; - strlcpy(dev_desc->model, (desc_buf + QUERY_DESC_HDR_SIZE), - min_t(u8, desc_buf[QUERY_DESC_LENGTH_OFFSET], - MAX_MODEL_LEN)); - - /* Null terminate the model string */ - dev_desc->model[MAX_MODEL_LEN] = '\0'; + /* + * ufshcd_read_string_desc returns size of the string + * reset the error value + */ + err = 0; out: kfree(desc_buf); return err; } +static void ufs_put_device_desc(struct ufs_dev_desc *dev_desc) +{ + kfree(dev_desc->model); + dev_desc->model = NULL; +} + static void ufs_fixup_device_setup(struct ufs_hba *hba, struct ufs_dev_desc *dev_desc) { @@ -6322,8 +6350,9 @@ static void ufs_fixup_device_setup(struct ufs_hba *hba, for (f = ufs_fixups; f->quirk; f++) { if ((f->card.wmanufacturerid == dev_desc->wmanufacturerid || f->card.wmanufacturerid == UFS_ANY_VENDOR) && - (STR_PRFX_EQUAL(f->card.model, dev_desc->model) || - !strcmp(f->card.model, UFS_ANY_MODEL))) + ((dev_desc->model && + STR_PRFX_EQUAL(f->card.model, dev_desc->model)) || + !strcmp(f->card.model, UFS_ANY_MODEL))) hba->dev_quirks |= f->quirk; } } @@ -6606,6 +6635,8 @@ static int ufshcd_probe_hba(struct ufs_hba *hba) } ufs_fixup_device_setup(hba, &card); + ufs_put_device_desc(&card); + ufshcd_tune_unipro_params(hba); ret = ufshcd_set_vccq_rail_unused(hba, diff --git a/drivers/scsi/ufs/ufshcd.h b/drivers/scsi/ufs/ufshcd.h index 33fdd3f281ae..c04bdbb1ea57 100644 --- a/drivers/scsi/ufs/ufshcd.h +++ b/drivers/scsi/ufs/ufshcd.h @@ -875,14 +875,17 @@ int ufshcd_read_desc_param(struct ufs_hba *hba, enum desc_idn desc_id, int desc_index, u8 param_offset, - u8 *param_read_buf, + void *param_read_buf, u8 param_size); int ufshcd_query_attr(struct ufs_hba *hba, enum query_opcode opcode, enum attr_idn idn, u8 index, u8 selector, u32 *attr_val); int ufshcd_query_flag(struct ufs_hba *hba, enum query_opcode opcode, enum flag_idn idn, bool *flag_res); -int ufshcd_read_string_desc(struct ufs_hba *hba, int desc_index, - u8 *buf, u32 size, bool ascii); + +#define SD_ASCII_STD true +#define SD_RAW false +int ufshcd_read_string_desc(struct ufs_hba *hba, u8 desc_index, + char **buf, bool ascii); int ufshcd_hold(struct ufs_hba *hba, bool async); void ufshcd_release(struct ufs_hba *hba); From 345643f2dc6086ab6f94997596c89438d5993827 Mon Sep 17 00:00:00 2001 From: Tomas Winkler Date: Thu, 5 Nov 2015 10:22:44 +0200 Subject: [PATCH 1067/1103] scsi: ufs: connect to RPMB subsystem Register UFS RPMB LUN with the RPMB subsystem and provide implementation for the RPMB access operations. RPMB partition is accessed via a sequence of security protocol in and security protocol out commands with UFS specific parameters. This multi step process is abstracted into 4 basic RPMB commands. V2: resend V3: resend V4: Kconfig: use select RPMB to ensure valid configuration V5: Revamp code using new sequence command. V6: Resend V7: Resend V8: 1. Replace scsi_execute_req_flags() with scsi_execute_req() 2. line over 80 characters fixes 3. scsi_device_get return value has to be checked V9: V1. adjust to new unregister api V2: nframes is 0 based now Change-Id: Ia45c6776d534fb311b6016aaa88f441d403cb0ca Signed-off-by: Alexander Usyskin Signed-off-by: Tomas Winkler Tested-by: Avri Altman --- drivers/scsi/ufs/Kconfig | 1 + drivers/scsi/ufs/ufshcd.c | 223 ++++++++++++++++++++++++++++++++++++++ drivers/scsi/ufs/ufshcd.h | 2 + 3 files changed, 226 insertions(+) diff --git a/drivers/scsi/ufs/Kconfig b/drivers/scsi/ufs/Kconfig index e09fe6ab3572..a3c1982b213a 100644 --- a/drivers/scsi/ufs/Kconfig +++ b/drivers/scsi/ufs/Kconfig @@ -38,6 +38,7 @@ config SCSI_UFSHCD select PM_DEVFREQ select DEVFREQ_GOV_SIMPLE_ONDEMAND select NLS + select RPMB ---help--- This selects the support for UFS devices in Linux, say Y and make sure that you know the name of your UFS host adapter (the card diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c index 361e264bb667..dfc30f3f21b3 100644 --- a/drivers/scsi/ufs/ufshcd.c +++ b/drivers/scsi/ufs/ufshcd.c @@ -37,11 +37,14 @@ * license terms, and distributes only under these terms. */ +#include #include #include #include #include #include +#include + #include "ufshcd.h" #include "ufs_quirks.h" #include "unipro.h" @@ -6220,6 +6223,217 @@ static void ufshcd_init_icc_levels(struct ufs_hba *hba) kfree(desc_buf); } +#define SEC_PROTOCOL_UFS 0xEC +#define SEC_SPECIFIC_UFS_RPMB 0x001 + +#define SEC_PROTOCOL_CMD_SIZE 12 +#define SEC_PROTOCOL_RETRIES 3 +#define SEC_PROTOCOL_RETRIES_ON_RESET 10 +#define SEC_PROTOCOL_TIMEOUT msecs_to_jiffies(1000) + +static int +ufshcd_rpmb_security_out(struct scsi_device *sdev, u8 region, + void *frames, u32 trans_len) +{ + struct scsi_sense_hdr sshdr; + int reset_retries = SEC_PROTOCOL_RETRIES_ON_RESET; + int ret; + u8 cmd[SEC_PROTOCOL_CMD_SIZE]; + + memset(cmd, 0, SEC_PROTOCOL_CMD_SIZE); + cmd[0] = SECURITY_PROTOCOL_OUT; + cmd[1] = SEC_PROTOCOL_UFS; + cmd[2] = region; + cmd[3] = SEC_SPECIFIC_UFS_RPMB; + cmd[4] = 0; /* inc_512 bit 7 set to 0 */ + put_unaligned_be32(trans_len, cmd + 6); /* transfer length */ + +retry: + ret = scsi_execute_req(sdev, cmd, DMA_TO_DEVICE, + frames, trans_len, &sshdr, + SEC_PROTOCOL_TIMEOUT, SEC_PROTOCOL_RETRIES, + NULL); + + if (ret && scsi_sense_valid(&sshdr) && + sshdr.sense_key == UNIT_ATTENTION && + sshdr.asc == 0x29 && sshdr.ascq == 0x00) + /* + * Device reset might occur several times, + * give it one more chance + */ + if (--reset_retries > 0) + goto retry; + + if (ret) + dev_err(&sdev->sdev_gendev, "%s: failed with err %0x\n", + __func__, ret); + + if (driver_byte(ret) & DRIVER_SENSE) + scsi_print_sense_hdr(sdev, "rpmb: security out", &sshdr); + + return ret; +} + +static int +ufshcd_rpmb_security_in(struct scsi_device *sdev, u8 region, + void *frames, u32 alloc_len) +{ + struct scsi_sense_hdr sshdr; + int reset_retries = SEC_PROTOCOL_RETRIES_ON_RESET; + int ret; + u8 cmd[SEC_PROTOCOL_CMD_SIZE]; + + memset(cmd, 0, SEC_PROTOCOL_CMD_SIZE); + cmd[0] = SECURITY_PROTOCOL_IN; + cmd[1] = SEC_PROTOCOL_UFS; + cmd[2] = region; + cmd[3] = SEC_SPECIFIC_UFS_RPMB; + cmd[4] = 0; /* inc_512 bit 7 set to 0 */ + put_unaligned_be32(alloc_len, cmd + 6); /* allocation length */ + +retry: + ret = scsi_execute_req(sdev, cmd, DMA_FROM_DEVICE, + frames, alloc_len, &sshdr, + SEC_PROTOCOL_TIMEOUT, SEC_PROTOCOL_RETRIES, + NULL); + + if (ret && scsi_sense_valid(&sshdr) && + sshdr.sense_key == UNIT_ATTENTION && + sshdr.asc == 0x29 && sshdr.ascq == 0x00) + /* + * Device reset might occur several times, + * give it one more chance + */ + if (--reset_retries > 0) + goto retry; + + if (ret) + dev_err(&sdev->sdev_gendev, "%s: failed with err %0x\n", + __func__, ret); + + if (driver_byte(ret) & DRIVER_SENSE) + scsi_print_sense_hdr(sdev, "rpmb: security in", &sshdr); + + return ret; +} + +static int ufshcd_rpmb_cmd_seq(struct device *dev, u8 target, + struct rpmb_cmd *cmds, u32 ncmds) +{ + unsigned long flags; + struct ufs_hba *hba = dev_get_drvdata(dev); + struct scsi_device *sdev; + struct rpmb_cmd *cmd; + u32 len; + u32 i; + int ret; + + spin_lock_irqsave(hba->host->host_lock, flags); + sdev = hba->sdev_ufs_rpmb; + if (sdev) { + ret = scsi_device_get(sdev); + if (!ret && !scsi_device_online(sdev)) { + ret = -ENODEV; + scsi_device_put(sdev); + } + } else { + ret = -ENODEV; + } + spin_unlock_irqrestore(hba->host->host_lock, flags); + if (ret) + return ret; + + for (ret = 0, i = 0; i < ncmds && !ret; i++) { + cmd = &cmds[i]; + len = rpmb_ioc_frames_len_jdec(cmd->nframes); + if (cmd->flags & RPMB_F_WRITE) + ret = ufshcd_rpmb_security_out(sdev, target, + cmd->frames, len); + else + ret = ufshcd_rpmb_security_in(sdev, target, + cmd->frames, len); + } + scsi_device_put(sdev); + return ret; +} + +static int ufshcd_rpmb_get_capacity(struct device *dev, u8 target) +{ + struct ufs_hba *hba = dev_get_drvdata(dev); + __be64 block_count; + int ret; + + ret = ufshcd_read_unit_desc_param(hba, + UFS_UPIU_RPMB_WLUN, + UNIT_DESC_PARAM_LOGICAL_BLK_COUNT, + (u8 *)&block_count, + sizeof(block_count)); + if (ret) + return ret; + + return be64_to_cpu(block_count) * SZ_512 / SZ_128K; +} + +static struct rpmb_ops ufshcd_rpmb_dev_ops = { + .cmd_seq = ufshcd_rpmb_cmd_seq, + .get_capacity = ufshcd_rpmb_get_capacity, + .type = RPMB_TYPE_UFS, + .auth_method = RPMB_HMAC_ALGO_SHA_256, + +}; + +static inline void ufshcd_rpmb_add(struct ufs_hba *hba) +{ + struct rpmb_dev *rdev; + u8 rpmb_rw_size = 1; + int ret; + + ret = scsi_device_get(hba->sdev_ufs_rpmb); + if (ret) + goto out_put_dev; + + if (hba->ufs_version >= UFSHCI_VERSION_21) { + ret = ufshcd_read_desc_param(hba, QUERY_DESC_IDN_GEOMETRY, 0, + GEOMETRY_DESC_PARAM_RPMB_RW_SIZE, + &rpmb_rw_size, + sizeof(rpmb_rw_size)); + if (ret) + goto out_put_dev; + } + + ufshcd_rpmb_dev_ops.rd_cnt_max = rpmb_rw_size; + ufshcd_rpmb_dev_ops.wr_cnt_max = rpmb_rw_size; + + rdev = rpmb_dev_register(hba->dev, 0, &ufshcd_rpmb_dev_ops); + if (IS_ERR(rdev)) { + dev_warn(hba->dev, "%s: cannot register to rpmb %ld\n", + dev_name(hba->dev), PTR_ERR(rdev)); + goto out_put_dev; + } + + return; + +out_put_dev: + scsi_device_put(hba->sdev_ufs_rpmb); + hba->sdev_ufs_rpmb = NULL; +} + +static inline void ufshcd_rpmb_remove(struct ufs_hba *hba) +{ + unsigned long flags; + + if (!hba->sdev_ufs_rpmb) + return; + + spin_lock_irqsave(hba->host->host_lock, flags); + + rpmb_dev_unregister_by_device(hba->dev, 0); + scsi_device_put(hba->sdev_ufs_rpmb); + hba->sdev_ufs_rpmb = NULL; + + spin_unlock_irqrestore(hba->host->host_lock, flags); +} + /** * ufshcd_scsi_add_wlus - Adds required W-LUs * @hba: per-adapter instance @@ -6267,6 +6481,8 @@ static int ufshcd_scsi_add_wlus(struct ufs_hba *hba) ret = PTR_ERR(sdev_rpmb); goto remove_sdev_ufs_device; } + hba->sdev_ufs_rpmb = sdev_rpmb; + scsi_device_put(sdev_rpmb); sdev_boot = __scsi_add_device(hba->host, 0, 0, @@ -6685,6 +6901,8 @@ static int ufshcd_probe_hba(struct ufs_hba *hba) if (ufshcd_scsi_add_wlus(hba)) goto out; + ufshcd_rpmb_add(hba); + /* Initialize devfreq after UFS device is detected */ if (ufshcd_is_clkscaling_supported(hba)) { memcpy(&hba->clk_scaling.saved_pwr_info.info, @@ -7925,6 +8143,8 @@ int ufshcd_shutdown(struct ufs_hba *hba) goto out; } + ufshcd_rpmb_remove(hba); + ret = ufshcd_suspend(hba, UFS_SHUTDOWN_PM); out: if (ret) @@ -7941,7 +8161,10 @@ EXPORT_SYMBOL(ufshcd_shutdown); */ void ufshcd_remove(struct ufs_hba *hba) { + ufshcd_rpmb_remove(hba); + ufs_sysfs_remove_nodes(hba->dev); + scsi_remove_host(hba->host); /* disable interrupts */ ufshcd_disable_intr(hba, hba->intr_mask); diff --git a/drivers/scsi/ufs/ufshcd.h b/drivers/scsi/ufs/ufshcd.h index c04bdbb1ea57..82b5e7d317f5 100644 --- a/drivers/scsi/ufs/ufshcd.h +++ b/drivers/scsi/ufs/ufshcd.h @@ -458,6 +458,7 @@ struct ufs_stats { * @utmrdl_dma_addr: UTMRDL DMA address * @host: Scsi_Host instance of the driver * @dev: device handle + * @sdev_ufs_rpmb: reference to RPMB device W-LU * @lrb: local reference block * @lrb_in_use: lrb in use * @outstanding_tasks: Bits representing outstanding task requests @@ -522,6 +523,7 @@ struct ufs_hba { * "UFS device" W-LU. */ struct scsi_device *sdev_ufs_device; + struct scsi_device *sdev_ufs_rpmb; enum ufs_dev_pwr_mode curr_dev_pwr_mode; enum uic_link_state uic_link_state; From f062f66e30a50c23b6bc498943afad4b9f859cfd Mon Sep 17 00:00:00 2001 From: Tomas Winkler Date: Thu, 14 May 2015 00:00:59 +0300 Subject: [PATCH 1068/1103] scsi: ufs: store device serial number. Retrieve device serial number and store it for RPMB subsystem use. V9: rebase v10: Fix Kdoc Change-Id: Ieee7f85696f6614cd2f3c81403124159ea85b77e Signed-off-by: Tomas Winkler Signed-off-by: Alexander Usyskin --- drivers/scsi/ufs/ufs.h | 4 ++++ drivers/scsi/ufs/ufshcd.c | 35 +++++++++++++++++++++++++++++------ 2 files changed, 33 insertions(+), 6 deletions(-) diff --git a/drivers/scsi/ufs/ufs.h b/drivers/scsi/ufs/ufs.h index c9774b59b6bf..b60dfcb5f009 100644 --- a/drivers/scsi/ufs/ufs.h +++ b/drivers/scsi/ufs/ufs.h @@ -614,10 +614,14 @@ struct ufs_dev_info { * * @wmanufacturerid: card details * @model: card model + * @serial_no: serial number + * @serial_no_len: serial number string length */ struct ufs_dev_desc { u16 wmanufacturerid; char *model; + char *serial_no; + size_t serial_no_len; }; /** diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c index dfc30f3f21b3..3a699a5d7ea8 100644 --- a/drivers/scsi/ufs/ufshcd.c +++ b/drivers/scsi/ufs/ufshcd.c @@ -43,6 +43,7 @@ #include #include #include +#include #include #include "ufshcd.h" @@ -6382,12 +6383,19 @@ static struct rpmb_ops ufshcd_rpmb_dev_ops = { }; -static inline void ufshcd_rpmb_add(struct ufs_hba *hba) +static inline void ufshcd_rpmb_add(struct ufs_hba *hba, + struct ufs_dev_desc *dev_desc) { struct rpmb_dev *rdev; u8 rpmb_rw_size = 1; int ret; + ufshcd_rpmb_dev_ops.dev_id = kmemdup(dev_desc->serial_no, + dev_desc->serial_no_len, + GFP_KERNEL); + if (ufshcd_rpmb_dev_ops.dev_id) + ufshcd_rpmb_dev_ops.dev_id_len = dev_desc->serial_no_len; + ret = scsi_device_get(hba->sdev_ufs_rpmb); if (ret) goto out_put_dev; @@ -6431,6 +6439,9 @@ static inline void ufshcd_rpmb_remove(struct ufs_hba *hba) scsi_device_put(hba->sdev_ufs_rpmb); hba->sdev_ufs_rpmb = NULL; + kfree(ufshcd_rpmb_dev_ops.dev_id); + ufshcd_rpmb_dev_ops.dev_id = NULL; + spin_unlock_irqrestore(hba->host->host_lock, flags); } @@ -6504,7 +6515,7 @@ static int ufs_get_device_desc(struct ufs_hba *hba, { int err; size_t buff_len; - u8 model_index; + u8 index; u8 *desc_buf; if (!dev_desc) @@ -6532,8 +6543,8 @@ static int ufs_get_device_desc(struct ufs_hba *hba, dev_desc->wmanufacturerid = desc_buf[DEVICE_DESC_PARAM_MANF_ID] << 8 | desc_buf[DEVICE_DESC_PARAM_MANF_ID + 1]; - model_index = desc_buf[DEVICE_DESC_PARAM_PRDCT_NAME]; - err = ufshcd_read_string_desc(hba, model_index, + index = desc_buf[DEVICE_DESC_PARAM_PRDCT_NAME]; + err = ufshcd_read_string_desc(hba, index, &dev_desc->model, SD_ASCII_STD); if (err < 0) { dev_err(hba->dev, "%s: Failed reading Product Name. err = %d\n", @@ -6541,6 +6552,14 @@ static int ufs_get_device_desc(struct ufs_hba *hba, goto out; } + index = desc_buf[DEVICE_DESC_PARAM_SN]; + err = ufshcd_read_string_desc(hba, index, &dev_desc->serial_no, SD_RAW); + if (err < 0) { + dev_err(hba->dev, "%s: Failed reading Serial No. err = %d\n", + __func__, err); + goto out; + } + /* * ufshcd_read_string_desc returns size of the string * reset the error value @@ -6556,6 +6575,9 @@ static void ufs_put_device_desc(struct ufs_dev_desc *dev_desc) { kfree(dev_desc->model); dev_desc->model = NULL; + + kfree(dev_desc->serial_no); + dev_desc->serial_no = NULL; } static void ufs_fixup_device_setup(struct ufs_hba *hba, @@ -6851,7 +6873,6 @@ static int ufshcd_probe_hba(struct ufs_hba *hba) } ufs_fixup_device_setup(hba, &card); - ufs_put_device_desc(&card); ufshcd_tune_unipro_params(hba); @@ -6901,7 +6922,7 @@ static int ufshcd_probe_hba(struct ufs_hba *hba) if (ufshcd_scsi_add_wlus(hba)) goto out; - ufshcd_rpmb_add(hba); + ufshcd_rpmb_add(hba, &card); /* Initialize devfreq after UFS device is detected */ if (ufshcd_is_clkscaling_supported(hba)) { @@ -6944,6 +6965,8 @@ static int ufshcd_probe_hba(struct ufs_hba *hba) } out: + + ufs_put_device_desc(&card); /* * If we failed to initialize the device or the device is not * present, turn off the power/clocks etc. From bd3911a92db379077fa5f0985be78385afcd17f3 Mon Sep 17 00:00:00 2001 From: Tomas Winkler Date: Sat, 28 Apr 2018 22:01:32 +0300 Subject: [PATCH 1069/1103] rpmb: add nvme rpmb frame type The NVMe RPMB frame differs in layout and endianity from the one defined by JDEC. Change-Id: Ifae77454e1bc8733eb1e5bcb2146dc198f94151d Signed-off-by: Tomas Winkler --- drivers/char/rpmb/cdev.c | 26 ++++++++++++++++++------- include/uapi/linux/rpmb.h | 41 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 60 insertions(+), 7 deletions(-) diff --git a/drivers/char/rpmb/cdev.c b/drivers/char/rpmb/cdev.c index bdac3894b111..028c7ecd2ac7 100644 --- a/drivers/char/rpmb/cdev.c +++ b/drivers/char/rpmb/cdev.c @@ -67,18 +67,28 @@ static int rpmb_release(struct inode *inode, struct file *fp) return 0; } +static size_t rpmb_ioc_frames_len(struct rpmb_dev *rdev, size_t nframes) +{ + if (rdev->ops->type == RPMB_TYPE_NVME) + return rpmb_ioc_frames_len_nvme(nframes); + else + return rpmb_ioc_frames_len_jdec(nframes); +} + /** * rpmb_cmd_copy_from_user - copy rpmb command from the user space * + * @rdev: rpmb device * @cmd: internal cmd structure * @ucmd: user space cmd structure * * Return: 0 on success, <0 on error */ -static int rpmb_cmd_copy_from_user(struct rpmb_cmd *cmd, +static int rpmb_cmd_copy_from_user(struct rpmb_dev *rdev, + struct rpmb_cmd *cmd, struct rpmb_ioc_cmd __user *ucmd) { - struct rpmb_frame *frames; + void *frames; u64 frames_ptr; if (get_user(cmd->flags, &ucmd->flags)) @@ -95,7 +105,7 @@ static int rpmb_cmd_copy_from_user(struct rpmb_cmd *cmd, return -EFAULT; frames = memdup_user(u64_to_user_ptr(frames_ptr), - rpmb_ioc_frames_len_jdec(cmd->nframes)); + rpmb_ioc_frames_len(rdev, cmd->nframes)); if (IS_ERR(frames)) return PTR_ERR(frames); @@ -106,12 +116,14 @@ static int rpmb_cmd_copy_from_user(struct rpmb_cmd *cmd, /** * rpmb_cmd_copy_to_user - copy rpmb command to the user space * + * @rdev: rpmb device * @ucmd: user space cmd structure * @cmd: internal cmd structure * * Return: 0 on success, <0 on error */ -static int rpmb_cmd_copy_to_user(struct rpmb_ioc_cmd __user *ucmd, +static int rpmb_cmd_copy_to_user(struct rpmb_dev *rdev, + struct rpmb_ioc_cmd __user *ucmd, struct rpmb_cmd *cmd) { u64 frames_ptr; @@ -121,7 +133,7 @@ static int rpmb_cmd_copy_to_user(struct rpmb_ioc_cmd __user *ucmd, /* some archs have issues with 64bit get_user */ if (copy_to_user(u64_to_user_ptr(frames_ptr), cmd->frames, - rpmb_ioc_frames_len_jdec(cmd->nframes))) + rpmb_ioc_frames_len(rdev, cmd->nframes))) return -EFAULT; return 0; @@ -167,7 +179,7 @@ static long rpmb_ioctl_seq_cmd(struct rpmb_dev *rdev, ucmds = (struct rpmb_ioc_cmd __user *)ptr->cmds; for (i = 0; i < ncmds; i++) { - ret = rpmb_cmd_copy_from_user(&cmds[i], &ucmds[i]); + ret = rpmb_cmd_copy_from_user(rdev, &cmds[i], &ucmds[i]); if (ret) goto out; } @@ -177,7 +189,7 @@ static long rpmb_ioctl_seq_cmd(struct rpmb_dev *rdev, goto out; for (i = 0; i < ncmds; i++) { - ret = rpmb_cmd_copy_to_user(&ucmds[i], &cmds[i]); + ret = rpmb_cmd_copy_to_user(rdev, &ucmds[i], &cmds[i]); if (ret) goto out; } diff --git a/include/uapi/linux/rpmb.h b/include/uapi/linux/rpmb.h index d304701cd258..f41a28ea9765 100644 --- a/include/uapi/linux/rpmb.h +++ b/include/uapi/linux/rpmb.h @@ -68,6 +68,44 @@ struct rpmb_frame_jdec { (sizeof(struct rpmb_frame_jdec) - \ offsetof(struct rpmb_frame_jdec, data)) +/** + * struct rpmb_frame_nvme - rpmb frame as defined by specs + * + * @key_mac : The authentication key or the message authentication + * code (MAC) depending on the request/response type. + * The MAC will be delivered in the last (or the only) + * block of data. + * @rpmb_target : RPMB target to access. + * @nonce : Random number generated by the host for the requests + * and copied to the response by the RPMB engine. + * @write_counter: Counter value for the total amount of the successful + * authenticated data write requests made by the host. + * @addr : Address of the data to be programmed to or read + * from the RPMB. Address is the serial number of + * the accessed block (half sector 256B). + * @block_count : Number of sctors (sectors, 512B) requested to be + * read/programmed. (In spec this field is named sector_count). + * @result : Includes information about the status of the write counter + * (valid, expired) and result of the access made to the RPMB. + * @req_resp : Defines the type of request and response to/from the memory. + * @data : variable sized payload 512 * block_count + */ +struct rpmb_frame_nvme { + __u8 key_mac[223]; + __u8 rpmb_target; + __u8 nonce[16]; + __le32 write_counter; + __le32 addr; + __le32 block_count; + __le16 result; + __le16 req_resp; + __u8 data[0]; +} __attribute__((packed)); + +#define rpmb_nvme_hmac_data_len \ + (sizeof(struct rpmb_frame_nvme) - \ + offsetof(struct rpmb_frame_nvme, rpmb_target)) + #define RPMB_PROGRAM_KEY 0x0001 /* Program RPMB Authentication Key */ #define RPMB_GET_WRITE_COUNTER 0x0002 /* Read RPMB write counter */ #define RPMB_WRITE_DATA 0x0003 /* Write data to RPMB partition */ @@ -142,6 +180,9 @@ struct rpmb_ioc_cmd { #define rpmb_ioc_frames_len_jdec(_n) \ (((_n) ?: 1) * sizeof(struct rpmb_frame_jdec)) +#define rpmb_ioc_frames_len_nvme(_n) \ + (sizeof(struct rpmb_frame_nvme) + (_n) * 512) + /** * struct rpmb_ioc_seq_cmd - rpmb command sequence * From fa3c1c1ef600fc695255215550cd8d3007148547 Mon Sep 17 00:00:00 2001 From: Tomas Winkler Date: Mon, 30 Apr 2018 12:57:51 +0300 Subject: [PATCH 1070/1103] tools/rpmb: add support for nvme device Change-Id: Iab3171a22d3f502b11beed1496959bfd8f47d568 Signed-off-by: Tomas Winkler --- tools/rpmb/rpmb.c | 816 +++++++++++++++++++++++++++++++++++++--------- 1 file changed, 656 insertions(+), 160 deletions(-) diff --git a/tools/rpmb/rpmb.c b/tools/rpmb/rpmb.c index 1a507db2739e..28b2280776d5 100644 --- a/tools/rpmb/rpmb.c +++ b/tools/rpmb/rpmb.c @@ -31,6 +31,11 @@ #define RPMB_MAC_SIZE 32 #define RPMB_NONCE_SIZE 16 +#define RPMB_FRAME_TYPE_JDEC 0 +#define RPMB_FRAME_TYPE_NVME 1 +#define RPMB_BLOCK_SIZE 256 +#define RPMB_SECTOR_SIZE 512 + bool verbose; #define rpmb_dbg(fmt, ARGS...) do { \ if (verbose) \ @@ -239,9 +244,10 @@ static ssize_t write_file(int fd, unsigned char *data, size_t size) return ret; } -static void dbg_dump_frame(const char *title, const struct rpmb_frame_jdec *f) +static void dbg_dump_frame_jdec(const char *title, const void *f, uint32_t cnt) { uint16_t result, req_resp; + const struct rpmb_frame_jdec *frame = f; if (!verbose) return; @@ -249,37 +255,405 @@ static void dbg_dump_frame(const char *title, const struct rpmb_frame_jdec *f) if (!f) return; - result = be16toh(f->result); - req_resp = be16toh(f->req_resp); + result = be16toh(frame->result); + req_resp = be16toh(frame->req_resp); if (req_resp & 0xf00) req_resp = RPMB_RESP2REQ(req_resp); fprintf(stderr, "--------------- %s ---------------\n", title ? title : "start"); fprintf(stderr, "ptr: %p\n", f); - dump_hex_buffer("key_mac: ", f->key_mac, 32); - dump_hex_buffer("data: ", f->data, 256); - dump_hex_buffer("nonce: ", f->nonce, 16); - fprintf(stderr, "write_counter: %u\n", be32toh(f->write_counter)); - fprintf(stderr, "address: %0X\n", be16toh(f->addr)); - fprintf(stderr, "block_count: %u\n", be16toh(f->block_count)); + dump_hex_buffer("key_mac: ", frame->key_mac, 32); + dump_hex_buffer("data: ", frame->data, 256); + dump_hex_buffer("nonce: ", frame->nonce, 16); + fprintf(stderr, "write_counter: %u\n", be32toh(frame->write_counter)); + fprintf(stderr, "address: %0X\n", be16toh(frame->addr)); + fprintf(stderr, "block_count: %u\n", be16toh(frame->block_count)); fprintf(stderr, "result %s:%d\n", rpmb_result_str(result), result); fprintf(stderr, "req_resp %s\n", rpmb_op_str(req_resp)); fprintf(stderr, "--------------- End ---------------\n"); } -static struct rpmb_frame_jdec *rpmb_alloc_frames(unsigned int cnt) +static void dbg_dump_frame_nvme(const char *title, const void *f, uint32_t cnt) +{ + uint16_t result, req_resp; + uint32_t keysize = 4; + uint32_t sector_count; + const struct rpmb_frame_nvme *frame = f; + + if (!verbose) + return; + + if (!f) + return; + + result = le16toh(frame->result); + req_resp = le16toh(frame->req_resp); + if (req_resp & 0xf00) + req_resp = RPMB_RESP2REQ(req_resp); + + sector_count = le32toh(frame->block_count); + + fprintf(stderr, "--------------- %s ---------------\n", + title ? title : "start"); + fprintf(stderr, "ptr: %p\n", f); + dump_hex_buffer("key_mac: ", &frame->key_mac[223 - keysize], keysize); + dump_hex_buffer("nonce: ", frame->nonce, 16); + fprintf(stderr, "rpmb_target: %u\n", frame->rpmb_target); + fprintf(stderr, "write_counter: %u\n", le32toh(frame->write_counter)); + fprintf(stderr, "address: %0X\n", le32toh(frame->addr)); + fprintf(stderr, "block_count: %u\n", sector_count); + fprintf(stderr, "result %s:%d\n", rpmb_result_str(result), result); + fprintf(stderr, "req_resp %s\n", rpmb_op_str(req_resp)); + dump_hex_buffer("data: ", frame->data, RPMB_SECTOR_SIZE * cnt); + fprintf(stderr, "--------------- End --------------\n"); +} + +static void dbg_dump_frame(uint8_t frame_type, const char *title, + const void *f, uint32_t cnt) +{ + if (frame_type == RPMB_FRAME_TYPE_NVME) + dbg_dump_frame_nvme(title, f, cnt); + else + dbg_dump_frame_jdec(title, f, cnt); +} + +static int rpmb_frame_set_key_mac_jdec(void *f, uint32_t block_count, + uint8_t *key_mac, size_t key_mac_size) +{ + struct rpmb_frame_jdec *frames = f; + + if (block_count == 0) + block_count = 1; + + memcpy(&frames[block_count - 1].key_mac, key_mac, key_mac_size); + + return 0; +} + +static int rpmb_frame_set_key_mac_nvme(void *f, uint32_t block_count, + uint8_t *key_mac, size_t key_mac_size) +{ + struct rpmb_frame_nvme *frame = f; + + memcpy(&frame->key_mac[223 - key_mac_size], key_mac, key_mac_size); + + return 0; +} + +static int rpmb_frame_set_key_mac(uint8_t frame_type, void *f, + uint32_t block_count, + uint8_t *key_mac, size_t key_mac_size) +{ + if (frame_type == RPMB_FRAME_TYPE_NVME) + return rpmb_frame_set_key_mac_nvme(f, block_count, + key_mac, key_mac_size); + else + return rpmb_frame_set_key_mac_jdec(f, block_count, + key_mac, key_mac_size); +} + +static uint8_t *rpmb_frame_get_key_mac_ptr_jdec(void *f, uint32_t block_count, + size_t key_size) { - return calloc(1, rpmb_ioc_frames_len_jdec(cnt)); + struct rpmb_frame_jdec *frame = f; + + if (block_count == 0) + block_count = 1; + + return frame[block_count - 1].key_mac; +} + +static uint8_t *rpmb_frame_get_key_mac_ptr_nvme(void *f, uint32_t block_count, + size_t key_size) +{ + struct rpmb_frame_nvme *frame = f; + + return &frame->key_mac[223 - key_size]; +} + +static uint8_t *rpmb_frame_get_key_mac_ptr(uint8_t frame_type, void *f, + uint32_t block_count, + size_t key_size) +{ + if (frame_type == RPMB_FRAME_TYPE_NVME) + return rpmb_frame_get_key_mac_ptr_nvme(f, block_count, + key_size); + else + return rpmb_frame_get_key_mac_ptr_jdec(f, block_count, + key_size); +} + +static uint8_t *rpmb_frame_get_nonce_ptr_jdec(void *f) +{ + struct rpmb_frame_jdec *frame = f; + + return frame->nonce; +} + +static uint8_t *rpmb_frame_get_nonce_ptr_nvme(void *f) +{ + struct rpmb_frame_nvme *frame = f; + + return frame->nonce; +} + +static uint8_t *rpmb_frame_get_nonce_ptr(uint8_t frame_type, void *f) +{ + return frame_type == RPMB_FRAME_TYPE_NVME ? + rpmb_frame_get_nonce_ptr_nvme(f) : + rpmb_frame_get_nonce_ptr_jdec(f); +} + +static uint32_t rpmb_frame_get_write_counter_jdec(void *f) +{ + struct rpmb_frame_jdec *frame = f; + + return be32toh(frame->write_counter); +} + +static uint32_t rpmb_frame_get_write_counter_nvme(void *f) +{ + struct rpmb_frame_nvme *frame = f; + + return le32toh(frame->write_counter); +} + +static uint32_t rpmb_frame_get_write_counter(uint8_t frame_type, void *f) +{ + return (frame_type == RPMB_FRAME_TYPE_NVME) ? + rpmb_frame_get_write_counter_nvme(f) : + rpmb_frame_get_write_counter_jdec(f); +} + +static uint32_t rpmb_frame_get_addr_jdec(void *f) +{ + struct rpmb_frame_jdec *frame = f; + + return be16toh(frame->addr); +} + +static uint32_t rpmb_frame_get_addr_nvme(void *f) +{ + struct rpmb_frame_nvme *frame = f; + + return le32toh(frame->addr); +} + +static uint32_t rpmb_frame_get_addr(uint8_t frame_type, void *f) +{ + return (frame_type == RPMB_FRAME_TYPE_NVME) ? + rpmb_frame_get_addr_nvme(f) : + rpmb_frame_get_addr_jdec(f); +} + +static uint16_t rpmb_frame_get_result_jdec(void *f) +{ + struct rpmb_frame_jdec *frames = f; + uint16_t block_count = be16toh(frames[0].block_count); + + if (block_count == 0) + block_count = 1; + + return be16toh(frames[block_count - 1].result); +} + +static uint16_t rpmb_frame_get_result_nvme(void *f) +{ + struct rpmb_frame_nvme *frame = f; + + return le16toh(frame->result); +} + +static uint16_t rpmb_frame_get_result(uint8_t frame_type, void *f) +{ + return (frame_type == RPMB_FRAME_TYPE_NVME) ? + rpmb_frame_get_result_nvme(f) : + rpmb_frame_get_result_jdec(f); +} + +static uint16_t rpmb_frame_get_req_resp_jdec(void *f) +{ + struct rpmb_frame_jdec *frame = f; + + return be16toh(frame->req_resp); +} + +static uint16_t rpmb_frame_get_req_resp_nvme(void *f) +{ + struct rpmb_frame_nvme *frame = f; + + return le16toh(frame->req_resp); +} + +static uint16_t rpmb_frame_get_req_resp(uint8_t frame_type, void *f) +{ + return frame_type == RPMB_FRAME_TYPE_NVME ? + rpmb_frame_get_req_resp_nvme(f) : + rpmb_frame_get_req_resp_jdec(f); +} + +static int rpmb_frame_set_jdec(void *f, + uint16_t req_resp, uint32_t block_count, + uint32_t addr, uint32_t write_counter) +{ + struct rpmb_frame_jdec *frames = f; + uint32_t i; + /* FIMXE: validate overflow */ + uint16_t __block_count = (uint16_t)block_count; + uint16_t __addr = (uint16_t)addr; + + for (i = 0; i < (block_count ?: 1); i++) { + frames[i].req_resp = htobe16(req_resp); + frames[i].block_count = htobe16(__block_count); + frames[i].addr = htobe16(__addr); + frames[i].write_counter = htobe32(write_counter); + } + + return 0; +} + +static int rpmb_frame_set_nvme(void *f, + uint16_t req_resp, uint32_t block_count, + uint32_t addr, uint32_t write_counter) +{ + struct rpmb_frame_nvme *frame = f; + + frame->req_resp = htole16(req_resp); + frame->block_count = htole32(block_count); + frame->addr = htole32(addr); + frame->write_counter = htole32(write_counter); + + return 0; +} + +static int rpmb_frame_set(uint8_t frame_type, void *f, + uint16_t req_resp, uint32_t block_count, + uint32_t addr, uint32_t write_counter) +{ + if (frame_type == RPMB_FRAME_TYPE_NVME) { + return rpmb_frame_set_nvme(f, req_resp, block_count, + addr, write_counter); + } else { + return rpmb_frame_set_jdec(f, req_resp, block_count, + addr, write_counter); + } +} + +static int rpmb_frame_write_data_jdec(int fd, void *f) +{ + struct rpmb_frame_jdec *frames = f; + uint16_t i, block_count = be16toh(frames[0].block_count); + + for (i = 0; i < block_count; i++) { + int ret; + + ret = write_file(fd, frames[i].data, sizeof(frames[i].data)); + if (ret < 0) + return ret; + } + return 0; +} + +static int rpmb_frame_write_data_nvme(int fd, void *f) +{ + struct rpmb_frame_nvme *frame = f; + uint32_t i, block_count = le32toh(frame->block_count); + + for (i = 0; i < block_count; i++) { + int ret; + + ret = write_file(fd, &frame->data[i], RPMB_SECTOR_SIZE); + if (ret < 0) + return ret; + } + return 0; +} + +static int rpmb_frame_write_data(uint8_t frame_type, int fd, void *f) +{ + return frame_type == RPMB_FRAME_TYPE_NVME ? + rpmb_frame_write_data_nvme(fd, f) : + rpmb_frame_write_data_jdec(fd, f); +} + +static int rpmb_frame_read_data_jdec(int fd, void *f) +{ + struct rpmb_frame_jdec *frames = f; + uint16_t i, block_count = be16toh(frames[0].block_count); + + for (i = 0; i < block_count; i++) { + int ret = read_file(fd, frames[i].data, + sizeof(frames[0].data)); + if (ret < 0) + return ret; + } + + return 0; +} + +static int rpmb_frame_read_data_nvme(int fd, void *f) +{ + struct rpmb_frame_nvme *frame = f; + uint32_t i, block_count = le32toh(frame->block_count); + + for (i = 0; i < block_count; i++) { + int ret; + + ret = read_file(fd, &frame->data[i], RPMB_SECTOR_SIZE); + if (ret < 0) + return ret; + } + + return 0; +} + +static int rpmb_frame_read_data(uint8_t frame_type, int fd, void *f) +{ + return frame_type == RPMB_FRAME_TYPE_NVME ? + rpmb_frame_read_data_nvme(fd, f) : + rpmb_frame_read_data_jdec(fd, f); } #if OPENSSL_VERSION_NUMBER < 0x10100000L -static int rpmb_calc_hmac_sha256(struct rpmb_frame_jdec *frames, - size_t blocks_cnt, - const unsigned char key[], - unsigned int key_size, - unsigned char mac[], - unsigned int mac_size) +static int rpmb_calc_hmac_sha256_jdec(struct rpmb_frame_jdec *frames, + size_t blocks_cnt, + const unsigned char key[], + unsigned int key_size, + unsigned char mac[], + unsigned int mac_size) +{ + HMAC_CTX ctx; + int ret; + unsigned int i; + + /* SSL returns 1 on success 0 on failure */ + + HMAC_CTX_init(&ctx); + ret = HMAC_Init_ex(&ctx, key, key_size, EVP_sha256(), NULL); + if (ret == 0) + goto out; + for (i = 0; i < block_count; i++) + HMAC_Update(&ctx, frames[i].data, rpmb_jdec_hmac_data_len); + + ret = HMAC_Final(&ctx, mac, &mac_size); + if (ret == 0) + goto out; + if (mac_size != RPMB_MAC_SIZE) + ret = 0; + + ret = 1; +out: + HMAC_CTX_cleanup(&ctx); + return ret == 1 ? 0 : -1; +} + +static int rpmb_calc_hmac_sha256_nvme(struct rpmb_frame_nvme *frame, + size_t block_count, + const unsigned char key[], + unsigned int key_size, + unsigned char mac[], + unsigned int mac_size) { HMAC_CTX ctx; int ret; @@ -291,8 +665,10 @@ static int rpmb_calc_hmac_sha256(struct rpmb_frame_jdec *frames, ret = HMAC_Init_ex(&ctx, key, key_size, EVP_sha256(), NULL); if (ret == 0) goto out; - for (i = 0; i < blocks_cnt; i++) - HMAC_Update(&ctx, frames[i].data, hmac_data_len); + + HMAC_Update(&ctx, &frame->rpmb_target, hmac_nvme_data_len); + for (i = 0; i < block_count; i++) + HMAC_Update(&ctx, frames->data[i], RPMB_SECTOR_SIZE); ret = HMAC_Final(&ctx, mac, &mac_size); if (ret == 0) @@ -306,12 +682,12 @@ static int rpmb_calc_hmac_sha256(struct rpmb_frame_jdec *frames, return ret == 1 ? 0 : -1; } #else -static int rpmb_calc_hmac_sha256(struct rpmb_frame_jdec *frames, - size_t blocks_cnt, - const unsigned char key[], - unsigned int key_size, - unsigned char mac[], - unsigned int mac_size) +static int rpmb_calc_hmac_sha256_jdec(struct rpmb_frame_jdec *frames, + size_t blocks_cnt, + const unsigned char key[], + unsigned int key_size, + unsigned char mac[], + unsigned int mac_size) { HMAC_CTX *ctx; int ret; @@ -338,37 +714,83 @@ static int rpmb_calc_hmac_sha256(struct rpmb_frame_jdec *frames, HMAC_CTX_free(ctx); return ret == 1 ? 0 : -1; } + +static int rpmb_calc_hmac_sha256_nvme(struct rpmb_frame_nvme *frame, + size_t block_count, + const unsigned char key[], + unsigned int key_size, + unsigned char mac[], + unsigned int mac_size) +{ + HMAC_CTX *ctx; + int ret; + unsigned int i; + + /* SSL returns 1 on success 0 on failure */ + + ctx = HMAC_CTX_new(); + + ret = HMAC_Init_ex(ctx, key, key_size, EVP_sha256(), NULL); + if (ret == 0) + goto out; + + HMAC_Update(ctx, &frame->rpmb_target, rpmb_nvme_hmac_data_len); + for (i = 0; i < block_count; i++) + HMAC_Update(ctx, &frame->data[i], RPMB_SECTOR_SIZE); + + ret = HMAC_Final(ctx, mac, &mac_size); + if (ret == 0) + goto out; + if (mac_size != RPMB_MAC_SIZE) + ret = 0; + + ret = 1; +out: + HMAC_CTX_free(ctx); + return ret == 1 ? 0 : -1; +} #endif -static int rpmb_check_req_resp(uint16_t req, struct rpmb_frame_jdec *frame_out) +static int rpmb_calc_hmac_sha256(uint8_t frame_type, void *f, + size_t block_count, + const unsigned char key[], + unsigned int key_size, + unsigned char mac[], + unsigned int mac_size) { - if (RPMB_REQ2RESP(req) != be16toh(frame_out->req_resp)) { - rpmb_err("RPMB response mismatch %04X != %04X\n.", - RPMB_REQ2RESP(req), be16toh(frame_out->req_resp)); - return -1; - } - return 0; + if (frame_type == RPMB_FRAME_TYPE_NVME) + return rpmb_calc_hmac_sha256_nvme(f, block_count, + key, key_size, + mac, mac_size); + else + return rpmb_calc_hmac_sha256_jdec(f, block_count, + key, key_size, + mac, mac_size); } -static int rpmb_check_mac(const unsigned char *key, - struct rpmb_frame_jdec *frames_out, - unsigned int cnt_out) +static int rpmb_check_mac(uint8_t frame_type, + const unsigned char *key, size_t key_size, + void *frames_out, unsigned int block_count) { unsigned char mac[RPMB_MAC_SIZE]; + unsigned char *mac_out; + int ret; - if (cnt_out == 0) { + if (block_count == 0) { rpmb_err("RPMB 0 output frames.\n"); return -1; } - rpmb_calc_hmac_sha256(frames_out, cnt_out, - key, RPMB_KEY_SIZE, - mac, RPMB_MAC_SIZE); + ret = rpmb_calc_hmac_sha256(frame_type, frames_out, block_count, + key, key_size, mac, RPMB_MAC_SIZE); + if (ret) + return ret; - if (memcmp(mac, frames_out[cnt_out - 1].key_mac, RPMB_MAC_SIZE)) { + mac_out = rpmb_frame_get_key_mac_ptr(frame_type, frames_out, + block_count, RPMB_MAC_SIZE); + if (memcmp(mac, mac_out, RPMB_MAC_SIZE)) { rpmb_err("RPMB hmac mismatch:\n"); - dump_hex_buffer("Result MAC: ", - frames_out[cnt_out - 1].key_mac, RPMB_MAC_SIZE); + dump_hex_buffer("Result MAC: ", mac_out, RPMB_MAC_SIZE); dump_hex_buffer("Expected MAC: ", mac, RPMB_MAC_SIZE); return -1; } @@ -376,23 +798,54 @@ static int rpmb_check_mac(const unsigned char *key, return 0; } -static int rpmb_ioctl(int fd, uint16_t req, - const struct rpmb_frame_jdec *frames_in, - unsigned int cnt_in, - struct rpmb_frame_jdec *frames_out, - unsigned int cnt_out) +static int rpmb_check_req_resp(uint8_t frame_type, + uint16_t req, void *frame_out) +{ + uint16_t req_resp = rpmb_frame_get_req_resp(frame_type, frame_out); + + if (RPMB_REQ2RESP(req) != req_resp) { + rpmb_err("RPMB response mismatch %04X != %04X\n.", + RPMB_REQ2RESP(req), req_resp); + return -1; + } + + return 0; +} + +static struct rpmb_frame_jdec *rpmb_frame_alloc_jdec(size_t block_count) +{ + return calloc(1, rpmb_ioc_frames_len_jdec(block_count)); +} + +static struct rpmb_frame_nvme *rpmb_frame_alloc_nvme(size_t sector_count) +{ + return calloc(1, rpmb_ioc_frames_len_nvme(sector_count)); +} + +static void *rpmb_frame_alloc(uint8_t type, size_t count) +{ + if (type == RPMB_FRAME_TYPE_NVME) + return rpmb_frame_alloc_nvme(count); + else + return rpmb_frame_alloc_jdec(count); +} + +static int rpmb_ioctl(uint8_t frame_type, int fd, uint16_t req, + const void *frames_in, unsigned int cnt_in, + void *frames_out, unsigned int cnt_out) { int ret; struct __attribute__((packed)) { struct rpmb_ioc_seq_cmd h; struct rpmb_ioc_cmd cmd[3]; } iseq = {}; - struct rpmb_frame_jdec *frame_res = NULL; + + void *frame_res = NULL; int i; uint32_t flags; rpmb_dbg("RPMB OP: %s\n", rpmb_op_str(req)); - dbg_dump_frame("In Frame: ", frames_in); + dbg_dump_frame(frame_type, "In Frame: ", frames_in, cnt_in); i = 0; flags = RPMB_F_WRITE; @@ -402,10 +855,11 @@ static int rpmb_ioctl(int fd, uint16_t req, i++; if (req == RPMB_WRITE_DATA || req == RPMB_PROGRAM_KEY) { - frame_res = rpmb_alloc_frames(0); + frame_res = rpmb_frame_alloc(frame_type, 0); if (!frame_res) return -ENOMEM; - frame_res->req_resp = htobe16(RPMB_RESULT_READ); + rpmb_frame_set(frame_type, frame_res, + RPMB_RESULT_READ, 0, 0, 0); rpmb_ioc_cmd_set(iseq.cmd[i], RPMB_F_WRITE, frame_res, 0); i++; } @@ -418,10 +872,10 @@ static int rpmb_ioctl(int fd, uint16_t req, if (ret < 0) rpmb_err("ioctl failure %d: %s.\n", ret, strerror(errno)); - ret = rpmb_check_req_resp(req, frames_out); + ret = rpmb_check_req_resp(frame_type, req, frames_out); - dbg_dump_frame("Res Frame: ", frame_res); - dbg_dump_frame("Out Frame: ", frames_out); + dbg_dump_frame(frame_type, "Res Frame: ", frame_res, 1); + dbg_dump_frame(frame_type, "Out Frame: ", frames_out, cnt_out); free(frame_res); return ret; } @@ -453,13 +907,61 @@ static int op_get_info(int nargs, char *argv[]) return 0; } +static int __rpmb_program_key(uint8_t frame_type, int dev_fd, + uint8_t *key, size_t key_size) +{ + void *frame_in, *frame_out; + uint16_t req = RPMB_PROGRAM_KEY; + int ret; + + frame_in = rpmb_frame_alloc(frame_type, 0); + frame_out = rpmb_frame_alloc(frame_type, 0); + if (!frame_in || !frame_out) { + ret = -ENOMEM; + goto out; + } + + rpmb_frame_set(frame_type, frame_in, req, 0, 0, 0); + + ret = rpmb_frame_set_key_mac(frame_type, frame_in, 0, key, key_size); + if (ret) + goto out; + + ret = rpmb_ioctl(frame_type, dev_fd, req, frame_in, 1, frame_out, 1); + if (ret) + goto out; + + ret = rpmb_check_req_resp(frame_type, req, frame_out); + if (ret) + goto out; + + ret = rpmb_frame_get_result(frame_type, frame_out); + if (ret) + rpmb_err("RPMB operation %s failed, %s[0x%04x].\n", + rpmb_op_str(req), rpmb_result_str(ret), ret); + +out: + free(frame_in); + free(frame_out); + + return 0; +} + +static uint8_t rpmb_cap_get_frame_type(struct rpmb_ioc_cap_cmd *cap) +{ + if (cap->device_type == RPMB_TYPE_NVME) + return RPMB_FRAME_TYPE_NVME; + else + return RPMB_FRAME_TYPE_JDEC; +} + static int op_rpmb_program_key(int nargs, char *argv[]) { int ret; int dev_fd = -1, key_fd = -1; - uint16_t req = RPMB_PROGRAM_KEY; + uint8_t key[RPMB_KEY_SIZE]; + uint8_t frame_type; struct rpmb_ioc_cap_cmd cap; - struct rpmb_frame_jdec *frame_in = NULL, *frame_out = NULL; ret = -EINVAL; if (nargs != 2) @@ -475,87 +977,73 @@ static int op_rpmb_program_key(int nargs, char *argv[]) goto out; argv++; - frame_in = rpmb_alloc_frames(0); - frame_out = rpmb_alloc_frames(0); - if (!frame_in || !frame_out) { - ret = -ENOMEM; - goto out; - } + read_file(key_fd, key, RPMB_KEY_SIZE); - frame_in->req_resp = htobe16(req); + frame_type = rpmb_cap_get_frame_type(&cap); - read_file(key_fd, frame_in->key_mac, RPMB_KEY_SIZE); - - ret = rpmb_ioctl(dev_fd, req, frame_in, 0, frame_out, 0); - if (ret) - goto out; - - if (RPMB_REQ2RESP(req) != be16toh(frame_out->req_resp)) { - rpmb_err("RPMB response mismatch.\n"); - ret = -1; - goto out; - } - - ret = be16toh(frame_out->result); - if (ret) - rpmb_err("RPMB operation %s failed, %s[0x%04x].\n", - rpmb_op_str(req), rpmb_result_str(ret), ret); + ret = __rpmb_program_key(frame_type, dev_fd, key, RPMB_KEY_SIZE); out: - free(frame_in); - free(frame_out); close_fd(dev_fd); close_fd(key_fd); return ret; } -static int rpmb_get_write_counter(int dev_fd, unsigned int *cnt, - const unsigned char *key) +static int rpmb_get_write_counter(uint8_t frame_type, int dev_fd, + unsigned int *cnt, const unsigned char *key) { int ret; uint16_t res = 0x000F; uint16_t req = RPMB_GET_WRITE_COUNTER; - struct rpmb_frame_jdec *frame_in = NULL; - struct rpmb_frame_jdec *frame_out = NULL; + void *frame_in = NULL; + void *frame_out = NULL; + uint8_t *nonce_in; + uint8_t *nonce_out; - frame_in = rpmb_alloc_frames(0); - frame_out = rpmb_alloc_frames(0); + frame_in = rpmb_frame_alloc(frame_type, 0); + frame_out = rpmb_frame_alloc(frame_type, 0); if (!frame_in || !frame_out) { ret = -ENOMEM; goto out; } - frame_in->req_resp = htobe16(req); - RAND_bytes(frame_in->nonce, RPMB_NONCE_SIZE); + rpmb_frame_set(frame_type, frame_in, req, 0, 0, 0); + nonce_in = rpmb_frame_get_nonce_ptr(frame_type, frame_in); + RAND_bytes(nonce_in, RPMB_NONCE_SIZE); - ret = rpmb_ioctl(dev_fd, req, frame_in, 0, frame_out, 0); + ret = rpmb_ioctl(frame_type, dev_fd, req, frame_in, 0, frame_out, 0); if (ret) goto out; - res = be16toh(frame_out->result); + ret = rpmb_check_req_resp(frame_type, req, frame_out); + if (ret) + goto out; + + res = rpmb_frame_get_result(frame_type, frame_out); if (res != RPMB_ERR_OK) { ret = -1; goto out; } - if (memcmp(&frame_in->nonce, &frame_out->nonce, RPMB_NONCE_SIZE)) { + nonce_out = rpmb_frame_get_nonce_ptr(frame_type, frame_out); + + if (memcmp(nonce_in, nonce_out, RPMB_NONCE_SIZE)) { rpmb_err("RPMB NONCE mismatch\n"); - dump_hex_buffer("Result NONCE:", - &frame_out->nonce, RPMB_NONCE_SIZE); - dump_hex_buffer("Expected NONCE: ", - &frame_in->nonce, RPMB_NONCE_SIZE); + dump_hex_buffer("Result NONCE:", nonce_out, RPMB_NONCE_SIZE); + dump_hex_buffer("Expected NONCE: ", nonce_in, RPMB_NONCE_SIZE); ret = -1; goto out; } if (key) { - ret = rpmb_check_mac(key, frame_out, 1); + ret = rpmb_check_mac(frame_type, key, RPMB_KEY_SIZE, + frame_out, 1); if (ret) goto out; } - *cnt = be32toh(frame_out->write_counter); + *cnt = rpmb_frame_get_write_counter(frame_type, frame_out); out: if (ret) @@ -573,7 +1061,8 @@ static int op_rpmb_get_write_counter(int nargs, char **argv) bool has_key; struct rpmb_ioc_cap_cmd cap; unsigned char key[RPMB_KEY_SIZE]; - unsigned int cnt; + unsigned int cnt = 0; + uint8_t frame_type; if (nargs == 2) has_key = true; @@ -588,6 +1077,8 @@ static int op_rpmb_get_write_counter(int nargs, char **argv) return ret; argv++; + frame_type = rpmb_cap_get_frame_type(&cap); + if (has_key) { key_fd = open_rd_file(argv[0], "key file"); if (key_fd < 0) @@ -598,9 +1089,9 @@ static int op_rpmb_get_write_counter(int nargs, char **argv) if (ret < 0) goto out; - ret = rpmb_get_write_counter(dev_fd, &cnt, key); + ret = rpmb_get_write_counter(frame_type, dev_fd, &cnt, key); } else { - ret = rpmb_get_write_counter(dev_fd, &cnt, NULL); + ret = rpmb_get_write_counter(frame_type, dev_fd, &cnt, NULL); } if (!ret) @@ -614,17 +1105,18 @@ static int op_rpmb_get_write_counter(int nargs, char **argv) static int op_rpmb_read_blocks(int nargs, char **argv) { - int i, ret; + int ret; int dev_fd = -1, data_fd = -1, key_fd = -1; uint16_t req = RPMB_READ_DATA; - uint16_t addr, blocks_cnt; + uint32_t addr, block_count; unsigned char key[RPMB_KEY_SIZE]; + uint8_t *nonce_in; unsigned long numarg; bool has_key; struct rpmb_ioc_cap_cmd cap; - struct rpmb_frame_jdec *frame_in = NULL; - struct rpmb_frame_jdec *frames_out = NULL; - struct rpmb_frame_jdec *frame_out; + void *frame_in = NULL; + void *frames_out = NULL; + uint8_t frame_type; ret = -EINVAL; if (nargs == 4) @@ -641,23 +1133,23 @@ static int op_rpmb_read_blocks(int nargs, char **argv) errno = 0; numarg = strtoul(argv[0], NULL, 0); - if (errno || numarg > USHRT_MAX) { + if (errno || numarg > UINT_MAX) { rpmb_err("wrong block address\n"); goto out; } - addr = (uint16_t)numarg; + addr = (uint32_t)numarg; argv++; errno = 0; numarg = strtoul(argv[0], NULL, 0); - if (errno || numarg > USHRT_MAX) { + if (errno || numarg > UINT_MAX) { rpmb_err("wrong blocks count\n"); goto out; } - blocks_cnt = (uint16_t)numarg; + block_count = (uint32_t)numarg; argv++; - if (blocks_cnt == 0) { + if (block_count == 0) { rpmb_err("wrong blocks count\n"); goto out; } @@ -678,27 +1170,30 @@ static int op_rpmb_read_blocks(int nargs, char **argv) goto out; } + frame_type = rpmb_cap_get_frame_type(&cap); + ret = 0; - frames_out = rpmb_alloc_frames(blocks_cnt); - frame_in = rpmb_alloc_frames(0); + frames_out = rpmb_frame_alloc(frame_type, block_count); + frame_in = rpmb_frame_alloc(frame_type, 0); if (!frames_out || !frame_in) { - rpmb_err("Cannot allocate %d RPMB frames\n", blocks_cnt); + rpmb_err("Cannot allocate %d RPMB frames\n", block_count); ret = -ENOMEM; goto out; } - frame_in->req_resp = htobe16(req); - frame_in->addr = htobe16(addr); - /* eMMc spec ask for 0 here this will be translated by the rpmb layer */ - frame_in->block_count = htobe16(blocks_cnt); - RAND_bytes(frame_in->nonce, RPMB_NONCE_SIZE); + /* eMMc spec ask for 0 block_count here + * this will be translated by the rpmb layer + */ + rpmb_frame_set(frame_type, frame_in, req, block_count, addr, 0); + nonce_in = rpmb_frame_get_nonce_ptr(frame_type, frame_in); + RAND_bytes(nonce_in, RPMB_NONCE_SIZE); - ret = rpmb_ioctl(dev_fd, req, frame_in, 0, frames_out, blocks_cnt); + ret = rpmb_ioctl(frame_type, dev_fd, req, frame_in, 0, + frames_out, block_count); if (ret) goto out; - frame_out = &frames_out[blocks_cnt - 1]; - ret = be16toh(frame_out->result); + ret = rpmb_frame_get_result(frame_type, frames_out); if (ret) { rpmb_err("RPMB operation %s failed, %s[0x%04x]\n", rpmb_op_str(req), rpmb_result_str(ret), ret); @@ -706,17 +1201,13 @@ static int op_rpmb_read_blocks(int nargs, char **argv) } if (has_key) { - ret = rpmb_check_mac(key, frames_out, blocks_cnt); + ret = rpmb_check_mac(frame_type, key, RPMB_KEY_SIZE, + frames_out, block_count); if (ret) goto out; } - for (i = 0; i < blocks_cnt; i++) { - ret = write_file(data_fd, frames_out[i].data, - sizeof(frames_out[i].data)); - if (ret < 0) - goto out; - } + ret = rpmb_frame_write_data(frame_type, data_fd, frames_out); out: free(frame_in); @@ -732,16 +1223,17 @@ static int op_rpmb_write_blocks(int nargs, char **argv) { int ret; int dev_fd = -1, key_fd = -1, data_fd = -1; - int i; uint16_t req = RPMB_WRITE_DATA; unsigned char key[RPMB_KEY_SIZE]; unsigned char mac[RPMB_MAC_SIZE]; unsigned long numarg; - uint16_t addr, blocks_cnt; - uint32_t write_counter; struct rpmb_ioc_cap_cmd cap; - struct rpmb_frame_jdec *frames_in = NULL; - struct rpmb_frame_jdec *frame_out = NULL; + uint16_t addr, block_count; + uint32_t write_counter = 0; + uint32_t write_counter_out = 0; + void *frames_in = NULL; + void *frame_out = NULL; + uint8_t frame_type; ret = -EINVAL; if (nargs != 5) @@ -767,10 +1259,10 @@ static int op_rpmb_write_blocks(int nargs, char **argv) rpmb_err("wrong blocks count\n"); goto out; } - blocks_cnt = (uint16_t)numarg; + block_count = (uint16_t)numarg; argv++; - if (blocks_cnt == 0) { + if (block_count == 0) { rpmb_err("wrong blocks count\n"); goto out; } @@ -789,60 +1281,64 @@ static int op_rpmb_write_blocks(int nargs, char **argv) if (ret < 0) goto out; - frames_in = rpmb_alloc_frames(blocks_cnt); - frame_out = rpmb_alloc_frames(0); + frame_type = rpmb_cap_get_frame_type(&cap); + + frames_in = rpmb_frame_alloc(frame_type, block_count); + frame_out = rpmb_frame_alloc(frame_type, 0); if (!frames_in || !frame_out) { rpmb_err("can't allocate memory for RPMB outer frames\n"); ret = -ENOMEM; goto out; } - ret = rpmb_get_write_counter(dev_fd, &write_counter, key); + ret = rpmb_get_write_counter(frame_type, dev_fd, &write_counter, NULL); if (ret) goto out; - for (i = 0; i < blocks_cnt; i++) { - frames_in[i].req_resp = htobe16(req); - frames_in[i].block_count = htobe16(blocks_cnt); - frames_in[i].addr = htobe16(addr); - frames_in[i].write_counter = htobe32(write_counter); - } + ret = rpmb_frame_set(frame_type, frames_in, + req, block_count, addr, write_counter); + if (ret) + goto out; - for (i = 0; i < blocks_cnt; i++) { - ret = read_file(data_fd, frames_in[i].data, - sizeof(frames_in[0].data)); - if (ret < 0) - goto out; - } + ret = rpmb_frame_read_data(frame_type, data_fd, frames_in); + if (ret) + goto out; - rpmb_calc_hmac_sha256(frames_in, blocks_cnt, + rpmb_calc_hmac_sha256(frame_type, frames_in, + block_count, key, RPMB_KEY_SIZE, mac, RPMB_MAC_SIZE); - memcpy(frames_in[blocks_cnt - 1].key_mac, mac, RPMB_MAC_SIZE); - ret = rpmb_ioctl(dev_fd, req, frames_in, blocks_cnt, frame_out, 0); + + rpmb_frame_set_key_mac(frame_type, frames_in, block_count, + mac, RPMB_MAC_SIZE); + + ret = rpmb_ioctl(frame_type, dev_fd, req, + frames_in, block_count, + frame_out, 0); if (ret != 0) goto out; - ret = be16toh(frame_out->result); + ret = rpmb_frame_get_result(frame_type, frame_out); if (ret) { rpmb_err("RPMB operation %s failed, %s[0x%04x]\n", rpmb_op_str(req), rpmb_result_str(ret), ret); ret = -1; } - if (be16toh(frame_out->addr) != addr) { + if (rpmb_frame_get_addr(frame_type, frame_out) != addr) { rpmb_err("RPMB addr mismatchs res=%04x req=%04x\n", - be16toh(frame_out->addr), addr); + rpmb_frame_get_addr(frame_type, frame_out), addr); ret = -1; } - if (be32toh(frame_out->write_counter) <= write_counter) { + write_counter_out = rpmb_frame_get_write_counter(frame_type, frame_out); + if (write_counter_out <= write_counter) { rpmb_err("RPMB write counter not incremented res=%x req=%x\n", - be32toh(frame_out->write_counter), write_counter); + write_counter_out, write_counter); ret = -1; } - ret = rpmb_check_mac(key, frame_out, 1); + /* TODO: check mac: spec is not clear what is computed by the device */ out: free(frames_in); free(frame_out); From 3be2a3961a59b01b2d418397a58de6bffdd7025a Mon Sep 17 00:00:00 2001 From: Tomas Winkler Date: Wed, 25 Apr 2018 19:07:06 +0300 Subject: [PATCH 1071/1103] nvme: connect to rpmb layer This patch covers rpmb storage operation as defined in NVMe spec 1.3a in section 8.10. It only covers standard RPMB storage API, the device configuration is not covered. Change-Id: I35c9cc7aeec5a08041b9986d60fc9ee55c66dda7 Signed-off-by: Tomas Winkler --- drivers/nvme/host/Kconfig | 1 + drivers/nvme/host/Makefile | 1 + drivers/nvme/host/core.c | 54 +++++++++++++---- drivers/nvme/host/nvme.h | 9 +++ drivers/nvme/host/pci.c | 4 ++ drivers/nvme/host/rpmb.c | 119 +++++++++++++++++++++++++++++++++++++ 6 files changed, 178 insertions(+), 10 deletions(-) create mode 100644 drivers/nvme/host/rpmb.c diff --git a/drivers/nvme/host/Kconfig b/drivers/nvme/host/Kconfig index 88a8b5916624..a0027cebf2db 100644 --- a/drivers/nvme/host/Kconfig +++ b/drivers/nvme/host/Kconfig @@ -5,6 +5,7 @@ config BLK_DEV_NVME tristate "NVM Express block device" depends on PCI && BLOCK select NVME_CORE + select RPMB ---help--- The NVM Express driver is for solid state drives directly connected to the PCI or PCI Express bus. If you know you diff --git a/drivers/nvme/host/Makefile b/drivers/nvme/host/Makefile index aea459c65ae1..99f99e87b82b 100644 --- a/drivers/nvme/host/Makefile +++ b/drivers/nvme/host/Makefile @@ -15,6 +15,7 @@ nvme-core-$(CONFIG_NVM) += lightnvm.o nvme-core-$(CONFIG_FAULT_INJECTION_DEBUG_FS) += fault_inject.o nvme-y += pci.o +nvme-y += rpmb.o nvme-fabrics-y += fabrics.o diff --git a/drivers/nvme/host/core.c b/drivers/nvme/host/core.c index dd8ec1dd9219..43aa98f729d6 100644 --- a/drivers/nvme/host/core.c +++ b/drivers/nvme/host/core.c @@ -11,7 +11,6 @@ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. */ - #include #include #include @@ -1659,25 +1658,57 @@ static const struct pr_ops nvme_pr_ops = { .pr_clear = nvme_pr_clear, }; -#ifdef CONFIG_BLK_SED_OPAL -int nvme_sec_submit(void *data, u16 spsp, u8 secp, void *buffer, size_t len, - bool send) +int nvme_sec_send(struct nvme_ctrl *ctrl, u8 nssf, u16 spsp, u8 secp, + void *buffer, size_t len) { - struct nvme_ctrl *ctrl = data; struct nvme_command cmd; + dev_dbg(ctrl->device, "%s target = %hhu SPSP = %hu SECP = %hhX len=%zd\n", + __func__, nssf, spsp, secp, len); + memset(&cmd, 0, sizeof(cmd)); - if (send) - cmd.common.opcode = nvme_admin_security_send; - else - cmd.common.opcode = nvme_admin_security_recv; + cmd.common.opcode = nvme_admin_security_send; + cmd.common.nsid = 0; + cmd.common.cdw10[0] = + cpu_to_le32(((u32)secp) << 24 | ((u32)spsp) << 8 | nssf); + cmd.common.cdw10[1] = cpu_to_le32(len); + + return __nvme_submit_sync_cmd(ctrl->admin_q, &cmd, NULL, buffer, len, + ADMIN_TIMEOUT, NVME_QID_ANY, 1, 0); +} +EXPORT_SYMBOL_GPL(nvme_sec_send); + +int nvme_sec_recv(struct nvme_ctrl *ctrl, u8 nssf, u16 spsp, u8 secp, + void *buffer, size_t len) +{ + struct nvme_command cmd; + + dev_dbg(ctrl->device, "%s target = %hhu SPSP = %hu SECP = %hhX len=%zd\n", + __func__, nssf, spsp, secp, len); + + memset(&cmd, 0, sizeof(cmd)); + cmd.common.opcode = nvme_admin_security_recv; cmd.common.nsid = 0; - cmd.common.cdw10[0] = cpu_to_le32(((u32)secp) << 24 | ((u32)spsp) << 8); + cmd.common.cdw10[0] = + cpu_to_le32(((u32)secp) << 24 | ((u32)spsp) << 8 | nssf); cmd.common.cdw10[1] = cpu_to_le32(len); return __nvme_submit_sync_cmd(ctrl->admin_q, &cmd, NULL, buffer, len, ADMIN_TIMEOUT, NVME_QID_ANY, 1, 0); } +EXPORT_SYMBOL_GPL(nvme_sec_recv); + +#ifdef CONFIG_BLK_SED_OPAL +int nvme_sec_submit(void *data, u16 spsp, u8 secp, void *buffer, size_t len, + bool send) +{ + struct nvme_ctrl *ctrl = data; + + if (send) + return nvme_sec_send(ctrl, 0, spsp, secp, buffer, len); + else + return nvme_sec_recv(ctrl, 0, spsp, secp, buffer, len); +} EXPORT_SYMBOL_GPL(nvme_sec_submit); #endif /* CONFIG_BLK_SED_OPAL */ @@ -2468,7 +2499,10 @@ int nvme_init_identify(struct nvme_ctrl *ctrl) ctrl->hmmaxd = le16_to_cpu(id->hmmaxd); } + ctrl->rpmbs = le32_to_cpu(id->rpmbs); + ret = nvme_mpath_init(ctrl, id); + kfree(id); if (ret < 0) diff --git a/drivers/nvme/host/nvme.h b/drivers/nvme/host/nvme.h index bb4a2003c097..a596e9e84d1e 100644 --- a/drivers/nvme/host/nvme.h +++ b/drivers/nvme/host/nvme.h @@ -21,6 +21,7 @@ #include #include #include +#include #include #include @@ -167,6 +168,7 @@ struct nvme_ctrl { struct list_head subsys_entry; struct opal_dev *opal_dev; + struct rpmb_dev *rdev; char name[12]; u16 cntlid; @@ -193,6 +195,7 @@ struct nvme_ctrl { u8 apsta; u32 oaes; u32 aen_result; + u32 rpmbs; unsigned int shutdown_timeout; unsigned int kato; bool subsystem; @@ -420,6 +423,12 @@ void nvme_start_ctrl(struct nvme_ctrl *ctrl); void nvme_stop_ctrl(struct nvme_ctrl *ctrl); void nvme_put_ctrl(struct nvme_ctrl *ctrl); int nvme_init_identify(struct nvme_ctrl *ctrl); +int nvme_sec_send(struct nvme_ctrl *ctrl, u8 nssf, u16 spsp, u8 secp, + void *buffer, size_t len); +int nvme_sec_recv(struct nvme_ctrl *ctrl, u8 nssf, u16 spsp, u8 secp, + void *buffer, size_t len); +int nvme_init_rpmb(struct nvme_ctrl *ctrl); +void nvme_exit_rpmb(struct nvme_ctrl *ctrl); void nvme_remove_namespaces(struct nvme_ctrl *ctrl); diff --git a/drivers/nvme/host/pci.c b/drivers/nvme/host/pci.c index d668682f91df..6c19fa7525e3 100644 --- a/drivers/nvme/host/pci.c +++ b/drivers/nvme/host/pci.c @@ -2289,6 +2289,10 @@ static void nvme_reset_work(struct work_struct *work) if (result) goto out; + result = nvme_init_rpmb(&dev->ctrl); + if (result < 0) + goto out; + if (dev->ctrl.oacs & NVME_CTRL_OACS_SEC_SUPP) { if (!dev->ctrl.opal_dev) dev->ctrl.opal_dev = diff --git a/drivers/nvme/host/rpmb.c b/drivers/nvme/host/rpmb.c new file mode 100644 index 000000000000..34e807bfc4f9 --- /dev/null +++ b/drivers/nvme/host/rpmb.c @@ -0,0 +1,119 @@ +// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0 +/* + * Copyright(c) 2018 Intel Corporation. All rights reserved. + */ +#include +#include "nvme.h" +#define NVME_SECP_RPMB 0xEA /* Security Protocol EAh is assigned + * for NVMe use (refer to ACS-4) + */ +#define NVME_SPSP_RPMB 0x0001 /* RPMB Target */ +static int nvme_rpmb_cmd_seq(struct device *dev, u8 target, + struct rpmb_cmd *cmds, u32 ncmds) +{ + struct nvme_ctrl *ctrl; + struct rpmb_cmd *cmd; + u32 size; + int ret; + int i; + + ctrl = dev_get_drvdata(dev); + + for (ret = 0, i = 0; i < ncmds && !ret; i++) { + cmd = &cmds[i]; + size = rpmb_ioc_frames_len_nvme(cmd->nframes); + if (cmd->flags & RPMB_F_WRITE) + ret = nvme_sec_send(ctrl, target, + NVME_SPSP_RPMB, NVME_SECP_RPMB, + cmd->frames, size); + else + ret = nvme_sec_recv(ctrl, target, + NVME_SPSP_RPMB, NVME_SECP_RPMB, + cmd->frames, size); + } + + return ret; +} + +static int nvme_rpmb_get_capacity(struct device *dev, u8 target) +{ + struct nvme_ctrl *ctrl; + + ctrl = dev_get_drvdata(dev); + + return ((ctrl->rpmbs >> 16) & 0xFF) + 1; +} + +static struct rpmb_ops nvme_rpmb_dev_ops = { + .cmd_seq = nvme_rpmb_cmd_seq, + .get_capacity = nvme_rpmb_get_capacity, + .type = RPMB_TYPE_NVME, +}; + +static void nvme_rpmb_set_cap(struct nvme_ctrl *ctrl, + struct rpmb_ops *ops) +{ + ops->wr_cnt_max = ((ctrl->rpmbs >> 24) & 0xFF) + 1; + ops->rd_cnt_max = ops->wr_cnt_max; + ops->block_size = 2; /* 1 sector == 2 half sectors */ + ops->auth_method = (ctrl->rpmbs >> 3) & 0x3; +} + +static void nvme_rpmb_add(struct nvme_ctrl *ctrl) +{ + struct rpmb_dev *rdev; + int ndevs = ctrl->rpmbs & 0x7; + int i; + + nvme_rpmb_set_cap(ctrl, &nvme_rpmb_dev_ops); + + /* Add RPMB partitions */ + for (i = 0; i < ndevs; i++) { + rdev = rpmb_dev_register(ctrl->device, i, &nvme_rpmb_dev_ops); + if (IS_ERR(rdev)) { + dev_warn(ctrl->device, "%s: cannot register to rpmb %ld\n", + dev_name(ctrl->device), PTR_ERR(rdev)); + } + dev_set_drvdata(&rdev->dev, ctrl); + } +} + +static void nvme_rpmb_remove(struct nvme_ctrl *ctrl) +{ + int ndevs = ctrl->rpmbs & 0x7; + int i; + + /* FIXME: target */ + for (i = 0; i < ndevs; i++) + rpmb_dev_unregister_by_device(ctrl->device, i); +} + +int nvme_init_rpmb(struct nvme_ctrl *ctrl) +{ + dev_err(ctrl->device, "RPMBS %X\n", ctrl->rpmbs); + + if ((ctrl->rpmbs & 0x7) == 0x0) { + dev_err(ctrl->device, "RPMBS No partitions\n"); + return 0; + } + + dev_err(ctrl->device, "RPMBS Number of partitions %d\n", + ctrl->rpmbs & 0x7); + dev_err(ctrl->device, "RPMBS Authentication Method: %d\n", + (ctrl->rpmbs >> 3) & 0x3); + dev_err(ctrl->device, "RPMBS Total Size: %d %dK", + (ctrl->rpmbs >> 16) & 0xFF, + (((ctrl->rpmbs >> 16) & 0xFF) + 1) * 128); + dev_err(ctrl->device, "RPMBS Access Size: %d %dB", + (ctrl->rpmbs >> 24) & 0xFF, + (((ctrl->rpmbs >> 24) & 0xFF) + 1) * 512); + + nvme_rpmb_add(ctrl); + + return 0; +} + +void nvme_exit_rpmb(struct nvme_ctrl *ctrl) +{ + nvme_rpmb_remove(ctrl); +} From 4427ed7c84ed0673b83ceb7a60df17009459d012 Mon Sep 17 00:00:00 2001 From: Tomas Winkler Date: Thu, 24 May 2018 14:57:02 +0300 Subject: [PATCH 1072/1103] rpmb: VRPMB-FE create virtio rpmb frontend driver This patch implements virtio rpmb frontend driver. The driver will work with RPMB VBS-U together to provide one communication channel between UOS and SOS. V2: 1. Change license to dual BSD/GPL 2. Fix coding style. 3. Use pr_fmt macro instead of ERR, DBG, ... V3: 1. Replace - with _ in file name. 2. Plug to rpmb framework instead of using own misc device 3. Use arrays of scatter lists instead of linearizing the data. V4: 1. Allocate memory for control structures, it's not possible to DMA from the stack. V5: 1. Add mutex and use wait queue instead of completion. 2. WIP code for getting capabilities V6: 1. Fix calculation of the allocation size for seq cmd 2. WIP code for getting capabilities 3. Drop unused constant RPMB_MAX_FRAMES Change-Id: I88a42f2e8f2ea1573aad9b5cafeae812c669a73e Signed-off-by: Tomas Winkler --- drivers/char/rpmb/Kconfig | 10 ++ drivers/char/rpmb/Makefile | 1 + drivers/char/rpmb/virtio_rpmb.c | 305 ++++++++++++++++++++++++++++++++ 3 files changed, 316 insertions(+) create mode 100644 drivers/char/rpmb/virtio_rpmb.c diff --git a/drivers/char/rpmb/Kconfig b/drivers/char/rpmb/Kconfig index c069664eec92..48f11c19bbda 100644 --- a/drivers/char/rpmb/Kconfig +++ b/drivers/char/rpmb/Kconfig @@ -30,3 +30,13 @@ config RPMB_SIM suitable only for testing of the RPMB subsystem or RPMB applications prior to RPMB key provisioning. Most people should say N here. + +config VIRTIO_RPMB + tristate "Virtio RPMB character device interface /dev/vrpmb" + default n + depends on VIRTIO + select RPMB + help + Say yes here if you want to access virtio RPMB from user space + via character device interface /dev/vrpmb. + This device interface is only for guest/frontend virtio driver. diff --git a/drivers/char/rpmb/Makefile b/drivers/char/rpmb/Makefile index 8bd1186948b0..281c012712ca 100644 --- a/drivers/char/rpmb/Makefile +++ b/drivers/char/rpmb/Makefile @@ -3,5 +3,6 @@ obj-$(CONFIG_RPMB) += rpmb.o rpmb-objs += core.o rpmb-$(CONFIG_RPMB_INTF_DEV) += cdev.o obj-$(CONFIG_RPMB_SIM) += rpmb_sim.o +obj-$(CONFIG_VIRTIO_RPMB) += virtio_rpmb.o ccflags-y += -D__CHECK_ENDIAN__ diff --git a/drivers/char/rpmb/virtio_rpmb.c b/drivers/char/rpmb/virtio_rpmb.c new file mode 100644 index 000000000000..ef3487c98805 --- /dev/null +++ b/drivers/char/rpmb/virtio_rpmb.c @@ -0,0 +1,305 @@ +// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0 +/* + * Virtio RPMB Front End Driver + * + * Copyright (c) 2018 Intel Corporation. All rights reserved. + */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static const char id[] = "RPMB:VIRTIO"; +#ifndef VIRTIO_ID_RPMB +#define VIRTIO_ID_RPMB 0xFFFF +#endif + +#define RPMB_SEQ_CMD_MAX 3 /* support up to 3 cmds */ + +struct virtio_rpmb_info { + struct virtqueue *vq; + struct mutex lock; /* info lock */ + wait_queue_head_t have_data; + struct rpmb_dev *rdev; +}; + +struct virtio_rpmb_ioc { + unsigned int ioc_cmd; + int result; + u8 target; + u8 reserved[3]; +}; + +static void virtio_rpmb_recv_done(struct virtqueue *vq) +{ + struct virtio_rpmb_info *vi; + struct virtio_device *vdev = vq->vdev; + + vi = vq->vdev->priv; + if (!vi) { + dev_err(&vdev->dev, "Error: no found vi data.\n"); + return; + } + + wake_up(&vi->have_data); +} + +static int rpmb_virtio_cmd_seq(struct device *dev, u8 target, + struct rpmb_cmd *cmds, u32 ncmds) +{ + struct virtio_device *vdev = dev_to_virtio(dev); + struct virtio_rpmb_info *vi = vdev->priv; + unsigned int i; + struct virtio_rpmb_ioc *vio_cmd; + struct rpmb_ioc_seq_cmd *seq_cmd; + size_t seq_cmd_sz; + struct scatterlist vio_ioc, vio_seq, frame[3]; + struct scatterlist *sgs[5]; + unsigned int num_out = 0, num_in = 0; + size_t sz; + int ret; + unsigned int len; + + if (ncmds > RPMB_SEQ_CMD_MAX) + return -EINVAL; + + mutex_lock(&vi->lock); + + vio_cmd = kzalloc(sizeof(*vio_cmd), GFP_KERNEL); + seq_cmd_sz = sizeof(*seq_cmd) + sizeof(struct rpmb_ioc_cmd) * ncmds; + seq_cmd = kzalloc(seq_cmd_sz, GFP_KERNEL); + if (!vio_cmd || !seq_cmd) { + ret = -ENOMEM; + goto out; + } + + vio_cmd->ioc_cmd = RPMB_IOC_SEQ_CMD; + vio_cmd->result = 0; + vio_cmd->target = target; + sg_init_one(&vio_ioc, vio_cmd, sizeof(*vio_cmd)); + sgs[num_out + num_in++] = &vio_ioc; + + seq_cmd->num_of_cmds = ncmds; + for (i = 0; i < ncmds; i++) { + seq_cmd->cmds[i].flags = cmds[i].flags; + seq_cmd->cmds[i].nframes = cmds[i].nframes; + seq_cmd->cmds[i].frames_ptr = i; + } + sg_init_one(&vio_seq, seq_cmd, seq_cmd_sz); + sgs[num_out + num_in++] = &vio_seq; + + for (i = 0; i < ncmds; i++) { + sz = sizeof(struct rpmb_frame_jdec) * (cmds[i].nframes ?: 1); + sg_init_one(&frame[i], cmds[i].frames, sz); + sgs[num_out + num_in++] = &frame[i]; + } + + virtqueue_add_sgs(vi->vq, sgs, num_out, num_in, vi, GFP_KERNEL); + virtqueue_kick(vi->vq); + + wait_event(vi->have_data, virtqueue_get_buf(vi->vq, &len)); + + ret = 0; + + if (vio_cmd->result != 0) { + dev_err(dev, "Error: command error = %d.\n", vio_cmd->result); + ret = -EIO; + } + +out: + kfree(vio_cmd); + kfree(seq_cmd); + mutex_unlock(&vi->lock); + return ret; +} + +static int rpmb_virtio_cmd_cap(struct device *dev, u8 target) +{ + struct virtio_device *vdev = dev_to_virtio(dev); + struct virtio_rpmb_info *vi = vdev->priv; + struct virtio_rpmb_ioc *vio_cmd; + struct rpmb_ioc_cap_cmd *cap_cmd; + struct scatterlist vio_ioc, cap_ioc; + struct scatterlist *sgs[2]; + unsigned int num_out = 0, num_in = 0; + unsigned int len; + int ret; + + mutex_lock(&vi->lock); + + vio_cmd = kzalloc(sizeof(*vio_cmd), GFP_KERNEL); + cap_cmd = kzalloc(sizeof(*cap_cmd), GFP_KERNEL); + if (!vio_cmd || !cap_cmd) { + ret = -ENOMEM; + goto out; + } + + vio_cmd->ioc_cmd = RPMB_IOC_CAP_CMD; + vio_cmd->result = 0; + vio_cmd->target = target; + sg_init_one(&vio_ioc, vio_cmd, sizeof(*vio_cmd)); + sgs[num_out + num_in++] = &vio_ioc; + + sg_init_one(&cap_ioc, cap_cmd, sizeof(*cap_cmd)); + sgs[num_out + num_in++] = &cap_ioc; + + virtqueue_add_sgs(vi->vq, sgs, num_out, num_in, vi, GFP_KERNEL); + virtqueue_kick(vi->vq); + + wait_event(vi->have_data, virtqueue_get_buf(vi->vq, &len)); + + ret = 0; + + if (vio_cmd->result != 0) { + dev_err(dev, "Error: command error = %d.\n", vio_cmd->result); + ret = -EIO; + } + +out: + kfree(vio_cmd); + kfree(cap_cmd); + + mutex_unlock(&vi->lock); + return ret; +} + +static int rpmb_virtio_get_capacity(struct device *dev, u8 target) +{ + return 0; +} + +static struct rpmb_ops rpmb_virtio_ops = { + .cmd_seq = rpmb_virtio_cmd_seq, + .get_capacity = rpmb_virtio_get_capacity, + .type = RPMB_TYPE_EMMC, +}; + +static int rpmb_virtio_dev_init(struct virtio_rpmb_info *vi) +{ + int ret = 0; + struct device *dev = &vi->vq->vdev->dev; + + rpmb_virtio_ops.dev_id_len = strlen(id); + rpmb_virtio_ops.dev_id = id; + rpmb_virtio_ops.wr_cnt_max = 1; + rpmb_virtio_ops.rd_cnt_max = 1; + rpmb_virtio_ops.block_size = 1; + + vi->rdev = rpmb_dev_register(dev, 0, &rpmb_virtio_ops); + if (IS_ERR(vi->rdev)) { + ret = PTR_ERR(vi->rdev); + goto err; + } + + dev_set_drvdata(dev, vi); +err: + return ret; +} + +static int virtio_rpmb_init(struct virtio_device *vdev) +{ + int ret; + struct virtio_rpmb_info *vi; + + vi = kzalloc(sizeof(*vi), GFP_KERNEL); + if (!vi) + return -ENOMEM; + + init_waitqueue_head(&vi->have_data); + mutex_init(&vi->lock); + vdev->priv = vi; + + /* We expect a single virtqueue. */ + vi->vq = virtio_find_single_vq(vdev, virtio_rpmb_recv_done, "request"); + if (IS_ERR(vi->vq)) { + dev_err(&vdev->dev, "get single vq failed!\n"); + ret = PTR_ERR(vi->vq); + goto err; + } + + /* create vrpmb device. */ + ret = rpmb_virtio_dev_init(vi); + if (ret) { + dev_err(&vdev->dev, "create vrpmb device failed.\n"); + goto err; + } + + dev_info(&vdev->dev, "init done!\n"); + + return 0; + +err: + kfree(vi); + return ret; +} + +static void virtio_rpmb_remove(struct virtio_device *vdev) +{ + struct virtio_rpmb_info *vi; + + vi = vdev->priv; + if (!vi) + return; + + if (wq_has_sleeper(&vi->have_data)) + wake_up(&vi->have_data); + + rpmb_dev_unregister(vi->rdev); + + if (vdev->config->reset) + vdev->config->reset(vdev); + + if (vdev->config->del_vqs) + vdev->config->del_vqs(vdev); + + kfree(vi); +} + +static int virtio_rpmb_probe(struct virtio_device *vdev) +{ + return virtio_rpmb_init(vdev); +} + +#ifdef CONFIG_PM_SLEEP +static int virtio_rpmb_freeze(struct virtio_device *vdev) +{ + virtio_rpmb_remove(vdev); + return 0; +} + +static int virtio_rpmb_restore(struct virtio_device *vdev) +{ + return virtio_rpmb_init(vdev); +} +#endif + +static struct virtio_device_id id_table[] = { + { VIRTIO_ID_RPMB, VIRTIO_DEV_ANY_ID }, + { 0 }, +}; + +static struct virtio_driver virtio_rpmb_driver = { + .driver.name = KBUILD_MODNAME, + .driver.owner = THIS_MODULE, + .id_table = id_table, + .probe = virtio_rpmb_probe, + .remove = virtio_rpmb_remove, +#ifdef CONFIG_PM_SLEEP + .freeze = virtio_rpmb_freeze, + .restore = virtio_rpmb_restore, +#endif +}; + +module_virtio_driver(virtio_rpmb_driver); +MODULE_DEVICE_TABLE(virtio, id_table); + +MODULE_DESCRIPTION("Virtio rpmb frontend driver"); +MODULE_AUTHOR("Intel Corporation"); +MODULE_LICENSE("Dual BSD/GPL"); From 2be46b4f938a096d2f1da768e9ea0d40f38f5b6c Mon Sep 17 00:00:00 2001 From: Tomas Winkler Date: Tue, 19 Jul 2016 00:08:05 +0300 Subject: [PATCH 1073/1103] char: rpmb: Document Replay Protected Memory Block (RPMB) subsystem Add rpmb documentatin in sphinx format. V7: new in the series V8: Rebase for v4.10 fix conf.py V9: 1. Rebase for v4.17 2. Add SPDX intentifiers. 3. Move under driver-api 4. Drop req_cmd() Change-Id: I4ec3481a8cf443ea6f5fb88a11b616d815163e8c Signed-off-by: Tomas Winkler Signed-off-by: Alexander Usyskin --- Documentation/conf.py | 2 + Documentation/driver-api/index.rst | 1 + Documentation/driver-api/rpmb/conf.py | 5 + Documentation/driver-api/rpmb/index.rst | 18 ++++ .../driver-api/rpmb/introduction.rst | 98 +++++++++++++++++++ Documentation/driver-api/rpmb/rpmb-tool.rst | 19 ++++ .../driver-api/rpmb/simulation-device.rst | 21 ++++ MAINTAINERS | 1 + 8 files changed, 165 insertions(+) create mode 100644 Documentation/driver-api/rpmb/conf.py create mode 100644 Documentation/driver-api/rpmb/index.rst create mode 100644 Documentation/driver-api/rpmb/introduction.rst create mode 100644 Documentation/driver-api/rpmb/rpmb-tool.rst create mode 100644 Documentation/driver-api/rpmb/simulation-device.rst diff --git a/Documentation/conf.py b/Documentation/conf.py index b691af4831fa..a57272e7820c 100644 --- a/Documentation/conf.py +++ b/Documentation/conf.py @@ -403,6 +403,8 @@ 'The kernel development community', 'manual'), ('userspace-api/index', 'userspace-api.tex', 'The Linux kernel user-space API guide', 'The kernel development community', 'manual'), + ('rpmb/index', 'rpmb.tex', 'Linux RPMB Subsystem Documentation', + 'The kernel development community', 'manual'), ] # The name of an image file (relative to this directory) to place at the top of diff --git a/Documentation/driver-api/index.rst b/Documentation/driver-api/index.rst index 6d9f2f9fe20e..d602f6c05972 100644 --- a/Documentation/driver-api/index.rst +++ b/Documentation/driver-api/index.rst @@ -53,6 +53,7 @@ available subsections can be seen below. slimbus soundwire/index fpga/index + rpmb/index .. only:: subproject and html diff --git a/Documentation/driver-api/rpmb/conf.py b/Documentation/driver-api/rpmb/conf.py new file mode 100644 index 000000000000..15430a0b3a08 --- /dev/null +++ b/Documentation/driver-api/rpmb/conf.py @@ -0,0 +1,5 @@ +# -*- coding: utf-8; mode: python -*- + +project = "Linux RPMB Subsystem" + +tags.add("subproject") diff --git a/Documentation/driver-api/rpmb/index.rst b/Documentation/driver-api/rpmb/index.rst new file mode 100644 index 000000000000..3813a44ad06e --- /dev/null +++ b/Documentation/driver-api/rpmb/index.rst @@ -0,0 +1,18 @@ +.. SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause + +============================================== +Replay Protected Memory Block (RPMB) subsystem +============================================== + +.. toctree:: + + introduction + simulation-device.rst + rpmb-tool.rst + +.. only:: subproject + + Indices + ======= + + * :ref:`genindex` diff --git a/Documentation/driver-api/rpmb/introduction.rst b/Documentation/driver-api/rpmb/introduction.rst new file mode 100644 index 000000000000..403cbcf6e142 --- /dev/null +++ b/Documentation/driver-api/rpmb/introduction.rst @@ -0,0 +1,98 @@ +.. SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause + +============= +Introduction: +============= + +Few storage technologies such is EMMC, UFS, and NVMe support RPMB +hardware partition with common protocol and frame layout. +The RPMB partition `cannot` be accessed via standard block layer, +but by a set of specific commands: + +WRITE, READ, GET_WRITE_COUNTER, and PROGRAM_KEY. + +The commands and the data are embedded within :c:type:`rpmb_frame `. + +An RPMB partition provides authenticated and replay protected access, +hence it is suitable as a secure storage. + +In-kernel API +------------- +The RPMB layer aims to provide in-kernel API for Trusted Execution +Environment (TEE) devices that are capable to securely compute the block +frame signature. In case a TEE device wish to store a replay protected +data, it creates an RPMB frame with requested data and computes HMAC of +the frame, then it requests the storage device via RPMB layer to store +the data. + +The layer provides APIs, for :c:func:`rpmb_seq_cmd()` for issuing sequence +of raw RPMB protocol frames, which is close to the functionality provided +by emmc multi ioctl interface. + +.. c:function:: int rpmb_cmd_seq(struct rpmb_dev *rdev, u8 target, struct rpmb_cmd *cmds, u32 ncmds); + + +A TEE driver can claim the RPMB interface, for example, via +:c:func:`class_interface_register`: + +.. code-block:: c + + struct class_interface tee_rpmb_intf = { + .class = &rpmb_class; + .add_dev = rpmb_add_device; + .remove_dev = rpmb_remove_device; + } + class_interface_register(&tee_rpmb_intf); + + +RPMB device registeration +---------------------------- + +A storage device registers its RPMB hardware (eMMC) partition or RPMB +W-LUN (UFS) with the RPMB layer :c:func:`rpmb_dev_register` providing +an implementation for :c:func:`rpmb_seq_cmd()` handler. The interface +enables sending sequence of RPMB standard frames. + +.. code-block:: c + + struct rpmb_ops mmc_rpmb_dev_ops = { + .cmd_seq = mmc_blk_rpmb_cmd_seq, + .type = RPMB_TYPE_EMMC, + ... + } + rpmb_dev_register(disk_to_dev(part_md->disk), &mmc_rpmb_dev_ops); + + +User space API +-------------- + +A parallel user space API is provided via /dev/rpmbX character +device with two IOCTL commands. +- First ``RPMB_IOC_VER_CMD``, return driver protocol version, +- second ``RPMB_IOC_CAP_CMD`` return capability structure, +- last ``RPMB_IOC_SEQ_CMD`` where the whole RPMB sequence, and + including ``RESULT_READ`` is supplied by the caller. +https://android.googlesource.com/trusty/app/storage/ + +.. code-block:: c + + struct rpmb_ioc_req_cmd ireq; + int ret; + + ireq.req_type = RPMB_WRITE_DATA; + rpmb_ioc_cmd_set(ireq.icmd, RPMB_F_WRITE, frames_in, cnt_in); + rpmb_ioc_cmd_set(ireq.ocmd, 0, frames_out, cnt_out); + + ret = ioctl(fd, RPMB_IOC_REQ_CMD, &ireq); + + +API +--- +.. kernel-doc:: include/linux/rpmb.h + +.. kernel-doc:: drivers/char/rpmb/core.c + +.. kernel-doc:: include/uapi/linux/rpmb.h + +.. kernel-doc:: drivers/char/rpmb/cdev.c + diff --git a/Documentation/driver-api/rpmb/rpmb-tool.rst b/Documentation/driver-api/rpmb/rpmb-tool.rst new file mode 100644 index 000000000000..3f4eed84542a --- /dev/null +++ b/Documentation/driver-api/rpmb/rpmb-tool.rst @@ -0,0 +1,19 @@ +.. SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause +========== +RPMB Tool +========== + +There is a sample rpmb tool under tools/rpmb/ directory that exercises +the RPMB devices via RPMB character devices interface (/dev/rpmbX) + +.. code-block:: none + + rpmb [-v] [-r|-s] + + rpmb get-info + rpmb program-key + rpmb write-counter [KEY_FILE] + rpmb write-blocks
+ rpmb read-blocks
[KEY_FILE] + + rpmb -v/--verbose: runs in verbose mode diff --git a/Documentation/driver-api/rpmb/simulation-device.rst b/Documentation/driver-api/rpmb/simulation-device.rst new file mode 100644 index 000000000000..21b7bc8bc39d --- /dev/null +++ b/Documentation/driver-api/rpmb/simulation-device.rst @@ -0,0 +1,21 @@ +.. SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause + +====================== +RPMB Simulation Device +====================== + +RPMB partition simulation device is a virtual device that +provides simulation of the RPMB protocol and uses kernel memory +as storage. + +This driver cannot promise any real security, it is suitable for testing +of the RPMB subsystem it self and mostly it was found useful for testing of +RPMB applications prior to RPMB key provisioning/programming as +The RPMB key programming can be performed only once in the life time +of the storage device. + +Implementation: +--------------- + +.. kernel-doc:: drivers/char/rpmb/rpmb_sim.c + diff --git a/MAINTAINERS b/MAINTAINERS index d21898548164..27e31b5339e0 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -12521,6 +12521,7 @@ F: drivers/char/rpmb/* F: include/uapi/linux/rpmb.h F: include/linux/rpmb.h F: Documentation/ABI/testing/sysfs-class-rpmb +F: Documentation/driver-api/rpmb.rst F: tools/rpmb/ RTL2830 MEDIA DRIVER From d14b078f0e1f73ab917bb39df4212b2fdc307252 Mon Sep 17 00:00:00 2001 From: Tomas Winkler Date: Tue, 29 Mar 2016 16:14:01 +0300 Subject: [PATCH 1074/1103] block: export block_class to be used by class interfaces Enable access to block devices via class_interface outside of the block subsystem. Change-Id: I6115a9b4655e47ec42e47c9720da8784139557bd Signed-off-by: Tomas Winkler --- block/genhd.c | 1 + 1 file changed, 1 insertion(+) diff --git a/block/genhd.c b/block/genhd.c index be5bab20b2ab..4c777e1b3bd9 100644 --- a/block/genhd.c +++ b/block/genhd.c @@ -1284,6 +1284,7 @@ static void disk_release(struct device *dev) struct class block_class = { .name = "block", }; +EXPORT_SYMBOL_GPL(block_class); static char *block_devnode(struct device *dev, umode_t *mode, kuid_t *uid, kgid_t *gid) From d08879333330c4b7dd251847cbce406a1db391f7 Mon Sep 17 00:00:00 2001 From: Alexander Usyskin Date: Tue, 23 Dec 2014 17:18:37 +0200 Subject: [PATCH 1075/1103] mei: spd: storage proxy driver Host storage proxy driver enables ME FW to store its data on a storage devices such as eMMC or UFS via host interface on a dedicated partition. This patch implements storage over an eMMC GPP partition and UFS LUN. A GPP partition is required for pre manufacturing operation since RPMB partition key can be written only once. V9: 1. Add SPXD Identifiers Change-Id: I524c237d240d93accc9ce071e92744191f23ce87 Signed-off-by: Alexander Usyskin Signed-off-by: Tomas Winkler --- drivers/misc/mei/Kconfig | 2 + drivers/misc/mei/Makefile | 2 + drivers/misc/mei/spd/Kconfig | 12 + drivers/misc/mei/spd/Makefile | 11 + drivers/misc/mei/spd/cmd.c | 485 +++++++++++++++++++++++++++++++++ drivers/misc/mei/spd/cmd.h | 230 ++++++++++++++++ drivers/misc/mei/spd/debugfs.c | 79 ++++++ drivers/misc/mei/spd/gpp.c | 299 ++++++++++++++++++++ drivers/misc/mei/spd/main.c | 118 ++++++++ drivers/misc/mei/spd/spd.h | 93 +++++++ 10 files changed, 1331 insertions(+) create mode 100644 drivers/misc/mei/spd/Kconfig create mode 100644 drivers/misc/mei/spd/Makefile create mode 100644 drivers/misc/mei/spd/cmd.c create mode 100644 drivers/misc/mei/spd/cmd.h create mode 100644 drivers/misc/mei/spd/debugfs.c create mode 100644 drivers/misc/mei/spd/gpp.c create mode 100644 drivers/misc/mei/spd/main.c create mode 100644 drivers/misc/mei/spd/spd.h diff --git a/drivers/misc/mei/Kconfig b/drivers/misc/mei/Kconfig index c49e1d2269af..2a82520f3e4e 100644 --- a/drivers/misc/mei/Kconfig +++ b/drivers/misc/mei/Kconfig @@ -43,3 +43,5 @@ config INTEL_MEI_TXE Supported SoCs: Intel Bay Trail + +source "drivers/misc/mei/spd/Kconfig" diff --git a/drivers/misc/mei/Makefile b/drivers/misc/mei/Makefile index cd6825afa8e1..f83e2fdc01d3 100644 --- a/drivers/misc/mei/Makefile +++ b/drivers/misc/mei/Makefile @@ -23,3 +23,5 @@ mei-txe-objs += hw-txe.o mei-$(CONFIG_EVENT_TRACING) += mei-trace.o CFLAGS_mei-trace.o = -I$(src) + +obj-$(CONFIG_INTEL_MEI_SPD) += spd/ diff --git a/drivers/misc/mei/spd/Kconfig b/drivers/misc/mei/spd/Kconfig new file mode 100644 index 000000000000..8347fbc9fbe0 --- /dev/null +++ b/drivers/misc/mei/spd/Kconfig @@ -0,0 +1,12 @@ +# +# Storage proxy device configuration +# +config INTEL_MEI_SPD + tristate "Intel MEI Host Storage Proxy Driver" + depends on INTEL_MEI && BLOCK + help + A driver for the host storage proxy ME client + The driver enables ME FW to store data on a storage devices + that are accessible only from the host. + + To compile this driver as a module, choose M here. diff --git a/drivers/misc/mei/spd/Makefile b/drivers/misc/mei/spd/Makefile new file mode 100644 index 000000000000..8e8aba94b9ef --- /dev/null +++ b/drivers/misc/mei/spd/Makefile @@ -0,0 +1,11 @@ +# +# Makefile for the Storage Proxy device driver. +# + +obj-$(CONFIG_INTEL_MEI_SPD) += mei_spd.o +mei_spd-objs := main.o +mei_spd-objs += cmd.o +mei_spd-objs += gpp.o +mei_spd-$(CONFIG_DEBUG_FS) += debugfs.o + +ccflags-y += -D__CHECK_ENDIAN__ diff --git a/drivers/misc/mei/spd/cmd.c b/drivers/misc/mei/spd/cmd.c new file mode 100644 index 000000000000..25d682d7eb09 --- /dev/null +++ b/drivers/misc/mei/spd/cmd.c @@ -0,0 +1,485 @@ +// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0 +/* + * Copyright(c) 2015 - 2018 Intel Corporation. All rights reserved. + */ +#include +#include +#include + +#include "cmd.h" +#include "spd.h" + +#define spd_cmd_size(_cmd) \ + (sizeof(struct spd_cmd_hdr) + \ + sizeof(struct spd_cmd_##_cmd)) +#define to_spd_hdr(_buf) (struct spd_cmd_hdr *)(_buf) +#define to_spd_cmd(_cmd, _buf) \ + (struct spd_cmd_##_cmd *)((_buf) + sizeof(struct spd_cmd_hdr)) + +const char *spd_cmd_str(enum spd_cmd_type cmd) +{ +#define __SPD_CMD(_cmd) SPD_##_cmd##_CMD +#define SPD_CMD(cmd) case __SPD_CMD(cmd): return #cmd + switch (cmd) { + SPD_CMD(NONE); + SPD_CMD(START_STOP); + SPD_CMD(RPMB_WRITE); + SPD_CMD(RPMB_READ); + SPD_CMD(RPMB_GET_COUNTER); + SPD_CMD(GPP_WRITE); + SPD_CMD(GPP_READ); + SPD_CMD(TRIM); + SPD_CMD(INIT); + SPD_CMD(STORAGE_STATUS); + SPD_CMD(MAX); + default: + return "unknown"; + } +#undef SPD_CMD +#undef __SPD_CMD +} + +const char *mei_spd_dev_str(enum spd_storage_type type) +{ +#define SPD_TYPE(type) case SPD_TYPE_##type: return #type + switch (type) { + SPD_TYPE(UNDEF); + SPD_TYPE(EMMC); + SPD_TYPE(UFS); + default: + return "unknown"; + } +#undef SPD_TYPE +} + +const char *mei_spd_state_str(enum mei_spd_state state) +{ +#define SPD_STATE(state) case MEI_SPD_STATE_##state: return #state + switch (state) { + SPD_STATE(INIT); + SPD_STATE(INIT_WAIT); + SPD_STATE(INIT_DONE); + SPD_STATE(RUNNING); + SPD_STATE(STOPPING); + default: + return "unknown"; + } +#undef SPD_STATE +} + +/** + * mei_spd_init_req - send init request + * + * @spd: spd device + * + * Return: 0 on success + * -EPROTO if called in wrong state + * < 0 on write error + */ +int mei_spd_cmd_init_req(struct mei_spd *spd) +{ + const int req_len = sizeof(struct spd_cmd_hdr); + struct spd_cmd_hdr *hdr; + u32 cmd_type = SPD_INIT_CMD; + ssize_t ret; + + spd_dbg(spd, "cmd [%d] %s : state [%d] %s\n", + cmd_type, spd_cmd_str(cmd_type), + spd->state, mei_spd_state_str(spd->state)); + + if (spd->state != MEI_SPD_STATE_INIT) + return -EPROTO; + + memset(spd->buf, 0, req_len); + hdr = to_spd_hdr(spd->buf); + + hdr->command_type = cmd_type; + hdr->is_response = 0; + hdr->len = req_len; + + spd->state = MEI_SPD_STATE_INIT_WAIT; + ret = mei_cldev_send(spd->cldev, spd->buf, req_len); + if (ret != req_len) { + spd_err(spd, "start send failed ret = %zd\n", ret); + return ret; + } + + return 0; +} + +/** + * mei_spd_cmd_init_rsp - handle init response message + * + * @spd: spd device + * @cmd: received spd command + * @cmd_sz: received command size + * + * Return: 0 on success; < 0 otherwise + */ +static int mei_spd_cmd_init_rsp(struct mei_spd *spd, struct spd_cmd *cmd, + ssize_t cmd_sz) +{ + int type; + int gpp_id; + int i; + + if (cmd_sz < spd_cmd_size(init_resp)) { + spd_err(spd, "Wrong init response size\n"); + return -EINVAL; + } + + if (spd->state != MEI_SPD_STATE_INIT_WAIT) + return -EPROTO; + + type = cmd->init_rsp.type; + gpp_id = cmd->init_rsp.gpp_partition_id; + + switch (type) { + case SPD_TYPE_EMMC: + if (gpp_id < 1 || gpp_id > 4) { + spd_err(spd, "%s unsupported gpp id %d\n", + mei_spd_dev_str(type), gpp_id); + return -EINVAL; + } + break; + + case SPD_TYPE_UFS: + if (gpp_id < 1 || gpp_id > 6) { + spd_err(spd, "%s unsupported gpp id %d\n", + mei_spd_dev_str(type), gpp_id); + return -EINVAL; + } + break; + + default: + spd_err(spd, "unsupported storage type %d\n", + cmd->init_rsp.type); + return -EINVAL; + } + + spd->dev_type = type; + spd->gpp_partition_id = gpp_id; + + if (cmd->init_rsp.serial_no_sz != 0) { + if (cmd->init_rsp.serial_no_sz != + cmd_sz - spd_cmd_size(init_resp)) { + spd_err(spd, "wrong serial no size %u?=%zu\n", + cmd->init_rsp.serial_no_sz, + cmd_sz - spd_cmd_size(init_resp)); + return -EMSGSIZE; + } + + if (cmd->init_rsp.serial_no_sz > 256) { + spd_err(spd, "serial no is too large %u\n", + cmd->init_rsp.serial_no_sz); + return -EMSGSIZE; + } + + spd->dev_id = kzalloc(cmd->init_rsp.serial_no_sz, GFP_KERNEL); + if (!spd->dev_id) + return -ENOMEM; + + spd->dev_id_sz = cmd->init_rsp.serial_no_sz; + if (type == SPD_TYPE_EMMC) { + /* FW have this in be32 format */ + __be32 *sno = (__be32 *)cmd->init_rsp.serial_no; + u32 *dev_id = (u32 *)spd->dev_id; + + for (i = 0; i < spd->dev_id_sz / sizeof(u32); i++) + dev_id[i] = be32_to_cpu(sno[i]); + } else { + memcpy(spd->dev_id, &cmd->init_rsp.serial_no, + cmd->init_rsp.serial_no_sz); + } + } + + spd->state = MEI_SPD_STATE_INIT_DONE; + + return 0; +} + +/** + * mei_spd_cmd_storage_status_req - send storage status message + * + * @spd: spd device + * + * Return: 0 on success + * -EPROTO if called in wrong state + * < 0 on write error + */ +int mei_spd_cmd_storage_status_req(struct mei_spd *spd) +{ + struct spd_cmd_hdr *hdr; + struct spd_cmd_storage_status_req *req; + const int req_len = spd_cmd_size(storage_status_req); + u32 cmd_type = SPD_STORAGE_STATUS_CMD; + ssize_t ret; + + spd_dbg(spd, "cmd [%d] %s : state [%d] %s\n", + cmd_type, spd_cmd_str(cmd_type), + spd->state, mei_spd_state_str(spd->state)); + + if (spd->state < MEI_SPD_STATE_INIT_DONE) + return -EPROTO; + + memset(spd->buf, 0, req_len); + hdr = to_spd_hdr(spd->buf); + + hdr->command_type = cmd_type; + hdr->is_response = 0; + hdr->len = req_len; + + req = to_spd_cmd(storage_status_req, spd->buf); + req->gpp_on = mei_spd_gpp_is_open(spd); + req->rpmb_on = 0; + + ret = mei_cldev_send(spd->cldev, spd->buf, req_len); + if (ret != req_len) { + spd_err(spd, "send storage status failed ret = %zd\n", ret); + return ret; + } + + if (req->gpp_on || req->rpmb_on) + spd->state = MEI_SPD_STATE_RUNNING; + else + spd->state = MEI_SPD_STATE_INIT_DONE; + + spd_dbg(spd, "cmd [%d] %s : state [%d] %s\n", + cmd_type, spd_cmd_str(cmd_type), + spd->state, mei_spd_state_str(spd->state)); + + return 0; +} + +static int mei_spd_cmd_gpp_write(struct mei_spd *spd, struct spd_cmd *cmd, + ssize_t out_buf_sz) +{ + size_t len = SPD_GPP_WRITE_DATA_LEN(*cmd); + int ret; + + if (out_buf_sz < spd_cmd_size(gpp_write_req)) { + spd_err(spd, "Wrong request size\n"); + return SPD_STATUS_INVALID_COMMAND; + } + + ret = mei_spd_gpp_write(spd, cmd->gpp_write_req.offset, + cmd->gpp_write_req.data, len); + if (ret) { + spd_err(spd, "Failed to write to gpp ret = %d\n", ret); + return SPD_STATUS_GENERAL_FAILURE; + } + + spd_dbg(spd, "wrote %zd bytes of data\n", len); + + cmd->header.len = spd_cmd_size(gpp_write_rsp); + + return SPD_STATUS_SUCCESS; +} + +static int mei_spd_cmd_gpp_read(struct mei_spd *spd, struct spd_cmd *cmd, + ssize_t out_buf_sz) +{ + size_t len; + int ret; + + if (out_buf_sz < spd_cmd_size(gpp_read_req)) { + spd_err(spd, "Wrong request size\n"); + return SPD_STATUS_INVALID_COMMAND; + } + + len = cmd->gpp_read_req.size_to_read; + if (len > SPD_CLIENT_GPP_DATA_MAX_SIZE) { + spd_err(spd, "Block is to large to read\n"); + return SPD_STATUS_INVALID_COMMAND; + } + + ret = mei_spd_gpp_read(spd, cmd->gpp_read_req.offset, + cmd->gpp_read_resp.data, len); + + if (ret) { + spd_err(spd, "Failed to read from gpp ret = %d\n", ret); + return SPD_STATUS_GENERAL_FAILURE; + } + + spd_dbg(spd, "read %zd bytes of data\n", len); + + cmd->header.len = spd_cmd_size(gpp_read_rsp) + len; + + return SPD_STATUS_SUCCESS; +} + +static int mei_spd_cmd_response(struct mei_spd *spd, ssize_t out_buf_sz) +{ + struct spd_cmd *cmd = (struct spd_cmd *)spd->buf; + u32 spd_cmd; + int ret; + + spd_cmd = cmd->header.command_type; + + spd_dbg(spd, "rsp [%d] %s : state [%d] %s\n", + spd_cmd, spd_cmd_str(spd_cmd), + spd->state, mei_spd_state_str(spd->state)); + + switch (spd_cmd) { + case SPD_INIT_CMD: + ret = mei_spd_cmd_init_rsp(spd, cmd, out_buf_sz); + if (ret) + break; + mutex_unlock(&spd->lock); + mei_spd_gpp_init(spd); + mutex_lock(&spd->lock); + break; + default: + ret = -EINVAL; + spd_err(spd, "Wrong response command %d\n", spd_cmd); + break; + } + + return ret; +} + +/** + * mei_spd_cmd_request - dispatch command requests from the SPD device + * + * @spd: spd device + * @out_buf_sz: buffer size + * + * Return: (TBD) + */ +static int mei_spd_cmd_request(struct mei_spd *spd, ssize_t out_buf_sz) +{ + struct spd_cmd *cmd = (struct spd_cmd *)spd->buf; + ssize_t written; + u32 spd_cmd; + int ret; + + spd_cmd = cmd->header.command_type; + + spd_dbg(spd, "req [%d] %s : state [%d] %s\n", + spd_cmd, spd_cmd_str(spd_cmd), + spd->state, mei_spd_state_str(spd->state)); + + if (spd->state < MEI_SPD_STATE_RUNNING) { + spd_err(spd, "Wrong state %d\n", spd->state); + ret = SPD_STATUS_INVALID_COMMAND; + goto reply; + } + + switch (spd_cmd) { + case SPD_RPMB_WRITE_CMD: + case SPD_RPMB_READ_CMD: + case SPD_RPMB_GET_COUNTER_CMD: + spd_err(spd, "Command %d is not supported\n", spd_cmd); + ret = SPD_STATUS_NOT_SUPPORTED; + break; + case SPD_GPP_WRITE_CMD: + ret = mei_spd_cmd_gpp_write(spd, cmd, out_buf_sz); + break; + case SPD_GPP_READ_CMD: + ret = mei_spd_cmd_gpp_read(spd, cmd, out_buf_sz); + break; + case SPD_TRIM_CMD: + spd_err(spd, "Command %d is not supported\n", spd_cmd); + ret = SPD_STATUS_NOT_SUPPORTED; + break; + default: + spd_err(spd, "Wrong request command %d\n", spd_cmd); + ret = SPD_STATUS_INVALID_COMMAND; + break; + } +reply: + cmd->header.is_response = 1; + cmd->header.status = ret; + if (ret != SPD_STATUS_SUCCESS) + cmd->header.len = sizeof(struct spd_cmd_hdr); + + written = mei_cldev_send(spd->cldev, spd->buf, cmd->header.len); + if (written != cmd->header.len) { + ret = SPD_STATUS_GENERAL_FAILURE; + spd_err(spd, "Failed to send reply written = %zd\n", written); + } + + /* FIXME: translate ret to errno */ + if (ret) + return -EINVAL; + + return 0; +} + +ssize_t mei_spd_cmd(struct mei_spd *spd) +{ + struct spd_cmd *cmd = (struct spd_cmd *)spd->buf; + ssize_t out_buf_sz; + int ret; + + out_buf_sz = mei_cldev_recv(spd->cldev, spd->buf, spd->buf_sz); + if (out_buf_sz < 0) { + spd_err(spd, "failure in receive ret = %zd\n", out_buf_sz); + return out_buf_sz; + } + + if (out_buf_sz == 0) { + spd_err(spd, "received empty msg\n"); + return 0; + } + + /* check that we've received at least sizeof(header) */ + if (out_buf_sz < sizeof(struct spd_cmd_hdr)) { + spd_err(spd, "Request is too short\n"); + return -EFAULT; + } + + if (cmd->header.is_response) + ret = mei_spd_cmd_response(spd, out_buf_sz); + else + ret = mei_spd_cmd_request(spd, out_buf_sz); + + return ret; +} + +static void mei_spd_status_send_work(struct work_struct *work) +{ + struct mei_spd *spd = + container_of(work, struct mei_spd, status_send_w); + + mutex_lock(&spd->lock); + mei_spd_cmd_storage_status_req(spd); + mutex_unlock(&spd->lock); +} + +void mei_spd_free(struct mei_spd *spd) +{ + if (!spd) + return; + + cancel_work_sync(&spd->status_send_w); + + kfree(spd->buf); + kfree(spd); +} + +struct mei_spd *mei_spd_alloc(struct mei_cl_device *cldev) +{ + struct mei_spd *spd; + u8 *buf; + + spd = kzalloc(sizeof(*spd), GFP_KERNEL); + if (!spd) + return NULL; + + spd->buf_sz = sizeof(struct spd_cmd) + SPD_CLIENT_GPP_DATA_MAX_SIZE; + buf = kmalloc(spd->buf_sz, GFP_KERNEL); + if (!buf) + goto free; + + spd->cldev = cldev; + spd->buf = buf; + spd->state = MEI_SPD_STATE_INIT; + mutex_init(&spd->lock); + INIT_WORK(&spd->status_send_w, mei_spd_status_send_work); + + return spd; +free: + kfree(spd); + return NULL; +} diff --git a/drivers/misc/mei/spd/cmd.h b/drivers/misc/mei/spd/cmd.h new file mode 100644 index 000000000000..3f77550f44ab --- /dev/null +++ b/drivers/misc/mei/spd/cmd.h @@ -0,0 +1,230 @@ +/* SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0 */ +/* + * Copyright (C) 2015-2018 Intel Corp. All rights reserved + */ +#ifndef _SPD_CMD_H +#define _SPD_CMD_H + +#include + +/** + * enum spd_cmd_type - available commands + * + * @SPD_NONE_CMD : Lower command sentinel. + * @SPD_START_STOP_CMD : start stop command (deprecated). [Host -> TEE] + * @SPD_RPMB_WRITE_CMD : RPMB write request. [TEE -> Host] + * @SPD_RPMB_READ_CMD : RPMB read request. [TEE -> Host] + * @SPD_RPMB_GET_COUNTER_CMD: get counter request [TEE -> Host] + * @SPD_GPP_WRITE_CMD : GPP write request. [TEE -> Host] + * @SPD_GPP_READ_CMD : GPP read request. [TEE -> Host] + * @SPD_TRIM_CMD : TRIM command [TEE -> Host] + * @SPD_INIT_CMD : initial handshake between host and fw. [Host -> TEE] + * @SPD_STORAGE_STATUS_CMD : the backing storage status. [Host -> TEE] + * @SPD_MAX_CMD: Upper command sentinel. + */ +enum spd_cmd_type { + SPD_NONE_CMD = 0, + SPD_START_STOP_CMD, + SPD_RPMB_WRITE_CMD, + SPD_RPMB_READ_CMD, + SPD_RPMB_GET_COUNTER_CMD, + SPD_GPP_WRITE_CMD, + SPD_GPP_READ_CMD, + SPD_TRIM_CMD, + SPD_INIT_CMD, + SPD_STORAGE_STATUS_CMD, + SPD_MAX_CMD, +}; + +enum spd_status { + SPD_STATUS_SUCCESS = 0, + SPD_STATUS_GENERAL_FAILURE = 1, + SPD_STATUS_NOT_READY = 2, + SPD_STATUS_NOT_SUPPORTED = 3, + SPD_STATUS_INVALID_COMMAND = 4, +}; + +/** + * enum spd_storage_type - storage device type + * + * @SPD_TYPE_UNDEF: lower enum sentinel + * @SPD_TYPE_EMMC: emmc device + * @SPD_TYPE_UFS: ufs device + * @SPD_TYPE_MAX: upper enum sentinel + */ +enum spd_storage_type { + SPD_TYPE_UNDEF = 0, + SPD_TYPE_EMMC = 1, + SPD_TYPE_UFS = 2, + SPD_TYPE_MAX +}; + +/** + * struct spd_cmd_hdr - Host storage Command Header + * + * @command_type: SPD_TYPES + * @is_response: 1 == Response, 0 == Request + * @len: command length + * @status: command status + * @reserved: reserved + */ +struct spd_cmd_hdr { + u32 command_type : 7; + u32 is_response : 1; + u32 len : 13; + u32 status : 8; + u32 reserved : 3; +} __packed; + +/** + * RPMB Frame Size as defined by the JDEC spec + */ +#define SPD_CLIENT_RPMB_DATA_MAX_SIZE (512) + +/** + * struct spd_cmd_init_resp + * commandType == HOST_STORAGE_INIT_CMD + * + * @gpp_partition_id: gpp_partition: + * UFS: LUN Number (0-7) + * EMMC: 1-4. + * 0xff: GPP not supported + * @type: storage hw type + * SPD_TYPE_EMMC + * SPD_TYPE_UFS + * @serial_no_sz: serial_no size + * @serial_no: device serial number + */ +struct spd_cmd_init_resp { + u32 gpp_partition_id; + u32 type; + u32 serial_no_sz; + u8 serial_no[0]; +}; + +/** + * struct spd_cmd_storage_status_req + * commandType == SPD_STORAGE_STATUS_CMD + * + * @gpp_on: availability of the gpp backing storage + * 0 - GP partition is accessible + * 1 - GP partition is not accessible + * @rpmb_on: availability of the backing storage + * 0 - RPMB partition is accessible + * 1 - RPBM partition is not accessible + */ +struct spd_cmd_storage_status_req { + u32 gpp_on; + u32 rpmb_on; +} __packed; + +/** + * struct spd_cmd_rpmb_write + * command_type == SPD_RPMB_WRITE_CMD + * + * @rpmb_frame: RPMB frame are constant size (512) + */ +struct spd_cmd_rpmb_write { + u8 rpmb_frame[0]; +} __packed; + +/** + * struct spd_cmd_rpmb_read + * command_type == SPD_RPMB_READ_CMD + * + * @rpmb_frame: RPMB frame are constant size (512) + */ +struct spd_cmd_rpmb_read { + u8 rpmb_frame[0]; +} __packed; + +/** + * struct spd_cmd_rpmb_get_counter + * command_type == SPD_RPMB_GET_COUNTER_CMD + * + * @rpmb_frame: frame containing frame counter + */ +struct spd_cmd_rpmb_get_counter { + u8 rpmb_frame[0]; +} __packed; + +/** + * struct spd_cmd_gpp_write_req + * command_type == SPD_GPP_WRITE_CMD + * + * @offset: frame offset in partition + * @data: 4K page + */ +struct spd_cmd_gpp_write_req { + u32 offset; + u8 data[0]; +} __packed; + +/** + * struct spd_cmd_gpp_write_rsp + * command_type == SPD_GPP_WRITE_CMD + * + * @reserved: reserved + */ +struct spd_cmd_gpp_write_rsp { + u32 reserved[2]; +} __packed; + +/** + * struct spd_cmd_gpp_read_req + * command_type == SPD_GPP_READ_CMD + * + * @offset: offset of a frame on GPP partition + * @size_to_read: data length to read (must be ) + */ +struct spd_cmd_gpp_read_req { + u32 offset; + u32 size_to_read; +} __packed; + +/** + * struct spd_cmd_gpp_read_rsp + * command_type == SPD_GPP_READ_CMD + * + * @reserved: reserved + * @data: data + */ +struct spd_cmd_gpp_read_rsp { + u32 reserved; + u8 data[0]; +} __packed; + +#define SPD_GPP_READ_DATA_LEN(cmd) ((cmd).header.len - \ + (sizeof(struct spd_cmd_hdr) + \ + sizeof(struct spd_cmd_gpp_read_rsp))) + +#define SPD_GPP_WRITE_DATA_LEN(cmd) ((cmd).header.len - \ + (sizeof(struct spd_cmd_hdr) + \ + sizeof(struct spd_cmd_gpp_write_req))) + +struct spd_cmd { + struct spd_cmd_hdr header; + + union { + struct spd_cmd_rpmb_write rpmb_write; + struct spd_cmd_rpmb_read rpmb_read; + struct spd_cmd_rpmb_get_counter rpmb_get_counter; + + struct spd_cmd_gpp_write_req gpp_write_req; + struct spd_cmd_gpp_write_rsp gpp_write_rsp; + + struct spd_cmd_gpp_read_req gpp_read_req; + struct spd_cmd_gpp_read_rsp gpp_read_resp; + + struct spd_cmd_init_resp init_rsp; + struct spd_cmd_storage_status_req status_req; + }; +} __packed; + +/* GPP Max data 4K */ +#define SPD_CLIENT_GPP_DATA_MAX_SIZE (4096) + +const char *spd_cmd_str(enum spd_cmd_type cmd); +const char *mei_spd_dev_str(enum spd_storage_type type); + +#endif /* _SPD_CMD_H */ diff --git a/drivers/misc/mei/spd/debugfs.c b/drivers/misc/mei/spd/debugfs.c new file mode 100644 index 000000000000..dfbb62a49fcc --- /dev/null +++ b/drivers/misc/mei/spd/debugfs.c @@ -0,0 +1,79 @@ +// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0 +/* + * Copyright(c) 2015 - 2018 Intel Corporation. All rights reserved. + */ +#include +#include +#include +#include + +#include "cmd.h" +#include "spd.h" + +static ssize_t mei_spd_dbgfs_read_info(struct file *fp, char __user *ubuf, + size_t cnt, loff_t *ppos) +{ + struct mei_spd *spd = fp->private_data; + size_t bufsz = 4095; + char *buf; + int pos = 0; + int ret; + + buf = kzalloc(bufsz, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + pos += scnprintf(buf + pos, bufsz - pos, "DEV STATE: [%d] %s\n", + spd->state, mei_spd_state_str(spd->state)); + pos += scnprintf(buf + pos, bufsz - pos, "DEV TYPE : [%d] %s\n", + spd->dev_type, mei_spd_dev_str(spd->dev_type)); + pos += scnprintf(buf + pos, bufsz - pos, " ID SIZE : %d\n", + spd->dev_id_sz); + pos += scnprintf(buf + pos, bufsz - pos, " ID : '%s'\n", "N/A"); + pos += scnprintf(buf + pos, bufsz - pos, "GPP\n"); + pos += scnprintf(buf + pos, bufsz - pos, " id : %d\n", + spd->gpp_partition_id); + pos += scnprintf(buf + pos, bufsz - pos, " opened : %1d\n", + mei_spd_gpp_is_open(spd)); + + ret = simple_read_from_buffer(ubuf, cnt, ppos, buf, pos); + kfree(buf); + return ret; +} + +static const struct file_operations mei_spd_dbgfs_fops_info = { + .open = simple_open, + .read = mei_spd_dbgfs_read_info, + .llseek = generic_file_llseek, +}; + +void mei_spd_dbgfs_deregister(struct mei_spd *spd) +{ + if (!spd->dbgfs_dir) + return; + debugfs_remove_recursive(spd->dbgfs_dir); + spd->dbgfs_dir = NULL; +} + +int mei_spd_dbgfs_register(struct mei_spd *spd, const char *name) +{ + struct dentry *dir, *f; + + dir = debugfs_create_dir(name, NULL); + if (!dir) + return -ENOMEM; + + spd->dbgfs_dir = dir; + + f = debugfs_create_file("info", 0400, dir, + spd, &mei_spd_dbgfs_fops_info); + if (!f) { + spd_err(spd, "info: registration failed\n"); + goto err; + } + + return 0; +err: + mei_spd_dbgfs_deregister(spd); + return -ENODEV; +} diff --git a/drivers/misc/mei/spd/gpp.c b/drivers/misc/mei/spd/gpp.c new file mode 100644 index 000000000000..b5d1a27a50ee --- /dev/null +++ b/drivers/misc/mei/spd/gpp.c @@ -0,0 +1,299 @@ +// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0 +/* + * Copyright(c) 2015 - 2018 Intel Corporation. All rights reserved. + */ +#include +#include +#include +#include +#include +#include + +#include "cmd.h" +#include "spd.h" + +static struct page *page_read(struct address_space *mapping, int index) +{ + return read_mapping_page(mapping, index, NULL); +} + +static int mei_spd_bd_read(struct mei_spd *spd, loff_t from, size_t len, + size_t *retlen, u_char *buf) +{ + struct page *page; + int index = from >> PAGE_SHIFT; + int offset = from & (PAGE_SIZE - 1); + int cpylen; + + while (len) { + if ((offset + len) > PAGE_SIZE) + cpylen = PAGE_SIZE - offset; + else + cpylen = len; + len = len - cpylen; + + page = page_read(spd->gpp->bd_inode->i_mapping, index); + if (IS_ERR(page)) + return PTR_ERR(page); + + memcpy(buf, page_address(page) + offset, cpylen); + put_page(page); + + if (retlen) + *retlen += cpylen; + buf += cpylen; + offset = 0; + index++; + } + return 0; +} + +static int _mei_spd_bd_write(struct block_device *dev, const u_char *buf, + loff_t to, size_t len, size_t *retlen) +{ + struct page *page; + struct address_space *mapping = dev->bd_inode->i_mapping; + int index = to >> PAGE_SHIFT; /* page index */ + int offset = to & ~PAGE_MASK; /* page offset */ + int cpylen; + + while (len) { + if ((offset + len) > PAGE_SIZE) + cpylen = PAGE_SIZE - offset; + else + cpylen = len; + len = len - cpylen; + + page = page_read(mapping, index); + if (IS_ERR(page)) + return PTR_ERR(page); + + if (memcmp(page_address(page) + offset, buf, cpylen)) { + lock_page(page); + memcpy(page_address(page) + offset, buf, cpylen); + set_page_dirty(page); + unlock_page(page); + balance_dirty_pages_ratelimited(mapping); + } + put_page(page); + + if (retlen) + *retlen += cpylen; + + buf += cpylen; + offset = 0; + index++; + } + return 0; +} + +static int mei_spd_bd_write(struct mei_spd *spd, loff_t to, size_t len, + size_t *retlen, const u_char *buf) +{ + int ret; + + ret = _mei_spd_bd_write(spd->gpp, buf, to, len, retlen); + if (ret > 0) + ret = 0; + + sync_blockdev(spd->gpp); + + return ret; +} + +static void mei_spd_bd_sync(struct mei_spd *spd) +{ + sync_blockdev(spd->gpp); +} + +#define GPP_FMODE (FMODE_WRITE | FMODE_READ | FMODE_EXCL) + +bool mei_spd_gpp_is_open(struct mei_spd *spd) +{ + struct request_queue *q; + + if (!spd->gpp) + return false; + + q = spd->gpp->bd_queue; + if (q && !blk_queue_stopped(q)) + return true; + + return false; +} + +static int mei_spd_gpp_open(struct mei_spd *spd, struct device *dev) +{ + int ret; + + if (spd->gpp) + return 0; + + spd->gpp = blkdev_get_by_dev(dev->devt, GPP_FMODE, spd); + if (IS_ERR(spd->gpp)) { + ret = PTR_ERR(spd->gpp); + spd->gpp = NULL; + spd_dbg(spd, "Can't get GPP block device %s ret = %d\n", + dev_name(dev), ret); + return ret; + } + + spd_dbg(spd, "gpp partition created\n"); + return 0; +} + +static int mei_spd_gpp_close(struct mei_spd *spd) +{ + if (!spd->gpp) + return 0; + + mei_spd_bd_sync(spd); + blkdev_put(spd->gpp, GPP_FMODE); + spd->gpp = NULL; + + spd_dbg(spd, "gpp partition removed\n"); + return 0; +} + +#define UFSHCD "ufshcd" +static bool mei_spd_lun_ufs_match(struct mei_spd *spd, struct device *dev) +{ + struct gendisk *disk = dev_to_disk(dev); + struct scsi_device *sdev; + + switch (disk->major) { + case SCSI_DISK0_MAJOR: + case SCSI_DISK1_MAJOR ... SCSI_DISK7_MAJOR: + case SCSI_DISK8_MAJOR ... SCSI_DISK15_MAJOR: + break; + default: + return false; + } + + sdev = to_scsi_device(dev->parent); + + if (!sdev->host || + strncmp(sdev->host->hostt->name, UFSHCD, strlen(UFSHCD))) + return false; + + return sdev->lun == spd->gpp_partition_id; +} + +static bool mei_spd_gpp_mmc_match(struct mei_spd *spd, struct device *dev) +{ + struct gendisk *disk = dev_to_disk(dev); + int idx, part_id; + + if (disk->major != MMC_BLOCK_MAJOR) + return false; + + if (sscanf(disk->disk_name, "mmcblk%dgp%d", &idx, &part_id) != 2) + return false; + + return part_id == spd->gpp_partition_id - 1; +} + +static bool mei_spd_gpp_match(struct mei_spd *spd, struct device *dev) +{ + /* we are only interested in physical partitions */ + if (strncmp(dev->type->name, "disk", sizeof("disk"))) + return false; + + if (spd->dev_type == SPD_TYPE_EMMC) + return mei_spd_gpp_mmc_match(spd, dev); + else if (spd->dev_type == SPD_TYPE_UFS) + return mei_spd_lun_ufs_match(spd, dev); + else + return false; +} + +static int gpp_add_device(struct device *dev, struct class_interface *intf) +{ + struct mei_spd *spd = container_of(intf, struct mei_spd, gpp_interface); + + if (!mei_spd_gpp_match(spd, dev)) + return 0; + + mutex_lock(&spd->lock); + if (mei_spd_gpp_open(spd, dev)) { + mutex_unlock(&spd->lock); + return 0; + } + + schedule_work(&spd->status_send_w); + mutex_unlock(&spd->lock); + + return 0; +} + +static void gpp_remove_device(struct device *dev, struct class_interface *intf) +{ + struct mei_spd *spd = container_of(intf, struct mei_spd, gpp_interface); + + if (!mei_spd_gpp_match(spd, dev)) + return; + + mutex_lock(&spd->lock); + if (mei_spd_gpp_close(spd)) { + mutex_unlock(&spd->lock); + return; + } + + if (spd->state != MEI_SPD_STATE_STOPPING) + schedule_work(&spd->status_send_w); + mutex_unlock(&spd->lock); +} + +int mei_spd_gpp_read(struct mei_spd *spd, size_t off, u8 *data, size_t size) +{ + int ret; + + spd_dbg(spd, "GPP read offset = %zx, size = %zx\n", off, size); + + if (!mei_spd_gpp_is_open(spd)) + return -ENODEV; + + ret = mei_spd_bd_read(spd, off, size, NULL, data); + if (ret) + spd_err(spd, "GPP read failed ret = %d\n", ret); + + return ret; +} + +int mei_spd_gpp_write(struct mei_spd *spd, size_t off, u8 *data, size_t size) +{ + int ret; + + spd_dbg(spd, "GPP write offset = %zx, size = %zx\n", off, size); + + if (!mei_spd_gpp_is_open(spd)) + return -ENODEV; + + ret = mei_spd_bd_write(spd, off, size, NULL, data); + if (ret) + spd_err(spd, "GPP write failed ret = %d\n", ret); + + return ret; +} + +void mei_spd_gpp_prepare(struct mei_spd *spd) +{ + spd->gpp_interface.add_dev = gpp_add_device; + spd->gpp_interface.remove_dev = gpp_remove_device; + spd->gpp_interface.class = &block_class; +} + +int mei_spd_gpp_init(struct mei_spd *spd) +{ + int ret; + + ret = class_interface_register(&spd->gpp_interface); + if (ret) + spd_err(spd, "Can't register interface\n"); + return ret; +} + +void mei_spd_gpp_exit(struct mei_spd *spd) +{ + class_interface_unregister(&spd->gpp_interface); +} diff --git a/drivers/misc/mei/spd/main.c b/drivers/misc/mei/spd/main.c new file mode 100644 index 000000000000..2adccce70eaf --- /dev/null +++ b/drivers/misc/mei/spd/main.c @@ -0,0 +1,118 @@ +// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0 +/* + * Copyright(c) 2015 - 2018 Intel Corporation. All rights reserved. + */ +#include + +#include "spd.h" + +static void mei_spd_rx_cb(struct mei_cl_device *cldev) +{ + struct mei_spd *spd = mei_cldev_get_drvdata(cldev); + + mutex_lock(&spd->lock); + mei_spd_cmd(spd); + mutex_unlock(&spd->lock); +} + +static int mei_spd_probe(struct mei_cl_device *cldev, + const struct mei_cl_device_id *id) +{ + struct mei_spd *spd; + u8 ver = mei_cldev_ver(cldev); + int ret; + + dev_dbg(&cldev->dev, "probing mei spd ver = %d\n", ver); + + if (ver < 2) { + dev_warn(&cldev->dev, "unuspported protocol version %d\n", ver); + return -ENODEV; + } + + spd = mei_spd_alloc(cldev); + if (!spd) + return -ENOMEM; + + mei_cldev_set_drvdata(cldev, spd); + + ret = mei_spd_dbgfs_register(spd, "spd"); + if (ret) + goto free; + + ret = mei_cldev_enable(cldev); + if (ret < 0) { + dev_err(&cldev->dev, "Could not enable device ret = %d\n", ret); + goto free; + } + + ret = mei_cldev_register_rx_cb(cldev, mei_spd_rx_cb); + if (ret) { + dev_err(&cldev->dev, "Error register event %d\n", ret); + goto disable; + } + + spd_dbg(spd, "protocol version %d\n", ver); + mei_spd_gpp_prepare(spd); + mutex_lock(&spd->lock); + ret = mei_spd_cmd_init_req(spd); + mutex_unlock(&spd->lock); + if (ret) { + dev_err(&cldev->dev, "Could not start ret = %d\n", ret); + goto disable; + } + + return 0; + +disable: + mei_cldev_disable(cldev); + +free: + mei_spd_dbgfs_deregister(spd); + mei_cldev_set_drvdata(cldev, NULL); + mei_spd_free(spd); + return ret; +} + +static int mei_spd_remove(struct mei_cl_device *cldev) +{ + struct mei_spd *spd = mei_cldev_get_drvdata(cldev); + + if (spd->state == MEI_SPD_STATE_RUNNING) { + spd->state = MEI_SPD_STATE_STOPPING; + mei_spd_gpp_exit(spd); + mutex_lock(&spd->lock); + mei_spd_cmd_storage_status_req(spd); + mutex_unlock(&spd->lock); + } + + mei_cldev_disable(cldev); + mei_spd_dbgfs_deregister(spd); + mei_cldev_set_drvdata(cldev, NULL); + mei_spd_free(spd); + + return 0; +} + +#define MEI_SPD_UUID UUID_LE(0x2a39291f, 0x5551, 0x482f, \ + 0x99, 0xcb, 0x9e, 0x22, 0x74, 0x97, 0x8c, 0xa8) + +static struct mei_cl_device_id mei_spd_tbl[] = { + { .uuid = MEI_SPD_UUID, .version = MEI_CL_VERSION_ANY}, + /* required last entry */ + { } +}; +MODULE_DEVICE_TABLE(mei, mei_spd_tbl); + +static struct mei_cl_driver mei_spd_driver = { + .id_table = mei_spd_tbl, + .name = "mei_spd", + + .probe = mei_spd_probe, + .remove = mei_spd_remove, +}; + +module_mei_cl_driver(mei_spd_driver); + +MODULE_AUTHOR("Intel Corporation"); +MODULE_LICENSE("Dual BSD/GPL"); +MODULE_DESCRIPTION("Storage Proxy driver based on mei bus"); diff --git a/drivers/misc/mei/spd/spd.h b/drivers/misc/mei/spd/spd.h new file mode 100644 index 000000000000..cd30d0ed18ae --- /dev/null +++ b/drivers/misc/mei/spd/spd.h @@ -0,0 +1,93 @@ +/* SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0 */ +/* + * Copyright (C) 2015-2018 Intel Corp. All rights reserved + */ +#ifndef _MEI_SPD_H +#define _MEI_SPD_H + +#include +#include + +enum mei_spd_state { + MEI_SPD_STATE_INIT, + MEI_SPD_STATE_INIT_WAIT, + MEI_SPD_STATE_INIT_DONE, + MEI_SPD_STATE_RUNNING, + MEI_SPD_STATE_STOPPING, +}; + +/** + * struct mei_spd - spd device struct + * + * @cldev: client bus device + * @gpp: GPP partition block device + * @gpp_partition_id: GPP partition id (1-6) + * @gpp_interface: gpp class interface for discovery + * @dev_type: storage device type + * @dev_id_sz: device id size + * @dev_id: device id string + * @lock: mutex to sync request processing + * @state: driver state + * @status_send_w: workitem for sending status to the FW + * @buf_sz: receive/transmit buffer allocated size + * @buf: receive/transmit buffer + * @dbgfs_dir: debugfs directory entry + */ +struct mei_spd { + struct mei_cl_device *cldev; + struct block_device *gpp; + u32 gpp_partition_id; + struct class_interface gpp_interface; + u32 dev_type; + u32 dev_id_sz; + u8 *dev_id; + struct mutex lock; /* mutex to sync request processing */ + enum mei_spd_state state; + struct work_struct status_send_w; + size_t buf_sz; + u8 *buf; + +#if IS_ENABLED(CONFIG_DEBUG_FS) + struct dentry *dbgfs_dir; +#endif /* CONFIG_DEBUG_FS */ +}; + +struct mei_spd *mei_spd_alloc(struct mei_cl_device *cldev); +void mei_spd_free(struct mei_spd *spd); + +int mei_spd_cmd_init_req(struct mei_spd *spd); +int mei_spd_cmd_storage_status_req(struct mei_spd *spd); +ssize_t mei_spd_cmd(struct mei_spd *spd); + +void mei_spd_gpp_prepare(struct mei_spd *spd); +bool mei_spd_gpp_is_open(struct mei_spd *spd); +int mei_spd_gpp_init(struct mei_spd *spd); +void mei_spd_gpp_exit(struct mei_spd *spd); +int mei_spd_gpp_read(struct mei_spd *spd, size_t off, u8 *data, size_t size); +int mei_spd_gpp_write(struct mei_spd *spd, size_t off, u8 *data, size_t size); + +#if IS_ENABLED(CONFIG_DEBUG_FS) +int mei_spd_dbgfs_register(struct mei_spd *spd, const char *name); +void mei_spd_dbgfs_deregister(struct mei_spd *spd); +#else +static inline int mei_spd_dbgfs_register(struct mei_spd *spd, const char *name) +{ + return 0; +} + +static inline void mei_spd_dbgfs_deregister(struct mei_spd *spd) +{ +} + +#endif /* CONFIG_DEBUG_FS */ + +const char *mei_spd_state_str(enum mei_spd_state state); + +#define spd_err(spd, fmt, ...) \ + dev_err(&(spd)->cldev->dev, fmt, ##__VA_ARGS__) +#define spd_warn(spd, fmt, ...) \ + dev_warn(&(spd)->cldev->dev, fmt, ##__VA_ARGS__) +#define spd_dbg(spd, fmt, ...) \ + dev_dbg(&(spd)->cldev->dev, fmt, ##__VA_ARGS__) + +#endif /* _MEI_SPD_H */ From ff848033a2b7ed729acf4afe9e7486059f77a506 Mon Sep 17 00:00:00 2001 From: Alexander Usyskin Date: Mon, 9 Feb 2015 17:13:20 +0200 Subject: [PATCH 1076/1103] mei: spd: connect to the rpmb subsystem Connect SPD to RPMB subsystem and implement RPMB storage commands. V9: add SPDX identifiers. Change-Id: I21c9f4526ae5906779b03a488c289c037a18d6e2 Signed-off-by: Alexander Usyskin Signed-off-by: Tomas Winkler --- drivers/misc/mei/spd/Kconfig | 2 +- drivers/misc/mei/spd/Makefile | 1 + drivers/misc/mei/spd/cmd.c | 67 +++++++++++- drivers/misc/mei/spd/main.c | 2 + drivers/misc/mei/spd/rpmb.c | 199 ++++++++++++++++++++++++++++++++++ drivers/misc/mei/spd/spd.h | 11 ++ 6 files changed, 278 insertions(+), 4 deletions(-) create mode 100644 drivers/misc/mei/spd/rpmb.c diff --git a/drivers/misc/mei/spd/Kconfig b/drivers/misc/mei/spd/Kconfig index 8347fbc9fbe0..085f9caa8c66 100644 --- a/drivers/misc/mei/spd/Kconfig +++ b/drivers/misc/mei/spd/Kconfig @@ -3,7 +3,7 @@ # config INTEL_MEI_SPD tristate "Intel MEI Host Storage Proxy Driver" - depends on INTEL_MEI && BLOCK + depends on INTEL_MEI && BLOCK && RPMB help A driver for the host storage proxy ME client The driver enables ME FW to store data on a storage devices diff --git a/drivers/misc/mei/spd/Makefile b/drivers/misc/mei/spd/Makefile index 8e8aba94b9ef..72d0bca2974e 100644 --- a/drivers/misc/mei/spd/Makefile +++ b/drivers/misc/mei/spd/Makefile @@ -6,6 +6,7 @@ obj-$(CONFIG_INTEL_MEI_SPD) += mei_spd.o mei_spd-objs := main.o mei_spd-objs += cmd.o mei_spd-objs += gpp.o +mei_spd-objs += rpmb.o mei_spd-$(CONFIG_DEBUG_FS) += debugfs.o ccflags-y += -D__CHECK_ENDIAN__ diff --git a/drivers/misc/mei/spd/cmd.c b/drivers/misc/mei/spd/cmd.c index 25d682d7eb09..3f45902e23da 100644 --- a/drivers/misc/mei/spd/cmd.c +++ b/drivers/misc/mei/spd/cmd.c @@ -12,6 +12,9 @@ #define spd_cmd_size(_cmd) \ (sizeof(struct spd_cmd_hdr) + \ sizeof(struct spd_cmd_##_cmd)) +#define spd_cmd_rpmb_size(_cmd) \ + (spd_cmd_size(_cmd) + SPD_CLIENT_RPMB_DATA_MAX_SIZE) + #define to_spd_hdr(_buf) (struct spd_cmd_hdr *)(_buf) #define to_spd_cmd(_cmd, _buf) \ (struct spd_cmd_##_cmd *)((_buf) + sizeof(struct spd_cmd_hdr)) @@ -231,7 +234,7 @@ int mei_spd_cmd_storage_status_req(struct mei_spd *spd) req = to_spd_cmd(storage_status_req, spd->buf); req->gpp_on = mei_spd_gpp_is_open(spd); - req->rpmb_on = 0; + req->rpmb_on = mei_spd_rpmb_is_open(spd); ret = mei_cldev_send(spd->cldev, spd->buf, req_len); if (ret != req_len) { @@ -308,6 +311,60 @@ static int mei_spd_cmd_gpp_read(struct mei_spd *spd, struct spd_cmd *cmd, return SPD_STATUS_SUCCESS; } +static int mei_spd_cmd_rpmb_read(struct mei_spd *spd, + struct spd_cmd *cmd, + ssize_t out_buf_sz) +{ + u8 *frame = cmd->rpmb_read.rpmb_frame; + + if (out_buf_sz != spd_cmd_rpmb_size(rpmb_read)) { + spd_err(spd, "Wrong request size\n"); + return SPD_STATUS_INVALID_COMMAND; + } + + if (mei_spd_rpmb_cmd_req(spd, RPMB_READ_DATA, frame)) + return SPD_STATUS_GENERAL_FAILURE; + + spd_dbg(spd, "read RPMB frame performed\n"); + return SPD_STATUS_SUCCESS; +} + +static int mei_spd_cmd_rpmb_write(struct mei_spd *spd, + struct spd_cmd *cmd, + ssize_t out_buf_sz) +{ + u8 *frame = cmd->rpmb_write.rpmb_frame; + + if (out_buf_sz != spd_cmd_rpmb_size(rpmb_write)) { + spd_err(spd, "Wrong request size\n"); + return SPD_STATUS_INVALID_COMMAND; + } + + if (mei_spd_rpmb_cmd_req(spd, RPMB_WRITE_DATA, frame)) + return SPD_STATUS_GENERAL_FAILURE; + + spd_dbg(spd, "write RPMB frame performed\n"); + return SPD_STATUS_SUCCESS; +} + +static int mei_spd_cmd_rpmb_get_counter(struct mei_spd *spd, + struct spd_cmd *cmd, + ssize_t out_buf_sz) +{ + u8 *frame = cmd->rpmb_get_counter.rpmb_frame; + + if (out_buf_sz != spd_cmd_rpmb_size(rpmb_get_counter)) { + spd_err(spd, "Wrong request size\n"); + return SPD_STATUS_INVALID_COMMAND; + } + + if (mei_spd_rpmb_cmd_req(spd, RPMB_WRITE_DATA, frame)) + return SPD_STATUS_GENERAL_FAILURE; + + spd_dbg(spd, "get RPMB counter performed\n"); + return SPD_STATUS_SUCCESS; +} + static int mei_spd_cmd_response(struct mei_spd *spd, ssize_t out_buf_sz) { struct spd_cmd *cmd = (struct spd_cmd *)spd->buf; @@ -326,6 +383,7 @@ static int mei_spd_cmd_response(struct mei_spd *spd, ssize_t out_buf_sz) if (ret) break; mutex_unlock(&spd->lock); + mei_spd_rpmb_init(spd); mei_spd_gpp_init(spd); mutex_lock(&spd->lock); break; @@ -367,10 +425,13 @@ static int mei_spd_cmd_request(struct mei_spd *spd, ssize_t out_buf_sz) switch (spd_cmd) { case SPD_RPMB_WRITE_CMD: + ret = mei_spd_cmd_rpmb_write(spd, cmd, out_buf_sz); + break; case SPD_RPMB_READ_CMD: + ret = mei_spd_cmd_rpmb_read(spd, cmd, out_buf_sz); + break; case SPD_RPMB_GET_COUNTER_CMD: - spd_err(spd, "Command %d is not supported\n", spd_cmd); - ret = SPD_STATUS_NOT_SUPPORTED; + ret = mei_spd_cmd_rpmb_get_counter(spd, cmd, out_buf_sz); break; case SPD_GPP_WRITE_CMD: ret = mei_spd_cmd_gpp_write(spd, cmd, out_buf_sz); diff --git a/drivers/misc/mei/spd/main.c b/drivers/misc/mei/spd/main.c index 2adccce70eaf..468cceffb7a0 100644 --- a/drivers/misc/mei/spd/main.c +++ b/drivers/misc/mei/spd/main.c @@ -53,6 +53,7 @@ static int mei_spd_probe(struct mei_cl_device *cldev, spd_dbg(spd, "protocol version %d\n", ver); mei_spd_gpp_prepare(spd); + mei_spd_rpmb_prepare(spd); mutex_lock(&spd->lock); ret = mei_spd_cmd_init_req(spd); mutex_unlock(&spd->lock); @@ -80,6 +81,7 @@ static int mei_spd_remove(struct mei_cl_device *cldev) if (spd->state == MEI_SPD_STATE_RUNNING) { spd->state = MEI_SPD_STATE_STOPPING; mei_spd_gpp_exit(spd); + mei_spd_rpmb_exit(spd); mutex_lock(&spd->lock); mei_spd_cmd_storage_status_req(spd); mutex_unlock(&spd->lock); diff --git a/drivers/misc/mei/spd/rpmb.c b/drivers/misc/mei/spd/rpmb.c new file mode 100644 index 000000000000..b74d0cd8f802 --- /dev/null +++ b/drivers/misc/mei/spd/rpmb.c @@ -0,0 +1,199 @@ +// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0 +/* + * Intel Host Storage Interface Linux driver + * Copyright (c) 2015 - 2018, Intel Corporation. + */ + +#include "cmd.h" +#include "spd.h" +#include + +static int mei_spd_rpmb_start(struct mei_spd *spd, struct rpmb_dev *rdev) +{ + if (spd->rdev == rdev) + return 0; + + if (spd->rdev) { + spd_warn(spd, "rpmb device already registered\n"); + return -EEXIST; + } + + spd->rdev = rpmb_dev_get(rdev); + spd_dbg(spd, "rpmb partition created\n"); + return 0; +} + +static int mei_spd_rpmb_stop(struct mei_spd *spd, struct rpmb_dev *rdev) +{ + if (!spd->rdev) { + spd_dbg(spd, "Already stopped\n"); + return -EPROTO; + } + + if (rdev && spd->rdev != rdev) { + spd_dbg(spd, "Wrong RPMB on stop\n"); + return -EINVAL; + } + + rpmb_dev_put(spd->rdev); + spd->rdev = NULL; + + spd_dbg(spd, "rpmb partition removed\n"); + return 0; +} + +static int mei_spd_rpmb_match(struct mei_spd *spd, struct rpmb_dev *rdev) +{ + if (spd->dev_id_sz && rdev->ops->dev_id) { + if (rdev->ops->dev_id_len != spd->dev_id_sz || + memcmp(rdev->ops->dev_id, spd->dev_id, + rdev->ops->dev_id_len)) { + spd_dbg(spd, "ignore request for another rpmb\n"); + /* return 0; FW sends garbage now, ignore it */ + } + } + + switch (rdev->ops->type) { + case RPMB_TYPE_EMMC: + if (spd->dev_type != SPD_TYPE_EMMC) + return 0; + break; + case RPMB_TYPE_UFS: + if (spd->dev_type != SPD_TYPE_UFS) + return 0; + break; + default: + return 0; + } + + return 1; +} + +static int rpmb_add_device(struct device *dev, struct class_interface *intf) +{ + struct mei_spd *spd = + container_of(intf, struct mei_spd, rpmb_interface); + struct rpmb_dev *rdev = to_rpmb_dev(dev); + + if (!mei_spd_rpmb_match(spd, rdev)) + return 0; + + mutex_lock(&spd->lock); + if (mei_spd_rpmb_start(spd, rdev)) { + mutex_unlock(&spd->lock); + return 0; + } + + schedule_work(&spd->status_send_w); + mutex_unlock(&spd->lock); + + return 0; +} + +static void rpmb_remove_device(struct device *dev, struct class_interface *intf) +{ + struct mei_spd *spd = + container_of(intf, struct mei_spd, rpmb_interface); + struct rpmb_dev *rdev = to_rpmb_dev(dev); + + if (!mei_spd_rpmb_match(spd, rdev)) + return; + + mutex_lock(&spd->lock); + if (mei_spd_rpmb_stop(spd, rdev)) { + mutex_unlock(&spd->lock); + return; + } + + if (spd->state != MEI_SPD_STATE_STOPPING) + schedule_work(&spd->status_send_w); + mutex_unlock(&spd->lock); +} + +void mei_spd_rpmb_prepare(struct mei_spd *spd) +{ + spd->rpmb_interface.add_dev = rpmb_add_device; + spd->rpmb_interface.remove_dev = rpmb_remove_device; + spd->rpmb_interface.class = &rpmb_class; +} + +/** + * mei_spd_rpmb_init - init RPMB connection + * + * @spd: device + * + * Locking: spd->lock should not be held + * Returns: 0 if initialized successfully, <0 otherwise + */ +int mei_spd_rpmb_init(struct mei_spd *spd) +{ + int ret; + + ret = class_interface_register(&spd->rpmb_interface); + if (ret) + spd_err(spd, "Can't register interface\n"); + return ret; +} + +/** + * mei_spd_rpmb_exit - clean RPMB connection + * + * @spd: device + * + * Locking: spd->lock should not be held + */ +void mei_spd_rpmb_exit(struct mei_spd *spd) +{ + class_interface_unregister(&spd->rpmb_interface); +} + +int mei_spd_rpmb_cmd_req(struct mei_spd *spd, u16 req, void *buf) +{ + struct rpmb_cmd cmd[3]; + struct rpmb_frame_jdec *frame_res = NULL; + u32 flags; + unsigned int i; + int ret; + + if (!spd->rdev) { + spd_err(spd, "RPMB not ready\n"); + return -ENODEV; + } + + i = 0; + flags = RPMB_F_WRITE; + if (req == RPMB_WRITE_DATA || req == RPMB_PROGRAM_KEY) + flags |= RPMB_F_REL_WRITE; + cmd[i].flags = flags; + cmd[i].nframes = 1; + cmd[i].frames = buf; + i++; + + if (req == RPMB_WRITE_DATA || req == RPMB_PROGRAM_KEY) { + frame_res = kzalloc(sizeof(*frame_res), GFP_KERNEL); + if (!frame_res) + return -ENOMEM; + frame_res->req_resp = cpu_to_be16(RPMB_RESULT_READ); + cmd[i].flags = RPMB_F_WRITE; + cmd[i].nframes = 1; + cmd[i].frames = frame_res; + i++; + } + + cmd[i].flags = 0; + cmd[i].nframes = 1; + cmd[i].frames = buf; + i++; + + ret = rpmb_cmd_seq(spd->rdev, cmd, i); + if (ret) + spd_err(spd, "RPMB req failed ret = %d\n", ret); + + kfree(frame_res); + return ret; +} + +bool mei_spd_rpmb_is_open(struct mei_spd *spd) +{ + return !!spd->rdev; +} diff --git a/drivers/misc/mei/spd/spd.h b/drivers/misc/mei/spd/spd.h index cd30d0ed18ae..b919a5cb7a4c 100644 --- a/drivers/misc/mei/spd/spd.h +++ b/drivers/misc/mei/spd/spd.h @@ -7,6 +7,7 @@ #include #include +#include enum mei_spd_state { MEI_SPD_STATE_INIT, @@ -26,6 +27,8 @@ enum mei_spd_state { * @dev_type: storage device type * @dev_id_sz: device id size * @dev_id: device id string + * @rdev: RPMB device + * @rpmb_interface: gpp class interface for discovery * @lock: mutex to sync request processing * @state: driver state * @status_send_w: workitem for sending status to the FW @@ -41,6 +44,8 @@ struct mei_spd { u32 dev_type; u32 dev_id_sz; u8 *dev_id; + struct rpmb_dev *rdev; + struct class_interface rpmb_interface; struct mutex lock; /* mutex to sync request processing */ enum mei_spd_state state; struct work_struct status_send_w; @@ -66,6 +71,12 @@ void mei_spd_gpp_exit(struct mei_spd *spd); int mei_spd_gpp_read(struct mei_spd *spd, size_t off, u8 *data, size_t size); int mei_spd_gpp_write(struct mei_spd *spd, size_t off, u8 *data, size_t size); +void mei_spd_rpmb_prepare(struct mei_spd *spd); +bool mei_spd_rpmb_is_open(struct mei_spd *spd); +int mei_spd_rpmb_init(struct mei_spd *spd); +void mei_spd_rpmb_exit(struct mei_spd *spd); +int mei_spd_rpmb_cmd_req(struct mei_spd *spd, u16 req_type, void *buf); + #if IS_ENABLED(CONFIG_DEBUG_FS) int mei_spd_dbgfs_register(struct mei_spd *spd, const char *name); void mei_spd_dbgfs_deregister(struct mei_spd *spd); From aa16c466d83a612463a0df6b61e0b0d922d78df9 Mon Sep 17 00:00:00 2001 From: "Pottratz, Dwane" Date: Fri, 27 Jan 2017 10:18:38 -0800 Subject: [PATCH 1077/1103] greybus: Remove android make file Remove the andorid make file. With multiple kernel repos in the android build system the make file will cause a redefine and fail Signed-off-by: Pottratz, Dwane --- drivers/staging/greybus/tools/Android.mk | 10 ---------- 1 file changed, 10 deletions(-) delete mode 100644 drivers/staging/greybus/tools/Android.mk diff --git a/drivers/staging/greybus/tools/Android.mk b/drivers/staging/greybus/tools/Android.mk deleted file mode 100644 index fdadbf611757..000000000000 --- a/drivers/staging/greybus/tools/Android.mk +++ /dev/null @@ -1,10 +0,0 @@ -LOCAL_PATH:= $(call my-dir) - -include $(CLEAR_VARS) - -LOCAL_SRC_FILES:= loopback_test.c -LOCAL_MODULE_TAGS := optional -LOCAL_MODULE := gb_loopback_test - -include $(BUILD_EXECUTABLE) - From 9a4029121bb2709198f5f99522ee2654591fe0f9 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Mon, 17 Sep 2018 16:29:07 +0200 Subject: [PATCH 1078/1103] x86/mm/init32: Mark text and rodata RO in one go The sequence of marking text and rodata read-only in 32bit init is: set_ro(text); kernel_set_to_readonly = 1; set_ro(rodata); When kernel_set_to_readonly is 1 it enables the protection mechanism in CPA for the read only regions. With the upcoming checks for existing mappings this consequently triggers the warning about an existing mapping being incorrect vs. static protections because rodata has not been converted yet. There is no technical reason to split the two, so just combine the RO protection to convert text and rodata in one go. Convert the printks to pr_info while at it. Reported-by: kernel test robot Signed-off-by: Thomas Gleixner Reviewed-by: Dave Hansen Cc: Peter Zijlstra Cc: Bin Yang Cc: Mark Gross Link: https://lkml.kernel.org/r/20180917143545.731701535@linutronix.de Cc: Zhang Ning Signed-off-by: Lili Li --- arch/x86/mm/init_32.c | 23 ++++------------------- 1 file changed, 4 insertions(+), 19 deletions(-) diff --git a/arch/x86/mm/init_32.c b/arch/x86/mm/init_32.c index 979e0a02cbe1..142c7d9f89cc 100644 --- a/arch/x86/mm/init_32.c +++ b/arch/x86/mm/init_32.c @@ -923,34 +923,19 @@ static void mark_nxdata_nx(void) void mark_rodata_ro(void) { unsigned long start = PFN_ALIGN(_text); - unsigned long size = PFN_ALIGN(_etext) - start; + unsigned long size = (unsigned long)__end_rodata - start; set_pages_ro(virt_to_page(start), size >> PAGE_SHIFT); - printk(KERN_INFO "Write protecting the kernel text: %luk\n", + pr_info("Write protecting kernel text and read-only data: %luk\n", size >> 10); kernel_set_to_readonly = 1; #ifdef CONFIG_CPA_DEBUG - printk(KERN_INFO "Testing CPA: Reverting %lx-%lx\n", - start, start+size); - set_pages_rw(virt_to_page(start), size>>PAGE_SHIFT); - - printk(KERN_INFO "Testing CPA: write protecting again\n"); - set_pages_ro(virt_to_page(start), size>>PAGE_SHIFT); -#endif - - start += size; - size = (unsigned long)__end_rodata - start; - set_pages_ro(virt_to_page(start), size >> PAGE_SHIFT); - printk(KERN_INFO "Write protecting the kernel read-only data: %luk\n", - size >> 10); - -#ifdef CONFIG_CPA_DEBUG - printk(KERN_INFO "Testing CPA: undo %lx-%lx\n", start, start + size); + pr_info("Testing CPA: Reverting %lx-%lx\n", start, start + size); set_pages_rw(virt_to_page(start), size >> PAGE_SHIFT); - printk(KERN_INFO "Testing CPA: write protecting again\n"); + pr_info("Testing CPA: write protecting again\n"); set_pages_ro(virt_to_page(start), size >> PAGE_SHIFT); #endif mark_nxdata_nx(); From 77448d693ba6731005e6a4873d461af62b55ac6d Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Mon, 17 Sep 2018 16:29:08 +0200 Subject: [PATCH 1079/1103] x86/mm/cpa: Split, rename and clean up try_preserve_large_page() Avoid the extra variable and gotos by splitting the function into the actual algorithm and a callable function which contains the lock protection. Rename it to should_split_large_page() while at it so the return values make actually sense. Clean up the code flow, comments and general whitespace damage while at it. No functional change. Signed-off-by: Thomas Gleixner Reviewed-by: Dave Hansen Cc: Peter Zijlstra Cc: Bin Yang Cc: Mark Gross Link: https://lkml.kernel.org/r/20180917143545.830507216@linutronix.de Cc: Zhang Ning Signed-off-by: Lili Li --- arch/x86/mm/pageattr.c | 121 ++++++++++++++++++++--------------------- 1 file changed, 60 insertions(+), 61 deletions(-) diff --git a/arch/x86/mm/pageattr.c b/arch/x86/mm/pageattr.c index 51a5a69ecac9..36f5d711ddbe 100644 --- a/arch/x86/mm/pageattr.c +++ b/arch/x86/mm/pageattr.c @@ -421,18 +421,18 @@ pte_t *lookup_address_in_pgd(pgd_t *pgd, unsigned long address, */ pte_t *lookup_address(unsigned long address, unsigned int *level) { - return lookup_address_in_pgd(pgd_offset_k(address), address, level); + return lookup_address_in_pgd(pgd_offset_k(address), address, level); } EXPORT_SYMBOL_GPL(lookup_address); static pte_t *_lookup_address_cpa(struct cpa_data *cpa, unsigned long address, unsigned int *level) { - if (cpa->pgd) + if (cpa->pgd) return lookup_address_in_pgd(cpa->pgd + pgd_index(address), address, level); - return lookup_address(address, level); + return lookup_address(address, level); } /* @@ -549,27 +549,22 @@ static pgprot_t pgprot_clear_protnone_bits(pgprot_t prot) return prot; } -static int -try_preserve_large_page(pte_t *kpte, unsigned long address, - struct cpa_data *cpa) +static int __should_split_large_page(pte_t *kpte, unsigned long address, + struct cpa_data *cpa) { - unsigned long nextpage_addr, numpages, pmask, psize, addr, pfn, old_pfn; - pte_t new_pte, old_pte, *tmp; + unsigned long numpages, pmask, psize, lpaddr, addr, pfn, old_pfn; pgprot_t old_prot, new_prot, req_prot; - int i, do_split = 1; + pte_t new_pte, old_pte, *tmp; enum pg_level level; + int i; - if (cpa->force_split) - return 1; - - spin_lock(&pgd_lock); /* * Check for races, another CPU might have split this page * up already: */ tmp = _lookup_address_cpa(cpa, address, &level); if (tmp != kpte) - goto out_unlock; + return 1; switch (level) { case PG_LEVEL_2M: @@ -581,8 +576,7 @@ try_preserve_large_page(pte_t *kpte, unsigned long address, old_pfn = pud_pfn(*(pud_t *)kpte); break; default: - do_split = -EINVAL; - goto out_unlock; + return -EINVAL; } psize = page_level_size(level); @@ -592,8 +586,8 @@ try_preserve_large_page(pte_t *kpte, unsigned long address, * Calculate the number of pages, which fit into this large * page starting at address: */ - nextpage_addr = (address + psize) & pmask; - numpages = (nextpage_addr - address) >> PAGE_SHIFT; + lpaddr = (address + psize) & pmask; + numpages = (lpaddr - address) >> PAGE_SHIFT; if (numpages < cpa->numpages) cpa->numpages = numpages; @@ -620,57 +614,62 @@ try_preserve_large_page(pte_t *kpte, unsigned long address, pgprot_val(req_prot) |= _PAGE_PSE; /* - * old_pfn points to the large page base pfn. So we need - * to add the offset of the virtual address: + * old_pfn points to the large page base pfn. So we need to add the + * offset of the virtual address: */ pfn = old_pfn + ((address & (psize - 1)) >> PAGE_SHIFT); cpa->pfn = pfn; - new_prot = static_protections(req_prot, address, pfn); + /* + * Calculate the large page base address and the number of 4K pages + * in the large page + */ + lpaddr = address & pmask; + numpages = psize >> PAGE_SHIFT; /* - * We need to check the full range, whether - * static_protection() requires a different pgprot for one of - * the pages in the range we try to preserve: + * Make sure that the requested pgprot does not violate the static + * protections. Check the full large page whether one of the pages + * in it results in a different pgprot than the first one of the + * requested range. If yes, then the page needs to be split. */ - addr = address & pmask; + new_prot = static_protections(req_prot, address, pfn); pfn = old_pfn; - for (i = 0; i < (psize >> PAGE_SHIFT); i++, addr += PAGE_SIZE, pfn++) { + for (i = 0, addr = lpaddr; i < numpages; i++, addr += PAGE_SIZE, pfn++) { pgprot_t chk_prot = static_protections(req_prot, addr, pfn); if (pgprot_val(chk_prot) != pgprot_val(new_prot)) - goto out_unlock; + return 1; } - /* - * If there are no changes, return. maxpages has been updated - * above: - */ - if (pgprot_val(new_prot) == pgprot_val(old_prot)) { - do_split = 0; - goto out_unlock; - } + /* If there are no changes, return. */ + if (pgprot_val(new_prot) == pgprot_val(old_prot)) + return 0; /* - * We need to change the attributes. Check, whether we can - * change the large page in one go. We request a split, when - * the address is not aligned and the number of pages is - * smaller than the number of pages in the large page. Note - * that we limited the number of possible pages already to - * the number of pages in the large page. + * Verify that the address is aligned and the number of pages + * covers the full page. */ - if (address == (address & pmask) && cpa->numpages == (psize >> PAGE_SHIFT)) { - /* - * The address is aligned and the number of pages - * covers the full page. - */ - new_pte = pfn_pte(old_pfn, new_prot); - __set_pmd_pte(kpte, address, new_pte); - cpa->flags |= CPA_FLUSHTLB; - do_split = 0; - } + if (address != lpaddr || cpa->numpages != numpages) + return 1; + + /* All checks passed. Update the large page mapping. */ + new_pte = pfn_pte(old_pfn, new_prot); + __set_pmd_pte(kpte, address, new_pte); + cpa->flags |= CPA_FLUSHTLB; + return 0; +} + +static int should_split_large_page(pte_t *kpte, unsigned long address, + struct cpa_data *cpa) +{ + int do_split; + + if (cpa->force_split) + return 1; -out_unlock: + spin_lock(&pgd_lock); + do_split = __should_split_large_page(kpte, address, cpa); spin_unlock(&pgd_lock); return do_split; @@ -1273,7 +1272,7 @@ static int __change_page_attr(struct cpa_data *cpa, int primary) * Check, whether we can keep the large page intact * and just change the pte: */ - do_split = try_preserve_large_page(kpte, address, cpa); + do_split = should_split_large_page(kpte, address, cpa); /* * When the range fits into the existing large page, * return. cp->numpages and cpa->tlbflush have been updated in @@ -1288,23 +1287,23 @@ static int __change_page_attr(struct cpa_data *cpa, int primary) err = split_large_page(cpa, kpte, address); if (!err) { /* - * Do a global flush tlb after splitting the large page - * and before we do the actual change page attribute in the PTE. - * - * With out this, we violate the TLB application note, that says - * "The TLBs may contain both ordinary and large-page + * Do a global flush tlb after splitting the large page + * and before we do the actual change page attribute in the PTE. + * + * With out this, we violate the TLB application note, that says + * "The TLBs may contain both ordinary and large-page * translations for a 4-KByte range of linear addresses. This * may occur if software modifies the paging structures so that * the page size used for the address range changes. If the two * translations differ with respect to page frame or attributes * (e.g., permissions), processor behavior is undefined and may * be implementation-specific." - * - * We do this global tlb flush inside the cpa_lock, so that we + * + * We do this global tlb flush inside the cpa_lock, so that we * don't allow any other cpu, with stale tlb entries change the * page attribute in parallel, that also falls into the * just split large page entry. - */ + */ flush_tlb_all(); goto repeat; } From 488bc9fe4e721711bac5f60f070c097bb80d69b2 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Mon, 17 Sep 2018 16:29:09 +0200 Subject: [PATCH 1080/1103] x86/mm/cpa: Rework static_protections() static_protections() is pretty unreadable. Split it up into separate checks for each protection area. Signed-off-by: Thomas Gleixner Reviewed-by: Dave Hansen Cc: Peter Zijlstra Cc: Bin Yang Cc: Mark Gross Link: https://lkml.kernel.org/r/20180917143545.913005317@linutronix.de Cc: Zhang Ning Signed-off-by: Lili Li --- arch/x86/mm/pageattr.c | 162 +++++++++++++++++++++++++---------------- 1 file changed, 98 insertions(+), 64 deletions(-) diff --git a/arch/x86/mm/pageattr.c b/arch/x86/mm/pageattr.c index 36f5d711ddbe..9731aeeebe71 100644 --- a/arch/x86/mm/pageattr.c +++ b/arch/x86/mm/pageattr.c @@ -286,84 +286,118 @@ static void cpa_flush_array(unsigned long *start, int numpages, int cache, } } +#ifdef CONFIG_PCI_BIOS /* - * Certain areas of memory on x86 require very specific protection flags, - * for example the BIOS area or kernel text. Callers don't always get this - * right (again, ioremap() on BIOS memory is not uncommon) so this function - * checks and fixes these known static required protection bits. + * The BIOS area between 640k and 1Mb needs to be executable for PCI BIOS + * based config access (CONFIG_PCI_GOBIOS) support. */ -static inline pgprot_t static_protections(pgprot_t prot, unsigned long address, - unsigned long pfn) -{ - pgprot_t forbidden = __pgprot(0); +#define BIOS_PFN PFN_DOWN(BIOS_BEGIN) +#define BIOS_PFN_END PFN_DOWN(BIOS_END) - /* - * The BIOS area between 640k and 1Mb needs to be executable for - * PCI BIOS based config access (CONFIG_PCI_GOBIOS) support. - */ -#ifdef CONFIG_PCI_BIOS - if (pcibios_enabled && within(pfn, BIOS_BEGIN >> PAGE_SHIFT, BIOS_END >> PAGE_SHIFT)) - pgprot_val(forbidden) |= _PAGE_NX; +static pgprotval_t protect_pci_bios(unsigned long pfn) +{ + if (pcibios_enabled && within(pfn, BIOS_PFN, BIOS_PFN_END)) + return _PAGE_NX; + return 0; +} +#else +static pgprotval_t protect_pci_bios(unsigned long pfn) +{ + return 0; +} #endif - /* - * The kernel text needs to be executable for obvious reasons - * Does not cover __inittext since that is gone later on. On - * 64bit we do not enforce !NX on the low mapping - */ - if (within(address, (unsigned long)_text, (unsigned long)_etext)) - pgprot_val(forbidden) |= _PAGE_NX; +/* + * The .rodata section needs to be read-only. Using the pfn catches all + * aliases. This also includes __ro_after_init, so do not enforce until + * kernel_set_to_readonly is true. + */ +static pgprotval_t protect_rodata(unsigned long pfn) +{ + unsigned long start_pfn = __pa_symbol(__start_rodata) >> PAGE_SHIFT; + unsigned long end_pfn = __pa_symbol(__end_rodata) >> PAGE_SHIFT; - /* - * The .rodata section needs to be read-only. Using the pfn - * catches all aliases. This also includes __ro_after_init, - * so do not enforce until kernel_set_to_readonly is true. - */ - if (kernel_set_to_readonly && - within(pfn, __pa_symbol(__start_rodata) >> PAGE_SHIFT, - __pa_symbol(__end_rodata) >> PAGE_SHIFT)) - pgprot_val(forbidden) |= _PAGE_RW; + if (kernel_set_to_readonly && within(pfn, start_pfn, end_pfn)) + return _PAGE_RW; + return 0; +} + +/* + * Protect kernel text against becoming non executable by forbidding + * _PAGE_NX. This protects only the high kernel mapping (_text -> _etext) + * out of which the kernel actually executes. Do not protect the low + * mapping. + * + * This does not cover __inittext since that is gone after boot. + */ +static pgprotval_t protect_kernel_text(unsigned long address) +{ + if (within(address, (unsigned long)_text, (unsigned long)_etext)) + return _PAGE_NX; + return 0; +} #if defined(CONFIG_X86_64) +/* + * Once the kernel maps the text as RO (kernel_set_to_readonly is set), + * kernel text mappings for the large page aligned text, rodata sections + * will be always read-only. For the kernel identity mappings covering the + * holes caused by this alignment can be anything that user asks. + * + * This will preserve the large page mappings for kernel text/data at no + * extra cost. + */ +static pgprotval_t protect_kernel_text_ro(unsigned long address) +{ + unsigned long end = (unsigned long)__end_rodata_hpage_align; + unsigned long start = (unsigned long)_text; + unsigned int level; + + if (!kernel_set_to_readonly || !within(address, start, end)) + return 0; /* - * Once the kernel maps the text as RO (kernel_set_to_readonly is set), - * kernel text mappings for the large page aligned text, rodata sections - * will be always read-only. For the kernel identity mappings covering - * the holes caused by this alignment can be anything that user asks. + * Don't enforce the !RW mapping for the kernel text mapping, if + * the current mapping is already using small page mapping. No + * need to work hard to preserve large page mappings in this case. * - * This will preserve the large page mappings for kernel text/data - * at no extra cost. + * This also fixes the Linux Xen paravirt guest boot failure caused + * by unexpected read-only mappings for kernel identity + * mappings. In this paravirt guest case, the kernel text mapping + * and the kernel identity mapping share the same page-table pages, + * so the protections for kernel text and identity mappings have to + * be the same. */ - if (kernel_set_to_readonly && - within(address, (unsigned long)_text, - (unsigned long)__end_rodata_hpage_align)) { - unsigned int level; - - /* - * Don't enforce the !RW mapping for the kernel text mapping, - * if the current mapping is already using small page mapping. - * No need to work hard to preserve large page mappings in this - * case. - * - * This also fixes the Linux Xen paravirt guest boot failure - * (because of unexpected read-only mappings for kernel identity - * mappings). In this paravirt guest case, the kernel text - * mapping and the kernel identity mapping share the same - * page-table pages. Thus we can't really use different - * protections for the kernel text and identity mappings. Also, - * these shared mappings are made of small page mappings. - * Thus this don't enforce !RW mapping for small page kernel - * text mapping logic will help Linux Xen parvirt guest boot - * as well. - */ - if (lookup_address(address, &level) && (level != PG_LEVEL_4K)) - pgprot_val(forbidden) |= _PAGE_RW; - } + if (lookup_address(address, &level) && (level != PG_LEVEL_4K)) + return _PAGE_RW; + return 0; +} +#else +static pgprotval_t protect_kernel_text_ro(unsigned long address) +{ + return 0; +} #endif - prot = __pgprot(pgprot_val(prot) & ~pgprot_val(forbidden)); +/* + * Certain areas of memory on x86 require very specific protection flags, + * for example the BIOS area or kernel text. Callers don't always get this + * right (again, ioremap() on BIOS memory is not uncommon) so this function + * checks and fixes these known static required protection bits. + */ +static inline pgprot_t static_protections(pgprot_t prot, unsigned long address, + unsigned long pfn) +{ + pgprotval_t forbidden; + + /* Operate on the virtual address */ + forbidden = protect_kernel_text(address); + forbidden |= protect_kernel_text_ro(address); - return prot; + /* Check the PFN directly */ + forbidden |= protect_pci_bios(pfn); + forbidden |= protect_rodata(pfn); + + return __pgprot(pgprot_val(prot) & ~forbidden); } /* From 754c237130a6b4013fdd66198bb709f476731c48 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Mon, 17 Sep 2018 16:29:10 +0200 Subject: [PATCH 1081/1103] x86/mm/cpa: Allow range check for static protections Checking static protections only page by page is slow especially for huge pages. To allow quick checks over a complete range, add the ability to do that. Make the checks inclusive so the ranges can be directly used for debug output later. No functional change. Signed-off-by: Thomas Gleixner Reviewed-by: Dave Hansen Cc: Peter Zijlstra Cc: Bin Yang Cc: Mark Gross Link: https://lkml.kernel.org/r/20180917143545.995734490@linutronix.de Cc: Zhang Ning Signed-off-by: Lili Li --- arch/x86/mm/pageattr.c | 69 +++++++++++++++++++++++++++--------------- 1 file changed, 44 insertions(+), 25 deletions(-) diff --git a/arch/x86/mm/pageattr.c b/arch/x86/mm/pageattr.c index 9731aeeebe71..61ff27848452 100644 --- a/arch/x86/mm/pageattr.c +++ b/arch/x86/mm/pageattr.c @@ -286,22 +286,29 @@ static void cpa_flush_array(unsigned long *start, int numpages, int cache, } } +static bool overlaps(unsigned long r1_start, unsigned long r1_end, + unsigned long r2_start, unsigned long r2_end) +{ + return (r1_start <= r2_end && r1_end >= r2_start) || + (r2_start <= r1_end && r2_end >= r1_start); +} + #ifdef CONFIG_PCI_BIOS /* * The BIOS area between 640k and 1Mb needs to be executable for PCI BIOS * based config access (CONFIG_PCI_GOBIOS) support. */ #define BIOS_PFN PFN_DOWN(BIOS_BEGIN) -#define BIOS_PFN_END PFN_DOWN(BIOS_END) +#define BIOS_PFN_END PFN_DOWN(BIOS_END - 1) -static pgprotval_t protect_pci_bios(unsigned long pfn) +static pgprotval_t protect_pci_bios(unsigned long spfn, unsigned long epfn) { - if (pcibios_enabled && within(pfn, BIOS_PFN, BIOS_PFN_END)) + if (pcibios_enabled && overlaps(spfn, epfn, BIOS_PFN, BIOS_PFN_END)) return _PAGE_NX; return 0; } #else -static pgprotval_t protect_pci_bios(unsigned long pfn) +static pgprotval_t protect_pci_bios(unsigned long spfn, unsigned long epfn) { return 0; } @@ -312,12 +319,17 @@ static pgprotval_t protect_pci_bios(unsigned long pfn) * aliases. This also includes __ro_after_init, so do not enforce until * kernel_set_to_readonly is true. */ -static pgprotval_t protect_rodata(unsigned long pfn) +static pgprotval_t protect_rodata(unsigned long spfn, unsigned long epfn) { - unsigned long start_pfn = __pa_symbol(__start_rodata) >> PAGE_SHIFT; - unsigned long end_pfn = __pa_symbol(__end_rodata) >> PAGE_SHIFT; + unsigned long epfn_ro, spfn_ro = PFN_DOWN(__pa_symbol(__start_rodata)); + + /* + * Note: __end_rodata is at page aligned and not inclusive, so + * subtract 1 to get the last enforced PFN in the rodata area. + */ + epfn_ro = PFN_DOWN(__pa_symbol(__end_rodata)) - 1; - if (kernel_set_to_readonly && within(pfn, start_pfn, end_pfn)) + if (kernel_set_to_readonly && overlaps(spfn, epfn, spfn_ro, epfn_ro)) return _PAGE_RW; return 0; } @@ -330,9 +342,12 @@ static pgprotval_t protect_rodata(unsigned long pfn) * * This does not cover __inittext since that is gone after boot. */ -static pgprotval_t protect_kernel_text(unsigned long address) +static pgprotval_t protect_kernel_text(unsigned long start, unsigned long end) { - if (within(address, (unsigned long)_text, (unsigned long)_etext)) + unsigned long t_end = (unsigned long)_etext - 1; + unsigned long t_start = (unsigned long)_text; + + if (overlaps(start, end, t_start, t_end)) return _PAGE_NX; return 0; } @@ -347,13 +362,14 @@ static pgprotval_t protect_kernel_text(unsigned long address) * This will preserve the large page mappings for kernel text/data at no * extra cost. */ -static pgprotval_t protect_kernel_text_ro(unsigned long address) +static pgprotval_t protect_kernel_text_ro(unsigned long start, + unsigned long end) { - unsigned long end = (unsigned long)__end_rodata_hpage_align; - unsigned long start = (unsigned long)_text; + unsigned long t_end = (unsigned long)__end_rodata_hpage_align - 1; + unsigned long t_start = (unsigned long)_text; unsigned int level; - if (!kernel_set_to_readonly || !within(address, start, end)) + if (!kernel_set_to_readonly || !overlaps(start, end, t_start, t_end)) return 0; /* * Don't enforce the !RW mapping for the kernel text mapping, if @@ -367,12 +383,13 @@ static pgprotval_t protect_kernel_text_ro(unsigned long address) * so the protections for kernel text and identity mappings have to * be the same. */ - if (lookup_address(address, &level) && (level != PG_LEVEL_4K)) + if (lookup_address(start, &level) && (level != PG_LEVEL_4K)) return _PAGE_RW; return 0; } #else -static pgprotval_t protect_kernel_text_ro(unsigned long address) +static pgprotval_t protect_kernel_text_ro(unsigned long start, + unsigned long end) { return 0; } @@ -384,18 +401,20 @@ static pgprotval_t protect_kernel_text_ro(unsigned long address) * right (again, ioremap() on BIOS memory is not uncommon) so this function * checks and fixes these known static required protection bits. */ -static inline pgprot_t static_protections(pgprot_t prot, unsigned long address, - unsigned long pfn) +static inline pgprot_t static_protections(pgprot_t prot, unsigned long start, + unsigned long pfn, unsigned long npg) { pgprotval_t forbidden; + unsigned long end; /* Operate on the virtual address */ - forbidden = protect_kernel_text(address); - forbidden |= protect_kernel_text_ro(address); + end = start + npg * PAGE_SIZE - 1; + forbidden = protect_kernel_text(start, end); + forbidden |= protect_kernel_text_ro(start, end); /* Check the PFN directly */ - forbidden |= protect_pci_bios(pfn); - forbidden |= protect_rodata(pfn); + forbidden |= protect_pci_bios(pfn, pfn + npg - 1); + forbidden |= protect_rodata(pfn, pfn + npg - 1); return __pgprot(pgprot_val(prot) & ~forbidden); } @@ -667,10 +686,10 @@ static int __should_split_large_page(pte_t *kpte, unsigned long address, * in it results in a different pgprot than the first one of the * requested range. If yes, then the page needs to be split. */ - new_prot = static_protections(req_prot, address, pfn); + new_prot = static_protections(req_prot, address, pfn, 1); pfn = old_pfn; for (i = 0, addr = lpaddr; i < numpages; i++, addr += PAGE_SIZE, pfn++) { - pgprot_t chk_prot = static_protections(req_prot, addr, pfn); + pgprot_t chk_prot = static_protections(req_prot, addr, pfn, 1); if (pgprot_val(chk_prot) != pgprot_val(new_prot)) return 1; @@ -1280,7 +1299,7 @@ static int __change_page_attr(struct cpa_data *cpa, int primary) pgprot_val(new_prot) &= ~pgprot_val(cpa->mask_clr); pgprot_val(new_prot) |= pgprot_val(cpa->mask_set); - new_prot = static_protections(new_prot, address, pfn); + new_prot = static_protections(new_prot, address, pfn, 1); new_prot = pgprot_clear_protnone_bits(new_prot); From a6713fc0eb719ff88ab3446cf1063eb72550ab28 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Mon, 17 Sep 2018 16:29:11 +0200 Subject: [PATCH 1082/1103] x86/mm/cpa: Add debug mechanism The whole static protection magic is silently fixing up anything which is handed in. That's just wrong. The offending call sites need to be fixed. Add a debug mechanism which emits a warning if a requested mapping needs to be fixed up. The DETECT debug mechanism is really not meant to be enabled except for developers, so limit the output hard to the protection fixups. Signed-off-by: Thomas Gleixner Reviewed-by: Dave Hansen Cc: Peter Zijlstra Cc: Bin Yang Cc: Mark Gross Link: https://lkml.kernel.org/r/20180917143546.078998733@linutronix.de Cc: Zhang Ning Signed-off-by: Lili Li --- arch/x86/mm/pageattr.c | 61 +++++++++++++++++++++++++++++++++++------- 1 file changed, 52 insertions(+), 9 deletions(-) diff --git a/arch/x86/mm/pageattr.c b/arch/x86/mm/pageattr.c index 61ff27848452..1f2519055b30 100644 --- a/arch/x86/mm/pageattr.c +++ b/arch/x86/mm/pageattr.c @@ -42,6 +42,13 @@ struct cpa_data { struct page **pages; }; +enum cpa_warn { + CPA_PROTECT, + CPA_DETECT, +}; + +static const int cpa_warn_level = CPA_PROTECT; + /* * Serialize cpa() (for !DEBUG_PAGEALLOC which uses large identity mappings) * using cpa_lock. So that we don't allow any other cpu, with stale large tlb @@ -395,6 +402,28 @@ static pgprotval_t protect_kernel_text_ro(unsigned long start, } #endif +static inline bool conflicts(pgprot_t prot, pgprotval_t val) +{ + return (pgprot_val(prot) & ~val) != pgprot_val(prot); +} + +static inline void check_conflict(int warnlvl, pgprot_t prot, pgprotval_t val, + unsigned long start, unsigned long end, + unsigned long pfn, const char *txt) +{ + static const char *lvltxt[] = { + [CPA_PROTECT] = "protect", + [CPA_DETECT] = "detect", + }; + + if (warnlvl > cpa_warn_level || !conflicts(prot, val)) + return; + + pr_warn("CPA %8s %10s: 0x%016lx - 0x%016lx PFN %lx req %016llx prevent %016llx\n", + lvltxt[warnlvl], txt, start, end, pfn, (unsigned long long)pgprot_val(prot), + (unsigned long long)val); +} + /* * Certain areas of memory on x86 require very specific protection flags, * for example the BIOS area or kernel text. Callers don't always get this @@ -402,19 +431,31 @@ static pgprotval_t protect_kernel_text_ro(unsigned long start, * checks and fixes these known static required protection bits. */ static inline pgprot_t static_protections(pgprot_t prot, unsigned long start, - unsigned long pfn, unsigned long npg) + unsigned long pfn, unsigned long npg, + int warnlvl) { - pgprotval_t forbidden; + pgprotval_t forbidden, res; unsigned long end; /* Operate on the virtual address */ end = start + npg * PAGE_SIZE - 1; - forbidden = protect_kernel_text(start, end); - forbidden |= protect_kernel_text_ro(start, end); + + res = protect_kernel_text(start, end); + check_conflict(warnlvl, prot, res, start, end, pfn, "Text NX"); + forbidden = res; + + res = protect_kernel_text_ro(start, end); + check_conflict(warnlvl, prot, res, start, end, pfn, "Text RO"); + forbidden |= res; /* Check the PFN directly */ - forbidden |= protect_pci_bios(pfn, pfn + npg - 1); - forbidden |= protect_rodata(pfn, pfn + npg - 1); + res = protect_pci_bios(pfn, pfn + npg - 1); + check_conflict(warnlvl, prot, res, start, end, pfn, "PCIBIOS NX"); + forbidden |= res; + + res = protect_rodata(pfn, pfn + npg - 1); + check_conflict(warnlvl, prot, res, start, end, pfn, "Rodata RO"); + forbidden |= res; return __pgprot(pgprot_val(prot) & ~forbidden); } @@ -686,10 +727,11 @@ static int __should_split_large_page(pte_t *kpte, unsigned long address, * in it results in a different pgprot than the first one of the * requested range. If yes, then the page needs to be split. */ - new_prot = static_protections(req_prot, address, pfn, 1); + new_prot = static_protections(req_prot, address, pfn, 1, CPA_DETECT); pfn = old_pfn; for (i = 0, addr = lpaddr; i < numpages; i++, addr += PAGE_SIZE, pfn++) { - pgprot_t chk_prot = static_protections(req_prot, addr, pfn, 1); + pgprot_t chk_prot = static_protections(req_prot, addr, pfn, 1, + CPA_DETECT); if (pgprot_val(chk_prot) != pgprot_val(new_prot)) return 1; @@ -1299,7 +1341,8 @@ static int __change_page_attr(struct cpa_data *cpa, int primary) pgprot_val(new_prot) &= ~pgprot_val(cpa->mask_clr); pgprot_val(new_prot) |= pgprot_val(cpa->mask_set); - new_prot = static_protections(new_prot, address, pfn, 1); + new_prot = static_protections(new_prot, address, pfn, 1, + CPA_PROTECT); new_prot = pgprot_clear_protnone_bits(new_prot); From 0dbb9a3713dc3bf534fe5cff00837e08ed3c1c0f Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Mon, 17 Sep 2018 16:29:12 +0200 Subject: [PATCH 1083/1103] x86/mm/cpa: Add large page preservation statistics The large page preservation mechanism is just magic and provides no information at all. Add optional statistic output in debugfs so the magic can be evaluated. Defaults is off. Output: 1G pages checked: 2 1G pages sameprot: 0 1G pages preserved: 0 2M pages checked: 540 2M pages sameprot: 466 2M pages preserved: 47 4K pages checked: 800770 4K pages set-checked: 7668 Signed-off-by: Thomas Gleixner Reviewed-by: Dave Hansen Cc: Peter Zijlstra Cc: Bin Yang Cc: Mark Gross Link: https://lkml.kernel.org/r/20180917143546.160867778@linutronix.de Cc: Zhang Ning Signed-off-by: Lili Li --- arch/x86/Kconfig | 8 ++++ arch/x86/mm/pageattr.c | 99 +++++++++++++++++++++++++++++++++++++++++- 2 files changed, 105 insertions(+), 2 deletions(-) diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig index 1a0be022f91d..65728bb1182c 100644 --- a/arch/x86/Kconfig +++ b/arch/x86/Kconfig @@ -1491,6 +1491,14 @@ config X86_DIRECT_GBPAGES supports them), so don't confuse the user by printing that we have them enabled. +config X86_CPA_STATISTICS + bool "Enable statistic for Change Page Attribute" + depends on DEBUG_FS + ---help--- + Expose statistics about the Change Page Attribute mechanims, which + helps to determine the effectivness of preserving large and huge + page mappings when mapping protections are changed. + config ARCH_HAS_MEM_ENCRYPT def_bool y diff --git a/arch/x86/mm/pageattr.c b/arch/x86/mm/pageattr.c index 1f2519055b30..cdf52eb86f3a 100644 --- a/arch/x86/mm/pageattr.c +++ b/arch/x86/mm/pageattr.c @@ -101,6 +101,95 @@ void arch_report_meminfo(struct seq_file *m) static inline void split_page_count(int level) { } #endif +#ifdef CONFIG_X86_CPA_STATISTICS + +static unsigned long cpa_1g_checked; +static unsigned long cpa_1g_sameprot; +static unsigned long cpa_1g_preserved; +static unsigned long cpa_2m_checked; +static unsigned long cpa_2m_sameprot; +static unsigned long cpa_2m_preserved; +static unsigned long cpa_4k_checked; +static unsigned long cpa_4k_install; + +static inline void cpa_inc_1g_checked(void) +{ + cpa_1g_checked++; +} + +static inline void cpa_inc_2m_checked(void) +{ + cpa_2m_checked++; +} + +static inline void cpa_inc_4k_checked(void) +{ + cpa_4k_checked++; +} + +static inline void cpa_inc_4k_install(void) +{ + cpa_4k_install++; +} + +static inline void cpa_inc_lp_sameprot(int level) +{ + if (level == PG_LEVEL_1G) + cpa_1g_sameprot++; + else + cpa_2m_sameprot++; +} + +static inline void cpa_inc_lp_preserved(int level) +{ + if (level == PG_LEVEL_1G) + cpa_1g_preserved++; + else + cpa_2m_preserved++; +} + +static int cpastats_show(struct seq_file *m, void *p) +{ + seq_printf(m, "1G pages checked: %16lu\n", cpa_1g_checked); + seq_printf(m, "1G pages sameprot: %16lu\n", cpa_1g_sameprot); + seq_printf(m, "1G pages preserved: %16lu\n", cpa_1g_preserved); + seq_printf(m, "2M pages checked: %16lu\n", cpa_2m_checked); + seq_printf(m, "2M pages sameprot: %16lu\n", cpa_2m_sameprot); + seq_printf(m, "2M pages preserved: %16lu\n", cpa_2m_preserved); + seq_printf(m, "4K pages checked: %16lu\n", cpa_4k_checked); + seq_printf(m, "4K pages set-checked: %16lu\n", cpa_4k_install); + return 0; +} + +static int cpastats_open(struct inode *inode, struct file *file) +{ + return single_open(file, cpastats_show, NULL); +} + +static const struct file_operations cpastats_fops = { + .open = cpastats_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static int __init cpa_stats_init(void) +{ + debugfs_create_file("cpa_stats", S_IRUSR, arch_debugfs_dir, NULL, + &cpastats_fops); + return 0; +} +late_initcall(cpa_stats_init); +#else +static inline void cpa_inc_1g_checked(void) { } +static inline void cpa_inc_2m_checked(void) { } +static inline void cpa_inc_4k_checked(void) { } +static inline void cpa_inc_4k_install(void) { } +static inline void cpa_inc_lp_sameprot(int level) { } +static inline void cpa_inc_lp_preserved(int level) { } +#endif + + static inline int within(unsigned long addr, unsigned long start, unsigned long end) { @@ -664,10 +753,12 @@ static int __should_split_large_page(pte_t *kpte, unsigned long address, case PG_LEVEL_2M: old_prot = pmd_pgprot(*(pmd_t *)kpte); old_pfn = pmd_pfn(*(pmd_t *)kpte); + cpa_inc_2m_checked(); break; case PG_LEVEL_1G: old_prot = pud_pgprot(*(pud_t *)kpte); old_pfn = pud_pfn(*(pud_t *)kpte); + cpa_inc_1g_checked(); break; default: return -EINVAL; @@ -732,14 +823,16 @@ static int __should_split_large_page(pte_t *kpte, unsigned long address, for (i = 0, addr = lpaddr; i < numpages; i++, addr += PAGE_SIZE, pfn++) { pgprot_t chk_prot = static_protections(req_prot, addr, pfn, 1, CPA_DETECT); - + cpa_inc_4k_checked(); if (pgprot_val(chk_prot) != pgprot_val(new_prot)) return 1; } /* If there are no changes, return. */ - if (pgprot_val(new_prot) == pgprot_val(old_prot)) + if (pgprot_val(new_prot) == pgprot_val(old_prot)) { + cpa_inc_lp_sameprot(level); return 0; + } /* * Verify that the address is aligned and the number of pages @@ -752,6 +845,7 @@ static int __should_split_large_page(pte_t *kpte, unsigned long address, new_pte = pfn_pte(old_pfn, new_prot); __set_pmd_pte(kpte, address, new_pte); cpa->flags |= CPA_FLUSHTLB; + cpa_inc_lp_preserved(level); return 0; } @@ -1341,6 +1435,7 @@ static int __change_page_attr(struct cpa_data *cpa, int primary) pgprot_val(new_prot) &= ~pgprot_val(cpa->mask_clr); pgprot_val(new_prot) |= pgprot_val(cpa->mask_set); + cpa_inc_4k_install(); new_prot = static_protections(new_prot, address, pfn, 1, CPA_PROTECT); From 491f800ab72d3bc81d895c2f42e78fe56262143c Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Mon, 17 Sep 2018 16:29:13 +0200 Subject: [PATCH 1084/1103] x86/mm/cpa: Avoid static protection checks on unmap If the new pgprot has the PRESENT bit cleared, then conflicts vs. RW/NX are completely irrelevant. Before: 1G pages checked: 2 1G pages sameprot: 0 1G pages preserved: 0 2M pages checked: 540 2M pages sameprot: 466 2M pages preserved: 47 4K pages checked: 800770 4K pages set-checked: 7668 After: 1G pages checked: 2 1G pages sameprot: 0 1G pages preserved: 0 2M pages checked: 540 2M pages sameprot: 466 2M pages preserved: 47 4K pages checked: 800709 4K pages set-checked: 7668 Signed-off-by: Thomas Gleixner Reviewed-by: Dave Hansen Cc: Peter Zijlstra Cc: Bin Yang Cc: Mark Gross Link: https://lkml.kernel.org/r/20180917143546.245849757@linutronix.de Cc: Zhang Ning Signed-off-by: Lili Li --- arch/x86/mm/pageattr.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/arch/x86/mm/pageattr.c b/arch/x86/mm/pageattr.c index cdf52eb86f3a..8f9083eb21ac 100644 --- a/arch/x86/mm/pageattr.c +++ b/arch/x86/mm/pageattr.c @@ -526,6 +526,13 @@ static inline pgprot_t static_protections(pgprot_t prot, unsigned long start, pgprotval_t forbidden, res; unsigned long end; + /* + * There is no point in checking RW/NX conflicts when the requested + * mapping is setting the page !PRESENT. + */ + if (!(pgprot_val(prot) & _PAGE_PRESENT)) + return prot; + /* Operate on the virtual address */ end = start + npg * PAGE_SIZE - 1; From 9dbc1786dcdd2ecc54925fa352064c7795595313 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Mon, 17 Sep 2018 16:29:14 +0200 Subject: [PATCH 1085/1103] x86/mm/cpa: Add sanity check for existing mappings With the range check it is possible to do a quick verification that the current mapping is correct vs. the static protection areas. In case a incorrect mapping is detected a warning is emitted and the large page is split up. If the large page is a 2M page, then the split code is forced to check the static protections for the PTE entries to fix up the incorrectness. For 1G pages this can't be done easily because that would require to either find the offending 2M areas before the split or afterwards. For now just warn about that case and revisit it when reported. Signed-off-by: Thomas Gleixner Reviewed-by: Dave Hansen Cc: Peter Zijlstra Cc: Bin Yang Cc: Mark Gross Link: https://lkml.kernel.org/r/20180917143546.331408643@linutronix.de Cc: Zhang Ning Signed-off-by: Lili Li --- arch/x86/mm/pageattr.c | 77 ++++++++++++++++++++++++++++++++++++------ 1 file changed, 67 insertions(+), 10 deletions(-) diff --git a/arch/x86/mm/pageattr.c b/arch/x86/mm/pageattr.c index 8f9083eb21ac..19781b0ab4b4 100644 --- a/arch/x86/mm/pageattr.c +++ b/arch/x86/mm/pageattr.c @@ -37,12 +37,14 @@ struct cpa_data { unsigned long numpages; int flags; unsigned long pfn; - unsigned force_split : 1; + unsigned force_split : 1, + force_static_prot : 1; int curpage; struct page **pages; }; enum cpa_warn { + CPA_CONFLICT, CPA_PROTECT, CPA_DETECT, }; @@ -501,6 +503,7 @@ static inline void check_conflict(int warnlvl, pgprot_t prot, pgprotval_t val, unsigned long pfn, const char *txt) { static const char *lvltxt[] = { + [CPA_CONFLICT] = "conflict", [CPA_PROTECT] = "protect", [CPA_DETECT] = "detect", }; @@ -743,7 +746,7 @@ static int __should_split_large_page(pte_t *kpte, unsigned long address, struct cpa_data *cpa) { unsigned long numpages, pmask, psize, lpaddr, addr, pfn, old_pfn; - pgprot_t old_prot, new_prot, req_prot; + pgprot_t old_prot, new_prot, req_prot, chk_prot; pte_t new_pte, old_pte, *tmp; enum pg_level level; int i; @@ -819,6 +822,23 @@ static int __should_split_large_page(pte_t *kpte, unsigned long address, lpaddr = address & pmask; numpages = psize >> PAGE_SHIFT; + /* + * Sanity check that the existing mapping is correct versus the static + * protections. static_protections() guards against !PRESENT, so no + * extra conditional required here. + */ + chk_prot = static_protections(old_prot, lpaddr, old_pfn, numpages, + CPA_CONFLICT); + + if (WARN_ON_ONCE(pgprot_val(chk_prot) != pgprot_val(old_prot))) { + /* + * Split the large page and tell the split code to + * enforce static protections. + */ + cpa->force_static_prot = 1; + return 1; + } + /* * Make sure that the requested pgprot does not violate the static * protections. Check the full large page whether one of the pages @@ -828,8 +848,8 @@ static int __should_split_large_page(pte_t *kpte, unsigned long address, new_prot = static_protections(req_prot, address, pfn, 1, CPA_DETECT); pfn = old_pfn; for (i = 0, addr = lpaddr; i < numpages; i++, addr += PAGE_SIZE, pfn++) { - pgprot_t chk_prot = static_protections(req_prot, addr, pfn, 1, - CPA_DETECT); + chk_prot = static_protections(req_prot, addr, pfn, 1, + CPA_DETECT); cpa_inc_4k_checked(); if (pgprot_val(chk_prot) != pgprot_val(new_prot)) return 1; @@ -871,15 +891,50 @@ static int should_split_large_page(pte_t *kpte, unsigned long address, return do_split; } +static void split_set_pte(struct cpa_data *cpa, pte_t *pte, unsigned long pfn, + pgprot_t ref_prot, unsigned long address, + unsigned long size) +{ + unsigned int npg = PFN_DOWN(size); + pgprot_t prot; + + /* + * If should_split_large_page() discovered an inconsistent mapping, + * remove the invalid protection in the split mapping. + */ + if (!cpa->force_static_prot) + goto set; + + prot = static_protections(ref_prot, address, pfn, npg, CPA_PROTECT); + + if (pgprot_val(prot) == pgprot_val(ref_prot)) + goto set; + + /* + * If this is splitting a PMD, fix it up. PUD splits cannot be + * fixed trivially as that would require to rescan the newly + * installed PMD mappings after returning from split_large_page() + * so an eventual further split can allocate the necessary PTE + * pages. Warn for now and revisit it in case this actually + * happens. + */ + if (size == PAGE_SIZE) + ref_prot = prot; + else + pr_warn_once("CPA: Cannot fixup static protections for PUD split\n"); +set: + set_pte(pte, pfn_pte(pfn, ref_prot)); +} + static int __split_large_page(struct cpa_data *cpa, pte_t *kpte, unsigned long address, struct page *base) { + unsigned long lpaddr, lpinc, ref_pfn, pfn, pfninc = 1; pte_t *pbase = (pte_t *)page_address(base); - unsigned long ref_pfn, pfn, pfninc = 1; unsigned int i, level; - pte_t *tmp; pgprot_t ref_prot; + pte_t *tmp; spin_lock(&pgd_lock); /* @@ -902,15 +957,17 @@ __split_large_page(struct cpa_data *cpa, pte_t *kpte, unsigned long address, * PAT bit to correct position. */ ref_prot = pgprot_large_2_4k(ref_prot); - ref_pfn = pmd_pfn(*(pmd_t *)kpte); + lpaddr = address & PMD_MASK; + lpinc = PAGE_SIZE; break; case PG_LEVEL_1G: ref_prot = pud_pgprot(*(pud_t *)kpte); ref_pfn = pud_pfn(*(pud_t *)kpte); pfninc = PMD_PAGE_SIZE >> PAGE_SHIFT; - + lpaddr = address & PUD_MASK; + lpinc = PMD_SIZE; /* * Clear the PSE flags if the PRESENT flag is not set * otherwise pmd_present/pmd_huge will return true @@ -931,8 +988,8 @@ __split_large_page(struct cpa_data *cpa, pte_t *kpte, unsigned long address, * Get the target pfn from the original entry: */ pfn = ref_pfn; - for (i = 0; i < PTRS_PER_PTE; i++, pfn += pfninc) - set_pte(&pbase[i], pfn_pte(pfn, ref_prot)); + for (i = 0; i < PTRS_PER_PTE; i++, pfn += pfninc, lpaddr += lpinc) + split_set_pte(cpa, pbase + i, pfn, ref_prot, lpaddr, lpinc); if (virt_addr_valid(address)) { unsigned long pfn = PFN_DOWN(__pa(address)); From 7901913a5c3dbd266552258c940cfc5d0a4883e6 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Mon, 17 Sep 2018 16:29:15 +0200 Subject: [PATCH 1086/1103] x86/mm/cpa: Optimize same protection check When the existing mapping is correct and the new requested page protections are the same as the existing ones, then further checks can be omitted and the large page can be preserved. The slow path 4k wise check will not come up with a different result. Before: 1G pages checked: 2 1G pages sameprot: 0 1G pages preserved: 0 2M pages checked: 540 2M pages sameprot: 466 2M pages preserved: 47 4K pages checked: 800709 4K pages set-checked: 7668 After: 1G pages checked: 2 1G pages sameprot: 0 1G pages preserved: 0 2M pages checked: 538 2M pages sameprot: 466 2M pages preserved: 47 4K pages checked: 560642 4K pages set-checked: 7668 Signed-off-by: Thomas Gleixner Reviewed-by: Dave Hansen Cc: Peter Zijlstra Cc: Bin Yang Cc: Mark Gross Link: https://lkml.kernel.org/r/20180917143546.424477581@linutronix.de Cc: Zhang Ning Signed-off-by: Lili Li --- arch/x86/mm/pageattr.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/arch/x86/mm/pageattr.c b/arch/x86/mm/pageattr.c index 19781b0ab4b4..5160334f9095 100644 --- a/arch/x86/mm/pageattr.c +++ b/arch/x86/mm/pageattr.c @@ -839,6 +839,20 @@ static int __should_split_large_page(pte_t *kpte, unsigned long address, return 1; } + /* + * Optimization: If the requested pgprot is the same as the current + * pgprot, then the large page can be preserved and no updates are + * required independent of alignment and length of the requested + * range. The above already established that the current pgprot is + * correct, which in consequence makes the requested pgprot correct + * as well if it is the same. The static protection scan below will + * not come to a different conclusion. + */ + if (pgprot_val(req_prot) == pgprot_val(old_prot)) { + cpa_inc_lp_sameprot(level); + return 0; + } + /* * Make sure that the requested pgprot does not violate the static * protections. Check the full large page whether one of the pages From e41bcaccaf0bbdf779fbe63dcfacb722f0793335 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Mon, 17 Sep 2018 16:29:16 +0200 Subject: [PATCH 1087/1103] x86/mm/cpa: Do the range check early To avoid excessive 4k wise checks in the common case do a quick check first whether the requested new page protections conflict with a static protection area in the large page. If there is no conflict then the decision whether to preserve or to split the page can be made immediately. If the requested range covers the full large page, preserve it. Otherwise split it up. No point in doing a slow crawl in 4k steps. Before: 1G pages checked: 2 1G pages sameprot: 0 1G pages preserved: 0 2M pages checked: 538 2M pages sameprot: 466 2M pages preserved: 47 4K pages checked: 560642 4K pages set-checked: 7668 After: 1G pages checked: 2 1G pages sameprot: 0 1G pages preserved: 0 2M pages checked: 541 2M pages sameprot: 466 2M pages preserved: 47 4K pages checked: 514 4K pages set-checked: 7668 Signed-off-by: Thomas Gleixner Reviewed-by: Dave Hansen Cc: Peter Zijlstra Cc: Bin Yang Cc: Mark Gross Link: https://lkml.kernel.org/r/20180917143546.507259989@linutronix.de Cc: Zhang Ning Signed-off-by: Lili Li --- arch/x86/mm/pageattr.c | 27 +++++++++++++++++++++++---- 1 file changed, 23 insertions(+), 4 deletions(-) diff --git a/arch/x86/mm/pageattr.c b/arch/x86/mm/pageattr.c index 5160334f9095..bbc5eb5cbc9d 100644 --- a/arch/x86/mm/pageattr.c +++ b/arch/x86/mm/pageattr.c @@ -854,10 +854,28 @@ static int __should_split_large_page(pte_t *kpte, unsigned long address, } /* - * Make sure that the requested pgprot does not violate the static - * protections. Check the full large page whether one of the pages - * in it results in a different pgprot than the first one of the - * requested range. If yes, then the page needs to be split. + * Optimization: Check whether the requested pgprot is conflicting + * with a static protection requirement in the large page. If not, + * then checking whether the requested range is fully covering the + * large page can be done right here. + */ + new_prot = static_protections(req_prot, lpaddr, old_pfn, numpages, + CPA_DETECT); + + if (pgprot_val(req_prot) == pgprot_val(new_prot)) { + if (address != lpaddr || cpa->numpages != numpages) + return 1; + goto setlp; + } + + /* + * Slow path. The full large page check above established that the + * requested pgprot cannot be applied to the full large page due to + * conflicting requirements of static protection regions. It might + * turn out that the whole requested range is covered by the + * modified protection of the first 4k segment at @address. This + * might result in the ability to preserve the large page + * nevertheless. */ new_prot = static_protections(req_prot, address, pfn, 1, CPA_DETECT); pfn = old_pfn; @@ -882,6 +900,7 @@ static int __should_split_large_page(pte_t *kpte, unsigned long address, if (address != lpaddr || cpa->numpages != numpages) return 1; +setlp: /* All checks passed. Update the large page mapping. */ new_pte = pfn_pte(old_pfn, new_prot); __set_pmd_pte(kpte, address, new_pte); From 2d71bae973382a2553852eb763abde1244d652ba Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Mon, 17 Sep 2018 16:29:17 +0200 Subject: [PATCH 1088/1103] x86/mm/cpa: Avoid the 4k pages check completely The extra loop which tries hard to preserve large pages in case of conflicts with static protection regions turns out to be not preserving anything, at least not in the experiments which have been conducted. There might be corner cases in which the code would be able to preserve a large page oaccsionally, but it's really not worth the extra code and the cycles wasted in the common case. Before: 1G pages checked: 2 1G pages sameprot: 0 1G pages preserved: 0 2M pages checked: 541 2M pages sameprot: 466 2M pages preserved: 47 4K pages checked: 514 4K pages set-checked: 7668 After: 1G pages checked: 2 1G pages sameprot: 0 1G pages preserved: 0 2M pages checked: 538 2M pages sameprot: 466 2M pages preserved: 47 4K pages set-checked: 7668 Signed-off-by: Thomas Gleixner Reviewed-by: Dave Hansen Cc: Peter Zijlstra Cc: Bin Yang Cc: Mark Gross Link: https://lkml.kernel.org/r/20180917143546.589642503@linutronix.de Cc: Zhang Ning Signed-off-by: Lili Li --- arch/x86/mm/pageattr.c | 64 +++++++++++------------------------------- 1 file changed, 16 insertions(+), 48 deletions(-) diff --git a/arch/x86/mm/pageattr.c b/arch/x86/mm/pageattr.c index bbc5eb5cbc9d..4e55ded01be5 100644 --- a/arch/x86/mm/pageattr.c +++ b/arch/x86/mm/pageattr.c @@ -111,7 +111,6 @@ static unsigned long cpa_1g_preserved; static unsigned long cpa_2m_checked; static unsigned long cpa_2m_sameprot; static unsigned long cpa_2m_preserved; -static unsigned long cpa_4k_checked; static unsigned long cpa_4k_install; static inline void cpa_inc_1g_checked(void) @@ -124,11 +123,6 @@ static inline void cpa_inc_2m_checked(void) cpa_2m_checked++; } -static inline void cpa_inc_4k_checked(void) -{ - cpa_4k_checked++; -} - static inline void cpa_inc_4k_install(void) { cpa_4k_install++; @@ -158,7 +152,6 @@ static int cpastats_show(struct seq_file *m, void *p) seq_printf(m, "2M pages checked: %16lu\n", cpa_2m_checked); seq_printf(m, "2M pages sameprot: %16lu\n", cpa_2m_sameprot); seq_printf(m, "2M pages preserved: %16lu\n", cpa_2m_preserved); - seq_printf(m, "4K pages checked: %16lu\n", cpa_4k_checked); seq_printf(m, "4K pages set-checked: %16lu\n", cpa_4k_install); return 0; } @@ -185,7 +178,6 @@ late_initcall(cpa_stats_init); #else static inline void cpa_inc_1g_checked(void) { } static inline void cpa_inc_2m_checked(void) { } -static inline void cpa_inc_4k_checked(void) { } static inline void cpa_inc_4k_install(void) { } static inline void cpa_inc_lp_sameprot(int level) { } static inline void cpa_inc_lp_preserved(int level) { } @@ -745,11 +737,10 @@ static pgprot_t pgprot_clear_protnone_bits(pgprot_t prot) static int __should_split_large_page(pte_t *kpte, unsigned long address, struct cpa_data *cpa) { - unsigned long numpages, pmask, psize, lpaddr, addr, pfn, old_pfn; + unsigned long numpages, pmask, psize, lpaddr, pfn, old_pfn; pgprot_t old_prot, new_prot, req_prot, chk_prot; pte_t new_pte, old_pte, *tmp; enum pg_level level; - int i; /* * Check for races, another CPU might have split this page @@ -854,53 +845,30 @@ static int __should_split_large_page(pte_t *kpte, unsigned long address, } /* - * Optimization: Check whether the requested pgprot is conflicting - * with a static protection requirement in the large page. If not, - * then checking whether the requested range is fully covering the - * large page can be done right here. + * If the requested range does not cover the full page, split it up */ - new_prot = static_protections(req_prot, lpaddr, old_pfn, numpages, - CPA_DETECT); - - if (pgprot_val(req_prot) == pgprot_val(new_prot)) { - if (address != lpaddr || cpa->numpages != numpages) - return 1; - goto setlp; - } + if (address != lpaddr || cpa->numpages != numpages) + return 1; /* - * Slow path. The full large page check above established that the - * requested pgprot cannot be applied to the full large page due to - * conflicting requirements of static protection regions. It might - * turn out that the whole requested range is covered by the - * modified protection of the first 4k segment at @address. This - * might result in the ability to preserve the large page - * nevertheless. + * Check whether the requested pgprot is conflicting with a static + * protection requirement in the large page. */ - new_prot = static_protections(req_prot, address, pfn, 1, CPA_DETECT); - pfn = old_pfn; - for (i = 0, addr = lpaddr; i < numpages; i++, addr += PAGE_SIZE, pfn++) { - chk_prot = static_protections(req_prot, addr, pfn, 1, - CPA_DETECT); - cpa_inc_4k_checked(); - if (pgprot_val(chk_prot) != pgprot_val(new_prot)) - return 1; - } - - /* If there are no changes, return. */ - if (pgprot_val(new_prot) == pgprot_val(old_prot)) { - cpa_inc_lp_sameprot(level); - return 0; - } + new_prot = static_protections(req_prot, lpaddr, old_pfn, numpages, + CPA_DETECT); /* - * Verify that the address is aligned and the number of pages - * covers the full page. + * If there is a conflict, split the large page. + * + * There used to be a 4k wise evaluation trying really hard to + * preserve the large pages, but experimentation has shown, that this + * does not help at all. There might be corner cases which would + * preserve one large page occasionally, but it's really not worth the + * extra code and cycles for the common case. */ - if (address != lpaddr || cpa->numpages != numpages) + if (pgprot_val(req_prot) != pgprot_val(new_prot)) return 1; -setlp: /* All checks passed. Update the large page mapping. */ new_pte = pfn_pte(old_pfn, new_prot); __set_pmd_pte(kpte, address, new_pte); From 7a9e49105812d3e0edba194b56a423827871b1d3 Mon Sep 17 00:00:00 2001 From: "G Jaya Kumaran, Vineetha" Date: Thu, 24 May 2018 18:45:28 +0800 Subject: [PATCH 1089/1103] Integration of CBC line discipline kernel module This patch enables the Carrier Board Communication (CBC) kernel module, which is implemented as a Linux kernel line discipline attached to a UART device. The CBC protocol defines a number of different channels which are provided as Linux character device nodes. Change-Id: Ic2a72229df9e9a80e306f2f44da37b1ac8539af3 Signed-off-by: G Jaya Kumaran, Vineetha --- drivers/tty/Kconfig | 3 + drivers/tty/Makefile | 1 + drivers/tty/cbc/Kconfig | 22 + drivers/tty/cbc/Makefile | 14 + drivers/tty/cbc/cbc_core.c | 307 +++++++++ drivers/tty/cbc/cbc_core.h | 44 ++ drivers/tty/cbc/cbc_core_public.h | 28 + drivers/tty/cbc/cbc_device.c | 66 ++ drivers/tty/cbc/cbc_device.h | 107 ++++ drivers/tty/cbc/cbc_device_manager.c | 879 ++++++++++++++++++++++++++ drivers/tty/cbc/cbc_device_manager.h | 66 ++ drivers/tty/cbc/cbc_link_checksum.c | 59 ++ drivers/tty/cbc/cbc_link_checksum.h | 44 ++ drivers/tty/cbc/cbc_link_layer.c | 484 ++++++++++++++ drivers/tty/cbc/cbc_link_layer.h | 38 ++ drivers/tty/cbc/cbc_memory.c | 153 +++++ drivers/tty/cbc/cbc_memory.h | 146 +++++ drivers/tty/cbc/cbc_mux_multiplexer.c | 145 +++++ drivers/tty/cbc/cbc_mux_multiplexer.h | 62 ++ drivers/tty/cbc/cbc_types.h | 137 ++++ include/cbc/cbc-core.h | 24 + include/uapi/linux/cbc/cbc-core.h | 24 + include/uapi/linux/major.h | 2 + include/uapi/linux/tty.h | 5 +- 24 files changed, 2858 insertions(+), 2 deletions(-) create mode 100644 drivers/tty/cbc/Kconfig create mode 100644 drivers/tty/cbc/Makefile create mode 100644 drivers/tty/cbc/cbc_core.c create mode 100644 drivers/tty/cbc/cbc_core.h create mode 100644 drivers/tty/cbc/cbc_core_public.h create mode 100644 drivers/tty/cbc/cbc_device.c create mode 100644 drivers/tty/cbc/cbc_device.h create mode 100644 drivers/tty/cbc/cbc_device_manager.c create mode 100644 drivers/tty/cbc/cbc_device_manager.h create mode 100644 drivers/tty/cbc/cbc_link_checksum.c create mode 100644 drivers/tty/cbc/cbc_link_checksum.h create mode 100644 drivers/tty/cbc/cbc_link_layer.c create mode 100644 drivers/tty/cbc/cbc_link_layer.h create mode 100644 drivers/tty/cbc/cbc_memory.c create mode 100644 drivers/tty/cbc/cbc_memory.h create mode 100644 drivers/tty/cbc/cbc_mux_multiplexer.c create mode 100644 drivers/tty/cbc/cbc_mux_multiplexer.h create mode 100644 drivers/tty/cbc/cbc_types.h create mode 100644 include/cbc/cbc-core.h create mode 100644 include/uapi/linux/cbc/cbc-core.h diff --git a/drivers/tty/Kconfig b/drivers/tty/Kconfig index 0840d27381ea..7210714592ac 100644 --- a/drivers/tty/Kconfig +++ b/drivers/tty/Kconfig @@ -441,4 +441,7 @@ config VCC depends on SUN_LDOMS help Support for Sun logical domain consoles. + +source "drivers/tty/cbc/Kconfig" + endif # TTY diff --git a/drivers/tty/Makefile b/drivers/tty/Makefile index c72cafdf32b4..4ae6744406d4 100644 --- a/drivers/tty/Makefile +++ b/drivers/tty/Makefile @@ -33,5 +33,6 @@ obj-$(CONFIG_PPC_EPAPR_HV_BYTECHAN) += ehv_bytechan.o obj-$(CONFIG_GOLDFISH_TTY) += goldfish.o obj-$(CONFIG_MIPS_EJTAG_FDC_TTY) += mips_ejtag_fdc.o obj-$(CONFIG_VCC) += vcc.o +obj-$(CONFIG_CBC_LDISC) += cbc/ obj-y += ipwireless/ diff --git a/drivers/tty/cbc/Kconfig b/drivers/tty/cbc/Kconfig new file mode 100644 index 000000000000..c9cde7fa43ca --- /dev/null +++ b/drivers/tty/cbc/Kconfig @@ -0,0 +1,22 @@ +# SPDX-License-Identifier: GPL-2.0 +# +# CBC (CarrierBoardCommunication) subsystem configuration +# + +config CBC_LDISC + tristate "CBC (Carrier Board Communication) line discipline" + depends on TTY + help + The CBC driver implements a line discipline supporting + the proprietary CBC (Carrier Board Communication) protocol. + + The CBC protocol is a serial line protocol with multiplexing + intended to be used in automotive IVI platforms for multi- + plexed communication between a vehicle IOC and CPU. It is + designed to transport small data packets (up to 64 bytes) + and features a transport protocol to transport larger data + chunks over a point to point connection. + + When initialised the driver presents a number of channels as + character devices. + diff --git a/drivers/tty/cbc/Makefile b/drivers/tty/cbc/Makefile new file mode 100644 index 000000000000..c0517817f605 --- /dev/null +++ b/drivers/tty/cbc/Makefile @@ -0,0 +1,14 @@ +# SPDX-License-Identifier: GPL-2.0 + + +cbc-ldisc-y := cbc_core.o +cbc-ldisc-y += cbc_device.o +cbc-ldisc-y += cbc_device_manager.o +cbc-ldisc-y += cbc_link_checksum.o +cbc-ldisc-y += cbc_link_layer.o +cbc-ldisc-y += cbc_memory.o +cbc-ldisc-y += cbc_mux_multiplexer.o + +obj-$(CONFIG_CBC_LDISC) += cbc-ldisc.o + +ccflags-y := -O2 -D_FORTIFY_SOURCE=2 -Wformat -Wformat-security -fstack-protector \ No newline at end of file diff --git a/drivers/tty/cbc/cbc_core.c b/drivers/tty/cbc/cbc_core.c new file mode 100644 index 000000000000..d9d15fa224ae --- /dev/null +++ b/drivers/tty/cbc/cbc_core.c @@ -0,0 +1,307 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * CBC line discipline kernel module. + * Handles Carrier Board Communications (CBC) protocol. + * + * Copyright (C) 2018 Intel Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "cbc_core.h" +#include "cbc_types.h" +#include "cbc_device_manager.h" +#include "cbc_link_layer.h" +#include "cbc_mux_multiplexer.h" + +MODULE_AUTHOR("Gerhard Bocksch. gerhard.bocksch@intel.com"); +MODULE_DESCRIPTION("CBC serial protocol demultiplexer"); +MODULE_LICENSE("GPL"); + +static int granularity = 4; +module_param(granularity, int, 0644); +MODULE_PARM_DESC(granularity, + "The granularity of the CBC messages. default: 4"); + +MODULE_ALIAS_LDISC(N_CBCCORE); + +/* Marker to mark the cbc_core as valid. */ +#define CBC_MAGIC 0xAFFEAFFE + +static struct cbc_struct cbc_core = { .magic = CBC_MAGIC }; + +/* + * cbc_core_configure_channel - Set the priority and receive + * callback for a CBC channel. + * @channel_idx: Channel identifier (see cbc_channel_enumeration) + * @priority: Priority for channel. + * @data: Data associated with channel. + * @receive: Receive callback associated with channel. + */ +void cbc_core_configure_channel(u32 const channel_idx, const u8 priority, + void *data, + void (*receive)(void *data, const u16 length, + const u8 * const buffer)) +{ + cbc_mux_configure_data_channel(channel_idx, priority, data, receive); +} +EXPORT_SYMBOL_GPL(cbc_core_configure_channel); + +/* + * cbc_core_send_data - Send to a CBC channel. + * @channel_idx: Channel identifier (see cbc_channel_enumeration) + * @priority: Priority for channel. + * @data: Data associated with channel. + * @receive: Receive function associated with channel. + */ +void cbc_core_send_data(const u32 channel_idx, const u16 length, + const u8 * const buffer) +{ + cbc_manager_transmit_data(channel_idx, length, buffer); +} +EXPORT_SYMBOL_GPL(cbc_core_send_data); + +/* + * cbc_ldisc_open - Open the CBC connection to the IOC. + * @tty: Handle to tty. + * + * This function is called by the tty module when the + * line discipline is requested. It allocates the memory pool and creates the + * CBC devices. + * Called in process context serialized from other ldisc calls. + * + * Return: 0 on success error otherwise. + */ +static int cbc_ldisc_open(struct tty_struct *tty) +{ + struct cbc_struct *cbc; + int err; + + pr_debug("cbc-ldisc open.\n"); + mutex_lock(&cbc_core.ldisc_mutex); + + if (WARN_ON(tty->ops->write == NULL)) { + pr_err("cbc-ldisc open write not supported.\n"); + err = -EOPNOTSUPP; + goto err_exit; + } + + cbc = tty->disc_data; + + err = -EEXIST; + /* First make sure we're not already connected. */ + if (WARN_ON(cbc && cbc->magic == CBC_MAGIC)) { + pr_err( + "cbc-ldisc CBC line discipline already open or CBC magic wrong.\n"); + goto err_exit; + } + + cbc = &cbc_core; + + cbc->tty = tty; + tty->disc_data = cbc; + tty->receive_room = 65536; + + /* Create class cbc-core. This will appear as /sys/class/cbc* */ + cbc->cbc_class = class_create(THIS_MODULE, "cbc"); + + if (WARN_ON(!cbc->cbc_class)) { + pr_err("cbc-ldisc open could not create cbc class.\n"); + goto err_exit; + } + + /* Create memory pool */ + cbc->memory_pool = cbc_memory_pool_create( + CBC_QUEUE_LENGTH * CBC_CHANNEL_MAX_NUMBER); + if (WARN_ON(!cbc->memory_pool)) { + pr_err("failed to create memory pool.\n"); + goto err_exit; + } + + /* Initialise on every open, so we start with sequence-counters at 0.*/ + cbc_link_layer_init(cbc->memory_pool); + + /* Register devices here. */ + err = cbc_register_devices(cbc->cbc_class, cbc->memory_pool); + if (err != 0) { + pr_err("register devices failed\n"); + goto err_exit; + } + + cbc_link_layer_set_frame_granularity(granularity); + + /* tty layer expects 0 on success */ + mutex_unlock(&cbc_core.ldisc_mutex); + return 0; + +err_exit: mutex_unlock(&cbc_core.ldisc_mutex); + return err; +} + +/* + * cbc_ldisc_close - Close down the CBC communication. + * @tty: Handle to tty + * + * Unregister CBC devices and destroy CBC class. + */ +static void cbc_ldisc_close(struct tty_struct *tty) +{ + struct cbc_struct *cbc; + + mutex_lock(&cbc_core.ldisc_mutex); + + cbc = (struct cbc_struct *) tty->disc_data; + pr_debug("cbc-ldisc close\n"); + + if (cbc && cbc->magic == CBC_MAGIC && cbc->tty == tty) { + cbc_unregister_devices(cbc->cbc_class); + class_destroy(cbc->cbc_class); + if (WARN_ON(!cbc_memory_pool_try_free(cbc->memory_pool))) + pr_err("could not free memory pool.\n"); + tty->disc_data = NULL; + cbc->tty = NULL; + } else { + WARN_ON(1); + pr_err("ldisc close with wrong CBC magic.\n"); + } + + mutex_unlock(&cbc_core.ldisc_mutex); +} + +/* + * Close line discipline on ldisc hangup. + * @tty: Handle to tty + */ +static int cbc_ldisc_hangup(struct tty_struct *tty) +{ + cbc_ldisc_close(tty); + return 0; +} + +/* + * cbc_ldisc_receive_buf - .receive call for a line discipline. + * @tty: Handle to tty + * @cp: Received data buffer. + * @fp: Not used + * @count: Amount of data received. + * + * Attempts to read a single CBC frame at a time. Cycles round + * until all data has been processed. + */ +static void cbc_ldisc_receive_buf(struct tty_struct *tty, + const unsigned char *cp, char *fp, int count) +{ + struct cbc_struct *cbc; + u8 accepted_bytes; /* per cbc_serial_receive call */ + unsigned int accepted_bytes_sum = 0; + + mutex_lock(&cbc_core.ldisc_mutex); + cbc = (struct cbc_struct *) tty->disc_data; + + if (cbc && (cbc->magic == CBC_MAGIC) && (cbc->tty == tty)) { + do { + u8 chunksize = 254; + + if ((count - accepted_bytes_sum) < 255) + chunksize = count - accepted_bytes_sum; + accepted_bytes = cbc_core_on_receive_cbc_serial_data( + chunksize, cp + accepted_bytes_sum); + accepted_bytes_sum += accepted_bytes; + cbc_link_layer_rx_handler(); + } while (accepted_bytes_sum != count); + } + mutex_unlock(&cbc_core.ldisc_mutex); +} + +/* + * Called from ldisc when write is possible. Not used. + */ +static void cbc_ldisc_write_wakeup(struct tty_struct *tty) +{ + (void) tty; +} + +/* Called to send messages from channel specific device to the IOC */ +enum cbc_error target_specific_send_cbc_uart_data(u16 length, + const u8 *raw_buffer) +{ + mutex_lock(&cbc_core.ldisc_mutex); + if (cbc_core.tty) { + set_bit(TTY_DO_WRITE_WAKEUP, &cbc_core.tty->flags); + + cbc_core.tty->ops->write(cbc_core.tty, raw_buffer, length); + } + mutex_unlock(&cbc_core.ldisc_mutex); + + return CBC_OK; +} + +static struct tty_ldisc_ops cbc_ldisc = { + .owner = THIS_MODULE, + .name = "cbc-ldisc", + .magic = TTY_LDISC_MAGIC, + .open = cbc_ldisc_open, + .close = cbc_ldisc_close, + .hangup = cbc_ldisc_hangup, + .receive_buf = cbc_ldisc_receive_buf, + .write_wakeup = cbc_ldisc_write_wakeup +}; + +/* + * cbc_init - Module init call. + * Registers this module as TTY line discipline. + */ +static int __init cbc_init(void) +{ + int status; + + pr_debug("cbc-ldisc init\n"); + mutex_init(&cbc_core.ldisc_mutex); + mutex_lock(&cbc_core.ldisc_mutex); + + cbc_kmod_devices_init(); + + /* Fill in line discipline, and register it */ + status = tty_register_ldisc(N_CBCCORE, &cbc_ldisc); + if (status) + pr_err("cbc-ldisc: can't register line discipline\n"); + + mutex_unlock(&cbc_core.ldisc_mutex); + return 0; +} + +/* + * cbc_exit - Module exit call. + * + * De-registers itself as line discipline. + */ +static void __exit cbc_exit(void) +{ + int i; + + mutex_lock(&cbc_core.ldisc_mutex); + + pr_debug("cbc-ldisc Exit\n"); + + i = tty_unregister_ldisc(N_CBCCORE); + if (i) + pr_err("cbc-ldisc: can't unregister ldisc (err %d).\n", i); + + mutex_unlock(&cbc_core.ldisc_mutex); +} + +module_init(cbc_init); +module_exit(cbc_exit); diff --git a/drivers/tty/cbc/cbc_core.h b/drivers/tty/cbc/cbc_core.h new file mode 100644 index 000000000000..71e07a3f7765 --- /dev/null +++ b/drivers/tty/cbc/cbc_core.h @@ -0,0 +1,44 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * CBC line discipline kernel module. + * Handles Carrier Board Communications (CBC) protocol. + * + * Copyright (C) 2018 Intel Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +#ifndef CBC_CORE_MOD_H_ +#define CBC_CORE_MOD_H_ + +#include +#include +#include +#include +#include + +#include "cbc_memory.h" + +#define CBC_IOCTL_MAGIC 0xf4 + +/* + * struct cbc_struct - + * + * @magic: Marker to mark the cbc_core as valid. + * @tty: tty associated with the CBC driver. + * @class: CBC device class + * @memory_pool: Memory pool of CBC buffer allocated for used by CBC driver. + * @ldisc_mutex: Mutex to avoid unloading while accessing the driver + */ +struct cbc_struct { + int magic; + struct tty_struct *tty; + struct class *cbc_class; + struct cbc_memory_pool *memory_pool; + struct mutex ldisc_mutex; +}; + +#endif /* CBC_CORE_MOD_H_ */ diff --git a/drivers/tty/cbc/cbc_core_public.h b/drivers/tty/cbc/cbc_core_public.h new file mode 100644 index 000000000000..85928f6efc68 --- /dev/null +++ b/drivers/tty/cbc/cbc_core_public.h @@ -0,0 +1,28 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * CBC line discipline kernel module. + * Handles Carrier Board Communications (CBC) protocol. + * + * Copyright (C) 2018 Intel Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +#ifndef CBC_CORE_PUBLIC_H +#define CBC_CORE_PUBLIC_H + +#include "cbc_types.h" + +/* CBC version. */ +#define CBC_VERSION_ID 1 + +/* The following function needs to be implemented on CM/IOC. */ +enum cbc_error target_specific_send_cbc_uart_data(u16 length, + const u8 *raw_buffer); + + +#endif /* CBC_CORE_PUBLIC_H */ + diff --git a/drivers/tty/cbc/cbc_device.c b/drivers/tty/cbc/cbc_device.c new file mode 100644 index 000000000000..1933a8527015 --- /dev/null +++ b/drivers/tty/cbc/cbc_device.c @@ -0,0 +1,66 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * CBC line discipline kernel module. + * Handles Carrier Board Communications (CBC) protocol. + * + * Copyright (C) 2018 Intel Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +#include + +#include "cbc_device.h" + + +void cbc_device_init(struct cbc_device_data *cd) +{ + if (cd) + INIT_LIST_HEAD(&cd->open_files_head); +} + +void cbc_file_init(struct cbc_file_data *file) +{ + if (file) { + cbc_buffer_queue_init(&file->queue); + init_waitqueue_head(&file->wq_read); + INIT_LIST_HEAD(&file->list); + } +} + +void cbc_file_enqueue(struct cbc_file_data *fd, struct cbc_buffer *buffer) +{ + if (fd) { + if (cbc_buffer_queue_enqueue(&fd->queue, buffer)) { + cbc_buffer_increment_ref(buffer); + wake_up_interruptible(&fd->wq_read); + } + } +} + +struct cbc_buffer *cbc_file_dequeue(struct cbc_file_data *fd) +{ + struct cbc_buffer *buffer = NULL; + + if (fd) + buffer = cbc_buffer_queue_dequeue(&fd->queue); + + if (buffer && atomic_read(&buffer->refcount) == 0) { + buffer = NULL; + pr_err("cbc-core: De-queueing an already freed buffer\n"); + } + + return buffer; +} + +int cbc_file_queue_empty(struct cbc_file_data *fd) +{ + if (fd) + return (fd->queue.write == fd->queue.read); + + return 1; +} + diff --git a/drivers/tty/cbc/cbc_device.h b/drivers/tty/cbc/cbc_device.h new file mode 100644 index 000000000000..deb0cd922316 --- /dev/null +++ b/drivers/tty/cbc/cbc_device.h @@ -0,0 +1,107 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * CBC line discipline kernel module. + * Handles Carrier Board Communications (CBC) protocol. + * + * Copyright (C) 2018 Intel Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +#ifndef CBC_DEVICE_H_ +#define CBC_DEVICE_H_ + +#include +#include +#include +#include +#include +#include +#include +#include + + +#include "cbc_types.h" +#include "cbc_memory.h" + +enum cbc_device_type { + CBC_DEVICE_TYPE_DEFAULT, + CBC_DEVICE_TYPE_RAW, + CBC_DEVICE_TYPE_HIDDEN, + CBC_DEVICE_TYPE_DEBUG +}; + +/* + * struct cbc_device_data -Data for a single channel e.g. /dev/cbc-pmt. + * @device_name: Device name. + * @device_type: CBC device type. + * See c:type 'enum cbc_device_type '. + * @device: Pointer to device struct. + * @open_files_head: Linked list of open files for this device. + * + * Configuration for a given CBC device. It will be stored in the device private + * data. + */ +struct cbc_device_data { + char *device_name; + enum cbc_device_type device_type; + struct device *device; + struct list_head open_files_head; +}; + +/* + * struct cbc_file_data - Data for a CBC device file . + * @queue: CBC buffer queue for this device. + * @wq_read: wait_queue_head_t fused for waking device on events. + * @cbc_device: Device data for this device. + * @list: list_head. + * + */ +struct cbc_file_data { + struct cbc_buffer_queue queue; + wait_queue_head_t wq_read; + struct cbc_device_data *cbc_device; + struct list_head list; +}; + +/* + * cbc_device_init - Initialise CBC device data + * @cd: pointer to CBC device data. + * + * Initialises device's list_head. + */ +void cbc_device_init(struct cbc_device_data *cd); + +/* + * cbc_file_init - Initialise CBC file data + * @file: pointer to CBC file data. + * + * Initialises device file's CBC queue, wait_queue_head_t and list_head. + */ +void cbc_file_init(struct cbc_file_data *file); + +/* + * cbc_file_enqueue -Add CBC buffer to queue. + * @fd: CBC device file data. + * buffer: Pointer to CBC buffer. + * Increases reference count on buffer. + */ +void cbc_file_enqueue(struct cbc_file_data *fd, struct cbc_buffer *buffer); + +/* + * cbc_file_dequeue - Remove buffer from head of queue. + * @fd: CBC device file data. + * + * Does not decrease reference count. + */ +struct cbc_buffer *cbc_file_dequeue(struct cbc_file_data *fd); + +/* + * cbc_file_queue_empty - Is CBC queue empty? + */ +int cbc_file_queue_empty(struct cbc_file_data *fd); + +#endif /* CBC_DEVICE_H_ */ diff --git a/drivers/tty/cbc/cbc_device_manager.c b/drivers/tty/cbc/cbc_device_manager.c new file mode 100644 index 000000000000..0e74183d9828 --- /dev/null +++ b/drivers/tty/cbc/cbc_device_manager.c @@ -0,0 +1,879 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * CBC line discipline kernel module. + * Handles Carrier Board Communications (CBC) protocol. + * + * Copyright (C) 2018 Intel Corporation + */ + +#include +#include +#include +#include +#include + +#include "cbc_core.h" +#include "cbc_device.h" +#include "cbc_device_manager.h" +#include "cbc_memory.h" +#include "cbc_mux_multiplexer.h" +#include "cbc_types.h" + +static int major; /* Default to dynamic major */ +module_param(major, int, 0); +MODULE_PARM_DESC(major, "Major device number"); + +/* + * Minor start number. + * This has to be 0 to allow a lookup of the device structs in an array. + */ +#define CBC_MINOR 0 +/* Device-name/driver-name when registering the devices in the linux kernel.*/ +#define DEVICE_NAME "cbc-core" + +/* Max number of open files per cbc channel */ +#define MAX_OPEN_FILES 6 + +static void demuxed_receive(void *void_data, struct cbc_buffer *cbc_buffer); + +static int cbc_device_open(struct inode *, struct file *); +static int cbc_device_release(struct inode *, struct file *); +static ssize_t cbc_device_read(struct file *, char __user *, size_t, + loff_t *); +static ssize_t cbc_device_write(struct file *, const char __user *, size_t, + loff_t *); +static unsigned int cbc_device_poll(struct file *file, poll_table *wait); +static long cbc_device_ioctl(struct file *, unsigned int, unsigned long); + +static const struct file_operations cbc_dev_file_operations = { + .owner = THIS_MODULE, + .open = cbc_device_open, + .release = cbc_device_release, + .read = cbc_device_read, + .write = cbc_device_write, + .poll = cbc_device_poll, + .unlocked_ioctl = cbc_device_ioctl +}; + +struct cbc_device_manager { + struct cdev cdev; + struct cbc_device_data channels[CBC_CHANNEL_MAX_NUMBER]; + struct mutex send_lock; + struct cbc_memory_pool *cbc_memory; +}; + +/* Currently, only one CBC per kernel supported.*/ +static struct cbc_device_manager cbc_device_mgr_configuration = { + .channels[CBC_CHANNEL_PMT].device_type = + CBC_DEVICE_TYPE_HIDDEN, + .channels[CBC_CHANNEL_PMT].device_name = + "cbc-pmt", + + .channels[CBC_CHANNEL_LIFECYCLE].device_type = + CBC_DEVICE_TYPE_DEFAULT, + .channels[CBC_CHANNEL_LIFECYCLE].device_name = + "cbc-lifecycle", + + .channels[CBC_CHANNEL_SIGNALS].device_type = + CBC_DEVICE_TYPE_DEFAULT, + .channels[CBC_CHANNEL_SIGNALS].device_name = + "cbc-signals", + + .channels[CBC_CHANNEL_EARLY_SIGNALS].device_type = + CBC_DEVICE_TYPE_DEFAULT, + .channels[CBC_CHANNEL_EARLY_SIGNALS].device_name = + "cbc-early-signals", + + .channels[CBC_CHANNEL_DIAGNOSIS].device_type = + CBC_DEVICE_TYPE_DEFAULT, + .channels[CBC_CHANNEL_DIAGNOSIS].device_name = + "cbc-diagnosis", + + .channels[CBC_CHANNEL_DLT].device_type = + CBC_DEVICE_TYPE_DEFAULT, + .channels[CBC_CHANNEL_DLT].device_name = + "cbc-dlt", + + .channels[CBC_CHANNEL_LINDA].device_type = + CBC_DEVICE_TYPE_HIDDEN, + .channels[CBC_CHANNEL_LINDA].device_name = + "cbc-linda", + + .channels[CBC_CHANNEL_OEM_RAW_CHANNEL_0].device_type = + CBC_DEVICE_TYPE_DEFAULT, + .channels[CBC_CHANNEL_OEM_RAW_CHANNEL_0].device_name = + "cbc-raw0", + + .channels[CBC_CHANNEL_OEM_RAW_CHANNEL_1].device_type = + CBC_DEVICE_TYPE_DEFAULT, + .channels[CBC_CHANNEL_OEM_RAW_CHANNEL_1].device_name = + "cbc-raw1", + + .channels[CBC_CHANNEL_OEM_RAW_CHANNEL_2].device_type = + CBC_DEVICE_TYPE_DEFAULT, + .channels[CBC_CHANNEL_OEM_RAW_CHANNEL_2].device_name = + "cbc-raw2", + + .channels[CBC_CHANNEL_OEM_RAW_CHANNEL_3].device_type = + CBC_DEVICE_TYPE_DEFAULT, + .channels[CBC_CHANNEL_OEM_RAW_CHANNEL_3].device_name = + "cbc-raw3", + + .channels[CBC_CHANNEL_OEM_RAW_CHANNEL_4].device_type = + CBC_DEVICE_TYPE_DEFAULT, + .channels[CBC_CHANNEL_OEM_RAW_CHANNEL_4].device_name = + "cbc-raw4", + + .channels[CBC_CHANNEL_OEM_RAW_CHANNEL_5].device_type = + CBC_DEVICE_TYPE_DEFAULT, + .channels[CBC_CHANNEL_OEM_RAW_CHANNEL_5].device_name = + "cbc-raw5", + + .channels[CBC_CHANNEL_OEM_RAW_CHANNEL_6].device_type = + CBC_DEVICE_TYPE_DEFAULT, + .channels[CBC_CHANNEL_OEM_RAW_CHANNEL_6].device_name = + "cbc-raw6", + + .channels[CBC_CHANNEL_OEM_RAW_CHANNEL_7].device_type = + CBC_DEVICE_TYPE_DEFAULT, + .channels[CBC_CHANNEL_OEM_RAW_CHANNEL_7].device_name = + "cbc-raw7", + + .channels[CBC_CHANNEL_OEM_RAW_CHANNEL_8].device_type = + CBC_DEVICE_TYPE_DEFAULT, + .channels[CBC_CHANNEL_OEM_RAW_CHANNEL_8].device_name = + "cbc-raw8", + + .channels[CBC_CHANNEL_OEM_RAW_CHANNEL_9].device_type = + CBC_DEVICE_TYPE_DEFAULT, + .channels[CBC_CHANNEL_OEM_RAW_CHANNEL_9].device_name = + "cbc-raw9", + + .channels[CBC_CHANNEL_OEM_RAW_CHANNEL_10].device_type = + CBC_DEVICE_TYPE_DEFAULT, + .channels[CBC_CHANNEL_OEM_RAW_CHANNEL_10].device_name = + "cbc-raw10", + + .channels[CBC_CHANNEL_OEM_RAW_CHANNEL_11].device_type = + CBC_DEVICE_TYPE_DEFAULT, + .channels[CBC_CHANNEL_OEM_RAW_CHANNEL_11].device_name = + "cbc-raw11", + + .channels[CBC_CHANNEL_DEBUG_OUT].device_type = + CBC_DEVICE_TYPE_DEBUG, + .channels[CBC_CHANNEL_DEBUG_OUT].device_name = + "cbc-debug-out", + + .channels[CBC_CHANNEL_DEBUG_IN].device_type = + CBC_DEVICE_TYPE_DEBUG, + .channels[CBC_CHANNEL_DEBUG_IN].device_name = + "cbc-debug-in", +}; + +static struct cbc_mux_channel_configuration cbc_mux_config; + +/* + * priority_show - Retrieve device attribute priority. + * @dev: device (i.e /dev/cbc*) + * @attr: Priority attribute + * @buf: Buffer to write to. + * + * Every channel (entry in /dev) has a priority. + * This can be set/read by a ioctl or in the sysfs. + */ +static ssize_t priority_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct cbc_device_data *chn_data = + (struct cbc_device_data *) dev_get_drvdata(dev); + int idx = chn_data - &cbc_device_mgr_configuration.channels[0]; + int prio = cbc_mux_multiplexer_get_priority(idx); + + pr_debug("cbc-core: read priority %i for channel: %i\n", prio, idx); + return scnprintf(buf, PAGE_SIZE, "%i\n", prio); +} + +/* + * priority_store - Store device attribute priority. + * @dev: device (i.e /dev/cbc*) + * @attr: Priority attribute + * @buf: Buffer to write to. + * + * Every channel (entry in /dev) has a priority. + * This can be set/read by a ioctl or in the sysfs. + */ +static ssize_t priority_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct cbc_device_data *chn_data = + (struct cbc_device_data *) dev_get_drvdata(dev); + int idx = chn_data - &cbc_device_mgr_configuration.channels[0]; + u8 tmp = 0; + int res = kstrtou8(buf, 0, &tmp); + + if ((res == 0) && (tmp < 8)) { + pr_debug("cbc-core: write priority %i to channel %i\n", tmp, + idx); + cbc_mux_multiplexer_set_priority(idx, tmp); + } + return count; +} +static DEVICE_ATTR_RW(priority); + +/* + * cbc_device_open - Open CBC char device. Implementation of .open. + * @inode:Pointer to inode object for this device. + * @file: Pointer to file object for this device. + * + * Return 0 if device opened successfully, Linux error code otherwise. + */ +static int cbc_device_open(struct inode *inode, struct file *file) +{ + struct cbc_device_data *device_data = + &cbc_device_mgr_configuration.channels[MINOR( + inode->i_rdev)]; + int ret = 0; + u32 num_open_files = 0; + struct cbc_file_data *file_data = kmalloc(sizeof(struct cbc_file_data), + GFP_KERNEL); + + if (!device_data) + ret = -EIO; + + if (!file_data) + ret = -ENOMEM; + + if (ret == 0) { + pr_debug("cbc_core: device_open: %d.%d %s\n", + MAJOR(inode->i_rdev), MINOR(inode->i_rdev), + device_data->device_name); + + if (MINOR(inode->i_rdev) >= CBC_CHANNEL_MAX_NUMBER) { + pr_err("cbc-core: invalid cbc channel number.\n"); + ret = -ENODEV; + } + } + + if (ret == 0) { + struct list_head *tmp; + + list_for_each(tmp, &device_data->open_files_head) + num_open_files++; + + if (num_open_files > MAX_OPEN_FILES) + ret = -EBUSY; + } + + if (ret == 0) { + cbc_file_init(file_data); + file_data->cbc_device = device_data; + list_add(&file_data->list, &device_data->open_files_head); + file->private_data = file_data; + } else { + kfree(file_data); + } + + return ret; +} + +/* + * cbc_device_release - Release char device. Implementation of .release + * @inode:Pointer to inode object for this device. + * @file: Pointer to file object for this device. + */ +static int cbc_device_release(struct inode *inode, struct file *file) +{ + u32 dev_idx = MINOR(inode->i_rdev); + struct cbc_file_data *file_data = file->private_data; + + if (file_data) { + list_del(&file_data->list); + + pr_debug("cbc-core: device_release: %d.%d %s\n", + MAJOR(inode->i_rdev), dev_idx, + file_data->cbc_device->device_name); + + while (!cbc_file_queue_empty(file_data)) + cbc_buffer_release(cbc_file_dequeue(file_data)); + + kfree(file_data); + file->private_data = NULL; + } + return 0; +} + +/* + * cbc_device_read - CBC device read. Implementation of .read. + * @file: Pointer to file object for this device. + * @user_buffer: Pointer to buffer containing data to be read. + * @length:Length of buffer. + * @offset: Offset into buffer. + */ +static ssize_t cbc_device_read(struct file *file, char __user *user_buffer, + size_t length, loff_t *offset) +{ + struct cbc_file_data *f = (struct cbc_file_data *) file->private_data; + s32 ret = 0; + + if (!f) + ret = -EIO; + + if (ret == 0) { + while (cbc_file_queue_empty(f) && (ret == 0)) { + if (file->f_flags & O_NONBLOCK) { + ret = -EAGAIN; + return ret; + } + ret = wait_event_interruptible(f->wq_read, + !(cbc_file_queue_empty(f))); + if ((ret != 0) && (ret != -ERESTARTSYS)) { + /* + * ERESTARTSYS happens when a file is polled + * while shutting down the ldisc. + * This is not an error. + */ + pr_err("cbc-core: fifo_read: woke up with error %d.\n", + ret); + ret = -EIO; + } + } + } + + if (ret == 0) { + if (!cbc_file_queue_empty(f)) { + struct cbc_buffer *cbc_buffer; + + cbc_buffer = cbc_file_dequeue(f); + + if (cbc_buffer) { + u32 offset = CBC_HEADER_SIZE; + u16 data_length = cbc_buffer->payload_length; + + if (f->cbc_device->device_type == + CBC_DEVICE_TYPE_RAW) { + offset = CBC_HEADER_SIZE + + CBC_RAWHEADER_SIZE; + data_length = data_length - + CBC_RAWHEADER_SIZE; + } else if (f->cbc_device->device_type == + CBC_DEVICE_TYPE_DEBUG) { + offset = 0; + data_length = cbc_buffer->frame_length; + } + + if (data_length <= length) { + ret = copy_to_user( + (void __user *) user_buffer, + &cbc_buffer->data[offset], + data_length); + if (ret == 0) { + ret = data_length; + } else { + pr_err( + "cbc-core: device_read %u bytes copy to user failed.\n", + data_length); + } + } else { + pr_err( + "cbc-core: device_read, buffer too small for %u bytes.\n", + data_length); + ret = -EINVAL; + } + cbc_buffer_release(cbc_buffer); + } else { + pr_err("cbc-core: dequeued a null-buffer.\n"); + } + + } else { + pr_err("cbc-core: queue empty after response to wait.\n"); + } + + } + return ret; +} + +/* + * cbc_device_write - Write data to char device. Implementation of .write. + * @file: Pointer to file object for this device. + * @user_buffer: Pointer to buffer containing data to be read. + * @length:Length of buffer. + * @offset: Offset into buffer. + */ +static ssize_t cbc_device_write(struct file *file, + const char __user *user_buffer, size_t length, loff_t *offset) +{ + int n = 0; + struct cbc_file_data *file_data = + (struct cbc_file_data *) file->private_data; + struct cbc_device_data *chn_data = file_data->cbc_device; + + struct cbc_buffer *cbc_buffer = cbc_memory_pool_get_buffer( + cbc_device_mgr_configuration.cbc_memory); + int ret = 0; + u32 payload_offset = CBC_HEADER_SIZE; + u32 additional_header_size = 0; + + u32 tmp = (u32) length; + + if (!cbc_buffer) { + pr_err("cbc-core: Out of memory.\n"); + ret = -ENOMEM; + } + + if (ret == 0) { + if (chn_data == NULL) { + pr_err("cbc-core: Channel data is NULL.\n"); + ret = -EINVAL; + } + } + + if (ret == 0) { + if (chn_data->device_type == CBC_DEVICE_TYPE_RAW) { + payload_offset = CBC_HEADER_SIZE + CBC_RAWHEADER_SIZE; + additional_header_size = CBC_RAWHEADER_SIZE; + } else if (chn_data->device_type == CBC_DEVICE_TYPE_DEBUG) { + ret = -EINVAL; /* debug channels do not support write */ + } + } + + if (length + payload_offset > CBC_BUFFER_SIZE) { + pr_err( + "cbc-core: Device_write %u bytes not possible,maximum buffer size exceeded.\n", + tmp); + ret = -EINVAL; + } + + if (ret == 0) { + if (user_buffer == NULL) { + pr_err("cbc-core: Device_write buffer is NULL.\n"); + ret = -EINVAL; + } + } + + if (ret == 0) { + if (copy_from_user(&cbc_buffer->data[payload_offset], + (void __user *) user_buffer, length) == 0) { + int idx = chn_data - + &cbc_device_mgr_configuration.channels[0]; + + n = length; + cbc_buffer->payload_length = length + + additional_header_size; + cbc_manager_transmit_buffer(idx, cbc_buffer); + } + } + + cbc_buffer_release(cbc_buffer); + + if (ret == 0) + ret = n; + return ret; +} + +/* + * cbc_mux_configure_data_channel - Configure the specified channel. + * @channel_idx: Channel identifier. + * @data: Data associated with this channel. + * @receive: Data receive function for this channel. + * + * Other kernel modules may wish to use the CBC line discipline. + * They can potentially define their own configurations for the CBC channels. + */ +void cbc_mux_configure_data_channel(u32 const channel_idx, const u8 priority, + void *data, + void (*receive)(void *data, const u16 length, + const u8 * const buffer)) +{ + if (channel_idx < CBC_CHANNEL_MAX_NUMBER) { + struct cbc_mux_channel *list = + &cbc_mux_config.cbc_mux_channel_list[channel_idx]; + list->data = data; + list->priority = priority; + list->buffer_receive = NULL; + list->data_receive = receive; + } +} + + +/* + * cbc_register_devices - Register the CBC channels as Linux character devices. + * @cbc_class: CBC device class. + * @memory: CBC memory pool allocated for CBC buffers. + * + * Return: 0 on success or Linux error code. + */ +int cbc_register_devices(struct class *cbc_class, + struct cbc_memory_pool *memory) +{ + int ret = 0; + int i; + dev_t devid; + + struct cbc_device_manager *cbc = &cbc_device_mgr_configuration; + + cbc->cbc_memory = memory; + + /* Set up the devices after the line discipline is opened. */ + if (major) { + devid = MKDEV(major, 0); + ret = register_chrdev_region(devid, CBC_CHANNEL_MAX_NUMBER, + DEVICE_NAME); + } else { + ret = alloc_chrdev_region(&devid, 0, CBC_CHANNEL_MAX_NUMBER, + DEVICE_NAME); + major = MAJOR(devid); + } + + if (ret < 0) + pr_err("cbc-core: ldisc open register chrdev region failed.\n"); + + if (ret == 0) { + cdev_init(&cbc->cdev, &cbc_dev_file_operations); + cbc->cdev.owner = THIS_MODULE; + cbc->cdev.ops = &cbc_dev_file_operations; + ret = cdev_add(&cbc->cdev, MKDEV(major, CBC_MINOR), + CBC_CHANNEL_MAX_NUMBER); + if (ret < 0) { + unregister_chrdev_region(MKDEV(major, CBC_MINOR), + CBC_CHANNEL_MAX_NUMBER); + pr_err("cbc-core: ldisc open add cdev failed\n"); + } + } + + if (ret == 0) { + for (i = 0; i < CBC_CHANNEL_MAX_NUMBER; i++) { + cbc_device_init(&cbc->channels[i]); + + if (cbc->channels[i].device_type != + CBC_DEVICE_TYPE_HIDDEN) { + /* + * Create the devices. + * These will appear in /sys/class/cbc and + * if udev is running, /dev + */ + cbc->channels[i].device = device_create( + cbc_class, NULL, + MKDEV(major, i), NULL, + cbc->channels[i].device_name, + i); + + /* Add the attribute */ + ret = device_create_file( + cbc->channels[i].device, + &dev_attr_priority); + + /* Set private data to point to the fifo */ + dev_set_drvdata(cbc->channels[i].device, + &cbc->channels[i]); + } else { + cbc->channels[i].device = NULL; + } + } + } + + if (ret != 0) + cbc_unregister_devices(cbc_class); + + return ret; +} + +/* + * cbc_unregister_devices - Remove CBC devices. + * @cbc_class: CBC Device class. + * + * Remove CBC device files and unregisters chrdev region. + */ +void cbc_unregister_devices(struct class *cbc_class) +{ + int i; + struct cbc_device_manager *cbc = &cbc_device_mgr_configuration; + + /* Remove the /dev/cbc* devices */ + cdev_del(&cbc->cdev); + + for (i = 0; i < CBC_CHANNEL_MAX_NUMBER; i++) { + if (cbc->channels[i].device != NULL) { + device_remove_file(cbc->channels[i].device, + &dev_attr_priority); + device_destroy(cbc_class, MKDEV(major, i)); + } + } + + /* + * Also destroys all class attribute files, + * because they are ref. counted. + */ + unregister_chrdev_region(MKDEV(major, CBC_MINOR), + CBC_CHANNEL_MAX_NUMBER); + + cbc->cbc_memory = NULL; +} + +/* + * cbc_manager_transmit_data - Transmit data on specified channel. + * @channel_idx: Channel identifier. + * @length: Length of data. + * @buffer: Pointer to data buffer. + * + * If a CBC buffer is available from the memory pool, and the channel + * is valid, the supplied data is copied into a CBC buffer and transmitted. + */ +void cbc_manager_transmit_data(const u32 channel_idx, const u16 length, + const u8 * const buffer) +{ + struct cbc_buffer *cbc_buffer = cbc_memory_pool_get_buffer( + cbc_device_mgr_configuration.cbc_memory); + struct cbc_device_data *chn_data; + u32 offset = CBC_HEADER_SIZE; + u32 copy_length = length; + + if (channel_idx >= CBC_CHANNEL_MAX_NUMBER) { + pr_err("cbc_mux_transmit_data(): Invalid cbc channel idx.\n"); + return; + } + + chn_data = &cbc_device_mgr_configuration.channels[channel_idx]; + + if (!cbc_buffer) + return; + + if (chn_data->device_type == CBC_DEVICE_TYPE_RAW) + offset = CBC_HEADER_SIZE + CBC_RAWHEADER_SIZE; + + if (length + offset > CBC_MAX_TOTAL_FRAME_SIZE) + copy_length = CBC_MAX_TOTAL_FRAME_SIZE - offset; + + memcpy(&cbc_buffer->data[offset], buffer, copy_length); + + cbc_manager_transmit_buffer(channel_idx, cbc_buffer); + cbc_buffer_release(cbc_buffer); +} + +/* + * cbc_manager_transmit_buffer - Transmits CBC buffer on specified channel. + * @channel_idx: Channel identifier. + * @buffer: CBC buffer to transmit. + */ +void cbc_manager_transmit_buffer(const u32 channel_idx, + struct cbc_buffer *buffer) +{ + if (channel_idx >= CBC_CHANNEL_MAX_NUMBER) { + pr_err("cbc_mux_transmit_data(): Invalid cbc channel idx.\n"); + } else { + enum cbc_error res = CBC_OK; + struct cbc_device_data *chn_data = + &cbc_device_mgr_configuration.channels[channel_idx]; + + mutex_lock(&cbc_device_mgr_configuration.send_lock); + + if (chn_data->device_type == CBC_DEVICE_TYPE_RAW) { + /* + * Room for raw header is already reserved in buffer. + * Calculate raw header data length without raw + * header. + */ + u32 real_payload_size = buffer->payload_length - + CBC_RAWHEADER_SIZE; + + buffer->data[CBC_HEADER_SIZE] = + CBC_RAW_CHANNEL_DIRECT_TRANSPORT; + buffer->data[CBC_HEADER_SIZE + 1] = + (u8) (real_payload_size & 0xFF); + buffer->data[CBC_HEADER_SIZE + 2] = + (u8) ((real_payload_size >> 8) & 0xFFU); + } + + res = cbc_mux_multiplexer_transmit_buffer(channel_idx, buffer); + mutex_unlock(&cbc_device_mgr_configuration.send_lock); + if (res != CBC_OK) + pr_err("Error transmitting frame %u.\n", res); + } + /* Buffer is released in the calling cbc_device_write() */ +} + +/* + * cbc_device_poll - Set up polling based on current status of queue. + * @file: Handle to cbc_device_data. + * @wait: Pointer to poll_table. + * + * Return: Updated poll mask. + */ +static unsigned int cbc_device_poll(struct file *file, poll_table *wait) +{ + struct cbc_file_data *f = (struct cbc_file_data *) file->private_data; + unsigned int mask = 0; + + poll_wait(file, &f->wq_read, wait); + + if (!cbc_file_queue_empty(f)) + mask |= (POLLIN | POLLRDNORM); + + if (!(f->queue.read + CBC_QUEUE_LENGTH == f->queue.write)) + mask |= (POLLOUT | POLLWRNORM); + + return mask; +} + +/* + * cbc_device_ioctl - Handle CBC channel ioctl call. + * @file: Handle to cbc_device_data. + * @cmd: ioctl command. + * @arg: argument associated with the command. + * + * The flag field and priority can get get/set for a CBC device (channel). + * + * Return: 0 if command successfully handled, Linux error otherwise. + */ +static long cbc_device_ioctl(struct file *file, unsigned int cmd, + unsigned long arg) +{ + int tmp; + struct cbc_file_data *file_data = + (struct cbc_file_data *) file->private_data; + struct cbc_device_data *chn_data = file_data->cbc_device; + + int idx = chn_data - &cbc_device_mgr_configuration.channels[0]; + + switch (cmd) { + case CBC_PRIORITY_GET: + tmp = cbc_mux_multiplexer_get_priority(idx); + if (copy_to_user((void __user *) arg, &tmp, sizeof(tmp))) + return -EFAULT; + return 0; + + case CBC_PRIORITY_SET: + if (copy_from_user(&tmp, (void __user *) arg, sizeof(tmp))) + return -EFAULT; + cbc_mux_multiplexer_set_priority(idx, tmp); + return 0; + + default: + return -EINVAL; + } + +} + +/* + * get_default_priority - Get default priority for specified channel. + * @channel_id: channel identifier. + * + * Return: Priority for specified channel. + */ +static u8 get_default_priority(enum cbc_channel_enumeration channel_id) +{ + u8 result = 1; + + switch (channel_id) { + case CBC_CHANNEL_PMT: + case CBC_CHANNEL_LIFECYCLE: + case CBC_CHANNEL_DLT: + case CBC_CHANNEL_LINDA: + result = 6; + break; + + case CBC_CHANNEL_DIAGNOSIS: + result = 2; + break; + + default: + result = 3; + break; + } + return result; +} + +/* + * demuxed_receive - Handle a CBC buffer received over UART. + * @void_data: CBC device data. + * @cbc_buffer: CBC buffer received over UART. + * + * Checks if there is valid data. Determines the frame type and + * adds to buffer queue. + */ +static void demuxed_receive(void *void_data, struct cbc_buffer *cbc_buffer) +{ + struct cbc_device_data *device_data = + (struct cbc_device_data *) void_data; + struct list_head *current_item; + struct cbc_file_data *current_file_data; + + if (device_data && cbc_buffer + && cbc_buffer->frame_length > + CBC_HEADER_SIZE + CBC_CHECKSUM_SIZE) { + /* Payload_length includes raw_header */ + u16 payload_length = cbc_buffer->frame_length - + (CBC_HEADER_SIZE + CBC_CHECKSUM_SIZE); + + if (device_data->device_type == CBC_DEVICE_TYPE_RAW) { + if (cbc_buffer->frame_length > + (CBC_HEADER_SIZE + CBC_RAWHEADER_SIZE + + CBC_CHECKSUM_SIZE)) { + u16 raw_length; + + raw_length = cbc_buffer->data[4]; + raw_length |= (cbc_buffer->data[5] << 8); + + if (raw_length + CBC_RAWHEADER_SIZE > + payload_length) { + pr_err( + "raw length (%i) is longer than payload length (%i)\n", + raw_length, payload_length); + /* Payload_length already set + * to max value + */ + } else { + payload_length = raw_length + + CBC_RAWHEADER_SIZE; + } + } else { + pr_err("cbc-core: Frame to short for a raw frame\n"); + } + cbc_buffer->payload_length = payload_length; + } else if (device_data->device_type == + CBC_DEVICE_TYPE_DEFAULT) { + cbc_buffer->payload_length = payload_length; + } + /* else, do not touch payload_length in a debug-channel */ + + /* Enqueue */ + for (current_item = device_data->open_files_head.next + ; current_item != &device_data->open_files_head; current_item = + current_item->next) { + + current_file_data = list_entry(current_item, + struct cbc_file_data, list); + /* File_enqueue increases ref. count. */ + cbc_file_enqueue(current_file_data, cbc_buffer); + } + } else { + pr_err("cbc-core: (<- IOC) dev_receive data is null\n"); + } +} + +/* + * cbc_kmod_devices_init - Configure CBC multiplexer. + * + * Configures multiplexer channel list and configures the multiplexer using + * this list. Initialises mutex lock for multiplexer. + */ +void cbc_kmod_devices_init(void) +{ + /* + * Set up the multiplexer channel list and use it to configure the + * multiplexer. + */ + u32 i = 0; + + for (; i < CBC_CHANNEL_MAX_NUMBER; i++) { + cbc_mux_config.cbc_mux_channel_list[i].buffer_receive = + demuxed_receive; + cbc_mux_config.cbc_mux_channel_list[i].data_receive = NULL; + cbc_mux_config.cbc_mux_channel_list[i].data = + &cbc_device_mgr_configuration.channels[i]; + cbc_mux_config.cbc_mux_channel_list[i].priority = + get_default_priority(i); + } + + cbc_mux_multiplexer_setup(&cbc_mux_config); + mutex_init(&cbc_device_mgr_configuration.send_lock); +} diff --git a/drivers/tty/cbc/cbc_device_manager.h b/drivers/tty/cbc/cbc_device_manager.h new file mode 100644 index 000000000000..a6f9b45da1aa --- /dev/null +++ b/drivers/tty/cbc/cbc_device_manager.h @@ -0,0 +1,66 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * CBC line discipline kernel module. + * Handles Carrier Board Communications (CBC) protocol. + * + * Copyright (C) 2018 Intel Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +#ifndef CBC_DEVICE_MANAGER_H +#define CBC_DEVICE_MANAGER_H + +#include "cbc_memory.h" + +#include +#include + +void cbc_kmod_devices_init(void); + +int cbc_register_devices(struct class *cbc_class, + struct cbc_memory_pool *memory); + +void cbc_unregister_devices(struct class *cbc_class); + +/* + * cbc_mux_configure_data_channel - Configure channels + * @channel_idx: Channel identifier (see cbc_channel_enumeration) + * @priority: Priority for this channel + * @data: Channel data + * @receive: Receive data function associated with this channel. + * Channels can only be configured after cbc_kmod_devices_init(). + * This will overwrite the settings for the devices. + * The device will be created anyway, to allow the cbc_socket_server to + * work without a requirement for handling missing devices. + */ +void cbc_mux_configure_data_channel(u32 const channel_idx, const u8 priority, + void *data, + void (*receive)(void *data, const u16 length, + const u8 * const buffer)); + +/* + * cbc_manager_transmit_data - Transmit data to IOC. + * @channel_idx: Channel identifier (see cbc_channel_enumeration) + * @length: Length of data + * @buffer: The data + * + * This is the version provided as a kernel symbol. + */ +void cbc_manager_transmit_data(const u32 channel_idx, const u16 length, + const u8 * const buffer); + +/* + * cbc_manager_transmit_data - Transmit data to IOC. + * @channel_idx: Channel identifier (see cbc_channel_enumeration) + * @buffer: The data + * + * This is the internal version, without memcpy. + */ +void cbc_manager_transmit_buffer(const u32 channel_idx, + struct cbc_buffer *buffer); + +#endif /* CBC_DEVICE_MANAGER_H */ diff --git a/drivers/tty/cbc/cbc_link_checksum.c b/drivers/tty/cbc/cbc_link_checksum.c new file mode 100644 index 000000000000..69f0ca74c3c8 --- /dev/null +++ b/drivers/tty/cbc/cbc_link_checksum.c @@ -0,0 +1,59 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * CBC line discipline kernel module. + * Handles Carrier Board Communications (CBC) protocol. + * + * Copyright (C) 2018 Intel Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +#include "cbc_types.h" +#include "cbc_link_checksum.h" + +enum cbc_error cbc_checksum_calculate(u8 length, + u8 const * const payload_data, u8 *checksum) +{ + u8 result = 0; /* Holds result of calculation */ + u8 counter = 0; + + /* Parameter validation */ + if (length == 0U) + return CBC_ERROR_PARAMETER_INCORRECT; + + if ((payload_data == NULL) || (checksum == NULL)) + return CBC_ERROR_NULL_POINTER_SUPPLIED; + + /* Perform calculation */ + do { + result += (u8) ((0x100 - *(payload_data + counter++)) & 0xFFU); + } while (counter != length); + + *checksum = result; + + return CBC_OK; +} + +enum cbc_error cbc_checksum_check(u8 length, u8 const * const payload_data, + u8 checksum, u8 *expected_checksum) +{ + u8 calculated_checksum = 0U; + enum cbc_error result = CBC_OK; + + enum cbc_error calc_result = cbc_checksum_calculate(length, + payload_data, &calculated_checksum); + + if ((calc_result == CBC_OK) && + (checksum == calculated_checksum)) + result = CBC_OK; + else + result = CBC_ERROR_CHECKSUM_MISMATCH; + + if (expected_checksum != NULL) + *expected_checksum = calculated_checksum; + + return result; +} diff --git a/drivers/tty/cbc/cbc_link_checksum.h b/drivers/tty/cbc/cbc_link_checksum.h new file mode 100644 index 000000000000..9f8f50f2cbd7 --- /dev/null +++ b/drivers/tty/cbc/cbc_link_checksum.h @@ -0,0 +1,44 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * CBC line discipline kernel module. + * Handles Carrier Board Communications (CBC) protocol. + * + * Copyright (C) 2018 Intel Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef _CBC_LINK_CHECKSUM_H_ +#define _CBC_LINK_CHECKSUM_H_ + +#include "cbc_types.h" + +/* + * cbc_checksum_calculate - Calculate checksum. + * @length: data length + * @payload_data:The data buffer. + * @checksum: Pointer to checksum. + * + * Based on summation of inverted individual byte values. + * + * Return: cbc_error if checksum cannot be generated. + */ + +enum cbc_error cbc_checksum_calculate(u8 length, + u8 const * const payload_data, u8 *checksum); + +/* + * Check checksum is valid for current data. + * @length: data length + * @payload_data: The data buffer. + * @checksum: Checksum value to check + * @expected_checksum: Expected checksum. + * + * Return: cbc_error if checksum is invalid. + */ +enum cbc_error cbc_checksum_check(u8 length, u8 const * const payload_data, + u8 checksum, u8 *expected_checksum); + +#endif /*_CBC_LINK_CHECKSUM_H_ */ diff --git a/drivers/tty/cbc/cbc_link_layer.c b/drivers/tty/cbc/cbc_link_layer.c new file mode 100644 index 000000000000..a55cd8ce7add --- /dev/null +++ b/drivers/tty/cbc/cbc_link_layer.c @@ -0,0 +1,484 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * CBC line discipline kernel module. + * Handles Carrier Board Communications (CBC) protocol. + * + * Copyright (C) 2018 Intel Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +#include +#include + +#include "cbc_core_public.h" +#include "cbc_link_checksum.h" +#include "cbc_link_layer.h" +#include "cbc_memory.h" +#include "cbc_mux_multiplexer.h" + +#define CBC_MAX_RING_BUFFER_SIZE 256 + +/* + * struct cbc_queue_control - Structure holding the queue control data. + * @next_element: Next element in received data buffer. + * @current_element: Current element in received data buffer. + * + * Handles the current (and next) position in the received data circular buffer. + */ +struct cbc_queue_control { + u8 next_element; + u8 current_element; +}; + +static struct cbc_memory_pool *memory_pool; + +static u8 rx_cvh_ring_message[CBC_MAX_RING_BUFFER_SIZE]; +static struct cbc_queue_control cvh_rx_queue_control; +static u8 number_of_bytes_expected; +static u8 number_of_bytes_skipped; +static u8 ignore_all_skipped_bytes = 1; +static u8 last_rx_frame_valid = 1; +static u8 rx_sequence_counter; + +/* Transmitted data queue. */ +static struct cbc_buffer_queue tx_queue; +static u8 tx_sequence_counter; /* Sequence counter value for next tx frame */ +static u8 cbc_frame_granularity; +static struct mutex transmit_frame_mutex; + +static void cbc_link_layer_transmit_frame(void); +static void cbc_link_release_rx_data(u8 bytes_to_free); + +/* + * calculate_total_frame_length - Calculates the total length of a frame. + * @buffer: Pointer to CBC buffer. + * + * Used for outgoing frames. Calculates the total length of a frame + * depending on its payload_length. Total length is stored in + * cbc_buffer->frame_length. + */ +static void calculate_total_frame_length(struct cbc_buffer *buffer); + +/* + * cbc_link_release_rx_data - Release the specified number of bytes from the + * internal rx buffer. + * @bytes_to_free: Number of bytes to be released. + */ +static void cbc_link_release_rx_data(u8 bytes_to_free) +{ + cvh_rx_queue_control.current_element += bytes_to_free; +} + +static void calculate_total_frame_length(struct cbc_buffer *buffer) +{ + u8 frame_length_in_bytes; + + if (!buffer) + return; + + frame_length_in_bytes = buffer->payload_length + CBC_HEADER_SIZE + + CBC_CHECKSUM_SIZE; + + /* Adjust frame_length to granularity */ + if ((frame_length_in_bytes % cbc_frame_granularity) != 0) + frame_length_in_bytes += cbc_frame_granularity - + (frame_length_in_bytes % + cbc_frame_granularity); + + buffer->frame_length = frame_length_in_bytes; +} + +/* + * cbc_link_layer_transmit_frame - Transmits a frame over a UART. + */ +static void cbc_link_layer_transmit_frame(void) +{ + u8 total_len; + u8 checksum = 0U; + s32 mutex_lock_result = -1; + u32 frame_transmission_counter = CBC_MAX_FRAME_TRANSMISSION_NUMBER; + struct cbc_buffer *buffer = NULL; + + mutex_lock_result = mutex_lock_interruptible(&transmit_frame_mutex); + if (mutex_lock_result != 0) { + pr_err("cbc-core: Could not lock the transmit_frame_mutex\n"); + return; + } + + /* If queue is not empty. */ + if (tx_queue.read != tx_queue.write) + buffer = cbc_buffer_queue_dequeue(&tx_queue); + + while (buffer && frame_transmission_counter) { + total_len = buffer->frame_length; + frame_transmission_counter--; + + /* Reset sequence counter bits first */ + buffer->data[1U] &= ~CBC_SEQUENCE_COUNTER_WIDTH_MASK; + + /* Qdd sequence counter */ + buffer->data[1U] |= tx_sequence_counter; + + /* + * Qdd checksum, subtract 1, as the checksum field + * itself cannot be included in calculation + */ + cbc_checksum_calculate(total_len - 1U, buffer->data, + &checksum); + buffer->data[total_len - 1U] = checksum; + + /* Try to send the frame */ + if (target_specific_send_cbc_uart_data(total_len, + buffer->data) != CBC_OK) { + /* Not sent, release anyway */ + pr_debug("cbc-core: Could not send packet.\n"); + + } else { + /* + * Data was transmitted, so increase the + * sequence counter for next frame + */ + tx_sequence_counter = (tx_sequence_counter + 1U) + & CBC_SEQUENCE_COUNTER_WIDTH_MASK; + } /* else */ + cbc_buffer_release(buffer); + buffer = NULL; + + /* If queue is not empty. */ + if (tx_queue.read != tx_queue.write) + buffer = cbc_buffer_queue_dequeue(&tx_queue); + } + + mutex_unlock(&transmit_frame_mutex); +} + +/* + * cbc_link_layer_get_stored_serial_data - Get stored serial data. + * @out_buf:Pointer to buffer populated with serial data. + * @max_length: Maximum amount of data to be retrieved. + * + * Populates out_buf and returns number of bytes read. Stop reading if the + * maximum length is reached. + * + * Return: The amount of data read. + */ +static u8 cbc_link_layer_get_stored_serial_data(u8 *out_buf, + u8 const max_length) +{ + u8 index8 = 0; + u8 curr = cvh_rx_queue_control.current_element; + u8 next = cvh_rx_queue_control.next_element; + + while (((curr + index8) & 0xFFU) != next) { + out_buf[index8] = rx_cvh_ring_message[(curr + index8) & 0xFFU]; + index8++; + /* Avoid memory overflow of target array */ + if (index8 >= max_length) + break; + } + return index8; +} + +/* + * cbc_link_layer_set_frame_granularity - Set the CBC frame granularity. + * @granularity: Supported values are 4, 8, 16 and 32 bytes. + * + * Return: CBC error, OK or incorrect parameter (if invalid granularity + * supplied). + */ +enum cbc_error cbc_link_layer_set_frame_granularity(u8 granularity) +{ + if ((granularity == 4) || (granularity == 8) || (granularity == 16) || + (granularity == 32U)) { + cbc_frame_granularity = granularity; + return CBC_OK; + } else { + return CBC_ERROR_PARAMETER_INCORRECT; + } /* else */ +} + +/* + * cbc_link_layer_init - Initialize link layer. + * + * This function shall be called once during startup. + * It shall be the first function of this file to be called. + */ +void cbc_link_layer_init(struct cbc_memory_pool *memory) +{ + cvh_rx_queue_control.next_element = 0; + cvh_rx_queue_control.current_element = 0; + + memory_pool = memory; + + number_of_bytes_expected = 0; + number_of_bytes_skipped = 0; + ignore_all_skipped_bytes = 1; + last_rx_frame_valid = 1; + rx_sequence_counter = 0; + + cbc_buffer_queue_init(&tx_queue); + tx_sequence_counter = 0; + + cbc_frame_granularity = 4; + + mutex_init(&transmit_frame_mutex); +} + +/* + * cbc_core_on_receive_cbc_serial_data - Called on reception of data on UART. + * @length: Maximum size of data to retrieve in one go. + * @rx_buf: Pointer to buffer to populate. + * + * This function is called on reception of serial data. It extracts single CBC + * frames from the received data. If incomplete frames are received, it waits + * for more data. Buffers are added to a circular buffer rx_cvh_ring_message. + * + * Return number of bytes retrieved. + */ +u8 cbc_core_on_receive_cbc_serial_data(u8 length, const u8 *rx_buf) +{ + u8 number_of_bytes_accepted = 0; + u8 next_try_element = 0; + + while (length != 0) { + next_try_element = + (u8) ((cvh_rx_queue_control.next_element + 1U) + % CBC_MAX_RING_BUFFER_SIZE); + if (next_try_element != cvh_rx_queue_control.current_element) { + rx_cvh_ring_message[cvh_rx_queue_control.next_element] = + *rx_buf; + + cvh_rx_queue_control.next_element = next_try_element; + rx_buf++; /* next byte */ + length--; + number_of_bytes_accepted++; + } else { + /* Buffer is full, do not store additional bytes */ + return number_of_bytes_accepted; + } /* else */ + } /* while */ + return number_of_bytes_accepted; +} + +static void _cbc_link_layer_checksum(u8 *rx_cvh_frame, u8 frame_length) +{ + u8 expected_checksum = 0U; + u8 checksum = 0U; + + checksum = rx_cvh_frame[frame_length - 1U]; + /* Check checksum is valid. */ + if (cbc_checksum_check(frame_length - 1U, + rx_cvh_frame, checksum, + &expected_checksum) + != CBC_OK) { + pr_err("cbc-core: Received CBC frame contains an invalid checksum\n"); + pr_err("cbc-core: found 0x%x expected 0x%x. frame discarded (length: %i), try to realign.\n", + checksum, + expected_checksum, + frame_length); + cbc_link_release_rx_data(1U); + number_of_bytes_skipped = 1; + last_rx_frame_valid = 0; + } else { + /* check the sequence counter */ + if ((rx_cvh_frame[1] + & CBC_SEQUENCE_COUNTER_WIDTH_MASK) + != rx_sequence_counter) { + pr_err("cbc-core: Found unexpected Rx sequence counter %i, expected %i\n", + rx_cvh_frame[1] + & 0x3, + rx_sequence_counter); + + /* + * Reset the sequence counter + * to the received value. + * + */ + rx_sequence_counter = + rx_cvh_frame[1] + & CBC_SEQUENCE_COUNTER_WIDTH_MASK; + } + } + +} + +/* + * cbc_link_layer_rx_handler - Process data received on UART. + * + * Processes received serial data and parses for complete frames + */ +void cbc_link_layer_rx_handler(void) +{ + u8 bytes_avail = 0U; + u8 service_layer_frame_length = 0U; + u8 frame_length = 0U; + + struct cbc_buffer *buffer; + u8 *rx_cvh_frame; + + buffer = cbc_memory_pool_get_buffer(memory_pool); + + if (!buffer) { + pr_err("cbc-core: Out of memory.\n"); + return; + } + + bytes_avail = cbc_link_layer_get_stored_serial_data( + buffer->data, CBC_BUFFER_SIZE); + rx_cvh_frame = buffer->data; + + /* Wait for at least one frame (minimum size) */ + while ((bytes_avail >= 8U) && + (bytes_avail >= number_of_bytes_expected)) { + /* Check for start of frame */ + if (rx_cvh_frame[0] == CBC_SOF) { + /* Log skipped bytes if necessary */ + if (number_of_bytes_skipped > 0U) { + if (ignore_all_skipped_bytes == 0U) + pr_err("Skipped %d bytes.\n", + number_of_bytes_skipped); + + number_of_bytes_skipped = 0U; + ignore_all_skipped_bytes = 1U; + } + + service_layer_frame_length = (rx_cvh_frame[1] >> + CBC_FRAME_LENGTH_SHIFT) & + CBC_FRAME_LENGTH_WIDTH_MASK; + + frame_length = (service_layer_frame_length + 2U) * 4U; + + if (frame_length > CBC_MAX_TOTAL_FRAME_SIZE) { + pr_err("cbc: Received frame has illegal length (%u bytes).Frame discarded, try to realign.\n", + frame_length); + cbc_link_release_rx_data(1U); + number_of_bytes_skipped = 1U; + last_rx_frame_valid = 0U; + } else if (bytes_avail >= frame_length) { + /* ok */ + _cbc_link_layer_checksum(rx_cvh_frame, + frame_length); + + /* Increment seq. counter. */ + rx_sequence_counter++; + rx_sequence_counter &= + CBC_SEQUENCE_COUNTER_WIDTH_MASK; + + /* Forward frame to Mux. layer. */ + buffer->frame_length = frame_length; + cbc_mux_multiplexer_process_rx_buffer( + buffer); + cbc_link_release_rx_data(frame_length); + + last_rx_frame_valid = 1; + /* else */ + number_of_bytes_expected = 0; + } else { + /* + * Wait for missing bytes to arrive, + * leave and try again. + */ + number_of_bytes_expected = frame_length; + } /* else */ + } else { + if (!(last_rx_frame_valid && (rx_cvh_frame[0] == + CBC_INTER_FRAME_FILL_BYTE))) + ignore_all_skipped_bytes = 0; + + /* + * No alignment found, + * skip current byte and try next one. + */ + cbc_link_release_rx_data(1U); + number_of_bytes_expected = 0; + ++number_of_bytes_skipped; + } /* else */ + + /* Process rx_buffer increases ref count, + * so always release here. + */ + cbc_buffer_release(buffer); + buffer = cbc_memory_pool_get_buffer(memory_pool); + + if (!buffer) { + pr_err("cbc-core: Out of memory.\n"); + rx_cvh_frame = NULL; + return; + } + bytes_avail = + cbc_link_layer_get_stored_serial_data( + buffer->data, CBC_BUFFER_SIZE); + rx_cvh_frame = buffer->data; + } /* while */ + + cbc_buffer_release(buffer); + +} + +/* + * cbc_link_layer_tx_handler - Triggers pending data transmission. + */ +enum cbc_error cbc_link_layer_tx_handler(void) +{ + cbc_link_layer_transmit_frame(); + + return CBC_OK; +} + +/* + * cbc_link_layer_assemble_buffer_for_transmission - Add frame to queue for + * transmission. + * @mux: CBC channel frame is associated with. + * @priority: Priority for frame. + * @buffer: Frame data. + * + * Generate a CBC frame from supplied data. + * Fills in CBC header details (adds start of frame identifier, frame length + * and channel. Also adds padding. Buffer is added to queue and transmission + * is triggered. + * + * Return CBC error code (CBC_OK if frame assembled successfully). + */ +enum cbc_error cbc_link_layer_assemble_buffer_for_transmission(u8 mux, + u8 priority, struct cbc_buffer *buffer) +{ + u8 frame_length_in_bytes; + enum cbc_error result = CBC_OK; + u32 i; + + if (!buffer) + return CBC_ERROR_NULL_POINTER_SUPPLIED; + + calculate_total_frame_length(buffer); + frame_length_in_bytes = buffer->frame_length; + + /* Fill in padding */ + for (i = buffer->payload_length + CBC_HEADER_SIZE; + i < frame_length_in_bytes; i++) + buffer->data[i] = 0xFF; + + /* Fill in cbc header. */ + buffer->data[0] = CBC_SOF; /* set start of frame byte */ + buffer->data[1] = ((((frame_length_in_bytes - 4U - 1U) / 4U) & + CBC_FRAME_LENGTH_WIDTH_MASK) << + CBC_FRAME_LENGTH_SHIFT); + buffer->data[2] = ((mux & CBC_MULTIPLEXER_WIDTH_MASK) << + CBC_MULTIPLEXER_SHIFT) | + (priority & CBC_PRIORITY_WIDTH_MASK); + + /* + * If transmission is done in a different thread, + * check for queue full first. + */ + cbc_buffer_queue_enqueue(&tx_queue, buffer); + cbc_buffer_increment_ref(buffer); + + /* Trigger transmission */ + cbc_link_layer_transmit_frame(); + + return result; +} diff --git a/drivers/tty/cbc/cbc_link_layer.h b/drivers/tty/cbc/cbc_link_layer.h new file mode 100644 index 000000000000..ba70a99b04e6 --- /dev/null +++ b/drivers/tty/cbc/cbc_link_layer.h @@ -0,0 +1,38 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * CBC line discipline kernel module. + * Handles Carrier Board Communications (CBC) protocol. + * + * Copyright (C) 2018 Intel Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +#ifndef CBC_LINK_LAYER_H_ +#define CBC_LINK_LAYER_H_ + +#include "cbc_types.h" +#include "cbc_memory.h" + +void cbc_link_layer_init(struct cbc_memory_pool *memory); + +enum cbc_error cbc_link_layer_set_frame_granularity(u8 granularity); + +enum cbc_error cbc_link_layer_tx_handler(void); + +void cbc_link_layer_rx_handler(void); + +u8 cbc_core_on_receive_cbc_serial_data(u8 length, const u8 *rx_buf); + +enum cbc_error cbc_link_layer_assemble_frame_for_transmission(u8 mux, + u8 priority, u8 service_frame_length, + u8 const * const raw_buffer); + +enum cbc_error cbc_link_layer_assemble_buffer_for_transmission(u8 mux, + u8 priority, struct cbc_buffer *buffer); + +#endif /*CBC_LINK_LAYER_H_ */ + diff --git a/drivers/tty/cbc/cbc_memory.c b/drivers/tty/cbc/cbc_memory.c new file mode 100644 index 000000000000..69cf65e8901a --- /dev/null +++ b/drivers/tty/cbc/cbc_memory.c @@ -0,0 +1,153 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * CBC line discipline kernel module. + * Handles Carrier Board Communications (CBC) protocol. + * + * Copyright (C) 2018 Intel Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +#include + +#include "cbc_memory.h" + +bool cbc_memory_pool_try_free(struct cbc_memory_pool *pool) +{ + u32 i = 0; + bool allfree = true; + int tmp; + + if (!pool) + return 0; + + mutex_lock(&pool->lock); + + for (i = 0; i < (pool->num_blocks); i++) { + tmp = atomic_read(&pool->pool[i].refcount); + if (tmp > 0) { + pr_err("Buffer %i was not freed. (%i refs)\n", + i, tmp); + allfree = false; + } + } + + mutex_unlock(&pool->lock); + + if (allfree) + kfree(pool); + + return allfree; +} + +struct cbc_memory_pool *cbc_memory_pool_create(const u16 num_blocks) +{ + size_t size; + struct cbc_memory_pool *new_pool; + u32 i = 0; + + /* Check we have a valid queue length before we go any further. */ + BUILD_BUG_ON(CBC_QUEUE_LENGTH & (CBC_QUEUE_LENGTH - 1)); + + size = sizeof(struct cbc_memory_pool) + + (sizeof(struct cbc_buffer) * num_blocks); + + new_pool = kmalloc(size, GFP_KERNEL); + new_pool->num_blocks = num_blocks; + mutex_init(&new_pool->lock); + + for (i = 0; i < num_blocks; i++) + atomic_set(&new_pool->pool[i].refcount, 0); + + return new_pool; +} + +struct cbc_buffer *cbc_memory_pool_get_buffer(struct cbc_memory_pool *pool) +{ + u32 i = 0; + struct cbc_buffer *buffer = NULL; + int tmp; + + if (pool) { + mutex_lock(&pool->lock); + + for (; i < pool->num_blocks; i++) { + tmp = atomic_read(&pool->pool[i].refcount); + if (tmp == 0) { + atomic_inc_and_test(&pool->pool[i].refcount); + buffer = &pool->pool[i]; + buffer->payload_length = 0; + buffer->frame_length = 0; + break; + } + } + mutex_unlock(&pool->lock); + } + return buffer; +} + +void cbc_buffer_release(struct cbc_buffer *buffer) +{ + int tmp; + + if (!buffer) + return; + + atomic_read(&buffer->refcount); + + tmp = atomic_dec_return(&buffer->refcount); + if (tmp == 0) + memset(buffer->data, 0xCD, CBC_BUFFER_SIZE); + +} + +void cbc_buffer_increment_ref(struct cbc_buffer *buffer) +{ + if (buffer) + atomic_inc(&buffer->refcount); +} + +void cbc_buffer_queue_init(struct cbc_buffer_queue *queue) +{ + queue->write = 0; + queue->read = 0; +} + +int cbc_buffer_queue_enqueue(struct cbc_buffer_queue *queue, + struct cbc_buffer *buffer) +{ + if (!queue || !buffer) + return 0; + + if (queue->read + CBC_QUEUE_LENGTH == queue->write) { + pr_err("cbc buffer queue full\n"); + return 0; + } + + queue->queue[queue->write & CBC_QUEUE_BM] = buffer; + queue->write++; + return 1; +} + +struct cbc_buffer *cbc_buffer_queue_dequeue(struct cbc_buffer_queue *queue) +{ + struct cbc_buffer *buffer = NULL; + + if (!queue) + return buffer; + + if (queue->read == queue->write) { + pr_err("cbc buffer queue: dequeue while empty.\n"); + return buffer; + } + + buffer = queue->queue[queue->read & CBC_QUEUE_BM]; + queue->queue[queue->read & CBC_QUEUE_BM] = NULL; + queue->read++; + + return buffer; +} + diff --git a/drivers/tty/cbc/cbc_memory.h b/drivers/tty/cbc/cbc_memory.h new file mode 100644 index 000000000000..d6e34fcd48ca --- /dev/null +++ b/drivers/tty/cbc/cbc_memory.h @@ -0,0 +1,146 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * CBC line discipline kernel module. + * Handles Carrier Board Communications (CBC) protocol. + * + * Copyright (C) 2018 Intel Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +#ifndef CBC_MEMORY_H_ +#define CBC_MEMORY_H_ + +#include +#include +#include + +#include "cbc_types.h" + +#define CBC_BUFFER_SIZE CBC_MAX_TOTAL_FRAME_SIZE + +/* + * struct cbc_buffer - Represents a single CBC frame buffer. + * @frame_length: Total length including headers and checksum. + * @payload_length: Length of payload without fill-bytes, including raw- + * header if present. + * @refcount: Reference count incremented/decremented when queuing + * de-queueing the buffer. + * @data: Contents of buffer. + */ +struct cbc_buffer { + u16 frame_length; + u16 payload_length; + atomic_t refcount; + u8 data[CBC_BUFFER_SIZE]; +}; + +/* + * struct cbc_memory_pool - Memory pool for cbc_buffer. + * @num_blocks: Number of blocks allocated (CBC queue length * maximum number of + * channels. + * lock: Mutex to lock memory operations. + * pool: The actual pool of CBC buffers. + * + * The cbc_memory_pool holds a number of cbc_buffers with reference counting. + */ +struct cbc_memory_pool { + u16 num_blocks; + struct mutex lock; + struct cbc_buffer pool[0]; +}; + +/* CBC queue length has to be a power of 2 */ +#define CBC_QUEUE_LENGTH 16 +#define CBC_QUEUE_BM (CBC_QUEUE_LENGTH - 1) + +/* + * cbc_buffer_queue - Circular buffer for cbc_buffer pointers. + * @queue: The queue of CBC buffer pointers. + * @write: Head of queue. + * @read: Tail of queue. + * + * Reference count handling is not done by this queue. + */ +struct cbc_buffer_queue { + struct cbc_buffer *queue[CBC_QUEUE_LENGTH]; + u8 write; + u8 read; +}; + +/* cbc_memory_pool_create - Create memory pool of CBC buffers. + * @num_blocks: Size of memory pool to create (based on queue size and number + * maximum of channels). + * + * Use kmalloc to create a new cbc_memory_pool with given number of cbc_buffers. + */ +struct cbc_memory_pool *cbc_memory_pool_create(const u16 num_blocks); + +/* + * cbc_memory_pool_try_free - Frees the pool if no buffer is in use. + * @pool: Pointer to memory pool. + * + * Ensure no new buffers are requested while calling this. + * + * Return: True if pool has been freed, false if not. + */ +bool cbc_memory_pool_try_free(struct cbc_memory_pool *pool); + +/* + * cbc_memory_pool_get_buffer - Returns a free CBC buffer if available. + * @pool: Pointer to memory pool. + * + * Return: Pointer to buffer is one is available, null otherwise. + */ +struct cbc_buffer *cbc_memory_pool_get_buffer(struct cbc_memory_pool *pool); + +/* + * cbc_buffer_release - Release CBC buffer (if not is use elsewhere). + * @buffer: Buffer to release. + * + * Decreases the reference count. A reference count of 0 marks this buffer as + * free. + */ +void cbc_buffer_release(struct cbc_buffer *buffer); + +/* + * cbc_buffer_increment_ref - Increases the reference count for a CBC buffer. + * @buffer: Buffer to increment ref count for. + */ +void cbc_buffer_increment_ref(struct cbc_buffer *buffer); + +/* + * cbc_buffer_queue_init - Initializes a cbc_buffer_queue. + * @queue: CBC buffer queue to initialise. + * + * Initialises head and tail. + */ +void cbc_buffer_queue_init(struct cbc_buffer_queue *queue); + +/* + * cbc_buffer_queue_enqueue - Add CBC buffer to a queue. + * @queue: CBC buffer queue. + * @buffer: Buffer to add. + * + * Enqueues a buffer into the queue. If the queue is full, + * the buffer will not be enqueued without any error. + * does not do reference count handling. + */ +int cbc_buffer_queue_enqueue(struct cbc_buffer_queue *queue, + struct cbc_buffer *buffer); + +/* + * cbc_buffer_queue_dequeue - Remove buffer from queue if not in use + * elsewhere. + * @queue: CBC buffer queue + * @buffer: Buffer to dequeue + * + * Dequeues a buffer. If queue is empty, null is returned. + * Does not do reference count handling + */ +struct cbc_buffer *cbc_buffer_queue_dequeue(struct cbc_buffer_queue *queue); + +#endif /* CBC_DEVICE_H_ */ diff --git a/drivers/tty/cbc/cbc_mux_multiplexer.c b/drivers/tty/cbc/cbc_mux_multiplexer.c new file mode 100644 index 000000000000..4439e34f5142 --- /dev/null +++ b/drivers/tty/cbc/cbc_mux_multiplexer.c @@ -0,0 +1,145 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * CBC line discipline kernel module. + * Handles Carrier Board Communications (CBC) protocol. + * + * Copyright (C) 2018 Intel Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +#include "cbc_link_layer.h" +#include "cbc_mux_multiplexer.h" + +static struct cbc_mux_channel_configuration *cbc_mux_configuration; + +void cbc_mux_multiplexer_setup(struct cbc_mux_channel_configuration *config) +{ + cbc_mux_configuration = config; +} + +/* + * cbc_mux_multiplexer_process_rx_buffer - Processes a received buffer. + * @cbc_buffer: CBC buffer to process. + * + * If the frame is valid, and the channel is valid, the contents of the CBC + * buffer are passed to the channel's receive function. + */ +void cbc_mux_multiplexer_process_rx_buffer(struct cbc_buffer *cbc_buffer) +{ + u8 mux_idx = 0U; + struct cbc_mux_channel_configuration *config; + + config = cbc_mux_configuration; + if (!cbc_buffer || cbc_buffer->frame_length < CBC_HEADER_SIZE) + return; + + mux_idx = (u8) ((cbc_buffer->data[2] >> CBC_MULTIPLEXER_SHIFT) + & CBC_MULTIPLEXER_WIDTH_MASK) & 0xFFU; + + if (config) { + struct cbc_mux_channel *channel; + + channel = &config->cbc_mux_channel_list[mux_idx]; + + if (channel) { + if (channel->buffer_receive) { + channel->buffer_receive(channel->data, + cbc_buffer); + } else if (channel->data_receive) { + channel->data_receive(channel->data, + cbc_buffer->payload_length, + &cbc_buffer->data[CBC_HEADER_SIZE]); + cbc_buffer_release(cbc_buffer); + } + } + + /* Send to debug device */ + channel = &config->cbc_mux_channel_list[CBC_CHANNEL_DEBUG_IN]; + if (channel && channel->buffer_receive) + channel->buffer_receive(channel->data, cbc_buffer); + } +} + +/* + * cbc_mux_multiplexer_transmit_buffer - Send a buffer. + * @channel_idx: Channel identifier. + * @buffer: CBC buffer to transmit. + * + * Assembles CBC buffer for transmission. + * + * Return: CBC Error. + */ +enum cbc_error cbc_mux_multiplexer_transmit_buffer( + enum cbc_channel_enumeration channel_idx, + struct cbc_buffer *cbc_buffer) +{ + enum cbc_error result = CBC_OK; + struct cbc_mux_channel *channel; + struct cbc_mux_channel_configuration *config; + + config = cbc_mux_configuration; + + /* + * Transmit will release the buffer, make sure a reference is held. + * until it is enqueued in the debug device. + */ + cbc_buffer_increment_ref(cbc_buffer); + + if (config) { + cbc_link_layer_assemble_buffer_for_transmission( + (u8) channel_idx, + config->cbc_mux_channel_list[channel_idx].priority, + cbc_buffer); + + /* Send to debug device */ + channel = &config->cbc_mux_channel_list[CBC_CHANNEL_DEBUG_OUT]; + if (channel && channel->buffer_receive) + channel->buffer_receive(channel->data, cbc_buffer); + } + + /* Send to debug device */ + channel = &config->cbc_mux_channel_list[CBC_CHANNEL_DEBUG_OUT]; + if (channel && channel->buffer_receive) + channel->buffer_receive(channel->data, cbc_buffer); + + cbc_buffer_release(cbc_buffer); + + return result; +} + +/* + * cbc_mux_multiplexer_set_priority - Set priority for specified channel. + * multiplexer. + * @channel_num: Channel ID. + * @new_priority: UNew priority value. + */ + +void cbc_mux_multiplexer_set_priority(u32 channel_num, u8 new_priority) +{ + struct cbc_mux_channel_configuration *config; + + config = cbc_mux_configuration; + if (config && (channel_num < CBC_CHANNEL_MAX_NUMBER)) + config->cbc_mux_channel_list[channel_num].priority = + new_priority; +} + +/* + * cbc_mux_multiplexer_get_priority - Get priority for specified channel. + * @channel_num: Channel ID. + * + * Return: Priority for this channel. + */ +u8 cbc_mux_multiplexer_get_priority(u32 channel_num) +{ + struct cbc_mux_channel_configuration *config; + + config = cbc_mux_configuration; + if (config && (channel_num < CBC_CHANNEL_MAX_NUMBER)) + return config->cbc_mux_channel_list[channel_num].priority; + return 0; +} diff --git a/drivers/tty/cbc/cbc_mux_multiplexer.h b/drivers/tty/cbc/cbc_mux_multiplexer.h new file mode 100644 index 000000000000..aa24777d6a13 --- /dev/null +++ b/drivers/tty/cbc/cbc_mux_multiplexer.h @@ -0,0 +1,62 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * CBC line discipline kernel module. + * Handles Carrier Board Communications (CBC) protocol. + * + * Copyright (C) 2018 Intel Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +#ifndef CBC_MUX_MULTIPLEXER_H_ +#define CBC_MUX_MULTIPLEXER_H_ + +#include "cbc_types.h" +#include "cbc_memory.h" + +struct cbc_mux_channel { + void (*buffer_receive)(void *data, struct cbc_buffer *cbc_buffer); + void (*data_receive)(void *data, const u16 length, + const u8 * const buffer); + void *data; + u8 priority; +}; + +/* + * Channel configuration struct. + * + * Priorities can be set via ioctl or in sysfs. + * Recommended values for the priorities: + * - CBC_CHANNEL_PMT: 6 + * - CBC_CHANNEL_SYSTEM_CONTROL: 6 + * - CBC_CHANNEL_PROCESS_DATA: 3 + * - CBC_CHANNEL_DIAGNOSTICS: 3 + * - CBC_CHANNEL_SW_TRANSFER: 2 + * - CBC_CHANNEL_DEBUG: 6 + * - CBC_CHANNEL_LINDA: 6 + * - default: 3 + * + */ +struct cbc_mux_channel_configuration { + struct cbc_mux_channel cbc_mux_channel_list[CBC_CHANNEL_MAX_NUMBER]; +}; + +/* Pass configuration to Multiplexer. */ +void cbc_mux_multiplexer_setup(struct cbc_mux_channel_configuration *config); + +/* Process buffer received over UARYT via link layer. */ +void cbc_mux_multiplexer_process_rx_buffer(struct cbc_buffer *cbc_buffer); + +enum cbc_error cbc_mux_multiplexer_transmit_buffer( + enum cbc_channel_enumeration channel, + struct cbc_buffer *cbc_buffer); + +void cbc_mux_multiplexer_set_priority(u32 channel_num, u8 new_priority); + +u8 cbc_mux_multiplexer_get_priority(u32 channel_num); + +#endif /*CBC_MUX_MULTIPLEXER_H_ */ + diff --git a/drivers/tty/cbc/cbc_types.h b/drivers/tty/cbc/cbc_types.h new file mode 100644 index 000000000000..28d6877ff36e --- /dev/null +++ b/drivers/tty/cbc/cbc_types.h @@ -0,0 +1,137 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * CBC line discipline kernel module. + * Handles Carrier Board Communications (CBC) protocol. + * + * Copyright (C) 2018 Intel Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +#ifndef CBC_TYPES_H_ +#define CBC_TYPES_H_ + +#include + + +/* Start of frame indicator. */ +#define CBC_SOF 0x05 +/* Fill byte allowed between CBC frames */ +#define CBC_INTER_FRAME_FILL_BYTE 0xFF + +/*Width bit mask for priority in Mux. layer */ +#define CBC_PRIORITY_WIDTH_MASK GENMASK(2, 0) +/* Width bit mask for multiplexer in Mux layer */ +#define CBC_MULTIPLEXER_WIDTH_MASK GENMASK(4, 0) +/* Shift multiplexer in Mux layer */ +#define CBC_MULTIPLEXER_SHIFT 0x03 + +/* Width bit mask for sequence counter in link layer */ +#define CBC_SEQUENCE_COUNTER_WIDTH_MASK GENMASK(1, 0) +/* Width bit mask for frame length link layer */ +#define CBC_FRAME_LENGTH_WIDTH_MASK GENMASK(4, 0) +/* Maximum possible frame size that can be specified + * with the IAS_CBC_FRAME_LENGTH_WIDTH_MASK + */ +#define CBC_MAX_POSSIBLE_FRAME_SIZE ((CBC_FRAME_LENGTH_WIDTH_MASK + 2) * 4) +/* Frame shift length in link layer */ +#define CBC_FRAME_LENGTH_SHIFT 0x02 + +#define CBC_HEADER_SIZE 0x03 +#define CBC_RAWHEADER_SIZE 0x03 +#define CBC_CHECKSUM_SIZE 1 + +/* + * Maximum size of a CBC frame. This includes the + * IAS_CBC_MAX_SERVICE_FRAME_SIZE, 4 bytes of CBC protocol + * overhead and up to 28 additional bytes for padding to + * 32 byte granularity. + */ +#define CBC_MAX_TOTAL_FRAME_SIZE 96 + +/* Enumeration of supported CBC channels */ +enum cbc_channel_enumeration { + CBC_CHANNEL_PMT = 0, + CBC_CHANNEL_LIFECYCLE = 1, + CBC_CHANNEL_SIGNALS = 2, + CBC_CHANNEL_EARLY_SIGNALS = 3, + CBC_CHANNEL_DIAGNOSIS = 4, + CBC_CHANNEL_DLT = 5, + CBC_CHANNEL_LINDA = 6, + CBC_CHANNEL_OEM_RAW_CHANNEL_0 = 7, + CBC_CHANNEL_OEM_RAW_CHANNEL_1 = 8, + CBC_CHANNEL_OEM_RAW_CHANNEL_2 = 9, + CBC_CHANNEL_OEM_RAW_CHANNEL_3 = 10, + CBC_CHANNEL_OEM_RAW_CHANNEL_4 = 11, + CBC_CHANNEL_OEM_RAW_CHANNEL_5 = 12, + CBC_CHANNEL_OEM_RAW_CHANNEL_6 = 13, + CBC_CHANNEL_OEM_RAW_CHANNEL_7 = 14, + CBC_CHANNEL_OEM_RAW_CHANNEL_8 = 15, + CBC_CHANNEL_OEM_RAW_CHANNEL_9 = 16, + CBC_CHANNEL_OEM_RAW_CHANNEL_10 = 17, + CBC_CHANNEL_OEM_RAW_CHANNEL_11 = 18, + CBC_CHANNEL_DEBUG_OUT = 19, + CBC_CHANNEL_DEBUG_IN = 20, + CBC_CHANNEL_MAX_NUMBER = 21 +}; + +/* + * CBC load monitoring (transmit/receive throughput, errors etc.) can + * be compiled in using the following define. + */ + +/* Enumeration containing available errors */ +enum cbc_error { + CBC_OK = 0, + CBC_ERROR_QUEUE_UNINITIALIZED = 1, + CBC_ERROR_QUEUE_FULL = 2, + CBC_ERROR_QUEUE_EMPTY = 3, + CBC_ERROR_PARAMETER_INCORRECT = 4, + CBC_ERROR_NULL_POINTER_SUPPLIED = 5, + CBC_ERROR_CHECKSUM_MISMATCH = 6, + CBC_ERROR_UNKNOWN_CHANNEL = 7, + CBC_ERROR_OUT_OF_QUEUE_MEMORY = 8, + CBC_ERROR_NO_DATA_IN_QUEUE_MEMORY = 9, + CBC_ERROR_NOT_PROCESSED = 10, + CBC_ERROR_TP_FRAME_NOT_SUPPORTED = 11, + CBC_ERROR_TP_FRAME_NOT_EXPECTED = 12, + CBC_ERROR_BUSY_TRY_AGAIN = 13, + CBC_ERROR_TEC = 14, + CBC_ERROR_UNKNOWN_PERIPHERAL_ID = 15, + CBC_ERROR_HW_NO_WRITE_ACCESS = 16, + CBC_ERROR_HW_NO_READ_ACCESS = 17, + CBC_ERROR_NOT_IMPLEMENTED = 18, + CBC_ERROR_GENERAL_ERROR = 19, + CBC_ERROR_UDP_GET_ADR = 20, + CBC_ERROR_UDP_OPEN_SOCKET = 21, + CBC_ERROR_UDP_CONNECTION_REFUSED = 22, + CBC_ERROR_UDP_CLOSE_INVALID_ID = 23, + CBC_ERROR_UDP_CLOSE_ERR = 24, + CBC_ERROR_DTC_LIST_EMPTY = 25, + CBC_ERROR_INCORRECT_VERSION = 26, + CBC_ERROR_POWER_SUPPLY_ERROR = 27, + CBC_ERROR_PARAMETER_INVALID = 28, + E_CBC_ERROR_NOT_INITIALIZED = 29, + CBC_ERROR_NUMBER_OF_ERRORS = 30, + CBC_ERROR_CUSTOMER_IMPLEMENTATION_MISSING = 31 +}; + +/* + * Maximum number of CBC frames transmitted in each cyclic call of the CBC + * core. + */ +# define CBC_MAX_FRAME_TRANSMISSION_NUMBER 50 + +/* + * Enumeration indicating whether raw channel uses protocol or handles raw + * data. + */ +enum cbc_service_raw_channel_svc { + CBC_RAW_CHANNEL_USE_TRANSPORT_PROTOCOL = 1, + CBC_RAW_CHANNEL_DIRECT_TRANSPORT = 2 +} +; +#endif /* CBC_TYPES_H_ */ diff --git a/include/cbc/cbc-core.h b/include/cbc/cbc-core.h new file mode 100644 index 000000000000..4bcdf52d84eb --- /dev/null +++ b/include/cbc/cbc-core.h @@ -0,0 +1,24 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * CBC line discipline kernel module. + * Handles Carrier Board Communications (CBC) protocol. + * + * Copyright (C) 2018 Intel Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +#ifndef _UAPI_CBC_CORE_H_ +#define _UAPI_CBC_CORE_H_ + +#include + +/* Get/set the priority of this channel */ +#define CBC_PRIORITY_GET _IOR(CBC_IOCTL_MAGIC, 2, int) +#define CBC_PRIORITY_SET _IOW(CBC_IOCTL_MAGIC, 3, int) + + +#endif /* CBC_CORE_MOD_H_ */ diff --git a/include/uapi/linux/cbc/cbc-core.h b/include/uapi/linux/cbc/cbc-core.h new file mode 100644 index 000000000000..4bcdf52d84eb --- /dev/null +++ b/include/uapi/linux/cbc/cbc-core.h @@ -0,0 +1,24 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * CBC line discipline kernel module. + * Handles Carrier Board Communications (CBC) protocol. + * + * Copyright (C) 2018 Intel Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +#ifndef _UAPI_CBC_CORE_H_ +#define _UAPI_CBC_CORE_H_ + +#include + +/* Get/set the priority of this channel */ +#define CBC_PRIORITY_GET _IOR(CBC_IOCTL_MAGIC, 2, int) +#define CBC_PRIORITY_SET _IOW(CBC_IOCTL_MAGIC, 3, int) + + +#endif /* CBC_CORE_MOD_H_ */ diff --git a/include/uapi/linux/major.h b/include/uapi/linux/major.h index 7e5fa8e15c43..e08767b96a45 100644 --- a/include/uapi/linux/major.h +++ b/include/uapi/linux/major.h @@ -135,6 +135,8 @@ #define ATARAID_MAJOR 114 +#define CBC_CORE_MAJOR 115 + #define SCSI_DISK8_MAJOR 128 #define SCSI_DISK9_MAJOR 129 #define SCSI_DISK10_MAJOR 130 diff --git a/include/uapi/linux/tty.h b/include/uapi/linux/tty.h index 376cccf397be..20217970ebfd 100644 --- a/include/uapi/linux/tty.h +++ b/include/uapi/linux/tty.h @@ -6,7 +6,7 @@ * 'tty.h' defines some structures used by tty_io.c and some defines. */ -#define NR_LDISCS 30 +#define NR_LDISCS 31 /* line disciplines */ #define N_TTY 0 @@ -37,6 +37,7 @@ #define N_TRACEROUTER 24 /* Trace data routing for MIPI P1149.7 */ #define N_NCI 25 /* NFC NCI UART */ #define N_SPEAKUP 26 /* Speakup communication with synths */ -#define N_NULL 27 /* Null ldisc used for error handling */ +#define N_CBCCORE 27 /* cbc protocol */ +#define N_NULL 28 /* Null ldisc used for error handling */ #endif /* _UAPI_LINUX_TTY_H */ From c2efd40057ea34b47fd4384bae7ac77ddf716798 Mon Sep 17 00:00:00 2001 From: "G Jaya Kumaran, Vineetha" Date: Fri, 25 May 2018 14:24:11 +0800 Subject: [PATCH 1090/1103] cbc: Avoid rx sequence counter mismatch warnings The receive sequence counter should only be incremented if the checksum passes, to avoid unnecessary spawning of receive sequence mismatch warnings. Change-Id: I8a595dd9365693e257e6f621c4ef26f0834488b9 Signed-off-by: G Jaya Kumaran, Vineetha --- drivers/tty/cbc/cbc_link_layer.c | 30 ++++++++++++++---------------- 1 file changed, 14 insertions(+), 16 deletions(-) diff --git a/drivers/tty/cbc/cbc_link_layer.c b/drivers/tty/cbc/cbc_link_layer.c index a55cd8ce7add..bd747aac250f 100644 --- a/drivers/tty/cbc/cbc_link_layer.c +++ b/drivers/tty/cbc/cbc_link_layer.c @@ -264,7 +264,8 @@ u8 cbc_core_on_receive_cbc_serial_data(u8 length, const u8 *rx_buf) return number_of_bytes_accepted; } -static void _cbc_link_layer_checksum(u8 *rx_cvh_frame, u8 frame_length) +static void _cbc_link_layer_checksum(u8 *rx_cvh_frame, u8 frame_length, + struct cbc_buffer *buffer) { u8 expected_checksum = 0U; u8 checksum = 0U; @@ -302,6 +303,17 @@ static void _cbc_link_layer_checksum(u8 *rx_cvh_frame, u8 frame_length) rx_cvh_frame[1] & CBC_SEQUENCE_COUNTER_WIDTH_MASK; } + + /* Increment seq. counter. */ + rx_sequence_counter++; + rx_sequence_counter &= + CBC_SEQUENCE_COUNTER_WIDTH_MASK; + + /* Forward frame to Mux. layer. */ + buffer->frame_length = frame_length; + cbc_mux_multiplexer_process_rx_buffer(buffer); + cbc_link_release_rx_data(frame_length); + last_rx_frame_valid = 1; } } @@ -361,21 +373,7 @@ void cbc_link_layer_rx_handler(void) } else if (bytes_avail >= frame_length) { /* ok */ _cbc_link_layer_checksum(rx_cvh_frame, - frame_length); - - /* Increment seq. counter. */ - rx_sequence_counter++; - rx_sequence_counter &= - CBC_SEQUENCE_COUNTER_WIDTH_MASK; - - /* Forward frame to Mux. layer. */ - buffer->frame_length = frame_length; - cbc_mux_multiplexer_process_rx_buffer( - buffer); - cbc_link_release_rx_data(frame_length); - - last_rx_frame_valid = 1; - /* else */ + frame_length, buffer); number_of_bytes_expected = 0; } else { /* From ac8f44bb8fc3021a444243357ef2dcf9fd094a34 Mon Sep 17 00:00:00 2001 From: padmarao edapalapati Date: Thu, 6 Sep 2018 10:38:22 +0530 Subject: [PATCH 1091/1103] Fix for cbc kernel driver crash during warm reboot Added a Sync mechanism with spinlocks between demuxed_receive and cbc_device_open, cbc_device_release Change-Id: Id5c71eb59bb390a0b39b7bc66d5536d9eecf607f Signed-off-by: padmarao edapalapati --- drivers/tty/cbc/cbc_device.c | 4 +++- drivers/tty/cbc/cbc_device.h | 3 +++ drivers/tty/cbc/cbc_device_manager.c | 12 ++++++++++++ 3 files changed, 18 insertions(+), 1 deletion(-) diff --git a/drivers/tty/cbc/cbc_device.c b/drivers/tty/cbc/cbc_device.c index 1933a8527015..23728c04dd6e 100644 --- a/drivers/tty/cbc/cbc_device.c +++ b/drivers/tty/cbc/cbc_device.c @@ -18,8 +18,10 @@ void cbc_device_init(struct cbc_device_data *cd) { - if (cd) + if (cd) { + spin_lock_init(&cd->cbc_device_lock); INIT_LIST_HEAD(&cd->open_files_head); + } } void cbc_file_init(struct cbc_file_data *file) diff --git a/drivers/tty/cbc/cbc_device.h b/drivers/tty/cbc/cbc_device.h index deb0cd922316..09c806716557 100644 --- a/drivers/tty/cbc/cbc_device.h +++ b/drivers/tty/cbc/cbc_device.h @@ -22,6 +22,7 @@ #include #include #include +#include #include "cbc_types.h" @@ -49,6 +50,8 @@ struct cbc_device_data { char *device_name; enum cbc_device_type device_type; struct device *device; + /* lock to sync demuxed_receive with cbc_device_release and open */ + spinlock_t cbc_device_lock; struct list_head open_files_head; }; diff --git a/drivers/tty/cbc/cbc_device_manager.c b/drivers/tty/cbc/cbc_device_manager.c index 0e74183d9828..2c8c1cf966ca 100644 --- a/drivers/tty/cbc/cbc_device_manager.c +++ b/drivers/tty/cbc/cbc_device_manager.c @@ -234,6 +234,7 @@ static int cbc_device_open(struct inode *inode, struct file *file) inode->i_rdev)]; int ret = 0; u32 num_open_files = 0; + unsigned long flags; struct cbc_file_data *file_data = kmalloc(sizeof(struct cbc_file_data), GFP_KERNEL); @@ -267,7 +268,9 @@ static int cbc_device_open(struct inode *inode, struct file *file) if (ret == 0) { cbc_file_init(file_data); file_data->cbc_device = device_data; + spin_lock_irqsave(&device_data->cbc_device_lock, flags); list_add(&file_data->list, &device_data->open_files_head); + spin_unlock_irqrestore(&device_data->cbc_device_lock, flags); file->private_data = file_data; } else { kfree(file_data); @@ -285,9 +288,14 @@ static int cbc_device_release(struct inode *inode, struct file *file) { u32 dev_idx = MINOR(inode->i_rdev); struct cbc_file_data *file_data = file->private_data; + unsigned long flags; if (file_data) { + spin_lock_irqsave( + &file_data->cbc_device->cbc_device_lock, flags); list_del(&file_data->list); + spin_unlock_irqrestore( + &file_data->cbc_device->cbc_device_lock, flags); pr_debug("cbc-core: device_release: %d.%d %s\n", MAJOR(inode->i_rdev), dev_idx, @@ -299,6 +307,7 @@ static int cbc_device_release(struct inode *inode, struct file *file) kfree(file_data); file->private_data = NULL; } + return 0; } @@ -796,6 +805,7 @@ static void demuxed_receive(void *void_data, struct cbc_buffer *cbc_buffer) (struct cbc_device_data *) void_data; struct list_head *current_item; struct cbc_file_data *current_file_data; + unsigned long flags; if (device_data && cbc_buffer && cbc_buffer->frame_length > @@ -836,6 +846,7 @@ static void demuxed_receive(void *void_data, struct cbc_buffer *cbc_buffer) /* else, do not touch payload_length in a debug-channel */ /* Enqueue */ + spin_lock_irqsave(&device_data->cbc_device_lock, flags); for (current_item = device_data->open_files_head.next ; current_item != &device_data->open_files_head; current_item = current_item->next) { @@ -845,6 +856,7 @@ static void demuxed_receive(void *void_data, struct cbc_buffer *cbc_buffer) /* File_enqueue increases ref. count. */ cbc_file_enqueue(current_file_data, cbc_buffer); } + spin_unlock_irqrestore(&device_data->cbc_device_lock, flags); } else { pr_err("cbc-core: (<- IOC) dev_receive data is null\n"); } From a75b5b28807fbac016cb4ab84528a3765bd3c6f7 Mon Sep 17 00:00:00 2001 From: "Vyas, Tarun" Date: Wed, 5 Sep 2018 08:14:21 -0700 Subject: [PATCH 1092/1103] staging: Add AVnu based Intel IGB driver This initial commit adds the AVnu based Intel IGB driver for the I210 ethernet controller to drivers/staging. It mirrors and will remain in sync with what's hosted at https://github.com/AVnu/igb_avb/tree/master/kmod which is the github repo for the AVnu IGB driver. It can be built as module with CONFIG_IGB_AVB=m. This is *exclusive* with the in-tree IGB driver located at drivers/ net/ethernet/intel/igb/, so, if one is enabled, then the other one will get disabled and vice-versa. Signed-off-by: Tarun Vyas --- drivers/staging/Kconfig | 2 + drivers/staging/Makefile | 1 + drivers/staging/igb_avb/COPYING | 339 + drivers/staging/igb_avb/Kconfig | 18 + drivers/staging/igb_avb/LICENSE | 24 + drivers/staging/igb_avb/Makefile | 18 + drivers/staging/igb_avb/README | 65 + drivers/staging/igb_avb/e1000_82575.c | 3809 +++++++ drivers/staging/igb_avb/e1000_82575.h | 510 + drivers/staging/igb_avb/e1000_api.c | 1160 +++ drivers/staging/igb_avb/e1000_api.h | 152 + drivers/staging/igb_avb/e1000_defines.h | 1486 +++ drivers/staging/igb_avb/e1000_hw.h | 792 ++ drivers/staging/igb_avb/e1000_i210.c | 993 ++ drivers/staging/igb_avb/e1000_i210.h | 101 + drivers/staging/igb_avb/e1000_mac.c | 2149 ++++ drivers/staging/igb_avb/e1000_mac.h | 81 + drivers/staging/igb_avb/e1000_manage.c | 552 + drivers/staging/igb_avb/e1000_manage.h | 86 + drivers/staging/igb_avb/e1000_mbx.c | 523 + drivers/staging/igb_avb/e1000_mbx.h | 84 + drivers/staging/igb_avb/e1000_nvm.c | 973 ++ drivers/staging/igb_avb/e1000_nvm.h | 70 + drivers/staging/igb_avb/e1000_osdep.h | 141 + drivers/staging/igb_avb/e1000_phy.c | 3396 ++++++ drivers/staging/igb_avb/e1000_phy.h | 252 + drivers/staging/igb_avb/e1000_regs.h | 633 ++ drivers/staging/igb_avb/igb.h | 937 ++ drivers/staging/igb_avb/igb_avb.7 | 253 + drivers/staging/igb_avb/igb_debugfs.c | 26 + drivers/staging/igb_avb/igb_ethtool.c | 2994 ++++++ drivers/staging/igb_avb/igb_hwmon.c | 257 + drivers/staging/igb_avb/igb_main.c | 10875 ++++++++++++++++++++ drivers/staging/igb_avb/igb_param.c | 871 ++ drivers/staging/igb_avb/igb_procfs.c | 356 + drivers/staging/igb_avb/igb_ptp.c | 1364 +++ drivers/staging/igb_avb/igb_regtest.h | 256 + drivers/staging/igb_avb/igb_vmdq.c | 433 + drivers/staging/igb_avb/igb_vmdq.h | 43 + drivers/staging/igb_avb/kcompat.c | 1977 ++++ drivers/staging/igb_avb/kcompat.h | 4707 +++++++++ drivers/staging/igb_avb/kcompat_ethtool.c | 1169 +++ drivers/staging/igb_avb/pci.updates | 19 + drivers/staging/igb_avb/startup.sh | 23 + 44 files changed, 44970 insertions(+) create mode 100644 drivers/staging/igb_avb/COPYING create mode 100644 drivers/staging/igb_avb/Kconfig create mode 100644 drivers/staging/igb_avb/LICENSE create mode 100644 drivers/staging/igb_avb/Makefile create mode 100644 drivers/staging/igb_avb/README create mode 100644 drivers/staging/igb_avb/e1000_82575.c create mode 100644 drivers/staging/igb_avb/e1000_82575.h create mode 100644 drivers/staging/igb_avb/e1000_api.c create mode 100644 drivers/staging/igb_avb/e1000_api.h create mode 100644 drivers/staging/igb_avb/e1000_defines.h create mode 100644 drivers/staging/igb_avb/e1000_hw.h create mode 100644 drivers/staging/igb_avb/e1000_i210.c create mode 100644 drivers/staging/igb_avb/e1000_i210.h create mode 100644 drivers/staging/igb_avb/e1000_mac.c create mode 100644 drivers/staging/igb_avb/e1000_mac.h create mode 100644 drivers/staging/igb_avb/e1000_manage.c create mode 100644 drivers/staging/igb_avb/e1000_manage.h create mode 100644 drivers/staging/igb_avb/e1000_mbx.c create mode 100644 drivers/staging/igb_avb/e1000_mbx.h create mode 100644 drivers/staging/igb_avb/e1000_nvm.c create mode 100644 drivers/staging/igb_avb/e1000_nvm.h create mode 100644 drivers/staging/igb_avb/e1000_osdep.h create mode 100644 drivers/staging/igb_avb/e1000_phy.c create mode 100644 drivers/staging/igb_avb/e1000_phy.h create mode 100644 drivers/staging/igb_avb/e1000_regs.h create mode 100644 drivers/staging/igb_avb/igb.h create mode 100755 drivers/staging/igb_avb/igb_avb.7 create mode 100644 drivers/staging/igb_avb/igb_debugfs.c create mode 100644 drivers/staging/igb_avb/igb_ethtool.c create mode 100644 drivers/staging/igb_avb/igb_hwmon.c create mode 100644 drivers/staging/igb_avb/igb_main.c create mode 100644 drivers/staging/igb_avb/igb_param.c create mode 100644 drivers/staging/igb_avb/igb_procfs.c create mode 100644 drivers/staging/igb_avb/igb_ptp.c create mode 100644 drivers/staging/igb_avb/igb_regtest.h create mode 100644 drivers/staging/igb_avb/igb_vmdq.c create mode 100644 drivers/staging/igb_avb/igb_vmdq.h create mode 100644 drivers/staging/igb_avb/kcompat.c create mode 100644 drivers/staging/igb_avb/kcompat.h create mode 100644 drivers/staging/igb_avb/kcompat_ethtool.c create mode 100644 drivers/staging/igb_avb/pci.updates create mode 100755 drivers/staging/igb_avb/startup.sh diff --git a/drivers/staging/Kconfig b/drivers/staging/Kconfig index 1abf76be2aa8..89735a5fd9e1 100644 --- a/drivers/staging/Kconfig +++ b/drivers/staging/Kconfig @@ -126,4 +126,6 @@ source "drivers/staging/axis-fifo/Kconfig" source "drivers/staging/erofs/Kconfig" +source "drivers/staging/igb_avb/Kconfig" + endif # STAGING diff --git a/drivers/staging/Makefile b/drivers/staging/Makefile index ab0cbe8815b1..f7d9b0acf361 100644 --- a/drivers/staging/Makefile +++ b/drivers/staging/Makefile @@ -53,3 +53,4 @@ obj-$(CONFIG_SOC_MT7621) += mt7621-dts/ obj-$(CONFIG_STAGING_GASKET_FRAMEWORK) += gasket/ obj-$(CONFIG_XIL_AXIS_FIFO) += axis-fifo/ obj-$(CONFIG_EROFS_FS) += erofs/ +obj-$(CONFIG_IGB_AVB) += igb_avb/ diff --git a/drivers/staging/igb_avb/COPYING b/drivers/staging/igb_avb/COPYING new file mode 100644 index 000000000000..d159169d1050 --- /dev/null +++ b/drivers/staging/igb_avb/COPYING @@ -0,0 +1,339 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Lesser General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + 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. + + 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., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. diff --git a/drivers/staging/igb_avb/Kconfig b/drivers/staging/igb_avb/Kconfig new file mode 100644 index 000000000000..e3da46a6f381 --- /dev/null +++ b/drivers/staging/igb_avb/Kconfig @@ -0,0 +1,18 @@ +config IGB_AVB + tristate "Avnu IGB AVB driver" + depends on IGB=n + select DCA + default n + ---help--- + This is the Intel I210 Ethernet driver that lives + at https://github.com/AVnu/OpenAvnu/tree/master/ + kmod/igb. Note that this is different from drivers/ + net/ethernet/intel/igb. It can be used for developing + Audio/Video Bridging applications, Industrial Ethernet + applications which require precise timing control over + frame transmission, or test harnesses for measuring system + latencies and sampling events. It is exclusive with the + in-tree IGB driver, so only one of them can be enabled + at any point in time. + + To build this as a module, say M, if not sure say N. diff --git a/drivers/staging/igb_avb/LICENSE b/drivers/staging/igb_avb/LICENSE new file mode 100644 index 000000000000..b84d7002e5c4 --- /dev/null +++ b/drivers/staging/igb_avb/LICENSE @@ -0,0 +1,24 @@ + + Intel(R) Gigabit Ethernet Linux driver + Copyright(c) 2007-2012 Intel Corporation. + + This program is free software; you can redistribute it and/or modify it + under the terms and conditions of the GNU General Public License, + version 2, as published by the Free Software Foundation. + + This program is distributed in the hope 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. + + 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., + 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. + + The full GNU General Public License is included in this distribution in + the file called "COPYING". + + Contact Information: + e1000-devel Mailing List + Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + diff --git a/drivers/staging/igb_avb/Makefile b/drivers/staging/igb_avb/Makefile new file mode 100644 index 000000000000..eaae47e157f3 --- /dev/null +++ b/drivers/staging/igb_avb/Makefile @@ -0,0 +1,18 @@ +obj-$(CONFIG_IGB_AVB) += igb_avb.o + +igb_avb-y := igb_main.o \ + e1000_82575.o \ + e1000_i210.o \ + e1000_mac.o \ + e1000_nvm.o e1000_phy.o \ + e1000_manage.o \ + igb_param.o \ + igb_ethtool.o \ + kcompat.o \ + e1000_api.o \ + e1000_mbx.o \ + igb_vmdq.o \ + igb_procfs.o \ + igb_hwmon.o \ + igb_debugfs.o \ + igb_ptp.o diff --git a/drivers/staging/igb_avb/README b/drivers/staging/igb_avb/README new file mode 100644 index 000000000000..c08ff5d4d18d --- /dev/null +++ b/drivers/staging/igb_avb/README @@ -0,0 +1,65 @@ +INTRODUCTION + +This component demonstrates various features of the Intel I210 Ethernet +controller. These features can be used for developing Audio/Video Bridging +applications, Industrial Ethernet applications which require precise timing +control over frame transmission, or test harnesses for measuring system +latencies and sampling events. + +This component - igb_avb - is limited to the Intel I210 Ethernet controller. +The kernel module can be loaded in parallel to existing in-kernel igb modules +which may be used on other supported Intel LAN controllers. Modifications are +required to the in-kernel drivers if the existing in-kernel igb driver has +support for the Intel I210. + +BUILDING + +The kernel igb module should be built which supports the latest Linux kernel +3.x PTP clock support. Unlike the standard igb driver, this version enables +PTP by default (and will fail to build without kernel PTP support enabled). + +RUNNING + +To install the kernel mode driver, you must have root permissions. Typically, +the driver is loaded by removing the currently running igb and running igb_avb: + sudo rmmod igb + sudo modprobe i2c_algo_bit + sudo modprobe dca + sudo modprobe ptp + sudo insmod ./igb_avb.ko + +Another option is to install the igb_avb driver in the "updates" directory +which will override igb for the other drivers claiming the same device ID. This +will allow the coexistence of igb and igb_avb. Copy igb_avb.ko to: + sudo cp igb_avb.ko /lib/modules/`uname -r`/updates/ + sudo depmod -a + modprobe igb_avb + +As the AVB Transmit queues (0,1) are mapped to a user-space application, +typical LAN traffic must be steered away from these queues. The driver +implements one method registering an ndo_select_queue handler to map traffic to +queue[3]. Another possibly faster method uses the the transmit packet steering +(XPS) functionality available since 2.6.35. An example script is below + +#!/bin/bash + +INTERFACE=p2p1 +export INTERFACE + +rmmod igb +rmmod igb_avb +insmod ./igb_avb.ko +sleep 1 +ifconfig $INTERFACE down +echo 0 > /sys/class/net/$INTERFACE/queues/tx-0/xps_cpus +echo 0 > /sys/class/net/$INTERFACE/queues/tx-1/xps_cpus +echo f > /sys/class/net/$INTERFACE/queues/tx-2/xps_cpus +echo f > /sys/class/net/$INTERFACE/queues/tx-3/xps_cpus +ifconfig $INTERFACE up + +You map also want to disable the network manager from 'managing' your +interface. The easiest way is to find the interface configuration scripts on +your distribution. On Fedora 18, these are located at +/etc/sysconfig/network-scripts/ifcfg-. Edit the file to set +'BOOTPROTO=none'. This eliminates DHCP trying to configure the interface while +you may be doing user-space application configuration. diff --git a/drivers/staging/igb_avb/e1000_82575.c b/drivers/staging/igb_avb/e1000_82575.c new file mode 100644 index 000000000000..2fcc3bf3af39 --- /dev/null +++ b/drivers/staging/igb_avb/e1000_82575.c @@ -0,0 +1,3809 @@ +/******************************************************************************* + + Intel(R) Gigabit Ethernet Linux driver + Copyright(c) 2007-2015 Intel Corporation. + + This program is free software; you can redistribute it and/or modify it + under the terms and conditions of the GNU General Public License, + version 2, as published by the Free Software Foundation. + + This program is distributed in the hope 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. + + The full GNU General Public License is included in this distribution in + the file called "COPYING". + + Contact Information: + Linux NICS + e1000-devel Mailing List + Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + +*******************************************************************************/ + +/* + * 82575EB Gigabit Network Connection + * 82575EB Gigabit Backplane Connection + * 82575GB Gigabit Network Connection + * 82576 Gigabit Network Connection + * 82576 Quad Port Gigabit Mezzanine Adapter + * 82580 Gigabit Network Connection + * I350 Gigabit Network Connection + */ + +#include "e1000_api.h" +#include "e1000_i210.h" + +static s32 e1000_init_phy_params_82575(struct e1000_hw *hw); +static s32 e1000_init_mac_params_82575(struct e1000_hw *hw); +static s32 e1000_acquire_phy_82575(struct e1000_hw *hw); +static void e1000_release_phy_82575(struct e1000_hw *hw); +static s32 e1000_acquire_nvm_82575(struct e1000_hw *hw); +static void e1000_release_nvm_82575(struct e1000_hw *hw); +static s32 e1000_check_for_link_82575(struct e1000_hw *hw); +static s32 e1000_check_for_link_media_swap(struct e1000_hw *hw); +static s32 e1000_get_cfg_done_82575(struct e1000_hw *hw); +static s32 e1000_get_link_up_info_82575(struct e1000_hw *hw, u16 *speed, + u16 *duplex); +static s32 e1000_phy_hw_reset_sgmii_82575(struct e1000_hw *hw); +static s32 e1000_read_phy_reg_sgmii_82575(struct e1000_hw *hw, u32 offset, + u16 *data); +static s32 e1000_reset_hw_82575(struct e1000_hw *hw); +static s32 e1000_reset_hw_82580(struct e1000_hw *hw); +static s32 e1000_read_phy_reg_82580(struct e1000_hw *hw, + u32 offset, u16 *data); +static s32 e1000_write_phy_reg_82580(struct e1000_hw *hw, + u32 offset, u16 data); +static s32 e1000_set_d0_lplu_state_82580(struct e1000_hw *hw, + bool active); +static s32 e1000_set_d3_lplu_state_82580(struct e1000_hw *hw, + bool active); +static s32 e1000_set_d0_lplu_state_82575(struct e1000_hw *hw, + bool active); +static s32 e1000_setup_copper_link_82575(struct e1000_hw *hw); +static s32 e1000_setup_serdes_link_82575(struct e1000_hw *hw); +static s32 e1000_get_media_type_82575(struct e1000_hw *hw); +#ifdef I2C_ENABLED +static s32 e1000_set_sfp_media_type_82575(struct e1000_hw *hw); +#endif +static s32 e1000_valid_led_default_82575(struct e1000_hw *hw, u16 *data); +static s32 e1000_write_phy_reg_sgmii_82575(struct e1000_hw *hw, + u32 offset, u16 data); +static void e1000_clear_hw_cntrs_82575(struct e1000_hw *hw); +static s32 e1000_acquire_swfw_sync_82575(struct e1000_hw *hw, u16 mask); +static s32 e1000_get_pcs_speed_and_duplex_82575(struct e1000_hw *hw, + u16 *speed, u16 *duplex); +static s32 e1000_get_phy_id_82575(struct e1000_hw *hw); +static void e1000_release_swfw_sync_82575(struct e1000_hw *hw, u16 mask); +static bool e1000_sgmii_active_82575(struct e1000_hw *hw); +static s32 e1000_reset_init_script_82575(struct e1000_hw *hw); +static s32 e1000_read_mac_addr_82575(struct e1000_hw *hw); +static void e1000_config_collision_dist_82575(struct e1000_hw *hw); +static void e1000_power_down_phy_copper_82575(struct e1000_hw *hw); +static void e1000_shutdown_serdes_link_82575(struct e1000_hw *hw); +static void e1000_power_up_serdes_link_82575(struct e1000_hw *hw); +static s32 e1000_set_pcie_completion_timeout(struct e1000_hw *hw); +static s32 e1000_reset_mdicnfg_82580(struct e1000_hw *hw); +static s32 e1000_validate_nvm_checksum_82580(struct e1000_hw *hw); +static s32 e1000_update_nvm_checksum_82580(struct e1000_hw *hw); +static s32 e1000_update_nvm_checksum_with_offset(struct e1000_hw *hw, + u16 offset); +static s32 e1000_validate_nvm_checksum_with_offset(struct e1000_hw *hw, + u16 offset); +static s32 e1000_validate_nvm_checksum_i350(struct e1000_hw *hw); +static s32 e1000_update_nvm_checksum_i350(struct e1000_hw *hw); +static void e1000_write_vfta_i350(struct e1000_hw *hw, u32 offset, u32 value); +static void e1000_clear_vfta_i350(struct e1000_hw *hw); + +static void e1000_i2c_start(struct e1000_hw *hw); +static void e1000_i2c_stop(struct e1000_hw *hw); +static void e1000_clock_in_i2c_byte(struct e1000_hw *hw, u8 *data); +static s32 e1000_clock_out_i2c_byte(struct e1000_hw *hw, u8 data); +static s32 e1000_get_i2c_ack(struct e1000_hw *hw); +static void e1000_clock_in_i2c_bit(struct e1000_hw *hw, bool *data); +static s32 e1000_clock_out_i2c_bit(struct e1000_hw *hw, bool data); +static void e1000_raise_i2c_clk(struct e1000_hw *hw, u32 *i2cctl); +static void e1000_lower_i2c_clk(struct e1000_hw *hw, u32 *i2cctl); +static s32 e1000_set_i2c_data(struct e1000_hw *hw, u32 *i2cctl, bool data); +static bool e1000_get_i2c_data(u32 *i2cctl); + +static const u16 e1000_82580_rxpbs_table[] = { + 36, 72, 144, 1, 2, 4, 8, 16, 35, 70, 140 }; +#define E1000_82580_RXPBS_TABLE_SIZE \ + (sizeof(e1000_82580_rxpbs_table) / \ + sizeof(e1000_82580_rxpbs_table[0])) + +/** + * e1000_sgmii_uses_mdio_82575 - Determine if I2C pins are for external MDIO + * @hw: pointer to the HW structure + * + * Called to determine if the I2C pins are being used for I2C or as an + * external MDIO interface since the two options are mutually exclusive. + **/ +static bool e1000_sgmii_uses_mdio_82575(struct e1000_hw *hw) +{ + u32 reg = 0; + bool ext_mdio = false; + + DEBUGFUNC("e1000_sgmii_uses_mdio_82575"); + + switch (hw->mac.type) { + case e1000_82575: + case e1000_82576: + reg = E1000_READ_REG(hw, E1000_MDIC); + ext_mdio = !!(reg & E1000_MDIC_DEST); + break; + case e1000_82580: + case e1000_i350: + case e1000_i354: + case e1000_i210: + case e1000_i211: + reg = E1000_READ_REG(hw, E1000_MDICNFG); + ext_mdio = !!(reg & E1000_MDICNFG_EXT_MDIO); + break; + default: + break; + } + return ext_mdio; +} + +/** + * e1000_init_phy_params_82575 - Init PHY func ptrs. + * @hw: pointer to the HW structure + **/ +static s32 e1000_init_phy_params_82575(struct e1000_hw *hw) +{ + struct e1000_phy_info *phy = &hw->phy; + s32 ret_val = E1000_SUCCESS; + u32 ctrl_ext; + + DEBUGFUNC("e1000_init_phy_params_82575"); + + phy->ops.read_i2c_byte = e1000_read_i2c_byte_generic; + phy->ops.write_i2c_byte = e1000_write_i2c_byte_generic; + + if (hw->phy.media_type != e1000_media_type_copper) { + phy->type = e1000_phy_none; + goto out; + } + + phy->ops.power_up = e1000_power_up_phy_copper; + phy->ops.power_down = e1000_power_down_phy_copper_82575; + + phy->autoneg_mask = AUTONEG_ADVERTISE_SPEED_DEFAULT; + phy->reset_delay_us = 100; + + phy->ops.acquire = e1000_acquire_phy_82575; + phy->ops.check_reset_block = e1000_check_reset_block_generic; + phy->ops.commit = e1000_phy_sw_reset_generic; + phy->ops.get_cfg_done = e1000_get_cfg_done_82575; + phy->ops.release = e1000_release_phy_82575; + + ctrl_ext = E1000_READ_REG(hw, E1000_CTRL_EXT); + + if (e1000_sgmii_active_82575(hw)) { + phy->ops.reset = e1000_phy_hw_reset_sgmii_82575; + ctrl_ext |= E1000_CTRL_I2C_ENA; + } else { + phy->ops.reset = e1000_phy_hw_reset_generic; + ctrl_ext &= ~E1000_CTRL_I2C_ENA; + } + + E1000_WRITE_REG(hw, E1000_CTRL_EXT, ctrl_ext); + e1000_reset_mdicnfg_82580(hw); + + if (e1000_sgmii_active_82575(hw) && !e1000_sgmii_uses_mdio_82575(hw)) { + phy->ops.read_reg = e1000_read_phy_reg_sgmii_82575; + phy->ops.write_reg = e1000_write_phy_reg_sgmii_82575; + } else { + switch (hw->mac.type) { + case e1000_82580: + case e1000_i350: + case e1000_i354: + phy->ops.read_reg = e1000_read_phy_reg_82580; + phy->ops.write_reg = e1000_write_phy_reg_82580; + break; + case e1000_i210: + case e1000_i211: + phy->ops.read_reg = e1000_read_phy_reg_gs40g; + phy->ops.write_reg = e1000_write_phy_reg_gs40g; + break; + default: + phy->ops.read_reg = e1000_read_phy_reg_igp; + phy->ops.write_reg = e1000_write_phy_reg_igp; + } + } + + /* Set phy->phy_addr and phy->id. */ + ret_val = e1000_get_phy_id_82575(hw); + + /* Verify phy id and set remaining function pointers */ + switch (phy->id) { + case M88E1543_E_PHY_ID: + case M88E1512_E_PHY_ID: + case I347AT4_E_PHY_ID: + case M88E1112_E_PHY_ID: + case M88E1340M_E_PHY_ID: + case M88E1111_I_PHY_ID: + phy->type = e1000_phy_m88; + phy->ops.check_polarity = e1000_check_polarity_m88; + phy->ops.get_info = e1000_get_phy_info_m88; + if (phy->id == I347AT4_E_PHY_ID || + phy->id == M88E1112_E_PHY_ID || + phy->id == M88E1340M_E_PHY_ID) + phy->ops.get_cable_length = + e1000_get_cable_length_m88_gen2; + else if (phy->id == M88E1543_E_PHY_ID || + phy->id == M88E1512_E_PHY_ID) + phy->ops.get_cable_length = + e1000_get_cable_length_m88_gen2; + else + phy->ops.get_cable_length = e1000_get_cable_length_m88; + phy->ops.force_speed_duplex = e1000_phy_force_speed_duplex_m88; + /* Check if this PHY is confgured for media swap. */ + if (phy->id == M88E1112_E_PHY_ID) { + u16 data; + + ret_val = phy->ops.write_reg(hw, + E1000_M88E1112_PAGE_ADDR, + 2); + if (ret_val) + goto out; + + ret_val = phy->ops.read_reg(hw, + E1000_M88E1112_MAC_CTRL_1, + &data); + if (ret_val) + goto out; + + data = (data & E1000_M88E1112_MAC_CTRL_1_MODE_MASK) >> + E1000_M88E1112_MAC_CTRL_1_MODE_SHIFT; + if (data == E1000_M88E1112_AUTO_COPPER_SGMII || + data == E1000_M88E1112_AUTO_COPPER_BASEX) + hw->mac.ops.check_for_link = + e1000_check_for_link_media_swap; + } + if (phy->id == M88E1512_E_PHY_ID) { + ret_val = e1000_initialize_M88E1512_phy(hw); + if (ret_val) + goto out; + } + break; + case IGP03E1000_E_PHY_ID: + case IGP04E1000_E_PHY_ID: + phy->type = e1000_phy_igp_3; + phy->ops.check_polarity = e1000_check_polarity_igp; + phy->ops.get_info = e1000_get_phy_info_igp; + phy->ops.get_cable_length = e1000_get_cable_length_igp_2; + phy->ops.force_speed_duplex = e1000_phy_force_speed_duplex_igp; + phy->ops.set_d0_lplu_state = e1000_set_d0_lplu_state_82575; + phy->ops.set_d3_lplu_state = e1000_set_d3_lplu_state_generic; + break; + case I82580_I_PHY_ID: + case I350_I_PHY_ID: + phy->type = e1000_phy_82580; + phy->ops.check_polarity = e1000_check_polarity_82577; + phy->ops.force_speed_duplex = + e1000_phy_force_speed_duplex_82577; + phy->ops.get_cable_length = e1000_get_cable_length_82577; + phy->ops.get_info = e1000_get_phy_info_82577; + phy->ops.set_d0_lplu_state = e1000_set_d0_lplu_state_82580; + phy->ops.set_d3_lplu_state = e1000_set_d3_lplu_state_82580; + break; + case I210_I_PHY_ID: + phy->type = e1000_phy_i210; + phy->ops.check_polarity = e1000_check_polarity_m88; + phy->ops.get_info = e1000_get_phy_info_m88; + phy->ops.get_cable_length = e1000_get_cable_length_m88_gen2; + phy->ops.set_d0_lplu_state = e1000_set_d0_lplu_state_82580; + phy->ops.set_d3_lplu_state = e1000_set_d3_lplu_state_82580; + phy->ops.force_speed_duplex = e1000_phy_force_speed_duplex_m88; + break; + default: + ret_val = -E1000_ERR_PHY; + goto out; + } + +out: + return ret_val; +} + +/** + * e1000_init_nvm_params_82575 - Init NVM func ptrs. + * @hw: pointer to the HW structure + **/ +s32 e1000_init_nvm_params_82575(struct e1000_hw *hw) +{ + struct e1000_nvm_info *nvm = &hw->nvm; + u32 eecd = E1000_READ_REG(hw, E1000_EECD); + u16 size; + + DEBUGFUNC("e1000_init_nvm_params_82575"); + + size = (u16)((eecd & E1000_EECD_SIZE_EX_MASK) >> + E1000_EECD_SIZE_EX_SHIFT); + /* + * Added to a constant, "size" becomes the left-shift value + * for setting word_size. + */ + size += NVM_WORD_SIZE_BASE_SHIFT; + + /* Just in case size is out of range, cap it to the largest + * EEPROM size supported + */ + if (size > 15) + size = 15; + + nvm->word_size = 1 << size; + if (hw->mac.type < e1000_i210) { + nvm->opcode_bits = 8; + nvm->delay_usec = 1; + + switch (nvm->override) { + case e1000_nvm_override_spi_large: + nvm->page_size = 32; + nvm->address_bits = 16; + break; + case e1000_nvm_override_spi_small: + nvm->page_size = 8; + nvm->address_bits = 8; + break; + default: + nvm->page_size = eecd & E1000_EECD_ADDR_BITS ? 32 : 8; + nvm->address_bits = eecd & E1000_EECD_ADDR_BITS ? + 16 : 8; + break; + } + if (nvm->word_size == (1 << 15)) + nvm->page_size = 128; + + nvm->type = e1000_nvm_eeprom_spi; + } else { + nvm->type = e1000_nvm_flash_hw; + } + + /* Function Pointers */ + nvm->ops.acquire = e1000_acquire_nvm_82575; + nvm->ops.release = e1000_release_nvm_82575; + if (nvm->word_size < (1 << 15)) + nvm->ops.read = e1000_read_nvm_eerd; + else + nvm->ops.read = e1000_read_nvm_spi; + + nvm->ops.write = e1000_write_nvm_spi; + nvm->ops.validate = e1000_validate_nvm_checksum_generic; + nvm->ops.update = e1000_update_nvm_checksum_generic; + nvm->ops.valid_led_default = e1000_valid_led_default_82575; + + /* override generic family function pointers for specific descendants */ + switch (hw->mac.type) { + case e1000_82580: + nvm->ops.validate = e1000_validate_nvm_checksum_82580; + nvm->ops.update = e1000_update_nvm_checksum_82580; + break; + case e1000_i350: + case e1000_i354: + nvm->ops.validate = e1000_validate_nvm_checksum_i350; + nvm->ops.update = e1000_update_nvm_checksum_i350; + break; + default: + break; + } + + return E1000_SUCCESS; +} + +/** + * e1000_init_mac_params_82575 - Init MAC func ptrs. + * @hw: pointer to the HW structure + **/ +static s32 e1000_init_mac_params_82575(struct e1000_hw *hw) +{ + struct e1000_mac_info *mac = &hw->mac; + struct e1000_dev_spec_82575 *dev_spec = &hw->dev_spec._82575; + + DEBUGFUNC("e1000_init_mac_params_82575"); + + /* Derives media type */ + e1000_get_media_type_82575(hw); + /* Set mta register count */ + mac->mta_reg_count = 128; + /* Set uta register count */ + mac->uta_reg_count = (hw->mac.type == e1000_82575) ? 0 : 128; + /* Set rar entry count */ + mac->rar_entry_count = E1000_RAR_ENTRIES_82575; + if (mac->type == e1000_82576) + mac->rar_entry_count = E1000_RAR_ENTRIES_82576; + if (mac->type == e1000_82580) + mac->rar_entry_count = E1000_RAR_ENTRIES_82580; + if (mac->type == e1000_i350 || mac->type == e1000_i354) + mac->rar_entry_count = E1000_RAR_ENTRIES_I350; + + /* Enable EEE default settings for EEE supported devices */ + if (mac->type >= e1000_i350) + dev_spec->eee_disable = false; + + /* Allow a single clear of the SW semaphore on I210 and newer */ + if (mac->type >= e1000_i210) + dev_spec->clear_semaphore_once = true; + + /* Set if part includes ASF firmware */ + mac->asf_firmware_present = true; + /* FWSM register */ + mac->has_fwsm = true; + /* ARC supported; valid only if manageability features are enabled. */ + mac->arc_subsystem_valid = + !!(E1000_READ_REG(hw, E1000_FWSM) & E1000_FWSM_MODE_MASK); + + /* Function pointers */ + + /* bus type/speed/width */ + mac->ops.get_bus_info = e1000_get_bus_info_pcie_generic; + /* reset */ + if (mac->type >= e1000_82580) + mac->ops.reset_hw = e1000_reset_hw_82580; + else + mac->ops.reset_hw = e1000_reset_hw_82575; + /* hw initialization */ + if ((mac->type == e1000_i210) || (mac->type == e1000_i211)) + mac->ops.init_hw = e1000_init_hw_i210; + else + mac->ops.init_hw = e1000_init_hw_82575; + /* link setup */ + mac->ops.setup_link = e1000_setup_link_generic; + /* physical interface link setup */ + mac->ops.setup_physical_interface = + (hw->phy.media_type == e1000_media_type_copper) + ? e1000_setup_copper_link_82575 : e1000_setup_serdes_link_82575; + /* physical interface shutdown */ + mac->ops.shutdown_serdes = e1000_shutdown_serdes_link_82575; + /* physical interface power up */ + mac->ops.power_up_serdes = e1000_power_up_serdes_link_82575; + /* check for link */ + mac->ops.check_for_link = e1000_check_for_link_82575; + /* read mac address */ + mac->ops.read_mac_addr = e1000_read_mac_addr_82575; + /* configure collision distance */ + mac->ops.config_collision_dist = e1000_config_collision_dist_82575; + /* multicast address update */ + mac->ops.update_mc_addr_list = e1000_update_mc_addr_list_generic; + if (hw->mac.type == e1000_i350 || mac->type == e1000_i354) { + /* writing VFTA */ + mac->ops.write_vfta = e1000_write_vfta_i350; + /* clearing VFTA */ + mac->ops.clear_vfta = e1000_clear_vfta_i350; + } else { + /* writing VFTA */ + mac->ops.write_vfta = e1000_write_vfta_generic; + /* clearing VFTA */ + mac->ops.clear_vfta = e1000_clear_vfta_generic; + } + if (hw->mac.type >= e1000_82580) + mac->ops.validate_mdi_setting = + e1000_validate_mdi_setting_crossover_generic; + /* ID LED init */ + mac->ops.id_led_init = e1000_id_led_init_generic; + /* blink LED */ + mac->ops.blink_led = e1000_blink_led_generic; + /* setup LED */ + mac->ops.setup_led = e1000_setup_led_generic; + /* cleanup LED */ + mac->ops.cleanup_led = e1000_cleanup_led_generic; + /* turn on/off LED */ + mac->ops.led_on = e1000_led_on_generic; + mac->ops.led_off = e1000_led_off_generic; + /* clear hardware counters */ + mac->ops.clear_hw_cntrs = e1000_clear_hw_cntrs_82575; + /* link info */ + mac->ops.get_link_up_info = e1000_get_link_up_info_82575; + /* get thermal sensor data */ + mac->ops.get_thermal_sensor_data = + e1000_get_thermal_sensor_data_generic; + mac->ops.init_thermal_sensor_thresh = + e1000_init_thermal_sensor_thresh_generic; + /* acquire SW_FW sync */ + mac->ops.acquire_swfw_sync = e1000_acquire_swfw_sync_82575; + mac->ops.release_swfw_sync = e1000_release_swfw_sync_82575; + if (mac->type >= e1000_i210) { + mac->ops.acquire_swfw_sync = e1000_acquire_swfw_sync_i210; + mac->ops.release_swfw_sync = e1000_release_swfw_sync_i210; + } + + /* set lan id for port to determine which phy lock to use */ + hw->mac.ops.set_lan_id(hw); + + return E1000_SUCCESS; +} + +/** + * e1000_init_function_pointers_82575 - Init func ptrs. + * @hw: pointer to the HW structure + * + * Called to initialize all function pointers and parameters. + **/ +void e1000_init_function_pointers_82575(struct e1000_hw *hw) +{ + DEBUGFUNC("e1000_init_function_pointers_82575"); + + hw->mac.ops.init_params = e1000_init_mac_params_82575; + hw->nvm.ops.init_params = e1000_init_nvm_params_82575; + hw->phy.ops.init_params = e1000_init_phy_params_82575; + hw->mbx.ops.init_params = e1000_init_mbx_params_pf; +} + +/** + * e1000_acquire_phy_82575 - Acquire rights to access PHY + * @hw: pointer to the HW structure + * + * Acquire access rights to the correct PHY. + **/ +static s32 e1000_acquire_phy_82575(struct e1000_hw *hw) +{ + u16 mask = E1000_SWFW_PHY0_SM; + + DEBUGFUNC("e1000_acquire_phy_82575"); + + if (hw->bus.func == E1000_FUNC_1) + mask = E1000_SWFW_PHY1_SM; + else if (hw->bus.func == E1000_FUNC_2) + mask = E1000_SWFW_PHY2_SM; + else if (hw->bus.func == E1000_FUNC_3) + mask = E1000_SWFW_PHY3_SM; + + return hw->mac.ops.acquire_swfw_sync(hw, mask); +} + +/** + * e1000_release_phy_82575 - Release rights to access PHY + * @hw: pointer to the HW structure + * + * A wrapper to release access rights to the correct PHY. + **/ +static void e1000_release_phy_82575(struct e1000_hw *hw) +{ + u16 mask = E1000_SWFW_PHY0_SM; + + DEBUGFUNC("e1000_release_phy_82575"); + + if (hw->bus.func == E1000_FUNC_1) + mask = E1000_SWFW_PHY1_SM; + else if (hw->bus.func == E1000_FUNC_2) + mask = E1000_SWFW_PHY2_SM; + else if (hw->bus.func == E1000_FUNC_3) + mask = E1000_SWFW_PHY3_SM; + + hw->mac.ops.release_swfw_sync(hw, mask); +} + +/** + * e1000_read_phy_reg_sgmii_82575 - Read PHY register using sgmii + * @hw: pointer to the HW structure + * @offset: register offset to be read + * @data: pointer to the read data + * + * Reads the PHY register at offset using the serial gigabit media independent + * interface and stores the retrieved information in data. + **/ +static s32 e1000_read_phy_reg_sgmii_82575(struct e1000_hw *hw, u32 offset, + u16 *data) +{ + s32 ret_val = -E1000_ERR_PARAM; + + DEBUGFUNC("e1000_read_phy_reg_sgmii_82575"); + + if (offset > E1000_MAX_SGMII_PHY_REG_ADDR) { + DEBUGOUT1("PHY Address %u is out of range\n", offset); + goto out; + } + + ret_val = hw->phy.ops.acquire(hw); + if (ret_val) + goto out; + + ret_val = e1000_read_phy_reg_i2c(hw, offset, data); + + hw->phy.ops.release(hw); + +out: + return ret_val; +} + +/** + * e1000_write_phy_reg_sgmii_82575 - Write PHY register using sgmii + * @hw: pointer to the HW structure + * @offset: register offset to write to + * @data: data to write at register offset + * + * Writes the data to PHY register at the offset using the serial gigabit + * media independent interface. + **/ +static s32 e1000_write_phy_reg_sgmii_82575(struct e1000_hw *hw, u32 offset, + u16 data) +{ + s32 ret_val = -E1000_ERR_PARAM; + + DEBUGFUNC("e1000_write_phy_reg_sgmii_82575"); + + if (offset > E1000_MAX_SGMII_PHY_REG_ADDR) { + DEBUGOUT1("PHY Address %d is out of range\n", offset); + goto out; + } + + ret_val = hw->phy.ops.acquire(hw); + if (ret_val) + goto out; + + ret_val = e1000_write_phy_reg_i2c(hw, offset, data); + + hw->phy.ops.release(hw); + +out: + return ret_val; +} + +/** + * e1000_get_phy_id_82575 - Retrieve PHY addr and id + * @hw: pointer to the HW structure + * + * Retrieves the PHY address and ID for both PHY's which do and do not use + * sgmi interface. + **/ +static s32 e1000_get_phy_id_82575(struct e1000_hw *hw) +{ + struct e1000_phy_info *phy = &hw->phy; + s32 ret_val = E1000_SUCCESS; + u16 phy_id; + u32 ctrl_ext; + u32 mdic; + + DEBUGFUNC("e1000_get_phy_id_82575"); + + /* some i354 devices need an extra read for phy id */ + if (hw->mac.type == e1000_i354) + e1000_get_phy_id(hw); + + /* + * For SGMII PHYs, we try the list of possible addresses until + * we find one that works. For non-SGMII PHYs + * (e.g. integrated copper PHYs), an address of 1 should + * work. The result of this function should mean phy->phy_addr + * and phy->id are set correctly. + */ + if (!e1000_sgmii_active_82575(hw)) { + phy->addr = 1; + ret_val = e1000_get_phy_id(hw); + goto out; + } + + if (e1000_sgmii_uses_mdio_82575(hw)) { + switch (hw->mac.type) { + case e1000_82575: + case e1000_82576: + mdic = E1000_READ_REG(hw, E1000_MDIC); + mdic &= E1000_MDIC_PHY_MASK; + phy->addr = mdic >> E1000_MDIC_PHY_SHIFT; + break; + case e1000_82580: + case e1000_i350: + case e1000_i354: + case e1000_i210: + case e1000_i211: + mdic = E1000_READ_REG(hw, E1000_MDICNFG); + mdic &= E1000_MDICNFG_PHY_MASK; + phy->addr = mdic >> E1000_MDICNFG_PHY_SHIFT; + break; + default: + ret_val = -E1000_ERR_PHY; + goto out; + break; + } + ret_val = e1000_get_phy_id(hw); + goto out; + } + + /* Power on sgmii phy if it is disabled */ + ctrl_ext = E1000_READ_REG(hw, E1000_CTRL_EXT); + E1000_WRITE_REG(hw, E1000_CTRL_EXT, + ctrl_ext & ~E1000_CTRL_EXT_SDP3_DATA); + E1000_WRITE_FLUSH(hw); + msec_delay(300); + + /* + * The address field in the I2CCMD register is 3 bits and 0 is invalid. + * Therefore, we need to test 1-7 + */ + for (phy->addr = 1; phy->addr < 8; phy->addr++) { + ret_val = e1000_read_phy_reg_sgmii_82575(hw, PHY_ID1, &phy_id); + if (ret_val == E1000_SUCCESS) { + DEBUGOUT2("Vendor ID 0x%08X read at address %u\n", + phy_id, phy->addr); + /* + * At the time of this writing, The M88 part is + * the only supported SGMII PHY product. + */ + if (phy_id == M88_VENDOR) + break; + } else { + DEBUGOUT1("PHY address %u was unreadable\n", + phy->addr); + } + } + + /* A valid PHY type couldn't be found. */ + if (phy->addr == 8) { + phy->addr = 0; + ret_val = -E1000_ERR_PHY; + } else { + ret_val = e1000_get_phy_id(hw); + } + + /* restore previous sfp cage power state */ + E1000_WRITE_REG(hw, E1000_CTRL_EXT, ctrl_ext); + +out: + return ret_val; +} + +/** + * e1000_phy_hw_reset_sgmii_82575 - Performs a PHY reset + * @hw: pointer to the HW structure + * + * Resets the PHY using the serial gigabit media independent interface. + **/ +static s32 e1000_phy_hw_reset_sgmii_82575(struct e1000_hw *hw) +{ + s32 ret_val = E1000_SUCCESS; + struct e1000_phy_info *phy = &hw->phy; + + DEBUGFUNC("e1000_phy_hw_reset_sgmii_82575"); + + /* + * This isn't a true "hard" reset, but is the only reset + * available to us at this time. + */ + + DEBUGOUT("Soft resetting SGMII attached PHY...\n"); + + if (!(hw->phy.ops.write_reg)) + goto out; + + /* + * SFP documentation requires the following to configure the SPF module + * to work on SGMII. No further documentation is given. + */ + ret_val = hw->phy.ops.write_reg(hw, 0x1B, 0x8084); + if (ret_val) + goto out; + + ret_val = hw->phy.ops.commit(hw); + if (ret_val) + goto out; + + if (phy->id == M88E1512_E_PHY_ID) + ret_val = e1000_initialize_M88E1512_phy(hw); +out: + return ret_val; +} + +/** + * e1000_set_d0_lplu_state_82575 - Set Low Power Linkup D0 state + * @hw: pointer to the HW structure + * @active: true to enable LPLU, false to disable + * + * Sets the LPLU D0 state according to the active flag. When + * activating LPLU this function also disables smart speed + * and vice versa. LPLU will not be activated unless the + * device autonegotiation advertisement meets standards of + * either 10 or 10/100 or 10/100/1000 at all duplexes. + * This is a function pointer entry point only called by + * PHY setup routines. + **/ +static s32 e1000_set_d0_lplu_state_82575(struct e1000_hw *hw, bool active) +{ + struct e1000_phy_info *phy = &hw->phy; + s32 ret_val = E1000_SUCCESS; + u16 data; + + DEBUGFUNC("e1000_set_d0_lplu_state_82575"); + + if (!(hw->phy.ops.read_reg)) + goto out; + + ret_val = phy->ops.read_reg(hw, IGP02E1000_PHY_POWER_MGMT, &data); + if (ret_val) + goto out; + + if (active) { + data |= IGP02E1000_PM_D0_LPLU; + ret_val = phy->ops.write_reg(hw, IGP02E1000_PHY_POWER_MGMT, + data); + if (ret_val) + goto out; + + /* When LPLU is enabled, we should disable SmartSpeed */ + ret_val = phy->ops.read_reg(hw, IGP01E1000_PHY_PORT_CONFIG, + &data); + data &= ~IGP01E1000_PSCFR_SMART_SPEED; + ret_val = phy->ops.write_reg(hw, IGP01E1000_PHY_PORT_CONFIG, + data); + if (ret_val) + goto out; + } else { + data &= ~IGP02E1000_PM_D0_LPLU; + ret_val = phy->ops.write_reg(hw, IGP02E1000_PHY_POWER_MGMT, + data); + /* + * LPLU and SmartSpeed are mutually exclusive. LPLU is used + * during Dx states where the power conservation is most + * important. During driver activity we should enable + * SmartSpeed, so performance is maintained. + */ + if (phy->smart_speed == e1000_smart_speed_on) { + ret_val = phy->ops.read_reg(hw, + IGP01E1000_PHY_PORT_CONFIG, + &data); + if (ret_val) + goto out; + + data |= IGP01E1000_PSCFR_SMART_SPEED; + ret_val = phy->ops.write_reg(hw, + IGP01E1000_PHY_PORT_CONFIG, + data); + if (ret_val) + goto out; + } else if (phy->smart_speed == e1000_smart_speed_off) { + ret_val = phy->ops.read_reg(hw, + IGP01E1000_PHY_PORT_CONFIG, + &data); + if (ret_val) + goto out; + + data &= ~IGP01E1000_PSCFR_SMART_SPEED; + ret_val = phy->ops.write_reg(hw, + IGP01E1000_PHY_PORT_CONFIG, + data); + if (ret_val) + goto out; + } + } + +out: + return ret_val; +} + +/** + * e1000_set_d0_lplu_state_82580 - Set Low Power Linkup D0 state + * @hw: pointer to the HW structure + * @active: true to enable LPLU, false to disable + * + * Sets the LPLU D0 state according to the active flag. When + * activating LPLU this function also disables smart speed + * and vice versa. LPLU will not be activated unless the + * device autonegotiation advertisement meets standards of + * either 10 or 10/100 or 10/100/1000 at all duplexes. + * This is a function pointer entry point only called by + * PHY setup routines. + **/ +static s32 e1000_set_d0_lplu_state_82580(struct e1000_hw *hw, bool active) +{ + struct e1000_phy_info *phy = &hw->phy; + u32 data; + + DEBUGFUNC("e1000_set_d0_lplu_state_82580"); + + data = E1000_READ_REG(hw, E1000_82580_PHY_POWER_MGMT); + + if (active) { + data |= E1000_82580_PM_D0_LPLU; + + /* When LPLU is enabled, we should disable SmartSpeed */ + data &= ~E1000_82580_PM_SPD; + } else { + data &= ~E1000_82580_PM_D0_LPLU; + + /* + * LPLU and SmartSpeed are mutually exclusive. LPLU is used + * during Dx states where the power conservation is most + * important. During driver activity we should enable + * SmartSpeed, so performance is maintained. + */ + if (phy->smart_speed == e1000_smart_speed_on) + data |= E1000_82580_PM_SPD; + else if (phy->smart_speed == e1000_smart_speed_off) + data &= ~E1000_82580_PM_SPD; + } + + E1000_WRITE_REG(hw, E1000_82580_PHY_POWER_MGMT, data); + return E1000_SUCCESS; +} + +/** + * e1000_set_d3_lplu_state_82580 - Sets low power link up state for D3 + * @hw: pointer to the HW structure + * @active: boolean used to enable/disable lplu + * + * Success returns 0, Failure returns 1 + * + * The low power link up (lplu) state is set to the power management level D3 + * and SmartSpeed is disabled when active is true, else clear lplu for D3 + * and enable Smartspeed. LPLU and Smartspeed are mutually exclusive. LPLU + * is used during Dx states where the power conservation is most important. + * During driver activity, SmartSpeed should be enabled so performance is + * maintained. + **/ +s32 e1000_set_d3_lplu_state_82580(struct e1000_hw *hw, bool active) +{ + struct e1000_phy_info *phy = &hw->phy; + u32 data; + + DEBUGFUNC("e1000_set_d3_lplu_state_82580"); + + data = E1000_READ_REG(hw, E1000_82580_PHY_POWER_MGMT); + + if (!active) { + data &= ~E1000_82580_PM_D3_LPLU; + /* + * LPLU and SmartSpeed are mutually exclusive. LPLU is used + * during Dx states where the power conservation is most + * important. During driver activity we should enable + * SmartSpeed, so performance is maintained. + */ + if (phy->smart_speed == e1000_smart_speed_on) + data |= E1000_82580_PM_SPD; + else if (phy->smart_speed == e1000_smart_speed_off) + data &= ~E1000_82580_PM_SPD; + } else if ((phy->autoneg_advertised == E1000_ALL_SPEED_DUPLEX) || + (phy->autoneg_advertised == E1000_ALL_NOT_GIG) || + (phy->autoneg_advertised == E1000_ALL_10_SPEED)) { + data |= E1000_82580_PM_D3_LPLU; + /* When LPLU is enabled, we should disable SmartSpeed */ + data &= ~E1000_82580_PM_SPD; + } + + E1000_WRITE_REG(hw, E1000_82580_PHY_POWER_MGMT, data); + return E1000_SUCCESS; +} + +/** + * e1000_acquire_nvm_82575 - Request for access to EEPROM + * @hw: pointer to the HW structure + * + * Acquire the necessary semaphores for exclusive access to the EEPROM. + * Set the EEPROM access request bit and wait for EEPROM access grant bit. + * Return successful if access grant bit set, else clear the request for + * EEPROM access and return -E1000_ERR_NVM (-1). + **/ +static s32 e1000_acquire_nvm_82575(struct e1000_hw *hw) +{ + s32 ret_val = E1000_SUCCESS; + + DEBUGFUNC("e1000_acquire_nvm_82575"); + + ret_val = e1000_acquire_swfw_sync_82575(hw, E1000_SWFW_EEP_SM); + if (ret_val) + goto out; + + /* + * Check if there is some access + * error this access may hook on + */ + if (hw->mac.type == e1000_i350) { + u32 eecd = E1000_READ_REG(hw, E1000_EECD); + if (eecd & (E1000_EECD_BLOCKED | E1000_EECD_ABORT | + E1000_EECD_TIMEOUT)) { + /* Clear all access error flags */ + E1000_WRITE_REG(hw, E1000_EECD, eecd | + E1000_EECD_ERROR_CLR); + DEBUGOUT("Nvm bit banging access error detected and cleared.\n"); + } + } + + if (hw->mac.type == e1000_82580) { + u32 eecd = E1000_READ_REG(hw, E1000_EECD); + if (eecd & E1000_EECD_BLOCKED) { + /* Clear access error flag */ + E1000_WRITE_REG(hw, E1000_EECD, eecd | + E1000_EECD_BLOCKED); + DEBUGOUT("Nvm bit banging access error detected and cleared.\n"); + } + } + + ret_val = e1000_acquire_nvm_generic(hw); + if (ret_val) + e1000_release_swfw_sync_82575(hw, E1000_SWFW_EEP_SM); + +out: + return ret_val; +} + +/** + * e1000_release_nvm_82575 - Release exclusive access to EEPROM + * @hw: pointer to the HW structure + * + * Stop any current commands to the EEPROM and clear the EEPROM request bit, + * then release the semaphores acquired. + **/ +static void e1000_release_nvm_82575(struct e1000_hw *hw) +{ + DEBUGFUNC("e1000_release_nvm_82575"); + + e1000_release_nvm_generic(hw); + + e1000_release_swfw_sync_82575(hw, E1000_SWFW_EEP_SM); +} + +/** + * e1000_acquire_swfw_sync_82575 - Acquire SW/FW semaphore + * @hw: pointer to the HW structure + * @mask: specifies which semaphore to acquire + * + * Acquire the SW/FW semaphore to access the PHY or NVM. The mask + * will also specify which port we're acquiring the lock for. + **/ +static s32 e1000_acquire_swfw_sync_82575(struct e1000_hw *hw, u16 mask) +{ + u32 swfw_sync; + u32 swmask = mask; + u32 fwmask = mask << 16; + s32 ret_val = E1000_SUCCESS; + s32 i = 0, timeout = 200; + + DEBUGFUNC("e1000_acquire_swfw_sync_82575"); + + while (i < timeout) { + if (e1000_get_hw_semaphore_generic(hw)) { + ret_val = -E1000_ERR_SWFW_SYNC; + goto out; + } + + swfw_sync = E1000_READ_REG(hw, E1000_SW_FW_SYNC); + if (!(swfw_sync & (fwmask | swmask))) + break; + + /* + * Firmware currently using resource (fwmask) + * or other software thread using resource (swmask) + */ + e1000_put_hw_semaphore_generic(hw); + msec_delay_irq(5); + i++; + } + + if (i == timeout) { + DEBUGOUT("Driver can't access resource, SW_FW_SYNC timeout.\n"); + ret_val = -E1000_ERR_SWFW_SYNC; + goto out; + } + + swfw_sync |= swmask; + E1000_WRITE_REG(hw, E1000_SW_FW_SYNC, swfw_sync); + + e1000_put_hw_semaphore_generic(hw); + +out: + return ret_val; +} + +/** + * e1000_release_swfw_sync_82575 - Release SW/FW semaphore + * @hw: pointer to the HW structure + * @mask: specifies which semaphore to acquire + * + * Release the SW/FW semaphore used to access the PHY or NVM. The mask + * will also specify which port we're releasing the lock for. + **/ +static void e1000_release_swfw_sync_82575(struct e1000_hw *hw, u16 mask) +{ + u32 swfw_sync; + + DEBUGFUNC("e1000_release_swfw_sync_82575"); + + while (e1000_get_hw_semaphore_generic(hw) != E1000_SUCCESS) + ; /* Empty */ + + swfw_sync = E1000_READ_REG(hw, E1000_SW_FW_SYNC); + swfw_sync &= ~mask; + E1000_WRITE_REG(hw, E1000_SW_FW_SYNC, swfw_sync); + + e1000_put_hw_semaphore_generic(hw); +} + +/** + * e1000_get_cfg_done_82575 - Read config done bit + * @hw: pointer to the HW structure + * + * Read the management control register for the config done bit for + * completion status. NOTE: silicon which is EEPROM-less will fail trying + * to read the config done bit, so an error is *ONLY* logged and returns + * E1000_SUCCESS. If we were to return with error, EEPROM-less silicon + * would not be able to be reset or change link. + **/ +static s32 e1000_get_cfg_done_82575(struct e1000_hw *hw) +{ + s32 timeout = PHY_CFG_TIMEOUT; + u32 mask = E1000_NVM_CFG_DONE_PORT_0; + + DEBUGFUNC("e1000_get_cfg_done_82575"); + + if (hw->bus.func == E1000_FUNC_1) + mask = E1000_NVM_CFG_DONE_PORT_1; + else if (hw->bus.func == E1000_FUNC_2) + mask = E1000_NVM_CFG_DONE_PORT_2; + else if (hw->bus.func == E1000_FUNC_3) + mask = E1000_NVM_CFG_DONE_PORT_3; + while (timeout) { + if (E1000_READ_REG(hw, E1000_EEMNGCTL) & mask) + break; + msec_delay(1); + timeout--; + } + if (!timeout) + DEBUGOUT("MNG configuration cycle has not completed.\n"); + + /* If EEPROM is not marked present, init the PHY manually */ + if (!(E1000_READ_REG(hw, E1000_EECD) & E1000_EECD_PRES) && + (hw->phy.type == e1000_phy_igp_3)) + e1000_phy_init_script_igp3(hw); + + return E1000_SUCCESS; +} + +/** + * e1000_get_link_up_info_82575 - Get link speed/duplex info + * @hw: pointer to the HW structure + * @speed: stores the current speed + * @duplex: stores the current duplex + * + * This is a wrapper function, if using the serial gigabit media independent + * interface, use PCS to retrieve the link speed and duplex information. + * Otherwise, use the generic function to get the link speed and duplex info. + **/ +static s32 e1000_get_link_up_info_82575(struct e1000_hw *hw, u16 *speed, + u16 *duplex) +{ + s32 ret_val; + + DEBUGFUNC("e1000_get_link_up_info_82575"); + + if (hw->phy.media_type != e1000_media_type_copper) + ret_val = e1000_get_pcs_speed_and_duplex_82575(hw, speed, + duplex); + else + ret_val = e1000_get_speed_and_duplex_copper_generic(hw, speed, + duplex); + + return ret_val; +} + +/** + * e1000_check_for_link_82575 - Check for link + * @hw: pointer to the HW structure + * + * If sgmii is enabled, then use the pcs register to determine link, otherwise + * use the generic interface for determining link. + **/ +static s32 e1000_check_for_link_82575(struct e1000_hw *hw) +{ + s32 ret_val; + u16 speed, duplex; + + DEBUGFUNC("e1000_check_for_link_82575"); + + if (hw->phy.media_type != e1000_media_type_copper) { + ret_val = e1000_get_pcs_speed_and_duplex_82575(hw, &speed, + &duplex); + /* + * Use this flag to determine if link needs to be checked or + * not. If we have link clear the flag so that we do not + * continue to check for link. + */ + hw->mac.get_link_status = !hw->mac.serdes_has_link; + + /* + * Configure Flow Control now that Auto-Neg has completed. + * First, we need to restore the desired flow control + * settings because we may have had to re-autoneg with a + * different link partner. + */ + ret_val = e1000_config_fc_after_link_up_generic(hw); + if (ret_val) + DEBUGOUT("Error configuring flow control\n"); + } else { + ret_val = e1000_check_for_copper_link_generic(hw); + } + + return ret_val; +} + +/** + * e1000_check_for_link_media_swap - Check which M88E1112 interface linked + * @hw: pointer to the HW structure + * + * Poll the M88E1112 interfaces to see which interface achieved link. + */ +static s32 e1000_check_for_link_media_swap(struct e1000_hw *hw) +{ + struct e1000_phy_info *phy = &hw->phy; + s32 ret_val; + u16 data; + u8 port = 0; + + DEBUGFUNC("e1000_check_for_link_media_swap"); + + /* Check for copper. */ + ret_val = phy->ops.write_reg(hw, E1000_M88E1112_PAGE_ADDR, 0); + if (ret_val) + return ret_val; + + ret_val = phy->ops.read_reg(hw, E1000_M88E1112_STATUS, &data); + if (ret_val) + return ret_val; + + if (data & E1000_M88E1112_STATUS_LINK) + port = E1000_MEDIA_PORT_COPPER; + + /* Check for other. */ + ret_val = phy->ops.write_reg(hw, E1000_M88E1112_PAGE_ADDR, 1); + if (ret_val) + return ret_val; + + ret_val = phy->ops.read_reg(hw, E1000_M88E1112_STATUS, &data); + if (ret_val) + return ret_val; + + if (data & E1000_M88E1112_STATUS_LINK) + port = E1000_MEDIA_PORT_OTHER; + + /* Determine if a swap needs to happen. */ + if (port && (hw->dev_spec._82575.media_port != port)) { + hw->dev_spec._82575.media_port = port; + hw->dev_spec._82575.media_changed = true; + } + + if (port == E1000_MEDIA_PORT_COPPER) { + /* reset page to 0 */ + ret_val = phy->ops.write_reg(hw, E1000_M88E1112_PAGE_ADDR, 0); + if (ret_val) + return ret_val; + e1000_check_for_link_82575(hw); + } else { + e1000_check_for_link_82575(hw); + /* reset page to 0 */ + ret_val = phy->ops.write_reg(hw, E1000_M88E1112_PAGE_ADDR, 0); + if (ret_val) + return ret_val; + } + + return E1000_SUCCESS; +} + +/** + * e1000_power_up_serdes_link_82575 - Power up the serdes link after shutdown + * @hw: pointer to the HW structure + **/ +static void e1000_power_up_serdes_link_82575(struct e1000_hw *hw) +{ + u32 reg; + + DEBUGFUNC("e1000_power_up_serdes_link_82575"); + + if ((hw->phy.media_type != e1000_media_type_internal_serdes) && + !e1000_sgmii_active_82575(hw)) + return; + + /* Enable PCS to turn on link */ + reg = E1000_READ_REG(hw, E1000_PCS_CFG0); + reg |= E1000_PCS_CFG_PCS_EN; + E1000_WRITE_REG(hw, E1000_PCS_CFG0, reg); + + /* Power up the laser */ + reg = E1000_READ_REG(hw, E1000_CTRL_EXT); + reg &= ~E1000_CTRL_EXT_SDP3_DATA; + E1000_WRITE_REG(hw, E1000_CTRL_EXT, reg); + + /* flush the write to verify completion */ + E1000_WRITE_FLUSH(hw); + msec_delay(1); +} + +/** + * e1000_get_pcs_speed_and_duplex_82575 - Retrieve current speed/duplex + * @hw: pointer to the HW structure + * @speed: stores the current speed + * @duplex: stores the current duplex + * + * Using the physical coding sub-layer (PCS), retrieve the current speed and + * duplex, then store the values in the pointers provided. + **/ +static s32 e1000_get_pcs_speed_and_duplex_82575(struct e1000_hw *hw, + u16 *speed, u16 *duplex) +{ + struct e1000_mac_info *mac = &hw->mac; + u32 pcs; + u32 status; + + DEBUGFUNC("e1000_get_pcs_speed_and_duplex_82575"); + + /* + * Read the PCS Status register for link state. For non-copper mode, + * the status register is not accurate. The PCS status register is + * used instead. + */ + pcs = E1000_READ_REG(hw, E1000_PCS_LSTAT); + + /* + * The link up bit determines when link is up on autoneg. + */ + if (pcs & E1000_PCS_LSTS_LINK_OK) { + mac->serdes_has_link = true; + + /* Detect and store PCS speed */ + if (pcs & E1000_PCS_LSTS_SPEED_1000) + *speed = SPEED_1000; + else if (pcs & E1000_PCS_LSTS_SPEED_100) + *speed = SPEED_100; + else + *speed = SPEED_10; + + /* Detect and store PCS duplex */ + if (pcs & E1000_PCS_LSTS_DUPLEX_FULL) + *duplex = FULL_DUPLEX; + else + *duplex = HALF_DUPLEX; + + /* Check if it is an I354 2.5Gb backplane connection. */ + if (mac->type == e1000_i354) { + status = E1000_READ_REG(hw, E1000_STATUS); + if ((status & E1000_STATUS_2P5_SKU) && + !(status & E1000_STATUS_2P5_SKU_OVER)) { + *speed = SPEED_2500; + *duplex = FULL_DUPLEX; + DEBUGOUT("2500 Mbs, "); + DEBUGOUT("Full Duplex\n"); + } + } + + } else { + mac->serdes_has_link = false; + *speed = 0; + *duplex = 0; + } + + return E1000_SUCCESS; +} + +/** + * e1000_shutdown_serdes_link_82575 - Remove link during power down + * @hw: pointer to the HW structure + * + * In the case of serdes shut down sfp and PCS on driver unload + * when management pass thru is not enabled. + **/ +void e1000_shutdown_serdes_link_82575(struct e1000_hw *hw) +{ + u32 reg; + + DEBUGFUNC("e1000_shutdown_serdes_link_82575"); + + if ((hw->phy.media_type != e1000_media_type_internal_serdes) && + !e1000_sgmii_active_82575(hw)) + return; + + if (!e1000_enable_mng_pass_thru(hw)) { + /* Disable PCS to turn off link */ + reg = E1000_READ_REG(hw, E1000_PCS_CFG0); + reg &= ~E1000_PCS_CFG_PCS_EN; + E1000_WRITE_REG(hw, E1000_PCS_CFG0, reg); + + /* shutdown the laser */ + reg = E1000_READ_REG(hw, E1000_CTRL_EXT); + reg |= E1000_CTRL_EXT_SDP3_DATA; + E1000_WRITE_REG(hw, E1000_CTRL_EXT, reg); + + /* flush the write to verify completion */ + E1000_WRITE_FLUSH(hw); + msec_delay(1); + } + + return; +} + +/** + * e1000_reset_hw_82575 - Reset hardware + * @hw: pointer to the HW structure + * + * This resets the hardware into a known state. + **/ +static s32 e1000_reset_hw_82575(struct e1000_hw *hw) +{ + u32 ctrl; + s32 ret_val; + + DEBUGFUNC("e1000_reset_hw_82575"); + + /* + * Prevent the PCI-E bus from sticking if there is no TLP connection + * on the last TLP read/write transaction when MAC is reset. + */ + ret_val = e1000_disable_pcie_master_generic(hw); + if (ret_val) + DEBUGOUT("PCI-E Master disable polling has failed.\n"); + + /* set the completion timeout for interface */ + ret_val = e1000_set_pcie_completion_timeout(hw); + if (ret_val) + DEBUGOUT("PCI-E Set completion timeout has failed.\n"); + + DEBUGOUT("Masking off all interrupts\n"); + E1000_WRITE_REG(hw, E1000_IMC, 0xffffffff); + + E1000_WRITE_REG(hw, E1000_RCTL, 0); + E1000_WRITE_REG(hw, E1000_TCTL, E1000_TCTL_PSP); + E1000_WRITE_FLUSH(hw); + + msec_delay(10); + + ctrl = E1000_READ_REG(hw, E1000_CTRL); + + DEBUGOUT("Issuing a global reset to MAC\n"); + E1000_WRITE_REG(hw, E1000_CTRL, ctrl | E1000_CTRL_RST); + + ret_val = e1000_get_auto_rd_done_generic(hw); + if (ret_val) { + /* + * When auto config read does not complete, do not + * return with an error. This can happen in situations + * where there is no eeprom and prevents getting link. + */ + DEBUGOUT("Auto Read Done did not complete\n"); + } + + /* If EEPROM is not present, run manual init scripts */ + if (!(E1000_READ_REG(hw, E1000_EECD) & E1000_EECD_PRES)) + e1000_reset_init_script_82575(hw); + + /* Clear any pending interrupt events. */ + E1000_WRITE_REG(hw, E1000_IMC, 0xffffffff); + E1000_READ_REG(hw, E1000_ICR); + + /* Install any alternate MAC address into RAR0 */ + ret_val = e1000_check_alt_mac_addr_generic(hw); + + return ret_val; +} + +/** + * e1000_init_hw_82575 - Initialize hardware + * @hw: pointer to the HW structure + * + * This inits the hardware readying it for operation. + **/ +s32 e1000_init_hw_82575(struct e1000_hw *hw) +{ + struct e1000_mac_info *mac = &hw->mac; + s32 ret_val; + u16 i, rar_count = mac->rar_entry_count; + + DEBUGFUNC("e1000_init_hw_82575"); + + /* Initialize identification LED */ + ret_val = mac->ops.id_led_init(hw); + if (ret_val) { + DEBUGOUT("Error initializing identification LED\n"); + /* This is not fatal and we should not stop init due to this */ + } + + /* Disabling VLAN filtering */ + DEBUGOUT("Initializing the IEEE VLAN\n"); + mac->ops.clear_vfta(hw); + + /* Setup the receive address */ + e1000_init_rx_addrs_generic(hw, rar_count); + + /* Zero out the Multicast HASH table */ + DEBUGOUT("Zeroing the MTA\n"); + for (i = 0; i < mac->mta_reg_count; i++) + E1000_WRITE_REG_ARRAY(hw, E1000_MTA, i, 0); + + /* Zero out the Unicast HASH table */ + DEBUGOUT("Zeroing the UTA\n"); + for (i = 0; i < mac->uta_reg_count; i++) + E1000_WRITE_REG_ARRAY(hw, E1000_UTA, i, 0); + + /* Setup link and flow control */ + ret_val = mac->ops.setup_link(hw); + + /* Set the default MTU size */ + hw->dev_spec._82575.mtu = 1500; + + /* + * Clear all of the statistics registers (clear on read). It is + * important that we do this after we have tried to establish link + * because the symbol error count will increment wildly if there + * is no link. + */ + e1000_clear_hw_cntrs_82575(hw); + + return ret_val; +} + +/** + * e1000_setup_copper_link_82575 - Configure copper link settings + * @hw: pointer to the HW structure + * + * Configures the link for auto-neg or forced speed and duplex. Then we check + * for link, once link is established calls to configure collision distance + * and flow control are called. + **/ +static s32 e1000_setup_copper_link_82575(struct e1000_hw *hw) +{ + u32 ctrl; + s32 ret_val; + u32 phpm_reg; + + DEBUGFUNC("e1000_setup_copper_link_82575"); + + ctrl = E1000_READ_REG(hw, E1000_CTRL); + ctrl |= E1000_CTRL_SLU; + ctrl &= ~(E1000_CTRL_FRCSPD | E1000_CTRL_FRCDPX); + E1000_WRITE_REG(hw, E1000_CTRL, ctrl); + + /* Clear Go Link Disconnect bit on supported devices */ + switch (hw->mac.type) { + case e1000_82580: + case e1000_i350: + case e1000_i210: + case e1000_i211: + phpm_reg = E1000_READ_REG(hw, E1000_82580_PHY_POWER_MGMT); + phpm_reg &= ~E1000_82580_PM_GO_LINKD; + E1000_WRITE_REG(hw, E1000_82580_PHY_POWER_MGMT, phpm_reg); + break; + default: + break; + } + + ret_val = e1000_setup_serdes_link_82575(hw); + if (ret_val) + goto out; + + if (e1000_sgmii_active_82575(hw) && !hw->phy.reset_disable) { + /* allow time for SFP cage time to power up phy */ + msec_delay(300); + + ret_val = hw->phy.ops.reset(hw); + if (ret_val) { + DEBUGOUT("Error resetting the PHY.\n"); + goto out; + } + } + switch (hw->phy.type) { + case e1000_phy_i210: + case e1000_phy_m88: + switch (hw->phy.id) { + case I347AT4_E_PHY_ID: + case M88E1112_E_PHY_ID: + case M88E1340M_E_PHY_ID: + case M88E1543_E_PHY_ID: + case M88E1512_E_PHY_ID: + case I210_I_PHY_ID: + ret_val = e1000_copper_link_setup_m88_gen2(hw); + break; + default: + ret_val = e1000_copper_link_setup_m88(hw); + break; + } + break; + case e1000_phy_igp_3: + ret_val = e1000_copper_link_setup_igp(hw); + break; + case e1000_phy_82580: + ret_val = e1000_copper_link_setup_82577(hw); + break; + default: + ret_val = -E1000_ERR_PHY; + break; + } + + if (ret_val) + goto out; + + ret_val = e1000_setup_copper_link_generic(hw); +out: + return ret_val; +} + +/** + * e1000_setup_serdes_link_82575 - Setup link for serdes + * @hw: pointer to the HW structure + * + * Configure the physical coding sub-layer (PCS) link. The PCS link is + * used on copper connections where the serialized gigabit media independent + * interface (sgmii), or serdes fiber is being used. Configures the link + * for auto-negotiation or forces speed/duplex. + **/ +static s32 e1000_setup_serdes_link_82575(struct e1000_hw *hw) +{ + u32 ctrl_ext, ctrl_reg, reg, anadv_reg; + bool pcs_autoneg; + s32 ret_val = E1000_SUCCESS; + u16 data; + + DEBUGFUNC("e1000_setup_serdes_link_82575"); + + if ((hw->phy.media_type != e1000_media_type_internal_serdes) && + !e1000_sgmii_active_82575(hw)) + return ret_val; + + /* + * On the 82575, SerDes loopback mode persists until it is + * explicitly turned off or a power cycle is performed. A read to + * the register does not indicate its status. Therefore, we ensure + * loopback mode is disabled during initialization. + */ + E1000_WRITE_REG(hw, E1000_SCTL, E1000_SCTL_DISABLE_SERDES_LOOPBACK); + + /* power on the sfp cage if present */ + ctrl_ext = E1000_READ_REG(hw, E1000_CTRL_EXT); + ctrl_ext &= ~E1000_CTRL_EXT_SDP3_DATA; + E1000_WRITE_REG(hw, E1000_CTRL_EXT, ctrl_ext); + + ctrl_reg = E1000_READ_REG(hw, E1000_CTRL); + ctrl_reg |= E1000_CTRL_SLU; + + /* set both sw defined pins on 82575/82576*/ + if (hw->mac.type == e1000_82575 || hw->mac.type == e1000_82576) + ctrl_reg |= E1000_CTRL_SWDPIN0 | E1000_CTRL_SWDPIN1; + + reg = E1000_READ_REG(hw, E1000_PCS_LCTL); + + /* default pcs_autoneg to the same setting as mac autoneg */ + pcs_autoneg = hw->mac.autoneg; + + switch (ctrl_ext & E1000_CTRL_EXT_LINK_MODE_MASK) { + case E1000_CTRL_EXT_LINK_MODE_SGMII: + /* sgmii mode lets the phy handle forcing speed/duplex */ + pcs_autoneg = true; + /* autoneg time out should be disabled for SGMII mode */ + reg &= ~(E1000_PCS_LCTL_AN_TIMEOUT); + break; + case E1000_CTRL_EXT_LINK_MODE_1000BASE_KX: + /* disable PCS autoneg and support parallel detect only */ + pcs_autoneg = false; + /* fall through to default case */ + default: + if (hw->mac.type == e1000_82575 || + hw->mac.type == e1000_82576) { + ret_val = hw->nvm.ops.read(hw, NVM_COMPAT, 1, &data); + if (ret_val) { + DEBUGOUT("NVM Read Error\n"); + return ret_val; + } + + if (data & E1000_EEPROM_PCS_AUTONEG_DISABLE_BIT) + pcs_autoneg = false; + } + + /* + * non-SGMII modes only supports a speed of 1000/Full for the + * link so it is best to just force the MAC and let the pcs + * link either autoneg or be forced to 1000/Full + */ + ctrl_reg |= E1000_CTRL_SPD_1000 | E1000_CTRL_FRCSPD | + E1000_CTRL_FD | E1000_CTRL_FRCDPX; + + /* set speed of 1000/Full if speed/duplex is forced */ + reg |= E1000_PCS_LCTL_FSV_1000 | E1000_PCS_LCTL_FDV_FULL; + break; + } + + E1000_WRITE_REG(hw, E1000_CTRL, ctrl_reg); + + /* + * New SerDes mode allows for forcing speed or autonegotiating speed + * at 1gb. Autoneg should be default set by most drivers. This is the + * mode that will be compatible with older link partners and switches. + * However, both are supported by the hardware and some drivers/tools. + */ + reg &= ~(E1000_PCS_LCTL_AN_ENABLE | E1000_PCS_LCTL_FLV_LINK_UP | + E1000_PCS_LCTL_FSD | E1000_PCS_LCTL_FORCE_LINK); + + if (pcs_autoneg) { + /* Set PCS register for autoneg */ + reg |= E1000_PCS_LCTL_AN_ENABLE | /* Enable Autoneg */ + E1000_PCS_LCTL_AN_RESTART; /* Restart autoneg */ + + /* Disable force flow control for autoneg */ + reg &= ~E1000_PCS_LCTL_FORCE_FCTRL; + + /* Configure flow control advertisement for autoneg */ + anadv_reg = E1000_READ_REG(hw, E1000_PCS_ANADV); + anadv_reg &= ~(E1000_TXCW_ASM_DIR | E1000_TXCW_PAUSE); + + switch (hw->fc.requested_mode) { + case e1000_fc_full: + case e1000_fc_rx_pause: + anadv_reg |= E1000_TXCW_ASM_DIR; + anadv_reg |= E1000_TXCW_PAUSE; + break; + case e1000_fc_tx_pause: + anadv_reg |= E1000_TXCW_ASM_DIR; + break; + default: + break; + } + + E1000_WRITE_REG(hw, E1000_PCS_ANADV, anadv_reg); + + DEBUGOUT1("Configuring Autoneg:PCS_LCTL=0x%08X\n", reg); + } else { + /* Set PCS register for forced link */ + reg |= E1000_PCS_LCTL_FSD; /* Force Speed */ + + /* Force flow control for forced link */ + reg |= E1000_PCS_LCTL_FORCE_FCTRL; + + DEBUGOUT1("Configuring Forced Link:PCS_LCTL=0x%08X\n", reg); + } + + E1000_WRITE_REG(hw, E1000_PCS_LCTL, reg); + + if (!pcs_autoneg && !e1000_sgmii_active_82575(hw)) + e1000_force_mac_fc_generic(hw); + + return ret_val; +} + +/** + * e1000_get_media_type_82575 - derives current media type. + * @hw: pointer to the HW structure + * + * The media type is chosen reflecting few settings. + * The following are taken into account: + * - link mode set in the current port Init Control Word #3 + * - current link mode settings in CSR register + * - MDIO vs. I2C PHY control interface chosen + * - SFP module media type + **/ +static s32 e1000_get_media_type_82575(struct e1000_hw *hw) +{ + struct e1000_dev_spec_82575 *dev_spec = &hw->dev_spec._82575; + s32 ret_val = E1000_SUCCESS; + u32 ctrl_ext = 0; + u32 link_mode = 0; + + /* Set internal phy as default */ + dev_spec->sgmii_active = false; + dev_spec->module_plugged = false; + + /* Get CSR setting */ + ctrl_ext = E1000_READ_REG(hw, E1000_CTRL_EXT); + + /* extract link mode setting */ + link_mode = ctrl_ext & E1000_CTRL_EXT_LINK_MODE_MASK; + + switch (link_mode) { + case E1000_CTRL_EXT_LINK_MODE_1000BASE_KX: + hw->phy.media_type = e1000_media_type_internal_serdes; + break; + case E1000_CTRL_EXT_LINK_MODE_GMII: + hw->phy.media_type = e1000_media_type_copper; + break; + case E1000_CTRL_EXT_LINK_MODE_SGMII: + /* Get phy control interface type set (MDIO vs. I2C)*/ + if (e1000_sgmii_uses_mdio_82575(hw)) { + hw->phy.media_type = e1000_media_type_copper; + dev_spec->sgmii_active = true; + break; + } + /* fall through for I2C based SGMII */ + case E1000_CTRL_EXT_LINK_MODE_PCIE_SERDES: + /* read media type from SFP EEPROM */ +#ifdef I2C_ENABLED + printk(KERN_INFO "igb_avb I2C enabled - set_sfp_media_type_82575() called"); + ret_val = e1000_set_sfp_media_type_82575(hw); +#else + printk(KERN_INFO "igb_avb I2C disabled - set_sfp_media_type_82575() not necessary"); + hw->phy.media_type = e1000_media_type_unknown; +#endif + if ((ret_val != E1000_SUCCESS) || + (hw->phy.media_type == e1000_media_type_unknown)) { + /* + * If media type was not identified then return media + * type defined by the CTRL_EXT settings. + */ + hw->phy.media_type = e1000_media_type_internal_serdes; + + if (link_mode == E1000_CTRL_EXT_LINK_MODE_SGMII) { + hw->phy.media_type = e1000_media_type_copper; + dev_spec->sgmii_active = true; + } + + break; + } + + /* do not change link mode for 100BaseFX */ + if (dev_spec->eth_flags.e100_base_fx) + break; + + /* change current link mode setting */ + ctrl_ext &= ~E1000_CTRL_EXT_LINK_MODE_MASK; + + if (hw->phy.media_type == e1000_media_type_copper) + ctrl_ext |= E1000_CTRL_EXT_LINK_MODE_SGMII; + else + ctrl_ext |= E1000_CTRL_EXT_LINK_MODE_PCIE_SERDES; + + E1000_WRITE_REG(hw, E1000_CTRL_EXT, ctrl_ext); + + break; + } + + return ret_val; +} + +/** + * e1000_set_sfp_media_type_82575 - derives SFP module media type. + * @hw: pointer to the HW structure + * + * The media type is chosen based on SFP module. + * compatibility flags retrieved from SFP ID EEPROM. + **/ +#ifdef I2C_ENABLED +static s32 e1000_set_sfp_media_type_82575(struct e1000_hw *hw) +{ + s32 ret_val = E1000_ERR_CONFIG; + u32 ctrl_ext = 0; + struct e1000_dev_spec_82575 *dev_spec = &hw->dev_spec._82575; + struct sfp_e1000_flags *eth_flags = &dev_spec->eth_flags; + u8 tranceiver_type = 0; + s32 timeout = 3; + + /* Turn I2C interface ON and power on sfp cage */ + ctrl_ext = E1000_READ_REG(hw, E1000_CTRL_EXT); + ctrl_ext &= ~E1000_CTRL_EXT_SDP3_DATA; + E1000_WRITE_REG(hw, E1000_CTRL_EXT, ctrl_ext | E1000_CTRL_I2C_ENA); + + E1000_WRITE_FLUSH(hw); + + /* Read SFP module data */ + while (timeout) { + ret_val = e1000_read_sfp_data_byte(hw, + E1000_I2CCMD_SFP_DATA_ADDR(E1000_SFF_IDENTIFIER_OFFSET), + &tranceiver_type); + if (ret_val == E1000_SUCCESS) + break; + msec_delay(100); + timeout--; + } + if (ret_val != E1000_SUCCESS) + goto out; + + ret_val = e1000_read_sfp_data_byte(hw, + E1000_I2CCMD_SFP_DATA_ADDR(E1000_SFF_ETH_FLAGS_OFFSET), + (u8 *)eth_flags); + if (ret_val != E1000_SUCCESS) + goto out; + + /* Check if there is some SFP module plugged and powered */ + if ((tranceiver_type == E1000_SFF_IDENTIFIER_SFP) || + (tranceiver_type == E1000_SFF_IDENTIFIER_SFF)) { + dev_spec->module_plugged = true; + if (eth_flags->e1000_base_lx || eth_flags->e1000_base_sx) { + hw->phy.media_type = e1000_media_type_internal_serdes; + } else if (eth_flags->e100_base_fx) { + dev_spec->sgmii_active = true; + hw->phy.media_type = e1000_media_type_internal_serdes; + } else if (eth_flags->e1000_base_t) { + dev_spec->sgmii_active = true; + hw->phy.media_type = e1000_media_type_copper; + } else { + hw->phy.media_type = e1000_media_type_unknown; + DEBUGOUT("PHY module has not been recognized\n"); + goto out; + } + } else { + hw->phy.media_type = e1000_media_type_unknown; + } + ret_val = E1000_SUCCESS; +out: + /* Restore I2C interface setting */ + E1000_WRITE_REG(hw, E1000_CTRL_EXT, ctrl_ext); + return ret_val; +} +#endif +/** + * e1000_valid_led_default_82575 - Verify a valid default LED config + * @hw: pointer to the HW structure + * @data: pointer to the NVM (EEPROM) + * + * Read the EEPROM for the current default LED configuration. If the + * LED configuration is not valid, set to a valid LED configuration. + **/ +static s32 e1000_valid_led_default_82575(struct e1000_hw *hw, u16 *data) +{ + s32 ret_val; + + DEBUGFUNC("e1000_valid_led_default_82575"); + + ret_val = hw->nvm.ops.read(hw, NVM_ID_LED_SETTINGS, 1, data); + if (ret_val) { + DEBUGOUT("NVM Read Error\n"); + goto out; + } + + if (*data == ID_LED_RESERVED_0000 || *data == ID_LED_RESERVED_FFFF) { + switch (hw->phy.media_type) { + case e1000_media_type_internal_serdes: + *data = ID_LED_DEFAULT_82575_SERDES; + break; + case e1000_media_type_copper: + default: + *data = ID_LED_DEFAULT; + break; + } + } +out: + return ret_val; +} + +/** + * e1000_sgmii_active_82575 - Return sgmii state + * @hw: pointer to the HW structure + * + * 82575 silicon has a serialized gigabit media independent interface (sgmii) + * which can be enabled for use in the embedded applications. Simply + * return the current state of the sgmii interface. + **/ +static bool e1000_sgmii_active_82575(struct e1000_hw *hw) +{ + struct e1000_dev_spec_82575 *dev_spec = &hw->dev_spec._82575; + return dev_spec->sgmii_active; +} + +/** + * e1000_reset_init_script_82575 - Inits HW defaults after reset + * @hw: pointer to the HW structure + * + * Inits recommended HW defaults after a reset when there is no EEPROM + * detected. This is only for the 82575. + **/ +static s32 e1000_reset_init_script_82575(struct e1000_hw *hw) +{ + DEBUGFUNC("e1000_reset_init_script_82575"); + + if (hw->mac.type == e1000_82575) { + DEBUGOUT("Running reset init script for 82575\n"); + /* SerDes configuration via SERDESCTRL */ + e1000_write_8bit_ctrl_reg_generic(hw, E1000_SCTL, 0x00, 0x0C); + e1000_write_8bit_ctrl_reg_generic(hw, E1000_SCTL, 0x01, 0x78); + e1000_write_8bit_ctrl_reg_generic(hw, E1000_SCTL, 0x1B, 0x23); + e1000_write_8bit_ctrl_reg_generic(hw, E1000_SCTL, 0x23, 0x15); + + /* CCM configuration via CCMCTL register */ + e1000_write_8bit_ctrl_reg_generic(hw, E1000_CCMCTL, 0x14, 0x00); + e1000_write_8bit_ctrl_reg_generic(hw, E1000_CCMCTL, 0x10, 0x00); + + /* PCIe lanes configuration */ + e1000_write_8bit_ctrl_reg_generic(hw, E1000_GIOCTL, 0x00, 0xEC); + e1000_write_8bit_ctrl_reg_generic(hw, E1000_GIOCTL, 0x61, 0xDF); + e1000_write_8bit_ctrl_reg_generic(hw, E1000_GIOCTL, 0x34, 0x05); + e1000_write_8bit_ctrl_reg_generic(hw, E1000_GIOCTL, 0x2F, 0x81); + + /* PCIe PLL Configuration */ + e1000_write_8bit_ctrl_reg_generic(hw, E1000_SCCTL, 0x02, 0x47); + e1000_write_8bit_ctrl_reg_generic(hw, E1000_SCCTL, 0x14, 0x00); + e1000_write_8bit_ctrl_reg_generic(hw, E1000_SCCTL, 0x10, 0x00); + } + + return E1000_SUCCESS; +} + +/** + * e1000_read_mac_addr_82575 - Read device MAC address + * @hw: pointer to the HW structure + **/ +static s32 e1000_read_mac_addr_82575(struct e1000_hw *hw) +{ + s32 ret_val; + + DEBUGFUNC("e1000_read_mac_addr_82575"); + + /* + * If there's an alternate MAC address place it in RAR0 + * so that it will override the Si installed default perm + * address. + */ + ret_val = e1000_check_alt_mac_addr_generic(hw); + if (ret_val) + goto out; + + ret_val = e1000_read_mac_addr_generic(hw); + +out: + return ret_val; +} + +/** + * e1000_config_collision_dist_82575 - Configure collision distance + * @hw: pointer to the HW structure + * + * Configures the collision distance to the default value and is used + * during link setup. + **/ +static void e1000_config_collision_dist_82575(struct e1000_hw *hw) +{ + u32 tctl_ext; + + DEBUGFUNC("e1000_config_collision_dist_82575"); + + tctl_ext = E1000_READ_REG(hw, E1000_TCTL_EXT); + + tctl_ext &= ~E1000_TCTL_EXT_COLD; + tctl_ext |= E1000_COLLISION_DISTANCE << E1000_TCTL_EXT_COLD_SHIFT; + + E1000_WRITE_REG(hw, E1000_TCTL_EXT, tctl_ext); + E1000_WRITE_FLUSH(hw); +} + +/** + * e1000_power_down_phy_copper_82575 - Remove link during PHY power down + * @hw: pointer to the HW structure + * + * In the case of a PHY power down to save power, or to turn off link during a + * driver unload, or wake on lan is not enabled, remove the link. + **/ +static void e1000_power_down_phy_copper_82575(struct e1000_hw *hw) +{ + struct e1000_phy_info *phy = &hw->phy; + + if (!(phy->ops.check_reset_block)) + return; + + /* If the management interface is not enabled, then power down */ + if (!(e1000_enable_mng_pass_thru(hw) || phy->ops.check_reset_block(hw))) + e1000_power_down_phy_copper(hw); + + return; +} + +/** + * e1000_clear_hw_cntrs_82575 - Clear device specific hardware counters + * @hw: pointer to the HW structure + * + * Clears the hardware counters by reading the counter registers. + **/ +static void e1000_clear_hw_cntrs_82575(struct e1000_hw *hw) +{ + DEBUGFUNC("e1000_clear_hw_cntrs_82575"); + + e1000_clear_hw_cntrs_base_generic(hw); + + E1000_READ_REG(hw, E1000_PRC64); + E1000_READ_REG(hw, E1000_PRC127); + E1000_READ_REG(hw, E1000_PRC255); + E1000_READ_REG(hw, E1000_PRC511); + E1000_READ_REG(hw, E1000_PRC1023); + E1000_READ_REG(hw, E1000_PRC1522); + E1000_READ_REG(hw, E1000_PTC64); + E1000_READ_REG(hw, E1000_PTC127); + E1000_READ_REG(hw, E1000_PTC255); + E1000_READ_REG(hw, E1000_PTC511); + E1000_READ_REG(hw, E1000_PTC1023); + E1000_READ_REG(hw, E1000_PTC1522); + + E1000_READ_REG(hw, E1000_ALGNERRC); + E1000_READ_REG(hw, E1000_RXERRC); + E1000_READ_REG(hw, E1000_TNCRS); + E1000_READ_REG(hw, E1000_CEXTERR); + E1000_READ_REG(hw, E1000_TSCTC); + E1000_READ_REG(hw, E1000_TSCTFC); + + E1000_READ_REG(hw, E1000_MGTPRC); + E1000_READ_REG(hw, E1000_MGTPDC); + E1000_READ_REG(hw, E1000_MGTPTC); + + E1000_READ_REG(hw, E1000_IAC); + E1000_READ_REG(hw, E1000_ICRXOC); + + E1000_READ_REG(hw, E1000_ICRXPTC); + E1000_READ_REG(hw, E1000_ICRXATC); + E1000_READ_REG(hw, E1000_ICTXPTC); + E1000_READ_REG(hw, E1000_ICTXATC); + E1000_READ_REG(hw, E1000_ICTXQEC); + E1000_READ_REG(hw, E1000_ICTXQMTC); + E1000_READ_REG(hw, E1000_ICRXDMTC); + + E1000_READ_REG(hw, E1000_CBTMPC); + E1000_READ_REG(hw, E1000_HTDPMC); + E1000_READ_REG(hw, E1000_CBRMPC); + E1000_READ_REG(hw, E1000_RPTHC); + E1000_READ_REG(hw, E1000_HGPTC); + E1000_READ_REG(hw, E1000_HTCBDPC); + E1000_READ_REG(hw, E1000_HGORCL); + E1000_READ_REG(hw, E1000_HGORCH); + E1000_READ_REG(hw, E1000_HGOTCL); + E1000_READ_REG(hw, E1000_HGOTCH); + E1000_READ_REG(hw, E1000_LENERRS); + + /* This register should not be read in copper configurations */ + if ((hw->phy.media_type == e1000_media_type_internal_serdes) || + e1000_sgmii_active_82575(hw)) + E1000_READ_REG(hw, E1000_SCVPC); +} + +/** + * e1000_rx_fifo_flush_82575 - Clean rx fifo after Rx enable + * @hw: pointer to the HW structure + * + * After Rx enable, if manageability is enabled then there is likely some + * bad data at the start of the fifo and possibly in the DMA fifo. This + * function clears the fifos and flushes any packets that came in as rx was + * being enabled. + **/ +void e1000_rx_fifo_flush_82575(struct e1000_hw *hw) +{ + u32 rctl, rlpml, rxdctl[4], rfctl, temp_rctl, rx_enabled; + int i, ms_wait; + + DEBUGFUNC("e1000_rx_fifo_flush_82575"); + + /* disable IPv6 options as per hardware errata */ + rfctl = E1000_READ_REG(hw, E1000_RFCTL); + rfctl |= E1000_RFCTL_IPV6_EX_DIS; + E1000_WRITE_REG(hw, E1000_RFCTL, rfctl); + + if (hw->mac.type != e1000_82575 || + !(E1000_READ_REG(hw, E1000_MANC) & E1000_MANC_RCV_TCO_EN)) + return; + + /* Disable all Rx queues */ + for (i = 0; i < 4; i++) { + rxdctl[i] = E1000_READ_REG(hw, E1000_RXDCTL(i)); + E1000_WRITE_REG(hw, E1000_RXDCTL(i), + rxdctl[i] & ~E1000_RXDCTL_QUEUE_ENABLE); + } + /* Poll all queues to verify they have shut down */ + for (ms_wait = 0; ms_wait < 10; ms_wait++) { + msec_delay(1); + rx_enabled = 0; + for (i = 0; i < 4; i++) + rx_enabled |= E1000_READ_REG(hw, E1000_RXDCTL(i)); + if (!(rx_enabled & E1000_RXDCTL_QUEUE_ENABLE)) + break; + } + + if (ms_wait == 10) + DEBUGOUT("Queue disable timed out after 10ms\n"); + + /* Clear RLPML, RCTL.SBP, RFCTL.LEF, and set RCTL.LPE so that all + * incoming packets are rejected. Set enable and wait 2ms so that + * any packet that was coming in as RCTL.EN was set is flushed + */ + E1000_WRITE_REG(hw, E1000_RFCTL, rfctl & ~E1000_RFCTL_LEF); + + rlpml = E1000_READ_REG(hw, E1000_RLPML); + E1000_WRITE_REG(hw, E1000_RLPML, 0); + + rctl = E1000_READ_REG(hw, E1000_RCTL); + temp_rctl = rctl & ~(E1000_RCTL_EN | E1000_RCTL_SBP); + temp_rctl |= E1000_RCTL_LPE; + + E1000_WRITE_REG(hw, E1000_RCTL, temp_rctl); + E1000_WRITE_REG(hw, E1000_RCTL, temp_rctl | E1000_RCTL_EN); + E1000_WRITE_FLUSH(hw); + msec_delay(2); + + /* Enable Rx queues that were previously enabled and restore our + * previous state + */ + for (i = 0; i < 4; i++) + E1000_WRITE_REG(hw, E1000_RXDCTL(i), rxdctl[i]); + E1000_WRITE_REG(hw, E1000_RCTL, rctl); + E1000_WRITE_FLUSH(hw); + + E1000_WRITE_REG(hw, E1000_RLPML, rlpml); + E1000_WRITE_REG(hw, E1000_RFCTL, rfctl); + + /* Flush receive errors generated by workaround */ + E1000_READ_REG(hw, E1000_ROC); + E1000_READ_REG(hw, E1000_RNBC); + E1000_READ_REG(hw, E1000_MPC); +} + +/** + * e1000_set_pcie_completion_timeout - set pci-e completion timeout + * @hw: pointer to the HW structure + * + * The defaults for 82575 and 82576 should be in the range of 50us to 50ms, + * however the hardware default for these parts is 500us to 1ms which is less + * than the 10ms recommended by the pci-e spec. To address this we need to + * increase the value to either 10ms to 200ms for capability version 1 config, + * or 16ms to 55ms for version 2. + **/ +static s32 e1000_set_pcie_completion_timeout(struct e1000_hw *hw) +{ + u32 gcr = E1000_READ_REG(hw, E1000_GCR); + s32 ret_val = E1000_SUCCESS; + u16 pcie_devctl2; + + /* only take action if timeout value is defaulted to 0 */ + if (gcr & E1000_GCR_CMPL_TMOUT_MASK) + goto out; + + /* + * if capababilities version is type 1 we can write the + * timeout of 10ms to 200ms through the GCR register + */ + if (!(gcr & E1000_GCR_CAP_VER2)) { + gcr |= E1000_GCR_CMPL_TMOUT_10ms; + goto out; + } + + /* + * for version 2 capabilities we need to write the config space + * directly in order to set the completion timeout value for + * 16ms to 55ms + */ + ret_val = e1000_read_pcie_cap_reg(hw, PCIE_DEVICE_CONTROL2, + &pcie_devctl2); + if (ret_val) + goto out; + + pcie_devctl2 |= PCIE_DEVICE_CONTROL2_16ms; + + ret_val = e1000_write_pcie_cap_reg(hw, PCIE_DEVICE_CONTROL2, + &pcie_devctl2); +out: + /* disable completion timeout resend */ + gcr &= ~E1000_GCR_CMPL_TMOUT_RESEND; + + E1000_WRITE_REG(hw, E1000_GCR, gcr); + return ret_val; +} + +/** + * e1000_vmdq_set_anti_spoofing_pf - enable or disable anti-spoofing + * @hw: pointer to the hardware struct + * @enable: state to enter, either enabled or disabled + * @pf: Physical Function pool - do not set anti-spoofing for the PF + * + * enables/disables L2 switch anti-spoofing functionality. + **/ +void e1000_vmdq_set_anti_spoofing_pf(struct e1000_hw *hw, bool enable, int pf) +{ + u32 reg_val, reg_offset; + + switch (hw->mac.type) { + case e1000_82576: + reg_offset = E1000_DTXSWC; + break; + case e1000_i350: + case e1000_i354: + reg_offset = E1000_TXSWC; + break; + default: + return; + } + + reg_val = E1000_READ_REG(hw, reg_offset); + if (enable) { + reg_val |= (E1000_DTXSWC_MAC_SPOOF_MASK | + E1000_DTXSWC_VLAN_SPOOF_MASK); + /* The PF can spoof - it has to in order to + * support emulation mode NICs + */ + reg_val ^= (1 << pf | 1 << (pf + MAX_NUM_VFS)); + } else { + reg_val &= ~(E1000_DTXSWC_MAC_SPOOF_MASK | + E1000_DTXSWC_VLAN_SPOOF_MASK); + } + E1000_WRITE_REG(hw, reg_offset, reg_val); +} + +/** + * e1000_vmdq_set_loopback_pf - enable or disable vmdq loopback + * @hw: pointer to the hardware struct + * @enable: state to enter, either enabled or disabled + * + * enables/disables L2 switch loopback functionality. + **/ +void e1000_vmdq_set_loopback_pf(struct e1000_hw *hw, bool enable) +{ + u32 dtxswc; + + switch (hw->mac.type) { + case e1000_82576: + dtxswc = E1000_READ_REG(hw, E1000_DTXSWC); + if (enable) + dtxswc |= E1000_DTXSWC_VMDQ_LOOPBACK_EN; + else + dtxswc &= ~E1000_DTXSWC_VMDQ_LOOPBACK_EN; + E1000_WRITE_REG(hw, E1000_DTXSWC, dtxswc); + break; + case e1000_i350: + case e1000_i354: + dtxswc = E1000_READ_REG(hw, E1000_TXSWC); + if (enable) + dtxswc |= E1000_DTXSWC_VMDQ_LOOPBACK_EN; + else + dtxswc &= ~E1000_DTXSWC_VMDQ_LOOPBACK_EN; + E1000_WRITE_REG(hw, E1000_TXSWC, dtxswc); + break; + default: + /* Currently no other hardware supports loopback */ + break; + } + + +} + +/** + * e1000_vmdq_set_replication_pf - enable or disable vmdq replication + * @hw: pointer to the hardware struct + * @enable: state to enter, either enabled or disabled + * + * enables/disables replication of packets across multiple pools. + **/ +void e1000_vmdq_set_replication_pf(struct e1000_hw *hw, bool enable) +{ + u32 vt_ctl = E1000_READ_REG(hw, E1000_VT_CTL); + + if (enable) + vt_ctl |= E1000_VT_CTL_VM_REPL_EN; + else + vt_ctl &= ~E1000_VT_CTL_VM_REPL_EN; + + E1000_WRITE_REG(hw, E1000_VT_CTL, vt_ctl); +} + +/** + * e1000_read_phy_reg_82580 - Read 82580 MDI control register + * @hw: pointer to the HW structure + * @offset: register offset to be read + * @data: pointer to the read data + * + * Reads the MDI control register in the PHY at offset and stores the + * information read to data. + **/ +static s32 e1000_read_phy_reg_82580(struct e1000_hw *hw, u32 offset, u16 *data) +{ + s32 ret_val; + + DEBUGFUNC("e1000_read_phy_reg_82580"); + + ret_val = hw->phy.ops.acquire(hw); + if (ret_val) + goto out; + + ret_val = e1000_read_phy_reg_mdic(hw, offset, data); + + hw->phy.ops.release(hw); + +out: + return ret_val; +} + +/** + * e1000_write_phy_reg_82580 - Write 82580 MDI control register + * @hw: pointer to the HW structure + * @offset: register offset to write to + * @data: data to write to register at offset + * + * Writes data to MDI control register in the PHY at offset. + **/ +static s32 e1000_write_phy_reg_82580(struct e1000_hw *hw, u32 offset, u16 data) +{ + s32 ret_val; + + DEBUGFUNC("e1000_write_phy_reg_82580"); + + ret_val = hw->phy.ops.acquire(hw); + if (ret_val) + goto out; + + ret_val = e1000_write_phy_reg_mdic(hw, offset, data); + + hw->phy.ops.release(hw); + +out: + return ret_val; +} + +/** + * e1000_reset_mdicnfg_82580 - Reset MDICNFG destination and com_mdio bits + * @hw: pointer to the HW structure + * + * This resets the the MDICNFG.Destination and MDICNFG.Com_MDIO bits based on + * the values found in the EEPROM. This addresses an issue in which these + * bits are not restored from EEPROM after reset. + **/ +static s32 e1000_reset_mdicnfg_82580(struct e1000_hw *hw) +{ + s32 ret_val = E1000_SUCCESS; + u32 mdicnfg; + u16 nvm_data = 0; + + DEBUGFUNC("e1000_reset_mdicnfg_82580"); + + if (hw->mac.type != e1000_82580) + goto out; + if (!e1000_sgmii_active_82575(hw)) + goto out; + + ret_val = hw->nvm.ops.read(hw, NVM_INIT_CONTROL3_PORT_A + + NVM_82580_LAN_FUNC_OFFSET(hw->bus.func), 1, + &nvm_data); + if (ret_val) { + DEBUGOUT("NVM Read Error\n"); + goto out; + } + + mdicnfg = E1000_READ_REG(hw, E1000_MDICNFG); + if (nvm_data & NVM_WORD24_EXT_MDIO) + mdicnfg |= E1000_MDICNFG_EXT_MDIO; + if (nvm_data & NVM_WORD24_COM_MDIO) + mdicnfg |= E1000_MDICNFG_COM_MDIO; + E1000_WRITE_REG(hw, E1000_MDICNFG, mdicnfg); +out: + return ret_val; +} + +/** + * e1000_reset_hw_82580 - Reset hardware + * @hw: pointer to the HW structure + * + * This resets function or entire device (all ports, etc.) + * to a known state. + **/ +static s32 e1000_reset_hw_82580(struct e1000_hw *hw) +{ + s32 ret_val = E1000_SUCCESS; + /* BH SW mailbox bit in SW_FW_SYNC */ + u16 swmbsw_mask = E1000_SW_SYNCH_MB; + u32 ctrl; + bool global_device_reset = hw->dev_spec._82575.global_device_reset; + + DEBUGFUNC("e1000_reset_hw_82580"); + + hw->dev_spec._82575.global_device_reset = false; + + /* 82580 does not reliably do global_device_reset due to hw errata */ + if (hw->mac.type == e1000_82580) + global_device_reset = false; + + /* Get current control state. */ + ctrl = E1000_READ_REG(hw, E1000_CTRL); + + /* + * Prevent the PCI-E bus from sticking if there is no TLP connection + * on the last TLP read/write transaction when MAC is reset. + */ + ret_val = e1000_disable_pcie_master_generic(hw); + if (ret_val) + DEBUGOUT("PCI-E Master disable polling has failed.\n"); + + DEBUGOUT("Masking off all interrupts\n"); + E1000_WRITE_REG(hw, E1000_IMC, 0xffffffff); + E1000_WRITE_REG(hw, E1000_RCTL, 0); + E1000_WRITE_REG(hw, E1000_TCTL, E1000_TCTL_PSP); + E1000_WRITE_FLUSH(hw); + + msec_delay(10); + + /* Determine whether or not a global dev reset is requested */ + if (global_device_reset && hw->mac.ops.acquire_swfw_sync(hw, + swmbsw_mask)) + global_device_reset = false; + + if (global_device_reset && !(E1000_READ_REG(hw, E1000_STATUS) & + E1000_STAT_DEV_RST_SET)) + ctrl |= E1000_CTRL_DEV_RST; + else + ctrl |= E1000_CTRL_RST; + + E1000_WRITE_REG(hw, E1000_CTRL, ctrl); + + switch (hw->device_id) { + case E1000_DEV_ID_DH89XXCC_SGMII: + break; + default: + E1000_WRITE_FLUSH(hw); + break; + } + + /* Add delay to insure DEV_RST or RST has time to complete */ + msec_delay(5); + + ret_val = e1000_get_auto_rd_done_generic(hw); + if (ret_val) { + /* + * When auto config read does not complete, do not + * return with an error. This can happen in situations + * where there is no eeprom and prevents getting link. + */ + DEBUGOUT("Auto Read Done did not complete\n"); + } + + /* clear global device reset status bit */ + E1000_WRITE_REG(hw, E1000_STATUS, E1000_STAT_DEV_RST_SET); + + /* Clear any pending interrupt events. */ + E1000_WRITE_REG(hw, E1000_IMC, 0xffffffff); + E1000_READ_REG(hw, E1000_ICR); + + ret_val = e1000_reset_mdicnfg_82580(hw); + if (ret_val) + DEBUGOUT("Could not reset MDICNFG based on EEPROM\n"); + + /* Install any alternate MAC address into RAR0 */ + ret_val = e1000_check_alt_mac_addr_generic(hw); + + /* Release semaphore */ + if (global_device_reset) + hw->mac.ops.release_swfw_sync(hw, swmbsw_mask); + + return ret_val; +} + +/** + * e1000_rxpbs_adjust_82580 - adjust RXPBS value to reflect actual Rx PBA size + * @data: data received by reading RXPBS register + * + * The 82580 uses a table based approach for packet buffer allocation sizes. + * This function converts the retrieved value into the correct table value + * 0x0 0x1 0x2 0x3 0x4 0x5 0x6 0x7 + * 0x0 36 72 144 1 2 4 8 16 + * 0x8 35 70 140 rsv rsv rsv rsv rsv + */ +u16 e1000_rxpbs_adjust_82580(u32 data) +{ + u16 ret_val = 0; + + if (data < E1000_82580_RXPBS_TABLE_SIZE) + ret_val = e1000_82580_rxpbs_table[data]; + + return ret_val; +} + +/** + * e1000_validate_nvm_checksum_with_offset - Validate EEPROM + * checksum + * @hw: pointer to the HW structure + * @offset: offset in words of the checksum protected region + * + * Calculates the EEPROM checksum by reading/adding each word of the EEPROM + * and then verifies that the sum of the EEPROM is equal to 0xBABA. + **/ +s32 e1000_validate_nvm_checksum_with_offset(struct e1000_hw *hw, u16 offset) +{ + s32 ret_val = E1000_SUCCESS; + u16 checksum = 0; + u16 i, nvm_data; + + DEBUGFUNC("e1000_validate_nvm_checksum_with_offset"); + + for (i = offset; i < ((NVM_CHECKSUM_REG + offset) + 1); i++) { + ret_val = hw->nvm.ops.read(hw, i, 1, &nvm_data); + if (ret_val) { + DEBUGOUT("NVM Read Error\n"); + goto out; + } + checksum += nvm_data; + } + + if (checksum != (u16) NVM_SUM) { + DEBUGOUT("NVM Checksum Invalid\n"); + ret_val = -E1000_ERR_NVM; + goto out; + } + +out: + return ret_val; +} + +/** + * e1000_update_nvm_checksum_with_offset - Update EEPROM + * checksum + * @hw: pointer to the HW structure + * @offset: offset in words of the checksum protected region + * + * Updates the EEPROM checksum by reading/adding each word of the EEPROM + * up to the checksum. Then calculates the EEPROM checksum and writes the + * value to the EEPROM. + **/ +s32 e1000_update_nvm_checksum_with_offset(struct e1000_hw *hw, u16 offset) +{ + s32 ret_val; + u16 checksum = 0; + u16 i, nvm_data; + + DEBUGFUNC("e1000_update_nvm_checksum_with_offset"); + + for (i = offset; i < (NVM_CHECKSUM_REG + offset); i++) { + ret_val = hw->nvm.ops.read(hw, i, 1, &nvm_data); + if (ret_val) { + DEBUGOUT("NVM Read Error while updating checksum.\n"); + goto out; + } + checksum += nvm_data; + } + checksum = (u16) NVM_SUM - checksum; + ret_val = hw->nvm.ops.write(hw, (NVM_CHECKSUM_REG + offset), 1, + &checksum); + if (ret_val) + DEBUGOUT("NVM Write Error while updating checksum.\n"); + +out: + return ret_val; +} + +/** + * e1000_validate_nvm_checksum_82580 - Validate EEPROM checksum + * @hw: pointer to the HW structure + * + * Calculates the EEPROM section checksum by reading/adding each word of + * the EEPROM and then verifies that the sum of the EEPROM is + * equal to 0xBABA. + **/ +static s32 e1000_validate_nvm_checksum_82580(struct e1000_hw *hw) +{ + s32 ret_val; + u16 eeprom_regions_count = 1; + u16 j, nvm_data; + u16 nvm_offset; + + DEBUGFUNC("e1000_validate_nvm_checksum_82580"); + + ret_val = hw->nvm.ops.read(hw, NVM_COMPATIBILITY_REG_3, 1, &nvm_data); + if (ret_val) { + DEBUGOUT("NVM Read Error\n"); + goto out; + } + + if (nvm_data & NVM_COMPATIBILITY_BIT_MASK) { + /* if chekcsums compatibility bit is set validate checksums + * for all 4 ports. */ + eeprom_regions_count = 4; + } + + for (j = 0; j < eeprom_regions_count; j++) { + nvm_offset = NVM_82580_LAN_FUNC_OFFSET(j); + ret_val = e1000_validate_nvm_checksum_with_offset(hw, + nvm_offset); + if (ret_val != E1000_SUCCESS) + goto out; + } + +out: + return ret_val; +} + +/** + * e1000_update_nvm_checksum_82580 - Update EEPROM checksum + * @hw: pointer to the HW structure + * + * Updates the EEPROM section checksums for all 4 ports by reading/adding + * each word of the EEPROM up to the checksum. Then calculates the EEPROM + * checksum and writes the value to the EEPROM. + **/ +static s32 e1000_update_nvm_checksum_82580(struct e1000_hw *hw) +{ + s32 ret_val; + u16 j, nvm_data; + u16 nvm_offset; + + DEBUGFUNC("e1000_update_nvm_checksum_82580"); + + ret_val = hw->nvm.ops.read(hw, NVM_COMPATIBILITY_REG_3, 1, &nvm_data); + if (ret_val) { + DEBUGOUT("NVM Read Error while updating checksum compatibility bit.\n"); + goto out; + } + + if (!(nvm_data & NVM_COMPATIBILITY_BIT_MASK)) { + /* set compatibility bit to validate checksums appropriately */ + nvm_data = nvm_data | NVM_COMPATIBILITY_BIT_MASK; + ret_val = hw->nvm.ops.write(hw, NVM_COMPATIBILITY_REG_3, 1, + &nvm_data); + if (ret_val) { + DEBUGOUT("NVM Write Error while updating checksum compatibility bit.\n"); + goto out; + } + } + + for (j = 0; j < 4; j++) { + nvm_offset = NVM_82580_LAN_FUNC_OFFSET(j); + ret_val = e1000_update_nvm_checksum_with_offset(hw, nvm_offset); + if (ret_val) + goto out; + } + +out: + return ret_val; +} + +/** + * e1000_validate_nvm_checksum_i350 - Validate EEPROM checksum + * @hw: pointer to the HW structure + * + * Calculates the EEPROM section checksum by reading/adding each word of + * the EEPROM and then verifies that the sum of the EEPROM is + * equal to 0xBABA. + **/ +static s32 e1000_validate_nvm_checksum_i350(struct e1000_hw *hw) +{ + s32 ret_val = E1000_SUCCESS; + u16 j; + u16 nvm_offset; + + DEBUGFUNC("e1000_validate_nvm_checksum_i350"); + + for (j = 0; j < 4; j++) { + nvm_offset = NVM_82580_LAN_FUNC_OFFSET(j); + ret_val = e1000_validate_nvm_checksum_with_offset(hw, + nvm_offset); + if (ret_val != E1000_SUCCESS) + goto out; + } + +out: + return ret_val; +} + +/** + * e1000_update_nvm_checksum_i350 - Update EEPROM checksum + * @hw: pointer to the HW structure + * + * Updates the EEPROM section checksums for all 4 ports by reading/adding + * each word of the EEPROM up to the checksum. Then calculates the EEPROM + * checksum and writes the value to the EEPROM. + **/ +static s32 e1000_update_nvm_checksum_i350(struct e1000_hw *hw) +{ + s32 ret_val = E1000_SUCCESS; + u16 j; + u16 nvm_offset; + + DEBUGFUNC("e1000_update_nvm_checksum_i350"); + + for (j = 0; j < 4; j++) { + nvm_offset = NVM_82580_LAN_FUNC_OFFSET(j); + ret_val = e1000_update_nvm_checksum_with_offset(hw, nvm_offset); + if (ret_val != E1000_SUCCESS) + goto out; + } + +out: + return ret_val; +} + +/** + * __e1000_access_emi_reg - Read/write EMI register + * @hw: pointer to the HW structure + * @addr: EMI address to program + * @data: pointer to value to read/write from/to the EMI address + * @read: boolean flag to indicate read or write + **/ +static s32 __e1000_access_emi_reg(struct e1000_hw *hw, u16 address, + u16 *data, bool read) +{ + s32 ret_val; + + DEBUGFUNC("__e1000_access_emi_reg"); + + ret_val = hw->phy.ops.write_reg(hw, E1000_EMIADD, address); + if (ret_val) + return ret_val; + + if (read) + ret_val = hw->phy.ops.read_reg(hw, E1000_EMIDATA, data); + else + ret_val = hw->phy.ops.write_reg(hw, E1000_EMIDATA, *data); + + return ret_val; +} + +/** + * e1000_read_emi_reg - Read Extended Management Interface register + * @hw: pointer to the HW structure + * @addr: EMI address to program + * @data: value to be read from the EMI address + **/ +s32 e1000_read_emi_reg(struct e1000_hw *hw, u16 addr, u16 *data) +{ + DEBUGFUNC("e1000_read_emi_reg"); + + return __e1000_access_emi_reg(hw, addr, data, true); +} + +/** + * e1000_initialize_M88E1512_phy - Initialize M88E1512 PHY + * @hw: pointer to the HW structure + * + * Initialize Marverl 1512 to work correctly with Avoton. + **/ +s32 e1000_initialize_M88E1512_phy(struct e1000_hw *hw) +{ + struct e1000_phy_info *phy = &hw->phy; + s32 ret_val = E1000_SUCCESS; + + DEBUGFUNC("e1000_initialize_M88E1512_phy"); + + /* Check if this is correct PHY. */ + if (phy->id != M88E1512_E_PHY_ID) + goto out; + + /* Switch to PHY page 0xFF. */ + ret_val = phy->ops.write_reg(hw, E1000_M88E1543_PAGE_ADDR, 0x00FF); + if (ret_val) + goto out; + + ret_val = phy->ops.write_reg(hw, E1000_M88E1512_CFG_REG_2, 0x214B); + if (ret_val) + goto out; + + ret_val = phy->ops.write_reg(hw, E1000_M88E1512_CFG_REG_1, 0x2144); + if (ret_val) + goto out; + + ret_val = phy->ops.write_reg(hw, E1000_M88E1512_CFG_REG_2, 0x0C28); + if (ret_val) + goto out; + + ret_val = phy->ops.write_reg(hw, E1000_M88E1512_CFG_REG_1, 0x2146); + if (ret_val) + goto out; + + ret_val = phy->ops.write_reg(hw, E1000_M88E1512_CFG_REG_2, 0xB233); + if (ret_val) + goto out; + + ret_val = phy->ops.write_reg(hw, E1000_M88E1512_CFG_REG_1, 0x214D); + if (ret_val) + goto out; + + ret_val = phy->ops.write_reg(hw, E1000_M88E1512_CFG_REG_2, 0xCC0C); + if (ret_val) + goto out; + + ret_val = phy->ops.write_reg(hw, E1000_M88E1512_CFG_REG_1, 0x2159); + if (ret_val) + goto out; + + /* Switch to PHY page 0xFB. */ + ret_val = phy->ops.write_reg(hw, E1000_M88E1543_PAGE_ADDR, 0x00FB); + if (ret_val) + goto out; + + ret_val = phy->ops.write_reg(hw, E1000_M88E1512_CFG_REG_3, 0x000D); + if (ret_val) + goto out; + + /* Switch to PHY page 0x12. */ + ret_val = phy->ops.write_reg(hw, E1000_M88E1543_PAGE_ADDR, 0x12); + if (ret_val) + goto out; + + /* Change mode to SGMII-to-Copper */ + ret_val = phy->ops.write_reg(hw, E1000_M88E1512_MODE, 0x8001); + if (ret_val) + goto out; + + /* Return the PHY to page 0. */ + ret_val = phy->ops.write_reg(hw, E1000_M88E1543_PAGE_ADDR, 0); + if (ret_val) + goto out; + + ret_val = phy->ops.commit(hw); + if (ret_val) { + DEBUGOUT("Error committing the PHY changes\n"); + return ret_val; + } + + msec_delay(1000); +out: + return ret_val; +} + +/** + * e1000_set_eee_i350 - Enable/disable EEE support + * @hw: pointer to the HW structure + * @adv1g: boolean flag enabling 1G EEE advertisement + * @adv100m: boolean flag enabling 100M EEE advertisement + * + * Enable/disable EEE based on setting in dev_spec structure. + * + **/ +s32 e1000_set_eee_i350(struct e1000_hw *hw, bool adv1G, bool adv100M) +{ + u32 ipcnfg, eeer; + + DEBUGFUNC("e1000_set_eee_i350"); + + if ((hw->mac.type < e1000_i350) || + (hw->phy.media_type != e1000_media_type_copper)) + goto out; + ipcnfg = E1000_READ_REG(hw, E1000_IPCNFG); + eeer = E1000_READ_REG(hw, E1000_EEER); + + /* enable or disable per user setting */ + if (!(hw->dev_spec._82575.eee_disable)) { + u32 eee_su = E1000_READ_REG(hw, E1000_EEE_SU); + + if (adv100M) + ipcnfg |= E1000_IPCNFG_EEE_100M_AN; + else + ipcnfg &= ~E1000_IPCNFG_EEE_100M_AN; + + if (adv1G) + ipcnfg |= E1000_IPCNFG_EEE_1G_AN; + else + ipcnfg &= ~E1000_IPCNFG_EEE_1G_AN; + + eeer |= (E1000_EEER_TX_LPI_EN | E1000_EEER_RX_LPI_EN | + E1000_EEER_LPI_FC); + + /* This bit should not be set in normal operation. */ + if (eee_su & E1000_EEE_SU_LPI_CLK_STP) + DEBUGOUT("LPI Clock Stop Bit should not be set!\n"); + } else { + ipcnfg &= ~(E1000_IPCNFG_EEE_1G_AN | E1000_IPCNFG_EEE_100M_AN); + eeer &= ~(E1000_EEER_TX_LPI_EN | E1000_EEER_RX_LPI_EN | + E1000_EEER_LPI_FC); + } + E1000_WRITE_REG(hw, E1000_IPCNFG, ipcnfg); + E1000_WRITE_REG(hw, E1000_EEER, eeer); + E1000_READ_REG(hw, E1000_IPCNFG); + E1000_READ_REG(hw, E1000_EEER); +out: + + return E1000_SUCCESS; +} + +/** + * e1000_set_eee_i354 - Enable/disable EEE support + * @hw: pointer to the HW structure + * @adv1g: boolean flag enabling 1G EEE advertisement + * @adv100m: boolean flag enabling 100M EEE advertisement + * + * Enable/disable EEE legacy mode based on setting in dev_spec structure. + * + **/ +s32 e1000_set_eee_i354(struct e1000_hw *hw, bool adv1G, bool adv100M) +{ + struct e1000_phy_info *phy = &hw->phy; + s32 ret_val = E1000_SUCCESS; + u16 phy_data; + + DEBUGFUNC("e1000_set_eee_i354"); + + if ((hw->phy.media_type != e1000_media_type_copper) || + ((phy->id != M88E1543_E_PHY_ID) && + (phy->id != M88E1512_E_PHY_ID))) + goto out; + + if (!hw->dev_spec._82575.eee_disable) { + /* Switch to PHY page 18. */ + ret_val = phy->ops.write_reg(hw, E1000_M88E1543_PAGE_ADDR, 18); + if (ret_val) + goto out; + + ret_val = phy->ops.read_reg(hw, E1000_M88E1543_EEE_CTRL_1, + &phy_data); + if (ret_val) + goto out; + + phy_data |= E1000_M88E1543_EEE_CTRL_1_MS; + ret_val = phy->ops.write_reg(hw, E1000_M88E1543_EEE_CTRL_1, + phy_data); + if (ret_val) + goto out; + + /* Return the PHY to page 0. */ + ret_val = phy->ops.write_reg(hw, E1000_M88E1543_PAGE_ADDR, 0); + if (ret_val) + goto out; + + /* Turn on EEE advertisement. */ + ret_val = e1000_read_xmdio_reg(hw, E1000_EEE_ADV_ADDR_I354, + E1000_EEE_ADV_DEV_I354, + &phy_data); + if (ret_val) + goto out; + + if (adv100M) + phy_data |= E1000_EEE_ADV_100_SUPPORTED; + else + phy_data &= ~E1000_EEE_ADV_100_SUPPORTED; + + if (adv1G) + phy_data |= E1000_EEE_ADV_1000_SUPPORTED; + else + phy_data &= ~E1000_EEE_ADV_1000_SUPPORTED; + + ret_val = e1000_write_xmdio_reg(hw, E1000_EEE_ADV_ADDR_I354, + E1000_EEE_ADV_DEV_I354, + phy_data); + } else { + /* Turn off EEE advertisement. */ + ret_val = e1000_read_xmdio_reg(hw, E1000_EEE_ADV_ADDR_I354, + E1000_EEE_ADV_DEV_I354, + &phy_data); + if (ret_val) + goto out; + + phy_data &= ~(E1000_EEE_ADV_100_SUPPORTED | + E1000_EEE_ADV_1000_SUPPORTED); + ret_val = e1000_write_xmdio_reg(hw, E1000_EEE_ADV_ADDR_I354, + E1000_EEE_ADV_DEV_I354, + phy_data); + } + +out: + return ret_val; +} + +/** + * e1000_get_eee_status_i354 - Get EEE status + * @hw: pointer to the HW structure + * @status: EEE status + * + * Get EEE status by guessing based on whether Tx or Rx LPI indications have + * been received. + **/ +s32 e1000_get_eee_status_i354(struct e1000_hw *hw, bool *status) +{ + struct e1000_phy_info *phy = &hw->phy; + s32 ret_val = E1000_SUCCESS; + u16 phy_data; + + DEBUGFUNC("e1000_get_eee_status_i354"); + + /* Check if EEE is supported on this device. */ + if ((hw->phy.media_type != e1000_media_type_copper) || + ((phy->id != M88E1543_E_PHY_ID) && + (phy->id != M88E1512_E_PHY_ID))) + goto out; + + ret_val = e1000_read_xmdio_reg(hw, E1000_PCS_STATUS_ADDR_I354, + E1000_PCS_STATUS_DEV_I354, + &phy_data); + if (ret_val) + goto out; + + *status = phy_data & (E1000_PCS_STATUS_TX_LPI_RCVD | + E1000_PCS_STATUS_RX_LPI_RCVD) ? true : false; + +out: + return ret_val; +} + +/* Due to a hw errata, if the host tries to configure the VFTA register + * while performing queries from the BMC or DMA, then the VFTA in some + * cases won't be written. + */ + +/** + * e1000_clear_vfta_i350 - Clear VLAN filter table + * @hw: pointer to the HW structure + * + * Clears the register array which contains the VLAN filter table by + * setting all the values to 0. + **/ +void e1000_clear_vfta_i350(struct e1000_hw *hw) +{ + u32 offset; + int i; + + DEBUGFUNC("e1000_clear_vfta_350"); + + for (offset = 0; offset < E1000_VLAN_FILTER_TBL_SIZE; offset++) { + for (i = 0; i < 10; i++) + E1000_WRITE_REG_ARRAY(hw, E1000_VFTA, offset, 0); + + E1000_WRITE_FLUSH(hw); + } +} + +/** + * e1000_write_vfta_i350 - Write value to VLAN filter table + * @hw: pointer to the HW structure + * @offset: register offset in VLAN filter table + * @value: register value written to VLAN filter table + * + * Writes value at the given offset in the register array which stores + * the VLAN filter table. + **/ +void e1000_write_vfta_i350(struct e1000_hw *hw, u32 offset, u32 value) +{ + int i; + + DEBUGFUNC("e1000_write_vfta_350"); + + for (i = 0; i < 10; i++) + E1000_WRITE_REG_ARRAY(hw, E1000_VFTA, offset, value); + + E1000_WRITE_FLUSH(hw); +} + +/** + * e1000_set_i2c_bb - Enable I2C bit-bang + * @hw: pointer to the HW structure + * + * Enable I2C bit-bang interface + * + **/ +s32 e1000_set_i2c_bb(struct e1000_hw *hw) +{ + s32 ret_val = E1000_SUCCESS; + u32 ctrl_ext, i2cparams; + + DEBUGFUNC("e1000_set_i2c_bb"); + + ctrl_ext = E1000_READ_REG(hw, E1000_CTRL_EXT); + ctrl_ext |= E1000_CTRL_I2C_ENA; + E1000_WRITE_REG(hw, E1000_CTRL_EXT, ctrl_ext); + E1000_WRITE_FLUSH(hw); + + i2cparams = E1000_READ_REG(hw, E1000_I2CPARAMS); + i2cparams |= E1000_I2CBB_EN; + i2cparams |= E1000_I2C_DATA_OE_N; + i2cparams |= E1000_I2C_CLK_OE_N; + E1000_WRITE_REG(hw, E1000_I2CPARAMS, i2cparams); + E1000_WRITE_FLUSH(hw); + + return ret_val; +} + +/** + * e1000_read_i2c_byte_generic - Reads 8 bit word over I2C + * @hw: pointer to hardware structure + * @byte_offset: byte offset to read + * @dev_addr: device address + * @data: value read + * + * Performs byte read operation over I2C interface at + * a specified device address. + **/ +s32 e1000_read_i2c_byte_generic(struct e1000_hw *hw, u8 byte_offset, + u8 dev_addr, u8 *data) +{ + s32 status = E1000_SUCCESS; + u32 max_retry = 10; + u32 retry = 1; + u16 swfw_mask = 0; + + bool nack = true; + + DEBUGFUNC("e1000_read_i2c_byte_generic"); + + swfw_mask = E1000_SWFW_PHY0_SM; + + do { + if (hw->mac.ops.acquire_swfw_sync(hw, swfw_mask) + != E1000_SUCCESS) { + status = E1000_ERR_SWFW_SYNC; + goto read_byte_out; + } + + e1000_i2c_start(hw); + + /* Device Address and write indication */ + status = e1000_clock_out_i2c_byte(hw, dev_addr); + if (status != E1000_SUCCESS) + goto fail; + + status = e1000_get_i2c_ack(hw); + if (status != E1000_SUCCESS) + goto fail; + + status = e1000_clock_out_i2c_byte(hw, byte_offset); + if (status != E1000_SUCCESS) + goto fail; + + status = e1000_get_i2c_ack(hw); + if (status != E1000_SUCCESS) + goto fail; + + e1000_i2c_start(hw); + + /* Device Address and read indication */ + status = e1000_clock_out_i2c_byte(hw, (dev_addr | 0x1)); + if (status != E1000_SUCCESS) + goto fail; + + status = e1000_get_i2c_ack(hw); + if (status != E1000_SUCCESS) + goto fail; + + e1000_clock_in_i2c_byte(hw, data); + + status = e1000_clock_out_i2c_bit(hw, nack); + if (status != E1000_SUCCESS) + goto fail; + + e1000_i2c_stop(hw); + break; + +fail: + hw->mac.ops.release_swfw_sync(hw, swfw_mask); + msec_delay(100); + e1000_i2c_bus_clear(hw); + retry++; + if (retry < max_retry) + DEBUGOUT("I2C byte read error - Retrying.\n"); + else + DEBUGOUT("I2C byte read error.\n"); + + } while (retry < max_retry); + + hw->mac.ops.release_swfw_sync(hw, swfw_mask); + +read_byte_out: + + return status; +} + +/** + * e1000_write_i2c_byte_generic - Writes 8 bit word over I2C + * @hw: pointer to hardware structure + * @byte_offset: byte offset to write + * @dev_addr: device address + * @data: value to write + * + * Performs byte write operation over I2C interface at + * a specified device address. + **/ +s32 e1000_write_i2c_byte_generic(struct e1000_hw *hw, u8 byte_offset, + u8 dev_addr, u8 data) +{ + s32 status = E1000_SUCCESS; + u32 max_retry = 1; + u32 retry = 0; + u16 swfw_mask = 0; + + DEBUGFUNC("e1000_write_i2c_byte_generic"); + + swfw_mask = E1000_SWFW_PHY0_SM; + + if (hw->mac.ops.acquire_swfw_sync(hw, swfw_mask) != E1000_SUCCESS) { + status = E1000_ERR_SWFW_SYNC; + goto write_byte_out; + } + + do { + e1000_i2c_start(hw); + + status = e1000_clock_out_i2c_byte(hw, dev_addr); + if (status != E1000_SUCCESS) + goto fail; + + status = e1000_get_i2c_ack(hw); + if (status != E1000_SUCCESS) + goto fail; + + status = e1000_clock_out_i2c_byte(hw, byte_offset); + if (status != E1000_SUCCESS) + goto fail; + + status = e1000_get_i2c_ack(hw); + if (status != E1000_SUCCESS) + goto fail; + + status = e1000_clock_out_i2c_byte(hw, data); + if (status != E1000_SUCCESS) + goto fail; + + status = e1000_get_i2c_ack(hw); + if (status != E1000_SUCCESS) + goto fail; + + e1000_i2c_stop(hw); + break; + +fail: + e1000_i2c_bus_clear(hw); + retry++; + if (retry < max_retry) + DEBUGOUT("I2C byte write error - Retrying.\n"); + else + DEBUGOUT("I2C byte write error.\n"); + } while (retry < max_retry); + + hw->mac.ops.release_swfw_sync(hw, swfw_mask); + +write_byte_out: + + return status; +} + +/** + * e1000_i2c_start - Sets I2C start condition + * @hw: pointer to hardware structure + * + * Sets I2C start condition (High -> Low on SDA while SCL is High) + **/ +static void e1000_i2c_start(struct e1000_hw *hw) +{ + u32 i2cctl = E1000_READ_REG(hw, E1000_I2CPARAMS); + + DEBUGFUNC("e1000_i2c_start"); + + /* Start condition must begin with data and clock high */ + e1000_set_i2c_data(hw, &i2cctl, 1); + e1000_raise_i2c_clk(hw, &i2cctl); + + /* Setup time for start condition (4.7us) */ + usec_delay(E1000_I2C_T_SU_STA); + + e1000_set_i2c_data(hw, &i2cctl, 0); + + /* Hold time for start condition (4us) */ + usec_delay(E1000_I2C_T_HD_STA); + + e1000_lower_i2c_clk(hw, &i2cctl); + + /* Minimum low period of clock is 4.7 us */ + usec_delay(E1000_I2C_T_LOW); + +} + +/** + * e1000_i2c_stop - Sets I2C stop condition + * @hw: pointer to hardware structure + * + * Sets I2C stop condition (Low -> High on SDA while SCL is High) + **/ +static void e1000_i2c_stop(struct e1000_hw *hw) +{ + u32 i2cctl = E1000_READ_REG(hw, E1000_I2CPARAMS); + + DEBUGFUNC("e1000_i2c_stop"); + + /* Stop condition must begin with data low and clock high */ + e1000_set_i2c_data(hw, &i2cctl, 0); + e1000_raise_i2c_clk(hw, &i2cctl); + + /* Setup time for stop condition (4us) */ + usec_delay(E1000_I2C_T_SU_STO); + + e1000_set_i2c_data(hw, &i2cctl, 1); + + /* bus free time between stop and start (4.7us)*/ + usec_delay(E1000_I2C_T_BUF); +} + +/** + * e1000_clock_in_i2c_byte - Clocks in one byte via I2C + * @hw: pointer to hardware structure + * @data: data byte to clock in + * + * Clocks in one byte data via I2C data/clock + **/ +static void e1000_clock_in_i2c_byte(struct e1000_hw *hw, u8 *data) +{ + s32 i; + bool bit = 0; + + DEBUGFUNC("e1000_clock_in_i2c_byte"); + + *data = 0; + for (i = 7; i >= 0; i--) { + e1000_clock_in_i2c_bit(hw, &bit); + *data |= bit << i; + } + +} + +/** + * e1000_clock_out_i2c_byte - Clocks out one byte via I2C + * @hw: pointer to hardware structure + * @data: data byte clocked out + * + * Clocks out one byte data via I2C data/clock + **/ +static s32 e1000_clock_out_i2c_byte(struct e1000_hw *hw, u8 data) +{ + s32 status = E1000_SUCCESS; + s32 i; + u32 i2cctl; + bool bit = 0; + + DEBUGFUNC("e1000_clock_out_i2c_byte"); + + for (i = 7; i >= 0; i--) { + bit = (data >> i) & 0x1; + status = e1000_clock_out_i2c_bit(hw, bit); + + if (status != E1000_SUCCESS) + break; + } + + /* Release SDA line (set high) */ + i2cctl = E1000_READ_REG(hw, E1000_I2CPARAMS); + + i2cctl |= E1000_I2C_DATA_OE_N; + E1000_WRITE_REG(hw, E1000_I2CPARAMS, i2cctl); + E1000_WRITE_FLUSH(hw); + + return status; +} + +/** + * e1000_get_i2c_ack - Polls for I2C ACK + * @hw: pointer to hardware structure + * + * Clocks in/out one bit via I2C data/clock + **/ +static s32 e1000_get_i2c_ack(struct e1000_hw *hw) +{ + s32 status = E1000_SUCCESS; + u32 i = 0; + u32 i2cctl = E1000_READ_REG(hw, E1000_I2CPARAMS); + u32 timeout = 10; + bool ack = true; + + DEBUGFUNC("e1000_get_i2c_ack"); + + e1000_raise_i2c_clk(hw, &i2cctl); + + /* Minimum high period of clock is 4us */ + usec_delay(E1000_I2C_T_HIGH); + + /* Wait until SCL returns high */ + for (i = 0; i < timeout; i++) { + usec_delay(1); + i2cctl = E1000_READ_REG(hw, E1000_I2CPARAMS); + if (i2cctl & E1000_I2C_CLK_IN) + break; + } + if (!(i2cctl & E1000_I2C_CLK_IN)) + return E1000_ERR_I2C; + + ack = e1000_get_i2c_data(&i2cctl); + if (ack) { + DEBUGOUT("I2C ack was not received.\n"); + status = E1000_ERR_I2C; + } + + e1000_lower_i2c_clk(hw, &i2cctl); + + /* Minimum low period of clock is 4.7 us */ + usec_delay(E1000_I2C_T_LOW); + + return status; +} + +/** + * e1000_clock_in_i2c_bit - Clocks in one bit via I2C data/clock + * @hw: pointer to hardware structure + * @data: read data value + * + * Clocks in one bit via I2C data/clock + **/ +static void e1000_clock_in_i2c_bit(struct e1000_hw *hw, bool *data) +{ + u32 i2cctl = E1000_READ_REG(hw, E1000_I2CPARAMS); + + DEBUGFUNC("e1000_clock_in_i2c_bit"); + + e1000_raise_i2c_clk(hw, &i2cctl); + + /* Minimum high period of clock is 4us */ + usec_delay(E1000_I2C_T_HIGH); + + i2cctl = E1000_READ_REG(hw, E1000_I2CPARAMS); + *data = e1000_get_i2c_data(&i2cctl); + + e1000_lower_i2c_clk(hw, &i2cctl); + + /* Minimum low period of clock is 4.7 us */ + usec_delay(E1000_I2C_T_LOW); + +} + +/** + * e1000_clock_out_i2c_bit - Clocks in/out one bit via I2C data/clock + * @hw: pointer to hardware structure + * @data: data value to write + * + * Clocks out one bit via I2C data/clock + **/ +static s32 e1000_clock_out_i2c_bit(struct e1000_hw *hw, bool data) +{ + s32 status; + u32 i2cctl = E1000_READ_REG(hw, E1000_I2CPARAMS); + + DEBUGFUNC("e1000_clock_out_i2c_bit"); + + status = e1000_set_i2c_data(hw, &i2cctl, data); + if (status == E1000_SUCCESS) { + e1000_raise_i2c_clk(hw, &i2cctl); + + /* Minimum high period of clock is 4us */ + usec_delay(E1000_I2C_T_HIGH); + + e1000_lower_i2c_clk(hw, &i2cctl); + + /* Minimum low period of clock is 4.7 us. + * This also takes care of the data hold time. + */ + usec_delay(E1000_I2C_T_LOW); + } else { + status = E1000_ERR_I2C; + DEBUGOUT1("I2C data was not set to %X\n", data); + } + + return status; +} +/** + * e1000_raise_i2c_clk - Raises the I2C SCL clock + * @hw: pointer to hardware structure + * @i2cctl: Current value of I2CCTL register + * + * Raises the I2C clock line '0'->'1' + **/ +static void e1000_raise_i2c_clk(struct e1000_hw *hw, u32 *i2cctl) +{ + DEBUGFUNC("e1000_raise_i2c_clk"); + + *i2cctl |= E1000_I2C_CLK_OUT; + *i2cctl &= ~E1000_I2C_CLK_OE_N; + E1000_WRITE_REG(hw, E1000_I2CPARAMS, *i2cctl); + E1000_WRITE_FLUSH(hw); + + /* SCL rise time (1000ns) */ + usec_delay(E1000_I2C_T_RISE); +} + +/** + * e1000_lower_i2c_clk - Lowers the I2C SCL clock + * @hw: pointer to hardware structure + * @i2cctl: Current value of I2CCTL register + * + * Lowers the I2C clock line '1'->'0' + **/ +static void e1000_lower_i2c_clk(struct e1000_hw *hw, u32 *i2cctl) +{ + + DEBUGFUNC("e1000_lower_i2c_clk"); + + *i2cctl &= ~E1000_I2C_CLK_OUT; + *i2cctl &= ~E1000_I2C_CLK_OE_N; + E1000_WRITE_REG(hw, E1000_I2CPARAMS, *i2cctl); + E1000_WRITE_FLUSH(hw); + + /* SCL fall time (300ns) */ + usec_delay(E1000_I2C_T_FALL); +} + +/** + * e1000_set_i2c_data - Sets the I2C data bit + * @hw: pointer to hardware structure + * @i2cctl: Current value of I2CCTL register + * @data: I2C data value (0 or 1) to set + * + * Sets the I2C data bit + **/ +static s32 e1000_set_i2c_data(struct e1000_hw *hw, u32 *i2cctl, bool data) +{ + s32 status = E1000_SUCCESS; + + DEBUGFUNC("e1000_set_i2c_data"); + + if (data) + *i2cctl |= E1000_I2C_DATA_OUT; + else + *i2cctl &= ~E1000_I2C_DATA_OUT; + + *i2cctl &= ~E1000_I2C_DATA_OE_N; + *i2cctl |= E1000_I2C_CLK_OE_N; + E1000_WRITE_REG(hw, E1000_I2CPARAMS, *i2cctl); + E1000_WRITE_FLUSH(hw); + + /* Data rise/fall (1000ns/300ns) and set-up time (250ns) */ + usec_delay(E1000_I2C_T_RISE + E1000_I2C_T_FALL + E1000_I2C_T_SU_DATA); + + *i2cctl = E1000_READ_REG(hw, E1000_I2CPARAMS); + if (data != e1000_get_i2c_data(i2cctl)) { + status = E1000_ERR_I2C; + DEBUGOUT1("Error - I2C data was not set to %X.\n", data); + } + + return status; +} + +/** + * e1000_get_i2c_data - Reads the I2C SDA data bit + * @hw: pointer to hardware structure + * @i2cctl: Current value of I2CCTL register + * + * Returns the I2C data bit value + **/ +static bool e1000_get_i2c_data(u32 *i2cctl) +{ + bool data; + + DEBUGFUNC("e1000_get_i2c_data"); + + if (*i2cctl & E1000_I2C_DATA_IN) + data = 1; + else + data = 0; + + return data; +} + +/** + * e1000_i2c_bus_clear - Clears the I2C bus + * @hw: pointer to hardware structure + * + * Clears the I2C bus by sending nine clock pulses. + * Used when data line is stuck low. + **/ +void e1000_i2c_bus_clear(struct e1000_hw *hw) +{ + u32 i2cctl = E1000_READ_REG(hw, E1000_I2CPARAMS); + u32 i; + + DEBUGFUNC("e1000_i2c_bus_clear"); + + e1000_i2c_start(hw); + + e1000_set_i2c_data(hw, &i2cctl, 1); + + for (i = 0; i < 9; i++) { + e1000_raise_i2c_clk(hw, &i2cctl); + + /* Min high period of clock is 4us */ + usec_delay(E1000_I2C_T_HIGH); + + e1000_lower_i2c_clk(hw, &i2cctl); + + /* Min low period of clock is 4.7us*/ + usec_delay(E1000_I2C_T_LOW); + } + + e1000_i2c_start(hw); + + /* Put the i2c bus back to default state */ + e1000_i2c_stop(hw); +} + +static const u8 e1000_emc_temp_data[4] = { + E1000_EMC_INTERNAL_DATA, + E1000_EMC_DIODE1_DATA, + E1000_EMC_DIODE2_DATA, + E1000_EMC_DIODE3_DATA +}; +static const u8 e1000_emc_therm_limit[4] = { + E1000_EMC_INTERNAL_THERM_LIMIT, + E1000_EMC_DIODE1_THERM_LIMIT, + E1000_EMC_DIODE2_THERM_LIMIT, + E1000_EMC_DIODE3_THERM_LIMIT +}; + +/** + * e1000_get_thermal_sensor_data_generic - Gathers thermal sensor data + * @hw: pointer to hardware structure + * + * Updates the temperatures in mac.thermal_sensor_data + **/ +s32 e1000_get_thermal_sensor_data_generic(struct e1000_hw *hw) +{ + u16 ets_offset; + u16 ets_cfg; + u16 ets_sensor; + u8 num_sensors; + u8 sensor_index; + u8 sensor_location; + u8 i; + struct e1000_thermal_sensor_data *data = &hw->mac.thermal_sensor_data; + + DEBUGFUNC("e1000_get_thermal_sensor_data_generic"); + + if ((hw->mac.type != e1000_i350) || (hw->bus.func != 0)) + return E1000_NOT_IMPLEMENTED; + + data->sensor[0].temp = (E1000_READ_REG(hw, E1000_THMJT) & 0xFF); + + /* Return the internal sensor only if ETS is unsupported */ + e1000_read_nvm(hw, NVM_ETS_CFG, 1, &ets_offset); + if ((ets_offset == 0x0000) || (ets_offset == 0xFFFF)) + return E1000_SUCCESS; + + e1000_read_nvm(hw, ets_offset, 1, &ets_cfg); + if (((ets_cfg & NVM_ETS_TYPE_MASK) >> NVM_ETS_TYPE_SHIFT) + != NVM_ETS_TYPE_EMC) + return E1000_NOT_IMPLEMENTED; + + num_sensors = (ets_cfg & NVM_ETS_NUM_SENSORS_MASK); + if (num_sensors > E1000_MAX_SENSORS) + num_sensors = E1000_MAX_SENSORS; + + for (i = 1; i < num_sensors; i++) { + e1000_read_nvm(hw, (ets_offset + i), 1, &ets_sensor); + sensor_index = ((ets_sensor & NVM_ETS_DATA_INDEX_MASK) >> + NVM_ETS_DATA_INDEX_SHIFT); + sensor_location = ((ets_sensor & NVM_ETS_DATA_LOC_MASK) >> + NVM_ETS_DATA_LOC_SHIFT); + + if (sensor_location != 0) + hw->phy.ops.read_i2c_byte(hw, + e1000_emc_temp_data[sensor_index], + E1000_I2C_THERMAL_SENSOR_ADDR, + &data->sensor[i].temp); + } + return E1000_SUCCESS; +} + +/** + * e1000_init_thermal_sensor_thresh_generic - Sets thermal sensor thresholds + * @hw: pointer to hardware structure + * + * Sets the thermal sensor thresholds according to the NVM map + * and save off the threshold and location values into mac.thermal_sensor_data + **/ +s32 e1000_init_thermal_sensor_thresh_generic(struct e1000_hw *hw) +{ + u16 ets_offset; + u16 ets_cfg; + u16 ets_sensor; + u8 low_thresh_delta; + u8 num_sensors; + u8 sensor_index; + u8 sensor_location; + u8 therm_limit; + u8 i; + struct e1000_thermal_sensor_data *data = &hw->mac.thermal_sensor_data; + + DEBUGFUNC("e1000_init_thermal_sensor_thresh_generic"); + + if ((hw->mac.type != e1000_i350) || (hw->bus.func != 0)) + return E1000_NOT_IMPLEMENTED; + + memset(data, 0, sizeof(struct e1000_thermal_sensor_data)); + + data->sensor[0].location = 0x1; + data->sensor[0].caution_thresh = + (E1000_READ_REG(hw, E1000_THHIGHTC) & 0xFF); + data->sensor[0].max_op_thresh = + (E1000_READ_REG(hw, E1000_THLOWTC) & 0xFF); + + /* Return the internal sensor only if ETS is unsupported */ + e1000_read_nvm(hw, NVM_ETS_CFG, 1, &ets_offset); + if ((ets_offset == 0x0000) || (ets_offset == 0xFFFF)) + return E1000_SUCCESS; + + e1000_read_nvm(hw, ets_offset, 1, &ets_cfg); + if (((ets_cfg & NVM_ETS_TYPE_MASK) >> NVM_ETS_TYPE_SHIFT) + != NVM_ETS_TYPE_EMC) + return E1000_NOT_IMPLEMENTED; + + low_thresh_delta = ((ets_cfg & NVM_ETS_LTHRES_DELTA_MASK) >> + NVM_ETS_LTHRES_DELTA_SHIFT); + num_sensors = (ets_cfg & NVM_ETS_NUM_SENSORS_MASK); + + for (i = 1; i <= num_sensors; i++) { + e1000_read_nvm(hw, (ets_offset + i), 1, &ets_sensor); + sensor_index = ((ets_sensor & NVM_ETS_DATA_INDEX_MASK) >> + NVM_ETS_DATA_INDEX_SHIFT); + sensor_location = ((ets_sensor & NVM_ETS_DATA_LOC_MASK) >> + NVM_ETS_DATA_LOC_SHIFT); + therm_limit = ets_sensor & NVM_ETS_DATA_HTHRESH_MASK; + + hw->phy.ops.write_i2c_byte(hw, + e1000_emc_therm_limit[sensor_index], + E1000_I2C_THERMAL_SENSOR_ADDR, + therm_limit); + + if ((i < E1000_MAX_SENSORS) && (sensor_location != 0)) { + data->sensor[i].location = sensor_location; + data->sensor[i].caution_thresh = therm_limit; + data->sensor[i].max_op_thresh = therm_limit - + low_thresh_delta; + } + } + return E1000_SUCCESS; +} diff --git a/drivers/staging/igb_avb/e1000_82575.h b/drivers/staging/igb_avb/e1000_82575.h new file mode 100644 index 000000000000..c6b61f71e353 --- /dev/null +++ b/drivers/staging/igb_avb/e1000_82575.h @@ -0,0 +1,510 @@ +/******************************************************************************* + + Intel(R) Gigabit Ethernet Linux driver + Copyright(c) 2007-2015 Intel Corporation. + + This program is free software; you can redistribute it and/or modify it + under the terms and conditions of the GNU General Public License, + version 2, as published by the Free Software Foundation. + + This program is distributed in the hope 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. + + The full GNU General Public License is included in this distribution in + the file called "COPYING". + + Contact Information: + Linux NICS + e1000-devel Mailing List + Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + +*******************************************************************************/ + +#ifndef _E1000_82575_H_ +#define _E1000_82575_H_ + +#define ID_LED_DEFAULT_82575_SERDES ((ID_LED_DEF1_DEF2 << 12) | \ + (ID_LED_DEF1_DEF2 << 8) | \ + (ID_LED_DEF1_DEF2 << 4) | \ + (ID_LED_OFF1_ON2)) +/* + * Receive Address Register Count + * Number of high/low register pairs in the RAR. The RAR (Receive Address + * Registers) holds the directed and multicast addresses that we monitor. + * These entries are also used for MAC-based filtering. + */ +/* + * For 82576, there are an additional set of RARs that begin at an offset + * separate from the first set of RARs. + */ +#define E1000_RAR_ENTRIES_82575 16 +#define E1000_RAR_ENTRIES_82576 24 +#define E1000_RAR_ENTRIES_82580 24 +#define E1000_RAR_ENTRIES_I350 32 +#define E1000_SW_SYNCH_MB 0x00000100 +#define E1000_STAT_DEV_RST_SET 0x00100000 +#define E1000_CTRL_DEV_RST 0x20000000 + +struct e1000_adv_data_desc { + __le64 buffer_addr; /* Address of the descriptor's data buffer */ + union { + u32 data; + struct { + u32 datalen:16; /* Data buffer length */ + u32 rsvd:4; + u32 dtyp:4; /* Descriptor type */ + u32 dcmd:8; /* Descriptor command */ + } config; + } lower; + union { + u32 data; + struct { + u32 status:4; /* Descriptor status */ + u32 idx:4; + u32 popts:6; /* Packet Options */ + u32 paylen:18; /* Payload length */ + } options; + } upper; +}; + +#define E1000_TXD_DTYP_ADV_C 0x2 /* Advanced Context Descriptor */ +#define E1000_TXD_DTYP_ADV_D 0x3 /* Advanced Data Descriptor */ +#define E1000_ADV_TXD_CMD_DEXT 0x20 /* Descriptor extension (0 = legacy) */ +#define E1000_ADV_TUCMD_IPV4 0x2 /* IP Packet Type: 1=IPv4 */ +#define E1000_ADV_TUCMD_IPV6 0x0 /* IP Packet Type: 0=IPv6 */ +#define E1000_ADV_TUCMD_L4T_UDP 0x0 /* L4 Packet TYPE of UDP */ +#define E1000_ADV_TUCMD_L4T_TCP 0x4 /* L4 Packet TYPE of TCP */ +#define E1000_ADV_TUCMD_MKRREQ 0x10 /* Indicates markers are required */ +#define E1000_ADV_DCMD_EOP 0x1 /* End of Packet */ +#define E1000_ADV_DCMD_IFCS 0x2 /* Insert FCS (Ethernet CRC) */ +#define E1000_ADV_DCMD_RS 0x8 /* Report Status */ +#define E1000_ADV_DCMD_VLE 0x40 /* Add VLAN tag */ +#define E1000_ADV_DCMD_TSE 0x80 /* TCP Seg enable */ +/* Extended Device Control */ +#define E1000_CTRL_EXT_NSICR 0x00000001 /* Disable Intr Clear all on read */ + +struct e1000_adv_context_desc { + union { + u32 ip_config; + struct { + u32 iplen:9; + u32 maclen:7; + u32 vlan_tag:16; + } fields; + } ip_setup; + u32 seq_num; + union { + u64 l4_config; + struct { + u32 mkrloc:9; + u32 tucmd:11; + u32 dtyp:4; + u32 adv:8; + u32 rsvd:4; + u32 idx:4; + u32 l4len:8; + u32 mss:16; + } fields; + } l4_setup; +}; + +/* SRRCTL bit definitions */ +#define E1000_SRRCTL_BSIZEPKT_SHIFT 10 /* Shift _right_ */ +#define E1000_SRRCTL_BSIZEHDRSIZE_MASK 0x00000F00 +#define E1000_SRRCTL_BSIZEHDRSIZE_SHIFT 2 /* Shift _left_ */ +#define E1000_SRRCTL_DESCTYPE_LEGACY 0x00000000 +#define E1000_SRRCTL_DESCTYPE_ADV_ONEBUF 0x02000000 +#define E1000_SRRCTL_DESCTYPE_HDR_SPLIT 0x04000000 +#define E1000_SRRCTL_DESCTYPE_HDR_SPLIT_ALWAYS 0x0A000000 +#define E1000_SRRCTL_DESCTYPE_HDR_REPLICATION 0x06000000 +#define E1000_SRRCTL_DESCTYPE_HDR_REPLICATION_LARGE_PKT 0x08000000 +#define E1000_SRRCTL_DESCTYPE_MASK 0x0E000000 +#define E1000_SRRCTL_TIMESTAMP 0x40000000 +#define E1000_SRRCTL_DROP_EN 0x80000000 + +#define E1000_SRRCTL_BSIZEPKT_MASK 0x0000007F +#define E1000_SRRCTL_BSIZEHDR_MASK 0x00003F00 + +#define E1000_TX_HEAD_WB_ENABLE 0x1 +#define E1000_TX_SEQNUM_WB_ENABLE 0x2 + +#define E1000_MRQC_ENABLE_RSS_4Q 0x00000002 +#define E1000_MRQC_ENABLE_VMDQ 0x00000003 +#define E1000_MRQC_ENABLE_VMDQ_RSS_2Q 0x00000005 +#define E1000_MRQC_RSS_FIELD_IPV4_UDP 0x00400000 +#define E1000_MRQC_RSS_FIELD_IPV6_UDP 0x00800000 +#define E1000_MRQC_RSS_FIELD_IPV6_UDP_EX 0x01000000 +#define E1000_MRQC_ENABLE_RSS_8Q 0x00000002 + +#define E1000_VMRCTL_MIRROR_PORT_SHIFT 8 +#define E1000_VMRCTL_MIRROR_DSTPORT_MASK (7 << \ + E1000_VMRCTL_MIRROR_PORT_SHIFT) +#define E1000_VMRCTL_POOL_MIRROR_ENABLE (1 << 0) +#define E1000_VMRCTL_UPLINK_MIRROR_ENABLE (1 << 1) +#define E1000_VMRCTL_DOWNLINK_MIRROR_ENABLE (1 << 2) + +#define E1000_EICR_TX_QUEUE ( \ + E1000_EICR_TX_QUEUE0 | \ + E1000_EICR_TX_QUEUE1 | \ + E1000_EICR_TX_QUEUE2 | \ + E1000_EICR_TX_QUEUE3) + +#define E1000_EICR_RX_QUEUE ( \ + E1000_EICR_RX_QUEUE0 | \ + E1000_EICR_RX_QUEUE1 | \ + E1000_EICR_RX_QUEUE2 | \ + E1000_EICR_RX_QUEUE3) + +#define E1000_EIMS_RX_QUEUE E1000_EICR_RX_QUEUE +#define E1000_EIMS_TX_QUEUE E1000_EICR_TX_QUEUE + +#define EIMS_ENABLE_MASK ( \ + E1000_EIMS_RX_QUEUE | \ + E1000_EIMS_TX_QUEUE | \ + E1000_EIMS_TCP_TIMER | \ + E1000_EIMS_OTHER) + +/* Immediate Interrupt Rx (A.K.A. Low Latency Interrupt) */ +#define E1000_IMIR_PORT_IM_EN 0x00010000 /* TCP port enable */ +#define E1000_IMIR_PORT_BP 0x00020000 /* TCP port check bypass */ +#define E1000_IMIREXT_SIZE_BP 0x00001000 /* Packet size bypass */ +#define E1000_IMIREXT_CTRL_URG 0x00002000 /* Check URG bit in header */ +#define E1000_IMIREXT_CTRL_ACK 0x00004000 /* Check ACK bit in header */ +#define E1000_IMIREXT_CTRL_PSH 0x00008000 /* Check PSH bit in header */ +#define E1000_IMIREXT_CTRL_RST 0x00010000 /* Check RST bit in header */ +#define E1000_IMIREXT_CTRL_SYN 0x00020000 /* Check SYN bit in header */ +#define E1000_IMIREXT_CTRL_FIN 0x00040000 /* Check FIN bit in header */ +#define E1000_IMIREXT_CTRL_BP 0x00080000 /* Bypass check of ctrl bits */ + +/* Receive Descriptor - Advanced */ +union e1000_adv_rx_desc { + struct { + __le64 pkt_addr; /* Packet buffer address */ + __le64 hdr_addr; /* Header buffer address */ + } read; + struct { + struct { + union { + __le32 data; + struct { + __le16 pkt_info; /*RSS type, Pkt type*/ + /* Split Header, header buffer len */ + __le16 hdr_info; + } hs_rss; + } lo_dword; + union { + __le32 rss; /* RSS Hash */ + struct { + __le16 ip_id; /* IP id */ + __le16 csum; /* Packet Checksum */ + } csum_ip; + } hi_dword; + } lower; + struct { + __le32 status_error; /* ext status/error */ + __le16 length; /* Packet length */ + __le16 vlan; /* VLAN tag */ + } upper; + } wb; /* writeback */ +}; + +#define E1000_RXDADV_RSSTYPE_MASK 0x0000000F +#define E1000_RXDADV_RSSTYPE_SHIFT 12 +#define E1000_RXDADV_HDRBUFLEN_MASK 0x7FE0 +#define E1000_RXDADV_HDRBUFLEN_SHIFT 5 +#define E1000_RXDADV_SPLITHEADER_EN 0x00001000 +#define E1000_RXDADV_SPH 0x8000 +#define E1000_RXDADV_STAT_TS 0x10000 /* Pkt was time stamped */ +#define E1000_RXDADV_STAT_TSIP 0x08000 /* timestamp in packet */ +#define E1000_RXDADV_ERR_HBO 0x00800000 + +/* RSS Hash results */ +#define E1000_RXDADV_RSSTYPE_NONE 0x00000000 +#define E1000_RXDADV_RSSTYPE_IPV4_TCP 0x00000001 +#define E1000_RXDADV_RSSTYPE_IPV4 0x00000002 +#define E1000_RXDADV_RSSTYPE_IPV6_TCP 0x00000003 +#define E1000_RXDADV_RSSTYPE_IPV6_EX 0x00000004 +#define E1000_RXDADV_RSSTYPE_IPV6 0x00000005 +#define E1000_RXDADV_RSSTYPE_IPV6_TCP_EX 0x00000006 +#define E1000_RXDADV_RSSTYPE_IPV4_UDP 0x00000007 +#define E1000_RXDADV_RSSTYPE_IPV6_UDP 0x00000008 +#define E1000_RXDADV_RSSTYPE_IPV6_UDP_EX 0x00000009 + +/* RSS Packet Types as indicated in the receive descriptor */ +#define E1000_RXDADV_PKTTYPE_ILMASK 0x000000F0 +#define E1000_RXDADV_PKTTYPE_TLMASK 0x00000F00 +#define E1000_RXDADV_PKTTYPE_NONE 0x00000000 +#define E1000_RXDADV_PKTTYPE_IPV4 0x00000010 /* IPV4 hdr present */ +#define E1000_RXDADV_PKTTYPE_IPV4_EX 0x00000020 /* IPV4 hdr + extensions */ +#define E1000_RXDADV_PKTTYPE_IPV6 0x00000040 /* IPV6 hdr present */ +#define E1000_RXDADV_PKTTYPE_IPV6_EX 0x00000080 /* IPV6 hdr + extensions */ +#define E1000_RXDADV_PKTTYPE_TCP 0x00000100 /* TCP hdr present */ +#define E1000_RXDADV_PKTTYPE_UDP 0x00000200 /* UDP hdr present */ +#define E1000_RXDADV_PKTTYPE_SCTP 0x00000400 /* SCTP hdr present */ +#define E1000_RXDADV_PKTTYPE_NFS 0x00000800 /* NFS hdr present */ + +#define E1000_RXDADV_PKTTYPE_IPSEC_ESP 0x00001000 /* IPSec ESP */ +#define E1000_RXDADV_PKTTYPE_IPSEC_AH 0x00002000 /* IPSec AH */ +#define E1000_RXDADV_PKTTYPE_LINKSEC 0x00004000 /* LinkSec Encap */ +#define E1000_RXDADV_PKTTYPE_ETQF 0x00008000 /* PKTTYPE is ETQF index */ +#define E1000_RXDADV_PKTTYPE_ETQF_MASK 0x00000070 /* ETQF has 8 indices */ +#define E1000_RXDADV_PKTTYPE_ETQF_SHIFT 4 /* Right-shift 4 bits */ + +/* LinkSec results */ +/* Security Processing bit Indication */ +#define E1000_RXDADV_LNKSEC_STATUS_SECP 0x00020000 +#define E1000_RXDADV_LNKSEC_ERROR_BIT_MASK 0x18000000 +#define E1000_RXDADV_LNKSEC_ERROR_NO_SA_MATCH 0x08000000 +#define E1000_RXDADV_LNKSEC_ERROR_REPLAY_ERROR 0x10000000 +#define E1000_RXDADV_LNKSEC_ERROR_BAD_SIG 0x18000000 + +#define E1000_RXDADV_IPSEC_STATUS_SECP 0x00020000 +#define E1000_RXDADV_IPSEC_ERROR_BIT_MASK 0x18000000 +#define E1000_RXDADV_IPSEC_ERROR_INVALID_PROTOCOL 0x08000000 +#define E1000_RXDADV_IPSEC_ERROR_INVALID_LENGTH 0x10000000 +#define E1000_RXDADV_IPSEC_ERROR_AUTHENTICATION_FAILED 0x18000000 + +/* Transmit Descriptor - Advanced */ +union e1000_adv_tx_desc { + struct { + __le64 buffer_addr; /* Address of descriptor's data buf */ + __le32 cmd_type_len; + __le32 olinfo_status; + } read; + struct { + __le64 rsvd; /* Reserved */ + __le32 nxtseq_seed; + __le32 status; + } wb; +}; + +/* Adv Transmit Descriptor Config Masks */ +#define E1000_ADVTXD_DTYP_CTXT 0x00200000 /* Advanced Context Descriptor */ +#define E1000_ADVTXD_DTYP_DATA 0x00300000 /* Advanced Data Descriptor */ +#define E1000_ADVTXD_DCMD_EOP 0x01000000 /* End of Packet */ +#define E1000_ADVTXD_DCMD_IFCS 0x02000000 /* Insert FCS (Ethernet CRC) */ +#define E1000_ADVTXD_DCMD_RS 0x08000000 /* Report Status */ +#define E1000_ADVTXD_DCMD_DDTYP_ISCSI 0x10000000 /* DDP hdr type or iSCSI */ +#define E1000_ADVTXD_DCMD_DEXT 0x20000000 /* Descriptor extension (1=Adv) */ +#define E1000_ADVTXD_DCMD_VLE 0x40000000 /* VLAN pkt enable */ +#define E1000_ADVTXD_DCMD_TSE 0x80000000 /* TCP Seg enable */ +#define E1000_ADVTXD_MAC_LINKSEC 0x00040000 /* Apply LinkSec on pkt */ +#define E1000_ADVTXD_MAC_TSTAMP 0x00080000 /* IEEE1588 Timestamp pkt */ +#define E1000_ADVTXD_STAT_SN_CRC 0x00000002 /* NXTSEQ/SEED prsnt in WB */ +#define E1000_ADVTXD_IDX_SHIFT 4 /* Adv desc Index shift */ +#define E1000_ADVTXD_POPTS_ISCO_1ST 0x00000000 /* 1st TSO of iSCSI PDU */ +#define E1000_ADVTXD_POPTS_ISCO_MDL 0x00000800 /* Middle TSO of iSCSI PDU */ +#define E1000_ADVTXD_POPTS_ISCO_LAST 0x00001000 /* Last TSO of iSCSI PDU */ +/* 1st & Last TSO-full iSCSI PDU*/ +#define E1000_ADVTXD_POPTS_ISCO_FULL 0x00001800 +#define E1000_ADVTXD_POPTS_IPSEC 0x00000400 /* IPSec offload request */ +#define E1000_ADVTXD_PAYLEN_SHIFT 14 /* Adv desc PAYLEN shift */ + +/* Context descriptors */ +struct e1000_adv_tx_context_desc { + __le32 vlan_macip_lens; + __le32 seqnum_seed; + __le32 type_tucmd_mlhl; + __le32 mss_l4len_idx; +}; + +#define E1000_ADVTXD_MACLEN_SHIFT 9 /* Adv ctxt desc mac len shift */ +#define E1000_ADVTXD_VLAN_SHIFT 16 /* Adv ctxt vlan tag shift */ +#define E1000_ADVTXD_TUCMD_IPV4 0x00000400 /* IP Packet Type: 1=IPv4 */ +#define E1000_ADVTXD_TUCMD_IPV6 0x00000000 /* IP Packet Type: 0=IPv6 */ +#define E1000_ADVTXD_TUCMD_L4T_UDP 0x00000000 /* L4 Packet TYPE of UDP */ +#define E1000_ADVTXD_TUCMD_L4T_TCP 0x00000800 /* L4 Packet TYPE of TCP */ +#define E1000_ADVTXD_TUCMD_L4T_SCTP 0x00001000 /* L4 Packet TYPE of SCTP */ +#define E1000_ADVTXD_TUCMD_IPSEC_TYPE_ESP 0x00002000 /* IPSec Type ESP */ +/* IPSec Encrypt Enable for ESP */ +#define E1000_ADVTXD_TUCMD_IPSEC_ENCRYPT_EN 0x00004000 +/* Req requires Markers and CRC */ +#define E1000_ADVTXD_TUCMD_MKRREQ 0x00002000 +#define E1000_ADVTXD_L4LEN_SHIFT 8 /* Adv ctxt L4LEN shift */ +#define E1000_ADVTXD_MSS_SHIFT 16 /* Adv ctxt MSS shift */ +/* Adv ctxt IPSec SA IDX mask */ +#define E1000_ADVTXD_IPSEC_SA_INDEX_MASK 0x000000FF +/* Adv ctxt IPSec ESP len mask */ +#define E1000_ADVTXD_IPSEC_ESP_LEN_MASK 0x000000FF + +/* Additional Transmit Descriptor Control definitions */ +#define E1000_TXDCTL_QUEUE_ENABLE 0x02000000 /* Ena specific Tx Queue */ +#define E1000_TXDCTL_SWFLSH 0x04000000 /* Tx Desc. wbk flushing */ +/* Tx Queue Arbitration Priority 0=low, 1=high */ +#define E1000_TXDCTL_PRIORITY 0x08000000 + +/* Additional Receive Descriptor Control definitions */ +#define E1000_RXDCTL_QUEUE_ENABLE 0x02000000 /* Ena specific Rx Queue */ +#define E1000_RXDCTL_SWFLSH 0x04000000 /* Rx Desc. wbk flushing */ + +/* Direct Cache Access (DCA) definitions */ +#define E1000_DCA_CTRL_DCA_ENABLE 0x00000000 /* DCA Enable */ +#define E1000_DCA_CTRL_DCA_DISABLE 0x00000001 /* DCA Disable */ + +#define E1000_DCA_CTRL_DCA_MODE_CB1 0x00 /* DCA Mode CB1 */ +#define E1000_DCA_CTRL_DCA_MODE_CB2 0x02 /* DCA Mode CB2 */ + +#define E1000_DCA_RXCTRL_CPUID_MASK 0x0000001F /* Rx CPUID Mask */ +#define E1000_DCA_RXCTRL_DESC_DCA_EN (1 << 5) /* DCA Rx Desc enable */ +#define E1000_DCA_RXCTRL_HEAD_DCA_EN (1 << 6) /* DCA Rx Desc header ena */ +#define E1000_DCA_RXCTRL_DATA_DCA_EN (1 << 7) /* DCA Rx Desc payload ena */ +#define E1000_DCA_RXCTRL_DESC_RRO_EN (1 << 9) /* DCA Rx Desc Relax Order */ + +#define E1000_DCA_TXCTRL_CPUID_MASK 0x0000001F /* Tx CPUID Mask */ +#define E1000_DCA_TXCTRL_DESC_DCA_EN (1 << 5) /* DCA Tx Desc enable */ +#define E1000_DCA_TXCTRL_DESC_RRO_EN (1 << 9) /* Tx rd Desc Relax Order */ +#define E1000_DCA_TXCTRL_TX_WB_RO_EN (1 << 11) /* Tx Desc writeback RO bit */ +#define E1000_DCA_TXCTRL_DATA_RRO_EN (1 << 13) /* Tx rd data Relax Order */ + +#define E1000_DCA_TXCTRL_CPUID_MASK_82576 0xFF000000 /* Tx CPUID Mask */ +#define E1000_DCA_RXCTRL_CPUID_MASK_82576 0xFF000000 /* Rx CPUID Mask */ +#define E1000_DCA_TXCTRL_CPUID_SHIFT_82576 24 /* Tx CPUID */ +#define E1000_DCA_RXCTRL_CPUID_SHIFT_82576 24 /* Rx CPUID */ + +/* Additional interrupt register bit definitions */ +#define E1000_ICR_LSECPNS 0x00000020 /* PN threshold - server */ +#define E1000_IMS_LSECPNS E1000_ICR_LSECPNS /* PN threshold - server */ +#define E1000_ICS_LSECPNS E1000_ICR_LSECPNS /* PN threshold - server */ + +/* ETQF register bit definitions */ +#define E1000_ETQF_FILTER_ENABLE (1 << 26) +#define E1000_ETQF_IMM_INT (1 << 29) +#define E1000_ETQF_1588 (1 << 30) +#define E1000_ETQF_QUEUE_ENABLE (1 << 31) +/* + * ETQF filter list: one static filter per filter consumer. This is + * to avoid filter collisions later. Add new filters + * here!! + * + * Current filters: + * EAPOL 802.1x (0x888e): Filter 0 + */ +#define E1000_ETQF_FILTER_EAPOL 0 + +#define E1000_FTQF_VF_BP 0x00008000 +#define E1000_FTQF_1588_TIME_STAMP 0x08000000 +#define E1000_FTQF_MASK 0xF0000000 +#define E1000_FTQF_MASK_PROTO_BP 0x10000000 +#define E1000_FTQF_MASK_SOURCE_ADDR_BP 0x20000000 +#define E1000_FTQF_MASK_DEST_ADDR_BP 0x40000000 +#define E1000_FTQF_MASK_SOURCE_PORT_BP 0x80000000 + +#define E1000_NVM_APME_82575 0x0400 +#define MAX_NUM_VFS 7 + +#define E1000_DTXSWC_MAC_SPOOF_MASK 0x000000FF /* Per VF MAC spoof cntrl */ +#define E1000_DTXSWC_VLAN_SPOOF_MASK 0x0000FF00 /* Per VF VLAN spoof cntrl */ +#define E1000_DTXSWC_LLE_MASK 0x00FF0000 /* Per VF Local LB enables */ +#define E1000_DTXSWC_VLAN_SPOOF_SHIFT 8 +#define E1000_DTXSWC_LLE_SHIFT 16 +#define E1000_DTXSWC_VMDQ_LOOPBACK_EN (1 << 31) /* global VF LB enable */ + +/* Easy defines for setting default pool, would normally be left a zero */ +#define E1000_VT_CTL_DEFAULT_POOL_SHIFT 7 +#define E1000_VT_CTL_DEFAULT_POOL_MASK (0x7 << E1000_VT_CTL_DEFAULT_POOL_SHIFT) + +/* Other useful VMD_CTL register defines */ +#define E1000_VT_CTL_IGNORE_MAC (1 << 28) +#define E1000_VT_CTL_DISABLE_DEF_POOL (1 << 29) +#define E1000_VT_CTL_VM_REPL_EN (1 << 30) + +/* Per VM Offload register setup */ +#define E1000_VMOLR_RLPML_MASK 0x00003FFF /* Long Packet Maximum Length mask */ +#define E1000_VMOLR_LPE 0x00010000 /* Accept Long packet */ +#define E1000_VMOLR_RSSE 0x00020000 /* Enable RSS */ +#define E1000_VMOLR_AUPE 0x01000000 /* Accept untagged packets */ +#define E1000_VMOLR_ROMPE 0x02000000 /* Accept overflow multicast */ +#define E1000_VMOLR_ROPE 0x04000000 /* Accept overflow unicast */ +#define E1000_VMOLR_BAM 0x08000000 /* Accept Broadcast packets */ +#define E1000_VMOLR_MPME 0x10000000 /* Multicast promiscuous mode */ +#define E1000_VMOLR_STRVLAN 0x40000000 /* Vlan stripping enable */ +#define E1000_VMOLR_STRCRC 0x80000000 /* CRC stripping enable */ + +#define E1000_VMOLR_VPE 0x00800000 /* VLAN promiscuous enable */ +#define E1000_VMOLR_UPE 0x20000000 /* Unicast promisuous enable */ +#define E1000_DVMOLR_HIDVLAN 0x20000000 /* Vlan hiding enable */ +#define E1000_DVMOLR_STRVLAN 0x40000000 /* Vlan stripping enable */ +#define E1000_DVMOLR_STRCRC 0x80000000 /* CRC stripping enable */ + +#define E1000_PBRWAC_WALPB 0x00000007 /* Wrap around event on LAN Rx PB */ +#define E1000_PBRWAC_PBE 0x00000008 /* Rx packet buffer empty */ + +#define E1000_VLVF_ARRAY_SIZE 32 +#define E1000_VLVF_VLANID_MASK 0x00000FFF +#define E1000_VLVF_POOLSEL_SHIFT 12 +#define E1000_VLVF_POOLSEL_MASK (0xFF << E1000_VLVF_POOLSEL_SHIFT) +#define E1000_VLVF_LVLAN 0x00100000 +#define E1000_VLVF_VLANID_ENABLE 0x80000000 + +#define E1000_VMVIR_VLANA_DEFAULT 0x40000000 /* Always use default VLAN */ +#define E1000_VMVIR_VLANA_NEVER 0x80000000 /* Never insert VLAN tag */ + +#define E1000_VF_INIT_TIMEOUT 200 /* Number of retries to clear RSTI */ + +#define E1000_IOVCTL 0x05BBC +#define E1000_IOVCTL_REUSE_VFQ 0x00000001 + +#define E1000_RPLOLR_STRVLAN 0x40000000 +#define E1000_RPLOLR_STRCRC 0x80000000 + +#define E1000_TCTL_EXT_COLD 0x000FFC00 +#define E1000_TCTL_EXT_COLD_SHIFT 10 + +#define E1000_DTXCTL_8023LL 0x0004 +#define E1000_DTXCTL_VLAN_ADDED 0x0008 +#define E1000_DTXCTL_OOS_ENABLE 0x0010 +#define E1000_DTXCTL_MDP_EN 0x0020 +#define E1000_DTXCTL_SPOOF_INT 0x0040 + +#define E1000_EEPROM_PCS_AUTONEG_DISABLE_BIT (1 << 14) + +#define ALL_QUEUES 0xFFFF + +/* Rx packet buffer size defines */ +#define E1000_RXPBS_SIZE_MASK_82576 0x0000007F +void e1000_vmdq_set_loopback_pf(struct e1000_hw *hw, bool enable); +void e1000_vmdq_set_anti_spoofing_pf(struct e1000_hw *hw, bool enable, int pf); +void e1000_vmdq_set_replication_pf(struct e1000_hw *hw, bool enable); +s32 e1000_init_nvm_params_82575(struct e1000_hw *hw); +s32 e1000_init_hw_82575(struct e1000_hw *hw); + +u16 e1000_rxpbs_adjust_82580(u32 data); +s32 e1000_read_emi_reg(struct e1000_hw *hw, u16 addr, u16 *data); +s32 e1000_set_eee_i350(struct e1000_hw *hw, bool adv1G, bool adv100M); +s32 e1000_set_eee_i354(struct e1000_hw *hw, bool adv1G, bool adv100M); +s32 e1000_get_eee_status_i354(struct e1000_hw *, bool *); +s32 e1000_initialize_M88E1512_phy(struct e1000_hw *hw); +#define E1000_I2C_THERMAL_SENSOR_ADDR 0xF8 +#define E1000_EMC_INTERNAL_DATA 0x00 +#define E1000_EMC_INTERNAL_THERM_LIMIT 0x20 +#define E1000_EMC_DIODE1_DATA 0x01 +#define E1000_EMC_DIODE1_THERM_LIMIT 0x19 +#define E1000_EMC_DIODE2_DATA 0x23 +#define E1000_EMC_DIODE2_THERM_LIMIT 0x1A +#define E1000_EMC_DIODE3_DATA 0x2A +#define E1000_EMC_DIODE3_THERM_LIMIT 0x30 + +s32 e1000_get_thermal_sensor_data_generic(struct e1000_hw *hw); +s32 e1000_init_thermal_sensor_thresh_generic(struct e1000_hw *hw); + +/* I2C SDA and SCL timing parameters for standard mode */ +#define E1000_I2C_T_HD_STA 4 +#define E1000_I2C_T_LOW 5 +#define E1000_I2C_T_HIGH 4 +#define E1000_I2C_T_SU_STA 5 +#define E1000_I2C_T_HD_DATA 5 +#define E1000_I2C_T_SU_DATA 1 +#define E1000_I2C_T_RISE 1 +#define E1000_I2C_T_FALL 1 +#define E1000_I2C_T_SU_STO 4 +#define E1000_I2C_T_BUF 5 + +s32 e1000_set_i2c_bb(struct e1000_hw *hw); +s32 e1000_read_i2c_byte_generic(struct e1000_hw *hw, u8 byte_offset, + u8 dev_addr, u8 *data); +s32 e1000_write_i2c_byte_generic(struct e1000_hw *hw, u8 byte_offset, + u8 dev_addr, u8 data); +void e1000_i2c_bus_clear(struct e1000_hw *hw); +#endif /* _E1000_82575_H_ */ diff --git a/drivers/staging/igb_avb/e1000_api.c b/drivers/staging/igb_avb/e1000_api.c new file mode 100644 index 000000000000..87bccbd19fc6 --- /dev/null +++ b/drivers/staging/igb_avb/e1000_api.c @@ -0,0 +1,1160 @@ +/******************************************************************************* + + Intel(R) Gigabit Ethernet Linux driver + Copyright(c) 2007-2015 Intel Corporation. + + This program is free software; you can redistribute it and/or modify it + under the terms and conditions of the GNU General Public License, + version 2, as published by the Free Software Foundation. + + This program is distributed in the hope 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. + + The full GNU General Public License is included in this distribution in + the file called "COPYING". + + Contact Information: + Linux NICS + e1000-devel Mailing List + Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + +*******************************************************************************/ + +#include "e1000_api.h" + +/** + * e1000_init_mac_params - Initialize MAC function pointers + * @hw: pointer to the HW structure + * + * This function initializes the function pointers for the MAC + * set of functions. Called by drivers or by e1000_setup_init_funcs. + **/ +s32 e1000_init_mac_params(struct e1000_hw *hw) +{ + s32 ret_val = E1000_SUCCESS; + + if (hw->mac.ops.init_params) { + ret_val = hw->mac.ops.init_params(hw); + if (ret_val) { + DEBUGOUT("MAC Initialization Error\n"); + goto out; + } + } else { + DEBUGOUT("mac.init_mac_params was NULL\n"); + ret_val = -E1000_ERR_CONFIG; + } + +out: + return ret_val; +} + +/** + * e1000_init_nvm_params - Initialize NVM function pointers + * @hw: pointer to the HW structure + * + * This function initializes the function pointers for the NVM + * set of functions. Called by drivers or by e1000_setup_init_funcs. + **/ +s32 e1000_init_nvm_params(struct e1000_hw *hw) +{ + s32 ret_val = E1000_SUCCESS; + + if (hw->nvm.ops.init_params) { + ret_val = hw->nvm.ops.init_params(hw); + if (ret_val) { + DEBUGOUT("NVM Initialization Error\n"); + goto out; + } + } else { + DEBUGOUT("nvm.init_nvm_params was NULL\n"); + ret_val = -E1000_ERR_CONFIG; + } + +out: + return ret_val; +} + +/** + * e1000_init_phy_params - Initialize PHY function pointers + * @hw: pointer to the HW structure + * + * This function initializes the function pointers for the PHY + * set of functions. Called by drivers or by e1000_setup_init_funcs. + **/ +s32 e1000_init_phy_params(struct e1000_hw *hw) +{ + s32 ret_val = E1000_SUCCESS; + + if (hw->phy.ops.init_params) { + ret_val = hw->phy.ops.init_params(hw); + if (ret_val) { + DEBUGOUT("PHY Initialization Error\n"); + goto out; + } + } else { + DEBUGOUT("phy.init_phy_params was NULL\n"); + ret_val = -E1000_ERR_CONFIG; + } + +out: + return ret_val; +} + +/** + * e1000_init_mbx_params - Initialize mailbox function pointers + * @hw: pointer to the HW structure + * + * This function initializes the function pointers for the PHY + * set of functions. Called by drivers or by e1000_setup_init_funcs. + **/ +s32 e1000_init_mbx_params(struct e1000_hw *hw) +{ + s32 ret_val = E1000_SUCCESS; + + if (hw->mbx.ops.init_params) { + ret_val = hw->mbx.ops.init_params(hw); + if (ret_val) { + DEBUGOUT("Mailbox Initialization Error\n"); + goto out; + } + } else { + DEBUGOUT("mbx.init_mbx_params was NULL\n"); + ret_val = -E1000_ERR_CONFIG; + } + +out: + return ret_val; +} + +/** + * e1000_set_mac_type - Sets MAC type + * @hw: pointer to the HW structure + * + * This function sets the mac type of the adapter based on the + * device ID stored in the hw structure. + * MUST BE FIRST FUNCTION CALLED (explicitly or through + * e1000_setup_init_funcs()). + **/ +s32 e1000_set_mac_type(struct e1000_hw *hw) +{ + struct e1000_mac_info *mac = &hw->mac; + s32 ret_val = E1000_SUCCESS; + + DEBUGFUNC("e1000_set_mac_type"); + + switch (hw->device_id) { + case E1000_DEV_ID_82575EB_COPPER: + case E1000_DEV_ID_82575EB_FIBER_SERDES: + case E1000_DEV_ID_82575GB_QUAD_COPPER: + mac->type = e1000_82575; + break; + case E1000_DEV_ID_82576: + case E1000_DEV_ID_82576_FIBER: + case E1000_DEV_ID_82576_SERDES: + case E1000_DEV_ID_82576_QUAD_COPPER: + case E1000_DEV_ID_82576_QUAD_COPPER_ET2: + case E1000_DEV_ID_82576_NS: + case E1000_DEV_ID_82576_NS_SERDES: + case E1000_DEV_ID_82576_SERDES_QUAD: + mac->type = e1000_82576; + break; + case E1000_DEV_ID_82580_COPPER: + case E1000_DEV_ID_82580_FIBER: + case E1000_DEV_ID_82580_SERDES: + case E1000_DEV_ID_82580_SGMII: + case E1000_DEV_ID_82580_COPPER_DUAL: + case E1000_DEV_ID_82580_QUAD_FIBER: + case E1000_DEV_ID_DH89XXCC_SGMII: + case E1000_DEV_ID_DH89XXCC_SERDES: + case E1000_DEV_ID_DH89XXCC_BACKPLANE: + case E1000_DEV_ID_DH89XXCC_SFP: + mac->type = e1000_82580; + break; + case E1000_DEV_ID_I350_COPPER: + case E1000_DEV_ID_I350_FIBER: + case E1000_DEV_ID_I350_SERDES: + case E1000_DEV_ID_I350_SGMII: + case E1000_DEV_ID_I350_DA4: + mac->type = e1000_i350; + break; + case E1000_DEV_ID_I210_COPPER_FLASHLESS: + case E1000_DEV_ID_I210_SERDES_FLASHLESS: + case E1000_DEV_ID_I210_COPPER: + case E1000_DEV_ID_I210_COPPER_OEM1: + case E1000_DEV_ID_I210_COPPER_IT: + case E1000_DEV_ID_I210_FIBER: + case E1000_DEV_ID_I210_SERDES: + case E1000_DEV_ID_I210_SGMII: + case E1000_DEV_ID_I210_AUTOMOTIVE: + mac->type = e1000_i210; + break; + case E1000_DEV_ID_I211_COPPER: + mac->type = e1000_i211; + break; + + case E1000_DEV_ID_I354_BACKPLANE_1GBPS: + case E1000_DEV_ID_I354_SGMII: + case E1000_DEV_ID_I354_BACKPLANE_2_5GBPS: + mac->type = e1000_i354; + break; + default: + /* Should never have loaded on this device */ + ret_val = -E1000_ERR_MAC_INIT; + break; + } + + return ret_val; +} + +/** + * e1000_setup_init_funcs - Initializes function pointers + * @hw: pointer to the HW structure + * @init_device: true will initialize the rest of the function pointers + * getting the device ready for use. false will only set + * MAC type and the function pointers for the other init + * functions. Passing false will not generate any hardware + * reads or writes. + * + * This function must be called by a driver in order to use the rest + * of the 'shared' code files. Called by drivers only. + **/ +s32 e1000_setup_init_funcs(struct e1000_hw *hw, bool init_device) +{ + s32 ret_val; + + /* Can't do much good without knowing the MAC type. */ + ret_val = e1000_set_mac_type(hw); + if (ret_val) { + DEBUGOUT("ERROR: MAC type could not be set properly.\n"); + goto out; + } + + if (!hw->hw_addr) { + DEBUGOUT("ERROR: Registers not mapped\n"); + ret_val = -E1000_ERR_CONFIG; + goto out; + } + + /* + * Init function pointers to generic implementations. We do this first + * allowing a driver module to override it afterward. + */ + e1000_init_mac_ops_generic(hw); + e1000_init_phy_ops_generic(hw); + e1000_init_nvm_ops_generic(hw); + e1000_init_mbx_ops_generic(hw); + + /* + * Set up the init function pointers. These are functions within the + * adapter family file that sets up function pointers for the rest of + * the functions in that family. + */ + switch (hw->mac.type) { + case e1000_82575: + case e1000_82576: + case e1000_82580: + case e1000_i350: + case e1000_i354: + e1000_init_function_pointers_82575(hw); + break; + case e1000_i210: + case e1000_i211: + e1000_init_function_pointers_i210(hw); + break; + default: + DEBUGOUT("Hardware not supported\n"); + ret_val = -E1000_ERR_CONFIG; + break; + } + + /* + * Initialize the rest of the function pointers. These require some + * register reads/writes in some cases. + */ + if (!(ret_val) && init_device) { + ret_val = e1000_init_mac_params(hw); + if (ret_val) + goto out; + + ret_val = e1000_init_nvm_params(hw); + if (ret_val) + goto out; + + ret_val = e1000_init_phy_params(hw); + if (ret_val) + goto out; + + ret_val = e1000_init_mbx_params(hw); + if (ret_val) + goto out; + } + +out: + return ret_val; +} + +/** + * e1000_get_bus_info - Obtain bus information for adapter + * @hw: pointer to the HW structure + * + * This will obtain information about the HW bus for which the + * adapter is attached and stores it in the hw structure. This is a + * function pointer entry point called by drivers. + **/ +s32 e1000_get_bus_info(struct e1000_hw *hw) +{ + if (hw->mac.ops.get_bus_info) + return hw->mac.ops.get_bus_info(hw); + + return E1000_SUCCESS; +} + +/** + * e1000_clear_vfta - Clear VLAN filter table + * @hw: pointer to the HW structure + * + * This clears the VLAN filter table on the adapter. This is a function + * pointer entry point called by drivers. + **/ +void e1000_clear_vfta(struct e1000_hw *hw) +{ + if (hw->mac.ops.clear_vfta) + hw->mac.ops.clear_vfta(hw); +} + +/** + * e1000_write_vfta - Write value to VLAN filter table + * @hw: pointer to the HW structure + * @offset: the 32-bit offset in which to write the value to. + * @value: the 32-bit value to write at location offset. + * + * This writes a 32-bit value to a 32-bit offset in the VLAN filter + * table. This is a function pointer entry point called by drivers. + **/ +void e1000_write_vfta(struct e1000_hw *hw, u32 offset, u32 value) +{ + if (hw->mac.ops.write_vfta) + hw->mac.ops.write_vfta(hw, offset, value); +} + +/** + * e1000_update_mc_addr_list - Update Multicast addresses + * @hw: pointer to the HW structure + * @mc_addr_list: array of multicast addresses to program + * @mc_addr_count: number of multicast addresses to program + * + * Updates the Multicast Table Array. + * The caller must have a packed mc_addr_list of multicast addresses. + **/ +void e1000_update_mc_addr_list(struct e1000_hw *hw, u8 *mc_addr_list, + u32 mc_addr_count) +{ + if (hw->mac.ops.update_mc_addr_list) + hw->mac.ops.update_mc_addr_list(hw, mc_addr_list, + mc_addr_count); +} + +/** + * e1000_force_mac_fc - Force MAC flow control + * @hw: pointer to the HW structure + * + * Force the MAC's flow control settings. Currently no func pointer exists + * and all implementations are handled in the generic version of this + * function. + **/ +s32 e1000_force_mac_fc(struct e1000_hw *hw) +{ + return e1000_force_mac_fc_generic(hw); +} + +/** + * e1000_check_for_link - Check/Store link connection + * @hw: pointer to the HW structure + * + * This checks the link condition of the adapter and stores the + * results in the hw->mac structure. This is a function pointer entry + * point called by drivers. + **/ +s32 e1000_check_for_link(struct e1000_hw *hw) +{ + if (hw->mac.ops.check_for_link) + return hw->mac.ops.check_for_link(hw); + + return -E1000_ERR_CONFIG; +} + +/** + * e1000_check_mng_mode - Check management mode + * @hw: pointer to the HW structure + * + * This checks if the adapter has manageability enabled. + * This is a function pointer entry point called by drivers. + **/ +bool e1000_check_mng_mode(struct e1000_hw *hw) +{ + if (hw->mac.ops.check_mng_mode) + return hw->mac.ops.check_mng_mode(hw); + + return false; +} + +/** + * e1000_mng_write_dhcp_info - Writes DHCP info to host interface + * @hw: pointer to the HW structure + * @buffer: pointer to the host interface + * @length: size of the buffer + * + * Writes the DHCP information to the host interface. + **/ +s32 e1000_mng_write_dhcp_info(struct e1000_hw *hw, u8 *buffer, u16 length) +{ + return e1000_mng_write_dhcp_info_generic(hw, buffer, length); +} + +/** + * e1000_reset_hw - Reset hardware + * @hw: pointer to the HW structure + * + * This resets the hardware into a known state. This is a function pointer + * entry point called by drivers. + **/ +s32 e1000_reset_hw(struct e1000_hw *hw) +{ + if (hw->mac.ops.reset_hw) + return hw->mac.ops.reset_hw(hw); + + return -E1000_ERR_CONFIG; +} + +/** + * e1000_init_hw - Initialize hardware + * @hw: pointer to the HW structure + * + * This inits the hardware readying it for operation. This is a function + * pointer entry point called by drivers. + **/ +s32 e1000_init_hw(struct e1000_hw *hw) +{ + if (hw->mac.ops.init_hw) + return hw->mac.ops.init_hw(hw); + + return -E1000_ERR_CONFIG; +} + +/** + * e1000_setup_link - Configures link and flow control + * @hw: pointer to the HW structure + * + * This configures link and flow control settings for the adapter. This + * is a function pointer entry point called by drivers. While modules can + * also call this, they probably call their own version of this function. + **/ +s32 e1000_setup_link(struct e1000_hw *hw) +{ + if (hw->mac.ops.setup_link) + return hw->mac.ops.setup_link(hw); + + return -E1000_ERR_CONFIG; +} + +/** + * e1000_get_speed_and_duplex - Returns current speed and duplex + * @hw: pointer to the HW structure + * @speed: pointer to a 16-bit value to store the speed + * @duplex: pointer to a 16-bit value to store the duplex. + * + * This returns the speed and duplex of the adapter in the two 'out' + * variables passed in. This is a function pointer entry point called + * by drivers. + **/ +s32 e1000_get_speed_and_duplex(struct e1000_hw *hw, u16 *speed, u16 *duplex) +{ + if (hw->mac.ops.get_link_up_info) + return hw->mac.ops.get_link_up_info(hw, speed, duplex); + + return -E1000_ERR_CONFIG; +} + +/** + * e1000_setup_led - Configures SW controllable LED + * @hw: pointer to the HW structure + * + * This prepares the SW controllable LED for use and saves the current state + * of the LED so it can be later restored. This is a function pointer entry + * point called by drivers. + **/ +s32 e1000_setup_led(struct e1000_hw *hw) +{ + if (hw->mac.ops.setup_led) + return hw->mac.ops.setup_led(hw); + + return E1000_SUCCESS; +} + +/** + * e1000_cleanup_led - Restores SW controllable LED + * @hw: pointer to the HW structure + * + * This restores the SW controllable LED to the value saved off by + * e1000_setup_led. This is a function pointer entry point called by drivers. + **/ +s32 e1000_cleanup_led(struct e1000_hw *hw) +{ + if (hw->mac.ops.cleanup_led) + return hw->mac.ops.cleanup_led(hw); + + return E1000_SUCCESS; +} + +/** + * e1000_blink_led - Blink SW controllable LED + * @hw: pointer to the HW structure + * + * This starts the adapter LED blinking. Request the LED to be setup first + * and cleaned up after. This is a function pointer entry point called by + * drivers. + **/ +s32 e1000_blink_led(struct e1000_hw *hw) +{ + if (hw->mac.ops.blink_led) + return hw->mac.ops.blink_led(hw); + + return E1000_SUCCESS; +} + +/** + * e1000_id_led_init - store LED configurations in SW + * @hw: pointer to the HW structure + * + * Initializes the LED config in SW. This is a function pointer entry point + * called by drivers. + **/ +s32 e1000_id_led_init(struct e1000_hw *hw) +{ + if (hw->mac.ops.id_led_init) + return hw->mac.ops.id_led_init(hw); + + return E1000_SUCCESS; +} + +/** + * e1000_led_on - Turn on SW controllable LED + * @hw: pointer to the HW structure + * + * Turns the SW defined LED on. This is a function pointer entry point + * called by drivers. + **/ +s32 e1000_led_on(struct e1000_hw *hw) +{ + if (hw->mac.ops.led_on) + return hw->mac.ops.led_on(hw); + + return E1000_SUCCESS; +} + +/** + * e1000_led_off - Turn off SW controllable LED + * @hw: pointer to the HW structure + * + * Turns the SW defined LED off. This is a function pointer entry point + * called by drivers. + **/ +s32 e1000_led_off(struct e1000_hw *hw) +{ + if (hw->mac.ops.led_off) + return hw->mac.ops.led_off(hw); + + return E1000_SUCCESS; +} + +/** + * e1000_reset_adaptive - Reset adaptive IFS + * @hw: pointer to the HW structure + * + * Resets the adaptive IFS. Currently no func pointer exists and all + * implementations are handled in the generic version of this function. + **/ +void e1000_reset_adaptive(struct e1000_hw *hw) +{ + e1000_reset_adaptive_generic(hw); +} + +/** + * e1000_update_adaptive - Update adaptive IFS + * @hw: pointer to the HW structure + * + * Updates adapter IFS. Currently no func pointer exists and all + * implementations are handled in the generic version of this function. + **/ +void e1000_update_adaptive(struct e1000_hw *hw) +{ + e1000_update_adaptive_generic(hw); +} + +/** + * e1000_disable_pcie_master - Disable PCI-Express master access + * @hw: pointer to the HW structure + * + * Disables PCI-Express master access and verifies there are no pending + * requests. Currently no func pointer exists and all implementations are + * handled in the generic version of this function. + **/ +s32 e1000_disable_pcie_master(struct e1000_hw *hw) +{ + return e1000_disable_pcie_master_generic(hw); +} + +/** + * e1000_config_collision_dist - Configure collision distance + * @hw: pointer to the HW structure + * + * Configures the collision distance to the default value and is used + * during link setup. + **/ +void e1000_config_collision_dist(struct e1000_hw *hw) +{ + if (hw->mac.ops.config_collision_dist) + hw->mac.ops.config_collision_dist(hw); +} + +/** + * e1000_rar_set - Sets a receive address register + * @hw: pointer to the HW structure + * @addr: address to set the RAR to + * @index: the RAR to set + * + * Sets a Receive Address Register (RAR) to the specified address. + **/ +int e1000_rar_set(struct e1000_hw *hw, u8 *addr, u32 index) +{ + if (hw->mac.ops.rar_set) + return hw->mac.ops.rar_set(hw, addr, index); + + return E1000_SUCCESS; +} + +/** + * e1000_validate_mdi_setting - Ensures valid MDI/MDIX SW state + * @hw: pointer to the HW structure + * + * Ensures that the MDI/MDIX SW state is valid. + **/ +s32 e1000_validate_mdi_setting(struct e1000_hw *hw) +{ + if (hw->mac.ops.validate_mdi_setting) + return hw->mac.ops.validate_mdi_setting(hw); + + return E1000_SUCCESS; +} + +/** + * e1000_hash_mc_addr - Determines address location in multicast table + * @hw: pointer to the HW structure + * @mc_addr: Multicast address to hash. + * + * This hashes an address to determine its location in the multicast + * table. Currently no func pointer exists and all implementations + * are handled in the generic version of this function. + **/ +u32 e1000_hash_mc_addr(struct e1000_hw *hw, u8 *mc_addr) +{ + return e1000_hash_mc_addr_generic(hw, mc_addr); +} + +/** + * e1000_enable_tx_pkt_filtering - Enable packet filtering on TX + * @hw: pointer to the HW structure + * + * Enables packet filtering on transmit packets if manageability is enabled + * and host interface is enabled. + * Currently no func pointer exists and all implementations are handled in the + * generic version of this function. + **/ +bool e1000_enable_tx_pkt_filtering(struct e1000_hw *hw) +{ + return e1000_enable_tx_pkt_filtering_generic(hw); +} + +/** + * e1000_mng_host_if_write - Writes to the manageability host interface + * @hw: pointer to the HW structure + * @buffer: pointer to the host interface buffer + * @length: size of the buffer + * @offset: location in the buffer to write to + * @sum: sum of the data (not checksum) + * + * This function writes the buffer content at the offset given on the host if. + * It also does alignment considerations to do the writes in most efficient + * way. Also fills up the sum of the buffer in *buffer parameter. + **/ +s32 e1000_mng_host_if_write(struct e1000_hw *hw, u8 *buffer, u16 length, + u16 offset, u8 *sum) +{ + return e1000_mng_host_if_write_generic(hw, buffer, length, offset, sum); +} + +/** + * e1000_mng_write_cmd_header - Writes manageability command header + * @hw: pointer to the HW structure + * @hdr: pointer to the host interface command header + * + * Writes the command header after does the checksum calculation. + **/ +s32 e1000_mng_write_cmd_header(struct e1000_hw *hw, + struct e1000_host_mng_command_header *hdr) +{ + return e1000_mng_write_cmd_header_generic(hw, hdr); +} + +/** + * e1000_mng_enable_host_if - Checks host interface is enabled + * @hw: pointer to the HW structure + * + * Returns E1000_success upon success, else E1000_ERR_HOST_INTERFACE_COMMAND + * + * This function checks whether the HOST IF is enabled for command operation + * and also checks whether the previous command is completed. It busy waits + * in case of previous command is not completed. + **/ +s32 e1000_mng_enable_host_if(struct e1000_hw *hw) +{ + return e1000_mng_enable_host_if_generic(hw); +} + +/** + * e1000_check_reset_block - Verifies PHY can be reset + * @hw: pointer to the HW structure + * + * Checks if the PHY is in a state that can be reset or if manageability + * has it tied up. This is a function pointer entry point called by drivers. + **/ +s32 e1000_check_reset_block(struct e1000_hw *hw) +{ + if (hw->phy.ops.check_reset_block) + return hw->phy.ops.check_reset_block(hw); + + return E1000_SUCCESS; +} + +/** + * e1000_read_phy_reg - Reads PHY register + * @hw: pointer to the HW structure + * @offset: the register to read + * @data: the buffer to store the 16-bit read. + * + * Reads the PHY register and returns the value in data. + * This is a function pointer entry point called by drivers. + **/ +s32 e1000_read_phy_reg(struct e1000_hw *hw, u32 offset, u16 *data) +{ + if (hw->phy.ops.read_reg) + return hw->phy.ops.read_reg(hw, offset, data); + + return E1000_SUCCESS; +} + +/** + * e1000_write_phy_reg - Writes PHY register + * @hw: pointer to the HW structure + * @offset: the register to write + * @data: the value to write. + * + * Writes the PHY register at offset with the value in data. + * This is a function pointer entry point called by drivers. + **/ +s32 e1000_write_phy_reg(struct e1000_hw *hw, u32 offset, u16 data) +{ + if (hw->phy.ops.write_reg) + return hw->phy.ops.write_reg(hw, offset, data); + + return E1000_SUCCESS; +} + +/** + * e1000_release_phy - Generic release PHY + * @hw: pointer to the HW structure + * + * Return if silicon family does not require a semaphore when accessing the + * PHY. + **/ +void e1000_release_phy(struct e1000_hw *hw) +{ + if (hw->phy.ops.release) + hw->phy.ops.release(hw); +} + +/** + * e1000_acquire_phy - Generic acquire PHY + * @hw: pointer to the HW structure + * + * Return success if silicon family does not require a semaphore when + * accessing the PHY. + **/ +s32 e1000_acquire_phy(struct e1000_hw *hw) +{ + if (hw->phy.ops.acquire) + return hw->phy.ops.acquire(hw); + + return E1000_SUCCESS; +} + +/** + * e1000_read_kmrn_reg - Reads register using Kumeran interface + * @hw: pointer to the HW structure + * @offset: the register to read + * @data: the location to store the 16-bit value read. + * + * Reads a register out of the Kumeran interface. Currently no func pointer + * exists and all implementations are handled in the generic version of + * this function. + **/ +s32 e1000_read_kmrn_reg(struct e1000_hw *hw, u32 offset, u16 *data) +{ + return e1000_read_kmrn_reg_generic(hw, offset, data); +} + +/** + * e1000_write_kmrn_reg - Writes register using Kumeran interface + * @hw: pointer to the HW structure + * @offset: the register to write + * @data: the value to write. + * + * Writes a register to the Kumeran interface. Currently no func pointer + * exists and all implementations are handled in the generic version of + * this function. + **/ +s32 e1000_write_kmrn_reg(struct e1000_hw *hw, u32 offset, u16 data) +{ + return e1000_write_kmrn_reg_generic(hw, offset, data); +} + +/** + * e1000_get_cable_length - Retrieves cable length estimation + * @hw: pointer to the HW structure + * + * This function estimates the cable length and stores them in + * hw->phy.min_length and hw->phy.max_length. This is a function pointer + * entry point called by drivers. + **/ +s32 e1000_get_cable_length(struct e1000_hw *hw) +{ + if (hw->phy.ops.get_cable_length) + return hw->phy.ops.get_cable_length(hw); + + return E1000_SUCCESS; +} + +/** + * e1000_get_phy_info - Retrieves PHY information from registers + * @hw: pointer to the HW structure + * + * This function gets some information from various PHY registers and + * populates hw->phy values with it. This is a function pointer entry + * point called by drivers. + **/ +s32 e1000_get_phy_info(struct e1000_hw *hw) +{ + if (hw->phy.ops.get_info) + return hw->phy.ops.get_info(hw); + + return E1000_SUCCESS; +} + +/** + * e1000_phy_hw_reset - Hard PHY reset + * @hw: pointer to the HW structure + * + * Performs a hard PHY reset. This is a function pointer entry point called + * by drivers. + **/ +s32 e1000_phy_hw_reset(struct e1000_hw *hw) +{ + if (hw->phy.ops.reset) + return hw->phy.ops.reset(hw); + + return E1000_SUCCESS; +} + +/** + * e1000_phy_commit - Soft PHY reset + * @hw: pointer to the HW structure + * + * Performs a soft PHY reset on those that apply. This is a function pointer + * entry point called by drivers. + **/ +s32 e1000_phy_commit(struct e1000_hw *hw) +{ + if (hw->phy.ops.commit) + return hw->phy.ops.commit(hw); + + return E1000_SUCCESS; +} + +/** + * e1000_set_d0_lplu_state - Sets low power link up state for D0 + * @hw: pointer to the HW structure + * @active: boolean used to enable/disable lplu + * + * Success returns 0, Failure returns 1 + * + * The low power link up (lplu) state is set to the power management level D0 + * and SmartSpeed is disabled when active is true, else clear lplu for D0 + * and enable Smartspeed. LPLU and Smartspeed are mutually exclusive. LPLU + * is used during Dx states where the power conservation is most important. + * During driver activity, SmartSpeed should be enabled so performance is + * maintained. This is a function pointer entry point called by drivers. + **/ +s32 e1000_set_d0_lplu_state(struct e1000_hw *hw, bool active) +{ + if (hw->phy.ops.set_d0_lplu_state) + return hw->phy.ops.set_d0_lplu_state(hw, active); + + return E1000_SUCCESS; +} + +/** + * e1000_set_d3_lplu_state - Sets low power link up state for D3 + * @hw: pointer to the HW structure + * @active: boolean used to enable/disable lplu + * + * Success returns 0, Failure returns 1 + * + * The low power link up (lplu) state is set to the power management level D3 + * and SmartSpeed is disabled when active is true, else clear lplu for D3 + * and enable Smartspeed. LPLU and Smartspeed are mutually exclusive. LPLU + * is used during Dx states where the power conservation is most important. + * During driver activity, SmartSpeed should be enabled so performance is + * maintained. This is a function pointer entry point called by drivers. + **/ +s32 e1000_set_d3_lplu_state(struct e1000_hw *hw, bool active) +{ + if (hw->phy.ops.set_d3_lplu_state) + return hw->phy.ops.set_d3_lplu_state(hw, active); + + return E1000_SUCCESS; +} + +/** + * e1000_read_mac_addr - Reads MAC address + * @hw: pointer to the HW structure + * + * Reads the MAC address out of the adapter and stores it in the HW structure. + * Currently no func pointer exists and all implementations are handled in the + * generic version of this function. + **/ +s32 e1000_read_mac_addr(struct e1000_hw *hw) +{ + if (hw->mac.ops.read_mac_addr) + return hw->mac.ops.read_mac_addr(hw); + + return e1000_read_mac_addr_generic(hw); +} + +/** + * e1000_read_pba_string - Read device part number string + * @hw: pointer to the HW structure + * @pba_num: pointer to device part number + * @pba_num_size: size of part number buffer + * + * Reads the product board assembly (PBA) number from the EEPROM and stores + * the value in pba_num. + * Currently no func pointer exists and all implementations are handled in the + * generic version of this function. + **/ +s32 e1000_read_pba_string(struct e1000_hw *hw, u8 *pba_num, u32 pba_num_size) +{ + return e1000_read_pba_string_generic(hw, pba_num, pba_num_size); +} + +/** + * e1000_read_pba_length - Read device part number string length + * @hw: pointer to the HW structure + * @pba_num_size: size of part number buffer + * + * Reads the product board assembly (PBA) number length from the EEPROM and + * stores the value in pba_num. + * Currently no func pointer exists and all implementations are handled in the + * generic version of this function. + **/ +s32 e1000_read_pba_length(struct e1000_hw *hw, u32 *pba_num_size) +{ + return e1000_read_pba_length_generic(hw, pba_num_size); +} + +/** + * e1000_validate_nvm_checksum - Verifies NVM (EEPROM) checksum + * @hw: pointer to the HW structure + * + * Validates the NVM checksum is correct. This is a function pointer entry + * point called by drivers. + **/ +s32 e1000_validate_nvm_checksum(struct e1000_hw *hw) +{ + if (hw->nvm.ops.validate) + return hw->nvm.ops.validate(hw); + + return -E1000_ERR_CONFIG; +} + +/** + * e1000_update_nvm_checksum - Updates NVM (EEPROM) checksum + * @hw: pointer to the HW structure + * + * Updates the NVM checksum. Currently no func pointer exists and all + * implementations are handled in the generic version of this function. + **/ +s32 e1000_update_nvm_checksum(struct e1000_hw *hw) +{ + if (hw->nvm.ops.update) + return hw->nvm.ops.update(hw); + + return -E1000_ERR_CONFIG; +} + +/** + * e1000_reload_nvm - Reloads EEPROM + * @hw: pointer to the HW structure + * + * Reloads the EEPROM by setting the "Reinitialize from EEPROM" bit in the + * extended control register. + **/ +void e1000_reload_nvm(struct e1000_hw *hw) +{ + if (hw->nvm.ops.reload) + hw->nvm.ops.reload(hw); +} + +/** + * e1000_read_nvm - Reads NVM (EEPROM) + * @hw: pointer to the HW structure + * @offset: the word offset to read + * @words: number of 16-bit words to read + * @data: pointer to the properly sized buffer for the data. + * + * Reads 16-bit chunks of data from the NVM (EEPROM). This is a function + * pointer entry point called by drivers. + **/ +s32 e1000_read_nvm(struct e1000_hw *hw, u16 offset, u16 words, u16 *data) +{ + if (hw->nvm.ops.read) + return hw->nvm.ops.read(hw, offset, words, data); + + return -E1000_ERR_CONFIG; +} + +/** + * e1000_write_nvm - Writes to NVM (EEPROM) + * @hw: pointer to the HW structure + * @offset: the word offset to read + * @words: number of 16-bit words to write + * @data: pointer to the properly sized buffer for the data. + * + * Writes 16-bit chunks of data to the NVM (EEPROM). This is a function + * pointer entry point called by drivers. + **/ +s32 e1000_write_nvm(struct e1000_hw *hw, u16 offset, u16 words, u16 *data) +{ + if (hw->nvm.ops.write) + return hw->nvm.ops.write(hw, offset, words, data); + + return E1000_SUCCESS; +} + +/** + * e1000_write_8bit_ctrl_reg - Writes 8bit Control register + * @hw: pointer to the HW structure + * @reg: 32bit register offset + * @offset: the register to write + * @data: the value to write. + * + * Writes the PHY register at offset with the value in data. + * This is a function pointer entry point called by drivers. + **/ +s32 e1000_write_8bit_ctrl_reg(struct e1000_hw *hw, u32 reg, u32 offset, + u8 data) +{ + return e1000_write_8bit_ctrl_reg_generic(hw, reg, offset, data); +} + +/** + * e1000_power_up_phy - Restores link in case of PHY power down + * @hw: pointer to the HW structure + * + * The phy may be powered down to save power, to turn off link when the + * driver is unloaded, or wake on lan is not enabled (among others). + **/ +void e1000_power_up_phy(struct e1000_hw *hw) +{ + if (hw->phy.ops.power_up) + hw->phy.ops.power_up(hw); + + e1000_setup_link(hw); +} + +/** + * e1000_power_down_phy - Power down PHY + * @hw: pointer to the HW structure + * + * The phy may be powered down to save power, to turn off link when the + * driver is unloaded, or wake on lan is not enabled (among others). + **/ +void e1000_power_down_phy(struct e1000_hw *hw) +{ + if (hw->phy.ops.power_down) + hw->phy.ops.power_down(hw); +} + +/** + * e1000_power_up_fiber_serdes_link - Power up serdes link + * @hw: pointer to the HW structure + * + * Power on the optics and PCS. + **/ +void e1000_power_up_fiber_serdes_link(struct e1000_hw *hw) +{ + if (hw->mac.ops.power_up_serdes) + hw->mac.ops.power_up_serdes(hw); +} + +/** + * e1000_shutdown_fiber_serdes_link - Remove link during power down + * @hw: pointer to the HW structure + * + * Shutdown the optics and PCS on driver unload. + **/ +void e1000_shutdown_fiber_serdes_link(struct e1000_hw *hw) +{ + if (hw->mac.ops.shutdown_serdes) + hw->mac.ops.shutdown_serdes(hw); +} + +/** + * e1000_get_thermal_sensor_data - Gathers thermal sensor data + * @hw: pointer to hardware structure + * + * Updates the temperatures in mac.thermal_sensor_data + **/ +s32 e1000_get_thermal_sensor_data(struct e1000_hw *hw) +{ + if (hw->mac.ops.get_thermal_sensor_data) + return hw->mac.ops.get_thermal_sensor_data(hw); + + return E1000_SUCCESS; +} + +/** + * e1000_init_thermal_sensor_thresh - Sets thermal sensor thresholds + * @hw: pointer to hardware structure + * + * Sets the thermal sensor thresholds according to the NVM map + **/ +s32 e1000_init_thermal_sensor_thresh(struct e1000_hw *hw) +{ + if (hw->mac.ops.init_thermal_sensor_thresh) + return hw->mac.ops.init_thermal_sensor_thresh(hw); + + return E1000_SUCCESS; +} + diff --git a/drivers/staging/igb_avb/e1000_api.h b/drivers/staging/igb_avb/e1000_api.h new file mode 100644 index 000000000000..32fce254685a --- /dev/null +++ b/drivers/staging/igb_avb/e1000_api.h @@ -0,0 +1,152 @@ +/******************************************************************************* + + Intel(R) Gigabit Ethernet Linux driver + Copyright(c) 2007-2015 Intel Corporation. + + This program is free software; you can redistribute it and/or modify it + under the terms and conditions of the GNU General Public License, + version 2, as published by the Free Software Foundation. + + This program is distributed in the hope 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. + + The full GNU General Public License is included in this distribution in + the file called "COPYING". + + Contact Information: + Linux NICS + e1000-devel Mailing List + Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + +*******************************************************************************/ + +#ifndef _E1000_API_H_ +#define _E1000_API_H_ + +#include "e1000_hw.h" + +extern void e1000_init_function_pointers_82575(struct e1000_hw *hw); +extern void e1000_rx_fifo_flush_82575(struct e1000_hw *hw); +extern void e1000_init_function_pointers_vf(struct e1000_hw *hw); +extern void e1000_power_up_fiber_serdes_link(struct e1000_hw *hw); +extern void e1000_shutdown_fiber_serdes_link(struct e1000_hw *hw); +extern void e1000_init_function_pointers_i210(struct e1000_hw *hw); + +s32 e1000_set_obff_timer(struct e1000_hw *hw, u32 itr); +s32 e1000_set_mac_type(struct e1000_hw *hw); +s32 e1000_setup_init_funcs(struct e1000_hw *hw, bool init_device); +s32 e1000_init_mac_params(struct e1000_hw *hw); +s32 e1000_init_nvm_params(struct e1000_hw *hw); +s32 e1000_init_phy_params(struct e1000_hw *hw); +s32 e1000_init_mbx_params(struct e1000_hw *hw); +s32 e1000_get_bus_info(struct e1000_hw *hw); +void e1000_clear_vfta(struct e1000_hw *hw); +void e1000_write_vfta(struct e1000_hw *hw, u32 offset, u32 value); +s32 e1000_force_mac_fc(struct e1000_hw *hw); +s32 e1000_check_for_link(struct e1000_hw *hw); +s32 e1000_reset_hw(struct e1000_hw *hw); +s32 e1000_init_hw(struct e1000_hw *hw); +s32 e1000_setup_link(struct e1000_hw *hw); +s32 e1000_get_speed_and_duplex(struct e1000_hw *hw, u16 *speed, u16 *duplex); +s32 e1000_disable_pcie_master(struct e1000_hw *hw); +void e1000_config_collision_dist(struct e1000_hw *hw); +int e1000_rar_set(struct e1000_hw *hw, u8 *addr, u32 index); +u32 e1000_hash_mc_addr(struct e1000_hw *hw, u8 *mc_addr); +void e1000_update_mc_addr_list(struct e1000_hw *hw, u8 *mc_addr_list, + u32 mc_addr_count); +s32 e1000_setup_led(struct e1000_hw *hw); +s32 e1000_cleanup_led(struct e1000_hw *hw); +s32 e1000_check_reset_block(struct e1000_hw *hw); +s32 e1000_blink_led(struct e1000_hw *hw); +s32 e1000_led_on(struct e1000_hw *hw); +s32 e1000_led_off(struct e1000_hw *hw); +s32 e1000_id_led_init(struct e1000_hw *hw); +void e1000_reset_adaptive(struct e1000_hw *hw); +void e1000_update_adaptive(struct e1000_hw *hw); +s32 e1000_get_cable_length(struct e1000_hw *hw); +s32 e1000_validate_mdi_setting(struct e1000_hw *hw); +s32 e1000_read_phy_reg(struct e1000_hw *hw, u32 offset, u16 *data); +s32 e1000_write_phy_reg(struct e1000_hw *hw, u32 offset, u16 data); +s32 e1000_write_8bit_ctrl_reg(struct e1000_hw *hw, u32 reg, u32 offset, + u8 data); +s32 e1000_get_phy_info(struct e1000_hw *hw); +void e1000_release_phy(struct e1000_hw *hw); +s32 e1000_acquire_phy(struct e1000_hw *hw); +s32 e1000_phy_hw_reset(struct e1000_hw *hw); +s32 e1000_phy_commit(struct e1000_hw *hw); +void e1000_power_up_phy(struct e1000_hw *hw); +void e1000_power_down_phy(struct e1000_hw *hw); +s32 e1000_read_mac_addr(struct e1000_hw *hw); +s32 e1000_read_pba_string(struct e1000_hw *hw, u8 *pba_num, u32 pba_num_size); +s32 e1000_read_pba_length(struct e1000_hw *hw, u32 *pba_num_size); +void e1000_reload_nvm(struct e1000_hw *hw); +s32 e1000_update_nvm_checksum(struct e1000_hw *hw); +s32 e1000_validate_nvm_checksum(struct e1000_hw *hw); +s32 e1000_read_nvm(struct e1000_hw *hw, u16 offset, u16 words, u16 *data); +s32 e1000_read_kmrn_reg(struct e1000_hw *hw, u32 offset, u16 *data); +s32 e1000_write_kmrn_reg(struct e1000_hw *hw, u32 offset, u16 data); +s32 e1000_write_nvm(struct e1000_hw *hw, u16 offset, u16 words, u16 *data); +s32 e1000_set_d3_lplu_state(struct e1000_hw *hw, bool active); +s32 e1000_set_d0_lplu_state(struct e1000_hw *hw, bool active); +bool e1000_check_mng_mode(struct e1000_hw *hw); +bool e1000_enable_tx_pkt_filtering(struct e1000_hw *hw); +s32 e1000_mng_enable_host_if(struct e1000_hw *hw); +s32 e1000_mng_host_if_write(struct e1000_hw *hw, u8 *buffer, u16 length, + u16 offset, u8 *sum); +s32 e1000_mng_write_cmd_header(struct e1000_hw *hw, + struct e1000_host_mng_command_header *hdr); +s32 e1000_mng_write_dhcp_info(struct e1000_hw *hw, u8 *buffer, u16 length); +s32 e1000_get_thermal_sensor_data(struct e1000_hw *hw); +s32 e1000_init_thermal_sensor_thresh(struct e1000_hw *hw); + +/* + * TBI_ACCEPT macro definition: + * + * This macro requires: + * a = a pointer to struct e1000_hw + * status = the 8 bit status field of the Rx descriptor with EOP set + * errors = the 8 bit error field of the Rx descriptor with EOP set + * length = the sum of all the length fields of the Rx descriptors that + * make up the current frame + * last_byte = the last byte of the frame DMAed by the hardware + * min_frame_size = the minimum frame length we want to accept. + * max_frame_size = the maximum frame length we want to accept. + * + * This macro is a conditional that should be used in the interrupt + * handler's Rx processing routine when RxErrors have been detected. + * + * Typical use: + * ... + * if (TBI_ACCEPT) { + * accept_frame = true; + * e1000_tbi_adjust_stats(adapter, MacAddress); + * frame_length--; + * } else { + * accept_frame = false; + * } + * ... + */ + +/* The carrier extension symbol, as received by the NIC. */ +#define CARRIER_EXTENSION 0x0F + +#define TBI_ACCEPT(a, status, errors, length, last_byte, \ + min_frame_size, max_frame_size) \ + (e1000_tbi_sbp_enabled_82543(a) && \ + (((errors) & E1000_RXD_ERR_FRAME_ERR_MASK) == E1000_RXD_ERR_CE) && \ + ((last_byte) == CARRIER_EXTENSION) && \ + (((status) & E1000_RXD_STAT_VP) ? \ + (((length) > ((min_frame_size) - VLAN_TAG_SIZE)) && \ + ((length) <= ((max_frame_size) + 1))) : \ + (((length) > (min_frame_size)) && \ + ((length) <= ((max_frame_size) + VLAN_TAG_SIZE + 1))))) + +#ifndef E1000_MAX +#define E1000_MAX(a, b) ((a) > (b) ? (a) : (b)) +#endif +#ifndef E1000_DIVIDE_ROUND_UP +#define E1000_DIVIDE_ROUND_UP(a, b) (((a) + (b) - 1) / (b)) /* ceil(a/b) */ +#endif +#endif /* _E1000_API_H_ */ diff --git a/drivers/staging/igb_avb/e1000_defines.h b/drivers/staging/igb_avb/e1000_defines.h new file mode 100644 index 000000000000..4022e22be775 --- /dev/null +++ b/drivers/staging/igb_avb/e1000_defines.h @@ -0,0 +1,1486 @@ +/******************************************************************************* + + Intel(R) Gigabit Ethernet Linux driver + Copyright(c) 2007-2015 Intel Corporation. + + This program is free software; you can redistribute it and/or modify it + under the terms and conditions of the GNU General Public License, + version 2, as published by the Free Software Foundation. + + This program is distributed in the hope 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. + + The full GNU General Public License is included in this distribution in + the file called "COPYING". + + Contact Information: + Linux NICS + e1000-devel Mailing List + Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + +*******************************************************************************/ + +#ifndef _E1000_DEFINES_H_ +#define _E1000_DEFINES_H_ + +/* Number of Transmit and Receive Descriptors must be a multiple of 8 */ +#define REQ_TX_DESCRIPTOR_MULTIPLE 8 +#define REQ_RX_DESCRIPTOR_MULTIPLE 8 + +/* Definitions for power management and wakeup registers */ +/* Wake Up Control */ +#define E1000_WUC_APME 0x00000001 /* APM Enable */ +#define E1000_WUC_PME_EN 0x00000002 /* PME Enable */ +#define E1000_WUC_PME_STATUS 0x00000004 /* PME Status */ +#define E1000_WUC_APMPME 0x00000008 /* Assert PME on APM Wakeup */ +#define E1000_WUC_PHY_WAKE 0x00000100 /* if PHY supports wakeup */ + +/* Wake Up Filter Control */ +#define E1000_WUFC_LNKC 0x00000001 /* Link Status Change Wakeup Enable */ +#define E1000_WUFC_MAG 0x00000002 /* Magic Packet Wakeup Enable */ +#define E1000_WUFC_EX 0x00000004 /* Directed Exact Wakeup Enable */ +#define E1000_WUFC_MC 0x00000008 /* Directed Multicast Wakeup Enable */ +#define E1000_WUFC_BC 0x00000010 /* Broadcast Wakeup Enable */ +#define E1000_WUFC_ARP 0x00000020 /* ARP Request Packet Wakeup Enable */ +#define E1000_WUFC_IPV4 0x00000040 /* Directed IPv4 Packet Wakeup Enable */ +#define E1000_WUFC_FLX0 0x00010000 /* Flexible Filter 0 Enable */ + +/* Wake Up Status */ +#define E1000_WUS_LNKC E1000_WUFC_LNKC +#define E1000_WUS_MAG E1000_WUFC_MAG +#define E1000_WUS_EX E1000_WUFC_EX +#define E1000_WUS_MC E1000_WUFC_MC +#define E1000_WUS_BC E1000_WUFC_BC + +/* Extended Device Control */ +#define E1000_CTRL_EXT_LPCD 0x00000004 /* LCD Power Cycle Done */ +#define E1000_CTRL_EXT_SDP4_DATA 0x00000010 /* SW Definable Pin 4 data */ +#define E1000_CTRL_EXT_SDP6_DATA 0x00000040 /* SW Definable Pin 6 data */ +#define E1000_CTRL_EXT_SDP2_DATA 0x00000040 /* SW Definable Pin 2 data */ +#define E1000_CTRL_EXT_SDP3_DATA 0x00000080 /* SW Definable Pin 3 data */ +#define E1000_CTRL_EXT_SDP6_DIR 0x00000400 /* Direction of SDP6 0=in 1=out */ +#define E1000_CTRL_EXT_SDP2_DIR 0x00000400 /* Direction of SDP2 0=in 1=out */ +#define E1000_CTRL_EXT_SDP3_DIR 0x00000800 /* Direction of SDP3 0=in 1=out */ +#define E1000_CTRL_EXT_FORCE_SMBUS 0x00000800 /* Force SMBus mode */ +#define E1000_CTRL_EXT_EE_RST 0x00002000 /* Reinitialize from EEPROM */ +/* Physical Func Reset Done Indication */ +#define E1000_CTRL_EXT_PFRSTD 0x00004000 +#define E1000_CTRL_EXT_SDLPE 0X00040000 /* SerDes Low Power Enable */ +#define E1000_CTRL_EXT_SPD_BYPS 0x00008000 /* Speed Select Bypass */ +#define E1000_CTRL_EXT_RO_DIS 0x00020000 /* Relaxed Ordering disable */ +#define E1000_CTRL_EXT_DMA_DYN_CLK_EN 0x00080000 /* DMA Dynamic Clk Gating */ +#define E1000_CTRL_EXT_LINK_MODE_MASK 0x00C00000 +/* Offset of the link mode field in Ctrl Ext register */ +#define E1000_CTRL_EXT_LINK_MODE_OFFSET 22 +#define E1000_CTRL_EXT_LINK_MODE_1000BASE_KX 0x00400000 +#define E1000_CTRL_EXT_LINK_MODE_GMII 0x00000000 +#define E1000_CTRL_EXT_LINK_MODE_PCIE_SERDES 0x00C00000 +#define E1000_CTRL_EXT_LINK_MODE_SGMII 0x00800000 +#define E1000_CTRL_EXT_EIAME 0x01000000 +#define E1000_CTRL_EXT_IRCA 0x00000001 +#define E1000_CTRL_EXT_DRV_LOAD 0x10000000 /* Drv loaded bit for FW */ +#define E1000_CTRL_EXT_IAME 0x08000000 /* Int ACK Auto-mask */ +#define E1000_CTRL_EXT_PBA_CLR 0x80000000 /* PBA Clear */ +#define E1000_CTRL_EXT_PHYPDEN 0x00100000 +#define E1000_I2CCMD_REG_ADDR_SHIFT 16 +#define E1000_I2CCMD_PHY_ADDR_SHIFT 24 +#define E1000_I2CCMD_OPCODE_READ 0x08000000 +#define E1000_I2CCMD_OPCODE_WRITE 0x00000000 +#define E1000_I2CCMD_READY 0x20000000 +#define E1000_I2CCMD_ERROR 0x80000000 +#define E1000_I2CCMD_SFP_DATA_ADDR(a) (0x0000 + (a)) +#define E1000_I2CCMD_SFP_DIAG_ADDR(a) (0x0100 + (a)) +#define E1000_MAX_SGMII_PHY_REG_ADDR 255 +#define E1000_I2CCMD_PHY_TIMEOUT 200 +#define E1000_IVAR_VALID 0x80 +#define E1000_GPIE_NSICR 0x00000001 +#define E1000_GPIE_MSIX_MODE 0x00000010 +#define E1000_GPIE_EIAME 0x40000000 +#define E1000_GPIE_PBA 0x80000000 + +/* Receive Descriptor bit definitions */ +#define E1000_RXD_STAT_DD 0x01 /* Descriptor Done */ +#define E1000_RXD_STAT_EOP 0x02 /* End of Packet */ +#define E1000_RXD_STAT_IXSM 0x04 /* Ignore checksum */ +#define E1000_RXD_STAT_VP 0x08 /* IEEE VLAN Packet */ +#define E1000_RXD_STAT_UDPCS 0x10 /* UDP xsum calculated */ +#define E1000_RXD_STAT_TCPCS 0x20 /* TCP xsum calculated */ +#define E1000_RXD_STAT_IPCS 0x40 /* IP xsum calculated */ +#define E1000_RXD_STAT_PIF 0x80 /* passed in-exact filter */ +#define E1000_RXD_STAT_IPIDV 0x200 /* IP identification valid */ +#define E1000_RXD_STAT_UDPV 0x400 /* Valid UDP checksum */ +#define E1000_RXD_STAT_DYNINT 0x800 /* Pkt caused INT via DYNINT */ +#define E1000_RXD_ERR_CE 0x01 /* CRC Error */ +#define E1000_RXD_ERR_SE 0x02 /* Symbol Error */ +#define E1000_RXD_ERR_SEQ 0x04 /* Sequence Error */ +#define E1000_RXD_ERR_CXE 0x10 /* Carrier Extension Error */ +#define E1000_RXD_ERR_TCPE 0x20 /* TCP/UDP Checksum Error */ +#define E1000_RXD_ERR_IPE 0x40 /* IP Checksum Error */ +#define E1000_RXD_ERR_RXE 0x80 /* Rx Data Error */ +#define E1000_RXD_SPC_VLAN_MASK 0x0FFF /* VLAN ID is in lower 12 bits */ + +#define E1000_RXDEXT_STATERR_TST 0x00000100 /* Time Stamp taken */ +#define E1000_RXDEXT_STATERR_LB 0x00040000 +#define E1000_RXDEXT_STATERR_CE 0x01000000 +#define E1000_RXDEXT_STATERR_SE 0x02000000 +#define E1000_RXDEXT_STATERR_SEQ 0x04000000 +#define E1000_RXDEXT_STATERR_CXE 0x10000000 +#define E1000_RXDEXT_STATERR_TCPE 0x20000000 +#define E1000_RXDEXT_STATERR_IPE 0x40000000 +#define E1000_RXDEXT_STATERR_RXE 0x80000000 + +/* mask to determine if packets should be dropped due to frame errors */ +#define E1000_RXD_ERR_FRAME_ERR_MASK ( \ + E1000_RXD_ERR_CE | \ + E1000_RXD_ERR_SE | \ + E1000_RXD_ERR_SEQ | \ + E1000_RXD_ERR_CXE | \ + E1000_RXD_ERR_RXE) + +/* Same mask, but for extended and packet split descriptors */ +#define E1000_RXDEXT_ERR_FRAME_ERR_MASK ( \ + E1000_RXDEXT_STATERR_CE | \ + E1000_RXDEXT_STATERR_SE | \ + E1000_RXDEXT_STATERR_SEQ | \ + E1000_RXDEXT_STATERR_CXE | \ + E1000_RXDEXT_STATERR_RXE) + +#define E1000_MRQC_RSS_FIELD_MASK 0xFFFF0000 +#define E1000_MRQC_RSS_FIELD_IPV4_TCP 0x00010000 +#define E1000_MRQC_RSS_FIELD_IPV4 0x00020000 +#define E1000_MRQC_RSS_FIELD_IPV6_TCP_EX 0x00040000 +#define E1000_MRQC_RSS_FIELD_IPV6 0x00100000 +#define E1000_MRQC_RSS_FIELD_IPV6_TCP 0x00200000 + +#define E1000_RXDPS_HDRSTAT_HDRSP 0x00008000 + +/* Management Control */ +#define E1000_MANC_SMBUS_EN 0x00000001 /* SMBus Enabled - RO */ +#define E1000_MANC_ASF_EN 0x00000002 /* ASF Enabled - RO */ +#define E1000_MANC_ARP_EN 0x00002000 /* Enable ARP Request Filtering */ +#define E1000_MANC_RCV_TCO_EN 0x00020000 /* Receive TCO Packets Enabled */ +#define E1000_MANC_BLK_PHY_RST_ON_IDE 0x00040000 /* Block phy resets */ +/* Enable MAC address filtering */ +#define E1000_MANC_EN_MAC_ADDR_FILTER 0x00100000 +/* Enable MNG packets to host memory */ +#define E1000_MANC_EN_MNG2HOST 0x00200000 + +#define E1000_MANC2H_PORT_623 0x00000020 /* Port 0x26f */ +#define E1000_MANC2H_PORT_664 0x00000040 /* Port 0x298 */ +#define E1000_MDEF_PORT_623 0x00000800 /* Port 0x26f */ +#define E1000_MDEF_PORT_664 0x00000400 /* Port 0x298 */ + +/* Receive Control */ +#define E1000_RCTL_RST 0x00000001 /* Software reset */ +#define E1000_RCTL_EN 0x00000002 /* enable */ +#define E1000_RCTL_SBP 0x00000004 /* store bad packet */ +#define E1000_RCTL_UPE 0x00000008 /* unicast promisc enable */ +#define E1000_RCTL_MPE 0x00000010 /* multicast promisc enable */ +#define E1000_RCTL_LPE 0x00000020 /* long packet enable */ +#define E1000_RCTL_LBM_NO 0x00000000 /* no loopback mode */ +#define E1000_RCTL_LBM_MAC 0x00000040 /* MAC loopback mode */ +#define E1000_RCTL_LBM_TCVR 0x000000C0 /* tcvr loopback mode */ +#define E1000_RCTL_DTYP_PS 0x00000400 /* Packet Split descriptor */ +#define E1000_RCTL_RDMTS_HALF 0x00000000 /* Rx desc min thresh size */ +#define E1000_RCTL_RDMTS_HEX 0x00010000 +#define E1000_RCTL_MO_SHIFT 12 /* multicast offset shift */ +#define E1000_RCTL_MO_3 0x00003000 /* multicast offset 15:4 */ +#define E1000_RCTL_BAM 0x00008000 /* broadcast enable */ +/* these buffer sizes are valid if E1000_RCTL_BSEX is 0 */ +#define E1000_RCTL_SZ_2048 0x00000000 /* Rx buffer size 2048 */ +#define E1000_RCTL_SZ_1024 0x00010000 /* Rx buffer size 1024 */ +#define E1000_RCTL_SZ_512 0x00020000 /* Rx buffer size 512 */ +#define E1000_RCTL_SZ_256 0x00030000 /* Rx buffer size 256 */ +/* these buffer sizes are valid if E1000_RCTL_BSEX is 1 */ +#define E1000_RCTL_SZ_16384 0x00010000 /* Rx buffer size 16384 */ +#define E1000_RCTL_SZ_8192 0x00020000 /* Rx buffer size 8192 */ +#define E1000_RCTL_SZ_4096 0x00030000 /* Rx buffer size 4096 */ +#define E1000_RCTL_VFE 0x00040000 /* vlan filter enable */ +#define E1000_RCTL_CFIEN 0x00080000 /* canonical form enable */ +#define E1000_RCTL_CFI 0x00100000 /* canonical form indicator */ +#define E1000_RCTL_DPF 0x00400000 /* discard pause frames */ +#define E1000_RCTL_PMCF 0x00800000 /* pass MAC control frames */ +#define E1000_RCTL_BSEX 0x02000000 /* Buffer size extension */ +#define E1000_RCTL_SECRC 0x04000000 /* Strip Ethernet CRC */ + +/* Use byte values for the following shift parameters + * Usage: + * psrctl |= (((ROUNDUP(value0, 128) >> E1000_PSRCTL_BSIZE0_SHIFT) & + * E1000_PSRCTL_BSIZE0_MASK) | + * ((ROUNDUP(value1, 1024) >> E1000_PSRCTL_BSIZE1_SHIFT) & + * E1000_PSRCTL_BSIZE1_MASK) | + * ((ROUNDUP(value2, 1024) << E1000_PSRCTL_BSIZE2_SHIFT) & + * E1000_PSRCTL_BSIZE2_MASK) | + * ((ROUNDUP(value3, 1024) << E1000_PSRCTL_BSIZE3_SHIFT) |; + * E1000_PSRCTL_BSIZE3_MASK)) + * where value0 = [128..16256], default=256 + * value1 = [1024..64512], default=4096 + * value2 = [0..64512], default=4096 + * value3 = [0..64512], default=0 + */ + +#define E1000_PSRCTL_BSIZE0_MASK 0x0000007F +#define E1000_PSRCTL_BSIZE1_MASK 0x00003F00 +#define E1000_PSRCTL_BSIZE2_MASK 0x003F0000 +#define E1000_PSRCTL_BSIZE3_MASK 0x3F000000 + +#define E1000_PSRCTL_BSIZE0_SHIFT 7 /* Shift _right_ 7 */ +#define E1000_PSRCTL_BSIZE1_SHIFT 2 /* Shift _right_ 2 */ +#define E1000_PSRCTL_BSIZE2_SHIFT 6 /* Shift _left_ 6 */ +#define E1000_PSRCTL_BSIZE3_SHIFT 14 /* Shift _left_ 14 */ + +/* SWFW_SYNC Definitions */ +#define E1000_SWFW_EEP_SM 0x01 +#define E1000_SWFW_PHY0_SM 0x02 +#define E1000_SWFW_PHY1_SM 0x04 +#define E1000_SWFW_CSR_SM 0x08 +#define E1000_SWFW_PHY2_SM 0x20 +#define E1000_SWFW_PHY3_SM 0x40 +#define E1000_SWFW_SW_MNG_SM 0x400 + +/* Device Control */ +#define E1000_CTRL_FD 0x00000001 /* Full duplex.0=half; 1=full */ +#define E1000_CTRL_PRIOR 0x00000004 /* Priority on PCI. 0=rx,1=fair */ +#define E1000_CTRL_GIO_MASTER_DISABLE 0x00000004 /*Blocks new Master reqs */ +#define E1000_CTRL_LRST 0x00000008 /* Link reset. 0=normal,1=reset */ +#define E1000_CTRL_ASDE 0x00000020 /* Auto-speed detect enable */ +#define E1000_CTRL_SLU 0x00000040 /* Set link up (Force Link) */ +#define E1000_CTRL_ILOS 0x00000080 /* Invert Loss-Of Signal */ +#define E1000_CTRL_SPD_SEL 0x00000300 /* Speed Select Mask */ +#define E1000_CTRL_SPD_10 0x00000000 /* Force 10Mb */ +#define E1000_CTRL_SPD_100 0x00000100 /* Force 100Mb */ +#define E1000_CTRL_SPD_1000 0x00000200 /* Force 1Gb */ +#define E1000_CTRL_FRCSPD 0x00000800 /* Force Speed */ +#define E1000_CTRL_FRCDPX 0x00001000 /* Force Duplex */ +#define E1000_CTRL_SWDPIN0 0x00040000 /* SWDPIN 0 value */ +#define E1000_CTRL_SWDPIN1 0x00080000 /* SWDPIN 1 value */ +#define E1000_CTRL_SWDPIN2 0x00100000 /* SWDPIN 2 value */ +#define E1000_CTRL_ADVD3WUC 0x00100000 /* D3 WUC */ +#define E1000_CTRL_SWDPIN3 0x00200000 /* SWDPIN 3 value */ +#define E1000_CTRL_SWDPIO0 0x00400000 /* SWDPIN 0 Input or output */ +#define E1000_CTRL_SDP0_DIR 0x00400000 /* SDP0 Data direction */ +#define E1000_CTRL_SDP1_DIR 0x00800000 /* SDP1 Data direction */ +#define E1000_CTRL_RST 0x04000000 /* Global reset */ +#define E1000_CTRL_RFCE 0x08000000 /* Receive Flow Control enable */ +#define E1000_CTRL_TFCE 0x10000000 /* Transmit flow control enable */ +#define E1000_CTRL_VME 0x40000000 /* IEEE VLAN mode enable */ +#define E1000_CTRL_PHY_RST 0x80000000 /* PHY Reset */ +#define E1000_CTRL_I2C_ENA 0x02000000 /* I2C enable */ + +#define E1000_CONNSW_ENRGSRC 0x4 +#define E1000_CONNSW_PHYSD 0x400 +#define E1000_CONNSW_PHY_PDN 0x800 +#define E1000_CONNSW_SERDESD 0x200 +#define E1000_CONNSW_AUTOSENSE_CONF 0x2 +#define E1000_CONNSW_AUTOSENSE_EN 0x1 +#define E1000_PCS_CFG_PCS_EN 8 +#define E1000_PCS_LCTL_FLV_LINK_UP 1 +#define E1000_PCS_LCTL_FSV_10 0 +#define E1000_PCS_LCTL_FSV_100 2 +#define E1000_PCS_LCTL_FSV_1000 4 +#define E1000_PCS_LCTL_FDV_FULL 8 +#define E1000_PCS_LCTL_FSD 0x10 +#define E1000_PCS_LCTL_FORCE_LINK 0x20 +#define E1000_PCS_LCTL_FORCE_FCTRL 0x80 +#define E1000_PCS_LCTL_AN_ENABLE 0x10000 +#define E1000_PCS_LCTL_AN_RESTART 0x20000 +#define E1000_PCS_LCTL_AN_TIMEOUT 0x40000 +#define E1000_ENABLE_SERDES_LOOPBACK 0x0410 + +#define E1000_PCS_LSTS_LINK_OK 1 +#define E1000_PCS_LSTS_SPEED_100 2 +#define E1000_PCS_LSTS_SPEED_1000 4 +#define E1000_PCS_LSTS_DUPLEX_FULL 8 +#define E1000_PCS_LSTS_SYNK_OK 0x10 +#define E1000_PCS_LSTS_AN_COMPLETE 0x10000 + +/* Device Status */ +#define E1000_STATUS_FD 0x00000001 /* Duplex 0=half 1=full */ +#define E1000_STATUS_LU 0x00000002 /* Link up.0=no,1=link */ +#define E1000_STATUS_FUNC_MASK 0x0000000C /* PCI Function Mask */ +#define E1000_STATUS_FUNC_SHIFT 2 +#define E1000_STATUS_FUNC_1 0x00000004 /* Function 1 */ +#define E1000_STATUS_TXOFF 0x00000010 /* transmission paused */ +#define E1000_STATUS_SPEED_MASK 0x000000C0 +#define E1000_STATUS_SPEED_10 0x00000000 /* Speed 10Mb/s */ +#define E1000_STATUS_SPEED_100 0x00000040 /* Speed 100Mb/s */ +#define E1000_STATUS_SPEED_1000 0x00000080 /* Speed 1000Mb/s */ +#define E1000_STATUS_LAN_INIT_DONE 0x00000200 /* Lan Init Compltn by NVM */ +#define E1000_STATUS_PHYRA 0x00000400 /* PHY Reset Asserted */ +#define E1000_STATUS_GIO_MASTER_ENABLE 0x00080000 /* Master request status */ +#define E1000_STATUS_2P5_SKU 0x00001000 /* Val of 2.5GBE SKU strap */ +#define E1000_STATUS_2P5_SKU_OVER 0x00002000 /* Val of 2.5GBE SKU Over */ + +#define SPEED_10 10 +#define SPEED_100 100 +#define SPEED_1000 1000 +#define SPEED_2500 2500 +#define HALF_DUPLEX 1 +#define FULL_DUPLEX 2 + +#define ADVERTISE_10_HALF 0x0001 +#define ADVERTISE_10_FULL 0x0002 +#define ADVERTISE_100_HALF 0x0004 +#define ADVERTISE_100_FULL 0x0008 +#define ADVERTISE_1000_HALF 0x0010 /* Not used, just FYI */ +#define ADVERTISE_1000_FULL 0x0020 + +/* 1000/H is not supported, nor spec-compliant. */ +#define E1000_ALL_SPEED_DUPLEX ( \ + ADVERTISE_10_HALF | ADVERTISE_10_FULL | ADVERTISE_100_HALF | \ + ADVERTISE_100_FULL | ADVERTISE_1000_FULL) +#define E1000_ALL_NOT_GIG ( \ + ADVERTISE_10_HALF | ADVERTISE_10_FULL | ADVERTISE_100_HALF | \ + ADVERTISE_100_FULL) +#define E1000_ALL_100_SPEED (ADVERTISE_100_HALF | ADVERTISE_100_FULL) +#define E1000_ALL_10_SPEED (ADVERTISE_10_HALF | ADVERTISE_10_FULL) +#define E1000_ALL_HALF_DUPLEX (ADVERTISE_10_HALF | ADVERTISE_100_HALF) + +#define AUTONEG_ADVERTISE_SPEED_DEFAULT E1000_ALL_SPEED_DUPLEX + +/* LED Control */ +#define E1000_LEDCTL_LED0_MODE_MASK 0x0000000F +#define E1000_LEDCTL_LED0_MODE_SHIFT 0 +#define E1000_LEDCTL_LED0_IVRT 0x00000040 +#define E1000_LEDCTL_LED0_BLINK 0x00000080 + +#define E1000_LEDCTL_MODE_LED_ON 0xE +#define E1000_LEDCTL_MODE_LED_OFF 0xF + +/* Transmit Descriptor bit definitions */ +#define E1000_TXD_DTYP_D 0x00100000 /* Data Descriptor */ +#define E1000_TXD_DTYP_C 0x00000000 /* Context Descriptor */ +#define E1000_TXD_POPTS_IXSM 0x01 /* Insert IP checksum */ +#define E1000_TXD_POPTS_TXSM 0x02 /* Insert TCP/UDP checksum */ +#define E1000_TXD_CMD_EOP 0x01000000 /* End of Packet */ +#define E1000_TXD_CMD_IFCS 0x02000000 /* Insert FCS (Ethernet CRC) */ +#define E1000_TXD_CMD_IC 0x04000000 /* Insert Checksum */ +#define E1000_TXD_CMD_RS 0x08000000 /* Report Status */ +#define E1000_TXD_CMD_RPS 0x10000000 /* Report Packet Sent */ +#define E1000_TXD_CMD_DEXT 0x20000000 /* Desc extension (0 = legacy) */ +#define E1000_TXD_CMD_VLE 0x40000000 /* Add VLAN tag */ +#define E1000_TXD_CMD_IDE 0x80000000 /* Enable Tidv register */ +#define E1000_TXD_STAT_DD 0x00000001 /* Descriptor Done */ +#define E1000_TXD_STAT_EC 0x00000002 /* Excess Collisions */ +#define E1000_TXD_STAT_LC 0x00000004 /* Late Collisions */ +#define E1000_TXD_STAT_TU 0x00000008 /* Transmit underrun */ +#define E1000_TXD_CMD_TCP 0x01000000 /* TCP packet */ +#define E1000_TXD_CMD_IP 0x02000000 /* IP packet */ +#define E1000_TXD_CMD_TSE 0x04000000 /* TCP Seg enable */ +#define E1000_TXD_STAT_TC 0x00000004 /* Tx Underrun */ +#define E1000_TXD_EXTCMD_TSTAMP 0x00000010 /* IEEE1588 Timestamp packet */ + +/* Transmit Control */ +#define E1000_TCTL_EN 0x00000002 /* enable Tx */ +#define E1000_TCTL_PSP 0x00000008 /* pad short packets */ +#define E1000_TCTL_CT 0x00000ff0 /* collision threshold */ +#define E1000_TCTL_COLD 0x003ff000 /* collision distance */ +#define E1000_TCTL_RTLC 0x01000000 /* Re-transmit on late collision */ +#define E1000_TCTL_MULR 0x10000000 /* Multiple request support */ + +/* Transmit Arbitration Count */ +#define E1000_TARC0_ENABLE 0x00000400 /* Enable Tx Queue 0 */ + +/* SerDes Control */ +#define E1000_SCTL_DISABLE_SERDES_LOOPBACK 0x0400 +#define E1000_SCTL_ENABLE_SERDES_LOOPBACK 0x0410 + +/* Receive Checksum Control */ +#define E1000_RXCSUM_IPOFL 0x00000100 /* IPv4 checksum offload */ +#define E1000_RXCSUM_TUOFL 0x00000200 /* TCP / UDP checksum offload */ +#define E1000_RXCSUM_CRCOFL 0x00000800 /* CRC32 offload enable */ +#define E1000_RXCSUM_IPPCSE 0x00001000 /* IP payload checksum enable */ +#define E1000_RXCSUM_PCSD 0x00002000 /* packet checksum disabled */ + +/* Header split receive */ +#define E1000_RFCTL_NFSW_DIS 0x00000040 +#define E1000_RFCTL_NFSR_DIS 0x00000080 +#define E1000_RFCTL_ACK_DIS 0x00001000 +#define E1000_RFCTL_EXTEN 0x00008000 +#define E1000_RFCTL_IPV6_EX_DIS 0x00010000 +#define E1000_RFCTL_NEW_IPV6_EXT_DIS 0x00020000 +#define E1000_RFCTL_LEF 0x00040000 + +/* Collision related configuration parameters */ +#define E1000_COLLISION_THRESHOLD 15 +#define E1000_CT_SHIFT 4 +#define E1000_COLLISION_DISTANCE 63 +#define E1000_COLD_SHIFT 12 + +/* Default values for the transmit IPG register */ +#define DEFAULT_82543_TIPG_IPGT_FIBER 9 +#define DEFAULT_82543_TIPG_IPGT_COPPER 8 + +#define E1000_TIPG_IPGT_MASK 0x000003FF + +#define DEFAULT_82543_TIPG_IPGR1 8 +#define E1000_TIPG_IPGR1_SHIFT 10 + +#define DEFAULT_82543_TIPG_IPGR2 6 +#define DEFAULT_80003ES2LAN_TIPG_IPGR2 7 +#define E1000_TIPG_IPGR2_SHIFT 20 + +/* Ethertype field values */ +#define ETHERNET_IEEE_VLAN_TYPE 0x8100 /* 802.3ac packet */ + +#define ETHERNET_FCS_SIZE 4 +#define MAX_JUMBO_FRAME_SIZE 0x3F00 +#define E1000_TX_PTR_GAP 0x1F + +/* Extended Configuration Control and Size */ +#define E1000_EXTCNF_CTRL_MDIO_SW_OWNERSHIP 0x00000020 +#define E1000_EXTCNF_CTRL_LCD_WRITE_ENABLE 0x00000001 +#define E1000_EXTCNF_CTRL_OEM_WRITE_ENABLE 0x00000008 +#define E1000_EXTCNF_CTRL_SWFLAG 0x00000020 +#define E1000_EXTCNF_CTRL_GATE_PHY_CFG 0x00000080 +#define E1000_EXTCNF_SIZE_EXT_PCIE_LENGTH_MASK 0x00FF0000 +#define E1000_EXTCNF_SIZE_EXT_PCIE_LENGTH_SHIFT 16 +#define E1000_EXTCNF_CTRL_EXT_CNF_POINTER_MASK 0x0FFF0000 +#define E1000_EXTCNF_CTRL_EXT_CNF_POINTER_SHIFT 16 + +#define E1000_PHY_CTRL_D0A_LPLU 0x00000002 +#define E1000_PHY_CTRL_NOND0A_LPLU 0x00000004 +#define E1000_PHY_CTRL_NOND0A_GBE_DISABLE 0x00000008 +#define E1000_PHY_CTRL_GBE_DISABLE 0x00000040 + +#define E1000_KABGTXD_BGSQLBIAS 0x00050000 + +/* PBA constants */ +#define E1000_PBA_8K 0x0008 /* 8KB */ +#define E1000_PBA_10K 0x000A /* 10KB */ +#define E1000_PBA_12K 0x000C /* 12KB */ +#define E1000_PBA_14K 0x000E /* 14KB */ +#define E1000_PBA_16K 0x0010 /* 16KB */ +#define E1000_PBA_18K 0x0012 +#define E1000_PBA_20K 0x0014 +#define E1000_PBA_22K 0x0016 +#define E1000_PBA_24K 0x0018 +#define E1000_PBA_26K 0x001A +#define E1000_PBA_30K 0x001E +#define E1000_PBA_32K 0x0020 +#define E1000_PBA_34K 0x0022 +#define E1000_PBA_35K 0x0023 +#define E1000_PBA_38K 0x0026 +#define E1000_PBA_40K 0x0028 +#define E1000_PBA_48K 0x0030 /* 48KB */ +#define E1000_PBA_64K 0x0040 /* 64KB */ + +#define E1000_PBA_RXA_MASK 0xFFFF + +#define E1000_PBS_16K E1000_PBA_16K + +/* Uncorrectable/correctable ECC Error counts and enable bits */ +#define E1000_PBECCSTS_CORR_ERR_CNT_MASK 0x000000FF +#define E1000_PBECCSTS_UNCORR_ERR_CNT_MASK 0x0000FF00 +#define E1000_PBECCSTS_UNCORR_ERR_CNT_SHIFT 8 +#define E1000_PBECCSTS_ECC_ENABLE 0x00010000 + +#define IFS_MAX 80 +#define IFS_MIN 40 +#define IFS_RATIO 4 +#define IFS_STEP 10 +#define MIN_NUM_XMITS 1000 + +/* SW Semaphore Register */ +#define E1000_SWSM_SMBI 0x00000001 /* Driver Semaphore bit */ +#define E1000_SWSM_SWESMBI 0x00000002 /* FW Semaphore bit */ +#define E1000_SWSM_DRV_LOAD 0x00000008 /* Driver Loaded Bit */ + +#define E1000_SWSM2_LOCK 0x00000002 /* Secondary driver semaphore bit */ + +/* Interrupt Cause Read */ +#define E1000_ICR_TXDW 0x00000001 /* Transmit desc written back */ +#define E1000_ICR_TXQE 0x00000002 /* Transmit Queue empty */ +#define E1000_ICR_LSC 0x00000004 /* Link Status Change */ +#define E1000_ICR_RXSEQ 0x00000008 /* Rx sequence error */ +#define E1000_ICR_RXDMT0 0x00000010 /* Rx desc min. threshold (0) */ +#define E1000_ICR_RXO 0x00000040 /* Rx overrun */ +#define E1000_ICR_RXT0 0x00000080 /* Rx timer intr (ring 0) */ +#define E1000_ICR_VMMB 0x00000100 /* VM MB event */ +#define E1000_ICR_RXCFG 0x00000400 /* Rx /c/ ordered set */ +#define E1000_ICR_GPI_EN0 0x00000800 /* GP Int 0 */ +#define E1000_ICR_GPI_EN1 0x00001000 /* GP Int 1 */ +#define E1000_ICR_GPI_EN2 0x00002000 /* GP Int 2 */ +#define E1000_ICR_GPI_EN3 0x00004000 /* GP Int 3 */ +#define E1000_ICR_TXD_LOW 0x00008000 +#define E1000_ICR_MNG 0x00040000 /* Manageability event */ +#define E1000_ICR_ECCER 0x00400000 /* Uncorrectable ECC Error */ +#define E1000_ICR_TS 0x00080000 /* Time Sync Interrupt */ +#define E1000_ICR_DRSTA 0x40000000 /* Device Reset Asserted */ +/* If this bit asserted, the driver should claim the interrupt */ +#define E1000_ICR_INT_ASSERTED 0x80000000 +#define E1000_ICR_DOUTSYNC 0x10000000 /* NIC DMA out of sync */ +#define E1000_ICR_FER 0x00400000 /* Fatal Error */ + +#define E1000_ICR_THS 0x00800000 /* ICR.THS: Thermal Sensor Event*/ +#define E1000_ICR_MDDET 0x10000000 /* Malicious Driver Detect */ + +/* Extended Interrupt Cause Read */ +#define E1000_EICR_RX_QUEUE0 0x00000001 /* Rx Queue 0 Interrupt */ +#define E1000_EICR_RX_QUEUE1 0x00000002 /* Rx Queue 1 Interrupt */ +#define E1000_EICR_RX_QUEUE2 0x00000004 /* Rx Queue 2 Interrupt */ +#define E1000_EICR_RX_QUEUE3 0x00000008 /* Rx Queue 3 Interrupt */ +#define E1000_EICR_TX_QUEUE0 0x00000100 /* Tx Queue 0 Interrupt */ +#define E1000_EICR_TX_QUEUE1 0x00000200 /* Tx Queue 1 Interrupt */ +#define E1000_EICR_TX_QUEUE2 0x00000400 /* Tx Queue 2 Interrupt */ +#define E1000_EICR_TX_QUEUE3 0x00000800 /* Tx Queue 3 Interrupt */ +#define E1000_EICR_TCP_TIMER 0x40000000 /* TCP Timer */ +#define E1000_EICR_OTHER 0x80000000 /* Interrupt Cause Active */ +/* TCP Timer */ +#define E1000_TCPTIMER_KS 0x00000100 /* KickStart */ +#define E1000_TCPTIMER_COUNT_ENABLE 0x00000200 /* Count Enable */ +#define E1000_TCPTIMER_COUNT_FINISH 0x00000400 /* Count finish */ +#define E1000_TCPTIMER_LOOP 0x00000800 /* Loop */ + +/* This defines the bits that are set in the Interrupt Mask + * Set/Read Register. Each bit is documented below: + * o RXT0 = Receiver Timer Interrupt (ring 0) + * o TXDW = Transmit Descriptor Written Back + * o RXDMT0 = Receive Descriptor Minimum Threshold hit (ring 0) + * o RXSEQ = Receive Sequence Error + * o LSC = Link Status Change + */ +#define IMS_ENABLE_MASK ( \ + E1000_IMS_RXT0 | \ + E1000_IMS_TXDW | \ + E1000_IMS_RXDMT0 | \ + E1000_IMS_RXSEQ | \ + E1000_IMS_LSC) + +/* Interrupt Mask Set */ +#define E1000_IMS_TXDW E1000_ICR_TXDW /* Tx desc written back */ +#define E1000_IMS_TXQE E1000_ICR_TXQE /* Transmit Queue empty */ +#define E1000_IMS_LSC E1000_ICR_LSC /* Link Status Change */ +#define E1000_IMS_VMMB E1000_ICR_VMMB /* Mail box activity */ +#define E1000_IMS_RXSEQ E1000_ICR_RXSEQ /* Rx sequence error */ +#define E1000_IMS_RXDMT0 E1000_ICR_RXDMT0 /* Rx desc min. threshold */ +#define E1000_IMS_RXO E1000_ICR_RXO /* Rx overrun */ +#define E1000_IMS_RXT0 E1000_ICR_RXT0 /* Rx timer intr */ +#define E1000_IMS_TXD_LOW E1000_ICR_TXD_LOW +#define E1000_IMS_ECCER E1000_ICR_ECCER /* Uncorrectable ECC Error */ +#define E1000_IMS_TS E1000_ICR_TS /* Time Sync Interrupt */ +#define E1000_IMS_DRSTA E1000_ICR_DRSTA /* Device Reset Asserted */ +#define E1000_IMS_DOUTSYNC E1000_ICR_DOUTSYNC /* NIC DMA out of sync */ +#define E1000_IMS_FER E1000_ICR_FER /* Fatal Error */ + +#define E1000_IMS_THS E1000_ICR_THS /* ICR.TS: Thermal Sensor Event*/ +#define E1000_IMS_MDDET E1000_ICR_MDDET /* Malicious Driver Detect */ +/* Extended Interrupt Mask Set */ +#define E1000_EIMS_RX_QUEUE0 E1000_EICR_RX_QUEUE0 /* Rx Queue 0 Interrupt */ +#define E1000_EIMS_RX_QUEUE1 E1000_EICR_RX_QUEUE1 /* Rx Queue 1 Interrupt */ +#define E1000_EIMS_RX_QUEUE2 E1000_EICR_RX_QUEUE2 /* Rx Queue 2 Interrupt */ +#define E1000_EIMS_RX_QUEUE3 E1000_EICR_RX_QUEUE3 /* Rx Queue 3 Interrupt */ +#define E1000_EIMS_TX_QUEUE0 E1000_EICR_TX_QUEUE0 /* Tx Queue 0 Interrupt */ +#define E1000_EIMS_TX_QUEUE1 E1000_EICR_TX_QUEUE1 /* Tx Queue 1 Interrupt */ +#define E1000_EIMS_TX_QUEUE2 E1000_EICR_TX_QUEUE2 /* Tx Queue 2 Interrupt */ +#define E1000_EIMS_TX_QUEUE3 E1000_EICR_TX_QUEUE3 /* Tx Queue 3 Interrupt */ +#define E1000_EIMS_TCP_TIMER E1000_EICR_TCP_TIMER /* TCP Timer */ +#define E1000_EIMS_OTHER E1000_EICR_OTHER /* Interrupt Cause Active */ + +/* Interrupt Cause Set */ +#define E1000_ICS_LSC E1000_ICR_LSC /* Link Status Change */ +#define E1000_ICS_RXSEQ E1000_ICR_RXSEQ /* Rx sequence error */ +#define E1000_ICS_RXDMT0 E1000_ICR_RXDMT0 /* Rx desc min. threshold */ + +/* Extended Interrupt Cause Set */ +#define E1000_EICS_RX_QUEUE0 E1000_EICR_RX_QUEUE0 /* Rx Queue 0 Interrupt */ +#define E1000_EICS_RX_QUEUE1 E1000_EICR_RX_QUEUE1 /* Rx Queue 1 Interrupt */ +#define E1000_EICS_RX_QUEUE2 E1000_EICR_RX_QUEUE2 /* Rx Queue 2 Interrupt */ +#define E1000_EICS_RX_QUEUE3 E1000_EICR_RX_QUEUE3 /* Rx Queue 3 Interrupt */ +#define E1000_EICS_TX_QUEUE0 E1000_EICR_TX_QUEUE0 /* Tx Queue 0 Interrupt */ +#define E1000_EICS_TX_QUEUE1 E1000_EICR_TX_QUEUE1 /* Tx Queue 1 Interrupt */ +#define E1000_EICS_TX_QUEUE2 E1000_EICR_TX_QUEUE2 /* Tx Queue 2 Interrupt */ +#define E1000_EICS_TX_QUEUE3 E1000_EICR_TX_QUEUE3 /* Tx Queue 3 Interrupt */ +#define E1000_EICS_TCP_TIMER E1000_EICR_TCP_TIMER /* TCP Timer */ +#define E1000_EICS_OTHER E1000_EICR_OTHER /* Interrupt Cause Active */ + +#define E1000_EITR_ITR_INT_MASK 0x0000FFFF +/* E1000_EITR_CNT_IGNR is only for 82576 and newer */ +#define E1000_EITR_CNT_IGNR 0x80000000 /* Don't reset counters on write */ +#define E1000_EITR_INTERVAL 0x00007FFC + +/* Transmit Descriptor Control */ +#define E1000_TXDCTL_PTHRESH 0x0000003F /* TXDCTL Prefetch Threshold */ +#define E1000_TXDCTL_HTHRESH 0x00003F00 /* TXDCTL Host Threshold */ +#define E1000_TXDCTL_WTHRESH 0x003F0000 /* TXDCTL Writeback Threshold */ +#define E1000_TXDCTL_GRAN 0x01000000 /* TXDCTL Granularity */ +#define E1000_TXDCTL_FULL_TX_DESC_WB 0x01010000 /* GRAN=1, WTHRESH=1 */ +#define E1000_TXDCTL_MAX_TX_DESC_PREFETCH 0x0100001F /* GRAN=1, PTHRESH=31 */ +/* Enable the counting of descriptors still to be processed. */ +#define E1000_TXDCTL_COUNT_DESC 0x00400000 + +/* Flow Control Constants */ +#define FLOW_CONTROL_ADDRESS_LOW 0x00C28001 +#define FLOW_CONTROL_ADDRESS_HIGH 0x00000100 +#define FLOW_CONTROL_TYPE 0x8808 + +/* 802.1q VLAN Packet Size */ +#define VLAN_TAG_SIZE 4 /* 802.3ac tag (not DMA'd) */ +#define E1000_VLAN_FILTER_TBL_SIZE 128 /* VLAN Filter Table (4096 bits) */ + +/* Receive Address + * Number of high/low register pairs in the RAR. The RAR (Receive Address + * Registers) holds the directed and multicast addresses that we monitor. + * Technically, we have 16 spots. However, we reserve one of these spots + * (RAR[15]) for our directed address used by controllers with + * manageability enabled, allowing us room for 15 multicast addresses. + */ +#define E1000_RAR_ENTRIES 15 +#define E1000_RAH_AV 0x80000000 /* Receive descriptor valid */ +#define E1000_RAL_MAC_ADDR_LEN 4 +#define E1000_RAH_MAC_ADDR_LEN 2 +#define E1000_RAH_QUEUE_MASK_82575 0x000C0000 +#define E1000_RAH_POOL_1 0x00040000 + +/* Error Codes */ +#define E1000_SUCCESS 0 +#define E1000_ERR_NVM 1 +#define E1000_ERR_PHY 2 +#define E1000_ERR_CONFIG 3 +#define E1000_ERR_PARAM 4 +#define E1000_ERR_MAC_INIT 5 +#define E1000_ERR_PHY_TYPE 6 +#define E1000_ERR_RESET 9 +#define E1000_ERR_MASTER_REQUESTS_PENDING 10 +#define E1000_ERR_HOST_INTERFACE_COMMAND 11 +#define E1000_BLK_PHY_RESET 12 +#define E1000_ERR_SWFW_SYNC 13 +#define E1000_NOT_IMPLEMENTED 14 +#define E1000_ERR_MBX 15 +#define E1000_ERR_INVALID_ARGUMENT 16 +#define E1000_ERR_NO_SPACE 17 +#define E1000_ERR_NVM_PBA_SECTION 18 +#define E1000_ERR_I2C 19 +#define E1000_ERR_INVM_VALUE_NOT_FOUND 20 + +/* Loop limit on how long we wait for auto-negotiation to complete */ +#define FIBER_LINK_UP_LIMIT 50 +#define COPPER_LINK_UP_LIMIT 10 +#define PHY_AUTO_NEG_LIMIT 45 +#define PHY_FORCE_LIMIT 20 +/* Number of 100 microseconds we wait for PCI Express master disable */ +#define MASTER_DISABLE_TIMEOUT 800 +/* Number of milliseconds we wait for PHY configuration done after MAC reset */ +#define PHY_CFG_TIMEOUT 100 +/* Number of 2 milliseconds we wait for acquiring MDIO ownership. */ +#define MDIO_OWNERSHIP_TIMEOUT 10 +/* Number of milliseconds for NVM auto read done after MAC reset. */ +#define AUTO_READ_DONE_TIMEOUT 10 + +/* Flow Control */ +#define E1000_FCRTH_RTH 0x0000FFF8 /* Mask Bits[15:3] for RTH */ +#define E1000_FCRTL_RTL 0x0000FFF8 /* Mask Bits[15:3] for RTL */ +#define E1000_FCRTL_XONE 0x80000000 /* Enable XON frame transmission */ + +/* Transmit Configuration Word */ +#define E1000_TXCW_FD 0x00000020 /* TXCW full duplex */ +#define E1000_TXCW_PAUSE 0x00000080 /* TXCW sym pause request */ +#define E1000_TXCW_ASM_DIR 0x00000100 /* TXCW astm pause direction */ +#define E1000_TXCW_PAUSE_MASK 0x00000180 /* TXCW pause request mask */ +#define E1000_TXCW_ANE 0x80000000 /* Auto-neg enable */ + +/* Receive Configuration Word */ +#define E1000_RXCW_CW 0x0000ffff /* RxConfigWord mask */ +#define E1000_RXCW_IV 0x08000000 /* Receive config invalid */ +#define E1000_RXCW_C 0x20000000 /* Receive config */ +#define E1000_RXCW_SYNCH 0x40000000 /* Receive config synch */ + +#define E1000_TSYNCTXCTL_VALID 0x00000001 /* Tx timestamp valid */ +#define E1000_TSYNCTXCTL_ENABLED 0x00000010 /* enable Tx timestamping */ + +#define E1000_TSYNCRXCTL_VALID 0x00000001 /* Rx timestamp valid */ +#define E1000_TSYNCRXCTL_TYPE_MASK 0x0000000E /* Rx type mask */ +#define E1000_TSYNCRXCTL_TYPE_L2_V2 0x00 +#define E1000_TSYNCRXCTL_TYPE_L4_V1 0x02 +#define E1000_TSYNCRXCTL_TYPE_L2_L4_V2 0x04 +#define E1000_TSYNCRXCTL_TYPE_ALL 0x08 +#define E1000_TSYNCRXCTL_TYPE_EVENT_V2 0x0A +#define E1000_TSYNCRXCTL_ENABLED 0x00000010 /* enable Rx timestamping */ +#define E1000_TSYNCRXCTL_SYSCFI 0x00000020 /* Sys clock frequency */ + +#define E1000_TSYNCRXCFG_PTP_V1_CTRLT_MASK 0x000000FF +#define E1000_TSYNCRXCFG_PTP_V1_SYNC_MESSAGE 0x00 +#define E1000_TSYNCRXCFG_PTP_V1_DELAY_REQ_MESSAGE 0x01 +#define E1000_TSYNCRXCFG_PTP_V1_FOLLOWUP_MESSAGE 0x02 +#define E1000_TSYNCRXCFG_PTP_V1_DELAY_RESP_MESSAGE 0x03 +#define E1000_TSYNCRXCFG_PTP_V1_MANAGEMENT_MESSAGE 0x04 + +#define E1000_TSYNCRXCFG_PTP_V2_MSGID_MASK 0x00000F00 +#define E1000_TSYNCRXCFG_PTP_V2_SYNC_MESSAGE 0x0000 +#define E1000_TSYNCRXCFG_PTP_V2_DELAY_REQ_MESSAGE 0x0100 +#define E1000_TSYNCRXCFG_PTP_V2_PATH_DELAY_REQ_MESSAGE 0x0200 +#define E1000_TSYNCRXCFG_PTP_V2_PATH_DELAY_RESP_MESSAGE 0x0300 +#define E1000_TSYNCRXCFG_PTP_V2_FOLLOWUP_MESSAGE 0x0800 +#define E1000_TSYNCRXCFG_PTP_V2_DELAY_RESP_MESSAGE 0x0900 +#define E1000_TSYNCRXCFG_PTP_V2_PATH_DELAY_FOLLOWUP_MESSAGE 0x0A00 +#define E1000_TSYNCRXCFG_PTP_V2_ANNOUNCE_MESSAGE 0x0B00 +#define E1000_TSYNCRXCFG_PTP_V2_SIGNALLING_MESSAGE 0x0C00 +#define E1000_TSYNCRXCFG_PTP_V2_MANAGEMENT_MESSAGE 0x0D00 + +#define E1000_TIMINCA_16NS_SHIFT 24 +#define E1000_TIMINCA_INCPERIOD_SHIFT 24 +#define E1000_TIMINCA_INCVALUE_MASK 0x00FFFFFF + +/* Time Sync Interrupt Cause/Mask Register Bits */ + +#define TSINTR_SYS_WRAP (1 << 0) /* SYSTIM Wrap around. */ +#define TSINTR_TXTS (1 << 1) /* Transmit Timestamp. */ +#define TSINTR_RXTS (1 << 2) /* Receive Timestamp. */ +#define TSINTR_TT0 (1 << 3) /* Target Time 0 Trigger. */ +#define TSINTR_TT1 (1 << 4) /* Target Time 1 Trigger. */ +#define TSINTR_AUTT0 (1 << 5) /* Auxiliary Timestamp 0 Taken. */ +#define TSINTR_AUTT1 (1 << 6) /* Auxiliary Timestamp 1 Taken. */ +#define TSINTR_TADJ (1 << 7) /* Time Adjust Done. */ + +#define TSYNC_INTERRUPTS TSINTR_TXTS +#define E1000_TSICR_TXTS TSINTR_TXTS + +/* TSAUXC Configuration Bits */ +#define TSAUXC_EN_TT0 (1 << 0) /* Enable target time 0. */ +#define TSAUXC_EN_TT1 (1 << 1) /* Enable target time 1. */ +#define TSAUXC_EN_CLK0 (1 << 2) /* Enable Configurable Frequency Clock 0. */ +#define TSAUXC_SAMP_AUT0 (1 << 3) /* Latch SYSTIML/H into AUXSTMPL/0. */ +#define TSAUXC_ST0 (1 << 4) /* Start Clock 0 Toggle on Target Time 0. */ +#define TSAUXC_EN_CLK1 (1 << 5) /* Enable Configurable Frequency Clock 1. */ +#define TSAUXC_SAMP_AUT1 (1 << 6) /* Latch SYSTIML/H into AUXSTMPL/1. */ +#define TSAUXC_ST1 (1 << 7) /* Start Clock 1 Toggle on Target Time 1. */ +#define TSAUXC_EN_TS0 (1 << 8) /* Enable hardware timestamp 0. */ +#define TSAUXC_AUTT0 (1 << 9) /* Auxiliary Timestamp Taken. */ +#define TSAUXC_EN_TS1 (1 << 10) /* Enable hardware timestamp 0. */ +#define TSAUXC_AUTT1 (1 << 11) /* Auxiliary Timestamp Taken. */ +#define TSAUXC_PLSG (1 << 17) /* Generate a pulse. */ +#define TSAUXC_DISABLE (1 << 31) /* Disable SYSTIM Count Operation. */ + +/* SDP Configuration Bits */ +#define AUX0_SEL_SDP0 (0 << 0) /* Assign SDP0 to auxiliary time stamp 0. */ +#define AUX0_SEL_SDP1 (1 << 0) /* Assign SDP1 to auxiliary time stamp 0. */ +#define AUX0_SEL_SDP2 (2 << 0) /* Assign SDP2 to auxiliary time stamp 0. */ +#define AUX0_SEL_SDP3 (3 << 0) /* Assign SDP3 to auxiliary time stamp 0. */ +#define AUX0_TS_SDP_EN (1 << 2) /* Enable auxiliary time stamp trigger 0. */ +#define AUX1_SEL_SDP0 (0 << 3) /* Assign SDP0 to auxiliary time stamp 1. */ +#define AUX1_SEL_SDP1 (1 << 3) /* Assign SDP1 to auxiliary time stamp 1. */ +#define AUX1_SEL_SDP2 (2 << 3) /* Assign SDP2 to auxiliary time stamp 1. */ +#define AUX1_SEL_SDP3 (3 << 3) /* Assign SDP3 to auxiliary time stamp 1. */ +#define AUX1_TS_SDP_EN (1 << 5) /* Enable auxiliary time stamp trigger 1. */ +#define TS_SDP0_SEL_TT0 (0 << 6) /* Target time 0 is output on SDP0. */ +#define TS_SDP0_SEL_TT1 (1 << 6) /* Target time 1 is output on SDP0. */ +#define TS_SDP0_SEL_FC0 (2 << 6) /* Freq clock 0 is output on SDP0. */ +#define TS_SDP0_SEL_FC1 (3 << 6) /* Freq clock 1 is output on SDP0. */ +#define TS_SDP0_EN (1 << 8) /* SDP0 is assigned to Tsync. */ +#define TS_SDP1_SEL_TT0 (0 << 9) /* Target time 0 is output on SDP1. */ +#define TS_SDP1_SEL_TT1 (1 << 9) /* Target time 1 is output on SDP1. */ +#define TS_SDP1_SEL_FC0 (2 << 9) /* Freq clock 0 is output on SDP1. */ +#define TS_SDP1_SEL_FC1 (3 << 9) /* Freq clock 1 is output on SDP1. */ +#define TS_SDP1_EN (1 << 11) /* SDP1 is assigned to Tsync. */ +#define TS_SDP2_SEL_TT0 (0 << 12) /* Target time 0 is output on SDP2. */ +#define TS_SDP2_SEL_TT1 (1 << 12) /* Target time 1 is output on SDP2. */ +#define TS_SDP2_SEL_FC0 (2 << 12) /* Freq clock 0 is output on SDP2. */ +#define TS_SDP2_SEL_FC1 (3 << 12) /* Freq clock 1 is output on SDP2. */ +#define TS_SDP2_EN (1 << 14) /* SDP2 is assigned to Tsync. */ +#define TS_SDP3_SEL_TT0 (0 << 15) /* Target time 0 is output on SDP3. */ +#define TS_SDP3_SEL_TT1 (1 << 15) /* Target time 1 is output on SDP3. */ +#define TS_SDP3_SEL_FC0 (2 << 15) /* Freq clock 0 is output on SDP3. */ +#define TS_SDP3_SEL_FC1 (3 << 15) /* Freq clock 1 is output on SDP3. */ +#define TS_SDP3_EN (1 << 17) /* SDP3 is assigned to Tsync. */ +/* TUPLE Filtering Configuration */ +#define E1000_TTQF_DISABLE_MASK 0xF0008000 /* TTQF Disable Mask */ +#define E1000_TTQF_QUEUE_ENABLE 0x100 /* TTQF Queue Enable Bit */ +#define E1000_TTQF_PROTOCOL_MASK 0xFF /* TTQF Protocol Mask */ +/* TTQF TCP Bit, shift with E1000_TTQF_PROTOCOL SHIFT */ +#define E1000_TTQF_PROTOCOL_TCP 0x0 +/* TTQF UDP Bit, shift with E1000_TTQF_PROTOCOL_SHIFT */ +#define E1000_TTQF_PROTOCOL_UDP 0x1 +/* TTQF SCTP Bit, shift with E1000_TTQF_PROTOCOL_SHIFT */ +#define E1000_TTQF_PROTOCOL_SCTP 0x2 +#define E1000_TTQF_PROTOCOL_SHIFT 5 /* TTQF Protocol Shift */ +#define E1000_TTQF_QUEUE_SHIFT 16 /* TTQF Queue Shfit */ +#define E1000_TTQF_RX_QUEUE_MASK 0x70000 /* TTQF Queue Mask */ +#define E1000_TTQF_MASK_ENABLE 0x10000000 /* TTQF Mask Enable Bit */ +#define E1000_IMIR_CLEAR_MASK 0xF001FFFF /* IMIR Reg Clear Mask */ +#define E1000_IMIR_PORT_BYPASS 0x20000 /* IMIR Port Bypass Bit */ +#define E1000_IMIR_PRIORITY_SHIFT 29 /* IMIR Priority Shift */ +#define E1000_IMIREXT_CLEAR_MASK 0x7FFFF /* IMIREXT Reg Clear Mask */ + +#define E1000_MDICNFG_EXT_MDIO 0x80000000 /* MDI ext/int destination */ +#define E1000_MDICNFG_COM_MDIO 0x40000000 /* MDI shared w/ lan 0 */ +#define E1000_MDICNFG_PHY_MASK 0x03E00000 +#define E1000_MDICNFG_PHY_SHIFT 21 + +#define E1000_MEDIA_PORT_COPPER 1 +#define E1000_MEDIA_PORT_OTHER 2 +#define E1000_M88E1112_AUTO_COPPER_SGMII 0x2 +#define E1000_M88E1112_AUTO_COPPER_BASEX 0x3 +#define E1000_M88E1112_STATUS_LINK 0x0004 /* Interface Link Bit */ +#define E1000_M88E1112_MAC_CTRL_1 0x10 +#define E1000_M88E1112_MAC_CTRL_1_MODE_MASK 0x0380 /* Mode Select */ +#define E1000_M88E1112_MAC_CTRL_1_MODE_SHIFT 7 +#define E1000_M88E1112_PAGE_ADDR 0x16 +#define E1000_M88E1112_STATUS 0x01 + +#define E1000_THSTAT_LOW_EVENT 0x20000000 /* Low thermal threshold */ +#define E1000_THSTAT_MID_EVENT 0x00200000 /* Mid thermal threshold */ +#define E1000_THSTAT_HIGH_EVENT 0x00002000 /* High thermal threshold */ +#define E1000_THSTAT_PWR_DOWN 0x00000001 /* Power Down Event */ +#define E1000_THSTAT_LINK_THROTTLE 0x00000002 /* Link Spd Throttle Event */ + +/* I350 EEE defines */ +#define E1000_IPCNFG_EEE_1G_AN 0x00000008 /* IPCNFG EEE Ena 1G AN */ +#define E1000_IPCNFG_EEE_100M_AN 0x00000004 /* IPCNFG EEE Ena 100M AN */ +#define E1000_EEER_TX_LPI_EN 0x00010000 /* EEER Tx LPI Enable */ +#define E1000_EEER_RX_LPI_EN 0x00020000 /* EEER Rx LPI Enable */ +#define E1000_EEER_LPI_FC 0x00040000 /* EEER Ena on Flow Cntrl */ +/* EEE status */ +#define E1000_EEER_EEE_NEG 0x20000000 /* EEE capability nego */ +#define E1000_EEER_RX_LPI_STATUS 0x40000000 /* Rx in LPI state */ +#define E1000_EEER_TX_LPI_STATUS 0x80000000 /* Tx in LPI state */ +#define E1000_EEE_LP_ADV_ADDR_I350 0x040F /* EEE LP Advertisement */ +#define E1000_M88E1543_PAGE_ADDR 0x16 /* Page Offset Register */ +#define E1000_M88E1543_EEE_CTRL_1 0x0 +#define E1000_M88E1543_EEE_CTRL_1_MS 0x0001 /* EEE Master/Slave */ +#define E1000_EEE_ADV_DEV_I354 7 +#define E1000_EEE_ADV_ADDR_I354 60 +#define E1000_EEE_ADV_100_SUPPORTED (1 << 1) /* 100BaseTx EEE Supported */ +#define E1000_EEE_ADV_1000_SUPPORTED (1 << 2) /* 1000BaseT EEE Supported */ +#define E1000_PCS_STATUS_DEV_I354 3 +#define E1000_PCS_STATUS_ADDR_I354 1 +#define E1000_PCS_STATUS_RX_LPI_RCVD 0x0400 +#define E1000_PCS_STATUS_TX_LPI_RCVD 0x0800 +#define E1000_M88E1512_CFG_REG_1 0x0010 +#define E1000_M88E1512_CFG_REG_2 0x0011 +#define E1000_M88E1512_CFG_REG_3 0x0007 +#define E1000_M88E1512_MODE 0x0014 +#define E1000_EEE_SU_LPI_CLK_STP 0x00800000 /* EEE LPI Clock Stop */ +#define E1000_EEE_LP_ADV_DEV_I210 7 /* EEE LP Adv Device */ +#define E1000_EEE_LP_ADV_ADDR_I210 61 /* EEE LP Adv Register */ +/* PCI Express Control */ +#define E1000_GCR_RXD_NO_SNOOP 0x00000001 +#define E1000_GCR_RXDSCW_NO_SNOOP 0x00000002 +#define E1000_GCR_RXDSCR_NO_SNOOP 0x00000004 +#define E1000_GCR_TXD_NO_SNOOP 0x00000008 +#define E1000_GCR_TXDSCW_NO_SNOOP 0x00000010 +#define E1000_GCR_TXDSCR_NO_SNOOP 0x00000020 +#define E1000_GCR_CMPL_TMOUT_MASK 0x0000F000 +#define E1000_GCR_CMPL_TMOUT_10ms 0x00001000 +#define E1000_GCR_CMPL_TMOUT_RESEND 0x00010000 +#define E1000_GCR_CAP_VER2 0x00040000 + +#define PCIE_NO_SNOOP_ALL (E1000_GCR_RXD_NO_SNOOP | \ + E1000_GCR_RXDSCW_NO_SNOOP | \ + E1000_GCR_RXDSCR_NO_SNOOP | \ + E1000_GCR_TXD_NO_SNOOP | \ + E1000_GCR_TXDSCW_NO_SNOOP | \ + E1000_GCR_TXDSCR_NO_SNOOP) + +#define E1000_MMDAC_FUNC_DATA 0x4000 /* Data, no post increment */ + +/* mPHY address control and data registers */ +#define E1000_MPHY_ADDR_CTL 0x0024 /* Address Control Reg */ +#define E1000_MPHY_ADDR_CTL_OFFSET_MASK 0xFFFF0000 +#define E1000_MPHY_DATA 0x0E10 /* Data Register */ + +/* AFE CSR Offset for PCS CLK */ +#define E1000_MPHY_PCS_CLK_REG_OFFSET 0x0004 +/* Override for near end digital loopback. */ +#define E1000_MPHY_PCS_CLK_REG_DIGINELBEN 0x10 + +/* PHY Control Register */ +#define MII_CR_SPEED_SELECT_MSB 0x0040 /* bits 6,13: 10=1000, 01=100, 00=10 */ +#define MII_CR_COLL_TEST_ENABLE 0x0080 /* Collision test enable */ +#define MII_CR_FULL_DUPLEX 0x0100 /* FDX =1, half duplex =0 */ +#define MII_CR_RESTART_AUTO_NEG 0x0200 /* Restart auto negotiation */ +#define MII_CR_ISOLATE 0x0400 /* Isolate PHY from MII */ +#define MII_CR_POWER_DOWN 0x0800 /* Power down */ +#define MII_CR_AUTO_NEG_EN 0x1000 /* Auto Neg Enable */ +#define MII_CR_SPEED_SELECT_LSB 0x2000 /* bits 6,13: 10=1000, 01=100, 00=10 */ +#define MII_CR_LOOPBACK 0x4000 /* 0 = normal, 1 = loopback */ +#define MII_CR_RESET 0x8000 /* 0 = normal, 1 = PHY reset */ +#define MII_CR_SPEED_1000 0x0040 +#define MII_CR_SPEED_100 0x2000 +#define MII_CR_SPEED_10 0x0000 + +/* PHY Status Register */ +#define MII_SR_EXTENDED_CAPS 0x0001 /* Extended register capabilities */ +#define MII_SR_JABBER_DETECT 0x0002 /* Jabber Detected */ +#define MII_SR_LINK_STATUS 0x0004 /* Link Status 1 = link */ +#define MII_SR_AUTONEG_CAPS 0x0008 /* Auto Neg Capable */ +#define MII_SR_REMOTE_FAULT 0x0010 /* Remote Fault Detect */ +#define MII_SR_AUTONEG_COMPLETE 0x0020 /* Auto Neg Complete */ +#define MII_SR_PREAMBLE_SUPPRESS 0x0040 /* Preamble may be suppressed */ +#define MII_SR_EXTENDED_STATUS 0x0100 /* Ext. status info in Reg 0x0F */ +#define MII_SR_100T2_HD_CAPS 0x0200 /* 100T2 Half Duplex Capable */ +#define MII_SR_100T2_FD_CAPS 0x0400 /* 100T2 Full Duplex Capable */ +#define MII_SR_10T_HD_CAPS 0x0800 /* 10T Half Duplex Capable */ +#define MII_SR_10T_FD_CAPS 0x1000 /* 10T Full Duplex Capable */ +#define MII_SR_100X_HD_CAPS 0x2000 /* 100X Half Duplex Capable */ +#define MII_SR_100X_FD_CAPS 0x4000 /* 100X Full Duplex Capable */ +#define MII_SR_100T4_CAPS 0x8000 /* 100T4 Capable */ + +/* Autoneg Advertisement Register */ +#define NWAY_AR_SELECTOR_FIELD 0x0001 /* indicates IEEE 802.3 CSMA/CD */ +#define NWAY_AR_10T_HD_CAPS 0x0020 /* 10T Half Duplex Capable */ +#define NWAY_AR_10T_FD_CAPS 0x0040 /* 10T Full Duplex Capable */ +#define NWAY_AR_100TX_HD_CAPS 0x0080 /* 100TX Half Duplex Capable */ +#define NWAY_AR_100TX_FD_CAPS 0x0100 /* 100TX Full Duplex Capable */ +#define NWAY_AR_100T4_CAPS 0x0200 /* 100T4 Capable */ +#define NWAY_AR_PAUSE 0x0400 /* Pause operation desired */ +#define NWAY_AR_ASM_DIR 0x0800 /* Asymmetric Pause Direction bit */ +#define NWAY_AR_REMOTE_FAULT 0x2000 /* Remote Fault detected */ +#define NWAY_AR_NEXT_PAGE 0x8000 /* Next Page ability supported */ + +/* Link Partner Ability Register (Base Page) */ +#define NWAY_LPAR_SELECTOR_FIELD 0x0000 /* LP protocol selector field */ +#define NWAY_LPAR_10T_HD_CAPS 0x0020 /* LP 10T Half Dplx Capable */ +#define NWAY_LPAR_10T_FD_CAPS 0x0040 /* LP 10T Full Dplx Capable */ +#define NWAY_LPAR_100TX_HD_CAPS 0x0080 /* LP 100TX Half Dplx Capable */ +#define NWAY_LPAR_100TX_FD_CAPS 0x0100 /* LP 100TX Full Dplx Capable */ +#define NWAY_LPAR_100T4_CAPS 0x0200 /* LP is 100T4 Capable */ +#define NWAY_LPAR_PAUSE 0x0400 /* LP Pause operation desired */ +#define NWAY_LPAR_ASM_DIR 0x0800 /* LP Asym Pause Direction bit */ +#define NWAY_LPAR_REMOTE_FAULT 0x2000 /* LP detected Remote Fault */ +#define NWAY_LPAR_ACKNOWLEDGE 0x4000 /* LP rx'd link code word */ +#define NWAY_LPAR_NEXT_PAGE 0x8000 /* Next Page ability supported */ + +/* Autoneg Expansion Register */ +#define NWAY_ER_LP_NWAY_CAPS 0x0001 /* LP has Auto Neg Capability */ +#define NWAY_ER_PAGE_RXD 0x0002 /* LP 10T Half Dplx Capable */ +#define NWAY_ER_NEXT_PAGE_CAPS 0x0004 /* LP 10T Full Dplx Capable */ +#define NWAY_ER_LP_NEXT_PAGE_CAPS 0x0008 /* LP 100TX Half Dplx Capable */ +#define NWAY_ER_PAR_DETECT_FAULT 0x0010 /* LP 100TX Full Dplx Capable */ + +/* 1000BASE-T Control Register */ +#define CR_1000T_ASYM_PAUSE 0x0080 /* Advertise asymmetric pause bit */ +#define CR_1000T_HD_CAPS 0x0100 /* Advertise 1000T HD capability */ +#define CR_1000T_FD_CAPS 0x0200 /* Advertise 1000T FD capability */ +/* 1=Repeater/switch device port 0=DTE device */ +#define CR_1000T_REPEATER_DTE 0x0400 +/* 1=Configure PHY as Master 0=Configure PHY as Slave */ +#define CR_1000T_MS_VALUE 0x0800 +/* 1=Master/Slave manual config value 0=Automatic Master/Slave config */ +#define CR_1000T_MS_ENABLE 0x1000 +#define CR_1000T_TEST_MODE_NORMAL 0x0000 /* Normal Operation */ +#define CR_1000T_TEST_MODE_1 0x2000 /* Transmit Waveform test */ +#define CR_1000T_TEST_MODE_2 0x4000 /* Master Transmit Jitter test */ +#define CR_1000T_TEST_MODE_3 0x6000 /* Slave Transmit Jitter test */ +#define CR_1000T_TEST_MODE_4 0x8000 /* Transmitter Distortion test */ + +/* 1000BASE-T Status Register */ +#define SR_1000T_IDLE_ERROR_CNT 0x00FF /* Num idle err since last rd */ +#define SR_1000T_ASYM_PAUSE_DIR 0x0100 /* LP asym pause direction bit */ +#define SR_1000T_LP_HD_CAPS 0x0400 /* LP is 1000T HD capable */ +#define SR_1000T_LP_FD_CAPS 0x0800 /* LP is 1000T FD capable */ +#define SR_1000T_REMOTE_RX_STATUS 0x1000 /* Remote receiver OK */ +#define SR_1000T_LOCAL_RX_STATUS 0x2000 /* Local receiver OK */ +#define SR_1000T_MS_CONFIG_RES 0x4000 /* 1=Local Tx Master, 0=Slave */ +#define SR_1000T_MS_CONFIG_FAULT 0x8000 /* Master/Slave config fault */ + +#define SR_1000T_PHY_EXCESSIVE_IDLE_ERR_COUNT 5 + +/* PHY 1000 MII Register/Bit Definitions */ +/* PHY Registers defined by IEEE */ +#define PHY_CONTROL 0x00 /* Control Register */ +#define PHY_STATUS 0x01 /* Status Register */ +#define PHY_ID1 0x02 /* Phy Id Reg (word 1) */ +#define PHY_ID2 0x03 /* Phy Id Reg (word 2) */ +#define PHY_AUTONEG_ADV 0x04 /* Autoneg Advertisement */ +#define PHY_LP_ABILITY 0x05 /* Link Partner Ability (Base Page) */ +#define PHY_AUTONEG_EXP 0x06 /* Autoneg Expansion Reg */ +#define PHY_NEXT_PAGE_TX 0x07 /* Next Page Tx */ +#define PHY_LP_NEXT_PAGE 0x08 /* Link Partner Next Page */ +#define PHY_1000T_CTRL 0x09 /* 1000Base-T Control Reg */ +#define PHY_1000T_STATUS 0x0A /* 1000Base-T Status Reg */ +#define PHY_EXT_STATUS 0x0F /* Extended Status Reg */ + +#define PHY_CONTROL_LB 0x4000 /* PHY Loopback bit */ + +/* NVM Control */ +#define E1000_EECD_SK 0x00000001 /* NVM Clock */ +#define E1000_EECD_CS 0x00000002 /* NVM Chip Select */ +#define E1000_EECD_DI 0x00000004 /* NVM Data In */ +#define E1000_EECD_DO 0x00000008 /* NVM Data Out */ +#define E1000_EECD_REQ 0x00000040 /* NVM Access Request */ +#define E1000_EECD_GNT 0x00000080 /* NVM Access Grant */ +#define E1000_EECD_PRES 0x00000100 /* NVM Present */ +#define E1000_EECD_SIZE 0x00000200 /* NVM Size (0=64 word 1=256 word) */ +#define E1000_EECD_BLOCKED 0x00008000 /* Bit banging access blocked flag */ +#define E1000_EECD_ABORT 0x00010000 /* NVM operation aborted flag */ +#define E1000_EECD_TIMEOUT 0x00020000 /* NVM read operation timeout flag */ +#define E1000_EECD_ERROR_CLR 0x00040000 /* NVM error status clear bit */ +/* NVM Addressing bits based on type 0=small, 1=large */ +#define E1000_EECD_ADDR_BITS 0x00000400 +#define E1000_NVM_GRANT_ATTEMPTS 1000 /* NVM # attempts to gain grant */ +#define E1000_EECD_AUTO_RD 0x00000200 /* NVM Auto Read done */ +#define E1000_EECD_SIZE_EX_MASK 0x00007800 /* NVM Size */ +#define E1000_EECD_SIZE_EX_SHIFT 11 +#define E1000_EECD_FLUPD 0x00080000 /* Update FLASH */ +#define E1000_EECD_AUPDEN 0x00100000 /* Ena Auto FLASH update */ +#define E1000_EECD_SEC1VAL 0x00400000 /* Sector One Valid */ +#define E1000_EECD_SEC1VAL_VALID_MASK (E1000_EECD_AUTO_RD | E1000_EECD_PRES) +#define E1000_EECD_FLUPD_I210 0x00800000 /* Update FLASH */ +#define E1000_EECD_FLUDONE_I210 0x04000000 /* Update FLASH done */ +#define E1000_EECD_FLASH_DETECTED_I210 0x00080000 /* FLASH detected */ +#define E1000_EECD_SEC1VAL_I210 0x02000000 /* Sector One Valid */ +#define E1000_FLUDONE_ATTEMPTS 20000 +#define E1000_EERD_EEWR_MAX_COUNT 512 /* buffered EEPROM words rw */ +#define E1000_I210_FIFO_SEL_RX 0x00 +#define E1000_I210_FIFO_SEL_TX_QAV(_i) (0x02 + (_i)) +#define E1000_I210_FIFO_SEL_TX_LEGACY E1000_I210_FIFO_SEL_TX_QAV(0) +#define E1000_I210_FIFO_SEL_BMC2OS_TX 0x06 +#define E1000_I210_FIFO_SEL_BMC2OS_RX 0x01 + +#define E1000_I210_FLASH_SECTOR_SIZE 0x1000 /* 4KB FLASH sector unit size */ +/* Secure FLASH mode requires removing MSb */ +#define E1000_I210_FW_PTR_MASK 0x7FFF +/* Firmware code revision field word offset*/ +#define E1000_I210_FW_VER_OFFSET 328 + +#define E1000_NVM_RW_REG_DATA 16 /* Offset to data in NVM read/write regs */ +#define E1000_NVM_RW_REG_DONE 2 /* Offset to READ/WRITE done bit */ +#define E1000_NVM_RW_REG_START 1 /* Start operation */ +#define E1000_NVM_RW_ADDR_SHIFT 2 /* Shift to the address bits */ +#define E1000_NVM_POLL_WRITE 1 /* Flag for polling for write complete */ +#define E1000_NVM_POLL_READ 0 /* Flag for polling for read complete */ +#define E1000_FLASH_UPDATES 2000 + +/* NVM Word Offsets */ +#define NVM_COMPAT 0x0003 +#define NVM_ID_LED_SETTINGS 0x0004 +#define NVM_VERSION 0x0005 +#define E1000_I210_NVM_FW_MODULE_PTR 0x0010 +#define E1000_I350_NVM_FW_MODULE_PTR 0x0051 +#define NVM_FUTURE_INIT_WORD1 0x0019 +#define NVM_ETRACK_WORD 0x0042 +#define NVM_ETRACK_HIWORD 0x0043 +#define NVM_COMB_VER_OFF 0x0083 +#define NVM_COMB_VER_PTR 0x003d + +/* NVM version defines */ +#define NVM_MAJOR_MASK 0xF000 +#define NVM_MINOR_MASK 0x0FF0 +#define NVM_IMAGE_ID_MASK 0x000F +#define NVM_COMB_VER_MASK 0x00FF +#define NVM_MAJOR_SHIFT 12 +#define NVM_MINOR_SHIFT 4 +#define NVM_COMB_VER_SHFT 8 +#define NVM_VER_INVALID 0xFFFF +#define NVM_ETRACK_SHIFT 16 +#define NVM_ETRACK_VALID 0x8000 +#define NVM_NEW_DEC_MASK 0x0F00 +#define NVM_HEX_CONV 16 +#define NVM_HEX_TENS 10 + +/* FW version defines */ +/* Offset of "Loader patch ptr" in Firmware Header */ +#define E1000_I350_NVM_FW_LOADER_PATCH_PTR_OFFSET 0x01 +/* Patch generation hour & minutes */ +#define E1000_I350_NVM_FW_VER_WORD1_OFFSET 0x04 +/* Patch generation month & day */ +#define E1000_I350_NVM_FW_VER_WORD2_OFFSET 0x05 +/* Patch generation year */ +#define E1000_I350_NVM_FW_VER_WORD3_OFFSET 0x06 +/* Patch major & minor numbers */ +#define E1000_I350_NVM_FW_VER_WORD4_OFFSET 0x07 + +#define NVM_MAC_ADDR 0x0000 +#define NVM_SUB_DEV_ID 0x000B +#define NVM_SUB_VEN_ID 0x000C +#define NVM_DEV_ID 0x000D +#define NVM_VEN_ID 0x000E +#define NVM_INIT_CTRL_2 0x000F +#define NVM_INIT_CTRL_4 0x0013 +#define NVM_LED_1_CFG 0x001C +#define NVM_LED_0_2_CFG 0x001F + +#define NVM_COMPAT_VALID_CSUM 0x0001 +#define NVM_FUTURE_INIT_WORD1_VALID_CSUM 0x0040 + +#define NVM_ETS_CFG 0x003E +#define NVM_ETS_LTHRES_DELTA_MASK 0x07C0 +#define NVM_ETS_LTHRES_DELTA_SHIFT 6 +#define NVM_ETS_TYPE_MASK 0x0038 +#define NVM_ETS_TYPE_SHIFT 3 +#define NVM_ETS_TYPE_EMC 0x000 +#define NVM_ETS_NUM_SENSORS_MASK 0x0007 +#define NVM_ETS_DATA_LOC_MASK 0x3C00 +#define NVM_ETS_DATA_LOC_SHIFT 10 +#define NVM_ETS_DATA_INDEX_MASK 0x0300 +#define NVM_ETS_DATA_INDEX_SHIFT 8 +#define NVM_ETS_DATA_HTHRESH_MASK 0x00FF +#define NVM_INIT_CONTROL2_REG 0x000F +#define NVM_INIT_CONTROL3_PORT_B 0x0014 +#define NVM_INIT_3GIO_3 0x001A +#define NVM_SWDEF_PINS_CTRL_PORT_0 0x0020 +#define NVM_INIT_CONTROL3_PORT_A 0x0024 +#define NVM_CFG 0x0012 +#define NVM_ALT_MAC_ADDR_PTR 0x0037 +#define NVM_CHECKSUM_REG 0x003F +#define NVM_COMPATIBILITY_REG_3 0x0003 +#define NVM_COMPATIBILITY_BIT_MASK 0x8000 + +#define E1000_NVM_CFG_DONE_PORT_0 0x040000 /* MNG config cycle done */ +#define E1000_NVM_CFG_DONE_PORT_1 0x080000 /* ...for second port */ +#define E1000_NVM_CFG_DONE_PORT_2 0x100000 /* ...for third port */ +#define E1000_NVM_CFG_DONE_PORT_3 0x200000 /* ...for fourth port */ + +#define NVM_82580_LAN_FUNC_OFFSET(a) ((a) ? (0x40 + (0x40 * (a))) : 0) + +/* Mask bits for fields in Word 0x24 of the NVM */ +#define NVM_WORD24_COM_MDIO 0x0008 /* MDIO interface shared */ +#define NVM_WORD24_EXT_MDIO 0x0004 /* MDIO accesses routed extrnl */ +/* Offset of Link Mode bits for 82575/82576 */ +#define NVM_WORD24_LNK_MODE_OFFSET 8 +/* Offset of Link Mode bits for 82580 up */ +#define NVM_WORD24_82580_LNK_MODE_OFFSET 4 + +/* Mask bits for fields in Word 0x0f of the NVM */ +#define NVM_WORD0F_PAUSE_MASK 0x3000 +#define NVM_WORD0F_PAUSE 0x1000 +#define NVM_WORD0F_ASM_DIR 0x2000 + +/* Mask bits for fields in Word 0x1a of the NVM */ +#define NVM_WORD1A_ASPM_MASK 0x000C + +/* Mask bits for fields in Word 0x03 of the EEPROM */ +#define NVM_COMPAT_LOM 0x0800 + +/* length of string needed to store PBA number */ +#define E1000_PBANUM_LENGTH 11 + +/* For checksumming, the sum of all words in the NVM should equal 0xBABA. */ +#define NVM_SUM 0xBABA + +/* PBA (printed board assembly) number words */ +#define NVM_PBA_OFFSET_0 8 +#define NVM_PBA_OFFSET_1 9 +#define NVM_PBA_PTR_GUARD 0xFAFA +#define NVM_RESERVED_WORD 0xFFFF +#define NVM_WORD_SIZE_BASE_SHIFT 6 + +/* NVM Commands - SPI */ +#define NVM_MAX_RETRY_SPI 5000 /* Max wait of 5ms, for RDY signal */ +#define NVM_READ_OPCODE_SPI 0x03 /* NVM read opcode */ +#define NVM_WRITE_OPCODE_SPI 0x02 /* NVM write opcode */ +#define NVM_A8_OPCODE_SPI 0x08 /* opcode bit-3 = address bit-8 */ +#define NVM_WREN_OPCODE_SPI 0x06 /* NVM set Write Enable latch */ +#define NVM_RDSR_OPCODE_SPI 0x05 /* NVM read Status register */ + +/* SPI NVM Status Register */ +#define NVM_STATUS_RDY_SPI 0x01 + +/* Word definitions for ID LED Settings */ +#define ID_LED_RESERVED_0000 0x0000 +#define ID_LED_RESERVED_FFFF 0xFFFF +#define ID_LED_DEFAULT ((ID_LED_OFF1_ON2 << 12) | \ + (ID_LED_OFF1_OFF2 << 8) | \ + (ID_LED_DEF1_DEF2 << 4) | \ + (ID_LED_DEF1_DEF2)) +#define ID_LED_DEF1_DEF2 0x1 +#define ID_LED_DEF1_ON2 0x2 +#define ID_LED_DEF1_OFF2 0x3 +#define ID_LED_ON1_DEF2 0x4 +#define ID_LED_ON1_ON2 0x5 +#define ID_LED_ON1_OFF2 0x6 +#define ID_LED_OFF1_DEF2 0x7 +#define ID_LED_OFF1_ON2 0x8 +#define ID_LED_OFF1_OFF2 0x9 + +#define IGP_ACTIVITY_LED_MASK 0xFFFFF0FF +#define IGP_ACTIVITY_LED_ENABLE 0x0300 +#define IGP_LED3_MODE 0x07000000 + +/* PCI/PCI-X/PCI-EX Config space */ +#define PCIX_COMMAND_REGISTER 0xE6 +#define PCIX_STATUS_REGISTER_LO 0xE8 +#define PCIX_STATUS_REGISTER_HI 0xEA +#define PCI_HEADER_TYPE_REGISTER 0x0E +#define PCIE_LINK_STATUS 0x12 +#define PCIE_DEVICE_CONTROL2 0x28 + +#define PCIX_COMMAND_MMRBC_MASK 0x000C +#define PCIX_COMMAND_MMRBC_SHIFT 0x2 +#define PCIX_STATUS_HI_MMRBC_MASK 0x0060 +#define PCIX_STATUS_HI_MMRBC_SHIFT 0x5 +#define PCIX_STATUS_HI_MMRBC_4K 0x3 +#define PCIX_STATUS_HI_MMRBC_2K 0x2 +#define PCIX_STATUS_LO_FUNC_MASK 0x7 +#define PCI_HEADER_TYPE_MULTIFUNC 0x80 +#define PCIE_LINK_WIDTH_MASK 0x3F0 +#define PCIE_LINK_WIDTH_SHIFT 4 +#define PCIE_LINK_SPEED_MASK 0x0F +#define PCIE_LINK_SPEED_2500 0x01 +#define PCIE_LINK_SPEED_5000 0x02 +#define PCIE_DEVICE_CONTROL2_16ms 0x0005 + +#ifndef ETH_ADDR_LEN +#define ETH_ADDR_LEN 6 +#endif + +#define PHY_REVISION_MASK 0xFFFFFFF0 +#define MAX_PHY_REG_ADDRESS 0x1F /* 5 bit address bus (0-0x1F) */ +#define MAX_PHY_MULTI_PAGE_REG 0xF + +/* Bit definitions for valid PHY IDs. + * I = Integrated + * E = External + */ +#define M88E1000_E_PHY_ID 0x01410C50 +#define M88E1000_I_PHY_ID 0x01410C30 +#define M88E1011_I_PHY_ID 0x01410C20 +#define IGP01E1000_I_PHY_ID 0x02A80380 +#define M88E1111_I_PHY_ID 0x01410CC0 +#define M88E1543_E_PHY_ID 0x01410EA0 +#define M88E1512_E_PHY_ID 0x01410DD0 +#define M88E1112_E_PHY_ID 0x01410C90 +#define I347AT4_E_PHY_ID 0x01410DC0 +#define M88E1340M_E_PHY_ID 0x01410DF0 +#define GG82563_E_PHY_ID 0x01410CA0 +#define IGP03E1000_E_PHY_ID 0x02A80390 +#define IFE_E_PHY_ID 0x02A80330 +#define IFE_PLUS_E_PHY_ID 0x02A80320 +#define IFE_C_E_PHY_ID 0x02A80310 +#define I82580_I_PHY_ID 0x015403A0 +#define I350_I_PHY_ID 0x015403B0 +#define I210_I_PHY_ID 0x01410C00 +#define IGP04E1000_E_PHY_ID 0x02A80391 +#define M88_VENDOR 0x0141 + +/* M88E1000 Specific Registers */ +#define M88E1000_PHY_SPEC_CTRL 0x10 /* PHY Specific Control Reg */ +#define M88E1000_PHY_SPEC_STATUS 0x11 /* PHY Specific Status Reg */ +#define M88E1000_EXT_PHY_SPEC_CTRL 0x14 /* Extended PHY Specific Cntrl */ +#define M88E1000_RX_ERR_CNTR 0x15 /* Receive Error Counter */ + +#define M88E1000_PHY_PAGE_SELECT 0x1D /* Reg 29 for pg number setting */ +#define M88E1000_PHY_GEN_CONTROL 0x1E /* meaning depends on reg 29 */ + +/* M88E1000 PHY Specific Control Register */ +#define M88E1000_PSCR_POLARITY_REVERSAL 0x0002 /* 1=Polarity Reverse enabled */ +/* MDI Crossover Mode bits 6:5 Manual MDI configuration */ +#define M88E1000_PSCR_MDI_MANUAL_MODE 0x0000 +#define M88E1000_PSCR_MDIX_MANUAL_MODE 0x0020 /* Manual MDIX configuration */ +/* 1000BASE-T: Auto crossover, 100BASE-TX/10BASE-T: MDI Mode */ +#define M88E1000_PSCR_AUTO_X_1000T 0x0040 +/* Auto crossover enabled all speeds */ +#define M88E1000_PSCR_AUTO_X_MODE 0x0060 +#define M88E1000_PSCR_ASSERT_CRS_ON_TX 0x0800 /* 1=Assert CRS on Tx */ + +/* M88E1000 PHY Specific Status Register */ +#define M88E1000_PSSR_REV_POLARITY 0x0002 /* 1=Polarity reversed */ +#define M88E1000_PSSR_DOWNSHIFT 0x0020 /* 1=Downshifted */ +#define M88E1000_PSSR_MDIX 0x0040 /* 1=MDIX; 0=MDI */ +/* 0 = <50M + * 1 = 50-80M + * 2 = 80-110M + * 3 = 110-140M + * 4 = >140M + */ +#define M88E1000_PSSR_CABLE_LENGTH 0x0380 +#define M88E1000_PSSR_LINK 0x0400 /* 1=Link up, 0=Link down */ +#define M88E1000_PSSR_SPD_DPLX_RESOLVED 0x0800 /* 1=Speed & Duplex resolved */ +#define M88E1000_PSSR_SPEED 0xC000 /* Speed, bits 14:15 */ +#define M88E1000_PSSR_1000MBS 0x8000 /* 10=1000Mbs */ + +#define M88E1000_PSSR_CABLE_LENGTH_SHIFT 7 + +/* Number of times we will attempt to autonegotiate before downshifting if we + * are the master + */ +#define M88E1000_EPSCR_MASTER_DOWNSHIFT_MASK 0x0C00 +#define M88E1000_EPSCR_MASTER_DOWNSHIFT_1X 0x0000 +/* Number of times we will attempt to autonegotiate before downshifting if we + * are the slave + */ +#define M88E1000_EPSCR_SLAVE_DOWNSHIFT_MASK 0x0300 +#define M88E1000_EPSCR_SLAVE_DOWNSHIFT_1X 0x0100 +#define M88E1000_EPSCR_TX_CLK_25 0x0070 /* 25 MHz TX_CLK */ + +/* Intel I347AT4 Registers */ +#define I347AT4_PCDL 0x10 /* PHY Cable Diagnostics Length */ +#define I347AT4_PCDC 0x15 /* PHY Cable Diagnostics Control */ +#define I347AT4_PAGE_SELECT 0x16 + +/* I347AT4 Extended PHY Specific Control Register */ + +/* Number of times we will attempt to autonegotiate before downshifting if we + * are the master + */ +#define I347AT4_PSCR_DOWNSHIFT_ENABLE 0x0800 +#define I347AT4_PSCR_DOWNSHIFT_MASK 0x7000 +#define I347AT4_PSCR_DOWNSHIFT_1X 0x0000 +#define I347AT4_PSCR_DOWNSHIFT_2X 0x1000 +#define I347AT4_PSCR_DOWNSHIFT_3X 0x2000 +#define I347AT4_PSCR_DOWNSHIFT_4X 0x3000 +#define I347AT4_PSCR_DOWNSHIFT_5X 0x4000 +#define I347AT4_PSCR_DOWNSHIFT_6X 0x5000 +#define I347AT4_PSCR_DOWNSHIFT_7X 0x6000 +#define I347AT4_PSCR_DOWNSHIFT_8X 0x7000 + +/* I347AT4 PHY Cable Diagnostics Control */ +#define I347AT4_PCDC_CABLE_LENGTH_UNIT 0x0400 /* 0=cm 1=meters */ + +/* M88E1112 only registers */ +#define M88E1112_VCT_DSP_DISTANCE 0x001A + +/* M88EC018 Rev 2 specific DownShift settings */ +#define M88EC018_EPSCR_DOWNSHIFT_COUNTER_MASK 0x0E00 +#define M88EC018_EPSCR_DOWNSHIFT_COUNTER_5X 0x0800 + +/* Bits... + * 15-5: page + * 4-0: register offset + */ +#define GG82563_PAGE_SHIFT 5 +#define GG82563_REG(page, reg) \ + (((page) << GG82563_PAGE_SHIFT) | ((reg) & MAX_PHY_REG_ADDRESS)) +#define GG82563_MIN_ALT_REG 30 + +/* GG82563 Specific Registers */ +#define GG82563_PHY_SPEC_CTRL GG82563_REG(0, 16) /* PHY Spec Cntrl */ +#define GG82563_PHY_PAGE_SELECT GG82563_REG(0, 22) /* Page Select */ +#define GG82563_PHY_SPEC_CTRL_2 GG82563_REG(0, 26) /* PHY Spec Cntrl2 */ +#define GG82563_PHY_PAGE_SELECT_ALT GG82563_REG(0, 29) /* Alt Page Select */ + +/* MAC Specific Control Register */ +#define GG82563_PHY_MAC_SPEC_CTRL GG82563_REG(2, 21) + +#define GG82563_PHY_DSP_DISTANCE GG82563_REG(5, 26) /* DSP Distance */ + +/* Page 193 - Port Control Registers */ +/* Kumeran Mode Control */ +#define GG82563_PHY_KMRN_MODE_CTRL GG82563_REG(193, 16) +#define GG82563_PHY_PWR_MGMT_CTRL GG82563_REG(193, 20) /* Pwr Mgt Ctrl */ + +/* Page 194 - KMRN Registers */ +#define GG82563_PHY_INBAND_CTRL GG82563_REG(194, 18) /* Inband Ctrl */ + +/* MDI Control */ +#define E1000_MDIC_REG_MASK 0x001F0000 +#define E1000_MDIC_REG_SHIFT 16 +#define E1000_MDIC_PHY_MASK 0x03E00000 +#define E1000_MDIC_PHY_SHIFT 21 +#define E1000_MDIC_OP_WRITE 0x04000000 +#define E1000_MDIC_OP_READ 0x08000000 +#define E1000_MDIC_READY 0x10000000 +#define E1000_MDIC_ERROR 0x40000000 +#define E1000_MDIC_DEST 0x80000000 + +/* SerDes Control */ +#define E1000_GEN_CTL_READY 0x80000000 +#define E1000_GEN_CTL_ADDRESS_SHIFT 8 +#define E1000_GEN_POLL_TIMEOUT 640 + +/* LinkSec register fields */ +#define E1000_LSECTXCAP_SUM_MASK 0x00FF0000 +#define E1000_LSECTXCAP_SUM_SHIFT 16 +#define E1000_LSECRXCAP_SUM_MASK 0x00FF0000 +#define E1000_LSECRXCAP_SUM_SHIFT 16 + +#define E1000_LSECTXCTRL_EN_MASK 0x00000003 +#define E1000_LSECTXCTRL_DISABLE 0x0 +#define E1000_LSECTXCTRL_AUTH 0x1 +#define E1000_LSECTXCTRL_AUTH_ENCRYPT 0x2 +#define E1000_LSECTXCTRL_AISCI 0x00000020 +#define E1000_LSECTXCTRL_PNTHRSH_MASK 0xFFFFFF00 +#define E1000_LSECTXCTRL_RSV_MASK 0x000000D8 + +#define E1000_LSECRXCTRL_EN_MASK 0x0000000C +#define E1000_LSECRXCTRL_EN_SHIFT 2 +#define E1000_LSECRXCTRL_DISABLE 0x0 +#define E1000_LSECRXCTRL_CHECK 0x1 +#define E1000_LSECRXCTRL_STRICT 0x2 +#define E1000_LSECRXCTRL_DROP 0x3 +#define E1000_LSECRXCTRL_PLSH 0x00000040 +#define E1000_LSECRXCTRL_RP 0x00000080 +#define E1000_LSECRXCTRL_RSV_MASK 0xFFFFFF33 + +/* Tx Rate-Scheduler Config fields */ +#define E1000_RTTBCNRC_RS_ENA 0x80000000 +#define E1000_RTTBCNRC_RF_DEC_MASK 0x00003FFF +#define E1000_RTTBCNRC_RF_INT_SHIFT 14 +#define E1000_RTTBCNRC_RF_INT_MASK \ + (E1000_RTTBCNRC_RF_DEC_MASK << E1000_RTTBCNRC_RF_INT_SHIFT) + +/* DMA Coalescing register fields */ +/* DMA Coalescing Watchdog Timer */ +#define E1000_DMACR_DMACWT_MASK 0x00003FFF +/* DMA Coalescing Rx Threshold */ +#define E1000_DMACR_DMACTHR_MASK 0x00FF0000 +#define E1000_DMACR_DMACTHR_SHIFT 16 +/* Lx when no PCIe transactions */ +#define E1000_DMACR_DMAC_LX_MASK 0x30000000 +#define E1000_DMACR_DMAC_LX_SHIFT 28 +#define E1000_DMACR_DMAC_EN 0x80000000 /* Enable DMA Coalescing */ +/* DMA Coalescing BMC-to-OS Watchdog Enable */ +#define E1000_DMACR_DC_BMC2OSW_EN 0x00008000 + +/* DMA Coalescing Transmit Threshold */ +#define E1000_DMCTXTH_DMCTTHR_MASK 0x00000FFF + +#define E1000_DMCTLX_TTLX_MASK 0x00000FFF /* Time to LX request */ + +/* Rx Traffic Rate Threshold */ +#define E1000_DMCRTRH_UTRESH_MASK 0x0007FFFF +/* Rx packet rate in current window */ +#define E1000_DMCRTRH_LRPRCW 0x80000000 + +/* DMA Coal Rx Traffic Current Count */ +#define E1000_DMCCNT_CCOUNT_MASK 0x01FFFFFF + +/* Flow ctrl Rx Threshold High val */ +#define E1000_FCRTC_RTH_COAL_MASK 0x0003FFF0 +#define E1000_FCRTC_RTH_COAL_SHIFT 4 +/* Lx power decision based on DMA coal */ +#define E1000_PCIEMISC_LX_DECISION 0x00000080 + +#define E1000_RXPBS_CFG_TS_EN 0x80000000 /* Timestamp in Rx buffer */ +#define E1000_RXPBS_SIZE_I210_MASK 0x0000003F /* Rx packet buffer size */ +#define E1000_TXPB0S_SIZE_I210_MASK 0x0000003F /* Tx packet buffer 0 size */ +#define I210_RXPBSIZE_DEFAULT 0x000000A2 /* RXPBSIZE default */ +#define I210_TXPBSIZE_DEFAULT 0x04000014 /* TXPBSIZE default */ + +/* Proxy Filter Control */ +#define E1000_PROXYFC_D0 0x00000001 /* Enable offload in D0 */ +#define E1000_PROXYFC_EX 0x00000004 /* Directed exact proxy */ +#define E1000_PROXYFC_MC 0x00000008 /* Directed MC Proxy */ +#define E1000_PROXYFC_BC 0x00000010 /* Broadcast Proxy Enable */ +#define E1000_PROXYFC_ARP_DIRECTED 0x00000020 /* Directed ARP Proxy Ena */ +#define E1000_PROXYFC_IPV4 0x00000040 /* Directed IPv4 Enable */ +#define E1000_PROXYFC_IPV6 0x00000080 /* Directed IPv6 Enable */ +#define E1000_PROXYFC_NS 0x00000200 /* IPv6 Neighbor Solicitation */ +#define E1000_PROXYFC_ARP 0x00000800 /* ARP Request Proxy Ena */ +/* Proxy Status */ +#define E1000_PROXYS_CLEAR 0xFFFFFFFF /* Clear */ + +/* Firmware Status */ +#define E1000_FWSTS_FWRI 0x80000000 /* FW Reset Indication */ +/* VF Control */ +#define E1000_VTCTRL_RST 0x04000000 /* Reset VF */ + +#define E1000_STATUS_LAN_ID_MASK 0x00000000C /* Mask for Lan ID field */ +/* Lan ID bit field offset in status register */ +#define E1000_STATUS_LAN_ID_OFFSET 2 +#define E1000_VFTA_ENTRIES 128 +#define E1000_TQAVCC_QUEUEMODE 0x80000000 /* queue mode, 0=strict, 1=SR mode */ +#define E1000_TQAVCTRL_TXMODE 0x00000001 /* Transmit mode, 0=legacy, 1=QAV */ +#define E1000_TQAVCTRL_1588_STAT_EN 0x00000004 /* report DMA time of tx packets */ +#define E1000_TQAVCTRL_DATA_FETCH_ARB 0x00000010 /* data fetch arbitration */ +#define E1000_TQAVCTRL_DATA_TRAN_ARB 0x00000100 /* data tx arbitration */ +#define E1000_TQAVCTRL_DATA_TRAN_TIM 0x00000200 /* data launch time valid */ +#define E1000_TQAVCTRL_SP_WAIT_SR 0x00000400 /* stall SP to guarantee SR */ +#define E1000_TQAVCTRL_FETCH_TM_SHIFT (16) /* ... and associated shift value */ + +/* Tx packet buffer fields */ +#define E1000_TXPBSIZE_PBSZ_MASK 0x3F +#define E1000_TXPBSIZE_TX0PB_SHIFT 0 +#define E1000_TXPBSIZE_TX1PB_SHIFT 6 +#define E1000_TXPBSIZE_TX2PB_SHIFT 12 +#define E1000_TXPBSIZE_TX3PB_SHIFT 18 +#ifndef E1000_UNUSEDARG +#define E1000_UNUSEDARG +#endif /* E1000_UNUSEDARG */ +#ifndef ERROR_REPORT +#define ERROR_REPORT(fmt) do { } while (0) +#endif /* ERROR_REPORT */ +#define E1000_TSAUXC_SAMP_AUTO 0x00000008 /* sample current ts */ +#endif /* _E1000_DEFINES_H_ */ diff --git a/drivers/staging/igb_avb/e1000_hw.h b/drivers/staging/igb_avb/e1000_hw.h new file mode 100644 index 000000000000..74cb22ee8ead --- /dev/null +++ b/drivers/staging/igb_avb/e1000_hw.h @@ -0,0 +1,792 @@ +/******************************************************************************* + + Intel(R) Gigabit Ethernet Linux driver + Copyright(c) 2007-2015 Intel Corporation. + + This program is free software; you can redistribute it and/or modify it + under the terms and conditions of the GNU General Public License, + version 2, as published by the Free Software Foundation. + + This program is distributed in the hope 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. + + The full GNU General Public License is included in this distribution in + the file called "COPYING". + + Contact Information: + Linux NICS + e1000-devel Mailing List + Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + +*******************************************************************************/ + +#ifndef _E1000_HW_H_ +#define _E1000_HW_H_ + +#include "e1000_osdep.h" +#include "e1000_regs.h" +#include "e1000_defines.h" + +struct e1000_hw; + +#define E1000_DEV_ID_82576 0x10C9 +#define E1000_DEV_ID_82576_FIBER 0x10E6 +#define E1000_DEV_ID_82576_SERDES 0x10E7 +#define E1000_DEV_ID_82576_QUAD_COPPER 0x10E8 +#define E1000_DEV_ID_82576_QUAD_COPPER_ET2 0x1526 +#define E1000_DEV_ID_82576_NS 0x150A +#define E1000_DEV_ID_82576_NS_SERDES 0x1518 +#define E1000_DEV_ID_82576_SERDES_QUAD 0x150D +#define E1000_DEV_ID_82575EB_COPPER 0x10A7 +#define E1000_DEV_ID_82575EB_FIBER_SERDES 0x10A9 +#define E1000_DEV_ID_82575GB_QUAD_COPPER 0x10D6 +#define E1000_DEV_ID_82580_COPPER 0x150E +#define E1000_DEV_ID_82580_FIBER 0x150F +#define E1000_DEV_ID_82580_SERDES 0x1510 +#define E1000_DEV_ID_82580_SGMII 0x1511 +#define E1000_DEV_ID_82580_COPPER_DUAL 0x1516 +#define E1000_DEV_ID_82580_QUAD_FIBER 0x1527 +#define E1000_DEV_ID_I350_COPPER 0x1521 +#define E1000_DEV_ID_I350_FIBER 0x1522 +#define E1000_DEV_ID_I350_SERDES 0x1523 +#define E1000_DEV_ID_I350_SGMII 0x1524 +#define E1000_DEV_ID_I350_DA4 0x1546 +#define E1000_DEV_ID_I210_COPPER 0x1533 +#define E1000_DEV_ID_I210_COPPER_OEM1 0x1534 +#define E1000_DEV_ID_I210_COPPER_IT 0x1535 +#define E1000_DEV_ID_I210_FIBER 0x1536 +#define E1000_DEV_ID_I210_SERDES 0x1537 +#define E1000_DEV_ID_I210_SGMII 0x1538 +#define E1000_DEV_ID_I210_AUTOMOTIVE 0x15F6 +#define E1000_DEV_ID_I210_COPPER_FLASHLESS 0x157B +#define E1000_DEV_ID_I210_SERDES_FLASHLESS 0x157C +#define E1000_DEV_ID_I211_COPPER 0x1539 +#define E1000_DEV_ID_I354_BACKPLANE_1GBPS 0x1F40 +#define E1000_DEV_ID_I354_SGMII 0x1F41 +#define E1000_DEV_ID_I354_BACKPLANE_2_5GBPS 0x1F45 +#define E1000_DEV_ID_DH89XXCC_SGMII 0x0438 +#define E1000_DEV_ID_DH89XXCC_SERDES 0x043A +#define E1000_DEV_ID_DH89XXCC_BACKPLANE 0x043C +#define E1000_DEV_ID_DH89XXCC_SFP 0x0440 + +#define E1000_REVISION_0 0 +#define E1000_REVISION_1 1 +#define E1000_REVISION_2 2 +#define E1000_REVISION_3 3 +#define E1000_REVISION_4 4 + +#define E1000_FUNC_0 0 +#define E1000_FUNC_1 1 +#define E1000_FUNC_2 2 +#define E1000_FUNC_3 3 + +#define E1000_ALT_MAC_ADDRESS_OFFSET_LAN0 0 +#define E1000_ALT_MAC_ADDRESS_OFFSET_LAN1 3 +#define E1000_ALT_MAC_ADDRESS_OFFSET_LAN2 6 +#define E1000_ALT_MAC_ADDRESS_OFFSET_LAN3 9 + +enum e1000_mac_type { + e1000_undefined = 0, + e1000_82575, + e1000_82576, + e1000_82580, + e1000_i350, + e1000_i354, + e1000_i210, + e1000_i211, + e1000_num_macs /* List is 1-based, so subtract 1 for true count. */ +}; + +enum e1000_media_type { + e1000_media_type_unknown = 0, + e1000_media_type_copper = 1, + e1000_media_type_fiber = 2, + e1000_media_type_internal_serdes = 3, + e1000_num_media_types +}; + +enum e1000_nvm_type { + e1000_nvm_unknown = 0, + e1000_nvm_none, + e1000_nvm_eeprom_spi, + e1000_nvm_flash_hw, + e1000_nvm_invm, + e1000_nvm_flash_sw +}; + +enum e1000_nvm_override { + e1000_nvm_override_none = 0, + e1000_nvm_override_spi_small, + e1000_nvm_override_spi_large, +}; + +enum e1000_phy_type { + e1000_phy_unknown = 0, + e1000_phy_none, + e1000_phy_m88, + e1000_phy_igp, + e1000_phy_igp_2, + e1000_phy_gg82563, + e1000_phy_igp_3, + e1000_phy_ife, + e1000_phy_82580, + e1000_phy_vf, + e1000_phy_i210, +}; + +enum e1000_bus_type { + e1000_bus_type_unknown = 0, + e1000_bus_type_pci, + e1000_bus_type_pcix, + e1000_bus_type_pci_express, + e1000_bus_type_reserved +}; + +enum e1000_bus_speed { + e1000_bus_speed_unknown = 0, + e1000_bus_speed_33, + e1000_bus_speed_66, + e1000_bus_speed_100, + e1000_bus_speed_120, + e1000_bus_speed_133, + e1000_bus_speed_2500, + e1000_bus_speed_5000, + e1000_bus_speed_reserved +}; + +enum e1000_bus_width { + e1000_bus_width_unknown = 0, + e1000_bus_width_pcie_x1, + e1000_bus_width_pcie_x2, + e1000_bus_width_pcie_x4 = 4, + e1000_bus_width_pcie_x8 = 8, + e1000_bus_width_32, + e1000_bus_width_64, + e1000_bus_width_reserved +}; + +enum e1000_1000t_rx_status { + e1000_1000t_rx_status_not_ok = 0, + e1000_1000t_rx_status_ok, + e1000_1000t_rx_status_undefined = 0xFF +}; + +enum e1000_rev_polarity { + e1000_rev_polarity_normal = 0, + e1000_rev_polarity_reversed, + e1000_rev_polarity_undefined = 0xFF +}; + +enum e1000_fc_mode { + e1000_fc_none = 0, + e1000_fc_rx_pause, + e1000_fc_tx_pause, + e1000_fc_full, + e1000_fc_default = 0xFF +}; + +enum e1000_ms_type { + e1000_ms_hw_default = 0, + e1000_ms_force_master, + e1000_ms_force_slave, + e1000_ms_auto +}; + +enum e1000_smart_speed { + e1000_smart_speed_default = 0, + e1000_smart_speed_on, + e1000_smart_speed_off +}; + +enum e1000_serdes_link_state { + e1000_serdes_link_down = 0, + e1000_serdes_link_autoneg_progress, + e1000_serdes_link_autoneg_complete, + e1000_serdes_link_forced_up +}; + +#ifndef __le16 +#define __le16 u16 +#endif +#ifndef __le32 +#define __le32 u32 +#endif +#ifndef __le64 +#define __le64 u64 +#endif +/* Receive Descriptor */ +struct e1000_rx_desc { + __le64 buffer_addr; /* Address of the descriptor's data buffer */ + __le16 length; /* Length of data DMAed into data buffer */ + __le16 csum; /* Packet checksum */ + u8 status; /* Descriptor status */ + u8 errors; /* Descriptor Errors */ + __le16 special; +}; + +/* Receive Descriptor - Extended */ +union e1000_rx_desc_extended { + struct { + __le64 buffer_addr; + __le64 reserved; + } read; + struct { + struct { + __le32 mrq; /* Multiple Rx Queues */ + union { + __le32 rss; /* RSS Hash */ + struct { + __le16 ip_id; /* IP id */ + __le16 csum; /* Packet Checksum */ + } csum_ip; + } hi_dword; + } lower; + struct { + __le32 status_error; /* ext status/error */ + __le16 length; + __le16 vlan; /* VLAN tag */ + } upper; + } wb; /* writeback */ +}; + +#define MAX_PS_BUFFERS 4 + +/* Number of packet split data buffers (not including the header buffer) */ +#define PS_PAGE_BUFFERS (MAX_PS_BUFFERS - 1) + +/* Receive Descriptor - Packet Split */ +union e1000_rx_desc_packet_split { + struct { + /* one buffer for protocol header(s), three data buffers */ + __le64 buffer_addr[MAX_PS_BUFFERS]; + } read; + struct { + struct { + __le32 mrq; /* Multiple Rx Queues */ + union { + __le32 rss; /* RSS Hash */ + struct { + __le16 ip_id; /* IP id */ + __le16 csum; /* Packet Checksum */ + } csum_ip; + } hi_dword; + } lower; + struct { + __le32 status_error; /* ext status/error */ + __le16 length0; /* length of buffer 0 */ + __le16 vlan; /* VLAN tag */ + } middle; + struct { + __le16 header_status; + /* length of buffers 1-3 */ + __le16 length[PS_PAGE_BUFFERS]; + } upper; + __le64 reserved; + } wb; /* writeback */ +}; + +/* Transmit Descriptor */ +struct e1000_tx_desc { + __le64 buffer_addr; /* Address of the descriptor's data buffer */ + union { + __le32 data; + struct { + __le16 length; /* Data buffer length */ + u8 cso; /* Checksum offset */ + u8 cmd; /* Descriptor control */ + } flags; + } lower; + union { + __le32 data; + struct { + u8 status; /* Descriptor status */ + u8 css; /* Checksum start */ + __le16 special; + } fields; + } upper; +}; + +/* Offload Context Descriptor */ +struct e1000_context_desc { + union { + __le32 ip_config; + struct { + u8 ipcss; /* IP checksum start */ + u8 ipcso; /* IP checksum offset */ + __le16 ipcse; /* IP checksum end */ + } ip_fields; + } lower_setup; + union { + __le32 tcp_config; + struct { + u8 tucss; /* TCP checksum start */ + u8 tucso; /* TCP checksum offset */ + __le16 tucse; /* TCP checksum end */ + } tcp_fields; + } upper_setup; + __le32 cmd_and_length; + union { + __le32 data; + struct { + u8 status; /* Descriptor status */ + u8 hdr_len; /* Header length */ + __le16 mss; /* Maximum segment size */ + } fields; + } tcp_seg_setup; +}; + +/* Offload data descriptor */ +struct e1000_data_desc { + __le64 buffer_addr; /* Address of the descriptor's buffer address */ + union { + __le32 data; + struct { + __le16 length; /* Data buffer length */ + u8 typ_len_ext; + u8 cmd; + } flags; + } lower; + union { + __le32 data; + struct { + u8 status; /* Descriptor status */ + u8 popts; /* Packet Options */ + __le16 special; + } fields; + } upper; +}; + +/* Statistics counters collected by the MAC */ +struct e1000_hw_stats { + u64 crcerrs; + u64 algnerrc; + u64 symerrs; + u64 rxerrc; + u64 mpc; + u64 scc; + u64 ecol; + u64 mcc; + u64 latecol; + u64 colc; + u64 dc; + u64 tncrs; + u64 sec; + u64 cexterr; + u64 rlec; + u64 xonrxc; + u64 xontxc; + u64 xoffrxc; + u64 xofftxc; + u64 fcruc; + u64 prc64; + u64 prc127; + u64 prc255; + u64 prc511; + u64 prc1023; + u64 prc1522; + u64 gprc; + u64 bprc; + u64 mprc; + u64 gptc; + u64 gorc; + u64 gotc; + u64 rnbc; + u64 ruc; + u64 rfc; + u64 roc; + u64 rjc; + u64 mgprc; + u64 mgpdc; + u64 mgptc; + u64 tor; + u64 tot; + u64 tpr; + u64 tpt; + u64 ptc64; + u64 ptc127; + u64 ptc255; + u64 ptc511; + u64 ptc1023; + u64 ptc1522; + u64 mptc; + u64 bptc; + u64 tsctc; + u64 tsctfc; + u64 iac; + u64 icrxptc; + u64 icrxatc; + u64 ictxptc; + u64 ictxatc; + u64 ictxqec; + u64 ictxqmtc; + u64 icrxdmtc; + u64 icrxoc; + u64 cbtmpc; + u64 htdpmc; + u64 cbrdpc; + u64 cbrmpc; + u64 rpthc; + u64 hgptc; + u64 htcbdpc; + u64 hgorc; + u64 hgotc; + u64 lenerrs; + u64 scvpc; + u64 hrmpc; + u64 doosync; + u64 o2bgptc; + u64 o2bspc; + u64 b2ospc; + u64 b2ogprc; +}; + +struct e1000_phy_stats { + u32 idle_errors; + u32 receive_errors; +}; + +struct e1000_host_mng_dhcp_cookie { + u32 signature; + u8 status; + u8 reserved0; + u16 vlan_id; + u32 reserved1; + u16 reserved2; + u8 reserved3; + u8 checksum; +}; + +/* Host Interface "Rev 1" */ +struct e1000_host_command_header { + u8 command_id; + u8 command_length; + u8 command_options; + u8 checksum; +}; + +#define E1000_HI_MAX_DATA_LENGTH 252 +struct e1000_host_command_info { + struct e1000_host_command_header command_header; + u8 command_data[E1000_HI_MAX_DATA_LENGTH]; +}; + +/* Host Interface "Rev 2" */ +struct e1000_host_mng_command_header { + u8 command_id; + u8 checksum; + u16 reserved1; + u16 reserved2; + u16 command_length; +}; + +#define E1000_HI_MAX_MNG_DATA_LENGTH 0x6F8 +struct e1000_host_mng_command_info { + struct e1000_host_mng_command_header command_header; + u8 command_data[E1000_HI_MAX_MNG_DATA_LENGTH]; +}; + +#include "e1000_mac.h" +#include "e1000_phy.h" +#include "e1000_nvm.h" +#include "e1000_manage.h" +#include "e1000_mbx.h" + +/* Function pointers for the MAC. */ +struct e1000_mac_operations { + s32 (*init_params)(struct e1000_hw *); + s32 (*id_led_init)(struct e1000_hw *); + s32 (*blink_led)(struct e1000_hw *); + bool (*check_mng_mode)(struct e1000_hw *); + s32 (*check_for_link)(struct e1000_hw *); + s32 (*cleanup_led)(struct e1000_hw *); + void (*clear_hw_cntrs)(struct e1000_hw *); + void (*clear_vfta)(struct e1000_hw *); + s32 (*get_bus_info)(struct e1000_hw *); + void (*set_lan_id)(struct e1000_hw *); + s32 (*get_link_up_info)(struct e1000_hw *, u16 *, u16 *); + s32 (*led_on)(struct e1000_hw *); + s32 (*led_off)(struct e1000_hw *); + void (*update_mc_addr_list)(struct e1000_hw *, u8 *, u32); + s32 (*reset_hw)(struct e1000_hw *); + s32 (*init_hw)(struct e1000_hw *); + void (*shutdown_serdes)(struct e1000_hw *); + void (*power_up_serdes)(struct e1000_hw *); + s32 (*setup_link)(struct e1000_hw *); + s32 (*setup_physical_interface)(struct e1000_hw *); + s32 (*setup_led)(struct e1000_hw *); + void (*write_vfta)(struct e1000_hw *, u32, u32); + void (*config_collision_dist)(struct e1000_hw *); + int (*rar_set)(struct e1000_hw *, u8*, u32); + s32 (*read_mac_addr)(struct e1000_hw *); + s32 (*validate_mdi_setting)(struct e1000_hw *); + s32 (*get_thermal_sensor_data)(struct e1000_hw *); + s32 (*init_thermal_sensor_thresh)(struct e1000_hw *); + s32 (*acquire_swfw_sync)(struct e1000_hw *, u16); + void (*release_swfw_sync)(struct e1000_hw *, u16); +}; + +/* When to use various PHY register access functions: + * + * Func Caller + * Function Does Does When to use + * ~~~~~~~~~~~~ ~~~~~ ~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * X_reg L,P,A n/a for simple PHY reg accesses + * X_reg_locked P,A L for multiple accesses of different regs + * on different pages + * X_reg_page A L,P for multiple accesses of different regs + * on the same page + * + * Where X=[read|write], L=locking, P=sets page, A=register access + * + */ +struct e1000_phy_operations { + s32 (*init_params)(struct e1000_hw *); + s32 (*acquire)(struct e1000_hw *); + s32 (*check_polarity)(struct e1000_hw *); + s32 (*check_reset_block)(struct e1000_hw *); + s32 (*commit)(struct e1000_hw *); + s32 (*force_speed_duplex)(struct e1000_hw *); + s32 (*get_cfg_done)(struct e1000_hw *hw); + s32 (*get_cable_length)(struct e1000_hw *); + s32 (*get_info)(struct e1000_hw *); + s32 (*set_page)(struct e1000_hw *, u16); + s32 (*read_reg)(struct e1000_hw *, u32, u16 *); + s32 (*read_reg_locked)(struct e1000_hw *, u32, u16 *); + s32 (*read_reg_page)(struct e1000_hw *, u32, u16 *); + void (*release)(struct e1000_hw *); + s32 (*reset)(struct e1000_hw *); + s32 (*set_d0_lplu_state)(struct e1000_hw *, bool); + s32 (*set_d3_lplu_state)(struct e1000_hw *, bool); + s32 (*write_reg)(struct e1000_hw *, u32, u16); + s32 (*write_reg_locked)(struct e1000_hw *, u32, u16); + s32 (*write_reg_page)(struct e1000_hw *, u32, u16); + void (*power_up)(struct e1000_hw *); + void (*power_down)(struct e1000_hw *); + s32 (*read_i2c_byte)(struct e1000_hw *, u8, u8, u8 *); + s32 (*write_i2c_byte)(struct e1000_hw *, u8, u8, u8); +}; + +/* Function pointers for the NVM. */ +struct e1000_nvm_operations { + s32 (*init_params)(struct e1000_hw *); + s32 (*acquire)(struct e1000_hw *); + s32 (*read)(struct e1000_hw *, u16, u16, u16 *); + void (*release)(struct e1000_hw *); + void (*reload)(struct e1000_hw *); + s32 (*update)(struct e1000_hw *); + s32 (*valid_led_default)(struct e1000_hw *, u16 *); + s32 (*validate)(struct e1000_hw *); + s32 (*write)(struct e1000_hw *, u16, u16, u16 *); +}; + +#define E1000_MAX_SENSORS 3 + +struct e1000_thermal_diode_data { + u8 location; + u8 temp; + u8 caution_thresh; + u8 max_op_thresh; +}; + +struct e1000_thermal_sensor_data { + struct e1000_thermal_diode_data sensor[E1000_MAX_SENSORS]; +}; + +struct e1000_mac_info { + struct e1000_mac_operations ops; + u8 addr[ETH_ADDR_LEN]; + u8 perm_addr[ETH_ADDR_LEN]; + + enum e1000_mac_type type; + + u32 collision_delta; + u32 ledctl_default; + u32 ledctl_mode1; + u32 ledctl_mode2; + u32 mc_filter_type; + u32 tx_packet_delta; + u32 txcw; + + u16 current_ifs_val; + u16 ifs_max_val; + u16 ifs_min_val; + u16 ifs_ratio; + u16 ifs_step_size; + u16 mta_reg_count; + u16 uta_reg_count; + + /* Maximum size of the MTA register table in all supported adapters */ +#define MAX_MTA_REG 128 + u32 mta_shadow[MAX_MTA_REG]; + u16 rar_entry_count; + + u8 forced_speed_duplex; + + bool adaptive_ifs; + bool has_fwsm; + bool arc_subsystem_valid; + bool asf_firmware_present; + bool autoneg; + bool autoneg_failed; + bool get_link_status; + bool in_ifs_mode; + enum e1000_serdes_link_state serdes_link_state; + bool serdes_has_link; + bool tx_pkt_filtering; + struct e1000_thermal_sensor_data thermal_sensor_data; +}; + +struct e1000_phy_info { + struct e1000_phy_operations ops; + enum e1000_phy_type type; + + enum e1000_1000t_rx_status local_rx; + enum e1000_1000t_rx_status remote_rx; + enum e1000_ms_type ms_type; + enum e1000_ms_type original_ms_type; + enum e1000_rev_polarity cable_polarity; + enum e1000_smart_speed smart_speed; + + u32 addr; + u32 id; + u32 reset_delay_us; /* in usec */ + u32 revision; + + enum e1000_media_type media_type; + + u16 autoneg_advertised; + u16 autoneg_mask; + u16 cable_length; + u16 max_cable_length; + u16 min_cable_length; + + u8 mdix; + + bool disable_polarity_correction; + bool is_mdix; + bool polarity_correction; + bool reset_disable; + bool speed_downgraded; + bool autoneg_wait_to_complete; +}; + +struct e1000_nvm_info { + struct e1000_nvm_operations ops; + enum e1000_nvm_type type; + enum e1000_nvm_override override; + + u32 flash_bank_size; + u32 flash_base_addr; + + u16 word_size; + u16 delay_usec; + u16 address_bits; + u16 opcode_bits; + u16 page_size; +}; + +struct e1000_bus_info { + enum e1000_bus_type type; + enum e1000_bus_speed speed; + enum e1000_bus_width width; + + u16 func; + u16 pci_cmd_word; +}; + +struct e1000_fc_info { + u32 high_water; /* Flow control high-water mark */ + u32 low_water; /* Flow control low-water mark */ + u16 pause_time; /* Flow control pause timer */ + u16 refresh_time; /* Flow control refresh timer */ + bool send_xon; /* Flow control send XON */ + bool strict_ieee; /* Strict IEEE mode */ + enum e1000_fc_mode current_mode; /* FC mode in effect */ + enum e1000_fc_mode requested_mode; /* FC mode requested by caller */ +}; + +struct e1000_mbx_operations { + s32 (*init_params)(struct e1000_hw *hw); + s32 (*read)(struct e1000_hw *, u32 *, u16, u16); + s32 (*write)(struct e1000_hw *, u32 *, u16, u16); + s32 (*read_posted)(struct e1000_hw *, u32 *, u16, u16); + s32 (*write_posted)(struct e1000_hw *, u32 *, u16, u16); + s32 (*check_for_msg)(struct e1000_hw *, u16); + s32 (*check_for_ack)(struct e1000_hw *, u16); + s32 (*check_for_rst)(struct e1000_hw *, u16); +}; + +struct e1000_mbx_stats { + u32 msgs_tx; + u32 msgs_rx; + + u32 acks; + u32 reqs; + u32 rsts; +}; + +struct e1000_mbx_info { + struct e1000_mbx_operations ops; + struct e1000_mbx_stats stats; + u32 timeout; + u32 usec_delay; + u16 size; +}; + +struct e1000_dev_spec_82575 { + bool sgmii_active; + bool global_device_reset; + bool eee_disable; + bool module_plugged; + bool clear_semaphore_once; + u32 mtu; + struct sfp_e1000_flags eth_flags; + u8 media_port; + bool media_changed; +}; + +struct e1000_dev_spec_vf { + u32 vf_number; + u32 v2p_mailbox; +}; + +struct e1000_hw { + void *back; + + u8 __iomem *hw_addr; + u8 __iomem *flash_address; + unsigned long io_base; + + struct e1000_mac_info mac; + struct e1000_fc_info fc; + struct e1000_phy_info phy; + struct e1000_nvm_info nvm; + struct e1000_bus_info bus; + struct e1000_mbx_info mbx; + struct e1000_host_mng_dhcp_cookie mng_cookie; + + union { + struct e1000_dev_spec_82575 _82575; + struct e1000_dev_spec_vf vf; + } dev_spec; + + u16 device_id; + u16 subsystem_vendor_id; + u16 subsystem_device_id; + u16 vendor_id; + + u8 revision_id; +}; + +#include "e1000_82575.h" +#include "e1000_i210.h" + +/* These functions must be implemented by drivers */ +s32 e1000_read_pcie_cap_reg(struct e1000_hw *hw, u32 reg, u16 *value); +s32 e1000_write_pcie_cap_reg(struct e1000_hw *hw, u32 reg, u16 *value); +void e1000_read_pci_cfg(struct e1000_hw *hw, u32 reg, u16 *value); +void e1000_write_pci_cfg(struct e1000_hw *hw, u32 reg, u16 *value); + +#endif diff --git a/drivers/staging/igb_avb/e1000_i210.c b/drivers/staging/igb_avb/e1000_i210.c new file mode 100644 index 000000000000..7e32fd112f33 --- /dev/null +++ b/drivers/staging/igb_avb/e1000_i210.c @@ -0,0 +1,993 @@ +/******************************************************************************* + + Intel(R) Gigabit Ethernet Linux driver + Copyright(c) 2007-2015 Intel Corporation. + + This program is free software; you can redistribute it and/or modify it + under the terms and conditions of the GNU General Public License, + version 2, as published by the Free Software Foundation. + + This program is distributed in the hope 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. + + The full GNU General Public License is included in this distribution in + the file called "COPYING". + + Contact Information: + Linux NICS + e1000-devel Mailing List + Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + +*******************************************************************************/ + +#include "e1000_api.h" + + +static s32 e1000_acquire_nvm_i210(struct e1000_hw *hw); +static void e1000_release_nvm_i210(struct e1000_hw *hw); +static s32 e1000_get_hw_semaphore_i210(struct e1000_hw *hw); +static s32 e1000_write_nvm_srwr(struct e1000_hw *hw, u16 offset, u16 words, + u16 *data); +static s32 e1000_pool_flash_update_done_i210(struct e1000_hw *hw); +static s32 e1000_valid_led_default_i210(struct e1000_hw *hw, u16 *data); + +/** + * e1000_acquire_nvm_i210 - Request for access to EEPROM + * @hw: pointer to the HW structure + * + * Acquire the necessary semaphores for exclusive access to the EEPROM. + * Set the EEPROM access request bit and wait for EEPROM access grant bit. + * Return successful if access grant bit set, else clear the request for + * EEPROM access and return -E1000_ERR_NVM (-1). + **/ +static s32 e1000_acquire_nvm_i210(struct e1000_hw *hw) +{ + s32 ret_val; + + DEBUGFUNC("e1000_acquire_nvm_i210"); + + ret_val = e1000_acquire_swfw_sync_i210(hw, E1000_SWFW_EEP_SM); + + return ret_val; +} + +/** + * e1000_release_nvm_i210 - Release exclusive access to EEPROM + * @hw: pointer to the HW structure + * + * Stop any current commands to the EEPROM and clear the EEPROM request bit, + * then release the semaphores acquired. + **/ +static void e1000_release_nvm_i210(struct e1000_hw *hw) +{ + DEBUGFUNC("e1000_release_nvm_i210"); + + e1000_release_swfw_sync_i210(hw, E1000_SWFW_EEP_SM); +} + +/** + * e1000_acquire_swfw_sync_i210 - Acquire SW/FW semaphore + * @hw: pointer to the HW structure + * @mask: specifies which semaphore to acquire + * + * Acquire the SW/FW semaphore to access the PHY or NVM. The mask + * will also specify which port we're acquiring the lock for. + **/ +s32 e1000_acquire_swfw_sync_i210(struct e1000_hw *hw, u16 mask) +{ + u32 swfw_sync; + u32 swmask = mask; + u32 fwmask = mask << 16; + s32 ret_val = E1000_SUCCESS; + s32 i = 0, timeout = 200; /* FIXME: find real value to use here */ + + DEBUGFUNC("e1000_acquire_swfw_sync_i210"); + + while (i < timeout) { + if (e1000_get_hw_semaphore_i210(hw)) { + ret_val = -E1000_ERR_SWFW_SYNC; + goto out; + } + + swfw_sync = E1000_READ_REG(hw, E1000_SW_FW_SYNC); + if (!(swfw_sync & (fwmask | swmask))) + break; + + /* + * Firmware currently using resource (fwmask) + * or other software thread using resource (swmask) + */ + e1000_put_hw_semaphore_generic(hw); + msec_delay_irq(5); + i++; + } + + if (i == timeout) { + DEBUGOUT("Driver can't access resource, SW_FW_SYNC timeout.\n"); + ret_val = -E1000_ERR_SWFW_SYNC; + goto out; + } + + swfw_sync |= swmask; + E1000_WRITE_REG(hw, E1000_SW_FW_SYNC, swfw_sync); + + e1000_put_hw_semaphore_generic(hw); + +out: + return ret_val; +} + +/** + * e1000_release_swfw_sync_i210 - Release SW/FW semaphore + * @hw: pointer to the HW structure + * @mask: specifies which semaphore to acquire + * + * Release the SW/FW semaphore used to access the PHY or NVM. The mask + * will also specify which port we're releasing the lock for. + **/ +void e1000_release_swfw_sync_i210(struct e1000_hw *hw, u16 mask) +{ + u32 swfw_sync; + + DEBUGFUNC("e1000_release_swfw_sync_i210"); + + while (e1000_get_hw_semaphore_i210(hw) != E1000_SUCCESS) + ; /* Empty */ + + swfw_sync = E1000_READ_REG(hw, E1000_SW_FW_SYNC); + swfw_sync &= ~mask; + E1000_WRITE_REG(hw, E1000_SW_FW_SYNC, swfw_sync); + + e1000_put_hw_semaphore_generic(hw); +} + +/** + * e1000_get_hw_semaphore_i210 - Acquire hardware semaphore + * @hw: pointer to the HW structure + * + * Acquire the HW semaphore to access the PHY or NVM + **/ +static s32 e1000_get_hw_semaphore_i210(struct e1000_hw *hw) +{ + u32 swsm; + s32 timeout = hw->nvm.word_size + 1; + s32 i = 0; + + DEBUGFUNC("e1000_get_hw_semaphore_i210"); + + /* Get the SW semaphore */ + while (i < timeout) { + swsm = E1000_READ_REG(hw, E1000_SWSM); + if (!(swsm & E1000_SWSM_SMBI)) + break; + + usec_delay(50); + i++; + } + + if (i == timeout) { + /* In rare circumstances, the SW semaphore may already be held + * unintentionally. Clear the semaphore once before giving up. + */ + if (hw->dev_spec._82575.clear_semaphore_once) { + hw->dev_spec._82575.clear_semaphore_once = false; + e1000_put_hw_semaphore_generic(hw); + for (i = 0; i < timeout; i++) { + swsm = E1000_READ_REG(hw, E1000_SWSM); + if (!(swsm & E1000_SWSM_SMBI)) + break; + + usec_delay(50); + } + } + + /* If we do not have the semaphore here, we have to give up. */ + if (i == timeout) { + DEBUGOUT("Driver can't access device - SMBI bit is set.\n"); + return -E1000_ERR_NVM; + } + } + + /* Get the FW semaphore. */ + for (i = 0; i < timeout; i++) { + swsm = E1000_READ_REG(hw, E1000_SWSM); + E1000_WRITE_REG(hw, E1000_SWSM, swsm | E1000_SWSM_SWESMBI); + + /* Semaphore acquired if bit latched */ + if (E1000_READ_REG(hw, E1000_SWSM) & E1000_SWSM_SWESMBI) + break; + + usec_delay(50); + } + + if (i == timeout) { + /* Release semaphores */ + e1000_put_hw_semaphore_generic(hw); + DEBUGOUT("Driver can't access the NVM\n"); + return -E1000_ERR_NVM; + } + + return E1000_SUCCESS; +} + +/** + * e1000_read_nvm_srrd_i210 - Reads Shadow Ram using EERD register + * @hw: pointer to the HW structure + * @offset: offset of word in the Shadow Ram to read + * @words: number of words to read + * @data: word read from the Shadow Ram + * + * Reads a 16 bit word from the Shadow Ram using the EERD register. + * Uses necessary synchronization semaphores. + **/ +s32 e1000_read_nvm_srrd_i210(struct e1000_hw *hw, u16 offset, u16 words, + u16 *data) +{ + s32 status = E1000_SUCCESS; + u16 i, count; + + DEBUGFUNC("e1000_read_nvm_srrd_i210"); + + /* We cannot hold synchronization semaphores for too long, + * because of forceful takeover procedure. However it is more efficient + * to read in bursts than synchronizing access for each word. */ + for (i = 0; i < words; i += E1000_EERD_EEWR_MAX_COUNT) { + count = (words - i) / E1000_EERD_EEWR_MAX_COUNT > 0 ? + E1000_EERD_EEWR_MAX_COUNT : (words - i); + if (hw->nvm.ops.acquire(hw) == E1000_SUCCESS) { + status = e1000_read_nvm_eerd(hw, offset, count, + data + i); + hw->nvm.ops.release(hw); + } else { + status = E1000_ERR_SWFW_SYNC; + } + + if (status != E1000_SUCCESS) + break; + } + + return status; +} + +/** + * e1000_write_nvm_srwr_i210 - Write to Shadow RAM using EEWR + * @hw: pointer to the HW structure + * @offset: offset within the Shadow RAM to be written to + * @words: number of words to write + * @data: 16 bit word(s) to be written to the Shadow RAM + * + * Writes data to Shadow RAM at offset using EEWR register. + * + * If e1000_update_nvm_checksum is not called after this function , the + * data will not be committed to FLASH and also Shadow RAM will most likely + * contain an invalid checksum. + * + * If error code is returned, data and Shadow RAM may be inconsistent - buffer + * partially written. + **/ +s32 e1000_write_nvm_srwr_i210(struct e1000_hw *hw, u16 offset, u16 words, + u16 *data) +{ + s32 status = E1000_SUCCESS; + u16 i, count; + + DEBUGFUNC("e1000_write_nvm_srwr_i210"); + + /* We cannot hold synchronization semaphores for too long, + * because of forceful takeover procedure. However it is more efficient + * to write in bursts than synchronizing access for each word. */ + for (i = 0; i < words; i += E1000_EERD_EEWR_MAX_COUNT) { + count = (words - i) / E1000_EERD_EEWR_MAX_COUNT > 0 ? + E1000_EERD_EEWR_MAX_COUNT : (words - i); + if (hw->nvm.ops.acquire(hw) == E1000_SUCCESS) { + status = e1000_write_nvm_srwr(hw, offset, count, + data + i); + hw->nvm.ops.release(hw); + } else { + status = E1000_ERR_SWFW_SYNC; + } + + if (status != E1000_SUCCESS) + break; + } + + return status; +} + +/** + * e1000_write_nvm_srwr - Write to Shadow Ram using EEWR + * @hw: pointer to the HW structure + * @offset: offset within the Shadow Ram to be written to + * @words: number of words to write + * @data: 16 bit word(s) to be written to the Shadow Ram + * + * Writes data to Shadow Ram at offset using EEWR register. + * + * If e1000_update_nvm_checksum is not called after this function , the + * Shadow Ram will most likely contain an invalid checksum. + **/ +static s32 e1000_write_nvm_srwr(struct e1000_hw *hw, u16 offset, u16 words, + u16 *data) +{ + struct e1000_nvm_info *nvm = &hw->nvm; + u32 i, k, eewr = 0; + u32 attempts = 100000; + s32 ret_val = E1000_SUCCESS; + + DEBUGFUNC("e1000_write_nvm_srwr"); + + /* + * A check for invalid values: offset too large, too many words, + * too many words for the offset, and not enough words. + */ + if ((offset >= nvm->word_size) || (words > (nvm->word_size - offset)) || + (words == 0)) { + DEBUGOUT("nvm parameter(s) out of bounds\n"); + ret_val = -E1000_ERR_NVM; + goto out; + } + + for (i = 0; i < words; i++) { + eewr = ((offset+i) << E1000_NVM_RW_ADDR_SHIFT) | + (data[i] << E1000_NVM_RW_REG_DATA) | + E1000_NVM_RW_REG_START; + + E1000_WRITE_REG(hw, E1000_SRWR, eewr); + + for (k = 0; k < attempts; k++) { + if (E1000_NVM_RW_REG_DONE & + E1000_READ_REG(hw, E1000_SRWR)) { + ret_val = E1000_SUCCESS; + break; + } + usec_delay(5); + } + + if (ret_val != E1000_SUCCESS) { + DEBUGOUT("Shadow RAM write EEWR timed out\n"); + break; + } + } + +out: + return ret_val; +} + +/** e1000_read_invm_word_i210 - Reads OTP + * @hw: pointer to the HW structure + * @address: the word address (aka eeprom offset) to read + * @data: pointer to the data read + * + * Reads 16-bit words from the OTP. Return error when the word is not + * stored in OTP. + **/ +static s32 e1000_read_invm_word_i210(struct e1000_hw *hw, u8 address, u16 *data) +{ + s32 status = -E1000_ERR_INVM_VALUE_NOT_FOUND; + u32 invm_dword; + u16 i; + u8 record_type, word_address; + + DEBUGFUNC("e1000_read_invm_word_i210"); + + for (i = 0; i < E1000_INVM_SIZE; i++) { + invm_dword = E1000_READ_REG(hw, E1000_INVM_DATA_REG(i)); + /* Get record type */ + record_type = INVM_DWORD_TO_RECORD_TYPE(invm_dword); + if (record_type == E1000_INVM_UNINITIALIZED_STRUCTURE) + break; + if (record_type == E1000_INVM_CSR_AUTOLOAD_STRUCTURE) + i += E1000_INVM_CSR_AUTOLOAD_DATA_SIZE_IN_DWORDS; + if (record_type == E1000_INVM_RSA_KEY_SHA256_STRUCTURE) + i += E1000_INVM_RSA_KEY_SHA256_DATA_SIZE_IN_DWORDS; + if (record_type == E1000_INVM_WORD_AUTOLOAD_STRUCTURE) { + word_address = INVM_DWORD_TO_WORD_ADDRESS(invm_dword); + if (word_address == address) { + *data = INVM_DWORD_TO_WORD_DATA(invm_dword); + DEBUGOUT2("Read INVM Word 0x%02x = %x", + address, *data); + status = E1000_SUCCESS; + break; + } + } + } + if (status != E1000_SUCCESS) + DEBUGOUT1("Requested word 0x%02x not found in OTP\n", address); + return status; +} + +/** e1000_read_invm_i210 - Read invm wrapper function for I210/I211 + * @hw: pointer to the HW structure + * @address: the word address (aka eeprom offset) to read + * @data: pointer to the data read + * + * Wrapper function to return data formerly found in the NVM. + **/ +static s32 e1000_read_invm_i210(struct e1000_hw *hw, u16 offset, + u16 E1000_UNUSEDARG words, u16 *data) +{ + s32 ret_val = E1000_SUCCESS; + + DEBUGFUNC("e1000_read_invm_i210"); + + /* Only the MAC addr is required to be present in the iNVM */ + switch (offset) { + case NVM_MAC_ADDR: + ret_val = e1000_read_invm_word_i210(hw, (u8)offset, &data[0]); + ret_val |= e1000_read_invm_word_i210(hw, (u8)offset+1, + &data[1]); + ret_val |= e1000_read_invm_word_i210(hw, (u8)offset+2, + &data[2]); + if (ret_val != E1000_SUCCESS) + DEBUGOUT("MAC Addr not found in iNVM\n"); + break; + case NVM_INIT_CTRL_2: + ret_val = e1000_read_invm_word_i210(hw, (u8)offset, data); + if (ret_val != E1000_SUCCESS) { + *data = NVM_INIT_CTRL_2_DEFAULT_I211; + ret_val = E1000_SUCCESS; + } + break; + case NVM_INIT_CTRL_4: + ret_val = e1000_read_invm_word_i210(hw, (u8)offset, data); + if (ret_val != E1000_SUCCESS) { + *data = NVM_INIT_CTRL_4_DEFAULT_I211; + ret_val = E1000_SUCCESS; + } + break; + case NVM_LED_1_CFG: + ret_val = e1000_read_invm_word_i210(hw, (u8)offset, data); + if (ret_val != E1000_SUCCESS) { + *data = NVM_LED_1_CFG_DEFAULT_I211; + ret_val = E1000_SUCCESS; + } + break; + case NVM_LED_0_2_CFG: + ret_val = e1000_read_invm_word_i210(hw, (u8)offset, data); + if (ret_val != E1000_SUCCESS) { + *data = NVM_LED_0_2_CFG_DEFAULT_I211; + ret_val = E1000_SUCCESS; + } + break; + case NVM_ID_LED_SETTINGS: + ret_val = e1000_read_invm_word_i210(hw, (u8)offset, data); + if (ret_val != E1000_SUCCESS) { + *data = ID_LED_RESERVED_FFFF; + ret_val = E1000_SUCCESS; + } + break; + case NVM_SUB_DEV_ID: + *data = hw->subsystem_device_id; + break; + case NVM_SUB_VEN_ID: + *data = hw->subsystem_vendor_id; + break; + case NVM_DEV_ID: + *data = hw->device_id; + break; + case NVM_VEN_ID: + *data = hw->vendor_id; + break; + default: + DEBUGOUT1("NVM word 0x%02x is not mapped.\n", offset); + *data = NVM_RESERVED_WORD; + break; + } + return ret_val; +} + +/** + * e1000_read_invm_version - Reads iNVM version and image type + * @hw: pointer to the HW structure + * @invm_ver: version structure for the version read + * + * Reads iNVM version and image type. + **/ +s32 e1000_read_invm_version(struct e1000_hw *hw, + struct e1000_fw_version *invm_ver) +{ + u32 *record = NULL; + u32 *next_record = NULL; + u32 i = 0; + u32 invm_dword = 0; + u32 invm_blocks = E1000_INVM_SIZE - (E1000_INVM_ULT_BYTES_SIZE / + E1000_INVM_RECORD_SIZE_IN_BYTES); + u32 buffer[E1000_INVM_SIZE]; + s32 status = -E1000_ERR_INVM_VALUE_NOT_FOUND; + u16 version = 0; + + DEBUGFUNC("e1000_read_invm_version"); + + /* Read iNVM memory */ + for (i = 0; i < E1000_INVM_SIZE; i++) { + invm_dword = E1000_READ_REG(hw, E1000_INVM_DATA_REG(i)); + buffer[i] = invm_dword; + } + + /* Read version number */ + for (i = 1; i < invm_blocks; i++) { + record = &buffer[invm_blocks - i]; + next_record = &buffer[invm_blocks - i + 1]; + + /* Check if we have first version location used */ + if ((i == 1) && ((*record & E1000_INVM_VER_FIELD_ONE) == 0)) { + version = 0; + status = E1000_SUCCESS; + break; + } + /* Check if we have second version location used */ + else if ((i == 1) && + ((*record & E1000_INVM_VER_FIELD_TWO) == 0)) { + version = (*record & E1000_INVM_VER_FIELD_ONE) >> 3; + status = E1000_SUCCESS; + break; + } + /* + * Check if we have odd version location + * used and it is the last one used + */ + else if ((((*record & E1000_INVM_VER_FIELD_ONE) == 0) && + ((*record & 0x3) == 0)) || (((*record & 0x3) != 0) && + (i != 1))) { + version = (*next_record & E1000_INVM_VER_FIELD_TWO) + >> 13; + status = E1000_SUCCESS; + break; + } + /* + * Check if we have even version location + * used and it is the last one used + */ + else if (((*record & E1000_INVM_VER_FIELD_TWO) == 0) && + ((*record & 0x3) == 0)) { + version = (*record & E1000_INVM_VER_FIELD_ONE) >> 3; + status = E1000_SUCCESS; + break; + } + } + + if (status == E1000_SUCCESS) { + invm_ver->invm_major = (version & E1000_INVM_MAJOR_MASK) + >> E1000_INVM_MAJOR_SHIFT; + invm_ver->invm_minor = version & E1000_INVM_MINOR_MASK; + } + /* Read Image Type */ + for (i = 1; i < invm_blocks; i++) { + record = &buffer[invm_blocks - i]; + next_record = &buffer[invm_blocks - i + 1]; + + /* Check if we have image type in first location used */ + if ((i == 1) && ((*record & E1000_INVM_IMGTYPE_FIELD) == 0)) { + invm_ver->invm_img_type = 0; + status = E1000_SUCCESS; + break; + } + /* Check if we have image type in first location used */ + else if ((((*record & 0x3) == 0) && + ((*record & E1000_INVM_IMGTYPE_FIELD) == 0)) || + ((((*record & 0x3) != 0) && (i != 1)))) { + invm_ver->invm_img_type = + (*next_record & E1000_INVM_IMGTYPE_FIELD) >> 23; + status = E1000_SUCCESS; + break; + } + } + return status; +} + +/** + * e1000_validate_nvm_checksum_i210 - Validate EEPROM checksum + * @hw: pointer to the HW structure + * + * Calculates the EEPROM checksum by reading/adding each word of the EEPROM + * and then verifies that the sum of the EEPROM is equal to 0xBABA. + **/ +s32 e1000_validate_nvm_checksum_i210(struct e1000_hw *hw) +{ + s32 status = E1000_SUCCESS; + s32 (*read_op_ptr)(struct e1000_hw *, u16, u16, u16 *); + + DEBUGFUNC("e1000_validate_nvm_checksum_i210"); + + if (hw->nvm.ops.acquire(hw) == E1000_SUCCESS) { + + /* + * Replace the read function with semaphore grabbing with + * the one that skips this for a while. + * We have semaphore taken already here. + */ + read_op_ptr = hw->nvm.ops.read; + hw->nvm.ops.read = e1000_read_nvm_eerd; + + status = e1000_validate_nvm_checksum_generic(hw); + + /* Revert original read operation. */ + hw->nvm.ops.read = read_op_ptr; + + hw->nvm.ops.release(hw); + } else { + status = E1000_ERR_SWFW_SYNC; + } + + return status; +} + + +/** + * e1000_update_nvm_checksum_i210 - Update EEPROM checksum + * @hw: pointer to the HW structure + * + * Updates the EEPROM checksum by reading/adding each word of the EEPROM + * up to the checksum. Then calculates the EEPROM checksum and writes the + * value to the EEPROM. Next commit EEPROM data onto the Flash. + **/ +s32 e1000_update_nvm_checksum_i210(struct e1000_hw *hw) +{ + s32 ret_val; + u16 checksum = 0; + u16 i, nvm_data; + + DEBUGFUNC("e1000_update_nvm_checksum_i210"); + + /* + * Read the first word from the EEPROM. If this times out or fails, do + * not continue or we could be in for a very long wait while every + * EEPROM read fails + */ + ret_val = e1000_read_nvm_eerd(hw, 0, 1, &nvm_data); + if (ret_val != E1000_SUCCESS) { + DEBUGOUT("EEPROM read failed\n"); + goto out; + } + + if (hw->nvm.ops.acquire(hw) == E1000_SUCCESS) { + /* + * Do not use hw->nvm.ops.write, hw->nvm.ops.read + * because we do not want to take the synchronization + * semaphores twice here. + */ + + for (i = 0; i < NVM_CHECKSUM_REG; i++) { + ret_val = e1000_read_nvm_eerd(hw, i, 1, &nvm_data); + if (ret_val) { + hw->nvm.ops.release(hw); + DEBUGOUT("NVM Read Error while updating checksum.\n"); + goto out; + } + checksum += nvm_data; + } + checksum = (u16) NVM_SUM - checksum; + ret_val = e1000_write_nvm_srwr(hw, NVM_CHECKSUM_REG, 1, + &checksum); + if (ret_val != E1000_SUCCESS) { + hw->nvm.ops.release(hw); + DEBUGOUT("NVM Write Error while updating checksum.\n"); + goto out; + } + + hw->nvm.ops.release(hw); + + ret_val = e1000_update_flash_i210(hw); + } else { + ret_val = E1000_ERR_SWFW_SYNC; + } +out: + return ret_val; +} + +/** + * e1000_get_flash_presence_i210 - Check if flash device is detected. + * @hw: pointer to the HW structure + * + **/ +bool e1000_get_flash_presence_i210(struct e1000_hw *hw) +{ + u32 eec = 0; + bool ret_val = false; + + DEBUGFUNC("e1000_get_flash_presence_i210"); + + eec = E1000_READ_REG(hw, E1000_EECD); + + if (eec & E1000_EECD_FLASH_DETECTED_I210) + ret_val = true; + + return ret_val; +} + +/** + * e1000_update_flash_i210 - Commit EEPROM to the flash + * @hw: pointer to the HW structure + * + **/ +s32 e1000_update_flash_i210(struct e1000_hw *hw) +{ + s32 ret_val; + u32 flup; + + DEBUGFUNC("e1000_update_flash_i210"); + + ret_val = e1000_pool_flash_update_done_i210(hw); + if (ret_val == -E1000_ERR_NVM) { + DEBUGOUT("Flash update time out\n"); + goto out; + } + + flup = E1000_READ_REG(hw, E1000_EECD) | E1000_EECD_FLUPD_I210; + E1000_WRITE_REG(hw, E1000_EECD, flup); + + ret_val = e1000_pool_flash_update_done_i210(hw); + if (ret_val == E1000_SUCCESS) + DEBUGOUT("Flash update complete\n"); + else + DEBUGOUT("Flash update time out\n"); + +out: + return ret_val; +} + +/** + * e1000_pool_flash_update_done_i210 - Pool FLUDONE status. + * @hw: pointer to the HW structure + * + **/ +s32 e1000_pool_flash_update_done_i210(struct e1000_hw *hw) +{ + s32 ret_val = -E1000_ERR_NVM; + u32 i, reg; + + DEBUGFUNC("e1000_pool_flash_update_done_i210"); + + for (i = 0; i < E1000_FLUDONE_ATTEMPTS; i++) { + reg = E1000_READ_REG(hw, E1000_EECD); + if (reg & E1000_EECD_FLUDONE_I210) { + ret_val = E1000_SUCCESS; + break; + } + usec_delay(5); + } + + return ret_val; +} + +/** + * e1000_init_nvm_params_i210 - Initialize i210 NVM function pointers + * @hw: pointer to the HW structure + * + * Initialize the i210/i211 NVM parameters and function pointers. + **/ +static s32 e1000_init_nvm_params_i210(struct e1000_hw *hw) +{ + s32 ret_val; + struct e1000_nvm_info *nvm = &hw->nvm; + + DEBUGFUNC("e1000_init_nvm_params_i210"); + + ret_val = e1000_init_nvm_params_82575(hw); + nvm->ops.acquire = e1000_acquire_nvm_i210; + nvm->ops.release = e1000_release_nvm_i210; + nvm->ops.valid_led_default = e1000_valid_led_default_i210; + if (e1000_get_flash_presence_i210(hw)) { + hw->nvm.type = e1000_nvm_flash_hw; + nvm->ops.read = e1000_read_nvm_srrd_i210; + nvm->ops.write = e1000_write_nvm_srwr_i210; + nvm->ops.validate = e1000_validate_nvm_checksum_i210; + nvm->ops.update = e1000_update_nvm_checksum_i210; + } else { + hw->nvm.type = e1000_nvm_invm; + nvm->ops.read = e1000_read_invm_i210; + nvm->ops.write = e1000_null_write_nvm; + nvm->ops.validate = e1000_null_ops_generic; + nvm->ops.update = e1000_null_ops_generic; + } + return ret_val; +} + +/** + * e1000_init_function_pointers_i210 - Init func ptrs. + * @hw: pointer to the HW structure + * + * Called to initialize all function pointers and parameters. + **/ +void e1000_init_function_pointers_i210(struct e1000_hw *hw) +{ + e1000_init_function_pointers_82575(hw); + hw->nvm.ops.init_params = e1000_init_nvm_params_i210; + + return; +} + +/** + * e1000_valid_led_default_i210 - Verify a valid default LED config + * @hw: pointer to the HW structure + * @data: pointer to the NVM (EEPROM) + * + * Read the EEPROM for the current default LED configuration. If the + * LED configuration is not valid, set to a valid LED configuration. + **/ +static s32 e1000_valid_led_default_i210(struct e1000_hw *hw, u16 *data) +{ + s32 ret_val; + + DEBUGFUNC("e1000_valid_led_default_i210"); + + ret_val = hw->nvm.ops.read(hw, NVM_ID_LED_SETTINGS, 1, data); + if (ret_val) { + DEBUGOUT("NVM Read Error\n"); + goto out; + } + + if (*data == ID_LED_RESERVED_0000 || *data == ID_LED_RESERVED_FFFF) { + switch (hw->phy.media_type) { + case e1000_media_type_internal_serdes: + *data = ID_LED_DEFAULT_I210_SERDES; + break; + case e1000_media_type_copper: + default: + *data = ID_LED_DEFAULT_I210; + break; + } + } +out: + return ret_val; +} + +/** + * __e1000_access_xmdio_reg - Read/write XMDIO register + * @hw: pointer to the HW structure + * @address: XMDIO address to program + * @dev_addr: device address to program + * @data: pointer to value to read/write from/to the XMDIO address + * @read: boolean flag to indicate read or write + **/ +static s32 __e1000_access_xmdio_reg(struct e1000_hw *hw, u16 address, + u8 dev_addr, u16 *data, bool read) +{ + s32 ret_val; + + DEBUGFUNC("__e1000_access_xmdio_reg"); + + ret_val = hw->phy.ops.write_reg(hw, E1000_MMDAC, dev_addr); + if (ret_val) + return ret_val; + + ret_val = hw->phy.ops.write_reg(hw, E1000_MMDAAD, address); + if (ret_val) + return ret_val; + + ret_val = hw->phy.ops.write_reg(hw, E1000_MMDAC, E1000_MMDAC_FUNC_DATA | + dev_addr); + if (ret_val) + return ret_val; + + if (read) + ret_val = hw->phy.ops.read_reg(hw, E1000_MMDAAD, data); + else + ret_val = hw->phy.ops.write_reg(hw, E1000_MMDAAD, *data); + if (ret_val) + return ret_val; + + /* Recalibrate the device back to 0 */ + ret_val = hw->phy.ops.write_reg(hw, E1000_MMDAC, 0); + if (ret_val) + return ret_val; + + return ret_val; +} + +/** + * e1000_read_xmdio_reg - Read XMDIO register + * @hw: pointer to the HW structure + * @addr: XMDIO address to program + * @dev_addr: device address to program + * @data: value to be read from the EMI address + **/ +s32 e1000_read_xmdio_reg(struct e1000_hw *hw, u16 addr, u8 dev_addr, u16 *data) +{ + DEBUGFUNC("e1000_read_xmdio_reg"); + + return __e1000_access_xmdio_reg(hw, addr, dev_addr, data, true); +} + +/** + * e1000_write_xmdio_reg - Write XMDIO register + * @hw: pointer to the HW structure + * @addr: XMDIO address to program + * @dev_addr: device address to program + * @data: value to be written to the XMDIO address + **/ +s32 e1000_write_xmdio_reg(struct e1000_hw *hw, u16 addr, u8 dev_addr, u16 data) +{ + DEBUGFUNC("e1000_read_xmdio_reg"); + + return __e1000_access_xmdio_reg(hw, addr, dev_addr, &data, false); +} + +/** + * e1000_pll_workaround_i210 + * @hw: pointer to the HW structure + * + * Works around an errata in the PLL circuit where it occasionally + * provides the wrong clock frequency after power up. + **/ +static s32 e1000_pll_workaround_i210(struct e1000_hw *hw) +{ + s32 ret_val; + u32 wuc, mdicnfg, ctrl, ctrl_ext, reg_val; + u16 nvm_word, phy_word, pci_word, tmp_nvm; + int i; + + /* Get and set needed register values */ + wuc = E1000_READ_REG(hw, E1000_WUC); + mdicnfg = E1000_READ_REG(hw, E1000_MDICNFG); + reg_val = mdicnfg & ~E1000_MDICNFG_EXT_MDIO; + E1000_WRITE_REG(hw, E1000_MDICNFG, reg_val); + + /* Get data from NVM, or set default */ + ret_val = e1000_read_invm_word_i210(hw, E1000_INVM_AUTOLOAD, + &nvm_word); + if (ret_val != E1000_SUCCESS) + nvm_word = E1000_INVM_DEFAULT_AL; + tmp_nvm = nvm_word | E1000_INVM_PLL_WO_VAL; + for (i = 0; i < E1000_MAX_PLL_TRIES; i++) { + /* check current state directly from internal PHY */ + e1000_read_phy_reg_gs40g(hw, (E1000_PHY_PLL_FREQ_PAGE | + E1000_PHY_PLL_FREQ_REG), &phy_word); + if ((phy_word & E1000_PHY_PLL_UNCONF) + != E1000_PHY_PLL_UNCONF) { + ret_val = E1000_SUCCESS; + break; + } else { + ret_val = -E1000_ERR_PHY; + } + /* directly reset the internal PHY */ + ctrl = E1000_READ_REG(hw, E1000_CTRL); + E1000_WRITE_REG(hw, E1000_CTRL, ctrl|E1000_CTRL_PHY_RST); + + ctrl_ext = E1000_READ_REG(hw, E1000_CTRL_EXT); + ctrl_ext |= (E1000_CTRL_EXT_PHYPDEN | E1000_CTRL_EXT_SDLPE); + E1000_WRITE_REG(hw, E1000_CTRL_EXT, ctrl_ext); + + E1000_WRITE_REG(hw, E1000_WUC, 0); + reg_val = (E1000_INVM_AUTOLOAD << 4) | (tmp_nvm << 16); + E1000_WRITE_REG(hw, E1000_EEARBC_I210, reg_val); + + e1000_read_pci_cfg(hw, E1000_PCI_PMCSR, &pci_word); + pci_word |= E1000_PCI_PMCSR_D3; + e1000_write_pci_cfg(hw, E1000_PCI_PMCSR, &pci_word); + msec_delay(1); + pci_word &= ~E1000_PCI_PMCSR_D3; + e1000_write_pci_cfg(hw, E1000_PCI_PMCSR, &pci_word); + reg_val = (E1000_INVM_AUTOLOAD << 4) | (nvm_word << 16); + E1000_WRITE_REG(hw, E1000_EEARBC_I210, reg_val); + + /* restore WUC register */ + E1000_WRITE_REG(hw, E1000_WUC, wuc); + } + /* restore MDICNFG setting */ + E1000_WRITE_REG(hw, E1000_MDICNFG, mdicnfg); + return ret_val; +} + +/** + * e1000_init_hw_i210 - Init hw for I210/I211 + * @hw: pointer to the HW structure + * + * Called to initialize hw for i210 hw family. + **/ +s32 e1000_init_hw_i210(struct e1000_hw *hw) +{ + s32 ret_val; + + DEBUGFUNC("e1000_init_hw_i210"); + if ((hw->mac.type >= e1000_i210) && + !(e1000_get_flash_presence_i210(hw))) { + ret_val = e1000_pll_workaround_i210(hw); + if (ret_val != E1000_SUCCESS) + return ret_val; + } + ret_val = e1000_init_hw_82575(hw); + return ret_val; +} diff --git a/drivers/staging/igb_avb/e1000_i210.h b/drivers/staging/igb_avb/e1000_i210.h new file mode 100644 index 000000000000..a14e897d26a0 --- /dev/null +++ b/drivers/staging/igb_avb/e1000_i210.h @@ -0,0 +1,101 @@ +/******************************************************************************* + + Intel(R) Gigabit Ethernet Linux driver + Copyright(c) 2007-2015 Intel Corporation. + + This program is free software; you can redistribute it and/or modify it + under the terms and conditions of the GNU General Public License, + version 2, as published by the Free Software Foundation. + + This program is distributed in the hope 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. + + The full GNU General Public License is included in this distribution in + the file called "COPYING". + + Contact Information: + Linux NICS + e1000-devel Mailing List + Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + +*******************************************************************************/ + +#ifndef _E1000_I210_H_ +#define _E1000_I210_H_ + +bool e1000_get_flash_presence_i210(struct e1000_hw *hw); +s32 e1000_update_flash_i210(struct e1000_hw *hw); +s32 e1000_update_nvm_checksum_i210(struct e1000_hw *hw); +s32 e1000_validate_nvm_checksum_i210(struct e1000_hw *hw); +s32 e1000_write_nvm_srwr_i210(struct e1000_hw *hw, u16 offset, + u16 words, u16 *data); +s32 e1000_read_nvm_srrd_i210(struct e1000_hw *hw, u16 offset, + u16 words, u16 *data); +s32 e1000_read_invm_version(struct e1000_hw *hw, + struct e1000_fw_version *invm_ver); +s32 e1000_acquire_swfw_sync_i210(struct e1000_hw *hw, u16 mask); +void e1000_release_swfw_sync_i210(struct e1000_hw *hw, u16 mask); +s32 e1000_read_xmdio_reg(struct e1000_hw *hw, u16 addr, u8 dev_addr, + u16 *data); +s32 e1000_write_xmdio_reg(struct e1000_hw *hw, u16 addr, u8 dev_addr, + u16 data); +s32 e1000_init_hw_i210(struct e1000_hw *hw); + +#define E1000_STM_OPCODE 0xDB00 +#define E1000_EEPROM_FLASH_SIZE_WORD 0x11 + +#define INVM_DWORD_TO_RECORD_TYPE(invm_dword) \ + (u8)((invm_dword) & 0x7) +#define INVM_DWORD_TO_WORD_ADDRESS(invm_dword) \ + (u8)(((invm_dword) & 0x0000FE00) >> 9) +#define INVM_DWORD_TO_WORD_DATA(invm_dword) \ + (u16)(((invm_dword) & 0xFFFF0000) >> 16) + +enum E1000_INVM_STRUCTURE_TYPE { + E1000_INVM_UNINITIALIZED_STRUCTURE = 0x00, + E1000_INVM_WORD_AUTOLOAD_STRUCTURE = 0x01, + E1000_INVM_CSR_AUTOLOAD_STRUCTURE = 0x02, + E1000_INVM_PHY_REGISTER_AUTOLOAD_STRUCTURE = 0x03, + E1000_INVM_RSA_KEY_SHA256_STRUCTURE = 0x04, + E1000_INVM_INVALIDATED_STRUCTURE = 0x0F, +}; + +#define E1000_INVM_RSA_KEY_SHA256_DATA_SIZE_IN_DWORDS 8 +#define E1000_INVM_CSR_AUTOLOAD_DATA_SIZE_IN_DWORDS 1 +#define E1000_INVM_ULT_BYTES_SIZE 8 +#define E1000_INVM_RECORD_SIZE_IN_BYTES 4 +#define E1000_INVM_VER_FIELD_ONE 0x1FF8 +#define E1000_INVM_VER_FIELD_TWO 0x7FE000 +#define E1000_INVM_IMGTYPE_FIELD 0x1F800000 + +#define E1000_INVM_MAJOR_MASK 0x3F0 +#define E1000_INVM_MINOR_MASK 0xF +#define E1000_INVM_MAJOR_SHIFT 4 + +#define ID_LED_DEFAULT_I210 ((ID_LED_OFF1_ON2 << 8) | \ + (ID_LED_DEF1_DEF2 << 4) | \ + (ID_LED_OFF1_OFF2)) +#define ID_LED_DEFAULT_I210_SERDES ((ID_LED_DEF1_DEF2 << 8) | \ + (ID_LED_DEF1_DEF2 << 4) | \ + (ID_LED_OFF1_ON2)) + +/* NVM offset defaults for I211 devices */ +#define NVM_INIT_CTRL_2_DEFAULT_I211 0X7243 +#define NVM_INIT_CTRL_4_DEFAULT_I211 0x00C1 +#define NVM_LED_1_CFG_DEFAULT_I211 0x0184 +#define NVM_LED_0_2_CFG_DEFAULT_I211 0x200C + +/* PLL Defines */ +#define E1000_PCI_PMCSR 0x44 +#define E1000_PCI_PMCSR_D3 0x03 +#define E1000_MAX_PLL_TRIES 5 +#define E1000_PHY_PLL_UNCONF 0xFF +#define E1000_PHY_PLL_FREQ_PAGE 0xFC0000 +#define E1000_PHY_PLL_FREQ_REG 0x000E +#define E1000_INVM_DEFAULT_AL 0x202F +#define E1000_INVM_AUTOLOAD 0x0A +#define E1000_INVM_PLL_WO_VAL 0x0010 + +#endif diff --git a/drivers/staging/igb_avb/e1000_mac.c b/drivers/staging/igb_avb/e1000_mac.c new file mode 100644 index 000000000000..f848b995c932 --- /dev/null +++ b/drivers/staging/igb_avb/e1000_mac.c @@ -0,0 +1,2149 @@ +/******************************************************************************* + + Intel(R) Gigabit Ethernet Linux driver + Copyright(c) 2007-2015 Intel Corporation. + + This program is free software; you can redistribute it and/or modify it + under the terms and conditions of the GNU General Public License, + version 2, as published by the Free Software Foundation. + + This program is distributed in the hope 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. + + The full GNU General Public License is included in this distribution in + the file called "COPYING". + + Contact Information: + Linux NICS + e1000-devel Mailing List + Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + +*******************************************************************************/ + +#include "e1000_api.h" + +static s32 e1000_validate_mdi_setting_generic(struct e1000_hw *hw); +static void e1000_set_lan_id_multi_port_pcie(struct e1000_hw *hw); +static void e1000_config_collision_dist_generic(struct e1000_hw *hw); +static int e1000_rar_set_generic(struct e1000_hw *hw, u8 *addr, u32 index); + +/** + * e1000_init_mac_ops_generic - Initialize MAC function pointers + * @hw: pointer to the HW structure + * + * Setups up the function pointers to no-op functions + **/ +void e1000_init_mac_ops_generic(struct e1000_hw *hw) +{ + struct e1000_mac_info *mac = &hw->mac; + DEBUGFUNC("e1000_init_mac_ops_generic"); + + /* General Setup */ + mac->ops.init_params = e1000_null_ops_generic; + mac->ops.init_hw = e1000_null_ops_generic; + mac->ops.reset_hw = e1000_null_ops_generic; + mac->ops.setup_physical_interface = e1000_null_ops_generic; + mac->ops.get_bus_info = e1000_null_ops_generic; + mac->ops.set_lan_id = e1000_set_lan_id_multi_port_pcie; + mac->ops.read_mac_addr = e1000_read_mac_addr_generic; + mac->ops.config_collision_dist = e1000_config_collision_dist_generic; + mac->ops.clear_hw_cntrs = e1000_null_mac_generic; + /* LED */ + mac->ops.cleanup_led = e1000_null_ops_generic; + mac->ops.setup_led = e1000_null_ops_generic; + mac->ops.blink_led = e1000_null_ops_generic; + mac->ops.led_on = e1000_null_ops_generic; + mac->ops.led_off = e1000_null_ops_generic; + /* LINK */ + mac->ops.setup_link = e1000_null_ops_generic; + mac->ops.get_link_up_info = e1000_null_link_info; + mac->ops.check_for_link = e1000_null_ops_generic; + /* Management */ + mac->ops.check_mng_mode = e1000_null_mng_mode; + /* VLAN, MC, etc. */ + mac->ops.update_mc_addr_list = e1000_null_update_mc; + mac->ops.clear_vfta = e1000_null_mac_generic; + mac->ops.write_vfta = e1000_null_write_vfta; + mac->ops.rar_set = e1000_rar_set_generic; + mac->ops.validate_mdi_setting = e1000_validate_mdi_setting_generic; +} + +/** + * e1000_null_ops_generic - No-op function, returns 0 + * @hw: pointer to the HW structure + **/ +s32 e1000_null_ops_generic(struct e1000_hw E1000_UNUSEDARG *hw) +{ + DEBUGFUNC("e1000_null_ops_generic"); + return E1000_SUCCESS; +} + +/** + * e1000_null_mac_generic - No-op function, return void + * @hw: pointer to the HW structure + **/ +void e1000_null_mac_generic(struct e1000_hw E1000_UNUSEDARG *hw) +{ + DEBUGFUNC("e1000_null_mac_generic"); + return; +} + +/** + * e1000_null_link_info - No-op function, return 0 + * @hw: pointer to the HW structure + **/ +s32 e1000_null_link_info(struct e1000_hw E1000_UNUSEDARG *hw, + u16 E1000_UNUSEDARG *s, u16 E1000_UNUSEDARG *d) +{ + DEBUGFUNC("e1000_null_link_info"); + return E1000_SUCCESS; +} + +/** + * e1000_null_mng_mode - No-op function, return false + * @hw: pointer to the HW structure + **/ +bool e1000_null_mng_mode(struct e1000_hw E1000_UNUSEDARG *hw) +{ + DEBUGFUNC("e1000_null_mng_mode"); + return false; +} + +/** + * e1000_null_update_mc - No-op function, return void + * @hw: pointer to the HW structure + **/ +void e1000_null_update_mc(struct e1000_hw E1000_UNUSEDARG *hw, + u8 E1000_UNUSEDARG *h, u32 E1000_UNUSEDARG a) +{ + DEBUGFUNC("e1000_null_update_mc"); + return; +} + +/** + * e1000_null_write_vfta - No-op function, return void + * @hw: pointer to the HW structure + **/ +void e1000_null_write_vfta(struct e1000_hw E1000_UNUSEDARG *hw, + u32 E1000_UNUSEDARG a, u32 E1000_UNUSEDARG b) +{ + DEBUGFUNC("e1000_null_write_vfta"); + return; +} + +/** + * e1000_null_rar_set - No-op function, return 0 + * @hw: pointer to the HW structure + **/ +int e1000_null_rar_set(struct e1000_hw E1000_UNUSEDARG *hw, + u8 E1000_UNUSEDARG *h, u32 E1000_UNUSEDARG a) +{ + DEBUGFUNC("e1000_null_rar_set"); + return E1000_SUCCESS; +} + +/** + * e1000_get_bus_info_pcie_generic - Get PCIe bus information + * @hw: pointer to the HW structure + * + * Determines and stores the system bus information for a particular + * network interface. The following bus information is determined and stored: + * bus speed, bus width, type (PCIe), and PCIe function. + **/ +s32 e1000_get_bus_info_pcie_generic(struct e1000_hw *hw) +{ + struct e1000_mac_info *mac = &hw->mac; + struct e1000_bus_info *bus = &hw->bus; + s32 ret_val; + u16 pcie_link_status; + + DEBUGFUNC("e1000_get_bus_info_pcie_generic"); + + bus->type = e1000_bus_type_pci_express; + + ret_val = e1000_read_pcie_cap_reg(hw, PCIE_LINK_STATUS, + &pcie_link_status); + if (ret_val) { + bus->width = e1000_bus_width_unknown; + bus->speed = e1000_bus_speed_unknown; + } else { + switch (pcie_link_status & PCIE_LINK_SPEED_MASK) { + case PCIE_LINK_SPEED_2500: + bus->speed = e1000_bus_speed_2500; + break; + case PCIE_LINK_SPEED_5000: + bus->speed = e1000_bus_speed_5000; + break; + default: + bus->speed = e1000_bus_speed_unknown; + break; + } + + bus->width = (enum e1000_bus_width)((pcie_link_status & + PCIE_LINK_WIDTH_MASK) >> PCIE_LINK_WIDTH_SHIFT); + } + + mac->ops.set_lan_id(hw); + + return E1000_SUCCESS; +} + +/** + * e1000_set_lan_id_multi_port_pcie - Set LAN id for PCIe multiple port devices + * + * @hw: pointer to the HW structure + * + * Determines the LAN function id by reading memory-mapped registers + * and swaps the port value if requested. + **/ +static void e1000_set_lan_id_multi_port_pcie(struct e1000_hw *hw) +{ + struct e1000_bus_info *bus = &hw->bus; + u32 reg; + + /* The status register reports the correct function number + * for the device regardless of function swap state. + */ + reg = E1000_READ_REG(hw, E1000_STATUS); + bus->func = (reg & E1000_STATUS_FUNC_MASK) >> E1000_STATUS_FUNC_SHIFT; +} + +/** + * e1000_set_lan_id_single_port - Set LAN id for a single port device + * @hw: pointer to the HW structure + * + * Sets the LAN function id to zero for a single port device. + **/ +void e1000_set_lan_id_single_port(struct e1000_hw *hw) +{ + struct e1000_bus_info *bus = &hw->bus; + + bus->func = 0; +} + +/** + * e1000_clear_vfta_generic - Clear VLAN filter table + * @hw: pointer to the HW structure + * + * Clears the register array which contains the VLAN filter table by + * setting all the values to 0. + **/ +void e1000_clear_vfta_generic(struct e1000_hw *hw) +{ + u32 offset; + + DEBUGFUNC("e1000_clear_vfta_generic"); + + for (offset = 0; offset < E1000_VLAN_FILTER_TBL_SIZE; offset++) { + E1000_WRITE_REG_ARRAY(hw, E1000_VFTA, offset, 0); + E1000_WRITE_FLUSH(hw); + } +} + +/** + * e1000_write_vfta_generic - Write value to VLAN filter table + * @hw: pointer to the HW structure + * @offset: register offset in VLAN filter table + * @value: register value written to VLAN filter table + * + * Writes value at the given offset in the register array which stores + * the VLAN filter table. + **/ +void e1000_write_vfta_generic(struct e1000_hw *hw, u32 offset, u32 value) +{ + DEBUGFUNC("e1000_write_vfta_generic"); + + E1000_WRITE_REG_ARRAY(hw, E1000_VFTA, offset, value); + E1000_WRITE_FLUSH(hw); +} + +/** + * e1000_init_rx_addrs_generic - Initialize receive address's + * @hw: pointer to the HW structure + * @rar_count: receive address registers + * + * Setup the receive address registers by setting the base receive address + * register to the devices MAC address and clearing all the other receive + * address registers to 0. + **/ +void e1000_init_rx_addrs_generic(struct e1000_hw *hw, u16 rar_count) +{ + u32 i; + u8 mac_addr[ETH_ADDR_LEN] = {0}; + + DEBUGFUNC("e1000_init_rx_addrs_generic"); + + /* Setup the receive address */ + DEBUGOUT("Programming MAC Address into RAR[0]\n"); + + hw->mac.ops.rar_set(hw, hw->mac.addr, 0); + + /* Zero out the other (rar_entry_count - 1) receive addresses */ + DEBUGOUT1("Clearing RAR[1-%u]\n", rar_count-1); + for (i = 1; i < rar_count; i++) + hw->mac.ops.rar_set(hw, mac_addr, i); +} + +/** + * e1000_check_alt_mac_addr_generic - Check for alternate MAC addr + * @hw: pointer to the HW structure + * + * Checks the nvm for an alternate MAC address. An alternate MAC address + * can be setup by pre-boot software and must be treated like a permanent + * address and must override the actual permanent MAC address. If an + * alternate MAC address is found it is programmed into RAR0, replacing + * the permanent address that was installed into RAR0 by the Si on reset. + * This function will return SUCCESS unless it encounters an error while + * reading the EEPROM. + **/ +s32 e1000_check_alt_mac_addr_generic(struct e1000_hw *hw) +{ + u32 i; + s32 ret_val; + u16 offset, nvm_alt_mac_addr_offset, nvm_data; + u8 alt_mac_addr[ETH_ADDR_LEN]; + + DEBUGFUNC("e1000_check_alt_mac_addr_generic"); + + ret_val = hw->nvm.ops.read(hw, NVM_COMPAT, 1, &nvm_data); + if (ret_val) + return ret_val; + + /* Alternate MAC address is handled by the option ROM for 82580 + * and newer. SW support not required. + */ + if (hw->mac.type >= e1000_82580) + return E1000_SUCCESS; + + ret_val = hw->nvm.ops.read(hw, NVM_ALT_MAC_ADDR_PTR, 1, + &nvm_alt_mac_addr_offset); + if (ret_val) { + DEBUGOUT("NVM Read Error\n"); + return ret_val; + } + + if ((nvm_alt_mac_addr_offset == 0xFFFF) || + (nvm_alt_mac_addr_offset == 0x0000)) + /* There is no Alternate MAC Address */ + return E1000_SUCCESS; + + if (hw->bus.func == E1000_FUNC_1) + nvm_alt_mac_addr_offset += E1000_ALT_MAC_ADDRESS_OFFSET_LAN1; + if (hw->bus.func == E1000_FUNC_2) + nvm_alt_mac_addr_offset += E1000_ALT_MAC_ADDRESS_OFFSET_LAN2; + + if (hw->bus.func == E1000_FUNC_3) + nvm_alt_mac_addr_offset += E1000_ALT_MAC_ADDRESS_OFFSET_LAN3; + for (i = 0; i < ETH_ADDR_LEN; i += 2) { + offset = nvm_alt_mac_addr_offset + (i >> 1); + ret_val = hw->nvm.ops.read(hw, offset, 1, &nvm_data); + if (ret_val) { + DEBUGOUT("NVM Read Error\n"); + return ret_val; + } + + alt_mac_addr[i] = (u8)(nvm_data & 0xFF); + alt_mac_addr[i + 1] = (u8)(nvm_data >> 8); + } + + /* if multicast bit is set, the alternate address will not be used */ + if (alt_mac_addr[0] & 0x01) { + DEBUGOUT("Ignoring Alternate Mac Address with MC bit set\n"); + return E1000_SUCCESS; + } + + /* We have a valid alternate MAC address, and we want to treat it the + * same as the normal permanent MAC address stored by the HW into the + * RAR. Do this by mapping this address into RAR0. + */ + hw->mac.ops.rar_set(hw, alt_mac_addr, 0); + + return E1000_SUCCESS; +} + +/** + * e1000_rar_set_generic - Set receive address register + * @hw: pointer to the HW structure + * @addr: pointer to the receive address + * @index: receive address array register + * + * Sets the receive address array register at index to the address passed + * in by addr. + **/ +static int e1000_rar_set_generic(struct e1000_hw *hw, u8 *addr, u32 index) +{ + u32 rar_low, rar_high; + + DEBUGFUNC("e1000_rar_set_generic"); + + /* HW expects these in little endian so we reverse the byte order + * from network order (big endian) to little endian + */ + rar_low = ((u32) addr[0] | ((u32) addr[1] << 8) | + ((u32) addr[2] << 16) | ((u32) addr[3] << 24)); + + rar_high = ((u32) addr[4] | ((u32) addr[5] << 8)); + + /* If MAC address zero, no need to set the AV bit */ + if (rar_low || rar_high) + rar_high |= E1000_RAH_AV; + + /* Some bridges will combine consecutive 32-bit writes into + * a single burst write, which will malfunction on some parts. + * The flushes avoid this. + */ + E1000_WRITE_REG(hw, E1000_RAL(index), rar_low); + E1000_WRITE_FLUSH(hw); + E1000_WRITE_REG(hw, E1000_RAH(index), rar_high); + E1000_WRITE_FLUSH(hw); + + return E1000_SUCCESS; +} + +/** + * e1000_hash_mc_addr_generic - Generate a multicast hash value + * @hw: pointer to the HW structure + * @mc_addr: pointer to a multicast address + * + * Generates a multicast address hash value which is used to determine + * the multicast filter table array address and new table value. + **/ +u32 e1000_hash_mc_addr_generic(struct e1000_hw *hw, u8 *mc_addr) +{ + u32 hash_value, hash_mask; + u8 bit_shift = 0; + + DEBUGFUNC("e1000_hash_mc_addr_generic"); + + /* Register count multiplied by bits per register */ + hash_mask = (hw->mac.mta_reg_count * 32) - 1; + + /* For a mc_filter_type of 0, bit_shift is the number of left-shifts + * where 0xFF would still fall within the hash mask. + */ + while (hash_mask >> bit_shift != 0xFF) + bit_shift++; + + /* The portion of the address that is used for the hash table + * is determined by the mc_filter_type setting. + * The algorithm is such that there is a total of 8 bits of shifting. + * The bit_shift for a mc_filter_type of 0 represents the number of + * left-shifts where the MSB of mc_addr[5] would still fall within + * the hash_mask. Case 0 does this exactly. Since there are a total + * of 8 bits of shifting, then mc_addr[4] will shift right the + * remaining number of bits. Thus 8 - bit_shift. The rest of the + * cases are a variation of this algorithm...essentially raising the + * number of bits to shift mc_addr[5] left, while still keeping the + * 8-bit shifting total. + * + * For example, given the following Destination MAC Address and an + * mta register count of 128 (thus a 4096-bit vector and 0xFFF mask), + * we can see that the bit_shift for case 0 is 4. These are the hash + * values resulting from each mc_filter_type... + * [0] [1] [2] [3] [4] [5] + * 01 AA 00 12 34 56 + * LSB MSB + * + * case 0: hash_value = ((0x34 >> 4) | (0x56 << 4)) & 0xFFF = 0x563 + * case 1: hash_value = ((0x34 >> 3) | (0x56 << 5)) & 0xFFF = 0xAC6 + * case 2: hash_value = ((0x34 >> 2) | (0x56 << 6)) & 0xFFF = 0x163 + * case 3: hash_value = ((0x34 >> 0) | (0x56 << 8)) & 0xFFF = 0x634 + */ + switch (hw->mac.mc_filter_type) { + default: + case 0: + break; + case 1: + bit_shift += 1; + break; + case 2: + bit_shift += 2; + break; + case 3: + bit_shift += 4; + break; + } + + hash_value = hash_mask & (((mc_addr[4] >> (8 - bit_shift)) | + (((u16) mc_addr[5]) << bit_shift))); + + return hash_value; +} + +/** + * e1000_update_mc_addr_list_generic - Update Multicast addresses + * @hw: pointer to the HW structure + * @mc_addr_list: array of multicast addresses to program + * @mc_addr_count: number of multicast addresses to program + * + * Updates entire Multicast Table Array. + * The caller must have a packed mc_addr_list of multicast addresses. + **/ +void e1000_update_mc_addr_list_generic(struct e1000_hw *hw, + u8 *mc_addr_list, u32 mc_addr_count) +{ + u32 hash_value, hash_bit, hash_reg; + int i; + + DEBUGFUNC("e1000_update_mc_addr_list_generic"); + + /* clear mta_shadow */ + memset(&hw->mac.mta_shadow, 0, sizeof(hw->mac.mta_shadow)); + + /* update mta_shadow from mc_addr_list */ + for (i = 0; (u32) i < mc_addr_count; i++) { + hash_value = e1000_hash_mc_addr_generic(hw, mc_addr_list); + + hash_reg = (hash_value >> 5) & (hw->mac.mta_reg_count - 1); + hash_bit = hash_value & 0x1F; + + hw->mac.mta_shadow[hash_reg] |= (1 << hash_bit); + mc_addr_list += (ETH_ADDR_LEN); + } + + /* replace the entire MTA table */ + for (i = hw->mac.mta_reg_count - 1; i >= 0; i--) + E1000_WRITE_REG_ARRAY(hw, E1000_MTA, i, hw->mac.mta_shadow[i]); + E1000_WRITE_FLUSH(hw); +} + +/** + * e1000_pcix_mmrbc_workaround_generic - Fix incorrect MMRBC value + * @hw: pointer to the HW structure + * + * In certain situations, a system BIOS may report that the PCIx maximum + * memory read byte count (MMRBC) value is higher than than the actual + * value. We check the PCIx command register with the current PCIx status + * register. + **/ +void e1000_pcix_mmrbc_workaround_generic(struct e1000_hw *hw) +{ + u16 cmd_mmrbc; + u16 pcix_cmd; + u16 pcix_stat_hi_word; + u16 stat_mmrbc; + + DEBUGFUNC("e1000_pcix_mmrbc_workaround_generic"); + + /* Workaround for PCI-X issue when BIOS sets MMRBC incorrectly */ + if (hw->bus.type != e1000_bus_type_pcix) + return; + + e1000_read_pci_cfg(hw, PCIX_COMMAND_REGISTER, &pcix_cmd); + e1000_read_pci_cfg(hw, PCIX_STATUS_REGISTER_HI, &pcix_stat_hi_word); + cmd_mmrbc = (pcix_cmd & PCIX_COMMAND_MMRBC_MASK) >> + PCIX_COMMAND_MMRBC_SHIFT; + stat_mmrbc = (pcix_stat_hi_word & PCIX_STATUS_HI_MMRBC_MASK) >> + PCIX_STATUS_HI_MMRBC_SHIFT; + if (stat_mmrbc == PCIX_STATUS_HI_MMRBC_4K) + stat_mmrbc = PCIX_STATUS_HI_MMRBC_2K; + if (cmd_mmrbc > stat_mmrbc) { + pcix_cmd &= ~PCIX_COMMAND_MMRBC_MASK; + pcix_cmd |= stat_mmrbc << PCIX_COMMAND_MMRBC_SHIFT; + e1000_write_pci_cfg(hw, PCIX_COMMAND_REGISTER, &pcix_cmd); + } +} + +/** + * e1000_clear_hw_cntrs_base_generic - Clear base hardware counters + * @hw: pointer to the HW structure + * + * Clears the base hardware counters by reading the counter registers. + **/ +void e1000_clear_hw_cntrs_base_generic(struct e1000_hw *hw) +{ + DEBUGFUNC("e1000_clear_hw_cntrs_base_generic"); + + E1000_READ_REG(hw, E1000_CRCERRS); + E1000_READ_REG(hw, E1000_SYMERRS); + E1000_READ_REG(hw, E1000_MPC); + E1000_READ_REG(hw, E1000_SCC); + E1000_READ_REG(hw, E1000_ECOL); + E1000_READ_REG(hw, E1000_MCC); + E1000_READ_REG(hw, E1000_LATECOL); + E1000_READ_REG(hw, E1000_COLC); + E1000_READ_REG(hw, E1000_DC); + E1000_READ_REG(hw, E1000_SEC); + E1000_READ_REG(hw, E1000_RLEC); + E1000_READ_REG(hw, E1000_XONRXC); + E1000_READ_REG(hw, E1000_XONTXC); + E1000_READ_REG(hw, E1000_XOFFRXC); + E1000_READ_REG(hw, E1000_XOFFTXC); + E1000_READ_REG(hw, E1000_FCRUC); + E1000_READ_REG(hw, E1000_GPRC); + E1000_READ_REG(hw, E1000_BPRC); + E1000_READ_REG(hw, E1000_MPRC); + E1000_READ_REG(hw, E1000_GPTC); + E1000_READ_REG(hw, E1000_GORCL); + E1000_READ_REG(hw, E1000_GORCH); + E1000_READ_REG(hw, E1000_GOTCL); + E1000_READ_REG(hw, E1000_GOTCH); + E1000_READ_REG(hw, E1000_RNBC); + E1000_READ_REG(hw, E1000_RUC); + E1000_READ_REG(hw, E1000_RFC); + E1000_READ_REG(hw, E1000_ROC); + E1000_READ_REG(hw, E1000_RJC); + E1000_READ_REG(hw, E1000_TORL); + E1000_READ_REG(hw, E1000_TORH); + E1000_READ_REG(hw, E1000_TOTL); + E1000_READ_REG(hw, E1000_TOTH); + E1000_READ_REG(hw, E1000_TPR); + E1000_READ_REG(hw, E1000_TPT); + E1000_READ_REG(hw, E1000_MPTC); + E1000_READ_REG(hw, E1000_BPTC); +} + +/** + * e1000_check_for_copper_link_generic - Check for link (Copper) + * @hw: pointer to the HW structure + * + * Checks to see of the link status of the hardware has changed. If a + * change in link status has been detected, then we read the PHY registers + * to get the current speed/duplex if link exists. + **/ +s32 e1000_check_for_copper_link_generic(struct e1000_hw *hw) +{ + struct e1000_mac_info *mac = &hw->mac; + s32 ret_val; + bool link; + + DEBUGFUNC("e1000_check_for_copper_link"); + + /* We only want to go out to the PHY registers to see if Auto-Neg + * has completed and/or if our link status has changed. The + * get_link_status flag is set upon receiving a Link Status + * Change or Rx Sequence Error interrupt. + */ + if (!mac->get_link_status) + return E1000_SUCCESS; + + /* First we want to see if the MII Status Register reports + * link. If so, then we want to get the current speed/duplex + * of the PHY. + */ + ret_val = e1000_phy_has_link_generic(hw, 1, 0, &link); + if (ret_val) + return ret_val; + + if (!link) + return E1000_SUCCESS; /* No link detected */ + + mac->get_link_status = false; + + /* Check if there was DownShift, must be checked + * immediately after link-up + */ + e1000_check_downshift_generic(hw); + + /* If we are forcing speed/duplex, then we simply return since + * we have already determined whether we have link or not. + */ + if (!mac->autoneg) + return -E1000_ERR_CONFIG; + + /* Auto-Neg is enabled. Auto Speed Detection takes care + * of MAC speed/duplex configuration. So we only need to + * configure Collision Distance in the MAC. + */ + mac->ops.config_collision_dist(hw); + + /* Configure Flow Control now that Auto-Neg has completed. + * First, we need to restore the desired flow control + * settings because we may have had to re-autoneg with a + * different link partner. + */ + ret_val = e1000_config_fc_after_link_up_generic(hw); + if (ret_val) + DEBUGOUT("Error configuring flow control\n"); + + return ret_val; +} + +/** + * e1000_check_for_fiber_link_generic - Check for link (Fiber) + * @hw: pointer to the HW structure + * + * Checks for link up on the hardware. If link is not up and we have + * a signal, then we need to force link up. + **/ +s32 e1000_check_for_fiber_link_generic(struct e1000_hw *hw) +{ + struct e1000_mac_info *mac = &hw->mac; + u32 rxcw; + u32 ctrl; + u32 status; + s32 ret_val; + + DEBUGFUNC("e1000_check_for_fiber_link_generic"); + + ctrl = E1000_READ_REG(hw, E1000_CTRL); + status = E1000_READ_REG(hw, E1000_STATUS); + rxcw = E1000_READ_REG(hw, E1000_RXCW); + + /* If we don't have link (auto-negotiation failed or link partner + * cannot auto-negotiate), the cable is plugged in (we have signal), + * and our link partner is not trying to auto-negotiate with us (we + * are receiving idles or data), we need to force link up. We also + * need to give auto-negotiation time to complete, in case the cable + * was just plugged in. The autoneg_failed flag does this. + */ + /* (ctrl & E1000_CTRL_SWDPIN1) == 1 == have signal */ + if ((ctrl & E1000_CTRL_SWDPIN1) && !(status & E1000_STATUS_LU) && + !(rxcw & E1000_RXCW_C)) { + if (!mac->autoneg_failed) { + mac->autoneg_failed = true; + return E1000_SUCCESS; + } + DEBUGOUT("NOT Rx'ing /C/, disable AutoNeg and force link.\n"); + + /* Disable auto-negotiation in the TXCW register */ + E1000_WRITE_REG(hw, E1000_TXCW, (mac->txcw & ~E1000_TXCW_ANE)); + + /* Force link-up and also force full-duplex. */ + ctrl = E1000_READ_REG(hw, E1000_CTRL); + ctrl |= (E1000_CTRL_SLU | E1000_CTRL_FD); + E1000_WRITE_REG(hw, E1000_CTRL, ctrl); + + /* Configure Flow Control after forcing link up. */ + ret_val = e1000_config_fc_after_link_up_generic(hw); + if (ret_val) { + DEBUGOUT("Error configuring flow control\n"); + return ret_val; + } + } else if ((ctrl & E1000_CTRL_SLU) && (rxcw & E1000_RXCW_C)) { + /* If we are forcing link and we are receiving /C/ ordered + * sets, re-enable auto-negotiation in the TXCW register + * and disable forced link in the Device Control register + * in an attempt to auto-negotiate with our link partner. + */ + DEBUGOUT("Rx'ing /C/, enable AutoNeg and stop forcing link.\n"); + E1000_WRITE_REG(hw, E1000_TXCW, mac->txcw); + E1000_WRITE_REG(hw, E1000_CTRL, (ctrl & ~E1000_CTRL_SLU)); + + mac->serdes_has_link = true; + } + + return E1000_SUCCESS; +} + +/** + * e1000_check_for_serdes_link_generic - Check for link (Serdes) + * @hw: pointer to the HW structure + * + * Checks for link up on the hardware. If link is not up and we have + * a signal, then we need to force link up. + **/ +s32 e1000_check_for_serdes_link_generic(struct e1000_hw *hw) +{ + struct e1000_mac_info *mac = &hw->mac; + u32 rxcw; + u32 ctrl; + u32 status; + s32 ret_val; + + DEBUGFUNC("e1000_check_for_serdes_link_generic"); + + ctrl = E1000_READ_REG(hw, E1000_CTRL); + status = E1000_READ_REG(hw, E1000_STATUS); + rxcw = E1000_READ_REG(hw, E1000_RXCW); + + /* If we don't have link (auto-negotiation failed or link partner + * cannot auto-negotiate), and our link partner is not trying to + * auto-negotiate with us (we are receiving idles or data), + * we need to force link up. We also need to give auto-negotiation + * time to complete. + */ + /* (ctrl & E1000_CTRL_SWDPIN1) == 1 == have signal */ + if (!(status & E1000_STATUS_LU) && !(rxcw & E1000_RXCW_C)) { + if (!mac->autoneg_failed) { + mac->autoneg_failed = true; + return E1000_SUCCESS; + } + DEBUGOUT("NOT Rx'ing /C/, disable AutoNeg and force link.\n"); + + /* Disable auto-negotiation in the TXCW register */ + E1000_WRITE_REG(hw, E1000_TXCW, (mac->txcw & ~E1000_TXCW_ANE)); + + /* Force link-up and also force full-duplex. */ + ctrl = E1000_READ_REG(hw, E1000_CTRL); + ctrl |= (E1000_CTRL_SLU | E1000_CTRL_FD); + E1000_WRITE_REG(hw, E1000_CTRL, ctrl); + + /* Configure Flow Control after forcing link up. */ + ret_val = e1000_config_fc_after_link_up_generic(hw); + if (ret_val) { + DEBUGOUT("Error configuring flow control\n"); + return ret_val; + } + } else if ((ctrl & E1000_CTRL_SLU) && (rxcw & E1000_RXCW_C)) { + /* If we are forcing link and we are receiving /C/ ordered + * sets, re-enable auto-negotiation in the TXCW register + * and disable forced link in the Device Control register + * in an attempt to auto-negotiate with our link partner. + */ + DEBUGOUT("Rx'ing /C/, enable AutoNeg and stop forcing link.\n"); + E1000_WRITE_REG(hw, E1000_TXCW, mac->txcw); + E1000_WRITE_REG(hw, E1000_CTRL, (ctrl & ~E1000_CTRL_SLU)); + + mac->serdes_has_link = true; + } else if (!(E1000_TXCW_ANE & E1000_READ_REG(hw, E1000_TXCW))) { + /* If we force link for non-auto-negotiation switch, check + * link status based on MAC synchronization for internal + * serdes media type. + */ + /* SYNCH bit and IV bit are sticky. */ + usec_delay(10); + rxcw = E1000_READ_REG(hw, E1000_RXCW); + if (rxcw & E1000_RXCW_SYNCH) { + if (!(rxcw & E1000_RXCW_IV)) { + mac->serdes_has_link = true; + DEBUGOUT("SERDES: Link up - forced.\n"); + } + } else { + mac->serdes_has_link = false; + DEBUGOUT("SERDES: Link down - force failed.\n"); + } + } + + if (E1000_TXCW_ANE & E1000_READ_REG(hw, E1000_TXCW)) { + status = E1000_READ_REG(hw, E1000_STATUS); + if (status & E1000_STATUS_LU) { + /* SYNCH bit and IV bit are sticky, so reread rxcw. */ + usec_delay(10); + rxcw = E1000_READ_REG(hw, E1000_RXCW); + if (rxcw & E1000_RXCW_SYNCH) { + if (!(rxcw & E1000_RXCW_IV)) { + mac->serdes_has_link = true; + DEBUGOUT("SERDES: Link up - autoneg completed successfully.\n"); + } else { + mac->serdes_has_link = false; + DEBUGOUT("SERDES: Link down - invalid codewords detected in autoneg.\n"); + } + } else { + mac->serdes_has_link = false; + DEBUGOUT("SERDES: Link down - no sync.\n"); + } + } else { + mac->serdes_has_link = false; + DEBUGOUT("SERDES: Link down - autoneg failed\n"); + } + } + + return E1000_SUCCESS; +} + +/** + * e1000_set_default_fc_generic - Set flow control default values + * @hw: pointer to the HW structure + * + * Read the EEPROM for the default values for flow control and store the + * values. + **/ +static s32 e1000_set_default_fc_generic(struct e1000_hw *hw) +{ + s32 ret_val; + u16 nvm_data; + u16 nvm_offset = 0; + + DEBUGFUNC("e1000_set_default_fc_generic"); + + /* Read and store word 0x0F of the EEPROM. This word contains bits + * that determine the hardware's default PAUSE (flow control) mode, + * a bit that determines whether the HW defaults to enabling or + * disabling auto-negotiation, and the direction of the + * SW defined pins. If there is no SW over-ride of the flow + * control setting, then the variable hw->fc will + * be initialized based on a value in the EEPROM. + */ + if (hw->mac.type == e1000_i350) { + nvm_offset = NVM_82580_LAN_FUNC_OFFSET(hw->bus.func); + ret_val = hw->nvm.ops.read(hw, + NVM_INIT_CONTROL2_REG + + nvm_offset, + 1, &nvm_data); + } else { + ret_val = hw->nvm.ops.read(hw, + NVM_INIT_CONTROL2_REG, + 1, &nvm_data); + } + + if (ret_val) { + DEBUGOUT("NVM Read Error\n"); + return ret_val; + } + + if (!(nvm_data & NVM_WORD0F_PAUSE_MASK)) + hw->fc.requested_mode = e1000_fc_none; + else if ((nvm_data & NVM_WORD0F_PAUSE_MASK) == + NVM_WORD0F_ASM_DIR) + hw->fc.requested_mode = e1000_fc_tx_pause; + else + hw->fc.requested_mode = e1000_fc_full; + + return E1000_SUCCESS; +} + +/** + * e1000_setup_link_generic - Setup flow control and link settings + * @hw: pointer to the HW structure + * + * Determines which flow control settings to use, then configures flow + * control. Calls the appropriate media-specific link configuration + * function. Assuming the adapter has a valid link partner, a valid link + * should be established. Assumes the hardware has previously been reset + * and the transmitter and receiver are not enabled. + **/ +s32 e1000_setup_link_generic(struct e1000_hw *hw) +{ + s32 ret_val; + + DEBUGFUNC("e1000_setup_link_generic"); + + /* In the case of the phy reset being blocked, we already have a link. + * We do not need to set it up again. + */ + if (hw->phy.ops.check_reset_block && hw->phy.ops.check_reset_block(hw)) + return E1000_SUCCESS; + + /* If requested flow control is set to default, set flow control + * based on the EEPROM flow control settings. + */ + if (hw->fc.requested_mode == e1000_fc_default) { + ret_val = e1000_set_default_fc_generic(hw); + if (ret_val) + return ret_val; + } + + /* Save off the requested flow control mode for use later. Depending + * on the link partner's capabilities, we may or may not use this mode. + */ + hw->fc.current_mode = hw->fc.requested_mode; + + DEBUGOUT1("After fix-ups FlowControl is now = %x\n", + hw->fc.current_mode); + + /* Call the necessary media_type subroutine to configure the link. */ + ret_val = hw->mac.ops.setup_physical_interface(hw); + if (ret_val) + return ret_val; + + /* Initialize the flow control address, type, and PAUSE timer + * registers to their default values. This is done even if flow + * control is disabled, because it does not hurt anything to + * initialize these registers. + */ + DEBUGOUT("Initializing the Flow Control address, type and timer regs\n"); + E1000_WRITE_REG(hw, E1000_FCT, FLOW_CONTROL_TYPE); + E1000_WRITE_REG(hw, E1000_FCAH, FLOW_CONTROL_ADDRESS_HIGH); + E1000_WRITE_REG(hw, E1000_FCAL, FLOW_CONTROL_ADDRESS_LOW); + + E1000_WRITE_REG(hw, E1000_FCTTV, hw->fc.pause_time); + + return e1000_set_fc_watermarks_generic(hw); +} + +/** + * e1000_commit_fc_settings_generic - Configure flow control + * @hw: pointer to the HW structure + * + * Write the flow control settings to the Transmit Config Word Register (TXCW) + * base on the flow control settings in e1000_mac_info. + **/ +static s32 e1000_commit_fc_settings_generic(struct e1000_hw *hw) +{ + struct e1000_mac_info *mac = &hw->mac; + u32 txcw; + + DEBUGFUNC("e1000_commit_fc_settings_generic"); + + /* Check for a software override of the flow control settings, and + * setup the device accordingly. If auto-negotiation is enabled, then + * software will have to set the "PAUSE" bits to the correct value in + * the Transmit Config Word Register (TXCW) and re-start auto- + * negotiation. However, if auto-negotiation is disabled, then + * software will have to manually configure the two flow control enable + * bits in the CTRL register. + * + * The possible values of the "fc" parameter are: + * 0: Flow control is completely disabled + * 1: Rx flow control is enabled (we can receive pause frames, + * but not send pause frames). + * 2: Tx flow control is enabled (we can send pause frames but we + * do not support receiving pause frames). + * 3: Both Rx and Tx flow control (symmetric) are enabled. + */ + switch (hw->fc.current_mode) { + case e1000_fc_none: + /* Flow control completely disabled by a software over-ride. */ + txcw = (E1000_TXCW_ANE | E1000_TXCW_FD); + break; + case e1000_fc_rx_pause: + /* Rx Flow control is enabled and Tx Flow control is disabled + * by a software over-ride. Since there really isn't a way to + * advertise that we are capable of Rx Pause ONLY, we will + * advertise that we support both symmetric and asymmetric Rx + * PAUSE. Later, we will disable the adapter's ability to send + * PAUSE frames. + */ + txcw = (E1000_TXCW_ANE | E1000_TXCW_FD | E1000_TXCW_PAUSE_MASK); + break; + case e1000_fc_tx_pause: + /* Tx Flow control is enabled, and Rx Flow control is disabled, + * by a software over-ride. + */ + txcw = (E1000_TXCW_ANE | E1000_TXCW_FD | E1000_TXCW_ASM_DIR); + break; + case e1000_fc_full: + /* Flow control (both Rx and Tx) is enabled by a software + * over-ride. + */ + txcw = (E1000_TXCW_ANE | E1000_TXCW_FD | E1000_TXCW_PAUSE_MASK); + break; + default: + DEBUGOUT("Flow control param set incorrectly\n"); + return -E1000_ERR_CONFIG; + break; + } + + E1000_WRITE_REG(hw, E1000_TXCW, txcw); + mac->txcw = txcw; + + return E1000_SUCCESS; +} + +/** + * e1000_poll_fiber_serdes_link_generic - Poll for link up + * @hw: pointer to the HW structure + * + * Polls for link up by reading the status register, if link fails to come + * up with auto-negotiation, then the link is forced if a signal is detected. + **/ +static s32 e1000_poll_fiber_serdes_link_generic(struct e1000_hw *hw) +{ + struct e1000_mac_info *mac = &hw->mac; + u32 i, status; + s32 ret_val; + + DEBUGFUNC("e1000_poll_fiber_serdes_link_generic"); + + /* If we have a signal (the cable is plugged in, or assumed true for + * serdes media) then poll for a "Link-Up" indication in the Device + * Status Register. Time-out if a link isn't seen in 500 milliseconds + * seconds (Auto-negotiation should complete in less than 500 + * milliseconds even if the other end is doing it in SW). + */ + for (i = 0; i < FIBER_LINK_UP_LIMIT; i++) { + msec_delay(10); + status = E1000_READ_REG(hw, E1000_STATUS); + if (status & E1000_STATUS_LU) + break; + } + if (i == FIBER_LINK_UP_LIMIT) { + DEBUGOUT("Never got a valid link from auto-neg!!!\n"); + mac->autoneg_failed = true; + /* AutoNeg failed to achieve a link, so we'll call + * mac->check_for_link. This routine will force the + * link up if we detect a signal. This will allow us to + * communicate with non-autonegotiating link partners. + */ + ret_val = mac->ops.check_for_link(hw); + if (ret_val) { + DEBUGOUT("Error while checking for link\n"); + return ret_val; + } + mac->autoneg_failed = false; + } else { + mac->autoneg_failed = false; + DEBUGOUT("Valid Link Found\n"); + } + + return E1000_SUCCESS; +} + +/** + * e1000_setup_fiber_serdes_link_generic - Setup link for fiber/serdes + * @hw: pointer to the HW structure + * + * Configures collision distance and flow control for fiber and serdes + * links. Upon successful setup, poll for link. + **/ +s32 e1000_setup_fiber_serdes_link_generic(struct e1000_hw *hw) +{ + u32 ctrl; + s32 ret_val; + + DEBUGFUNC("e1000_setup_fiber_serdes_link_generic"); + + ctrl = E1000_READ_REG(hw, E1000_CTRL); + + /* Take the link out of reset */ + ctrl &= ~E1000_CTRL_LRST; + + hw->mac.ops.config_collision_dist(hw); + + ret_val = e1000_commit_fc_settings_generic(hw); + if (ret_val) + return ret_val; + + /* Since auto-negotiation is enabled, take the link out of reset (the + * link will be in reset, because we previously reset the chip). This + * will restart auto-negotiation. If auto-negotiation is successful + * then the link-up status bit will be set and the flow control enable + * bits (RFCE and TFCE) will be set according to their negotiated value. + */ + DEBUGOUT("Auto-negotiation enabled\n"); + + E1000_WRITE_REG(hw, E1000_CTRL, ctrl); + E1000_WRITE_FLUSH(hw); + msec_delay(1); + + /* For these adapters, the SW definable pin 1 is set when the optics + * detect a signal. If we have a signal, then poll for a "Link-Up" + * indication. + */ + if (hw->phy.media_type == e1000_media_type_internal_serdes || + (E1000_READ_REG(hw, E1000_CTRL) & E1000_CTRL_SWDPIN1)) { + ret_val = e1000_poll_fiber_serdes_link_generic(hw); + } else { + DEBUGOUT("No signal detected\n"); + } + + return ret_val; +} + +/** + * e1000_config_collision_dist_generic - Configure collision distance + * @hw: pointer to the HW structure + * + * Configures the collision distance to the default value and is used + * during link setup. + **/ +static void e1000_config_collision_dist_generic(struct e1000_hw *hw) +{ + u32 tctl; + + DEBUGFUNC("e1000_config_collision_dist_generic"); + + tctl = E1000_READ_REG(hw, E1000_TCTL); + + tctl &= ~E1000_TCTL_COLD; + tctl |= E1000_COLLISION_DISTANCE << E1000_COLD_SHIFT; + + E1000_WRITE_REG(hw, E1000_TCTL, tctl); + E1000_WRITE_FLUSH(hw); +} + +/** + * e1000_set_fc_watermarks_generic - Set flow control high/low watermarks + * @hw: pointer to the HW structure + * + * Sets the flow control high/low threshold (watermark) registers. If + * flow control XON frame transmission is enabled, then set XON frame + * transmission as well. + **/ +s32 e1000_set_fc_watermarks_generic(struct e1000_hw *hw) +{ + u32 fcrtl = 0, fcrth = 0; + + DEBUGFUNC("e1000_set_fc_watermarks_generic"); + + /* Set the flow control receive threshold registers. Normally, + * these registers will be set to a default threshold that may be + * adjusted later by the driver's runtime code. However, if the + * ability to transmit pause frames is not enabled, then these + * registers will be set to 0. + */ + if (hw->fc.current_mode & e1000_fc_tx_pause) { + /* We need to set up the Receive Threshold high and low water + * marks as well as (optionally) enabling the transmission of + * XON frames. + */ + fcrtl = hw->fc.low_water; + if (hw->fc.send_xon) + fcrtl |= E1000_FCRTL_XONE; + + fcrth = hw->fc.high_water; + } + E1000_WRITE_REG(hw, E1000_FCRTL, fcrtl); + E1000_WRITE_REG(hw, E1000_FCRTH, fcrth); + + return E1000_SUCCESS; +} + +/** + * e1000_force_mac_fc_generic - Force the MAC's flow control settings + * @hw: pointer to the HW structure + * + * Force the MAC's flow control settings. Sets the TFCE and RFCE bits in the + * device control register to reflect the adapter settings. TFCE and RFCE + * need to be explicitly set by software when a copper PHY is used because + * autonegotiation is managed by the PHY rather than the MAC. Software must + * also configure these bits when link is forced on a fiber connection. + **/ +s32 e1000_force_mac_fc_generic(struct e1000_hw *hw) +{ + u32 ctrl; + + DEBUGFUNC("e1000_force_mac_fc_generic"); + + ctrl = E1000_READ_REG(hw, E1000_CTRL); + + /* Because we didn't get link via the internal auto-negotiation + * mechanism (we either forced link or we got link via PHY + * auto-neg), we have to manually enable/disable transmit an + * receive flow control. + * + * The "Case" statement below enables/disable flow control + * according to the "hw->fc.current_mode" parameter. + * + * The possible values of the "fc" parameter are: + * 0: Flow control is completely disabled + * 1: Rx flow control is enabled (we can receive pause + * frames but not send pause frames). + * 2: Tx flow control is enabled (we can send pause frames + * frames but we do not receive pause frames). + * 3: Both Rx and Tx flow control (symmetric) is enabled. + * other: No other values should be possible at this point. + */ + DEBUGOUT1("hw->fc.current_mode = %u\n", hw->fc.current_mode); + + switch (hw->fc.current_mode) { + case e1000_fc_none: + ctrl &= (~(E1000_CTRL_TFCE | E1000_CTRL_RFCE)); + break; + case e1000_fc_rx_pause: + ctrl &= (~E1000_CTRL_TFCE); + ctrl |= E1000_CTRL_RFCE; + break; + case e1000_fc_tx_pause: + ctrl &= (~E1000_CTRL_RFCE); + ctrl |= E1000_CTRL_TFCE; + break; + case e1000_fc_full: + ctrl |= (E1000_CTRL_TFCE | E1000_CTRL_RFCE); + break; + default: + DEBUGOUT("Flow control param set incorrectly\n"); + return -E1000_ERR_CONFIG; + } + + E1000_WRITE_REG(hw, E1000_CTRL, ctrl); + + return E1000_SUCCESS; +} + +/** + * e1000_config_fc_after_link_up_generic - Configures flow control after link + * @hw: pointer to the HW structure + * + * Checks the status of auto-negotiation after link up to ensure that the + * speed and duplex were not forced. If the link needed to be forced, then + * flow control needs to be forced also. If auto-negotiation is enabled + * and did not fail, then we configure flow control based on our link + * partner. + **/ +s32 e1000_config_fc_after_link_up_generic(struct e1000_hw *hw) +{ + struct e1000_mac_info *mac = &hw->mac; + s32 ret_val = E1000_SUCCESS; + u32 pcs_status_reg, pcs_adv_reg, pcs_lp_ability_reg, pcs_ctrl_reg; + u16 mii_status_reg, mii_nway_adv_reg, mii_nway_lp_ability_reg; + u16 speed, duplex; + + DEBUGFUNC("e1000_config_fc_after_link_up_generic"); + + /* Check for the case where we have fiber media and auto-neg failed + * so we had to force link. In this case, we need to force the + * configuration of the MAC to match the "fc" parameter. + */ + if (mac->autoneg_failed) { + if (hw->phy.media_type == e1000_media_type_fiber || + hw->phy.media_type == e1000_media_type_internal_serdes) + ret_val = e1000_force_mac_fc_generic(hw); + } else { + if (hw->phy.media_type == e1000_media_type_copper) + ret_val = e1000_force_mac_fc_generic(hw); + } + + if (ret_val) { + DEBUGOUT("Error forcing flow control settings\n"); + return ret_val; + } + + /* Check for the case where we have copper media and auto-neg is + * enabled. In this case, we need to check and see if Auto-Neg + * has completed, and if so, how the PHY and link partner has + * flow control configured. + */ + if ((hw->phy.media_type == e1000_media_type_copper) && mac->autoneg) { + /* Read the MII Status Register and check to see if AutoNeg + * has completed. We read this twice because this reg has + * some "sticky" (latched) bits. + */ + ret_val = hw->phy.ops.read_reg(hw, PHY_STATUS, &mii_status_reg); + if (ret_val) + return ret_val; + ret_val = hw->phy.ops.read_reg(hw, PHY_STATUS, &mii_status_reg); + if (ret_val) + return ret_val; + + if (!(mii_status_reg & MII_SR_AUTONEG_COMPLETE)) { + DEBUGOUT("Copper PHY and Auto Neg has not completed.\n"); + return ret_val; + } + + /* The AutoNeg process has completed, so we now need to + * read both the Auto Negotiation Advertisement + * Register (Address 4) and the Auto_Negotiation Base + * Page Ability Register (Address 5) to determine how + * flow control was negotiated. + */ + ret_val = hw->phy.ops.read_reg(hw, PHY_AUTONEG_ADV, + &mii_nway_adv_reg); + if (ret_val) + return ret_val; + ret_val = hw->phy.ops.read_reg(hw, PHY_LP_ABILITY, + &mii_nway_lp_ability_reg); + if (ret_val) + return ret_val; + + /* Two bits in the Auto Negotiation Advertisement Register + * (Address 4) and two bits in the Auto Negotiation Base + * Page Ability Register (Address 5) determine flow control + * for both the PHY and the link partner. The following + * table, taken out of the IEEE 802.3ab/D6.0 dated March 25, + * 1999, describes these PAUSE resolution bits and how flow + * control is determined based upon these settings. + * NOTE: DC = Don't Care + * + * LOCAL DEVICE | LINK PARTNER + * PAUSE | ASM_DIR | PAUSE | ASM_DIR | NIC Resolution + *-------|---------|-------|---------|-------------------- + * 0 | 0 | DC | DC | e1000_fc_none + * 0 | 1 | 0 | DC | e1000_fc_none + * 0 | 1 | 1 | 0 | e1000_fc_none + * 0 | 1 | 1 | 1 | e1000_fc_tx_pause + * 1 | 0 | 0 | DC | e1000_fc_none + * 1 | DC | 1 | DC | e1000_fc_full + * 1 | 1 | 0 | 0 | e1000_fc_none + * 1 | 1 | 0 | 1 | e1000_fc_rx_pause + * + * Are both PAUSE bits set to 1? If so, this implies + * Symmetric Flow Control is enabled at both ends. The + * ASM_DIR bits are irrelevant per the spec. + * + * For Symmetric Flow Control: + * + * LOCAL DEVICE | LINK PARTNER + * PAUSE | ASM_DIR | PAUSE | ASM_DIR | Result + *-------|---------|-------|---------|-------------------- + * 1 | DC | 1 | DC | E1000_fc_full + * + */ + if ((mii_nway_adv_reg & NWAY_AR_PAUSE) && + (mii_nway_lp_ability_reg & NWAY_LPAR_PAUSE)) { + /* Now we need to check if the user selected Rx ONLY + * of pause frames. In this case, we had to advertise + * FULL flow control because we could not advertise Rx + * ONLY. Hence, we must now check to see if we need to + * turn OFF the TRANSMISSION of PAUSE frames. + */ + if (hw->fc.requested_mode == e1000_fc_full) { + hw->fc.current_mode = e1000_fc_full; + DEBUGOUT("Flow Control = FULL.\n"); + } else { + hw->fc.current_mode = e1000_fc_rx_pause; + DEBUGOUT("Flow Control = Rx PAUSE frames only.\n"); + } + } + /* For receiving PAUSE frames ONLY. + * + * LOCAL DEVICE | LINK PARTNER + * PAUSE | ASM_DIR | PAUSE | ASM_DIR | Result + *-------|---------|-------|---------|-------------------- + * 0 | 1 | 1 | 1 | e1000_fc_tx_pause + */ + else if (!(mii_nway_adv_reg & NWAY_AR_PAUSE) && + (mii_nway_adv_reg & NWAY_AR_ASM_DIR) && + (mii_nway_lp_ability_reg & NWAY_LPAR_PAUSE) && + (mii_nway_lp_ability_reg & NWAY_LPAR_ASM_DIR)) { + hw->fc.current_mode = e1000_fc_tx_pause; + DEBUGOUT("Flow Control = Tx PAUSE frames only.\n"); + } + /* For transmitting PAUSE frames ONLY. + * + * LOCAL DEVICE | LINK PARTNER + * PAUSE | ASM_DIR | PAUSE | ASM_DIR | Result + *-------|---------|-------|---------|-------------------- + * 1 | 1 | 0 | 1 | e1000_fc_rx_pause + */ + else if ((mii_nway_adv_reg & NWAY_AR_PAUSE) && + (mii_nway_adv_reg & NWAY_AR_ASM_DIR) && + !(mii_nway_lp_ability_reg & NWAY_LPAR_PAUSE) && + (mii_nway_lp_ability_reg & NWAY_LPAR_ASM_DIR)) { + hw->fc.current_mode = e1000_fc_rx_pause; + DEBUGOUT("Flow Control = Rx PAUSE frames only.\n"); + } else { + /* Per the IEEE spec, at this point flow control + * should be disabled. + */ + hw->fc.current_mode = e1000_fc_none; + DEBUGOUT("Flow Control = NONE.\n"); + } + + /* Now we need to do one last check... If we auto- + * negotiated to HALF DUPLEX, flow control should not be + * enabled per IEEE 802.3 spec. + */ + ret_val = mac->ops.get_link_up_info(hw, &speed, &duplex); + if (ret_val) { + DEBUGOUT("Error getting link speed and duplex\n"); + return ret_val; + } + + if (duplex == HALF_DUPLEX) + hw->fc.current_mode = e1000_fc_none; + + /* Now we call a subroutine to actually force the MAC + * controller to use the correct flow control settings. + */ + ret_val = e1000_force_mac_fc_generic(hw); + if (ret_val) { + DEBUGOUT("Error forcing flow control settings\n"); + return ret_val; + } + } + + /* Check for the case where we have SerDes media and auto-neg is + * enabled. In this case, we need to check and see if Auto-Neg + * has completed, and if so, how the PHY and link partner has + * flow control configured. + */ + if ((hw->phy.media_type == e1000_media_type_internal_serdes) && + mac->autoneg) { + /* Read the PCS_LSTS and check to see if AutoNeg + * has completed. + */ + pcs_status_reg = E1000_READ_REG(hw, E1000_PCS_LSTAT); + + if (!(pcs_status_reg & E1000_PCS_LSTS_AN_COMPLETE)) { + DEBUGOUT("PCS Auto Neg has not completed.\n"); + return ret_val; + } + + /* The AutoNeg process has completed, so we now need to + * read both the Auto Negotiation Advertisement + * Register (PCS_ANADV) and the Auto_Negotiation Base + * Page Ability Register (PCS_LPAB) to determine how + * flow control was negotiated. + */ + pcs_adv_reg = E1000_READ_REG(hw, E1000_PCS_ANADV); + pcs_lp_ability_reg = E1000_READ_REG(hw, E1000_PCS_LPAB); + + /* Two bits in the Auto Negotiation Advertisement Register + * (PCS_ANADV) and two bits in the Auto Negotiation Base + * Page Ability Register (PCS_LPAB) determine flow control + * for both the PHY and the link partner. The following + * table, taken out of the IEEE 802.3ab/D6.0 dated March 25, + * 1999, describes these PAUSE resolution bits and how flow + * control is determined based upon these settings. + * NOTE: DC = Don't Care + * + * LOCAL DEVICE | LINK PARTNER + * PAUSE | ASM_DIR | PAUSE | ASM_DIR | NIC Resolution + *-------|---------|-------|---------|-------------------- + * 0 | 0 | DC | DC | e1000_fc_none + * 0 | 1 | 0 | DC | e1000_fc_none + * 0 | 1 | 1 | 0 | e1000_fc_none + * 0 | 1 | 1 | 1 | e1000_fc_tx_pause + * 1 | 0 | 0 | DC | e1000_fc_none + * 1 | DC | 1 | DC | e1000_fc_full + * 1 | 1 | 0 | 0 | e1000_fc_none + * 1 | 1 | 0 | 1 | e1000_fc_rx_pause + * + * Are both PAUSE bits set to 1? If so, this implies + * Symmetric Flow Control is enabled at both ends. The + * ASM_DIR bits are irrelevant per the spec. + * + * For Symmetric Flow Control: + * + * LOCAL DEVICE | LINK PARTNER + * PAUSE | ASM_DIR | PAUSE | ASM_DIR | Result + *-------|---------|-------|---------|-------------------- + * 1 | DC | 1 | DC | e1000_fc_full + * + */ + if ((pcs_adv_reg & E1000_TXCW_PAUSE) && + (pcs_lp_ability_reg & E1000_TXCW_PAUSE)) { + /* Now we need to check if the user selected Rx ONLY + * of pause frames. In this case, we had to advertise + * FULL flow control because we could not advertise Rx + * ONLY. Hence, we must now check to see if we need to + * turn OFF the TRANSMISSION of PAUSE frames. + */ + if (hw->fc.requested_mode == e1000_fc_full) { + hw->fc.current_mode = e1000_fc_full; + DEBUGOUT("Flow Control = FULL.\n"); + } else { + hw->fc.current_mode = e1000_fc_rx_pause; + DEBUGOUT("Flow Control = Rx PAUSE frames only.\n"); + } + } + /* For receiving PAUSE frames ONLY. + * + * LOCAL DEVICE | LINK PARTNER + * PAUSE | ASM_DIR | PAUSE | ASM_DIR | Result + *-------|---------|-------|---------|-------------------- + * 0 | 1 | 1 | 1 | e1000_fc_tx_pause + */ + else if (!(pcs_adv_reg & E1000_TXCW_PAUSE) && + (pcs_adv_reg & E1000_TXCW_ASM_DIR) && + (pcs_lp_ability_reg & E1000_TXCW_PAUSE) && + (pcs_lp_ability_reg & E1000_TXCW_ASM_DIR)) { + hw->fc.current_mode = e1000_fc_tx_pause; + DEBUGOUT("Flow Control = Tx PAUSE frames only.\n"); + } + /* For transmitting PAUSE frames ONLY. + * + * LOCAL DEVICE | LINK PARTNER + * PAUSE | ASM_DIR | PAUSE | ASM_DIR | Result + *-------|---------|-------|---------|-------------------- + * 1 | 1 | 0 | 1 | e1000_fc_rx_pause + */ + else if ((pcs_adv_reg & E1000_TXCW_PAUSE) && + (pcs_adv_reg & E1000_TXCW_ASM_DIR) && + !(pcs_lp_ability_reg & E1000_TXCW_PAUSE) && + (pcs_lp_ability_reg & E1000_TXCW_ASM_DIR)) { + hw->fc.current_mode = e1000_fc_rx_pause; + DEBUGOUT("Flow Control = Rx PAUSE frames only.\n"); + } else { + /* Per the IEEE spec, at this point flow control + * should be disabled. + */ + hw->fc.current_mode = e1000_fc_none; + DEBUGOUT("Flow Control = NONE.\n"); + } + + /* Now we call a subroutine to actually force the MAC + * controller to use the correct flow control settings. + */ + pcs_ctrl_reg = E1000_READ_REG(hw, E1000_PCS_LCTL); + pcs_ctrl_reg |= E1000_PCS_LCTL_FORCE_FCTRL; + E1000_WRITE_REG(hw, E1000_PCS_LCTL, pcs_ctrl_reg); + + ret_val = e1000_force_mac_fc_generic(hw); + if (ret_val) { + DEBUGOUT("Error forcing flow control settings\n"); + return ret_val; + } + } + + return E1000_SUCCESS; +} + +/** + * e1000_get_speed_and_duplex_copper_generic - Retrieve current speed/duplex + * @hw: pointer to the HW structure + * @speed: stores the current speed + * @duplex: stores the current duplex + * + * Read the status register for the current speed/duplex and store the current + * speed and duplex for copper connections. + **/ +s32 e1000_get_speed_and_duplex_copper_generic(struct e1000_hw *hw, u16 *speed, + u16 *duplex) +{ + u32 status; + + DEBUGFUNC("e1000_get_speed_and_duplex_copper_generic"); + + status = E1000_READ_REG(hw, E1000_STATUS); + if (status & E1000_STATUS_SPEED_1000) { + *speed = SPEED_1000; + DEBUGOUT("1000 Mbs, "); + } else if (status & E1000_STATUS_SPEED_100) { + *speed = SPEED_100; + DEBUGOUT("100 Mbs, "); + } else { + *speed = SPEED_10; + DEBUGOUT("10 Mbs, "); + } + + if (status & E1000_STATUS_FD) { + *duplex = FULL_DUPLEX; + DEBUGOUT("Full Duplex\n"); + } else { + *duplex = HALF_DUPLEX; + DEBUGOUT("Half Duplex\n"); + } + + return E1000_SUCCESS; +} + +/** + * e1000_get_speed_and_duplex_fiber_generic - Retrieve current speed/duplex + * @hw: pointer to the HW structure + * @speed: stores the current speed + * @duplex: stores the current duplex + * + * Sets the speed and duplex to gigabit full duplex (the only possible option) + * for fiber/serdes links. + **/ +s32 e1000_get_speed_and_duplex_fiber_serdes_generic(struct e1000_hw E1000_UNUSEDARG *hw, + u16 *speed, u16 *duplex) +{ + DEBUGFUNC("e1000_get_speed_and_duplex_fiber_serdes_generic"); + + *speed = SPEED_1000; + *duplex = FULL_DUPLEX; + + return E1000_SUCCESS; +} + +/** + * e1000_get_hw_semaphore_generic - Acquire hardware semaphore + * @hw: pointer to the HW structure + * + * Acquire the HW semaphore to access the PHY or NVM + **/ +s32 e1000_get_hw_semaphore_generic(struct e1000_hw *hw) +{ + u32 swsm; + s32 timeout = hw->nvm.word_size + 1; + s32 i = 0; + + DEBUGFUNC("e1000_get_hw_semaphore_generic"); + + /* Get the SW semaphore */ + while (i < timeout) { + swsm = E1000_READ_REG(hw, E1000_SWSM); + if (!(swsm & E1000_SWSM_SMBI)) + break; + + usec_delay(50); + i++; + } + + if (i == timeout) { + DEBUGOUT("Driver can't access device - SMBI bit is set.\n"); + return -E1000_ERR_NVM; + } + + /* Get the FW semaphore. */ + for (i = 0; i < timeout; i++) { + swsm = E1000_READ_REG(hw, E1000_SWSM); + E1000_WRITE_REG(hw, E1000_SWSM, swsm | E1000_SWSM_SWESMBI); + + /* Semaphore acquired if bit latched */ + if (E1000_READ_REG(hw, E1000_SWSM) & E1000_SWSM_SWESMBI) + break; + + usec_delay(50); + } + + if (i == timeout) { + /* Release semaphores */ + e1000_put_hw_semaphore_generic(hw); + DEBUGOUT("Driver can't access the NVM\n"); + return -E1000_ERR_NVM; + } + + return E1000_SUCCESS; +} + +/** + * e1000_put_hw_semaphore_generic - Release hardware semaphore + * @hw: pointer to the HW structure + * + * Release hardware semaphore used to access the PHY or NVM + **/ +void e1000_put_hw_semaphore_generic(struct e1000_hw *hw) +{ + u32 swsm; + + DEBUGFUNC("e1000_put_hw_semaphore_generic"); + + swsm = E1000_READ_REG(hw, E1000_SWSM); + + swsm &= ~(E1000_SWSM_SMBI | E1000_SWSM_SWESMBI); + + E1000_WRITE_REG(hw, E1000_SWSM, swsm); +} + +/** + * e1000_get_auto_rd_done_generic - Check for auto read completion + * @hw: pointer to the HW structure + * + * Check EEPROM for Auto Read done bit. + **/ +s32 e1000_get_auto_rd_done_generic(struct e1000_hw *hw) +{ + s32 i = 0; + + DEBUGFUNC("e1000_get_auto_rd_done_generic"); + + while (i < AUTO_READ_DONE_TIMEOUT) { + if (E1000_READ_REG(hw, E1000_EECD) & E1000_EECD_AUTO_RD) + break; + msec_delay(1); + i++; + } + + if (i == AUTO_READ_DONE_TIMEOUT) { + DEBUGOUT("Auto read by HW from NVM has not completed.\n"); + return -E1000_ERR_RESET; + } + + return E1000_SUCCESS; +} + +/** + * e1000_valid_led_default_generic - Verify a valid default LED config + * @hw: pointer to the HW structure + * @data: pointer to the NVM (EEPROM) + * + * Read the EEPROM for the current default LED configuration. If the + * LED configuration is not valid, set to a valid LED configuration. + **/ +s32 e1000_valid_led_default_generic(struct e1000_hw *hw, u16 *data) +{ + s32 ret_val; + + DEBUGFUNC("e1000_valid_led_default_generic"); + + ret_val = hw->nvm.ops.read(hw, NVM_ID_LED_SETTINGS, 1, data); + if (ret_val) { + DEBUGOUT("NVM Read Error\n"); + return ret_val; + } + + if (*data == ID_LED_RESERVED_0000 || *data == ID_LED_RESERVED_FFFF) + *data = ID_LED_DEFAULT; + + return E1000_SUCCESS; +} + +/** + * e1000_id_led_init_generic - + * @hw: pointer to the HW structure + * + **/ +s32 e1000_id_led_init_generic(struct e1000_hw *hw) +{ + struct e1000_mac_info *mac = &hw->mac; + s32 ret_val; + const u32 ledctl_mask = 0x000000FF; + const u32 ledctl_on = E1000_LEDCTL_MODE_LED_ON; + const u32 ledctl_off = E1000_LEDCTL_MODE_LED_OFF; + u16 data, i, temp; + const u16 led_mask = 0x0F; + + DEBUGFUNC("e1000_id_led_init_generic"); + + ret_val = hw->nvm.ops.valid_led_default(hw, &data); + if (ret_val) + return ret_val; + + mac->ledctl_default = E1000_READ_REG(hw, E1000_LEDCTL); + mac->ledctl_mode1 = mac->ledctl_default; + mac->ledctl_mode2 = mac->ledctl_default; + + for (i = 0; i < 4; i++) { + temp = (data >> (i << 2)) & led_mask; + switch (temp) { + case ID_LED_ON1_DEF2: + case ID_LED_ON1_ON2: + case ID_LED_ON1_OFF2: + mac->ledctl_mode1 &= ~(ledctl_mask << (i << 3)); + mac->ledctl_mode1 |= ledctl_on << (i << 3); + break; + case ID_LED_OFF1_DEF2: + case ID_LED_OFF1_ON2: + case ID_LED_OFF1_OFF2: + mac->ledctl_mode1 &= ~(ledctl_mask << (i << 3)); + mac->ledctl_mode1 |= ledctl_off << (i << 3); + break; + default: + /* Do nothing */ + break; + } + switch (temp) { + case ID_LED_DEF1_ON2: + case ID_LED_ON1_ON2: + case ID_LED_OFF1_ON2: + mac->ledctl_mode2 &= ~(ledctl_mask << (i << 3)); + mac->ledctl_mode2 |= ledctl_on << (i << 3); + break; + case ID_LED_DEF1_OFF2: + case ID_LED_ON1_OFF2: + case ID_LED_OFF1_OFF2: + mac->ledctl_mode2 &= ~(ledctl_mask << (i << 3)); + mac->ledctl_mode2 |= ledctl_off << (i << 3); + break; + default: + /* Do nothing */ + break; + } + } + + return E1000_SUCCESS; +} + +/** + * e1000_setup_led_generic - Configures SW controllable LED + * @hw: pointer to the HW structure + * + * This prepares the SW controllable LED for use and saves the current state + * of the LED so it can be later restored. + **/ +s32 e1000_setup_led_generic(struct e1000_hw *hw) +{ + u32 ledctl; + + DEBUGFUNC("e1000_setup_led_generic"); + + if (hw->mac.ops.setup_led != e1000_setup_led_generic) + return -E1000_ERR_CONFIG; + + if (hw->phy.media_type == e1000_media_type_fiber) { + ledctl = E1000_READ_REG(hw, E1000_LEDCTL); + hw->mac.ledctl_default = ledctl; + /* Turn off LED0 */ + ledctl &= ~(E1000_LEDCTL_LED0_IVRT | E1000_LEDCTL_LED0_BLINK | + E1000_LEDCTL_LED0_MODE_MASK); + ledctl |= (E1000_LEDCTL_MODE_LED_OFF << + E1000_LEDCTL_LED0_MODE_SHIFT); + E1000_WRITE_REG(hw, E1000_LEDCTL, ledctl); + } else if (hw->phy.media_type == e1000_media_type_copper) { + E1000_WRITE_REG(hw, E1000_LEDCTL, hw->mac.ledctl_mode1); + } + + return E1000_SUCCESS; +} + +/** + * e1000_cleanup_led_generic - Set LED config to default operation + * @hw: pointer to the HW structure + * + * Remove the current LED configuration and set the LED configuration + * to the default value, saved from the EEPROM. + **/ +s32 e1000_cleanup_led_generic(struct e1000_hw *hw) +{ + DEBUGFUNC("e1000_cleanup_led_generic"); + + E1000_WRITE_REG(hw, E1000_LEDCTL, hw->mac.ledctl_default); + return E1000_SUCCESS; +} + +/** + * e1000_blink_led_generic - Blink LED + * @hw: pointer to the HW structure + * + * Blink the LEDs which are set to be on. + **/ +s32 e1000_blink_led_generic(struct e1000_hw *hw) +{ + u32 ledctl_blink = 0; + u32 i; + + DEBUGFUNC("e1000_blink_led_generic"); + + if (hw->phy.media_type == e1000_media_type_fiber) { + /* always blink LED0 for PCI-E fiber */ + ledctl_blink = E1000_LEDCTL_LED0_BLINK | + (E1000_LEDCTL_MODE_LED_ON << E1000_LEDCTL_LED0_MODE_SHIFT); + } else { + /* Set the blink bit for each LED that's "on" (0x0E) + * (or "off" if inverted) in ledctl_mode2. The blink + * logic in hardware only works when mode is set to "on" + * so it must be changed accordingly when the mode is + * "off" and inverted. + */ + ledctl_blink = hw->mac.ledctl_mode2; + for (i = 0; i < 32; i += 8) { + u32 mode = (hw->mac.ledctl_mode2 >> i) & + E1000_LEDCTL_LED0_MODE_MASK; + u32 led_default = hw->mac.ledctl_default >> i; + + if ((!(led_default & E1000_LEDCTL_LED0_IVRT) && + (mode == E1000_LEDCTL_MODE_LED_ON)) || + ((led_default & E1000_LEDCTL_LED0_IVRT) && + (mode == E1000_LEDCTL_MODE_LED_OFF))) { + ledctl_blink &= + ~(E1000_LEDCTL_LED0_MODE_MASK << i); + ledctl_blink |= (E1000_LEDCTL_LED0_BLINK | + E1000_LEDCTL_MODE_LED_ON) << i; + } + } + } + + E1000_WRITE_REG(hw, E1000_LEDCTL, ledctl_blink); + + return E1000_SUCCESS; +} + +/** + * e1000_led_on_generic - Turn LED on + * @hw: pointer to the HW structure + * + * Turn LED on. + **/ +s32 e1000_led_on_generic(struct e1000_hw *hw) +{ + u32 ctrl; + + DEBUGFUNC("e1000_led_on_generic"); + + switch (hw->phy.media_type) { + case e1000_media_type_fiber: + ctrl = E1000_READ_REG(hw, E1000_CTRL); + ctrl &= ~E1000_CTRL_SWDPIN0; + ctrl |= E1000_CTRL_SWDPIO0; + E1000_WRITE_REG(hw, E1000_CTRL, ctrl); + break; + case e1000_media_type_copper: + E1000_WRITE_REG(hw, E1000_LEDCTL, hw->mac.ledctl_mode2); + break; + default: + break; + } + + return E1000_SUCCESS; +} + +/** + * e1000_led_off_generic - Turn LED off + * @hw: pointer to the HW structure + * + * Turn LED off. + **/ +s32 e1000_led_off_generic(struct e1000_hw *hw) +{ + u32 ctrl; + + DEBUGFUNC("e1000_led_off_generic"); + + switch (hw->phy.media_type) { + case e1000_media_type_fiber: + ctrl = E1000_READ_REG(hw, E1000_CTRL); + ctrl |= E1000_CTRL_SWDPIN0; + ctrl |= E1000_CTRL_SWDPIO0; + E1000_WRITE_REG(hw, E1000_CTRL, ctrl); + break; + case e1000_media_type_copper: + E1000_WRITE_REG(hw, E1000_LEDCTL, hw->mac.ledctl_mode1); + break; + default: + break; + } + + return E1000_SUCCESS; +} + +/** + * e1000_set_pcie_no_snoop_generic - Set PCI-express capabilities + * @hw: pointer to the HW structure + * @no_snoop: bitmap of snoop events + * + * Set the PCI-express register to snoop for events enabled in 'no_snoop'. + **/ +void e1000_set_pcie_no_snoop_generic(struct e1000_hw *hw, u32 no_snoop) +{ + u32 gcr; + + DEBUGFUNC("e1000_set_pcie_no_snoop_generic"); + + if (hw->bus.type != e1000_bus_type_pci_express) + return; + + if (no_snoop) { + gcr = E1000_READ_REG(hw, E1000_GCR); + gcr &= ~(PCIE_NO_SNOOP_ALL); + gcr |= no_snoop; + E1000_WRITE_REG(hw, E1000_GCR, gcr); + } +} + +/** + * e1000_disable_pcie_master_generic - Disables PCI-express master access + * @hw: pointer to the HW structure + * + * Returns E1000_SUCCESS if successful, else returns -10 + * (-E1000_ERR_MASTER_REQUESTS_PENDING) if master disable bit has not caused + * the master requests to be disabled. + * + * Disables PCI-Express master access and verifies there are no pending + * requests. + **/ +s32 e1000_disable_pcie_master_generic(struct e1000_hw *hw) +{ + u32 ctrl; + s32 timeout = MASTER_DISABLE_TIMEOUT; + + DEBUGFUNC("e1000_disable_pcie_master_generic"); + + if (hw->bus.type != e1000_bus_type_pci_express) + return E1000_SUCCESS; + + ctrl = E1000_READ_REG(hw, E1000_CTRL); + ctrl |= E1000_CTRL_GIO_MASTER_DISABLE; + E1000_WRITE_REG(hw, E1000_CTRL, ctrl); + + while (timeout) { + if (!(E1000_READ_REG(hw, E1000_STATUS) & + E1000_STATUS_GIO_MASTER_ENABLE) || + E1000_REMOVED(hw->hw_addr)) + break; + usec_delay(100); + timeout--; + } + + if (!timeout) { + DEBUGOUT("Master requests are pending.\n"); + return -E1000_ERR_MASTER_REQUESTS_PENDING; + } + + return E1000_SUCCESS; +} + +/** + * e1000_reset_adaptive_generic - Reset Adaptive Interframe Spacing + * @hw: pointer to the HW structure + * + * Reset the Adaptive Interframe Spacing throttle to default values. + **/ +void e1000_reset_adaptive_generic(struct e1000_hw *hw) +{ + struct e1000_mac_info *mac = &hw->mac; + + DEBUGFUNC("e1000_reset_adaptive_generic"); + + if (!mac->adaptive_ifs) { + DEBUGOUT("Not in Adaptive IFS mode!\n"); + return; + } + + mac->current_ifs_val = 0; + mac->ifs_min_val = IFS_MIN; + mac->ifs_max_val = IFS_MAX; + mac->ifs_step_size = IFS_STEP; + mac->ifs_ratio = IFS_RATIO; + + mac->in_ifs_mode = false; + E1000_WRITE_REG(hw, E1000_AIT, 0); +} + +/** + * e1000_update_adaptive_generic - Update Adaptive Interframe Spacing + * @hw: pointer to the HW structure + * + * Update the Adaptive Interframe Spacing Throttle value based on the + * time between transmitted packets and time between collisions. + **/ +void e1000_update_adaptive_generic(struct e1000_hw *hw) +{ + struct e1000_mac_info *mac = &hw->mac; + + DEBUGFUNC("e1000_update_adaptive_generic"); + + if (!mac->adaptive_ifs) { + DEBUGOUT("Not in Adaptive IFS mode!\n"); + return; + } + + if ((mac->collision_delta * mac->ifs_ratio) > mac->tx_packet_delta) { + if (mac->tx_packet_delta > MIN_NUM_XMITS) { + mac->in_ifs_mode = true; + if (mac->current_ifs_val < mac->ifs_max_val) { + if (!mac->current_ifs_val) + mac->current_ifs_val = mac->ifs_min_val; + else + mac->current_ifs_val += + mac->ifs_step_size; + E1000_WRITE_REG(hw, E1000_AIT, + mac->current_ifs_val); + } + } + } else { + if (mac->in_ifs_mode && + (mac->tx_packet_delta <= MIN_NUM_XMITS)) { + mac->current_ifs_val = 0; + mac->in_ifs_mode = false; + E1000_WRITE_REG(hw, E1000_AIT, 0); + } + } +} + +/** + * e1000_validate_mdi_setting_generic - Verify MDI/MDIx settings + * @hw: pointer to the HW structure + * + * Verify that when not using auto-negotiation that MDI/MDIx is correctly + * set, which is forced to MDI mode only. + **/ +static s32 e1000_validate_mdi_setting_generic(struct e1000_hw *hw) +{ + DEBUGFUNC("e1000_validate_mdi_setting_generic"); + + if (!hw->mac.autoneg && (hw->phy.mdix == 0 || hw->phy.mdix == 3)) { + DEBUGOUT("Invalid MDI setting detected\n"); + hw->phy.mdix = 1; + return -E1000_ERR_CONFIG; + } + + return E1000_SUCCESS; +} + +/** + * e1000_validate_mdi_setting_crossover_generic - Verify MDI/MDIx settings + * @hw: pointer to the HW structure + * + * Validate the MDI/MDIx setting, allowing for auto-crossover during forced + * operation. + **/ +s32 e1000_validate_mdi_setting_crossover_generic(struct e1000_hw E1000_UNUSEDARG *hw) +{ + DEBUGFUNC("e1000_validate_mdi_setting_crossover_generic"); + + return E1000_SUCCESS; +} + +/** + * e1000_write_8bit_ctrl_reg_generic - Write a 8bit CTRL register + * @hw: pointer to the HW structure + * @reg: 32bit register offset such as E1000_SCTL + * @offset: register offset to write to + * @data: data to write at register offset + * + * Writes an address/data control type register. There are several of these + * and they all have the format address << 8 | data and bit 31 is polled for + * completion. + **/ +s32 e1000_write_8bit_ctrl_reg_generic(struct e1000_hw *hw, u32 reg, + u32 offset, u8 data) +{ + u32 i, regvalue = 0; + + DEBUGFUNC("e1000_write_8bit_ctrl_reg_generic"); + + /* Set up the address and data */ + regvalue = ((u32)data) | (offset << E1000_GEN_CTL_ADDRESS_SHIFT); + E1000_WRITE_REG(hw, reg, regvalue); + + /* Poll the ready bit to see if the MDI read completed */ + for (i = 0; i < E1000_GEN_POLL_TIMEOUT; i++) { + usec_delay(5); + regvalue = E1000_READ_REG(hw, reg); + if (regvalue & E1000_GEN_CTL_READY) + break; + } + if (!(regvalue & E1000_GEN_CTL_READY)) { + DEBUGOUT1("Reg %08x did not indicate ready\n", reg); + return -E1000_ERR_PHY; + } + + return E1000_SUCCESS; +} diff --git a/drivers/staging/igb_avb/e1000_mac.h b/drivers/staging/igb_avb/e1000_mac.h new file mode 100644 index 000000000000..a3878361095e --- /dev/null +++ b/drivers/staging/igb_avb/e1000_mac.h @@ -0,0 +1,81 @@ +/******************************************************************************* + + Intel(R) Gigabit Ethernet Linux driver + Copyright(c) 2007-2015 Intel Corporation. + + This program is free software; you can redistribute it and/or modify it + under the terms and conditions of the GNU General Public License, + version 2, as published by the Free Software Foundation. + + This program is distributed in the hope 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. + + The full GNU General Public License is included in this distribution in + the file called "COPYING". + + Contact Information: + Linux NICS + e1000-devel Mailing List + Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + +*******************************************************************************/ + +#ifndef _E1000_MAC_H_ +#define _E1000_MAC_H_ + +void e1000_init_mac_ops_generic(struct e1000_hw *hw); +#ifndef E1000_REMOVED +#define E1000_REMOVED(a) (0) +#endif /* E1000_REMOVED */ +void e1000_null_mac_generic(struct e1000_hw *hw); +s32 e1000_null_ops_generic(struct e1000_hw *hw); +s32 e1000_null_link_info(struct e1000_hw *hw, u16 *s, u16 *d); +bool e1000_null_mng_mode(struct e1000_hw *hw); +void e1000_null_update_mc(struct e1000_hw *hw, u8 *h, u32 a); +void e1000_null_write_vfta(struct e1000_hw *hw, u32 a, u32 b); +int e1000_null_rar_set(struct e1000_hw *hw, u8 *h, u32 a); +s32 e1000_blink_led_generic(struct e1000_hw *hw); +s32 e1000_check_for_copper_link_generic(struct e1000_hw *hw); +s32 e1000_check_for_fiber_link_generic(struct e1000_hw *hw); +s32 e1000_check_for_serdes_link_generic(struct e1000_hw *hw); +s32 e1000_cleanup_led_generic(struct e1000_hw *hw); +s32 e1000_config_fc_after_link_up_generic(struct e1000_hw *hw); +s32 e1000_disable_pcie_master_generic(struct e1000_hw *hw); +s32 e1000_force_mac_fc_generic(struct e1000_hw *hw); +s32 e1000_get_auto_rd_done_generic(struct e1000_hw *hw); +s32 e1000_get_bus_info_pcie_generic(struct e1000_hw *hw); +void e1000_set_lan_id_single_port(struct e1000_hw *hw); +s32 e1000_get_hw_semaphore_generic(struct e1000_hw *hw); +s32 e1000_get_speed_and_duplex_copper_generic(struct e1000_hw *hw, u16 *speed, + u16 *duplex); +s32 e1000_get_speed_and_duplex_fiber_serdes_generic(struct e1000_hw *hw, + u16 *speed, u16 *duplex); +s32 e1000_id_led_init_generic(struct e1000_hw *hw); +s32 e1000_led_on_generic(struct e1000_hw *hw); +s32 e1000_led_off_generic(struct e1000_hw *hw); +void e1000_update_mc_addr_list_generic(struct e1000_hw *hw, + u8 *mc_addr_list, u32 mc_addr_count); +s32 e1000_set_fc_watermarks_generic(struct e1000_hw *hw); +s32 e1000_setup_fiber_serdes_link_generic(struct e1000_hw *hw); +s32 e1000_setup_led_generic(struct e1000_hw *hw); +s32 e1000_setup_link_generic(struct e1000_hw *hw); +s32 e1000_validate_mdi_setting_crossover_generic(struct e1000_hw *hw); +s32 e1000_write_8bit_ctrl_reg_generic(struct e1000_hw *hw, u32 reg, + u32 offset, u8 data); + +u32 e1000_hash_mc_addr_generic(struct e1000_hw *hw, u8 *mc_addr); + +void e1000_clear_hw_cntrs_base_generic(struct e1000_hw *hw); +void e1000_clear_vfta_generic(struct e1000_hw *hw); +void e1000_init_rx_addrs_generic(struct e1000_hw *hw, u16 rar_count); +void e1000_pcix_mmrbc_workaround_generic(struct e1000_hw *hw); +void e1000_put_hw_semaphore_generic(struct e1000_hw *hw); +s32 e1000_check_alt_mac_addr_generic(struct e1000_hw *hw); +void e1000_reset_adaptive_generic(struct e1000_hw *hw); +void e1000_set_pcie_no_snoop_generic(struct e1000_hw *hw, u32 no_snoop); +void e1000_update_adaptive_generic(struct e1000_hw *hw); +void e1000_write_vfta_generic(struct e1000_hw *hw, u32 offset, u32 value); + +#endif diff --git a/drivers/staging/igb_avb/e1000_manage.c b/drivers/staging/igb_avb/e1000_manage.c new file mode 100644 index 000000000000..36671fbdab13 --- /dev/null +++ b/drivers/staging/igb_avb/e1000_manage.c @@ -0,0 +1,552 @@ +/******************************************************************************* + + Intel(R) Gigabit Ethernet Linux driver + Copyright(c) 2007-2015 Intel Corporation. + + This program is free software; you can redistribute it and/or modify it + under the terms and conditions of the GNU General Public License, + version 2, as published by the Free Software Foundation. + + This program is distributed in the hope 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. + + The full GNU General Public License is included in this distribution in + the file called "COPYING". + + Contact Information: + Linux NICS + e1000-devel Mailing List + Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + +*******************************************************************************/ + +#include "e1000_api.h" + +/** + * e1000_calculate_checksum - Calculate checksum for buffer + * @buffer: pointer to EEPROM + * @length: size of EEPROM to calculate a checksum for + * + * Calculates the checksum for some buffer on a specified length. The + * checksum calculated is returned. + **/ +u8 e1000_calculate_checksum(u8 *buffer, u32 length) +{ + u32 i; + u8 sum = 0; + + DEBUGFUNC("e1000_calculate_checksum"); + + if (!buffer) + return 0; + + for (i = 0; i < length; i++) + sum += buffer[i]; + + return (u8) (0 - sum); +} + +/** + * e1000_mng_enable_host_if_generic - Checks host interface is enabled + * @hw: pointer to the HW structure + * + * Returns E1000_success upon success, else E1000_ERR_HOST_INTERFACE_COMMAND + * + * This function checks whether the HOST IF is enabled for command operation + * and also checks whether the previous command is completed. It busy waits + * in case of previous command is not completed. + **/ +s32 e1000_mng_enable_host_if_generic(struct e1000_hw *hw) +{ + u32 hicr; + u8 i; + + DEBUGFUNC("e1000_mng_enable_host_if_generic"); + + if (!hw->mac.arc_subsystem_valid) { + DEBUGOUT("ARC subsystem not valid.\n"); + return -E1000_ERR_HOST_INTERFACE_COMMAND; + } + + /* Check that the host interface is enabled. */ + hicr = E1000_READ_REG(hw, E1000_HICR); + if (!(hicr & E1000_HICR_EN)) { + DEBUGOUT("E1000_HOST_EN bit disabled.\n"); + return -E1000_ERR_HOST_INTERFACE_COMMAND; + } + /* check the previous command is completed */ + for (i = 0; i < E1000_MNG_DHCP_COMMAND_TIMEOUT; i++) { + hicr = E1000_READ_REG(hw, E1000_HICR); + if (!(hicr & E1000_HICR_C)) + break; + msec_delay_irq(1); + } + + if (i == E1000_MNG_DHCP_COMMAND_TIMEOUT) { + DEBUGOUT("Previous command timeout failed .\n"); + return -E1000_ERR_HOST_INTERFACE_COMMAND; + } + + return E1000_SUCCESS; +} + +/** + * e1000_check_mng_mode_generic - Generic check management mode + * @hw: pointer to the HW structure + * + * Reads the firmware semaphore register and returns true (>0) if + * manageability is enabled, else false (0). + **/ +bool e1000_check_mng_mode_generic(struct e1000_hw *hw) +{ + u32 fwsm = E1000_READ_REG(hw, E1000_FWSM); + + DEBUGFUNC("e1000_check_mng_mode_generic"); + + + return (fwsm & E1000_FWSM_MODE_MASK) == + (E1000_MNG_IAMT_MODE << E1000_FWSM_MODE_SHIFT); +} + +/** + * e1000_enable_tx_pkt_filtering_generic - Enable packet filtering on Tx + * @hw: pointer to the HW structure + * + * Enables packet filtering on transmit packets if manageability is enabled + * and host interface is enabled. + **/ +bool e1000_enable_tx_pkt_filtering_generic(struct e1000_hw *hw) +{ + struct e1000_host_mng_dhcp_cookie *hdr = &hw->mng_cookie; + u32 *buffer = (u32 *)&hw->mng_cookie; + u32 offset; + s32 ret_val, hdr_csum, csum; + u8 i, len; + + DEBUGFUNC("e1000_enable_tx_pkt_filtering_generic"); + + hw->mac.tx_pkt_filtering = true; + + /* No manageability, no filtering */ + if (!hw->mac.ops.check_mng_mode(hw)) { + hw->mac.tx_pkt_filtering = false; + return hw->mac.tx_pkt_filtering; + } + + /* If we can't read from the host interface for whatever + * reason, disable filtering. + */ + ret_val = e1000_mng_enable_host_if_generic(hw); + if (ret_val != E1000_SUCCESS) { + hw->mac.tx_pkt_filtering = false; + return hw->mac.tx_pkt_filtering; + } + + /* Read in the header. Length and offset are in dwords. */ + len = E1000_MNG_DHCP_COOKIE_LENGTH >> 2; + offset = E1000_MNG_DHCP_COOKIE_OFFSET >> 2; + for (i = 0; i < len; i++) + *(buffer + i) = E1000_READ_REG_ARRAY_DWORD(hw, E1000_HOST_IF, + offset + i); + hdr_csum = hdr->checksum; + hdr->checksum = 0; + csum = e1000_calculate_checksum((u8 *)hdr, + E1000_MNG_DHCP_COOKIE_LENGTH); + /* If either the checksums or signature don't match, then + * the cookie area isn't considered valid, in which case we + * take the safe route of assuming Tx filtering is enabled. + */ + if ((hdr_csum != csum) || (hdr->signature != E1000_IAMT_SIGNATURE)) { + hw->mac.tx_pkt_filtering = true; + return hw->mac.tx_pkt_filtering; + } + + /* Cookie area is valid, make the final check for filtering. */ + if (!(hdr->status & E1000_MNG_DHCP_COOKIE_STATUS_PARSING)) + hw->mac.tx_pkt_filtering = false; + + return hw->mac.tx_pkt_filtering; +} + +/** + * e1000_mng_write_cmd_header_generic - Writes manageability command header + * @hw: pointer to the HW structure + * @hdr: pointer to the host interface command header + * + * Writes the command header after does the checksum calculation. + **/ +s32 e1000_mng_write_cmd_header_generic(struct e1000_hw *hw, + struct e1000_host_mng_command_header *hdr) +{ + u16 i, length = sizeof(struct e1000_host_mng_command_header); + + DEBUGFUNC("e1000_mng_write_cmd_header_generic"); + + /* Write the whole command header structure with new checksum. */ + + hdr->checksum = e1000_calculate_checksum((u8 *)hdr, length); + + length >>= 2; + /* Write the relevant command block into the ram area. */ + for (i = 0; i < length; i++) { + E1000_WRITE_REG_ARRAY_DWORD(hw, E1000_HOST_IF, i, + *((u32 *) hdr + i)); + E1000_WRITE_FLUSH(hw); + } + + return E1000_SUCCESS; +} + +/** + * e1000_mng_host_if_write_generic - Write to the manageability host interface + * @hw: pointer to the HW structure + * @buffer: pointer to the host interface buffer + * @length: size of the buffer + * @offset: location in the buffer to write to + * @sum: sum of the data (not checksum) + * + * This function writes the buffer content at the offset given on the host if. + * It also does alignment considerations to do the writes in most efficient + * way. Also fills up the sum of the buffer in *buffer parameter. + **/ +s32 e1000_mng_host_if_write_generic(struct e1000_hw *hw, u8 *buffer, + u16 length, u16 offset, u8 *sum) +{ + u8 *tmp; + u8 *bufptr = buffer; + u32 data = 0; + u16 remaining, i, j, prev_bytes; + + DEBUGFUNC("e1000_mng_host_if_write_generic"); + + /* sum = only sum of the data and it is not checksum */ + + if (length == 0 || offset + length > E1000_HI_MAX_MNG_DATA_LENGTH) + return -E1000_ERR_PARAM; + + tmp = (u8 *)&data; + prev_bytes = offset & 0x3; + offset >>= 2; + + if (prev_bytes) { + data = E1000_READ_REG_ARRAY_DWORD(hw, E1000_HOST_IF, offset); + for (j = prev_bytes; j < sizeof(u32); j++) { + *(tmp + j) = *bufptr++; + *sum += *(tmp + j); + } + E1000_WRITE_REG_ARRAY_DWORD(hw, E1000_HOST_IF, offset, data); + length -= j - prev_bytes; + offset++; + } + + remaining = length & 0x3; + length -= remaining; + + /* Calculate length in DWORDs */ + length >>= 2; + + /* The device driver writes the relevant command block into the + * ram area. + */ + for (i = 0; i < length; i++) { + for (j = 0; j < sizeof(u32); j++) { + *(tmp + j) = *bufptr++; + *sum += *(tmp + j); + } + + E1000_WRITE_REG_ARRAY_DWORD(hw, E1000_HOST_IF, offset + i, + data); + } + if (remaining) { + for (j = 0; j < sizeof(u32); j++) { + if (j < remaining) + *(tmp + j) = *bufptr++; + else + *(tmp + j) = 0; + + *sum += *(tmp + j); + } + E1000_WRITE_REG_ARRAY_DWORD(hw, E1000_HOST_IF, offset + i, + data); + } + + return E1000_SUCCESS; +} + +/** + * e1000_mng_write_dhcp_info_generic - Writes DHCP info to host interface + * @hw: pointer to the HW structure + * @buffer: pointer to the host interface + * @length: size of the buffer + * + * Writes the DHCP information to the host interface. + **/ +s32 e1000_mng_write_dhcp_info_generic(struct e1000_hw *hw, u8 *buffer, + u16 length) +{ + struct e1000_host_mng_command_header hdr; + s32 ret_val; + u32 hicr; + + DEBUGFUNC("e1000_mng_write_dhcp_info_generic"); + + hdr.command_id = E1000_MNG_DHCP_TX_PAYLOAD_CMD; + hdr.command_length = length; + hdr.reserved1 = 0; + hdr.reserved2 = 0; + hdr.checksum = 0; + + /* Enable the host interface */ + ret_val = e1000_mng_enable_host_if_generic(hw); + if (ret_val) + return ret_val; + + /* Populate the host interface with the contents of "buffer". */ + ret_val = e1000_mng_host_if_write_generic(hw, buffer, length, + sizeof(hdr), &(hdr.checksum)); + if (ret_val) + return ret_val; + + /* Write the manageability command header */ + ret_val = e1000_mng_write_cmd_header_generic(hw, &hdr); + if (ret_val) + return ret_val; + + /* Tell the ARC a new command is pending. */ + hicr = E1000_READ_REG(hw, E1000_HICR); + E1000_WRITE_REG(hw, E1000_HICR, hicr | E1000_HICR_C); + + return E1000_SUCCESS; +} + +/** + * e1000_enable_mng_pass_thru - Check if management passthrough is needed + * @hw: pointer to the HW structure + * + * Verifies the hardware needs to leave interface enabled so that frames can + * be directed to and from the management interface. + **/ +bool e1000_enable_mng_pass_thru(struct e1000_hw *hw) +{ + u32 manc; + u32 fwsm, factps; + + DEBUGFUNC("e1000_enable_mng_pass_thru"); + + if (!hw->mac.asf_firmware_present) + return false; + + manc = E1000_READ_REG(hw, E1000_MANC); + + if (!(manc & E1000_MANC_RCV_TCO_EN)) + return false; + + if (hw->mac.has_fwsm) { + fwsm = E1000_READ_REG(hw, E1000_FWSM); + factps = E1000_READ_REG(hw, E1000_FACTPS); + + if (!(factps & E1000_FACTPS_MNGCG) && + ((fwsm & E1000_FWSM_MODE_MASK) == + (e1000_mng_mode_pt << E1000_FWSM_MODE_SHIFT))) + return true; + } else if ((manc & E1000_MANC_SMBUS_EN) && + !(manc & E1000_MANC_ASF_EN)) { + return true; + } + + return false; +} + +/** + * e1000_host_interface_command - Writes buffer to host interface + * @hw: pointer to the HW structure + * @buffer: contains a command to write + * @length: the byte length of the buffer, must be multiple of 4 bytes + * + * Writes a buffer to the Host Interface. Upon success, returns E1000_SUCCESS + * else returns E1000_ERR_HOST_INTERFACE_COMMAND. + **/ +s32 e1000_host_interface_command(struct e1000_hw *hw, u8 *buffer, u32 length) +{ + u32 hicr, i; + + DEBUGFUNC("e1000_host_interface_command"); + + if (!(hw->mac.arc_subsystem_valid)) { + DEBUGOUT("Hardware doesn't support host interface command.\n"); + return E1000_SUCCESS; + } + + if (!hw->mac.asf_firmware_present) { + DEBUGOUT("Firmware is not present.\n"); + return E1000_SUCCESS; + } + + if (length == 0 || length & 0x3 || + length > E1000_HI_MAX_BLOCK_BYTE_LENGTH) { + DEBUGOUT("Buffer length failure.\n"); + return -E1000_ERR_HOST_INTERFACE_COMMAND; + } + + /* Check that the host interface is enabled. */ + hicr = E1000_READ_REG(hw, E1000_HICR); + if (!(hicr & E1000_HICR_EN)) { + DEBUGOUT("E1000_HOST_EN bit disabled.\n"); + return -E1000_ERR_HOST_INTERFACE_COMMAND; + } + + /* Calculate length in DWORDs */ + length >>= 2; + + /* The device driver writes the relevant command block + * into the ram area. + */ + for (i = 0; i < length; i++) + E1000_WRITE_REG_ARRAY_DWORD(hw, E1000_HOST_IF, i, + *((u32 *)buffer + i)); + + /* Setting this bit tells the ARC that a new command is pending. */ + E1000_WRITE_REG(hw, E1000_HICR, hicr | E1000_HICR_C); + + for (i = 0; i < E1000_HI_COMMAND_TIMEOUT; i++) { + hicr = E1000_READ_REG(hw, E1000_HICR); + if (!(hicr & E1000_HICR_C)) + break; + msec_delay(1); + } + + /* Check command successful completion. */ + if (i == E1000_HI_COMMAND_TIMEOUT || + (!(E1000_READ_REG(hw, E1000_HICR) & E1000_HICR_SV))) { + DEBUGOUT("Command has failed with no status valid.\n"); + return -E1000_ERR_HOST_INTERFACE_COMMAND; + } + + for (i = 0; i < length; i++) + *((u32 *)buffer + i) = E1000_READ_REG_ARRAY_DWORD(hw, + E1000_HOST_IF, + i); + + return E1000_SUCCESS; +} +/** + * e1000_load_firmware - Writes proxy FW code buffer to host interface + * and execute. + * @hw: pointer to the HW structure + * @buffer: contains a firmware to write + * @length: the byte length of the buffer, must be multiple of 4 bytes + * + * Upon success returns E1000_SUCCESS, returns E1000_ERR_CONFIG if not enabled + * in HW else returns E1000_ERR_HOST_INTERFACE_COMMAND. + **/ +s32 e1000_load_firmware(struct e1000_hw *hw, u8 *buffer, u32 length) +{ + u32 hicr, hibba, fwsm, icr, i; + + DEBUGFUNC("e1000_load_firmware"); + + if (hw->mac.type < e1000_i210) { + DEBUGOUT("Hardware doesn't support loading FW by the driver\n"); + return -E1000_ERR_CONFIG; + } + + /* Check that the host interface is enabled. */ + hicr = E1000_READ_REG(hw, E1000_HICR); + if (!(hicr & E1000_HICR_EN)) { + DEBUGOUT("E1000_HOST_EN bit disabled.\n"); + return -E1000_ERR_CONFIG; + } + if (!(hicr & E1000_HICR_MEMORY_BASE_EN)) { + DEBUGOUT("E1000_HICR_MEMORY_BASE_EN bit disabled.\n"); + return -E1000_ERR_CONFIG; + } + + if (length == 0 || length & 0x3 || length > E1000_HI_FW_MAX_LENGTH) { + DEBUGOUT("Buffer length failure.\n"); + return -E1000_ERR_INVALID_ARGUMENT; + } + + /* Clear notification from ROM-FW by reading ICR register */ + icr = E1000_READ_REG(hw, E1000_ICR_V2); + + /* Reset ROM-FW */ + hicr = E1000_READ_REG(hw, E1000_HICR); + hicr |= E1000_HICR_FW_RESET_ENABLE; + E1000_WRITE_REG(hw, E1000_HICR, hicr); + hicr |= E1000_HICR_FW_RESET; + E1000_WRITE_REG(hw, E1000_HICR, hicr); + E1000_WRITE_FLUSH(hw); + + /* Wait till MAC notifies about its readiness after ROM-FW reset */ + for (i = 0; i < (E1000_HI_COMMAND_TIMEOUT * 2); i++) { + icr = E1000_READ_REG(hw, E1000_ICR_V2); + if (icr & E1000_ICR_MNG) + break; + msec_delay(1); + } + + /* Check for timeout */ + if (i == E1000_HI_COMMAND_TIMEOUT) { + DEBUGOUT("FW reset failed.\n"); + return -E1000_ERR_HOST_INTERFACE_COMMAND; + } + + /* Wait till MAC is ready to accept new FW code */ + for (i = 0; i < E1000_HI_COMMAND_TIMEOUT; i++) { + fwsm = E1000_READ_REG(hw, E1000_FWSM); + if ((fwsm & E1000_FWSM_FW_VALID) && + ((fwsm & E1000_FWSM_MODE_MASK) >> E1000_FWSM_MODE_SHIFT == + E1000_FWSM_HI_EN_ONLY_MODE)) + break; + msec_delay(1); + } + + /* Check for timeout */ + if (i == E1000_HI_COMMAND_TIMEOUT) { + DEBUGOUT("FW reset failed.\n"); + return -E1000_ERR_HOST_INTERFACE_COMMAND; + } + + /* Calculate length in DWORDs */ + length >>= 2; + + /* The device driver writes the relevant FW code block + * into the ram area in DWORDs via 1kB ram addressing window. + */ + for (i = 0; i < length; i++) { + if (!(i % E1000_HI_FW_BLOCK_DWORD_LENGTH)) { + /* Point to correct 1kB ram window */ + hibba = E1000_HI_FW_BASE_ADDRESS + + ((E1000_HI_FW_BLOCK_DWORD_LENGTH << 2) * + (i / E1000_HI_FW_BLOCK_DWORD_LENGTH)); + + E1000_WRITE_REG(hw, E1000_HIBBA, hibba); + } + + E1000_WRITE_REG_ARRAY_DWORD(hw, E1000_HOST_IF, + i % E1000_HI_FW_BLOCK_DWORD_LENGTH, + *((u32 *)buffer + i)); + } + + /* Setting this bit tells the ARC that a new FW is ready to execute. */ + hicr = E1000_READ_REG(hw, E1000_HICR); + E1000_WRITE_REG(hw, E1000_HICR, hicr | E1000_HICR_C); + + for (i = 0; i < E1000_HI_COMMAND_TIMEOUT; i++) { + hicr = E1000_READ_REG(hw, E1000_HICR); + if (!(hicr & E1000_HICR_C)) + break; + msec_delay(1); + } + + /* Check for successful FW start. */ + if (i == E1000_HI_COMMAND_TIMEOUT) { + DEBUGOUT("New FW did not start within timeout period.\n"); + return -E1000_ERR_HOST_INTERFACE_COMMAND; + } + + return E1000_SUCCESS; +} + diff --git a/drivers/staging/igb_avb/e1000_manage.h b/drivers/staging/igb_avb/e1000_manage.h new file mode 100644 index 000000000000..09afc1aed497 --- /dev/null +++ b/drivers/staging/igb_avb/e1000_manage.h @@ -0,0 +1,86 @@ +/******************************************************************************* + + Intel(R) Gigabit Ethernet Linux driver + Copyright(c) 2007-2015 Intel Corporation. + + This program is free software; you can redistribute it and/or modify it + under the terms and conditions of the GNU General Public License, + version 2, as published by the Free Software Foundation. + + This program is distributed in the hope 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. + + The full GNU General Public License is included in this distribution in + the file called "COPYING". + + Contact Information: + Linux NICS + e1000-devel Mailing List + Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + +*******************************************************************************/ + +#ifndef _E1000_MANAGE_H_ +#define _E1000_MANAGE_H_ + +bool e1000_check_mng_mode_generic(struct e1000_hw *hw); +bool e1000_enable_tx_pkt_filtering_generic(struct e1000_hw *hw); +s32 e1000_mng_enable_host_if_generic(struct e1000_hw *hw); +s32 e1000_mng_host_if_write_generic(struct e1000_hw *hw, u8 *buffer, + u16 length, u16 offset, u8 *sum); +s32 e1000_mng_write_cmd_header_generic(struct e1000_hw *hw, + struct e1000_host_mng_command_header *hdr); +s32 e1000_mng_write_dhcp_info_generic(struct e1000_hw *hw, + u8 *buffer, u16 length); +bool e1000_enable_mng_pass_thru(struct e1000_hw *hw); +u8 e1000_calculate_checksum(u8 *buffer, u32 length); +s32 e1000_host_interface_command(struct e1000_hw *hw, u8 *buffer, u32 length); +s32 e1000_load_firmware(struct e1000_hw *hw, u8 *buffer, u32 length); + +enum e1000_mng_mode { + e1000_mng_mode_none = 0, + e1000_mng_mode_asf, + e1000_mng_mode_pt, + e1000_mng_mode_ipmi, + e1000_mng_mode_host_if_only +}; + +#define E1000_FACTPS_MNGCG 0x20000000 + +#define E1000_FWSM_MODE_MASK 0xE +#define E1000_FWSM_MODE_SHIFT 1 +#define E1000_FWSM_FW_VALID 0x00008000 +#define E1000_FWSM_HI_EN_ONLY_MODE 0x4 + +#define E1000_MNG_IAMT_MODE 0x3 +#define E1000_MNG_DHCP_COOKIE_LENGTH 0x10 +#define E1000_MNG_DHCP_COOKIE_OFFSET 0x6F0 +#define E1000_MNG_DHCP_COMMAND_TIMEOUT 10 +#define E1000_MNG_DHCP_TX_PAYLOAD_CMD 64 +#define E1000_MNG_DHCP_COOKIE_STATUS_PARSING 0x1 +#define E1000_MNG_DHCP_COOKIE_STATUS_VLAN 0x2 + +#define E1000_VFTA_ENTRY_SHIFT 5 +#define E1000_VFTA_ENTRY_MASK 0x7F +#define E1000_VFTA_ENTRY_BIT_SHIFT_MASK 0x1F + +#define E1000_HI_MAX_BLOCK_BYTE_LENGTH 1792 /* Num of bytes in range */ +#define E1000_HI_MAX_BLOCK_DWORD_LENGTH 448 /* Num of dwords in range */ +#define E1000_HI_COMMAND_TIMEOUT 500 /* Process HI cmd limit */ +#define E1000_HI_FW_BASE_ADDRESS 0x10000 +#define E1000_HI_FW_MAX_LENGTH (64 * 1024) /* Num of bytes */ +#define E1000_HI_FW_BLOCK_DWORD_LENGTH 256 /* Num of DWORDs per page */ +#define E1000_HICR_MEMORY_BASE_EN 0x200 /* MB Enable bit - RO */ +#define E1000_HICR_EN 0x01 /* Enable bit - RO */ +/* Driver sets this bit when done to put command in RAM */ +#define E1000_HICR_C 0x02 +#define E1000_HICR_SV 0x04 /* Status Validity */ +#define E1000_HICR_FW_RESET_ENABLE 0x40 +#define E1000_HICR_FW_RESET 0x80 + +/* Intel(R) Active Management Technology signature */ +#define E1000_IAMT_SIGNATURE 0x544D4149 + +#endif diff --git a/drivers/staging/igb_avb/e1000_mbx.c b/drivers/staging/igb_avb/e1000_mbx.c new file mode 100644 index 000000000000..f2998f470ce5 --- /dev/null +++ b/drivers/staging/igb_avb/e1000_mbx.c @@ -0,0 +1,523 @@ +/******************************************************************************* + + Intel(R) Gigabit Ethernet Linux driver + Copyright(c) 2007-2015 Intel Corporation. + + This program is free software; you can redistribute it and/or modify it + under the terms and conditions of the GNU General Public License, + version 2, as published by the Free Software Foundation. + + This program is distributed in the hope 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. + + The full GNU General Public License is included in this distribution in + the file called "COPYING". + + Contact Information: + Linux NICS + e1000-devel Mailing List + Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + +*******************************************************************************/ + +#include "e1000_mbx.h" + +/** + * e1000_null_mbx_check_for_flag - No-op function, return 0 + * @hw: pointer to the HW structure + **/ +static s32 e1000_null_mbx_check_for_flag(struct e1000_hw E1000_UNUSEDARG *hw, + u16 E1000_UNUSEDARG mbx_id) +{ + DEBUGFUNC("e1000_null_mbx_check_flag"); + + return E1000_SUCCESS; +} + +/** + * e1000_null_mbx_transact - No-op function, return 0 + * @hw: pointer to the HW structure + **/ +static s32 e1000_null_mbx_transact(struct e1000_hw E1000_UNUSEDARG *hw, + u32 E1000_UNUSEDARG *msg, + u16 E1000_UNUSEDARG size, + u16 E1000_UNUSEDARG mbx_id) +{ + DEBUGFUNC("e1000_null_mbx_rw_msg"); + + return E1000_SUCCESS; +} + +/** + * e1000_read_mbx - Reads a message from the mailbox + * @hw: pointer to the HW structure + * @msg: The message buffer + * @size: Length of buffer + * @mbx_id: id of mailbox to read + * + * returns SUCCESS if it successfuly read message from buffer + **/ +s32 e1000_read_mbx(struct e1000_hw *hw, u32 *msg, u16 size, u16 mbx_id) +{ + struct e1000_mbx_info *mbx = &hw->mbx; + s32 ret_val = -E1000_ERR_MBX; + + DEBUGFUNC("e1000_read_mbx"); + + /* limit read to size of mailbox */ + if (size > mbx->size) + size = mbx->size; + + if (mbx->ops.read) + ret_val = mbx->ops.read(hw, msg, size, mbx_id); + + return ret_val; +} + +/** + * e1000_write_mbx - Write a message to the mailbox + * @hw: pointer to the HW structure + * @msg: The message buffer + * @size: Length of buffer + * @mbx_id: id of mailbox to write + * + * returns SUCCESS if it successfully copied message into the buffer + **/ +s32 e1000_write_mbx(struct e1000_hw *hw, u32 *msg, u16 size, u16 mbx_id) +{ + struct e1000_mbx_info *mbx = &hw->mbx; + s32 ret_val = E1000_SUCCESS; + + DEBUGFUNC("e1000_write_mbx"); + + if (size > mbx->size) + ret_val = -E1000_ERR_MBX; + + else if (mbx->ops.write) + ret_val = mbx->ops.write(hw, msg, size, mbx_id); + + return ret_val; +} + +/** + * e1000_check_for_msg - checks to see if someone sent us mail + * @hw: pointer to the HW structure + * @mbx_id: id of mailbox to check + * + * returns SUCCESS if the Status bit was found or else ERR_MBX + **/ +s32 e1000_check_for_msg(struct e1000_hw *hw, u16 mbx_id) +{ + struct e1000_mbx_info *mbx = &hw->mbx; + s32 ret_val = -E1000_ERR_MBX; + + DEBUGFUNC("e1000_check_for_msg"); + + if (mbx->ops.check_for_msg) + ret_val = mbx->ops.check_for_msg(hw, mbx_id); + + return ret_val; +} + +/** + * e1000_check_for_ack - checks to see if someone sent us ACK + * @hw: pointer to the HW structure + * @mbx_id: id of mailbox to check + * + * returns SUCCESS if the Status bit was found or else ERR_MBX + **/ +s32 e1000_check_for_ack(struct e1000_hw *hw, u16 mbx_id) +{ + struct e1000_mbx_info *mbx = &hw->mbx; + s32 ret_val = -E1000_ERR_MBX; + + DEBUGFUNC("e1000_check_for_ack"); + + if (mbx->ops.check_for_ack) + ret_val = mbx->ops.check_for_ack(hw, mbx_id); + + return ret_val; +} + +/** + * e1000_check_for_rst - checks to see if other side has reset + * @hw: pointer to the HW structure + * @mbx_id: id of mailbox to check + * + * returns SUCCESS if the Status bit was found or else ERR_MBX + **/ +s32 e1000_check_for_rst(struct e1000_hw *hw, u16 mbx_id) +{ + struct e1000_mbx_info *mbx = &hw->mbx; + s32 ret_val = -E1000_ERR_MBX; + + DEBUGFUNC("e1000_check_for_rst"); + + if (mbx->ops.check_for_rst) + ret_val = mbx->ops.check_for_rst(hw, mbx_id); + + return ret_val; +} + +/** + * e1000_poll_for_msg - Wait for message notification + * @hw: pointer to the HW structure + * @mbx_id: id of mailbox to write + * + * returns SUCCESS if it successfully received a message notification + **/ +static s32 e1000_poll_for_msg(struct e1000_hw *hw, u16 mbx_id) +{ + struct e1000_mbx_info *mbx = &hw->mbx; + int countdown = mbx->timeout; + + DEBUGFUNC("e1000_poll_for_msg"); + + if (!countdown || !mbx->ops.check_for_msg) + goto out; + + while (countdown && mbx->ops.check_for_msg(hw, mbx_id)) { + countdown--; + if (!countdown) + break; + usec_delay(mbx->usec_delay); + } + + /* if we failed, all future posted messages fail until reset */ + if (!countdown) + mbx->timeout = 0; +out: + return countdown ? E1000_SUCCESS : -E1000_ERR_MBX; +} + +/** + * e1000_poll_for_ack - Wait for message acknowledgement + * @hw: pointer to the HW structure + * @mbx_id: id of mailbox to write + * + * returns SUCCESS if it successfully received a message acknowledgement + **/ +static s32 e1000_poll_for_ack(struct e1000_hw *hw, u16 mbx_id) +{ + struct e1000_mbx_info *mbx = &hw->mbx; + int countdown = mbx->timeout; + + DEBUGFUNC("e1000_poll_for_ack"); + + if (!countdown || !mbx->ops.check_for_ack) + goto out; + + while (countdown && mbx->ops.check_for_ack(hw, mbx_id)) { + countdown--; + if (!countdown) + break; + usec_delay(mbx->usec_delay); + } + + /* if we failed, all future posted messages fail until reset */ + if (!countdown) + mbx->timeout = 0; +out: + return countdown ? E1000_SUCCESS : -E1000_ERR_MBX; +} + +/** + * e1000_read_posted_mbx - Wait for message notification and receive message + * @hw: pointer to the HW structure + * @msg: The message buffer + * @size: Length of buffer + * @mbx_id: id of mailbox to write + * + * returns SUCCESS if it successfully received a message notification and + * copied it into the receive buffer. + **/ +s32 e1000_read_posted_mbx(struct e1000_hw *hw, u32 *msg, u16 size, u16 mbx_id) +{ + struct e1000_mbx_info *mbx = &hw->mbx; + s32 ret_val = -E1000_ERR_MBX; + + DEBUGFUNC("e1000_read_posted_mbx"); + + if (!mbx->ops.read) + goto out; + + ret_val = e1000_poll_for_msg(hw, mbx_id); + + /* if ack received read message, otherwise we timed out */ + if (!ret_val) + ret_val = mbx->ops.read(hw, msg, size, mbx_id); +out: + return ret_val; +} + +/** + * e1000_write_posted_mbx - Write a message to the mailbox, wait for ack + * @hw: pointer to the HW structure + * @msg: The message buffer + * @size: Length of buffer + * @mbx_id: id of mailbox to write + * + * returns SUCCESS if it successfully copied message into the buffer and + * received an ack to that message within delay * timeout period + **/ +s32 e1000_write_posted_mbx(struct e1000_hw *hw, u32 *msg, u16 size, u16 mbx_id) +{ + struct e1000_mbx_info *mbx = &hw->mbx; + s32 ret_val = -E1000_ERR_MBX; + + DEBUGFUNC("e1000_write_posted_mbx"); + + /* exit if either we can't write or there isn't a defined timeout */ + if (!mbx->ops.write || !mbx->timeout) + goto out; + + /* send msg */ + ret_val = mbx->ops.write(hw, msg, size, mbx_id); + + /* if msg sent wait until we receive an ack */ + if (!ret_val) + ret_val = e1000_poll_for_ack(hw, mbx_id); +out: + return ret_val; +} + +/** + * e1000_init_mbx_ops_generic - Initialize mbx function pointers + * @hw: pointer to the HW structure + * + * Sets the function pointers to no-op functions + **/ +void e1000_init_mbx_ops_generic(struct e1000_hw *hw) +{ + struct e1000_mbx_info *mbx = &hw->mbx; + mbx->ops.init_params = e1000_null_ops_generic; + mbx->ops.read = e1000_null_mbx_transact; + mbx->ops.write = e1000_null_mbx_transact; + mbx->ops.check_for_msg = e1000_null_mbx_check_for_flag; + mbx->ops.check_for_ack = e1000_null_mbx_check_for_flag; + mbx->ops.check_for_rst = e1000_null_mbx_check_for_flag; + mbx->ops.read_posted = e1000_read_posted_mbx; + mbx->ops.write_posted = e1000_write_posted_mbx; +} + +static s32 e1000_check_for_bit_pf(struct e1000_hw *hw, u32 mask) +{ + u32 mbvficr = E1000_READ_REG(hw, E1000_MBVFICR); + s32 ret_val = -E1000_ERR_MBX; + + if (mbvficr & mask) { + ret_val = E1000_SUCCESS; + E1000_WRITE_REG(hw, E1000_MBVFICR, mask); + } + + return ret_val; +} + +/** + * e1000_check_for_msg_pf - checks to see if the VF has sent mail + * @hw: pointer to the HW structure + * @vf_number: the VF index + * + * returns SUCCESS if the VF has set the Status bit or else ERR_MBX + **/ +static s32 e1000_check_for_msg_pf(struct e1000_hw *hw, u16 vf_number) +{ + s32 ret_val = -E1000_ERR_MBX; + + DEBUGFUNC("e1000_check_for_msg_pf"); + + if (!e1000_check_for_bit_pf(hw, E1000_MBVFICR_VFREQ_VF1 << vf_number)) { + ret_val = E1000_SUCCESS; + hw->mbx.stats.reqs++; + } + + return ret_val; +} + +/** + * e1000_check_for_ack_pf - checks to see if the VF has ACKed + * @hw: pointer to the HW structure + * @vf_number: the VF index + * + * returns SUCCESS if the VF has set the Status bit or else ERR_MBX + **/ +static s32 e1000_check_for_ack_pf(struct e1000_hw *hw, u16 vf_number) +{ + s32 ret_val = -E1000_ERR_MBX; + + DEBUGFUNC("e1000_check_for_ack_pf"); + + if (!e1000_check_for_bit_pf(hw, E1000_MBVFICR_VFACK_VF1 << vf_number)) { + ret_val = E1000_SUCCESS; + hw->mbx.stats.acks++; + } + + return ret_val; +} + +/** + * e1000_check_for_rst_pf - checks to see if the VF has reset + * @hw: pointer to the HW structure + * @vf_number: the VF index + * + * returns SUCCESS if the VF has set the Status bit or else ERR_MBX + **/ +static s32 e1000_check_for_rst_pf(struct e1000_hw *hw, u16 vf_number) +{ + u32 vflre = E1000_READ_REG(hw, E1000_VFLRE); + s32 ret_val = -E1000_ERR_MBX; + + DEBUGFUNC("e1000_check_for_rst_pf"); + + if (vflre & (1 << vf_number)) { + ret_val = E1000_SUCCESS; + E1000_WRITE_REG(hw, E1000_VFLRE, (1 << vf_number)); + hw->mbx.stats.rsts++; + } + + return ret_val; +} + +/** + * e1000_obtain_mbx_lock_pf - obtain mailbox lock + * @hw: pointer to the HW structure + * @vf_number: the VF index + * + * return SUCCESS if we obtained the mailbox lock + **/ +static s32 e1000_obtain_mbx_lock_pf(struct e1000_hw *hw, u16 vf_number) +{ + s32 ret_val = -E1000_ERR_MBX; + u32 p2v_mailbox; + + DEBUGFUNC("e1000_obtain_mbx_lock_pf"); + + /* Take ownership of the buffer */ + E1000_WRITE_REG(hw, E1000_P2VMAILBOX(vf_number), E1000_P2VMAILBOX_PFU); + + /* reserve mailbox for vf use */ + p2v_mailbox = E1000_READ_REG(hw, E1000_P2VMAILBOX(vf_number)); + if (p2v_mailbox & E1000_P2VMAILBOX_PFU) + ret_val = E1000_SUCCESS; + + return ret_val; +} + +/** + * e1000_write_mbx_pf - Places a message in the mailbox + * @hw: pointer to the HW structure + * @msg: The message buffer + * @size: Length of buffer + * @vf_number: the VF index + * + * returns SUCCESS if it successfully copied message into the buffer + **/ +static s32 e1000_write_mbx_pf(struct e1000_hw *hw, u32 *msg, u16 size, + u16 vf_number) +{ + s32 ret_val; + u16 i; + + DEBUGFUNC("e1000_write_mbx_pf"); + + /* lock the mailbox to prevent pf/vf race condition */ + ret_val = e1000_obtain_mbx_lock_pf(hw, vf_number); + if (ret_val) + goto out_no_write; + + /* flush msg and acks as we are overwriting the message buffer */ + e1000_check_for_msg_pf(hw, vf_number); + e1000_check_for_ack_pf(hw, vf_number); + + /* copy the caller specified message to the mailbox memory buffer */ + for (i = 0; i < size; i++) + E1000_WRITE_REG_ARRAY(hw, E1000_VMBMEM(vf_number), i, msg[i]); + + /* Interrupt VF to tell it a message has been sent and release buffer*/ + E1000_WRITE_REG(hw, E1000_P2VMAILBOX(vf_number), E1000_P2VMAILBOX_STS); + + /* update stats */ + hw->mbx.stats.msgs_tx++; + +out_no_write: + return ret_val; + +} + +/** + * e1000_read_mbx_pf - Read a message from the mailbox + * @hw: pointer to the HW structure + * @msg: The message buffer + * @size: Length of buffer + * @vf_number: the VF index + * + * This function copies a message from the mailbox buffer to the caller's + * memory buffer. The presumption is that the caller knows that there was + * a message due to a VF request so no polling for message is needed. + **/ +static s32 e1000_read_mbx_pf(struct e1000_hw *hw, u32 *msg, u16 size, + u16 vf_number) +{ + s32 ret_val; + u16 i; + + DEBUGFUNC("e1000_read_mbx_pf"); + + /* lock the mailbox to prevent pf/vf race condition */ + ret_val = e1000_obtain_mbx_lock_pf(hw, vf_number); + if (ret_val) + goto out_no_read; + + /* copy the message to the mailbox memory buffer */ + for (i = 0; i < size; i++) + msg[i] = E1000_READ_REG_ARRAY(hw, E1000_VMBMEM(vf_number), i); + + /* Acknowledge the message and release buffer */ + E1000_WRITE_REG(hw, E1000_P2VMAILBOX(vf_number), E1000_P2VMAILBOX_ACK); + + /* update stats */ + hw->mbx.stats.msgs_rx++; + +out_no_read: + return ret_val; +} + +/** + * e1000_init_mbx_params_pf - set initial values for pf mailbox + * @hw: pointer to the HW structure + * + * Initializes the hw->mbx struct to correct values for pf mailbox + */ +s32 e1000_init_mbx_params_pf(struct e1000_hw *hw) +{ + struct e1000_mbx_info *mbx = &hw->mbx; + + switch (hw->mac.type) { + case e1000_82576: + case e1000_i350: + case e1000_i354: + mbx->timeout = 0; + mbx->usec_delay = 0; + + mbx->size = E1000_VFMAILBOX_SIZE; + + mbx->ops.read = e1000_read_mbx_pf; + mbx->ops.write = e1000_write_mbx_pf; + mbx->ops.read_posted = e1000_read_posted_mbx; + mbx->ops.write_posted = e1000_write_posted_mbx; + mbx->ops.check_for_msg = e1000_check_for_msg_pf; + mbx->ops.check_for_ack = e1000_check_for_ack_pf; + mbx->ops.check_for_rst = e1000_check_for_rst_pf; + + mbx->stats.msgs_tx = 0; + mbx->stats.msgs_rx = 0; + mbx->stats.reqs = 0; + mbx->stats.acks = 0; + mbx->stats.rsts = 0; + default: + return E1000_SUCCESS; + } +} + diff --git a/drivers/staging/igb_avb/e1000_mbx.h b/drivers/staging/igb_avb/e1000_mbx.h new file mode 100644 index 000000000000..28900216ac25 --- /dev/null +++ b/drivers/staging/igb_avb/e1000_mbx.h @@ -0,0 +1,84 @@ +/******************************************************************************* + + Intel(R) Gigabit Ethernet Linux driver + Copyright(c) 2007-2015 Intel Corporation. + + This program is free software; you can redistribute it and/or modify it + under the terms and conditions of the GNU General Public License, + version 2, as published by the Free Software Foundation. + + This program is distributed in the hope 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. + + The full GNU General Public License is included in this distribution in + the file called "COPYING". + + Contact Information: + Linux NICS + e1000-devel Mailing List + Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + +*******************************************************************************/ + +#ifndef _E1000_MBX_H_ +#define _E1000_MBX_H_ + +#include "e1000_api.h" + +#define E1000_P2VMAILBOX_STS 0x00000001 /* Initiate message send to VF */ +#define E1000_P2VMAILBOX_ACK 0x00000002 /* Ack message recv'd from VF */ +#define E1000_P2VMAILBOX_VFU 0x00000004 /* VF owns the mailbox buffer */ +#define E1000_P2VMAILBOX_PFU 0x00000008 /* PF owns the mailbox buffer */ +#define E1000_P2VMAILBOX_RVFU 0x00000010 /* Reset VFU - used when VF stuck */ + +#define E1000_MBVFICR_VFREQ_MASK 0x000000FF /* bits for VF messages */ +#define E1000_MBVFICR_VFREQ_VF1 0x00000001 /* bit for VF 1 message */ +#define E1000_MBVFICR_VFACK_MASK 0x00FF0000 /* bits for VF acks */ +#define E1000_MBVFICR_VFACK_VF1 0x00010000 /* bit for VF 1 ack */ + +#define E1000_VFMAILBOX_SIZE 16 /* 16 32 bit words - 64 bytes */ + +/* If it's a E1000_VF_* msg then it originates in the VF and is sent to the + * PF. The reverse is true if it is E1000_PF_*. + * Message ACK's are the value or'd with 0xF0000000 + */ +/* Msgs below or'd with this are the ACK */ +#define E1000_VT_MSGTYPE_ACK 0x80000000 +/* Msgs below or'd with this are the NACK */ +#define E1000_VT_MSGTYPE_NACK 0x40000000 +/* Indicates that VF is still clear to send requests */ +#define E1000_VT_MSGTYPE_CTS 0x20000000 +#define E1000_VT_MSGINFO_SHIFT 16 +/* bits 23:16 are used for extra info for certain messages */ +#define E1000_VT_MSGINFO_MASK (0xFF << E1000_VT_MSGINFO_SHIFT) + +#define E1000_VF_RESET 0x01 /* VF requests reset */ +#define E1000_VF_SET_MAC_ADDR 0x02 /* VF requests to set MAC addr */ +#define E1000_VF_SET_MULTICAST 0x03 /* VF requests to set MC addr */ +#define E1000_VF_SET_MULTICAST_COUNT_MASK (0x1F << E1000_VT_MSGINFO_SHIFT) +#define E1000_VF_SET_MULTICAST_OVERFLOW (0x80 << E1000_VT_MSGINFO_SHIFT) +#define E1000_VF_SET_VLAN 0x04 /* VF requests to set VLAN */ +#define E1000_VF_SET_VLAN_ADD (0x01 << E1000_VT_MSGINFO_SHIFT) +#define E1000_VF_SET_LPE 0x05 /* reqs to set VMOLR.LPE */ +#define E1000_VF_SET_PROMISC 0x06 /* reqs to clear VMOLR.ROPE/MPME*/ +#define E1000_VF_SET_PROMISC_UNICAST (0x01 << E1000_VT_MSGINFO_SHIFT) +#define E1000_VF_SET_PROMISC_MULTICAST (0x02 << E1000_VT_MSGINFO_SHIFT) + +#define E1000_PF_CONTROL_MSG 0x0100 /* PF control message */ + +#define E1000_VF_MBX_INIT_TIMEOUT 2000 /* number of retries on mailbox */ +#define E1000_VF_MBX_INIT_DELAY 500 /* microseconds between retries */ + +s32 e1000_read_mbx(struct e1000_hw *, u32 *, u16, u16); +s32 e1000_write_mbx(struct e1000_hw *, u32 *, u16, u16); +s32 e1000_read_posted_mbx(struct e1000_hw *, u32 *, u16, u16); +s32 e1000_write_posted_mbx(struct e1000_hw *, u32 *, u16, u16); +s32 e1000_check_for_msg(struct e1000_hw *, u16); +s32 e1000_check_for_ack(struct e1000_hw *, u16); +s32 e1000_check_for_rst(struct e1000_hw *, u16); +void e1000_init_mbx_ops_generic(struct e1000_hw *hw); +s32 e1000_init_mbx_params_pf(struct e1000_hw *); + +#endif /* _E1000_MBX_H_ */ diff --git a/drivers/staging/igb_avb/e1000_nvm.c b/drivers/staging/igb_avb/e1000_nvm.c new file mode 100644 index 000000000000..c328f40dfa20 --- /dev/null +++ b/drivers/staging/igb_avb/e1000_nvm.c @@ -0,0 +1,973 @@ +/******************************************************************************* + + Intel(R) Gigabit Ethernet Linux driver + Copyright(c) 2007-2015 Intel Corporation. + + This program is free software; you can redistribute it and/or modify it + under the terms and conditions of the GNU General Public License, + version 2, as published by the Free Software Foundation. + + This program is distributed in the hope 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. + + The full GNU General Public License is included in this distribution in + the file called "COPYING". + + Contact Information: + Linux NICS + e1000-devel Mailing List + Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + +*******************************************************************************/ + +#include "e1000_api.h" + +static void e1000_reload_nvm_generic(struct e1000_hw *hw); + +/** + * e1000_init_nvm_ops_generic - Initialize NVM function pointers + * @hw: pointer to the HW structure + * + * Setups up the function pointers to no-op functions + **/ +void e1000_init_nvm_ops_generic(struct e1000_hw *hw) +{ + struct e1000_nvm_info *nvm = &hw->nvm; + DEBUGFUNC("e1000_init_nvm_ops_generic"); + + /* Initialize function pointers */ + nvm->ops.init_params = e1000_null_ops_generic; + nvm->ops.acquire = e1000_null_ops_generic; + nvm->ops.read = e1000_null_read_nvm; + nvm->ops.release = e1000_null_nvm_generic; + nvm->ops.reload = e1000_reload_nvm_generic; + nvm->ops.update = e1000_null_ops_generic; + nvm->ops.valid_led_default = e1000_null_led_default; + nvm->ops.validate = e1000_null_ops_generic; + nvm->ops.write = e1000_null_write_nvm; +} + +/** + * e1000_null_nvm_read - No-op function, return 0 + * @hw: pointer to the HW structure + **/ +s32 e1000_null_read_nvm(struct e1000_hw E1000_UNUSEDARG *hw, + u16 E1000_UNUSEDARG a, u16 E1000_UNUSEDARG b, + u16 E1000_UNUSEDARG *c) +{ + DEBUGFUNC("e1000_null_read_nvm"); + return E1000_SUCCESS; +} + +/** + * e1000_null_nvm_generic - No-op function, return void + * @hw: pointer to the HW structure + **/ +void e1000_null_nvm_generic(struct e1000_hw E1000_UNUSEDARG *hw) +{ + DEBUGFUNC("e1000_null_nvm_generic"); + return; +} + +/** + * e1000_null_led_default - No-op function, return 0 + * @hw: pointer to the HW structure + **/ +s32 e1000_null_led_default(struct e1000_hw E1000_UNUSEDARG *hw, + u16 E1000_UNUSEDARG *data) +{ + DEBUGFUNC("e1000_null_led_default"); + return E1000_SUCCESS; +} + +/** + * e1000_null_write_nvm - No-op function, return 0 + * @hw: pointer to the HW structure + **/ +s32 e1000_null_write_nvm(struct e1000_hw E1000_UNUSEDARG *hw, + u16 E1000_UNUSEDARG a, u16 E1000_UNUSEDARG b, + u16 E1000_UNUSEDARG *c) +{ + DEBUGFUNC("e1000_null_write_nvm"); + return E1000_SUCCESS; +} + +/** + * e1000_raise_eec_clk - Raise EEPROM clock + * @hw: pointer to the HW structure + * @eecd: pointer to the EEPROM + * + * Enable/Raise the EEPROM clock bit. + **/ +static void e1000_raise_eec_clk(struct e1000_hw *hw, u32 *eecd) +{ + *eecd = *eecd | E1000_EECD_SK; + E1000_WRITE_REG(hw, E1000_EECD, *eecd); + E1000_WRITE_FLUSH(hw); + usec_delay(hw->nvm.delay_usec); +} + +/** + * e1000_lower_eec_clk - Lower EEPROM clock + * @hw: pointer to the HW structure + * @eecd: pointer to the EEPROM + * + * Clear/Lower the EEPROM clock bit. + **/ +static void e1000_lower_eec_clk(struct e1000_hw *hw, u32 *eecd) +{ + *eecd = *eecd & ~E1000_EECD_SK; + E1000_WRITE_REG(hw, E1000_EECD, *eecd); + E1000_WRITE_FLUSH(hw); + usec_delay(hw->nvm.delay_usec); +} + +/** + * e1000_shift_out_eec_bits - Shift data bits our to the EEPROM + * @hw: pointer to the HW structure + * @data: data to send to the EEPROM + * @count: number of bits to shift out + * + * We need to shift 'count' bits out to the EEPROM. So, the value in the + * "data" parameter will be shifted out to the EEPROM one bit at a time. + * In order to do this, "data" must be broken down into bits. + **/ +static void e1000_shift_out_eec_bits(struct e1000_hw *hw, u16 data, u16 count) +{ + struct e1000_nvm_info *nvm = &hw->nvm; + u32 eecd = E1000_READ_REG(hw, E1000_EECD); + u32 mask; + + DEBUGFUNC("e1000_shift_out_eec_bits"); + + mask = 0x01 << (count - 1); + if (nvm->type == e1000_nvm_eeprom_spi) + eecd |= E1000_EECD_DO; + + do { + eecd &= ~E1000_EECD_DI; + + if (data & mask) + eecd |= E1000_EECD_DI; + + E1000_WRITE_REG(hw, E1000_EECD, eecd); + E1000_WRITE_FLUSH(hw); + + usec_delay(nvm->delay_usec); + + e1000_raise_eec_clk(hw, &eecd); + e1000_lower_eec_clk(hw, &eecd); + + mask >>= 1; + } while (mask); + + eecd &= ~E1000_EECD_DI; + E1000_WRITE_REG(hw, E1000_EECD, eecd); +} + +/** + * e1000_shift_in_eec_bits - Shift data bits in from the EEPROM + * @hw: pointer to the HW structure + * @count: number of bits to shift in + * + * In order to read a register from the EEPROM, we need to shift 'count' bits + * in from the EEPROM. Bits are "shifted in" by raising the clock input to + * the EEPROM (setting the SK bit), and then reading the value of the data out + * "DO" bit. During this "shifting in" process the data in "DI" bit should + * always be clear. + **/ +static u16 e1000_shift_in_eec_bits(struct e1000_hw *hw, u16 count) +{ + u32 eecd; + u32 i; + u16 data; + + DEBUGFUNC("e1000_shift_in_eec_bits"); + + eecd = E1000_READ_REG(hw, E1000_EECD); + + eecd &= ~(E1000_EECD_DO | E1000_EECD_DI); + data = 0; + + for (i = 0; i < count; i++) { + data <<= 1; + e1000_raise_eec_clk(hw, &eecd); + + eecd = E1000_READ_REG(hw, E1000_EECD); + + eecd &= ~E1000_EECD_DI; + if (eecd & E1000_EECD_DO) + data |= 1; + + e1000_lower_eec_clk(hw, &eecd); + } + + return data; +} + +/** + * e1000_poll_eerd_eewr_done - Poll for EEPROM read/write completion + * @hw: pointer to the HW structure + * @ee_reg: EEPROM flag for polling + * + * Polls the EEPROM status bit for either read or write completion based + * upon the value of 'ee_reg'. + **/ +s32 e1000_poll_eerd_eewr_done(struct e1000_hw *hw, int ee_reg) +{ + u32 attempts = 100000; + u32 i, reg = 0; + + DEBUGFUNC("e1000_poll_eerd_eewr_done"); + + for (i = 0; i < attempts; i++) { + if (ee_reg == E1000_NVM_POLL_READ) + reg = E1000_READ_REG(hw, E1000_EERD); + else + reg = E1000_READ_REG(hw, E1000_EEWR); + + if (reg & E1000_NVM_RW_REG_DONE) + return E1000_SUCCESS; + + usec_delay(5); + } + + return -E1000_ERR_NVM; +} + +/** + * e1000_acquire_nvm_generic - Generic request for access to EEPROM + * @hw: pointer to the HW structure + * + * Set the EEPROM access request bit and wait for EEPROM access grant bit. + * Return successful if access grant bit set, else clear the request for + * EEPROM access and return -E1000_ERR_NVM (-1). + **/ +s32 e1000_acquire_nvm_generic(struct e1000_hw *hw) +{ + u32 eecd = E1000_READ_REG(hw, E1000_EECD); + s32 timeout = E1000_NVM_GRANT_ATTEMPTS; + + DEBUGFUNC("e1000_acquire_nvm_generic"); + + E1000_WRITE_REG(hw, E1000_EECD, eecd | E1000_EECD_REQ); + eecd = E1000_READ_REG(hw, E1000_EECD); + + while (timeout) { + if (eecd & E1000_EECD_GNT) + break; + usec_delay(5); + eecd = E1000_READ_REG(hw, E1000_EECD); + timeout--; + } + + if (!timeout) { + eecd &= ~E1000_EECD_REQ; + E1000_WRITE_REG(hw, E1000_EECD, eecd); + DEBUGOUT("Could not acquire NVM grant\n"); + return -E1000_ERR_NVM; + } + + return E1000_SUCCESS; +} + +/** + * e1000_standby_nvm - Return EEPROM to standby state + * @hw: pointer to the HW structure + * + * Return the EEPROM to a standby state. + **/ +static void e1000_standby_nvm(struct e1000_hw *hw) +{ + struct e1000_nvm_info *nvm = &hw->nvm; + u32 eecd = E1000_READ_REG(hw, E1000_EECD); + + DEBUGFUNC("e1000_standby_nvm"); + + if (nvm->type == e1000_nvm_eeprom_spi) { + /* Toggle CS to flush commands */ + eecd |= E1000_EECD_CS; + E1000_WRITE_REG(hw, E1000_EECD, eecd); + E1000_WRITE_FLUSH(hw); + usec_delay(nvm->delay_usec); + eecd &= ~E1000_EECD_CS; + E1000_WRITE_REG(hw, E1000_EECD, eecd); + E1000_WRITE_FLUSH(hw); + usec_delay(nvm->delay_usec); + } +} + +/** + * e1000_stop_nvm - Terminate EEPROM command + * @hw: pointer to the HW structure + * + * Terminates the current command by inverting the EEPROM's chip select pin. + **/ +static void e1000_stop_nvm(struct e1000_hw *hw) +{ + u32 eecd; + + DEBUGFUNC("e1000_stop_nvm"); + + eecd = E1000_READ_REG(hw, E1000_EECD); + if (hw->nvm.type == e1000_nvm_eeprom_spi) { + /* Pull CS high */ + eecd |= E1000_EECD_CS; + e1000_lower_eec_clk(hw, &eecd); + } +} + +/** + * e1000_release_nvm_generic - Release exclusive access to EEPROM + * @hw: pointer to the HW structure + * + * Stop any current commands to the EEPROM and clear the EEPROM request bit. + **/ +void e1000_release_nvm_generic(struct e1000_hw *hw) +{ + u32 eecd; + + DEBUGFUNC("e1000_release_nvm_generic"); + + e1000_stop_nvm(hw); + + eecd = E1000_READ_REG(hw, E1000_EECD); + eecd &= ~E1000_EECD_REQ; + E1000_WRITE_REG(hw, E1000_EECD, eecd); +} + +/** + * e1000_ready_nvm_eeprom - Prepares EEPROM for read/write + * @hw: pointer to the HW structure + * + * Setups the EEPROM for reading and writing. + **/ +static s32 e1000_ready_nvm_eeprom(struct e1000_hw *hw) +{ + struct e1000_nvm_info *nvm = &hw->nvm; + u32 eecd = E1000_READ_REG(hw, E1000_EECD); + u8 spi_stat_reg; + + DEBUGFUNC("e1000_ready_nvm_eeprom"); + + if (nvm->type == e1000_nvm_eeprom_spi) { + u16 timeout = NVM_MAX_RETRY_SPI; + + /* Clear SK and CS */ + eecd &= ~(E1000_EECD_CS | E1000_EECD_SK); + E1000_WRITE_REG(hw, E1000_EECD, eecd); + E1000_WRITE_FLUSH(hw); + usec_delay(1); + + /* Read "Status Register" repeatedly until the LSB is cleared. + * The EEPROM will signal that the command has been completed + * by clearing bit 0 of the internal status register. If it's + * not cleared within 'timeout', then error out. + */ + while (timeout) { + e1000_shift_out_eec_bits(hw, NVM_RDSR_OPCODE_SPI, + hw->nvm.opcode_bits); + spi_stat_reg = (u8)e1000_shift_in_eec_bits(hw, 8); + if (!(spi_stat_reg & NVM_STATUS_RDY_SPI)) + break; + + usec_delay(5); + e1000_standby_nvm(hw); + timeout--; + } + + if (!timeout) { + DEBUGOUT("SPI NVM Status error\n"); + return -E1000_ERR_NVM; + } + } + + return E1000_SUCCESS; +} + +/** + * e1000_read_nvm_spi - Read EEPROM's using SPI + * @hw: pointer to the HW structure + * @offset: offset of word in the EEPROM to read + * @words: number of words to read + * @data: word read from the EEPROM + * + * Reads a 16 bit word from the EEPROM. + **/ +s32 e1000_read_nvm_spi(struct e1000_hw *hw, u16 offset, u16 words, u16 *data) +{ + struct e1000_nvm_info *nvm = &hw->nvm; + u32 i = 0; + s32 ret_val; + u16 word_in; + u8 read_opcode = NVM_READ_OPCODE_SPI; + + DEBUGFUNC("e1000_read_nvm_spi"); + + /* A check for invalid values: offset too large, too many words, + * and not enough words. + */ + if ((offset >= nvm->word_size) || (words > (nvm->word_size - offset)) || + (words == 0)) { + DEBUGOUT("nvm parameter(s) out of bounds\n"); + return -E1000_ERR_NVM; + } + + ret_val = nvm->ops.acquire(hw); + if (ret_val) + return ret_val; + + ret_val = e1000_ready_nvm_eeprom(hw); + if (ret_val) + goto release; + + e1000_standby_nvm(hw); + + if ((nvm->address_bits == 8) && (offset >= 128)) + read_opcode |= NVM_A8_OPCODE_SPI; + + /* Send the READ command (opcode + addr) */ + e1000_shift_out_eec_bits(hw, read_opcode, nvm->opcode_bits); + e1000_shift_out_eec_bits(hw, (u16)(offset*2), nvm->address_bits); + + /* Read the data. SPI NVMs increment the address with each byte + * read and will roll over if reading beyond the end. This allows + * us to read the whole NVM from any offset + */ + for (i = 0; i < words; i++) { + word_in = e1000_shift_in_eec_bits(hw, 16); + data[i] = (word_in >> 8) | (word_in << 8); + } + +release: + nvm->ops.release(hw); + + return ret_val; +} + +/** + * e1000_read_nvm_eerd - Reads EEPROM using EERD register + * @hw: pointer to the HW structure + * @offset: offset of word in the EEPROM to read + * @words: number of words to read + * @data: word read from the EEPROM + * + * Reads a 16 bit word from the EEPROM using the EERD register. + **/ +s32 e1000_read_nvm_eerd(struct e1000_hw *hw, u16 offset, u16 words, u16 *data) +{ + struct e1000_nvm_info *nvm = &hw->nvm; + u32 i, eerd = 0; + s32 ret_val = E1000_SUCCESS; + + DEBUGFUNC("e1000_read_nvm_eerd"); + + /* A check for invalid values: offset too large, too many words, + * too many words for the offset, and not enough words. + */ + if ((offset >= nvm->word_size) || (words > (nvm->word_size - offset)) || + (words == 0)) { + DEBUGOUT("nvm parameter(s) out of bounds\n"); + return -E1000_ERR_NVM; + } + + for (i = 0; i < words; i++) { + eerd = ((offset+i) << E1000_NVM_RW_ADDR_SHIFT) + + E1000_NVM_RW_REG_START; + + E1000_WRITE_REG(hw, E1000_EERD, eerd); + ret_val = e1000_poll_eerd_eewr_done(hw, E1000_NVM_POLL_READ); + if (ret_val) + break; + + data[i] = (E1000_READ_REG(hw, E1000_EERD) >> + E1000_NVM_RW_REG_DATA); + } + + if (ret_val) + DEBUGOUT1("NVM read error: %d\n", ret_val); + + return ret_val; +} + +/** + * e1000_write_nvm_spi - Write to EEPROM using SPI + * @hw: pointer to the HW structure + * @offset: offset within the EEPROM to be written to + * @words: number of words to write + * @data: 16 bit word(s) to be written to the EEPROM + * + * Writes data to EEPROM at offset using SPI interface. + * + * If e1000_update_nvm_checksum is not called after this function , the + * EEPROM will most likely contain an invalid checksum. + **/ +s32 e1000_write_nvm_spi(struct e1000_hw *hw, u16 offset, u16 words, u16 *data) +{ + struct e1000_nvm_info *nvm = &hw->nvm; + s32 ret_val = -E1000_ERR_NVM; + u16 widx = 0; + + DEBUGFUNC("e1000_write_nvm_spi"); + + /* A check for invalid values: offset too large, too many words, + * and not enough words. + */ + if ((offset >= nvm->word_size) || (words > (nvm->word_size - offset)) || + (words == 0)) { + DEBUGOUT("nvm parameter(s) out of bounds\n"); + return -E1000_ERR_NVM; + } + + while (widx < words) { + u8 write_opcode = NVM_WRITE_OPCODE_SPI; + + ret_val = nvm->ops.acquire(hw); + if (ret_val) + return ret_val; + + ret_val = e1000_ready_nvm_eeprom(hw); + if (ret_val) { + nvm->ops.release(hw); + return ret_val; + } + + e1000_standby_nvm(hw); + + /* Send the WRITE ENABLE command (8 bit opcode) */ + e1000_shift_out_eec_bits(hw, NVM_WREN_OPCODE_SPI, + nvm->opcode_bits); + + e1000_standby_nvm(hw); + + /* Some SPI eeproms use the 8th address bit embedded in the + * opcode + */ + if ((nvm->address_bits == 8) && (offset >= 128)) + write_opcode |= NVM_A8_OPCODE_SPI; + + /* Send the Write command (8-bit opcode + addr) */ + e1000_shift_out_eec_bits(hw, write_opcode, nvm->opcode_bits); + e1000_shift_out_eec_bits(hw, (u16)((offset + widx) * 2), + nvm->address_bits); + + /* Loop to allow for up to whole page write of eeprom */ + while (widx < words) { + u16 word_out = data[widx]; + word_out = (word_out >> 8) | (word_out << 8); + e1000_shift_out_eec_bits(hw, word_out, 16); + widx++; + + if ((((offset + widx) * 2) % nvm->page_size) == 0) { + e1000_standby_nvm(hw); + break; + } + } + msec_delay(10); + nvm->ops.release(hw); + } + + return ret_val; +} + +/** + * e1000_read_pba_string_generic - Read device part number + * @hw: pointer to the HW structure + * @pba_num: pointer to device part number + * @pba_num_size: size of part number buffer + * + * Reads the product board assembly (PBA) number from the EEPROM and stores + * the value in pba_num. + **/ +s32 e1000_read_pba_string_generic(struct e1000_hw *hw, u8 *pba_num, + u32 pba_num_size) +{ + s32 ret_val; + u16 nvm_data; + u16 pba_ptr; + u16 offset; + u16 length; + + DEBUGFUNC("e1000_read_pba_string_generic"); + + if ((hw->mac.type >= e1000_i210) && + !e1000_get_flash_presence_i210(hw)) { + DEBUGOUT("Flashless no PBA string\n"); + return -E1000_ERR_NVM_PBA_SECTION; + } + + if (pba_num == NULL) { + DEBUGOUT("PBA string buffer was null\n"); + return -E1000_ERR_INVALID_ARGUMENT; + } + + ret_val = hw->nvm.ops.read(hw, NVM_PBA_OFFSET_0, 1, &nvm_data); + if (ret_val) { + DEBUGOUT("NVM Read Error\n"); + return ret_val; + } + + ret_val = hw->nvm.ops.read(hw, NVM_PBA_OFFSET_1, 1, &pba_ptr); + if (ret_val) { + DEBUGOUT("NVM Read Error\n"); + return ret_val; + } + + /* if nvm_data is not ptr guard the PBA must be in legacy format which + * means pba_ptr is actually our second data word for the PBA number + * and we can decode it into an ascii string + */ + if (nvm_data != NVM_PBA_PTR_GUARD) { + DEBUGOUT("NVM PBA number is not stored as string\n"); + + /* make sure callers buffer is big enough to store the PBA */ + if (pba_num_size < E1000_PBANUM_LENGTH) { + DEBUGOUT("PBA string buffer too small\n"); + return E1000_ERR_NO_SPACE; + } + + /* extract hex string from data and pba_ptr */ + pba_num[0] = (nvm_data >> 12) & 0xF; + pba_num[1] = (nvm_data >> 8) & 0xF; + pba_num[2] = (nvm_data >> 4) & 0xF; + pba_num[3] = nvm_data & 0xF; + pba_num[4] = (pba_ptr >> 12) & 0xF; + pba_num[5] = (pba_ptr >> 8) & 0xF; + pba_num[6] = '-'; + pba_num[7] = 0; + pba_num[8] = (pba_ptr >> 4) & 0xF; + pba_num[9] = pba_ptr & 0xF; + + /* put a null character on the end of our string */ + pba_num[10] = '\0'; + + /* switch all the data but the '-' to hex char */ + for (offset = 0; offset < 10; offset++) { + if (pba_num[offset] < 0xA) + pba_num[offset] += '0'; + else if (pba_num[offset] < 0x10) + pba_num[offset] += 'A' - 0xA; + } + + return E1000_SUCCESS; + } + + ret_val = hw->nvm.ops.read(hw, pba_ptr, 1, &length); + if (ret_val) { + DEBUGOUT("NVM Read Error\n"); + return ret_val; + } + + if (length == 0xFFFF || length == 0) { + DEBUGOUT("NVM PBA number section invalid length\n"); + return -E1000_ERR_NVM_PBA_SECTION; + } + /* check if pba_num buffer is big enough */ + if (pba_num_size < (((u32)length * 2) - 1)) { + DEBUGOUT("PBA string buffer too small\n"); + return -E1000_ERR_NO_SPACE; + } + + /* trim pba length from start of string */ + pba_ptr++; + length--; + + for (offset = 0; offset < length; offset++) { + ret_val = hw->nvm.ops.read(hw, pba_ptr + offset, 1, &nvm_data); + if (ret_val) { + DEBUGOUT("NVM Read Error\n"); + return ret_val; + } + pba_num[offset * 2] = (u8)(nvm_data >> 8); + pba_num[(offset * 2) + 1] = (u8)(nvm_data & 0xFF); + } + pba_num[offset * 2] = '\0'; + + return E1000_SUCCESS; +} + +/** + * e1000_read_pba_length_generic - Read device part number length + * @hw: pointer to the HW structure + * @pba_num_size: size of part number buffer + * + * Reads the product board assembly (PBA) number length from the EEPROM and + * stores the value in pba_num_size. + **/ +s32 e1000_read_pba_length_generic(struct e1000_hw *hw, u32 *pba_num_size) +{ + s32 ret_val; + u16 nvm_data; + u16 pba_ptr; + u16 length; + + DEBUGFUNC("e1000_read_pba_length_generic"); + + if (pba_num_size == NULL) { + DEBUGOUT("PBA buffer size was null\n"); + return -E1000_ERR_INVALID_ARGUMENT; + } + + ret_val = hw->nvm.ops.read(hw, NVM_PBA_OFFSET_0, 1, &nvm_data); + if (ret_val) { + DEBUGOUT("NVM Read Error\n"); + return ret_val; + } + + ret_val = hw->nvm.ops.read(hw, NVM_PBA_OFFSET_1, 1, &pba_ptr); + if (ret_val) { + DEBUGOUT("NVM Read Error\n"); + return ret_val; + } + + /* if data is not ptr guard the PBA must be in legacy format */ + if (nvm_data != NVM_PBA_PTR_GUARD) { + *pba_num_size = E1000_PBANUM_LENGTH; + return E1000_SUCCESS; + } + + ret_val = hw->nvm.ops.read(hw, pba_ptr, 1, &length); + if (ret_val) { + DEBUGOUT("NVM Read Error\n"); + return ret_val; + } + + if (length == 0xFFFF || length == 0) { + DEBUGOUT("NVM PBA number section invalid length\n"); + return -E1000_ERR_NVM_PBA_SECTION; + } + + /* Convert from length in u16 values to u8 chars, add 1 for NULL, + * and subtract 2 because length field is included in length. + */ + *pba_num_size = ((u32)length * 2) - 1; + + return E1000_SUCCESS; +} + +/** + * e1000_read_mac_addr_generic - Read device MAC address + * @hw: pointer to the HW structure + * + * Reads the device MAC address from the EEPROM and stores the value. + * Since devices with two ports use the same EEPROM, we increment the + * last bit in the MAC address for the second port. + **/ +s32 e1000_read_mac_addr_generic(struct e1000_hw *hw) +{ + u32 rar_high; + u32 rar_low; + u16 i; + + rar_high = E1000_READ_REG(hw, E1000_RAH(0)); + rar_low = E1000_READ_REG(hw, E1000_RAL(0)); + + for (i = 0; i < E1000_RAL_MAC_ADDR_LEN; i++) + hw->mac.perm_addr[i] = (u8)(rar_low >> (i*8)); + + for (i = 0; i < E1000_RAH_MAC_ADDR_LEN; i++) + hw->mac.perm_addr[i+4] = (u8)(rar_high >> (i*8)); + + for (i = 0; i < ETH_ADDR_LEN; i++) + hw->mac.addr[i] = hw->mac.perm_addr[i]; + + return E1000_SUCCESS; +} + +/** + * e1000_validate_nvm_checksum_generic - Validate EEPROM checksum + * @hw: pointer to the HW structure + * + * Calculates the EEPROM checksum by reading/adding each word of the EEPROM + * and then verifies that the sum of the EEPROM is equal to 0xBABA. + **/ +s32 e1000_validate_nvm_checksum_generic(struct e1000_hw *hw) +{ + s32 ret_val; + u16 checksum = 0; + u16 i, nvm_data; + + DEBUGFUNC("e1000_validate_nvm_checksum_generic"); + + for (i = 0; i < (NVM_CHECKSUM_REG + 1); i++) { + ret_val = hw->nvm.ops.read(hw, i, 1, &nvm_data); + if (ret_val) { + DEBUGOUT("NVM Read Error\n"); + return ret_val; + } + checksum += nvm_data; + } + + if (checksum != (u16) NVM_SUM) { + DEBUGOUT("NVM Checksum Invalid\n"); + return -E1000_ERR_NVM; + } + + return E1000_SUCCESS; +} + +/** + * e1000_update_nvm_checksum_generic - Update EEPROM checksum + * @hw: pointer to the HW structure + * + * Updates the EEPROM checksum by reading/adding each word of the EEPROM + * up to the checksum. Then calculates the EEPROM checksum and writes the + * value to the EEPROM. + **/ +s32 e1000_update_nvm_checksum_generic(struct e1000_hw *hw) +{ + s32 ret_val; + u16 checksum = 0; + u16 i, nvm_data; + + DEBUGFUNC("e1000_update_nvm_checksum"); + + for (i = 0; i < NVM_CHECKSUM_REG; i++) { + ret_val = hw->nvm.ops.read(hw, i, 1, &nvm_data); + if (ret_val) { + DEBUGOUT("NVM Read Error while updating checksum.\n"); + return ret_val; + } + checksum += nvm_data; + } + checksum = (u16) NVM_SUM - checksum; + ret_val = hw->nvm.ops.write(hw, NVM_CHECKSUM_REG, 1, &checksum); + if (ret_val) + DEBUGOUT("NVM Write Error while updating checksum.\n"); + + return ret_val; +} + +/** + * e1000_reload_nvm_generic - Reloads EEPROM + * @hw: pointer to the HW structure + * + * Reloads the EEPROM by setting the "Reinitialize from EEPROM" bit in the + * extended control register. + **/ +static void e1000_reload_nvm_generic(struct e1000_hw *hw) +{ + u32 ctrl_ext; + + DEBUGFUNC("e1000_reload_nvm_generic"); + + usec_delay(10); + ctrl_ext = E1000_READ_REG(hw, E1000_CTRL_EXT); + ctrl_ext |= E1000_CTRL_EXT_EE_RST; + E1000_WRITE_REG(hw, E1000_CTRL_EXT, ctrl_ext); + E1000_WRITE_FLUSH(hw); +} + +/** + * e1000_get_fw_version - Get firmware version information + * @hw: pointer to the HW structure + * @fw_vers: pointer to output version structure + * + * unsupported/not present features return 0 in version structure + **/ +void e1000_get_fw_version(struct e1000_hw *hw, struct e1000_fw_version *fw_vers) +{ + u16 eeprom_verh, eeprom_verl, etrack_test, fw_version; + u8 q, hval, rem, result; + u16 comb_verh, comb_verl, comb_offset; + + memset(fw_vers, 0, sizeof(struct e1000_fw_version)); + + /* basic eeprom version numbers, bits used vary by part and by tool + * used to create the nvm images */ + /* Check which data format we have */ + switch (hw->mac.type) { + case e1000_i211: + e1000_read_invm_version(hw, fw_vers); + return; + case e1000_82575: + case e1000_82576: + case e1000_82580: + hw->nvm.ops.read(hw, NVM_ETRACK_HIWORD, 1, &etrack_test); + /* Use this format, unless EETRACK ID exists, + * then use alternate format + */ + if ((etrack_test & NVM_MAJOR_MASK) != NVM_ETRACK_VALID) { + hw->nvm.ops.read(hw, NVM_VERSION, 1, &fw_version); + fw_vers->eep_major = (fw_version & NVM_MAJOR_MASK) + >> NVM_MAJOR_SHIFT; + fw_vers->eep_minor = (fw_version & NVM_MINOR_MASK) + >> NVM_MINOR_SHIFT; + fw_vers->eep_build = (fw_version & NVM_IMAGE_ID_MASK); + goto etrack_id; + } + break; + case e1000_i210: + if (!(e1000_get_flash_presence_i210(hw))) { + e1000_read_invm_version(hw, fw_vers); + return; + } + /* fall through */ + case e1000_i350: + hw->nvm.ops.read(hw, NVM_ETRACK_HIWORD, 1, &etrack_test); + /* find combo image version */ + hw->nvm.ops.read(hw, NVM_COMB_VER_PTR, 1, &comb_offset); + if ((comb_offset != 0x0) && + (comb_offset != NVM_VER_INVALID)) { + + hw->nvm.ops.read(hw, (NVM_COMB_VER_OFF + comb_offset + + 1), 1, &comb_verh); + hw->nvm.ops.read(hw, (NVM_COMB_VER_OFF + comb_offset), + 1, &comb_verl); + + /* get Option Rom version if it exists and is valid */ + if ((comb_verh && comb_verl) && + ((comb_verh != NVM_VER_INVALID) && + (comb_verl != NVM_VER_INVALID))) { + + fw_vers->or_valid = true; + fw_vers->or_major = + comb_verl >> NVM_COMB_VER_SHFT; + fw_vers->or_build = + (comb_verl << NVM_COMB_VER_SHFT) + | (comb_verh >> NVM_COMB_VER_SHFT); + fw_vers->or_patch = + comb_verh & NVM_COMB_VER_MASK; + } + } + break; + default: + hw->nvm.ops.read(hw, NVM_ETRACK_HIWORD, 1, &etrack_test); + return; + } + hw->nvm.ops.read(hw, NVM_VERSION, 1, &fw_version); + fw_vers->eep_major = (fw_version & NVM_MAJOR_MASK) + >> NVM_MAJOR_SHIFT; + + /* check for old style version format in newer images*/ + if ((fw_version & NVM_NEW_DEC_MASK) == 0x0) { + eeprom_verl = (fw_version & NVM_COMB_VER_MASK); + } else { + eeprom_verl = (fw_version & NVM_MINOR_MASK) + >> NVM_MINOR_SHIFT; + } + /* Convert minor value to hex before assigning to output struct + * Val to be converted will not be higher than 99, per tool output + */ + q = eeprom_verl / NVM_HEX_CONV; + hval = q * NVM_HEX_TENS; + rem = eeprom_verl % NVM_HEX_CONV; + result = hval + rem; + fw_vers->eep_minor = result; + +etrack_id: + if ((etrack_test & NVM_MAJOR_MASK) == NVM_ETRACK_VALID) { + hw->nvm.ops.read(hw, NVM_ETRACK_WORD, 1, &eeprom_verl); + hw->nvm.ops.read(hw, (NVM_ETRACK_WORD + 1), 1, &eeprom_verh); + fw_vers->etrack_id = (eeprom_verh << NVM_ETRACK_SHIFT) + | eeprom_verl; + } else if ((etrack_test & NVM_ETRACK_VALID) == 0) { + hw->nvm.ops.read(hw, NVM_ETRACK_WORD, 1, &eeprom_verh); + hw->nvm.ops.read(hw, (NVM_ETRACK_WORD + 1), 1, &eeprom_verl); + fw_vers->etrack_id = (eeprom_verh << NVM_ETRACK_SHIFT) | + eeprom_verl; + } +} + diff --git a/drivers/staging/igb_avb/e1000_nvm.h b/drivers/staging/igb_avb/e1000_nvm.h new file mode 100644 index 000000000000..a4263113d72d --- /dev/null +++ b/drivers/staging/igb_avb/e1000_nvm.h @@ -0,0 +1,70 @@ +/******************************************************************************* + + Intel(R) Gigabit Ethernet Linux driver + Copyright(c) 2007-2015 Intel Corporation. + + This program is free software; you can redistribute it and/or modify it + under the terms and conditions of the GNU General Public License, + version 2, as published by the Free Software Foundation. + + This program is distributed in the hope 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. + + The full GNU General Public License is included in this distribution in + the file called "COPYING". + + Contact Information: + Linux NICS + e1000-devel Mailing List + Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + +*******************************************************************************/ + +#ifndef _E1000_NVM_H_ +#define _E1000_NVM_H_ + +struct e1000_fw_version { + u32 etrack_id; + u16 eep_major; + u16 eep_minor; + u16 eep_build; + + u8 invm_major; + u8 invm_minor; + u8 invm_img_type; + + bool or_valid; + u16 or_major; + u16 or_build; + u16 or_patch; +}; + +void e1000_init_nvm_ops_generic(struct e1000_hw *hw); +s32 e1000_null_read_nvm(struct e1000_hw *hw, u16 a, u16 b, u16 *c); +void e1000_null_nvm_generic(struct e1000_hw *hw); +s32 e1000_null_led_default(struct e1000_hw *hw, u16 *data); +s32 e1000_null_write_nvm(struct e1000_hw *hw, u16 a, u16 b, u16 *c); +s32 e1000_acquire_nvm_generic(struct e1000_hw *hw); + +s32 e1000_poll_eerd_eewr_done(struct e1000_hw *hw, int ee_reg); +s32 e1000_read_mac_addr_generic(struct e1000_hw *hw); +s32 e1000_read_pba_string_generic(struct e1000_hw *hw, u8 *pba_num, + u32 pba_num_size); +s32 e1000_read_pba_length_generic(struct e1000_hw *hw, u32 *pba_num_size); +s32 e1000_read_nvm_spi(struct e1000_hw *hw, u16 offset, u16 words, u16 *data); +s32 e1000_read_nvm_eerd(struct e1000_hw *hw, u16 offset, u16 words, + u16 *data); +s32 e1000_valid_led_default_generic(struct e1000_hw *hw, u16 *data); +s32 e1000_validate_nvm_checksum_generic(struct e1000_hw *hw); +s32 e1000_write_nvm_spi(struct e1000_hw *hw, u16 offset, u16 words, + u16 *data); +s32 e1000_update_nvm_checksum_generic(struct e1000_hw *hw); +void e1000_release_nvm_generic(struct e1000_hw *hw); +void e1000_get_fw_version(struct e1000_hw *hw, + struct e1000_fw_version *fw_vers); + +#define E1000_STM_OPCODE 0xDB00 + +#endif diff --git a/drivers/staging/igb_avb/e1000_osdep.h b/drivers/staging/igb_avb/e1000_osdep.h new file mode 100644 index 000000000000..3c6b79586cf8 --- /dev/null +++ b/drivers/staging/igb_avb/e1000_osdep.h @@ -0,0 +1,141 @@ +/******************************************************************************* + + Intel(R) Gigabit Ethernet Linux driver + Copyright(c) 2007-2015 Intel Corporation. + + This program is free software; you can redistribute it and/or modify it + under the terms and conditions of the GNU General Public License, + version 2, as published by the Free Software Foundation. + + This program is distributed in the hope 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. + + The full GNU General Public License is included in this distribution in + the file called "COPYING". + + Contact Information: + Linux NICS + e1000-devel Mailing List + Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + +*******************************************************************************/ + +/* glue for the OS independent part of e1000 + * includes register access macros + */ + +#ifndef _E1000_OSDEP_H_ +#define _E1000_OSDEP_H_ + +#include +#include +#include +#include +#include +#include "kcompat.h" + +#define usec_delay(x) udelay(x) +#define usec_delay_irq(x) udelay(x) +#ifndef msec_delay +#define msec_delay(x) do { \ + /* Don't mdelay in interrupt context! */ \ + if (in_interrupt()) \ + BUG(); \ + else \ + msleep(x); \ +} while (0) + +/* Some workarounds require millisecond delays and are run during interrupt + * context. Most notably, when establishing link, the phy may need tweaking + * but cannot process phy register reads/writes faster than millisecond + * intervals...and we establish link due to a "link status change" interrupt. + */ +#define msec_delay_irq(x) mdelay(x) + +#define E1000_READ_REG(x, y) e1000_read_reg(x, y) +#endif + +#define PCI_COMMAND_REGISTER PCI_COMMAND +#define CMD_MEM_WRT_INVALIDATE PCI_COMMAND_INVALIDATE +#define ETH_ADDR_LEN ETH_ALEN + +#ifdef __BIG_ENDIAN +#define E1000_BIG_ENDIAN __BIG_ENDIAN +#endif + +#ifdef DEBUG +#define DEBUGOUT(S) pr_debug(S) +#define DEBUGOUT1(S, A...) pr_debug(S, ## A) +#else +#define DEBUGOUT(S) +#define DEBUGOUT1(S, A...) +#endif + +#ifdef DEBUG_FUNC +#define DEBUGFUNC(F) DEBUGOUT(F "\n") +#else +#define DEBUGFUNC(F) +#endif +#define DEBUGOUT2 DEBUGOUT1 +#define DEBUGOUT3 DEBUGOUT2 +#define DEBUGOUT7 DEBUGOUT3 + +#define E1000_REGISTER(a, reg) reg + +/* forward declaration */ +struct e1000_hw; + +/* write operations, indexed using DWORDS */ +#define E1000_WRITE_REG(hw, reg, val) \ +do { \ + u8 __iomem *hw_addr = READ_ONCE((hw)->hw_addr); \ + if (!E1000_REMOVED(hw_addr)) \ + writel((val), &hw_addr[(reg)]); \ +} while (0) + +u32 e1000_read_reg(struct e1000_hw *hw, u32 reg); + +#define E1000_WRITE_REG_ARRAY(hw, reg, idx, val) \ + E1000_WRITE_REG((hw), (reg) + ((idx) << 2), (val)) + +#define E1000_READ_REG_ARRAY(hw, reg, idx) ( \ + e1000_read_reg((hw), (reg) + ((idx) << 2))) + +#define E1000_READ_REG_ARRAY_DWORD E1000_READ_REG_ARRAY +#define E1000_WRITE_REG_ARRAY_DWORD E1000_WRITE_REG_ARRAY + +#define E1000_WRITE_REG_ARRAY_WORD(a, reg, offset, value) ( \ + writew((value), ((a)->hw_addr + E1000_REGISTER(a, reg) + \ + ((offset) << 1)))) + +#define E1000_READ_REG_ARRAY_WORD(a, reg, offset) ( \ + readw((a)->hw_addr + E1000_REGISTER(a, reg) + ((offset) << 1))) + +#define E1000_WRITE_REG_ARRAY_BYTE(a, reg, offset, value) ( \ + writeb((value), ((a)->hw_addr + E1000_REGISTER(a, reg) + (offset)))) + +#define E1000_READ_REG_ARRAY_BYTE(a, reg, offset) ( \ + readb((a)->hw_addr + E1000_REGISTER(a, reg) + (offset))) + +#define E1000_WRITE_REG_IO(a, reg, offset) do { \ + outl(reg, ((a)->io_base)); \ + outl(offset, ((a)->io_base + 4)); \ + } while (0) + +#define E1000_WRITE_FLUSH(a) E1000_READ_REG(a, E1000_STATUS) + +#define E1000_WRITE_FLASH_REG(a, reg, value) ( \ + writel((value), ((a)->flash_address + reg))) + +#define E1000_WRITE_FLASH_REG16(a, reg, value) ( \ + writew((value), ((a)->flash_address + reg))) + +#define E1000_READ_FLASH_REG(a, reg) (readl((a)->flash_address + reg)) + +#define E1000_READ_FLASH_REG16(a, reg) (readw((a)->flash_address + reg)) + +#define E1000_REMOVED(h) unlikely(!(h)) + +#endif /* _E1000_OSDEP_H_ */ diff --git a/drivers/staging/igb_avb/e1000_phy.c b/drivers/staging/igb_avb/e1000_phy.c new file mode 100644 index 000000000000..0534e4a34af2 --- /dev/null +++ b/drivers/staging/igb_avb/e1000_phy.c @@ -0,0 +1,3396 @@ +/******************************************************************************* + + Intel(R) Gigabit Ethernet Linux driver + Copyright(c) 2007-2015 Intel Corporation. + + This program is free software; you can redistribute it and/or modify it + under the terms and conditions of the GNU General Public License, + version 2, as published by the Free Software Foundation. + + This program is distributed in the hope 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. + + The full GNU General Public License is included in this distribution in + the file called "COPYING". + + Contact Information: + Linux NICS + e1000-devel Mailing List + Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + +*******************************************************************************/ + +#include "e1000_api.h" + +static s32 e1000_wait_autoneg(struct e1000_hw *hw); +/* Cable length tables */ +static const u16 e1000_m88_cable_length_table[] = { + 0, 50, 80, 110, 140, 140, E1000_CABLE_LENGTH_UNDEFINED }; +#define M88E1000_CABLE_LENGTH_TABLE_SIZE \ + (sizeof(e1000_m88_cable_length_table) / \ + sizeof(e1000_m88_cable_length_table[0])) + +static const u16 e1000_igp_2_cable_length_table[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 3, 5, 8, 11, 13, 16, 18, 21, 0, 0, 0, 3, + 6, 10, 13, 16, 19, 23, 26, 29, 32, 35, 38, 41, 6, 10, 14, 18, 22, + 26, 30, 33, 37, 41, 44, 48, 51, 54, 58, 61, 21, 26, 31, 35, 40, + 44, 49, 53, 57, 61, 65, 68, 72, 75, 79, 82, 40, 45, 51, 56, 61, + 66, 70, 75, 79, 83, 87, 91, 94, 98, 101, 104, 60, 66, 72, 77, 82, + 87, 92, 96, 100, 104, 108, 111, 114, 117, 119, 121, 83, 89, 95, + 100, 105, 109, 113, 116, 119, 122, 124, 104, 109, 114, 118, 121, + 124}; +#define IGP02E1000_CABLE_LENGTH_TABLE_SIZE \ + (sizeof(e1000_igp_2_cable_length_table) / \ + sizeof(e1000_igp_2_cable_length_table[0])) + +/** + * e1000_init_phy_ops_generic - Initialize PHY function pointers + * @hw: pointer to the HW structure + * + * Setups up the function pointers to no-op functions + **/ +void e1000_init_phy_ops_generic(struct e1000_hw *hw) +{ + struct e1000_phy_info *phy = &hw->phy; + DEBUGFUNC("e1000_init_phy_ops_generic"); + + /* Initialize function pointers */ + phy->ops.init_params = e1000_null_ops_generic; + phy->ops.acquire = e1000_null_ops_generic; + phy->ops.check_polarity = e1000_null_ops_generic; + phy->ops.check_reset_block = e1000_null_ops_generic; + phy->ops.commit = e1000_null_ops_generic; + phy->ops.force_speed_duplex = e1000_null_ops_generic; + phy->ops.get_cfg_done = e1000_null_ops_generic; + phy->ops.get_cable_length = e1000_null_ops_generic; + phy->ops.get_info = e1000_null_ops_generic; + phy->ops.set_page = e1000_null_set_page; + phy->ops.read_reg = e1000_null_read_reg; + phy->ops.read_reg_locked = e1000_null_read_reg; + phy->ops.read_reg_page = e1000_null_read_reg; + phy->ops.release = e1000_null_phy_generic; + phy->ops.reset = e1000_null_ops_generic; + phy->ops.set_d0_lplu_state = e1000_null_lplu_state; + phy->ops.set_d3_lplu_state = e1000_null_lplu_state; + phy->ops.write_reg = e1000_null_write_reg; + phy->ops.write_reg_locked = e1000_null_write_reg; + phy->ops.write_reg_page = e1000_null_write_reg; + phy->ops.power_up = e1000_null_phy_generic; + phy->ops.power_down = e1000_null_phy_generic; + phy->ops.read_i2c_byte = e1000_read_i2c_byte_null; + phy->ops.write_i2c_byte = e1000_write_i2c_byte_null; +} + +/** + * e1000_null_set_page - No-op function, return 0 + * @hw: pointer to the HW structure + **/ +s32 e1000_null_set_page(struct e1000_hw E1000_UNUSEDARG *hw, + u16 E1000_UNUSEDARG data) +{ + DEBUGFUNC("e1000_null_set_page"); + return E1000_SUCCESS; +} + +/** + * e1000_null_read_reg - No-op function, return 0 + * @hw: pointer to the HW structure + **/ +s32 e1000_null_read_reg(struct e1000_hw E1000_UNUSEDARG *hw, + u32 E1000_UNUSEDARG offset, u16 E1000_UNUSEDARG *data) +{ + DEBUGFUNC("e1000_null_read_reg"); + return E1000_SUCCESS; +} + +/** + * e1000_null_phy_generic - No-op function, return void + * @hw: pointer to the HW structure + **/ +void e1000_null_phy_generic(struct e1000_hw E1000_UNUSEDARG *hw) +{ + DEBUGFUNC("e1000_null_phy_generic"); + return; +} + +/** + * e1000_null_lplu_state - No-op function, return 0 + * @hw: pointer to the HW structure + **/ +s32 e1000_null_lplu_state(struct e1000_hw E1000_UNUSEDARG *hw, + bool E1000_UNUSEDARG active) +{ + DEBUGFUNC("e1000_null_lplu_state"); + return E1000_SUCCESS; +} + +/** + * e1000_null_write_reg - No-op function, return 0 + * @hw: pointer to the HW structure + **/ +s32 e1000_null_write_reg(struct e1000_hw E1000_UNUSEDARG *hw, + u32 E1000_UNUSEDARG offset, u16 E1000_UNUSEDARG data) +{ + DEBUGFUNC("e1000_null_write_reg"); + return E1000_SUCCESS; +} + +/** + * e1000_read_i2c_byte_null - No-op function, return 0 + * @hw: pointer to hardware structure + * @byte_offset: byte offset to write + * @dev_addr: device address + * @data: data value read + * + **/ +s32 e1000_read_i2c_byte_null(struct e1000_hw E1000_UNUSEDARG *hw, + u8 E1000_UNUSEDARG byte_offset, + u8 E1000_UNUSEDARG dev_addr, + u8 E1000_UNUSEDARG *data) +{ + DEBUGFUNC("e1000_read_i2c_byte_null"); + return E1000_SUCCESS; +} + +/** + * e1000_write_i2c_byte_null - No-op function, return 0 + * @hw: pointer to hardware structure + * @byte_offset: byte offset to write + * @dev_addr: device address + * @data: data value to write + * + **/ +s32 e1000_write_i2c_byte_null(struct e1000_hw E1000_UNUSEDARG *hw, + u8 E1000_UNUSEDARG byte_offset, + u8 E1000_UNUSEDARG dev_addr, + u8 E1000_UNUSEDARG data) +{ + DEBUGFUNC("e1000_write_i2c_byte_null"); + return E1000_SUCCESS; +} + +/** + * e1000_check_reset_block_generic - Check if PHY reset is blocked + * @hw: pointer to the HW structure + * + * Read the PHY management control register and check whether a PHY reset + * is blocked. If a reset is not blocked return E1000_SUCCESS, otherwise + * return E1000_BLK_PHY_RESET (12). + **/ +s32 e1000_check_reset_block_generic(struct e1000_hw *hw) +{ + u32 manc; + + DEBUGFUNC("e1000_check_reset_block"); + + manc = E1000_READ_REG(hw, E1000_MANC); + + return (manc & E1000_MANC_BLK_PHY_RST_ON_IDE) ? + E1000_BLK_PHY_RESET : E1000_SUCCESS; +} + +/** + * e1000_get_phy_id - Retrieve the PHY ID and revision + * @hw: pointer to the HW structure + * + * Reads the PHY registers and stores the PHY ID and possibly the PHY + * revision in the hardware structure. + **/ +s32 e1000_get_phy_id(struct e1000_hw *hw) +{ + struct e1000_phy_info *phy = &hw->phy; + s32 ret_val = E1000_SUCCESS; + u16 phy_id; + + DEBUGFUNC("e1000_get_phy_id"); + + if (!phy->ops.read_reg) + return E1000_SUCCESS; + + ret_val = phy->ops.read_reg(hw, PHY_ID1, &phy_id); + if (ret_val) + return ret_val; + + phy->id = (u32)(phy_id << 16); + usec_delay(20); + ret_val = phy->ops.read_reg(hw, PHY_ID2, &phy_id); + if (ret_val) + return ret_val; + + phy->id |= (u32)(phy_id & PHY_REVISION_MASK); + phy->revision = (u32)(phy_id & ~PHY_REVISION_MASK); + + return E1000_SUCCESS; +} + +/** + * e1000_phy_reset_dsp_generic - Reset PHY DSP + * @hw: pointer to the HW structure + * + * Reset the digital signal processor. + **/ +s32 e1000_phy_reset_dsp_generic(struct e1000_hw *hw) +{ + s32 ret_val; + + DEBUGFUNC("e1000_phy_reset_dsp_generic"); + + if (!hw->phy.ops.write_reg) + return E1000_SUCCESS; + + ret_val = hw->phy.ops.write_reg(hw, M88E1000_PHY_GEN_CONTROL, 0xC1); + if (ret_val) + return ret_val; + + return hw->phy.ops.write_reg(hw, M88E1000_PHY_GEN_CONTROL, 0); +} + +/** + * e1000_read_phy_reg_mdic - Read MDI control register + * @hw: pointer to the HW structure + * @offset: register offset to be read + * @data: pointer to the read data + * + * Reads the MDI control register in the PHY at offset and stores the + * information read to data. + **/ +s32 e1000_read_phy_reg_mdic(struct e1000_hw *hw, u32 offset, u16 *data) +{ + struct e1000_phy_info *phy = &hw->phy; + u32 i, mdic = 0; + + DEBUGFUNC("e1000_read_phy_reg_mdic"); + + if (offset > MAX_PHY_REG_ADDRESS) { + DEBUGOUT1("PHY Address %d is out of range\n", offset); + return -E1000_ERR_PARAM; + } + + /* Set up Op-code, Phy Address, and register offset in the MDI + * Control register. The MAC will take care of interfacing with the + * PHY to retrieve the desired data. + */ + mdic = ((offset << E1000_MDIC_REG_SHIFT) | + (phy->addr << E1000_MDIC_PHY_SHIFT) | + (E1000_MDIC_OP_READ)); + + E1000_WRITE_REG(hw, E1000_MDIC, mdic); + + /* Poll the ready bit to see if the MDI read completed + * Increasing the time out as testing showed failures with + * the lower time out + */ + for (i = 0; i < (E1000_GEN_POLL_TIMEOUT * 3); i++) { + usec_delay_irq(50); + mdic = E1000_READ_REG(hw, E1000_MDIC); + if (mdic & E1000_MDIC_READY) + break; + } + if (!(mdic & E1000_MDIC_READY)) { + DEBUGOUT("MDI Read did not complete\n"); + return -E1000_ERR_PHY; + } + if (mdic & E1000_MDIC_ERROR) { + DEBUGOUT("MDI Error\n"); + return -E1000_ERR_PHY; + } + if (((mdic & E1000_MDIC_REG_MASK) >> E1000_MDIC_REG_SHIFT) != offset) { + DEBUGOUT2("MDI Read offset error - requested %d, returned %d\n", + offset, + (mdic & E1000_MDIC_REG_MASK) >> E1000_MDIC_REG_SHIFT); + return -E1000_ERR_PHY; + } + *data = (u16) mdic; + + return E1000_SUCCESS; +} + +/** + * e1000_write_phy_reg_mdic - Write MDI control register + * @hw: pointer to the HW structure + * @offset: register offset to write to + * @data: data to write to register at offset + * + * Writes data to MDI control register in the PHY at offset. + **/ +s32 e1000_write_phy_reg_mdic(struct e1000_hw *hw, u32 offset, u16 data) +{ + struct e1000_phy_info *phy = &hw->phy; + u32 i, mdic = 0; + + DEBUGFUNC("e1000_write_phy_reg_mdic"); + + if (offset > MAX_PHY_REG_ADDRESS) { + DEBUGOUT1("PHY Address %d is out of range\n", offset); + return -E1000_ERR_PARAM; + } + + /* Set up Op-code, Phy Address, and register offset in the MDI + * Control register. The MAC will take care of interfacing with the + * PHY to retrieve the desired data. + */ + mdic = (((u32)data) | + (offset << E1000_MDIC_REG_SHIFT) | + (phy->addr << E1000_MDIC_PHY_SHIFT) | + (E1000_MDIC_OP_WRITE)); + + E1000_WRITE_REG(hw, E1000_MDIC, mdic); + + /* Poll the ready bit to see if the MDI read completed + * Increasing the time out as testing showed failures with + * the lower time out + */ + for (i = 0; i < (E1000_GEN_POLL_TIMEOUT * 3); i++) { + usec_delay_irq(50); + mdic = E1000_READ_REG(hw, E1000_MDIC); + if (mdic & E1000_MDIC_READY) + break; + } + if (!(mdic & E1000_MDIC_READY)) { + DEBUGOUT("MDI Write did not complete\n"); + return -E1000_ERR_PHY; + } + if (mdic & E1000_MDIC_ERROR) { + DEBUGOUT("MDI Error\n"); + return -E1000_ERR_PHY; + } + if (((mdic & E1000_MDIC_REG_MASK) >> E1000_MDIC_REG_SHIFT) != offset) { + DEBUGOUT2("MDI Write offset error - requested %d, returned %d\n", + offset, + (mdic & E1000_MDIC_REG_MASK) >> E1000_MDIC_REG_SHIFT); + return -E1000_ERR_PHY; + } + + return E1000_SUCCESS; +} + +/** + * e1000_read_phy_reg_i2c - Read PHY register using i2c + * @hw: pointer to the HW structure + * @offset: register offset to be read + * @data: pointer to the read data + * + * Reads the PHY register at offset using the i2c interface and stores the + * retrieved information in data. + **/ +s32 e1000_read_phy_reg_i2c(struct e1000_hw *hw, u32 offset, u16 *data) +{ + struct e1000_phy_info *phy = &hw->phy; + u32 i, i2ccmd = 0; + + DEBUGFUNC("e1000_read_phy_reg_i2c"); + + /* Set up Op-code, Phy Address, and register address in the I2CCMD + * register. The MAC will take care of interfacing with the + * PHY to retrieve the desired data. + */ + i2ccmd = ((offset << E1000_I2CCMD_REG_ADDR_SHIFT) | + (phy->addr << E1000_I2CCMD_PHY_ADDR_SHIFT) | + (E1000_I2CCMD_OPCODE_READ)); + + E1000_WRITE_REG(hw, E1000_I2CCMD, i2ccmd); + + /* Poll the ready bit to see if the I2C read completed */ + for (i = 0; i < E1000_I2CCMD_PHY_TIMEOUT; i++) { + usec_delay(50); + i2ccmd = E1000_READ_REG(hw, E1000_I2CCMD); + if (i2ccmd & E1000_I2CCMD_READY) + break; + } + if (!(i2ccmd & E1000_I2CCMD_READY)) { + DEBUGOUT("I2CCMD Read did not complete\n"); + return -E1000_ERR_PHY; + } + if (i2ccmd & E1000_I2CCMD_ERROR) { + DEBUGOUT("I2CCMD Error bit set\n"); + return -E1000_ERR_PHY; + } + + /* Need to byte-swap the 16-bit value. */ + *data = ((i2ccmd >> 8) & 0x00FF) | ((i2ccmd << 8) & 0xFF00); + + return E1000_SUCCESS; +} + +/** + * e1000_write_phy_reg_i2c - Write PHY register using i2c + * @hw: pointer to the HW structure + * @offset: register offset to write to + * @data: data to write at register offset + * + * Writes the data to PHY register at the offset using the i2c interface. + **/ +s32 e1000_write_phy_reg_i2c(struct e1000_hw *hw, u32 offset, u16 data) +{ + struct e1000_phy_info *phy = &hw->phy; + u32 i, i2ccmd = 0; + u16 phy_data_swapped; + + DEBUGFUNC("e1000_write_phy_reg_i2c"); + + /* Prevent overwritting SFP I2C EEPROM which is at A0 address.*/ + if ((hw->phy.addr == 0) || (hw->phy.addr > 7)) { + DEBUGOUT1("PHY I2C Address %d is out of range.\n", + hw->phy.addr); + return -E1000_ERR_CONFIG; + } + + /* Swap the data bytes for the I2C interface */ + phy_data_swapped = ((data >> 8) & 0x00FF) | ((data << 8) & 0xFF00); + + /* Set up Op-code, Phy Address, and register address in the I2CCMD + * register. The MAC will take care of interfacing with the + * PHY to retrieve the desired data. + */ + i2ccmd = ((offset << E1000_I2CCMD_REG_ADDR_SHIFT) | + (phy->addr << E1000_I2CCMD_PHY_ADDR_SHIFT) | + E1000_I2CCMD_OPCODE_WRITE | + phy_data_swapped); + + E1000_WRITE_REG(hw, E1000_I2CCMD, i2ccmd); + + /* Poll the ready bit to see if the I2C read completed */ + for (i = 0; i < E1000_I2CCMD_PHY_TIMEOUT; i++) { + usec_delay(50); + i2ccmd = E1000_READ_REG(hw, E1000_I2CCMD); + if (i2ccmd & E1000_I2CCMD_READY) + break; + } + if (!(i2ccmd & E1000_I2CCMD_READY)) { + DEBUGOUT("I2CCMD Write did not complete\n"); + return -E1000_ERR_PHY; + } + if (i2ccmd & E1000_I2CCMD_ERROR) { + DEBUGOUT("I2CCMD Error bit set\n"); + return -E1000_ERR_PHY; + } + + return E1000_SUCCESS; +} + +/** + * e1000_read_sfp_data_byte - Reads SFP module data. + * @hw: pointer to the HW structure + * @offset: byte location offset to be read + * @data: read data buffer pointer + * + * Reads one byte from SFP module data stored + * in SFP resided EEPROM memory or SFP diagnostic area. + * Function should be called with + * E1000_I2CCMD_SFP_DATA_ADDR() for SFP module database access + * E1000_I2CCMD_SFP_DIAG_ADDR() for SFP diagnostics parameters + * access + **/ +s32 e1000_read_sfp_data_byte(struct e1000_hw *hw, u16 offset, u8 *data) +{ + u32 i = 0; + u32 i2ccmd = 0; + u32 data_local = 0; + + DEBUGFUNC("e1000_read_sfp_data_byte"); + + if (offset > E1000_I2CCMD_SFP_DIAG_ADDR(255)) { + DEBUGOUT("I2CCMD command address exceeds upper limit\n"); + return -E1000_ERR_PHY; + } + + /* Set up Op-code, EEPROM Address,in the I2CCMD + * register. The MAC will take care of interfacing with the + * EEPROM to retrieve the desired data. + */ + i2ccmd = ((offset << E1000_I2CCMD_REG_ADDR_SHIFT) | + E1000_I2CCMD_OPCODE_READ); + + E1000_WRITE_REG(hw, E1000_I2CCMD, i2ccmd); + + /* Poll the ready bit to see if the I2C read completed */ + for (i = 0; i < E1000_I2CCMD_PHY_TIMEOUT; i++) { + usec_delay(50); + data_local = E1000_READ_REG(hw, E1000_I2CCMD); + if (data_local & E1000_I2CCMD_READY) + break; + } + if (!(data_local & E1000_I2CCMD_READY)) { + DEBUGOUT("I2CCMD Read did not complete\n"); + return -E1000_ERR_PHY; + } + if (data_local & E1000_I2CCMD_ERROR) { + DEBUGOUT("I2CCMD Error bit set\n"); + return -E1000_ERR_PHY; + } + *data = (u8) data_local & 0xFF; + + return E1000_SUCCESS; +} + +/** + * e1000_write_sfp_data_byte - Writes SFP module data. + * @hw: pointer to the HW structure + * @offset: byte location offset to write to + * @data: data to write + * + * Writes one byte to SFP module data stored + * in SFP resided EEPROM memory or SFP diagnostic area. + * Function should be called with + * E1000_I2CCMD_SFP_DATA_ADDR() for SFP module database access + * E1000_I2CCMD_SFP_DIAG_ADDR() for SFP diagnostics parameters + * access + **/ +s32 e1000_write_sfp_data_byte(struct e1000_hw *hw, u16 offset, u8 data) +{ + u32 i = 0; + u32 i2ccmd = 0; + u32 data_local = 0; + + DEBUGFUNC("e1000_write_sfp_data_byte"); + + if (offset > E1000_I2CCMD_SFP_DIAG_ADDR(255)) { + DEBUGOUT("I2CCMD command address exceeds upper limit\n"); + return -E1000_ERR_PHY; + } + /* The programming interface is 16 bits wide + * so we need to read the whole word first + * then update appropriate byte lane and write + * the updated word back. + */ + /* Set up Op-code, EEPROM Address,in the I2CCMD + * register. The MAC will take care of interfacing + * with an EEPROM to write the data given. + */ + i2ccmd = ((offset << E1000_I2CCMD_REG_ADDR_SHIFT) | + E1000_I2CCMD_OPCODE_READ); + /* Set a command to read single word */ + E1000_WRITE_REG(hw, E1000_I2CCMD, i2ccmd); + for (i = 0; i < E1000_I2CCMD_PHY_TIMEOUT; i++) { + usec_delay(50); + /* Poll the ready bit to see if lastly + * launched I2C operation completed + */ + i2ccmd = E1000_READ_REG(hw, E1000_I2CCMD); + if (i2ccmd & E1000_I2CCMD_READY) { + /* Check if this is READ or WRITE phase */ + if ((i2ccmd & E1000_I2CCMD_OPCODE_READ) == + E1000_I2CCMD_OPCODE_READ) { + /* Write the selected byte + * lane and update whole word + */ + data_local = i2ccmd & 0xFF00; + data_local |= data; + i2ccmd = ((offset << + E1000_I2CCMD_REG_ADDR_SHIFT) | + E1000_I2CCMD_OPCODE_WRITE | data_local); + E1000_WRITE_REG(hw, E1000_I2CCMD, i2ccmd); + } else { + break; + } + } + } + if (!(i2ccmd & E1000_I2CCMD_READY)) { + DEBUGOUT("I2CCMD Write did not complete\n"); + return -E1000_ERR_PHY; + } + if (i2ccmd & E1000_I2CCMD_ERROR) { + DEBUGOUT("I2CCMD Error bit set\n"); + return -E1000_ERR_PHY; + } + return E1000_SUCCESS; +} + +/** + * e1000_read_phy_reg_m88 - Read m88 PHY register + * @hw: pointer to the HW structure + * @offset: register offset to be read + * @data: pointer to the read data + * + * Acquires semaphore, if necessary, then reads the PHY register at offset + * and storing the retrieved information in data. Release any acquired + * semaphores before exiting. + **/ +s32 e1000_read_phy_reg_m88(struct e1000_hw *hw, u32 offset, u16 *data) +{ + s32 ret_val; + + DEBUGFUNC("e1000_read_phy_reg_m88"); + + if (!hw->phy.ops.acquire) + return E1000_SUCCESS; + + ret_val = hw->phy.ops.acquire(hw); + if (ret_val) + return ret_val; + + ret_val = e1000_read_phy_reg_mdic(hw, MAX_PHY_REG_ADDRESS & offset, + data); + + hw->phy.ops.release(hw); + + return ret_val; +} + +/** + * e1000_write_phy_reg_m88 - Write m88 PHY register + * @hw: pointer to the HW structure + * @offset: register offset to write to + * @data: data to write at register offset + * + * Acquires semaphore, if necessary, then writes the data to PHY register + * at the offset. Release any acquired semaphores before exiting. + **/ +s32 e1000_write_phy_reg_m88(struct e1000_hw *hw, u32 offset, u16 data) +{ + s32 ret_val; + + DEBUGFUNC("e1000_write_phy_reg_m88"); + + if (!hw->phy.ops.acquire) + return E1000_SUCCESS; + + ret_val = hw->phy.ops.acquire(hw); + if (ret_val) + return ret_val; + + ret_val = e1000_write_phy_reg_mdic(hw, MAX_PHY_REG_ADDRESS & offset, + data); + + hw->phy.ops.release(hw); + + return ret_val; +} + +/** + * e1000_set_page_igp - Set page as on IGP-like PHY(s) + * @hw: pointer to the HW structure + * @page: page to set (shifted left when necessary) + * + * Sets PHY page required for PHY register access. Assumes semaphore is + * already acquired. Note, this function sets phy.addr to 1 so the caller + * must set it appropriately (if necessary) after this function returns. + **/ +s32 e1000_set_page_igp(struct e1000_hw *hw, u16 page) +{ + DEBUGFUNC("e1000_set_page_igp"); + + DEBUGOUT1("Setting page 0x%x\n", page); + + hw->phy.addr = 1; + + return e1000_write_phy_reg_mdic(hw, IGP01E1000_PHY_PAGE_SELECT, page); +} + +/** + * __e1000_read_phy_reg_igp - Read igp PHY register + * @hw: pointer to the HW structure + * @offset: register offset to be read + * @data: pointer to the read data + * @locked: semaphore has already been acquired or not + * + * Acquires semaphore, if necessary, then reads the PHY register at offset + * and stores the retrieved information in data. Release any acquired + * semaphores before exiting. + **/ +static s32 __e1000_read_phy_reg_igp(struct e1000_hw *hw, u32 offset, u16 *data, + bool locked) +{ + s32 ret_val = E1000_SUCCESS; + + DEBUGFUNC("__e1000_read_phy_reg_igp"); + + if (!locked) { + if (!hw->phy.ops.acquire) + return E1000_SUCCESS; + + ret_val = hw->phy.ops.acquire(hw); + if (ret_val) + return ret_val; + } + + if (offset > MAX_PHY_MULTI_PAGE_REG) + ret_val = e1000_write_phy_reg_mdic(hw, + IGP01E1000_PHY_PAGE_SELECT, + (u16)offset); + if (!ret_val) + ret_val = e1000_read_phy_reg_mdic(hw, + MAX_PHY_REG_ADDRESS & offset, + data); + if (!locked) + hw->phy.ops.release(hw); + + return ret_val; +} + +/** + * e1000_read_phy_reg_igp - Read igp PHY register + * @hw: pointer to the HW structure + * @offset: register offset to be read + * @data: pointer to the read data + * + * Acquires semaphore then reads the PHY register at offset and stores the + * retrieved information in data. + * Release the acquired semaphore before exiting. + **/ +s32 e1000_read_phy_reg_igp(struct e1000_hw *hw, u32 offset, u16 *data) +{ + return __e1000_read_phy_reg_igp(hw, offset, data, false); +} + +/** + * e1000_read_phy_reg_igp_locked - Read igp PHY register + * @hw: pointer to the HW structure + * @offset: register offset to be read + * @data: pointer to the read data + * + * Reads the PHY register at offset and stores the retrieved information + * in data. Assumes semaphore already acquired. + **/ +s32 e1000_read_phy_reg_igp_locked(struct e1000_hw *hw, u32 offset, u16 *data) +{ + return __e1000_read_phy_reg_igp(hw, offset, data, true); +} + +/** + * e1000_write_phy_reg_igp - Write igp PHY register + * @hw: pointer to the HW structure + * @offset: register offset to write to + * @data: data to write at register offset + * @locked: semaphore has already been acquired or not + * + * Acquires semaphore, if necessary, then writes the data to PHY register + * at the offset. Release any acquired semaphores before exiting. + **/ +static s32 __e1000_write_phy_reg_igp(struct e1000_hw *hw, u32 offset, u16 data, + bool locked) +{ + s32 ret_val = E1000_SUCCESS; + + DEBUGFUNC("e1000_write_phy_reg_igp"); + + if (!locked) { + if (!hw->phy.ops.acquire) + return E1000_SUCCESS; + + ret_val = hw->phy.ops.acquire(hw); + if (ret_val) + return ret_val; + } + + if (offset > MAX_PHY_MULTI_PAGE_REG) + ret_val = e1000_write_phy_reg_mdic(hw, + IGP01E1000_PHY_PAGE_SELECT, + (u16)offset); + if (!ret_val) + ret_val = e1000_write_phy_reg_mdic(hw, MAX_PHY_REG_ADDRESS & + offset, + data); + if (!locked) + hw->phy.ops.release(hw); + + return ret_val; +} + +/** + * e1000_write_phy_reg_igp - Write igp PHY register + * @hw: pointer to the HW structure + * @offset: register offset to write to + * @data: data to write at register offset + * + * Acquires semaphore then writes the data to PHY register + * at the offset. Release any acquired semaphores before exiting. + **/ +s32 e1000_write_phy_reg_igp(struct e1000_hw *hw, u32 offset, u16 data) +{ + return __e1000_write_phy_reg_igp(hw, offset, data, false); +} + +/** + * e1000_write_phy_reg_igp_locked - Write igp PHY register + * @hw: pointer to the HW structure + * @offset: register offset to write to + * @data: data to write at register offset + * + * Writes the data to PHY register at the offset. + * Assumes semaphore already acquired. + **/ +s32 e1000_write_phy_reg_igp_locked(struct e1000_hw *hw, u32 offset, u16 data) +{ + return __e1000_write_phy_reg_igp(hw, offset, data, true); +} + +/** + * __e1000_read_kmrn_reg - Read kumeran register + * @hw: pointer to the HW structure + * @offset: register offset to be read + * @data: pointer to the read data + * @locked: semaphore has already been acquired or not + * + * Acquires semaphore, if necessary. Then reads the PHY register at offset + * using the kumeran interface. The information retrieved is stored in data. + * Release any acquired semaphores before exiting. + **/ +static s32 __e1000_read_kmrn_reg(struct e1000_hw *hw, u32 offset, u16 *data, + bool locked) +{ + u32 kmrnctrlsta; + + DEBUGFUNC("__e1000_read_kmrn_reg"); + + if (!locked) { + s32 ret_val = E1000_SUCCESS; + + if (!hw->phy.ops.acquire) + return E1000_SUCCESS; + + ret_val = hw->phy.ops.acquire(hw); + if (ret_val) + return ret_val; + } + + kmrnctrlsta = ((offset << E1000_KMRNCTRLSTA_OFFSET_SHIFT) & + E1000_KMRNCTRLSTA_OFFSET) | E1000_KMRNCTRLSTA_REN; + E1000_WRITE_REG(hw, E1000_KMRNCTRLSTA, kmrnctrlsta); + E1000_WRITE_FLUSH(hw); + + usec_delay(2); + + kmrnctrlsta = E1000_READ_REG(hw, E1000_KMRNCTRLSTA); + *data = (u16)kmrnctrlsta; + + if (!locked) + hw->phy.ops.release(hw); + + return E1000_SUCCESS; +} + +/** + * e1000_read_kmrn_reg_generic - Read kumeran register + * @hw: pointer to the HW structure + * @offset: register offset to be read + * @data: pointer to the read data + * + * Acquires semaphore then reads the PHY register at offset using the + * kumeran interface. The information retrieved is stored in data. + * Release the acquired semaphore before exiting. + **/ +s32 e1000_read_kmrn_reg_generic(struct e1000_hw *hw, u32 offset, u16 *data) +{ + return __e1000_read_kmrn_reg(hw, offset, data, false); +} + +/** + * e1000_read_kmrn_reg_locked - Read kumeran register + * @hw: pointer to the HW structure + * @offset: register offset to be read + * @data: pointer to the read data + * + * Reads the PHY register at offset using the kumeran interface. The + * information retrieved is stored in data. + * Assumes semaphore already acquired. + **/ +s32 e1000_read_kmrn_reg_locked(struct e1000_hw *hw, u32 offset, u16 *data) +{ + return __e1000_read_kmrn_reg(hw, offset, data, true); +} + +/** + * __e1000_write_kmrn_reg - Write kumeran register + * @hw: pointer to the HW structure + * @offset: register offset to write to + * @data: data to write at register offset + * @locked: semaphore has already been acquired or not + * + * Acquires semaphore, if necessary. Then write the data to PHY register + * at the offset using the kumeran interface. Release any acquired semaphores + * before exiting. + **/ +static s32 __e1000_write_kmrn_reg(struct e1000_hw *hw, u32 offset, u16 data, + bool locked) +{ + u32 kmrnctrlsta; + + DEBUGFUNC("e1000_write_kmrn_reg_generic"); + + if (!locked) { + s32 ret_val = E1000_SUCCESS; + + if (!hw->phy.ops.acquire) + return E1000_SUCCESS; + + ret_val = hw->phy.ops.acquire(hw); + if (ret_val) + return ret_val; + } + + kmrnctrlsta = ((offset << E1000_KMRNCTRLSTA_OFFSET_SHIFT) & + E1000_KMRNCTRLSTA_OFFSET) | data; + E1000_WRITE_REG(hw, E1000_KMRNCTRLSTA, kmrnctrlsta); + E1000_WRITE_FLUSH(hw); + + usec_delay(2); + + if (!locked) + hw->phy.ops.release(hw); + + return E1000_SUCCESS; +} + +/** + * e1000_write_kmrn_reg_generic - Write kumeran register + * @hw: pointer to the HW structure + * @offset: register offset to write to + * @data: data to write at register offset + * + * Acquires semaphore then writes the data to the PHY register at the offset + * using the kumeran interface. Release the acquired semaphore before exiting. + **/ +s32 e1000_write_kmrn_reg_generic(struct e1000_hw *hw, u32 offset, u16 data) +{ + return __e1000_write_kmrn_reg(hw, offset, data, false); +} + +/** + * e1000_write_kmrn_reg_locked - Write kumeran register + * @hw: pointer to the HW structure + * @offset: register offset to write to + * @data: data to write at register offset + * + * Write the data to PHY register at the offset using the kumeran interface. + * Assumes semaphore already acquired. + **/ +s32 e1000_write_kmrn_reg_locked(struct e1000_hw *hw, u32 offset, u16 data) +{ + return __e1000_write_kmrn_reg(hw, offset, data, true); +} + +/** + * e1000_set_master_slave_mode - Setup PHY for Master/slave mode + * @hw: pointer to the HW structure + * + * Sets up Master/slave mode + **/ +static s32 e1000_set_master_slave_mode(struct e1000_hw *hw) +{ + s32 ret_val; + u16 phy_data; + + /* Resolve Master/Slave mode */ + ret_val = hw->phy.ops.read_reg(hw, PHY_1000T_CTRL, &phy_data); + if (ret_val) + return ret_val; + + /* load defaults for future use */ + hw->phy.original_ms_type = (phy_data & CR_1000T_MS_ENABLE) ? + ((phy_data & CR_1000T_MS_VALUE) ? + e1000_ms_force_master : + e1000_ms_force_slave) : e1000_ms_auto; + + switch (hw->phy.ms_type) { + case e1000_ms_force_master: + phy_data |= (CR_1000T_MS_ENABLE | CR_1000T_MS_VALUE); + break; + case e1000_ms_force_slave: + phy_data |= CR_1000T_MS_ENABLE; + phy_data &= ~(CR_1000T_MS_VALUE); + break; + case e1000_ms_auto: + phy_data &= ~CR_1000T_MS_ENABLE; + /* fall-through */ + default: + break; + } + + return hw->phy.ops.write_reg(hw, PHY_1000T_CTRL, phy_data); +} + +/** + * e1000_copper_link_setup_82577 - Setup 82577 PHY for copper link + * @hw: pointer to the HW structure + * + * Sets up Carrier-sense on Transmit and downshift values. + **/ +s32 e1000_copper_link_setup_82577(struct e1000_hw *hw) +{ + s32 ret_val; + u16 phy_data; + + DEBUGFUNC("e1000_copper_link_setup_82577"); + + if (hw->phy.reset_disable) + return E1000_SUCCESS; + + if (hw->phy.type == e1000_phy_82580) { + ret_val = hw->phy.ops.reset(hw); + if (ret_val) { + DEBUGOUT("Error resetting the PHY.\n"); + return ret_val; + } + } + + /* Enable CRS on Tx. This must be set for half-duplex operation. */ + ret_val = hw->phy.ops.read_reg(hw, I82577_CFG_REG, &phy_data); + if (ret_val) + return ret_val; + + phy_data |= I82577_CFG_ASSERT_CRS_ON_TX; + + /* Enable downshift */ + phy_data |= I82577_CFG_ENABLE_DOWNSHIFT; + + ret_val = hw->phy.ops.write_reg(hw, I82577_CFG_REG, phy_data); + if (ret_val) + return ret_val; + + /* Set MDI/MDIX mode */ + ret_val = hw->phy.ops.read_reg(hw, I82577_PHY_CTRL_2, &phy_data); + if (ret_val) + return ret_val; + phy_data &= ~I82577_PHY_CTRL2_MDIX_CFG_MASK; + /* Options: + * 0 - Auto (default) + * 1 - MDI mode + * 2 - MDI-X mode + */ + switch (hw->phy.mdix) { + case 1: + break; + case 2: + phy_data |= I82577_PHY_CTRL2_MANUAL_MDIX; + break; + case 0: + default: + phy_data |= I82577_PHY_CTRL2_AUTO_MDI_MDIX; + break; + } + ret_val = hw->phy.ops.write_reg(hw, I82577_PHY_CTRL_2, phy_data); + if (ret_val) + return ret_val; + + return e1000_set_master_slave_mode(hw); +} + +/** + * e1000_copper_link_setup_m88 - Setup m88 PHY's for copper link + * @hw: pointer to the HW structure + * + * Sets up MDI/MDI-X and polarity for m88 PHY's. If necessary, transmit clock + * and downshift values are set also. + **/ +s32 e1000_copper_link_setup_m88(struct e1000_hw *hw) +{ + struct e1000_phy_info *phy = &hw->phy; + s32 ret_val; + u16 phy_data; + + DEBUGFUNC("e1000_copper_link_setup_m88"); + + if (phy->reset_disable) + return E1000_SUCCESS; + + /* Enable CRS on Tx. This must be set for half-duplex operation. */ + ret_val = phy->ops.read_reg(hw, M88E1000_PHY_SPEC_CTRL, &phy_data); + if (ret_val) + return ret_val; + + phy_data |= M88E1000_PSCR_ASSERT_CRS_ON_TX; + + /* Options: + * MDI/MDI-X = 0 (default) + * 0 - Auto for all speeds + * 1 - MDI mode + * 2 - MDI-X mode + * 3 - Auto for 1000Base-T only (MDI-X for 10/100Base-T modes) + */ + phy_data &= ~M88E1000_PSCR_AUTO_X_MODE; + + switch (phy->mdix) { + case 1: + phy_data |= M88E1000_PSCR_MDI_MANUAL_MODE; + break; + case 2: + phy_data |= M88E1000_PSCR_MDIX_MANUAL_MODE; + break; + case 3: + phy_data |= M88E1000_PSCR_AUTO_X_1000T; + break; + case 0: + default: + phy_data |= M88E1000_PSCR_AUTO_X_MODE; + break; + } + + /* Options: + * disable_polarity_correction = 0 (default) + * Automatic Correction for Reversed Cable Polarity + * 0 - Disabled + * 1 - Enabled + */ + phy_data &= ~M88E1000_PSCR_POLARITY_REVERSAL; + if (phy->disable_polarity_correction) + phy_data |= M88E1000_PSCR_POLARITY_REVERSAL; + + ret_val = phy->ops.write_reg(hw, M88E1000_PHY_SPEC_CTRL, phy_data); + if (ret_val) + return ret_val; + + if (phy->revision < E1000_REVISION_4) { + /* Force TX_CLK in the Extended PHY Specific Control Register + * to 25MHz clock. + */ + ret_val = phy->ops.read_reg(hw, M88E1000_EXT_PHY_SPEC_CTRL, + &phy_data); + if (ret_val) + return ret_val; + + phy_data |= M88E1000_EPSCR_TX_CLK_25; + + if ((phy->revision == E1000_REVISION_2) && + (phy->id == M88E1111_I_PHY_ID)) { + /* 82573L PHY - set the downshift counter to 5x. */ + phy_data &= ~M88EC018_EPSCR_DOWNSHIFT_COUNTER_MASK; + phy_data |= M88EC018_EPSCR_DOWNSHIFT_COUNTER_5X; + } else { + /* Configure Master and Slave downshift values */ + phy_data &= ~(M88E1000_EPSCR_MASTER_DOWNSHIFT_MASK | + M88E1000_EPSCR_SLAVE_DOWNSHIFT_MASK); + phy_data |= (M88E1000_EPSCR_MASTER_DOWNSHIFT_1X | + M88E1000_EPSCR_SLAVE_DOWNSHIFT_1X); + } + ret_val = phy->ops.write_reg(hw, M88E1000_EXT_PHY_SPEC_CTRL, + phy_data); + if (ret_val) + return ret_val; + } + + /* Commit the changes. */ + ret_val = phy->ops.commit(hw); + if (ret_val) { + DEBUGOUT("Error committing the PHY changes\n"); + return ret_val; + } + + return E1000_SUCCESS; +} + +/** + * e1000_copper_link_setup_m88_gen2 - Setup m88 PHY's for copper link + * @hw: pointer to the HW structure + * + * Sets up MDI/MDI-X and polarity for i347-AT4, m88e1322 and m88e1112 PHY's. + * Also enables and sets the downshift parameters. + **/ +s32 e1000_copper_link_setup_m88_gen2(struct e1000_hw *hw) +{ + struct e1000_phy_info *phy = &hw->phy; + s32 ret_val; + u16 phy_data; + + DEBUGFUNC("e1000_copper_link_setup_m88_gen2"); + + if (phy->reset_disable) + return E1000_SUCCESS; + + /* Enable CRS on Tx. This must be set for half-duplex operation. */ + ret_val = phy->ops.read_reg(hw, M88E1000_PHY_SPEC_CTRL, &phy_data); + if (ret_val) + return ret_val; + + /* Options: + * MDI/MDI-X = 0 (default) + * 0 - Auto for all speeds + * 1 - MDI mode + * 2 - MDI-X mode + * 3 - Auto for 1000Base-T only (MDI-X for 10/100Base-T modes) + */ + phy_data &= ~M88E1000_PSCR_AUTO_X_MODE; + + switch (phy->mdix) { + case 1: + phy_data |= M88E1000_PSCR_MDI_MANUAL_MODE; + break; + case 2: + phy_data |= M88E1000_PSCR_MDIX_MANUAL_MODE; + break; + case 3: + /* M88E1112 does not support this mode) */ + if (phy->id != M88E1112_E_PHY_ID) { + phy_data |= M88E1000_PSCR_AUTO_X_1000T; + break; + } + case 0: + default: + phy_data |= M88E1000_PSCR_AUTO_X_MODE; + break; + } + + /* Options: + * disable_polarity_correction = 0 (default) + * Automatic Correction for Reversed Cable Polarity + * 0 - Disabled + * 1 - Enabled + */ + phy_data &= ~M88E1000_PSCR_POLARITY_REVERSAL; + if (phy->disable_polarity_correction) + phy_data |= M88E1000_PSCR_POLARITY_REVERSAL; + + /* Enable downshift and setting it to X6 */ + if (phy->id == M88E1543_E_PHY_ID) { + phy_data &= ~I347AT4_PSCR_DOWNSHIFT_ENABLE; + ret_val = + phy->ops.write_reg(hw, M88E1000_PHY_SPEC_CTRL, phy_data); + if (ret_val) + return ret_val; + + ret_val = phy->ops.commit(hw); + if (ret_val) { + DEBUGOUT("Error committing the PHY changes\n"); + return ret_val; + } + } + + phy_data &= ~I347AT4_PSCR_DOWNSHIFT_MASK; + phy_data |= I347AT4_PSCR_DOWNSHIFT_6X; + phy_data |= I347AT4_PSCR_DOWNSHIFT_ENABLE; + + ret_val = phy->ops.write_reg(hw, M88E1000_PHY_SPEC_CTRL, phy_data); + if (ret_val) + return ret_val; + + /* Commit the changes. */ + ret_val = phy->ops.commit(hw); + if (ret_val) { + DEBUGOUT("Error committing the PHY changes\n"); + return ret_val; + } + + ret_val = e1000_set_master_slave_mode(hw); + if (ret_val) + return ret_val; + + return E1000_SUCCESS; +} + +/** + * e1000_copper_link_setup_igp - Setup igp PHY's for copper link + * @hw: pointer to the HW structure + * + * Sets up LPLU, MDI/MDI-X, polarity, Smartspeed and Master/Slave config for + * igp PHY's. + **/ +s32 e1000_copper_link_setup_igp(struct e1000_hw *hw) +{ + struct e1000_phy_info *phy = &hw->phy; + s32 ret_val; + u16 data; + + DEBUGFUNC("e1000_copper_link_setup_igp"); + + if (phy->reset_disable) + return E1000_SUCCESS; + + ret_val = hw->phy.ops.reset(hw); + if (ret_val) { + DEBUGOUT("Error resetting the PHY.\n"); + return ret_val; + } + + /* Wait 100ms for MAC to configure PHY from NVM settings, to avoid + * timeout issues when LFS is enabled. + */ + msec_delay(100); + + /* disable lplu d0 during driver init */ + if (hw->phy.ops.set_d0_lplu_state) { + ret_val = hw->phy.ops.set_d0_lplu_state(hw, false); + if (ret_val) { + DEBUGOUT("Error Disabling LPLU D0\n"); + return ret_val; + } + } + /* Configure mdi-mdix settings */ + ret_val = phy->ops.read_reg(hw, IGP01E1000_PHY_PORT_CTRL, &data); + if (ret_val) + return ret_val; + + data &= ~IGP01E1000_PSCR_AUTO_MDIX; + + switch (phy->mdix) { + case 1: + data &= ~IGP01E1000_PSCR_FORCE_MDI_MDIX; + break; + case 2: + data |= IGP01E1000_PSCR_FORCE_MDI_MDIX; + break; + case 0: + default: + data |= IGP01E1000_PSCR_AUTO_MDIX; + break; + } + ret_val = phy->ops.write_reg(hw, IGP01E1000_PHY_PORT_CTRL, data); + if (ret_val) + return ret_val; + + /* set auto-master slave resolution settings */ + if (hw->mac.autoneg) { + /* when autonegotiation advertisement is only 1000Mbps then we + * should disable SmartSpeed and enable Auto MasterSlave + * resolution as hardware default. + */ + if (phy->autoneg_advertised == ADVERTISE_1000_FULL) { + /* Disable SmartSpeed */ + ret_val = phy->ops.read_reg(hw, + IGP01E1000_PHY_PORT_CONFIG, + &data); + if (ret_val) + return ret_val; + + data &= ~IGP01E1000_PSCFR_SMART_SPEED; + ret_val = phy->ops.write_reg(hw, + IGP01E1000_PHY_PORT_CONFIG, + data); + if (ret_val) + return ret_val; + + /* Set auto Master/Slave resolution process */ + ret_val = phy->ops.read_reg(hw, PHY_1000T_CTRL, &data); + if (ret_val) + return ret_val; + + data &= ~CR_1000T_MS_ENABLE; + ret_val = phy->ops.write_reg(hw, PHY_1000T_CTRL, data); + if (ret_val) + return ret_val; + } + + ret_val = e1000_set_master_slave_mode(hw); + } + + return ret_val; +} + +/** + * e1000_phy_setup_autoneg - Configure PHY for auto-negotiation + * @hw: pointer to the HW structure + * + * Reads the MII auto-neg advertisement register and/or the 1000T control + * register and if the PHY is already setup for auto-negotiation, then + * return successful. Otherwise, setup advertisement and flow control to + * the appropriate values for the wanted auto-negotiation. + **/ +static s32 e1000_phy_setup_autoneg(struct e1000_hw *hw) +{ + struct e1000_phy_info *phy = &hw->phy; + s32 ret_val; + u16 mii_autoneg_adv_reg; + u16 mii_1000t_ctrl_reg = 0; + + DEBUGFUNC("e1000_phy_setup_autoneg"); + + phy->autoneg_advertised &= phy->autoneg_mask; + + /* Read the MII Auto-Neg Advertisement Register (Address 4). */ + ret_val = phy->ops.read_reg(hw, PHY_AUTONEG_ADV, &mii_autoneg_adv_reg); + if (ret_val) + return ret_val; + + if (phy->autoneg_mask & ADVERTISE_1000_FULL) { + /* Read the MII 1000Base-T Control Register (Address 9). */ + ret_val = phy->ops.read_reg(hw, PHY_1000T_CTRL, + &mii_1000t_ctrl_reg); + if (ret_val) + return ret_val; + } + + /* Need to parse both autoneg_advertised and fc and set up + * the appropriate PHY registers. First we will parse for + * autoneg_advertised software override. Since we can advertise + * a plethora of combinations, we need to check each bit + * individually. + */ + + /* First we clear all the 10/100 mb speed bits in the Auto-Neg + * Advertisement Register (Address 4) and the 1000 mb speed bits in + * the 1000Base-T Control Register (Address 9). + */ + mii_autoneg_adv_reg &= ~(NWAY_AR_100TX_FD_CAPS | + NWAY_AR_100TX_HD_CAPS | + NWAY_AR_10T_FD_CAPS | + NWAY_AR_10T_HD_CAPS); + mii_1000t_ctrl_reg &= ~(CR_1000T_HD_CAPS | CR_1000T_FD_CAPS); + + DEBUGOUT1("autoneg_advertised %x\n", phy->autoneg_advertised); + + /* Do we want to advertise 10 Mb Half Duplex? */ + if (phy->autoneg_advertised & ADVERTISE_10_HALF) { + DEBUGOUT("Advertise 10mb Half duplex\n"); + mii_autoneg_adv_reg |= NWAY_AR_10T_HD_CAPS; + } + + /* Do we want to advertise 10 Mb Full Duplex? */ + if (phy->autoneg_advertised & ADVERTISE_10_FULL) { + DEBUGOUT("Advertise 10mb Full duplex\n"); + mii_autoneg_adv_reg |= NWAY_AR_10T_FD_CAPS; + } + + /* Do we want to advertise 100 Mb Half Duplex? */ + if (phy->autoneg_advertised & ADVERTISE_100_HALF) { + DEBUGOUT("Advertise 100mb Half duplex\n"); + mii_autoneg_adv_reg |= NWAY_AR_100TX_HD_CAPS; + } + + /* Do we want to advertise 100 Mb Full Duplex? */ + if (phy->autoneg_advertised & ADVERTISE_100_FULL) { + DEBUGOUT("Advertise 100mb Full duplex\n"); + mii_autoneg_adv_reg |= NWAY_AR_100TX_FD_CAPS; + } + + /* We do not allow the Phy to advertise 1000 Mb Half Duplex */ + if (phy->autoneg_advertised & ADVERTISE_1000_HALF) + DEBUGOUT("Advertise 1000mb Half duplex request denied!\n"); + + /* Do we want to advertise 1000 Mb Full Duplex? */ + if (phy->autoneg_advertised & ADVERTISE_1000_FULL) { + DEBUGOUT("Advertise 1000mb Full duplex\n"); + mii_1000t_ctrl_reg |= CR_1000T_FD_CAPS; + } + + /* Check for a software override of the flow control settings, and + * setup the PHY advertisement registers accordingly. If + * auto-negotiation is enabled, then software will have to set the + * "PAUSE" bits to the correct value in the Auto-Negotiation + * Advertisement Register (PHY_AUTONEG_ADV) and re-start auto- + * negotiation. + * + * The possible values of the "fc" parameter are: + * 0: Flow control is completely disabled + * 1: Rx flow control is enabled (we can receive pause frames + * but not send pause frames). + * 2: Tx flow control is enabled (we can send pause frames + * but we do not support receiving pause frames). + * 3: Both Rx and Tx flow control (symmetric) are enabled. + * other: No software override. The flow control configuration + * in the EEPROM is used. + */ + switch (hw->fc.current_mode) { + case e1000_fc_none: + /* Flow control (Rx & Tx) is completely disabled by a + * software over-ride. + */ + mii_autoneg_adv_reg &= ~(NWAY_AR_ASM_DIR | NWAY_AR_PAUSE); + break; + case e1000_fc_rx_pause: + /* Rx Flow control is enabled, and Tx Flow control is + * disabled, by a software over-ride. + * + * Since there really isn't a way to advertise that we are + * capable of Rx Pause ONLY, we will advertise that we + * support both symmetric and asymmetric Rx PAUSE. Later + * (in e1000_config_fc_after_link_up) we will disable the + * hw's ability to send PAUSE frames. + */ + mii_autoneg_adv_reg |= (NWAY_AR_ASM_DIR | NWAY_AR_PAUSE); + break; + case e1000_fc_tx_pause: + /* Tx Flow control is enabled, and Rx Flow control is + * disabled, by a software over-ride. + */ + mii_autoneg_adv_reg |= NWAY_AR_ASM_DIR; + mii_autoneg_adv_reg &= ~NWAY_AR_PAUSE; + break; + case e1000_fc_full: + /* Flow control (both Rx and Tx) is enabled by a software + * over-ride. + */ + mii_autoneg_adv_reg |= (NWAY_AR_ASM_DIR | NWAY_AR_PAUSE); + break; + default: + DEBUGOUT("Flow control param set incorrectly\n"); + return -E1000_ERR_CONFIG; + } + + ret_val = phy->ops.write_reg(hw, PHY_AUTONEG_ADV, mii_autoneg_adv_reg); + if (ret_val) + return ret_val; + + DEBUGOUT1("Auto-Neg Advertising %x\n", mii_autoneg_adv_reg); + + if (phy->autoneg_mask & ADVERTISE_1000_FULL) + ret_val = phy->ops.write_reg(hw, PHY_1000T_CTRL, + mii_1000t_ctrl_reg); + + return ret_val; +} + +/** + * e1000_copper_link_autoneg - Setup/Enable autoneg for copper link + * @hw: pointer to the HW structure + * + * Performs initial bounds checking on autoneg advertisement parameter, then + * configure to advertise the full capability. Setup the PHY to autoneg + * and restart the negotiation process between the link partner. If + * autoneg_wait_to_complete, then wait for autoneg to complete before exiting. + **/ +static s32 e1000_copper_link_autoneg(struct e1000_hw *hw) +{ + struct e1000_phy_info *phy = &hw->phy; + s32 ret_val; + u16 phy_ctrl; + + DEBUGFUNC("e1000_copper_link_autoneg"); + + /* Perform some bounds checking on the autoneg advertisement + * parameter. + */ + phy->autoneg_advertised &= phy->autoneg_mask; + + /* If autoneg_advertised is zero, we assume it was not defaulted + * by the calling code so we set to advertise full capability. + */ + if (!phy->autoneg_advertised) + phy->autoneg_advertised = phy->autoneg_mask; + + DEBUGOUT("Reconfiguring auto-neg advertisement params\n"); + ret_val = e1000_phy_setup_autoneg(hw); + if (ret_val) { + DEBUGOUT("Error Setting up Auto-Negotiation\n"); + return ret_val; + } + DEBUGOUT("Restarting Auto-Neg\n"); + + /* Restart auto-negotiation by setting the Auto Neg Enable bit and + * the Auto Neg Restart bit in the PHY control register. + */ + ret_val = phy->ops.read_reg(hw, PHY_CONTROL, &phy_ctrl); + if (ret_val) + return ret_val; + + phy_ctrl |= (MII_CR_AUTO_NEG_EN | MII_CR_RESTART_AUTO_NEG); + ret_val = phy->ops.write_reg(hw, PHY_CONTROL, phy_ctrl); + if (ret_val) + return ret_val; + + /* Does the user want to wait for Auto-Neg to complete here, or + * check at a later time (for example, callback routine). + */ + if (phy->autoneg_wait_to_complete) { + ret_val = e1000_wait_autoneg(hw); + if (ret_val) { + DEBUGOUT("Error while waiting for autoneg to complete\n"); + return ret_val; + } + } + + hw->mac.get_link_status = true; + + return ret_val; +} + +/** + * e1000_setup_copper_link_generic - Configure copper link settings + * @hw: pointer to the HW structure + * + * Calls the appropriate function to configure the link for auto-neg or forced + * speed and duplex. Then we check for link, once link is established calls + * to configure collision distance and flow control are called. If link is + * not established, we return -E1000_ERR_PHY (-2). + **/ +s32 e1000_setup_copper_link_generic(struct e1000_hw *hw) +{ + s32 ret_val; + bool link; + + DEBUGFUNC("e1000_setup_copper_link_generic"); + + if (hw->mac.autoneg) { + /* Setup autoneg and flow control advertisement and perform + * autonegotiation. + */ + ret_val = e1000_copper_link_autoneg(hw); + if (ret_val) + return ret_val; + } else { + /* PHY will be set to 10H, 10F, 100H or 100F + * depending on user settings. + */ + DEBUGOUT("Forcing Speed and Duplex\n"); + ret_val = hw->phy.ops.force_speed_duplex(hw); + if (ret_val) { + DEBUGOUT("Error Forcing Speed and Duplex\n"); + return ret_val; + } + } + + /* Check link status. Wait up to 100 microseconds for link to become + * valid. + */ + ret_val = e1000_phy_has_link_generic(hw, COPPER_LINK_UP_LIMIT, 10, + &link); + if (ret_val) + return ret_val; + + if (link) { + DEBUGOUT("Valid link established!!!\n"); + hw->mac.ops.config_collision_dist(hw); + ret_val = e1000_config_fc_after_link_up_generic(hw); + } else { + DEBUGOUT("Unable to establish link!!!\n"); + } + + return ret_val; +} + +/** + * e1000_phy_force_speed_duplex_igp - Force speed/duplex for igp PHY + * @hw: pointer to the HW structure + * + * Calls the PHY setup function to force speed and duplex. Clears the + * auto-crossover to force MDI manually. Waits for link and returns + * successful if link up is successful, else -E1000_ERR_PHY (-2). + **/ +s32 e1000_phy_force_speed_duplex_igp(struct e1000_hw *hw) +{ + struct e1000_phy_info *phy = &hw->phy; + s32 ret_val; + u16 phy_data; + bool link; + + DEBUGFUNC("e1000_phy_force_speed_duplex_igp"); + + ret_val = phy->ops.read_reg(hw, PHY_CONTROL, &phy_data); + if (ret_val) + return ret_val; + + e1000_phy_force_speed_duplex_setup(hw, &phy_data); + + ret_val = phy->ops.write_reg(hw, PHY_CONTROL, phy_data); + if (ret_val) + return ret_val; + + /* Clear Auto-Crossover to force MDI manually. IGP requires MDI + * forced whenever speed and duplex are forced. + */ + ret_val = phy->ops.read_reg(hw, IGP01E1000_PHY_PORT_CTRL, &phy_data); + if (ret_val) + return ret_val; + + phy_data &= ~IGP01E1000_PSCR_AUTO_MDIX; + phy_data &= ~IGP01E1000_PSCR_FORCE_MDI_MDIX; + + ret_val = phy->ops.write_reg(hw, IGP01E1000_PHY_PORT_CTRL, phy_data); + if (ret_val) + return ret_val; + + DEBUGOUT1("IGP PSCR: %X\n", phy_data); + + usec_delay(1); + + if (phy->autoneg_wait_to_complete) { + DEBUGOUT("Waiting for forced speed/duplex link on IGP phy.\n"); + + ret_val = e1000_phy_has_link_generic(hw, PHY_FORCE_LIMIT, + 100000, &link); + if (ret_val) + return ret_val; + + if (!link) + DEBUGOUT("Link taking longer than expected.\n"); + + /* Try once more */ + ret_val = e1000_phy_has_link_generic(hw, PHY_FORCE_LIMIT, + 100000, &link); + } + + return ret_val; +} + +/** + * e1000_phy_force_speed_duplex_m88 - Force speed/duplex for m88 PHY + * @hw: pointer to the HW structure + * + * Calls the PHY setup function to force speed and duplex. Clears the + * auto-crossover to force MDI manually. Resets the PHY to commit the + * changes. If time expires while waiting for link up, we reset the DSP. + * After reset, TX_CLK and CRS on Tx must be set. Return successful upon + * successful completion, else return corresponding error code. + **/ +s32 e1000_phy_force_speed_duplex_m88(struct e1000_hw *hw) +{ + struct e1000_phy_info *phy = &hw->phy; + s32 ret_val; + u16 phy_data; + bool link; + + DEBUGFUNC("e1000_phy_force_speed_duplex_m88"); + + /* I210 and I211 devices support Auto-Crossover in forced operation. */ + if (phy->type != e1000_phy_i210) { + /* Clear Auto-Crossover to force MDI manually. M88E1000 + * requires MDI forced whenever speed and duplex are forced. + */ + ret_val = phy->ops.read_reg(hw, M88E1000_PHY_SPEC_CTRL, + &phy_data); + if (ret_val) + return ret_val; + + phy_data &= ~M88E1000_PSCR_AUTO_X_MODE; + ret_val = phy->ops.write_reg(hw, M88E1000_PHY_SPEC_CTRL, + phy_data); + if (ret_val) + return ret_val; + } + + DEBUGOUT1("M88E1000 PSCR: %X\n", phy_data); + + ret_val = phy->ops.read_reg(hw, PHY_CONTROL, &phy_data); + if (ret_val) + return ret_val; + + e1000_phy_force_speed_duplex_setup(hw, &phy_data); + + ret_val = phy->ops.write_reg(hw, PHY_CONTROL, phy_data); + if (ret_val) + return ret_val; + + /* Reset the phy to commit changes. */ + ret_val = hw->phy.ops.commit(hw); + if (ret_val) + return ret_val; + + if (phy->autoneg_wait_to_complete) { + DEBUGOUT("Waiting for forced speed/duplex link on M88 phy.\n"); + + ret_val = e1000_phy_has_link_generic(hw, PHY_FORCE_LIMIT, + 100000, &link); + if (ret_val) + return ret_val; + + if (!link) { + bool reset_dsp = true; + + switch (hw->phy.id) { + case I347AT4_E_PHY_ID: + case M88E1340M_E_PHY_ID: + case M88E1112_E_PHY_ID: + case M88E1543_E_PHY_ID: + case M88E1512_E_PHY_ID: + case I210_I_PHY_ID: + reset_dsp = false; + break; + default: + if (hw->phy.type != e1000_phy_m88) + reset_dsp = false; + break; + } + + if (!reset_dsp) { + DEBUGOUT("Link taking longer than expected.\n"); + } else { + /* We didn't get link. + * Reset the DSP and cross our fingers. + */ + ret_val = phy->ops.write_reg(hw, + M88E1000_PHY_PAGE_SELECT, + 0x001d); + if (ret_val) + return ret_val; + ret_val = e1000_phy_reset_dsp_generic(hw); + if (ret_val) + return ret_val; + } + } + + /* Try once more */ + ret_val = e1000_phy_has_link_generic(hw, PHY_FORCE_LIMIT, + 100000, &link); + if (ret_val) + return ret_val; + } + + if (hw->phy.type != e1000_phy_m88) + return E1000_SUCCESS; + + if (hw->phy.id == I347AT4_E_PHY_ID || + hw->phy.id == M88E1340M_E_PHY_ID || + hw->phy.id == M88E1112_E_PHY_ID) + return E1000_SUCCESS; + if (hw->phy.id == I210_I_PHY_ID) + return E1000_SUCCESS; + if ((hw->phy.id == M88E1543_E_PHY_ID) || + (hw->phy.id == M88E1512_E_PHY_ID)) + return E1000_SUCCESS; + ret_val = phy->ops.read_reg(hw, M88E1000_EXT_PHY_SPEC_CTRL, &phy_data); + if (ret_val) + return ret_val; + + /* Resetting the phy means we need to re-force TX_CLK in the + * Extended PHY Specific Control Register to 25MHz clock from + * the reset value of 2.5MHz. + */ + phy_data |= M88E1000_EPSCR_TX_CLK_25; + ret_val = phy->ops.write_reg(hw, M88E1000_EXT_PHY_SPEC_CTRL, phy_data); + if (ret_val) + return ret_val; + + /* In addition, we must re-enable CRS on Tx for both half and full + * duplex. + */ + ret_val = phy->ops.read_reg(hw, M88E1000_PHY_SPEC_CTRL, &phy_data); + if (ret_val) + return ret_val; + + phy_data |= M88E1000_PSCR_ASSERT_CRS_ON_TX; + ret_val = phy->ops.write_reg(hw, M88E1000_PHY_SPEC_CTRL, phy_data); + + return ret_val; +} + +/** + * e1000_phy_force_speed_duplex_ife - Force PHY speed & duplex + * @hw: pointer to the HW structure + * + * Forces the speed and duplex settings of the PHY. + * This is a function pointer entry point only called by + * PHY setup routines. + **/ +s32 e1000_phy_force_speed_duplex_ife(struct e1000_hw *hw) +{ + struct e1000_phy_info *phy = &hw->phy; + s32 ret_val; + u16 data; + bool link; + + DEBUGFUNC("e1000_phy_force_speed_duplex_ife"); + + ret_val = phy->ops.read_reg(hw, PHY_CONTROL, &data); + if (ret_val) + return ret_val; + + e1000_phy_force_speed_duplex_setup(hw, &data); + + ret_val = phy->ops.write_reg(hw, PHY_CONTROL, data); + if (ret_val) + return ret_val; + + /* Disable MDI-X support for 10/100 */ + ret_val = phy->ops.read_reg(hw, IFE_PHY_MDIX_CONTROL, &data); + if (ret_val) + return ret_val; + + data &= ~IFE_PMC_AUTO_MDIX; + data &= ~IFE_PMC_FORCE_MDIX; + + ret_val = phy->ops.write_reg(hw, IFE_PHY_MDIX_CONTROL, data); + if (ret_val) + return ret_val; + + DEBUGOUT1("IFE PMC: %X\n", data); + + usec_delay(1); + + if (phy->autoneg_wait_to_complete) { + DEBUGOUT("Waiting for forced speed/duplex link on IFE phy.\n"); + + ret_val = e1000_phy_has_link_generic(hw, PHY_FORCE_LIMIT, + 100000, &link); + if (ret_val) + return ret_val; + + if (!link) + DEBUGOUT("Link taking longer than expected.\n"); + + /* Try once more */ + ret_val = e1000_phy_has_link_generic(hw, PHY_FORCE_LIMIT, + 100000, &link); + if (ret_val) + return ret_val; + } + + return E1000_SUCCESS; +} + +/** + * e1000_phy_force_speed_duplex_setup - Configure forced PHY speed/duplex + * @hw: pointer to the HW structure + * @phy_ctrl: pointer to current value of PHY_CONTROL + * + * Forces speed and duplex on the PHY by doing the following: disable flow + * control, force speed/duplex on the MAC, disable auto speed detection, + * disable auto-negotiation, configure duplex, configure speed, configure + * the collision distance, write configuration to CTRL register. The + * caller must write to the PHY_CONTROL register for these settings to + * take affect. + **/ +void e1000_phy_force_speed_duplex_setup(struct e1000_hw *hw, u16 *phy_ctrl) +{ + struct e1000_mac_info *mac = &hw->mac; + u32 ctrl; + + DEBUGFUNC("e1000_phy_force_speed_duplex_setup"); + + /* Turn off flow control when forcing speed/duplex */ + hw->fc.current_mode = e1000_fc_none; + + /* Force speed/duplex on the mac */ + ctrl = E1000_READ_REG(hw, E1000_CTRL); + ctrl |= (E1000_CTRL_FRCSPD | E1000_CTRL_FRCDPX); + ctrl &= ~E1000_CTRL_SPD_SEL; + + /* Disable Auto Speed Detection */ + ctrl &= ~E1000_CTRL_ASDE; + + /* Disable autoneg on the phy */ + *phy_ctrl &= ~MII_CR_AUTO_NEG_EN; + + /* Forcing Full or Half Duplex? */ + if (mac->forced_speed_duplex & E1000_ALL_HALF_DUPLEX) { + ctrl &= ~E1000_CTRL_FD; + *phy_ctrl &= ~MII_CR_FULL_DUPLEX; + DEBUGOUT("Half Duplex\n"); + } else { + ctrl |= E1000_CTRL_FD; + *phy_ctrl |= MII_CR_FULL_DUPLEX; + DEBUGOUT("Full Duplex\n"); + } + + /* Forcing 10mb or 100mb? */ + if (mac->forced_speed_duplex & E1000_ALL_100_SPEED) { + ctrl |= E1000_CTRL_SPD_100; + *phy_ctrl |= MII_CR_SPEED_100; + *phy_ctrl &= ~MII_CR_SPEED_1000; + DEBUGOUT("Forcing 100mb\n"); + } else { + ctrl &= ~(E1000_CTRL_SPD_1000 | E1000_CTRL_SPD_100); + *phy_ctrl &= ~(MII_CR_SPEED_1000 | MII_CR_SPEED_100); + DEBUGOUT("Forcing 10mb\n"); + } + + hw->mac.ops.config_collision_dist(hw); + + E1000_WRITE_REG(hw, E1000_CTRL, ctrl); +} + +/** + * e1000_set_d3_lplu_state_generic - Sets low power link up state for D3 + * @hw: pointer to the HW structure + * @active: boolean used to enable/disable lplu + * + * Success returns 0, Failure returns 1 + * + * The low power link up (lplu) state is set to the power management level D3 + * and SmartSpeed is disabled when active is true, else clear lplu for D3 + * and enable Smartspeed. LPLU and Smartspeed are mutually exclusive. LPLU + * is used during Dx states where the power conservation is most important. + * During driver activity, SmartSpeed should be enabled so performance is + * maintained. + **/ +s32 e1000_set_d3_lplu_state_generic(struct e1000_hw *hw, bool active) +{ + struct e1000_phy_info *phy = &hw->phy; + s32 ret_val; + u16 data; + + DEBUGFUNC("e1000_set_d3_lplu_state_generic"); + + if (!hw->phy.ops.read_reg) + return E1000_SUCCESS; + + ret_val = phy->ops.read_reg(hw, IGP02E1000_PHY_POWER_MGMT, &data); + if (ret_val) + return ret_val; + + if (!active) { + data &= ~IGP02E1000_PM_D3_LPLU; + ret_val = phy->ops.write_reg(hw, IGP02E1000_PHY_POWER_MGMT, + data); + if (ret_val) + return ret_val; + /* LPLU and SmartSpeed are mutually exclusive. LPLU is used + * during Dx states where the power conservation is most + * important. During driver activity we should enable + * SmartSpeed, so performance is maintained. + */ + if (phy->smart_speed == e1000_smart_speed_on) { + ret_val = phy->ops.read_reg(hw, + IGP01E1000_PHY_PORT_CONFIG, + &data); + if (ret_val) + return ret_val; + + data |= IGP01E1000_PSCFR_SMART_SPEED; + ret_val = phy->ops.write_reg(hw, + IGP01E1000_PHY_PORT_CONFIG, + data); + if (ret_val) + return ret_val; + } else if (phy->smart_speed == e1000_smart_speed_off) { + ret_val = phy->ops.read_reg(hw, + IGP01E1000_PHY_PORT_CONFIG, + &data); + if (ret_val) + return ret_val; + + data &= ~IGP01E1000_PSCFR_SMART_SPEED; + ret_val = phy->ops.write_reg(hw, + IGP01E1000_PHY_PORT_CONFIG, + data); + if (ret_val) + return ret_val; + } + } else if ((phy->autoneg_advertised == E1000_ALL_SPEED_DUPLEX) || + (phy->autoneg_advertised == E1000_ALL_NOT_GIG) || + (phy->autoneg_advertised == E1000_ALL_10_SPEED)) { + data |= IGP02E1000_PM_D3_LPLU; + ret_val = phy->ops.write_reg(hw, IGP02E1000_PHY_POWER_MGMT, + data); + if (ret_val) + return ret_val; + + /* When LPLU is enabled, we should disable SmartSpeed */ + ret_val = phy->ops.read_reg(hw, IGP01E1000_PHY_PORT_CONFIG, + &data); + if (ret_val) + return ret_val; + + data &= ~IGP01E1000_PSCFR_SMART_SPEED; + ret_val = phy->ops.write_reg(hw, IGP01E1000_PHY_PORT_CONFIG, + data); + } + + return ret_val; +} + +/** + * e1000_check_downshift_generic - Checks whether a downshift in speed occurred + * @hw: pointer to the HW structure + * + * Success returns 0, Failure returns 1 + * + * A downshift is detected by querying the PHY link health. + **/ +s32 e1000_check_downshift_generic(struct e1000_hw *hw) +{ + struct e1000_phy_info *phy = &hw->phy; + s32 ret_val; + u16 phy_data, offset, mask; + + DEBUGFUNC("e1000_check_downshift_generic"); + + switch (phy->type) { + case e1000_phy_i210: + case e1000_phy_m88: + case e1000_phy_gg82563: + offset = M88E1000_PHY_SPEC_STATUS; + mask = M88E1000_PSSR_DOWNSHIFT; + break; + case e1000_phy_igp_2: + case e1000_phy_igp_3: + offset = IGP01E1000_PHY_LINK_HEALTH; + mask = IGP01E1000_PLHR_SS_DOWNGRADE; + break; + default: + /* speed downshift not supported */ + phy->speed_downgraded = false; + return E1000_SUCCESS; + } + + ret_val = phy->ops.read_reg(hw, offset, &phy_data); + + if (!ret_val) + phy->speed_downgraded = !!(phy_data & mask); + + return ret_val; +} + +/** + * e1000_check_polarity_m88 - Checks the polarity. + * @hw: pointer to the HW structure + * + * Success returns 0, Failure returns -E1000_ERR_PHY (-2) + * + * Polarity is determined based on the PHY specific status register. + **/ +s32 e1000_check_polarity_m88(struct e1000_hw *hw) +{ + struct e1000_phy_info *phy = &hw->phy; + s32 ret_val; + u16 data; + + DEBUGFUNC("e1000_check_polarity_m88"); + + ret_val = phy->ops.read_reg(hw, M88E1000_PHY_SPEC_STATUS, &data); + + if (!ret_val) + phy->cable_polarity = ((data & M88E1000_PSSR_REV_POLARITY) + ? e1000_rev_polarity_reversed + : e1000_rev_polarity_normal); + + return ret_val; +} + +/** + * e1000_check_polarity_igp - Checks the polarity. + * @hw: pointer to the HW structure + * + * Success returns 0, Failure returns -E1000_ERR_PHY (-2) + * + * Polarity is determined based on the PHY port status register, and the + * current speed (since there is no polarity at 100Mbps). + **/ +s32 e1000_check_polarity_igp(struct e1000_hw *hw) +{ + struct e1000_phy_info *phy = &hw->phy; + s32 ret_val; + u16 data, offset, mask; + + DEBUGFUNC("e1000_check_polarity_igp"); + + /* Polarity is determined based on the speed of + * our connection. + */ + ret_val = phy->ops.read_reg(hw, IGP01E1000_PHY_PORT_STATUS, &data); + if (ret_val) + return ret_val; + + if ((data & IGP01E1000_PSSR_SPEED_MASK) == + IGP01E1000_PSSR_SPEED_1000MBPS) { + offset = IGP01E1000_PHY_PCS_INIT_REG; + mask = IGP01E1000_PHY_POLARITY_MASK; + } else { + /* This really only applies to 10Mbps since + * there is no polarity for 100Mbps (always 0). + */ + offset = IGP01E1000_PHY_PORT_STATUS; + mask = IGP01E1000_PSSR_POLARITY_REVERSED; + } + + ret_val = phy->ops.read_reg(hw, offset, &data); + + if (!ret_val) + phy->cable_polarity = ((data & mask) + ? e1000_rev_polarity_reversed + : e1000_rev_polarity_normal); + + return ret_val; +} + +/** + * e1000_check_polarity_ife - Check cable polarity for IFE PHY + * @hw: pointer to the HW structure + * + * Polarity is determined on the polarity reversal feature being enabled. + **/ +s32 e1000_check_polarity_ife(struct e1000_hw *hw) +{ + struct e1000_phy_info *phy = &hw->phy; + s32 ret_val; + u16 phy_data, offset, mask; + + DEBUGFUNC("e1000_check_polarity_ife"); + + /* Polarity is determined based on the reversal feature being enabled. + */ + if (phy->polarity_correction) { + offset = IFE_PHY_EXTENDED_STATUS_CONTROL; + mask = IFE_PESC_POLARITY_REVERSED; + } else { + offset = IFE_PHY_SPECIAL_CONTROL; + mask = IFE_PSC_FORCE_POLARITY; + } + + ret_val = phy->ops.read_reg(hw, offset, &phy_data); + + if (!ret_val) + phy->cable_polarity = ((phy_data & mask) + ? e1000_rev_polarity_reversed + : e1000_rev_polarity_normal); + + return ret_val; +} + +/** + * e1000_wait_autoneg - Wait for auto-neg completion + * @hw: pointer to the HW structure + * + * Waits for auto-negotiation to complete or for the auto-negotiation time + * limit to expire, which ever happens first. + **/ +static s32 e1000_wait_autoneg(struct e1000_hw *hw) +{ + s32 ret_val = E1000_SUCCESS; + u16 i, phy_status; + + DEBUGFUNC("e1000_wait_autoneg"); + + if (!hw->phy.ops.read_reg) + return E1000_SUCCESS; + + /* Break after autoneg completes or PHY_AUTO_NEG_LIMIT expires. */ + for (i = PHY_AUTO_NEG_LIMIT; i > 0; i--) { + ret_val = hw->phy.ops.read_reg(hw, PHY_STATUS, &phy_status); + if (ret_val) + break; + ret_val = hw->phy.ops.read_reg(hw, PHY_STATUS, &phy_status); + if (ret_val) + break; + if (phy_status & MII_SR_AUTONEG_COMPLETE) + break; + msec_delay(100); + } + + /* PHY_AUTO_NEG_TIME expiration doesn't guarantee auto-negotiation + * has completed. + */ + return ret_val; +} + +/** + * e1000_phy_has_link_generic - Polls PHY for link + * @hw: pointer to the HW structure + * @iterations: number of times to poll for link + * @usec_interval: delay between polling attempts + * @success: pointer to whether polling was successful or not + * + * Polls the PHY status register for link, 'iterations' number of times. + **/ +s32 e1000_phy_has_link_generic(struct e1000_hw *hw, u32 iterations, + u32 usec_interval, bool *success) +{ + s32 ret_val = E1000_SUCCESS; + u16 i, phy_status; + + DEBUGFUNC("e1000_phy_has_link_generic"); + + if (!hw->phy.ops.read_reg) + return E1000_SUCCESS; + + for (i = 0; i < iterations; i++) { + /* Some PHYs require the PHY_STATUS register to be read + * twice due to the link bit being sticky. No harm doing + * it across the board. + */ + ret_val = hw->phy.ops.read_reg(hw, PHY_STATUS, &phy_status); + if (ret_val) { + /* If the first read fails, another entity may have + * ownership of the resources, wait and try again to + * see if they have relinquished the resources yet. + */ + if (usec_interval >= 1000) + msec_delay(usec_interval/1000); + else + usec_delay(usec_interval); + } + ret_val = hw->phy.ops.read_reg(hw, PHY_STATUS, &phy_status); + if (ret_val) + break; + if (phy_status & MII_SR_LINK_STATUS) + break; + if (usec_interval >= 1000) + msec_delay(usec_interval/1000); + else + usec_delay(usec_interval); + } + + *success = (i < iterations); + + return ret_val; +} + +/** + * e1000_get_cable_length_m88 - Determine cable length for m88 PHY + * @hw: pointer to the HW structure + * + * Reads the PHY specific status register to retrieve the cable length + * information. The cable length is determined by averaging the minimum and + * maximum values to get the "average" cable length. The m88 PHY has four + * possible cable length values, which are: + * Register Value Cable Length + * 0 < 50 meters + * 1 50 - 80 meters + * 2 80 - 110 meters + * 3 110 - 140 meters + * 4 > 140 meters + **/ +s32 e1000_get_cable_length_m88(struct e1000_hw *hw) +{ + struct e1000_phy_info *phy = &hw->phy; + s32 ret_val; + u16 phy_data, index; + + DEBUGFUNC("e1000_get_cable_length_m88"); + + ret_val = phy->ops.read_reg(hw, M88E1000_PHY_SPEC_STATUS, &phy_data); + if (ret_val) + return ret_val; + + index = ((phy_data & M88E1000_PSSR_CABLE_LENGTH) >> + M88E1000_PSSR_CABLE_LENGTH_SHIFT); + + if (index >= M88E1000_CABLE_LENGTH_TABLE_SIZE - 1) + return -E1000_ERR_PHY; + + phy->min_cable_length = e1000_m88_cable_length_table[index]; + phy->max_cable_length = e1000_m88_cable_length_table[index + 1]; + + phy->cable_length = (phy->min_cable_length + phy->max_cable_length) / 2; + + return E1000_SUCCESS; +} + +s32 e1000_get_cable_length_m88_gen2(struct e1000_hw *hw) +{ + struct e1000_phy_info *phy = &hw->phy; + s32 ret_val; + u16 phy_data, phy_data2, is_cm; + u16 index, default_page; + + DEBUGFUNC("e1000_get_cable_length_m88_gen2"); + + switch (hw->phy.id) { + case I210_I_PHY_ID: + /* Get cable length from PHY Cable Diagnostics Control Reg */ + ret_val = phy->ops.read_reg(hw, (0x7 << GS40G_PAGE_SHIFT) + + (I347AT4_PCDL + phy->addr), + &phy_data); + if (ret_val) + return ret_val; + + /* Check if the unit of cable length is meters or cm */ + ret_val = phy->ops.read_reg(hw, (0x7 << GS40G_PAGE_SHIFT) + + I347AT4_PCDC, &phy_data2); + if (ret_val) + return ret_val; + + is_cm = !(phy_data2 & I347AT4_PCDC_CABLE_LENGTH_UNIT); + + /* Populate the phy structure with cable length in meters */ + phy->min_cable_length = phy_data / (is_cm ? 100 : 1); + phy->max_cable_length = phy_data / (is_cm ? 100 : 1); + phy->cable_length = phy_data / (is_cm ? 100 : 1); + break; + case M88E1543_E_PHY_ID: + case M88E1512_E_PHY_ID: + case M88E1340M_E_PHY_ID: + case I347AT4_E_PHY_ID: + /* Remember the original page select and set it to 7 */ + ret_val = phy->ops.read_reg(hw, I347AT4_PAGE_SELECT, + &default_page); + if (ret_val) + return ret_val; + + ret_val = phy->ops.write_reg(hw, I347AT4_PAGE_SELECT, 0x07); + if (ret_val) + return ret_val; + + /* Get cable length from PHY Cable Diagnostics Control Reg */ + ret_val = phy->ops.read_reg(hw, (I347AT4_PCDL + phy->addr), + &phy_data); + if (ret_val) + return ret_val; + + /* Check if the unit of cable length is meters or cm */ + ret_val = phy->ops.read_reg(hw, I347AT4_PCDC, &phy_data2); + if (ret_val) + return ret_val; + + is_cm = !(phy_data2 & I347AT4_PCDC_CABLE_LENGTH_UNIT); + + /* Populate the phy structure with cable length in meters */ + phy->min_cable_length = phy_data / (is_cm ? 100 : 1); + phy->max_cable_length = phy_data / (is_cm ? 100 : 1); + phy->cable_length = phy_data / (is_cm ? 100 : 1); + + /* Reset the page select to its original value */ + ret_val = phy->ops.write_reg(hw, I347AT4_PAGE_SELECT, + default_page); + if (ret_val) + return ret_val; + break; + + case M88E1112_E_PHY_ID: + /* Remember the original page select and set it to 5 */ + ret_val = phy->ops.read_reg(hw, I347AT4_PAGE_SELECT, + &default_page); + if (ret_val) + return ret_val; + + ret_val = phy->ops.write_reg(hw, I347AT4_PAGE_SELECT, 0x05); + if (ret_val) + return ret_val; + + ret_val = phy->ops.read_reg(hw, M88E1112_VCT_DSP_DISTANCE, + &phy_data); + if (ret_val) + return ret_val; + + index = (phy_data & M88E1000_PSSR_CABLE_LENGTH) >> + M88E1000_PSSR_CABLE_LENGTH_SHIFT; + + if (index >= M88E1000_CABLE_LENGTH_TABLE_SIZE - 1) + return -E1000_ERR_PHY; + + phy->min_cable_length = e1000_m88_cable_length_table[index]; + phy->max_cable_length = e1000_m88_cable_length_table[index + 1]; + + phy->cable_length = (phy->min_cable_length + + phy->max_cable_length) / 2; + + /* Reset the page select to its original value */ + ret_val = phy->ops.write_reg(hw, I347AT4_PAGE_SELECT, + default_page); + if (ret_val) + return ret_val; + + break; + default: + return -E1000_ERR_PHY; + } + + return ret_val; +} + +/** + * e1000_get_cable_length_igp_2 - Determine cable length for igp2 PHY + * @hw: pointer to the HW structure + * + * The automatic gain control (agc) normalizes the amplitude of the + * received signal, adjusting for the attenuation produced by the + * cable. By reading the AGC registers, which represent the + * combination of coarse and fine gain value, the value can be put + * into a lookup table to obtain the approximate cable length + * for each channel. + **/ +s32 e1000_get_cable_length_igp_2(struct e1000_hw *hw) +{ + struct e1000_phy_info *phy = &hw->phy; + s32 ret_val; + u16 phy_data, i, agc_value = 0; + u16 cur_agc_index, max_agc_index = 0; + u16 min_agc_index = IGP02E1000_CABLE_LENGTH_TABLE_SIZE - 1; + static const u16 agc_reg_array[IGP02E1000_PHY_CHANNEL_NUM] = { + IGP02E1000_PHY_AGC_A, + IGP02E1000_PHY_AGC_B, + IGP02E1000_PHY_AGC_C, + IGP02E1000_PHY_AGC_D + }; + + DEBUGFUNC("e1000_get_cable_length_igp_2"); + + /* Read the AGC registers for all channels */ + for (i = 0; i < IGP02E1000_PHY_CHANNEL_NUM; i++) { + ret_val = phy->ops.read_reg(hw, agc_reg_array[i], &phy_data); + if (ret_val) + return ret_val; + + /* Getting bits 15:9, which represent the combination of + * coarse and fine gain values. The result is a number + * that can be put into the lookup table to obtain the + * approximate cable length. + */ + cur_agc_index = ((phy_data >> IGP02E1000_AGC_LENGTH_SHIFT) & + IGP02E1000_AGC_LENGTH_MASK); + + /* Array index bound check. */ + if ((cur_agc_index >= IGP02E1000_CABLE_LENGTH_TABLE_SIZE) || + (cur_agc_index == 0)) + return -E1000_ERR_PHY; + + /* Remove min & max AGC values from calculation. */ + if (e1000_igp_2_cable_length_table[min_agc_index] > + e1000_igp_2_cable_length_table[cur_agc_index]) + min_agc_index = cur_agc_index; + if (e1000_igp_2_cable_length_table[max_agc_index] < + e1000_igp_2_cable_length_table[cur_agc_index]) + max_agc_index = cur_agc_index; + + agc_value += e1000_igp_2_cable_length_table[cur_agc_index]; + } + + agc_value -= (e1000_igp_2_cable_length_table[min_agc_index] + + e1000_igp_2_cable_length_table[max_agc_index]); + agc_value /= (IGP02E1000_PHY_CHANNEL_NUM - 2); + + /* Calculate cable length with the error range of +/- 10 meters. */ + phy->min_cable_length = (((agc_value - IGP02E1000_AGC_RANGE) > 0) ? + (agc_value - IGP02E1000_AGC_RANGE) : 0); + phy->max_cable_length = agc_value + IGP02E1000_AGC_RANGE; + + phy->cable_length = (phy->min_cable_length + phy->max_cable_length) / 2; + + return E1000_SUCCESS; +} + +/** + * e1000_get_phy_info_m88 - Retrieve PHY information + * @hw: pointer to the HW structure + * + * Valid for only copper links. Read the PHY status register (sticky read) + * to verify that link is up. Read the PHY special control register to + * determine the polarity and 10base-T extended distance. Read the PHY + * special status register to determine MDI/MDIx and current speed. If + * speed is 1000, then determine cable length, local and remote receiver. + **/ +s32 e1000_get_phy_info_m88(struct e1000_hw *hw) +{ + struct e1000_phy_info *phy = &hw->phy; + s32 ret_val; + u16 phy_data; + bool link; + + DEBUGFUNC("e1000_get_phy_info_m88"); + + if (phy->media_type != e1000_media_type_copper) { + DEBUGOUT("Phy info is only valid for copper media\n"); + return -E1000_ERR_CONFIG; + } + + ret_val = e1000_phy_has_link_generic(hw, 1, 0, &link); + if (ret_val) + return ret_val; + + if (!link) { + DEBUGOUT("Phy info is only valid if link is up\n"); + return -E1000_ERR_CONFIG; + } + + ret_val = phy->ops.read_reg(hw, M88E1000_PHY_SPEC_CTRL, &phy_data); + if (ret_val) + return ret_val; + + phy->polarity_correction = !!(phy_data & + M88E1000_PSCR_POLARITY_REVERSAL); + + ret_val = e1000_check_polarity_m88(hw); + if (ret_val) + return ret_val; + + ret_val = phy->ops.read_reg(hw, M88E1000_PHY_SPEC_STATUS, &phy_data); + if (ret_val) + return ret_val; + + phy->is_mdix = !!(phy_data & M88E1000_PSSR_MDIX); + + if ((phy_data & M88E1000_PSSR_SPEED) == M88E1000_PSSR_1000MBS) { + ret_val = hw->phy.ops.get_cable_length(hw); + if (ret_val) + return ret_val; + + ret_val = phy->ops.read_reg(hw, PHY_1000T_STATUS, &phy_data); + if (ret_val) + return ret_val; + + phy->local_rx = (phy_data & SR_1000T_LOCAL_RX_STATUS) + ? e1000_1000t_rx_status_ok + : e1000_1000t_rx_status_not_ok; + + phy->remote_rx = (phy_data & SR_1000T_REMOTE_RX_STATUS) + ? e1000_1000t_rx_status_ok + : e1000_1000t_rx_status_not_ok; + } else { + /* Set values to "undefined" */ + phy->cable_length = E1000_CABLE_LENGTH_UNDEFINED; + phy->local_rx = e1000_1000t_rx_status_undefined; + phy->remote_rx = e1000_1000t_rx_status_undefined; + } + + return ret_val; +} + +/** + * e1000_get_phy_info_igp - Retrieve igp PHY information + * @hw: pointer to the HW structure + * + * Read PHY status to determine if link is up. If link is up, then + * set/determine 10base-T extended distance and polarity correction. Read + * PHY port status to determine MDI/MDIx and speed. Based on the speed, + * determine on the cable length, local and remote receiver. + **/ +s32 e1000_get_phy_info_igp(struct e1000_hw *hw) +{ + struct e1000_phy_info *phy = &hw->phy; + s32 ret_val; + u16 data; + bool link; + + DEBUGFUNC("e1000_get_phy_info_igp"); + + ret_val = e1000_phy_has_link_generic(hw, 1, 0, &link); + if (ret_val) + return ret_val; + + if (!link) { + DEBUGOUT("Phy info is only valid if link is up\n"); + return -E1000_ERR_CONFIG; + } + + phy->polarity_correction = true; + + ret_val = e1000_check_polarity_igp(hw); + if (ret_val) + return ret_val; + + ret_val = phy->ops.read_reg(hw, IGP01E1000_PHY_PORT_STATUS, &data); + if (ret_val) + return ret_val; + + phy->is_mdix = !!(data & IGP01E1000_PSSR_MDIX); + + if ((data & IGP01E1000_PSSR_SPEED_MASK) == + IGP01E1000_PSSR_SPEED_1000MBPS) { + ret_val = phy->ops.get_cable_length(hw); + if (ret_val) + return ret_val; + + ret_val = phy->ops.read_reg(hw, PHY_1000T_STATUS, &data); + if (ret_val) + return ret_val; + + phy->local_rx = (data & SR_1000T_LOCAL_RX_STATUS) + ? e1000_1000t_rx_status_ok + : e1000_1000t_rx_status_not_ok; + + phy->remote_rx = (data & SR_1000T_REMOTE_RX_STATUS) + ? e1000_1000t_rx_status_ok + : e1000_1000t_rx_status_not_ok; + } else { + phy->cable_length = E1000_CABLE_LENGTH_UNDEFINED; + phy->local_rx = e1000_1000t_rx_status_undefined; + phy->remote_rx = e1000_1000t_rx_status_undefined; + } + + return ret_val; +} + +/** + * e1000_get_phy_info_ife - Retrieves various IFE PHY states + * @hw: pointer to the HW structure + * + * Populates "phy" structure with various feature states. + **/ +s32 e1000_get_phy_info_ife(struct e1000_hw *hw) +{ + struct e1000_phy_info *phy = &hw->phy; + s32 ret_val; + u16 data; + bool link; + + DEBUGFUNC("e1000_get_phy_info_ife"); + + ret_val = e1000_phy_has_link_generic(hw, 1, 0, &link); + if (ret_val) + return ret_val; + + if (!link) { + DEBUGOUT("Phy info is only valid if link is up\n"); + return -E1000_ERR_CONFIG; + } + + ret_val = phy->ops.read_reg(hw, IFE_PHY_SPECIAL_CONTROL, &data); + if (ret_val) + return ret_val; + phy->polarity_correction = !(data & IFE_PSC_AUTO_POLARITY_DISABLE); + + if (phy->polarity_correction) { + ret_val = e1000_check_polarity_ife(hw); + if (ret_val) + return ret_val; + } else { + /* Polarity is forced */ + phy->cable_polarity = ((data & IFE_PSC_FORCE_POLARITY) + ? e1000_rev_polarity_reversed + : e1000_rev_polarity_normal); + } + + ret_val = phy->ops.read_reg(hw, IFE_PHY_MDIX_CONTROL, &data); + if (ret_val) + return ret_val; + + phy->is_mdix = !!(data & IFE_PMC_MDIX_STATUS); + + /* The following parameters are undefined for 10/100 operation. */ + phy->cable_length = E1000_CABLE_LENGTH_UNDEFINED; + phy->local_rx = e1000_1000t_rx_status_undefined; + phy->remote_rx = e1000_1000t_rx_status_undefined; + + return E1000_SUCCESS; +} + +/** + * e1000_phy_sw_reset_generic - PHY software reset + * @hw: pointer to the HW structure + * + * Does a software reset of the PHY by reading the PHY control register and + * setting/write the control register reset bit to the PHY. + **/ +s32 e1000_phy_sw_reset_generic(struct e1000_hw *hw) +{ + s32 ret_val; + u16 phy_ctrl; + + DEBUGFUNC("e1000_phy_sw_reset_generic"); + + if (!hw->phy.ops.read_reg) + return E1000_SUCCESS; + + ret_val = hw->phy.ops.read_reg(hw, PHY_CONTROL, &phy_ctrl); + if (ret_val) + return ret_val; + + phy_ctrl |= MII_CR_RESET; + ret_val = hw->phy.ops.write_reg(hw, PHY_CONTROL, phy_ctrl); + if (ret_val) + return ret_val; + + usec_delay(1); + + return ret_val; +} + +/** + * e1000_phy_hw_reset_generic - PHY hardware reset + * @hw: pointer to the HW structure + * + * Verify the reset block is not blocking us from resetting. Acquire + * semaphore (if necessary) and read/set/write the device control reset + * bit in the PHY. Wait the appropriate delay time for the device to + * reset and release the semaphore (if necessary). + **/ +s32 e1000_phy_hw_reset_generic(struct e1000_hw *hw) +{ + struct e1000_phy_info *phy = &hw->phy; + s32 ret_val; + u32 ctrl; + + DEBUGFUNC("e1000_phy_hw_reset_generic"); + + if (phy->ops.check_reset_block) { + ret_val = phy->ops.check_reset_block(hw); + if (ret_val) + return E1000_SUCCESS; + } + + ret_val = phy->ops.acquire(hw); + if (ret_val) + return ret_val; + + ctrl = E1000_READ_REG(hw, E1000_CTRL); + E1000_WRITE_REG(hw, E1000_CTRL, ctrl | E1000_CTRL_PHY_RST); + E1000_WRITE_FLUSH(hw); + + usec_delay(phy->reset_delay_us); + + E1000_WRITE_REG(hw, E1000_CTRL, ctrl); + E1000_WRITE_FLUSH(hw); + + usec_delay(150); + + phy->ops.release(hw); + + return phy->ops.get_cfg_done(hw); +} + +/** + * e1000_get_cfg_done_generic - Generic configuration done + * @hw: pointer to the HW structure + * + * Generic function to wait 10 milli-seconds for configuration to complete + * and return success. + **/ +s32 e1000_get_cfg_done_generic(struct e1000_hw E1000_UNUSEDARG *hw) +{ + DEBUGFUNC("e1000_get_cfg_done_generic"); + + msec_delay_irq(10); + + return E1000_SUCCESS; +} + +/** + * e1000_phy_init_script_igp3 - Inits the IGP3 PHY + * @hw: pointer to the HW structure + * + * Initializes a Intel Gigabit PHY3 when an EEPROM is not present. + **/ +s32 e1000_phy_init_script_igp3(struct e1000_hw *hw) +{ + DEBUGOUT("Running IGP 3 PHY init script\n"); + + /* PHY init IGP 3 */ + /* Enable rise/fall, 10-mode work in class-A */ + hw->phy.ops.write_reg(hw, 0x2F5B, 0x9018); + /* Remove all caps from Replica path filter */ + hw->phy.ops.write_reg(hw, 0x2F52, 0x0000); + /* Bias trimming for ADC, AFE and Driver (Default) */ + hw->phy.ops.write_reg(hw, 0x2FB1, 0x8B24); + /* Increase Hybrid poly bias */ + hw->phy.ops.write_reg(hw, 0x2FB2, 0xF8F0); + /* Add 4% to Tx amplitude in Gig mode */ + hw->phy.ops.write_reg(hw, 0x2010, 0x10B0); + /* Disable trimming (TTT) */ + hw->phy.ops.write_reg(hw, 0x2011, 0x0000); + /* Poly DC correction to 94.6% + 2% for all channels */ + hw->phy.ops.write_reg(hw, 0x20DD, 0x249A); + /* ABS DC correction to 95.9% */ + hw->phy.ops.write_reg(hw, 0x20DE, 0x00D3); + /* BG temp curve trim */ + hw->phy.ops.write_reg(hw, 0x28B4, 0x04CE); + /* Increasing ADC OPAMP stage 1 currents to max */ + hw->phy.ops.write_reg(hw, 0x2F70, 0x29E4); + /* Force 1000 ( required for enabling PHY regs configuration) */ + hw->phy.ops.write_reg(hw, 0x0000, 0x0140); + /* Set upd_freq to 6 */ + hw->phy.ops.write_reg(hw, 0x1F30, 0x1606); + /* Disable NPDFE */ + hw->phy.ops.write_reg(hw, 0x1F31, 0xB814); + /* Disable adaptive fixed FFE (Default) */ + hw->phy.ops.write_reg(hw, 0x1F35, 0x002A); + /* Enable FFE hysteresis */ + hw->phy.ops.write_reg(hw, 0x1F3E, 0x0067); + /* Fixed FFE for short cable lengths */ + hw->phy.ops.write_reg(hw, 0x1F54, 0x0065); + /* Fixed FFE for medium cable lengths */ + hw->phy.ops.write_reg(hw, 0x1F55, 0x002A); + /* Fixed FFE for long cable lengths */ + hw->phy.ops.write_reg(hw, 0x1F56, 0x002A); + /* Enable Adaptive Clip Threshold */ + hw->phy.ops.write_reg(hw, 0x1F72, 0x3FB0); + /* AHT reset limit to 1 */ + hw->phy.ops.write_reg(hw, 0x1F76, 0xC0FF); + /* Set AHT master delay to 127 msec */ + hw->phy.ops.write_reg(hw, 0x1F77, 0x1DEC); + /* Set scan bits for AHT */ + hw->phy.ops.write_reg(hw, 0x1F78, 0xF9EF); + /* Set AHT Preset bits */ + hw->phy.ops.write_reg(hw, 0x1F79, 0x0210); + /* Change integ_factor of channel A to 3 */ + hw->phy.ops.write_reg(hw, 0x1895, 0x0003); + /* Change prop_factor of channels BCD to 8 */ + hw->phy.ops.write_reg(hw, 0x1796, 0x0008); + /* Change cg_icount + enable integbp for channels BCD */ + hw->phy.ops.write_reg(hw, 0x1798, 0xD008); + /* Change cg_icount + enable integbp + change prop_factor_master + * to 8 for channel A + */ + hw->phy.ops.write_reg(hw, 0x1898, 0xD918); + /* Disable AHT in Slave mode on channel A */ + hw->phy.ops.write_reg(hw, 0x187A, 0x0800); + /* Enable LPLU and disable AN to 1000 in non-D0a states, + * Enable SPD+B2B + */ + hw->phy.ops.write_reg(hw, 0x0019, 0x008D); + /* Enable restart AN on an1000_dis change */ + hw->phy.ops.write_reg(hw, 0x001B, 0x2080); + /* Enable wh_fifo read clock in 10/100 modes */ + hw->phy.ops.write_reg(hw, 0x0014, 0x0045); + /* Restart AN, Speed selection is 1000 */ + hw->phy.ops.write_reg(hw, 0x0000, 0x1340); + + return E1000_SUCCESS; +} + +/** + * e1000_get_phy_type_from_id - Get PHY type from id + * @phy_id: phy_id read from the phy + * + * Returns the phy type from the id. + **/ +enum e1000_phy_type e1000_get_phy_type_from_id(u32 phy_id) +{ + enum e1000_phy_type phy_type = e1000_phy_unknown; + + switch (phy_id) { + case M88E1000_I_PHY_ID: + case M88E1000_E_PHY_ID: + case M88E1111_I_PHY_ID: + case M88E1011_I_PHY_ID: + case M88E1543_E_PHY_ID: + case M88E1512_E_PHY_ID: + case I347AT4_E_PHY_ID: + case M88E1112_E_PHY_ID: + case M88E1340M_E_PHY_ID: + phy_type = e1000_phy_m88; + break; + case IGP01E1000_I_PHY_ID: /* IGP 1 & 2 share this */ + phy_type = e1000_phy_igp_2; + break; + case GG82563_E_PHY_ID: + phy_type = e1000_phy_gg82563; + break; + case IGP03E1000_E_PHY_ID: + phy_type = e1000_phy_igp_3; + break; + case IFE_E_PHY_ID: + case IFE_PLUS_E_PHY_ID: + case IFE_C_E_PHY_ID: + phy_type = e1000_phy_ife; + break; + case I82580_I_PHY_ID: + phy_type = e1000_phy_82580; + break; + case I210_I_PHY_ID: + phy_type = e1000_phy_i210; + break; + default: + phy_type = e1000_phy_unknown; + break; + } + return phy_type; +} + +/** + * e1000_determine_phy_address - Determines PHY address. + * @hw: pointer to the HW structure + * + * This uses a trial and error method to loop through possible PHY + * addresses. It tests each by reading the PHY ID registers and + * checking for a match. + **/ +s32 e1000_determine_phy_address(struct e1000_hw *hw) +{ + u32 phy_addr = 0; + u32 i; + enum e1000_phy_type phy_type = e1000_phy_unknown; + + hw->phy.id = phy_type; + + for (phy_addr = 0; phy_addr < E1000_MAX_PHY_ADDR; phy_addr++) { + hw->phy.addr = phy_addr; + i = 0; + + do { + e1000_get_phy_id(hw); + phy_type = e1000_get_phy_type_from_id(hw->phy.id); + + /* If phy_type is valid, break - we found our + * PHY address + */ + if (phy_type != e1000_phy_unknown) + return E1000_SUCCESS; + + msec_delay(1); + i++; + } while (i < 10); + } + + return -E1000_ERR_PHY_TYPE; +} + +/** + * e1000_power_up_phy_copper - Restore copper link in case of PHY power down + * @hw: pointer to the HW structure + * + * In the case of a PHY power down to save power, or to turn off link during a + * driver unload, or wake on lan is not enabled, restore the link to previous + * settings. + **/ +void e1000_power_up_phy_copper(struct e1000_hw *hw) +{ + u16 mii_reg = 0; + + /* The PHY will retain its settings across a power down/up cycle */ + hw->phy.ops.read_reg(hw, PHY_CONTROL, &mii_reg); + mii_reg &= ~MII_CR_POWER_DOWN; + hw->phy.ops.write_reg(hw, PHY_CONTROL, mii_reg); +} + +/** + * e1000_power_down_phy_copper - Restore copper link in case of PHY power down + * @hw: pointer to the HW structure + * + * In the case of a PHY power down to save power, or to turn off link during a + * driver unload, or wake on lan is not enabled, restore the link to previous + * settings. + **/ +void e1000_power_down_phy_copper(struct e1000_hw *hw) +{ + u16 mii_reg = 0; + + /* The PHY will retain its settings across a power down/up cycle */ + hw->phy.ops.read_reg(hw, PHY_CONTROL, &mii_reg); + mii_reg |= MII_CR_POWER_DOWN; + hw->phy.ops.write_reg(hw, PHY_CONTROL, mii_reg); + msec_delay(1); +} + +/** + * e1000_check_polarity_82577 - Checks the polarity. + * @hw: pointer to the HW structure + * + * Success returns 0, Failure returns -E1000_ERR_PHY (-2) + * + * Polarity is determined based on the PHY specific status register. + **/ +s32 e1000_check_polarity_82577(struct e1000_hw *hw) +{ + struct e1000_phy_info *phy = &hw->phy; + s32 ret_val; + u16 data; + + DEBUGFUNC("e1000_check_polarity_82577"); + + ret_val = phy->ops.read_reg(hw, I82577_PHY_STATUS_2, &data); + + if (!ret_val) + phy->cable_polarity = ((data & I82577_PHY_STATUS2_REV_POLARITY) + ? e1000_rev_polarity_reversed + : e1000_rev_polarity_normal); + + return ret_val; +} + +/** + * e1000_phy_force_speed_duplex_82577 - Force speed/duplex for I82577 PHY + * @hw: pointer to the HW structure + * + * Calls the PHY setup function to force speed and duplex. + **/ +s32 e1000_phy_force_speed_duplex_82577(struct e1000_hw *hw) +{ + struct e1000_phy_info *phy = &hw->phy; + s32 ret_val; + u16 phy_data; + bool link; + + DEBUGFUNC("e1000_phy_force_speed_duplex_82577"); + + ret_val = phy->ops.read_reg(hw, PHY_CONTROL, &phy_data); + if (ret_val) + return ret_val; + + e1000_phy_force_speed_duplex_setup(hw, &phy_data); + + ret_val = phy->ops.write_reg(hw, PHY_CONTROL, phy_data); + if (ret_val) + return ret_val; + + usec_delay(1); + + if (phy->autoneg_wait_to_complete) { + DEBUGOUT("Waiting for forced speed/duplex link on 82577 phy\n"); + + ret_val = e1000_phy_has_link_generic(hw, PHY_FORCE_LIMIT, + 100000, &link); + if (ret_val) + return ret_val; + + if (!link) + DEBUGOUT("Link taking longer than expected.\n"); + + /* Try once more */ + ret_val = e1000_phy_has_link_generic(hw, PHY_FORCE_LIMIT, + 100000, &link); + } + + return ret_val; +} + +/** + * e1000_get_phy_info_82577 - Retrieve I82577 PHY information + * @hw: pointer to the HW structure + * + * Read PHY status to determine if link is up. If link is up, then + * set/determine 10base-T extended distance and polarity correction. Read + * PHY port status to determine MDI/MDIx and speed. Based on the speed, + * determine on the cable length, local and remote receiver. + **/ +s32 e1000_get_phy_info_82577(struct e1000_hw *hw) +{ + struct e1000_phy_info *phy = &hw->phy; + s32 ret_val; + u16 data; + bool link; + + DEBUGFUNC("e1000_get_phy_info_82577"); + + ret_val = e1000_phy_has_link_generic(hw, 1, 0, &link); + if (ret_val) + return ret_val; + + if (!link) { + DEBUGOUT("Phy info is only valid if link is up\n"); + return -E1000_ERR_CONFIG; + } + + phy->polarity_correction = true; + + ret_val = e1000_check_polarity_82577(hw); + if (ret_val) + return ret_val; + + ret_val = phy->ops.read_reg(hw, I82577_PHY_STATUS_2, &data); + if (ret_val) + return ret_val; + + phy->is_mdix = !!(data & I82577_PHY_STATUS2_MDIX); + + if ((data & I82577_PHY_STATUS2_SPEED_MASK) == + I82577_PHY_STATUS2_SPEED_1000MBPS) { + ret_val = hw->phy.ops.get_cable_length(hw); + if (ret_val) + return ret_val; + + ret_val = phy->ops.read_reg(hw, PHY_1000T_STATUS, &data); + if (ret_val) + return ret_val; + + phy->local_rx = (data & SR_1000T_LOCAL_RX_STATUS) + ? e1000_1000t_rx_status_ok + : e1000_1000t_rx_status_not_ok; + + phy->remote_rx = (data & SR_1000T_REMOTE_RX_STATUS) + ? e1000_1000t_rx_status_ok + : e1000_1000t_rx_status_not_ok; + } else { + phy->cable_length = E1000_CABLE_LENGTH_UNDEFINED; + phy->local_rx = e1000_1000t_rx_status_undefined; + phy->remote_rx = e1000_1000t_rx_status_undefined; + } + + return E1000_SUCCESS; +} + +/** + * e1000_get_cable_length_82577 - Determine cable length for 82577 PHY + * @hw: pointer to the HW structure + * + * Reads the diagnostic status register and verifies result is valid before + * placing it in the phy_cable_length field. + **/ +s32 e1000_get_cable_length_82577(struct e1000_hw *hw) +{ + struct e1000_phy_info *phy = &hw->phy; + s32 ret_val; + u16 phy_data, length; + + DEBUGFUNC("e1000_get_cable_length_82577"); + + ret_val = phy->ops.read_reg(hw, I82577_PHY_DIAG_STATUS, &phy_data); + if (ret_val) + return ret_val; + + length = ((phy_data & I82577_DSTATUS_CABLE_LENGTH) >> + I82577_DSTATUS_CABLE_LENGTH_SHIFT); + + if (length == E1000_CABLE_LENGTH_UNDEFINED) + return -E1000_ERR_PHY; + + phy->cable_length = length; + + return E1000_SUCCESS; +} + +/** + * e1000_write_phy_reg_gs40g - Write GS40G PHY register + * @hw: pointer to the HW structure + * @offset: register offset to write to + * @data: data to write at register offset + * + * Acquires semaphore, if necessary, then writes the data to PHY register + * at the offset. Release any acquired semaphores before exiting. + **/ +s32 e1000_write_phy_reg_gs40g(struct e1000_hw *hw, u32 offset, u16 data) +{ + s32 ret_val; + u16 page = offset >> GS40G_PAGE_SHIFT; + + DEBUGFUNC("e1000_write_phy_reg_gs40g"); + + offset = offset & GS40G_OFFSET_MASK; + ret_val = hw->phy.ops.acquire(hw); + if (ret_val) + return ret_val; + + ret_val = e1000_write_phy_reg_mdic(hw, GS40G_PAGE_SELECT, page); + if (ret_val) + goto release; + ret_val = e1000_write_phy_reg_mdic(hw, offset, data); + +release: + hw->phy.ops.release(hw); + return ret_val; +} + +/** + * e1000_read_phy_reg_gs40g - Read GS40G PHY register + * @hw: pointer to the HW structure + * @offset: lower half is register offset to read to + * upper half is page to use. + * @data: data to read at register offset + * + * Acquires semaphore, if necessary, then reads the data in the PHY register + * at the offset. Release any acquired semaphores before exiting. + **/ +s32 e1000_read_phy_reg_gs40g(struct e1000_hw *hw, u32 offset, u16 *data) +{ + s32 ret_val; + u16 page = offset >> GS40G_PAGE_SHIFT; + + DEBUGFUNC("e1000_read_phy_reg_gs40g"); + + offset = offset & GS40G_OFFSET_MASK; + ret_val = hw->phy.ops.acquire(hw); + if (ret_val) + return ret_val; + + ret_val = e1000_write_phy_reg_mdic(hw, GS40G_PAGE_SELECT, page); + if (ret_val) + goto release; + ret_val = e1000_read_phy_reg_mdic(hw, offset, data); + +release: + hw->phy.ops.release(hw); + return ret_val; +} + +/** + * e1000_read_phy_reg_mphy - Read mPHY control register + * @hw: pointer to the HW structure + * @address: address to be read + * @data: pointer to the read data + * + * Reads the mPHY control register in the PHY at offset and stores the + * information read to data. + **/ +s32 e1000_read_phy_reg_mphy(struct e1000_hw *hw, u32 address, u32 *data) +{ + u32 mphy_ctrl = 0; + bool locked = false; + bool ready; + + DEBUGFUNC("e1000_read_phy_reg_mphy"); + + /* Check if mPHY is ready to read/write operations */ + ready = e1000_is_mphy_ready(hw); + if (!ready) + return -E1000_ERR_PHY; + + /* Check if mPHY access is disabled and enable it if so */ + mphy_ctrl = E1000_READ_REG(hw, E1000_MPHY_ADDR_CTRL); + if (mphy_ctrl & E1000_MPHY_DIS_ACCESS) { + locked = true; + ready = e1000_is_mphy_ready(hw); + if (!ready) + return -E1000_ERR_PHY; + mphy_ctrl |= E1000_MPHY_ENA_ACCESS; + E1000_WRITE_REG(hw, E1000_MPHY_ADDR_CTRL, mphy_ctrl); + } + + /* Set the address that we want to read */ + ready = e1000_is_mphy_ready(hw); + if (!ready) + return -E1000_ERR_PHY; + + /* We mask address, because we want to use only current lane */ + mphy_ctrl = (mphy_ctrl & ~E1000_MPHY_ADDRESS_MASK & + ~E1000_MPHY_ADDRESS_FNC_OVERRIDE) | + (address & E1000_MPHY_ADDRESS_MASK); + E1000_WRITE_REG(hw, E1000_MPHY_ADDR_CTRL, mphy_ctrl); + + /* Read data from the address */ + ready = e1000_is_mphy_ready(hw); + if (!ready) + return -E1000_ERR_PHY; + *data = E1000_READ_REG(hw, E1000_MPHY_DATA); + + /* Disable access to mPHY if it was originally disabled */ + if (locked) + ready = e1000_is_mphy_ready(hw); + if (!ready) + return -E1000_ERR_PHY; + E1000_WRITE_REG(hw, E1000_MPHY_ADDR_CTRL, + E1000_MPHY_DIS_ACCESS); + + return E1000_SUCCESS; +} + +/** + * e1000_write_phy_reg_mphy - Write mPHY control register + * @hw: pointer to the HW structure + * @address: address to write to + * @data: data to write to register at offset + * @line_override: used when we want to use different line than default one + * + * Writes data to mPHY control register. + **/ +s32 e1000_write_phy_reg_mphy(struct e1000_hw *hw, u32 address, u32 data, + bool line_override) +{ + u32 mphy_ctrl = 0; + bool locked = false; + bool ready; + + DEBUGFUNC("e1000_write_phy_reg_mphy"); + + /* Check if mPHY is ready to read/write operations */ + ready = e1000_is_mphy_ready(hw); + if (!ready) + return -E1000_ERR_PHY; + + /* Check if mPHY access is disabled and enable it if so */ + mphy_ctrl = E1000_READ_REG(hw, E1000_MPHY_ADDR_CTRL); + if (mphy_ctrl & E1000_MPHY_DIS_ACCESS) { + locked = true; + ready = e1000_is_mphy_ready(hw); + if (!ready) + return -E1000_ERR_PHY; + mphy_ctrl |= E1000_MPHY_ENA_ACCESS; + E1000_WRITE_REG(hw, E1000_MPHY_ADDR_CTRL, mphy_ctrl); + } + + /* Set the address that we want to read */ + ready = e1000_is_mphy_ready(hw); + if (!ready) + return -E1000_ERR_PHY; + + /* We mask address, because we want to use only current lane */ + if (line_override) + mphy_ctrl |= E1000_MPHY_ADDRESS_FNC_OVERRIDE; + else + mphy_ctrl &= ~E1000_MPHY_ADDRESS_FNC_OVERRIDE; + mphy_ctrl = (mphy_ctrl & ~E1000_MPHY_ADDRESS_MASK) | + (address & E1000_MPHY_ADDRESS_MASK); + E1000_WRITE_REG(hw, E1000_MPHY_ADDR_CTRL, mphy_ctrl); + + /* Read data from the address */ + ready = e1000_is_mphy_ready(hw); + if (!ready) + return -E1000_ERR_PHY; + E1000_WRITE_REG(hw, E1000_MPHY_DATA, data); + + /* Disable access to mPHY if it was originally disabled */ + if (locked) + ready = e1000_is_mphy_ready(hw); + if (!ready) + return -E1000_ERR_PHY; + E1000_WRITE_REG(hw, E1000_MPHY_ADDR_CTRL, + E1000_MPHY_DIS_ACCESS); + + return E1000_SUCCESS; +} + +/** + * e1000_is_mphy_ready - Check if mPHY control register is not busy + * @hw: pointer to the HW structure + * + * Returns mPHY control register status. + **/ +bool e1000_is_mphy_ready(struct e1000_hw *hw) +{ + u16 retry_count = 0; + u32 mphy_ctrl = 0; + bool ready = false; + + while (retry_count < 2) { + mphy_ctrl = E1000_READ_REG(hw, E1000_MPHY_ADDR_CTRL); + if (mphy_ctrl & E1000_MPHY_BUSY) { + usec_delay(20); + retry_count++; + continue; + } + ready = true; + break; + } + + if (!ready) + DEBUGOUT("ERROR READING mPHY control register, phy is busy.\n"); + + return ready; +} diff --git a/drivers/staging/igb_avb/e1000_phy.h b/drivers/staging/igb_avb/e1000_phy.h new file mode 100644 index 000000000000..a109c914ce8d --- /dev/null +++ b/drivers/staging/igb_avb/e1000_phy.h @@ -0,0 +1,252 @@ +/******************************************************************************* + + Intel(R) Gigabit Ethernet Linux driver + Copyright(c) 2007-2015 Intel Corporation. + + This program is free software; you can redistribute it and/or modify it + under the terms and conditions of the GNU General Public License, + version 2, as published by the Free Software Foundation. + + This program is distributed in the hope 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. + + The full GNU General Public License is included in this distribution in + the file called "COPYING". + + Contact Information: + Linux NICS + e1000-devel Mailing List + Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + +*******************************************************************************/ + +#ifndef _E1000_PHY_H_ +#define _E1000_PHY_H_ + +void e1000_init_phy_ops_generic(struct e1000_hw *hw); +s32 e1000_null_read_reg(struct e1000_hw *hw, u32 offset, u16 *data); +void e1000_null_phy_generic(struct e1000_hw *hw); +s32 e1000_null_lplu_state(struct e1000_hw *hw, bool active); +s32 e1000_null_write_reg(struct e1000_hw *hw, u32 offset, u16 data); +s32 e1000_null_set_page(struct e1000_hw *hw, u16 data); +s32 e1000_read_i2c_byte_null(struct e1000_hw *hw, u8 byte_offset, + u8 dev_addr, u8 *data); +s32 e1000_write_i2c_byte_null(struct e1000_hw *hw, u8 byte_offset, + u8 dev_addr, u8 data); +s32 e1000_check_downshift_generic(struct e1000_hw *hw); +s32 e1000_check_polarity_m88(struct e1000_hw *hw); +s32 e1000_check_polarity_igp(struct e1000_hw *hw); +s32 e1000_check_polarity_ife(struct e1000_hw *hw); +s32 e1000_check_reset_block_generic(struct e1000_hw *hw); +s32 e1000_copper_link_setup_igp(struct e1000_hw *hw); +s32 e1000_copper_link_setup_m88(struct e1000_hw *hw); +s32 e1000_copper_link_setup_m88_gen2(struct e1000_hw *hw); +s32 e1000_phy_force_speed_duplex_igp(struct e1000_hw *hw); +s32 e1000_phy_force_speed_duplex_m88(struct e1000_hw *hw); +s32 e1000_phy_force_speed_duplex_ife(struct e1000_hw *hw); +s32 e1000_get_cable_length_m88(struct e1000_hw *hw); +s32 e1000_get_cable_length_m88_gen2(struct e1000_hw *hw); +s32 e1000_get_cable_length_igp_2(struct e1000_hw *hw); +s32 e1000_get_cfg_done_generic(struct e1000_hw *hw); +s32 e1000_get_phy_id(struct e1000_hw *hw); +s32 e1000_get_phy_info_igp(struct e1000_hw *hw); +s32 e1000_get_phy_info_m88(struct e1000_hw *hw); +s32 e1000_get_phy_info_ife(struct e1000_hw *hw); +s32 e1000_phy_sw_reset_generic(struct e1000_hw *hw); +void e1000_phy_force_speed_duplex_setup(struct e1000_hw *hw, u16 *phy_ctrl); +s32 e1000_phy_hw_reset_generic(struct e1000_hw *hw); +s32 e1000_phy_reset_dsp_generic(struct e1000_hw *hw); +s32 e1000_read_kmrn_reg_generic(struct e1000_hw *hw, u32 offset, u16 *data); +s32 e1000_read_kmrn_reg_locked(struct e1000_hw *hw, u32 offset, u16 *data); +s32 e1000_set_page_igp(struct e1000_hw *hw, u16 page); +s32 e1000_read_phy_reg_igp(struct e1000_hw *hw, u32 offset, u16 *data); +s32 e1000_read_phy_reg_igp_locked(struct e1000_hw *hw, u32 offset, u16 *data); +s32 e1000_read_phy_reg_m88(struct e1000_hw *hw, u32 offset, u16 *data); +s32 e1000_set_d3_lplu_state_generic(struct e1000_hw *hw, bool active); +s32 e1000_setup_copper_link_generic(struct e1000_hw *hw); +s32 e1000_write_kmrn_reg_generic(struct e1000_hw *hw, u32 offset, u16 data); +s32 e1000_write_kmrn_reg_locked(struct e1000_hw *hw, u32 offset, u16 data); +s32 e1000_write_phy_reg_igp(struct e1000_hw *hw, u32 offset, u16 data); +s32 e1000_write_phy_reg_igp_locked(struct e1000_hw *hw, u32 offset, u16 data); +s32 e1000_write_phy_reg_m88(struct e1000_hw *hw, u32 offset, u16 data); +s32 e1000_phy_has_link_generic(struct e1000_hw *hw, u32 iterations, + u32 usec_interval, bool *success); +s32 e1000_phy_init_script_igp3(struct e1000_hw *hw); +enum e1000_phy_type e1000_get_phy_type_from_id(u32 phy_id); +s32 e1000_determine_phy_address(struct e1000_hw *hw); +s32 e1000_enable_phy_wakeup_reg_access_bm(struct e1000_hw *hw, u16 *phy_reg); +s32 e1000_disable_phy_wakeup_reg_access_bm(struct e1000_hw *hw, u16 *phy_reg); +void e1000_power_up_phy_copper(struct e1000_hw *hw); +void e1000_power_down_phy_copper(struct e1000_hw *hw); +s32 e1000_read_phy_reg_mdic(struct e1000_hw *hw, u32 offset, u16 *data); +s32 e1000_write_phy_reg_mdic(struct e1000_hw *hw, u32 offset, u16 data); +s32 e1000_read_phy_reg_i2c(struct e1000_hw *hw, u32 offset, u16 *data); +s32 e1000_write_phy_reg_i2c(struct e1000_hw *hw, u32 offset, u16 data); +s32 e1000_read_sfp_data_byte(struct e1000_hw *hw, u16 offset, u8 *data); +s32 e1000_write_sfp_data_byte(struct e1000_hw *hw, u16 offset, u8 data); +s32 e1000_copper_link_setup_82577(struct e1000_hw *hw); +s32 e1000_check_polarity_82577(struct e1000_hw *hw); +s32 e1000_get_phy_info_82577(struct e1000_hw *hw); +s32 e1000_phy_force_speed_duplex_82577(struct e1000_hw *hw); +s32 e1000_get_cable_length_82577(struct e1000_hw *hw); +s32 e1000_write_phy_reg_gs40g(struct e1000_hw *hw, u32 offset, u16 data); +s32 e1000_read_phy_reg_gs40g(struct e1000_hw *hw, u32 offset, u16 *data); +s32 e1000_read_phy_reg_mphy(struct e1000_hw *hw, u32 address, u32 *data); +s32 e1000_write_phy_reg_mphy(struct e1000_hw *hw, u32 address, u32 data, + bool line_override); +bool e1000_is_mphy_ready(struct e1000_hw *hw); + +#define E1000_MAX_PHY_ADDR 8 + +/* IGP01E1000 Specific Registers */ +#define IGP01E1000_PHY_PORT_CONFIG 0x10 /* Port Config */ +#define IGP01E1000_PHY_PORT_STATUS 0x11 /* Status */ +#define IGP01E1000_PHY_PORT_CTRL 0x12 /* Control */ +#define IGP01E1000_PHY_LINK_HEALTH 0x13 /* PHY Link Health */ +#define IGP02E1000_PHY_POWER_MGMT 0x19 /* Power Management */ +#define IGP01E1000_PHY_PAGE_SELECT 0x1F /* Page Select */ +#define BM_PHY_PAGE_SELECT 22 /* Page Select for BM */ +#define IGP_PAGE_SHIFT 5 +#define PHY_REG_MASK 0x1F + +/* GS40G - I210 PHY defines */ +#define GS40G_PAGE_SELECT 0x16 +#define GS40G_PAGE_SHIFT 16 +#define GS40G_OFFSET_MASK 0xFFFF +#define GS40G_PAGE_2 0x20000 +#define GS40G_MAC_REG2 0x15 +#define GS40G_MAC_LB 0x4140 +#define GS40G_MAC_SPEED_1G 0X0006 +#define GS40G_COPPER_SPEC 0x0010 + +#define HV_INTC_FC_PAGE_START 768 +#define I82578_ADDR_REG 29 +#define I82577_ADDR_REG 16 +#define I82577_CFG_REG 22 +#define I82577_CFG_ASSERT_CRS_ON_TX (1 << 15) +#define I82577_CFG_ENABLE_DOWNSHIFT (3 << 10) /* auto downshift */ +#define I82577_CTRL_REG 23 + +/* 82577 specific PHY registers */ +#define I82577_PHY_CTRL_2 18 +#define I82577_PHY_LBK_CTRL 19 +#define I82577_PHY_STATUS_2 26 +#define I82577_PHY_DIAG_STATUS 31 + +/* I82577 PHY Status 2 */ +#define I82577_PHY_STATUS2_REV_POLARITY 0x0400 +#define I82577_PHY_STATUS2_MDIX 0x0800 +#define I82577_PHY_STATUS2_SPEED_MASK 0x0300 +#define I82577_PHY_STATUS2_SPEED_1000MBPS 0x0200 + +/* I82577 PHY Control 2 */ +#define I82577_PHY_CTRL2_MANUAL_MDIX 0x0200 +#define I82577_PHY_CTRL2_AUTO_MDI_MDIX 0x0400 +#define I82577_PHY_CTRL2_MDIX_CFG_MASK 0x0600 + +/* I82577 PHY Diagnostics Status */ +#define I82577_DSTATUS_CABLE_LENGTH 0x03FC +#define I82577_DSTATUS_CABLE_LENGTH_SHIFT 2 + +/* 82580 PHY Power Management */ +#define E1000_82580_PHY_POWER_MGMT 0xE14 +#define E1000_82580_PM_SPD 0x0001 /* Smart Power Down */ +#define E1000_82580_PM_D0_LPLU 0x0002 /* For D0a states */ +#define E1000_82580_PM_D3_LPLU 0x0004 /* For all other states */ +#define E1000_82580_PM_GO_LINKD 0x0020 /* Go Link Disconnect */ + +#define E1000_MPHY_DIS_ACCESS 0x80000000 /* disable_access bit */ +#define E1000_MPHY_ENA_ACCESS 0x40000000 /* enable_access bit */ +#define E1000_MPHY_BUSY 0x00010000 /* busy bit */ +#define E1000_MPHY_ADDRESS_FNC_OVERRIDE 0x20000000 /* fnc_override bit */ +#define E1000_MPHY_ADDRESS_MASK 0x0000FFFF /* address mask */ + +#define IGP01E1000_PHY_PCS_INIT_REG 0x00B4 +#define IGP01E1000_PHY_POLARITY_MASK 0x0078 + +#define IGP01E1000_PSCR_AUTO_MDIX 0x1000 +#define IGP01E1000_PSCR_FORCE_MDI_MDIX 0x2000 /* 0=MDI, 1=MDIX */ + +#define IGP01E1000_PSCFR_SMART_SPEED 0x0080 + +#define IGP02E1000_PM_SPD 0x0001 /* Smart Power Down */ +#define IGP02E1000_PM_D0_LPLU 0x0002 /* For D0a states */ +#define IGP02E1000_PM_D3_LPLU 0x0004 /* For all other states */ + +#define IGP01E1000_PLHR_SS_DOWNGRADE 0x8000 + +#define IGP01E1000_PSSR_POLARITY_REVERSED 0x0002 +#define IGP01E1000_PSSR_MDIX 0x0800 +#define IGP01E1000_PSSR_SPEED_MASK 0xC000 +#define IGP01E1000_PSSR_SPEED_1000MBPS 0xC000 + +#define IGP02E1000_PHY_CHANNEL_NUM 4 +#define IGP02E1000_PHY_AGC_A 0x11B1 +#define IGP02E1000_PHY_AGC_B 0x12B1 +#define IGP02E1000_PHY_AGC_C 0x14B1 +#define IGP02E1000_PHY_AGC_D 0x18B1 + +#define IGP02E1000_AGC_LENGTH_SHIFT 9 /* Course=15:13, Fine=12:9 */ +#define IGP02E1000_AGC_LENGTH_MASK 0x7F +#define IGP02E1000_AGC_RANGE 15 + +#define E1000_CABLE_LENGTH_UNDEFINED 0xFF + +#define E1000_KMRNCTRLSTA_OFFSET 0x001F0000 +#define E1000_KMRNCTRLSTA_OFFSET_SHIFT 16 +#define E1000_KMRNCTRLSTA_REN 0x00200000 +#define E1000_KMRNCTRLSTA_DIAG_OFFSET 0x3 /* Kumeran Diagnostic */ +#define E1000_KMRNCTRLSTA_TIMEOUTS 0x4 /* Kumeran Timeouts */ +#define E1000_KMRNCTRLSTA_INBAND_PARAM 0x9 /* Kumeran InBand Parameters */ +#define E1000_KMRNCTRLSTA_IBIST_DISABLE 0x0200 /* Kumeran IBIST Disable */ +#define E1000_KMRNCTRLSTA_DIAG_NELPBK 0x1000 /* Nearend Loopback mode */ + +#define IFE_PHY_EXTENDED_STATUS_CONTROL 0x10 +#define IFE_PHY_SPECIAL_CONTROL 0x11 /* 100BaseTx PHY Special Ctrl */ +#define IFE_PHY_SPECIAL_CONTROL_LED 0x1B /* PHY Special and LED Ctrl */ +#define IFE_PHY_MDIX_CONTROL 0x1C /* MDI/MDI-X Control */ + +/* IFE PHY Extended Status Control */ +#define IFE_PESC_POLARITY_REVERSED 0x0100 + +/* IFE PHY Special Control */ +#define IFE_PSC_AUTO_POLARITY_DISABLE 0x0010 +#define IFE_PSC_FORCE_POLARITY 0x0020 + +/* IFE PHY Special Control and LED Control */ +#define IFE_PSCL_PROBE_MODE 0x0020 +#define IFE_PSCL_PROBE_LEDS_OFF 0x0006 /* Force LEDs 0 and 2 off */ +#define IFE_PSCL_PROBE_LEDS_ON 0x0007 /* Force LEDs 0 and 2 on */ + +/* IFE PHY MDIX Control */ +#define IFE_PMC_MDIX_STATUS 0x0020 /* 1=MDI-X, 0=MDI */ +#define IFE_PMC_FORCE_MDIX 0x0040 /* 1=force MDI-X, 0=force MDI */ +#define IFE_PMC_AUTO_MDIX 0x0080 /* 1=enable auto, 0=disable */ + +/* SFP modules ID memory locations */ +#define E1000_SFF_IDENTIFIER_OFFSET 0x00 +#define E1000_SFF_IDENTIFIER_SFF 0x02 +#define E1000_SFF_IDENTIFIER_SFP 0x03 + +#define E1000_SFF_ETH_FLAGS_OFFSET 0x06 +/* Flags for SFP modules compatible with ETH up to 1Gb */ +struct sfp_e1000_flags { + u8 e1000_base_sx:1; + u8 e1000_base_lx:1; + u8 e1000_base_cx:1; + u8 e1000_base_t:1; + u8 e100_base_lx:1; + u8 e100_base_fx:1; + u8 e10_base_bx10:1; + u8 e10_base_px:1; +}; + +/* Vendor OUIs: format of OUI is 0x[byte0][byte1][byte2][00] */ +#define E1000_SFF_VENDOR_OUI_TYCO 0x00407600 +#define E1000_SFF_VENDOR_OUI_FTL 0x00906500 +#define E1000_SFF_VENDOR_OUI_AVAGO 0x00176A00 +#define E1000_SFF_VENDOR_OUI_INTEL 0x001B2100 + +#endif diff --git a/drivers/staging/igb_avb/e1000_regs.h b/drivers/staging/igb_avb/e1000_regs.h new file mode 100644 index 000000000000..caf1d04dee87 --- /dev/null +++ b/drivers/staging/igb_avb/e1000_regs.h @@ -0,0 +1,633 @@ +/******************************************************************************* + + Intel(R) Gigabit Ethernet Linux driver + Copyright(c) 2007-2015 Intel Corporation. + + This program is free software; you can redistribute it and/or modify it + under the terms and conditions of the GNU General Public License, + version 2, as published by the Free Software Foundation. + + This program is distributed in the hope 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. + + The full GNU General Public License is included in this distribution in + the file called "COPYING". + + Contact Information: + Linux NICS + e1000-devel Mailing List + Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + +*******************************************************************************/ + +#ifndef _E1000_REGS_H_ +#define _E1000_REGS_H_ + +#define E1000_CTRL 0x00000 /* Device Control - RW */ +#define E1000_STATUS 0x00008 /* Device Status - RO */ +#define E1000_EECD 0x00010 /* EEPROM/Flash Control - RW */ +#define E1000_EERD 0x00014 /* EEPROM Read - RW */ +#define E1000_CTRL_EXT 0x00018 /* Extended Device Control - RW */ +#define E1000_FLA 0x0001C /* Flash Access - RW */ +#define E1000_MDIC 0x00020 /* MDI Control - RW */ +#define E1000_MDICNFG 0x00E04 /* MDI Config - RW */ +#define E1000_REGISTER_SET_SIZE 0x20000 /* CSR Size */ +#define E1000_EEPROM_INIT_CTRL_WORD_2 0x0F /* EEPROM Init Ctrl Word 2 */ +#define E1000_EEPROM_PCIE_CTRL_WORD_2 0x28 /* EEPROM PCIe Ctrl Word 2 */ +#define E1000_BARCTRL 0x5BBC /* BAR ctrl reg */ +#define E1000_BARCTRL_FLSIZE 0x0700 /* BAR ctrl Flsize */ +#define E1000_BARCTRL_CSRSIZE 0x2000 /* BAR ctrl CSR size */ +#define E1000_MPHY_ADDR_CTRL 0x0024 /* GbE MPHY Address Control */ +#define E1000_MPHY_DATA 0x0E10 /* GBE MPHY Data */ +#define E1000_MPHY_STAT 0x0E0C /* GBE MPHY Statistics */ +#define E1000_PPHY_CTRL 0x5b48 /* PCIe PHY Control */ +#define E1000_I350_BARCTRL 0x5BFC /* BAR ctrl reg */ +#define E1000_I350_DTXMXPKTSZ 0x355C /* Maximum sent packet size reg*/ +#define E1000_SCTL 0x00024 /* SerDes Control - RW */ +#define E1000_FCAL 0x00028 /* Flow Control Address Low - RW */ +#define E1000_FCAH 0x0002C /* Flow Control Address High -RW */ +#define E1000_FCT 0x00030 /* Flow Control Type - RW */ +#define E1000_CONNSW 0x00034 /* Copper/Fiber switch control - RW */ +#define E1000_VET 0x00038 /* VLAN Ether Type - RW */ +#define E1000_TSSDP 0x0003C /* Time Sync SDP Configuration Register - RW */ +#define E1000_ICR 0x000C0 /* Interrupt Cause Read - R/clr */ +#define E1000_ITR 0x000C4 /* Interrupt Throttling Rate - RW */ +#define E1000_ICS 0x000C8 /* Interrupt Cause Set - WO */ +#define E1000_IMS 0x000D0 /* Interrupt Mask Set - RW */ +#define E1000_IMC 0x000D8 /* Interrupt Mask Clear - WO */ +#define E1000_IAM 0x000E0 /* Interrupt Acknowledge Auto Mask */ +#define E1000_RCTL 0x00100 /* Rx Control - RW */ +#define E1000_FCTTV 0x00170 /* Flow Control Transmit Timer Value - RW */ +#define E1000_TXCW 0x00178 /* Tx Configuration Word - RW */ +#define E1000_RXCW 0x00180 /* Rx Configuration Word - RO */ +#define E1000_EICR 0x01580 /* Ext. Interrupt Cause Read - R/clr */ +#define E1000_EITR(_n) (0x01680 + (0x4 * (_n))) +#define E1000_EICS 0x01520 /* Ext. Interrupt Cause Set - W0 */ +#define E1000_EIMS 0x01524 /* Ext. Interrupt Mask Set/Read - RW */ +#define E1000_EIMC 0x01528 /* Ext. Interrupt Mask Clear - WO */ +#define E1000_EIAC 0x0152C /* Ext. Interrupt Auto Clear - RW */ +#define E1000_EIAM 0x01530 /* Ext. Interrupt Ack Auto Clear Mask - RW */ +#define E1000_GPIE 0x01514 /* General Purpose Interrupt Enable - RW */ +#define E1000_IVAR0 0x01700 /* Interrupt Vector Allocation (array) - RW */ +#define E1000_IVAR_MISC 0x01740 /* IVAR for "other" causes - RW */ +#define E1000_TCTL 0x00400 /* Tx Control - RW */ +#define E1000_TCTL_EXT 0x00404 /* Extended Tx Control - RW */ +#define E1000_TIPG 0x00410 /* Tx Inter-packet gap -RW */ +#define E1000_AIT 0x00458 /* Adaptive Interframe Spacing Throttle - RW */ +#define E1000_LEDCTL 0x00E00 /* LED Control - RW */ +#define E1000_LEDMUX 0x08130 /* LED MUX Control */ +#define E1000_EXTCNF_CTRL 0x00F00 /* Extended Configuration Control */ +#define E1000_EXTCNF_SIZE 0x00F08 /* Extended Configuration Size */ +#define E1000_PHY_CTRL 0x00F10 /* PHY Control Register in CSR */ +#define E1000_PBA 0x01000 /* Packet Buffer Allocation - RW */ +#define E1000_PBS 0x01008 /* Packet Buffer Size */ +#define E1000_EEMNGCTL 0x01010 /* MNG EEprom Control */ +#define E1000_EEARBC 0x01024 /* EEPROM Auto Read Bus Control */ +#define E1000_EEARBC_I210 0x12024 /* EEPROM Auto Read Bus Control */ +#define E1000_EEWR 0x0102C /* EEPROM Write Register - RW */ +#define E1000_FLOP 0x0103C /* FLASH Opcode Register */ +#define E1000_I2CCMD 0x01028 /* SFPI2C Command Register - RW */ +#define E1000_I2CPARAMS 0x0102C /* SFPI2C Parameters Register - RW */ +#define E1000_I2CBB_EN 0x00000100 /* I2C - Bit Bang Enable */ +#define E1000_I2C_CLK_OUT 0x00000200 /* I2C- Clock */ +#define E1000_I2C_DATA_OUT 0x00000400 /* I2C- Data Out */ +#define E1000_I2C_DATA_OE_N 0x00000800 /* I2C- Data Output Enable */ +#define E1000_I2C_DATA_IN 0x00001000 /* I2C- Data In */ +#define E1000_I2C_CLK_OE_N 0x00002000 /* I2C- Clock Output Enable */ +#define E1000_I2C_CLK_IN 0x00004000 /* I2C- Clock In */ +#define E1000_I2C_CLK_STRETCH_DIS 0x00008000 /* I2C- Dis Clk Stretching */ +#define E1000_WDSTP 0x01040 /* Watchdog Setup - RW */ +#define E1000_SWDSTS 0x01044 /* SW Device Status - RW */ +#define E1000_FRTIMER 0x01048 /* Free Running Timer - RW */ +#define E1000_TCPTIMER 0x0104C /* TCP Timer - RW */ +#define E1000_VPDDIAG 0x01060 /* VPD Diagnostic - RO */ +#define E1000_ICR_V2 0x01500 /* Intr Cause - new location - RC */ +#define E1000_ICS_V2 0x01504 /* Intr Cause Set - new location - WO */ +#define E1000_IMS_V2 0x01508 /* Intr Mask Set/Read - new location - RW */ +#define E1000_IMC_V2 0x0150C /* Intr Mask Clear - new location - WO */ +#define E1000_IAM_V2 0x01510 /* Intr Ack Auto Mask - new location - RW */ +#define E1000_ERT 0x02008 /* Early Rx Threshold - RW */ +#define E1000_FCRTL 0x02160 /* Flow Control Receive Threshold Low - RW */ +#define E1000_FCRTH 0x02168 /* Flow Control Receive Threshold High - RW */ +#define E1000_PSRCTL 0x02170 /* Packet Split Receive Control - RW */ +#define E1000_RDFH 0x02410 /* Rx Data FIFO Head - RW */ +#define E1000_RDFT 0x02418 /* Rx Data FIFO Tail - RW */ +#define E1000_RDFHS 0x02420 /* Rx Data FIFO Head Saved - RW */ +#define E1000_RDFTS 0x02428 /* Rx Data FIFO Tail Saved - RW */ +#define E1000_RDFPC 0x02430 /* Rx Data FIFO Packet Count - RW */ +#define E1000_PBRTH 0x02458 /* PB Rx Arbitration Threshold - RW */ +#define E1000_FCRTV 0x02460 /* Flow Control Refresh Timer Value - RW */ +/* Split and Replication Rx Control - RW */ +#define E1000_RDPUMB 0x025CC /* DMA Rx Descriptor uC Mailbox - RW */ +#define E1000_RDPUAD 0x025D0 /* DMA Rx Descriptor uC Addr Command - RW */ +#define E1000_RDPUWD 0x025D4 /* DMA Rx Descriptor uC Data Write - RW */ +#define E1000_RDPURD 0x025D8 /* DMA Rx Descriptor uC Data Read - RW */ +#define E1000_RDPUCTL 0x025DC /* DMA Rx Descriptor uC Control - RW */ +#define E1000_PBDIAG 0x02458 /* Packet Buffer Diagnostic - RW */ +#define E1000_RXPBS 0x02404 /* Rx Packet Buffer Size - RW */ +#define E1000_IRPBS 0x02404 /* Same as RXPBS, renamed for newer Si - RW */ +#define E1000_PBRWAC 0x024E8 /* Rx packet buffer wrap around counter - RO */ +#define E1000_RDTR 0x02820 /* Rx Delay Timer - RW */ +#define E1000_RADV 0x0282C /* Rx Interrupt Absolute Delay Timer - RW */ +#define E1000_EMIADD 0x10 /* Extended Memory Indirect Address */ +#define E1000_EMIDATA 0x11 /* Extended Memory Indirect Data */ +#define E1000_SRWR 0x12018 /* Shadow Ram Write Register - RW */ +#define E1000_I210_FLMNGCTL 0x12038 +#define E1000_I210_FLMNGDATA 0x1203C +#define E1000_I210_FLMNGCNT 0x12040 + +#define E1000_I210_FLSWCTL 0x12048 +#define E1000_I210_FLSWDATA 0x1204C +#define E1000_I210_FLSWCNT 0x12050 + +#define E1000_I210_FLA 0x1201C + +#define E1000_INVM_DATA_REG(_n) (0x12120 + 4*(_n)) +#define E1000_INVM_SIZE 64 /* Number of INVM Data Registers */ + +/* QAV Tx mode control register */ +#define E1000_I210_TQAVCTRL 0x3570 +#define E1000_DTXMXPKTSZ 0x0355C + +/* High credit registers where _n can be 0 or 1. */ +#define E1000_I210_TQAVHC(_n) (0x300C + 0x40 * (_n)) + +/* Queues fetch arbitration priority control register */ +#define E1000_I210_TQAVARBCTRL 0x3574 +/* Queues priority masks where _n and _p can be 0-3. */ +#define E1000_TQAVARBCTRL_QUEUE_PRI(_n, _p) ((_p) << (2 * (_n))) +/* QAV Tx mode control registers where _n can be 0 or 1. */ +#define E1000_I210_TQAVCC(_n) (0x3004 + 0x40 * (_n)) + +/* QAV Tx mode control register bitfields masks */ +#define E1000_TQAVCC_IDLE_SLOPE 0xFFFF /* Idle slope */ +#define E1000_TQAVCC_KEEP_CREDITS (1 << 30) /* Keep credits opt enable */ +#define E1000_TQAVCC_QUEUE_MODE (1 << 31) /* SP vs. SR Tx mode */ + +/* Good transmitted packets counter registers */ +#define E1000_PQGPTC(_n) (0x010014 + (0x100 * (_n))) + +/* Queues packet buffer size masks where _n can be 0-3 and _s 0-63 [kB] */ +#define E1000_I210_TXPBS_SIZE(_n, _s) ((_s) << (6 * (_n))) + +#define E1000_MMDAC 13 /* MMD Access Control */ +#define E1000_MMDAAD 14 /* MMD Access Address/Data */ + +/* Convenience macros + * + * Note: "_n" is the queue number of the register to be written to. + * + * Example usage: + * E1000_RDBAL_REG(current_rx_queue) + */ +#define E1000_RDBAL(_n) ((_n) < 4 ? (0x02800 + ((_n) * 0x100)) : \ + (0x0C000 + ((_n) * 0x40))) +#define E1000_RDBAH(_n) ((_n) < 4 ? (0x02804 + ((_n) * 0x100)) : \ + (0x0C004 + ((_n) * 0x40))) +#define E1000_RDLEN(_n) ((_n) < 4 ? (0x02808 + ((_n) * 0x100)) : \ + (0x0C008 + ((_n) * 0x40))) +#define E1000_SRRCTL(_n) ((_n) < 4 ? (0x0280C + ((_n) * 0x100)) : \ + (0x0C00C + ((_n) * 0x40))) +#define E1000_RDH(_n) ((_n) < 4 ? (0x02810 + ((_n) * 0x100)) : \ + (0x0C010 + ((_n) * 0x40))) +#define E1000_RXCTL(_n) ((_n) < 4 ? (0x02814 + ((_n) * 0x100)) : \ + (0x0C014 + ((_n) * 0x40))) +#define E1000_DCA_RXCTRL(_n) E1000_RXCTL(_n) +#define E1000_RDT(_n) ((_n) < 4 ? (0x02818 + ((_n) * 0x100)) : \ + (0x0C018 + ((_n) * 0x40))) +#define E1000_RXDCTL(_n) ((_n) < 4 ? (0x02828 + ((_n) * 0x100)) : \ + (0x0C028 + ((_n) * 0x40))) +#define E1000_RQDPC(_n) ((_n) < 4 ? (0x02830 + ((_n) * 0x100)) : \ + (0x0C030 + ((_n) * 0x40))) +#define E1000_TDBAL(_n) ((_n) < 4 ? (0x03800 + ((_n) * 0x100)) : \ + (0x0E000 + ((_n) * 0x40))) +#define E1000_TDBAH(_n) ((_n) < 4 ? (0x03804 + ((_n) * 0x100)) : \ + (0x0E004 + ((_n) * 0x40))) +#define E1000_TDLEN(_n) ((_n) < 4 ? (0x03808 + ((_n) * 0x100)) : \ + (0x0E008 + ((_n) * 0x40))) +#define E1000_TDH(_n) ((_n) < 4 ? (0x03810 + ((_n) * 0x100)) : \ + (0x0E010 + ((_n) * 0x40))) +#define E1000_TXCTL(_n) ((_n) < 4 ? (0x03814 + ((_n) * 0x100)) : \ + (0x0E014 + ((_n) * 0x40))) +#define E1000_DCA_TXCTRL(_n) E1000_TXCTL(_n) +#define E1000_TDT(_n) ((_n) < 4 ? (0x03818 + ((_n) * 0x100)) : \ + (0x0E018 + ((_n) * 0x40))) +#define E1000_TXDCTL(_n) ((_n) < 4 ? (0x03828 + ((_n) * 0x100)) : \ + (0x0E028 + ((_n) * 0x40))) +#define E1000_TDWBAL(_n) ((_n) < 4 ? (0x03838 + ((_n) * 0x100)) : \ + (0x0E038 + ((_n) * 0x40))) +#define E1000_TDWBAH(_n) ((_n) < 4 ? (0x0383C + ((_n) * 0x100)) : \ + (0x0E03C + ((_n) * 0x40))) +#define E1000_TARC(_n) (0x03840 + ((_n) * 0x100)) +#define E1000_RSRPD 0x02C00 /* Rx Small Packet Detect - RW */ +#define E1000_RAID 0x02C08 /* Receive Ack Interrupt Delay - RW */ +#define E1000_KABGTXD 0x03004 /* AFE Band Gap Transmit Ref Data */ +#define E1000_PSRTYPE(_i) (0x05480 + ((_i) * 4)) +#define E1000_RAL(_i) (((_i) <= 15) ? (0x05400 + ((_i) * 8)) : \ + (0x054E0 + ((_i - 16) * 8))) +#define E1000_RAH(_i) (((_i) <= 15) ? (0x05404 + ((_i) * 8)) : \ + (0x054E4 + ((_i - 16) * 8))) +#define E1000_SHRAL(_i) (0x05438 + ((_i) * 8)) +#define E1000_SHRAH(_i) (0x0543C + ((_i) * 8)) +#define E1000_IP4AT_REG(_i) (0x05840 + ((_i) * 8)) +#define E1000_IP6AT_REG(_i) (0x05880 + ((_i) * 4)) +#define E1000_WUPM_REG(_i) (0x05A00 + ((_i) * 4)) +#define E1000_FFMT_REG(_i) (0x09000 + ((_i) * 8)) +#define E1000_FFVT_REG(_i) (0x09800 + ((_i) * 8)) +#define E1000_FFLT_REG(_i) (0x05F00 + ((_i) * 8)) +#define E1000_PBSLAC 0x03100 /* Pkt Buffer Slave Access Control */ +#define E1000_PBSLAD(_n) (0x03110 + (0x4 * (_n))) /* Pkt Buffer DWORD */ +#define E1000_TXPBS 0x03404 /* Tx Packet Buffer Size - RW */ +/* Same as TXPBS, renamed for newer Si - RW */ +#define E1000_ITPBS 0x03404 +#define E1000_TDFH 0x03410 /* Tx Data FIFO Head - RW */ +#define E1000_TDFT 0x03418 /* Tx Data FIFO Tail - RW */ +#define E1000_TDFHS 0x03420 /* Tx Data FIFO Head Saved - RW */ +#define E1000_TDFTS 0x03428 /* Tx Data FIFO Tail Saved - RW */ +#define E1000_TDFPC 0x03430 /* Tx Data FIFO Packet Count - RW */ +#define E1000_TDPUMB 0x0357C /* DMA Tx Desc uC Mail Box - RW */ +#define E1000_TDPUAD 0x03580 /* DMA Tx Desc uC Addr Command - RW */ +#define E1000_TDPUWD 0x03584 /* DMA Tx Desc uC Data Write - RW */ +#define E1000_TDPURD 0x03588 /* DMA Tx Desc uC Data Read - RW */ +#define E1000_TDPUCTL 0x0358C /* DMA Tx Desc uC Control - RW */ +#define E1000_DTXCTL 0x03590 /* DMA Tx Control - RW */ +#define E1000_DTXTCPFLGL 0x0359C /* DMA Tx Control flag low - RW */ +#define E1000_DTXTCPFLGH 0x035A0 /* DMA Tx Control flag high - RW */ +/* DMA Tx Max Total Allow Size Reqs - RW */ +#define E1000_DTXMXSZRQ 0x03540 +#define E1000_TIDV 0x03820 /* Tx Interrupt Delay Value - RW */ +#define E1000_TADV 0x0382C /* Tx Interrupt Absolute Delay Val - RW */ +#define E1000_CRCERRS 0x04000 /* CRC Error Count - R/clr */ +#define E1000_ALGNERRC 0x04004 /* Alignment Error Count - R/clr */ +#define E1000_SYMERRS 0x04008 /* Symbol Error Count - R/clr */ +#define E1000_RXERRC 0x0400C /* Receive Error Count - R/clr */ +#define E1000_MPC 0x04010 /* Missed Packet Count - R/clr */ +#define E1000_SCC 0x04014 /* Single Collision Count - R/clr */ +#define E1000_ECOL 0x04018 /* Excessive Collision Count - R/clr */ +#define E1000_MCC 0x0401C /* Multiple Collision Count - R/clr */ +#define E1000_LATECOL 0x04020 /* Late Collision Count - R/clr */ +#define E1000_COLC 0x04028 /* Collision Count - R/clr */ +#define E1000_DC 0x04030 /* Defer Count - R/clr */ +#define E1000_TNCRS 0x04034 /* Tx-No CRS - R/clr */ +#define E1000_SEC 0x04038 /* Sequence Error Count - R/clr */ +#define E1000_CEXTERR 0x0403C /* Carrier Extension Error Count - R/clr */ +#define E1000_RLEC 0x04040 /* Receive Length Error Count - R/clr */ +#define E1000_XONRXC 0x04048 /* XON Rx Count - R/clr */ +#define E1000_XONTXC 0x0404C /* XON Tx Count - R/clr */ +#define E1000_XOFFRXC 0x04050 /* XOFF Rx Count - R/clr */ +#define E1000_XOFFTXC 0x04054 /* XOFF Tx Count - R/clr */ +#define E1000_FCRUC 0x04058 /* Flow Control Rx Unsupported Count- R/clr */ +#define E1000_PRC64 0x0405C /* Packets Rx (64 bytes) - R/clr */ +#define E1000_PRC127 0x04060 /* Packets Rx (65-127 bytes) - R/clr */ +#define E1000_PRC255 0x04064 /* Packets Rx (128-255 bytes) - R/clr */ +#define E1000_PRC511 0x04068 /* Packets Rx (255-511 bytes) - R/clr */ +#define E1000_PRC1023 0x0406C /* Packets Rx (512-1023 bytes) - R/clr */ +#define E1000_PRC1522 0x04070 /* Packets Rx (1024-1522 bytes) - R/clr */ +#define E1000_GPRC 0x04074 /* Good Packets Rx Count - R/clr */ +#define E1000_BPRC 0x04078 /* Broadcast Packets Rx Count - R/clr */ +#define E1000_MPRC 0x0407C /* Multicast Packets Rx Count - R/clr */ +#define E1000_GPTC 0x04080 /* Good Packets Tx Count - R/clr */ +#define E1000_GORCL 0x04088 /* Good Octets Rx Count Low - R/clr */ +#define E1000_GORCH 0x0408C /* Good Octets Rx Count High - R/clr */ +#define E1000_GOTCL 0x04090 /* Good Octets Tx Count Low - R/clr */ +#define E1000_GOTCH 0x04094 /* Good Octets Tx Count High - R/clr */ +#define E1000_RNBC 0x040A0 /* Rx No Buffers Count - R/clr */ +#define E1000_RUC 0x040A4 /* Rx Undersize Count - R/clr */ +#define E1000_RFC 0x040A8 /* Rx Fragment Count - R/clr */ +#define E1000_ROC 0x040AC /* Rx Oversize Count - R/clr */ +#define E1000_RJC 0x040B0 /* Rx Jabber Count - R/clr */ +#define E1000_MGTPRC 0x040B4 /* Management Packets Rx Count - R/clr */ +#define E1000_MGTPDC 0x040B8 /* Management Packets Dropped Count - R/clr */ +#define E1000_MGTPTC 0x040BC /* Management Packets Tx Count - R/clr */ +#define E1000_TORL 0x040C0 /* Total Octets Rx Low - R/clr */ +#define E1000_TORH 0x040C4 /* Total Octets Rx High - R/clr */ +#define E1000_TOTL 0x040C8 /* Total Octets Tx Low - R/clr */ +#define E1000_TOTH 0x040CC /* Total Octets Tx High - R/clr */ +#define E1000_TPR 0x040D0 /* Total Packets Rx - R/clr */ +#define E1000_TPT 0x040D4 /* Total Packets Tx - R/clr */ +#define E1000_PTC64 0x040D8 /* Packets Tx (64 bytes) - R/clr */ +#define E1000_PTC127 0x040DC /* Packets Tx (65-127 bytes) - R/clr */ +#define E1000_PTC255 0x040E0 /* Packets Tx (128-255 bytes) - R/clr */ +#define E1000_PTC511 0x040E4 /* Packets Tx (256-511 bytes) - R/clr */ +#define E1000_PTC1023 0x040E8 /* Packets Tx (512-1023 bytes) - R/clr */ +#define E1000_PTC1522 0x040EC /* Packets Tx (1024-1522 Bytes) - R/clr */ +#define E1000_MPTC 0x040F0 /* Multicast Packets Tx Count - R/clr */ +#define E1000_BPTC 0x040F4 /* Broadcast Packets Tx Count - R/clr */ +#define E1000_TSCTC 0x040F8 /* TCP Segmentation Context Tx - R/clr */ +#define E1000_TSCTFC 0x040FC /* TCP Segmentation Context Tx Fail - R/clr */ +#define E1000_IAC 0x04100 /* Interrupt Assertion Count */ +#define E1000_ICRXPTC 0x04104 /* Interrupt Cause Rx Pkt Timer Expire Count */ +#define E1000_ICRXATC 0x04108 /* Interrupt Cause Rx Abs Timer Expire Count */ +#define E1000_ICTXPTC 0x0410C /* Interrupt Cause Tx Pkt Timer Expire Count */ +#define E1000_ICTXATC 0x04110 /* Interrupt Cause Tx Abs Timer Expire Count */ +#define E1000_ICTXQEC 0x04118 /* Interrupt Cause Tx Queue Empty Count */ +#define E1000_ICTXQMTC 0x0411C /* Interrupt Cause Tx Queue Min Thresh Count */ +#define E1000_ICRXDMTC 0x04120 /* Interrupt Cause Rx Desc Min Thresh Count */ +#define E1000_ICRXOC 0x04124 /* Interrupt Cause Receiver Overrun Count */ + +/* Virtualization statistical counters */ +#define E1000_PFVFGPRC(_n) (0x010010 + (0x100 * (_n))) +#define E1000_PFVFGPTC(_n) (0x010014 + (0x100 * (_n))) +#define E1000_PFVFGORC(_n) (0x010018 + (0x100 * (_n))) +#define E1000_PFVFGOTC(_n) (0x010034 + (0x100 * (_n))) +#define E1000_PFVFMPRC(_n) (0x010038 + (0x100 * (_n))) +#define E1000_PFVFGPRLBC(_n) (0x010040 + (0x100 * (_n))) +#define E1000_PFVFGPTLBC(_n) (0x010044 + (0x100 * (_n))) +#define E1000_PFVFGORLBC(_n) (0x010048 + (0x100 * (_n))) +#define E1000_PFVFGOTLBC(_n) (0x010050 + (0x100 * (_n))) + +/* LinkSec */ +#define E1000_LSECTXUT 0x04300 /* Tx Untagged Pkt Cnt */ +#define E1000_LSECTXPKTE 0x04304 /* Encrypted Tx Pkts Cnt */ +#define E1000_LSECTXPKTP 0x04308 /* Protected Tx Pkt Cnt */ +#define E1000_LSECTXOCTE 0x0430C /* Encrypted Tx Octets Cnt */ +#define E1000_LSECTXOCTP 0x04310 /* Protected Tx Octets Cnt */ +#define E1000_LSECRXUT 0x04314 /* Untagged non-Strict Rx Pkt Cnt */ +#define E1000_LSECRXOCTD 0x0431C /* Rx Octets Decrypted Count */ +#define E1000_LSECRXOCTV 0x04320 /* Rx Octets Validated */ +#define E1000_LSECRXBAD 0x04324 /* Rx Bad Tag */ +#define E1000_LSECRXNOSCI 0x04328 /* Rx Packet No SCI Count */ +#define E1000_LSECRXUNSCI 0x0432C /* Rx Packet Unknown SCI Count */ +#define E1000_LSECRXUNCH 0x04330 /* Rx Unchecked Packets Count */ +#define E1000_LSECRXDELAY 0x04340 /* Rx Delayed Packet Count */ +#define E1000_LSECRXLATE 0x04350 /* Rx Late Packets Count */ +#define E1000_LSECRXOK(_n) (0x04360 + (0x04 * (_n))) /* Rx Pkt OK Cnt */ +#define E1000_LSECRXINV(_n) (0x04380 + (0x04 * (_n))) /* Rx Invalid Cnt */ +#define E1000_LSECRXNV(_n) (0x043A0 + (0x04 * (_n))) /* Rx Not Valid Cnt */ +#define E1000_LSECRXUNSA 0x043C0 /* Rx Unused SA Count */ +#define E1000_LSECRXNUSA 0x043D0 /* Rx Not Using SA Count */ +#define E1000_LSECTXCAP 0x0B000 /* Tx Capabilities Register - RO */ +#define E1000_LSECRXCAP 0x0B300 /* Rx Capabilities Register - RO */ +#define E1000_LSECTXCTRL 0x0B004 /* Tx Control - RW */ +#define E1000_LSECRXCTRL 0x0B304 /* Rx Control - RW */ +#define E1000_LSECTXSCL 0x0B008 /* Tx SCI Low - RW */ +#define E1000_LSECTXSCH 0x0B00C /* Tx SCI High - RW */ +#define E1000_LSECTXSA 0x0B010 /* Tx SA0 - RW */ +#define E1000_LSECTXPN0 0x0B018 /* Tx SA PN 0 - RW */ +#define E1000_LSECTXPN1 0x0B01C /* Tx SA PN 1 - RW */ +#define E1000_LSECRXSCL 0x0B3D0 /* Rx SCI Low - RW */ +#define E1000_LSECRXSCH 0x0B3E0 /* Rx SCI High - RW */ +/* LinkSec Tx 128-bit Key 0 - WO */ +#define E1000_LSECTXKEY0(_n) (0x0B020 + (0x04 * (_n))) +/* LinkSec Tx 128-bit Key 1 - WO */ +#define E1000_LSECTXKEY1(_n) (0x0B030 + (0x04 * (_n))) +#define E1000_LSECRXSA(_n) (0x0B310 + (0x04 * (_n))) /* Rx SAs - RW */ +#define E1000_LSECRXPN(_n) (0x0B330 + (0x04 * (_n))) /* Rx SAs - RW */ +/* LinkSec Rx Keys - where _n is the SA no. and _m the 4 dwords of the 128 bit + * key - RW. + */ +#define E1000_LSECRXKEY(_n, _m) (0x0B350 + (0x10 * (_n)) + (0x04 * (_m))) + +#define E1000_SSVPC 0x041A0 /* Switch Security Violation Pkt Cnt */ +#define E1000_IPSCTRL 0xB430 /* IpSec Control Register */ +#define E1000_IPSRXCMD 0x0B408 /* IPSec Rx Command Register - RW */ +#define E1000_IPSRXIDX 0x0B400 /* IPSec Rx Index - RW */ +/* IPSec Rx IPv4/v6 Address - RW */ +#define E1000_IPSRXIPADDR(_n) (0x0B420 + (0x04 * (_n))) +/* IPSec Rx 128-bit Key - RW */ +#define E1000_IPSRXKEY(_n) (0x0B410 + (0x04 * (_n))) +#define E1000_IPSRXSALT 0x0B404 /* IPSec Rx Salt - RW */ +#define E1000_IPSRXSPI 0x0B40C /* IPSec Rx SPI - RW */ +/* IPSec Tx 128-bit Key - RW */ +#define E1000_IPSTXKEY(_n) (0x0B460 + (0x04 * (_n))) +#define E1000_IPSTXSALT 0x0B454 /* IPSec Tx Salt - RW */ +#define E1000_IPSTXIDX 0x0B450 /* IPSec Tx SA IDX - RW */ +#define E1000_PCS_CFG0 0x04200 /* PCS Configuration 0 - RW */ +#define E1000_PCS_LCTL 0x04208 /* PCS Link Control - RW */ +#define E1000_PCS_LSTAT 0x0420C /* PCS Link Status - RO */ +#define E1000_CBTMPC 0x0402C /* Circuit Breaker Tx Packet Count */ +#define E1000_HTDPMC 0x0403C /* Host Transmit Discarded Packets */ +#define E1000_CBRDPC 0x04044 /* Circuit Breaker Rx Dropped Count */ +#define E1000_CBRMPC 0x040FC /* Circuit Breaker Rx Packet Count */ +#define E1000_RPTHC 0x04104 /* Rx Packets To Host */ +#define E1000_HGPTC 0x04118 /* Host Good Packets Tx Count */ +#define E1000_HTCBDPC 0x04124 /* Host Tx Circuit Breaker Dropped Count */ +#define E1000_HGORCL 0x04128 /* Host Good Octets Received Count Low */ +#define E1000_HGORCH 0x0412C /* Host Good Octets Received Count High */ +#define E1000_HGOTCL 0x04130 /* Host Good Octets Transmit Count Low */ +#define E1000_HGOTCH 0x04134 /* Host Good Octets Transmit Count High */ +#define E1000_LENERRS 0x04138 /* Length Errors Count */ +#define E1000_SCVPC 0x04228 /* SerDes/SGMII Code Violation Pkt Count */ +#define E1000_HRMPC 0x0A018 /* Header Redirection Missed Packet Count */ +#define E1000_PCS_ANADV 0x04218 /* AN advertisement - RW */ +#define E1000_PCS_LPAB 0x0421C /* Link Partner Ability - RW */ +#define E1000_PCS_NPTX 0x04220 /* AN Next Page Transmit - RW */ +#define E1000_PCS_LPABNP 0x04224 /* Link Partner Ability Next Pg - RW */ +#define E1000_RXCSUM 0x05000 /* Rx Checksum Control - RW */ +#define E1000_RLPML 0x05004 /* Rx Long Packet Max Length */ +#define E1000_RFCTL 0x05008 /* Receive Filter Control*/ +#define E1000_MTA 0x05200 /* Multicast Table Array - RW Array */ +#define E1000_RA 0x05400 /* Receive Address - RW Array */ +#define E1000_RA2 0x054E0 /* 2nd half of Rx address array - RW Array */ +#define E1000_VFTA 0x05600 /* VLAN Filter Table Array - RW Array */ +#define E1000_VT_CTL 0x0581C /* VMDq Control - RW */ +#define E1000_CIAA 0x05B88 /* Config Indirect Access Address - RW */ +#define E1000_CIAD 0x05B8C /* Config Indirect Access Data - RW */ +#define E1000_VFQA0 0x0B000 /* VLAN Filter Queue Array 0 - RW Array */ +#define E1000_VFQA1 0x0B200 /* VLAN Filter Queue Array 1 - RW Array */ +#define E1000_WUC 0x05800 /* Wakeup Control - RW */ +#define E1000_WUFC 0x05808 /* Wakeup Filter Control - RW */ +#define E1000_WUS 0x05810 /* Wakeup Status - RO */ +#define E1000_MANC 0x05820 /* Management Control - RW */ +#define E1000_IPAV 0x05838 /* IP Address Valid - RW */ +#define E1000_IP4AT 0x05840 /* IPv4 Address Table - RW Array */ +#define E1000_IP6AT 0x05880 /* IPv6 Address Table - RW Array */ +#define E1000_WUPL 0x05900 /* Wakeup Packet Length - RW */ +#define E1000_WUPM 0x05A00 /* Wakeup Packet Memory - RO A */ +#define E1000_PBACL 0x05B68 /* MSIx PBA Clear - Read/Write 1's to clear */ +#define E1000_FFLT 0x05F00 /* Flexible Filter Length Table - RW Array */ +#define E1000_HOST_IF 0x08800 /* Host Interface */ +#define E1000_HIBBA 0x8F40 /* Host Interface Buffer Base Address */ +/* Flexible Host Filter Table */ +#define E1000_FHFT(_n) (0x09000 + ((_n) * 0x100)) +/* Ext Flexible Host Filter Table */ +#define E1000_FHFT_EXT(_n) (0x09A00 + ((_n) * 0x100)) + +#define E1000_KMRNCTRLSTA 0x00034 /* MAC-PHY interface - RW */ +#define E1000_MANC2H 0x05860 /* Management Control To Host - RW */ +/* Management Decision Filters */ +#define E1000_MDEF(_n) (0x05890 + (4 * (_n))) +#define E1000_SW_FW_SYNC 0x05B5C /* SW-FW Synchronization - RW */ +#define E1000_CCMCTL 0x05B48 /* CCM Control Register */ +#define E1000_GIOCTL 0x05B44 /* GIO Analog Control Register */ +#define E1000_SCCTL 0x05B4C /* PCIc PLL Configuration Register */ +#define E1000_GCR 0x05B00 /* PCI-Ex Control */ +#define E1000_GCR2 0x05B64 /* PCI-Ex Control #2 */ +#define E1000_GSCL_1 0x05B10 /* PCI-Ex Statistic Control #1 */ +#define E1000_GSCL_2 0x05B14 /* PCI-Ex Statistic Control #2 */ +#define E1000_GSCL_3 0x05B18 /* PCI-Ex Statistic Control #3 */ +#define E1000_GSCL_4 0x05B1C /* PCI-Ex Statistic Control #4 */ +#define E1000_FACTPS 0x05B30 /* Function Active and Power State to MNG */ +#define E1000_SWSM 0x05B50 /* SW Semaphore */ +#define E1000_FWSM 0x05B54 /* FW Semaphore */ +/* Driver-only SW semaphore (not used by BOOT agents) */ +#define E1000_SWSM2 0x05B58 +#define E1000_DCA_ID 0x05B70 /* DCA Requester ID Information - RO */ +#define E1000_DCA_CTRL 0x05B74 /* DCA Control - RW */ +#define E1000_UFUSE 0x05B78 /* UFUSE - RO */ +#define E1000_FFLT_DBG 0x05F04 /* Debug Register */ +#define E1000_HICR 0x08F00 /* Host Interface Control */ +#define E1000_FWSTS 0x08F0C /* FW Status */ + +/* RSS registers */ +#define E1000_CPUVEC 0x02C10 /* CPU Vector Register - RW */ +#define E1000_MRQC 0x05818 /* Multiple Receive Control - RW */ +#define E1000_IMIR(_i) (0x05A80 + ((_i) * 4)) /* Immediate Interrupt */ +#define E1000_IMIREXT(_i) (0x05AA0 + ((_i) * 4)) /* Immediate INTR Ext*/ +#define E1000_IMIRVP 0x05AC0 /* Immediate INT Rx VLAN Priority -RW */ +#define E1000_MSIXBM(_i) (0x01600 + ((_i) * 4)) /* MSI-X Alloc Reg -RW */ +#define E1000_RETA(_i) (0x05C00 + ((_i) * 4)) /* Redirection Table - RW */ +#define E1000_RSSRK(_i) (0x05C80 + ((_i) * 4)) /* RSS Random Key - RW */ +#define E1000_RSSIM 0x05864 /* RSS Interrupt Mask */ +#define E1000_RSSIR 0x05868 /* RSS Interrupt Request */ +/* VT Registers */ +#define E1000_SWPBS 0x03004 /* Switch Packet Buffer Size - RW */ +#define E1000_MBVFICR 0x00C80 /* Mailbox VF Cause - RWC */ +#define E1000_MBVFIMR 0x00C84 /* Mailbox VF int Mask - RW */ +#define E1000_VFLRE 0x00C88 /* VF Register Events - RWC */ +#define E1000_VFRE 0x00C8C /* VF Receive Enables */ +#define E1000_VFTE 0x00C90 /* VF Transmit Enables */ +#define E1000_QDE 0x02408 /* Queue Drop Enable - RW */ +#define E1000_DTXSWC 0x03500 /* DMA Tx Switch Control - RW */ +#define E1000_WVBR 0x03554 /* VM Wrong Behavior - RWS */ +#define E1000_RPLOLR 0x05AF0 /* Replication Offload - RW */ +#define E1000_UTA 0x0A000 /* Unicast Table Array - RW */ +#define E1000_IOVTCL 0x05BBC /* IOV Control Register */ +#define E1000_VMRCTL 0X05D80 /* Virtual Mirror Rule Control */ +#define E1000_VMRVLAN 0x05D90 /* Virtual Mirror Rule VLAN */ +#define E1000_VMRVM 0x05DA0 /* Virtual Mirror Rule VM */ +#define E1000_MDFB 0x03558 /* Malicious Driver free block */ +#define E1000_LVMMC 0x03548 /* Last VM Misbehavior cause */ +#define E1000_TXSWC 0x05ACC /* Tx Switch Control */ +#define E1000_SCCRL 0x05DB0 /* Storm Control Control */ +#define E1000_BSCTRH 0x05DB8 /* Broadcast Storm Control Threshold */ +#define E1000_MSCTRH 0x05DBC /* Multicast Storm Control Threshold */ +/* These act per VF so an array friendly macro is used */ +#define E1000_V2PMAILBOX(_n) (0x00C40 + (4 * (_n))) +#define E1000_P2VMAILBOX(_n) (0x00C00 + (4 * (_n))) +#define E1000_VMBMEM(_n) (0x00800 + (64 * (_n))) +#define E1000_VFVMBMEM(_n) (0x00800 + (_n)) +#define E1000_VMOLR(_n) (0x05AD0 + (4 * (_n))) +/* VLAN Virtual Machine Filter - RW */ +#define E1000_VLVF(_n) (0x05D00 + (4 * (_n))) +#define E1000_VMVIR(_n) (0x03700 + (4 * (_n))) +#define E1000_DVMOLR(_n) (0x0C038 + (0x40 * (_n))) /* DMA VM offload */ +#define E1000_VTCTRL(_n) (0x10000 + (0x100 * (_n))) /* VT Control */ +#define E1000_TSYNCRXCTL 0x0B620 /* Rx Time Sync Control register - RW */ +#define E1000_TSYNCTXCTL 0x0B614 /* Tx Time Sync Control register - RW */ +#define E1000_TSYNCRXCFG 0x05F50 /* Time Sync Rx Configuration - RW */ +#define E1000_RXSTMPL 0x0B624 /* Rx timestamp Low - RO */ +#define E1000_RXSTMPH 0x0B628 /* Rx timestamp High - RO */ +#define E1000_RXSATRL 0x0B62C /* Rx timestamp attribute low - RO */ +#define E1000_RXSATRH 0x0B630 /* Rx timestamp attribute high - RO */ +#define E1000_TXSTMPL 0x0B618 /* Tx timestamp value Low - RO */ +#define E1000_TXSTMPH 0x0B61C /* Tx timestamp value High - RO */ +#define E1000_SYSTIML 0x0B600 /* System time register Low - RO */ +#define E1000_SYSTIMH 0x0B604 /* System time register High - RO */ +#define E1000_TIMINCA 0x0B608 /* Increment attributes register - RW */ +#define E1000_TIMADJL 0x0B60C /* Time sync time adjustment offset Low - RW */ +#define E1000_TIMADJH 0x0B610 /* Time sync time adjustment offset High - RW */ +#define E1000_TSAUXC 0x0B640 /* Timesync Auxiliary Control register */ +#define E1000_TRGTTIML0 0x0B644 /* Target Time Register 0 Low - RW */ +#define E1000_TRGTTIMH0 0x0B648 /* Target Time Register 0 High - RW */ +#define E1000_TRGTTIML1 0x0B64C /* Target Time Register 1 Low - RW */ +#define E1000_TRGTTIMH1 0x0B650 /* Target Time Register 1 High - RW */ +#define E1000_FREQOUT0 0x0B654 /* Frequency Out 0 Control Register - RW */ +#define E1000_FREQOUT1 0x0B658 /* Frequency Out 1 Control Register - RW */ +#define E1000_AUXSTMPL0 0x0B65C /* Auxiliary Time Stamp 0 Register Low - RO */ +#define E1000_AUXSTMPH0 0x0B660 /* Auxiliary Time Stamp 0 Register High - RO */ +#define E1000_AUXSTMPL1 0x0B664 /* Auxiliary Time Stamp 1 Register Low - RO */ +#define E1000_AUXSTMPH1 0x0B668 /* Auxiliary Time Stamp 1 Register High - RO */ +#define E1000_SYSTIMR 0x0B6F8 /* System time register Residue */ +#define E1000_TSICR 0x0B66C /* Interrupt Cause Register */ +#define E1000_TSIM 0x0B674 /* Interrupt Mask Register */ + +/* Filtering Registers */ +#define E1000_SAQF(_n) (0x05980 + (4 * (_n))) /* Source Address Queue Fltr */ +#define E1000_DAQF(_n) (0x059A0 + (4 * (_n))) /* Dest Address Queue Fltr */ +#define E1000_SPQF(_n) (0x059C0 + (4 * (_n))) /* Source Port Queue Fltr */ +#define E1000_FTQF(_n) (0x059E0 + (4 * (_n))) /* 5-tuple Queue Fltr */ +#define E1000_TTQF(_n) (0x059E0 + (4 * (_n))) /* 2-tuple Queue Fltr */ +#define E1000_SYNQF(_n) (0x055FC + (4 * (_n))) /* SYN Packet Queue Fltr */ +#define E1000_ETQF(_n) (0x05CB0 + (4 * (_n))) /* EType Queue Fltr */ + +#define E1000_RTTDCS 0x3600 /* Reedtown Tx Desc plane control and status */ +#define E1000_RTTPCS 0x3474 /* Reedtown Tx Packet Plane control and status */ +#define E1000_RTRPCS 0x2474 /* Rx packet plane control and status */ +#define E1000_RTRUP2TC 0x05AC4 /* Rx User Priority to Traffic Class */ +#define E1000_RTTUP2TC 0x0418 /* Transmit User Priority to Traffic Class */ +/* Tx Desc plane TC Rate-scheduler config */ +#define E1000_RTTDTCRC(_n) (0x3610 + ((_n) * 4)) +/* Tx Packet plane TC Rate-Scheduler Config */ +#define E1000_RTTPTCRC(_n) (0x3480 + ((_n) * 4)) +/* Rx Packet plane TC Rate-Scheduler Config */ +#define E1000_RTRPTCRC(_n) (0x2480 + ((_n) * 4)) +/* Tx Desc Plane TC Rate-Scheduler Status */ +#define E1000_RTTDTCRS(_n) (0x3630 + ((_n) * 4)) +/* Tx Desc Plane TC Rate-Scheduler MMW */ +#define E1000_RTTDTCRM(_n) (0x3650 + ((_n) * 4)) +/* Tx Packet plane TC Rate-Scheduler Status */ +#define E1000_RTTPTCRS(_n) (0x34A0 + ((_n) * 4)) +/* Tx Packet plane TC Rate-scheduler MMW */ +#define E1000_RTTPTCRM(_n) (0x34C0 + ((_n) * 4)) +/* Rx Packet plane TC Rate-Scheduler Status */ +#define E1000_RTRPTCRS(_n) (0x24A0 + ((_n) * 4)) +/* Rx Packet plane TC Rate-Scheduler MMW */ +#define E1000_RTRPTCRM(_n) (0x24C0 + ((_n) * 4)) +/* Tx Desc plane VM Rate-Scheduler MMW*/ +#define E1000_RTTDVMRM(_n) (0x3670 + ((_n) * 4)) +/* Tx BCN Rate-Scheduler MMW */ +#define E1000_RTTBCNRM(_n) (0x3690 + ((_n) * 4)) +#define E1000_RTTDQSEL 0x3604 /* Tx Desc Plane Queue Select */ +#define E1000_RTTDVMRC 0x3608 /* Tx Desc Plane VM Rate-Scheduler Config */ +#define E1000_RTTDVMRS 0x360C /* Tx Desc Plane VM Rate-Scheduler Status */ +#define E1000_RTTBCNRC 0x36B0 /* Tx BCN Rate-Scheduler Config */ +#define E1000_RTTBCNRS 0x36B4 /* Tx BCN Rate-Scheduler Status */ +#define E1000_RTTBCNCR 0xB200 /* Tx BCN Control Register */ +#define E1000_RTTBCNTG 0x35A4 /* Tx BCN Tagging */ +#define E1000_RTTBCNCP 0xB208 /* Tx BCN Congestion point */ +#define E1000_RTRBCNCR 0xB20C /* Rx BCN Control Register */ +#define E1000_RTTBCNRD 0x36B8 /* Tx BCN Rate Drift */ +#define E1000_PFCTOP 0x1080 /* Priority Flow Control Type and Opcode */ +#define E1000_RTTBCNIDX 0xB204 /* Tx BCN Congestion Point */ +#define E1000_RTTBCNACH 0x0B214 /* Tx BCN Control High */ +#define E1000_RTTBCNACL 0x0B210 /* Tx BCN Control Low */ + +/* DMA Coalescing registers */ +#define E1000_DMACR 0x02508 /* Control Register */ +#define E1000_DMCTXTH 0x03550 /* Transmit Threshold */ +#define E1000_DMCTLX 0x02514 /* Time to Lx Request */ +#define E1000_DMCRTRH 0x05DD0 /* Receive Packet Rate Threshold */ +#define E1000_DMCCNT 0x05DD4 /* Current Rx Count */ +#define E1000_FCRTC 0x02170 /* Flow Control Rx high watermark */ +#define E1000_PCIEMISC 0x05BB8 /* PCIE misc config register */ + +/* PCIe Parity Status Register */ +#define E1000_PCIEERRSTS 0x05BA8 + +#define E1000_PROXYS 0x5F64 /* Proxying Status */ +#define E1000_PROXYFC 0x5F60 /* Proxying Filter Control */ +/* Thermal sensor configuration and status registers */ +#define E1000_THMJT 0x08100 /* Junction Temperature */ +#define E1000_THLOWTC 0x08104 /* Low Threshold Control */ +#define E1000_THMIDTC 0x08108 /* Mid Threshold Control */ +#define E1000_THHIGHTC 0x0810C /* High Threshold Control */ +#define E1000_THSTAT 0x08110 /* Thermal Sensor Status */ + +/* Energy Efficient Ethernet "EEE" registers */ +#define E1000_IPCNFG 0x0E38 /* Internal PHY Configuration */ +#define E1000_LTRC 0x01A0 /* Latency Tolerance Reporting Control */ +#define E1000_EEER 0x0E30 /* Energy Efficient Ethernet "EEE"*/ +#define E1000_EEE_SU 0x0E34 /* EEE Setup */ +#define E1000_TLPIC 0x4148 /* EEE Tx LPI Count - TLPIC */ +#define E1000_RLPIC 0x414C /* EEE Rx LPI Count - RLPIC */ + +/* OS2BMC Registers */ +#define E1000_B2OSPC 0x08FE0 /* BMC2OS packets sent by BMC */ +#define E1000_B2OGPRC 0x04158 /* BMC2OS packets received by host */ +#define E1000_O2BGPTC 0x08FE4 /* OS2BMC packets received by BMC */ +#define E1000_O2BSPC 0x0415C /* OS2BMC packets transmitted by host */ + +#endif diff --git a/drivers/staging/igb_avb/igb.h b/drivers/staging/igb_avb/igb.h new file mode 100644 index 000000000000..c8bbf307f908 --- /dev/null +++ b/drivers/staging/igb_avb/igb.h @@ -0,0 +1,937 @@ +/******************************************************************************* + + Intel(R) Gigabit Ethernet Linux driver + Copyright(c) 2007-2016 Intel Corporation. + + This program is free software; you can redistribute it and/or modify it + under the terms and conditions of the GNU General Public License, + version 2, as published by the Free Software Foundation. + + This program is distributed in the hope 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. + + The full GNU General Public License is included in this distribution in + the file called "COPYING". + + Contact Information: + Linux NICS + e1000-devel Mailing List + Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + +*******************************************************************************/ + +/* Linux PRO/1000 Ethernet Driver main header file */ + +#ifndef _IGB_H_ +#define _IGB_H_ + +#include + +#ifndef IGB_NO_LRO +#include +#endif + +#include +#include +#include + +#ifdef SIOCETHTOOL +#include +#endif + +struct igb_adapter; + +struct igb_user_page; + +struct igb_user_page { + struct igb_user_page *prev; + struct igb_user_page *next; + struct page *page; + dma_addr_t page_dma; +}; +#if defined(CONFIG_DCA) || defined(CONFIG_DCA_MODULE) +#define IGB_DCA +#endif +#ifdef IGB_DCA +#include +#endif + +#include "kcompat.h" + +#ifdef HAVE_SCTP +#include +#endif + +#include "e1000_api.h" +#include "e1000_82575.h" +#include "e1000_manage.h" +#include "e1000_mbx.h" + +#define IGB_ERR(args...) pr_err(KERN_ERR "igb: " args) + +#define PFX "igb: " +#define DPRINTK(nlevel, klevel, fmt, args...) \ + (void)((NETIF_MSG_##nlevel & adapter->msg_enable) && \ + printk(KERN_##klevel PFX "%s: %s: " fmt, adapter->netdev->name, \ + __func__ , ## args)) + +#ifdef HAVE_PTP_1588_CLOCK +#ifdef HAVE_INCLUDE_LINUX_TIMECOUNTER_H +#include +#else +#include +#endif /* HAVE_INCLUDE_TIMECOUNTER_H */ +#include +#include +#endif /* HAVE_PTP_1588_CLOCK */ + +#ifdef HAVE_I2C_SUPPORT +#include +#include +#endif /* HAVE_I2C_SUPPORT */ + +#include +typedef u64 cycle_t; + +/* Interrupt defines */ +#define IGB_START_ITR 648 /* ~6000 ints/sec */ +#define IGB_4K_ITR 980 +#define IGB_20K_ITR 196 +#define IGB_70K_ITR 56 + +/* Interrupt modes, as used by the IntMode paramter */ +#define IGB_INT_MODE_LEGACY 0 +#define IGB_INT_MODE_MSI 1 +#define IGB_INT_MODE_MSIX 2 + +/* TX/RX descriptor defines */ +#define IGB_DEFAULT_TXD 256 +#define IGB_DEFAULT_TX_WORK 128 +#define IGB_MIN_TXD 80 +#define IGB_MAX_TXD 4096 + +#define IGB_DEFAULT_RXD 256 +#define IGB_MIN_RXD 80 +#define IGB_MAX_RXD 4096 + +#define IGB_MIN_ITR_USECS 10 /* 100k irq/sec */ +#define IGB_MAX_ITR_USECS 8191 /* 120 irq/sec */ + +#define NON_Q_VECTORS 1 +#define MAX_Q_VECTORS 10 + +/* Transmit and receive queues */ +#define IGB_MAX_RX_QUEUES 16 +#define IGB_MAX_RX_QUEUES_82575 4 +#define IGB_MAX_RX_QUEUES_I211 2 +#define IGB_MAX_TX_QUEUES 16 + +#define IGB_MAX_VF_MC_ENTRIES 30 +#define IGB_MAX_VF_FUNCTIONS 8 +#define IGB_82576_VF_DEV_ID 0x10CA +#define IGB_I350_VF_DEV_ID 0x1520 +#define IGB_MAX_UTA_ENTRIES 128 +#define MAX_EMULATION_MAC_ADDRS 16 +#define OUI_LEN 3 +#define IGB_MAX_VMDQ_QUEUES 8 + +struct vf_data_storage { + unsigned char vf_mac_addresses[ETH_ALEN]; + u16 vf_mc_hashes[IGB_MAX_VF_MC_ENTRIES]; + u16 num_vf_mc_hashes; + u16 default_vf_vlan_id; + u16 vlans_enabled; + unsigned char em_mac_addresses[MAX_EMULATION_MAC_ADDRS * ETH_ALEN]; + u32 uta_table_copy[IGB_MAX_UTA_ENTRIES]; + u32 flags; + unsigned long last_nack; +#ifdef IFLA_VF_MAX + u16 pf_vlan; /* When set, guest VLAN config not allowed. */ + u16 pf_qos; + u16 tx_rate; +#ifdef HAVE_VF_SPOOFCHK_CONFIGURE + bool spoofchk_enabled; +#endif +#endif +}; + +#define IGB_VF_FLAG_CTS 0x00000001 /* VF is clear to send data */ +#define IGB_VF_FLAG_UNI_PROMISC 0x00000002 /* VF has unicast promisc */ +#define IGB_VF_FLAG_MULTI_PROMISC 0x00000004 /* VF has multicast promisc */ +#define IGB_VF_FLAG_PF_SET_MAC 0x00000008 /* PF has set MAC address */ + +/* RX descriptor control thresholds. + * PTHRESH - MAC will consider prefetch if it has fewer than this number of + * descriptors available in its onboard memory. + * Setting this to 0 disables RX descriptor prefetch. + * HTHRESH - MAC will only prefetch if there are at least this many descriptors + * available in host memory. + * If PTHRESH is 0, this should also be 0. + * WTHRESH - RX descriptor writeback threshold - MAC will delay writing back + * descriptors until either it has this many to write back, or the + * ITR timer expires. + */ +#define IGB_RX_PTHRESH ((hw->mac.type == e1000_i354) ? 12 : 8) +#define IGB_RX_HTHRESH 8 +#define IGB_TX_PTHRESH ((hw->mac.type == e1000_i354) ? 20 : 8) +#define IGB_TX_HTHRESH 1 +#define IGB_RX_WTHRESH ((hw->mac.type == e1000_82576 && \ + adapter->msix_entries) ? 1 : 4) + +/* this is the size past which hardware will drop packets when setting LPE=0 */ +#define MAXIMUM_ETHERNET_VLAN_SIZE 1522 + +/* NOTE: netdev_alloc_skb reserves 16 bytes, NET_IP_ALIGN means we + * reserve 2 more, and skb_shared_info adds an additional 384 more, + * this adds roughly 448 bytes of extra data meaning the smallest + * allocation we could have is 1K. + * i.e. RXBUFFER_512 --> size-1024 slab + */ +/* Supported Rx Buffer Sizes */ +#define IGB_RXBUFFER_256 256 +#define IGB_RXBUFFER_2048 2048 +#define IGB_RXBUFFER_16384 16384 +#define IGB_RX_HDR_LEN IGB_RXBUFFER_256 +#if MAX_SKB_FRAGS < 8 +#define IGB_RX_BUFSZ ALIGN(MAX_JUMBO_FRAME_SIZE / MAX_SKB_FRAGS, 1024) +#else +#define IGB_RX_BUFSZ IGB_RXBUFFER_2048 +#endif + + +/* Packet Buffer allocations */ +#define IGB_PBA_BYTES_SHIFT 0xA +#define IGB_TX_HEAD_ADDR_SHIFT 7 +#define IGB_PBA_TX_MASK 0xFFFF0000 + +#define IGB_FC_PAUSE_TIME 0x0680 /* 858 usec */ + +/* How many Rx Buffers do we bundle into one write to the hardware ? */ +#define IGB_RX_BUFFER_WRITE 16 /* Must be power of 2 */ + +#define IGB_EEPROM_APME 0x0400 +#define AUTO_ALL_MODES 0 + +#ifndef IGB_MASTER_SLAVE +/* Switch to override PHY master/slave setting */ +#define IGB_MASTER_SLAVE e1000_ms_hw_default +#endif + +#define IGB_MNG_VLAN_NONE -1 + +#ifndef IGB_NO_LRO +#define IGB_LRO_MAX 32 /*Maximum number of LRO descriptors*/ +struct igb_lro_stats { + u32 flushed; + u32 coal; +}; + +/* + * igb_lro_header - header format to be aggregated by LRO + * @iph: IP header without options + * @tcp: TCP header + * @ts: Optional TCP timestamp data in TCP options + * + * This structure relies on the check above that verifies that the header + * is IPv4 and does not contain any options. + */ +struct igb_lrohdr { + struct iphdr iph; + struct tcphdr th; + __be32 ts[0]; +}; + +struct igb_lro_list { + struct sk_buff_head active; + struct igb_lro_stats stats; +}; + +#endif /* IGB_NO_LRO */ +struct igb_cb { +#ifndef IGB_NO_LRO +#ifdef CONFIG_IGB_DISABLE_PACKET_SPLIT + union { /* Union defining head/tail partner */ + struct sk_buff *head; + struct sk_buff *tail; + }; +#endif + __be32 tsecr; /* timestamp echo response */ + u32 tsval; /* timestamp value in host order */ + u32 next_seq; /* next expected sequence number */ + u16 free; /* 65521 minus total size */ + u16 mss; /* size of data portion of packet */ + u16 append_cnt; /* number of skb's appended */ +#endif /* IGB_NO_LRO */ +#ifdef HAVE_VLAN_RX_REGISTER + u16 vid; /* VLAN tag */ +#endif +}; +#define IGB_CB(skb) ((struct igb_cb *)(skb)->cb) + +enum igb_tx_flags { + /* cmd_type flags */ + IGB_TX_FLAGS_VLAN = 0x01, + IGB_TX_FLAGS_TSO = 0x02, + IGB_TX_FLAGS_TSTAMP = 0x04, + + /* olinfo flags */ + IGB_TX_FLAGS_IPV4 = 0x10, + IGB_TX_FLAGS_CSUM = 0x20, +}; + +/* VLAN info */ +#define IGB_TX_FLAGS_VLAN_MASK 0xffff0000 +#define IGB_TX_FLAGS_VLAN_SHIFT 16 + +/* + * The largest size we can write to the descriptor is 65535. In order to + * maintain a power of two alignment we have to limit ourselves to 32K. + */ +#define IGB_MAX_TXD_PWR 15 +#define IGB_MAX_DATA_PER_TXD (1 << IGB_MAX_TXD_PWR) + +/* Tx Descriptors needed, worst case */ +#define TXD_USE_COUNT(S) DIV_ROUND_UP((S), IGB_MAX_DATA_PER_TXD) +#ifndef MAX_SKB_FRAGS +#define DESC_NEEDED 4 +#elif (MAX_SKB_FRAGS < 16) +#define DESC_NEEDED ((MAX_SKB_FRAGS * TXD_USE_COUNT(PAGE_SIZE)) + 4) +#else +#define DESC_NEEDED (MAX_SKB_FRAGS + 4) +#endif + +/* wrapper around a pointer to a socket buffer, + * so a DMA handle can be stored along with the buffer */ +struct igb_tx_buffer { + union e1000_adv_tx_desc *next_to_watch; + unsigned long time_stamp; + struct sk_buff *skb; + unsigned int bytecount; + u16 gso_segs; + __be16 protocol; + + DEFINE_DMA_UNMAP_ADDR(dma); + DEFINE_DMA_UNMAP_LEN(len); + u32 tx_flags; +}; + +struct igb_rx_buffer { + dma_addr_t dma; +#ifdef CONFIG_IGB_DISABLE_PACKET_SPLIT + struct sk_buff *skb; +#else + struct page *page; + u32 page_offset; +#endif +}; + +struct igb_tx_queue_stats { + u64 packets; + u64 bytes; + u64 restart_queue; +}; + +struct igb_rx_queue_stats { + u64 packets; + u64 bytes; + u64 drops; + u64 csum_err; + u64 alloc_failed; +}; + +struct igb_rx_packet_stats { + u64 ipv4_packets; /* IPv4 headers processed */ + u64 ipv4e_packets; /* IPv4E headers with extensions processed */ + u64 ipv6_packets; /* IPv6 headers processed */ + u64 ipv6e_packets; /* IPv6E headers with extensions processed */ + u64 tcp_packets; /* TCP headers processed */ + u64 udp_packets; /* UDP headers processed */ + u64 sctp_packets; /* SCTP headers processed */ + u64 nfs_packets; /* NFS headers processe */ + u64 other_packets; +}; + +struct igb_ring_container { + struct igb_ring *ring; /* pointer to linked list of rings */ + unsigned int total_bytes; /* total bytes processed this int */ + unsigned int total_packets; /* total packets processed this int */ + u16 work_limit; /* total work allowed per interrupt */ + u8 count; /* total number of rings in vector */ + u8 itr; /* current ITR setting for ring */ +}; + +struct igb_ring { + struct igb_q_vector *q_vector; /* backlink to q_vector */ + struct net_device *netdev; /* back pointer to net_device */ + struct device *dev; /* device for dma mapping */ + union { /* array of buffer info structs */ + struct igb_tx_buffer *tx_buffer_info; + struct igb_rx_buffer *rx_buffer_info; + }; + void *desc; /* descriptor ring memory */ + unsigned long flags; /* ring specific flags */ + void __iomem *tail; /* pointer to ring tail register */ + dma_addr_t dma; /* phys address of the ring */ + unsigned int size; /* length of desc. ring in bytes */ + + u16 count; /* number of desc. in the ring */ + u8 queue_index; /* logical index of the ring*/ + u8 reg_idx; /* physical index of the ring */ + + /* everything past this point are written often */ + u16 next_to_clean; + u16 next_to_use; + u16 next_to_alloc; + + union { + /* TX */ + struct { + struct igb_tx_queue_stats tx_stats; + }; + /* RX */ + struct { + struct igb_rx_queue_stats rx_stats; + struct igb_rx_packet_stats pkt_stats; +#ifdef CONFIG_IGB_DISABLE_PACKET_SPLIT + u16 rx_buffer_len; +#else + struct sk_buff *skb; +#endif + }; + }; +#ifdef CONFIG_IGB_VMDQ_NETDEV + struct net_device *vmdq_netdev; + int vqueue_index; /* queue index for virtual netdev */ +#endif +} ____cacheline_internodealigned_in_smp; + +struct igb_q_vector { + struct igb_adapter *adapter; /* backlink */ + int cpu; /* CPU for DCA */ + u32 eims_value; /* EIMS mask value */ + + u16 itr_val; + u8 set_itr; + void __iomem *itr_register; + + struct igb_ring_container rx, tx; + + struct napi_struct napi; +#ifndef IGB_NO_LRO + struct igb_lro_list lrolist; /* LRO list for queue vector*/ +#endif + struct rcu_head rcu; /* to avoid race with update stats on free */ + char name[IFNAMSIZ + 9]; +#ifndef HAVE_NETDEV_NAPI_LIST + struct net_device poll_dev; +#endif + + /* for dynamic allocation of rings associated with this q_vector */ + struct igb_ring ring[0] ____cacheline_internodealigned_in_smp; +}; + +enum e1000_ring_flags_t { +#ifndef HAVE_NDO_SET_FEATURES + IGB_RING_FLAG_RX_CSUM, +#endif + IGB_RING_FLAG_RX_SCTP_CSUM, + IGB_RING_FLAG_RX_LB_VLAN_BSWAP, + IGB_RING_FLAG_TX_CTX_IDX, + IGB_RING_FLAG_TX_DETECT_HANG, +}; + +struct igb_mac_addr { + u8 addr[ETH_ALEN]; + u16 queue; + u16 state; /* bitmask */ +}; +#define IGB_MAC_STATE_DEFAULT 0x1 +#define IGB_MAC_STATE_MODIFIED 0x2 +#define IGB_MAC_STATE_IN_USE 0x4 + +#define IGB_TXD_DCMD (E1000_ADVTXD_DCMD_EOP | E1000_ADVTXD_DCMD_RS) + +#define IGB_RX_DESC(R, i) \ + (&(((union e1000_adv_rx_desc *)((R)->desc))[i])) +#define IGB_TX_DESC(R, i) \ + (&(((union e1000_adv_tx_desc *)((R)->desc))[i])) +#define IGB_TX_CTXTDESC(R, i) \ + (&(((struct e1000_adv_tx_context_desc *)((R)->desc))[i])) + +#ifdef CONFIG_IGB_VMDQ_NETDEV +#define netdev_ring(ring) \ + ((ring->vmdq_netdev ? ring->vmdq_netdev : ring->netdev)) +#define ring_queue_index(ring) \ + ((ring->vmdq_netdev ? ring->vqueue_index : ring->queue_index)) +#else +#define netdev_ring(ring) (ring->netdev) +#define ring_queue_index(ring) (ring->queue_index) +#endif /* CONFIG_IGB_VMDQ_NETDEV */ + +/* igb_test_staterr - tests bits within Rx descriptor status and error fields */ +static inline __le32 igb_test_staterr(union e1000_adv_rx_desc *rx_desc, + const u32 stat_err_bits) +{ + return rx_desc->wb.upper.status_error & cpu_to_le32(stat_err_bits); +} + +/* igb_desc_unused - calculate if we have unused descriptors */ +static inline u16 igb_desc_unused(const struct igb_ring *ring) +{ + u16 ntc = ring->next_to_clean; + u16 ntu = ring->next_to_use; + + return ((ntc > ntu) ? 0 : ring->count) + ntc - ntu - 1; +} + +#ifdef CONFIG_BQL +static inline struct netdev_queue *txring_txq(const struct igb_ring *tx_ring) +{ + return netdev_get_tx_queue(tx_ring->netdev, tx_ring->queue_index); +} +#endif /* CONFIG_BQL */ + +struct igb_therm_proc_data { + struct e1000_hw *hw; + struct e1000_thermal_diode_data *sensor_data; +}; + +#ifdef IGB_HWMON +#define IGB_HWMON_TYPE_LOC 0 +#define IGB_HWMON_TYPE_TEMP 1 +#define IGB_HWMON_TYPE_CAUTION 2 +#define IGB_HWMON_TYPE_MAX 3 + +struct hwmon_attr { + struct device_attribute dev_attr; + struct e1000_hw *hw; + struct e1000_thermal_diode_data *sensor; + char name[12]; + }; + +struct hwmon_buff { + struct device *device; + struct hwmon_attr *hwmon_list; + unsigned int n_hwmon; + }; +#endif /* IGB_HWMON */ +#define IGB_N_EXTTS 2 +#define IGB_N_PEROUT 2 +#define IGB_N_SDP 4 +#ifdef ETHTOOL_GRXFHINDIR +#define IGB_RETA_SIZE 128 +#endif /* ETHTOOL_GRXFHINDIR */ + +/* board specific private data structure */ +struct igb_adapter { +#ifdef HAVE_VLAN_RX_REGISTER + /* vlgrp must be first member of structure */ + struct vlan_group *vlgrp; +#else + unsigned long active_vlans[BITS_TO_LONGS(VLAN_N_VID)]; +#endif + struct net_device *netdev; + + unsigned long state; + unsigned int flags; + + unsigned int num_q_vectors; + struct msix_entry *msix_entries; + + + /* TX */ + u16 tx_work_limit; + u32 tx_timeout_count; + int num_tx_queues; + struct igb_ring *tx_ring[IGB_MAX_TX_QUEUES]; + + /* RX */ + int num_rx_queues; + struct igb_ring *rx_ring[IGB_MAX_RX_QUEUES]; + + struct timer_list watchdog_timer; + struct timer_list dma_err_timer; + struct timer_list phy_info_timer; + u16 mng_vlan_id; + u32 bd_number; + u32 wol; + u32 en_mng_pt; + u16 link_speed; + u16 link_duplex; + u8 port_num; + + /* Interrupt Throttle Rate */ + u32 rx_itr_setting; + u32 tx_itr_setting; + + struct work_struct reset_task; + struct work_struct watchdog_task; + struct work_struct dma_err_task; + bool fc_autoneg; + u8 tx_timeout_factor; + +#ifdef DEBUG + bool tx_hang_detected; + bool disable_hw_reset; +#endif + u32 max_frame_size; + + /* OS defined structs */ + struct pci_dev *pdev; + /* user-dma specific variables */ + u32 uring_tx_init; + u32 uring_rx_init; +#ifndef HAVE_NETDEV_STATS_IN_NETDEV + struct net_device_stats net_stats; +#endif +#ifndef IGB_NO_LRO + struct igb_lro_stats lro_stats; +#endif + + /* structs defined in e1000_hw.h */ + struct e1000_hw hw; + struct e1000_hw_stats stats; + struct e1000_phy_info phy_info; + struct e1000_phy_stats phy_stats; + +#ifdef ETHTOOL_TEST + u32 test_icr; + struct igb_ring test_tx_ring; + struct igb_ring test_rx_ring; +#endif + + int msg_enable; + + struct igb_q_vector *q_vector[MAX_Q_VECTORS]; + u32 eims_enable_mask; + u32 eims_other; + + /* to not mess up cache alignment, always add to the bottom */ + u32 *config_space; + u16 tx_ring_count; + u16 rx_ring_count; + struct vf_data_storage *vf_data; +#ifdef IFLA_VF_MAX + int vf_rate_link_speed; +#endif + u32 lli_port; + u32 lli_size; + unsigned int vfs_allocated_count; + /* Malicious Driver Detection flag. Valid only when SR-IOV is enabled */ + bool mdd; + int int_mode; + u32 rss_queues; + u32 tss_queues; + u32 vmdq_pools; + char fw_version[32]; + u32 wvbr; + struct igb_mac_addr *mac_table; +#ifdef CONFIG_IGB_VMDQ_NETDEV + struct net_device *vmdq_netdev[IGB_MAX_VMDQ_QUEUES]; +#endif + int vferr_refcount; + int dmac; + u32 *shadow_vfta; + + /* External Thermal Sensor support flag */ + bool ets; +#ifdef IGB_HWMON + struct hwmon_buff igb_hwmon_buff; +#else /* IGB_HWMON */ +#ifdef IGB_PROCFS + struct proc_dir_entry *eth_dir; + struct proc_dir_entry *info_dir; + struct proc_dir_entry *therm_dir[E1000_MAX_SENSORS]; + struct igb_therm_proc_data therm_data[E1000_MAX_SENSORS]; + bool old_lsc; +#endif /* IGB_PROCFS */ +#endif /* IGB_HWMON */ + u32 etrack_id; + +#ifdef HAVE_PTP_1588_CLOCK + struct ptp_clock *ptp_clock; + struct ptp_clock_info ptp_caps; + struct delayed_work ptp_overflow_work; + struct work_struct ptp_tx_work; + struct sk_buff *ptp_tx_skb; + struct hwtstamp_config tstamp_config; + unsigned long ptp_tx_start; + unsigned long last_rx_ptp_check; + unsigned long last_rx_timestamp; + spinlock_t tmreg_lock; + struct cyclecounter cc; + struct timecounter tc; + u32 tx_hwtstamp_timeouts; + u32 rx_hwtstamp_cleared; + +#ifdef HAVE_PTP_1588_CLOCK_PINS + struct ptp_pin_desc sdp_config[IGB_N_SDP]; +#endif /* HAVE_PTP_1588_CLOCK_PINS */ + struct { + struct timespec64 start; + struct timespec64 period; + } perout[IGB_N_PEROUT]; +#endif /* HAVE_PTP_1588_CLOCK */ + +#ifdef HAVE_I2C_SUPPORT + struct i2c_algo_bit_data i2c_algo; + struct i2c_adapter i2c_adap; + struct i2c_client *i2c_client; +#endif /* HAVE_I2C_SUPPORT */ + unsigned long link_check_timeout; + + int devrc; + + int copper_tries; + u16 eee_advert; +#ifdef ETHTOOL_GRXFHINDIR + u32 rss_indir_tbl_init; + u8 rss_indir_tbl[IGB_RETA_SIZE]; +#endif + struct mutex lock; +}; + +#ifdef CONFIG_IGB_VMDQ_NETDEV +struct igb_vmdq_adapter { +#ifdef HAVE_VLAN_RX_REGISTER + /* vlgrp must be first member of structure */ + struct vlan_group *vlgrp; +#else + unsigned long active_vlans[BITS_TO_LONGS(VLAN_N_VID)]; +#endif + struct igb_adapter *real_adapter; + struct net_device *vnetdev; + struct net_device_stats net_stats; + struct igb_ring *tx_ring; + struct igb_ring *rx_ring; +}; +#endif + +#define IGB_FLAG_HAS_MSI (1 << 0) +#define IGB_FLAG_DCA_ENABLED (1 << 1) +#define IGB_FLAG_LLI_PUSH (1 << 2) +#define IGB_FLAG_QUAD_PORT_A (1 << 3) +#define IGB_FLAG_QUEUE_PAIRS (1 << 4) +#define IGB_FLAG_EEE (1 << 5) +#define IGB_FLAG_DMAC (1 << 6) +#define IGB_FLAG_DETECT_BAD_DMA (1 << 7) +#define IGB_FLAG_PTP (1 << 8) +#define IGB_FLAG_RSS_FIELD_IPV4_UDP (1 << 9) +#define IGB_FLAG_RSS_FIELD_IPV6_UDP (1 << 10) +#define IGB_FLAG_WOL_SUPPORTED (1 << 11) +#define IGB_FLAG_NEED_LINK_UPDATE (1 << 12) +#define IGB_FLAG_LOOPBACK_ENABLE (1 << 13) +#define IGB_FLAG_MEDIA_RESET (1 << 14) +#define IGB_FLAG_MAS_ENABLE (1 << 15) + +/* Media Auto Sense */ +#define IGB_MAS_ENABLE_0 0X0001 +#define IGB_MAS_ENABLE_1 0X0002 +#define IGB_MAS_ENABLE_2 0X0004 +#define IGB_MAS_ENABLE_3 0X0008 + +#define IGB_MIN_TXPBSIZE 20408 +#define IGB_TX_BUF_4096 4096 + +#define IGB_DMCTLX_DCFLUSH_DIS 0x80000000 /* Disable DMA Coal Flush */ + +/* DMA Coalescing defines */ +#define IGB_DMAC_DISABLE 0 +#define IGB_DMAC_MIN 250 +#define IGB_DMAC_500 500 +#define IGB_DMAC_EN_DEFAULT 1000 +#define IGB_DMAC_2000 2000 +#define IGB_DMAC_3000 3000 +#define IGB_DMAC_4000 4000 +#define IGB_DMAC_5000 5000 +#define IGB_DMAC_6000 6000 +#define IGB_DMAC_7000 7000 +#define IGB_DMAC_8000 8000 +#define IGB_DMAC_9000 9000 +#define IGB_DMAC_MAX 10000 + +#define IGB_82576_TSYNC_SHIFT 19 +#define IGB_82580_TSYNC_SHIFT 24 +#define IGB_TS_HDR_LEN 16 + +/* CEM Support */ +#define FW_HDR_LEN 0x4 +#define FW_CMD_DRV_INFO 0xDD +#define FW_CMD_DRV_INFO_LEN 0x5 +#define FW_CMD_RESERVED 0X0 +#define FW_RESP_SUCCESS 0x1 +#define FW_UNUSED_VER 0x0 +#define FW_MAX_RETRIES 3 +#define FW_STATUS_SUCCESS 0x1 +#define FW_FAMILY_DRV_VER 0Xffffffff + +#define IGB_MAX_LINK_TRIES 20 + +struct e1000_fw_hdr { + u8 cmd; + u8 buf_len; + union { + u8 cmd_resv; + u8 ret_status; + } cmd_or_resp; + u8 checksum; +}; + +#pragma pack(push, 1) +struct e1000_fw_drv_info { + struct e1000_fw_hdr hdr; + u8 port_num; + u32 drv_version; + u16 pad; /* end spacing to ensure length is mult. of dword */ + u8 pad2; /* end spacing to ensure length is mult. of dword2 */ +}; +#pragma pack(pop) + +enum e1000_state_t { + __IGB_TESTING, + __IGB_RESETTING, + __IGB_DOWN, + __IGB_PTP_TX_IN_PROGRESS, +}; + +extern char igb_driver_name[]; +extern char igb_driver_version[]; + +extern void igb_up(struct igb_adapter *); +extern void igb_down(struct igb_adapter *); +extern void igb_reinit_locked(struct igb_adapter *); +extern void igb_reset(struct igb_adapter *); +#ifdef ETHTOOL_SRXFHINDIR +extern void igb_write_rss_indir_tbl(struct igb_adapter *); +#endif +extern int igb_set_spd_dplx(struct igb_adapter *, u16); +extern int igb_setup_tx_resources(struct igb_ring *); +extern int igb_setup_rx_resources(struct igb_ring *); +extern void igb_free_tx_resources(struct igb_ring *); +extern void igb_free_rx_resources(struct igb_ring *); +extern void igb_configure_tx_ring(struct igb_adapter *, struct igb_ring *); +extern void igb_configure_rx_ring(struct igb_adapter *, struct igb_ring *); +extern void igb_setup_tctl(struct igb_adapter *); +extern void igb_setup_rctl(struct igb_adapter *); +extern netdev_tx_t igb_xmit_frame_ring(struct sk_buff *, struct igb_ring *); +extern void igb_unmap_and_free_tx_resource(struct igb_ring *, + struct igb_tx_buffer *); +extern void igb_alloc_rx_buffers(struct igb_ring *, u16); +extern void igb_clean_rx_ring(struct igb_ring *); +extern int igb_setup_queues(struct igb_adapter *adapter); +extern void igb_update_stats(struct igb_adapter *); +extern bool igb_has_link(struct igb_adapter *adapter); +extern void igb_set_ethtool_ops(struct net_device *); +extern void igb_check_options(struct igb_adapter *); +extern void igb_power_up_link(struct igb_adapter *); +#ifdef HAVE_PTP_1588_CLOCK +extern void igb_ptp_init(struct igb_adapter *adapter); +extern void igb_ptp_stop(struct igb_adapter *adapter); +extern void igb_ptp_reset(struct igb_adapter *adapter); +extern void igb_ptp_tx_work(struct work_struct *work); +extern void igb_ptp_rx_hang(struct igb_adapter *adapter); +extern void igb_ptp_tx_hwtstamp(struct igb_adapter *adapter); +extern void igb_ptp_rx_rgtstamp(struct igb_q_vector *q_vector, + struct sk_buff *skb); +extern void igb_ptp_rx_pktstamp(struct igb_q_vector *q_vector, + unsigned char *va, + struct sk_buff *skb); +extern int igb_ptp_hwtstamp_ioctl(struct net_device *netdev, + struct ifreq *ifr, int cmd); +#endif /* HAVE_PTP_1588_CLOCK */ +#ifdef ETHTOOL_OPS_COMPAT +extern int ethtool_ioctl(struct ifreq *); +#endif +extern int igb_write_mc_addr_list(struct net_device *netdev); +extern int igb_add_mac_filter(struct igb_adapter *adapter, u8 *addr, u16 queue); +extern int igb_del_mac_filter(struct igb_adapter *adapter, u8 *addr, u16 queue); +extern int igb_available_rars(struct igb_adapter *adapter); +extern s32 igb_vlvf_set(struct igb_adapter *, u32, bool, u32); +extern void igb_configure_vt_default_pool(struct igb_adapter *adapter); +extern void igb_enable_vlan_tags(struct igb_adapter *adapter); +#ifndef HAVE_VLAN_RX_REGISTER +extern void igb_vlan_mode(struct net_device *, u32); +#endif + +#define E1000_PCS_CFG_IGN_SD 1 + +int igb_ptp_set_ts_config(struct net_device *netdev, struct ifreq *ifr); +int igb_ptp_get_ts_config(struct net_device *netdev, struct ifreq *ifr); +#ifdef IGB_HWMON +void igb_sysfs_exit(struct igb_adapter *adapter); +int igb_sysfs_init(struct igb_adapter *adapter); +#else +#ifdef IGB_PROCFS +int igb_procfs_init(struct igb_adapter *adapter); +void igb_procfs_exit(struct igb_adapter *adapter); +int igb_procfs_topdir_init(void); +void igb_procfs_topdir_exit(void); +#endif /* IGB_PROCFS */ +#endif /* IGB_HWMON */ + +#define IGB_BIND _IOW('E', 200, int) +#define IGB_UNBIND _IOW('E', 201, int) +#define IGB_MAPRING _IOW('E', 202, int) +#define IGB_MAP_TX_RING IGB_MAPRING +#define IGB_UNMAPRING _IOW('E', 203, int) +#define IGB_UNMAP_TX_RING IGB_UNMAPRING +#define IGB_MAPBUF _IOW('E', 204, int) +#define IGB_UNMAPBUF _IOW('E', 205, int) +#define IGB_LINKSPEED _IOW('E', 206, int) +#define IGB_MAP_RX_RING _IOW('E', 207, int) +#define IGB_UNMAP_RX_RING _IOW('E', 208, int) + +/*set of newly defined ioctl calls - new libigb compatibility + each of them is an equivalent of the old ioctl + changed numberiong convention: new_ioctl = old_ioctl + 100*/ + +#define IGB_IOCTL_MAPRING _IOW('E', 302, int) +#define IGB_IOCTL_MAP_TX_RING IGB_IOCTL_MAPRING +#define IGB_IOCTL_UNMAPRING _IOW('E', 303, int) +#define IGB_IOCTL_UNMAP_TX_RING IGB_IOCTL_UNMAPRING +#define IGB_IOCTL_MAPBUF _IOW('E', 304, int) +#define IGB_IOCTL_UNMAPBUF _IOW('E', 305, int) +#define IGB_IOCTL_MAP_RX_RING _IOW('E', 307, int) +#define IGB_IOCTL_UNMAP_RX_RING _IOW('E', 308, int) + + +/*END*/ + +#define IGB_BIND_NAMESZ 24 + +struct igb_bind_cmd { + char iface[IGB_BIND_NAMESZ]; + u32 mmap_size; +}; + +struct igb_pci_lookup { + struct igb_adapter *adapter; + char *pci_info; +}; + +/* used with both map/unmap ring & buf ioctls */ +struct igb_buf_cmd { + u64 physaddr; + u32 queue; + u32 mmap_size; + u64 pa; +}; + +struct igb_link_cmd { + u32 up; + u32 speed; + u32 duplex; +}; + +struct igb_private_data { + struct igb_adapter *adapter; + /* user-dma specific variable for buffer */ + struct igb_user_page *userpages; + /* user-dma specific variable for TX and RX */ + u32 uring_tx_init; + u32 uring_rx_init; +}; + +#endif /* _IGB_H_ */ diff --git a/drivers/staging/igb_avb/igb_avb.7 b/drivers/staging/igb_avb/igb_avb.7 new file mode 100755 index 000000000000..ed8dba1036ad --- /dev/null +++ b/drivers/staging/igb_avb/igb_avb.7 @@ -0,0 +1,253 @@ +.\" LICENSE +.\" +.\" This software program is released under the terms of a license agreement between you ('Licensee') and Intel. Do not use or load this software or any associated materials (collectively, the 'Software') until you have carefully read the full terms and conditions of the LICENSE located in this software package. By loading or using the Software, you agree to the terms of this Agreement. If you do not agree with the terms of this Agreement, do not install or use the Software. +.\" +.\" * Other names and brands may be claimed as the property of others. +.\" +.TH igb 1 "January 5, 2012" + +.SH NAME +igb \-This file describes the Linux* Base Driver for the Gigabit Family of Adapters. +.SH SYNOPSIS +.PD 0.4v +modprobe igb [