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