aboutgitcodelistschat:MatrixIRC
path: root/cooker/call.c
diff options
context:
space:
mode:
authorStefano Brivio <sbrivio@redhat.com>2023-06-01 11:04:38 +0200
committerStefano Brivio <sbrivio@redhat.com>2023-06-01 11:10:27 +0200
commit1644bbec6161ec971a2ba3c213ce285b995cac22 (patch)
tree75974577237f8ae1a0989755be0a953c631f794e /cooker/call.c
parentbb47d77d316137c9deddd46135b22dc144ae1ea9 (diff)
downloadseitan-1644bbec6161ec971a2ba3c213ce285b995cac22.tar
seitan-1644bbec6161ec971a2ba3c213ce285b995cac22.tar.gz
seitan-1644bbec6161ec971a2ba3c213ce285b995cac22.tar.bz2
seitan-1644bbec6161ec971a2ba3c213ce285b995cac22.tar.lz
seitan-1644bbec6161ec971a2ba3c213ce285b995cac22.tar.xz
seitan-1644bbec6161ec971a2ba3c213ce285b995cac22.tar.zst
seitan-1644bbec6161ec971a2ba3c213ce285b995cac22.zip
cooker: OP_CALL and OP_COPY stuff
...mostly. Signed-off-by: Stefano Brivio <sbrivio@redhat.com>
Diffstat (limited to 'cooker/call.c')
-rw-r--r--cooker/call.c424
1 files changed, 424 insertions, 0 deletions
diff --git a/cooker/call.c b/cooker/call.c
new file mode 100644
index 0000000..e1abb39
--- /dev/null
+++ b/cooker/call.c
@@ -0,0 +1,424 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+/* seitan - Syscall Expressive Interpreter, Transformer and Notifier
+ *
+ * cooker/call.c - Parse "call" rules from JSON recipe into bytecode
+ *
+ * Copyright 2023 Red Hat GmbH
+ * Author: Stefano Brivio <sbrivio@redhat.com>
+ */
+
+#include "parson.h"
+#include "calls.h"
+#include "cooker.h"
+#include "gluten.h"
+#include "emit.h"
+#include "parse.h"
+#include "util.h"
+
+/**
+
+struct syscall_desc {
+ unsigned nr:9;
+
+ unsigned arg_count:3;
+ unsigned has_ret:1;
+
+ unsigned arg_deref:6;
+
+ struct gluten_offset data[];
+};
+
+match: "tag": "x" -- meaning "set"
+call: "tag": { "set": "x", "get": "y" }
+
+Examples of arguments:
+- INT: 2 write 2 in ro_data, and pointer to it
+ parse_arg() passes null offset
+ parse_field() passes back ro_data offset
+
+- INT *: 2 write 2 in ro_data (using size), and pointer to it
+ no flags or tags parse_arg() passes ro_data offset
+
+- INT *: 2 - write 2 in ro_data at x
+ COPY_ON_CALL - emit op_copy from x to y
+ or "set" tag - write pointer to y
+ parse_arg() passes data offset
+
+- INT: "get" <tag> write pointer to tag
+ parse_arg() passes null offset
+ parse_field() gives back ro_data or data offset
+
+- INT *: "get" <tag> write pointer to tag
+ no COPY_ON_CALL parse_arg() passes null offset
+ parse_field() gives back data offset
+
+- STRING: abcd write abcd to ro_data (using size), and pointer to it
+ parse_arg() passes ro_data offset
+
+- STRUCT: 1, 2 write struct to ro_data, and pointer to it
+ parse_tag() passes ro_data offset
+
+- STRUCT: "get" <tag> write pointer to tag
+ parse_arg() passes null offset
+ parse_field() gives back data offset
+
+- STRUCT: <tag>, 2 - write 2 to ro_data at x
+ - allocate rw_data at y
+ - emit op_copy from tag to y
+ - emit op_copy from x to y + offset of "2"
+ - write pointer to y
+ parse_arg() passes data offset
+ */
+
+static union value parse_field(struct gluten_ctx *g,
+ struct gluten_offset *base_offset,
+ int index, struct field *f, JSON_Value *jvalue,
+ bool dry_run, bool add)
+{
+ struct gluten_offset offset = *base_offset;
+ union value v = { .v_num = 0 };
+ JSON_Object *tmp1, *tmp2;
+ struct field *f_inner;
+ JSON_Value *sel;
+
+ if (f->name)
+ debug(" parsing field name %s", f->name);
+
+ if (offset.type != OFFSET_NULL)
+ offset.offset += f->offset;
+
+ if (json_value_get_type(jvalue) == JSONObject &&
+ (tmp1 = json_value_get_object(jvalue)) &&
+ (tmp2 = json_object_get_object(tmp1, "tag"))) {
+ const char *tag_set, *tag_get;
+ size_t count = 0;
+
+ if ((tag_set = json_object_get_string(tmp2, "set"))) {
+ count++;
+ debug(" setting tag reference (post) '%s'", tag_set);
+
+ if (!dry_run)
+ gluten_add_tag_post(g, tag_set, offset);
+ }
+
+ if ((tag_get = json_object_get_string(tmp2, "get"))) {
+ struct gluten_offset tag_offset;
+
+ count++;
+ debug(" getting tag reference '%s'", tag_get);
+
+ /* TODO: Check type */
+ tag_offset = gluten_get_tag(g, tag_get);
+ if (tag_offset.type == OFFSET_NULL)
+ die(" tag not found");
+
+ if (base_offset->type == OFFSET_NULL)
+ *base_offset = tag_offset;
+ else
+ emit_copy_field(g, f, offset, tag_offset);
+ }
+
+ if (json_object_get_count(tmp2) > count)
+ die("stray object in tag reference");
+
+ if (!count)
+ die("invalid tag specification");
+
+ jvalue = json_object_get_value(tmp1, "value");
+
+ if (tag_get) {
+ if (jvalue)
+ die("stray value with \"get\" tag");
+
+ return v;
+ }
+ }
+
+ if (!jvalue)
+ return v;
+
+ switch (f->type) {
+ case INT:
+ case LONG:
+ case U32:
+ if (f->flags == FLAGS) {
+ /* fetch/combine expr algebra loop */
+ ;
+ }
+ if (f->flags == MASK) {
+ /* calculate mask first */
+ ;
+ }
+
+ v.v_num = value_get_num(f->desc.d_num, jvalue);
+ if (dry_run)
+ break;
+
+ if (base_offset->type == OFFSET_NULL)
+ *base_offset = emit_data(g, f->type, 0, &v);
+ else if (add)
+ emit_data_or(g, offset, f->type, &v);
+ else
+ emit_data_at(g, offset, f->type, &v);
+
+ break;
+ case SELECT:
+ f_inner = f->desc.d_select->field;
+
+ if ((tmp1 = json_value_get_object(jvalue))) {
+ if (!(sel = json_object_get_value(tmp1, f_inner->name)))
+ die(" no selector for '%s'", f_inner->name);
+ } else {
+ sel = jvalue;
+ }
+
+ v = parse_field(g, &offset, index, f_inner, sel, false, false);
+
+ f = select_field(g, index, f->desc.d_select, v);
+ if (f)
+ parse_field(g, &offset, index, f, jvalue, false, add);
+ break;
+ case STRING:
+ v.v_str = json_value_get_string(jvalue);
+ if (strlen(v.v_str) + 1 > f->size)
+ die(" string %s too long for field", v.v_str);
+
+ if (dry_run)
+ break;
+
+ emit_data_at(g, offset, f->type, &v);
+ break;
+ case STRUCT:
+ for (f_inner = f->desc.d_struct; f_inner->name; f_inner++) {
+ JSON_Value *f_value;
+
+ tmp1 = json_value_get_object(jvalue);
+ f_value = json_object_get_value(tmp1, f_inner->name);
+ if (!f_value)
+ continue;
+
+ parse_field(g, &offset, index, f_inner, f_value, false,
+ add);
+ }
+ break;
+ default:
+ ;
+ }
+
+ return v;
+}
+
+bool arg_needs_temp(struct field *f, int pos, JSON_Value *jvalue,
+ bool *top_level_tag, int level)
+{
+ struct gluten_offset unused = { .type = OFFSET_NULL, .offset = 0 };
+ union value v = { .v_num = 0 };
+ struct field *f_inner;
+ JSON_Object *tmp;
+ JSON_Value *sel;
+
+ if (f->flags & COPY_ON_CALL)
+ return true;
+
+ if (f->flags & SIZE)
+ return false;
+
+ if (json_value_get_type(jvalue) == JSONObject &&
+ (tmp = json_value_get_object(jvalue)) &&
+ (tmp = json_object_get_object(tmp, "tag"))) {
+ if (json_object_get_string(tmp, "set"))
+ return true;
+
+ if (level)
+ return true;
+
+ if (top_level_tag)
+ *top_level_tag = true;
+
+ return false;
+ }
+
+ switch (f->type) {
+ case INT:
+ case LONG:
+ case U32:
+ return false;
+ case SELECT:
+ f_inner = f->desc.d_select->field;
+ if (arg_needs_temp(f_inner, pos, jvalue, top_level_tag, level))
+ return true;
+
+ if ((tmp = json_value_get_object(jvalue))) {
+ if (!(sel = json_object_get_value(tmp, f_inner->name)))
+ die(" no selector for '%s'", f_inner->name);
+ } else {
+ sel = jvalue;
+ }
+
+ v = parse_field(NULL, &unused, pos, f_inner, sel, true, false);
+
+ f = select_field(NULL, pos, f->desc.d_select, v);
+ if (f)
+ return arg_needs_temp(f, pos, jvalue, NULL, level + 1);
+
+ return false;
+ case STRING:
+ return false;
+ case STRUCT:
+ for (f_inner = f->desc.d_struct; f_inner->name; f_inner++) {
+ JSON_Value *f_value;
+
+ tmp = json_value_get_object(jvalue);
+ f_value = json_object_get_value(tmp, f_inner->name);
+ if (!f_value)
+ continue;
+
+ if (arg_needs_temp(f, pos, f_value, NULL, level + 1))
+ return true;
+ }
+
+ return false;
+ default:
+ ;
+ }
+
+ return false;
+}
+
+static struct gluten_offset parse_arg(struct gluten_ctx *g, struct arg *a,
+ struct gluten_offset offset,
+ JSON_Value *jvalue)
+{
+ bool top_level_tag = false;
+
+ debug(" Parsing call argument %s", a->f.name);
+
+ if (offset.type != OFFSET_NULL) {
+ parse_field(g, &offset, a->pos, &a->f, jvalue, false, true);
+ return offset;
+ }
+
+ if (arg_needs_temp(&a->f, a->pos, jvalue, &top_level_tag, 0))
+ offset = gluten_rw_alloc(g, a->f.size);
+ else if (a->f.size && !top_level_tag)
+ offset = gluten_ro_alloc(g, a->f.size);
+
+ parse_field(g, &offset, a->pos, &a->f, jvalue, false, false);
+
+ return offset;
+}
+
+static void parse_call(struct gluten_ctx *g, JSON_Object *obj, const char *ret,
+ struct arg *args)
+{
+ struct gluten_offset offset[6] = { 0 }, ret_offset = { 0 };
+ /* Minimum requirements for argument specification:
+ * - if argument can be FDPATH, exactly one value for that position
+ * - if argument is a size field, value is optional
+ * - otherwise, every argument needs to be specified
+ */
+ struct {
+ bool needs_fd;
+ bool has_fd;
+ } arg_check[6] = { 0 };
+ unsigned count = 0;
+ struct arg *a;
+
+ /* Set requirements first */
+ for (a = args; a->f.name; a++) {
+ if (a->f.type == SELECTED) {
+ if (!g->selected_arg[a->pos])
+ die(" No argument selected for %s", a->f.name);
+ a = g->selected_arg[a->pos];
+ }
+
+ if (a->f.type == FDPATH)
+ arg_check[a->pos].needs_fd = true;
+ }
+
+ /* TODO: Factor this out into a function in... parse.c? */
+ for (a = args; a->f.name; a++) {
+ JSON_Value *jvalue;
+
+ if (a->f.type == SELECTED)
+ a = g->selected_arg[a->pos];
+
+ /* Not common with parse_match(), though */
+ if ((jvalue = json_object_get_value(obj, a->f.name))) {
+ if (arg_check[a->pos].has_fd)
+ die(" Conflicting value for %s", a->f.name);
+ else if (arg_check[a->pos].needs_fd)
+ arg_check[a->pos].has_fd = true;
+
+ offset[a->pos] = parse_arg(g, a, offset[a->pos],
+ jvalue);
+ count++;
+ } else if (arg_check[a->pos].needs_fd &&
+ arg_check[a->pos].has_fd) {
+ ;
+ } else if (a->f.flags & SIZE) {
+ offset[a->pos] = parse_arg(g, a, offset[a->pos],
+ jvalue);
+ } else {
+ die(" No specification for argument %s", a->f.name);
+ }
+ }
+
+ if (ret) {
+ ret_offset = gluten_rw_alloc_type(g, U64);
+ gluten_add_tag_post(g, ret, ret_offset);
+ }
+
+ /* emit_call() */
+
+ if (count != json_object_get_count(obj))
+ die(" Stray elements in call");
+}
+
+void handle_calls(struct gluten_ctx *g, JSON_Value *value)
+{
+ JSON_Array *calls = json_value_get_array(value);
+ unsigned i, count;
+
+ if (calls)
+ count = json_array_get_count(calls);
+ else
+ count = 1;
+
+ for (i = 0; i < count; i++) {
+ struct call **set, *call;
+ JSON_Object *obj, *args;
+ const char *name, *ret;
+
+ if (calls)
+ obj = json_array_get_object(calls, i);
+ else
+ obj = json_value_get_object(value);
+
+ name = json_object_get_name(obj, 0);
+ value = json_object_get_value_at(obj, 0);
+ args = json_object_get_object(obj, name);
+
+ debug(" Parsing call %s", name);
+
+ ret = json_object_get_string(obj, "ret");
+
+ /* TODO: Factor this out into a function in calls.c */
+ for (set = call_sets, call = set[0]; *set; ) {
+ if (!call->name) {
+ set++;
+ call = set[0];
+ continue;
+ }
+
+ if (!strcmp(name, call->name)) {
+ debug(" Found description for %s",
+ name);
+ parse_call(g, args, ret, call->args);
+ break;
+ }
+ call++;
+ }
+ }
+}