Spawn Processes using memory primitives rather than and elf loader.
This allows us to remove the temporary syscall for that style of process spawn.
This commit is contained in:
parent
b06c76e477
commit
23895b5c6c
|
@ -1,5 +1,6 @@
|
|||
add_library(mammoth_lib STATIC
|
||||
src/debug.cpp
|
||||
src/process.cpp
|
||||
src/thread.cpp
|
||||
)
|
||||
|
||||
|
|
|
@ -1,3 +1,9 @@
|
|||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
void dbgln(const char*);
|
||||
|
||||
// Checks that the code is ok.
|
||||
// if not exits the process.
|
||||
void check(uint64_t);
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
uint64_t SpawnProcessFromElfRegion(uint64_t program);
|
|
@ -1,5 +1,31 @@
|
|||
#include "include/mammoth/debug.h"
|
||||
|
||||
#include <zcall.h>
|
||||
#include <zerrors.h>
|
||||
|
||||
void dbgln(const char* str) { ZDebug(str); }
|
||||
void dbgln(const char* str) {
|
||||
// Safe to ignore the result since right now this doesn't throw.
|
||||
uint64_t _ = ZDebug(str);
|
||||
}
|
||||
|
||||
void check(uint64_t code) {
|
||||
switch (code) {
|
||||
case Z_OK:
|
||||
return;
|
||||
case ZE_NOT_FOUND:
|
||||
dbgln("crash: NOT_FOUND");
|
||||
break;
|
||||
case ZE_INVALID:
|
||||
dbgln("crash: INVALID");
|
||||
break;
|
||||
case ZE_DENIED:
|
||||
dbgln("crash: DENIED");
|
||||
break;
|
||||
case ZE_UNIMPLEMENTED:
|
||||
dbgln("crash: UNIMPLEMENTED");
|
||||
default:
|
||||
dbgln("Unhandled code");
|
||||
break;
|
||||
}
|
||||
ZProcessExit(code);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,97 @@
|
|||
#include "include/mammoth/process.h"
|
||||
|
||||
#include <zcall.h>
|
||||
|
||||
#include "include/mammoth/debug.h"
|
||||
|
||||
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<uint8_t*>(base);
|
||||
uint8_t* destptr = reinterpret_cast<uint8_t*>(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<Elf64Header*>(base);
|
||||
Elf64ProgramHeader* programs =
|
||||
reinterpret_cast<Elf64ProgramHeader*>(base + header->phoff);
|
||||
for (uint64_t i = 0; i < header->phnum; i++) {
|
||||
Elf64ProgramHeader& program = programs[i];
|
||||
dbgln("Create mem object");
|
||||
uint64_t mem_cap;
|
||||
uint64_t size = program.filesz;
|
||||
check(ZMemoryObjectCreate(size, &mem_cap));
|
||||
|
||||
dbgln("Map Local");
|
||||
uint64_t vaddr;
|
||||
check(ZAddressSpaceMap(Z_INIT_AS_SELF, 0, mem_cap, &vaddr));
|
||||
|
||||
dbgln("Copy");
|
||||
memcpy(base + program.offset, program.filesz, vaddr);
|
||||
|
||||
dbgln("Map Foreign");
|
||||
check(ZAddressSpaceMap(as_cap, program.vaddr, mem_cap, &vaddr));
|
||||
}
|
||||
return header->entry;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
uint64_t SpawnProcessFromElfRegion(uint64_t program) {
|
||||
dbgln("Spawn");
|
||||
uint64_t proc_cap;
|
||||
uint64_t as_cap;
|
||||
check(ZProcessSpawn(Z_INIT_PROC_SELF, &proc_cap, &as_cap));
|
||||
|
||||
uint64_t entry_point = LoadElfProgram(program, as_cap);
|
||||
dbgln("Thread Create");
|
||||
uint64_t thread_cap;
|
||||
check(ZThreadCreate(proc_cap, &thread_cap));
|
||||
|
||||
dbgln("Thread start");
|
||||
check(ZThreadStart(thread_cap, entry_point, 0, 0));
|
||||
}
|
|
@ -8,7 +8,7 @@ add_executable(test
|
|||
)
|
||||
|
||||
target_link_libraries(test
|
||||
zion_lib)
|
||||
mammoth_lib)
|
||||
|
||||
set_target_properties(test
|
||||
PROPERTIES
|
||||
|
|
14
sys/test.cpp
14
sys/test.cpp
|
@ -1,15 +1,11 @@
|
|||
#include <zcall.h>
|
||||
#include <zerrors.h>
|
||||
#include <mammoth/debug.h>
|
||||
#include <mammoth/process.h>
|
||||
|
||||
constexpr uint64_t prog2 = 0x00000020'00000000;
|
||||
|
||||
int main() {
|
||||
ZDebug("Testing");
|
||||
uint64_t err = ZProcessSpawnElf(Z_INIT_PROC_SELF, prog2, 0x2000);
|
||||
if (err != Z_OK) {
|
||||
ZDebug("Error");
|
||||
} else {
|
||||
ZDebug("Return");
|
||||
}
|
||||
dbgln("Testing");
|
||||
check(SpawnProcessFromElfRegion(prog2));
|
||||
dbgln("Return");
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -4,17 +4,33 @@
|
|||
#include "object/thread.h"
|
||||
|
||||
template <>
|
||||
Process& Capability::obj<Process>() {
|
||||
RefPtr<Process> Capability::obj<Process>() {
|
||||
if (type_ != PROCESS) {
|
||||
panic("Accessing %u cap as object.", type_);
|
||||
}
|
||||
return *static_cast<Process*>(obj_.get());
|
||||
return StaticCastRefPtr<Process>(obj_);
|
||||
}
|
||||
|
||||
template <>
|
||||
Thread& Capability::obj<Thread>() {
|
||||
RefPtr<Thread> Capability::obj<Thread>() {
|
||||
if (type_ != THREAD) {
|
||||
panic("Accessing %u cap as object.", type_);
|
||||
}
|
||||
return *static_cast<Thread*>(obj_.get());
|
||||
return StaticCastRefPtr<Thread>(obj_);
|
||||
}
|
||||
|
||||
template <>
|
||||
RefPtr<AddressSpace> Capability::obj<AddressSpace>() {
|
||||
if (type_ != ADDRESS_SPACE) {
|
||||
panic("Accessing %u cap as object.", type_);
|
||||
}
|
||||
return StaticCastRefPtr<AddressSpace>(obj_);
|
||||
}
|
||||
|
||||
template <>
|
||||
RefPtr<MemoryObject> Capability::obj<MemoryObject>() {
|
||||
if (type_ != MEMORY_OBJECT) {
|
||||
panic("Accessing %u cap as object.", type_);
|
||||
}
|
||||
return StaticCastRefPtr<MemoryObject>(obj_);
|
||||
}
|
||||
|
|
|
@ -14,6 +14,8 @@ class Capability {
|
|||
UNDEFINED,
|
||||
PROCESS,
|
||||
THREAD,
|
||||
ADDRESS_SPACE,
|
||||
MEMORY_OBJECT,
|
||||
};
|
||||
Capability(const RefPtr<KernelObject>& obj, Type type, uint64_t id,
|
||||
uint64_t permissions)
|
||||
|
@ -25,7 +27,7 @@ class Capability {
|
|||
}
|
||||
|
||||
template <typename T>
|
||||
T& obj();
|
||||
RefPtr<T> obj();
|
||||
|
||||
uint64_t id() { return id_; }
|
||||
|
||||
|
|
|
@ -34,7 +34,7 @@ struct GdtPointer {
|
|||
uint64_t base;
|
||||
} __attribute__((packed));
|
||||
|
||||
static uint64_t gGdtSegments[7];
|
||||
static uint64_t gGdtSegments[8];
|
||||
static TaskStateSegment gTaskStateSegment;
|
||||
|
||||
// Defined in load_gdt.s
|
||||
|
@ -70,13 +70,15 @@ void InitGdt() {
|
|||
// User CS
|
||||
gGdtSegments[3] =
|
||||
CreateSegment(default_bits | GDT_RING3 | GDT_EXECUTABLE, GDT_FLAGS);
|
||||
// Copy for the sysret function.
|
||||
gGdtSegments[5] = gGdtSegments[3];
|
||||
|
||||
// User DS
|
||||
gGdtSegments[4] = CreateSegment(default_bits | GDT_RING3, GDT_FLAGS);
|
||||
|
||||
gTaskStateSegment.iomap_base = sizeof(TaskStateSegment);
|
||||
gGdtSegments[5] = CreateTssSegment(&gTaskStateSegment);
|
||||
gGdtSegments[6] = reinterpret_cast<uint64_t>(&gTaskStateSegment) >> 32;
|
||||
gGdtSegments[6] = CreateTssSegment(&gTaskStateSegment);
|
||||
gGdtSegments[7] = reinterpret_cast<uint64_t>(&gTaskStateSegment) >> 32;
|
||||
|
||||
GdtPointer gdtp{
|
||||
.size = sizeof(gGdtSegments) - 1,
|
||||
|
@ -86,7 +88,7 @@ void InitGdt() {
|
|||
asm volatile("lgdt %0" ::"m"(gdtp));
|
||||
ReloadSegments();
|
||||
asm volatile(
|
||||
"mov $0x28, %%ax;"
|
||||
"mov $0x30, %%ax;"
|
||||
"ltr %%ax;" ::
|
||||
: "rax");
|
||||
}
|
||||
|
|
|
@ -12,53 +12,45 @@
|
|||
#define Z_PROCESS_SPAWN 0x02
|
||||
#define Z_PROCESS_START 0x03
|
||||
|
||||
#define Z_PROCESS_SPAWN_ELF 0x1'00000002
|
||||
|
||||
#define ZC_PROC_SPAWN_PROC 0x100
|
||||
#define ZC_PROC_SPAWN_THREAD 0x101
|
||||
|
||||
#define Z_INIT_PROC_SELF 0x1
|
||||
#define Z_INIT_AS_SELF 0x2
|
||||
|
||||
// Thread Calls.
|
||||
#define Z_THREAD_CREATE 0x10
|
||||
#define Z_THREAD_START 0x11
|
||||
#define Z_THREAD_EXIT 0x12
|
||||
|
||||
// Memory Calls
|
||||
#define Z_ADDRESS_SPACE_MAP 0x21
|
||||
#define Z_ADDRESS_SPACE_UNMAP 0x22
|
||||
|
||||
#define Z_MEMORY_OBJECT_CREATE 0x30
|
||||
|
||||
// Debugging Calls.
|
||||
#define Z_DEBUG_PRINT 0x10000000
|
||||
|
||||
uint64_t ZDebug(const char* message);
|
||||
void ZProcessExit(uint64_t code);
|
||||
|
||||
// TODO: Move structs into an internal header.
|
||||
struct ZProcessSpawnElfReq {
|
||||
uint64_t cap_id;
|
||||
uint64_t elf_base;
|
||||
uint64_t elf_size;
|
||||
};
|
||||
[[nodiscard]] uint64_t ZProcessSpawn(uint64_t proc_cap, uint64_t* new_proc_cap,
|
||||
uint64_t* new_as_cap);
|
||||
|
||||
// Creates a child process of the current one and
|
||||
// starts its root thread from the provided ELF file.
|
||||
uint64_t ZProcessSpawnElf(uint64_t cap_id, uint64_t elf_base,
|
||||
uint64_t elf_size);
|
||||
// UNUSED for now, I think we can get away with just starting a thread.
|
||||
[[nodiscard]] uint64_t ZProcessStart(uint64_t proc_cap, uint64_t thread_cap,
|
||||
uint64_t entry, uint64_t arg1,
|
||||
uint64_t arg2);
|
||||
|
||||
struct ZThreadCreateReq {
|
||||
uint64_t proc_cap;
|
||||
};
|
||||
[[nodiscard]] uint64_t ZThreadCreate(uint64_t proc_cap, uint64_t* thread_cap);
|
||||
|
||||
struct ZThreadCreateResp {
|
||||
uint64_t thread_cap;
|
||||
};
|
||||
|
||||
uint64_t ZThreadCreate(uint64_t proc_cap, uint64_t* thread_cap);
|
||||
|
||||
struct ZThreadStartReq {
|
||||
uint64_t thread_cap;
|
||||
uint64_t entry;
|
||||
uint64_t arg1;
|
||||
uint64_t arg2;
|
||||
};
|
||||
|
||||
uint64_t ZThreadStart(uint64_t thread_cap, uint64_t entry, uint64_t arg1,
|
||||
uint64_t arg2);
|
||||
[[nodiscard]] uint64_t ZThreadStart(uint64_t thread_cap, uint64_t entry,
|
||||
uint64_t arg1, uint64_t arg2);
|
||||
|
||||
void ZThreadExit();
|
||||
|
||||
[[nodiscard]] uint64_t ZAddressSpaceMap(uint64_t as_cap, uint64_t offset,
|
||||
uint64_t mem_cap, uint64_t* vaddr);
|
||||
[[nodiscard]] uint64_t ZMemoryObjectCreate(uint64_t size, uint64_t* mem_cap);
|
||||
|
||||
[[nodiscard]] uint64_t ZDebug(const char* message);
|
||||
|
|
|
@ -4,3 +4,4 @@
|
|||
#define ZE_NOT_FOUND 0x1
|
||||
#define ZE_INVALID 0x2
|
||||
#define ZE_DENIED 0x4
|
||||
#define ZE_UNIMPLEMENTED 0x8
|
||||
|
|
|
@ -80,6 +80,8 @@ extern "C" void interrupt_protection_fault(InterruptFrame* frame) {
|
|||
dbgln("GDT");
|
||||
}
|
||||
dbgln("Index: %u", err >> 3);
|
||||
dbgln("RIP: %m", frame->rip);
|
||||
dbgln("RSP: %m", frame->rsp);
|
||||
|
||||
panic("GP");
|
||||
}
|
||||
|
@ -90,7 +92,7 @@ extern "C" void interrupt_page_fault(InterruptFrame* frame) {
|
|||
uint64_t cr2;
|
||||
asm volatile("mov %%cr2, %0" : "=r"(cr2));
|
||||
|
||||
if (gScheduler->CurrentProcess().vmm().HandlePageFault(cr2)) {
|
||||
if (gScheduler->CurrentProcess().vmas()->HandlePageFault(cr2)) {
|
||||
dbgln("Handled");
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
#pragma once
|
||||
|
||||
#include "debug/debug.h"
|
||||
|
||||
template <typename T>
|
||||
class RefPtr;
|
||||
|
||||
|
@ -26,6 +28,7 @@ class RefPtr {
|
|||
ptr_->Acquire();
|
||||
}
|
||||
if (old && old->Release()) {
|
||||
dbgln("Deleting obj %m", old);
|
||||
delete old;
|
||||
}
|
||||
|
||||
|
|
|
@ -62,7 +62,7 @@ uint64_t LoadElfProgram(Process& dest_proc, uint64_t base, uint64_t offset) {
|
|||
program.paddr, program.filesz, program.memsz, program.align);
|
||||
auto mem_obj = MakeRefCounted<MemoryObject>(program.filesz);
|
||||
mem_obj->CopyBytesToObject(base + program.offset, program.filesz);
|
||||
dest_proc.vmm().MapInMemoryObject(program.vaddr, mem_obj);
|
||||
dest_proc.vmas()->MapInMemoryObject(program.vaddr, mem_obj);
|
||||
}
|
||||
return header->entry;
|
||||
}
|
||||
|
|
|
@ -58,7 +58,7 @@ void LoadInitProgram() {
|
|||
|
||||
CopyIntoNonResidentProcess(reinterpret_cast<uint64_t>(prog2.address),
|
||||
prog2.size, *proc,
|
||||
proc->vmm().GetNextMemMapAddr(prog2.size));
|
||||
proc->vmas()->GetNextMemMapAddr(prog2.size));
|
||||
|
||||
proc->CreateThread()->Start(entry, 0, 0);
|
||||
}
|
||||
|
|
|
@ -105,7 +105,7 @@ void CopyPageIntoNonResidentProcess(uint64_t base, uint64_t size,
|
|||
if (dest_virt & 0xFFF) {
|
||||
panic("NR copy to non page aligned");
|
||||
}
|
||||
uint64_t phys = AllocatePageIfNecessary(dest_virt, dest_proc.vmm().cr3());
|
||||
uint64_t phys = AllocatePageIfNecessary(dest_virt, dest_proc.vmas()->cr3());
|
||||
uint8_t* src = reinterpret_cast<uint8_t*>(base);
|
||||
uint8_t* dest =
|
||||
reinterpret_cast<uint8_t*>(phys + boot::GetHigherHalfDirectMap());
|
||||
|
|
|
@ -6,10 +6,10 @@
|
|||
|
||||
extern KernelStackManager* gKernelStackManager;
|
||||
|
||||
AddressSpace AddressSpace::ForRoot() {
|
||||
RefPtr<AddressSpace> AddressSpace::ForRoot() {
|
||||
uint64_t cr3 = 0;
|
||||
asm volatile("mov %%cr3, %0;" : "=r"(cr3));
|
||||
return {cr3};
|
||||
return MakeRefCounted<AddressSpace>(cr3);
|
||||
}
|
||||
|
||||
AddressSpace::AddressSpace() {
|
||||
|
@ -22,6 +22,10 @@ uint64_t AddressSpace::AllocateUserStack() {
|
|||
}
|
||||
|
||||
uint64_t AddressSpace::GetNextMemMapAddr(uint64_t size) {
|
||||
if (size == 0) {
|
||||
panic("Zero size memmap");
|
||||
}
|
||||
size = ((size - 1) & ~0xFFF) + 0x1000;
|
||||
uint64_t addr = next_memmap_addr_;
|
||||
next_memmap_addr_ += size;
|
||||
if (next_memmap_addr_ >= 0x30'00000000) {
|
||||
|
@ -35,6 +39,12 @@ void AddressSpace::MapInMemoryObject(uint64_t vaddr,
|
|||
memory_mappings_.PushBack({.vaddr = vaddr, .mem_obj = mem_obj});
|
||||
}
|
||||
|
||||
uint64_t AddressSpace::MapInMemoryObject(const RefPtr<MemoryObject>& mem_obj) {
|
||||
uint64_t vaddr = GetNextMemMapAddr(mem_obj->size());
|
||||
memory_mappings_.PushBack({.vaddr = vaddr, .mem_obj = mem_obj});
|
||||
return vaddr;
|
||||
}
|
||||
|
||||
uint64_t* AddressSpace::AllocateKernelStack() {
|
||||
return gKernelStackManager->AllocateKernelStack();
|
||||
}
|
||||
|
|
|
@ -24,7 +24,7 @@
|
|||
// 0xFFFFFFFF 40000000 - 0xFFFFFFFF 7FFFFFFF : KERNEL_HEAP (1 GiB)
|
||||
// 0xFFFFFFFF 80000000 - 0xFFFFFFFF 80FFFFFF : KERNEL_CODE (16 MiB)
|
||||
// 0xFFFFFFFF 90000000 - 0xFFFFFFFF 9FFFFFFF : KERNEL_STACK (256 MiB)
|
||||
class AddressSpace {
|
||||
class AddressSpace : public KernelObject {
|
||||
public:
|
||||
enum MemoryType {
|
||||
UNSPECIFIED,
|
||||
|
@ -40,7 +40,7 @@ class AddressSpace {
|
|||
KERNEL_STACK,
|
||||
};
|
||||
|
||||
static AddressSpace ForRoot();
|
||||
static RefPtr<AddressSpace> ForRoot();
|
||||
|
||||
AddressSpace();
|
||||
AddressSpace(const AddressSpace&) = delete;
|
||||
|
@ -56,6 +56,8 @@ class AddressSpace {
|
|||
// Note this is unsafe for now as it may clobber other mappings.
|
||||
void MapInMemoryObject(uint64_t vaddr, const RefPtr<MemoryObject>& mem_obj);
|
||||
|
||||
uint64_t MapInMemoryObject(const RefPtr<MemoryObject>& mem_obj);
|
||||
|
||||
// Kernel Mappings.
|
||||
uint64_t* AllocateKernelStack();
|
||||
|
||||
|
@ -63,6 +65,7 @@ class AddressSpace {
|
|||
bool HandlePageFault(uint64_t vaddr);
|
||||
|
||||
private:
|
||||
friend class MakeRefCountedFriend<AddressSpace>;
|
||||
AddressSpace(uint64_t cr3) : cr3_(cr3) {}
|
||||
uint64_t cr3_ = 0;
|
||||
|
||||
|
|
|
@ -25,10 +25,13 @@ RefPtr<Process> Process::Create() {
|
|||
proc->caps_.PushBack(
|
||||
new Capability(proc, Capability::PROCESS, Z_INIT_PROC_SELF,
|
||||
ZC_PROC_SPAWN_PROC | ZC_PROC_SPAWN_THREAD));
|
||||
proc->caps_.PushBack(new Capability(proc->vmas(), Capability::ADDRESS_SPACE,
|
||||
Z_INIT_AS_SELF, ZC_WRITE));
|
||||
return proc;
|
||||
}
|
||||
|
||||
Process::Process() : id_(gNextId++), state_(RUNNING) {}
|
||||
Process::Process()
|
||||
: id_(gNextId++), vmm_(MakeRefCounted<AddressSpace>()), state_(RUNNING) {}
|
||||
|
||||
RefPtr<Thread> Process::CreateThread() {
|
||||
RefPtr<Thread> thread = MakeRefCounted<Thread>(*this, next_thread_id_++);
|
||||
|
@ -77,3 +80,22 @@ uint64_t Process::AddCapability(const RefPtr<Thread>& thread) {
|
|||
caps_.PushBack(new Capability(thread, Capability::THREAD, cap_id, ZC_WRITE));
|
||||
return cap_id;
|
||||
}
|
||||
|
||||
uint64_t Process::AddCapability(const RefPtr<Process>& p) {
|
||||
uint64_t cap_id = next_cap_id_++;
|
||||
caps_.PushBack(new Capability(p, Capability::PROCESS, cap_id,
|
||||
ZC_WRITE | ZC_PROC_SPAWN_THREAD));
|
||||
return cap_id;
|
||||
}
|
||||
uint64_t Process::AddCapability(const RefPtr<AddressSpace>& as) {
|
||||
uint64_t cap_id = next_cap_id_++;
|
||||
caps_.PushBack(
|
||||
new Capability(as, Capability::ADDRESS_SPACE, cap_id, ZC_WRITE));
|
||||
return cap_id;
|
||||
}
|
||||
uint64_t Process::AddCapability(const RefPtr<MemoryObject>& mo) {
|
||||
uint64_t cap_id = next_cap_id_++;
|
||||
caps_.PushBack(
|
||||
new Capability(mo, Capability::MEMORY_OBJECT, cap_id, ZC_WRITE));
|
||||
return cap_id;
|
||||
}
|
||||
|
|
|
@ -23,13 +23,16 @@ class Process : public KernelObject {
|
|||
static RefPtr<Process> Create();
|
||||
|
||||
uint64_t id() const { return id_; }
|
||||
AddressSpace& vmm() { return vmm_; }
|
||||
RefPtr<AddressSpace> vmas() { return vmm_; }
|
||||
|
||||
RefPtr<Thread> CreateThread();
|
||||
RefPtr<Thread> GetThread(uint64_t tid);
|
||||
|
||||
SharedPtr<Capability> GetCapability(uint64_t cid);
|
||||
uint64_t AddCapability(const RefPtr<Thread>& t);
|
||||
uint64_t AddCapability(const RefPtr<Process>& p);
|
||||
uint64_t AddCapability(const RefPtr<AddressSpace>& as);
|
||||
uint64_t AddCapability(const RefPtr<MemoryObject>& mo);
|
||||
// Checks the state of all child threads and transitions to
|
||||
// finished if all have finished.
|
||||
void CheckState();
|
||||
|
@ -41,7 +44,7 @@ class Process : public KernelObject {
|
|||
Process();
|
||||
Process(uint64_t id) : id_(id), vmm_(AddressSpace::ForRoot()) {}
|
||||
uint64_t id_;
|
||||
AddressSpace vmm_;
|
||||
RefPtr<AddressSpace> vmm_;
|
||||
State state_;
|
||||
|
||||
uint64_t next_thread_id_ = 0;
|
||||
|
|
|
@ -29,7 +29,7 @@ RefPtr<Thread> Thread::Create(Process& proc, uint64_t tid) {
|
|||
}
|
||||
|
||||
Thread::Thread(Process& proc, uint64_t tid) : process_(proc), id_(tid) {
|
||||
uint64_t* stack_ptr = proc.vmm().AllocateKernelStack();
|
||||
uint64_t* stack_ptr = proc.vmas()->AllocateKernelStack();
|
||||
// 0: rip
|
||||
*(stack_ptr) = reinterpret_cast<uint64_t>(thread_init);
|
||||
// 1-4: rax, rcx, rdx, rbx
|
||||
|
@ -37,7 +37,7 @@ Thread::Thread(Process& proc, uint64_t tid) : process_(proc), id_(tid) {
|
|||
*(stack_ptr - 5) = reinterpret_cast<uint64_t>(stack_ptr + 1);
|
||||
// 6-15: rsi, rdi, r8, r9, r10, r11, r12, r13, r14, r15
|
||||
// 16: cr3
|
||||
*(stack_ptr - 16) = proc.vmm().cr3();
|
||||
*(stack_ptr - 16) = proc.vmas()->cr3();
|
||||
rsp0_ = reinterpret_cast<uint64_t>(stack_ptr - 16);
|
||||
rsp0_start_ = reinterpret_cast<uint64_t>(stack_ptr);
|
||||
}
|
||||
|
@ -55,7 +55,7 @@ void Thread::Start(uint64_t entry, uint64_t arg1, uint64_t arg2) {
|
|||
|
||||
void Thread::Init() {
|
||||
dbgln("Thread start.", pid(), id_);
|
||||
uint64_t rsp = process_.vmm().AllocateUserStack();
|
||||
uint64_t rsp = process_.vmas()->AllocateUserStack();
|
||||
SetRsp0(rsp0_start_);
|
||||
jump_user_space(rip_, rsp, arg1_, arg2_);
|
||||
}
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
#include "object/process.h"
|
||||
#include "scheduler/process_manager.h"
|
||||
#include "scheduler/scheduler.h"
|
||||
#include "usr/zcall_internal.h"
|
||||
|
||||
#define EFER 0xC0000080
|
||||
#define STAR 0xC0000081
|
||||
|
@ -46,36 +47,35 @@ void InitSyscall() {
|
|||
}
|
||||
|
||||
uint64_t star_val = GetMSR(STAR);
|
||||
// FIXME: Fix GDT such that we can properly set the user CS.
|
||||
// Due to the ability to jump from a 64 bit kernel into compatibility mode,
|
||||
// we set the user_cs to the kernel_cs because it adds 16 to jump to 64-bit
|
||||
// mode. See AMD Manual 3.4 instruction SYSRET for more info.
|
||||
uint64_t kernel_cs = 0x8;
|
||||
uint64_t user_cs = kernel_cs;
|
||||
// Due to the ability to jump from a 64 bit kernel into compatibility mode,
|
||||
// this will actually use CS 0x20 (and SS 0x18).
|
||||
// See AMD Manual 3.4 instruction SYSRET for more info.
|
||||
uint64_t user_cs = 0x18;
|
||||
star_val |= (kernel_cs << 32) | (user_cs << 48);
|
||||
SetMSR(STAR, star_val);
|
||||
SetMSR(LSTAR, reinterpret_cast<uint64_t>(syscall_enter));
|
||||
}
|
||||
|
||||
uint64_t ProcessSpawnElf(ZProcessSpawnElfReq* req) {
|
||||
uint64_t ProcessSpawn(ZProcessSpawnReq* req, ZProcessSpawnResp* resp) {
|
||||
auto& curr_proc = gScheduler->CurrentProcess();
|
||||
auto cap = curr_proc.GetCapability(req->cap_id);
|
||||
auto cap = curr_proc.GetCapability(req->proc_cap);
|
||||
if (cap.empty()) {
|
||||
return ZE_NOT_FOUND;
|
||||
}
|
||||
if (!cap->CheckType(Capability::PROCESS)) {
|
||||
return ZE_INVALID;
|
||||
}
|
||||
|
||||
if (!cap->HasPermissions(ZC_PROC_SPAWN_PROC)) {
|
||||
return ZE_DENIED;
|
||||
}
|
||||
dbgln("Proc spawn: %u:%u", req->elf_base, req->elf_size);
|
||||
RefPtr<Process> proc = Process::Create();
|
||||
gProcMan->InsertProcess(proc);
|
||||
uint64_t entry = LoadElfProgram(*proc, req->elf_base, req->elf_size);
|
||||
proc->CreateThread()->Start(entry, 0, 0);
|
||||
return 0;
|
||||
|
||||
resp->proc_cap = curr_proc.AddCapability(proc);
|
||||
resp->as_cap = curr_proc.AddCapability(proc->vmas());
|
||||
|
||||
return Z_OK;
|
||||
}
|
||||
|
||||
uint64_t ThreadCreate(ZThreadCreateReq* req, ZThreadCreateResp* resp) {
|
||||
|
@ -92,8 +92,8 @@ uint64_t ThreadCreate(ZThreadCreateReq* req, ZThreadCreateResp* resp) {
|
|||
return ZE_DENIED;
|
||||
}
|
||||
|
||||
Process& parent_proc = cap->obj<Process>();
|
||||
auto thread = parent_proc.CreateThread();
|
||||
auto parent_proc = cap->obj<Process>();
|
||||
auto thread = parent_proc->CreateThread();
|
||||
resp->thread_cap = curr_proc.AddCapability(thread);
|
||||
|
||||
return Z_OK;
|
||||
|
@ -113,9 +113,43 @@ uint64_t ThreadStart(ZThreadStartReq* req) {
|
|||
return ZE_DENIED;
|
||||
}
|
||||
|
||||
Thread& thread = cap->obj<Thread>();
|
||||
auto thread = cap->obj<Thread>();
|
||||
// FIXME: validate entry point is in user space.
|
||||
thread.Start(req->entry, req->arg1, req->arg2);
|
||||
thread->Start(req->entry, req->arg1, req->arg2);
|
||||
return Z_OK;
|
||||
}
|
||||
|
||||
uint64_t AddressSpaceMap(ZAddressSpaceMapReq* req, ZAddressSpaceMapResp* resp) {
|
||||
auto& curr_proc = gScheduler->CurrentProcess();
|
||||
auto as_cap = curr_proc.GetCapability(req->as_cap);
|
||||
auto mem_cap = curr_proc.GetCapability(req->mem_cap);
|
||||
if (as_cap.empty() || mem_cap.empty()) {
|
||||
return ZE_NOT_FOUND;
|
||||
}
|
||||
if (!as_cap->CheckType(Capability::ADDRESS_SPACE) ||
|
||||
!mem_cap->CheckType(Capability::MEMORY_OBJECT)) {
|
||||
return ZE_INVALID;
|
||||
}
|
||||
if (!as_cap->HasPermissions(ZC_WRITE) || !mem_cap->HasPermissions(ZC_WRITE)) {
|
||||
return ZE_DENIED;
|
||||
}
|
||||
auto as = as_cap->obj<AddressSpace>();
|
||||
auto mo = mem_cap->obj<MemoryObject>();
|
||||
// FIXME: Validation necessary.
|
||||
if (req->offset != 0) {
|
||||
as->MapInMemoryObject(req->offset, mo);
|
||||
resp->vaddr = req->offset;
|
||||
} else {
|
||||
resp->vaddr = as->MapInMemoryObject(mo);
|
||||
}
|
||||
}
|
||||
|
||||
uint64_t MemoryObjectCreate(ZMemoryObjectCreateReq* req,
|
||||
ZMemoryObjectCreateResp* resp) {
|
||||
auto& curr_proc = gScheduler->CurrentProcess();
|
||||
resp->mem_cap =
|
||||
curr_proc.AddCapability(MakeRefCounted<MemoryObject>(req->size));
|
||||
return Z_OK;
|
||||
}
|
||||
|
||||
extern "C" uint64_t SyscallHandler(uint64_t call_id, void* req, void* resp) {
|
||||
|
@ -123,14 +157,13 @@ extern "C" uint64_t SyscallHandler(uint64_t call_id, void* req, void* resp) {
|
|||
switch (call_id) {
|
||||
case Z_PROCESS_EXIT:
|
||||
// FIXME: kill process here.
|
||||
dbgln("Exit code: %u", req);
|
||||
thread.Exit();
|
||||
panic("Returned from thread exit");
|
||||
break;
|
||||
case Z_DEBUG_PRINT:
|
||||
dbgln("[Debug] %s", req);
|
||||
break;
|
||||
case Z_PROCESS_SPAWN:
|
||||
return ProcessSpawnElf(reinterpret_cast<ZProcessSpawnElfReq*>(req));
|
||||
return ProcessSpawn(reinterpret_cast<ZProcessSpawnReq*>(req),
|
||||
reinterpret_cast<ZProcessSpawnResp*>(resp));
|
||||
case Z_THREAD_CREATE:
|
||||
return ThreadCreate(reinterpret_cast<ZThreadCreateReq*>(req),
|
||||
reinterpret_cast<ZThreadCreateResp*>(resp));
|
||||
|
@ -140,6 +173,17 @@ extern "C" uint64_t SyscallHandler(uint64_t call_id, void* req, void* resp) {
|
|||
thread.Exit();
|
||||
panic("Returned from thread exit");
|
||||
break;
|
||||
|
||||
case Z_ADDRESS_SPACE_MAP:
|
||||
return AddressSpaceMap(reinterpret_cast<ZAddressSpaceMapReq*>(req),
|
||||
reinterpret_cast<ZAddressSpaceMapResp*>(resp));
|
||||
case Z_MEMORY_OBJECT_CREATE:
|
||||
return MemoryObjectCreate(
|
||||
reinterpret_cast<ZMemoryObjectCreateReq*>(req),
|
||||
reinterpret_cast<ZMemoryObjectCreateResp*>(resp));
|
||||
case Z_DEBUG_PRINT:
|
||||
dbgln("[Debug] %s", req);
|
||||
break;
|
||||
default:
|
||||
panic("Unhandled syscall number: %x", call_id);
|
||||
}
|
||||
|
|
|
@ -6,5 +6,8 @@ _start:
|
|||
call _exit
|
||||
|
||||
_exit:
|
||||
// EXIT syscall.
|
||||
mov $1, %rdi
|
||||
// Return code as a param.
|
||||
mov %rax, %rsi
|
||||
syscall
|
||||
|
|
|
@ -2,6 +2,8 @@
|
|||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "usr/zcall_internal.h"
|
||||
|
||||
uint64_t SysCall0(uint64_t number) {
|
||||
uint64_t return_code;
|
||||
asm("syscall" : "=a"(return_code) : "D"(number));
|
||||
|
@ -23,18 +25,20 @@ uint64_t SysCall2(uint64_t number, const void* first, const void* second) {
|
|||
return return_code;
|
||||
}
|
||||
|
||||
uint64_t ZDebug(const char* message) {
|
||||
return SysCall1(Z_DEBUG_PRINT, message);
|
||||
void ZProcessExit(uint64_t code) {
|
||||
SysCall1(Z_PROCESS_EXIT, reinterpret_cast<void*>(code));
|
||||
}
|
||||
|
||||
uint64_t ZProcessSpawnElf(uint64_t cap_id, uint64_t elf_base,
|
||||
uint64_t elf_size) {
|
||||
ZProcessSpawnElfReq req{
|
||||
.cap_id = cap_id,
|
||||
.elf_base = elf_base,
|
||||
.elf_size = elf_size,
|
||||
uint64_t ZProcessSpawn(uint64_t proc_cap, uint64_t* new_proc_cap,
|
||||
uint64_t* new_as_cap) {
|
||||
ZProcessSpawnReq req{
|
||||
.proc_cap = proc_cap,
|
||||
};
|
||||
return SysCall1(Z_PROCESS_SPAWN, &req);
|
||||
ZProcessSpawnResp resp;
|
||||
uint64_t ret = SysCall2(Z_PROCESS_SPAWN, &req, &resp);
|
||||
*new_proc_cap = resp.proc_cap;
|
||||
*new_as_cap = resp.as_cap;
|
||||
return ret;
|
||||
}
|
||||
|
||||
uint64_t ZThreadCreate(uint64_t proc_cap, uint64_t* thread_cap) {
|
||||
|
@ -59,3 +63,29 @@ uint64_t ZThreadStart(uint64_t thread_cap, uint64_t entry, uint64_t arg1,
|
|||
}
|
||||
|
||||
void ZThreadExit() { SysCall0(Z_THREAD_EXIT); }
|
||||
|
||||
uint64_t ZAddressSpaceMap(uint64_t as_cap, uint64_t offset, uint64_t mem_cap,
|
||||
uint64_t* vaddr) {
|
||||
ZAddressSpaceMapReq req{
|
||||
.as_cap = as_cap,
|
||||
.offset = offset,
|
||||
.mem_cap = mem_cap,
|
||||
};
|
||||
ZAddressSpaceMapResp resp;
|
||||
uint64_t ret = SysCall2(Z_ADDRESS_SPACE_MAP, &req, &resp);
|
||||
*vaddr = resp.vaddr;
|
||||
return ret;
|
||||
}
|
||||
uint64_t ZMemoryObjectCreate(uint64_t size, uint64_t* mem_cap) {
|
||||
ZMemoryObjectCreateReq req{
|
||||
.size = size,
|
||||
};
|
||||
ZMemoryObjectCreateResp resp;
|
||||
uint64_t ret = SysCall2(Z_MEMORY_OBJECT_CREATE, &req, &resp);
|
||||
*mem_cap = resp.mem_cap;
|
||||
return ret;
|
||||
}
|
||||
|
||||
uint64_t ZDebug(const char* message) {
|
||||
return SysCall1(Z_DEBUG_PRINT, message);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,45 @@
|
|||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
struct ZProcessSpawnReq {
|
||||
uint64_t proc_cap;
|
||||
};
|
||||
|
||||
struct ZProcessSpawnResp {
|
||||
uint64_t proc_cap;
|
||||
uint64_t as_cap;
|
||||
};
|
||||
|
||||
struct ZThreadCreateReq {
|
||||
uint64_t proc_cap;
|
||||
};
|
||||
|
||||
struct ZThreadCreateResp {
|
||||
uint64_t thread_cap;
|
||||
};
|
||||
|
||||
struct ZThreadStartReq {
|
||||
uint64_t thread_cap;
|
||||
uint64_t entry;
|
||||
uint64_t arg1;
|
||||
uint64_t arg2;
|
||||
};
|
||||
|
||||
struct ZAddressSpaceMapReq {
|
||||
uint64_t as_cap;
|
||||
uint64_t offset;
|
||||
uint64_t mem_cap;
|
||||
};
|
||||
|
||||
struct ZAddressSpaceMapResp {
|
||||
uint64_t vaddr;
|
||||
};
|
||||
|
||||
struct ZMemoryObjectCreateReq {
|
||||
uint64_t size;
|
||||
};
|
||||
|
||||
struct ZMemoryObjectCreateResp {
|
||||
uint64_t mem_cap;
|
||||
};
|
Loading…
Reference in New Issue