1#!/usr/bin/perl -w
2
3use warnings;
4use strict;
5use POSIX;
6
7our $debug = 0; # produce copious debugging output at run-time?
8
9our @msgs = (
10    # flags:
11    #   s  - applicable to save
12    #   r  - applicable to restore
13    #   c  - function pointer in callbacks struct rather than fixed function
14    #   x  - function pointer is in struct {save,restore}_callbacks
15    #         and its null-ness needs to be passed through to the helper's xc
16    #   W  - needs a return value; callback is synchronous
17    #   A  - needs a return value; callback is asynchronous
18    [  1, 'sr',     "log",                   [qw(uint32_t level
19                                                 uint32_t errnoval
20                                                 STRING context
21                                                 STRING formatted)] ],
22    [  2, 'sr',     "progress",              [qw(STRING context
23                                                 STRING doing_what),
24                                                'unsigned long', 'done',
25                                                'unsigned long', 'total'] ],
26    [  3, 'srcxA',  "suspend", [] ],
27    [  4, 'srcxA',  "postcopy", [] ],
28    [  5, 'srcxA',  "checkpoint", [] ],
29    [  6, 'srcxA',  "wait_checkpoint", [] ],
30    [  7, 'scxA',   "switch_qemu_logdirty",  [qw(uint32_t domid
31                                              unsigned enable)] ],
32    [  8, 'rcx',    "restore_results",       ['xen_pfn_t', 'store_gfn',
33                                              'xen_pfn_t', 'console_gfn'] ],
34    [  9, 'srW',    "complete",              [qw(int retval
35                                                 int errnoval)] ],
36);
37
38#----------------------------------------
39
40our %cbs;
41our %func;
42our %func_ah;
43our @outfuncs;
44our %out_decls;
45our %out_body;
46our %msgnum_used;
47
48die unless @ARGV==1;
49die if $ARGV[0] =~ m/^-/;
50
51our ($intendedout) = @ARGV;
52
53$intendedout =~ m/([a-z]+)\.([ch])$/ or die;
54my ($want_ah, $ch) = ($1, $2);
55
56my $declprefix = '';
57
58foreach my $ah (qw(callout helper)) {
59    $out_body{$ah} .=
60        <<END_BOTH.($ah eq 'callout' ? <<END_CALLOUT : <<END_HELPER);
61#include "libxl_osdeps.h"
62
63#include <assert.h>
64#include <string.h>
65#include <stdint.h>
66#include <limits.h>
67END_BOTH
68
69#include "libxl_internal.h"
70
71END_CALLOUT
72
73#include <xenctrl.h>
74#include <xenguest.h>
75#include "_libxl_save_msgs_${ah}.h"
76
77END_HELPER
78}
79
80die $want_ah unless defined $out_body{$want_ah};
81
82sub f_decl ($$$$) {
83    my ($name, $ah, $c_rtype, $c_decl) = @_;
84    $out_decls{$name} = "${declprefix}$c_rtype $name$c_decl;\n";
85    $func{$name} = "$c_rtype $name$c_decl\n{\n" . ($func{$name} || '');
86    $func_ah{$name} = $ah;
87}
88
89sub f_more ($$) {
90    my ($name, $addbody) = @_;
91    $func{$name} ||= '';
92    $func{$name} .= $addbody;
93    push @outfuncs, $name;
94}
95
96our $libxl = "libxl__srm";
97our $callback = "${libxl}_callout_callback";
98our $receiveds = "${libxl}_callout_received";
99our $sendreply = "${libxl}_callout_sendreply";
100our $getcallbacks = "${libxl}_callout_get_callbacks";
101our $enumcallbacks = "${libxl}_callout_enumcallbacks";
102sub cbtype ($) { "${libxl}_".$_[0]."_autogen_callbacks"; };
103
104f_decl($sendreply, 'callout', 'void', "(int r, void *user)");
105
106our $helper = "helper";
107our $encode = "${helper}_stub";
108our $allocbuf = "${helper}_allocbuf";
109our $transmit = "${helper}_transmitmsg";
110our $getreply = "${helper}_getreply";
111our $setcallbacks = "${helper}_setcallbacks";
112
113f_decl($allocbuf, 'helper', 'unsigned char *', '(int len, void *user)');
114f_decl($transmit, 'helper', 'void',
115       '(unsigned char *msg_freed, int len, void *user)');
116f_decl($getreply, 'helper', 'int', '(void *user)');
117
118sub typeid ($) { my ($t) = @_; $t =~ s/\W/_/; return $t; };
119
120$out_body{'callout'} .= <<END;
121static int bytes_get(const unsigned char **msg,
122		     const unsigned char *const endmsg,
123		     void *result, int rlen)
124{
125    if (endmsg - *msg < rlen) return 0;
126    memcpy(result,*msg,rlen);
127    *msg += rlen;
128    return 1;
129}
130
131END
132$out_body{'helper'} .= <<END;
133static void bytes_put(unsigned char *const buf, int *len,
134		      const void *value, int vlen)
135{
136    assert(vlen < INT_MAX/2 - *len);
137    if (buf)
138	memcpy(buf + *len, value, vlen);
139    *len += vlen;
140}
141
142END
143
144foreach my $simpletype (qw(int uint16_t uint32_t unsigned), 'unsigned long', 'xen_pfn_t') {
145    my $typeid = typeid($simpletype);
146    $out_body{'callout'} .= <<END;
147static int ${typeid}_get(const unsigned char **msg,
148                        const unsigned char *const endmsg,
149                        $simpletype *result)
150{
151    return bytes_get(msg, endmsg, result, sizeof(*result));
152}
153
154END
155    $out_body{'helper'} .= <<END;
156static void ${typeid}_put(unsigned char *const buf, int *len,
157			 const $simpletype value)
158{
159    bytes_put(buf, len, &value, sizeof(value));
160}
161
162END
163}
164
165$out_body{'callout'} .= <<END;
166static int BLOCK_get(const unsigned char **msg,
167                      const unsigned char *const endmsg,
168                      const uint8_t **result, uint32_t *result_size)
169{
170    if (!uint32_t_get(msg,endmsg,result_size)) return 0;
171    if (endmsg - *msg < *result_size) return 0;
172    *result = (const void*)*msg;
173    *msg += *result_size;
174    return 1;
175}
176
177static int STRING_get(const unsigned char **msg,
178                      const unsigned char *const endmsg,
179                      const char **result)
180{
181    const uint8_t *data;
182    uint32_t datalen;
183    if (!BLOCK_get(msg,endmsg,&data,&datalen)) return 0;
184    if (datalen == 0) return 0;
185    if (data[datalen-1] != '\\0') return 0;
186    *result = (const void*)data;
187    return 1;
188}
189
190END
191$out_body{'helper'} .= <<END;
192static void BLOCK_put(unsigned char *const buf,
193                      int *len,
194		      const uint8_t *bytes, uint32_t size)
195{
196    uint32_t_put(buf, len, size);
197    bytes_put(buf, len, bytes, size);
198}
199
200static void STRING_put(unsigned char *const buf,
201		       int *len,
202		       const char *string)
203{
204    size_t slen = strlen(string);
205    assert(slen < INT_MAX / 4);
206    assert(slen < (uint32_t)0x40000000);
207    BLOCK_put(buf, len, (const void*)string, slen+1);
208}
209
210END
211
212foreach my $sr (qw(save restore)) {
213    f_decl("${getcallbacks}_${sr}", 'callout',
214           "const ".cbtype($sr)." *",
215           "(void *data)");
216
217    f_decl("${receiveds}_${sr}", 'callout', 'int',
218	   "(const unsigned char *msg, uint32_t len, void *user)");
219
220    f_decl("${enumcallbacks}_${sr}", 'callout', 'unsigned',
221           "(const ".cbtype($sr)." *cbs)");
222    f_more("${enumcallbacks}_${sr}", "    unsigned cbflags = 0;\n");
223
224    f_decl("${setcallbacks}_${sr}", 'helper', 'void',
225           "(struct ${sr}_callbacks *cbs, unsigned cbflags)");
226
227    f_more("${receiveds}_${sr}",
228           <<END_ALWAYS.($debug ? <<END_DEBUG : '').<<END_ALWAYS);
229    const unsigned char *const endmsg = msg + len;
230    uint16_t mtype;
231    if (!uint16_t_get(&msg,endmsg,&mtype)) return 0;
232END_ALWAYS
233    fprintf(stderr,"libxl callout receiver: got len=%u mtype=%u\\n",len,mtype);
234END_DEBUG
235    switch (mtype) {
236
237END_ALWAYS
238
239    $cbs{$sr} = "typedef struct ".cbtype($sr)." {\n";
240}
241
242foreach my $msginfo (@msgs) {
243    my ($msgnum, $flags, $name, $args) = @$msginfo;
244    die if $msgnum_used{$msgnum}++;
245
246    my $f_more_sr = sub {
247        my ($contents_spec, $fnamebase) = @_;
248        $fnamebase ||= "${receiveds}";
249        foreach my $sr (qw(save restore)) {
250            $sr =~ m/^./;
251            next unless $flags =~ m/$&/;
252            my $contents = (!ref $contents_spec) ? $contents_spec :
253                $contents_spec->($sr);
254            f_more("${fnamebase}_${sr}", $contents);
255        }
256    };
257
258    $f_more_sr->("    case $msgnum: { /* $name */\n");
259    if ($flags =~ m/W/) {
260        $f_more_sr->("        int r;\n");
261    }
262
263    my $c_rtype_helper = $flags =~ m/[WA]/ ? 'int' : 'void';
264    my $c_rtype_callout = $flags =~ m/W/ ? 'int' : 'void';
265    my $c_decl = '(';
266    my $c_callback_args = '';
267
268    f_more("${encode}_$name",
269           <<END_ALWAYS.($debug ? <<END_DEBUG : '').<<END_ALWAYS);
270    unsigned char *buf = 0;
271    int len = 0, allocd = 0;
272
273END_ALWAYS
274    fprintf(stderr,"libxl-save-helper: encoding $name\\n");
275END_DEBUG
276    for (;;) {
277        uint16_t_put(buf, &len, $msgnum /* $name */);
278END_ALWAYS
279
280    my @args = @$args;
281    my $c_recv = '';
282    my ($argtype, $arg);
283    while (($argtype, $arg, @args) = @args) {
284	my $typeid = typeid($argtype);
285        my $c_args = "$arg";
286        my $c_get_args = "&$arg";
287	if ($argtype eq 'STRING') {
288	    $c_decl .= "const char *$arg, ";
289	    $f_more_sr->("        const char *$arg;\n");
290        } elsif ($argtype eq 'BLOCK') {
291            $c_decl .= "const uint8_t *$arg, uint32_t ${arg}_size, ";
292            $c_args .= ", ${arg}_size";
293            $c_get_args .= ",&${arg}_size";
294	    $f_more_sr->("        const uint8_t *$arg;\n".
295                         "        uint32_t ${arg}_size;\n");
296	} else {
297	    $c_decl .= "$argtype $arg, ";
298	    $f_more_sr->("        $argtype $arg;\n");
299	}
300	$c_callback_args .= "$c_args, ";
301	$c_recv.=
302            "        if (!${typeid}_get(&msg,endmsg,$c_get_args)) return 0;\n";
303        f_more("${encode}_$name", "	${typeid}_put(buf, &len, $c_args);\n");
304    }
305    $f_more_sr->($c_recv);
306    $c_decl .= "void *user)";
307    $c_callback_args .= "user";
308
309    $f_more_sr->("        if (msg != endmsg) return 0;\n");
310
311    my $c_callback;
312    if ($flags !~ m/c/) {
313        $c_callback = "${callback}_$name";
314    } else {
315        $f_more_sr->(sub {
316            my ($sr) = @_;
317            $cbs{$sr} .= "    $c_rtype_callout (*${name})$c_decl;\n";
318            return
319          "        const ".cbtype($sr)." *const cbs =\n".
320            "            ${getcallbacks}_${sr}(user);\n";
321                       });
322        $c_callback = "cbs->${name}";
323    }
324    my $c_make_callback = "$c_callback($c_callback_args)";
325    if ($flags !~ m/W/) {
326	$f_more_sr->("        $c_make_callback;\n");
327    } else {
328        $f_more_sr->("        r = $c_make_callback;\n".
329                     "        $sendreply(r, user);\n");
330	f_decl($sendreply, 'callout', 'void', '(int r, void *user)');
331    }
332    if ($flags =~ m/x/) {
333        my $c_v = "(1u<<$msgnum)";
334        my $c_cb = "cbs->$name";
335        $f_more_sr->("    if ($c_cb) cbflags |= $c_v;\n", $enumcallbacks);
336        $f_more_sr->("    $c_cb = (cbflags & $c_v) ? ${encode}_${name} : 0;\n",
337                     $setcallbacks);
338    }
339    $f_more_sr->("        return 1;\n    }\n\n");
340    f_decl("${callback}_$name", 'callout', $c_rtype_callout, $c_decl);
341    f_decl("${encode}_$name", 'helper', $c_rtype_helper, $c_decl);
342    f_more("${encode}_$name",
343"        if (buf) break;
344        buf = ${helper}_allocbuf(len, user);
345        assert(buf);
346        allocd = len;
347        len = 0;
348    }
349    assert(len == allocd);
350    ${transmit}(buf, len, user);
351");
352    if ($flags =~ m/[WA]/) {
353	f_more("${encode}_$name",
354               (<<END_ALWAYS.($debug ? <<END_DEBUG : '').<<END_ALWAYS));
355    int r = ${helper}_getreply(user);
356END_ALWAYS
357    fprintf(stderr,"libxl-save-helper: $name got reply %d\\n",r);
358END_DEBUG
359    return r;
360END_ALWAYS
361    }
362}
363
364print "/* AUTOGENERATED by $0 DO NOT EDIT */\n\n" or die $!;
365
366foreach my $sr (qw(save restore)) {
367    f_more("${enumcallbacks}_${sr}",
368           "    return cbflags;\n");
369    f_more("${receiveds}_${sr}",
370           "    default:\n".
371           "        return 0;\n".
372           "    }");
373    $cbs{$sr} .= "} ".cbtype($sr).";\n\n";
374    if ($ch eq 'h') {
375        print $cbs{$sr} or die $!;
376        print "struct ${sr}_callbacks;\n";
377    }
378}
379
380if ($ch eq 'c') {
381    foreach my $name (@outfuncs) {
382        next unless defined $func{$name};
383        $func{$name} .= "}\n\n";
384        $out_body{$func_ah{$name}} .= $func{$name};
385        delete $func{$name};
386    }
387    print $out_body{$want_ah} or die $!;
388} else {
389    foreach my $name (sort keys %out_decls) {
390        next unless $func_ah{$name} eq $want_ah;
391        print $out_decls{$name} or die $!;
392    }
393}
394
395close STDOUT or die $!;
396