Compare commits

..

19 Commits

Author SHA1 Message Date
f429e8c7bb driver-install: implemented a simple installer 2026-05-20 17:34:44 +03:00
cba2e8cfef Kernel: Implemented banos - a WIP C driver API
Banos is a stable WIP C driver API that is supposed to provide a simple
interface to interact with the kernel and load the modules dynamically.
It is WIP and atm this just implements module loading with a custom
banos_install syscall. Banos will not try to substitute parts of the
kernel instead it will just expose kernel functionality via a stable
BINARY API. Meaning binaries (should) remain forward and backward
compatible on a binary level.

Banos modules work similarly to those in linux, you expose symbols via
BANOS_EXPORT which allows you to export a name + addr paired symbol.
It puts it in the .banos-export section. Drivers provide metadata about
themselves in the REQUIRED .banos-driver section. Symbols are resolved
at runtime. The kernel exposes the driver functionality via the same
.banos-export export mechanism.

Banos modules are elf RELOCATABLE files (object files) which have
partial linking (only banos symbols should remain). Modules will
eventually define dependencies, will export symbols and will allow you
to build a complex object hierarchy.

This patch adds the banos_install syscall which takes in the driver
image to install and may only be executed by super users. The API
doesn't validate already loaded modules, as thats something the
userspace MAY choose to keep track of. Multi-instance functionality
shall be implemented via driver specific behaviuor (exposed in the dev
filesystem or some other means).

Modules are supposed to allow you to alter kernel behavior and extend
it, allowing you to create filesystems, drivers, networking
modifications, schedulers, probers, and more (hopefully) whilst
remaining binary compatible with any version of the kernel (again,
hopefully).
2026-05-20 17:34:42 +03:00
3ad67614aa Kernel: moved read/write_from_user out of Process 2026-05-20 17:34:39 +03:00
14aa28b043 Kernel: Handle TTY ioctls on all inodes not implementing it
This reduces debug spam while compiling software
2026-05-20 16:57:24 +03:00
6045726e41 Kernel: Optimize PageTable::unmap_range on x86_64 2026-05-20 16:16:44 +03:00
45e55d8907 LibC: Don't compare equal elements in qsort
This seemed to break supertuxkart which returned `less` in this case
2026-05-20 05:54:45 +03:00
e9d6431728 DynamicLoader: Fix dynamic TLS init order 2026-05-20 05:03:20 +03:00
aa8be130f9 DynamicLoader: Cleanup lazy PLT relocations 2026-05-20 05:02:48 +03:00
a19e6938eb Kernel: Remove TTY keyboard thread
This was really hacky as it had no idea when the keyboard had events
and the blocking was just one millisecond sleeps :D

Now keyboard device checks if current tty is receiving input and if so
it forwards the events to the TTY.
2026-05-20 04:17:03 +03:00
32206069bc Kernel: Use per cpu fast page for {Memory,File}BackedRegion 2026-05-20 02:51:48 +03:00
46a1903f8d Kernel: Use per cpu fast page for PMM 2026-05-20 02:46:50 +03:00
a3ca49ff1f Kernel: Don't sync ext2 inode on read
This is the most common operation and we don't even update any fields
during read (although we should update atime). The disk read+write is a
bit too heavy with the current cache system
2026-05-20 02:14:40 +03:00
94f92d982c Kernel: Optimize PageTable address space reservation
I only did this for the 64 bit target.
2026-05-20 02:01:16 +03:00
4f5f84bb5b Kernel: Speed up mmap address space reservation by a lot
Instead of scanning the page table for free range, we not use the
process's mapped regions to find a slot. This speeds up mmap by a lot!
2026-05-20 01:08:08 +03:00
5cb5ae2dfe Kernel: Use per cpu fast pages for DiskCache
This makes accessing disk cache a lot faster
2026-05-20 00:17:51 +03:00
7704e3c5c0 Kernel: Implement per cpu fast pages
Basically every fast page usage should be converted into this but I'll
do them one by one when they show up in profiles
2026-05-20 00:16:56 +03:00
376e4b4c45 Kernel: Reduce the number of sent IPIs
Only send an IPI when the target processors don't have pending messages.
This basically gets rid of TLB shootdowns from showing up in profiles.
Before they were taking maybe >10% kernel time :^D
2026-05-19 23:52:38 +03:00
24c37e7381 Kernel: Move TLB invalidation out of standard SMPMessages
This makes accessing TLB messages much faster as TLB flushes are very
frequent in comparison to other messages
2026-05-19 23:51:01 +03:00
fb9c67ab15 Kernel: Add PageTable API to invalidate full address space 2026-05-19 23:46:56 +03:00
38 changed files with 1110 additions and 388 deletions

View File

@@ -11,6 +11,7 @@ set(KERNEL_SOURCES
kernel/Audio/Controller.cpp kernel/Audio/Controller.cpp
kernel/Audio/HDAudio/AudioFunctionGroup.cpp kernel/Audio/HDAudio/AudioFunctionGroup.cpp
kernel/Audio/HDAudio/Controller.cpp kernel/Audio/HDAudio/Controller.cpp
kernel/Banos.cpp
kernel/BootInfo.cpp kernel/BootInfo.cpp
kernel/CPUID.cpp kernel/CPUID.cpp
kernel/Credentials.cpp kernel/Credentials.cpp
@@ -121,6 +122,7 @@ set(KERNEL_SOURCES
kernel/USB/USBManager.cpp kernel/USB/USBManager.cpp
kernel/USB/XHCI/Controller.cpp kernel/USB/XHCI/Controller.cpp
kernel/USB/XHCI/Device.cpp kernel/USB/XHCI/Device.cpp
kernel/UserCopy.cpp
icxxabi.cpp icxxabi.cpp
) )

View File

@@ -26,8 +26,6 @@ namespace Kernel
constexpr uint64_t s_page_flag_mask = 0x8000000000000FFF; constexpr uint64_t s_page_flag_mask = 0x8000000000000FFF;
constexpr uint64_t s_page_addr_mask = ~s_page_flag_mask; constexpr uint64_t s_page_addr_mask = ~s_page_flag_mask;
static bool s_is_initialized = false;
static PageTable* s_kernel = nullptr; static PageTable* s_kernel = nullptr;
static bool s_has_nxe = false; static bool s_has_nxe = false;
static bool s_has_pge = false; static bool s_has_pge = false;
@@ -260,7 +258,11 @@ namespace Kernel
ASSERT(index < 512); ASSERT(index < 512);
ASSERT(s_fast_page_pt); ASSERT(s_fast_page_pt);
ASSERT(s_fast_page_lock.current_processor_has_lock());
if (index < reserved_fast_pages)
ASSERT(s_fast_page_lock.current_processor_has_lock());
else
ASSERT(Processor::get_interrupt_state() == InterruptState::Disabled);
ASSERT(!(s_fast_page_pt[index] & Flags::Present)); ASSERT(!(s_fast_page_pt[index] & Flags::Present));
s_fast_page_pt[index] = paddr | Flags::ReadWrite | Flags::Present; s_fast_page_pt[index] = paddr | Flags::ReadWrite | Flags::Present;
@@ -274,7 +276,11 @@ namespace Kernel
{ {
ASSERT(index < 512); ASSERT(index < 512);
ASSERT(s_fast_page_pt); ASSERT(s_fast_page_pt);
ASSERT(s_fast_page_lock.current_processor_has_lock());
if (index < reserved_fast_pages)
ASSERT(s_fast_page_lock.current_processor_has_lock());
else
ASSERT(Processor::get_interrupt_state() == InterruptState::Disabled);
ASSERT((s_fast_page_pt[index] & Flags::Present)); ASSERT((s_fast_page_pt[index] & Flags::Present));
s_fast_page_pt[index] = 0; s_fast_page_pt[index] = 0;
@@ -341,32 +347,10 @@ namespace Kernel
const bool is_userspace = (vaddr < KERNEL_OFFSET); const bool is_userspace = (vaddr < KERNEL_OFFSET);
if (is_userspace && this != &PageTable::current()) if (is_userspace && this != &PageTable::current())
; ;
else if (pages <= 32 || !s_is_initialized) else if (pages >= full_tlb_flush_threshold)
{ invalidate_full_address_space(!is_userspace);
for (size_t i = 0; i < pages; i++) else for (size_t i = 0; i < pages; i++)
asm volatile("invlpg (%0)" :: "r"(vaddr + i * PAGE_SIZE)); asm volatile("invlpg (%0)" :: "r"(vaddr + i * PAGE_SIZE));
}
else if (is_userspace || !s_has_pge)
{
asm volatile("movl %0, %%cr3" :: "r"(static_cast<uint32_t>(m_highest_paging_struct)));
}
else
{
asm volatile(
"movl %%cr4, %%eax;"
"andl $~0x80, %%eax;"
"movl %%eax, %%cr4;"
"movl %0, %%cr3;"
"orl $0x80, %%eax;"
"movl %%eax, %%cr4;"
:
: "r"(static_cast<uint32_t>(m_highest_paging_struct))
: "eax"
);
}
if (send_smp_message) if (send_smp_message)
{ {
@@ -381,6 +365,34 @@ namespace Kernel
} }
} }
void PageTable::invalidate_full_address_space(bool global)
{
if (!global || !s_has_pge)
{
asm volatile(
"movl %%cr3, %%eax;"
"movl %%eax, %%cr3;"
::: "eax"
);
}
else
{
asm volatile(
"movl %%cr4, %%eax;"
"andl $~0x80, %%eax;"
"movl %%eax, %%cr4;"
"movl %%cr3, %%ecx;"
"movl %%ecx, %%cr3;"
"orl $0x80, %%eax;"
"movl %%eax, %%cr4;"
::: "eax", "ecx"
);
}
}
void PageTable::unmap_page(vaddr_t vaddr, bool invalidate) void PageTable::unmap_page(vaddr_t vaddr, bool invalidate)
{ {
ASSERT(vaddr); ASSERT(vaddr);
@@ -597,30 +609,24 @@ namespace Kernel
return true; return true;
} }
bool PageTable::reserve_page(vaddr_t vaddr, bool only_free, bool send_smp_message) void PageTable::reserve_page(vaddr_t vaddr)
{ {
SpinLockGuard _(m_lock);
ASSERT(vaddr % PAGE_SIZE == 0); ASSERT(vaddr % PAGE_SIZE == 0);
if (only_free && !is_page_free(vaddr)) SpinLockGuard _(m_lock);
return false; ASSERT(is_page_free(vaddr));
map_page_at(0, vaddr, Flags::Reserved, MemoryType::Normal, send_smp_message); map_page_at(0, vaddr, Flags::Reserved, MemoryType::Normal, false);
return true;
} }
bool PageTable::reserve_range(vaddr_t vaddr, size_t bytes, bool only_free) void PageTable::reserve_range(vaddr_t vaddr, size_t bytes)
{ {
if (size_t rem = bytes % PAGE_SIZE) if (size_t rem = bytes % PAGE_SIZE)
bytes += PAGE_SIZE - rem; bytes += PAGE_SIZE - rem;
ASSERT(vaddr % PAGE_SIZE == 0); ASSERT(vaddr % PAGE_SIZE == 0);
SpinLockGuard _(m_lock); SpinLockGuard _(m_lock);
if (only_free && !is_range_free(vaddr, bytes)) ASSERT(is_range_free(vaddr, bytes));
return false;
for (size_t offset = 0; offset < bytes; offset += PAGE_SIZE) for (size_t offset = 0; offset < bytes; offset += PAGE_SIZE)
reserve_page(vaddr + offset, true, false); reserve_page(vaddr + offset);
invalidate_range(vaddr, bytes / PAGE_SIZE, true);
return true;
} }
vaddr_t PageTable::reserve_free_page(vaddr_t first_address, vaddr_t last_address) vaddr_t PageTable::reserve_free_page(vaddr_t first_address, vaddr_t last_address)
@@ -675,7 +681,7 @@ namespace Kernel
vaddr |= (vaddr_t)pdpte << 30; vaddr |= (vaddr_t)pdpte << 30;
vaddr |= (vaddr_t)pde << 21; vaddr |= (vaddr_t)pde << 21;
vaddr |= (vaddr_t)pte << 12; vaddr |= (vaddr_t)pte << 12;
ASSERT(reserve_page(vaddr)); reserve_page(vaddr);
return vaddr; return vaddr;
} }
unmap_fast_page(2); unmap_fast_page(2);
@@ -693,7 +699,7 @@ namespace Kernel
{ {
if (is_page_free(vaddr)) if (is_page_free(vaddr))
{ {
ASSERT(reserve_page(vaddr)); reserve_page(vaddr);
return vaddr; return vaddr;
} }
} }
@@ -726,7 +732,7 @@ namespace Kernel
} }
if (valid) if (valid)
{ {
ASSERT(reserve_range(vaddr, page_count * PAGE_SIZE)); reserve_range(vaddr, page_count * PAGE_SIZE);
return vaddr; return vaddr;
} }
} }

View File

