diff options
Diffstat (limited to 'tools/python/xen/ext/xu/xu.c')
-rw-r--r-- | tools/python/xen/ext/xu/xu.c | 1386 |
1 files changed, 1386 insertions, 0 deletions
diff --git a/tools/python/xen/ext/xu/xu.c b/tools/python/xen/ext/xu/xu.c new file mode 100644 index 0000000000..48c975912d --- /dev/null +++ b/tools/python/xen/ext/xu/xu.c @@ -0,0 +1,1386 @@ +/****************************************************************************** + * utils.c + * + * Copyright (c) 2004, K A Fraser + */ + +#include <Python.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/ioctl.h> +#include <sys/types.h> +#include <sys/wait.h> +#include <sys/stat.h> +#include <sys/socket.h> +#include <sys/mman.h> +#include <sys/poll.h> +#include <netinet/in.h> +#include <fcntl.h> +#include <unistd.h> +#include <errno.h> +#include <signal.h> +#include <xc.h> + +#include <hypervisor-if.h> +#include "domain_controller.h" + +#include <asm-xen/proc_cmd.h> + +#define XENPKG "xen.ext.xu" + +/* Needed for Python versions earlier than 2.3. */ +#ifndef PyMODINIT_FUNC +#define PyMODINIT_FUNC DL_EXPORT(void) +#endif + +/* NB. The following should be kept in sync with the kernel's evtchn driver. */ +#define EVTCHN_DEV_NAME "/dev/xen/evtchn" +#define EVTCHN_DEV_MAJOR 10 +#define EVTCHN_DEV_MINOR 200 +#define PORT_NORMAL 0x0000 /* A standard event notification. */ +#define PORT_EXCEPTION 0x8000 /* An exceptional notification. */ +#define PORTIDX_MASK 0x7fff /* Strip subtype to obtain port index. */ +/* /dev/xen/evtchn ioctls: */ +/* EVTCHN_RESET: Clear and reinit the event buffer. Clear error condition. */ +#define EVTCHN_RESET _IO('E', 1) +/* EVTCHN_BIND: Bind to teh specified event-channel port. */ +#define EVTCHN_BIND _IO('E', 2) +/* EVTCHN_UNBIND: Unbind from the specified event-channel port. */ +#define EVTCHN_UNBIND _IO('E', 3) + +/* Size of a machine page frame. */ +#define PAGE_SIZE 4096 + + +/* + * *********************** NOTIFIER *********************** + */ + +typedef struct { + PyObject_HEAD; + int evtchn_fd; +} xu_notifier_object; + +static PyObject *xu_notifier_read(PyObject *self, PyObject *args) +{ + xu_notifier_object *xun = (xu_notifier_object *)self; + u16 v; + int bytes; + + if ( !PyArg_ParseTuple(args, "") ) + return NULL; + + while ( (bytes = read(xun->evtchn_fd, &v, sizeof(v))) == -1 ) + { + if ( errno == EINTR ) + continue; + if ( errno == EAGAIN ) + goto none; + return PyErr_SetFromErrno(PyExc_IOError); + } + + if ( bytes == sizeof(v) ) + return Py_BuildValue("(i,i)", v&PORTIDX_MASK, v&~PORTIDX_MASK); + + none: + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject *xu_notifier_unmask(PyObject *self, PyObject *args) +{ + xu_notifier_object *xun = (xu_notifier_object *)self; + u16 v; + int idx; + + if ( !PyArg_ParseTuple(args, "i", &idx) ) + return NULL; + + v = (u16)idx; + + (void)write(xun->evtchn_fd, &v, sizeof(v)); + + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject *xu_notifier_bind(PyObject *self, PyObject *args) +{ + xu_notifier_object *xun = (xu_notifier_object *)self; + int idx; + + if ( !PyArg_ParseTuple(args, "i", &idx) ) + return NULL; + + if ( ioctl(xun->evtchn_fd, EVTCHN_BIND, idx) != 0 ) + return PyErr_SetFromErrno(PyExc_IOError); + + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject *xu_notifier_unbind(PyObject *self, PyObject *args) +{ + xu_notifier_object *xun = (xu_notifier_object *)self; + int idx; + + if ( !PyArg_ParseTuple(args, "i", &idx) ) + return NULL; + + if ( ioctl(xun->evtchn_fd, EVTCHN_UNBIND, idx) != 0 ) + return PyErr_SetFromErrno(PyExc_IOError); + + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject *xu_notifier_fileno(PyObject *self, PyObject *args) +{ + xu_notifier_object *xun = (xu_notifier_object *)self; + return PyInt_FromLong(xun->evtchn_fd); +} + +static PyMethodDef xu_notifier_methods[] = { + { "read", + (PyCFunction)xu_notifier_read, + METH_VARARGS, + "Read a (@port, @type) pair.\n" }, + + { "unmask", + (PyCFunction)xu_notifier_unmask, + METH_VARARGS, + "Unmask notifications for a @port.\n" }, + + { "bind", + (PyCFunction)xu_notifier_bind, + METH_VARARGS, + "Get notifications for a @port.\n" }, + + { "unbind", + (PyCFunction)xu_notifier_unbind, + METH_VARARGS, + "No longer get notifications for a @port.\n" }, + + { "fileno", + (PyCFunction)xu_notifier_fileno, + METH_VARARGS, + "Return the file descriptor for the notification channel.\n" }, + + { NULL, NULL, 0, NULL } +}; + +staticforward PyTypeObject xu_notifier_type; + +static PyObject *xu_notifier_new(PyObject *self, PyObject *args) +{ + xu_notifier_object *xun; + + if ( !PyArg_ParseTuple(args, "") ) + return NULL; + + xun = PyObject_New(xu_notifier_object, &xu_notifier_type); + + reopen: + xun->evtchn_fd = open(EVTCHN_DEV_NAME, O_NONBLOCK|O_RDWR); + if ( xun->evtchn_fd == -1 ) + { + if ( (errno == ENOENT) && + ((mkdir("/dev/xen", 0755) == 0) || (errno == EEXIST)) && + (mknod(EVTCHN_DEV_NAME, S_IFCHR|0600, + (EVTCHN_DEV_MAJOR << 8) | EVTCHN_DEV_MINOR) == 0) ) + goto reopen; + PyObject_Del((PyObject *)xun); + return PyErr_SetFromErrno(PyExc_IOError); + } + + return (PyObject *)xun; +} + +static PyObject *xu_notifier_getattr(PyObject *obj, char *name) +{ + if ( strcmp(name, "EXCEPTION") == 0 ) + return PyInt_FromLong(PORT_EXCEPTION); + if ( strcmp(name, "NORMAL") == 0 ) + return PyInt_FromLong(PORT_NORMAL); + return Py_FindMethod(xu_notifier_methods, obj, name); +} + +static void xu_notifier_dealloc(PyObject *self) +{ + xu_notifier_object *xun = (xu_notifier_object *)self; + (void)close(xun->evtchn_fd); + PyObject_Del(self); +} + +static PyTypeObject xu_notifier_type = { + PyObject_HEAD_INIT(&PyType_Type) + 0, + "notifier", + sizeof(xu_notifier_object), + 0, + xu_notifier_dealloc, /* tp_dealloc */ + NULL, /* tp_print */ + xu_notifier_getattr, /* tp_getattr */ + NULL, /* tp_setattr */ + NULL, /* tp_compare */ + NULL, /* tp_repr */ + NULL, /* tp_as_number */ + NULL, /* tp_as_sequence */ + NULL, /* tp_as_mapping */ + NULL /* tp_hash */ +}; + + + +/* + * *********************** MESSAGE *********************** + */ + +#define TYPE(_x,_y) (((_x)<<8)|(_y)) +#define P2C(_struct, _field, _ctype) \ + do { \ + PyObject *obj; \ + if ( (obj = PyDict_GetItemString(payload, #_field)) != NULL ) \ + { \ + if ( PyInt_Check(obj) ) \ + { \ + ((_struct *)&xum->msg.msg[0])->_field = \ + (_ctype)PyInt_AsLong(obj); \ + dict_items_parsed++; \ + } \ + else if ( PyLong_Check(obj) ) \ + { \ + ((_struct *)&xum->msg.msg[0])->_field = \ + (_ctype)PyLong_AsUnsignedLongLong(obj); \ + dict_items_parsed++; \ + } \ + } \ + xum->msg.length = sizeof(_struct); \ + } while ( 0 ) +#define C2P(_struct, _field, _pytype, _ctype) \ + do { \ + PyObject *obj = Py ## _pytype ## _From ## _ctype \ + (((_struct *)&xum->msg.msg[0])->_field); \ + if ( dict == NULL ) dict = PyDict_New(); \ + PyDict_SetItemString(dict, #_field, obj); \ + } while ( 0 ) + +typedef struct { + PyObject_HEAD; + control_msg_t msg; +} xu_message_object; + +static PyObject *xu_message_append_payload(PyObject *self, PyObject *args) +{ + xu_message_object *xum = (xu_message_object *)self; + char *str; + int len; + + if ( !PyArg_ParseTuple(args, "s#", &str, &len) ) + return NULL; + + if ( (len + xum->msg.length) > sizeof(xum->msg.msg) ) + { + PyErr_SetString(PyExc_RuntimeError, "out of space in control message"); + return NULL; + } + + memcpy(&xum->msg.msg[xum->msg.length], str, len); + xum->msg.length += len; + + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject *xu_message_set_response_fields(PyObject *self, PyObject *args) +{ + xu_message_object *xum = (xu_message_object *)self; + PyObject *payload; + int dict_items_parsed = 0; + + if ( !PyArg_ParseTuple(args, "O", &payload) ) + return NULL; + + if ( !PyDict_Check(payload) ) + { + PyErr_SetString(PyExc_TypeError, "payload is not a dictionary"); + return NULL; + } + + switch ( TYPE(xum->msg.type, xum->msg.subtype) ) + { + case TYPE(CMSG_BLKIF_FE, CMSG_BLKIF_FE_DRIVER_STATUS_CHANGED): + P2C(blkif_fe_driver_status_changed_t, nr_interfaces, u32); + break; + case TYPE(CMSG_NETIF_FE, CMSG_NETIF_FE_DRIVER_STATUS_CHANGED): + P2C(netif_fe_driver_status_changed_t, nr_interfaces, u32); + break; + } + + if ( dict_items_parsed != PyDict_Size(payload) ) + { + PyErr_SetString(PyExc_TypeError, "payload contains bad items"); + return NULL; + } + + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject *xu_message_get_payload(PyObject *self, PyObject *args) +{ + xu_message_object *xum = (xu_message_object *)self; + PyObject *dict = NULL; + + if ( !PyArg_ParseTuple(args, "") ) + return NULL; + + switch ( TYPE(xum->msg.type, xum->msg.subtype) ) + { + case TYPE(CMSG_BLKIF_FE, CMSG_BLKIF_FE_INTERFACE_STATUS_CHANGED): + C2P(blkif_fe_interface_status_changed_t, handle, Int, Long); + C2P(blkif_fe_interface_status_changed_t, status, Int, Long); + C2P(blkif_fe_interface_status_changed_t, evtchn, Int, Long); + return dict; + case TYPE(CMSG_BLKIF_FE, CMSG_BLKIF_FE_DRIVER_STATUS_CHANGED): + C2P(blkif_fe_driver_status_changed_t, status, Int, Long); + return dict; + case TYPE(CMSG_BLKIF_FE, CMSG_BLKIF_FE_INTERFACE_CONNECT): + C2P(blkif_fe_interface_connect_t, handle, Int, Long); + C2P(blkif_fe_interface_connect_t, shmem_frame, Int, Long); + return dict; + case TYPE(CMSG_BLKIF_FE, CMSG_BLKIF_FE_INTERFACE_DISCONNECT): + C2P(blkif_fe_interface_disconnect_t, handle, Int, Long); + return dict; + case TYPE(CMSG_BLKIF_BE, CMSG_BLKIF_BE_CREATE): + C2P(blkif_be_create_t, domid, Int, Long); + C2P(blkif_be_create_t, blkif_handle, Int, Long); + C2P(blkif_be_create_t, status, Int, Long); + return dict; + case TYPE(CMSG_BLKIF_BE, CMSG_BLKIF_BE_DESTROY): + C2P(blkif_be_destroy_t, domid, Int, Long); + C2P(blkif_be_destroy_t, blkif_handle, Int, Long); + C2P(blkif_be_destroy_t, status, Int, Long); + return dict; + case TYPE(CMSG_BLKIF_BE, CMSG_BLKIF_BE_CONNECT): + C2P(blkif_be_connect_t, domid, Int, Long); + C2P(blkif_be_connect_t, blkif_handle, Int, Long); + C2P(blkif_be_connect_t, shmem_frame, Int, Long); + C2P(blkif_be_connect_t, evtchn, Int, Long); + C2P(blkif_be_connect_t, status, Int, Long); + return dict; + case TYPE(CMSG_BLKIF_BE, CMSG_BLKIF_BE_DISCONNECT): + C2P(blkif_be_disconnect_t, domid, Int, Long); + C2P(blkif_be_disconnect_t, blkif_handle, Int, Long); + C2P(blkif_be_disconnect_t, status, Int, Long); + return dict; + case TYPE(CMSG_BLKIF_BE, CMSG_BLKIF_BE_VBD_CREATE): + C2P(blkif_be_vbd_create_t, domid, Int, Long); + C2P(blkif_be_vbd_create_t, blkif_handle, Int, Long); + C2P(blkif_be_vbd_create_t, vdevice, Int, Long); + C2P(blkif_be_vbd_create_t, readonly, Int, Long); + C2P(blkif_be_vbd_create_t, status, Int, Long); + return dict; + case TYPE(CMSG_BLKIF_BE, CMSG_BLKIF_BE_VBD_DESTROY): + C2P(blkif_be_vbd_destroy_t, domid, Int, Long); + C2P(blkif_be_vbd_destroy_t, blkif_handle, Int, Long); + C2P(blkif_be_vbd_destroy_t, vdevice, Int, Long); + C2P(blkif_be_vbd_destroy_t, status, Int, Long); + return dict; + case TYPE(CMSG_BLKIF_BE, CMSG_BLKIF_BE_VBD_GROW): + C2P(blkif_be_vbd_grow_t, domid, Int, Long); + C2P(blkif_be_vbd_grow_t, blkif_handle, Int, Long); + C2P(blkif_be_vbd_grow_t, vdevice, Int, Long); + C2P(blkif_be_vbd_grow_t, extent.sector_start, + Long, UnsignedLongLong); + C2P(blkif_be_vbd_grow_t, extent.sector_length, + Long, UnsignedLongLong); + C2P(blkif_be_vbd_grow_t, extent.device, Int, Long); + C2P(blkif_be_vbd_grow_t, status, Int, Long); + return dict; + case TYPE(CMSG_BLKIF_BE, CMSG_BLKIF_BE_VBD_SHRINK): + C2P(blkif_be_vbd_shrink_t, domid, Int, Long); + C2P(blkif_be_vbd_shrink_t, blkif_handle, Int, Long); + C2P(blkif_be_vbd_shrink_t, vdevice, Int, Long); + C2P(blkif_be_vbd_shrink_t, status, Int, Long); + return dict; + case TYPE(CMSG_BLKIF_BE, CMSG_BLKIF_BE_DRIVER_STATUS_CHANGED): + C2P(blkif_be_driver_status_changed_t, status, Int, Long); + return dict; + case TYPE(CMSG_NETIF_FE, CMSG_NETIF_FE_INTERFACE_STATUS_CHANGED): + C2P(netif_fe_interface_status_changed_t, handle, Int, Long); + C2P(netif_fe_interface_status_changed_t, status, Int, Long); + C2P(netif_fe_interface_status_changed_t, evtchn, Int, Long); + return dict; + case TYPE(CMSG_NETIF_FE, CMSG_NETIF_FE_DRIVER_STATUS_CHANGED): + C2P(netif_fe_driver_status_changed_t, status, Int, Long); + C2P(netif_fe_driver_status_changed_t, nr_interfaces, Int, Long); + return dict; + case TYPE(CMSG_NETIF_FE, CMSG_NETIF_FE_INTERFACE_CONNECT): + C2P(netif_fe_interface_connect_t, handle, Int, Long); + C2P(netif_fe_interface_connect_t, tx_shmem_frame, Int, Long); + C2P(netif_fe_interface_connect_t, rx_shmem_frame, Int, Long); + return dict; + case TYPE(CMSG_NETIF_FE, CMSG_NETIF_FE_INTERFACE_DISCONNECT): + C2P(netif_fe_interface_disconnect_t, handle, Int, Long); + return dict; + case TYPE(CMSG_NETIF_BE, CMSG_NETIF_BE_CREATE): + C2P(netif_be_create_t, domid, Int, Long); + C2P(netif_be_create_t, netif_handle, Int, Long); + C2P(netif_be_create_t, status, Int, Long); + return dict; + case TYPE(CMSG_NETIF_BE, CMSG_NETIF_BE_DESTROY): + C2P(netif_be_destroy_t, domid, Int, Long); + C2P(netif_be_destroy_t, netif_handle, Int, Long); + C2P(netif_be_destroy_t, status, Int, Long); + return dict; + case TYPE(CMSG_NETIF_BE, CMSG_NETIF_BE_CONNECT): + C2P(netif_be_connect_t, domid, Int, Long); + C2P(netif_be_connect_t, netif_handle, Int, Long); + C2P(netif_be_connect_t, tx_shmem_frame, Int, Long); + C2P(netif_be_connect_t, rx_shmem_frame, Int, Long); + C2P(netif_be_connect_t, evtchn, Int, Long); + C2P(netif_be_connect_t, status, Int, Long); + return dict; + case TYPE(CMSG_NETIF_BE, CMSG_NETIF_BE_DISCONNECT): + C2P(netif_be_disconnect_t, domid, Int, Long); + C2P(netif_be_disconnect_t, netif_handle, Int, Long); + C2P(netif_be_disconnect_t, status, Int, Long); + return dict; + case TYPE(CMSG_NETIF_BE, CMSG_NETIF_BE_DRIVER_STATUS_CHANGED): + C2P(netif_be_driver_status_changed_t, status, Int, Long); + return dict; + } + + return PyString_FromStringAndSize(xum->msg.msg, xum->msg.length); +} + +static PyObject *xu_message_get_header(PyObject *self, PyObject *args) +{ + xu_message_object *xum = (xu_message_object *)self; + + if ( !PyArg_ParseTuple(args, "") ) + return NULL; + + return Py_BuildValue("{s:i,s:i,s:i}", + "type", xum->msg.type, + "subtype", xum->msg.subtype, + "id", xum->msg.id); +} + +static PyMethodDef xu_message_methods[] = { + { "append_payload", + (PyCFunction)xu_message_append_payload, + METH_VARARGS, + "Append @str to the message payload.\n" }, + + { "set_response_fields", + (PyCFunction)xu_message_set_response_fields, + METH_VARARGS, + "Fill in the response fields in a message that was passed to us.\n" }, + + { "get_payload", + (PyCFunction)xu_message_get_payload, + METH_VARARGS, + "Return the message payload in string form.\n" }, + + { "get_header", + (PyCFunction)xu_message_get_header, + METH_VARARGS, + "Returns a dictionary of values for @type, @subtype, and @id.\n" }, + + { NULL, NULL, 0, NULL } +}; + +staticforward PyTypeObject xu_message_type; + +static PyObject *xu_message_new(PyObject *self, PyObject *args) +{ + xu_message_object *xum; + int type, subtype, id, dict_items_parsed = 0; + PyObject *payload = NULL; + + if ( !PyArg_ParseTuple(args, "iii|O", &type, &subtype, &id, &payload) ) + return NULL; + + xum = PyObject_New(xu_message_object, &xu_message_type); + + xum->msg.type = type; + xum->msg.subtype = subtype; + xum->msg.id = id; + xum->msg.length = 0; + + if ( payload == NULL ) + return (PyObject *)xum; + + if ( !PyDict_Check(payload) ) + { + PyErr_SetString(PyExc_TypeError, "payload is not a dictionary"); + PyObject_Del((PyObject *)xum); + return NULL; + } + + switch ( TYPE(type, subtype) ) + { + case TYPE(CMSG_BLKIF_FE, CMSG_BLKIF_FE_INTERFACE_STATUS_CHANGED): + P2C(blkif_fe_interface_status_changed_t, handle, u32); + P2C(blkif_fe_interface_status_changed_t, status, u32); + P2C(blkif_fe_interface_status_changed_t, evtchn, u16); + break; + case TYPE(CMSG_BLKIF_BE, CMSG_BLKIF_BE_CREATE): + P2C(blkif_be_create_t, domid, u32); + P2C(blkif_be_create_t, blkif_handle, u32); + break; + case TYPE(CMSG_BLKIF_BE, CMSG_BLKIF_BE_DESTROY): + P2C(blkif_be_destroy_t, domid, u32); + P2C(blkif_be_destroy_t, blkif_handle, u32); + break; + case TYPE(CMSG_BLKIF_BE, CMSG_BLKIF_BE_CONNECT): + P2C(blkif_be_connect_t, domid, u32); + P2C(blkif_be_connect_t, blkif_handle, u32); + P2C(blkif_be_connect_t, shmem_frame, memory_t); + P2C(blkif_be_connect_t, evtchn, u16); + break; + case TYPE(CMSG_BLKIF_BE, CMSG_BLKIF_BE_DISCONNECT): + P2C(blkif_be_disconnect_t, domid, u32); + P2C(blkif_be_disconnect_t, blkif_handle, u32); + break; + case TYPE(CMSG_BLKIF_BE, CMSG_BLKIF_BE_VBD_CREATE): + P2C(blkif_be_vbd_create_t, domid, u32); + P2C(blkif_be_vbd_create_t, blkif_handle, u32); + P2C(blkif_be_vbd_create_t, vdevice, blkif_vdev_t); + P2C(blkif_be_vbd_create_t, readonly, u16); + break; + case TYPE(CMSG_BLKIF_BE, CMSG_BLKIF_BE_VBD_DESTROY): + P2C(blkif_be_vbd_destroy_t, domid, u32); + P2C(blkif_be_vbd_destroy_t, blkif_handle, u32); + P2C(blkif_be_vbd_destroy_t, vdevice, blkif_vdev_t); + break; + case TYPE(CMSG_BLKIF_BE, CMSG_BLKIF_BE_VBD_GROW): + P2C(blkif_be_vbd_grow_t, domid, u32); + P2C(blkif_be_vbd_grow_t, blkif_handle, u32); + P2C(blkif_be_vbd_grow_t, vdevice, blkif_vdev_t); + P2C(blkif_be_vbd_grow_t, extent.sector_start, blkif_sector_t); + P2C(blkif_be_vbd_grow_t, extent.sector_length, blkif_sector_t); + P2C(blkif_be_vbd_grow_t, extent.device, blkif_pdev_t); + break; + case TYPE(CMSG_BLKIF_BE, CMSG_BLKIF_BE_VBD_SHRINK): + P2C(blkif_be_vbd_shrink_t, domid, u32); + P2C(blkif_be_vbd_shrink_t, blkif_handle, u32); + P2C(blkif_be_vbd_shrink_t, vdevice, blkif_vdev_t); + break; + case TYPE(CMSG_NETIF_FE, CMSG_NETIF_FE_INTERFACE_STATUS_CHANGED): + P2C(netif_fe_interface_status_changed_t, handle, u32); + P2C(netif_fe_interface_status_changed_t, status, u32); + P2C(netif_fe_interface_status_changed_t, evtchn, u16); + P2C(netif_fe_interface_status_changed_t, mac[0], u8); + P2C(netif_fe_interface_status_changed_t, mac[1], u8); + P2C(netif_fe_interface_status_changed_t, mac[2], u8); + P2C(netif_fe_interface_status_changed_t, mac[3], u8); + P2C(netif_fe_interface_status_changed_t, mac[4], u8); + P2C(netif_fe_interface_status_changed_t, mac[5], u8); + break; + case TYPE(CMSG_NETIF_BE, CMSG_NETIF_BE_CREATE): + P2C(netif_be_create_t, domid, u32); + P2C(netif_be_create_t, netif_handle, u32); + P2C(netif_be_create_t, mac[0], u8); + P2C(netif_be_create_t, mac[1], u8); + P2C(netif_be_create_t, mac[2], u8); + P2C(netif_be_create_t, mac[3], u8); + P2C(netif_be_create_t, mac[4], u8); + P2C(netif_be_create_t, mac[5], u8); + break; + case TYPE(CMSG_NETIF_BE, CMSG_NETIF_BE_DESTROY): + P2C(netif_be_destroy_t, domid, u32); + P2C(netif_be_destroy_t, netif_handle, u32); + break; + case TYPE(CMSG_NETIF_BE, CMSG_NETIF_BE_CONNECT): + P2C(netif_be_connect_t, domid, u32); + P2C(netif_be_connect_t, netif_handle, u32); + P2C(netif_be_connect_t, tx_shmem_frame, memory_t); + P2C(netif_be_connect_t, rx_shmem_frame, memory_t); + P2C(netif_be_connect_t, evtchn, u16); + break; + case TYPE(CMSG_NETIF_BE, CMSG_NETIF_BE_DISCONNECT): + P2C(netif_be_disconnect_t, domid, u32); + P2C(netif_be_disconnect_t, netif_handle, u32); + break; + case TYPE(CMSG_NETIF_FE, CMSG_NETIF_FE_DRIVER_STATUS_CHANGED): + P2C(netif_fe_driver_status_changed_t, status, u32); + P2C(netif_fe_driver_status_changed_t, nr_interfaces, u32); + break; + } + + if ( dict_items_parsed != PyDict_Size(payload) ) + { + PyErr_SetString(PyExc_TypeError, "payload contains bad items"); + PyObject_Del((PyObject *)xum); + return NULL; + } + + return (PyObject *)xum; +} + +static PyObject *xu_message_getattr(PyObject *obj, char *name) +{ + xu_message_object *xum; + if ( strcmp(name, "MAX_PAYLOAD") == 0 ) + return PyInt_FromLong(sizeof(xum->msg.msg)); + return Py_FindMethod(xu_message_methods, obj, name); +} + +static void xu_message_dealloc(PyObject *self) +{ + PyObject_Del(self); +} + +static PyTypeObject xu_message_type = { + PyObject_HEAD_INIT(&PyType_Type) + 0, + "message", + sizeof(xu_message_object), + 0, + xu_message_dealloc, /* tp_dealloc */ + NULL, /* tp_print */ + xu_message_getattr, /* tp_getattr */ + NULL, /* tp_setattr */ + NULL, /* tp_compare */ + NULL, /* tp_repr */ + NULL, /* tp_as_number */ + NULL, /* tp_as_sequence */ + NULL, /* tp_as_mapping */ + NULL /* tp_hash */ +}; + + + +/* + * *********************** PORT *********************** + */ + +static control_if_t *map_control_interface(int fd, unsigned long pfn) +{ + char *vaddr = mmap(NULL, PAGE_SIZE, PROT_READ|PROT_WRITE, + MAP_SHARED, fd, pfn * PAGE_SIZE); + if ( vaddr == MAP_FAILED ) + return NULL; + return (control_if_t *)(vaddr + 2048); +} +static void unmap_control_interface(int fd, control_if_t *c) +{ + char *vaddr = (char *)c - 2048; + (void)munmap(vaddr, PAGE_SIZE); +} + +typedef struct xu_port_object { + PyObject_HEAD; + int mem_fd; + int xc_handle; + u32 remote_dom; + int local_port, remote_port; + control_if_t *interface; + CONTROL_RING_IDX tx_req_cons, tx_resp_prod; + CONTROL_RING_IDX rx_req_prod, rx_resp_cons; +} xu_port_object; + +static PyObject *port_error; + +static int xup_connect(xu_port_object *xup, domid_t dom, + int local_port, int remote_port){ + // From our prespective rx = producer, tx = consumer. + int err = 0; + printf("%s> dom=%u %d:%d\n", __FUNCTION__, (unsigned int)dom, + local_port, remote_port); + + // Consumer = tx. + //xup->interface->tx_resp_prod = 0; + //xup->interface->tx_req_prod = 0; + xup->tx_resp_prod = xup->interface->tx_resp_prod; + xup->tx_req_cons = xup->interface->tx_resp_prod; + printf("%s> tx: %u %u : %u %u\n", __FUNCTION__, + (unsigned int)xup->interface->tx_resp_prod, + (unsigned int)xup->tx_resp_prod, + (unsigned int)xup->tx_req_cons, + (unsigned int)xup->interface->tx_req_prod); + + // Producer = rx. + //xup->interface->rx_req_prod = 0; + //xup->interface->rx_resp_prod = 0; + xup->rx_req_prod = xup->interface->rx_req_prod; + xup->rx_resp_cons = xup->interface->rx_resp_prod; + printf("%s> rx: %u %u : %u %u\n", __FUNCTION__, + (unsigned int)xup->rx_resp_cons, + (unsigned int)xup->interface->rx_resp_prod, + (unsigned int)xup->interface->rx_req_prod, + (unsigned int)xup->rx_req_prod); + + xup->remote_dom = dom; + xup->local_port = local_port; + xup->remote_port = remote_port; + + printf("%s< err=%d\n", __FUNCTION__, err); + return err; +} + +static PyObject *xu_port_notify(PyObject *self, PyObject *args) +{ + xu_port_object *xup = (xu_port_object *)self; + + if ( !PyArg_ParseTuple(args, "") ) + return NULL; + + (void)xc_evtchn_send(xup->xc_handle, xup->local_port); + + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject *xu_port_read_request(PyObject *self, PyObject *args) +{ + xu_port_object *xup = (xu_port_object *)self; + xu_message_object *xum; + CONTROL_RING_IDX c = xup->tx_req_cons; + control_if_t *cif = xup->interface; + control_msg_t *cmsg; + + if ( !PyArg_ParseTuple(args, "") ) + return NULL; + + if ( (c == cif->tx_req_prod) || + ((c - xup->tx_resp_prod) == CONTROL_RING_SIZE) ) + { + PyErr_SetString(port_error, "no request to read"); + return NULL; + } + + cmsg = &cif->tx_ring[MASK_CONTROL_IDX(c)]; + xum = PyObject_New(xu_message_object, &xu_message_type); + memcpy(&xum->msg, cmsg, sizeof(*cmsg)); + if ( xum->msg.length > sizeof(xum->msg.msg) ) + xum->msg.length = sizeof(xum->msg.msg); + xup->tx_req_cons++; + return (PyObject *)xum; +} + +static PyObject *xu_port_write_request(PyObject *self, PyObject *args) +{ + xu_port_object *xup = (xu_port_object *)self; + xu_message_object *xum; + CONTROL_RING_IDX p = xup->rx_req_prod; + control_if_t *cif = xup->interface; + control_msg_t *cmsg; + + if ( !PyArg_ParseTuple(args, "O", (PyObject **)&xum) ) + return NULL; + + if ( !PyObject_TypeCheck((PyObject *)xum, &xu_message_type) ) + { + PyErr_SetString(PyExc_TypeError, "expected a " XENPKG ".message"); + return NULL; + } + + if ( ((p - xup->rx_resp_cons) == CONTROL_RING_SIZE) ) + { + PyErr_SetString(port_error, "no space to write request"); + return NULL; + } + + cmsg = &cif->rx_ring[MASK_CONTROL_IDX(p)]; + memcpy(cmsg, &xum->msg, sizeof(*cmsg)); + + xup->rx_req_prod = cif->rx_req_prod = p + 1; + + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject *xu_port_read_response(PyObject *self, PyObject *args) +{ + xu_port_object *xup = (xu_port_object *)self; + xu_message_object *xum; + CONTROL_RING_IDX c = xup->rx_resp_cons; + control_if_t *cif = xup->interface; + control_msg_t *cmsg; + + if ( !PyArg_ParseTuple(args, "") ) + return NULL; + + if ( (c == cif->rx_resp_prod) || (c == xup->rx_req_prod) ) + { + PyErr_SetString(port_error, "no response to read"); + return NULL; + } + + cmsg = &cif->rx_ring[MASK_CONTROL_IDX(c)]; + xum = PyObject_New(xu_message_object, &xu_message_type); + memcpy(&xum->msg, cmsg, sizeof(*cmsg)); + if ( xum->msg.length > sizeof(xum->msg.msg) ) + xum->msg.length = sizeof(xum->msg.msg); + xup->rx_resp_cons++; + return (PyObject *)xum; +} + +static PyObject *xu_port_write_response(PyObject *self, PyObject *args) +{ + xu_port_object *xup = (xu_port_object *)self; + xu_message_object *xum; + CONTROL_RING_IDX p = xup->tx_resp_prod; + control_if_t *cif = xup->interface; + control_msg_t *cmsg; + + if ( !PyArg_ParseTuple(args, "O", (PyObject **)&xum) ) + return NULL; + + if ( !PyObject_TypeCheck((PyObject *)xum, &xu_message_type) ) + { + PyErr_SetString(PyExc_TypeError, "expected a " XENPKG ".message"); + return NULL; + } + + if ( p == xup->tx_req_cons ) + { + PyErr_SetString(port_error, "no space to write response"); + return NULL; + } + + cmsg = &cif->tx_ring[MASK_CONTROL_IDX(p)]; + memcpy(cmsg, &xum->msg, sizeof(*cmsg)); + + xup->tx_resp_prod = cif->tx_resp_prod = p + 1; + + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject *xu_port_request_to_read(PyObject *self, PyObject *args) +{ + xu_port_object *xup = (xu_port_object *)self; + CONTROL_RING_IDX c = xup->tx_req_cons; + control_if_t *cif = xup->interface; + + if ( !PyArg_ParseTuple(args, "") ) + return NULL; + + if ( (c == cif->tx_req_prod) || + ((c - xup->tx_resp_prod) == CONTROL_RING_SIZE) ) + return PyInt_FromLong(0); + + return PyInt_FromLong(1); +} + +static PyObject *xu_port_space_to_write_request(PyObject *self, PyObject *args) +{ + xu_port_object *xup = (xu_port_object *)self; + CONTROL_RING_IDX p = xup->rx_req_prod; + + if ( !PyArg_ParseTuple(args, "") ) + return NULL; + + if ( ((p - xup->rx_resp_cons) == CONTROL_RING_SIZE) ) + return PyInt_FromLong(0); + + return PyInt_FromLong(1); +} + +static PyObject *xu_port_response_to_read(PyObject *self, PyObject *args) +{ + xu_port_object *xup = (xu_port_object *)self; + CONTROL_RING_IDX c = xup->rx_resp_cons; + control_if_t *cif = xup->interface; + + if ( !PyArg_ParseTuple(args, "") ) + return NULL; + + if ( (c == cif->rx_resp_prod) || (c == xup->rx_req_prod) ) + return PyInt_FromLong(0); + + return PyInt_FromLong(1); +} + +static PyObject *xu_port_space_to_write_response( + PyObject *self, PyObject *args) +{ + xu_port_object *xup = (xu_port_object *)self; + CONTROL_RING_IDX p = xup->tx_resp_prod; + + if ( !PyArg_ParseTuple(args, "") ) + return NULL; + + if ( p == xup->tx_req_cons ) + return PyInt_FromLong(0); + + return PyInt_FromLong(1); +} + +static PyMethodDef xu_port_methods[] = { + { "notify", + (PyCFunction)xu_port_notify, + METH_VARARGS, + "Send a notification to the remote end.\n" }, + + { "read_request", + (PyCFunction)xu_port_read_request, + METH_VARARGS, + "Read a request message from the control interface.\n" }, + + { "write_request", + (PyCFunction)xu_port_write_request, + METH_VARARGS, + "Write a request message to the control interface.\n" }, + + { "read_response", + (PyCFunction)xu_port_read_response, + METH_VARARGS, + "Read a response message from the control interface.\n" }, + + { "write_response", + (PyCFunction)xu_port_write_response, + METH_VARARGS, + "Write a response message to the control interface.\n" }, + + { "request_to_read", + (PyCFunction)xu_port_request_to_read, + METH_VARARGS, + "Returns TRUE if there is a request message to read.\n" }, + + { "space_to_write_request", + (PyCFunction)xu_port_space_to_write_request, + METH_VARARGS, + "Returns TRUE if there is space to write a request message.\n" }, + + { "response_to_read", + (PyCFunction)xu_port_response_to_read, + METH_VARARGS, + "Returns TRUE if there is a response message to read.\n" }, + + { "space_to_write_response", + (PyCFunction)xu_port_space_to_write_response, + METH_VARARGS, + "Returns TRUE if there is space to write a response message.\n" }, + + { NULL, NULL, 0, NULL } +}; + +staticforward PyTypeObject xu_port_type; + +static PyObject *xu_port_new(PyObject *self, PyObject *args) +{ + xu_port_object *xup; + u32 dom; + int port1, port2; + xc_dominfo_t info; + + if ( !PyArg_ParseTuple(args, "i", &dom) ) + return NULL; + + xup = PyObject_New(xu_port_object, &xu_port_type); + + if ( (xup->mem_fd = open("/dev/mem", O_RDWR)) == -1 ) + { + PyErr_SetString(port_error, "Could not open '/dev/mem'"); + goto fail1; + } + + /* Set the General-Purpose Subject whose page frame will be mapped. */ + (void)ioctl(xup->mem_fd, _IO('M', 1), (unsigned long)dom); + + if ( (xup->xc_handle = xc_interface_open()) == -1 ) + { + PyErr_SetString(port_error, "Could not open Xen control interface"); + goto fail2; + } + + if ( dom == 0 ) + { + /* + * The control-interface event channel for DOM0 is already set up. + * We use an ioctl to discover the port at our end of the channel. + */ + port1 = ioctl(xup->xc_handle, IOCTL_PRIVCMD_INITDOMAIN_EVTCHN, NULL); + port2 = -1; /* We don't need the remote end of the DOM0 link. */ + if ( port1 < 0 ) + { + PyErr_SetString(port_error, "Could not open channel to DOM0"); + goto fail3; + } + } + else if ( xc_evtchn_bind_interdomain(xup->xc_handle, + DOMID_SELF, dom, + &port1, &port2) != 0 ) + { + PyErr_SetString(port_error, "Could not open channel to domain"); + goto fail3; + } + + if ( (xc_domain_getinfo(xup->xc_handle, dom, 1, &info) != 1) || + (info.domid != dom) ) + { + PyErr_SetString(port_error, "Failed to obtain domain status"); + goto fail4; + } + + xup->interface = + map_control_interface(xup->mem_fd, info.shared_info_frame); + if ( xup->interface == NULL ) + { + PyErr_SetString(port_error, "Failed to map domain control interface"); + goto fail4; + } + + xup_connect(xup, dom, port1, port2); + return (PyObject *)xup; + + + fail4: + (void)xc_evtchn_close(xup->xc_handle, DOMID_SELF, port1); + fail3: + (void)xc_interface_close(xup->xc_handle); + fail2: + (void)close(xup->mem_fd); + fail1: + PyObject_Del((PyObject *)xup); + return NULL; +} + +static PyObject *xu_port_getattr(PyObject *obj, char *name) +{ + xu_port_object *xup = (xu_port_object *)obj; + if ( strcmp(name, "local_port") == 0 ) + return PyInt_FromLong(xup->local_port); + if ( strcmp(name, "remote_port") == 0 ) + return PyInt_FromLong(xup->remote_port); + if ( strcmp(name, "remote_dom") == 0 ) + return PyInt_FromLong(xup->remote_dom); + return Py_FindMethod(xu_port_methods, obj, name); +} + +static void xu_port_dealloc(PyObject *self) +{ + xu_port_object *xup = (xu_port_object *)self; + unmap_control_interface(xup->mem_fd, xup->interface); + if ( xup->remote_dom != 0 ) + (void)xc_evtchn_close(xup->xc_handle, DOMID_SELF, xup->local_port); + (void)xc_interface_close(xup->xc_handle); + (void)close(xup->mem_fd); + PyObject_Del(self); +} + +static PyTypeObject xu_port_type = { + PyObject_HEAD_INIT(&PyType_Type) + 0, + "port", + sizeof(xu_port_object), + 0, + xu_port_dealloc, /* tp_dealloc */ + NULL, /* tp_print */ + xu_port_getattr, /* tp_getattr */ + NULL, /* tp_setattr */ + NULL, /* tp_compare */ + NULL, /* tp_repr */ + NULL, /* tp_as_number */ + NULL, /* tp_as_sequence */ + NULL, /* tp_as_mapping */ + NULL /* tp_hash */ +}; + + + +/* + * *********************** BUFFER *********************** + */ + +#define BUFSZ 65536 +#define MASK_BUF_IDX(_i) ((_i)&(BUFSZ-1)) +typedef unsigned int BUF_IDX; + +typedef struct { + PyObject_HEAD; + char *buf; + unsigned int prod, cons; +} xu_buffer_object; + +static PyObject *__xu_buffer_peek(xu_buffer_object *xub, int max) +{ + PyObject *str1, *str2; + int len1, len2, c = MASK_BUF_IDX(xub->cons); + + len1 = xub->prod - xub->cons; + if ( len1 > (BUFSZ - c) ) /* clip to ring wrap */ + len1 = BUFSZ - c; + if ( len1 > max ) /* clip to specified maximum */ + len1 = max; + if ( len1 < 0 ) /* sanity */ + len1 = 0; + + if ( (str1 = PyString_FromStringAndSize(&xub->buf[c], len1)) == NULL ) + return NULL; + + if ( (len1 < (xub->prod - xub->cons)) && (len1 < max) ) + { + len2 = max - len1; + if ( len2 > MASK_BUF_IDX(xub->prod) ) + len2 = MASK_BUF_IDX(xub->prod); + if ( len2 > 0 ) + { + str2 = PyString_FromStringAndSize(&xub->buf[0], len2); + if ( str2 == NULL ) + return NULL; + PyString_ConcatAndDel(&str1, str2); + if ( str1 == NULL ) + return NULL; + } + } + + return str1; +} + +static PyObject *xu_buffer_peek(PyObject *self, PyObject *args) +{ + xu_buffer_object *xub = (xu_buffer_object *)self; + int max = 1024; + + if ( !PyArg_ParseTuple(args, "|i", &max) ) + return NULL; + + return __xu_buffer_peek(xub, max); +} + +static PyObject *xu_buffer_read(PyObject *self, PyObject *args) +{ + xu_buffer_object *xub = (xu_buffer_object *)self; + PyObject *str; + int max = 1024; + + if ( !PyArg_ParseTuple(args, "|i", &max) ) + return NULL; + + if ( (str = __xu_buffer_peek(xub, max)) != NULL ) + xub->cons += PyString_Size(str); + + return str; +} + +static PyObject *xu_buffer_discard(PyObject *self, PyObject *args) +{ + xu_buffer_object *xub = (xu_buffer_object *)self; + int max, len; + + if ( !PyArg_ParseTuple(args, "i", &max) ) + return NULL; + + len = xub->prod - xub->cons; + if ( len > max ) + len = max; + if ( len < 0 ) + len = 0; + + xub->cons += len; + + return PyInt_FromLong(len); +} + +static PyObject *xu_buffer_write(PyObject *self, PyObject *args) +{ + xu_buffer_object *xub = (xu_buffer_object *)self; + char *str; + int len, len1, len2; + + if ( !PyArg_ParseTuple(args, "s#", &str, &len) ) + return NULL; + + len1 = len; + if ( len1 > (BUFSZ - MASK_BUF_IDX(xub->prod)) ) + len1 = BUFSZ - MASK_BUF_IDX(xub->prod); + if ( len1 > (BUFSZ - (xub->prod - xub->cons)) ) + len1 = BUFSZ - (xub->prod - xub->cons); + + if ( len1 == 0 ) + return PyInt_FromLong(0); + + memcpy(&xub->buf[MASK_BUF_IDX(xub->prod)], &str[0], len1); + xub->prod += len1; + + if ( len1 < len ) + { + len2 = len - len1; + if ( len2 > (BUFSZ - MASK_BUF_IDX(xub->prod)) ) + len2 = BUFSZ - MASK_BUF_IDX(xub->prod); + if ( len2 > (BUFSZ - (xub->prod - xub->cons)) ) + len2 = BUFSZ - (xub->prod - xub->cons); + if ( len2 != 0 ) + { + memcpy(&xub->buf[MASK_BUF_IDX(xub->prod)], &str[len1], len2); + xub->prod += len2; + return PyInt_FromLong(len1 + len2); + } + } + + return PyInt_FromLong(len1); +} + +static PyObject *xu_buffer_empty(PyObject *self, PyObject *args) +{ + xu_buffer_object *xub = (xu_buffer_object *)self; + + if ( !PyArg_ParseTuple(args, "") ) + return NULL; + + if ( xub->cons == xub->prod ) + return PyInt_FromLong(1); + + return PyInt_FromLong(0); +} + +static PyObject *xu_buffer_full(PyObject *self, PyObject *args) +{ + xu_buffer_object *xub = (xu_buffer_object *)self; + + if ( !PyArg_ParseTuple(args, "") ) + return NULL; + + if ( (xub->prod - xub->cons) == BUFSZ ) + return PyInt_FromLong(1); + + return PyInt_FromLong(0); +} + +static PyMethodDef xu_buffer_methods[] = { + { "peek", + (PyCFunction)xu_buffer_peek, + METH_VARARGS, + "Peek up to @max bytes from the buffer. Returns a string.\n" }, + + { "read", + (PyCFunction)xu_buffer_read, + METH_VARARGS, + "Read up to @max bytes from the buffer. Returns a string.\n" }, + + { "discard", + (PyCFunction)xu_buffer_discard, + METH_VARARGS, + "Discard up to @max bytes from the buffer. Returns number of bytes.\n" }, + + { "write", + (PyCFunction)xu_buffer_write, + METH_VARARGS, + "Write @string into buffer. Return number of bytes written.\n" }, + + { "empty", + (PyCFunction)xu_buffer_empty, + METH_VARARGS, + "Return TRUE if the buffer is empty.\n" }, + + { "full", + (PyCFunction)xu_buffer_full, + METH_VARARGS, + "Return TRUE if the buffer is full.\n" }, + + { NULL, NULL, 0, NULL } +}; + +staticforward PyTypeObject xu_buffer_type; + +static PyObject *xu_buffer_new(PyObject *self, PyObject *args) +{ + xu_buffer_object *xub; + + if ( !PyArg_ParseTuple(args, "") ) + return NULL; + + xub = PyObject_New(xu_buffer_object, &xu_buffer_type); + + if ( (xub->buf = malloc(BUFSZ)) == NULL ) + { + PyObject_Del((PyObject *)xub); + return NULL; + } + + xub->prod = xub->cons = 0; + + return (PyObject *)xub; +} + +static PyObject *xu_buffer_getattr(PyObject *obj, char *name) +{ + return Py_FindMethod(xu_buffer_methods, obj, name); +} + +static void xu_buffer_dealloc(PyObject *self) +{ + xu_buffer_object *xub = (xu_buffer_object *)self; + free(xub->buf); + PyObject_Del(self); +} + +static PyTypeObject xu_buffer_type = { + PyObject_HEAD_INIT(&PyType_Type) + 0, + "buffer", + sizeof(xu_buffer_object), + 0, + xu_buffer_dealloc, /* tp_dealloc */ + NULL, /* tp_print */ + xu_buffer_getattr, /* tp_getattr */ + NULL, /* tp_setattr */ + NULL, /* tp_compare */ + NULL, /* tp_repr */ + NULL, /* tp_as_number */ + NULL, /* tp_as_sequence */ + NULL, /* tp_as_mapping */ + NULL /* tp_hash */ +}; + + + +/* + * *********************** MODULE WRAPPER *********************** + */ + +static void handle_child_death(int dummy) +{ + while ( waitpid(-1, NULL, WNOHANG) > 0 ) + continue; +} + +static PyObject *xu_autoreap(PyObject *self, PyObject *args) +{ + struct sigaction sa; + + if ( !PyArg_ParseTuple(args, "") ) + return NULL; + + memset(&sa, 0, sizeof(sa)); + sa.sa_handler = handle_child_death; + sigemptyset(&sa.sa_mask); + sa.sa_flags = SA_NOCLDSTOP | SA_RESTART; + (void)sigaction(SIGCHLD, &sa, NULL); + + Py_INCREF(Py_None); + return Py_None; +} + +static PyMethodDef xu_methods[] = { + { "notifier", xu_notifier_new, METH_VARARGS, + "Create a new notifier." }, + { "message", xu_message_new, METH_VARARGS, + "Create a new communications message." }, + { "port", xu_port_new, METH_VARARGS, + "Create a new communications port." }, + { "buffer", xu_buffer_new, METH_VARARGS, + "Create a new ring buffer." }, + { "autoreap", xu_autoreap, METH_VARARGS, + "Ensure that zombie children are automatically reaped by the OS." }, + { NULL, NULL, 0, NULL } +}; + +PyMODINIT_FUNC initxu(void) +{ + PyObject *m, *d; + + m = Py_InitModule(XENPKG, xu_methods); + + d = PyModule_GetDict(m); + port_error = PyErr_NewException(XENPKG ".PortError", NULL, NULL); + PyDict_SetItemString(d, "PortError", port_error); +} |