Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

Merge tag 'acpi-5.17-rc1-2' of git://git.kernel.org/pub/scm/linux/kernel/git/rafael/linux-pm

Pull more ACPI updates from Rafael Wysocki:
"The most significant item here is the Platform Firmware Runtime Update
and Telemetry (PFRUT) support designed to allow certain pieces of the
platform firmware to be updated on the fly, among other things.

Also important is the e820 handling change on x86 that should work
around PCI BAR allocation issues on some systems shipping since 2019.

The rest is just a handful of assorted fixes and cleanups on top of
the ACPI material merged previously.

Specifics:

- Add support for the the Platform Firmware Runtime Update and
Telemetry (PFRUT) interface based on ACPI to allow certain pieces
of the platform firmware to be updated without restarting the
system and to provide a mechanism for collecting platform firmware
telemetry data (Chen Yu, Dan Carpenter, Yang Yingliang).

- Ignore E820 reservations covering PCI host bridge windows on
sufficiently recent x86 systems to avoid issues with allocating PCI
BARs on systems where the E820 reservations cover the entire PCI
host bridge memory window returned by the _CRS object in the
system's ACPI tables (Hans de Goede).

- Fix and clean up acpi_scan_init() (Rafael Wysocki).

- Add more sanity checking to ACPI SPCR tables parsing (Mark
Langsdorf).

- Fix up ACPI APD (AMD Soc) driver initialization (Jiasheng Jiang).

- Drop unnecessary "static" from the ACPI PCC address space handling
driver added recently (kernel test robot)"

* tag 'acpi-5.17-rc1-2' of git://git.kernel.org/pub/scm/linux/kernel/git/rafael/linux-pm:
ACPI: PCC: pcc_ctx can be static
ACPI: scan: Rename label in acpi_scan_init()
ACPI: scan: Simplify initialization of power and sleep buttons
ACPI: scan: Change acpi_scan_init() return value type to void
ACPI: SPCR: check if table->serial_port.access_width is too wide
ACPI: APD: Check for NULL pointer after calling devm_ioremap()
x86/PCI: Ignore E820 reservations for bridge windows on newer systems
ACPI: pfr_telemetry: Fix info leak in pfrt_log_ioctl()
ACPI: pfr_update: Fix return value check in pfru_write()
ACPI: tools: Introduce utility for firmware updates/telemetry
ACPI: Introduce Platform Firmware Runtime Telemetry driver
ACPI: Introduce Platform Firmware Runtime Update device driver
efi: Introduce EFI_FIRMWARE_MANAGEMENT_CAPSULE_HEADER and corresponding structures

