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)