@@ -24,7 +24,6 @@ namespace Kernel
SpinLock PageTable::s_fast_page_lock; SpinLock PageTable::s_fast_page_lock;
static constexpr vaddr_t s_hhdm_offset = 0xFFFF800000000000; static constexpr vaddr_t s_hhdm_offset = 0xFFFF800000000000;
static bool s_is_initialized = false;
constexpr uint64_t s_page_flag_mask = 0x8000000000000FFF; constexpr uint64_t s_page_flag_mask = 0x8000000000000FFF;
constexpr uint64_t s_page_addr_mask = ~s_page_flag_mask; constexpr uint64_t s_page_addr_mask = ~s_page_flag_mask;
@@ -383,7 +382,11 @@ namespace Kernel
ASSERT(index < 512); ASSERT(index < 512);
ASSERT(s_fast_page_pt); ASSERT(s_fast_page_pt);
ASSERT(s_fast_page_lock.current_processor_has_lock());
if (index < reserved_fast_pages)
ASSERT(s_fast_page_lock.current_processor_has_lock());
else
ASSERT(Processor::get_interrupt_state() == InterruptState::Disabled);
ASSERT(!(s_fast_page_pt[index] & Flags::Present)); ASSERT(!(s_fast_page_pt[index] & Flags::Present));
s_fast_page_pt[index] = paddr | Flags::ReadWrite | Flags::Present; s_fast_page_pt[index] = paddr | Flags::ReadWrite | Flags::Present;
@@ -397,7 +400,11 @@ namespace Kernel
{ {
ASSERT(index < 512); ASSERT(index < 512);
ASSERT(s_fast_page_pt); ASSERT(s_fast_page_pt);
ASSERT(s_fast_page_lock.current_processor_has_lock());
if (index < reserved_fast_pages)
ASSERT(s_fast_page_lock.current_processor_has_lock());
else
ASSERT(Processor::get_interrupt_state() == InterruptState::Disabled);
ASSERT((s_fast_page_pt[index] & Flags::Present)); ASSERT((s_fast_page_pt[index] & Flags::Present));
s_fast_page_pt[index] = 0; s_fast_page_pt[index] = 0;
@@ -470,32 +477,10 @@ namespace Kernel
const bool is_userspace = (vaddr < KERNEL_OFFSET); const bool is_userspace = (vaddr < KERNEL_OFFSET);
if (is_userspace && this != &PageTable::current()) if (is_userspace && this != &PageTable::current())
; ;
else if (pages <= 32 || !s_is_initialized) else if (pages >= full_tlb_flush_threshold)
{ invalidate_full_address_space(!is_userspace);
for (size_t i = 0; i < pages; i++) else for (size_t i = 0; i < pages; i++)
asm volatile("invlpg (%0)" :: "r"(vaddr + i * PAGE_SIZE)); asm volatile("invlpg (%0)" :: "r"(vaddr + i * PAGE_SIZE));
}
else if (is_userspace || !s_has_pge)
{
asm volatile("movq %0, %%cr3" :: "r"(m_highest_paging_struct));
}
else
{
asm volatile(
"movq %%cr4, %%rax;"
"andq $~0x80, %%rax;"
"movq %%rax, %%cr4;"
"movq %0, %%cr3;"
"orq $0x80, %%rax;"
"movq %%rax, %%cr4;"
:
: "r"(m_highest_paging_struct)
: "rax"
);
}
if (send_smp_message) if (send_smp_message)
{ {
@@ -510,6 +495,34 @@ namespace Kernel
} }
} }
void PageTable::invalidate_full_address_space(bool global)
{
if (!global || !s_has_pge)
{
asm volatile(
"movq %%cr3, %%rax;"
"movq %%rax, %%cr3;"
::: "rax"
);
}
else
{
asm volatile(
"movq %%cr4, %%rax;"
"andq $~0x80, %%rax;"
"movq %%rax, %%cr4;"
"movq %%cr3, %%rcx;"
"movq %%rcx, %%cr3;"
"orq $0x80, %%rax;"
"movq %%rax, %%cr4;"
::: "rax", "rcx"
);
}
}
void PageTable::unmap_page(vaddr_t vaddr, bool invalidate) void PageTable::unmap_page(vaddr_t vaddr, bool invalidate)
{ {
ASSERT(vaddr); ASSERT(vaddr);
@@ -551,12 +564,70 @@ namespace Kernel
{ {
ASSERT(vaddr % PAGE_SIZE == 0); ASSERT(vaddr % PAGE_SIZE == 0);
const size_t page_count = range_page_count(vaddr, size); ASSERT(is_canonical(vaddr));
ASSERT(is_canonical(vaddr + size - 1));
const vaddr_t uc_vaddr_start = uncanonicalize(vaddr);
const vaddr_t uc_vaddr_end = uncanonicalize(vaddr + size - 1);
uint16_t pml4e = (uc_vaddr_start >> 39) & 0x1FF;
uint16_t pdpte = (uc_vaddr_start >> 30) & 0x1FF;
uint16_t pde = (uc_vaddr_start >> 21) & 0x1FF;
uint16_t pte = (uc_vaddr_start >> 12) & 0x1FF;
const uint16_t e_pml4e = (uc_vaddr_end >> 39) & 0x1FF;
const uint16_t e_pdpte = (uc_vaddr_end >> 30) & 0x1FF;
const uint16_t e_pde = (uc_vaddr_end >> 21) & 0x1FF;
const uint16_t e_pte = (uc_vaddr_end >> 12) & 0x1FF;
SpinLockGuard _(m_lock); SpinLockGuard _(m_lock);
for (vaddr_t page = 0; page < page_count; page++)
unmap_page(vaddr + page * PAGE_SIZE, false); uint64_t* pml4 = P2V(m_highest_paging_struct);
invalidate_range(vaddr, page_count, true); for (; pml4e <= e_pml4e; pml4e++)
{
#define UNALLOCATE_TABLE_IF_EMPTY(outer, inner) \
if (old_##inner##e == 0 && inner##e == 512) { \
unallocate_page(outer[outer##e] & s_page_addr_mask); \
outer[outer##e] = 0; \
}
if (!(pml4[pml4e] & Flags::Present))
continue;
const uint16_t old_pdpte = pdpte;
uint64_t* pdpt = P2V(pml4[pml4e] & s_page_addr_mask);
for (; pdpte < 512; pdpte++)
{
if (pml4e == e_pml4e && pdpte > e_pdpte)
break;
if (!(pdpt[pdpte] & Flags::Present))
continue;
const uint16_t old_pde = pde;
uint64_t* pd = P2V(pdpt[pdpte] & s_page_addr_mask);
for (; pde < 512; pde++)
{
if (pml4e == e_pml4e && pdpte == e_pdpte && pde > e_pde)
break;
if (!(pd[pde] & Flags::Present))
continue;
const uint16_t old_pte = pte;
uint64_t* pt = P2V(pd[pde] & s_page_addr_mask);
for (; pte < 512; pte++)
{
if (pml4e == e_pml4e && pdpte == e_pdpte && pde == e_pde && pte > e_pte)
break;
pt[pte] = 0;
}
UNALLOCATE_TABLE_IF_EMPTY(pd, pt);
pte = 0;
}
UNALLOCATE_TABLE_IF_EMPTY(pdpt, pd);
pde = 0;
}
UNALLOCATE_TABLE_IF_EMPTY(pml4, pdpt);
pdpte = 0;
#undef UNALLOCATE_TABLE_IF_EMPTY
}
invalidate_range(vaddr, range_page_count(vaddr, size), true);
} }
void PageTable::map_page_at(paddr_t paddr, vaddr_t vaddr, flags_t flags, MemoryType memory_type, bool invalidate) void PageTable::map_page_at(paddr_t paddr, vaddr_t vaddr, flags_t flags, MemoryType memory_type, bool invalidate)
@@ -746,29 +817,71 @@ namespace Kernel
return get_page_data(addr) & s_page_addr_mask; return get_page_data(addr) & s_page_addr_mask;
} }
bool PageTable::reserve_page(vaddr_t vaddr, bool only_free, bool invalidate) void PageTable::reserve_page(vaddr_t vaddr)
{ {
SpinLockGuard _(m_lock);
ASSERT(vaddr % PAGE_SIZE == 0); ASSERT(vaddr % PAGE_SIZE == 0);
if (only_free && !is_page_free(vaddr)) SpinLockGuard _(m_lock);
return false; ASSERT(is_page_free(vaddr));
map_page_at(0, vaddr, Flags::Reserved, MemoryType::Normal, invalidate); map_page_at(0, vaddr, Flags::Reserved, MemoryType::Normal, false);
return true;
} }
bool PageTable::reserve_range(vaddr_t vaddr, size_t bytes, bool only_free) void PageTable::reserve_range(vaddr_t vaddr, size_t bytes)
{ {
if (size_t rem = bytes % PAGE_SIZE) if (size_t rem = bytes % PAGE_SIZE)
bytes += PAGE_SIZE - rem; bytes += PAGE_SIZE - rem;
ASSERT(vaddr % PAGE_SIZE == 0); ASSERT(vaddr % PAGE_SIZE == 0);
ASSERT(is_canonical(vaddr));
ASSERT(is_canonical(vaddr + bytes - 1));
const vaddr_t uc_vaddr_start = uncanonicalize(vaddr);
uint16_t pml4e = (uc_vaddr_start >> 39) & 0x1FF;
uint16_t pdpte = (uc_vaddr_start >> 30) & 0x1FF;
uint16_t pde = (uc_vaddr_start >> 21) & 0x1FF;
uint16_t pte = (uc_vaddr_start >> 12) & 0x1FF;
size_t pages_to_reserve = bytes / PAGE_SIZE;
ASSERT(pages_to_reserve);
SpinLockGuard _(m_lock); SpinLockGuard _(m_lock);
if (only_free && !is_range_free(vaddr, bytes))
return false; uint64_t* pml4 = P2V(m_highest_paging_struct);
for (size_t offset = 0; offset < bytes; offset += PAGE_SIZE) for (;; pml4e++)
reserve_page(vaddr + offset, true, false); {
invalidate_range(vaddr, bytes / PAGE_SIZE, true); #define CHECK_IF_PRESENT(expr) \
return true; if (!((expr) & Flags::Present)) { \
const paddr_t paddr = allocate_zeroed_page_aligned_page(); \
ASSERT(paddr); \
(expr) = paddr | Flags::Present; \
}
CHECK_IF_PRESENT(pml4[pml4e]);
uint64_t* pdpt = P2V(pml4[pml4e] & s_page_addr_mask);
for (; pdpte < 512; pdpte++)
{
CHECK_IF_PRESENT(pdpt[pdpte]);
uint64_t* pd = P2V(pdpt[pdpte] & s_page_addr_mask);
for (; pde < 512; pde++)
{
CHECK_IF_PRESENT(pd[pde]);
uint64_t* pt = P2V(pd[pde] & s_page_addr_mask);
for (; pte < 512; pte++)
{
ASSERT(!(pt[pte] & Flags::Used));
pt[pte] = Flags::Reserved;
pages_to_reserve--;
if (pages_to_reserve == 0)
return;
}
pte = 0;
}
pde = 0;
}
pdpte = 0;
#undef CHECK_IF_PRESENT
}
ASSERT_NOT_REACHED();
} }
vaddr_t PageTable::reserve_free_page(vaddr_t first_address, vaddr_t last_address) vaddr_t PageTable::reserve_free_page(vaddr_t first_address, vaddr_t last_address)
@@ -782,6 +895,7 @@ namespace Kernel
ASSERT(is_canonical(first_address)); ASSERT(is_canonical(first_address));
ASSERT(is_canonical(last_address - 1)); ASSERT(is_canonical(last_address - 1));
const vaddr_t uc_vaddr_start = uncanonicalize(first_address); const vaddr_t uc_vaddr_start = uncanonicalize(first_address);
const vaddr_t uc_vaddr_end = uncanonicalize(last_address - 1); const vaddr_t uc_vaddr_end = uncanonicalize(last_address - 1);
@@ -831,7 +945,7 @@ namespace Kernel
vaddr |= static_cast<uint64_t>(pde) << 21; vaddr |= static_cast<uint64_t>(pde) << 21;
vaddr |= static_cast<uint64_t>(pte) << 12; vaddr |= static_cast<uint64_t>(pte) << 12;
vaddr = canonicalize(vaddr); vaddr = canonicalize(vaddr);
ASSERT(reserve_page(vaddr)); reserve_page(vaddr);
return vaddr; return vaddr;
} }
pte = 0; pte = 0;
@@ -845,7 +959,7 @@ namespace Kernel
{ {
if (vaddr_t vaddr = canonicalize(uc_vaddr); is_page_free(vaddr)) if (vaddr_t vaddr = canonicalize(uc_vaddr); is_page_free(vaddr))
{ {
ASSERT(reserve_page(vaddr)); reserve_page(vaddr);
return vaddr; return vaddr;
} }
} }
@@ -855,44 +969,90 @@ namespace Kernel
vaddr_t PageTable::reserve_free_contiguous_pages(size_t page_count, vaddr_t first_address, vaddr_t last_address) vaddr_t PageTable::reserve_free_contiguous_pages(size_t page_count, vaddr_t first_address, vaddr_t last_address)
{ {
if (first_address >= KERNEL_OFFSET && first_address < (vaddr_t)g_kernel_start) if (first_address >= KERNEL_OFFSET && first_address < reinterpret_cast<vaddr_t>(g_kernel_start))
first_address = (vaddr_t)g_kernel_start; first_address = reinterpret_cast<vaddr_t>(g_kernel_start);
if (size_t rem = first_address % PAGE_SIZE) if (const auto rem = first_address % PAGE_SIZE)
first_address += PAGE_SIZE - rem; first_address += PAGE_SIZE - rem;
if (size_t rem = last_address % PAGE_SIZE) if (const auto rem = last_address % PAGE_SIZE)
last_address -= rem; last_address -= rem;
ASSERT(is_canonical(first_address)); ASSERT(is_canonical(first_address));
ASSERT(is_canonical(last_address - 1)); ASSERT(is_canonical(last_address - 1));
const vaddr_t uc_vaddr_start = uncanonicalize(first_address);
const vaddr_t uc_vaddr_end = uncanonicalize(last_address - 1);
uint16_t pml4e = (uc_vaddr_start >> 39) & 0x1FF;
uint16_t pdpte = (uc_vaddr_start >> 30) & 0x1FF;
uint16_t pde = (uc_vaddr_start >> 21) & 0x1FF;
uint16_t pte = (uc_vaddr_start >> 12) & 0x1FF;
const uint16_t e_pml4e = (uc_vaddr_end >> 39) & 0x1FF;
const uint16_t e_pdpte = (uc_vaddr_end >> 30) & 0x1FF;
const uint16_t e_pde = (uc_vaddr_end >> 21) & 0x1FF;
const uint16_t e_pte = (uc_vaddr_end >> 12) & 0x1FF;
vaddr_t vaddr = first_address;
size_t free_count = 0;
SpinLockGuard _(m_lock); SpinLockGuard _(m_lock);
for (vaddr_t vaddr = first_address; vaddr < last_address;) const uint64_t* pml4 = P2V(m_highest_paging_struct);
for (; pml4e <= e_pml4e; pml4e++)
{ {
bool valid { true }; #define CHECK_IF_PRESENT(expr, advance) \
for (size_t page = 0; page < page_count; page++) if (!((expr) & Flags::Present)) { \
{ if ((free_count += advance) >= page_count) \
if (!is_canonical(vaddr + page * PAGE_SIZE)) goto found_free_region; \
{ continue; \
vaddr = canonicalize(uncanonicalize(vaddr + page * PAGE_SIZE));
valid = false;
break;
}
if (!is_page_free(vaddr + page * PAGE_SIZE))
{
vaddr += (page + 1) * PAGE_SIZE;
valid = false;
break;
}
} }
if (valid) CHECK_IF_PRESENT(pml4[pml4e], 512 * 512 * 512);
const uint64_t* pdpt = P2V(pml4[pml4e] & s_page_addr_mask);
for (; pdpte < 512; pdpte++)
{ {
ASSERT(reserve_range(vaddr, page_count * PAGE_SIZE)); if (pml4e == e_pml4e && pdpte > e_pdpte)
return vaddr; break;
CHECK_IF_PRESENT(pdpt[pdpte], 512 * 512);
const uint64_t* pd = P2V(pdpt[pdpte] & s_page_addr_mask);
for (; pde < 512; pde++)
{
if (pml4e == e_pml4e && pdpte == e_pdpte && pde > e_pde)
break;
CHECK_IF_PRESENT(pd[pde], 512);
uint64_t* pt = P2V(pd[pde] & s_page_addr_mask);
for (; pte < 512; pte++)
{
if (pml4e == e_pml4e && pdpte == e_pdpte && pde == e_pde && pte > e_pte)
break;
if (!(pt[pte] & Flags::Used))
{
if (++free_count >= page_count)
goto found_free_region;
}
else
{
vaddr = 0;
vaddr |= static_cast<uint64_t>(pml4e) << 39;
vaddr |= static_cast<uint64_t>(pdpte) << 30;
vaddr |= static_cast<uint64_t>(pde) << 21;
vaddr |= static_cast<uint64_t>(pte) << 12;
vaddr = canonicalize(vaddr + PAGE_SIZE);
free_count = 0;
}
}
pte = 0;
}
pde = 0;
} }
pdpte = 0;
#undef CHECK_IF_PRESENT
} }
return 0; return 0;
found_free_region:
reserve_range(vaddr, page_count * PAGE_SIZE);
return vaddr;
} }
bool PageTable::is_page_free(vaddr_t page) const bool PageTable::is_page_free(vaddr_t page) const

View File

@@ -41,7 +41,18 @@ SECTIONS
{ {
g_kernel_writable_start = .; g_kernel_writable_start = .;
*(.data) *(.data)
. = ALIGN(8);
g_drv_builtin_begin = .;
KEEP(*(.banos-driver))
g_drv_builtin_end = .;
. = ALIGN(8);
g_banos_export = .;
KEEP(*(.banos-export))
g_banos_export_end = .;
} }
.bss ALIGN(4K) : AT(ADDR(.bss) - KERNEL_OFFSET) .bss ALIGN(4K) : AT(ADDR(.bss) - KERNEL_OFFSET)
{ {
g_kernel_bss_start = .; g_kernel_bss_start = .;

View File

@@ -0,0 +1,27 @@
#pragma once
// Copyright (c) 2026 Dcraftbg
// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
// The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
// THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#include "version.h"
#include "revision.h"
#define BANOS_DRIVER_REVISION_CURRENT 0
typedef struct Banos_Driver Banos_Driver;
struct Banos_Driver {
unsigned long driver_size;
banos_version_t minimal_banos_version;
const char* name;
const char* license;
banos_version_t version;
// NOTE: checkout BANOS_DRIVER_INSTANCE_SIZE.
// You may use this instance data for anything you wish to store.
// If you need more than that just allocate it on the heap or
// globally if you add the proper verification of having your driver run only
// within a single instance
int (*init)(Banos_Driver* drv);
int (*uninit)(Banos_Driver* drv);
};
#define BANOS_DRIVER_API static __attribute__((section(".banos-driver"), used, aligned(8)))

View File

@@ -0,0 +1,15 @@
#pragma once
// Copyright (c) 2026 Dcraftbg
// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
// The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
// THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
typedef struct Banos_Symbol {
const char* name;
void* arg;
} Banos_Symbol;
#define BANOS_EXPORT_SYMBOL(symname, str, ptr) \
static __attribute__((section(".banos-export"), used, aligned(8))) Banos_Symbol __symbol_##symname = {\
.name = str, \
.arg = ptr \
};
#define BANOS_EXPORT(name) BANOS_EXPORT_SYMBOL(name, #name, (void*)&name)

View File

@@ -0,0 +1,13 @@
#pragma once
// Copyright (c) 2026 Dcraftbg
// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
// The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
// THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#ifdef __cplusplus
extern "C" {
#endif
void banos_dprintln(const char* str);
#ifdef __cplusplus
}
#endif

View File

@@ -0,0 +1,2 @@
#pragma once
typedef unsigned long banos_revision_t;

View File

@@ -0,0 +1,28 @@
#pragma once
// Copyright (c) 2026 Dcraftbg
// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
// The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
// THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
// [ 8 bit major ] [ 8 minor ] [ 16 patch]
typedef unsigned int banos_version_t;
#define BANOS_VERSION_MAJOR_SHIFT 24
#define BANOS_VERSION_MINOR_SHIFT 16
#define BANOS_VERSION_PATCH_SHIFT 0
#define BANOS_VERSION_MAJOR_MASK 0xFF
#define BANOS_VERSION_MINOR_MASK 0xFF
#define BANOS_VERSION_PATCH_MASK 0xFFFF
#define BANOS_VERSION_MAKE(major, minor, patch) \
(banos_version_t)( \
(((major) & BANOS_VERSION_MAJOR_MASK) << BANOS_VERSION_MAJOR_SHIFT) | \
(((minor) & BANOS_VERSION_MINOR_MASK) << BANOS_VERSION_MINOR_SHIFT) | \
(((patch) & BANOS_VERSION_PATCH_MASK) << BANOS_VERSION_PATCH_SHIFT) \
)
#define BANOS_VERSION_CURRENT BANOS_VERSION_MAKE(0, 0, 1)
#define BANOS_VERSION_GET_MAJOR(v) (((v) >> BANOS_VERSION_MAJOR_SHIFT) & BANOS_VERSION_MAJOR_MASK)
#define BANOS_VERSION_GET_MINOR(v) (((v) >> BANOS_VERSION_MINOR_SHIFT) & BANOS_VERSION_MINOR_MASK)
#define BANOS_VERSION_GET_PATCH(v) (((v) >> BANOS_VERSION_PATCH_SHIFT) & BANOS_VERSION_PATCH_MASK)

View File

@@ -0,0 +1,10 @@
#pragma once
#include <BAN/Vector.h>
#include <BAN/StringView.h>
typedef struct Banos_Symbol Banos_Symbol;
namespace Banos {
void* resolve_symbol(const char* name);
void import_symbols(Banos_Symbol* symbols, size_t count);
void initialize_initial_drivers(void);
BAN::ErrorOr<size_t> load_driver_from_image(const char* u_image);
}

View File

@@ -35,8 +35,6 @@ namespace Kernel
virtual bool has_error_impl() const override { return m_reading_count == 0; } virtual bool has_error_impl() const override { return m_reading_count == 0; }
virtual bool has_hungup_impl() const override { return m_writing_count == 0; } virtual bool has_hungup_impl() const override { return m_writing_count == 0; }
virtual BAN::ErrorOr<long> ioctl_impl(int, void*) override;
private: private:
Pipe(const struct stat&); Pipe(const struct stat&);

View File

@@ -61,6 +61,7 @@ namespace Kernel
{ {
public: public:
static BAN::ErrorOr<BAN::RefPtr<KeyboardDevice>> create(mode_t mode, uid_t uid, gid_t gid); static BAN::ErrorOr<BAN::RefPtr<KeyboardDevice>> create(mode_t mode, uid_t uid, gid_t gid);
static BAN::ErrorOr<void> initialize_tty_thread();
void notify(); void notify();

View File

@@ -14,6 +14,12 @@ namespace Kernel
requires BAN::is_same_v<decltype(func()), void>; requires BAN::is_same_v<decltype(func()), void>;
}; };
template<typename F>
concept with_per_cpu_fast_page_callback = requires(F func, void* addr)
{
requires BAN::is_same_v<decltype(func(addr)), void>;
};
template<typename F> template<typename F>
concept with_fast_page_callback_error = requires(F func) concept with_fast_page_callback_error = requires(F func)
{ {
@@ -45,6 +51,10 @@ namespace Kernel
WriteThrough, WriteThrough,
}; };
static constexpr bool full_tlb_flush_threshold = 32;
static constexpr size_t reserved_fast_pages = 0x10;
public: public:
static void initialize_fast_page(); static void initialize_fast_page();
static void initialize_and_load(); static void initialize_and_load();
@@ -72,6 +82,18 @@ namespace Kernel
unmap_fast_page(); unmap_fast_page();
} }
template<with_per_cpu_fast_page_callback F>
static void with_per_cpu_fast_page(paddr_t paddr, F callback)
{
const auto state = Processor::get_interrupt_state();
Processor::set_interrupt_state(InterruptState::Disabled);
const size_t index = Processor::current_index() + reserved_fast_pages;
void* addr = map_fast_page(index, paddr);
callback(addr);
unmap_fast_page(index);
Processor::set_interrupt_state(state);
}
template<with_fast_page_callback_error F> template<with_fast_page_callback_error F>
static BAN::ErrorOr<void> with_fast_page(paddr_t paddr, F callback) static BAN::ErrorOr<void> with_fast_page(paddr_t paddr, F callback)
{ {
@@ -123,8 +145,8 @@ namespace Kernel
bool is_page_free(vaddr_t) const; bool is_page_free(vaddr_t) const;
bool is_range_free(vaddr_t, size_t bytes) const; bool is_range_free(vaddr_t, size_t bytes) const;
bool reserve_page(vaddr_t, bool only_free = true, bool invalidate = true); void reserve_page(vaddr_t);
bool reserve_range(vaddr_t, size_t bytes, bool only_free = true); void reserve_range(vaddr_t, size_t bytes);
vaddr_t reserve_free_page(vaddr_t first_address, vaddr_t last_address = UINTPTR_MAX); vaddr_t reserve_free_page(vaddr_t first_address, vaddr_t last_address = UINTPTR_MAX);
vaddr_t reserve_free_contiguous_pages(size_t page_count, vaddr_t first_address, vaddr_t last_address = UINTPTR_MAX); vaddr_t reserve_free_contiguous_pages(size_t page_count, vaddr_t first_address, vaddr_t last_address = UINTPTR_MAX);
@@ -133,6 +155,7 @@ namespace Kernel
void invalidate_page(vaddr_t addr, bool send_smp_message) { invalidate_range(addr, 1, send_smp_message); } void invalidate_page(vaddr_t addr, bool send_smp_message) { invalidate_range(addr, 1, send_smp_message); }
void invalidate_range(vaddr_t addr, size_t pages, bool send_smp_message); void invalidate_range(vaddr_t addr, size_t pages, bool send_smp_message);
void invalidate_full_address_space(bool global);
InterruptState lock() const { return m_lock.lock(); } InterruptState lock() const { return m_lock.lock(); }
void unlock(InterruptState state) const { m_lock.unlock(state); } void unlock(InterruptState state) const { m_lock.unlock(state); }

View File

@@ -221,6 +221,8 @@ namespace Kernel
BAN::ErrorOr<long> sys_load_keymap(const char* path); BAN::ErrorOr<long> sys_load_keymap(const char* path);
BAN::ErrorOr<long> sys_banos_install(const char* object);
BAN::RefPtr<TTY> controlling_terminal() { return m_controlling_terminal; } BAN::RefPtr<TTY> controlling_terminal() { return m_controlling_terminal; }
static Process& current() { return Thread::current().process(); } static Process& current() { return Thread::current().process(); }
@@ -278,13 +280,12 @@ namespace Kernel
// You must hold reader end of m_mapped_region_lock when calling this. // You must hold reader end of m_mapped_region_lock when calling this.
size_t find_mapped_region(vaddr_t) const; size_t find_mapped_region(vaddr_t) const;
BAN::ErrorOr<AddressRange> find_free_address_range(size_t size);
BAN::ErrorOr<VirtualFileSystem::File> find_file(int fd, const char* path, int flags) const; BAN::ErrorOr<VirtualFileSystem::File> find_file(int fd, const char* path, int flags) const;
BAN::ErrorOr<FileParent> find_parent_file(int fd, const char* path, int flags) const; BAN::ErrorOr<FileParent> find_parent_file(int fd, const char* path, int flags) const;
BAN::ErrorOr<VirtualFileSystem::File> find_relative_parent(int fd, const char* path) const; BAN::ErrorOr<VirtualFileSystem::File> find_relative_parent(int fd, const char* path) const;
BAN::ErrorOr<void> read_from_user(const void* user_addr, void* out, size_t size);
BAN::ErrorOr<void> read_string_from_user(const char* user_addr, char* out, size_t max_size);
BAN::ErrorOr<void> write_to_user(void* user_addr, const void* in, size_t size);
BAN::ErrorOr<MemoryRegion*> validate_and_pin_pointer_access(const void*, size_t, bool needs_write); BAN::ErrorOr<MemoryRegion*> validate_and_pin_pointer_access(const void*, size_t, bool needs_write);
uint64_t signal_pending_mask() const uint64_t signal_pending_mask() const

View File

@@ -29,6 +29,13 @@ namespace Kernel
BAN_NON_MOVABLE(Processor); BAN_NON_MOVABLE(Processor);
public: public:
struct TLBEntry
{
vaddr_t vaddr;
size_t page_count;
class PageTable* page_table;
};
struct SMPMessage struct SMPMessage
{ {
enum class Type enum class Type
@@ -43,12 +50,7 @@ namespace Kernel
Type type; Type type;
union union
{ {
struct TLBEntry flush_tlb;
{
uintptr_t vaddr;
size_t page_count;
void* page_table;
} flush_tlb;
SchedulerQueue::Node* new_thread; SchedulerQueue::Node* new_thread;
SchedulerQueue::Node* unblock_thread; SchedulerQueue::Node* unblock_thread;
bool dummy; bool dummy;
@@ -130,7 +132,7 @@ namespace Kernel
static void handle_ipi(); static void handle_ipi();
static void handle_smp_messages(); static void handle_smp_messages();
static void send_smp_message(ProcessorID, const SMPMessage&, bool send_ipi = true); static bool send_smp_message(ProcessorID, const SMPMessage&, bool send_ipi = true);
static void broadcast_smp_message(const SMPMessage&); static void broadcast_smp_message(const SMPMessage&);
static void load_segments(); static void load_segments();
@@ -178,6 +180,9 @@ namespace Kernel
asm volatile("mov %[value], %%gs:%a[offset]" :: [value]"r"(value), [offset]"ir"(offset) : "memory"); asm volatile("mov %[value], %%gs:%a[offset]" :: [value]"r"(value), [offset]"ir"(offset) : "memory");
} }
void lock_tlb_lock();
void unlock_tlb_lock();
private: private:
static ProcessorID s_bsp_id; static ProcessorID s_bsp_id;
static BAN::Atomic<uint8_t> s_processor_count; static BAN::Atomic<uint8_t> s_processor_count;
@@ -211,6 +216,11 @@ namespace Kernel
BAN::Atomic<SMPMessage*> m_smp_free { nullptr }; BAN::Atomic<SMPMessage*> m_smp_free { nullptr };
SMPMessage* m_smp_message_storage { nullptr }; SMPMessage* m_smp_message_storage { nullptr };
BAN::Atomic<bool> m_tlb_lock { false };
size_t m_tlb_entry_count { 0 };
BAN::Array<TLBEntry, 32> m_tlb_entries;
bool m_tlb_global { false };
void* m_current_page_table { nullptr }; void* m_current_page_table { nullptr };
friend class BAN::Array<Processor, 0xFF>; friend class BAN::Array<Processor, 0xFF>;

View File

@@ -0,0 +1,7 @@
#pragma once
#include <BAN/Errors.h>
namespace Kernel {
BAN::ErrorOr<void> read_from_user(const void* user_addr, void* out, size_t size);
BAN::ErrorOr<void> read_string_from_user(const char* user_addr, char* out, size_t max_size);
BAN::ErrorOr<void> write_to_user(void* user_addr, const void* in, size_t size);
};

215
kernel/kernel/Banos.cpp Normal file
View File

@@ -0,0 +1,215 @@
#include <kernel/Debug.h>
#include <kernel/Banos.h>
#include <BAN/Assert.h>
#include <banos/driver.h>
#include <banos/print.h>
#include <banos/export.h>
#include <kernel/FS/VirtualFileSystem.h>
#include <kernel/Memory/PageTable.h>
#include <kernel/ELF.h>
#include <LibELF/Types.h>
#include <LibELF/Values.h>
#include <kernel/Process.h>
#include <BAN/HashMap.h>
#include <kernel/Lock/SpinLock.h>
#include <kernel/UserCopy.h>
using namespace LibELF;
using namespace Kernel;
extern "C" {
void banos_dprintln(const char* str) {
dprintln("{}", str);
}
void* banos_lookup_symbol(const char* str) {
return Banos::resolve_symbol(str);
}
}
BANOS_EXPORT(banos_dprintln);
BANOS_EXPORT(banos_lookup_symbol);
BAN::HashMap<BAN::StringView, void*> g_banos_symbols;
void* Banos::resolve_symbol(const char* name) {
auto it = g_banos_symbols.find(name);
return it == g_banos_symbols.end() ? NULL : it->value;
}
void Banos::import_symbols(Banos_Symbol* symbols, size_t count) {
for(size_t i = 0; i < count; ++i) {
auto sym = symbols + i;
MUST(g_banos_symbols.insert(sym->name, sym->arg));
}
}
// TODO: driver unloading with a reference counter
struct Driver_Instance {
Banos_Driver* drv;
};
static BAN::Vector<Driver_Instance> s_driver_instaces;
static SpinLock s_driver_instaces_lock;
extern Banos_Symbol g_banos_export[],
g_banos_export_end[];
static void load_drv(Banos_Driver* drv) {
ASSERT(drv->driver_size >= sizeof(Banos_Driver));
dprintln("Loading driver:");
dprintln(" name: {}", drv->name);
if(drv->license) dprintln(" license: {}", drv->license);
dprintln(" version: {}.{}.{}", BANOS_VERSION_GET_MAJOR(drv->version), BANOS_VERSION_GET_MINOR(drv->version), BANOS_VERSION_GET_PATCH(drv->version));
int e = drv->init(drv);
if(e < 0) dprintln(" Failed to init {} => {}", drv->name, -e);
}
BAN::ErrorOr<size_t> Banos::load_driver_from_image(const char* u_image) {
if(!Process::current().credentials().is_superuser()) return BAN::Error::from_errno(EPERM);
// TODO: permission verification. Only root should be allowed to do this
LibELF::ElfNativeFileHeader header;
const unsigned char elf_class =
#if ARCH(i686)
ELFCLASS32;
#elif ARCH(x86_64)
ELFCLASS64;
#else
# error update elf class
#endif
// TODO: is banan-os really ever gonna be running on MSB machines?
const unsigned char elf_data = ELFDATA2LSB;
// TODO: do we need to verify e_machine? I mean we do not really care.
// But I'm leaving this todo:
// Look up EM_X86_64 and EM_360|EM_860|EM_960
TRY(read_from_user(u_image, &header, sizeof header));
if( header.e_ident[EI_MAG0] != ELFMAG0 ||
header.e_ident[EI_MAG1] != ELFMAG1 ||
header.e_ident[EI_MAG2] != ELFMAG2 ||
header.e_ident[EI_MAG3] != ELFMAG3 ||
header.e_ident[EI_CLASS] != elf_class ||
header.e_ident[EI_DATA] != elf_data ||
header.e_ident[EI_VERSION] != EV_CURRENT ||
header.e_type != ET_REL ||
header.e_version != EV_CURRENT ||
header.e_ehsize != sizeof(header) ||
header.e_shentsize != sizeof(ElfNativeSectionHeader))
return BAN::Error::from_errno(EINVAL);
BAN::Vector<LibELF::ElfNativeSectionHeader> secs(header.e_shnum);
TRY(read_from_user(u_image + header.e_shoff, secs.data(), secs.size() * sizeof(*secs.data())));
auto shstr = secs[header.e_shstrndx];
size_t total_size = 0;
LibELF::ElfNativeSectionHeader *strtab = nullptr,
*symtab = nullptr,
*driver_section = nullptr;
for(auto& sec : secs) {
if(sec.sh_flags & LibELF::SHF_ALLOC) {
sec.sh_addr = total_size;
total_size += sec.sh_size;
}
if(sec.sh_name == 0) continue;
char name[256];
TRY(read_string_from_user(u_image + shstr.sh_offset + sec.sh_name, name, sizeof name));
BAN::StringView name_sv(name);
if(sec.sh_type == LibELF::SHT_SYMTAB) {
symtab = &sec;
}
// TODO: verify sh_type for both of these?
if(name_sv == ".strtab") {
strtab = &sec;
} else if(name_sv == ".banos-driver") {
driver_section = &sec;
}
}
if(!symtab || !strtab || !driver_section)
return BAN::Error::from_errno(EINVAL);
total_size += PAGE_SIZE;
total_size &= ~(PAGE_SIZE-1);
auto driver = TRY(VirtualRange::create_to_vaddr_range(PageTable::kernel(), { KERNEL_OFFSET, UINTPTR_MAX }, total_size, PageTable::Execute | PageTable::ReadWrite | PageTable::Present, true));
for(auto& sec : secs) {
if(sec.sh_flags & LibELF::SHF_ALLOC) {
sec.sh_addr += driver->vaddr();
}
}
Banos_Driver* banos_driver = reinterpret_cast<Banos_Driver*>(driver_section->sh_addr);
for(auto& sec : secs) {
if(sec.sh_name == 0) continue;
if(sec.sh_flags & LibELF::SHF_ALLOC) {
TRY(read_from_user(u_image + sec.sh_offset, reinterpret_cast<char*>(sec.sh_addr), sec.sh_size));
}
if(sec.sh_type == LibELF::SHT_RELA) {
auto& link_sec = secs[sec.sh_info];
size_t rela_count = sec.sh_size/sizeof(LibELF::ElfNativeRelocationA);
BAN::Vector<LibELF::ElfNativeRelocationA> rela_data(rela_count);
TRY(read_from_user(u_image + sec.sh_offset, rela_data.data(), rela_count * sizeof *rela_data.data()));
for(auto rela : rela_data) {
auto type = ELF64_R_TYPE(rela.r_info);
auto symbol = ELF64_R_SYM(rela.r_info);
vaddr_t value = 0;
LibELF::ElfNativeSymbol sym;
TRY(read_from_user(u_image + symtab->sh_offset + sizeof(sym) * symbol, &sym, sizeof sym));
if(sym.st_shndx) {
value = secs[sym.st_shndx].sh_addr;
} else {
char name[256];
TRY(read_string_from_user(u_image + strtab->sh_offset + sym.st_name, name, sizeof name));
value = reinterpret_cast<vaddr_t>(Banos::resolve_symbol(name));
if(!value) {
derrorln("Failed to find symbol {}", name);
return BAN::Error::from_errno(ENOENT);
}
}
vaddr_t at = link_sec.sh_addr + rela.r_offset;
size_t size = 0;
switch(type) {
case LibELF::R_X86_64_PLT32:
case LibELF::R_X86_64_PC32:
value -= at;
// fallthrough
case LibELF::R_X86_64_32:
case LibELF::R_X86_64_32S:
value += rela.r_addend;
size = sizeof(uint32_t);
break;
case LibELF::R_X86_64_64:
value += rela.r_addend;
size = sizeof(uint64_t);
break;
default:
derrorln("TODO: Unsupported relocation type {}", type);
return BAN::Error::from_errno(ENOSYS);
}
switch(size) {
case 4: *reinterpret_cast<uint32_t*>(at) = value; break;
case 8: *reinterpret_cast<uint64_t*>(at) = value; break;
}
}
}
}
Driver_Instance instance;
instance.drv = banos_driver;
load_drv(instance.drv);
SpinLockGuard _(s_driver_instaces_lock);
TRY(s_driver_instaces.push_back(instance));
// TODO: import symbols and resolve redefintions :)
return s_driver_instaces.size() - 1;
}
// NOTE: should be more than plenty ;)
extern char g_drv_builtin_begin[];
extern char g_drv_builtin_end[];
void Banos::initialize_initial_drivers(void) {
import_symbols(g_banos_export, g_banos_export_end - g_banos_export);
char* head = g_drv_builtin_begin;
while(head < g_drv_builtin_end) {
Banos_Driver* drv = (Banos_Driver*)head;
load_drv(drv);
head += drv->driver_size;
}
}

View File

@@ -230,13 +230,11 @@ namespace Kernel
if (static_cast<BAN::make_unsigned_t<decltype(offset)>>(offset) >= UINT32_MAX || buffer.size() >= UINT32_MAX || buffer.size() >= (size_t)(UINT32_MAX - offset)) if (static_cast<BAN::make_unsigned_t<decltype(offset)>>(offset) >= UINT32_MAX || buffer.size() >= UINT32_MAX || buffer.size() >= (size_t)(UINT32_MAX - offset))
return BAN::Error::from_errno(EOVERFLOW); return BAN::Error::from_errno(EOVERFLOW);
RWLockRDGuard _0(m_lock); RWLockRDGuard _(m_lock);
if (static_cast<BAN::make_unsigned_t<decltype(offset)>>(offset) >= static_cast<size_t>(m_size)) if (static_cast<BAN::make_unsigned_t<decltype(offset)>>(offset) >= static_cast<size_t>(m_size))
return 0; return 0;
ScopedSync _1(*this);
uint32_t count = buffer.size(); uint32_t count = buffer.size();
if (offset + buffer.size() > static_cast<size_t>(m_size)) if (offset + buffer.size() > static_cast<size_t>(m_size))
count = m_size - offset; count = m_size - offset;
@@ -283,7 +281,7 @@ namespace Kernel
if (static_cast<size_t>(m_size) < offset + buffer.size()) if (static_cast<size_t>(m_size) < offset + buffer.size())
TRY(truncate_impl(offset + buffer.size())); TRY(truncate_impl(offset + buffer.size()));
ScopedSync _(*this); ScopedSync _1(*this);
const uint32_t block_size = blksize(); const uint32_t block_size = blksize();

View File

@@ -5,6 +5,7 @@
#include <kernel/Memory/FileBackedRegion.h> #include <kernel/Memory/FileBackedRegion.h>
#include <fcntl.h> #include <fcntl.h>
#include <sys/ioctl.h>
#include <sys/statvfs.h> #include <sys/statvfs.h>
namespace Kernel namespace Kernel
@@ -317,7 +318,22 @@ namespace Kernel
BAN::ErrorOr<long> Inode::ioctl(int request, void* arg) BAN::ErrorOr<long> Inode::ioctl(int request, void* arg)
{ {
return ioctl_impl(request, arg); auto ret = ioctl_impl(request, arg);
if (!ret.is_error() || ret.error().get_error_code() != ENOTSUP)
return BAN::move(ret);
switch (request)
{
case TIOCGWINSZ:
case TIOCSWINSZ:
case TCGETS:
case TCSETS:
case TCSETSW:
case TCSETSF:
return BAN::Error::from_errno(EINVAL);
default:
return BAN::Error::from_errno(ENOTSUP);
}
} }
BAN::ErrorOr<void> Inode::add_epoll(class Epoll* epoll) BAN::ErrorOr<void> Inode::add_epoll(class Epoll* epoll)

View File

@@ -268,19 +268,4 @@ namespace Kernel
return BAN::Error::from_errno(ENODEV); return BAN::Error::from_errno(ENODEV);
} }
BAN::ErrorOr<long> Pipe::ioctl_impl(int cmd, void* arg)
{
switch (cmd)
{
case TIOCGWINSZ:
case TIOCSWINSZ:
case TCGETS:
case TCSETS:
case TCSETSW:
case TCSETSF:
return BAN::Error::from_errno(EINVAL);
}
return Inode::ioctl_impl(cmd, arg);
}
} }

