Compare commits
111 Commits
e145690db8
...
97461c7c1f
Author | SHA1 | Date | |
---|---|---|---|
97461c7c1f | |||
ccf8f404a8 | |||
4e08c9d8ce | |||
0e64c57e10 | |||
3b83d7ccaf | |||
e0aa552fae | |||
f82fbbe60c | |||
c8a92af4d2 | |||
e11280ad3f | |||
1c3377fc98 | |||
e43777bd31 | |||
97a8a4a4a2 | |||
81f56083c5 | |||
cdb73836b0 | |||
bb7887a29d | |||
177282d79c | |||
5abd8814e3 | |||
e21b608af4 | |||
4f13be7741 | |||
1707739477 | |||
13a5c0e445 | |||
9b3c7816a3 | |||
26211bd49f | |||
0c7c249935 | |||
5d41b4b113 | |||
24272c57ef | |||
50d52c9a6b | |||
83982a24e2 | |||
ee712432bd | |||
229b06c63b | |||
522aa2f812 | |||
83e6b8cd21 | |||
57482e4e93 | |||
b360307f41 | |||
9f2c9fb190 | |||
531b2848ac | |||
b0e1b8a2b2 | |||
2dd3a23092 | |||
743aedcd49 | |||
be9026442e | |||
de6041fede | |||
52944ba5d8 | |||
69a9f7f06a | |||
5f8376409d | |||
e37ff67da2 | |||
9cddf9485d | |||
4091799701 | |||
ea886f58a0 | |||
8a7ddfca80 | |||
1a54342454 | |||
d4c4c0177d | |||
3ac9fed23a | |||
261fc73146 | |||
7a2e313a20 | |||
e9df5fd663 | |||
bcbf43e55c | |||
531afc3d6f | |||
f8b3567042 | |||
34c35163e4 | |||
dfdc3b2d11 | |||
25a460e3c6 | |||
136c0b3ae9 | |||
4e3ef9593d | |||
a6f0a7056f | |||
edda41a7bb | |||
4a5db1dca7 | |||
928ade123c | |||
e40304f2f1 | |||
854f585e1a | |||
97b7572933 | |||
0f5910add7 | |||
baa71b09cc | |||
4768d5fc12 | |||
bbd9f1d187 | |||
15f340dbbe | |||
8daffa876c | |||
28469497e9 | |||
d3cb642e5f | |||
0ee9bd7290 | |||
eaea4603c6 | |||
4021cb3ac0 | |||
ad9c7af0bf | |||
950f4ef608 | |||
525d567af6 | |||
c9ebe89899 | |||
cf160d1260 | |||
66add380cf | |||
cf3f61e373 | |||
c1f9d3323f | |||
97eacc027e | |||
f5deb1048a | |||
b3e16068ef | |||
e90b90c556 | |||
a8eb7a6b66 | |||
d89685bb36 | |||
1e16a78106 | |||
a3362429d3 | |||
0d3e7d4463 | |||
b42c866db8 | |||
9f5b3b76d2 | |||
f44411aa46 | |||
6aabe19fb4 | |||
ffc223c2cf | |||
c8c4d31cca | |||
19ee20b6f5 | |||
38e87d8f8f | |||
96b1d1c2f2 | |||
eb03ae91e0 | |||
89cbe5957b | |||
44834b8a0f | |||
eaf7a1620b |
2
.gdbconf
2
.gdbconf
@ -1,4 +1,4 @@
|
|||||||
file initrd/boot/moon.elf
|
file kernel/bin/moon.elf
|
||||||
break _start
|
break _start
|
||||||
target remote :1234
|
target remote :1234
|
||||||
continue
|
continue
|
@ -6,7 +6,7 @@ Not so much at the moment.
|
|||||||
|
|
||||||
- x86_64-compatible [kernel](kernel/).
|
- x86_64-compatible [kernel](kernel/).
|
||||||
- Keeps track of which [memory](kernel/src/memory/) is used and which memory is free, and can allocate memory for itself and [user programs](kernel/src/sys/mem.cpp).
|
- Keeps track of which [memory](kernel/src/memory/) is used and which memory is free, and can allocate memory for itself and [user programs](kernel/src/sys/mem.cpp).
|
||||||
- Can [load files](kernel/src/init/InitRD.cpp) from an [initial ramdisk](initrd/), no disk support yet.
|
- Can [load files](kernel/src/fs/) from an [initial ramdisk](initrd/), no disk support yet.
|
||||||
- Basic preemptive multitasking, round-robin [scheduler](kernel/src/thread/) that can switch between tasks.
|
- Basic preemptive multitasking, round-robin [scheduler](kernel/src/thread/) that can switch between tasks.
|
||||||
- Can [load userspace ELF programs](kernel/src/sys/elf/) from the initial ramdisk as user tasks.
|
- Can [load userspace ELF programs](kernel/src/sys/elf/) from the initial ramdisk as user tasks.
|
||||||
- [System call](kernel/src/sys/) interface and bare-bones [C Library](libs/libc/).
|
- [System call](kernel/src/sys/) interface and bare-bones [C Library](libs/libc/).
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
APPS := init
|
APPS := init sym
|
||||||
|
|
||||||
APPS_DIR := $(LUNA_ROOT)/apps
|
APPS_DIR := $(LUNA_ROOT)/apps
|
||||||
APPS_SRC := $(APPS_DIR)/src
|
APPS_SRC := $(APPS_DIR)/src
|
||||||
|
@ -43,7 +43,25 @@ int main()
|
|||||||
{
|
{
|
||||||
if (gettid() == 0) // why are we the idle task?
|
if (gettid() == 0) // why are we the idle task?
|
||||||
{
|
{
|
||||||
__luna_abort("SHENANIGANS! init is tid 0 (which is reserved for the idle task)\n");
|
fprintf(stderr, "SHENANIGANS! init is tid 0 (which is reserved for the idle task)\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
FILE* serial = fopen("/dev/serial", "r");
|
||||||
|
if (!serial)
|
||||||
|
{
|
||||||
|
perror("fopen");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if (fputs("Hello from init!\n", serial) < 0)
|
||||||
|
{
|
||||||
|
perror("fputs");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if (fclose(serial) < 0)
|
||||||
|
{
|
||||||
|
perror("fclose");
|
||||||
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
printf("Welcome to Luna!\n");
|
printf("Welcome to Luna!\n");
|
||||||
@ -67,6 +85,23 @@ int main()
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (fseek(config, 0, SEEK_END) < 0)
|
||||||
|
{
|
||||||
|
perror("fseek");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
long offset = ftell(config);
|
||||||
|
if (offset < 0)
|
||||||
|
{
|
||||||
|
perror("ftell");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("%s is %ld bytes long\n", filename, offset);
|
||||||
|
|
||||||
|
rewind(config);
|
||||||
|
|
||||||
char buf[4096];
|
char buf[4096];
|
||||||
|
|
||||||
size_t nread = fread(buf, sizeof(buf), 1, config);
|
size_t nread = fread(buf, sizeof(buf), 1, config);
|
||||||
@ -82,7 +117,9 @@ int main()
|
|||||||
|
|
||||||
if (fclose(config) < 0) { perror("fclose"); }
|
if (fclose(config) < 0) { perror("fclose"); }
|
||||||
|
|
||||||
printf("\n\nPress any key to restart.\n");
|
sleep(2);
|
||||||
|
|
||||||
|
printf("\n\nPress any key to restart.\n\n");
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
38
apps/src/sym.c
Normal file
38
apps/src/sym.c
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
sleep(6);
|
||||||
|
|
||||||
|
FILE* syms = fopen("/sys/moon.sym", "r");
|
||||||
|
if (!syms)
|
||||||
|
{
|
||||||
|
perror("fopen");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
char buf[1025];
|
||||||
|
|
||||||
|
if (fseek(syms, 8000, SEEK_SET) < 0)
|
||||||
|
{
|
||||||
|
perror("fseek");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t nread = fread(buf, 1024, 1, syms);
|
||||||
|
if (ferror(syms))
|
||||||
|
{
|
||||||
|
perror("fread");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
buf[nread] = 0;
|
||||||
|
|
||||||
|
printf("%s\n", strchr(buf, '\n') + 1);
|
||||||
|
|
||||||
|
fclose(syms);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
@ -3,7 +3,8 @@ MOON_SRC := $(MOON_DIR)/src
|
|||||||
MOON_OBJ := $(MOON_DIR)/lib
|
MOON_OBJ := $(MOON_DIR)/lib
|
||||||
MOON_BIN := $(MOON_DIR)/bin
|
MOON_BIN := $(MOON_DIR)/bin
|
||||||
|
|
||||||
CFLAGS := -pedantic -Wall -Wextra -Werror -Wfloat-equal -Wdisabled-optimization -Wformat=2 -Winit-self -Wmissing-include-dirs -Wswitch-default -Wcast-qual -Wundef -Wcast-align -Wwrite-strings -Wlogical-op -Wredundant-decls -Wshadow -Wconversion -Os -ffreestanding -fstack-protector-all -fno-omit-frame-pointer -mno-red-zone -mno-mmx -mno-sse -mno-sse2 -fshort-wchar -mcmodel=kernel -I$(MOON_DIR)/include -isystem $(MOON_DIR)/include/std
|
CFLAGS ?= -Os
|
||||||
|
CFLAGS := ${CFLAGS} -pedantic -Wall -Wextra -Werror -Wfloat-equal -Wdisabled-optimization -Wformat=2 -Winit-self -Wmissing-include-dirs -Wswitch-default -Wcast-qual -Wundef -Wcast-align -Wwrite-strings -Wlogical-op -Wredundant-decls -Wshadow -Wconversion -ffreestanding -fstack-protector-strong -fno-omit-frame-pointer -mno-red-zone -mno-mmx -mno-sse -mno-sse2 -fshort-wchar -mcmodel=kernel -I$(MOON_DIR)/include -isystem $(MOON_DIR)/include/std
|
||||||
CXXFLAGS := -fno-rtti -fno-exceptions -Wsign-promo -Wstrict-null-sentinel -Wctor-dtor-privacy
|
CXXFLAGS := -fno-rtti -fno-exceptions -Wsign-promo -Wstrict-null-sentinel -Wctor-dtor-privacy
|
||||||
ASMFLAGS := -felf64
|
ASMFLAGS := -felf64
|
||||||
LDFLAGS := -T$(MOON_DIR)/moon.ld -nostdlib -lgcc -Wl,--build-id=none -z max-page-size=0x1000 -mno-red-zone -mcmodel=kernel
|
LDFLAGS := -T$(MOON_DIR)/moon.ld -nostdlib -lgcc -Wl,--build-id=none -z max-page-size=0x1000 -mno-red-zone -mcmodel=kernel
|
||||||
|
@ -2,8 +2,10 @@
|
|||||||
|
|
||||||
#define EPERM 1
|
#define EPERM 1
|
||||||
#define ENOENT 2
|
#define ENOENT 2
|
||||||
|
#define ENOEXEC 8
|
||||||
#define EBADF 9
|
#define EBADF 9
|
||||||
#define ENOMEM 12
|
#define ENOMEM 12
|
||||||
|
#define EFAULT 14
|
||||||
#define EISDIR 21
|
#define EISDIR 21
|
||||||
#define EINVAL 22
|
#define EINVAL 22
|
||||||
#define EMFILE 24
|
#define EMFILE 24
|
||||||
|
@ -29,6 +29,17 @@ struct Descriptor
|
|||||||
|
|
||||||
void open(VFS::Node* node, bool can_read, bool can_write);
|
void open(VFS::Node* node, bool can_read, bool can_write);
|
||||||
|
|
||||||
|
int seek(long offset);
|
||||||
|
long offset()
|
||||||
|
{
|
||||||
|
return (long)m_offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned long length()
|
||||||
|
{
|
||||||
|
return m_node->length;
|
||||||
|
}
|
||||||
|
|
||||||
Descriptor(const Descriptor& other);
|
Descriptor(const Descriptor& other);
|
||||||
Descriptor();
|
Descriptor();
|
||||||
|
|
||||||
|
@ -6,6 +6,7 @@ typedef long ssize_t;
|
|||||||
|
|
||||||
#define VFS_FILE 0x0
|
#define VFS_FILE 0x0
|
||||||
#define VFS_DIRECTORY 0x1
|
#define VFS_DIRECTORY 0x1
|
||||||
|
#define VFS_DEVICE 0x2
|
||||||
|
|
||||||
#define VFS_MOUNTPOINT 0x1
|
#define VFS_MOUNTPOINT 0x1
|
||||||
|
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
|
|
||||||
namespace ConsoleDevice
|
namespace ConsoleDevice
|
||||||
{
|
{
|
||||||
VFS::Node* create_new();
|
VFS::Node* create_new(const char* devname);
|
||||||
|
|
||||||
ssize_t write(VFS::Node* node, size_t offset, size_t size, const char* buffer);
|
ssize_t write(VFS::Node* node, size_t offset, size_t size, const char* buffer);
|
||||||
}
|
}
|
9
kernel/include/fs/devices/Serial.h
Normal file
9
kernel/include/fs/devices/Serial.h
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
#pragma once
|
||||||
|
#include "fs/VFS.h"
|
||||||
|
|
||||||
|
namespace SerialDevice
|
||||||
|
{
|
||||||
|
VFS::Node* create_new(const char* devname);
|
||||||
|
|
||||||
|
ssize_t write(VFS::Node* node, size_t offset, size_t size, const char* buffer);
|
||||||
|
}
|
@ -3,7 +3,7 @@
|
|||||||
|
|
||||||
namespace VersionDevice
|
namespace VersionDevice
|
||||||
{
|
{
|
||||||
VFS::Node* create_new();
|
VFS::Node* create_new(const char* devname);
|
||||||
|
|
||||||
ssize_t read(VFS::Node* node, size_t offset, size_t size, char* buffer);
|
ssize_t read(VFS::Node* node, size_t offset, size_t size, char* buffer);
|
||||||
}
|
}
|
@ -8,4 +8,10 @@ namespace Interrupts
|
|||||||
|
|
||||||
bool is_in_handler();
|
bool is_in_handler();
|
||||||
void return_from_handler(Context* context);
|
void return_from_handler(Context* context);
|
||||||
|
|
||||||
|
bool are_enabled();
|
||||||
|
bool were_enabled();
|
||||||
|
void push_and_disable();
|
||||||
|
void push_and_enable();
|
||||||
|
void pop();
|
||||||
}
|
}
|
27
kernel/include/memory/AddressSpace.h
Normal file
27
kernel/include/memory/AddressSpace.h
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
#pragma once
|
||||||
|
#include "memory/Paging.h"
|
||||||
|
|
||||||
|
struct AddressSpace
|
||||||
|
{
|
||||||
|
static AddressSpace create();
|
||||||
|
|
||||||
|
void destroy();
|
||||||
|
|
||||||
|
void detach();
|
||||||
|
|
||||||
|
AddressSpace clone();
|
||||||
|
|
||||||
|
PageTable* get_pml4()
|
||||||
|
{
|
||||||
|
return m_pml4;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool is_cloned()
|
||||||
|
{
|
||||||
|
return m_cloned;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
PageTable* m_pml4;
|
||||||
|
bool m_cloned;
|
||||||
|
};
|
@ -29,4 +29,6 @@ namespace MemoryManager
|
|||||||
|
|
||||||
void release_page(void* page);
|
void release_page(void* page);
|
||||||
void release_pages(void* pages, uint64_t count);
|
void release_pages(void* pages, uint64_t count);
|
||||||
|
|
||||||
|
void protect(void* page, uint64_t count, int flags);
|
||||||
}
|
}
|
@ -5,27 +5,25 @@
|
|||||||
#define PAGE_SIZE 4096
|
#define PAGE_SIZE 4096
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
namespace Paging
|
struct PageDirectoryEntry
|
||||||
{
|
{
|
||||||
struct PageDirectoryEntry
|
bool present : 1;
|
||||||
{
|
bool read_write : 1;
|
||||||
bool Present : 1;
|
bool user : 1;
|
||||||
bool ReadWrite : 1;
|
bool write_through : 1;
|
||||||
bool UserSuper : 1;
|
bool cache_disabled : 1;
|
||||||
bool WriteThrough : 1;
|
bool accessed : 1;
|
||||||
bool CacheDisabled : 1;
|
bool ignore0 : 1;
|
||||||
bool Accessed : 1;
|
bool larger_pages : 1;
|
||||||
bool ignore0 : 1;
|
bool ignore1 : 1;
|
||||||
bool LargerPages : 1;
|
uint8_t available : 3;
|
||||||
bool ignore1 : 1;
|
uint64_t address : 52;
|
||||||
uint8_t Available : 3;
|
|
||||||
uint64_t Address : 52;
|
|
||||||
|
|
||||||
void set_address(uint64_t addr);
|
void set_address(uint64_t addr);
|
||||||
};
|
uint64_t get_address();
|
||||||
|
};
|
||||||
|
|
||||||
struct PageTable
|
struct PageTable
|
||||||
{
|
{
|
||||||
PageDirectoryEntry entries[512];
|
PageDirectoryEntry entries[512];
|
||||||
} __attribute__((aligned(0x1000)));
|
} __attribute__((aligned(PAGE_SIZE)));
|
||||||
}
|
|
@ -1,29 +1,45 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
#include "memory/AddressSpace.h"
|
||||||
#include "memory/Paging.h"
|
#include "memory/Paging.h"
|
||||||
|
|
||||||
namespace Paging
|
enum Flags
|
||||||
{
|
{
|
||||||
enum Flags
|
ReadWrite = 1 << 0,
|
||||||
{
|
User = 1 << 1,
|
||||||
ReadWrite = 1 << 0,
|
Execute = 1 << 2
|
||||||
User = 1 << 1,
|
};
|
||||||
Execute = 1 << 2
|
namespace VMM
|
||||||
};
|
{
|
||||||
class VirtualMemoryManager
|
void init(); // Fetch page table from cr3
|
||||||
{
|
|
||||||
public:
|
|
||||||
void init(); // fetch page table from cr3
|
|
||||||
void init(PageTable* cr3);
|
|
||||||
|
|
||||||
void map(uint64_t virtualAddress, uint64_t physicalAddress, int flags);
|
void switch_to_user_address_space(AddressSpace& space);
|
||||||
void remap(uint64_t virtualAddress, int flags);
|
void switch_to_previous_user_address_space();
|
||||||
void unmap(uint64_t virtualAddress);
|
void switch_back_to_kernel_address_space();
|
||||||
uint64_t getPhysical(uint64_t virtualAddress);
|
|
||||||
uint64_t getFlags(uint64_t virtualAddress);
|
|
||||||
|
|
||||||
private:
|
void enter_syscall_context();
|
||||||
PageTable* PML4;
|
void exit_syscall_context();
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
extern Paging::VirtualMemoryManager kernelVMM;
|
void apply_address_space();
|
||||||
|
|
||||||
|
bool is_using_kernel_address_space();
|
||||||
|
|
||||||
|
void map(uint64_t vaddr, uint64_t paddr, int flags);
|
||||||
|
void remap(uint64_t vaddr, int flags);
|
||||||
|
void unmap(uint64_t vaddr);
|
||||||
|
uint64_t get_physical(uint64_t vaddr);
|
||||||
|
uint64_t get_flags(uint64_t vaddr);
|
||||||
|
|
||||||
|
PageDirectoryEntry* find_pde(PageTable* root, uint64_t vaddr);
|
||||||
|
PageDirectoryEntry* create_pde_if_not_exists(PageTable* root, uint64_t vaddr);
|
||||||
|
|
||||||
|
void propagate_read_write(PageTable* root, uint64_t vaddr);
|
||||||
|
void propagate_user(PageTable* root, uint64_t vaddr);
|
||||||
|
|
||||||
|
void flush_tlb(uint64_t addr);
|
||||||
|
|
||||||
|
void decompose_vaddr(uint64_t vaddr, uint64_t& page_index, uint64_t& pt_index, uint64_t& pd_index,
|
||||||
|
uint64_t& pdp_index);
|
||||||
|
uint64_t recompose_vaddr(uint64_t page_index, uint64_t pt_index, uint64_t pd_index, uint64_t pdp_index);
|
||||||
|
|
||||||
|
void install_kernel_page_directory_into_address_space(AddressSpace& space);
|
||||||
|
};
|
@ -3,6 +3,8 @@
|
|||||||
|
|
||||||
namespace Utilities
|
namespace Utilities
|
||||||
{
|
{
|
||||||
uint64_t get_blocks_from_size(uint64_t blocksize,
|
inline uint64_t get_blocks_from_size(uint64_t blocksize, uint64_t size)
|
||||||
uint64_t size); // Returns how many blocks of size blocksize does size occupy.
|
{
|
||||||
|
return (size + (blocksize - 1)) / blocksize;
|
||||||
|
}
|
||||||
}
|
}
|
@ -17,3 +17,5 @@ void* memcpy(void* dest, const void* src, size_t n);
|
|||||||
void* memset(void* dest, int c, size_t n);
|
void* memset(void* dest, int c, size_t n);
|
||||||
int memcmp(const void* a, const void* b, size_t n);
|
int memcmp(const void* a, const void* b, size_t n);
|
||||||
void* memmove(void* dest, void* src, size_t n);
|
void* memmove(void* dest, void* src, size_t n);
|
||||||
|
|
||||||
|
char* strdup(const char* src);
|
@ -14,10 +14,14 @@
|
|||||||
#define SYS_open 9
|
#define SYS_open 9
|
||||||
#define SYS_read 10
|
#define SYS_read 10
|
||||||
#define SYS_close 11
|
#define SYS_close 11
|
||||||
|
#define SYS_seek 12
|
||||||
|
#define SYS_exec 13
|
||||||
|
|
||||||
namespace Syscall
|
namespace Syscall
|
||||||
{
|
{
|
||||||
void entry(Context* context);
|
void entry(Context* context);
|
||||||
|
|
||||||
|
char* strdup_from_user(const char* user_string);
|
||||||
}
|
}
|
||||||
|
|
||||||
void sys_exit(Context* context, int status);
|
void sys_exit(Context* context, int status);
|
||||||
@ -32,3 +36,5 @@ void sys_munmap(Context* context, void* address, size_t size);
|
|||||||
void sys_open(Context* context, const char* filename, int flags);
|
void sys_open(Context* context, const char* filename, int flags);
|
||||||
void sys_read(Context* context, int fd, size_t size, char* buffer);
|
void sys_read(Context* context, int fd, size_t size, char* buffer);
|
||||||
void sys_close(Context* context, int fd);
|
void sys_close(Context* context, int fd);
|
||||||
|
void sys_seek(Context* context, int fd, long offset, int whence);
|
||||||
|
void sys_exec(Context* context, const char* pathname);
|
@ -1,9 +1,13 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
#include "fs/VFS.h"
|
||||||
#include "sys/elf/Image.h"
|
#include "sys/elf/Image.h"
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
namespace ELFLoader
|
namespace ELFLoader
|
||||||
{
|
{
|
||||||
ELFImage* load_elf_from_address(uintptr_t addr);
|
ELFImage* load_elf_from_vfs(VFS::Node* node); // This function assumes check_elf_image has been called first.
|
||||||
ELFImage* load_elf_from_filesystem(const char* filename);
|
ELFImage* load_elf_from_filesystem(const char* filename);
|
||||||
|
void release_elf_image(ELFImage* image);
|
||||||
|
long check_elf_image(VFS::Node* node);
|
||||||
|
long check_elf_image_from_filesystem(const char* filename);
|
||||||
}
|
}
|
@ -1,6 +1,8 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include "thread/Task.h"
|
#include "thread/Task.h"
|
||||||
|
|
||||||
|
#define TASK_PAGES_IN_STACK 4
|
||||||
|
|
||||||
namespace Scheduler
|
namespace Scheduler
|
||||||
{
|
{
|
||||||
void init();
|
void init();
|
||||||
@ -8,7 +10,8 @@ namespace Scheduler
|
|||||||
void exit(int status);
|
void exit(int status);
|
||||||
void sleep(unsigned long ms);
|
void sleep(unsigned long ms);
|
||||||
void add_kernel_task(void (*task)(void));
|
void add_kernel_task(void (*task)(void));
|
||||||
void add_user_task(void* task);
|
|
||||||
|
Task* create_user_task();
|
||||||
|
|
||||||
void load_user_task(const char* filename);
|
void load_user_task(const char* filename);
|
||||||
|
|
||||||
@ -22,4 +25,6 @@ namespace Scheduler
|
|||||||
|
|
||||||
void reap_task(Task* task);
|
void reap_task(Task* task);
|
||||||
void reap_tasks();
|
void reap_tasks();
|
||||||
|
|
||||||
|
void reset_task(Task* task, ELFImage* new_image);
|
||||||
}
|
}
|
@ -1,6 +1,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include "fs/FileDescriptor.h"
|
#include "fs/FileDescriptor.h"
|
||||||
#include "interrupts/Context.h"
|
#include "interrupts/Context.h"
|
||||||
|
#include "memory/AddressSpace.h"
|
||||||
#include "sys/elf/Image.h"
|
#include "sys/elf/Image.h"
|
||||||
|
|
||||||
#define TASK_MAX_FDS 8
|
#define TASK_MAX_FDS 8
|
||||||
@ -36,11 +37,15 @@ struct Task
|
|||||||
char floating_region[512] __attribute__((aligned(16)));
|
char floating_region[512] __attribute__((aligned(16)));
|
||||||
bool floating_saved = false;
|
bool floating_saved = false;
|
||||||
|
|
||||||
|
bool user_task = true;
|
||||||
|
|
||||||
bool is_user_task();
|
bool is_user_task();
|
||||||
|
|
||||||
ELFImage* image = nullptr;
|
ELFImage* image = nullptr;
|
||||||
|
|
||||||
Descriptor files[TASK_MAX_FDS];
|
Descriptor files[TASK_MAX_FDS];
|
||||||
|
|
||||||
|
AddressSpace address_space;
|
||||||
};
|
};
|
||||||
|
|
||||||
void set_context_from_task(Task& task, Context* ctx);
|
void set_context_from_task(Task& task, Context* ctx);
|
||||||
|
24
kernel/include/utils/Addresses.h
Normal file
24
kernel/include/utils/Addresses.h
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#ifndef PAGE_SIZE
|
||||||
|
#define PAGE_SIZE 4096
|
||||||
|
#endif
|
||||||
|
|
||||||
|
inline uintptr_t get_top_of_stack(uintptr_t bottom, size_t stack_pages)
|
||||||
|
{
|
||||||
|
return bottom + (stack_pages * PAGE_SIZE) - sizeof(uintptr_t);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline uintptr_t round_down_to_nearest_page(uintptr_t addr)
|
||||||
|
{
|
||||||
|
return addr - (addr % PAGE_SIZE);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline uintptr_t round_up_to_nearest_page(uintptr_t addr)
|
||||||
|
{
|
||||||
|
if (addr % PAGE_SIZE) return addr + (PAGE_SIZE - (addr % PAGE_SIZE));
|
||||||
|
return addr;
|
||||||
|
}
|
52
kernel/include/utils/Registers.h
Normal file
52
kernel/include/utils/Registers.h
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
extern "C" uintptr_t asm_get_rflags();
|
||||||
|
extern "C" void asm_set_rflags(uintptr_t);
|
||||||
|
|
||||||
|
inline uintptr_t read_rflags()
|
||||||
|
{
|
||||||
|
return asm_get_rflags();
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void write_rflags(uintptr_t value)
|
||||||
|
{
|
||||||
|
asm_set_rflags(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline uintptr_t read_cr0()
|
||||||
|
{
|
||||||
|
uintptr_t value;
|
||||||
|
asm volatile("mov %%cr0, %0" : "=r"(value));
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline uintptr_t read_cr3()
|
||||||
|
{
|
||||||
|
uintptr_t value;
|
||||||
|
asm volatile("mov %%cr3, %0" : "=r"(value));
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline uintptr_t read_cr4()
|
||||||
|
{
|
||||||
|
uintptr_t value;
|
||||||
|
asm volatile("mov %%cr4, %0" : "=r"(value));
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T> inline void write_cr0(T value)
|
||||||
|
{
|
||||||
|
asm volatile("mov %0, %%cr0" : : "r"(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T> inline void write_cr3(T value)
|
||||||
|
{
|
||||||
|
asm volatile("mov %0, %%cr3" : : "r"(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T> inline void write_cr4(T value)
|
||||||
|
{
|
||||||
|
asm volatile("mov %0, %%cr4" : : "r"(value));
|
||||||
|
}
|
6
kernel/include/utils/move.h
Normal file
6
kernel/include/utils/move.h
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
template <typename T> inline T&& move(T& lvalue)
|
||||||
|
{
|
||||||
|
return (T &&) lvalue;
|
||||||
|
}
|
@ -1,4 +1,5 @@
|
|||||||
#include "fs/FileDescriptor.h"
|
#include "fs/FileDescriptor.h"
|
||||||
|
#include "errno.h"
|
||||||
|
|
||||||
Descriptor::Descriptor() : m_is_open(false)
|
Descriptor::Descriptor() : m_is_open(false)
|
||||||
{
|
{
|
||||||
@ -32,3 +33,11 @@ ssize_t Descriptor::write(size_t size, const char* buffer)
|
|||||||
m_offset += result;
|
m_offset += result;
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int Descriptor::seek(long offset)
|
||||||
|
{
|
||||||
|
if (m_node->type != VFS_DEVICE && (uint64_t)offset > m_node->length)
|
||||||
|
return -EINVAL; // FIXME: Support seeking beyond the current file's length.
|
||||||
|
m_offset = (uint64_t)offset;
|
||||||
|
return 0;
|
||||||
|
}
|
@ -5,15 +5,15 @@
|
|||||||
#include "std/stdlib.h"
|
#include "std/stdlib.h"
|
||||||
#include "std/string.h"
|
#include "std/string.h"
|
||||||
|
|
||||||
VFS::Node* ConsoleDevice::create_new()
|
VFS::Node* ConsoleDevice::create_new(const char* devname)
|
||||||
{
|
{
|
||||||
VFS::Node* dev = new VFS::Node;
|
VFS::Node* dev = new VFS::Node;
|
||||||
dev->write_func = ConsoleDevice::write;
|
dev->write_func = ConsoleDevice::write;
|
||||||
dev->inode = 0;
|
dev->inode = 0;
|
||||||
dev->length = 0;
|
dev->length = 0;
|
||||||
dev->type = VFS_FILE;
|
dev->type = VFS_DEVICE;
|
||||||
dev->flags = 0;
|
dev->flags = 0;
|
||||||
strncpy(dev->name, "console", sizeof(dev->name));
|
strncpy(dev->name, devname, sizeof(dev->name));
|
||||||
return dev;
|
return dev;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
#include "fs/devices/DeviceFS.h"
|
#include "fs/devices/DeviceFS.h"
|
||||||
#include "fs/devices/Console.h"
|
#include "fs/devices/Console.h"
|
||||||
|
#include "fs/devices/Serial.h"
|
||||||
#include "fs/devices/Version.h"
|
#include "fs/devices/Version.h"
|
||||||
#include "std/stdlib.h"
|
#include "std/stdlib.h"
|
||||||
#include "std/string.h"
|
#include "std/string.h"
|
||||||
@ -21,8 +22,9 @@ VFS::Node* DeviceFS::get()
|
|||||||
devfs_root->find_func = DeviceFS::finddir;
|
devfs_root->find_func = DeviceFS::finddir;
|
||||||
strncpy(devfs_root->name, "dev", sizeof(devfs_root->name));
|
strncpy(devfs_root->name, "dev", sizeof(devfs_root->name));
|
||||||
|
|
||||||
devfs_files[devfs_file_count++] = VersionDevice::create_new();
|
devfs_files[devfs_file_count++] = VersionDevice::create_new("version");
|
||||||
devfs_files[devfs_file_count++] = ConsoleDevice::create_new();
|
devfs_files[devfs_file_count++] = ConsoleDevice::create_new("console");
|
||||||
|
devfs_files[devfs_file_count++] = SerialDevice::create_new("serial");
|
||||||
return devfs_root;
|
return devfs_root;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
25
kernel/src/fs/devices/Serial.cpp
Normal file
25
kernel/src/fs/devices/Serial.cpp
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
#include "io/Serial.h"
|
||||||
|
#include "config.h"
|
||||||
|
#include "fs/devices/Serial.h"
|
||||||
|
#include "std/stdio.h"
|
||||||
|
#include "std/stdlib.h"
|
||||||
|
#include "std/string.h"
|
||||||
|
|
||||||
|
VFS::Node* SerialDevice::create_new(const char* devname)
|
||||||
|
{
|
||||||
|
VFS::Node* dev = new VFS::Node;
|
||||||
|
dev->write_func = SerialDevice::write;
|
||||||
|
dev->inode = 0;
|
||||||
|
dev->length = 0;
|
||||||
|
dev->type = VFS_DEVICE;
|
||||||
|
dev->flags = 0;
|
||||||
|
strncpy(dev->name, devname, sizeof(dev->name));
|
||||||
|
return dev;
|
||||||
|
}
|
||||||
|
|
||||||
|
ssize_t SerialDevice::write(VFS::Node* node, size_t, size_t size, const char* buffer)
|
||||||
|
{
|
||||||
|
if (!node) return -1;
|
||||||
|
Serial::write(buffer, size);
|
||||||
|
return (ssize_t)size;
|
||||||
|
}
|
@ -4,15 +4,15 @@
|
|||||||
#include "std/stdlib.h"
|
#include "std/stdlib.h"
|
||||||
#include "std/string.h"
|
#include "std/string.h"
|
||||||
|
|
||||||
VFS::Node* VersionDevice::create_new()
|
VFS::Node* VersionDevice::create_new(const char* devname)
|
||||||
{
|
{
|
||||||
VFS::Node* dev = new VFS::Node;
|
VFS::Node* dev = new VFS::Node;
|
||||||
dev->read_func = VersionDevice::read;
|
dev->read_func = VersionDevice::read;
|
||||||
dev->inode = 0;
|
dev->inode = 0;
|
||||||
dev->length = strlen(moon_version()) + 5;
|
dev->length = strlen(moon_version()) + 5;
|
||||||
dev->type = VFS_FILE;
|
dev->type = VFS_DEVICE;
|
||||||
dev->flags = 0;
|
dev->flags = 0;
|
||||||
strncpy(dev->name, "version", sizeof(dev->name));
|
strncpy(dev->name, devname, sizeof(dev->name));
|
||||||
return dev;
|
return dev;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -49,15 +49,15 @@ struct InternalGDT
|
|||||||
GDTEntry user_data;
|
GDTEntry user_data;
|
||||||
GDTEntry tss;
|
GDTEntry tss;
|
||||||
HighGDTEntry tss2;
|
HighGDTEntry tss2;
|
||||||
} __attribute__((packed)) __attribute((aligned(0x1000)));
|
} __attribute__((packed)) __attribute((aligned(PAGE_SIZE)));
|
||||||
|
|
||||||
__attribute__((aligned(0x1000))) static InternalGDT internal_gdt = {{0x0000, 0x0000, 0x00, 0x00, 0x00, 0x00},
|
__attribute__((aligned(PAGE_SIZE))) static InternalGDT internal_gdt = {{0x0000, 0x0000, 0x00, 0x00, 0x00, 0x00},
|
||||||
{0xffff, 0x0000, 0x00, 0x9a, 0xaf, 0x00},
|
{0xffff, 0x0000, 0x00, 0x9a, 0xaf, 0x00},
|
||||||
{0xffff, 0x0000, 0x00, 0x92, 0xcf, 0x00},
|
{0xffff, 0x0000, 0x00, 0x92, 0xcf, 0x00},
|
||||||
{0xffff, 0x0000, 0x00, 0xfa, 0xaf, 0x00},
|
{0xffff, 0x0000, 0x00, 0xfa, 0xaf, 0x00},
|
||||||
{0xffff, 0x0000, 0x00, 0xf2, 0xcf, 0x00},
|
{0xffff, 0x0000, 0x00, 0xf2, 0xcf, 0x00},
|
||||||
{0x0000, 0x0000, 0x00, 0xe9, 0x0f, 0x00},
|
{0x0000, 0x0000, 0x00, 0xe9, 0x0f, 0x00},
|
||||||
{0x00000000, 0x00000000}};
|
{0x00000000, 0x00000000}};
|
||||||
|
|
||||||
static TSS main_tss;
|
static TSS main_tss;
|
||||||
|
|
||||||
@ -78,20 +78,35 @@ static void set_limit(GDTEntry* entry, uint32_t limit)
|
|||||||
entry->limit1_flags = (entry->limit1_flags & 0xF0) | ((limit >> 16) & 0xF);
|
entry->limit1_flags = (entry->limit1_flags & 0xF0) | ((limit >> 16) & 0xF);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void set_tss_base(GDTEntry* tss1, HighGDTEntry* tss2, uint64_t addr)
|
||||||
|
{
|
||||||
|
set_base(tss1, addr & 0xffffffff);
|
||||||
|
tss2->base_high = (uint32_t)(addr >> 32);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void setup_tss()
|
||||||
|
{
|
||||||
|
memset(&main_tss, 0, sizeof(TSS));
|
||||||
|
main_tss.rsp[0] =
|
||||||
|
(uint64_t)MemoryManager::get_pages(4) + (PAGE_SIZE * 4) - 8; // allocate 16KB for the syscall stack
|
||||||
|
main_tss.iomap_base = sizeof(TSS);
|
||||||
|
set_tss_base(&internal_gdt.tss, &internal_gdt.tss2, (uint64_t)&main_tss);
|
||||||
|
set_limit(&internal_gdt.tss, sizeof(TSS) - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void load_tss()
|
||||||
|
{
|
||||||
|
kdbgln("Loading TR (GDT entry 0x2b)");
|
||||||
|
load_tr(0x2b);
|
||||||
|
}
|
||||||
|
|
||||||
void GDT::load()
|
void GDT::load()
|
||||||
{
|
{
|
||||||
static GDTR gdtr;
|
static GDTR gdtr;
|
||||||
gdtr.offset = (uint64_t)&internal_gdt;
|
gdtr.offset = (uint64_t)&internal_gdt;
|
||||||
gdtr.size = sizeof(InternalGDT);
|
gdtr.size = sizeof(InternalGDT);
|
||||||
memset(&main_tss, 0, sizeof(TSS));
|
setup_tss();
|
||||||
main_tss.rsp[0] =
|
|
||||||
(uint64_t)MemoryManager::get_pages(4) + (PAGE_SIZE * 4) - 8; // allocate 16KB for the syscall stack
|
|
||||||
main_tss.iomap_base = sizeof(TSS);
|
|
||||||
set_base(&internal_gdt.tss, (uint64_t)&main_tss & 0xffffffff);
|
|
||||||
internal_gdt.tss2.base_high = (uint32_t)(((uint64_t)&main_tss >> 32) & 0xffffffff);
|
|
||||||
set_limit(&internal_gdt.tss, sizeof(TSS) - 1);
|
|
||||||
kdbgln("Loading GDT at offset %lx, size %d", gdtr.offset, gdtr.size);
|
kdbgln("Loading GDT at offset %lx, size %d", gdtr.offset, gdtr.size);
|
||||||
load_gdt(&gdtr);
|
load_gdt(&gdtr);
|
||||||
kdbgln("Loading TR (GDT entry 0x2b)");
|
load_tss();
|
||||||
load_tr(0x2b);
|
|
||||||
}
|
}
|
@ -51,6 +51,8 @@ extern "C" void common_handler(Context* context)
|
|||||||
StackTracer tracer(context->rbp);
|
StackTracer tracer(context->rbp);
|
||||||
tracer.trace_with_ip(context->rip);
|
tracer.trace_with_ip(context->rip);
|
||||||
|
|
||||||
|
hang(); // FIXME: Remove this when multiple address spaces are working.
|
||||||
|
|
||||||
Scheduler::task_misbehave(context, -3);
|
Scheduler::task_misbehave(context, -3);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
#include "interrupts/Interrupts.h"
|
#include "interrupts/Interrupts.h"
|
||||||
#include "trace/StackTracer.h"
|
#include "trace/StackTracer.h"
|
||||||
|
#include "utils/Registers.h"
|
||||||
|
|
||||||
void Interrupts::disable()
|
void Interrupts::disable()
|
||||||
{
|
{
|
||||||
@ -25,3 +26,34 @@ void Interrupts::return_from_handler(Context* context)
|
|||||||
:
|
:
|
||||||
: "r"(context));
|
: "r"(context));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Interrupts::are_enabled()
|
||||||
|
{
|
||||||
|
return (read_rflags() & 0x200) > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool saved_interrupt_state;
|
||||||
|
|
||||||
|
void Interrupts::push_and_disable()
|
||||||
|
{
|
||||||
|
saved_interrupt_state = are_enabled();
|
||||||
|
disable();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Interrupts::push_and_enable()
|
||||||
|
{
|
||||||
|
saved_interrupt_state = are_enabled();
|
||||||
|
enable();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Interrupts::pop()
|
||||||
|
{
|
||||||
|
if (saved_interrupt_state && !are_enabled()) enable();
|
||||||
|
else if (!saved_interrupt_state && are_enabled())
|
||||||
|
disable();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Interrupts::were_enabled()
|
||||||
|
{
|
||||||
|
return saved_interrupt_state;
|
||||||
|
}
|
@ -26,3 +26,15 @@ asm_enable_sse:
|
|||||||
or ax, 3 << 9
|
or ax, 3 << 9
|
||||||
mov cr4, rax
|
mov cr4, rax
|
||||||
ret
|
ret
|
||||||
|
|
||||||
|
global asm_get_rflags
|
||||||
|
asm_get_rflags:
|
||||||
|
pushfq
|
||||||
|
pop rax
|
||||||
|
ret
|
||||||
|
|
||||||
|
global asm_set_rflags
|
||||||
|
asm_set_rflags:
|
||||||
|
push rdi
|
||||||
|
popfq
|
||||||
|
ret
|
@ -16,6 +16,7 @@
|
|||||||
#include "io/PIC.h"
|
#include "io/PIC.h"
|
||||||
#include "io/Serial.h"
|
#include "io/Serial.h"
|
||||||
#include "log/Log.h"
|
#include "log/Log.h"
|
||||||
|
#include "memory/AddressSpace.h"
|
||||||
#include "memory/Memory.h"
|
#include "memory/Memory.h"
|
||||||
#include "memory/MemoryManager.h"
|
#include "memory/MemoryManager.h"
|
||||||
#include "memory/MemoryMap.h"
|
#include "memory/MemoryMap.h"
|
||||||
@ -56,12 +57,6 @@ extern "C" void _start()
|
|||||||
|
|
||||||
kinfoln("Loaded IDT");
|
kinfoln("Loaded IDT");
|
||||||
|
|
||||||
PIC::remap();
|
|
||||||
PIC::enable_master(0b11111100); // enable keyboard and PIT
|
|
||||||
PIC::enable_slave(0b11111111);
|
|
||||||
|
|
||||||
kinfoln("Prepared PIC");
|
|
||||||
|
|
||||||
PIT::initialize(1000); // 1000 times per second
|
PIT::initialize(1000); // 1000 times per second
|
||||||
|
|
||||||
kinfoln("Prepared PIT");
|
kinfoln("Prepared PIT");
|
||||||
@ -87,7 +82,9 @@ extern "C" void _start()
|
|||||||
|
|
||||||
Init::finish_kernel_boot();
|
Init::finish_kernel_boot();
|
||||||
|
|
||||||
Interrupts::enable(); // Task switching commences here
|
PIC::remap();
|
||||||
|
PIC::enable_master(0b11111100); // enable keyboard and PIT
|
||||||
|
PIC::enable_slave(0b11111111);
|
||||||
|
|
||||||
kinfoln("Interrupts enabled");
|
kinfoln("Interrupts enabled");
|
||||||
|
|
||||||
|
95
kernel/src/memory/AddressSpace.cpp
Normal file
95
kernel/src/memory/AddressSpace.cpp
Normal file
@ -0,0 +1,95 @@
|
|||||||
|
#define MODULE "vmm"
|
||||||
|
|
||||||
|
#include "memory/AddressSpace.h"
|
||||||
|
#include "log/Log.h"
|
||||||
|
#include "memory/PMM.h"
|
||||||
|
#include "memory/VMM.h"
|
||||||
|
#include "utils/move.h"
|
||||||
|
|
||||||
|
AddressSpace AddressSpace::create()
|
||||||
|
{
|
||||||
|
AddressSpace result;
|
||||||
|
result.m_pml4 = (PageTable*)PMM::request_page();
|
||||||
|
result.m_cloned = false;
|
||||||
|
VMM::install_kernel_page_directory_into_address_space(result);
|
||||||
|
return move(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
void AddressSpace::destroy()
|
||||||
|
{
|
||||||
|
if (m_cloned)
|
||||||
|
{
|
||||||
|
kdbgln("Will not destroy a cloned address space, I don't own it");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
uint64_t pages_freed = 0;
|
||||||
|
for (int i = 0; i < 512; i++)
|
||||||
|
{
|
||||||
|
PageDirectoryEntry& pdp_pde = m_pml4->entries[i];
|
||||||
|
if (!pdp_pde.present) continue;
|
||||||
|
if (pdp_pde.larger_pages)
|
||||||
|
{
|
||||||
|
pages_freed++;
|
||||||
|
PMM::free_page((void*)pdp_pde.get_address());
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
PageTable* pdp = (PageTable*)pdp_pde.get_address();
|
||||||
|
for (int j = 0; j < 511; j++) // skip the last page directory, it's the kernel one
|
||||||
|
{
|
||||||
|
PageDirectoryEntry& pd_pde = pdp->entries[j];
|
||||||
|
if (!pd_pde.present) continue;
|
||||||
|
if (pd_pde.larger_pages)
|
||||||
|
{
|
||||||
|
pages_freed++;
|
||||||
|
PMM::free_page((void*)pd_pde.get_address());
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
PageTable* pd = (PageTable*)pd_pde.get_address();
|
||||||
|
for (int k = 0; k < 512; k++)
|
||||||
|
{
|
||||||
|
PageDirectoryEntry& pt_pde = pd->entries[k];
|
||||||
|
if (!pt_pde.present) continue;
|
||||||
|
if (pt_pde.larger_pages)
|
||||||
|
{
|
||||||
|
pages_freed++;
|
||||||
|
PMM::free_page((void*)pt_pde.get_address());
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
PageTable* pt = (PageTable*)pt_pde.get_address();
|
||||||
|
for (int l = 0; l < 512; l++)
|
||||||
|
{
|
||||||
|
PageDirectoryEntry& pde = pt->entries[l];
|
||||||
|
if (!pde.present) continue;
|
||||||
|
pages_freed++;
|
||||||
|
PMM::free_page((void*)pde.get_address());
|
||||||
|
}
|
||||||
|
pages_freed++;
|
||||||
|
PMM::free_page(pt);
|
||||||
|
}
|
||||||
|
pages_freed++;
|
||||||
|
PMM::free_page(pd);
|
||||||
|
}
|
||||||
|
pages_freed++;
|
||||||
|
PMM::free_page(pdp);
|
||||||
|
}
|
||||||
|
pages_freed++;
|
||||||
|
PMM::free_page(m_pml4);
|
||||||
|
|
||||||
|
kdbgln("Reclaimed %ld pages from address space!", pages_freed);
|
||||||
|
}
|
||||||
|
|
||||||
|
void AddressSpace::detach()
|
||||||
|
{
|
||||||
|
if (!m_cloned) return;
|
||||||
|
m_pml4 = (PageTable*)PMM::request_page();
|
||||||
|
VMM::install_kernel_page_directory_into_address_space(*this);
|
||||||
|
m_cloned = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
AddressSpace AddressSpace::clone()
|
||||||
|
{
|
||||||
|
AddressSpace result;
|
||||||
|
result.m_pml4 = m_pml4;
|
||||||
|
result.m_cloned = true;
|
||||||
|
return result;
|
||||||
|
}
|
@ -1,3 +1,5 @@
|
|||||||
|
#define MODULE "kheap"
|
||||||
|
|
||||||
#include "memory/KernelHeap.h"
|
#include "memory/KernelHeap.h"
|
||||||
#include "assert.h"
|
#include "assert.h"
|
||||||
|
|
||||||
@ -68,7 +70,7 @@ uint64_t KernelHeap::request_virtual_pages(uint64_t count)
|
|||||||
|
|
||||||
void KernelHeap::free_virtual_page(uint64_t address)
|
void KernelHeap::free_virtual_page(uint64_t address)
|
||||||
{
|
{
|
||||||
ASSERT(address >= ALLOC_BASE && address < ALLOC_END);
|
if (address < ALLOC_BASE || address >= ALLOC_END) return;
|
||||||
uint64_t index = (address - ALLOC_BASE) / PAGE_SIZE;
|
uint64_t index = (address - ALLOC_BASE) / PAGE_SIZE;
|
||||||
bitmap_set(index, false);
|
bitmap_set(index, false);
|
||||||
if (start_index > index) start_index = index;
|
if (start_index > index) start_index = index;
|
||||||
@ -76,7 +78,7 @@ void KernelHeap::free_virtual_page(uint64_t address)
|
|||||||
|
|
||||||
void KernelHeap::free_virtual_pages(uint64_t address, uint64_t count)
|
void KernelHeap::free_virtual_pages(uint64_t address, uint64_t count)
|
||||||
{
|
{
|
||||||
ASSERT(address >= ALLOC_BASE && address < ALLOC_END);
|
if (address < ALLOC_BASE || address >= ALLOC_END) return;
|
||||||
uint64_t index = (address - ALLOC_BASE) / PAGE_SIZE;
|
uint64_t index = (address - ALLOC_BASE) / PAGE_SIZE;
|
||||||
for (uint64_t i = 0; i < count; i++) { bitmap_set(index + i, false); }
|
for (uint64_t i = 0; i < count; i++) { bitmap_set(index + i, false); }
|
||||||
if (start_index > index) start_index = index;
|
if (start_index > index) start_index = index;
|
||||||
|
@ -45,7 +45,7 @@ uint64_t Memory::get_usable()
|
|||||||
|
|
||||||
bool Memory::is_kernel_address(uintptr_t address)
|
bool Memory::is_kernel_address(uintptr_t address)
|
||||||
{
|
{
|
||||||
return address > 0xfffffffff8000000;
|
return address >= 0xfffffffff8000000;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Memory::is_user_address(uintptr_t address)
|
bool Memory::is_user_address(uintptr_t address)
|
||||||
|
@ -12,7 +12,7 @@
|
|||||||
void MemoryManager::init()
|
void MemoryManager::init()
|
||||||
{
|
{
|
||||||
PMM::init();
|
PMM::init();
|
||||||
kernelVMM.init();
|
VMM::init();
|
||||||
PMM::map_bitmap_to_virtual();
|
PMM::map_bitmap_to_virtual();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -26,7 +26,7 @@ void* MemoryManager::get_mapping(void* physicalAddress, int flags)
|
|||||||
#endif
|
#endif
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
kernelVMM.map(virtualAddress, (uint64_t)physicalAddress, flags);
|
VMM::map(virtualAddress, (uint64_t)physicalAddress, flags);
|
||||||
return (void*)virtualAddress;
|
return (void*)virtualAddress;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -41,7 +41,7 @@ void* MemoryManager::get_unaligned_mapping(void* physicalAddress, int flags)
|
|||||||
#endif
|
#endif
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
kernelVMM.map(virtualAddress, (uint64_t)physicalAddress - offset, flags);
|
VMM::map(virtualAddress, (uint64_t)physicalAddress - offset, flags);
|
||||||
return (void*)(virtualAddress + offset);
|
return (void*)(virtualAddress + offset);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -62,7 +62,7 @@ void* MemoryManager::get_unaligned_mappings(void* physicalAddress, uint64_t coun
|
|||||||
}
|
}
|
||||||
for (uint64_t i = 0; i < count; i++)
|
for (uint64_t i = 0; i < count; i++)
|
||||||
{
|
{
|
||||||
kernelVMM.map(virtualAddress + (i * PAGE_SIZE), ((uint64_t)physicalAddress - offset) + (i * PAGE_SIZE), flags);
|
VMM::map(virtualAddress + (i * PAGE_SIZE), ((uint64_t)physicalAddress - offset) + (i * PAGE_SIZE), flags);
|
||||||
}
|
}
|
||||||
return (void*)(virtualAddress + offset);
|
return (void*)(virtualAddress + offset);
|
||||||
}
|
}
|
||||||
@ -70,7 +70,7 @@ void* MemoryManager::get_unaligned_mappings(void* physicalAddress, uint64_t coun
|
|||||||
void MemoryManager::release_unaligned_mapping(void* mapping)
|
void MemoryManager::release_unaligned_mapping(void* mapping)
|
||||||
{
|
{
|
||||||
uint64_t offset = (uint64_t)mapping % PAGE_SIZE;
|
uint64_t offset = (uint64_t)mapping % PAGE_SIZE;
|
||||||
kernelVMM.unmap((uint64_t)mapping - offset);
|
VMM::unmap((uint64_t)mapping - offset);
|
||||||
KernelHeap::free_virtual_page((uint64_t)mapping - offset);
|
KernelHeap::free_virtual_page((uint64_t)mapping - offset);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -80,12 +80,12 @@ void MemoryManager::release_unaligned_mappings(void* mapping, uint64_t count)
|
|||||||
if (count == 1) return release_unaligned_mapping(mapping);
|
if (count == 1) return release_unaligned_mapping(mapping);
|
||||||
uint64_t offset = (uint64_t)mapping % PAGE_SIZE;
|
uint64_t offset = (uint64_t)mapping % PAGE_SIZE;
|
||||||
KernelHeap::free_virtual_pages((uint64_t)mapping - offset, count);
|
KernelHeap::free_virtual_pages((uint64_t)mapping - offset, count);
|
||||||
for (uint64_t i = 0; i < count; i++) { kernelVMM.unmap(((uint64_t)mapping - offset) + (i * PAGE_SIZE)); }
|
for (uint64_t i = 0; i < count; i++) { VMM::unmap(((uint64_t)mapping - offset) + (i * PAGE_SIZE)); }
|
||||||
}
|
}
|
||||||
|
|
||||||
void MemoryManager::release_mapping(void* mapping)
|
void MemoryManager::release_mapping(void* mapping)
|
||||||
{
|
{
|
||||||
kernelVMM.unmap((uint64_t)mapping);
|
VMM::unmap((uint64_t)mapping);
|
||||||
KernelHeap::free_virtual_page((uint64_t)mapping);
|
KernelHeap::free_virtual_page((uint64_t)mapping);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -112,15 +112,15 @@ void* MemoryManager::get_page_at(uint64_t addr, int flags)
|
|||||||
#endif
|
#endif
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
kernelVMM.map(addr, (uint64_t)physicalAddress, flags);
|
VMM::map(addr, (uint64_t)physicalAddress, flags);
|
||||||
return (void*)addr;
|
return (void*)addr;
|
||||||
}
|
}
|
||||||
|
|
||||||
void MemoryManager::release_page(void* page)
|
void MemoryManager::release_page(void* page)
|
||||||
{
|
{
|
||||||
uint64_t physicalAddress = kernelVMM.getPhysical((uint64_t)page);
|
uint64_t physicalAddress = VMM::get_physical((uint64_t)page);
|
||||||
ASSERT(physicalAddress != UINT64_MAX); // this address is not mapped in the virtual address space...
|
ASSERT(physicalAddress != UINT64_MAX); // this address is not mapped in the virtual address space...
|
||||||
kernelVMM.unmap((uint64_t)page);
|
VMM::unmap((uint64_t)page);
|
||||||
PMM::free_page((void*)physicalAddress);
|
PMM::free_page((void*)physicalAddress);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -158,13 +158,13 @@ void* MemoryManager::get_pages_at(uint64_t addr, uint64_t count, int flags)
|
|||||||
// smaller range, so this might not be fatal.
|
// smaller range, so this might not be fatal.
|
||||||
{
|
{
|
||||||
#ifdef MM_DEBUG
|
#ifdef MM_DEBUG
|
||||||
kwarnln("OOM while allocating page %ld of memory. this might be recoverable...");
|
kwarnln("OOM while allocating page %ld of memory. this might be recoverable...", i);
|
||||||
#endif
|
#endif
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
kernelVMM.map(addr + (i * PAGE_SIZE), (uint64_t)physicalAddress, flags);
|
VMM::map(addr + (i * PAGE_SIZE), (uint64_t)physicalAddress, flags);
|
||||||
#ifdef MM_DEBUG
|
#ifdef MM_DEBUG
|
||||||
kdbgln("allocating virtual %lx, physical %p", virtualAddress + (i * PAGE_SIZE), physicalAddress);
|
kdbgln("allocating virtual %lx, physical %p", addr + (i * PAGE_SIZE), physicalAddress);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
return (void*)addr;
|
return (void*)addr;
|
||||||
@ -180,9 +180,9 @@ void MemoryManager::release_pages(void* pages, uint64_t count)
|
|||||||
for (uint64_t i = 0; i < count; i++)
|
for (uint64_t i = 0; i < count; i++)
|
||||||
{
|
{
|
||||||
void* page = (void*)((uint64_t)pages + (i * PAGE_SIZE));
|
void* page = (void*)((uint64_t)pages + (i * PAGE_SIZE));
|
||||||
uint64_t physicalAddress = kernelVMM.getPhysical((uint64_t)page);
|
uint64_t physicalAddress = VMM::get_physical((uint64_t)page);
|
||||||
ASSERT(physicalAddress != UINT64_MAX);
|
ASSERT(physicalAddress != UINT64_MAX);
|
||||||
kernelVMM.unmap((uint64_t)page);
|
VMM::unmap((uint64_t)page);
|
||||||
#ifdef MM_DEBUG
|
#ifdef MM_DEBUG
|
||||||
kdbgln("releasing virtual %p, physical %lx", page, physicalAddress);
|
kdbgln("releasing virtual %p, physical %lx", page, physicalAddress);
|
||||||
#endif
|
#endif
|
||||||
@ -190,3 +190,8 @@ void MemoryManager::release_pages(void* pages, uint64_t count)
|
|||||||
}
|
}
|
||||||
KernelHeap::free_virtual_pages((uint64_t)pages, count);
|
KernelHeap::free_virtual_pages((uint64_t)pages, count);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void MemoryManager::protect(void* page, uint64_t count, int flags)
|
||||||
|
{
|
||||||
|
for (uint64_t i = 0; i < count; i++) { VMM::remap((uint64_t)page + (i * PAGE_SIZE), flags); }
|
||||||
|
}
|
@ -3,9 +3,14 @@
|
|||||||
#pragma GCC push_options
|
#pragma GCC push_options
|
||||||
#pragma GCC diagnostic ignored "-Wconversion"
|
#pragma GCC diagnostic ignored "-Wconversion"
|
||||||
|
|
||||||
void Paging::PageDirectoryEntry::set_address(uint64_t addr)
|
void PageDirectoryEntry::set_address(uint64_t addr)
|
||||||
{
|
{
|
||||||
this->Address = (addr >> 12);
|
this->address = (addr >> 12);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t PageDirectoryEntry::get_address()
|
||||||
|
{
|
||||||
|
return (uint64_t)this->address << 12;
|
||||||
}
|
}
|
||||||
|
|
||||||
#pragma GCC pop_options
|
#pragma GCC pop_options
|
@ -1,228 +1,291 @@
|
|||||||
|
#define MODULE "vmm"
|
||||||
|
|
||||||
#include "memory/VMM.h"
|
#include "memory/VMM.h"
|
||||||
#include "assert.h"
|
#include "assert.h"
|
||||||
|
#include "log/Log.h"
|
||||||
#include "memory/PMM.h"
|
#include "memory/PMM.h"
|
||||||
|
#include "misc/utils.h"
|
||||||
#include "std/string.h"
|
#include "std/string.h"
|
||||||
|
#include "utils/Addresses.h"
|
||||||
|
#include "utils/Registers.h"
|
||||||
|
|
||||||
Paging::VirtualMemoryManager kernelVMM;
|
static PageTable* kernel_pml4;
|
||||||
|
static PageTable* current_pml4;
|
||||||
|
static AddressSpace* user_address_space;
|
||||||
|
|
||||||
namespace Paging
|
void VMM::switch_back_to_kernel_address_space()
|
||||||
{
|
{
|
||||||
void VirtualMemoryManager::init()
|
if (current_pml4 != kernel_pml4) { current_pml4 = kernel_pml4; }
|
||||||
|
}
|
||||||
|
|
||||||
|
void VMM::switch_to_user_address_space(AddressSpace& space)
|
||||||
|
{
|
||||||
|
user_address_space = &space;
|
||||||
|
current_pml4 = user_address_space->get_pml4();
|
||||||
|
}
|
||||||
|
|
||||||
|
void VMM::switch_to_previous_user_address_space()
|
||||||
|
{
|
||||||
|
current_pml4 = user_address_space->get_pml4();
|
||||||
|
}
|
||||||
|
|
||||||
|
void VMM::enter_syscall_context()
|
||||||
|
{
|
||||||
|
if (current_pml4 != kernel_pml4)
|
||||||
{
|
{
|
||||||
asm volatile("mov %%cr3, %0" : "=r"(PML4));
|
current_pml4 = kernel_pml4;
|
||||||
}
|
apply_address_space();
|
||||||
|
switch_to_previous_user_address_space();
|
||||||
void VirtualMemoryManager::init(PageTable* cr3)
|
|
||||||
{
|
|
||||||
PML4 = cr3;
|
|
||||||
}
|
|
||||||
|
|
||||||
void VirtualMemoryManager::unmap(uint64_t virtualAddress)
|
|
||||||
{
|
|
||||||
virtualAddress >>= 12;
|
|
||||||
uint64_t P_i = virtualAddress & 0x1ff;
|
|
||||||
virtualAddress >>= 9;
|
|
||||||
uint64_t PT_i = virtualAddress & 0x1ff;
|
|
||||||
virtualAddress >>= 9;
|
|
||||||
uint64_t PD_i = virtualAddress & 0x1ff;
|
|
||||||
virtualAddress >>= 9;
|
|
||||||
uint64_t PDP_i = virtualAddress & 0x1ff;
|
|
||||||
|
|
||||||
PageDirectoryEntry PDE;
|
|
||||||
|
|
||||||
PDE = PML4->entries[PDP_i];
|
|
||||||
PageTable* PDP;
|
|
||||||
if (!PDE.Present)
|
|
||||||
{
|
|
||||||
return; // Already unmapped
|
|
||||||
}
|
|
||||||
else { PDP = (PageTable*)((uint64_t)PDE.Address << 12); }
|
|
||||||
|
|
||||||
PDE = PDP->entries[PD_i];
|
|
||||||
PageTable* PD;
|
|
||||||
if (!PDE.Present)
|
|
||||||
{
|
|
||||||
return; // Already unmapped
|
|
||||||
}
|
|
||||||
else { PD = (PageTable*)((uint64_t)PDE.Address << 12); }
|
|
||||||
|
|
||||||
PDE = PD->entries[PT_i];
|
|
||||||
PageTable* PT;
|
|
||||||
if (!PDE.Present)
|
|
||||||
{
|
|
||||||
return; // Already unmapped
|
|
||||||
}
|
|
||||||
else { PT = (PageTable*)((uint64_t)PDE.Address << 12); }
|
|
||||||
|
|
||||||
PDE = PT->entries[P_i];
|
|
||||||
PDE.Present = false;
|
|
||||||
PT->entries[P_i] = PDE;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint64_t VirtualMemoryManager::getPhysical(uint64_t virtualAddress)
|
|
||||||
{
|
|
||||||
virtualAddress >>= 12;
|
|
||||||
uint64_t P_i = virtualAddress & 0x1ff;
|
|
||||||
virtualAddress >>= 9;
|
|
||||||
uint64_t PT_i = virtualAddress & 0x1ff;
|
|
||||||
virtualAddress >>= 9;
|
|
||||||
uint64_t PD_i = virtualAddress & 0x1ff;
|
|
||||||
virtualAddress >>= 9;
|
|
||||||
uint64_t PDP_i = virtualAddress & 0x1ff;
|
|
||||||
|
|
||||||
PageDirectoryEntry PDE;
|
|
||||||
|
|
||||||
PDE = PML4->entries[PDP_i];
|
|
||||||
PageTable* PDP;
|
|
||||||
if (!PDE.Present)
|
|
||||||
{
|
|
||||||
return UINT64_MAX; // Not mapped
|
|
||||||
}
|
|
||||||
else { PDP = (PageTable*)((uint64_t)PDE.Address << 12); }
|
|
||||||
|
|
||||||
PDE = PDP->entries[PD_i];
|
|
||||||
PageTable* PD;
|
|
||||||
if (!PDE.Present)
|
|
||||||
{
|
|
||||||
return UINT64_MAX; // Not mapped
|
|
||||||
}
|
|
||||||
else { PD = (PageTable*)((uint64_t)PDE.Address << 12); }
|
|
||||||
|
|
||||||
PDE = PD->entries[PT_i];
|
|
||||||
PageTable* PT;
|
|
||||||
if (!PDE.Present)
|
|
||||||
{
|
|
||||||
return UINT64_MAX; // Not mapped
|
|
||||||
}
|
|
||||||
else { PT = (PageTable*)((uint64_t)PDE.Address << 12); }
|
|
||||||
|
|
||||||
PDE = PT->entries[P_i];
|
|
||||||
return PDE.Address << 12;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint64_t VirtualMemoryManager::getFlags(uint64_t virtualAddress)
|
|
||||||
{
|
|
||||||
virtualAddress >>= 12;
|
|
||||||
uint64_t P_i = virtualAddress & 0x1ff;
|
|
||||||
virtualAddress >>= 9;
|
|
||||||
uint64_t PT_i = virtualAddress & 0x1ff;
|
|
||||||
virtualAddress >>= 9;
|
|
||||||
uint64_t PD_i = virtualAddress & 0x1ff;
|
|
||||||
virtualAddress >>= 9;
|
|
||||||
uint64_t PDP_i = virtualAddress & 0x1ff;
|
|
||||||
|
|
||||||
PageDirectoryEntry PDE;
|
|
||||||
|
|
||||||
PDE = PML4->entries[PDP_i];
|
|
||||||
PageTable* PDP;
|
|
||||||
if (!PDE.Present)
|
|
||||||
{
|
|
||||||
return 0; // Not mapped
|
|
||||||
}
|
|
||||||
else { PDP = (PageTable*)((uint64_t)PDE.Address << 12); }
|
|
||||||
|
|
||||||
PDE = PDP->entries[PD_i];
|
|
||||||
PageTable* PD;
|
|
||||||
if (!PDE.Present)
|
|
||||||
{
|
|
||||||
return 0; // Not mapped
|
|
||||||
}
|
|
||||||
else { PD = (PageTable*)((uint64_t)PDE.Address << 12); }
|
|
||||||
|
|
||||||
PDE = PD->entries[PT_i];
|
|
||||||
PageTable* PT;
|
|
||||||
if (!PDE.Present)
|
|
||||||
{
|
|
||||||
return 0; // Not mapped
|
|
||||||
}
|
|
||||||
else { PT = (PageTable*)((uint64_t)PDE.Address << 12); }
|
|
||||||
|
|
||||||
uint64_t flags = 0;
|
|
||||||
|
|
||||||
PDE = PT->entries[P_i];
|
|
||||||
if (PDE.UserSuper) flags |= User;
|
|
||||||
if (PDE.ReadWrite) flags |= ReadWrite;
|
|
||||||
return flags;
|
|
||||||
}
|
|
||||||
|
|
||||||
void VirtualMemoryManager::map(uint64_t virtualAddress, uint64_t physicalAddress, int flags)
|
|
||||||
{
|
|
||||||
virtualAddress >>= 12;
|
|
||||||
uint64_t P_i = virtualAddress & 0x1ff;
|
|
||||||
virtualAddress >>= 9;
|
|
||||||
uint64_t PT_i = virtualAddress & 0x1ff;
|
|
||||||
virtualAddress >>= 9;
|
|
||||||
uint64_t PD_i = virtualAddress & 0x1ff;
|
|
||||||
virtualAddress >>= 9;
|
|
||||||
uint64_t PDP_i = virtualAddress & 0x1ff;
|
|
||||||
|
|
||||||
PageDirectoryEntry PDE;
|
|
||||||
|
|
||||||
PDE = PML4->entries[PDP_i];
|
|
||||||
PageTable* PDP;
|
|
||||||
if (!PDE.Present)
|
|
||||||
{
|
|
||||||
PDP = (PageTable*)PMM::request_page();
|
|
||||||
ASSERT(!(PMM_DID_FAIL(PDP)));
|
|
||||||
memset(PDP, 0, 0x1000);
|
|
||||||
PDE.set_address((uint64_t)PDP);
|
|
||||||
PDE.Present = true;
|
|
||||||
PDE.ReadWrite = true;
|
|
||||||
if (flags & User) PDE.UserSuper = true;
|
|
||||||
PML4->entries[PDP_i] = PDE;
|
|
||||||
}
|
|
||||||
else { PDP = (PageTable*)((uint64_t)PDE.Address << 12); }
|
|
||||||
if ((flags & User) && !PDE.UserSuper)
|
|
||||||
{
|
|
||||||
PDE.UserSuper = true;
|
|
||||||
PML4->entries[PDP_i] = PDE;
|
|
||||||
}
|
|
||||||
|
|
||||||
PDE = PDP->entries[PD_i];
|
|
||||||
PageTable* PD;
|
|
||||||
if (!PDE.Present)
|
|
||||||
{
|
|
||||||
PD = (PageTable*)PMM::request_page();
|
|
||||||
ASSERT(!(PMM_DID_FAIL(PD)));
|
|
||||||
memset(PD, 0, 0x1000);
|
|
||||||
PDE.set_address((uint64_t)PD);
|
|
||||||
PDE.Present = true;
|
|
||||||
PDE.ReadWrite = true;
|
|
||||||
if (flags & User) PDE.UserSuper = true;
|
|
||||||
PDP->entries[PD_i] = PDE;
|
|
||||||
}
|
|
||||||
else { PD = (PageTable*)((uint64_t)PDE.Address << 12); }
|
|
||||||
if ((flags & User) && !PDE.UserSuper)
|
|
||||||
{
|
|
||||||
PDE.UserSuper = true;
|
|
||||||
PDP->entries[PD_i] = PDE;
|
|
||||||
}
|
|
||||||
|
|
||||||
PDE = PD->entries[PT_i];
|
|
||||||
PageTable* PT;
|
|
||||||
if (!PDE.Present)
|
|
||||||
{
|
|
||||||
PT = (PageTable*)PMM::request_page();
|
|
||||||
ASSERT(!(PMM_DID_FAIL(PT)));
|
|
||||||
memset(PT, 0, 0x1000);
|
|
||||||
PDE.set_address((uint64_t)PT);
|
|
||||||
PDE.Present = true;
|
|
||||||
PDE.ReadWrite = true;
|
|
||||||
if (flags & User) PDE.UserSuper = true;
|
|
||||||
PD->entries[PT_i] = PDE;
|
|
||||||
}
|
|
||||||
else { PT = (PageTable*)((uint64_t)PDE.Address << 12); }
|
|
||||||
if ((flags & User) && !PDE.UserSuper)
|
|
||||||
{
|
|
||||||
PDE.UserSuper = true;
|
|
||||||
PD->entries[PT_i] = PDE;
|
|
||||||
}
|
|
||||||
|
|
||||||
PDE = PT->entries[P_i];
|
|
||||||
PDE.Present = true;
|
|
||||||
PDE.ReadWrite = flags & ReadWrite;
|
|
||||||
PDE.UserSuper = flags & User;
|
|
||||||
PDE.set_address(physicalAddress);
|
|
||||||
PT->entries[P_i] = PDE;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void VMM::exit_syscall_context()
|
||||||
|
{
|
||||||
|
if (current_pml4 != user_address_space->get_pml4()) { switch_to_previous_user_address_space(); }
|
||||||
|
apply_address_space();
|
||||||
|
}
|
||||||
|
|
||||||
|
void VMM::apply_address_space()
|
||||||
|
{
|
||||||
|
write_cr3(current_pml4);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool VMM::is_using_kernel_address_space()
|
||||||
|
{
|
||||||
|
return current_pml4 == kernel_pml4;
|
||||||
|
}
|
||||||
|
|
||||||
|
void VMM::init()
|
||||||
|
{
|
||||||
|
kernel_pml4 = (PageTable*)read_cr3();
|
||||||
|
current_pml4 = kernel_pml4;
|
||||||
|
}
|
||||||
|
|
||||||
|
void VMM::unmap(uint64_t vaddr)
|
||||||
|
{
|
||||||
|
vaddr = round_down_to_nearest_page(vaddr);
|
||||||
|
|
||||||
|
PageDirectoryEntry* pde = find_pde(current_pml4, vaddr);
|
||||||
|
if (!pde) return; // Already unmapped
|
||||||
|
|
||||||
|
memset(pde, 0, sizeof(PageDirectoryEntry));
|
||||||
|
flush_tlb(vaddr);
|
||||||
|
}
|
||||||
|
|
||||||
|
void VMM::remap(uint64_t vaddr, int flags)
|
||||||
|
{
|
||||||
|
vaddr = round_down_to_nearest_page(vaddr);
|
||||||
|
|
||||||
|
PageDirectoryEntry* pde = find_pde(current_pml4, vaddr);
|
||||||
|
if (!pde) return; // Not mapped
|
||||||
|
|
||||||
|
if (flags & User) propagate_user(current_pml4, vaddr);
|
||||||
|
else
|
||||||
|
pde->user = false;
|
||||||
|
if (flags & ReadWrite) propagate_read_write(current_pml4, vaddr);
|
||||||
|
else
|
||||||
|
pde->read_write = false;
|
||||||
|
flush_tlb(vaddr);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t VMM::get_physical(uint64_t vaddr)
|
||||||
|
{
|
||||||
|
PageDirectoryEntry* pde = find_pde(current_pml4, round_down_to_nearest_page(vaddr));
|
||||||
|
if (!pde) return UINT64_MAX; // Not mapped
|
||||||
|
|
||||||
|
return pde->get_address() | (vaddr % PAGE_SIZE);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t VMM::get_flags(uint64_t vaddr)
|
||||||
|
{
|
||||||
|
PageDirectoryEntry* pde = find_pde(current_pml4, round_down_to_nearest_page(vaddr));
|
||||||
|
if (!pde) return 0; // Not mapped
|
||||||
|
|
||||||
|
uint64_t flags = 0;
|
||||||
|
if (pde->user) flags |= User;
|
||||||
|
if (pde->read_write) flags |= ReadWrite;
|
||||||
|
return flags;
|
||||||
|
}
|
||||||
|
|
||||||
|
void VMM::map(uint64_t vaddr, uint64_t paddr, int flags)
|
||||||
|
{
|
||||||
|
vaddr = round_down_to_nearest_page(vaddr);
|
||||||
|
PageDirectoryEntry* pde = find_pde(current_pml4, vaddr);
|
||||||
|
bool will_flush_tlb = true;
|
||||||
|
if (!pde)
|
||||||
|
{
|
||||||
|
pde = create_pde_if_not_exists(current_pml4, vaddr);
|
||||||
|
will_flush_tlb = false;
|
||||||
|
}
|
||||||
|
else if (pde->larger_pages)
|
||||||
|
{
|
||||||
|
unmap(vaddr);
|
||||||
|
pde = create_pde_if_not_exists(current_pml4, vaddr);
|
||||||
|
}
|
||||||
|
|
||||||
|
pde->set_address(round_down_to_nearest_page(paddr));
|
||||||
|
if (flags & User) propagate_user(current_pml4, vaddr);
|
||||||
|
else
|
||||||
|
pde->user = false;
|
||||||
|
if (flags & ReadWrite) propagate_read_write(current_pml4, vaddr);
|
||||||
|
else
|
||||||
|
pde->read_write = false;
|
||||||
|
if (will_flush_tlb) flush_tlb(vaddr);
|
||||||
|
}
|
||||||
|
|
||||||
|
PageDirectoryEntry* VMM::find_pde(PageTable* root, uint64_t vaddr)
|
||||||
|
{
|
||||||
|
uint64_t page_index;
|
||||||
|
PageDirectoryEntry* pde;
|
||||||
|
PageTable* pt = root;
|
||||||
|
|
||||||
|
uint64_t indexes[3];
|
||||||
|
|
||||||
|
decompose_vaddr(vaddr, page_index, indexes[2], indexes[1], indexes[0]);
|
||||||
|
|
||||||
|
for (int i = 0; i < 3;
|
||||||
|
i++) // Walk through the page map level 4, page directory pointer, and page directory to find the page table.
|
||||||
|
{
|
||||||
|
pde = &pt->entries[indexes[i]];
|
||||||
|
if (!pde->present) return nullptr;
|
||||||
|
else if (pde->larger_pages)
|
||||||
|
return pde;
|
||||||
|
else { pt = (PageTable*)pde->get_address(); }
|
||||||
|
}
|
||||||
|
|
||||||
|
pde = &pt->entries[page_index]; // PT
|
||||||
|
if (!pde->present) return nullptr;
|
||||||
|
return pde;
|
||||||
|
}
|
||||||
|
|
||||||
|
PageDirectoryEntry* VMM::create_pde_if_not_exists(PageTable* root, uint64_t vaddr)
|
||||||
|
{
|
||||||
|
uint64_t page_index;
|
||||||
|
PageDirectoryEntry* pde;
|
||||||
|
PageTable* pt = root;
|
||||||
|
|
||||||
|
uint64_t indexes[3];
|
||||||
|
|
||||||
|
decompose_vaddr(vaddr, page_index, indexes[2], indexes[1], indexes[0]);
|
||||||
|
|
||||||
|
auto pde_create_if_not_present = [&]() {
|
||||||
|
pt = (PageTable*)PMM::request_page();
|
||||||
|
ASSERT(!(PMM_DID_FAIL(pt)));
|
||||||
|
memset(pt, 0, PAGE_SIZE);
|
||||||
|
pde->set_address((uint64_t)pt);
|
||||||
|
pde->present = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
for (int i = 0; i < 3; i++)
|
||||||
|
{
|
||||||
|
pde = &pt->entries[indexes[i]];
|
||||||
|
if (!pde->present) { pde_create_if_not_present(); }
|
||||||
|
else if (pde->larger_pages)
|
||||||
|
return pde;
|
||||||
|
else { pt = (PageTable*)pde->get_address(); }
|
||||||
|
}
|
||||||
|
|
||||||
|
pde = &pt->entries[page_index];
|
||||||
|
if (!pde->present) { pde->present = true; }
|
||||||
|
return pde;
|
||||||
|
}
|
||||||
|
|
||||||
|
void VMM::propagate_read_write(PageTable* root, uint64_t vaddr)
|
||||||
|
{
|
||||||
|
uint64_t page_index;
|
||||||
|
PageDirectoryEntry* pde;
|
||||||
|
PageTable* pt = root;
|
||||||
|
|
||||||
|
uint64_t indexes[3];
|
||||||
|
|
||||||
|
decompose_vaddr(vaddr, page_index, indexes[2], indexes[1], indexes[0]);
|
||||||
|
|
||||||
|
for (int i = 0; i < 3; i++)
|
||||||
|
{
|
||||||
|
pde = &pt->entries[indexes[i]];
|
||||||
|
if (!pde->present) return;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
pde->read_write = true;
|
||||||
|
if (pde->larger_pages) return;
|
||||||
|
pt = (PageTable*)pde->get_address();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pde = &pt->entries[page_index];
|
||||||
|
if (!pde->present) return;
|
||||||
|
else
|
||||||
|
pde->read_write = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void VMM::propagate_user(PageTable* root, uint64_t vaddr)
|
||||||
|
{
|
||||||
|
uint64_t page_index;
|
||||||
|
PageDirectoryEntry* pde;
|
||||||
|
PageTable* pt = root;
|
||||||
|
|
||||||
|
uint64_t indexes[3];
|
||||||
|
|
||||||
|
decompose_vaddr(vaddr, page_index, indexes[2], indexes[1], indexes[0]);
|
||||||
|
|
||||||
|
for (int i = 0; i < 3; i++)
|
||||||
|
{
|
||||||
|
pde = &pt->entries[indexes[i]];
|
||||||
|
if (!pde->present) return;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
pde->user = true;
|
||||||
|
if (pde->larger_pages) return;
|
||||||
|
pt = (PageTable*)pde->get_address();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pde = &pt->entries[page_index];
|
||||||
|
if (!pde->present) return;
|
||||||
|
else
|
||||||
|
pde->user = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void VMM::flush_tlb(uint64_t addr)
|
||||||
|
{
|
||||||
|
asm volatile("invlpg (%0)" : : "r"(addr) : "memory");
|
||||||
|
}
|
||||||
|
|
||||||
|
void VMM::decompose_vaddr(uint64_t vaddr, uint64_t& page_index, uint64_t& pt_index, uint64_t& pd_index,
|
||||||
|
uint64_t& pdp_index)
|
||||||
|
{
|
||||||
|
vaddr >>= 12;
|
||||||
|
page_index = vaddr & 0x1ff;
|
||||||
|
vaddr >>= 9;
|
||||||
|
pt_index = vaddr & 0x1ff;
|
||||||
|
vaddr >>= 9;
|
||||||
|
pd_index = vaddr & 0x1ff;
|
||||||
|
vaddr >>= 9;
|
||||||
|
pdp_index = vaddr & 0x1ff;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t VMM::recompose_vaddr(uint64_t page_index, uint64_t pt_index, uint64_t pd_index, uint64_t pdp_index)
|
||||||
|
{
|
||||||
|
return pdp_index << 39 | pd_index << 30 | pt_index << 21 | page_index << 12;
|
||||||
|
}
|
||||||
|
|
||||||
|
void VMM::install_kernel_page_directory_into_address_space(AddressSpace& space)
|
||||||
|
{
|
||||||
|
PageTable* space_pml4 = space.get_pml4();
|
||||||
|
PageTable* kernel_last_pdp = (PageTable*)kernel_pml4->entries[511].get_address();
|
||||||
|
PageTable* kernel_last_pd = (PageTable*)kernel_last_pdp->entries[511].get_address();
|
||||||
|
|
||||||
|
PageTable* space_last_pdp = (PageTable*)PMM::request_page();
|
||||||
|
|
||||||
|
PageDirectoryEntry& space_last_pdp_pde = space_pml4->entries[511];
|
||||||
|
|
||||||
|
space_last_pdp_pde.present = true;
|
||||||
|
space_last_pdp_pde.read_write = true;
|
||||||
|
space_last_pdp_pde.set_address((uint64_t)space_last_pdp);
|
||||||
|
|
||||||
|
PageDirectoryEntry& space_last_pd_pde = space_last_pdp->entries[511];
|
||||||
|
|
||||||
|
space_last_pd_pde.present = true;
|
||||||
|
space_last_pd_pde.read_write = true;
|
||||||
|
space_last_pd_pde.set_address((uint64_t)kernel_last_pd);
|
||||||
|
}
|
@ -8,7 +8,7 @@
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifndef MOON_MINOR
|
#ifndef MOON_MINOR
|
||||||
#define MOON_MINOR 10
|
#define MOON_MINOR 11
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifndef _MOON_SUFFIX
|
#ifndef _MOON_SUFFIX
|
||||||
|
@ -8,6 +8,7 @@
|
|||||||
#include "interrupts/Interrupts.h"
|
#include "interrupts/Interrupts.h"
|
||||||
#include "io/IO.h"
|
#include "io/IO.h"
|
||||||
#include "log/Log.h"
|
#include "log/Log.h"
|
||||||
|
#include "memory/VMM.h"
|
||||||
#include "misc/hang.h"
|
#include "misc/hang.h"
|
||||||
#include "std/string.h"
|
#include "std/string.h"
|
||||||
|
|
||||||
@ -85,6 +86,11 @@ static void try_idt_triple_fault()
|
|||||||
[[noreturn]] void reboot()
|
[[noreturn]] void reboot()
|
||||||
{
|
{
|
||||||
Interrupts::disable();
|
Interrupts::disable();
|
||||||
|
if (!VMM::is_using_kernel_address_space())
|
||||||
|
{
|
||||||
|
VMM::switch_back_to_kernel_address_space();
|
||||||
|
VMM::apply_address_space();
|
||||||
|
}
|
||||||
kinfoln("Attempting reboot using ACPI");
|
kinfoln("Attempting reboot using ACPI");
|
||||||
try_acpi_reboot();
|
try_acpi_reboot();
|
||||||
kinfoln("Attempting reboot using keyboard RESET pulsing");
|
kinfoln("Attempting reboot using keyboard RESET pulsing");
|
||||||
|
@ -1,6 +0,0 @@
|
|||||||
#include "misc/utils.h"
|
|
||||||
|
|
||||||
uint64_t Utilities::get_blocks_from_size(uint64_t blocksize, uint64_t size)
|
|
||||||
{
|
|
||||||
return (size + (blocksize - 1)) / blocksize;
|
|
||||||
}
|
|
@ -1,4 +1,5 @@
|
|||||||
#include <string.h>
|
#include "std/string.h"
|
||||||
|
#include "std/stdlib.h"
|
||||||
|
|
||||||
size_t strlen(const char* __s)
|
size_t strlen(const char* __s)
|
||||||
{
|
{
|
||||||
@ -122,3 +123,11 @@ void* memmove(void* dest, void* src, size_t n)
|
|||||||
}
|
}
|
||||||
return dest;
|
return dest;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
char* strdup(const char* src)
|
||||||
|
{
|
||||||
|
size_t length = strlen(src);
|
||||||
|
char* duplicated = (char*)kmalloc(length + 1);
|
||||||
|
memcpy(duplicated, src, length + 1);
|
||||||
|
return duplicated;
|
||||||
|
}
|
@ -1,11 +1,14 @@
|
|||||||
#include "sys/Syscall.h"
|
#include "sys/Syscall.h"
|
||||||
#include "errno.h"
|
#include "errno.h"
|
||||||
#include "io/Serial.h"
|
#include "io/Serial.h"
|
||||||
|
#include "memory/VMM.h"
|
||||||
|
#include "std/string.h"
|
||||||
#include "thread/Scheduler.h"
|
#include "thread/Scheduler.h"
|
||||||
|
|
||||||
void Syscall::entry(Context* context)
|
void Syscall::entry(Context* context)
|
||||||
{
|
{
|
||||||
asm volatile("cli");
|
asm volatile("cli");
|
||||||
|
VMM::enter_syscall_context();
|
||||||
switch (context->rax)
|
switch (context->rax)
|
||||||
{
|
{
|
||||||
case SYS_exit: sys_exit(context, (int)context->rdi); break;
|
case SYS_exit: sys_exit(context, (int)context->rdi); break;
|
||||||
@ -20,6 +23,16 @@ void Syscall::entry(Context* context)
|
|||||||
case SYS_open: sys_open(context, (const char*)context->rdi, (int)context->rsi); break;
|
case SYS_open: sys_open(context, (const char*)context->rdi, (int)context->rsi); break;
|
||||||
case SYS_read: sys_read(context, (int)context->rdi, context->rsi, (char*)context->rdx); break;
|
case SYS_read: sys_read(context, (int)context->rdi, context->rsi, (char*)context->rdx); break;
|
||||||
case SYS_close: sys_close(context, (int)context->rdi); break;
|
case SYS_close: sys_close(context, (int)context->rdi); break;
|
||||||
|
case SYS_seek: sys_seek(context, (int)context->rdi, (long)context->rsi, (int)context->rdx); break;
|
||||||
|
case SYS_exec: sys_exec(context, (const char*)context->rdi); break;
|
||||||
default: context->rax = -ENOSYS; break;
|
default: context->rax = -ENOSYS; break;
|
||||||
}
|
}
|
||||||
|
VMM::exit_syscall_context();
|
||||||
|
}
|
||||||
|
|
||||||
|
char* Syscall::strdup_from_user(const char* user_string)
|
||||||
|
{
|
||||||
|
uint64_t phys = VMM::get_physical((uint64_t)user_string);
|
||||||
|
if (phys == (uint64_t)-1) { return nullptr; }
|
||||||
|
return strdup((const char*)phys);
|
||||||
}
|
}
|
@ -1,14 +1,19 @@
|
|||||||
#define MODULE "elf"
|
#define MODULE "elf"
|
||||||
|
|
||||||
#include "sys/elf/ELFLoader.h"
|
#include "sys/elf/ELFLoader.h"
|
||||||
|
#include "assert.h"
|
||||||
|
#include "errno.h"
|
||||||
#include "fs/VFS.h"
|
#include "fs/VFS.h"
|
||||||
#include "init/InitRD.h"
|
#include "init/InitRD.h"
|
||||||
#include "log/Log.h"
|
#include "log/Log.h"
|
||||||
|
#include "memory/Memory.h"
|
||||||
#include "memory/MemoryManager.h"
|
#include "memory/MemoryManager.h"
|
||||||
|
#include "memory/VMM.h"
|
||||||
#include "misc/utils.h"
|
#include "misc/utils.h"
|
||||||
#include "std/stdlib.h"
|
#include "std/stdlib.h"
|
||||||
#include "std/string.h"
|
#include "std/string.h"
|
||||||
#include "sys/elf/ELF.h"
|
#include "sys/elf/ELF.h"
|
||||||
|
#include "utils/Addresses.h"
|
||||||
|
|
||||||
static const char* format_permissions(uint32_t flags)
|
static const char* format_permissions(uint32_t flags)
|
||||||
{
|
{
|
||||||
@ -36,78 +41,72 @@ ELFImage* ELFLoader::load_elf_from_filesystem(const char* filename)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void* file = kmalloc(node->length);
|
ELFImage* result = load_elf_from_vfs(node);
|
||||||
if (VFS::read(node, 0, node->length, (char*)file) < 0)
|
|
||||||
{
|
|
||||||
kwarnln("Failed to read ELF image from file");
|
|
||||||
kfree(file);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
ELFImage* result =
|
|
||||||
load_elf_from_address((uintptr_t)file); // FIXME: Read headers and sections as we go along the file, to avoid
|
|
||||||
// loading the entire file at once into memory.
|
|
||||||
|
|
||||||
kfree(file);
|
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
ELFImage* ELFLoader::load_elf_from_address(uintptr_t addr)
|
long ELFLoader::check_elf_image_from_filesystem(const char* filename)
|
||||||
{
|
{
|
||||||
Elf64_Ehdr* elf_ehdr = (Elf64_Ehdr*)addr;
|
VFS::Node* node = VFS::resolve_path(filename);
|
||||||
if (strncmp((const char*)elf_ehdr->e_ident, ELFMAG, SELFMAG) != 0)
|
|
||||||
|
if (!node)
|
||||||
{
|
{
|
||||||
kwarnln("ELF file has invalid magic, skipping");
|
kwarnln("Failed to open file %s for checking", filename);
|
||||||
return 0;
|
return -ENOENT;
|
||||||
}
|
}
|
||||||
if (elf_ehdr->e_ident[EI_CLASS] != ELFCLASS64)
|
|
||||||
|
if (node->type == VFS_DIRECTORY)
|
||||||
{
|
{
|
||||||
kwarnln("ELF file is not ELF64, skipping");
|
kwarnln("Failed to check %s: is a directory", filename);
|
||||||
return 0;
|
return -EISDIR;
|
||||||
}
|
|
||||||
if (elf_ehdr->e_ident[EI_DATA] != ELFDATA2LSB)
|
|
||||||
{
|
|
||||||
kwarnln("ELF file is not little-endian, skipping");
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
if (elf_ehdr->e_type != ET_EXEC)
|
|
||||||
{
|
|
||||||
kwarnln("not supported: ELF file is not an executable");
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
if (elf_ehdr->e_machine != EM_MACH)
|
|
||||||
{
|
|
||||||
kwarnln("Unsupported target machine");
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
if (elf_ehdr->e_phnum == 0)
|
|
||||||
{
|
|
||||||
kwarnln("ELF file has no PHDRS");
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return check_elf_image(node);
|
||||||
|
}
|
||||||
|
|
||||||
|
ELFImage* ELFLoader::load_elf_from_vfs(VFS::Node* node)
|
||||||
|
{
|
||||||
|
Elf64_Ehdr elf_ehdr;
|
||||||
|
ASSERT(VFS::read(node, 0, sizeof(elf_ehdr), (char*)&elf_ehdr) >= 0);
|
||||||
|
ASSERT(strncmp((const char*)elf_ehdr.e_ident, ELFMAG, SELFMAG) ==
|
||||||
|
0); // If you haven't checked the ELF executable with check_elf_image() first, then an assertion fail is your
|
||||||
|
// fault =D
|
||||||
|
ASSERT(elf_ehdr.e_ident[EI_CLASS] == ELFCLASS64);
|
||||||
|
ASSERT(elf_ehdr.e_ident[EI_DATA] == ELFDATA2LSB);
|
||||||
|
ASSERT(elf_ehdr.e_type == ET_EXEC);
|
||||||
|
ASSERT(elf_ehdr.e_machine == EM_MACH);
|
||||||
|
ASSERT(elf_ehdr.e_phnum != 0);
|
||||||
ELFImage* image = (ELFImage*)kmalloc(sizeof(ELFImage) - sizeof(ELFSection));
|
ELFImage* image = (ELFImage*)kmalloc(sizeof(ELFImage) - sizeof(ELFSection));
|
||||||
memset(image, 0, sizeof(ELFImage) - sizeof(ELFSection));
|
memset(image, 0, sizeof(ELFImage) - sizeof(ELFSection));
|
||||||
image->entry = elf_ehdr->e_entry;
|
image->entry = elf_ehdr.e_entry;
|
||||||
int i;
|
int i;
|
||||||
Elf64_Phdr* phdr;
|
Elf64_Phdr phdr;
|
||||||
for (phdr = (Elf64_Phdr*)((uint64_t)addr + elf_ehdr->e_phoff), i = 0; i < elf_ehdr->e_phnum;
|
for (VFS::read(node, elf_ehdr.e_phoff, sizeof(Elf64_Phdr), (char*)&phdr), i = 0; i < elf_ehdr.e_phnum;
|
||||||
i++, phdr = (Elf64_Phdr*)((uint8_t*)phdr + elf_ehdr->e_phentsize))
|
i++, VFS::read(node, elf_ehdr.e_phoff + (i * elf_ehdr.e_phentsize), sizeof(Elf64_Phdr), (char*)&phdr))
|
||||||
{
|
{
|
||||||
if (phdr->p_type == PT_LOAD)
|
if (phdr.p_type == PT_LOAD)
|
||||||
{
|
{
|
||||||
kdbgln("Loading loadable segment at address %lx, file size %ld, mem size %ld, permissions %s",
|
kdbgln("Loading loadable segment at address %lx, file size %ld, mem size %ld, permissions %s", phdr.p_vaddr,
|
||||||
phdr->p_vaddr, phdr->p_filesz, phdr->p_memsz, format_permissions(phdr->p_flags));
|
phdr.p_filesz, phdr.p_memsz, format_permissions(phdr.p_flags));
|
||||||
if (!phdr->p_vaddr)
|
ASSERT(phdr.p_vaddr);
|
||||||
{
|
|
||||||
kerrorln("Address is NULL, this is invalid :(");
|
uint64_t pages = Utilities::get_blocks_from_size(PAGE_SIZE, (phdr.p_vaddr % PAGE_SIZE) + phdr.p_memsz);
|
||||||
return 0;
|
void* buffer = (void*)((uint64_t)MemoryManager::get_pages_at(round_down_to_nearest_page(phdr.p_vaddr),
|
||||||
}
|
pages, MAP_READ_WRITE) +
|
||||||
uint64_t pages = Utilities::get_blocks_from_size(0x1000, phdr->p_memsz);
|
(phdr.p_vaddr % PAGE_SIZE));
|
||||||
void* buffer = MemoryManager::get_pages_at(phdr->p_vaddr, pages,
|
|
||||||
phdr->p_flags & 2 ? MAP_READ_WRITE | MAP_USER : MAP_USER);
|
if (VMM::is_using_kernel_address_space()) { VMM::switch_to_previous_user_address_space(); }
|
||||||
memcpy(buffer, (void*)(addr + phdr->p_offset), phdr->p_filesz);
|
VMM::apply_address_space();
|
||||||
memset((void*)((uint64_t)buffer + phdr->p_filesz), 0, phdr->p_memsz - phdr->p_filesz);
|
|
||||||
|
VFS::read(node, phdr.p_offset, phdr.p_filesz, (char*)buffer);
|
||||||
|
memset((void*)((uint64_t)buffer + phdr.p_filesz), 0, phdr.p_memsz - phdr.p_filesz);
|
||||||
|
|
||||||
|
VMM::switch_back_to_kernel_address_space();
|
||||||
|
VMM::apply_address_space();
|
||||||
|
VMM::switch_to_previous_user_address_space();
|
||||||
|
|
||||||
|
MemoryManager::protect(buffer, pages, phdr.p_flags & 2 ? MAP_READ_WRITE | MAP_USER : MAP_USER);
|
||||||
|
|
||||||
image = (ELFImage*)krealloc(image, (sizeof(ELFImage) - sizeof(ELFSection)) +
|
image = (ELFImage*)krealloc(image, (sizeof(ELFImage) - sizeof(ELFSection)) +
|
||||||
(image->section_count + 1) * sizeof(ELFSection));
|
(image->section_count + 1) * sizeof(ELFSection));
|
||||||
ELFSection& section = image->sections[image->section_count];
|
ELFSection& section = image->sections[image->section_count];
|
||||||
@ -117,10 +116,86 @@ ELFImage* ELFLoader::load_elf_from_address(uintptr_t addr)
|
|||||||
}
|
}
|
||||||
else { kdbgln("skipping non-loadable segment"); }
|
else { kdbgln("skipping non-loadable segment"); }
|
||||||
}
|
}
|
||||||
if (!image->section_count)
|
ASSERT(image->section_count);
|
||||||
{
|
|
||||||
kfree(image);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
return image;
|
return image;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
long ELFLoader::check_elf_image(VFS::Node* node)
|
||||||
|
{
|
||||||
|
Elf64_Ehdr elf_ehdr;
|
||||||
|
if (VFS::read(node, 0, sizeof(elf_ehdr), (char*)&elf_ehdr) < 0)
|
||||||
|
{
|
||||||
|
kwarnln("Unable to read ELF header");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (strncmp((const char*)elf_ehdr.e_ident, ELFMAG, SELFMAG) != 0)
|
||||||
|
{
|
||||||
|
kwarnln("ELF file has invalid magic, skipping");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (elf_ehdr.e_ident[EI_CLASS] != ELFCLASS64)
|
||||||
|
{
|
||||||
|
kwarnln("ELF file is not ELF64, skipping");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (elf_ehdr.e_ident[EI_DATA] != ELFDATA2LSB)
|
||||||
|
{
|
||||||
|
kwarnln("ELF file is not little-endian, skipping");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (elf_ehdr.e_type != ET_EXEC)
|
||||||
|
{
|
||||||
|
kwarnln("not supported: ELF file is not an executable");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (elf_ehdr.e_machine != EM_MACH)
|
||||||
|
{
|
||||||
|
kwarnln("Unsupported target machine");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (elf_ehdr.e_phnum == 0)
|
||||||
|
{
|
||||||
|
kwarnln("ELF file has no PHDRS");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
int i;
|
||||||
|
int loadable_sections = 0;
|
||||||
|
long memusage = 0;
|
||||||
|
Elf64_Phdr phdr;
|
||||||
|
for (VFS::read(node, elf_ehdr.e_phoff, sizeof(Elf64_Phdr), (char*)&phdr), i = 0; i < elf_ehdr.e_phnum;
|
||||||
|
i++, VFS::read(node, elf_ehdr.e_phoff + (i * elf_ehdr.e_phentsize), sizeof(Elf64_Phdr), (char*)&phdr))
|
||||||
|
{
|
||||||
|
if (phdr.p_type == PT_LOAD)
|
||||||
|
{
|
||||||
|
if (!phdr.p_vaddr)
|
||||||
|
{
|
||||||
|
kerrorln("segment address is NULL, this is invalid :(");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (Memory::is_kernel_address(phdr.p_vaddr) || Memory::is_kernel_address(phdr.p_vaddr + phdr.p_memsz))
|
||||||
|
{
|
||||||
|
kerrorln("trying to load ELF into kernel memory");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
loadable_sections++;
|
||||||
|
memusage += Utilities::get_blocks_from_size(PAGE_SIZE, phdr.p_memsz) * PAGE_SIZE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!loadable_sections)
|
||||||
|
{
|
||||||
|
kwarnln("No loadable sections");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return memusage;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ELFLoader::release_elf_image(ELFImage* image)
|
||||||
|
{
|
||||||
|
for (uint64_t i = 0; i < image->section_count; i++)
|
||||||
|
{
|
||||||
|
ELFSection& section = image->sections[i];
|
||||||
|
kdbgln("Freeing up section %lx, was using %ld pages", section.base, section.pages);
|
||||||
|
MemoryManager::release_pages((void*)round_down_to_nearest_page(section.base), section.pages);
|
||||||
|
}
|
||||||
|
kfree(image);
|
||||||
|
}
|
96
kernel/src/sys/exec.cpp
Normal file
96
kernel/src/sys/exec.cpp
Normal file
@ -0,0 +1,96 @@
|
|||||||
|
#define MODULE "exec"
|
||||||
|
|
||||||
|
#include "assert.h"
|
||||||
|
#include "errno.h"
|
||||||
|
#include "interrupts/Interrupts.h"
|
||||||
|
#include "memory/MemoryManager.h"
|
||||||
|
#include "memory/PMM.h"
|
||||||
|
#include "memory/VMM.h"
|
||||||
|
#include "std/stdlib.h"
|
||||||
|
#include "std/string.h"
|
||||||
|
#include "sys/Syscall.h"
|
||||||
|
#include "sys/elf/ELFLoader.h"
|
||||||
|
#include "thread/Scheduler.h"
|
||||||
|
|
||||||
|
void sys_exec(Context* context, const char* pathname)
|
||||||
|
{
|
||||||
|
/*context->rax = -ENOSYS; // FIXME: Make exec() work under separate address spaces.
|
||||||
|
return;*/
|
||||||
|
|
||||||
|
char* kpathname = Syscall::strdup_from_user(pathname);
|
||||||
|
if (!kpathname)
|
||||||
|
{
|
||||||
|
context->rax = -EFAULT;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
kinfoln("exec(): executing %s", kpathname);
|
||||||
|
|
||||||
|
VFS::Node* program = VFS::resolve_path(kpathname);
|
||||||
|
if (!program)
|
||||||
|
{
|
||||||
|
kfree(kpathname);
|
||||||
|
context->rax = -ENOENT;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (program->type == VFS_DIRECTORY)
|
||||||
|
{
|
||||||
|
kfree(kpathname);
|
||||||
|
context->rax = -EISDIR;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
long memusage;
|
||||||
|
if ((memusage = ELFLoader::check_elf_image(program)) < 0)
|
||||||
|
{
|
||||||
|
kfree(kpathname);
|
||||||
|
context->rax = -ENOEXEC;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t allocated_stack = (uint64_t)MemoryManager::get_pages(TASK_PAGES_IN_STACK, MAP_READ_WRITE | MAP_USER);
|
||||||
|
if (!allocated_stack)
|
||||||
|
{
|
||||||
|
kfree(kpathname);
|
||||||
|
context->rax = -ENOMEM;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t allocated_stack_phys = VMM::get_physical(allocated_stack);
|
||||||
|
|
||||||
|
if ((uint64_t)memusage > PMM::get_free())
|
||||||
|
{
|
||||||
|
kfree(kpathname);
|
||||||
|
MemoryManager::release_pages((void*)allocated_stack, TASK_PAGES_IN_STACK);
|
||||||
|
context->rax = -ENOMEM;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Interrupts::disable();
|
||||||
|
ASSERT(!Interrupts::are_enabled()); // This part is pretty sensitive.
|
||||||
|
|
||||||
|
Task* task = Scheduler::current_task();
|
||||||
|
ASSERT(task);
|
||||||
|
|
||||||
|
// At this point, pretty much nothing can fail.
|
||||||
|
|
||||||
|
ELFImage* image = ELFLoader::load_elf_from_vfs(program);
|
||||||
|
ASSERT(image); // If check_elf_image succeeded, load_elf_from_vfs MUST succeed, unless something has gone terribly
|
||||||
|
// wrong.
|
||||||
|
|
||||||
|
task->allocated_stack = allocated_stack;
|
||||||
|
|
||||||
|
for (uint64_t i = 0; i < TASK_PAGES_IN_STACK; i++)
|
||||||
|
{
|
||||||
|
VMM::map(allocated_stack + (i * PAGE_SIZE), allocated_stack_phys + (i * PAGE_SIZE), MAP_READ_WRITE | MAP_USER);
|
||||||
|
}
|
||||||
|
|
||||||
|
Scheduler::reset_task(task, image);
|
||||||
|
|
||||||
|
set_context_from_task(*task, context);
|
||||||
|
|
||||||
|
kfree(kpathname);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
@ -5,6 +5,7 @@
|
|||||||
#include "log/Log.h"
|
#include "log/Log.h"
|
||||||
#include "memory/MemoryManager.h"
|
#include "memory/MemoryManager.h"
|
||||||
#include "memory/VMM.h"
|
#include "memory/VMM.h"
|
||||||
|
#include "misc/utils.h"
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
|
|
||||||
#define MAP_FAIL(errno) 0xffffffffffffff00 | (unsigned char)(errno)
|
#define MAP_FAIL(errno) 0xffffffffffffff00 | (unsigned char)(errno)
|
||||||
@ -13,7 +14,13 @@ void sys_mmap(Context* context, void* address, size_t size, int flags)
|
|||||||
{
|
{
|
||||||
if (size < PAGE_SIZE)
|
if (size < PAGE_SIZE)
|
||||||
{
|
{
|
||||||
kdbgln("sys_mmap: size too small");
|
kwarnln("mmap(): size too small");
|
||||||
|
context->rax = MAP_FAIL(EINVAL);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (size % PAGE_SIZE)
|
||||||
|
{
|
||||||
|
kwarnln("mmap(): size not a multiple of PAGE_SIZE");
|
||||||
context->rax = MAP_FAIL(EINVAL);
|
context->rax = MAP_FAIL(EINVAL);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -21,40 +28,42 @@ void sys_mmap(Context* context, void* address, size_t size, int flags)
|
|||||||
if (flags & MAP_READ_WRITE) real_flags |= MAP_READ_WRITE;
|
if (flags & MAP_READ_WRITE) real_flags |= MAP_READ_WRITE;
|
||||||
if (address)
|
if (address)
|
||||||
{
|
{
|
||||||
kdbgln("sys_mmap: %ld pages at address %p, %s", size / PAGE_SIZE, address,
|
kdbgln("mmap(): %ld pages at address %p, %s", size / PAGE_SIZE, address,
|
||||||
real_flags & MAP_READ_WRITE ? "rw" : "ro");
|
real_flags & MAP_READ_WRITE ? "rw" : "ro");
|
||||||
if (kernelVMM.getPhysical((uint64_t)address) != (uint64_t)-1) // Address is already used.
|
if (VMM::get_physical((uint64_t)address) != (uint64_t)-1) // Address is already used.
|
||||||
{
|
{
|
||||||
kdbgln("attempt to mmap an already mapped address");
|
kwarnln("attempt to map an already mapped address");
|
||||||
context->rax = MAP_FAIL(ENOMEM);
|
context->rax = MAP_FAIL(ENOMEM);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
uint64_t offset = (uint64_t)address % PAGE_SIZE;
|
uint64_t offset = (uint64_t)address % PAGE_SIZE;
|
||||||
void* result = MemoryManager::get_pages_at((uint64_t)address - offset, size / PAGE_SIZE, real_flags);
|
void* result = MemoryManager::get_pages_at((uint64_t)address - offset,
|
||||||
|
Utilities::get_blocks_from_size(PAGE_SIZE, size), real_flags);
|
||||||
if (result)
|
if (result)
|
||||||
{
|
{
|
||||||
kdbgln("mmap succeeded: %p", result);
|
kdbgln("mmap() succeeded: %p", result);
|
||||||
context->rax = (uint64_t)result;
|
context->rax = (uint64_t)result;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
kdbgln("mmap failed");
|
kwarnln("mmap() failed");
|
||||||
context->rax = MAP_FAIL(ENOMEM);
|
context->rax = MAP_FAIL(ENOMEM);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
kdbgln("sys_mmap: %ld pages at any address, %s", size / PAGE_SIZE, real_flags & MAP_READ_WRITE ? "rw" : "ro");
|
kdbgln("mmap(): %ld pages at any address, %s", Utilities::get_blocks_from_size(PAGE_SIZE, size),
|
||||||
void* result = MemoryManager::get_pages(size / PAGE_SIZE, real_flags);
|
real_flags & MAP_READ_WRITE ? "rw" : "ro");
|
||||||
|
void* result = MemoryManager::get_pages(Utilities::get_blocks_from_size(PAGE_SIZE, size), real_flags);
|
||||||
if (result)
|
if (result)
|
||||||
{
|
{
|
||||||
kdbgln("mmap succeeded: %p", result);
|
kdbgln("mmap() succeeded: %p", result);
|
||||||
context->rax = (uint64_t)result;
|
context->rax = (uint64_t)result;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
kdbgln("mmap failed");
|
kwarnln("mmap() failed");
|
||||||
context->rax = MAP_FAIL(ENOMEM);
|
context->rax = MAP_FAIL(ENOMEM);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -62,29 +71,35 @@ void sys_mmap(Context* context, void* address, size_t size, int flags)
|
|||||||
|
|
||||||
void sys_munmap(Context* context, void* address, size_t size)
|
void sys_munmap(Context* context, void* address, size_t size)
|
||||||
{
|
{
|
||||||
kdbgln("sys_munmap: attempting to unmap %p", address);
|
kdbgln("munmap(): attempting to unmap %p", address);
|
||||||
if (size < PAGE_SIZE)
|
if (size < PAGE_SIZE)
|
||||||
{
|
{
|
||||||
kdbgln("munmap failed: size is too small");
|
kwarnln("munmap() failed: size is too small");
|
||||||
|
context->rax = -EINVAL;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (size % PAGE_SIZE)
|
||||||
|
{
|
||||||
|
kwarnln("munmap() failed: size is not a multiple of PAGE_SIZE");
|
||||||
context->rax = -EINVAL;
|
context->rax = -EINVAL;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!address)
|
if (!address)
|
||||||
{
|
{
|
||||||
kdbgln("munmap failed: attempted to unmap page 0");
|
kwarnln("munmap() failed: attempted to unmap page 0");
|
||||||
context->rax = -EINVAL;
|
context->rax = -EINVAL;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
uint64_t flags = kernelVMM.getFlags((uint64_t)address);
|
uint64_t flags = VMM::get_flags((uint64_t)address);
|
||||||
if (!(flags & MAP_USER))
|
if (!(flags & MAP_USER))
|
||||||
{
|
{
|
||||||
kdbgln("munmap failed: attempted to unmap a kernel page");
|
kwarnln("munmap() failed: attempted to unmap a non-existent or kernel page");
|
||||||
context->rax = -EINVAL;
|
context->rax = -EINVAL;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
uint64_t offset = (uint64_t)address % PAGE_SIZE;
|
uint64_t offset = (uint64_t)address % PAGE_SIZE;
|
||||||
MemoryManager::release_pages((void*)((uint64_t)address - offset), size / PAGE_SIZE);
|
MemoryManager::release_pages((void*)((uint64_t)address - offset), Utilities::get_blocks_from_size(PAGE_SIZE, size));
|
||||||
kdbgln("munmap succeeded");
|
kdbgln("munmap() succeeded");
|
||||||
context->rax = 0;
|
context->rax = 0;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
@ -4,46 +4,85 @@
|
|||||||
#include "interrupts/Context.h"
|
#include "interrupts/Context.h"
|
||||||
#include "io/Serial.h"
|
#include "io/Serial.h"
|
||||||
#include "log/Log.h"
|
#include "log/Log.h"
|
||||||
|
#include "memory/VMM.h"
|
||||||
#include "render/TextRenderer.h"
|
#include "render/TextRenderer.h"
|
||||||
|
#include "std/stdlib.h"
|
||||||
|
#include "sys/Syscall.h"
|
||||||
#include "thread/Scheduler.h"
|
#include "thread/Scheduler.h"
|
||||||
#include "thread/Task.h"
|
#include "thread/Task.h"
|
||||||
|
|
||||||
#define OPEN_READ 1
|
#define OPEN_READ 1
|
||||||
#define OPEN_WRITE 2
|
#define OPEN_WRITE 2
|
||||||
|
|
||||||
#define STDIO_FAIL(function, error) kwarnln("%s failed with %s", #function, #error)
|
#define SEEK_SET 0
|
||||||
|
#define SEEK_CUR 1
|
||||||
|
#define SEEK_END 2
|
||||||
|
|
||||||
void sys_write(Context* context, int fd, size_t size, const char* addr)
|
void sys_seek(Context* context, int fd, long offset, int whence)
|
||||||
{
|
{
|
||||||
if (!addr)
|
if (whence < SEEK_SET || whence > SEEK_END)
|
||||||
{
|
{
|
||||||
STDIO_FAIL(write, EINVAL);
|
context->rax = -EINVAL;
|
||||||
context->rax = -EINVAL; // FIXME: This should probably return EFAULT.
|
return;
|
||||||
|
}
|
||||||
|
if (fd >= TASK_MAX_FDS || fd < 0)
|
||||||
|
{
|
||||||
|
context->rax = -EBADF;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Task* current_task = Scheduler::current_task();
|
||||||
|
if (!current_task->files[fd].is_open())
|
||||||
|
{
|
||||||
|
context->rax = -EBADF;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
long new_offset;
|
||||||
|
if (whence == SEEK_SET) new_offset = offset;
|
||||||
|
else if (whence == SEEK_CUR)
|
||||||
|
new_offset = offset + current_task->files[fd].offset();
|
||||||
|
else if (whence == SEEK_END)
|
||||||
|
new_offset = current_task->files[fd].length() + offset;
|
||||||
|
else
|
||||||
|
__builtin_unreachable();
|
||||||
|
if (new_offset < 0)
|
||||||
|
{
|
||||||
|
context->rax = -EINVAL; // FIXME: Is this the right error?
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
int result = current_task->files[fd].seek(new_offset);
|
||||||
|
if (result < 0)
|
||||||
|
{
|
||||||
|
context->rax = result;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
context->rax = new_offset;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
void sys_write(Context* context, int fd, size_t size, const char* addr)
|
||||||
|
{
|
||||||
|
if (!addr)
|
||||||
|
{
|
||||||
|
context->rax = -EFAULT;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (fd >= TASK_MAX_FDS || fd < 0)
|
if (fd >= TASK_MAX_FDS || fd < 0)
|
||||||
{
|
{
|
||||||
kwarnln("file descriptor %d is outside the valid range", fd);
|
|
||||||
STDIO_FAIL(write, EBADF);
|
|
||||||
context->rax = -EBADF;
|
context->rax = -EBADF;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
Task* current_task = Scheduler::current_task();
|
Task* current_task = Scheduler::current_task();
|
||||||
if (!current_task->files[fd].is_open())
|
if (!current_task->files[fd].is_open())
|
||||||
{
|
{
|
||||||
kwarnln("file descriptor %d is not open", fd);
|
|
||||||
STDIO_FAIL(write, EBADF);
|
|
||||||
context->rax = -EBADF;
|
context->rax = -EBADF;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!current_task->files[fd].can_write())
|
if (!current_task->files[fd].can_write())
|
||||||
{
|
{
|
||||||
kwarnln("file descriptor %d is not open for writing", fd);
|
|
||||||
STDIO_FAIL(write, EBADF);
|
|
||||||
context->rax = -EBADF;
|
context->rax = -EBADF;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
ssize_t result = current_task->files[fd].write(size, addr);
|
ssize_t result = current_task->files[fd].write(size, (const char*)VMM::get_physical((uint64_t)addr));
|
||||||
context->rax = (size_t)result;
|
context->rax = (size_t)result;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -59,15 +98,21 @@ void sys_open(Context* context, const char* filename, int flags)
|
|||||||
|
|
||||||
if (fd == TASK_MAX_FDS)
|
if (fd == TASK_MAX_FDS)
|
||||||
{
|
{
|
||||||
STDIO_FAIL(open, EMFILE);
|
|
||||||
context->rax = -EMFILE;
|
context->rax = -EMFILE;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
VFS::Node* node = VFS::resolve_path(filename);
|
char* kernel_filename = Syscall::strdup_from_user(filename);
|
||||||
|
if (!kernel_filename)
|
||||||
|
{
|
||||||
|
context->rax = -EFAULT;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
VFS::Node* node = VFS::resolve_path(kernel_filename);
|
||||||
if (!node)
|
if (!node)
|
||||||
{
|
{
|
||||||
STDIO_FAIL(open, ENOENT);
|
kfree(kernel_filename);
|
||||||
context->rax = -ENOENT;
|
context->rax = -ENOENT;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -76,17 +121,18 @@ void sys_open(Context* context, const char* filename, int flags)
|
|||||||
bool can_write = (flags & OPEN_WRITE) > 0;
|
bool can_write = (flags & OPEN_WRITE) > 0;
|
||||||
if (!can_read && !can_write)
|
if (!can_read && !can_write)
|
||||||
{
|
{
|
||||||
STDIO_FAIL(open, EINVAL);
|
kfree(kernel_filename);
|
||||||
context->rax = -EINVAL;
|
context->rax = -EINVAL;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
kdbgln("open(): opening %s %s, allocated file descriptor %d", filename,
|
kdbgln("open(): opening %s %s, allocated file descriptor %d", kernel_filename,
|
||||||
(can_read && can_write) ? "rw"
|
(can_read && can_write) ? "rw"
|
||||||
: can_read ? "r-"
|
: can_read ? "r-"
|
||||||
: "-w",
|
: "-w",
|
||||||
fd);
|
fd);
|
||||||
|
|
||||||
|
kfree(kernel_filename);
|
||||||
current_task->files[fd].open(node, can_read, can_write);
|
current_task->files[fd].open(node, can_read, can_write);
|
||||||
context->rax = fd;
|
context->rax = fd;
|
||||||
return;
|
return;
|
||||||
@ -96,24 +142,21 @@ void sys_read(Context* context, int fd, size_t size, char* buffer)
|
|||||||
{
|
{
|
||||||
if (!buffer)
|
if (!buffer)
|
||||||
{
|
{
|
||||||
STDIO_FAIL(read, EINVAL);
|
context->rax = -EFAULT;
|
||||||
context->rax = -EINVAL; // FIXME: This should probably return EFAULT.
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (fd >= TASK_MAX_FDS || fd < 0)
|
if (fd >= TASK_MAX_FDS || fd < 0)
|
||||||
{
|
{
|
||||||
STDIO_FAIL(read, EBADF);
|
|
||||||
context->rax = -EBADF;
|
context->rax = -EBADF;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
Task* current_task = Scheduler::current_task();
|
Task* current_task = Scheduler::current_task();
|
||||||
if (!current_task->files[fd].is_open() || !current_task->files[fd].can_read())
|
if (!current_task->files[fd].is_open() || !current_task->files[fd].can_read())
|
||||||
{
|
{
|
||||||
STDIO_FAIL(read, EBADF);
|
|
||||||
context->rax = -EBADF;
|
context->rax = -EBADF;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
ssize_t result = current_task->files[fd].read(size, buffer);
|
ssize_t result = current_task->files[fd].read(size, (char*)VMM::get_physical((uint64_t)buffer));
|
||||||
context->rax = (size_t)result;
|
context->rax = (size_t)result;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -122,14 +165,12 @@ void sys_close(Context* context, int fd)
|
|||||||
{
|
{
|
||||||
if (fd >= TASK_MAX_FDS || fd < 0)
|
if (fd >= TASK_MAX_FDS || fd < 0)
|
||||||
{
|
{
|
||||||
STDIO_FAIL(close, EBADF);
|
|
||||||
context->rax = -EBADF;
|
context->rax = -EBADF;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
Task* current_task = Scheduler::current_task();
|
Task* current_task = Scheduler::current_task();
|
||||||
if (!current_task->files[fd].is_open())
|
if (!current_task->files[fd].is_open())
|
||||||
{
|
{
|
||||||
STDIO_FAIL(close, EBADF);
|
|
||||||
context->rax = -EBADF;
|
context->rax = -EBADF;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -7,14 +7,15 @@
|
|||||||
#include "memory/MemoryManager.h"
|
#include "memory/MemoryManager.h"
|
||||||
#include "memory/VMM.h"
|
#include "memory/VMM.h"
|
||||||
#include "misc/hang.h"
|
#include "misc/hang.h"
|
||||||
|
#include "misc/utils.h"
|
||||||
#include "panic/Panic.h"
|
#include "panic/Panic.h"
|
||||||
#include "std/stdlib.h"
|
#include "std/stdlib.h"
|
||||||
#include "std/string.h"
|
#include "std/string.h"
|
||||||
#include "sys/elf/ELFLoader.h"
|
#include "sys/elf/ELFLoader.h"
|
||||||
#include "thread/PIT.h"
|
#include "thread/PIT.h"
|
||||||
#include "thread/Task.h"
|
#include "thread/Task.h"
|
||||||
|
#include "utils/Addresses.h"
|
||||||
#define TASK_PAGES_IN_STACK 4
|
#include "utils/Registers.h"
|
||||||
|
|
||||||
static uint64_t task_num = 0;
|
static uint64_t task_num = 0;
|
||||||
|
|
||||||
@ -35,11 +36,12 @@ void Scheduler::init()
|
|||||||
memset(&idle_task, 0, sizeof(Task));
|
memset(&idle_task, 0, sizeof(Task));
|
||||||
idle_task.id = free_tid++;
|
idle_task.id = free_tid++;
|
||||||
idle_task.regs.rip = (uint64_t)idle_task_function;
|
idle_task.regs.rip = (uint64_t)idle_task_function;
|
||||||
idle_task.regs.rsp = (uint64_t)MemoryManager::get_page();
|
idle_task.regs.rsp = get_top_of_stack((uint64_t)MemoryManager::get_page(), 1);
|
||||||
idle_task.regs.cs = 0x08;
|
idle_task.regs.cs = 0x08;
|
||||||
idle_task.regs.ss = 0x10;
|
idle_task.regs.ss = 0x10;
|
||||||
idle_task.regs.rflags = (1 << 21) | (1 << 9);
|
idle_task.regs.rflags = (1 << 21) | (1 << 9);
|
||||||
idle_task.task_sleep = 1000;
|
idle_task.task_sleep = 1000;
|
||||||
|
idle_task.user_task = false;
|
||||||
idle_task.state = idle_task.Idle;
|
idle_task.state = idle_task.Idle;
|
||||||
|
|
||||||
base_task = new Task;
|
base_task = new Task;
|
||||||
@ -50,6 +52,7 @@ void Scheduler::init()
|
|||||||
sched_current_task->next_task = sched_current_task;
|
sched_current_task->next_task = sched_current_task;
|
||||||
sched_current_task->prev_task = sched_current_task;
|
sched_current_task->prev_task = sched_current_task;
|
||||||
sched_current_task->state = sched_current_task->Running;
|
sched_current_task->state = sched_current_task->Running;
|
||||||
|
sched_current_task->user_task = false;
|
||||||
task_num++;
|
task_num++;
|
||||||
// the other registers will be saved next task switch
|
// the other registers will be saved next task switch
|
||||||
|
|
||||||
@ -60,16 +63,16 @@ void Scheduler::add_kernel_task(void (*task)(void))
|
|||||||
{
|
{
|
||||||
Task* new_task = new Task;
|
Task* new_task = new Task;
|
||||||
ASSERT(new_task);
|
ASSERT(new_task);
|
||||||
|
new_task->user_task = false;
|
||||||
new_task->id = free_tid++;
|
new_task->id = free_tid++;
|
||||||
new_task->regs.rip = (uint64_t)task;
|
new_task->regs.rip = (uint64_t)task;
|
||||||
new_task->allocated_stack =
|
new_task->allocated_stack =
|
||||||
(uint64_t)MemoryManager::get_pages(TASK_PAGES_IN_STACK); // 16 KB is enough for everyone, right?
|
(uint64_t)MemoryManager::get_pages(TASK_PAGES_IN_STACK); // 16 KB is enough for everyone, right?
|
||||||
new_task->regs.rsp = new_task->allocated_stack + (PAGE_SIZE * TASK_PAGES_IN_STACK) - sizeof(uintptr_t);
|
new_task->regs.rsp = get_top_of_stack(new_task->allocated_stack, TASK_PAGES_IN_STACK);
|
||||||
new_task->regs.cs = 0x08;
|
new_task->regs.cs = 0x08;
|
||||||
new_task->regs.ss = 0x10;
|
new_task->regs.ss = 0x10;
|
||||||
new_task->regs.ds = 0x10;
|
new_task->regs.ds = 0x10;
|
||||||
asm volatile("pushfq; movq (%%rsp), %%rax; movq %%rax, %0; popfq;" : "=m"(new_task->regs.rflags)::"%rax");
|
new_task->regs.rflags = read_rflags() | 0x200; // enable interrupts
|
||||||
new_task->regs.rflags |= 0x200; // enable interrupts
|
|
||||||
new_task->task_sleep = 0;
|
new_task->task_sleep = 0;
|
||||||
new_task->task_time = 0;
|
new_task->task_time = 0;
|
||||||
new_task->cpu_time = 0;
|
new_task->cpu_time = 0;
|
||||||
@ -84,19 +87,16 @@ void Scheduler::add_kernel_task(void (*task)(void))
|
|||||||
new_task->id, new_task->regs.rsp, task_num);
|
new_task->id, new_task->regs.rsp, task_num);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Scheduler::add_user_task(void* task)
|
Task* Scheduler::create_user_task()
|
||||||
{
|
{
|
||||||
Task* new_task = new Task;
|
Task* new_task = new Task;
|
||||||
ASSERT(new_task);
|
ASSERT(new_task);
|
||||||
|
memset(&new_task->regs, 0, sizeof(Context));
|
||||||
|
new_task->user_task = true;
|
||||||
new_task->id = free_tid++;
|
new_task->id = free_tid++;
|
||||||
new_task->regs.rip = (uint64_t)task;
|
|
||||||
new_task->allocated_stack = (uint64_t)MemoryManager::get_pages(
|
new_task->allocated_stack = (uint64_t)MemoryManager::get_pages(
|
||||||
TASK_PAGES_IN_STACK, MAP_READ_WRITE | MAP_USER); // 16 KB is enough for everyone, right?
|
TASK_PAGES_IN_STACK, MAP_READ_WRITE | MAP_USER); // 16 KB is enough for everyone, right?
|
||||||
new_task->regs.rsp = new_task->allocated_stack + (PAGE_SIZE * TASK_PAGES_IN_STACK) - sizeof(uintptr_t);
|
new_task->regs.rsp = get_top_of_stack(new_task->allocated_stack, TASK_PAGES_IN_STACK);
|
||||||
new_task->regs.cs = 0x18 | 0x03;
|
|
||||||
new_task->regs.ss = 0x20 | 0x03;
|
|
||||||
new_task->regs.ds = 0x20 | 0x03;
|
|
||||||
new_task->regs.rflags = (1 << 21) | (1 << 9); // enable interrupts
|
|
||||||
new_task->task_sleep = 0;
|
new_task->task_sleep = 0;
|
||||||
new_task->task_time = 0;
|
new_task->task_time = 0;
|
||||||
new_task->cpu_time = 0;
|
new_task->cpu_time = 0;
|
||||||
@ -105,30 +105,36 @@ void Scheduler::add_user_task(void* task)
|
|||||||
base_task->prev_task = new_task;
|
base_task->prev_task = new_task;
|
||||||
new_task->next_task = base_task;
|
new_task->next_task = base_task;
|
||||||
end_task = new_task;
|
end_task = new_task;
|
||||||
new_task->state = new_task->Running;
|
|
||||||
task_num++;
|
task_num++;
|
||||||
kinfoln("Adding user task: starts at %lx, tid %ld, stack at %lx, total tasks: %ld", new_task->regs.rip,
|
return new_task;
|
||||||
new_task->id, new_task->regs.rsp, task_num);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Scheduler::load_user_task(const char* filename)
|
void Scheduler::load_user_task(const char* filename)
|
||||||
{
|
{
|
||||||
kinfoln("Loading user task: %s", filename);
|
kinfoln("Loading user task: %s", filename);
|
||||||
Task* new_task = new Task;
|
Interrupts::push_and_disable();
|
||||||
ASSERT(new_task);
|
if (ELFLoader::check_elf_image_from_filesystem(filename) < 0)
|
||||||
new_task->id = free_tid++;
|
|
||||||
ELFImage* image = ELFLoader::load_elf_from_filesystem(filename);
|
|
||||||
if (!image)
|
|
||||||
{
|
{
|
||||||
kerrorln("Failed to load %s from initrd", filename);
|
kerrorln("Failed to load %s from initrd", filename);
|
||||||
delete new_task;
|
Interrupts::pop();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
Task* new_task = new Task;
|
||||||
|
ASSERT(new_task);
|
||||||
|
memset(&new_task->regs, 0, sizeof(Context));
|
||||||
|
new_task->id = free_tid++;
|
||||||
|
new_task->address_space = AddressSpace::create();
|
||||||
|
VMM::switch_to_user_address_space(new_task->address_space);
|
||||||
|
ELFImage* image = ELFLoader::load_elf_from_filesystem(
|
||||||
|
filename); // FIXME: TOCTOU? Right now, impossible, since interrupts are disabled and SMP is not a thing. But in
|
||||||
|
// the future, it might be possible.
|
||||||
|
ASSERT(image);
|
||||||
|
new_task->user_task = true;
|
||||||
new_task->regs.rip = image->entry;
|
new_task->regs.rip = image->entry;
|
||||||
new_task->image = image;
|
new_task->image = image;
|
||||||
new_task->allocated_stack = (uint64_t)MemoryManager::get_pages(
|
new_task->allocated_stack = (uint64_t)MemoryManager::get_pages(
|
||||||
TASK_PAGES_IN_STACK, MAP_READ_WRITE | MAP_USER); // 16 KB is enough for everyone, right?
|
TASK_PAGES_IN_STACK, MAP_READ_WRITE | MAP_USER); // 16 KB is enough for everyone, right?
|
||||||
new_task->regs.rsp = new_task->allocated_stack + (PAGE_SIZE * TASK_PAGES_IN_STACK) - sizeof(uintptr_t);
|
new_task->regs.rsp = get_top_of_stack(new_task->allocated_stack, TASK_PAGES_IN_STACK);
|
||||||
new_task->regs.cs = 0x18 | 0x03;
|
new_task->regs.cs = 0x18 | 0x03;
|
||||||
new_task->regs.ss = 0x20 | 0x03;
|
new_task->regs.ss = 0x20 | 0x03;
|
||||||
new_task->regs.ds = 0x20 | 0x03;
|
new_task->regs.ds = 0x20 | 0x03;
|
||||||
@ -145,6 +151,25 @@ void Scheduler::load_user_task(const char* filename)
|
|||||||
task_num++;
|
task_num++;
|
||||||
kinfoln("Adding user task: loaded at %lx, tid %ld, stack at %lx, total tasks: %ld", new_task->regs.rip,
|
kinfoln("Adding user task: loaded at %lx, tid %ld, stack at %lx, total tasks: %ld", new_task->regs.rip,
|
||||||
new_task->id, new_task->regs.rsp, task_num);
|
new_task->id, new_task->regs.rsp, task_num);
|
||||||
|
VMM::switch_back_to_kernel_address_space();
|
||||||
|
Interrupts::pop();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Scheduler::reset_task(Task* task, ELFImage* new_image)
|
||||||
|
{
|
||||||
|
memset(&task->regs, 0, sizeof(Context));
|
||||||
|
task->state = task->Running;
|
||||||
|
task->regs.rip = new_image->entry;
|
||||||
|
task->image = new_image;
|
||||||
|
task->regs.rsp = get_top_of_stack(task->allocated_stack, TASK_PAGES_IN_STACK);
|
||||||
|
task->regs.cs = 0x18 | 0x03;
|
||||||
|
task->regs.ss = 0x20 | 0x03;
|
||||||
|
task->regs.ds = 0x20 | 0x03;
|
||||||
|
task->regs.rflags = (1 << 21) | (1 << 9); // enable interrupts
|
||||||
|
task->task_sleep = 0;
|
||||||
|
task->cpu_time = 0;
|
||||||
|
kinfoln("Resetting task: loaded at %lx, tid %ld, stack at %lx, total tasks: %ld", task->regs.rip, task->id,
|
||||||
|
task->regs.rsp, task_num);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Scheduler::reap_task(Task* task)
|
void Scheduler::reap_task(Task* task)
|
||||||
@ -153,18 +178,29 @@ void Scheduler::reap_task(Task* task)
|
|||||||
task_num--;
|
task_num--;
|
||||||
Task* exiting_task = task;
|
Task* exiting_task = task;
|
||||||
ASSERT(task->id != 0); // WHY IN THE WORLD WOULD WE BE REAPING THE IDLE TASK?
|
ASSERT(task->id != 0); // WHY IN THE WORLD WOULD WE BE REAPING THE IDLE TASK?
|
||||||
|
if (exiting_task->is_user_task())
|
||||||
|
{
|
||||||
|
VMM::switch_back_to_kernel_address_space();
|
||||||
|
VMM::apply_address_space();
|
||||||
|
VMM::switch_to_user_address_space(exiting_task->address_space);
|
||||||
|
}
|
||||||
kinfoln("reaping task %ld, exited with code %ld", exiting_task->id, exiting_task->exit_status);
|
kinfoln("reaping task %ld, exited with code %ld", exiting_task->id, exiting_task->exit_status);
|
||||||
if (exiting_task->allocated_stack)
|
if (exiting_task->allocated_stack)
|
||||||
MemoryManager::release_pages((void*)exiting_task->allocated_stack, TASK_PAGES_IN_STACK);
|
MemoryManager::release_pages((void*)exiting_task->allocated_stack, TASK_PAGES_IN_STACK);
|
||||||
if (exiting_task->image)
|
if (exiting_task->image) // FIXME: Also free pages the task has mmap-ed but not munmap-ed.
|
||||||
{
|
{
|
||||||
for (uint64_t i = 0; i < exiting_task->image->section_count; i++)
|
// ELFLoader::release_elf_image(exiting_task->image);
|
||||||
{
|
|
||||||
ELFSection& section = exiting_task->image->sections[i];
|
|
||||||
kdbgln("Task was using region %lx, which used %ld pages", section.base, section.pages);
|
|
||||||
}
|
|
||||||
kfree(exiting_task->image);
|
kfree(exiting_task->image);
|
||||||
}
|
}
|
||||||
|
if (exiting_task->is_user_task())
|
||||||
|
{
|
||||||
|
VMM::switch_back_to_kernel_address_space();
|
||||||
|
VMM::apply_address_space();
|
||||||
|
Interrupts::push_and_enable();
|
||||||
|
exiting_task->address_space.destroy();
|
||||||
|
Interrupts::pop();
|
||||||
|
}
|
||||||
|
for (int i = 0; i < TASK_MAX_FDS; i++) { exiting_task->files[i].close(); }
|
||||||
delete exiting_task;
|
delete exiting_task;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -291,7 +327,17 @@ void Scheduler::task_yield(Context* context)
|
|||||||
{
|
{
|
||||||
task_save_floating(*original_task);
|
task_save_floating(*original_task);
|
||||||
}
|
}
|
||||||
if (sched_current_task->is_user_task()) { task_restore_floating(*sched_current_task); }
|
if (sched_current_task->is_user_task())
|
||||||
|
{
|
||||||
|
VMM::switch_to_user_address_space(sched_current_task->address_space);
|
||||||
|
VMM::apply_address_space();
|
||||||
|
task_restore_floating(*sched_current_task);
|
||||||
|
}
|
||||||
|
else if (!was_idle && original_task->is_user_task() && !sched_current_task->is_user_task())
|
||||||
|
{
|
||||||
|
VMM::switch_back_to_kernel_address_space();
|
||||||
|
VMM::apply_address_space();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
sched_current_task->task_time = 20;
|
sched_current_task->task_time = 20;
|
||||||
set_context_from_task(*sched_current_task, context);
|
set_context_from_task(*sched_current_task, context);
|
||||||
|
@ -25,5 +25,5 @@ void task_restore_floating(Task& task)
|
|||||||
|
|
||||||
bool Task::is_user_task()
|
bool Task::is_user_task()
|
||||||
{
|
{
|
||||||
return (regs.cs & 3) > 0;
|
return user_task;
|
||||||
}
|
}
|
8
libs/libc/include/bits/seek.h
Normal file
8
libs/libc/include/bits/seek.h
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
#ifndef _BITS_SEEK_H
|
||||||
|
#define _BITS_SEEK_H
|
||||||
|
|
||||||
|
#define SEEK_SET 0 // Seek from beginning of file.
|
||||||
|
#define SEEK_CUR 1 // Seek from current position.
|
||||||
|
#define SEEK_END 2 // Seek from end of file.
|
||||||
|
|
||||||
|
#endif
|
@ -1,15 +1,18 @@
|
|||||||
#ifndef _ERRNO_H
|
#ifndef _ERRNO_H
|
||||||
#define _ERRNO_H
|
#define _ERRNO_H
|
||||||
|
|
||||||
|
/* The last error encountered during a call to a library or system function. */
|
||||||
extern int errno;
|
extern int errno;
|
||||||
|
|
||||||
#define EPERM 1
|
#define EPERM 1 // Operation not permitted
|
||||||
#define ENOENT 2
|
#define ENOENT 2 // No such file or directory
|
||||||
#define EBADF 9
|
#define ENOEXEC 8 // Exec format error
|
||||||
#define ENOMEM 12
|
#define EBADF 9 // Bad file descriptor
|
||||||
#define EISDIR 21
|
#define ENOMEM 12 // Cannot allocate memory
|
||||||
#define EINVAL 22
|
#define EFAULT 14 // Bad address
|
||||||
#define EMFILE 24
|
#define EISDIR 21 // Is a directory
|
||||||
#define ENOSYS 38
|
#define EINVAL 22 // Invalid argument
|
||||||
|
#define EMFILE 24 // Too many open files
|
||||||
|
#define ENOSYS 38 // Function not implemented
|
||||||
|
|
||||||
#endif
|
#endif
|
@ -1,8 +1,11 @@
|
|||||||
#ifndef _FCNTL_H
|
#ifndef _FCNTL_H
|
||||||
#define _FCNTL_H
|
#define _FCNTL_H
|
||||||
|
|
||||||
|
/* Open for reading only. */
|
||||||
#define O_RDONLY 1
|
#define O_RDONLY 1
|
||||||
|
/* Open for writing only. */
|
||||||
#define O_WRONLY 2
|
#define O_WRONLY 2
|
||||||
|
/* Open for reading and writing. */
|
||||||
#define O_RDWR 3
|
#define O_RDWR 3
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
@ -10,7 +13,8 @@ extern "C"
|
|||||||
{
|
{
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
int open(const char*, int);
|
/* Opens the file specified by pathname. Returns a file descriptor on success, or -1 on error. */
|
||||||
|
int open(const char* pathname, int flags);
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
|
@ -9,8 +9,13 @@ extern "C"
|
|||||||
{
|
{
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
/* Returns the current program's thread identifier. */
|
||||||
pid_t gettid();
|
pid_t gettid();
|
||||||
unsigned int msleep(unsigned int);
|
|
||||||
|
/* Sleeps for ms milliseconds. */
|
||||||
|
unsigned int msleep(unsigned int ms);
|
||||||
|
|
||||||
|
/* Prints a message to standard error and aborts the program. */
|
||||||
noreturn void __luna_abort(const char* message);
|
noreturn void __luna_abort(const char* message);
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
|
@ -13,6 +13,8 @@
|
|||||||
#define SYS_open 9
|
#define SYS_open 9
|
||||||
#define SYS_read 10
|
#define SYS_read 10
|
||||||
#define SYS_close 11
|
#define SYS_close 11
|
||||||
|
#define SYS_seek 12
|
||||||
|
#define SYS_exec 13
|
||||||
|
|
||||||
#ifndef __want_syscalls
|
#ifndef __want_syscalls
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
#include <stdarg.h>
|
#include <stdarg.h>
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
|
|
||||||
#define SEEK_SET 0
|
#include <bits/seek.h>
|
||||||
|
|
||||||
typedef struct
|
typedef struct
|
||||||
{
|
{
|
||||||
@ -13,36 +13,109 @@ typedef struct
|
|||||||
int f_err;
|
int f_err;
|
||||||
} FILE;
|
} FILE;
|
||||||
|
|
||||||
extern FILE* __stderr;
|
extern FILE* stderr;
|
||||||
extern FILE* __stdout;
|
extern FILE* stdout;
|
||||||
#define stderr __stderr
|
#define stdout stdout
|
||||||
#define stdout __stdout
|
#define stderr stderr
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
long f_offset;
|
||||||
|
} fpos_t;
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
extern "C"
|
extern "C"
|
||||||
{
|
{
|
||||||
#endif
|
#endif
|
||||||
int fclose(FILE*);
|
|
||||||
int fflush(FILE*);
|
/* Closes the file handle stream. */
|
||||||
FILE* fopen(const char*, const char*);
|
int fclose(FILE* stream);
|
||||||
int fprintf(FILE*, const char*, ...);
|
|
||||||
size_t fread(void*, size_t, size_t, FILE*);
|
/* Does not do anything for now, since buffered IO is not implemented yet. */
|
||||||
int fseek(FILE*, long, int);
|
int fflush(FILE* stream);
|
||||||
long ftell(FILE*);
|
|
||||||
size_t fwrite(const void*, size_t, size_t, FILE*);
|
/* Opens the file specified by pathname. Returns the file handle on success, or NULL on error. */
|
||||||
int ferror(FILE*);
|
FILE* fopen(const char* pathname, const char* mode);
|
||||||
int feof(FILE*);
|
|
||||||
void clearerr(FILE*);
|
/* Returns a new file associated with the file descriptor fd. */
|
||||||
void setbuf(FILE*, char*);
|
FILE* fdopen(int fd, const char* mode);
|
||||||
int vfprintf(FILE*, const char*, va_list);
|
|
||||||
int printf(const char*, ...);
|
/* Writes formatted output according to the string format to the file stream. */
|
||||||
int vprintf(const char*, va_list);
|
int fprintf(FILE* stream, const char* format, ...);
|
||||||
int sprintf(char*, const char*, ...);
|
|
||||||
int snprintf(char*, size_t, const char*, ...);
|
/* Reads nmemb items of size size from the file stream into buf. */
|
||||||
int vsprintf(char*, const char*, va_list);
|
size_t fread(void* buf, size_t size, size_t nmemb, FILE* stream);
|
||||||
int vsnprintf(char*, size_t, const char*, va_list);
|
|
||||||
int puts(const char*);
|
/* Moves stream's read/write offset by offset, depending on whence. */
|
||||||
void perror(const char*);
|
int fseek(FILE* stream, long offset, int whence);
|
||||||
|
|
||||||
|
/* Moves stream's read/write offset to the offset stored in the pos structure. */
|
||||||
|
int fsetpos(FILE* stream, const fpos_t* pos);
|
||||||
|
|
||||||
|
/* Returns the current offset for stream. */
|
||||||
|
long ftell(FILE* stream);
|
||||||
|
|
||||||
|
/* Stores the current offset for stream in the pos structure. */
|
||||||
|
int fgetpos(FILE* stream, fpos_t* pos);
|
||||||
|
|
||||||
|
/* Rewinds stream's offset to start of file. */
|
||||||
|
void rewind(FILE* stream);
|
||||||
|
|
||||||
|
/* Writes nmemb items of size size from buf into the file stream. */
|
||||||
|
size_t fwrite(const void* buf, size_t size, size_t nmemb, FILE* stream);
|
||||||
|
|
||||||
|
/* Returns nonzero if the error flag in stream was set. */
|
||||||
|
int ferror(FILE* stream);
|
||||||
|
|
||||||
|
/* Returns nonzero if the end-of-file flag in stream was set. */
|
||||||
|
int feof(FILE* stream);
|
||||||
|
|
||||||
|
/* Clears the error and end-of-file flags from stream. */
|
||||||
|
void clearerr(FILE* stream);
|
||||||
|
|
||||||
|
void setbuf(FILE*, char*); // Not implemented.
|
||||||
|
|
||||||
|
/* Writes formatted output according to the string format to the file stream. */
|
||||||
|
int vfprintf(FILE* stream, const char* format, va_list ap);
|
||||||
|
|
||||||
|
/* Writes formatted output according to the string format to standard output. */
|
||||||
|
int printf(const char* format, ...);
|
||||||
|
|
||||||
|
/* Writes formatted output according to the string format to standard output. */
|
||||||
|
int vprintf(const char* format, va_list ap);
|
||||||
|
|
||||||
|
/* Writes formatted output according to the string format to the string str. This function is unsafe, use snprintf
|
||||||
|
* instead. */
|
||||||
|
int sprintf(char* str, const char* format, ...);
|
||||||
|
|
||||||
|
/* Writes at most max bytes of formatted output according to the string format to the string str.*/
|
||||||
|
int snprintf(char* str, size_t max, const char* format, ...);
|
||||||
|
|
||||||
|
/* Writes formatted output according to the string format to the string str. This function is unsafe, use vsnprintf
|
||||||
|
* instead. */
|
||||||
|
int vsprintf(char* str, const char* format, va_list ap);
|
||||||
|
|
||||||
|
/* Writes at most max bytes of formatted output according to the string format to the string str. */
|
||||||
|
int vsnprintf(char* str, size_t max, const char* format, va_list ap);
|
||||||
|
|
||||||
|
/* Writes the string str followed by a trailing newline to stdout. */
|
||||||
|
int puts(const char* str);
|
||||||
|
|
||||||
|
/* Writes the string str to the file stream. */
|
||||||
|
int fputs(const char* str, FILE* stream);
|
||||||
|
|
||||||
|
/* Writes the character c to the file stream. */
|
||||||
|
int fputc(int c, FILE* stream);
|
||||||
|
|
||||||
|
/* Writes the character c to the file stream. */
|
||||||
|
int putc(int c, FILE* stream);
|
||||||
|
|
||||||
|
/* Writes the character c to standard output. */
|
||||||
|
int putchar(int c);
|
||||||
|
|
||||||
|
/* Prints a message to standard error consisting of the string str followed by a colon and the string representation
|
||||||
|
* of the last error encountered during a call to a system or library function. */
|
||||||
|
void perror(const char* str);
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
|
@ -9,13 +9,37 @@ extern "C"
|
|||||||
{
|
{
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
/* Aborts the program. */
|
||||||
noreturn void abort();
|
noreturn void abort();
|
||||||
noreturn void exit(int);
|
|
||||||
int atexit(void (*)(void));
|
/* Normally exits the program with the specified status code. */
|
||||||
|
noreturn void exit(int status);
|
||||||
|
|
||||||
|
/* Registers a handler function to be run at normal program termination. */
|
||||||
|
int atexit(void (*handler)(void));
|
||||||
|
|
||||||
|
/* Not implemented.*/
|
||||||
int atoi(const char*);
|
int atoi(const char*);
|
||||||
void free(void*);
|
|
||||||
|
/* Not implemented. */
|
||||||
char* getenv(const char*);
|
char* getenv(const char*);
|
||||||
void* malloc(size_t);
|
|
||||||
|
/* Allocates n bytes of memory and returns a pointer to it. This memory should be freed by calling free() when it is
|
||||||
|
* not in use anymore. */
|
||||||
|
void* malloc(size_t n);
|
||||||
|
|
||||||
|
/* Allocates enough bytes of memory for an array containing nmemb items of size n and returns a pointer to it. This
|
||||||
|
* memory should be freed by calling free() when it is not in use anymore. */
|
||||||
|
void* calloc(size_t nmemb, size_t n);
|
||||||
|
|
||||||
|
/* Resizes memory allocated by malloc() or calloc() to n bytes. Returns a pointer to the new resized region of
|
||||||
|
* memory, which should be used instead of the old one. This memory should be freed by calling free() when it is not
|
||||||
|
* in use anymore. */
|
||||||
|
void* realloc(void* ptr, size_t n);
|
||||||
|
|
||||||
|
/* Frees a pointer to memory allocated by malloc(), calloc() or realloc(). Accessing the contents of ptr afterwards
|
||||||
|
* is undefined behavior. */
|
||||||
|
void free(void* ptr);
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
|
@ -9,21 +9,37 @@ extern "C"
|
|||||||
{
|
{
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
void* memcpy(void*, const void*, size_t);
|
/* Copies n bytes from src to dst. */
|
||||||
void* memset(void*, int, size_t);
|
void* memcpy(void* dest, const void* src, size_t n);
|
||||||
void* memclr(void*, size_t);
|
|
||||||
|
|
||||||
size_t strlen(const char*);
|
/* Sets n bytes of buf to c, cast to a character. */
|
||||||
|
void* memset(void* buf, int c, size_t n);
|
||||||
|
|
||||||
deprecated("strcpy is unsafe and should not be used; use strncpy instead") char* strcpy(char*, const char*);
|
/* Clears n bytes of buf. */
|
||||||
char* strncpy(char*, const char*, size_t);
|
void* memclr(void* buf, size_t n);
|
||||||
|
|
||||||
char* strchr(const char*, int);
|
/* Returns the length of the string str. */
|
||||||
|
size_t strlen(const char* str);
|
||||||
|
|
||||||
deprecated("strcat is unsafe and should not be used; use strncat instead") char* strcat(char*, const char*);
|
/* Copies the string src into dest. This function is unsafe, use strncpy instead. */
|
||||||
char* strncat(char*, const char*, size_t);
|
deprecated("strcpy is unsafe and should not be used; use strncpy instead") char* strcpy(char* dest,
|
||||||
|
const char* src);
|
||||||
|
|
||||||
char* strerror(int);
|
/* Copies at most max bytes from the string src into dest. */
|
||||||
|
char* strncpy(char* dest, const char* src, size_t max);
|
||||||
|
|
||||||
|
/* Returns a pointer to the first occurrence of the character c in str, or NULL if it is not found. */
|
||||||
|
char* strchr(const char* str, int c);
|
||||||
|
|
||||||
|
/* Concatenates the string src into dest. This function is unsafe, use strncat instead. */
|
||||||
|
deprecated("strcat is unsafe and should not be used; use strncat instead") char* strcat(char* dest,
|
||||||
|
const char* src);
|
||||||
|
|
||||||
|
/* Concatenates at most max bytes of the string src into dest. */
|
||||||
|
char* strncat(char* dest, const char* src, size_t max);
|
||||||
|
|
||||||
|
/* Returns the error string associated with the error number err. */
|
||||||
|
char* strerror(int err);
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
|
@ -2,9 +2,9 @@
|
|||||||
#define _SYS_MMAN_H
|
#define _SYS_MMAN_H
|
||||||
|
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
|
#include <sys/types.h> // for off_t
|
||||||
|
|
||||||
typedef unsigned long off_t;
|
/* Address returned by mmap when it fails. */
|
||||||
|
|
||||||
#define MAP_FAILED (void*)-1
|
#define MAP_FAILED (void*)-1
|
||||||
|
|
||||||
#define PROT_READ_WRITE 1
|
#define PROT_READ_WRITE 1
|
||||||
@ -16,8 +16,13 @@ extern "C"
|
|||||||
{
|
{
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
void* mmap(void*, size_t, int, int, int, off_t);
|
/* Maps size bytes of memory (rounded up to the nearest page-aligned size) into the current process's address
|
||||||
int munmap(void* addr, size_t len);
|
* space at addr. If addr is null, the kernel will choose an address. */
|
||||||
|
void* mmap(void* addr, size_t size, int prot, int flags, int fd, off_t offset);
|
||||||
|
|
||||||
|
/* Unmaps size bytes of memory (rounded up to the nearest page-aligned size) at addr from the current process's
|
||||||
|
* address space. */
|
||||||
|
int munmap(void* addr, size_t size);
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,16 @@
|
|||||||
#ifndef _SYS_TYPES_H
|
#ifndef _SYS_TYPES_H
|
||||||
#define _SYS_TYPES_H
|
#define _SYS_TYPES_H
|
||||||
|
|
||||||
|
/* The type of a process identifier. */
|
||||||
typedef long int pid_t;
|
typedef long int pid_t;
|
||||||
|
|
||||||
|
/* The type returned by sizeof(). */
|
||||||
typedef unsigned long int size_t;
|
typedef unsigned long int size_t;
|
||||||
|
|
||||||
|
/* Signed version of size_t. */
|
||||||
typedef long int ssize_t;
|
typedef long int ssize_t;
|
||||||
|
|
||||||
|
/* The type of an offset into a file. */
|
||||||
|
typedef long int off_t;
|
||||||
|
|
||||||
#endif
|
#endif
|
@ -1,6 +1,8 @@
|
|||||||
#ifndef _UNISTD_H
|
#ifndef _UNISTD_H
|
||||||
#define _UNISTD_H
|
#define _UNISTD_H
|
||||||
|
|
||||||
|
#include <bits/macros.h>
|
||||||
|
#include <bits/seek.h>
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
@ -8,16 +10,37 @@ extern "C"
|
|||||||
{
|
{
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
int execv(const char*, char* const[]);
|
/* Executes the program program. On success, does not return. The second parameter (arguments) is not implemented
|
||||||
int execve(const char*, char* const[], char* const[]);
|
* for now. You can pass NULL to it. */
|
||||||
int execvp(const char*, char* const[]);
|
int execv(const char* program, char* const[]);
|
||||||
pid_t fork(void);
|
|
||||||
long syscall(long, ...);
|
|
||||||
unsigned int sleep(unsigned int);
|
|
||||||
|
|
||||||
ssize_t read(int, void*, size_t);
|
/* Not implemented. */
|
||||||
ssize_t write(int, const void*, size_t);
|
int execve(const char*, char* const[], char* const[]);
|
||||||
int close(int);
|
/* Not implemented. */
|
||||||
|
int execvp(const char*, char* const[]);
|
||||||
|
/* Not implemented. */
|
||||||
|
pid_t fork(void);
|
||||||
|
|
||||||
|
/* Terminates the program with the status code status. */
|
||||||
|
noreturn void _exit(int status);
|
||||||
|
|
||||||
|
/* Calls the kernel for a specific service, determined by number. */
|
||||||
|
long syscall(long number, ...);
|
||||||
|
|
||||||
|
/* Suspends execution for a chosen amount of seconds. */
|
||||||
|
unsigned int sleep(unsigned int seconds);
|
||||||
|
|
||||||
|
/* Reads count bytes from the file descriptor fd into the memory at buf. */
|
||||||
|
ssize_t read(int fd, void* buf, size_t count);
|
||||||
|
|
||||||
|
/* Writes count bytes of the memory at buf to the file descriptor fd. */
|
||||||
|
ssize_t write(int fd, const void* buf, size_t count);
|
||||||
|
|
||||||
|
/* Closes the file descriptor fd. */
|
||||||
|
int close(int fd);
|
||||||
|
|
||||||
|
/* Moves the read/write file offset for fd to offset, depending on whence. */
|
||||||
|
off_t lseek(int fd, off_t offset, int whence);
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
|
25
libs/libc/src/atexit.cpp
Normal file
25
libs/libc/src/atexit.cpp
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
#include <stdlib.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#define ATEXIT_MAX_FUNCS 32
|
||||||
|
|
||||||
|
typedef void (*atexit_func_t)(void);
|
||||||
|
|
||||||
|
atexit_func_t atexit_functions[ATEXIT_MAX_FUNCS];
|
||||||
|
int atexit_function_count = 0;
|
||||||
|
|
||||||
|
extern "C"
|
||||||
|
{
|
||||||
|
int atexit(atexit_func_t handler)
|
||||||
|
{
|
||||||
|
if (atexit_function_count >= ATEXIT_MAX_FUNCS) return -1;
|
||||||
|
atexit_functions[atexit_function_count++] = handler;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
noreturn void exit(int status)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < atexit_function_count; i++) { atexit_functions[i](); }
|
||||||
|
_exit(status);
|
||||||
|
}
|
||||||
|
}
|
@ -5,8 +5,8 @@
|
|||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
FILE* __stderr;
|
FILE* stderr;
|
||||||
FILE* __stdout;
|
FILE* stdout;
|
||||||
|
|
||||||
extern "C"
|
extern "C"
|
||||||
{
|
{
|
||||||
@ -40,6 +40,19 @@ extern "C"
|
|||||||
return stream;
|
return stream;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
FILE* fdopen(int fd, const char*)
|
||||||
|
{
|
||||||
|
if (fd < 0) // FIXME: Also check if the mode string is compatible with how fd was opened.
|
||||||
|
{
|
||||||
|
errno = EBADF;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
FILE* stream = (FILE*)malloc(sizeof(FILE));
|
||||||
|
stream->f_fd = fd;
|
||||||
|
clearerr(stream);
|
||||||
|
return stream;
|
||||||
|
}
|
||||||
|
|
||||||
size_t fread(void* buf, size_t size, size_t nmemb, FILE* stream)
|
size_t fread(void* buf, size_t size, size_t nmemb, FILE* stream)
|
||||||
{
|
{
|
||||||
ssize_t status = read(stream->f_fd, buf, size * nmemb);
|
ssize_t status = read(stream->f_fd, buf, size * nmemb);
|
||||||
@ -67,14 +80,37 @@ extern "C"
|
|||||||
stream->f_err = stream->f_eof = 0;
|
stream->f_err = stream->f_eof = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int fseek(FILE*, long, int)
|
int fseek(FILE* stream, long offset, int whence)
|
||||||
{
|
{
|
||||||
NOT_IMPLEMENTED("fseek");
|
long result = lseek(stream->f_fd, offset, whence);
|
||||||
|
if (result < 0) { return -1; }
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
long ftell(FILE*)
|
int fsetpos(FILE* stream, const fpos_t* pos)
|
||||||
{
|
{
|
||||||
NOT_IMPLEMENTED("ftell");
|
return fseek(stream, pos->f_offset, SEEK_SET);
|
||||||
|
}
|
||||||
|
|
||||||
|
long ftell(FILE* stream)
|
||||||
|
{
|
||||||
|
return lseek(stream->f_fd, 0,
|
||||||
|
SEEK_CUR); // FIXME: Store the last seeked position in the file struct to avoid redundant syscalls
|
||||||
|
// maybe? We'd have to update this value in fread() and fwrite() as well...
|
||||||
|
}
|
||||||
|
|
||||||
|
int fgetpos(FILE* stream, fpos_t* pos)
|
||||||
|
{
|
||||||
|
long result = ftell(stream);
|
||||||
|
if (result < 0) { return -1; }
|
||||||
|
pos->f_offset = result;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void rewind(FILE* stream)
|
||||||
|
{
|
||||||
|
lseek(stream->f_fd, 0, SEEK_SET);
|
||||||
|
clearerr(stream);
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t fwrite(const void* buf, size_t size, size_t nmemb, FILE* stream)
|
size_t fwrite(const void* buf, size_t size, size_t nmemb, FILE* stream)
|
||||||
|
@ -2,13 +2,30 @@
|
|||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
static void terminate_libc()
|
||||||
|
{
|
||||||
|
fclose(stdout);
|
||||||
|
fclose(stderr);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void check_for_file(int fd, FILE** target_stream, const char* path, const char* mode)
|
||||||
|
{
|
||||||
|
if (lseek(fd, 0, SEEK_CUR) < 0)
|
||||||
|
{
|
||||||
|
if (errno == EBADF) *target_stream = fopen(path, mode);
|
||||||
|
else
|
||||||
|
exit(errno);
|
||||||
|
if (!*target_stream) exit(errno);
|
||||||
|
errno = 0;
|
||||||
|
}
|
||||||
|
else { *target_stream = fdopen(fd, mode); }
|
||||||
|
}
|
||||||
|
|
||||||
extern "C" void initialize_libc()
|
extern "C" void initialize_libc()
|
||||||
{
|
{
|
||||||
__stderr = fopen("/dev/console", "rw");
|
check_for_file(0, &stdout, "/dev/console", "rw");
|
||||||
if (!stderr) exit(errno);
|
check_for_file(1, &stderr, "/dev/console", "rw");
|
||||||
__stdout = fopen("/dev/console", "rw");
|
atexit(terminate_libc);
|
||||||
if (!stdout) exit(errno);
|
|
||||||
clearerr(__stderr);
|
|
||||||
clearerr(__stdout);
|
|
||||||
}
|
}
|
@ -5,8 +5,6 @@
|
|||||||
#include <sys/syscall.h>
|
#include <sys/syscall.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
#define noreturn __attribute__((noreturn))
|
|
||||||
|
|
||||||
extern "C"
|
extern "C"
|
||||||
{
|
{
|
||||||
pid_t gettid()
|
pid_t gettid()
|
||||||
@ -21,7 +19,7 @@ extern "C"
|
|||||||
|
|
||||||
noreturn void __luna_abort(const char* message)
|
noreturn void __luna_abort(const char* message)
|
||||||
{
|
{
|
||||||
fwrite(message, strlen(message), 1, stdout);
|
fputs(message, stderr);
|
||||||
abort();
|
abort();
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -42,7 +42,7 @@ long int __luna_syscall5(long int sys_num, unsigned long int arg0, unsigned long
|
|||||||
{
|
{
|
||||||
long int result;
|
long int result;
|
||||||
register unsigned long int value0 asm("r10") = arg3;
|
register unsigned long int value0 asm("r10") = arg3;
|
||||||
register unsigned long int value1 asm("r10") = arg4;
|
register unsigned long int value1 asm("r8") = arg4;
|
||||||
asm volatile("int $0x42" : "=a"(result) : "a"(sys_num), "D"(arg0), "S"(arg1), "d"(arg2), "r"(value0), "r"(value1));
|
asm volatile("int $0x42" : "=a"(result) : "a"(sys_num), "D"(arg0), "S"(arg1), "d"(arg2), "r"(value0), "r"(value1));
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
@ -10,10 +10,31 @@ extern "C"
|
|||||||
{
|
{
|
||||||
int puts(const char* s)
|
int puts(const char* s)
|
||||||
{
|
{
|
||||||
long nwritten = fwrite(s, strlen(s), 1, stdout);
|
int nwritten = fputs(s, stdout);
|
||||||
if (nwritten < 0) return -1;
|
if (nwritten < 0) return -1;
|
||||||
nwritten += fwrite("\n", 1, 1, stdout);
|
if (putchar('\n') < 0) return -1;
|
||||||
return (int)nwritten;
|
return nwritten + 1;
|
||||||
|
}
|
||||||
|
int fputs(const char* s, FILE* stream)
|
||||||
|
{
|
||||||
|
int result = (int)fwrite(s, strlen(s), 1, stream);
|
||||||
|
if (ferror(stream)) return -1;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
int fputc(int c, FILE* stream)
|
||||||
|
{
|
||||||
|
char chr = (char)c;
|
||||||
|
fwrite(&chr, 1, 1, stream);
|
||||||
|
if (ferror(stream)) { return -1; }
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
int putc(int c, FILE* stream)
|
||||||
|
{
|
||||||
|
return fputc(c, stream);
|
||||||
|
}
|
||||||
|
int putchar(int c)
|
||||||
|
{
|
||||||
|
return fputc(c, stdout);
|
||||||
}
|
}
|
||||||
void perror(const char* s)
|
void perror(const char* s)
|
||||||
{
|
{
|
||||||
|
@ -3,32 +3,18 @@
|
|||||||
#include <sys/syscall.h>
|
#include <sys/syscall.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
#define noreturn __attribute__((noreturn))
|
|
||||||
|
|
||||||
#define maybe_unused __attribute__((maybe_unused))
|
|
||||||
#define unused __attribute__((unused))
|
|
||||||
|
|
||||||
extern "C"
|
extern "C"
|
||||||
{
|
{
|
||||||
noreturn void abort()
|
noreturn void abort()
|
||||||
{
|
{
|
||||||
exit(-1);
|
_exit(-1);
|
||||||
}
|
}
|
||||||
|
|
||||||
noreturn void exit(int status)
|
|
||||||
{
|
|
||||||
syscall(SYS_exit, status);
|
|
||||||
__builtin_unreachable();
|
|
||||||
}
|
|
||||||
|
|
||||||
int atexit(void (*)(void))
|
|
||||||
{
|
|
||||||
NOT_IMPLEMENTED("atexit");
|
|
||||||
}
|
|
||||||
int atoi(const char*)
|
int atoi(const char*)
|
||||||
{
|
{
|
||||||
NOT_IMPLEMENTED("atoi");
|
NOT_IMPLEMENTED("atoi");
|
||||||
}
|
}
|
||||||
|
|
||||||
char* getenv(const char*)
|
char* getenv(const char*)
|
||||||
{
|
{
|
||||||
NOT_IMPLEMENTED("getenv");
|
NOT_IMPLEMENTED("getenv");
|
||||||
|
@ -10,18 +10,18 @@ extern "C"
|
|||||||
return dest;
|
return dest;
|
||||||
}
|
}
|
||||||
|
|
||||||
void* memset(void* dest, int c, size_t n)
|
void* memset(void* buf, int c, size_t n)
|
||||||
{
|
{
|
||||||
for (size_t i = 0; i < n; ++i) { *((char*)dest + i) = (char)c; }
|
for (size_t i = 0; i < n; ++i) { *((char*)buf + i) = (char)c; }
|
||||||
return dest;
|
return buf;
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t strlen(const char* s)
|
size_t strlen(const char* str)
|
||||||
{
|
{
|
||||||
const char* i = s;
|
const char* i = str;
|
||||||
for (; *i; ++i)
|
for (; *i; ++i)
|
||||||
;
|
;
|
||||||
return (i - s);
|
return (i - str);
|
||||||
}
|
}
|
||||||
|
|
||||||
char* strcpy(char* dest, const char* src)
|
char* strcpy(char* dest, const char* src)
|
||||||
@ -30,10 +30,10 @@ extern "C"
|
|||||||
return dest;
|
return dest;
|
||||||
}
|
}
|
||||||
|
|
||||||
char* strncpy(char* dest, const char* src, size_t n)
|
char* strncpy(char* dest, const char* src, size_t max) // FIXME: Implement strncpy according to the specification.
|
||||||
{
|
{
|
||||||
size_t src_len = strlen(src) + 1; // NULL byte
|
size_t src_len = strlen(src) + 1; // NULL byte
|
||||||
memcpy(dest, src, src_len > n ? n : src_len);
|
memcpy(dest, src, src_len > max ? max : src_len);
|
||||||
return dest;
|
return dest;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -49,40 +49,40 @@ extern "C"
|
|||||||
return dest;
|
return dest;
|
||||||
}
|
}
|
||||||
|
|
||||||
char* strncat(char* dest, const char* src, size_t n)
|
char* strncat(char* dest, const char* src, size_t max)
|
||||||
{
|
{
|
||||||
size_t dest_len = strlen(dest);
|
size_t dest_len = strlen(dest);
|
||||||
size_t i;
|
size_t i;
|
||||||
|
|
||||||
for (i = 0; i < n && *(src + i); i++) *(char*)(dest + dest_len + i) = *(const char*)(src + i);
|
for (i = 0; i < max && *(src + i); i++) *(char*)(dest + dest_len + i) = *(const char*)(src + i);
|
||||||
|
|
||||||
*(char*)(dest + dest_len + i) = '\0';
|
*(char*)(dest + dest_len + i) = '\0';
|
||||||
|
|
||||||
return dest;
|
return dest;
|
||||||
}
|
}
|
||||||
|
|
||||||
char* strchr(const char* str, int chr)
|
char* strchr(const char* str, int c)
|
||||||
{
|
{
|
||||||
while (*str && *str != (char)chr) str++;
|
while (*str && *str != (char)c) str++;
|
||||||
if (*str) return const_cast<char*>(str);
|
if (*str) return const_cast<char*>(str);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
void* memclr(void* start, size_t count)
|
void* memclr(void* buf, size_t n)
|
||||||
{
|
{
|
||||||
// "i" is our counter of how many bytes we've cleared
|
// "i" is our counter of how many bytes we've cleared
|
||||||
size_t i;
|
size_t i;
|
||||||
|
|
||||||
// find out if "m_start" is aligned on a SSE_XMM_SIZE boundary
|
// find out if "m_start" is aligned on a SSE_XMM_SIZE boundary
|
||||||
if ((size_t)start & (15))
|
if ((size_t)buf & (15))
|
||||||
{
|
{
|
||||||
i = 0;
|
i = 0;
|
||||||
|
|
||||||
// we need to clear byte-by-byte until "m_start" is aligned on an SSE_XMM_SIZE boundary
|
// we need to clear byte-by-byte until "m_start" is aligned on an SSE_XMM_SIZE boundary
|
||||||
// ... and lets make sure we don't copy 'too' many bytes (i < m_count)
|
// ... and lets make sure we don't copy 'too' many bytes (i < m_count)
|
||||||
while (((size_t)start + i) & (15) && i < count)
|
while (((size_t)buf + i) & (15) && i < n)
|
||||||
{
|
{
|
||||||
asm("stosb;" ::"D"((size_t)start + i), "a"(0));
|
asm("stosb;" ::"D"((size_t)buf + i), "a"(0));
|
||||||
i++;
|
i++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -93,42 +93,44 @@ extern "C"
|
|||||||
}
|
}
|
||||||
|
|
||||||
// clear 64-byte chunks of memory (4 16-byte operations)
|
// clear 64-byte chunks of memory (4 16-byte operations)
|
||||||
for (; i + 64 <= count; i += 64)
|
for (; i + 64 <= n; i += 64)
|
||||||
{
|
{
|
||||||
asm volatile(" pxor %%xmm0, %%xmm0; " // set XMM0 to 0
|
asm volatile(" pxor %%xmm0, %%xmm0; " // set XMM0 to 0
|
||||||
" movdqa %%xmm0, 0(%0); " // move 16 bytes from XMM0 to %0 + 0
|
" movdqa %%xmm0, 0(%0); " // move 16 bytes from XMM0 to %0 + 0
|
||||||
" movdqa %%xmm0, 16(%0); "
|
" movdqa %%xmm0, 16(%0); "
|
||||||
" movdqa %%xmm0, 32(%0); "
|
" movdqa %%xmm0, 32(%0); "
|
||||||
" movdqa %%xmm0, 48(%0); " ::"r"((size_t)start + i));
|
" movdqa %%xmm0, 48(%0); " ::"r"((size_t)buf + i));
|
||||||
}
|
}
|
||||||
|
|
||||||
// copy the remaining bytes (if any)
|
// copy the remaining bytes (if any)
|
||||||
asm(" rep stosb; " ::"a"((size_t)(0)), "D"(((size_t)start) + i), "c"(count - i));
|
asm(" rep stosb; " ::"a"((size_t)(0)), "D"(((size_t)buf) + i), "c"(n - i));
|
||||||
|
|
||||||
// "i" will contain the total amount of bytes that were actually transfered
|
// "i" will contain the total amount of bytes that were actually transfered
|
||||||
i += count - i;
|
i += n - i;
|
||||||
|
|
||||||
// we return "m_start" + the amount of bytes that were transfered
|
// we return "m_start" + the amount of bytes that were transfered
|
||||||
return (void*)(((size_t)start) + i);
|
return (void*)(((size_t)buf) + i);
|
||||||
}
|
}
|
||||||
|
|
||||||
#pragma GCC push_options
|
#pragma GCC push_options
|
||||||
#pragma GCC diagnostic ignored "-Wwrite-strings"
|
#pragma GCC diagnostic ignored "-Wwrite-strings"
|
||||||
|
|
||||||
char* strerror(int errnum)
|
char* strerror(int err)
|
||||||
{
|
{
|
||||||
switch (errnum)
|
switch (err)
|
||||||
{
|
{
|
||||||
case EPERM: return "Operation not permitted";
|
case EPERM: return "Operation not permitted";
|
||||||
case EINVAL: return "Invalid argument";
|
case EINVAL: return "Invalid argument";
|
||||||
case ENOMEM: return "Out of memory";
|
case ENOMEM: return "Cannot allocate memory";
|
||||||
case ENOSYS: return "Function not implemented";
|
case ENOSYS: return "Function not implemented";
|
||||||
case ENOENT: return "No such file or directory";
|
case ENOENT: return "No such file or directory";
|
||||||
case EBADF: return "Bad file descriptor";
|
case EBADF: return "Bad file descriptor";
|
||||||
case EMFILE: return "Too many open files";
|
case EMFILE: return "Too many open files";
|
||||||
case EISDIR: return "Is a directory";
|
case EISDIR: return "Is a directory";
|
||||||
|
case ENOEXEC: return "Exec format error";
|
||||||
|
case EFAULT: return "Bad address";
|
||||||
case 0: return "Success";
|
case 0: return "Success";
|
||||||
default: return (char*)(unsigned long int)errnum;
|
default: return (char*)(unsigned long int)err;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6,9 +6,9 @@
|
|||||||
|
|
||||||
extern "C"
|
extern "C"
|
||||||
{
|
{
|
||||||
int execv(const char*, char* const[])
|
int execv(const char* program, char* const[])
|
||||||
{
|
{
|
||||||
NOT_IMPLEMENTED("execv");
|
return (int)syscall(SYS_exec, program);
|
||||||
}
|
}
|
||||||
int execve(const char*, char* const[], char* const[])
|
int execve(const char*, char* const[], char* const[])
|
||||||
{
|
{
|
||||||
@ -36,6 +36,7 @@ extern "C"
|
|||||||
case SYS_rand: result = __luna_syscall0(number); break;
|
case SYS_rand: result = __luna_syscall0(number); break;
|
||||||
case SYS_exit:
|
case SYS_exit:
|
||||||
case SYS_close:
|
case SYS_close:
|
||||||
|
case SYS_exec:
|
||||||
case SYS_sleep: result = __luna_syscall1(number, va_arg(ap, arg)); break;
|
case SYS_sleep: result = __luna_syscall1(number, va_arg(ap, arg)); break;
|
||||||
case SYS_munmap:
|
case SYS_munmap:
|
||||||
case SYS_open: {
|
case SYS_open: {
|
||||||
@ -44,6 +45,7 @@ extern "C"
|
|||||||
result = __luna_syscall2(number, arg0, arg1);
|
result = __luna_syscall2(number, arg0, arg1);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case SYS_seek:
|
||||||
case SYS_write:
|
case SYS_write:
|
||||||
case SYS_read:
|
case SYS_read:
|
||||||
case SYS_mmap: {
|
case SYS_mmap: {
|
||||||
@ -88,4 +90,15 @@ extern "C"
|
|||||||
{
|
{
|
||||||
return (int)syscall(SYS_close, fd);
|
return (int)syscall(SYS_close, fd);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
off_t lseek(int fd, off_t offset, int whence)
|
||||||
|
{
|
||||||
|
return syscall(SYS_seek, fd, offset, whence);
|
||||||
|
}
|
||||||
|
|
||||||
|
noreturn void _exit(int status)
|
||||||
|
{
|
||||||
|
syscall(SYS_exit, status);
|
||||||
|
__builtin_unreachable();
|
||||||
|
}
|
||||||
}
|
}
|
0
tools/build-debug.sh
Normal file → Executable file
0
tools/build-debug.sh
Normal file → Executable file
0
tools/gdb.sh
Normal file → Executable file
0
tools/gdb.sh
Normal file → Executable file
Loading…
Reference in New Issue
Block a user