1 // SPDX-License-Identifier: GPL-2.0
3 * ACPI Platform Firmware Runtime Update Device driver
5 * Copyright (C) 2021 Intel Corporation
6 * Author: Chen Yu <yu.c.chen@intel.com>
8 * pfr_update driver is used for Platform Firmware Runtime
9 * Update, which includes the code injection and driver update.
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>
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>
27 #include <uapi/linux/pfrut.h>
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
34 #define PFRU_CODE_INJECT_TYPE 1
35 #define PFRU_DRIVER_UPDATE_TYPE 2
37 #define PFRU_REVID_1 1
38 #define PFRU_REVID_2 2
39 #define PFRU_DEFAULT_REV_ID PFRU_REVID_1
44 CAP_CODE_TYPE_IDX
= 2,
46 CAP_CODE_RT_VER_IDX
= 4,
48 CAP_DRV_RT_VER_IDX
= 6,
52 CAP_OEM_INFO_IDX
= 10,
58 BUF_EXT_STATUS_IDX
= 1,
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,
75 enum pfru_start_action
{
78 START_STAGE_ACTIVATE
= 2,
83 struct device
*parent_dev
;
84 struct miscdevice miscdev
;
87 static DEFINE_IDA(pfru_ida
);
91 * https://uefi.org/sites/default/files/resources/Intel_MM_OS_Interface_Spec_Rev100.pdf
93 * pfru_guid is the parameter for _DSM method
95 static const guid_t pfru_guid
=
96 GUID_INIT(0xECF9533B, 0x4A3C, 0x4E89, 0x93, 0x9E, 0xC7, 0x71,
97 0x12, 0x60, 0x1C, 0x6D);
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);
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);
109 static inline int pfru_valid_revid(u32 id
)
111 return id
== PFRU_REVID_1
|| id
== PFRU_REVID_2
;
114 static inline struct pfru_device
*to_pfru_dev(struct file
*file
)
116 return container_of(file
->private_data
, struct pfru_device
, miscdev
);
119 static int query_capability(struct pfru_update_cap_info
*cap_hdr
,
120 struct pfru_device
*pfru_dev
)
122 acpi_handle handle
= ACPI_HANDLE(pfru_dev
->parent_dev
);
123 union acpi_object
*out_obj
;
126 out_obj
= acpi_evaluate_dsm_typed(handle
, &pfru_guid
,
128 PFRU_FUNC_QUERY_UPDATE_CAP
,
129 NULL
, ACPI_TYPE_PACKAGE
);
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
;
147 cap_hdr
->status
= out_obj
->package
.elements
[CAP_STATUS_IDX
].integer
.value
;
148 if (cap_hdr
->status
!= DSM_SUCCEED
) {
150 dev_dbg(pfru_dev
->parent_dev
, "Error Status:%d\n", cap_hdr
->status
);
151 goto free_acpi_buffer
;
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
;
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
;
186 static int query_buffer(struct pfru_com_buf_info
*info
,
187 struct pfru_device
*pfru_dev
)
189 acpi_handle handle
= ACPI_HANDLE(pfru_dev
->parent_dev
);
190 union acpi_object
*out_obj
;
193 out_obj
= acpi_evaluate_dsm_typed(handle
, &pfru_guid
,
194 pfru_dev
->rev_id
, PFRU_FUNC_QUERY_BUF
,
195 NULL
, ACPI_TYPE_PACKAGE
);
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
;
207 info
->status
= out_obj
->package
.elements
[BUF_STATUS_IDX
].integer
.value
;
209 out_obj
->package
.elements
[BUF_EXT_STATUS_IDX
].integer
.value
;
210 if (info
->status
!= DSM_SUCCEED
) {
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
);
215 goto free_acpi_buffer
;
219 out_obj
->package
.elements
[BUF_ADDR_LOW_IDX
].integer
.value
;
221 out_obj
->package
.elements
[BUF_ADDR_HI_IDX
].integer
.value
;
222 info
->buf_size
= out_obj
->package
.elements
[BUF_SIZE_IDX
].integer
.value
;
232 static int get_image_type(const struct efi_manage_capsule_image_header
*img_hdr
,
233 struct pfru_device
*pfru_dev
)
235 const efi_guid_t
*image_type_id
= &img_hdr
->image_type_id
;
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
;
241 if (guid_equal(image_type_id
, &pfru_drv_update_guid
))
242 return PFRU_DRIVER_UPDATE_TYPE
;
247 static int adjust_efi_size(const struct efi_manage_capsule_image_header
*img_hdr
,
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.
257 size
+= sizeof(struct efi_manage_capsule_image_header
);
258 switch (img_hdr
->ver
) {
260 return size
- 2 * sizeof(u64
);
263 return size
- sizeof(u64
);
266 /* only support version 1 and 2 */
271 static bool applicable_image(const void *data
, struct pfru_update_cap_info
*cap
,
272 struct pfru_device
*pfru_dev
)
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
;
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.
287 size
= cap_hdr
->headersize
;
290 * Current data structure size plus variable array indicated
291 * by number of (emb_drv_cnt + payload_cnt)
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
;
297 type
= get_image_type(m_img_hdr
, pfru_dev
);
301 size
= adjust_efi_size(m_img_hdr
, size
);
306 size
+= sizeof(u64
) + auth
->auth_info
.hdr
.len
;
307 payload_hdr
= (struct pfru_payload_hdr
*)(data
+ size
);
309 /* finally compare the version */
310 if (type
== PFRU_CODE_INJECT_TYPE
)
311 return payload_hdr
->rt_ver
>= cap
->code_rt_version
;
313 return payload_hdr
->rt_ver
>= cap
->drv_rt_version
;
316 static void print_update_debug_info(struct pfru_updated_result
*result
,
317 struct pfru_device
*pfru_dev
)
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
);
330 static int start_update(int action
, struct pfru_device
*pfru_dev
)
332 union acpi_object
*out_obj
, in_obj
, in_buf
;
333 struct pfru_updated_result update_result
;
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
;
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
);
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
;
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
;
366 if (update_result
.status
!= DSM_SUCCEED
) {
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
);
372 goto free_acpi_buffer
;
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
;
384 print_update_debug_info(&update_result
, pfru_dev
);
393 static long pfru_ioctl(struct file
*file
, unsigned int cmd
, unsigned long arg
)
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
;
402 case PFRU_IOC_QUERY_CAP
:
403 ret
= query_capability(&cap_hdr
, pfru_dev
);
407 if (copy_to_user(p
, &cap_hdr
, sizeof(cap_hdr
)))
412 case PFRU_IOC_SET_REV
:
413 if (copy_from_user(&rev
, p
, sizeof(rev
)))
416 if (!pfru_valid_revid(rev
))
419 pfru_dev
->rev_id
= rev
;
424 return start_update(START_STAGE
, pfru_dev
);
426 case PFRU_IOC_ACTIVATE
:
427 return start_update(START_ACTIVATE
, pfru_dev
);
429 case PFRU_IOC_STAGE_ACTIVATE
:
430 return start_update(START_STAGE_ACTIVATE
, pfru_dev
);
437 static ssize_t
pfru_write(struct file
*file
, const char __user
*buf
,
438 size_t len
, loff_t
*ppos
)
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
;
449 ret
= query_buffer(&buf_info
, pfru_dev
);
453 if (len
> buf_info
.buf_size
)
456 iov
.iov_base
= (void __user
*)buf
;
458 iov_iter_init(&iter
, ITER_SOURCE
, &iov
, 1, len
);
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
);
466 if (!copy_from_iter_full(buf_ptr
, len
, &iter
)) {
471 /* check if the capsule header has a valid version number */
472 ret
= query_capability(&cap
, pfru_dev
);
476 if (!applicable_image(buf_ptr
, &cap
, pfru_dev
))
485 static const struct file_operations acpi_pfru_fops
= {
486 .owner
= THIS_MODULE
,
488 .unlocked_ioctl
= pfru_ioctl
,
489 .llseek
= noop_llseek
,
492 static void acpi_pfru_remove(struct platform_device
*pdev
)
494 struct pfru_device
*pfru_dev
= platform_get_drvdata(pdev
);
496 misc_deregister(&pfru_dev
->miscdev
);
499 static void pfru_put_idx(void *data
)
501 struct pfru_device
*pfru_dev
= data
;
503 ida_free(&pfru_ida
, pfru_dev
->index
);
506 static int acpi_pfru_probe(struct platform_device
*pdev
)
508 acpi_handle handle
= ACPI_HANDLE(&pdev
->dev
);
509 struct pfru_device
*pfru_dev
;
512 if (!acpi_has_method(handle
, "_DSM")) {
513 dev_dbg(&pdev
->dev
, "Missing _DSM\n");
517 pfru_dev
= devm_kzalloc(&pdev
->dev
, sizeof(*pfru_dev
), GFP_KERNEL
);
521 ret
= ida_alloc(&pfru_ida
, GFP_KERNEL
);
525 pfru_dev
->index
= ret
;
526 ret
= devm_add_action_or_reset(&pdev
->dev
, pfru_put_idx
, pfru_dev
);
530 pfru_dev
->rev_id
= PFRU_DEFAULT_REV_ID
;
531 pfru_dev
->parent_dev
= &pdev
->dev
;
533 pfru_dev
->miscdev
.minor
= MISC_DYNAMIC_MINOR
;
534 pfru_dev
->miscdev
.name
= devm_kasprintf(&pdev
->dev
, GFP_KERNEL
,
535 "pfru%d", pfru_dev
->index
);
536 if (!pfru_dev
->miscdev
.name
)
539 pfru_dev
->miscdev
.nodename
= devm_kasprintf(&pdev
->dev
, GFP_KERNEL
,
540 "acpi_pfr_update%d", pfru_dev
->index
);
541 if (!pfru_dev
->miscdev
.nodename
)
544 pfru_dev
->miscdev
.fops
= &acpi_pfru_fops
;
545 pfru_dev
->miscdev
.parent
= &pdev
->dev
;
547 ret
= misc_register(&pfru_dev
->miscdev
);
551 platform_set_drvdata(pdev
, pfru_dev
);
556 static const struct acpi_device_id acpi_pfru_ids
[] = {
560 MODULE_DEVICE_TABLE(acpi
, acpi_pfru_ids
);
562 static struct platform_driver acpi_pfru_driver
= {
564 .name
= "pfr_update",
565 .acpi_match_table
= acpi_pfru_ids
,
567 .probe
= acpi_pfru_probe
,
568 .remove
= acpi_pfru_remove
,
570 module_platform_driver(acpi_pfru_driver
);
572 MODULE_DESCRIPTION("Platform Firmware Runtime Update device driver");
573 MODULE_LICENSE("GPL v2");