View File

@@ -1,7 +1,10 @@
#include <BAN/CircularQueue.h>
#include <kernel/Device/DeviceNumbers.h> #include <kernel/Device/DeviceNumbers.h>
#include <kernel/FS/DevFS/FileSystem.h> #include <kernel/FS/DevFS/FileSystem.h>
#include <kernel/Input/InputDevice.h> #include <kernel/Input/InputDevice.h>
#include <kernel/Lock/SpinLockAsMutex.h> #include <kernel/Lock/SpinLockAsMutex.h>
#include <kernel/Terminal/TTY.h>
#include <LibInput/Joystick.h> #include <LibInput/Joystick.h>
#include <LibInput/KeyEvent.h> #include <LibInput/KeyEvent.h>
@@ -24,6 +27,10 @@ namespace Kernel
static SpinLock s_joystick_lock; static SpinLock s_joystick_lock;
static BAN::Vector<BAN::WeakPtr<InputDevice>> s_joysticks; static BAN::Vector<BAN::WeakPtr<InputDevice>> s_joysticks;
static SpinLock s_tty_keyboard_event_lock;
static ThreadBlocker s_tty_keyboard_event_blocker;
static BAN::CircularQueue<LibInput::RawKeyEvent, 128> s_tty_keyboard_events;
static const char* get_name_format(InputDevice::Type type) static const char* get_name_format(InputDevice::Type type)
{ {
switch (type) switch (type)
@@ -201,6 +208,15 @@ namespace Kernel
break; break;
} }
} }
if (TTY::current()->should_receive_input())
{
SpinLockGuard _(s_tty_keyboard_event_lock);
if (!s_tty_keyboard_events.full())
s_tty_keyboard_events.push(key_event);
s_tty_keyboard_event_blocker.unblock();
return;
}
} }
if (m_event_count == m_max_event_count) if (m_event_count == m_max_event_count)
@@ -261,6 +277,41 @@ namespace Kernel
} }
static void tty_keyboard_thread(void*)
{
static BAN::Atomic<bool> initialized = false;
[[maybe_unused]] bool old_initialized = initialized.exchange(true);
ASSERT(old_initialized == false);
for (;;)
{
LibInput::RawKeyEvent event;
{
SpinLockGuard guard(s_tty_keyboard_event_lock);
if (s_tty_keyboard_events.empty())
{
SpinLockGuardAsMutex smutex(guard);
s_tty_keyboard_event_blocker.block_indefinite(&smutex);
continue;
}
event = s_tty_keyboard_events.front();
s_tty_keyboard_events.pop();
}
TTY::current()->on_key_event(event);
}
}
BAN::ErrorOr<void> KeyboardDevice::initialize_tty_thread()
{
auto* thread = TRY(Thread::create_kernel(tty_keyboard_thread, nullptr));
ASSERT(thread);
TRY(Processor::scheduler().add_thread(thread));
return {};
}
BAN::ErrorOr<BAN::RefPtr<KeyboardDevice>> KeyboardDevice::create(mode_t mode, uid_t uid, gid_t gid) BAN::ErrorOr<BAN::RefPtr<KeyboardDevice>> KeyboardDevice::create(mode_t mode, uid_t uid, gid_t gid)
{ {

View File

@@ -97,8 +97,8 @@ namespace Kernel
return; return;
uint8_t page_buffer[PAGE_SIZE]; uint8_t page_buffer[PAGE_SIZE];
PageTable::with_fast_page(pages[page_index], [&] { PageTable::with_per_cpu_fast_page(pages[page_index], [&](void* addr) {
memcpy(page_buffer, PageTable::fast_page_as_ptr(), PAGE_SIZE); memcpy(page_buffer, addr, PAGE_SIZE);
}); });
const size_t write_size = BAN::Math::min<size_t>(PAGE_SIZE, inode->size() - page_index * PAGE_SIZE); const size_t write_size = BAN::Math::min<size_t>(PAGE_SIZE, inode->size() - page_index * PAGE_SIZE);
@@ -162,8 +162,8 @@ namespace Kernel
m_shared_data->pages[shared_page_index] = Heap::get().take_free_page(); m_shared_data->pages[shared_page_index] = Heap::get().take_free_page();
if (m_shared_data->pages[shared_page_index] == 0) if (m_shared_data->pages[shared_page_index] == 0)
return BAN::Error::from_errno(ENOMEM); return BAN::Error::from_errno(ENOMEM);
PageTable::with_fast_page(m_shared_data->pages[shared_page_index], [&] { PageTable::with_per_cpu_fast_page(m_shared_data->pages[shared_page_index], [&](void* addr) {
memcpy(PageTable::fast_page_as_ptr(), page_buffer, PAGE_SIZE); memcpy(addr, page_buffer, PAGE_SIZE);
}); });
shared_data_has_correct_page = true; shared_data_has_correct_page = true;
} }
@@ -181,12 +181,12 @@ namespace Kernel
return BAN::Error::from_errno(ENOMEM); return BAN::Error::from_errno(ENOMEM);
if (!shared_data_has_correct_page) if (!shared_data_has_correct_page)
{ {
PageTable::with_fast_page(m_shared_data->pages[shared_page_index], [&] { PageTable::with_per_cpu_fast_page(m_shared_data->pages[shared_page_index], [&](void* addr) {
memcpy(page_buffer, PageTable::fast_page_as_ptr(), PAGE_SIZE); memcpy(page_buffer, addr, PAGE_SIZE);
}); });
} }
PageTable::with_fast_page(paddr, [&] { PageTable::with_per_cpu_fast_page(paddr, [&](void* addr) {
memcpy(PageTable::fast_page_as_ptr(), page_buffer, PAGE_SIZE); memcpy(addr, page_buffer, PAGE_SIZE);
}); });
m_dirty_pages[local_page_index] = paddr; m_dirty_pages[local_page_index] = paddr;
m_page_table.map_page_at(paddr, vaddr, m_flags); m_page_table.map_page_at(paddr, vaddr, m_flags);
@@ -210,8 +210,8 @@ namespace Kernel
return BAN::Error::from_errno(ENOMEM); return BAN::Error::from_errno(ENOMEM);
ASSERT(&m_page_table == &PageTable::current()); ASSERT(&m_page_table == &PageTable::current());
PageTable::with_fast_page(paddr, [vaddr] { PageTable::with_per_cpu_fast_page(paddr, [vaddr](void* addr) {
memcpy(PageTable::fast_page_as_ptr(), reinterpret_cast<void*>(vaddr), PAGE_SIZE); memcpy(addr, reinterpret_cast<void*>(vaddr), PAGE_SIZE);
}); });
m_dirty_pages[local_page_index] = paddr; m_dirty_pages[local_page_index] = paddr;
@@ -240,8 +240,8 @@ namespace Kernel
return BAN::Error::from_errno(ENOMEM); return BAN::Error::from_errno(ENOMEM);
ASSERT(&m_page_table == &PageTable::current() || &m_page_table == &PageTable::kernel()); ASSERT(&m_page_table == &PageTable::current() || &m_page_table == &PageTable::kernel());
PageTable::with_fast_page(paddr, [&] { PageTable::with_per_cpu_fast_page(paddr, [&](void* addr) {
memcpy(PageTable::fast_page_as_ptr(), reinterpret_cast<void*>(vaddr), PAGE_SIZE); memcpy(addr, reinterpret_cast<void*>(vaddr), PAGE_SIZE);
}); });
result->m_page_table.map_page_at(paddr, vaddr, m_flags); result->m_page_table.map_page_at(paddr, vaddr, m_flags);

