diff --git a/api/arch/x86/paging.hpp b/api/arch/x86/paging.hpp index 2fa2fe7847..15eb01aa47 100644 --- a/api/arch/x86/paging.hpp +++ b/api/arch/x86/paging.hpp @@ -22,7 +22,7 @@ #include #include #include -#include +#include #include #include diff --git a/api/hal/machine.hpp b/api/hal/machine.hpp index 3f9a920991..87653be4a5 100644 --- a/api/hal/machine.hpp +++ b/api/hal/machine.hpp @@ -21,7 +21,7 @@ #include #include -#include +#include namespace os { namespace detail { class Machine; } diff --git a/api/hal/machine_memory.hpp b/api/hal/machine_memory.hpp index 951b9567d5..eec08f2008 100644 --- a/api/hal/machine_memory.hpp +++ b/api/hal/machine_memory.hpp @@ -15,7 +15,7 @@ // limitations under the License. #include -#include +#include #ifndef OS_MACHINE_MEMORY_HPP #define OS_MACHINE_MEMORY_HPP diff --git a/api/mem/alloc.hpp b/api/mem/alloc.hpp new file mode 100644 index 0000000000..93e70218a2 --- /dev/null +++ b/api/mem/alloc.hpp @@ -0,0 +1,29 @@ +#ifndef MEM_ALLOC_HPP +#define MEM_ALLOC_HPP + +#include +#include + +namespace os::mem { + static constexpr size_t PAGE_SZ = 4096; // TODO: put elsewhere + + using Raw_allocator = buddy::Alloc; + + /** Get default allocator for untyped allocations */ + Raw_allocator& raw_allocator(); + + template + using Typed_allocator = Allocator; + + /** Get default std::allocator for typed allocations */ + template + inline Typed_allocator system_allocator() { + return Typed_allocator(raw_allocator()); + } + + /** True once the heap is initialized and usable */ + bool heap_ready(); + +} // namespace os::mem + +#endif // MEM_ALLOC_HPP diff --git a/api/mem/alloc/arena.hpp b/api/mem/alloc/arena.hpp new file mode 100644 index 0000000000..3a95e71785 --- /dev/null +++ b/api/mem/alloc/arena.hpp @@ -0,0 +1,167 @@ +#ifndef MEM_ALLOC_ARENA_HPP +#define MEM_ALLOC_ARENA_HPP + +#include "kprint" +#include +#include +#include +#include + +namespace os::mem { + +struct Region { uintptr_t addr; size_t size; }; + +class Arena { +public: + static constexpr size_t PAGE_SZ = 4096; + + Arena(uintptr_t base, size_t size) + : base_{align_up(base, PAGE_SZ)}, size_{align_down(size, PAGE_SZ)} { + if (size_) free_.push_back({base_, size_}); + } + + void* allocate(size_t len) noexcept { + return allocate_aligned(PAGE_SZ, len); + } + + void* allocate_aligned(size_t alignment, size_t len) noexcept { + alignment = std::max(alignment, PAGE_SZ); + len = align_up(len, PAGE_SZ); + + for (auto it = free_.begin(); it != free_.end(); ++it) { + uintptr_t a = align_up(it->addr, alignment); + if (a + len > it->addr + it->size) continue; + + // split left + if (a > it->addr) { + Region left{it->addr, a - it->addr}; + it->addr = a; + it->size -= left.size; + free_.insert(std::upper_bound(free_.begin(), free_.end(), left, cmp), left); + } + + // split right + const uintptr_t end = it->addr + len; + const uintptr_t it_end = it->addr + it->size; + if (end < it_end) { + Region right{end, it_end - end}; + it->size = len; + free_.insert(std::upper_bound(free_.begin(), free_.end(), right, cmp), right); + } + + void* ret = (void*)it->addr; + free_.erase(it); + return ret; + } + return nullptr; + } + + void* allocate_at(void* addr, size_t len) noexcept { + // assumes region is deallocated + kprintf("[arena] allocating at %p, %zu bytes", addr, len); + + uintptr_t a = (uintptr_t)addr; + len = align_up(len, PAGE_SZ); + if (a < base_ || a + len > base_ + size_) return nullptr; + + for (auto it = free_.begin(); it != free_.end(); ++it) { + const uintptr_t start = it->addr; + const uintptr_t end = start + it->size; + if (a >= start && a + len <= end) { + Region left{start, a - start}; + Region right{a + len, end - (a + len)}; + free_.erase(it); + if (left.size) + free_.insert(std::upper_bound(free_.begin(), free_.end(), left, cmp), left); + if (right.size) + free_.insert(std::upper_bound(free_.begin(), free_.end(), right, cmp), right); + return addr; + } + } + return nullptr; + } + + void deallocate(void* addr, size_t len) noexcept { + if (!addr || !len) return; + Region r{align_down((uintptr_t)addr, PAGE_SZ), align_up(len, PAGE_SZ)}; + + auto it = std::upper_bound(free_.begin(), free_.end(), r, cmp); + if (it != free_.begin()) { + auto prev = std::prev(it); + if (prev->addr + prev->size == r.addr) { + prev->size += r.size; + maybe_merge_with_next(prev); + return; + } + } + it = free_.insert(it, r); + maybe_merge_with_next(it); + } + + bool is_range_free(uintptr_t a, size_t len) const noexcept { + len = align_up(len, PAGE_SZ); + for (auto& r : free_) { + if (a >= r.addr && a + len <= r.addr + r.size) { + return true; + } + if (a + len <= r.addr) { + break; // past it + } + } + return false; + } + + size_t bytes_free() const noexcept { + size_t s = 0; + for (auto& r : free_) s += r.size; + return s; + } + + size_t bytes_used() const noexcept { + return size_ - bytes_free(); + } + + uintptr_t allocation_end() const noexcept { + if (free_.empty()) return base_ + size_; + auto last = std::prev(free_.end()); + if (last->addr + last->size == base_ + size_) return last->addr; + return base_ + size_; + } + + void reset() { + free_.clear(); + if (size_) free_.push_back({base_, size_}); + } + + // alignment helpers + template + static constexpr T align_up(T x, size_t a) noexcept { + return (x + a - 1) & ~(T)(a - 1); + } + template + static constexpr T align_down(T x, size_t a) noexcept { + return x & ~(T)(a - 1); + } + +private: + static inline bool cmp(const Region& a, const Region& b) noexcept { + return a.addr < b.addr; + } + + void maybe_merge_with_next(std::list::iterator it) { + auto nx = std::next(it); + if (nx != free_.end() && it->addr + it->size == nx->addr) { + it->size += nx->size; + free_.erase(nx); + } + } + + uintptr_t base_{0}; + size_t size_{0}; + std::list free_; +}; + +} // namespace os::mem + +#endif // MEM_ALLOC_ARENA_HPP + diff --git a/api/util/alloc_buddy.hpp b/api/mem/alloc/buddy.hpp similarity index 99% rename from api/util/alloc_buddy.hpp rename to api/mem/alloc/buddy.hpp index bc6e291fd2..2a2f0418cf 100644 --- a/api/util/alloc_buddy.hpp +++ b/api/mem/alloc/buddy.hpp @@ -53,6 +53,8 @@ namespace util { } namespace os::mem::buddy { + static constexpr size_t PAGE_SZ = 4096; + using namespace util::literals; using namespace util::bitops; diff --git a/api/util/alloc_lstack.hpp b/api/mem/alloc/lstack.hpp similarity index 100% rename from api/util/alloc_lstack.hpp rename to api/mem/alloc/lstack.hpp diff --git a/api/util/alloc_pmr.hpp b/api/mem/alloc/pmr.hpp similarity index 100% rename from api/util/alloc_pmr.hpp rename to api/mem/alloc/pmr.hpp diff --git a/api/util/allocator.hpp b/api/mem/allocator.hpp similarity index 100% rename from api/util/allocator.hpp rename to api/mem/allocator.hpp diff --git a/api/mem/flags.hpp b/api/mem/flags.hpp new file mode 100644 index 0000000000..cdaa7cf7b5 --- /dev/null +++ b/api/mem/flags.hpp @@ -0,0 +1,49 @@ +#ifndef MEM_FLAGS_HPP +#define MEM_FLAGS_HPP + +#include +#include +#include + +// TODO: separate architecture-specific Access flags from allocation-specific +namespace os::mem { + enum class Access : uint8_t { + none = 0, + read = 1, + write = 2, + execute = 4 + }; +} + +namespace os::mem::alloc { + enum class Sharing : uint64_t { + Anonymous = MAP_ANONYMOUS, // not backed by any file + Shared = MAP_SHARED, // shared with other processes, changes are carried to underlying file + Private = MAP_PRIVATE, // copy-on-write. (note: changes to underlying files post-mapping is unspecified) + FixedOverride = MAP_FIXED, // place mapping at address. underlying [data:data+length) is discarded + FixedFriendly = MAP_FIXED_NOREPLACE // same as fixed, but fails if region was occuppied + }; + + using Flags = os::mem::alloc::Sharing; // FIXME: ::Sharing should be its own type for exclusivity + using Protection = os::mem::Access; // HACK: works since we're assuming x86 +} // os::mem + +namespace util { + inline namespace bitops { + template<> + struct enable_bitmask_ops { + using type = typename std::underlying_type::type; + static constexpr bool enable = true; + }; + } + + inline namespace bitops { + template<> + struct enable_bitmask_ops { + using type = typename std::underlying_type::type; + static constexpr bool enable = true; + }; + } +} + +#endif // MEM_FLAGS_HPP diff --git a/api/kernel/memmap.hpp b/api/mem/memmap.hpp similarity index 99% rename from api/kernel/memmap.hpp rename to api/mem/memmap.hpp index 1009c2388f..917db58fef 100644 --- a/api/kernel/memmap.hpp +++ b/api/mem/memmap.hpp @@ -16,8 +16,8 @@ // limitations under the License. #pragma once -#ifndef KERNEL_MEMMAP_HPP -#define KERNEL_MEMMAP_HPP +#ifndef MEM_MEMMAP_HPP +#define MEM_MEMMAP_HPP #include #include @@ -466,4 +466,4 @@ class Memory_map { }; //< class Memory_map } // ns os::mem -#endif //< KERNEL_MEMMAP_HPP +#endif //< MEM_MEMMAP_HPP diff --git a/api/kernel/memory.hpp b/api/mem/vmap.hpp similarity index 54% rename from api/kernel/memory.hpp rename to api/mem/vmap.hpp index c19480b4c1..de3347dff2 100644 --- a/api/kernel/memory.hpp +++ b/api/mem/vmap.hpp @@ -1,54 +1,20 @@ -// -*- C++ -*- -// This file is a part of the IncludeOS unikernel - www.includeos.org -// -// Copyright 2017 Oslo and Akershus University College of Applied Sciences -// and Alfred Bratterud -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#ifndef KERNEL_MEMORY_HPP -#define KERNEL_MEMORY_HPP +#ifndef MEM_VMAP_HPP +#define MEM_VMAP_HPP #include #include -#include -#include -#include +#include +#include +#include +#include #include -#include +#include +#include +#include +#include namespace os::mem { - /** POSIX mprotect compliant access bits **/ - enum class Access : uint8_t { - none = 0, - read = 1, - write = 2, - execute = 4 - }; - - using Raw_allocator = buddy::Alloc; - - /** Get default allocator for untyped allocations */ - Raw_allocator& raw_allocator(); - - template - using Typed_allocator = Allocator; - - /** Get default std::allocator for typed allocations */ - template - Typed_allocator system_allocator() { return Typed_allocator(raw_allocator()); } - /** Get bitfield with bit set for each supported page size */ uintptr_t supported_page_sizes(); @@ -69,15 +35,14 @@ namespace os::mem { * For interfacing with the virtual memory API, e.g. mem::map / mem::protect. **/ template - struct Mapping - { + struct Mapping { static const size_t any_size; uintptr_t lin = 0; uintptr_t phys = 0; - Fl flags {}; - size_t size = 0; - size_t page_sizes = 0; + Fl flags {}; + size_t size = 0; + size_t page_sizes = 0; // Constructors Mapping() = default; @@ -101,15 +66,13 @@ namespace os::mem { inline size_t max_psize() const noexcept; std::string to_string() const; - - }; // struct Mapping<> + }; using Map = Mapping<>; /** Exception class possibly used by various ::mem functions. **/ class Memory_exception : public std::runtime_error - { using runtime_error::runtime_error; }; - + { using std::runtime_error::runtime_error; }; /** * Map linear address to physical memory, according to provided Mapping. @@ -130,7 +93,9 @@ namespace os::mem { /** Determine active page size of a given linear address **/ uintptr_t active_page_size(uintptr_t addr); - uintptr_t active_page_size(void* addr); + inline uintptr_t active_page_size(void* addr) { + return active_page_size((uintptr_t) addr); + } /** * Set and return access flags for a given linear address range. @@ -157,92 +122,70 @@ namespace os::mem { **/ Access protect_page(uintptr_t linear, Access flags = Access::read); - /** Get the physical address to which linear address is mapped **/ uintptr_t virt_to_phys(uintptr_t linear); - void virtual_move(uintptr_t src, size_t size, uintptr_t dst, const char* label); + inline void virtual_move(uintptr_t src, size_t size, uintptr_t dst, const char* label) + { + using namespace util::bitops; + const auto flags = os::mem::Access::read | os::mem::Access::write; + // setup @dst as new virt area for @src + os::mem::map({dst, src, flags, size}, label); + // unpresent @src + os::mem::protect(src, size, os::mem::Access::none); + } /** Virtual memory map **/ inline Memory_map& vmmap() { // TODO Move to machine static Memory_map memmap; return memmap; - }; - - bool heap_ready(); - -} // os::mem - - - - - -// Enable bitwise ops on access flags -namespace util { -inline namespace bitops { - template<> - struct enable_bitmask_ops { - using type = typename std::underlying_type::type; - static constexpr bool enable = true; - }; -} -} + } -namespace os::mem { // - // mem::Mapping implementation + // Mapping implementation // - template - Mapping::Mapping(uintptr_t linear, uintptr_t physical, Fl fl, size_t sz) - : lin{linear}, phys{physical}, flags{fl}, size{sz}, - page_sizes{any_size} {} + inline Mapping::Mapping(uintptr_t linear, uintptr_t physical, Fl fl, size_t sz) + : lin{linear}, phys{physical}, flags{fl}, size{sz}, page_sizes{any_size} {} template - Mapping::Mapping(uintptr_t linear, uintptr_t physical, Fl fl, size_t sz, size_t psz) - : lin{linear}, phys{physical}, flags{fl}, size{sz}, page_sizes{psz} - {} + inline Mapping::Mapping(uintptr_t linear, uintptr_t physical, Fl fl, size_t sz, size_t psz) + : lin{linear}, phys{physical}, flags{fl}, size{sz}, page_sizes{psz} {} template - bool Mapping::operator==(const Mapping& rhs) const noexcept - { return lin == rhs.lin - && phys == rhs.phys - && flags == rhs.flags - && size == rhs.size - && page_sizes == rhs.page_sizes; } + inline bool Mapping::operator==(const Mapping& rhs) const noexcept { + return lin == rhs.lin + && phys == rhs.phys + && flags == rhs.flags + && size == rhs.size + && page_sizes == rhs.page_sizes; + } template - Mapping::operator bool() const noexcept - { return size != 0 && page_sizes !=0; } + inline Mapping::operator bool() const noexcept { + return size != 0 && page_sizes != 0; + } template - bool Mapping::operator!=(const Mapping& rhs) const noexcept - { return ! (*this == rhs); } + inline bool Mapping::operator!=(const Mapping& rhs) const noexcept { + return !(*this == rhs); + } template - Mapping Mapping::operator+(const Mapping& rhs) noexcept - { + inline Mapping Mapping::operator+(const Mapping& rhs) noexcept { using namespace util::bitops; Mapping res; // Adding with empty map behaves like 0 + x / x + 0. - if (! rhs) { - return *this; - } - - if (! *this) - return rhs; - - if (res == rhs) - return res; + if (not rhs) return *this; + if (not *this) return rhs; + if (res == rhs) return res; // The mappings must have connecting ranges - if ((rhs.lin + rhs.size != lin) - and lin + size != rhs.lin) - { + if ((rhs.lin + rhs.size != lin) and (lin + size != rhs.lin)) { Ensures(!res); return res; } @@ -253,93 +196,73 @@ namespace os::mem { // The mappings can span several page sizes res.page_sizes |= rhs.page_sizes; - if (page_sizes && page_sizes != rhs.page_sizes) - { + if (page_sizes && page_sizes != rhs.page_sizes) { res.page_sizes |= page_sizes; } - res.size = size + rhs.size; + res.size = size + rhs.size; res.flags = flags & rhs.flags; - if (rhs) - Ensures(res); - + if (rhs) Ensures(res); return res; } template - Mapping Mapping::operator+=(const Mapping& rhs) noexcept { + inline Mapping Mapping::operator+=(const Mapping& rhs) noexcept { *this = *this + rhs; return *this; } template - size_t Mapping::min_psize() const noexcept - { return util::bits::keepfirst(page_sizes); } + inline size_t Mapping::min_psize() const noexcept { + return util::bits::keepfirst(page_sizes); + } template - size_t Mapping::max_psize() const noexcept - { return util::bits::keeplast(page_sizes); } + inline size_t Mapping::max_psize() const noexcept { + return util::bits::keeplast(page_sizes); + } template - inline std::string Mapping::to_string() const - { + inline std::string Mapping::to_string() const { using namespace util::literals; - char buffer[1024]; - int len = snprintf(buffer, sizeof(buffer), - "%p -> %p, size %s, flags %#x", - (void*) lin, - (void*) phys, - util::Byte_r(size).to_string().c_str(), - (int) flags); - - const bool isseq = __builtin_popcount(page_sizes) == 1; + + std::string s = std::format( "lin {} -> phys {}, size {}, flags {:#x}", + lin, phys, util::Byte_r(size).to_string(), static_cast(flags)); + + const bool isseq = std::popcount(page_sizes) == 1; if (isseq) { - len += snprintf(buffer + len, sizeof(buffer) - len, - " (%lu pages á %s)", - size / page_sizes, - util::Byte_r(page_sizes).to_string().c_str()); + const auto pages = size / page_sizes; + s += std::format(" ({} pages á {})", pages, util::Byte_r(page_sizes).to_string()); + } else { + s += std::format(" (page sizes: {})", page_sizes_str(page_sizes)); } - else { - len += snprintf(buffer + len, sizeof(buffer) - len, - " (page sizes: %s)", page_sizes_str(page_sizes).c_str()); - } - - return std::string(buffer, len); + return s; } - inline std::string page_sizes_str(size_t bits) - { + inline std::string page_sizes_str(size_t bits) { using namespace util::literals; + if (bits == 0) return "None"; std::string out; - while (bits){ - auto ps = 1 << (__builtin_ffsl(bits) - 1); - bits &= ~ps; - out += util::Byte_r(ps).to_string(); - if (bits) - out += ", "; - } + while (bits) { + // index of lowest set bit (well-defined because bits != 0 here) + const unsigned tz = std::countr_zero(bits); - return out; - } + // convert that bit to the corresponding power-of-two size + const std::size_t ps = 1 << tz; - inline uintptr_t active_page_size(void* addr) { - return active_page_size((uintptr_t) addr); - } + // and remove that bit from the input + bits &= ~ps; - inline void - virtual_move(uintptr_t src, size_t size, uintptr_t dst, const char* label) - { - using namespace util::bitops; - const auto flags = os::mem::Access::read | os::mem::Access::write; - // setup @dst as new virt area for @src - os::mem::map({dst, src, flags, size}, label); - // unpresent @src - os::mem::protect(src, size, os::mem::Access::none); + // append formatted size; add a comma if there are more bits left + std::format_to(std::back_inserter(out), "{}", util::Byte_r(ps).to_string()); + if (bits) out += ", "; + } + return out; } -} +} // namespace os::mem -#endif +#endif // MEM_VMAP_HPP diff --git a/api/net/tcp/connection.hpp b/api/net/tcp/connection.hpp index a6c35cfeaa..846216a243 100644 --- a/api/net/tcp/connection.hpp +++ b/api/net/tcp/connection.hpp @@ -31,7 +31,7 @@ #include #include -#include +#include namespace net { // Forward declaration of the TCP object diff --git a/api/net/tcp/tcp.hpp b/api/net/tcp/tcp.hpp index a3072857a3..2ee67a9b42 100644 --- a/api/net/tcp/tcp.hpp +++ b/api/net/tcp/tcp.hpp @@ -31,7 +31,7 @@ #include #include #include -#include +#include namespace net { diff --git a/api/util/bitops.hpp b/api/util/bitops.hpp index 4c9c0e6175..226e46a094 100644 --- a/api/util/bitops.hpp +++ b/api/util/bitops.hpp @@ -128,6 +128,21 @@ has_flag(E field, E flags){ return (field & flags) == flags ; } +// bool missing_flag(flag) +template +constexpr typename std::enable_if::enable, bool>::type +missing_flag(E flag){ + using base_type = typename std::underlying_type::type; + return not static_cast(flag); +} + +// bool missing_flag(field, flags) +template +constexpr typename std::enable_if::enable, bool>::type +missing_flag(E field, E flags){ + return (field & flags) != flags ; +} + // Enable for uint8_t template<> diff --git a/api/util/errno.hpp b/api/util/errno.hpp new file mode 100644 index 0000000000..f62297b0b8 --- /dev/null +++ b/api/util/errno.hpp @@ -0,0 +1,160 @@ +#ifndef UTIL_ERRNO_HPP +#define UTIL_ERRNO_HPP + +#include +#include + +namespace util::format { + + constexpr std::string_view errno_name(int e) noexcept { + switch (e) { // list generated from musl's errno.h + case 0: return "OK"; + case EPERM: return "EPERM"; + case ENOENT: return "ENOENT"; + case ESRCH: return "ESRCH"; + case EINTR: return "EINTR"; + case EIO: return "EIO"; + case ENXIO: return "ENXIO"; + case E2BIG: return "E2BIG"; + case ENOEXEC: return "ENOEXEC"; + case EBADF: return "EBADF"; + case ECHILD: return "ECHILD"; + // case EAGAIN: return "EAGAIN"; // EWOULDBLOCK + case ENOMEM: return "ENOMEM"; + case EACCES: return "EACCES"; + case EFAULT: return "EFAULT"; + case ENOTBLK: return "ENOTBLK"; + case EBUSY: return "EBUSY"; + case EEXIST: return "EEXIST"; + case EXDEV: return "EXDEV"; + case ENODEV: return "ENODEV"; + case ENOTDIR: return "ENOTDIR"; + case EISDIR: return "EISDIR"; + case EINVAL: return "EINVAL"; + case ENFILE: return "ENFILE"; + case EMFILE: return "EMFILE"; + case ENOTTY: return "ENOTTY"; + case ETXTBSY: return "ETXTBSY"; + case EFBIG: return "EFBIG"; + case ENOSPC: return "ENOSPC"; + case ESPIPE: return "ESPIPE"; + case EROFS: return "EROFS"; + case EMLINK: return "EMLINK"; + case EPIPE: return "EPIPE"; + case EDOM: return "EDOM"; + case ERANGE: return "ERANGE"; + case EDEADLK: return "EDEADLK"; // == EDEADLOCK + case ENAMETOOLONG: return "ENAMETOOLONG"; + case ENOLCK: return "ENOLCK"; + case ENOSYS: return "ENOSYS"; + case ENOTEMPTY: return "ENOTEMPTY"; + case ELOOP: return "ELOOP"; + case EWOULDBLOCK: return "EWOULDBLOCK"; // == EAGAIN + case ENOMSG: return "ENOMSG"; + case EIDRM: return "EIDRM"; + case ECHRNG: return "ECHRNG"; + case EL2NSYNC: return "EL2NSYNC"; + case EL3HLT: return "EL3HLT"; + case EL3RST: return "EL3RST"; + case ELNRNG: return "ELNRNG"; + case EUNATCH: return "EUNATCH"; + case ENOCSI: return "ENOCSI"; + case EL2HLT: return "EL2HLT"; + case EBADE: return "EBADE"; + case EBADR: return "EBADR"; + case EXFULL: return "EXFULL"; + case ENOANO: return "ENOANO"; + case EBADRQC: return "EBADRQC"; + case EBADSLT: return "EBADSLT"; + // case EDEADLOCK: return "EDEADLOCK"; // EDEADLK + case EBFONT: return "EBFONT"; + case ENOSTR: return "ENOSTR"; + case ENODATA: return "ENODATA"; + case ETIME: return "ETIME"; + case ENOSR: return "ENOSR"; + case ENONET: return "ENONET"; + case ENOPKG: return "ENOPKG"; + case EREMOTE: return "EREMOTE"; + case ENOLINK: return "ENOLINK"; + case EADV: return "EADV"; + case ESRMNT: return "ESRMNT"; + case ECOMM: return "ECOMM"; + case EPROTO: return "EPROTO"; + case EMULTIHOP: return "EMULTIHOP"; + case EDOTDOT: return "EDOTDOT"; + case EBADMSG: return "EBADMSG"; + case EOVERFLOW: return "EOVERFLOW"; + case ENOTUNIQ: return "ENOTUNIQ"; + case EBADFD: return "EBADFD"; + case EREMCHG: return "EREMCHG"; + case ELIBACC: return "ELIBACC"; + case ELIBBAD: return "ELIBBAD"; + case ELIBSCN: return "ELIBSCN"; + case ELIBMAX: return "ELIBMAX"; + case ELIBEXEC: return "ELIBEXEC"; + case EILSEQ: return "EILSEQ"; + case ERESTART: return "ERESTART"; + case ESTRPIPE: return "ESTRPIPE"; + case EUSERS: return "EUSERS"; + case ENOTSOCK: return "ENOTSOCK"; + case EDESTADDRREQ: return "EDESTADDRREQ"; + case EMSGSIZE: return "EMSGSIZE"; + case EPROTOTYPE: return "EPROTOTYPE"; + case ENOPROTOOPT: return "ENOPROTOOPT"; + case EPROTONOSUPPORT: return "EPROTONOSUPPORT"; + case ESOCKTNOSUPPORT: return "ESOCKTNOSUPPORT"; + // case EOPNOTSUPP: return "EOPNOTSUPP"; // ENOTSUP + case ENOTSUP: return "ENOTSUP"; + case EPFNOSUPPORT: return "EPFNOSUPPORT"; + case EAFNOSUPPORT: return "EAFNOSUPPORT"; + case EADDRINUSE: return "EADDRINUSE"; + case EADDRNOTAVAIL: return "EADDRNOTAVAIL"; + case ENETDOWN: return "ENETDOWN"; + case ENETUNREACH: return "ENETUNREACH"; + case ENETRESET: return "ENETRESET"; + case ECONNABORTED: return "ECONNABORTED"; + case ECONNRESET: return "ECONNRESET"; + case ENOBUFS: return "ENOBUFS"; + case EISCONN: return "EISCONN"; + case ENOTCONN: return "ENOTCONN"; + case ESHUTDOWN: return "ESHUTDOWN"; + case ETOOMANYREFS: return "ETOOMANYREFS"; + case ETIMEDOUT: return "ETIMEDOUT"; + case ECONNREFUSED: return "ECONNREFUSED"; + case EHOSTDOWN: return "EHOSTDOWN"; + case EHOSTUNREACH: return "EHOSTUNREACH"; + case EALREADY: return "EALREADY"; + case EINPROGRESS: return "EINPROGRESS"; + case ESTALE: return "ESTALE"; + case EUCLEAN: return "EUCLEAN"; + case ENOTNAM: return "ENOTNAM"; + case ENAVAIL: return "ENAVAIL"; + case EISNAM: return "EISNAM"; + case EREMOTEIO: return "EREMOTEIO"; + case EDQUOT: return "EDQUOT"; + case ENOMEDIUM: return "ENOMEDIUM"; + case EMEDIUMTYPE: return "EMEDIUMTYPE"; + case ECANCELED: return "ECANCELED"; + case ENOKEY: return "ENOKEY"; + case EKEYEXPIRED: return "EKEYEXPIRED"; + case EKEYREVOKED: return "EKEYREVOKED"; + case EKEYREJECTED: return "EKEYREJECTED"; + case EOWNERDEAD: return "EOWNERDEAD"; + case ENOTRECOVERABLE: return "ENOTRECOVERABLE"; + case ERFKILL: return "ERFKILL"; + case EHWPOISON: return "EHWPOISON"; + default: return "ERRNO_UNKNOWN"; + } + + //unreachable(); // TODO: C++23 + return ""; + } + + inline const char* errno_cstr(int e) noexcept { + return errno_name(e).data(); + } + +} // namespace util + +#endif // UTIL_ERRNO_HPP + diff --git a/api/util/typename.hpp b/api/util/typename.hpp index 676f48ac76..87446be52e 100644 --- a/api/util/typename.hpp +++ b/api/util/typename.hpp @@ -17,7 +17,7 @@ #include #include -#include +#include #include // Demangle diff --git a/deps/musl/patches/mmap.patch b/deps/musl/patches/mmap.patch new file mode 100644 index 0000000000..5bda61600b --- /dev/null +++ b/deps/musl/patches/mmap.patch @@ -0,0 +1,21 @@ +diff --git a/src/mman/mmap.c b/src/mman/mmap.c +index 43e5e029..43307692 100644 +--- a/src/mman/mmap.c ++++ b/src/mman/mmap.c +@@ -36,4 +36,15 @@ void *__mmap(void *start, size_t len, int prot, int flags, int fd, off_t off) + return (void *)__syscall_ret(ret); + } + +-weak_alias(__mmap, mmap); ++void *__includeos_mmap(void *start, size_t len, int prot, int flags, int fd, off_t off) ++{ ++ long ret; ++ if (flags & MAP_FIXED) { ++ __vm_wait(); ++ } ++ ret = __syscall(SYS_mmap, start, len, prot, flags, fd, off); ++ ++ return (void *) ret; ++} ++ ++weak_alias(__includeos_mmap, mmap); diff --git a/lib/LiveUpdate/src/os.cpp b/lib/LiveUpdate/src/os.cpp index f85d439b65..5618343bd9 100644 --- a/lib/LiveUpdate/src/os.cpp +++ b/lib/LiveUpdate/src/os.cpp @@ -1,6 +1,6 @@ #include #include -#include +#include #define HIGHMEM_LOCATION (1ull << 45) //#define LIU_DEBUG 1 diff --git a/lib/LiveUpdate/src/storage.cpp b/lib/LiveUpdate/src/storage.cpp index abb7fed443..05f599fe9e 100644 --- a/lib/LiveUpdate/src/storage.cpp +++ b/lib/LiveUpdate/src/storage.cpp @@ -21,7 +21,7 @@ #include "storage.hpp" #include #include -#include +#include #include #include //#define VERIFY_MEMORY diff --git a/lib/LiveUpdate/src/update.cpp b/lib/LiveUpdate/src/update.cpp index 3f777fdb34..1aff0b8ed3 100644 --- a/lib/LiveUpdate/src/update.cpp +++ b/lib/LiveUpdate/src/update.cpp @@ -29,7 +29,6 @@ #include "storage.hpp" #include #include -#include #include // for flushing #define LPRINT(x, ...) printf(x, ##__VA_ARGS__); diff --git a/src/arch/aarch64/paging.cpp b/src/arch/aarch64/paging.cpp index eda50ca68e..f8877eae1c 100644 --- a/src/arch/aarch64/paging.cpp +++ b/src/arch/aarch64/paging.cpp @@ -15,8 +15,9 @@ // See the License for the specific language governing permissions and // limitations under the License. +#include #include -#include +#include __attribute__((weak)) void __arch_init_paging() diff --git a/src/arch/i686/paging.cpp b/src/arch/i686/paging.cpp index 8779245939..032055d636 100644 --- a/src/arch/i686/paging.cpp +++ b/src/arch/i686/paging.cpp @@ -15,9 +15,9 @@ // See the License for the specific language governing permissions and // limitations under the License. +#include "mem/vmap.hpp" #include #include -#include __attribute__((weak)) void __arch_init_paging() diff --git a/src/arch/x86_64/ist.cpp b/src/arch/x86_64/ist.cpp index 059757e441..bb46081890 100644 --- a/src/arch/x86_64/ist.cpp +++ b/src/arch/x86_64/ist.cpp @@ -2,7 +2,7 @@ #include #include -#include +#include #ifdef DEBUG_IST #define IST_PRINT(X, ...) printf(X, __VA_ARGS__) diff --git a/src/kernel/elf.cpp b/src/kernel/elf.cpp index 20b1c5ff28..2a64e59012 100644 --- a/src/kernel/elf.cpp +++ b/src/kernel/elf.cpp @@ -61,12 +61,9 @@ static const uintptr_t ELF_START = reinterpret_cast(&_ELF_START_); extern "C" char * __cxa_demangle(const char *name, char *buf, size_t *n, int *status); -template -static std::string to_hex_string(N n) -{ - char buffer[16]; - int len = snprintf(buffer, sizeof(buffer), "%#x", n); - return std::string(buffer, len); +template +[[nodiscard]] static std::string to_hex_string(N n) { + return std::format("{:#x}", n); } static ElfEhdr& elf_header() { @@ -479,8 +476,7 @@ void elf_check_symbols_ok() } #ifdef ARCH_x86_64 -#include -#include +#include #include void elf_protect_symbol_areas() { diff --git a/src/kernel/liveupdate.cpp b/src/kernel/liveupdate.cpp index 0c9ba2e727..a1a12e1d9b 100644 --- a/src/kernel/liveupdate.cpp +++ b/src/kernel/liveupdate.cpp @@ -1,6 +1,6 @@ #include #include -#include +#include #define HIGHMEM_LOCATION (1ull << 45) //#define LIU_DEBUG 1 diff --git a/src/kernel/memmap.cpp b/src/kernel/memmap.cpp index 38ca1dd009..967817bbe0 100644 --- a/src/kernel/memmap.cpp +++ b/src/kernel/memmap.cpp @@ -17,7 +17,7 @@ #include #include -#include +#include #include #include #include diff --git a/src/kernel/multiboot.cpp b/src/kernel/multiboot.cpp index 74573fdcc4..b25194cde7 100644 --- a/src/kernel/multiboot.cpp +++ b/src/kernel/multiboot.cpp @@ -20,7 +20,7 @@ #include #include #include -#include +#include #include template diff --git a/src/kernel/system_log.cpp b/src/kernel/system_log.cpp index a16660a83f..748c86a0af 100644 --- a/src/kernel/system_log.cpp +++ b/src/kernel/system_log.cpp @@ -1,7 +1,7 @@ #include #include #include -#include +#include #include #include diff --git a/src/musl/mlock.cpp b/src/musl/mlock.cpp index 9c1044e565..9328a2caf1 100644 --- a/src/musl/mlock.cpp +++ b/src/musl/mlock.cpp @@ -1,5 +1,4 @@ #include "common.hpp" -#include static long sys_mlock(const void* /* addr */, size_t /* len */) { diff --git a/src/musl/mmap.cpp b/src/musl/mmap.cpp index 691681691c..7944e67b8c 100644 --- a/src/musl/mmap.cpp +++ b/src/musl/mmap.cpp @@ -1,19 +1,21 @@ #include "common.hpp" +#include "util/errno.hpp" #include #include #include -#include #include -#include +#include +#include +#include "mem/flags.hpp" #include #include using Alloc = os::mem::Raw_allocator; -static Alloc* alloc; +static Alloc* default_allocator; -Alloc& os::mem::raw_allocator() { - Expects(alloc); - return *alloc; +os::mem::Raw_allocator& os::mem::raw_allocator() { + Expects(default_allocator); + return *default_allocator; } uintptr_t __init_mmap(uintptr_t addr_begin, size_t size) @@ -21,104 +23,150 @@ uintptr_t __init_mmap(uintptr_t addr_begin, size_t size) auto aligned_begin = (addr_begin + Alloc::align - 1) & ~(Alloc::align - 1); int64_t len = size & ~int64_t(Alloc::align - 1); - alloc = Alloc::create((void*)aligned_begin, len); + default_allocator = Alloc::create((void*)aligned_begin, len); return aligned_begin + len; } extern "C" __attribute__((weak)) void* kalloc(size_t size) { Expects(kernel::heap_ready()); - return alloc->allocate(size); + return default_allocator->allocate(size); } extern "C" __attribute__((weak)) void* kalloc_aligned(size_t alignment, size_t size) { Expects(kernel::heap_ready()); - return alloc->do_allocate(size, alignment); + return default_allocator->do_allocate(size, alignment); } extern "C" __attribute__((weak)) void kfree (void* ptr, size_t size) { - alloc->deallocate(ptr, size); + default_allocator->deallocate(ptr, size); } size_t mmap_bytes_used() { - return alloc->bytes_used(); + return default_allocator->bytes_used(); } size_t mmap_bytes_free() { - return alloc->bytes_free(); + return default_allocator->bytes_free(); } uintptr_t mmap_allocation_end() { - return alloc->highest_used(); + return default_allocator->highest_used(); } -static void* sys_mmap(void * addr, size_t length, int /*prot*/, int flags, - int fd, off_t /*offset*/) +static void *mmap_failed(int _errno) { + kprintf("[mmap] FAIL errno=%s free=%zu used=%zu\n", util::format::errno_cstr(_errno), + mmap_bytes_free(), mmap_bytes_used()); + + errno = _errno; + return MAP_FAILED; +} + +using Fd = std::optional; // FIXME: proper type + +static void* __sys_mmap(/*const*/ uintptr_t addr, const size_t length, const os::mem::Access prot, + const os::mem::alloc::Flags flags, const Fd file_descriptor, const off_t offset) { + using namespace os::mem::alloc; + // NOTE: `must` and `should` messages in this function refer to POSIX mmap(3p) - // TODO: Implement minimal functionality to be POSIX compliant - // https://pubs.opengroup.org/onlinepubs/009695399/functions/mmap.html - if (length <= 0) { - Expectsf(false, "Must always allocate at least 1 byte. Got {}", length); - errno = EINVAL; - return MAP_FAILED; - } + // kprintf("[mmap] addr=%p len=%zu prot=0x%x flags=0x%x fd=%d off=%lld free=%zu used=%zu\n", _addr, length, prot, _flags, _fd, (long long)offset, mmap_bytes_free(), mmap_bytes_used()); // TODO: debugging - if (fd > -1) { - // None of our file systems support memory mapping at the moment - Expects(false && "Mapping to file descriptor not supported"); - errno = ENODEV; - return MAP_FAILED; + // TODO(mazunki): this is unnecessary when ::Sharing becomes a real type + if (util::has_flag(flags, Sharing::Private) == util::has_flag(flags, Sharing::Shared)) { + Expectsf(false, "sys_mmap: mapping must be either Private xor Shared"); + return mmap_failed(EINVAL); } - if ((flags & MAP_ANONYMOUS) == 0) { - Expects(false && "We only support MAP_ANONYMOUS calls to mmap()"); - errno = ENOTSUP; - return MAP_FAILED; + if (length == 0) { + Expectsf(false, "Mapping must never allocate 0 bytes"); + return mmap_failed(EINVAL); } - if ((flags & MAP_FIXED) > 0) { - Expects(false && "MAP_FIXED not supported."); - errno = ENOTSUP; - return MAP_FAILED; + if (util::has_flag(Flags::Anonymous)) { + if (file_descriptor) { + Expectsf(false, "Anonymous mappings must set fd=-1, got fd={}", file_descriptor.value()); // TODO(mazunki): rename -1 when signature changes + return mmap_failed(EINVAL); + } + if (offset != 0) { + Expectsf(false, "Anonymous mappings should have offset=0, got offset={}", offset); + return mmap_failed(EINVAL); + } } - if (((flags & MAP_PRIVATE) > 0) && ((flags & MAP_ANONYMOUS) == 0)) { - Expects(false && "MAP_PRIVATE only supported for MAP_ANONYMOUS"); - errno = ENOTSUP; - return MAP_FAILED; + // NOTE: specifying an address with non-fixed allocation is, per POSIX, only + // a hint. for now, we ignore this hint. + // + // this is the only reason why `uintptr_t addr` isn't const + if ((addr != 0) && util::missing_flag(flags, Flags::FixedOverride)) { + addr = 0; } - if (((flags & MAP_PRIVATE) > 0) && (addr != 0)) { - Expects(false && "MAP_PRIVATE only supported for new allocations (address=0)."); - errno = ENOTSUP; - return MAP_FAILED; - } + // TODO: Implement minimal functionality to be POSIX compliant + // https://pubs.opengroup.org/onlinepubs/009695399/functions/mmap.html - if (((flags & MAP_SHARED) == 0) && ((flags & MAP_PRIVATE) == 0)) { - Expects(false && "MAP_SHARED or MAP_PRIVATE must be set."); - errno = ENOTSUP; - return MAP_FAILED; + if (file_descriptor) { + Expectsf(false, "Mapping to file descriptor is not yet implemented, got fd={}", file_descriptor.value()); + return mmap_failed(ENOTSUP); } - // If we get here, the following should be true: - // MAP_ANONYMOUS set + MAP_SHARED or MAP_PRIVATE - // fd should be 0, address should be 0 for MAP_PRIVATE - // (address is in any case ignored) + if (util::missing_flag(flags, Sharing::Anonymous)) { + Expectsf(false, "Support for non-MAP_ANONYMOUS mappings is not yet implemented"); + return mmap_failed(ENOTSUP); + } - auto* res = kalloc(length); + void *res = nullptr; + + if (util::has_flag(flags, Flags::FixedOverride) or util::has_flag(flags, Flags::FixedFriendly)) { + if (addr == 0) { + return mmap_failed(EINVAL); // invalid address + } + if ((addr % os::mem::PAGE_SZ) != 0) { + return mmap_failed(ENOTSUP); // invalid alignment + } + + const size_t len_rounded = util::bits::roundto(os::mem::PAGE_SZ, length); + const bool do_override = util::has_flag(flags, Flags::FixedOverride) ? true : false; + + return mmap_failed(ENOTSUP); + // res = kalloc_fixed(reinterpret_cast(addr), len_rounded, do_override); + + } else { + if (util::has_flag(flags, Flags::Private)) { + if (util::missing_flag(flags, Flags::Anonymous)) { + Expectsf(false, "Support for MAP_PRIVATE other than with MAP_ANONYMOUS is not yet implemented"); + return mmap_failed(ENOTSUP); + } + if (addr != 0) { + Expectsf(false, "Support for MAP_PRIVATE other than for new allocations (addr=0) is not yet implemented. Got addr={}", addr); + return mmap_failed(ENOTSUP); + } + } + + res = kalloc(length); + } if (UNLIKELY(res == nullptr)) { - errno = ENOMEM; - return MAP_FAILED; + return mmap_failed(ENOMEM); } memset(res, 0, length); return res; } +static void* sys_mmap(void *_addr, size_t length, int _prot, int _flags, int _fd, off_t offset) +{ + const os::mem::alloc::Flags flags = static_cast(_flags); + const os::mem::alloc::Protection prot = static_cast(_prot); + const Fd file_descriptor = (_fd == -1) ? std::nullopt : Fd(_fd); + const uintptr_t addr = reinterpret_cast(_addr); + + return __sys_mmap(addr, length, prot, flags, file_descriptor, offset); +} + + extern "C" void* syscall_SYS_mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset) diff --git a/src/musl/mprotect.cpp b/src/musl/mprotect.cpp index 5be2bc6cbc..bcc4d71d4f 100644 --- a/src/musl/mprotect.cpp +++ b/src/musl/mprotect.cpp @@ -1,5 +1,4 @@ #include "common.hpp" -#include static long sys_mprotect(void* /*addr*/, size_t /*len*/, int /*prot*/) { diff --git a/src/net/buffer_store.cpp b/src/net/buffer_store.cpp index 13eecba202..17b089af59 100644 --- a/src/net/buffer_store.cpp +++ b/src/net/buffer_store.cpp @@ -18,7 +18,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/src/platform/x86_pc/idt.cpp b/src/platform/x86_pc/idt.cpp index 3d07f969a9..75c8e393cf 100644 --- a/src/platform/x86_pc/idt.cpp +++ b/src/platform/x86_pc/idt.cpp @@ -4,7 +4,7 @@ #include #include #include -#include +#include #define RING0_CODE_SEG 0x8 extern "C" { diff --git a/src/platform/x86_pc/os.cpp b/src/platform/x86_pc/os.cpp index 9b2097889c..d51512374a 100644 --- a/src/platform/x86_pc/os.cpp +++ b/src/platform/x86_pc/os.cpp @@ -23,7 +23,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/src/platform/x86_pc/softreset.cpp b/src/platform/x86_pc/softreset.cpp index aad752a7cd..0cb8c72f44 100644 --- a/src/platform/x86_pc/softreset.cpp +++ b/src/platform/x86_pc/softreset.cpp @@ -1,6 +1,5 @@ #include #include -#include #include #include using namespace util::literals; diff --git a/src/posix/memalign.cpp b/src/posix/memalign.cpp index 9281f5598a..b239ecf935 100644 --- a/src/posix/memalign.cpp +++ b/src/posix/memalign.cpp @@ -15,9 +15,9 @@ // See the License for the specific language governing permissions and // limitations under the License. +#include #include #include -#include #include extern "C" diff --git a/src/util/pmr_default.cpp b/src/util/pmr_default.cpp index cc7fc7f4e4..7caafecb39 100644 --- a/src/util/pmr_default.cpp +++ b/src/util/pmr_default.cpp @@ -1,5 +1,5 @@ -#include +#include std::pmr::memory_resource* std::pmr::get_default_resource() noexcept { static os::mem::Default_pmr* default_pmr; diff --git a/test/integration/kernel/memmap/service.cpp b/test/integration/kernel/memmap/service.cpp index 017838fef9..ca8d2ebbca 100644 --- a/test/integration/kernel/memmap/service.cpp +++ b/test/integration/kernel/memmap/service.cpp @@ -16,7 +16,7 @@ // limitations under the License. #include -#include +#include #include diff --git a/test/integration/memory/paging/service.cpp b/test/integration/memory/paging/service.cpp index 595ee71a69..340df5550f 100644 --- a/test/integration/memory/paging/service.cpp +++ b/test/integration/memory/paging/service.cpp @@ -21,7 +21,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/test/unit/kernel/os_test.cpp b/test/unit/kernel/os_test.cpp index 92bdabe0a4..6722f6f8a3 100644 --- a/test/unit/kernel/os_test.cpp +++ b/test/unit/kernel/os_test.cpp @@ -17,7 +17,7 @@ #include #include -#include +#include CASE("version() returns string representation of OS version") { diff --git a/test/unit/memory/alloc/buddy_alloc_test.cpp b/test/unit/memory/alloc/buddy_alloc_test.cpp index cdac74d4aa..2d47f4849b 100644 --- a/test/unit/memory/alloc/buddy_alloc_test.cpp +++ b/test/unit/memory/alloc/buddy_alloc_test.cpp @@ -17,8 +17,8 @@ // #define DEBUG_UNIT #include -#include -#include +#include +#include #include struct Pool { diff --git a/test/unit/memory/alloc/pmr_alloc_test.cpp b/test/unit/memory/alloc/pmr_alloc_test.cpp index 97b43a0358..1d3cd39a0d 100644 --- a/test/unit/memory/alloc/pmr_alloc_test.cpp +++ b/test/unit/memory/alloc/pmr_alloc_test.cpp @@ -17,7 +17,7 @@ #define DEBUG_UNIT #include -#include +#include #include #if __has_include() diff --git a/test/unit/memory/generic/test_memory.cpp b/test/unit/memory/generic/test_memory.cpp index 3be60dccb8..d935f6c8ae 100644 --- a/test/unit/memory/generic/test_memory.cpp +++ b/test/unit/memory/generic/test_memory.cpp @@ -23,7 +23,7 @@ #include #include -#include +#include #include #include #include diff --git a/test/unit/memory/lstack/test_lstack.hpp b/test/unit/memory/lstack/test_lstack.hpp index 8d5dd2f6f4..519d1f6d57 100644 --- a/test/unit/memory/lstack/test_lstack.hpp +++ b/test/unit/memory/lstack/test_lstack.hpp @@ -23,7 +23,7 @@ #include #include -#include +#include #include #include #include diff --git a/test/unit/memory/mapping/memmap_test.cpp b/test/unit/memory/mapping/memmap_test.cpp index 66c082ff9b..adf5fb69c0 100644 --- a/test/unit/memory/mapping/memmap_test.cpp +++ b/test/unit/memory/mapping/memmap_test.cpp @@ -17,7 +17,7 @@ #include #include -#include +#include using namespace os::mem; CASE ("Using the fixed memory range class") diff --git a/test/unit/memory/paging/x86_paging.cpp b/test/unit/memory/paging/x86_paging.cpp index 214e376bb6..9bf9c6f744 100644 --- a/test/unit/memory/paging/x86_paging.cpp +++ b/test/unit/memory/paging/x86_paging.cpp @@ -21,7 +21,7 @@ #include #include #include -#include +#include #include using namespace util;