From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from mailman by lists.gnu.org with tmda-scanned (Exim 4.43) id 1Ipjf7-0002AF-DV for qemu-devel@nongnu.org; Wed, 07 Nov 2007 07:05:37 -0500 Received: from exim by lists.gnu.org with spam-scanned (Exim 4.43) id 1Ipjf5-00029X-OK for qemu-devel@nongnu.org; Wed, 07 Nov 2007 07:05:36 -0500 Received: from [199.232.76.173] (helo=monty-python.gnu.org) by lists.gnu.org with esmtp (Exim 4.43) id 1Ipjf5-00029U-L3 for qemu-devel@nongnu.org; Wed, 07 Nov 2007 07:05:35 -0500 Received: from mail.gmx.net ([213.165.64.20]) by monty-python.gnu.org with smtp (Exim 4.60) (envelope-from ) id 1Ipjf4-0004la-FS for qemu-devel@nongnu.org; Wed, 07 Nov 2007 07:05:35 -0500 Date: Wed, 7 Nov 2007 13:05:31 +0100 From: Thomas Bleher Subject: Re: [Qemu-devel] [PATCH] Add TPM support Message-ID: <20071107120530.GC7624@thomas> References: <20071031120636.GA7567@thomas> <20071031125438.GG7712@networkno.de> <20071031141020.GC7567@thomas> <20071031161427.GI7712@networkno.de> <20071101155556.GC15568@thomas> <20071105141528.GE8650@thomas> <472F3966.1090206@bellard.org> <20071106080734.GB7621@thomas> <4730C497.5070201@bellard.org> MIME-Version: 1.0 Content-Type: multipart/signed; micalg=pgp-sha1; protocol="application/pgp-signature"; boundary="bjuZg6miEcdLYP6q" Content-Disposition: inline In-Reply-To: <4730C497.5070201@bellard.org> Reply-To: qemu-devel@nongnu.org List-Id: qemu-devel.nongnu.org List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , To: qemu-devel@nongnu.org --bjuZg6miEcdLYP6q Content-Type: multipart/mixed; boundary="7gGkHNMELEOhSGF6" Content-Disposition: inline --7gGkHNMELEOhSGF6 Content-Type: text/plain; charset=utf-8 Content-Disposition: inline Content-Transfer-Encoding: quoted-printable * Fabrice Bellard [2007-11-06 20:47]: > Thomas Bleher wrote: > > * Fabrice Bellard [2007-11-05 16:40]: > >> Thomas Bleher wrote: > > + result =3D write(s->tpm_fd, s->send_data, s->send_data_index); > > + if (result < s->send_data_index) { > > + fprintf(stderr, "WARNING: Failed to write data to tpm!\n"); > > + return ATML_STATUS_BUSY; > > + } >=20 > You should handle EINTR and EAGAIN. >=20 > > + s->send_data_index =3D 0; > > + s->recv_data_pos =3D 0; > > + s->recv_data_length =3D 0; > > + s->data_to_send =3D 0; > > + s->data_to_recv =3D 1; > > + } > > + if (s->data_to_recv) { > > + if (poll(&(s->tpm_poll), 1, 0) > 0) { >=20 > poll is not needed as read will block. >=20 > > + result =3D read(s->tpm_fd, s->recv_data, 2048); > > + if (result < 6) { // a minimal packet is 6 bytes long > > + fprintf(stderr, "WARNING: Not enough data from tpm!\n"= ); > > + return ATML_STATUS_BUSY; > > + } >=20 > same comment as write(). >=20 > > + res =3D connect(sock, (struct sockaddr*)&addr, sizeof(struct socka= ddr_un)); > > + if (res < 0) > > + return -1; >=20 > same comment for EINTR. >=20 > +/* should be called first, initializes all structures and connects to > the external emulator */ > +void tpm_configure(const char* tpm_socket) > +{ >=20 > Suppress this function (cf tpm_register). >=20 > > +/* split of from tpm_configure() so the configuration can be called ea= rlier */ > > +void tpm_register() >=20 > Ansi C requires void in parameters. Allocate the state and return it. > Suppress the global variable tpm_state. Pass the path to this function > and suppress the configure function. Add a global variable for the path > and register the device if it is not NULL. Thank you for the review! An updated patch is appended. The patch has been tested using a Linux client with IMA added. Thomas --7gGkHNMELEOhSGF6 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="tpm-support.patch" Content-Transfer-Encoding: quoted-printable Author: Thomas Bleher Date: Wed Nov 7 12:27:14 2007 +0100 This patch adds support for an Atmel TPM chip. =20 Background: TPMs are rather complex chips, supporting many commands and implementing complex crypto protocols like Direct Anonymous Attestation (DAA). Therefore, this patch does not directly implement a TPM chip, but instead utilizes the TPM emulator project (http://tpm-emulator.berlios.= de/). The TPM emulator can be run as a daemon, communicating through a unix d= omain socket. =20 This patch adds a "-tpm path" parameter to qemu, where "path" is the un= ix domain socket of the TPM emulator. If the parameter is given, the chip = is registered in the emulated system. Otherwise, behaviour is unchanged. =20 The interface presented inside qemu is that of an Atmel TPM chip, simply because there is a Linux driver for this chip and the interface is very simple. I do not own any TPM chip, therefore the interface was written purely by looking at the Linux driver. =20 Use case: This patch makes it possible to experiment with software like= IBMs Integrity Measurement Architecture (IMA), without having an actual TPM = (this patch was developed for a demonstration involving IMA, among other thin= gs). It should also be possible to use Microsofts BitLocker technology, alth= ough this hasn't been tested yet. --- a/Makefile.target +++ b/Makefile.target @@ -460,6 +460,9 @@ VL_OBJS +=3D ne2000.o VL_OBJS +=3D pcnet.o VL_OBJS +=3D rtl8139.o =20 +# TPM device +VL_OBJS +=3D tpm.o + ifeq ($(TARGET_BASE_ARCH), i386) # Hardware support VL_OBJS+=3D ide.o pckbd.o ps2.o vga.o $(SOUND_HW) dma.o $(AUDIODRV) --- /dev/null +++ b/hw/tpm.c @@ -0,0 +1,228 @@ +/* + * TPM emulation + * Copyright (c) 2007 Thomas Bleher + * + * Permission is hereby granted, free of charge, to any person obtaining a= copy + * of this software and associated documentation files (the "Software"), t= o deal + * in the Software without restriction, including without limitation the r= ights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or se= ll + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included= in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS= OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OT= HER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING= FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS = IN + * THE SOFTWARE. + */ + +/* + * This driver emulates a TPM chip. TPM chips are quite complex, and a TPM + * emulator already exists, therefore this driver just connects to this + * emulator and forwards all the data. For the TPM emulator project, see + * http://tpm-emulator.berlios.de/ + * + * The author does not own any TPM chip himself, so the Linux Kernel drive= r for + * Atmel TPM chips was taken as a reference. The code works fine with the = Linux + * driver, but no tests have been done on other operating systems. + * + * The two enums below were copied from the Linux Kernel source code. Othe= r than + * that, no code was re-used. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "vl.h" + +#define TPM_ADDR 0x4E +/* just choose a free port */ +#define TPM_PORT_LO 0x00 +#define TPM_PORT_HI 0x67 +#define TPM_PORT ((TPM_PORT_HI << 8) | TPM_PORT_LO) + +/* write status bits */ +enum tpm_atmel_write_status { + ATML_STATUS_ABORT =3D 0x01, + ATML_STATUS_LASTBYTE =3D 0x04 +}; +/* read status bits */ +enum tpm_atmel_read_status { + ATML_STATUS_BUSY =3D 0x01, + ATML_STATUS_DATA_AVAIL =3D 0x02, + ATML_STATUS_REWRITE =3D 0x04, + ATML_STATUS_READY =3D 0x08 +}; + +struct TPMState { + unsigned char last_init_input, last_status_input; + unsigned char send_data[2048], recv_data[2048]; + unsigned int send_data_index, recv_data_pos, recv_data_length; + int tpm_fd; + int data_to_send, data_to_recv; /* boolean flags */ +}; + +static uint32_t tpm_ioport_read_data(void *opaque, uint32_t addr) +{ + TPMState *s =3D opaque; + if (s->recv_data_pos >=3D s->recv_data_length) { + fprintf(stderr, "WARNING: Trying to read more data than is there!\= n"); + return 0; + } + return s->recv_data[s->recv_data_pos++]; +} + +static void tpm_ioport_write_data(void *opaque, uint32_t addr, uint32_t va= l) +{ + TPMState *s =3D opaque; + if (s->data_to_recv) { + fprintf(stderr, "WARNING: tpm received data when not supposed to! = Discarding\n"); + return; + } + s->send_data[s->send_data_index++] =3D (char) (val & 0xFF); + s->data_to_send =3D 1; +} + +static uint32_t tpm_ioport_read_status(void *opaque, uint32_t addr) +{ + TPMState *s =3D opaque; + unsigned char status; + int ret; + + status =3D 0; +/* This is a bit unclean: On the first status request we trigger sending + * the data to the tpm. + */ + if (s->data_to_send) { + do + ret =3D write(s->tpm_fd, s->send_data, s->send_data_index); + while (ret < 0 && (errno =3D=3D EINTR || errno =3D=3D EAGAIN)); + if (ret < s->send_data_index) { + fprintf(stderr, "WARNING: Failed to write data to tpm!\n"); + return ATML_STATUS_BUSY; + } + s->send_data_index =3D 0; + s->recv_data_pos =3D 0; + s->recv_data_length =3D 0; + s->data_to_send =3D 0; + s->data_to_recv =3D 1; + } + if (s->data_to_recv) { + do + ret =3D read(s->tpm_fd, s->recv_data, 2048); + while (ret < 0 && (errno =3D=3D EINTR || errno =3D=3D EAGAIN)); + if (ret < 6) { // a minimal packet is 6 bytes long + fprintf(stderr, "WARNING: Not enough data from tpm!\n"); + return ATML_STATUS_BUSY; + } + s->recv_data_length =3D ret; + s->recv_data_pos =3D 0; + s->data_to_recv =3D 0; + } + + if (s->recv_data_length > s->recv_data_pos) + status |=3D ATML_STATUS_DATA_AVAIL; + if (s->data_to_recv) + status |=3D ATML_STATUS_BUSY; + if (!status) + status =3D ATML_STATUS_READY; + + return status; +} + +static void tpm_ioport_write_status(void *opaque, uint32_t addr, uint32_t = val) +{ + TPMState *s =3D opaque; + s->last_status_input =3D (char) (val & 0xFF); + // TODO: If we receive a Cancel-message we should act on it. +} + +static uint32_t tpm_ioport_read_init(void *opaque, uint32_t addr) +{ + TPMState *s =3D opaque; + + switch (s->last_init_input) { + /* see atmel_verify_tpm11() in the linux kernel sources */ + case 0: + case 1: + return 1; + case 4: + return 'A'; + case 5: + return 'T'; + case 6: + return 'M'; + case 7: + return 'L'; + case 8: + return TPM_PORT_LO; + case 9: + return TPM_PORT_HI; + } + + return 0; +} + +static void tpm_ioport_write_init(void *opaque, uint32_t addr, uint32_t va= l) +{ + TPMState *s =3D opaque; + s->last_init_input =3D (char) (val & 0xFF); +} + +static int tpm_open_socket(const char* tpm_path) +{ + int sock, ret, len; + struct sockaddr_un addr; + + sock =3D socket(PF_UNIX, SOCK_STREAM, 0); + if (sock < 0) + return -1; + addr.sun_family =3D AF_UNIX; + len =3D strlen(tpm_path) + 1; // include terminating \0 + if (len > sizeof(addr.sun_path)) { + errno =3D ENAMETOOLONG; + return -1; + } + strcpy(addr.sun_path, tpm_path); + do + ret =3D connect(sock, (struct sockaddr*)&addr, sizeof(struct socka= ddr_un)); + while (ret < 0 && errno =3D=3D EINTR); + if (ret < 0) + return -1; + return sock; +} + +TPMState* tpm_register(const char* tpm_path) +{ + TPMState *s; + s =3D qemu_mallocz(sizeof(TPMState)); + if (s =3D=3D NULL) + return NULL; + + s->tpm_fd =3D tpm_open_socket(tpm_path); + if (s->tpm_fd =3D=3D -1) + return NULL; + + /* these are only for initialization */ + register_ioport_write(TPM_ADDR, 1, 1, tpm_ioport_write_init, s); + register_ioport_read (TPM_ADDR+1, 1, 1, tpm_ioport_read_init, s); + + /* real TPM interface */ + register_ioport_write(TPM_PORT, 1, 1, tpm_ioport_write_data, s); + register_ioport_read (TPM_PORT, 1, 1, tpm_ioport_read_data, s); + register_ioport_write(TPM_PORT+1, 1, 1, tpm_ioport_write_status, s); + register_ioport_read (TPM_PORT+1, 1, 1, tpm_ioport_read_status, s); + + return s; +} + --- a/vl.c +++ b/vl.c @@ -223,6 +223,7 @@ int alt_grab =3D 0; unsigned int nb_prom_envs =3D 0; const char *prom_envs[MAX_PROM_ENVS]; #endif +const char *tpm_path; =20 #define TFR(expr) do { if ((expr) !=3D -1) break; } while (errno =3D=3D EI= NTR) =20 @@ -7112,6 +7113,8 @@ static void help(int exitcode) #endif "-clock force the use of the given methods for timer a= larm.\n" " To see what timers are available use -clock he= lp\n" + "-tpm path enable TPM support inside qemu and connect to = a tpm\n" + " emulator on the given path\n" "\n" "During emulation, the following keys are useful:\n" "ctrl-alt-f toggle full screen\n" @@ -7212,6 +7215,7 @@ enum { QEMU_OPTION_prom_env, QEMU_OPTION_old_param, QEMU_OPTION_clock, + QEMU_OPTION_tpm, }; =20 typedef struct QEMUOption { @@ -7318,6 +7322,7 @@ const QEMUOption qemu_options[] =3D { { "old-param", 0, QEMU_OPTION_old_param }, #endif { "clock", HAS_ARG, QEMU_OPTION_clock }, + { "tpm", HAS_ARG, QEMU_OPTION_tpm }, { NULL }, }; =20 @@ -8107,6 +8112,9 @@ int main(int argc, char **argv) case QEMU_OPTION_clock: configure_alarms(optarg); break; + case QEMU_OPTION_tpm: + tpm_path =3D optarg; + break; } } } @@ -8439,6 +8447,13 @@ int main(int argc, char **argv) } } =20 + if (tpm_path) { + if (!tpm_register(tpm_path)) { + perror("Failed to create connection to the TPM emulator"); + exit(1); + } + } + if (display_state.dpy_refresh) { display_state.gui_timer =3D qemu_new_timer(rt_clock, gui_update, &= display_state); qemu_mod_timer(display_state.gui_timer, qemu_get_clock(rt_clock)); --- a/vl.h +++ b/vl.h @@ -1720,4 +1720,8 @@ void readline_start(const char *prompt, int is_passwo= rd, =20 void kqemu_record_dump(void); =20 +/* tpm.c */ +typedef struct TPMState TPMState; +TPMState* tpm_register(const char* tpm_socket); + #endif /* VL_H */ --7gGkHNMELEOhSGF6-- --bjuZg6miEcdLYP6q Content-Type: application/pgp-signature; name="signature.asc" Content-Description: Digital signature Content-Disposition: inline -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.6 (GNU/Linux) iD8DBQFHMaoKD9qpeVMU938RAoDmAJsHk7jVV6TzBbbxbo0a32prcwvfBACgjaXN GFpjf9SYZoc7dkoX2dkSWq0= =sQuW -----END PGP SIGNATURE----- --bjuZg6miEcdLYP6q--