Message ID | 838245e376c7e6fd0fe1ef55d004ed53763846a2.1634310710.git.yu.c.chen@intel.com (mailing list archive) |
---|---|
State | Superseded, archived |
Headers | show |
Series | Introduce Platform Firmware Runtime Update and Telemetry drivers | expand |
On Sat, Oct 16, 2021 at 06:44:31PM +0800, Chen Yu wrote: > Platform Firmware Runtime Update(PFRU) Telemetry Service is part of RoT > (Root of Trust), which allows PFRU handler and other PFRU drivers to > produce telemetry data to upper layer OS consumer at runtime. > > The linux provides interfaces for the user to query the parameters of > telemetry data, and the user could read out the telemetry data > accordingly. What type of interface is this? How does userspace interact with it? > > Also add the ABI documentation. Add it where? > > Typical log looks like: > [SmmRuntimeUpdateHandler.ProcessSmmRuntimeUpdate] > ProcessSmmRuntimeUpdate = START, Action = 2 > [SmmRuntimeUpdateHandler.ProcessSmmRuntimeUpdate] > FwVersion = 0, CodeInjectionVersion = 1 > [ShadowSmmRuntimeUpdateImage] > Image = 0x74D9B000, ImageSize = 0x1172 > [ProcessSmmRuntimeUpdate] > ShadowSmmRuntimeUpdateImage.Status = Success > [ValidateSmmRuntimeUpdateImage] > CapsuleHeader.CapsuleGuid = 6DCBD5ED-E82D-4C44-BDA1-7194199AD92A > [ValidateSmmRuntimeUpdateImage] > FmpCapHeader.Version = 1 > [ValidateSmmRuntimeUpdateImage] > FmpCapImageHeader.UpdateImageTypeId = B2F84B79-7B6E-4E45-885F-3FB9BB185402 > [ValidateSmmRuntimeUpdateImage] > SmmRuntimeUpdateVerifyImageWithDenylist.Status = Success > [ValidateSmmRuntimeUpdateImage] > SmmRuntimeUpdateVerifyImageWithAllowlist.Status = Success > [SmmCodeInjectionVerifyPayloadHeader] > PayloadHeader.Signature = 0x31494353 > [SmmCodeInjectionVerifyPayloadHeader] > PayloadHeader.PlatformId = 63462139-A8B1-AA4E-9024-F2BB53EA4723 > [SmmCodeInjectionVerifyPayloadHeader] > PayloadHeader.SupportedSmmFirmwareVersion = 0, > PayloadHeader.SmmCodeInjectionRuntimeVersion = 1 > [ProcessSmmRuntimeUpdate] > ValidateSmmRuntimeUpdateImage.Status = Success > CPU CSR[0B102D28] Before = 7FBF830E > CPU CSR[0B102D28] After = 7FBF8310 > [ProcessSmmRuntimeUpdate] ProcessSmmCodeInjection.Status = Success > [SmmRuntimeUpdateHandler.ProcessSmmRuntimeUpdate] > ProcessSmmRuntimeUpdate = End, Status = Success This log does not make any sense to me, where is it from? Why the odd line-wrapping? > > Signed-off-by: Chen Yu <yu.c.chen@intel.com> > --- > v4: Remove the telemetry kernel Kconfig and combine it with pfru > (Rafael J. Wysocki) > Remove redundant parens. (Rafael J. Wysocki) > v3: Use __u32 instead of int and __64 instead of unsigned long > in include/uapi/linux/pfru.h (Greg Kroah-Hartman) > Rename the structure in uapi to start with a prefix pfru so as > to avoid confusing in the global namespace. (Greg Kroah-Hartman) > v2: Do similar clean up as pfru_update driver: > Add sanity check for duplicated instance of ACPI device. > Update the driver to work with allocated telem_device objects. > (Mike Rapoport) > For each switch case pair, get rid of the magic case numbers > and add a default clause with the error handling. > (Mike Rapoport) > --- > drivers/acpi/pfru/pfru_update.c | 380 +++++++++++++++++++++++++++++++- > include/uapi/linux/pfru.h | 43 ++++ > 2 files changed, 421 insertions(+), 2 deletions(-) > > diff --git a/drivers/acpi/pfru/pfru_update.c b/drivers/acpi/pfru/pfru_update.c > index f57a39e79808..fe5b1bf0aeb3 100644 > --- a/drivers/acpi/pfru/pfru_update.c > +++ b/drivers/acpi/pfru/pfru_update.c > @@ -55,6 +55,27 @@ enum update_index { > UPDATE_NR_IDX, > }; > > +enum log_index { > + LOG_STATUS_IDX, > + LOG_EXT_STATUS_IDX, > + LOG_MAX_SZ_IDX, > + LOG_CHUNK1_LO_IDX, > + LOG_CHUNK1_HI_IDX, > + LOG_CHUNK1_SZ_IDX, > + LOG_CHUNK2_LO_IDX, > + LOG_CHUNK2_HI_IDX, > + LOG_CHUNK2_SZ_IDX, > + LOG_ROLLOVER_CNT_IDX, > + LOG_RESET_CNT_IDX, > + LOG_NR_IDX, > +}; > + > +struct pfru_log_device { > + struct device *dev; > + guid_t uuid; > + struct pfru_log_info info; > +}; > + > struct pfru_device { > guid_t uuid, code_uuid, drv_uuid; > int rev_id; > @@ -62,6 +83,299 @@ struct pfru_device { > }; > > static struct pfru_device *pfru_dev; > +static struct pfru_log_device *pfru_log_dev; > + > +static int get_pfru_log_data_info(struct pfru_log_data_info *data_info, > + int log_type) > +{ > + union acpi_object *out_obj, in_obj, in_buf; > + acpi_handle handle; > + int ret = -EINVAL; > + > + handle = ACPI_HANDLE(pfru_log_dev->dev); > + > + memset(&in_obj, 0, sizeof(in_obj)); > + memset(&in_buf, 0, sizeof(in_buf)); > + in_obj.type = ACPI_TYPE_PACKAGE; > + in_obj.package.count = 1; > + in_obj.package.elements = &in_buf; > + in_buf.type = ACPI_TYPE_INTEGER; > + in_buf.integer.value = log_type; > + > + out_obj = acpi_evaluate_dsm_typed(handle, &pfru_log_dev->uuid, > + pfru_log_dev->info.log_revid, FUNC_GET_DATA, > + &in_obj, ACPI_TYPE_PACKAGE); > + if (!out_obj) > + return ret; > + > + if (out_obj->package.count < LOG_NR_IDX) > + goto free_acpi_buffer; > + > + if (out_obj->package.elements[LOG_STATUS_IDX].type != ACPI_TYPE_INTEGER) > + goto free_acpi_buffer; > + > + data_info->status = out_obj->package.elements[LOG_STATUS_IDX].integer.value; > + > + if (out_obj->package.elements[LOG_EXT_STATUS_IDX].type != ACPI_TYPE_INTEGER) > + goto free_acpi_buffer; > + > + data_info->ext_status = > + out_obj->package.elements[LOG_EXT_STATUS_IDX].integer.value; > + > + if (out_obj->package.elements[LOG_MAX_SZ_IDX].type != ACPI_TYPE_INTEGER) > + goto free_acpi_buffer; > + > + data_info->max_data_size = > + out_obj->package.elements[LOG_MAX_SZ_IDX].integer.value; > + > + if (out_obj->package.elements[LOG_CHUNK1_LO_IDX].type != ACPI_TYPE_INTEGER) > + goto free_acpi_buffer; > + > + data_info->chunk1_addr_lo = > + out_obj->package.elements[LOG_CHUNK1_LO_IDX].integer.value; > + > + if (out_obj->package.elements[LOG_CHUNK1_HI_IDX].type != ACPI_TYPE_INTEGER) > + goto free_acpi_buffer; > + > + data_info->chunk1_addr_hi = > + out_obj->package.elements[LOG_CHUNK1_HI_IDX].integer.value; > + > + if (out_obj->package.elements[LOG_CHUNK1_SZ_IDX].type != ACPI_TYPE_INTEGER) > + goto free_acpi_buffer; > + > + data_info->chunk1_size = > + out_obj->package.elements[LOG_CHUNK1_SZ_IDX].integer.value; > + > + if (out_obj->package.elements[LOG_CHUNK2_LO_IDX].type != ACPI_TYPE_INTEGER) > + goto free_acpi_buffer; > + > + data_info->chunk2_addr_lo = > + out_obj->package.elements[LOG_CHUNK2_LO_IDX].integer.value; > + > + if (out_obj->package.elements[LOG_CHUNK2_HI_IDX].type != ACPI_TYPE_INTEGER) > + goto free_acpi_buffer; > + > + data_info->chunk2_addr_hi = > + out_obj->package.elements[LOG_CHUNK2_HI_IDX].integer.value; > + > + if (out_obj->package.elements[LOG_CHUNK2_SZ_IDX].type != ACPI_TYPE_INTEGER) > + goto free_acpi_buffer; > + > + data_info->chunk2_size = > + out_obj->package.elements[LOG_CHUNK2_SZ_IDX].integer.value; > + > + if (out_obj->package.elements[LOG_ROLLOVER_CNT_IDX].type != ACPI_TYPE_INTEGER) > + goto free_acpi_buffer; > + > + data_info->rollover_cnt = > + out_obj->package.elements[LOG_ROLLOVER_CNT_IDX].integer.value; > + > + if (out_obj->package.elements[LOG_RESET_CNT_IDX].type != ACPI_TYPE_INTEGER) > + goto free_acpi_buffer; > + > + data_info->reset_cnt = > + out_obj->package.elements[LOG_RESET_CNT_IDX].integer.value; > + > + ret = 0; > +free_acpi_buffer: > + ACPI_FREE(out_obj); > + > + return ret; > +} > + > +static int set_pfru_log_level(int level) > +{ > + union acpi_object *out_obj, *obj, in_obj, in_buf; > + enum pfru_dsm_status status; > + acpi_handle handle; > + int ret = -EINVAL; > + > + handle = ACPI_HANDLE(pfru_log_dev->dev); > + > + memset(&in_obj, 0, sizeof(in_obj)); > + memset(&in_buf, 0, sizeof(in_buf)); > + in_obj.type = ACPI_TYPE_PACKAGE; > + in_obj.package.count = 1; > + in_obj.package.elements = &in_buf; > + in_buf.type = ACPI_TYPE_INTEGER; > + in_buf.integer.value = level; > + > + out_obj = acpi_evaluate_dsm_typed(handle, &pfru_log_dev->uuid, > + pfru_log_dev->info.log_revid, FUNC_SET_LEV, > + &in_obj, ACPI_TYPE_PACKAGE); > + if (!out_obj) > + return -EINVAL; > + > + obj = &out_obj->package.elements[0]; > + status = obj->integer.value; > + if (status) > + goto free_acpi_buffer; > + > + obj = &out_obj->package.elements[1]; > + status = obj->integer.value; > + if (status) > + goto free_acpi_buffer; > + > + ret = 0; > + > +free_acpi_buffer: > + ACPI_FREE(out_obj); > + > + return ret; > +} > + > +static int get_pfru_log_level(int *level) > +{ > + union acpi_object *out_obj, *obj; > + enum pfru_dsm_status status; > + acpi_handle handle; > + int ret = -EINVAL; > + > + handle = ACPI_HANDLE(pfru_log_dev->dev); > + out_obj = acpi_evaluate_dsm_typed(handle, &pfru_log_dev->uuid, > + pfru_log_dev->info.log_revid, FUNC_GET_LEV, > + NULL, ACPI_TYPE_PACKAGE); > + if (!out_obj) > + return -EINVAL; > + > + obj = &out_obj->package.elements[0]; > + if (obj->type != ACPI_TYPE_INTEGER) > + goto free_acpi_buffer; > + > + status = obj->integer.value; > + if (status) > + goto free_acpi_buffer; > + > + obj = &out_obj->package.elements[1]; > + if (obj->type != ACPI_TYPE_INTEGER) > + goto free_acpi_buffer; > + > + status = obj->integer.value; > + if (status) > + goto free_acpi_buffer; > + > + obj = &out_obj->package.elements[2]; > + if (obj->type != ACPI_TYPE_INTEGER) > + goto free_acpi_buffer; > + > + *level = obj->integer.value; > + ret = 0; > +free_acpi_buffer: > + ACPI_FREE(out_obj); > + > + return ret; > +} > + > +static int valid_log_level(int level) > +{ > + return level == LOG_ERR || level == LOG_WARN || > + level == LOG_INFO || level == LOG_VERB; > +} > + > +static int valid_log_type(int type) > +{ > + return type == LOG_EXEC_IDX || type == LOG_HISTORY_IDX; > +} > + > +long pfru_log_ioctl(struct file *file, unsigned int cmd, unsigned long arg) > +{ > + struct pfru_log_data_info data_info; > + struct pfru_log_info info; > + void __user *p; > + int ret = 0; > + > + if (!pfru_log_dev) > + return -ENODEV; > + > + p = (void __user *)arg; > + > + switch (cmd) { > + case PFRU_LOG_IOC_SET_INFO: > + if (copy_from_user(&info, p, sizeof(info))) > + return -EFAULT; > + > + if (pfru_valid_revid(info.log_revid)) > + pfru_log_dev->info.log_revid = info.log_revid; > + > + if (valid_log_level(info.log_level)) { > + ret = set_pfru_log_level(info.log_level); > + if (ret) > + return ret; > + pfru_log_dev->info.log_level = info.log_level; > + } > + > + if (valid_log_type(info.log_type)) > + pfru_log_dev->info.log_type = info.log_type; > + > + break; > + case PFRU_LOG_IOC_GET_INFO: > + ret = get_pfru_log_level(&info.log_level); > + if (ret) > + return ret; > + > + info.log_type = pfru_log_dev->info.log_type; > + info.log_revid = pfru_log_dev->info.log_revid; > + if (copy_to_user(p, &info, sizeof(info))) > + ret = -EFAULT; > + > + break; > + case PFRU_LOG_IOC_GET_DATA_INFO: > + ret = get_pfru_log_data_info(&data_info, pfru_log_dev->info.log_type); > + if (ret) > + return ret; > + > + if (copy_to_user(p, &data_info, sizeof(struct pfru_log_data_info))) > + ret = -EFAULT; > + > + break; > + default: > + ret = -ENOTTY; > + break; > + } > + return ret; > +} > + > +ssize_t pfru_log_read(struct file *filp, char __user *ubuf, > + size_t size, loff_t *off) > +{ > + struct pfru_log_data_info info; > + phys_addr_t base_addr; > + int buf_size, ret; > + char *buf_ptr; > + > + if (!pfru_log_dev) > + return -ENODEV; > + > + if (*off < 0) > + return -EINVAL; > + > + ret = get_pfru_log_data_info(&info, pfru_log_dev->info.log_type); > + if (ret) > + return ret; > + > + base_addr = (phys_addr_t)(info.chunk2_addr_lo | (info.chunk2_addr_hi << 32)); > + /* pfru update has not been launched yet.*/ > + if (!base_addr) > + return -EBUSY; > + > + buf_size = info.max_data_size; > + if (*off >= buf_size) > + return 0; > + > + buf_ptr = memremap(base_addr, buf_size, MEMREMAP_WB); > + if (IS_ERR(buf_ptr)) > + return PTR_ERR(buf_ptr); > + > + size = min_t(size_t, size, buf_size - *off); > + if (copy_to_user(ubuf, buf_ptr + *off, size)) > + ret = -EFAULT; > + else > + ret = 0; As all you are doing is mapping some memory and reading from it, why do you need a read() file operation at all? Why not just use mmap? thanks, greg k-h
On Wed, Oct 20, 2021 at 04:29:39PM +0800, Chen Yu wrote: > > > +ssize_t pfru_log_read(struct file *filp, char __user *ubuf, > > > + size_t size, loff_t *off) > > > +{ > > > + struct pfru_log_data_info info; > > > + phys_addr_t base_addr; > > > + int buf_size, ret; > > > + char *buf_ptr; > > > + > > > + if (!pfru_log_dev) > > > + return -ENODEV; > > > + > > > + if (*off < 0) > > > + return -EINVAL; > > > + > > > + ret = get_pfru_log_data_info(&info, pfru_log_dev->info.log_type); > > > + if (ret) > > > + return ret; > > > + > > > + base_addr = (phys_addr_t)(info.chunk2_addr_lo | (info.chunk2_addr_hi << 32)); > > > + /* pfru update has not been launched yet.*/ > > > + if (!base_addr) > > > + return -EBUSY; > > > + > > > + buf_size = info.max_data_size; > > > + if (*off >= buf_size) > > > + return 0; > > > + > > > + buf_ptr = memremap(base_addr, buf_size, MEMREMAP_WB); > > > + if (IS_ERR(buf_ptr)) > > > + return PTR_ERR(buf_ptr); > > > + > > > + size = min_t(size_t, size, buf_size - *off); > > > + if (copy_to_user(ubuf, buf_ptr + *off, size)) > > > + ret = -EFAULT; > > > + else > > > + ret = 0; > > > > As all you are doing is mapping some memory and reading from it, why do > > you need a read() file operation at all? Why not just use mmap? > > > In the beginning mmap() interface was provided to the user. Then it was > realized that there is no guarantee in the spec that, the physical address > provided by the BIOS would remain unchanged. So instead of asking the user > to mmap the file each time before reading the log, the read() is leveraged > here to always memremap() the latest address. So you are forced to memremap on _EVERY_ read call because the BIOS can change things underneath us without the kernel knowing about it? How does the chunk2_addr_lo and _hi values change while the system is running? Where does that happen, and isn't this going to be a very slow and expensive read call all the time? thanks, greg k-h
On Sat, Oct 16, 2021 at 05:10:25PM +0200, Greg Kroah-Hartman wrote: > On Sat, Oct 16, 2021 at 06:44:31PM +0800, Chen Yu wrote: > > Platform Firmware Runtime Update(PFRU) Telemetry Service is part of RoT > > (Root of Trust), which allows PFRU handler and other PFRU drivers to > > produce telemetry data to upper layer OS consumer at runtime. > > > > The linux provides interfaces for the user to query the parameters of > > telemetry data, and the user could read out the telemetry data > > accordingly. > > What type of interface is this? How does userspace interact with it? > > > > > Also add the ABI documentation. > > Add it where? > They are all ioctl interfaces, and was introduced in previous patch in Documentation/ABI/testing/pfru. The way userspace interace with it is introduced in next patch in the man page. I'll revise the commit log to better describe how user could use it. > > > > Typical log looks like: > > [SmmRuntimeUpdateHandler.ProcessSmmRuntimeUpdate] > > ProcessSmmRuntimeUpdate = START, Action = 2 > > [SmmRuntimeUpdateHandler.ProcessSmmRuntimeUpdate] > > FwVersion = 0, CodeInjectionVersion = 1 > > [ShadowSmmRuntimeUpdateImage] > > Image = 0x74D9B000, ImageSize = 0x1172 > > [ProcessSmmRuntimeUpdate] > > ShadowSmmRuntimeUpdateImage.Status = Success > > [ValidateSmmRuntimeUpdateImage] > > CapsuleHeader.CapsuleGuid = 6DCBD5ED-E82D-4C44-BDA1-7194199AD92A > > [ValidateSmmRuntimeUpdateImage] > > FmpCapHeader.Version = 1 > > [ValidateSmmRuntimeUpdateImage] > > FmpCapImageHeader.UpdateImageTypeId = B2F84B79-7B6E-4E45-885F-3FB9BB185402 > > [ValidateSmmRuntimeUpdateImage] > > SmmRuntimeUpdateVerifyImageWithDenylist.Status = Success > > [ValidateSmmRuntimeUpdateImage] > > SmmRuntimeUpdateVerifyImageWithAllowlist.Status = Success > > [SmmCodeInjectionVerifyPayloadHeader] > > PayloadHeader.Signature = 0x31494353 > > [SmmCodeInjectionVerifyPayloadHeader] > > PayloadHeader.PlatformId = 63462139-A8B1-AA4E-9024-F2BB53EA4723 > > [SmmCodeInjectionVerifyPayloadHeader] > > PayloadHeader.SupportedSmmFirmwareVersion = 0, > > PayloadHeader.SmmCodeInjectionRuntimeVersion = 1 > > [ProcessSmmRuntimeUpdate] > > ValidateSmmRuntimeUpdateImage.Status = Success > > CPU CSR[0B102D28] Before = 7FBF830E > > CPU CSR[0B102D28] After = 7FBF8310 > > [ProcessSmmRuntimeUpdate] ProcessSmmCodeInjection.Status = Success > > [SmmRuntimeUpdateHandler.ProcessSmmRuntimeUpdate] > > ProcessSmmRuntimeUpdate = End, Status = Success > > This log does not make any sense to me, where is it from? Why the odd > line-wrapping? > It is from the telemetry log generated by the BIOS. Since this content is platform specific, I'll remove the log in next version. > > +}; > > + > > + [snip...] > > +ssize_t pfru_log_read(struct file *filp, char __user *ubuf, > > + size_t size, loff_t *off) > > +{ > > + struct pfru_log_data_info info; > > + phys_addr_t base_addr; > > + int buf_size, ret; > > + char *buf_ptr; > > + > > + if (!pfru_log_dev) > > + return -ENODEV; > > + > > + if (*off < 0) > > + return -EINVAL; > > + > > + ret = get_pfru_log_data_info(&info, pfru_log_dev->info.log_type); > > + if (ret) > > + return ret; > > + > > + base_addr = (phys_addr_t)(info.chunk2_addr_lo | (info.chunk2_addr_hi << 32)); > > + /* pfru update has not been launched yet.*/ > > + if (!base_addr) > > + return -EBUSY; > > + > > + buf_size = info.max_data_size; > > + if (*off >= buf_size) > > + return 0; > > + > > + buf_ptr = memremap(base_addr, buf_size, MEMREMAP_WB); > > + if (IS_ERR(buf_ptr)) > > + return PTR_ERR(buf_ptr); > > + > > + size = min_t(size_t, size, buf_size - *off); > > + if (copy_to_user(ubuf, buf_ptr + *off, size)) > > + ret = -EFAULT; > > + else > > + ret = 0; > > As all you are doing is mapping some memory and reading from it, why do > you need a read() file operation at all? Why not just use mmap? > In the beginning mmap() interface was provided to the user. Then it was realized that there is no guarantee in the spec that, the physical address provided by the BIOS would remain unchanged. So instead of asking the user to mmap the file each time before reading the log, the read() is leveraged here to always memremap() the latest address. thanks, Chenyu > thanks, > > greg k-h
On Wed, Oct 20, 2021 at 10:27:28AM +0200, Greg Kroah-Hartman wrote: > On Wed, Oct 20, 2021 at 04:29:39PM +0800, Chen Yu wrote: > > > > +ssize_t pfru_log_read(struct file *filp, char __user *ubuf, > > > > + size_t size, loff_t *off) > > > > +{ > > > > + struct pfru_log_data_info info; > > > > + phys_addr_t base_addr; > > > > + int buf_size, ret; > > > > + char *buf_ptr; > > > > + > > > > + if (!pfru_log_dev) > > > > + return -ENODEV; > > > > + > > > > + if (*off < 0) > > > > + return -EINVAL; > > > > + > > > > + ret = get_pfru_log_data_info(&info, pfru_log_dev->info.log_type); > > > > + if (ret) > > > > + return ret; > > > > + > > > > + base_addr = (phys_addr_t)(info.chunk2_addr_lo | (info.chunk2_addr_hi << 32)); > > > > + /* pfru update has not been launched yet.*/ > > > > + if (!base_addr) > > > > + return -EBUSY; > > > > + > > > > + buf_size = info.max_data_size; > > > > + if (*off >= buf_size) > > > > + return 0; > > > > + > > > > + buf_ptr = memremap(base_addr, buf_size, MEMREMAP_WB); > > > > + if (IS_ERR(buf_ptr)) > > > > + return PTR_ERR(buf_ptr); > > > > + > > > > + size = min_t(size_t, size, buf_size - *off); > > > > + if (copy_to_user(ubuf, buf_ptr + *off, size)) > > > > + ret = -EFAULT; > > > > + else > > > > + ret = 0; > > > > > > As all you are doing is mapping some memory and reading from it, why do > > > you need a read() file operation at all? Why not just use mmap? > > > > > In the beginning mmap() interface was provided to the user. Then it was > > realized that there is no guarantee in the spec that, the physical address > > provided by the BIOS would remain unchanged. So instead of asking the user > > to mmap the file each time before reading the log, the read() is leveraged > > here to always memremap() the latest address. > > So you are forced to memremap on _EVERY_ read call because the BIOS can > change things underneath us without the kernel knowing about it? How > does the chunk2_addr_lo and _hi values change while the system is > running? Where does that happen, and isn't this going to be a very slow > and expensive read call all the time? And maybe it is something that can be fixed in the spec so that the address will remain constant? (I realise it's wishful thinking, but sill...)
On Wed, Oct 20, 2021 at 10:27:28AM +0200, Greg Kroah-Hartman wrote: > On Wed, Oct 20, 2021 at 04:29:39PM +0800, Chen Yu wrote: > > > > +ssize_t pfru_log_read(struct file *filp, char __user *ubuf, > > > > + size_t size, loff_t *off) > > > > +{ > > > > + struct pfru_log_data_info info; > > > > + phys_addr_t base_addr; > > > > + int buf_size, ret; > > > > + char *buf_ptr; > > > > + > > > > + if (!pfru_log_dev) > > > > + return -ENODEV; > > > > + > > > > + if (*off < 0) > > > > + return -EINVAL; > > > > + > > > > + ret = get_pfru_log_data_info(&info, pfru_log_dev->info.log_type); > > > > + if (ret) > > > > + return ret; > > > > + > > > > + base_addr = (phys_addr_t)(info.chunk2_addr_lo | (info.chunk2_addr_hi << 32)); > > > > + /* pfru update has not been launched yet.*/ > > > > + if (!base_addr) > > > > + return -EBUSY; > > > > + > > > > + buf_size = info.max_data_size; > > > > + if (*off >= buf_size) > > > > + return 0; > > > > + > > > > + buf_ptr = memremap(base_addr, buf_size, MEMREMAP_WB); > > > > + if (IS_ERR(buf_ptr)) > > > > + return PTR_ERR(buf_ptr); > > > > + > > > > + size = min_t(size_t, size, buf_size - *off); > > > > + if (copy_to_user(ubuf, buf_ptr + *off, size)) > > > > + ret = -EFAULT; > > > > + else > > > > + ret = 0; > > > > > > As all you are doing is mapping some memory and reading from it, why do > > > you need a read() file operation at all? Why not just use mmap? > > > > > In the beginning mmap() interface was provided to the user. Then it was > > realized that there is no guarantee in the spec that, the physical address > > provided by the BIOS would remain unchanged. So instead of asking the user > > to mmap the file each time before reading the log, the read() is leveraged > > here to always memremap() the latest address. > > So you are forced to memremap on _EVERY_ read call because the BIOS can > change things underneath us without the kernel knowing about it? How > does the chunk2_addr_lo and _hi values change while the system is > running? Where does that happen, and isn't this going to be a very slow > and expensive read call all the time? > It was not documented in the spec whether the chunk address will change or not, for safety I changed it from mmap() to read(). I'll try to reach the spec designer and check if the address will change or not. thanks, Chenyu > thanks, > > greg k-h
Hi Chen, I love your patch! Perhaps something to improve: [auto build test WARNING on rafael-pm/linux-next] [also build test WARNING on efi/next linus/master v5.15-rc6 next-20211020] [If your patch is applied to the wrong git tree, kindly drop us a note. And when submitting patch, we suggest to use '--base' as documented in https://git-scm.com/docs/git-format-patch] url: https://github.com/0day-ci/linux/commits/Chen-Yu/Introduce-Platform-Firmware-Runtime-Update-and-Telemetry-drivers/20211016-184349 base: https://git.kernel.org/pub/scm/linux/kernel/git/rafael/linux-pm.git linux-next config: x86_64-randconfig-c007-20211021 (attached as .config) compiler: clang version 14.0.0 (https://github.com/llvm/llvm-project 3cea2505fd8d99a9ba0cb625aecfe28a47c4e3f8) reproduce (this is a W=1 build): wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross chmod +x ~/bin/make.cross # https://github.com/0day-ci/linux/commit/93acf331ef196b860f6b34a5e9f8b3a249f1a0ce git remote add linux-review https://github.com/0day-ci/linux git fetch --no-tags linux-review Chen-Yu/Introduce-Platform-Firmware-Runtime-Update-and-Telemetry-drivers/20211016-184349 git checkout 93acf331ef196b860f6b34a5e9f8b3a249f1a0ce # save the attached .config to linux build tree COMPILER_INSTALL_PATH=$HOME/0day COMPILER=clang make.cross W=1 ARCH=x86_64 If you fix the issue, kindly add following tag as appropriate Reported-by: kernel test robot <lkp@intel.com> All warnings (new ones prefixed by >>): >> drivers/acpi/pfru/pfru_update.c:280:6: warning: no previous prototype for function 'pfru_log_ioctl' [-Wmissing-prototypes] long pfru_log_ioctl(struct file *file, unsigned int cmd, unsigned long arg) ^ drivers/acpi/pfru/pfru_update.c:280:1: note: declare 'static' if the function is not intended to be used outside of this translation unit long pfru_log_ioctl(struct file *file, unsigned int cmd, unsigned long arg) ^ static >> drivers/acpi/pfru/pfru_update.c:338:9: warning: no previous prototype for function 'pfru_log_read' [-Wmissing-prototypes] ssize_t pfru_log_read(struct file *filp, char __user *ubuf, ^ drivers/acpi/pfru/pfru_update.c:338:1: note: declare 'static' if the function is not intended to be used outside of this translation unit ssize_t pfru_log_read(struct file *filp, char __user *ubuf, ^ static 2 warnings generated. vim +/pfru_log_ioctl +280 drivers/acpi/pfru/pfru_update.c 279 > 280 long pfru_log_ioctl(struct file *file, unsigned int cmd, unsigned long arg) 281 { 282 struct pfru_log_data_info data_info; 283 struct pfru_log_info info; 284 void __user *p; 285 int ret = 0; 286 287 if (!pfru_log_dev) 288 return -ENODEV; 289 290 p = (void __user *)arg; 291 292 switch (cmd) { 293 case PFRU_LOG_IOC_SET_INFO: 294 if (copy_from_user(&info, p, sizeof(info))) 295 return -EFAULT; 296 297 if (pfru_valid_revid(info.log_revid)) 298 pfru_log_dev->info.log_revid = info.log_revid; 299 300 if (valid_log_level(info.log_level)) { 301 ret = set_pfru_log_level(info.log_level); 302 if (ret) 303 return ret; 304 pfru_log_dev->info.log_level = info.log_level; 305 } 306 307 if (valid_log_type(info.log_type)) 308 pfru_log_dev->info.log_type = info.log_type; 309 310 break; 311 case PFRU_LOG_IOC_GET_INFO: 312 ret = get_pfru_log_level(&info.log_level); 313 if (ret) 314 return ret; 315 316 info.log_type = pfru_log_dev->info.log_type; 317 info.log_revid = pfru_log_dev->info.log_revid; 318 if (copy_to_user(p, &info, sizeof(info))) 319 ret = -EFAULT; 320 321 break; 322 case PFRU_LOG_IOC_GET_DATA_INFO: 323 ret = get_pfru_log_data_info(&data_info, pfru_log_dev->info.log_type); 324 if (ret) 325 return ret; 326 327 if (copy_to_user(p, &data_info, sizeof(struct pfru_log_data_info))) 328 ret = -EFAULT; 329 330 break; 331 default: 332 ret = -ENOTTY; 333 break; 334 } 335 return ret; 336 } 337 > 338 ssize_t pfru_log_read(struct file *filp, char __user *ubuf, 339 size_t size, loff_t *off) 340 { 341 struct pfru_log_data_info info; 342 phys_addr_t base_addr; 343 int buf_size, ret; 344 char *buf_ptr; 345 346 if (!pfru_log_dev) 347 return -ENODEV; 348 349 if (*off < 0) 350 return -EINVAL; 351 352 ret = get_pfru_log_data_info(&info, pfru_log_dev->info.log_type); 353 if (ret) 354 return ret; 355 356 base_addr = (phys_addr_t)(info.chunk2_addr_lo | (info.chunk2_addr_hi << 32)); 357 /* pfru update has not been launched yet.*/ 358 if (!base_addr) 359 return -EBUSY; 360 361 buf_size = info.max_data_size; 362 if (*off >= buf_size) 363 return 0; 364 365 buf_ptr = memremap(base_addr, buf_size, MEMREMAP_WB); 366 if (IS_ERR(buf_ptr)) 367 return PTR_ERR(buf_ptr); 368 369 size = min_t(size_t, size, buf_size - *off); 370 if (copy_to_user(ubuf, buf_ptr + *off, size)) 371 ret = -EFAULT; 372 else 373 ret = 0; 374 375 memunmap(buf_ptr); 376 377 return ret ? ret : size; 378 } 379 --- 0-DAY CI Kernel Test Service, Intel Corporation https://lists.01.org/hyperkitty/list/kbuild-all@lists.01.org
diff --git a/drivers/acpi/pfru/pfru_update.c b/drivers/acpi/pfru/pfru_update.c index f57a39e79808..fe5b1bf0aeb3 100644 --- a/drivers/acpi/pfru/pfru_update.c +++ b/drivers/acpi/pfru/pfru_update.c @@ -55,6 +55,27 @@ enum update_index { UPDATE_NR_IDX, }; +enum log_index { + LOG_STATUS_IDX, + LOG_EXT_STATUS_IDX, + LOG_MAX_SZ_IDX, + LOG_CHUNK1_LO_IDX, + LOG_CHUNK1_HI_IDX, + LOG_CHUNK1_SZ_IDX, + LOG_CHUNK2_LO_IDX, + LOG_CHUNK2_HI_IDX, + LOG_CHUNK2_SZ_IDX, + LOG_ROLLOVER_CNT_IDX, + LOG_RESET_CNT_IDX, + LOG_NR_IDX, +}; + +struct pfru_log_device { + struct device *dev; + guid_t uuid; + struct pfru_log_info info; +}; + struct pfru_device { guid_t uuid, code_uuid, drv_uuid; int rev_id; @@ -62,6 +83,299 @@ struct pfru_device { }; static struct pfru_device *pfru_dev; +static struct pfru_log_device *pfru_log_dev; + +static int get_pfru_log_data_info(struct pfru_log_data_info *data_info, + int log_type) +{ + union acpi_object *out_obj, in_obj, in_buf; + acpi_handle handle; + int ret = -EINVAL; + + handle = ACPI_HANDLE(pfru_log_dev->dev); + + memset(&in_obj, 0, sizeof(in_obj)); + memset(&in_buf, 0, sizeof(in_buf)); + in_obj.type = ACPI_TYPE_PACKAGE; + in_obj.package.count = 1; + in_obj.package.elements = &in_buf; + in_buf.type = ACPI_TYPE_INTEGER; + in_buf.integer.value = log_type; + + out_obj = acpi_evaluate_dsm_typed(handle, &pfru_log_dev->uuid, + pfru_log_dev->info.log_revid, FUNC_GET_DATA, + &in_obj, ACPI_TYPE_PACKAGE); + if (!out_obj) + return ret; + + if (out_obj->package.count < LOG_NR_IDX) + goto free_acpi_buffer; + + if (out_obj->package.elements[LOG_STATUS_IDX].type != ACPI_TYPE_INTEGER) + goto free_acpi_buffer; + + data_info->status = out_obj->package.elements[LOG_STATUS_IDX].integer.value; + + if (out_obj->package.elements[LOG_EXT_STATUS_IDX].type != ACPI_TYPE_INTEGER) + goto free_acpi_buffer; + + data_info->ext_status = + out_obj->package.elements[LOG_EXT_STATUS_IDX].integer.value; + + if (out_obj->package.elements[LOG_MAX_SZ_IDX].type != ACPI_TYPE_INTEGER) + goto free_acpi_buffer; + + data_info->max_data_size = + out_obj->package.elements[LOG_MAX_SZ_IDX].integer.value; + + if (out_obj->package.elements[LOG_CHUNK1_LO_IDX].type != ACPI_TYPE_INTEGER) + goto free_acpi_buffer; + + data_info->chunk1_addr_lo = + out_obj->package.elements[LOG_CHUNK1_LO_IDX].integer.value; + + if (out_obj->package.elements[LOG_CHUNK1_HI_IDX].type != ACPI_TYPE_INTEGER) + goto free_acpi_buffer; + + data_info->chunk1_addr_hi = + out_obj->package.elements[LOG_CHUNK1_HI_IDX].integer.value; + + if (out_obj->package.elements[LOG_CHUNK1_SZ_IDX].type != ACPI_TYPE_INTEGER) + goto free_acpi_buffer; + + data_info->chunk1_size = + out_obj->package.elements[LOG_CHUNK1_SZ_IDX].integer.value; + + if (out_obj->package.elements[LOG_CHUNK2_LO_IDX].type != ACPI_TYPE_INTEGER) + goto free_acpi_buffer; + + data_info->chunk2_addr_lo = + out_obj->package.elements[LOG_CHUNK2_LO_IDX].integer.value; + + if (out_obj->package.elements[LOG_CHUNK2_HI_IDX].type != ACPI_TYPE_INTEGER) + goto free_acpi_buffer; + + data_info->chunk2_addr_hi = + out_obj->package.elements[LOG_CHUNK2_HI_IDX].integer.value; + + if (out_obj->package.elements[LOG_CHUNK2_SZ_IDX].type != ACPI_TYPE_INTEGER) + goto free_acpi_buffer; + + data_info->chunk2_size = + out_obj->package.elements[LOG_CHUNK2_SZ_IDX].integer.value; + + if (out_obj->package.elements[LOG_ROLLOVER_CNT_IDX].type != ACPI_TYPE_INTEGER) + goto free_acpi_buffer; + + data_info->rollover_cnt = + out_obj->package.elements[LOG_ROLLOVER_CNT_IDX].integer.value; + + if (out_obj->package.elements[LOG_RESET_CNT_IDX].type != ACPI_TYPE_INTEGER) + goto free_acpi_buffer; + + data_info->reset_cnt = + out_obj->package.elements[LOG_RESET_CNT_IDX].integer.value; + + ret = 0; +free_acpi_buffer: + ACPI_FREE(out_obj); + + return ret; +} + +static int set_pfru_log_level(int level) +{ + union acpi_object *out_obj, *obj, in_obj, in_buf; + enum pfru_dsm_status status; + acpi_handle handle; + int ret = -EINVAL; + + handle = ACPI_HANDLE(pfru_log_dev->dev); + + memset(&in_obj, 0, sizeof(in_obj)); + memset(&in_buf, 0, sizeof(in_buf)); + in_obj.type = ACPI_TYPE_PACKAGE; + in_obj.package.count = 1; + in_obj.package.elements = &in_buf; + in_buf.type = ACPI_TYPE_INTEGER; + in_buf.integer.value = level; + + out_obj = acpi_evaluate_dsm_typed(handle, &pfru_log_dev->uuid, + pfru_log_dev->info.log_revid, FUNC_SET_LEV, + &in_obj, ACPI_TYPE_PACKAGE); + if (!out_obj) + return -EINVAL; + + obj = &out_obj->package.elements[0]; + status = obj->integer.value; + if (status) + goto free_acpi_buffer; + + obj = &out_obj->package.elements[1]; + status = obj->integer.value; + if (status) + goto free_acpi_buffer; + + ret = 0; + +free_acpi_buffer: + ACPI_FREE(out_obj); + + return ret; +} + +static int get_pfru_log_level(int *level) +{ + union acpi_object *out_obj, *obj; + enum pfru_dsm_status status; + acpi_handle handle; + int ret = -EINVAL; + + handle = ACPI_HANDLE(pfru_log_dev->dev); + out_obj = acpi_evaluate_dsm_typed(handle, &pfru_log_dev->uuid, + pfru_log_dev->info.log_revid, FUNC_GET_LEV, + NULL, ACPI_TYPE_PACKAGE); + if (!out_obj) + return -EINVAL; + + obj = &out_obj->package.elements[0]; + if (obj->type != ACPI_TYPE_INTEGER) + goto free_acpi_buffer; + + status = obj->integer.value; + if (status) + goto free_acpi_buffer; + + obj = &out_obj->package.elements[1]; + if (obj->type != ACPI_TYPE_INTEGER) + goto free_acpi_buffer; + + status = obj->integer.value; + if (status) + goto free_acpi_buffer; + + obj = &out_obj->package.elements[2]; + if (obj->type != ACPI_TYPE_INTEGER) + goto free_acpi_buffer; + + *level = obj->integer.value; + ret = 0; +free_acpi_buffer: + ACPI_FREE(out_obj); + + return ret; +} + +static int valid_log_level(int level) +{ + return level == LOG_ERR || level == LOG_WARN || + level == LOG_INFO || level == LOG_VERB; +} + +static int valid_log_type(int type) +{ + return type == LOG_EXEC_IDX || type == LOG_HISTORY_IDX; +} + +long pfru_log_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + struct pfru_log_data_info data_info; + struct pfru_log_info info; + void __user *p; + int ret = 0; + + if (!pfru_log_dev) + return -ENODEV; + + p = (void __user *)arg; + + switch (cmd) { + case PFRU_LOG_IOC_SET_INFO: + if (copy_from_user(&info, p, sizeof(info))) + return -EFAULT; + + if (pfru_valid_revid(info.log_revid)) + pfru_log_dev->info.log_revid = info.log_revid; + + if (valid_log_level(info.log_level)) { + ret = set_pfru_log_level(info.log_level); + if (ret) + return ret; + pfru_log_dev->info.log_level = info.log_level; + } + + if (valid_log_type(info.log_type)) + pfru_log_dev->info.log_type = info.log_type; + + break; + case PFRU_LOG_IOC_GET_INFO: + ret = get_pfru_log_level(&info.log_level); + if (ret) + return ret; + + info.log_type = pfru_log_dev->info.log_type; + info.log_revid = pfru_log_dev->info.log_revid; + if (copy_to_user(p, &info, sizeof(info))) + ret = -EFAULT; + + break; + case PFRU_LOG_IOC_GET_DATA_INFO: + ret = get_pfru_log_data_info(&data_info, pfru_log_dev->info.log_type); + if (ret) + return ret; + + if (copy_to_user(p, &data_info, sizeof(struct pfru_log_data_info))) + ret = -EFAULT; + + break; + default: + ret = -ENOTTY; + break; + } + return ret; +} + +ssize_t pfru_log_read(struct file *filp, char __user *ubuf, + size_t size, loff_t *off) +{ + struct pfru_log_data_info info; + phys_addr_t base_addr; + int buf_size, ret; + char *buf_ptr; + + if (!pfru_log_dev) + return -ENODEV; + + if (*off < 0) + return -EINVAL; + + ret = get_pfru_log_data_info(&info, pfru_log_dev->info.log_type); + if (ret) + return ret; + + base_addr = (phys_addr_t)(info.chunk2_addr_lo | (info.chunk2_addr_hi << 32)); + /* pfru update has not been launched yet.*/ + if (!base_addr) + return -EBUSY; + + buf_size = info.max_data_size; + if (*off >= buf_size) + return 0; + + buf_ptr = memremap(base_addr, buf_size, MEMREMAP_WB); + if (IS_ERR(buf_ptr)) + return PTR_ERR(buf_ptr); + + size = min_t(size_t, size, buf_size - *off); + if (copy_to_user(ubuf, buf_ptr + *off, size)) + ret = -EFAULT; + else + ret = 0; + + memunmap(buf_ptr); + + return ret ? ret : size; +} static int query_capability(struct pfru_update_cap_info *cap) { @@ -406,7 +720,7 @@ static long pfru_ioctl(struct file *file, unsigned int cmd, unsigned long arg) ret = start_acpi_update(START_STAGE_ACTIVATE); break; default: - ret = -ENOTTY; + ret = pfru_log_ioctl(file, cmd, arg); break; } @@ -467,6 +781,7 @@ static ssize_t pfru_write(struct file *file, const char __user *buf, static const struct file_operations acpi_pfru_fops = { .owner = THIS_MODULE, .write = pfru_write, + .read = pfru_log_read, .unlocked_ioctl = pfru_ioctl, .llseek = noop_llseek, }; @@ -478,6 +793,44 @@ static struct miscdevice pfru_misc_dev = { .fops = &acpi_pfru_fops, }; +static int acpi_pfru_log_remove(struct platform_device *pdev) +{ + return 0; +} + +static int acpi_pfru_log_probe(struct platform_device *pdev) +{ + acpi_handle handle; + int ret; + + /* One instance is expected. */ + if (pfru_log_dev) + return 0; + + pfru_log_dev = kzalloc(sizeof(*pfru_log_dev), GFP_KERNEL); + if (!pfru_log_dev) + return -ENOMEM; + + ret = guid_parse(PFRU_LOG_UUID, &pfru_log_dev->uuid); + if (ret) + goto out; + + pfru_log_dev->info.log_revid = 1; + pfru_log_dev->dev = &pdev->dev; + handle = ACPI_HANDLE(pfru_log_dev->dev); + if (!acpi_has_method(handle, "_DSM")) { + ret = -ENODEV; + goto out; + } + + return 0; +out: + kfree(pfru_log_dev); + pfru_log_dev = NULL; + + return ret; +} + static int acpi_pfru_remove(struct platform_device *pdev) { return 0; @@ -526,6 +879,21 @@ static int acpi_pfru_probe(struct platform_device *pdev) return ret; } +static const struct acpi_device_id acpi_pfru_log_ids[] = { + {"INTC1081", 0}, + {} +}; +MODULE_DEVICE_TABLE(acpi, acpi_pfru_log_ids); + +static struct platform_driver acpi_pfru_log_driver = { + .driver = { + .name = "pfru_log", + .acpi_match_table = acpi_pfru_log_ids, + }, + .probe = acpi_pfru_log_probe, + .remove = acpi_pfru_log_remove, +}; + static const struct acpi_device_id acpi_pfru_ids[] = { {"INTC1080", 0}, {} @@ -549,7 +917,11 @@ static int __init pfru_init(void) if (ret) return ret; - return platform_driver_register(&acpi_pfru_driver); + ret = platform_driver_register(&acpi_pfru_driver); + if (ret) + misc_deregister(&pfru_misc_dev); + + return platform_driver_register(&acpi_pfru_log_driver); } static void __exit pfru_exit(void) @@ -558,6 +930,10 @@ static void __exit pfru_exit(void) misc_deregister(&pfru_misc_dev); kfree(pfru_dev); pfru_dev = NULL; + + platform_driver_unregister(&acpi_pfru_log_driver); + kfree(pfru_log_dev); + pfru_log_dev = NULL; } module_init(pfru_init); diff --git a/include/uapi/linux/pfru.h b/include/uapi/linux/pfru.h index 127fc38638cb..9ab74d9cd21a 100644 --- a/include/uapi/linux/pfru.h +++ b/include/uapi/linux/pfru.h @@ -99,4 +99,47 @@ struct pfru_updated_result { __u64 high_exec_time; }; +#define PFRU_LOG_UUID "75191659-8178-4D9D-B88F-AC5E5E93E8BF" + +/* Telemetry structures. */ +struct pfru_log_data_info { + enum pfru_dsm_status status; + enum pfru_dsm_status ext_status; + __u64 chunk1_addr_lo; + __u64 chunk1_addr_hi; + __u64 chunk2_addr_lo; + __u64 chunk2_addr_hi; + __u32 max_data_size; + __u32 chunk1_size; + __u32 chunk2_size; + __u32 rollover_cnt; + __u32 reset_cnt; +}; + +struct pfru_log_info { + __u32 log_level; + __u32 log_type; + __u32 log_revid; +}; + +/* Two logs: history and execution log */ +#define LOG_EXEC_IDX 0 +#define LOG_HISTORY_IDX 1 +#define NR_LOG_TYPE 2 + +#define LOG_ERR 0 +#define LOG_WARN 1 +#define LOG_INFO 2 +#define LOG_VERB 4 + +#define FUNC_SET_LEV 1 +#define FUNC_GET_LEV 2 +#define FUNC_GET_DATA 3 + +#define LOG_NAME_SIZE 10 + +#define PFRU_LOG_IOC_SET_INFO _IOW(PFRU_MAGIC, 0x05, struct pfru_log_info) +#define PFRU_LOG_IOC_GET_INFO _IOR(PFRU_MAGIC, 0x06, struct pfru_log_info) +#define PFRU_LOG_IOC_GET_DATA_INFO _IOR(PFRU_MAGIC, 0x07, struct pfru_log_data_info) + #endif /* __PFRU_H__ */
Platform Firmware Runtime Update(PFRU) Telemetry Service is part of RoT (Root of Trust), which allows PFRU handler and other PFRU drivers to produce telemetry data to upper layer OS consumer at runtime. The linux provides interfaces for the user to query the parameters of telemetry data, and the user could read out the telemetry data accordingly. Also add the ABI documentation. Typical log looks like: [SmmRuntimeUpdateHandler.ProcessSmmRuntimeUpdate] ProcessSmmRuntimeUpdate = START, Action = 2 [SmmRuntimeUpdateHandler.ProcessSmmRuntimeUpdate] FwVersion = 0, CodeInjectionVersion = 1 [ShadowSmmRuntimeUpdateImage] Image = 0x74D9B000, ImageSize = 0x1172 [ProcessSmmRuntimeUpdate] ShadowSmmRuntimeUpdateImage.Status = Success [ValidateSmmRuntimeUpdateImage] CapsuleHeader.CapsuleGuid = 6DCBD5ED-E82D-4C44-BDA1-7194199AD92A [ValidateSmmRuntimeUpdateImage] FmpCapHeader.Version = 1 [ValidateSmmRuntimeUpdateImage] FmpCapImageHeader.UpdateImageTypeId = B2F84B79-7B6E-4E45-885F-3FB9BB185402 [ValidateSmmRuntimeUpdateImage] SmmRuntimeUpdateVerifyImageWithDenylist.Status = Success [ValidateSmmRuntimeUpdateImage] SmmRuntimeUpdateVerifyImageWithAllowlist.Status = Success [SmmCodeInjectionVerifyPayloadHeader] PayloadHeader.Signature = 0x31494353 [SmmCodeInjectionVerifyPayloadHeader] PayloadHeader.PlatformId = 63462139-A8B1-AA4E-9024-F2BB53EA4723 [SmmCodeInjectionVerifyPayloadHeader] PayloadHeader.SupportedSmmFirmwareVersion = 0, PayloadHeader.SmmCodeInjectionRuntimeVersion = 1 [ProcessSmmRuntimeUpdate] ValidateSmmRuntimeUpdateImage.Status = Success CPU CSR[0B102D28] Before = 7FBF830E CPU CSR[0B102D28] After = 7FBF8310 [ProcessSmmRuntimeUpdate] ProcessSmmCodeInjection.Status = Success [SmmRuntimeUpdateHandler.ProcessSmmRuntimeUpdate] ProcessSmmRuntimeUpdate = End, Status = Success Signed-off-by: Chen Yu <yu.c.chen@intel.com> --- v4: Remove the telemetry kernel Kconfig and combine it with pfru (Rafael J. Wysocki) Remove redundant parens. (Rafael J. Wysocki) v3: Use __u32 instead of int and __64 instead of unsigned long in include/uapi/linux/pfru.h (Greg Kroah-Hartman) Rename the structure in uapi to start with a prefix pfru so as to avoid confusing in the global namespace. (Greg Kroah-Hartman) v2: Do similar clean up as pfru_update driver: Add sanity check for duplicated instance of ACPI device. Update the driver to work with allocated telem_device objects. (Mike Rapoport) For each switch case pair, get rid of the magic case numbers and add a default clause with the error handling. (Mike Rapoport) --- drivers/acpi/pfru/pfru_update.c | 380 +++++++++++++++++++++++++++++++- include/uapi/linux/pfru.h | 43 ++++ 2 files changed, 421 insertions(+), 2 deletions(-)