View File

@@ -64,8 +64,8 @@ namespace Kernel
return BAN::Error::from_errno(ENOMEM); return BAN::Error::from_errno(ENOMEM);
m_page_table.map_page_at(paddr, vaddr, m_flags); m_page_table.map_page_at(paddr, vaddr, m_flags);
PageTable::with_fast_page(paddr, [] { PageTable::with_per_cpu_fast_page(paddr, [](void* addr) {
memset(PageTable::fast_page_as_ptr(), 0x00, PAGE_SIZE); memset(addr, 0x00, PAGE_SIZE);
}); });
return true; return true;
@@ -93,8 +93,8 @@ namespace Kernel
m_page_table.map_page_at(paddr, vaddr, m_flags); m_page_table.map_page_at(paddr, vaddr, m_flags);
ASSERT(&m_page_table == &PageTable::current()); ASSERT(&m_page_table == &PageTable::current());
PageTable::with_fast_page(physical_page->paddr, [vaddr] { PageTable::with_per_cpu_fast_page(physical_page->paddr, [vaddr](void* addr) {
memcpy(reinterpret_cast<void*>(vaddr), PageTable::fast_page_as_ptr(), PAGE_SIZE); memcpy(reinterpret_cast<void*>(vaddr), addr, PAGE_SIZE);
}); });
if (--physical_page->ref_count == 0) if (--physical_page->ref_count == 0)
@@ -164,9 +164,9 @@ namespace Kernel
size_t written = 0; size_t written = 0;
while (written < buffer_size) while (written < buffer_size)
{ {
vaddr_t write_vaddr = m_vaddr + offset_into_region + written; const vaddr_t write_vaddr = m_vaddr + offset_into_region + written;
vaddr_t page_offset = write_vaddr % PAGE_SIZE; const vaddr_t page_offset = write_vaddr % PAGE_SIZE;
size_t bytes = BAN::Math::min<size_t>(buffer_size - written, PAGE_SIZE - page_offset); const size_t bytes = BAN::Math::min<size_t>(buffer_size - written, PAGE_SIZE - page_offset);
if (!(m_page_table.get_page_flags(write_vaddr & PAGE_ADDR_MASK) & PageTable::ReadWrite)) if (!(m_page_table.get_page_flags(write_vaddr & PAGE_ADDR_MASK) & PageTable::ReadWrite))
{ {
@@ -180,8 +180,8 @@ namespace Kernel
const paddr_t paddr = m_page_table.physical_address_of(write_vaddr & PAGE_ADDR_MASK); const paddr_t paddr = m_page_table.physical_address_of(write_vaddr & PAGE_ADDR_MASK);
ASSERT(paddr); ASSERT(paddr);
PageTable::with_fast_page(paddr, [&] { PageTable::with_per_cpu_fast_page(paddr, [&](void* addr) {
memcpy(PageTable::fast_page_as_ptr(page_offset), (void*)(buffer + written), bytes); memcpy(static_cast<uint8_t*>(addr) + page_offset, (void*)(buffer + written), bytes);
}); });
written += bytes; written += bytes;

View File

@@ -21,8 +21,8 @@ namespace Kernel
const size_t bitmap_page_count = BAN::Math::div_round_up<size_t>(m_page_count, bits_per_page); const size_t bitmap_page_count = BAN::Math::div_round_up<size_t>(m_page_count, bits_per_page);
for (size_t i = 0; i < bitmap_page_count; i++) for (size_t i = 0; i < bitmap_page_count; i++)
{ {
PageTable::with_fast_page(paddr + i * PAGE_SIZE, [] { PageTable::with_per_cpu_fast_page(paddr + i * PAGE_SIZE, [](void* addr) {
memset(PageTable::fast_page_as_ptr(), 0, PAGE_SIZE); memset(addr, 0, PAGE_SIZE);
}); });
} }
@@ -40,15 +40,15 @@ namespace Kernel
BAN::Optional<size_t> page_matched_bit; BAN::Optional<size_t> page_matched_bit;
const paddr_t current_paddr = m_paddr + i * PAGE_SIZE; const paddr_t current_paddr = m_paddr + i * PAGE_SIZE;
PageTable::with_fast_page(current_paddr, [&page_matched_bit] { PageTable::with_per_cpu_fast_page(current_paddr, [&page_matched_bit](void* addr) {
for (size_t j = 0; j < PAGE_SIZE / sizeof(size_t); j++) for (size_t j = 0; j < PAGE_SIZE / sizeof(size_t); j++)
{ {
static_assert(sizeof(size_t) == sizeof(long)); static_assert(sizeof(size_t) == sizeof(long));
const size_t current = PageTable::fast_page_as_sized<volatile size_t>(j); auto& current = static_cast<size_t*>(addr)[j];
if (current == BAN::numeric_limits<size_t>::max()) if (current == BAN::numeric_limits<size_t>::max())
continue; continue;
const int ctz = __builtin_ctzl(~current); const int ctz = __builtin_ctzl(~current);
PageTable::fast_page_as_sized<volatile size_t>(j) = current | (static_cast<size_t>(1) << ctz); current |= static_cast<size_t>(1) << ctz;
page_matched_bit = j * sizeof(size_t) * 8 + ctz; page_matched_bit = j * sizeof(size_t) * 8 + ctz;
return; return;
} }
@@ -75,15 +75,14 @@ namespace Kernel
const size_t paddr_index = (paddr - m_paddr) / PAGE_SIZE; const size_t paddr_index = (paddr - m_paddr) / PAGE_SIZE;
PageTable::with_fast_page(m_paddr + paddr_index / bits_per_page * PAGE_SIZE, [paddr_index] { PageTable::with_per_cpu_fast_page(m_paddr + paddr_index / bits_per_page * PAGE_SIZE, [paddr_index] (void* addr) {
const size_t bitmap_bit = paddr_index % bits_per_page; const size_t bitmap_bit = paddr_index % bits_per_page;
const size_t byte = bitmap_bit / 8; const size_t byte = bitmap_bit / 8;
const size_t bit = bitmap_bit % 8; const size_t bit = bitmap_bit % 8;
volatile uint8_t& bitmap_byte = PageTable::fast_page_as_sized<volatile uint8_t>(byte); uint8_t& bitmap_byte = static_cast<uint8_t*>(addr)[byte];
ASSERT(bitmap_byte & (1u << bit)); ASSERT(bitmap_byte & (1u << bit));
bitmap_byte &= ~(1u << bit);
bitmap_byte = bitmap_byte & ~(1u << bit);
}); });
m_free_pages++; m_free_pages++;
@@ -103,8 +102,8 @@ namespace Kernel
const size_t bit = bit_index % 8; const size_t bit = bit_index % 8;
uint8_t current; uint8_t current;
PageTable::with_fast_page(m_paddr + page_index * PAGE_SIZE, [&current, byte] { PageTable::with_per_cpu_fast_page(m_paddr + page_index * PAGE_SIZE, [&current, byte](void* addr) {
current = PageTable::fast_page_as_sized<volatile uint8_t>(byte); current = static_cast<uint8_t*>(addr)[byte];
}); });
return current & (1u << bit); return current & (1u << bit);
@@ -117,9 +116,9 @@ namespace Kernel
const size_t bit_index = buffer_bit % bits_per_page; const size_t bit_index = buffer_bit % bits_per_page;
const size_t byte = bit_index / 8; const size_t byte = bit_index / 8;
const size_t bit = bit_index % 8; const size_t bit = bit_index % 8;
PageTable::with_fast_page(m_paddr + page_index * PAGE_SIZE, [byte, bit] { PageTable::with_per_cpu_fast_page(m_paddr + page_index * PAGE_SIZE, [byte, bit](void* addr) {
volatile uint8_t& current = PageTable::fast_page_as_sized<volatile uint8_t>(byte); uint8_t& current = static_cast<uint8_t*>(addr)[byte];
current = current | (1u << bit); current |= 1u << bit;
}); });
}; };

View File

@@ -4,6 +4,7 @@
#include <kernel/Networking/NetworkManager.h> #include <kernel/Networking/NetworkManager.h>
#include <kernel/OpenFileDescriptorSet.h> #include <kernel/OpenFileDescriptorSet.h>
#include <kernel/Process.h> #include <kernel/Process.h>
#include <kernel/UserCopy.h>
#include <fcntl.h> #include <fcntl.h>
#include <sys/file.h> #include <sys/file.h>
@@ -254,7 +255,7 @@ namespace Kernel
if (cmd == F_SETLK || cmd == F_SETLKW || cmd == F_GETLK) if (cmd == F_SETLK || cmd == F_SETLKW || cmd == F_GETLK)
{ {
struct flock flock; struct flock flock;
TRY(Process::current().read_from_user(reinterpret_cast<void*>(extra), &flock, sizeof(struct flock))); TRY(read_from_user(reinterpret_cast<void*>(extra), &flock, sizeof(struct flock)));
flock.l_pid = Process::current().pid(); flock.l_pid = Process::current().pid();
BAN::RefPtr<Inode> inode; BAN::RefPtr<Inode> inode;
@@ -307,7 +308,7 @@ namespace Kernel
} }
if (cmd == F_GETLK) if (cmd == F_GETLK)
TRY(Process::current().write_to_user(reinterpret_cast<void*>(extra), &flock, sizeof(struct flock))); TRY(write_to_user(reinterpret_cast<void*>(extra), &flock, sizeof(struct flock)));
return 0; return 0;
} }

View File

@@ -20,7 +20,9 @@
#include <kernel/Storage/StorageDevice.h> #include <kernel/Storage/StorageDevice.h>
#include <kernel/Terminal/PseudoTerminal.h> #include <kernel/Terminal/PseudoTerminal.h>
#include <kernel/Timer/Timer.h> #include <kernel/Timer/Timer.h>
#include <kernel/UserCopy.h>
#include <kernel/Banos.h>
#include <LibELF/AuxiliaryVector.h> #include <LibELF/AuxiliaryVector.h>
#include <LibInput/KeyboardLayout.h> #include <LibInput/KeyboardLayout.h>
@@ -483,6 +485,26 @@ namespace Kernel
return l; return l;
} }
BAN::ErrorOr<AddressRange> Process::find_free_address_range(size_t size)
{
if (auto rem = size % PAGE_SIZE)
size += PAGE_SIZE - rem;
vaddr_t vaddr = m_shared_page_vaddr ? m_shared_page_vaddr + PAGE_SIZE : 0x400000;
for (size_t i = find_mapped_region(vaddr); i < m_mapped_regions.size(); i++)
{
const auto& region = m_mapped_regions[i];
if (!region->overlaps(vaddr, size))
return AddressRange { vaddr, vaddr + size };
vaddr = region->vaddr() + region->size();
if (auto rem = vaddr % PAGE_SIZE)
vaddr += PAGE_SIZE - rem;
}
return BAN::Error::from_errno(ENOMEM);
}
size_t Process::proc_meminfo(off_t offset, BAN::ByteSpan buffer) const size_t Process::proc_meminfo(off_t offset, BAN::ByteSpan buffer) const
{ {
ASSERT(offset >= 0); ASSERT(offset >= 0);
@@ -2470,7 +2492,7 @@ namespace Kernel
RWLockWRGuard _(m_memory_region_lock); RWLockWRGuard _(m_memory_region_lock);
AddressRange address_range { .start = 0x400000, .end = USERSPACE_END }; AddressRange address_range;
if (args.flags & (MAP_FIXED | MAP_FIXED_NOREPLACE)) if (args.flags & (MAP_FIXED | MAP_FIXED_NOREPLACE))
{ {
const vaddr_t vaddr = reinterpret_cast<vaddr_t>(args.addr); const vaddr_t vaddr = reinterpret_cast<vaddr_t>(args.addr);
@@ -2509,22 +2531,30 @@ namespace Kernel
} }
} }
} }
else if (const vaddr_t vaddr = reinterpret_cast<vaddr_t>(args.addr); vaddr == 0)
;
else if (vaddr % PAGE_SIZE)
;
else if (!PageTable::is_valid_pointer(vaddr))
;
else if (!PageTable::is_valid_pointer(vaddr + args.len))
;
else if (!page_table().is_range_free(vaddr, args.len))
;
else else
{ {
address_range = { bool use_address_hint = false;
.start = vaddr,
.end = vaddr + args.len, const vaddr_t vaddr = reinterpret_cast<vaddr_t>(args.addr);
}; if (vaddr == 0 || vaddr % PAGE_SIZE)
;
else if (!PageTable::is_valid_pointer(vaddr))
;
else if (!PageTable::is_valid_pointer(vaddr + args.len))
;
else if (!page_table().is_range_free(vaddr, args.len))
;
else
{
use_address_hint = true;
address_range = {
.start = vaddr,
.end = vaddr + args.len,
};
}
if (!use_address_hint)
address_range = TRY(find_free_address_range(args.len));
} }
if (args.flags & MAP_ANONYMOUS) if (args.flags & MAP_ANONYMOUS)
@@ -3857,47 +3887,6 @@ namespace Kernel
return region->allocate_page_containing(address, wants_write); return region->allocate_page_containing(address, wants_write);
} }
extern "C" bool safe_user_memcpy(void*, const void*, size_t);
extern "C" bool safe_user_strncpy(void*, const void*, size_t);
static inline bool is_valid_user_address(const void* user_addr, size_t size)
{
const vaddr_t user_vaddr = reinterpret_cast<vaddr_t>(user_addr);
if (BAN::Math::will_addition_overflow<vaddr_t>(user_vaddr, size))
return false;
if (user_vaddr + size > USERSPACE_END)
return false;
return true;
}
BAN::ErrorOr<void> Process::read_from_user(const void* user_addr, void* out, size_t size)
{
if (!is_valid_user_address(user_addr, size))
return BAN::Error::from_errno(EFAULT);
if (!safe_user_memcpy(out, user_addr, size))
return BAN::Error::from_errno(EFAULT);
return {};
}
BAN::ErrorOr<void> Process::read_string_from_user(const char* user_addr, char* out, size_t max_size)
{
max_size = BAN::Math::min<size_t>(max_size, USERSPACE_END - reinterpret_cast<vaddr_t>(user_addr));
if (!is_valid_user_address(user_addr, max_size))
return BAN::Error::from_errno(EFAULT);
if (!safe_user_strncpy(out, user_addr, max_size))
return BAN::Error::from_errno(EFAULT);
return {};
}
BAN::ErrorOr<void> Process::write_to_user(void* user_addr, const void* in, size_t size)
{
if (!is_valid_user_address(user_addr, size))
return BAN::Error::from_errno(EFAULT);
if (!safe_user_memcpy(user_addr, in, size))
return BAN::Error::from_errno(EFAULT);
return {};
}
BAN::ErrorOr<MemoryRegion*> Process::validate_and_pin_pointer_access(const void* ptr, size_t size, bool needs_write) BAN::ErrorOr<MemoryRegion*> Process::validate_and_pin_pointer_access(const void* ptr, size_t size, bool needs_write)
{ {
// TODO: allow pinning multiple regions? // TODO: allow pinning multiple regions?
@@ -3962,4 +3951,7 @@ namespace Kernel
return BAN::Error::from_errno(EFAULT); return BAN::Error::from_errno(EFAULT);
} }
BAN::ErrorOr<long> Process::sys_banos_install(const char* u_image) {
return TRY(Banos::load_driver_from_image(u_image));
}
} }

