1# awk script to generate hypercall handler prototypes and a macro for doing
2# the calls of the handlers inside a switch() statement.
3
4BEGIN {
5    printf("/* Generated file, do not edit! */\n\n");
6    e = 0;
7    n = 0;
8    p = 0;
9    nc = 0;
10}
11
12# Issue error to stderr
13function do_err(msg) {
14    print "Error: "msg": "$0 >"/dev/stderr";
15    exit 1;
16}
17
18# Generate handler call
19function do_call(f, p,    i) {
20    printf("            ret = %s_%s(", pre[f, p], fn[f]);
21    for (i = 1; i <= n_args[f]; i++) {
22        if (i > 1)
23            printf(", ");
24        if (ptr[f, i])
25            printf("(XEN_GUEST_HANDLE_PARAM(%s)){ _p(a%d) }", typ[f, i], i);
26        else
27            printf("(%s)(a%d)", typ[f, i], i);
28    }
29    printf("); \\\n");
30}
31
32# Generate case statement for call
33function do_case(f, p) {
34    printf("        case __HYPERVISOR_%s: \\\n", fn[f]);
35    do_call(f, p);
36    printf("            break; \\\n");
37}
38
39# Generate switch statement for calling handlers
40function do_switch(ca, p,    i) {
41    printf("        switch ( num ) \\\n");
42    printf("        { \\\n");
43    for (i = 1; i <= nc; i++)
44        if (call[i] == ca && call_prio[i] == p)
45            do_case(call_fn[i], call_p[i]);
46    printf("        default: \\\n");
47    printf("            ret = -ENOSYS; \\\n");
48    printf("            break; \\\n");
49    printf("        } \\\n");
50}
51
52function rest_of_line(par,    i, val) {
53    val = $(par);
54    for (i = par + 1; i <= NF; i++)
55        val = val " " $(i);
56    return val;
57}
58
59# Handle comments (multi- and single line)
60$1 == "/*" {
61    comment = 1;
62}
63comment == 1 {
64    if ($(NF) == "*/") comment = 0;
65    next;
66}
67
68# Skip preprocessing artefacts
69$1 == "extern" {
70    next;
71}
72/^#/ {
73    next;
74}
75
76# Drop empty lines
77NF == 0 {
78    next;
79}
80
81# Handle "handle:" line
82$1 == "handle:" {
83    if (NF < 3)
84        do_err("\"handle:\" requires at least two parameters");
85    val = rest_of_line(3);
86    xlate[val] = $2;
87    next;
88}
89
90# Handle "defhandle:" line
91$1 == "defhandle:" {
92    if (NF < 2)
93        do_err("\"defhandle:\" requires at least one parameter");
94    e++;
95    if (NF == 2) {
96        emit[e] = sprintf("DEFINE_XEN_GUEST_HANDLE(%s);", $2);
97    } else {
98        val = rest_of_line(3);
99        emit[e] = sprintf("__DEFINE_XEN_GUEST_HANDLE(%s, %s);", $2, val);
100        xlate[val] = $2;
101    }
102    next;
103}
104
105# Handle "rettype:" line
106$1 == "rettype:" {
107    if (NF < 3)
108        do_err("\"rettype:\" requires at least two parameters");
109    if ($2 in rettype)
110        do_err("rettype can be set only once for each prefix");
111    rettype[$2] = rest_of_line(3);
112    next;
113}
114
115# Handle "caller:" line
116$1 == "caller:" {
117    caller[$2] = 1;
118    next;
119}
120
121# Handle "prefix:" line
122$1 == "prefix:" {
123    p = NF - 1;
124    for (i = 2; i <= NF; i++) {
125        prefix[i - 1] = $(i);
126        if (!(prefix[i - 1] in rettype))
127            rettype[prefix[i - 1]] = "long";
128    }
129    next;
130}
131
132# Handle "table:" line
133$1 == "table:" {
134    table = 1;
135    for (i = 2; i <= NF; i++)
136        col[i - 1] = $(i);
137    n_cols = NF - 1;
138    next;
139}
140
141# Handle table definition line
142table == 1 {
143    if (NF != n_cols + 1)
144        do_err("Table definition line has wrong number of fields");
145    for (c = 1; c <= n_cols; c++) {
146        if (caller[col[c]] != 1)
147            continue;
148        if ($(c + 1) == "-")
149            continue;
150        pref = $(c + 1);
151        idx = index(pref, ":");
152        if (idx == 0)
153            prio = 100;
154        else {
155            prio = substr(pref, idx + 1) + 0;
156            pref = substr(pref, 1, idx - 1);
157            if (prio >= 100 || prio < 1)
158                do_err("Priority must be in the range 1..99");
159        }
160        fnd = 0;
161        for (i = 1; i <= n; i++) {
162            if (fn[i] != $1)
163                continue;
164            for (j = 1; j <= n_pre[i]; j++) {
165                if (pre[i, j] == pref) {
166                    prios[col[c], prio]++;
167                    if (prios[col[c], prio] == 1) {
168                        n_prios[col[c]]++;
169                        prio_list[col[c], n_prios[col[c]]] = prio;
170                        prio_mask[col[c], prio] = "(1ULL << __HYPERVISOR_"$1")";
171                    } else
172                        prio_mask[col[c], prio] = prio_mask[col[c], prio] " | (1ULL << __HYPERVISOR_"$1")";
173                    nc++;
174                    call[nc] = col[c];
175                    call_fn[nc] = i;
176                    call_p[nc] = j;
177                    call_prio[nc] = prio;
178                    fnd = 1;
179                }
180            }
181        }
182        if (fnd == 0)
183            do_err("No prototype for prefix/hypercall combination");
184    }
185    next;
186}
187
188# Prototype line
189{
190    bro = index($0, "(");
191    brc = index($0, ")");
192    if (bro < 2 || brc < bro)
193        do_err("No valid prototype line");
194    n++;
195    fn[n] = substr($0, 1, bro - 1);
196    n_pre[n] = p;
197    for (i = 1; i <= p; i++)
198        pre[n, i] = prefix[i];
199    args = substr($0, bro + 1, brc - bro - 1);
200    n_args[n] = split(args, a, ",");
201    if (n_args[n] > 5)
202        do_err("Too many parameters");
203    for (i = 1; i <= n_args[n]; i++) {
204        sub("^ *", "", a[i]);         # Remove leading white space
205        sub(" +", " ", a[i]);         # Replace multiple spaces with single ones
206        sub(" *$", "", a[i]);         # Remove trailing white space
207        ptr[n, i] = index(a[i], "*"); # Is it a pointer type?
208        sub("[*]", "", a[i]);         # Remove "*"
209        if (index(a[i], " ") == 0)
210            do_err("Parameter with no type or no name");
211        typ[n, i] = a[i];
212        sub(" [^ ]+$", "", typ[n, i]);    # Remove parameter name
213        if (ptr[n, i] && (typ[n, i] in xlate))
214            typ[n, i] = xlate[typ[n, i]];
215        arg[n, i] = a[i];
216        sub("^([^ ]+ )+", "", arg[n, i]); # Remove parameter type
217    }
218}
219
220# Generate the output
221END {
222    # Verbatim generated lines
223    for (i = 1; i <= e; i++)
224        printf("%s\n", emit[i]);
225    printf("\n");
226    # Generate prototypes
227    for (i = 1; i <= n; i++) {
228        for (p = 1; p <= n_pre[i]; p++) {
229            printf("%s %s_%s(", rettype[pre[i, p]], pre[i, p], fn[i]);
230            if (n_args[i] == 0)
231                printf("void");
232            else
233                for (j = 1; j <= n_args[i]; j++) {
234                    if (j > 1)
235                        printf(", ");
236                    if (ptr[i, j])
237                        printf("XEN_GUEST_HANDLE_PARAM(%s)", typ[i, j]);
238                    else
239                        printf("%s", typ[i, j]);
240                    printf(" %s", arg[i, j]);
241                }
242            printf(");\n");
243        }
244    }
245    # Generate call sequences and args array contents
246    for (ca in caller) {
247        if (caller[ca] != 1)
248            continue;
249        need_mask = 0;
250        for (pl = 1; pl <= n_prios[ca]; pl++) {
251            for (pll = pl; pll > 1; pll--) {
252                if (prio_list[ca, pl] > p_list[pll - 1])
253                    break;
254                else
255                    p_list[pll] = p_list[pll - 1];
256            }
257            p_list[pll] = prio_list[ca, pl];
258            # If any prio but the default one has more than 1 entry we need "mask"
259            if (p_list[pll] != 100 && prios[ca, p_list[pll]] > 1)
260                need_mask = 1;
261        }
262        printf("\n");
263        printf("#define call_handlers_%s(num, ret, a1, a2, a3, a4, a5) \\\n", ca);
264        printf("({ \\\n");
265        if (need_mask)
266            printf("    uint64_t mask = (num) > 63 ? 0 : 1ULL << (num); \\\n");
267        printf("    ");
268        for (pl = 1; pl <= n_prios[ca]; pl++) {
269            if (prios[ca, p_list[pl]] > 1) {
270                if (pl < n_prios[ca]) {
271                    printf("    if ( likely(mask & (%s)) ) \\\n", prio_mask[ca, p_list[pl]]);
272                    printf("    { \\\n");
273                }
274                if (prios[ca, p_list[pl]] == 2) {
275                    fnd = 0;
276                    for (i = 1; i <= nc; i++)
277                        if (call[i] == ca && call_prio[i] == p_list[pl]) {
278                            fnd++;
279                            if (fnd == 1)
280                                printf("        if ( (num) == __HYPERVISOR_%s ) \\\n", fn[call_fn[i]]);
281                            else
282                                printf("        else \\\n");
283                            do_call(call_fn[i], call_p[i]);
284                        }
285                } else {
286                    do_switch(ca, p_list[pl]);
287                }
288                if (pl < n_prios[ca])
289                    printf("    } \\\n");
290            } else {
291                for (i = 1; i <= nc; i++)
292                    if (call[i] == ca && call_prio[i] == p_list[pl]) {
293                        printf("if ( likely((num) == __HYPERVISOR_%s) ) \\\n", fn[call_fn[i]]);
294                        do_call(call_fn[i], call_p[i]);
295                    }
296            }
297            if (pl < n_prios[ca] || prios[ca, p_list[pl]] <= 2)
298                printf("    else \\\n");
299        }
300        if (prios[ca, p_list[n_prios[ca]]] <= 2) {
301            printf("\\\n");
302            printf("        ret = -ENOSYS; \\\n");
303        }
304        printf("})\n");
305        delete p_list;
306        printf("\n");
307        printf("#define hypercall_args_%s \\\n", ca);
308        printf("{ \\\n");
309        for (i = 1; i <= nc; i++)
310            if (call[i] == ca)
311                printf("[__HYPERVISOR_%s] = %d, \\\n", fn[call_fn[i]], n_args[call_fn[i]]);
312        printf("}\n");
313    }
314}
315