source: trunk/packages/xen-common/xen-common/tools/ioemu/patches/tpm-tis-device @ 34

Last change on this file since 34 was 34, checked in by hartmans, 18 years ago

Add xen and xen-common

File size: 37.5 KB
Line 
1# HG changeset patch
2# User kaf24@localhost.localdomain
3# Node ID d60b709724f48397b95da3d56299213cae391789
4# Parent  bbcac2aea0e8196cd75a3bf6dbe57bebf8c1e5b2
5[QEMU] Add a TIS device model compliant to the 1.2 TPM specification.
6It implements all registers necessary to make the Linux TIS driver
7work (tpm_tis.c). All of the basic registers supported by this type of
8device are implemented. Also the locality selection has been
9implemented, but has not been tested. The legacy registers as
10described in the specification are not supported.
11
12Current caveat: The device has so far not yet been integrated with the
13virtual TPM available in the repository. It will require changes to
14the virtual TPM spawned by the vTPM manager to offer an additional message
15interface. The TIS interface itself then needs to have an additional
16transport implemented. (see vTPMTransmit array).
17
18The relevant specification for the device model can be found here:
19https://www.trustedcomputinggroup.org/groups/pc_client/TCG_PCClientTPMSpecification_1-20_1-00_FINAL.pdf
20
21Signed-off-by: Stefan Berger <stefanb@us.ibm.com>
22
23Index: ioemu/Makefile.target
24===================================================================
25--- ioemu.orig/Makefile.target  2007-05-03 15:20:44.000000000 +0100
26+++ ioemu/Makefile.target       2007-05-03 15:20:44.000000000 +0100
27@@ -369,6 +369,7 @@
28 VL_OBJS+= piix4acpi.o
29 VL_OBJS+= xenstore.o
30 VL_OBJS+= xen_platform.o
31+VL_OBJS+= tpm_tis.o
32 DEFINES += -DHAS_AUDIO
33 endif
34 ifeq ($(TARGET_BASE_ARCH), ppc)
35Index: ioemu/hw/pc.c
36===================================================================
37--- ioemu.orig/hw/pc.c  2007-05-03 15:20:43.000000000 +0100
38+++ ioemu/hw/pc.c       2007-05-03 15:20:44.000000000 +0100
39@@ -877,6 +877,9 @@
40         }
41     }
42 
43+    if (has_tpm_device())
44+        tpm_tis_init(&pic_set_irq_new, isa_pic, 11);
45+
46     kbd_init();
47     DMA_init(0);
48 #ifdef HAS_AUDIO
49Index: ioemu/hw/tpm_tis.c
50===================================================================
51--- /dev/null   1970-01-01 00:00:00.000000000 +0000
52+++ ioemu/hw/tpm_tis.c  2007-05-03 15:20:44.000000000 +0100
53@@ -0,0 +1,1128 @@
54+/*
55+ * tpm_tis.c - QEMU emulator for a 1.2 TPM with TIS interface
56+ *
57+ * Copyright (C) 2006 IBM Corporation
58+ *
59+ * Author: Stefan Berger <stefanb@us.ibm.com>
60+ *         David Safford <safford@us.ibm.com>
61+ *
62+ * This program is free software; you can redistribute it and/or
63+ * modify it under the terms of the GNU General Public License as
64+ * published by the Free Software Foundation, version 2 of the
65+ * License.
66+ *
67+ *
68+ * Implementation of the TIS interface according to specs at
69+ * https://www.trustedcomputinggroup.org/groups/pc_client/TCG_PCClientTPMSpecification_1-20_1-00_FINAL.pdf
70+ *
71+ */
72+
73+#include <sys/types.h>
74+#include <sys/stat.h>
75+#include <sys/socket.h>
76+#include <sys/un.h>
77+#include <fcntl.h>
78+#include <errno.h>
79+#include "vl.h"
80+
81+//#define DEBUG_TPM
82+
83+#define TPM_MAX_PKT                  4096
84+
85+#define VTPM_BAD_INSTANCE             (uint32_t)0xffffffff
86+
87+#define TIS_ADDR_BASE                 0xFED40000
88+
89+/* tis registers */
90+#define TPM_REG_ACCESS                0x00
91+#define TPM_REG_INT_ENABLE            0x08
92+#define TPM_REG_INT_VECTOR            0x0c
93+#define TPM_REG_INT_STATUS            0x10
94+#define TPM_REG_INTF_CAPABILITY       0x14
95+#define TPM_REG_STS                   0x18
96+#define TPM_REG_DATA_FIFO             0x24
97+#define TPM_REG_DID_VID               0xf00
98+#define TPM_REG_RID                   0xf04
99+
100+#define STS_VALID                    (1 << 7)
101+#define STS_COMMAND_READY            (1 << 6)
102+#define STS_TPM_GO                   (1 << 5)
103+#define STS_DATA_AVAILABLE           (1 << 4)
104+#define STS_EXPECT                   (1 << 3)
105+#define STS_RESPONSE_RETRY           (1 << 1)
106+
107+#define ACCESS_TPM_REG_VALID_STS     (1 << 7)
108+#define ACCESS_ACTIVE_LOCALITY       (1 << 5)
109+#define ACCESS_BEEN_SEIZED           (1 << 4)
110+#define ACCESS_SEIZE                 (1 << 3)
111+#define ACCESS_PENDING_REQUEST       (1 << 2)
112+#define ACCESS_REQUEST_USE           (1 << 1)
113+#define ACCESS_TPM_ESTABLISHMENT     (1 << 0)
114+
115+#define INT_ENABLED                  (1 << 31)
116+#define INT_DATA_AVAILABLE           (1 << 0)
117+#define INT_LOCALITY_CHANGED         (1 << 2)
118+#define INT_COMMAND_READY            (1 << 7)
119+
120+#define INTERRUPTS_SUPPORTED         (INT_LOCALITY_CHANGED | \
121+                                      INT_DATA_AVAILABLE   | \
122+                                      INT_COMMAND_READY)
123+#define CAPABILITIES_SUPPORTED       ((1 << 4) |            \
124+                                      INTERRUPTS_SUPPORTED)
125+
126+enum {
127+  STATE_IDLE = 0,
128+  STATE_READY,
129+  STATE_COMPLETION,
130+  STATE_EXECUTION,
131+  STATE_RECEPTION
132+};
133+
134+#define NUM_LOCALITIES   5
135+#define NO_LOCALITY      0xff
136+
137+#define IS_VALID_LOC(x) ((x) < NUM_LOCALITIES)
138+
139+#define TPM_DID          0x0001
140+#define TPM_VID          0x0001
141+#define TPM_RID          0x0001
142+
143+/* if the connection to the vTPM should be closed after a successfully
144+   received response; set to '0' to allow keeping the connection */
145+#define FORCE_CLOSE      0
146+
147+/* local data structures */
148+
149+typedef struct TPMTx {
150+    int fd[2];
151+} tpmTx;
152+
153+typedef struct TPMBuffer {
154+    uint8_t instance[4];      /* instance number in network byte order */
155+    uint8_t buf[TPM_MAX_PKT];
156+} __attribute__((packed)) tpmBuffer;
157+
158+/* locality data */
159+typedef struct TPMLocal {
160+    uint32_t state;
161+    uint8_t access;
162+    uint8_t sts;
163+    uint32_t inte;
164+    uint32_t ints;
165+} tpmLoc;
166+
167+/* overall state of the TPM interface; 's' marks as save upon suspension */
168+typedef struct TPMState {
169+    uint32_t offset;            /* s */
170+    tpmBuffer buffer;           /* s */
171+    uint8_t active_loc;         /* s */
172+    uint8_t aborting_locty;
173+    uint8_t next_locty;
174+    uint8_t irq_pending;        /* s */
175+    tpmLoc loc[NUM_LOCALITIES]; /* s */
176+    QEMUTimer *poll_timer;
177+    SetIRQFunc *set_irq;
178+    void *irq_opaque;
179+    int irq;
180+    int poll_attempts;
181+    uint32_t vtpm_instance;  /* vtpm inst. number; determined from xenstore*/
182+    int Transmitlayer;
183+    tpmTx tpmTx;
184+} tpmState;
185+
186+
187+/* local prototypes */
188+static int TPM_Send(tpmState *s, tpmBuffer *buffer, uint8_t locty, char *msg);
189+static int TPM_Receive(tpmState *s, tpmBuffer *buffer);
190+static uint32_t vtpm_instance_from_xenstore(void);
191+static void tis_poll_timer(void *opaque);
192+static void tis_prep_next_interrupt(tpmState *s);
193+static void tis_raise_irq(tpmState *s, uint8_t locty, uint32_t irqmask);
194+static void close_vtpm_channel(tpmState *s, int force);
195+static void open_vtpm_channel(tpmState *s);
196+static void tis_attempt_receive(tpmState *s, uint8_t locty);
197+
198+/* transport layer functions: local sockets */
199+static int create_local_socket(tpmState *s, uint32_t vtpm_instance);
200+static int write_local_socket(tpmState *s, const tpmBuffer *);
201+static int read_local_socket(tpmState *s, tpmBuffer *);
202+static int close_local_socket(tpmState *s, int force);
203+static int has_channel_local_socket(tpmState *s);
204+#define LOCAL_SOCKET_PATH      "/var/vtpm/vtpm_all.socket"
205+
206+
207+#define NUM_TRANSPORTS 1
208+
209+struct vTPM_transmit {
210+    int (*open) (tpmState *s, uint32_t vtpm_instance);
211+    int (*write) (tpmState *s, const tpmBuffer *);
212+    int (*read) (tpmState *s, tpmBuffer *);
213+    int (*close) (tpmState *s, int);
214+    int (*has_channel) (tpmState *s);
215+} vTPMTransmit[NUM_TRANSPORTS] = {
216+    { .open = create_local_socket,
217+      .write = write_local_socket,
218+      .read = read_local_socket,
219+      .close = close_local_socket,
220+      .has_channel = has_channel_local_socket,
221+    }
222+};
223+
224+
225+#define IS_COMM_WITH_VTPM(s)                            \
226+     ((s)->Transmitlayer >= 0 &&                        \
227+      vTPMTransmit[(s)->Transmitlayer].has_channel(s))
228+
229+
230+/**********************************************************************
231+ helper functions
232+ *********************************************************************/
233+
234+static inline uint32_t tpm_get_size_from_buffer(const uint8_t *buffer)
235+{
236+    uint32_t len = (buffer[4] << 8) + buffer[5];
237+    return len;
238+}
239+
240+static inline void tpm_initialize_instance(tpmState *s, uint32_t instance)
241+{
242+    s->buffer.instance[0] = (instance >> 24) & 0xff;
243+    s->buffer.instance[1] = (instance >> 16) & 0xff;
244+    s->buffer.instance[2] = (instance >>  8) & 0xff;
245+    s->buffer.instance[3] = (instance >>  0) & 0xff;
246+}
247+
248+/*
249+ * open communication channel with a vTPM
250+ */
251+static void open_vtpm_channel(tpmState *s)
252+{
253+    int idx;
254+    /* search a usable transmit layer */
255+    for (idx = 0; idx < NUM_TRANSPORTS; idx++) {
256+        if (1 == vTPMTransmit[idx].open(s, s->vtpm_instance)) {
257+            /* found one */
258+            s->Transmitlayer = idx;
259+            break;
260+        }
261+    }
262+}
263+
264+/*
265+ * close the communication channel with the vTPM
266+ */
267+static inline void close_vtpm_channel(tpmState *s, int force)
268+{
269+    if (1 == vTPMTransmit[s->Transmitlayer].close(s, force)) {
270+        s->Transmitlayer = -1;
271+    }
272+}
273+
274+static inline uint8_t locality_from_addr(target_phys_addr_t addr)
275+{
276+    return (uint8_t)((addr >> 12) & 0x7);
277+}
278+
279+
280+/**********************************************************************
281+    low-level transmission layer methods
282+ *********************************************************************/
283+
284+/*
285+ * the 'open' method that creates the filedescriptor for communicating
286+ * only one is needed for reading and writing
287+ */
288+static int create_local_socket(tpmState *s, uint32_t vtpm_instance)
289+{
290+    int success = 1;
291+    if (s->tpmTx.fd[0] < 0) {
292+        s->tpmTx.fd[0] = socket(PF_LOCAL, SOCK_STREAM, 0);
293+
294+        if (has_channel_local_socket(s)) {
295+            struct sockaddr_un addr;
296+            memset(&addr, 0x0, sizeof(addr));
297+            addr.sun_family = AF_LOCAL;
298+            strcpy(addr.sun_path, LOCAL_SOCKET_PATH);
299+            if (connect(s->tpmTx.fd[0],
300+                        (struct sockaddr *)&addr,
301+                        sizeof(addr)) != 0) {
302+                close_local_socket(s, 1);
303+                success = 0;
304+            } else {
305+                /* put filedescriptor in non-blocking mode for polling */
306+                int flags = fcntl(s->tpmTx.fd[0], F_GETFL);
307+                fcntl(s->tpmTx.fd[0], F_SETFL, flags | O_NONBLOCK);
308+            }
309+#ifdef DEBUG_TPM
310+            if (success)
311+                fprintf(logfile,"Successfully connected using local socket "
312+                                LOCAL_SOCKET_PATH ".\n");
313+            else
314+                fprintf(logfile,"Could not connect to local socket "
315+                                LOCAL_SOCKET_PATH ".\n");
316+#endif
317+        } else {
318+            success = 0;
319+        }
320+    }
321+    return success;
322+}
323+
324+/*
325+ * the 'write' method for sending requests to the vTPM
326+ * four bytes with the vTPM instance number are prepended to each request
327+ * the locality in which the command was sent is transmitted in the
328+ * highest 3 bits
329+ */
330+static int write_local_socket(tpmState *s, const tpmBuffer *buffer)
331+{
332+    uint32_t size = tpm_get_size_from_buffer(buffer->buf);
333+    int len;
334+
335+    len = write(s->tpmTx.fd[0],
336+                buffer->instance,
337+                sizeof(buffer->instance) + size);
338+    if (len == sizeof(buffer->instance) + size) {
339+        return len;
340+    } else {
341+        return -1;
342+    }
343+}
344+
345+/*
346+ * the 'read' method for receiving of responses from the TPM
347+ * this function expects that four bytes with the instance number
348+ * are received from the vTPM
349+ */
350+static int read_local_socket(tpmState *s, tpmBuffer *buffer)
351+{
352+    int off;
353+#ifdef DEBUG_TPM
354+    fprintf(logfile, "Reading from fd %d\n", s->tpmTx.fd[0]);
355+#endif
356+    off = read(s->tpmTx.fd[0],
357+               buffer->instance,
358+               sizeof(buffer->instance)+TPM_MAX_PKT);
359+#ifdef DEBUG_TPM
360+    fprintf(logfile, "Read %d bytes\n", off);
361+#endif
362+    return off;
363+}
364+
365+/*
366+ * the 'close' method
367+ * shut down communication with the vTPM
368+ * 'force' = 1 indicates that the socket *must* be closed
369+ * 'force' = 0 indicates that a connection may be maintained
370+ */
371+static int close_local_socket(tpmState *s, int force)
372+{
373+    if (force) {
374+        close(s->tpmTx.fd[0]);
375+#ifdef DEBUG_TPM
376+        fprintf(logfile,"Closed connection with fd %d\n",s->tpmTx.fd[0]);
377+#endif
378+        s->tpmTx.fd[0] = -1;
379+        return 1; /* socket was closed */
380+    }
381+#ifdef DEBUG_TPM
382+    fprintf(logfile,"Keeping connection with fd %d\n",s->tpmTx.fd[0]);
383+#endif
384+    return 0;
385+}
386+
387+/*
388+ * the 'has_channel' method that checks whether there's a communication
389+ * channel with the vTPM
390+ */
391+static int has_channel_local_socket(tpmState *s)
392+{
393+    return (s->tpmTx.fd[0] > 0);
394+}
395+
396+/**********************************************************************/
397+
398+/*
399+ * read a byte of response data
400+ */
401+static uint32_t tpm_data_read(tpmState *s, uint8_t locty)
402+{
403+    uint32_t ret, len;
404+
405+    /* try to receive data, if none are there it is ok */
406+    tis_attempt_receive(s, locty);
407+
408+    if (s->loc[locty].state != STATE_COMPLETION) {
409+        return 0xff;
410+    }
411+
412+    len = tpm_get_size_from_buffer(s->buffer.buf);
413+    ret = s->buffer.buf[s->offset++];
414+    if (s->offset >= len) {
415+        s->loc[locty].sts = STS_VALID ;
416+        s->offset = 0;
417+    }
418+#ifdef DEBUG_TPM
419+    fprintf(logfile,"tpm_data_read byte x%02x   [%d]\n",ret,s->offset-1);
420+#endif
421+    return ret;
422+}
423+
424+
425+
426+/* raise an interrupt if allowed */
427+static void tis_raise_irq(tpmState *s, uint8_t locty, uint32_t irqmask)
428+{
429+    if (!s->irq_pending &&
430+        (s->loc[locty].inte & INT_ENABLED) &&
431+        (s->loc[locty].inte & irqmask)) {
432+        if ((irqmask & s->loc[locty].ints) == 0) {
433+#ifdef DEBUG_TPM
434+            fprintf(logfile,"Raising IRQ for flag %08x\n",irqmask);
435+#endif
436+            s->set_irq(s->irq_opaque, s->irq, 1);
437+            s->irq_pending = 1;
438+            s->loc[locty].ints |= irqmask;
439+        }
440+    }
441+}
442+
443+/* abort execution of command */
444+static void tis_abort(tpmState *s)
445+{
446+    s->offset = 0;
447+    s->active_loc = s->next_locty;
448+
449+    /*
450+     * Need to react differently depending on who's aborting now and
451+     * which locality will become active afterwards.
452+     */
453+    if (s->aborting_locty == s->next_locty) {
454+        s->loc[s->aborting_locty].state = STATE_READY;
455+        s->loc[s->aborting_locty].sts   = STS_COMMAND_READY;
456+        tis_raise_irq(s, s->aborting_locty, INT_COMMAND_READY);
457+    }
458+
459+    /* locality after abort is another one than the current one */
460+    if (s->aborting_locty != s->next_locty && s->next_locty != NO_LOCALITY) {
461+        s->loc[s->aborting_locty].access &= ~ACCESS_ACTIVE_LOCALITY;
462+        s->loc[s->next_locty].access     |=  ACCESS_ACTIVE_LOCALITY;
463+        tis_raise_irq(s, s->next_locty, INT_LOCALITY_CHANGED);
464+    }
465+
466+    s->aborting_locty = NO_LOCALITY; /* nobody's aborting a command anymore */
467+
468+    qemu_del_timer(s->poll_timer);
469+}
470+
471+/* abort current command */
472+static void tis_prep_abort(tpmState *s, uint8_t locty, uint8_t newlocty)
473+{
474+    s->aborting_locty = locty; /* current locality */
475+    s->next_locty = newlocty;  /* locality after successful abort */
476+
477+    /*
478+     * only abort a command using an interrupt if currently executing
479+     * a command AND if there's a valid connection to the vTPM.
480+     */
481+    if (s->loc[locty].state == STATE_EXECUTION &&
482+        IS_COMM_WITH_VTPM(s)) {
483+        /* start timer and inside the timer wait for the result */
484+        s->poll_attempts = 0;
485+        tis_prep_next_interrupt(s);
486+    } else {
487+        tis_abort(s);
488+    }
489+}
490+
491+
492+/*
493+ * Try to receive a response from the vTPM
494+ */
495+static void tis_attempt_receive(tpmState *s, uint8_t locty)
496+{
497+    /*
498+     * Attempt to read from the vTPM here if
499+     * - not aborting a command
500+     * - command has been sent and state is 'EXECUTION' now
501+     * - no data are already available (data have already been read)
502+     * - there's a communication path to the vTPM established
503+     */
504+    if (!IS_VALID_LOC(s->aborting_locty)) {
505+        if (s->loc[locty].state == STATE_EXECUTION) {
506+            if (0 == (s->loc[locty].sts & STS_DATA_AVAILABLE)){
507+                if (IS_COMM_WITH_VTPM(s)) {
508+                    int n = TPM_Receive(s, &s->buffer);
509+                    if (n > 0) {
510+                        s->loc[locty].sts = STS_VALID | STS_DATA_AVAILABLE;
511+                        s->loc[locty].state = STATE_COMPLETION;
512+                        close_vtpm_channel(s, FORCE_CLOSE);
513+                        tis_raise_irq(s, locty, INT_DATA_AVAILABLE);
514+                    }
515+                }
516+            }
517+        }
518+    }
519+}
520+
521+/*
522+ * Read a register of the TIS interface
523+ * See specs pages 33-63 for description of the registers
524+ */
525+static uint32_t tis_mem_readl(void *opaque, target_phys_addr_t addr)
526+{
527+    tpmState *s = (tpmState *)opaque;
528+    uint16_t offset = addr & 0xffc;
529+    uint8_t shift = (addr & 0x3) * 8;
530+    uint32_t val = 0;
531+    uint8_t locty = locality_from_addr(addr);
532+
533+    if (offset == TPM_REG_ACCESS) {
534+        if (s->active_loc == locty) {
535+            s->loc[locty].access |= (1 << 5);
536+         } else {
537+            s->loc[locty].access &= ~(1 << 5);
538+        }
539+        val = s->loc[locty].access;
540+    } else
541+    if (offset == TPM_REG_INT_ENABLE) {
542+        val = s->loc[locty].inte;
543+    } else
544+    if (offset == TPM_REG_INT_VECTOR) {
545+        val = s->irq;
546+    } else
547+    if (offset == TPM_REG_INT_STATUS) {
548+        tis_attempt_receive(s, locty);
549+        val = s->loc[locty].ints;
550+    } else
551+    if (offset == TPM_REG_INTF_CAPABILITY) {
552+        val = CAPABILITIES_SUPPORTED;
553+    } else
554+    if (offset == TPM_REG_STS) { /* status register */
555+        tis_attempt_receive(s, locty);
556+        val = (sizeof(s->buffer.buf) - s->offset) << 8 | s->loc[locty].sts;
557+    } else
558+    if (offset == TPM_REG_DATA_FIFO) {
559+      val = tpm_data_read(s, locty);
560+    } else
561+    if (offset == TPM_REG_DID_VID) {
562+        val = (TPM_DID << 16) | TPM_VID;
563+    } else
564+    if (offset == TPM_REG_RID) {
565+         val = TPM_RID;
566+    }
567+
568+    if (shift)
569+        val >>= shift;
570+
571+#ifdef DEBUG_TPM
572+    fprintf(logfile," read(%08x) = %08x\n",
573+            (int)addr,
574+            val);
575+#endif
576+
577+    return val;
578+}
579+
580+/*
581+ * Write a value to a register of the TIS interface
582+ * See specs pages 33-63 for description of the registers
583+ */
584+static void tis_mem_writel(void *opaque, target_phys_addr_t addr, uint32_t val)
585+{
586+    tpmState* s=(tpmState*)opaque;
587+    uint16_t off = addr & 0xfff;
588+    uint8_t locty = locality_from_addr(addr);
589+    int n, c;
590+    uint32_t len;
591+
592+#ifdef DEBUG_TPM
593+    fprintf(logfile,"write(%08x) = %08x\n",
594+            (int)addr,
595+            val);
596+#endif
597+
598+    if (off == TPM_REG_ACCESS) {
599+        if (val & ACCESS_ACTIVE_LOCALITY) {
600+            /* give up locality if currently owned */
601+            if (s->active_loc == locty) {
602+                uint8_t newlocty = NO_LOCALITY;
603+                s->loc[locty].access &= ~(ACCESS_PENDING_REQUEST);
604+                /* anybody wants the locality ? */
605+                for (c = NUM_LOCALITIES - 1; c >= 0; c--) {
606+                    if (s->loc[c].access & ACCESS_REQUEST_USE) {
607+                        s->loc[c].access |= ACCESS_TPM_REG_VALID_STS;
608+                        s->loc[c].access &= ~ACCESS_REQUEST_USE;
609+                        newlocty = c;
610+                        break;
611+                    }
612+                }
613+                tis_prep_abort(s, locty, newlocty);
614+            }
615+        }
616+        if (val & ACCESS_BEEN_SEIZED) {
617+            /* clear the flag */
618+            s->loc[locty].access &= ~ACCESS_BEEN_SEIZED;
619+        }
620+        if (val & ACCESS_SEIZE) {
621+            if (locty > s->active_loc && IS_VALID_LOC(s->active_loc)) {
622+                s->loc[s->active_loc].access |= ACCESS_BEEN_SEIZED;
623+                s->loc[locty].access = ACCESS_TPM_REG_VALID_STS;
624+                tis_prep_abort(s, s->active_loc, locty);
625+            }
626+        }
627+        if (val & ACCESS_REQUEST_USE) {
628+            if (IS_VALID_LOC(s->active_loc)) {
629+                /* locality election */
630+                s->loc[s->active_loc].access |= ACCESS_PENDING_REQUEST;
631+            } else {
632+                /* no locality active -> make this one active now */
633+                s->loc[locty].access |= ACCESS_ACTIVE_LOCALITY;
634+                s->active_loc = locty;
635+                tis_raise_irq(s, locty, INT_LOCALITY_CHANGED);
636+            }
637+        }
638+    } else
639+    if (off == TPM_REG_INT_ENABLE) {
640+        s->loc[locty].inte = (val & (INT_ENABLED | (0x3 << 3) |
641+                                     INTERRUPTS_SUPPORTED));
642+    } else
643+    if (off == TPM_REG_INT_STATUS) {
644+        /* clearing of interrupt flags */
645+        if ((val & INTERRUPTS_SUPPORTED) &&
646+            (s->loc[locty].ints & INTERRUPTS_SUPPORTED)) {
647+            s->set_irq(s->irq_opaque, s->irq, 0);
648+            s->irq_pending = 0;
649+        }
650+        s->loc[locty].ints &= ~(val & INTERRUPTS_SUPPORTED);
651+    } else
652+    if (off == TPM_REG_STS) {
653+        if (val & STS_COMMAND_READY) {
654+            if (s->loc[locty].state == STATE_IDLE) {
655+                s->loc[locty].sts   = STS_COMMAND_READY;
656+                s->loc[locty].state = STATE_READY;
657+                tis_raise_irq(s, locty, INT_COMMAND_READY);
658+            } else if (s->loc[locty].state == STATE_COMPLETION ||
659+                       s->loc[locty].state == STATE_EXECUTION  ||
660+                       s->loc[locty].state == STATE_RECEPTION) {
661+                /* abort currently running command */
662+                tis_prep_abort(s, locty, locty);
663+            }
664+        }
665+        if (val & STS_TPM_GO) {
666+            n = TPM_Send(s, &s->buffer, locty, "tpm_data_write");
667+            if (n > 0) {
668+                /* sending of data was successful */
669+                s->offset = 0;
670+                s->loc[locty].state = STATE_EXECUTION;
671+                if (s->loc[locty].inte & (INT_ENABLED | INT_DATA_AVAILABLE)) {
672+                    s->poll_attempts = 0;
673+                    tis_prep_next_interrupt(s);
674+                }
675+            }
676+        }
677+        if (val & STS_RESPONSE_RETRY) {
678+            s->offset = 0;
679+        }
680+    } else if (off == TPM_REG_DATA_FIFO) {
681+        /* data fifo */
682+        if (s->loc[locty].state == STATE_IDLE ||
683+            s->loc[locty].state == STATE_EXECUTION ||
684+            s->loc[locty].state == STATE_COMPLETION) {
685+            /* drop the byte */
686+        } else {
687+#ifdef TPM_DEBUG
688+        fprintf(logfile,"Byte to send to TPM: %02x\n", val);
689+#endif
690+            s->loc[locty].state = STATE_RECEPTION;
691+
692+            if (s->offset < sizeof(s->buffer.buf))
693+                s->buffer.buf[s->offset++] = (uint8_t)val;
694+
695+            if (s->offset > 5) {
696+                /* we have a packet length - see if we have all of it */
697+                len = tpm_get_size_from_buffer(s->buffer.buf);
698+                if (len > s->offset) {
699+                    s->loc[locty].sts = STS_EXPECT | STS_VALID;
700+                } else {
701+                    s->loc[locty].sts = STS_VALID;
702+                }
703+            }
704+        }
705+    }
706+}
707+
708+/*
709+ * Prepare the next interrupt for example after a command has
710+ * been sent out for the purpose of receiving the response.
711+ * Depending on how many interrupts (used for polling on the fd) have
712+ * already been schedule, this function determines the delta in time
713+ * to the next interrupt. This accomodates for commands that finish
714+ * quickly.
715+ */
716+static void tis_prep_next_interrupt(tpmState *s)
717+{
718+    int64_t expiration;
719+    int rate = 5; /* 5 times per second */
720+
721+    /*
722+       poll often at the beginning for quickly finished commands,
723+       then back off
724+     */
725+    if (s->poll_attempts < 5) {
726+        rate = 20;
727+    } else if (s->poll_attempts < 10) {
728+        rate = 10;
729+    }
730+
731+    expiration = qemu_get_clock(vm_clock) + (ticks_per_sec / rate);
732+    qemu_mod_timer(s->poll_timer, expiration);
733+    s->poll_attempts++;
734+}
735+
736+
737+/*
738+ * The polling routine called when the 'timer interrupt' fires.
739+ * Tries to receive a command from the vTPM.
740+ */
741+static void tis_poll_timer(void *opaque)
742+{
743+    tpmState* s=(tpmState*)opaque;
744+    uint8_t locty = s->active_loc;
745+
746+    if (!IS_VALID_LOC(locty) ||
747+        (!(s->loc[locty].inte & INT_ENABLED) &&
748+          (s->aborting_locty != NO_LOCALITY)) ||
749+        !IS_COMM_WITH_VTPM(s)) {
750+        /* no more interrupts requested, so no more polling needed */
751+        qemu_del_timer(s->poll_timer);
752+    }
753+
754+    if (!IS_COMM_WITH_VTPM(s)) {
755+        if (s->aborting_locty != NO_LOCALITY) {
756+            tis_abort(s);
757+        }
758+        return;
759+    }
760+
761+    if (s->aborting_locty != NO_LOCALITY) {
762+        int n = TPM_Receive(s, &s->buffer);
763+#ifdef DEBUG_TPM
764+        fprintf(logfile,"Receiving for abort.\n");
765+#endif
766+        if (n > 0) {
767+            close_vtpm_channel(s, FORCE_CLOSE);
768+            tis_abort(s);
769+#ifdef DEBUG_TPM
770+            fprintf(logfile,"Abort is complete.\n");
771+#endif
772+        } else {
773+            tis_prep_next_interrupt(s);
774+        }
775+    } else if (IS_VALID_LOC(locty)) {
776+        if (s->loc[locty].state == STATE_EXECUTION) {
777+           /* poll for result */
778+            int n = TPM_Receive(s, &s->buffer);
779+            if (n > 0) {
780+                s->loc[locty].sts = STS_VALID | STS_DATA_AVAILABLE;
781+                s->loc[locty].state = STATE_COMPLETION;
782+                close_vtpm_channel(s, FORCE_CLOSE);
783+                tis_raise_irq(s, locty, INT_DATA_AVAILABLE);
784+            } else {
785+                /* nothing received */
786+                tis_prep_next_interrupt(s);
787+            }
788+        }
789+    }
790+}
791+
792+
793+static CPUReadMemoryFunc *tis_readfn[3]={
794+    tis_mem_readl,
795+    tis_mem_readl,
796+    tis_mem_readl
797+};
798+
799+static CPUWriteMemoryFunc *tis_writefn[3]={
800+    tis_mem_writel,
801+    tis_mem_writel,
802+    tis_mem_writel
803+};
804+
805+/*
806+ * Save the internal state of this interface for later resumption.
807+ * Need to get any outstanding responses from the vTPM back, so
808+ * this might delay the suspend for a while.
809+ */
810+static void tpm_save(QEMUFile* f,void* opaque)
811+{
812+    tpmState* s=(tpmState*)opaque;
813+    uint8_t locty = s->active_loc;
814+    int c;
815+
816+    /* need to wait for outstanding requests to complete */
817+    if (s->loc[locty].state == STATE_EXECUTION) {
818+        int repeats = 30; /* 30 seconds; really should be infty */
819+        while (repeats > 0 &&
820+               !(s->loc[s->active_loc].sts & STS_DATA_AVAILABLE)) {
821+            int n = TPM_Receive(s, &s->buffer);
822+            if (n > 0) {
823+                if (IS_VALID_LOC(s->active_loc)) {
824+                    s->loc[s->active_loc].sts = STS_VALID | STS_DATA_AVAILABLE;
825+                    s->loc[s->active_loc].state = STATE_COMPLETION;
826+                    tis_raise_irq(s, s->active_loc, INT_DATA_AVAILABLE);
827+                }
828+                /* close the connection with the vTPM for good */
829+                close_vtpm_channel(s, 1);
830+                break;
831+            }
832+            sleep(1);
833+        }
834+    }
835+
836+    if (IS_COMM_WITH_VTPM(s)) {
837+        close_vtpm_channel(s, 1);
838+    }
839+
840+    qemu_put_be32s(f,&s->offset);
841+    qemu_put_buffer(f, s->buffer.buf, TPM_MAX_PKT);
842+    qemu_put_8s(f, &s->active_loc);
843+    qemu_put_8s(f, &s->irq_pending);
844+    for (c = 0; c < NUM_LOCALITIES; c++) {
845+        qemu_put_be32s(f, &s->loc[c].state);
846+        qemu_put_8s(f, &s->loc[c].access);
847+        qemu_put_8s(f, &s->loc[c].sts);
848+        qemu_put_be32s(f, &s->loc[c].inte);
849+        qemu_put_be32s(f, &s->loc[c].ints);
850+    }
851+}
852+
853+/*
854+ * load TIS interface state
855+ */
856+static int tpm_load(QEMUFile* f,void* opaque,int version_id)
857+{
858+    tpmState* s=(tpmState*)opaque;
859+    int c;
860+
861+    if (version_id != 1)
862+        return -EINVAL;
863+
864+    qemu_get_be32s(f,&s->offset);
865+    qemu_get_buffer(f, s->buffer.buf, TPM_MAX_PKT);
866+    qemu_get_8s(f, &s->active_loc);
867+    qemu_get_8s(f, &s->irq_pending);
868+    for (c = 0; c < NUM_LOCALITIES; c++) {
869+        qemu_get_be32s(f, &s->loc[c].state);
870+        qemu_get_8s(f, &s->loc[c].access);
871+        qemu_get_8s(f, &s->loc[c].sts);
872+        qemu_get_be32s(f, &s->loc[c].inte);
873+        qemu_get_be32s(f, &s->loc[c].ints);
874+    }
875+
876+    /* need to be able to get the instance number from the xenstore */
877+    s->vtpm_instance = vtpm_instance_from_xenstore();
878+    if (s->vtpm_instance == VTPM_BAD_INSTANCE)
879+        return -EINVAL;
880+    tpm_initialize_instance(s, s->vtpm_instance);
881+
882+    return 0;
883+}
884+
885+
886+typedef struct LPCtpmState {
887+    tpmState tpm;
888+    int mem;
889+} LPCtpmState;
890+
891+
892+/*
893+ * initialize TIS interface
894+ */
895+void tpm_tis_init(SetIRQFunc *set_irq, void *opaque, int irq)
896+{
897+    LPCtpmState *d;
898+    tpmState *s;
899+    int c = 0;
900+    uint32_t vtpm_in;
901+
902+    vtpm_in = vtpm_instance_from_xenstore();
903+    /* no valid vtpm instance -> no device */
904+    if (vtpm_in == VTPM_BAD_INSTANCE)
905+        return;
906+
907+    d = qemu_mallocz(sizeof(LPCtpmState));
908+    d->mem = cpu_register_io_memory(0, tis_readfn, tis_writefn, d);
909+
910+    if (d->mem == -1) {
911+       return;
912+    }
913+
914+    cpu_register_physical_memory(TIS_ADDR_BASE,
915+                                 0x1000 * NUM_LOCALITIES, d->mem);
916+
917+    /* initialize tpmState */
918+    s = &d->tpm;
919+
920+    s->offset = 0;
921+    s->active_loc = NO_LOCALITY;
922+
923+    while (c < NUM_LOCALITIES) {
924+        s->loc[c].access = (1 << 7);
925+        s->loc[c].sts = 0;
926+        s->loc[c].inte = (1 << 3);
927+        s->loc[c].ints = 0;
928+        s->loc[c].state = STATE_IDLE;
929+        c++;
930+    }
931+    s->poll_timer = qemu_new_timer(vm_clock, tis_poll_timer, s);
932+    s->set_irq = set_irq;
933+    s->irq_opaque = opaque;
934+    s->irq = irq;
935+    s->vtpm_instance = vtpm_in;
936+    s->Transmitlayer = -1;
937+    s->tpmTx.fd[0] = -1;
938+    s->tpmTx.fd[1] = -1;
939+    s->aborting_locty = NO_LOCALITY;
940+
941+    tpm_initialize_instance(s, s->vtpm_instance);
942+    memset(s->buffer.buf,0,sizeof(s->buffer.buf));
943+
944+    register_savevm("tpm-tis", 0, 1, tpm_save, tpm_load, s);
945+}
946+
947+/****************************************************************************/
948+/*  optional verbose logging of data to/from vtpm                           */
949+/****************************************************************************/
950+#ifdef DEBUG_TPM
951+static void showBuff(unsigned char *buff, char *string)
952+{
953+    uint32_t i, len;
954+
955+    len = tpm_get_size_from_buffer(buff);
956+    fprintf(logfile,"%s length = %d\n", string, len);
957+    for (i = 0; i < len; i++) {
958+        if (i && !(i % 16)) {
959+            fprintf(logfile,"\n");
960+        }
961+        fprintf(logfile,"%.2X ", buff[i]);
962+    }
963+    fprintf(logfile,"\n");
964+}
965+#endif
966+
967+/****************************************************************************/
968+/* Transmit request to TPM and read Response                                */
969+/****************************************************************************/
970+
971+const static unsigned char tpm_failure[] = {
972+    0x00, 0x00,
973+    0x00, 0x00, 0x00, 0x0a,
974+    0x00, 0x00, 0x00, 0x09
975+};
976+
977+
978+/*
979+ * Send a TPM request.
980+ */
981+static int TPM_Send(tpmState *s, tpmBuffer *buffer, uint8_t locty, char *msg)
982+{
983+    int len;
984+    uint32_t size = tpm_get_size_from_buffer(buffer->buf);
985+
986+    /* try to establish a connection to the vTPM */
987+    if ( !IS_COMM_WITH_VTPM(s)) {
988+        open_vtpm_channel(s);
989+    }
990+
991+    if ( !IS_COMM_WITH_VTPM(s)) {
992+        unsigned char tag = buffer->buf[1];
993+
994+        /* there's a failure response from the TPM */
995+        memcpy(buffer->buf, tpm_failure, sizeof(tpm_failure));
996+        buffer->buf[1] = tag + 3;
997+        if (IS_VALID_LOC(s->active_loc)) {
998+            s->loc[s->active_loc].sts = STS_DATA_AVAILABLE | STS_VALID;
999+        }
1000+#ifdef DEBUG_TPM
1001+        fprintf(logfile,"No TPM running!\n");
1002+#endif
1003+        /* the request went out ok. */
1004+        return sizeof(buffer->instance) + size;
1005+    }
1006+
1007+#ifdef DEBUG_TPM
1008+    showBuff(buffer->buf, "To TPM");
1009+#endif
1010+
1011+    /* transmit the locality in the highest 3 bits */
1012+    buffer->instance[0] &= 0x1f;
1013+    buffer->instance[0] |= (locty << 5);
1014+
1015+    len = vTPMTransmit[s->Transmitlayer].write(s, buffer);
1016+    if (len < 0) {
1017+        s->Transmitlayer = -1;
1018+    }
1019+    return len;
1020+}
1021+
1022+/*
1023+ * Try to receive data from the file descriptor. Since it is in
1024+ * non-blocking mode it is possible that no data are actually received -
1025+ * whatever calls this function needs to try again later.
1026+ */
1027+static int TPM_Receive(tpmState *s, tpmBuffer *buffer)
1028+{
1029+    int off;
1030+
1031+    off = vTPMTransmit[s->Transmitlayer].read(s, buffer);
1032+
1033+    if (off < 0) {
1034+        /* EAGAIN is set in errno due to non-blocking mode */
1035+        return -1;
1036+    }
1037+
1038+    if (off == 0) {
1039+#ifdef DEBUG_TPM
1040+        fprintf(logfile,"TPM GONE? errno=%d\n",errno);
1041+#endif
1042+        close_vtpm_channel(s, 1);
1043+        /* pretend that data are available */
1044+        if (IS_VALID_LOC(s->active_loc)) {
1045+            s->loc[s->active_loc].sts = STS_VALID | STS_DATA_AVAILABLE;
1046+            s->loc[s->active_loc].state = STATE_COMPLETION;
1047+            tis_raise_irq(s, s->active_loc, INT_DATA_AVAILABLE);
1048+        }
1049+        return -1;
1050+    }
1051+
1052+#ifdef DEBUG_TPM
1053+    if (off > sizeof(buffer->instance ) + 6) {
1054+        uint32_t size = tpm_get_size_from_buffer(buffer->buf);
1055+        if (size + sizeof(buffer->instance) != off) {
1056+            fprintf(logfile,"TPM: Packet size is bad! %d != %d\n",
1057+                    (int)(size + sizeof(buffer->instance)),
1058+                    off);
1059+        } else {
1060+            uint32_t ret;
1061+            showBuff(buffer->buf, "From TPM");
1062+            ret = (buffer->buf[8])*256 + buffer->buf[9];
1063+            if (ret)
1064+                fprintf(logfile,"Receive failed with error %d\n", ret);
1065+            else
1066+                fprintf(logfile,"Receive succeeded. Got response of length %d (=%d)\n",
1067+                       size, off);
1068+        }
1069+    }
1070+#endif
1071+
1072+    /* assuming reading in one chunk for now */
1073+    return off;
1074+}
1075+
1076+
1077+/****************************************************************************
1078+   Helper functions for reading data from the xenstore such as
1079+   reading virtual TPM instance information
1080+ ****************************************************************************/
1081+int has_tpm_device(void)
1082+{
1083+    int ret = 0;
1084+    struct xs_handle *handle = xs_daemon_open();
1085+    if (handle) {
1086+        ret = xenstore_domain_has_devtype(handle, "vtpm");
1087+        xs_daemon_close(handle);
1088+    }
1089+    return ret;
1090+}
1091+
1092+
1093+/*
1094+ * Wait until hotplug scripts have finished then read the vTPM instance
1095+ * number from the xenstore.
1096+ */
1097+static uint32_t vtpm_instance_from_xenstore(void)
1098+{
1099+    unsigned int num;
1100+    uint32_t number = VTPM_BAD_INSTANCE;
1101+    int end = 0;
1102+    char *token = "tok";
1103+    int subscribed = 0;
1104+    int ctr = 0;
1105+    fd_set readfds;
1106+
1107+    struct xs_handle *handle = xs_daemon_open();
1108+
1109+    FD_ZERO(&readfds);
1110+
1111+    if (handle) {
1112+        char **e = xenstore_domain_get_devices(handle, "vtpm", &num);
1113+        int fd = xs_fileno(handle);
1114+        FD_SET(fd, &readfds);
1115+        if (e) {
1116+            do {
1117+                struct timeval tv = {
1118+                    .tv_sec  = 30,
1119+                    .tv_usec = 0,
1120+                };
1121+                /* need to make sure that the hotplug scripts have finished */
1122+                char *status = xenstore_read_hotplug_status(handle,
1123+                                                            "vtpm",
1124+                                                            e[0]);
1125+                if (status) {
1126+                    if (!strcmp(status, "connected")) {
1127+                        char *inst = xenstore_backend_read_variable(handle,
1128+                                                                    "vtpm",
1129+                                                                    e[0],
1130+                                                                   "instance");
1131+                        if (1 != (sscanf(inst,"%d",&number)))
1132+                            number = VTPM_BAD_INSTANCE;
1133+                        free(inst);
1134+                    } else {
1135+                        fprintf(logfile,
1136+                                "bad status '%s' from vtpm hotplug\n",
1137+                                status);
1138+                    }
1139+                    free(status);
1140+                    end = 1;
1141+                } else {
1142+                    /* no status, yet */
1143+                    int rc;
1144+                    unsigned int nr;
1145+                    char **f;
1146+
1147+                    if (!subscribed) {
1148+                        rc = xenstore_subscribe_to_hotplug_status(handle,
1149+                                                                  "vtpm",
1150+                                                                  e[0],
1151+                                                                  token);
1152+                        if (rc != 0)
1153+                            break;
1154+                        subscribed = 1;
1155+                    }
1156+                    rc = select(fd+1, &readfds, NULL, NULL, &tv);
1157+                    /* get what's available -- drain the fd */
1158+                    f = xs_read_watch(handle, &nr);
1159+                    ctr++;
1160+                    free(f);
1161+                    if (ctr > 2)
1162+                        end = 1;
1163+                }
1164+            } while (end == 0);
1165+            free(e);
1166+        }
1167+        if (subscribed) {
1168+            /* clean up */
1169+            xenstore_unsubscribe_from_hotplug_status(handle,
1170+                                                     "vtpm",
1171+                                                     e[0],
1172+                                                     token);
1173+        }
1174+        xs_daemon_close(handle);
1175+    }
1176+    if (number == VTPM_BAD_INSTANCE)
1177+        fprintf(logfile, "no valid vtpm instance");
1178+    else
1179+        fprintf(logfile,"vtpm instance:%d\n",number);
1180+    return number;
1181+}
1182Index: ioemu/vl.h
1183===================================================================
1184--- ioemu.orig/vl.h     2007-05-03 15:20:44.000000000 +0100
1185+++ ioemu/vl.h  2007-05-03 15:20:44.000000000 +0100
1186@@ -933,6 +933,10 @@
1187 void piix4_pm_init(PCIBus *bus, int devfn);
1188 void acpi_bios_init(void);
1189 
1190+/* tpm_tis.c */
1191+int has_tpm_device(void);
1192+void tpm_tis_init(SetIRQFunc *set_irq, void *irq_opaque, int irq);
1193+
1194 /* piix4acpi.c */
1195 extern void pci_piix4_acpi_init(PCIBus *bus, int devfn);
1196 
Note: See TracBrowser for help on using the repository browser.