The Design and Implementation of the FreeBSD Operating System, Second Edition
Now available: The Design and Implementation of the FreeBSD Operating System (Second Edition)


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]

FreeBSD/Linux Kernel Cross Reference
sys/norma/xmm_import.c

Version: -  FREEBSD  -  FREEBSD-13-STABLE  -  FREEBSD-13-0  -  FREEBSD-12-STABLE  -  FREEBSD-12-0  -  FREEBSD-11-STABLE  -  FREEBSD-11-0  -  FREEBSD-10-STABLE  -  FREEBSD-10-0  -  FREEBSD-9-STABLE  -  FREEBSD-9-0  -  FREEBSD-8-STABLE  -  FREEBSD-8-0  -  FREEBSD-7-STABLE  -  FREEBSD-7-0  -  FREEBSD-6-STABLE  -  FREEBSD-6-0  -  FREEBSD-5-STABLE  -  FREEBSD-5-0  -  FREEBSD-4-STABLE  -  FREEBSD-3-STABLE  -  FREEBSD22  -  l41  -  OPENBSD  -  linux-2.6  -  MK84  -  PLAN9  -  xnu-8792 
SearchContext: -  none  -  3  -  10 

    1 /* 
    2  * Mach Operating System
    3  * Copyright (c) 1991 Carnegie Mellon University
    4  * All Rights Reserved.
    5  * 
    6  * Permission to use, copy, modify and distribute this software and its
    7  * documentation is hereby granted, provided that both the copyright
    8  * notice and this permission notice appear in all copies of the
    9  * software, derivative works or modified versions, and any portions
   10  * thereof, and that both notices appear in supporting documentation.
   11  * 
   12  * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS 
   13  * CONDITION.  CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
   14  * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
   15  * 
   16  * Carnegie Mellon requests users of this software to return to
   17  * 
   18  *  Software Distribution Coordinator  or  Software.Distribution@CS.CMU.EDU
   19  *  School of Computer Science
   20  *  Carnegie Mellon University
   21  *  Pittsburgh PA 15213-3890
   22  * 
   23  * any improvements or extensions that they make and grant Carnegie the
   24  * rights to redistribute these changes.
   25  */
   26 /*
   27  * HISTORY
   28  * $Log:        xmm_import.c,v $
   29  * Revision 2.6  92/03/10  16:29:11  jsb
   30  *      Merged in norma branch changes as of NORMA_MK7.
   31  *      [92/03/09  12:51:19  jsb]
   32  * 
   33  * Revision 2.5.2.5  92/02/21  11:25:54  jsb
   34  *      Reference mobj on port to mobj conversion; release when done.
   35  *      [92/02/20  10:53:45  jsb]
   36  * 
   37  *      Deallocate all resources upon termination.
   38  *      Deallocate allocated replies.
   39  *      Changed MACH_PORT_NULL uses to IP_NULL.
   40  *      [92/02/18  07:56:15  jsb]
   41  * 
   42  *      Explicitly provide name parameter to xmm_decl macro.
   43  *      Handle m_import_terminate being called before proxy_set_ready has been.
   44  *      This can happen because the vm system may terminate an object after
   45  *      it has been initialized but before it is ready.
   46  *      [92/02/16  15:26:31  jsb]
   47  * 
   48  *      Don't call proxy_terminate on a null port.
   49  *      [92/02/11  18:21:03  jsb]
   50  * 
   51  *      Added ipc_kobject_set to null to break port/mobj association.
   52  *      Accordingly, removed dead field and associated logic.
   53  *      [92/02/11  13:22:39  jsb]
   54  * 
   55  *      Renamed xmm_import_notify to xmm_kernel_notify.
   56  *      [92/02/10  17:26:50  jsb]
   57  * 
   58  *      Use xmm object instead of <guessed host, memory_object> pair in
   59  *      proxy_init. Renamed {mobj,kobj}_port to xmm_{pager,kernel}.
   60  *      [92/02/10  16:56:20  jsb]
   61  * 
   62  *      Use new xmm_decl, and new memory_object_name and deallocation protocol.
   63  *      [92/02/09  12:51:12  jsb]
   64  * 
   65  * Revision 2.5.2.4  92/01/21  22:22:24  jsb
   66  *      18-Jan-92 David L. Black (dlb) at Open Software Foundation
   67  *      Add dead field to mobj and use to synchronize termination
   68  *      against other operations.
   69  * 
   70  * Revision 2.5.2.3  92/01/21  21:54:12  jsb
   71  *      Added xmm_import_notify stub.
   72  *      [92/01/21  18:20:54  jsb]
   73  * 
   74  *      Use ports instead of pointers when communicating with xmm_export.c.
   75  *      De-linted. Supports new (dlb) memory object routines.
   76  *      Supports arbitrary reply ports to lock_request, etc.
   77  *      Converted mach_port_t (and port_t) to ipc_port_t.
   78  *      [92/01/20  17:22:22  jsb]
   79  * 
   80  *      Fixes from OSF.
   81  *      [92/01/17  14:15:01  jsb]
   82  * 
   83  * Revision 2.5.2.2.1.1  92/01/15  12:16:26  jeffreyh
   84  *      Pass memory_object_name port to proxy terminate. (dlb)
   85  * 
   86  * Revision 2.5.2.2  92/01/09  18:46:13  jsb
   87  *      Use remote_host_priv() instead of norma_get_special_port().
   88  *      [92/01/04  18:33:18  jsb]
   89  * 
   90  * Revision 2.5.2.1  92/01/03  16:38:50  jsb
   91  *      Corrected log.
   92  *      [91/12/24  14:33:29  jsb]
   93  * 
   94  * Revision 2.5  91/12/10  13:26:24  jsb
   95  *      Added missing third parameter in call to proxy_terminate.
   96  *      [91/12/10  12:48:52  jsb]
   97  * 
   98  * Revision 2.4  91/11/14  16:52:34  rpd
   99  *      Replaced master_device_port_at_node call with calls to
  100  *      norma_get_special_port and norma_port_location_hint.
  101  *      [91/11/00            jsb]
  102  * 
  103  * Revision 2.3  91/07/01  08:26:12  jsb
  104  *      Fixed object importation protocol. Return valid return values.
  105  *      [91/06/29  15:30:10  jsb]
  106  * 
  107  * Revision 2.2  91/06/17  15:48:18  jsb
  108  *      First checkin.
  109  *      [91/06/17  11:03:28  jsb]
  110  * 
  111  */
  112 /*
  113  *      File:   norma/xmm_import.c
  114  *      Author: Joseph S. Barrera III
  115  *      Date:   1991
  116  *
  117  *      Xmm layer for mapping a remote object.
  118  */
  119 
  120 #include <norma/xmm_obj.h>
  121 #include <norma/ipc_node.h>
  122 #include <ipc/ipc_space.h>
  123 #include <ipc/ipc_port.h>
  124 #include <mach/notify.h>
  125 #include <mach/proxy.h>
  126 
  127 struct mobj {
  128         struct xmm_obj  obj;
  129         ipc_port_t      xmm_object;
  130         ipc_port_t      xmm_pager;
  131         ipc_port_t      xmm_kernel;
  132         boolean_t       terminated;
  133 };
  134 
  135 #undef  KOBJ
  136 #define KOBJ    ((struct mobj *) kobj)
  137 
  138 #define m_import_deallocate             m_interpose_deallocate
  139 #define k_import_data_unavailable       k_invalid_data_unavailable
  140 #define k_import_get_attributes         k_invalid_get_attributes
  141 #define k_import_lock_request           k_invalid_lock_request
  142 #define k_import_data_error             k_invalid_data_error
  143 #define k_import_set_ready              k_invalid_set_ready
  144 #define k_import_destroy                k_invalid_destroy
  145 #define k_import_data_supply            k_invalid_data_supply
  146 
  147 xmm_decl(import, "import", sizeof(struct mobj));
  148 
  149 kern_return_t
  150 xmm_import_create(xmm_object, new_mobj)
  151         ipc_port_t xmm_object;
  152         xmm_obj_t *new_mobj;
  153 {
  154         xmm_obj_t mobj;
  155         kern_return_t kr;
  156 
  157         kr = xmm_obj_allocate(&import_class, XMM_OBJ_NULL, &mobj);
  158         if (kr != KERN_SUCCESS) {
  159                 return kr;
  160         }
  161 
  162         MOBJ->xmm_object = xmm_object;
  163         MOBJ->xmm_pager = IP_NULL;
  164         MOBJ->xmm_kernel = IP_NULL;
  165         MOBJ->terminated = FALSE;
  166 
  167         *new_mobj = mobj;
  168         return KERN_SUCCESS;
  169 }
  170 
  171 m_import_init(mobj, k_kobj, pagesize, internal, size)
  172         xmm_obj_t mobj;
  173         xmm_obj_t k_kobj;
  174         vm_size_t pagesize;
  175         boolean_t internal;
  176         vm_size_t size;
  177 {
  178         kern_return_t kr;
  179         xmm_obj_t kobj = mobj;
  180         
  181 #ifdef  lint
  182         M_INIT(mobj, k_kobj, pagesize, internal, size);
  183 #endif  lint
  184         xmm_kobj_link(kobj, k_kobj);
  185 
  186         MOBJ->xmm_kernel = ipc_port_alloc_kernel();
  187         if (MOBJ->xmm_kernel == IP_NULL) {
  188                 panic("m_import_init: allocate xmm_kernel");
  189         }
  190         xmm_obj_reference(mobj);
  191         ipc_kobject_set(MOBJ->xmm_kernel, (ipc_kobject_t) mobj,
  192                         IKOT_XMM_KERNEL);
  193 
  194         kr = proxy_init(MOBJ->xmm_object, MOBJ->xmm_kernel, pagesize,
  195                         internal, size);
  196 #if 1
  197         if (kr) {
  198                 panic("m_import_init: proxy_init returns %x\n", kr);
  199         }
  200 #endif
  201         return KERN_SUCCESS;
  202 }
  203 
  204 m_import_terminate(mobj, kobj)
  205         xmm_obj_t mobj;
  206         xmm_obj_t kobj;
  207 {
  208         kern_return_t kr;
  209 
  210 #ifdef  lint
  211         M_TERMINATE(mobj, kobj);
  212 #endif  lint
  213         assert(MOBJ->xmm_kernel != IP_NULL);
  214         if (MOBJ->xmm_pager == IP_NULL) {
  215                 /*
  216                  * This can happen because the vm system only waits
  217                  * until the object is initialized before terminating
  218                  * it; it does not wait until the object is ready.
  219                  * We must therefore wait for _proxy_set_ready ourselves
  220                  * before terminating the object. We don't even have the
  221                  * option of calling proxy_terminate on xmm_object, since
  222                  * we no longer have send rights for xmm_object.
  223                  *
  224                  * We need to retain the xmm_kernel to mobj association
  225                  * so that we can process _proxy_set_ready. Fortunately,
  226                  * there is no need to break this association, since the
  227                  * only call that we should receive is _proxy_set_ready.
  228                  * Any spurious calls to anything else will be caught
  229                  * at the xmm_server level.
  230                  */
  231                 printf("m_import_terminate on unready object 0x%x\n", mobj);
  232                 MOBJ->terminated = TRUE;
  233                 return KERN_SUCCESS;
  234         }
  235         /*
  236          * Deallocate resources associated with mobj.
  237          * MOBJ->xmm_object was deallocated via proxy_init.
  238          * MOBJ->xmm_kernel will be deallocated via proxy_terminate.
  239          * We must explicitly deallocate MOBJ->xmm_pager
  240          * (to make mobj inaccessible) and then release mobj.
  241          */
  242         ipc_kobject_set(MOBJ->xmm_kernel, IKO_NULL, IKOT_NONE);
  243         ipc_port_dealloc_kernel(MOBJ->xmm_kernel);
  244         kr = proxy_terminate(MOBJ->xmm_pager);
  245         assert(kr == KERN_SUCCESS);
  246         xmm_obj_release(mobj);
  247         return KERN_SUCCESS;
  248 }
  249 
  250 m_import_copy(mobj, kobj, offset, length, new_mobj)
  251         xmm_obj_t mobj;
  252         xmm_obj_t kobj;
  253         vm_offset_t offset;
  254         vm_size_t length;
  255         xmm_obj_t new_mobj;
  256 {
  257 #ifdef  lint
  258         M_COPY(mobj, kobj, offset, length, new_mobj);
  259 #endif  lint
  260         panic("m_import_copy\n");
  261 }
  262 
  263 /*
  264  *      VM system should handle ready synchronization for everything else
  265  */
  266 
  267 m_import_data_request(mobj, kobj, offset, length, desired_access)
  268         xmm_obj_t mobj;
  269         xmm_obj_t kobj;
  270         vm_offset_t offset;
  271         vm_size_t length;
  272         vm_prot_t desired_access;
  273 {
  274 #ifdef  lint
  275         M_DATA_REQUEST(mobj, kobj, offset, length, desired_access);
  276 #endif  lint
  277         return proxy_data_request(MOBJ->xmm_pager, offset, length,
  278                                   desired_access);
  279 }
  280 
  281 m_import_data_unlock(mobj, kobj, offset, length, desired_access)
  282         xmm_obj_t mobj;
  283         xmm_obj_t kobj;
  284         vm_offset_t offset;
  285         vm_size_t length;
  286         vm_prot_t desired_access;
  287 {
  288 #ifdef  lint
  289         M_DATA_UNLOCK(mobj, kobj, offset, length, desired_access);
  290 #endif  lint
  291         return proxy_data_unlock(MOBJ->xmm_pager, offset, length,
  292                                  desired_access);
  293 }
  294 
  295 m_import_data_write(mobj, kobj, offset, data, length)
  296         xmm_obj_t mobj;
  297         xmm_obj_t kobj;
  298         vm_offset_t offset;
  299         vm_offset_t data;
  300         vm_size_t length;
  301 {
  302 #ifdef  lint
  303         M_DATA_WRITE(mobj, kobj, offset, data, length);
  304 #endif  lint
  305         return proxy_data_write(MOBJ->xmm_pager, offset, data, length);
  306 }
  307 
  308 m_import_lock_completed(reply, offset, length)
  309         xmm_reply_t reply;
  310         vm_offset_t offset;
  311         vm_size_t length;
  312 {
  313         ipc_port_t reply_to = reply->reply_to;
  314 
  315 #ifdef  lint
  316         M_LOCK_COMPLETED(reply, offset, length);
  317 #endif  lint
  318         assert(reply->reply_to_type == MACH_MSG_TYPE_PORT_SEND_ONCE);
  319         xmm_reply_deallocate(reply);
  320         return proxy_lock_completed(reply_to, offset, length);
  321 }
  322 
  323 m_import_supply_completed(reply, offset, length, result, error_offset)
  324         xmm_reply_t reply;
  325         vm_offset_t offset;
  326         vm_size_t length;
  327         kern_return_t result;
  328         vm_offset_t error_offset;
  329 {
  330         ipc_port_t reply_to = reply->reply_to;
  331 
  332 #ifdef  lint
  333         M_SUPPLY_COMPLETED(reply, offset, length, result, error_offset);
  334 #endif  lint
  335         assert(reply->reply_to_type == MACH_MSG_TYPE_PORT_SEND_ONCE);
  336         xmm_reply_deallocate(reply);
  337         return proxy_supply_completed(reply_to, offset, length, result,
  338                                       error_offset);
  339 }
  340 
  341 m_import_data_return(mobj, kobj, offset, data, length)
  342         xmm_obj_t mobj;
  343         xmm_obj_t kobj;
  344         vm_offset_t offset;
  345         vm_offset_t data;
  346         vm_size_t length;
  347 {
  348 #ifdef  lint
  349         M_DATA_RETURN(mobj, kobj, offset, data, length);
  350 #endif  lint
  351         return proxy_data_return(MOBJ->xmm_pager, offset, data, length);
  352 }
  353 
  354 m_import_change_completed(reply, may_cache, copy_strategy)
  355         xmm_reply_t reply;
  356         boolean_t may_cache;
  357         memory_object_copy_strategy_t copy_strategy;
  358 {
  359         ipc_port_t reply_to = reply->reply_to;
  360 
  361 #ifdef  lint
  362         M_CHANGE_COMPLETED(reply, may_cache, copy_strategy);
  363 #endif  lint
  364         assert(reply->reply_to_type == MACH_MSG_TYPE_PORT_SEND_ONCE);
  365         xmm_reply_deallocate(reply);
  366         return proxy_change_completed(reply_to,  may_cache, copy_strategy);
  367 }
  368 
  369 xmm_obj_t
  370 convert_xmm_kernel_to_kobj(xmm_kernel)
  371         ipc_port_t xmm_kernel;
  372 {
  373         xmm_obj_t kobj = XMM_OBJ_NULL;
  374 
  375         if (IP_VALID(xmm_kernel)) {
  376                 ip_lock(xmm_kernel);
  377                 if (ip_active(xmm_kernel) &&
  378                     ip_kotype(xmm_kernel) == IKOT_XMM_KERNEL) {
  379                         kobj = (xmm_obj_t) xmm_kernel->ip_kobject;
  380                         xmm_obj_reference(kobj);
  381                 }
  382                 ip_unlock(xmm_kernel);
  383         }
  384         return kobj;
  385 }
  386 
  387 boolean_t
  388 xmm_kernel_notify(msg)
  389         mach_msg_header_t *msg;
  390 {
  391         return FALSE;
  392 }
  393 
  394 _proxy_data_unavailable(xmm_kernel, offset, length)
  395         ipc_port_t      xmm_kernel;
  396         vm_offset_t     offset;
  397         vm_size_t       length;
  398 {
  399         xmm_obj_t kobj;
  400         kern_return_t kr;
  401 
  402         kobj = convert_xmm_kernel_to_kobj(xmm_kernel);
  403         if (kobj == XMM_OBJ_NULL) {
  404                 return KERN_FAILURE;
  405         }
  406         kr = K_DATA_UNAVAILABLE(kobj, offset, length);
  407         xmm_obj_release(kobj);
  408         return kr;
  409 }
  410 
  411 _proxy_get_attributes(xmm_kernel, object_ready, may_cache, copy_strategy)
  412         ipc_port_t      xmm_kernel;
  413         boolean_t       *object_ready;
  414         boolean_t       *may_cache;
  415         memory_object_copy_strategy_t *copy_strategy;
  416 {
  417         xmm_obj_t kobj;
  418         kern_return_t kr;
  419 
  420         kobj = convert_xmm_kernel_to_kobj(xmm_kernel);
  421         if (kobj == XMM_OBJ_NULL) {
  422                 return KERN_FAILURE;
  423         }
  424         kr = K_GET_ATTRIBUTES(kobj, object_ready, may_cache, copy_strategy);
  425         xmm_obj_release(kobj);
  426         return kr;
  427 }
  428 
  429 _proxy_lock_request(xmm_kernel, offset, length, should_clean, should_flush,
  430                     prot, reply_to)
  431         ipc_port_t      xmm_kernel;
  432         vm_offset_t     offset;
  433         vm_size_t       length;
  434         boolean_t       should_clean;
  435         boolean_t       should_flush;
  436         vm_prot_t       prot;
  437         ipc_port_t      reply_to;
  438 {
  439         kern_return_t kr;
  440         xmm_reply_t reply;
  441         xmm_obj_t kobj;
  442 
  443         kobj = convert_xmm_kernel_to_kobj(xmm_kernel);
  444         if (kobj == XMM_OBJ_NULL) {
  445                 /*
  446                  * XXX  What about reply message???
  447                  * XXX  Printf here for now.
  448                  * XXX  Same thing goes for other calls that ask for replies.
  449                  */
  450                 printf("Rejecting proxy_lock_request on dead object\n");
  451                 return KERN_FAILURE;
  452         }
  453         kr = xmm_reply_allocate_send_once(kobj, reply_to, &reply);
  454         if (kr != KERN_SUCCESS) {
  455                 return kr;
  456         }
  457         kr = K_LOCK_REQUEST(kobj, offset, length, should_clean, should_flush,
  458                             prot, reply);
  459         xmm_obj_release(kobj);
  460         return kr;
  461 }
  462 
  463 _proxy_data_error(xmm_kernel, offset, length, error_value)
  464         ipc_port_t      xmm_kernel;
  465         vm_offset_t     offset;
  466         vm_size_t       length;
  467         kern_return_t   error_value;
  468 {
  469         xmm_obj_t kobj;
  470         kern_return_t kr;
  471 
  472         kobj = convert_xmm_kernel_to_kobj(xmm_kernel);
  473         if (kobj == XMM_OBJ_NULL) {
  474                 return KERN_FAILURE;
  475         }
  476         kr = K_DATA_ERROR(kobj, offset, length, error_value);
  477         xmm_obj_release(kobj);
  478         return kr;
  479 }
  480 
  481 _proxy_set_ready(xmm_kernel, xmm_pager, object_ready, may_cache, copy_strategy,
  482                  error_value, use_old_pageout, memory_object_name, reply_to)
  483         ipc_port_t      xmm_kernel;
  484         ipc_port_t      xmm_pager;
  485         boolean_t       object_ready;
  486         boolean_t       may_cache;
  487         memory_object_copy_strategy_t copy_strategy;
  488         kern_return_t   error_value;
  489         boolean_t       use_old_pageout;
  490         ipc_port_t      memory_object_name;
  491         ipc_port_t      reply_to;
  492 {
  493         xmm_obj_t kobj;
  494         xmm_reply_t reply;
  495         kern_return_t kr;
  496 
  497         if (error_value) {
  498                 /* destroy? or should export have done that? */
  499                 printf("proxy_set_ready loses\n");
  500         }
  501         kobj = convert_xmm_kernel_to_kobj(xmm_kernel);
  502         if (kobj == XMM_OBJ_NULL) {
  503                 return KERN_FAILURE;
  504         }
  505         KOBJ->xmm_pager = xmm_pager;
  506         if (KOBJ->terminated) {
  507                 /*
  508                  * Now that we have xmm_pager, we can process the
  509                  * pending m_import_terminate.
  510                  *
  511                  * XXX what should we do with reply?
  512                  */
  513                 printf("_proxy_set_ready on terminated 0x%x\n", kobj);
  514                 kr = m_import_terminate(kobj, kobj);
  515                 xmm_obj_release(kobj);
  516                 return kr;
  517         }
  518         kr = xmm_reply_allocate_send_once(kobj, reply_to, &reply);
  519         if (kr != KERN_SUCCESS) {
  520                 return kr;
  521         }
  522         kr = K_SET_READY(kobj, object_ready, may_cache, copy_strategy,
  523                          use_old_pageout, memory_object_name, reply);
  524         xmm_obj_release(kobj);
  525         return kr;
  526 }
  527 
  528 _proxy_destroy(xmm_kernel, reason)
  529         ipc_port_t      xmm_kernel;
  530         kern_return_t   reason;
  531 {
  532         xmm_obj_t kobj;
  533         kern_return_t kr;
  534 
  535         kobj = convert_xmm_kernel_to_kobj(xmm_kernel);
  536         if (kobj == XMM_OBJ_NULL) {
  537                 return KERN_FAILURE;
  538         }
  539         kr = K_DESTROY(kobj, reason);
  540         xmm_obj_release(kobj);
  541         return kr;
  542 }
  543 
  544 _proxy_data_supply(xmm_kernel, offset, data, length, lock_value, precious,
  545                    reply_to)
  546         ipc_port_t      xmm_kernel;
  547         vm_offset_t     offset;
  548         pointer_t       data;
  549         unsigned int    length;
  550         vm_prot_t       lock_value;
  551         ipc_port_t      reply_to;
  552 {
  553         xmm_obj_t kobj;
  554         xmm_reply_t reply;
  555         kern_return_t kr;
  556 
  557         kobj = convert_xmm_kernel_to_kobj(xmm_kernel);
  558         if (kobj == XMM_OBJ_NULL) {
  559                 return KERN_FAILURE;
  560         }
  561         kr = xmm_reply_allocate_send_once(kobj, reply_to, &reply);
  562         if (kr != KERN_SUCCESS) {
  563                 return kr;
  564         }
  565         kr = K_DATA_SUPPLY(kobj, offset, data, length, lock_value, precious,
  566                            reply);
  567         xmm_obj_release(kobj);
  568         return kr;
  569 }

Cache object: 3a84859720f0d76cf6c6ae8dbe555281


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]


This page is part of the FreeBSD/Linux Linux Kernel Cross-Reference, and was automatically generated using a modified version of the LXR engine.