View File

@@ -351,6 +351,51 @@ namespace Kernel
handle_smp_messages(); handle_smp_messages();
} }
void Processor::load_segments()
{
load_fsbase();
load_gsbase();
}
void Processor::load_fsbase()
{
const auto addr = scheduler().current_thread().get_fsbase();
#if ARCH(x86_64)
const uint32_t addr_hi = addr >> 32;
const uint32_t addr_lo = addr & 0xFFFFFFFF;
asm volatile("wrmsr" :: "d"(addr_hi), "a"(addr_lo), "c"(MSR_IA32_FS_BASE));
#elif ARCH(i686)
gdt().set_fsbase(addr);
#endif
}
void Processor::load_gsbase()
{
const auto addr = scheduler().current_thread().get_gsbase();
#if ARCH(x86_64)
const uint32_t addr_hi = addr >> 32;
const uint32_t addr_lo = addr & 0xFFFFFFFF;
asm volatile("wrmsr" :: "d"(addr_hi), "a"(addr_lo), "c"(MSR_IA32_KERNEL_GS_BASE));
#elif ARCH(i686)
gdt().set_gsbase(addr);
#endif
}
void Processor::lock_tlb_lock()
{
bool expected = false;
while (!m_tlb_lock.compare_exchange(expected, true, BAN::MemoryOrder::memory_order_acquire))
{
__builtin_ia32_pause();
expected = false;
}
}
void Processor::unlock_tlb_lock()
{
m_tlb_lock.store(false, BAN::MemoryOrder::memory_order_release);
}
void Processor::handle_smp_messages() void Processor::handle_smp_messages()
{ {
auto state = get_interrupt_state(); auto state = get_interrupt_state();
@@ -386,10 +431,7 @@ namespace Kernel
switch (message->type) switch (message->type)
{ {
case SMPMessage::Type::FlushTLB: case SMPMessage::Type::FlushTLB:
if (message->flush_tlb.page_table && message->flush_tlb.page_table != processor.m_current_page_table) ASSERT_NOT_REACHED();
break;
PageTable::current().invalidate_range(message->flush_tlb.vaddr, message->flush_tlb.page_count, false);
break;
case SMPMessage::Type::NewThread: case SMPMessage::Type::NewThread:
processor.m_scheduler->add_thread(message->new_thread); processor.m_scheduler->add_thread(message->new_thread);
break; break;
@@ -420,46 +462,64 @@ namespace Kernel
last_handled->next = processor.m_smp_free; last_handled->next = processor.m_smp_free;
} }
{
processor.lock_tlb_lock();
const size_t tlb_entry_count = processor.m_tlb_entry_count;
const auto tlb_entries = processor.m_tlb_entries;
const bool tlb_global = processor.m_tlb_global;
processor.m_tlb_entry_count = 0;
processor.m_tlb_global = false;
processor.unlock_tlb_lock();
auto& page_table = PageTable::current();
size_t pages = 0;
for (size_t i = 0; i < tlb_entry_count; i++)
if (tlb_entries[i].page_table == nullptr || tlb_entries[i].page_table == &page_table)
pages += tlb_entries[i].page_count;
if (pages >= PageTable::full_tlb_flush_threshold || tlb_entry_count >= processor.m_tlb_entries.size())
page_table.invalidate_full_address_space(tlb_global);
else for (size_t i = 0; i < tlb_entry_count; i++)
if (tlb_entries[i].page_table == nullptr || tlb_entries[i].page_table == &page_table)
page_table.invalidate_range(tlb_entries[i].vaddr, tlb_entries[i].page_count, false);
}
set_interrupt_state(state); set_interrupt_state(state);
} }
void Processor::load_segments() bool Processor::send_smp_message(ProcessorID processor_id, const SMPMessage& message, bool send_ipi)
{
load_fsbase();
load_gsbase();
}
void Processor::load_fsbase()
{
const auto addr = scheduler().current_thread().get_fsbase();
#if ARCH(x86_64)
const uint32_t addr_hi = addr >> 32;
const uint32_t addr_lo = addr & 0xFFFFFFFF;
asm volatile("wrmsr" :: "d"(addr_hi), "a"(addr_lo), "c"(MSR_IA32_FS_BASE));
#elif ARCH(i686)
gdt().set_fsbase(addr);
#endif
}
void Processor::load_gsbase()
{
const auto addr = scheduler().current_thread().get_gsbase();
#if ARCH(x86_64)
const uint32_t addr_hi = addr >> 32;
const uint32_t addr_lo = addr & 0xFFFFFFFF;
asm volatile("wrmsr" :: "d"(addr_hi), "a"(addr_lo), "c"(MSR_IA32_KERNEL_GS_BASE));
#elif ARCH(i686)
gdt().set_gsbase(addr);
#endif
}
void Processor::send_smp_message(ProcessorID processor_id, const SMPMessage& message, bool send_ipi)
{ {
auto state = get_interrupt_state(); auto state = get_interrupt_state();
set_interrupt_state(InterruptState::Disabled); set_interrupt_state(InterruptState::Disabled);
auto& processor = s_processors[processor_id.m_id]; auto& processor = s_processors[processor_id.m_id];
if (message.type == SMPMessage::Type::FlushTLB)
{
processor.lock_tlb_lock();
const bool is_first_entry = (processor.m_tlb_entry_count == 0);
const auto& tlb_msg = message.flush_tlb;
processor.m_tlb_global |= (tlb_msg.page_table == nullptr);
if (processor.m_tlb_entry_count < processor.m_tlb_entries.size())
{
processor.m_tlb_entries[processor.m_tlb_entry_count++] = {
.vaddr = tlb_msg.vaddr,
.page_count = tlb_msg.page_count,
.page_table = static_cast<PageTable*>(tlb_msg.page_table),
};
}
processor.unlock_tlb_lock();
set_interrupt_state(state);
return is_first_entry;
}
// find a slot for message // find a slot for message
auto* storage = processor.m_smp_free.exchange(nullptr); auto* storage = processor.m_smp_free.exchange(nullptr);
while (storage == nullptr) while (storage == nullptr)
@@ -499,15 +559,19 @@ namespace Kernel
storage->next = processor.m_smp_pending; storage->next = processor.m_smp_pending;
} }
const bool needs_ipi = (storage->next == nullptr);
if (send_ipi) if (send_ipi)
{ {
if (processor_id == current_id()) if (processor_id == current_id())
handle_smp_messages(); handle_smp_messages();
else else if (needs_ipi)
InterruptController::get().send_ipi(processor_id); InterruptController::get().send_ipi(processor_id);
} }
set_interrupt_state(state); set_interrupt_state(state);
return needs_ipi;
} }
void Processor::broadcast_smp_message(const SMPMessage& message) void Processor::broadcast_smp_message(const SMPMessage& message)
@@ -518,15 +582,18 @@ namespace Kernel
const auto state = get_interrupt_state(); const auto state = get_interrupt_state();
set_interrupt_state(InterruptState::Disabled); set_interrupt_state(InterruptState::Disabled);
bool needs_ipi = false;
const auto current_id = Processor::current_id(); const auto current_id = Processor::current_id();
for (size_t i = 0; i < Processor::count(); i++) for (size_t i = 0; i < Processor::count(); i++)
{ {
const auto processor_id = s_processor_ids[i]; const auto processor_id = s_processor_ids[i];
if (processor_id != current_id) if (processor_id != current_id)
send_smp_message(processor_id, message, false); needs_ipi |= send_smp_message(processor_id, message, false);
} }
InterruptController::get().broadcast_ipi(); if (needs_ipi)
InterruptController::get().broadcast_ipi();
set_interrupt_state(state); set_interrupt_state(state);
} }

