Compare commits

...

19 Commits

Author SHA1 Message Date
Bananymous 7e9e4c47ae LibELF: Optimize LoadableELF::clone() memory usage
We only clone mapped pages that have been marked as writeble.
Read/execute only pages will be exactly as in the file itself and
can be demand paged also :D
2023-09-29 02:05:12 +03:00
Bananymous 603fc200e6 Kernel: Add some sanity assertions/functions 2023-09-29 02:03:19 +03:00
Bananymous c11e84b248 Kernel: Use the new on demand ELF structure
All executable files are now read from disk and paged on demand.
This was a big rewrite of the old ELF library but in the end
everything seems much cleaner, since all the old functionality was
not actually needed for execution.

I have to do some measurements, but I feel like memory usage dropped
quite a bit after this change.
2023-09-29 02:00:10 +03:00
Bananymous be13120554 LibELF: Implement new ELF structure
This structure is used for demand pagable execution. It handles all
memory allocation and file reading.
2023-09-29 01:58:03 +03:00
Bananymous 9943edad5a LibELF: Add types for native executable 2023-09-29 01:56:57 +03:00
Bananymous f4049be975 Kernel: Fix off by one error when calculating pages in range 2023-09-29 01:56:15 +03:00
Bananymous 6cf7e01fe9 Kernel: Don't map interrupt stack as userspace accessable 2023-09-28 21:58:24 +03:00
Bananymous b51d2f5295 Kernel: mmap regions are now demand paged
mmap will not actually take any memory unless you use the given
memory.
2023-09-28 21:07:14 +03:00
Bananymous 49d941ad65 LibC: Fix a bug in malloc
You could not allocate with size equal to one of the pool sizes.
2023-09-28 21:05:27 +03:00
Bananymous a66c3bdae5 Kernel: Remove duplicate code in VirtualRange::create_to_vaddr_range 2023-09-28 13:59:49 +03:00
Bananymous 547eabb403 Kernel: Reboot will now always succeed
If acpi reset fails, we forcefully trigger a triple fault to restart
the system.
2023-09-28 13:53:03 +03:00
Bananymous 79851394b3 Kernel/LibC/Userspace: Add SYS_POWEROFF + cli tool
You can now shutdown/reboot banan-os with the poweroff cli tool.

Reboot doesn't seem to work on qemu.
2023-09-28 12:36:47 +03:00
Bananymous fcdc922343 Kernel: Enter ACPI mode with lai 2023-09-28 12:30:27 +03:00
Bananymous 0b11d76576 LibC: Add errno for unknown error 2023-09-28 12:06:17 +03:00
Bananymous f7097398ca Kernel: Make tty overload correct has_data() function
This allows snake game to work again :)
2023-09-28 11:54:12 +03:00
Bananymous 85b1252b9e Userspace: Use printf length modifiers when printing 2023-09-28 11:49:31 +03:00
Bananymous 1cd12b5f16 LibC: Implement length modifiers to printf 2023-09-28 11:42:57 +03:00
Bananymous c84b66d078 Shell: String leading and trailing whitespace from commands
This fixes a bug of inserting empty argument if command had trailing
whitespace
2023-09-28 10:28:49 +03:00
Bananymous 27adb9486b BAN: Update Endiannes API
Add functions to swap endiannes or convert host to big/little endian

This code should be very compiler friendly and should be optimized to
single bswap instruction on x86.
2023-09-28 01:22:14 +03:00
34 changed files with 986 additions and 247 deletions

View File

@ -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;

View File

@ -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;
}
}

View File

@ -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;
};
}

View File

@ -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
}

View File

@ -118,7 +118,7 @@ set(LIBC_SOURCES
)
set(LIBELF_SOURCES
../LibELF/LibELF/ELF.cpp
../LibELF/LibELF/LoadableELF.cpp
)
set(KERNEL_SOURCES

View File

@ -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();
}
}

View File

@ -9,5 +9,6 @@ namespace IDT
void initialize();
void register_irq_handler(uint8_t irq, void(*f)());
[[noreturn]] void force_triple_fault();
}

View File

@ -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();

View File

@ -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;
}
}

View File

@ -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 };

View File

@ -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);

View File

@ -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;

View File

@ -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)

View File

@ -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;

View File

@ -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);
}
}

View File

@ -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;

View File

@ -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();

View File

@ -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;

View File

@ -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;

View File

@ -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();

View File

@ -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();

View File

@ -91,6 +91,8 @@ __BEGIN_DECLS
#define ENOTBLK 82
#define EEXISTS 83
#define EUNKNOWN 0xFF
#define errno __errno
extern int __errno;

View File

@ -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

View File

@ -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

View File

@ -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);
}

View File

@ -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;
}

View File

@ -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;

View File

@ -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);
}

View File

@ -9,6 +9,7 @@ set(USERSPACE_PROJECTS
id
init
ls
poweroff
Shell
snake
stat

View File

@ -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;

View File

@ -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;
}

View File

@ -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
)

View File

@ -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;
}

View File

@ -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");