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