source: trunk/packages/xen-3.1/xen-3.1/linux-2.6-xen-sparse/drivers/char/tpm/tpm_xen.c @ 34

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

Add xen and xen-common

File size: 16.4 KB
Line 
1/*
2 * Copyright (c) 2005, IBM Corporation
3 *
4 * Author: Stefan Berger, stefanb@us.ibm.com
5 * Grant table support: Mahadevan Gomathisankaran
6 *
7 * This code has been derived from drivers/xen/netfront/netfront.c
8 *
9 * Copyright (c) 2002-2004, K A Fraser
10 *
11 * This program is free software; you can redistribute it and/or
12 * modify it under the terms of the GNU General Public License version 2
13 * as published by the Free Software Foundation; or, when distributed
14 * separately from the Linux kernel or incorporated into other
15 * software packages, subject to the following license:
16 *
17 * Permission is hereby granted, free of charge, to any person obtaining a copy
18 * of this source file (the "Software"), to deal in the Software without
19 * restriction, including without limitation the rights to use, copy, modify,
20 * merge, publish, distribute, sublicense, and/or sell copies of the Software,
21 * and to permit persons to whom the Software is furnished to do so, subject to
22 * the following conditions:
23 *
24 * The above copyright notice and this permission notice shall be included in
25 * all copies or substantial portions of the Software.
26 *
27 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
28 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
29 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
30 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
31 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
32 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
33 * IN THE SOFTWARE.
34 */
35
36#include <linux/errno.h>
37#include <linux/err.h>
38#include <linux/interrupt.h>
39#include <linux/mutex.h>
40#include <asm/uaccess.h>
41#include <xen/evtchn.h>
42#include <xen/interface/grant_table.h>
43#include <xen/interface/io/tpmif.h>
44#include <xen/gnttab.h>
45#include <xen/xenbus.h>
46#include "tpm.h"
47#include "tpm_vtpm.h"
48
49#undef DEBUG
50
51/* local structures */
52struct tpm_private {
53        struct tpm_chip *chip;
54
55        tpmif_tx_interface_t *tx;
56        atomic_t refcnt;
57        unsigned int irq;
58        u8 is_connected;
59        u8 is_suspended;
60
61        spinlock_t tx_lock;
62
63        struct tx_buffer *tx_buffers[TPMIF_TX_RING_SIZE];
64
65        atomic_t tx_busy;
66        void *tx_remember;
67
68        domid_t backend_id;
69        wait_queue_head_t wait_q;
70
71        struct xenbus_device *dev;
72        int ring_ref;
73};
74
75struct tx_buffer {
76        unsigned int size;      // available space in data
77        unsigned int len;       // used space in data
78        unsigned char *data;    // pointer to a page
79};
80
81
82/* locally visible variables */
83static grant_ref_t gref_head;
84static struct tpm_private *my_priv;
85
86/* local function prototypes */
87static irqreturn_t tpmif_int(int irq,
88                             void *tpm_priv,
89                             struct pt_regs *ptregs);
90static void tpmif_rx_action(unsigned long unused);
91static int tpmif_connect(struct xenbus_device *dev,
92                         struct tpm_private *tp,
93                         domid_t domid);
94static DECLARE_TASKLET(tpmif_rx_tasklet, tpmif_rx_action, 0);
95static int tpmif_allocate_tx_buffers(struct tpm_private *tp);
96static void tpmif_free_tx_buffers(struct tpm_private *tp);
97static void tpmif_set_connected_state(struct tpm_private *tp,
98                                      u8 newstate);
99static int tpm_xmit(struct tpm_private *tp,
100                    const u8 * buf, size_t count, int userbuffer,
101                    void *remember);
102static void destroy_tpmring(struct tpm_private *tp);
103void __exit tpmif_exit(void);
104
105#define DPRINTK(fmt, args...) \
106    pr_debug("xen_tpm_fr (%s:%d) " fmt, __FUNCTION__, __LINE__, ##args)
107#define IPRINTK(fmt, args...) \
108    printk(KERN_INFO "xen_tpm_fr: " fmt, ##args)
109#define WPRINTK(fmt, args...) \
110    printk(KERN_WARNING "xen_tpm_fr: " fmt, ##args)
111
112#define GRANT_INVALID_REF       0
113
114
115static inline int
116tx_buffer_copy(struct tx_buffer *txb, const u8 *src, int len,
117               int isuserbuffer)
118{
119        int copied = len;
120
121        if (len > txb->size)
122                copied = txb->size;
123        if (isuserbuffer) {
124                if (copy_from_user(txb->data, src, copied))
125                        return -EFAULT;
126        } else {
127                memcpy(txb->data, src, copied);
128        }
129        txb->len = len;
130        return copied;
131}
132
133static inline struct tx_buffer *tx_buffer_alloc(void)
134{
135        struct tx_buffer *txb;
136
137        txb = kzalloc(sizeof(struct tx_buffer), GFP_KERNEL);
138        if (!txb)
139                return NULL;
140
141        txb->len = 0;
142        txb->size = PAGE_SIZE;
143        txb->data = (unsigned char *)__get_free_page(GFP_KERNEL);
144        if (txb->data == NULL) {
145                kfree(txb);
146                txb = NULL;
147        }
148
149        return txb;
150}
151
152
153static inline void tx_buffer_free(struct tx_buffer *txb)
154{
155        if (txb) {
156                free_page((long)txb->data);
157                kfree(txb);
158        }
159}
160
161/**************************************************************
162 Utility function for the tpm_private structure
163**************************************************************/
164static void tpm_private_init(struct tpm_private *tp)
165{
166        spin_lock_init(&tp->tx_lock);
167        init_waitqueue_head(&tp->wait_q);
168        atomic_set(&tp->refcnt, 1);
169}
170
171static void tpm_private_put(void)
172{
173        if (!atomic_dec_and_test(&my_priv->refcnt))
174                return;
175
176        tpmif_free_tx_buffers(my_priv);
177        kfree(my_priv);
178        my_priv = NULL;
179}
180
181static struct tpm_private *tpm_private_get(void)
182{
183        int err;
184
185        if (my_priv) {
186                atomic_inc(&my_priv->refcnt);
187                return my_priv;
188        }
189
190        my_priv = kzalloc(sizeof(struct tpm_private), GFP_KERNEL);
191        if (!my_priv)
192                return NULL;
193
194        tpm_private_init(my_priv);
195        err = tpmif_allocate_tx_buffers(my_priv);
196        if (err < 0)
197                tpm_private_put();
198
199        return my_priv;
200}
201
202/**************************************************************
203
204 The interface to let the tpm plugin register its callback
205 function and send data to another partition using this module
206
207**************************************************************/
208
209static DEFINE_MUTEX(suspend_lock);
210/*
211 * Send data via this module by calling this function
212 */
213int vtpm_vd_send(struct tpm_private *tp,
214                 const u8 * buf, size_t count, void *ptr)
215{
216        int sent;
217
218        mutex_lock(&suspend_lock);
219        sent = tpm_xmit(tp, buf, count, 0, ptr);
220        mutex_unlock(&suspend_lock);
221
222        return sent;
223}
224
225/**************************************************************
226 XENBUS support code
227**************************************************************/
228
229static int setup_tpmring(struct xenbus_device *dev,
230                         struct tpm_private *tp)
231{
232        tpmif_tx_interface_t *sring;
233        int err;
234
235        tp->ring_ref = GRANT_INVALID_REF;
236
237        sring = (void *)__get_free_page(GFP_KERNEL);
238        if (!sring) {
239                xenbus_dev_fatal(dev, -ENOMEM, "allocating shared ring");
240                return -ENOMEM;
241        }
242        tp->tx = sring;
243
244        err = xenbus_grant_ring(dev, virt_to_mfn(tp->tx));
245        if (err < 0) {
246                free_page((unsigned long)sring);
247                tp->tx = NULL;
248                xenbus_dev_fatal(dev, err, "allocating grant reference");
249                goto fail;
250        }
251        tp->ring_ref = err;
252
253        err = tpmif_connect(dev, tp, dev->otherend_id);
254        if (err)
255                goto fail;
256
257        return 0;
258fail:
259        destroy_tpmring(tp);
260        return err;
261}
262
263
264static void destroy_tpmring(struct tpm_private *tp)
265{
266        tpmif_set_connected_state(tp, 0);
267
268        if (tp->ring_ref != GRANT_INVALID_REF) {
269                gnttab_end_foreign_access(tp->ring_ref, 0,
270                                          (unsigned long)tp->tx);
271                tp->ring_ref = GRANT_INVALID_REF;
272                tp->tx = NULL;
273        }
274
275        if (tp->irq)
276                unbind_from_irqhandler(tp->irq, tp);
277
278        tp->irq = 0;
279}
280
281
282static int talk_to_backend(struct xenbus_device *dev,
283                           struct tpm_private *tp)
284{
285        const char *message = NULL;
286        int err;
287        struct xenbus_transaction xbt;
288
289        err = setup_tpmring(dev, tp);
290        if (err) {
291                xenbus_dev_fatal(dev, err, "setting up ring");
292                goto out;
293        }
294
295again:
296        err = xenbus_transaction_start(&xbt);
297        if (err) {
298                xenbus_dev_fatal(dev, err, "starting transaction");
299                goto destroy_tpmring;
300        }
301
302        err = xenbus_printf(xbt, dev->nodename,
303                            "ring-ref","%u", tp->ring_ref);
304        if (err) {
305                message = "writing ring-ref";
306                goto abort_transaction;
307        }
308
309        err = xenbus_printf(xbt, dev->nodename, "event-channel", "%u",
310                            irq_to_evtchn_port(tp->irq));
311        if (err) {
312                message = "writing event-channel";
313                goto abort_transaction;
314        }
315
316        err = xenbus_transaction_end(xbt, 0);
317        if (err == -EAGAIN)
318                goto again;
319        if (err) {
320                xenbus_dev_fatal(dev, err, "completing transaction");
321                goto destroy_tpmring;
322        }
323
324        xenbus_switch_state(dev, XenbusStateConnected);
325
326        return 0;
327
328abort_transaction:
329        xenbus_transaction_end(xbt, 1);
330        if (message)
331                xenbus_dev_error(dev, err, "%s", message);
332destroy_tpmring:
333        destroy_tpmring(tp);
334out:
335        return err;
336}
337
338/**
339 * Callback received when the backend's state changes.
340 */
341static void backend_changed(struct xenbus_device *dev,
342                            enum xenbus_state backend_state)
343{
344        struct tpm_private *tp = tpm_private_from_dev(&dev->dev);
345        DPRINTK("\n");
346
347        switch (backend_state) {
348        case XenbusStateInitialising:
349        case XenbusStateInitWait:
350        case XenbusStateInitialised:
351        case XenbusStateUnknown:
352                break;
353
354        case XenbusStateConnected:
355                tpmif_set_connected_state(tp, 1);
356                break;
357
358        case XenbusStateClosing:
359                tpmif_set_connected_state(tp, 0);
360                xenbus_frontend_closed(dev);
361                break;
362
363        case XenbusStateClosed:
364                tpmif_set_connected_state(tp, 0);
365                if (tp->is_suspended == 0)
366                        device_unregister(&dev->dev);
367                xenbus_frontend_closed(dev);
368                break;
369        }
370}
371
372static int tpmfront_probe(struct xenbus_device *dev,
373                          const struct xenbus_device_id *id)
374{
375        int err;
376        int handle;
377        struct tpm_private *tp = tpm_private_get();
378
379        if (!tp)
380                return -ENOMEM;
381
382        tp->chip = init_vtpm(&dev->dev, tp);
383        if (IS_ERR(tp->chip))
384                return PTR_ERR(tp->chip);
385
386        err = xenbus_scanf(XBT_NIL, dev->nodename,
387                           "handle", "%i", &handle);
388        if (XENBUS_EXIST_ERR(err))
389                return err;
390
391        if (err < 0) {
392                xenbus_dev_fatal(dev,err,"reading virtual-device");
393                return err;
394        }
395
396        tp->dev = dev;
397
398        err = talk_to_backend(dev, tp);
399        if (err) {
400                tpm_private_put();
401                return err;
402        }
403
404        return 0;
405}
406
407
408static int tpmfront_remove(struct xenbus_device *dev)
409{
410        struct tpm_private *tp = tpm_private_from_dev(&dev->dev);
411        destroy_tpmring(tp);
412        cleanup_vtpm(&dev->dev);
413        return 0;
414}
415
416static int tpmfront_suspend(struct xenbus_device *dev)
417{
418        struct tpm_private *tp = tpm_private_from_dev(&dev->dev);
419        u32 ctr;
420
421        /* Take the lock, preventing any application from sending. */
422        mutex_lock(&suspend_lock);
423        tp->is_suspended = 1;
424
425        for (ctr = 0; atomic_read(&tp->tx_busy); ctr++) {
426                if ((ctr % 10) == 0)
427                        printk("TPM-FE [INFO]: Waiting for outstanding "
428                               "request.\n");
429                /* Wait for a request to be responded to. */
430                interruptible_sleep_on_timeout(&tp->wait_q, 100);
431        }
432
433        return 0;
434}
435
436static int tpmfront_suspend_finish(struct tpm_private *tp)
437{
438        tp->is_suspended = 0;
439        /* Allow applications to send again. */
440        mutex_unlock(&suspend_lock);
441        return 0;
442}
443
444static int tpmfront_suspend_cancel(struct xenbus_device *dev)
445{
446        struct tpm_private *tp = tpm_private_from_dev(&dev->dev);
447        return tpmfront_suspend_finish(tp);
448}
449
450static int tpmfront_resume(struct xenbus_device *dev)
451{
452        struct tpm_private *tp = tpm_private_from_dev(&dev->dev);
453        destroy_tpmring(tp);
454        return talk_to_backend(dev, tp);
455}
456
457static int tpmif_connect(struct xenbus_device *dev,
458                         struct tpm_private *tp,
459                         domid_t domid)
460{
461        int err;
462
463        tp->backend_id = domid;
464
465        err = bind_listening_port_to_irqhandler(
466                domid, tpmif_int, SA_SAMPLE_RANDOM, "tpmif", tp);
467        if (err <= 0) {
468                WPRINTK("bind_listening_port_to_irqhandler failed "
469                        "(err=%d)\n", err);
470                return err;
471        }
472        tp->irq = err;
473
474        return 0;
475}
476
477static struct xenbus_device_id tpmfront_ids[] = {
478        { "vtpm" },
479        { "" }
480};
481
482static struct xenbus_driver tpmfront = {
483        .name = "vtpm",
484        .owner = THIS_MODULE,
485        .ids = tpmfront_ids,
486        .probe = tpmfront_probe,
487        .remove =  tpmfront_remove,
488        .resume = tpmfront_resume,
489        .otherend_changed = backend_changed,
490        .suspend = tpmfront_suspend,
491        .suspend_cancel = tpmfront_suspend_cancel,
492};
493
494static void __init init_tpm_xenbus(void)
495{
496        xenbus_register_frontend(&tpmfront);
497}
498
499static int tpmif_allocate_tx_buffers(struct tpm_private *tp)
500{
501        unsigned int i;
502
503        for (i = 0; i < TPMIF_TX_RING_SIZE; i++) {
504                tp->tx_buffers[i] = tx_buffer_alloc();
505                if (!tp->tx_buffers[i]) {
506                        tpmif_free_tx_buffers(tp);
507                        return -ENOMEM;
508                }
509        }
510        return 0;
511}
512
513static void tpmif_free_tx_buffers(struct tpm_private *tp)
514{
515        unsigned int i;
516
517        for (i = 0; i < TPMIF_TX_RING_SIZE; i++)
518                tx_buffer_free(tp->tx_buffers[i]);
519}
520
521static void tpmif_rx_action(unsigned long priv)
522{
523        struct tpm_private *tp = (struct tpm_private *)priv;
524        int i = 0;
525        unsigned int received;
526        unsigned int offset = 0;
527        u8 *buffer;
528        tpmif_tx_request_t *tx = &tp->tx->ring[i].req;
529
530        atomic_set(&tp->tx_busy, 0);
531        wake_up_interruptible(&tp->wait_q);
532
533        received = tx->size;
534
535        buffer = kmalloc(received, GFP_ATOMIC);
536        if (!buffer)
537                return;
538
539        for (i = 0; i < TPMIF_TX_RING_SIZE && offset < received; i++) {
540                struct tx_buffer *txb = tp->tx_buffers[i];
541                tpmif_tx_request_t *tx;
542                unsigned int tocopy;
543
544                tx = &tp->tx->ring[i].req;
545                tocopy = tx->size;
546                if (tocopy > PAGE_SIZE)
547                        tocopy = PAGE_SIZE;
548
549                memcpy(&buffer[offset], txb->data, tocopy);
550
551                gnttab_release_grant_reference(&gref_head, tx->ref);
552
553                offset += tocopy;
554        }
555
556        vtpm_vd_recv(tp->chip, buffer, received, tp->tx_remember);
557        kfree(buffer);
558}
559
560
561static irqreturn_t tpmif_int(int irq, void *tpm_priv, struct pt_regs *ptregs)
562{
563        struct tpm_private *tp = tpm_priv;
564        unsigned long flags;
565
566        spin_lock_irqsave(&tp->tx_lock, flags);
567        tpmif_rx_tasklet.data = (unsigned long)tp;
568        tasklet_schedule(&tpmif_rx_tasklet);
569        spin_unlock_irqrestore(&tp->tx_lock, flags);
570
571        return IRQ_HANDLED;
572}
573
574
575static int tpm_xmit(struct tpm_private *tp,
576                    const u8 * buf, size_t count, int isuserbuffer,
577                    void *remember)
578{
579        tpmif_tx_request_t *tx;
580        TPMIF_RING_IDX i;
581        unsigned int offset = 0;
582
583        spin_lock_irq(&tp->tx_lock);
584
585        if (unlikely(atomic_read(&tp->tx_busy))) {
586                printk("tpm_xmit: There's an outstanding request/response "
587                       "on the way!\n");
588                spin_unlock_irq(&tp->tx_lock);
589                return -EBUSY;
590        }
591
592        if (tp->is_connected != 1) {
593                spin_unlock_irq(&tp->tx_lock);
594                return -EIO;
595        }
596
597        for (i = 0; count > 0 && i < TPMIF_TX_RING_SIZE; i++) {
598                struct tx_buffer *txb = tp->tx_buffers[i];
599                int copied;
600
601                if (!txb) {
602                        DPRINTK("txb (i=%d) is NULL. buffers initilized?\n"
603                                "Not transmitting anything!\n", i);
604                        spin_unlock_irq(&tp->tx_lock);
605                        return -EFAULT;
606                }
607
608                copied = tx_buffer_copy(txb, &buf[offset], count,
609                                        isuserbuffer);
610                if (copied < 0) {
611                        /* An error occurred */
612                        spin_unlock_irq(&tp->tx_lock);
613                        return copied;
614                }
615                count -= copied;
616                offset += copied;
617
618                tx = &tp->tx->ring[i].req;
619                tx->addr = virt_to_machine(txb->data);
620                tx->size = txb->len;
621
622                DPRINTK("First 4 characters sent by TPM-FE are "
623                        "0x%02x 0x%02x 0x%02x 0x%02x\n",
624                        txb->data[0],txb->data[1],txb->data[2],txb->data[3]);
625
626                /* Get the granttable reference for this page. */
627                tx->ref = gnttab_claim_grant_reference(&gref_head);
628                if (tx->ref == -ENOSPC) {
629                        spin_unlock_irq(&tp->tx_lock);
630                        DPRINTK("Grant table claim reference failed in "
631                                "func:%s line:%d file:%s\n",
632                                __FUNCTION__, __LINE__, __FILE__);
633                        return -ENOSPC;
634                }
635                gnttab_grant_foreign_access_ref(tx->ref,
636                                                tp->backend_id,
637                                                virt_to_mfn(txb->data),
638                                                0 /*RW*/);
639                wmb();
640        }
641
642        atomic_set(&tp->tx_busy, 1);
643        tp->tx_remember = remember;
644
645        mb();
646
647        notify_remote_via_irq(tp->irq);
648
649        spin_unlock_irq(&tp->tx_lock);
650        return offset;
651}
652
653
654static void tpmif_notify_upperlayer(struct tpm_private *tp)
655{
656        /* Notify upper layer about the state of the connection to the BE. */
657        vtpm_vd_status(tp->chip, (tp->is_connected
658                                  ? TPM_VD_STATUS_CONNECTED
659                                  : TPM_VD_STATUS_DISCONNECTED));
660}
661
662
663static void tpmif_set_connected_state(struct tpm_private *tp, u8 is_connected)
664{
665        /*
666         * Don't notify upper layer if we are in suspend mode and
667         * should disconnect - assumption is that we will resume
668         * The mutex keeps apps from sending.
669         */
670        if (is_connected == 0 && tp->is_suspended == 1)
671                return;
672
673        /*
674         * Unlock the mutex if we are connected again
675         * after being suspended - now resuming.
676         * This also removes the suspend state.
677         */
678        if (is_connected == 1 && tp->is_suspended == 1)
679                tpmfront_suspend_finish(tp);
680
681        if (is_connected != tp->is_connected) {
682                tp->is_connected = is_connected;
683                tpmif_notify_upperlayer(tp);
684        }
685}
686
687
688
689/* =================================================================
690 * Initialization function.
691 * =================================================================
692 */
693
694
695static int __init tpmif_init(void)
696{
697        struct tpm_private *tp;
698
699        if (is_initial_xendomain())
700                return -EPERM;
701
702        tp = tpm_private_get();
703        if (!tp)
704                return -ENOMEM;
705
706        IPRINTK("Initialising the vTPM driver.\n");
707        if (gnttab_alloc_grant_references(TPMIF_TX_RING_SIZE,
708                                          &gref_head) < 0) {
709                tpm_private_put();
710                return -EFAULT;
711        }
712
713        init_tpm_xenbus();
714        return 0;
715}
716
717
718module_init(tpmif_init);
719
720MODULE_LICENSE("Dual BSD/GPL");
Note: See TracBrowser for help on using the repository browser.