diff --git a/.bochsrc b/.bochsrc new file mode 100644 index 0000000..5a417a9 --- /dev/null +++ b/.bochsrc @@ -0,0 +1,15 @@ +megs: 1024 +ata0-master: type=disk, path=builddbg/disk.img, mode=flat, cylinders=512, heads=16, spt=50 +display_library: sdl2, options="gui_debug" +boot: disk +com1: enabled=1, mode=file, dev=serial.out, baud=9600, parity=none, bits=8, stopbits=1 +cpu: model=corei7_sandy_bridge_2600k +pci: enabled=1, chipset=i440bx +log: bochs.log +# TODO: Make this portable, by building bochs locally. +romimage: file=/home/drew/opt/bochs/share/bochs/BIOS-bochs-latest +vgaromimage: file=/home/drew/opt/bochs/share/bochs/VGABIOS-lgpl-latest.bin +vga: extension=vbe +port_e9_hack: enabled=1 +# Breaks on XCHGW %BX, %BX +magic_break: enabled=1 diff --git a/.forgejo/workflows/ci.yml b/.forgejo/workflows/ci.yml new file mode 100644 index 0000000..bf7a604 --- /dev/null +++ b/.forgejo/workflows/ci.yml @@ -0,0 +1,42 @@ +name: Check + +on: + push: + branches: [main] + pull_request: + branches: [main] + +env: + # Should speed up builds. + CARGO_INCREMENTAL: 0 + # Should reduce the size of ./target to improve cache load/store. + CARGO_PROFILE_TEST_DEBUG: 0 + +jobs: + check: + name: Check Rust + runs-on: docker + + steps: + - name: Checkout Repo + uses: actions/checkout@v4 + + - name: Install Rust + uses: https://codeberg.org/wackbyte/rust-toolchain@trunk + with: + toolchain: nightly-2025-10-02 + components: rustfmt, clippy, rust-src + - name: Cache + uses: https://github.com/Swatinem/rust-cache@v2 + with: + # Don't cache ~/.cargo/bin since we restore the cache after we install things there + cache-bin: "false" + workspaces: "backend" + - name: "Check Format" + run: cargo fmt --check + working-directory: rust + - name: "Lint" + run: | + rustup component add clippy + cargo clippy --locked -- -D warnings + working-directory: rust diff --git a/.gitignore b/.gitignore index ae043f6..7e2de93 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,17 @@ builddbg/ +test-bin/ __pycache__/ +.ccls-cache/ compile_commands.json +bochs.log +serial.out + sysroot/bin -sysroot/usr/bin +sysroot/usr +sysroot/.crates.toml +sysroot/.crates2.json + +rust/target +yunq/venv +yunq/rust/target diff --git a/CMakeLists.txt b/CMakeLists.txt index ae70f18..e99a91c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.2) +cmake_minimum_required(VERSION 3.5) # Set because our cross compiler can't do dynamic linking? set(CMAKE_TRY_COMPILE_TARGET_TYPE "STATIC_LIBRARY") @@ -9,11 +9,19 @@ set(CMAKE_CXX_STANDARD 20) set(CMAKE_CXX_STANDARD_REQUIRED True) set(CMAKE_EXPORT_COMPILE_COMMANDS True) -set(BASE_COMPILE_FLAGS "-ffreestanding -fno-rtti -fno-exceptions") +set(BASE_COMPILE_FLAGS "-ffreestanding -fno-rtti -fno-exceptions -mincoming-stack-boundary=3") set(BASE_LINK_FLAGS "-nostdlib") +if (enable_testing) + include(CTest) + find_package(Catch2 3 REQUIRED) + find_program(MEMORYCHECK_COMMAND valgrind) + set(MEMORYCHECK_COMMAND_OPTIONS "--trace-children=yes --leak-check=full") + add_custom_target(build_test) +endif() + add_subdirectory(zion) -add_subdirectory(lib) add_subdirectory(yunq) +add_subdirectory(lib) add_subdirectory(usr) add_subdirectory(sys) diff --git a/RELEASES.md b/RELEASES.md index 3c5bb6f..7b19818 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -1,5 +1,24 @@ # Releases +## AcadiaOS 0.1.1 (WIP) + +### Denali + +- AHCI Driver can use more than one command slot. +- Resets AHCI Controller on start. +- Uses IDENTIFY DEVICE to get sector size. + +### Glacier + +- Unit Testing setup for Host Machine +- Unit Tests for: Vector +- Added Iterators for: Vector, Array, ArrayView +- HashMap Move Semantics + +### Yunq + +- Moved message parsing/serialization to shared library. + ## AcadiaOS 0.1.0 (2023-12-08) This marks the first release of AcadiaOS! There is very little user functionality currently but a diff --git a/lib/glacier/CMakeLists.txt b/lib/glacier/CMakeLists.txt index e20b9c9..ef0489f 100644 --- a/lib/glacier/CMakeLists.txt +++ b/lib/glacier/CMakeLists.txt @@ -29,3 +29,7 @@ target_include_directories(glacier_kernel set_target_properties(glacier_kernel PROPERTIES COMPILE_FLAGS "${CMAKE_CXX_FLAGS} ${BASE_COMPILE_FLAGS} -mcmodel=kernel -mgeneral-regs-only") + +if (enable_testing) + add_subdirectory(test) +endif() diff --git a/lib/glacier/container/array.h b/lib/glacier/container/array.h index 171550b..62f9f87 100644 --- a/lib/glacier/container/array.h +++ b/lib/glacier/container/array.h @@ -2,6 +2,7 @@ #include +#include "glacier/container/array_iter.h" #include "glacier/container/array_view.h" namespace glcr { @@ -37,6 +38,13 @@ class Array { uint64_t size() const { return size_; } bool empty() const { return size_ == 0; } + typedef ArrayIterator Iterator; + + Iterator begin() { return {data_, size_}; } + const Iterator begin() const { return {data_, size_}; } + Iterator end() { return {nullptr, 0}; } + const Iterator end() const { return {nullptr, 0}; } + private: T* data_; uint64_t size_; diff --git a/lib/glacier/container/array_iter.h b/lib/glacier/container/array_iter.h new file mode 100644 index 0000000..195e9df --- /dev/null +++ b/lib/glacier/container/array_iter.h @@ -0,0 +1,40 @@ +#pragma once + +#include + +namespace glcr { + +template +class ArrayIterator { + public: + ArrayIterator(T* item, uint64_t size) : item_(item), size_(size) {} + + ArrayIterator next() { + if (size_ <= 1) { + return {nullptr, 0}; + } + return {item_ + 1, size_ - 1}; + } + + ArrayIterator& operator++() { + if (size_ <= 1) { + item_ = nullptr; + size_ = 0; + } else { + item_++; + size_--; + } + return *this; + } + + T& operator*() { return *item_; } + T* operator->() { return item_; } + bool operator==(const ArrayIterator& other) { return item_ == other.item_; } + bool operator!=(const ArrayIterator& other) { return item_ != other.item_; } + + private: + T* item_; + uint64_t size_; +}; + +} // namespace glcr diff --git a/lib/glacier/container/array_view.h b/lib/glacier/container/array_view.h index da16e9d..f4415e8 100644 --- a/lib/glacier/container/array_view.h +++ b/lib/glacier/container/array_view.h @@ -2,6 +2,8 @@ #include +#include "glacier/container/array_iter.h" + namespace glcr { template @@ -10,7 +12,9 @@ class ArrayView { ArrayView() : data_(nullptr), size_(0) {} ArrayView(const ArrayView&) = default; + ArrayView& operator=(const ArrayView&) = default; ArrayView(ArrayView&&) = default; + ArrayView& operator=(ArrayView&&) = default; ArrayView(T* data, uint64_t size) : data_(data), size_(size) {} @@ -24,6 +28,23 @@ class ArrayView { uint64_t size() const { return size_; } bool empty() const { return size_; } + typedef ArrayIterator Iterator; + + Iterator begin() { + if (size_ == 0) { + return {nullptr, 0}; + } + return {data_, size_}; + } + const Iterator begin() const { + if (size_ == 0) { + return {nullptr, 0}; + } + return {data_, size_}; + } + Iterator end() { return {nullptr, 0}; } + const Iterator end() const { return {nullptr, 0}; } + private: T* data_; uint64_t size_; diff --git a/lib/glacier/container/hash_map.h b/lib/glacier/container/hash_map.h index 1390ab6..10d4aca 100644 --- a/lib/glacier/container/hash_map.h +++ b/lib/glacier/container/hash_map.h @@ -18,9 +18,8 @@ class HashMap { HashMap() = default; HashMap(const HashMap&) = delete; HashMap& operator=(const HashMap&) = delete; - // TODO: Implement Move. - HashMap(HashMap&&) = delete; - HashMap& operator=(HashMap&&) = delete; + HashMap(HashMap&&); + HashMap& operator=(HashMap&&); // Accessors. uint64_t size() { return size_; } @@ -63,6 +62,21 @@ class HashMap { void ResizeIfNecessary(); }; +template +HashMap::HashMap(HashMap&& other) { + data_ = glcr::Move(other.data_); + size_ = other.size_; + other.size_ = 0; +} + +template +HashMap& HashMap::operator=(HashMap&& other) { + data_ = glcr::Move(other.data_); + size_ = other.size_; + other.size_ = 0; + return *this; +} + template V& HashMap::at(const K& key) { uint64_t hc = H()(key); @@ -74,7 +88,8 @@ V& HashMap::at(const K& key) { } } // TODO: Add a failure mode here instead of constructing an object. - ll.PushFront({key, {}}); + K k2 = key; + ll.PushFront({glcr::Move(k2), {}}); return ll.PeekFront().second(); } @@ -194,8 +209,7 @@ template void HashMap::Resize(uint64_t new_size) { Array>> new_data(new_size); - for (uint64_t i = 0; i < data_.size(); i++) { - auto& ll = data_[i]; + for (auto& ll : data_) { while (!ll.empty()) { auto pair = ll.PopFront(); uint64_t hc = H()(pair.first()); diff --git a/lib/glacier/container/intrusive_list.h b/lib/glacier/container/intrusive_list.h index 8957a32..aef7b83 100644 --- a/lib/glacier/container/intrusive_list.h +++ b/lib/glacier/container/intrusive_list.h @@ -20,6 +20,8 @@ class IntrusiveList { void PushFront(const RefPtr& obj); void PushBack(const RefPtr& obj); + void Remove(const RefPtr& obj); + RefPtr PopFront(); RefPtr PopBack(); @@ -64,6 +66,29 @@ void IntrusiveList::PushBack(const RefPtr& obj) { back_ = obj; } +template +void IntrusiveList::Remove(const RefPtr& obj) { + if (!obj) { + return; + } + if (front_ == obj) { + front_ = obj->next_; + } + if (back_ == obj) { + back_ = obj->prev_; + } + if (obj->prev_) { + obj->prev_->next_ = obj->next_; + } + if (obj->next_) { + obj->next_->prev_ = obj->prev_; + } + + obj->prev_ = nullptr; + obj->next_ = nullptr; + size_--; +} + template RefPtr IntrusiveList::PopFront() { if (front_ == nullptr) { diff --git a/lib/glacier/container/vector.h b/lib/glacier/container/vector.h index afe6229..b4b994c 100644 --- a/lib/glacier/container/vector.h +++ b/lib/glacier/container/vector.h @@ -1,5 +1,6 @@ #pragma once +#include #include #include @@ -37,6 +38,7 @@ class Vector { // Setters. // FIXME: Handle downsizing. + // TODO: Rename this so it is clear that this only affects capacity. void Resize(uint64_t capacity); void PushBack(const T& item); @@ -45,8 +47,16 @@ class Vector { template void EmplaceBack(Args&&... args); + T& PeekBack(); T&& PopBack(); + typedef ArrayIterator Iterator; + + Iterator begin() { return {data_, size_}; } + const Iterator begin() const { return {data_, size_}; } + Iterator end() { return {nullptr, 0}; } + const Iterator end() const { return {nullptr, 0}; } + private: T* data_; uint64_t size_; @@ -121,6 +131,11 @@ void Vector::EmplaceBack(Args&&... args) { data_[size_++] = T(args...); } +template +T& Vector::PeekBack() { + return data_[size_ - 1]; +} + template T&& Vector::PopBack() { size_--; diff --git a/lib/glacier/string/string.cpp b/lib/glacier/string/string.cpp index e0a5b67..a2b6db3 100644 --- a/lib/glacier/string/string.cpp +++ b/lib/glacier/string/string.cpp @@ -32,7 +32,7 @@ String::String(const String& other) : String(other.cstr_, other.length_) {} String& String::operator=(const String& other) { if (cstr_) { - delete cstr_; + delete[] cstr_; } length_ = other.length_; cstr_ = new char[length_ + 1]; @@ -51,7 +51,7 @@ String::String(String&& other) : cstr_(other.cstr_), length_(other.length_) { String& String::operator=(String&& other) { if (cstr_) { - delete cstr_; + delete[] cstr_; } cstr_ = other.cstr_; length_ = other.length_; @@ -64,7 +64,7 @@ String& String::operator=(String&& other) { String::~String() { if (cstr_) { - delete cstr_; + delete[] cstr_; } } diff --git a/lib/glacier/test/CMakeLists.txt b/lib/glacier/test/CMakeLists.txt new file mode 100644 index 0000000..c3701f7 --- /dev/null +++ b/lib/glacier/test/CMakeLists.txt @@ -0,0 +1,2 @@ +add_subdirectory(container) + diff --git a/lib/glacier/test/container/CMakeLists.txt b/lib/glacier/test/container/CMakeLists.txt new file mode 100644 index 0000000..f5488ab --- /dev/null +++ b/lib/glacier/test/container/CMakeLists.txt @@ -0,0 +1,8 @@ +add_executable(glc_vec_test vector.cpp) +target_link_libraries(glc_vec_test glacier Catch2::Catch2WithMain) +target_include_directories(glc_vec_test PRIVATE "../..") +add_test(NAME glc_vec_test COMMAND $) + +add_dependencies(build_test + glc_vec_test) + diff --git a/lib/glacier/test/container/vector.cpp b/lib/glacier/test/container/vector.cpp new file mode 100644 index 0000000..4ec0d65 --- /dev/null +++ b/lib/glacier/test/container/vector.cpp @@ -0,0 +1,172 @@ +#include "container/vector.h" + +#include + +using namespace glcr; + +TEST_CASE("Empty Vector", "[vector]") { + Vector v; + REQUIRE(v.size() == 0); + REQUIRE(v.empty()); +} + +TEST_CASE("Push/Pop Vector", "[vector]") { + Vector v; + v.PushBack(42); + REQUIRE(v.size() == 1); + REQUIRE(v.capacity() >= 1); + v.PushBack(33); + REQUIRE(v.size() == 2); + REQUIRE(v.capacity() >= 2); + + REQUIRE(v.at(0) == 42); + REQUIRE(v[0] == 42); + REQUIRE(v.at(1) == 33); + REQUIRE(v[1] == 33); + + REQUIRE(v.PopBack() == 33); + REQUIRE(v.size() == 1); + REQUIRE(v.PopBack() == 42); + REQUIRE(v.size() == 0); +} + +class ConstructRecorder { + public: + static uint64_t construct_cnt; + static uint64_t copy_cnt; + static uint64_t move_cnt; + ConstructRecorder() { construct_cnt++; } + ConstructRecorder(const ConstructRecorder&) { copy_cnt++; } + ConstructRecorder& operator=(const ConstructRecorder&) { + copy_cnt++; + return *this; + } + + ConstructRecorder(ConstructRecorder&&) { move_cnt++; } + ConstructRecorder& operator=(ConstructRecorder&&) { + move_cnt++; + return *this; + } + + static void Reset() { + construct_cnt = 0; + copy_cnt = 0; + move_cnt = 0; + } + + private: + uint64_t dummy_data = 0; +}; + +uint64_t ConstructRecorder::construct_cnt = 0; +uint64_t ConstructRecorder::copy_cnt = 0; +uint64_t ConstructRecorder::move_cnt = 0; + +TEST_CASE("Data-Type Construction", "[vector]") { + ConstructRecorder::Reset(); + Vector v; + + SECTION("Copy Insert") { + ConstructRecorder obj; + v.PushBack(obj); + // This is overfitted on the implementation which also default constructs + // the held objects when allocating a new backing array. + REQUIRE(ConstructRecorder::construct_cnt == 2); + REQUIRE(ConstructRecorder::copy_cnt == 1); + REQUIRE(ConstructRecorder::move_cnt == 0); + } + + SECTION("Move Insert") { + ConstructRecorder obj; + v.PushBack(glcr::Move(obj)); + // This is overfitted on the implementation which also default constructs + // the held objects when allocating a new backing array. + REQUIRE(ConstructRecorder::construct_cnt == 2); + REQUIRE(ConstructRecorder::copy_cnt == 0); + REQUIRE(ConstructRecorder::move_cnt == 1); + } + + SECTION("RValue Insert") { + v.PushBack({}); + // This is overfitted on the implementation which also default constructs + // the held objects when allocating a new backing array. + REQUIRE(ConstructRecorder::construct_cnt == 2); + REQUIRE(ConstructRecorder::copy_cnt == 0); + REQUIRE(ConstructRecorder::move_cnt == 1); + } + + SECTION("Emplace Insert") { + v.EmplaceBack(); + // This is overfitted on the implementation which also default constructs + // the held objects when allocating a new backing array. + REQUIRE(ConstructRecorder::construct_cnt == 2); + REQUIRE(ConstructRecorder::copy_cnt == 0); + REQUIRE(ConstructRecorder::move_cnt == 1); + } + + SECTION("PopBack Move") { + v.EmplaceBack(); + ConstructRecorder obj = v.PopBack(); + + // This is overfitted on the implementation which also default constructs + // the held objects when allocating a new backing array. + REQUIRE(ConstructRecorder::construct_cnt == 2); + REQUIRE(ConstructRecorder::copy_cnt == 0); + // 1 from emplace, 1 from pop. (No additional regular constructions). + REQUIRE(ConstructRecorder::move_cnt == 2); + } +} + +TEST_CASE("Vector Move", "[vector]") { + ConstructRecorder::Reset(); + + Vector v; + v.PushBack({}); + v.PushBack({}); + v.PushBack({}); + + uint64_t construct = ConstructRecorder::construct_cnt; + uint64_t copy = ConstructRecorder::copy_cnt; + uint64_t move = ConstructRecorder::move_cnt; + + Vector v2(glcr::Move(v)); + + REQUIRE(v2.size() == 3); + REQUIRE(v2.capacity() >= 3); + REQUIRE(ConstructRecorder::construct_cnt == construct); + REQUIRE(ConstructRecorder::copy_cnt == copy); + REQUIRE(ConstructRecorder::move_cnt == move); + + Vector v3 = glcr::Move(v2); + + REQUIRE(v3.size() == 3); + REQUIRE(v3.capacity() >= 3); + REQUIRE(ConstructRecorder::construct_cnt == construct); + REQUIRE(ConstructRecorder::copy_cnt == copy); + REQUIRE(ConstructRecorder::move_cnt == move); +} + +TEST_CASE("Vector Iterator", "[vector]") { + Vector v; + for (uint64_t i = 0; i < 100; i++) { + v.PushBack(42); + } + + SECTION("For Range Loop") { + uint64_t iters = 0; + for (uint64_t i : v) { + REQUIRE(i == 42); + iters++; + } + REQUIRE(iters == 100); + } + + SECTION("Raw Iter Loop") { + uint64_t iters = 0; + for (auto it = v.begin(); it != v.end(); ++it) { + REQUIRE(*it == 42); + iters++; + } + REQUIRE(iters == 100); + } +} diff --git a/lib/mammoth/CMakeLists.txt b/lib/mammoth/CMakeLists.txt index ea431ab..5ceed59 100644 --- a/lib/mammoth/CMakeLists.txt +++ b/lib/mammoth/CMakeLists.txt @@ -1,14 +1,7 @@ add_library(mammoth STATIC - file/file.cpp - input/keyboard.cpp - ipc/channel.cpp - ipc/endpoint_client.cpp - ipc/endpoint_server.cpp ipc/port_client.cpp ipc/port_server.cpp - proc/process.cpp proc/thread.cpp - sync/mutex.cpp sync/semaphore.cpp util/debug.cpp util/init.cpp @@ -24,7 +17,6 @@ target_include_directories(mammoth target_link_libraries(mammoth glacier - victoriafalls_yunq yellowstone_yunq voyageurs_yunq zion_stub diff --git a/lib/mammoth/file/file.cpp b/lib/mammoth/file/file.cpp deleted file mode 100644 index 7d2a9a7..0000000 --- a/lib/mammoth/file/file.cpp +++ /dev/null @@ -1,81 +0,0 @@ -#include "file/file.h" - -#include -#include -#include -#include -#include - -#include "util/debug.h" - -namespace mmth { -namespace { - -using yellowstone::Endpoint; -using yellowstone::GetEndpointRequest; -using yellowstone::YellowstoneClient; - -VFSClient* gVfsClient = nullptr; - -void GetVfsClientIfNeeded() { - if (gVfsClient == nullptr) { - // TODO: Add an unowned client so we don't have to duplicate this cap every - // time. - uint64_t dup_cap; - check(ZCapDuplicate(gInitEndpointCap, kZionPerm_All, &dup_cap)); - YellowstoneClient client(dup_cap); - - GetEndpointRequest yreq; - yreq.set_endpoint_name("victoriafalls"); - Endpoint yresp; - check(client.GetEndpoint(yreq, yresp)); - - gVfsClient = new VFSClient(yresp.endpoint()); - } -} - -} // namespace - -void SetVfsCap(z_cap_t vfs_cap) { gVfsClient = new VFSClient(vfs_cap); } - -File File::Open(glcr::StringView path) { - GetVfsClientIfNeeded(); - - OpenFileRequest req; - req.set_path(path); - OpenFileResponse resp; - check(gVfsClient->OpenFile(req, resp)); - - return File(OwnedMemoryRegion::FromCapability(resp.memory()), resp.size()); -} - -glcr::StringView File::as_str() { - return glcr::StringView((char*)raw_ptr(), size_); -} - -void* File::raw_ptr() { return reinterpret_cast(file_data_.vaddr()); } -uint8_t* File::byte_ptr() { - return reinterpret_cast(file_data_.vaddr()); -} - -glcr::ErrorOr> ListDirectory(glcr::StringView path) { - GetVfsClientIfNeeded(); - - GetDirectoryRequest req; - req.set_path(path); - Directory dir; - auto status = gVfsClient->GetDirectory(req, dir); - if (!status.ok()) { - dbgln("Error in getting directory: {}", status.message()); - return status.code(); - } - - auto file_views = glcr::StrSplit(dir.filenames(), ','); - glcr::Vector files; - for (uint64_t i = 0; i < file_views.size(); i++) { - files.PushBack(file_views[i]); - } - return files; -} - -} // namespace mmth diff --git a/lib/mammoth/file/file.h b/lib/mammoth/file/file.h deleted file mode 100644 index 7b55f2e..0000000 --- a/lib/mammoth/file/file.h +++ /dev/null @@ -1,38 +0,0 @@ -#pragma once - -#include -#include -#include -#include -#include - -#include "mammoth/util/memory_region.h" - -namespace mmth { - -// Intended for use in yellowstone since it already has the VFS cap. -void SetVfsCap(z_cap_t vfs_cap); - -class File { - public: - static File Open(glcr::StringView path); - - uint64_t size() { return size_; } - - glcr::StringView as_str(); - - void* raw_ptr(); - uint8_t* byte_ptr(); - - private: - OwnedMemoryRegion file_data_; - uint64_t size_; - - File(OwnedMemoryRegion&& file, uint64_t size) - : file_data_(glcr::Move(file)), size_(size) {} -}; - -// TODO: Move this to a separate file. -glcr::ErrorOr> ListDirectory(glcr::StringView path); - -} // namespace mmth diff --git a/lib/mammoth/input/keyboard.cpp b/lib/mammoth/input/keyboard.cpp deleted file mode 100644 index 77b4858..0000000 --- a/lib/mammoth/input/keyboard.cpp +++ /dev/null @@ -1,283 +0,0 @@ -#include "input/keyboard.h" - -#include -#include -#include - -#include "util/debug.h" - -namespace mmth { -namespace { - -using yellowstone::Endpoint; -using yellowstone::GetEndpointRequest; -using yellowstone::YellowstoneClient; - -void KeyboardListenerEntry(void* keyboard_base) { - reinterpret_cast(keyboard_base)->ListenLoop(); -} - -} // namespace - -KeyboardListenerBase::KeyboardListenerBase() { - auto server_or = PortServer::Create(); - if (!server_or) { - crash("Failed to create server", server_or.error()); - } - server_ = server_or.value(); -} - -void KeyboardListenerBase::Register() { - uint64_t dup_cap; - check(ZCapDuplicate(gInitEndpointCap, kZionPerm_All, &dup_cap)); - YellowstoneClient client(dup_cap); - - GetEndpointRequest req; - req.set_endpoint_name("voyageurs"); - Endpoint endpt; - check(client.GetEndpoint(req, endpt)); - - VoyageursClient vclient(endpt.endpoint()); - KeyboardListener listn; - - // TODO: Create a "ASSIGN_OR_CRASH" macro to simplify this. - auto client_or = server_.CreateClient(); - if (!client_or.ok()) { - crash("Failed to create client", client_or.error()); - } - listn.set_port_capability(client_or.value().cap()); - check(vclient.RegisterKeyboardListener(listn)); -} - -Thread KeyboardListenerBase::Listen() { - return Thread(KeyboardListenerEntry, this); -} - -void KeyboardListenerBase::ListenLoop() { - while (true) { - auto scancode_or = server_.RecvChar(); - if (!scancode_or.ok()) { - check(scancode_or.error()); - } - uint8_t scancode = scancode_or.value(); - - if (scancode == 0xE0) { - extended_on_ = true; - continue; - } - - Keycode k = ScancodeToKeycode(scancode); - Action a = ScancodeToAction(scancode); - HandleKeycode(k, a); - } -} - -void KeyboardListenerBase::HandleKeycode(Keycode code, Action action) { - char c = '\0'; - - if (action == kPressed) { - if (code >= kA && code <= kZ) { - if (IsShift()) { - const char* alpha = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; - c = alpha[code - kA]; - - } else { - const char* alpha = "abcdefghijklmnopqrstuvwxyz"; - c = alpha[code - kA]; - } - } else if (code >= k1 && code <= k0) { - if (IsShift()) { - const char* num = "!@#$%^&*()"; - c = num[code - k1]; - } else { - const char* num = "1234567890"; - c = num[code - k1]; - } - } else if (code >= kMinus && code <= kBacktick) { - if (IsShift()) { - const char* sym = "_+{}|?:\"<>~"; - c = sym[code - kMinus]; - } else { - const char* sym = "-=[]\\/;',.`"; - c = sym[code - kMinus]; - } - } else if (code == kEnter) { - c = '\n'; - } else if (code == kSpace) { - c = ' '; - } else if (code == kTab) { - c = '\t'; - } else if (code == kBackspace) { - c = '\b'; - } else if (code == kLShift) { - lshift_ = true; - } else if (code == kRShift) { - rshift_ = true; - } - } else if (action == kReleased) { - if (code == kLShift) { - lshift_ = false; - } else if (code == kRShift) { - rshift_ = false; - } - } - - if (c != '\0') { - HandleCharacter(c); - } -} - -Keycode KeyboardListenerBase::ScancodeToKeycode(uint8_t scancode) { - // Cancel out the released bit. - scancode &= 0x7F; - if (extended_on_) { - extended_on_ = false; - - switch (scancode) { - case 0x1D: - return kRCtrl; - case 0x38: - return kRAlt; - case 0x48: - return kUp; - case 0x4B: - return kLeft; - case 0x4D: - return kRight; - case 0x50: - return kDown; - case 0x53: - return kDelete; - case 0x5B: - return kSuper; - } - dbgln("Unknown extended scancode {x}", scancode); - - return kUnknownKeycode; - } - - switch (scancode) { - case 0x01: - return kEsc; - case 0x02: - return k1; - case 0x03: - return k2; - case 0x04: - return k3; - case 0x05: - return k4; - case 0x06: - return k5; - case 0x07: - return k6; - case 0x08: - return k7; - case 0x09: - return k8; - case 0x0A: - return k9; - case 0x0B: - return k0; - case 0x0C: - return kMinus; - case 0x0D: - return kEquals; - case 0x0E: - return kBackspace; - case 0x0F: - return kTab; - case 0x10: - return kQ; - case 0x11: - return kW; - case 0x12: - return kE; - case 0x13: - return kR; - case 0x14: - return kT; - case 0x15: - return kY; - case 0x16: - return kU; - case 0x17: - return kI; - case 0x18: - return kO; - case 0x19: - return kP; - case 0x1A: - return kLBrace; - case 0x1B: - return kRBrace; - case 0x1C: - return kEnter; - case 0x1D: - return kLCtrl; - case 0x1E: - return kA; - case 0x1F: - return kS; - case 0x20: - return kD; - case 0x21: - return kF; - case 0x22: - return kG; - case 0x23: - return kH; - case 0x24: - return kJ; - case 0x25: - return kK; - case 0x26: - return kL; - case 0x27: - return kSemicolon; - case 0x28: - return kQuote; - case 0x29: - return kBacktick; - case 0x2A: - return kLShift; - case 0x2B: - return kBSlash; - case 0x2C: - return kZ; - case 0x2D: - return kX; - case 0x2E: - return kC; - case 0x2F: - return kV; - case 0x30: - return kB; - case 0x31: - return kN; - case 0x32: - return kM; - case 0x33: - return kComma; - case 0x34: - return kPeriod; - case 0x35: - return kFSlash; - case 0x36: - return kRShift; - case 0x38: - return kLAlt; - case 0x39: - return kSpace; - } - - dbgln("Unknown scancode {x}", scancode); - - return kUnknownKeycode; -} - -Action KeyboardListenerBase::ScancodeToAction(uint8_t scancode) { - return (scancode & 0x80) ? kReleased : kPressed; -} - -} // namespace mmth diff --git a/lib/mammoth/input/keyboard.h b/lib/mammoth/input/keyboard.h deleted file mode 100644 index cba2d18..0000000 --- a/lib/mammoth/input/keyboard.h +++ /dev/null @@ -1,123 +0,0 @@ -#pragma once - -#include "mammoth/ipc/port_server.h" -#include "mammoth/proc/thread.h" - -namespace mmth { - -enum Keycode { - kUnknownKeycode = 0x0, - - kA = 0x1, - kB = 0x2, - kC = 0x3, - kD = 0x4, - kE = 0x5, - kF = 0x6, - kG = 0x7, - kH = 0x8, - kI = 0x9, - kJ = 0xA, - kK = 0xB, - kL = 0xC, - kM = 0xD, - kN = 0xE, - kO = 0xF, - kP = 0x10, - kQ = 0x11, - kR = 0x12, - kS = 0x13, - kT = 0x14, - kU = 0x15, - kV = 0x16, - kW = 0x17, - kX = 0x18, - kY = 0x19, - kZ = 0x1A, - - k1 = 0x20, - k2 = 0x21, - k3 = 0x22, - k4 = 0x23, - k5 = 0x24, - k6 = 0x25, - k7 = 0x26, - k8 = 0x27, - k9 = 0x28, - k0 = 0x29, - - kSpace = 0x30, - kEnter = 0x31, - kTab = 0x32, - kBackspace = 0x33, - kDelete = 0x34, - - kMinus = 0x40, - kEquals = 0x41, - kLBrace = 0x42, - kRBrace = 0x43, - kBSlash = 0x44, - kFSlash = 0x45, - kSemicolon = 0x46, - kQuote = 0x47, - kComma = 0x48, - kPeriod = 0x49, - kBacktick = 0x4A, - - kLShift = 0x50, - kRShift = 0x51, - kLCtrl = 0x52, - kRCtrl = 0x53, - kLAlt = 0x54, - kRAlt = 0x55, - kSuper = 0x56, - kEsc = 0x57, - kUp = 0x58, - kDown = 0x59, - kLeft = 0x5A, - kRight = 0x5B, -}; - -enum Action { - kUnknownAction, - kPressed, - kReleased, -}; - -class KeyboardListenerBase { - public: - KeyboardListenerBase(); - KeyboardListenerBase(const KeyboardListenerBase&) = delete; - KeyboardListenerBase(KeyboardListenerBase&&) = delete; - - void Register(); - - Thread Listen(); - - void ListenLoop(); - - // Override this to recieve all raw keycodes. By default - // this function will try to translate each keycode into - // a printable character and call HandleCharacter. - virtual void HandleKeycode(Keycode code, Action action); - - // This function is called by the default HandleKeycode - // implementation if you do not override it. If it recieves - // input that corresponds to a printable character it will - virtual void HandleCharacter(char c){}; - - private: - PortServer server_; - - bool extended_on_ = false; - - bool lshift_ = false; - bool rshift_ = false; - - Keycode ScancodeToKeycode(uint8_t scancode); - Action ScancodeToAction(uint8_t scancode); - - bool IsShift() { return lshift_ || rshift_; } -}; - -} // namespace mmth diff --git a/lib/mammoth/ipc/channel.cpp b/lib/mammoth/ipc/channel.cpp deleted file mode 100644 index a014bfd..0000000 --- a/lib/mammoth/ipc/channel.cpp +++ /dev/null @@ -1,60 +0,0 @@ -#include "ipc/channel.h" - -#include - -#include "util/debug.h" - -namespace mmth { -namespace { - -uint64_t strlen(const char* ptr) { - uint64_t len = 0; - while (*ptr != '\0') { - len++; - ptr++; - } - return len; -} - -} // namespace - -void Channel::adopt_cap(uint64_t id) { - if (chan_cap_ != 0) { - crash("Adopting over channel.", glcr::ALREADY_EXISTS); - } - chan_cap_ = id; -} -z_cap_t Channel::release_cap() { - z_cap_t cap = chan_cap_; - chan_cap_ = 0; - return cap; -} - -z_cap_t Channel::cap() { return chan_cap_; } - -z_err_t Channel::WriteStr(const char* msg) { - if (!chan_cap_) { - return glcr::NULL_PTR; - } - return ZChannelSend(chan_cap_, strlen(msg), msg, 0, nullptr); -} - -z_err_t Channel::ReadStr(char* buffer, uint64_t* size) { - if (!chan_cap_) { - return glcr::NULL_PTR; - } - uint64_t num_caps = 0; - return ZChannelRecv(chan_cap_, size, reinterpret_cast(buffer), - &num_caps, nullptr); -} - -z_err_t CreateChannels(Channel& c1, Channel& c2) { - z_cap_t chan1, chan2; - RET_ERR(ZChannelCreate(&chan1, &chan2)); - - c1.adopt_cap(chan1); - c2.adopt_cap(chan2); - return glcr::OK; -} - -} // namespace mmth diff --git a/lib/mammoth/ipc/channel.h b/lib/mammoth/ipc/channel.h deleted file mode 100644 index f290d92..0000000 --- a/lib/mammoth/ipc/channel.h +++ /dev/null @@ -1,51 +0,0 @@ -#pragma once - -#include -#include -#include - -namespace mmth { - -class Channel { - public: - Channel() {} - void adopt_cap(uint64_t id); - z_cap_t release_cap(); - z_cap_t cap(); - - z_err_t WriteStr(const char* msg); - z_err_t ReadStr(char* buffer, uint64_t* size); - - template - z_err_t WriteStruct(T*); - - template - z_err_t ReadStructAndCap(T*, uint64_t*); - - // FIXME: Close channel here. - ~Channel() {} - - private: - z_cap_t chan_cap_ = 0; -}; - -uint64_t CreateChannels(Channel& c1, Channel& c2); - -template -z_err_t Channel::WriteStruct(T* obj) { - return ZChannelSend(chan_cap_, sizeof(T), obj, 0, nullptr); -} - -template -z_err_t Channel::ReadStructAndCap(T* obj, uint64_t* cap) { - uint64_t num_bytes = sizeof(T); - uint64_t num_caps = 1; - RET_ERR(ZChannelRecv(chan_cap_, &num_bytes, obj, &num_caps, cap)); - - if (num_caps != 1 || num_bytes != sizeof(T)) { - return glcr::FAILED_PRECONDITION; - } - return glcr::OK; -} - -} // namespace mmth diff --git a/lib/mammoth/ipc/endpoint_client.cpp b/lib/mammoth/ipc/endpoint_client.cpp deleted file mode 100644 index 33c50cf..0000000 --- a/lib/mammoth/ipc/endpoint_client.cpp +++ /dev/null @@ -1,9 +0,0 @@ -#include "ipc/endpoint_server.h" - -namespace mmth { - -glcr::UniquePtr EndpointClient::AdoptEndpoint(z_cap_t cap) { - return glcr::UniquePtr(new EndpointClient(cap)); -} - -} // namespace mmth diff --git a/lib/mammoth/ipc/endpoint_client.h b/lib/mammoth/ipc/endpoint_client.h deleted file mode 100644 index 41227b1..0000000 --- a/lib/mammoth/ipc/endpoint_client.h +++ /dev/null @@ -1,69 +0,0 @@ -#pragma once - -#include -#include -#include -#include -#include - -namespace mmth { - -class EndpointClient { - public: - EndpointClient() = delete; - EndpointClient(const EndpointClient&) = delete; - EndpointClient& operator=(const EndpointClient&) = delete; - - static glcr::UniquePtr AdoptEndpoint(z_cap_t cap); - - template - glcr::ErrorOr> CallEndpointGetCap(const Req& req); - - template - glcr::ErrorOr CallEndpoint(const Req& req); - - z_cap_t GetCap() const { return cap_; } - - private: - EndpointClient(uint64_t cap) : cap_(cap) {} - z_cap_t cap_; -}; - -template -glcr::ErrorOr> EndpointClient::CallEndpointGetCap( - const Req& req) { - uint64_t reply_port_cap; - RET_ERR(ZEndpointSend(cap_, sizeof(Req), &req, 0, nullptr, &reply_port_cap)); - - Resp resp; - z_cap_t cap = 0; - uint64_t num_caps = 1; - uint64_t num_bytes = sizeof(Resp); - RET_ERR(ZReplyPortRecv(reply_port_cap, &num_bytes, &resp, &num_caps, &cap)); - - if (num_bytes != sizeof(resp) || num_caps != 1) { - return glcr::FAILED_PRECONDITION; - } - - return glcr::Pair{resp, cap}; -} - -template -glcr::ErrorOr EndpointClient::CallEndpoint(const Req& req) { - uint64_t reply_port_cap; - RET_ERR(ZEndpointSend(cap_, sizeof(Req), &req, 0, nullptr, &reply_port_cap)); - - Resp resp; - uint64_t num_bytes = sizeof(Resp); - uint64_t num_caps = 0; - RET_ERR( - ZReplyPortRecv(reply_port_cap, &num_bytes, &resp, &num_caps, nullptr)); - - if (num_bytes != sizeof(resp)) { - return glcr::FAILED_PRECONDITION; - } - - return resp; -} - -} // namespace mmth diff --git a/lib/mammoth/ipc/endpoint_server.cpp b/lib/mammoth/ipc/endpoint_server.cpp deleted file mode 100644 index 823c5f0..0000000 --- a/lib/mammoth/ipc/endpoint_server.cpp +++ /dev/null @@ -1,45 +0,0 @@ -#include "ipc/endpoint_server.h" - -#include "util/debug.h" - -namespace mmth { -// Declared as friend in EndpointServer. -void EndpointServerThreadBootstrap(void* endpoint_server) { - reinterpret_cast(endpoint_server)->ServerThread(); -} - -glcr::ErrorOr> EndpointServer::CreateClient() { - uint64_t client_cap; - RET_ERR(ZCapDuplicate(endpoint_cap_, ~(kZionPerm_Read), &client_cap)); - return EndpointClient::AdoptEndpoint(client_cap); -} - -Thread EndpointServer::RunServer() { - return Thread(EndpointServerThreadBootstrap, this); -} - -void EndpointServer::ServerThread() { - while (true) { - uint64_t message_size = kBufferSize; - uint64_t reply_port_cap = 0; - uint64_t num_caps = 0; - glcr::ErrorCode err = static_cast( - ZEndpointRecv(endpoint_cap_, &message_size, recieve_buffer_, &num_caps, - nullptr, &reply_port_cap)); - if (err != glcr::OK) { - dbgln("Error in receive: {x}", err); - continue; - } - - RequestContext request(recieve_buffer_, message_size); - ResponseContext response(reply_port_cap); - // FIXME: Consider pumping these errors into the response as well. - check(HandleRequest(request, response)); - if (!response.HasWritten()) { - dbgln("Returning without having written a response. Req type {x}", - request.request_id()); - } - } -} - -} // namespace mmth diff --git a/lib/mammoth/ipc/endpoint_server.h b/lib/mammoth/ipc/endpoint_server.h deleted file mode 100644 index 7ce438d..0000000 --- a/lib/mammoth/ipc/endpoint_server.h +++ /dev/null @@ -1,39 +0,0 @@ -#pragma once - -#include -#include -#include - -#include "mammoth/ipc/endpoint_client.h" -#include "mammoth/ipc/request_context.h" -#include "mammoth/ipc/response_context.h" -#include "mammoth/proc/thread.h" - -namespace mmth { -class EndpointServer { - public: - EndpointServer() = delete; - EndpointServer(const EndpointServer&) = delete; - EndpointServer& operator=(const EndpointServer&) = delete; - - glcr::ErrorOr> CreateClient(); - - Thread RunServer(); - - virtual glcr::ErrorCode HandleRequest(RequestContext& request, - ResponseContext& response) = 0; - - protected: - EndpointServer(z_cap_t cap) : endpoint_cap_(cap) {} - - private: - z_cap_t endpoint_cap_; - - static const uint64_t kBufferSize = 1024; - uint8_t recieve_buffer_[kBufferSize]; - - friend void EndpointServerThreadBootstrap(void* endpoint_server); - void ServerThread(); -}; - -} // namespace mmth diff --git a/lib/mammoth/ipc/port_client.cpp b/lib/mammoth/ipc/port_client.cpp index 3c9f909..db6c60f 100644 --- a/lib/mammoth/ipc/port_client.cpp +++ b/lib/mammoth/ipc/port_client.cpp @@ -19,4 +19,14 @@ glcr::ErrorCode PortClient::WriteByte(uint8_t byte) { return static_cast( ZPortSend(port_cap_, 1, &byte, 0, nullptr)); } + +glcr::ErrorCode PortClient::Write(uint16_t data) { + return static_cast( + ZPortSend(port_cap_, 2, &data, 0, nullptr)); +} + +glcr::ErrorCode PortClient::Write(uint64_t data) { + return static_cast( + ZPortSend(port_cap_, 8, &data, 0, nullptr)); +} } // namespace mmth diff --git a/lib/mammoth/ipc/port_client.h b/lib/mammoth/ipc/port_client.h index 80571a3..a633357 100644 --- a/lib/mammoth/ipc/port_client.h +++ b/lib/mammoth/ipc/port_client.h @@ -10,6 +10,7 @@ namespace mmth { class PortClient { public: PortClient() {} + PortClient(z_cap_t port_cap); static PortClient AdoptPort(z_cap_t port_cap); template @@ -18,6 +19,8 @@ class PortClient { glcr::ErrorCode WriteString(glcr::String str, z_cap_t cap); glcr::ErrorCode WriteByte(uint8_t byte); + glcr::ErrorCode Write(uint16_t data); + glcr::ErrorCode Write(uint64_t data); z_cap_t cap() { return port_cap_; } @@ -25,8 +28,6 @@ class PortClient { private: z_cap_t port_cap_ = 0; - - PortClient(z_cap_t port_cap); }; template diff --git a/lib/mammoth/ipc/port_server.cpp b/lib/mammoth/ipc/port_server.cpp index 4643143..f81e3b8 100644 --- a/lib/mammoth/ipc/port_server.cpp +++ b/lib/mammoth/ipc/port_server.cpp @@ -55,4 +55,12 @@ glcr::ErrorOr PortServer::RecvChar() { return byte; } +glcr::ErrorOr PortServer::RecvUint16() { + uint64_t bytes = 2; + uint64_t caps = 0; + uint16_t data; + RET_ERR(ZPortRecv(port_cap_, &bytes, &data, &caps, nullptr)); + return data; +} + } // namespace mmth diff --git a/lib/mammoth/ipc/port_server.h b/lib/mammoth/ipc/port_server.h index 9e46599..acc5430 100644 --- a/lib/mammoth/ipc/port_server.h +++ b/lib/mammoth/ipc/port_server.h @@ -19,6 +19,7 @@ class PortServer { glcr::ErrorCode PollForIntCap(uint64_t* msg, uint64_t* cap); glcr::ErrorOr RecvChar(); + glcr::ErrorOr RecvUint16(); z_cap_t cap() { return port_cap_; } diff --git a/lib/mammoth/ipc/request_context.h b/lib/mammoth/ipc/request_context.h deleted file mode 100644 index 0f8ca96..0000000 --- a/lib/mammoth/ipc/request_context.h +++ /dev/null @@ -1,32 +0,0 @@ -#pragma once - -#include -#include - -class RequestContext { - public: - RequestContext(void* buffer, uint64_t buffer_length) - : buffer_(buffer), buffer_length_(buffer_length) { - if (buffer_length_ < sizeof(uint64_t)) { - request_id_ = -1; - } else { - request_id_ = *reinterpret_cast(buffer); - } - } - - uint64_t request_id() { return request_id_; } - - template - glcr::ErrorCode As(T** arg) { - if (buffer_length_ < sizeof(T)) { - return glcr::INVALID_ARGUMENT; - } - *arg = reinterpret_cast(buffer_); - return glcr::OK; - } - - private: - uint64_t request_id_; - void* buffer_; - uint64_t buffer_length_; -}; diff --git a/lib/mammoth/proc/process.cpp b/lib/mammoth/proc/process.cpp deleted file mode 100644 index c7a79e5..0000000 --- a/lib/mammoth/proc/process.cpp +++ /dev/null @@ -1,147 +0,0 @@ -#include "proc/process.h" - -#include -#include - -#include "ipc/endpoint_server.h" -#include "ipc/port_client.h" -#include "ipc/port_server.h" -#include "util/debug.h" -#include "util/init.h" - -#define MAM_PROC_DEBUG 0 - -namespace mmth { -namespace { - -typedef struct { - char ident[16]; - uint16_t type; - uint16_t machine; - uint32_t version; - uint64_t entry; - uint64_t phoff; - uint64_t shoff; - uint32_t flags; - uint16_t ehsize; - uint16_t phentsize; - uint16_t phnum; - uint16_t shentsize; - uint16_t shnum; - uint16_t shstrndx; -} Elf64Header; - -typedef struct { - uint32_t name; - uint32_t type; - uint64_t flags; - uint64_t addr; - uint64_t offset; - uint64_t size; - uint32_t link; - uint32_t info; - uint64_t addralign; - uint64_t entsize; -} Elf64SectionHeader; - -typedef struct { - uint32_t type; - uint32_t flags; - uint64_t offset; - uint64_t vaddr; - uint64_t paddr; - uint64_t filesz; - uint64_t memsz; - uint64_t align; -} Elf64ProgramHeader; - -void memcpy(uint64_t base, uint64_t len, uint64_t dest) { - uint8_t* srcptr = reinterpret_cast(base); - uint8_t* destptr = reinterpret_cast(dest); - for (uint64_t i = 0; i < len; i++) { - destptr[i] = srcptr[i]; - } -} - -uint64_t LoadElfProgram(uint64_t base, uint64_t as_cap) { - Elf64Header* header = reinterpret_cast(base); - Elf64ProgramHeader* programs = - reinterpret_cast(base + header->phoff); - for (uint64_t i = 0; i < header->phnum; i++) { - Elf64ProgramHeader& program = programs[i]; -#if MAM_PROC_DEBUG - dbgln("Create mem object"); -#endif - uint64_t page_offset = program.vaddr & 0xFFF; - uint64_t mem_cap; - uint64_t size = page_offset + program.memsz; - check(ZMemoryObjectCreate(size, &mem_cap)); - -#if MAM_PROC_DEBUG - dbgln("Map Local"); -#endif - uint64_t vaddr; - check(ZAddressSpaceMap(gSelfVmasCap, 0, mem_cap, 0, &vaddr)); - uint8_t* offset = reinterpret_cast(vaddr); - for (uint64_t j = 0; j < size; j++) { - offset[j] = 0; - } - -#if MAM_PROC_DEBUG - dbgln("Copy"); -#endif - memcpy(base + program.offset, program.filesz, vaddr + page_offset); - -#if MAM_PROC_DEBUG - dbgln("Map Foreign"); -#endif - check(ZAddressSpaceMap(as_cap, program.vaddr - page_offset, mem_cap, 0, - &vaddr)); - } - return header->entry; -} - -} // namespace - -glcr::ErrorOr SpawnProcessFromElfRegion(uint64_t program, - z_cap_t yellowstone_client) { - uint64_t proc_cap; - uint64_t as_cap; - uint64_t foreign_port_id; - uint64_t port_cap; - -#if MAM_PROC_DEBUG - dbgln("Port Create"); -#endif - ASSIGN_OR_RETURN(PortServer server, PortServer::Create()); - ASSIGN_OR_RETURN(PortClient pclient, server.CreateClient()); - -#if MAM_PROC_DEBUG - dbgln("Spawn"); -#endif - RET_ERR(ZProcessSpawn(gSelfProcCap, server.cap(), &proc_cap, &as_cap, - &foreign_port_id)); - - uint64_t entry_point = LoadElfProgram(program, as_cap); - -#if MAM_PROC_DEBUG - dbgln("Thread Create"); -#endif - uint64_t thread_cap; - RET_ERR(ZThreadCreate(proc_cap, &thread_cap)); - - uint64_t dup_proc_cap; - RET_ERR(ZCapDuplicate(proc_cap, kZionPerm_All, &dup_proc_cap)); - RET_ERR(pclient.WriteMessage(Z_INIT_SELF_PROC, dup_proc_cap)); - RET_ERR(pclient.WriteMessage(Z_INIT_SELF_VMAS, as_cap)); - RET_ERR(pclient.WriteMessage(Z_INIT_ENDPOINT, yellowstone_client)); - -#if MAM_PROC_DEBUG - dbgln("Thread start"); -#endif - RET_ERR(ZThreadStart(thread_cap, entry_point, foreign_port_id, 0)); - - return proc_cap; -} - -} // namespace mmth diff --git a/lib/mammoth/proc/process.h b/lib/mammoth/proc/process.h deleted file mode 100644 index c5a0ef4..0000000 --- a/lib/mammoth/proc/process.h +++ /dev/null @@ -1,12 +0,0 @@ -#pragma once - -#include -#include -#include - -namespace mmth { - -glcr::ErrorOr SpawnProcessFromElfRegion(uint64_t program, - z_cap_t yellowstone_client); - -} // namespace mmth diff --git a/lib/mammoth/sync/mutex.cpp b/lib/mammoth/sync/mutex.cpp deleted file mode 100644 index 9b134ed..0000000 --- a/lib/mammoth/sync/mutex.cpp +++ /dev/null @@ -1,31 +0,0 @@ -#include "sync/mutex.h" - -#include - -namespace mmth { - -Mutex::Mutex(Mutex&& other) : mutex_cap_(other.mutex_cap_) { - other.mutex_cap_ = 0; -} - -Mutex& Mutex::operator=(Mutex&& other) { - // TODO: Release existing mutex if it exists. - mutex_cap_ = other.mutex_cap_; - other.mutex_cap_ = 0; - return *this; -} - -glcr::ErrorOr Mutex::Create() { - z_cap_t mutex_cap; - RET_ERR(ZMutexCreate(&mutex_cap)); - return Mutex(mutex_cap); -} - -glcr::ErrorCode Mutex::Lock() { - return static_cast(ZMutexLock(mutex_cap_)); -} -glcr::ErrorCode Mutex::Release() { - return static_cast(ZMutexRelease(mutex_cap_)); -} - -} // namespace mmth diff --git a/lib/mammoth/sync/mutex.h b/lib/mammoth/sync/mutex.h deleted file mode 100644 index ee77394..0000000 --- a/lib/mammoth/sync/mutex.h +++ /dev/null @@ -1,25 +0,0 @@ -#pragma once - -#include -#include - -namespace mmth { - -class Mutex { - public: - Mutex(const Mutex&) = delete; - Mutex(Mutex&&); - Mutex& operator=(Mutex&&); - - static glcr::ErrorOr Create(); - - glcr::ErrorCode Lock(); - glcr::ErrorCode Release(); - - private: - z_cap_t mutex_cap_; - - Mutex(z_cap_t mutex_cap) : mutex_cap_(mutex_cap) {} -}; - -} // namespace mmth diff --git a/lib/mammoth/util/memory_region.cpp b/lib/mammoth/util/memory_region.cpp index a4989f2..caa9eb9 100644 --- a/lib/mammoth/util/memory_region.cpp +++ b/lib/mammoth/util/memory_region.cpp @@ -71,4 +71,8 @@ z_cap_t OwnedMemoryRegion::DuplicateCap() { return cap; } +OwnedMemoryRegion OwnedMemoryRegion::Duplicate() { + return OwnedMemoryRegion::FromCapability(DuplicateCap()); +} + } // namespace mmth diff --git a/lib/mammoth/util/memory_region.h b/lib/mammoth/util/memory_region.h index 97d1cf6..37c34b5 100644 --- a/lib/mammoth/util/memory_region.h +++ b/lib/mammoth/util/memory_region.h @@ -25,14 +25,15 @@ class OwnedMemoryRegion { static OwnedMemoryRegion ContiguousPhysical(uint64_t size, uint64_t* paddr); static OwnedMemoryRegion DirectPhysical(uint64_t paddr, uint64_t size); - uint64_t vaddr() { return vaddr_; } - uint64_t size() { return size_; } + uint64_t vaddr() const { return vaddr_; } + uint64_t size() const { return size_; } - z_cap_t cap() { return vmmo_cap_; } + z_cap_t cap() const { return vmmo_cap_; } z_cap_t DuplicateCap(); + OwnedMemoryRegion Duplicate(); - bool empty() { return vmmo_cap_ != 0; } - explicit operator bool() { return vmmo_cap_ != 0; } + bool empty() const { return vmmo_cap_ == 0; } + explicit operator bool() const { return vmmo_cap_ != 0; } private: OwnedMemoryRegion(uint64_t vmmo_cap, uint64_t vaddr, uint64_t size) diff --git a/lib/mammoth/util/new.cpp b/lib/mammoth/util/new.cpp index 9e9c2a5..3357d1e 100644 --- a/lib/mammoth/util/new.cpp +++ b/lib/mammoth/util/new.cpp @@ -12,7 +12,7 @@ class PageAllocator { public: static uint64_t AllocatePagePair() { uint64_t mem_cap; - check(ZMemoryObjectCreate(0x2000, &mem_cap)); + check(ZMemoryObjectCreate(0x4000, &mem_cap)); uint64_t vaddr; check(ZAddressSpaceMap(gSelfVmasCap, 0, mem_cap, /* align= */ 0x2000, @@ -60,8 +60,8 @@ class BuddyAllocator { void* Allocate(uint64_t size) { check(ZMutexLock(mutex_cap_)); - if (size > (0x2000 - sizeof(BuddySlot))) { - crash("Can't allocate greater than one page", glcr::UNIMPLEMENTED); + if (size > (0x4000 - sizeof(BuddySlot))) { + crash("Can't allocate greater than four pages", glcr::UNIMPLEMENTED); } if (free_front_ == nullptr) { AddPage(); diff --git a/lib/yunq/CMakeLists.txt b/lib/yunq/CMakeLists.txt index dea897d..2c87371 100644 --- a/lib/yunq/CMakeLists.txt +++ b/lib/yunq/CMakeLists.txt @@ -1,4 +1,5 @@ set(yunq_files + message_view.cpp serialize.cpp ) @@ -15,3 +16,7 @@ target_include_directories(yunq set_target_properties(yunq PROPERTIES COMPILE_FLAGS "${CMAKE_CXX_FLAGS} ${BASE_COMPILE_FLAGS}") + +if (enable_testing) + add_subdirectory(test) +endif() diff --git a/lib/yunq/message_view.cpp b/lib/yunq/message_view.cpp new file mode 100644 index 0000000..4a3b7df --- /dev/null +++ b/lib/yunq/message_view.cpp @@ -0,0 +1,72 @@ +#include "message_view.h" + +namespace yunq { + +namespace { + +const uint64_t kIdentByte = 0x33441122; + +} // namespace + +glcr::Status MessageView::CheckHeader() const { + if (buffer_.At(offset_ + 0) != kIdentByte) { + return glcr::InvalidArgument("Trying to parse an invalid yunq message."); + } + // TODO: Parse core size. + // TODO: Parse extension size. + // TODO: Check CRC32 + // TODO: Parse options. + return glcr::Status::Ok(); +} + +uint32_t MessageView::MessageLength() const { + return buffer_.At(offset_ + 8); +} + +template <> +glcr::ErrorOr MessageView::ReadField( + uint64_t field_index) const { + return buffer_.At(field_offset(field_index)); +} + +template <> +glcr::ErrorOr MessageView::ReadField( + uint64_t field_index) const { + return buffer_.At(field_offset(field_index)); +} + +template <> +glcr::ErrorOr MessageView::ReadField( + uint64_t field_index) const { + ExtensionPointer ptr = + buffer_.At(field_offset(field_index)); + + return buffer_.StringAt(offset_ + ptr.offset, ptr.length); +} + +template <> +glcr::ErrorOr> MessageView::ReadRepeated( + uint64_t field_index) const { + ExtensionPointer pointer = + buffer_.At(field_offset(field_index)); + + glcr::Vector v; + v.Resize(pointer.length / sizeof(uint64_t)); + for (uint64_t i = offset_ + pointer.offset; + i < offset_ + pointer.offset + pointer.length; i += sizeof(uint64_t)) { + v.PushBack(buffer_.At(i)); + } + return v; +} + +glcr::ErrorOr MessageView::ReadCapability( + uint64_t field_index) const { + return buffer_.At(field_offset(field_index)); +} + +glcr::ErrorOr MessageView::ReadCapability( + uint64_t field_index, const glcr::CapBuffer& caps) const { + uint64_t offset = buffer_.At(field_offset(field_index)); + return caps.At(offset); +} +} // namespace yunq diff --git a/lib/yunq/message_view.h b/lib/yunq/message_view.h new file mode 100644 index 0000000..4be5535 --- /dev/null +++ b/lib/yunq/message_view.h @@ -0,0 +1,91 @@ +#pragma once + +#include +#include +#include +#include +#include + +#include "yunq/yunq.h" + +namespace yunq { + +class MessageView { + public: + MessageView(const glcr::ByteBuffer& buffer, uint64_t offset) + : buffer_(buffer), offset_(offset) {} + + [[nodiscard]] glcr::Status CheckHeader() const; + uint32_t MessageLength() const; + + // TODO: Implement glcr::StatusOr + template + glcr::ErrorOr ReadField(uint64_t field_index) const; + + template + glcr::ErrorOr> ReadRepeated(uint64_t field_index) const; + + glcr::ErrorOr ReadCapability(uint64_t field_index) const; + glcr::ErrorOr ReadCapability(uint64_t field_index, + const glcr::CapBuffer& caps) const; + + template + glcr::Status ReadMessage(uint64_t field_index, T& message) const; + + template + glcr::Status ReadRepeatedMessage(uint64_t field_index, + glcr::Vector& messages) const; + + private: + const glcr::ByteBuffer& buffer_; + uint64_t offset_; + + uint64_t field_offset(uint64_t field_index) const { + return offset_ + kHeaderSize + (8 * field_index); + } +}; + +template <> +glcr::ErrorOr MessageView::ReadField( + uint64_t field_index) const; + +template <> +glcr::ErrorOr MessageView::ReadField( + uint64_t field_index) const; + +template <> +glcr::ErrorOr MessageView::ReadField( + uint64_t field_index) const; + +template <> +glcr::ErrorOr> MessageView::ReadRepeated( + uint64_t field_index) const; + +template +glcr::Status MessageView::ReadMessage(uint64_t field_index, T& message) const { + ExtensionPointer ptr = + buffer_.At(field_offset(field_index)); + + MessageView subview(buffer_, offset_ + ptr.offset); + return message.ParseFromBytes(subview); +} + +template +glcr::Status MessageView::ReadRepeatedMessage(uint64_t field_index, + glcr::Vector& messages) const { + ExtensionPointer ptr = + buffer_.At(field_offset(field_index)); + + uint64_t ext_offset = ptr.offset; + + while (ext_offset < ptr.offset + ptr.length) { + MessageView subview(buffer_, offset_ + ext_offset); + messages.EmplaceBack(); + RETURN_ERROR(messages.PeekBack().ParseFromBytes(subview)); + ext_offset += subview.MessageLength(); + } + + return glcr::Status::Ok(); +} + +} // namespace yunq diff --git a/lib/yunq/serialize.cpp b/lib/yunq/serialize.cpp index afaad06..9d58ac8 100644 --- a/lib/yunq/serialize.cpp +++ b/lib/yunq/serialize.cpp @@ -3,27 +3,71 @@ namespace yunq { namespace { -const uint64_t kIdentByte = 0x33441122; +const uint32_t kIdentByte = 0x33441122; } // namespace -glcr::Status CheckHeader(const glcr::ByteBuffer& buffer, uint64_t offset) { - if (buffer.At(offset + 0) != kIdentByte) { - return glcr::InvalidArgument("Trying to parse an invalid yunq message."); - } - // TODO: Parse core size. - // TODO: Parse extension size. - // TODO: Check CRC32 - // TODO: Parse options. - return glcr::Status::Ok(); +void Serializer::WriteHeader() { + buffer_.WriteAt(offset_ + 0, kIdentByte); + buffer_.WriteAt(offset_ + 4, core_size_); + buffer_.WriteAt(offset_ + 8, next_extension_); + buffer_.WriteAt(offset_ + 12, 0); // TODO: Calculate CRC32. } -void WriteHeader(glcr::ByteBuffer& bytes, uint64_t offset, uint32_t core_size, - uint32_t extension_size) { - bytes.WriteAt(offset + 0, kIdentByte); - bytes.WriteAt(offset + 4, core_size); - bytes.WriteAt(offset + 8, extension_size); - bytes.WriteAt(offset + 12, 0); // TODO: Calculate CRC32. +template <> +void Serializer::WriteField(uint64_t field_index, + const uint64_t& value) { + buffer_.WriteAt(field_offset(field_index), value); } +template <> +void Serializer::WriteField(uint64_t field_index, + const int64_t& value) { + buffer_.WriteAt(field_offset(field_index), value); +} + +template <> +void Serializer::WriteField(uint64_t field_index, + const glcr::String& value) { + ExtensionPointer ptr{ + .offset = (uint32_t)next_extension_, + // FIXME: Check downcast of str length. + .length = (uint32_t)value.length(), + }; + + buffer_.WriteStringAt(offset_ + next_extension_, value); + next_extension_ += ptr.length; + + buffer_.WriteAt(field_offset(field_index), ptr); +} + +template <> +void Serializer::WriteRepeated(uint64_t field_index, + const glcr::Vector& value) { + ExtensionPointer ptr{ + .offset = (uint32_t)next_extension_, + .length = (uint32_t)(value.size() * sizeof(uint64_t)), + }; + + next_extension_ += ptr.length; + buffer_.WriteAt(field_offset(field_index), ptr); + + for (uint64_t i = 0; i < value.size(); i++) { + uint32_t ext_offset = offset_ + ptr.offset + (i * sizeof(uint64_t)); + buffer_.WriteAt(ext_offset, value.at(i)); + } +} + +void Serializer::WriteCapability(uint64_t field_index, uint64_t value) { + if (caps_) { + buffer_.WriteAt(field_offset(field_index), next_cap_); + caps_.value().get().WriteAt(next_cap_++, value); + } else { + WriteField(field_index, value); + } +} + +void Serializer::WriteRepeatedCapability(uint64_t field_index, + const glcr::Vector& value) {} + } // namespace yunq diff --git a/lib/yunq/serialize.h b/lib/yunq/serialize.h index 2e90779..170917f 100644 --- a/lib/yunq/serialize.h +++ b/lib/yunq/serialize.h @@ -1,14 +1,126 @@ #pragma once #include +#include +#include +#include +#include #include +#include "yunq/yunq.h" + namespace yunq { -[[nodiscard]] glcr::Status CheckHeader(const glcr::ByteBuffer& buffer, - uint64_t offset); +class Serializer { + public: + Serializer(glcr::ByteBuffer& bytes, uint64_t offset, uint64_t num_fields) + : buffer_(bytes), + offset_(offset), + next_extension_(kHeaderSize + (8 * num_fields)), + core_size_(next_extension_), + caps_() {} + Serializer(glcr::ByteBuffer& bytes, uint64_t offset, uint64_t num_fields, + glcr::CapBuffer& caps) + : buffer_(bytes), + offset_(offset), + next_extension_(kHeaderSize + (8 * num_fields)), + core_size_(next_extension_), + caps_(caps) {} + template + void WriteField(uint64_t field_index, const T& value); -void WriteHeader(glcr::ByteBuffer& bytes, uint64_t offset, uint32_t core_size, - uint32_t extension_size); + template + void WriteRepeated(uint64_t field_index, const glcr::Vector& value); + + void WriteCapability(uint64_t field_index, uint64_t value); + + void WriteRepeatedCapability(uint64_t field_index, + const glcr::Vector& value); + + template + void WriteMessage(uint64_t field_index, const T& value); + + template + void WriteRepeatedMessage(uint64_t field_index, const glcr::Vector& value); + + void WriteHeader(); + + uint64_t size() const { return next_extension_; } + + private: + glcr::ByteBuffer& buffer_; + uint64_t offset_; + uint64_t next_extension_; + uint64_t core_size_; + uint64_t next_cap_ = 0; + glcr::Optional> caps_; + + uint64_t field_offset(uint64_t field_index) const { + return offset_ + kHeaderSize + (8 * field_index); + } +}; + +template <> +void Serializer::WriteField(uint64_t field_index, + const uint64_t& value); + +template <> +void Serializer::WriteField(uint64_t field_index, + const int64_t& value); + +template <> +void Serializer::WriteField(uint64_t field_index, + const glcr::String& value); + +template <> +void Serializer::WriteRepeated(uint64_t field_index, + const glcr::Vector& value); + +template +void Serializer::WriteMessage(uint64_t field_index, const T& value) { + uint64_t length = 0; + if (caps_) { + length = value.SerializeToBytes(buffer_, offset_ + next_extension_, + caps_.value().get()); + } else { + length = value.SerializeToBytes(buffer_, offset_ + next_extension_); + } + + ExtensionPointer ptr{ + .offset = (uint32_t)next_extension_, + .length = (uint32_t)length, + }; + + next_extension_ += length; + + buffer_.WriteAt(field_offset(field_index), ptr); +} + +template +void Serializer::WriteRepeatedMessage(uint64_t field_index, + const glcr::Vector& value) { + uint64_t next_offset = next_extension_; + uint64_t length = value.size(); + + for (T& message : value) { + uint64_t msg_length = 0; + if (caps_) { + msg_length = message.SerializeToBytes(buffer_, offset_ + next_offset, + caps_.value().get()); + } else { + msg_length = message.SerializeToBytes(buffer_, offset_ + next_offset); + } + next_offset += msg_length; + } + + ExtensionPointer ptr{ + .offset = (uint32_t)next_extension_, + .length = (uint32_t)length, + }; + + next_extension_ = next_offset; + + buffer_.WriteAt(field_offset(field_index), ptr); +} } // namespace yunq diff --git a/lib/yunq/test/CMakeLists.txt b/lib/yunq/test/CMakeLists.txt new file mode 100644 index 0000000..d33b8e2 --- /dev/null +++ b/lib/yunq/test/CMakeLists.txt @@ -0,0 +1,44 @@ +add_executable(yunq_test yunq_test.cpp) + +add_dependencies(yunq_test + example_yunq) + +target_link_libraries(yunq_test + Catch2::Catch2WithMain + example_yunq) + +target_include_directories(yunq_test PRIVATE "." "../../../zion/include") + +add_test(NAME yunq_test COMMAND $) + +add_dependencies(build_test + yunq_test) + + +# Build the yunq manually rather than using the generator +# because we don't want to link against mammoth and overrite new. +set(target example_yunq) +add_library(example_yunq + ${CMAKE_CURRENT_SOURCE_DIR}/example/example.yunq.cpp + ) + +target_include_directories(example_yunq + PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/example" + ) + +target_link_libraries(${target} + glacier + yunq + zion_stub +) + +set(PYTHON "${CMAKE_SOURCE_DIR}/yunq/venv/bin/python") +set(YUNQ "${CMAKE_SOURCE_DIR}/yunq/yunq.py") + +add_custom_command( + OUTPUT + ${CMAKE_CURRENT_SOURCE_DIR}/example/example.yunq.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/example/example.yunq.h + COMMAND ${PYTHON} ${YUNQ} ${CMAKE_CURRENT_SOURCE_DIR}/example/example.yunq + DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/example/example.yunq +) diff --git a/lib/yunq/test/example/example.yunq b/lib/yunq/test/example/example.yunq new file mode 100644 index 0000000..7b11e08 --- /dev/null +++ b/lib/yunq/test/example/example.yunq @@ -0,0 +1,27 @@ +package ex; + +message Basic { + u64 unsigned_int; + i64 signed_int; + string strn; +} + +message Cap { + capability cap; +} + +message Repeated { + repeated u64 unsigned_ints; +} + + +message Nested { + Basic basic; + Cap cap1; + Cap cap2; +} + +message RepeatedNested { + repeated Basic basics; + repeated Cap caps; +} diff --git a/lib/yunq/test/example/example.yunq.cpp b/lib/yunq/test/example/example.yunq.cpp new file mode 100644 index 0000000..08cbd0f --- /dev/null +++ b/lib/yunq/test/example/example.yunq.cpp @@ -0,0 +1,178 @@ +// Generated file -- DO NOT MODIFY. +#include "example.yunq.h" + +#include +#include + + +namespace ex { + +namespace { + +const uint64_t header_size = 24; // 4x uint32, 1x uint64 + +struct ExtPointer { + uint32_t offset; + uint32_t length; +}; + +} // namespace +glcr::Status Basic::ParseFromBytes(const yunq::MessageView& message) { + RETURN_ERROR(ParseFromBytesInternal(message)); + return glcr::Status::Ok(); +} + +glcr::Status Basic::ParseFromBytes(const yunq::MessageView& message, const glcr::CapBuffer& caps) { + RETURN_ERROR(ParseFromBytesInternal(message)); + return glcr::Status::Ok(); +} + +glcr::Status Basic::ParseFromBytesInternal(const yunq::MessageView& message) { + RETURN_ERROR(message.CheckHeader()); + // Parse field. + ASSIGN_OR_RETURN(field_, message.ReadField(0)); + + return glcr::Status::Ok(); +} + +uint64_t Basic::SerializeToBytes(glcr::ByteBuffer& bytes, uint64_t offset) const { + yunq::Serializer serializer(bytes, offset, 1); + return SerializeInternal(serializer); +} + +uint64_t Basic::SerializeToBytes(glcr::ByteBuffer& bytes, uint64_t offset, glcr::CapBuffer& caps) const { + yunq::Serializer serializer(bytes, offset, 1, caps); + return SerializeInternal(serializer); +} + +uint64_t Basic::SerializeInternal(yunq::Serializer& serializer) const { + // Write field. + serializer.WriteField(0, field_); + + serializer.WriteHeader(); + + return serializer.size(); +} +glcr::Status Types::ParseFromBytes(const yunq::MessageView& message) { + RETURN_ERROR(ParseFromBytesInternal(message)); + return glcr::Status::Ok(); +} + +glcr::Status Types::ParseFromBytes(const yunq::MessageView& message, const glcr::CapBuffer& caps) { + RETURN_ERROR(ParseFromBytesInternal(message)); + return glcr::Status::Ok(); +} + +glcr::Status Types::ParseFromBytesInternal(const yunq::MessageView& message) { + RETURN_ERROR(message.CheckHeader()); + // Parse unsigned_int. + ASSIGN_OR_RETURN(unsigned_int_, message.ReadField(0)); + // Parse signed_int. + ASSIGN_OR_RETURN(signed_int_, message.ReadField(1)); + // Parse str. + ASSIGN_OR_RETURN(str_, message.ReadField(2)); + + return glcr::Status::Ok(); +} + +uint64_t Types::SerializeToBytes(glcr::ByteBuffer& bytes, uint64_t offset) const { + yunq::Serializer serializer(bytes, offset, 3); + return SerializeInternal(serializer); +} + +uint64_t Types::SerializeToBytes(glcr::ByteBuffer& bytes, uint64_t offset, glcr::CapBuffer& caps) const { + yunq::Serializer serializer(bytes, offset, 3, caps); + return SerializeInternal(serializer); +} + +uint64_t Types::SerializeInternal(yunq::Serializer& serializer) const { + // Write unsigned_int. + serializer.WriteField(0, unsigned_int_); + // Write signed_int. + serializer.WriteField(1, signed_int_); + // Write str. + serializer.WriteField(2, str_); + + serializer.WriteHeader(); + + return serializer.size(); +} +glcr::Status Cap::ParseFromBytes(const yunq::MessageView& message) { + RETURN_ERROR(ParseFromBytesInternal(message)); + // Parse cap. + ASSIGN_OR_RETURN(cap_, message.ReadCapability(0)); + return glcr::Status::Ok(); +} + +glcr::Status Cap::ParseFromBytes(const yunq::MessageView& message, const glcr::CapBuffer& caps) { + RETURN_ERROR(ParseFromBytesInternal(message)); + // Parse cap. + ASSIGN_OR_RETURN(cap_, message.ReadCapability(0, caps)); + return glcr::Status::Ok(); +} + +glcr::Status Cap::ParseFromBytesInternal(const yunq::MessageView& message) { + RETURN_ERROR(message.CheckHeader()); + // Parse cap. + + return glcr::Status::Ok(); +} + +uint64_t Cap::SerializeToBytes(glcr::ByteBuffer& bytes, uint64_t offset) const { + yunq::Serializer serializer(bytes, offset, 1); + return SerializeInternal(serializer); +} + +uint64_t Cap::SerializeToBytes(glcr::ByteBuffer& bytes, uint64_t offset, glcr::CapBuffer& caps) const { + yunq::Serializer serializer(bytes, offset, 1, caps); + return SerializeInternal(serializer); +} + +uint64_t Cap::SerializeInternal(yunq::Serializer& serializer) const { + // Write cap. + serializer.WriteCapability(0, cap_); + + serializer.WriteHeader(); + + return serializer.size(); +} +glcr::Status Repeated::ParseFromBytes(const yunq::MessageView& message) { + RETURN_ERROR(ParseFromBytesInternal(message)); + return glcr::Status::Ok(); +} + +glcr::Status Repeated::ParseFromBytes(const yunq::MessageView& message, const glcr::CapBuffer& caps) { + RETURN_ERROR(ParseFromBytesInternal(message)); + return glcr::Status::Ok(); +} + +glcr::Status Repeated::ParseFromBytesInternal(const yunq::MessageView& message) { + RETURN_ERROR(message.CheckHeader()); + // Parse unsigned_ints. + ASSIGN_OR_RETURN(unsigned_ints_, message.ReadRepeated(0)); + + + return glcr::Status::Ok(); +} + +uint64_t Repeated::SerializeToBytes(glcr::ByteBuffer& bytes, uint64_t offset) const { + yunq::Serializer serializer(bytes, offset, 1); + return SerializeInternal(serializer); +} + +uint64_t Repeated::SerializeToBytes(glcr::ByteBuffer& bytes, uint64_t offset, glcr::CapBuffer& caps) const { + yunq::Serializer serializer(bytes, offset, 1, caps); + return SerializeInternal(serializer); +} + +uint64_t Repeated::SerializeInternal(yunq::Serializer& serializer) const { + // Write unsigned_ints. + serializer.WriteRepeated(0, unsigned_ints_); + + serializer.WriteHeader(); + + return serializer.size(); +} + + +} // namepace ex diff --git a/lib/yunq/test/example/example.yunq.h b/lib/yunq/test/example/example.yunq.h new file mode 100644 index 0000000..83232e5 --- /dev/null +++ b/lib/yunq/test/example/example.yunq.h @@ -0,0 +1,129 @@ +// Generated file - DO NOT MODIFY +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include + + +namespace ex { + +class Basic { + public: + Basic() {} + // Delete copy and move until implemented. + Basic(const Basic&) = delete; + Basic(Basic&&) = default; + Basic& operator=(Basic&&) = default; + + [[nodiscard]] glcr::Status ParseFromBytes(const yunq::MessageView& message); + [[nodiscard]] glcr::Status ParseFromBytes(const yunq::MessageView& message, const glcr::CapBuffer&); + uint64_t SerializeToBytes(glcr::ByteBuffer&, uint64_t offset) const; + uint64_t SerializeToBytes(glcr::ByteBuffer&, uint64_t offset, glcr::CapBuffer&) const; + + const uint64_t& field() const { return field_; } + uint64_t& mutable_field() { return field_; } + void set_field(const uint64_t& value) { field_ = value; } + + private: + uint64_t field_; + + // Parses everything except for caps. + glcr::Status ParseFromBytesInternal(const yunq::MessageView& message); + + uint64_t SerializeInternal(yunq::Serializer& serializer) const; +}; +class Types { + public: + Types() {} + // Delete copy and move until implemented. + Types(const Types&) = delete; + Types(Types&&) = default; + Types& operator=(Types&&) = default; + + [[nodiscard]] glcr::Status ParseFromBytes(const yunq::MessageView& message); + [[nodiscard]] glcr::Status ParseFromBytes(const yunq::MessageView& message, const glcr::CapBuffer&); + uint64_t SerializeToBytes(glcr::ByteBuffer&, uint64_t offset) const; + uint64_t SerializeToBytes(glcr::ByteBuffer&, uint64_t offset, glcr::CapBuffer&) const; + + const uint64_t& unsigned_int() const { return unsigned_int_; } + uint64_t& mutable_unsigned_int() { return unsigned_int_; } + void set_unsigned_int(const uint64_t& value) { unsigned_int_ = value; } + + const int64_t& signed_int() const { return signed_int_; } + int64_t& mutable_signed_int() { return signed_int_; } + void set_signed_int(const int64_t& value) { signed_int_ = value; } + + const glcr::String& str() const { return str_; } + glcr::String& mutable_str() { return str_; } + void set_str(const glcr::String& value) { str_ = value; } + + private: + uint64_t unsigned_int_; + int64_t signed_int_; + glcr::String str_; + + // Parses everything except for caps. + glcr::Status ParseFromBytesInternal(const yunq::MessageView& message); + + uint64_t SerializeInternal(yunq::Serializer& serializer) const; +}; +class Cap { + public: + Cap() {} + // Delete copy and move until implemented. + Cap(const Cap&) = delete; + Cap(Cap&&) = default; + Cap& operator=(Cap&&) = default; + + [[nodiscard]] glcr::Status ParseFromBytes(const yunq::MessageView& message); + [[nodiscard]] glcr::Status ParseFromBytes(const yunq::MessageView& message, const glcr::CapBuffer&); + uint64_t SerializeToBytes(glcr::ByteBuffer&, uint64_t offset) const; + uint64_t SerializeToBytes(glcr::ByteBuffer&, uint64_t offset, glcr::CapBuffer&) const; + + const z_cap_t& cap() const { return cap_; } + z_cap_t& mutable_cap() { return cap_; } + void set_cap(const z_cap_t& value) { cap_ = value; } + + private: + z_cap_t cap_; + + // Parses everything except for caps. + glcr::Status ParseFromBytesInternal(const yunq::MessageView& message); + + uint64_t SerializeInternal(yunq::Serializer& serializer) const; +}; +class Repeated { + public: + Repeated() {} + // Delete copy and move until implemented. + Repeated(const Repeated&) = delete; + Repeated(Repeated&&) = default; + Repeated& operator=(Repeated&&) = default; + + [[nodiscard]] glcr::Status ParseFromBytes(const yunq::MessageView& message); + [[nodiscard]] glcr::Status ParseFromBytes(const yunq::MessageView& message, const glcr::CapBuffer&); + uint64_t SerializeToBytes(glcr::ByteBuffer&, uint64_t offset) const; + uint64_t SerializeToBytes(glcr::ByteBuffer&, uint64_t offset, glcr::CapBuffer&) const; + + const glcr::Vector& unsigned_ints() const { return unsigned_ints_; } + glcr::Vector& mutable_unsigned_ints() { return unsigned_ints_; } + void add_unsigned_ints(const uint64_t& value) { unsigned_ints_.PushBack(value); } + void add_unsigned_ints(uint64_t&& value) { unsigned_ints_.PushBack(glcr::Move(value)); } + + private: + glcr::Vector unsigned_ints_; + + // Parses everything except for caps. + glcr::Status ParseFromBytesInternal(const yunq::MessageView& message); + + uint64_t SerializeInternal(yunq::Serializer& serializer) const; +}; + + +} // namepace ex diff --git a/lib/yunq/test/yunq_test.cpp b/lib/yunq/test/yunq_test.cpp new file mode 100644 index 0000000..d22a846 --- /dev/null +++ b/lib/yunq/test/yunq_test.cpp @@ -0,0 +1,100 @@ +#include + +#include "example/example.yunq.h" + +TEST_CASE("Basic Setter/Getter", "[yunq]") { + ex::Basic b; + b.set_field(1); + REQUIRE(b.field() == 1); +} + +TEST_CASE("Basic serialization", "[yunq]") { + ex::Basic a; + a.set_field(1); + + glcr::ByteBuffer buf(1024); + a.SerializeToBytes(buf, 0); + + ex::Basic b; + yunq::MessageView v(buf, 0); + REQUIRE(b.ParseFromBytes(v).ok() == true); + + REQUIRE(b.field() == 1); +} + +TEST_CASE("Types Setter/Getter", "[yunq]") { + ex::Types t; + t.set_unsigned_int(1); + t.set_signed_int(-1); + t.set_str("test"); + + REQUIRE(t.unsigned_int() == 1); + REQUIRE(t.signed_int() == -1); + REQUIRE(t.str() == "test"); +} + +TEST_CASE("Types Serialization", "[yunq]") { + ex::Types a; + a.set_unsigned_int(1); + a.set_signed_int(-1); + a.set_str("test"); + + glcr::ByteBuffer buf(1024); + a.SerializeToBytes(buf, 0); + + ex::Types b; + yunq::MessageView v(buf, 0); + REQUIRE(b.ParseFromBytes(v).ok() == true); + + REQUIRE(b.unsigned_int() == 1); + REQUIRE(b.signed_int() == -1); + REQUIRE(b.str() == "test"); +} + +TEST_CASE("Cap Setter/Getter", "[yunq]") { + ex::Cap c; + c.set_cap(1234); + + REQUIRE(c.cap() == 1234); +} + +TEST_CASE("Cap Serialization Inline", "[yunq]") { + ex::Cap a; + a.set_cap(1234); + + glcr::ByteBuffer buf(1024); + a.SerializeToBytes(buf, 0); + + ex::Cap b; + yunq::MessageView v(buf, 0); + REQUIRE(b.ParseFromBytes(v).ok() == true); + + REQUIRE(b.cap() == 1234); +} + +TEST_CASE("Cap Serialization Sidebuffer", "[yunq]") { + ex::Cap a; + a.set_cap(1234); + + glcr::ByteBuffer buf(1024); + glcr::CapBuffer caps(1); + a.SerializeToBytes(buf, 0, caps); + + ex::Cap b; + yunq::MessageView v(buf, 0); + REQUIRE(b.ParseFromBytes(v, caps).ok() == true); + + REQUIRE(b.cap() == 1234); +} + +TEST_CASE("Repeated Setter/Getter", "[yunq]") { + ex::Repeated r; + r.mutable_unsigned_ints().PushBack(1); + r.add_unsigned_ints(2); + uint64_t c = 3; + r.add_unsigned_ints(glcr::Move(c)); + + REQUIRE(r.unsigned_ints()[0] == 1); + REQUIRE(r.unsigned_ints()[1] == 2); + REQUIRE(r.unsigned_ints()[2] == 3); +} diff --git a/lib/yunq/yunq.h b/lib/yunq/yunq.h new file mode 100644 index 0000000..3a33a8c --- /dev/null +++ b/lib/yunq/yunq.h @@ -0,0 +1,22 @@ +#pragma once + +#include + +namespace yunq { + +struct MessageHeader { + uint32_t ident; + uint32_t core_length; + uint32_t length; + uint32_t crc32; + uint64_t options; +} __attribute__((packed)); + +const uint64_t kHeaderSize = 24; // 4x uint32, 1x uint64 + +struct ExtensionPointer { + uint32_t offset; + uint32_t length; +}; + +} // namespace yunq diff --git a/rust-toolchain.toml b/rust-toolchain.toml new file mode 100644 index 0000000..f7acab6 --- /dev/null +++ b/rust-toolchain.toml @@ -0,0 +1,3 @@ +[toolchain] +channel = "nightly-2025-10-02" +components = ["rustfmt", "rust-analyzer", "clippy", "rust-src"] diff --git a/rust/.cargo/config.toml b/rust/.cargo/config.toml new file mode 100644 index 0000000..c2174c1 --- /dev/null +++ b/rust/.cargo/config.toml @@ -0,0 +1,10 @@ +[unstable] +build-std-features = ["compiler-builtins-mem"] +build-std = ["core", "compiler_builtins", "alloc"] + +[build] +target = "x86_64-acadia-os.json" + +[alias] +test_pc = "test --target=x86_64-unknown-linux-gnu -Z build-std=std --lib" + diff --git a/rust/Cargo.lock b/rust/Cargo.lock new file mode 100644 index 0000000..f20f302 --- /dev/null +++ b/rust/Cargo.lock @@ -0,0 +1,248 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "autocfg" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" + +[[package]] +name = "bitfield-struct" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de05f8756f1c68937349406d4632ae96ae35901019b5e59c508d9c38c64715fb" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "convert_case" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec182b0ca2f35d8fc196cf3404988fd8b8c739a4d270ff118a398feb0cbec1ca" +dependencies = [ + "unicode-segmentation", +] + +[[package]] +name = "denali" +version = "0.1.0" +dependencies = [ + "bitfield-struct", + "mammoth", + "pci", + "yellowstone-yunq", + "yunq", + "yunqc", +] + +[[package]] +name = "denali_client" +version = "0.1.0" +dependencies = [ + "mammoth", + "yunq", + "yunqc", +] + +[[package]] +name = "ext2" +version = "0.1.0" +dependencies = [ + "denali_client", + "mammoth", + "yellowstone-yunq", +] + +[[package]] +name = "linked_list_allocator" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9afa463f5405ee81cdb9cc2baf37e08ec7e4c8209442b5d72c04cfb2cd6e6286" +dependencies = [ + "spinning_top", +] + +[[package]] +name = "lock_api" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" +dependencies = [ + "autocfg", + "scopeguard", +] + +[[package]] +name = "mammoth" +version = "0.1.0" +dependencies = [ + "linked_list_allocator", +] + +[[package]] +name = "pci" +version = "0.1.0" +dependencies = [ + "bitfield-struct", + "mammoth", +] + +[[package]] +name = "prettyplease" +version = "0.2.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f12335488a2f3b0a83b14edad48dca9879ce89b2edd10e80237e4e852dd645e" +dependencies = [ + "proc-macro2", + "syn", +] + +[[package]] +name = "proc-macro2" +version = "1.0.86" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "spinning_top" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b9eb1a2f4c41445a3a0ff9abc5221c5fcd28e1f13cd7c0397706f9ac938ddb0" +dependencies = [ + "lock_api", +] + +[[package]] +name = "syn" +version = "2.0.72" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc4b9b9bf2add8093d3f2c0204471e951b2285580335de42f9d2534f3ae7a8af" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "testbed" +version = "0.1.0" +dependencies = [ + "mammoth", + "yellowstone-yunq", + "yunq", +] + +[[package]] +name = "teton" +version = "0.1.0" +dependencies = [ + "mammoth", + "victoriafalls", + "voyageurs", + "yellowstone-yunq", +] + +[[package]] +name = "unicode-ident" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" + +[[package]] +name = "unicode-segmentation" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4c87d22b6e3f4a18d4d40ef354e97c90fcb14dd91d7dc0aa9d8a1172ebf7202" + +[[package]] +name = "victoriafalls" +version = "0.1.0" +dependencies = [ + "denali_client", + "ext2", + "mammoth", + "yellowstone-yunq", + "yunq", + "yunqc", +] + +[[package]] +name = "voyageurs" +version = "0.1.0" +dependencies = [ + "mammoth", + "yellowstone-yunq", + "yunq", + "yunqc", +] + +[[package]] +name = "yellowstone" +version = "0.1.0" +dependencies = [ + "denali_client", + "mammoth", + "victoriafalls", + "voyageurs", + "yellowstone-yunq", + "yunq", +] + +[[package]] +name = "yellowstone-yunq" +version = "0.1.0" +dependencies = [ + "mammoth", + "yunq", + "yunqc", +] + +[[package]] +name = "yunq" +version = "0.1.0" +dependencies = [ + "mammoth", +] + +[[package]] +name = "yunq-test" +version = "0.1.0" +dependencies = [ + "mammoth", + "yunq", + "yunqc", +] + +[[package]] +name = "yunqc" +version = "0.1.0" +dependencies = [ + "convert_case", + "prettyplease", + "proc-macro2", + "quote", + "syn", +] diff --git a/rust/Cargo.toml b/rust/Cargo.toml new file mode 100644 index 0000000..f1886f6 --- /dev/null +++ b/rust/Cargo.toml @@ -0,0 +1,16 @@ +[workspace] + +members = [ + "lib/client/denali_client", "lib/fs/ext2", + "lib/mammoth", "lib/pci", + "lib/voyageurs", + "lib/yellowstone", + "lib/yunq", + "lib/yunq-test", + "sys/denali", + "sys/teton", + "sys/victoriafalls", + "sys/yellowstone", + "usr/testbed", +] +resolver = "2" diff --git a/rust/lib/client/denali_client/Cargo.toml b/rust/lib/client/denali_client/Cargo.toml new file mode 100644 index 0000000..625a2d5 --- /dev/null +++ b/rust/lib/client/denali_client/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "denali_client" +version = "0.1.0" +edition = "2024" + +[dependencies] +mammoth = { path = "../../mammoth" } +yunq = { path = "../../yunq" } + +[build-dependencies] +yunqc = { path = "../../../../yunq/rust" } diff --git a/rust/lib/client/denali_client/build.rs b/rust/lib/client/denali_client/build.rs new file mode 100644 index 0000000..f5228b3 --- /dev/null +++ b/rust/lib/client/denali_client/build.rs @@ -0,0 +1,14 @@ +use std::fs; + +fn main() { + let input_file = "../../../sys/denali/denali.yunq"; + + println!("cargo::rerun-if-changed={input_file}"); + + let input = fs::read_to_string(input_file).expect("Failed to read input file"); + + let code = yunqc::codegen(&input).expect("Failed to generate yunq code."); + + let out = std::env::var("OUT_DIR").unwrap() + "/yunq.rs"; + fs::write(out, code).expect("Failed to write generated code."); +} diff --git a/rust/lib/client/denali_client/src/disk_reader.rs b/rust/lib/client/denali_client/src/disk_reader.rs new file mode 100644 index 0000000..0283d29 --- /dev/null +++ b/rust/lib/client/denali_client/src/disk_reader.rs @@ -0,0 +1,50 @@ +use mammoth::{cap::Capability, zion::ZError}; + +use crate::{DenaliClient, DiskBlock, ReadManyRequest, ReadRequest}; + +pub struct DiskReader { + client: DenaliClient, + disk_id: u64, + lba_offset: u64, + block_multiplier: u64, +} + +impl DiskReader { + pub fn new(client: DenaliClient, disk_id: u64, lba_offset: u64, block_multiplier: u64) -> Self { + Self { + client, + disk_id, + lba_offset, + block_multiplier, + } + } + + // TODO: Make yunq clients callable from a non-mutable reference so this can be called from + // shared ownership. + pub fn read(&mut self, lba: u64, cnt: u64) -> Result { + let read_resp = self.client.read(&ReadRequest { + device_id: self.disk_id, + block: DiskBlock { + lba: self.lba_offset + (lba * self.block_multiplier), + size: cnt * self.block_multiplier, + }, + })?; + + Ok(Capability::take(read_resp.memory)) + } + + pub fn read_many(&mut self, blocks: &[DiskBlock]) -> Result { + let read_resp = self.client.read_many(&ReadManyRequest { + device_id: self.disk_id, + blocks: blocks + .iter() + .map(|b| DiskBlock { + lba: self.lba_offset + (b.lba * self.block_multiplier), + size: b.size * self.block_multiplier, + }) + .collect(), + })?; + + Ok(Capability::take(read_resp.memory)) + } +} diff --git a/rust/lib/client/denali_client/src/lib.rs b/rust/lib/client/denali_client/src/lib.rs new file mode 100644 index 0000000..72dfc10 --- /dev/null +++ b/rust/lib/client/denali_client/src/lib.rs @@ -0,0 +1,9 @@ +#![no_std] + +use core::include; + +include!(concat!(env!("OUT_DIR"), "/yunq.rs")); + +mod disk_reader; + +pub use disk_reader::DiskReader; diff --git a/rust/lib/fs/ext2/Cargo.toml b/rust/lib/fs/ext2/Cargo.toml new file mode 100644 index 0000000..17c6f3f --- /dev/null +++ b/rust/lib/fs/ext2/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "ext2" +version = "0.1.0" +edition = "2024" + +[dependencies] +denali_client = { path = "../../client/denali_client" } +mammoth = { path = "../../mammoth" } +yellowstone-yunq = { path = "../../yellowstone/" } diff --git a/rust/lib/fs/ext2/src/ext2_driver.rs b/rust/lib/fs/ext2/src/ext2_driver.rs new file mode 100644 index 0000000..0997b62 --- /dev/null +++ b/rust/lib/fs/ext2/src/ext2_driver.rs @@ -0,0 +1,282 @@ +use core::cmp::min; + +use alloc::{collections::BTreeMap, string::String, vec::Vec}; +use denali_client::{DenaliClient, DiskBlock, DiskReader, ReadRequest}; +use mammoth::{cap::Capability, debug, mem::MemoryRegion, zion::ZError}; +use yellowstone_yunq::DenaliInfo; + +use crate::types::{BlockGroupDescriptor, DirEntry, Inode, Superblock}; + +pub struct FileInfo { + pub inode: u32, + pub name: String, +} + +/// Ext2 Driver with the ability to read files and directories from the given disk. +/// +/// Implementation based on the information available at +/// https://www.nongnu.org/ext2-doc/ext2.html +pub struct Ext2Driver { + reader: DiskReader, + superblock_region: MemoryRegion, + bgdt_region: MemoryRegion, + + /// Cache of the memory regions for the inode tables available indexed by + /// the block_group number. + inode_table_map: Vec>, + + /// Cache of inode_num to memory capability. + /// This is particularly important for directories so we + /// don't iterate over the disk each time. + inode_cache: BTreeMap, +} + +impl Ext2Driver { + pub fn new(denali_info: DenaliInfo) -> Self { + let mut client = DenaliClient::new(Capability::take(denali_info.denali_endpoint)); + + // Calculate the absolute offset and size of the superblock. It is located at + // offset 1024 of the partition and is 1024 bytes long. (Mostly extra + // reserved space). + // Ref: https://www.nongnu.org/ext2-doc/ext2.html#def-superblock + let abs_superblock_start = denali_info.lba_offset + 2; + let abs_superblock_size = 2; // TODO: This assumes 512 bytes sectors. + let superblock_region = MemoryRegion::from_cap(Capability::take( + client + .read(&ReadRequest { + device_id: denali_info.device_id, + block: DiskBlock { + lba: abs_superblock_start, + size: abs_superblock_size, + }, + }) + .unwrap() + .memory, + )) + .unwrap(); + let superblock: &Superblock = superblock_region.as_ref(); + assert!(superblock.is_valid()); + + let mut reader = DiskReader::new( + client, + denali_info.device_id, + denali_info.lba_offset, + superblock.sectors_per_block(), + ); + + let bgdt_region = MemoryRegion::from_cap( + reader + .read(superblock.bgdt_block_num(), superblock.bgdt_block_size()) + .unwrap(), + ) + .unwrap(); + + let mut inode_table_map = Vec::new(); + inode_table_map.resize_with(superblock.num_block_groups() as usize, || None); + + Self { + reader, + superblock_region, + bgdt_region, + inode_table_map, + inode_cache: BTreeMap::new(), + } + } + + fn superblock(&self) -> &Superblock { + self.superblock_region.as_ref() + } + + fn bgdt(&self) -> &[BlockGroupDescriptor] { + self.bgdt_region.slice() + } + + /// Updates the cached inode tables to contain the inode table for + /// a specific group. + fn populate_inode_table_if_none(&mut self, block_group_num: usize) { + if self.inode_table_map[block_group_num].is_none() { + debug!( + "Cache MISS on inode table for block_group {}", + block_group_num + ); + let inode_table = self.bgdt()[block_group_num].inode_table; + self.inode_table_map[block_group_num] = Some( + MemoryRegion::from_cap( + self.reader + .read( + inode_table as u64, + self.superblock().inode_table_block_size(), + ) + .unwrap(), + ) + .unwrap(), + ); + } else { + debug!( + "Cache HIT on inode table for block_group {}", + block_group_num + ); + } + } + + pub fn get_inode(&mut self, inode_num: u32) -> Inode { + // See the following for a description of finding an inode. + // https://www.nongnu.org/ext2-doc/ext2.html#idm140660447281728 + let block_group_num = (inode_num - 1) / self.superblock().inodes_per_group; + self.populate_inode_table_if_none(block_group_num as usize); + let region = self.inode_table_map[block_group_num as usize] + .as_ref() + .unwrap(); + + let local_index = (inode_num - 1) % self.superblock().inodes_per_group; + let offset = self.superblock().inode_size() * local_index as u64; + unsafe { region.raw_ptr_at_offset::(offset).read().clone() } + } + + fn get_blocks_from_single_indirect(&mut self, block_num: u64, num_blocks: usize) -> Vec { + assert!(num_blocks <= 256); + let single_indr_block_mem = + MemoryRegion::from_cap(self.reader.read(block_num, 1).unwrap()).unwrap(); + + single_indr_block_mem.slice()[..num_blocks].to_vec() + } + + fn get_blocks_from_double_indirect(&mut self, block_num: u64, num_blocks: usize) -> Vec { + assert!(num_blocks > 0 && num_blocks <= (256 * 256)); + let num_dbl_indr = ((num_blocks - 1) / 256) + 1; + + let dbl_indr_block_mem = + MemoryRegion::from_cap(self.reader.read(block_num, 1).unwrap()).unwrap(); + + let dbl_indr_blocks: &[u32] = &dbl_indr_block_mem.slice()[0..num_dbl_indr]; + + let mut blocks_to_read = Vec::new(); + + for (i, dbl_indr_block) in dbl_indr_blocks.iter().enumerate() { + let num_blocks_in_single = min(num_blocks - (256 * i), 256); + blocks_to_read.append( + &mut self + .get_blocks_from_single_indirect(*dbl_indr_block as u64, num_blocks_in_single), + ); + } + + blocks_to_read + } + + fn run_len_compress_blocks(&self, blocks: Vec) -> Vec { + let mut curr_block = DiskBlock { + lba: blocks[0] as u64, + size: 1, + }; + + let mut iter = blocks.into_iter(); + iter.next(); + + let mut blocks = Vec::new(); + + for block in iter { + if block as u64 == (curr_block.lba + curr_block.size) { + curr_block.size += 1; + } else { + blocks.push(curr_block.clone()); + + curr_block.lba = block as u64; + curr_block.size = 1; + } + } + + blocks.push(curr_block); + + blocks + } + + fn read_inode(&mut self, _inode_num: u32, inode: Inode) -> Result { + // TODO: Cache this method using _inode_num + // TODO: This assumes 512 byte sectors. + let real_block_cnt = (inode.blocks as u64 - 1) / (self.superblock().block_size() / 512) + 1; + if inode.block[14] != 0 { + debug!("Can't handle triply indirect inodes yet."); + return Err(ZError::UNIMPLEMENTED); + } + + let mut blocks_to_read = Vec::new(); + + for i in 0..min(12, real_block_cnt) { + blocks_to_read.push(inode.block[i as usize]) + } + + // Singly indirect block. + if inode.block[12] != 0 { + let num_blocks = min(256, real_block_cnt - 12) as usize; + blocks_to_read.append( + &mut self.get_blocks_from_single_indirect(inode.block[12] as u64, num_blocks), + ); + } + + // Doubly indirect block. + if inode.block[13] != 0 { + let num_blocks = min(256 * 256, real_block_cnt - 268) as usize; + blocks_to_read.append( + &mut self.get_blocks_from_double_indirect(inode.block[13] as u64, num_blocks), + ); + }; + + self.reader + .read_many(&self.run_len_compress_blocks(blocks_to_read)) + } + + fn read_inode_into_mem( + &mut self, + inode_num: u32, + inode: Inode, + ) -> Result { + if !self.inode_cache.contains_key(&inode_num) { + debug!("Cache MISS for inode_num: {}", inode_num); + let inode_cap = self.read_inode(inode_num, inode)?; + self.inode_cache.insert(inode_num, inode_cap); + } else { + debug!("Cache HIT for inode_num: {}", inode_num); + } + + MemoryRegion::from_cap(self.inode_cache[&inode_num].duplicate(Capability::PERMS_ALL)?) + } + + pub fn read_file(&mut self, inode_num: u32) -> Result { + let inode = self.get_inode(inode_num); + if (inode.mode & 0x8000) == 0 { + debug!("Reading non file."); + return Err(ZError::INVALID_ARGUMENT); + } + self.read_inode(inode_num, inode) + } + + pub fn read_directory(&mut self, inode_num: u32) -> Result, ZError> { + let inode = self.get_inode(inode_num); + if (inode.mode & 0x4000) == 0 { + let mode = inode.mode; + debug!("Reading non directory. Inode {:?}, Mode {}", inode, mode); + return Err(ZError::INVALID_ARGUMENT); + } + + let dir = self.read_inode_into_mem(inode_num, inode)?; + + let mut file_names = Vec::new(); + + let mut offset = 0; + while offset < dir.size() { + let dir_ptr: DirEntry = unsafe { dir.raw_ptr_at_offset::(offset).read() }; + + let name = dir_ptr.name; + let file_name: String = + String::from_utf8(name[..dir_ptr.name_len as usize].to_vec()).unwrap(); + file_names.push(FileInfo { + inode: dir_ptr.inode, + name: file_name, + }); + + offset += dir_ptr.record_length as u64; + } + + Ok(file_names) + } +} diff --git a/rust/lib/fs/ext2/src/lib.rs b/rust/lib/fs/ext2/src/lib.rs new file mode 100644 index 0000000..40d9458 --- /dev/null +++ b/rust/lib/fs/ext2/src/lib.rs @@ -0,0 +1,8 @@ +#![no_std] + +extern crate alloc; + +mod ext2_driver; +mod types; + +pub use ext2_driver::Ext2Driver; diff --git a/rust/lib/fs/ext2/src/types.rs b/rust/lib/fs/ext2/src/types.rs new file mode 100644 index 0000000..fd1f59b --- /dev/null +++ b/rust/lib/fs/ext2/src/types.rs @@ -0,0 +1,126 @@ +/// Superblock structure. +/// https://www.nongnu.org/ext2-doc/ext2.html#superblock +#[repr(C, packed)] +pub struct Superblock { + pub inodes_count: u32, + pub blocks_count: u32, + pub reserved_blocks_count: u32, + pub free_blocks_count: u32, + pub free_inodes_count: u32, + pub first_data_blok: u32, + pub log_block_size: u32, + pub log_frag_size: u32, + pub blocks_per_group: u32, + pub frags_per_group: u32, + pub inodes_per_group: u32, + pub mtime: u32, + pub wtime: u32, + pub mnt_count: u16, + pub max_mnt_count: u16, + pub magic: u16, + pub state: u16, + pub errors: u16, + pub minor_rev_level: u16, + pub lastcheck: u32, + pub checkinterval: u32, + pub creator_os: u32, + pub rev_level: u32, + pub def_resuid: u16, + pub def_resgid: u16, + pub first_ino: u32, + pub inode_size: u16, +} + +impl Superblock { + pub fn is_valid(&self) -> bool { + self.magic == 0xEF53 + } + + pub fn sectors_per_block(&self) -> u64 { + 1 << (self.log_block_size + 1) + } + + pub fn block_size(&self) -> u64 { + 1024 << self.log_block_size + } + + pub fn bgdt_block_num(&self) -> u64 { + if self.block_size() == 1024 { 2 } else { 1 } + } + + pub fn bgdt_block_size(&self) -> u64 { + (self.num_block_groups() * (size_of::() as u64) - 1) + / self.block_size() + + 1 + } + + pub fn num_block_groups(&self) -> u64 { + (((self.blocks_count - 1) / self.blocks_per_group) + 1) as u64 + } + + pub fn inode_size(&self) -> u64 { + if self.rev_level >= 1 { + self.inode_size as u64 + } else { + const DEFAULT_INODE_SIZE: u64 = 0x80; + DEFAULT_INODE_SIZE + } + } + + pub fn inode_table_block_size(&self) -> u64 { + (self.inode_size() * self.inodes_per_group as u64) / self.block_size() + } +} + +#[repr(C, packed)] +#[derive(Debug)] +pub struct BlockGroupDescriptor { + pub block_bitmap: u32, + pub inode_bitmap: u32, + pub inode_table: u32, + pub free_blocks_count: u16, + pub free_inodes_count: u16, + pub used_dirs_count: u16, + reserved: [u8; 14], +} + +const _: () = assert!(size_of::() == 32); + +#[repr(C, packed)] +#[derive(Clone, Debug)] +pub struct Inode { + pub mode: u16, + pub uid: u16, + pub size: u32, + pub atime: u32, + pub ctime: u32, + pub mtime: u32, + pub dtime: u32, + pub gid: u16, + pub links_count: u16, + pub blocks: u32, + pub flags: u32, + pub osd1: u32, + pub block: [u32; 15], + pub generation: u32, + pub file_acl: u32, + pub dir_acl: u32, + pub faddr: u32, + pub osd2: [u32; 3], +} + +const _: () = assert!(size_of::() == 128); + +#[allow(dead_code)] +pub const EXT2_FT_FILE: u8 = 0x1; +#[allow(dead_code)] +pub const EXT2_FT_DIR: u8 = 0x2; + +#[repr(C, packed)] +pub struct DirEntry { + pub inode: u32, + pub record_length: u16, + pub name_len: u8, + pub file_type: u8, + pub name: [u8; 256], +} diff --git a/rust/lib/mammoth/Cargo.toml b/rust/lib/mammoth/Cargo.toml new file mode 100644 index 0000000..b8fd6ef --- /dev/null +++ b/rust/lib/mammoth/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "mammoth" +version = "0.1.0" +edition = "2021" + +[lib] +name = "mammoth" + +[dependencies] +linked_list_allocator = "0.10.5" + +[features] +hosted = [] +default = ["hosted"] diff --git a/rust/lib/mammoth/build.rs b/rust/lib/mammoth/build.rs new file mode 100644 index 0000000..6797dfb --- /dev/null +++ b/rust/lib/mammoth/build.rs @@ -0,0 +1,18 @@ +use std::env; + +fn main() { + let mut curr_directory = env::current_dir().unwrap(); + println!("{:?}", curr_directory); + assert!(curr_directory.pop()); + assert!(curr_directory.pop()); + assert!(curr_directory.pop()); + + curr_directory.push("builddbg"); + curr_directory.push("zion"); + + println!( + "cargo:rustc-link-search={}", + curr_directory.to_str().unwrap() + ); + println!("cargo:rustc-link-lib=zion_stub"); +} diff --git a/rust/lib/mammoth/src/bindings.rs b/rust/lib/mammoth/src/bindings.rs new file mode 100644 index 0000000..c26dba3 --- /dev/null +++ b/rust/lib/mammoth/src/bindings.rs @@ -0,0 +1,576 @@ +/* automatically generated by rust-bindgen 0.69.4 */ + +pub const _STDINT_H: u32 = 1; +pub const _FEATURES_H: u32 = 1; +pub const _ISOC95_SOURCE: u32 = 1; +pub const _ISOC99_SOURCE: u32 = 1; +pub const _ISOC11_SOURCE: u32 = 1; +pub const _ISOC2X_SOURCE: u32 = 1; +pub const _POSIX_SOURCE: u32 = 1; +pub const _POSIX_C_SOURCE: u32 = 200809; +pub const _XOPEN_SOURCE: u32 = 700; +pub const _XOPEN_SOURCE_EXTENDED: u32 = 1; +pub const _LARGEFILE64_SOURCE: u32 = 1; +pub const _DEFAULT_SOURCE: u32 = 1; +pub const _ATFILE_SOURCE: u32 = 1; +pub const _DYNAMIC_STACK_SIZE_SOURCE: u32 = 1; +pub const __GLIBC_USE_ISOC2X: u32 = 1; +pub const __USE_ISOC11: u32 = 1; +pub const __USE_ISOC99: u32 = 1; +pub const __USE_ISOC95: u32 = 1; +pub const __USE_ISOCXX11: u32 = 1; +pub const __USE_POSIX: u32 = 1; +pub const __USE_POSIX2: u32 = 1; +pub const __USE_POSIX199309: u32 = 1; +pub const __USE_POSIX199506: u32 = 1; +pub const __USE_XOPEN2K: u32 = 1; +pub const __USE_XOPEN2K8: u32 = 1; +pub const __USE_XOPEN: u32 = 1; +pub const __USE_XOPEN_EXTENDED: u32 = 1; +pub const __USE_UNIX98: u32 = 1; +pub const _LARGEFILE_SOURCE: u32 = 1; +pub const __USE_XOPEN2K8XSI: u32 = 1; +pub const __USE_XOPEN2KXSI: u32 = 1; +pub const __USE_LARGEFILE: u32 = 1; +pub const __USE_LARGEFILE64: u32 = 1; +pub const __WORDSIZE: u32 = 64; +pub const __WORDSIZE_TIME64_COMPAT32: u32 = 1; +pub const __SYSCALL_WORDSIZE: u32 = 64; +pub const __TIMESIZE: u32 = 64; +pub const __USE_MISC: u32 = 1; +pub const __USE_ATFILE: u32 = 1; +pub const __USE_DYNAMIC_STACK_SIZE: u32 = 1; +pub const __USE_GNU: u32 = 1; +pub const __USE_FORTIFY_LEVEL: u32 = 0; +pub const __GLIBC_USE_DEPRECATED_GETS: u32 = 0; +pub const __GLIBC_USE_DEPRECATED_SCANF: u32 = 0; +pub const __GLIBC_USE_C2X_STRTOL: u32 = 1; +pub const _STDC_PREDEF_H: u32 = 1; +pub const __STDC_IEC_559__: u32 = 1; +pub const __STDC_IEC_60559_BFP__: u32 = 201404; +pub const __STDC_IEC_559_COMPLEX__: u32 = 1; +pub const __STDC_IEC_60559_COMPLEX__: u32 = 201404; +pub const __STDC_ISO_10646__: u32 = 201706; +pub const __GNU_LIBRARY__: u32 = 6; +pub const __GLIBC__: u32 = 2; +pub const __GLIBC_MINOR__: u32 = 39; +pub const _SYS_CDEFS_H: u32 = 1; +pub const __glibc_c99_flexarr_available: u32 = 1; +pub const __LDOUBLE_REDIRECTS_TO_FLOAT128_ABI: u32 = 0; +pub const __HAVE_GENERIC_SELECTION: u32 = 0; +pub const __GLIBC_USE_LIB_EXT2: u32 = 1; +pub const __GLIBC_USE_IEC_60559_BFP_EXT: u32 = 1; +pub const __GLIBC_USE_IEC_60559_BFP_EXT_C2X: u32 = 1; +pub const __GLIBC_USE_IEC_60559_EXT: u32 = 1; +pub const __GLIBC_USE_IEC_60559_FUNCS_EXT: u32 = 1; +pub const __GLIBC_USE_IEC_60559_FUNCS_EXT_C2X: u32 = 1; +pub const __GLIBC_USE_IEC_60559_TYPES_EXT: u32 = 1; +pub const _BITS_TYPES_H: u32 = 1; +pub const _BITS_TYPESIZES_H: u32 = 1; +pub const __OFF_T_MATCHES_OFF64_T: u32 = 1; +pub const __INO_T_MATCHES_INO64_T: u32 = 1; +pub const __RLIM_T_MATCHES_RLIM64_T: u32 = 1; +pub const __STATFS_MATCHES_STATFS64: u32 = 1; +pub const __KERNEL_OLD_TIMEVAL_MATCHES_TIMEVAL64: u32 = 1; +pub const __FD_SETSIZE: u32 = 1024; +pub const _BITS_TIME64_H: u32 = 1; +pub const _BITS_WCHAR_H: u32 = 1; +pub const _BITS_STDINT_INTN_H: u32 = 1; +pub const _BITS_STDINT_UINTN_H: u32 = 1; +pub const _BITS_STDINT_LEAST_H: u32 = 1; +pub const INT8_MIN: i32 = -128; +pub const INT16_MIN: i32 = -32768; +pub const INT32_MIN: i32 = -2147483648; +pub const INT8_MAX: u32 = 127; +pub const INT16_MAX: u32 = 32767; +pub const INT32_MAX: u32 = 2147483647; +pub const UINT8_MAX: u32 = 255; +pub const UINT16_MAX: u32 = 65535; +pub const UINT32_MAX: u32 = 4294967295; +pub const INT_LEAST8_MIN: i32 = -128; +pub const INT_LEAST16_MIN: i32 = -32768; +pub const INT_LEAST32_MIN: i32 = -2147483648; +pub const INT_LEAST8_MAX: u32 = 127; +pub const INT_LEAST16_MAX: u32 = 32767; +pub const INT_LEAST32_MAX: u32 = 2147483647; +pub const UINT_LEAST8_MAX: u32 = 255; +pub const UINT_LEAST16_MAX: u32 = 65535; +pub const UINT_LEAST32_MAX: u32 = 4294967295; +pub const INT_FAST8_MIN: i32 = -128; +pub const INT_FAST16_MIN: i64 = -9223372036854775808; +pub const INT_FAST32_MIN: i64 = -9223372036854775808; +pub const INT_FAST8_MAX: u32 = 127; +pub const INT_FAST16_MAX: u64 = 9223372036854775807; +pub const INT_FAST32_MAX: u64 = 9223372036854775807; +pub const UINT_FAST8_MAX: u32 = 255; +pub const UINT_FAST16_MAX: i32 = -1; +pub const UINT_FAST32_MAX: i32 = -1; +pub const INTPTR_MIN: i64 = -9223372036854775808; +pub const INTPTR_MAX: u64 = 9223372036854775807; +pub const UINTPTR_MAX: i32 = -1; +pub const PTRDIFF_MIN: i64 = -9223372036854775808; +pub const PTRDIFF_MAX: u64 = 9223372036854775807; +pub const SIG_ATOMIC_MIN: i32 = -2147483648; +pub const SIG_ATOMIC_MAX: u32 = 2147483647; +pub const SIZE_MAX: i32 = -1; +pub const WINT_MIN: u32 = 0; +pub const WINT_MAX: u32 = 4294967295; +pub const INT8_WIDTH: u32 = 8; +pub const UINT8_WIDTH: u32 = 8; +pub const INT16_WIDTH: u32 = 16; +pub const UINT16_WIDTH: u32 = 16; +pub const INT32_WIDTH: u32 = 32; +pub const UINT32_WIDTH: u32 = 32; +pub const INT64_WIDTH: u32 = 64; +pub const UINT64_WIDTH: u32 = 64; +pub const INT_LEAST8_WIDTH: u32 = 8; +pub const UINT_LEAST8_WIDTH: u32 = 8; +pub const INT_LEAST16_WIDTH: u32 = 16; +pub const UINT_LEAST16_WIDTH: u32 = 16; +pub const INT_LEAST32_WIDTH: u32 = 32; +pub const UINT_LEAST32_WIDTH: u32 = 32; +pub const INT_LEAST64_WIDTH: u32 = 64; +pub const UINT_LEAST64_WIDTH: u32 = 64; +pub const INT_FAST8_WIDTH: u32 = 8; +pub const UINT_FAST8_WIDTH: u32 = 8; +pub const INT_FAST16_WIDTH: u32 = 64; +pub const UINT_FAST16_WIDTH: u32 = 64; +pub const INT_FAST32_WIDTH: u32 = 64; +pub const UINT_FAST32_WIDTH: u32 = 64; +pub const INT_FAST64_WIDTH: u32 = 64; +pub const UINT_FAST64_WIDTH: u32 = 64; +pub const INTPTR_WIDTH: u32 = 64; +pub const UINTPTR_WIDTH: u32 = 64; +pub const INTMAX_WIDTH: u32 = 64; +pub const UINTMAX_WIDTH: u32 = 64; +pub const PTRDIFF_WIDTH: u32 = 64; +pub const SIG_ATOMIC_WIDTH: u32 = 32; +pub const SIZE_WIDTH: u32 = 64; +pub const WCHAR_WIDTH: u32 = 32; +pub const WINT_WIDTH: u32 = 32; +pub type __u_char = ::core::ffi::c_uchar; +pub type __u_short = ::core::ffi::c_ushort; +pub type __u_int = ::core::ffi::c_uint; +pub type __u_long = ::core::ffi::c_ulong; +pub type __int8_t = ::core::ffi::c_schar; +pub type __uint8_t = ::core::ffi::c_uchar; +pub type __int16_t = ::core::ffi::c_short; +pub type __uint16_t = ::core::ffi::c_ushort; +pub type __int32_t = ::core::ffi::c_int; +pub type __uint32_t = ::core::ffi::c_uint; +pub type __int64_t = ::core::ffi::c_long; +pub type __uint64_t = ::core::ffi::c_ulong; +pub type __int_least8_t = __int8_t; +pub type __uint_least8_t = __uint8_t; +pub type __int_least16_t = __int16_t; +pub type __uint_least16_t = __uint16_t; +pub type __int_least32_t = __int32_t; +pub type __uint_least32_t = __uint32_t; +pub type __int_least64_t = __int64_t; +pub type __uint_least64_t = __uint64_t; +pub type __quad_t = ::core::ffi::c_long; +pub type __u_quad_t = ::core::ffi::c_ulong; +pub type __intmax_t = ::core::ffi::c_long; +pub type __uintmax_t = ::core::ffi::c_ulong; +pub type __dev_t = ::core::ffi::c_ulong; +pub type __uid_t = ::core::ffi::c_uint; +pub type __gid_t = ::core::ffi::c_uint; +pub type __ino_t = ::core::ffi::c_ulong; +pub type __ino64_t = ::core::ffi::c_ulong; +pub type __mode_t = ::core::ffi::c_uint; +pub type __nlink_t = ::core::ffi::c_ulong; +pub type __off_t = ::core::ffi::c_long; +pub type __off64_t = ::core::ffi::c_long; +pub type __pid_t = ::core::ffi::c_int; +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct __fsid_t { + pub __val: [::core::ffi::c_int; 2usize], +} +pub type __clock_t = ::core::ffi::c_long; +pub type __rlim_t = ::core::ffi::c_ulong; +pub type __rlim64_t = ::core::ffi::c_ulong; +pub type __id_t = ::core::ffi::c_uint; +pub type __time_t = ::core::ffi::c_long; +pub type __useconds_t = ::core::ffi::c_uint; +pub type __suseconds_t = ::core::ffi::c_long; +pub type __suseconds64_t = ::core::ffi::c_long; +pub type __daddr_t = ::core::ffi::c_int; +pub type __key_t = ::core::ffi::c_int; +pub type __clockid_t = ::core::ffi::c_int; +pub type __timer_t = *mut ::core::ffi::c_void; +pub type __blksize_t = ::core::ffi::c_long; +pub type __blkcnt_t = ::core::ffi::c_long; +pub type __blkcnt64_t = ::core::ffi::c_long; +pub type __fsblkcnt_t = ::core::ffi::c_ulong; +pub type __fsblkcnt64_t = ::core::ffi::c_ulong; +pub type __fsfilcnt_t = ::core::ffi::c_ulong; +pub type __fsfilcnt64_t = ::core::ffi::c_ulong; +pub type __fsword_t = ::core::ffi::c_long; +pub type __ssize_t = ::core::ffi::c_long; +pub type __syscall_slong_t = ::core::ffi::c_long; +pub type __syscall_ulong_t = ::core::ffi::c_ulong; +pub type __loff_t = __off64_t; +pub type __caddr_t = *mut ::core::ffi::c_char; +pub type __intptr_t = ::core::ffi::c_long; +pub type __socklen_t = ::core::ffi::c_uint; +pub type __sig_atomic_t = ::core::ffi::c_int; +pub type int_least8_t = __int_least8_t; +pub type int_least16_t = __int_least16_t; +pub type int_least32_t = __int_least32_t; +pub type int_least64_t = __int_least64_t; +pub type uint_least8_t = __uint_least8_t; +pub type uint_least16_t = __uint_least16_t; +pub type uint_least32_t = __uint_least32_t; +pub type uint_least64_t = __uint_least64_t; +pub type int_fast8_t = ::core::ffi::c_schar; +pub type int_fast16_t = ::core::ffi::c_long; +pub type int_fast32_t = ::core::ffi::c_long; +pub type int_fast64_t = ::core::ffi::c_long; +pub type uint_fast8_t = ::core::ffi::c_uchar; +pub type uint_fast16_t = ::core::ffi::c_ulong; +pub type uint_fast32_t = ::core::ffi::c_ulong; +pub type uint_fast64_t = ::core::ffi::c_ulong; +pub type intmax_t = __intmax_t; +pub type uintmax_t = __uintmax_t; +pub type z_err_t = u64; +pub const kZionProcessExit: u64 = 1; +pub const kZionProcessSpawn: u64 = 2; +pub const kZionProcessWait: u64 = 3; +pub const kZionThreadCreate: u64 = 16; +pub const kZionThreadStart: u64 = 17; +pub const kZionThreadExit: u64 = 18; +pub const kZionThreadWait: u64 = 19; +pub const kZionThreadSleep: u64 = 20; +pub const kZionAddressSpaceMap: u64 = 33; +pub const kZionAddressSpaceUnmap: u64 = 34; +pub const kZionMemoryObjectCreate: u64 = 48; +pub const kZionMemoryObjectCreatePhysical: u64 = 49; +pub const kZionMemoryObjectCreateContiguous: u64 = 50; +pub const kZionMemoryObjectDuplicate: u64 = 56; +pub const kZionMemoryObjectInspect: u64 = 57; +pub const kZionChannelCreate: u64 = 64; +pub const kZionChannelSend: u64 = 65; +pub const kZionChannelRecv: u64 = 66; +pub const kZionChannelSendRecv: u64 = 67; +pub const kZionPortCreate: u64 = 80; +pub const kZionPortSend: u64 = 81; +pub const kZionPortRecv: u64 = 82; +pub const kZionPortPoll: u64 = 83; +pub const kZionIrqRegister: u64 = 88; +pub const kZionMsiIrqRegister: u64 = 89; +pub const kZionEndpointCreate: u64 = 96; +pub const kZionEndpointSend: u64 = 97; +pub const kZionEndpointRecv: u64 = 98; +pub const kZionReplyPortSend: u64 = 99; +pub const kZionReplyPortRecv: u64 = 100; +pub const kZionEndpointCall: u64 = 101; +pub const kZionCapDuplicate: u64 = 112; +pub const kZionCapRelease: u64 = 113; +pub const kZionMutexCreate: u64 = 128; +pub const kZionMutexLock: u64 = 129; +pub const kZionMutexRelease: u64 = 130; +pub const kZionSemaphoreCreate: u64 = 131; +pub const kZionSemaphoreWait: u64 = 132; +pub const kZionSemaphoreSignal: u64 = 133; +pub const kZionDebug: u64 = 65536; +pub const kZIrqKbd: u64 = 34; +pub const kZIrqPci1: u64 = 48; +pub const kZIrqPci2: u64 = 49; +pub const kZIrqPci3: u64 = 50; +pub const kZIrqPci4: u64 = 51; +pub type z_cap_t = u64; +pub type z_perm_t = u64; +pub const kZionInvalidCapability: u64 = 0; +pub const kZionPerm_Write: u64 = 1; +pub const kZionPerm_Read: u64 = 2; +pub const kZionPerm_Transmit: u64 = 16; +pub const kZionPerm_Duplicate: u64 = 32; +pub const kZionPerm_SpawnProcess: u64 = 256; +pub const kZionPerm_SpawnThread: u64 = 512; +pub const kZionPerm_Lock: u64 = 256; +pub const kZionPerm_Release: u64 = 512; +pub const kZionPerm_Wait: u64 = 256; +pub const kZionPerm_Signal: u64 = 512; +pub const kZionPerm_None: z_perm_t = 0; +pub const kZionPerm_All: z_perm_t = 18446744073709551615; +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct ZFramebufferInfo { + pub address_phys: u64, + pub width: u64, + pub height: u64, + pub pitch: u64, + pub bpp: u16, + pub memory_model: u8, + pub red_mask_size: u8, + pub red_mask_shift: u8, + pub green_mask_size: u8, + pub green_mask_shift: u8, + pub blue_mask_size: u8, + pub blue_mask_shift: u8, +} +extern "C" { + #[link_name = "\u{1}_Z8SysCall1mPKv"] + pub fn SysCall1(code: u64, req: *const ::core::ffi::c_void) -> z_err_t; +} +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct ZProcessExitReq { + pub code: u64, +} +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct ZProcessSpawnReq { + pub proc_cap: z_cap_t, + pub bootstrap_cap: z_cap_t, + pub new_proc_cap: *mut z_cap_t, + pub new_vmas_cap: *mut z_cap_t, + pub new_bootstrap_cap: *mut z_cap_t, +} +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct ZProcessWaitReq { + pub proc_cap: z_cap_t, + pub exit_code: *mut z_err_t, +} +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct ZThreadCreateReq { + pub proc_cap: z_cap_t, + pub thread_cap: *mut z_cap_t, +} +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct ZThreadStartReq { + pub thread_cap: z_cap_t, + pub entry: u64, + pub arg1: u64, + pub arg2: u64, +} +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct ZThreadExitReq {} +extern "C" { + #[link_name = "\u{1}_Z11ZThreadExitv"] + pub fn ZThreadExit() -> z_err_t; +} +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct ZThreadWaitReq { + pub thread_cap: z_cap_t, +} +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct ZThreadSleepReq { + pub millis: u64, +} +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct ZAddressSpaceMapReq { + pub vmas_cap: z_cap_t, + pub vmas_offset: u64, + pub vmmo_cap: z_cap_t, + pub align: u64, + pub vaddr: *mut u64, +} +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct ZAddressSpaceUnmapReq { + pub vmas_cap: z_cap_t, + pub lower_addr: u64, + pub upper_addr: u64, +} +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct ZMemoryObjectCreateReq { + pub size: u64, + pub vmmo_cap: *mut z_cap_t, +} +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct ZMemoryObjectCreatePhysicalReq { + pub paddr: u64, + pub size: u64, + pub vmmo_cap: *mut z_cap_t, +} +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct ZMemoryObjectCreateContiguousReq { + pub size: u64, + pub vmmo_cap: *mut z_cap_t, + pub paddr: *mut u64, +} +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct ZMemoryObjectDuplicateReq { + pub vmmo_cap: z_cap_t, + pub base_offset: u64, + pub length: u64, + pub new_vmmo_cap: *mut z_cap_t, +} +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct ZMemoryObjectInspectReq { + pub vmmo_cap: z_cap_t, + pub size: *mut u64, +} +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct ZChannelCreateReq { + pub channel1: *mut z_cap_t, + pub channel2: *mut z_cap_t, +} +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct ZChannelSendReq { + pub chan_cap: z_cap_t, + pub num_bytes: u64, + pub data: *const ::core::ffi::c_void, + pub num_caps: u64, + pub caps: *mut z_cap_t, +} +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct ZChannelRecvReq { + pub chan_cap: z_cap_t, + pub num_bytes: *mut u64, + pub data: *mut ::core::ffi::c_void, + pub num_caps: *mut u64, + pub caps: *mut z_cap_t, +} +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct ZPortCreateReq { + pub port_cap: *mut z_cap_t, +} +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct ZPortSendReq { + pub port_cap: z_cap_t, + pub num_bytes: u64, + pub data: *const ::core::ffi::c_void, + pub num_caps: u64, + pub caps: *mut z_cap_t, +} +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct ZPortRecvReq { + pub port_cap: z_cap_t, + pub num_bytes: *mut u64, + pub data: *mut ::core::ffi::c_void, + pub num_caps: *mut u64, + pub caps: *mut z_cap_t, +} +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct ZPortPollReq { + pub port_cap: z_cap_t, + pub num_bytes: *mut u64, + pub data: *mut ::core::ffi::c_void, + pub num_caps: *mut u64, + pub caps: *mut z_cap_t, +} +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct ZIrqRegisterReq { + pub irq_num: u64, + pub port_cap: *mut z_cap_t, +} +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct ZMsiIrqRegisterReq { + pub irq_num: *mut u64, + pub port_cap: *mut z_cap_t, +} +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct ZEndpointCreateReq { + pub endpoint_cap: *mut z_cap_t, +} +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct ZEndpointSendReq { + pub endpoint_cap: z_cap_t, + pub num_bytes: u64, + pub data: *const ::core::ffi::c_void, + pub num_caps: u64, + pub caps: *const z_cap_t, + pub reply_port_cap: *mut z_cap_t, +} +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct ZEndpointRecvReq { + pub endpoint_cap: z_cap_t, + pub num_bytes: *mut u64, + pub data: *mut ::core::ffi::c_void, + pub num_caps: *mut u64, + pub caps: *mut z_cap_t, + pub reply_port_cap: *mut z_cap_t, +} +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct ZReplyPortSendReq { + pub reply_port_cap: z_cap_t, + pub num_bytes: u64, + pub data: *const ::core::ffi::c_void, + pub num_caps: u64, + pub caps: *const z_cap_t, +} +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct ZReplyPortRecvReq { + pub reply_port_cap: z_cap_t, + pub num_bytes: *mut u64, + pub data: *mut ::core::ffi::c_void, + pub num_caps: *mut u64, + pub caps: *mut z_cap_t, +} +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct ZCapDuplicateReq { + pub cap_in: z_cap_t, + pub perm_mask: z_perm_t, + pub cap_out: *mut z_cap_t, +} +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct ZCapReleaseReq { + pub cap: z_cap_t, +} +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct ZMutexCreateReq { + pub mutex_cap: *mut z_cap_t, +} +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct ZMutexLockReq { + pub mutex_cap: z_cap_t, +} +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct ZMutexReleaseReq { + pub mutex_cap: z_cap_t, +} +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct ZSemaphoreCreateReq { + pub semaphore_cap: *mut z_cap_t, +} +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct ZSemaphoreWaitReq { + pub semaphore_cap: z_cap_t, +} +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct ZSemaphoreSignalReq { + pub semaphore_cap: z_cap_t, +} +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct ZDebugReq { + pub message: *const ::core::ffi::c_char, + pub size: u64, +} diff --git a/rust/lib/mammoth/src/cap.rs b/rust/lib/mammoth/src/cap.rs new file mode 100644 index 0000000..29f3d47 --- /dev/null +++ b/rust/lib/mammoth/src/cap.rs @@ -0,0 +1,48 @@ +use crate::cap_syscall; +use crate::zion::{z_cap_t, ZError}; + +pub struct Capability { + cap: z_cap_t, +} + +impl Capability { + pub fn take(cap: z_cap_t) -> Self { + Self { cap } + } + + pub fn take_copy(cap: z_cap_t) -> Result { + Ok(Self::take(cap_syscall::cap_duplicate( + cap, + Self::PERMS_ALL, + )?)) + } + + pub fn raw(&self) -> z_cap_t { + self.cap + } + + pub fn release(mut self) -> z_cap_t { + let cap = self.cap; + self.cap = 0; + cap + } + + pub const PERMS_ALL: u64 = u64::MAX; + pub fn duplicate(&self, perm_mask: u64) -> Result { + Ok(Self::take(cap_syscall::cap_duplicate(self.cap, perm_mask)?)) + } +} + +impl Drop for Capability { + fn drop(&mut self) { + if self.cap != 0 { + if let Err(e) = cap_syscall::cap_release(self.cap) { + crate::debug!( + "WARN: error during cap release for cap {:#x}: {:?}", + self.cap, + e + ); + } + } + } +} diff --git a/rust/lib/mammoth/src/cap_syscall.rs b/rust/lib/mammoth/src/cap_syscall.rs new file mode 100644 index 0000000..b820bdc --- /dev/null +++ b/rust/lib/mammoth/src/cap_syscall.rs @@ -0,0 +1,30 @@ +use core::ffi::c_void; + +use crate::zion::{self, z_cap_t, ZError}; + +fn syscall(id: u64, req: &T) -> Result<(), ZError> { + unsafe { + let resp = zion::SysCall1(id, req as *const T as *const c_void); + if resp != 0 { + return Err(zion::ZError::from(resp)); + } + } + Ok(()) +} + +pub fn cap_duplicate(cap: z_cap_t, perm_mask: u64) -> Result { + let mut new_cap = 0; + syscall( + zion::kZionCapDuplicate, + &zion::ZCapDuplicateReq { + cap_in: cap, + perm_mask, + cap_out: &mut new_cap, + }, + )?; + Ok(new_cap) +} + +pub fn cap_release(cap: z_cap_t) -> Result<(), ZError> { + syscall(zion::kZionCapRelease, &zion::ZCapReleaseReq { cap }) +} diff --git a/rust/lib/mammoth/src/elf.rs b/rust/lib/mammoth/src/elf.rs new file mode 100644 index 0000000..96807d2 --- /dev/null +++ b/rust/lib/mammoth/src/elf.rs @@ -0,0 +1,343 @@ +use crate::cap::Capability; +use crate::init; +use crate::syscall; +use crate::zion::ZError; + +use alloc::fmt::Debug; + +const ELF_MAGIC: u32 = 0x464C457F; + +const ELF_IDENT_32BIT: u8 = 0x1; +const ELF_IDENT_64BIT: u8 = 0x2; + +const ELF_ENDIAN_LITTLE: u8 = 0x1; +const ELF_ENDIAN_BIG: u8 = 0x2; +const ELF_VERSION_CURRENT: u8 = 0x1; +const ELF_ABI_SYSV: u8 = 0x0; +const ELF_ABI_LINUX: u8 = 0x3; + +const ELF_FILE_RELOC: u16 = 0x1; +const ELF_FILE_EXEC: u16 = 0x2; +const ELF_FILE_DYN: u16 = 0x3; +const ELF_FILE_CORE: u16 = 0x4; + +const ELF_MACH_X86: u16 = 0x3; +const ELF_MACH_AMD64: u16 = 0x3e; + +#[repr(C, packed(1))] +// Header spec from wikipedia: https://en.wikipedia.org/wiki/Executable_and_Linkable_Format#ELF_header +struct Elf64Header { + // 0x7F454C46 (0x7F followed by 'ELF') + magic: u32, + + // 1 for 32 bit, 2 for 64 bit. + elf_class: u8, + + // 1 for little, 2 for big. + endianess: u8, + + // Current version is 1. + ident_version: u8, + + // Target OS abi. + abi: u8, + abi_version: u8, + + ident_padding: [u8; 7], + + file_type: u16, + machine: u16, + version: u32, + entry: u64, + program_header_offset: u64, + section_header_offset: u64, + flags: u32, + header_size: u16, + program_header_entry_size: u16, + program_header_count: u16, + section_header_entry_size: u16, + section_header_entry_number: u16, + section_header_str_index: u16, +} + +impl Debug for Elf64Header { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + let magic = self.magic; + let elf_class = match self.elf_class { + ELF_IDENT_32BIT => "32 bit", + ELF_IDENT_64BIT => "64 bit", + _ => "Unknown", + }; + let endianess = match self.endianess { + ELF_ENDIAN_LITTLE => "Little", + ELF_ENDIAN_BIG => "Big", + _ => "Unknown", + }; + + let ident_version = self.ident_version; + let version = self.version; + + f.write_fmt(format_args!( + "ELF Header Magic: {:#x}, Class: {}, Endianess: {}, Version (ident): {}, Version: {}\n", + magic, elf_class, endianess, ident_version, version, + ))?; + + let abi = match self.abi { + ELF_ABI_SYSV => "SYSV", + ELF_ABI_LINUX => "Linux", + _ => "Unknown", + }; + let abi_version = self.abi_version; + + f.write_fmt(format_args!("\tABI: {}, Version: {}\n", abi, abi_version))?; + + let file_type = match self.file_type { + ELF_FILE_EXEC => "Executable", + ELF_FILE_RELOC => "Relocatable", + ELF_FILE_DYN => "Shared Obj", + ELF_FILE_CORE => "Core file", + _ => "Unknown", + }; + + let machine = match self.machine { + ELF_MACH_X86 => "x86 (32bit)", + ELF_MACH_AMD64 => "x86-64", + _ => "Unknown", + }; + + let entry_point = self.entry; + + f.write_fmt(format_args!( + "\tFile type: {}, Machine Arch: {}, Entry point {:#x}", + file_type, machine, entry_point + )) + } +} + +fn validate_header(elf_header: &Elf64Header) -> Result<(), ZError> { + if elf_header.magic != ELF_MAGIC { + let magic = elf_header.magic; + debug!( + "Elf header incorrect got {:#x} expected {:#x}", + magic, ELF_MAGIC + ); + return Err(ZError::INVALID_ARGUMENT); + } + + if elf_header.elf_class != ELF_IDENT_64BIT { + let class = elf_header.elf_class; + debug!( + "Elf class must be {} for 64 bit, got: {}", + ELF_IDENT_64BIT, class + ); + return Err(ZError::INVALID_ARGUMENT); + } + + if elf_header.endianess != ELF_ENDIAN_LITTLE { + let endianess = elf_header.endianess; + debug!( + "Elf endianess must be {} for little, got: {}", + ELF_ENDIAN_LITTLE, endianess + ); + return Err(ZError::INVALID_ARGUMENT); + } + + if elf_header.ident_version != ELF_VERSION_CURRENT { + let version = elf_header.ident_version; + debug!( + "Elf version (ident) must be {}, got: {}", + ELF_VERSION_CURRENT, version + ); + return Err(ZError::INVALID_ARGUMENT); + } + + if elf_header.file_type != ELF_FILE_EXEC { + let file_type = elf_header.file_type; + debug!( + "Elf file type must be {} for executable, got {:x}", + ELF_FILE_EXEC, file_type + ); + return Err(ZError::INVALID_ARGUMENT); + } + + Ok(()) +} + +const ELF_PROG_NULL: u32 = 0x0; +const ELF_PROG_LOAD: u32 = 0x1; +const ELF_PROG_DYNAMIC: u32 = 0x2; +const ELF_PROG_INTERP: u32 = 0x3; +const ELF_PROG_NOTE: u32 = 0x4; +const ELF_PROG_SHLIB: u32 = 0x5; +const ELF_PROG_PTHDR: u32 = 0x6; +const ELF_PROG_THREAD_LOCAL: u32 = 0x7; + +// Stack unwind tables. +const ELF_PROG_GNU_EH_FRAME: u32 = 0x6474e550; +const ELF_PROG_GNU_STACK: u32 = 0x6474e551; +const ELF_PROG_GNU_RELRO: u32 = 0x6474e552; + +#[repr(C, packed(1))] +struct Elf64ProgramHeader { + prog_type: u32, + flags: u32, + offset: u64, + vaddr: u64, + paddr: u64, + file_size: u64, + mem_size: u64, + align: u64, +} + +impl Debug for Elf64ProgramHeader { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + let prog_type = match self.prog_type { + ELF_PROG_NULL => "NULL", + ELF_PROG_LOAD => "LOAD", + ELF_PROG_DYNAMIC => "DYNAMIC", + ELF_PROG_INTERP => "INTERP", + ELF_PROG_NOTE => "NOTE", + ELF_PROG_SHLIB => "SHARED LIB", + ELF_PROG_PTHDR => "PROG TABLE HEADER", + ELF_PROG_THREAD_LOCAL => "THREAD LOCAL", + ELF_PROG_GNU_EH_FRAME => "GNU EH FRAME", + ELF_PROG_GNU_RELRO => "GNU RELOCATABLE", + ELF_PROG_GNU_STACK => "GNU STACK", + _ => "UNKNOWN", + }; + + let offset = self.offset; + let vaddr = self.vaddr; + let paddr = self.paddr; + let file_size = self.file_size; + let mem_size = self.mem_size; + let align = self.align; + f.write_fmt(format_args!( + "Type: {}, offset: {:#x}, vaddr: {:#x}, paddr: {:#x}, file_size: {:#x}, mem_size: {:#x}, align: {:#x}", + prog_type, offset, vaddr, paddr, file_size, mem_size, align + )) + } +} + +fn load_program_segment( + prog_header: &Elf64ProgramHeader, + file: &[u8], + vmas: &Capability, +) -> Result<(), ZError> { + debug!("{:?}", prog_header); + match prog_header.prog_type { + ELF_PROG_NULL + | ELF_PROG_NOTE + | ELF_PROG_PTHDR + | ELF_PROG_GNU_STACK + | ELF_PROG_GNU_RELRO + | ELF_PROG_GNU_EH_FRAME => Ok(()), + ELF_PROG_LOAD => { + let page_offset = prog_header.vaddr & 0xFFF; + let mem_size = page_offset + prog_header.mem_size; + + let mut mem_object = crate::mem::MemoryRegion::new(mem_size)?; + + for i in mem_object.mut_slice() { + *i = 0; + } + + let file_start = prog_header.offset as usize; + let file_end = file_start + prog_header.file_size as usize; + let from_slice = &file[file_start..file_end]; + + let mem_start = page_offset as usize; + let mem_end = mem_start + (prog_header.file_size as usize); + let to_slice: &mut [u8] = &mut mem_object.mut_slice()[mem_start..mem_end]; + + to_slice.copy_from_slice(from_slice); + + let vaddr = prog_header.vaddr - page_offset; + syscall::address_space_map_external(vmas, mem_object.cap(), vaddr) + } + ELF_PROG_DYNAMIC => { + debug!("Unimplemented dynamic elf sections."); + Err(ZError::UNIMPLEMENTED) + } + ELF_PROG_INTERP => { + debug!("Unimplemented interpreter elf sections."); + Err(ZError::UNIMPLEMENTED) + } + ELF_PROG_SHLIB => { + debug!("Unimplemented shared lib elf sections."); + Err(ZError::UNIMPLEMENTED) + } + ELF_PROG_THREAD_LOCAL => { + debug!("Unimplemented thread local elf sections."); + Err(ZError::UNIMPLEMENTED) + } + _ => { + let prog_type = prog_header.prog_type; + debug!("Unknown elf program header type: {:#x}", prog_type); + Err(ZError::UNKNOWN) + } + } +} + +fn load_elf_program(elf_file: &[u8], vmas: &Capability) -> Result { + assert!(elf_file.len() > size_of::()); + let header: &Elf64Header = unsafe { + elf_file + .as_ptr() + .cast::() + .as_ref() + .ok_or(ZError::NULL_PTR)? + }; + debug!("{:?}", header); + validate_header(header)?; + + for prog_ind in 0..header.program_header_count { + let prog_header_offset = header.program_header_offset + + ((prog_ind as u64) * (header.program_header_entry_size as u64)); + let prog_header_end = prog_header_offset + header.program_header_entry_size as u64; + let prog_header_slice = &elf_file[prog_header_offset as usize..prog_header_end as usize]; + let prog_header: &Elf64ProgramHeader = unsafe { + prog_header_slice + .as_ptr() + .cast::() + .as_ref() + .ok_or(ZError::NULL_PTR)? + }; + + load_program_segment(prog_header, elf_file, vmas)?; + } + Ok(header.entry) +} + +pub fn spawn_process_from_elf_and_init( + elf_file: &[u8], + init_cap: Capability, +) -> Result { + let self_cap = Capability::take_copy(unsafe { init::SELF_PROC_CAP })?; + let port_cap = syscall::port_create()?; + + let (new_proc_cap, new_as_cap, foreign_port_id) = + syscall::process_spawn(&self_cap, port_cap.duplicate(Capability::PERMS_ALL)?)?; + + let entry_point = load_elf_program(elf_file, &new_as_cap)?; + + let port = crate::port::PortClient::take_from(port_cap); + + port.write_u64_and_cap( + crate::init::Z_INIT_SELF_PROC, + new_proc_cap.duplicate(Capability::PERMS_ALL)?, + )?; + port.write_u64_and_cap(crate::init::Z_INIT_SELF_VMAS, new_as_cap)?; + port.write_u64_and_cap(crate::init::Z_INIT_ENDPOINT, init_cap)?; + + let thread_cap = syscall::thread_create(&new_proc_cap)?; + + syscall::thread_start(&thread_cap, entry_point, foreign_port_id, 0)?; + + Ok(new_proc_cap) +} + +pub fn spawn_process_from_elf(elf_file: &[u8]) -> Result { + let yellowstone = Capability::take_copy(unsafe { crate::init::INIT_ENDPOINT })?; + spawn_process_from_elf_and_init(elf_file, yellowstone) +} diff --git a/rust/lib/mammoth/src/init.rs b/rust/lib/mammoth/src/init.rs new file mode 100644 index 0000000..c190ad1 --- /dev/null +++ b/rust/lib/mammoth/src/init.rs @@ -0,0 +1,50 @@ +use crate::cap::Capability; +use crate::syscall; +use crate::zion::z_cap_t; + +// From /zion/include/ztypes.h +pub const Z_INIT_SELF_PROC: u64 = 0x4000_0000; +pub const Z_INIT_SELF_VMAS: u64 = 0x4000_0001; +pub const Z_INIT_ENDPOINT: u64 = 0x4100_0000; +const Z_BOOT_DENALI_VMMO: u64 = 0x4200_0000; +const Z_BOOT_VICTORIA_FALLS_VMMO: u64 = 0x4200_0001; +const Z_BOOT_PCI_VMMO: u64 = 0x4200_0002; +const Z_BOOT_FRAMEBUFFER_INFO_VMMO: u64 = 0x4200_0003; + +pub static mut SELF_PROC_CAP: z_cap_t = 0; +pub static mut SELF_VMAS_CAP: z_cap_t = 0; +pub static mut INIT_ENDPOINT: z_cap_t = 0; + +// Boot capabilities, are generally only passed to yellowstone. +pub static mut BOOT_DENALI_VMMO: z_cap_t = 0; +pub static mut BOOT_VICTORIA_FALLS_VMMO: z_cap_t = 0; +pub static mut BOOT_PCI_VMMO: z_cap_t = 0; +pub static mut BOOT_FRAMEBUFFER_INFO_VMMO: z_cap_t = 0; + +pub fn parse_init_port(port_cap: z_cap_t) { + let init_port = Capability::take(port_cap); + loop { + let mut bytes: [u8; 8] = [0; 8]; + let mut caps: [u64; 1] = [0]; + + let resp = syscall::port_poll(&init_port, &mut bytes, &mut caps); + if resp.is_err() { + break; + } + + let init_sig = u64::from_le_bytes(bytes); + + unsafe { + match init_sig { + Z_INIT_SELF_PROC => SELF_PROC_CAP = caps[0], + Z_INIT_SELF_VMAS => SELF_VMAS_CAP = caps[0], + Z_INIT_ENDPOINT => INIT_ENDPOINT = caps[0], + Z_BOOT_DENALI_VMMO => BOOT_DENALI_VMMO = caps[0], + Z_BOOT_VICTORIA_FALLS_VMMO => BOOT_VICTORIA_FALLS_VMMO = caps[0], + Z_BOOT_PCI_VMMO => BOOT_PCI_VMMO = caps[0], + Z_BOOT_FRAMEBUFFER_INFO_VMMO => BOOT_FRAMEBUFFER_INFO_VMMO = caps[0], + _ => syscall::debug("Unknown Cap in Init"), + } + } + } +} diff --git a/rust/lib/mammoth/src/lib.rs b/rust/lib/mammoth/src/lib.rs new file mode 100644 index 0000000..2058a6b --- /dev/null +++ b/rust/lib/mammoth/src/lib.rs @@ -0,0 +1,21 @@ +#![no_std] +#![allow(non_upper_case_globals)] +#![allow(non_camel_case_types)] +#![allow(non_snake_case)] + +extern crate alloc; + +#[macro_use] +pub mod macros; + +pub mod cap; +mod cap_syscall; +pub mod elf; +pub mod init; +pub mod mem; +pub mod port; +pub mod sync; +pub mod syscall; +pub mod task; +pub mod thread; +pub mod zion; diff --git a/rust/lib/mammoth/src/macros.rs b/rust/lib/mammoth/src/macros.rs new file mode 100644 index 0000000..3fc64c5 --- /dev/null +++ b/rust/lib/mammoth/src/macros.rs @@ -0,0 +1,61 @@ +use alloc::string::String; +use alloc::vec::Vec; +use core::fmt; + +#[derive(Default)] +pub struct Writer { + int_vec: Vec, +} + +impl Writer { + pub fn new() -> Self { + Writer::default() + } +} + +impl From for String { + fn from(value: Writer) -> Self { + String::from_utf8(value.int_vec).expect("Failed to convert") + } +} + +impl fmt::Write for Writer { + fn write_str(&mut self, s: &str) -> fmt::Result { + for c in s.bytes() { + self.int_vec.push(c); + } + fmt::Result::Ok(()) + } +} + +#[macro_export] +macro_rules! debug { + () => { + $crate::syscall::debug(""); + }; + ($fmt:literal) => { + $crate::syscall::debug($fmt); + }; + ($fmt:literal, $($val:expr),+) => {{ + use core::fmt::Write as _; + let mut w = $crate::macros::Writer::new(); + write!(&mut w, $fmt, $($val),*).expect("Failed to format"); + let s: alloc::string::String = w.into(); + $crate::syscall::debug(&s); + }}; +} + +#[macro_export] +macro_rules! define_entry { + () => { + #[no_mangle] + pub extern "C" fn _start(init_port: $crate::zion::z_cap_t) -> ! { + $crate::init::parse_init_port(init_port); + $crate::mem::init_heap(); + + let resp = main(); + + $crate::syscall::process_exit(resp); + } + }; +} diff --git a/rust/lib/mammoth/src/mem.rs b/rust/lib/mammoth/src/mem.rs new file mode 100644 index 0000000..90183ec --- /dev/null +++ b/rust/lib/mammoth/src/mem.rs @@ -0,0 +1,197 @@ +use crate::cap::Capability; +use crate::syscall; +use crate::zion::ZError; +use alloc::slice; +use core::fmt::Debug; +use core::ptr::{addr_of, addr_of_mut}; + +#[cfg(feature = "hosted")] +use linked_list_allocator::LockedHeap; + +#[cfg(feature = "hosted")] +#[global_allocator] +static ALLOCATOR: LockedHeap = LockedHeap::empty(); + +pub static mut CAN_ALLOC: bool = false; + +#[cfg(feature = "hosted")] +pub fn init_heap() { + // 1 MiB + let size = 0x10_0000; + let vmmo_cap = syscall::memory_object_create(size).expect("Failed to create memory object"); + let vaddr = syscall::address_space_map(&vmmo_cap).expect("Failed to map memory object"); + unsafe { + ALLOCATOR.lock().init(vaddr as *mut u8, size as usize); + CAN_ALLOC = true; + } +} + +pub struct MemoryRegion { + mem_cap: Capability, + virt_addr: u64, + size: u64, +} + +impl MemoryRegion { + pub fn direct_physical(paddr: u64, size: u64) -> Result { + let mem_cap = syscall::memory_object_direct_physical(paddr, size)?; + let virt_addr = syscall::address_space_map(&mem_cap)?; + Ok(Self { + mem_cap, + virt_addr, + size, + }) + } + + pub fn contiguous_physical(size: u64) -> Result<(Self, u64), ZError> { + let (mem_cap, paddr) = syscall::memory_object_contiguous_physical(size)?; + let virt_addr = syscall::address_space_map(&mem_cap)?; + Ok(( + Self { + mem_cap, + virt_addr, + size, + }, + paddr, + )) + } + + pub fn from_cap(mem_cap: Capability) -> Result { + let virt_addr = syscall::address_space_map(&mem_cap)?; + let size = syscall::memory_object_inspect(&mem_cap)?; + Ok(Self { + mem_cap, + virt_addr, + size, + }) + } + + pub fn new(size: u64) -> Result { + let mem_cap = syscall::memory_object_create(size)?; + let virt_addr = syscall::address_space_map(&mem_cap)?; + Ok(Self { + mem_cap, + virt_addr, + size, + }) + } + + pub fn slice(&self) -> &[T] { + unsafe { + slice::from_raw_parts( + self.virt_addr as *const T, + self.size as usize / size_of::(), + ) + } + } + + pub fn mut_slice(&mut self) -> &mut [T] { + unsafe { + slice::from_raw_parts_mut( + self.virt_addr as *mut T, + self.size as usize / size_of::(), + ) + } + } + + pub fn raw_ptr_at_offset(&self, offset: u64) -> *const T { + // TODO: Come up with a better safety check here. + // We can't use the size of T because it might not be sized. + assert!(offset + size_of::() as u64 <= self.size); + (self.virt_addr + offset) as *const T + } + + pub fn cap(&self) -> &Capability { + &self.mem_cap + } + + pub fn size(&self) -> u64 { + self.size + } + + pub fn duplicate(&self, offset: u64, length: u64) -> Result { + syscall::memory_obj_duplicate(&self.mem_cap, offset, length) + } +} + +impl AsRef for MemoryRegion { + fn as_ref(&self) -> &T { + unsafe { (self.virt_addr as *const T).as_ref().unwrap() } + } +} + +impl AsMut for MemoryRegion { + fn as_mut(&mut self) -> &mut T { + unsafe { (self.virt_addr as *mut T).as_mut().unwrap() } + } +} + +impl Drop for MemoryRegion { + fn drop(&mut self) { + // FIXME: We shouldn't have to do this manual adjustment. + let mut max = self.virt_addr + self.size; + if (max & 0xFFF) != 0 { + max += 0x1000 - (max & 0xFFF); + } + syscall::address_space_unmap(self.virt_addr, max).expect("Failed to unmap memory"); + } +} + +pub struct Volatile { + /// TODO: This should maybe be MaybeUninit. + data: T, +} + +impl Volatile { + pub fn read(&self) -> T + where + T: Copy, + { + unsafe { addr_of!(self.data).cast::().read_volatile() } + } + + pub fn write(&mut self, data: T) { + unsafe { + addr_of_mut!(self.data).cast::().write_volatile(data); + } + } + + pub fn update(&mut self, func: F) + where + T: Copy, + F: Fn(&mut T), + { + let mut data = self.read(); + func(&mut data); + self.write(data); + } +} + +impl Debug for Volatile +where + T: Debug + Copy, +{ + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + write!(f, "{:?}", self.read()) + } +} + +pub fn map_cap_and_leak(mem_cap: Capability) -> u64 { + let vaddr = syscall::address_space_map(&mem_cap).unwrap(); + mem_cap.release(); + vaddr +} + +pub fn map_direct_physical_and_leak(paddr: u64, size: u64) -> u64 { + let mem_cap = syscall::memory_object_direct_physical(paddr, size).unwrap(); + let vaddr = syscall::address_space_map(&mem_cap).unwrap(); + mem_cap.release(); + vaddr +} + +pub fn map_physical_and_leak(size: u64) -> (u64, u64) { + let (mem_cap, paddr) = syscall::memory_object_contiguous_physical(size).unwrap(); + let vaddr = syscall::address_space_map(&mem_cap).unwrap(); + mem_cap.release(); + (vaddr, paddr) +} diff --git a/rust/lib/mammoth/src/port.rs b/rust/lib/mammoth/src/port.rs new file mode 100644 index 0000000..34bddb1 --- /dev/null +++ b/rust/lib/mammoth/src/port.rs @@ -0,0 +1,68 @@ +use crate::cap::Capability; +use crate::syscall::{port_create, port_recv}; +use crate::zion::{kZionPerm_Read, z_cap_t, ZError}; + +pub struct PortServer { + port_cap: Capability, +} + +impl PortServer { + pub fn new() -> Result { + Ok(Self { + port_cap: port_create()?, + }) + } + + pub fn from_cap(port_cap: Capability) -> Self { + Self { port_cap } + } + + pub fn create_client_cap(&self) -> Result { + self.port_cap + .duplicate(!kZionPerm_Read) + .map(|c| c.release()) + } + + pub fn recv_byte(&self) -> Result { + let mut caps: [z_cap_t; 0] = []; + let mut bytes: [u8; 1] = [0]; + + port_recv(&self.port_cap, &mut bytes, &mut caps)?; + + Ok(bytes[0]) + } + + pub fn recv_u16(&self) -> Result { + let mut caps: [z_cap_t; 0] = []; + let mut bytes: [u8; 2] = [0; 2]; + + port_recv(&self.port_cap, &mut bytes, &mut caps)?; + + Ok(u16::from_le_bytes(bytes)) + } + + pub fn recv_null(&self) -> Result<(), ZError> { + let mut caps: [z_cap_t; 0] = []; + let mut bytes: [u8; 0] = []; + + port_recv(&self.port_cap, &mut bytes, &mut caps)?; + + Ok(()) + } +} + +pub struct PortClient { + port_cap: Capability, +} + +impl PortClient { + pub fn take_from(port_cap: Capability) -> Self { + Self { port_cap } + } + + #[warn(unused_results)] + pub fn write_u64_and_cap(&self, bytes: u64, cap: Capability) -> Result<(), ZError> { + let mut caps: [z_cap_t; 1] = [cap.release()]; + crate::syscall::port_send(&self.port_cap, &bytes.to_le_bytes(), &mut caps) + } +} diff --git a/rust/lib/mammoth/src/sync.rs b/rust/lib/mammoth/src/sync.rs new file mode 100644 index 0000000..f435e32 --- /dev/null +++ b/rust/lib/mammoth/src/sync.rs @@ -0,0 +1,81 @@ +use core::cell::UnsafeCell; +use core::ops::Deref; +use core::ops::DerefMut; + +use crate::{cap::Capability, syscall, zion::ZError}; + +pub struct Semaphore { + cap: Capability, +} + +impl Semaphore { + pub fn new() -> Result { + syscall::semaphore_create().map(|cap| Self { cap }) + } + + pub fn wait(&self) -> Result<(), ZError> { + syscall::semaphone_wait(&self.cap) + } + + pub fn signal(&self) -> Result<(), ZError> { + syscall::semaphone_signal(&self.cap) + } +} + +pub struct Mutex { + cap: Capability, + data: UnsafeCell, +} + +unsafe impl Sync for Mutex where T: Send {} + +pub struct MutexGuard<'a, T> { + mutex: &'a Mutex, +} + +unsafe impl Send for MutexGuard<'_, T> where T: Send {} +unsafe impl Sync for MutexGuard<'_, T> where T: Sync {} + +impl Deref for MutexGuard<'_, T> { + type Target = T; + + fn deref(&self) -> &Self::Target { + unsafe { &*self.mutex.data.get() } + } +} + +impl DerefMut for MutexGuard<'_, T> { + fn deref_mut(&mut self) -> &mut Self::Target { + unsafe { &mut *self.mutex.data.get() } + } +} + +impl<'a, T> Mutex { + pub fn new(data: T) -> Mutex { + Mutex { + cap: syscall::mutex_create().unwrap(), + data: UnsafeCell::new(data), + } + } + + pub fn lock(&'a self) -> MutexGuard<'a, T> { + syscall::mutex_lock(&self.cap).unwrap(); + + MutexGuard { mutex: self } + } +} + +impl Drop for MutexGuard<'_, T> { + fn drop(&mut self) { + syscall::mutex_release(&self.mutex.cap).unwrap(); + } +} + +impl Default for Mutex +where + T: Default, +{ + fn default() -> Self { + Self::new(T::default()) + } +} diff --git a/rust/lib/mammoth/src/syscall.rs b/rust/lib/mammoth/src/syscall.rs new file mode 100644 index 0000000..bab76ca --- /dev/null +++ b/rust/lib/mammoth/src/syscall.rs @@ -0,0 +1,473 @@ +extern crate alloc; + +use crate::cap::Capability; +use crate::zion; +use crate::zion::z_cap_t; +use crate::zion::ZError; +use core::ffi::c_void; + +#[cfg(feature = "hosted")] +use core::panic::PanicInfo; + +fn syscall(id: u64, req: &T) -> Result<(), ZError> { + unsafe { + let resp = zion::SysCall1(id, req as *const T as *const c_void); + if resp != 0 { + return Err(zion::ZError::from(resp)); + } + } + Ok(()) +} + +#[cfg(feature = "hosted")] +#[panic_handler] +fn panic(info: &PanicInfo) -> ! { + unsafe { + if crate::mem::CAN_ALLOC { + crate::debug!("Panic occured: {}", info); + } else { + debug("Panic occured before heap initialized.") + } + } + // Internal error. + let req = zion::ZProcessExitReq { code: 0x100 }; + let _ = syscall(zion::kZionProcessExit, &req); + unreachable!() +} + +pub fn debug(msg: &str) { + let req = zion::ZDebugReq { + message: msg.as_ptr() as *const i8, + size: msg.len() as u64, + }; + syscall(zion::kZionDebug, &req).expect("Failed to write"); +} + +pub fn process_spawn( + proc_cap: &Capability, + bootstrap_cap: Capability, +) -> Result<(Capability, Capability, u64), ZError> { + let mut new_proc_cap = 0; + let mut new_as_cap = 0; + let mut new_bootstrap_cap = 0; + + syscall( + zion::kZionProcessSpawn, + &zion::ZProcessSpawnReq { + proc_cap: proc_cap.raw(), + bootstrap_cap: bootstrap_cap.release(), + new_proc_cap: &mut new_proc_cap, + new_vmas_cap: &mut new_as_cap, + new_bootstrap_cap: &mut new_bootstrap_cap, + }, + )?; + + Ok(( + Capability::take(new_proc_cap), + Capability::take(new_as_cap), + new_bootstrap_cap, + )) +} + +pub fn process_exit(code: u64) -> ! { + let _ = syscall(zion::kZionProcessExit, &zion::ZProcessExitReq { code }); + + unreachable!() +} + +pub fn process_wait(proc_cap: &Capability) -> Result { + let mut err_code = 0; + syscall( + zion::kZionProcessWait, + &zion::ZProcessWaitReq { + proc_cap: proc_cap.raw(), + exit_code: &mut err_code, + }, + )?; + Ok(err_code) +} + +pub fn thread_create(proc_cap: &Capability) -> Result { + let mut cap = 0; + syscall( + zion::kZionThreadCreate, + &zion::ZThreadCreateReq { + proc_cap: proc_cap.raw(), + thread_cap: &mut cap, + }, + )?; + Ok(Capability::take(cap)) +} + +pub fn thread_sleep(millis: u64) -> Result<(), ZError> { + syscall(zion::kZionThreadSleep, &zion::ZThreadSleepReq { millis }) +} + +pub fn thread_start( + thread_cap: &Capability, + entry: u64, + arg1: u64, + arg2: u64, +) -> Result<(), ZError> { + syscall( + zion::kZionThreadStart, + &zion::ZThreadStartReq { + thread_cap: thread_cap.raw(), + entry, + arg1, + arg2, + }, + ) +} + +pub fn thread_wait(thread_cap: &Capability) -> Result<(), ZError> { + syscall( + zion::kZionThreadWait, + &zion::ZThreadWaitReq { + thread_cap: thread_cap.raw(), + }, + ) +} + +pub fn thread_exit() -> ! { + let _ = syscall(zion::kZionThreadExit, &zion::ZThreadExitReq {}); + unreachable!(); +} + +pub fn memory_object_create(size: u64) -> Result { + let mut vmmo_cap = 0; + let obj_req = zion::ZMemoryObjectCreateReq { + size, + vmmo_cap: &mut vmmo_cap as *mut u64, + }; + syscall(zion::kZionMemoryObjectCreate, &obj_req)?; + Ok(Capability::take(vmmo_cap)) +} + +pub fn memory_object_direct_physical(paddr: u64, size: u64) -> Result { + let mut vmmo_cap = 0; + syscall( + zion::kZionMemoryObjectCreatePhysical, + &zion::ZMemoryObjectCreatePhysicalReq { + paddr, + size, + vmmo_cap: &mut vmmo_cap, + }, + )?; + Ok(Capability::take(vmmo_cap)) +} + +pub fn memory_object_contiguous_physical(size: u64) -> Result<(Capability, u64), ZError> { + let mut vmmo_cap = 0; + let mut paddr = 0; + syscall( + zion::kZionMemoryObjectCreateContiguous, + &zion::ZMemoryObjectCreateContiguousReq { + size, + paddr: &mut paddr, + vmmo_cap: &mut vmmo_cap, + }, + )?; + + Ok((Capability::take(vmmo_cap), paddr)) +} + +pub fn memory_object_inspect(mem_cap: &Capability) -> Result { + let mut mem_size = 0; + syscall( + zion::kZionMemoryObjectInspect, + &zion::ZMemoryObjectInspectReq { + vmmo_cap: mem_cap.raw(), + size: &mut mem_size, + }, + )?; + Ok(mem_size) +} + +pub fn memory_obj_duplicate( + mem_cap: &Capability, + base_offset: u64, + length: u64, +) -> Result { + let mut new_cap = 0; + syscall( + zion::kZionMemoryObjectDuplicate, + &zion::ZMemoryObjectDuplicateReq { + vmmo_cap: mem_cap.raw(), + base_offset, + length, + new_vmmo_cap: &mut new_cap, + }, + )?; + Ok(Capability::take(new_cap)) +} + +pub fn address_space_map(vmmo_cap: &Capability) -> Result { + let mut vaddr: u64 = 0; + // FIXME: Allow caller to pass these options. + let vmas_req = zion::ZAddressSpaceMapReq { + vmmo_cap: vmmo_cap.raw(), + vmas_cap: unsafe { crate::init::SELF_VMAS_CAP }, + align: 0x2000, + vaddr: &mut vaddr as *mut u64, + vmas_offset: 0, + }; + + syscall(zion::kZionAddressSpaceMap, &vmas_req)?; + Ok(vaddr) +} + +pub fn address_space_map_external( + vmas_cap: &Capability, + vmmo_cap: &Capability, + vaddr: u64, +) -> Result<(), ZError> { + let mut vaddr_throw: u64 = 0; + let vmas_req = zion::ZAddressSpaceMapReq { + vmas_cap: vmas_cap.raw(), + vmmo_cap: vmmo_cap.raw(), + align: 0, + vaddr: &mut vaddr_throw as *mut u64, + vmas_offset: vaddr, + }; + + syscall(zion::kZionAddressSpaceMap, &vmas_req)?; + Ok(()) +} + +pub fn address_space_unmap(lower_addr: u64, upper_addr: u64) -> Result<(), ZError> { + syscall( + zion::kZionAddressSpaceUnmap, + &zion::ZAddressSpaceUnmapReq { + vmas_cap: unsafe { crate::init::SELF_VMAS_CAP }, + lower_addr, + upper_addr, + }, + ) +} + +pub fn port_create() -> Result { + let mut port_cap = 0; + syscall( + zion::kZionPortCreate, + &zion::ZPortCreateReq { + port_cap: &mut port_cap, + }, + )?; + Ok(Capability::take(port_cap)) +} + +pub fn port_send(port_cap: &Capability, bytes: &[u8], caps: &mut [z_cap_t]) -> Result<(), ZError> { + syscall( + zion::kZionPortSend, + &zion::ZPortSendReq { + port_cap: port_cap.raw(), + num_bytes: bytes.len() as u64, + data: bytes.as_ptr() as *const c_void, + num_caps: caps.len() as u64, + // FIXME: This shouldn't need to be mutable. + caps: caps.as_mut_ptr(), + }, + ) +} + +pub fn port_recv( + port_cap: &Capability, + bytes: &mut [u8], + caps: &mut [u64], +) -> Result<(u64, u64), ZError> { + let mut num_bytes = bytes.len() as u64; + let mut num_caps = caps.len() as u64; + syscall( + zion::kZionPortRecv, + &zion::ZPortRecvReq { + port_cap: port_cap.raw(), + data: bytes.as_mut_ptr() as *mut c_void, + num_bytes: &mut num_bytes as *mut u64, + caps: caps.as_mut_ptr(), + num_caps: &mut num_caps as *mut u64, + }, + )?; + Ok((num_bytes, num_caps)) +} + +pub fn port_poll( + port_cap: &Capability, + bytes: &mut [u8], + caps: &mut [u64], +) -> Result<(u64, u64), ZError> { + let mut num_bytes = bytes.len() as u64; + let mut num_caps = caps.len() as u64; + let req = zion::ZPortPollReq { + port_cap: port_cap.raw(), + data: bytes.as_mut_ptr() as *mut c_void, + num_bytes: &mut num_bytes as *mut u64, + caps: caps.as_mut_ptr(), + num_caps: &mut num_caps as *mut u64, + }; + syscall(zion::kZionPortPoll, &req)?; + Ok((num_bytes, num_caps)) +} + +pub fn register_msi_irq() -> Result<(Capability, u64), ZError> { + let mut irq_num: u64 = 0; + let mut port_cap: z_cap_t = 0; + syscall( + zion::kZionMsiIrqRegister, + &zion::ZMsiIrqRegisterReq { + irq_num: &mut irq_num as *mut u64, + port_cap: &mut port_cap, + }, + )?; + Ok((Capability::take(port_cap), irq_num)) +} + +pub fn endpoint_create() -> Result { + let mut endpoint_cap: z_cap_t = 0; + syscall( + zion::kZionEndpointCreate, + &zion::ZEndpointCreateReq { + endpoint_cap: &mut endpoint_cap, + }, + )?; + Ok(Capability::take(endpoint_cap)) +} + +pub fn endpoint_send( + endpoint_cap: &Capability, + bytes: &[u8], + caps: &[z_cap_t], +) -> Result { + let mut reply_port_cap: u64 = 0; + let send_req = zion::ZEndpointSendReq { + caps: caps.as_ptr(), + num_caps: caps.len() as u64, + endpoint_cap: endpoint_cap.raw(), + data: bytes.as_ptr() as *const c_void, + num_bytes: bytes.len() as u64, + reply_port_cap: &mut reply_port_cap, + }; + + syscall(zion::kZionEndpointSend, &send_req)?; + + Ok(Capability::take(reply_port_cap)) +} + +pub fn endpoint_recv( + endpoint_cap: &Capability, + bytes: &mut [u8], + caps: &mut [z_cap_t], +) -> Result<(u64, u64, Capability), ZError> { + let mut num_bytes = bytes.len() as u64; + let mut num_caps = caps.len() as u64; + let mut reply_port_cap = 0; + let recv_req = zion::ZEndpointRecvReq { + endpoint_cap: endpoint_cap.raw(), + data: bytes.as_mut_ptr() as *mut c_void, + num_bytes: &mut num_bytes, + caps: caps.as_mut_ptr(), + num_caps: &mut num_caps, + reply_port_cap: &mut reply_port_cap, + }; + + syscall(zion::kZionEndpointRecv, &recv_req)?; + + Ok((num_bytes, num_caps, Capability::take(reply_port_cap))) +} + +pub fn reply_port_send( + reply_port_cap: Capability, + bytes: &[u8], + caps: &[z_cap_t], +) -> Result<(), ZError> { + syscall( + zion::kZionReplyPortSend, + &zion::ZReplyPortSendReq { + reply_port_cap: reply_port_cap.raw(), + data: bytes.as_ptr() as *const c_void, + num_bytes: bytes.len() as u64, + caps: caps.as_ptr(), + num_caps: caps.len() as u64, + }, + ) +} + +pub fn reply_port_recv( + reply_port_cap: Capability, + bytes: &mut [u8], + caps: &mut [z_cap_t], +) -> Result<(u64, u64), ZError> { + let mut num_bytes = bytes.len() as u64; + let mut num_caps = caps.len() as u64; + let recv_req = zion::ZReplyPortRecvReq { + reply_port_cap: reply_port_cap.raw(), + caps: caps.as_mut_ptr(), + num_caps: &mut num_caps, + data: bytes.as_mut_ptr() as *mut c_void, + num_bytes: &mut num_bytes, + }; + + syscall(zion::kZionReplyPortRecv, &recv_req)?; + + Ok((num_bytes, num_caps)) +} + +pub fn mutex_create() -> Result { + let mut mutex_cap: z_cap_t = 0; + syscall( + zion::kZionMutexCreate, + &zion::ZMutexCreateReq { + mutex_cap: &mut mutex_cap, + }, + )?; + + Ok(Capability::take(mutex_cap)) +} + +pub fn mutex_lock(mutex_cap: &Capability) -> Result<(), ZError> { + syscall( + zion::kZionMutexLock, + &zion::ZMutexLockReq { + mutex_cap: mutex_cap.raw(), + }, + ) +} + +pub fn mutex_release(mutex_cap: &Capability) -> Result<(), ZError> { + syscall( + zion::kZionMutexRelease, + &zion::ZMutexReleaseReq { + mutex_cap: mutex_cap.raw(), + }, + ) +} + +pub fn semaphore_create() -> Result { + let mut sem_cap: z_cap_t = 0; + syscall( + zion::kZionSemaphoreCreate, + &zion::ZSemaphoreCreateReq { + semaphore_cap: &mut sem_cap, + }, + )?; + + Ok(Capability::take(sem_cap)) +} + +pub fn semaphone_signal(sem_cap: &Capability) -> Result<(), ZError> { + syscall( + zion::kZionSemaphoreSignal, + &zion::ZSemaphoreSignalReq { + semaphore_cap: sem_cap.raw(), + }, + ) +} + +pub fn semaphone_wait(sem_cap: &Capability) -> Result<(), ZError> { + syscall( + zion::kZionSemaphoreWait, + &zion::ZSemaphoreWaitReq { + semaphore_cap: sem_cap.raw(), + }, + ) +} diff --git a/rust/lib/mammoth/src/task/mod.rs b/rust/lib/mammoth/src/task/mod.rs new file mode 100644 index 0000000..ec25afa --- /dev/null +++ b/rust/lib/mammoth/src/task/mod.rs @@ -0,0 +1,145 @@ +use core::{ + future::Future, + pin::Pin, + sync::atomic::{AtomicU64, Ordering}, + task::{Context, Poll, Waker}, +}; + +use alloc::{ + boxed::Box, + collections::{BTreeMap, VecDeque}, + sync::Arc, + task::Wake, +}; + +use crate::{sync::Mutex, syscall}; + +#[derive(PartialEq, Eq, PartialOrd, Ord, Copy, Clone)] +struct TaskId(u64); + +impl TaskId { + pub fn new() -> TaskId { + static NEXT_ID: AtomicU64 = AtomicU64::new(0); + TaskId(NEXT_ID.fetch_add(1, Ordering::Relaxed)) + } +} + +pub struct Task { + id: TaskId, + future: Pin + Send>>, +} + +impl Task { + pub fn new(future: impl Future + Sync + Send + 'static) -> Task { + Task { + id: TaskId::new(), + future: Box::pin(future), + } + } + + pub fn poll(&mut self, context: &mut Context) -> Poll<()> { + self.future.as_mut().poll(context) + } +} + +struct TaskWaker { + task_id: TaskId, + task_queue: Arc>>, +} + +impl TaskWaker { + fn create_waker(task_id: TaskId, task_queue: Arc>>) -> Waker { + Waker::from(Arc::new(TaskWaker { + task_id, + task_queue, + })) + } + fn wake_task(&self) { + self.task_queue.lock().push_back(self.task_id); + } +} + +impl Wake for TaskWaker { + fn wake(self: Arc) { + self.wake_task(); + } + + fn wake_by_ref(self: &Arc) { + self.wake_task(); + } +} + +#[derive(Default)] +pub struct Executor { + tasks: Arc>>, + // TODO: Consider a better datastructure for this. + task_queue: Arc>>, + waker_cache: BTreeMap, +} + +impl Executor { + pub fn new() -> Executor { + Executor::default() + } + + pub fn spawn(&mut self, task: Task) { + let task_id = task.id; + if self.tasks.lock().insert(task_id, task).is_some() { + panic!("Task is already existed in executor map"); + } + self.task_queue.lock().push_back(task_id); + } + + fn run_ready_tasks(&mut self) { + while let Some(task_id) = self.task_queue.lock().pop_front() { + let mut tasks = self.tasks.lock(); + let task = tasks.get_mut(&task_id).unwrap(); + let waker = self + .waker_cache + .entry(task_id) + .or_insert_with(|| TaskWaker::create_waker(task_id, self.task_queue.clone())); + let mut ctx = Context::from_waker(waker); + match task.poll(&mut ctx) { + Poll::Ready(()) => { + tasks.remove(&task_id); + self.waker_cache.remove(&task_id); + } + Poll::Pending => {} + }; + } + } + + pub fn run(&mut self) { + loop { + self.run_ready_tasks(); + // TODO: We need some sort of semaphore wait here. + syscall::thread_sleep(10).unwrap(); + } + } + + pub fn new_spawner(&self) -> Spawner { + Spawner::new(self.tasks.clone(), self.task_queue.clone()) + } +} + +pub struct Spawner { + tasks: Arc>>, + task_queue: Arc>>, +} + +impl Spawner { + fn new( + tasks: Arc>>, + task_queue: Arc>>, + ) -> Self { + Spawner { tasks, task_queue } + } + + pub fn spawn(&self, task: Task) { + let task_id = task.id; + if self.tasks.lock().insert(task_id, task).is_some() { + panic!("Task is already existed in executor map"); + } + self.task_queue.lock().push_back(task_id); + } +} diff --git a/rust/lib/mammoth/src/thread.rs b/rust/lib/mammoth/src/thread.rs new file mode 100644 index 0000000..aeb4b27 --- /dev/null +++ b/rust/lib/mammoth/src/thread.rs @@ -0,0 +1,46 @@ +use crate::cap::Capability; +use crate::syscall; +use crate::zion; + +use alloc::boxed::Box; +use core::ffi::c_void; + +pub struct JoinHandle { + cap: Capability, +} + +impl JoinHandle { + pub fn join(&self) -> Result<(), zion::ZError> { + syscall::thread_wait(&self.cap) + } +} + +#[no_mangle] +extern "C" fn entry_point(func: *mut c_void) -> ! { + unsafe { + Box::from_raw(func as *mut Box)(); + } + + syscall::thread_exit() +} + +pub fn spawn(f: F) -> JoinHandle +where + F: FnOnce() + Send + 'static, +{ + // This is very tricky. + // If we have been passed a closure that doesn't capture + // anything it will be 0 size and creating a Box of it + // will create a pointer with address 0x1. + // So we create this "main" closure that captures f to get around this. + // Also somehow having the explicit type annotation here is important. + let main: Box = Box::new(move || { + f(); + }); + let raw_main = Box::into_raw(Box::new(main)); + let proc_cap = Capability::take_copy(unsafe { crate::init::SELF_PROC_CAP }).unwrap(); + let cap = syscall::thread_create(&proc_cap).unwrap(); + syscall::thread_start(&cap, entry_point as usize as u64, raw_main as u64, 0).unwrap(); + + JoinHandle { cap } +} diff --git a/rust/lib/mammoth/src/zion.rs b/rust/lib/mammoth/src/zion.rs new file mode 100644 index 0000000..9aa3a69 --- /dev/null +++ b/rust/lib/mammoth/src/zion.rs @@ -0,0 +1,75 @@ +include!("bindings.rs"); + +use core::fmt; + +pub enum ZError { + UNKNOWN = 0x0, + // First set of error codes generally indicate user errors. + INVALID_ARGUMENT = 0x1, + NOT_FOUND = 0x2, + PERMISSION_DENIED = 0x3, + NULL_PTR = 0x4, + EMPTY = 0x5, + ALREADY_EXISTS = 0x6, + BUFFER_SIZE = 0x7, + FAILED_PRECONDITION = 0x8, + + // Second set of error codes generally indicate service errors. + INTERNAL = 0x100, + UNIMPLEMENTED = 0x101, + EXHAUSTED = 0x102, + INVALID_RESPONSE = 0x103, + + // Kernel specific error codes (relating to capabilities). + CAP_NOT_FOUND = 0x1000, + CAP_WRONG_TYPE = 0x1001, + CAP_PERMISSION_DENIED = 0x1002, +} + +impl From for ZError { + fn from(value: u64) -> Self { + match value { + 0x1 => ZError::INVALID_ARGUMENT, + 0x2 => ZError::NOT_FOUND, + 0x3 => ZError::PERMISSION_DENIED, + 0x4 => ZError::NULL_PTR, + 0x5 => ZError::EMPTY, + 0x6 => ZError::ALREADY_EXISTS, + 0x7 => ZError::BUFFER_SIZE, + 0x8 => ZError::FAILED_PRECONDITION, + + 0x100 => ZError::INTERNAL, + 0x101 => ZError::UNIMPLEMENTED, + 0x102 => ZError::EXHAUSTED, + 0x103 => ZError::INVALID_RESPONSE, + + 0x1000 => ZError::CAP_NOT_FOUND, + 0x1001 => ZError::CAP_WRONG_TYPE, + 0x1002 => ZError::CAP_PERMISSION_DENIED, + _ => ZError::UNKNOWN, + } + } +} + +impl fmt::Debug for ZError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let str = match self { + ZError::INVALID_ARGUMENT => "INVALID_ARGUMENT", + ZError::NOT_FOUND => "NOT_FOUND", + ZError::PERMISSION_DENIED => "PERMISSION_DENIED", + ZError::NULL_PTR => "NULL_PTR", + ZError::EMPTY => "EMPTY", + ZError::ALREADY_EXISTS => "ALREADY_EXISTS", + ZError::BUFFER_SIZE => "BUFFER_SIZE", + ZError::FAILED_PRECONDITION => "FAILED_PRECONDITION", + ZError::INTERNAL => "INTERNAL", + ZError::UNIMPLEMENTED => "UNIMPLEMENTED", + ZError::INVALID_RESPONSE => "INVALID_RESPONSE", + ZError::CAP_NOT_FOUND => "CAP_NOT_FOUND", + ZError::CAP_WRONG_TYPE => "CAP_WRONG_TYPE", + ZError::CAP_PERMISSION_DENIED => "CAP_PERMISSION_DENIED", + _ => "ZError", + }; + f.write_str(str) + } +} diff --git a/rust/lib/pci/Cargo.toml b/rust/lib/pci/Cargo.toml new file mode 100644 index 0000000..f47f8b1 --- /dev/null +++ b/rust/lib/pci/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "pci" +version = "0.1.0" +edition = "2024" + +[dependencies] +bitfield-struct = "0.8.0" +mammoth = {path = "../mammoth/"} diff --git a/rust/lib/pci/src/device.rs b/rust/lib/pci/src/device.rs new file mode 100644 index 0000000..e65e3da --- /dev/null +++ b/rust/lib/pci/src/device.rs @@ -0,0 +1,97 @@ +use alloc::vec::Vec; +use mammoth::{cap::Capability, mem::MemoryRegion, syscall, zion::ZError}; + +use crate::header::{ + PciCapabilityPointer, PciDeviceHeader, PciHeaderType, PciMsiCapability, get_header_type, +}; + +pub struct PciDevice { + memory_region: MemoryRegion, +} + +impl PciDevice { + pub fn from(memory_region: MemoryRegion) -> Result { + match get_header_type(&memory_region)? { + PciHeaderType::Device => {} + t => { + mammoth::debug!("Invalid header type: {:?}", t); + return Err(ZError::INVALID_ARGUMENT); + } + } + Ok(Self { memory_region }) + } + + pub fn from_cap(capability: Capability) -> Result { + Self::from(MemoryRegion::from_cap(capability)?) + } + + pub fn header(&self) -> &PciDeviceHeader { + self.memory_region.as_ref() + } + + pub fn get_capability_list(&self) -> Result, ZError> { + let status = self.header().status; + if !status.capability_list() { + return Err(ZError::NOT_FOUND); + } + + let mut cap_offset = self.header().capability_ptr; + + let mut cap_vec = Vec::new(); + while cap_offset != 0 { + let cap_ptr: &PciCapabilityPointer = unsafe { + self.memory_region + .raw_ptr_at_offset::(cap_offset as u64) + .as_ref() + .unwrap() + }; + cap_vec.push(cap_ptr); + cap_offset = cap_ptr.next_cap_offset; + } + + Ok(cap_vec) + } + + pub fn register_msi(&mut self) -> Result { + let caps = self.get_capability_list()?; + const MSI_CAP_ID: u8 = 0x05; + let msi_cap: &PciCapabilityPointer = caps + .iter() + .find(|cp| cp.cap_id == MSI_CAP_ID) + .ok_or(ZError::NOT_FOUND)?; + + let msi_cap = unsafe { + ((msi_cap as *const PciCapabilityPointer) as *mut PciMsiCapability) + .as_mut() + .unwrap() + }; + + let control = msi_cap.msi_control; + assert!( + control.capable_address_64(), + "We don't handle the non-64bit case for MSI yet." + ); + assert!( + control.multi_message_capable() == 0, + "We don't yet handle multi-message capable devices." + ); + + // FIXME: These probably need to be volatile writes. + let header: &mut PciDeviceHeader = self.memory_region.as_mut(); + header.command = header.command.with_interrupt_disable(true); + msi_cap.msi_control = control.with_msi_enable(true); + + // For setting addr and data field, see intel ref + // Vol 3. Section 11.11 + // TODO: This is hardcoded to APIC 0 currently. + msi_cap.msi_addr_lower = 0xFEE00000; + msi_cap.msi_addr_upper_or_data = 0x0; + + let (cap, irq_num) = syscall::register_msi_irq()?; + + // TODO: Do we need to set the specific level triggering options for this? + msi_cap.msi_data_if_64 = irq_num as u32; + + Ok(cap) + } +} diff --git a/rust/lib/pci/src/header.rs b/rust/lib/pci/src/header.rs new file mode 100644 index 0000000..ff2a144 --- /dev/null +++ b/rust/lib/pci/src/header.rs @@ -0,0 +1,180 @@ +use bitfield_struct::bitfield; +use mammoth::{mem::MemoryRegion, zion::ZError}; + +#[bitfield(u16)] +pub struct PciCommand { + io_space_enable: bool, + memory_space_enable: bool, + bus_master_enable: bool, + + #[bits(access=RO)] + special_cycles_enable: bool, + + #[bits(access=RO)] + memory_write_and_invalidate_enable: bool, + + #[bits(access=RO)] + vga_pallette_snoop_enable: bool, + + parity_error_response_enable: bool, + + #[bits(access=RO)] + wait_cycle_enable: bool, + + serr_enable: bool, + + fast_back_to_back_enable: bool, + + /// Parity is reversed here, set to true to disable. + /// Does not affect MSI. + pub interrupt_disable: bool, + + #[bits(5)] + __: u8, +} + +#[bitfield(u16)] +pub struct PciStatus { + #[bits(3)] + __: u8, + + #[bits(access=RO)] + pub interrupt_status: bool, + #[bits(access=RO)] + pub capability_list: bool, + #[bits(access=RO)] + pub capable_of_66mhz: bool, + + ___: bool, + + #[bits(access=RO)] + pub fast_back_to_back_capabale: bool, + + /// Write 1 to clear + pub master_data_parity_error: bool, + + #[bits(2, access=RO)] + pub devsel_timing: u8, + + /// Write 1 to clear + pub signaled_target_abort: bool, + /// Write 1 to clear + pub received_target_abort: bool, + /// Write 1 to clear + pub received_master_abort: bool, + /// Write 1 to clear + pub signaled_system_erro: bool, + /// Write 1 to clear + pub detected_parity_error: bool, +} + +/// Header definitions from https://wiki.osdev.org/PCI +#[repr(C, packed)] +#[derive(Debug)] +pub struct HeaderShared { + pub vendor_id: u16, + pub device_id: u16, + pub command: PciCommand, + pub status: PciStatus, + pub revision_id: u8, + pub prog_if: u8, + pub subclass: u8, + pub class_code: u8, + pub cache_line_size: u8, + pub latency_timer: u8, + pub header_type: u8, + bist: u8, +} + +const _: () = assert!(size_of::() == 16); + +#[repr(C, packed)] +#[derive(Debug)] +pub struct PciDeviceHeader { + pub vendor_id: u16, + pub device_id: u16, + pub command: PciCommand, + pub status: PciStatus, + pub revision_id: u8, + pub prog_if: u8, + pub subclass: u8, + pub class_code: u8, + pub cache_line_size: u8, + pub latency_timer: u8, + pub header_type: u8, + bist: u8, + pub bars: [u32; 6], + pub cardbus_cis_ptr: u32, + pub subsystem_vendor_id: u16, + pub subsystem_id: u16, + pub expansion_rom_address: u32, + pub capability_ptr: u8, + __: [u8; 7], + pub interrupt_line: u8, + pub interrupt_pin: u8, + pub min_grant: u8, + pub max_latency: u8, +} + +const _: () = assert!(size_of::() == 0x40); + +#[repr(C, packed)] +#[derive(Debug)] +pub struct PciCapabilityPointer { + pub cap_id: u8, + pub next_cap_offset: u8, +} + +#[bitfield(u16)] +pub struct PciMsiControl { + pub msi_enable: bool, + #[bits(3, access=RO)] + pub multi_message_capable: u8, + #[bits(3)] + pub multi_message_enable: u8, + + #[bits(access=RO)] + pub capable_address_64: bool, + + #[bits(access=RO)] + pub per_vector_masking: bool, + + #[bits(7)] + __: u8, +} + +#[repr(C, packed)] +#[derive(Debug)] +pub struct PciMsiCapability { + pub cap_id: u8, + pub next_cap_offset: u8, + pub msi_control: PciMsiControl, + pub msi_addr_lower: u32, + pub msi_addr_upper_or_data: u32, + pub msi_data_if_64: u32, + pub mask: u32, + pub pending: u32, +} + +#[derive(Debug)] +pub enum PciHeaderType { + Device, + PciBridge, + CardBusBridge, +} + +pub fn get_header_type(memory_region: &MemoryRegion) -> Result { + let shared: &HeaderShared = memory_region.as_ref(); + // The only reference I can find to the high bit here is at + // https://www.khoury.northeastern.edu/~pjd/cs7680/homework/pci-enumeration.html + // > Header Type: bit 7 (0x80) indicates whether it is a multi-function device, + match shared.header_type & (!0x80) { + 0x0 => Ok(PciHeaderType::Device), + 0x1 => Ok(PciHeaderType::PciBridge), + 0x2 => Ok(PciHeaderType::CardBusBridge), + _ => { + mammoth::debug!("Unknown pci header type: {:#x}", shared.header_type); + Err(ZError::INVALID_ARGUMENT) + } + } +} diff --git a/rust/lib/pci/src/lib.rs b/rust/lib/pci/src/lib.rs new file mode 100644 index 0000000..44e141a --- /dev/null +++ b/rust/lib/pci/src/lib.rs @@ -0,0 +1,8 @@ +#![no_std] + +extern crate alloc; + +mod device; +mod header; + +pub use device::PciDevice; diff --git a/rust/lib/voyageurs/Cargo.toml b/rust/lib/voyageurs/Cargo.toml new file mode 100644 index 0000000..e011e0c --- /dev/null +++ b/rust/lib/voyageurs/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "voyageurs" +version = "0.1.0" +edition = "2021" + +[dependencies] +mammoth = { path = "../mammoth" } +yellowstone-yunq = { path = "../yellowstone" } +yunq = {path = "../yunq"} + +[build-dependencies] +yunqc = {path = "../../../yunq/rust"} + diff --git a/rust/lib/voyageurs/build.rs b/rust/lib/voyageurs/build.rs new file mode 100644 index 0000000..52b5dfc --- /dev/null +++ b/rust/lib/voyageurs/build.rs @@ -0,0 +1,14 @@ +use std::fs; + +fn main() { + let input_file = "../../../sys/voyageurs/lib/voyageurs/voyageurs.yunq"; + + println!("cargo::rerun-if-changed={input_file}"); + + let input = fs::read_to_string(input_file).expect("Failed to read input file"); + + let code = yunqc::codegen(&input).expect("Failed to generate yunq code."); + + let out = std::env::var("OUT_DIR").unwrap() + "/yunq.rs"; + fs::write(out, code).expect("Failed to write generated code."); +} diff --git a/rust/lib/voyageurs/src/lib.rs b/rust/lib/voyageurs/src/lib.rs new file mode 100644 index 0000000..e92646c --- /dev/null +++ b/rust/lib/voyageurs/src/lib.rs @@ -0,0 +1,8 @@ +#![no_std] +#![feature(box_into_inner)] + +use core::include; + +include!(concat!(env!("OUT_DIR"), "/yunq.rs")); + +pub mod listener; diff --git a/rust/lib/voyageurs/src/listener.rs b/rust/lib/voyageurs/src/listener.rs new file mode 100644 index 0000000..89e40b6 --- /dev/null +++ b/rust/lib/voyageurs/src/listener.rs @@ -0,0 +1,226 @@ +use alloc::string::ToString; +use mammoth::cap::Capability; +use mammoth::port::PortServer; +use mammoth::thread; + +#[repr(u8)] +#[allow(dead_code)] +#[derive(PartialEq, Eq, PartialOrd, Ord, Copy, Clone)] +enum Keycode { + Unknown = 0x0, + + A = 0x1, + B = 0x2, + C = 0x3, + D = 0x4, + E = 0x5, + F = 0x6, + G = 0x7, + H = 0x8, + I = 0x9, + J = 0xA, + K = 0xB, + L = 0xC, + M = 0xD, + N = 0xE, + O = 0xF, + P = 0x10, + Q = 0x11, + R = 0x12, + S = 0x13, + T = 0x14, + U = 0x15, + V = 0x16, + W = 0x17, + X = 0x18, + Y = 0x19, + Z = 0x1A, + + NUM1 = 0x20, + NUM2 = 0x21, + NUM3 = 0x22, + NUM4 = 0x23, + NUM5 = 0x24, + NUM6 = 0x25, + NUM7 = 0x26, + NUM8 = 0x27, + NUM9 = 0x28, + NUM0 = 0x29, + + Space = 0x30, + Enter = 0x31, + Tab = 0x32, + Backspace = 0x33, + Delete = 0x34, + + Minus = 0x40, + Equals = 0x41, + LBrace = 0x42, + RBrace = 0x43, + BSlash = 0x44, + FSlash = 0x45, + Semicolon = 0x46, + Quote = 0x47, + Comma = 0x48, + Period = 0x49, + Backtick = 0x4A, + + LShift = 0x50, + RShift = 0x51, + LCtrl = 0x52, + RCtrl = 0x53, + LAlt = 0x54, + RAlt = 0x55, + Super = 0x56, + Esc = 0x57, + Up = 0x58, + Down = 0x59, + Left = 0x5A, + Right = 0x5B, +} + +impl Keycode { + fn from_scancode(scancode: u16) -> Self { + match scancode as u8 { + 0x04 => Keycode::A, + 0x05 => Keycode::B, + 0x06 => Keycode::C, + 0x07 => Keycode::D, + 0x08 => Keycode::E, + 0x09 => Keycode::F, + 0x0A => Keycode::G, + 0x0B => Keycode::H, + 0x0C => Keycode::I, + 0x0D => Keycode::J, + 0x0E => Keycode::K, + 0x0F => Keycode::L, + 0x10 => Keycode::M, + 0x11 => Keycode::N, + 0x12 => Keycode::O, + 0x13 => Keycode::P, + 0x14 => Keycode::Q, + 0x15 => Keycode::R, + 0x16 => Keycode::S, + 0x17 => Keycode::T, + 0x18 => Keycode::U, + 0x19 => Keycode::V, + 0x1A => Keycode::W, + 0x1B => Keycode::X, + 0x1C => Keycode::Y, + 0x1D => Keycode::Z, + 0x1E => Keycode::NUM1, + 0x1F => Keycode::NUM2, + 0x20 => Keycode::NUM3, + 0x21 => Keycode::NUM4, + 0x22 => Keycode::NUM5, + 0x23 => Keycode::NUM6, + 0x24 => Keycode::NUM7, + 0x25 => Keycode::NUM8, + 0x26 => Keycode::NUM9, + 0x27 => Keycode::NUM0, + 0x28 => Keycode::Enter, + 0x29 => Keycode::Esc, + 0x2A => Keycode::Backspace, + 0x2B => Keycode::Tab, + 0x2C => Keycode::Space, + 0x2D => Keycode::Minus, + 0x2E => Keycode::Equals, + 0x2F => Keycode::LBrace, + 0x30 => Keycode::RBrace, + 0x31 => Keycode::BSlash, + 0x33 => Keycode::Semicolon, + 0x34 => Keycode::Quote, + 0x35 => Keycode::Backtick, + 0x36 => Keycode::Comma, + 0x37 => Keycode::Period, + 0x38 => Keycode::FSlash, + 0x39 => Keycode::Esc, + _ => Keycode::Unknown, + } + } +} + +struct Modifiers(u8); + +impl Modifiers { + fn from_scancode(scancode: u16) -> Self { + Self((scancode >> 8) as u8) + } + + fn is_shift(&self) -> bool { + ((self.0 & 0x20) == 0x20) || ((self.0 & 0x2) == 0x2) + } +} + +fn into_char(keycode: Keycode, modifiers: Modifiers) -> char { + match keycode { + k if (Keycode::A..=Keycode::Z).contains(&k) => { + if modifiers.is_shift() { + let chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; + chars.as_bytes()[k as usize - Keycode::A as usize] as char + } else { + let chars = "abcdefghijklmnopqrstuvwxyz"; + chars.as_bytes()[k as usize - Keycode::A as usize] as char + } + } + k if (Keycode::NUM1..=Keycode::NUM0).contains(&k) => { + if modifiers.is_shift() { + let chars = "!@#$%^&*()"; + chars.as_bytes()[k as usize - Keycode::NUM1 as usize] as char + } else { + let chars = "12345687890"; + chars.as_bytes()[k as usize - Keycode::NUM1 as usize] as char + } + } + k if (Keycode::Minus..=Keycode::Backtick).contains(&k) => { + if modifiers.is_shift() { + let chars = "_+{}|?:\"<>~"; + chars.as_bytes()[k as usize - Keycode::Minus as usize] as char + } else { + let chars = "-=[]\\/;',.`"; + chars.as_bytes()[k as usize - Keycode::Minus as usize] as char + } + } + Keycode::Enter => '\n', + Keycode::Space => ' ', + Keycode::Tab => '\t', + Keycode::Backspace => '\x08', + _ => '\0', + } +} + +pub trait KeyboardHandler { + fn handle_char(&mut self, c: char); +} + +pub fn spawn_keyboard_listener(mut handler: T) -> thread::JoinHandle +where + T: KeyboardHandler + Send + 'static, +{ + let listen_port = PortServer::new().unwrap(); + let voyageur_endpoint = yellowstone_yunq::from_init_endpoint() + .get_endpoint(&yellowstone_yunq::GetEndpointRequest { + endpoint_name: "voyageurs".to_string(), + }) + .unwrap() + .endpoint; + + let mut voyageur_client = crate::VoyageursClient::new(Capability::take(voyageur_endpoint)); + + voyageur_client + .register_keyboard_listener(&crate::KeyboardListener { + port_capability: listen_port.create_client_cap().unwrap(), + }) + .unwrap(); + + let listen_thread = move || loop { + let scancode = listen_port.recv_u16().expect("Failed to recieve scancode"); + + let keycode = Keycode::from_scancode(scancode); + let modifiers = Modifiers::from_scancode(scancode); + + handler.handle_char(into_char(keycode, modifiers)) + }; + + thread::spawn(listen_thread) +} diff --git a/rust/lib/yellowstone/Cargo.toml b/rust/lib/yellowstone/Cargo.toml new file mode 100644 index 0000000..74c80c4 --- /dev/null +++ b/rust/lib/yellowstone/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "yellowstone-yunq" +version = "0.1.0" +edition = "2021" + +[dependencies] +mammoth = { path = "../mammoth" } +yunq = {path = "../yunq"} + +[build-dependencies] +yunqc = {path = "../../../yunq/rust"} + diff --git a/rust/lib/yellowstone/build.rs b/rust/lib/yellowstone/build.rs new file mode 100644 index 0000000..24472ac --- /dev/null +++ b/rust/lib/yellowstone/build.rs @@ -0,0 +1,14 @@ +use std::fs; + +fn main() { + let input_file = "../../../sys/yellowstone/lib/yellowstone/yellowstone.yunq"; + + println!("cargo::rerun-if-changed={input_file}"); + + let input = fs::read_to_string(input_file).expect("Failed to read input file"); + + let code = yunqc::codegen(&input).expect("Failed to generate yunq code."); + + let out = std::env::var("OUT_DIR").unwrap() + "/yunq.rs"; + fs::write(out, code).expect("Failed to write generated code."); +} diff --git a/rust/lib/yellowstone/src/lib.rs b/rust/lib/yellowstone/src/lib.rs new file mode 100644 index 0000000..ffdc267 --- /dev/null +++ b/rust/lib/yellowstone/src/lib.rs @@ -0,0 +1,21 @@ +#![no_std] + +use core::include; + +include!(concat!(env!("OUT_DIR"), "/yunq.rs")); + +use mammoth::init::INIT_ENDPOINT; + +static mut YELLOWSTONE_INIT: Option = None; + +pub fn from_init_endpoint() -> &'static mut YellowstoneClient { + unsafe { + #[allow(static_mut_refs)] + if YELLOWSTONE_INIT.is_none() { + YELLOWSTONE_INIT = Some(YellowstoneClient::new(Capability::take(INIT_ENDPOINT))); + } + + #[allow(static_mut_refs)] + YELLOWSTONE_INIT.as_mut().unwrap() + } +} diff --git a/rust/lib/yunq-test/Cargo.toml b/rust/lib/yunq-test/Cargo.toml new file mode 100644 index 0000000..4a4591d --- /dev/null +++ b/rust/lib/yunq-test/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "yunq-test" +version = "0.1.0" +edition = "2021" + +[dependencies] +mammoth = { path = "../mammoth", default-features = false} +yunq = {path = "../yunq", default-features = false} + +[build-dependencies] +yunqc = {path = "../../../yunq/rust"} + diff --git a/rust/lib/yunq-test/build.rs b/rust/lib/yunq-test/build.rs new file mode 100644 index 0000000..ef1bf8d --- /dev/null +++ b/rust/lib/yunq-test/build.rs @@ -0,0 +1,14 @@ +use std::fs; + +fn main() { + let input_file = "../../../lib/yunq/test/example/example.yunq"; + + println!("cargo::rerun-if-changed={input_file}"); + + let input = fs::read_to_string(input_file).expect("Failed to read input file"); + + let code = yunqc::codegen(&input).expect("Failed to generate yunq code."); + + let out = std::env::var("OUT_DIR").unwrap() + "/yunq.rs"; + fs::write(out, code).expect("Failed to write generated code."); +} diff --git a/rust/lib/yunq-test/src/lib.rs b/rust/lib/yunq-test/src/lib.rs new file mode 100644 index 0000000..9af3d55 --- /dev/null +++ b/rust/lib/yunq-test/src/lib.rs @@ -0,0 +1,140 @@ +#![no_std] + +include!(concat!(env!("OUT_DIR"), "/yunq.rs")); + +#[cfg(test)] +mod tests { + use super::*; + + extern crate std; + use std::println; + use std::vec; + + #[test] + fn basic_serialization() -> Result<(), ZError> { + let basic = Basic { + unsigned_int: 82, + signed_int: -1234, + strn: "abc".to_string(), + }; + + let mut buf = ByteBuffer::<1024>::new(); + let mut caps = Vec::new(); + basic.serialize(&mut buf, 0, &mut caps)?; + + let parsed = Basic::parse(&buf, 0, &caps)?; + + assert!(parsed == basic); + + Ok(()) + } + + #[test] + fn basic_serialization_as_request() -> Result<(), ZError> { + let basic = Basic { + unsigned_int: 82, + signed_int: -1234, + strn: "abc".to_string(), + }; + + let mut buf = ByteBuffer::<1024>::new(); + let mut caps = Vec::new(); + let req_id = 12; + basic.serialize_as_request(req_id, &mut buf, &mut caps)?; + + assert!(buf.at::(8)? == req_id); + + let parsed = Basic::parse_from_request(&buf, &caps)?; + + assert!(parsed == basic); + Ok(()) + } + + #[test] + fn capability_serialization() -> Result<(), ZError> { + let cap_id = 100; + let cap = Cap { cap: cap_id }; + + let mut buf = ByteBuffer::<1024>::new(); + let mut caps = Vec::new(); + cap.serialize(&mut buf, 0, &mut caps)?; + + assert!(caps.len() == 1); + assert!(caps[0] == cap_id); + + let parsed = Cap::parse(&buf, 0, &caps)?; + + assert!(parsed == cap); + + Ok(()) + } + + #[test] + fn repeated_serialization() -> Result<(), ZError> { + let rep = Repeated { + unsigned_ints: vec![0, 1, 3], + }; + + let mut buf = ByteBuffer::<1024>::new(); + let mut caps = Vec::new(); + rep.serialize(&mut buf, 0, &mut caps)?; + + let parsed = Repeated::parse(&buf, 0, &caps)?; + + assert!(parsed == rep); + + Ok(()) + } + + #[test] + fn nested_serialization() -> Result<(), ZError> { + let nested = Nested { + basic: Basic { + unsigned_int: 82, + signed_int: -1234, + strn: "abc".to_string(), + }, + cap1: Cap { cap: 37 }, + cap2: Cap { cap: 39 }, + }; + + let mut buf = ByteBuffer::<1024>::new(); + let mut caps = Vec::new(); + nested.serialize(&mut buf, 0, &mut caps)?; + + let parsed = Nested::parse(&buf, 0, &caps)?; + + assert!(parsed == nested); + + Ok(()) + } + + #[test] + fn repeated_nested_serialization() -> Result<(), ZError> { + let nested = RepeatedNested { + basics: vec![ + Basic { + unsigned_int: 82, + signed_int: -1234, + strn: "abc".to_string(), + }, + Basic { + unsigned_int: 21, + signed_int: -8, + strn: "def".to_string(), + }, + ], + caps: vec![Cap { cap: 123 }, Cap { cap: 12343 }], + }; + + let mut buf = ByteBuffer::<1024>::new(); + let mut caps = Vec::new(); + nested.serialize(&mut buf, 0, &mut caps)?; + + let parsed = RepeatedNested::parse(&buf, 0, &caps)?; + + assert!(parsed == nested); + + Ok(()) + } +} diff --git a/rust/lib/yunq/Cargo.toml b/rust/lib/yunq/Cargo.toml new file mode 100644 index 0000000..d5c3db0 --- /dev/null +++ b/rust/lib/yunq/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "yunq" +version = "0.1.0" +edition = "2021" + +[dependencies] +mammoth = {path = "../mammoth", default-features = false} + +[features] +default = ["hosted"] +hosted = ["mammoth/hosted"] diff --git a/rust/lib/yunq/src/buffer.rs b/rust/lib/yunq/src/buffer.rs new file mode 100644 index 0000000..7966337 --- /dev/null +++ b/rust/lib/yunq/src/buffer.rs @@ -0,0 +1,67 @@ +use alloc::boxed::Box; +use mammoth::zion::ZError; + +pub struct ByteBuffer { + buffer: Box<[u8; N]>, +} + +impl Default for ByteBuffer { + fn default() -> Self { + Self { + buffer: Box::new([0; N]), + } + } +} + +impl ByteBuffer { + pub fn new() -> Self { + ByteBuffer::default() + } + + pub fn size(&self) -> u64 { + N as u64 + } + + pub fn slice(&self, len: usize) -> &[u8] { + &self.buffer[..len] + } + + pub fn mut_slice(&mut self) -> &mut [u8] { + &mut self.buffer[..] + } + + pub fn write_at(&mut self, offset: usize, obj: T) -> Result<(), ZError> { + if (size_of::() + offset) > N { + return Err(ZError::BUFFER_SIZE); + } + unsafe { + *(self.buffer[offset..].as_mut_ptr() as *mut T) = obj; + } + Ok(()) + } + + pub fn write_str_at(&mut self, offset: usize, s: &str) -> Result<(), ZError> { + if (s.len() + offset) > N { + return Err(ZError::BUFFER_SIZE); + } + for i in 0..s.len() { + self.buffer[offset + i] = s.as_bytes()[i]; + } + Ok(()) + } + + pub fn at(&self, offset: usize) -> Result { + if (size_of::() + offset) > N { + return Err(ZError::BUFFER_SIZE); + } + unsafe { Ok(*(self.buffer[offset..].as_ptr() as *const T)) } + } + + pub fn str_at(&self, offset: usize, len: usize) -> Result<&str, ZError> { + if (len + offset) > N { + return Err(ZError::BUFFER_SIZE); + } + alloc::str::from_utf8(&self.buffer[offset..offset + len]) + .map_err(|_| ZError::INVALID_ARGUMENT) + } +} diff --git a/rust/lib/yunq/src/client.rs b/rust/lib/yunq/src/client.rs new file mode 100644 index 0000000..c4c45b8 --- /dev/null +++ b/rust/lib/yunq/src/client.rs @@ -0,0 +1,38 @@ +use crate::buffer::ByteBuffer; +use crate::message::YunqMessage; +use alloc::vec::Vec; +use mammoth::cap::Capability; +use mammoth::zion::ZError; + +pub fn call_endpoint( + request_id: u64, + req: &Req, + byte_buffer: &mut ByteBuffer, + endpoint_cap: &Capability, +) -> Result { + let mut cap_buffer = Vec::new(); + let length = req.serialize_as_request(request_id, byte_buffer, &mut cap_buffer)?; + + let reply_port_cap = mammoth::syscall::endpoint_send( + endpoint_cap, + byte_buffer.slice(16 + length), + cap_buffer.as_slice(), + )?; + + // FIXME: Add a way to zero out the byte buffer. + + cap_buffer = vec![0; 10]; + mammoth::syscall::reply_port_recv( + reply_port_cap, + byte_buffer.mut_slice(), + cap_buffer.as_mut_slice(), + )?; + + let resp_code: u64 = byte_buffer.at(8)?; + + if resp_code != 0 { + return Err(ZError::from(resp_code)); + } + + Resp::parse_from_request(byte_buffer, &cap_buffer) +} diff --git a/rust/lib/yunq/src/lib.rs b/rust/lib/yunq/src/lib.rs new file mode 100644 index 0000000..93166ac --- /dev/null +++ b/rust/lib/yunq/src/lib.rs @@ -0,0 +1,12 @@ +#![no_std] + +#[macro_use] +extern crate alloc; + +mod buffer; +pub mod client; +pub mod message; +pub mod server; + +pub use buffer::ByteBuffer; +pub use message::YunqMessage; diff --git a/rust/lib/yunq/src/message.rs b/rust/lib/yunq/src/message.rs new file mode 100644 index 0000000..568916f --- /dev/null +++ b/rust/lib/yunq/src/message.rs @@ -0,0 +1,144 @@ +use crate::buffer::ByteBuffer; +use alloc::vec::Vec; +use mammoth::zion::z_cap_t; +use mammoth::zion::ZError; + +pub const MESSAGE_IDENT: u32 = 0x33441122; +pub const MESSAGE_HEADER_SIZE: usize = 24; // 4x uint32, 1x uint64 +const SENTINEL: u32 = 0xBEEFDEAD; +const SERIALIZE_HEADER_SIZE: u32 = 0x10; + +pub fn field_offset(offset: usize, field_index: usize) -> usize { + offset + MESSAGE_HEADER_SIZE + (8 * field_index) +} + +pub fn parse_repeated( + buf: &ByteBuffer, + offset: usize, + len: usize, +) -> Result, ZError> { + let mut repeated = Vec::new(); + for i in 0..len { + repeated.push(buf.at::(offset + (i * size_of::()))?); + } + Ok(repeated) +} + +pub fn parse_repeated_message( + buf: &ByteBuffer, + mut offset: usize, + len: usize, + caps: &[z_cap_t], +) -> Result, ZError> { + let mut repeated = Vec::new(); + for _ in 0..len { + // FIXME: This is a bad way to get the length. + let msg_len = buf.at::(offset + 8)? as usize; + repeated.push(T::parse(buf, offset, caps)?); + offset += msg_len; + } + Ok(repeated) +} + +pub fn serialize_repeated( + buf: &mut ByteBuffer, + offset: usize, + data: &[T], +) -> Result { + for (i, val) in data.iter().enumerate() { + buf.write_at(offset + (i * size_of::()), val)?; + } + Ok(offset + size_of_val(data)) +} + +pub fn serialize_repeated_message( + buf: &mut ByteBuffer, + mut offset: usize, + data: &[T], + caps: &mut Vec, +) -> Result { + for item in data { + offset += item.serialize(buf, offset, caps)?; + } + Ok(offset) +} + +pub fn serialize_error(buf: &mut ByteBuffer, err: ZError) { + buf.write_at(0, SENTINEL) + .expect("Failed to serialize SENTINEL"); + buf.write_at(4, SERIALIZE_HEADER_SIZE) + .expect("Failed to serialize size"); + buf.write_at(8, err as u64) + .expect("Failed to serialize error"); +} + +pub trait YunqMessage { + fn parse( + buf: &ByteBuffer, + offset: usize, + caps: &[z_cap_t], + ) -> Result + where + Self: Sized; + + fn parse_from_request( + buf: &ByteBuffer, + caps: &[z_cap_t], + ) -> Result + where + Self: Sized, + { + if buf.at::(0)? != SENTINEL { + return Err(ZError::INVALID_RESPONSE); + } + + Self::parse(buf, 16, caps) + } + + fn serialize( + &self, + buf: &mut ByteBuffer, + offset: usize, + caps: &mut Vec, + ) -> Result; + + fn serialize_as_request( + &self, + request_id: u64, + buf: &mut ByteBuffer, + caps: &mut Vec, + ) -> Result { + buf.write_at(0, SENTINEL)?; + buf.write_at(8, request_id)?; + + let length = self.serialize(buf, 16, caps)?; + + buf.write_at(4, (16 + length) as u32)?; + + Ok(length + 16) + } +} + +pub struct Empty {} + +impl YunqMessage for Empty { + fn parse( + _buf: &ByteBuffer, + _offset: usize, + _caps: &[z_cap_t], + ) -> Result + where + Self: Sized, + { + Ok(Self {}) + } + + fn serialize( + &self, + _buf: &mut ByteBuffer, + _offset: usize, + _caps: &mut Vec, + ) -> Result { + Ok(0) + } +} diff --git a/rust/lib/yunq/src/server.rs b/rust/lib/yunq/src/server.rs new file mode 100644 index 0000000..cdfe418 --- /dev/null +++ b/rust/lib/yunq/src/server.rs @@ -0,0 +1,144 @@ +use core::future::Future; + +use crate::buffer::ByteBuffer; +use alloc::sync::Arc; +use alloc::vec::Vec; +use mammoth::cap::Capability; +use mammoth::syscall; +use mammoth::task::Spawner; +use mammoth::task::Task; +use mammoth::thread; +use mammoth::thread::JoinHandle; +use mammoth::zion::z_cap_t; +use mammoth::zion::ZError; + +pub trait YunqServer { + fn server_loop(&mut self) { + loop { + let mut byte_buffer = ByteBuffer::<1024>::new(); + let mut cap_buffer = vec![0; 10]; + let (_, _, reply_port_cap) = syscall::endpoint_recv( + self.endpoint_cap(), + byte_buffer.mut_slice(), + &mut cap_buffer, + ) + .expect("Failed to call endpoint recv"); + + let method = byte_buffer + .at::(8) + .expect("Failed to access request length."); + let resp = self.handle_request(method, &mut byte_buffer, &mut cap_buffer); + match resp { + Ok(resp_len) => syscall::reply_port_send( + reply_port_cap, + byte_buffer.slice(resp_len), + &cap_buffer, + ) + .expect("Failed to reply"), + Err(err) => { + crate::message::serialize_error(&mut byte_buffer, err); + syscall::reply_port_send(reply_port_cap, byte_buffer.slice(0x10), &[]) + .expect("Failed to reply w/ error") + } + } + } + } + + fn endpoint_cap(&self) -> &Capability; + + fn create_client_cap(&self) -> Result { + self.endpoint_cap() + .duplicate(!mammoth::zion::kZionPerm_Read) + } + fn handle_request( + &mut self, + method_number: u64, + byte_buffer: &mut ByteBuffer<1024>, + cap_buffer: &mut Vec, + ) -> Result; +} + +pub fn spawn_server_thread(mut server: T) -> JoinHandle +where + T: YunqServer + Send + 'static, +{ + thread::spawn(move || server.server_loop()) +} + +pub trait AsyncYunqServer +where + Self: Send + Sync + 'static, +{ + fn server_loop(self: Arc, spawner: Spawner) { + loop { + let mut byte_buffer = ByteBuffer::<1024>::new(); + let mut cap_buffer = vec![0; 10]; + let (_, _, reply_port_cap) = syscall::endpoint_recv( + self.endpoint_cap(), + byte_buffer.mut_slice(), + &mut cap_buffer, + ) + .expect("Failed to call endpoint recv"); + + let method = byte_buffer + .at::(8) + .expect("Failed to access request length."); + let self_clone = self.clone(); + spawner.spawn(Task::new(async move { + self_clone + .handle_request_and_response(method, byte_buffer, cap_buffer, reply_port_cap) + .await + })); + } + } + + fn handle_request_and_response( + &self, + method: u64, + mut byte_buffer: ByteBuffer<1024>, + mut cap_buffer: Vec, + reply_port_cap: Capability, + ) -> impl Future + Sync + Send { + async move { + let resp = self + .handle_request(method, &mut byte_buffer, &mut cap_buffer) + .await; + + match resp { + Ok(resp_len) => syscall::reply_port_send( + reply_port_cap, + byte_buffer.slice(resp_len), + &cap_buffer, + ) + .expect("Failed to reply"), + Err(err) => { + crate::message::serialize_error(&mut byte_buffer, err); + syscall::reply_port_send(reply_port_cap, byte_buffer.slice(0x10), &[]) + .expect("Failed to reply w/ error") + } + } + } + } + + fn endpoint_cap(&self) -> &Capability; + + fn create_client_cap(&self) -> Result { + self.endpoint_cap() + .duplicate(!mammoth::zion::kZionPerm_Read) + } + fn handle_request( + &self, + method_number: u64, + byte_buffer: &mut ByteBuffer<1024>, + cap_buffer: &mut Vec, + ) -> impl Future> + Sync + Send; +} + +pub fn spawn_async_server_thread(server: Arc, spawner: Spawner) -> JoinHandle +where + T: AsyncYunqServer + Send + Sync + 'static, +{ + thread::spawn(move || { + server.server_loop(spawner); + }) +} diff --git a/rust/sys/denali/Cargo.toml b/rust/sys/denali/Cargo.toml new file mode 100644 index 0000000..ad19780 --- /dev/null +++ b/rust/sys/denali/Cargo.toml @@ -0,0 +1,17 @@ +[package] +name = "denali" +version = "0.1.0" +edition = "2021" + +[dependencies] +bitfield-struct = "0.8.0" +mammoth = { path = "../../lib/mammoth" } +pci = { path = "../../lib/pci" } +yunq = { path = "../../lib/yunq" } +yellowstone-yunq = { path = "../../lib/yellowstone" } + +[[bin]] +name = "denali" + +[build-dependencies] +yunqc = { path = "../../../yunq/rust" } diff --git a/rust/sys/denali/build.rs b/rust/sys/denali/build.rs new file mode 100644 index 0000000..a57b29c --- /dev/null +++ b/rust/sys/denali/build.rs @@ -0,0 +1,14 @@ +use std::fs; + +fn main() { + let input_file = "denali.yunq"; + + println!("cargo::rerun-if-changed={input_file}"); + + let input = fs::read_to_string(input_file).expect("Failed to read input file"); + + let code = yunqc::codegen(&input).expect("Failed to generate yunq code."); + + let out = std::env::var("OUT_DIR").unwrap() + "/yunq.rs"; + fs::write(out, code).expect("Failed to write generated code."); +} diff --git a/sys/denali/lib/denali/denali.yunq b/rust/sys/denali/denali.yunq similarity index 72% rename from sys/denali/lib/denali/denali.yunq rename to rust/sys/denali/denali.yunq index 340861b..8348968 100644 --- a/sys/denali/lib/denali/denali.yunq +++ b/rust/sys/denali/denali.yunq @@ -3,18 +3,20 @@ interface Denali { method ReadMany(ReadManyRequest) -> (ReadResponse); } -message ReadRequest { - u64 device_id; +message DiskBlock { u64 lba; u64 size; } +message ReadRequest { + u64 device_id; + DiskBlock block; +} + + message ReadManyRequest { u64 device_id; - // FIXME: Add repeated message fields. - // Must be the same length. - repeated u64 lba; - repeated u64 sector_cnt; + repeated DiskBlock blocks; } message ReadResponse { @@ -23,3 +25,4 @@ message ReadResponse { capability memory; } + diff --git a/rust/sys/denali/src/ahci/ahci_command.rs b/rust/sys/denali/src/ahci/ahci_command.rs new file mode 100644 index 0000000..5870587 --- /dev/null +++ b/rust/sys/denali/src/ahci/ahci_command.rs @@ -0,0 +1,264 @@ +#[derive(Debug)] +#[repr(C, packed)] +pub struct CommandHeader { + pub command: u16, + pub prd_table_length: u16, + pub prd_byte_count: u32, + pub command_table_base_addr: u64, + __: u64, + ___: u64, +} + +pub type CommandList = [CommandHeader; 32]; + +#[allow(dead_code)] +#[derive(Clone, Copy, Debug, PartialEq)] +#[repr(u8)] +pub enum FisType { + RegisterHostToDevice = 0x27, // Register FIS - host to device + RegisterDeviceToHost = 0x34, // Register FIS - device to host + DmaActivate = 0x39, // DMA activate FIS - device to host + DmaSetup = 0x41, // DMA setup FIS - bidirectional + Data = 0x46, // Data FIS - bidirectional + BistActivate = 0x58, // BIST activate FIS - bidirectional + PioSetup = 0x5F, // PIO setup FIS - device to host + SetDeviceBits = 0xA1, // Set device bits FIS - device to host + Unknown = 0x0, +} + +#[allow(dead_code)] +#[derive(Copy, Clone, Debug)] +#[repr(C, packed)] +pub struct DmaFis { + // DWORD 0 + fis_type: u8, // FIS_TYPE_DMA_SETUP + pmport_dia: u8, // Port multiplier + + __: u16, // Reserved + + // DWORD 1&2 + dma_buffer_id: u64, // DMA Buffer Identifier. Used to Identify DMA buffer + // in host memory. SATA Spec says host specific and not + // in Spec. Trying AHCI spec might work. + + // DWORD 3 + ___: u32, + + // DWORD 4 + dma_buffer_offest: u32, // Byte offset into buffer. First 2 bits must be 0 + + // DWORD 5 + transfer_count: u32, // Number of bytes to transfer. Bit 0 must be 0 + + // DWORD 6 + ____: u32, // Reserved +} + +const _: () = assert!(size_of::() == 28); + +#[allow(dead_code)] +#[derive(Copy, Clone, Debug)] +#[repr(C, packed)] +pub struct PioSetupFis { + // DWORD 0 + fis_type: FisType, // FIS_TYPE_PIO_SETUP + + pmport_di: u8, // Port multiplier + + status: u8, // Status register + error: u8, // Error register + + // DWORD 1 + lba0: u8, // LBA low register, 7:0 + lba1: u8, // LBA mid register, 15:8 + lba2: u8, // LBA high register, 23:16 + device: u8, // Device register + + // DWORD 2 + lba3: u8, // LBA register, 31:24 + lba4: u8, // LBA register, 39:32 + lba5: u8, // LBA register, 47:40 + __: u8, // Reserved + + // DWORD 3 + countl: u8, // Count register, 7:0 + counth: u8, // Count register, 15:8 + ___: u8, // Reserved + e_status: u8, // New value of status register + + // DWORD 4 + tc: u16, // Transfer count + ____: u16, // Reserved +} + +const _: () = assert!(size_of::() == 20); + +#[derive(Clone, Copy, Debug)] +#[repr(u8)] +pub enum SataCommand { + IdentifyDevice = 0xEC, + DmaReadExt = 0x25, +} + +#[allow(dead_code)] // Read by memory. +#[derive(Clone, Copy, Debug)] +#[repr(C)] +pub struct HostToDeviceRegisterFis { + fis_type: FisType, // FIS_TYPE_REG_H2D + pmp_and_c: u8, + command: SataCommand, // Command register + featurel: u8, // Feature register, 7:0 + + // DWORD 1 + lba0: u8, // LBA low register, 7:0 + lba1: u8, // LBA mid register, 15:8 + lba2: u8, // LBA high register, 23:16 + device: u8, // Device register + + // DWORD 2 + lba3: u8, // LBA register, 31:24 + lba4: u8, // LBA register, 39:32 + lba5: u8, // LBA register, 47:40 + featureh: u8, // Feature register, 15:8 + + // DWORD 3 + count: u16, + icc: u8, // Isochronous command completion + control: u8, // Control register + + // DWORD 4 + reserved: u32, // Reserved +} + +const _: () = assert!(size_of::() == 20); + +impl HostToDeviceRegisterFis { + pub fn new_command(command: SataCommand, lba: u64, sectors: u16) -> Self { + Self { + fis_type: FisType::RegisterHostToDevice, + pmp_and_c: 0x80, // Set command bit + command, + featurel: 0, + + lba0: (lba & 0xFF) as u8, + lba1: ((lba >> 8) & 0xFF) as u8, + lba2: ((lba >> 16) & 0xFF) as u8, + device: (1 << 6), // ATA LBA Mode + + lba3: ((lba >> 24) & 0xFF) as u8, + lba4: ((lba >> 32) & 0xFF) as u8, + lba5: ((lba >> 40) & 0xFF) as u8, + featureh: 0, + + count: sectors, + icc: 0, + control: 0, + + reserved: 0, + } + } +} + +#[derive(Copy, Clone, Debug)] +#[repr(C, packed)] +pub struct DeviceToHostRegisterFis { + // DWORD 0 + pub fis_type: FisType, // FIS_TYPE_REG_D2H + + pub pmport_and_i: u8, + + pub status: u8, // Status register + pub error: u8, // Error register + + // DWORD 1 + pub lba0: u8, // LBA low register, 7:0 + pub lba1: u8, // LBA mid register, 15:8 + pub lba2: u8, // LBA high register, 23:16 + pub device: u8, // Device register + + // DWORD 2 + pub lba3: u8, // LBA register, 31:24 + pub lba4: u8, // LBA register, 39:32 + pub lba5: u8, // LBA register, 47:40 + __: u8, + + // DWORD 3 + pub count: u16, + ___: u16, + + ____: u32, +} + +const _: () = assert!(size_of::() == 20); + +#[derive(Copy, Clone, Debug)] +#[repr(C, packed)] +pub struct SetDeviceBitsFis { + fis_type: FisType, + pmport_and_i: u8, + status: u8, + error: u8, + reserved: u32, +} + +const _: () = assert!(size_of::() == 8); + +#[derive(Debug)] +#[repr(C, packed)] +pub struct ReceivedFis { + pub dma_fis: DmaFis, + __: u32, + + pub pio_set_fis: PioSetupFis, + ___: [u8; 12], + + pub device_to_host_register_fis: DeviceToHostRegisterFis, + ____: u32, + + pub set_device_bits_fis: SetDeviceBitsFis, + + pub unknown_fis: [u8; 64], +} + +const _: () = assert!(size_of::() == 0xA0); + +#[derive(Copy, Clone)] +#[repr(C)] +pub union CommandFis { + pub host_to_device: HostToDeviceRegisterFis, + + // Used to ensure the repr is + pub __: [u8; 64], +} + +const _: () = assert!(size_of::() == 0x40); + +impl core::fmt::Debug for CommandFis { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + f.write_fmt(format_args!("{:?}", unsafe { self.host_to_device })) + } +} + +#[derive(Copy, Clone, Debug)] +#[repr(C, packed)] +pub struct PhysicalRegionDescriptor { + pub region_address: u64, + __: u32, + // bit 0 must be one. + // 21:0 is byte count + // 31 is Interrupt on Completion + pub byte_count: u32, +} + +const _: () = assert!(size_of::() == 0x10); + +#[derive(Debug)] +#[repr(C, packed)] +pub struct CommandTable { + pub command_fis: CommandFis, + pub atapi_command: [u8; 0x10], + __: [u8; 0x30], + pub prdt: [PhysicalRegionDescriptor; 8], +} + +const _: () = assert!(size_of::() == 0x100); diff --git a/rust/sys/denali/src/ahci/command.rs b/rust/sys/denali/src/ahci/command.rs new file mode 100644 index 0000000..c827bb1 --- /dev/null +++ b/rust/sys/denali/src/ahci/command.rs @@ -0,0 +1,112 @@ +use core::{ + future::Future, + ops::Deref, + ops::DerefMut, + pin::Pin, + task::{Context, Poll, Waker}, +}; + +use alloc::boxed::Box; +use alloc::sync::Arc; +use mammoth::{cap::Capability, sync::Mutex, syscall, zion::ZError}; + +use super::ahci_command::{HostToDeviceRegisterFis, SataCommand}; + +pub enum CommandStatus { + Empty, + NotSent, + Pending(Waker), + Complete, +} + +pub struct CommandFuture { + status: Arc>, + trigger: Box, +} + +impl CommandFuture { + pub fn new(status: Arc>, trigger: Box) -> Self { + Self { status, trigger } + } +} + +impl Drop for CommandFuture { + fn drop(&mut self) { + *self.status.lock() = CommandStatus::Empty; + } +} + +impl Future for CommandFuture { + type Output = (); + + fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + let s = self.deref_mut(); + let mut status = s.status.lock(); + match status.deref() { + CommandStatus::NotSent => { + *status = CommandStatus::Pending(cx.waker().clone()); + (s.trigger)(); + Poll::Pending + } + CommandStatus::Pending(_) => Poll::Pending, + CommandStatus::Complete => Poll::Ready(()), + CommandStatus::Empty => panic!("Polling empty command slot"), + } + } +} + +pub struct Command { + pub command: SataCommand, + pub lba: u64, + pub sector_cnt: u16, + pub paddr: u64, + + memory_region: Option, +} + +impl Command { + pub fn identify() -> Result { + let (memory_region, paddr) = syscall::memory_object_contiguous_physical(512)?; + + Ok(Self { + command: SataCommand::IdentifyDevice, + lba: 0, + sector_cnt: 1, + paddr, + memory_region: Some(memory_region), + }) + } + + pub fn read(lba: u64, lba_count: u16) -> Result { + let (memory_region, paddr) = + syscall::memory_object_contiguous_physical(512 * (lba_count as u64))?; + + Ok(Self { + command: SataCommand::DmaReadExt, + lba, + sector_cnt: lba_count, + paddr, + memory_region: Some(memory_region), + }) + } + + pub fn read_manual(lba: u64, lba_count: u16, paddr: u64) -> Self { + Self { + command: SataCommand::DmaReadExt, + lba, + sector_cnt: lba_count, + paddr, + memory_region: None, + } + } + + pub fn release_mem_cap(&mut self) -> u64 { + self.memory_region.take().unwrap().release() + } +} + +impl From<&Command> for HostToDeviceRegisterFis { + fn from(val: &Command) -> Self { + HostToDeviceRegisterFis::new_command(val.command, val.lba, val.sector_cnt) + } +} diff --git a/rust/sys/denali/src/ahci/controller.rs b/rust/sys/denali/src/ahci/controller.rs new file mode 100644 index 0000000..2a402f0 --- /dev/null +++ b/rust/sys/denali/src/ahci/controller.rs @@ -0,0 +1,166 @@ +use alloc::sync::Arc; +use mammoth::{ + cap::Capability, + mem::{self, MemoryRegion}, + sync::Mutex, + thread, + zion::ZError, +}; +use pci::PciDevice; + +use crate::ahci::{ + port::{AhciDeviceDetection, AhciInterfacePowerManagement}, + Command, +}; + +use super::{hba::AhciHba, port::AhciPortHba, port_controller::PortController}; + +pub struct AhciController { + pci_device: Mutex, + hba: Mutex<&'static mut AhciHba>, + ports: [Option; 32], + hba_vaddr: u64, +} + +impl AhciController { + pub fn new(pci_memory: Capability) -> Self { + let pci_device = PciDevice::from_cap(pci_memory).unwrap(); + + let hba_vaddr = + mem::map_direct_physical_and_leak(pci_device.header().bars[5] as u64, 0x1100); + let hba = unsafe { (hba_vaddr as *mut AhciHba).as_mut().unwrap() }; + let mut controller = Self { + pci_device: Mutex::new(pci_device), + hba: Mutex::new(hba), + ports: [const { None }; 32], + hba_vaddr, + }; + controller.init(); + controller + } + + fn init(&mut self) { + self.hba.lock().init(); + let hba = self.hba.lock(); + for i in 0..(hba.capabilities.read().num_ports() as usize) { + let port_index = 1 << i; + if (hba.port_implemented.read() & port_index) != port_index { + mammoth::debug!("Skipping port {}, not implemented", i); + continue; + } + + let port_offset: usize = 0x100 + (0x80 * i); + let port = unsafe { + ((self.hba_vaddr as usize + port_offset) as *mut AhciPortHba) + .as_mut() + .unwrap() + }; + + let sata_status = port.sata_status.read(); + if (sata_status.device_detection() != AhciDeviceDetection::CommunicationEstablished) + || (sata_status.interface_power_management() + != AhciInterfacePowerManagement::Active) + { + mammoth::debug!( + "Skipping port {}, no communcation. Status: {:?}", + i, + sata_status + ); + continue; + } + + self.ports[i] = Some(PortController::new(port)); + } + } + + fn register_irq(&self) -> Result { + self.pci_device.lock().register_msi() + } + + fn handle_irq(&self) { + let mut hba = self.hba.lock(); + for i in 0..hba.capabilities.read().num_ports() { + let int_offset = 1 << i; + if (hba.interrupt_status.read() & int_offset) == int_offset { + if let Some(port) = &self.ports[i as usize] { + port.handle_interrupt(); + hba.interrupt_status.update(|is| { + *is &= !int_offset; + }); + } + } + } + } + + pub async fn identify_ports(&self) -> Result<(), ZError> { + for port in self.ports.iter().flatten() { + let sig = port.get_signature(); + if sig == 0x101 { + let mut command = Command::identify()?; + mammoth::debug!("IDENT!"); + port.issue_command(&command)?.await; + let memory_region = + MemoryRegion::from_cap(Capability::take(command.release_mem_cap()))?; + let ident = memory_region.slice::(); + let new_sector_size = if ident[106] & (1 << 12) != 0 { + ident[117] as u32 | ((ident[118] as u32) << 16) + } else { + 512 + }; + + let lba_count = if ident[83] & (1 << 10) != 0 { + ident[100] as u64 + | (ident[101] as u64) << 16 + | (ident[102] as u64) << 32 + | (ident[103] as u64) << 48 + } else { + ident[60] as u64 | (ident[61] as u64) << 16 + }; + mammoth::debug!("Sector size: {:#0x}", new_sector_size); + mammoth::debug!("LBA Count: {:#0x}", lba_count); + + // We hardcode assumptions about sector size in a few places, better to panic if we + // are wrong. + assert_eq!( + new_sector_size, 512, + "Sector size {} differs from 512.", + new_sector_size + ); + } else { + mammoth::debug!("Skipping non-sata sig: {:#0x}", sig); + } + } + Ok(()) + } + + pub async fn issue_command(&self, port_num: usize, command: &Command) -> Result<(), ZError> { + assert!(port_num < 32); + + self.ports[port_num] + .as_ref() + .ok_or(ZError::INVALID_ARGUMENT)? + .issue_command(command)? + .await; + + Ok(()) + } +} + +pub fn spawn_irq_thread(controller: Arc) -> thread::JoinHandle { + let irq_thread = move || { + let irq_port_cap = controller.register_irq().unwrap(); + let irq_port = mammoth::port::PortServer::from_cap(irq_port_cap); + controller.hba.lock().global_host_control.update(|ghc| { + ghc.set_interrupt_enable(true); + }); + loop { + irq_port.recv_null().unwrap(); + controller.handle_irq(); + } + }; + thread::spawn(irq_thread) +} + +pub async fn identify_ports(controller: Arc) { + controller.identify_ports().await.unwrap(); +} diff --git a/rust/sys/denali/src/ahci/hba.rs b/rust/sys/denali/src/ahci/hba.rs new file mode 100644 index 0000000..2cfdc63 --- /dev/null +++ b/rust/sys/denali/src/ahci/hba.rs @@ -0,0 +1,181 @@ +use bitfield_struct::bitfield; +use mammoth::mem::Volatile; + +const fn increment(val: u8) -> u8 { + val + 1 +} + +#[derive(Debug, PartialEq, Eq)] +#[repr(u8)] +enum InterfaceSpeedSupport { + Reserved = 0b0000, + // 1.5 Gbps + Gen1 = 0b0001, + // 3 Gbps + Gen2 = 0b0010, + // 6 Gbps + Gen3 = 0b0011, + Unknown = 0b1111, +} + +impl InterfaceSpeedSupport { + const fn from_bits(value: u8) -> Self { + match value { + 0b0000 => Self::Reserved, + 0b0001 => Self::Gen1, + 0b0010 => Self::Gen2, + 0b0011 => Self::Gen3, + _ => Self::Unknown, + } + } +} + +#[bitfield(u32)] +pub struct AhciCapabilities { + #[bits(5, access = RO, from = increment)] + pub num_ports: u8, + + #[bits(access = RO)] + supports_external_sata: bool, + + #[bits(access = RO)] + enclosure_management_supported: bool, + + #[bits(access = RO)] + command_completed_coalescing_supported: bool, + + #[bits(5, access = RO, from = increment)] + num_commands: u8, + + #[bits(access = RO)] + partial_state_capable: bool, + + #[bits(access = RO)] + slumber_state_capable: bool, + + #[bits(access = RO)] + pio_multiple_drq_block: bool, + + #[bits(access = RO)] + fis_based_switching_supported: bool, + + #[bits(access = RO)] + supports_port_multiplier: bool, + + #[bits(access = RO)] + supports_ahci_mode_only: bool, + + __: bool, + + #[bits(4, access = RO)] + interface_speed_support: InterfaceSpeedSupport, + + #[bits(access = RO)] + supports_command_list_override: bool, + + #[bits(access = RO)] + supports_activity_led: bool, + + #[bits(access = RO)] + supports_aggressive_link_power_management: bool, + + #[bits(access = RO)] + supports_staggered_spin_up: bool, + + #[bits(access = RO)] + supports_mechanical_presence_switch: bool, + + #[bits(access = RO)] + supports_snotification_register: bool, + + #[bits(access = RO)] + supports_native_command_queueing: bool, + + #[bits(access = RO)] + supports_64_bit_addressing: bool, +} + +#[bitfield(u32)] +pub struct AhciGlobalControl { + pub hba_reset: bool, + pub interrupt_enable: bool, + + #[bits(access = RO)] + pub msi_revert_to_single_message: bool, + + #[bits(28)] + __: u32, + + pub ahci_enable: bool, +} + +#[bitfield(u32)] +pub struct AhciCapabilitiesExtended { + #[bits(access = RO)] + bios_os_handoff: bool, + + #[bits(access = RO)] + nvmhci_present: bool, + + #[bits(access = RO)] + automatic_partial_to_slumber_transitions: bool, + + #[bits(access = RO)] + supports_device_sleep: bool, + + #[bits(access = RO)] + supports_aggressive_device_sleep_management: bool, + + #[bits(27)] + __: u32, +} + +#[bitfield(u32)] +pub struct AhciBiosHandoffControl { + bios_owned_semaphore: bool, + os_owned_semaphore: bool, + smi_on_os_ownership_change_enable: bool, + os_ownership_change: bool, + bios_busy: bool, + + #[bits(27)] + __: u32, +} + +#[derive(Debug)] +#[repr(C)] +pub struct AhciHba { + pub capabilities: Volatile, + pub global_host_control: Volatile, + pub interrupt_status: Volatile, + pub port_implemented: Volatile, + pub version: Volatile, + pub ccc_ctl: Volatile, // 0x14, Command completion coalescing control + pub ccc_pts: Volatile, // 0x18, Command completion coalescing ports + pub em_loc: Volatile, // 0x1C, Enclosure management location + pub em_ctl: Volatile, // 0x20, Enclosure management control + pub capabilities_ext: Volatile, + pub bohc: Volatile, +} + +impl AhciHba { + pub fn init(&mut self) { + self.global_host_control.update(|ghc| { + ghc.set_hba_reset(true); + }); + + mammoth::syscall::thread_sleep(50).unwrap(); + + loop { + if !self.global_host_control.read().hba_reset() { + break; + } + } + + self.global_host_control.update(|ghc| { + ghc.set_ahci_enable(true); + }); + + mammoth::syscall::thread_sleep(50).unwrap(); + } +} diff --git a/rust/sys/denali/src/ahci/mod.rs b/rust/sys/denali/src/ahci/mod.rs new file mode 100644 index 0000000..f98bcf2 --- /dev/null +++ b/rust/sys/denali/src/ahci/mod.rs @@ -0,0 +1,11 @@ +mod ahci_command; +mod command; +mod controller; +mod hba; +mod port; +mod port_controller; + +pub use command::Command; +pub use controller::identify_ports; +pub use controller::spawn_irq_thread; +pub use controller::AhciController; diff --git a/rust/sys/denali/src/ahci/port.rs b/rust/sys/denali/src/ahci/port.rs new file mode 100644 index 0000000..e32abc5 --- /dev/null +++ b/rust/sys/denali/src/ahci/port.rs @@ -0,0 +1,435 @@ +use bitfield_struct::bitfield; +use mammoth::mem::Volatile; + +#[bitfield(u32)] +pub struct AhciPortInterruptStatus { + pub device_to_host_register_fis_interrupt: bool, + pub pio_setup_fis_interrupt: bool, + pub dma_setup_fis_interrupt: bool, + pub set_device_bits_interrupt: bool, + #[bits(access = RO)] + pub unknown_fis_interrupt: bool, + pub descriptor_prossed: bool, + #[bits(access = RO)] + pub port_connect_change_status: bool, + pub device_mechanical_presence_status: bool, + + #[bits(14)] + __: u32, + + #[bits(access = RO)] + pub phy_rdy_change_status: bool, + pub incorrect_port_multiplier_status: bool, + pub overflow_status: bool, + + __: bool, + pub interface_non_fatal_error_status: bool, + pub interface_fatal_error_status: bool, + pub host_bus_data_error_status: bool, + pub host_bus_fatal_error_status: bool, + pub task_file_error_status: bool, + pub cold_port_detect_status: bool, +} + +#[bitfield(u32)] +pub struct AhciPortInterruptEnable { + pub device_to_host_register_fis_enable: bool, + pub pio_setup_fis_enable: bool, + pub dma_setup_fis_enable: bool, + pub set_device_bits_fis_enable: bool, + pub unknown_fis_enable: bool, + pub descriptor_processed_enable: bool, + pub port_change_enable: bool, + pub device_mechanical_presence_enable: bool, + + #[bits(14)] + __: u32, + + pub phy_rdy_change_enable: bool, + pub incorrect_port_multiplier_enable: bool, + pub overflow_enable: bool, + + __: bool, + + pub interface_non_fatal_error_enable: bool, + pub interface_fatal_error_enable: bool, + pub host_bus_data_error_enable: bool, + pub host_bust_fatal_error_enable: bool, + pub task_file_error_enable: bool, + pub cold_presence_detect_enable: bool, +} + +#[repr(u8)] +#[derive(Debug)] +enum InterfaceCommunicationControl { + NoOpOrIdle = 0x0, + Active = 0x1, + Partial = 0x2, + Slumber = 0x6, + DevSleep = 0x8, + Unknown = 0xF, +} + +impl InterfaceCommunicationControl { + const fn from_bits(value: u8) -> Self { + match value { + 0x0 => Self::NoOpOrIdle, + 0x1 => Self::Active, + 0x2 => Self::Partial, + 0x6 => Self::Slumber, + 0x8 => Self::DevSleep, + _ => Self::Unknown, + } + } + + const fn into_bits(self) -> u8 { + self as _ + } +} + +#[bitfield(u32)] +pub struct AhciPortCommandAndStatus { + pub start: bool, + pub spin_up_device: bool, + pub power_on_device: bool, + pub command_list_overide: bool, + pub fis_recieve_enable: bool, + + #[bits(3)] + __: u8, + + #[bits(5, access = RO)] + pub current_command_slot: u8, + + #[bits(access = RO)] + pub mechanical_presence_switch_state: bool, + #[bits(access = RO)] + pub fis_receive_running: bool, + #[bits(access = RO)] + pub command_list_running: bool, + #[bits(access = RO)] + pub cold_presence_state: bool, + pub port_multipler_attached: bool, + #[bits(access = RO)] + pub hot_plug_capable_port: bool, + #[bits(access = RO)] + pub mechanical_presence_switch_attached_to_port: bool, + #[bits(access = RO)] + pub cold_presence_detection: bool, + #[bits(access = RO)] + pub external_sata_port: bool, + #[bits(access = RO)] + pub fis_base_switch_capable: bool, + pub automatic_partial_to_slumber_transitions_enable: bool, + pub device_is_atapi: bool, + pub drive_led_on_atapi_enable: bool, + pub aggressive_power_link_management_enable: bool, + pub aggressive_slumber_partial: bool, + + #[bits(4)] + pub interface_communication_control: InterfaceCommunicationControl, +} + +#[bitfield(u32)] +pub struct AhciPortTaskFileData { + #[bits(access = RO)] + err_status: bool, + #[bits(2, access = RO)] + command_specific_status_lo: u8, + #[bits(access = RO)] + data_transfer_requested: bool, + #[bits(3, access = RO)] + command_specific_status_hi: u8, + #[bits(access = RO)] + busy_status: bool, + + #[bits(8, access = RO)] + error: u8, + + #[bits(16)] + __: u16, +} + +#[derive(Copy, Clone, Debug, PartialEq)] +#[repr(u8)] +pub enum AhciDeviceDetection { + NoDevice = 0x0, + NoCommunication = 0x1, + CommunicationEstablished = 0x3, + OfflineMode = 0x4, + Unknown = 0xF, +} + +impl AhciDeviceDetection { + const fn from_bits(value: u8) -> Self { + match value { + 0x0 => Self::NoDevice, + 0x1 => Self::NoCommunication, + 0x3 => Self::CommunicationEstablished, + 0x4 => Self::OfflineMode, + _ => Self::Unknown, + } + } +} + +#[derive(Copy, Clone, Debug)] +#[repr(u8)] +pub enum AhciCurrentInterfaceSpeed { + NoDevice = 0x0, + Gen1 = 0x1, + Gen2 = 0x2, + Gen3 = 0x3, + Unknown = 0xF, +} + +impl AhciCurrentInterfaceSpeed { + const fn from_bits(value: u8) -> Self { + match value { + 0x0 => Self::NoDevice, + 0x1 => Self::Gen1, + 0x2 => Self::Gen2, + 0x3 => Self::Gen3, + _ => Self::Unknown, + } + } +} + +#[derive(Copy, Clone, Debug, PartialEq)] +#[repr(u8)] +pub enum AhciInterfacePowerManagement { + NoDevice = 0x0, + Active = 0x1, + PartialPower = 0x2, + Slumber = 0x6, + DevSleep = 0x8, + Unknown = 0xF, +} + +impl AhciInterfacePowerManagement { + const fn from_bits(value: u8) -> Self { + match value { + 0x0 => Self::NoDevice, + 0x1 => Self::Active, + 0x2 => Self::PartialPower, + 0x6 => Self::Slumber, + 0x8 => Self::DevSleep, + _ => Self::Unknown, + } + } +} + +#[bitfield(u32)] +#[derive(PartialEq)] +pub struct AhciSataStatus { + #[bits(4, access = RO)] + pub device_detection: AhciDeviceDetection, + + #[bits(4, access = RO)] + pub current_interface_speed: AhciCurrentInterfaceSpeed, + + #[bits(4, access = RO)] + pub interface_power_management: AhciInterfacePowerManagement, + + #[bits(20)] + __: u32, +} + +#[derive(Debug)] +#[repr(u8)] +enum AhciDeviceDetectionInitialization { + NoDevice = 0x0, + PerformInterfaceCommunicationInitializationSequence = 0x1, + DisableSata = 0x4, + Unknown = 0xF, +} + +impl AhciDeviceDetectionInitialization { + const fn into_bits(self) -> u8 { + self as _ + } + + const fn from_bits(value: u8) -> Self { + match value { + 0x0 => Self::NoDevice, + 0x1 => Self::PerformInterfaceCommunicationInitializationSequence, + 0x4 => Self::DisableSata, + _ => Self::Unknown, + } + } +} + +#[derive(Debug)] +#[repr(u8)] +enum AhciSpeedAllowed { + NoRestrictions = 0x0, + LimitGen1 = 0x1, + LimitGen2 = 0x2, + LimitGen3 = 0x3, + Unknown = 0xF, +} + +impl AhciSpeedAllowed { + const fn into_bits(self) -> u8 { + self as _ + } + + const fn from_bits(value: u8) -> Self { + match value { + 0x0 => Self::NoRestrictions, + 0x1 => Self::LimitGen1, + 0x2 => Self::LimitGen2, + 0x3 => Self::LimitGen3, + _ => Self::Unknown, + } + } +} + +#[bitfield(u32)] +pub struct AhciSataControl { + #[bits(4)] + device_detection_initialization: AhciDeviceDetectionInitialization, + + #[bits(4)] + speed_allowed: AhciSpeedAllowed, + + partial_transition_disabled: bool, + slumber_transition_disabled: bool, + devsleep_transition_disabled: bool, + + __: bool, + + #[bits(20)] + __: u32, +} + +#[bitfield(u32)] +pub struct AhciSataError { + pub recovered_data_integrity_error: bool, + pub recovered_communications_error: bool, + + #[bits(6)] + __: u8, + + pub transient_data_integrity_error: bool, + pub persisten_communication_or_data_integrity_error: bool, + pub protocol_error: bool, + pub internal_error: bool, + + #[bits(4)] + __: u8, + + pub phy_ready_change: bool, + pub phy_internal_error: bool, + pub comm_wake: bool, + pub decode_error: bool, + __: bool, + pub crc_error: bool, + pub handshake_error: bool, + pub link_sequence_error: bool, + pub transport_state_transition_error: bool, + pub uknown_fis_type: bool, + pub exchanged: bool, + + #[bits(5)] + __: u8, +} + +#[bitfield(u32)] +pub struct AhciFisBasedSwitchingControl { + enable: bool, + device_error_clear: bool, + + #[bits(access = RO)] + single_device_error: bool, + + #[bits(5)] + __: u8, + + #[bits(4)] + device_to_issue: u8, + + #[bits(4, access = RO)] + active_device_optimization: u8, + + #[bits(4, access = RO)] + device_with_error: u8, + + #[bits(12)] + __: u16, +} + +#[bitfield(u32)] +pub struct AhciDeviceSleep { + aggressive_device_sleep_enable: bool, + + #[bits(access = RO)] + device_sleep_present: bool, + + device_sleep_exit_timeout: u8, + + #[bits(5)] + minimum_device_sleep_assertion_time: u8, + + #[bits(10)] + device_sleep_idle_timeout: u16, + + #[bits(4)] + dito_multiplier: u8, + + #[bits(3)] + __: u8, +} + +#[derive(Debug)] +#[repr(C)] +pub struct AhciPortHba { + pub command_list_base: Volatile, + pub fis_base: Volatile, + pub interrupt_status: Volatile, + pub interrupt_enable: Volatile, + pub command: Volatile, + __: Volatile, + pub task_file_data: Volatile, + pub signature: Volatile, + pub sata_status: Volatile, + pub sata_control: Volatile, + pub sata_error: Volatile, + pub sata_active: Volatile, + pub command_issue: Volatile, + pub sata_notification: Volatile, + pub fis_based_switching_ctl: Volatile, + pub device_sleep: Volatile, +} + +const _: () = assert!(size_of::() == 0x48); + +impl AhciPortHba { + pub fn init(&mut self, command_list_phys: u64, fis_phys: u64) { + let sata_status = self.sata_status.read(); + assert_eq!( + sata_status.device_detection(), + AhciDeviceDetection::CommunicationEstablished + ); + assert_eq!( + sata_status.interface_power_management(), + AhciInterfacePowerManagement::Active, + ); + + self.command_list_base.write(command_list_phys); + self.fis_base.write(fis_phys); + + self.interrupt_enable + .write(AhciPortInterruptEnable::from_bits(0xFFFF_FFFF)); + // Overwrite all errors. + self.sata_error.write(AhciSataError::from_bits(0xFFFF_FFFF)); + + self.command.update(|command| { + *command = command.with_fis_recieve_enable(true).with_start(true); + }); + } + + pub fn issue_command(&mut self, slot: usize) { + assert!(slot < 32); + self.command_issue.update(|ci| *ci |= 1 << slot); + } +} diff --git a/rust/sys/denali/src/ahci/port_controller.rs b/rust/sys/denali/src/ahci/port_controller.rs new file mode 100644 index 0000000..42dfa00 --- /dev/null +++ b/rust/sys/denali/src/ahci/port_controller.rs @@ -0,0 +1,159 @@ +use core::array; +use core::ops::Deref; + +use alloc::{boxed::Box, sync::Arc}; +use mammoth::{mem, sync::Mutex, zion::ZError}; + +use crate::ahci::port::AhciPortInterruptStatus; + +use super::{ + ahci_command::{CommandList, CommandTable, FisType, HostToDeviceRegisterFis, ReceivedFis}, + command::{CommandFuture, CommandStatus}, + port::AhciPortHba, + Command, +}; + +struct CommandStructures { + command_list: &'static mut CommandList, + received_fis: &'static mut ReceivedFis, + command_tables: &'static mut [CommandTable; 32], +} + +pub struct PortController { + ahci_port_hba: Arc>, + command_structures: Mutex, + + command_slots: [Arc>; 32], +} + +impl PortController { + pub fn new(ahci_port_hba: &'static mut AhciPortHba) -> Self { + let (command_vaddr, command_paddr) = mem::map_physical_and_leak(0x2500); + ahci_port_hba.init(command_paddr, command_paddr + 0x400); + + let command_list = unsafe { (command_vaddr as *mut CommandList).as_mut().unwrap() }; + let received_fis = unsafe { + ((command_vaddr + 0x400) as *mut ReceivedFis) + .as_mut() + .unwrap() + }; + let command_tables = unsafe { + ((command_vaddr + 0x500) as *mut [CommandTable; 32]) + .as_mut() + .unwrap() + }; + + // This leaves space for 8 prdt entries. + for (i, header) in command_list.iter_mut().enumerate() { + header.command_table_base_addr = (command_paddr + 0x500) + (0x100 * (i as u64)); + } + + let command_slots = array::from_fn(|_| Arc::new(Mutex::new(CommandStatus::Empty))); + + Self { + ahci_port_hba: Arc::new(Mutex::new(ahci_port_hba)), + command_structures: Mutex::new(CommandStructures { + command_list, + received_fis, + command_tables, + }), + command_slots, + } + } + + pub fn get_signature(&self) -> u32 { + self.ahci_port_hba.lock().signature.read() + } + + pub fn issue_command(&self, command: &Command) -> Result { + let slot = self.select_slot()?; + let command_slot = self.command_slots[slot].clone(); + + let ahci_port_hba_clone = self.ahci_port_hba.clone(); + let slot_clone = slot; + let future = CommandFuture::new( + command_slot, + Box::new(move || { + ahci_port_hba_clone.lock().issue_command(slot_clone); + }), + ); + + let mut command_structures = self.command_structures.lock(); + + command_structures.command_tables[slot] + .command_fis + .host_to_device = command.into(); + + command_structures.command_tables[slot].prdt[0].region_address = command.paddr; + command_structures.command_tables[slot].prdt[0].byte_count = + 512 * (command.sector_cnt as u32); + + command_structures.command_list[slot].prd_table_length = 1; + + command_structures.command_list[slot].command = + (size_of::() as u16 / 4) & 0x1F; + command_structures.command_list[slot].command |= 1 << 7; + + Ok(future) + } + + fn select_slot(&self) -> Result { + // TODO: We have a race condition here. + for i in 0..self.command_slots.len() { + let mut slot = self.command_slots[i].lock(); + if matches!(*slot, CommandStatus::Empty) { + *slot = CommandStatus::NotSent; + return Ok(i); + } + } + Err(ZError::EXHAUSTED) + } + + pub fn handle_interrupt(&self) { + let int_status = self.ahci_port_hba.lock().interrupt_status.read(); + if int_status.device_to_host_register_fis_interrupt() { + let received_fis = self + .command_structures + .lock() + .received_fis + .device_to_host_register_fis; + assert_eq!( + received_fis.fis_type as u8, + FisType::RegisterDeviceToHost as u8 + ); + if received_fis.error != 0 { + mammoth::debug!("D2H err: {:#0x}", received_fis.error); + mammoth::debug!("Status: {:#0x}", received_fis.status); + } + + self.ahci_port_hba.lock().interrupt_status.write( + AhciPortInterruptStatus::new().with_device_to_host_register_fis_interrupt(true), + ); + } + if int_status.pio_setup_fis_interrupt() { + self.ahci_port_hba + .lock() + .interrupt_status + .write(AhciPortInterruptStatus::new().with_pio_setup_fis_interrupt(true)); + } + + for i in 0..32 { + let int_offset = 1 << i; + + // If there is no longer a command issued on a slot and we have something in + // the command list that is pending we know that this is the command that finished. + if (self.ahci_port_hba.lock().command_issue.read() & int_offset) != int_offset { + let mut command_status = self.command_slots[i].lock(); + let mut did_complete = false; + if let CommandStatus::Pending(ref waker) = command_status.deref() { + waker.wake_by_ref(); + did_complete = true; + } + + if did_complete { + *command_status = CommandStatus::Complete; + } + } + } + } +} diff --git a/rust/sys/denali/src/bin/denali.rs b/rust/sys/denali/src/bin/denali.rs new file mode 100644 index 0000000..0e831d3 --- /dev/null +++ b/rust/sys/denali/src/bin/denali.rs @@ -0,0 +1,67 @@ +#![no_std] +#![no_main] + +extern crate alloc; + +use alloc::sync::Arc; +use mammoth::{ + define_entry, + sync::Mutex, + task::{Executor, Task}, + zion::z_err_t, +}; + +use denali::ahci::{identify_ports, spawn_irq_thread, AhciController}; +use denali::{denali_server::DenaliServerImpl, AsyncDenaliServer}; +use yellowstone_yunq::RegisterEndpointRequest; +use yunq::server::AsyncYunqServer; + +define_entry!(); + +#[no_mangle] +extern "C" fn main() -> z_err_t { + mammoth::debug!("IN Denali!"); + + let yellowstone = yellowstone_yunq::from_init_endpoint(); + + let ahci_info = yellowstone + .get_ahci_info() + .expect("Failed to get ahci info"); + + let ahci_controller = Arc::new(AhciController::new(mammoth::cap::Capability::take( + ahci_info.ahci_region, + ))); + + let executor = Arc::new(Mutex::new(Executor::new())); + + executor + .clone() + .lock() + .spawn(Task::new(identify_ports(ahci_controller.clone()))); + + let thread = spawn_irq_thread(ahci_controller.clone()); + + let denali_server = + Arc::new(AsyncDenaliServer::new(DenaliServerImpl::new(ahci_controller.clone())).unwrap()); + + let server_thread = yunq::server::spawn_async_server_thread( + denali_server.clone(), + executor.lock().new_spawner(), + ); + + let yellowstone = yellowstone_yunq::from_init_endpoint(); + yellowstone + .register_endpoint(&RegisterEndpointRequest { + endpoint_name: "denali".into(), + endpoint_capability: denali_server.create_client_cap().unwrap().release(), + }) + .unwrap(); + + executor.clone().lock().run(); + + thread.join().expect("Failed to wait on irq thread."); + server_thread + .join() + .expect("Failed to wait on server thread."); + 0 +} diff --git a/rust/sys/denali/src/denali_server.rs b/rust/sys/denali/src/denali_server.rs new file mode 100644 index 0000000..511ff0f --- /dev/null +++ b/rust/sys/denali/src/denali_server.rs @@ -0,0 +1,61 @@ +use alloc::sync::Arc; +use mammoth::{syscall, zion::ZError}; + +use crate::{ + ahci::{AhciController, Command}, + AsyncDenaliServerHandler, ReadManyRequest, ReadRequest, ReadResponse, +}; + +pub struct DenaliServerImpl { + ahci_controller: Arc, +} + +impl DenaliServerImpl { + pub fn new(controller: Arc) -> Self { + DenaliServerImpl { + ahci_controller: controller, + } + } +} + +impl AsyncDenaliServerHandler for DenaliServerImpl { + async fn read(&self, req: ReadRequest) -> Result { + let mut command = Command::read(req.block.lba, req.block.size as u16)?; + self.ahci_controller + .issue_command(req.device_id as usize, &command) + .await?; + + Ok(ReadResponse { + device_id: req.device_id, + size: req.block.size, + memory: command.release_mem_cap(), + }) + } + + async fn read_many(&self, req: ReadManyRequest) -> Result { + let total_sector_cnt = req.blocks.iter().map(|b| b.size).sum(); + + // FIXME: Don't hardcode this. + let sector_size = 512; + + let (mem_cap, mut paddr) = + syscall::memory_object_contiguous_physical(sector_size * total_sector_cnt)?; + + for block in req.blocks { + let command = Command::read_manual(block.lba, block.size as u16, paddr); + + // TODO: We should actually be able to read all of these in parallel. + self.ahci_controller + .issue_command(req.device_id as usize, &command) + .await?; + + paddr += block.size * sector_size; + } + + Ok(ReadResponse { + device_id: req.device_id, + size: total_sector_cnt, + memory: mem_cap.release(), + }) + } +} diff --git a/rust/sys/denali/src/lib.rs b/rust/sys/denali/src/lib.rs new file mode 100644 index 0000000..0d06e8b --- /dev/null +++ b/rust/sys/denali/src/lib.rs @@ -0,0 +1,8 @@ +#![no_std] + +use core::include; + +include!(concat!(env!("OUT_DIR"), "/yunq.rs")); + +pub mod ahci; +pub mod denali_server; diff --git a/rust/sys/teton/Cargo.toml b/rust/sys/teton/Cargo.toml new file mode 100644 index 0000000..baa787a --- /dev/null +++ b/rust/sys/teton/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "teton" +version = "0.1.0" +edition = "2021" + +[dependencies] +mammoth = { path = "../../lib/mammoth" } +victoriafalls = { path = "../victoriafalls" } +voyageurs = { path = "../../lib/voyageurs" } +yellowstone-yunq = { path = "../../lib/yellowstone" } diff --git a/rust/sys/teton/src/console.rs b/rust/sys/teton/src/console.rs new file mode 100644 index 0000000..2e282dc --- /dev/null +++ b/rust/sys/teton/src/console.rs @@ -0,0 +1,36 @@ +use crate::framebuffer::Framebuffer; +use crate::psf::Psf; + +pub struct Console { + framebuffer: Framebuffer, + psf: Psf, +} + +impl Console { + pub fn new(framebuffer: Framebuffer, psf: Psf) -> Self { + Self { framebuffer, psf } + } + + pub fn write_char(&mut self, chr: char, row: u32, col: u32) { + if row >= self.rows() { + panic!("Write at row {}, max is {}", row, self.rows()) + } + if col >= self.cols() { + panic!("Write at col {}, max is {}", col, self.cols()) + } + let glyph = self.psf.glyph(chr as u32); + self.framebuffer.draw_glyph( + glyph, + row * (self.psf.height() + 1), + col * (self.psf.width() + 1), + ); + } + + pub fn cols(&self) -> u32 { + self.framebuffer.width() / (self.psf.width() + 1) + } + + pub fn rows(&self) -> u32 { + self.framebuffer.height() / (self.psf.height() + 1) + } +} diff --git a/rust/sys/teton/src/framebuffer.rs b/rust/sys/teton/src/framebuffer.rs new file mode 100644 index 0000000..f083367 --- /dev/null +++ b/rust/sys/teton/src/framebuffer.rs @@ -0,0 +1,47 @@ +use mammoth::{mem::MemoryRegion, zion::ZError}; +use yellowstone_yunq::FramebufferInfo; + +pub struct Framebuffer { + fb_info: FramebufferInfo, + memory_region: MemoryRegion, +} + +impl Framebuffer { + pub fn from_info(fb_info: FramebufferInfo) -> Result { + let size = fb_info.height * fb_info.pitch; + let memory_region = MemoryRegion::direct_physical(fb_info.address_phys, size)?; + Ok(Self { + fb_info, + memory_region, + }) + } + + fn draw_pixel(&mut self, row: u32, col: u32, pixel: u32) { + let index = row * (self.fb_info.pitch as u32 / 4) + col; + self.memory_region.mut_slice()[index as usize] = pixel; + } + + pub fn draw_glyph(&mut self, glyph: &[u8], row: u32, col: u32) { + let gl_width = 8; + let gl_height = 16; + + #[allow(clippy::needless_range_loop)] + for r in 0..gl_height { + for c in 0..gl_width { + if ((glyph[r] >> c) % 2) == 1 { + self.draw_pixel(row + (r as u32), col + (gl_width - c - 1), 0xFFFFFFFF); + } else { + self.draw_pixel(row + (r as u32), col + (gl_width - c - 1), 0); + } + } + } + } + + pub fn width(&self) -> u32 { + self.fb_info.width as u32 + } + + pub fn height(&self) -> u32 { + self.fb_info.height as u32 + } +} diff --git a/rust/sys/teton/src/main.rs b/rust/sys/teton/src/main.rs new file mode 100644 index 0000000..982beb7 --- /dev/null +++ b/rust/sys/teton/src/main.rs @@ -0,0 +1,48 @@ +#![no_std] +#![no_main] + +extern crate alloc; + +mod console; +mod framebuffer; +mod psf; +mod terminal; + +use mammoth::{debug, define_entry, zion::z_err_t}; +use voyageurs::listener; + +define_entry!(); + +#[no_mangle] +extern "C" fn main() -> z_err_t { + debug!("Teton Starting"); + + let yellowstone = yellowstone_yunq::from_init_endpoint(); + let framebuffer_info = yellowstone + .get_framebuffer_info() + .expect("Failed to get framebuffer info."); + + debug!( + "FB addr {:#x}, bpp {}, width {} , height {}, pitch {}", + framebuffer_info.address_phys, + framebuffer_info.bpp, + framebuffer_info.width, + framebuffer_info.height, + framebuffer_info.pitch + ); + + let framebuffer = framebuffer::Framebuffer::from_info(framebuffer_info) + .expect("Failed to create framebuffer"); + + let psf = psf::Psf::new("/default8x16.psfu").expect("Failed to open font file."); + let console = console::Console::new(framebuffer, psf); + let terminal = terminal::Terminal::new(console); + + let kb_listener = listener::spawn_keyboard_listener(terminal); + + kb_listener + .join() + .expect("Failed to wait on keyboard listener"); + + 0 +} diff --git a/rust/sys/teton/src/psf.rs b/rust/sys/teton/src/psf.rs new file mode 100644 index 0000000..3f99f65 --- /dev/null +++ b/rust/sys/teton/src/psf.rs @@ -0,0 +1,78 @@ +use mammoth::debug; +use mammoth::zion::ZError; +use victoriafalls::file::File; + +const MAGIC_HEADER: u32 = 0x864AB572; + +#[repr(C)] +struct PsfHeader { + magic: u32, /* magic bytes to identify PSF */ + version: u32, /* zero */ + headersize: u32, /* offset of bitmaps in file, 32 */ + flags: u32, /* 0 if there's no unicode table */ + numglyph: u32, /* number of glyphs */ + bytes_per_glyph: u32, /* size of each glyph */ + height: u32, /* height in pixels */ + width: u32, /* width in pixels */ +} + +pub struct Psf { + file: File, + header: &'static PsfHeader, +} + +impl Psf { + pub fn new(path: &str) -> Result { + let file = File::open(path)?; + + let header = file.slice()[0..core::mem::size_of::()] + .as_ptr() + .cast(); + let psf = Self { + file, + header: unsafe { &*header }, + }; + + psf.validate()?; + + Ok(psf) + } + + fn validate(&self) -> Result<(), ZError> { + if self.header.magic != MAGIC_HEADER { + debug!("PSF: Magic value: {:x}", self.header.magic); + return Err(ZError::INVALID_ARGUMENT); + } + + if self.header.version != 0 { + debug!("PSF non-zero version"); + return Err(ZError::INVALID_ARGUMENT); + } + + if self.header.height != 0x10 { + debug!("PSF height other than 16 not handled"); + return Err(ZError::UNIMPLEMENTED); + } + + if self.header.width != 0x8 { + debug!("PSF width other than 8 not handled"); + return Err(ZError::UNIMPLEMENTED); + } + Ok(()) + } + + pub fn glyph(&self, index: u32) -> &[u8] { + let offset: usize = + (self.header.headersize + (index * self.header.bytes_per_glyph)) as usize; + let len: usize = self.header.bytes_per_glyph as usize; + &self.file.slice()[offset..offset + len] + } + + pub fn width(&self) -> u32 { + self.header.width + } + + pub fn height(&self) -> u32 { + self.header.height + } +} diff --git a/rust/sys/teton/src/terminal.rs b/rust/sys/teton/src/terminal.rs new file mode 100644 index 0000000..f8a9e0f --- /dev/null +++ b/rust/sys/teton/src/terminal.rs @@ -0,0 +1,129 @@ +use core::str::Split; + +use crate::console::Console; +use alloc::{ + format, + string::{String, ToString}, +}; +use victoriafalls::dir; +use voyageurs::listener::KeyboardHandler; + +pub struct Terminal { + console: Console, + curr_cmd: String, + cwd: String, + row: u32, +} + +impl KeyboardHandler for Terminal { + fn handle_char(&mut self, c: char) { + let mut should_execute = false; + match c { + '\x08' => { + let mut chars = self.curr_cmd.chars(); + chars.next_back(); // Pop last char. + self.curr_cmd = chars.collect(); + } + '\n' => should_execute = true, + _ => self.curr_cmd.push(c), + } + self.rewrite_command(); + if should_execute { + self.execute_command(); + self.rewrite_command(); + } + } +} + +impl Terminal { + pub fn new(console: Console) -> Self { + let mut term = Self { + console, + curr_cmd: String::new(), + cwd: "/".to_string(), + row: 0, + }; + term.rewrite_command(); + term + } + + fn rewrite_command(&mut self) { + self.console.write_char('>', self.row, 0); + + let mut col = 1; + for c in self.curr_cmd.chars() { + self.console.write_char(c, self.row, col); + col += 1; + } + + // Hacky way to properly backspace. + // FIXME: This won't work once we have line wrapping. + self.console.write_char(' ', self.row, col) + } + + fn write_line(&mut self, line: &str) { + for (col, c) in line.chars().enumerate() { + self.console.write_char(c, self.row, col as u32); + } + + self.row += 1 + } + + fn execute_command(&mut self) { + self.row += 1; + + let curr_cmd = self.curr_cmd.clone(); + let mut tokens = curr_cmd.split(' '); + + match tokens.next() { + None => {} + Some(command) => self.execute_command_parsed(command, tokens), + } + + self.curr_cmd.clear() + } + + fn execute_command_parsed(&mut self, cmd: &str, mut args: Split<'_, char>) { + // TODO: Check that no extraneous params are provided. + match cmd { + "help" => self.write_line("Available commands are 'pwd', 'ls', 'cd', and 'exec'"), + "pwd" => self.write_line(&self.cwd.clone()), + "cd" => match args.next() { + None => self.write_line("Specify a directory"), + Some(directory) => match dir::exists(directory) { + Ok(true) => self.cwd = directory.to_string(), + Ok(false) => self.write_line(&format!("Directory not found: {}", directory)), + Err(e) => self.write_line(&format!("Error stating directory {:?}", e)), + }, + }, + "ls" => { + let use_dir = match args.next() { + None => self.cwd.clone(), + Some(d) => d.to_string(), + }; + match dir::ls(&use_dir) { + Err(e) => self.write_line(&format!("Error reading directory {:?}", e)), + Ok(files) => { + for file in files { + self.write_line(&file); + } + } + } + } + "exec" => match args.next() { + None => self.write_line("Specify a program"), + Some(prog) => { + let file = victoriafalls::file::File::open(prog).expect("Failed to open file"); + let proc_cap = mammoth::elf::spawn_process_from_elf(file.slice()) + .expect("Failed to spawn process"); + + let exit_code = mammoth::syscall::process_wait(&proc_cap) + .expect("Failed to wait on process."); + + self.write_line(&format!("Process exit code: {}", exit_code)); + } + }, + _ => self.write_line(&format!("Unrecognized command: {}", cmd)), + } + } +} diff --git a/rust/sys/victoriafalls/Cargo.toml b/rust/sys/victoriafalls/Cargo.toml new file mode 100644 index 0000000..5464a78 --- /dev/null +++ b/rust/sys/victoriafalls/Cargo.toml @@ -0,0 +1,17 @@ +[package] +name = "victoriafalls" +version = "0.1.0" +edition = "2021" + +[dependencies] +mammoth = { path = "../../lib/mammoth" } +yellowstone-yunq = { path = "../../lib/yellowstone" } +yunq = { path = "../../lib/yunq" } +denali_client = { path = "../../lib/client/denali_client" } +ext2 = { path = "../../lib/fs/ext2" } + +[build-dependencies] +yunqc = { path = "../../../yunq/rust" } + +[[bin]] +name = "victoriafalls" diff --git a/rust/sys/victoriafalls/build.rs b/rust/sys/victoriafalls/build.rs new file mode 100644 index 0000000..ad812f3 --- /dev/null +++ b/rust/sys/victoriafalls/build.rs @@ -0,0 +1,14 @@ +use std::fs; + +fn main() { + let input_file = "victoriafalls.yunq"; + + println!("cargo::rerun-if-changed={input_file}"); + + let input = fs::read_to_string(input_file).expect("Failed to read input file"); + + let code = yunqc::codegen(&input).expect("Failed to generate yunq code."); + + let out = std::env::var("OUT_DIR").unwrap() + "/yunq.rs"; + fs::write(out, code).expect("Failed to write generated code."); +} diff --git a/rust/sys/victoriafalls/src/bin/victoriafalls.rs b/rust/sys/victoriafalls/src/bin/victoriafalls.rs new file mode 100644 index 0000000..770b238 --- /dev/null +++ b/rust/sys/victoriafalls/src/bin/victoriafalls.rs @@ -0,0 +1,35 @@ +#![no_std] +#![no_main] + +use ext2::Ext2Driver; +use mammoth::{define_entry, zion::z_err_t}; +use victoriafalls::{server::VictoriaFallsServerImpl, VFSServer}; +use yellowstone_yunq::RegisterEndpointRequest; +use yunq::server::spawn_server_thread; +use yunq::server::YunqServer; + +define_entry!(); + +#[no_mangle] +extern "C" fn main() -> z_err_t { + let yellowstone = yellowstone_yunq::from_init_endpoint(); + + let denali_info = yellowstone.get_denali().unwrap(); + + let driver = Ext2Driver::new(denali_info); + + let vfs_server = VFSServer::new(VictoriaFallsServerImpl::new(driver)).unwrap(); + + let yellowstone = yellowstone_yunq::from_init_endpoint(); + yellowstone + .register_endpoint(&RegisterEndpointRequest { + endpoint_name: "victoriafalls".into(), + endpoint_capability: vfs_server.create_client_cap().unwrap().release(), + }) + .unwrap(); + + let server_thread = spawn_server_thread(vfs_server); + server_thread.join().unwrap(); + + 0 +} diff --git a/rust/sys/victoriafalls/src/dir.rs b/rust/sys/victoriafalls/src/dir.rs new file mode 100644 index 0000000..fb6efeb --- /dev/null +++ b/rust/sys/victoriafalls/src/dir.rs @@ -0,0 +1,27 @@ +use alloc::{ + string::{String, ToString as _}, + vec::Vec, +}; +use mammoth::zion::ZError; + +pub fn exists(path: &str) -> Result { + let vfs = crate::get_client(); + let result = vfs.get_directory(&crate::GetDirectoryRequest { + path: path.to_string(), + }); + + match result { + Ok(_) => Ok(true), + Err(ZError::NOT_FOUND) => Ok(false), + Err(e) => Err(e), + } +} + +pub fn ls(path: &str) -> Result, ZError> { + let vfs = crate::get_client(); + let result = vfs.get_directory(&crate::GetDirectoryRequest { + path: path.to_string(), + })?; + + Ok(result.filenames.split(',').map(|s| s.to_string()).collect()) +} diff --git a/rust/sys/victoriafalls/src/file.rs b/rust/sys/victoriafalls/src/file.rs new file mode 100644 index 0000000..feb8eec --- /dev/null +++ b/rust/sys/victoriafalls/src/file.rs @@ -0,0 +1,30 @@ +use crate::OpenFileRequest; +use alloc::string::ToString; +use mammoth::{cap::Capability, mem::MemoryRegion, zion::ZError}; + +pub struct File { + memory: MemoryRegion, + len: usize, +} + +impl File { + pub fn open(path: &str) -> Result { + let vfs = crate::get_client(); + let resp = vfs.open_file(&OpenFileRequest { + path: path.to_string(), + })?; + + Ok(Self { + memory: mammoth::mem::MemoryRegion::from_cap(Capability::take(resp.memory))?, + len: resp.size as usize, + }) + } + + pub fn slice(&self) -> &[u8] { + &self.memory.slice()[..self.len] + } + + pub fn memory(&self) -> &MemoryRegion { + &self.memory + } +} diff --git a/rust/sys/victoriafalls/src/lib.rs b/rust/sys/victoriafalls/src/lib.rs new file mode 100644 index 0000000..a213a8b --- /dev/null +++ b/rust/sys/victoriafalls/src/lib.rs @@ -0,0 +1,32 @@ +#![no_std] + +use core::include; + +include!(concat!(env!("OUT_DIR"), "/yunq.rs")); + +pub mod dir; +pub mod file; +pub mod server; + +static mut VFS_CLIENT: Option = None; + +fn get_client() -> &'static mut VFSClient { + unsafe { + #[allow(static_mut_refs)] + if VFS_CLIENT.is_none() { + let endpoint_cap = yellowstone_yunq::from_init_endpoint() + .get_endpoint(&yellowstone_yunq::GetEndpointRequest { + endpoint_name: "victoriafalls".to_string(), + }) + .expect("Failed to get VFS endpoint"); + + VFS_CLIENT = Some(VFSClient::new(Capability::take(endpoint_cap.endpoint))); + } + #[allow(static_mut_refs)] + VFS_CLIENT.as_mut().unwrap() + } +} + +pub fn set_client(client: VFSClient) { + unsafe { VFS_CLIENT = Some(client) }; +} diff --git a/rust/sys/victoriafalls/src/server.rs b/rust/sys/victoriafalls/src/server.rs new file mode 100644 index 0000000..a901ea9 --- /dev/null +++ b/rust/sys/victoriafalls/src/server.rs @@ -0,0 +1,75 @@ +use alloc::{string::String, vec::Vec}; +use ext2::Ext2Driver; +use mammoth::{debug, zion::ZError}; + +use crate::{Directory, GetDirectoryRequest, OpenFileRequest, OpenFileResponse, VFSServerHandler}; + +pub struct VictoriaFallsServerImpl { + ext2_driver: Ext2Driver, +} + +impl VictoriaFallsServerImpl { + pub fn new(ext2_driver: Ext2Driver) -> Self { + VictoriaFallsServerImpl { ext2_driver } + } + + fn find_path_in_dir(&mut self, inode_num: u32, file_name: &str) -> Result { + let files = self.ext2_driver.read_directory(inode_num)?; + + files + .iter() + .find(|fi| fi.name == file_name) + .map(|fi| fi.inode) + .ok_or(ZError::NOT_FOUND) + } +} + +impl VFSServerHandler for VictoriaFallsServerImpl { + fn open_file(&mut self, req: OpenFileRequest) -> Result { + debug!("Reading {}", req.path); + let mut tokens = req.path.split('/'); + if tokens.next() != Some("") { + debug!("Path must be absolute"); + return Err(ZError::INVALID_ARGUMENT); + } + + let mut inode_num = 2; // Start with root. + + for path_token in tokens { + inode_num = self.find_path_in_dir(inode_num, path_token)?; + } + + let inode = self.ext2_driver.get_inode(inode_num); + Ok(OpenFileResponse { + path: req.path, + memory: self.ext2_driver.read_file(inode_num)?.release(), + size: inode.size as u64, + }) + } + + fn get_directory(&mut self, req: GetDirectoryRequest) -> Result { + debug!("Reading dir {}", req.path); + let mut tokens = req.path.split('/'); + if tokens.next() != Some("") { + debug!("Path must be absolute"); + return Err(ZError::INVALID_ARGUMENT); + } + + let mut inode_num = 2; // Start with root. + + for path_token in tokens { + inode_num = self.find_path_in_dir(inode_num, path_token)?; + } + + let files: Vec = self + .ext2_driver + .read_directory(inode_num)? + .into_iter() + .map(|fi| fi.name) + .collect(); + + Ok(Directory { + filenames: files.join(","), + }) + } +} diff --git a/sys/victoriafalls/lib/victoriafalls/victoriafalls.yunq b/rust/sys/victoriafalls/victoriafalls.yunq similarity index 99% rename from sys/victoriafalls/lib/victoriafalls/victoriafalls.yunq rename to rust/sys/victoriafalls/victoriafalls.yunq index 5da582e..1cc0d45 100644 --- a/sys/victoriafalls/lib/victoriafalls/victoriafalls.yunq +++ b/rust/sys/victoriafalls/victoriafalls.yunq @@ -21,3 +21,4 @@ message Directory { // , separated list of filenames until we have repeated strings. string filenames; } + diff --git a/rust/sys/yellowstone/Cargo.toml b/rust/sys/yellowstone/Cargo.toml new file mode 100644 index 0000000..6a89d4d --- /dev/null +++ b/rust/sys/yellowstone/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "yellowstone" +version = "0.1.0" +edition = "2021" + +[dependencies] +mammoth = { path = "../../lib/mammoth" } +denali_client = { path = "../../lib/client/denali_client" } +victoriafalls = { path = "../victoriafalls" } +voyageurs = { path = "../../lib/voyageurs" } +yellowstone-yunq = { path = "../../lib/yellowstone" } +yunq = { path = "../../lib/yunq" } diff --git a/rust/sys/yellowstone/src/gpt.rs b/rust/sys/yellowstone/src/gpt.rs new file mode 100644 index 0000000..87bf3ce --- /dev/null +++ b/rust/sys/yellowstone/src/gpt.rs @@ -0,0 +1,138 @@ +use denali_client::DenaliClient; +use mammoth::{cap::Capability, zion::ZError}; + +const MBR_SIG: u16 = 0xAA55; + +const GPT_OS_TYPE: u8 = 0xEE; + +#[repr(C, packed)] +struct MbrPartition { + boot_indicator: u8, + starting_chs: [u8; 3], + os_type: u8, + ending_chs: [u8; 3], + starting_lba: u32, + ending_lba: u32, +} + +#[repr(C, packed)] +struct PartitionHeader { + signature: u64, + revision: u32, + header_size: u32, + crc_32: u32, + reserved: u32, + lba_self: u64, + lba_mirror: u64, + lba_min: u64, + lba_max: u64, + guid_low: u64, + guid_high: u64, + lba_partition_entries: u64, + num_partitions: u32, + partition_entry_size: u32, + partition_entry_crc32: u32, +} + +#[repr(C, packed)] +struct PartitionEntry { + type_guid_low: u64, + type_guid_high: u64, + part_guid_low: u64, + part_guid_high: u64, + lba_start: u64, + lba_end: u64, + attrs: u64, + partition_name: [u8; 72], +} + +pub fn read_gpt(mut denali: DenaliClient) -> Result { + let resp = denali.read(&denali_client::ReadRequest { + device_id: 0, + block: denali_client::DiskBlock { lba: 0, size: 2 }, + })?; + + let first_lbas = mammoth::mem::MemoryRegion::from_cap(Capability::take(resp.memory))?; + + let maybe_mbr_sig = u16::from_le_bytes(first_lbas.slice()[0x1FE..0x200].try_into().unwrap()); + + if maybe_mbr_sig != MBR_SIG { + mammoth::debug!("MBR Sig {:#x} does not match {:#x}", maybe_mbr_sig, MBR_SIG); + return Err(ZError::FAILED_PRECONDITION); + } + + let mbr = unsafe { + first_lbas.slice::()[0x1BE..] + .as_ptr() + .cast::() + .as_ref() + .unwrap() + }; + + if mbr.os_type != GPT_OS_TYPE { + let os_type = mbr.os_type; + mammoth::debug!( + "MBR OS Type: {:#x} does not match GPT ({:#x})", + os_type, + GPT_OS_TYPE + ); + } + + let gpt_header = unsafe { + first_lbas.slice::()[512..] + .as_ptr() + .cast::() + .as_ref() + .unwrap() + }; + + let num_partitions = gpt_header.num_partitions; + let partition_entry_size = gpt_header.partition_entry_size; + let lba_partition_entries = gpt_header.lba_partition_entries; + let num_blocks = (num_partitions * partition_entry_size).div_ceil(512) as u64; + + mammoth::debug!( + "Reading partition table from LBA {}, {} entries of size {} equals {} blocks.", + lba_partition_entries, + num_partitions, + partition_entry_size, + num_blocks + ); + + let resp = denali.read(&denali_client::ReadRequest { + device_id: 0, + block: denali_client::DiskBlock { + lba: lba_partition_entries, + size: num_blocks, + }, + })?; + + let partition_table = mammoth::mem::MemoryRegion::from_cap(Capability::take(resp.memory))?; + + for ind in 0..num_partitions { + let offset = (ind * partition_entry_size) as usize; + let limit = offset + (partition_entry_size as usize); + + let partition_entry = unsafe { + partition_table.slice::()[offset..limit] + .as_ptr() + .cast::() + .as_ref() + .unwrap() + }; + + if partition_entry.type_guid_low == 0 && partition_entry.type_guid_high == 0 { + continue; + } + + const LFS_DATA_LOW: u64 = 0x477284830fc63daf; + const LFS_DATA_HIGH: u64 = 0xe47d47d8693d798e; + if partition_entry.type_guid_low == LFS_DATA_LOW + && partition_entry.type_guid_high == LFS_DATA_HIGH + { + return Ok(partition_entry.lba_start); + } + } + + Err(ZError::NOT_FOUND) +} diff --git a/rust/sys/yellowstone/src/main.rs b/rust/sys/yellowstone/src/main.rs new file mode 100644 index 0000000..dbe8c2e --- /dev/null +++ b/rust/sys/yellowstone/src/main.rs @@ -0,0 +1,98 @@ +#![no_std] +#![no_main] + +extern crate alloc; + +use alloc::{string::ToString, sync::Arc, vec::Vec}; +use mammoth::{ + cap::Capability, + define_entry, elf, + init::{BOOT_FRAMEBUFFER_INFO_VMMO, BOOT_PCI_VMMO}, + mem::MemoryRegion, + zion::{kZionPerm_All, z_cap_t, z_err_t, ZError}, +}; +use yellowstone_yunq::YellowstoneServer; +use yunq::server::{spawn_server_thread, YunqServer}; + +mod gpt; +mod pci; +mod server; + +define_entry!(); + +fn spawn_from_mem_region( + mem_region: &mammoth::mem::MemoryRegion, + server_cap: Capability, +) -> Result<(), ZError> { + elf::spawn_process_from_elf_and_init(mem_region.slice(), server_cap)?; + Ok(()) +} + +fn spawn_from_vmmo(vmmo_cap: z_cap_t, server_cap: Capability) -> Result<(), ZError> { + let region = mammoth::mem::MemoryRegion::from_cap(Capability::take(vmmo_cap))?; + spawn_from_mem_region(®ion, server_cap)?; + Ok(()) +} + +#[no_mangle] +extern "C" fn main() -> z_err_t { + let pci_region = MemoryRegion::from_cap(Capability::take(unsafe { BOOT_PCI_VMMO })) + .expect("Failed to create PCI region"); + let fb_region = MemoryRegion::from_cap(Capability::take(unsafe { BOOT_FRAMEBUFFER_INFO_VMMO })) + .expect("Failed to create Framebuffer region"); + let context = Arc::new( + server::YellowstoneServerContext::new(pci_region, fb_region) + .expect("Failed to create yellowstone context"), + ); + let handler = server::YellowstoneServerImpl::new(context.clone()); + let server = YellowstoneServer::new(handler).expect("Couldn't create yellowstone server"); + + let client_cap = server.create_client_cap().unwrap(); + let server_thread = spawn_server_thread(server); + + spawn_from_vmmo( + unsafe { mammoth::init::BOOT_DENALI_VMMO }, + client_cap.duplicate(kZionPerm_All).unwrap(), + ) + .expect("Failed to spawn denali"); + + context.wait("denali").expect("Failed to wait for denali"); + mammoth::debug!("Denali registered."); + + spawn_from_vmmo( + unsafe { mammoth::init::BOOT_VICTORIA_FALLS_VMMO }, + client_cap.duplicate(kZionPerm_All).unwrap(), + ) + .expect("Failed to spawn victoriafalls"); + + context.wait("victoriafalls").unwrap(); + mammoth::debug!("VFS Registered"); + + let file = victoriafalls::file::File::open("/init.txt").unwrap(); + + let init_files: Vec<_> = core::str::from_utf8(file.slice()) + .unwrap() + .trim() + .split('\n') + .collect(); + + mammoth::debug!("Init files: {:?}", init_files); + + for bin_name in init_files { + // FIXME implement dependencies. + if bin_name == "teton" { + context.wait("voyageurs").unwrap(); + } + let path = "/bin/".to_string() + bin_name; + + let bin_file = victoriafalls::file::File::open(&path).unwrap(); + spawn_from_mem_region( + bin_file.memory(), + client_cap.duplicate(kZionPerm_All).unwrap(), + ) + .unwrap(); + } + + server_thread.join().expect("Failed to join thread"); + 0 +} diff --git a/rust/sys/yellowstone/src/pci.rs b/rust/sys/yellowstone/src/pci.rs new file mode 100644 index 0000000..e421f74 --- /dev/null +++ b/rust/sys/yellowstone/src/pci.rs @@ -0,0 +1,131 @@ +use mammoth::{cap::Capability, mem::MemoryRegion, zion::ZError}; + +pub struct PciReader { + memory_region: MemoryRegion, +} + +type DevPredicate = fn(u8, u8, u8) -> bool; + +impl PciReader { + pub fn new(memory_region: MemoryRegion) -> Self { + Self { memory_region } + } + + pub fn get_ahci_region(&self) -> Result { + match self.probe_pci(|class, _, _| class == 0x1) { + Some(m) => Ok(m), + None => Err(ZError::NOT_FOUND), + } + } + + pub fn get_xhci_region(&self) -> Result { + match self.probe_pci(|class, subclass, prog_interface| { + class == 0xC && subclass == 0x3 && prog_interface == 0x30 + }) { + Some(m) => Ok(m), + None => Err(ZError::NOT_FOUND), + } + } + + fn probe_pci(&self, pred: DevPredicate) -> Option { + let base_header = self.pci_header(0, 0, 0); + if (base_header.header_type & 0x80) == 0 { + if let Some(dev) = self.probe_bus(pred, 0) { + return Some(dev); + } + } else { + for fun in 0..8 { + let fun_hdr = self.pci_header(0, 0, fun); + if fun_hdr.vendor_id != 0xFFFF { + if let Some(dev) = self.probe_bus(pred, fun) { + return Some(dev); + } + } + } + } + None + } + + fn probe_bus(&self, pred: DevPredicate, bus: u8) -> Option { + for dev in 0..0x20 { + if let Some(dev) = self.probe_device(pred, bus, dev) { + return Some(dev); + } + } + None + } + + fn probe_device(&self, pred: DevPredicate, bus: u8, dev: u8) -> Option { + let device_base_header = self.pci_header(bus, dev, 0); + if device_base_header.vendor_id == 0xFFFF { + return None; + } + + if let Some(dev) = self.probe_function(pred, bus, dev, 0) { + return Some(dev); + } + + if (device_base_header.header_type & 0x80) != 0 { + for fun in 1..8 { + if let Some(dev) = self.probe_function(pred, bus, dev, fun) { + return Some(dev); + } + } + } + None + } + + fn probe_function(&self, pred: DevPredicate, bus: u8, dev: u8, fun: u8) -> Option { + let function_header = self.pci_header(bus, dev, fun); + + mammoth::debug!( + "PCI Function: {:#x} {:#x} {:#x}", + function_header.class_code, + function_header.subclass, + function_header.prog_interface + ); + + if pred( + function_header.class_code, + function_header.subclass, + function_header.prog_interface, + ) { + mammoth::debug!("Found!"); + let offset = pci_header_offset(bus, dev, fun); + Some( + self.memory_region + .duplicate(offset as u64, 0x1000) + .expect("Failed to duplicate PCI cap"), + ) + } else { + None + } + } + + fn pci_header(&self, bus: u8, dev: u8, fun: u8) -> &PciHeader { + let offset = pci_header_offset(bus, dev, fun); + let header_slice: &[u8] = + &self.memory_region.slice()[offset..offset + size_of::()]; + unsafe { header_slice.as_ptr().cast::().as_ref().unwrap() } + } +} + +#[repr(C, packed)] +struct PciHeader { + vendor_id: u16, + device_id: u16, + command_reg: u16, + status_reg: u16, + revision: u8, + prog_interface: u8, + subclass: u8, + class_code: u8, + cache_line_size: u8, + latency_timer: u8, + header_type: u8, + bist: u8, +} + +fn pci_header_offset(bus: u8, dev: u8, fun: u8) -> usize { + ((bus as usize) << 20) | ((dev as usize) << 15) | ((fun as usize) << 12) +} diff --git a/rust/sys/yellowstone/src/server.rs b/rust/sys/yellowstone/src/server.rs new file mode 100644 index 0000000..a6d5fa1 --- /dev/null +++ b/rust/sys/yellowstone/src/server.rs @@ -0,0 +1,138 @@ +use alloc::sync::Arc; +use alloc::{collections::BTreeMap, string::String}; +use mammoth::sync::Mutex; +use mammoth::{cap::Capability, mem::MemoryRegion, zion::ZError}; +use victoriafalls::VFSClient; +use yellowstone_yunq::{ + AhciInfo, DenaliInfo, Endpoint, FramebufferInfo, GetEndpointRequest, RegisterEndpointRequest, + XhciInfo, YellowstoneServerHandler, +}; + +use crate::pci::PciReader; + +pub struct YellowstoneServerContext { + registration_semaphore: mammoth::sync::Semaphore, + pci_reader: PciReader, + framebuffer_info_region: MemoryRegion, + service_map: Mutex>, +} + +impl YellowstoneServerContext { + fn framebuffer_info(&self) -> yellowstone_yunq::FramebufferInfo { + let fb_info: &mammoth::zion::ZFramebufferInfo = unsafe { + self.framebuffer_info_region + .slice::() + .as_ptr() + .cast::() + .as_ref() + .unwrap() + }; + + yellowstone_yunq::FramebufferInfo { + address_phys: fb_info.address_phys, + width: fb_info.width, + height: fb_info.height, + pitch: fb_info.pitch, + bpp: fb_info.bpp as u64, + memory_model: fb_info.memory_model as u64, + red_mask_size: fb_info.red_mask_size as u64, + red_mask_shift: fb_info.red_mask_shift as u64, + blue_mask_size: fb_info.blue_mask_size as u64, + blue_mask_shift: fb_info.blue_mask_shift as u64, + green_mask_size: fb_info.green_mask_size as u64, + green_mask_shift: fb_info.green_mask_shift as u64, + } + } +} + +impl YellowstoneServerContext { + pub fn new(pci_region: MemoryRegion, fb_region: MemoryRegion) -> Result { + Ok(Self { + registration_semaphore: mammoth::sync::Semaphore::new()?, + pci_reader: PciReader::new(pci_region), + framebuffer_info_region: fb_region, + service_map: Mutex::new(BTreeMap::new()), + }) + } + + pub fn wait(&self, service: &str) -> Result<(), ZError> { + loop { + if self.service_map.lock().get(service).is_some() { + return Ok(()); + } + self.registration_semaphore.wait().unwrap(); + } + } +} + +pub struct YellowstoneServerImpl { + context: Arc, +} + +impl YellowstoneServerImpl { + pub fn new(context: Arc) -> Self { + Self { context } + } +} + +impl YellowstoneServerHandler for YellowstoneServerImpl { + fn register_endpoint(&mut self, req: RegisterEndpointRequest) -> Result<(), ZError> { + if req.endpoint_name == "victoriafalls" { + victoriafalls::set_client(VFSClient::new( + Capability::take_copy(req.endpoint_capability).unwrap(), + )); + } + + self.context + .service_map + .lock() + .insert(req.endpoint_name, Capability::take(req.endpoint_capability)); + + self.context.registration_semaphore.signal()?; + Ok(()) + } + + fn get_endpoint(&mut self, req: GetEndpointRequest) -> Result { + match self.context.service_map.lock().get(&req.endpoint_name) { + Some(cap) => Ok(Endpoint { + endpoint: cap.duplicate(Capability::PERMS_ALL)?.release(), + }), + None => Err(ZError::NOT_FOUND), + } + } + + fn get_ahci_info(&mut self) -> Result { + Ok(AhciInfo { + ahci_region: self.context.pci_reader.get_ahci_region()?.release(), + region_length: 0x1000, + }) + } + + fn get_xhci_info(&mut self) -> Result { + Ok(XhciInfo { + xhci_region: self.context.pci_reader.get_xhci_region()?.release(), + region_length: 0x1000, + }) + } + + fn get_framebuffer_info(&mut self) -> Result { + Ok(self.context.framebuffer_info()) + } + + fn get_denali(&mut self) -> Result { + match self.context.service_map.lock().get("denali") { + Some(ep_cap) => crate::gpt::read_gpt(denali_client::DenaliClient::new( + ep_cap.duplicate(Capability::PERMS_ALL).unwrap(), + )) + .map(|lba| DenaliInfo { + denali_endpoint: ep_cap.duplicate(Capability::PERMS_ALL).unwrap().release(), + device_id: 0, + lba_offset: lba, + }), + None => { + mammoth::debug!("Denali not yet registered"); + Err(ZError::FAILED_PRECONDITION) + } + } + } +} diff --git a/rust/usr/testbed/Cargo.toml b/rust/usr/testbed/Cargo.toml new file mode 100644 index 0000000..aff88d2 --- /dev/null +++ b/rust/usr/testbed/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "testbed" +version = "0.1.0" +edition = "2021" + +[dependencies] +mammoth = { path = "../../lib/mammoth" } +yellowstone-yunq = { path = "../../lib/yellowstone" } +yunq = { path = "../../lib/yunq" } diff --git a/rust/usr/testbed/src/main.rs b/rust/usr/testbed/src/main.rs new file mode 100644 index 0000000..2ea144b --- /dev/null +++ b/rust/usr/testbed/src/main.rs @@ -0,0 +1,75 @@ +#![no_std] +#![no_main] + +extern crate alloc; + +use alloc::boxed::Box; +use alloc::string::ToString; +use mammoth::debug; +use mammoth::define_entry; +use mammoth::task::Executor; +use mammoth::task::Task; +use mammoth::thread; +use mammoth::zion::z_err_t; +use yellowstone_yunq::GetEndpointRequest; + +define_entry!(); + +pub fn testthread() { + debug!("Testing 1, 8 ,9"); +} + +async fn inner_async() { + debug!("Inner Async executed"); +} + +async fn test_async() { + debug!("Async executed"); + + let i = inner_async(); + + debug!("inner async created"); + + i.await; + + debug!("inner async returned"); +} + +#[no_mangle] +pub extern "C" fn main() -> z_err_t { + debug!("Testing!"); + + let yellowstone = yellowstone_yunq::from_init_endpoint(); + + debug!("Get endpoint"); + + let endpoint = yellowstone + .get_endpoint(&GetEndpointRequest { + endpoint_name: "denali".to_string(), + }) + .expect("Failed to get endpoint"); + + debug!("Got endpoint w/ cap: {:#x}", endpoint.endpoint); + + let a = Box::new(1); + let b = Box::new(1); + debug!("Addrs: {:p} {:p}", a, b); + + let x = Box::new(|| 1); + debug!("Addr: {:p}", x); + debug!("Addr: {:p}", &x); + + let t = thread::spawn(testthread); + t.join().expect("Failed to wait."); + + let t = thread::spawn(|| debug!("Testing 4, 5, 6")); + t.join().expect("Failed to wait."); + + let mut executor = Executor::new(); + + executor.spawn(Task::new(test_async())); + + executor.run(); + + 0 +} diff --git a/rust/x86_64-acadia-os.json b/rust/x86_64-acadia-os.json new file mode 100644 index 0000000..1060335 --- /dev/null +++ b/rust/x86_64-acadia-os.json @@ -0,0 +1,13 @@ +{ + "llvm-target": "x86_64-unknown-none", + "data-layout": "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128", + "arch": "x86_64", + "target-endian": "little", + "target-pointer-width": 64, + "target-c-int-width": 32, + "os": "none", + "executables": true, + "linker-flavor": "ld.lld", + "linker": "rust-lld", + "panic-strategy": "abort" +} diff --git a/scripts/bochs.sh b/scripts/bochs.sh new file mode 100755 index 0000000..52180e8 --- /dev/null +++ b/scripts/bochs.sh @@ -0,0 +1,19 @@ +#! /bin/bash + +set -e + +DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" + +BUILD_DIR="${DIR}/../builddbg" + +bash ${DIR}/build.sh +sudo sh ${DIR}/build_image.sh ${BUILD_DIR}/disk.img + +BOCHS_ARGS= +if [[ $1 == "debug" ]]; then + BOCHS_ARGS+="-dbg" +fi + +# TODO Make this portable, build bochs as a part of toolchain? +~/opt/bochs/bin/bochs $BOCHS_ARGS -q + diff --git a/scripts/build.sh b/scripts/build.sh new file mode 100755 index 0000000..9b64905 --- /dev/null +++ b/scripts/build.sh @@ -0,0 +1,32 @@ +#! /bin/bash + +set -e + +DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" + +echo $DIR + +BUILD_DIR="${DIR}/../builddbg" + +pushd $BUILD_DIR +ninja +ninja install + +CARGO_USR_ROOT="${DIR}/../sysroot/usr/" +CARGO_SYS_ROOT="${DIR}/../sysroot/" + +# Need to pushd so rustup gets the toolchain from rust/rust_toolchain.toml +pushd "${DIR}/../rust" + +for BIN in ${DIR}/../rust/usr/*/; do + cargo install --force --path "${BIN}" --root $CARGO_USR_ROOT +done + +for BIN in ${DIR}/../rust/sys/*/; do + cargo install --all-features --force --path "${BIN}" --root $CARGO_SYS_ROOT +done +popd + +popd + + diff --git a/scripts/build_image.sh b/scripts/build_image.sh index e26074f..9b39927 100644 --- a/scripts/build_image.sh +++ b/scripts/build_image.sh @@ -1,7 +1,11 @@ -#!/us +#! /bin/bash set -e +SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd ) +REPO_ROOT="$SCRIPT_DIR/.." +BUILD_DIR="$REPO_ROOT/builddbg" + if [[ $# -ne 1 ]]; then echo "Must specify disk image name." fi @@ -15,37 +19,41 @@ if [ -z "$dev" ]; then fi echo "Loopback device: ${dev}" +EFI_DIR="$BUILD_DIR/efi" +SYSROOT="$BUILD_DIR/sysroot" + cleanup() { - umount efi - rm -rf efi - umount sysroot - rm -rf sysroot + umount $EFI_DIR + rm -rf $EFI_DIR + umount $SYSROOT + rm -rf $SYSROOT losetup -d $dev } trap cleanup EXIT -parted -s $dev mklabel gpt mkpart EFI fat32 1MiB 10MiB mkpart ext2 10MiB 100% set 1 esp on -mkfs.fat -F 12 "${dev}p1" -mke2fs "${dev}p2" +parted -s $dev mklabel gpt mkpart BIOS ext2 1MiB 2MiB mkpart EFI fat32 2MiB 11MiB mkpart ext2 11MiB 100% set 1 bios_grub on set 2 esp on +mkfs.fat -F 12 "${dev}p2" +mke2fs "${dev}p3" limine bios-install "${dev}" -mkdir -p efi/ -mount "${dev}p1" efi/ -mkdir -p efi/EFI/BOOT -cp /usr/share/limine/BOOTX64.EFI efi/EFI/BOOT -cp /usr/share/limine/limine-bios.sys efi/ -cp ../zion/boot/limine.cfg efi/ -cp zion/zion efi/ -mkdir -p efi/sys -cp sys/yellowstone/yellowstone efi/sys/yellowstone -cp sys/denali/denali efi/sys/denali -cp sys/victoriafalls/victoriafalls efi/sys/victoriafalls +mkdir -p $EFI_DIR +mount "${dev}p2" $EFI_DIR -mkdir -p sysroot -mount "${dev}p2" sysroot/ -rsync -a ../sysroot . -ls sysroot/ +mkdir -p $EFI_DIR/EFI/BOOT +cp /usr/share/limine/BOOTX64.EFI $EFI_DIR/EFI/BOOT +cp /usr/share/limine/limine-bios.sys $EFI_DIR +cp $REPO_ROOT/zion/boot/limine.conf $EFI_DIR/ +cp $BUILD_DIR/zion/zion $EFI_DIR/ +mkdir -p $EFI_DIR/sys +cp $REPO_ROOT/sysroot/bin/yellowstone $EFI_DIR/sys/yellowstone +cp $REPO_ROOT/sysroot/bin/denali $EFI_DIR/sys/denali +cp $REPO_ROOT/sysroot/bin/victoriafalls $EFI_DIR/sys/victoriafalls + +mkdir -p $SYSROOT +mount "${dev}p3" $SYSROOT +rsync -a "$REPO_ROOT/sysroot" $BUILD_DIR +ls $SYSROOT chown drew:drew $1 diff --git a/scripts/qemu.sh b/scripts/qemu.sh index b2ea1cc..22d6f98 100755 --- a/scripts/qemu.sh +++ b/scripts/qemu.sh @@ -8,11 +8,9 @@ echo $DIR BUILD_DIR="${DIR}/../builddbg" -pushd $BUILD_DIR -ninja -ninja install -sudo sh ${DIR}/build_image.sh disk.img +bash ${DIR}/build.sh +sudo sh ${DIR}/build_image.sh ${BUILD_DIR}/disk.img QEMU_ARGS= if [[ $1 == "debug" ]]; then @@ -20,7 +18,7 @@ if [[ $1 == "debug" ]]; then fi # Use machine q35 to access PCI devices. -qemu-system-x86_64 -machine q35 -d guest_errors -m 1G -serial stdio -hda disk.img ${QEMU_ARGS} +qemu-system-x86_64 -machine q35 -d guest_errors -m 1G -serial stdio -hda ${BUILD_DIR}/disk.img ${QEMU_ARGS} -device nec-usb-xhci,id=xhci -device usb-kbd,bus=xhci.0 popd # Extra options to add to this script in the future. diff --git a/scripts/test-mem.sh b/scripts/test-mem.sh new file mode 100755 index 0000000..046c120 --- /dev/null +++ b/scripts/test-mem.sh @@ -0,0 +1,16 @@ +#! /bin/bash + +set -e + +DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" + +pushd "$DIR/.." +if [[ ! -e test-bin ]]; then + cmake -B test-bin/ -G Ninja -D enable_testing=on -D CMAKE_ASM-ATT_COMPILER=gcc +fi +pushd test-bin/ +ninja build_test +ctest --output-on-failure -T memcheck +popd +popd + diff --git a/scripts/test.sh b/scripts/test.sh new file mode 100755 index 0000000..ec1b1eb --- /dev/null +++ b/scripts/test.sh @@ -0,0 +1,16 @@ +#! /bin/bash + +set -e + +DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" + +pushd "$DIR/.." +if [[ ! -e test-bin ]]; then + cmake -B test-bin/ -G Ninja -D enable_testing=on -D CMAKE_ASM-ATT_COMPILER=gcc +fi +pushd test-bin/ +ninja build_test +ctest --output-on-failure +popd +popd + diff --git a/sys/CMakeLists.txt b/sys/CMakeLists.txt index e2eb139..b6fe722 100644 --- a/sys/CMakeLists.txt +++ b/sys/CMakeLists.txt @@ -1,8 +1,5 @@ set(CMAKE_SHARED_LIBRARY_LINK_CXX_FLAGS "") -add_subdirectory(denali) -add_subdirectory(teton) -add_subdirectory(victoriafalls) add_subdirectory(voyageurs) add_subdirectory(yellowstone) diff --git a/sys/denali/CMakeLists.txt b/sys/denali/CMakeLists.txt deleted file mode 100644 index eab0515..0000000 --- a/sys/denali/CMakeLists.txt +++ /dev/null @@ -1,25 +0,0 @@ -add_executable(denali - ahci/ahci_device.cpp - ahci/ahci_driver.cpp - ahci/command.cpp - denali.cpp - denali_server.cpp - ) - - -target_include_directories(denali - PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}) - -target_link_libraries(denali - denali_yunq - glacier - mammoth - yellowstone_yunq - ) - -set_target_properties(denali PROPERTIES - COMPILE_FLAGS "${CMAKE_CXX_FLAGS} ${BASE_COMPILE_FLAGS}" - LINK_FLAGS "${CMAKE_EXE_LINK_FLAGS} ${BASE_LINK_FLAGS}" - ) - -yunq_gen(lib/denali lib denali) diff --git a/sys/denali/ahci/ahci.h b/sys/denali/ahci/ahci.h deleted file mode 100644 index 5e275a5..0000000 --- a/sys/denali/ahci/ahci.h +++ /dev/null @@ -1,248 +0,0 @@ -#pragma once - -#include - -struct PciDeviceHeader { - uint16_t vendor_id; - uint16_t device_id; - uint16_t command_reg; - uint16_t status_reg; - uint8_t revision; - uint8_t prog_interface; - uint8_t subclass; - uint8_t class_code; - uint8_t cache_line_size; - uint8_t latency_timer; - uint8_t header_type; - uint8_t bist; - uint32_t bars[5]; - uint32_t abar; - uint32_t reserved0; - uint32_t subsystem_id; - uint32_t expansion_rom; - uint8_t cap_ptr; - uint8_t reserved1[7]; - uint8_t interrupt_line; - uint8_t interrupt_pin; - uint8_t min_grant; - uint8_t max_latency; -} __attribute__((packed)); - -struct PciMsiCap { - uint8_t cap_id; - uint8_t next_offset; - uint8_t message_control; - uint8_t reserved; - uint64_t message_address; - uint16_t message_data; -} __attribute__((packed)); - -struct AhciHba { - uint32_t capabilities; - uint32_t global_host_control; - uint32_t interrupt_status; - uint32_t port_implemented; - uint32_t version; - uint32_t ccc_ctl; // 0x14, Command completion coalescing control - uint32_t ccc_pts; // 0x18, Command completion coalescing ports - uint32_t em_loc; // 0x1C, Enclosure management location - uint32_t em_ctl; // 0x20, Enclosure management control - uint32_t capabilities_ext; - uint32_t bohc; // 0x28, BIOS/OS handoff control and status -} __attribute__((packed)); - -struct AhciPort { - uint64_t command_list_base; - uint64_t fis_base; - uint32_t interrupt_status; - uint32_t interrupt_enable; - uint32_t command; - uint32_t reserved; - uint32_t task_file_data; - uint32_t signature; - uint32_t sata_status; - uint32_t sata_control; - uint32_t sata_error; - uint32_t sata_active; - uint32_t command_issue; - uint32_t sata_notification; - uint32_t fis_based_switching_ctl; - uint32_t device_sleep; -} __attribute__((packed)); - -struct CommandHeader { - uint16_t command; - uint16_t prd_table_length; - uint32_t prd_byte_count; - uint64_t command_table_base_addr; - uint64_t reserved1; - uint64_t reserved2; -} __attribute__((packed)); - -struct CommandList { - CommandHeader command_headers[32]; -} __attribute__((packed)); - -struct PhysicalRegionDescriptor { - uint64_t region_address; - uint32_t reserved; - // bit 0 must be one. - // 21:0 is byte count - // 31 is Interrupt on Completion - uint32_t byte_count; -} __attribute__((packed)); - -struct CommandTable { - uint8_t command_fis[64]; - uint8_t atapi_command[16]; - uint8_t reserved[48]; - PhysicalRegionDescriptor prdt[8]; -} __attribute__((packed)); - -typedef enum { - FIS_TYPE_REG_H2D = 0x27, // Register FIS - host to device - FIS_TYPE_REG_D2H = 0x34, // Register FIS - device to host - FIS_TYPE_DMA_ACT = 0x39, // DMA activate FIS - device to host - FIS_TYPE_DMA_SETUP = 0x41, // DMA setup FIS - bidirectional - FIS_TYPE_DATA = 0x46, // Data FIS - bidirectional - FIS_TYPE_BIST = 0x58, // BIST activate FIS - bidirectional - FIS_TYPE_PIO_SETUP = 0x5F, // PIO setup FIS - device to host - FIS_TYPE_DEV_BITS = 0xA1, // Set device bits FIS - device to host -} FIS_TYPE; - -struct DmaFis { - // DWORD 0 - uint8_t fis_type; // FIS_TYPE_DMA_SETUP - - uint8_t pmport : 4; // Port multiplier - uint8_t rsv0 : 1; // Reserved - uint8_t d : 1; // Data transfer direction, 1 - device to host - uint8_t i : 1; // Interrupt bit - uint8_t a : 1; // Auto-activate. Specifies if DMA Activate FIS is needed - - uint8_t rsved[2]; // Reserved - - // DWORD 1&2 - - uint64_t DMAbufferID; // DMA Buffer Identifier. Used to Identify DMA buffer - // in host memory. SATA Spec says host specific and not - // in Spec. Trying AHCI spec might work. - - // DWORD 3 - uint32_t rsvd; // More reserved - - // DWORD 4 - uint32_t DMAbufOffset; // Byte offset into buffer. First 2 bits must be 0 - - // DWORD 5 - uint32_t TransferCount; // Number of bytes to transfer. Bit 0 must be 0 - - // DWORD 6 - uint32_t resvd; // Reserved -} __attribute__((packed)); - -struct PioSetupFis { - // DWORD 0 - uint8_t fis_type; // FIS_TYPE_PIO_SETUP - - uint8_t pmport : 4; // Port multiplier - uint8_t rsv0 : 1; // Reserved - uint8_t d : 1; // Data transfer direction, 1 - device to host - uint8_t i : 1; // Interrupt bit - uint8_t rsv1 : 1; - - uint8_t status; // Status register - uint8_t error; // Error register - - // DWORD 1 - uint8_t lba0; // LBA low register, 7:0 - uint8_t lba1; // LBA mid register, 15:8 - uint8_t lba2; // LBA high register, 23:16 - uint8_t device; // Device register - - // DWORD 2 - uint8_t lba3; // LBA register, 31:24 - uint8_t lba4; // LBA register, 39:32 - uint8_t lba5; // LBA register, 47:40 - uint8_t rsv2; // Reserved - - // DWORD 3 - uint8_t countl; // Count register, 7:0 - uint8_t counth; // Count register, 15:8 - uint8_t rsv3; // Reserved - uint8_t e_status; // New value of status register - - // DWORD 4 - uint16_t tc; // Transfer count - uint8_t rsv4[2]; // Reserved -} __attribute__((packed)); - -struct HostToDeviceRegisterFis { - uint8_t fis_type; // FIS_TYPE_REG_H2D - uint8_t pmp_and_c; - uint8_t command; // Command register - uint8_t featurel; // Feature register, 7:0 - - // DWORD 1 - uint8_t lba0; // LBA low register, 7:0 - uint8_t lba1; // LBA mid register, 15:8 - uint8_t lba2; // LBA high register, 23:16 - uint8_t device; // Device register - - // DWORD 2 - uint8_t lba3; // LBA register, 31:24 - uint8_t lba4; // LBA register, 39:32 - uint8_t lba5; // LBA register, 47:40 - uint8_t featureh; // Feature register, 15:8 - - // DWORD 3 - uint16_t count; - uint8_t icc; // Isochronous command completion - uint8_t control; // Control register - - // DWORD 4 - uint32_t reserved; // Reserved -} __attribute__((packed)); -struct DeviceToHostRegisterFis { - // DWORD 0 - uint8_t fis_type; // FIS_TYPE_REG_D2H - - uint8_t pmport_and_i; - - uint8_t status; // Status register - uint8_t error; // Error register - - // DWORD 1 - uint8_t lba0; // LBA low register, 7:0 - uint8_t lba1; // LBA mid register, 15:8 - uint8_t lba2; // LBA high register, 23:16 - uint8_t device; // Device register - - // DWORD 2 - uint8_t lba3; // LBA register, 31:24 - uint8_t lba4; // LBA register, 39:32 - uint8_t lba5; // LBA register, 47:40 - uint8_t reserved1; - - // DWORD 3 - uint16_t count; - uint16_t reserved2; - - uint32_t reserved3; -} __attribute__((packed)); -struct SetDeviceBitsFis { -} __attribute__((packed)); - -struct ReceivedFis { - DmaFis dma_fis; - uint32_t reserved0; - - PioSetupFis pio_set_fis; - uint32_t reserved1[3]; - - DeviceToHostRegisterFis device_to_host_register_fis; - uint32_t reserved2; - - SetDeviceBitsFis set_device_bits_fis; - uint8_t unknown_fis[64]; -} __attribute__((packed)); diff --git a/sys/denali/ahci/ahci_device.cpp b/sys/denali/ahci/ahci_device.cpp deleted file mode 100644 index 994f4db..0000000 --- a/sys/denali/ahci/ahci_device.cpp +++ /dev/null @@ -1,120 +0,0 @@ -#include "ahci/ahci_device.h" - -#include -#include -#include - -AhciDevice::AhciDevice(AhciPort* port) : port_struct_(port) { - if ((port_struct_->sata_status & 0x103) != 0x103) { - return; - } - - // 0x0-0x400 -> Command List - // 0x400-0x500 -> Received FIS - // 0x500-0x2500 -> Command Tables (0x100 each) (Max PRDT Length is 8 for now) - uint64_t paddr; - command_structures_ = - mmth::OwnedMemoryRegion::ContiguousPhysical(0x2500, &paddr); - - command_list_ = reinterpret_cast(command_structures_.vaddr()); - port_struct_->command_list_base = paddr; - - received_fis_ = - reinterpret_cast(command_structures_.vaddr() + 0x400); - port_struct_->fis_base = paddr + 0x400; - - command_tables_ = - reinterpret_cast(command_structures_.vaddr() + 0x500); - - for (uint64_t i = 0; i < 32; i++) { - command_list_->command_headers[i].command_table_base_addr = - (paddr + 0x500) + (0x100 * i); - } - port_struct_->interrupt_enable = 0xFFFFFFFF; - // Reset the CMD and FRE bits since we move these structures. - // FIXME: I think we need to poll these bits to make sure they become - // 0 before setting them back to one. - port_struct_->command &= ~(0x00000011); - port_struct_->command |= 0x00000011; -} - -glcr::ErrorCode AhciDevice::IssueCommand(Command* command) { - command->PopulateFis(command_tables_->command_fis); - command->PopulatePrdt(command_tables_->prdt); - - command_list_->command_headers[0].command = - (sizeof(HostToDeviceRegisterFis) / 2) & 0x1F; - command_list_->command_headers[0].prd_table_length = 1; - command_list_->command_headers[0].prd_byte_count = 0; - - commands_[0] = command; - - commands_issued_ |= 1; - port_struct_->command_issue |= 1; - - return glcr::OK; -} - -void AhciDevice::DumpInfo() { - dbgln("Comlist: {x}", port_struct_->command_list_base); - dbgln("FIS: {x}", port_struct_->fis_base); - dbgln("Command: {x}", port_struct_->command); - dbgln("Signature: {x}", port_struct_->signature); - dbgln("SATA status: {x}", port_struct_->sata_status); - dbgln("Int status: {x}", port_struct_->interrupt_status); - dbgln("Int enable: {x}", port_struct_->interrupt_enable); - - // Just dump one command info for now. - for (uint64_t i = 0; i < 32; i++) { - dbgln("Command Header: {}", i); - dbgln("Command {x}", command_list_->command_headers[i].command); - dbgln("PRD Len: {x}", command_list_->command_headers[i].prd_table_length); - dbgln("Command Table {x}", - command_list_->command_headers[i].command_table_base_addr); - } -} - -void AhciDevice::HandleIrq() { - uint32_t int_status = port_struct_->interrupt_status; - // FIXME: Probably only clear the interrupts we know how to handle. - port_struct_->interrupt_status = int_status; - - uint32_t commands_finished = commands_issued_ & ~port_struct_->command_issue; - - // FIXME: Pass error codes to the callback. - for (uint64_t i = 0; i < 32; i++) { - if (commands_finished & (1 << i)) { - commands_issued_ &= ~(1 << i); - commands_[i]->SignalComplete(); - } - } - - // TODO: Do something with this information. - if (int_status & 0x1) { - // Device to host. - DeviceToHostRegisterFis& fis = received_fis_->device_to_host_register_fis; - if (fis.fis_type != FIS_TYPE_REG_D2H) { - dbgln("BAD FIS TYPE (exp,act): {x}, {x}", - static_cast(FIS_TYPE_REG_D2H), - static_cast(fis.fis_type)); - return; - } - if (fis.error) { - dbgln("D2H err: {x}", fis.error); - dbgln("status: {x}", fis.status); - } - } - if (int_status & 0x2) { - // PIO. - PioSetupFis& fis = received_fis_->pio_set_fis; - if (fis.fis_type != FIS_TYPE_PIO_SETUP) { - dbgln("BAD FIS TYPE (exp,act): {x}, {x}", - static_cast(FIS_TYPE_PIO_SETUP), - static_cast(fis.fis_type)); - return; - } - if (fis.error) { - dbgln("PIO err: {x}", fis.error); - } - } -} diff --git a/sys/denali/ahci/ahci_device.h b/sys/denali/ahci/ahci_device.h deleted file mode 100644 index bc4fc94..0000000 --- a/sys/denali/ahci/ahci_device.h +++ /dev/null @@ -1,37 +0,0 @@ -#pragma once - -#include -#include -#include - -#include "ahci/ahci.h" -#include "ahci/command.h" - -class AhciDevice { - public: - AhciDevice() {} - // Caller retains ownership of the pointer. - AhciDevice(AhciPort* port_struct); - - void DumpInfo(); - - bool IsInit() { return port_struct_ != nullptr && command_structures_; } - - glcr::ErrorCode IssueCommand(Command* command); - - void HandleIrq(); - - AhciDevice(const AhciDevice&) = delete; - AhciDevice& operator=(const AhciDevice&) = delete; - - private: - AhciPort* port_struct_ = nullptr; - mmth::OwnedMemoryRegion command_structures_; - - CommandList* command_list_ = nullptr; - ReceivedFis* received_fis_ = nullptr; - CommandTable* command_tables_ = nullptr; - - Command* commands_[32]; - volatile uint32_t commands_issued_ = 0; -}; diff --git a/sys/denali/ahci/ahci_driver.cpp b/sys/denali/ahci/ahci_driver.cpp deleted file mode 100644 index 64b340d..0000000 --- a/sys/denali/ahci/ahci_driver.cpp +++ /dev/null @@ -1,229 +0,0 @@ -#include "ahci/ahci_driver.h" - -#include -#include -#include -#include -#include - -namespace { - -const uint64_t kGhc_InteruptEnable = 0x2; - -void interrupt_thread(void* void_driver) { - AhciDriver* driver = static_cast(void_driver); - - driver->InterruptLoop(); - - crash("Driver returned from interrupt loop", glcr::INTERNAL); -} - -} // namespace - -glcr::ErrorOr> AhciDriver::Init( - mmth::OwnedMemoryRegion&& pci_region) { - glcr::UniquePtr driver(new AhciDriver(glcr::Move(pci_region))); - // RET_ERR(driver->LoadCapabilities()); - RET_ERR(driver->LoadHbaRegisters()); - RET_ERR(driver->LoadDevices()); - RET_ERR(driver->RegisterIrq()); - // driver->DumpCapabilities(); - // driver->DumpPorts(); - return driver; -} - -glcr::ErrorOr AhciDriver::GetDevice(uint64_t id) { - if (id >= 32) { - return glcr::INVALID_ARGUMENT; - } - - if (devices_[id] != nullptr && !devices_[id]->IsInit()) { - return glcr::NOT_FOUND; - } - - return devices_[id]; -} - -void AhciDriver::DumpCapabilities() { - dbgln("AHCI Capabilities:"); - uint32_t caps = ahci_hba_->capabilities; - - if (caps & 0x20) { - dbgln("External SATA"); - } - if (caps & 0x40) { - dbgln("Enclosure Management"); - } - if (caps & 0x80) { - dbgln("Command Completion Coalescing"); - } - if (caps & 0x2000) { - dbgln("Partial State Capable"); - } - if (caps & 0x4000) { - dbgln("Slumber state capable"); - } - if (caps & 0x8000) { - dbgln("PIO Multiple DRQ Block"); - } - if (caps & 0x1'0000) { - dbgln("FIS-Based Switching"); - } - if (caps & 0x2'0000) { - dbgln("Port Multiplier"); - } - if (caps & 0x4'0000) { - dbgln("AHCI mode only"); - } - dbgln("Speed support: {}", (caps & 0xF0'0000) >> 20); - if (caps & 0x100'0000) { - dbgln("Command list override"); - } - if (caps & 0x200'0000) { - dbgln("Activity LED"); - } - if (caps & 0x400'0000) { - dbgln("Aggresive link power management"); - } - if (caps & 0x800'0000) { - dbgln("Staggered spin up"); - } - if (caps & 0x1000'0000) { - dbgln("Mechanical Switch Presence"); - } - if (caps & 0x2000'0000) { - dbgln("SNotification Register"); - } - if (caps & 0x4000'0000) { - dbgln("Native Command Queueing"); - } - if (caps & 0x8000'0000) { - dbgln("64bit Addressing"); - } - - // Secondary. - caps = ahci_hba_->capabilities_ext; - if (caps & 0x1) { - dbgln("BIOS/OS handoff"); - } - if (caps & 0x2) { - dbgln("NVMHCI Present"); - } - if (caps & 0x4) { - dbgln("Auto partial to slumber tranisitions"); - } - if (caps & 0x8) { - dbgln("Device sleep"); - } - if (caps & 0x10) { - dbgln("Aggressive device sleep management"); - } - - dbgln("Control {x}", ahci_hba_->global_host_control); -} - -void AhciDriver::DumpPorts() { - for (uint64_t i = 0; i < 6; i++) { - AhciDevice* dev = devices_[i]; - if (dev == nullptr || !dev->IsInit()) { - continue; - } - - dbgln(""); - dbgln("Port {}:", i); - dev->DumpInfo(); - } -} - -void AhciDriver::InterruptLoop() { - dbgln("Starting interrupt loop"); - while (true) { - uint64_t bytes, caps; - check(ZPortRecv(irq_port_cap_, &bytes, nullptr, &caps, nullptr)); - for (uint64_t i = 0; i < 32; i++) { - if (devices_[i] != nullptr && devices_[i]->IsInit() && - (ahci_hba_->interrupt_status & (1 << i))) { - devices_[i]->HandleIrq(); - ahci_hba_->interrupt_status &= ~(1 << i); - } - } - } -} - -glcr::ErrorCode AhciDriver::LoadCapabilities() { - if (!(pci_device_header_->status_reg & 0x10)) { - dbgln("No caps!"); - return glcr::FAILED_PRECONDITION; - } - uint8_t* base = reinterpret_cast(pci_device_header_); - uint16_t offset = pci_device_header_->cap_ptr; - do { - uint16_t* cap = reinterpret_cast(base + offset); - switch (*cap & 0xFF) { - case 0x01: - dbgln("Power Management"); - break; - case 0x05: - dbgln("MSI"); - break; - case 0x12: - dbgln("SATA"); - break; - default: - dbgln("Unrecognized cap"); - break; - } - - offset = (*cap & 0xFF00) >> 8; - } while (offset); - return glcr::OK; -} - -glcr::ErrorCode AhciDriver::RegisterIrq() { - if (pci_device_header_->interrupt_pin == 0) { - crash("Can't register IRQ without a pin num", glcr::INVALID_ARGUMENT); - } - uint64_t irq_num = 0; - switch (pci_device_header_->interrupt_pin) { - case 1: - irq_num = kZIrqPci1; - break; - case 2: - irq_num = kZIrqPci2; - break; - case 3: - irq_num = kZIrqPci3; - break; - case 4: - irq_num = kZIrqPci4; - break; - } - - RET_ERR(ZIrqRegister(irq_num, &irq_port_cap_)); - irq_thread_ = Thread(interrupt_thread, this); - ahci_hba_->global_host_control |= kGhc_InteruptEnable; - return glcr::OK; -} - -glcr::ErrorCode AhciDriver::LoadHbaRegisters() { - ahci_region_ = mmth::OwnedMemoryRegion ::DirectPhysical( - pci_device_header_->abar, 0x1100); - ahci_hba_ = reinterpret_cast(ahci_region_.vaddr()); - num_ports_ = (ahci_hba_->capabilities & 0x1F) + 1; - num_commands_ = ((ahci_hba_->capabilities & 0x1F00) >> 8) + 1; - - return glcr::OK; -} - -glcr::ErrorCode AhciDriver::LoadDevices() { - for (uint8_t i = 0; i < 32; i++) { - if (!(ahci_hba_->port_implemented & (1 << i))) { - devices_[i] = nullptr; - continue; - } - uint64_t port_addr = - reinterpret_cast(ahci_hba_) + 0x100 + (0x80 * i); - devices_[i] = new AhciDevice(reinterpret_cast(port_addr)); - } - return glcr::OK; -} diff --git a/sys/denali/ahci/ahci_driver.h b/sys/denali/ahci/ahci_driver.h deleted file mode 100644 index 0713861..0000000 --- a/sys/denali/ahci/ahci_driver.h +++ /dev/null @@ -1,47 +0,0 @@ -#pragma once - -#include -#include -#include -#include - -#include "ahci/ahci.h" -#include "ahci/ahci_device.h" - -class AhciDriver { - public: - static glcr::ErrorOr> Init( - mmth::OwnedMemoryRegion&& ahci_phys); - glcr::ErrorCode RegisterIrq(); - - void InterruptLoop(); - - glcr::ErrorOr GetDevice(uint64_t id); - - void DumpCapabilities(); - void DumpPorts(); - - private: - mmth::OwnedMemoryRegion pci_region_; - PciDeviceHeader* pci_device_header_ = nullptr; - mmth::OwnedMemoryRegion ahci_region_; - AhciHba* ahci_hba_ = nullptr; - - // TODO: Allocate these dynamically. - AhciDevice* devices_[32]; - - Thread irq_thread_; - uint64_t irq_port_cap_ = 0; - - uint64_t num_ports_; - uint64_t num_commands_; - - glcr::ErrorCode LoadCapabilities(); - glcr::ErrorCode LoadHbaRegisters(); - glcr::ErrorCode LoadDevices(); - - AhciDriver(mmth::OwnedMemoryRegion&& pci_region) - : pci_region_(glcr::Move(pci_region)), - pci_device_header_( - reinterpret_cast(pci_region_.vaddr())) {} -}; diff --git a/sys/denali/ahci/command.cpp b/sys/denali/ahci/command.cpp deleted file mode 100644 index eea4ad0..0000000 --- a/sys/denali/ahci/command.cpp +++ /dev/null @@ -1,63 +0,0 @@ -#include "ahci/command.h" - -#include "ahci/ahci.h" - -namespace { - -void* memcpy(void* dest, const void* src, uint64_t count) { - uint8_t* d = (uint8_t*)dest; - const uint8_t* s = (uint8_t*)src; - for (uint64_t i = 0; i < count; i++) { - d[i] = s[i]; - } - return dest; -} - -} // namespace - -Command::~Command() {} - -DmaReadCommand::DmaReadCommand(uint64_t lba, uint64_t sector_cnt, - uint64_t paddr) - : lba_(lba), - sector_cnt_(sector_cnt), - paddr_(paddr), - callback_semaphore_() {} - -DmaReadCommand::~DmaReadCommand() {} - -void DmaReadCommand::PopulateFis(uint8_t* command_fis) { - HostToDeviceRegisterFis fis{ - .fis_type = FIS_TYPE_REG_H2D, - .pmp_and_c = 0x80, - .command = 0x25, - .featurel = 0, - - .lba0 = static_cast(lba_ & 0xFF), - .lba1 = static_cast((lba_ >> 8) & 0xFF), - .lba2 = static_cast((lba_ >> 16) & 0xFF), - .device = (1 << 6), // ATA LBA Mode - - .lba3 = static_cast((lba_ >> 24) & 0xFF), - .lba4 = static_cast((lba_ >> 32) & 0xFF), - .lba5 = static_cast((lba_ >> 40) & 0xFF), - .featureh = 0, - - .count = static_cast(sector_cnt_), - .icc = 0, - .control = 0, - - .reserved = 0, - }; - - uint64_t bytes = sector_cnt_ * 512; - - memcpy(command_fis, &fis, sizeof(fis)); -} -void DmaReadCommand::PopulatePrdt(PhysicalRegionDescriptor* prdt) { - prdt[0].region_address = paddr_; - prdt[0].byte_count = sector_cnt_ * 512; -} - -void DmaReadCommand::SignalComplete() { callback_semaphore_.Signal(); } -void DmaReadCommand::WaitComplete() { callback_semaphore_.Wait(); } diff --git a/sys/denali/ahci/command.h b/sys/denali/ahci/command.h deleted file mode 100644 index 61fef58..0000000 --- a/sys/denali/ahci/command.h +++ /dev/null @@ -1,36 +0,0 @@ -#pragma once - -#include -#include - -#include "ahci/ahci.h" - -class Command { - public: - virtual ~Command(); - virtual void PopulateFis(uint8_t* command_fis) = 0; - virtual void PopulatePrdt(PhysicalRegionDescriptor* prdt) = 0; - virtual void WaitComplete() = 0; - virtual void SignalComplete() = 0; -}; - -class DmaReadCommand : public Command { - public: - DmaReadCommand(uint64_t lba, uint64_t sector_cnt, uint64_t dest_paddr); - - virtual ~DmaReadCommand() override; - - void PopulateFis(uint8_t* command_fis) override; - void PopulatePrdt(PhysicalRegionDescriptor* prdt) override; - - void WaitComplete() override; - void SignalComplete() override; - - private: - uint64_t lba_; - uint64_t sector_cnt_; - uint64_t paddr_; - // TODO: Make this owned by the device so that we don't have to create a new - // one with the kernel every time a command is issued. - mmth::Semaphore callback_semaphore_; -}; diff --git a/sys/denali/denali.cpp b/sys/denali/denali.cpp deleted file mode 100644 index 4173651..0000000 --- a/sys/denali/denali.cpp +++ /dev/null @@ -1,37 +0,0 @@ - -#include -#include -#include -#include - -#include "ahci/ahci_driver.h" -#include "denali_server.h" - -using yellowstone::AhciInfo; -using yellowstone::RegisterEndpointRequest; -using yellowstone::YellowstoneClient; - -uint64_t main(uint64_t init_port_cap) { - check(ParseInitPort(init_port_cap)); - - YellowstoneClient stub(gInitEndpointCap); - AhciInfo ahci; - check(stub.GetAhciInfo(ahci)); - mmth::OwnedMemoryRegion ahci_region = - mmth::OwnedMemoryRegion::FromCapability(ahci.ahci_region()); - ASSIGN_OR_RETURN(auto driver, AhciDriver::Init(glcr::Move(ahci_region))); - - ASSIGN_OR_RETURN(glcr::UniquePtr server, - DenaliServer::Create(*driver)); - - Thread server_thread = server->RunServer(); - - RegisterEndpointRequest req; - req.set_endpoint_name("denali"); - ASSIGN_OR_RETURN(z_cap_t client_cap, server->CreateClientCap()); - req.set_endpoint_capability(client_cap); - check(stub.RegisterEndpoint(req)); - - check(server_thread.Join()); - return 0; -} diff --git a/sys/denali/denali_server.cpp b/sys/denali/denali_server.cpp deleted file mode 100644 index 0c397c4..0000000 --- a/sys/denali/denali_server.cpp +++ /dev/null @@ -1,64 +0,0 @@ -#include "denali_server.h" - -#include -#include -#include -#include - -glcr::ErrorOr> DenaliServer::Create( - AhciDriver& driver) { - z_cap_t cap; - RET_ERR(ZEndpointCreate(&cap)); - return glcr::UniquePtr(new DenaliServer(cap, driver)); -} - -glcr::Status DenaliServer::HandleRead(const ReadRequest& req, - ReadResponse& resp) { - ASSIGN_OR_RETURN(AhciDevice * device, driver_.GetDevice(req.device_id())); - - uint64_t paddr; - mmth::OwnedMemoryRegion region = - mmth::OwnedMemoryRegion::ContiguousPhysical(req.size() * 512, &paddr); - - DmaReadCommand command(req.lba(), req.size(), paddr); - device->IssueCommand(&command); - - command.WaitComplete(); - - resp.set_device_id(req.device_id()); - resp.set_size(req.size()); - resp.set_memory(region.DuplicateCap()); - return glcr::Status::Ok(); -} - -glcr::Status DenaliServer::HandleReadMany(const ReadManyRequest& req, - ReadResponse& resp) { - ASSIGN_OR_RETURN(AhciDevice * device, driver_.GetDevice(req.device_id())); - - if (req.lba().size() != req.sector_cnt().size()) { - return glcr::InvalidArgument("LBA and Sector Cnt must be the same length."); - } - - uint64_t sector_cnt = 0; - for (uint64_t i = 0; i < req.sector_cnt().size(); i++) { - sector_cnt += req.sector_cnt().at(i); - } - uint64_t region_paddr; - mmth::OwnedMemoryRegion region = mmth::OwnedMemoryRegion::ContiguousPhysical( - sector_cnt * 512, ®ion_paddr); - - for (uint64_t i = 0; i < req.lba().size(); i++) { - uint64_t lba = req.lba().at(i); - uint64_t size = req.sector_cnt().at(i); - DmaReadCommand command(lba, size, region_paddr); - device->IssueCommand(&command); - command.WaitComplete(); - - region_paddr += size * 512; - } - - resp.set_device_id(req.device_id()); - resp.set_size(sector_cnt); - resp.set_memory(region.DuplicateCap()); - return glcr::Status::Ok(); -} diff --git a/sys/denali/denali_server.h b/sys/denali/denali_server.h deleted file mode 100644 index 483fabe..0000000 --- a/sys/denali/denali_server.h +++ /dev/null @@ -1,25 +0,0 @@ -#pragma once - -#include - -#include "ahci/ahci_driver.h" -#include "lib/denali/denali.yunq.server.h" - -class DenaliServer : public DenaliServerBase { - public: - static glcr::ErrorOr> Create( - AhciDriver& driver); - - glcr::Status HandleRead(const ReadRequest& req, ReadResponse& resp) override; - glcr::Status HandleReadMany(const ReadManyRequest& req, - ReadResponse& resp) override; - - private: - static const uint64_t kBuffSize = 1024; - uint8_t read_buffer_[kBuffSize]; - - AhciDriver& driver_; - - DenaliServer(z_cap_t endpoint_cap, AhciDriver& driver) - : DenaliServerBase(endpoint_cap), driver_(driver) {} -}; diff --git a/sys/denali/lib/denali/denali.yunq.client.cpp b/sys/denali/lib/denali/denali.yunq.client.cpp deleted file mode 100644 index af49317..0000000 --- a/sys/denali/lib/denali/denali.yunq.client.cpp +++ /dev/null @@ -1,98 +0,0 @@ -// Generated file - DO NOT MODIFY -#include "denali.yunq.client.h" - -#include -#include -#include -#include - - - - -DenaliClient::~DenaliClient() { - if (endpoint_ != 0) { - check(ZCapRelease(endpoint_)); - } -} - - - - -glcr::Status DenaliClient::Read(const ReadRequest& request, ReadResponse& response) { - - uint64_t buffer_size = kBufferSize; - uint64_t cap_size = kCapBufferSize; - - const uint32_t kSentinel = 0xBEEFDEAD; - buffer_.WriteAt(0, kSentinel); - buffer_.WriteAt(8, 0); - - cap_buffer_.Reset(); - - uint64_t length = request.SerializeToBytes(buffer_, /*offset=*/16, cap_buffer_); - - - buffer_.WriteAt(4, 16 + length); - - z_cap_t reply_port_cap; - RET_ERR(ZEndpointSend(endpoint_, 16 + length, buffer_.RawPtr(), cap_buffer_.UsedSlots(), cap_buffer_.RawPtr(), &reply_port_cap)); - - // FIXME: Add a way to zero out the first buffer. - RET_ERR(ZReplyPortRecv(reply_port_cap, &buffer_size, buffer_.RawPtr(), &cap_size, cap_buffer_.RawPtr())); - - if (buffer_.At(0) != kSentinel) { - return glcr::InvalidResponse("Got an invalid response from server."); - } - - // Check Response Code. - RET_ERR(buffer_.At(8)); - - - RETURN_ERROR(response.ParseFromBytes(buffer_, 16, cap_buffer_)); - - - return glcr::OK; -} - - - - -glcr::Status DenaliClient::ReadMany(const ReadManyRequest& request, ReadResponse& response) { - - uint64_t buffer_size = kBufferSize; - uint64_t cap_size = kCapBufferSize; - - const uint32_t kSentinel = 0xBEEFDEAD; - buffer_.WriteAt(0, kSentinel); - buffer_.WriteAt(8, 1); - - cap_buffer_.Reset(); - - uint64_t length = request.SerializeToBytes(buffer_, /*offset=*/16, cap_buffer_); - - - buffer_.WriteAt(4, 16 + length); - - z_cap_t reply_port_cap; - RET_ERR(ZEndpointSend(endpoint_, 16 + length, buffer_.RawPtr(), cap_buffer_.UsedSlots(), cap_buffer_.RawPtr(), &reply_port_cap)); - - // FIXME: Add a way to zero out the first buffer. - RET_ERR(ZReplyPortRecv(reply_port_cap, &buffer_size, buffer_.RawPtr(), &cap_size, cap_buffer_.RawPtr())); - - if (buffer_.At(0) != kSentinel) { - return glcr::InvalidResponse("Got an invalid response from server."); - } - - // Check Response Code. - RET_ERR(buffer_.At(8)); - - - RETURN_ERROR(response.ParseFromBytes(buffer_, 16, cap_buffer_)); - - - return glcr::OK; -} - - - - diff --git a/sys/denali/lib/denali/denali.yunq.client.h b/sys/denali/lib/denali/denali.yunq.client.h deleted file mode 100644 index 46fd28d..0000000 --- a/sys/denali/lib/denali/denali.yunq.client.h +++ /dev/null @@ -1,37 +0,0 @@ -// Generated file - DO NOT MODIFY -#pragma once - -#include -#include -#include -#include - -#include "denali.yunq.h" - - -class DenaliClient { - public: - DenaliClient(z_cap_t Denali_cap) : endpoint_(Denali_cap) {} - DenaliClient(const DenaliClient&) = delete; - DenaliClient(DenaliClient&& other) : endpoint_(other.endpoint_) {other.endpoint_ = 0;}; - ~DenaliClient(); - - z_cap_t Capability() { return endpoint_; } - - - - [[nodiscard]] glcr::Status Read(const ReadRequest& request, ReadResponse& response); - - - - [[nodiscard]] glcr::Status ReadMany(const ReadManyRequest& request, ReadResponse& response); - - - private: - z_cap_t endpoint_; - uint64_t kBufferSize = 0x1000; - glcr::ByteBuffer buffer_{kBufferSize}; - uint64_t kCapBufferSize = 0x10; - glcr::CapBuffer cap_buffer_{kCapBufferSize}; -}; - diff --git a/sys/denali/lib/denali/denali.yunq.cpp b/sys/denali/lib/denali/denali.yunq.cpp deleted file mode 100644 index 0f6646b..0000000 --- a/sys/denali/lib/denali/denali.yunq.cpp +++ /dev/null @@ -1,248 +0,0 @@ -// Generated file -- DO NOT MODIFY. -#include "denali.yunq.h" - -#include - - -namespace { - -const uint64_t header_size = 24; // 4x uint32, 1x uint64 - -struct ExtPointer { - uint32_t offset; - uint32_t length; -}; - -} // namespace -glcr::Status ReadRequest::ParseFromBytes(const glcr::ByteBuffer& bytes, uint64_t offset) { - RETURN_ERROR(ParseFromBytesInternal(bytes, offset)); - return glcr::Status::Ok(); -} - -glcr::Status ReadRequest::ParseFromBytes(const glcr::ByteBuffer& bytes, uint64_t offset, const glcr::CapBuffer& caps) { - RETURN_ERROR(ParseFromBytesInternal(bytes, offset)); - return glcr::Status::Ok(); -} - -glcr::Status ReadRequest::ParseFromBytesInternal(const glcr::ByteBuffer& bytes, uint64_t offset) { - RETURN_ERROR(yunq::CheckHeader(bytes, offset)); - // Parse device_id. - set_device_id(bytes.At(offset + header_size + (8 * 0))); - // Parse lba. - set_lba(bytes.At(offset + header_size + (8 * 1))); - // Parse size. - set_size(bytes.At(offset + header_size + (8 * 2))); - - return glcr::Status::Ok(); -} - -uint64_t ReadRequest::SerializeToBytes(glcr::ByteBuffer& bytes, uint64_t offset) const { - uint32_t next_extension = header_size + 8 * 3; - const uint32_t core_size = next_extension; - // Write device_id. - bytes.WriteAt(offset + header_size + (8 * 0), device_id()); - // Write lba. - bytes.WriteAt(offset + header_size + (8 * 1), lba()); - // Write size. - bytes.WriteAt(offset + header_size + (8 * 2), size()); - - // The next extension pointer is the length of the message. - yunq::WriteHeader(bytes, offset, core_size, next_extension); - - return next_extension; -} - -uint64_t ReadRequest::SerializeToBytes(glcr::ByteBuffer& bytes, uint64_t offset, glcr::CapBuffer& caps) const { - uint32_t next_extension = header_size + 8 * 3; - const uint32_t core_size = next_extension; - uint64_t next_cap = 0; - // Write device_id. - bytes.WriteAt(offset + header_size + (8 * 0), device_id()); - // Write lba. - bytes.WriteAt(offset + header_size + (8 * 1), lba()); - // Write size. - bytes.WriteAt(offset + header_size + (8 * 2), size()); - - // The next extension pointer is the length of the message. - yunq::WriteHeader(bytes, offset, core_size, next_extension); - - return next_extension; -} -glcr::Status ReadManyRequest::ParseFromBytes(const glcr::ByteBuffer& bytes, uint64_t offset) { - RETURN_ERROR(ParseFromBytesInternal(bytes, offset)); - return glcr::Status::Ok(); -} - -glcr::Status ReadManyRequest::ParseFromBytes(const glcr::ByteBuffer& bytes, uint64_t offset, const glcr::CapBuffer& caps) { - RETURN_ERROR(ParseFromBytesInternal(bytes, offset)); - return glcr::Status::Ok(); -} - -glcr::Status ReadManyRequest::ParseFromBytesInternal(const glcr::ByteBuffer& bytes, uint64_t offset) { - RETURN_ERROR(yunq::CheckHeader(bytes, offset)); - // Parse device_id. - set_device_id(bytes.At(offset + header_size + (8 * 0))); - // Parse lba. - auto lba_pointer = bytes.At(offset + header_size + (8 * 1)); - - lba_.Resize(lba_pointer.length / sizeof(uint64_t)); - for (uint64_t i = offset + lba_pointer.offset; - i < offset + lba_pointer.offset + lba_pointer.length; - i += sizeof(uint64_t)) { - lba_.PushBack(bytes.At(i)); - } - - // Parse sector_cnt. - auto sector_cnt_pointer = bytes.At(offset + header_size + (8 * 2)); - - sector_cnt_.Resize(sector_cnt_pointer.length / sizeof(uint64_t)); - for (uint64_t i = offset + sector_cnt_pointer.offset; - i < offset + sector_cnt_pointer.offset + sector_cnt_pointer.length; - i += sizeof(uint64_t)) { - sector_cnt_.PushBack(bytes.At(i)); - } - - - return glcr::Status::Ok(); -} - -uint64_t ReadManyRequest::SerializeToBytes(glcr::ByteBuffer& bytes, uint64_t offset) const { - uint32_t next_extension = header_size + 8 * 3; - const uint32_t core_size = next_extension; - // Write device_id. - bytes.WriteAt(offset + header_size + (8 * 0), device_id()); - // Write lba. - ExtPointer lba_ptr{ - .offset = next_extension, - .length = (uint32_t)(lba().size() * sizeof(uint64_t)), - }; - - next_extension += lba_ptr.length; - bytes.WriteAt(offset + header_size + (8 * 1), lba_ptr); - - for (uint64_t i = 0; i < lba().size(); i++) { - uint32_t ext_offset = offset + lba_ptr.offset + (i * sizeof(uint64_t)); - bytes.WriteAt(ext_offset, lba().at(i)); - } - // Write sector_cnt. - ExtPointer sector_cnt_ptr{ - .offset = next_extension, - .length = (uint32_t)(sector_cnt().size() * sizeof(uint64_t)), - }; - - next_extension += sector_cnt_ptr.length; - bytes.WriteAt(offset + header_size + (8 * 2), sector_cnt_ptr); - - for (uint64_t i = 0; i < sector_cnt().size(); i++) { - uint32_t ext_offset = offset + sector_cnt_ptr.offset + (i * sizeof(uint64_t)); - bytes.WriteAt(ext_offset, sector_cnt().at(i)); - } - - // The next extension pointer is the length of the message. - yunq::WriteHeader(bytes, offset, core_size, next_extension); - - return next_extension; -} - -uint64_t ReadManyRequest::SerializeToBytes(glcr::ByteBuffer& bytes, uint64_t offset, glcr::CapBuffer& caps) const { - uint32_t next_extension = header_size + 8 * 3; - const uint32_t core_size = next_extension; - uint64_t next_cap = 0; - // Write device_id. - bytes.WriteAt(offset + header_size + (8 * 0), device_id()); - // Write lba. - ExtPointer lba_ptr{ - .offset = next_extension, - .length = (uint32_t)(lba().size() * sizeof(uint64_t)), - }; - - next_extension += lba_ptr.length; - bytes.WriteAt(offset + header_size + (8 * 1), lba_ptr); - - for (uint64_t i = 0; i < lba().size(); i++) { - uint32_t ext_offset = offset + lba_ptr.offset + (i * sizeof(uint64_t)); - bytes.WriteAt(ext_offset, lba().at(i)); - } - // Write sector_cnt. - ExtPointer sector_cnt_ptr{ - .offset = next_extension, - .length = (uint32_t)(sector_cnt().size() * sizeof(uint64_t)), - }; - - next_extension += sector_cnt_ptr.length; - bytes.WriteAt(offset + header_size + (8 * 2), sector_cnt_ptr); - - for (uint64_t i = 0; i < sector_cnt().size(); i++) { - uint32_t ext_offset = offset + sector_cnt_ptr.offset + (i * sizeof(uint64_t)); - bytes.WriteAt(ext_offset, sector_cnt().at(i)); - } - - // The next extension pointer is the length of the message. - yunq::WriteHeader(bytes, offset, core_size, next_extension); - - return next_extension; -} -glcr::Status ReadResponse::ParseFromBytes(const glcr::ByteBuffer& bytes, uint64_t offset) { - RETURN_ERROR(ParseFromBytesInternal(bytes, offset)); - // Parse memory. - // FIXME: Implement in-buffer capabilities for inprocess serialization. - set_memory(0); - return glcr::Status::Ok(); -} - -glcr::Status ReadResponse::ParseFromBytes(const glcr::ByteBuffer& bytes, uint64_t offset, const glcr::CapBuffer& caps) { - RETURN_ERROR(ParseFromBytesInternal(bytes, offset)); - // Parse memory. - uint64_t memory_ptr = bytes.At(offset + header_size + (8 * 2)); - - set_memory(caps.At(memory_ptr)); - return glcr::Status::Ok(); -} - -glcr::Status ReadResponse::ParseFromBytesInternal(const glcr::ByteBuffer& bytes, uint64_t offset) { - RETURN_ERROR(yunq::CheckHeader(bytes, offset)); - // Parse device_id. - set_device_id(bytes.At(offset + header_size + (8 * 0))); - // Parse size. - set_size(bytes.At(offset + header_size + (8 * 1))); - // Parse memory. - // Skip Cap. - - return glcr::Status::Ok(); -} - -uint64_t ReadResponse::SerializeToBytes(glcr::ByteBuffer& bytes, uint64_t offset) const { - uint32_t next_extension = header_size + 8 * 3; - const uint32_t core_size = next_extension; - // Write device_id. - bytes.WriteAt(offset + header_size + (8 * 0), device_id()); - // Write size. - bytes.WriteAt(offset + header_size + (8 * 1), size()); - // Write memory. - // FIXME: Implement inbuffer capabilities. - bytes.WriteAt(offset + header_size + (8 * 2), 0); - - // The next extension pointer is the length of the message. - yunq::WriteHeader(bytes, offset, core_size, next_extension); - - return next_extension; -} - -uint64_t ReadResponse::SerializeToBytes(glcr::ByteBuffer& bytes, uint64_t offset, glcr::CapBuffer& caps) const { - uint32_t next_extension = header_size + 8 * 3; - const uint32_t core_size = next_extension; - uint64_t next_cap = 0; - // Write device_id. - bytes.WriteAt(offset + header_size + (8 * 0), device_id()); - // Write size. - bytes.WriteAt(offset + header_size + (8 * 1), size()); - // Write memory. - caps.WriteAt(next_cap, memory()); - bytes.WriteAt(offset + header_size + (8 * 2), next_cap++); - - // The next extension pointer is the length of the message. - yunq::WriteHeader(bytes, offset, core_size, next_extension); - - return next_extension; -} - diff --git a/sys/denali/lib/denali/denali.yunq.h b/sys/denali/lib/denali/denali.yunq.h deleted file mode 100644 index 414c807..0000000 --- a/sys/denali/lib/denali/denali.yunq.h +++ /dev/null @@ -1,90 +0,0 @@ -// Generated file - DO NOT MODIFY -#pragma once - -#include -#include -#include -#include -#include -#include - - -class ReadRequest { - public: - ReadRequest() {} - // Delete copy and move until implemented. - ReadRequest(const ReadRequest&) = delete; - ReadRequest(ReadRequest&&) = delete; - - [[nodiscard]] glcr::Status ParseFromBytes(const glcr::ByteBuffer&, uint64_t offset); - [[nodiscard]] glcr::Status ParseFromBytes(const glcr::ByteBuffer&, uint64_t offset, const glcr::CapBuffer&); - uint64_t SerializeToBytes(glcr::ByteBuffer&, uint64_t offset) const; - uint64_t SerializeToBytes(glcr::ByteBuffer&, uint64_t offset, glcr::CapBuffer&) const; - const uint64_t& device_id() const { return device_id_; } - void set_device_id(const uint64_t& value) { device_id_ = value; } - const uint64_t& lba() const { return lba_; } - void set_lba(const uint64_t& value) { lba_ = value; } - const uint64_t& size() const { return size_; } - void set_size(const uint64_t& value) { size_ = value; } - - private: - uint64_t device_id_; - uint64_t lba_; - uint64_t size_; - - // Parses everything except for caps. - glcr::Status ParseFromBytesInternal(const glcr::ByteBuffer&, uint64_t offset); -}; -class ReadManyRequest { - public: - ReadManyRequest() {} - // Delete copy and move until implemented. - ReadManyRequest(const ReadManyRequest&) = delete; - ReadManyRequest(ReadManyRequest&&) = delete; - - [[nodiscard]] glcr::Status ParseFromBytes(const glcr::ByteBuffer&, uint64_t offset); - [[nodiscard]] glcr::Status ParseFromBytes(const glcr::ByteBuffer&, uint64_t offset, const glcr::CapBuffer&); - uint64_t SerializeToBytes(glcr::ByteBuffer&, uint64_t offset) const; - uint64_t SerializeToBytes(glcr::ByteBuffer&, uint64_t offset, glcr::CapBuffer&) const; - const uint64_t& device_id() const { return device_id_; } - void set_device_id(const uint64_t& value) { device_id_ = value; } - const glcr::Vector& lba() const { return lba_; } - void add_lba(const uint64_t& value) { lba_.PushBack(value); } - const glcr::Vector& sector_cnt() const { return sector_cnt_; } - void add_sector_cnt(const uint64_t& value) { sector_cnt_.PushBack(value); } - - private: - uint64_t device_id_; - glcr::Vector lba_; - glcr::Vector sector_cnt_; - - // Parses everything except for caps. - glcr::Status ParseFromBytesInternal(const glcr::ByteBuffer&, uint64_t offset); -}; -class ReadResponse { - public: - ReadResponse() {} - // Delete copy and move until implemented. - ReadResponse(const ReadResponse&) = delete; - ReadResponse(ReadResponse&&) = delete; - - [[nodiscard]] glcr::Status ParseFromBytes(const glcr::ByteBuffer&, uint64_t offset); - [[nodiscard]] glcr::Status ParseFromBytes(const glcr::ByteBuffer&, uint64_t offset, const glcr::CapBuffer&); - uint64_t SerializeToBytes(glcr::ByteBuffer&, uint64_t offset) const; - uint64_t SerializeToBytes(glcr::ByteBuffer&, uint64_t offset, glcr::CapBuffer&) const; - const uint64_t& device_id() const { return device_id_; } - void set_device_id(const uint64_t& value) { device_id_ = value; } - const uint64_t& size() const { return size_; } - void set_size(const uint64_t& value) { size_ = value; } - const z_cap_t& memory() const { return memory_; } - void set_memory(const z_cap_t& value) { memory_ = value; } - - private: - uint64_t device_id_; - uint64_t size_; - z_cap_t memory_; - - // Parses everything except for caps. - glcr::Status ParseFromBytesInternal(const glcr::ByteBuffer&, uint64_t offset); -}; - diff --git a/sys/denali/lib/denali/denali.yunq.server.cpp b/sys/denali/lib/denali/denali.yunq.server.cpp deleted file mode 100644 index 9f9764e..0000000 --- a/sys/denali/lib/denali/denali.yunq.server.cpp +++ /dev/null @@ -1,150 +0,0 @@ -// Generated file -- DO NOT MODIFY. -#include "denali.yunq.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 - - - -void DenaliServerBaseThreadBootstrap(void* server_base) { - ((DenaliServerBase*)server_base)->ServerThread(); -} - -DenaliServerBase::~DenaliServerBase() { - if (endpoint_ != 0) { - check(ZCapRelease(endpoint_)); - } -} - -glcr::ErrorOr DenaliServerBase::CreateClientCap() { - uint64_t client_cap; - RET_ERR(ZCapDuplicate(endpoint_, ~(kZionPerm_Read), &client_cap)); - return client_cap; -} - -glcr::ErrorOr DenaliServerBase::CreateClient() { - uint64_t client_cap; - RET_ERR(ZCapDuplicate(endpoint_, ~(kZionPerm_Read), &client_cap)); - return DenaliClient(client_cap); -} - -Thread DenaliServerBase::RunServer() { - return Thread(DenaliServerBaseThreadBootstrap, this); -} - -void DenaliServerBase::ServerThread() { - glcr::ByteBuffer recv_buffer(0x1000); - glcr::CapBuffer recv_cap(0x10); - glcr::ByteBuffer resp_buffer(0x1000); - glcr::CapBuffer resp_cap(0x10); - z_cap_t reply_port_cap; - - while (true) { - uint64_t recv_cap_size = 0x10; - uint64_t recv_buf_size = 0x1000; - recv_cap.Reset(); - glcr::ErrorCode recv_err = static_cast(ZEndpointRecv(endpoint_, &recv_buf_size, recv_buffer.RawPtr(), &recv_cap_size, recv_cap.RawPtr(), &reply_port_cap)); - if (recv_err != glcr::OK) { - dbgln("Error in receive: {x}", recv_err); - continue; - } - - uint64_t resp_length = 0; - - glcr::ErrorCode reply_err = glcr::OK; - resp_cap.Reset(); - glcr::Status err = HandleRequest(recv_buffer, recv_cap, resp_buffer, resp_length, resp_cap); - if (!err) { - WriteError(resp_buffer, err.code()); - dbgln("Responding Error {}", err.message()); - reply_err = static_cast(ZReplyPortSend(reply_port_cap, kHeaderSize, resp_buffer.RawPtr(), 0, nullptr)); - } else { - WriteHeader(resp_buffer, resp_length); - reply_err = static_cast(ZReplyPortSend(reply_port_cap, kHeaderSize + resp_length, resp_buffer.RawPtr(), resp_cap.UsedSlots(), resp_cap.RawPtr())); - } - if (reply_err != glcr::OK) { - dbgln("Error in reply: {x}", reply_err); - } - } - -} - -glcr::Status DenaliServerBase::HandleRequest(const glcr::ByteBuffer& request, - const glcr::CapBuffer& req_caps, - glcr::ByteBuffer& response, uint64_t& resp_length, - glcr::CapBuffer& resp_caps) { - if (request.At(0) != kSentinel) { - return glcr::InvalidArgument("Request Not Valid"); - } - - uint64_t method_select = request.At(8); - - switch(method_select) { - case 0: { - - - ReadRequest yunq_request; - RETURN_ERROR(yunq_request.ParseFromBytes(request, kHeaderSize, req_caps)); - - - - ReadResponse yunq_response; - - - - RETURN_ERROR(HandleRead(yunq_request, yunq_response)); - - - - resp_length = yunq_response.SerializeToBytes(response, kHeaderSize, resp_caps); - - break; - } - case 1: { - - - ReadManyRequest yunq_request; - RETURN_ERROR(yunq_request.ParseFromBytes(request, kHeaderSize, req_caps)); - - - - ReadResponse yunq_response; - - - - RETURN_ERROR(HandleReadMany(yunq_request, yunq_response)); - - - - resp_length = yunq_response.SerializeToBytes(response, kHeaderSize, resp_caps); - - break; - } - default: { - return glcr::Unimplemented("Method unimplemented by server."); - } - } - return glcr::Status::Ok(); -} - - diff --git a/sys/denali/lib/denali/denali.yunq.server.h b/sys/denali/lib/denali/denali.yunq.server.h deleted file mode 100644 index 5dc12e3..0000000 --- a/sys/denali/lib/denali/denali.yunq.server.h +++ /dev/null @@ -1,50 +0,0 @@ -// Generated File -- DO NOT MODIFY. -#pragma once - -#include -#include -#include -#include - -#include "denali.yunq.h" -#include "denali.yunq.client.h" - - - - - -class DenaliServerBase { - public: - DenaliServerBase(z_cap_t Denali_cap) : endpoint_(Denali_cap) {} - DenaliServerBase(const DenaliServerBase&) = delete; - DenaliServerBase(DenaliServerBase&&) = delete; - virtual ~DenaliServerBase(); - - glcr::ErrorOr CreateClientCap(); - glcr::ErrorOr CreateClient(); - - [[nodiscard]] Thread RunServer(); - - - - [[nodiscard]] virtual glcr::Status HandleRead(const ReadRequest&, ReadResponse&) = 0; - - - - [[nodiscard]] virtual glcr::Status HandleReadMany(const ReadManyRequest&, ReadResponse&) = 0; - - - - private: - z_cap_t endpoint_; - - friend void DenaliServerBaseThreadBootstrap(void*); - void ServerThread(); - - [[nodiscard]] glcr::Status HandleRequest(const glcr::ByteBuffer& request, const glcr::CapBuffer& req_caps, - glcr::ByteBuffer& response, uint64_t& resp_length, - glcr::CapBuffer& resp_caps); -}; - - - diff --git a/sys/teton/CMakeLists.txt b/sys/teton/CMakeLists.txt deleted file mode 100644 index 19a6706..0000000 --- a/sys/teton/CMakeLists.txt +++ /dev/null @@ -1,26 +0,0 @@ -add_executable(teton - framebuffer/console.cpp - framebuffer/framebuffer.cpp - framebuffer/psf.cpp - terminal.cpp - teton.cpp - ) - -target_include_directories(teton - PRIVATE ${CMAKE_CURRENT_SOURCE_DIR} - "${CMAKE_CURRENT_SOURCE_DIR}/include") - -target_link_libraries(teton - glacier - mammoth - victoriafalls_yunq - voyageurs_yunq - yellowstone_yunq - ) - -set_target_properties(teton PROPERTIES - COMPILE_FLAGS "${CMAKE_CXX_FLAGS} ${BASE_COMPILE_FLAGS}" - LINK_FLAGS "${CMAKE_EXE_LINK_FLAGS} ${BASE_LINK_FLAGS}" - ) - -install(TARGETS teton) diff --git a/sys/teton/framebuffer/console.cpp b/sys/teton/framebuffer/console.cpp deleted file mode 100644 index 7e0a02e..0000000 --- a/sys/teton/framebuffer/console.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include "framebuffer/console.h" - -#include - -void Console::WriteChar(char c) { - if (c == '\n') { - CursorReturn(); - return; - } - if (c == '\t') { - WriteString(" "); - return; - } - if (c == '\b') { - cursor_pos_--; - WriteChar(' '); - cursor_pos_--; - return; - } - - uint64_t row = cursor_pos_ / cols(); - if (row >= rows()) { - crash("Unimplemented console scroll.", glcr::UNIMPLEMENTED); - } - uint64_t fb_row = row * (psf_.height() + 1); - uint64_t col = cursor_pos_ % cols(); - uint64_t fb_col = col * (psf_.width() + 1); - - uint8_t* glyph = psf_.glyph(c); - - for (uint32_t r = fb_row; r < fb_row + psf_.height(); r++) { - for (uint32_t j = fb_col; j < fb_col + psf_.width(); j++) { - uint8_t glyph_offset = psf_.width() - (j - fb_col) - 1; - if ((glyph[r - fb_row] & (1 << glyph_offset))) { - framebuf_.DrawPixel(r, j, 0xFFFFFFF); - } else { - framebuf_.DrawPixel(r, j, 0); - } - } - } - - cursor_pos_++; -} - -void Console::WriteString(glcr::StringView str) { - for (uint64_t i = 0; i < str.size(); i++) { - WriteChar(str[i]); - } -} - -void Console::CursorReturn() { - cursor_pos_ -= cursor_pos_ % cols(); - cursor_pos_ += cols(); -} diff --git a/sys/teton/framebuffer/console.h b/sys/teton/framebuffer/console.h deleted file mode 100644 index 9afd2f4..0000000 --- a/sys/teton/framebuffer/console.h +++ /dev/null @@ -1,26 +0,0 @@ -#pragma once - -#include - -#include "framebuffer/framebuffer.h" -#include "framebuffer/psf.h" - -class Console { - public: - explicit Console(Framebuffer& fb, Psf& psf) : framebuf_(fb), psf_(psf) {} - - void WriteChar(char c); - void WriteString(glcr::StringView str); - - uint32_t rows() { return framebuf_.height() / (psf_.height() + 1); } - uint32_t cols() { return framebuf_.width() / (psf_.width() + 1); } - - private: - // TODO: Don't store a reference here. - Framebuffer& framebuf_; - Psf& psf_; - uint64_t cursor_pos_ = 0; - - void CursorIncr(); - void CursorReturn(); -}; diff --git a/sys/teton/framebuffer/framebuffer.cpp b/sys/teton/framebuffer/framebuffer.cpp deleted file mode 100644 index 5960c84..0000000 --- a/sys/teton/framebuffer/framebuffer.cpp +++ /dev/null @@ -1,29 +0,0 @@ -#include "framebuffer/framebuffer.h" - -Framebuffer::Framebuffer(const yellowstone::FramebufferInfo& info) - : fb_info_(info), cursor_pos_(0) { - uint64_t buff_size_bytes = fb_info_.height() * fb_info_.pitch(); - fb_memory_ = mmth::OwnedMemoryRegion::DirectPhysical(fb_info_.address_phys(), - buff_size_bytes); - fb_ = reinterpret_cast(fb_memory_.vaddr()); -} - -void Framebuffer::DrawPixel(uint32_t row, uint32_t col, uint32_t pixel) { - // Div by 4 because pitch is in bytes and fb_ is a 32bit array. - fb_[(row * fb_info_.pitch() / 4) + col] = pixel; -} - -void Framebuffer::DrawGlyph(uint8_t* glyph) { - uint32_t gl_width = 8; - uint32_t gl_height = 16; - - for (uint8_t r = 0; r < gl_height; r++) { - for (uint8_t c = 0; c < gl_width; c++) { - if (((glyph[r] >> c) % 2) == 1) { - DrawPixel(r, gl_width - c - 1, 0xFFFF'FFFF); - } else { - DrawPixel(r, gl_width - c - 1, 0); - } - } - } -}; diff --git a/sys/teton/framebuffer/framebuffer.h b/sys/teton/framebuffer/framebuffer.h deleted file mode 100644 index d6db590..0000000 --- a/sys/teton/framebuffer/framebuffer.h +++ /dev/null @@ -1,25 +0,0 @@ -#pragma once - -#include -#include - -class Framebuffer { - public: - Framebuffer(const yellowstone::FramebufferInfo& info); - - void DrawPixel(uint32_t row, uint32_t col, uint32_t pixel); - - void DrawGlyph(uint8_t* glyph); - - uint64_t width() { return fb_info_.width(); } - uint64_t height() { return fb_info_.height(); } - - private: - // FIXME: Implement Yunq copy or move so we - // don't have to store a reference here. - const yellowstone::FramebufferInfo& fb_info_; - - mmth::OwnedMemoryRegion fb_memory_; - uint32_t* fb_; - uint32_t cursor_pos_; -}; diff --git a/sys/teton/framebuffer/psf.cpp b/sys/teton/framebuffer/psf.cpp deleted file mode 100644 index 43714c6..0000000 --- a/sys/teton/framebuffer/psf.cpp +++ /dev/null @@ -1,46 +0,0 @@ -#include "framebuffer/psf.h" - -#include -#include - -namespace { - -const uint32_t kMagic = 0x864AB572; - -} - -Psf::Psf(glcr::StringView path) - : psf_file_(mmth::File::Open(path)), - header_(reinterpret_cast(psf_file_.raw_ptr())) { - EnsureValid(); -} - -void Psf::DumpHeader() { - dbgln("Magic: {x}", header_->magic); - dbgln("Version: {x}", header_->version); - dbgln("Header Sz: {x}", header_->headersize); - dbgln("Flags: {x}", header_->flags); - dbgln("Length: {x}", header_->numglyph); - dbgln("Glyph Size: {x}", header_->bytesperglyph); - dbgln("Height: {x}", header_->height); - dbgln("Width: {x}", header_->width); -} - -void Psf::EnsureValid() { - if (header_->magic != kMagic) { - dbgln("PSF: Magic value: {x}", header_->magic); - crash("PSF: Invalid magic value", glcr::INVALID_ARGUMENT); - } - - if (header_->version != 0) { - crash("PSF non-zero version", glcr::INVALID_ARGUMENT); - } - - if (header_->height != 0x10) { - crash("PSF height other than 16 not handled", glcr::UNIMPLEMENTED); - } - - if (header_->width != 0x8) { - crash("PSF width other than 8 not handled", glcr::UNIMPLEMENTED); - } -} diff --git a/sys/teton/framebuffer/psf.h b/sys/teton/framebuffer/psf.h deleted file mode 100644 index bfa890d..0000000 --- a/sys/teton/framebuffer/psf.h +++ /dev/null @@ -1,37 +0,0 @@ -#pragma once - -#include - -struct PsfHeader { - uint32_t magic; /* magic bytes to identify PSF */ - uint32_t version; /* zero */ - uint32_t headersize; /* offset of bitmaps in file, 32 */ - uint32_t flags; /* 0 if there's no unicode table */ - uint32_t numglyph; /* number of glyphs */ - uint32_t bytesperglyph; /* size of each glyph */ - uint32_t height; /* height in pixels */ - uint32_t width; /* width in pixels */ -}; - -class Psf { - public: - Psf(glcr::StringView path); - - void DumpHeader(); - - uint32_t size() { return header_->numglyph; } - uint32_t width() { return header_->width; } - uint32_t height() { return header_->height; } - - uint8_t* glyph(uint32_t index) { - return reinterpret_cast(psf_file_.byte_ptr() + - header_->headersize + - (index * header_->bytesperglyph)); - } - - private: - mmth::File psf_file_; - PsfHeader* header_; - - void EnsureValid(); -}; diff --git a/sys/teton/terminal.cpp b/sys/teton/terminal.cpp deleted file mode 100644 index fda6396..0000000 --- a/sys/teton/terminal.cpp +++ /dev/null @@ -1,92 +0,0 @@ -#include "terminal.h" - -#include -#include -#include -#include -#include -#include - -void Terminal::HandleCharacter(char c) { - console_.WriteChar(c); - if (c == '\n') { - glcr::String str = current_command_.ToString(); - ExecuteCommand(str); - current_command_.Reset(); - } else if (c == '\b') { - current_command_.DeleteLast(); - } else { - current_command_.PushBack(c); - } -} - -void Terminal::ExecuteCommand(const glcr::String& command) { - auto tokens = glcr::StrSplit(command, ' '); - if (tokens.size() == 0) { - console_.WriteChar('>'); - return; - } - glcr::StringView cmd = tokens[0]; - if (cmd == "help") { - console_.WriteString("Available Commands: pwd cd ls\n"); - } else if (cmd == "pwd") { - console_.WriteString(cwd_); - console_.WriteChar('\n'); - } else if (cmd == "cd") { - if (tokens.size() != 2) { - console_.WriteString("Provide an absolute path.\n>"); - return; - } - auto files_or = mmth::ListDirectory(tokens[1]); - if (files_or.ok()) { - cwd_ = tokens[1]; - } else { - console_.WriteString(glcr::StrFormat("Error: {}\n", files_or.error())); - } - - } else if (cmd == "ls") { - glcr::StringView directory; - if (tokens.size() > 1) { - directory = tokens[1]; - } else { - directory = cwd_; - } - auto files_or = mmth::ListDirectory(directory); - if (!files_or.ok()) { - console_.WriteString(glcr::StrFormat("Error: {}\n", files_or.error())); - } else { - auto& files = files_or.value(); - for (uint64_t i = 0; i < files.size(); i++) { - console_.WriteString(files[i]); - console_.WriteChar('\n'); - } - } - } else if (cmd == "exec") { - if (tokens.size() != 2) { - console_.WriteString("Provide the name of an executable.\n>"); - return; - } - auto file = mmth::File::Open(tokens[1]); - - // TODO: Wait until the process exits. - auto error_or_cap = mmth::SpawnProcessFromElfRegion( - (uint64_t)file.raw_ptr(), gInitEndpointCap); - if (!error_or_cap.ok()) { - console_.WriteString( - glcr::StrFormat("Error: {}\n", error_or_cap.error())); - return; - } - uint64_t err_code; - check(ZProcessWait(error_or_cap.value(), &err_code)); - if (err_code != 0) { - console_.WriteString(glcr::StrFormat( - "Process Error: {}\n", static_cast(err_code))); - } - - } else { - console_.WriteString("Unknown command: "); - console_.WriteString(command); - console_.WriteChar('\n'); - } - console_.WriteChar('>'); -} diff --git a/sys/teton/terminal.h b/sys/teton/terminal.h deleted file mode 100644 index 71408a2..0000000 --- a/sys/teton/terminal.h +++ /dev/null @@ -1,19 +0,0 @@ -#include -#include - -#include "framebuffer/console.h" - -class Terminal : public mmth::KeyboardListenerBase { - public: - Terminal(Console& c) : mmth::KeyboardListenerBase(), console_(c) {} - - virtual void HandleCharacter(char c) override; - - void ExecuteCommand(const glcr::String& command); - - private: - Console& console_; - glcr::VariableStringBuilder current_command_; - - glcr::String cwd_ = "/"; -}; diff --git a/sys/teton/teton.cpp b/sys/teton/teton.cpp deleted file mode 100644 index f89c689..0000000 --- a/sys/teton/teton.cpp +++ /dev/null @@ -1,48 +0,0 @@ -#include -#include -#include -#include -#include - -#include "framebuffer/console.h" -#include "framebuffer/framebuffer.h" -#include "framebuffer/psf.h" -#include "terminal.h" - -using yellowstone::FramebufferInfo; -using yellowstone::YellowstoneClient; - -uint64_t main(uint64_t init_port) { - ParseInitPort(init_port); - - dbgln("Teton Starting"); - - // 1. Set up framebuffer. - YellowstoneClient client(gInitEndpointCap); - - FramebufferInfo framebuffer; - check(client.GetFramebufferInfo(framebuffer)); - dbgln("FB addr {x}, bpp {}, width {} , height {}, pitch {}", - framebuffer.address_phys(), framebuffer.bpp(), framebuffer.width(), - framebuffer.height(), framebuffer.pitch()); - - Framebuffer fbuf(framebuffer); - - // 2. Parse a font file. - - Psf psf("/default8x16.psfu"); - psf.DumpHeader(); - - // 3. Write a line to the screen. - Console console(fbuf, psf); - console.WriteChar('>'); - - Terminal terminal(console); - terminal.Register(); - - Thread lthread = terminal.Listen(); - - check(lthread.Join()); - - return glcr::OK; -} diff --git a/sys/victoriafalls/CMakeLists.txt b/sys/victoriafalls/CMakeLists.txt deleted file mode 100644 index 964bbab..0000000 --- a/sys/victoriafalls/CMakeLists.txt +++ /dev/null @@ -1,26 +0,0 @@ -add_executable(victoriafalls - fs/ext2/ext2_block_reader.cpp - fs/ext2/ext2_driver.cpp - fs/ext2/inode_table.cpp - victoriafalls.cpp - victoriafalls_server.cpp - ) - -target_include_directories(victoriafalls - PRIVATE ${CMAKE_CURRENT_SOURCE_DIR} - "${CMAKE_CURRENT_SOURCE_DIR}/include") - -target_link_libraries(victoriafalls - denali_yunq - glacier - mammoth - victoriafalls_yunq - yellowstone_yunq - ) - -set_target_properties(victoriafalls PROPERTIES - COMPILE_FLAGS "${CMAKE_CXX_FLAGS} ${BASE_COMPILE_FLAGS}" - LINK_FLAGS "${CMAKE_EXE_LINK_FLAGS} ${BASE_LINK_FLAGS}" - ) - -yunq_gen(lib/victoriafalls lib victoriafalls) diff --git a/sys/victoriafalls/fs/ext2/ext2.h b/sys/victoriafalls/fs/ext2/ext2.h deleted file mode 100644 index 8a3ab2a..0000000 --- a/sys/victoriafalls/fs/ext2/ext2.h +++ /dev/null @@ -1,76 +0,0 @@ -#pragma once - -#include - -struct Superblock { - uint32_t inodes_count; - uint32_t blocks_count; - uint32_t reserved_blocks_count; - uint32_t free_blocks_count; - uint32_t free_inodes_count; - uint32_t first_data_blok; - uint32_t log_block_size; - uint32_t log_frag_size; - uint32_t blocks_per_group; - uint32_t frags_per_group; - uint32_t inodes_per_group; - uint32_t mtime; - uint32_t wtime; - uint16_t mnt_count; - uint16_t max_mnt_count; - uint16_t magic; - uint16_t state; - uint16_t errors; - uint16_t minor_rev_level; - uint32_t lastcheck; - uint32_t checkinterval; - uint32_t creator_os; - uint32_t rev_level; - uint16_t def_resuid; - uint16_t def_resgid; - uint32_t first_ino; - uint16_t inode_size; -} __attribute__((__packed__)); - -struct BlockGroupDescriptor { - uint32_t block_bitmap; - uint32_t inode_bitmap; - uint32_t inode_table; - uint16_t free_blocks_count; - uint16_t free_inodes_count; - uint16_t used_dirs_count; - uint8_t reserved[14]; -} __attribute__((__packed__)); - -struct Inode { - uint16_t mode; - uint16_t uid; - uint32_t size; - uint32_t atime; - uint32_t ctime; - uint32_t mtime; - uint32_t dtime; - uint16_t gid; - uint16_t links_count; - uint32_t blocks; - uint32_t flags; - uint32_t osd1; - uint32_t block[15]; - uint32_t generation; - uint32_t file_acl; - uint32_t dir_acl; - uint32_t faddr; - uint32_t osd2[3]; -} __attribute__((__packed__)); - -constexpr uint8_t kExt2FtUnknown = 0; -constexpr uint8_t kExt2FtFile = 1; -constexpr uint8_t kExt2FtDirectory = 2; - -struct DirEntry { - uint32_t inode; - uint16_t record_length; - uint8_t name_len; - uint8_t file_type; - char name[256]; -} __attribute__((__packed__)); diff --git a/sys/victoriafalls/fs/ext2/ext2_block_reader.cpp b/sys/victoriafalls/fs/ext2/ext2_block_reader.cpp deleted file mode 100644 index 3262f48..0000000 --- a/sys/victoriafalls/fs/ext2/ext2_block_reader.cpp +++ /dev/null @@ -1,117 +0,0 @@ -#include "fs/ext2/ext2_block_reader.h" - -#include - -glcr::ErrorOr> Ext2BlockReader::Init( - const yellowstone::DenaliInfo& denali_info) { - // Read 1024 bytes from 1024 offset. - // FIXME: Don't assume 512 byte sectors somehow. - DenaliClient client(denali_info.denali_endpoint()); - ReadRequest req; - req.set_device_id(denali_info.device_id()); - req.set_lba(denali_info.lba_offset() + 2); - req.set_size(2); - ReadResponse resp; - auto status = client.Read(req, resp); - if (!status.ok()) { - dbgln("Failed to read superblock: {}", status.message()); - return status.code(); - } - mmth::OwnedMemoryRegion superblock = - mmth::OwnedMemoryRegion::FromCapability(resp.memory()); - - return glcr::SharedPtr( - new Ext2BlockReader(glcr::Move(client), denali_info.device_id(), - denali_info.lba_offset(), glcr::Move(superblock))); -} - -Superblock* Ext2BlockReader::GetSuperblock() { - return reinterpret_cast(super_block_region_.vaddr()); -} - -uint64_t Ext2BlockReader::SectorsPerBlock() { - return 1 << (GetSuperblock()->log_block_size + 1); -} - -uint64_t Ext2BlockReader::BlockSize() { - return 1024 << (GetSuperblock()->log_block_size); -} - -uint64_t Ext2BlockReader::NumberOfBlockGroups() { - return ((GetSuperblock()->blocks_count - 1) / - GetSuperblock()->blocks_per_group) + - 1; -} - -uint64_t Ext2BlockReader::BgdtBlockNum() { - return (BlockSize() == 1024) ? 2 : 1; -} - -uint64_t Ext2BlockReader::BgdtBlockSize() { - return ((NumberOfBlockGroups() * sizeof(BlockGroupDescriptor) - 1) / - BlockSize()) + - 1; -} - -uint64_t Ext2BlockReader::InodeSize() { - constexpr uint64_t kDefaultInodeSize = 0x80; - return GetSuperblock()->rev_level >= 1 ? GetSuperblock()->inode_size - : kDefaultInodeSize; -} - -uint64_t Ext2BlockReader::InodeTableBlockSize() { - return (InodeSize() * GetSuperblock()->inodes_per_group) / BlockSize(); -} - -glcr::ErrorOr Ext2BlockReader::ReadBlock( - uint64_t block_number) { - return ReadBlocks(block_number, 1); -} -glcr::ErrorOr Ext2BlockReader::ReadBlocks( - uint64_t block_number, uint64_t num_blocks) { - ReadRequest req; - req.set_device_id(device_id_); - req.set_lba(lba_offset_ + block_number * SectorsPerBlock()); - req.set_size(num_blocks * SectorsPerBlock()); - ReadResponse resp; - auto status = denali_.Read(req, resp); - if (!status.ok()) { - dbgln("Failed to read blocks: {}", status.message()); - return status.code(); - } - return mmth::OwnedMemoryRegion::FromCapability(resp.memory()); -} - -glcr::ErrorOr Ext2BlockReader::ReadBlocks( - const glcr::Vector& block_list) { - ReadManyRequest req; - req.set_device_id(device_id_); - for (uint64_t i = 0; i < block_list.size(); i++) { - uint64_t curr_start = lba_offset_ + block_list.at(i) * SectorsPerBlock(); - uint64_t curr_run_len = 1; - while ((i + 1) < block_list.size() && - block_list.at(i + 1) == block_list.at(i) + 1) { - i++; - curr_run_len++; - } - req.add_lba(curr_start); - req.add_sector_cnt(curr_run_len * SectorsPerBlock()); - dbgln("Read {x}, {x}", curr_start, curr_run_len * SectorsPerBlock()); - } - dbgln("Read many: {x}", req.lba().size()); - ReadResponse resp; - auto status = denali_.ReadMany(req, resp); - if (!status.ok()) { - dbgln("Failed to read blocks: {}", status.message()); - return status.code(); - } - return mmth::OwnedMemoryRegion::FromCapability(resp.memory()); -} - -Ext2BlockReader::Ext2BlockReader(DenaliClient&& denali, uint64_t device_id, - uint64_t lba_offset, - mmth::OwnedMemoryRegion&& super_block) - : denali_(glcr::Move(denali)), - device_id_(device_id), - lba_offset_(lba_offset), - super_block_region_(glcr::Move(super_block)) {} diff --git a/sys/victoriafalls/fs/ext2/ext2_block_reader.h b/sys/victoriafalls/fs/ext2/ext2_block_reader.h deleted file mode 100644 index aa49fba..0000000 --- a/sys/victoriafalls/fs/ext2/ext2_block_reader.h +++ /dev/null @@ -1,49 +0,0 @@ -#pragma once - -#include -#include -#include -#include -#include - -#include "fs/ext2/ext2.h" - -/* Simple Wrapper class around the denali client to translate blocks to sectors. - * - * By necessity contains the Ext Superblock (to make the translation - * calculation). - * */ -class Ext2BlockReader { - public: - static glcr::ErrorOr> Init( - const yellowstone::DenaliInfo& denali_info); - - // TODO: Consider creating a new class wrapper with these computations. - Superblock* GetSuperblock(); - uint64_t BlockSize(); - uint64_t NumberOfBlockGroups(); - uint64_t BgdtBlockNum(); - uint64_t BgdtBlockSize(); - uint64_t InodeSize(); - // FIXME: This probably needs to take into account the block group number - // because the last table will likely be smaller. - uint64_t InodeTableBlockSize(); - - glcr::ErrorOr ReadBlock(uint64_t block_number); - glcr::ErrorOr ReadBlocks(uint64_t block_number, - uint64_t num_blocks); - - glcr::ErrorOr ReadBlocks( - const glcr::Vector& block_list); - - private: - DenaliClient denali_; - uint64_t device_id_; - uint64_t lba_offset_; - mmth::OwnedMemoryRegion super_block_region_; - - Ext2BlockReader(DenaliClient&& denali, uint64_t device_id, - uint64_t lba_offset, mmth::OwnedMemoryRegion&& super_block); - - uint64_t SectorsPerBlock(); -}; diff --git a/sys/victoriafalls/fs/ext2/ext2_driver.cpp b/sys/victoriafalls/fs/ext2/ext2_driver.cpp deleted file mode 100644 index 2202163..0000000 --- a/sys/victoriafalls/fs/ext2/ext2_driver.cpp +++ /dev/null @@ -1,152 +0,0 @@ -#include "fs/ext2/ext2_driver.h" - -#include -#include - -glcr::ErrorOr Ext2Driver::Init( - const yellowstone::DenaliInfo& denali_info) { - ASSIGN_OR_RETURN(glcr::SharedPtr reader, - Ext2BlockReader::Init(glcr::Move(denali_info))); - - ASSIGN_OR_RETURN( - mmth::OwnedMemoryRegion bgdt, - reader->ReadBlocks(reader->BgdtBlockNum(), reader->BgdtBlockSize())); - glcr::UniquePtr inode_table( - new InodeTable(reader, glcr::Move(bgdt))); - - return Ext2Driver(reader, glcr::Move(inode_table)); -} - -glcr::ErrorCode Ext2Driver::ProbePartition() { - Superblock* superblock = ext2_reader_->GetSuperblock(); - if (superblock->magic != 0xEF53) { - dbgln("Invalid EXT2 magic code: {x}"); - return glcr::INVALID_ARGUMENT; - } - dbgln("Block size: 0x{x}", 1024 << superblock->log_block_size); - - dbgln("Blocks: 0x{x} (0x{x} per group)", superblock->blocks_count, - superblock->blocks_per_group); - dbgln("Inodes: 0x{x} (0x{x} per group)", superblock->inodes_count, - superblock->inodes_per_group); - dbgln("Inode size: 0x{x}", superblock->inode_size); - - dbgln("Mounts: 0x{x} out of 0x{x}", superblock->mnt_count, - superblock->max_mnt_count); - dbgln("State: {x}", superblock->state); - - dbgln("Created by: {x}", superblock->creator_os); - - return glcr::OK; -} - -glcr::ErrorOr Ext2Driver::GetInode(uint32_t inode_number) { - return inode_table_->GetInode(inode_number); -} - -glcr::ErrorOr> Ext2Driver::ReadDirectory( - uint32_t inode_number) { - ASSIGN_OR_RETURN(Inode * inode, inode_table_->GetInode(inode_number)); - if (!(inode->mode & 0x4000)) { - dbgln("Reading non directory."); - return glcr::INVALID_ARGUMENT; - } - - // This calculation is cursed. - uint64_t real_block_cnt = - (inode->blocks - 1) / (ext2_reader_->BlockSize() / 512) + 1; - - if (real_block_cnt > 12) { - dbgln("Cant handle indirect blocks yet"); - return glcr::FAILED_PRECONDITION; - } - - glcr::Vector directory; - for (uint64_t i = 0; i < real_block_cnt; i++) { - dbgln("Getting block {x}", inode->block[i]); - ASSIGN_OR_RETURN(mmth::OwnedMemoryRegion block, - ext2_reader_->ReadBlock(inode->block[i])); - uint64_t addr = block.vaddr(); - while (addr < block.vaddr() + ext2_reader_->BlockSize()) { - DirEntry* entry = reinterpret_cast(addr); - directory.PushBack(*entry); - glcr::StringView name(entry->name, entry->name_len); - switch (entry->file_type) { - case kExt2FtFile: - dbgln("FILE (0x{x}): {}", entry->inode, name); - break; - case kExt2FtDirectory: - dbgln("DIR (0x{x}): {}", entry->inode, name); - break; - default: - dbgln("UNK (0x{x}): {}", entry->inode, name); - } - addr += entry->record_length; - } - } - return directory; -} - -glcr::ErrorOr Ext2Driver::ReadFile( - uint64_t inode_number) { - ASSIGN_OR_RETURN(Inode * inode, inode_table_->GetInode(inode_number)); - - if (!(inode->mode & 0x8000)) { - dbgln("Reading non file."); - return glcr::INVALID_ARGUMENT; - } - - // This calculation is cursed. - uint64_t real_block_cnt = - (inode->blocks - 1) / (ext2_reader_->BlockSize() / 512) + 1; - - if (inode->block[14]) { - dbgln("Can't handle triply-indirect blocks yet."); - return glcr::UNIMPLEMENTED; - } - - mmth::OwnedMemoryRegion double_indirect_block; - if (inode->block[13]) { - ASSIGN_OR_RETURN(double_indirect_block, - ext2_reader_->ReadBlock(inode->block[13])); - } - - mmth::OwnedMemoryRegion indirect_block; - if (inode->block[12]) { - ASSIGN_OR_RETURN(indirect_block, ext2_reader_->ReadBlock(inode->block[12])); - } - - glcr::Vector blocks_to_read; - for (uint64_t i = 0; i < 12 && i < real_block_cnt; i++) { - blocks_to_read.PushBack(inode->block[i]); - } - - uint32_t* indr_block_array = - reinterpret_cast(indirect_block.vaddr()); - for (uint64_t i = 12; i < 268 && i < real_block_cnt; i++) { - uint64_t offset = i - 12; - blocks_to_read.PushBack(indr_block_array[offset]); - } - - uint32_t* dbl_indr_block_array = - reinterpret_cast(double_indirect_block.vaddr()); - for (uint64_t i = 0; i < 256; i++) { - uint64_t block = 268 + (256 * i); - if (block >= real_block_cnt) { - break; - } - ASSIGN_OR_RETURN(mmth::OwnedMemoryRegion single_indr_block, - ext2_reader_->ReadBlock(dbl_indr_block_array[i])); - uint32_t* single_indr_block_array = - reinterpret_cast(single_indr_block.vaddr()); - for (uint64_t j = 0; j < 256; j++) { - uint64_t block_inner = block + j; - if (block_inner >= real_block_cnt) { - break; - } - blocks_to_read.PushBack(single_indr_block_array[j]); - } - } - - return ext2_reader_->ReadBlocks(blocks_to_read); -} diff --git a/sys/victoriafalls/fs/ext2/ext2_driver.h b/sys/victoriafalls/fs/ext2/ext2_driver.h deleted file mode 100644 index b1b4100..0000000 --- a/sys/victoriafalls/fs/ext2/ext2_driver.h +++ /dev/null @@ -1,31 +0,0 @@ -#pragma once - -#include -#include -#include - -#include "fs/ext2/ext2.h" -#include "fs/ext2/ext2_block_reader.h" -#include "fs/ext2/inode_table.h" - -class Ext2Driver { - public: - static glcr::ErrorOr Init( - const yellowstone::DenaliInfo& denali_info); - - glcr::ErrorCode ProbePartition(); - - glcr::ErrorOr GetInode(uint32_t inode_number); - - glcr::ErrorOr> ReadDirectory(uint32_t inode_number); - - glcr::ErrorOr ReadFile(uint64_t inode_number); - - private: - glcr::SharedPtr ext2_reader_; - glcr::UniquePtr inode_table_; - - Ext2Driver(const glcr::SharedPtr& reader, - glcr::UniquePtr inode_table) - : ext2_reader_(reader), inode_table_(glcr::Move(inode_table)) {} -}; diff --git a/sys/victoriafalls/fs/ext2/inode_table.cpp b/sys/victoriafalls/fs/ext2/inode_table.cpp deleted file mode 100644 index 12bab0f..0000000 --- a/sys/victoriafalls/fs/ext2/inode_table.cpp +++ /dev/null @@ -1,33 +0,0 @@ -#include "fs/ext2/inode_table.h" - -InodeTable::InodeTable(const glcr::SharedPtr& reader, - mmth::OwnedMemoryRegion&& bgdt_region) - : ext2_reader_(reader), - bgdt_region_(glcr::Move(bgdt_region)), - bgdt_(reinterpret_cast(bgdt_region_.vaddr())) { - inode_tables_.Resize(ext2_reader_->NumberOfBlockGroups()); -} - -glcr::ErrorOr InodeTable::GetInode(uint32_t inode_num) { - uint64_t inodes_per_group = ext2_reader_->GetSuperblock()->inodes_per_group; - uint64_t block_group_num = (inode_num - 1) / inodes_per_group; - ASSIGN_OR_RETURN(Inode * root, GetRootOfInodeTable(block_group_num)); - uint64_t local_index = (inode_num - 1) % inodes_per_group; - return reinterpret_cast(reinterpret_cast(root) + - local_index * ext2_reader_->InodeSize()); -} - -glcr::ErrorOr InodeTable::GetRootOfInodeTable( - uint64_t block_group_num) { - if (block_group_num > ext2_reader_->NumberOfBlockGroups()) { - return glcr::INVALID_ARGUMENT; - } - - if (!inode_tables_[block_group_num]) { - ASSIGN_OR_RETURN( - inode_tables_[block_group_num], - ext2_reader_->ReadBlocks(bgdt_[block_group_num].inode_table, - ext2_reader_->InodeTableBlockSize())); - } - return reinterpret_cast(inode_tables_[block_group_num].vaddr()); -} diff --git a/sys/victoriafalls/fs/ext2/inode_table.h b/sys/victoriafalls/fs/ext2/inode_table.h deleted file mode 100644 index 18bb36d..0000000 --- a/sys/victoriafalls/fs/ext2/inode_table.h +++ /dev/null @@ -1,24 +0,0 @@ -#pragma once - -#include -#include -#include - -#include "fs/ext2/ext2_block_reader.h" - -class InodeTable { - public: - InodeTable(const glcr::SharedPtr& driver, - mmth::OwnedMemoryRegion&& bgdt_region); - - glcr::ErrorOr GetInode(uint32_t inode_num); - - private: - glcr::SharedPtr ext2_reader_; - mmth::OwnedMemoryRegion bgdt_region_; - BlockGroupDescriptor* bgdt_; - - glcr::Vector inode_tables_; - - glcr::ErrorOr GetRootOfInodeTable(uint64_t block_group_num); -}; diff --git a/sys/victoriafalls/lib/victoriafalls/victoriafalls.yunq.client.cpp b/sys/victoriafalls/lib/victoriafalls/victoriafalls.yunq.client.cpp deleted file mode 100644 index aa289d2..0000000 --- a/sys/victoriafalls/lib/victoriafalls/victoriafalls.yunq.client.cpp +++ /dev/null @@ -1,98 +0,0 @@ -// Generated file - DO NOT MODIFY -#include "victoriafalls.yunq.client.h" - -#include -#include -#include -#include - - - - -VFSClient::~VFSClient() { - if (endpoint_ != 0) { - check(ZCapRelease(endpoint_)); - } -} - - - - -glcr::Status VFSClient::OpenFile(const OpenFileRequest& request, OpenFileResponse& response) { - - uint64_t buffer_size = kBufferSize; - uint64_t cap_size = kCapBufferSize; - - const uint32_t kSentinel = 0xBEEFDEAD; - buffer_.WriteAt(0, kSentinel); - buffer_.WriteAt(8, 0); - - cap_buffer_.Reset(); - - uint64_t length = request.SerializeToBytes(buffer_, /*offset=*/16, cap_buffer_); - - - buffer_.WriteAt(4, 16 + length); - - z_cap_t reply_port_cap; - RET_ERR(ZEndpointSend(endpoint_, 16 + length, buffer_.RawPtr(), cap_buffer_.UsedSlots(), cap_buffer_.RawPtr(), &reply_port_cap)); - - // FIXME: Add a way to zero out the first buffer. - RET_ERR(ZReplyPortRecv(reply_port_cap, &buffer_size, buffer_.RawPtr(), &cap_size, cap_buffer_.RawPtr())); - - if (buffer_.At(0) != kSentinel) { - return glcr::InvalidResponse("Got an invalid response from server."); - } - - // Check Response Code. - RET_ERR(buffer_.At(8)); - - - RETURN_ERROR(response.ParseFromBytes(buffer_, 16, cap_buffer_)); - - - return glcr::OK; -} - - - - -glcr::Status VFSClient::GetDirectory(const GetDirectoryRequest& request, Directory& response) { - - uint64_t buffer_size = kBufferSize; - uint64_t cap_size = kCapBufferSize; - - const uint32_t kSentinel = 0xBEEFDEAD; - buffer_.WriteAt(0, kSentinel); - buffer_.WriteAt(8, 1); - - cap_buffer_.Reset(); - - uint64_t length = request.SerializeToBytes(buffer_, /*offset=*/16, cap_buffer_); - - - buffer_.WriteAt(4, 16 + length); - - z_cap_t reply_port_cap; - RET_ERR(ZEndpointSend(endpoint_, 16 + length, buffer_.RawPtr(), cap_buffer_.UsedSlots(), cap_buffer_.RawPtr(), &reply_port_cap)); - - // FIXME: Add a way to zero out the first buffer. - RET_ERR(ZReplyPortRecv(reply_port_cap, &buffer_size, buffer_.RawPtr(), &cap_size, cap_buffer_.RawPtr())); - - if (buffer_.At(0) != kSentinel) { - return glcr::InvalidResponse("Got an invalid response from server."); - } - - // Check Response Code. - RET_ERR(buffer_.At(8)); - - - RETURN_ERROR(response.ParseFromBytes(buffer_, 16, cap_buffer_)); - - - return glcr::OK; -} - - - - diff --git a/sys/victoriafalls/lib/victoriafalls/victoriafalls.yunq.client.h b/sys/victoriafalls/lib/victoriafalls/victoriafalls.yunq.client.h deleted file mode 100644 index 35dab56..0000000 --- a/sys/victoriafalls/lib/victoriafalls/victoriafalls.yunq.client.h +++ /dev/null @@ -1,37 +0,0 @@ -// Generated file - DO NOT MODIFY -#pragma once - -#include -#include -#include -#include - -#include "victoriafalls.yunq.h" - - -class VFSClient { - public: - VFSClient(z_cap_t VFS_cap) : endpoint_(VFS_cap) {} - VFSClient(const VFSClient&) = delete; - VFSClient(VFSClient&& other) : endpoint_(other.endpoint_) {other.endpoint_ = 0;}; - ~VFSClient(); - - z_cap_t Capability() { return endpoint_; } - - - - [[nodiscard]] glcr::Status OpenFile(const OpenFileRequest& request, OpenFileResponse& response); - - - - [[nodiscard]] glcr::Status GetDirectory(const GetDirectoryRequest& request, Directory& response); - - - private: - z_cap_t endpoint_; - uint64_t kBufferSize = 0x1000; - glcr::ByteBuffer buffer_{kBufferSize}; - uint64_t kCapBufferSize = 0x10; - glcr::CapBuffer cap_buffer_{kCapBufferSize}; -}; - diff --git a/sys/victoriafalls/lib/victoriafalls/victoriafalls.yunq.cpp b/sys/victoriafalls/lib/victoriafalls/victoriafalls.yunq.cpp deleted file mode 100644 index 44c50b0..0000000 --- a/sys/victoriafalls/lib/victoriafalls/victoriafalls.yunq.cpp +++ /dev/null @@ -1,286 +0,0 @@ -// Generated file -- DO NOT MODIFY. -#include "victoriafalls.yunq.h" - -#include - - -namespace { - -const uint64_t header_size = 24; // 4x uint32, 1x uint64 - -struct ExtPointer { - uint32_t offset; - uint32_t length; -}; - -} // namespace -glcr::Status OpenFileRequest::ParseFromBytes(const glcr::ByteBuffer& bytes, uint64_t offset) { - RETURN_ERROR(ParseFromBytesInternal(bytes, offset)); - return glcr::Status::Ok(); -} - -glcr::Status OpenFileRequest::ParseFromBytes(const glcr::ByteBuffer& bytes, uint64_t offset, const glcr::CapBuffer& caps) { - RETURN_ERROR(ParseFromBytesInternal(bytes, offset)); - return glcr::Status::Ok(); -} - -glcr::Status OpenFileRequest::ParseFromBytesInternal(const glcr::ByteBuffer& bytes, uint64_t offset) { - RETURN_ERROR(yunq::CheckHeader(bytes, offset)); - // Parse path. - auto path_pointer = bytes.At(offset + header_size + (8 * 0)); - - set_path(bytes.StringAt(offset + path_pointer.offset, path_pointer.length)); - - return glcr::Status::Ok(); -} - -uint64_t OpenFileRequest::SerializeToBytes(glcr::ByteBuffer& bytes, uint64_t offset) const { - uint32_t next_extension = header_size + 8 * 1; - 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); - - // The next extension pointer is the length of the message. - yunq::WriteHeader(bytes, offset, core_size, next_extension); - - return next_extension; -} - -uint64_t OpenFileRequest::SerializeToBytes(glcr::ByteBuffer& bytes, uint64_t offset, glcr::CapBuffer& caps) const { - uint32_t next_extension = header_size + 8 * 1; - 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); - - // The next extension pointer is the length of the message. - yunq::WriteHeader(bytes, offset, core_size, next_extension); - - return next_extension; -} -glcr::Status OpenFileResponse::ParseFromBytes(const glcr::ByteBuffer& bytes, uint64_t offset) { - RETURN_ERROR(ParseFromBytesInternal(bytes, offset)); - // Parse memory. - // FIXME: Implement in-buffer capabilities for inprocess serialization. - set_memory(0); - return glcr::Status::Ok(); -} - -glcr::Status OpenFileResponse::ParseFromBytes(const glcr::ByteBuffer& bytes, uint64_t offset, const glcr::CapBuffer& caps) { - RETURN_ERROR(ParseFromBytesInternal(bytes, offset)); - // Parse memory. - uint64_t memory_ptr = bytes.At(offset + header_size + (8 * 2)); - - set_memory(caps.At(memory_ptr)); - return glcr::Status::Ok(); -} - -glcr::Status OpenFileResponse::ParseFromBytesInternal(const glcr::ByteBuffer& bytes, uint64_t offset) { - RETURN_ERROR(yunq::CheckHeader(bytes, offset)); - // Parse path. - auto path_pointer = bytes.At(offset + header_size + (8 * 0)); - - set_path(bytes.StringAt(offset + path_pointer.offset, path_pointer.length)); - // Parse size. - set_size(bytes.At(offset + header_size + (8 * 1))); - // Parse memory. - // Skip Cap. - - return glcr::Status::Ok(); -} - -uint64_t OpenFileResponse::SerializeToBytes(glcr::ByteBuffer& bytes, uint64_t offset) const { - 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 size. - bytes.WriteAt(offset + header_size + (8 * 1), size()); - // Write memory. - // FIXME: Implement inbuffer capabilities. - bytes.WriteAt(offset + header_size + (8 * 2), 0); - - // The next extension pointer is the length of the message. - yunq::WriteHeader(bytes, offset, core_size, next_extension); - - return next_extension; -} - -uint64_t OpenFileResponse::SerializeToBytes(glcr::ByteBuffer& bytes, uint64_t offset, glcr::CapBuffer& caps) const { - 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 size. - bytes.WriteAt(offset + header_size + (8 * 1), size()); - // Write memory. - caps.WriteAt(next_cap, memory()); - bytes.WriteAt(offset + header_size + (8 * 2), next_cap++); - - // The next extension pointer is the length of the message. - yunq::WriteHeader(bytes, offset, core_size, next_extension); - - return next_extension; -} -glcr::Status GetDirectoryRequest::ParseFromBytes(const glcr::ByteBuffer& bytes, uint64_t offset) { - RETURN_ERROR(ParseFromBytesInternal(bytes, offset)); - return glcr::Status::Ok(); -} - -glcr::Status GetDirectoryRequest::ParseFromBytes(const glcr::ByteBuffer& bytes, uint64_t offset, const glcr::CapBuffer& caps) { - RETURN_ERROR(ParseFromBytesInternal(bytes, offset)); - return glcr::Status::Ok(); -} - -glcr::Status GetDirectoryRequest::ParseFromBytesInternal(const glcr::ByteBuffer& bytes, uint64_t offset) { - RETURN_ERROR(yunq::CheckHeader(bytes, offset)); - // Parse path. - auto path_pointer = bytes.At(offset + header_size + (8 * 0)); - - set_path(bytes.StringAt(offset + path_pointer.offset, path_pointer.length)); - - return glcr::Status::Ok(); -} - -uint64_t GetDirectoryRequest::SerializeToBytes(glcr::ByteBuffer& bytes, uint64_t offset) const { - uint32_t next_extension = header_size + 8 * 1; - 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); - - // The next extension pointer is the length of the message. - yunq::WriteHeader(bytes, offset, core_size, next_extension); - - return next_extension; -} - -uint64_t GetDirectoryRequest::SerializeToBytes(glcr::ByteBuffer& bytes, uint64_t offset, glcr::CapBuffer& caps) const { - uint32_t next_extension = header_size + 8 * 1; - 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); - - // The next extension pointer is the length of the message. - yunq::WriteHeader(bytes, offset, core_size, next_extension); - - return next_extension; -} -glcr::Status Directory::ParseFromBytes(const glcr::ByteBuffer& bytes, uint64_t offset) { - RETURN_ERROR(ParseFromBytesInternal(bytes, offset)); - return glcr::Status::Ok(); -} - -glcr::Status Directory::ParseFromBytes(const glcr::ByteBuffer& bytes, uint64_t offset, const glcr::CapBuffer& caps) { - RETURN_ERROR(ParseFromBytesInternal(bytes, offset)); - return glcr::Status::Ok(); -} - -glcr::Status Directory::ParseFromBytesInternal(const glcr::ByteBuffer& bytes, uint64_t offset) { - RETURN_ERROR(yunq::CheckHeader(bytes, offset)); - // Parse filenames. - auto filenames_pointer = bytes.At(offset + header_size + (8 * 0)); - - set_filenames(bytes.StringAt(offset + filenames_pointer.offset, filenames_pointer.length)); - - return glcr::Status::Ok(); -} - -uint64_t Directory::SerializeToBytes(glcr::ByteBuffer& bytes, uint64_t offset) const { - uint32_t next_extension = header_size + 8 * 1; - const uint32_t core_size = next_extension; - // Write filenames. - ExtPointer filenames_ptr{ - .offset = next_extension, - // FIXME: Check downcast of str length. - .length = (uint32_t)filenames().length(), - }; - - bytes.WriteStringAt(offset + next_extension, filenames()); - next_extension += filenames_ptr.length; - - bytes.WriteAt(offset + header_size + (8 * 0), filenames_ptr); - - // The next extension pointer is the length of the message. - yunq::WriteHeader(bytes, offset, core_size, next_extension); - - return next_extension; -} - -uint64_t Directory::SerializeToBytes(glcr::ByteBuffer& bytes, uint64_t offset, glcr::CapBuffer& caps) const { - uint32_t next_extension = header_size + 8 * 1; - const uint32_t core_size = next_extension; - uint64_t next_cap = 0; - // Write filenames. - ExtPointer filenames_ptr{ - .offset = next_extension, - // FIXME: Check downcast of str length. - .length = (uint32_t)filenames().length(), - }; - - bytes.WriteStringAt(offset + next_extension, filenames()); - next_extension += filenames_ptr.length; - - bytes.WriteAt(offset + header_size + (8 * 0), filenames_ptr); - - // The next extension pointer is the length of the message. - yunq::WriteHeader(bytes, offset, core_size, next_extension); - - return next_extension; -} - diff --git a/sys/victoriafalls/lib/victoriafalls/victoriafalls.yunq.h b/sys/victoriafalls/lib/victoriafalls/victoriafalls.yunq.h deleted file mode 100644 index 7549408..0000000 --- a/sys/victoriafalls/lib/victoriafalls/victoriafalls.yunq.h +++ /dev/null @@ -1,98 +0,0 @@ -// Generated file - DO NOT MODIFY -#pragma once - -#include -#include -#include -#include -#include -#include - - -class OpenFileRequest { - public: - OpenFileRequest() {} - // Delete copy and move until implemented. - OpenFileRequest(const OpenFileRequest&) = delete; - OpenFileRequest(OpenFileRequest&&) = delete; - - [[nodiscard]] glcr::Status ParseFromBytes(const glcr::ByteBuffer&, uint64_t offset); - [[nodiscard]] glcr::Status ParseFromBytes(const glcr::ByteBuffer&, uint64_t offset, const glcr::CapBuffer&); - uint64_t SerializeToBytes(glcr::ByteBuffer&, uint64_t offset) const; - uint64_t SerializeToBytes(glcr::ByteBuffer&, uint64_t offset, glcr::CapBuffer&) const; - const glcr::String& path() const { return path_; } - void set_path(const glcr::String& value) { path_ = value; } - - private: - glcr::String path_; - - // Parses everything except for caps. - glcr::Status ParseFromBytesInternal(const glcr::ByteBuffer&, uint64_t offset); -}; -class OpenFileResponse { - public: - OpenFileResponse() {} - // Delete copy and move until implemented. - OpenFileResponse(const OpenFileResponse&) = delete; - OpenFileResponse(OpenFileResponse&&) = delete; - - [[nodiscard]] glcr::Status ParseFromBytes(const glcr::ByteBuffer&, uint64_t offset); - [[nodiscard]] glcr::Status ParseFromBytes(const glcr::ByteBuffer&, uint64_t offset, const glcr::CapBuffer&); - uint64_t SerializeToBytes(glcr::ByteBuffer&, uint64_t offset) const; - uint64_t SerializeToBytes(glcr::ByteBuffer&, uint64_t offset, glcr::CapBuffer&) const; - const glcr::String& path() const { return path_; } - void set_path(const glcr::String& value) { path_ = value; } - const uint64_t& size() const { return size_; } - void set_size(const uint64_t& value) { size_ = value; } - const z_cap_t& memory() const { return memory_; } - void set_memory(const z_cap_t& value) { memory_ = value; } - - private: - glcr::String path_; - uint64_t size_; - z_cap_t memory_; - - // Parses everything except for caps. - glcr::Status ParseFromBytesInternal(const glcr::ByteBuffer&, uint64_t offset); -}; -class GetDirectoryRequest { - public: - GetDirectoryRequest() {} - // Delete copy and move until implemented. - GetDirectoryRequest(const GetDirectoryRequest&) = delete; - GetDirectoryRequest(GetDirectoryRequest&&) = delete; - - [[nodiscard]] glcr::Status ParseFromBytes(const glcr::ByteBuffer&, uint64_t offset); - [[nodiscard]] glcr::Status ParseFromBytes(const glcr::ByteBuffer&, uint64_t offset, const glcr::CapBuffer&); - uint64_t SerializeToBytes(glcr::ByteBuffer&, uint64_t offset) const; - uint64_t SerializeToBytes(glcr::ByteBuffer&, uint64_t offset, glcr::CapBuffer&) const; - const glcr::String& path() const { return path_; } - void set_path(const glcr::String& value) { path_ = value; } - - private: - glcr::String path_; - - // Parses everything except for caps. - glcr::Status ParseFromBytesInternal(const glcr::ByteBuffer&, uint64_t offset); -}; -class Directory { - public: - Directory() {} - // Delete copy and move until implemented. - Directory(const Directory&) = delete; - Directory(Directory&&) = delete; - - [[nodiscard]] glcr::Status ParseFromBytes(const glcr::ByteBuffer&, uint64_t offset); - [[nodiscard]] glcr::Status ParseFromBytes(const glcr::ByteBuffer&, uint64_t offset, const glcr::CapBuffer&); - uint64_t SerializeToBytes(glcr::ByteBuffer&, uint64_t offset) const; - uint64_t SerializeToBytes(glcr::ByteBuffer&, uint64_t offset, glcr::CapBuffer&) const; - const glcr::String& filenames() const { return filenames_; } - void set_filenames(const glcr::String& value) { filenames_ = value; } - - private: - glcr::String filenames_; - - // Parses everything except for caps. - glcr::Status ParseFromBytesInternal(const glcr::ByteBuffer&, uint64_t offset); -}; - diff --git a/sys/victoriafalls/lib/victoriafalls/victoriafalls.yunq.server.cpp b/sys/victoriafalls/lib/victoriafalls/victoriafalls.yunq.server.cpp deleted file mode 100644 index 85a8a73..0000000 --- a/sys/victoriafalls/lib/victoriafalls/victoriafalls.yunq.server.cpp +++ /dev/null @@ -1,150 +0,0 @@ -// Generated file -- DO NOT MODIFY. -#include "victoriafalls.yunq.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 - - - -void VFSServerBaseThreadBootstrap(void* server_base) { - ((VFSServerBase*)server_base)->ServerThread(); -} - -VFSServerBase::~VFSServerBase() { - if (endpoint_ != 0) { - check(ZCapRelease(endpoint_)); - } -} - -glcr::ErrorOr VFSServerBase::CreateClientCap() { - uint64_t client_cap; - RET_ERR(ZCapDuplicate(endpoint_, ~(kZionPerm_Read), &client_cap)); - return client_cap; -} - -glcr::ErrorOr VFSServerBase::CreateClient() { - uint64_t client_cap; - RET_ERR(ZCapDuplicate(endpoint_, ~(kZionPerm_Read), &client_cap)); - return VFSClient(client_cap); -} - -Thread VFSServerBase::RunServer() { - return Thread(VFSServerBaseThreadBootstrap, this); -} - -void VFSServerBase::ServerThread() { - glcr::ByteBuffer recv_buffer(0x1000); - glcr::CapBuffer recv_cap(0x10); - glcr::ByteBuffer resp_buffer(0x1000); - glcr::CapBuffer resp_cap(0x10); - z_cap_t reply_port_cap; - - while (true) { - uint64_t recv_cap_size = 0x10; - uint64_t recv_buf_size = 0x1000; - recv_cap.Reset(); - glcr::ErrorCode recv_err = static_cast(ZEndpointRecv(endpoint_, &recv_buf_size, recv_buffer.RawPtr(), &recv_cap_size, recv_cap.RawPtr(), &reply_port_cap)); - if (recv_err != glcr::OK) { - dbgln("Error in receive: {x}", recv_err); - continue; - } - - uint64_t resp_length = 0; - - glcr::ErrorCode reply_err = glcr::OK; - resp_cap.Reset(); - glcr::Status err = HandleRequest(recv_buffer, recv_cap, resp_buffer, resp_length, resp_cap); - if (!err) { - WriteError(resp_buffer, err.code()); - dbgln("Responding Error {}", err.message()); - reply_err = static_cast(ZReplyPortSend(reply_port_cap, kHeaderSize, resp_buffer.RawPtr(), 0, nullptr)); - } else { - WriteHeader(resp_buffer, resp_length); - reply_err = static_cast(ZReplyPortSend(reply_port_cap, kHeaderSize + resp_length, resp_buffer.RawPtr(), resp_cap.UsedSlots(), resp_cap.RawPtr())); - } - if (reply_err != glcr::OK) { - dbgln("Error in reply: {x}", reply_err); - } - } - -} - -glcr::Status VFSServerBase::HandleRequest(const glcr::ByteBuffer& request, - const glcr::CapBuffer& req_caps, - glcr::ByteBuffer& response, uint64_t& resp_length, - glcr::CapBuffer& resp_caps) { - if (request.At(0) != kSentinel) { - return glcr::InvalidArgument("Request Not Valid"); - } - - uint64_t method_select = request.At(8); - - switch(method_select) { - case 0: { - - - OpenFileRequest yunq_request; - RETURN_ERROR(yunq_request.ParseFromBytes(request, kHeaderSize, req_caps)); - - - - OpenFileResponse yunq_response; - - - - RETURN_ERROR(HandleOpenFile(yunq_request, yunq_response)); - - - - resp_length = yunq_response.SerializeToBytes(response, kHeaderSize, resp_caps); - - break; - } - case 1: { - - - GetDirectoryRequest yunq_request; - RETURN_ERROR(yunq_request.ParseFromBytes(request, kHeaderSize, req_caps)); - - - - Directory yunq_response; - - - - RETURN_ERROR(HandleGetDirectory(yunq_request, yunq_response)); - - - - resp_length = yunq_response.SerializeToBytes(response, kHeaderSize, resp_caps); - - break; - } - default: { - return glcr::Unimplemented("Method unimplemented by server."); - } - } - return glcr::Status::Ok(); -} - - diff --git a/sys/victoriafalls/lib/victoriafalls/victoriafalls.yunq.server.h b/sys/victoriafalls/lib/victoriafalls/victoriafalls.yunq.server.h deleted file mode 100644 index bf91f66..0000000 --- a/sys/victoriafalls/lib/victoriafalls/victoriafalls.yunq.server.h +++ /dev/null @@ -1,50 +0,0 @@ -// Generated File -- DO NOT MODIFY. -#pragma once - -#include -#include -#include -#include - -#include "victoriafalls.yunq.h" -#include "victoriafalls.yunq.client.h" - - - - - -class VFSServerBase { - public: - VFSServerBase(z_cap_t VFS_cap) : endpoint_(VFS_cap) {} - VFSServerBase(const VFSServerBase&) = delete; - VFSServerBase(VFSServerBase&&) = delete; - virtual ~VFSServerBase(); - - glcr::ErrorOr CreateClientCap(); - glcr::ErrorOr CreateClient(); - - [[nodiscard]] Thread RunServer(); - - - - [[nodiscard]] virtual glcr::Status HandleOpenFile(const OpenFileRequest&, OpenFileResponse&) = 0; - - - - [[nodiscard]] virtual glcr::Status HandleGetDirectory(const GetDirectoryRequest&, Directory&) = 0; - - - - private: - z_cap_t endpoint_; - - friend void VFSServerBaseThreadBootstrap(void*); - void ServerThread(); - - [[nodiscard]] glcr::Status HandleRequest(const glcr::ByteBuffer& request, const glcr::CapBuffer& req_caps, - glcr::ByteBuffer& response, uint64_t& resp_length, - glcr::CapBuffer& resp_caps); -}; - - - diff --git a/sys/victoriafalls/victoriafalls.cpp b/sys/victoriafalls/victoriafalls.cpp deleted file mode 100644 index 56ee383..0000000 --- a/sys/victoriafalls/victoriafalls.cpp +++ /dev/null @@ -1,35 +0,0 @@ -#include -#include -#include - -#include "fs/ext2/ext2_driver.h" -#include "victoriafalls_server.h" - -using yellowstone::DenaliInfo; -using yellowstone::RegisterEndpointRequest; -using yellowstone::YellowstoneClient; - -uint64_t main(uint64_t init_cap) { - ParseInitPort(init_cap); - - dbgln("VFs Started"); - - YellowstoneClient yellowstone(gInitEndpointCap); - DenaliInfo denali_info; - check(yellowstone.GetDenali(denali_info)); - ASSIGN_OR_RETURN(Ext2Driver ext2, Ext2Driver::Init(denali_info)); - - ASSIGN_OR_RETURN(auto server, VFSServer::Create(ext2)); - - Thread server_thread = server->RunServer(); - - RegisterEndpointRequest req; - req.set_endpoint_name("victoriafalls"); - ASSIGN_OR_RETURN(auto client_cap, server->CreateClientCap()); - req.set_endpoint_capability(client_cap); - check(yellowstone.RegisterEndpoint(req)); - - RET_ERR(server_thread.Join()); - - return 0; -} diff --git a/sys/victoriafalls/victoriafalls_server.cpp b/sys/victoriafalls/victoriafalls_server.cpp deleted file mode 100644 index fbc5e42..0000000 --- a/sys/victoriafalls/victoriafalls_server.cpp +++ /dev/null @@ -1,109 +0,0 @@ -#include "victoriafalls_server.h" - -#include -#include -#include - -glcr::ErrorOr> VFSServer::Create( - Ext2Driver& driver) { - z_cap_t endpoint_cap; - RET_ERR(ZEndpointCreate(&endpoint_cap)); - return glcr::UniquePtr(new VFSServer(endpoint_cap, driver)); -} - -glcr::Status VFSServer::HandleOpenFile(const OpenFileRequest& request, - OpenFileResponse& response) { - auto path_tokens = glcr::StrSplit(request.path(), '/'); - // Require all paths to be absolute rather than relative. - // If the path starts with '/' then the first token will be empty. - if (path_tokens.at(0) != "") { - return glcr::InvalidArgument("Open file supports only absolute paths."); - } - - ASSIGN_OR_RETURN(auto files, driver_.ReadDirectory(2)); - for (uint64_t i = 1; i < path_tokens.size() - 1; i++) { - bool found_token = false; - for (uint64_t j = 0; j < files.size() && !found_token; j++) { - if (path_tokens.at(i) == - glcr::StringView(files.at(j).name, files.at(j).name_len)) { - ASSIGN_OR_RETURN(files, driver_.ReadDirectory(files.at(j).inode)); - found_token = true; - } - } - if (!found_token) { - return glcr::NotFound(glcr::StrFormat("Directory '{}' not found.", - glcr::String(path_tokens.at(i)))); - } - } - - uint64_t inode_num; - mmth::OwnedMemoryRegion region; - for (uint64_t j = 0; j < files.size(); j++) { - if (path_tokens.at(path_tokens.size() - 1) == - glcr::StringView(files.at(j).name, files.at(j).name_len)) { - inode_num = files.at(j).inode; - ASSIGN_OR_RETURN(region, driver_.ReadFile(files.at(j).inode)); - break; - } - } - if (!region) { - return glcr::NotFound( - glcr::StrFormat("File '{}' not found.", - glcr::String(path_tokens.at(path_tokens.size() - 1)))); - } - - response.set_path(request.path()); - // FIXME: There isn't really a reason we need to map the file into memory then - // duplicate the cap. In the future just get the cap from the read then pass - // it to the caller directly. - response.set_memory(region.DuplicateCap()); - // TODO: Consider folding this up into the actual read call. - ASSIGN_OR_RETURN(Inode * inode, driver_.GetInode(inode_num)); - // FIXME: This technically only sets the lower 32 bits. - response.set_size(inode->size); - return glcr::Status::Ok(); -} - -glcr::Status VFSServer::HandleGetDirectory(const GetDirectoryRequest& request, - Directory& response) { - auto path_tokens = glcr::StrSplit(request.path(), '/'); - - if (path_tokens.at(0) != "") { - return glcr::InvalidArgument("Get Directory only supports absolute path."); - } - - // If there is a trailing slash we can get rid of the empty string. - if (path_tokens.at(path_tokens.size() - 1) == "") { - path_tokens.PopBack(); - } - - ASSIGN_OR_RETURN(auto files, driver_.ReadDirectory(2)); - for (uint64_t i = 1; i < path_tokens.size(); i++) { - bool found_token = false; - for (uint64_t j = 0; j < files.size() && !found_token; j++) { - if (path_tokens.at(i) == - glcr::StringView(files.at(j).name, files.at(j).name_len)) { - ASSIGN_OR_RETURN(files, driver_.ReadDirectory(files.at(j).inode)); - found_token = true; - } - } - if (!found_token) { - return glcr::NotFound(glcr::StrFormat("Directory '{}' not found.", - glcr::String(path_tokens.at(i)))); - } - } - - glcr::VariableStringBuilder filelist; - for (uint64_t i = 0; i < files.size(); i++) { - filelist.PushBack(glcr::StringView(files.at(i).name, files.at(i).name_len)); - filelist.PushBack(','); - } - // Remove trailing comma. - if (filelist.size() > 0) { - filelist.DeleteLast(); - } - - response.set_filenames(filelist.ToString()); - - return glcr::Status::Ok(); -} diff --git a/sys/victoriafalls/victoriafalls_server.h b/sys/victoriafalls/victoriafalls_server.h deleted file mode 100644 index 8716093..0000000 --- a/sys/victoriafalls/victoriafalls_server.h +++ /dev/null @@ -1,24 +0,0 @@ -#pragma once - -#include - -#include "fs/ext2/ext2_driver.h" -#include "victoriafalls/victoriafalls.yunq.server.h" - -class VFSServer : public VFSServerBase { - public: - static glcr::ErrorOr> Create(Ext2Driver& driver); - - glcr::Status HandleOpenFile(const OpenFileRequest&, - OpenFileResponse&) override; - - glcr::Status HandleGetDirectory(const GetDirectoryRequest&, - Directory&) override; - - private: - // FIXME: Don't store this as a reference. - Ext2Driver& driver_; - - VFSServer(z_cap_t endpoint_cap, Ext2Driver& driver) - : VFSServerBase(endpoint_cap), driver_(driver) {} -}; diff --git a/sys/voyageurs/CMakeLists.txt b/sys/voyageurs/CMakeLists.txt index 10a3754..75a852c 100644 --- a/sys/voyageurs/CMakeLists.txt +++ b/sys/voyageurs/CMakeLists.txt @@ -1,5 +1,10 @@ add_executable(voyageurs keyboard/keyboard_driver.cpp + xhci/device_slot.cpp + xhci/endpoint.cpp + xhci/trb.cpp + xhci/trb_ring.cpp + xhci/xhci_driver.cpp voyageurs_server.cpp voyageurs.cpp) diff --git a/sys/voyageurs/keyboard/keyboard_driver.cpp b/sys/voyageurs/keyboard/keyboard_driver.cpp index f96a3ad..fc4f1d1 100644 --- a/sys/voyageurs/keyboard/keyboard_driver.cpp +++ b/sys/voyageurs/keyboard/keyboard_driver.cpp @@ -2,13 +2,24 @@ #include +namespace { + +KeyboardDriver* gKeyboardDriver = nullptr; + +} + void InterruptEnter(void* void_keyboard) { KeyboardDriver* keyboard = static_cast(void_keyboard); keyboard->InterruptLoop(); } -KeyboardDriver::KeyboardDriver() { check(ZIrqRegister(kZIrqKbd, &irq_cap_)); } +KeyboardDriver::KeyboardDriver() { + check(ZPortCreate(&port_cap_)); + gKeyboardDriver = this; +} + +z_cap_t KeyboardDriver::GetPortCap() { return gKeyboardDriver->port_cap_; } void KeyboardDriver::RegisterListener(uint64_t port_cap) { listeners_.PushFront(mmth::PortClient::AdoptPort(port_cap)); @@ -21,13 +32,37 @@ Thread KeyboardDriver::StartInterruptLoop() { void KeyboardDriver::InterruptLoop() { dbgln("Interrupt"); while (true) { - uint8_t scancode; - uint64_t num_bytes = 1; + uint64_t scancode; + uint64_t num_bytes = 8; uint64_t num_caps = 0; - check(ZPortRecv(irq_cap_, &num_bytes, &scancode, &num_caps, nullptr)); + check(ZPortRecv(port_cap_, &num_bytes, &scancode, &num_caps, nullptr)); - for (mmth::PortClient& client : listeners_) { - client.WriteByte(scancode); - } + ProcessInput(scancode); + } +} + +void KeyboardDriver::ProcessInput(uint64_t input) { + uint16_t modifiers = (input & 0xFF) << 8; + uint64_t new_bitmap = 0; + for (uint8_t i = 2; i < 8; i++) { + uint8_t code = (input >> (8 * i)) & 0xFF; + if (code == 0) { + break; + } + if (code >= 64) { + dbgln("Skipping keycode: {x}", code); + } + uint64_t bit = 1 << code; + new_bitmap |= bit; + if ((bitmap_ & bit) != bit) { + SendKeypress(modifiers | code); + } + } + bitmap_ = new_bitmap; +} + +void KeyboardDriver::SendKeypress(uint16_t scancode) { + for (mmth::PortClient& client : listeners_) { + client.Write(scancode); } } diff --git a/sys/voyageurs/keyboard/keyboard_driver.h b/sys/voyageurs/keyboard/keyboard_driver.h index b38af0e..54dee91 100644 --- a/sys/voyageurs/keyboard/keyboard_driver.h +++ b/sys/voyageurs/keyboard/keyboard_driver.h @@ -11,12 +11,19 @@ class KeyboardDriver { KeyboardDriver(const KeyboardDriver&) = delete; KeyboardDriver(KeyboardDriver&&) = delete; + static z_cap_t GetPortCap(); + void RegisterListener(uint64_t port_cap); Thread StartInterruptLoop(); void InterruptLoop(); private: - z_cap_t irq_cap_; + z_cap_t port_cap_; glcr::LinkedList listeners_; + + uint64_t bitmap_ = 0; + + void ProcessInput(uint64_t input); + void SendKeypress(uint16_t scancode); }; diff --git a/sys/voyageurs/lib/voyageurs/voyageurs.yunq.cpp b/sys/voyageurs/lib/voyageurs/voyageurs.yunq.cpp index 3adfc57..1ad84f2 100644 --- a/sys/voyageurs/lib/voyageurs/voyageurs.yunq.cpp +++ b/sys/voyageurs/lib/voyageurs/voyageurs.yunq.cpp @@ -1,6 +1,7 @@ // Generated file -- DO NOT MODIFY. #include "voyageurs.yunq.h" +#include #include @@ -14,55 +15,43 @@ struct ExtPointer { }; } // namespace -glcr::Status KeyboardListener::ParseFromBytes(const glcr::ByteBuffer& bytes, uint64_t offset) { - RETURN_ERROR(ParseFromBytesInternal(bytes, offset)); +glcr::Status KeyboardListener::ParseFromBytes(const yunq::MessageView& message) { + RETURN_ERROR(ParseFromBytesInternal(message)); // Parse port_capability. - // FIXME: Implement in-buffer capabilities for inprocess serialization. - set_port_capability(0); + ASSIGN_OR_RETURN(port_capability_, message.ReadCapability(0)); return glcr::Status::Ok(); } -glcr::Status KeyboardListener::ParseFromBytes(const glcr::ByteBuffer& bytes, uint64_t offset, const glcr::CapBuffer& caps) { - RETURN_ERROR(ParseFromBytesInternal(bytes, offset)); +glcr::Status KeyboardListener::ParseFromBytes(const yunq::MessageView& message, const glcr::CapBuffer& caps) { + RETURN_ERROR(ParseFromBytesInternal(message)); // Parse port_capability. - uint64_t port_capability_ptr = bytes.At(offset + header_size + (8 * 0)); - - set_port_capability(caps.At(port_capability_ptr)); + ASSIGN_OR_RETURN(port_capability_, message.ReadCapability(0, caps)); return glcr::Status::Ok(); } -glcr::Status KeyboardListener::ParseFromBytesInternal(const glcr::ByteBuffer& bytes, uint64_t offset) { - RETURN_ERROR(yunq::CheckHeader(bytes, offset)); +glcr::Status KeyboardListener::ParseFromBytesInternal(const yunq::MessageView& message) { + RETURN_ERROR(message.CheckHeader()); // Parse port_capability. - // Skip Cap. return glcr::Status::Ok(); } uint64_t KeyboardListener::SerializeToBytes(glcr::ByteBuffer& bytes, uint64_t offset) const { - uint32_t next_extension = header_size + 8 * 1; - const uint32_t core_size = next_extension; - // Write port_capability. - // FIXME: Implement inbuffer capabilities. - bytes.WriteAt(offset + header_size + (8 * 0), 0); - - // The next extension pointer is the length of the message. - yunq::WriteHeader(bytes, offset, core_size, next_extension); - - return next_extension; + yunq::Serializer serializer(bytes, offset, 1); + return SerializeInternal(serializer); } uint64_t KeyboardListener::SerializeToBytes(glcr::ByteBuffer& bytes, uint64_t offset, glcr::CapBuffer& caps) const { - uint32_t next_extension = header_size + 8 * 1; - const uint32_t core_size = next_extension; - uint64_t next_cap = 0; + yunq::Serializer serializer(bytes, offset, 1, caps); + return SerializeInternal(serializer); +} + +uint64_t KeyboardListener::SerializeInternal(yunq::Serializer& serializer) const { // Write port_capability. - caps.WriteAt(next_cap, port_capability()); - bytes.WriteAt(offset + header_size + (8 * 0), next_cap++); + serializer.WriteCapability(0, port_capability_); - // The next extension pointer is the length of the message. - yunq::WriteHeader(bytes, offset, core_size, next_extension); + serializer.WriteHeader(); - return next_extension; + return serializer.size(); } diff --git a/sys/voyageurs/lib/voyageurs/voyageurs.yunq.h b/sys/voyageurs/lib/voyageurs/voyageurs.yunq.h index da5c585..af8eca1 100644 --- a/sys/voyageurs/lib/voyageurs/voyageurs.yunq.h +++ b/sys/voyageurs/lib/voyageurs/voyageurs.yunq.h @@ -6,6 +6,8 @@ #include #include #include +#include +#include #include @@ -14,19 +16,24 @@ class KeyboardListener { KeyboardListener() {} // Delete copy and move until implemented. KeyboardListener(const KeyboardListener&) = delete; - KeyboardListener(KeyboardListener&&) = delete; + KeyboardListener(KeyboardListener&&) = default; + KeyboardListener& operator=(KeyboardListener&&) = default; - [[nodiscard]] glcr::Status ParseFromBytes(const glcr::ByteBuffer&, uint64_t offset); - [[nodiscard]] glcr::Status ParseFromBytes(const glcr::ByteBuffer&, uint64_t offset, const glcr::CapBuffer&); + [[nodiscard]] glcr::Status ParseFromBytes(const yunq::MessageView& message); + [[nodiscard]] glcr::Status ParseFromBytes(const yunq::MessageView& message, const glcr::CapBuffer&); uint64_t SerializeToBytes(glcr::ByteBuffer&, uint64_t offset) const; uint64_t SerializeToBytes(glcr::ByteBuffer&, uint64_t offset, glcr::CapBuffer&) const; + const z_cap_t& port_capability() const { return port_capability_; } + z_cap_t& mutable_port_capability() { return port_capability_; } void set_port_capability(const z_cap_t& value) { port_capability_ = value; } private: z_cap_t port_capability_; // Parses everything except for caps. - glcr::Status ParseFromBytesInternal(const glcr::ByteBuffer&, uint64_t offset); + glcr::Status ParseFromBytesInternal(const yunq::MessageView& message); + + uint64_t SerializeInternal(yunq::Serializer& serializer) const; }; diff --git a/sys/voyageurs/lib/voyageurs/voyageurs.yunq.server.cpp b/sys/voyageurs/lib/voyageurs/voyageurs.yunq.server.cpp index f8376dc..80fa141 100644 --- a/sys/voyageurs/lib/voyageurs/voyageurs.yunq.server.cpp +++ b/sys/voyageurs/lib/voyageurs/voyageurs.yunq.server.cpp @@ -104,7 +104,8 @@ glcr::Status VoyageursServerBase::HandleRequest(const glcr::ByteBuffer& request, KeyboardListener yunq_request; - RETURN_ERROR(yunq_request.ParseFromBytes(request, kHeaderSize, req_caps)); + yunq::MessageView request_view(request, kHeaderSize); + RETURN_ERROR(yunq_request.ParseFromBytes(request_view, req_caps)); diff --git a/sys/voyageurs/voyageurs.cpp b/sys/voyageurs/voyageurs.cpp index f3d375f..663d972 100644 --- a/sys/voyageurs/voyageurs.cpp +++ b/sys/voyageurs/voyageurs.cpp @@ -5,6 +5,7 @@ #include "keyboard/keyboard_driver.h" #include "voyageurs_server.h" +#include "xhci/xhci_driver.h" using yellowstone::RegisterEndpointRequest; using yellowstone::YellowstoneClient; @@ -12,9 +13,12 @@ using yellowstone::YellowstoneClient; uint64_t main(uint64_t init_port) { ParseInitPort(init_port); - dbgln("Initializing PS/2 Driver."); + YellowstoneClient yellowstone(gInitEndpointCap); + KeyboardDriver driver; + ASSIGN_OR_RETURN(auto xhci, XhciDriver::InitiateDriver(yellowstone)); + dbgln("Starting PS/2 Thread."); Thread keyboard_thread = driver.StartInterruptLoop(); @@ -24,8 +28,6 @@ uint64_t main(uint64_t init_port) { Thread server_thread = server->RunServer(); - YellowstoneClient yellowstone(gInitEndpointCap); - RegisterEndpointRequest req; req.set_endpoint_name("voyageurs"); ASSIGN_OR_RETURN(z_cap_t client_cap, server->CreateClientCap()); diff --git a/sys/voyageurs/xhci/control_command.h b/sys/voyageurs/xhci/control_command.h new file mode 100644 index 0000000..9cc05cd --- /dev/null +++ b/sys/voyageurs/xhci/control_command.h @@ -0,0 +1,69 @@ +#pragma once + +#include +#include +#include + +#include "xhci/descriptors.h" +#include "xhci/xhci.h" + +template +class ReadControlCommand { + public: + ReadControlCommand() { + output_mem_ = + mmth::OwnedMemoryRegion::ContiguousPhysical(0x1000, &output_phys_); + } + + XhciTrb SetupTrb() { + uint64_t request_type = RequestConstants::RequestType(); + uint64_t request = RequestConstants::Request() << 8; + uint64_t value = RequestConstants::Value() << 16; + // TODO: May need to be non-0 for string descriptors. + uint64_t index = (uint64_t)0 << 32; + uint64_t length = (uint64_t)0x1000 << 48; + return { + .parameter = request_type | request | value | index | length, + .status = 8, + .type_and_cycle = 1 | (1 << 5) | (1 << 6) | (2 << 10), + // IN Data Stage + .control = 3, + }; + } + + XhciTrb DataTrb() { + return { + .parameter = output_phys_, + .status = 0x1000, + .type_and_cycle = 1 | (1 << 5) | (3 << 10), + .control = 1, + }; + } + + XhciTrb StatusTrb() { + return { + .parameter = 0, + .status = 0, + .type_and_cycle = 1 | (1 << 5) | (4 << 10), + .control = 0, + }; + } + + Output* AwaitResult() { + if (!is_complete_) { + semaphore_->Wait(); + } + is_complete_ = true; + return reinterpret_cast(output_mem_.vaddr()); + } + + glcr::SharedPtr CompletionSemaphore() { return semaphore_; } + + private: + uint64_t output_phys_; + mmth::OwnedMemoryRegion output_mem_; + + bool is_complete_ = false; + glcr::SharedPtr semaphore_ = + glcr::MakeShared(); +}; diff --git a/sys/voyageurs/xhci/descriptors.h b/sys/voyageurs/xhci/descriptors.h new file mode 100644 index 0000000..8aec53b --- /dev/null +++ b/sys/voyageurs/xhci/descriptors.h @@ -0,0 +1,77 @@ +#pragma once + +#include + +template +class RequestConstants { + public: + static uint8_t RequestType(); + static uint8_t Request(); + static uint16_t Value(); +}; + +struct DeviceDescriptor { + uint8_t length; + uint8_t type; + uint16_t usb_spec; + uint8_t device_class; + uint8_t device_subclass; + uint8_t device_protocol; + uint8_t max_packet_size_exp; + uint16_t vendor_id; + uint16_t product_id; + uint16_t device_release; + uint8_t manufacturer_string_index; + uint8_t product_string_index; + uint8_t serial_string_index; + uint8_t num_configurations; +} __attribute__((packed)); + +template <> +class RequestConstants { + public: + static uint8_t RequestType() { return 0x80; } + static uint8_t Request() { return 0x6; }; + static uint16_t Value() { return 0x100; }; +}; + +struct ConfigurationDescriptor { + uint8_t length; + uint8_t type; + uint16_t total_length; + uint8_t num_interfaces; + uint8_t configuration_value; + uint8_t configuration_string_index; + uint8_t attributes; + uint8_t max_power; +} __attribute__((packed)); + +template <> +class RequestConstants { + public: + static uint8_t RequestType() { return 0x80; } + static uint8_t Request() { return 0x6; } + // TODO: Consider getting other description indexes. + static uint16_t Value() { return 0x200; } +}; + +struct InterfaceDescriptor { + uint8_t length; + uint8_t type; + uint8_t interface_number; + uint8_t alternate_setting; + uint8_t num_endpoints; + uint8_t interface_class; + uint8_t interface_subclass; + uint8_t interface_protocol; + uint8_t interfact_string_index; +} __attribute__((packed)); + +struct EndpointDescriptor { + uint8_t length; + uint8_t type; + uint8_t endpoint_address; + uint8_t attributes; + uint16_t max_packet_size; + uint8_t interval; +} __attribute__((packed)); diff --git a/sys/voyageurs/xhci/device_slot.cpp b/sys/voyageurs/xhci/device_slot.cpp new file mode 100644 index 0000000..1634f7c --- /dev/null +++ b/sys/voyageurs/xhci/device_slot.cpp @@ -0,0 +1,108 @@ +#include "xhci/device_slot.h" + +#include +#include + +#include "xhci/trb.h" +#include "xhci/xhci_driver.h" + +void DeviceSlot::EnableAndInitializeDataStructures( + XhciDriver* driver, uint8_t slot_index, uint64_t* output_context, + volatile uint32_t* doorbell) { + enabled_ = true; + xhci_driver_ = driver; + slot_index_ = slot_index; + doorbell_ = doorbell; + + context_memory_ = + mmth::OwnedMemoryRegion::ContiguousPhysical(0x1000, &context_phys_); + + device_context_ = + reinterpret_cast(context_memory_.vaddr()); + *output_context = context_phys_; + input_context_ = reinterpret_cast(context_memory_.vaddr() + + kInputSlotContextOffset); + + control_endpoint_transfer_trb_ = glcr::MakeUnique(); +} + +XhciTrb DeviceSlot::CreateAddressDeviceCommand(uint8_t root_port, + uint32_t route_string, + uint16_t max_packet_size) { + // Initialize Slot Context and Endpoint 0 Context. + input_context_->input.add_contexts = 0x3; + input_context_->input.drop_contexts = 0; + // Set context_entries to 1. XHCI 4.3.3 + input_context_->slot_context.route_speed_entries = (0x1 << 27) | route_string; + input_context_->slot_context.latency_port_number = root_port << 16; + + // Initialize Control Endpoint. + input_context_->endpoint_contexts[0].state = 0; + constexpr uint16_t kCerr = 0x3 << 1; + constexpr uint16_t kTypeControl = 0x4 << 3; + input_context_->endpoint_contexts[0].error_and_type = + kCerr | kTypeControl | (max_packet_size << 16); + + input_context_->endpoint_contexts[0].tr_dequeue_ptr = + control_endpoint_transfer_trb_->PhysicalAddress() | 0x1; + + endpoints_ = glcr::Array(32); + + return ::CreateAddressDeviceCommand(context_phys_ + kInputSlotContextOffset, + slot_index_); +} + +uint8_t DeviceSlot::State() { + return device_context_->slot_context.address_and_state >> 27; +} + +void DeviceSlot::TransferComplete(uint8_t endpoint_index, uint64_t trb_phys) { + if (endpoint_index >= 32) { + dbgln("ERROR: Received transfer for invalid endpoint {x}", endpoint_index); + return; + } + + if (endpoint_index != 1) { + if (!endpoints_[endpoint_index].Enabled()) { + dbgln("ERROR: XHCI received transfer for disabled endpoint {x}", + endpoint_index); + return; + } + endpoints_[endpoint_index].TransferComplete(trb_phys); + return; + } + + if (!control_completion_sempahores_.Contains(trb_phys)) { + // Skip this if we don't have a semaphore for it (Setup and Transfer Trbs). + return; + } + + control_completion_sempahores_.at(trb_phys)->Signal(); + check(control_completion_sempahores_.Delete(trb_phys)); +} + +mmth::Semaphore DeviceSlot::IssueConfigureDeviceCommand( + uint8_t config_value, glcr::UniquePtr client) { + input_context_->input.add_contexts = (0x1 << 3) | 0x1; + input_context_->input.configuration_value = config_value; + // TODO: Maybe don't hardcode this. + input_context_->input.interface_number = 0; + + input_context_->slot_context.route_speed_entries &= 0xFFFFFF; + + // TODO: Don't hardcode this. + uint8_t max_endpoint = 0x3; + input_context_->slot_context.route_speed_entries |= (max_endpoint << 27); + + // TODO: Dont' hardcode this. + endpoints_[3].Initialize(input_context_->endpoint_contexts + 2, + glcr::Move(client)); + + xhci_driver_->IssueCommand(CreateConfigureEndpointCommand( + context_phys_ + kInputSlotContextOffset, slot_index_)); + return configure_device_semaphore_; +} + +void DeviceSlot::SignalConfigureDeviceCompleted() { + configure_device_semaphore_.Signal(); +} diff --git a/sys/voyageurs/xhci/device_slot.h b/sys/voyageurs/xhci/device_slot.h new file mode 100644 index 0000000..473d1c5 --- /dev/null +++ b/sys/voyageurs/xhci/device_slot.h @@ -0,0 +1,78 @@ +#pragma once + +#include +#include +#include +#include +#include +#include + +#include "xhci/control_command.h" +#include "xhci/endpoint.h" +#include "xhci/trb_ring.h" +#include "xhci/xhci.h" + +class XhciDriver; + +class DeviceSlot { + public: + DeviceSlot() = default; + DeviceSlot(const DeviceSlot&) = delete; + DeviceSlot(DeviceSlot&&) = delete; + + void EnableAndInitializeDataStructures(XhciDriver* driver, + uint8_t slot_index_, + uint64_t* output_context, + volatile uint32_t* doorbell); + + XhciTrb CreateAddressDeviceCommand(uint8_t root_port, uint32_t route_string, + uint16_t max_packet_size); + + mmth::Semaphore IssueConfigureDeviceCommand( + uint8_t config_value, glcr::UniquePtr client); + void SignalConfigureDeviceCompleted(); + + // Caller must keep the command in scope until it completes. + template + void ExecuteReadControlCommand(ReadControlCommand& command); + + void TransferComplete(uint8_t endpoint_index, uint64_t trb_phys); + + uint8_t State(); + + private: + bool enabled_ = false; + + XhciDriver* xhci_driver_; + uint8_t slot_index_ = 0; + volatile uint32_t* doorbell_ = nullptr; + + uint64_t context_phys_ = 0; + mmth::OwnedMemoryRegion context_memory_; + + static constexpr uint64_t kInputSlotContextOffset = 0x400; + + XhciDeviceContext* device_context_; + XhciInputContext* input_context_; + + glcr::UniquePtr control_endpoint_transfer_trb_; + glcr::HashMap> + control_completion_sempahores_; + + mmth::Semaphore configure_device_semaphore_; + + glcr::Array endpoints_; +}; + +template +void DeviceSlot::ExecuteReadControlCommand(ReadControlCommand& command) { + control_endpoint_transfer_trb_->EnqueueTrb(command.SetupTrb()); + control_endpoint_transfer_trb_->EnqueueTrb(command.DataTrb()); + uint64_t last_phys = + control_endpoint_transfer_trb_->EnqueueTrb(command.StatusTrb()); + // Ring the control endpoint doorbell. + *doorbell_ = 1; + + check(control_completion_sempahores_.Insert(last_phys, + command.CompletionSemaphore())); +} diff --git a/sys/voyageurs/xhci/endpoint.cpp b/sys/voyageurs/xhci/endpoint.cpp new file mode 100644 index 0000000..6583953 --- /dev/null +++ b/sys/voyageurs/xhci/endpoint.cpp @@ -0,0 +1,32 @@ +#include "xhci/endpoint.h" + +#include + +#include "xhci/trb.h" + +void Endpoint::Initialize(XhciEndpointContext* context, + glcr::UniquePtr client) { + enabled_ = true; + context_ = context; + client_ = glcr::Move(client); + + trb_ring_ = glcr::MakeUnique(); + recv_mem_ = mmth::OwnedMemoryRegion::ContiguousPhysical(0x1000, &recv_phys_); + + context_->tr_dequeue_ptr = trb_ring_->PhysicalAddress() | 1; + + context_->error_and_type = (0x3 << 1) | (0x7 << 3) | (0x8 << 16); + for (uint64_t i = 0; i < 10; i++) { + trb_ring_->EnqueueTrb(CreateNormalTrb(recv_phys_ + offset_, 8)); + offset_ += 8; + } +} + +void Endpoint::TransferComplete(uint64_t trb_phys) { + uint64_t phys_offset = + (trb_phys - trb_ring_->PhysicalAddress()) / sizeof(XhciTrb); + uint64_t data = *((uint64_t*)recv_mem_.vaddr() + phys_offset); + client_->Write(data); + trb_ring_->EnqueueTrb(CreateNormalTrb(recv_phys_ + offset_, 8)); + offset_ += 8; +} diff --git a/sys/voyageurs/xhci/endpoint.h b/sys/voyageurs/xhci/endpoint.h new file mode 100644 index 0000000..fd4a4d0 --- /dev/null +++ b/sys/voyageurs/xhci/endpoint.h @@ -0,0 +1,31 @@ +#pragma once + +#include +#include + +#include "xhci/trb_ring.h" +#include "xhci/xhci.h" + +class Endpoint { + public: + Endpoint() {} + + void Initialize(XhciEndpointContext* context, + glcr::UniquePtr client); + + bool Enabled() { return enabled_; } + + void TransferComplete(uint64_t trb_phys); + + private: + bool enabled_ = false; + + XhciEndpointContext* context_ = nullptr; + glcr::UniquePtr trb_ring_; + + uint64_t recv_phys_; + mmth::OwnedMemoryRegion recv_mem_; + uint64_t offset_ = 0; + + glcr::UniquePtr client_; +}; diff --git a/sys/voyageurs/xhci/trb.cpp b/sys/voyageurs/xhci/trb.cpp new file mode 100644 index 0000000..7fca204 --- /dev/null +++ b/sys/voyageurs/xhci/trb.cpp @@ -0,0 +1,84 @@ +#include "xhci/trb.h" + +constexpr uint8_t kTrb_TypeOffset = 10; + +constexpr uint16_t kTrb_Cycle = 1; +constexpr uint16_t kTrb_ToggleCycle = (1 << 1); +constexpr uint16_t kTrb_InterruptShortPacket = (1 << 2); +constexpr uint16_t kTrb_Interrupt = (1 << 5); +constexpr uint16_t kTrb_BSR = (1 << 9); + +namespace { + +uint16_t TypeToInt(TrbType type) { + return static_cast(type) << kTrb_TypeOffset; +} + +} // namespace + +TrbType GetType(const XhciTrb& trb) { + return TrbType(trb.type_and_cycle >> kTrb_TypeOffset); +} + +XhciTrb CreateNormalTrb(uint64_t physical_address, uint8_t size) { + return { + .parameter = physical_address, + .status = size, + .type_and_cycle = (uint16_t)(TypeToInt(TrbType::Normal) | kTrb_Cycle | + kTrb_Interrupt | kTrb_InterruptShortPacket), + .control = 0, + }; +} + +XhciTrb CreateLinkTrb(uint64_t physical_address) { + return { + .parameter = physical_address, + .status = 0, + .type_and_cycle = (uint16_t)(TypeToInt(TrbType::Link) | kTrb_ToggleCycle), + .control = 0, + }; +} + +XhciTrb CreateEnableSlotTrb() { + return { + .parameter = 0, + .status = 0, + // FIXME: Accept Cycle Bit as a parameter. + .type_and_cycle = (uint16_t)(TypeToInt(TrbType::EnableSlot) | kTrb_Cycle), + // FIXME: Specify slot type if necessary. (XHCI Table 7-9)? + .control = 0, + }; +} + +XhciTrb CreateAddressDeviceCommand(uint64_t input_context, uint8_t slot_id) { + return { + .parameter = input_context, + .status = 0, + // Always cycle the device straight to addressed. + .type_and_cycle = + (uint16_t)(TypeToInt(TrbType::AddressDevice) | kTrb_Cycle), + .control = (uint16_t)(slot_id << 8), + }; +} + +XhciTrb CreateConfigureEndpointCommand(uint64_t input_context, + uint8_t slot_id) { + return { + .parameter = input_context, + .status = 0, + .type_and_cycle = + (uint16_t)(TypeToInt(TrbType::ConfigureEndpoint) | kTrb_Cycle), + .control = (uint16_t)(slot_id << 8), + }; +} + +XhciTrb CreateNoOpCommandTrb() { + return { + .parameter = 0, + .status = 0, + // FIXME: Accept Cycle Bit as a parameter. + .type_and_cycle = + (uint16_t)(TypeToInt(TrbType::NoOpCommand) | kTrb_Cycle), + .control = 0, + }; +} diff --git a/sys/voyageurs/xhci/trb.h b/sys/voyageurs/xhci/trb.h new file mode 100644 index 0000000..f7fee03 --- /dev/null +++ b/sys/voyageurs/xhci/trb.h @@ -0,0 +1,38 @@ +#pragma once + +#include "xhci/xhci.h" + +enum class TrbType : uint8_t { + Reserved = 0, + + // Transfers + Normal = 1, + SetupStage = 2, + DataStage = 3, + StatusStage = 4, + Isoch = 5, + Link = 6, + EventData = 7, + NoOp = 8, + + // Commands + EnableSlot = 9, + AddressDevice = 11, + ConfigureEndpoint = 12, + NoOpCommand = 23, + + // Events + Transfer = 32, + CommandCompletion = 33, + PortStatusChange = 34, +}; + +TrbType GetType(const XhciTrb& trb); + +XhciTrb CreateNormalTrb(uint64_t physical_address, uint8_t size); +XhciTrb CreateLinkTrb(uint64_t physical_address); + +XhciTrb CreateEnableSlotTrb(); +XhciTrb CreateAddressDeviceCommand(uint64_t input_context, uint8_t slot_id); +XhciTrb CreateConfigureEndpointCommand(uint64_t input_context, uint8_t slot_id); +XhciTrb CreateNoOpCommandTrb(); diff --git a/sys/voyageurs/xhci/trb_ring.cpp b/sys/voyageurs/xhci/trb_ring.cpp new file mode 100644 index 0000000..f251a63 --- /dev/null +++ b/sys/voyageurs/xhci/trb_ring.cpp @@ -0,0 +1,46 @@ +#include "xhci/trb_ring.h" + +#include + +#include "xhci/trb.h" + +TrbRing::TrbRing() { + uint64_t number_trbs = 0x1000 / sizeof(XhciTrb); + page_ = mmth::OwnedMemoryRegion::ContiguousPhysical(0x1000, &phys_address_); + + // Zero out data. + uint64_t* page_ptr = reinterpret_cast(page_.vaddr()); + for (uint64_t i = 0; i < 0x1000 / sizeof(uint64_t); i++) { + page_ptr[i] = 0; + } + trb_list_ = glcr::ArrayView( + reinterpret_cast(page_.vaddr()), number_trbs); + + // Point the end of the command ring back to the start. + trb_list_[trb_list_.size() - 1] = CreateLinkTrb(phys_address_); +} + +XhciTrb TrbRing::GetTrbFromPhysical(uint64_t address) { + uint64_t offset = address - phys_address_; + if (offset >= 0x1000) { + crash("Invalid offset in GetTrbFromPhysical", glcr::INVALID_ARGUMENT); + } + offset /= sizeof(XhciTrb); + return trb_list_[offset]; +} + +uint64_t TrbRingWriter::EnqueueTrb(const XhciTrb& trb) { + uint64_t ptr = enqueue_ptr_++; + if (enqueue_ptr_ == trb_list_.size()) { + crash("Not implemented: queue wrapping", glcr::UNIMPLEMENTED); + } + + trb_list_[ptr] = trb; + return phys_address_ + (ptr * sizeof(XhciTrb)); +} + +bool TrbRingReader::HasNext() { + return (trb_list_[dequeue_ptr_].type_and_cycle & 0x1) == cycle_bit_; +} + +XhciTrb TrbRingReader::Read() { return trb_list_[dequeue_ptr_++]; } diff --git a/sys/voyageurs/xhci/trb_ring.h b/sys/voyageurs/xhci/trb_ring.h new file mode 100644 index 0000000..c2d0668 --- /dev/null +++ b/sys/voyageurs/xhci/trb_ring.h @@ -0,0 +1,39 @@ +#pragma once + +#include +#include + +#include "xhci/xhci.h" + +class TrbRing { + public: + TrbRing(); + + uint64_t PhysicalAddress() { return phys_address_; } + XhciTrb GetTrbFromPhysical(uint64_t address); + + protected: + uint64_t phys_address_; + mmth::OwnedMemoryRegion page_; + glcr::ArrayView trb_list_; +}; + +class TrbRingWriter : public TrbRing { + public: + uint64_t EnqueueTrb(const XhciTrb& trb); + + private: + uint64_t enqueue_ptr_ = 0; +}; + +class TrbRingReader : public TrbRing { + public: + bool HasNext(); + XhciTrb Read(); + + uint64_t DequeuePtr() { return phys_address_ + dequeue_ptr_; } + + private: + uint64_t dequeue_ptr_ = 0; + uint8_t cycle_bit_ = 1; +}; diff --git a/sys/voyageurs/xhci/xhci.h b/sys/voyageurs/xhci/xhci.h new file mode 100644 index 0000000..2304249 --- /dev/null +++ b/sys/voyageurs/xhci/xhci.h @@ -0,0 +1,143 @@ +#pragma once + +#include + +// TODO: Move to shared lib for denali and voyageurs. +struct PciDeviceHeader { + uint16_t vendor_id; + uint16_t device_id; + uint16_t command_reg; + uint16_t status_reg; + uint8_t revision; + uint8_t prog_interface; + uint8_t subclass; + uint8_t class_code; + uint8_t cache_line_size; + uint8_t latency_timer; + uint8_t header_type; + uint8_t bist; + uint32_t bars[5]; + uint32_t abar; + uint32_t reserved0; + uint32_t subsystem_id; + uint32_t expansion_rom; + uint8_t cap_ptr; + uint8_t reserved1[7]; + uint8_t interrupt_line; + uint8_t interrupt_pin; + uint8_t min_grant; + uint8_t max_latency; +} __attribute__((packed)); + +struct XhciCapabilities { + // NOTE: In qemu access these addresses at anything other than a 32bit offset + // seems to give bogus values so we group the fields more than necessary. + uint32_t length_and_version; + uint32_t hcs_params_1; + uint32_t hcs_params_2; + uint32_t hcs_params_3; + uint16_t capabilites; + uint16_t ext_capabilities_pointer; + uint32_t doorbell_offset; + uint32_t runtime_offset; + uint32_t capabilities2; +} __attribute__((packed)); + +struct XhciPort { + uint32_t status_and_control; + uint32_t power_management; + uint32_t link_info; + uint32_t lpm_control; +} __attribute__((packed)); + +struct XhciOperational { + uint32_t usb_command; + uint32_t usb_status; + uint32_t page_size; + uint32_t reserved; + uint32_t reserved2; + uint32_t device_notification_control; + uint64_t command_ring_control; + uint64_t reserved3; + uint64_t reserved4; + uint64_t device_context_base; + uint32_t configure; + XhciPort ports[255]; +} __attribute__((packed)); + +struct XhciInterrupter { + uint32_t management; + uint32_t moderation; + uint32_t event_ring_segment_table_size; + uint32_t reserved; + uint64_t event_ring_segment_table_base_address; + uint64_t event_ring_dequeue_pointer; +} __attribute__((packed)); + +struct XhciRuntime { + uint32_t microframe_index; + uint32_t reserved1; + uint64_t reserved2; + uint64_t reserved3; + uint64_t reserved4; + XhciInterrupter interrupters[1024]; +} __attribute__((packed)); + +struct XhciDoorbells { + uint32_t doorbell[256]; +} __attribute__((packed)); + +struct XhciSlotContext { + uint32_t route_speed_entries; + uint32_t latency_port_number; + uint32_t parent_and_interrupt; + uint32_t address_and_state; + uint64_t reserved1; + uint64_t reserved2; +} __attribute__((packed)); + +struct XhciEndpointContext { + uint32_t state; + uint32_t error_and_type; + uint64_t tr_dequeue_ptr; + uint32_t average_trb_length; + uint32_t reserved1; + uint64_t reserved2; +} __attribute__((packed)); + +struct XhciDeviceContext { + XhciSlotContext slot_context; + XhciEndpointContext endpoint_contexts[31]; +} __attribute__((packed)); + +struct XhciInputControlContext { + uint32_t drop_contexts; + uint32_t add_contexts; + uint64_t reserved1; + uint64_t reserved2; + uint32_t reserved3; + uint8_t configuration_value; + uint8_t interface_number; + uint8_t alternate_setting; + uint8_t reserved4; +} __attribute__((packed)); + +struct XhciInputContext { + XhciInputControlContext input; + XhciSlotContext slot_context; + XhciEndpointContext endpoint_contexts[31]; +} __attribute__((packed)); + +struct XhciTrb { + uint64_t parameter; + uint32_t status; + uint16_t type_and_cycle; + uint16_t control; +} __attribute__((packed)); + +struct XhciEventRingSegmentTableEntry { + uint64_t ring_segment_base; + uint16_t ring_segment_size; + uint16_t reserved1; + uint32_t reserved2; +} __attribute__((packed)); diff --git a/sys/voyageurs/xhci/xhci_driver.cpp b/sys/voyageurs/xhci/xhci_driver.cpp new file mode 100644 index 0000000..4eedce0 --- /dev/null +++ b/sys/voyageurs/xhci/xhci_driver.cpp @@ -0,0 +1,393 @@ +#include "xhci/xhci_driver.h" + +#include +#include +#include +#include + +#include "keyboard/keyboard_driver.h" +#include "xhci/descriptors.h" +#include "xhci/trb.h" +#include "xhci/xhci.h" + +void interrupt_thread(void* void_driver) { + XhciDriver* driver = static_cast(void_driver); + + driver->InterruptLoop(); + + crash("Driver returned from interrupt loop", glcr::INTERNAL); +} + +void configure_device(void* void_device_slot) { + DeviceSlot* device_slot = static_cast(void_device_slot); + + dbgln("Configuring device"); + + ReadControlCommand command; + + device_slot->ExecuteReadControlCommand(command); + + DeviceDescriptor* descriptor = command.AwaitResult(); + + dbgln("Descriptor Type {x}, ({x})", descriptor->type, descriptor->usb_spec); + dbgln("Device Class/Sub/Protocol: {x}/{x}/{x}", descriptor->device_class, + descriptor->device_subclass, descriptor->device_protocol); + dbgln("Num Configurations: {}", descriptor->num_configurations); + + ReadControlCommand config_command; + + device_slot->ExecuteReadControlCommand(config_command); + + ConfigurationDescriptor* config_descriptor = config_command.AwaitResult(); + + dbgln("Configuration Value: {x}", config_descriptor->configuration_value); + dbgln("Num Interfaces: {x}", config_descriptor->num_interfaces); + dbgln("Size: {x}, Total Length: {x}", config_descriptor->length, + config_descriptor->total_length); + + uint64_t next_vaddr = + reinterpret_cast(config_descriptor) + config_descriptor->length; + for (auto i = config_descriptor->num_interfaces; i > 0; i--) { + InterfaceDescriptor* interface = + reinterpret_cast(next_vaddr); + dbgln("Interface: {x}", interface->interface_number); + dbgln("Interface Class/Sub/Protocol: {x}/{x}/{x}", + interface->interface_class, interface->interface_subclass, + interface->interface_protocol); + dbgln("Num Endpoints: {x}", interface->num_endpoints); + + next_vaddr += interface->length; + for (auto j = interface->num_endpoints; j > 0; j--) { + EndpointDescriptor* endpoint = + reinterpret_cast(next_vaddr); + if (endpoint->type != 5) { + dbgln("Descriptor type {x}, skipping", endpoint->type); + j++; + next_vaddr += endpoint->length; + continue; + } + dbgln("Endpoint Addr: {x}", endpoint->endpoint_address); + dbgln("Endpoint Attr: {x}", endpoint->attributes); + next_vaddr += endpoint->length; + } + } + + dbgln("---- Configuring with configuration: {x}", + config_descriptor->configuration_value); + + device_slot + ->IssueConfigureDeviceCommand( + config_descriptor->configuration_value, + glcr::MakeUnique(KeyboardDriver::GetPortCap())) + .Wait(); + + dbgln("Configured!"); +} + +glcr::ErrorOr> XhciDriver::InitiateDriver( + yellowstone::YellowstoneClient& yellowstone) { + yellowstone::XhciInfo info; + check(yellowstone.GetXhciInfo(info)); + + mmth::OwnedMemoryRegion pci_region = + mmth::OwnedMemoryRegion::FromCapability(info.mutable_xhci_region()); + // Have to make this a heap object so that the reference passed to the + // interrupt loop remains valid. + glcr::UniquePtr driver(new XhciDriver(glcr::Move(pci_region))); + driver->ParseMmioStructures(); + driver->FreeExistingMemoryStructures(); + driver->ResetController(); + driver->StartInterruptThread(); + dbgln("XHCI CONTROLLER RESET"); + driver->NoOpCommand(); + driver->InitiateDevices(); + return glcr::Move(driver); +} + +void XhciDriver::InterruptLoop() { + while (true) { + while ((runtime_->interrupters[0].management & 0x1) != 0x1) { + check(ZThreadSleep(50)); + } + while (event_ring_.HasNext()) { + XhciTrb trb = event_ring_.Read(); + switch (GetType(trb)) { + case TrbType::Transfer: + HandleTransferCompletion(trb); + break; + case TrbType::CommandCompletion: + HandleCommandCompletion(trb); + break; + case TrbType::PortStatusChange: + dbgln("Port Status Change Event on Port {x}, enabling slot.", + ((trb.parameter >> 24) & 0xFF) - 1); + command_ring_.EnqueueTrb(CreateEnableSlotTrb()); + doorbells_->doorbell[0] = 0; + break; + + default: + dbgln("Unknown TRB Type {x} received.", (uint8_t)GetType(trb)); + break; + } + } + + runtime_->interrupters[0].event_ring_dequeue_pointer = + event_ring_.DequeuePtr() | 0x8; + runtime_->interrupters[0].management |= 0x1; + } +} + +void XhciDriver::DumpDebugInfo() { + dbgln("Code: {x} {x} {x}", pci_device_header_->class_code, + pci_device_header_->subclass, pci_device_header_->prog_interface); + + dbgln("BAR: {x}", pci_device_header_->bars[0] & ~0xFFF); + dbgln("In P: {x} L: {x}", pci_device_header_->interrupt_pin, + pci_device_header_->interrupt_line); + + dbgln("Cap length: {x}", capabilities_->length_and_version & 0xFF); + dbgln("XHCI Version: {x}", + (capabilities_->length_and_version & 0xFFFF0000) >> 16); + dbgln("Max Slots: {x}", capabilities_->hcs_params_1 & 0xFF); + dbgln("Max Interrupters: {x}", (capabilities_->hcs_params_1 & 0x3FF00) >> 8); + uint64_t max_ports = (capabilities_->hcs_params_1 & 0xFF00'0000) >> 24; + dbgln("Params 2: {x}", capabilities_->hcs_params_2); + dbgln("Params 3: {x}", capabilities_->hcs_params_3); + dbgln("Max Ports: {x}", max_ports); + dbgln("Capabilities: {x}", capabilities_->capabilites); + dbgln("Doorbell: {x}", capabilities_->doorbell_offset); + dbgln("Runtime: {x}", capabilities_->runtime_offset); + + dbgln("Op cmd: {x} sts: {x}", operational_->usb_command, + operational_->usb_status); + dbgln("Page size: {x}", operational_->page_size); + dbgln("Device Context Base Array: {x}", operational_->device_context_base); + dbgln("Command Ring Control: {x}", operational_->command_ring_control); + dbgln("Config: {x}", operational_->configure); + + for (uint64_t i = 0; i < max_ports; i++) { + XhciPort* port = reinterpret_cast( + reinterpret_cast(operational_) + 0x400 + (0x10 * i)); + port->status_and_control &= ~0x10000; + dbgln("Port {x}: {x}", i, port->status_and_control); + if ((port->status_and_control & 0x3) == 0x1) { + dbgln("Resetting: {x}", i); + port->status_and_control |= 0x10; + doorbells_->doorbell[0] = 0; + } + } + + dbgln("Int 0 ES: {x}", + runtime_->interrupters[0].event_ring_segment_table_base_address); +} + +void XhciDriver::IssueCommand(const XhciTrb& command) { + command_ring_.EnqueueTrb(command); + doorbells_->doorbell[0] = 0; +} + +XhciDriver::XhciDriver(mmth::OwnedMemoryRegion&& pci_space) + : pci_region_(glcr::Move(pci_space)) {} + +glcr::ErrorCode XhciDriver::ParseMmioStructures() { + pci_device_header_ = reinterpret_cast(pci_region_.vaddr()); + + // TODO: Officially determine the size of this memory. + mmio_regions_ = mmth::OwnedMemoryRegion::DirectPhysical( + pci_device_header_->bars[0] & ~0xFFF, 0x3000); + + capabilities_ = reinterpret_cast(mmio_regions_.vaddr()); + + uint8_t max_device_slots = capabilities_->hcs_params_1 & 0xFF; + devices_ = glcr::Array(max_device_slots); + + uint64_t op_base = + mmio_regions_.vaddr() + (capabilities_->length_and_version & 0xFF); + operational_ = reinterpret_cast(op_base); + + runtime_ = reinterpret_cast(mmio_regions_.vaddr() + + capabilities_->runtime_offset); + + dbgln("INTTTT: {x}", (uint64_t)runtime_->interrupters); + + doorbells_ = reinterpret_cast(mmio_regions_.vaddr() + + capabilities_->doorbell_offset); + dbgln("Doorbells: {x}", (uint64_t)doorbells_); + + return glcr::OK; +} + +glcr::ErrorCode XhciDriver::ResetController() { + // Stop the Host Controller. + // FIXME: Do this before freeing existing structures. + operational_->usb_command &= ~0x1; + + while ((operational_->usb_status & 0x1) != 0x1) { + dbgln("Waiting XHCI Halt."); + RET_ERR(ZThreadSleep(50)); + } + + // Host Controller Reset + operational_->usb_command |= 0x2; + + while (operational_->usb_command & 0x2) { + dbgln("Waiting Reset"); + RET_ERR(ZThreadSleep(50)); + } + + while (operational_->usb_command & (0x1 << 11)) { + dbgln("Waiting controller ready"); + RET_ERR(ZThreadSleep(50)); + } + + InitiateCommandRing(); + InitiateDeviceContextBaseArray(); + InitiateEventRingSegmentTable(); + + // Run the controller. + operational_->usb_command |= 0x1; + + return glcr::OK; +} + +void XhciDriver::StartInterruptThread() { + interrupt_thread_ = Thread(interrupt_thread, this); +} + +glcr::ErrorCode XhciDriver::InitiateCommandRing() { + operational_->command_ring_control = command_ring_.PhysicalAddress(); + return glcr::OK; +} + +glcr::ErrorCode XhciDriver::InitiateDeviceContextBaseArray() { + uint64_t dcba_phys; + device_context_base_array_mem_ = + mmth::OwnedMemoryRegion::ContiguousPhysical(0x1000, &dcba_phys); + device_context_base_array_ = + reinterpret_cast(device_context_base_array_mem_.vaddr()); + + operational_->device_context_base = dcba_phys; + + uint64_t max_slots = (0x1000 / sizeof(uint64_t)) - 1; + + if (max_slots > (capabilities_->hcs_params_1 & 0xFF)) { + max_slots = capabilities_->hcs_params_1 & 0xFF; + } + operational_->configure = + (operational_->configure & ~0xFF) | (max_slots & 0xFF); + + // TODO: Initialize scratchpad if that is needed by the controller. + + return glcr::OK; +} + +glcr::ErrorCode XhciDriver::InitiateEventRingSegmentTable() { + uint64_t erst_phys; + event_ring_segment_table_mem_ = + mmth::OwnedMemoryRegion::ContiguousPhysical(0x1000, &erst_phys); + + event_ring_segment_table_ = reinterpret_cast( + event_ring_segment_table_mem_.vaddr()); + + uint64_t ers_size = 0x1000 / sizeof(XhciTrb); + event_ring_segment_table_[0].ring_segment_base = + event_ring_.PhysicalAddress(); + event_ring_segment_table_[0].ring_segment_size = ers_size & 0xFFFF; + event_ring_segment_table_[0].reserved1 = 0; + event_ring_segment_table_[0].reserved2 = 0; + + runtime_->interrupters[0].event_ring_dequeue_pointer = + event_ring_.PhysicalAddress() | 0x8; + runtime_->interrupters[0].event_ring_segment_table_size = 1; + runtime_->interrupters[0].event_ring_segment_table_base_address = erst_phys; + + // Enable interrupts. + runtime_->interrupters[0].management |= 0x2; + runtime_->interrupters[0].moderation = 4000; + operational_->usb_command |= 0x4; + return glcr::OK; +} + +glcr::ErrorCode XhciDriver::InitiateDevices() { + uint64_t max_ports = (capabilities_->hcs_params_1 & 0xFF00'0000) >> 24; + for (uint64_t i = 0; i < max_ports; i++) { + XhciPort* port = reinterpret_cast( + reinterpret_cast(operational_) + 0x400 + (0x10 * i)); + port->status_and_control &= ~0x10000; + dbgln("Port {x}: {x}", i, port->status_and_control); + if ((port->status_and_control & 0x3) == 0x1) { + dbgln("Resetting: {x}", i); + port->status_and_control |= 0x10; + } + } + return glcr::OK; +} + +glcr::ErrorCode XhciDriver::NoOpCommand() { + command_ring_.EnqueueTrb(CreateNoOpCommandTrb()); + doorbells_->doorbell[0] = 0; + return glcr::OK; +} + +void XhciDriver::HandleCommandCompletion( + const XhciTrb& command_completion_trb) { + uint8_t status = command_completion_trb.status >> 24; + if (status != 1) { + dbgln("Command Completion Status: {x}", command_completion_trb.status); + check(glcr::INTERNAL); + } + + XhciTrb orig_trb = + command_ring_.GetTrbFromPhysical(command_completion_trb.parameter); + uint8_t slot = command_completion_trb.control >> 8; + switch (GetType(orig_trb)) { + case TrbType::EnableSlot: + dbgln("Slot Enabled: {x}", slot); + InitializeSlot(slot); + break; + case TrbType::AddressDevice: + dbgln("Device Addressed: {x}", slot); + dbgln("State: {x}", devices_[slot - 1].State()); + Thread(configure_device, &devices_[slot - 1]); + break; + case TrbType::ConfigureEndpoint: + dbgln("Device COnfigured: {x}", slot); + dbgln("State: {x}", devices_[slot - 1].State()); + devices_[slot - 1].SignalConfigureDeviceCompleted(); + break; + case TrbType::NoOpCommand: + dbgln("No-op Command Completed"); + break; + default: + dbgln("Unhandled Command Completion Type: {x}", + (uint8_t)(GetType(orig_trb))); + } +} + +void XhciDriver::HandleTransferCompletion(const XhciTrb& transfer_event_trb) { + uint8_t status = transfer_event_trb.status >> 24; + if (status != 1 && status != 0xD) { + dbgln("Unexpected Status {x}", status); + return; + } + uint8_t slot_id = transfer_event_trb.control >> 8; + uint8_t endpoint_id = transfer_event_trb.control & 0x1F; + uint64_t trb_phys = transfer_event_trb.parameter; + devices_[slot_id - 1].TransferComplete(endpoint_id, trb_phys); +} + +void XhciDriver::InitializeSlot(uint8_t slot_index) { + // TODO: Consider making this array one longer and ignore the first value. + devices_[slot_index - 1].EnableAndInitializeDataStructures( + this, slot_index, &(device_context_base_array_[slot_index]), + &doorbells_->doorbell[slot_index]); + XhciPort* port = + reinterpret_cast(reinterpret_cast(operational_) + + 0x400 + (0x10 * (slot_index - 1))); + uint8_t port_speed = (port->status_and_control >> 10) & 0xF; + uint8_t max_packet_size = 8; + XhciTrb address_device = devices_[slot_index - 1].CreateAddressDeviceCommand( + 0x5, 0, max_packet_size); + command_ring_.EnqueueTrb(address_device); + doorbells_->doorbell[0] = 0; +} diff --git a/sys/voyageurs/xhci/xhci_driver.h b/sys/voyageurs/xhci/xhci_driver.h new file mode 100644 index 0000000..2b70715 --- /dev/null +++ b/sys/voyageurs/xhci/xhci_driver.h @@ -0,0 +1,74 @@ +#pragma once + +#include +#include +#include +#include +#include +#include + +#include "xhci/device_slot.h" +#include "xhci/trb_ring.h" +#include "xhci/xhci.h" + +class XhciDriver { + public: + static glcr::ErrorOr> InitiateDriver( + yellowstone::YellowstoneClient& yellowstone); + + XhciDriver(const XhciDriver&) = delete; + XhciDriver(XhciDriver&&) = default; + + void DumpDebugInfo(); + + void InterruptLoop(); + + void IssueCommand(const XhciTrb& command); + + private: + // MMIO Structures. + mmth::OwnedMemoryRegion pci_region_; + PciDeviceHeader* pci_device_header_; + + mmth::OwnedMemoryRegion mmio_regions_; + volatile XhciCapabilities* capabilities_; + volatile XhciOperational* operational_; + // TODO: Extended Capabilities. + volatile XhciRuntime* runtime_; + volatile XhciDoorbells* doorbells_; + + // Host Memory Regions. + TrbRingWriter command_ring_; + + mmth::OwnedMemoryRegion device_context_base_array_mem_; + uint64_t* device_context_base_array_; + + mmth::OwnedMemoryRegion event_ring_segment_table_mem_; + XhciEventRingSegmentTableEntry* event_ring_segment_table_; + + TrbRingReader event_ring_; + Thread interrupt_thread_; + + glcr::Array devices_; + + XhciDriver(mmth::OwnedMemoryRegion&& pci_space); + + glcr::ErrorCode ParseMmioStructures(); + glcr::ErrorCode FreeExistingMemoryStructures() { return glcr::OK; } + + glcr::ErrorCode ResetController(); + void StartInterruptThread(); + + glcr::ErrorCode InitiateCommandRing(); + glcr::ErrorCode InitiateDeviceContextBaseArray(); + glcr::ErrorCode InitiateEventRingSegmentTable(); + + glcr::ErrorCode InitiateDevices(); + + glcr::ErrorCode NoOpCommand(); + + void HandleCommandCompletion(const XhciTrb& command_completion_trb); + void HandleTransferCompletion(const XhciTrb& transfer_event_trb); + + void InitializeSlot(uint8_t slot_index); +}; diff --git a/sys/yellowstone/CMakeLists.txt b/sys/yellowstone/CMakeLists.txt index 22f7444..12936d3 100644 --- a/sys/yellowstone/CMakeLists.txt +++ b/sys/yellowstone/CMakeLists.txt @@ -1,25 +1,2 @@ -add_executable(yellowstone - hw/gpt.cpp - hw/pcie.cpp - yellowstone.cpp - yellowstone_server.cpp - ) - -target_include_directories(yellowstone - PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}) - -target_link_libraries(yellowstone - denali_yunq - mammoth - glacier - victoriafalls_yunq - yellowstone_yunq - ) - -set_target_properties(yellowstone PROPERTIES - COMPILE_FLAGS "${CMAKE_CXX_FLAGS} ${BASE_COMPILE_FLAGS}" - LINK_FLAGS "${CMAKE_EXE_LINK_FLAGS} ${BASE_LINK_FLAGS}" - ) - yunq_gen(lib/yellowstone lib yellowstone) diff --git a/sys/yellowstone/hw/gpt.cpp b/sys/yellowstone/hw/gpt.cpp deleted file mode 100644 index b4eddc7..0000000 --- a/sys/yellowstone/hw/gpt.cpp +++ /dev/null @@ -1,133 +0,0 @@ -#include "hw/gpt.h" - -#include -#include -#include -#include -#include - -#define GPT_DEBUG 0 - -const uint64_t kSectorSize = 512; - -const uint64_t kGptPartitionSignature = 0x54524150'20494645; - -const uint64_t kLfsDataLow = 0x477284830fc63daf; -const uint64_t kLfsDataHigh = 0xe47d47d8693d798e; - -struct MbrPartition { - uint8_t boot_indicator; - uint8_t starting_chs[3]; - uint8_t os_type; - uint8_t ending_chs[3]; - uint32_t starting_lba; - uint32_t ending_lba; -} __attribute__((packed)); - -struct ParititionHeader { - uint64_t signature; - uint32_t revision; - uint32_t header_size; - uint32_t crc_32; - uint32_t reserved; - uint64_t lba_self; - uint64_t lba_mirror; - uint64_t lba_min; - uint64_t lba_max; - uint64_t guid_low; - uint64_t guid_high; - uint64_t lba_partition_entries; - uint32_t num_partitions; - uint32_t parition_entry_size; - uint32_t partition_entry_crc32; -} __attribute__((packed)); - -struct PartitionEntry { - uint64_t type_guid_low; - uint64_t type_guid_high; - uint64_t part_guid_low; - uint64_t part_guid_high; - uint64_t lba_start; - uint64_t lba_end; - uint64_t attributes; - char partition_name[72]; -} __attribute__((packed)); - -GptReader::GptReader(glcr::UniquePtr denali) - : denali_(glcr::Move(denali)) {} - -glcr::Status GptReader::ParsePartitionTables() { - ReadRequest req; - req.set_device_id(0); - req.set_lba(0); - req.set_size(2); - ReadResponse resp; - RETURN_ERROR(denali_->Read(req, resp)); - mmth::OwnedMemoryRegion lba_1_and_2 = - mmth::OwnedMemoryRegion::FromCapability(resp.memory()); - uint16_t* mbr_sig = reinterpret_cast(lba_1_and_2.vaddr() + 0x1FE); - if (*mbr_sig != 0xAA55) { - return glcr::FailedPrecondition( - glcr::StrFormat("Invalid MBR Sig: {x}", *mbr_sig)); - } - MbrPartition* first_partition = - reinterpret_cast(lba_1_and_2.vaddr() + 0x1BE); - if (first_partition->boot_indicator != 0) { - return glcr::FailedPrecondition(glcr::StrFormat( - "Boot indicator set: {}", first_partition->boot_indicator)); - } - if (first_partition->os_type != 0xEE) { - return glcr::FailedPrecondition( - glcr::StrFormat("Incorrect OS type: {x}", first_partition->os_type)); - } -#if GPT_DEBUG - dbgln("LBAs: ({x}, {x})", first_partition->starting_lba, - first_partition->ending_lba); -#endif - - // FIXME: Don't hardcode sector size. - ParititionHeader* header = - reinterpret_cast(lba_1_and_2.vaddr() + 512); - - uint64_t num_partitions = header->num_partitions; - uint64_t entry_size = header->parition_entry_size; - uint64_t num_blocks = (num_partitions * entry_size) / 512; - -#if GPT_DEBUG - dbgln("signature {}", header->signature); - dbgln("lba_partition_entries {x}", header->lba_partition_entries); - dbgln("num_partitions: {x}", num_partitions); - dbgln("partition_entry_size: {x}", entry_size); - dbgln("Num blocks: {x}", num_blocks); -#endif - - req.set_device_id(0); - req.set_lba(header->lba_partition_entries); - req.set_size(num_blocks); - RETURN_ERROR(denali_->Read(req, resp)); - mmth::OwnedMemoryRegion part_table = - mmth::OwnedMemoryRegion::FromCapability(resp.memory()); - for (uint64_t i = 0; i < num_partitions; i++) { - PartitionEntry* entry = reinterpret_cast( - part_table.vaddr() + (i * entry_size)); - if (entry->type_guid_low != 0 || entry->type_guid_high != 0) { -#if GPT_DEBUG - dbgln("Entry {}", i); - dbgln("T Guid: {x}-{x}", entry->type_guid_high, entry->type_guid_low); - dbgln("P Guid: {x}-{x}", entry->part_guid_high, entry->part_guid_low); - dbgln("LBA: {x}, {x}", entry->lba_start, entry->lba_end); - dbgln("Attrs: {x}", entry->attributes); -#endif - // For now we hardcode these values to the type that is - // created in our setup script. - // FIXME: Set up our own root partition type guid at some - // point. - if (entry->type_guid_low == kLfsDataLow && - entry->type_guid_high == kLfsDataHigh) { - primary_partition_lba_ = entry->lba_start; - } - } - } - - return glcr::Status::Ok(); -} diff --git a/sys/yellowstone/hw/gpt.h b/sys/yellowstone/hw/gpt.h deleted file mode 100644 index 4ce95d3..0000000 --- a/sys/yellowstone/hw/gpt.h +++ /dev/null @@ -1,20 +0,0 @@ -#pragma once - -#include -#include -#include -#include -#include - -class GptReader { - public: - GptReader(glcr::UniquePtr denali); - - glcr::Status ParsePartitionTables(); - - uint64_t GetPrimaryPartitionLba() { return primary_partition_lba_; } - - private: - glcr::UniquePtr denali_; - uint64_t primary_partition_lba_; -}; diff --git a/sys/yellowstone/hw/pcie.cpp b/sys/yellowstone/hw/pcie.cpp deleted file mode 100644 index 08a2e7b..0000000 --- a/sys/yellowstone/hw/pcie.cpp +++ /dev/null @@ -1,95 +0,0 @@ -#include "hw/pcie.h" - -#include -#include -#include - -#define PCI_DEBUG 0 - -namespace { - -PciDeviceHeader* PciHeader(uint64_t base, uint64_t bus, uint64_t dev, - uint64_t fun) { - return reinterpret_cast(base + (bus << 20) + (dev << 15) + - (fun << 12)); -} - -} // namespace - -PciReader::PciReader() { - uint64_t vaddr; - check(ZAddressSpaceMap(gSelfVmasCap, 0, gBootPciVmmoCap, 0, &vaddr)); - - PciDump(vaddr); - - header_ = PciHeader(vaddr, 0, 0, 0); -} - -z_cap_t PciReader::GetAhciVmmo() { - uint64_t new_cap; - check(ZMemoryObjectDuplicate(gBootPciVmmoCap, achi_device_offset_, - kPcieConfigurationSize, &new_cap)); - return new_cap; -} - -void PciReader::FunctionDump(uint64_t base, uint64_t bus, uint64_t dev, - uint64_t fun) { - PciDeviceHeader* hdr = PciHeader(base, bus, dev, fun); - if (hdr->vendor_id == 0xFFFF) { - return; - } -#if PCI_DEBUG - dbgln( - "[{}.{}.{}] (Vendor, Device): ({x}, {x}), (Type, Class, Sub, PIF): ({}, " - "{x}, {x}, {x})", - bus, dev, fun, hdr->vendor_id, hdr->device_id, hdr->header_type, - hdr->class_code, hdr->subclass, hdr->prog_interface); -#endif - - if ((hdr->class_code == 0x6) && (hdr->subclass == 0x4)) { - dbgln("FIXME: Handle PCI to PCI bridge."); - } - if (hdr->class_code == 0x1) { -#if PCI_DEBUG - dbgln("SATA Device at: {x}", reinterpret_cast(hdr) - base); -#endif - achi_device_offset_ = reinterpret_cast(hdr) - base; - } -} - -void PciReader::DeviceDump(uint64_t base, uint64_t bus, uint64_t dev) { - PciDeviceHeader* hdr = PciHeader(base, bus, dev, 0); - if (hdr->vendor_id == 0xFFFF) { - return; - } - - FunctionDump(base, bus, dev, 0); - - // Device is multifunction. - if (hdr->header_type & 0x80) { - for (uint64_t f = 1; f < 0x8; f++) { - FunctionDump(base, bus, dev, f); - } - } -} - -void PciReader::BusDump(uint64_t base, uint64_t bus) { - for (uint64_t dev = 0; dev < 0x20; dev++) { - DeviceDump(base, bus, dev); - } -} - -void PciReader::PciDump(uint64_t base) { - PciDeviceHeader* hdr = PciHeader(base, 0, 0, 0); - if ((hdr->header_type & 0x80) == 0) { - // Single bus system. - BusDump(base, 0); - } else { - for (uint64_t f = 0; f < 8; f++) { - PciDeviceHeader* f_hdr = PciHeader(base, 0, 0, f); - if (f_hdr->vendor_id != 0xFFFF) { - BusDump(base, f); - } - } - } -} diff --git a/sys/yellowstone/hw/pcie.h b/sys/yellowstone/hw/pcie.h deleted file mode 100644 index d9e5778..0000000 --- a/sys/yellowstone/hw/pcie.h +++ /dev/null @@ -1,41 +0,0 @@ -#pragma once - -#include -#include - -struct PciDeviceHeader { - uint16_t vendor_id; - uint16_t device_id; - uint16_t command_reg; - uint16_t status_reg; - uint8_t revision; - uint8_t prog_interface; - uint8_t subclass; - uint8_t class_code; - uint8_t cache_line_size; - uint8_t latency_timer; - uint8_t header_type; - uint8_t bist; -} __attribute__((packed)); - -// TODO: Figure out if it safe to hardcode this. -// For the memory mapped access to PCI, it may be true that -// each configuration item is always the size of a single page. -const uint64_t kPcieConfigurationSize = 0x1000; - -class PciReader { - public: - PciReader(); - - z_cap_t GetAhciVmmo(); - - private: - PciDeviceHeader* header_; - - uint64_t achi_device_offset_ = 0; - - void PciDump(uint64_t vaddr); - void BusDump(uint64_t base, uint64_t bus); - void DeviceDump(uint64_t base, uint64_t bus, uint64_t dev); - void FunctionDump(uint64_t base, uint64_t bus, uint64_t dev, uint64_t fun); -}; diff --git a/sys/yellowstone/lib/yellowstone/yellowstone.yunq b/sys/yellowstone/lib/yellowstone/yellowstone.yunq index 95b3dfb..3bf9d9d 100644 --- a/sys/yellowstone/lib/yellowstone/yellowstone.yunq +++ b/sys/yellowstone/lib/yellowstone/yellowstone.yunq @@ -4,6 +4,7 @@ interface Yellowstone { method RegisterEndpoint(RegisterEndpointRequest) -> (); method GetEndpoint(GetEndpointRequest) -> (Endpoint); method GetAhciInfo() -> (AhciInfo); + method GetXhciInfo() -> (XhciInfo); method GetFramebufferInfo() -> (FramebufferInfo); method GetDenali() -> (DenaliInfo); } @@ -26,6 +27,11 @@ message AhciInfo { u64 region_length; } +message XhciInfo { + capability xhci_region; + u64 region_length; +} + message FramebufferInfo { u64 address_phys; u64 width; diff --git a/sys/yellowstone/lib/yellowstone/yellowstone.yunq.client.cpp b/sys/yellowstone/lib/yellowstone/yellowstone.yunq.client.cpp index d7baf54..05f451c 100644 --- a/sys/yellowstone/lib/yellowstone/yellowstone.yunq.client.cpp +++ b/sys/yellowstone/lib/yellowstone/yellowstone.yunq.client.cpp @@ -87,7 +87,8 @@ glcr::Status YellowstoneClient::GetEndpoint(const GetEndpointRequest& request, E RET_ERR(buffer_.At(8)); - RETURN_ERROR(response.ParseFromBytes(buffer_, 16, cap_buffer_)); + yunq::MessageView resp_view(buffer_, 16); + RETURN_ERROR(response.ParseFromBytes(resp_view, cap_buffer_)); return glcr::OK; @@ -126,7 +127,8 @@ glcr::Status YellowstoneClient::GetAhciInfo(AhciInfo& response) { RET_ERR(buffer_.At(8)); - RETURN_ERROR(response.ParseFromBytes(buffer_, 16, cap_buffer_)); + yunq::MessageView resp_view(buffer_, 16); + RETURN_ERROR(response.ParseFromBytes(resp_view, cap_buffer_)); return glcr::OK; @@ -135,7 +137,7 @@ glcr::Status YellowstoneClient::GetAhciInfo(AhciInfo& response) { -glcr::Status YellowstoneClient::GetFramebufferInfo(FramebufferInfo& response) { +glcr::Status YellowstoneClient::GetXhciInfo(XhciInfo& response) { uint64_t buffer_size = kBufferSize; uint64_t cap_size = kCapBufferSize; @@ -165,7 +167,8 @@ glcr::Status YellowstoneClient::GetFramebufferInfo(FramebufferInfo& response) { RET_ERR(buffer_.At(8)); - RETURN_ERROR(response.ParseFromBytes(buffer_, 16, cap_buffer_)); + yunq::MessageView resp_view(buffer_, 16); + RETURN_ERROR(response.ParseFromBytes(resp_view, cap_buffer_)); return glcr::OK; @@ -174,7 +177,7 @@ glcr::Status YellowstoneClient::GetFramebufferInfo(FramebufferInfo& response) { -glcr::Status YellowstoneClient::GetDenali(DenaliInfo& response) { +glcr::Status YellowstoneClient::GetFramebufferInfo(FramebufferInfo& response) { uint64_t buffer_size = kBufferSize; uint64_t cap_size = kCapBufferSize; @@ -204,7 +207,48 @@ glcr::Status YellowstoneClient::GetDenali(DenaliInfo& response) { RET_ERR(buffer_.At(8)); - RETURN_ERROR(response.ParseFromBytes(buffer_, 16, cap_buffer_)); + yunq::MessageView resp_view(buffer_, 16); + RETURN_ERROR(response.ParseFromBytes(resp_view, cap_buffer_)); + + + return glcr::OK; +} + + + + +glcr::Status YellowstoneClient::GetDenali(DenaliInfo& response) { + + uint64_t buffer_size = kBufferSize; + uint64_t cap_size = kCapBufferSize; + + const uint32_t kSentinel = 0xBEEFDEAD; + buffer_.WriteAt(0, kSentinel); + buffer_.WriteAt(8, 5); + + cap_buffer_.Reset(); + + uint64_t length = 0; + + + buffer_.WriteAt(4, 16 + length); + + z_cap_t reply_port_cap; + RET_ERR(ZEndpointSend(endpoint_, 16 + length, buffer_.RawPtr(), cap_buffer_.UsedSlots(), cap_buffer_.RawPtr(), &reply_port_cap)); + + // FIXME: Add a way to zero out the first buffer. + RET_ERR(ZReplyPortRecv(reply_port_cap, &buffer_size, buffer_.RawPtr(), &cap_size, cap_buffer_.RawPtr())); + + if (buffer_.At(0) != kSentinel) { + return glcr::InvalidResponse("Got an invalid response from server."); + } + + // Check Response Code. + RET_ERR(buffer_.At(8)); + + + yunq::MessageView resp_view(buffer_, 16); + RETURN_ERROR(response.ParseFromBytes(resp_view, cap_buffer_)); return glcr::OK; diff --git a/sys/yellowstone/lib/yellowstone/yellowstone.yunq.client.h b/sys/yellowstone/lib/yellowstone/yellowstone.yunq.client.h index 1793f69..afe8619 100644 --- a/sys/yellowstone/lib/yellowstone/yellowstone.yunq.client.h +++ b/sys/yellowstone/lib/yellowstone/yellowstone.yunq.client.h @@ -34,6 +34,10 @@ class YellowstoneClient { + [[nodiscard]] glcr::Status GetXhciInfo(XhciInfo& response); + + + [[nodiscard]] glcr::Status GetFramebufferInfo(FramebufferInfo& response); diff --git a/sys/yellowstone/lib/yellowstone/yellowstone.yunq.cpp b/sys/yellowstone/lib/yellowstone/yellowstone.yunq.cpp index 6b78856..1134546 100644 --- a/sys/yellowstone/lib/yellowstone/yellowstone.yunq.cpp +++ b/sys/yellowstone/lib/yellowstone/yellowstone.yunq.cpp @@ -1,6 +1,7 @@ // Generated file -- DO NOT MODIFY. #include "yellowstone.yunq.h" +#include #include @@ -16,423 +17,336 @@ struct ExtPointer { }; } // namespace -glcr::Status RegisterEndpointRequest::ParseFromBytes(const glcr::ByteBuffer& bytes, uint64_t offset) { - RETURN_ERROR(ParseFromBytesInternal(bytes, offset)); +glcr::Status RegisterEndpointRequest::ParseFromBytes(const yunq::MessageView& message) { + RETURN_ERROR(ParseFromBytesInternal(message)); // Parse endpoint_capability. - // FIXME: Implement in-buffer capabilities for inprocess serialization. - set_endpoint_capability(0); + ASSIGN_OR_RETURN(endpoint_capability_, message.ReadCapability(1)); return glcr::Status::Ok(); } -glcr::Status RegisterEndpointRequest::ParseFromBytes(const glcr::ByteBuffer& bytes, uint64_t offset, const glcr::CapBuffer& caps) { - RETURN_ERROR(ParseFromBytesInternal(bytes, offset)); +glcr::Status RegisterEndpointRequest::ParseFromBytes(const yunq::MessageView& message, const glcr::CapBuffer& caps) { + RETURN_ERROR(ParseFromBytesInternal(message)); // Parse endpoint_capability. - uint64_t endpoint_capability_ptr = bytes.At(offset + header_size + (8 * 1)); - - set_endpoint_capability(caps.At(endpoint_capability_ptr)); + ASSIGN_OR_RETURN(endpoint_capability_, message.ReadCapability(1, caps)); return glcr::Status::Ok(); } -glcr::Status RegisterEndpointRequest::ParseFromBytesInternal(const glcr::ByteBuffer& bytes, uint64_t offset) { - RETURN_ERROR(yunq::CheckHeader(bytes, offset)); +glcr::Status RegisterEndpointRequest::ParseFromBytesInternal(const yunq::MessageView& message) { + RETURN_ERROR(message.CheckHeader()); // Parse endpoint_name. - auto endpoint_name_pointer = bytes.At(offset + header_size + (8 * 0)); - - set_endpoint_name(bytes.StringAt(offset + endpoint_name_pointer.offset, endpoint_name_pointer.length)); + ASSIGN_OR_RETURN(endpoint_name_, message.ReadField(0)); // Parse endpoint_capability. - // Skip Cap. return glcr::Status::Ok(); } uint64_t RegisterEndpointRequest::SerializeToBytes(glcr::ByteBuffer& bytes, uint64_t offset) const { - uint32_t next_extension = header_size + 8 * 2; - const uint32_t core_size = next_extension; - // Write endpoint_name. - ExtPointer endpoint_name_ptr{ - .offset = next_extension, - // FIXME: Check downcast of str length. - .length = (uint32_t)endpoint_name().length(), - }; - - bytes.WriteStringAt(offset + next_extension, endpoint_name()); - next_extension += endpoint_name_ptr.length; - - bytes.WriteAt(offset + header_size + (8 * 0), endpoint_name_ptr); - // Write endpoint_capability. - // FIXME: Implement inbuffer capabilities. - bytes.WriteAt(offset + header_size + (8 * 1), 0); - - // The next extension pointer is the length of the message. - yunq::WriteHeader(bytes, offset, core_size, next_extension); - - return next_extension; + yunq::Serializer serializer(bytes, offset, 2); + return SerializeInternal(serializer); } uint64_t RegisterEndpointRequest::SerializeToBytes(glcr::ByteBuffer& bytes, uint64_t offset, glcr::CapBuffer& caps) const { - uint32_t next_extension = header_size + 8 * 2; - const uint32_t core_size = next_extension; - uint64_t next_cap = 0; + yunq::Serializer serializer(bytes, offset, 2, caps); + return SerializeInternal(serializer); +} + +uint64_t RegisterEndpointRequest::SerializeInternal(yunq::Serializer& serializer) const { // Write endpoint_name. - ExtPointer endpoint_name_ptr{ - .offset = next_extension, - // FIXME: Check downcast of str length. - .length = (uint32_t)endpoint_name().length(), - }; - - bytes.WriteStringAt(offset + next_extension, endpoint_name()); - next_extension += endpoint_name_ptr.length; - - bytes.WriteAt(offset + header_size + (8 * 0), endpoint_name_ptr); + serializer.WriteField(0, endpoint_name_); // Write endpoint_capability. - caps.WriteAt(next_cap, endpoint_capability()); - bytes.WriteAt(offset + header_size + (8 * 1), next_cap++); + serializer.WriteCapability(1, endpoint_capability_); - // The next extension pointer is the length of the message. - yunq::WriteHeader(bytes, offset, core_size, next_extension); + serializer.WriteHeader(); - return next_extension; + return serializer.size(); } -glcr::Status GetEndpointRequest::ParseFromBytes(const glcr::ByteBuffer& bytes, uint64_t offset) { - RETURN_ERROR(ParseFromBytesInternal(bytes, offset)); +glcr::Status GetEndpointRequest::ParseFromBytes(const yunq::MessageView& message) { + RETURN_ERROR(ParseFromBytesInternal(message)); return glcr::Status::Ok(); } -glcr::Status GetEndpointRequest::ParseFromBytes(const glcr::ByteBuffer& bytes, uint64_t offset, const glcr::CapBuffer& caps) { - RETURN_ERROR(ParseFromBytesInternal(bytes, offset)); +glcr::Status GetEndpointRequest::ParseFromBytes(const yunq::MessageView& message, const glcr::CapBuffer& caps) { + RETURN_ERROR(ParseFromBytesInternal(message)); return glcr::Status::Ok(); } -glcr::Status GetEndpointRequest::ParseFromBytesInternal(const glcr::ByteBuffer& bytes, uint64_t offset) { - RETURN_ERROR(yunq::CheckHeader(bytes, offset)); +glcr::Status GetEndpointRequest::ParseFromBytesInternal(const yunq::MessageView& message) { + RETURN_ERROR(message.CheckHeader()); // Parse endpoint_name. - auto endpoint_name_pointer = bytes.At(offset + header_size + (8 * 0)); - - set_endpoint_name(bytes.StringAt(offset + endpoint_name_pointer.offset, endpoint_name_pointer.length)); + ASSIGN_OR_RETURN(endpoint_name_, message.ReadField(0)); return glcr::Status::Ok(); } uint64_t GetEndpointRequest::SerializeToBytes(glcr::ByteBuffer& bytes, uint64_t offset) const { - uint32_t next_extension = header_size + 8 * 1; - const uint32_t core_size = next_extension; - // Write endpoint_name. - ExtPointer endpoint_name_ptr{ - .offset = next_extension, - // FIXME: Check downcast of str length. - .length = (uint32_t)endpoint_name().length(), - }; - - bytes.WriteStringAt(offset + next_extension, endpoint_name()); - next_extension += endpoint_name_ptr.length; - - bytes.WriteAt(offset + header_size + (8 * 0), endpoint_name_ptr); - - // The next extension pointer is the length of the message. - yunq::WriteHeader(bytes, offset, core_size, next_extension); - - return next_extension; + yunq::Serializer serializer(bytes, offset, 1); + return SerializeInternal(serializer); } uint64_t GetEndpointRequest::SerializeToBytes(glcr::ByteBuffer& bytes, uint64_t offset, glcr::CapBuffer& caps) const { - uint32_t next_extension = header_size + 8 * 1; - const uint32_t core_size = next_extension; - uint64_t next_cap = 0; + yunq::Serializer serializer(bytes, offset, 1, caps); + return SerializeInternal(serializer); +} + +uint64_t GetEndpointRequest::SerializeInternal(yunq::Serializer& serializer) const { // Write endpoint_name. - ExtPointer endpoint_name_ptr{ - .offset = next_extension, - // FIXME: Check downcast of str length. - .length = (uint32_t)endpoint_name().length(), - }; + serializer.WriteField(0, endpoint_name_); - bytes.WriteStringAt(offset + next_extension, endpoint_name()); - next_extension += endpoint_name_ptr.length; + serializer.WriteHeader(); - bytes.WriteAt(offset + header_size + (8 * 0), endpoint_name_ptr); - - // The next extension pointer is the length of the message. - yunq::WriteHeader(bytes, offset, core_size, next_extension); - - return next_extension; + return serializer.size(); } -glcr::Status Endpoint::ParseFromBytes(const glcr::ByteBuffer& bytes, uint64_t offset) { - RETURN_ERROR(ParseFromBytesInternal(bytes, offset)); +glcr::Status Endpoint::ParseFromBytes(const yunq::MessageView& message) { + RETURN_ERROR(ParseFromBytesInternal(message)); // Parse endpoint. - // FIXME: Implement in-buffer capabilities for inprocess serialization. - set_endpoint(0); + ASSIGN_OR_RETURN(endpoint_, message.ReadCapability(0)); return glcr::Status::Ok(); } -glcr::Status Endpoint::ParseFromBytes(const glcr::ByteBuffer& bytes, uint64_t offset, const glcr::CapBuffer& caps) { - RETURN_ERROR(ParseFromBytesInternal(bytes, offset)); +glcr::Status Endpoint::ParseFromBytes(const yunq::MessageView& message, const glcr::CapBuffer& caps) { + RETURN_ERROR(ParseFromBytesInternal(message)); // Parse endpoint. - uint64_t endpoint_ptr = bytes.At(offset + header_size + (8 * 0)); - - set_endpoint(caps.At(endpoint_ptr)); + ASSIGN_OR_RETURN(endpoint_, message.ReadCapability(0, caps)); return glcr::Status::Ok(); } -glcr::Status Endpoint::ParseFromBytesInternal(const glcr::ByteBuffer& bytes, uint64_t offset) { - RETURN_ERROR(yunq::CheckHeader(bytes, offset)); +glcr::Status Endpoint::ParseFromBytesInternal(const yunq::MessageView& message) { + RETURN_ERROR(message.CheckHeader()); // Parse endpoint. - // Skip Cap. return glcr::Status::Ok(); } uint64_t Endpoint::SerializeToBytes(glcr::ByteBuffer& bytes, uint64_t offset) const { - uint32_t next_extension = header_size + 8 * 1; - const uint32_t core_size = next_extension; - // Write endpoint. - // FIXME: Implement inbuffer capabilities. - bytes.WriteAt(offset + header_size + (8 * 0), 0); - - // The next extension pointer is the length of the message. - yunq::WriteHeader(bytes, offset, core_size, next_extension); - - return next_extension; + yunq::Serializer serializer(bytes, offset, 1); + return SerializeInternal(serializer); } uint64_t Endpoint::SerializeToBytes(glcr::ByteBuffer& bytes, uint64_t offset, glcr::CapBuffer& caps) const { - uint32_t next_extension = header_size + 8 * 1; - const uint32_t core_size = next_extension; - uint64_t next_cap = 0; + yunq::Serializer serializer(bytes, offset, 1, caps); + return SerializeInternal(serializer); +} + +uint64_t Endpoint::SerializeInternal(yunq::Serializer& serializer) const { // Write endpoint. - caps.WriteAt(next_cap, endpoint()); - bytes.WriteAt(offset + header_size + (8 * 0), next_cap++); + serializer.WriteCapability(0, endpoint_); - // The next extension pointer is the length of the message. - yunq::WriteHeader(bytes, offset, core_size, next_extension); + serializer.WriteHeader(); - return next_extension; + return serializer.size(); } -glcr::Status AhciInfo::ParseFromBytes(const glcr::ByteBuffer& bytes, uint64_t offset) { - RETURN_ERROR(ParseFromBytesInternal(bytes, offset)); +glcr::Status AhciInfo::ParseFromBytes(const yunq::MessageView& message) { + RETURN_ERROR(ParseFromBytesInternal(message)); // Parse ahci_region. - // FIXME: Implement in-buffer capabilities for inprocess serialization. - set_ahci_region(0); + ASSIGN_OR_RETURN(ahci_region_, message.ReadCapability(0)); return glcr::Status::Ok(); } -glcr::Status AhciInfo::ParseFromBytes(const glcr::ByteBuffer& bytes, uint64_t offset, const glcr::CapBuffer& caps) { - RETURN_ERROR(ParseFromBytesInternal(bytes, offset)); +glcr::Status AhciInfo::ParseFromBytes(const yunq::MessageView& message, const glcr::CapBuffer& caps) { + RETURN_ERROR(ParseFromBytesInternal(message)); // Parse ahci_region. - uint64_t ahci_region_ptr = bytes.At(offset + header_size + (8 * 0)); - - set_ahci_region(caps.At(ahci_region_ptr)); + ASSIGN_OR_RETURN(ahci_region_, message.ReadCapability(0, caps)); return glcr::Status::Ok(); } -glcr::Status AhciInfo::ParseFromBytesInternal(const glcr::ByteBuffer& bytes, uint64_t offset) { - RETURN_ERROR(yunq::CheckHeader(bytes, offset)); +glcr::Status AhciInfo::ParseFromBytesInternal(const yunq::MessageView& message) { + RETURN_ERROR(message.CheckHeader()); // Parse ahci_region. - // Skip Cap. // Parse region_length. - set_region_length(bytes.At(offset + header_size + (8 * 1))); + ASSIGN_OR_RETURN(region_length_, message.ReadField(1)); return glcr::Status::Ok(); } uint64_t AhciInfo::SerializeToBytes(glcr::ByteBuffer& bytes, uint64_t offset) const { - uint32_t next_extension = header_size + 8 * 2; - const uint32_t core_size = next_extension; - // Write ahci_region. - // FIXME: Implement inbuffer capabilities. - bytes.WriteAt(offset + header_size + (8 * 0), 0); - // Write region_length. - bytes.WriteAt(offset + header_size + (8 * 1), region_length()); - - // The next extension pointer is the length of the message. - yunq::WriteHeader(bytes, offset, core_size, next_extension); - - return next_extension; + yunq::Serializer serializer(bytes, offset, 2); + return SerializeInternal(serializer); } uint64_t AhciInfo::SerializeToBytes(glcr::ByteBuffer& bytes, uint64_t offset, glcr::CapBuffer& caps) const { - uint32_t next_extension = header_size + 8 * 2; - const uint32_t core_size = next_extension; - uint64_t next_cap = 0; + yunq::Serializer serializer(bytes, offset, 2, caps); + return SerializeInternal(serializer); +} + +uint64_t AhciInfo::SerializeInternal(yunq::Serializer& serializer) const { // Write ahci_region. - caps.WriteAt(next_cap, ahci_region()); - bytes.WriteAt(offset + header_size + (8 * 0), next_cap++); + serializer.WriteCapability(0, ahci_region_); // Write region_length. - bytes.WriteAt(offset + header_size + (8 * 1), region_length()); + serializer.WriteField(1, region_length_); - // The next extension pointer is the length of the message. - yunq::WriteHeader(bytes, offset, core_size, next_extension); + serializer.WriteHeader(); - return next_extension; + return serializer.size(); } -glcr::Status FramebufferInfo::ParseFromBytes(const glcr::ByteBuffer& bytes, uint64_t offset) { - RETURN_ERROR(ParseFromBytesInternal(bytes, offset)); +glcr::Status XhciInfo::ParseFromBytes(const yunq::MessageView& message) { + RETURN_ERROR(ParseFromBytesInternal(message)); + // Parse xhci_region. + ASSIGN_OR_RETURN(xhci_region_, message.ReadCapability(0)); return glcr::Status::Ok(); } -glcr::Status FramebufferInfo::ParseFromBytes(const glcr::ByteBuffer& bytes, uint64_t offset, const glcr::CapBuffer& caps) { - RETURN_ERROR(ParseFromBytesInternal(bytes, offset)); +glcr::Status XhciInfo::ParseFromBytes(const yunq::MessageView& message, const glcr::CapBuffer& caps) { + RETURN_ERROR(ParseFromBytesInternal(message)); + // Parse xhci_region. + ASSIGN_OR_RETURN(xhci_region_, message.ReadCapability(0, caps)); return glcr::Status::Ok(); } -glcr::Status FramebufferInfo::ParseFromBytesInternal(const glcr::ByteBuffer& bytes, uint64_t offset) { - RETURN_ERROR(yunq::CheckHeader(bytes, offset)); +glcr::Status XhciInfo::ParseFromBytesInternal(const yunq::MessageView& message) { + RETURN_ERROR(message.CheckHeader()); + // Parse xhci_region. + // Parse region_length. + ASSIGN_OR_RETURN(region_length_, message.ReadField(1)); + + return glcr::Status::Ok(); +} + +uint64_t XhciInfo::SerializeToBytes(glcr::ByteBuffer& bytes, uint64_t offset) const { + yunq::Serializer serializer(bytes, offset, 2); + return SerializeInternal(serializer); +} + +uint64_t XhciInfo::SerializeToBytes(glcr::ByteBuffer& bytes, uint64_t offset, glcr::CapBuffer& caps) const { + yunq::Serializer serializer(bytes, offset, 2, caps); + return SerializeInternal(serializer); +} + +uint64_t XhciInfo::SerializeInternal(yunq::Serializer& serializer) const { + // Write xhci_region. + serializer.WriteCapability(0, xhci_region_); + // Write region_length. + serializer.WriteField(1, region_length_); + + serializer.WriteHeader(); + + return serializer.size(); +} +glcr::Status FramebufferInfo::ParseFromBytes(const yunq::MessageView& message) { + RETURN_ERROR(ParseFromBytesInternal(message)); + return glcr::Status::Ok(); +} + +glcr::Status FramebufferInfo::ParseFromBytes(const yunq::MessageView& message, const glcr::CapBuffer& caps) { + RETURN_ERROR(ParseFromBytesInternal(message)); + return glcr::Status::Ok(); +} + +glcr::Status FramebufferInfo::ParseFromBytesInternal(const yunq::MessageView& message) { + RETURN_ERROR(message.CheckHeader()); // Parse address_phys. - set_address_phys(bytes.At(offset + header_size + (8 * 0))); + ASSIGN_OR_RETURN(address_phys_, message.ReadField(0)); // Parse width. - set_width(bytes.At(offset + header_size + (8 * 1))); + ASSIGN_OR_RETURN(width_, message.ReadField(1)); // Parse height. - set_height(bytes.At(offset + header_size + (8 * 2))); + ASSIGN_OR_RETURN(height_, message.ReadField(2)); // Parse pitch. - set_pitch(bytes.At(offset + header_size + (8 * 3))); + ASSIGN_OR_RETURN(pitch_, message.ReadField(3)); // Parse bpp. - set_bpp(bytes.At(offset + header_size + (8 * 4))); + ASSIGN_OR_RETURN(bpp_, message.ReadField(4)); // Parse memory_model. - set_memory_model(bytes.At(offset + header_size + (8 * 5))); + ASSIGN_OR_RETURN(memory_model_, message.ReadField(5)); // Parse red_mask_size. - set_red_mask_size(bytes.At(offset + header_size + (8 * 6))); + ASSIGN_OR_RETURN(red_mask_size_, message.ReadField(6)); // Parse red_mask_shift. - set_red_mask_shift(bytes.At(offset + header_size + (8 * 7))); + ASSIGN_OR_RETURN(red_mask_shift_, message.ReadField(7)); // Parse green_mask_size. - set_green_mask_size(bytes.At(offset + header_size + (8 * 8))); + ASSIGN_OR_RETURN(green_mask_size_, message.ReadField(8)); // Parse green_mask_shift. - set_green_mask_shift(bytes.At(offset + header_size + (8 * 9))); + ASSIGN_OR_RETURN(green_mask_shift_, message.ReadField(9)); // Parse blue_mask_size. - set_blue_mask_size(bytes.At(offset + header_size + (8 * 10))); + ASSIGN_OR_RETURN(blue_mask_size_, message.ReadField(10)); // Parse blue_mask_shift. - set_blue_mask_shift(bytes.At(offset + header_size + (8 * 11))); + ASSIGN_OR_RETURN(blue_mask_shift_, message.ReadField(11)); return glcr::Status::Ok(); } uint64_t FramebufferInfo::SerializeToBytes(glcr::ByteBuffer& bytes, uint64_t offset) const { - uint32_t next_extension = header_size + 8 * 12; - const uint32_t core_size = next_extension; - // Write address_phys. - bytes.WriteAt(offset + header_size + (8 * 0), address_phys()); - // Write width. - bytes.WriteAt(offset + header_size + (8 * 1), width()); - // Write height. - bytes.WriteAt(offset + header_size + (8 * 2), height()); - // Write pitch. - bytes.WriteAt(offset + header_size + (8 * 3), pitch()); - // Write bpp. - bytes.WriteAt(offset + header_size + (8 * 4), bpp()); - // Write memory_model. - bytes.WriteAt(offset + header_size + (8 * 5), memory_model()); - // Write red_mask_size. - bytes.WriteAt(offset + header_size + (8 * 6), red_mask_size()); - // Write red_mask_shift. - bytes.WriteAt(offset + header_size + (8 * 7), red_mask_shift()); - // Write green_mask_size. - bytes.WriteAt(offset + header_size + (8 * 8), green_mask_size()); - // Write green_mask_shift. - bytes.WriteAt(offset + header_size + (8 * 9), green_mask_shift()); - // Write blue_mask_size. - bytes.WriteAt(offset + header_size + (8 * 10), blue_mask_size()); - // Write blue_mask_shift. - bytes.WriteAt(offset + header_size + (8 * 11), blue_mask_shift()); - - // The next extension pointer is the length of the message. - yunq::WriteHeader(bytes, offset, core_size, next_extension); - - return next_extension; + yunq::Serializer serializer(bytes, offset, 12); + return SerializeInternal(serializer); } uint64_t FramebufferInfo::SerializeToBytes(glcr::ByteBuffer& bytes, uint64_t offset, glcr::CapBuffer& caps) const { - uint32_t next_extension = header_size + 8 * 12; - const uint32_t core_size = next_extension; - uint64_t next_cap = 0; + yunq::Serializer serializer(bytes, offset, 12, caps); + return SerializeInternal(serializer); +} + +uint64_t FramebufferInfo::SerializeInternal(yunq::Serializer& serializer) const { // Write address_phys. - bytes.WriteAt(offset + header_size + (8 * 0), address_phys()); + serializer.WriteField(0, address_phys_); // Write width. - bytes.WriteAt(offset + header_size + (8 * 1), width()); + serializer.WriteField(1, width_); // Write height. - bytes.WriteAt(offset + header_size + (8 * 2), height()); + serializer.WriteField(2, height_); // Write pitch. - bytes.WriteAt(offset + header_size + (8 * 3), pitch()); + serializer.WriteField(3, pitch_); // Write bpp. - bytes.WriteAt(offset + header_size + (8 * 4), bpp()); + serializer.WriteField(4, bpp_); // Write memory_model. - bytes.WriteAt(offset + header_size + (8 * 5), memory_model()); + serializer.WriteField(5, memory_model_); // Write red_mask_size. - bytes.WriteAt(offset + header_size + (8 * 6), red_mask_size()); + serializer.WriteField(6, red_mask_size_); // Write red_mask_shift. - bytes.WriteAt(offset + header_size + (8 * 7), red_mask_shift()); + serializer.WriteField(7, red_mask_shift_); // Write green_mask_size. - bytes.WriteAt(offset + header_size + (8 * 8), green_mask_size()); + serializer.WriteField(8, green_mask_size_); // Write green_mask_shift. - bytes.WriteAt(offset + header_size + (8 * 9), green_mask_shift()); + serializer.WriteField(9, green_mask_shift_); // Write blue_mask_size. - bytes.WriteAt(offset + header_size + (8 * 10), blue_mask_size()); + serializer.WriteField(10, blue_mask_size_); // Write blue_mask_shift. - bytes.WriteAt(offset + header_size + (8 * 11), blue_mask_shift()); + serializer.WriteField(11, blue_mask_shift_); - // The next extension pointer is the length of the message. - yunq::WriteHeader(bytes, offset, core_size, next_extension); + serializer.WriteHeader(); - return next_extension; + return serializer.size(); } -glcr::Status DenaliInfo::ParseFromBytes(const glcr::ByteBuffer& bytes, uint64_t offset) { - RETURN_ERROR(ParseFromBytesInternal(bytes, offset)); +glcr::Status DenaliInfo::ParseFromBytes(const yunq::MessageView& message) { + RETURN_ERROR(ParseFromBytesInternal(message)); // Parse denali_endpoint. - // FIXME: Implement in-buffer capabilities for inprocess serialization. - set_denali_endpoint(0); + ASSIGN_OR_RETURN(denali_endpoint_, message.ReadCapability(0)); return glcr::Status::Ok(); } -glcr::Status DenaliInfo::ParseFromBytes(const glcr::ByteBuffer& bytes, uint64_t offset, const glcr::CapBuffer& caps) { - RETURN_ERROR(ParseFromBytesInternal(bytes, offset)); +glcr::Status DenaliInfo::ParseFromBytes(const yunq::MessageView& message, const glcr::CapBuffer& caps) { + RETURN_ERROR(ParseFromBytesInternal(message)); // Parse denali_endpoint. - uint64_t denali_endpoint_ptr = bytes.At(offset + header_size + (8 * 0)); - - set_denali_endpoint(caps.At(denali_endpoint_ptr)); + ASSIGN_OR_RETURN(denali_endpoint_, message.ReadCapability(0, caps)); return glcr::Status::Ok(); } -glcr::Status DenaliInfo::ParseFromBytesInternal(const glcr::ByteBuffer& bytes, uint64_t offset) { - RETURN_ERROR(yunq::CheckHeader(bytes, offset)); +glcr::Status DenaliInfo::ParseFromBytesInternal(const yunq::MessageView& message) { + RETURN_ERROR(message.CheckHeader()); // Parse denali_endpoint. - // Skip Cap. // Parse device_id. - set_device_id(bytes.At(offset + header_size + (8 * 1))); + ASSIGN_OR_RETURN(device_id_, message.ReadField(1)); // Parse lba_offset. - set_lba_offset(bytes.At(offset + header_size + (8 * 2))); + ASSIGN_OR_RETURN(lba_offset_, message.ReadField(2)); return glcr::Status::Ok(); } uint64_t DenaliInfo::SerializeToBytes(glcr::ByteBuffer& bytes, uint64_t offset) const { - uint32_t next_extension = header_size + 8 * 3; - const uint32_t core_size = next_extension; - // Write denali_endpoint. - // FIXME: Implement inbuffer capabilities. - bytes.WriteAt(offset + header_size + (8 * 0), 0); - // Write device_id. - bytes.WriteAt(offset + header_size + (8 * 1), device_id()); - // Write lba_offset. - bytes.WriteAt(offset + header_size + (8 * 2), lba_offset()); - - // The next extension pointer is the length of the message. - yunq::WriteHeader(bytes, offset, core_size, next_extension); - - return next_extension; + yunq::Serializer serializer(bytes, offset, 3); + return SerializeInternal(serializer); } uint64_t DenaliInfo::SerializeToBytes(glcr::ByteBuffer& bytes, uint64_t offset, glcr::CapBuffer& caps) const { - uint32_t next_extension = header_size + 8 * 3; - const uint32_t core_size = next_extension; - uint64_t next_cap = 0; + yunq::Serializer serializer(bytes, offset, 3, caps); + return SerializeInternal(serializer); +} + +uint64_t DenaliInfo::SerializeInternal(yunq::Serializer& serializer) const { // Write denali_endpoint. - caps.WriteAt(next_cap, denali_endpoint()); - bytes.WriteAt(offset + header_size + (8 * 0), next_cap++); + serializer.WriteCapability(0, denali_endpoint_); // Write device_id. - bytes.WriteAt(offset + header_size + (8 * 1), device_id()); + serializer.WriteField(1, device_id_); // Write lba_offset. - bytes.WriteAt(offset + header_size + (8 * 2), lba_offset()); + serializer.WriteField(2, lba_offset_); - // The next extension pointer is the length of the message. - yunq::WriteHeader(bytes, offset, core_size, next_extension); + serializer.WriteHeader(); - return next_extension; + return serializer.size(); } diff --git a/sys/yellowstone/lib/yellowstone/yellowstone.yunq.h b/sys/yellowstone/lib/yellowstone/yellowstone.yunq.h index 3fb2f88..8130d35 100644 --- a/sys/yellowstone/lib/yellowstone/yellowstone.yunq.h +++ b/sys/yellowstone/lib/yellowstone/yellowstone.yunq.h @@ -6,6 +6,8 @@ #include #include #include +#include +#include #include @@ -16,15 +18,20 @@ class RegisterEndpointRequest { RegisterEndpointRequest() {} // Delete copy and move until implemented. RegisterEndpointRequest(const RegisterEndpointRequest&) = delete; - RegisterEndpointRequest(RegisterEndpointRequest&&) = delete; + RegisterEndpointRequest(RegisterEndpointRequest&&) = default; + RegisterEndpointRequest& operator=(RegisterEndpointRequest&&) = default; - [[nodiscard]] glcr::Status ParseFromBytes(const glcr::ByteBuffer&, uint64_t offset); - [[nodiscard]] glcr::Status ParseFromBytes(const glcr::ByteBuffer&, uint64_t offset, const glcr::CapBuffer&); + [[nodiscard]] glcr::Status ParseFromBytes(const yunq::MessageView& message); + [[nodiscard]] glcr::Status ParseFromBytes(const yunq::MessageView& message, const glcr::CapBuffer&); uint64_t SerializeToBytes(glcr::ByteBuffer&, uint64_t offset) const; uint64_t SerializeToBytes(glcr::ByteBuffer&, uint64_t offset, glcr::CapBuffer&) const; + const glcr::String& endpoint_name() const { return endpoint_name_; } + glcr::String& mutable_endpoint_name() { return endpoint_name_; } void set_endpoint_name(const glcr::String& value) { endpoint_name_ = value; } + const z_cap_t& endpoint_capability() const { return endpoint_capability_; } + z_cap_t& mutable_endpoint_capability() { return endpoint_capability_; } void set_endpoint_capability(const z_cap_t& value) { endpoint_capability_ = value; } private: @@ -32,62 +39,79 @@ class RegisterEndpointRequest { z_cap_t endpoint_capability_; // Parses everything except for caps. - glcr::Status ParseFromBytesInternal(const glcr::ByteBuffer&, uint64_t offset); + glcr::Status ParseFromBytesInternal(const yunq::MessageView& message); + + uint64_t SerializeInternal(yunq::Serializer& serializer) const; }; class GetEndpointRequest { public: GetEndpointRequest() {} // Delete copy and move until implemented. GetEndpointRequest(const GetEndpointRequest&) = delete; - GetEndpointRequest(GetEndpointRequest&&) = delete; + GetEndpointRequest(GetEndpointRequest&&) = default; + GetEndpointRequest& operator=(GetEndpointRequest&&) = default; - [[nodiscard]] glcr::Status ParseFromBytes(const glcr::ByteBuffer&, uint64_t offset); - [[nodiscard]] glcr::Status ParseFromBytes(const glcr::ByteBuffer&, uint64_t offset, const glcr::CapBuffer&); + [[nodiscard]] glcr::Status ParseFromBytes(const yunq::MessageView& message); + [[nodiscard]] glcr::Status ParseFromBytes(const yunq::MessageView& message, const glcr::CapBuffer&); uint64_t SerializeToBytes(glcr::ByteBuffer&, uint64_t offset) const; uint64_t SerializeToBytes(glcr::ByteBuffer&, uint64_t offset, glcr::CapBuffer&) const; + const glcr::String& endpoint_name() const { return endpoint_name_; } + glcr::String& mutable_endpoint_name() { return endpoint_name_; } void set_endpoint_name(const glcr::String& value) { endpoint_name_ = value; } private: glcr::String endpoint_name_; // Parses everything except for caps. - glcr::Status ParseFromBytesInternal(const glcr::ByteBuffer&, uint64_t offset); + glcr::Status ParseFromBytesInternal(const yunq::MessageView& message); + + uint64_t SerializeInternal(yunq::Serializer& serializer) const; }; class Endpoint { public: Endpoint() {} // Delete copy and move until implemented. Endpoint(const Endpoint&) = delete; - Endpoint(Endpoint&&) = delete; + Endpoint(Endpoint&&) = default; + Endpoint& operator=(Endpoint&&) = default; - [[nodiscard]] glcr::Status ParseFromBytes(const glcr::ByteBuffer&, uint64_t offset); - [[nodiscard]] glcr::Status ParseFromBytes(const glcr::ByteBuffer&, uint64_t offset, const glcr::CapBuffer&); + [[nodiscard]] glcr::Status ParseFromBytes(const yunq::MessageView& message); + [[nodiscard]] glcr::Status ParseFromBytes(const yunq::MessageView& message, const glcr::CapBuffer&); uint64_t SerializeToBytes(glcr::ByteBuffer&, uint64_t offset) const; uint64_t SerializeToBytes(glcr::ByteBuffer&, uint64_t offset, glcr::CapBuffer&) const; + const z_cap_t& endpoint() const { return endpoint_; } + z_cap_t& mutable_endpoint() { return endpoint_; } void set_endpoint(const z_cap_t& value) { endpoint_ = value; } private: z_cap_t endpoint_; // Parses everything except for caps. - glcr::Status ParseFromBytesInternal(const glcr::ByteBuffer&, uint64_t offset); + glcr::Status ParseFromBytesInternal(const yunq::MessageView& message); + + uint64_t SerializeInternal(yunq::Serializer& serializer) const; }; class AhciInfo { public: AhciInfo() {} // Delete copy and move until implemented. AhciInfo(const AhciInfo&) = delete; - AhciInfo(AhciInfo&&) = delete; + AhciInfo(AhciInfo&&) = default; + AhciInfo& operator=(AhciInfo&&) = default; - [[nodiscard]] glcr::Status ParseFromBytes(const glcr::ByteBuffer&, uint64_t offset); - [[nodiscard]] glcr::Status ParseFromBytes(const glcr::ByteBuffer&, uint64_t offset, const glcr::CapBuffer&); + [[nodiscard]] glcr::Status ParseFromBytes(const yunq::MessageView& message); + [[nodiscard]] glcr::Status ParseFromBytes(const yunq::MessageView& message, const glcr::CapBuffer&); uint64_t SerializeToBytes(glcr::ByteBuffer&, uint64_t offset) const; uint64_t SerializeToBytes(glcr::ByteBuffer&, uint64_t offset, glcr::CapBuffer&) const; + const z_cap_t& ahci_region() const { return ahci_region_; } + z_cap_t& mutable_ahci_region() { return ahci_region_; } void set_ahci_region(const z_cap_t& value) { ahci_region_ = value; } + const uint64_t& region_length() const { return region_length_; } + uint64_t& mutable_region_length() { return region_length_; } void set_region_length(const uint64_t& value) { region_length_ = value; } private: @@ -95,42 +119,99 @@ class AhciInfo { uint64_t region_length_; // Parses everything except for caps. - glcr::Status ParseFromBytesInternal(const glcr::ByteBuffer&, uint64_t offset); + glcr::Status ParseFromBytesInternal(const yunq::MessageView& message); + + uint64_t SerializeInternal(yunq::Serializer& serializer) const; +}; +class XhciInfo { + public: + XhciInfo() {} + // Delete copy and move until implemented. + XhciInfo(const XhciInfo&) = delete; + XhciInfo(XhciInfo&&) = default; + XhciInfo& operator=(XhciInfo&&) = default; + + [[nodiscard]] glcr::Status ParseFromBytes(const yunq::MessageView& message); + [[nodiscard]] glcr::Status ParseFromBytes(const yunq::MessageView& message, const glcr::CapBuffer&); + uint64_t SerializeToBytes(glcr::ByteBuffer&, uint64_t offset) const; + uint64_t SerializeToBytes(glcr::ByteBuffer&, uint64_t offset, glcr::CapBuffer&) const; + + const z_cap_t& xhci_region() const { return xhci_region_; } + z_cap_t& mutable_xhci_region() { return xhci_region_; } + void set_xhci_region(const z_cap_t& value) { xhci_region_ = value; } + + const uint64_t& region_length() const { return region_length_; } + uint64_t& mutable_region_length() { return region_length_; } + void set_region_length(const uint64_t& value) { region_length_ = value; } + + private: + z_cap_t xhci_region_; + uint64_t region_length_; + + // Parses everything except for caps. + glcr::Status ParseFromBytesInternal(const yunq::MessageView& message); + + uint64_t SerializeInternal(yunq::Serializer& serializer) const; }; class FramebufferInfo { public: FramebufferInfo() {} // Delete copy and move until implemented. FramebufferInfo(const FramebufferInfo&) = delete; - FramebufferInfo(FramebufferInfo&&) = delete; + FramebufferInfo(FramebufferInfo&&) = default; + FramebufferInfo& operator=(FramebufferInfo&&) = default; - [[nodiscard]] glcr::Status ParseFromBytes(const glcr::ByteBuffer&, uint64_t offset); - [[nodiscard]] glcr::Status ParseFromBytes(const glcr::ByteBuffer&, uint64_t offset, const glcr::CapBuffer&); + [[nodiscard]] glcr::Status ParseFromBytes(const yunq::MessageView& message); + [[nodiscard]] glcr::Status ParseFromBytes(const yunq::MessageView& message, const glcr::CapBuffer&); uint64_t SerializeToBytes(glcr::ByteBuffer&, uint64_t offset) const; uint64_t SerializeToBytes(glcr::ByteBuffer&, uint64_t offset, glcr::CapBuffer&) const; + const uint64_t& address_phys() const { return address_phys_; } + uint64_t& mutable_address_phys() { return address_phys_; } void set_address_phys(const uint64_t& value) { address_phys_ = value; } + const uint64_t& width() const { return width_; } + uint64_t& mutable_width() { return width_; } void set_width(const uint64_t& value) { width_ = value; } + const uint64_t& height() const { return height_; } + uint64_t& mutable_height() { return height_; } void set_height(const uint64_t& value) { height_ = value; } + const uint64_t& pitch() const { return pitch_; } + uint64_t& mutable_pitch() { return pitch_; } void set_pitch(const uint64_t& value) { pitch_ = value; } + const uint64_t& bpp() const { return bpp_; } + uint64_t& mutable_bpp() { return bpp_; } void set_bpp(const uint64_t& value) { bpp_ = value; } + const uint64_t& memory_model() const { return memory_model_; } + uint64_t& mutable_memory_model() { return memory_model_; } void set_memory_model(const uint64_t& value) { memory_model_ = value; } + const uint64_t& red_mask_size() const { return red_mask_size_; } + uint64_t& mutable_red_mask_size() { return red_mask_size_; } void set_red_mask_size(const uint64_t& value) { red_mask_size_ = value; } + const uint64_t& red_mask_shift() const { return red_mask_shift_; } + uint64_t& mutable_red_mask_shift() { return red_mask_shift_; } void set_red_mask_shift(const uint64_t& value) { red_mask_shift_ = value; } + const uint64_t& green_mask_size() const { return green_mask_size_; } + uint64_t& mutable_green_mask_size() { return green_mask_size_; } void set_green_mask_size(const uint64_t& value) { green_mask_size_ = value; } + const uint64_t& green_mask_shift() const { return green_mask_shift_; } + uint64_t& mutable_green_mask_shift() { return green_mask_shift_; } void set_green_mask_shift(const uint64_t& value) { green_mask_shift_ = value; } + const uint64_t& blue_mask_size() const { return blue_mask_size_; } + uint64_t& mutable_blue_mask_size() { return blue_mask_size_; } void set_blue_mask_size(const uint64_t& value) { blue_mask_size_ = value; } + const uint64_t& blue_mask_shift() const { return blue_mask_shift_; } + uint64_t& mutable_blue_mask_shift() { return blue_mask_shift_; } void set_blue_mask_shift(const uint64_t& value) { blue_mask_shift_ = value; } private: @@ -148,24 +229,33 @@ class FramebufferInfo { uint64_t blue_mask_shift_; // Parses everything except for caps. - glcr::Status ParseFromBytesInternal(const glcr::ByteBuffer&, uint64_t offset); + glcr::Status ParseFromBytesInternal(const yunq::MessageView& message); + + uint64_t SerializeInternal(yunq::Serializer& serializer) const; }; class DenaliInfo { public: DenaliInfo() {} // Delete copy and move until implemented. DenaliInfo(const DenaliInfo&) = delete; - DenaliInfo(DenaliInfo&&) = delete; + DenaliInfo(DenaliInfo&&) = default; + DenaliInfo& operator=(DenaliInfo&&) = default; - [[nodiscard]] glcr::Status ParseFromBytes(const glcr::ByteBuffer&, uint64_t offset); - [[nodiscard]] glcr::Status ParseFromBytes(const glcr::ByteBuffer&, uint64_t offset, const glcr::CapBuffer&); + [[nodiscard]] glcr::Status ParseFromBytes(const yunq::MessageView& message); + [[nodiscard]] glcr::Status ParseFromBytes(const yunq::MessageView& message, const glcr::CapBuffer&); uint64_t SerializeToBytes(glcr::ByteBuffer&, uint64_t offset) const; uint64_t SerializeToBytes(glcr::ByteBuffer&, uint64_t offset, glcr::CapBuffer&) const; + const z_cap_t& denali_endpoint() const { return denali_endpoint_; } + z_cap_t& mutable_denali_endpoint() { return denali_endpoint_; } void set_denali_endpoint(const z_cap_t& value) { denali_endpoint_ = value; } + const uint64_t& device_id() const { return device_id_; } + uint64_t& mutable_device_id() { return device_id_; } void set_device_id(const uint64_t& value) { device_id_ = value; } + const uint64_t& lba_offset() const { return lba_offset_; } + uint64_t& mutable_lba_offset() { return lba_offset_; } void set_lba_offset(const uint64_t& value) { lba_offset_ = value; } private: @@ -174,7 +264,9 @@ class DenaliInfo { uint64_t lba_offset_; // Parses everything except for caps. - glcr::Status ParseFromBytesInternal(const glcr::ByteBuffer&, uint64_t offset); + glcr::Status ParseFromBytesInternal(const yunq::MessageView& message); + + uint64_t SerializeInternal(yunq::Serializer& serializer) const; }; diff --git a/sys/yellowstone/lib/yellowstone/yellowstone.yunq.server.cpp b/sys/yellowstone/lib/yellowstone/yellowstone.yunq.server.cpp index c0205ae..5d8c670 100644 --- a/sys/yellowstone/lib/yellowstone/yellowstone.yunq.server.cpp +++ b/sys/yellowstone/lib/yellowstone/yellowstone.yunq.server.cpp @@ -106,7 +106,8 @@ glcr::Status YellowstoneServerBase::HandleRequest(const glcr::ByteBuffer& reques RegisterEndpointRequest yunq_request; - RETURN_ERROR(yunq_request.ParseFromBytes(request, kHeaderSize, req_caps)); + yunq::MessageView request_view(request, kHeaderSize); + RETURN_ERROR(yunq_request.ParseFromBytes(request_view, req_caps)); @@ -124,7 +125,8 @@ glcr::Status YellowstoneServerBase::HandleRequest(const glcr::ByteBuffer& reques GetEndpointRequest yunq_request; - RETURN_ERROR(yunq_request.ParseFromBytes(request, kHeaderSize, req_caps)); + yunq::MessageView request_view(request, kHeaderSize); + RETURN_ERROR(yunq_request.ParseFromBytes(request_view, req_caps)); @@ -162,6 +164,23 @@ glcr::Status YellowstoneServerBase::HandleRequest(const glcr::ByteBuffer& reques + XhciInfo yunq_response; + + + + RETURN_ERROR(HandleGetXhciInfo(yunq_response)); + + + + resp_length = yunq_response.SerializeToBytes(response, kHeaderSize, resp_caps); + + break; + } + case 4: { + + + + FramebufferInfo yunq_response; @@ -174,7 +193,7 @@ glcr::Status YellowstoneServerBase::HandleRequest(const glcr::ByteBuffer& reques break; } - case 4: { + case 5: { diff --git a/sys/yellowstone/lib/yellowstone/yellowstone.yunq.server.h b/sys/yellowstone/lib/yellowstone/yellowstone.yunq.server.h index b725140..bdaa1fb 100644 --- a/sys/yellowstone/lib/yellowstone/yellowstone.yunq.server.h +++ b/sys/yellowstone/lib/yellowstone/yellowstone.yunq.server.h @@ -41,6 +41,10 @@ class YellowstoneServerBase { + [[nodiscard]] virtual glcr::Status HandleGetXhciInfo(XhciInfo&) = 0; + + + [[nodiscard]] virtual glcr::Status HandleGetFramebufferInfo(FramebufferInfo&) = 0; diff --git a/sys/yellowstone/yellowstone.cpp b/sys/yellowstone/yellowstone.cpp deleted file mode 100644 index ced7332..0000000 --- a/sys/yellowstone/yellowstone.cpp +++ /dev/null @@ -1,67 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "hw/gpt.h" -#include "hw/pcie.h" -#include "yellowstone_server.h" - -glcr::ErrorCode SpawnProcess(z_cap_t vmmo_cap, z_cap_t yellowstone_cap) { - mmth::OwnedMemoryRegion region = - mmth::OwnedMemoryRegion::FromCapability(vmmo_cap); - auto error_or = - mmth::SpawnProcessFromElfRegion(region.vaddr(), yellowstone_cap); - if (error_or.ok()) { - return glcr::OK; - } - return error_or.error(); -} - -uint64_t main(uint64_t port_cap) { - check(ParseInitPort(port_cap)); - dbgln("Yellowstone Initializing."); - - ASSIGN_OR_RETURN(auto server, yellowstone::YellowstoneServer::Create()); - Thread server_thread = server->RunServer(); - - ASSIGN_OR_RETURN(uint64_t client_cap, server->CreateClientCap()); - check(SpawnProcess(gBootDenaliVmmoCap, client_cap)); - - server->WaitDenaliRegistered(); - - ASSIGN_OR_RETURN(client_cap, server->CreateClientCap()); - check(SpawnProcess(gBootVictoriaFallsVmmoCap, client_cap)); - - server->WaitVictoriaFallsRegistered(); - - dbgln("VFS Available."); - - mmth::File init_file = mmth::File::Open("/init.txt"); - - glcr::Vector files = - glcr::StrSplit(init_file.as_str(), '\n'); - - for (uint64_t i = 0; i < files.size(); i++) { - if (!files[i].empty()) { - mmth::File binary = - mmth::File::Open(glcr::StrFormat("/bin/{}", files[i])); - - ASSIGN_OR_RETURN(client_cap, server->CreateClientCap()); - auto error_or = mmth::SpawnProcessFromElfRegion( - (uint64_t)binary.raw_ptr(), client_cap); - if (!error_or.ok()) { - check(error_or.error()); - } - } - } - - check(server_thread.Join()); - dbgln("Yellowstone Finished Successfully."); - return 0; -} diff --git a/sys/yellowstone/yellowstone_server.cpp b/sys/yellowstone/yellowstone_server.cpp deleted file mode 100644 index e4e05d6..0000000 --- a/sys/yellowstone/yellowstone_server.cpp +++ /dev/null @@ -1,135 +0,0 @@ -#include "yellowstone_server.h" - -#include -#include -#include -#include -#include -#include -#include - -#include "hw/gpt.h" -#include "hw/pcie.h" - -namespace yellowstone { -namespace { - -struct PartitionInfo { - uint64_t device_id; - uint64_t partition_lba; -}; - -glcr::ErrorOr HandleDenaliRegistration(z_cap_t endpoint_cap) { - GptReader reader( - glcr::UniquePtr(new DenaliClient(endpoint_cap))); - - auto status = reader.ParsePartitionTables(); - if (!status.ok()) { - dbgln("GPT Reader: {}", status.message()); - return status.code(); - } - - return PartitionInfo{.device_id = 0, - .partition_lba = reader.GetPrimaryPartitionLba()}; -} - -} // namespace - -glcr::ErrorOr> YellowstoneServer::Create() { - z_cap_t endpoint_cap; - RET_ERR(ZEndpointCreate(&endpoint_cap)); - - return glcr::UniquePtr( - new YellowstoneServer(endpoint_cap)); -} - -YellowstoneServer::YellowstoneServer(z_cap_t endpoint_cap) - : YellowstoneServerBase(endpoint_cap) {} - -glcr::Status YellowstoneServer::HandleGetAhciInfo(AhciInfo& info) { - info.set_ahci_region(pci_reader_.GetAhciVmmo()); - info.set_region_length(kPcieConfigurationSize); - return glcr::Status::Ok(); -} - -glcr::Status YellowstoneServer::HandleGetFramebufferInfo( - FramebufferInfo& info) { - // FIXME: Don't do this for each request. - mmth::OwnedMemoryRegion region = - mmth::OwnedMemoryRegion::FromCapability(gBootFramebufferVmmoCap); - ZFramebufferInfo* fb = reinterpret_cast(region.vaddr()); - - info.set_address_phys(fb->address_phys); - info.set_width(fb->width); - info.set_height(fb->height); - info.set_pitch(fb->pitch); - info.set_bpp(fb->bpp); - info.set_memory_model(fb->memory_model); - info.set_red_mask_size(fb->red_mask_size); - info.set_red_mask_shift(fb->red_mask_shift); - info.set_green_mask_size(fb->green_mask_size); - info.set_green_mask_shift(fb->green_mask_shift); - info.set_blue_mask_size(fb->blue_mask_size); - info.set_blue_mask_shift(fb->blue_mask_shift); - - return glcr::Status::Ok(); -} - -glcr::Status YellowstoneServer::HandleGetDenali(DenaliInfo& info) { - if (!endpoint_map_.Contains("denali")) { - return glcr::NotFound("Denali Capability Not registered"); - } - z_cap_t new_denali; - check(ZCapDuplicate(endpoint_map_.at("denali"), kZionPerm_All, &new_denali)); - info.set_denali_endpoint(new_denali); - info.set_device_id(device_id_); - info.set_lba_offset(lba_offset_); - return glcr::Status::Ok(); -} - -glcr::Status YellowstoneServer::HandleRegisterEndpoint( - const RegisterEndpointRequest& req) { - dbgln("Registering {}.", req.endpoint_name().view()); - check(endpoint_map_.Insert(req.endpoint_name(), req.endpoint_capability())); - if (req.endpoint_name() == "denali") { - z_cap_t dup_cap; - check(ZCapDuplicate(req.endpoint_capability(), kZionPerm_All, &dup_cap)); - auto part_info_or = HandleDenaliRegistration(dup_cap); - if (!part_info_or.ok()) { - check(part_info_or.error()); - } - device_id_ = part_info_or.value().device_id; - lba_offset_ = part_info_or.value().partition_lba; - - has_denali_semaphore_.Signal(); - } else if (req.endpoint_name() == "victoriafalls") { - // FIXME: Probably make a separate copy for use within yellowstone vs - // transmit to other processes. - mmth::SetVfsCap(req.endpoint_capability()); - has_victoriafalls_semaphore_.Signal(); - } else { - dbgln("[WARN] Got endpoint cap type: {}", req.endpoint_name().cstr()); - } - return glcr::Status::Ok(); -} - -glcr::Status YellowstoneServer::HandleGetEndpoint(const GetEndpointRequest& req, - Endpoint& resp) { - if (!endpoint_map_.Contains(req.endpoint_name())) { - return glcr::NotFound( - glcr::StrFormat("Endpoint '{}' not found.", req.endpoint_name())); - } - z_cap_t cap = endpoint_map_.at(req.endpoint_name()); - z_cap_t new_cap; - check(ZCapDuplicate(cap, kZionPerm_All, &new_cap)); - resp.set_endpoint(new_cap); - return glcr::Status::Ok(); -} - -void YellowstoneServer::WaitDenaliRegistered() { has_denali_semaphore_.Wait(); } - -void YellowstoneServer::WaitVictoriaFallsRegistered() { - has_victoriafalls_semaphore_.Wait(); -} - -} // namespace yellowstone diff --git a/sys/yellowstone/yellowstone_server.h b/sys/yellowstone/yellowstone_server.h deleted file mode 100644 index 6866001..0000000 --- a/sys/yellowstone/yellowstone_server.h +++ /dev/null @@ -1,43 +0,0 @@ -#pragma once - -#include -#include -#include -#include -#include -#include - -#include "hw/pcie.h" -#include "lib/yellowstone/yellowstone.yunq.server.h" - -namespace yellowstone { - -class YellowstoneServer : public YellowstoneServerBase { - public: - static glcr::ErrorOr> Create(); - - glcr::Status HandleGetAhciInfo(AhciInfo&) override; - glcr::Status HandleGetFramebufferInfo(FramebufferInfo&) override; - glcr::Status HandleGetDenali(DenaliInfo&) override; - glcr::Status HandleRegisterEndpoint(const RegisterEndpointRequest&) override; - glcr::Status HandleGetEndpoint(const GetEndpointRequest&, Endpoint&) override; - - void WaitDenaliRegistered(); - void WaitVictoriaFallsRegistered(); - - private: - glcr::HashMap endpoint_map_; - - uint64_t device_id_ = 0; - uint64_t lba_offset_ = 0; - glcr::SharedPtr vfs_client_; - - PciReader pci_reader_; - - mmth::Semaphore has_denali_semaphore_; - mmth::Semaphore has_victoriafalls_semaphore_; - - YellowstoneServer(z_cap_t endpoint_cap); -}; - -} // namespace yellowstone diff --git a/usr/testbed/test.cpp b/usr/testbed/test.cpp index ef3f2eb..3f7f324 100644 --- a/usr/testbed/test.cpp +++ b/usr/testbed/test.cpp @@ -1,7 +1,10 @@ #include +#include uint64_t main(uint64_t init_port_cap) { dbgln("testbed"); + check(ZThreadSleep(2000)); + dbgln("testbed2"); return glcr::OK; } diff --git a/yunq/CMakeLists.txt b/yunq/CMakeLists.txt index 271476d..df824b8 100644 --- a/yunq/CMakeLists.txt +++ b/yunq/CMakeLists.txt @@ -19,6 +19,7 @@ macro(yunq_gen dir include_dir name) mammoth glacier yunq + zion_stub ) set_target_properties(${target} PROPERTIES COMPILE_FLAGS "${CMAKE_CXX_FLAGS} ${BASE_COMPILE_FLAGS}" diff --git a/yunq/client.cpp.jinja b/yunq/client.cpp.jinja index 0643ad6..e4e8102 100644 --- a/yunq/client.cpp.jinja +++ b/yunq/client.cpp.jinja @@ -31,7 +31,7 @@ glcr::Status {{interface.name}}Client::{{method.name}}(const {{method.request}}& const uint32_t kSentinel = 0xBEEFDEAD; buffer_.WriteAt(0, kSentinel); - buffer_.WriteAt(8, {{loop.index0}}); + buffer_.WriteAt(8, {{method.number}}); cap_buffer_.Reset(); {% if method.request == None %} @@ -56,7 +56,8 @@ glcr::Status {{interface.name}}Client::{{method.name}}(const {{method.request}}& RET_ERR(buffer_.At(8)); {% if method.response != None %} - RETURN_ERROR(response.ParseFromBytes(buffer_, 16, cap_buffer_)); + yunq::MessageView resp_view(buffer_, 16); + RETURN_ERROR(response.ParseFromBytes(resp_view, cap_buffer_)); {% endif %} return glcr::OK; diff --git a/yunq/example/example.yunq b/yunq/example/example.yunq deleted file mode 100644 index f286199..0000000 --- a/yunq/example/example.yunq +++ /dev/null @@ -1,16 +0,0 @@ -package srv.file; - -interface VFS { - method open (OpenFileRequest) -> (File); -} - -message OpenFileRequest { - string path; - repeated u64 options; -} - -message File { - string path; - u64 attrs; - capability mem_cap; -} diff --git a/yunq/example/example.yunq.client.cpp b/yunq/example/example.yunq.client.cpp deleted file mode 100644 index 1d3dd21..0000000 --- a/yunq/example/example.yunq.client.cpp +++ /dev/null @@ -1,63 +0,0 @@ -// Generated file - DO NOT MODIFY -#include "example.yunq.client.h" - -#include -#include -#include -#include - - -namespace srv::file { - - - -VFSClient::~VFSClient() { - if (endpoint_ != 0) { - check(ZCapRelease(endpoint_)); - } -} - - - - -glcr::ErrorCode VFSClient::open(const OpenFileRequest& request, File& response) { - - uint64_t buffer_size = kBufferSize; - uint64_t cap_size = kCapBufferSize; - - const uint32_t kSentinel = 0xBEEFDEAD; - buffer_.WriteAt(0, kSentinel); - buffer_.WriteAt(8, 0); - - cap_buffer_.Reset(); - - uint64_t length = request.SerializeToBytes(buffer_, /*offset=*/16, cap_buffer_); - - - buffer_.WriteAt(4, 16 + length); - - z_cap_t reply_port_cap; - RET_ERR(ZEndpointSend(endpoint_, 16 + length, buffer_.RawPtr(), cap_buffer_.UsedSlots(), cap_buffer_.RawPtr(), &reply_port_cap)); - - // FIXME: Add a way to zero out the first buffer. - RET_ERR(ZReplyPortRecv(reply_port_cap, &buffer_size, buffer_.RawPtr(), &cap_size, cap_buffer_.RawPtr())); - - if (buffer_.At(0) != kSentinel) { - return glcr::INVALID_RESPONSE; - } - - // Check Response Code. - RET_ERR(buffer_.At(8)); - - - response.ParseFromBytes(buffer_, 16, cap_buffer_); - - - return glcr::OK; -} - - - - - -} // namepace srv::file diff --git a/yunq/example/example.yunq.client.h b/yunq/example/example.yunq.client.h deleted file mode 100644 index d9fdb3c..0000000 --- a/yunq/example/example.yunq.client.h +++ /dev/null @@ -1,37 +0,0 @@ -// Generated file - DO NOT MODIFY -#pragma once - -#include -#include -#include -#include - -#include "example.yunq.h" - - -namespace srv::file { - -class VFSClient { - public: - VFSClient(z_cap_t VFS_cap) : endpoint_(VFS_cap) {} - VFSClient(const VFSClient&) = delete; - VFSClient(VFSClient&& other) : endpoint_(other.endpoint_) {other.endpoint_ = 0;}; - ~VFSClient(); - - z_cap_t Capability() { return endpoint_; } - - - - [[nodiscard]] glcr::ErrorCode open(const OpenFileRequest& request, File& response); - - - private: - z_cap_t endpoint_; - uint64_t kBufferSize = 0x1000; - glcr::ByteBuffer buffer_{kBufferSize}; - uint64_t kCapBufferSize = 0x10; - glcr::CapBuffer cap_buffer_{kCapBufferSize}; -}; - - -} // namepace srv::file diff --git a/yunq/example/example.yunq.cpp b/yunq/example/example.yunq.cpp deleted file mode 100644 index 1211542..0000000 --- a/yunq/example/example.yunq.cpp +++ /dev/null @@ -1,210 +0,0 @@ -// Generated file -- DO NOT MODIFY. -#include "example.yunq.h" - - -namespace srv::file { - -namespace { - -const uint64_t header_size = 24; // 4x uint32, 1x uint64 - -struct ExtPointer { - uint32_t offset; - uint32_t length; -}; - -void CheckHeader(const glcr::ByteBuffer& bytes) { - // TODO: Check ident. - // TODO: Parse core size. - // TODO: Parse extension size. - // TODO: Check CRC32 - // TODO: Parse options. -} - -void WriteHeader(glcr::ByteBuffer& bytes, uint64_t offset, uint32_t core_size, uint32_t extension_size) { - bytes.WriteAt(offset + 0, 0xDEADBEEF); // TODO: Chose a more unique ident sequence. - bytes.WriteAt(offset + 4, core_size); - bytes.WriteAt(offset + 8, extension_size); - bytes.WriteAt(offset + 12, 0); // TODO: Calculate CRC32. - bytes.WriteAt(offset + 16, 0); // TODO: Add options. -} - -} // namespace -void OpenFileRequest::ParseFromBytes(const glcr::ByteBuffer& bytes, uint64_t offset) { - ParseFromBytesInternal(bytes, offset); -} - -void OpenFileRequest::ParseFromBytes(const glcr::ByteBuffer& bytes, uint64_t offset, const glcr::CapBuffer& caps) { - ParseFromBytesInternal(bytes, offset); -} - -void OpenFileRequest::ParseFromBytesInternal(const glcr::ByteBuffer& bytes, uint64_t offset) { - CheckHeader(bytes); - // Parse path. - auto path_pointer = bytes.At(offset + header_size + (8 * 0)); - - set_path(bytes.StringAt(offset + path_pointer.offset, path_pointer.length)); - // Parse options. - auto options_pointer = bytes.At(offset + header_size + (8 * 1)); - - options_.Resize(options_pointer.length / sizeof(uint64_t)); - for (uint64_t i = offset + options_pointer.offset; - i < offset + options_pointer.offset + options_pointer.length; - i += sizeof(uint64_t)) { - options_.PushBack(bytes.At(i)); - } - - -} - -uint64_t OpenFileRequest::SerializeToBytes(glcr::ByteBuffer& bytes, uint64_t offset) const { - 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. - ExtPointer options_ptr{ - .offset = next_extension, - .length = (uint32_t)(options().size() * sizeof(uint64_t)), - }; - - next_extension += options_ptr.length; - bytes.WriteAt(offset + header_size + (8 * 1), options_ptr); - - for (uint64_t i = 0; i < options().size(); i++) { - uint32_t ext_offset = offset + options_ptr.offset + (i * sizeof(uint64_t)); - bytes.WriteAt(ext_offset, options().at(i)); - } - - // 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) const { - 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. - ExtPointer options_ptr{ - .offset = next_extension, - .length = (uint32_t)(options().size() * sizeof(uint64_t)), - }; - - next_extension += options_ptr.length; - bytes.WriteAt(offset + header_size + (8 * 1), options_ptr); - - for (uint64_t i = 0; i < options().size(); i++) { - uint32_t ext_offset = offset + options_ptr.offset + (i * sizeof(uint64_t)); - bytes.WriteAt(ext_offset, options().at(i)); - } - - // 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) { - ParseFromBytesInternal(bytes, offset); - // Parse mem_cap. - // FIXME: Implement in-buffer capabilities for inprocess serialization. - set_mem_cap(0); -} - -void File::ParseFromBytes(const glcr::ByteBuffer& bytes, uint64_t offset, const glcr::CapBuffer& caps) { - ParseFromBytesInternal(bytes, offset); - // Parse mem_cap. - uint64_t mem_cap_ptr = bytes.At(offset + header_size + (8 * 2)); - - set_mem_cap(caps.At(mem_cap_ptr)); -} - -void File::ParseFromBytesInternal(const glcr::ByteBuffer& bytes, uint64_t offset) { - CheckHeader(bytes); - // Parse path. - auto path_pointer = bytes.At(offset + header_size + (8 * 0)); - - set_path(bytes.StringAt(offset + path_pointer.offset, path_pointer.length)); - // Parse attrs. - set_attrs(bytes.At(offset + header_size + (8 * 1))); - // Parse mem_cap. - // Skip Cap. - -} - -uint64_t File::SerializeToBytes(glcr::ByteBuffer& bytes, uint64_t offset) const { - 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) const { - 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; -} - - -} // namepace srv::file diff --git a/yunq/example/example.yunq.h b/yunq/example/example.yunq.h deleted file mode 100644 index 1b9176e..0000000 --- a/yunq/example/example.yunq.h +++ /dev/null @@ -1,64 +0,0 @@ -// Generated file - DO NOT MODIFY -#pragma once - -#include -#include -#include -#include -#include - - -namespace srv::file { - -class OpenFileRequest { - public: - OpenFileRequest() {} - // Delete copy and move until implemented. - OpenFileRequest(const OpenFileRequest&) = delete; - OpenFileRequest(OpenFileRequest&&) = delete; - - void ParseFromBytes(const glcr::ByteBuffer&, uint64_t offset); - void ParseFromBytes(const glcr::ByteBuffer&, uint64_t offset, const glcr::CapBuffer&); - uint64_t SerializeToBytes(glcr::ByteBuffer&, uint64_t offset) const; - uint64_t SerializeToBytes(glcr::ByteBuffer&, uint64_t offset, glcr::CapBuffer&) const; - const glcr::String& path() const { return path_; } - void set_path(const glcr::String& value) { path_ = value; } - const glcr::Vector& options() const { return options_; } - void add_options(const uint64_t& value) { options_.PushBack(value); } - - private: - glcr::String path_; - glcr::Vector options_; - - // Parses everything except for caps. - void ParseFromBytesInternal(const glcr::ByteBuffer&, uint64_t offset); -}; -class File { - public: - File() {} - // Delete copy and move until implemented. - File(const File&) = delete; - File(File&&) = delete; - - void ParseFromBytes(const glcr::ByteBuffer&, uint64_t offset); - void ParseFromBytes(const glcr::ByteBuffer&, uint64_t offset, const glcr::CapBuffer&); - uint64_t SerializeToBytes(glcr::ByteBuffer&, uint64_t offset) const; - uint64_t SerializeToBytes(glcr::ByteBuffer&, uint64_t offset, glcr::CapBuffer&) const; - const glcr::String& path() const { return path_; } - void set_path(const glcr::String& value) { path_ = value; } - const uint64_t& attrs() const { return attrs_; } - void set_attrs(const uint64_t& value) { attrs_ = value; } - const z_cap_t& mem_cap() const { 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_; - - // Parses everything except for caps. - void ParseFromBytesInternal(const glcr::ByteBuffer&, uint64_t offset); -}; - - -} // namepace srv::file diff --git a/yunq/example/example.yunq.server.cpp b/yunq/example/example.yunq.server.cpp deleted file mode 100644 index 555c8d7..0000000 --- a/yunq/example/example.yunq.server.cpp +++ /dev/null @@ -1,133 +0,0 @@ -// Generated file -- DO NOT MODIFY. -#include "example.yunq.server.h" - -#include -#include - - -namespace srv::file { - -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 - - - -void VFSServerBaseThreadBootstrap(void* server_base) { - ((VFSServerBase*)server_base)->ServerThread(); -} - -VFSServerBase::~VFSServerBase() { - if (endpoint_ != 0) { - check(ZCapRelease(endpoint_)); - } -} - -glcr::ErrorOr VFSServerBase::CreateClientCap() { - uint64_t client_cap; - RET_ERR(ZCapDuplicate(endpoint_, ~(kZionPerm_Read), &client_cap)); - return client_cap; -} - -glcr::ErrorOr VFSServerBase::CreateClient() { - uint64_t client_cap; - RET_ERR(ZCapDuplicate(endpoint_, ~(kZionPerm_Read), &client_cap)); - return VFSClient(client_cap); -} - -Thread VFSServerBase::RunServer() { - return Thread(VFSServerBaseThreadBootstrap, this); -} - -void VFSServerBase::ServerThread() { - glcr::ByteBuffer recv_buffer(0x1000); - glcr::CapBuffer recv_cap(0x10); - glcr::ByteBuffer resp_buffer(0x1000); - glcr::CapBuffer resp_cap(0x10); - z_cap_t reply_port_cap; - - while (true) { - uint64_t recv_cap_size = 0x10; - uint64_t recv_buf_size = 0x1000; - recv_cap.Reset(); - glcr::ErrorCode recv_err = static_cast(ZEndpointRecv(endpoint_, &recv_buf_size, recv_buffer.RawPtr(), &recv_cap_size, recv_cap.RawPtr(), &reply_port_cap)); - if (recv_err != glcr::OK) { - dbgln("Error in receive: {x}", recv_err); - continue; - } - - uint64_t resp_length = 0; - - glcr::ErrorCode reply_err = glcr::OK; - resp_cap.Reset(); - glcr::ErrorCode err = HandleRequest(recv_buffer, recv_cap, resp_buffer, resp_length, resp_cap); - if (err != glcr::OK) { - WriteError(resp_buffer, err); - reply_err = static_cast(ZReplyPortSend(reply_port_cap, kHeaderSize, resp_buffer.RawPtr(), 0, nullptr)); - } else { - WriteHeader(resp_buffer, resp_length); - reply_err = static_cast(ZReplyPortSend(reply_port_cap, kHeaderSize + resp_length, resp_buffer.RawPtr(), resp_cap.UsedSlots(), resp_cap.RawPtr())); - } - if (reply_err != glcr::OK) { - dbgln("Error in reply: {x}", reply_err); - } - } - -} - -glcr::ErrorCode VFSServerBase::HandleRequest(const glcr::ByteBuffer& request, - const glcr::CapBuffer& req_caps, - glcr::ByteBuffer& response, uint64_t& resp_length, - glcr::CapBuffer& resp_caps) { - if (request.At(0) != kSentinel) { - return glcr::INVALID_ARGUMENT; - } - - uint64_t method_select = request.At(8); - - switch(method_select) { - case 0: { - - - OpenFileRequest yunq_request; - yunq_request.ParseFromBytes(request, kHeaderSize, req_caps); - - - - File yunq_response; - - - - RET_ERR(Handleopen(yunq_request, yunq_response)); - - - - resp_length = yunq_response.SerializeToBytes(response, kHeaderSize, resp_caps); - - break; - } - default: { - return glcr::UNIMPLEMENTED; - } - } - return glcr::OK; -} - - - -} // namepace srv::file diff --git a/yunq/example/example.yunq.server.h b/yunq/example/example.yunq.server.h deleted file mode 100644 index e1cb556..0000000 --- a/yunq/example/example.yunq.server.h +++ /dev/null @@ -1,49 +0,0 @@ -// Generated File -- DO NOT MODIFY. -#pragma once - -#include -#include -#include - -#include "example.yunq.h" -#include "example.yunq.client.h" - - -namespace srv::file { - - - - -class VFSServerBase { - public: - VFSServerBase(z_cap_t VFS_cap) : endpoint_(VFS_cap) {} - VFSServerBase(const VFSServerBase&) = delete; - VFSServerBase(VFSServerBase&&) = delete; - virtual ~VFSServerBase(); - - glcr::ErrorOr CreateClientCap(); - glcr::ErrorOr CreateClient(); - - [[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, const glcr::CapBuffer& req_caps, - glcr::ByteBuffer& response, uint64_t& resp_length, - glcr::CapBuffer& resp_caps); -}; - - - - -} // namepace srv::file diff --git a/yunq/message.cpp.jinja b/yunq/message.cpp.jinja index 30f5556..b5e36b9 100644 --- a/yunq/message.cpp.jinja +++ b/yunq/message.cpp.jinja @@ -1,6 +1,7 @@ // Generated file -- DO NOT MODIFY. #include "{{file}}.h" +#include #include {% if package != None %} @@ -18,167 +19,108 @@ struct ExtPointer { } // namespace {%- for message in messages %} -glcr::Status {{message.name}}::ParseFromBytes(const glcr::ByteBuffer& bytes, uint64_t offset) { - RETURN_ERROR(ParseFromBytesInternal(bytes, offset)); +glcr::Status {{message.name}}::ParseFromBytes(const yunq::MessageView& message) { + RETURN_ERROR(ParseFromBytesInternal(message)); {%- for field in message.fields %} {%- if field.type == Type.CAPABILITY %} - // Parse {{field.name}}. - // FIXME: Implement in-buffer capabilities for inprocess serialization. - set_{{field.name}}(0); -{%- endif %} -{%- endfor %} - return glcr::Status::Ok(); -} - -glcr::Status {{message.name}}::ParseFromBytes(const glcr::ByteBuffer& bytes, uint64_t offset, const glcr::CapBuffer& caps) { - RETURN_ERROR(ParseFromBytesInternal(bytes, offset)); - -{%- for field in message.fields %} -{%- if field.type == Type.CAPABILITY %} - // Parse {{field.name}}. - uint64_t {{field.name}}_ptr = bytes.At(offset + header_size + (8 * {{loop.index0}})); - - set_{{field.name}}(caps.At({{field.name}}_ptr)); -{%- endif %} -{%- endfor %} - return glcr::Status::Ok(); -} - -glcr::Status {{message.name}}::ParseFromBytesInternal(const glcr::ByteBuffer& bytes, uint64_t offset) { - RETURN_ERROR(yunq::CheckHeader(bytes, offset)); - -{%- for field in message.fields %} - // Parse {{field.name}}. {%- if not field.repeated %} -{%- if field.type == Type.U64 %} - set_{{field.name}}(bytes.At(offset + header_size + (8 * {{loop.index0}}))); -{%- elif field.type == Type.I64 %} - set_{{field.name}}(bytes.At(offset + header_size + (8 * {{loop.index0}}))); -{%- elif field.type == Type.STRING %} - auto {{field.name}}_pointer = bytes.At(offset + header_size + (8 * {{loop.index0}})); - - set_{{field.name}}(bytes.StringAt(offset + {{field.name}}_pointer.offset, {{field.name}}_pointer.length)); -{%- elif field.type == Type.CAPABILITY %} - // Skip Cap. -{%- else %} - // TODO: Unimplemented parsing {{field.type}} + // Parse {{field.name}}. + ASSIGN_OR_RETURN({{field.name}}_, message.ReadCapability({{field.number}})); + {%- else %} + // Parse {{field.name}}. + ASSIGN_OR_RETURN({{field.name}}_, message.ReadRepeatedCapability({{field.number}})); + {%- endif %} {%- endif %} -{%- else %} - auto {{field.name}}_pointer = bytes.At(offset + header_size + (8 * {{loop.index0}})); +{%- endfor %} + return glcr::Status::Ok(); +} - {{field.name}}_.Resize({{field.name}}_pointer.length / sizeof({{field.cpp_type()}})); - for (uint64_t i = offset + {{field.name}}_pointer.offset; - i < offset + {{field.name}}_pointer.offset + {{field.name}}_pointer.length; - i += sizeof({{field.cpp_type()}})) { - {{field.name}}_.PushBack(bytes.At<{{field.cpp_type()}}>(i)); - } +glcr::Status {{message.name}}::ParseFromBytes(const yunq::MessageView& message, const glcr::CapBuffer& caps) { + RETURN_ERROR(ParseFromBytesInternal(message)); + +{%- for field in message.fields %} +{%- if field.type == Type.CAPABILITY %} +{%- if not field.repeated %} + // Parse {{field.name}}. + ASSIGN_OR_RETURN({{field.name}}_, message.ReadCapability({{field.number}}, caps)); + {%- else %} + // Parse {{field.name}}. + ASSIGN_OR_RETURN({{field.name}}_, message.ReadRepeatedCapability({{field.number}}, caps)); + {%- endif %} +{%- endif %} +{%- endfor %} + return glcr::Status::Ok(); +} + +glcr::Status {{message.name}}::ParseFromBytesInternal(const yunq::MessageView& message) { + RETURN_ERROR(message.CheckHeader()); + +{%- for field in message.fields %} + // Parse {{field.name}}. +{%- if field.type == Type.MESSAGE %} + +{%- if not field.repeated %} + message.ReadMessage<{{field.cpp_type()}}>({{field.number}}, {{field.name}}_); +{%- else %} + message.ReadRepeatedMessage<{{field.cpp_type()}}>({{field.number}}, {{field.name}}_); {% endif %} + +{%- elif field.type != Type.CAPABILITY %} + +{%- if not field.repeated %} + ASSIGN_OR_RETURN({{field.name}}_, message.ReadField<{{field.cpp_type()}}>({{field.number}})); +{%- else %} + ASSIGN_OR_RETURN({{field.name}}_, message.ReadRepeated<{{field.cpp_type()}}>({{field.number}})); +{% endif %} + +{%- endif %} {%- endfor %} return glcr::Status::Ok(); } uint64_t {{message.name}}::SerializeToBytes(glcr::ByteBuffer& bytes, uint64_t offset) const { - uint32_t next_extension = header_size + 8 * {{ message.fields | length }}; - const uint32_t core_size = next_extension; - -{%- for field in message.fields %} - // Write {{field.name}}. -{%- if not field.repeated %} -{%- if field.type == Type.U64 %} - bytes.WriteAt(offset + header_size + (8 * {{loop.index0}}), {{field.name}}()); -{%- elif field.type == Type.I64 %} - bytes.WriteAt(offset + header_size + (8 * {{loop.index0}}), {{field.name}}()); -{%- elif field.type == Type.STRING %} - ExtPointer {{field.name}}_ptr{ - .offset = next_extension, - // FIXME: Check downcast of str length. - .length = (uint32_t){{field.name}}().length(), - }; - - bytes.WriteStringAt(offset + next_extension, {{field.name}}()); - next_extension += {{field.name}}_ptr.length; - - bytes.WriteAt(offset + header_size + (8 * {{loop.index0}}), {{field.name}}_ptr); -{%- elif field.type == Type.CAPABILITY %} - // FIXME: Implement inbuffer capabilities. - bytes.WriteAt(offset + header_size + (8 * {{loop.index0}}), 0); -{%- else %} - // TODO: Unimplemented serialization {{field.type}} -{%- endif %} -{%- else %} - ExtPointer {{field.name}}_ptr{ - .offset = next_extension, - .length = (uint32_t)({{field.name}}().size() * sizeof({{field.cpp_type()}})), - }; - - next_extension += {{field.name}}_ptr.length; - bytes.WriteAt(offset + header_size + (8 * {{loop.index0}}), {{field.name}}_ptr); - - for (uint64_t i = 0; i < {{field.name}}().size(); i++) { - uint32_t ext_offset = offset + {{field.name}}_ptr.offset + (i * sizeof({{field.cpp_type()}})); - bytes.WriteAt<{{field.cpp_type()}}>(ext_offset, {{field.name}}().at(i)); - } -{%- endif %} -{%- endfor %} - - // The next extension pointer is the length of the message. - yunq::WriteHeader(bytes, offset, core_size, next_extension); - - return next_extension; + yunq::Serializer serializer(bytes, offset, {{ message.fields | length }}); + return SerializeInternal(serializer); } uint64_t {{message.name}}::SerializeToBytes(glcr::ByteBuffer& bytes, uint64_t offset, glcr::CapBuffer& caps) const { - uint32_t next_extension = header_size + 8 * {{ message.fields | length}}; - const uint32_t core_size = next_extension; - uint64_t next_cap = 0; - + yunq::Serializer serializer(bytes, offset, {{ message.fields | length }}, caps); + return SerializeInternal(serializer); +} + +uint64_t {{message.name}}::SerializeInternal(yunq::Serializer& serializer) const { {%- for field in message.fields %} // Write {{field.name}}. {%- if not field.repeated %} -{%- if field.type == Type.U64 %} - bytes.WriteAt(offset + header_size + (8 * {{loop.index0}}), {{field.name}}()); -{%- elif field.type == Type.I64 %} - bytes.WriteAt(offset + header_size + (8 * {{loop.index0}}), {{field.name}}()); -{%- elif field.type == Type.STRING %} - ExtPointer {{field.name}}_ptr{ - .offset = next_extension, - // FIXME: Check downcast of str length. - .length = (uint32_t){{field.name}}().length(), - }; - bytes.WriteStringAt(offset + next_extension, {{field.name}}()); - next_extension += {{field.name}}_ptr.length; - - bytes.WriteAt(offset + header_size + (8 * {{loop.index0}}), {{field.name}}_ptr); +{%- if field.type == Type.MESSAGE %} + serializer.WriteMessage<{{field.cpp_type()}}>({{field.number}}, {{field.name}}_); {%- elif field.type == Type.CAPABILITY %} - caps.WriteAt(next_cap, {{field.name}}()); - bytes.WriteAt(offset + header_size + (8 * {{loop.index0}}), next_cap++); + serializer.WriteCapability({{field.number}}, {{field.name}}_); {%- else %} - // TODO: Unimplemented serialization {{field.type}} + serializer.WriteField<{{field.cpp_type()}}>({{field.number}}, {{field.name}}_); {%- endif %} + {%- else %} - ExtPointer {{field.name}}_ptr{ - .offset = next_extension, - .length = (uint32_t)({{field.name}}().size() * sizeof({{field.cpp_type()}})), - }; - next_extension += {{field.name}}_ptr.length; - bytes.WriteAt(offset + header_size + (8 * {{loop.index0}}), {{field.name}}_ptr); +{%- if field.type == Type.MESSAGE %} + serializer.WriteRepeatedMessage<{{field.cpp_type()}}>({{field.number}}, {{field.name}}_); +{%- elif field.type == Type.CAPABILITY %} + serializer.WriteRepeatedCapability({{field.number}}, {{field.name}}_); +{%- else %} + serializer.WriteRepeated<{{field.cpp_type()}}>({{field.number}}, {{field.name}}_); +{%- endif %} - for (uint64_t i = 0; i < {{field.name}}().size(); i++) { - uint32_t ext_offset = offset + {{field.name}}_ptr.offset + (i * sizeof({{field.cpp_type()}})); - bytes.WriteAt<{{field.cpp_type()}}>(ext_offset, {{field.name}}().at(i)); - } {%- endif %} {%- endfor %} - // The next extension pointer is the length of the message. - yunq::WriteHeader(bytes, offset, core_size, next_extension); + serializer.WriteHeader(); - return next_extension; + return serializer.size(); } + {%- endfor %} {% if package != None %} diff --git a/yunq/message.h.jinja b/yunq/message.h.jinja index cf15b59..2cca2c2 100644 --- a/yunq/message.h.jinja +++ b/yunq/message.h.jinja @@ -6,6 +6,8 @@ #include #include #include +#include +#include #include {% if package != None %} @@ -18,21 +20,35 @@ class {{message.name}} { {{message.name}}() {} // Delete copy and move until implemented. {{message.name}}(const {{message.name}}&) = delete; - {{message.name}}({{message.name}}&&) = delete; + {{message.name}}({{message.name}}&&) = default; + {{message.name}}& operator=({{message.name}}&&) = default; - [[nodiscard]] glcr::Status ParseFromBytes(const glcr::ByteBuffer&, uint64_t offset); - [[nodiscard]] glcr::Status ParseFromBytes(const glcr::ByteBuffer&, uint64_t offset, const glcr::CapBuffer&); + [[nodiscard]] glcr::Status ParseFromBytes(const yunq::MessageView& message); + [[nodiscard]] glcr::Status ParseFromBytes(const yunq::MessageView& message, const glcr::CapBuffer&); uint64_t SerializeToBytes(glcr::ByteBuffer&, uint64_t offset) const; uint64_t SerializeToBytes(glcr::ByteBuffer&, uint64_t offset, glcr::CapBuffer&) const; {%- for field in message.fields %} {%- if not field.repeated %} + const {{field.cpp_type()}}& {{field.name}}() const { return {{field.name}}_; } + {{field.cpp_type()}}& mutable_{{field.name}}() { return {{field.name}}_; } + + {%- if field.type != Type.MESSAGE %} void set_{{field.name}}(const {{field.cpp_type()}}& value) { {{field.name}}_ = value; } + {%- endif %} + {%- else %} + const glcr::Vector<{{field.cpp_type()}}>& {{field.name}}() const { return {{field.name}}_; } + glcr::Vector<{{field.cpp_type()}}>& mutable_{{field.name}}() { return {{field.name}}_; } + + {%- if field.type != Type.MESSAGE %} void add_{{field.name}}(const {{field.cpp_type()}}& value) { {{field.name}}_.PushBack(value); } {%- endif %} + void add_{{field.name}}({{field.cpp_type()}}&& value) { {{field.name}}_.PushBack(glcr::Move(value)); } + + {%- endif %} {%- endfor %} private: @@ -45,7 +61,9 @@ class {{message.name}} { {%- endfor %} // Parses everything except for caps. - glcr::Status ParseFromBytesInternal(const glcr::ByteBuffer&, uint64_t offset); + glcr::Status ParseFromBytesInternal(const yunq::MessageView& message); + + uint64_t SerializeInternal(yunq::Serializer& serializer) const; }; {%- endfor %} diff --git a/yunq/parser.py b/yunq/parser.py index d9c3eaa..c3ec971 100644 --- a/yunq/parser.py +++ b/yunq/parser.py @@ -17,6 +17,7 @@ class LexemeType(Enum): ARROW = 7 SEMICOLON = 8 DOT = 9 + EQUALS = 10 class Lexeme(): @@ -105,6 +106,7 @@ class Type(Enum): STRING = 3 BYTES = 4 CAPABILITY = 5 + MESSAGE = 6 type_str_dict = { "u64": Type.U64, @@ -123,12 +125,18 @@ type_to_cppstr = { } class Field(): - def __init__(self, fieldtype: Type, name: str, repeated = False): - self.type = fieldtype + def __init__(self, field_type_str: str, name: str, repeated = False): + if field_type_str in type_str_dict.keys(): + self.type = type_str_dict[field_type_str] + else: + self.type = Type.MESSAGE + self.type_str = field_type_str self.name = name self.repeated = repeated def cpp_type(self): + if self.type == Type.MESSAGE: + return self.type_str return type_to_cppstr[self.type] class Message(): @@ -214,8 +222,11 @@ class Parser(): methods: list[Method] = [] method_names = set() + next_method_number = 0 while self.peektype() != LexemeType.RIGHT_BRACE: m = self.method() + m.number = next_method_number + next_method_number += 1 if m.name in method_names: sys.exit("Method %s declared twice on %s" % (m.name, name)) method_names.add(m.name) @@ -266,8 +277,11 @@ class Parser(): fields: list[Field] = [] field_names = set() + next_field_num = 0 while self.peektype() != LexemeType.RIGHT_BRACE: f = self.field() + f.number = next_field_num + next_field_num += 1 if f.name in field_names: sys.exit("Field %s declared twice on %s" % (f.name, name)) field_names.add(f.name) @@ -286,14 +300,10 @@ class Parser(): if field_type_str == "repeated": repeated = True field_type_str = self.consume_identifier() - - if field_type_str not in type_str_dict.keys(): - sys.exit("Expected type got '%s'" % field_type_str) - field_type = type_str_dict[field_type_str] name = self.consume_identifier() self.consume_check(LexemeType.SEMICOLON) - return Field(field_type, name, repeated) + return Field(field_type_str, name, repeated) def type_check(decls: list[Decl]): if sum(1 for decl in decls if type(decl) is Package) > 1: @@ -313,6 +323,12 @@ def type_check(decls: list[Decl]): sys.exit("Response type '%s' for '%s.%s' does not exist" % (method.response, decl.name, method.name)) if type(name_dict[method.response]) is not Message: sys.exit("Response type '%s' for '%s.%s' should be a message" % (method.response, decl.name, method.name)) + elif type(decl) is Message: + for field in decl.fields: + if field.type == Type.MESSAGE: + if field.type_str not in name_dict.keys(): + sys.exit("Field type '%s' for field '%s' in message '%s' not found." % + (field.type_str, field.name, decl.name)) def print_ast(decls: list[Decl]): for decl in decls: diff --git a/yunq/rust/Cargo.lock b/yunq/rust/Cargo.lock new file mode 100644 index 0000000..cfaa503 --- /dev/null +++ b/yunq/rust/Cargo.lock @@ -0,0 +1,267 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "anstream" +version = "0.6.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "418c75fa768af9c03be99d17643f93f79bbba589895012a80e3452a19ddda15b" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "is_terminal_polyfill", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "038dfcf04a5feb68e9c60b21c9625a54c2c0616e79b72b0fd87075a056ae1d1b" + +[[package]] +name = "anstyle-parse" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c03a11a9034d92058ceb6ee011ce58af4a9bf61491aa7e1e59ecd24bd40d22d4" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad186efb764318d35165f1758e7dcef3b10628e26d41a44bc5550652e6804391" +dependencies = [ + "windows-sys", +] + +[[package]] +name = "anstyle-wincon" +version = "3.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61a38449feb7068f52bb06c12759005cf459ee52bb4adc1d5a7c4322d716fb19" +dependencies = [ + "anstyle", + "windows-sys", +] + +[[package]] +name = "clap" +version = "4.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5db83dced34638ad474f39f250d7fea9598bdd239eaced1bdf45d597da0f433f" +dependencies = [ + "clap_builder", + "clap_derive", +] + +[[package]] +name = "clap_builder" +version = "4.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7e204572485eb3fbf28f871612191521df159bc3e15a9f5064c66dba3a8c05f" +dependencies = [ + "anstream", + "anstyle", + "clap_lex", + "strsim", +] + +[[package]] +name = "clap_derive" +version = "4.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c780290ccf4fb26629baa7a1081e68ced113f1d3ec302fa5948f1c381ebf06c6" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "clap_lex" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b82cf0babdbd58558212896d1a4272303a57bdb245c2bf1147185fb45640e70" + +[[package]] +name = "colorchoice" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b6a852b24ab71dffc585bcb46eaf7959d175cb865a7152e35b348d1b2960422" + +[[package]] +name = "convert_case" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec182b0ca2f35d8fc196cf3404988fd8b8c739a4d270ff118a398feb0cbec1ca" +dependencies = [ + "unicode-segmentation", +] + +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + +[[package]] +name = "is_terminal_polyfill" +version = "1.70.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8478577c03552c21db0e2724ffb8986a5ce7af88107e6be5d2ee6e158c12800" + +[[package]] +name = "prettyplease" +version = "0.2.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f12335488a2f3b0a83b14edad48dca9879ce89b2edd10e80237e4e852dd645e" +dependencies = [ + "proc-macro2", + "syn", +] + +[[package]] +name = "proc-macro2" +version = "1.0.85" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22244ce15aa966053a896d1accb3a6e68469b97c7f33f284b99f0d576879fc23" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "strsim" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + +[[package]] +name = "syn" +version = "2.0.72" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc4b9b9bf2add8093d3f2c0204471e951b2285580335de42f9d2534f3ae7a8af" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "unicode-ident" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" + +[[package]] +name = "unicode-segmentation" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4c87d22b6e3f4a18d4d40ef354e97c90fcb14dd91d7dc0aa9d8a1172ebf7202" + +[[package]] +name = "utf8parse" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_gnullvm", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88ba073cf16d5372720ec942a8ccbf61626074c6d4dd2e745299726ce8b89670" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0" + +[[package]] +name = "yunqc" +version = "0.1.0" +dependencies = [ + "clap", + "convert_case", + "prettyplease", + "proc-macro2", + "quote", + "syn", +] diff --git a/yunq/rust/Cargo.toml b/yunq/rust/Cargo.toml new file mode 100644 index 0000000..a972f93 --- /dev/null +++ b/yunq/rust/Cargo.toml @@ -0,0 +1,20 @@ +[package] +name = "yunqc" +version = "0.1.0" +edition = "2021" + +[dependencies] +convert_case = "0.6.0" +prettyplease = "0.2.20" +proc-macro2 = { version = "1.0" } +quote = { version = "1.0" } +syn = "2.0.72" + +clap = { version = "4.5.7", features = ["derive"], optional = true} + +[features] +build-binary = ["clap"] + +[[bin]] +name = "yunqc" +required-features = ["build-binary"] diff --git a/yunq/rust/src/codegen.rs b/yunq/rust/src/codegen.rs new file mode 100644 index 0000000..9462f4b --- /dev/null +++ b/yunq/rust/src/codegen.rs @@ -0,0 +1,591 @@ +use crate::parser::{Decl, Interface, Message, Method, Type}; +use crate::parser::{Field, FieldType}; +use convert_case::{Case, Casing}; +use proc_macro2::Ident; +use proc_macro2::Span; +use proc_macro2::TokenStream; +use quote::quote; + +fn ident(s: &str) -> Ident { + Ident::new(s, Span::call_site()) +} + +fn to_path(field_type: &FieldType) -> TokenStream { + let rust_type = ident(&field_type.inner_type.rust_type()); + if field_type.repeated { + quote! { + Vec::<#rust_type> + } + } else { + quote! { + #rust_type + } + } +} + +fn serialize_field(field: &Field) -> proc_macro2::TokenStream { + let ind = field.number as usize; + let name = ident(&field.name); + if field.field_type.repeated { + match &field.field_type.inner_type { + Type::String => unimplemented!(), + Type::Capability => unimplemented!(), + Type::U64 => quote! { + { + let rep_offset = next_extension; + let rep_len = self.#name.len() as u32; + next_extension = yunq::message::serialize_repeated(buf, offset + next_extension as usize, &self.#name)? as u32; + + buf.write_at(yunq::message::field_offset(offset, #ind), rep_offset)?; + buf.write_at(yunq::message::field_offset(offset, #ind) + 4, rep_len)?; + } + }, + Type::I64 => unimplemented!(), + Type::Bytes => unimplemented!(), + Type::Message(_) => quote! { + { + let rep_offset = next_extension; + let rep_len = self.#name.len() as u32; + next_extension = yunq::message::serialize_repeated_message(buf, offset + next_extension as usize, &self.#name, caps)? as u32; + + buf.write_at(yunq::message::field_offset(offset, #ind), rep_offset)?; + buf.write_at(yunq::message::field_offset(offset, #ind) + 4, rep_len)?; + } + }, + } + } else { + match &field.field_type.inner_type { + Type::String => quote! { + { + let str_offset = next_extension; + let str_length = self.#name.len() as u32; + + buf.write_str_at(offset + next_extension as usize, &self.#name)?; + next_extension += str_length; + + buf.write_at(yunq::message::field_offset(offset, #ind), str_offset)?; + buf.write_at(yunq::message::field_offset(offset, #ind) + 4, str_length)?; + } + }, + Type::Capability => quote! { + { + let cap_ind = caps.len(); + caps.push(self.#name); + + buf.write_at(yunq::message::field_offset(offset, #ind), cap_ind as u64)?; + } + }, + Type::U64 => quote! { + { + buf.write_at(yunq::message::field_offset(offset, #ind), self.#name)?; + } + }, + Type::I64 => quote! { + { + buf.write_at(yunq::message::field_offset(offset, #ind), self.#name)?; + } + }, + Type::Message(_) => quote! { + { + let msg_offset = next_extension; + let msg_length = self.#name.serialize(buf, offset + next_extension as usize, caps)? as u32; + next_extension += msg_length; + + buf.write_at(yunq::message::field_offset(offset, #ind), msg_offset)?; + buf.write_at(yunq::message::field_offset(offset, #ind) + 4, msg_length)?; + } + }, + Type::Bytes => unimplemented!(), + } + } +} + +fn parse_field(field: &Field) -> TokenStream { + let ind = field.number as usize; + let name = ident(&field.name); + if field.field_type.repeated { + match &field.field_type.inner_type { + Type::String => unimplemented!(), + Type::Capability => unimplemented!(), + Type::U64 => quote! { + let #name = { + let rep_offset = buf.at::(yunq::message::field_offset(offset, #ind))?; + let rep_len = buf.at::(yunq::message::field_offset(offset, #ind) + 4)?; + + yunq::message::parse_repeated(buf, offset + rep_offset as usize, rep_len as usize)? + }; + }, + Type::I64 => unimplemented!(), + Type::Bytes => unimplemented!(), + Type::Message(_s) => quote! { + let #name = { + let rep_offset = buf.at::(yunq::message::field_offset(offset, #ind))?; + let rep_len = buf.at::(yunq::message::field_offset(offset, #ind) + 4)?; + + yunq::message::parse_repeated_message(buf, offset + rep_offset as usize, rep_len as usize, caps)? + }; + }, + } + } else { + match &field.field_type.inner_type { + Type::String => quote! { + let #name = { + let str_offset = buf.at::(yunq::message::field_offset(offset, #ind))?; + let str_length = buf.at::(yunq::message::field_offset(offset, #ind) + 4)?; + + buf.str_at(offset + str_offset as usize, str_length as usize)?.to_string() + }; + }, + Type::Capability => quote! { + let #name = { + let cap_ind = buf.at::(yunq::message::field_offset(offset, #ind))?; + caps[cap_ind as usize] + }; + }, + Type::U64 => quote! { + let #name = buf.at::(yunq::message::field_offset(offset, #ind))?; + }, + Type::I64 => quote! { + let #name = buf.at::(yunq::message::field_offset(offset, #ind))?; + }, + Type::Bytes => { + unimplemented!(); + } + Type::Message(s) => { + let m_type = ident(s); + quote! { + let #name = { + let msg_offset = buf.at::(yunq::message::field_offset(offset, #ind))? as usize; + + #m_type::parse(buf, offset + msg_offset, caps)? + }; + } + } + } + } +} + +fn generate_serialize(message: &Message) -> TokenStream { + let num_fields = message.fields.len(); + let serializers = message.fields.iter().map(serialize_field); + quote! { + #[allow(unused_variables)] // caps may be unused. + fn serialize( + &self, + buf: &mut yunq::ByteBuffer, + offset: usize, + caps: &mut Vec, + ) -> Result { + let num_fields = #num_fields; + let core_size: u32 = (yunq::message::MESSAGE_HEADER_SIZE + 8 * num_fields) as u32; + #[allow(unused_mut)] + let mut next_extension = core_size; + + #(#serializers)* + + buf.write_at(offset, yunq::message::MESSAGE_IDENT)?; + buf.write_at(offset + 4, core_size)?; + buf.write_at(offset + 8, next_extension)?; + buf.write_at(offset + 12, 0_u32)?; + Ok(next_extension as usize) + } + } +} + +fn generate_parse(message: &Message) -> TokenStream { + let field_names = message.fields.iter().map(|field| ident(&field.name)); + let parsers = message.fields.iter().map(parse_field); + quote! { + #[allow(unused_variables)] // caps may be unused. + fn parse( + buf: &yunq::ByteBuffer, + offset: usize, + caps: &[z_cap_t], + ) -> Result + where + Self: Sized, + { + if buf.at::(offset)? != yunq::message::MESSAGE_IDENT { + mammoth::debug!("Expected IDENT at offest {:#x}, got {:#x}", offset, buf.at::(offset)?); + return Err(ZError::INVALID_ARGUMENT); + } + // TODO: Parse core size. + // TODO: Parse extension size. + // TODO: Check CRC32 + // TODO: Parse options. + + #(#parsers)* + + Ok(Self { + #(#field_names),* + }) + + } + } +} + +fn generate_message(message: &Message) -> TokenStream { + let name = ident(&message.name); + let field_names = message.fields.iter().map(|f| ident(&f.name)); + let field_types = message.fields.iter().map(|f| to_path(&f.field_type)); + + let serialize = generate_serialize(message); + let parse = generate_parse(message); + quote! { + #[derive(PartialEq, Eq, Clone, Debug)] + pub struct #name { + #(pub #field_names: #field_types),* + } + + impl YunqMessage for #name { + #serialize + + #parse + } + } +} + +fn generate_method(method: &Method) -> TokenStream { + let id = proc_macro2::Literal::u64_suffixed(method.number); + let name = ident(&method.name.to_case(Case::Snake)); + let maybe_req = method.request.clone().map(|r| ident(&r)); + let maybe_resp = method.response.clone().map(|r| ident(&r)); + match (maybe_req, maybe_resp) { + (Some(req), Some(resp)) => quote! { + pub fn #name (&mut self, req: & #req) -> Result<#resp, ZError> { + yunq::client::call_endpoint(#id, req, &mut self.byte_buffer, &self.endpoint_cap) + } + }, + (Some(req), None) => quote! { + pub fn #name (&mut self, req: & #req) -> Result { + yunq::client::call_endpoint(#id, req, &mut self.byte_buffer, &self.endpoint_cap) + } + }, + (None, Some(resp)) => quote! { + pub fn #name (&mut self) -> Result<#resp, ZError> { + yunq::client::call_endpoint(#id, &yunq::message::Empty{}, &mut self.byte_buffer, &self.endpoint_cap) + } + }, + _ => unreachable!(), + } +} + +fn generate_client(interface: &Interface) -> TokenStream { + let client_name = interface.name.clone() + "Client"; + let name = ident(&client_name); + let methods = interface.methods.iter().map(generate_method); + quote! { + pub struct #name { + endpoint_cap: Capability, + byte_buffer: ByteBuffer<0x1000>, + } + + impl #name { + pub fn new(endpoint_cap: Capability) -> Self { + Self { + endpoint_cap, + byte_buffer: ByteBuffer::new(), + } + } + #(#methods)* + } + + } +} + +fn generate_server_case(method: &Method) -> TokenStream { + let id = proc_macro2::Literal::u64_suffixed(method.number); + let name = ident(&method.name.to_case(Case::Snake)); + let maybe_req = method.request.clone().map(|r| ident(&r)); + let maybe_resp = method.response.clone().map(|r| ident(&r)); + match (maybe_req, maybe_resp) { + (Some(req), Some(_)) => quote! { + #id => { + let req = #req::parse_from_request(byte_buffer, cap_buffer)?; + let resp = self.handler.#name(req)?; + cap_buffer.clear(); + let resp_len = resp.serialize_as_request(0, byte_buffer, cap_buffer)?; + Ok(resp_len) + }, + }, + (Some(req), None) => quote! { + #id => { + let req = #req::parse_from_request(byte_buffer, cap_buffer)?; + self.handler.#name(req)?; + cap_buffer.clear(); + // TODO: Implement serialization for EmptyMessage so this is less hacky. + yunq::message::serialize_error(byte_buffer, ZError::from(0)); + Ok(0x10) + }, + }, + (None, Some(_)) => quote! { + #id => { + let resp = self.handler.#name()?; + cap_buffer.clear(); + let resp_len = resp.serialize_as_request(0, byte_buffer, cap_buffer)?; + Ok(resp_len) + }, + }, + _ => unreachable!(), + } +} + +fn generate_server_method(method: &Method) -> TokenStream { + let name = ident(&method.name.to_case(Case::Snake)); + let maybe_req = method.request.clone().map(|r| ident(&r)); + let maybe_resp = method.response.clone().map(|r| ident(&r)); + match (maybe_req, maybe_resp) { + (Some(req), Some(resp)) => quote! { + fn #name (&mut self, req: #req) -> Result<#resp, ZError>; + }, + (Some(req), None) => quote! { + fn #name (&mut self, req: #req) -> Result<(), ZError>; + }, + (None, Some(resp)) => quote! { + fn #name (&mut self) -> Result<#resp, ZError>; + }, + _ => unreachable!(), + } +} + +fn generate_server(interface: &Interface) -> TokenStream { + let server_name = ident(&(interface.name.clone() + "Server")); + let server_trait = ident(&(interface.name.clone() + "ServerHandler")); + let server_trait_methods = interface.methods.iter().map(generate_server_method); + let server_match_cases = interface.methods.iter().map(generate_server_case); + quote! { + pub trait #server_trait { + #(#server_trait_methods)* + } + + pub struct #server_name { + endpoint_cap: Capability, + handler: T + } + + impl #server_name { + pub fn new(handler: T) -> Result { + Ok(Self { + endpoint_cap: syscall::endpoint_create()?, + handler, + }) + } + } + + impl yunq::server::YunqServer for #server_name { + fn endpoint_cap(&self) -> &Capability { + &self.endpoint_cap + } + + fn handle_request( + &mut self, + method_number: u64, + byte_buffer: &mut ByteBuffer<1024>, + cap_buffer: &mut Vec, + ) -> Result { + match method_number { + #(#server_match_cases)* + + _ => Err(ZError::UNIMPLEMENTED) + } + } + } + } +} + +fn generate_async_server_case(method: &Method) -> TokenStream { + let id = proc_macro2::Literal::u64_suffixed(method.number); + let name = ident(&method.name.to_case(Case::Snake)); + let maybe_req = method.request.clone().map(|r| ident(&r)); + let maybe_resp = method.response.clone().map(|r| ident(&r)); + match (maybe_req, maybe_resp) { + (Some(req), Some(_)) => quote! { + #id => { + let req = #req::parse_from_request(byte_buffer, cap_buffer)?; + let resp = self.handler.#name(req).await?; + cap_buffer.clear(); + let resp_len = resp.serialize_as_request(0, byte_buffer, cap_buffer)?; + Ok(resp_len) + }, + }, + (Some(req), None) => quote! { + #id => { + let req = #req::parse_from_request(byte_buffer, cap_buffer)?; + self.handler.#name(req).await?; + cap_buffer.clear(); + // TODO: Implement serialization for EmptyMessage so this is less hacky. + yunq::message::serialize_error(byte_buffer, ZError::from(0)); + Ok(0x10) + }, + }, + (None, Some(_)) => quote! { + #id => { + let resp = self.handler.#name().await?; + cap_buffer.clear(); + let resp_len = resp.serialize_as_request(0, byte_buffer, cap_buffer)?; + Ok(resp_len) + }, + }, + _ => unreachable!(), + } +} + +fn generate_async_server_method(method: &Method) -> TokenStream { + let name = ident(&method.name.to_case(Case::Snake)); + let maybe_req = method.request.clone().map(|r| ident(&r)); + let maybe_resp = method.response.clone().map(|r| ident(&r)); + match (maybe_req, maybe_resp) { + (Some(req), Some(resp)) => quote! { + fn #name (&self, req: #req) -> impl Future> + Sync + Send; + }, + (Some(req), None) => quote! { + fn #name (&self, req: #req) -> impl Future> + Sync + Send; + }, + (None, Some(resp)) => quote! { + fn #name (&self) -> impl Future> + Sync + Send; + }, + _ => unreachable!(), + } +} + +fn generate_async_server(interface: &Interface) -> TokenStream { + let server_name = ident(&(String::from("Async") + &interface.name.clone() + "Server")); + let server_trait = ident(&(String::from("Async") + &interface.name.clone() + "ServerHandler")); + let server_trait_methods = interface.methods.iter().map(generate_async_server_method); + let server_match_cases = interface.methods.iter().map(generate_async_server_case); + quote! { + pub trait #server_trait { + #(#server_trait_methods)* + } + + pub struct #server_name { + endpoint_cap: Capability, + handler: T + } + + impl #server_name { + pub fn new(handler: T) -> Result { + Ok(Self { + endpoint_cap: syscall::endpoint_create()?, + handler, + }) + } + } + + impl yunq::server::AsyncYunqServer for #server_name { + fn endpoint_cap(&self) -> &Capability { + &self.endpoint_cap + } + + async fn handle_request( + &self, + method_number: u64, + byte_buffer: &mut ByteBuffer<1024>, + cap_buffer: &mut Vec, + ) -> Result { + match method_number { + #(#server_match_cases)* + + _ => Err(ZError::UNIMPLEMENTED) + } + } + } + } +} + +fn generate_interface(interface: &Interface) -> TokenStream { + let client = generate_client(interface); + let server = generate_server(interface); + let async_server = generate_async_server(interface); + quote! { + #client + + #server + + #async_server + } +} + +fn any_strings(ast: &[Decl]) -> bool { + ast.iter() + .filter_map(|decl| match decl { + Decl::Message(m) => Some(m), + _ => None, + }) + .flat_map(|message| message.fields.iter()) + .any(|field| field.field_type.inner_type == Type::String) +} + +fn any_interfaces(ast: &[Decl]) -> bool { + ast.iter().any(|decl| matches!(decl, Decl::Interface(_))) +} + +pub fn generate_code(ast: &[Decl]) -> String { + let str_imports = if any_strings(ast) { + quote! { + use alloc::string::String; + use alloc::string::ToString; + } + } else { + quote! {} + }; + + let interface_imports = if any_interfaces(ast) { + quote! { + use core::future::Future; + use mammoth::cap::Capability; + use mammoth::syscall; + } + } else { + quote! {} + }; + + let prelude = quote! { + + extern crate alloc; + + use alloc::vec::Vec; + use mammoth::zion::z_cap_t; + use mammoth::zion::ZError; + #[allow(unused_imports)] + use yunq::ByteBuffer; + use yunq::YunqMessage; + + #str_imports + + #interface_imports + + }; + + let message_decls = ast + .iter() + .filter_map(|decl| match decl { + Decl::Message(m) => Some(m), + _ => None, + }) + .map(generate_message); + + let interface_decls = ast + .iter() + .filter_map(|decl| match decl { + Decl::Interface(i) => Some(i), + _ => None, + }) + .map(generate_interface); + + let output = quote! { + #prelude + + #(#message_decls)* + + #(#interface_decls)* + } + .to_string(); + + let tokens = syn::parse_file(&output).unwrap_or_else(|e| { + println!("{}", output); + panic!("{}", e); + }); + + prettyplease::unparse(&tokens) +} diff --git a/yunq/rust/src/lexer.rs b/yunq/rust/src/lexer.rs new file mode 100644 index 0000000..666c1f5 --- /dev/null +++ b/yunq/rust/src/lexer.rs @@ -0,0 +1,226 @@ +#[derive(Debug, PartialEq)] +pub enum TokenType { + EndOfFile, + Name, + LeftBrace, + RightBrace, + LeftParen, + RightParen, + Arrow, + Semicolon, + Dot, + Equals, +} + +#[derive(Debug)] +#[allow(dead_code)] +pub struct Token { + pub token_type: TokenType, + line: usize, + start: usize, + end: usize, + pub chars: String, +} + +impl std::fmt::Display for Token { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!( + f, + "'{}' at line {} position {} ", + self.chars, self.line, self.start + ) + } +} + +pub fn lex_input(input: &str) -> Result, String> { + let mut index = 0; + let mut position = 0; + let mut line = 1; + + let mut tokens = Vec::new(); + + let mut chars = input.chars().peekable(); + loop { + match chars.next() { + Some(c) => { + position += 1; + match c { + '\n' => { + position = 0; + line += 1; + } + ' ' | '\t' | '\r' => {} + '/' => { + match chars.next() { + Some('/') => { + index += 1; + // TODO: Add a consume until function. + loop { + match chars.next() { + Some('\n') => { + index += 1; + position = 0; + line += 1; + break; + } + Some(_) => { + index += 1; + } + None => break, + + } + + } + } + _ => { + return Err(format!("Unexpected token '/' at line {} position {}", line, position)); + } + + } + } + '{' => tokens.push(Token{ + token_type: TokenType::LeftBrace, + line, + start: position, + end: position + 1, + chars: input[index..index+1].to_string(), + + }), + '}' => tokens.push(Token{ + token_type: TokenType::RightBrace, + line, + start: position, + end: position + 1, + chars: input[index..index+1].to_string(), + + }), + '(' => tokens.push(Token{ + token_type: TokenType::LeftParen, + line, + start: position, + end: position + 1, + chars: input[index..index+1].to_string(), + + }), + ')' => tokens.push(Token{ + token_type: TokenType::RightParen, + line, + start: position, + end: position + 1, + chars: input[index..index+1].to_string(), + + }), + ';' => tokens.push(Token{ + token_type: TokenType::Semicolon, + line, + start: position, + end: position + 1, + chars: input[index..index+1].to_string(), + + }), + '.' => tokens.push(Token{ + token_type: TokenType::Dot, + line, + start: position, + end: position + 1, + chars: input[index..index+1].to_string(), + + }), + '=' => tokens.push(Token{ + token_type: TokenType::Equals, + line, + start: position, + end: position + 1, + chars: input[index..index+1].to_string(), + + }), + '-' => match chars.next() { + Some('>') => { + position += 1; + tokens.push(Token{ + token_type: TokenType::Arrow, + line, + start: position - 1, + end: position + 1, + chars: input[index..index+1].to_string(), + }); + index += 1; + } + Some(c) => return Err(format!("Expected > to follow - (to form arrow '->') on line {} at position {}. But got '{}'", line, position + 1, c)), + None => return Err(format!("Expected > to follow - but got end of input instead on line {} position {}", line, position)) + }, + 'a'..='z' | 'A'..='Z' => { + let name_start = index; + let pos_start = position; + loop { + match chars.peek() { + Some(c) => { + match c { + 'a'..='z' | 'A'..='Z' | '0'..='9' | '_' => { + position += 1; + chars.next(); + index += 1; + }, + '\n' => { + chars.next(); + tokens.push(Token{ + token_type: TokenType::Name, + line, + start: pos_start, + end: position + 1, + chars: input[name_start..index+1].to_string(), + }); + position = 0; + index += 1; + line += 1; + break; + }, + _ => { + tokens.push(Token{ + token_type: TokenType::Name, + line, + start: pos_start, + end: position + 1, + chars: input[name_start..index+1].to_string() + }); + break; + }, + } + } + None => { + tokens.push(Token{ + token_type: TokenType::Name, + line, + start: pos_start, + end: position + 1, + chars: input[name_start..index].to_string(), + }); + break; + } + } + } + }, + _ => { + return Err(format!( + "Unexpected token on line {} character {}: {}", + line, position, c + )) + } + } + } + None => { + tokens.push(Token { + token_type: TokenType::EndOfFile, + line, + start: position, + end: position, + chars: "EOF".to_string(), + }); + break; + } + } + index += 1; + } + + Ok(tokens) +} diff --git a/yunq/rust/src/lib.rs b/yunq/rust/src/lib.rs new file mode 100644 index 0000000..07ecdb9 --- /dev/null +++ b/yunq/rust/src/lib.rs @@ -0,0 +1,15 @@ +mod codegen; +mod lexer; +mod parser; + +use std::error::Error; + +pub fn codegen(input: &str) -> Result> { + let tokens = lexer::lex_input(input)?; + + let mut ast_parser = parser::Parser::new(&tokens); + ast_parser.parse_ast()?; + ast_parser.type_check()?; + + Ok(codegen::generate_code(ast_parser.ast())) +} diff --git a/yunq/rust/src/main.rs b/yunq/rust/src/main.rs new file mode 100644 index 0000000..a835985 --- /dev/null +++ b/yunq/rust/src/main.rs @@ -0,0 +1,27 @@ +use std::error::Error; +use std::fs; + +use clap::Parser; + +#[derive(Parser)] +#[command(about)] +struct Args { + // The .yunq file to parse + #[arg(short, long)] + input_path: String, + + // The .rs file to generate + #[arg(short, long)] + output_path: String, +} + +fn main() -> Result<(), Box> { + let args = Args::parse(); + let input = fs::read_to_string(args.input_path)?; + + let code = yunqc::codegen(&input)?; + + fs::write(args.output_path, code)?; + + Ok(()) +} diff --git a/yunq/rust/src/parser.rs b/yunq/rust/src/parser.rs new file mode 100644 index 0000000..3ea7e2b --- /dev/null +++ b/yunq/rust/src/parser.rs @@ -0,0 +1,424 @@ +use std::collections::HashMap; +use std::collections::HashSet; +use std::fmt::Debug; +use std::fmt::Display; + +use crate::lexer::Token; +use crate::lexer::TokenType; + +#[derive(Clone, PartialEq)] +pub enum Type { + U64, + I64, + String, + Bytes, + Capability, + Message(String), +} + +impl Type { + pub fn rust_type(&self) -> String { + match self { + Type::U64 => "u64".to_string(), + Type::I64 => "i64".to_string(), + Type::String => "String".to_string(), + Type::Bytes => "Vec".to_string(), + Type::Capability => "z_cap_t".to_string(), + Type::Message(s) => s.clone(), + } + } +} + +#[derive(Clone)] +pub struct FieldType { + pub repeated: bool, + pub inner_type: Type, +} + +impl Display for FieldType { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + if self.repeated { + write!(f, "repeated ")?; + } + write!( + f, + "{}", + match &self.inner_type { + Type::U64 => "u64", + Type::I64 => "i64", + Type::String => "string", + Type::Bytes => "bytes", + Type::Capability => "cap", + Type::Message(s) => s, + } + ) + } +} + +impl TryFrom<&String> for Type { + type Error = String; + + fn try_from(value: &String) -> Result { + match value.as_str() { + "u64" => Ok(Type::U64), + "i64" => Ok(Type::I64), + "string" => Ok(Type::String), + "bytes" => Ok(Type::Bytes), + "capability" => Ok(Type::Capability), + _ => Ok(Type::Message(value.clone())), + } + } +} + +#[derive(Clone)] +pub struct Field { + pub field_type: FieldType, + pub name: String, + pub number: u64, +} + +#[derive(Clone)] +pub struct Message { + pub name: String, + pub fields: Vec, +} + +pub struct Method { + pub name: String, + pub number: u64, + pub request: Option, + pub response: Option, +} + +impl Debug for Method { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let request_str = match &self.request { + Some(s) => format!("({})", s), + None => "()".to_string(), + }; + let response_str = match &self.response { + Some(s) => format!("({})", s), + None => "()".to_string(), + }; + + write!( + f, + "{}: {}: {} -> {}", + self.number, self.name, request_str, response_str + ) + } +} + +pub struct Interface { + pub name: String, + pub methods: Vec, +} + +pub enum Decl { + Message(Message), + Interface(Interface), + Package(Vec), +} + +impl Debug for Decl { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Decl::Message(m) => { + writeln!(f, "Message {}", m.name)?; + for field in &m.fields { + writeln!( + f, + "\t{}: {} ({})", + field.number, field.name, field.field_type + )?; + } + } + Decl::Interface(i) => { + writeln!(f, "Interface {}", i.name)?; + for method in &i.methods { + writeln!(f, "\t{:?}", method)?; + } + } + Decl::Package(p) => { + writeln!(f, "Package {};", p.join("."))?; + } + } + Ok(()) + } +} + +pub struct Parser<'a> { + tokens: &'a Vec, + current_index: usize, + ast: Vec, + type_map: HashMap, +} + +impl<'a> Parser<'a> { + pub fn new(tokens: &'a Vec) -> Self { + Self { + tokens, + current_index: 0, + ast: Vec::new(), + type_map: HashMap::new(), + } + } + + fn next_type_is(&self, tok_type: TokenType) -> bool { + self.current_index < self.tokens.len() + && self.tokens[self.current_index].token_type == tok_type + } + + fn consume_token(&mut self) -> &Token { + if self.current_index >= self.tokens.len() { + panic!("Consumed tokens past end of input.") + } + + let t = &self.tokens[self.current_index]; + self.current_index += 1; + t + } + + fn consume_token_type(&mut self, t: TokenType) -> Result<&Token, String> { + let token = self.consume_token(); + if token.token_type == t { + Ok(token) + } else { + Err(format!("Expected {:?} but found {}", t, token)) + } + } + + fn consume_identifier(&mut self) -> Result<&Token, String> { + self.consume_token_type(TokenType::Name) + } + + fn consume_keyword<'b>(&'b mut self, keyword: &str) -> Result<&'b Token, String> { + let token = self.consume_token_type(TokenType::Name)?; + if token.chars != keyword { + Err(format!("Expected '{}' but got {}", keyword, token)) + } else { + Ok(token) + } + } + + fn package(&mut self) -> Result { + // 'package' was consumed by decl. + let mut package_names = vec![self.consume_identifier()?.chars.clone()]; + while self.next_type_is(TokenType::Dot) { + self.consume_token_type(TokenType::Dot)?; + package_names.push(self.consume_identifier()?.chars.clone()); + } + self.consume_token_type(TokenType::Semicolon)?; + Ok(Decl::Package(package_names)) + } + + fn field(&mut self, number: u64) -> Result { + let mut type_identifier = self.consume_identifier()?; + let mut repeated = false; + if type_identifier.chars == "repeated" { + repeated = true; + type_identifier = self.consume_identifier()?; + } + + let parsed_type = Type::try_from(&type_identifier.chars) + .map_err(|err| format!("{}: {}", err, type_identifier))?; + let name_identifier = self.consume_identifier()?.chars.clone(); + + self.consume_token_type(TokenType::Semicolon)?; + + Ok(Field { + field_type: FieldType { + inner_type: parsed_type, + repeated, + }, + name: name_identifier, + number, + }) + } + + fn message(&mut self) -> Result { + // 'message' was consumed by decl. + let name = self.consume_identifier()?.chars.clone(); + + self.consume_token_type(TokenType::LeftBrace)?; + + let mut fields = Vec::new(); + let mut next_field_number = 0; + while !self.next_type_is(TokenType::RightBrace) { + fields.push(self.field(next_field_number)?); + next_field_number += 1; + } + + self.consume_token_type(TokenType::RightBrace)?; + + Ok(Decl::Message(Message { name, fields })) + } + + fn method(&mut self, number: u64) -> Result { + self.consume_keyword("method")?; + + let name = self.consume_identifier()?.chars.clone(); + + self.consume_token_type(TokenType::LeftParen)?; + let request = if self.next_type_is(TokenType::Name) { + Some(self.consume_identifier()?.chars.clone()) + } else { + None + }; + self.consume_token_type(TokenType::RightParen)?; + + self.consume_token_type(TokenType::Arrow)?; + + self.consume_token_type(TokenType::LeftParen)?; + let response = if self.next_type_is(TokenType::Name) { + Some(self.consume_identifier()?.chars.clone()) + } else { + None + }; + self.consume_token_type(TokenType::RightParen)?; + + self.consume_token_type(TokenType::Semicolon)?; + + Ok(Method { + name, + request, + response, + number, + }) + } + + fn interface(&mut self) -> Result { + let name = self.consume_identifier()?.chars.clone(); + + self.consume_token_type(TokenType::LeftBrace)?; + + let mut methods = Vec::new(); + let mut next_method_number = 0; + while !self.next_type_is(TokenType::RightBrace) { + methods.push(self.method(next_method_number)?); + next_method_number += 1; + } + + self.consume_token_type(TokenType::RightBrace)?; + + Ok(Decl::Interface(Interface { name, methods })) + } + + fn decl(&mut self) -> Result { + let token = self.consume_identifier()?; + match token.chars.as_str() { + "package" => self.package(), + "message" => self.message(), + "interface" => self.interface(), + _ => Err(format!( + "Expected one of 'package', 'message', or 'interface' but got {}", + token + )), + } + } + + pub fn parse_ast(&mut self) -> Result<(), String> { + while !self.next_type_is(TokenType::EndOfFile) { + let decl = self.decl()?; + self.ast.push(decl); + } + Ok(()) + } + + pub fn ast(&'a self) -> &'a Vec { + &self.ast + } + + fn ensure_message_and_interface_names_unique(&mut self) -> Result<(), String> { + let mut names = HashSet::new(); + for decl in &self.ast { + match decl { + Decl::Message(m) => { + if names.contains(&m.name) { + // TODO: Keep token information for a better error message here. + return Err(format!("Duplicate name {}", m.name)); + }; + names.insert(m.name.clone()); + self.type_map.insert(m.name.clone(), m.clone()); + } + Decl::Interface(i) => { + if names.contains(&i.name) { + // TODO: Keep token information for a better error message here. + return Err(format!("Duplicate name {}", i.name)); + }; + names.insert(i.name.clone()); + } + _ => {} + } + } + Ok(()) + } + + fn type_check_message(&self, message: &Message) -> Result<(), String> { + let mut field_names = HashSet::new(); + + for field in &message.fields { + if field_names.contains(&field.name) { + return Err(format!( + "Field name '{}' used twice in message '{}'", + field.name, message.name + )); + } + field_names.insert(field.name.clone()); + + if let Type::Message(name) = &field.field_type.inner_type { + if !self.type_map.contains_key(name) { + return Err(format!( + "Unknown type '{}' on field '{}' in message '{}'", + name, field.name, message.name + )); + } + } + } + Ok(()) + } + + fn type_check_interface(&self, interface: &Interface) -> Result<(), String> { + let mut method_names = HashSet::new(); + + for method in &interface.methods { + if method_names.contains(&method.name) { + return Err(format!( + "Method name '{}' used twice in interface '{}'", + method.name, interface.name + )); + } + method_names.insert(method.name.clone()); + + if let Some(name) = &method.request { + if !self.type_map.contains_key(name) { + return Err(format!( + "Unknown request type '{}' on method '{}' in interface '{}'", + name, method.name, interface.name + )); + } + } + if let Some(name) = &method.response { + if !self.type_map.contains_key(name) { + return Err(format!( + "Unknown response type '{}' on method '{}' in interface '{}'", + name, method.name, interface.name + )); + } + } + } + Ok(()) + } + + pub fn type_check(&mut self) -> Result<(), String> { + self.ensure_message_and_interface_names_unique()?; + for decl in &self.ast { + match decl { + Decl::Message(m) => self.type_check_message(m)?, + Decl::Interface(i) => self.type_check_interface(i)?, + _ => {} + } + } + Ok(()) + } +} diff --git a/yunq/server.cpp.jinja b/yunq/server.cpp.jinja index 6f8a712..bee46b5 100644 --- a/yunq/server.cpp.jinja +++ b/yunq/server.cpp.jinja @@ -103,11 +103,12 @@ glcr::Status {{interface.name}}ServerBase::HandleRequest(const glcr::ByteBuffer& switch(method_select) { {%- for method in interface.methods %} - case {{loop.index0}}: { + case {{method.number}}: { {% if method.request != None %} {{method.request}} yunq_request; - RETURN_ERROR(yunq_request.ParseFromBytes(request, kHeaderSize, req_caps)); + yunq::MessageView request_view(request, kHeaderSize); + RETURN_ERROR(yunq_request.ParseFromBytes(request_view, req_caps)); {% endif %} {% if method.response != None %} diff --git a/yunq/yunq.py b/yunq/yunq.py index c217e19..ad6e944 100755 --- a/yunq/yunq.py +++ b/yunq/yunq.py @@ -34,6 +34,7 @@ def main(): jinja_env = Environment(loader=FileSystemLoader(pathlib.Path(__file__).parent.resolve())) message_header_tmpl = jinja_env.get_template("message.h.jinja") + message_header_tmpl.globals['Type'] = Type with open(filename + '.h', mode='w') as f: message_header = message_header_tmpl.render(messages=messages, package=package) f.write(message_header) @@ -44,6 +45,9 @@ def main(): message_impl = message_impl_tmpl.render(file=filebase, messages=messages, package=package) f.write(message_impl) + if len(interfaces) == 0: + return + 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=filebase, interfaces=interfaces, package=package) diff --git a/zion/CMakeLists.txt b/zion/CMakeLists.txt index 2e63927..598a3de 100644 --- a/zion/CMakeLists.txt +++ b/zion/CMakeLists.txt @@ -102,4 +102,7 @@ target_include_directories(zion_stub set_target_properties(zion_stub PROPERTIES - COMPILE_FLAGS "${_Z_COMPILE_FLAGS}") + COMPILE_FLAGS "${BASE_COMPILE_FLAGS} -nostdlib -c" + LINK_FLAGS "${BASE_LINK_FLAGS} -nostartfiles -static -lgcc" + ) + diff --git a/zion/boot/limine.cfg b/zion/boot/limine.cfg deleted file mode 100644 index 0d3a965..0000000 --- a/zion/boot/limine.cfg +++ /dev/null @@ -1,11 +0,0 @@ -# Needs to be copied into the EFI partition of the drive. - -TIMEOUT=0 - -:AcadiaOS - PROTOCOL=limine - - KERNEL_PATH=boot:///zion - MODULE_PATH=boot:///sys/yellowstone - MODULE_PATH=boot:///sys/denali - MODULE_PATH=boot:///sys/victoriafalls diff --git a/zion/boot/limine.conf b/zion/boot/limine.conf new file mode 100644 index 0000000..718e5c3 --- /dev/null +++ b/zion/boot/limine.conf @@ -0,0 +1,11 @@ +# Needs to be copied into the EFI partition of the drive. + +timeout: 0 + +/AcadiaOS +protocol: limine + +kernel_path: boot():/zion +module_path: boot():/sys/yellowstone +module_path: boot():/sys/denali +module_path: boot():/sys/victoriafalls diff --git a/zion/capability/capability.h b/zion/capability/capability.h index 6a20183..96911c7 100644 --- a/zion/capability/capability.h +++ b/zion/capability/capability.h @@ -5,6 +5,7 @@ #include #include +#include "debug/debug.h" #include "include/ztypes.h" #include "object/kernel_object.h" @@ -42,7 +43,10 @@ class Capability : public glcr::RefCounted { template glcr::RefPtr Capability::obj() { if (obj_->TypeTag() != KernelObjectTag::type) { - return nullptr; + uint64_t type = KernelObjectTag::type; + dbgln("Mismatched type tag returning nullptr."); + dbgln("Expected {x} got {x}", type, obj_->TypeTag()); + panic("Unhandled obj type mismatch"); } return StaticCastRefPtr(obj_); } diff --git a/zion/debug/debug.cpp b/zion/debug/debug.cpp index a2929d9..7d10174 100644 --- a/zion/debug/debug.cpp +++ b/zion/debug/debug.cpp @@ -5,14 +5,18 @@ #define COM1 0x3f8 +#define BOCHS_DBG 1 + namespace { bool is_transmit_empty() { return (inb(COM1 + 5) & 0x20) != 0; } void dbgputchar(char c) { - while (!is_transmit_empty()) - ; + while (!is_transmit_empty()); outb(COM1, c); +#if BOCHS_DBG + outb(0xe9, c); +#endif } void dbg(const glcr::StringView& str) { @@ -39,3 +43,52 @@ void dbgln(const glcr::StringView& str) { dbg(str); dbg("\n"); } + +namespace { + +// Helper function to write a byte to a specified COM1 register offset +void write_serial_port(uint16_t offset, uint8_t value) { + outb(COM1 + offset, value); +} + +// Helper function to read a byte from a specified COM1 register offset +uint8_t read_serial_port(uint16_t offset) { return inb(COM1 + offset); } + +} // namespace + +namespace serial { + +// Function to initialize the serial port with a given baud rate +void initialize(uint32_t baud_rate) { + // Disable interrupts + write_serial_port(1, 0x00); + + // Enable DLAB (Divisor Latch Access Bit) in Line Control Register (LCR) + write_serial_port(3, read_serial_port(3) | 0x80); + + // Calculate the divisor + // Baud rate = 115200 / divisor (approximately) + uint16_t divisor = 115200 / baud_rate; + + // Set the low byte of the divisor + write_serial_port(0, divisor & 0xFF); + + // Set the high byte of the divisor + write_serial_port(1, (divisor >> 8) & 0xFF); + + // Clear DLAB and set data bits, stop bits, and parity + // 8 data bits, 1 stop bit, no parity + write_serial_port(3, 0x03); // 00000011b + + // Enable FIFO, clear buffers, set trigger level (e.g., 1 byte) + write_serial_port(2, 0xC7); // 11000111b + + // Enable IRQs (optional, for interrupt-driven communication) + // write_serial_port(1, 0x01); + + // Set Modem Control Register (MCR) + // Enable RTS, DTR, OUT1, OUT2, loopback off, IRQs enabled + write_serial_port(4, 0x0B); // 00001011b +} + +} // namespace serial diff --git a/zion/debug/debug.h b/zion/debug/debug.h index 63b0d0e..d3a6fd4 100644 --- a/zion/debug/debug.h +++ b/zion/debug/debug.h @@ -37,3 +37,9 @@ void panic(const char* str, Args&&... args) { #define UNREACHABLE \ panic("Unreachable {}, {}", glcr::StringView(__FILE__), __LINE__); \ __builtin_unreachable(); + +namespace serial { + +void initialize(uint32_t baud_rate); + +} diff --git a/zion/include/zcall.h b/zion/include/zcall.h index efe5d16..6092037 100644 --- a/zion/include/zcall.h +++ b/zion/include/zcall.h @@ -17,6 +17,7 @@ SYS4(ThreadStart, z_cap_t, thread_cap, uint64_t, entry, uint64_t, arg1, uint64_t, arg2); SYS0(ThreadExit); SYS1(ThreadWait, z_cap_t, thread_cap); +SYS1(ThreadSleep, uint64_t, millis); SYS5(AddressSpaceMap, z_cap_t, vmas_cap, uint64_t, vmas_offset, z_cap_t, vmmo_cap, uint64_t, align, uint64_t*, vaddr); @@ -47,7 +48,7 @@ SYS5(PortRecv, z_cap_t, port_cap, uint64_t*, num_bytes, void*, data, uint64_t*, SYS5(PortPoll, z_cap_t, port_cap, uint64_t*, num_bytes, void*, data, uint64_t*, num_caps, z_cap_t*, caps); -SYS2(IrqRegister, uint64_t, irq_num, z_cap_t*, port_cap); +SYS2(MsiIrqRegister, uint64_t*, irq_num, z_cap_t*, port_cap); SYS1(EndpointCreate, z_cap_t*, endpoint_cap); SYS6(EndpointSend, z_cap_t, endpoint_cap, uint64_t, num_bytes, const void*, @@ -55,7 +56,7 @@ SYS6(EndpointSend, z_cap_t, endpoint_cap, uint64_t, num_bytes, const void*, SYS6(EndpointRecv, z_cap_t, endpoint_cap, uint64_t*, num_bytes, void*, data, uint64_t*, num_caps, z_cap_t*, caps, z_cap_t*, reply_port_cap); SYS5(ReplyPortSend, z_cap_t, reply_port_cap, uint64_t, num_bytes, const void*, - data, uint64_t, num_caps, z_cap_t*, caps); + data, uint64_t, num_caps, const z_cap_t*, caps); SYS5(ReplyPortRecv, z_cap_t, reply_port_cap, uint64_t*, num_bytes, void*, data, uint64_t*, num_caps, z_cap_t*, caps); diff --git a/zion/include/ztypes.h b/zion/include/ztypes.h index f25df71..b60db9c 100644 --- a/zion/include/ztypes.h +++ b/zion/include/ztypes.h @@ -19,6 +19,7 @@ const uint64_t kZionThreadCreate = 0x10; const uint64_t kZionThreadStart = 0x11; const uint64_t kZionThreadExit = 0x12; const uint64_t kZionThreadWait = 0x13; +const uint64_t kZionThreadSleep = 0x14; // Memory Calls const uint64_t kZionAddressSpaceMap = 0x21; @@ -42,7 +43,7 @@ const uint64_t kZionPortSend = 0x51; const uint64_t kZionPortRecv = 0x52; const uint64_t kZionPortPoll = 0x53; -const uint64_t kZionIrqRegister = 0x58; +const uint64_t kZionMsiIrqRegister = 0x59; const uint64_t kZionEndpointCreate = 0x60; const uint64_t kZionEndpointSend = 0x61; diff --git a/zion/interrupt/apic.cpp b/zion/interrupt/apic.cpp index 927edfd..66d7e0c 100644 --- a/zion/interrupt/apic.cpp +++ b/zion/interrupt/apic.cpp @@ -136,23 +136,6 @@ Apic::Apic(const ApicConfiguration& config) // FIXME: Get this offset from ACPI. SetIoDoubleReg(0x14, 0x20 | APIC_MASK); - // Map Keyboard - SetIoDoubleReg(0x12, 0x22); - - // For now set these based on the presets in the following spec. - // http://web.archive.org/web/20161130153145/http://download.intel.com/design/chipsets/datashts/29056601.pdf - // FIXME: However in the future we should likely use the MADT for legacy - // interrupts and AML for PCI etc. - // PCI Line 1-4 - // FIXME: These should be level triggered according to spec I believe - // but because we handle the interrupt outside of the kernel it is tricky - // to wait to send the end of interrupt message. - // Because of this leave them as edge triggered and send EOI immediately. - SetIoDoubleReg(0x30, 0x30); - SetIoDoubleReg(0x32, 0x31); - SetIoDoubleReg(0x34, 0x32); - SetIoDoubleReg(0x36, 0x33); - DumpInfo(); } diff --git a/zion/interrupt/apic_timer.cpp b/zion/interrupt/apic_timer.cpp index 9fbbaf6..8a78329 100644 --- a/zion/interrupt/apic_timer.cpp +++ b/zion/interrupt/apic_timer.cpp @@ -4,7 +4,7 @@ #include "interrupt/apic.h" #include "interrupt/timer.h" -const uint32_t kScheduleFrequency = 20; +const uint32_t kScheduleFrequency = 100; ApicTimer* gApicTimer = nullptr; void ApicTimer::Init() { diff --git a/zion/interrupt/driver_manager.cpp b/zion/interrupt/driver_manager.cpp index 67e78fc..3aa2bc5 100644 --- a/zion/interrupt/driver_manager.cpp +++ b/zion/interrupt/driver_manager.cpp @@ -9,18 +9,24 @@ DriverManager& DriverManager::Get() { return *gDriverManager; } DriverManager::DriverManager() { gDriverManager = this; } void DriverManager::WriteMessage(uint64_t irq_num, IpcMessage&& message) { - if (!driver_map_.Contains(irq_num)) { + if (irq_num < IRQ_OFFSET) { + dbgln("WARN IRQ {x} below min offset {x}", irq_num, IRQ_OFFSET); + } + uint64_t offset = irq_num - IRQ_OFFSET; + if (offset >= driver_list_.size()) { dbgln("WARN IRQ for {x} with no registered driver", irq_num); return; } - driver_map_.at(irq_num)->Send(glcr::Move(message)); + driver_list_[offset]->Send(glcr::Move(message)); } -glcr::ErrorCode DriverManager::RegisterListener(uint64_t irq_num, - glcr::RefPtr port) { - if (driver_map_.Contains(irq_num)) { - return glcr::ALREADY_EXISTS; +glcr::ErrorOr DriverManager::RegisterListener( + glcr::RefPtr port) { + if (driver_list_.size() + IRQ_OFFSET >= 0xFF) { + return glcr::EXHAUSTED; } - return driver_map_.Insert(irq_num, port); + uint8_t offset = (driver_list_.size() + IRQ_OFFSET); + driver_list_.PushBack(port); + return offset; } diff --git a/zion/interrupt/driver_manager.h b/zion/interrupt/driver_manager.h index db205c8..3fb897f 100644 --- a/zion/interrupt/driver_manager.h +++ b/zion/interrupt/driver_manager.h @@ -15,9 +15,10 @@ class DriverManager { void WriteMessage(uint64_t irq_num, IpcMessage&& message); - [[nodiscard]] glcr::ErrorCode RegisterListener(uint64_t irq_num, - glcr::RefPtr port); + [[nodiscard]] glcr::ErrorOr RegisterListener( + glcr::RefPtr port); private: - glcr::HashMap> driver_map_; + const uint64_t IRQ_OFFSET = 0x60; + glcr::Vector> driver_list_; }; diff --git a/zion/interrupt/interrupt.cpp b/zion/interrupt/interrupt.cpp index 8b6c114..7858e88 100644 --- a/zion/interrupt/interrupt.cpp +++ b/zion/interrupt/interrupt.cpp @@ -109,11 +109,11 @@ extern "C" void interrupt_protection_fault(InterruptFrame* frame) { } else { dbgln("GDT"); } - dbgln("Index: {}", err >> 3); + dbgln("Index: {} ({x})", err >> 3, err >> 3); dbgln("RIP: {x}", frame->rip); dbgln("RAX: {x}, RBX: {x}, RCX: {x}, RDX: {x}", frame->rax, frame->rbx, frame->rcx, frame->rdx); - dbgln("RSP: {x}", frame->rsp); + dbgln("RSP: {x}, RBP: {x}", frame->rsp, frame->rbp); StackUnwind(frame->rbp); if (IsUserSpace(frame->rip)) { @@ -186,51 +186,38 @@ uint64_t cnt = 0; extern "C" void isr_apic_timer(); extern "C" void interrupt_apic_timer(InterruptFrame*) { cnt++; - if (cnt % 20 == 0) { + if (cnt % 100 == 0) { if (cnt == 20) { KernelHeap::DumpDebugData(); phys_mem::DumpPhysicalMemoryUsage(); } - dbgln("timer: {}s", cnt * 50 / 1000); + dbgln("timer: {}s", cnt * 10 / 1000); } gApic->SignalEOI(); gScheduler->Preempt(); } -extern "C" void isr_keyboard(); -extern "C" void interrupt_keyboard(InterruptFrame*) { - glcr::Array data(1); - data[0] = inb(0x60); - IpcMessage msg{.data = glcr::Move(data)}; - DriverManager::Get().WriteMessage(kZIrqKbd, glcr::Move(msg)); - +extern "C" void isr_60(); +extern "C" void interrupt_60(InterruptFrame*) { + DriverManager::Get().WriteMessage(0x60, {}); gApic->SignalEOI(); } -extern "C" void isr_pci1(); -extern "C" void interrupt_pci1(InterruptFrame*) { - DriverManager::Get().WriteMessage(kZIrqPci1, {}); +extern "C" void isr_61(); +extern "C" void interrupt_61(InterruptFrame*) { + DriverManager::Get().WriteMessage(0x61, {}); gApic->SignalEOI(); } -extern "C" void isr_pci2(); -extern "C" void interrupt_pci2(InterruptFrame*) { - DriverManager::Get().WriteMessage(kZIrqPci2, {}); - dbgln("Interrupt PCI line 2"); +extern "C" void isr_62(); +extern "C" void interrupt_62(InterruptFrame*) { + DriverManager::Get().WriteMessage(0x62, {}); gApic->SignalEOI(); } -extern "C" void isr_pci3(); -extern "C" void interrupt_pci3(InterruptFrame*) { - DriverManager::Get().WriteMessage(kZIrqPci3, {}); - dbgln("Interrupt PCI line 3"); - gApic->SignalEOI(); -} - -extern "C" void isr_pci4(); -extern "C" void interrupt_pci4(InterruptFrame*) { - DriverManager::Get().WriteMessage(kZIrqPci4, {}); - dbgln("Interrupt PCI line 4"); +extern "C" void isr_63(); +extern "C" void interrupt_63(InterruptFrame*) { + DriverManager::Get().WriteMessage(0x63, {}); gApic->SignalEOI(); } @@ -243,12 +230,11 @@ void InitIdt() { gIdt[0x20] = CreateDescriptor(isr_timer); gIdt[0x21] = CreateDescriptor(isr_apic_timer); - gIdt[0x22] = CreateDescriptor(isr_keyboard); - gIdt[0x30] = CreateDescriptor(isr_pci1); - gIdt[0x31] = CreateDescriptor(isr_pci2); - gIdt[0x32] = CreateDescriptor(isr_pci3); - gIdt[0x33] = CreateDescriptor(isr_pci4); + gIdt[0x60] = CreateDescriptor(isr_60); + gIdt[0x61] = CreateDescriptor(isr_61); + gIdt[0x62] = CreateDescriptor(isr_62); + gIdt[0x63] = CreateDescriptor(isr_63); InterruptDescriptorTablePointer idtp{ .size = sizeof(gIdt), diff --git a/zion/interrupt/interrupt_enter.s b/zion/interrupt/interrupt_enter.s index db02a22..167d631 100644 --- a/zion/interrupt/interrupt_enter.s +++ b/zion/interrupt/interrupt_enter.s @@ -63,10 +63,8 @@ isr_handler fpe_fault isr_handler timer isr_handler apic_timer -isr_handler keyboard - -isr_handler pci1 -isr_handler pci2 -isr_handler pci3 -isr_handler pci4 +isr_handler 60 +isr_handler 61 +isr_handler 62 +isr_handler 63 diff --git a/zion/lib/memory_mapping_tree.cpp b/zion/lib/memory_mapping_tree.cpp index 91259ff..d4b2dac 100644 --- a/zion/lib/memory_mapping_tree.cpp +++ b/zion/lib/memory_mapping_tree.cpp @@ -45,11 +45,15 @@ glcr::ErrorCode MemoryMappingTree::FreeMemoryRange(uint64_t vaddr_base, } auto predecessor_or = mapping_tree_.Predecessor(vaddr_base); if (predecessor_or && predecessor_or.value().get().vaddr_limit > vaddr_base) { + dbgln("Free memory Predecessor check failed: {x} > {x}", + predecessor_or.value().get().vaddr_limit, vaddr_base); return glcr::FAILED_PRECONDITION; } auto last_predecessor_or = mapping_tree_.Predecessor(vaddr_limit); if (last_predecessor_or && last_predecessor_or.value().get().vaddr_limit > vaddr_limit) { + dbgln("Free memory Last Predecessor check failed: {x} > {x}", + last_predecessor_or.value().get().vaddr_limit, vaddr_limit); return glcr::FAILED_PRECONDITION; } diff --git a/zion/loader/init_loader.cpp b/zion/loader/init_loader.cpp index 7e1f16a..b05c70a 100644 --- a/zion/loader/init_loader.cpp +++ b/zion/loader/init_loader.cpp @@ -13,7 +13,7 @@ #include "scheduler/process_manager.h" #include "scheduler/scheduler.h" -#define K_INIT_DEBUG 0 +#define K_INIT_DEBUG 1 namespace { @@ -67,6 +67,9 @@ uint64_t LoadElfProgram(Process& dest_proc, uint64_t base, uint64_t offset) { reinterpret_cast(base + header->phoff); for (uint64_t i = 0; i < header->phnum; i++) { Elf64ProgramHeader& program = programs[i]; + if (program.type != 1) { + continue; + } #if K_INIT_DEBUG dbgln( "prog: type: {}, flags: {}, offset: {}\n vaddr: {x}, paddr: {x}\n " @@ -74,12 +77,19 @@ uint64_t LoadElfProgram(Process& dest_proc, uint64_t base, uint64_t offset) { program.type, program.flags, program.offset, program.vaddr, program.paddr, program.filesz, program.memsz, program.align); #endif - auto mem_obj = glcr::MakeRefCounted(program.memsz); - mem_obj->CopyBytesToObject(base + program.offset, program.filesz); - PANIC_ON_ERR( - dest_proc.vmas()->MapInMemoryObject( - program.vaddr, glcr::StaticCastRefPtr(mem_obj)), - "Couldn't map in init program."); + uint64_t page_offset = program.vaddr & 0xFFF; + auto mem_obj = + glcr::MakeRefCounted(program.memsz + page_offset); + + // Super hacky but if we adjust the offsets to handle a non-aligned page. + mem_obj->CopyBytesToObject(base + program.offset - page_offset, + program.filesz + page_offset); + auto map_res = dest_proc.vmas()->MapInMemoryObject( + program.vaddr - page_offset, + glcr::StaticCastRefPtr(mem_obj)); + if (map_res != glcr::OK) { + panic("Couldn't map in init program {}", map_res); + } } return header->entry; } @@ -90,7 +100,8 @@ void DumpModules() { dbgln("[boot] Dumping bootloader modules."); for (uint64_t i = 0; i < resp.module_count; i++) { const limine_file& file = *resp.modules[i]; - dbgln(" {},{x},{}", file.path, file.address, file.size); + dbgln(" {},{x},{x}", glcr::String(file.path), (uint64_t)file.address, + file.size); } #endif } @@ -111,6 +122,8 @@ void WriteInitProgram(glcr::RefPtr port, glcr::String name, uint64_t id) { const limine_file& prog = GetInitProgram(name); glcr::RefPtr prog_vmmo = glcr::MakeRefCounted(prog.size); + // TODO: These seem to be page aligned we should just construct an object + // around them. prog_vmmo->CopyBytesToObject(reinterpret_cast(prog.address), prog.size); port->WriteKernel(id, MakeRefCounted(prog_vmmo)); @@ -121,6 +134,10 @@ glcr::ErrorCode WritePciVmmo(glcr::RefPtr port, uint64_t id) { auto vmmo = glcr::MakeRefCounted(config.base, config.offset); +#if K_INIT_DEBUG + dbgln("PCI Configuration found at: {x}:{x}", config.base, config.offset); +#endif + port->WriteKernel(id, MakeRefCounted(vmmo)); return glcr::OK; diff --git a/zion/memory/kernel_heap.cpp b/zion/memory/kernel_heap.cpp index 6ad518a..68b9b24 100644 --- a/zion/memory/kernel_heap.cpp +++ b/zion/memory/kernel_heap.cpp @@ -91,6 +91,11 @@ void* KernelHeap::Allocate(uint64_t size) { uint64_t address = next_addr_; alloc_count_ += 1; next_addr_ += size; + + // Ensure alingment for these pointers. + if (next_addr_ & 0x7) { + next_addr_ = (next_addr_ & ~0x7) + 0x8; + } return reinterpret_cast(address); } diff --git a/zion/object/memory_object.cpp b/zion/object/memory_object.cpp index 552f22a..9938db6 100644 --- a/zion/object/memory_object.cpp +++ b/zion/object/memory_object.cpp @@ -47,16 +47,16 @@ VariableMemoryObject::VariableMemoryObject(uint64_t size) : size_(size) { // FIXME: Do this lazily. uint64_t num_pages = size_ / 0x1000; phys_page_list_ = glcr::Array(num_pages); - for (uint64_t i = 0; i < phys_page_list_.size(); i++) { - phys_page_list_[i] = 0; + for (uint64_t& page : phys_page_list_) { + page = 0; } } VariableMemoryObject::~VariableMemoryObject() { - for (uint64_t p = 0; p < phys_page_list_.size(); p++) { - if (phys_page_list_[p] != 0) { + for (uint64_t& page : phys_page_list_) { + if (page != 0) { // TODO: We may be able to do some sort of coalescing here. - phys_mem::FreePage(phys_page_list_[p]); + phys_mem::FreePage(page); } } } diff --git a/zion/object/process.cpp b/zion/object/process.cpp index 8f45086..6fde4f1 100644 --- a/zion/object/process.cpp +++ b/zion/object/process.cpp @@ -65,9 +65,9 @@ void Process::Exit(uint64_t exit_code) { state_ = CLEANUP; exit_code_ = exit_code; - for (uint64_t i = 0; i < threads_.size(); i++) { - if (!threads_[i]->IsDying()) { - threads_[i]->SetState(Thread::CLEANUP); + for (const auto& t : threads_) { + if (!t->IsDying()) { + t->SetState(Thread::CLEANUP); } } @@ -93,9 +93,9 @@ void Process::Cleanup() { } // 1. For each thread, call cleanup. - for (uint64_t i = 0; i < threads_.size(); i++) { - if (threads_[i]->GetState() == Thread::CLEANUP) { - threads_[i]->Cleanup(); + for (const auto& t : threads_) { + if (t->GetState() == Thread::CLEANUP) { + t->Cleanup(); } } diff --git a/zion/object/thread.cpp b/zion/object/thread.cpp index 701792c..fea38f9 100644 --- a/zion/object/thread.cpp +++ b/zion/object/thread.cpp @@ -51,6 +51,9 @@ Thread::Thread(Process& proc, uint64_t tid) if (reinterpret_cast(fx_data_) & 0x8) { fx_data_ += 8; } + for (uint16_t i = 0; i < 512; i++) { + fx_data_[i] = 0; + } } uint64_t Thread::pid() const { return process_.id(); } @@ -120,8 +123,10 @@ void Thread::Cleanup() { // 2. Unblock waiting threads. while (blocked_threads_.size() != 0) { auto thread = blocked_threads_.PopFront(); - thread->SetState(Thread::RUNNABLE); - gScheduler->Enqueue(thread); + if (!thread->IsDying()) { + thread->SetState(Thread::RUNNABLE); + gScheduler->Enqueue(thread); + } } // 3. Release Kernel Stack diff --git a/zion/object/thread.h b/zion/object/thread.h index 53bee2b..6c32007 100644 --- a/zion/object/thread.h +++ b/zion/object/thread.h @@ -29,6 +29,7 @@ class Thread : public KernelObject, public glcr::IntrusiveListNode { RUNNING, RUNNABLE, BLOCKED, + SLEEPING, CLEANUP, FINISHED, }; @@ -69,6 +70,9 @@ class Thread : public KernelObject, public glcr::IntrusiveListNode { void Wait(); + void SetSleepTicks(uint64_t sleep_ticks) { sleep_ticks_ = sleep_ticks; } + bool DecrementSleepTicks() { return --sleep_ticks_ == 0; } + private: friend class glcr::MakeRefCountedFriend; Thread(Process& proc, uint64_t tid); @@ -79,6 +83,7 @@ class Thread : public KernelObject, public glcr::IntrusiveListNode { State state_ = CREATED; bool is_kernel_ = false; uint64_t user_stack_base_; + uint64_t sleep_ticks_; // Startup Context for the thread. uint64_t rip_; diff --git a/zion/scheduler/scheduler.cpp b/zion/scheduler/scheduler.cpp index 5d745c8..2a21d30 100644 --- a/zion/scheduler/scheduler.cpp +++ b/zion/scheduler/scheduler.cpp @@ -45,14 +45,11 @@ void Scheduler::Preempt() { return; } + DecrementSleepingThreads(); + ClearDeadThreadsFromFront(); asm volatile("cli"); - if (current_thread_ == sleep_thread_) { - // Sleep should never be preempted. (We should yield it if another thread - // becomes scheduleable). - return; - } if (runnable_threads_.size() == 0) { // Continue. @@ -102,9 +99,33 @@ void Scheduler::Yield() { SwapToCurrent(*prev); } +void Scheduler::Sleep(uint64_t millis) { + // FIXME: Improve resolution of sleep calls. + uint64_t ticks = (millis / 50) + 1; + current_thread_->SetSleepTicks(ticks); + current_thread_->SetState(Thread::SLEEPING); + sleeping_threads_.PushBack(current_thread_); + Yield(); +} + void Scheduler::ClearDeadThreadsFromFront() { while (runnable_threads_.size() > 0 && runnable_threads_.PeekFront()->IsDying()) { runnable_threads_.PopFront(); } } + +void Scheduler::DecrementSleepingThreads() { + auto thread = sleeping_threads_.PeekFront(); + while (thread) { + if (thread->DecrementSleepTicks()) { + auto thread_next = thread->next_; + sleeping_threads_.Remove(thread); + thread->SetState(Thread::RUNNABLE); + runnable_threads_.PushBack(thread); + thread = thread_next; + } else { + thread = thread->next_; + } + } +} diff --git a/zion/scheduler/scheduler.h b/zion/scheduler/scheduler.h index 823ded8..37aa0b9 100644 --- a/zion/scheduler/scheduler.h +++ b/zion/scheduler/scheduler.h @@ -23,18 +23,23 @@ class Scheduler { void Preempt(); void Yield(); + void Sleep(uint64_t millis); + private: bool enabled_ = false; glcr::RefPtr current_thread_; glcr::IntrusiveList runnable_threads_; + glcr::IntrusiveList sleeping_threads_; + glcr::RefPtr sleep_thread_; Scheduler(); void SwapToCurrent(Thread& prev); void ClearDeadThreadsFromFront(); + void DecrementSleepingThreads(); }; extern Scheduler* gScheduler; diff --git a/zion/syscall/ipc.cpp b/zion/syscall/ipc.cpp index a49e1ed..55c0799 100644 --- a/zion/syscall/ipc.cpp +++ b/zion/syscall/ipc.cpp @@ -28,12 +28,12 @@ glcr::ErrorOr TranslateRequestToIpcMessage(const T& req) { glcr::ArrayView caps(req.caps, req.num_caps); message.caps.Resize(caps.size()); - for (uint64_t i = 0; i < caps.size(); i++) { + for (uint64_t capid : caps) { // FIXME: This would feel safer closer to the relevant syscall. // FIXME: Race conditions on get->check->release here. Would be better to // have that as a single call on the process. (This pattern repeats other // places too). - auto cap = gScheduler->CurrentProcess().GetCapability(caps[i]); + auto cap = gScheduler->CurrentProcess().GetCapability(capid); if (!cap) { return glcr::CAP_NOT_FOUND; } @@ -41,7 +41,7 @@ glcr::ErrorOr TranslateRequestToIpcMessage(const T& req) { return glcr::CAP_PERMISSION_DENIED; } message.caps.PushBack( - gScheduler->CurrentProcess().ReleaseCapability(caps[i])); + gScheduler->CurrentProcess().ReleaseCapability(capid)); } return message; @@ -156,13 +156,14 @@ glcr::ErrorCode PortPoll(ZPortPollReq* req) { return TranslateIpcMessageToResponse(msg, req); } -glcr::ErrorCode IrqRegister(ZIrqRegisterReq* req) { +glcr::ErrorCode MsiIrqRegister(ZMsiIrqRegisterReq* req) { auto& proc = gScheduler->CurrentProcess(); - glcr::RefPtr port = glcr::MakeRefCounted(); *req->port_cap = proc.AddNewCapability(port); - return DriverManager::Get().RegisterListener(req->irq_num, port); + ASSIGN_OR_RETURN(*req->irq_num, DriverManager::Get().RegisterListener(port)); + + return glcr::OK; } glcr::ErrorCode EndpointCreate(ZEndpointCreateReq* req) { @@ -175,7 +176,7 @@ glcr::ErrorCode EndpointSend(ZEndpointSendReq* req) { auto& proc = gScheduler->CurrentProcess(); auto endpoint_cap = proc.GetCapability(req->endpoint_cap); - ValidateCapability(endpoint_cap, kZionPerm_Write); + RET_ERR(ValidateCapability(endpoint_cap, kZionPerm_Write)); auto endpoint = endpoint_cap->obj(); auto reply_port = ReplyPort::Create(); @@ -191,7 +192,7 @@ glcr::ErrorCode EndpointRecv(ZEndpointRecvReq* req) { auto& proc = gScheduler->CurrentProcess(); auto endpoint_cap = proc.GetCapability(req->endpoint_cap); - ValidateCapability(endpoint_cap, kZionPerm_Read); + RET_ERR(ValidateCapability(endpoint_cap, kZionPerm_Read)); auto endpoint = endpoint_cap->obj(); ASSIGN_OR_RETURN(IpcMessage msg, @@ -202,7 +203,7 @@ glcr::ErrorCode EndpointRecv(ZEndpointRecvReq* req) { glcr::ErrorCode ReplyPortSend(ZReplyPortSendReq* req) { auto& proc = gScheduler->CurrentProcess(); auto reply_port_cap = proc.GetCapability(req->reply_port_cap); - ValidateCapability(reply_port_cap, kZionPerm_Read); + RET_ERR(ValidateCapability(reply_port_cap, kZionPerm_Write)); auto reply_port = reply_port_cap->obj(); ASSIGN_OR_RETURN(IpcMessage message, TranslateRequestToIpcMessage(*req)); @@ -212,7 +213,7 @@ glcr::ErrorCode ReplyPortRecv(ZReplyPortRecvReq* req) { auto& proc = gScheduler->CurrentProcess(); auto reply_port_cap = proc.GetCapability(req->reply_port_cap); - ValidateCapability(reply_port_cap, kZionPerm_Read); + RET_ERR(ValidateCapability(reply_port_cap, kZionPerm_Read)); auto reply_port = reply_port_cap->obj(); ASSIGN_OR_RETURN(IpcMessage msg, diff --git a/zion/syscall/ipc.h b/zion/syscall/ipc.h index 4a3ae97..9b0f072 100644 --- a/zion/syscall/ipc.h +++ b/zion/syscall/ipc.h @@ -12,7 +12,7 @@ glcr::ErrorCode PortCreate(ZPortCreateReq* req); glcr::ErrorCode PortSend(ZPortSendReq* req); glcr::ErrorCode PortRecv(ZPortRecvReq* req); glcr::ErrorCode PortPoll(ZPortPollReq* req); -glcr::ErrorCode IrqRegister(ZIrqRegisterReq* req); +glcr::ErrorCode MsiIrqRegister(ZMsiIrqRegisterReq* req); glcr::ErrorCode EndpointCreate(ZEndpointCreateReq* req); glcr::ErrorCode EndpointSend(ZEndpointSendReq* req); diff --git a/zion/syscall/syscall.cpp b/zion/syscall/syscall.cpp index ac260a5..096c931 100644 --- a/zion/syscall/syscall.cpp +++ b/zion/syscall/syscall.cpp @@ -59,6 +59,7 @@ extern "C" z_err_t SyscallHandler(uint64_t call_id, void* req) { CASE(ThreadStart); CASE(ThreadExit); CASE(ThreadWait); + CASE(ThreadSleep); // syscall/address_space.h CASE(AddressSpaceMap); CASE(AddressSpaceUnmap); @@ -76,7 +77,7 @@ extern "C" z_err_t SyscallHandler(uint64_t call_id, void* req) { CASE(PortSend); CASE(PortRecv); CASE(PortPoll); - CASE(IrqRegister); + CASE(MsiIrqRegister); CASE(EndpointCreate); CASE(EndpointSend); CASE(EndpointRecv); diff --git a/zion/syscall/thread.cpp b/zion/syscall/thread.cpp index 36ca2fe..19f212e 100644 --- a/zion/syscall/thread.cpp +++ b/zion/syscall/thread.cpp @@ -53,3 +53,8 @@ glcr::ErrorCode ThreadWait(ZThreadWaitReq* req) { thread->Wait(); return glcr::OK; } + +glcr::ErrorCode ThreadSleep(ZThreadSleepReq* req) { + gScheduler->Sleep(req->millis); + return glcr::OK; +} diff --git a/zion/syscall/thread.h b/zion/syscall/thread.h index 17cc288..4abd323 100644 --- a/zion/syscall/thread.h +++ b/zion/syscall/thread.h @@ -8,3 +8,4 @@ glcr::ErrorCode ThreadCreate(ZThreadCreateReq* req); glcr::ErrorCode ThreadStart(ZThreadStartReq* req); glcr::ErrorCode ThreadExit(ZThreadExitReq*); glcr::ErrorCode ThreadWait(ZThreadWaitReq* req); +glcr::ErrorCode ThreadSleep(ZThreadSleepReq* req); diff --git a/zion/zion.cpp b/zion/zion.cpp index fe9e497..b2ee2e7 100644 --- a/zion/zion.cpp +++ b/zion/zion.cpp @@ -20,6 +20,7 @@ #include "syscall/syscall.h" extern "C" void zion() { + serial::initialize(9600); dbgln("[boot] Init GDT & IDT."); InitGdt(); InitIdt();