linux-qubasis

linux oasis port as a qubes template

git clone https://9o.is/git/linux-qubasis.git

libxl_save_msgs_gen.py

(15356B)


      1 import sys
      2 import re
      3 import textwrap
      4 
      5 # --- Configuration and Globals ---
      6 DEBUG = False  # produce copious debugging output at run-time?
      7 
      8 # Message Definitions (similar to the Perl @msgs array)
      9 # flags:
     10 #   s  - applicable to save
     11 #   r  - applicable to restore
     12 #   c  - function pointer in callbacks struct rather than fixed function
     13 #   x  - function pointer is in struct {save,restore}_callbacks
     14 #        and its null-ness needs to be passed through to the helper's xc
     15 #   W  - needs a return value; callback is synchronous
     16 #   A  - needs a return value; callback is asynchronous
     17 msgs = [
     18     {
     19         'flags': 'sr', 'name': 'log', 'args': [
     20             'uint32_t', 'level', 'uint32_t', 'errnoval',
     21             'STRING', 'context', 'STRING', 'formatted'
     22         ]
     23     },
     24     {
     25         'flags': 'sr', 'name': 'progress', 'args': [
     26             'STRING', 'context', 'STRING', 'doing_what',
     27             'unsigned long', 'done', 'unsigned long', 'total'
     28         ]
     29     },
     30     {'flags': 'srcxA', 'name': 'suspend', 'args': []},
     31     {'flags': 'srcxA', 'name': 'postcopy', 'args': []},
     32     {'flags': 'srcxA', 'name': 'checkpoint', 'args': []},
     33     {'flags': 'srcxA', 'name': 'wait_checkpoint', 'args': []},
     34     {
     35         'flags': 'scxA', 'name': 'switch_qemu_logdirty', 'args': [
     36             'uint32_t', 'domid', 'unsigned', 'enable'
     37         ]
     38     },
     39     {
     40         'flags': 'rcxW', 'name': 'static_data_done', 'args': [
     41             'unsigned', 'missing'
     42         ]
     43     },
     44     {
     45         'flags': 'rcx', 'name': 'restore_results', 'args': [
     46             'xen_pfn_t', 'store_gfn', 'xen_pfn_t', 'console_gfn'
     47         ]
     48     },
     49     {
     50         'flags': 'srW', 'name': 'complete', 'args': [
     51             'int', 'retval', 'int', 'errnoval'
     52         ]
     53     },
     54 ]
     55 
     56 # Output storage
     57 cbs = {}
     58 func = {}
     59 func_ah = {}
     60 outfuncs = []
     61 out_decls = {}
     62 out_body = {}
     63 msgnum = 0
     64 
     65 # C prefix definitions
     66 libxl = "libxl__srm"
     67 callback = f"{libxl}_callout_callback"
     68 receiveds = f"{libxl}_callout_received"
     69 sendreply = f"{libxl}_callout_sendreply"
     70 getcallbacks = f"{libxl}_callout_get_callbacks"
     71 enumcallbacks = f"{libxl}_callout_enumcallbacks"
     72 helper = "helper"
     73 encode = f"{helper}_stub"
     74 allocbuf = f"{helper}_allocbuf"
     75 transmit = f"{helper}_transmitmsg"
     76 getreply = f"{helper}_getreply"
     77 setcallbacks = f"{helper}_setcallbacks"
     78 
     79 # --- Utility Functions ---
     80 
     81 def cbtype(sr):
     82     """Returns the name of the callback structure type."""
     83     return f"{libxl}_{sr}_autogen_callbacks"
     84 
     85 def typeid(t):
     86     """Sanitizes a type name for use as a function suffix."""
     87     return re.sub(r'\W', '_', t)
     88 
     89 def f_decl(name, ah, c_rtype, c_decl, declprefix=''):
     90     """Declares a C function and starts its definition."""
     91     out_decls[name] = f"{declprefix}{c_rtype} {name}{c_decl};\n"
     92     func[name] = f"{c_rtype} {name}{c_decl}\n{{\n" + func.get(name, '')
     93     func_ah[name] = ah
     94 
     95 def f_more(name, addbody):
     96     """Appends to a C function's body."""
     97     func[name] = func.get(name, '') + addbody
     98     if name not in outfuncs:
     99         outfuncs.append(name)
    100 
    101 def generate_body_header(ah):
    102     """Generates the common includes/headers for the C file."""
    103     base_includes = textwrap.dedent("""\
    104         #include "libxl_osdeps.h"
    105         #include <assert.h>
    106         #include <string.h>
    107         #include <stdint.h>
    108         #include <limits.h>
    109     """)
    110     if ah == 'callout':
    111         return f"""{base_includes}
    112 #include "libxl_internal.h"
    113 """
    114     else: # helper
    115         return f"""{base_includes}
    116 #include <xenctrl.h>
    117 #include <xenguest.h>
    118 #include "_libxl_save_msgs_{ah}.h"
    119 """
    120 
    121 def generate_simple_type_get_put(simpletype):
    122     """Generates C functions for serializing/deserializing simple types."""
    123     type_id = typeid(simpletype)
    124 
    125     callout_get = textwrap.dedent(f"""\
    126         static int {type_id}_get(const unsigned char **msg,
    127                                 const unsigned char *const endmsg,
    128                                 {simpletype} *result)
    129         {{
    130             return bytes_get(msg, endmsg, result, sizeof(*result));
    131         }}
    132         """)
    133 
    134     helper_put = textwrap.dedent(f"""\
    135         static void {type_id}_put(unsigned char *const buf, int *len,
    136                                   const {simpletype} value)
    137         {{
    138             bytes_put(buf, len, &value, sizeof(value));
    139         }}
    140         """)
    141     return callout_get, helper_put
    142 
    143 # --- Main Logic ---
    144 
    145 if __name__ == "__main__":
    146     if len(sys.argv) != 2:
    147         sys.stderr.write("Usage: python3 script.py <output_file>\n")
    148         sys.exit(1)
    149 
    150     intendedout = sys.argv[1]
    151     match = re.match(r"([a-z]+)\.([ch])$", intendedout)
    152     if not match:
    153         sys.stderr.write(f"Invalid output file name: {intendedout}\n")
    154         sys.exit(1)
    155 
    156     want_ah, ch = match.groups()
    157 
    158     # 1. Generate base file contents
    159     for ah in ['callout', 'helper']:
    160         out_body[ah] = generate_body_header(ah)
    161 
    162     if want_ah not in out_body:
    163         sys.stderr.write(f"Unknown target: {want_ah}\n")
    164         sys.exit(1)
    165 
    166     # 2. Define base functions (sendreply, allocbuf, transmit, getreply)
    167     f_decl(sendreply, 'callout', 'void', "(int r, void *user)")
    168     f_decl(allocbuf, 'helper', 'unsigned char *', '(int len, void *user)')
    169     f_decl(transmit, 'helper', 'void', '(unsigned char *msg_freed, int len, void *user)')
    170     f_decl(getreply, 'helper', 'int', '(void *user)')
    171 
    172     # 3. Generate generic byte and special type functions (BLOCK, STRING)
    173     out_body['callout'] += textwrap.dedent("""\
    174         static int bytes_get(const unsigned char **msg,
    175                              const unsigned char *const endmsg,
    176                              void *result, int rlen)
    177         {
    178             if (endmsg - *msg < rlen) return 0;
    179             memcpy(result, *msg, rlen);
    180             *msg += rlen;
    181             return 1;
    182         }
    183 
    184         """)
    185 
    186     out_body['helper'] += textwrap.dedent("""\
    187         static void bytes_put(unsigned char *const buf, int *len,
    188                               const void *value, int vlen)
    189         {
    190             assert(vlen < INT_MAX/2 - *len);
    191             if (buf)
    192                 memcpy(buf + *len, value, vlen);
    193             *len += vlen;
    194         }
    195 
    196         """)
    197 
    198     # Simple types
    199     simple_types = ['int', 'uint16_t', 'uint32_t', 'unsigned', 'unsigned long', 'xen_pfn_t']
    200     for st in simple_types:
    201         callout_get, helper_put = generate_simple_type_get_put(st)
    202         out_body['callout'] += callout_get
    203         out_body['helper'] += helper_put
    204 
    205     # Complex types (BLOCK, STRING)
    206     out_body['callout'] += textwrap.dedent("""\
    207         static int BLOCK_get(const unsigned char **msg,
    208                               const unsigned char *const endmsg,
    209                               const uint8_t **result, uint32_t *result_size)
    210         {
    211             if (!uint32_t_get(msg, endmsg, result_size)) return 0;
    212             if (endmsg - *msg < *result_size) return 0;
    213             *result = (const void*)*msg;
    214             *msg += *result_size;
    215             return 1;
    216         }
    217 
    218         static int STRING_get(const unsigned char **msg,
    219                               const unsigned char *const endmsg,
    220                               const char **result)
    221         {
    222             const uint8_t *data;
    223             uint32_t datalen;
    224             if (!BLOCK_get(msg, endmsg, &data, &datalen)) return 0;
    225             if (datalen == 0) return 0;
    226             if (data[datalen-1] != '\\0') return 0;
    227             *result = (const void*)data;
    228             return 1;
    229         }
    230 
    231         """)
    232 
    233     out_body['helper'] += textwrap.dedent("""\
    234         static void BLOCK_put(unsigned char *const buf,
    235                               int *len,
    236                               const uint8_t *bytes, uint32_t size)
    237         {
    238             uint32_t_put(buf, len, size);
    239             bytes_put(buf, len, bytes, size);
    240         }
    241 
    242         static void STRING_put(unsigned char *const buf,
    243                                int *len,
    244                                const char *string)
    245         {
    246             size_t slen = strlen(string);
    247             assert(slen < INT_MAX / 4);
    248             assert(slen < (uint32_t)0x40000000);
    249             BLOCK_put(buf, len, (const void*)string, slen+1);
    250         }
    251 
    252         """)
    253 
    254     # 4. Generate save/restore specific functions (getcallbacks, receiveds, enumcallbacks, setcallbacks)
    255     for sr in ['save', 'restore']:
    256         f_decl(f"{getcallbacks}_{sr}", 'callout',
    257                f"const {cbtype(sr)} *", "(void *data)")
    258 
    259         f_decl(f"{receiveds}_{sr}", 'callout', 'int',
    260                "(const unsigned char *msg, uint32_t len, void *user)")
    261 
    262         f_decl(f"{enumcallbacks}_{sr}", 'callout', 'unsigned',
    263                f"(const {cbtype(sr)} *cbs)")
    264         f_more(f"{enumcallbacks}_{sr}", "    unsigned cbflags = 0;\n")
    265 
    266         f_decl(f"{setcallbacks}_{sr}", 'helper', 'void',
    267                f"(struct {sr}_callbacks *cbs, unsigned cbflags)")
    268 
    269         receiveds_body = textwrap.dedent("""\
    270             const unsigned char *const endmsg = msg + len;
    271             uint16_t mtype;
    272             if (!uint16_t_get(&msg, endmsg, &mtype)) return 0;
    273         """)
    274         if DEBUG:
    275             receiveds_body += f'    fprintf(stderr,"libxl callout receiver: got len=%u mtype=%u\\n",len,mtype);\n'
    276         receiveds_body += textwrap.dedent("""\
    277             switch (mtype) {
    278         """)
    279         f_more(f"{receiveds}_{sr}", receiveds_body)
    280 
    281         cbs[sr] = f"typedef struct {cbtype(sr)} {{\n"
    282 
    283     # 5. Loop through messages and generate code
    284     for msg in msgs:
    285         msgnum += 1
    286         flags = msg['flags']
    287         name = msg['name']
    288         args_list = msg['args']
    289 
    290         # Function to add code to the receiveds and other save/restore functions
    291         def f_more_sr(contents_spec, fnamebase=receiveds):
    292             for sr in ['save', 'restore']:
    293                 if sr[0] in flags: # Check if flag 's' or 'r' is present
    294                     contents = contents_spec(sr) if callable(contents_spec) else contents_spec
    295                     f_more(f"{fnamebase}_{sr}", contents)
    296 
    297         # Start of case block in receiveds
    298         f_more_sr(f"    case {msgnum}: {{ /* {name} */\n")
    299         if 'W' in flags:
    300             f_more_sr("        int r;\n")
    301 
    302         # C types for the generated functions
    303         c_rtype_helper = 'int' if any(f in flags for f in 'WA') else 'void'
    304         c_rtype_callout = 'int' if 'W' in flags else 'void'
    305         c_decl = '('
    306         c_callback_args = ''
    307 
    308         # Start of encode function body
    309         encode_body = textwrap.dedent(f"""\
    310             unsigned char *buf = 0;
    311             int len = 0, allocd = 0;
    312         """)
    313         if DEBUG:
    314             encode_body += f'    fprintf(stderr,"libxl-save-helper: encoding {name}\\n");\n'
    315         encode_body += textwrap.dedent("""\
    316             for (;;) {
    317                 uint16_t_put(buf, &len, msgnum);
    318         """)
    319         f_more(f"{encode}_{name}", encode_body)
    320 
    321         # Loop through arguments
    322         c_recv = ''
    323         arg_iter = iter(args_list)
    324         for argtype, arg in zip(arg_iter, arg_iter):
    325             type_id = typeid(argtype)
    326             c_args = arg
    327             c_get_args = f"&{arg}"
    328 
    329             if argtype == 'STRING':
    330                 c_decl += f"const char *{arg}, "
    331                 f_more_sr(f"        const char *{arg};\n")
    332             elif argtype == 'BLOCK':
    333                 c_decl += f"const uint8_t *{arg}, uint32_t {arg}_size, "
    334                 c_args += f", {arg}_size"
    335                 c_get_args += f", &{arg}_size"
    336                 f_more_sr(f"        const uint8_t *{arg};\n"
    337                           f"        uint32_t {arg}_size;\n")
    338             else:
    339                 c_decl += f"{argtype} {arg}, "
    340                 f_more_sr(f"        {argtype} {arg};\n")
    341 
    342             c_callback_args += f"{c_args}, "
    343             c_recv += f"        if (!{type_id}_get(&msg, endmsg, {c_get_args})) return 0;\n"
    344             f_more(f"{encode}_{name}", f"        {type_id}_put(buf, &len, {c_args});\n")
    345 
    346         # Finalize function signatures
    347         c_decl += "void *user)"
    348         c_callback_args += "user"
    349 
    350         # Finalize receiveds: argument decoding check
    351         f_more_sr(c_recv)
    352         f_more_sr("        if (msg != endmsg) return 0;\n")
    353 
    354         # Set up the callback dispatch
    355         c_callback = ''
    356         if 'c' not in flags:
    357             c_callback = f"{callback}_{name}"
    358         else:
    359             def cb_struct_update(sr):
    360                 cbs[sr] += f"    {c_rtype_callout} (*{name}){c_decl};\n"
    361                 return (
    362                     f"        const {cbtype(sr)} *const cbs =\n"
    363                     f"            {getcallbacks}_{sr}(user);\n"
    364                 )
    365             f_more_sr(cb_struct_update)
    366             c_callback = f"cbs->{name}"
    367 
    368         c_make_callback = f"{c_callback}({c_callback_args})"
    369 
    370         # Handle return value / reply
    371         if 'W' not in flags:
    372             f_more_sr(f"        {c_make_callback};\n")
    373         else:
    374             f_more_sr(f"        r = {c_make_callback};\n"
    375                       f"        {sendreply}(r, user);\n")
    376             f_decl(sendreply, 'callout', 'void', '(int r, void *user)') # Redundant, but harmless
    377 
    378         # Handle 'x' (null-ness check) flag for callbacks struct
    379         if 'x' in flags:
    380             c_v = f"(1u<<{msgnum})"
    381             c_cb = f"cbs->{name}"
    382             f_more_sr(f"    if ({c_cb}) cbflags |= {c_v};\n", enumcallbacks)
    383             f_more_sr(f"    if (cbflags & {c_v}) {c_cb} = {encode}_{name};\n", setcallbacks)
    384 
    385         # Finalize case block in receiveds
    386         f_more_sr("        return 1;\n    }\n\n")
    387 
    388         # Declare the generated functions
    389         f_decl(f"{callback}_{name}", 'callout', c_rtype_callout, c_decl)
    390         f_decl(f"{encode}_{name}", 'helper', c_rtype_helper, c_decl)
    391 
    392         # Finalize encode function body
    393         encode_finalize = textwrap.dedent(f"""\
    394             if (buf) break;
    395             buf = {allocbuf}(len, user);
    396             assert(buf);
    397             allocd = len;
    398             len = 0;
    399         }}
    400         assert(len == allocd);
    401         {transmit}(buf, len, user);
    402         """)
    403         f_more(f"{encode}_{name}", encode_finalize)
    404 
    405         # Add reply handling for helper (if W or A)
    406         if any(f in flags for f in 'WA'):
    407             reply_body = textwrap.dedent(f"""\
    408                 int r = {getreply}(user);
    409             """)
    410             if DEBUG:
    411                 reply_body += f'    fprintf(stderr,"libxl-save-helper: {name} got reply %d\\n",r);\n'
    412             reply_body += textwrap.dedent("""\
    413                 return r;
    414             """)
    415             f_more(f"{encode}_{name}", reply_body)
    416 
    417 
    418     # 6. Final output generation
    419     output = "/* AUTOGENERATED by libxl_save_msgs_gen.py DO NOT EDIT */\n\n"
    420 
    421     for sr in ['save', 'restore']:
    422         f_more(f"{enumcallbacks}_{sr}", "    return cbflags;\n")
    423         f_more(f"{receiveds}_{sr}",
    424                "    default:\n"
    425                "        return 0;\n"
    426                "    }\n")
    427         cbs[sr] += f"}} {cbtype(sr)};\n\n"
    428 
    429         if ch == 'h':
    430             output += cbs[sr]
    431             output += f"struct {sr}_callbacks;\n"
    432 
    433     if ch == 'c':
    434         for name in outfuncs:
    435             if name in func:
    436                 func[name] += "}\n\n"
    437                 out_body[func_ah[name]] += func[name]
    438                 del func[name]
    439         output += out_body[want_ah]
    440     else: # ch == 'h'
    441         for name in sorted(out_decls.keys()):
    442             if func_ah[name] == want_ah:
    443                 output += out_decls[name]
    444 
    445     sys.stdout.write(output)