View File

@@ -66,8 +66,8 @@ namespace Kernel
if (!(cache.sector_mask & (1 << page_cache_offset))) if (!(cache.sector_mask & (1 << page_cache_offset)))
return false; return false;
PageTable::with_fast_page(cache.paddr, [&] { PageTable::with_per_cpu_fast_page(cache.paddr, [&](void* addr) {
memcpy(buffer.data(), PageTable::fast_page_as_ptr(page_cache_offset * m_sector_size), m_sector_size); memcpy(buffer.data(), static_cast<uint8_t*>(addr) + page_cache_offset * m_sector_size, m_sector_size);
}); });
return true; return true;
@@ -107,8 +107,8 @@ namespace Kernel
auto& cache = m_cache[index]; auto& cache = m_cache[index];
PageTable::with_fast_page(cache.paddr, [&] { PageTable::with_per_cpu_fast_page(cache.paddr, [&](void* addr) {
memcpy(PageTable::fast_page_as_ptr(page_cache_offset * m_sector_size), buffer.data(), m_sector_size); memcpy(static_cast<uint8_t*>(addr) + page_cache_offset * m_sector_size, buffer.data(), m_sector_size);
}); });
cache.sector_mask |= 1 << page_cache_offset; cache.sector_mask |= 1 << page_cache_offset;
@@ -133,8 +133,8 @@ namespace Kernel
if (cache.dirty_mask == 0) if (cache.dirty_mask == 0)
return {}; return {};
PageTable::with_fast_page(cache.paddr, [&] { PageTable::with_per_cpu_fast_page(cache.paddr, [&](void* addr) {
memcpy(m_sync_cache.data(), PageTable::fast_page_as_ptr(), PAGE_SIZE); memcpy(m_sync_cache.data(), addr, PAGE_SIZE);
}); });
temp_cache = cache; temp_cache = cache;

