1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* |
3 | * Copyright 2012 IBM Corporation |
4 | * |
5 | * Author: Ashley Lai <ashleydlai@gmail.com> |
6 | * Nayna Jain <nayna@linux.vnet.ibm.com> |
7 | * |
8 | * Maintained by: <tpmdd-devel@lists.sourceforge.net> |
9 | * |
10 | * Read the event log created by the firmware on PPC64 |
11 | */ |
12 | |
13 | #include <linux/device.h> |
14 | #include <linux/slab.h> |
15 | #include <linux/io.h> |
16 | #include <linux/ioport.h> |
17 | #include <linux/of.h> |
18 | #include <linux/of_address.h> |
19 | #include <linux/of_reserved_mem.h> |
20 | #include <linux/tpm_eventlog.h> |
21 | |
22 | #include "../tpm.h" |
23 | #include "common.h" |
24 | |
25 | static int tpm_read_log_memory_region(struct tpm_chip *chip) |
26 | { |
27 | struct device_node *node; |
28 | struct resource res; |
29 | int rc; |
30 | |
31 | node = of_parse_phandle(np: chip->dev.parent->of_node, phandle_name: "memory-region" , index: 0); |
32 | if (!node) |
33 | return -ENODEV; |
34 | |
35 | rc = of_address_to_resource(dev: node, index: 0, r: &res); |
36 | of_node_put(node); |
37 | if (rc) |
38 | return rc; |
39 | |
40 | chip->log.bios_event_log = devm_memremap(dev: &chip->dev, offset: res.start, size: resource_size(res: &res), |
41 | flags: MEMREMAP_WB); |
42 | if (IS_ERR(ptr: chip->log.bios_event_log)) |
43 | return -ENOMEM; |
44 | |
45 | chip->log.bios_event_log_end = chip->log.bios_event_log + resource_size(res: &res); |
46 | |
47 | return chip->flags & TPM_CHIP_FLAG_TPM2 ? EFI_TCG2_EVENT_LOG_FORMAT_TCG_2 : |
48 | EFI_TCG2_EVENT_LOG_FORMAT_TCG_1_2; |
49 | } |
50 | |
51 | int tpm_read_log_of(struct tpm_chip *chip) |
52 | { |
53 | struct device_node *np; |
54 | const u32 *sizep; |
55 | const u64 *basep; |
56 | struct tpm_bios_log *log; |
57 | u32 size; |
58 | u64 base; |
59 | |
60 | log = &chip->log; |
61 | if (chip->dev.parent && chip->dev.parent->of_node) |
62 | np = chip->dev.parent->of_node; |
63 | else |
64 | return -ENODEV; |
65 | |
66 | if (of_property_read_bool(np, propname: "powered-while-suspended" )) |
67 | chip->flags |= TPM_CHIP_FLAG_ALWAYS_POWERED; |
68 | |
69 | sizep = of_get_property(node: np, name: "linux,sml-size" , NULL); |
70 | basep = of_get_property(node: np, name: "linux,sml-base" , NULL); |
71 | if (sizep == NULL && basep == NULL) |
72 | return tpm_read_log_memory_region(chip); |
73 | if (sizep == NULL || basep == NULL) |
74 | return -EIO; |
75 | |
76 | /* |
77 | * For both vtpm/tpm, firmware has log addr and log size in big |
78 | * endian format. But in case of vtpm, there is a method called |
79 | * sml-handover which is run during kernel init even before |
80 | * device tree is setup. This sml-handover function takes care |
81 | * of endianness and writes to sml-base and sml-size in little |
82 | * endian format. For this reason, vtpm doesn't need conversion |
83 | * but physical tpm needs the conversion. |
84 | */ |
85 | if (of_property_match_string(np, propname: "compatible" , string: "IBM,vtpm" ) < 0 && |
86 | of_property_match_string(np, propname: "compatible" , string: "IBM,vtpm20" ) < 0) { |
87 | size = be32_to_cpup(p: (__force __be32 *)sizep); |
88 | base = be64_to_cpup(p: (__force __be64 *)basep); |
89 | } else { |
90 | size = *sizep; |
91 | base = *basep; |
92 | } |
93 | |
94 | if (size == 0) { |
95 | dev_warn(&chip->dev, "%s: Event log area empty\n" , __func__); |
96 | return -EIO; |
97 | } |
98 | |
99 | log->bios_event_log = devm_kmemdup(dev: &chip->dev, __va(base), len: size, GFP_KERNEL); |
100 | if (!log->bios_event_log) |
101 | return -ENOMEM; |
102 | |
103 | log->bios_event_log_end = log->bios_event_log + size; |
104 | |
105 | if (chip->flags & TPM_CHIP_FLAG_TPM2) |
106 | return EFI_TCG2_EVENT_LOG_FORMAT_TCG_2; |
107 | return EFI_TCG2_EVENT_LOG_FORMAT_TCG_1_2; |
108 | } |
109 | |