📄 tpm_tis.c
字号:
/* * tpm_tis.c - QEMU emulator for a 1.2 TPM with TIS interface * * Copyright (C) 2006 IBM Corporation * * Author: Stefan Berger <stefanb@us.ibm.com> * David Safford <safford@us.ibm.com> * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation, version 2 of the * License. * * * Implementation of the TIS interface according to specs at * https://www.trustedcomputinggroup.org/groups/pc_client/TCG_PCClientTPMSpecification_1-20_1-00_FINAL.pdf * */#include <sys/types.h>#include <sys/stat.h>#include <sys/socket.h>#include <sys/un.h>#include <fcntl.h>#include <errno.h>#include "vl.h"//#define DEBUG_TPM#define TPM_MAX_PKT 4096#define VTPM_BAD_INSTANCE (uint32_t)0xffffffff#define TIS_ADDR_BASE 0xFED40000/* tis registers */#define TPM_REG_ACCESS 0x00#define TPM_REG_INT_ENABLE 0x08#define TPM_REG_INT_VECTOR 0x0c#define TPM_REG_INT_STATUS 0x10#define TPM_REG_INTF_CAPABILITY 0x14#define TPM_REG_STS 0x18#define TPM_REG_DATA_FIFO 0x24#define TPM_REG_DID_VID 0xf00#define TPM_REG_RID 0xf04#define STS_VALID (1 << 7)#define STS_COMMAND_READY (1 << 6)#define STS_TPM_GO (1 << 5)#define STS_DATA_AVAILABLE (1 << 4)#define STS_EXPECT (1 << 3)#define STS_RESPONSE_RETRY (1 << 1)#define ACCESS_TPM_REG_VALID_STS (1 << 7)#define ACCESS_ACTIVE_LOCALITY (1 << 5)#define ACCESS_BEEN_SEIZED (1 << 4)#define ACCESS_SEIZE (1 << 3)#define ACCESS_PENDING_REQUEST (1 << 2)#define ACCESS_REQUEST_USE (1 << 1)#define ACCESS_TPM_ESTABLISHMENT (1 << 0)#define INT_ENABLED (1 << 31)#define INT_DATA_AVAILABLE (1 << 0)#define INT_LOCALITY_CHANGED (1 << 2)#define INT_COMMAND_READY (1 << 7)#define INTERRUPTS_SUPPORTED (INT_LOCALITY_CHANGED | \ INT_DATA_AVAILABLE | \ INT_COMMAND_READY)#define CAPABILITIES_SUPPORTED ((1 << 4) | \ INTERRUPTS_SUPPORTED)enum { STATE_IDLE = 0, STATE_READY, STATE_COMPLETION, STATE_EXECUTION, STATE_RECEPTION};#define NUM_LOCALITIES 5#define NO_LOCALITY 0xff#define IS_VALID_LOC(x) ((x) < NUM_LOCALITIES)#define TPM_DID 0x0001#define TPM_VID 0x0001#define TPM_RID 0x0001/* if the connection to the vTPM should be closed after a successfully received response; set to '0' to allow keeping the connection */#define FORCE_CLOSE 0/* local data structures */typedef struct TPMTx { int fd[2];} tpmTx;typedef struct TPMBuffer { uint8_t instance[4]; /* instance number in network byte order */ uint8_t buf[TPM_MAX_PKT];} __attribute__((packed)) tpmBuffer;/* locality data */typedef struct TPMLocal { uint32_t state; uint8_t access; uint8_t sts; uint32_t inte; uint32_t ints;} tpmLoc;/* overall state of the TPM interface; 's' marks as save upon suspension */typedef struct TPMState { uint32_t offset; /* s */ tpmBuffer buffer; /* s */ uint8_t active_loc; /* s */ uint8_t aborting_locty; uint8_t next_locty; uint8_t irq_pending; /* s */ tpmLoc loc[NUM_LOCALITIES]; /* s */ QEMUTimer *poll_timer; SetIRQFunc *set_irq; void *irq_opaque; int irq; int poll_attempts; uint32_t vtpm_instance; /* vtpm inst. number; determined from xenstore*/ int Transmitlayer; tpmTx tpmTx;} tpmState;/* local prototypes */static int TPM_Send(tpmState *s, tpmBuffer *buffer, uint8_t locty, char *msg);static int TPM_Receive(tpmState *s, tpmBuffer *buffer);static uint32_t vtpm_instance_from_xenstore(void);static void tis_poll_timer(void *opaque);static void tis_prep_next_interrupt(tpmState *s);static void tis_raise_irq(tpmState *s, uint8_t locty, uint32_t irqmask);static void close_vtpm_channel(tpmState *s, int force);static void open_vtpm_channel(tpmState *s);static void tis_attempt_receive(tpmState *s, uint8_t locty);/* transport layer functions: local sockets */static int create_local_socket(tpmState *s, uint32_t vtpm_instance);static int write_local_socket(tpmState *s, const tpmBuffer *);static int read_local_socket(tpmState *s, tpmBuffer *);static int close_local_socket(tpmState *s, int force);static int has_channel_local_socket(tpmState *s);#define LOCAL_SOCKET_PATH "/var/vtpm/socks/%d.socket" #define NUM_TRANSPORTS 1struct vTPM_transmit { int (*open_fn) (tpmState *s, uint32_t vtpm_instance); int (*write_fn) (tpmState *s, const tpmBuffer *); int (*read_fn) (tpmState *s, tpmBuffer *); int (*close_fn) (tpmState *s, int); int (*has_channel) (tpmState *s);} vTPMTransmit[NUM_TRANSPORTS] = { { .open_fn = create_local_socket, .write_fn = write_local_socket, .read_fn = read_local_socket, .close_fn = close_local_socket, .has_channel = has_channel_local_socket, }};#define IS_COMM_WITH_VTPM(s) \ ((s)->Transmitlayer >= 0 && \ vTPMTransmit[(s)->Transmitlayer].has_channel(s))/********************************************************************** helper functions *********************************************************************/static inline uint32_t tpm_get_size_from_buffer(const uint8_t *buffer){ uint32_t len = (buffer[4] << 8) + buffer[5]; return len;}static inline void tpm_initialize_instance(tpmState *s, uint32_t instance){ s->buffer.instance[0] = (instance >> 24) & 0xff; s->buffer.instance[1] = (instance >> 16) & 0xff; s->buffer.instance[2] = (instance >> 8) & 0xff; s->buffer.instance[3] = (instance >> 0) & 0xff;}/* * open communication channel with a vTPM */static void open_vtpm_channel(tpmState *s){ int idx; /* search a usable transmit layer */ for (idx = 0; idx < NUM_TRANSPORTS; idx++) { if (1 == vTPMTransmit[idx].open_fn(s, s->vtpm_instance)) { /* found one */ s->Transmitlayer = idx; break; } }}/* * close the communication channel with the vTPM */static inline void close_vtpm_channel(tpmState *s, int force){ if (1 == vTPMTransmit[s->Transmitlayer].close_fn(s, force)) { s->Transmitlayer = -1; }}static inline uint8_t locality_from_addr(target_phys_addr_t addr){ return (uint8_t)((addr >> 12) & 0x7);}/********************************************************************** low-level transmission layer methods *********************************************************************//* * the 'open' method that creates the filedescriptor for communicating * only one is needed for reading and writing */static int create_local_socket(tpmState *s, uint32_t vtpm_instance){ int success = 1; if (s->tpmTx.fd[0] < 0) { s->tpmTx.fd[0] = socket(PF_LOCAL, SOCK_STREAM, 0);#ifdef DEBUG_TPM fprintf(logfile," SOCKET FD %d errno %d \n", s->tpmTx.fd[0], errno );#endif if (has_channel_local_socket(s)) { int ret; struct sockaddr_un addr; memset(&addr, 0x0, sizeof(addr)); addr.sun_family = AF_LOCAL; snprintf(addr.sun_path, sizeof(addr.sun_path)-1, LOCAL_SOCKET_PATH, (uint32_t) vtpm_instance);#ifdef DEBUG_TPM fprintf(logfile," SOCKET NAME %s \n", addr.sun_path );#endif if ((ret = connect(s->tpmTx.fd[0], (struct sockaddr *)&addr, sizeof(addr))) != 0) { close_local_socket(s, 1);#ifdef DEBUG_TPM fprintf(logfile," RET %d errno %d\n", ret, errno );#endif success = 0; } else { /* put filedescriptor in non-blocking mode for polling */#ifdef DEBUG_TPM fprintf(logfile," put filedescriptor in non-blocking mode " "for polling \n");#endif int flags = fcntl(s->tpmTx.fd[0], F_GETFL); fcntl(s->tpmTx.fd[0], F_SETFL, flags | O_NONBLOCK); }#ifdef DEBUG_TPM if (success) fprintf(logfile," Successfully connected using local socket" "%s.\n",addr.sun_path); else fprintf(logfile," Could not connect to local socket " "%s.\n",addr.sun_path);#endif } else { success = 0; } } return success;}/* * the 'write' method for sending requests to the vTPM * four bytes with the vTPM instance number are prepended to each request * the locality in which the command was sent is transmitted in the * highest 3 bits */static int write_local_socket(tpmState *s, const tpmBuffer *buffer){ uint32_t size = tpm_get_size_from_buffer(buffer->buf); int len; len = write(s->tpmTx.fd[0], buffer->instance, sizeof(buffer->instance) + size); if (len == sizeof(buffer->instance) + size) { return len; } else { return -1; }}/* * the 'read' method for receiving of responses from the TPM * this function expects that four bytes with the instance number * are received from the vTPM */static int read_local_socket(tpmState *s, tpmBuffer *buffer){ int off;#ifdef DEBUG_TPM fprintf(logfile, "Reading from fd %d\n", s->tpmTx.fd[0]);#endif off = read(s->tpmTx.fd[0], buffer->instance, sizeof(buffer->instance)+TPM_MAX_PKT);#ifdef DEBUG_TPM fprintf(logfile, "Read %d bytes\n", off);#endif return off;}/* * the 'close' method * shut down communication with the vTPM * 'force' = 1 indicates that the socket *must* be closed * 'force' = 0 indicates that a connection may be maintained */static int close_local_socket(tpmState *s, int force){ if (force) { close(s->tpmTx.fd[0]);#ifdef DEBUG_TPM fprintf(logfile,"Closed connection with fd %d\n",s->tpmTx.fd[0]);#endif s->tpmTx.fd[0] = -1; return 1; /* socket was closed */ }#ifdef DEBUG_TPM fprintf(logfile,"Keeping connection with fd %d\n",s->tpmTx.fd[0]);#endif return 0;}/* * the 'has_channel' method that checks whether there's a communication * channel with the vTPM */static int has_channel_local_socket(tpmState *s){ return (s->tpmTx.fd[0] > 0);}/**********************************************************************//* * read a byte of response data */static uint32_t tpm_data_read(tpmState *s, uint8_t locty){ uint32_t ret, len; /* try to receive data, if none are there it is ok */ tis_attempt_receive(s, locty); if (s->loc[locty].state != STATE_COMPLETION) { return 0xff; } len = tpm_get_size_from_buffer(s->buffer.buf); ret = s->buffer.buf[s->offset++]; if (s->offset >= len) { s->loc[locty].sts = STS_VALID ; s->offset = 0; }#ifdef DEBUG_TPM fprintf(logfile,"tpm_data_read byte x%02x [%d]\n",ret,s->offset-1);#endif return ret;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -