Compare commits

...

17 Commits

Author SHA1 Message Date
Bananymous 5d62fa3f10 Kernel: Clenup stacktrace printing on exception
Start from current ip and bp. This removes kernel call stack to debug
printing function from the stack trace
2026-01-16 16:31:35 +02:00
Bananymous d3df00f0ba Kernel: Make Processor structure default to zero
This moves processor info to bss instead of having it in data section
2026-01-16 16:24:47 +02:00
Bananymous 34e84f8b07 Kernel: Reduce the number of TLB invalidations
Invalidations are not done if mapping or unmapping previously unmapped
page. TLB invalidate IPIs are now ignored if they don't affect the
currently mapped address space
2026-01-16 16:22:29 +02:00
Bananymous 1143dc3cae Kernel: Rework syscall memory validation and locking
Process's memory regions are now behind an rwlock instead of using the
full process lock. This allows most pointer validations to not block as
write operations to memory regions are rare.

Thread's userspace stack is now part of process's memory regions. This
simplifies code that explicitly looped over threads to see if the
accessed address was inside a thread's stack.

Only drawback of this is that MemoryRegions don't support guard pages,
so userspace stackoverflow will be handeled as cleanly as it was prior
to this.

This patch also fixes some unnecessary locking of the process lock and
moves locking to the internal helper functions instead of asserting that
the lock is held. Also we now make sure loaded ELF regions are in sorted
order as we previously expected.
2026-01-16 16:09:38 +02:00
Bananymous 0299d4d44e Kernel/LibC: remove SYS_TERMID
This syscall is not needed. /dev/tty is already a symlink to the
controlling terminal. Also this syscall did not handle pseudo terminals
2026-01-16 15:57:36 +02:00
Bananymous 1d07151743 ports/xash-fwgs: Cleanup patches
Remove patches that are no longer needed and cleanup the general support
patch
2026-01-13 20:51:58 +02:00
Bananymous a83fa6f4c6 Kernel: Optimize futexes
Eeach futex object now has its own mutex to prevent unnecessary locking
of the process/global futex lock. This basically removes sys_futex from
profiles when running software with llvmpipe
2026-01-13 19:18:52 +02:00
Bananymous c30fc9d60f LibGUI: Rewrite using epoll
select is slow :^)
2026-01-12 23:53:11 +02:00
Bananymous 311a68160c Kernel: Don't delete futex objects after they are not used anymore
Hashmap insertions and deletions made futex very slow to use. When
running SuperTuxKart, ~15% of cpu time was spent doing these.

Never freeing objects is not great either but at least the performance
is usable now :)
2026-01-12 23:52:04 +02:00
Bananymous 343aef31c3 AudioServer: Rewrite using epoll
select is slow :^)
2026-01-12 23:46:51 +02:00
Bananymous 3ac8f7e14f WindowServer: Rewrite using epoll
Looking at profiles, select is a very slow syscall as it has to allocate
a temporary epoll instance
2026-01-12 23:46:03 +02:00
Bananymous 0cef66d155 Kernel: Fix epoll reporting multiple of the same event 2026-01-12 23:45:17 +02:00
Bananymous 9ffbb9fbf0 LibC: Fix clock_gettime return value 2026-01-11 22:44:03 +02:00
Bananymous c9a8f5b456 Kernel: Fix ext2 fileystem super block backups
Check if rev >= 1 sets sparse superblock feature instead of assuming it
is set
2026-01-11 19:55:10 +02:00
Bananymous 4e3831e380 Kernel: ACHI use ext commands for LBAs >=24 bits
AFAICS non extended commands are supposed to support 27 bit LBAs but
qemu seems to ignore bits 27:24. Maybe I'm just doing something wrong
but this seems to fix this.

This fixes using big disks :D ATM using using disks >= 8 GiB (with 512
byte LBAs) returned wrong data on reads, failing the boot :D
2026-01-11 15:15:58 +02:00
Bananymous cae2b3bd14 Kernel: Cleanup ext2 indirect block lookup
If we are not allocating and the block is null, add a fast path to
delete it. This also prevents possibly blocking filesystem block wrapper
allocation
2026-01-11 04:00:04 +02:00
Bananymous 5637b8602b Kernel: Fix setting ext2 symbolic link target
If a link was >= 60 bytes but got shrinked to 60 bytes, reading it would
rebort garbage and unlinking it would leak blocks
2026-01-11 03:58:48 +02:00
30 changed files with 1437 additions and 1090 deletions

View File

@ -221,7 +221,7 @@ namespace Kernel
ASSERT(!(pt[pte] & Flags::Present));
pt[pte] = paddr | Flags::ReadWrite | Flags::Present;
invalidate(fast_page(), false);
asm volatile("invlpg (%0)" :: "r"(fast_page()) : "memory");
}
void PageTable::unmap_fast_page()
@ -241,7 +241,7 @@ namespace Kernel
ASSERT(pt[pte] & Flags::Present);
pt[pte] = 0;
invalidate(fast_page(), false);
asm volatile("invlpg (%0)" :: "r"(fast_page()) : "memory");
}
BAN::ErrorOr<PageTable*> PageTable::create_userspace()
@ -314,7 +314,8 @@ namespace Kernel
.type = Processor::SMPMessage::Type::FlushTLB,
.flush_tlb = {
.vaddr = vaddr,
.page_count = 1
.page_count = 1,
.page_table = vaddr < KERNEL_OFFSET ? this : nullptr,
}
});
}
@ -343,8 +344,12 @@ namespace Kernel
uint64_t* pd = reinterpret_cast<uint64_t*>(P2V(pdpt[pdpte] & PAGE_ADDR_MASK));
uint64_t* pt = reinterpret_cast<uint64_t*>(P2V(pd[pde] & PAGE_ADDR_MASK));
const paddr_t old_paddr = pt[pte] & PAGE_ADDR_MASK;
pt[pte] = 0;
invalidate(vaddr, send_smp_message);
if (old_paddr != 0)
invalidate(vaddr, send_smp_message);
}
void PageTable::unmap_range(vaddr_t vaddr, size_t size)
@ -361,7 +366,8 @@ namespace Kernel
.type = Processor::SMPMessage::Type::FlushTLB,
.flush_tlb = {
.vaddr = vaddr,
.page_count = page_count
.page_count = page_count,
.page_table = vaddr < KERNEL_OFFSET ? this : nullptr,
}
});
}
@ -417,9 +423,13 @@ namespace Kernel
uwr_flags &= ~Flags::Present;
uint64_t* pt = reinterpret_cast<uint64_t*>(P2V(pd[pde] & PAGE_ADDR_MASK));
const paddr_t old_paddr = pt[pte] & PAGE_ADDR_MASK;
pt[pte] = paddr | uwr_flags | extra_flags;
invalidate(vaddr, send_smp_message);
if (old_paddr != 0)
invalidate(vaddr, send_smp_message);
}
void PageTable::map_range_at(paddr_t paddr, vaddr_t vaddr, size_t size, flags_t flags, MemoryType memory_type)
@ -438,7 +448,8 @@ namespace Kernel
.type = Processor::SMPMessage::Type::FlushTLB,
.flush_tlb = {
.vaddr = vaddr,
.page_count = page_count
.page_count = page_count,
.page_table = vaddr < KERNEL_OFFSET ? this : nullptr,
}
});
}
@ -523,6 +534,7 @@ namespace Kernel
.flush_tlb = {
.vaddr = vaddr,
.page_count = bytes / PAGE_SIZE,
.page_table = vaddr < KERNEL_OFFSET ? this : nullptr,
}
});
return true;

View File

