diff --git a/lib/glacier/status/error.h b/lib/glacier/status/error.h index 2bad027..95229d3 100644 --- a/lib/glacier/status/error.h +++ b/lib/glacier/status/error.h @@ -30,6 +30,7 @@ static const uint64_t FAILED_PRECONDITION = 0x8; static const uint64_t INTERNAL = 0x100; static const uint64_t UNIMPLEMENTED = 0x101; static const uint64_t EXHAUSTED = 0x102; +static const uint64_t INVALID_RESPONSE = 0x103; // Kernel specific error codes (relating to capabilities). static const uint64_t CAP_NOT_FOUND = 0x1000; diff --git a/yunq/client.cpp.jinja b/yunq/client.cpp.jinja new file mode 100644 index 0000000..9e520c7 --- /dev/null +++ b/yunq/client.cpp.jinja @@ -0,0 +1,46 @@ +// Generated file - DO NOT MODIFY +#include "{{file}}.client.h" + +#include +#include + +{% for interface in interfaces %} +{% for method in interface.methods %} + +glcr::ErrorCode {{method.name}}(const {{method.request}}& request, {{method.response}}& response) { + uint64_t buffer_size = 0x1000; + // FIXME: Maybe raise this limit. + uint64_t cap_size = 100; + glcr::ByteBuffer buffer(buffer_size); + glcr::CapBuffer caps(cap_size); + + const uint32_t kSentinel = 0xBEEFDEAD; + buffer.WriteAt(0, kSentinel); + buffer.WriteAt(8, {method_number}); + + uint64_t length = request.SerializeToBytes(buffer, /*offset=*/16, caps); + buffer.WriteAt(4, 16 + length); + + z_cap_t reply_port_cap; + // FIXME: We need to be able to send capabilities via endpoint call. + RET_ERR(ZEndpointSend(endpoint_, 16 + length, buffer.RawPtr())); + + // FIXME: Add a way to zero out the first buffer. + glcr::ByteBuffer recv_buffer(buffer_size); + glcr::CapBuffer recv_caps(cap_size); + RET_ERR(ZReplyPortRecv(reply_port_cap, &buffer_size, recv_buffer.RawPtr(), &cap_size, recv_caps.RawPtr())); + + if (recv_buffer.At(0) != kSentinel) { + return glcr::INVALID_RESPONSE; + } + + // Check Response Code. + RET_ERR(recv_buffer.At(8)); + + response.ParseFromBytes(recv_buffer, 16, recv_caps); + + return glcr::OK; +} + +{% endfor %} +{% endfor %} diff --git a/yunq/client.h.jinja b/yunq/client.h.jinja new file mode 100644 index 0000000..30e3f39 --- /dev/null +++ b/yunq/client.h.jinja @@ -0,0 +1,22 @@ +// Generated file - DO NOT MODIFY +#pragma once + +#include + +#include "{{file}}.h" + +{% for interface in interfaces -%} +class {{interface.name}}Client { + public: + {{interface.name}}Client(z_cap_t {{interface.name}}_cap) : endpoint_({{interface.name}}_cap) {} + {{interface.name}}Client(const {{interface.name}}Client&) = delete; + {{interface.name}}Client({{interface.name}}Client&&) = delete; +{% for method in interface.methods %} + [[nodiscard]] glcr::ErrorCode {{method.name}}(const {{method.request}}& request, {{method.response}}& response); +{% endfor %} + private: + z_cap_t endpoint_; +}; + +{%- endfor %} + diff --git a/yunq/example.yunq.client.cpp b/yunq/example.yunq.client.cpp new file mode 100644 index 0000000..52d0c93 --- /dev/null +++ b/yunq/example.yunq.client.cpp @@ -0,0 +1,45 @@ +// Generated file - DO NOT MODIFY +#include "example.yunq.client.h" + +#include +#include + + + + +glcr::ErrorCode open(const OpenFileRequest& request, File& response) { + uint64_t buffer_size = 0x1000; + // FIXME: Maybe raise this limit. + uint64_t cap_size = 100; + glcr::ByteBuffer buffer(buffer_size); + glcr::CapBuffer caps(cap_size); + + const uint32_t kSentinel = 0xBEEFDEAD; + buffer.WriteAt(0, kSentinel); + buffer.WriteAt(8, {method_number}); + + uint64_t length = request.SerializeToBytes(buffer, /*offset=*/16, caps); + buffer.WriteAt(4, 16 + length); + + z_cap_t reply_port_cap; + // FIXME: We need to be able to send capabilities via endpoint call. + RET_ERR(ZEndpointSend(endpoint_, 16 + length, buffer.RawPtr())); + + // FIXME: Add a way to zero out the first buffer. + glcr::ByteBuffer recv_buffer(buffer_size); + glcr::CapBuffer recv_caps(cap_size); + RET_ERR(ZReplyPortRecv(reply_port_cap, &buffer_size, recv_buffer.RawPtr(), &cap_size, recv_caps.RawPtr())); + + if (recv_buffer.At(0) != kSentinel) { + return glcr::INVALID_RESPONSE; + } + + // Check Response Code. + RET_ERR(recv_buffer.At(8)); + + response.ParseFromBytes(recv_buffer, 16, recv_caps); + + return glcr::OK; +} + + diff --git a/yunq/example.yunq.client.h b/yunq/example.yunq.client.h new file mode 100644 index 0000000..4769c88 --- /dev/null +++ b/yunq/example.yunq.client.h @@ -0,0 +1,18 @@ +// Generated file - DO NOT MODIFY +#pragma once + +#include + +#include "example.yunq.h" + +class VFSClient { + public: + VFSClient(z_cap_t VFS_cap) : endpoint_(VFS_cap) {} + VFSClient(const VFSClient&) = delete; + VFSClient(VFSClient&&) = delete; + + [[nodiscard]] glcr::ErrorCode open(const OpenFileRequest& request, File& response); + + private: + z_cap_t endpoint_; +}; diff --git a/yunq/example.yunq.cpp b/yunq/example.yunq.cpp index afcacc2..14a444f 100644 --- a/yunq/example.yunq.cpp +++ b/yunq/example.yunq.cpp @@ -1,3 +1,4 @@ +// Generated file -- DO NOT MODIFY. #include "example.yunq.h" namespace { @@ -26,86 +27,148 @@ void WriteHeader(glcr::ByteBuffer& bytes, uint64_t offset, uint32_t core_size, u } } // namespace - - - - void OpenFileRequest::ParseFromBytes(const glcr::ByteBuffer& bytes, uint64_t offset) { CheckHeader(bytes); - - - - auto {field.name}_pointer = bytes.At(offset + header_size + (8 * {loop.index})); + // Parse path. + auto path_pointer = bytes.At(offset + header_size + (8 * 0)); set_path(bytes.StringAt(offset + path_pointer.offset, path_pointer.length)); - - - - set_options(bytes.At(offset + header_size + (8 * 2))); - - + // Parse options. + set_options(bytes.At(offset + header_size + (8 * 1))); } -void {message.name}::ParseFromBytes(const glcr::ByteBuffer& bytes, uint64_t offset, const glcr::CapBuffer& caps) { +void OpenFileRequest::ParseFromBytes(const glcr::ByteBuffer& bytes, uint64_t offset, const glcr::CapBuffer& caps) { CheckHeader(bytes); - - - - auto {field.name}_pointer = bytes.At(offset + header_size + (8 * {loop.index})); + // Parse path. + auto path_pointer = bytes.At(offset + header_size + (8 * 0)); set_path(bytes.StringAt(offset + path_pointer.offset, path_pointer.length)); - - - - set_options(bytes.At(offset + header_size + (8 * 2))); - - + // Parse options. + set_options(bytes.At(offset + header_size + (8 * 1))); } +uint64_t OpenFileRequest::SerializeToBytes(glcr::ByteBuffer& bytes, uint64_t offset) { + uint32_t next_extension = header_size + 8 * 2; + const uint32_t core_size = next_extension; + // Write path. + ExtPointer path_ptr{ + .offset = next_extension, + // FIXME: Check downcast of str length. + .length = (uint32_t)path().length(), + }; + bytes.WriteStringAt(offset + next_extension, path()); + next_extension += path_ptr.length; + bytes.WriteAt(offset + header_size + (8 * 0), path_ptr); + // Write options. + bytes.WriteAt(offset + header_size + (8 * 1), options()); + // The next extension pointer is the length of the message. + WriteHeader(bytes, offset, core_size, next_extension); + + return next_extension; +} + +uint64_t OpenFileRequest::SerializeToBytes(glcr::ByteBuffer& bytes, uint64_t offset, glcr::CapBuffer& caps) { + uint32_t next_extension = header_size + 8 * 2; + const uint32_t core_size = next_extension; + uint64_t next_cap = 0; + // Write path. + ExtPointer path_ptr{ + .offset = next_extension, + // FIXME: Check downcast of str length. + .length = (uint32_t)path().length(), + }; + + bytes.WriteStringAt(offset + next_extension, path()); + next_extension += path_ptr.length; + + bytes.WriteAt(offset + header_size + (8 * 0), path_ptr); + // Write options. + bytes.WriteAt(offset + header_size + (8 * 1), options()); + + // The next extension pointer is the length of the message. + WriteHeader(bytes, offset, core_size, next_extension); + + return next_extension; +} void File::ParseFromBytes(const glcr::ByteBuffer& bytes, uint64_t offset) { CheckHeader(bytes); - - - - auto {field.name}_pointer = bytes.At(offset + header_size + (8 * {loop.index})); + // Parse path. + auto path_pointer = bytes.At(offset + header_size + (8 * 0)); set_path(bytes.StringAt(offset + path_pointer.offset, path_pointer.length)); - - - - set_attrs(bytes.At(offset + header_size + (8 * 2))); - - - + // Parse attrs. + set_attrs(bytes.At(offset + header_size + (8 * 1))); + // Parse mem_cap. // FIXME: Implement in-buffer capabilities for inprocess serialization. set_mem_cap(0); - - } -void {message.name}::ParseFromBytes(const glcr::ByteBuffer& bytes, uint64_t offset, const glcr::CapBuffer& caps) { +void File::ParseFromBytes(const glcr::ByteBuffer& bytes, uint64_t offset, const glcr::CapBuffer& caps) { CheckHeader(bytes); - - - - auto {field.name}_pointer = bytes.At(offset + header_size + (8 * {loop.index})); + // Parse path. + auto path_pointer = bytes.At(offset + header_size + (8 * 0)); set_path(bytes.StringAt(offset + path_pointer.offset, path_pointer.length)); - - - - set_attrs(bytes.At(offset + header_size + (8 * 2))); - - - - uint64_t mem_cap_ptr = bytes.At(offset + header_size + (8 * {offset})); + // Parse attrs. + set_attrs(bytes.At(offset + header_size + (8 * 1))); + // Parse mem_cap. + uint64_t mem_cap_ptr = bytes.At(offset + header_size + (8 * 2)); set_mem_cap(caps.At(mem_cap_ptr)); - - } +uint64_t File::SerializeToBytes(glcr::ByteBuffer& bytes, uint64_t offset) { + uint32_t next_extension = header_size + 8 * 3; + const uint32_t core_size = next_extension; + // Write path. + ExtPointer path_ptr{ + .offset = next_extension, + // FIXME: Check downcast of str length. + .length = (uint32_t)path().length(), + }; + bytes.WriteStringAt(offset + next_extension, path()); + next_extension += path_ptr.length; + + bytes.WriteAt(offset + header_size + (8 * 0), path_ptr); + // Write attrs. + bytes.WriteAt(offset + header_size + (8 * 1), attrs()); + // Write mem_cap. + // FIXME: Implement inbuffer capabilities. + bytes.WriteAt(offset + header_size + (8 * 2), 0); + + // The next extension pointer is the length of the message. + WriteHeader(bytes, offset, core_size, next_extension); + + return next_extension; +} + +uint64_t File::SerializeToBytes(glcr::ByteBuffer& bytes, uint64_t offset, glcr::CapBuffer& caps) { + uint32_t next_extension = header_size + 8 * 3; + const uint32_t core_size = next_extension; + uint64_t next_cap = 0; + // Write path. + ExtPointer path_ptr{ + .offset = next_extension, + // FIXME: Check downcast of str length. + .length = (uint32_t)path().length(), + }; + + bytes.WriteStringAt(offset + next_extension, path()); + next_extension += path_ptr.length; + + bytes.WriteAt(offset + header_size + (8 * 0), path_ptr); + // Write attrs. + bytes.WriteAt(offset + header_size + (8 * 1), attrs()); + // Write mem_cap. + caps.WriteAt(next_cap, mem_cap()); + bytes.WriteAt(offset + header_size + (8 * 2), next_cap++); + + // The next extension pointer is the length of the message. + WriteHeader(bytes, offset, core_size, next_extension); + + return next_extension; +} \ No newline at end of file diff --git a/yunq/example.yunq.h b/yunq/example.yunq.h index 73e264b..9313321 100644 --- a/yunq/example.yunq.h +++ b/yunq/example.yunq.h @@ -5,9 +5,6 @@ #include #include #include - - - class OpenFileRequest { public: OpenFileRequest() {} @@ -19,24 +16,16 @@ class OpenFileRequest { void ParseFromBytes(const glcr::ByteBuffer&, uint64_t offset, const glcr::CapBuffer&); uint64_t SerializeToBytes(glcr::ByteBuffer&, uint64_t offset); uint64_t SerializeToBytes(glcr::ByteBuffer&, uint64_t offset, glcr::CapBuffer&); - - glcr::String path() { return path_; } void set_path(const glcr::String& value) { path_ = value; } - uint64_t options() { return options_; } void set_options(const uint64_t& value) { options_ = value; } - private: - glcr::String path_; - uint64_t options_; - -} - +}; class File { public: File() {} @@ -48,25 +37,16 @@ class File { void ParseFromBytes(const glcr::ByteBuffer&, uint64_t offset, const glcr::CapBuffer&); uint64_t SerializeToBytes(glcr::ByteBuffer&, uint64_t offset); uint64_t SerializeToBytes(glcr::ByteBuffer&, uint64_t offset, glcr::CapBuffer&); - - glcr::String path() { return path_; } void set_path(const glcr::String& value) { path_ = value; } - uint64_t attrs() { return attrs_; } void set_attrs(const uint64_t& value) { attrs_ = value; } - z_cap_t mem_cap() { return mem_cap_; } void set_mem_cap(const z_cap_t& value) { mem_cap_ = value; } - private: - glcr::String path_; - uint64_t attrs_; - z_cap_t mem_cap_; - -} +}; \ No newline at end of file diff --git a/yunq/example.yunq.server.cpp b/yunq/example.yunq.server.cpp new file mode 100644 index 0000000..52d0c93 --- /dev/null +++ b/yunq/example.yunq.server.cpp @@ -0,0 +1,45 @@ +// Generated file - DO NOT MODIFY +#include "example.yunq.client.h" + +#include +#include + + + + +glcr::ErrorCode open(const OpenFileRequest& request, File& response) { + uint64_t buffer_size = 0x1000; + // FIXME: Maybe raise this limit. + uint64_t cap_size = 100; + glcr::ByteBuffer buffer(buffer_size); + glcr::CapBuffer caps(cap_size); + + const uint32_t kSentinel = 0xBEEFDEAD; + buffer.WriteAt(0, kSentinel); + buffer.WriteAt(8, {method_number}); + + uint64_t length = request.SerializeToBytes(buffer, /*offset=*/16, caps); + buffer.WriteAt(4, 16 + length); + + z_cap_t reply_port_cap; + // FIXME: We need to be able to send capabilities via endpoint call. + RET_ERR(ZEndpointSend(endpoint_, 16 + length, buffer.RawPtr())); + + // FIXME: Add a way to zero out the first buffer. + glcr::ByteBuffer recv_buffer(buffer_size); + glcr::CapBuffer recv_caps(cap_size); + RET_ERR(ZReplyPortRecv(reply_port_cap, &buffer_size, recv_buffer.RawPtr(), &cap_size, recv_caps.RawPtr())); + + if (recv_buffer.At(0) != kSentinel) { + return glcr::INVALID_RESPONSE; + } + + // Check Response Code. + RET_ERR(recv_buffer.At(8)); + + response.ParseFromBytes(recv_buffer, 16, recv_caps); + + return glcr::OK; +} + + diff --git a/yunq/example.yunq.server.h b/yunq/example.yunq.server.h new file mode 100644 index 0000000..7ebaacc --- /dev/null +++ b/yunq/example.yunq.server.h @@ -0,0 +1,32 @@ +// Generated File -- DO NOT MODIFY. +#pragma once + +#include +#include + +#include "example.yunq.h" + + + +class VFSServerBase { + public: + VFSServerBase(z_cap_t VFS_cap) : {} + VFSServerBase(const VFSServerBase&) = delete; + VFSServerBase(VFSServerBase&&) = delete; + + [[nodiscard]] Thread RunServer(); + + + [[nodiscard]] virtual glcr::ErrorCode Handleopen(const OpenFileRequest&, File&) = 0; + + + private: + z_cap_t endpoint_; + + friend void VFSServerBaseThreadBootstrap(void*); + void ServerThread(); + + [[nodiscard]] glcr::ErrorCode HandleRequest(const glcr::ByteBuffer& request, glcr::ByteBuffer& response, + glcr::CapBuffer& resp_caps); +}; + diff --git a/yunq/server.cpp.jinja b/yunq/server.cpp.jinja new file mode 100644 index 0000000..57d95e9 --- /dev/null +++ b/yunq/server.cpp.jinja @@ -0,0 +1,93 @@ +// Generated file -- DO NOT MODIFY. +#include "{{file}}.server.h" + +#include +#include + +namespace { + +const uint32_t kSentinel = 0xBEEFDEAD; +const uint32_t kHeaderSize = 0x10; + +void WriteError(glcr::ByteBuffer& buffer, glcr::ErrorCode err) { + buffer.WriteAt(0, kSentinel); + buffer.WriteAt(4, kHeaderSize); + buffer.WriteAt(8, err); +} + +void WriteHeader(glcr::ByteBuffer& buffer, uint64_t message_length) { + buffer.WriteAt(0, kSentinel); + buffer.WriteAt(4, kHeaderSize + message_length); + buffer.WriteAt(8, glcr::OK); +} + +} // namespace + + +{% for interface in interfaces %} +void {{interface.name}}ServerBaseThreadBootstrap(void* server_base) { + ({{interface.name}}ServerBase*)(server_base)->ServerThread(); +} + +Thread {{interface.name}}::RunServer() { + return Thread({{interface.name}}ServerBaseThreadBootstrap, this); +} + +void {{interface.name}}::ServerThread() { + glcr::ByteBuffer recv_buffer(0x1000); + glcr::ByteBuffer resp_buffer(0x1000); + uint64_t resp_cap_size = 0x10; + glcr::CapBuffer resp_cap(resp_cap_size); + z_cap_t reply_port_cap; + + while (true) { + uint64_t recv_buf_size = 0x1000; + glcr::ErrorCode recv_err = ZEndpointRecv(endpoint_, &recv_buf_size, recv_buffer.RawPtr(), &reply_port_cap); + if (recv_err != glcr::OK) { + dbgln("Error in receive: %x", recv_err); + continue; + } + + uint64_t resp_length = 0; + + glcr::ErrorCode err = HandlRequest(recv_buffer, resp_buffer, resp_length, resp_cap); + if (err != glcr::OK) { + WriteError(resp_buffer, err); + ZReplyPortSend(reply_port_cap, kHeaderSize, resp_buffer.RawPtr(), 0, nullptr); + } else { + WriteHeader(resp_buffer, resp_length); + ZReplyPortSend(reply_port_cap, resp_length, resp_buffer.RawPtr(), resp_cap.UsedSlots(), resp_cap.RawPtr()); + } + } + +} + +glcr::ErrorCode {{interface.name}}::HandleRequest(const glcr::ByteBuffer& request, glcr::ByteBuffer& response, uint64_t& resp_length + glcr::CapBuffer& resp_caps) { + if (recv_buffer.At(0) != kSentinel) { + return glcr::INVALID_INPUT; + } + + uint64_t method_select = recv_buffer.At(8); + + switch(method_select) { +{%- for method in interface.methods %} + case {{loop.index0}}: { + {{method.request}} request; + {{method.response}} response; + + request.ParseFromBytes(recv_buffer, kHeaderSize); + + RET_ERR(Handle{{method.name}}(request, response)); + + resp_length = response.SerializeToBytes(resp_buffer, kHeaderSize, resp_cap); + break; + } +{%- endfor %} + default: { + return glcr::UNIMPLEMENTED; + } + } + return glcr::OK; +} +{% endfor %} diff --git a/yunq/server.h.jinja b/yunq/server.h.jinja new file mode 100644 index 0000000..a50e6ce --- /dev/null +++ b/yunq/server.h.jinja @@ -0,0 +1,33 @@ +// Generated File -- DO NOT MODIFY. +#pragma once + +#include +#include + +#include "{{file}}.h" + +{% for interface in interfaces %} + +class {{interface.name}}ServerBase { + public: + {{interface.name}}ServerBase(z_cap_t {{interface.name}}_cap) : {} + {{interface.name}}ServerBase(const {{interface.name}}ServerBase&) = delete; + {{interface.name}}ServerBase({{interface.name}}ServerBase&&) = delete; + + [[nodiscard]] Thread RunServer(); + +{% for method in interface.methods %} + [[nodiscard]] virtual glcr::ErrorCode Handle{{method.name}}(const {{method.request}}&, {{method.response}}&) = 0; +{% endfor %} + + private: + z_cap_t endpoint_; + + friend void {{interface.name}}ServerBaseThreadBootstrap(void*); + void ServerThread(); + + [[nodiscard]] glcr::ErrorCode HandleRequest(const glcr::ByteBuffer& request, glcr::ByteBuffer& response, + glcr::CapBuffer& resp_caps); +}; + +{% endfor %} diff --git a/yunq/yunq.py b/yunq/yunq.py index de09d55..874380b 100755 --- a/yunq/yunq.py +++ b/yunq/yunq.py @@ -38,6 +38,25 @@ def main(): message_impl = message_impl_tmpl.render(file=filename, messages=messages) f.write(message_impl) + client_header_tmpl = jinja_env.get_template("client.h.jinja") + with open(filename + '.client.h', mode='w') as f: + client_header = client_header_tmpl.render(file=filename, interfaces=interfaces) + f.write(client_header) + + client_impl_tmpl = jinja_env.get_template("client.cpp.jinja") + with open(filename + '.client.cpp', mode='w') as f: + client_impl = client_impl_tmpl.render(file=filename, interfaces=interfaces) + f.write(client_impl) + + server_header_tmpl = jinja_env.get_template("server.h.jinja") + with open(filename + '.server.h', mode='w') as f: + server_header = server_header_tmpl.render(file=filename, interfaces=interfaces) + f.write(server_header) + + server_impl_tmpl = jinja_env.get_template("server.cpp.jinja") + with open(filename + '.server.cpp', mode='w') as f: + server_impl = client_impl_tmpl.render(file=filename, interfaces=interfaces) + f.write(server_impl) if __name__ == "__main__": main()