]> asedeno.scripts.mit.edu Git - linux.git/blob - drivers/thunderbolt/domain.c
ab4b304306f7c8907cc0d77994172ecf0303e40c
[linux.git] / drivers / thunderbolt / domain.c
1 /*
2  * Thunderbolt bus support
3  *
4  * Copyright (C) 2017, Intel Corporation
5  * Author:  Mika Westerberg <mika.westerberg@linux.intel.com>
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License version 2 as
9  * published by the Free Software Foundation.
10  */
11
12 #include <linux/device.h>
13 #include <linux/idr.h>
14 #include <linux/module.h>
15 #include <linux/slab.h>
16 #include <linux/random.h>
17 #include <crypto/hash.h>
18
19 #include "tb.h"
20
21 static DEFINE_IDA(tb_domain_ida);
22
23 static bool match_service_id(const struct tb_service_id *id,
24                              const struct tb_service *svc)
25 {
26         if (id->match_flags & TBSVC_MATCH_PROTOCOL_KEY) {
27                 if (strcmp(id->protocol_key, svc->key))
28                         return false;
29         }
30
31         if (id->match_flags & TBSVC_MATCH_PROTOCOL_ID) {
32                 if (id->protocol_id != svc->prtcid)
33                         return false;
34         }
35
36         if (id->match_flags & TBSVC_MATCH_PROTOCOL_VERSION) {
37                 if (id->protocol_version != svc->prtcvers)
38                         return false;
39         }
40
41         if (id->match_flags & TBSVC_MATCH_PROTOCOL_VERSION) {
42                 if (id->protocol_revision != svc->prtcrevs)
43                         return false;
44         }
45
46         return true;
47 }
48
49 static const struct tb_service_id *__tb_service_match(struct device *dev,
50                                                       struct device_driver *drv)
51 {
52         struct tb_service_driver *driver;
53         const struct tb_service_id *ids;
54         struct tb_service *svc;
55
56         svc = tb_to_service(dev);
57         if (!svc)
58                 return NULL;
59
60         driver = container_of(drv, struct tb_service_driver, driver);
61         if (!driver->id_table)
62                 return NULL;
63
64         for (ids = driver->id_table; ids->match_flags != 0; ids++) {
65                 if (match_service_id(ids, svc))
66                         return ids;
67         }
68
69         return NULL;
70 }
71
72 static int tb_service_match(struct device *dev, struct device_driver *drv)
73 {
74         return !!__tb_service_match(dev, drv);
75 }
76
77 static int tb_service_probe(struct device *dev)
78 {
79         struct tb_service *svc = tb_to_service(dev);
80         struct tb_service_driver *driver;
81         const struct tb_service_id *id;
82
83         driver = container_of(dev->driver, struct tb_service_driver, driver);
84         id = __tb_service_match(dev, &driver->driver);
85
86         return driver->probe(svc, id);
87 }
88
89 static int tb_service_remove(struct device *dev)
90 {
91         struct tb_service *svc = tb_to_service(dev);
92         struct tb_service_driver *driver;
93
94         driver = container_of(dev->driver, struct tb_service_driver, driver);
95         if (driver->remove)
96                 driver->remove(svc);
97
98         return 0;
99 }
100
101 static void tb_service_shutdown(struct device *dev)
102 {
103         struct tb_service_driver *driver;
104         struct tb_service *svc;
105
106         svc = tb_to_service(dev);
107         if (!svc || !dev->driver)
108                 return;
109
110         driver = container_of(dev->driver, struct tb_service_driver, driver);
111         if (driver->shutdown)
112                 driver->shutdown(svc);
113 }
114
115 static const char * const tb_security_names[] = {
116         [TB_SECURITY_NONE] = "none",
117         [TB_SECURITY_USER] = "user",
118         [TB_SECURITY_SECURE] = "secure",
119         [TB_SECURITY_DPONLY] = "dponly",
120 };
121
122 static ssize_t boot_acl_show(struct device *dev, struct device_attribute *attr,
123                              char *buf)
124 {
125         struct tb *tb = container_of(dev, struct tb, dev);
126         uuid_t *uuids;
127         ssize_t ret;
128         int i;
129
130         uuids = kcalloc(tb->nboot_acl, sizeof(uuid_t), GFP_KERNEL);
131         if (!uuids)
132                 return -ENOMEM;
133
134         if (mutex_lock_interruptible(&tb->lock)) {
135                 ret = -ERESTARTSYS;
136                 goto out;
137         }
138         ret = tb->cm_ops->get_boot_acl(tb, uuids, tb->nboot_acl);
139         if (ret) {
140                 mutex_unlock(&tb->lock);
141                 goto out;
142         }
143         mutex_unlock(&tb->lock);
144
145         for (ret = 0, i = 0; i < tb->nboot_acl; i++) {
146                 if (!uuid_is_null(&uuids[i]))
147                         ret += snprintf(buf + ret, PAGE_SIZE - ret, "%pUb",
148                                         &uuids[i]);
149
150                 ret += snprintf(buf + ret, PAGE_SIZE - ret, "%s",
151                                i < tb->nboot_acl - 1 ? "," : "\n");
152         }
153
154 out:
155         kfree(uuids);
156         return ret;
157 }
158
159 static ssize_t boot_acl_store(struct device *dev, struct device_attribute *attr,
160                               const char *buf, size_t count)
161 {
162         struct tb *tb = container_of(dev, struct tb, dev);
163         char *str, *s, *uuid_str;
164         ssize_t ret = 0;
165         uuid_t *acl;
166         int i = 0;
167
168         /*
169          * Make sure the value is not bigger than tb->nboot_acl * UUID
170          * length + commas and optional "\n". Also the smallest allowable
171          * string is tb->nboot_acl * ",".
172          */
173         if (count > (UUID_STRING_LEN + 1) * tb->nboot_acl + 1)
174                 return -EINVAL;
175         if (count < tb->nboot_acl - 1)
176                 return -EINVAL;
177
178         str = kstrdup(buf, GFP_KERNEL);
179         if (!str)
180                 return -ENOMEM;
181
182         acl = kcalloc(tb->nboot_acl, sizeof(uuid_t), GFP_KERNEL);
183         if (!acl) {
184                 ret = -ENOMEM;
185                 goto err_free_str;
186         }
187
188         uuid_str = strim(str);
189         while ((s = strsep(&uuid_str, ",")) != NULL && i < tb->nboot_acl) {
190                 size_t len = strlen(s);
191
192                 if (len) {
193                         if (len != UUID_STRING_LEN) {
194                                 ret = -EINVAL;
195                                 goto err_free_acl;
196                         }
197                         ret = uuid_parse(s, &acl[i]);
198                         if (ret)
199                                 goto err_free_acl;
200                 }
201
202                 i++;
203         }
204
205         if (s || i < tb->nboot_acl) {
206                 ret = -EINVAL;
207                 goto err_free_acl;
208         }
209
210         if (mutex_lock_interruptible(&tb->lock)) {
211                 ret = -ERESTARTSYS;
212                 goto err_free_acl;
213         }
214         ret = tb->cm_ops->set_boot_acl(tb, acl, tb->nboot_acl);
215         mutex_unlock(&tb->lock);
216
217 err_free_acl:
218         kfree(acl);
219 err_free_str:
220         kfree(str);
221
222         return ret ?: count;
223 }
224 static DEVICE_ATTR_RW(boot_acl);
225
226 static ssize_t security_show(struct device *dev, struct device_attribute *attr,
227                              char *buf)
228 {
229         struct tb *tb = container_of(dev, struct tb, dev);
230
231         return sprintf(buf, "%s\n", tb_security_names[tb->security_level]);
232 }
233 static DEVICE_ATTR_RO(security);
234
235 static struct attribute *domain_attrs[] = {
236         &dev_attr_boot_acl.attr,
237         &dev_attr_security.attr,
238         NULL,
239 };
240
241 static umode_t domain_attr_is_visible(struct kobject *kobj,
242                                       struct attribute *attr, int n)
243 {
244         struct device *dev = container_of(kobj, struct device, kobj);
245         struct tb *tb = container_of(dev, struct tb, dev);
246
247         if (attr == &dev_attr_boot_acl.attr) {
248                 if (tb->nboot_acl &&
249                     tb->cm_ops->get_boot_acl &&
250                     tb->cm_ops->set_boot_acl)
251                         return attr->mode;
252                 return 0;
253         }
254
255         return attr->mode;
256 }
257
258 static struct attribute_group domain_attr_group = {
259         .is_visible = domain_attr_is_visible,
260         .attrs = domain_attrs,
261 };
262
263 static const struct attribute_group *domain_attr_groups[] = {
264         &domain_attr_group,
265         NULL,
266 };
267
268 struct bus_type tb_bus_type = {
269         .name = "thunderbolt",
270         .match = tb_service_match,
271         .probe = tb_service_probe,
272         .remove = tb_service_remove,
273         .shutdown = tb_service_shutdown,
274 };
275
276 static void tb_domain_release(struct device *dev)
277 {
278         struct tb *tb = container_of(dev, struct tb, dev);
279
280         tb_ctl_free(tb->ctl);
281         destroy_workqueue(tb->wq);
282         ida_simple_remove(&tb_domain_ida, tb->index);
283         mutex_destroy(&tb->lock);
284         kfree(tb);
285 }
286
287 struct device_type tb_domain_type = {
288         .name = "thunderbolt_domain",
289         .release = tb_domain_release,
290 };
291
292 /**
293  * tb_domain_alloc() - Allocate a domain
294  * @nhi: Pointer to the host controller
295  * @privsize: Size of the connection manager private data
296  *
297  * Allocates and initializes a new Thunderbolt domain. Connection
298  * managers are expected to call this and then fill in @cm_ops
299  * accordingly.
300  *
301  * Call tb_domain_put() to release the domain before it has been added
302  * to the system.
303  *
304  * Return: allocated domain structure on %NULL in case of error
305  */
306 struct tb *tb_domain_alloc(struct tb_nhi *nhi, size_t privsize)
307 {
308         struct tb *tb;
309
310         /*
311          * Make sure the structure sizes map with that the hardware
312          * expects because bit-fields are being used.
313          */
314         BUILD_BUG_ON(sizeof(struct tb_regs_switch_header) != 5 * 4);
315         BUILD_BUG_ON(sizeof(struct tb_regs_port_header) != 8 * 4);
316         BUILD_BUG_ON(sizeof(struct tb_regs_hop) != 2 * 4);
317
318         tb = kzalloc(sizeof(*tb) + privsize, GFP_KERNEL);
319         if (!tb)
320                 return NULL;
321
322         tb->nhi = nhi;
323         mutex_init(&tb->lock);
324
325         tb->index = ida_simple_get(&tb_domain_ida, 0, 0, GFP_KERNEL);
326         if (tb->index < 0)
327                 goto err_free;
328
329         tb->wq = alloc_ordered_workqueue("thunderbolt%d", 0, tb->index);
330         if (!tb->wq)
331                 goto err_remove_ida;
332
333         tb->dev.parent = &nhi->pdev->dev;
334         tb->dev.bus = &tb_bus_type;
335         tb->dev.type = &tb_domain_type;
336         tb->dev.groups = domain_attr_groups;
337         dev_set_name(&tb->dev, "domain%d", tb->index);
338         device_initialize(&tb->dev);
339
340         return tb;
341
342 err_remove_ida:
343         ida_simple_remove(&tb_domain_ida, tb->index);
344 err_free:
345         kfree(tb);
346
347         return NULL;
348 }
349
350 static bool tb_domain_event_cb(void *data, enum tb_cfg_pkg_type type,
351                                const void *buf, size_t size)
352 {
353         struct tb *tb = data;
354
355         if (!tb->cm_ops->handle_event) {
356                 tb_warn(tb, "domain does not have event handler\n");
357                 return true;
358         }
359
360         switch (type) {
361         case TB_CFG_PKG_XDOMAIN_REQ:
362         case TB_CFG_PKG_XDOMAIN_RESP:
363                 return tb_xdomain_handle_request(tb, type, buf, size);
364
365         default:
366                 tb->cm_ops->handle_event(tb, type, buf, size);
367         }
368
369         return true;
370 }
371
372 /**
373  * tb_domain_add() - Add domain to the system
374  * @tb: Domain to add
375  *
376  * Starts the domain and adds it to the system. Hotplugging devices will
377  * work after this has been returned successfully. In order to remove
378  * and release the domain after this function has been called, call
379  * tb_domain_remove().
380  *
381  * Return: %0 in case of success and negative errno in case of error
382  */
383 int tb_domain_add(struct tb *tb)
384 {
385         int ret;
386
387         if (WARN_ON(!tb->cm_ops))
388                 return -EINVAL;
389
390         mutex_lock(&tb->lock);
391
392         tb->ctl = tb_ctl_alloc(tb->nhi, tb_domain_event_cb, tb);
393         if (!tb->ctl) {
394                 ret = -ENOMEM;
395                 goto err_unlock;
396         }
397
398         /*
399          * tb_schedule_hotplug_handler may be called as soon as the config
400          * channel is started. Thats why we have to hold the lock here.
401          */
402         tb_ctl_start(tb->ctl);
403
404         if (tb->cm_ops->driver_ready) {
405                 ret = tb->cm_ops->driver_ready(tb);
406                 if (ret)
407                         goto err_ctl_stop;
408         }
409
410         ret = device_add(&tb->dev);
411         if (ret)
412                 goto err_ctl_stop;
413
414         /* Start the domain */
415         if (tb->cm_ops->start) {
416                 ret = tb->cm_ops->start(tb);
417                 if (ret)
418                         goto err_domain_del;
419         }
420
421         /* This starts event processing */
422         mutex_unlock(&tb->lock);
423
424         return 0;
425
426 err_domain_del:
427         device_del(&tb->dev);
428 err_ctl_stop:
429         tb_ctl_stop(tb->ctl);
430 err_unlock:
431         mutex_unlock(&tb->lock);
432
433         return ret;
434 }
435
436 /**
437  * tb_domain_remove() - Removes and releases a domain
438  * @tb: Domain to remove
439  *
440  * Stops the domain, removes it from the system and releases all
441  * resources once the last reference has been released.
442  */
443 void tb_domain_remove(struct tb *tb)
444 {
445         mutex_lock(&tb->lock);
446         if (tb->cm_ops->stop)
447                 tb->cm_ops->stop(tb);
448         /* Stop the domain control traffic */
449         tb_ctl_stop(tb->ctl);
450         mutex_unlock(&tb->lock);
451
452         flush_workqueue(tb->wq);
453         device_unregister(&tb->dev);
454 }
455
456 /**
457  * tb_domain_suspend_noirq() - Suspend a domain
458  * @tb: Domain to suspend
459  *
460  * Suspends all devices in the domain and stops the control channel.
461  */
462 int tb_domain_suspend_noirq(struct tb *tb)
463 {
464         int ret = 0;
465
466         /*
467          * The control channel interrupt is left enabled during suspend
468          * and taking the lock here prevents any events happening before
469          * we actually have stopped the domain and the control channel.
470          */
471         mutex_lock(&tb->lock);
472         if (tb->cm_ops->suspend_noirq)
473                 ret = tb->cm_ops->suspend_noirq(tb);
474         if (!ret)
475                 tb_ctl_stop(tb->ctl);
476         mutex_unlock(&tb->lock);
477
478         return ret;
479 }
480
481 /**
482  * tb_domain_resume_noirq() - Resume a domain
483  * @tb: Domain to resume
484  *
485  * Re-starts the control channel, and resumes all devices connected to
486  * the domain.
487  */
488 int tb_domain_resume_noirq(struct tb *tb)
489 {
490         int ret = 0;
491
492         mutex_lock(&tb->lock);
493         tb_ctl_start(tb->ctl);
494         if (tb->cm_ops->resume_noirq)
495                 ret = tb->cm_ops->resume_noirq(tb);
496         mutex_unlock(&tb->lock);
497
498         return ret;
499 }
500
501 int tb_domain_suspend(struct tb *tb)
502 {
503         int ret;
504
505         mutex_lock(&tb->lock);
506         if (tb->cm_ops->suspend) {
507                 ret = tb->cm_ops->suspend(tb);
508                 if (ret) {
509                         mutex_unlock(&tb->lock);
510                         return ret;
511                 }
512         }
513         mutex_unlock(&tb->lock);
514         return 0;
515 }
516
517 void tb_domain_complete(struct tb *tb)
518 {
519         mutex_lock(&tb->lock);
520         if (tb->cm_ops->complete)
521                 tb->cm_ops->complete(tb);
522         mutex_unlock(&tb->lock);
523 }
524
525 /**
526  * tb_domain_approve_switch() - Approve switch
527  * @tb: Domain the switch belongs to
528  * @sw: Switch to approve
529  *
530  * This will approve switch by connection manager specific means. In
531  * case of success the connection manager will create tunnels for all
532  * supported protocols.
533  */
534 int tb_domain_approve_switch(struct tb *tb, struct tb_switch *sw)
535 {
536         struct tb_switch *parent_sw;
537
538         if (!tb->cm_ops->approve_switch)
539                 return -EPERM;
540
541         /* The parent switch must be authorized before this one */
542         parent_sw = tb_to_switch(sw->dev.parent);
543         if (!parent_sw || !parent_sw->authorized)
544                 return -EINVAL;
545
546         return tb->cm_ops->approve_switch(tb, sw);
547 }
548
549 /**
550  * tb_domain_approve_switch_key() - Approve switch and add key
551  * @tb: Domain the switch belongs to
552  * @sw: Switch to approve
553  *
554  * For switches that support secure connect, this function first adds
555  * key to the switch NVM using connection manager specific means. If
556  * adding the key is successful, the switch is approved and connected.
557  *
558  * Return: %0 on success and negative errno in case of failure.
559  */
560 int tb_domain_approve_switch_key(struct tb *tb, struct tb_switch *sw)
561 {
562         struct tb_switch *parent_sw;
563         int ret;
564
565         if (!tb->cm_ops->approve_switch || !tb->cm_ops->add_switch_key)
566                 return -EPERM;
567
568         /* The parent switch must be authorized before this one */
569         parent_sw = tb_to_switch(sw->dev.parent);
570         if (!parent_sw || !parent_sw->authorized)
571                 return -EINVAL;
572
573         ret = tb->cm_ops->add_switch_key(tb, sw);
574         if (ret)
575                 return ret;
576
577         return tb->cm_ops->approve_switch(tb, sw);
578 }
579
580 /**
581  * tb_domain_challenge_switch_key() - Challenge and approve switch
582  * @tb: Domain the switch belongs to
583  * @sw: Switch to approve
584  *
585  * For switches that support secure connect, this function generates
586  * random challenge and sends it to the switch. The switch responds to
587  * this and if the response matches our random challenge, the switch is
588  * approved and connected.
589  *
590  * Return: %0 on success and negative errno in case of failure.
591  */
592 int tb_domain_challenge_switch_key(struct tb *tb, struct tb_switch *sw)
593 {
594         u8 challenge[TB_SWITCH_KEY_SIZE];
595         u8 response[TB_SWITCH_KEY_SIZE];
596         u8 hmac[TB_SWITCH_KEY_SIZE];
597         struct tb_switch *parent_sw;
598         struct crypto_shash *tfm;
599         struct shash_desc *shash;
600         int ret;
601
602         if (!tb->cm_ops->approve_switch || !tb->cm_ops->challenge_switch_key)
603                 return -EPERM;
604
605         /* The parent switch must be authorized before this one */
606         parent_sw = tb_to_switch(sw->dev.parent);
607         if (!parent_sw || !parent_sw->authorized)
608                 return -EINVAL;
609
610         get_random_bytes(challenge, sizeof(challenge));
611         ret = tb->cm_ops->challenge_switch_key(tb, sw, challenge, response);
612         if (ret)
613                 return ret;
614
615         tfm = crypto_alloc_shash("hmac(sha256)", 0, 0);
616         if (IS_ERR(tfm))
617                 return PTR_ERR(tfm);
618
619         ret = crypto_shash_setkey(tfm, sw->key, TB_SWITCH_KEY_SIZE);
620         if (ret)
621                 goto err_free_tfm;
622
623         shash = kzalloc(sizeof(*shash) + crypto_shash_descsize(tfm),
624                         GFP_KERNEL);
625         if (!shash) {
626                 ret = -ENOMEM;
627                 goto err_free_tfm;
628         }
629
630         shash->tfm = tfm;
631         shash->flags = CRYPTO_TFM_REQ_MAY_SLEEP;
632
633         memset(hmac, 0, sizeof(hmac));
634         ret = crypto_shash_digest(shash, challenge, sizeof(hmac), hmac);
635         if (ret)
636                 goto err_free_shash;
637
638         /* The returned HMAC must match the one we calculated */
639         if (memcmp(response, hmac, sizeof(hmac))) {
640                 ret = -EKEYREJECTED;
641                 goto err_free_shash;
642         }
643
644         crypto_free_shash(tfm);
645         kfree(shash);
646
647         return tb->cm_ops->approve_switch(tb, sw);
648
649 err_free_shash:
650         kfree(shash);
651 err_free_tfm:
652         crypto_free_shash(tfm);
653
654         return ret;
655 }
656
657 /**
658  * tb_domain_disconnect_pcie_paths() - Disconnect all PCIe paths
659  * @tb: Domain whose PCIe paths to disconnect
660  *
661  * This needs to be called in preparation for NVM upgrade of the host
662  * controller. Makes sure all PCIe paths are disconnected.
663  *
664  * Return %0 on success and negative errno in case of error.
665  */
666 int tb_domain_disconnect_pcie_paths(struct tb *tb)
667 {
668         if (!tb->cm_ops->disconnect_pcie_paths)
669                 return -EPERM;
670
671         return tb->cm_ops->disconnect_pcie_paths(tb);
672 }
673
674 /**
675  * tb_domain_approve_xdomain_paths() - Enable DMA paths for XDomain
676  * @tb: Domain enabling the DMA paths
677  * @xd: XDomain DMA paths are created to
678  *
679  * Calls connection manager specific method to enable DMA paths to the
680  * XDomain in question.
681  *
682  * Return: 0% in case of success and negative errno otherwise. In
683  * particular returns %-ENOTSUPP if the connection manager
684  * implementation does not support XDomains.
685  */
686 int tb_domain_approve_xdomain_paths(struct tb *tb, struct tb_xdomain *xd)
687 {
688         if (!tb->cm_ops->approve_xdomain_paths)
689                 return -ENOTSUPP;
690
691         return tb->cm_ops->approve_xdomain_paths(tb, xd);
692 }
693
694 /**
695  * tb_domain_disconnect_xdomain_paths() - Disable DMA paths for XDomain
696  * @tb: Domain disabling the DMA paths
697  * @xd: XDomain whose DMA paths are disconnected
698  *
699  * Calls connection manager specific method to disconnect DMA paths to
700  * the XDomain in question.
701  *
702  * Return: 0% in case of success and negative errno otherwise. In
703  * particular returns %-ENOTSUPP if the connection manager
704  * implementation does not support XDomains.
705  */
706 int tb_domain_disconnect_xdomain_paths(struct tb *tb, struct tb_xdomain *xd)
707 {
708         if (!tb->cm_ops->disconnect_xdomain_paths)
709                 return -ENOTSUPP;
710
711         return tb->cm_ops->disconnect_xdomain_paths(tb, xd);
712 }
713
714 static int disconnect_xdomain(struct device *dev, void *data)
715 {
716         struct tb_xdomain *xd;
717         struct tb *tb = data;
718         int ret = 0;
719
720         xd = tb_to_xdomain(dev);
721         if (xd && xd->tb == tb)
722                 ret = tb_xdomain_disable_paths(xd);
723
724         return ret;
725 }
726
727 /**
728  * tb_domain_disconnect_all_paths() - Disconnect all paths for the domain
729  * @tb: Domain whose paths are disconnected
730  *
731  * This function can be used to disconnect all paths (PCIe, XDomain) for
732  * example in preparation for host NVM firmware upgrade. After this is
733  * called the paths cannot be established without resetting the switch.
734  *
735  * Return: %0 in case of success and negative errno otherwise.
736  */
737 int tb_domain_disconnect_all_paths(struct tb *tb)
738 {
739         int ret;
740
741         ret = tb_domain_disconnect_pcie_paths(tb);
742         if (ret)
743                 return ret;
744
745         return bus_for_each_dev(&tb_bus_type, NULL, tb, disconnect_xdomain);
746 }
747
748 int tb_domain_init(void)
749 {
750         int ret;
751
752         ret = tb_xdomain_init();
753         if (ret)
754                 return ret;
755         ret = bus_register(&tb_bus_type);
756         if (ret)
757                 tb_xdomain_exit();
758
759         return ret;
760 }
761
762 void tb_domain_exit(void)
763 {
764         bus_unregister(&tb_bus_type);
765         ida_destroy(&tb_domain_ida);
766         tb_switch_exit();
767         tb_xdomain_exit();
768 }