View File

@@ -5,6 +5,7 @@
#include <kernel/Device/DeviceNumbers.h> #include <kernel/Device/DeviceNumbers.h>
#include <kernel/FS/DevFS/FileSystem.h> #include <kernel/FS/DevFS/FileSystem.h>
#include <kernel/FS/VirtualFileSystem.h> #include <kernel/FS/VirtualFileSystem.h>
#include <kernel/Input/InputDevice.h>
#include <kernel/Lock/LockGuard.h> #include <kernel/Lock/LockGuard.h>
#include <kernel/Lock/SpinLockAsMutex.h> #include <kernel/Lock/SpinLockAsMutex.h>
#include <kernel/Process.h> #include <kernel/Process.h>
@@ -117,40 +118,9 @@ namespace Kernel
return {}; return {};
} }
void TTY::keyboard_task(void*)
{
BAN::RefPtr<Inode> keyboard_inode;
if (auto ret = DevFileSystem::get().root_inode()->find_inode("keyboard"_sv); !ret.is_error())
keyboard_inode = ret.release_value();
else
{
dprintln("could not open keyboard device: {}", ret.error());
return;
}
while (true)
{
while (TTY::current()->m_tty_ctrl.receive_input)
{
if (!keyboard_inode->can_read())
{
SystemTimer::get().sleep_ms(1);
continue;
}
LibInput::RawKeyEvent event;
[[maybe_unused]] const size_t read = MUST(keyboard_inode->read(0, BAN::ByteSpan::from(event)));
ASSERT(read == sizeof(event));
TTY::current()->on_key_event(LibInput::KeyboardLayout::get().key_event_from_raw(event));
}
}
}
void TTY::initialize_devices() void TTY::initialize_devices()
{ {
auto* thread = MUST(Thread::create_kernel(&TTY::keyboard_task, nullptr)); MUST(KeyboardDevice::initialize_tty_thread());
MUST(Processor::scheduler().add_thread(thread));
DevFileSystem::get().add_inode("tty", MUST(DevTTY::create(0666, 0, 0))); DevFileSystem::get().add_inode("tty", MUST(DevTTY::create(0666, 0, 0)));
} }
@@ -268,7 +238,7 @@ namespace Kernel
if (ch == _POSIX_VDISABLE) if (ch == _POSIX_VDISABLE)
return; return;
LockGuard _0(m_mutex); LockGuard _(m_mutex);
const auto termios = get_termios(); const auto termios = get_termios();