@ -521,7 +521,7 @@ namespace Kernel
ASSERT(!(pt[pte] & Flags::Present));
pt[pte] = paddr | Flags::ReadWrite | Flags::Present;
invalidate(fast_page(), false);
asm volatile("invlpg (%0)" :: "r"(fast_page()) : "memory");
}
void PageTable::unmap_fast_page()
@ -544,7 +544,7 @@ namespace Kernel
ASSERT(pt[pte] & Flags::Present);
pt[pte] = 0;
invalidate(fast_page(), false);
asm volatile("invlpg (%0)" :: "r"(fast_page()) : "memory");
}
BAN::ErrorOr<PageTable*> PageTable::create_userspace()
@ -623,7 +623,8 @@ namespace Kernel
.type = Processor::SMPMessage::Type::FlushTLB,
.flush_tlb = {
.vaddr = vaddr,
.page_count = 1
.page_count = 1,
.page_table = vaddr < KERNEL_OFFSET ? this : nullptr,
}
});
}
@ -658,8 +659,12 @@ namespace Kernel
uint64_t* pd = P2V(pdpt[pdpte] & s_page_addr_mask);
uint64_t* pt = P2V(pd[pde] & s_page_addr_mask);
const paddr_t old_paddr = pt[pte] & PAGE_ADDR_MASK;
pt[pte] = 0;
invalidate(vaddr, send_smp_message);
if (old_paddr != 0)
invalidate(vaddr, send_smp_message);
}
void PageTable::unmap_range(vaddr_t vaddr, size_t size)
@ -676,7 +681,8 @@ namespace Kernel
.type = Processor::SMPMessage::Type::FlushTLB,
.flush_tlb = {
.vaddr = vaddr,
.page_count = page_count
.page_count = page_count,
.page_table = vaddr < KERNEL_OFFSET ? this : nullptr,
}
});
}
@ -742,9 +748,12 @@ namespace Kernel
if (!(flags & Flags::Present))
uwr_flags &= ~Flags::Present;
const paddr_t old_paddr = pt[pte] & PAGE_ADDR_MASK;
pt[pte] = paddr | uwr_flags | extra_flags;
invalidate(vaddr, send_smp_message);
if (old_paddr != 0)
invalidate(vaddr, send_smp_message);
}
void PageTable::map_range_at(paddr_t paddr, vaddr_t vaddr, size_t size, flags_t flags, MemoryType memory_type)
@ -765,7 +774,8 @@ namespace Kernel
.type = Processor::SMPMessage::Type::FlushTLB,
.flush_tlb = {
.vaddr = vaddr,
.page_count = page_count
.page_count = page_count,
.page_table = vaddr < KERNEL_OFFSET ? this : nullptr,
}
});
}
@ -840,6 +850,7 @@ namespace Kernel
.flush_tlb = {
.vaddr = vaddr,
.page_count = bytes / PAGE_SIZE,
.page_table = vaddr < KERNEL_OFFSET ? this : nullptr,
}
});
return true;

View File

