mirror of
https://github.com/justinian/jsix.git
synced 2025-12-09 16:04:32 -08:00
[kernel] Spit out vm_area types
The vm_space allow() functionality was a bit janky; using VMAs for all regions would be a lot cleaner. To that end, this change: - Adds a "static array" ctor to kutil::vector for setting the kernel address space's VMA list. This way a kernel heap VMA can be created without the heap already existing. - Splits vm_area into different subclasses depending on desired behavior - Splits out the concept of vm_mapper which maps vm_areas to vm_spaces, so that some kinds of VMA can be inherently single-space - Implements VMA resizing so that userspace can grow allocations. - Obsolete page_table_indices is removed Also, the following bugs were fixed: - kutil::map iterators on empty maps no longer break - memory::page_count was doing page-align, not page-count See: Github bug #242 See: [frobozz blog post](https://jsix.dev/posts/frobozz/) Tags:
This commit is contained in:
@@ -60,6 +60,7 @@ modules:
|
||||
- src/kernel/syscalls/thread.cpp
|
||||
- src/kernel/syscalls/vm_area.cpp
|
||||
- src/kernel/task.s
|
||||
- src/kernel/vm_mapper.cpp
|
||||
- src/kernel/vm_space.cpp
|
||||
- src/kernel/crtn.s
|
||||
|
||||
|
||||
@@ -63,13 +63,23 @@ main(int argc, const char **argv)
|
||||
|
||||
uintptr_t base = 0xcc0000000;
|
||||
j6_handle_t vma = j6_handle_invalid;
|
||||
j6_status_t result = _syscall_vma_create_map(&vma, 0x100000, base);
|
||||
j6_status_t result = _syscall_vma_create_map(&vma, 0x100000, base, 1);
|
||||
if (result != j6_status_ok)
|
||||
return result;
|
||||
|
||||
size_t size = 0x800000;
|
||||
result = _syscall_vma_resize(vma, &size);
|
||||
if (result != j6_status_ok)
|
||||
return result;
|
||||
|
||||
if (size == 0x800000)
|
||||
_syscall_system_log("main thread resized memory area");
|
||||
|
||||
uint64_t *vma_ptr = reinterpret_cast<uint64_t*>(base);
|
||||
for (int i = 0; i < 4096; ++i)
|
||||
vma_ptr[i] = uint64_t(i);
|
||||
for (int i = 0; i < 300; ++i)
|
||||
vma_ptr[i * 512] = uint64_t(i);
|
||||
|
||||
_syscall_system_log("main thread wrote to memory area");
|
||||
|
||||
result = _syscall_endpoint_create(&endp);
|
||||
if (result != j6_status_ok)
|
||||
@@ -84,7 +94,7 @@ main(int argc, const char **argv)
|
||||
_syscall_system_log("main thread created sub thread");
|
||||
|
||||
char message[] = "MAIN THREAD SUCCESSFULLY CALLED SENDRECV IF THIS IS LOWERCASE";
|
||||
size_t size = sizeof(message);
|
||||
size = sizeof(message);
|
||||
result = _syscall_endpoint_sendrecv(endp, &size, (void*)message);
|
||||
if (result != j6_status_ok)
|
||||
return result;
|
||||
|
||||
@@ -17,7 +17,7 @@ namespace memory {
|
||||
constexpr uintptr_t page_offset = 0xffffc00000000000ull;
|
||||
|
||||
/// Max number of pages for a kernel stack
|
||||
constexpr unsigned kernel_stack_pages = 1;
|
||||
constexpr unsigned kernel_stack_pages = 4;
|
||||
|
||||
/// Max number of pages for a kernel buffer
|
||||
constexpr unsigned kernel_buffer_pages = 16;
|
||||
@@ -62,6 +62,6 @@ namespace memory {
|
||||
/// \arg bytes The number of bytes desired
|
||||
/// \returns The number of pages needed to contain the desired bytes
|
||||
inline size_t page_count(size_t bytes) {
|
||||
return ((bytes - 1) & (frame_size - 1)) + 1;
|
||||
return ((bytes - 1) >> 12) + 1;
|
||||
}
|
||||
} // namespace memory
|
||||
|
||||
@@ -24,8 +24,9 @@ SYSCALL(0x2a, endpoint_send, j6_handle_t, size_t, void *)
|
||||
SYSCALL(0x2b, endpoint_receive, j6_handle_t, size_t *, void *)
|
||||
SYSCALL(0x2c, endpoint_sendrecv, j6_handle_t, size_t *, void *)
|
||||
|
||||
SYSCALL(0x30, vma_create, j6_handle_t *, size_t)
|
||||
SYSCALL(0x31, vma_create_map, j6_handle_t *, size_t, uintptr_t)
|
||||
SYSCALL(0x30, vma_create, j6_handle_t *, size_t, uint32_t)
|
||||
SYSCALL(0x31, vma_create_map, j6_handle_t *, size_t, uintptr_t, uint32_t)
|
||||
SYSCALL(0x32, vma_close, j6_handle_t)
|
||||
SYSCALL(0x33, vma_map, j6_handle_t, uintptr_t)
|
||||
SYSCALL(0x34, vma_unmap, j6_handle_t)
|
||||
SYSCALL(0x34, vma_resize, j6_handle_t, size_t *)
|
||||
|
||||
@@ -35,6 +35,9 @@ kutil::heap_allocator &g_kernel_heap = __g_kernel_heap_storage.value;
|
||||
static kutil::no_construct<frame_allocator> __g_frame_allocator_storage;
|
||||
frame_allocator &g_frame_allocator = __g_frame_allocator_storage.value;
|
||||
|
||||
static kutil::no_construct<vm_area_open> __g_kernel_heap_area_storage;
|
||||
vm_area_open &g_kernel_heap_area = __g_kernel_heap_area_storage.value;
|
||||
|
||||
void * operator new(size_t size) { return g_kernel_heap.allocate(size); }
|
||||
void * operator new [] (size_t size) { return g_kernel_heap.allocate(size); }
|
||||
void operator delete (void *p) noexcept { return g_kernel_heap.free(p); }
|
||||
@@ -104,7 +107,11 @@ memory_initialize_pre_ctors(args::header *kargs)
|
||||
|
||||
process *kp = process::create_kernel_process(kpml4);
|
||||
vm_space &vm = kp->space();
|
||||
vm.allow(memory::heap_start, memory::kernel_max_heap, true);
|
||||
|
||||
vm_area *heap = new (&g_kernel_heap_area)
|
||||
vm_area_open(memory::kernel_max_heap, vm, vm_flags::write);
|
||||
|
||||
vm.add(memory::heap_start, heap);
|
||||
}
|
||||
|
||||
void
|
||||
|
||||
@@ -22,6 +22,7 @@ process::process() :
|
||||
s_processes.append(this);
|
||||
}
|
||||
|
||||
// The "kernel process"-only constructor
|
||||
process::process(page_table *kpml4) :
|
||||
kobject {kobject::type::process},
|
||||
m_space {kpml4},
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
#include "frame_allocator.h"
|
||||
#include "kernel_memory.h"
|
||||
#include "objects/vm_area.h"
|
||||
#include "vm_space.h"
|
||||
@@ -6,64 +5,55 @@
|
||||
using memory::frame_size;
|
||||
|
||||
vm_area::vm_area(size_t size, vm_flags flags) :
|
||||
m_size(size),
|
||||
m_flags(flags),
|
||||
kobject(kobject::type::vma)
|
||||
m_size {size},
|
||||
m_flags {flags},
|
||||
kobject {kobject::type::vma}
|
||||
{
|
||||
}
|
||||
|
||||
vm_area::~vm_area()
|
||||
{
|
||||
for (auto &it : m_spaces)
|
||||
remove_from(it.key);
|
||||
|
||||
frame_allocator &fa = frame_allocator::get();
|
||||
for (auto &m : m_mappings)
|
||||
fa.free(m.phys, m.count);
|
||||
}
|
||||
vm_area::~vm_area() {}
|
||||
|
||||
size_t
|
||||
vm_area::resize(size_t size)
|
||||
{
|
||||
if (mapper().can_resize(size))
|
||||
m_size = size;
|
||||
return m_size;
|
||||
}
|
||||
|
||||
void
|
||||
vm_area::add_to(vm_space *space, uintptr_t base)
|
||||
vm_area::commit(uintptr_t phys, uintptr_t offset, size_t count)
|
||||
{
|
||||
kassert(space, "null vm_space passed to vm_area::add_to");
|
||||
|
||||
// Multiple copies in the same space not yet supported
|
||||
uintptr_t *prev = m_spaces.find(space);
|
||||
if (prev) return;
|
||||
|
||||
if (m_spaces.count()) {
|
||||
// Just get the first address space in the map
|
||||
auto it = m_spaces.begin();
|
||||
vm_space *source = it->key;
|
||||
uintptr_t from = it->val;
|
||||
size_t pages = memory::page_count(m_size);
|
||||
space->copy_from(*source, from, base, pages);
|
||||
}
|
||||
|
||||
m_spaces.insert(space, base);
|
||||
space->add(base, this);
|
||||
mapper().map(offset, count, phys);
|
||||
}
|
||||
|
||||
void
|
||||
vm_area::remove_from(vm_space *space)
|
||||
vm_area::uncommit(uintptr_t offset, size_t count)
|
||||
{
|
||||
mapper().unmap(offset, count);
|
||||
}
|
||||
|
||||
void
|
||||
vm_area::on_no_handles()
|
||||
{
|
||||
kobject::on_no_handles();
|
||||
delete this;
|
||||
}
|
||||
|
||||
|
||||
|
||||
vm_area_shared::vm_area_shared(size_t size, vm_flags flags) :
|
||||
m_mapper {*this},
|
||||
vm_area {size, flags}
|
||||
{
|
||||
}
|
||||
|
||||
vm_area_shared::~vm_area_shared()
|
||||
{
|
||||
size_t pages = memory::page_count(m_size);
|
||||
uintptr_t *base = m_spaces.find(space);
|
||||
if (space && base) {
|
||||
space->clear(*base, pages);
|
||||
m_spaces.erase(space);
|
||||
}
|
||||
space->remove(this);
|
||||
}
|
||||
|
||||
size_t
|
||||
vm_area::overlaps(uintptr_t offset, size_t pages, size_t *count)
|
||||
vm_area_shared::overlaps(uintptr_t offset, size_t pages, size_t *count)
|
||||
{
|
||||
size_t first = 0;
|
||||
size_t n = 0;
|
||||
@@ -84,14 +74,16 @@ vm_area::overlaps(uintptr_t offset, size_t pages, size_t *count)
|
||||
return first;
|
||||
}
|
||||
|
||||
bool
|
||||
vm_area::commit(uintptr_t phys, uintptr_t offset, size_t count)
|
||||
void
|
||||
vm_area_shared::commit(uintptr_t phys, uintptr_t offset, size_t count)
|
||||
{
|
||||
size_t n = 0;
|
||||
size_t o = overlaps(offset, count, &n);
|
||||
|
||||
// Mapping overlaps not allowed
|
||||
if (n) return false;
|
||||
if (n) return;
|
||||
|
||||
m_mapper.map(offset, count, phys);
|
||||
|
||||
o = m_mappings.sorted_insert({
|
||||
.offset = offset,
|
||||
@@ -99,7 +91,6 @@ vm_area::commit(uintptr_t phys, uintptr_t offset, size_t count)
|
||||
.phys = phys});
|
||||
n = 1;
|
||||
|
||||
map(offset, count, phys);
|
||||
|
||||
// Try to expand to abutting similar areas
|
||||
if (o > 0 &&
|
||||
@@ -134,15 +125,17 @@ vm_area::commit(uintptr_t phys, uintptr_t offset, size_t count)
|
||||
if (n > 1)
|
||||
m_mappings.remove_at(o+1, n-1);
|
||||
|
||||
return true;
|
||||
return;
|
||||
}
|
||||
|
||||
bool
|
||||
vm_area::uncommit(uintptr_t offset, size_t count)
|
||||
void
|
||||
vm_area_shared::uncommit(uintptr_t offset, size_t count)
|
||||
{
|
||||
size_t n = 0;
|
||||
size_t o = overlaps(offset, count, &n);
|
||||
if (!n) return true;
|
||||
if (!n) return;
|
||||
|
||||
m_mapper.unmap(offset, count);
|
||||
|
||||
mapping *first = &m_mappings[o];
|
||||
mapping *last = &m_mappings[o+n-1];
|
||||
@@ -169,7 +162,7 @@ vm_area::uncommit(uintptr_t offset, size_t count)
|
||||
size_t remove_pages = first->count;
|
||||
first->count = leading / frame_size;
|
||||
remove_pages -= first->count;
|
||||
unmap(first->end(), remove_pages);
|
||||
m_mapper.unmap(first->end(), remove_pages);
|
||||
}
|
||||
|
||||
if (trailing) {
|
||||
@@ -178,7 +171,7 @@ vm_area::uncommit(uintptr_t offset, size_t count)
|
||||
last->offset = end;
|
||||
last->count = trailing / frame_size;
|
||||
remove_pages -= last->count;
|
||||
unmap(remove_off, remove_pages);
|
||||
m_mapper.unmap(remove_off, remove_pages);
|
||||
last->phys += remove_pages * frame_size;
|
||||
}
|
||||
|
||||
@@ -189,39 +182,32 @@ vm_area::uncommit(uintptr_t offset, size_t count)
|
||||
if (offset <= m.offset && end >= m.end()) {
|
||||
if (!delete_count) delete_start = i;
|
||||
++delete_count;
|
||||
unmap(m.offset, m.count);
|
||||
m_mapper.unmap(m.offset, m.count);
|
||||
}
|
||||
}
|
||||
|
||||
if (delete_count)
|
||||
m_mappings.remove_at(delete_start, delete_count);
|
||||
|
||||
return true;
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
vm_area_open::vm_area_open(size_t size, vm_space &space, vm_flags flags) :
|
||||
m_mapper(*this, space),
|
||||
vm_area(size, flags)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
vm_area::map(uintptr_t offset, size_t count, uintptr_t phys)
|
||||
vm_area_open::commit(uintptr_t phys, uintptr_t offset, size_t count)
|
||||
{
|
||||
for (auto &it : m_spaces) {
|
||||
uintptr_t addr = it.val + offset;
|
||||
vm_space *space = it.key;
|
||||
space->page_in(addr, phys, count);
|
||||
}
|
||||
m_mapper.map(offset, count, phys);
|
||||
}
|
||||
|
||||
void
|
||||
vm_area::unmap(uintptr_t offset, size_t count)
|
||||
vm_area_open::uncommit(uintptr_t offset, size_t count)
|
||||
{
|
||||
for (auto &it : m_spaces) {
|
||||
uintptr_t addr = it.val + offset;
|
||||
vm_space *space = it.key;
|
||||
space->clear(addr, count);
|
||||
}
|
||||
m_mapper.unmap(offset, count);
|
||||
}
|
||||
|
||||
void
|
||||
vm_area::on_no_handles()
|
||||
{
|
||||
kobject::on_no_handles();
|
||||
delete this;
|
||||
}
|
||||
|
||||
@@ -2,12 +2,16 @@
|
||||
/// \file vm_area.h
|
||||
/// Definition of VMA objects and related functions
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "j6/signals.h"
|
||||
#include "kutil/enum_bitfields.h"
|
||||
#include "kutil/map.h"
|
||||
#include "kutil/vector.h"
|
||||
|
||||
#include "kernel_memory.h"
|
||||
#include "objects/kobject.h"
|
||||
#include "vm_mapper.h"
|
||||
|
||||
class vm_space;
|
||||
|
||||
@@ -15,14 +19,17 @@ enum class vm_flags : uint32_t
|
||||
{
|
||||
none = 0x00000000,
|
||||
|
||||
zero = 0x00000001,
|
||||
contiguous = 0x00000002,
|
||||
write = 0x00000001,
|
||||
exec = 0x00000002,
|
||||
|
||||
zero = 0x00000010,
|
||||
contiguous = 0x00000020,
|
||||
|
||||
large_pages = 0x00000100,
|
||||
huge_pages = 0x00000200
|
||||
};
|
||||
huge_pages = 0x00000200,
|
||||
|
||||
IS_BITFIELD(vm_flags);
|
||||
user_mask = 0x0000ffff ///< flags allowed via syscall
|
||||
};
|
||||
|
||||
|
||||
/// Virtual memory areas allow control over memory allocation
|
||||
@@ -30,16 +37,20 @@ class vm_area :
|
||||
public kobject
|
||||
{
|
||||
public:
|
||||
static constexpr kobject::type type = kobject::type::vma;
|
||||
|
||||
/// Constructor.
|
||||
/// \arg size Initial virtual size of the memory area
|
||||
/// \arg flags Flags for this memory area
|
||||
vm_area(size_t size, vm_flags flags = vm_flags::none);
|
||||
|
||||
virtual ~vm_area();
|
||||
|
||||
static constexpr kobject::type type = kobject::type::vma;
|
||||
|
||||
/// Get the current virtual size of the memory area
|
||||
size_t size() const { return m_size; }
|
||||
inline size_t size() const { return m_size; }
|
||||
|
||||
/// Get the flags set for this area
|
||||
inline vm_flags flags() const { return m_flags; }
|
||||
|
||||
/// Change the virtual size of the memory area. This may cause
|
||||
/// deallocation if the new size is smaller than the current size.
|
||||
@@ -49,37 +60,52 @@ public:
|
||||
/// \returns The new virtual size
|
||||
size_t resize(size_t size);
|
||||
|
||||
/// Add this virtual area to a process' virtual address space. If
|
||||
/// the given base address is zero, a base address will be chosen
|
||||
/// automatically.
|
||||
/// \arg s The target address space
|
||||
/// \arg base The base address this area will be mapped to
|
||||
void add_to(vm_space *s, uintptr_t base);
|
||||
/// Get the mapper object that maps this area to address spaces
|
||||
virtual vm_mapper & mapper() = 0;
|
||||
virtual const vm_mapper & mapper() const = 0;
|
||||
|
||||
/// Remove this virtual area from a process' virtual address space.
|
||||
/// \arg s The target address space
|
||||
void remove_from(vm_space *s);
|
||||
/// Check whether allocation at the given offset is allowed
|
||||
virtual bool allowed(uintptr_t offset) const { return true; }
|
||||
|
||||
/// Commit contiguous physical pages to this area
|
||||
/// \arg phys The physical address of the first page
|
||||
/// \arg offset The offset from the start of this area these pages represent
|
||||
/// \arg count The number of pages
|
||||
/// \returns True if successful
|
||||
bool commit(uintptr_t phys, uintptr_t offset, size_t count);
|
||||
virtual void commit(uintptr_t phys, uintptr_t offset, size_t count);
|
||||
|
||||
/// Uncommit physical pages from this area
|
||||
/// \arg offset The offset from the start of this area these pages represent
|
||||
/// \arg count The number of pages
|
||||
/// \returns True if successful
|
||||
bool uncommit(uintptr_t offset, size_t count);
|
||||
|
||||
/// Get the flags set for this area
|
||||
vm_flags flags() const { return m_flags; }
|
||||
virtual void uncommit(uintptr_t offset, size_t count);
|
||||
|
||||
protected:
|
||||
virtual void on_no_handles() override;
|
||||
|
||||
size_t m_size;
|
||||
vm_flags m_flags;
|
||||
};
|
||||
|
||||
|
||||
/// The standard, sharable, user-controllable VMA type
|
||||
class vm_area_shared :
|
||||
public vm_area
|
||||
{
|
||||
public:
|
||||
/// Constructor.
|
||||
/// \arg size Initial virtual size of the memory area
|
||||
/// \arg flags Flags for this memory area
|
||||
vm_area_shared(size_t size, vm_flags flags = vm_flags::none);
|
||||
virtual ~vm_area_shared();
|
||||
|
||||
virtual vm_mapper & mapper() override { return m_mapper; }
|
||||
virtual const vm_mapper & mapper() const override { return m_mapper; }
|
||||
|
||||
virtual void commit(uintptr_t phys, uintptr_t offset, size_t count) override;
|
||||
virtual void uncommit(uintptr_t offset, size_t count) override;
|
||||
|
||||
private:
|
||||
vm_mapper_multi m_mapper;
|
||||
|
||||
struct mapping {
|
||||
uintptr_t offset;
|
||||
size_t count;
|
||||
@@ -95,11 +121,72 @@ private:
|
||||
|
||||
size_t overlaps(uintptr_t offset, size_t pages, size_t *count);
|
||||
|
||||
void map(uintptr_t offset, size_t count, uintptr_t phys);
|
||||
void unmap(uintptr_t offset, size_t count);
|
||||
|
||||
size_t m_size;
|
||||
vm_flags m_flags;
|
||||
kutil::map<vm_space*, uintptr_t> m_spaces;
|
||||
kutil::vector<mapping> m_mappings;
|
||||
};
|
||||
|
||||
/// Area split into standard-sized segments
|
||||
class vm_area_buffers :
|
||||
public vm_area
|
||||
{
|
||||
public:
|
||||
/// Constructor.
|
||||
/// \arg size Initial virtual size of the memory area
|
||||
/// \arg space The address space this area belongs to
|
||||
/// \arg flags Flags for this memory area
|
||||
vm_area_buffers(size_t size, vm_space &space, vm_flags flags);
|
||||
|
||||
virtual vm_mapper & mapper() override { return m_mapper; }
|
||||
virtual const vm_mapper & mapper() const override { return m_mapper; }
|
||||
|
||||
virtual bool allowed(uintptr_t offset) const override;
|
||||
virtual void commit(uintptr_t phys, uintptr_t offset, size_t count) override;
|
||||
virtual void uncommit(uintptr_t offset, size_t count) override;
|
||||
|
||||
private:
|
||||
vm_mapper_single m_mapper;
|
||||
};
|
||||
|
||||
|
||||
/// Area backed by an external source (like a loaded program)
|
||||
class vm_area_backed :
|
||||
public vm_area
|
||||
{
|
||||
public:
|
||||
/// Constructor.
|
||||
/// \arg size Initial virtual size of the memory area
|
||||
/// \arg flags Flags for this memory area
|
||||
vm_area_backed(size_t size, vm_flags flags);
|
||||
|
||||
virtual vm_mapper & mapper() override { return m_mapper; }
|
||||
virtual const vm_mapper & mapper() const override { return m_mapper; }
|
||||
|
||||
virtual void commit(uintptr_t phys, uintptr_t offset, size_t count) override;
|
||||
virtual void uncommit(uintptr_t offset, size_t count) override;
|
||||
|
||||
private:
|
||||
vm_mapper_multi m_mapper;
|
||||
};
|
||||
|
||||
|
||||
/// Area that allows open allocation (eg, kernel heap)
|
||||
class vm_area_open :
|
||||
public vm_area
|
||||
{
|
||||
public:
|
||||
/// Constructor.
|
||||
/// \arg size Initial virtual size of the memory area
|
||||
/// \arg space The address space this area belongs to
|
||||
/// \arg flags Flags for this memory area
|
||||
vm_area_open(size_t size, vm_space &space, vm_flags flags);
|
||||
|
||||
virtual vm_mapper & mapper() override { return m_mapper; }
|
||||
virtual const vm_mapper & mapper() const override { return m_mapper; }
|
||||
|
||||
virtual void commit(uintptr_t phys, uintptr_t offset, size_t count) override;
|
||||
virtual void uncommit(uintptr_t offset, size_t count) override;
|
||||
|
||||
private:
|
||||
vm_mapper_single m_mapper;
|
||||
};
|
||||
|
||||
IS_BITFIELD(vm_flags);
|
||||
|
||||
@@ -165,6 +165,7 @@ page_table::iterator::ensure_table(level l)
|
||||
|
||||
m_table[unsigned(l)] = table;
|
||||
parent = (phys & ~0xfffull) | flags;
|
||||
__atomic_thread_fence(__ATOMIC_SEQ_CST);
|
||||
}
|
||||
|
||||
page_table *
|
||||
@@ -296,26 +297,3 @@ page_table::dump(page_table::level lvl, bool recurse)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
page_table_indices::page_table_indices(uint64_t v) :
|
||||
index{
|
||||
(v >> 39) & 0x1ff,
|
||||
(v >> 30) & 0x1ff,
|
||||
(v >> 21) & 0x1ff,
|
||||
(v >> 12) & 0x1ff }
|
||||
{}
|
||||
|
||||
uintptr_t
|
||||
page_table_indices::addr() const
|
||||
{
|
||||
return
|
||||
(index[0] << 39) |
|
||||
(index[1] << 30) |
|
||||
(index[2] << 21) |
|
||||
(index[3] << 12);
|
||||
}
|
||||
|
||||
bool operator==(const page_table_indices &l, const page_table_indices &r)
|
||||
{
|
||||
return l[0] == r[0] && l[1] == r[1] && l[2] == r[2] && l[3] == r[3];
|
||||
}
|
||||
|
||||
@@ -177,25 +177,6 @@ struct page_table
|
||||
};
|
||||
|
||||
|
||||
/// Helper struct for computing page table indices of a given address.
|
||||
struct page_table_indices
|
||||
{
|
||||
page_table_indices(uint64_t v = 0);
|
||||
|
||||
uintptr_t addr() const;
|
||||
|
||||
inline operator uintptr_t() const { return addr(); }
|
||||
|
||||
/// Get the index for a given level of page table.
|
||||
uint64_t & operator[](int i) { return index[i]; }
|
||||
uint64_t operator[](int i) const { return index[i]; }
|
||||
uint64_t & operator[](page_table::level i) { return index[static_cast<unsigned>(i)]; }
|
||||
uint64_t operator[](page_table::level i) const { return index[static_cast<unsigned>(i)]; }
|
||||
uint64_t index[4]; ///< Indices for each level of tables.
|
||||
};
|
||||
|
||||
bool operator==(const page_table_indices &l, const page_table_indices &r);
|
||||
|
||||
inline page_table::level operator+(page_table::level a, unsigned b) {
|
||||
return static_cast<page_table::level>(static_cast<unsigned>(a) + b);
|
||||
}
|
||||
|
||||
@@ -22,6 +22,11 @@ syscall_handler_prelude:
|
||||
push rbp
|
||||
mov rbp, rsp
|
||||
|
||||
; account for the hole in the sysv abi
|
||||
; argument list since SYSCALL uses rcx
|
||||
mov rcx, r8
|
||||
mov r8, r9
|
||||
|
||||
push rbx
|
||||
push r11
|
||||
push r12
|
||||
|
||||
@@ -11,17 +11,19 @@
|
||||
namespace syscalls {
|
||||
|
||||
j6_status_t
|
||||
vma_create(j6_handle_t *handle, size_t size)
|
||||
vma_create(j6_handle_t *handle, size_t size, uint32_t flags)
|
||||
{
|
||||
construct_handle<vm_area>(handle, size);
|
||||
vm_flags f = vm_flags::user_mask & flags;
|
||||
construct_handle<vm_area_shared>(handle, size, f);
|
||||
return j6_status_ok;
|
||||
}
|
||||
|
||||
j6_status_t
|
||||
vma_create_map(j6_handle_t *handle, size_t size, uintptr_t base)
|
||||
vma_create_map(j6_handle_t *handle, size_t size, uintptr_t base, uint32_t flags)
|
||||
{
|
||||
vm_area *a = construct_handle<vm_area>(handle, size);
|
||||
a->add_to(&process::current().space(), base);
|
||||
vm_flags f = vm_flags::user_mask & flags;
|
||||
vm_area *a = construct_handle<vm_area_shared>(handle, size, f);
|
||||
process::current().space().add(base, a);
|
||||
return j6_status_ok;
|
||||
}
|
||||
|
||||
@@ -31,7 +33,7 @@ vma_map(j6_handle_t handle, uintptr_t base)
|
||||
vm_area *a = get_handle<vm_area>(handle);
|
||||
if (!a) return j6_err_invalid_arg;
|
||||
|
||||
a->add_to(&process::current().space(), base);
|
||||
process::current().space().add(base, a);
|
||||
return j6_status_ok;
|
||||
}
|
||||
|
||||
@@ -41,7 +43,7 @@ vma_unmap(j6_handle_t handle)
|
||||
vm_area *a = get_handle<vm_area>(handle);
|
||||
if (!a) return j6_err_invalid_arg;
|
||||
|
||||
a->remove_from(&process::current().space());
|
||||
process::current().space().remove(a);
|
||||
return j6_status_ok;
|
||||
}
|
||||
|
||||
@@ -56,6 +58,19 @@ vma_close(j6_handle_t handle)
|
||||
return j6_status_ok;
|
||||
}
|
||||
|
||||
j6_status_t
|
||||
vma_resize(j6_handle_t handle, size_t *size)
|
||||
{
|
||||
if (!size)
|
||||
return j6_err_invalid_arg;
|
||||
|
||||
vm_area *a = get_handle<vm_area>(handle);
|
||||
if (!a) return j6_err_invalid_arg;
|
||||
|
||||
*size = a->resize(*size);
|
||||
return j6_status_ok;
|
||||
}
|
||||
|
||||
|
||||
|
||||
} // namespace syscalls
|
||||
|
||||
98
src/kernel/vm_mapper.cpp
Normal file
98
src/kernel/vm_mapper.cpp
Normal file
@@ -0,0 +1,98 @@
|
||||
#include "objects/vm_area.h"
|
||||
#include "vm_mapper.h"
|
||||
#include "vm_space.h"
|
||||
|
||||
|
||||
vm_mapper_single::vm_mapper_single(vm_area &area, vm_space &space) :
|
||||
m_area(area), m_space(space)
|
||||
{}
|
||||
|
||||
vm_mapper_single::~vm_mapper_single()
|
||||
{
|
||||
m_space.clear(m_area, 0, memory::page_count(m_area.size()), true);
|
||||
}
|
||||
|
||||
bool
|
||||
vm_mapper_single::can_resize(size_t size) const
|
||||
{
|
||||
return m_space.can_resize(m_area, size);
|
||||
}
|
||||
|
||||
void
|
||||
vm_mapper_single::map(uintptr_t offset, size_t count, uintptr_t phys)
|
||||
{
|
||||
m_space.page_in(m_area, offset, phys, count);
|
||||
}
|
||||
|
||||
void
|
||||
vm_mapper_single::unmap(uintptr_t offset, size_t count)
|
||||
{
|
||||
m_space.clear(m_area, offset, count, true);
|
||||
}
|
||||
|
||||
|
||||
|
||||
vm_mapper_multi::vm_mapper_multi(vm_area &area) :
|
||||
m_area(area)
|
||||
{
|
||||
}
|
||||
|
||||
vm_mapper_multi::~vm_mapper_multi()
|
||||
{
|
||||
if (!m_spaces.count())
|
||||
return;
|
||||
|
||||
size_t count = memory::page_count(m_area.size());
|
||||
|
||||
for (int i = 1; i < m_spaces.count(); ++i)
|
||||
m_spaces[i]->clear(m_area, 0, count);
|
||||
|
||||
m_spaces[0]->clear(m_area, 0, count, true);
|
||||
}
|
||||
|
||||
bool
|
||||
vm_mapper_multi::can_resize(size_t size) const
|
||||
{
|
||||
for (auto &it : m_spaces)
|
||||
if (!it->can_resize(m_area, size))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
vm_mapper_multi::map(uintptr_t offset, size_t count, uintptr_t phys)
|
||||
{
|
||||
for (auto &it : m_spaces)
|
||||
it->page_in(m_area, offset, phys, count);
|
||||
}
|
||||
|
||||
void
|
||||
vm_mapper_multi::unmap(uintptr_t offset, size_t count)
|
||||
{
|
||||
for (auto &it : m_spaces)
|
||||
it->clear(m_area, offset, count);
|
||||
}
|
||||
|
||||
void
|
||||
vm_mapper_multi::add(vm_space *space)
|
||||
{
|
||||
if (m_spaces.count()) {
|
||||
vm_space *source = m_spaces[0];
|
||||
space->copy_from(*source, m_area);
|
||||
}
|
||||
|
||||
m_spaces.append(space);
|
||||
}
|
||||
|
||||
void
|
||||
vm_mapper_multi::remove(vm_space *space)
|
||||
{
|
||||
size_t count = memory::page_count(m_area.size());
|
||||
|
||||
for (int i = 0; i < m_spaces.count(); ++i) {
|
||||
if (m_spaces[i] == space) {
|
||||
m_spaces.remove_swap_at(i);
|
||||
space->clear(m_area, 0, count, m_spaces.count() == 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
79
src/kernel/vm_mapper.h
Normal file
79
src/kernel/vm_mapper.h
Normal file
@@ -0,0 +1,79 @@
|
||||
#pragma once
|
||||
/// \file vm_mapper.h
|
||||
/// VMA to address space mapping interface and implementing objects
|
||||
|
||||
#include <stdint.h>
|
||||
#include "kutil/vector.h"
|
||||
|
||||
class vm_area;
|
||||
class vm_space;
|
||||
|
||||
/// An interface to map vm_areas to one or more vm_spaces
|
||||
class vm_mapper
|
||||
{
|
||||
public:
|
||||
virtual ~vm_mapper() {}
|
||||
|
||||
/// Check whether the owning VMA can be resized to the given size.
|
||||
/// \arg size The desired size
|
||||
/// \returns True if resize is possible
|
||||
virtual bool can_resize(size_t size) const = 0;
|
||||
|
||||
/// Map the given physical pages into the owning VMA at the given offset
|
||||
/// \arg offset Offset into the VMA of the requested virtual address
|
||||
/// \arg count Number of contiguous physical pages to map
|
||||
/// \arg phys The starting physical address of the pages
|
||||
virtual void map(uintptr_t offset, size_t count, uintptr_t phys) = 0;
|
||||
|
||||
/// Unmap the pages corresponding to the given offset from the owning VMA
|
||||
/// \arg offset Offset into the VMA of the requested virtual address
|
||||
/// \arg count Number of pages to unmap
|
||||
virtual void unmap(uintptr_t offset, size_t count) = 0;
|
||||
|
||||
/// Add the given address space to the list of spaces the owning VMA is
|
||||
/// mapped to, if applicable.
|
||||
virtual void add(vm_space *space) {}
|
||||
|
||||
/// Remove the given address space from the list of spaces the owning VMA
|
||||
/// is mapped to, if applicable.
|
||||
virtual void remove(vm_space *space) {}
|
||||
};
|
||||
|
||||
|
||||
/// A vm_mapper that maps a VMA to a single vm_space
|
||||
class vm_mapper_single :
|
||||
public vm_mapper
|
||||
{
|
||||
public:
|
||||
vm_mapper_single(vm_area &area, vm_space &space);
|
||||
virtual ~vm_mapper_single();
|
||||
|
||||
virtual bool can_resize(size_t size) const override;
|
||||
virtual void map(uintptr_t offset, size_t count, uintptr_t phys) override;
|
||||
virtual void unmap(uintptr_t offset, size_t count) override;
|
||||
|
||||
private:
|
||||
vm_area &m_area;
|
||||
vm_space &m_space;
|
||||
};
|
||||
|
||||
|
||||
/// A vm_mapper that maps a VMA to multiple vm_spaces
|
||||
class vm_mapper_multi :
|
||||
public vm_mapper
|
||||
{
|
||||
public:
|
||||
vm_mapper_multi(vm_area &area);
|
||||
virtual ~vm_mapper_multi();
|
||||
|
||||
virtual bool can_resize(size_t size) const override;
|
||||
virtual void map(uintptr_t offset, size_t count, uintptr_t phys) override;
|
||||
virtual void unmap(uintptr_t offset, size_t count) override;
|
||||
virtual void add(vm_space *space) override;
|
||||
virtual void remove(vm_space *space) override;
|
||||
|
||||
private:
|
||||
vm_area &m_area;
|
||||
kutil::vector<vm_space*> m_spaces;
|
||||
};
|
||||
|
||||
@@ -1,10 +1,14 @@
|
||||
#include "frame_allocator.h"
|
||||
#include "kernel_memory.h"
|
||||
#include "log.h"
|
||||
#include "objects/process.h"
|
||||
#include "objects/thread.h"
|
||||
#include "objects/vm_area.h"
|
||||
#include "vm_space.h"
|
||||
|
||||
// The initial memory for the array of areas for the kernel space
|
||||
static uint64_t kernel_areas[16];
|
||||
|
||||
int
|
||||
vm_space::area::compare(const vm_space::area &o) const
|
||||
{
|
||||
@@ -20,7 +24,12 @@ vm_space::area::operator==(const vm_space::area &o) const
|
||||
}
|
||||
|
||||
|
||||
vm_space::vm_space(page_table *p) : m_kernel(true), m_pml4(p) {}
|
||||
// Kernel address space contsructor
|
||||
vm_space::vm_space(page_table *p) :
|
||||
m_kernel {true},
|
||||
m_pml4 {p},
|
||||
m_areas {reinterpret_cast<vm_space::area*>(kernel_areas), 0, 8}
|
||||
{}
|
||||
|
||||
vm_space::vm_space() :
|
||||
m_kernel(false)
|
||||
@@ -36,7 +45,7 @@ vm_space::vm_space() :
|
||||
vm_space::~vm_space()
|
||||
{
|
||||
for (auto &a : m_areas)
|
||||
a.area->remove_from(this);
|
||||
a.area->mapper().remove(this);
|
||||
|
||||
kassert(!is_kernel(), "Kernel vm_space destructor!");
|
||||
|
||||
@@ -61,6 +70,7 @@ vm_space::add(uintptr_t base, vm_area *area)
|
||||
{
|
||||
//TODO: check for collisions
|
||||
m_areas.sorted_insert({base, area});
|
||||
area->mapper().add(this);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -70,12 +80,36 @@ vm_space::remove(vm_area *area)
|
||||
for (auto &a : m_areas) {
|
||||
if (a.area == area) {
|
||||
m_areas.remove(a);
|
||||
area->mapper().remove(this);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool
|
||||
vm_space::can_resize(const vm_area &vma, size_t size) const
|
||||
{
|
||||
uintptr_t base = 0;
|
||||
unsigned n = m_areas.count();
|
||||
for (unsigned i = 0; i < n - 1; ++i) {
|
||||
const area &prev = m_areas[i - 1];
|
||||
if (prev.area != &vma)
|
||||
continue;
|
||||
|
||||
base = prev.base;
|
||||
const area &next = m_areas[i];
|
||||
if (prev.base + size > next.base)
|
||||
return false;
|
||||
}
|
||||
|
||||
uintptr_t end = base + size;
|
||||
uintptr_t space_end = is_kernel() ?
|
||||
uint64_t(-1) : 0x7fffffffffff;
|
||||
|
||||
return end <= space_end;
|
||||
}
|
||||
|
||||
vm_area *
|
||||
vm_space::get(uintptr_t addr, uintptr_t *base)
|
||||
{
|
||||
@@ -89,11 +123,28 @@ vm_space::get(uintptr_t addr, uintptr_t *base)
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void
|
||||
vm_space::copy_from(const vm_space &source, uintptr_t from, uintptr_t to, size_t count)
|
||||
bool
|
||||
vm_space::find_vma(const vm_area &vma, uintptr_t &base) const
|
||||
{
|
||||
page_table::iterator sit {from, source.m_pml4};
|
||||
for (auto &a : m_areas) {
|
||||
if (a.area != &vma) continue;
|
||||
base = a.base;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void
|
||||
vm_space::copy_from(const vm_space &source, const vm_area &vma)
|
||||
{
|
||||
uintptr_t to = 0;
|
||||
uintptr_t from = 0;
|
||||
if (!find_vma(vma, from) || !source.find_vma(vma, from))
|
||||
return;
|
||||
|
||||
size_t count = memory::page_count(vma.size());
|
||||
page_table::iterator dit {to, m_pml4};
|
||||
page_table::iterator sit {from, source.m_pml4};
|
||||
|
||||
while (count--) {
|
||||
uint64_t &e = dit.entry(page_table::level::pt);
|
||||
@@ -105,23 +156,39 @@ vm_space::copy_from(const vm_space &source, uintptr_t from, uintptr_t to, size_t
|
||||
}
|
||||
|
||||
void
|
||||
vm_space::page_in(uintptr_t virt, uintptr_t phys, size_t count)
|
||||
vm_space::page_in(const vm_area &vma, uintptr_t offset, uintptr_t phys, size_t count)
|
||||
{
|
||||
using memory::frame_size;
|
||||
|
||||
uintptr_t base = 0;
|
||||
if (!find_vma(vma, base))
|
||||
return;
|
||||
|
||||
uintptr_t virt = base + offset;
|
||||
page_table::flag flags =
|
||||
page_table::flag::present |
|
||||
(m_kernel ? page_table::flag::none : page_table::flag::user) |
|
||||
((vma.flags() && vm_flags::write) ? page_table::flag::write : page_table::flag::none);
|
||||
|
||||
page_table::iterator it {virt, m_pml4};
|
||||
|
||||
for (size_t i = 0; i < count; ++i) {
|
||||
uint64_t &e = it.entry(page_table::level::pt);
|
||||
bool allowed = (e & page_table::flag::allowed);
|
||||
e = (phys + i * memory::frame_size) |
|
||||
(allowed ? page_table::flag::allowed : page_table::flag::none);
|
||||
it.entry(page_table::level::pt) =
|
||||
(phys + i * frame_size) | flags;
|
||||
++it;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
vm_space::clear(uintptr_t addr, size_t count)
|
||||
vm_space::clear(const vm_area &vma, uintptr_t offset, size_t count, bool free)
|
||||
{
|
||||
using memory::frame_size;
|
||||
|
||||
uintptr_t base = 0;
|
||||
if (!find_vma(vma, base))
|
||||
return;
|
||||
|
||||
uintptr_t addr = base + offset;
|
||||
uintptr_t free_start = 0;
|
||||
size_t free_count = 0;
|
||||
|
||||
@@ -137,7 +204,7 @@ vm_space::clear(uintptr_t addr, size_t count)
|
||||
if (free_count && phys == free_start + (free_count * frame_size)) {
|
||||
++free_count;
|
||||
} else {
|
||||
if (free_count)
|
||||
if (free && free_count)
|
||||
fa.free(free_start, free_count);
|
||||
free_start = phys;
|
||||
free_count = 1;
|
||||
@@ -149,7 +216,7 @@ vm_space::clear(uintptr_t addr, size_t count)
|
||||
++it;
|
||||
}
|
||||
|
||||
if (free_count)
|
||||
if (free && free_count)
|
||||
fa.free(free_start, free_count);
|
||||
}
|
||||
|
||||
@@ -209,7 +276,7 @@ vm_space::handle_fault(uintptr_t addr, fault_type fault)
|
||||
uintptr_t base = 0;
|
||||
vm_area *area = get(addr, &base);
|
||||
|
||||
if (!area && !it.allowed())
|
||||
if ((!area || !area->allowed(page)) && !it.allowed())
|
||||
return false;
|
||||
|
||||
uintptr_t phys = 0;
|
||||
@@ -226,12 +293,13 @@ vm_space::handle_fault(uintptr_t addr, fault_type fault)
|
||||
? page_table::flag::global
|
||||
: page_table::flag::user);
|
||||
|
||||
it.entry(page_table::level::pt) = phys | flags;
|
||||
if (area) {
|
||||
uintptr_t offset = page - base;
|
||||
area->commit(phys, offset, 1);
|
||||
return true;
|
||||
}
|
||||
|
||||
it.entry(page_table::level::pt) = phys | flags;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -5,8 +5,8 @@
|
||||
#include <stdint.h>
|
||||
#include "kutil/enum_bitfields.h"
|
||||
#include "kutil/vector.h"
|
||||
#include "page_table.h"
|
||||
|
||||
struct page_table;
|
||||
class process;
|
||||
struct TCB;
|
||||
class vm_area;
|
||||
@@ -47,23 +47,19 @@ public:
|
||||
/// Get the kernel virtual memory space
|
||||
static vm_space & kernel_space();
|
||||
|
||||
/// Copy a range of mappings from the given address space
|
||||
/// \arg source The address space that already contains the mappings
|
||||
/// \arg from The starting virtual address in the source address space
|
||||
/// \arg to The starting virtual address in this address space
|
||||
/// \arg count The number of page entries to copy
|
||||
void copy_from(const vm_space &source, uintptr_t from, uintptr_t to, size_t count);
|
||||
|
||||
/// Map virtual addressses to the given physical pages
|
||||
/// \arg virt The starting virutal address
|
||||
/// \arg phys The starting physical address
|
||||
/// \arg count The number of contiugous physical pages to map
|
||||
void page_in(uintptr_t virt, uintptr_t phys, size_t count);
|
||||
/// \arg area The VMA this mapping applies to
|
||||
/// \arg offset Offset of the starting virutal address from the VMA base
|
||||
/// \arg phys The starting physical address
|
||||
/// \arg count The number of contiugous physical pages to map
|
||||
void page_in(const vm_area &area, uintptr_t offset, uintptr_t phys, size_t count);
|
||||
|
||||
/// Clear mappings from the given region
|
||||
/// \arg start The starting virutal address to clear
|
||||
/// \arg area The VMA these mappings applies to
|
||||
/// \arg offset Offset of the starting virutal address from the VMA base
|
||||
/// \arg count The number of pages worth of mappings to clear
|
||||
void clear(uintptr_t start, size_t count);
|
||||
/// \arg free If true, free the pages back to the system
|
||||
void clear(const vm_area &vma, uintptr_t start, size_t count, bool free = false);
|
||||
|
||||
/// Mark whether allocation is allowed or not in a range of
|
||||
/// virtual memory.
|
||||
@@ -106,6 +102,18 @@ public:
|
||||
static size_t copy(vm_space &source, vm_space &dest, void *from, void *to, size_t length);
|
||||
|
||||
private:
|
||||
friend class vm_mapper_single;
|
||||
friend class vm_mapper_multi;
|
||||
|
||||
/// Find a given VMA in this address space
|
||||
bool find_vma(const vm_area &vma, uintptr_t &base) const;
|
||||
|
||||
/// Check if a VMA can be resized
|
||||
bool can_resize(const vm_area &vma, size_t size) const;
|
||||
|
||||
/// Copy a range of mappings from the given address space
|
||||
void copy_from(const vm_space &source, const vm_area &vma);
|
||||
|
||||
bool m_kernel;
|
||||
page_table *m_pml4;
|
||||
|
||||
|
||||
@@ -105,16 +105,19 @@ public:
|
||||
}
|
||||
|
||||
iterator begin() {
|
||||
if (!m_count) return iterator {0};
|
||||
iterator it {m_nodes - 1, m_nodes + m_capacity};
|
||||
return ++it;
|
||||
}
|
||||
|
||||
const iterator begin() const {
|
||||
if (!m_count) return iterator {0};
|
||||
iterator it {m_nodes - 1, m_nodes + m_capacity};
|
||||
return ++it;
|
||||
}
|
||||
|
||||
const iterator end() const {
|
||||
if (!m_count) return iterator {0};
|
||||
return iterator(m_nodes + m_capacity);
|
||||
}
|
||||
|
||||
|
||||
@@ -45,7 +45,7 @@ public:
|
||||
}
|
||||
|
||||
/// Move constructor. Takes ownership of the other's array.
|
||||
vector(vector&& other) :
|
||||
vector(vector &&other) :
|
||||
m_size(other.m_size),
|
||||
m_capacity(other.m_capacity),
|
||||
m_elements(other.m_elements)
|
||||
@@ -55,6 +55,15 @@ public:
|
||||
other.m_elements = nullptr;
|
||||
}
|
||||
|
||||
/// Static array constructor. Starts the vector off with the given
|
||||
/// static storage.
|
||||
vector(T *data, size_t size, size_t capacity) :
|
||||
m_size(size),
|
||||
m_capacity(capacity),
|
||||
m_elements(&data[0])
|
||||
{
|
||||
}
|
||||
|
||||
/// Destructor. Destroys any remaining items in the array.
|
||||
~vector()
|
||||
{
|
||||
|
||||
@@ -4,7 +4,11 @@
|
||||
push rbp
|
||||
mov rbp, rsp
|
||||
|
||||
; address of args should already be in rdi, etc
|
||||
; args should already be in rdi, etc, but rcx will
|
||||
; get stomped, so shift args out one spot from rcx
|
||||
mov r9, r8
|
||||
mov r8, rcx
|
||||
|
||||
mov rax, %2
|
||||
syscall
|
||||
; result is now already in rax, so just return
|
||||
@@ -17,5 +21,7 @@
|
||||
%define SYSCALL(n, name, a) SYSCALL name, n
|
||||
%define SYSCALL(n, name, a, b) SYSCALL name, n
|
||||
%define SYSCALL(n, name, a, b, c) SYSCALL name, n
|
||||
%define SYSCALL(n, name, a, b, c, d) SYSCALL name, n
|
||||
%define SYSCALL(n, name, a, b, c, d, e) SYSCALL name, n
|
||||
|
||||
%include "syscalls.inc"
|
||||
|
||||
Reference in New Issue
Block a user