View File

@@ -9,6 +9,7 @@
#include <kernel/Scheduler.h> #include <kernel/Scheduler.h>
#include <kernel/Thread.h> #include <kernel/Thread.h>
#include <kernel/Timer/Timer.h> #include <kernel/Timer/Timer.h>
#include <kernel/UserCopy.h>
namespace Kernel namespace Kernel
{ {
@@ -727,7 +728,7 @@ namespace Kernel
{ {
static_assert(sizeof(T) >= sizeof(uintptr_t)); static_assert(sizeof(T) >= sizeof(uintptr_t));
sp -= sizeof(T); sp -= sizeof(T);
if (m_process->write_to_user(reinterpret_cast<void*>(sp), &value, sizeof(T)).is_error()) if (write_to_user(reinterpret_cast<void*>(sp), &value, sizeof(T)).is_error())
m_process->exit(128 + SIGSEGV, SIGSEGV | 0x80); m_process->exit(128 + SIGSEGV, SIGSEGV | 0x80);
}; };

View File

@@ -0,0 +1,40 @@
#include <kernel/UserCopy.h>
extern "C" bool safe_user_memcpy(void*, const void*, size_t);
extern "C" bool safe_user_strncpy(void*, const void*, size_t);
static inline bool is_valid_user_address(const void* user_addr, size_t size)
{
const vaddr_t user_vaddr = reinterpret_cast<vaddr_t>(user_addr);
if (BAN::Math::will_addition_overflow<vaddr_t>(user_vaddr, size))
return false;
if (user_vaddr + size > USERSPACE_END)
return false;
return true;
}
BAN::ErrorOr<void> Kernel::read_from_user(const void* user_addr, void* out, size_t size)
{
if (!is_valid_user_address(user_addr, size))
return BAN::Error::from_errno(EFAULT);
if (!safe_user_memcpy(out, user_addr, size))
return BAN::Error::from_errno(EFAULT);
return {};
}
BAN::ErrorOr<void> Kernel::read_string_from_user(const char* user_addr, char* out, size_t max_size)
{
max_size = BAN::Math::min<size_t>(max_size, USERSPACE_END - reinterpret_cast<vaddr_t>(user_addr));
if (!is_valid_user_address(user_addr, max_size))
return BAN::Error::from_errno(EFAULT);
if (!safe_user_strncpy(out, user_addr, max_size))
return BAN::Error::from_errno(EFAULT);
return {};
}
BAN::ErrorOr<void> Kernel::write_to_user(void* user_addr, const void* in, size_t size)
{
if (!is_valid_user_address(user_addr, size))
return BAN::Error::from_errno(EFAULT);
if (!safe_user_memcpy(user_addr, in, size))
return BAN::Error::from_errno(EFAULT);
return {};
}

View File

@@ -27,6 +27,7 @@
#include <kernel/Terminal/VirtualTTY.h> #include <kernel/Terminal/VirtualTTY.h>
#include <kernel/Timer/Timer.h> #include <kernel/Timer/Timer.h>
#include <kernel/USB/USBManager.h> #include <kernel/USB/USBManager.h>
#include <kernel/Banos.h>
#include <LibInput/KeyboardLayout.h> #include <LibInput/KeyboardLayout.h>
@@ -261,6 +262,8 @@ static void init2(void*)
TTY::initialize_devices(); TTY::initialize_devices();
Banos::initialize_initial_drivers();
auto console_path = MUST(BAN::String::formatted("/dev/{}", cmdline.console)); auto console_path = MUST(BAN::String::formatted("/dev/{}", cmdline.console));
auto console_path_sv = console_path.sv(); auto console_path_sv = console_path.sv();
MUST(Process::create_userspace({ 0, 0, 0, 0 }, "/usr/bin/init"_sv, BAN::Span<BAN::StringView>(&console_path_sv, 1))); MUST(Process::create_userspace({ 0, 0, 0, 0 }, "/usr/bin/init"_sv, BAN::Span<BAN::StringView>(&console_path_sv, 1)));

View File

@@ -117,6 +117,7 @@ __BEGIN_DECLS
O(SYS_SETGROUPS, setgroups) \ O(SYS_SETGROUPS, setgroups) \
O(SYS_CHROOT, chroot) \ O(SYS_CHROOT, chroot) \
O(SYS_EVENTFD, eventfd) \ O(SYS_EVENTFD, eventfd) \
O(SYS_BANOS_INSTALL, banos_install) \
enum Syscall enum Syscall
{ {

View File

@@ -809,7 +809,7 @@ static qsort_pair qsort_partition(uint8_t* pbegin, uint8_t* pend, size_t width,
while (eq < gt) while (eq < gt)
{ {
const int comp = compar(eq, pivot); const int comp = (eq == pivot) ? 0 : compar(eq, pivot);
if (comp < 0) if (comp < 0)
{ {

View File

@@ -57,6 +57,7 @@ set(USERSPACE_PROGRAMS
whoami whoami
WindowServer WindowServer
yes yes
driver-install
) )
foreach(project ${USERSPACE_PROGRAMS}) foreach(project ${USERSPACE_PROGRAMS})

View File

@@ -700,50 +700,16 @@ static void relocate_elf(LoadedElf& elf, bool lazy_load)
} }
else else
{ {
const size_t pltrelent = (elf.pltrel == DT_REL) switch (elf.pltrel)
? sizeof(ElfNativeRelocation)
: sizeof(ElfNativeRelocationA);
for (size_t i = 0; i < elf.pltrelsz / pltrelent; i++)
{ {
const auto info = (elf.pltrel == DT_REL) case DT_REL:
? reinterpret_cast<ElfNativeRelocation*>(elf.jmprel)[i].r_info for (size_t i = 0; i < elf.pltrelsz / sizeof(ElfNativeRelocation); i++)
: reinterpret_cast<ElfNativeRelocationA*>(elf.jmprel)[i].r_info; *reinterpret_cast<uintptr_t*>(elf.base + reinterpret_cast<ElfNativeRelocation*>(elf.jmprel)[i].r_offset) += elf.base;
const auto offset = (elf.pltrel == DT_REL) break;
? reinterpret_cast<ElfNativeRelocation*>(elf.jmprel)[i].r_offset case DT_RELA:
: reinterpret_cast<ElfNativeRelocationA*>(elf.jmprel)[i].r_offset; for (size_t i = 0; i < elf.pltrelsz / sizeof(ElfNativeRelocationA); i++)
*reinterpret_cast<uintptr_t*>(elf.base + reinterpret_cast<ElfNativeRelocationA*>(elf.jmprel)[i].r_offset) += elf.base;
#if defined(__x86_64__) break;
if (ELF64_R_TYPE(info) != R_X86_64_JUMP_SLOT)
print_error_and_exit("jmprel relocation not R_X86_64_JUMP_SLOT", 0);
#elif defined(__i686__)
if (ELF32_R_TYPE(info) != R_386_JMP_SLOT)
print_error_and_exit("jmprel relocation not R_386_JMP_SLOT", 0);
#else
#error "unsupported architecture"
#endif
bool do_relocation = false;
if (const uint32_t symbol_index = ELF_R_SYM(info))
{
const auto& symbol = *reinterpret_cast<ElfNativeSymbol*>(elf.symtab + symbol_index * elf.syment);
const char* symbol_name = reinterpret_cast<const char*>(elf.strtab + symbol.st_name);
if (strcmp(symbol_name, "__tls_get_addr") == 0 || strcmp(symbol_name, "___tls_get_addr") == 0)
do_relocation = true;
}
if (!do_relocation)
*reinterpret_cast<uintptr_t*>(elf.base + offset) += elf.base;
else switch (elf.pltrel)
{
case DT_REL:
handle_relocation(elf, reinterpret_cast<ElfNativeRelocation*>(elf.jmprel)[i], true);
break;
case DT_RELA:
handle_relocation(elf, reinterpret_cast<ElfNativeRelocationA*>(elf.jmprel)[i], true);
break;
}
} }
} }
} }
@@ -1279,6 +1245,28 @@ static MasterTLS initialize_master_tls()
module_count++; module_count++;
} }
{
const sys_mmap_t mmap_args {
.addr = nullptr,
.len = sizeof(_dynamic_tls_t) + s_max_loaded_files * sizeof(_dynamic_tls_entry_t),
.prot = PROT_READ | PROT_WRITE,
.flags = MAP_ANONYMOUS | MAP_PRIVATE,
.fildes = -1,
.off = 0,
};
const auto ret = syscall(SYS_MMAP, &mmap_args);
if (ret < 0)
print_error_and_exit("failed to allocate dynamic TLS", ret);
s_dynamic_tls = reinterpret_cast<_dynamic_tls_t*>(ret);
*s_dynamic_tls = {
.lock = 0,
.entry_count = 0,
.entries = reinterpret_cast<_dynamic_tls_entry_t*>(s_dynamic_tls + 1),
};
}
if (module_count == 0) if (module_count == 0)
return { .addr = nullptr, .size = 0, .module_count = 0 }; return { .addr = nullptr, .size = 0, .module_count = 0 };
@@ -1329,28 +1317,6 @@ static MasterTLS initialize_master_tls()
elf.tls_offset = tls_offset; elf.tls_offset = tls_offset;
} }
{
const sys_mmap_t mmap_args {
.addr = nullptr,
.len = sizeof(_dynamic_tls_t) + s_max_loaded_files * sizeof(_dynamic_tls_entry_t),
.prot = PROT_READ | PROT_WRITE,
.flags = MAP_ANONYMOUS | MAP_PRIVATE,
.fildes = -1,
.off = 0,
};
const auto ret = syscall(SYS_MMAP, &mmap_args);
if (ret < 0)
print_error_and_exit("failed to allocate dynamic TLS", ret);
s_dynamic_tls = reinterpret_cast<_dynamic_tls_t*>(ret);
*s_dynamic_tls = {
.lock = 0,
.entry_count = 0,
.entries = reinterpret_cast<_dynamic_tls_entry_t*>(s_dynamic_tls + 1),
};
}
return { return {
.addr = master_tls_addr, .addr = master_tls_addr,
.size = master_tls_size, .size = master_tls_size,

View File

@@ -0,0 +1,9 @@
set(SOURCES
main.cpp
)
add_executable(driver-install ${SOURCES})
banan_link_library(driver-install ban)
banan_link_library(driver-install libc)
install(TARGETS driver-install OPTIONAL)

View File

@@ -0,0 +1,93 @@
#include <stdio.h>
#include <assert.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/syscall.h>
char* g_exe;
char* shift_args(int *argc, char ***argv) {
return (assert((*argc) > 0), ((*argc)--, *((*argv)++)));
}
void help(FILE* sink) {
fprintf(sink, "%s <input filename>\n", g_exe);
}
#define eprintf(...) fprintf(stderr, __VA_ARGS__)
#define eprintfln(fmt, ...) eprintf(fmt "\n", ## __VA_ARGS__)
char* read_entire_file(const char* path) {
char* result = NULL;
char* head = NULL;
char* end = NULL;
size_t buf_size = 0;
long at = 0;
FILE *f = fopen(path, "rb");
if(!f) {
fprintf(stderr, "ERROR Could not open file %s: %s\n",path,strerror(errno));
return NULL;
}
if(fseek(f, 0, SEEK_END) != 0) {
fprintf(stderr, "ERROR Could not fseek on file %s: %s\n",path,strerror(errno));
result = NULL; goto DEFER;
}
at = ftell(f);
if(at == -1L) {
fprintf(stderr, "ERROR Could not ftell on file %s: %s\n",path,strerror(errno));
result = NULL; goto DEFER;
}
buf_size = at+1;
rewind(f);
result = (char*)malloc(buf_size);
assert(result && "Ran out of memory");
head = result;
end = result+buf_size-1;
while(head != end) {
head += fread(head, 1, end-head, f);
if(ferror(f)) {
fprintf(stderr, "ERROR Could not fread on file %s: %s\n",path,strerror(errno));
free(result);
result = NULL; goto DEFER;
}
}
result[buf_size-1] = '\0';
DEFER:
fclose(f);
return result;
}
static int banos_install(const void* driver_image) {
return syscall(SYS_BANOS_INSTALL, driver_image);
}
int main(int argc, char** argv)
{
g_exe = shift_args(&argc, &argv);
char* input_filename = NULL;
while(argc > 0) {
char* arg = shift_args(&argc, &argv);
if(!input_filename) input_filename = arg;
else {
eprintfln("ERROR: Unexpected argument `%s'", arg);
help(stderr);
return 1;
}
}
if(!input_filename) {
eprintfln("ERROR: Missing input filename!");
help(stderr);
return 1;
}
char* data = read_entire_file(input_filename);
if(!data) return 1;
int id = banos_install(data);
if(id == -1) {
eprintfln("ERROR: Failed to install driver `%s': %s", input_filename, strerror(errno));
return 1;
}
printf("%d\n", id);
return 0;
}