1 | /* |
2 | * tpm_crb.c - QEMU's TPM CRB interface emulator |
3 | * |
4 | * Copyright (c) 2018 Red Hat, Inc. |
5 | * |
6 | * Authors: |
7 | * Marc-André Lureau <marcandre.lureau@redhat.com> |
8 | * |
9 | * This work is licensed under the terms of the GNU GPL, version 2 or later. |
10 | * See the COPYING file in the top-level directory. |
11 | * |
12 | * tpm_crb is a device for TPM 2.0 Command Response Buffer (CRB) Interface |
13 | * as defined in TCG PC Client Platform TPM Profile (PTP) Specification |
14 | * Family “2.0” Level 00 Revision 01.03 v22 |
15 | */ |
16 | |
17 | #include "qemu/osdep.h" |
18 | |
19 | #include "qemu/module.h" |
20 | #include "qapi/error.h" |
21 | #include "exec/address-spaces.h" |
22 | #include "hw/qdev-properties.h" |
23 | #include "hw/pci/pci_ids.h" |
24 | #include "hw/acpi/tpm.h" |
25 | #include "migration/vmstate.h" |
26 | #include "sysemu/tpm_backend.h" |
27 | #include "sysemu/reset.h" |
28 | #include "tpm_int.h" |
29 | #include "tpm_util.h" |
30 | #include "tpm_ppi.h" |
31 | #include "trace.h" |
32 | |
33 | typedef struct CRBState { |
34 | DeviceState parent_obj; |
35 | |
36 | TPMBackend *tpmbe; |
37 | TPMBackendCmd cmd; |
38 | uint32_t regs[TPM_CRB_R_MAX]; |
39 | MemoryRegion mmio; |
40 | MemoryRegion cmdmem; |
41 | |
42 | size_t be_buffer_size; |
43 | |
44 | bool ppi_enabled; |
45 | TPMPPI ppi; |
46 | } CRBState; |
47 | |
48 | #define CRB(obj) OBJECT_CHECK(CRBState, (obj), TYPE_TPM_CRB) |
49 | |
50 | #define CRB_INTF_TYPE_CRB_ACTIVE 0b1 |
51 | #define CRB_INTF_VERSION_CRB 0b1 |
52 | #define CRB_INTF_CAP_LOCALITY_0_ONLY 0b0 |
53 | #define CRB_INTF_CAP_IDLE_FAST 0b0 |
54 | #define CRB_INTF_CAP_XFER_SIZE_64 0b11 |
55 | #define CRB_INTF_CAP_FIFO_NOT_SUPPORTED 0b0 |
56 | #define CRB_INTF_CAP_CRB_SUPPORTED 0b1 |
57 | #define CRB_INTF_IF_SELECTOR_CRB 0b1 |
58 | |
59 | #define CRB_CTRL_CMD_SIZE (TPM_CRB_ADDR_SIZE - A_CRB_DATA_BUFFER) |
60 | |
61 | enum crb_loc_ctrl { |
62 | CRB_LOC_CTRL_REQUEST_ACCESS = BIT(0), |
63 | CRB_LOC_CTRL_RELINQUISH = BIT(1), |
64 | CRB_LOC_CTRL_SEIZE = BIT(2), |
65 | CRB_LOC_CTRL_RESET_ESTABLISHMENT_BIT = BIT(3), |
66 | }; |
67 | |
68 | enum crb_ctrl_req { |
69 | CRB_CTRL_REQ_CMD_READY = BIT(0), |
70 | CRB_CTRL_REQ_GO_IDLE = BIT(1), |
71 | }; |
72 | |
73 | enum crb_start { |
74 | CRB_START_INVOKE = BIT(0), |
75 | }; |
76 | |
77 | enum crb_cancel { |
78 | CRB_CANCEL_INVOKE = BIT(0), |
79 | }; |
80 | |
81 | #define TPM_CRB_NO_LOCALITY 0xff |
82 | |
83 | static uint64_t tpm_crb_mmio_read(void *opaque, hwaddr addr, |
84 | unsigned size) |
85 | { |
86 | CRBState *s = CRB(opaque); |
87 | void *regs = (void *)&s->regs + (addr & ~3); |
88 | unsigned offset = addr & 3; |
89 | uint32_t val = *(uint32_t *)regs >> (8 * offset); |
90 | |
91 | switch (addr) { |
92 | case A_CRB_LOC_STATE: |
93 | val |= !tpm_backend_get_tpm_established_flag(s->tpmbe); |
94 | break; |
95 | } |
96 | |
97 | trace_tpm_crb_mmio_read(addr, size, val); |
98 | |
99 | return val; |
100 | } |
101 | |
102 | static uint8_t tpm_crb_get_active_locty(CRBState *s) |
103 | { |
104 | if (!ARRAY_FIELD_EX32(s->regs, CRB_LOC_STATE, locAssigned)) { |
105 | return TPM_CRB_NO_LOCALITY; |
106 | } |
107 | return ARRAY_FIELD_EX32(s->regs, CRB_LOC_STATE, activeLocality); |
108 | } |
109 | |
110 | static void tpm_crb_mmio_write(void *opaque, hwaddr addr, |
111 | uint64_t val, unsigned size) |
112 | { |
113 | CRBState *s = CRB(opaque); |
114 | uint8_t locty = addr >> 12; |
115 | |
116 | trace_tpm_crb_mmio_write(addr, size, val); |
117 | |
118 | switch (addr) { |
119 | case A_CRB_CTRL_REQ: |
120 | switch (val) { |
121 | case CRB_CTRL_REQ_CMD_READY: |
122 | ARRAY_FIELD_DP32(s->regs, CRB_CTRL_STS, |
123 | tpmIdle, 0); |
124 | break; |
125 | case CRB_CTRL_REQ_GO_IDLE: |
126 | ARRAY_FIELD_DP32(s->regs, CRB_CTRL_STS, |
127 | tpmIdle, 1); |
128 | break; |
129 | } |
130 | break; |
131 | case A_CRB_CTRL_CANCEL: |
132 | if (val == CRB_CANCEL_INVOKE && |
133 | s->regs[R_CRB_CTRL_START] & CRB_START_INVOKE) { |
134 | tpm_backend_cancel_cmd(s->tpmbe); |
135 | } |
136 | break; |
137 | case A_CRB_CTRL_START: |
138 | if (val == CRB_START_INVOKE && |
139 | !(s->regs[R_CRB_CTRL_START] & CRB_START_INVOKE) && |
140 | tpm_crb_get_active_locty(s) == locty) { |
141 | void *mem = memory_region_get_ram_ptr(&s->cmdmem); |
142 | |
143 | s->regs[R_CRB_CTRL_START] |= CRB_START_INVOKE; |
144 | s->cmd = (TPMBackendCmd) { |
145 | .in = mem, |
146 | .in_len = MIN(tpm_cmd_get_size(mem), s->be_buffer_size), |
147 | .out = mem, |
148 | .out_len = s->be_buffer_size, |
149 | }; |
150 | |
151 | tpm_backend_deliver_request(s->tpmbe, &s->cmd); |
152 | } |
153 | break; |
154 | case A_CRB_LOC_CTRL: |
155 | switch (val) { |
156 | case CRB_LOC_CTRL_RESET_ESTABLISHMENT_BIT: |
157 | /* not loc 3 or 4 */ |
158 | break; |
159 | case CRB_LOC_CTRL_RELINQUISH: |
160 | ARRAY_FIELD_DP32(s->regs, CRB_LOC_STATE, |
161 | locAssigned, 0); |
162 | ARRAY_FIELD_DP32(s->regs, CRB_LOC_STS, |
163 | Granted, 0); |
164 | break; |
165 | case CRB_LOC_CTRL_REQUEST_ACCESS: |
166 | ARRAY_FIELD_DP32(s->regs, CRB_LOC_STS, |
167 | Granted, 1); |
168 | ARRAY_FIELD_DP32(s->regs, CRB_LOC_STS, |
169 | beenSeized, 0); |
170 | ARRAY_FIELD_DP32(s->regs, CRB_LOC_STATE, |
171 | locAssigned, 1); |
172 | break; |
173 | } |
174 | break; |
175 | } |
176 | } |
177 | |
178 | static const MemoryRegionOps tpm_crb_memory_ops = { |
179 | .read = tpm_crb_mmio_read, |
180 | .write = tpm_crb_mmio_write, |
181 | .endianness = DEVICE_LITTLE_ENDIAN, |
182 | .valid = { |
183 | .min_access_size = 1, |
184 | .max_access_size = 4, |
185 | }, |
186 | }; |
187 | |
188 | static void tpm_crb_request_completed(TPMIf *ti, int ret) |
189 | { |
190 | CRBState *s = CRB(ti); |
191 | |
192 | s->regs[R_CRB_CTRL_START] &= ~CRB_START_INVOKE; |
193 | if (ret != 0) { |
194 | ARRAY_FIELD_DP32(s->regs, CRB_CTRL_STS, |
195 | tpmSts, 1); /* fatal error */ |
196 | } |
197 | } |
198 | |
199 | static enum TPMVersion tpm_crb_get_version(TPMIf *ti) |
200 | { |
201 | CRBState *s = CRB(ti); |
202 | |
203 | return tpm_backend_get_tpm_version(s->tpmbe); |
204 | } |
205 | |
206 | static int tpm_crb_pre_save(void *opaque) |
207 | { |
208 | CRBState *s = opaque; |
209 | |
210 | tpm_backend_finish_sync(s->tpmbe); |
211 | |
212 | return 0; |
213 | } |
214 | |
215 | static const VMStateDescription vmstate_tpm_crb = { |
216 | .name = "tpm-crb" , |
217 | .pre_save = tpm_crb_pre_save, |
218 | .fields = (VMStateField[]) { |
219 | VMSTATE_UINT32_ARRAY(regs, CRBState, TPM_CRB_R_MAX), |
220 | VMSTATE_END_OF_LIST(), |
221 | } |
222 | }; |
223 | |
224 | static Property tpm_crb_properties[] = { |
225 | DEFINE_PROP_TPMBE("tpmdev" , CRBState, tpmbe), |
226 | DEFINE_PROP_BOOL("ppi" , CRBState, ppi_enabled, true), |
227 | DEFINE_PROP_END_OF_LIST(), |
228 | }; |
229 | |
230 | static void tpm_crb_reset(void *dev) |
231 | { |
232 | CRBState *s = CRB(dev); |
233 | |
234 | if (s->ppi_enabled) { |
235 | tpm_ppi_reset(&s->ppi); |
236 | } |
237 | tpm_backend_reset(s->tpmbe); |
238 | |
239 | memset(s->regs, 0, sizeof(s->regs)); |
240 | |
241 | ARRAY_FIELD_DP32(s->regs, CRB_LOC_STATE, |
242 | tpmRegValidSts, 1); |
243 | ARRAY_FIELD_DP32(s->regs, CRB_CTRL_STS, |
244 | tpmIdle, 1); |
245 | ARRAY_FIELD_DP32(s->regs, CRB_INTF_ID, |
246 | InterfaceType, CRB_INTF_TYPE_CRB_ACTIVE); |
247 | ARRAY_FIELD_DP32(s->regs, CRB_INTF_ID, |
248 | InterfaceVersion, CRB_INTF_VERSION_CRB); |
249 | ARRAY_FIELD_DP32(s->regs, CRB_INTF_ID, |
250 | CapLocality, CRB_INTF_CAP_LOCALITY_0_ONLY); |
251 | ARRAY_FIELD_DP32(s->regs, CRB_INTF_ID, |
252 | CapCRBIdleBypass, CRB_INTF_CAP_IDLE_FAST); |
253 | ARRAY_FIELD_DP32(s->regs, CRB_INTF_ID, |
254 | CapDataXferSizeSupport, CRB_INTF_CAP_XFER_SIZE_64); |
255 | ARRAY_FIELD_DP32(s->regs, CRB_INTF_ID, |
256 | CapFIFO, CRB_INTF_CAP_FIFO_NOT_SUPPORTED); |
257 | ARRAY_FIELD_DP32(s->regs, CRB_INTF_ID, |
258 | CapCRB, CRB_INTF_CAP_CRB_SUPPORTED); |
259 | ARRAY_FIELD_DP32(s->regs, CRB_INTF_ID, |
260 | InterfaceSelector, CRB_INTF_IF_SELECTOR_CRB); |
261 | ARRAY_FIELD_DP32(s->regs, CRB_INTF_ID, |
262 | RID, 0b0000); |
263 | ARRAY_FIELD_DP32(s->regs, CRB_INTF_ID2, |
264 | VID, PCI_VENDOR_ID_IBM); |
265 | |
266 | s->regs[R_CRB_CTRL_CMD_SIZE] = CRB_CTRL_CMD_SIZE; |
267 | s->regs[R_CRB_CTRL_CMD_LADDR] = TPM_CRB_ADDR_BASE + A_CRB_DATA_BUFFER; |
268 | s->regs[R_CRB_CTRL_RSP_SIZE] = CRB_CTRL_CMD_SIZE; |
269 | s->regs[R_CRB_CTRL_RSP_ADDR] = TPM_CRB_ADDR_BASE + A_CRB_DATA_BUFFER; |
270 | |
271 | s->be_buffer_size = MIN(tpm_backend_get_buffer_size(s->tpmbe), |
272 | CRB_CTRL_CMD_SIZE); |
273 | |
274 | if (tpm_backend_startup_tpm(s->tpmbe, s->be_buffer_size) < 0) { |
275 | exit(1); |
276 | } |
277 | } |
278 | |
279 | static void tpm_crb_realize(DeviceState *dev, Error **errp) |
280 | { |
281 | CRBState *s = CRB(dev); |
282 | |
283 | if (!tpm_find()) { |
284 | error_setg(errp, "at most one TPM device is permitted" ); |
285 | return; |
286 | } |
287 | if (!s->tpmbe) { |
288 | error_setg(errp, "'tpmdev' property is required" ); |
289 | return; |
290 | } |
291 | |
292 | memory_region_init_io(&s->mmio, OBJECT(s), &tpm_crb_memory_ops, s, |
293 | "tpm-crb-mmio" , sizeof(s->regs)); |
294 | memory_region_init_ram(&s->cmdmem, OBJECT(s), |
295 | "tpm-crb-cmd" , CRB_CTRL_CMD_SIZE, errp); |
296 | |
297 | memory_region_add_subregion(get_system_memory(), |
298 | TPM_CRB_ADDR_BASE, &s->mmio); |
299 | memory_region_add_subregion(get_system_memory(), |
300 | TPM_CRB_ADDR_BASE + sizeof(s->regs), &s->cmdmem); |
301 | |
302 | if (s->ppi_enabled) { |
303 | tpm_ppi_init(&s->ppi, get_system_memory(), |
304 | TPM_PPI_ADDR_BASE, OBJECT(s)); |
305 | } |
306 | |
307 | qemu_register_reset(tpm_crb_reset, dev); |
308 | } |
309 | |
310 | static void tpm_crb_class_init(ObjectClass *klass, void *data) |
311 | { |
312 | DeviceClass *dc = DEVICE_CLASS(klass); |
313 | TPMIfClass *tc = TPM_IF_CLASS(klass); |
314 | |
315 | dc->realize = tpm_crb_realize; |
316 | dc->props = tpm_crb_properties; |
317 | dc->vmsd = &vmstate_tpm_crb; |
318 | dc->user_creatable = true; |
319 | tc->model = TPM_MODEL_TPM_CRB; |
320 | tc->get_version = tpm_crb_get_version; |
321 | tc->request_completed = tpm_crb_request_completed; |
322 | |
323 | set_bit(DEVICE_CATEGORY_MISC, dc->categories); |
324 | } |
325 | |
326 | static const TypeInfo tpm_crb_info = { |
327 | .name = TYPE_TPM_CRB, |
328 | /* could be TYPE_SYS_BUS_DEVICE (or LPC etc) */ |
329 | .parent = TYPE_DEVICE, |
330 | .instance_size = sizeof(CRBState), |
331 | .class_init = tpm_crb_class_init, |
332 | .interfaces = (InterfaceInfo[]) { |
333 | { TYPE_TPM_IF }, |
334 | { } |
335 | } |
336 | }; |
337 | |
338 | static void tpm_crb_register(void) |
339 | { |
340 | type_register_static(&tpm_crb_info); |
341 | } |
342 | |
343 | type_init(tpm_crb_register) |
344 | |