diff --git a/drivers/misc/mods/mods_acpi.c b/drivers/misc/mods/mods_acpi.c index 0d914482..e43ae78e 100644 --- a/drivers/misc/mods/mods_acpi.c +++ b/drivers/misc/mods/mods_acpi.c @@ -2,7 +2,7 @@ /* * mods_acpi.c - This file is part of NVIDIA MODS kernel driver. * - * Copyright (c) 2008-2020, NVIDIA CORPORATION. All rights reserved. + * Copyright (c) 2008-2022, NVIDIA CORPORATION. All rights reserved. * * NVIDIA MODS kernel driver is free software: you can redistribute it and/or * modify it under the terms of the GNU General Public License, @@ -34,6 +34,105 @@ static acpi_status mods_acpi_find_acpi_handler(acpi_handle, * PRIVATE FUNCTIONS * *********************/ +struct acpi_dev_children_in_data { + u32 expected_acpi_ids_buf[4]; + u32 num_expected_acpi_ids; + u32 acpi_dev_mask; + acpi_handle dev_handle; +}; + +typedef int (*dev_children_fptr)(struct mods_client *, + u32, + acpi_handle, + void *); + +static int acpi_store_dev_children(struct mods_client *client, + u32 curr_acpi_id, + acpi_handle dev_handle, + void *out_data) +{ + struct MODS_GET_ACPI_DEV_CHILDREN *acpi_out_data + = (struct MODS_GET_ACPI_DEV_CHILDREN *)out_data; + + if (acpi_out_data->num_children >= ACPI_MAX_DEV_CHILDREN) { + cl_error("ACPI: output buffer too small to store all children\n"); + return -ENOBUFS; + } + + acpi_out_data->children[acpi_out_data->num_children] = curr_acpi_id; + ++acpi_out_data->num_children; + + return OK; +} + +static int acpi_compare_dev_id(struct mods_client *client, + u32 curr_acpi_id, + acpi_handle dev_handle, + void *out_data) +{ + u32 i; + struct acpi_dev_children_in_data *dev_child_data + = (struct acpi_dev_children_in_data *)out_data; + + for (i = 0; i < dev_child_data->num_expected_acpi_ids; ++i) { + if ((dev_child_data->expected_acpi_ids_buf[i] & dev_child_data->acpi_dev_mask) + == (curr_acpi_id & dev_child_data->acpi_dev_mask)) { + dev_child_data->dev_handle = dev_handle; + /* + * returning unique error code to signal caller that value is found + */ + return -EALREADY; + } + } + return -ENODEV; +} + +static int acpi_get_dev_children(struct mods_client *client, + dev_children_fptr fptr, + acpi_handle dev_handle, + void *out_data) +{ + int err = OK; + acpi_status status; + struct acpi_device *device = NULL; + struct list_head *node = NULL; + struct list_head *next = NULL; + + LOG_ENT(); + + status = acpi_bus_get_device(dev_handle, &device); + if (ACPI_FAILURE(status) || !device) { + cl_error("ACPI: device for fetching device children not found\n"); + LOG_EXT(); + return -EINVAL; + } + + list_for_each_safe(node, next, &device->children) { + unsigned long long device_id = 0; + + struct acpi_device *dev = + list_entry(node, struct acpi_device, node); + + status = acpi_evaluate_integer(dev->handle, + "_ADR", + NULL, + &device_id); + if (ACPI_FAILURE(status)) + /* Couldnt query device_id for this device */ + continue; + + err = fptr(client, device_id, dev->handle, out_data); + if (err == -EALREADY) { + err = OK; + break; + } + } + + LOG_EXT(); + return err; +} + + /* store handle if found. */ static void mods_acpi_handle_init(struct mods_client *client, char *method_name, @@ -187,9 +286,6 @@ static int mods_eval_acpi_method(struct mods_client *client, union acpi_object acpi_params[ACPI_MAX_ARGUMENT_NUMBER]; acpi_handle acpi_method_handler = NULL; struct pci_dev *dev = NULL; - struct acpi_device *acpi_dev = NULL; - struct list_head *node = NULL; - struct list_head *next = NULL; LOG_ENT(); @@ -229,46 +325,20 @@ static int mods_eval_acpi_method(struct mods_client *client, } if (acpi_id != ACPI_MODS_IGNORE_ACPI_ID) { - status = acpi_bus_get_device(acpi_method_handler, &acpi_dev); - if (ACPI_FAILURE(status) || !acpi_dev) { - cl_error("ACPI: device for %s not found\n", - p->method_name); + struct acpi_dev_children_in_data in_data = { {}, 0, 0, NULL}; + + in_data.expected_acpi_ids_buf[0] = acpi_id; + in_data.num_expected_acpi_ids = 1; + in_data.acpi_dev_mask = 0xffffffff; + + err = acpi_get_dev_children(client, acpi_compare_dev_id, + acpi_method_handler, (void *)&in_data); + if (err) { pci_dev_put(dev); LOG_EXT(); return -EINVAL; } - acpi_method_handler = NULL; - - list_for_each_safe(node, next, &acpi_dev->children) { - unsigned long long device_id = 0; - - struct acpi_device *acpi_dev = - list_entry(node, struct acpi_device, node); - - if (!acpi_dev) - continue; - - status = acpi_evaluate_integer(acpi_dev->handle, - "_ADR", - NULL, - &device_id); - if (ACPI_FAILURE(status)) - /* Couldn't query device_id for this device */ - continue; - - if (device_id == acpi_id) { - acpi_method_handler = acpi_dev->handle; - cl_debug(DEBUG_ACPI, - "ACPI: found %s (id = 0x%x) on dev %04x:%02x:%02x.%x\n", - p->method_name, - (unsigned int)device_id, - pdevice->domain, - pdevice->bus, - pdevice->device, - pdevice->function); - break; - } - } + acpi_method_handler = in_data.dev_handle; } if (!acpi_method_handler) { @@ -279,6 +349,14 @@ static int mods_eval_acpi_method(struct mods_client *client, LOG_EXT(); return -EINVAL; } + cl_debug(DEBUG_ACPI, + "ACPI: found %s (id = 0x%x) on dev %04x:%02x:%02x.%x\n", + p->method_name, + (unsigned int)acpi_id, + pdevice ? pdevice->domain : 0U, + pdevice ? pdevice->bus : 0U, + pdevice ? pdevice->device : 0U, + pdevice ? pdevice->function : 0U); input.count = p->argument_count; input.pointer = acpi_params; @@ -368,19 +446,17 @@ static int mods_acpi_get_ddc(struct mods_client *client, struct MODS_ACPI_GET_DDC_2 *p, struct mods_pci_dev_2 *pci_device) { - int err; - acpi_status status; - struct acpi_device *device = NULL; - struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL }; - union acpi_object *ddc; - union acpi_object ddc_arg0 = { ACPI_TYPE_INTEGER }; - struct acpi_object_list input = { 1, &ddc_arg0 }; - struct list_head *node; - struct list_head *next; - u32 i; - acpi_handle dev_handle = NULL; - acpi_handle lcd_dev_handle = NULL; - struct pci_dev *dev = NULL; + int err; + acpi_status status; + union acpi_object *ddc; + union acpi_object ddc_arg0 = { ACPI_TYPE_INTEGER }; + struct acpi_object_list input = { 1, &ddc_arg0 }; + u32 i; + acpi_handle dev_handle = NULL; + acpi_handle lcd_dev_handle = NULL; + struct pci_dev *dev = NULL; + bool data_found = false; + struct acpi_dev_children_in_data in_data = { {}, 0, 0, NULL }; LOG_ENT(); @@ -411,49 +487,26 @@ static int mods_acpi_get_ddc(struct mods_client *client, return -EINVAL; } - status = acpi_bus_get_device(dev_handle, &device); - if (ACPI_FAILURE(status) || !device) { - cl_error("ACPI: device for _DDC not found\n"); + /* + * List of supported display's (panels) + * Reference: https://uefi.org/sites/default/files/resources/ACPI_6_1.pdf + * Section: Table B-390 Video Output Device Attributes + */ + in_data.expected_acpi_ids_buf[0] = 0x0110; + in_data.expected_acpi_ids_buf[1] = 0x0118; + in_data.expected_acpi_ids_buf[2] = 0x0400; + in_data.expected_acpi_ids_buf[3] = 0xa450; + in_data.num_expected_acpi_ids = 4; + in_data.acpi_dev_mask = 0xffff; + + err = acpi_get_dev_children(client, acpi_compare_dev_id, + dev_handle, (void *)&in_data); + if (err) { pci_dev_put(dev); LOG_EXT(); return -EINVAL; } - - list_for_each_safe(node, next, &device->children) { - unsigned long long device_id = 0; - - struct acpi_device *dev = - list_entry(node, struct acpi_device, node); - - if (!dev) - continue; - - status = acpi_evaluate_integer(dev->handle, - "_ADR", - NULL, - &device_id); - if (ACPI_FAILURE(status)) - /* Couldnt query device_id for this device */ - continue; - - device_id = (device_id & 0xffff); - - if ((device_id == 0x0110) || /* Only for an LCD*/ - (device_id == 0x0118) || - (device_id == 0x0400)) { - - lcd_dev_handle = dev->handle; - cl_debug(DEBUG_ACPI, - "ACPI: Found LCD 0x%llx on dev %04x:%02x:%02x.%x\n", - device_id, - p->device.domain, - p->device.bus, - p->device.device, - p->device.function); - break; - } - - } + lcd_dev_handle = in_data.dev_handle; if (lcd_dev_handle == NULL) { cl_error("ACPI: LCD not found for dev %04x:%02x:%02x.%x\n", @@ -466,49 +519,54 @@ static int mods_acpi_get_ddc(struct mods_client *client, return -EINVAL; } + cl_debug(DEBUG_ACPI, + "ACPI: Found LCD on dev %04x:%02x:%02x.%x\n", + p->device.domain, + p->device.bus, + p->device.device, + p->device.function); + /* * As per ACPI Spec 3.0: - * ARG0 = 0x1 for 128 bytes EDID buffer - * ARG0 = 0x2 for 256 bytes EDID buffer + * ARG0 is size of EDID buffer in 128-byte blocks. */ - for (i = 1; i <= 2; i++) { + err = -EINVAL; + for (i = 4; i >= 1; --i) { + struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL }; + ddc_arg0.integer.value = i; status = acpi_evaluate_object(lcd_dev_handle, "_DDC", &input, &output); - if (ACPI_SUCCESS(status)) + if (ACPI_SUCCESS(status)) { + ddc = output.pointer; + if (ddc && (ddc->type != ACPI_TYPE_BUFFER)) + continue; + + if (!ddc || ddc->buffer.length == 0) { + cl_error("unsupported ACPI output type\n"); + } else if (ddc->buffer.length > sizeof(p->out_buffer)) { + cl_error( + "output buffer too small for ACPI method _DDC (EDID)\n"); + } else { + p->out_data_size = ddc->buffer.length; + memcpy(p->out_buffer, + ddc->buffer.pointer, + p->out_data_size); + err = OK; + data_found = true; + } + + kfree(ddc); + break; - } - - if (ACPI_FAILURE(status)) { - cl_error("ACPI method _DDC (EDID) failed\n"); - pci_dev_put(dev); - LOG_EXT(); - return -EINVAL; - } - - ddc = output.pointer; - if (ddc && (ddc->type == ACPI_TYPE_BUFFER) - && (ddc->buffer.length > 0)) { - - if (ddc->buffer.length <= sizeof(p->out_buffer)) { - p->out_data_size = ddc->buffer.length; - memcpy(p->out_buffer, - ddc->buffer.pointer, - p->out_data_size); - err = OK; - } else { - cl_error( - "output buffer too small for ACPI method _DDC (EDID)\n"); - err = -EINVAL; } - } else { - cl_error("unsupported ACPI output type\n"); - err = -EINVAL; } - kfree(output.pointer); + if (!data_found) + cl_error("ACPI method _DDC (EDID) failed\n"); + pci_dev_put(dev); LOG_EXT(); return err; @@ -574,3 +632,56 @@ int esc_mods_acpi_get_ddc(struct mods_client *client, return mods_acpi_get_ddc(client, pp, &device); } + +int esc_mods_get_acpi_dev_children(struct mods_client *client, + struct MODS_GET_ACPI_DEV_CHILDREN *p) +{ + int err; + struct pci_dev *dev = NULL; + acpi_handle dev_handle = NULL; + + LOG_ENT(); + + cl_debug(DEBUG_ACPI, + "ACPI: failed to get children for dev %04x:%02x:%02x.%x\n", + p->device.domain, + p->device.bus, + p->device.device, + p->device.function); + + err = mods_find_pci_dev(client, &p->device, &dev); + if (unlikely(err)) { + if (err == -ENODEV) + cl_error("ACPI: dev %04x:%02x:%02x.%x not found\n", + p->device.domain, + p->device.bus, + p->device.device, + p->device.function); + LOG_EXT(); + return err; + } + + dev_handle = MODS_ACPI_HANDLE(&dev->dev); + if (!dev_handle) { + cl_error("ACPI: handle for fetching device children not found\n"); + pci_dev_put(dev); + LOG_EXT(); + return -EINVAL; + } + + p->num_children = 0; + err = acpi_get_dev_children(client, acpi_store_dev_children, + dev_handle, (void *)p); + + if (err) { + cl_error("ACPI: failed to get children for dev %04x:%02x:%02x.%x\n", + p->device.domain, + p->device.bus, + p->device.device, + p->device.function); + } + + pci_dev_put(dev); + LOG_EXT(); + return err; +} diff --git a/drivers/misc/mods/mods_config.h b/drivers/misc/mods/mods_config.h index e2a9d340..4024f8b4 100644 --- a/drivers/misc/mods/mods_config.h +++ b/drivers/misc/mods/mods_config.h @@ -2,7 +2,7 @@ /* * mods_config.h - This file is part of NVIDIA MODS kernel driver. * - * Copyright (c) 2008-2021, NVIDIA CORPORATION. All rights reserved. + * Copyright (c) 2008-2022, NVIDIA CORPORATION. All rights reserved. * * NVIDIA MODS kernel driver is free software: you can redistribute it and/or * modify it under the terms of the GNU General Public License, @@ -100,4 +100,8 @@ # define MODS_HAS_TEGRA 1 #endif +#if defined(MODS_HAS_TEGRA) && KERNEL_VERSION(5, 1, 0) <= MODS_KERNEL_VERSION +# define MODS_ENABLE_BPMP_MRQ_API 1 +#endif + #endif /* _MODS_CONFIG_H_ */ diff --git a/drivers/misc/mods/mods_internal.h b/drivers/misc/mods/mods_internal.h index a69430ac..1243fb93 100644 --- a/drivers/misc/mods/mods_internal.h +++ b/drivers/misc/mods/mods_internal.h @@ -2,7 +2,7 @@ /* * mods_internal.h - This file is part of NVIDIA MODS kernel driver. * - * Copyright (c) 2008-2022, NVIDIA CORPORATION. All rights reserved. + * Copyright (c) 2008-2022, NVIDIA CORPORATION. All rights reserved. * * NVIDIA MODS kernel driver is free software: you can redistribute it and/or * modify it under the terms of the GNU General Public License, @@ -38,8 +38,7 @@ #include #endif -#if KERNEL_VERSION(5, 1, 0) <= LINUX_VERSION_CODE -#define MODS_ENABLE_BPMP_MRQ_API +#ifdef MODS_ENABLE_BPMP_MRQ_API #include #endif @@ -515,6 +514,8 @@ int esc_mods_acpi_get_ddc(struct mods_client *client, struct MODS_ACPI_GET_DDC *p); int esc_mods_acpi_get_ddc_2(struct mods_client *client, struct MODS_ACPI_GET_DDC_2 *p); +int esc_mods_get_acpi_dev_children(struct mods_client *client, + struct MODS_GET_ACPI_DEV_CHILDREN *p); #endif /* pci */ #ifdef CONFIG_PCI diff --git a/drivers/misc/mods/mods_irq.c b/drivers/misc/mods/mods_irq.c index a6aacdfa..85d6c5b0 100644 --- a/drivers/misc/mods/mods_irq.c +++ b/drivers/misc/mods/mods_irq.c @@ -2,7 +2,7 @@ /* * mods_irq.c - This file is part of NVIDIA MODS kernel driver. * - * Copyright (c) 2008-2021, NVIDIA CORPORATION. All rights reserved. + * Copyright (c) 2008-2022, NVIDIA CORPORATION. All rights reserved. * * NVIDIA MODS kernel driver is free software: you can redistribute it and/or * modify it under the terms of the GNU General Public License, @@ -534,10 +534,10 @@ static int add_irq_map(struct mods_client *client, (irq_type == MODS_IRQ_TYPE_MSIX)) { cl_debug(DEBUG_ISR, "dev %04x:%02x:%02x.%x registered %s IRQ 0x%x\n", - pci_domain_nr(dev->bus), - dev->bus->number, - PCI_SLOT(dev->devfn), - PCI_FUNC(dev->devfn), + dev ? pci_domain_nr(dev->bus) : 0U, + dev ? dev->bus->number : 0U, + dev ? PCI_SLOT(dev->devfn) : 0U, + dev ? PCI_FUNC(dev->devfn) : 0U, mods_irq_type_name(irq_type), irq); } diff --git a/drivers/misc/mods/mods_krnl.c b/drivers/misc/mods/mods_krnl.c index 34df1345..9980e217 100644 --- a/drivers/misc/mods/mods_krnl.c +++ b/drivers/misc/mods/mods_krnl.c @@ -100,6 +100,14 @@ static const struct pci_device_id mods_pci_table[] = { .class = (PCI_CLASS_DISPLAY_3D << 8), .class_mask = ~0 }, + { + .vendor = PCI_VENDOR_ID_NVIDIA, + .device = PCI_ANY_ID, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .class = (PCI_CLASS_BRIDGE_OTHER << 8), + .class_mask = ~0 + }, { 0 } }; @@ -1697,6 +1705,7 @@ static int esc_mods_write_sysfs_node(struct mods_client *client, memcpy(pdata->path, "/sys/", 5); pdata->path[sizeof(pdata->path) - 1] = 0; + memset(&task, 0, sizeof(task)); task.path = pdata->path; task.data = pdata->contents; task.data_size = pdata->size; @@ -2244,6 +2253,10 @@ static long mods_krnl_ioctl(struct file *fp, esc_mods_acpi_get_ddc_2, MODS_ACPI_GET_DDC_2); break; + case MODS_ESC_GET_ACPI_DEV_CHILDREN: + MODS_IOCTL(MODS_ESC_GET_ACPI_DEV_CHILDREN, + esc_mods_get_acpi_dev_children, MODS_GET_ACPI_DEV_CHILDREN); + break; #else case MODS_ESC_EVAL_ACPI_METHOD: /* fallthrough */ @@ -2256,6 +2269,8 @@ static long mods_krnl_ioctl(struct file *fp, case MODS_ESC_ACPI_GET_DDC: /* fallthrough */ case MODS_ESC_ACPI_GET_DDC_2: + /* fallthrough */ + case MODS_ESC_GET_ACPI_DEV_CHILDREN: /* Silent failure to avoid clogging kernel log */ err = -EINVAL; break; diff --git a/drivers/misc/mods/mods_mem.c b/drivers/misc/mods/mods_mem.c index 1f0767d8..886bb54b 100644 --- a/drivers/misc/mods/mods_mem.c +++ b/drivers/misc/mods/mods_mem.c @@ -2,7 +2,7 @@ /* * mods_mem.c - This file is part of NVIDIA MODS kernel driver. * - * Copyright (c) 2008-2021, NVIDIA CORPORATION. All rights reserved. + * Copyright (c) 2008-2022, NVIDIA CORPORATION. All rights reserved. * * NVIDIA MODS kernel driver is free software: you can redistribute it and/or * modify it under the terms of the GNU General Public License, @@ -27,7 +27,7 @@ #include #endif -#ifdef CONFIG_ARCH64 +#ifdef CONFIG_ARM64 #include #endif @@ -343,7 +343,7 @@ static int mods_create_default_dma_map(struct mods_client *client, return err; } -#endif +#endif /* CONFIG_PCI */ /* Find the dma mapping chunk for the specified memory. */ static struct MODS_DMA_MAP *find_dma_map(struct MODS_MEM_INFO *p_mem_info, @@ -2051,7 +2051,7 @@ int esc_mods_dma_unmap_memory(struct mods_client *client, LOG_EXT(); return err; } -#endif +#endif /* CONFIG_PCI */ #ifdef MODS_HAS_TEGRA /* map dma buffer by iommu */ @@ -2141,15 +2141,15 @@ int esc_mods_iommu_dma_map_memory(struct mods_client *client, /* Check if IOVAs are contiguous */ iova_offset = 0; for_each_sg(sgt->sgl, sg, sgt->nents, i) { - if (sg_dma_address(sg) != (~(dma_addr_t)0) && - sg_dma_address(sg) != (iova + iova_offset)) { + iova_offset = iova_offset + sg->offset; + if (sg_dma_address(sg) != (iova + iova_offset) + || sg_dma_len(sg) != sg->length) { cl_error("sg not contiguous:dma 0x%llx, iova 0x%llx\n", sg_dma_address(sg), (u64)(iova + iova_offset)); err = -EINVAL; break; } - iova_offset += sg->length; } if (err) { dma_unmap_sg_attrs(smmu_pdev->dev, sgt->sgl, sgt->nents, @@ -2236,7 +2236,7 @@ failed: LOG_EXT(); return err; } -#endif +#endif /* MODS_HAS_TEGRA */ #ifdef CONFIG_ARM64 static void clear_contiguous_cache(struct mods_client *client, @@ -2422,7 +2422,7 @@ int esc_mods_flush_cpu_cache_range(struct mods_client *client, LOG_EXT(); return OK; } -#endif +#endif /* CONFIG_ARM64 */ static int mods_post_alloc(struct mods_client *client, struct MODS_PHYS_CHUNK *chunk, diff --git a/include/uapi/misc/mods.h b/include/uapi/misc/mods.h index 6277b861..6ddba7eb 100644 --- a/include/uapi/misc/mods.h +++ b/include/uapi/misc/mods.h @@ -25,7 +25,7 @@ /* Driver version */ #define MODS_DRIVER_VERSION_MAJOR 4 -#define MODS_DRIVER_VERSION_MINOR 2 +#define MODS_DRIVER_VERSION_MINOR 8 #define MODS_DRIVER_VERSION ((MODS_DRIVER_VERSION_MAJOR << 8) | \ ((MODS_DRIVER_VERSION_MINOR / 10) << 4) | \ (MODS_DRIVER_VERSION_MINOR % 10)) @@ -1056,6 +1056,7 @@ union ACPI_ARGUMENT { #define ACPI_MODS_TYPE_METHOD 3 #define ACPI_MAX_BUFFER_LENGTH 4096 +#define ACPI_MAX_DEV_CHILDREN 16 #define ACPI_MAX_METHOD_LENGTH 12 #define ACPI_MAX_ARGUMENT_NUMBER 12 @@ -1151,6 +1152,16 @@ struct MODS_ACPI_GET_DDC { struct mods_pci_dev device; }; +/* Used by MODS_ESC_GET_ACPI_DEV_CHILDREN ioctl */ +struct MODS_GET_ACPI_DEV_CHILDREN { + /* OUT */ + __u32 num_children; + __u32 children[ACPI_MAX_DEV_CHILDREN]; + + /* IN */ + struct mods_pci_dev_2 device; +}; + /* Used by ioctls: * - MODS_ESC_GET_API_VERSION * - MODS_ESC_GET_KERNEL_VERSION (deprecated) @@ -2009,6 +2020,7 @@ struct MODS_TEGRA_OIST_STATUS { #define MODS_ESC_MODS_GET_DRIVER_STATS MODSIO(R, 135, MODS_GET_DRIVER_STATS) #define MODS_ESC_BPMP_SET_PCIE_STATE MODSIO(W, 136, MODS_SET_PCIE_STATE) #define MODS_ESC_BPMP_INIT_PCIE_EP_PLL MODSIO(W, 137, MODS_INIT_PCIE_EP_PLL) +#define MODS_ESC_GET_ACPI_DEV_CHILDREN MODSIO(WR, 138, MODS_GET_ACPI_DEV_CHILDREN) #define MODS_ESC_SEND_TZ_MSG MODSIO(WR, 139, MODS_TZ_PARAMS) #define MODS_ESC_OIST_STATUS MODSIO(WR, 140, MODS_TEGRA_OIST_STATUS)