+1995 -60
+1
Documentation/userspace-api/ioctl/ioctl-number.rst
··· 367 367 <mailto:aherrman@de.ibm.com> 368 368 0xE5 00-3F linux/fuse.h 369 369 0xEC 00-01 drivers/platform/chrome/cros_ec_dev.h ChromeOS EC driver 370 + 0xEE 00-09 uapi/linux/pfrut.h Platform Firmware Runtime Update and Telemetry 370 371 0xF3 00-3F drivers/usb/misc/sisusbvga/sisusb.h sisfb (in development) 371 372 <mailto:thomas@winischhofer.net> 372 373 0xF6 all LTTng Linux Trace Toolkit Next Generation
+22 -1
arch/x86/kernel/resource.c
··· 1 1 // SPDX-License-Identifier: GPL-2.0 2 + #include <linux/dmi.h> 2 3 #include <linux/ioport.h> 3 4 #include <asm/e820/api.h> 4 5 ··· 24 23 res->start = end + 1; 25 24 } 26 25 26 + /* 27 + * Some BIOS-es contain a bug where they add addresses which map to 28 + * system RAM in the PCI host bridge window returned by the ACPI _CRS 29 + * method, see commit 4dc2287c1805 ("x86: avoid E820 regions when 30 + * allocating address space"). To avoid this Linux by default excludes 31 + * E820 reservations when allocating addresses since 2010. 32 + * In 2019 some systems have shown-up with E820 reservations which cover 33 + * the entire _CRS returned PCI host bridge window, causing all attempts 34 + * to assign memory to PCI BARs to fail if Linux uses E820 reservations. 35 + * 36 + * Ideally Linux would fully stop using E820 reservations, but then 37 + * the old systems this was added for will regress. 38 + * Instead keep the old behavior for old systems, while ignoring the 39 + * E820 reservations for any systems from now on. 40 + */ 27 41 static void remove_e820_regions(struct resource *avail) 28 42 { 29 - int i; 43 + int i, year = dmi_get_bios_year(); 30 44 struct e820_entry *entry; 45 + 46 + if (year >= 2018) 47 + return; 48 + 49 + pr_info_once("PCI: Removing E820 reservations from host bridge windows\n"); 31 50 32 51 for (i = 0; i < e820_table->nr_entries; i++) { 33 52 entry = &e820_table->entries[i];
+22
drivers/acpi/Kconfig
··· 520 520 userspace. The configurable ACPI groups will be visible under 521 521 /config/acpi, assuming configfs is mounted under /config. 522 522 523 + config ACPI_PFRUT 524 + tristate "ACPI Platform Firmware Runtime Update and Telemetry" 525 + depends on 64BIT 526 + help 527 + This mechanism allows certain pieces of the platform firmware 528 + to be updated on the fly while the system is running (runtime) 529 + without the need to restart it, which is key in the cases when 530 + the system needs to be available 100% of the time and it cannot 531 + afford the downtime related to restarting it, or when the work 532 + carried out by the system is particularly important, so it cannot 533 + be interrupted, and it is not practical to wait until it is complete. 534 + 535 + The existing firmware code can be modified (driver update) or 536 + extended by adding new code to the firmware (code injection). 537 + 538 + Besides, the telemetry driver allows user space to fetch telemetry 539 + data from the firmware with the help of the Platform Firmware Runtime 540 + Telemetry interface. 541 + 542 + To compile the drivers as modules, choose M here: 543 + the modules will be called pfr_update and pfr_telemetry. 544 + 523 545 if ARM64 524 546 source "drivers/acpi/arm64/Kconfig" 525 547
+1
drivers/acpi/Makefile
··· 103 103 obj-$(CONFIG_ACPI_SPCR_TABLE) += spcr.o 104 104 obj-$(CONFIG_ACPI_DEBUGGER_USER) += acpi_dbg.o 105 105 obj-$(CONFIG_ACPI_PPTT) += pptt.o 106 + obj-$(CONFIG_ACPI_PFRUT) += pfr_update.o pfr_telemetry.o 106 107 107 108 # processor has its own "processor." module_param namespace 108 109 processor-y := processor_driver.o
+2
drivers/acpi/acpi_apd.c
··· 102 102 resource_size(rentry->res)); 103 103 break; 104 104 } 105 + if (!clk_data->base) 106 + return -ENOMEM; 105 107 106 108 acpi_dev_free_resource_list(&resource_list); 107 109
+1 -1
drivers/acpi/acpi_pcc.c
··· 31 31 struct acpi_pcc_info ctx; 32 32 }; 33 33 34 - struct acpi_pcc_info pcc_ctx; 34 + static struct acpi_pcc_info pcc_ctx; 35 35 36 36 static void pcc_rx_callback(struct mbox_client *cl, void *m) 37 37 {
+1 -1
drivers/acpi/internal.h
··· 14 14 int early_acpi_osi_init(void); 15 15 int acpi_osi_init(void); 16 16 acpi_status acpi_os_initialize1(void); 17 - int acpi_scan_init(void); 17 + void acpi_scan_init(void); 18 18 #ifdef CONFIG_PCI 19 19 void acpi_pci_root_init(void); 20 20 void acpi_pci_link_init(void);
+435
drivers/acpi/pfr_telemetry.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + /* 3 + * ACPI Platform Firmware Runtime Telemetry driver 4 + * 5 + * Copyright (C) 2021 Intel Corporation 6 + * Author: Chen Yu <yu.c.chen@intel.com> 7 + * 8 + * This driver allows user space to fetch telemetry data from the 9 + * firmware with the help of the Platform Firmware Runtime Telemetry 10 + * interface. 11 + */ 12 + #include <linux/acpi.h> 13 + #include <linux/device.h> 14 + #include <linux/err.h> 15 + #include <linux/errno.h> 16 + #include <linux/file.h> 17 + #include <linux/fs.h> 18 + #include <linux/miscdevice.h> 19 + #include <linux/module.h> 20 + #include <linux/mm.h> 21 + #include <linux/platform_device.h> 22 + #include <linux/string.h> 23 + #include <linux/uaccess.h> 24 + #include <linux/uio.h> 25 + #include <linux/uuid.h> 26 + 27 + #include <uapi/linux/pfrut.h> 28 + 29 + #define PFRT_LOG_EXEC_IDX 0 30 + #define PFRT_LOG_HISTORY_IDX 1 31 + 32 + #define PFRT_LOG_ERR 0 33 + #define PFRT_LOG_WARN 1 34 + #define PFRT_LOG_INFO 2 35 + #define PFRT_LOG_VERB 4 36 + 37 + #define PFRT_FUNC_SET_LEV 1 38 + #define PFRT_FUNC_GET_LEV 2 39 + #define PFRT_FUNC_GET_DATA 3 40 + 41 + #define PFRT_REVID_1 1 42 + #define PFRT_REVID_2 2 43 + #define PFRT_DEFAULT_REV_ID PFRT_REVID_1 44 + 45 + enum log_index { 46 + LOG_STATUS_IDX = 0, 47 + LOG_EXT_STATUS_IDX = 1, 48 + LOG_MAX_SZ_IDX = 2, 49 + LOG_CHUNK1_LO_IDX = 3, 50 + LOG_CHUNK1_HI_IDX = 4, 51 + LOG_CHUNK1_SZ_IDX = 5, 52 + LOG_CHUNK2_LO_IDX = 6, 53 + LOG_CHUNK2_HI_IDX = 7, 54 + LOG_CHUNK2_SZ_IDX = 8, 55 + LOG_ROLLOVER_CNT_IDX = 9, 56 + LOG_RESET_CNT_IDX = 10, 57 + LOG_NR_IDX 58 + }; 59 + 60 + struct pfrt_log_device { 61 + int index; 62 + struct pfrt_log_info info; 63 + struct device *parent_dev; 64 + struct miscdevice miscdev; 65 + }; 66 + 67 + /* pfrt_guid is the parameter for _DSM method */ 68 + static const guid_t pfrt_log_guid = 69 + GUID_INIT(0x75191659, 0x8178, 0x4D9D, 0xB8, 0x8F, 0xAC, 0x5E, 70 + 0x5E, 0x93, 0xE8, 0xBF); 71 + 72 + static DEFINE_IDA(pfrt_log_ida); 73 + 74 + static inline struct pfrt_log_device *to_pfrt_log_dev(struct file *file) 75 + { 76 + return container_of(file->private_data, struct pfrt_log_device, miscdev); 77 + } 78 + 79 + static int get_pfrt_log_data_info(struct pfrt_log_data_info *data_info, 80 + struct pfrt_log_device *pfrt_log_dev) 81 + { 82 + acpi_handle handle = ACPI_HANDLE(pfrt_log_dev->parent_dev); 83 + union acpi_object *out_obj, in_obj, in_buf; 84 + int ret = -EBUSY; 85 + 86 + memset(data_info, 0, sizeof(*data_info)); 87 + memset(&in_obj, 0, sizeof(in_obj)); 88 + memset(&in_buf, 0, sizeof(in_buf)); 89 + in_obj.type = ACPI_TYPE_PACKAGE; 90 + in_obj.package.count = 1; 91 + in_obj.package.elements = &in_buf; 92 + in_buf.type = ACPI_TYPE_INTEGER; 93 + in_buf.integer.value = pfrt_log_dev->info.log_type; 94 + 95 + out_obj = acpi_evaluate_dsm_typed(handle, &pfrt_log_guid, 96 + pfrt_log_dev->info.log_revid, PFRT_FUNC_GET_DATA, 97 + &in_obj, ACPI_TYPE_PACKAGE); 98 + if (!out_obj) 99 + return -EINVAL; 100 + 101 + if (out_obj->package.count < LOG_NR_IDX || 102 + out_obj->package.elements[LOG_STATUS_IDX].type != ACPI_TYPE_INTEGER || 103 + out_obj->package.elements[LOG_EXT_STATUS_IDX].type != ACPI_TYPE_INTEGER || 104 + out_obj->package.elements[LOG_MAX_SZ_IDX].type != ACPI_TYPE_INTEGER || 105 + out_obj->package.elements[LOG_CHUNK1_LO_IDX].type != ACPI_TYPE_INTEGER || 106 + out_obj->package.elements[LOG_CHUNK1_HI_IDX].type != ACPI_TYPE_INTEGER || 107 + out_obj->package.elements[LOG_CHUNK1_SZ_IDX].type != ACPI_TYPE_INTEGER || 108 + out_obj->package.elements[LOG_CHUNK2_LO_IDX].type != ACPI_TYPE_INTEGER || 109 + out_obj->package.elements[LOG_CHUNK2_HI_IDX].type != ACPI_TYPE_INTEGER || 110 + out_obj->package.elements[LOG_CHUNK2_SZ_IDX].type != ACPI_TYPE_INTEGER || 111 + out_obj->package.elements[LOG_ROLLOVER_CNT_IDX].type != ACPI_TYPE_INTEGER || 112 + out_obj->package.elements[LOG_RESET_CNT_IDX].type != ACPI_TYPE_INTEGER) 113 + goto free_acpi_buffer; 114 + 115 + data_info->status = out_obj->package.elements[LOG_STATUS_IDX].integer.value; 116 + data_info->ext_status = 117 + out_obj->package.elements[LOG_EXT_STATUS_IDX].integer.value; 118 + if (data_info->status != DSM_SUCCEED) { 119 + dev_dbg(pfrt_log_dev->parent_dev, "Error Status:%d\n", data_info->status); 120 + dev_dbg(pfrt_log_dev->parent_dev, "Error Extend Status:%d\n", 121 + data_info->ext_status); 122 + goto free_acpi_buffer; 123 + } 124 + 125 + data_info->max_data_size = 126 + out_obj->package.elements[LOG_MAX_SZ_IDX].integer.value; 127 + data_info->chunk1_addr_lo = 128 + out_obj->package.elements[LOG_CHUNK1_LO_IDX].integer.value; 129 + data_info->chunk1_addr_hi = 130 + out_obj->package.elements[LOG_CHUNK1_HI_IDX].integer.value; 131 + data_info->chunk1_size = 132 + out_obj->package.elements[LOG_CHUNK1_SZ_IDX].integer.value; 133 + data_info->chunk2_addr_lo = 134 + out_obj->package.elements[LOG_CHUNK2_LO_IDX].integer.value; 135 + data_info->chunk2_addr_hi = 136 + out_obj->package.elements[LOG_CHUNK2_HI_IDX].integer.value; 137 + data_info->chunk2_size = 138 + out_obj->package.elements[LOG_CHUNK2_SZ_IDX].integer.value; 139 + data_info->rollover_cnt = 140 + out_obj->package.elements[LOG_ROLLOVER_CNT_IDX].integer.value; 141 + data_info->reset_cnt = 142 + out_obj->package.elements[LOG_RESET_CNT_IDX].integer.value; 143 + 144 + ret = 0; 145 + 146 + free_acpi_buffer: 147 + kfree(out_obj); 148 + 149 + return ret; 150 + } 151 + 152 + static int set_pfrt_log_level(int level, struct pfrt_log_device *pfrt_log_dev) 153 + { 154 + acpi_handle handle = ACPI_HANDLE(pfrt_log_dev->parent_dev); 155 + union acpi_object *out_obj, *obj, in_obj, in_buf; 156 + enum pfru_dsm_status status, ext_status; 157 + int ret = 0; 158 + 159 + memset(&in_obj, 0, sizeof(in_obj)); 160 + memset(&in_buf, 0, sizeof(in_buf)); 161 + in_obj.type = ACPI_TYPE_PACKAGE; 162 + in_obj.package.count = 1; 163 + in_obj.package.elements = &in_buf; 164 + in_buf.type = ACPI_TYPE_INTEGER; 165 + in_buf.integer.value = level; 166 + 167 + out_obj = acpi_evaluate_dsm_typed(handle, &pfrt_log_guid, 168 + pfrt_log_dev->info.log_revid, PFRT_FUNC_SET_LEV, 169 + &in_obj, ACPI_TYPE_PACKAGE); 170 + if (!out_obj) 171 + return -EINVAL; 172 + 173 + obj = &out_obj->package.elements[0]; 174 + status = obj->integer.value; 175 + if (status != DSM_SUCCEED) { 176 + obj = &out_obj->package.elements[1]; 177 + ext_status = obj->integer.value; 178 + dev_dbg(pfrt_log_dev->parent_dev, "Error Status:%d\n", status); 179 + dev_dbg(pfrt_log_dev->parent_dev, "Error Extend Status:%d\n", ext_status); 180 + ret = -EBUSY; 181 + } 182 + 183 + kfree(out_obj); 184 + 185 + return ret; 186 + } 187 + 188 + static int get_pfrt_log_level(struct pfrt_log_device *pfrt_log_dev) 189 + { 190 + acpi_handle handle = ACPI_HANDLE(pfrt_log_dev->parent_dev); 191 + union acpi_object *out_obj, *obj; 192 + enum pfru_dsm_status status, ext_status; 193 + int ret = -EBUSY; 194 + 195 + out_obj = acpi_evaluate_dsm_typed(handle, &pfrt_log_guid, 196 + pfrt_log_dev->info.log_revid, PFRT_FUNC_GET_LEV, 197 + NULL, ACPI_TYPE_PACKAGE); 198 + if (!out_obj) 199 + return -EINVAL; 200 + 201 + obj = &out_obj->package.elements[0]; 202 + if (obj->type != ACPI_TYPE_INTEGER) 203 + goto free_acpi_buffer; 204 + 205 + status = obj->integer.value; 206 + if (status != DSM_SUCCEED) { 207 + obj = &out_obj->package.elements[1]; 208 + ext_status = obj->integer.value; 209 + dev_dbg(pfrt_log_dev->parent_dev, "Error Status:%d\n", status); 210 + dev_dbg(pfrt_log_dev->parent_dev, "Error Extend Status:%d\n", ext_status); 211 + goto free_acpi_buffer; 212 + } 213 + 214 + obj = &out_obj->package.elements[2]; 215 + if (obj->type != ACPI_TYPE_INTEGER) 216 + goto free_acpi_buffer; 217 + 218 + ret = obj->integer.value; 219 + 220 + free_acpi_buffer: 221 + kfree(out_obj); 222 + 223 + return ret; 224 + } 225 + 226 + static int valid_log_level(u32 level) 227 + { 228 + return level == PFRT_LOG_ERR || level == PFRT_LOG_WARN || 229 + level == PFRT_LOG_INFO || level == PFRT_LOG_VERB; 230 + } 231 + 232 + static int valid_log_type(u32 type) 233 + { 234 + return type == PFRT_LOG_EXEC_IDX || type == PFRT_LOG_HISTORY_IDX; 235 + } 236 + 237 + static inline int valid_log_revid(u32 id) 238 + { 239 + return id == PFRT_REVID_1 || id == PFRT_REVID_2; 240 + } 241 + 242 + static long pfrt_log_ioctl(struct file *file, unsigned int cmd, unsigned long arg) 243 + { 244 + struct pfrt_log_device *pfrt_log_dev = to_pfrt_log_dev(file); 245 + struct pfrt_log_data_info data_info; 246 + struct pfrt_log_info info; 247 + void __user *p; 248 + int ret = 0; 249 + 250 + p = (void __user *)arg; 251 + 252 + switch (cmd) { 253 + case PFRT_LOG_IOC_SET_INFO: 254 + if (copy_from_user(&info, p, sizeof(info))) 255 + return -EFAULT; 256 + 257 + if (valid_log_revid(info.log_revid)) 258 + pfrt_log_dev->info.log_revid = info.log_revid; 259 + 260 + if (valid_log_level(info.log_level)) { 261 + ret = set_pfrt_log_level(info.log_level, pfrt_log_dev); 262 + if (ret < 0) 263 + return ret; 264 + 265 + pfrt_log_dev->info.log_level = info.log_level; 266 + } 267 + 268 + if (valid_log_type(info.log_type)) 269 + pfrt_log_dev->info.log_type = info.log_type; 270 + 271 + return 0; 272 + 273 + case PFRT_LOG_IOC_GET_INFO: 274 + info.log_level = get_pfrt_log_level(pfrt_log_dev); 275 + if (ret < 0) 276 + return ret; 277 + 278 + info.log_type = pfrt_log_dev->info.log_type; 279 + info.log_revid = pfrt_log_dev->info.log_revid; 280 + if (copy_to_user(p, &info, sizeof(info))) 281 + return -EFAULT; 282 + 283 + return 0; 284 + 285 + case PFRT_LOG_IOC_GET_DATA_INFO: 286 + ret = get_pfrt_log_data_info(&data_info, pfrt_log_dev); 287 + if (ret) 288 + return ret; 289 + 290 + if (copy_to_user(p, &data_info, sizeof(struct pfrt_log_data_info))) 291 + return -EFAULT; 292 + 293 + return 0; 294 + 295 + default: 296 + return -ENOTTY; 297 + } 298 + } 299 + 300 + static int 301 + pfrt_log_mmap(struct file *file, struct vm_area_struct *vma) 302 + { 303 + struct pfrt_log_device *pfrt_log_dev; 304 + struct pfrt_log_data_info info; 305 + unsigned long psize, vsize; 306 + phys_addr_t base_addr; 307 + int ret; 308 + 309 + if (vma->vm_flags & VM_WRITE) 310 + return -EROFS; 311 + 312 + /* changing from read to write with mprotect is not allowed */ 313 + vma->vm_flags &= ~VM_MAYWRITE; 314 + 315 + pfrt_log_dev = to_pfrt_log_dev(file); 316 + 317 + ret = get_pfrt_log_data_info(&info, pfrt_log_dev); 318 + if (ret) 319 + return ret; 320 + 321 + base_addr = (phys_addr_t)((info.chunk2_addr_hi << 32) | info.chunk2_addr_lo); 322 + /* pfrt update has not been launched yet */ 323 + if (!base_addr) 324 + return -ENODEV; 325 + 326 + psize = info.max_data_size; 327 + /* base address and total buffer size must be page aligned */ 328 + if (!PAGE_ALIGNED(base_addr) || !PAGE_ALIGNED(psize)) 329 + return -ENODEV; 330 + 331 + vsize = vma->vm_end - vma->vm_start; 332 + if (vsize > psize) 333 + return -EINVAL; 334 + 335 + vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); 336 + if (io_remap_pfn_range(vma, vma->vm_start, PFN_DOWN(base_addr), 337 + vsize, vma->vm_page_prot)) 338 + return -EAGAIN; 339 + 340 + return 0; 341 + } 342 + 343 + static const struct file_operations acpi_pfrt_log_fops = { 344 + .owner = THIS_MODULE, 345 + .mmap = pfrt_log_mmap, 346 + .unlocked_ioctl = pfrt_log_ioctl, 347 + .llseek = noop_llseek, 348 + }; 349 + 350 + static int acpi_pfrt_log_remove(struct platform_device *pdev) 351 + { 352 + struct pfrt_log_device *pfrt_log_dev = platform_get_drvdata(pdev); 353 + 354 + misc_deregister(&pfrt_log_dev->miscdev); 355 + 356 + return 0; 357 + } 358 + 359 + static void pfrt_log_put_idx(void *data) 360 + { 361 + struct pfrt_log_device *pfrt_log_dev = data; 362 + 363 + ida_free(&pfrt_log_ida, pfrt_log_dev->index); 364 + } 365 + 366 + static int acpi_pfrt_log_probe(struct platform_device *pdev) 367 + { 368 + acpi_handle handle = ACPI_HANDLE(&pdev->dev); 369 + struct pfrt_log_device *pfrt_log_dev; 370 + int ret; 371 + 372 + if (!acpi_has_method(handle, "_DSM")) { 373 + dev_dbg(&pdev->dev, "Missing _DSM\n"); 374 + return -ENODEV; 375 + } 376 + 377 + pfrt_log_dev = devm_kzalloc(&pdev->dev, sizeof(*pfrt_log_dev), GFP_KERNEL); 378 + if (!pfrt_log_dev) 379 + return -ENOMEM; 380 + 381 + ret = ida_alloc(&pfrt_log_ida, GFP_KERNEL); 382 + if (ret < 0) 383 + return ret; 384 + 385 + pfrt_log_dev->index = ret; 386 + ret = devm_add_action_or_reset(&pdev->dev, pfrt_log_put_idx, pfrt_log_dev); 387 + if (ret) 388 + return ret; 389 + 390 + pfrt_log_dev->info.log_revid = PFRT_DEFAULT_REV_ID; 391 + pfrt_log_dev->parent_dev = &pdev->dev; 392 + 393 + pfrt_log_dev->miscdev.minor = MISC_DYNAMIC_MINOR; 394 + pfrt_log_dev->miscdev.name = devm_kasprintf(&pdev->dev, GFP_KERNEL, 395 + "pfrt%d", 396 + pfrt_log_dev->index); 397 + if (!pfrt_log_dev->miscdev.name) 398 + return -ENOMEM; 399 + 400 + pfrt_log_dev->miscdev.nodename = devm_kasprintf(&pdev->dev, GFP_KERNEL, 401 + "acpi_pfr_telemetry%d", 402 + pfrt_log_dev->index); 403 + if (!pfrt_log_dev->miscdev.nodename) 404 + return -ENOMEM; 405 + 406 + pfrt_log_dev->miscdev.fops = &acpi_pfrt_log_fops; 407 + pfrt_log_dev->miscdev.parent = &pdev->dev; 408 + 409 + ret = misc_register(&pfrt_log_dev->miscdev); 410 + if (ret) 411 + return ret; 412 + 413 + platform_set_drvdata(pdev, pfrt_log_dev); 414 + 415 + return 0; 416 + } 417 + 418 + static const struct acpi_device_id acpi_pfrt_log_ids[] = { 419 + {"INTC1081"}, 420 + {} 421 + }; 422 + MODULE_DEVICE_TABLE(acpi, acpi_pfrt_log_ids); 423 + 424 + static struct platform_driver acpi_pfrt_log_driver = { 425 + .driver = { 426 + .name = "pfr_telemetry", 427 + .acpi_match_table = acpi_pfrt_log_ids, 428 + }, 429 + .probe = acpi_pfrt_log_probe, 430 + .remove = acpi_pfrt_log_remove, 431 + }; 432 + module_platform_driver(acpi_pfrt_log_driver); 433 + 434 + MODULE_DESCRIPTION("Platform Firmware Runtime Update Telemetry driver"); 435 + MODULE_LICENSE("GPL v2");
+575
drivers/acpi/pfr_update.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + /* 3 + * ACPI Platform Firmware Runtime Update Device driver 4 + * 5 + * Copyright (C) 2021 Intel Corporation 6 + * Author: Chen Yu <yu.c.chen@intel.com> 7 + * 8 + * pfr_update driver is used for Platform Firmware Runtime 9 + * Update, which includes the code injection and driver update. 10 + */ 11 + #include <linux/acpi.h> 12 + #include <linux/device.h> 13 + #include <linux/efi.h> 14 + #include <linux/err.h> 15 + #include <linux/errno.h> 16 + #include <linux/file.h> 17 + #include <linux/fs.h> 18 + #include <linux/idr.h> 19 + #include <linux/miscdevice.h> 20 + #include <linux/module.h> 21 + #include <linux/platform_device.h> 22 + #include <linux/string.h> 23 + #include <linux/uaccess.h> 24 + #include <linux/uio.h> 25 + #include <linux/uuid.h> 26 + 27 + #include <uapi/linux/pfrut.h> 28 + 29 + #define PFRU_FUNC_STANDARD_QUERY 0 30 + #define PFRU_FUNC_QUERY_UPDATE_CAP 1 31 + #define PFRU_FUNC_QUERY_BUF 2 32 + #define PFRU_FUNC_START 3 33 + 34 + #define PFRU_CODE_INJECT_TYPE 1 35 + #define PFRU_DRIVER_UPDATE_TYPE 2 36 + 37 + #define PFRU_REVID_1 1 38 + #define PFRU_REVID_2 2 39 + #define PFRU_DEFAULT_REV_ID PFRU_REVID_1 40 + 41 + enum cap_index { 42 + CAP_STATUS_IDX = 0, 43 + CAP_UPDATE_IDX = 1, 44 + CAP_CODE_TYPE_IDX = 2, 45 + CAP_FW_VER_IDX = 3, 46 + CAP_CODE_RT_VER_IDX = 4, 47 + CAP_DRV_TYPE_IDX = 5, 48 + CAP_DRV_RT_VER_IDX = 6, 49 + CAP_DRV_SVN_IDX = 7, 50 + CAP_PLAT_ID_IDX = 8, 51 + CAP_OEM_ID_IDX = 9, 52 + CAP_OEM_INFO_IDX = 10, 53 + CAP_NR_IDX 54 + }; 55 + 56 + enum buf_index { 57 + BUF_STATUS_IDX = 0, 58 + BUF_EXT_STATUS_IDX = 1, 59 + BUF_ADDR_LOW_IDX = 2, 60 + BUF_ADDR_HI_IDX = 3, 61 + BUF_SIZE_IDX = 4, 62 + BUF_NR_IDX 63 + }; 64 + 65 + enum update_index { 66 + UPDATE_STATUS_IDX = 0, 67 + UPDATE_EXT_STATUS_IDX = 1, 68 + UPDATE_AUTH_TIME_LOW_IDX = 2, 69 + UPDATE_AUTH_TIME_HI_IDX = 3, 70 + UPDATE_EXEC_TIME_LOW_IDX = 4, 71 + UPDATE_EXEC_TIME_HI_IDX = 5, 72 + UPDATE_NR_IDX 73 + }; 74 + 75 + enum pfru_start_action { 76 + START_STAGE = 0, 77 + START_ACTIVATE = 1, 78 + START_STAGE_ACTIVATE = 2, 79 + }; 80 + 81 + struct pfru_device { 82 + u32 rev_id, index; 83 + struct device *parent_dev; 84 + struct miscdevice miscdev; 85 + }; 86 + 87 + static DEFINE_IDA(pfru_ida); 88 + 89 + /* 90 + * Manual reference: 91 + * https://uefi.org/sites/default/files/resources/Intel_MM_OS_Interface_Spec_Rev100.pdf 92 + * 93 + * pfru_guid is the parameter for _DSM method 94 + */ 95 + static const guid_t pfru_guid = 96 + GUID_INIT(0xECF9533B, 0x4A3C, 0x4E89, 0x93, 0x9E, 0xC7, 0x71, 97 + 0x12, 0x60, 0x1C, 0x6D); 98 + 99 + /* pfru_code_inj_guid is the UUID to identify code injection EFI capsule file */ 100 + static const guid_t pfru_code_inj_guid = 101 + GUID_INIT(0xB2F84B79, 0x7B6E, 0x4E45, 0x88, 0x5F, 0x3F, 0xB9, 102 + 0xBB, 0x18, 0x54, 0x02); 103 + 104 + /* pfru_drv_update_guid is the UUID to identify driver update EFI capsule file */ 105 + static const guid_t pfru_drv_update_guid = 106 + GUID_INIT(0x4569DD8C, 0x75F1, 0x429A, 0xA3, 0xD6, 0x24, 0xDE, 107 + 0x80, 0x97, 0xA0, 0xDF); 108 + 109 + static inline int pfru_valid_revid(u32 id) 110 + { 111 + return id == PFRU_REVID_1 || id == PFRU_REVID_2; 112 + } 113 + 114 + static inline struct pfru_device *to_pfru_dev(struct file *file) 115 + { 116 + return container_of(file->private_data, struct pfru_device, miscdev); 117 + } 118 + 119 + static int query_capability(struct pfru_update_cap_info *cap_hdr, 120 + struct pfru_device *pfru_dev) 121 + { 122 + acpi_handle handle = ACPI_HANDLE(pfru_dev->parent_dev); 123 + union acpi_object *out_obj; 124 + int ret = -EINVAL; 125 + 126 + out_obj = acpi_evaluate_dsm_typed(handle, &pfru_guid, 127 + pfru_dev->rev_id, 128 + PFRU_FUNC_QUERY_UPDATE_CAP, 129 + NULL, ACPI_TYPE_PACKAGE); 130 + if (!out_obj) 131 + return ret; 132 + 133 + if (out_obj->package.count < CAP_NR_IDX || 134 + out_obj->package.elements[CAP_STATUS_IDX].type != ACPI_TYPE_INTEGER || 135 + out_obj->package.elements[CAP_UPDATE_IDX].type != ACPI_TYPE_INTEGER || 136 + out_obj->package.elements[CAP_CODE_TYPE_IDX].type != ACPI_TYPE_BUFFER || 137 + out_obj->package.elements[CAP_FW_VER_IDX].type != ACPI_TYPE_INTEGER || 138 + out_obj->package.elements[CAP_CODE_RT_VER_IDX].type != ACPI_TYPE_INTEGER || 139 + out_obj->package.elements[CAP_DRV_TYPE_IDX].type != ACPI_TYPE_BUFFER || 140 + out_obj->package.elements[CAP_DRV_RT_VER_IDX].type != ACPI_TYPE_INTEGER || 141 + out_obj->package.elements[CAP_DRV_SVN_IDX].type != ACPI_TYPE_INTEGER || 142 + out_obj->package.elements[CAP_PLAT_ID_IDX].type != ACPI_TYPE_BUFFER || 143 + out_obj->package.elements[CAP_OEM_ID_IDX].type != ACPI_TYPE_BUFFER || 144 + out_obj->package.elements[CAP_OEM_INFO_IDX].type != ACPI_TYPE_BUFFER) 145 + goto free_acpi_buffer; 146 + 147 + cap_hdr->status = out_obj->package.elements[CAP_STATUS_IDX].integer.value; 148 + if (cap_hdr->status != DSM_SUCCEED) { 149 + ret = -EBUSY; 150 + dev_dbg(pfru_dev->parent_dev, "Error Status:%d\n", cap_hdr->status); 151 + goto free_acpi_buffer; 152 + } 153 + 154 + cap_hdr->update_cap = out_obj->package.elements[CAP_UPDATE_IDX].integer.value; 155 + memcpy(&cap_hdr->code_type, 156 + out_obj->package.elements[CAP_CODE_TYPE_IDX].buffer.pointer, 157 + out_obj->package.elements[CAP_CODE_TYPE_IDX].buffer.length); 158 + cap_hdr->fw_version = 159 + out_obj->package.elements[CAP_FW_VER_IDX].integer.value; 160 + cap_hdr->code_rt_version = 161 + out_obj->package.elements[CAP_CODE_RT_VER_IDX].integer.value; 162 + memcpy(&cap_hdr->drv_type, 163 + out_obj->package.elements[CAP_DRV_TYPE_IDX].buffer.pointer, 164 + out_obj->package.elements[CAP_DRV_TYPE_IDX].buffer.length); 165 + cap_hdr->drv_rt_version = 166 + out_obj->package.elements[CAP_DRV_RT_VER_IDX].integer.value; 167 + cap_hdr->drv_svn = 168 + out_obj->package.elements[CAP_DRV_SVN_IDX].integer.value; 169 + memcpy(&cap_hdr->platform_id, 170 + out_obj->package.elements[CAP_PLAT_ID_IDX].buffer.pointer, 171 + out_obj->package.elements[CAP_PLAT_ID_IDX].buffer.length); 172 + memcpy(&cap_hdr->oem_id, 173 + out_obj->package.elements[CAP_OEM_ID_IDX].buffer.pointer, 174 + out_obj->package.elements[CAP_OEM_ID_IDX].buffer.length); 175 + cap_hdr->oem_info_len = 176 + out_obj->package.elements[CAP_OEM_INFO_IDX].buffer.length; 177 + 178 + ret = 0; 179 + 180 + free_acpi_buffer: 181 + kfree(out_obj); 182 + 183 + return ret; 184 + } 185 + 186 + static int query_buffer(struct pfru_com_buf_info *info, 187 + struct pfru_device *pfru_dev) 188 + { 189 + acpi_handle handle = ACPI_HANDLE(pfru_dev->parent_dev); 190 + union acpi_object *out_obj; 191 + int ret = -EINVAL; 192 + 193 + out_obj = acpi_evaluate_dsm_typed(handle, &pfru_guid, 194 + pfru_dev->rev_id, PFRU_FUNC_QUERY_BUF, 195 + NULL, ACPI_TYPE_PACKAGE); 196 + if (!out_obj) 197 + return ret; 198 + 199 + if (out_obj->package.count < BUF_NR_IDX || 200 + out_obj->package.elements[BUF_STATUS_IDX].type != ACPI_TYPE_INTEGER || 201 + out_obj->package.elements[BUF_EXT_STATUS_IDX].type != ACPI_TYPE_INTEGER || 202 + out_obj->package.elements[BUF_ADDR_LOW_IDX].type != ACPI_TYPE_INTEGER || 203 + out_obj->package.elements[BUF_ADDR_HI_IDX].type != ACPI_TYPE_INTEGER || 204 + out_obj->package.elements[BUF_SIZE_IDX].type != ACPI_TYPE_INTEGER) 205 + goto free_acpi_buffer; 206 + 207 + info->status = out_obj->package.elements[BUF_STATUS_IDX].integer.value; 208 + info->ext_status = 209 + out_obj->package.elements[BUF_EXT_STATUS_IDX].integer.value; 210 + if (info->status != DSM_SUCCEED) { 211 + ret = -EBUSY; 212 + dev_dbg(pfru_dev->parent_dev, "Error Status:%d\n", info->status); 213 + dev_dbg(pfru_dev->parent_dev, "Error Extended Status:%d\n", info->ext_status); 214 + 215 + goto free_acpi_buffer; 216 + } 217 + 218 + info->addr_lo = 219 + out_obj->package.elements[BUF_ADDR_LOW_IDX].integer.value; 220 + info->addr_hi = 221 + out_obj->package.elements[BUF_ADDR_HI_IDX].integer.value; 222 + info->buf_size = out_obj->package.elements[BUF_SIZE_IDX].integer.value; 223 + 224 + ret = 0; 225 + 226 + free_acpi_buffer: 227 + kfree(out_obj); 228 + 229 + return ret; 230 + } 231 + 232 + static int get_image_type(const struct efi_manage_capsule_image_header *img_hdr, 233 + struct pfru_device *pfru_dev) 234 + { 235 + const efi_guid_t *image_type_id = &img_hdr->image_type_id; 236 + 237 + /* check whether this is a code injection or driver update */ 238 + if (guid_equal(image_type_id, &pfru_code_inj_guid)) 239 + return PFRU_CODE_INJECT_TYPE; 240 + 241 + if (guid_equal(image_type_id, &pfru_drv_update_guid)) 242 + return PFRU_DRIVER_UPDATE_TYPE; 243 + 244 + return -EINVAL; 245 + } 246 + 247 + static int adjust_efi_size(const struct efi_manage_capsule_image_header *img_hdr, 248 + int size) 249 + { 250 + /* 251 + * The (u64 hw_ins) was introduced in UEFI spec version 2, 252 + * and (u64 capsule_support) was introduced in version 3. 253 + * The size needs to be adjusted accordingly. That is to 254 + * say, version 1 should subtract the size of hw_ins+capsule_support, 255 + * and version 2 should sbstract the size of capsule_support. 256 + */ 257 + size += sizeof(struct efi_manage_capsule_image_header); 258 + switch (img_hdr->ver) { 259 + case 1: 260 + return size - 2 * sizeof(u64); 261 + 262 + case 2: 263 + return size - sizeof(u64); 264 + 265 + default: 266 + /* only support version 1 and 2 */ 267 + return -EINVAL; 268 + } 269 + } 270 + 271 + static bool applicable_image(const void *data, struct pfru_update_cap_info *cap, 272 + struct pfru_device *pfru_dev) 273 + { 274 + struct pfru_payload_hdr *payload_hdr; 275 + const efi_capsule_header_t *cap_hdr = data; 276 + const struct efi_manage_capsule_header *m_hdr; 277 + const struct efi_manage_capsule_image_header *m_img_hdr; 278 + const struct efi_image_auth *auth; 279 + int type, size; 280 + 281 + /* 282 + * If the code in the capsule is older than the current 283 + * firmware code, the update will be rejected by the firmware, 284 + * so check the version of it upfront without engaging the 285 + * Management Mode update mechanism which may be costly. 286 + */ 287 + size = cap_hdr->headersize; 288 + m_hdr = data + size; 289 + /* 290 + * Current data structure size plus variable array indicated 291 + * by number of (emb_drv_cnt + payload_cnt) 292 + */ 293 + size += offsetof(struct efi_manage_capsule_header, offset_list) + 294 + (m_hdr->emb_drv_cnt + m_hdr->payload_cnt) * sizeof(u64); 295 + m_img_hdr = data + size; 296 + 297 + type = get_image_type(m_img_hdr, pfru_dev); 298 + if (type < 0) 299 + return false; 300 + 301 + size = adjust_efi_size(m_img_hdr, size); 302 + if (size < 0) 303 + return false; 304 + 305 + auth = data + size; 306 + size += sizeof(u64) + auth->auth_info.hdr.len; 307 + payload_hdr = (struct pfru_payload_hdr *)(data + size); 308 + 309 + /* finally compare the version */ 310 + if (type == PFRU_CODE_INJECT_TYPE) 311 + return payload_hdr->rt_ver >= cap->code_rt_version; 312 + 313 + return payload_hdr->rt_ver >= cap->drv_rt_version; 314 + } 315 + 316 + static void print_update_debug_info(struct pfru_updated_result *result, 317 + struct pfru_device *pfru_dev) 318 + { 319 + dev_dbg(pfru_dev->parent_dev, "Update result:\n"); 320 + dev_dbg(pfru_dev->parent_dev, "Authentication Time Low:%lld\n", 321 + result->low_auth_time); 322 + dev_dbg(pfru_dev->parent_dev, "Authentication Time High:%lld\n", 323 + result->high_auth_time); 324 + dev_dbg(pfru_dev->parent_dev, "Execution Time Low:%lld\n", 325 + result->low_exec_time); 326 + dev_dbg(pfru_dev->parent_dev, "Execution Time High:%lld\n", 327 + result->high_exec_time); 328 + } 329 + 330 + static int start_update(int action, struct pfru_device *pfru_dev) 331 + { 332 + union acpi_object *out_obj, in_obj, in_buf; 333 + struct pfru_updated_result update_result; 334 + acpi_handle handle; 335 + int ret = -EINVAL; 336 + 337 + memset(&in_obj, 0, sizeof(in_obj)); 338 + memset(&in_buf, 0, sizeof(in_buf)); 339 + in_obj.type = ACPI_TYPE_PACKAGE; 340 + in_obj.package.count = 1; 341 + in_obj.package.elements = &in_buf; 342 + in_buf.type = ACPI_TYPE_INTEGER; 343 + in_buf.integer.value = action; 344 + 345 + handle = ACPI_HANDLE(pfru_dev->parent_dev); 346 + out_obj = acpi_evaluate_dsm_typed(handle, &pfru_guid, 347 + pfru_dev->rev_id, PFRU_FUNC_START, 348 + &in_obj, ACPI_TYPE_PACKAGE); 349 + if (!out_obj) 350 + return ret; 351 + 352 + if (out_obj->package.count < UPDATE_NR_IDX || 353 + out_obj->package.elements[UPDATE_STATUS_IDX].type != ACPI_TYPE_INTEGER || 354 + out_obj->package.elements[UPDATE_EXT_STATUS_IDX].type != ACPI_TYPE_INTEGER || 355 + out_obj->package.elements[UPDATE_AUTH_TIME_LOW_IDX].type != ACPI_TYPE_INTEGER || 356 + out_obj->package.elements[UPDATE_AUTH_TIME_HI_IDX].type != ACPI_TYPE_INTEGER || 357 + out_obj->package.elements[UPDATE_EXEC_TIME_LOW_IDX].type != ACPI_TYPE_INTEGER || 358 + out_obj->package.elements[UPDATE_EXEC_TIME_HI_IDX].type != ACPI_TYPE_INTEGER) 359 + goto free_acpi_buffer; 360 + 361 + update_result.status = 362 + out_obj->package.elements[UPDATE_STATUS_IDX].integer.value; 363 + update_result.ext_status = 364 + out_obj->package.elements[UPDATE_EXT_STATUS_IDX].integer.value; 365 + 366 + if (update_result.status != DSM_SUCCEED) { 367 + ret = -EBUSY; 368 + dev_dbg(pfru_dev->parent_dev, "Error Status:%d\n", update_result.status); 369 + dev_dbg(pfru_dev->parent_dev, "Error Extended Status:%d\n", 370 + update_result.ext_status); 371 + 372 + goto free_acpi_buffer; 373 + } 374 + 375 + update_result.low_auth_time = 376 + out_obj->package.elements[UPDATE_AUTH_TIME_LOW_IDX].integer.value; 377 + update_result.high_auth_time = 378 + out_obj->package.elements[UPDATE_AUTH_TIME_HI_IDX].integer.value; 379 + update_result.low_exec_time = 380 + out_obj->package.elements[UPDATE_EXEC_TIME_LOW_IDX].integer.value; 381 + update_result.high_exec_time = 382 + out_obj->package.elements[UPDATE_EXEC_TIME_HI_IDX].integer.value; 383 + 384 + print_update_debug_info(&update_result, pfru_dev); 385 + ret = 0; 386 + 387 + free_acpi_buffer: 388 + kfree(out_obj); 389 + 390 + return ret; 391 + } 392 + 393 + static long pfru_ioctl(struct file *file, unsigned int cmd, unsigned long arg) 394 + { 395 + struct pfru_update_cap_info cap_hdr; 396 + struct pfru_device *pfru_dev = to_pfru_dev(file); 397 + void __user *p = (void __user *)arg; 398 + u32 rev; 399 + int ret; 400 + 401 + switch (cmd) { 402 + case PFRU_IOC_QUERY_CAP: 403 + ret = query_capability(&cap_hdr, pfru_dev); 404 + if (ret) 405 + return ret; 406 + 407 + if (copy_to_user(p, &cap_hdr, sizeof(cap_hdr))) 408 + return -EFAULT; 409 + 410 + return 0; 411 + 412 + case PFRU_IOC_SET_REV: 413 + if (copy_from_user(&rev, p, sizeof(rev))) 414 + return -EFAULT; 415 + 416 + if (!pfru_valid_revid(rev)) 417 + return -EINVAL; 418 + 419 + pfru_dev->rev_id = rev; 420 + 421 + return 0; 422 + 423 + case PFRU_IOC_STAGE: 424 + return start_update(START_STAGE, pfru_dev); 425 + 426 + case PFRU_IOC_ACTIVATE: 427 + return start_update(START_ACTIVATE, pfru_dev); 428 + 429 + case PFRU_IOC_STAGE_ACTIVATE: 430 + return start_update(START_STAGE_ACTIVATE, pfru_dev); 431 + 432 + default: 433 + return -ENOTTY; 434 + } 435 + } 436 + 437 + static ssize_t pfru_write(struct file *file, const char __user *buf, 438 + size_t len, loff_t *ppos) 439 + { 440 + struct pfru_device *pfru_dev = to_pfru_dev(file); 441 + struct pfru_update_cap_info cap; 442 + struct pfru_com_buf_info buf_info; 443 + phys_addr_t phy_addr; 444 + struct iov_iter iter; 445 + struct iovec iov; 446 + char *buf_ptr; 447 + int ret; 448 + 449 + ret = query_buffer(&buf_info, pfru_dev); 450 + if (ret) 451 + return ret; 452 + 453 + if (len > buf_info.buf_size) 454 + return -EINVAL; 455 + 456 + iov.iov_base = (void __user *)buf; 457 + iov.iov_len = len; 458 + iov_iter_init(&iter, WRITE, &iov, 1, len); 459 + 460 + /* map the communication buffer */ 461 + phy_addr = (phys_addr_t)((buf_info.addr_hi << 32) | buf_info.addr_lo); 462 + buf_ptr = memremap(phy_addr, buf_info.buf_size, MEMREMAP_WB); 463 + if (!buf_ptr) 464 + return -ENOMEM; 465 + 466 + if (!copy_from_iter_full(buf_ptr, len, &iter)) { 467 + ret = -EINVAL; 468 + goto unmap; 469 + } 470 + 471 + /* check if the capsule header has a valid version number */ 472 + ret = query_capability(&cap, pfru_dev); 473 + if (ret) 474 + goto unmap; 475 + 476 + if (!applicable_image(buf_ptr, &cap, pfru_dev)) 477 + ret = -EINVAL; 478 + 479 + unmap: 480 + memunmap(buf_ptr); 481 + 482 + return ret ?: len; 483 + } 484 + 485 + static const struct file_operations acpi_pfru_fops = { 486 + .owner = THIS_MODULE, 487 + .write = pfru_write, 488 + .unlocked_ioctl = pfru_ioctl, 489 + .llseek = noop_llseek, 490 + }; 491 + 492 + static int acpi_pfru_remove(struct platform_device *pdev) 493 + { 494 + struct pfru_device *pfru_dev = platform_get_drvdata(pdev); 495 + 496 + misc_deregister(&pfru_dev->miscdev); 497 + 498 + return 0; 499 + } 500 + 501 + static void pfru_put_idx(void *data) 502 + { 503 + struct pfru_device *pfru_dev = data; 504 + 505 + ida_free(&pfru_ida, pfru_dev->index); 506 + } 507 + 508 + static int acpi_pfru_probe(struct platform_device *pdev) 509 + { 510 + acpi_handle handle = ACPI_HANDLE(&pdev->dev); 511 + struct pfru_device *pfru_dev; 512 + int ret; 513 + 514 + if (!acpi_has_method(handle, "_DSM")) { 515 + dev_dbg(&pdev->dev, "Missing _DSM\n"); 516 + return -ENODEV; 517 + } 518 + 519 + pfru_dev = devm_kzalloc(&pdev->dev, sizeof(*pfru_dev), GFP_KERNEL); 520 + if (!pfru_dev) 521 + return -ENOMEM; 522 + 523 + ret = ida_alloc(&pfru_ida, GFP_KERNEL); 524 + if (ret < 0) 525 + return ret; 526 + 527 + pfru_dev->index = ret; 528 + ret = devm_add_action_or_reset(&pdev->dev, pfru_put_idx, pfru_dev); 529 + if (ret) 530 + return ret; 531 + 532 + pfru_dev->rev_id = PFRU_DEFAULT_REV_ID; 533 + pfru_dev->parent_dev = &pdev->dev; 534 + 535 + pfru_dev->miscdev.minor = MISC_DYNAMIC_MINOR; 536 + pfru_dev->miscdev.name = devm_kasprintf(&pdev->dev, GFP_KERNEL, 537 + "pfru%d", pfru_dev->index); 538 + if (!pfru_dev->miscdev.name) 539 + return -ENOMEM; 540 + 541 + pfru_dev->miscdev.nodename = devm_kasprintf(&pdev->dev, GFP_KERNEL, 542 + "acpi_pfr_update%d", pfru_dev->index); 543 + if (!pfru_dev->miscdev.nodename) 544 + return -ENOMEM; 545 + 546 + pfru_dev->miscdev.fops = &acpi_pfru_fops; 547 + pfru_dev->miscdev.parent = &pdev->dev; 548 + 549 + ret = misc_register(&pfru_dev->miscdev); 550 + if (ret) 551 + return ret; 552 + 553 + platform_set_drvdata(pdev, pfru_dev); 554 + 555 + return 0; 556 + } 557 + 558 + static const struct acpi_device_id acpi_pfru_ids[] = { 559 + {"INTC1080"}, 560 + {} 561 + }; 562 + MODULE_DEVICE_TABLE(acpi, acpi_pfru_ids); 563 + 564 + static struct platform_driver acpi_pfru_driver = { 565 + .driver = { 566 + .name = "pfr_update", 567 + .acpi_match_table = acpi_pfru_ids, 568 + }, 569 + .probe = acpi_pfru_probe, 570 + .remove = acpi_pfru_remove, 571 + }; 572 + module_platform_driver(acpi_pfru_driver); 573 + 574 + MODULE_DESCRIPTION("Platform Firmware Runtime Update device driver"); 575 + MODULE_LICENSE("GPL v2");
+26 -46
drivers/acpi/scan.c
··· 2502 2502 } 2503 2503 EXPORT_SYMBOL_GPL(acpi_bus_register_early_device); 2504 2504 2505 - static int acpi_bus_scan_fixed(void) 2505 + static void acpi_bus_scan_fixed(void) 2506 2506 { 2507 - int result = 0; 2508 - 2509 - /* 2510 - * Enumerate all fixed-feature devices. 2511 - */ 2512 2507 if (!(acpi_gbl_FADT.flags & ACPI_FADT_POWER_BUTTON)) { 2513 - struct acpi_device *device = NULL; 2508 + struct acpi_device *adev = NULL; 2514 2509 2515 - result = acpi_add_single_object(&device, NULL, 2516 - ACPI_BUS_TYPE_POWER_BUTTON, false); 2517 - if (result) 2518 - return result; 2519 - 2520 - device->flags.match_driver = true; 2521 - result = device_attach(&device->dev); 2522 - if (result < 0) 2523 - return result; 2524 - 2525 - device_init_wakeup(&device->dev, true); 2510 + acpi_add_single_object(&adev, NULL, ACPI_BUS_TYPE_POWER_BUTTON, 2511 + false); 2512 + if (adev) { 2513 + adev->flags.match_driver = true; 2514 + if (device_attach(&adev->dev) >= 0) 2515 + device_init_wakeup(&adev->dev, true); 2516 + else 2517 + dev_dbg(&adev->dev, "No driver\n"); 2518 + } 2526 2519 } 2527 2520 2528 2521 if (!(acpi_gbl_FADT.flags & ACPI_FADT_SLEEP_BUTTON)) { 2529 - struct acpi_device *device = NULL; 2522 + struct acpi_device *adev = NULL; 2530 2523 2531 - result = acpi_add_single_object(&device, NULL, 2532 - ACPI_BUS_TYPE_SLEEP_BUTTON, false); 2533 - if (result) 2534 - return result; 2535 - 2536 - device->flags.match_driver = true; 2537 - result = device_attach(&device->dev); 2524 + acpi_add_single_object(&adev, NULL, ACPI_BUS_TYPE_SLEEP_BUTTON, 2525 + false); 2526 + if (adev) { 2527 + adev->flags.match_driver = true; 2528 + if (device_attach(&adev->dev) < 0) 2529 + dev_dbg(&adev->dev, "No driver\n"); 2530 + } 2538 2531 } 2539 - 2540 - return result < 0 ? result : 0; 2541 2532 } 2542 2533 2543 2534 static void __init acpi_get_spcr_uart_addr(void) ··· 2549 2558 2550 2559 static bool acpi_scan_initialized; 2551 2560 2552 - int __init acpi_scan_init(void) 2561 + void __init acpi_scan_init(void) 2553 2562 { 2554 - int result; 2555 2563 acpi_status status; 2556 2564 struct acpi_table_stao *stao_ptr; 2557 2565 ··· 2600 2610 /* 2601 2611 * Enumerate devices in the ACPI namespace. 2602 2612 */ 2603 - result = acpi_bus_scan(ACPI_ROOT_OBJECT); 2604 - if (result) 2605 - goto out; 2613 + if (acpi_bus_scan(ACPI_ROOT_OBJECT)) 2614 + goto unlock; 2606 2615 2607 2616 acpi_root = acpi_fetch_acpi_dev(ACPI_ROOT_OBJECT); 2608 2617 if (!acpi_root) 2609 - goto out; 2618 + goto unlock; 2610 2619 2611 2620 /* Fixed feature devices do not exist on HW-reduced platform */ 2612 - if (!acpi_gbl_reduced_hardware) { 2613 - result = acpi_bus_scan_fixed(); 2614 - if (result) { 2615 - acpi_detach_data(acpi_root->handle, 2616 - acpi_scan_drop_device); 2617 - acpi_device_del(acpi_root); 2618 - acpi_bus_put_acpi_device(acpi_root); 2619 - goto out; 2620 - } 2621 - } 2621 + if (!acpi_gbl_reduced_hardware) 2622 + acpi_bus_scan_fixed(); 2622 2623 2623 2624 acpi_turn_off_unused_power_resources(); 2624 2625 2625 2626 acpi_scan_initialized = true; 2626 2627 2627 - out: 2628 + unlock: 2628 2629 mutex_unlock(&acpi_scan_lock); 2629 - return result; 2630 2630 } 2631 2631 2632 2632 static struct acpi_probe_entry *ape;
+7 -2
drivers/acpi/spcr.c
··· 107 107 pr_info("SPCR table version %d\n", table->header.revision); 108 108 109 109 if (table->serial_port.space_id == ACPI_ADR_SPACE_SYSTEM_MEMORY) { 110 - switch (ACPI_ACCESS_BIT_WIDTH(( 111 - table->serial_port.access_width))) { 110 + u32 bit_width = table->serial_port.access_width; 111 + 112 + if (bit_width > ACPI_ACCESS_BIT_MAX) { 113 + pr_err("Unacceptable wide SPCR Access Width. Defaulting to byte size\n"); 114 + bit_width = ACPI_ACCESS_BIT_DEFAULT; 115 + } 116 + switch (ACPI_ACCESS_BIT_WIDTH((bit_width))) { 112 117 default: 113 118 pr_err("Unexpected SPCR Access Width. Defaulting to byte size\n"); 114 119 fallthrough;
+46
include/linux/efi.h
··· 148 148 u32 imagesize; 149 149 } efi_capsule_header_t; 150 150 151 + /* EFI_FIRMWARE_MANAGEMENT_CAPSULE_HEADER */ 152 + struct efi_manage_capsule_header { 153 + u32 ver; 154 + u16 emb_drv_cnt; 155 + u16 payload_cnt; 156 + /* 157 + * Variable-size array of the size given by the sum of 158 + * emb_drv_cnt and payload_cnt. 159 + */ 160 + u64 offset_list[]; 161 + } __packed; 162 + 163 + /* EFI_FIRMWARE_MANAGEMENT_CAPSULE_IMAGE_HEADER */ 164 + struct efi_manage_capsule_image_header { 165 + u32 ver; 166 + efi_guid_t image_type_id; 167 + u8 image_index; 168 + u8 reserved_bytes[3]; 169 + u32 image_size; 170 + u32 vendor_code_size; 171 + /* hw_ins was introduced in version 2 */ 172 + u64 hw_ins; 173 + /* capsule_support was introduced in version 3 */ 174 + u64 capsule_support; 175 + } __packed; 176 + 177 + /* WIN_CERTIFICATE */ 178 + struct win_cert { 179 + u32 len; 180 + u16 rev; 181 + u16 cert_type; 182 + }; 183 + 184 + /* WIN_CERTIFICATE_UEFI_GUID */ 185 + struct win_cert_uefi_guid { 186 + struct win_cert hdr; 187 + efi_guid_t cert_type; 188 + u8 cert_data[]; 189 + }; 190 + 191 + /* EFI_FIRMWARE_IMAGE_AUTHENTICATION */ 192 + struct efi_image_auth { 193 + u64 mon_count; 194 + struct win_cert_uefi_guid auth_info; 195 + }; 196 + 151 197 /* 152 198 * EFI capsule flags 153 199 */
+262
include/uapi/linux/pfrut.h
··· 1 + /* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ 2 + /* 3 + * Platform Firmware Runtime Update header 4 + * 5 + * Copyright(c) 2021 Intel Corporation. All rights reserved. 6 + */ 7 + #ifndef __PFRUT_H__ 8 + #define __PFRUT_H__ 9 + 10 + #include <linux/ioctl.h> 11 + #include <linux/types.h> 12 + 13 + #define PFRUT_IOCTL_MAGIC 0xEE 14 + 15 + /** 16 + * PFRU_IOC_SET_REV - _IOW(PFRUT_IOCTL_MAGIC, 0x01, unsigned int) 17 + * 18 + * Return: 19 + * * 0 - success 20 + * * -EFAULT - fail to read the revision id 21 + * * -EINVAL - user provides an invalid revision id 22 + * 23 + * Set the Revision ID for Platform Firmware Runtime Update. 24 + */ 25 + #define PFRU_IOC_SET_REV _IOW(PFRUT_IOCTL_MAGIC, 0x01, unsigned int) 26 + 27 + /** 28 + * PFRU_IOC_STAGE - _IOW(PFRUT_IOCTL_MAGIC, 0x02, unsigned int) 29 + * 30 + * Return: 31 + * * 0 - success 32 + * * -EINVAL - stage phase returns invalid result 33 + * 34 + * Stage a capsule image from communication buffer and perform authentication. 35 + */ 36 + #define PFRU_IOC_STAGE _IOW(PFRUT_IOCTL_MAGIC, 0x02, unsigned int) 37 + 38 + /** 39 + * PFRU_IOC_ACTIVATE - _IOW(PFRUT_IOCTL_MAGIC, 0x03, unsigned int) 40 + * 41 + * Return: 42 + * * 0 - success 43 + * * -EINVAL - activate phase returns invalid result 44 + * 45 + * Activate a previously staged capsule image. 46 + */ 47 + #define PFRU_IOC_ACTIVATE _IOW(PFRUT_IOCTL_MAGIC, 0x03, unsigned int) 48 + 49 + /** 50 + * PFRU_IOC_STAGE_ACTIVATE - _IOW(PFRUT_IOCTL_MAGIC, 0x04, unsigned int) 51 + * 52 + * Return: 53 + * * 0 - success 54 + * * -EINVAL - stage/activate phase returns invalid result. 55 + * 56 + * Perform both stage and activation action. 57 + */ 58 + #define PFRU_IOC_STAGE_ACTIVATE _IOW(PFRUT_IOCTL_MAGIC, 0x04, unsigned int) 59 + 60 + /** 61 + * PFRU_IOC_QUERY_CAP - _IOR(PFRUT_IOCTL_MAGIC, 0x05, 62 + * struct pfru_update_cap_info) 63 + * 64 + * Return: 65 + * * 0 - success 66 + * * -EINVAL - query phase returns invalid result 67 + * * -EFAULT - the result fails to be copied to userspace 68 + * 69 + * Retrieve information on the Platform Firmware Runtime Update capability. 70 + * The information is a struct pfru_update_cap_info. 71 + */ 72 + #define PFRU_IOC_QUERY_CAP _IOR(PFRUT_IOCTL_MAGIC, 0x05, struct pfru_update_cap_info) 73 + 74 + /** 75 + * struct pfru_payload_hdr - Capsule file payload header. 76 + * 77 + * @sig: Signature of this capsule file. 78 + * @hdr_version: Revision of this header structure. 79 + * @hdr_size: Size of this header, including the OemHeader bytes. 80 + * @hw_ver: The supported firmware version. 81 + * @rt_ver: Version of the code injection image. 82 + * @platform_id: A platform specific GUID to specify the platform what 83 + * this capsule image support. 84 + */ 85 + struct pfru_payload_hdr { 86 + __u32 sig; 87 + __u32 hdr_version; 88 + __u32 hdr_size; 89 + __u32 hw_ver; 90 + __u32 rt_ver; 91 + __u8 platform_id[16]; 92 + }; 93 + 94 + enum pfru_dsm_status { 95 + DSM_SUCCEED = 0, 96 + DSM_FUNC_NOT_SUPPORT = 1, 97 + DSM_INVAL_INPUT = 2, 98 + DSM_HARDWARE_ERR = 3, 99 + DSM_RETRY_SUGGESTED = 4, 100 + DSM_UNKNOWN = 5, 101 + DSM_FUNC_SPEC_ERR = 6, 102 + }; 103 + 104 + /** 105 + * struct pfru_update_cap_info - Runtime update capability information. 106 + * 107 + * @status: Indicator of whether this query succeed. 108 + * @update_cap: Bitmap to indicate whether the feature is supported. 109 + * @code_type: A buffer containing an image type GUID. 110 + * @fw_version: Platform firmware version. 111 + * @code_rt_version: Code injection runtime version for anti-rollback. 112 + * @drv_type: A buffer containing an image type GUID. 113 + * @drv_rt_version: The version of the driver update runtime code. 114 + * @drv_svn: The secure version number(SVN) of the driver update runtime code. 115 + * @platform_id: A buffer containing a platform ID GUID. 116 + * @oem_id: A buffer containing an OEM ID GUID. 117 + * @oem_info_len: Length of the buffer containing the vendor specific information. 118 + */ 119 + struct pfru_update_cap_info { 120 + __u32 status; 121 + __u32 update_cap; 122 + 123 + __u8 code_type[16]; 124 + __u32 fw_version; 125 + __u32 code_rt_version; 126 + 127 + __u8 drv_type[16]; 128 + __u32 drv_rt_version; 129 + __u32 drv_svn; 130 + 131 + __u8 platform_id[16]; 132 + __u8 oem_id[16]; 133 + 134 + __u32 oem_info_len; 135 + }; 136 + 137 + /** 138 + * struct pfru_com_buf_info - Communication buffer information. 139 + * 140 + * @status: Indicator of whether this query succeed. 141 + * @ext_status: Implementation specific query result. 142 + * @addr_lo: Low 32bit physical address of the communication buffer to hold 143 + * a runtime update package. 144 + * @addr_hi: High 32bit physical address of the communication buffer to hold 145 + * a runtime update package. 146 + * @buf_size: Maximum size in bytes of the communication buffer. 147 + */ 148 + struct pfru_com_buf_info { 149 + __u32 status; 150 + __u32 ext_status; 151 + __u64 addr_lo; 152 + __u64 addr_hi; 153 + __u32 buf_size; 154 + }; 155 + 156 + /** 157 + * struct pfru_updated_result - Platform firmware runtime update result information. 158 + * @status: Indicator of whether this update succeed. 159 + * @ext_status: Implementation specific update result. 160 + * @low_auth_time: Low 32bit value of image authentication time in nanosecond. 161 + * @high_auth_time: High 32bit value of image authentication time in nanosecond. 162 + * @low_exec_time: Low 32bit value of image execution time in nanosecond. 163 + * @high_exec_time: High 32bit value of image execution time in nanosecond. 164 + */ 165 + struct pfru_updated_result { 166 + __u32 status; 167 + __u32 ext_status; 168 + __u64 low_auth_time; 169 + __u64 high_auth_time; 170 + __u64 low_exec_time; 171 + __u64 high_exec_time; 172 + }; 173 + 174 + /** 175 + * struct pfrt_log_data_info - Log Data from telemetry service. 176 + * @status: Indicator of whether this update succeed. 177 + * @ext_status: Implementation specific update result. 178 + * @chunk1_addr_lo: Low 32bit physical address of the telemetry data chunk1 179 + * starting address. 180 + * @chunk1_addr_hi: High 32bit physical address of the telemetry data chunk1 181 + * starting address. 182 + * @chunk2_addr_lo: Low 32bit physical address of the telemetry data chunk2 183 + * starting address. 184 + * @chunk2_addr_hi: High 32bit physical address of the telemetry data chunk2 185 + * starting address. 186 + * @max_data_size: Maximum supported size of data of all data chunks combined. 187 + * @chunk1_size: Data size in bytes of the telemetry data chunk1 buffer. 188 + * @chunk2_size: Data size in bytes of the telemetry data chunk2 buffer. 189 + * @rollover_cnt: Number of times telemetry data buffer is overwritten 190 + * since telemetry buffer reset. 191 + * @reset_cnt: Number of times telemetry services resets that results in 192 + * rollover count and data chunk buffers are reset. 193 + */ 194 + struct pfrt_log_data_info { 195 + __u32 status; 196 + __u32 ext_status; 197 + __u64 chunk1_addr_lo; 198 + __u64 chunk1_addr_hi; 199 + __u64 chunk2_addr_lo; 200 + __u64 chunk2_addr_hi; 201 + __u32 max_data_size; 202 + __u32 chunk1_size; 203 + __u32 chunk2_size; 204 + __u32 rollover_cnt; 205 + __u32 reset_cnt; 206 + }; 207 + 208 + /** 209 + * struct pfrt_log_info - Telemetry log information. 210 + * @log_level: The telemetry log level. 211 + * @log_type: The telemetry log type(history and execution). 212 + * @log_revid: The telemetry log revision id. 213 + */ 214 + struct pfrt_log_info { 215 + __u32 log_level; 216 + __u32 log_type; 217 + __u32 log_revid; 218 + }; 219 + 220 + /** 221 + * PFRT_LOG_IOC_SET_INFO - _IOW(PFRUT_IOCTL_MAGIC, 0x06, 222 + * struct pfrt_log_info) 223 + * 224 + * Return: 225 + * * 0 - success 226 + * * -EFAULT - fail to get the setting parameter 227 + * * -EINVAL - fail to set the log level 228 + * 229 + * Set the PFRT log level and log type. The input information is 230 + * a struct pfrt_log_info. 231 + */ 232 + #define PFRT_LOG_IOC_SET_INFO _IOW(PFRUT_IOCTL_MAGIC, 0x06, struct pfrt_log_info) 233 + 234 + /** 235 + * PFRT_LOG_IOC_GET_INFO - _IOR(PFRUT_IOCTL_MAGIC, 0x07, 236 + * struct pfrt_log_info) 237 + * 238 + * Return: 239 + * * 0 - success 240 + * * -EINVAL - fail to get the log level 241 + * * -EFAULT - fail to copy the result back to userspace 242 + * 243 + * Retrieve log level and log type of the telemetry. The information is 244 + * a struct pfrt_log_info. 245 + */ 246 + #define PFRT_LOG_IOC_GET_INFO _IOR(PFRUT_IOCTL_MAGIC, 0x07, struct pfrt_log_info) 247 + 248 + /** 249 + * PFRT_LOG_IOC_GET_DATA_INFO - _IOR(PFRUT_IOCTL_MAGIC, 0x08, 250 + * struct pfrt_log_data_info) 251 + * 252 + * Return: 253 + * * 0 - success 254 + * * -EINVAL - fail to get the log buffer information 255 + * * -EFAULT - fail to copy the log buffer information to userspace 256 + * 257 + * Retrieve data information about the telemetry. The information 258 + * is a struct pfrt_log_data_info. 259 + */ 260 + #define PFRT_LOG_IOC_GET_DATA_INFO _IOR(PFRUT_IOCTL_MAGIC, 0x08, struct pfrt_log_data_info) 261 + 262 + #endif /* __PFRUT_H__ */
+1
tools/power/acpi/.gitignore
··· 2 2 /acpidbg 3 3 /acpidump 4 4 /ec 5 + /pfrut 5 6 /include/
+8 -8
tools/power/acpi/Makefile
··· 9 9 10 10 .NOTPARALLEL: 11 11 12 - all: acpidbg acpidump ec 13 - clean: acpidbg_clean acpidump_clean ec_clean 14 - install: acpidbg_install acpidump_install ec_install 15 - uninstall: acpidbg_uninstall acpidump_uninstall ec_uninstall 12 + all: acpidbg acpidump ec pfrut 13 + clean: acpidbg_clean acpidump_clean ec_clean pfrut_clean 14 + install: acpidbg_install acpidump_install ec_install pfrut_install 15 + uninstall: acpidbg_uninstall acpidump_uninstall ec_uninstall pfrut_uninstall 16 16 17 - acpidbg acpidump ec: FORCE 17 + acpidbg acpidump ec pfrut: FORCE 18 18 $(call descend,tools/$@,all) 19 - acpidbg_clean acpidump_clean ec_clean: 19 + acpidbg_clean acpidump_clean ec_clean pfrut_clean: 20 20 $(call descend,tools/$(@:_clean=),clean) 21 - acpidbg_install acpidump_install ec_install: 21 + acpidbg_install acpidump_install ec_install pfrut_install: 22 22 $(call descend,tools/$(@:_install=),install) 23 - acpidbg_uninstall acpidump_uninstall ec_uninstall: 23 + acpidbg_uninstall acpidump_uninstall ec_uninstall pfrut_uninstall: 24 24 $(call descend,tools/$(@:_uninstall=),uninstall) 25 25 26 26 .PHONY: FORCE
+1 -1
tools/power/acpi/Makefile.rules
··· 9 9 toolobjs := $(addprefix $(objdir),$(TOOL_OBJS)) 10 10 $(OUTPUT)$(TOOL): $(toolobjs) FORCE 11 11 $(ECHO) " LD " $(subst $(OUTPUT),,$@) 12 - $(QUIET) $(LD) $(CFLAGS) $(LDFLAGS) $(toolobjs) -L$(OUTPUT) -o $@ 12 + $(QUIET) $(LD) $(CFLAGS) $(toolobjs) $(LDFLAGS) -L$(OUTPUT) -o $@ 13 13 $(ECHO) " STRIP " $(subst $(OUTPUT),,$@) 14 14 $(QUIET) $(STRIPCMD) $@ 15 15
+137
tools/power/acpi/man/pfrut.8
··· 1 + .TH "PFRUT" "8" "October 2021" "pfrut 1.0" "" 2 + .hy 3 + .SH Name 4 + .PP 5 + pfrut \- Platform Firmware Runtime Update and Telemetry tool 6 + .SH SYNOPSIS 7 + .PP 8 + \f[B]pfrut\f[R] [\f[I]Options\f[R]] 9 + .SH DESCRIPTION 10 + .PP 11 + The PFRUT(Platform Firmware Runtime Update and Telemetry) kernel interface is designed 12 + to 13 + .PD 0 14 + .P 15 + .PD 16 + interact with the platform firmware interface defined in the 17 + .PD 0 18 + .P 19 + .PD 20 + Management Mode Firmware Runtime 21 + Update (https://uefi.org/sites/default/files/resources/Intel_MM_OS_Interface_Spec_Rev100.pdf) 22 + .PD 0 23 + .P 24 + .PD 25 + \f[B]pfrut\f[R] is the tool to interact with the kernel interface. 26 + .PD 0 27 + .P 28 + .PD 29 + .SH OPTIONS 30 + .TP 31 + .B \f[B]\-h\f[R], \f[B]\-\-help\f[R] 32 + Display helper information. 33 + .TP 34 + .B \f[B]\-l\f[R], \f[B]\-\-load\f[R] 35 + Load the capsule file into the system. 36 + To be more specific, the capsule file will be copied to the 37 + communication buffer. 38 + .TP 39 + .B \f[B]\-s\f[R], \f[B]\-\-stage\f[R] 40 + Stage the capsule image from communication buffer into Management Mode 41 + and perform authentication. 42 + .TP 43 + .B \f[B]\-a\f[R], \f[B]\-\-activate\f[R] 44 + Activate a previous staged capsule image. 45 + .TP 46 + .B \f[B]\-u\f[R], \f[B]\-\-update\f[R] 47 + Perform both stage and activation actions. 48 + .TP 49 + .B \f[B]\-q\f[R], \f[B]\-\-query\f[R] 50 + Query the update capability. 51 + .TP 52 + .B \f[B]\-d\f[R], \f[B]\-\-setrev\f[R] 53 + Set the revision ID of code injection/driver update. 54 + .TP 55 + .B \f[B]\-D\f[R], \f[B]\-\-setrevlog\f[R] 56 + Set the revision ID of telemetry. 57 + .TP 58 + .B \f[B]\-G\f[R], \f[B]\-\-getloginfo\f[R] 59 + Get telemetry log information and print it out. 60 + .TP 61 + .B \f[B]\-T\f[R], \f[B]\-\-type\f[R] 62 + Set the telemetry log data type. 63 + .TP 64 + .B \f[B]\-L\f[R], \f[B]\-\-level\f[R] 65 + Set the telemetry log level. 66 + .TP 67 + .B \f[B]\-R\f[R], \f[B]\-\-read\f[R] 68 + Read all the telemetry data and print it out. 69 + .SH EXAMPLES 70 + .PP 71 + \f[B]pfrut \-G\f[R] 72 + .PP 73 + log_level:4 74 + .PD 0 75 + .P 76 + .PD 77 + log_type:0 78 + .PD 0 79 + .P 80 + .PD 81 + log_revid:2 82 + .PD 0 83 + .P 84 + .PD 85 + max_data_size:65536 86 + .PD 0 87 + .P 88 + .PD 89 + chunk1_size:0 90 + .PD 0 91 + .P 92 + .PD 93 + chunk2_size:1401 94 + .PD 0 95 + .P 96 + .PD 97 + rollover_cnt:0 98 + .PD 0 99 + .P 100 + .PD 101 + reset_cnt:4 102 + .PP 103 + \f[B]pfru \-q\f[R] 104 + .PP 105 + code injection image type:794bf8b2\-6e7b\-454e\-885f\-3fb9bb185402 106 + .PD 0 107 + .P 108 + .PD 109 + fw_version:0 110 + .PD 0 111 + .P 112 + .PD 113 + code_rt_version:1 114 + .PD 0 115 + .P 116 + .PD 117 + driver update image type:0e5f0b14\-f849\-7945\-ad81\-bc7b6d2bb245 118 + .PD 0 119 + .P 120 + .PD 121 + drv_rt_version:0 122 + .PD 0 123 + .P 124 + .PD 125 + drv_svn:0 126 + .PD 0 127 + .P 128 + .PD 129 + platform id:39214663\-b1a8\-4eaa\-9024\-f2bb53ea4723 130 + .PD 0 131 + .P 132 + .PD 133 + oem id:a36db54f\-ea2a\-e14e\-b7c4\-b5780e51ba3d 134 + .PP 135 + \f[B]pfrut \-l yours.cap \-u \-T 1 \-L 4\f[R] 136 + .SH AUTHORS 137 + Chen Yu.
+23
tools/power/acpi/tools/pfrut/Makefile
··· 1 + # SPDX-License-Identifier: GPL-2.0+ 2 + 3 + include ../../Makefile.config 4 + 5 + TOOL = pfrut 6 + EXTRA_INSTALL = install-man 7 + EXTRA_UNINSTALL = uninstall-man 8 + 9 + CFLAGS += -Wall -O2 10 + CFLAGS += -DPFRUT_HEADER='"../../../../../include/uapi/linux/pfrut.h"' 11 + LDFLAGS += -luuid 12 + 13 + TOOL_OBJS = \ 14 + pfrut.o 15 + 16 + include ../../Makefile.rules 17 + 18 + install-man: $(srctree)/man/pfrut.8 19 + $(ECHO) " INST " pfrut.8 20 + $(QUIET) $(INSTALL_DATA) -D $< $(DESTDIR)$(mandir)/man8/pfrut.8 21 + uninstall-man: 22 + $(ECHO) " UNINST " pfrut.8 23 + $(QUIET) rm -f $(DESTDIR)$(mandir)/man8/pfrut.8
+424
tools/power/acpi/tools/pfrut/pfrut.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + /* 3 + * Platform Firmware Runtime Update tool to do Management 4 + * Mode code injection/driver update and telemetry retrieval. 5 + * 6 + * This tool uses the interfaces provided by pfr_update and 7 + * pfr_telemetry drivers. These interfaces are exposed via 8 + * /dev/pfr_update and /dev/pfr_telemetry. Write operation 9 + * on the /dev/pfr_update is to load the EFI capsule into 10 + * kernel space. Mmap/read operations on /dev/pfr_telemetry 11 + * could be used to read the telemetry data to user space. 12 + */ 13 + #define _GNU_SOURCE 14 + #include <stdio.h> 15 + #include <stdlib.h> 16 + #include <string.h> 17 + #include <sys/types.h> 18 + #include <sys/stat.h> 19 + #include <fcntl.h> 20 + #include <unistd.h> 21 + #include <getopt.h> 22 + #include <sys/ioctl.h> 23 + #include <sys/mman.h> 24 + #include <uuid/uuid.h> 25 + #include PFRUT_HEADER 26 + 27 + char *capsule_name; 28 + int action, query_cap, log_type, log_level, log_read, log_getinfo, 29 + revid, log_revid; 30 + int set_log_level, set_log_type, 31 + set_revid, set_log_revid; 32 + 33 + char *progname; 34 + 35 + #define LOG_ERR 0 36 + #define LOG_WARN 1 37 + #define LOG_INFO 2 38 + #define LOG_VERB 4 39 + #define LOG_EXEC_IDX 0 40 + #define LOG_HISTORY_IDX 1 41 + #define REVID_1 1 42 + #define REVID_2 2 43 + 44 + static int valid_log_level(int level) 45 + { 46 + return level == LOG_ERR || level == LOG_WARN || 47 + level == LOG_INFO || level == LOG_VERB; 48 + } 49 + 50 + static int valid_log_type(int type) 51 + { 52 + return type == LOG_EXEC_IDX || type == LOG_HISTORY_IDX; 53 + } 54 + 55 + static inline int valid_log_revid(int id) 56 + { 57 + return id == REVID_1 || id == REVID_2; 58 + } 59 + 60 + static void help(void) 61 + { 62 + fprintf(stderr, 63 + "usage: %s [OPTIONS]\n" 64 + " code injection:\n" 65 + " -l, --load\n" 66 + " -s, --stage\n" 67 + " -a, --activate\n" 68 + " -u, --update [stage and activate]\n" 69 + " -q, --query\n" 70 + " -d, --revid update\n" 71 + " telemetry:\n" 72 + " -G, --getloginfo\n" 73 + " -T, --type(0:execution, 1:history)\n" 74 + " -L, --level(0, 1, 2, 4)\n" 75 + " -R, --read\n" 76 + " -D, --revid log\n", 77 + progname); 78 + } 79 + 80 + char *option_string = "l:sauqd:GT:L:RD:h"; 81 + static struct option long_options[] = { 82 + {"load", required_argument, 0, 'l'}, 83 + {"stage", no_argument, 0, 's'}, 84 + {"activate", no_argument, 0, 'a'}, 85 + {"update", no_argument, 0, 'u'}, 86 + {"query", no_argument, 0, 'q'}, 87 + {"getloginfo", no_argument, 0, 'G'}, 88 + {"type", required_argument, 0, 'T'}, 89 + {"level", required_argument, 0, 'L'}, 90 + {"read", no_argument, 0, 'R'}, 91 + {"setrev", required_argument, 0, 'd'}, 92 + {"setrevlog", required_argument, 0, 'D'}, 93 + {"help", no_argument, 0, 'h'}, 94 + {} 95 + }; 96 + 97 + static void parse_options(int argc, char **argv) 98 + { 99 + int option_index = 0; 100 + char *pathname; 101 + int opt; 102 + 103 + pathname = strdup(argv[0]); 104 + progname = basename(pathname); 105 + 106 + while ((opt = getopt_long_only(argc, argv, option_string, 107 + long_options, &option_index)) != -1) { 108 + switch (opt) { 109 + case 'l': 110 + capsule_name = optarg; 111 + break; 112 + case 's': 113 + action = 1; 114 + break; 115 + case 'a': 116 + action = 2; 117 + break; 118 + case 'u': 119 + action = 3; 120 + break; 121 + case 'q': 122 + query_cap = 1; 123 + break; 124 + case 'G': 125 + log_getinfo = 1; 126 + break; 127 + case 'T': 128 + log_type = atoi(optarg); 129 + set_log_type = 1; 130 + break; 131 + case 'L': 132 + log_level = atoi(optarg); 133 + set_log_level = 1; 134 + break; 135 + case 'R': 136 + log_read = 1; 137 + break; 138 + case 'd': 139 + revid = atoi(optarg); 140 + set_revid = 1; 141 + break; 142 + case 'D': 143 + log_revid = atoi(optarg); 144 + set_log_revid = 1; 145 + break; 146 + case 'h': 147 + help(); 148 + exit(0); 149 + default: 150 + break; 151 + } 152 + } 153 + } 154 + 155 + void print_cap(struct pfru_update_cap_info *cap) 156 + { 157 + char *uuid; 158 + 159 + uuid = malloc(37); 160 + if (!uuid) { 161 + perror("Can not allocate uuid buffer\n"); 162 + exit(1); 163 + } 164 + 165 + uuid_unparse(cap->code_type, uuid); 166 + printf("code injection image type:%s\n", uuid); 167 + printf("fw_version:%d\n", cap->fw_version); 168 + printf("code_rt_version:%d\n", cap->code_rt_version); 169 + 170 + uuid_unparse(cap->drv_type, uuid); 171 + printf("driver update image type:%s\n", uuid); 172 + printf("drv_rt_version:%d\n", cap->drv_rt_version); 173 + printf("drv_svn:%d\n", cap->drv_svn); 174 + 175 + uuid_unparse(cap->platform_id, uuid); 176 + printf("platform id:%s\n", uuid); 177 + uuid_unparse(cap->oem_id, uuid); 178 + printf("oem id:%s\n", uuid); 179 + printf("oem information length:%d\n", cap->oem_info_len); 180 + 181 + free(uuid); 182 + } 183 + 184 + int main(int argc, char *argv[]) 185 + { 186 + int fd_update, fd_update_log, fd_capsule; 187 + struct pfrt_log_data_info data_info; 188 + struct pfrt_log_info info; 189 + struct pfru_update_cap_info cap; 190 + void *addr_map_capsule; 191 + struct stat st; 192 + char *log_buf; 193 + int ret = 0; 194 + 195 + if (getuid() != 0) { 196 + printf("Please run the tool as root - Exiting.\n"); 197 + return 1; 198 + } 199 + 200 + parse_options(argc, argv); 201 + 202 + fd_update = open("/dev/acpi_pfr_update0", O_RDWR); 203 + if (fd_update < 0) { 204 + printf("PFRU device not supported - Quit...\n"); 205 + return 1; 206 + } 207 + 208 + fd_update_log = open("/dev/acpi_pfr_telemetry0", O_RDWR); 209 + if (fd_update_log < 0) { 210 + printf("PFRT device not supported - Quit...\n"); 211 + return 1; 212 + } 213 + 214 + if (query_cap) { 215 + ret = ioctl(fd_update, PFRU_IOC_QUERY_CAP, &cap); 216 + if (ret) 217 + perror("Query Update Capability info failed."); 218 + else 219 + print_cap(&cap); 220 + 221 + close(fd_update); 222 + close(fd_update_log); 223 + 224 + return ret; 225 + } 226 + 227 + if (log_getinfo) { 228 + ret = ioctl(fd_update_log, PFRT_LOG_IOC_GET_DATA_INFO, &data_info); 229 + if (ret) { 230 + perror("Get telemetry data info failed."); 231 + close(fd_update); 232 + close(fd_update_log); 233 + 234 + return 1; 235 + } 236 + 237 + ret = ioctl(fd_update_log, PFRT_LOG_IOC_GET_INFO, &info); 238 + if (ret) { 239 + perror("Get telemetry info failed."); 240 + close(fd_update); 241 + close(fd_update_log); 242 + 243 + return 1; 244 + } 245 + 246 + printf("log_level:%d\n", info.log_level); 247 + printf("log_type:%d\n", info.log_type); 248 + printf("log_revid:%d\n", info.log_revid); 249 + printf("max_data_size:%d\n", data_info.max_data_size); 250 + printf("chunk1_size:%d\n", data_info.chunk1_size); 251 + printf("chunk2_size:%d\n", data_info.chunk2_size); 252 + printf("rollover_cnt:%d\n", data_info.rollover_cnt); 253 + printf("reset_cnt:%d\n", data_info.reset_cnt); 254 + 255 + return 0; 256 + } 257 + 258 + info.log_level = -1; 259 + info.log_type = -1; 260 + info.log_revid = -1; 261 + 262 + if (set_log_level) { 263 + if (!valid_log_level(log_level)) { 264 + printf("Invalid log level %d\n", 265 + log_level); 266 + } else { 267 + info.log_level = log_level; 268 + } 269 + } 270 + 271 + if (set_log_type) { 272 + if (!valid_log_type(log_type)) { 273 + printf("Invalid log type %d\n", 274 + log_type); 275 + } else { 276 + info.log_type = log_type; 277 + } 278 + } 279 + 280 + if (set_log_revid) { 281 + if (!valid_log_revid(log_revid)) { 282 + printf("Invalid log revid %d, unchanged.\n", 283 + log_revid); 284 + } else { 285 + info.log_revid = log_revid; 286 + } 287 + } 288 + 289 + ret = ioctl(fd_update_log, PFRT_LOG_IOC_SET_INFO, &info); 290 + if (ret) { 291 + perror("Log information set failed.(log_level, log_type, log_revid)"); 292 + close(fd_update); 293 + close(fd_update_log); 294 + 295 + return 1; 296 + } 297 + 298 + if (set_revid) { 299 + ret = ioctl(fd_update, PFRU_IOC_SET_REV, &revid); 300 + if (ret) { 301 + perror("pfru update revid set failed"); 302 + close(fd_update); 303 + close(fd_update_log); 304 + 305 + return 1; 306 + } 307 + 308 + printf("pfru update revid set to %d\n", revid); 309 + } 310 + 311 + if (capsule_name) { 312 + fd_capsule = open(capsule_name, O_RDONLY); 313 + if (fd_capsule < 0) { 314 + perror("Can not open capsule file..."); 315 + close(fd_update); 316 + close(fd_update_log); 317 + 318 + return 1; 319 + } 320 + 321 + if (fstat(fd_capsule, &st) < 0) { 322 + perror("Can not fstat capsule file..."); 323 + close(fd_capsule); 324 + close(fd_update); 325 + close(fd_update_log); 326 + 327 + return 1; 328 + } 329 + 330 + addr_map_capsule = mmap(NULL, st.st_size, PROT_READ, MAP_SHARED, 331 + fd_capsule, 0); 332 + if (addr_map_capsule == MAP_FAILED) { 333 + perror("Failed to mmap capsule file."); 334 + close(fd_capsule); 335 + close(fd_update); 336 + close(fd_update_log); 337 + 338 + return 1; 339 + } 340 + 341 + ret = write(fd_update, (char *)addr_map_capsule, st.st_size); 342 + printf("Load %d bytes of capsule file into the system\n", 343 + ret); 344 + 345 + if (ret == -1) { 346 + perror("Failed to load capsule file"); 347 + close(fd_capsule); 348 + close(fd_update); 349 + close(fd_update_log); 350 + 351 + return 1; 352 + } 353 + 354 + munmap(addr_map_capsule, st.st_size); 355 + close(fd_capsule); 356 + printf("Load done.\n"); 357 + } 358 + 359 + if (action) { 360 + if (action == 1) { 361 + ret = ioctl(fd_update, PFRU_IOC_STAGE, NULL); 362 + } else if (action == 2) { 363 + ret = ioctl(fd_update, PFRU_IOC_ACTIVATE, NULL); 364 + } else if (action == 3) { 365 + ret = ioctl(fd_update, PFRU_IOC_STAGE_ACTIVATE, NULL); 366 + } else { 367 + close(fd_update); 368 + close(fd_update_log); 369 + 370 + return 1; 371 + } 372 + printf("Update finished, return %d\n", ret); 373 + } 374 + 375 + close(fd_update); 376 + 377 + if (log_read) { 378 + void *p_mmap; 379 + int max_data_sz; 380 + 381 + ret = ioctl(fd_update_log, PFRT_LOG_IOC_GET_DATA_INFO, &data_info); 382 + if (ret) { 383 + perror("Get telemetry data info failed."); 384 + close(fd_update_log); 385 + 386 + return 1; 387 + } 388 + 389 + max_data_sz = data_info.max_data_size; 390 + if (!max_data_sz) { 391 + printf("No telemetry data available.\n"); 392 + close(fd_update_log); 393 + 394 + return 1; 395 + } 396 + 397 + log_buf = malloc(max_data_sz + 1); 398 + if (!log_buf) { 399 + perror("log_buf allocate failed."); 400 + close(fd_update_log); 401 + 402 + return 1; 403 + } 404 + 405 + p_mmap = mmap(NULL, max_data_sz, PROT_READ, MAP_SHARED, fd_update_log, 0); 406 + if (p_mmap == MAP_FAILED) { 407 + perror("mmap error."); 408 + close(fd_update_log); 409 + 410 + return 1; 411 + } 412 + 413 + memcpy(log_buf, p_mmap, max_data_sz); 414 + log_buf[max_data_sz] = '\0'; 415 + printf("%s\n", log_buf); 416 + free(log_buf); 417 + 418 + munmap(p_mmap, max_data_sz); 419 + } 420 + 421 + close(fd_update_log); 422 + 423 + return 0; 424 + }