Compare commits
19 Commits
8d5369fafe
...
7e9e4c47ae
Author | SHA1 | Date |
---|---|---|
Bananymous | 7e9e4c47ae | |
Bananymous | 603fc200e6 | |
Bananymous | c11e84b248 | |
Bananymous | be13120554 | |
Bananymous | 9943edad5a | |
Bananymous | f4049be975 | |
Bananymous | 6cf7e01fe9 | |
Bananymous | b51d2f5295 | |
Bananymous | 49d941ad65 | |
Bananymous | a66c3bdae5 | |
Bananymous | 547eabb403 | |
Bananymous | 79851394b3 | |
Bananymous | fcdc922343 | |
Bananymous | 0b11d76576 | |
Bananymous | f7097398ca | |
Bananymous | 85b1252b9e | |
Bananymous | 1cd12b5f16 | |
Bananymous | c84b66d078 | |
Bananymous | 27adb9486b |
|
@ -7,20 +7,62 @@
|
|||
namespace BAN
|
||||
{
|
||||
|
||||
template<integral T>
|
||||
constexpr T swap_endianness(T value)
|
||||
{
|
||||
if constexpr(sizeof(T) == 1)
|
||||
return value;
|
||||
if constexpr(sizeof(T) == 2)
|
||||
return (((value >> 8) & 0xFF) << 0)
|
||||
| (((value >> 0) & 0xFF) << 8);
|
||||
if constexpr(sizeof(T) == 4)
|
||||
return (((value >> 24) & 0xFF) << 0)
|
||||
| (((value >> 16) & 0xFF) << 8)
|
||||
| (((value >> 8) & 0xFF) << 16)
|
||||
| (((value >> 0) & 0xFF) << 24);
|
||||
if constexpr(sizeof(T) == 8)
|
||||
return (((value >> 56) & 0xFF) << 0)
|
||||
| (((value >> 48) & 0xFF) << 8)
|
||||
| (((value >> 40) & 0xFF) << 16)
|
||||
| (((value >> 32) & 0xFF) << 24)
|
||||
| (((value >> 24) & 0xFF) << 32)
|
||||
| (((value >> 16) & 0xFF) << 40)
|
||||
| (((value >> 8) & 0xFF) << 48)
|
||||
| (((value >> 0) & 0xFF) << 56);
|
||||
T result { 0 };
|
||||
for (size_t i = 0; i < sizeof(T); i++)
|
||||
result |= ((value >> (i * 8)) & 0xFF) << ((sizeof(T) - i - 1) * 8);
|
||||
return result;
|
||||
}
|
||||
|
||||
template<integral T>
|
||||
constexpr T host_to_little_endian(T value)
|
||||
{
|
||||
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
|
||||
return value;
|
||||
#else
|
||||
return swap_endianness(value);
|
||||
#endif
|
||||
}
|
||||
|
||||
template<integral T>
|
||||
constexpr T host_to_big_endian(T value)
|
||||
{
|
||||
#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
|
||||
return value;
|
||||
#else
|
||||
return swap_endianness(value);
|
||||
#endif
|
||||
}
|
||||
|
||||
template<integral T>
|
||||
struct LittleEndian
|
||||
{
|
||||
constexpr operator T() const
|
||||
{
|
||||
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
|
||||
return raw;
|
||||
#else
|
||||
T result { 0 };
|
||||
for (size_t i = 0; i < sizeof(T); i++)
|
||||
result = (result << 8) | ((raw >> (sizeof(T) - i - 1) * 8) & 0xFF);
|
||||
return result;
|
||||
#endif
|
||||
return host_to_little_endian(raw);
|
||||
}
|
||||
private:
|
||||
T raw;
|
||||
};
|
||||
|
||||
|
@ -29,14 +71,7 @@ namespace BAN
|
|||
{
|
||||
constexpr operator T() const
|
||||
{
|
||||
#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
|
||||
return raw;
|
||||
#else
|
||||
T result { 0 };
|
||||
for (size_t i = 0; i < sizeof(T); i++)
|
||||
result = (result << 8) | ((raw >> (i * 8)) & 0xFF);
|
||||
return result;
|
||||
#endif
|
||||
return host_to_big_endian(raw);
|
||||
}
|
||||
private:
|
||||
T raw;
|
||||
|
|
|
@ -0,0 +1,293 @@
|
|||
#include <BAN/ScopeGuard.h>
|
||||
#include <kernel/Memory/Heap.h>
|
||||
#include <kernel/LockGuard.h>
|
||||
#include <LibELF/LoadableELF.h>
|
||||
#include <LibELF/Values.h>
|
||||
|
||||
namespace LibELF
|
||||
{
|
||||
|
||||
using namespace Kernel;
|
||||
|
||||
BAN::ErrorOr<BAN::UniqPtr<LoadableELF>> LoadableELF::load_from_inode(PageTable& page_table, BAN::RefPtr<Inode> inode)
|
||||
{
|
||||
auto* elf_ptr = new LoadableELF(page_table, inode);
|
||||
if (elf_ptr == nullptr)
|
||||
return BAN::Error::from_errno(ENOMEM);
|
||||
auto elf = BAN::UniqPtr<LoadableELF>::adopt(elf_ptr);
|
||||
TRY(elf->initialize());
|
||||
return BAN::move(elf);
|
||||
}
|
||||
|
||||
LoadableELF::LoadableELF(PageTable& page_table, BAN::RefPtr<Inode> inode)
|
||||
: m_inode(inode)
|
||||
, m_page_table(page_table)
|
||||
{
|
||||
}
|
||||
|
||||
LoadableELF::~LoadableELF()
|
||||
{
|
||||
for (const auto& program_header : m_program_headers)
|
||||
{
|
||||
switch (program_header.p_type)
|
||||
{
|
||||
case PT_NULL:
|
||||
continue;
|
||||
case PT_LOAD:
|
||||
{
|
||||
vaddr_t start = program_header.p_vaddr & PAGE_ADDR_MASK;
|
||||
size_t pages = range_page_count(program_header.p_vaddr, program_header.p_memsz);
|
||||
for (size_t i = 0; i < pages; i++)
|
||||
{
|
||||
paddr_t paddr = m_page_table.physical_address_of(start + i * PAGE_SIZE);
|
||||
if (paddr != 0)
|
||||
Heap::get().release_page(paddr);
|
||||
}
|
||||
m_page_table.unmap_range(start, pages * PAGE_SIZE);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
ASSERT_NOT_REACHED();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BAN::ErrorOr<void> LoadableELF::initialize()
|
||||
{
|
||||
if ((size_t)m_inode->size() < sizeof(ElfNativeFileHeader))
|
||||
{
|
||||
dprintln("Too small file");
|
||||
return BAN::Error::from_errno(ENOEXEC);
|
||||
}
|
||||
|
||||
size_t nread = TRY(m_inode->read(0, &m_file_header, sizeof(m_file_header)));
|
||||
ASSERT(nread == sizeof(m_file_header));
|
||||
|
||||
if (m_file_header.e_ident[EI_MAG0] != ELFMAG0 ||
|
||||
m_file_header.e_ident[EI_MAG1] != ELFMAG1 ||
|
||||
m_file_header.e_ident[EI_MAG2] != ELFMAG2 ||
|
||||
m_file_header.e_ident[EI_MAG3] != ELFMAG3)
|
||||
{
|
||||
dprintln("Invalid magic in header");
|
||||
return BAN::Error::from_errno(ENOEXEC);
|
||||
}
|
||||
|
||||
if (m_file_header.e_ident[EI_DATA] != ELFDATA2LSB)
|
||||
{
|
||||
dprintln("Only little-endian is supported");
|
||||
return BAN::Error::from_errno(ENOEXEC);
|
||||
}
|
||||
|
||||
if (m_file_header.e_ident[EI_VERSION] != EV_CURRENT)
|
||||
{
|
||||
dprintln("Invalid version");
|
||||
return BAN::Error::from_errno(ENOEXEC);
|
||||
}
|
||||
|
||||
#if ARCH(i386)
|
||||
if (m_file_header.e_ident[EI_CLASS] != ELFCLASS32)
|
||||
#elif ARCH(x86_64)
|
||||
if (m_file_header.e_ident[EI_CLASS] != ELFCLASS64)
|
||||
#endif
|
||||
{
|
||||
dprintln("Not in native format");
|
||||
return BAN::Error::from_errno(EINVAL);
|
||||
}
|
||||
|
||||
if (m_file_header.e_type != ET_EXEC)
|
||||
{
|
||||
dprintln("Only executable files are supported");
|
||||
return BAN::Error::from_errno(EINVAL);
|
||||
}
|
||||
|
||||
if (m_file_header.e_version != EV_CURRENT)
|
||||
{
|
||||
dprintln("Unsupported version");
|
||||
return BAN::Error::from_errno(EINVAL);
|
||||
}
|
||||
|
||||
ASSERT(m_file_header.e_phentsize <= sizeof(ElfNativeProgramHeader));
|
||||
|
||||
TRY(m_program_headers.resize(m_file_header.e_phnum));
|
||||
for (size_t i = 0; i < m_file_header.e_phnum; i++)
|
||||
{
|
||||
TRY(m_inode->read(m_file_header.e_phoff + m_file_header.e_phentsize * i, &m_program_headers[i], m_file_header.e_phentsize));
|
||||
|
||||
const auto& pheader = m_program_headers[i];
|
||||
if (pheader.p_type != PT_NULL && pheader.p_type != PT_LOAD)
|
||||
{
|
||||
dprintln("Unsupported program header type {}", pheader.p_type);
|
||||
return BAN::Error::from_errno(ENOTSUP);
|
||||
}
|
||||
if (pheader.p_memsz < pheader.p_filesz)
|
||||
{
|
||||
dprintln("Invalid program header");
|
||||
return BAN::Error::from_errno(EINVAL);
|
||||
}
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
vaddr_t LoadableELF::entry_point() const
|
||||
{
|
||||
return m_file_header.e_entry;
|
||||
}
|
||||
|
||||
bool LoadableELF::contains(vaddr_t address) const
|
||||
{
|
||||
for (const auto& program_header : m_program_headers)
|
||||
{
|
||||
switch (program_header.p_type)
|
||||
{
|
||||
case PT_NULL:
|
||||
continue;
|
||||
case PT_LOAD:
|
||||
if (program_header.p_vaddr <= address && address < program_header.p_vaddr + program_header.p_memsz)
|
||||
return true;
|
||||
break;
|
||||
default:
|
||||
ASSERT_NOT_REACHED();
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void LoadableELF::reserve_address_space()
|
||||
{
|
||||
for (const auto& program_header : m_program_headers)
|
||||
{
|
||||
switch (program_header.p_type)
|
||||
{
|
||||
case PT_NULL:
|
||||
break;
|
||||
case PT_LOAD:
|
||||
{
|
||||
vaddr_t page_vaddr = program_header.p_vaddr & PAGE_ADDR_MASK;
|
||||
size_t pages = range_page_count(program_header.p_vaddr, program_header.p_memsz);
|
||||
ASSERT(m_page_table.reserve_range(page_vaddr, pages * PAGE_SIZE));
|
||||
break;
|
||||
}
|
||||
default:
|
||||
ASSERT_NOT_REACHED();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BAN::ErrorOr<void> LoadableELF::load_page_to_memory(vaddr_t address)
|
||||
{
|
||||
for (const auto& program_header : m_program_headers)
|
||||
{
|
||||
switch (program_header.p_type)
|
||||
{
|
||||
case PT_NULL:
|
||||
break;
|
||||
case PT_LOAD:
|
||||
{
|
||||
if (!(program_header.p_vaddr <= address && address < program_header.p_vaddr + program_header.p_memsz))
|
||||
continue;
|
||||
|
||||
PageTable::flags_t flags = PageTable::Flags::UserSupervisor | PageTable::Flags::Present;
|
||||
if (program_header.p_flags & LibELF::PF_W)
|
||||
flags |= PageTable::Flags::ReadWrite;
|
||||
if (program_header.p_flags & LibELF::PF_X)
|
||||
flags |= PageTable::Flags::Execute;
|
||||
|
||||
vaddr_t vaddr = address & PAGE_ADDR_MASK;
|
||||
paddr_t paddr = Heap::get().take_free_page();
|
||||
if (paddr == 0)
|
||||
return BAN::Error::from_errno(ENOMEM);
|
||||
|
||||
m_page_table.map_page_at(paddr, vaddr, flags);
|
||||
|
||||
memset((void*)vaddr, 0x00, PAGE_SIZE);
|
||||
|
||||
if (vaddr / PAGE_SIZE < BAN::Math::div_round_up<size_t>(program_header.p_vaddr + program_header.p_filesz, PAGE_SIZE))
|
||||
{
|
||||
size_t vaddr_offset = 0;
|
||||
if (vaddr < program_header.p_vaddr)
|
||||
vaddr_offset = program_header.p_vaddr - vaddr;
|
||||
|
||||
size_t file_offset = 0;
|
||||
if (vaddr > program_header.p_vaddr)
|
||||
file_offset = vaddr - program_header.p_vaddr;
|
||||
|
||||
size_t bytes = BAN::Math::min<size_t>(PAGE_SIZE - vaddr_offset, program_header.p_filesz - file_offset);
|
||||
TRY(m_inode->read(program_header.p_offset + file_offset, (void*)(vaddr + vaddr_offset), bytes));
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
default:
|
||||
ASSERT_NOT_REACHED();
|
||||
}
|
||||
}
|
||||
ASSERT_NOT_REACHED();
|
||||
}
|
||||
|
||||
|
||||
BAN::ErrorOr<BAN::UniqPtr<LoadableELF>> LoadableELF::clone(Kernel::PageTable& new_page_table)
|
||||
{
|
||||
auto* elf_ptr = new LoadableELF(new_page_table, m_inode);
|
||||
if (elf_ptr == nullptr)
|
||||
return BAN::Error::from_errno(ENOMEM);
|
||||
auto elf = BAN::UniqPtr<LoadableELF>::adopt(elf_ptr);
|
||||
|
||||
memcpy(&elf->m_file_header, &m_file_header, sizeof(ElfNativeFileHeader));
|
||||
|
||||
TRY(elf->m_program_headers.resize(m_program_headers.size()));
|
||||
memcpy(elf->m_program_headers.data(), m_program_headers.data(), m_program_headers.size() * sizeof(ElfNativeProgramHeader));
|
||||
|
||||
elf->reserve_address_space();
|
||||
|
||||
ASSERT(&PageTable::current() == &m_page_table);
|
||||
LockGuard _(m_page_table);
|
||||
ASSERT(m_page_table.is_page_free(0));
|
||||
|
||||
for (const auto& program_header : m_program_headers)
|
||||
{
|
||||
switch (program_header.p_type)
|
||||
{
|
||||
case PT_NULL:
|
||||
break;
|
||||
case PT_LOAD:
|
||||
{
|
||||
if (!(program_header.p_flags & LibELF::PF_W))
|
||||
continue;
|
||||
|
||||
PageTable::flags_t flags = PageTable::Flags::UserSupervisor | PageTable::Flags::Present;
|
||||
if (program_header.p_flags & LibELF::PF_W)
|
||||
flags |= PageTable::Flags::ReadWrite;
|
||||
if (program_header.p_flags & LibELF::PF_X)
|
||||
flags |= PageTable::Flags::Execute;
|
||||
|
||||
vaddr_t start = program_header.p_vaddr & PAGE_ADDR_MASK;
|
||||
size_t pages = range_page_count(program_header.p_vaddr, program_header.p_memsz);
|
||||
|
||||
for (size_t i = 0; i < pages; i++)
|
||||
{
|
||||
if (m_page_table.physical_address_of(start + i * PAGE_SIZE) == 0)
|
||||
continue;
|
||||
|
||||
paddr_t paddr = Heap::get().take_free_page();
|
||||
if (paddr == 0)
|
||||
return BAN::Error::from_errno(ENOMEM);
|
||||
|
||||
m_page_table.map_page_at(paddr, 0, PageTable::Flags::ReadWrite | PageTable::Flags::Present);
|
||||
memcpy((void*)0, (void*)(start + i * PAGE_SIZE), PAGE_SIZE);
|
||||
m_page_table.unmap_page(0);
|
||||
|
||||
new_page_table.map_page_at(paddr, start + i * PAGE_SIZE, flags);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
default:
|
||||
ASSERT_NOT_REACHED();
|
||||
}
|
||||
}
|
||||
|
||||
return elf;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,47 @@
|
|||
#pragma once
|
||||
|
||||
#ifndef __is_kernel
|
||||
#error "This is kernel only header"
|
||||
#endif
|
||||
|
||||
#include <BAN/UniqPtr.h>
|
||||
#include <BAN/Vector.h>
|
||||
|
||||
#include <kernel/FS/Inode.h>
|
||||
#include <kernel/Memory/PageTable.h>
|
||||
|
||||
#include <LibELF/Types.h>
|
||||
|
||||
namespace LibELF
|
||||
{
|
||||
|
||||
class LoadableELF
|
||||
{
|
||||
BAN_NON_COPYABLE(LoadableELF);
|
||||
BAN_NON_MOVABLE(LoadableELF);
|
||||
|
||||
public:
|
||||
static BAN::ErrorOr<BAN::UniqPtr<LoadableELF>> load_from_inode(Kernel::PageTable&, BAN::RefPtr<Kernel::Inode>);
|
||||
~LoadableELF();
|
||||
|
||||
Kernel::vaddr_t entry_point() const;
|
||||
|
||||
bool contains(Kernel::vaddr_t address) const;
|
||||
void reserve_address_space();
|
||||
|
||||
BAN::ErrorOr<void> load_page_to_memory(Kernel::vaddr_t address);
|
||||
|
||||
BAN::ErrorOr<BAN::UniqPtr<LoadableELF>> clone(Kernel::PageTable&);
|
||||
|
||||
private:
|
||||
LoadableELF(Kernel::PageTable&, BAN::RefPtr<Kernel::Inode>);
|
||||
BAN::ErrorOr<void> initialize();
|
||||
|
||||
private:
|
||||
BAN::RefPtr<Kernel::Inode> m_inode;
|
||||
Kernel::PageTable& m_page_table;
|
||||
ElfNativeFileHeader m_file_header;
|
||||
BAN::Vector<ElfNativeProgramHeader> m_program_headers;
|
||||
};
|
||||
|
||||
}
|
|
@ -1,5 +1,7 @@
|
|||
#pragma once
|
||||
|
||||
#include <kernel/Arch.h>
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
namespace LibELF
|
||||
|
@ -153,4 +155,35 @@ namespace LibELF
|
|||
Elf64Xword p_align;
|
||||
};
|
||||
|
||||
|
||||
#if ARCH(i386)
|
||||
using ElfNativeAddr = Elf32Addr;
|
||||
using ElfNativeOff = Elf32Off;
|
||||
using ElfNativeHalf = Elf32Half;
|
||||
using ElfNativeWord = Elf32Word;
|
||||
using ElfNativeSword = Elf32Sword;
|
||||
using ElfNativeXword = Elf32Xword;
|
||||
using ElfNativeSxword = Elf32Sxword;
|
||||
using ElfNativeFileHeader = Elf32FileHeader;
|
||||
using ElfNativeSectionHeader = Elf32SectionHeader;
|
||||
using ElfNativeSymbol = Elf32Symbol;
|
||||
using ElfNativeRelocation = Elf32Relocation;
|
||||
using ElfNativeRelocationA = Elf32RelocationA;
|
||||
using ElfNativeProgramHeader = Elf32ProgramHeader;
|
||||
#elif ARCH(x86_64)
|
||||
using ElfNativeAddr = Elf64Addr;
|
||||
using ElfNativeOff = Elf64Off;
|
||||
using ElfNativeHalf = Elf64Half;
|
||||
using ElfNativeWord = Elf64Word;
|
||||
using ElfNativeSword = Elf64Sword;
|
||||
using ElfNativeXword = Elf64Xword;
|
||||
using ElfNativeSxword = Elf64Sxword;
|
||||
using ElfNativeFileHeader = Elf64FileHeader;
|
||||
using ElfNativeSectionHeader = Elf64SectionHeader;
|
||||
using ElfNativeSymbol = Elf64Symbol;
|
||||
using ElfNativeRelocation = Elf64Relocation;
|
||||
using ElfNativeRelocationA = Elf64RelocationA;
|
||||
using ElfNativeProgramHeader = Elf64ProgramHeader;
|
||||
#endif
|
||||
|
||||
}
|
|
@ -118,7 +118,7 @@ set(LIBC_SOURCES
|
|||
)
|
||||
|
||||
set(LIBELF_SOURCES
|
||||
../LibELF/LibELF/ELF.cpp
|
||||
../LibELF/LibELF/LoadableELF.cpp
|
||||
)
|
||||
|
||||
set(KERNEL_SOURCES
|
||||
|
|
|
@ -101,6 +101,29 @@ namespace IDT
|
|||
UnkownException0x1F,
|
||||
};
|
||||
|
||||
struct PageFaultError
|
||||
{
|
||||
union
|
||||
{
|
||||
uint32_t raw;
|
||||
struct
|
||||
{
|
||||
uint32_t present : 1;
|
||||
uint32_t write : 1;
|
||||
uint32_t userspace : 1;
|
||||
uint32_t reserved_write : 1;
|
||||
uint32_t instruction : 1;
|
||||
uint32_t protection_key : 1;
|
||||
uint32_t shadow_stack : 1;
|
||||
uint32_t reserved1 : 8;
|
||||
uint32_t sgx_violation : 1;
|
||||
uint32_t reserved2 : 16;
|
||||
};
|
||||
};
|
||||
|
||||
};
|
||||
static_assert(sizeof(PageFaultError) == 4);
|
||||
|
||||
static const char* isr_exceptions[] =
|
||||
{
|
||||
"Division Error",
|
||||
|
@ -148,6 +171,29 @@ namespace IDT
|
|||
pid_t tid = Kernel::Scheduler::current_tid();
|
||||
pid_t pid = tid ? Kernel::Process::current().pid() : 0;
|
||||
|
||||
if (pid && isr == ISR::PageFault)
|
||||
{
|
||||
PageFaultError page_fault_error;
|
||||
page_fault_error.raw = error;
|
||||
|
||||
// Try demand paging on non present pages
|
||||
if (!page_fault_error.present)
|
||||
{
|
||||
asm volatile("sti");
|
||||
auto result = Kernel::Process::current().allocate_page_for_demand_paging(regs->cr2);
|
||||
asm volatile("cli");
|
||||
|
||||
if (!result.is_error() && result.value())
|
||||
return;
|
||||
|
||||
if (result.is_error())
|
||||
{
|
||||
dwarnln("Demand paging: {}", result.error());
|
||||
Kernel::Thread::current().handle_signal(SIGTERM);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (tid)
|
||||
{
|
||||
auto start = Kernel::Thread::current().stack_base();
|
||||
|
@ -424,4 +470,14 @@ namespace IDT
|
|||
flush_idt();
|
||||
}
|
||||
|
||||
[[noreturn]] void force_triple_fault()
|
||||
{
|
||||
// load 0 sized IDT and trigger an interrupt to force triple fault
|
||||
asm volatile("cli");
|
||||
s_idtr.size = 0;
|
||||
flush_idt();
|
||||
asm volatile("int $0x00");
|
||||
ASSERT_NOT_REACHED();
|
||||
}
|
||||
|
||||
}
|
|
@ -9,5 +9,6 @@ namespace IDT
|
|||
|
||||
void initialize();
|
||||
void register_irq_handler(uint8_t irq, void(*f)());
|
||||
[[noreturn]] void force_triple_fault();
|
||||
|
||||
}
|
|
@ -16,6 +16,11 @@ public:
|
|||
|
||||
static void initialize(bool force_pic);
|
||||
static InterruptController& get();
|
||||
|
||||
void enter_acpi_mode();
|
||||
|
||||
private:
|
||||
bool m_using_apic { false };
|
||||
};
|
||||
|
||||
bool interrupts_enabled();
|
|
@ -75,7 +75,7 @@ namespace Kernel
|
|||
{
|
||||
size_t first_page = start / PAGE_SIZE;
|
||||
size_t last_page = BAN::Math::div_round_up<size_t>(start + bytes, PAGE_SIZE);
|
||||
return last_page - first_page + 1;
|
||||
return last_page - first_page;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -15,9 +15,9 @@ namespace Kernel
|
|||
|
||||
public:
|
||||
// Create virtual range to fixed virtual address
|
||||
static BAN::ErrorOr<BAN::UniqPtr<VirtualRange>> create_to_vaddr(PageTable&, vaddr_t, size_t, PageTable::flags_t flags);
|
||||
static BAN::ErrorOr<BAN::UniqPtr<VirtualRange>> create_to_vaddr(PageTable&, vaddr_t, size_t, PageTable::flags_t flags, bool preallocate_pages);
|
||||
// Create virtual range to virtual address range
|
||||
static BAN::ErrorOr<BAN::UniqPtr<VirtualRange>> create_to_vaddr_range(PageTable&, vaddr_t vaddr_start, vaddr_t vaddr_end, size_t, PageTable::flags_t flags);
|
||||
static BAN::ErrorOr<BAN::UniqPtr<VirtualRange>> create_to_vaddr_range(PageTable&, vaddr_t vaddr_start, vaddr_t vaddr_end, size_t, PageTable::flags_t flags, bool preallocate_pages);
|
||||
// Create virtual range in kernel memory with kmalloc
|
||||
static BAN::ErrorOr<BAN::UniqPtr<VirtualRange>> create_kmalloc(size_t);
|
||||
~VirtualRange();
|
||||
|
@ -28,15 +28,21 @@ namespace Kernel
|
|||
size_t size() const { return m_size; }
|
||||
PageTable::flags_t flags() const { return m_flags; }
|
||||
|
||||
void set_zero();
|
||||
bool contains(vaddr_t address) const { return vaddr() <= address && address < vaddr() + size(); }
|
||||
|
||||
BAN::ErrorOr<void> allocate_page_for_demand_paging(vaddr_t address);
|
||||
|
||||
void copy_from(size_t offset, const uint8_t* buffer, size_t bytes);
|
||||
|
||||
private:
|
||||
VirtualRange(PageTable&);
|
||||
VirtualRange(PageTable&, bool preallocated, bool kmalloc);
|
||||
|
||||
void set_zero();
|
||||
|
||||
private:
|
||||
PageTable& m_page_table;
|
||||
bool m_kmalloc { false };
|
||||
const bool m_preallocated;
|
||||
const bool m_kmalloc;
|
||||
vaddr_t m_vaddr { 0 };
|
||||
size_t m_size { 0 };
|
||||
PageTable::flags_t m_flags { 0 };
|
||||
|
|
|
@ -19,6 +19,7 @@ namespace Kernel::PCI
|
|||
class BarRegion
|
||||
{
|
||||
BAN_NON_COPYABLE(BarRegion);
|
||||
BAN_NON_MOVABLE(BarRegion);
|
||||
|
||||
public:
|
||||
static BAN::ErrorOr<BAN::UniqPtr<BarRegion>> create(PCI::Device&, uint8_t bar_num);
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
#include <sys/mman.h>
|
||||
#include <termios.h>
|
||||
|
||||
namespace LibELF { class ELF; }
|
||||
namespace LibELF { class LoadableELF; }
|
||||
|
||||
namespace Kernel
|
||||
{
|
||||
|
@ -111,6 +111,8 @@ namespace Kernel
|
|||
|
||||
BAN::ErrorOr<long> sys_sync(bool should_block);
|
||||
|
||||
BAN::ErrorOr<long> sys_poweroff(int command);
|
||||
|
||||
BAN::ErrorOr<void> mount(BAN::StringView source, BAN::StringView target);
|
||||
|
||||
BAN::ErrorOr<long> sys_read_dir_entries(int fd, DirectoryEntryList* buffer, size_t buffer_size);
|
||||
|
@ -139,15 +141,17 @@ namespace Kernel
|
|||
bool is_userspace() const { return m_is_userspace; }
|
||||
const userspace_info_t& userspace_info() const { return m_userspace_info; }
|
||||
|
||||
// Returns error if page could not be allocated
|
||||
// Returns true if the page was allocated successfully
|
||||
// Return false if access was page violation (segfault)
|
||||
BAN::ErrorOr<bool> allocate_page_for_demand_paging(vaddr_t addr);
|
||||
|
||||
private:
|
||||
Process(const Credentials&, pid_t pid, pid_t parent, pid_t sid, pid_t pgrp);
|
||||
static Process* create_process(const Credentials&, pid_t parent, pid_t sid = 0, pid_t pgrp = 0);
|
||||
|
||||
// Load an elf file to virtual address space of the current page table
|
||||
static BAN::ErrorOr<BAN::UniqPtr<LibELF::ELF>> load_elf_for_exec(const Credentials&, BAN::StringView file_path, const BAN::String& cwd);
|
||||
|
||||
// Copy an elf file from the current page table to the processes own
|
||||
void load_elf_to_memory(LibELF::ELF&);
|
||||
// Load elf from a file
|
||||
static BAN::ErrorOr<BAN::UniqPtr<LibELF::LoadableELF>> load_elf_for_exec(const Credentials&, BAN::StringView file_path, const BAN::String& cwd, Kernel::PageTable&);
|
||||
|
||||
int block_until_exit();
|
||||
|
||||
|
@ -165,17 +169,12 @@ namespace Kernel
|
|||
int waiting { 0 };
|
||||
};
|
||||
|
||||
struct MappedRange
|
||||
{
|
||||
bool can_be_unmapped;
|
||||
BAN::UniqPtr<VirtualRange> range;
|
||||
};
|
||||
|
||||
Credentials m_credentials;
|
||||
|
||||
OpenFileDescriptorSet m_open_file_descriptors;
|
||||
|
||||
BAN::Vector<MappedRange> m_mapped_ranges;
|
||||
BAN::UniqPtr<LibELF::LoadableELF> m_loadable_elf;
|
||||
BAN::Vector<BAN::UniqPtr<VirtualRange>> m_mapped_ranges;
|
||||
|
||||
pid_t m_sid;
|
||||
pid_t m_pgrp;
|
||||
|
|
|
@ -40,7 +40,7 @@ namespace Kernel
|
|||
void putchar(uint8_t ch);
|
||||
virtual void putchar_impl(uint8_t ch) = 0;
|
||||
|
||||
bool has_data() const;
|
||||
virtual bool has_data_impl() const override;
|
||||
|
||||
protected:
|
||||
TTY(mode_t mode, uid_t uid, gid_t gid)
|
||||
|
|
|
@ -70,6 +70,7 @@ namespace Kernel
|
|||
|
||||
vaddr_t stack_base() const { return m_stack->vaddr(); }
|
||||
size_t stack_size() const { return m_stack->size(); }
|
||||
VirtualRange& stack() { return *m_stack; }
|
||||
|
||||
vaddr_t interrupt_stack_base() const { return m_interrupt_stack ? m_interrupt_stack->vaddr() : 0; }
|
||||
size_t interrupt_stack_size() const { return m_interrupt_stack ? m_interrupt_stack->size() : 0; }
|
||||
|
@ -94,7 +95,7 @@ namespace Kernel
|
|||
void validate_stack() const;
|
||||
|
||||
private:
|
||||
static constexpr size_t m_kernel_stack_size = PAGE_SIZE * 1;
|
||||
static constexpr size_t m_kernel_stack_size = PAGE_SIZE * 4;
|
||||
static constexpr size_t m_userspace_stack_size = PAGE_SIZE * 2;
|
||||
static constexpr size_t m_interrupt_stack_size = PAGE_SIZE * 2;
|
||||
BAN::UniqPtr<VirtualRange> m_interrupt_stack;
|
||||
|
|
|
@ -3,6 +3,8 @@
|
|||
#include <kernel/APIC.h>
|
||||
#include <kernel/PIC.h>
|
||||
|
||||
#include <lai/helpers/sci.h>
|
||||
|
||||
static InterruptController* s_instance = nullptr;
|
||||
|
||||
InterruptController& InterruptController::get()
|
||||
|
@ -19,11 +21,26 @@ void InterruptController::initialize(bool force_pic)
|
|||
PIC::remap();
|
||||
|
||||
if (!force_pic)
|
||||
{
|
||||
s_instance = APIC::create();
|
||||
if (s_instance)
|
||||
return;
|
||||
if (s_instance)
|
||||
{
|
||||
s_instance->m_using_apic = true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
dprintln("Using PIC instead of APIC");
|
||||
s_instance = PIC::create();
|
||||
ASSERT(s_instance);
|
||||
|
||||
s_instance->m_using_apic = false;
|
||||
}
|
||||
|
||||
void InterruptController::enter_acpi_mode()
|
||||
{
|
||||
if (lai_enable_acpi(m_using_apic ? 1 : 0) != 0)
|
||||
dwarnln("could not enter acpi mode");
|
||||
}
|
||||
|
||||
bool interrupts_enabled()
|
||||
|
@ -31,4 +48,4 @@ bool interrupts_enabled()
|
|||
uintptr_t flags;
|
||||
asm volatile("pushf; pop %0" : "=r"(flags) :: "memory");
|
||||
return flags & (1 << 9);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,26 +5,27 @@
|
|||
namespace Kernel
|
||||
{
|
||||
|
||||
BAN::ErrorOr<BAN::UniqPtr<VirtualRange>> VirtualRange::create_to_vaddr(PageTable& page_table, vaddr_t vaddr, size_t size, PageTable::flags_t flags)
|
||||
BAN::ErrorOr<BAN::UniqPtr<VirtualRange>> VirtualRange::create_to_vaddr(PageTable& page_table, vaddr_t vaddr, size_t size, PageTable::flags_t flags, bool preallocate_pages)
|
||||
{
|
||||
ASSERT(size % PAGE_SIZE == 0);
|
||||
ASSERT(vaddr % PAGE_SIZE == 0);
|
||||
ASSERT(vaddr > 0);
|
||||
|
||||
VirtualRange* result_ptr = new VirtualRange(page_table);
|
||||
VirtualRange* result_ptr = new VirtualRange(page_table, preallocate_pages, false);
|
||||
if (result_ptr == nullptr)
|
||||
return BAN::Error::from_errno(ENOMEM);
|
||||
auto result = BAN::UniqPtr<VirtualRange>::adopt(result_ptr);
|
||||
|
||||
result->m_kmalloc = false;
|
||||
auto result = BAN::UniqPtr<VirtualRange>::adopt(result_ptr);
|
||||
result->m_vaddr = vaddr;
|
||||
result->m_size = size;
|
||||
result->m_flags = flags;
|
||||
|
||||
ASSERT(page_table.reserve_range(vaddr, size));
|
||||
|
||||
size_t needed_pages = size / PAGE_SIZE;
|
||||
if (!preallocate_pages)
|
||||
return result;
|
||||
|
||||
size_t needed_pages = size / PAGE_SIZE;
|
||||
for (size_t i = 0; i < needed_pages; i++)
|
||||
{
|
||||
paddr_t paddr = Heap::get().take_free_page();
|
||||
|
@ -39,10 +40,12 @@ namespace Kernel
|
|||
page_table.map_page_at(paddr, vaddr + i * PAGE_SIZE, flags);
|
||||
}
|
||||
|
||||
result->set_zero();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
BAN::ErrorOr<BAN::UniqPtr<VirtualRange>> VirtualRange::create_to_vaddr_range(PageTable& page_table, vaddr_t vaddr_start, vaddr_t vaddr_end, size_t size, PageTable::flags_t flags)
|
||||
BAN::ErrorOr<BAN::UniqPtr<VirtualRange>> VirtualRange::create_to_vaddr_range(PageTable& page_table, vaddr_t vaddr_start, vaddr_t vaddr_end, size_t size, PageTable::flags_t flags, bool preallocate_pages)
|
||||
{
|
||||
ASSERT(size % PAGE_SIZE == 0);
|
||||
ASSERT(vaddr_start > 0);
|
||||
|
@ -56,47 +59,24 @@ namespace Kernel
|
|||
ASSERT(vaddr_start < vaddr_end);
|
||||
ASSERT(vaddr_end - vaddr_start + 1 >= size / PAGE_SIZE);
|
||||
|
||||
VirtualRange* result_ptr = new VirtualRange(page_table);
|
||||
if (result_ptr == nullptr)
|
||||
return BAN::Error::from_errno(ENOMEM);
|
||||
auto result = BAN::UniqPtr<VirtualRange>::adopt(result_ptr);
|
||||
|
||||
result->m_kmalloc = false;
|
||||
result->m_vaddr = 0;
|
||||
result->m_size = size;
|
||||
result->m_flags = flags;
|
||||
|
||||
vaddr_t vaddr = page_table.reserve_free_contiguous_pages(size / PAGE_SIZE, vaddr_start, vaddr_end);
|
||||
if (vaddr == 0)
|
||||
return BAN::Error::from_errno(ENOMEM);
|
||||
ASSERT(vaddr + size <= vaddr_end);
|
||||
result->m_vaddr = vaddr;
|
||||
|
||||
size_t needed_pages = size / PAGE_SIZE;
|
||||
|
||||
for (size_t i = 0; i < needed_pages; i++)
|
||||
{
|
||||
paddr_t paddr = Heap::get().take_free_page();
|
||||
if (paddr == 0)
|
||||
{
|
||||
for (size_t j = 0; j < i; j++)
|
||||
Heap::get().release_page(page_table.physical_address_of(vaddr + j * PAGE_SIZE));
|
||||
page_table.unmap_range(vaddr, size);
|
||||
result->m_vaddr = 0;
|
||||
return BAN::Error::from_errno(ENOMEM);
|
||||
}
|
||||
page_table.map_page_at(paddr, vaddr + i * PAGE_SIZE, flags);
|
||||
dprintln("no free {} byte area", size);
|
||||
return BAN::Error::from_errno(ENOMEM);
|
||||
}
|
||||
ASSERT(vaddr + size <= vaddr_end);
|
||||
|
||||
return result;
|
||||
LockGuard _(page_table);
|
||||
page_table.unmap_range(vaddr, size); // We have to unmap here to allow reservation in create_to_vaddr()
|
||||
return create_to_vaddr(page_table, vaddr, size, flags, preallocate_pages);
|
||||
}
|
||||
|
||||
BAN::ErrorOr<BAN::UniqPtr<VirtualRange>> VirtualRange::create_kmalloc(size_t size)
|
||||
{
|
||||
VirtualRange* result = new VirtualRange(PageTable::kernel());
|
||||
VirtualRange* result = new VirtualRange(PageTable::kernel(), false, true);
|
||||
ASSERT(result);
|
||||
|
||||
result->m_kmalloc = true;
|
||||
result->m_size = size;
|
||||
result->m_flags = PageTable::Flags::ReadWrite | PageTable::Flags::Present;
|
||||
result->m_vaddr = (vaddr_t)kmalloc(size);
|
||||
|
@ -106,11 +86,15 @@ namespace Kernel
|
|||
return BAN::Error::from_errno(ENOMEM);
|
||||
}
|
||||
|
||||
result->set_zero();
|
||||
|
||||
return BAN::UniqPtr<VirtualRange>::adopt(result);
|
||||
}
|
||||
|
||||
VirtualRange::VirtualRange(PageTable& page_table)
|
||||
VirtualRange::VirtualRange(PageTable& page_table, bool preallocated, bool kmalloc)
|
||||
: m_page_table(page_table)
|
||||
, m_preallocated(preallocated)
|
||||
, m_kmalloc(kmalloc)
|
||||
{ }
|
||||
|
||||
VirtualRange::~VirtualRange()
|
||||
|
@ -123,7 +107,11 @@ namespace Kernel
|
|||
else
|
||||
{
|
||||
for (size_t offset = 0; offset < size(); offset += PAGE_SIZE)
|
||||
Heap::get().release_page(m_page_table.physical_address_of(vaddr() + offset));
|
||||
{
|
||||
paddr_t paddr = m_page_table.physical_address_of(vaddr() + offset);
|
||||
if (paddr)
|
||||
Heap::get().release_page(paddr);
|
||||
}
|
||||
m_page_table.unmap_range(vaddr(), size());
|
||||
}
|
||||
}
|
||||
|
@ -131,13 +119,21 @@ namespace Kernel
|
|||
BAN::ErrorOr<BAN::UniqPtr<VirtualRange>> VirtualRange::clone(PageTable& page_table)
|
||||
{
|
||||
ASSERT(&PageTable::current() == &m_page_table);
|
||||
ASSERT(&m_page_table != &page_table);
|
||||
|
||||
auto result = TRY(create_to_vaddr(page_table, vaddr(), size(), flags()));
|
||||
auto result = TRY(create_to_vaddr(page_table, vaddr(), size(), flags(), m_preallocated));
|
||||
|
||||
LockGuard _(m_page_table);
|
||||
ASSERT(m_page_table.is_page_free(0));
|
||||
for (size_t offset = 0; offset < size(); offset += PAGE_SIZE)
|
||||
{
|
||||
if (!m_preallocated && m_page_table.physical_address_of(vaddr() + offset))
|
||||
{
|
||||
paddr_t paddr = Heap::get().take_free_page();
|
||||
if (paddr == 0)
|
||||
return BAN::Error::from_errno(ENOMEM);
|
||||
result->m_page_table.map_page_at(paddr, vaddr() + offset, m_flags);
|
||||
}
|
||||
m_page_table.map_page_at(result->m_page_table.physical_address_of(vaddr() + offset), 0, PageTable::Flags::ReadWrite | PageTable::Flags::Present);
|
||||
memcpy((void*)0, (void*)(vaddr() + offset), PAGE_SIZE);
|
||||
}
|
||||
|
@ -146,11 +142,31 @@ namespace Kernel
|
|||
return result;
|
||||
}
|
||||
|
||||
BAN::ErrorOr<void> VirtualRange::allocate_page_for_demand_paging(vaddr_t address)
|
||||
{
|
||||
ASSERT(!m_kmalloc);
|
||||
ASSERT(!m_preallocated);
|
||||
ASSERT(contains(address));
|
||||
ASSERT(&PageTable::current() == &m_page_table);
|
||||
|
||||
vaddr_t vaddr = address & PAGE_ADDR_MASK;
|
||||
ASSERT(m_page_table.physical_address_of(vaddr) == 0);
|
||||
|
||||
paddr_t paddr = Heap::get().take_free_page();
|
||||
if (paddr == 0)
|
||||
return BAN::Error::from_errno(ENOMEM);
|
||||
|
||||
m_page_table.map_page_at(paddr, vaddr, m_flags);
|
||||
memset((void*)vaddr, 0x00, PAGE_SIZE);
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
void VirtualRange::set_zero()
|
||||
{
|
||||
PageTable& page_table = PageTable::current();
|
||||
|
||||
if (&page_table == &m_page_table)
|
||||
if (m_kmalloc || &page_table == &m_page_table)
|
||||
{
|
||||
memset((void*)vaddr(), 0, size());
|
||||
return;
|
||||
|
@ -178,7 +194,7 @@ namespace Kernel
|
|||
|
||||
PageTable& page_table = PageTable::current();
|
||||
|
||||
if (&page_table == &m_page_table)
|
||||
if (m_kmalloc || &page_table == &m_page_table)
|
||||
{
|
||||
memcpy((void*)(vaddr() + offset), buffer, bytes);
|
||||
return;
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
#include <kernel/CriticalScope.h>
|
||||
#include <kernel/FS/DevFS/FileSystem.h>
|
||||
#include <kernel/FS/VirtualFileSystem.h>
|
||||
#include <kernel/IDT.h>
|
||||
#include <kernel/InterruptController.h>
|
||||
#include <kernel/LockGuard.h>
|
||||
#include <kernel/Memory/Heap.h>
|
||||
|
@ -11,11 +12,14 @@
|
|||
#include <kernel/Scheduler.h>
|
||||
#include <kernel/Storage/StorageDevice.h>
|
||||
#include <kernel/Timer/Timer.h>
|
||||
#include <LibELF/ELF.h>
|
||||
#include <LibELF/Values.h>
|
||||
|
||||
#include <LibELF/LoadableELF.h>
|
||||
|
||||
#include <lai/helpers/pm.h>
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <stdio.h>
|
||||
#include <sys/banan-os.h>
|
||||
#include <sys/sysmacros.h>
|
||||
#include <sys/wait.h>
|
||||
|
||||
|
@ -106,19 +110,15 @@ namespace Kernel
|
|||
|
||||
BAN::ErrorOr<Process*> Process::create_userspace(const Credentials& credentials, BAN::StringView path)
|
||||
{
|
||||
auto elf = TRY(load_elf_for_exec(credentials, path, "/"sv));
|
||||
|
||||
auto* process = create_process(credentials, 0);
|
||||
MUST(process->m_working_directory.push_back('/'));
|
||||
process->m_page_table = BAN::UniqPtr<PageTable>::adopt(MUST(PageTable::create_userspace()));;
|
||||
process->m_page_table = BAN::UniqPtr<PageTable>::adopt(MUST(PageTable::create_userspace()));
|
||||
|
||||
process->load_elf_to_memory(*elf);
|
||||
process->m_loadable_elf = TRY(load_elf_for_exec(credentials, path, "/"sv, process->page_table()));
|
||||
process->m_loadable_elf->reserve_address_space();
|
||||
|
||||
process->m_is_userspace = true;
|
||||
process->m_userspace_info.entry = elf->file_header_native().e_entry;
|
||||
|
||||
// NOTE: we clear the elf since we don't need the memory anymore
|
||||
elf.clear();
|
||||
process->m_userspace_info.entry = process->m_loadable_elf->entry_point();
|
||||
|
||||
char** argv = nullptr;
|
||||
{
|
||||
|
@ -132,9 +132,9 @@ namespace Kernel
|
|||
process->page_table(),
|
||||
0x400000, KERNEL_OFFSET,
|
||||
needed_bytes,
|
||||
PageTable::Flags::UserSupervisor | PageTable::Flags::ReadWrite | PageTable::Flags::Present
|
||||
PageTable::Flags::UserSupervisor | PageTable::Flags::ReadWrite | PageTable::Flags::Present,
|
||||
true
|
||||
));
|
||||
argv_range->set_zero();
|
||||
|
||||
uintptr_t temp = argv_range->vaddr() + sizeof(char*) * 2;
|
||||
argv_range->copy_from(0, (uint8_t*)&temp, sizeof(char*));
|
||||
|
@ -144,7 +144,7 @@ namespace Kernel
|
|||
|
||||
argv_range->copy_from(sizeof(char*) * 2, (const uint8_t*)path.data(), path.size());
|
||||
|
||||
MUST(process->m_mapped_ranges.emplace_back(false, BAN::move(argv_range)));
|
||||
MUST(process->m_mapped_ranges.push_back(BAN::move(argv_range)));
|
||||
}
|
||||
|
||||
process->m_userspace_info.argc = 1;
|
||||
|
@ -286,7 +286,7 @@ namespace Kernel
|
|||
return 0;
|
||||
}
|
||||
|
||||
BAN::ErrorOr<BAN::UniqPtr<LibELF::ELF>> Process::load_elf_for_exec(const Credentials& credentials, BAN::StringView file_path, const BAN::String& cwd)
|
||||
BAN::ErrorOr<BAN::UniqPtr<LibELF::LoadableELF>> Process::load_elf_for_exec(const Credentials& credentials, BAN::StringView file_path, const BAN::String& cwd, PageTable& page_table)
|
||||
{
|
||||
if (file_path.empty())
|
||||
return BAN::Error::from_errno(ENOENT);
|
||||
|
@ -303,29 +303,7 @@ namespace Kernel
|
|||
}
|
||||
|
||||
auto file = TRY(VirtualFileSystem::get().file_from_absolute_path(credentials, absolute_path, O_EXEC));
|
||||
|
||||
auto elf_or_error = LibELF::ELF::load_from_file(file.inode);
|
||||
if (elf_or_error.is_error())
|
||||
{
|
||||
if (elf_or_error.error().get_error_code() == EINVAL)
|
||||
return BAN::Error::from_errno(ENOEXEC);
|
||||
return elf_or_error.error();
|
||||
}
|
||||
|
||||
auto elf = elf_or_error.release_value();
|
||||
if (!elf->is_native())
|
||||
{
|
||||
derrorln("ELF has invalid architecture");
|
||||
return BAN::Error::from_errno(EINVAL);
|
||||
}
|
||||
|
||||
if (elf->file_header_native().e_type != LibELF::ET_EXEC)
|
||||
{
|
||||
derrorln("Not an executable");
|
||||
return BAN::Error::from_errno(ENOEXEC);
|
||||
}
|
||||
|
||||
return BAN::move(elf);
|
||||
return TRY(LibELF::LoadableELF::load_from_inode(page_table, file.inode));
|
||||
}
|
||||
|
||||
BAN::ErrorOr<long> Process::sys_fork(uintptr_t rsp, uintptr_t rip)
|
||||
|
@ -340,10 +318,12 @@ namespace Kernel
|
|||
OpenFileDescriptorSet open_file_descriptors(m_credentials);
|
||||
TRY(open_file_descriptors.clone_from(m_open_file_descriptors));
|
||||
|
||||
BAN::Vector<MappedRange> mapped_ranges;
|
||||
BAN::Vector<BAN::UniqPtr<VirtualRange>> mapped_ranges;
|
||||
TRY(mapped_ranges.reserve(m_mapped_ranges.size()));
|
||||
for (auto& mapped_range : m_mapped_ranges)
|
||||
MUST(mapped_ranges.emplace_back(mapped_range.can_be_unmapped, TRY(mapped_range.range->clone(*page_table))));
|
||||
MUST(mapped_ranges.push_back(TRY(mapped_range->clone(*page_table))));
|
||||
|
||||
auto loadable_elf = TRY(m_loadable_elf->clone(*page_table));
|
||||
|
||||
Process* forked = create_process(m_credentials, m_pid, m_sid, m_pgrp);
|
||||
forked->m_controlling_terminal = m_controlling_terminal;
|
||||
|
@ -351,6 +331,7 @@ namespace Kernel
|
|||
forked->m_page_table = BAN::move(page_table);
|
||||
forked->m_open_file_descriptors = BAN::move(open_file_descriptors);
|
||||
forked->m_mapped_ranges = BAN::move(mapped_ranges);
|
||||
forked->m_loadable_elf = BAN::move(loadable_elf);
|
||||
forked->m_is_userspace = m_is_userspace;
|
||||
forked->m_userspace_info = m_userspace_info;
|
||||
forked->m_has_called_exec = false;
|
||||
|
@ -369,52 +350,39 @@ namespace Kernel
|
|||
{
|
||||
// NOTE: We scope everything for automatic deletion
|
||||
{
|
||||
LockGuard _(m_lock);
|
||||
|
||||
BAN::Vector<BAN::String> str_argv;
|
||||
for (int i = 0; argv && argv[i]; i++)
|
||||
{
|
||||
validate_pointer_access(argv + i, sizeof(char*));
|
||||
validate_string_access(argv[i]);
|
||||
TRY(str_argv.emplace_back(argv[i]));
|
||||
}
|
||||
|
||||
BAN::Vector<BAN::String> str_envp;
|
||||
|
||||
for (int i = 0; envp && envp[i]; i++)
|
||||
{
|
||||
LockGuard _(m_lock);
|
||||
|
||||
for (int i = 0; argv && argv[i]; i++)
|
||||
{
|
||||
validate_pointer_access(argv + i, sizeof(char*));
|
||||
validate_string_access(argv[i]);
|
||||
TRY(str_argv.emplace_back(argv[i]));
|
||||
}
|
||||
|
||||
for (int i = 0; envp && envp[i]; i++)
|
||||
{
|
||||
validate_pointer_access(envp + 1, sizeof(char*));
|
||||
validate_string_access(envp[i]);
|
||||
TRY(str_envp.emplace_back(envp[i]));
|
||||
}
|
||||
validate_pointer_access(envp + 1, sizeof(char*));
|
||||
validate_string_access(envp[i]);
|
||||
TRY(str_envp.emplace_back(envp[i]));
|
||||
}
|
||||
|
||||
BAN::String working_directory;
|
||||
|
||||
{
|
||||
LockGuard _(m_lock);
|
||||
TRY(working_directory.append(m_working_directory));
|
||||
}
|
||||
|
||||
auto elf = TRY(load_elf_for_exec(m_credentials, path, working_directory));
|
||||
|
||||
LockGuard lock_guard(m_lock);
|
||||
BAN::String executable_path;
|
||||
TRY(executable_path.append(path));
|
||||
|
||||
m_open_file_descriptors.close_cloexec();
|
||||
|
||||
m_mapped_ranges.clear();
|
||||
m_loadable_elf.clear();
|
||||
|
||||
load_elf_to_memory(*elf);
|
||||
|
||||
m_userspace_info.entry = elf->file_header_native().e_entry;
|
||||
m_loadable_elf = TRY(load_elf_for_exec(m_credentials, executable_path, m_working_directory, page_table()));
|
||||
m_loadable_elf->reserve_address_space();
|
||||
m_userspace_info.entry = m_loadable_elf->entry_point();
|
||||
|
||||
for (size_t i = 0; i < sizeof(m_signal_handlers) / sizeof(*m_signal_handlers); i++)
|
||||
m_signal_handlers[i] = (vaddr_t)SIG_DFL;
|
||||
|
||||
// NOTE: we clear the elf since we don't need the memory anymore
|
||||
elf.clear();
|
||||
|
||||
ASSERT(m_threads.size() == 1);
|
||||
ASSERT(&Process::current() == this);
|
||||
|
||||
|
@ -433,9 +401,9 @@ namespace Kernel
|
|||
page_table(),
|
||||
0x400000, KERNEL_OFFSET,
|
||||
bytes,
|
||||
PageTable::Flags::UserSupervisor | PageTable::Flags::ReadWrite | PageTable::Flags::Present
|
||||
PageTable::Flags::UserSupervisor | PageTable::Flags::ReadWrite | PageTable::Flags::Present,
|
||||
true
|
||||
));
|
||||
range->set_zero();
|
||||
|
||||
size_t data_offset = sizeof(char*) * (container.size() + 1);
|
||||
for (size_t i = 0; i < container.size(); i++)
|
||||
|
@ -454,17 +422,19 @@ namespace Kernel
|
|||
|
||||
auto argv_range = create_range(str_argv);
|
||||
m_userspace_info.argv = (char**)argv_range->vaddr();
|
||||
MUST(m_mapped_ranges.emplace_back(false, BAN::move(argv_range)));
|
||||
MUST(m_mapped_ranges.push_back(BAN::move(argv_range)));
|
||||
|
||||
auto envp_range = create_range(str_envp);
|
||||
m_userspace_info.envp = (char**)envp_range->vaddr();
|
||||
MUST(m_mapped_ranges.emplace_back(false, BAN::move(envp_range)));
|
||||
MUST(m_mapped_ranges.push_back(BAN::move(envp_range)));
|
||||
|
||||
m_userspace_info.argc = str_argv.size();
|
||||
|
||||
asm volatile("cli");
|
||||
}
|
||||
|
||||
m_has_called_exec = true;
|
||||
|
||||
m_threads.front()->setup_exec();
|
||||
Scheduler::get().execute_current_thread();
|
||||
ASSERT_NOT_REACHED();
|
||||
|
@ -542,63 +512,6 @@ namespace Kernel
|
|||
return 0;
|
||||
}
|
||||
|
||||
void Process::load_elf_to_memory(LibELF::ELF& elf)
|
||||
{
|
||||
ASSERT(elf.is_native());
|
||||
|
||||
auto& elf_file_header = elf.file_header_native();
|
||||
for (size_t i = 0; i < elf_file_header.e_phnum; i++)
|
||||
{
|
||||
auto& elf_program_header = elf.program_header_native(i);
|
||||
|
||||
switch (elf_program_header.p_type)
|
||||
{
|
||||
case LibELF::PT_NULL:
|
||||
break;
|
||||
case LibELF::PT_LOAD:
|
||||
{
|
||||
PageTable::flags_t flags = PageTable::Flags::UserSupervisor | PageTable::Flags::Present;
|
||||
if (elf_program_header.p_flags & LibELF::PF_W)
|
||||
flags |= PageTable::Flags::ReadWrite;
|
||||
if (elf_program_header.p_flags & LibELF::PF_X)
|
||||
flags |= PageTable::Flags::Execute;
|
||||
|
||||
size_t page_start = elf_program_header.p_vaddr / PAGE_SIZE;
|
||||
size_t page_end = BAN::Math::div_round_up<size_t>(elf_program_header.p_vaddr + elf_program_header.p_memsz, PAGE_SIZE);
|
||||
size_t page_count = page_end - page_start;
|
||||
|
||||
page_table().lock();
|
||||
|
||||
if (!page_table().is_range_free(page_start * PAGE_SIZE, page_count * PAGE_SIZE))
|
||||
{
|
||||
page_table().debug_dump();
|
||||
Kernel::panic("vaddr {8H}-{8H} not free {8H}-{8H}",
|
||||
page_start * PAGE_SIZE,
|
||||
page_start * PAGE_SIZE + page_count * PAGE_SIZE
|
||||
);
|
||||
}
|
||||
|
||||
{
|
||||
LockGuard _(m_lock);
|
||||
auto range = MUST(VirtualRange::create_to_vaddr(page_table(), page_start * PAGE_SIZE, page_count * PAGE_SIZE, flags));
|
||||
range->set_zero();
|
||||
range->copy_from(elf_program_header.p_vaddr % PAGE_SIZE, elf.data() + elf_program_header.p_offset, elf_program_header.p_filesz);
|
||||
|
||||
MUST(m_mapped_ranges.emplace_back(false, BAN::move(range)));
|
||||
}
|
||||
|
||||
page_table().unlock();
|
||||
|
||||
break;
|
||||
}
|
||||
default:
|
||||
ASSERT_NOT_REACHED();
|
||||
}
|
||||
}
|
||||
|
||||
m_has_called_exec = true;
|
||||
}
|
||||
|
||||
BAN::ErrorOr<void> Process::create_file(BAN::StringView path, mode_t mode)
|
||||
{
|
||||
LockGuard _(m_lock);
|
||||
|
@ -619,6 +532,35 @@ namespace Kernel
|
|||
return {};
|
||||
}
|
||||
|
||||
BAN::ErrorOr<bool> Process::allocate_page_for_demand_paging(vaddr_t address)
|
||||
{
|
||||
ASSERT(&Process::current() == this);
|
||||
|
||||
LockGuard _(m_lock);
|
||||
|
||||
if (Thread::current().stack().contains(address))
|
||||
{
|
||||
TRY(Thread::current().stack().allocate_page_for_demand_paging(address));
|
||||
return true;
|
||||
}
|
||||
|
||||
for (auto& mapped_range : m_mapped_ranges)
|
||||
{
|
||||
if (!mapped_range->contains(address))
|
||||
continue;
|
||||
TRY(mapped_range->allocate_page_for_demand_paging(address));
|
||||
return true;
|
||||
}
|
||||
|
||||
if (m_loadable_elf && m_loadable_elf->contains(address))
|
||||
{
|
||||
TRY(m_loadable_elf->load_page_to_memory(address));
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
BAN::ErrorOr<long> Process::open_file(BAN::StringView path, int flags, mode_t mode)
|
||||
{
|
||||
BAN::String absolute_path = TRY(absolute_path_of(path));
|
||||
|
@ -773,6 +715,45 @@ namespace Kernel
|
|||
return 0;
|
||||
}
|
||||
|
||||
[[noreturn]] static void reset_system()
|
||||
{
|
||||
lai_acpi_reset();
|
||||
|
||||
// acpi reset did not work
|
||||
|
||||
dwarnln("Could not reset with ACPI, crashing the cpu");
|
||||
|
||||
// reset through triple fault
|
||||
IDT::force_triple_fault();
|
||||
}
|
||||
|
||||
BAN::ErrorOr<long> Process::sys_poweroff(int command)
|
||||
{
|
||||
if (command != POWEROFF_REBOOT && command != POWEROFF_SHUTDOWN)
|
||||
return BAN::Error::from_errno(EINVAL);
|
||||
|
||||
// FIXME: gracefully kill all processes
|
||||
|
||||
DevFileSystem::get().initiate_sync(true);
|
||||
|
||||
lai_api_error_t error;
|
||||
switch (command)
|
||||
{
|
||||
case POWEROFF_REBOOT:
|
||||
reset_system();
|
||||
break;
|
||||
case POWEROFF_SHUTDOWN:
|
||||
error = lai_enter_sleep(5);
|
||||
break;
|
||||
default:
|
||||
ASSERT_NOT_REACHED();
|
||||
}
|
||||
|
||||
// If we reach here, there was an error
|
||||
dprintln("{}", lai_api_error_to_string(error));
|
||||
return BAN::Error::from_errno(EUNKNOWN);
|
||||
}
|
||||
|
||||
BAN::ErrorOr<long> Process::sys_read_dir_entries(int fd, DirectoryEntryList* list, size_t list_size)
|
||||
{
|
||||
LockGuard _(m_lock);
|
||||
|
@ -847,13 +828,13 @@ namespace Kernel
|
|||
page_table(),
|
||||
0x400000, KERNEL_OFFSET,
|
||||
args->len,
|
||||
PageTable::Flags::UserSupervisor | PageTable::Flags::ReadWrite | PageTable::Flags::Present
|
||||
PageTable::Flags::UserSupervisor | PageTable::Flags::ReadWrite | PageTable::Flags::Present,
|
||||
false
|
||||
));
|
||||
range->set_zero();
|
||||
|
||||
LockGuard _(m_lock);
|
||||
TRY(m_mapped_ranges.emplace_back(true, BAN::move(range)));
|
||||
return m_mapped_ranges.back().range->vaddr();
|
||||
TRY(m_mapped_ranges.push_back(BAN::move(range)));
|
||||
return m_mapped_ranges.back()->vaddr();
|
||||
}
|
||||
|
||||
return BAN::Error::from_errno(ENOTSUP);
|
||||
|
@ -872,9 +853,7 @@ namespace Kernel
|
|||
|
||||
for (size_t i = 0; i < m_mapped_ranges.size(); i++)
|
||||
{
|
||||
if (!m_mapped_ranges[i].can_be_unmapped)
|
||||
continue;
|
||||
auto& range = m_mapped_ranges[i].range;
|
||||
auto& range = m_mapped_ranges[i];
|
||||
if (vaddr + len < range->vaddr() || vaddr >= range->vaddr() + range->size())
|
||||
continue;
|
||||
m_mapped_ranges.remove(i);
|
||||
|
@ -1363,9 +1342,12 @@ namespace Kernel
|
|||
|
||||
// FIXME: should we allow cross mapping access?
|
||||
for (auto& mapped_range : m_mapped_ranges)
|
||||
if (vaddr >= mapped_range.range->vaddr() && vaddr + size <= mapped_range.range->vaddr() + mapped_range.range->size())
|
||||
if (vaddr >= mapped_range->vaddr() && vaddr + size <= mapped_range->vaddr() + mapped_range->size())
|
||||
return;
|
||||
|
||||
if (m_loadable_elf->contains(vaddr))
|
||||
return;
|
||||
|
||||
unauthorized_access:
|
||||
dwarnln("process {}, thread {} attempted to make an invalid pointer access", pid(), Thread::current().tid());
|
||||
Debug::dump_stack_trace();
|
||||
|
|
|
@ -196,6 +196,9 @@ namespace Kernel
|
|||
case SYS_TTY_CTRL:
|
||||
ret = Process::current().sys_tty_ctrl((int)arg1, (int)arg2, (int)arg3);
|
||||
break;
|
||||
case SYS_POWEROFF:
|
||||
ret = Process::current().sys_poweroff((int)arg1);
|
||||
break;
|
||||
default:
|
||||
dwarnln("Unknown syscall {}", syscall);
|
||||
break;
|
||||
|
|
|
@ -328,7 +328,7 @@ namespace Kernel
|
|||
return count;
|
||||
}
|
||||
|
||||
bool TTY::has_data() const
|
||||
bool TTY::has_data_impl() const
|
||||
{
|
||||
LockGuard _(m_lock);
|
||||
return m_output.flush;
|
||||
|
|
|
@ -122,8 +122,21 @@ namespace Kernel
|
|||
|
||||
thread->m_is_userspace = true;
|
||||
|
||||
thread->m_stack = TRY(VirtualRange::create_to_vaddr_range(process->page_table(), 0x300000, KERNEL_OFFSET, m_userspace_stack_size, PageTable::Flags::UserSupervisor | PageTable::Flags::ReadWrite | PageTable::Flags::Present));
|
||||
thread->m_interrupt_stack = TRY(VirtualRange::create_to_vaddr_range(process->page_table(), 0x300000, KERNEL_OFFSET, m_interrupt_stack_size, PageTable::Flags::UserSupervisor | PageTable::Flags::ReadWrite | PageTable::Flags::Present));
|
||||
thread->m_stack = TRY(VirtualRange::create_to_vaddr_range(
|
||||
process->page_table(),
|
||||
0x300000, KERNEL_OFFSET,
|
||||
m_userspace_stack_size,
|
||||
PageTable::Flags::UserSupervisor | PageTable::Flags::ReadWrite | PageTable::Flags::Present,
|
||||
true
|
||||
));
|
||||
|
||||
thread->m_interrupt_stack = TRY(VirtualRange::create_to_vaddr_range(
|
||||
process->page_table(),
|
||||
0x300000, KERNEL_OFFSET,
|
||||
m_interrupt_stack_size,
|
||||
PageTable::Flags::ReadWrite | PageTable::Flags::Present,
|
||||
true
|
||||
));
|
||||
|
||||
thread->setup_exec();
|
||||
|
||||
|
|
|
@ -161,6 +161,8 @@ static void init2(void*)
|
|||
|
||||
dprintln("Scheduler started");
|
||||
|
||||
InterruptController::get().enter_acpi_mode();
|
||||
|
||||
auto console = MUST(DevFileSystem::get().root_inode()->find_inode(cmdline.console));
|
||||
ASSERT(console->is_tty());
|
||||
((TTY*)console.ptr())->set_as_current();
|
||||
|
|
|
@ -91,6 +91,8 @@ __BEGIN_DECLS
|
|||
#define ENOTBLK 82
|
||||
#define EEXISTS 83
|
||||
|
||||
#define EUNKNOWN 0xFF
|
||||
|
||||
#define errno __errno
|
||||
|
||||
extern int __errno;
|
||||
|
|
|
@ -11,6 +11,9 @@ __BEGIN_DECLS
|
|||
#define TTY_FLAG_ENABLE_OUTPUT 1
|
||||
#define TTY_FLAG_ENABLE_INPUT 2
|
||||
|
||||
#define POWEROFF_SHUTDOWN 0
|
||||
#define POWEROFF_REBOOT 1
|
||||
|
||||
/*
|
||||
fildes: refers to valid tty device
|
||||
command: one of TTY_CMD_* definitions
|
||||
|
@ -19,6 +22,7 @@ flags: bitwise or of TTY_FLAG_* definitions
|
|||
return value: 0 on success, -1 on failure and errno set to the error
|
||||
*/
|
||||
int tty_ctrl(int fildes, int command, int flags);
|
||||
int poweroff(int command);
|
||||
|
||||
__END_DECLS
|
||||
|
||||
|
|
|
@ -54,6 +54,7 @@ __BEGIN_DECLS
|
|||
#define SYS_MMAP 51
|
||||
#define SYS_MUNMAP 52
|
||||
#define SYS_TTY_CTRL 53
|
||||
#define SYS_POWEROFF 54
|
||||
|
||||
__END_DECLS
|
||||
|
||||
|
|
|
@ -123,7 +123,7 @@ void* malloc(size_t size)
|
|||
|
||||
// find the first pool with size atleast size
|
||||
size_t first_usable_pool = 0;
|
||||
while (s_malloc_pools[first_usable_pool].size < size)
|
||||
while (s_malloc_pools[first_usable_pool].size - sizeof(malloc_node_t) < size)
|
||||
first_usable_pool++;
|
||||
// first_usable_pool = ceil(log(size/s_malloc_smallest_pool, s_malloc_pool_size_mult))
|
||||
|
||||
|
@ -140,6 +140,7 @@ void* malloc(size_t size)
|
|||
continue;
|
||||
if (!allocate_pool(i))
|
||||
break;
|
||||
// NOTE: always works since we just created the pool
|
||||
return allocate_from_pool(i, size);
|
||||
}
|
||||
|
||||
|
|
|
@ -15,6 +15,19 @@
|
|||
written++; \
|
||||
} while (false)
|
||||
|
||||
enum class length_t
|
||||
{
|
||||
none,
|
||||
hh,
|
||||
h,
|
||||
l,
|
||||
ll,
|
||||
j,
|
||||
z,
|
||||
t,
|
||||
L,
|
||||
};
|
||||
|
||||
struct format_options_t
|
||||
{
|
||||
bool alternate_form { false };
|
||||
|
@ -24,6 +37,7 @@ struct format_options_t
|
|||
bool show_plus_sign { false };
|
||||
int width { -1 };
|
||||
int percision { -1 };
|
||||
length_t length { length_t::none };
|
||||
};
|
||||
|
||||
template<BAN::integral T>
|
||||
|
@ -308,45 +322,157 @@ extern "C" int printf_impl(const char* format, va_list arguments, int (*putc_fun
|
|||
options.percision = percision;
|
||||
}
|
||||
|
||||
// TODO: Lenght modifier
|
||||
// PARSE LENGTH
|
||||
if (*format == 'h')
|
||||
{
|
||||
if (*(format + 1) == 'h')
|
||||
{
|
||||
format++;
|
||||
options.length = length_t::hh;
|
||||
}
|
||||
else
|
||||
options.length = length_t::h;
|
||||
}
|
||||
else if (*format == 'l')
|
||||
{
|
||||
if (*(format + 1) == 'l')
|
||||
{
|
||||
format++;
|
||||
options.length = length_t::ll;
|
||||
}
|
||||
else
|
||||
options.length = length_t::l;
|
||||
}
|
||||
else if (*format == 'j')
|
||||
options.length = length_t::j;
|
||||
else if (*format == 'z')
|
||||
options.length = length_t::z;
|
||||
else if (*format == 't')
|
||||
options.length = length_t::t;
|
||||
else if (*format == 'L')
|
||||
options.length = length_t::L;
|
||||
else
|
||||
format--;
|
||||
format++;
|
||||
|
||||
char conversion[128];
|
||||
const char* string = nullptr;
|
||||
|
||||
int length = -1;
|
||||
|
||||
#define PARSE_INT_CASE(length, type) \
|
||||
case length_t::length: integer_to_string<type>(conversion, va_arg(arguments, type), BASE_, UPPER_, options); break
|
||||
|
||||
#define PARSE_INT_CASE_CAST(length, cast, type) \
|
||||
case length_t::length: integer_to_string<cast>(conversion, va_arg(arguments, type), BASE_, UPPER_, options); break
|
||||
|
||||
#define PARSE_INT_DEFAULT(type) \
|
||||
default: integer_to_string<type>(conversion, va_arg(arguments, type), BASE_, UPPER_, options); break
|
||||
|
||||
switch (*format)
|
||||
{
|
||||
case 'd':
|
||||
case 'i':
|
||||
{
|
||||
int value = va_arg(arguments, int);
|
||||
integer_to_string<int>(conversion, value, 10, false, options);
|
||||
switch (options.length)
|
||||
{
|
||||
#define BASE_ 10
|
||||
#define UPPER_ false
|
||||
PARSE_INT_CASE_CAST(hh, signed char, int);
|
||||
PARSE_INT_CASE_CAST(h, short, int);
|
||||
PARSE_INT_CASE(l, long);
|
||||
PARSE_INT_CASE(ll, long long);
|
||||
PARSE_INT_CASE(j, intmax_t);
|
||||
PARSE_INT_CASE(z, ssize_t);
|
||||
PARSE_INT_CASE(t, ptrdiff_t);
|
||||
PARSE_INT_DEFAULT(int);
|
||||
#undef BASE_
|
||||
#undef UPPER_
|
||||
}
|
||||
string = conversion;
|
||||
format++;
|
||||
break;
|
||||
}
|
||||
case 'o':
|
||||
{
|
||||
unsigned int value = va_arg(arguments, unsigned int);
|
||||
integer_to_string<unsigned int>(conversion, value, 8, false, options);
|
||||
switch (options.length)
|
||||
{
|
||||
#define BASE_ 8
|
||||
#define UPPER_ false
|
||||
PARSE_INT_CASE_CAST(hh, unsigned char, unsigned int);
|
||||
PARSE_INT_CASE_CAST(h, unsigned short, unsigned int);
|
||||
PARSE_INT_CASE(l, unsigned long);
|
||||
PARSE_INT_CASE(ll, unsigned long long);
|
||||
PARSE_INT_CASE(j, uintmax_t);
|
||||
PARSE_INT_CASE(z, size_t);
|
||||
PARSE_INT_CASE(t, uintptr_t);
|
||||
PARSE_INT_DEFAULT(unsigned int);
|
||||
#undef BASE_
|
||||
#undef UPPER_
|
||||
}
|
||||
string = conversion;
|
||||
format++;
|
||||
break;
|
||||
}
|
||||
case 'u':
|
||||
{
|
||||
unsigned int value = va_arg(arguments, unsigned int);
|
||||
integer_to_string<unsigned int>(conversion, value, 10, false, options);
|
||||
switch (options.length)
|
||||
{
|
||||
#define BASE_ 10
|
||||
#define UPPER_ false
|
||||
PARSE_INT_CASE_CAST(hh, unsigned char, unsigned int);
|
||||
PARSE_INT_CASE_CAST(h, unsigned short, unsigned int);
|
||||
PARSE_INT_CASE(l, unsigned long);
|
||||
PARSE_INT_CASE(ll, unsigned long long);
|
||||
PARSE_INT_CASE(j, uintmax_t);
|
||||
PARSE_INT_CASE(z, size_t);
|
||||
PARSE_INT_CASE(t, uintptr_t);
|
||||
PARSE_INT_DEFAULT(unsigned int);
|
||||
#undef BASE_
|
||||
#undef UPPER_
|
||||
}
|
||||
string = conversion;
|
||||
format++;
|
||||
break;
|
||||
}
|
||||
case 'x':
|
||||
{
|
||||
switch (options.length)
|
||||
{
|
||||
#define BASE_ 16
|
||||
#define UPPER_ false
|
||||
PARSE_INT_CASE_CAST(hh, unsigned char, unsigned int);
|
||||
PARSE_INT_CASE_CAST(h, unsigned short, unsigned int);
|
||||
PARSE_INT_CASE(l, unsigned long);
|
||||
PARSE_INT_CASE(ll, unsigned long long);
|
||||
PARSE_INT_CASE(j, uintmax_t);
|
||||
PARSE_INT_CASE(z, size_t);
|
||||
PARSE_INT_CASE(t, uintptr_t);
|
||||
PARSE_INT_DEFAULT(unsigned int);
|
||||
#undef BASE_
|
||||
#undef UPPER_
|
||||
}
|
||||
string = conversion;
|
||||
format++;
|
||||
break;
|
||||
}
|
||||
case 'X':
|
||||
{
|
||||
unsigned int value = va_arg(arguments, unsigned int);
|
||||
integer_to_string<unsigned int>(conversion, value, 16, *format == 'X', options);
|
||||
switch (options.length)
|
||||
{
|
||||
#define BASE_ 16
|
||||
#define UPPER_ true
|
||||
PARSE_INT_CASE_CAST(hh, unsigned char, unsigned int);
|
||||
PARSE_INT_CASE_CAST(h, unsigned short, unsigned int);
|
||||
PARSE_INT_CASE(l, unsigned long);
|
||||
PARSE_INT_CASE(ll, unsigned long long);
|
||||
PARSE_INT_CASE(j, uintmax_t);
|
||||
PARSE_INT_CASE(z, size_t);
|
||||
PARSE_INT_CASE(t, uintptr_t);
|
||||
PARSE_INT_DEFAULT(unsigned int);
|
||||
#undef BASE_
|
||||
#undef UPPER_
|
||||
}
|
||||
string = conversion;
|
||||
format++;
|
||||
break;
|
||||
|
@ -355,8 +481,11 @@ extern "C" int printf_impl(const char* format, va_list arguments, int (*putc_fun
|
|||
case 'e':
|
||||
case 'E':
|
||||
{
|
||||
double value = va_arg(arguments, double);
|
||||
floating_point_to_exponent_string<double>(conversion, value, *format == 'E', options);
|
||||
switch (options.length)
|
||||
{
|
||||
case length_t::L: floating_point_to_exponent_string<long double> (conversion, va_arg(arguments, long double), *format == 'E', options); break;
|
||||
default: floating_point_to_exponent_string<double> (conversion, va_arg(arguments, double), *format == 'E', options); break;
|
||||
}
|
||||
string = conversion;
|
||||
format++;
|
||||
break;
|
||||
|
@ -364,13 +493,15 @@ extern "C" int printf_impl(const char* format, va_list arguments, int (*putc_fun
|
|||
case 'f':
|
||||
case 'F':
|
||||
{
|
||||
double value = va_arg(arguments, double);
|
||||
floating_point_to_string<double>(conversion, value, *format == 'F', options);
|
||||
switch (options.length)
|
||||
{
|
||||
case length_t::L: floating_point_to_string<long double> (conversion, va_arg(arguments, long double), *format == 'F', options); break;
|
||||
default: floating_point_to_string<double> (conversion, va_arg(arguments, double), *format == 'F', options); break;
|
||||
}
|
||||
string = conversion;
|
||||
format++;
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
case 'g':
|
||||
case 'G':
|
||||
// TODO
|
||||
|
@ -379,6 +510,7 @@ extern "C" int printf_impl(const char* format, va_list arguments, int (*putc_fun
|
|||
case 'A':
|
||||
// TODO
|
||||
break;
|
||||
#endif
|
||||
case 'c':
|
||||
{
|
||||
conversion[0] = va_arg(arguments, int);
|
||||
|
@ -416,8 +548,17 @@ extern "C" int printf_impl(const char* format, va_list arguments, int (*putc_fun
|
|||
}
|
||||
case 'n':
|
||||
{
|
||||
int* target = va_arg(arguments, int*);
|
||||
*target = written;
|
||||
switch (options.length)
|
||||
{
|
||||
case length_t::hh: *va_arg(arguments, signed char*) = written; break;
|
||||
case length_t::h: *va_arg(arguments, short*) = written; break;
|
||||
case length_t::l: *va_arg(arguments, long*) = written; break;
|
||||
case length_t::ll: *va_arg(arguments, long long*) = written; break;
|
||||
case length_t::j: *va_arg(arguments, intmax_t*) = written; break;
|
||||
case length_t::z: *va_arg(arguments, ssize_t*) = written; break;
|
||||
case length_t::t: *va_arg(arguments, ptrdiff_t*) = written; break;
|
||||
default: *va_arg(arguments, int*) = written; break;
|
||||
}
|
||||
format++;
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -173,6 +173,7 @@ const char* strerrorname_np(int error)
|
|||
case EXDEV: return "EXDEV";
|
||||
case EEXISTS: return "EEXISTS";
|
||||
case ENOTBLK: return "ENOTBLK";
|
||||
case EUNKNOWN: return "EUNKNOWN";
|
||||
}
|
||||
|
||||
errno = EINVAL;
|
||||
|
@ -267,6 +268,7 @@ const char* strerrordesc_np(int error)
|
|||
case EXDEV: return "Cross-device link.";
|
||||
case EEXISTS: return "File exists";
|
||||
case ENOTBLK: return "Block device required";
|
||||
case EUNKNOWN: return "Unknown error";
|
||||
}
|
||||
|
||||
errno = EINVAL;
|
||||
|
|
|
@ -6,3 +6,8 @@ int tty_ctrl(int fildes, int command, int flags)
|
|||
{
|
||||
return syscall(SYS_TTY_CTRL, fildes, command, flags);
|
||||
}
|
||||
|
||||
int poweroff(int command)
|
||||
{
|
||||
return syscall(SYS_POWEROFF, command);
|
||||
}
|
||||
|
|
|
@ -9,6 +9,7 @@ set(USERSPACE_PROJECTS
|
|||
id
|
||||
init
|
||||
ls
|
||||
poweroff
|
||||
Shell
|
||||
snake
|
||||
stat
|
||||
|
|
|
@ -159,6 +159,21 @@ BAN::Optional<BAN::String> parse_dollar(BAN::StringView command, size_t& i)
|
|||
return "$"sv;
|
||||
}
|
||||
|
||||
BAN::StringView strip_whitespace(BAN::StringView sv)
|
||||
{
|
||||
size_t leading = 0;
|
||||
while (leading < sv.size() && isspace(sv[leading]))
|
||||
leading++;
|
||||
sv = sv.substring(leading);
|
||||
|
||||
size_t trailing = 0;
|
||||
while (trailing < sv.size() && isspace(sv[sv.size() - trailing - 1]))
|
||||
trailing++;
|
||||
sv = sv.substring(0, sv.size() - trailing);
|
||||
|
||||
return sv;
|
||||
}
|
||||
|
||||
BAN::Vector<BAN::Vector<BAN::String>> parse_command(BAN::StringView command_view)
|
||||
{
|
||||
enum class State
|
||||
|
@ -168,6 +183,8 @@ BAN::Vector<BAN::Vector<BAN::String>> parse_command(BAN::StringView command_view
|
|||
DoubleQuote,
|
||||
};
|
||||
|
||||
command_view = strip_whitespace(command_view);
|
||||
|
||||
BAN::Vector<BAN::Vector<BAN::String>> result;
|
||||
BAN::Vector<BAN::String> command_args;
|
||||
|
||||
|
|
|
@ -26,11 +26,11 @@ int parse_int(const char* val)
|
|||
return result;
|
||||
}
|
||||
|
||||
void print_time(uint64_t start_ns, uint64_t end_ns, int transfered)
|
||||
void print_time(uint64_t start_ns, uint64_t end_ns, size_t transfered)
|
||||
{
|
||||
static bool first = true;
|
||||
uint64_t duration_ns = end_ns - start_ns;
|
||||
printf("%s%d bytes copied, %d.%09d s\e[K\n", (first ? "" : "\e[F"), transfered, (int)(duration_ns / 1'000'000'000), (int)(duration_ns % 1'000'000'000));
|
||||
printf("%s%zu bytes copied, %d.%09d s\e[K\n", (first ? "" : "\e[F"), transfered, (int)(duration_ns / 1'000'000'000), (int)(duration_ns % 1'000'000'000));
|
||||
first = false;
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,17 @@
|
|||
cmake_minimum_required(VERSION 3.26)
|
||||
|
||||
project(poweroff CXX)
|
||||
|
||||
set(SOURCES
|
||||
main.cpp
|
||||
)
|
||||
|
||||
add_executable(poweroff ${SOURCES})
|
||||
target_compile_options(poweroff PUBLIC -O2 -g)
|
||||
target_link_libraries(poweroff PUBLIC libc)
|
||||
|
||||
add_custom_target(poweroff-install
|
||||
COMMAND sudo cp ${CMAKE_CURRENT_BINARY_DIR}/poweroff ${BANAN_BIN}/
|
||||
DEPENDS poweroff
|
||||
USES_TERMINAL
|
||||
)
|
|
@ -0,0 +1,38 @@
|
|||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/banan-os.h>
|
||||
|
||||
void usage(int ret, char* arg0)
|
||||
{
|
||||
FILE* fout = (ret == 0) ? stdout : stderr;
|
||||
fprintf(fout, "usage: %s [OPTIONS]...\n");
|
||||
fprintf(fout, " -s, --shutdown Shutdown the system (default)\n");
|
||||
fprintf(fout, " -r, --reboot Reboot the system\n");
|
||||
fprintf(fout, " -h, --help Show this message\n");
|
||||
exit(ret);
|
||||
}
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
int operation = POWEROFF_SHUTDOWN;
|
||||
for (int i = 1; i < argc; i++)
|
||||
{
|
||||
if (strcmp(argv[i], "-s") == 0 || strcmp(argv[i], "--shutdown") == 0)
|
||||
operation = POWEROFF_SHUTDOWN;
|
||||
else if (strcmp(argv[i], "-r") == 0 || strcmp(argv[i], "--reboot") == 0)
|
||||
operation = POWEROFF_REBOOT;
|
||||
else if (strcmp(argv[i], "-h") == 0 || strcmp(argv[i], "--help") == 0)
|
||||
usage(0, argv[0]);
|
||||
else
|
||||
usage(1, argv[0]);
|
||||
}
|
||||
|
||||
if (poweroff(operation) == -1)
|
||||
{
|
||||
perror("poweroff");
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -65,9 +65,9 @@ int main(int argc, char** argv)
|
|||
access[10] = '\0';
|
||||
|
||||
printf(" File: %s\n", argv[i]);
|
||||
printf(" Size: %-15d Blocks: %-10d IO Block: %-6d %s\n", (int)st.st_size, (int)st.st_blocks, (int)st.st_blksize, type);
|
||||
printf("Device: %d,%-5d Inode: %-11d Links: %-5d Device type: %d,%d\n", (int)major(st.st_dev), (int)minor(st.st_dev), (int)st.st_ino, (int)st.st_nlink, (int)major(st.st_rdev), (int)minor(st.st_rdev));
|
||||
printf("Access: (%04o/%s) Uid: %5d Gid: %5d\n", (int)(st.st_mode & S_IRWXMASK), access, (int)st.st_uid, (int)st.st_gid);
|
||||
printf(" Size: %-15ld Blocks: %-10ld IO Block: %-6ld %s\n", st.st_size, st.st_blocks, st.st_blksize, type);
|
||||
printf("Device: %lu,%-5lu Inode: %-11lu Links: %-5lu Device type: %lu,%lu\n", major(st.st_dev), minor(st.st_dev), st.st_ino, st.st_nlink, major(st.st_rdev), minor(st.st_rdev));
|
||||
printf("Access: (%04o/%s) Uid: %5d Gid: %5d\n", st.st_mode & S_IRWXMASK, access, st.st_uid, st.st_gid);
|
||||
printf("Access: "); print_timestamp(st.st_atim); printf("\n");
|
||||
printf("Modify: "); print_timestamp(st.st_mtim); printf("\n");
|
||||
printf("Change: "); print_timestamp(st.st_ctim); printf("\n");
|
||||
|
|
Loading…
Reference in New Issue