[v2,16/22] golang/xenlight: implement keyed union C to Go marshaling
diff mbox series

Message ID 938dbf7c3a083ec050c16729805f4ce5f3f2891f.1573840474.git.rosbrookn@ainfosec.com
State New
Headers show
Series
  • generated Go libxl bindings using IDL
Related show

Commit Message

Nick Rosbrook Nov. 15, 2019, 7:44 p.m. UTC
From: Nick Rosbrook <rosbrookn@ainfosec.com>

Switch over union key to determine how to populate 'union' in Go struct.

Since the unions of C types cannot be directly accessed, add C structs in
cgo preamble to assist in marshaling keyed unions. This allows the C
type defined in the preamble to be populated first, and then accessed
directly to populate the Go struct.

Signed-off-by: Nick Rosbrook <rosbrookn@ainfosec.com>
---
Changes in v2:
- Do not use global variable for extra helper function definitions.
  Instead, return a tuple which contains a list of extra helper
  functions associated with the original.
 
 tools/golang/xenlight/gengotypes.py  | 153 +++++++++-
 tools/golang/xenlight/helpers.gen.go | 440 +++++++++++++++++++++++++++
 2 files changed, 590 insertions(+), 3 deletions(-)

Comments

George Dunlap Dec. 4, 2019, 6:40 p.m. UTC | #1
On 11/15/19 7:44 PM, Nick Rosbrook wrote:
> From: Nick Rosbrook <rosbrookn@ainfosec.com>
> 
> Switch over union key to determine how to populate 'union' in Go struct.
> 
> Since the unions of C types cannot be directly accessed, add C structs in
> cgo preamble to assist in marshaling keyed unions. This allows the C
> type defined in the preamble to be populated first, and then accessed
> directly to populate the Go struct.

