aboutsummaryrefslogtreecommitdiff
path: root/c_emulator/config_utils.cpp
blob: abe9b31b843c41935a5f55194cbfb3081fbcdb02 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
#include "config_utils.h"

#include <fstream>
#include <jsoncons_ext/jsonschema/jsonschema.hpp>
#include <sstream>
#include <stdexcept>

#include "config_schema.h"
#include "default_config.h"
#include "file_utils.h"
#include "sail.h"
#include "sail_config.h"
#include "sail_riscv_model.h"

std::string keypath_to_str(const std::vector<const char *> &keypath) {
  std::string s;
  if (keypath.empty()) {
    return s;
  }
  for (auto part : keypath) {
    s += part;
    s += ".";
  }
  s.pop_back();
  return s;
}

uint64_t get_config_uint64(const std::vector<const char *> &keypath) {
  sail_config_json json = sail_config_get(keypath.size(), keypath.data());

  if (json == nullptr) {
    throw std::runtime_error("Failed to find configuration option '" + keypath_to_str(keypath) + "'.");
  }

  if (sail_config_is_int(json)) {
    sail_int big_n;
    CREATE(sail_int)(&big_n);
    sail_config_unwrap_int(&big_n, json);
    // TODO: This truncates and ignores the sign.
    uint64_t n = sail_int_get_ui(big_n);
    KILL(sail_int)(&big_n);
    return n;
  }

  if (sail_config_is_bits(json)) {
    lbits big_bits;
    CREATE(lbits)(&big_bits);
    sail_config_unwrap_bits(&big_bits, json);
    if (big_bits.len > 64) {
      KILL(lbits)(&big_bits);
      throw std::runtime_error(
        "Couldn't read " + std::to_string(big_bits.len) + "-bit value '" + keypath_to_str(keypath) + "' into uint64_t."
      );
    }
    // Second parameter is unused; see
    // https://github.com/rems-project/sail/issues/1429
    fbits bits = CONVERT_OF(fbits, lbits)(big_bits, false);
    KILL(lbits)(&big_bits);
    return bits;
  }

  throw std::runtime_error(
    "Configuration option '" + keypath_to_str(keypath) + "' could not be parsed as an integer.\n"
  );
}

bool get_config_bool(const std::vector<const char *> &keypath) {
  sail_config_json json = sail_config_get(keypath.size(), keypath.data());

  if (json == nullptr) {
    throw std::runtime_error("Failed to find configuration option '" + keypath_to_str(keypath) + "'.");
  }

  if (sail_config_is_bool(json)) {
    return sail_config_unwrap_bool(json);
  }

  throw std::runtime_error(
    "Configuration option '" + keypath_to_str(keypath) + "' could not be parsed as a boolean.\n"
  );
}

const char *get_default_config() {
  return DEFAULT_JSON;
}

const char *get_default_rv32_config() {
  return DEFAULT_JSON_RV32;
}

const char *get_config_schema() {
  return CONFIG_SCHEMA;
}

void validate_config_schema(const jsoncons::json &json_config, const std::string &source_desc) {
  using jsoncons::json;
  namespace jsonschema = jsoncons::jsonschema;

  // Compile the schema.
  json schema = json::parse(get_config_schema());
  auto options = jsonschema::evaluation_options{}.default_version(jsonschema::schema_version::draft202012());
  // Throws schema_error if compilation fails.
  jsonschema::json_schema<json> compiled = jsonschema::make_json_schema(std::move(schema), options);

  bool is_valid = true;
  std::ostringstream error_stream;
  auto report = [&is_valid, &error_stream](const jsonschema::validation_message &msg) -> jsonschema::walk_result {
    is_valid = false;
    error_stream << "- " << msg.instance_location().string() << ": " << msg.message() << "\n";
    return jsonschema::walk_result::advance;
  };
  compiled.validate(json_config, report);
  if (!is_valid) {
    throw std::runtime_error("Schema conformance check failed for " + source_desc + ":\n" + error_stream.str());
  }
}