@ -76,6 +76,7 @@
namespace Debug
{
void dump_stack_trace();
void dump_stack_trace(uintptr_t ip, uintptr_t bp);
void dump_qr_code();
void putchar(char);

View File

@ -70,6 +70,7 @@ namespace Kernel
BAN::ErrorOr<void> cleanup_indirect_block(uint32_t block, uint32_t depth);
BAN::ErrorOr<void> cleanup_default_links();
BAN::ErrorOr<void> cleanup_data_blocks();
BAN::ErrorOr<void> cleanup_from_fs();
BAN::ErrorOr<void> sync();

View File

@ -133,7 +133,7 @@ namespace Kernel
void map_kernel_memory();
void prepare_fast_page();
static void invalidate(vaddr_t, bool send_smp_message);
void invalidate(vaddr_t, bool send_smp_message);
static void map_fast_page(paddr_t);
static void unmap_fast_page();

View File

@ -9,6 +9,7 @@
#include <kernel/ELF.h>
#include <kernel/FS/Inode.h>
#include <kernel/Lock/Mutex.h>
#include <kernel/Lock/RWLock.h>
#include <kernel/Memory/Heap.h>
#include <kernel/Memory/MemoryRegion.h>
#include <kernel/Memory/SharedMemoryObject.h>
@ -218,8 +219,6 @@ namespace Kernel
BAN::ErrorOr<long> sys_tcgetpgrp(int fd);
BAN::ErrorOr<long> sys_tcsetpgrp(int fd, pid_t pgid);
BAN::ErrorOr<long> sys_termid(char*);
BAN::ErrorOr<long> sys_clock_gettime(clockid_t, timespec*);
BAN::ErrorOr<long> sys_load_keymap(const char* path);
@ -274,17 +273,20 @@ namespace Kernel
};
// Adds new region to the sorted array of mapped regions
// You must hold writer end of m_mapped_region_lock when calling this.
BAN::ErrorOr<void> add_mapped_region(BAN::UniqPtr<MemoryRegion>&&);
// If address is contained by a region, returns the index of that.
// Otherwise returns the address of the first region after this address.
// You must hold reader end of m_mapped_region_lock when calling this.
size_t find_mapped_region(vaddr_t) 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<VirtualFileSystem::File> find_relative_parent(int fd, const char* path) const;
BAN::ErrorOr<void> validate_string_access(const char*);
BAN::ErrorOr<void> validate_pointer_access(const void*, size_t, bool needs_write);
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);
uint64_t signal_pending_mask() const
@ -329,6 +331,7 @@ namespace Kernel
OpenFileDescriptorSet m_open_file_descriptors;
mutable RWLock m_memory_region_lock;
BAN::Vector<BAN::UniqPtr<MemoryRegion>> m_mapped_regions;
pid_t m_sid;
@ -348,6 +351,7 @@ namespace Kernel
struct futex_t
{
Mutex mutex;
ThreadBlocker blocker;
uint32_t waiters { 0 };
uint32_t to_wakeup { 0 };

View File

@ -46,6 +46,7 @@ namespace Kernel
{
uintptr_t vaddr;
size_t page_count;
void* page_table;
} flush_tlb;
SchedulerQueue::Node* new_thread;
SchedulerQueue::Node* unblock_thread;
@ -208,7 +209,7 @@ namespace Kernel
static vaddr_t s_shared_page_vaddr;
ProcessorID m_id { 0 };
uint8_t m_index { 0xFF };
uint8_t m_index { 0 };
vaddr_t m_thread_syscall_stack;

View File

@ -16,6 +16,7 @@
namespace Kernel
{
class MemoryBackedRegion;
class Process;
class Thread
@ -103,9 +104,7 @@ namespace Kernel
vaddr_t kernel_stack_top() const { return m_kernel_stack->vaddr() + m_kernel_stack->size(); }
VirtualRange& kernel_stack() { return *m_kernel_stack; }
vaddr_t userspace_stack_bottom() const { return is_userspace() ? m_userspace_stack->vaddr() : UINTPTR_MAX; }
vaddr_t userspace_stack_top() const { return is_userspace() ? m_userspace_stack->vaddr() + m_userspace_stack->size() : 0; }
VirtualRange& userspace_stack() { ASSERT(is_userspace()); return *m_userspace_stack; }
MemoryBackedRegion& userspace_stack() { ASSERT(is_userspace() && m_userspace_stack); return *m_userspace_stack; }
static Thread& current();
static pid_t current_tid();
@ -129,7 +128,7 @@ namespace Kernel
void set_gsbase(vaddr_t base) { m_gsbase = base; }
vaddr_t get_gsbase() const { return m_gsbase; }
size_t virtual_page_count() const { return (m_kernel_stack ? (m_kernel_stack->size() / PAGE_SIZE) : 0) + (m_userspace_stack ? (m_userspace_stack->size() / PAGE_SIZE) : 0); }
size_t virtual_page_count() const { return m_kernel_stack ? (m_kernel_stack->size() / PAGE_SIZE) : 0; }
size_t physical_page_count() const { return virtual_page_count(); }
YieldRegisters& yield_registers() { return m_yield_registers; }
@ -160,7 +159,7 @@ namespace Kernel
BAN::UniqPtr<PageTable> m_keep_alive_page_table;
BAN::UniqPtr<VirtualRange> m_kernel_stack;
BAN::UniqPtr<VirtualRange> m_userspace_stack;
MemoryBackedRegion* m_userspace_stack { nullptr };
const pid_t m_tid { 0 };
State m_state { State::NotStarted };
Process* m_process { nullptr };

View File

@ -29,28 +29,33 @@ namespace Debug
static uint8_t s_debug_ansi_state { 0 };
void dump_stack_trace()
{
dump_stack_trace(0, reinterpret_cast<uintptr_t>(__builtin_frame_address(0)));
}
void dump_stack_trace(uintptr_t ip, uintptr_t bp)
{
using namespace Kernel;
struct stackframe
{
stackframe* bp;
uintptr_t ip;
void* ip;
};
SpinLockGuard _(s_debug_lock);
stackframe* frame = (stackframe*)__builtin_frame_address(0);
if (!frame)
{
dprintln("Could not get frame address");
return;
}
uintptr_t first_ip = frame->ip;
uintptr_t last_ip = 0;
const stackframe* frame = reinterpret_cast<const stackframe*>(bp);
void* first_ip = frame->ip;
void* last_ip = 0;
bool first = true;
BAN::Formatter::print(Debug::putchar, "\e[36mStack trace:\r\n");
if (ip != 0)
BAN::Formatter::print(Debug::putchar, " {}\r\n", reinterpret_cast<void*>(ip));
while (frame)
{
if (!PageTable::is_valid_pointer((vaddr_t)frame))

View File

@ -128,16 +128,18 @@ namespace Kernel
{
auto& [inode, events] = *it;
#define REMOVE_IT_AND_CONTINUE() \
#define REMOVE_IT() \
({ \
m_processing_events.remove(it); \
if (event_count > 0) \
break; \
it = m_processing_events.begin(); \
continue; \
})
auto listen_it = m_listening_events.find(inode);
if (listen_it == m_listening_events.end())
REMOVE_IT_AND_CONTINUE();
REMOVE_IT();
auto& listen = listen_it->value;
{
@ -149,7 +151,7 @@ namespace Kernel
}
if (events == 0)
REMOVE_IT_AND_CONTINUE();
REMOVE_IT();
{
LockGuard inode_locker(inode->m_mutex);
@ -165,9 +167,9 @@ namespace Kernel
}
if (events == 0)
REMOVE_IT_AND_CONTINUE();
REMOVE_IT();
#undef REMOVE_IT_AND_CONTINUE
#undef REMOVE_IT
for (size_t fd = 0; fd < listen.events.size() && event_count < event_span.size(); fd++)
{

View File

@ -1,4 +1,5 @@
#include <BAN/ScopeGuard.h>
#include <BAN/Sort.h>
#include <kernel/FS/Ext2/FileSystem.h>
#include <kernel/Lock/LockGuard.h>
@ -127,7 +128,7 @@ namespace Kernel
{
auto block_buffer = TRY(m_buffer_manager.get_buffer());
if (superblock().rev_level == Ext2::Enum::GOOD_OLD_REV)
if ((superblock().rev_level == Ext2::Enum::GOOD_OLD_REV) || !(m_superblock.feature_ro_compat & Ext2::Enum::FEATURE_RO_COMPAT_SPARSE_SUPER))
{
// In revision 0 all blockgroups contain superblock backup
TRY(m_superblock_backups.reserve(number_of_block_groups - 1));
@ -148,6 +149,8 @@ namespace Kernel
// We don't really care if this succeeds or not
(void)m_superblock_backups.shrink_to_fit();
BAN::sort::sort(m_superblock_backups.begin(), m_superblock_backups.end());
}
for (uint32_t bg : m_superblock_backups)
@ -488,14 +491,7 @@ namespace Kernel
TRY(read_block(bgd_location.block, bgd_buffer));
auto& bgd = *(Ext2::BlockGroupDescriptor*)(bgd_buffer.data() + bgd_location.offset);
const uint32_t inode_byte_offset = inode_index * superblock().inode_size;
BlockLocation location
{
.block = inode_byte_offset / block_size + bgd.inode_table,
.offset = inode_byte_offset % block_size
};
const auto bgd = bgd_buffer.span().slice(bgd_location.offset).as<Ext2::BlockGroupDescriptor>();
#if EXT2_VERIFY_INODE
const uint32_t inode_bitmap_block = bgd.inode_bitmap;
@ -503,14 +499,18 @@ namespace Kernel
// NOTE: we can reuse the bgd_buffer since it is not needed anymore
auto& inode_bitmap = bgd_buffer;
read_block(inode_bitmap_block, inode_bitmap.span());
TRY(read_block(inode_bitmap_block, inode_bitmap));
const uint32_t byte = inode_index / 8;
const uint32_t bit = inode_index % 8;
ASSERT(inode_bitmap[byte] & (1 << bit));
#endif
return location;
const uint32_t inode_byte_offset = inode_index * superblock().inode_size;
return BlockLocation {
.block = inode_byte_offset / block_size + bgd.inode_table,
.offset = inode_byte_offset % block_size
};
}
Ext2FS::BlockLocation Ext2FS::locate_block_group_descriptior(uint32_t group_index)
@ -525,8 +525,7 @@ namespace Kernel
// Block Group Descriptor table is in the block after superblock
const uint32_t bgd_byte_offset = (superblock().first_data_block + 1) * block_size + sizeof(Ext2::BlockGroupDescriptor) * group_index;
return
{
return BlockLocation {
.block = bgd_byte_offset / block_size,
.offset = bgd_byte_offset % block_size
};

View File

@ -61,13 +61,13 @@ namespace Kernel
const uint32_t inode_blocks_per_fs_block = blksize() / 512;
const uint32_t indices_per_fs_block = blksize() / sizeof(uint32_t);
if (block == 0 && !allocate)
return BAN::Optional<uint32_t>();
if (depth == 0)
{
if (block == 0)
{
if (!allocate)
return BAN::Optional<uint32_t>();
block = TRY(m_fs.reserve_free_block(block_group()));
m_inode.blocks += inode_blocks_per_fs_block;
@ -87,9 +87,6 @@ namespace Kernel
TRY(m_fs.read_block(block, block_buffer));
else
{
if (!allocate)
return BAN::Optional<uint32_t>();
block = TRY(m_fs.reserve_free_block(block_group()));
m_inode.blocks += inode_blocks_per_fs_block;
@ -171,8 +168,10 @@ namespace Kernel
BAN::ErrorOr<void> Ext2Inode::set_link_target_impl(BAN::StringView target)
{
ASSERT(mode().iflnk());
if (m_inode.size < sizeof(m_inode.block) && target.size() < sizeof(m_inode.block))
if (target.size() < sizeof(m_inode.block))
{
if (m_inode.size >= sizeof(m_inode.block))
TRY(cleanup_data_blocks());
memset(m_inode.block, 0, sizeof(m_inode.block));
memcpy(m_inode.block, target.data(), target.size());
m_inode.size = target.size();
@ -412,10 +411,8 @@ namespace Kernel
return {};
}
BAN::ErrorOr<void> Ext2Inode::cleanup_from_fs()
BAN::ErrorOr<void> Ext2Inode::cleanup_data_blocks()
{
ASSERT(m_inode.links_count == 0);
if (mode().iflnk() && (size_t)size() < sizeof(m_inode.block))
goto done;
@ -436,12 +433,16 @@ done:
// mark blocks as deleted
memset(m_inode.block, 0x00, sizeof(m_inode.block));
// FIXME: this is only required since fs does not get
// deleting inode from its cache
TRY(sync());
TRY(m_fs.delete_inode(ino()));
return {};
}
BAN::ErrorOr<void> Ext2Inode::cleanup_from_fs()
{
ASSERT(m_inode.links_count == 0);
TRY(cleanup_data_blocks());
TRY(m_fs.delete_inode(ino()));
return {};
}

View File

@ -277,7 +277,11 @@ namespace Kernel
#endif
if (isr == ISR::PageFault)
PageTable::current().debug_dump();
Debug::dump_stack_trace();
#if ARCH(x86_64)
Debug::dump_stack_trace(interrupt_stack->ip, regs->rbp);
#elif ARCH(i686)
Debug::dump_stack_trace(interrupt_stack->ip, regs->ebp);
#endif
Debug::s_debug_lock.unlock(InterruptState::Disabled);

View File

@ -148,12 +148,12 @@ namespace Kernel
if (m_preallocated)
return false;
SpinLockGuard _(m_lock);
const size_t index = (vaddr - this->vaddr()) / PAGE_SIZE;
if (m_paddrs[index])
return false;
SpinLockGuard _(m_lock);
m_paddrs[index] = Heap::get().take_free_page();
if (m_paddrs[index] == 0)
return BAN::Error::from_errno(ENOMEM);

File diff suppressed because it is too large Load Diff

View File

@ -60,8 +60,13 @@ namespace Kernel
Kernel::panic("Trying to initialize invalid processor {}", id.m_id);
if (id == s_bsp_id)
{
for (auto& processor : s_processors)
{
processor.m_id = PROCESSOR_NONE;
processor.m_index = 0xFF;
}
}
auto& processor = s_processors[id.m_id];
@ -370,6 +375,8 @@ namespace Kernel
switch (message->type)
{
case SMPMessage::Type::FlushTLB:
if (message->flush_tlb.page_table && message->flush_tlb.page_table != processor.m_current_page_table)
break;
for (size_t i = 0; i < message->flush_tlb.page_count; i++)
asm volatile("invlpg (%0)" :: "r"(message->flush_tlb.vaddr + i * PAGE_SIZE) : "memory");
break;

View File

@ -256,16 +256,16 @@ namespace Kernel
fis_command.fis_type = FIS_TYPE_REGISTER_H2D;
fis_command.c = 1;
bool need_extended = lba >= (1 << 28) || sector_count > 0xFF;
ASSERT (!need_extended || (m_command_set & ATA_COMMANDSET_LBA48_SUPPORTED));
const bool needs_extended = lba >= (1 << 24) || sector_count > 0xFF;
ASSERT (!needs_extended || (m_command_set & ATA_COMMANDSET_LBA48_SUPPORTED));
switch (command)
{
case Command::Read:
fis_command.command = need_extended ? ATA_COMMAND_READ_DMA_EXT : ATA_COMMAND_READ_DMA;
fis_command.command = needs_extended ? ATA_COMMAND_READ_DMA_EXT : ATA_COMMAND_READ_DMA;
break;
case Command::Write:
fis_command.command = need_extended ? ATA_COMMAND_WRITE_DMA_EXT : ATA_COMMAND_WRITE_DMA;
fis_command.command = needs_extended ? ATA_COMMAND_WRITE_DMA_EXT : ATA_COMMAND_WRITE_DMA;
break;
default:
ASSERT_NOT_REACHED();

View File

@ -4,6 +4,7 @@
#include <kernel/InterruptController.h>
#include <kernel/InterruptStack.h>
#include <kernel/Memory/kmalloc.h>
#include <kernel/Memory/MemoryBackedRegion.h>
#include <kernel/Process.h>
#include <kernel/Scheduler.h>
#include <kernel/Thread.h>
@ -218,13 +219,16 @@ namespace Kernel
true, true
));
thread->m_userspace_stack = TRY(VirtualRange::create_to_vaddr_range(
auto userspace_stack = TRY(MemoryBackedRegion::create(
page_table,
stack_addr_start, USERSPACE_END,
userspace_stack_size,
{ stack_addr_start, USERSPACE_END },
MemoryRegion::Type::PRIVATE,
PageTable::Flags::UserSupervisor | PageTable::Flags::ReadWrite | PageTable::Flags::Present,
false, true
O_RDWR
));
thread->m_userspace_stack = userspace_stack.ptr();
TRY(process->add_mapped_region(BAN::move(userspace_stack)));
thread_deleter.disable();
@ -319,16 +323,18 @@ namespace Kernel
{
auto* thread = TRY(create_userspace(m_process, m_process->page_table()));
save_sse();
if (Processor::get_current_sse_thread() == this)
save_sse();
memcpy(thread->m_sse_storage, m_sse_storage, sizeof(m_sse_storage));
TRY(thread->userspace_stack().allocate_page_for_demand_paging(thread->userspace_stack_top() - PAGE_SIZE));
PageTable::with_fast_page(thread->userspace_stack().paddr_of(thread->userspace_stack_top() - PAGE_SIZE), [=] {
PageTable::fast_page_as<void*>(PAGE_SIZE - sizeof(uintptr_t)) = arg;
});
TRY(thread->userspace_stack().copy_data_to_region(
thread->m_userspace_stack->size() - sizeof(void*),
reinterpret_cast<const uint8_t*>(&arg),
sizeof(void*)
));
const vaddr_t entry_addr = reinterpret_cast<vaddr_t>(entry);
thread->setup_exec(entry_addr, thread->userspace_stack_top() - sizeof(uintptr_t));
thread->setup_exec(entry_addr, thread->userspace_stack().vaddr() + thread->userspace_stack().size() - sizeof(void*));
return thread;
}
@ -346,14 +352,18 @@ namespace Kernel
thread->m_is_userspace = true;
thread->m_kernel_stack = TRY(m_kernel_stack->clone(new_process->page_table()));
thread->m_userspace_stack = TRY(m_userspace_stack->clone(new_process->page_table()));
const auto stack_index = new_process->find_mapped_region(m_userspace_stack->vaddr());
thread->m_userspace_stack = static_cast<MemoryBackedRegion*>(new_process->m_mapped_regions[stack_index].ptr());
ASSERT(thread->m_userspace_stack->vaddr() == m_userspace_stack->vaddr());
thread->m_fsbase = m_fsbase;
thread->m_gsbase = m_gsbase;
thread->m_state = State::NotStarted;
save_sse();
if (Processor::get_current_sse_thread() == this)
save_sse();
memcpy(thread->m_sse_storage, m_sse_storage, sizeof(m_sse_storage));
thread->m_yield_registers = {};
@ -397,29 +407,18 @@ namespace Kernel
if (needed_size > m_userspace_stack->size())
return BAN::Error::from_errno(ENOBUFS);
vaddr_t vaddr = userspace_stack_top() - needed_size;
vaddr_t vaddr = userspace_stack().vaddr() + userspace_stack().size() - needed_size;
const size_t page_count = BAN::Math::div_round_up<size_t>(needed_size, PAGE_SIZE);
for (size_t i = 0; i < page_count; i++)
TRY(m_userspace_stack->allocate_page_for_demand_paging(vaddr + i * PAGE_SIZE));
TRY(m_userspace_stack->allocate_page_containing(vaddr + i * PAGE_SIZE, true));
const auto stack_copy_buf =
[this](BAN::ConstByteSpan buffer, vaddr_t vaddr) -> void
{
ASSERT(vaddr + buffer.size() <= userspace_stack_top());
size_t bytes_copied = 0;
while (bytes_copied < buffer.size())
{
const size_t to_copy = BAN::Math::min<size_t>(buffer.size() - bytes_copied, PAGE_SIZE - (vaddr % PAGE_SIZE));
PageTable::with_fast_page(userspace_stack().paddr_of(vaddr & PAGE_ADDR_MASK), [=]() {
memcpy(PageTable::fast_page_as_ptr(vaddr % PAGE_SIZE), buffer.data() + bytes_copied, to_copy);
});
vaddr += to_copy;
bytes_copied += to_copy;
}
ASSERT(vaddr >= m_userspace_stack->vaddr());
ASSERT(vaddr + buffer.size() <= m_userspace_stack->vaddr() + m_userspace_stack->size());
MUST(m_userspace_stack->copy_data_to_region(vaddr - m_userspace_stack->vaddr(), buffer.data(), buffer.size()));
};
const auto stack_push_buf =
@ -471,7 +470,7 @@ namespace Kernel
stack_push_str(envp[i]);
}
setup_exec(entry, userspace_stack_top() - needed_size);
setup_exec(entry, m_userspace_stack->vaddr() + m_userspace_stack->size() - needed_size);
return {};
}

View File

@ -1,17 +1,17 @@
From 2b3a0198640c23a7f8f8247ed1edae309c11e15f Mon Sep 17 00:00:00 2001
From 49d1ca61e6249c3bc3284a6b55578b4ddd7b13ac Mon Sep 17 00:00:00 2001
From: Oskari Alaranta <oskari.alaranta@bananymous.com>
Date: Thu, 31 Jul 2025 22:15:43 +0300
Subject: [PATCH 1/2] add support for banan-os
Date: Tue, 13 Jan 2026 20:49:19 +0200
Subject: [PATCH] add support for banan-os
---
engine/common/net_http.c | 4 +++-
engine/common/net_ws.c | 17 +++++++++++++++++
engine/common/whereami.c | 16 ++++++++++++++++
engine/platform/posix/lib_posix.c | 2 ++
public/build.c | 2 ++
public/build.h | 3 +++
public/buildenums.h | 3 +++
7 files changed, 46 insertions(+), 1 deletion(-)
engine/common/net_http.c | 4 +++-
engine/common/net_ws.c | 6 ++++++
engine/common/whereami.c | 2 +-
engine/platform/posix/net.h | 4 ++++
public/build.c | 2 ++
public/build.h | 3 +++
public/buildenums.h | 3 +++
7 files changed, 22 insertions(+), 2 deletions(-)
diff --git a/engine/common/net_http.c b/engine/common/net_http.c
index ff6fd41b..ebb5bbad 100644
@ -36,23 +36,10 @@ index ff6fd41b..ebb5bbad 100644
res = fcntl( file->socket, F_GETFL, 0 );
diff --git a/engine/common/net_ws.c b/engine/common/net_ws.c
index 7ee4cba0..e93a2410 100644
index 7ee4cba0..b44d5cf1 100644
--- a/engine/common/net_ws.c
+++ b/engine/common/net_ws.c
@@ -1364,7 +1364,12 @@ static qboolean NET_QueuePacket( netsrc_t sock, netadr_t *from, byte *data, size
byte buf[NET_MAX_FRAGMENT];
int ret, protocol;
int net_socket;
+#ifdef XASH_BANAN_OS
+ socklen_t addr_len;
+#else
WSAsize_t addr_len;
+#endif
+
struct sockaddr_storage addr = { 0 };
*length = 0;
@@ -1617,7 +1622,11 @@ static int NET_IPSocket( const char *net_iface, int port, int family )
@@ -1617,7 +1617,11 @@ static int NET_IPSocket( const char *net_iface, int port, int family )
if( family == AF_INET6 )
pfamily = PF_INET6;
@ -64,7 +51,7 @@ index 7ee4cba0..e93a2410 100644
{
err = WSAGetLastError();
if( err != WSAEAFNOSUPPORT )
@@ -1625,6 +1634,7 @@ static int NET_IPSocket( const char *net_iface, int port, int family )
@@ -1625,6 +1629,7 @@ static int NET_IPSocket( const char *net_iface, int port, int family )
return INVALID_SOCKET;
}
@ -72,7 +59,7 @@ index 7ee4cba0..e93a2410 100644
if( NET_IsSocketError( ioctlsocket( net_socket, FIONBIO, (void*)&_true )))
{
struct timeval timeout;
@@ -1634,6 +1644,7 @@ static int NET_IPSocket( const char *net_iface, int port, int family )
@@ -1634,6 +1639,7 @@ static int NET_IPSocket( const char *net_iface, int port, int family )
timeout.tv_sec = timeout.tv_usec = 0;
setsockopt( net_socket, SOL_SOCKET, SO_RCVTIMEO, (char *)&timeout, sizeof(timeout));
}
@ -80,81 +67,34 @@ index 7ee4cba0..e93a2410 100644
// make it broadcast capable
if( NET_IsSocketError( setsockopt( net_socket, SOL_SOCKET, SO_BROADCAST, (char *)&_true, sizeof( _true ))))
@@ -1686,6 +1697,7 @@ static int NET_IPSocket( const char *net_iface, int port, int family )
optval = 0x10; // IPTOS_LOWDELAY
Con_Printf( "Enabling LOWDELAY TOS option\n" );
+#ifdef IP_TOS
if( NET_IsSocketError( setsockopt( net_socket, IPPROTO_IP, IP_TOS, (const char *)&optval, sizeof( optval ))))
{
err = WSAGetLastError();
@@ -1694,6 +1706,7 @@ static int NET_IPSocket( const char *net_iface, int port, int family )
closesocket( net_socket );
return INVALID_SOCKET;
}
+#endif
}
if( Sys_CheckParm( "-loopback" ))
@@ -1801,7 +1814,11 @@ static void NET_DetermineLocalAddress( void )
char hostname[512];
char buff[512];
struct sockaddr_storage address;
+#ifdef XASH_BANAN_OS
+ socklen_t namelen;
+#else
WSAsize_t namelen;
+#endif
const char *net_addr_string;
memset( &net_local, 0, sizeof( netadr_t ));
diff --git a/engine/common/whereami.c b/engine/common/whereami.c
index 31726774..5b7ff504 100644
index 31726774..69a6cb9f 100644
--- a/engine/common/whereami.c
+++ b/engine/common/whereami.c
@@ -851,6 +851,22 @@ int WAI_PREFIX(getModulePath)(char* out, int capacity, int* dirname_length)
return -1;
@@ -175,7 +175,7 @@ int WAI_PREFIX(getModulePath)(char* out, int capacity, int* dirname_length)
return length;
}
+#elif defined(__banan_os__)
+
+/* Not possible on this platform */
+
+WAI_FUNCSPEC
+int WAI_PREFIX(getExecutablePath)(char* out, int capacity, int* dirname_length)
+{
+ return -1;
+}
+
+WAI_FUNCSPEC
+int WAI_PREFIX(getModulePath)(char* out, int capacity, int* dirname_length)
+{
+ return -1;
+}
+
#else
-#elif defined(__linux__) || defined(__CYGWIN__) || defined(__sun) || defined(__serenity__) || defined(WAI_USE_PROC_SELF_EXE)
+#elif defined(__linux__) || defined(__CYGWIN__) || defined(__sun) || defined(__serenity__) || defined(__banan_os__) || defined(WAI_USE_PROC_SELF_EXE)
#error unsupported platform
diff --git a/engine/platform/posix/lib_posix.c b/engine/platform/posix/lib_posix.c
index 23d1c3b1..a5a84fe1 100644
--- a/engine/platform/posix/lib_posix.c
+++ b/engine/platform/posix/lib_posix.c
@@ -208,6 +208,7 @@ const char *COM_NameForFunction( void *hInstance, void *function )
return Loader_GetFuncName_int(wm, function);
else
#include <stdio.h>
#include <stdlib.h>
diff --git a/engine/platform/posix/net.h b/engine/platform/posix/net.h
index 58532927..6ab3cd1f 100644
--- a/engine/platform/posix/net.h
+++ b/engine/platform/posix/net.h
@@ -90,6 +90,10 @@ static int ioctl_stub( int d, unsigned long r, ... )
#define closesocket close
#endif
+#ifndef XASH_BANAN_OS
// NOTE: dladdr() is a glibc extension
{
Dl_info info = {0};
@@ -215,6 +216,7 @@ const char *COM_NameForFunction( void *hInstance, void *function )
if( ret && info.dli_sname )
return COM_GetPlatformNeutralName( info.dli_sname );
}
#define SOCKET int
+#if XASH_BANAN_OS
+typedef socklen_t WSAsize_t;
+#else
typedef int WSAsize_t;
+#endif
#ifdef XASH_ALLOW_SAVERESTORE_OFFSETS
return COM_OffsetNameForFunction( function );
#else
#endif // NET_H
diff --git a/public/build.c b/public/build.c
index 6c85bbaf..460b4040 100644
--- a/public/build.c
@ -211,5 +151,5 @@ index 4dc327d3..57585f73 100644
#error
#endif
--
2.50.1
2.52.0

View File

@ -1,25 +0,0 @@
From b8c6ea53b4a8119f299fb6b5e4684c30795037a7 Mon Sep 17 00:00:00 2001
From: Oskari Alaranta <oskari.alaranta@bananymous.com>
Date: Thu, 31 Jul 2025 22:36:28 +0300
Subject: [PATCH] Don't export have libbacktrace
This doesnt work :)
---
3rdparty/libbacktrace/wscript | 1 -
1 file changed, 1 deletion(-)
diff --git a/3rdparty/libbacktrace/wscript b/3rdparty/libbacktrace/wscript
index 2e076a56..98aca8d5 100644
--- a/3rdparty/libbacktrace/wscript
+++ b/3rdparty/libbacktrace/wscript
@@ -180,7 +180,6 @@ def build(bld):
features = 'frandomseed' if bld.env.HAVE_FRANDOM_SEED else '',
use = 'EXTRAFLAGS lzma z zstd',
includes = '. libbacktrace/',
- export_defines = 'HAVE_LIBBACKTRACE=1',
export_includes = 'libbacktrace/'
)
--
2.50.1

View File

@ -1,26 +0,0 @@
From 1e1fb9caff45f0cb05e89bfe452a7ff2abb558ff Mon Sep 17 00:00:00 2001
From: Oskari Alaranta <oskari.alaranta@bananymous.com>
Date: Thu, 31 Jul 2025 22:17:48 +0300
Subject: [PATCH] include alloca on banan-os
This detection does not work :(
---
lib/os.h | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/lib/os.h b/lib/os.h
index 9ded7358..66b56704 100644
--- a/lib/os.h
+++ b/lib/os.h
@@ -62,7 +62,7 @@ void *_alloca(size_t size);
#endif /* _V_IFDEFJAIL_H_ */
-#ifdef HAVE_ALLOCA_H
+#if defined(HAVE_ALLOCA_H) || defined(__banan_os__)
# include <alloca.h>
#endif
--
2.50.1

View File

@ -9,7 +9,6 @@ __BEGIN_DECLS
O(SYS_EXIT, exit) \
O(SYS_READ, read) \
O(SYS_WRITE, write) \
O(SYS_TERMID, termid) \
O(SYS_CLOSE, close) \
O(SYS_OPENAT, openat) \
O(SYS_SEEK, seek) \

View File

@ -138,7 +138,7 @@ char* ctermid(char* buffer)
{
static char s_buffer[L_ctermid];
char* target = buffer ? buffer : s_buffer;
syscall(SYS_TERMID, target);
strcpy(buffer, "/dev/tty");
return target;
}

View File

@ -62,7 +62,7 @@ int clock_gettime(clockid_t clock_id, struct timespec* tp)
if (clock_id == CLOCK_REALTIME)
tp->tv_sec += sgettime.realtime_seconds;
return monotonic_ns;
return 0;
}
}

View File

@ -5,8 +5,8 @@
#include <fcntl.h>
#include <stdlib.h>
#include <sys/banan-os.h>
#include <sys/epoll.h>
#include <sys/mman.h>
#include <sys/select.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <unistd.h>
@ -67,6 +67,18 @@ namespace LibGUI
return BAN::Error::from_errno(errno);
BAN::ScopeGuard server_closer([server_fd] { close(server_fd); });
int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
if (epoll_fd == -1)
return BAN::Error::from_errno(errno);
BAN::ScopeGuard epoll_closer([epoll_fd] { close(epoll_fd); });
epoll_event epoll_event {
.events = EPOLLIN,
.data = { .fd = server_fd },
};
if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, server_fd, &epoll_event) == -1)
return BAN::Error::from_errno(errno);
if (fcntl(server_fd, F_SETFD, fcntl(server_fd, F_GETFD) | FD_CLOEXEC) == -1)
return BAN::Error::from_errno(errno);
@ -100,7 +112,7 @@ namespace LibGUI
TRY(create_packet.title.append(title));
TRY(create_packet.send_serialized(server_fd));
auto window = TRY(BAN::UniqPtr<Window>::create(server_fd, attributes));
auto window = TRY(BAN::UniqPtr<Window>::create(server_fd, epoll_fd, attributes));
bool resized = false;
window->set_resize_window_event_callback([&]() { resized = true; });
@ -109,6 +121,7 @@ namespace LibGUI
window->set_resize_window_event_callback({});
server_closer.disable();
epoll_closer.disable();
return window;
}
@ -261,6 +274,7 @@ namespace LibGUI
{
munmap(m_framebuffer_smo, m_width * m_height * 4);
close(m_server_fd);
close(m_epoll_fd);
}
BAN::ErrorOr<void> Window::handle_resize_event(const EventPacket::ResizeWindowEvent& event)
@ -289,10 +303,8 @@ namespace LibGUI
void Window::wait_events()
{
fd_set fds;
FD_ZERO(&fds);
FD_SET(m_server_fd, &fds);
select(m_server_fd + 1, &fds, nullptr, nullptr, nullptr);
epoll_event dummy;
epoll_wait(m_epoll_fd, &dummy, 1, -1);
}
void Window::poll_events()
@ -300,13 +312,8 @@ namespace LibGUI
#define TRY_OR_BREAK(...) ({ auto&& e = (__VA_ARGS__); if (e.is_error()) break; e.release_value(); })
for (;;)
{
fd_set fds;
FD_ZERO(&fds);
FD_SET(m_server_fd, &fds);
timeval timeout { .tv_sec = 0, .tv_usec = 0 };
select(m_server_fd + 1, &fds, nullptr, nullptr, &timeout);
if (!FD_ISSET(m_server_fd, &fds))
epoll_event event;
if (epoll_wait(m_epoll_fd, &event, 1, 0) == 0)
break;
auto packet_or_error = recv_packet(m_server_fd);

View File

@ -81,8 +81,9 @@ namespace LibGUI
int server_fd() const { return m_server_fd; }
private:
Window(int server_fd, Attributes attributes)
Window(int server_fd, int epoll_fd, Attributes attributes)
: m_server_fd(server_fd)
, m_epoll_fd(epoll_fd)
, m_attributes(attributes)
{ }
@ -93,6 +94,7 @@ namespace LibGUI
private:
const int m_server_fd;
const int m_epoll_fd;
bool m_handling_socket_error { false };

View File

@ -5,8 +5,8 @@
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/epoll.h>
#include <sys/ioctl.h>
#include <sys/select.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/un.h>
@ -119,12 +119,24 @@ int main()
if (server_fd == -1)
return 1;
struct ClientInfo
const int epoll_fd = epoll_create1(0);
if (epoll_fd == -1)
{
int fd;
};
dwarnln("failed to create epoll: {}", strerror(errno));
return 1;
}
BAN::Vector<ClientInfo> clients;
{
epoll_event event {
.events = EPOLLIN,
.data = { .fd = server_fd },
};
if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, server_fd, &event) == -1)
{
dwarnln("failed to add server socket to epoll: {}", strerror(errno));
return -1;
}
}
dprintln("AudioServer started");
@ -132,67 +144,70 @@ int main()
for (;;)
{
fd_set fds;
FD_ZERO(&fds);
FD_SET(server_fd, &fds);
int max_fd = server_fd;
for (const auto& client : clients)
{
max_fd = BAN::Math::max(max_fd, client.fd);
FD_SET(client.fd, &fds);
}
const uint64_t current_ms = get_current_ms();
next_update_ms = current_ms + audio_server->update();
const uint64_t timeout_ms = next_update_ms - current_ms;
timeval timeout {
.tv_sec = static_cast<time_t>(timeout_ms / 1000),
.tv_usec = static_cast<suseconds_t>((timeout_ms % 1000) * 1000)
};
if (select(max_fd + 1, &fds, nullptr, nullptr, &timeout) == -1)
epoll_event events[16];
int event_count = epoll_wait(epoll_fd, events, 16, timeout_ms);
if (event_count == -1 && errno != EINTR)
{
dwarnln("select: {}", strerror(errno));
dwarnln("epoll_wait: {}", strerror(errno));
break;
}
if (FD_ISSET(server_fd, &fds))
for (int i = 0; i < event_count; i++)
{
const int client_fd = accept(server_fd, nullptr, nullptr);
if (client_fd == -1)
if (events[i].data.fd == server_fd)
{
dwarnln("accept: {}", strerror(errno));
ASSERT(events[i].events & EPOLLIN);
const int client_fd = accept(server_fd, nullptr, nullptr);
if (client_fd == -1)
{
dwarnln("accept: {}", strerror(errno));
continue;
}
epoll_event event {
.events = EPOLLIN,
.data = { .fd = client_fd },
};
if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, client_fd, &event) == -1)
{
dwarnln("Failed to add client to epoll: {}", strerror(errno));
close(client_fd);
continue;
}
if (auto ret = audio_server->on_new_client(client_fd); ret.is_error())
{
dwarnln("Failed to initialize client: {}", ret.error());
epoll_ctl(epoll_fd, EPOLL_CTL_DEL, client_fd, nullptr);
close(client_fd);
continue;
}
continue;
}
if (auto ret = clients.emplace_back(client_fd); ret.is_error())
const int client_fd = events[i].data.fd;
if (events[i].events & EPOLLHUP)
{
dwarnln("Failed to add client: {}", ret.error());
audio_server->on_client_disconnect(client_fd);
epoll_ctl(epoll_fd, EPOLL_CTL_DEL, client_fd, nullptr);
close(client_fd);
continue;
}
if (auto ret = audio_server->on_new_client(client_fd); ret.is_error())
{
dwarnln("Failed to initialize client: {}", ret.error());
clients.pop_back();
close(client_fd);
continue;
}
}
for (size_t i = 0; i < clients.size(); i++)
{
auto& client = clients[i];
if (!FD_ISSET(client.fd, &fds))
continue;
ASSERT(events[i].events & EPOLLIN);
LibAudio::Packet packet;
const ssize_t nrecv = recv(client.fd, &packet, sizeof(packet), 0);
const ssize_t nrecv = recv(client_fd, &packet, sizeof(packet), 0);
if (nrecv < static_cast<ssize_t>(sizeof(packet)) || !audio_server->on_client_packet(client.fd, packet))
if (nrecv < static_cast<ssize_t>(sizeof(packet)) || !audio_server->on_client_packet(client_fd, packet))
{
if (nrecv == 0)
;
@ -201,10 +216,9 @@ int main()
else if (nrecv < static_cast<ssize_t>(sizeof(packet)))
dwarnln("client sent only {} bytes, {} expected", nrecv, sizeof(packet));
audio_server->on_client_disconnect(client.fd);
close(client.fd);
clients.remove(i--);
continue;
audio_server->on_client_disconnect(client_fd);
epoll_ctl(epoll_fd, EPOLL_CTL_DEL, client_fd, nullptr);
close(client_fd);
}
}
}

