1 // Copyright 2017 The Fuchsia Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include <algorithm>
6
7 #include "types.h"
8
9 using std::string;
10 using std::vector;
11
12 const std::map<string, string> rust_overrides = {
13 {"any[]IN", "*const u8"},
14 {"any[]OUT", "*mut u8"},
15 {"any[]INOUT", "*mut u8"}};
16
17 const std::map<string, string> rust_primitives = {
18 {"int8_t", "i8"},
19 {"int16_t", "i16"},
20 {"int32_t", "i32"},
21 {"int64_t", "i64"},
22 {"uint8_t", "u8"},
23 {"uint16_t", "u16"},
24 {"uint32_t", "u32"},
25 {"uint64_t", "u64"},
26 {"size_t", "usize"},
27 {"uintptr_t", "usize"},
28 {"int", "isize"},
29 {"char", "u8"},
30 {"float", "f32"},
31 {"double", "f64"},
32 };
33
34 const std::map<string, string> rust_reserved_words = {
35 {"proc", "proc_"},
36 };
37
has_attribute(const char * attr,const vector<string> & attrs)38 bool has_attribute(const char* attr, const vector<string>& attrs) {
39 return std::find(attrs.begin(), attrs.end(), attr) != attrs.end();
40 }
41
dump_attributes(const vector<string> & attrs)42 void dump_attributes(const vector<string>& attrs) {
43 for (auto& a : attrs) {
44 fprintf(stderr, "%s ", a.c_str());
45 }
46 fprintf(stderr, "\n");
47 }
48
kind_str() const49 string ArraySpec::kind_str() const {
50 switch (kind) {
51 case IN:
52 return "IN";
53 case OUT:
54 return "OUT";
55 default:
56 return "INOUT";
57 }
58 }
59
kind_lowercase_str() const60 string ArraySpec::kind_lowercase_str() const {
61 switch (kind) {
62 case IN:
63 return "in";
64 case OUT:
65 return "out";
66 default:
67 return "inout";
68 }
69 }
70
assign_kind(const vector<string> & attrs)71 bool ArraySpec::assign_kind(const vector<string>& attrs) {
72 if (has_attribute("IN", attrs)) {
73 kind = ArraySpec::IN;
74 } else if (has_attribute("OUT", attrs)) {
75 kind = ArraySpec::OUT;
76 } else if (has_attribute("INOUT", attrs)) {
77 kind = ArraySpec::INOUT;
78 } else {
79 return false;
80 }
81 return true;
82 }
83
to_string() const84 string ArraySpec::to_string() const {
85 return "[]" + kind_str();
86 }
87
map_override(const string & name,const std::map<string,string> & overrides)88 const string map_override(const string& name, const std::map<string, string>& overrides) {
89 auto ft = overrides.find(name);
90 return (ft == overrides.end()) ? name : ft->second;
91 }
92
to_string() const93 string TypeSpec::to_string() const {
94 return type + (arr_spec ? arr_spec->to_string() : string());
95 }
96
as_cpp_declaration(bool is_wrapped) const97 string TypeSpec::as_cpp_declaration(bool is_wrapped) const {
98 if (!arr_spec) {
99 return type + " " + name;
100 }
101
102 if (is_wrapped &&
103 arr_spec->kind == ArraySpec::OUT && arr_spec->count == 1 &&
104 type == "zx_handle_t") {
105 return "user_out_handle* " + name;
106 }
107
108 string modifier = arr_spec->kind == ArraySpec::IN ? "const " : "";
109 string ptr_type = type == "any" ? "void" : type;
110
111 if (is_wrapped) {
112 // This policy strings here matches the enum defined in the
113 // kernel's user_ptr.h.
114 string policy = arr_spec->kind_lowercase_str();
115 return "user_" + policy + "_ptr<" + modifier + ptr_type + "> " + name;
116 }
117 return modifier + ptr_type + "* " + name;
118 }
119
as_rust_declaration() const120 string TypeSpec::as_rust_declaration() const {
121 auto overridden = map_override(to_string(), rust_overrides);
122 auto scalar_type = map_override(type, rust_primitives);
123 auto safe_name = map_override(name, rust_reserved_words);
124
125 if (overridden != to_string()) {
126 return safe_name + ": " + overridden;
127 } else if (!arr_spec) {
128 return safe_name + ": " + scalar_type;
129 } else {
130 string ret = safe_name + ": ";
131 if (arr_spec->kind == ArraySpec::IN) {
132 ret += "*const ";
133 } else {
134 ret += "*mut ";
135 }
136
137 ret += scalar_type;
138 if (arr_spec->count > 1)
139 ret += " " + std::to_string(arr_spec->count);
140 return ret;
141 }
142 }
143
as_cpp_cast(const string & arg) const144 string TypeSpec::as_cpp_cast(const string& arg) const {
145 if (!arr_spec) {
146 return "static_cast<" + type + ">(" + arg + ")";
147 }
148
149 string modifier = arr_spec->kind == ArraySpec::IN ? "const " : "";
150 string cast_type = type == "any" ? "void*" : type + "*";
151 return "reinterpret_cast<" + modifier + cast_type + ">(" + arg + ")";
152 }
153
is_vdso() const154 bool Syscall::is_vdso() const {
155 return has_attribute("vdsocall", attributes);
156 }
157
is_noreturn() const158 bool Syscall::is_noreturn() const {
159 return has_attribute("noreturn", attributes);
160 }
161
is_blocking() const162 bool Syscall::is_blocking() const {
163 return has_attribute("blocking", attributes);
164 }
165
is_internal() const166 bool Syscall::is_internal() const {
167 return has_attribute("internal", attributes);
168 }
169
num_kernel_args() const170 size_t Syscall::num_kernel_args() const {
171 return is_noreturn() ? arg_spec.size() : arg_spec.size() + ret_spec.size() - 1;
172 }
173
for_each_return(const std::function<void (const TypeSpec &)> & cb) const174 void Syscall::for_each_return(const std::function<void(const TypeSpec&)>& cb) const {
175 if (ret_spec.size() > 1) {
176 std::for_each(ret_spec.begin() + 1, ret_spec.end(), cb);
177 }
178 }
179
for_each_kernel_arg(const std::function<void (const TypeSpec &)> & cb) const180 void Syscall::for_each_kernel_arg(const std::function<void(const TypeSpec&)>& cb) const {
181 std::for_each(arg_spec.begin(), arg_spec.end(), cb);
182 for_each_return(cb);
183 }
184
validate() const185 bool Syscall::validate() const {
186 if (ret_spec.size() > 0 && is_noreturn()) {
187 print_error("noreturn should have zero return arguments");
188 return false;
189 }
190
191 if (num_kernel_args() > kMaxArgs) {
192 print_error("invalid number of arguments");
193 return false;
194 }
195
196 if (ret_spec.size() >= 1 && !ret_spec[0].name.empty()) {
197 print_error("the first return argument cannot be named, yet...");
198 return false;
199 }
200
201 if (is_blocking() &&
202 (ret_spec.size() == 0 || ret_spec[0].type != "zx_status_t")) {
203 print_error("blocking must have first return be of type zx_status_t");
204 return false;
205 }
206
207 if (is_vdso() && is_internal()) {
208 print_error("vdsocall cannot be internal");
209 return false;
210 }
211
212 bool valid_args = true;
213 for_each_kernel_arg([this, &valid_args](const TypeSpec& arg) {
214 if (arg.name.empty()) {
215 print_error("all arguments need to be named, except the first return");
216 valid_args = false;
217 }
218 if (arg.arr_spec) {
219 if (!validate_array_spec(arg)) {
220 valid_args = false;
221 }
222 }
223 });
224 return valid_args;
225 }
226
assign_index(int * next_index)227 void Syscall::assign_index(int* next_index) {
228 if (!is_vdso())
229 index = (*next_index)++;
230 }
231
validate_array_spec(const TypeSpec & ts) const232 bool Syscall::validate_array_spec(const TypeSpec& ts) const {
233 if (ts.arr_spec->count > 0)
234 return true;
235 // find arguments that represent the array count.
236 for (const string& multiplier : ts.arr_spec->multipliers) {
237 auto arg = std::find_if(arg_spec.begin(), arg_spec.end(), [&](const TypeSpec& a) {
238 return a.name == multiplier;
239 });
240 if (arg == arg_spec.end()) {
241 std::string err = "invalid array spec for " + ts.name + ": '" + multiplier + "' does not refer to an argument";
242 print_error(err.c_str());
243 return false;
244 }
245 // TODO:cpu also enforce INOUT here.
246 if (arg->arr_spec && arg->arr_spec->count != 1u) {
247 std::string err = "invalid array spec for " + ts.name + ": '" + multiplier + "' refers to an array of size != 1";
248 return false;
249 }
250 }
251 return true;
252 }
253
print_error(const char * what) const254 void Syscall::print_error(const char* what) const {
255 fprintf(stderr, "error: %s : %s\n", name.c_str(), what);
256 }
257
return_type() const258 string Syscall::return_type() const {
259 if (ret_spec.empty()) {
260 return "void";
261 }
262 return ret_spec[0].to_string();
263 }
264
is_void_return() const265 bool Syscall::is_void_return() const {
266 return return_type() == "void";
267 }
268