Blech. :-(

> +def xenlight_golang_union_fields_from_C(ty = None):
> +    s = ''
> +
> +    for f in ty.fields:
> +        gotypename = xenlight_golang_fmt_name(f.type.typename)
> +        ctypename  = f.type.typename
> +        gofname    = xenlight_golang_fmt_name(f.name)
> +        cfname     = f.name
> +
> +        is_castable = (f.type.json_parse_type == 'JSON_INTEGER' or
> +                       isinstance(f.type, idl.Enumeration) or
> +                       gotypename in go_builtin_types)
> +
> +        if not is_castable:
> +            s += 'if err := x.{}.fromC(&tmp.{});'.format(gofname,cfname)
> +            s += 'err != nil {\n return err \n}\n'
> +
> +        # We just did an unsafe.Pointer cast from []byte to the 'union' type
> +        # struct, so we need to make sure that any string fields are actually
> +        # converted properly.
> +        elif gotypename == 'string':
> +            s += 'x.{} = C.GoString(tmp.{})\n'.format(gofname,cfname)
> +
> +        else:
> +            s += 'x.{} = {}(tmp.{})\n'.format(gofname,gotypename,cfname)

It looks like this is duplicating (differently!) the field-copying code
from golang_define_from_C.  Is there any reason you couldn't have a
single function, `xenlight_golang_fields_from_C`, which would be used
for both?


> +typedef struct libxl_channelinfo_connection_union_pty {
> +	char * path;
> +} libxl_channelinfo_connection_union_pty;

It would be nice if there were some way we could verify that the
structures generated here matched the C unions generated.  It would
stink pretty badly if they drifted and nobody noticed until we started
getting weird errors.

We don't have to solve it now, but let's put it on the to-do list and
have a think about it.

Overall looks about as good as it can -- thanks again for working this out.

 -George
George Dunlap Dec. 5, 2019, 12:22 p.m. UTC | #2
On 12/4/19 6:40 PM, George Dunlap wrote:
> On 11/15/19 7:44 PM, Nick Rosbrook wrote:
>> From: Nick Rosbrook <rosbrookn@ainfosec.com>
>>
>> Switch over union key to determine how to populate 'union' in Go struct.
>>
>> Since the unions of C types cannot be directly accessed, add C structs in
>> cgo preamble to assist in marshaling keyed unions. This allows the C
>> type defined in the preamble to be populated first, and then accessed
>> directly to populate the Go struct.
> 
> Blech. :-(
> 
>> +def xenlight_golang_union_fields_from_C(ty = None):
>> +    s = ''
>> +
>> +    for f in ty.fields:
>> +        gotypename = xenlight_golang_fmt_name(f.type.typename)
>> +        ctypename  = f.type.typename
>> +        gofname    = xenlight_golang_fmt_name(f.name)
>> +        cfname     = f.name
>> +
>> +        is_castable = (f.type.json_parse_type == 'JSON_INTEGER' or
>> +                       isinstance(f.type, idl.Enumeration) or
>> +                       gotypename in go_builtin_types)
>> +
>> +        if not is_castable:
>> +            s += 'if err := x.{}.fromC(&tmp.{});'.format(gofname,cfname)
>> +            s += 'err != nil {\n return err \n}\n'
>> +
>> +        # We just did an unsafe.Pointer cast from []byte to the 'union' type
>> +        # struct, so we need to make sure that any string fields are actually
>> +        # converted properly.
>> +        elif gotypename == 'string':
>> +            s += 'x.{} = C.GoString(tmp.{})\n'.format(gofname,cfname)
>> +
>> +        else:
>> +            s += 'x.{} = {}(tmp.{})\n'.format(gofname,gotypename,cfname)
> 
> It looks like this is duplicating (differently!) the field-copying code
> from golang_define_from_C.  Is there any reason you couldn't have a
> single function, `xenlight_golang_fields_from_C`, which would be used
> for both?
> 
> 
>> +typedef struct libxl_channelinfo_connection_union_pty {
>> +	char * path;
>> +} libxl_channelinfo_connection_union_pty;
> 
> It would be nice if there were some way we could verify that the
> structures generated here matched the C unions generated.  It would
> stink pretty badly if they drifted and nobody noticed until we started
> getting weird errors.
> 
> We don't have to solve it now, but let's put it on the to-do list and
> have a think about it.

Actually, it turns out we don't strictly need to duplicate this at all,
if we use the `typeof` operator, like this:

---
typedef typeof(((struct libxl_channelinfo *)NULL)->u.pty)
libxl_channelinfo_connection_union_pty;

typedef typeof(((struct libxl_domain_build_info *)NULL)->u.hvm)
libxl_domain_build_info_type_union_hvm;

typedef typeof(((struct libxl_domain_build_info *)NULL)->u.pv)
libxl_domain_build_info_type_union_pv;

typedef typeof(((struct libxl_domain_build_info *)NULL)->u.pvh)
libxl_domain_build_info_type_union_pvh;

typedef typeof(((struct libxl_device_usbdev *)NULL)->u.hostdev)
libxl_device_usbdev_type_union_hostdev;

typedef typeof(((struct libxl_device_channel *)NULL)->u.socket)
libxl_device_channel_connection_union_socket;
---

This guarantees we'll have the correct layout for the resulting type.

I talked to Ian Jackson, and he agreed that long-term it would be good
for the C generator to generate named types for these union elements
(likke you have here).  If you felt really motivated you could do that
now; but I think using the `typeof` trick would be suitable to get this
patch in.

 -George
Nick Rosbrook Dec. 5, 2019, 4:53 p.m. UTC | #3
> > It looks like this is duplicating (differently!) the field-copying code
> > from golang_define_from_C.  Is there any reason you couldn't have a
> > single function, `xenlight_golang_fields_from_C`, which would be used
> > for both?

No, I should be able to re-factor that. Thanks.

> Actually, it turns out we don't strictly need to duplicate this at all,
> if we use the `typeof` operator, like this:
>
> ---
> typedef typeof(((struct libxl_channelinfo *)NULL)->u.pty)
> libxl_channelinfo_connection_union_pty;
>
> typedef typeof(((struct libxl_domain_build_info *)NULL)->u.hvm)
> libxl_domain_build_info_type_union_hvm;
>
> typedef typeof(((struct libxl_domain_build_info *)NULL)->u.pv)
> libxl_domain_build_info_type_union_pv;
>
> typedef typeof(((struct libxl_domain_build_info *)NULL)->u.pvh)
> libxl_domain_build_info_type_union_pvh;
>
> typedef typeof(((struct libxl_device_usbdev *)NULL)->u.hostdev)
> libxl_device_usbdev_type_union_hostdev;
>
> typedef typeof(((struct libxl_device_channel *)NULL)->u.socket)
> libxl_device_channel_connection_union_socket;
> ---
>
> This guarantees we'll have the correct layout for the resulting type.

Well that's pretty cool.

> I talked to Ian Jackson, and he agreed that long-term it would be good
> for the C generator to generate named types for these union elements
> (likke you have here).  If you felt really motivated you could do that
> now; but I think using the `typeof` trick would be suitable to get this
> patch in.

I'll take a look at least and see if I can get it done fairly easily.
Otherwise, I'll use this trick.

Thanks,
-NR
George Dunlap Dec. 5, 2019, 5:33 p.m. UTC | #4
On 12/5/19 4:53 PM, Nick Rosbrook wrote:
>>> It looks like this is duplicating (differently!) the field-copying code
>>> from golang_define_from_C.  Is there any reason you couldn't have a
>>> single function, `xenlight_golang_fields_from_C`, which would be used
>>> for both?
> 
> No, I should be able to re-factor that. Thanks.
> 
>> Actually, it turns out we don't strictly need to duplicate this at all,
>> if we use the `typeof` operator, like this:
>>
>> ---
>> typedef typeof(((struct libxl_channelinfo *)NULL)->u.pty)
>> libxl_channelinfo_connection_union_pty;
>>
>> typedef typeof(((struct libxl_domain_build_info *)NULL)->u.hvm)
>> libxl_domain_build_info_type_union_hvm;
>>
>> typedef typeof(((struct libxl_domain_build_info *)NULL)->u.pv)
>> libxl_domain_build_info_type_union_pv;
>>
>> typedef typeof(((struct libxl_domain_build_info *)NULL)->u.pvh)
>> libxl_domain_build_info_type_union_pvh;
>>
>> typedef typeof(((struct libxl_device_usbdev *)NULL)->u.hostdev)
>> libxl_device_usbdev_type_union_hostdev;
>>
>> typedef typeof(((struct libxl_device_channel *)NULL)->u.socket)
>> libxl_device_channel_connection_union_socket;
>> ---
>>
>> This guarantees we'll have the correct layout for the resulting type.
> 
> Well that's pretty cool.
> 
>> I talked to Ian Jackson, and he agreed that long-term it would be good
>> for the C generator to generate named types for these union elements
>> (likke you have here).  If you felt really motivated you could do that
>> now; but I think using the `typeof` trick would be suitable to get this
>> patch in.
> 
> I'll take a look at least and see if I can get it done fairly easily.
> Otherwise, I'll use this trick.

It actually occurs to me that the "named struct elements of union" would
still technically open up a window for divergence: i.e., if somehow the
type of the named struct didn't match up with the union element.

I.e., the following *shouldn't* happen, but technically it *could*:

----
struct libxl_domain_build_info_union_hvm {
 ...
}

struct libxl_domain_build_info {
  union {
    libxl_domain_struct_build_info_hvm2 hvm;
  } u;
}
---

Using the `typeof` trick above guarantees that the types the marshaling
functions are using are identical to the types actually specified in the
union itself.  Particularly as this is just generated code nobody's
going to look at, I'm inclined to think the cost is near-zero.  Since
the benefit is non-zero, I'd be inclined to say just go with that instead.

And it's easier!

Thoughts?

 -George
Nick Rosbrook Dec. 5, 2019, 6:39 p.m. UTC | #5
> It actually occurs to me that the "named struct elements of union" would
> still technically open up a window for divergence: i.e., if somehow the
> type of the named struct didn't match up with the union element.
>
> I.e., the following *shouldn't* happen, but technically it *could*:
>
> ----
> struct libxl_domain_build_info_union_hvm {
>  ...
> }
>
> struct libxl_domain_build_info {
>   union {
>     libxl_domain_struct_build_info_hvm2 hvm;
>   } u;
> }
> ---
>
> Using the `typeof` trick above guarantees that the types the marshaling
> functions are using are identical to the types actually specified in the
> union itself.  Particularly as this is just generated code nobody's
> going to look at, I'm inclined to think the cost is near-zero.  Since
> the benefit is non-zero, I'd be inclined to say just go with that instead.
>
> And it's easier!
>
> Thoughts?

In that case I'll just use the typeof trick :)

-NR
George Dunlap Dec. 6, 2019, 10:46 a.m. UTC | #6
On 12/5/19 6:39 PM, Nick Rosbrook wrote:
>> It actually occurs to me that the "named struct elements of union" would
>> still technically open up a window for divergence: i.e., if somehow the
>> type of the named struct didn't match up with the union element.
>>
>> I.e., the following *shouldn't* happen, but technically it *could*:
>>
>> ----
>> struct libxl_domain_build_info_union_hvm {
>>  ...
>> }
>>
>> struct libxl_domain_build_info {
>>   union {
>>     libxl_domain_struct_build_info_hvm2 hvm;
>>   } u;
>> }
>> ---
>>
>> Using the `typeof` trick above guarantees that the types the marshaling
>> functions are using are identical to the types actually specified in the
>> union itself.  Particularly as this is just generated code nobody's
>> going to look at, I'm inclined to think the cost is near-zero.  Since
>> the benefit is non-zero, I'd be inclined to say just go with that instead.
>>
>> And it's easier!
>>
>> Thoughts?
> 
> In that case I'll just use the typeof trick :)

OK.  FYI I'm going to have to stop reviewing here for a bit; if you
re-send the series with the comments on 1-16 addressed though, I'll skim
through and check it in if it looks good.

 -George
Nick Rosbrook Dec. 6, 2019, 3:39 p.m. UTC | #7
> OK.  FYI I'm going to have to stop reviewing here for a bit; if you
> re-send the series with the comments on 1-16 addressed though, I'll skim
> through and check it in if it looks good.

Okay, thanks for the heads up. I'm hoping to send v3 in the next few days.

-NR

Patch
diff mbox series

diff --git a/tools/golang/xenlight/gengotypes.py b/tools/golang/xenlight/gengotypes.py
index 0c8a1327a1..57cecd5989 100644
--- a/tools/golang/xenlight/gengotypes.py
+++ b/tools/golang/xenlight/gengotypes.py
@@ -24,6 +24,10 @@  go_keywords = ['type', 'func']
 go_builtin_types = ['bool', 'string', 'int', 'byte',
                     'uint16', 'uint32', 'uint64']
 
+# cgo preamble for xenlight_helpers.go, created during type generation and
+# written later.
+cgo_helpers_preamble = []
+
 def xenlight_golang_generate_types(path = None, types = None, comment = None):
     """
     Generate a .go file (types.gen.go by default)
@@ -168,6 +172,8 @@  def xenlight_golang_define_union(ty = None, structname = ''):
         extras.append(r[0])
         extras.extend(r[1])
 
+        xenlight_golang_union_cgo_preamble(f.type, name=name)
+
         # Define function to implement 'union' interface
         name = xenlight_golang_fmt_name(name)
         s = 'func (x {}) is{}(){{}}\n'.format(name, interface_name)
@@ -182,6 +188,18 @@  def xenlight_golang_define_union(ty = None, structname = ''):
 
     return (s,extras)
 
+def xenlight_golang_union_cgo_preamble(ty = None, name = ''):
+    s = ''
+
+    s += 'typedef struct {} {{\n'.format(name)
+
+    for f in ty.fields:
+        s += '\t{} {};\n'.format(f.type.typename, f.name)
+
+    s += '}} {};\n'.format(name)
+
+    cgo_helpers_preamble.append(s)
+
 def xenlight_golang_generate_helpers(path = None, types = None, comment = None):
     """
     Generate a .go file (helpers.gen.go by default)
@@ -195,6 +213,7 @@  def xenlight_golang_generate_helpers(path = None, types = None, comment = None):
         if comment is not None:
             f.write(comment)
         f.write('package xenlight\n')
+        f.write('import (\n"unsafe"\n"errors"\n"fmt"\n)\n')
 
         # Cgo preamble
         f.write('/*\n')
@@ -203,19 +222,38 @@  def xenlight_golang_generate_helpers(path = None, types = None, comment = None):
         f.write('#include <libxl.h>\n')
         f.write('\n')
 
+        for s in cgo_helpers_preamble:
+            f.write(s)
+            f.write('\n')
+
         f.write('*/\nimport "C"\n')
 
         for ty in types:
             if not isinstance(ty, idl.Struct):
                 continue
 
-            f.write(xenlight_golang_define_from_C(ty))
+            (fdef, extras) = xenlight_golang_define_from_C(ty)
+
+            f.write(fdef)
             f.write('\n')
 
+            for extra in extras:
+                f.write(extra)
+                f.write('\n')
+
     go_fmt(path)
 
 def xenlight_golang_define_from_C(ty = None, typename = None, nested = False):
+    """
+    Define the fromC helper function for ty.
+
+    Return a tuple that contains a string with the
+    function definition, and a (potentially empty) list
+    of extra definitions that are associated with
+    this helper function.
+    """
     s = ''
+    extras = []
 
     gotypename = ctypename = ''
 
@@ -280,10 +318,16 @@  def xenlight_golang_define_from_C(ty = None, typename = None, nested = False):
                 s += 'x.{} = {}\n'.format(goname, varname)
 
         elif isinstance(f.type, idl.Struct):
-            s += xenlight_golang_define_from_C(f.type, typename=f.name, nested=True)
+            r = xenlight_golang_define_from_C(f.type, typename=f.name, nested=True)
+
+            s += r[0]
+            extras.extend(r[1])
 
         elif isinstance(f.type, idl.KeyedUnion):
-            pass
+            r = xenlight_golang_union_from_C(f.type, f.name, ty.typename)
+
+            s += r[0]
+            extras.extend(r[1])
 
         else:
             raise Exception('type {} not supported'.format(f.type))
@@ -292,6 +336,109 @@  def xenlight_golang_define_from_C(ty = None, typename = None, nested = False):
         s += 'return nil'
         s += '}\n'
 
+    return (s,extras)
+
+def xenlight_golang_union_from_C(ty = None, union_name = '', struct_name = ''):
+    extras = []
+
+    keyname   = ty.keyvar.name
+    gokeyname = xenlight_golang_fmt_name(keyname)
+    keytype   = ty.keyvar.type.typename
+    gokeytype = xenlight_golang_fmt_name(keytype)
+
+    interface_name = '{}_{}_union'.format(struct_name, keyname)
+    interface_name = xenlight_golang_fmt_name(interface_name, exported=False)
+
+    cgo_keyname = keyname
+    if cgo_keyname in go_keywords:
+        cgo_keyname = '_' + cgo_keyname
+
+    cases = {}
+
+    for f in ty.fields:
+        val = '{}_{}'.format(keytype, f.name)
+        val = xenlight_golang_fmt_name(val)
+
+        # Add to list of cases to make for the switch
+        # statement below.
+        if f.type is None:
+            continue
+
+        cases[f.name] = val
+
+        # Define fromC func for 'union' struct.
+        typename   = '{}_{}_union_{}'.format(struct_name,keyname,f.name)
+        gotypename = xenlight_golang_fmt_name(typename)
+
+        # Define the function here. The cases for keyed unions are a little
+        # different.
+        s = 'func (x *{}) fromC(xc *C.{}) error {{\n'.format(gotypename,struct_name)
+        s += 'if {}(xc.{}) != {} {{\n'.format(gokeytype,cgo_keyname,val)
+        err_string = '"expected union key {}"'.format(val)
+        s += 'return errors.New({})\n'.format(err_string)
+        s += '}\n\n'
+        s += 'tmp := (*C.{})(unsafe.Pointer(&xc.{}[0]))\n'.format(typename,union_name)
+
+        s += xenlight_golang_union_fields_from_C(f.type)
+        s += 'return nil\n'
+        s += '}\n'
+
+        extras.append(s)
+
+    s = 'x.{} = {}(xc.{})\n'.format(gokeyname,gokeytype,cgo_keyname)
+    s += 'switch x.{}{{\n'.format(gokeyname)
+
+    # Create switch statement to determine which 'union element'
+    # to populate in the Go struct.
+    for case_name, case_val in cases.items():
+        s += 'case {}:\n'.format(case_val)
+
+        gotype = '{}_{}_union_{}'.format(struct_name,keyname,case_name)
+        gotype = xenlight_golang_fmt_name(gotype)
+        goname = '{}_{}'.format(keyname,case_name)
+        goname = xenlight_golang_fmt_name(goname,exported=False)
+
+        s += 'var {} {}\n'.format(goname, gotype)
+        s += 'if err := {}.fromC(xc);'.format(goname)
+        s += 'err != nil {\n return err \n}\n'
+
+        field_name = xenlight_golang_fmt_name('{}_union'.format(keyname))
+        s += 'x.{} = {}\n'.format(field_name, goname)
+
+    # End switch statement
+    s += 'default:\n'
+    err_string = '"invalid union key \'%v\'", x.{}'.format(gokeyname)
+    s += 'return fmt.Errorf({})'.format(err_string)
+    s += '}\n'
+
+    return (s,extras)
+
+def xenlight_golang_union_fields_from_C(ty = None):
+    s = ''
+
+    for f in ty.fields:
+        gotypename = xenlight_golang_fmt_name(f.type.typename)
+        ctypename  = f.type.typename
+        gofname    = xenlight_golang_fmt_name(f.name)
+        cfname     = f.name
+
+        is_castable = (f.type.json_parse_type == 'JSON_INTEGER' or
+                       isinstance(f.type, idl.Enumeration) or
+                       gotypename in go_builtin_types)
+
+        if not is_castable:
+            s += 'if err := x.{}.fromC(&tmp.{});'.format(gofname,cfname)
+            s += 'err != nil {\n return err \n}\n'
+
+        # We just did an unsafe.Pointer cast from []byte to the 'union' type
+        # struct, so we need to make sure that any string fields are actually
+        # converted properly.
+        elif gotypename == 'string':
+            s += 'x.{} = C.GoString(tmp.{})\n'.format(gofname,cfname)
+
+        else:
+            s += 'x.{} = {}(tmp.{})\n'.format(gofname,gotypename,cfname)
+
     return s
 
 def xenlight_golang_fmt_name(name, exported = True):
diff --git a/tools/golang/xenlight/helpers.gen.go b/tools/golang/xenlight/helpers.gen.go
index 0de42eaa6b..d2dd9f2507 100644
--- a/tools/golang/xenlight/helpers.gen.go
+++ b/tools/golang/xenlight/helpers.gen.go
@@ -5,11 +5,122 @@ 
 //
 package xenlight
 
+import (
+	"errors"
+	"fmt"
+	"unsafe"
+)
+
 /*
 #cgo LDFLAGS: -lxenlight
 #include <stdlib.h>
 #include <libxl.h>
 
+typedef struct libxl_channelinfo_connection_union_pty {
+	char * path;
+} libxl_channelinfo_connection_union_pty;
+
+typedef struct libxl_domain_build_info_type_union_hvm {
+	char * firmware;
+	libxl_bios_type bios;
+	libxl_defbool pae;
+	libxl_defbool apic;
+	libxl_defbool acpi;
+	libxl_defbool acpi_s3;
+	libxl_defbool acpi_s4;
+	libxl_defbool acpi_laptop_slate;
+	libxl_defbool nx;
+	libxl_defbool viridian;
+	libxl_bitmap viridian_enable;
+	libxl_bitmap viridian_disable;
+	char * timeoffset;
+	libxl_defbool hpet;
+	libxl_defbool vpt_align;
+	uint64_t mmio_hole_memkb;
+	libxl_timer_mode timer_mode;
+	libxl_defbool nested_hvm;
+	libxl_defbool altp2m;
+	char * system_firmware;
+	char * smbios_firmware;
+	char * acpi_firmware;
+	libxl_hdtype hdtype;
+	libxl_defbool nographic;
+	libxl_vga_interface_info vga;
+	libxl_vnc_info vnc;
+	char * keymap;
+	libxl_sdl_info sdl;
+	libxl_spice_info spice;
+	libxl_defbool gfx_passthru;
+	libxl_gfx_passthru_kind gfx_passthru_kind;
+	char * serial;
+	char * boot;
+	libxl_defbool usb;
+	int usbversion;
+	char * usbdevice;
+	libxl_defbool vkb_device;
+	char * soundhw;
+	libxl_defbool xen_platform_pci;
+	libxl_string_list usbdevice_list;
+	libxl_vendor_device vendor_device;
+	libxl_ms_vm_genid ms_vm_genid;
+	libxl_string_list serial_list;
+	libxl_rdm_reserve rdm;
+	uint64_t rdm_mem_boundary_memkb;
+	uint64_t mca_caps;
+} libxl_domain_build_info_type_union_hvm;
+
+typedef struct libxl_domain_build_info_type_union_pv {
+	char * kernel;
+	uint64_t slack_memkb;
+	char * bootloader;
+	libxl_string_list bootloader_args;
+	char * cmdline;
+	char * ramdisk;
+	char * features;
+	libxl_defbool e820_host;
+} libxl_domain_build_info_type_union_pv;
+
+typedef struct libxl_domain_build_info_type_union_pvh {
+	libxl_defbool pvshim;
+	char * pvshim_path;
+	char * pvshim_cmdline;
+	char * pvshim_extra;
+} libxl_domain_build_info_type_union_pvh;
+
+typedef struct libxl_device_usbdev_type_union_hostdev {
+	uint8_t hostbus;
+	uint8_t hostaddr;
+} libxl_device_usbdev_type_union_hostdev;
+
+typedef struct libxl_device_channel_connection_union_socket {
+	char * path;
+} libxl_device_channel_connection_union_socket;
+
+typedef struct libxl_event_type_union_domain_shutdown {
+	uint8_t shutdown_reason;
+} libxl_event_type_union_domain_shutdown;
+
+typedef struct libxl_event_type_union_disk_eject {
+	char * vdev;
+	libxl_device_disk disk;
+} libxl_event_type_union_disk_eject;
+
+typedef struct libxl_event_type_union_operation_complete {
+	int rc;
+} libxl_event_type_union_operation_complete;
+
+typedef struct libxl_psr_hw_info_type_union_cat {
+	uint32_t cos_max;
+	uint32_t cbm_len;
+	bool cdp_enabled;
+} libxl_psr_hw_info_type_union_cat;
+
+typedef struct libxl_psr_hw_info_type_union_mba {
+	uint32_t cos_max;
+	uint32_t thrtl_max;
+	bool linear;
+} libxl_psr_hw_info_type_union_mba;
+
 */
 import "C"
 
@@ -151,6 +262,27 @@  func (x *Channelinfo) fromC(xc *C.libxl_channelinfo) error {
 	x.State = int(xc.state)
 	x.Evtch = int(xc.evtch)
 	x.Rref = int(xc.rref)
+	x.Connection = ChannelConnection(xc.connection)
+	switch x.Connection {
+	case ChannelConnectionPty:
+		var connectionPty ChannelinfoConnectionUnionPty
+		if err := connectionPty.fromC(xc); err != nil {
+			return err
+		}
+		x.ConnectionUnion = connectionPty
+	default:
+		return fmt.Errorf("invalid union key '%v'", x.Connection)
+	}
+	return nil
+}
+
+func (x *ChannelinfoConnectionUnionPty) fromC(xc *C.libxl_channelinfo) error {
+	if ChannelConnection(xc.connection) != ChannelConnectionPty {
+		return errors.New("expected union key ChannelConnectionPty")
+	}
+
+	tmp := (*C.libxl_channelinfo_connection_union_pty)(unsafe.Pointer(&xc.u[0]))
+	x.Path = C.GoString(tmp.path)
 	return nil
 }
 
@@ -400,12 +532,180 @@  func (x *DomainBuildInfo) fromC(xc *C.libxl_domain_build_info) error {
 	}
 	x.DmRestrict = defboolDmRestrict
 	x.Tee = TeeType(xc.tee)
+	x.Type = DomainType(xc._type)
+	switch x.Type {
+	case DomainTypePv:
+		var typePv DomainBuildInfoTypeUnionPv
+		if err := typePv.fromC(xc); err != nil {
+			return err
+		}
+		x.TypeUnion = typePv
+	case DomainTypeHvm:
+		var typeHvm DomainBuildInfoTypeUnionHvm
+		if err := typeHvm.fromC(xc); err != nil {
+			return err
+		}
+		x.TypeUnion = typeHvm
+	case DomainTypePvh:
+		var typePvh DomainBuildInfoTypeUnionPvh
+		if err := typePvh.fromC(xc); err != nil {
+			return err
+		}
+		x.TypeUnion = typePvh
+	default:
+		return fmt.Errorf("invalid union key '%v'", x.Type)
+	}
 	x.ArchArm.GicVersion = GicVersion(xc.arch_arm.gic_version)
 	x.ArchArm.Vuart = VuartType(xc.arch_arm.vuart)
 	x.Altp2M = Altp2MMode(xc.altp2m)
 	return nil
 }
 
+func (x *DomainBuildInfoTypeUnionHvm) fromC(xc *C.libxl_domain_build_info) error {
+	if DomainType(xc._type) != DomainTypeHvm {
+		return errors.New("expected union key DomainTypeHvm")
+	}
+
+	tmp := (*C.libxl_domain_build_info_type_union_hvm)(unsafe.Pointer(&xc.u[0]))
+	x.Firmware = C.GoString(tmp.firmware)
+	x.Bios = BiosType(tmp.bios)
+	if err := x.Pae.fromC(&tmp.pae); err != nil {
+		return err
+	}
+	if err := x.Apic.fromC(&tmp.apic); err != nil {
+		return err
+	}
+	if err := x.Acpi.fromC(&tmp.acpi); err != nil {
+		return err
+	}
+	if err := x.AcpiS3.fromC(&tmp.acpi_s3); err != nil {
+		return err
+	}
+	if err := x.AcpiS4.fromC(&tmp.acpi_s4); err != nil {
+		return err
+	}
+	if err := x.AcpiLaptopSlate.fromC(&tmp.acpi_laptop_slate); err != nil {
+		return err
+	}
+	if err := x.Nx.fromC(&tmp.nx); err != nil {
+		return err
+	}
+	if err := x.Viridian.fromC(&tmp.viridian); err != nil {
+		return err
+	}
+	if err := x.ViridianEnable.fromC(&tmp.viridian_enable); err != nil {
+		return err
+	}
+	if err := x.ViridianDisable.fromC(&tmp.viridian_disable); err != nil {
+		return err
+	}
+	x.Timeoffset = C.GoString(tmp.timeoffset)
+	if err := x.Hpet.fromC(&tmp.hpet); err != nil {
+		return err
+	}
+	if err := x.VptAlign.fromC(&tmp.vpt_align); err != nil {
+		return err
+	}
+	x.MmioHoleMemkb = uint64(tmp.mmio_hole_memkb)
+	x.TimerMode = TimerMode(tmp.timer_mode)
+	if err := x.NestedHvm.fromC(&tmp.nested_hvm); err != nil {
+		return err
+	}
+	if err := x.Altp2M.fromC(&tmp.altp2m); err != nil {
+		return err
+	}
+	x.SystemFirmware = C.GoString(tmp.system_firmware)
+	x.SmbiosFirmware = C.GoString(tmp.smbios_firmware)
+	x.AcpiFirmware = C.GoString(tmp.acpi_firmware)
+	x.Hdtype = Hdtype(tmp.hdtype)
+	if err := x.Nographic.fromC(&tmp.nographic); err != nil {
+		return err
+	}
+	if err := x.Vga.fromC(&tmp.vga); err != nil {
+		return err
+	}
+	if err := x.Vnc.fromC(&tmp.vnc); err != nil {
+		return err
+	}
+	x.Keymap = C.GoString(tmp.keymap)
+	if err := x.Sdl.fromC(&tmp.sdl); err != nil {
+		return err
+	}
+	if err := x.Spice.fromC(&tmp.spice); err != nil {
+		return err
+	}
+	if err := x.GfxPassthru.fromC(&tmp.gfx_passthru); err != nil {
+		return err
+	}
+	x.GfxPassthruKind = GfxPassthruKind(tmp.gfx_passthru_kind)
+	x.Serial = C.GoString(tmp.serial)
+	x.Boot = C.GoString(tmp.boot)
+	if err := x.Usb.fromC(&tmp.usb); err != nil {
+		return err
+	}
+	x.Usbversion = int(tmp.usbversion)
+	x.Usbdevice = C.GoString(tmp.usbdevice)
+	if err := x.VkbDevice.fromC(&tmp.vkb_device); err != nil {
+		return err
+	}
+	x.Soundhw = C.GoString(tmp.soundhw)
+	if err := x.XenPlatformPci.fromC(&tmp.xen_platform_pci); err != nil {
+		return err
+	}
+	if err := x.UsbdeviceList.fromC(&tmp.usbdevice_list); err != nil {
+		return err
+	}
+	x.VendorDevice = VendorDevice(tmp.vendor_device)
+	if err := x.MsVmGenid.fromC(&tmp.ms_vm_genid); err != nil {
+		return err
+	}
+	if err := x.SerialList.fromC(&tmp.serial_list); err != nil {
+		return err
+	}
+	if err := x.Rdm.fromC(&tmp.rdm); err != nil {
+		return err
+	}
+	x.RdmMemBoundaryMemkb = uint64(tmp.rdm_mem_boundary_memkb)
+	x.McaCaps = uint64(tmp.mca_caps)
+	return nil
+}
+
+func (x *DomainBuildInfoTypeUnionPv) fromC(xc *C.libxl_domain_build_info) error {
+	if DomainType(xc._type) != DomainTypePv {
+		return errors.New("expected union key DomainTypePv")
+	}
+
+	tmp := (*C.libxl_domain_build_info_type_union_pv)(unsafe.Pointer(&xc.u[0]))
+	x.Kernel = C.GoString(tmp.kernel)
+	x.SlackMemkb = uint64(tmp.slack_memkb)
+	x.Bootloader = C.GoString(tmp.bootloader)
+	if err := x.BootloaderArgs.fromC(&tmp.bootloader_args); err != nil {
+		return err
+	}
+	x.Cmdline = C.GoString(tmp.cmdline)
+	x.Ramdisk = C.GoString(tmp.ramdisk)
+	x.Features = C.GoString(tmp.features)
+	if err := x.E820Host.fromC(&tmp.e820_host); err != nil {
+		return err
+	}
+	return nil
+}
+
+func (x *DomainBuildInfoTypeUnionPvh) fromC(xc *C.libxl_domain_build_info) error {
+	if DomainType(xc._type) != DomainTypePvh {
+		return errors.New("expected union key DomainTypePvh")
+	}
+
+	tmp := (*C.libxl_domain_build_info_type_union_pvh)(unsafe.Pointer(&xc.u[0]))
+	if err := x.Pvshim.fromC(&tmp.pvshim); err != nil {
+		return err
+	}
+	x.PvshimPath = C.GoString(tmp.pvshim_path)
+	x.PvshimCmdline = C.GoString(tmp.pvshim_cmdline)
+	x.PvshimExtra = C.GoString(tmp.pvshim_extra)
+	return nil
+}
+
 func (x *DeviceVfb) fromC(xc *C.libxl_device_vfb) error {
 	x.BackendDomid = Domid(xc.backend_domid)
 	x.BackendDomname = C.GoString(xc.backend_domname)
@@ -584,6 +884,28 @@  func (x *DeviceUsbctrl) fromC(xc *C.libxl_device_usbctrl) error {
 func (x *DeviceUsbdev) fromC(xc *C.libxl_device_usbdev) error {
 	x.Ctrl = Devid(xc.ctrl)
 	x.Port = int(xc.port)
+	x.Type = UsbdevType(xc._type)
+	switch x.Type {
+	case UsbdevTypeHostdev:
+		var typeHostdev DeviceUsbdevTypeUnionHostdev
+		if err := typeHostdev.fromC(xc); err != nil {
+			return err
+		}
+		x.TypeUnion = typeHostdev
+	default:
+		return fmt.Errorf("invalid union key '%v'", x.Type)
+	}
+	return nil
+}
+
+func (x *DeviceUsbdevTypeUnionHostdev) fromC(xc *C.libxl_device_usbdev) error {
+	if UsbdevType(xc._type) != UsbdevTypeHostdev {
+		return errors.New("expected union key UsbdevTypeHostdev")
+	}
+
+	tmp := (*C.libxl_device_usbdev_type_union_hostdev)(unsafe.Pointer(&xc.u[0]))
+	x.Hostbus = byte(tmp.hostbus)
+	x.Hostaddr = byte(tmp.hostaddr)
 	return nil
 }
 
@@ -626,6 +948,27 @@  func (x *DeviceChannel) fromC(xc *C.libxl_device_channel) error {
 	x.BackendDomname = C.GoString(xc.backend_domname)
 	x.Devid = Devid(xc.devid)
 	x.Name = C.GoString(xc.name)
+	x.Connection = ChannelConnection(xc.connection)
+	switch x.Connection {
+	case ChannelConnectionSocket:
+		var connectionSocket DeviceChannelConnectionUnionSocket
+		if err := connectionSocket.fromC(xc); err != nil {
+			return err
+		}
+		x.ConnectionUnion = connectionSocket
+	default:
+		return fmt.Errorf("invalid union key '%v'", x.Connection)
+	}
+	return nil
+}
+
+func (x *DeviceChannelConnectionUnionSocket) fromC(xc *C.libxl_device_channel) error {
+	if ChannelConnection(xc.connection) != ChannelConnectionSocket {
+		return errors.New("expected union key ChannelConnectionSocket")
+	}
+
+	tmp := (*C.libxl_device_channel_connection_union_socket)(unsafe.Pointer(&xc.u[0]))
+	x.Path = C.GoString(tmp.path)
 	return nil
 }
 
@@ -952,6 +1295,62 @@  func (x *Event) fromC(xc *C.libxl_event) error {
 	}
 	x.Domuuid = uuidDomuuid
 	x.ForUser = uint64(xc.for_user)
+	x.Type = EventType(xc._type)
+	switch x.Type {
+	case EventTypeOperationComplete:
+		var typeOperationComplete EventTypeUnionOperationComplete
+		if err := typeOperationComplete.fromC(xc); err != nil {
+			return err
+		}
+		x.TypeUnion = typeOperationComplete
+	case EventTypeDomainShutdown:
+		var typeDomainShutdown EventTypeUnionDomainShutdown
+		if err := typeDomainShutdown.fromC(xc); err != nil {
+			return err
+		}
+		x.TypeUnion = typeDomainShutdown
+	case EventTypeDiskEject:
+		var typeDiskEject EventTypeUnionDiskEject
+		if err := typeDiskEject.fromC(xc); err != nil {
+			return err
+		}
+		x.TypeUnion = typeDiskEject
+	default:
+		return fmt.Errorf("invalid union key '%v'", x.Type)
+	}
+	return nil
+}
+
+func (x *EventTypeUnionDomainShutdown) fromC(xc *C.libxl_event) error {
+	if EventType(xc._type) != EventTypeDomainShutdown {
+		return errors.New("expected union key EventTypeDomainShutdown")
+	}
+
+	tmp := (*C.libxl_event_type_union_domain_shutdown)(unsafe.Pointer(&xc.u[0]))
+	x.ShutdownReason = byte(tmp.shutdown_reason)
+	return nil
+}
+
+func (x *EventTypeUnionDiskEject) fromC(xc *C.libxl_event) error {
+	if EventType(xc._type) != EventTypeDiskEject {
+		return errors.New("expected union key EventTypeDiskEject")
+	}
+
+	tmp := (*C.libxl_event_type_union_disk_eject)(unsafe.Pointer(&xc.u[0]))
+	x.Vdev = C.GoString(tmp.vdev)
+	if err := x.Disk.fromC(&tmp.disk); err != nil {
+		return err
+	}
+	return nil
+}
+
+func (x *EventTypeUnionOperationComplete) fromC(xc *C.libxl_event) error {
+	if EventType(xc._type) != EventTypeOperationComplete {
+		return errors.New("expected union key EventTypeOperationComplete")
+	}
+
+	tmp := (*C.libxl_event_type_union_operation_complete)(unsafe.Pointer(&xc.u[0]))
+	x.Rc = int(tmp.rc)
 	return nil
 }
 
@@ -965,5 +1364,46 @@  func (x *PsrCatInfo) fromC(xc *C.libxl_psr_cat_info) error {
 
 func (x *PsrHwInfo) fromC(xc *C.libxl_psr_hw_info) error {
 	x.Id = uint32(xc.id)
+	x.Type = PsrFeatType(xc._type)
+	switch x.Type {
+	case PsrFeatTypeMba:
+		var typeMba PsrHwInfoTypeUnionMba
+		if err := typeMba.fromC(xc); err != nil {
+			return err
+		}
+		x.TypeUnion = typeMba
+	case PsrFeatTypeCat:
+		var typeCat PsrHwInfoTypeUnionCat
+		if err := typeCat.fromC(xc); err != nil {
+			return err
+		}
+		x.TypeUnion = typeCat
+	default:
+		return fmt.Errorf("invalid union key '%v'", x.Type)
+	}
+	return nil
+}
+
+func (x *PsrHwInfoTypeUnionCat) fromC(xc *C.libxl_psr_hw_info) error {
+	if PsrFeatType(xc._type) != PsrFeatTypeCat {
+		return errors.New("expected union key PsrFeatTypeCat")
+	}
+
+	tmp := (*C.libxl_psr_hw_info_type_union_cat)(unsafe.Pointer(&xc.u[0]))
+	x.CosMax = uint32(tmp.cos_max)
+	x.CbmLen = uint32(tmp.cbm_len)
+	x.CdpEnabled = bool(tmp.cdp_enabled)
+	return nil
+}
+
+func (x *PsrHwInfoTypeUnionMba) fromC(xc *C.libxl_psr_hw_info) error {
+	if PsrFeatType(xc._type) != PsrFeatTypeMba {
+		return errors.New("expected union key PsrFeatTypeMba")
+	}
+
+	tmp := (*C.libxl_psr_hw_info_type_union_mba)(unsafe.Pointer(&xc.u[0]))
+	x.CosMax = uint32(tmp.cos_max)
+	x.ThrtlMax = uint32(tmp.thrtl_max)
+	x.Linear = bool(tmp.linear)
 	return nil
 }