| // Copyright 2019 The Fuchsia Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "tools/kazoo/syscall_library.h" |
| |
| #include <zircon/compiler.h> |
| |
| #include "src/lib/fxl/logging.h" |
| #include "src/lib/fxl/strings/trim.h" |
| #include "tools/kazoo/output_util.h" |
| |
| namespace { |
| |
| bool ValidateTransport(const rapidjson::Value& interface) { |
| if (!interface.HasMember("maybe_attributes")) { |
| return false; |
| } |
| for (const auto& attrib : interface["maybe_attributes"].GetArray()) { |
| if (attrib.GetObject()["name"].Get<std::string>() == "Transport") { |
| if (attrib.GetObject()["value"].Get<std::string>() == "Syscall") { |
| return true; |
| } |
| } |
| } |
| return false; |
| } |
| |
| std::string GetCategory(const rapidjson::Value& interface, const std::string& interface_name) { |
| if (interface.HasMember("maybe_attributes")) { |
| for (const auto& attrib : interface["maybe_attributes"].GetArray()) { |
| if (attrib.GetObject()["name"].Get<std::string>() == "NoProtocolPrefix") { |
| return std::string(); |
| } |
| } |
| } |
| |
| // "zx/" or "zz/". |
| constexpr size_t kPrefixLen = 3; |
| return ToLowerAscii( |
| interface_name.substr(kPrefixLen, interface_name.size() - kPrefixLen)); |
| } |
| |
| std::string GetDocAttribute(const rapidjson::Value& method) { |
| if (!method.HasMember("maybe_attributes")) { |
| return std::string(); |
| } |
| for (const auto& attrib : method["maybe_attributes"].GetArray()) { |
| if (attrib.GetObject()["name"].Get<std::string>() == "Doc") { |
| return fxl::TrimString(fxl::StringView(attrib.GetObject()["value"].GetString()), " \t\n") |
| .ToString(); |
| } |
| } |
| return std::string(); |
| } |
| |
| } // namespace |
| |
| Type TypeFromJson(const SyscallLibrary& library, const rapidjson::Value& type) { |
| if (!type.HasMember("kind")) { |
| FXL_LOG(ERROR) << "type has no 'kind'"; |
| return Type(); |
| } |
| |
| std::string kind = type["kind"].GetString(); |
| if (kind == "primitive") { |
| std::string subtype = type["subtype"].GetString(); |
| if (subtype == "uint8") { |
| return Type(TypeUint8{}); |
| } else if (subtype == "uint16") { |
| return Type(TypeUint16{}); |
| } else if (subtype == "int32") { |
| return Type(TypeInt32{}); |
| } else if (subtype == "uint32") { |
| return Type(TypeUint32{}); |
| } else if (subtype == "int64") { |
| return Type(TypeInt64{}); |
| } else if (subtype == "uint64") { |
| return Type(TypeUint64{}); |
| } else if (subtype == "usize") { |
| return Type(TypeSizeT{}); |
| } else if (subtype == "bool") { |
| return Type(TypeBool{}); |
| } else { |
| FXL_CHECK(false) << "TODO: primitive subtype=" << subtype; |
| } |
| } else if (kind == "identifier") { |
| std::string id = type["identifier"].GetString(); |
| return library.TypeFromIdentifier(type["identifier"].GetString()); |
| } else if (kind == "handle") { |
| return Type(TypeHandle(type["subtype"].GetString())); |
| } else if (kind == "vector") { |
| Type contained_type = TypeFromJson(library, type["element_type"]); |
| return Type(TypeVector(contained_type)); |
| } else if (kind == "string") { |
| return Type(TypeString{}); |
| } |
| |
| FXL_CHECK(false) << "TODO: kind=" << kind; |
| return Type(); |
| } |
| |
| bool Syscall::HasAttribute(const char* attrib_name) const { |
| return attributes_.find(attrib_name) != attributes_.end(); |
| } |
| |
| size_t Syscall::NumKernelArgs() const { |
| if (is_noreturn()) { |
| return request_.members().size(); |
| } |
| |
| // The first return value is passed as the ordinary C return |
| // value, but only if there is at least one return value. |
| size_t ret_values = response_.members().size(); |
| if (ret_values > 0) { |
| --ret_values; |
| } |
| return request_.members().size() + ret_values; |
| } |
| |
| Type SyscallLibrary::TypeFromIdentifier(const std::string& id) const { |
| // TODO: Load struct, enum, union, usings and return one of them here! |
| return Type(); |
| } |
| |
| // static |
| bool SyscallLibraryLoader::FromJson(const std::string& json_ir, SyscallLibrary* library, |
| bool match_original_order) { |
| rapidjson::Document document; |
| document.Parse(json_ir); |
| |
| // Maybe do schema validation here, though we rely on fidlc for many details |
| // and general sanity, so probably only in a diagnostic mode. |
| |
| if (!document.IsObject()) { |
| FXL_LOG(ERROR) << "Root of json wasn't object."; |
| return false; |
| } |
| |
| library->name_ = document["name"].GetString(); |
| if (library->name_ != "zz" && library->name_ != "zx") { |
| FXL_LOG(ERROR) << "Library name wasn't zz or zx as expected."; |
| return false; |
| } |
| |
| FXL_DCHECK(library->syscalls_.empty()); |
| |
| for (const auto& interface : document["interface_declarations"].GetArray()) { |
| if (!ValidateTransport(interface)) { |
| FXL_LOG(ERROR) << "Expected Transport to be Syscall."; |
| return false; |
| } |
| |
| std::string interface_name = interface["name"].GetString(); |
| std::string category = GetCategory(interface, interface_name); |
| |
| for (const auto& method : interface["methods"].GetArray()) { |
| auto syscall = std::make_unique<Syscall>(); |
| syscall->original_interface_ = interface_name; |
| syscall->original_name_ = method["name"].GetString(); |
| syscall->category_ = category; |
| syscall->name_ = |
| category + (category.empty() ? "" : "_") + CamelToSnake(method["name"].GetString()); |
| syscall->is_noreturn_ = !method["has_response"].GetBool(); |
| syscall->short_description_ = GetDocAttribute(method); |
| if (method.HasMember("maybe_attributes")) { |
| for (const auto& attrib : method["maybe_attributes"].GetArray()) { |
| syscall->attributes_[attrib["name"].GetString()] = attrib["value"].GetString(); |
| } |
| } |
| |
| FXL_CHECK(method["has_request"].GetBool()); // Events are not expected in syscalls. |
| |
| auto add_struct_members = [&library](Struct* strukt, const rapidjson::Value& arg) { |
| Type type = TypeFromJson(*library, arg["type"]); |
| if (std::holds_alternative<TypeVector>(type)) { |
| std::string name(arg["name"].GetString()); |
| Type subtype(TypePointer(std::get<TypeVector>(type).contained_type())); |
| strukt->members_.emplace_back(name, std::move(subtype)); |
| strukt->members_.emplace_back("num_" + name, Type(TypeSizeT{})); |
| } else if (std::holds_alternative<TypeString>(type)) { |
| std::string name(arg["name"].GetString()); |
| Type subtype(TypePointer(Type(TypeChar{}))); |
| strukt->members_.emplace_back(name, subtype); |
| strukt->members_.emplace_back(name + "_size", Type(TypeSizeT{})); |
| } else { |
| strukt->members_.emplace_back(arg["name"].GetString(), std::move(type)); |
| } |
| }; |
| |
| Struct& req = syscall->request_; |
| req.name_ = syscall->original_name_ + "#request"; |
| for (const auto& arg : method["maybe_request"].GetArray()) { |
| add_struct_members(&req, arg); |
| } |
| |
| if (method["has_response"].GetBool()) { |
| Struct& resp = syscall->response_; |
| resp.name_ = syscall->original_name_ + "#response"; |
| for (const auto& arg : method["maybe_response"].GetArray()) { |
| add_struct_members(&resp, arg); |
| } |
| } |
| |
| library->syscalls_.push_back(std::move(syscall)); |
| } |
| } |
| |
| if (match_original_order && !MakeSyscallOrderMatchOldDeclarationOrder(library)) { |
| return false; |
| } |
| |
| return true; |
| } |
| |
| // static |
| bool SyscallLibraryLoader::MakeSyscallOrderMatchOldDeclarationOrder(SyscallLibrary* library) { |
| // During transition, output in the order that the file was originally in to |
| // facilitate simple diffing. |
| static constexpr const char* kOrderFromOriginalSyscallsAbigen[] = { |
| "clock_get", |
| "clock_get_monotonic", |
| "nanosleep", |
| "ticks_get", |
| "ticks_per_second", |
| "deadline_after", |
| "clock_adjust", |
| "system_get_dcache_line_size", |
| "system_get_num_cpus", |
| "system_get_version", |
| "system_get_physmem", |
| "system_get_features", |
| "system_get_event", |
| "cache_flush", |
| "handle_close", |
| "handle_close_many", |
| "handle_duplicate", |
| "handle_replace", |
| "object_wait_one", |
| "object_wait_many", |
| "object_wait_async", |
| "object_signal", |
| "object_signal_peer", |
| "object_get_property", |
| "object_set_property", |
| "object_get_info", |
| "object_get_child", |
| "object_set_profile", |
| "channel_create", |
| "channel_read", |
| "channel_read_etc", |
| "channel_write", |
| "channel_write_etc", |
| "channel_call_noretry", |
| "channel_call_finish", |
| "channel_call", |
| "socket_create", |
| "socket_write", |
| "socket_read", |
| "socket_shutdown", |
| "thread_exit", |
| "thread_create", |
| "thread_start", |
| "thread_read_state", |
| "thread_write_state", |
| "process_exit", |
| "process_create", |
| "process_start", |
| "process_read_memory", |
| "process_write_memory", |
| "job_create", |
| "job_set_policy", |
| "task_bind_exception_port", |
| "task_suspend", |
| "task_suspend_token", |
| "task_resume_from_exception", |
| "task_create_exception_channel", |
| "task_kill", |
| "exception_get_thread", |
| "exception_get_process", |
| "event_create", |
| "eventpair_create", |
| "futex_wait", |
| "futex_wake", |
| "futex_requeue", |
| "futex_wake_single_owner", |
| "futex_requeue_single_owner", |
| "futex_get_owner", |
| "port_create", |
| "port_queue", |
| "port_wait", |
| "port_cancel", |
| "timer_create", |
| "timer_set", |
| "timer_cancel", |
| "vmo_create", |
| "vmo_read", |
| "vmo_write", |
| "vmo_get_size", |
| "vmo_set_size", |
| "vmo_op_range", |
| "vmo_create_child", |
| "vmo_set_cache_policy", |
| "vmo_replace_as_executable", |
| "vmar_allocate", |
| "vmar_destroy", |
| "vmar_map", |
| "vmar_unmap", |
| "vmar_protect", |
| "cprng_draw_once", |
| "cprng_draw", |
| "cprng_add_entropy", |
| "fifo_create", |
| "fifo_read", |
| "fifo_write", |
| "profile_create", |
| "vmar_unmap_handle_close_thread_exit", |
| "futex_wake_handle_close_thread_exit", |
| "debuglog_create", |
| "debuglog_write", |
| "debuglog_read", |
| "ktrace_read", |
| "ktrace_control", |
| "ktrace_write", |
| "mtrace_control", |
| "debug_read", |
| "debug_write", |
| "debug_send_command", |
| "interrupt_create", |
| "interrupt_bind", |
| "interrupt_wait", |
| "interrupt_destroy", |
| "interrupt_ack", |
| "interrupt_trigger", |
| "interrupt_bind_vcpu", |
| "ioports_request", |
| "ioports_release", |
| "vmo_create_contiguous", |
| "vmo_create_physical", |
| "iommu_create", |
| "bti_create", |
| "bti_pin", |
| "bti_release_quarantine", |
| "pmt_unpin", |
| "framebuffer_get_info", |
| "framebuffer_set_range", |
| "pci_get_nth_device", |
| "pci_enable_bus_master", |
| "pci_reset_device", |
| "pci_config_read", |
| "pci_config_write", |
| "pci_cfg_pio_rw", |
| "pci_get_bar", |
| "pci_map_interrupt", |
| "pci_query_irq_mode", |
| "pci_set_irq_mode", |
| "pci_init", |
| "pci_add_subtract_io_range", |
| "pc_firmware_tables", |
| "smc_call", |
| "resource_create", |
| "guest_create", |
| "guest_set_trap", |
| "vcpu_create", |
| "vcpu_resume", |
| "vcpu_interrupt", |
| "vcpu_read_state", |
| "vcpu_write_state", |
| "system_mexec", |
| "system_mexec_payload_get", |
| "system_powerctl", |
| "pager_create", |
| "pager_create_vmo", |
| "pager_detach_vmo", |
| "pager_supply_pages", |
| "syscall_test_0", |
| "syscall_test_1", |
| "syscall_test_2", |
| "syscall_test_3", |
| "syscall_test_4", |
| "syscall_test_5", |
| "syscall_test_6", |
| "syscall_test_7", |
| "syscall_test_8", |
| "syscall_test_wrapper", |
| "syscall_test_handle_create", |
| }; |
| |
| if (library->syscalls_.size() != countof(kOrderFromOriginalSyscallsAbigen)) { |
| FXL_LOG(ERROR) << "Have " << library->syscalls_.size() << " syscalls, but original has " |
| << countof(kOrderFromOriginalSyscallsAbigen) << " syscalls."; |
| return false; |
| } |
| |
| std::vector<std::unique_ptr<Syscall>> in_order; |
| |
| // TODO(scottmg): This is a crappy linear search done N times, but it's 1) a |
| // small N; 2) will be removed once this tool is the standard and we don't use |
| // abigen any more. |
| for (size_t i = 0; i < countof(kOrderFromOriginalSyscallsAbigen); ++i) { |
| for (size_t j = 0; j < library->syscalls_.size(); ++j) { |
| if (!library->syscalls_[j]) { |
| continue; |
| } |
| if (library->syscalls_[j]->name() == kOrderFromOriginalSyscallsAbigen[i]) { |
| in_order.push_back(std::move(library->syscalls_[j])); |
| break; |
| } |
| } |
| } |
| |
| library->syscalls_ = std::move(in_order); |
| |
| return true; |
| } |