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