View File

@ -1460,28 +1460,17 @@ void WindowServer::remove_client_fd(int fd)
break;
}
}
m_deleted_window = true;
}
int WindowServer::get_client_fds(fd_set& fds) const
WindowServer::ClientData& WindowServer::get_client_data(int fd)
{
int max_fd = 0;
for (const auto& [fd, _] : m_client_data)
{
FD_SET(fd, &fds);
max_fd = BAN::Math::max(max_fd, fd);
}
return max_fd;
}
auto it = m_client_data.find(fd);
if (it != m_client_data.end())
return it->value;
void WindowServer::for_each_client_fd(const BAN::Function<BAN::Iteration(int, ClientData&)>& callback)
{
m_deleted_window = false;
for (auto& [fd, cliend_data] : m_client_data)
{
if (m_deleted_window)
break;
callback(fd, cliend_data);
}
dwarnln("could not find client {}", fd);
for (auto& [client_fd, _] : m_client_data)
dwarnln(" {}", client_fd);
ASSERT_NOT_REACHED();
}

View File

@ -15,8 +15,6 @@
#include <LibInput/KeyEvent.h>
#include <LibInput/MouseEvent.h>
#include <sys/select.h>
class WindowServer
{
public:
@ -58,8 +56,7 @@ public:
void add_client_fd(int fd);
void remove_client_fd(int fd);
int get_client_fds(fd_set& fds) const;
void for_each_client_fd(const BAN::Function<BAN::Iteration(int, ClientData&)>& callback);
ClientData& get_client_data(int fd);
bool is_stopped() const { return m_is_stopped; }
@ -114,7 +111,6 @@ private:
bool m_is_mouse_relative { false };
bool m_deleted_window { false };
bool m_is_stopped { false };
bool m_is_bouncing_window = false;

View File

@ -9,9 +9,9 @@
#include <fcntl.h>
#include <stdlib.h>
#include <sys/banan-os.h>
#include <sys/epoll.h>
#include <sys/mman.h>
#include <sys/socket.h>
#include <sys/select.h>
#include <sys/stat.h>
#include <sys/un.h>
#include <unistd.h>
@ -157,10 +157,29 @@ int main()
return 1;
}
int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
if (epoll_fd == -1)
{
dwarnln("epoll_create1: {}", strerror(errno));
return 1;
}
{
epoll_event event {
.events = EPOLLIN,
.data = { .fd = server_fd },
};
if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, server_fd, &event) == -1)
{
dwarnln("epoll_ctl server: {}", strerror(errno));
return 1;
}
}
if (tty_ctrl(STDIN_FILENO, TTY_CMD_UNSET, TTY_FLAG_ENABLE_INPUT) == -1)
{
perror("tty_ctrl");
exit(1);
dwarnln("tty_ctrl: {}", strerror(errno));
return 1;
}
atexit([]() { tty_ctrl(STDIN_FILENO, TTY_CMD_SET, TTY_FLAG_ENABLE_INPUT); });
@ -188,11 +207,37 @@ int main()
int keyboard_fd = open("/dev/keyboard", O_RDONLY | O_CLOEXEC);
if (keyboard_fd == -1)
perror("open");
dwarnln("open keyboard: {}", strerror(errno));
else
{
epoll_event event {
.events = EPOLLIN,
.data = { .fd = keyboard_fd },
};
if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, keyboard_fd, &event) == -1)
{
dwarnln("epoll_ctl keyboard: {}", strerror(errno));
close(keyboard_fd);
keyboard_fd = -1;
}
}
int mouse_fd = open("/dev/mouse", O_RDONLY | O_CLOEXEC);
if (mouse_fd == -1)
perror("open");
dwarnln("open mouse: {}", strerror(errno));
else
{
epoll_event event {
.events = EPOLLIN,
.data = { .fd = mouse_fd },
};
if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, mouse_fd, &event) == -1)
{
dwarnln("epoll_ctl mouse: {}", strerror(errno));
close(mouse_fd);
mouse_fd = -1;
}
}
dprintln("Window server started");
@ -208,11 +253,11 @@ int main()
{
timespec current_ts;
clock_gettime(CLOCK_MONOTONIC, &current_ts);
return (current_ts.tv_sec * 1'000'000) + (current_ts.tv_nsec / 1'000);
return (current_ts.tv_sec * 1'000'000) + (current_ts.tv_nsec / 1000);
};
constexpr uint64_t sync_interval_us = 1'000'000 / 60;
uint64_t last_sync_us = 0;
uint64_t last_sync_us = get_current_us() - sync_interval_us;
while (!window_server.is_stopped())
{
const auto current_us = get_current_us();
@ -222,168 +267,182 @@ int main()
last_sync_us += sync_interval_us;
}
int max_fd = server_fd;
fd_set fds;
FD_ZERO(&fds);
FD_SET(server_fd, &fds);
if (keyboard_fd != -1)
{
FD_SET(keyboard_fd, &fds);
max_fd = BAN::Math::max(max_fd, keyboard_fd);
}
if (mouse_fd != -1)
{
FD_SET(mouse_fd, &fds);
max_fd = BAN::Math::max(max_fd, mouse_fd);
}
max_fd = BAN::Math::max(max_fd, window_server.get_client_fds(fds));
timeval select_timeout {};
timespec timeout = {};
if (auto current_us = get_current_us(); current_us - last_sync_us < sync_interval_us)
select_timeout.tv_usec = sync_interval_us - (current_us - last_sync_us);
timeout.tv_nsec = (sync_interval_us - (current_us - last_sync_us)) * 1000;
int nselect = select(max_fd + 1, &fds, nullptr, nullptr, &select_timeout);
if (nselect == 0)
continue;
if (nselect == -1)
epoll_event events[16];
int epoll_events = epoll_pwait2(epoll_fd, events, 16, &timeout, nullptr);
if (epoll_events == -1 && errno != EINTR)
{
dwarnln("select: {}", strerror(errno));
dwarnln("epoll_pwait2: {}", strerror(errno));
break;
}
if (FD_ISSET(server_fd, &fds))
for (int i = 0; i < epoll_events; i++)
{
int window_fd = accept4(server_fd, nullptr, nullptr, SOCK_NONBLOCK | SOCK_CLOEXEC);
if (window_fd == -1)
if (events[i].data.fd == server_fd)
{
dwarnln("accept: {}", strerror(errno));
continue;
}
window_server.add_client_fd(window_fd);
}
ASSERT(events[i].events & EPOLLIN);
if (keyboard_fd != -1 && FD_ISSET(keyboard_fd, &fds))
{
LibInput::RawKeyEvent event;
if (read(keyboard_fd, &event, sizeof(event)) == -1)
{
perror("read");
continue;
}
window_server.on_key_event(LibInput::KeyboardLayout::get().key_event_from_raw(event));
}
if (mouse_fd != -1 && FD_ISSET(mouse_fd, &fds))
{
LibInput::MouseEvent event;
if (read(mouse_fd, &event, sizeof(event)) == -1)
{
perror("read");
continue;
}
switch (event.type)
{
case LibInput::MouseEventType::MouseButtonEvent:
window_server.on_mouse_button(event.button_event);
break;
case LibInput::MouseEventType::MouseMoveEvent:
window_server.on_mouse_move(event.move_event);
break;
case LibInput::MouseEventType::MouseMoveAbsEvent:
window_server.on_mouse_move_abs(event.move_abs_event);
break;
case LibInput::MouseEventType::MouseScrollEvent:
window_server.on_mouse_scroll(event.scroll_event);
break;
}
}
window_server.for_each_client_fd(
[&](int fd, WindowServer::ClientData& client_data) -> BAN::Iteration
{
if (!FD_ISSET(fd, &fds))
return BAN::Iteration::Continue;
if (client_data.packet_buffer.empty())
int window_fd = accept4(server_fd, nullptr, nullptr, SOCK_NONBLOCK | SOCK_CLOEXEC);
if (window_fd == -1)
{
uint32_t packet_size;
const ssize_t nrecv = recv(fd, &packet_size, sizeof(uint32_t), 0);
if (nrecv < 0)
dwarnln("recv: {}", strerror(errno));
if (nrecv > 0 && nrecv != sizeof(uint32_t))
dwarnln("could not read packet size with a single recv call, closing connection...");
if (nrecv != sizeof(uint32_t))
{
window_server.remove_client_fd(fd);
return BAN::Iteration::Continue;
}
if (packet_size < 4)
{
dwarnln("client sent invalid packet, closing connection...");
return BAN::Iteration::Continue;
}
// this is a bit harsh, but i don't want to work on skipping streaming packets
if (client_data.packet_buffer.resize(packet_size).is_error())
{
dwarnln("could not allocate memory for client packet, closing connection...");
window_server.remove_client_fd(fd);
return BAN::Iteration::Continue;
}
client_data.packet_buffer_nread = 0;
return BAN::Iteration::Continue;
dwarnln("accept: {}", strerror(errno));
continue;
}
const ssize_t nrecv = recv(
fd,
client_data.packet_buffer.data() + client_data.packet_buffer_nread,
client_data.packet_buffer.size() - client_data.packet_buffer_nread,
0
);
epoll_event event {
.events = EPOLLIN,
.data = { .fd = window_fd },
};
if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, window_fd, &event) == -1)
{
dwarnln("epoll_ctl: {}", strerror(errno));
close(window_fd);
continue;
}
window_server.add_client_fd(window_fd);
continue;
}
if (events[i].data.fd == keyboard_fd)
{
ASSERT(events[i].events & EPOLLIN);
LibInput::RawKeyEvent event;
if (read(keyboard_fd, &event, sizeof(event)) == -1)
{
dwarnln("read keyboard: {}", strerror(errno));
continue;
}
window_server.on_key_event(LibInput::KeyboardLayout::get().key_event_from_raw(event));
continue;
}
if (events[i].data.fd == mouse_fd)
{
ASSERT(events[i].events & EPOLLIN);
LibInput::MouseEvent event;
if (read(mouse_fd, &event, sizeof(event)) == -1)
{
dwarnln("read mouse: {}", strerror(errno));
continue;
}
switch (event.type)
{
case LibInput::MouseEventType::MouseButtonEvent:
window_server.on_mouse_button(event.button_event);
break;
case LibInput::MouseEventType::MouseMoveEvent:
window_server.on_mouse_move(event.move_event);
break;
case LibInput::MouseEventType::MouseMoveAbsEvent:
window_server.on_mouse_move_abs(event.move_abs_event);
break;
case LibInput::MouseEventType::MouseScrollEvent:
window_server.on_mouse_scroll(event.scroll_event);
break;
}
continue;
}
const int client_fd = events[i].data.fd;
if (events[i].events & EPOLLHUP)
{
epoll_ctl(epoll_fd, EPOLL_CTL_DEL, client_fd, nullptr);
window_server.remove_client_fd(client_fd);
continue;
}
ASSERT(events[i].events & EPOLLIN);
auto& client_data = window_server.get_client_data(client_fd);
if (client_data.packet_buffer.empty())
{
uint32_t packet_size;
const ssize_t nrecv = recv(client_fd, &packet_size, sizeof(uint32_t), 0);
if (nrecv < 0)
dwarnln("recv: {}", strerror(errno));
if (nrecv <= 0)
dwarnln("recv 1: {}", strerror(errno));
if (nrecv > 0 && nrecv != sizeof(uint32_t))
dwarnln("could not read packet size with a single recv call, closing connection...");
if (nrecv != sizeof(uint32_t))
{
window_server.remove_client_fd(fd);
return BAN::Iteration::Continue;
epoll_ctl(epoll_fd, EPOLL_CTL_DEL, client_fd, nullptr);
window_server.remove_client_fd(client_fd);
break;
}
client_data.packet_buffer_nread += nrecv;
if (client_data.packet_buffer_nread < client_data.packet_buffer.size())
return BAN::Iteration::Continue;
ASSERT(client_data.packet_buffer.size() >= sizeof(uint32_t));
switch (*reinterpret_cast<LibGUI::PacketType*>(client_data.packet_buffer.data()))
if (packet_size < 4)
{
#define WINDOW_PACKET_CASE(enum, function) \
case LibGUI::PacketType::enum: \
if (auto ret = LibGUI::WindowPacket::enum::deserialize(client_data.packet_buffer.span()); !ret.is_error()) \
window_server.function(fd, ret.release_value()); \
break
WINDOW_PACKET_CASE(WindowCreate, on_window_create);
WINDOW_PACKET_CASE(WindowInvalidate, on_window_invalidate);
WINDOW_PACKET_CASE(WindowSetPosition, on_window_set_position);
WINDOW_PACKET_CASE(WindowSetAttributes, on_window_set_attributes);
WINDOW_PACKET_CASE(WindowSetMouseRelative, on_window_set_mouse_relative);
WINDOW_PACKET_CASE(WindowSetSize, on_window_set_size);
WINDOW_PACKET_CASE(WindowSetMinSize, on_window_set_min_size);
WINDOW_PACKET_CASE(WindowSetMaxSize, on_window_set_max_size);
WINDOW_PACKET_CASE(WindowSetFullscreen, on_window_set_fullscreen);
WINDOW_PACKET_CASE(WindowSetTitle, on_window_set_title);
WINDOW_PACKET_CASE(WindowSetCursor, on_window_set_cursor);
#undef WINDOW_PACKET_CASE
default:
dprintln("unhandled packet type: {}", *reinterpret_cast<uint32_t*>(client_data.packet_buffer.data()));
dwarnln("client sent invalid packet, closing connection...");
epoll_ctl(epoll_fd, EPOLL_CTL_DEL, client_fd, nullptr);
window_server.remove_client_fd(client_fd);
break;
}
// this is a bit harsh, but i don't want to work on skipping streaming packets
if (client_data.packet_buffer.resize(packet_size).is_error())
{
dwarnln("could not allocate memory for client packet, closing connection...");
epoll_ctl(epoll_fd, EPOLL_CTL_DEL, client_fd, nullptr);
window_server.remove_client_fd(client_fd);
break;
}
client_data.packet_buffer.clear();
client_data.packet_buffer_nread = 0;
return BAN::Iteration::Continue;
continue;
}
);
const ssize_t nrecv = recv(
client_fd,
client_data.packet_buffer.data() + client_data.packet_buffer_nread,
client_data.packet_buffer.size() - client_data.packet_buffer_nread,
0
);
if (nrecv < 0)
dwarnln("recv 2: {}", strerror(errno));
if (nrecv <= 0)
{
epoll_ctl(epoll_fd, EPOLL_CTL_DEL, client_fd, nullptr);
window_server.remove_client_fd(client_fd);
break;
}
client_data.packet_buffer_nread += nrecv;
if (client_data.packet_buffer_nread < client_data.packet_buffer.size())
continue;
ASSERT(client_data.packet_buffer.size() >= sizeof(uint32_t));
switch (*reinterpret_cast<LibGUI::PacketType*>(client_data.packet_buffer.data()))
{
#define WINDOW_PACKET_CASE(enum, function) \
case LibGUI::PacketType::enum: \
if (auto ret = LibGUI::WindowPacket::enum::deserialize(client_data.packet_buffer.span()); !ret.is_error()) \
window_server.function(client_fd, ret.release_value()); \
break
WINDOW_PACKET_CASE(WindowCreate, on_window_create);
WINDOW_PACKET_CASE(WindowInvalidate, on_window_invalidate);
WINDOW_PACKET_CASE(WindowSetPosition, on_window_set_position);
WINDOW_PACKET_CASE(WindowSetAttributes, on_window_set_attributes);
WINDOW_PACKET_CASE(WindowSetMouseRelative, on_window_set_mouse_relative);
WINDOW_PACKET_CASE(WindowSetSize, on_window_set_size);
WINDOW_PACKET_CASE(WindowSetMinSize, on_window_set_min_size);
WINDOW_PACKET_CASE(WindowSetMaxSize, on_window_set_max_size);
WINDOW_PACKET_CASE(WindowSetFullscreen, on_window_set_fullscreen);
WINDOW_PACKET_CASE(WindowSetTitle, on_window_set_title);
WINDOW_PACKET_CASE(WindowSetCursor, on_window_set_cursor);
#undef WINDOW_PACKET_CASE
default:
dprintln("unhandled packet type: {}", *reinterpret_cast<uint32_t*>(client_data.packet_buffer.data()));
}
client_data.packet_buffer.clear();
client_data.packet_buffer_nread = 0;
}
}
}